diff options
1581 files changed, 226435 insertions, 29187 deletions
@@ -6,8 +6,9 @@ ScummVM Team PR Office --------- - Arnaud Boutonne - Public Relations Officer, Project Administrator - Eugene Sandulenko - Project Leader + Arnaud Boutonne - Public Relations Officer, Project + Administrator + Eugene Sandulenko - Project Leader Core Team --------- @@ -18,29 +19,30 @@ ScummVM Team Retired Project Leaders ----------------------- James Brown - Vincent Hamm - ScummVM co-founder, Original Cruise/CinE author + Vincent Hamm - ScummVM co-founder, Original Cruise/CinE + author Max Horn - Ludvig Strigeus - Original ScummVM and SimonVM author + Ludvig Strigeus - Original ScummVM and SimonVM author Engine Teams ------------ SCUMM: Torbjorn Andersson - James Brown - (retired) - Jonathan Gray - (retired) - Vincent Hamm - (retired) - Max Horn - (retired) + James Brown - (retired) + Jonathan Gray - (retired) + Vincent Hamm - (retired) + Max Horn - (retired) Travis Howell - Pawel Kolodziejski - Codecs, iMUSE, Smush, etc. - Gregory Montoir - (retired) - Eugene Sandulenko - FT INSANE, MM NES, MM C64, game detection, - Herc/CGA - Ludvig Strigeus - (retired) + Pawel Kolodziejski - Codecs, iMUSE, Smush, etc. + Gregory Montoir - (retired) + Eugene Sandulenko - FT INSANE, MM NES, MM C64, game detection, + Herc/CGA + Ludvig Strigeus - (retired) HE: - Jonathan Gray - (retired) + Jonathan Gray - (retired) Travis Howell - Gregory Montoir - (retired) + Gregory Montoir - (retired) Eugene Sandulenko AGI: @@ -48,26 +50,26 @@ ScummVM Team Matthew Hoops Filippos Karapetis Pawel Kolodziejski - Walter van Niftrik - (retired) + Walter van Niftrik - (retired) Kari Salminen Eugene Sandulenko - David Symonds - (retired) + David Symonds - (retired) AGOS: Torbjorn Andersson Paul Gilbert Travis Howell - Oliver Kiehl - (retired) - Ludvig Strigeus - (retired) + Oliver Kiehl - (retired) + Ludvig Strigeus - (retired) CGE: Arnaud Boutonne Paul Gilbert Cine: - Vincent Hamm - (retired) + Vincent Hamm - (retired) Pawel Kolodziejski - Gregory Montoir - (retired) + Gregory Montoir - (retired) Kari Salminen Eugene Sandulenko @@ -76,7 +78,7 @@ ScummVM Team CruisE: Paul Gilbert - Vincent Hamm - (retired) + Vincent Hamm - (retired) Draci: Denis Kasak @@ -89,7 +91,9 @@ ScummVM Team DreamWeb: Torbjorn Andersson Bertrand Augereau - Vladimir Menshakov - (retired) + Filippos Karapetis + Vladimir Menshakov - (retired) + Willem Jan Palenstijn Gob: Torbjorn Andersson @@ -108,10 +112,10 @@ ScummVM Team Eugene Sandulenko Kyra: - Torbjorn Andersson - VQA Player + Torbjorn Andersson - VQA Player Oystein Eftevaag Florian Kagerer - Gregory Montoir - (retired) + Gregory Montoir - (retired) Johannes Schickel Lastexpress: @@ -137,15 +141,18 @@ ScummVM Team Parallaction: peres + Pegasus: + Matthew Hoops + Queen: - David Eriksson - (retired) - Gregory Montoir - (retired) + David Eriksson - (retired) + Gregory Montoir - (retired) Joost Peters SAGA: Torbjorn Andersson - Daniel Balsom - Original engine reimplementation author - (retired) + Daniel Balsom - Original engine reimplementation author + (retired) Filippos Karapetis Andrew Kurushin Eugene Sandulenko @@ -153,61 +160,73 @@ ScummVM Team SCI: Greg Frieger Paul Gilbert - Max Horn - (retired) + Max Horn - (retired) Filippos Karapetis Martin Kiewitz - Walter van Niftrik - (retired) + Walter van Niftrik - (retired) Willem Jan Palenstijn Jordi Vilalta Prat Lars Skovlund Sky: - Robert Goeffringmann - (retired) - Oliver Kiehl - (retired) + Robert Goeffringmann - (retired) + Oliver Kiehl - (retired) Joost Peters Sword1: - Fabio Battaglia - PSX version support - Thierry Crozat - Mac version support - Robert Goeffringmann - (retired) + Fabio Battaglia - PSX version support + Thierry Crozat - Mac version support + Robert Goeffringmann - (retired) Sword2: Torbjorn Andersson - Fabio Battaglia - PSX version support - Jonathan Gray - (retired) + Fabio Battaglia - PSX version support + Jonathan Gray - (retired) Sword25: Torbjorn Andersson Paul Gilbert - Max Horn - (retired) + Max Horn - (retired) Filippos Karapetis Eugene Sandulenko TeenAgent: - Robert Megone - Help with callback rewriting - Vladimir Menshakov - (retired) + Robert Megone - Help with callback rewriting + Vladimir Menshakov - (retired) Tinsel: Torbjorn Andersson - Fabio Battaglia - PSX version support + Fabio Battaglia - PSX version support Paul Gilbert Sven Hesse - Max Horn - (retired) + Max Horn - (retired) Filippos Karapetis Joost Peters + Toltecs: + Benjamin Haisch + Filippos Karapetis + + Tony: + Arnaud Boutonne + Paul Gilbert + Alyssa Milburn + Toon: Sylvain Dupont Touche: - Gregory Montoir - (retired) + Gregory Montoir - (retired) TsAGE: Arnaud Boutonne Paul Gilbert Tucker: - Gregory Montoir - (retired) + Gregory Montoir - (retired) + + Wintermute: + Einar Johan T. Somaaen Backend Teams ------------- @@ -231,26 +250,26 @@ ScummVM Team Lubomyr Lisen Maemo: - Frantisek Dufka - (retired) + Frantisek Dufka - (retired) Tarek Soliman Nintendo 64: Fabio Battaglia Nintendo DS: - Bertrand Augereau - HQ software scaler + Bertrand Augereau - HQ software scaler Neil Millstone OpenPandora: John Willis PocketPC / WinCE: - Nicolas Bacca - (retired) + Nicolas Bacca - (retired) Ismail Khatib - Kostas Nakos - (retired) + Kostas Nakos - (retired) PlayStation 2: - Robert Goeffringmann - (retired) + Robert Goeffringmann - (retired) Max Lingua PSP (PlayStation Portable): @@ -258,8 +277,8 @@ ScummVM Team Joost Peters SDL (Win/Linux/OS X/etc.): - Max Horn - (retired) - Eugene Sandulenko - Asm routines, GFX layers + Max Horn - (retired) + Eugene Sandulenko - Asm routines, GFX layers SymbianOS: Jurgen Braam @@ -274,8 +293,9 @@ ScummVM Team Other subsystems ---------------- Infrastructure: - Max Horn - Backend & Engine APIs, file API, sound mixer, - audiostreams, data structures, etc. (retired) + Max Horn - Backend & Engine APIs, file API, sound + mixer, audiostreams, data structures, etc. + (retired) Eugene Sandulenko Johannes Schickel @@ -285,30 +305,31 @@ ScummVM Team Johannes Schickel Miscellaneous: - David Corrales-Lopez - Filesystem access improvements (GSoC 2007 - task) (retired) - Jerome Fisher - MT-32 emulator - Benjamin Haisch - Heavily improved de-/encoder for DXA videos - Jochen Hoenicke - Speaker & PCjr sound support, AdLib work - (retired) - Chris Page - Return to launcher, savestate improvements, - leak fixes, ... (GSoC 2008 task) (retired) - Robin Watts - ARM assembly routines for nice speedups on - several ports; improvements to the sound mixer + David Corrales-Lopez - Filesystem access improvements (GSoC 2007 + task) (retired) + Jerome Fisher - MT-32 emulator + Benjamin Haisch - Heavily improved de-/encoder for DXA videos + Jochen Hoenicke - Speaker & PCjr sound support, AdLib work + (retired) + Chris Page - Return to launcher, savestate improvements, + leak fixes, ... (GSoC 2008 task) (retired) + Robin Watts - ARM assembly routines for nice speedups on + several ports; improvements to the sound + mixer Website (code) -------------- - Fredrik Wendel - (retired) + Fredrik Wendel - (retired) Website (maintenance) --------------------- - James Brown - IRC Logs maintainer - Thierry Crozat - Wiki maintainer - Andre Heider - Buildbot maintainer - Joost Peters - Doxygen Project Documentation maintainer - Jordi Vilalta Prat - Wiki maintainer - Eugene Sandulenko - Forum, IRC channel, Screen Shots and Mailing - list maintainer + James Brown - IRC Logs maintainer + Thierry Crozat - Wiki maintainer + Andre Heider - Buildbot maintainer + Joost Peters - Doxygen Project Documentation maintainer + Jordi Vilalta Prat - Wiki maintainer + Eugene Sandulenko - Forum, IRC channel, Screen Shots and Mailing + list maintainer John Willis Website (content) @@ -317,31 +338,31 @@ ScummVM Team Documentation ------------- - Thierry Crozat - Numerous contributions to documentation - Joachim Eberhard - Numerous contributions to documentation - (retired) - Matthew Hoops - Wiki editor + Thierry Crozat - Numerous contributions to documentation + Joachim Eberhard - Numerous contributions to documentation + (retired) + Matthew Hoops - Wiki editor Retired Team Members -------------------- - Chris Apers - Former PalmOS porter - Ralph Brorsen - Help with GUI implementation - Jamieson Christian - iMUSE, MIDI, all things musical - Felix Jakschitsch - Zak256 reverse engineering - Mutwin Kraus - Original MacOS porter - Peter Moraliyski - Port: GP32 - Jeremy Newman - Former webmaster - Lionel Ulmer - Port: X11 - Won Star - Former GP32 porter + Chris Apers - Former PalmOS porter + Ralph Brorsen - Help with GUI implementation + Jamieson Christian - iMUSE, MIDI, all things musical + Felix Jakschitsch - Zak256 reverse engineering + Mutwin Kraus - Original MacOS porter + Peter Moraliyski - Port: GP32 + Jeremy Newman - Former webmaster + Lionel Ulmer - Port: X11 + Won Star - Former GP32 porter Other contributions ******************* Packages -------- AmigaOS 4: - Hans-Joerg Frieden - (retired) + Hans-Joerg Frieden - (retired) Hubert Maier - Juha Niemimaki - (retired) + Juha Niemimaki - (retired) Atari/FreeMiNT: Keith Scroggins @@ -351,22 +372,22 @@ Other contributions Luc Schrijvers Debian GNU/Linux: - Tore Anderson - (retired) + Tore Anderson - (retired) David Weinehall Fedora / RedHat: Willem Jan Palenstijn Mac OS X: - Max Horn - (retired) + Max Horn - (retired) Oystein Eftevaag Mandriva: - Dominik Scherer - (retired) + Dominik Scherer - (retired) MorphOS: Fabien Coeurjoly - Ruediger Hanke - (retired) + Ruediger Hanke - (retired) OS/2: Paul Smedley @@ -384,16 +405,19 @@ Other contributions Travis Howell Win64: - Chris Gray - (retired) + Chris Gray - (retired) Johannes Schickel Translations ------------ - Thierry Crozat - Translation Lead + Thierry Crozat - Translation Lead Basque: Mikel Iturbe Urretxa + Belarusian: + Ivan Lukyanov + Catalan: Jordi Vilalta Prat @@ -403,12 +427,18 @@ Other contributions Danish: Steffen Nyeland + Finnish: + Toni Saarela + French: Thierry Crozat + Galician: + Santiago G. Sanz + German: Simon Sawatzki - Lothar Serra Mari + Lothar Serra Mari - (retired) Hungarian: Alex Bevilacqua @@ -444,92 +474,95 @@ Other contributions Websites (design) ----------------- - Dobo Balazs - Website design - William Claydon - Skins for doxygen, buildbot and wiki - Yaroslav Fedevych - HTML/CSS for the website - Jean Marc Gimenez - ScummVM logo - David Jensen - SVG logo conversion - Raina - ScummVM forum buttons + Dobo Balazs - Website design + William Claydon - Skins for doxygen, buildbot and wiki + Yaroslav Fedevych - HTML/CSS for the website + Jean Marc Gimenez - ScummVM logo + David Jensen - SVG logo conversion + Raina - ScummVM forum buttons Code contributions ------------------ - Ori Avtalion - Subtitle control options in the GUI; BASS GUI - fixes - Stuart Caie - Decoders for Amiga and AtariST data files (AGOS - engine) - Paolo Costabel - PSP port contributions - Martin Doucha - CinE engine objectification - Thomas Fach-Pedersen - ProTracker module player, Smacker video decoder - Tobias Gunkel - Sound support for C64 version of MM/Zak, Loom - PCE support - Janne Huttunen - V3 actor mask support, Dig/FT SMUSH audio - Kovacs Endre Janos - Several fixes for Simon1 - Jeroen Janssen - Numerous readability and bugfix patches - Andreas Karlsson - Initial port for SymbianOS - Claudio Matsuoka - Daily Linux builds - Thomas Mayer - PSP port contributions - Sean Murray - ScummVM tools GUI application (GSoC 2007 task) - n0p - Windows CE port aspect ratio correction scaler - and right click input method - Mikesch Nepomuk - MI1 VGA floppy patches - Nicolas Noble - Config file and ALSA support - Tim Phillips - Initial MI1 CD music support - Quietust - Sound support for Amiga SCUMM V2/V3 games, MM - NES support - Robert Crossfield - Improved support for Apple II/C64 versions of MM - Andreas Roever - Broken Sword I & II MPEG2 cutscene support - Edward Rudd - Fixes for playing MP3 versions of MI1/Loom audio - Daniel Schepler - Final MI1 CD music support, initial Ogg Vorbis - support - Andre Souza - SDL-based OpenGL renderer - Tom Frost - WebOS port contributions + Ori Avtalion - Subtitle control options in the GUI; BASS GUI + fixes + Stuart Caie - Decoders for Amiga and AtariST data files + (AGOS engine) + Paolo Costabel - PSP port contributions + Martin Doucha - CinE engine objectification + Thomas Fach-Pedersen - ProTracker module player, Smacker video + decoder + Tobias Gunkel - Sound support for C64 version of MM/Zak, Loom + PCE support + Janne Huttunen - V3 actor mask support, Dig/FT SMUSH audio + Kovacs Endre Janos - Several fixes for Simon1 + Jeroen Janssen - Numerous readability and bugfix patches + Andreas Karlsson - Initial port for SymbianOS + Claudio Matsuoka - Daily Linux builds + Thomas Mayer - PSP port contributions + Sean Murray - ScummVM tools GUI application (GSoC 2007 task) + n0p - Windows CE port aspect ratio correction scaler + and right click input method + Mikesch Nepomuk - MI1 VGA floppy patches + Nicolas Noble - Config file and ALSA support + Tim Phillips - Initial MI1 CD music support + Quietust - Sound support for Amiga SCUMM V2/V3 games, MM + NES support + Robert Crossfield - Improved support for Apple II/C64 versions of + MM + Andreas Roever - Broken Sword I & II MPEG2 cutscene support + Edward Rudd - Fixes for playing MP3 versions of MI1/Loom + audio + Daniel Schepler - Final MI1 CD music support, initial Ogg Vorbis + support + Andre Souza - SDL-based OpenGL renderer + Tom Frost - WebOS port contributions FreeSCI Contributors -------------------- - Francois-R Boyer - MT-32 information and mapping code - Rainer Canavan - IRIX MIDI driver and bug fixes + Francois-R Boyer - MT-32 information and mapping code + Rainer Canavan - IRIX MIDI driver and bug fixes Xiaojun Chen - Paul David Doherty - Game version information - Vyacheslav Dikonov - Config script improvements - Ruediger Hanke - Port to the MorphOS platform - Matt Hargett - Clean-ups, bugfixes, Hardcore QA, Win32 - Max Horn - SetJump implementation - Ravi I. - SCI0 sound resource specification - Emmanuel Jeandel - Bugfixes and bug reports - Dmitry Jemerov - Port to the Win32 platform, numerous bugfixes - Chris Kehler - Makefile enhancements - Christopher T. Lansdo - Original CVS maintainer, Alpha compatibility - fixes - Sergey Lapin - Port of Carl's type 2 decompression code - Rickard Lind - MT-32->GM MIDI mapping magic, sound research - Hubert Maier - AmigaOS 4 port - Johannes Manhave - Document format translation - Claudio Matsuoka - CVS snapshots, daily builds, BeOS and cygwin - ports - Dark Minister - SCI research (bytecode and parser) - Carl Muckenhoupt - Sources to the SCI resource viewer tools that - started it all - Anders Baden Nielsen - PPC testing - Walter van Niftrik - Ports to the Dreamcast and GP32 platforms - Rune Orsval - Configuration file editor - Solomon Peachy - SDL ports and much of the sound subsystem - Robey Pointer - Bug tracking system hosting - Magnus Reftel - Heap implementation, Python class viewer, - bugfixes - Christoph Reichenbach - UN*X code, VM/Graphics/Sound/other - infrastructure - George Reid - FreeBSD package management - Lars Skovlund - Project maintenance, most documentation, - bugfixes, SCI1 support - Rink Springer - Port to the DOS platform, several bug fixes - Rainer De Temple - SCI research + Paul David Doherty - Game version information + Vyacheslav Dikonov - Config script improvements + Ruediger Hanke - Port to the MorphOS platform + Matt Hargett - Clean-ups, bugfixes, Hardcore QA, Win32 + Max Horn - SetJump implementation + Ravi I. - SCI0 sound resource specification + Emmanuel Jeandel - Bugfixes and bug reports + Dmitry Jemerov - Port to the Win32 platform, numerous bugfixes + Chris Kehler - Makefile enhancements + Christopher T. Lansdown - Original CVS maintainer, Alpha compatibility + fixes + Sergey Lapin - Port of Carl's type 2 decompression code + Rickard Lind - MT-32->GM MIDI mapping magic, sound research + Hubert Maier - AmigaOS 4 port + Johannes Manhave - Document format translation + Claudio Matsuoka - CVS snapshots, daily builds, BeOS and cygwin + ports + Dark Minister - SCI research (bytecode and parser) + Carl Muckenhoupt - Sources to the SCI resource viewer tools that + started it all + Anders Baden Nielsen - PPC testing + Walter van Niftrik - Ports to the Dreamcast and GP32 platforms + Rune Orsval - Configuration file editor + Solomon Peachy - SDL ports and much of the sound subsystem + Robey Pointer - Bug tracking system hosting + Magnus Reftel - Heap implementation, Python class viewer, + bugfixes + Christoph Reichenbach - UN*X code, VM/Graphics/Sound/other + infrastructure + George Reid - FreeBSD package management + Lars Skovlund - Project maintenance, most documentation, + bugfixes, SCI1 support + Rink Springer - Port to the DOS platform, several bug fixes + Rainer De Temple - SCI research Sean Terrell - Hugues Valois - Game selection menu - Jordi Vilalta - Numerous code and website clean-up patches - Petr Vyhnak - The DCL-INFLATE algorithm, many Win32 - improvements - Bas Zoetekouw - Man pages, debian package management, CVS - maintenance + Hugues Valois - Game selection menu + Jordi Vilalta - Numerous code and website clean-up patches + Petr Vyhnak - The DCL-INFLATE algorithm, many Win32 + improvements + Bas Zoetekouw - Man pages, debian package management, CVS + maintenance Special thanks to Prof. Dr. Gary Nutt for allowing the FreeSCI VM extension as a course project in his Advanced OS course. @@ -541,28 +574,27 @@ Other contributions Special thanks to ***************** - Daniel Balsom - For the original Reinherit (SAGA) code - Sander Buskens - For his work on the initial reversing of Monkey2 - Canadacow - For the original MT-32 emulator - Kevin Carnes - For Scumm16, the basis of ScummVM's older gfx codecs - Curt Coder - For the original TrollVM (preAGI) code - Patrick Combet - For the original Gobliiins ADL player - Ivan Dubrov - For contributing the initial version of the Gobliiins - engine - Henrik Engqvist - For generously providing hosting for our buildbot, SVN - repository, planet and doxygen sites as well as tons of - HD space - DOSBox Team - For their awesome OPL2 and OPL3 emulator - Till Kresslein - For design of modern ScummVM GUI - Jezar - For his freeverb filter implementation - Jim Leiterman - Various info on his FM-TOWNS/Marty SCUMM ports - lloyd - For deep tech details about C64 Zak & MM - Sarien Team - Original AGI engine code - Jimmi Thogersen - For ScummRev, and much obscure code/documentation - Tristan - For additional work on the original MT-32 emulator - James Woodcock - Soundtrack enhancements - - Some icons by Yusuke Kamiyamane + Daniel Balsom - For the original Reinherit (SAGA) code + Sander Buskens - For his work on the initial reversing of Monkey2 + Canadacow - For the original MT-32 emulator + Kevin Carnes - For Scumm16, the basis of ScummVM's older gfx codecs + Curt Coder - For the original TrollVM (preAGI) code + Patrick Combet - For the original Gobliiins ADL player + Ivan Dubrov - For contributing the initial version of the Gobliiins + engine + Henrik Engqvist - For generously providing hosting for our buildbot, SVN + repository, planet and doxygen sites as well as tons + of HD space + DOSBox Team - For their awesome OPL2 and OPL3 emulator + Yusuke Kamiyamane - For contributing some GUI icons + Till Kresslein - For design of modern ScummVM GUI + Jezar - For his freeverb filter implementation + Jim Leiterman - Various info on his FM-TOWNS/Marty SCUMM ports + lloyd - For deep tech details about C64 Zak & MM + Sarien Team - Original AGI engine code + Jimmi Thogersen - For ScummRev, and much obscure code/documentation + Tristan - For additional work on the original MT-32 emulator + James Woodcock - Soundtrack enhancements Tony Warriner and everyone at Revolution Software Ltd. for sharing with us the source of some of their brilliant games, allowing us to release @@ -597,3 +629,16 @@ Special thanks to Broken Sword 2.5 team for providing sources of their engine and their great support. + Neil Dodwell and David Dew from Creative Reality for providing the source + of Dreamweb and for their tremendous support. + + Janusz Wisniewski and Miroslaw Liminowicz from Laboratorium Komputerowe + Avalon for providing full source code for Soltys and letting us + redistribute the game. + + Jan Nedoma for providing the sources to the Wintermute-engine, and for his + support while porting the engine to ScummVM. + + Bob Bell, Michel Kripalani, Tommy Yune, from Presto Studios for providing + the source code of The Journeyman Project: Pegasus Prime. + @@ -1,5 +1,5 @@ ScummVM -Copyright (C) 2001-2012 by the following: +Copyright (C) 2001-2013 by the following: If you have contributed to this project then you deserve to be on this list. Contact us (see: AUTHORS) and we'll add you. @@ -1,7 +1,71 @@ For a more comprehensive changelog of the latest experimental code, see: https://github.com/scummvm/scummvm/commits/ -1.5.0 (????-??-??) +1.6.0 (????-??-??) + New Games: + - Added support for 3 Skulls of the Toltecs. + - Added support for Eye of the Beholder. + - Added support for Eye of the Beholder II: The Legend of Darkmoon. + + General: + - Added a new save/load chooser based on a grid of thumbnails. This is only + supported for resolutions bigger than 640x400. The old chooser is still + available and used for games without thumbnail support. It is possible to + select the old one as default too. + - Rewrote VideoDecoder subsystem. + - Added Galician translation. + - Added Finnish translation. + - Added Belarusian translation. + - Using the mouse wheel on a slider widget now changes the value by the + smallest possible amount. This is more predictable than the old behaviour, + which was to change the value by "one pixel" which would sometimes not + change it at all. + - Updated MT-32 emulation code to latest munt project snapshot. + + Cine: + - Improved audio support for Amiga and AtariST versions of Future Wars. + Now music fades out slowly instead of stopping immediately. Sound + effects are now properly panned, when requested by the game. + +Drascula: + - Resolved multiple UI issues with the original save/load screen. + - Added advanced savegame functionality, including savegame timestamps and + thumbnails and the ability to load and delete savegames from the launcher. + It's now possible to use the ScummvM save/load dialogs. + - The F7 key (previously unmapped) now always shows the ScummVM load screen. + The F10 key displays either the original save/load screen, or the ScummVM + save screen, if the user has selected to use the ScummVM save/load dialogs. + + Dreamweb: + - Now that the game is freeware, there is a small extra help text showing + the available commands in the in-game terminals when the player uses the + 'help' command. Previously, players needed to consult the manual for the + available commands. Since this reference to the manual is a form of copy + protection, this extra line can be toggled by the ScummVM copy protection + command line option. + + Groovie: + - Simplified the movie speed options, and added a custom option for The 7th + Guest. Movie options are now "normal" and "fast", with the latter changing + the movie speed in T7G to match the faster movie speed of the iOS version. + The game entry might need to be readded in the launcher for the new setting + to appear. + + SAGA: + - Added music support for the Macintosh version of I Have No Mouth and, I + Must Scream. + + SCUMM: + - Implemented Monkey Island 2 Macintosh's audio driver. Now we properly + support its sample based audio output. The same output is also used for + the m68k Macintosh version of Indiana Jones and the Fate of Atlantis. + - Improved music support for the Macintosh version of Monkey Island 1. It + now uses the original instruments, rather than approximating them with + General MIDI instruments, and should sound a lot closer to the original. + - Added sound and music support for the Macintosh version of Loom. + - Handle double-clicking in the Macintosh version of Loom. + +1.5.0 (2012-07-27) New Games: - Added support for Backyard Baseball 2003. - Added support for Blue Force. @@ -264,10 +264,14 @@ Other Games: Discworld 2: Missing Presumed ...!? [dw2] Dragon History [draci] Drascula: The Vampire Strikes Back [drascula] + Eye of the Beholder [eob] + Eye of the Beholder II: The Legend of + Darkmoon [eob2] Flight of the Amazon Queen [queen] Future Wars [fw] Inherit the Earth: Quest for the Orb [ite] Nippon Safes Inc. [nippon] + Lands of Lore: The Throne of Chaos [lol] The Legend of Kyrandia [kyra1] The Legend of Kyrandia: The Hand of Fate [kyra2] The Legend of Kyrandia: Malcolm's Revenge [kyra3] @@ -384,21 +388,24 @@ entering any answer. Chances are that it will work. ScummVM will skip copy protection in the following games: - * Maniac Mansion - * Zak McKracken and the Alien Mindbenders - * Loom (EGA) - * The Secret of Monkey Island (VGA) - * Monkey Island 2: LeChuck's Revenge * Beneath a Steel Sky -- bypassed with kind permission from Revolution Software. + * Dreamweb + -- a list of available commands in the in-game terminals is now shown + when the player uses the 'help' command * Inherit the Earth: Quest for the Orb (Floppy version) -- bypassed with kind permission from Wyrmkeep Entertainment, since it was bypassed in all CD releases of the game. + * Loom (EGA DOS) + * Maniac Mansion + * Monkey Island 2: LeChuck's Revenge * Simon the Sorcerer 1 (Floppy version) * Simon the Sorcerer 2 (Floppy version) -- bypassed with kind permission from Adventure Soft, since it was bypassed in all CD releases of the game. + * The Secret of Monkey Island (VGA) * Waxworks + * Zak McKracken and the Alien Mindbenders 3.2) Commodore64 games notes: @@ -2033,9 +2040,10 @@ The following keywords are recognized: super2xsai, supereagle, advmame2x, advmame3x, hq2x, hq3x, tv2x, dotmatrix) - confirm_exit bool Ask for confirmation by the user before quitting - (SDL backend only). - console bool Enable the console window (default: enabled) (Windows only). + confirm_exit bool Ask for confirmation by the user before + quitting (SDL backend only). + console bool Enable the console window (default: enabled) + (Windows only). cdrom number Number of CD-ROM unit to use for audio. If negative, don't even try to access the CD-ROM. joystick_num number Number of joystick device to use for input @@ -2063,7 +2071,8 @@ The following keywords are recognized: supported by some MIDI drivers.) copy_protection bool Enable copy protection in certain games, in - those cases where ScummVM disables it by default. + those cases where ScummVM disables it by + default. demo_mode bool Start demo in Maniac Mansion alt_intro bool Use alternative intro for CD versions of Beneath a Steel Sky and Flight of the Amazon @@ -2124,8 +2133,9 @@ Lands of Lore: The Throne of Chaos adds the following non-standard keywords: Space Quest IV CD adds the following non-standard keyword: - silver_cursors bool If true, an alternate set of silver mouse cursors - is used instead of the original golden ones + silver_cursors bool If true, an alternate set of silver mouse + cursors is used instead of the original golden + ones Simon the Sorcerer 1 and 2 add the following non-standard keywords: @@ -2134,19 +2144,32 @@ Simon the Sorcerer 1 and 2 add the following non-standard keywords: The Legend of Kyrandia adds the following non-standard keyword: - walkspeed int The walk speed (0-4) + walkspeed number The walk speed (0-4) + +The Legend of Kyrandia: The Hand of Fate adds the following non-standard +keyword: + + walkspeed number The walk speed (3 or 5, resp. fast or + slow) The Legend of Kyrandia: Malcolm's Revenge adds the following non-standard keywords: + walkspeed number The walk speed (3 or 5, resp. fast or + slow) studio_audience bool If true, applause and cheering sounds are heard whenever Malcolm makes a joke skip_support bool If true, the player can skip text and cutscenes - helium_mode bool If true, people sound like they've inhaled Helium + helium_mode bool If true, people sound like they've inhaled + Helium The 7th Guest adds the following non-standard keyword: - t7g_speed string Video playback speed (normal, tweaked, im_an_ios) + fast_movie_speed bool If true, movies are played at an increased + speed, matching the speed of the iOS version. + Movies without sound are still played at their + normal speed, to avoid music synchronization + issues 8.2) Custom game options that can be toggled via the GUI diff --git a/audio/audiostream.cpp b/audio/audiostream.cpp index 1c5c435359..2d65d4afef 100644 --- a/audio/audiostream.cpp +++ b/audio/audiostream.cpp @@ -386,4 +386,42 @@ Timestamp convertTimeToStreamPos(const Timestamp &where, int rate, bool isStereo return Timestamp(result.secs(), result.numberOfFrames(), result.framerate()); } +/** + * An AudioStream wrapper that cuts off the amount of samples read after a + * given time length is reached. + */ +class LimitingAudioStream : public AudioStream { +public: + LimitingAudioStream(AudioStream *parentStream, const Audio::Timestamp &length, DisposeAfterUse::Flag disposeAfterUse) : + _parentStream(parentStream), _samplesRead(0), _disposeAfterUse(disposeAfterUse), + _totalSamples(length.convertToFramerate(getRate()).totalNumberOfFrames() * getChannels()) {} + + ~LimitingAudioStream() { + if (_disposeAfterUse == DisposeAfterUse::YES) + delete _parentStream; + } + + int readBuffer(int16 *buffer, const int numSamples) { + // Cap us off so we don't read past _totalSamples + int samplesRead = _parentStream->readBuffer(buffer, MIN<int>(numSamples, _totalSamples - _samplesRead)); + _samplesRead += samplesRead; + return samplesRead; + } + + bool endOfData() const { return _parentStream->endOfData() || _samplesRead >= _totalSamples; } + bool isStereo() const { return _parentStream->isStereo(); } + int getRate() const { return _parentStream->getRate(); } + +private: + int getChannels() const { return isStereo() ? 2 : 1; } + + AudioStream *_parentStream; + DisposeAfterUse::Flag _disposeAfterUse; + uint32 _totalSamples, _samplesRead; +}; + +AudioStream *makeLimitingAudioStream(AudioStream *parentStream, const Timestamp &length, DisposeAfterUse::Flag disposeAfterUse) { + return new LimitingAudioStream(parentStream, length, disposeAfterUse); +} + } // End of namespace Audio diff --git a/audio/audiostream.h b/audio/audiostream.h index 801f13d9d9..d6d4a16280 100644 --- a/audio/audiostream.h +++ b/audio/audiostream.h @@ -356,6 +356,16 @@ QueuingAudioStream *makeQueuingAudioStream(int rate, bool stereo); */ Timestamp convertTimeToStreamPos(const Timestamp &where, int rate, bool isStereo); +/** + * Factory function for an AudioStream wrapper that cuts off the amount of samples read after a + * given time length is reached. + * + * @param parentStream The stream to limit + * @param length The time length to limit the stream to + * @param disposeAfterUse Whether the parent stream object should be destroyed on destruction of the returned stream + */ +AudioStream *makeLimitingAudioStream(AudioStream *parentStream, const Timestamp &length, DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES); + } // End of namespace Audio #endif diff --git a/audio/decoders/adpcm.cpp b/audio/decoders/adpcm.cpp index 535652a0b3..f069ee3417 100644 --- a/audio/decoders/adpcm.cpp +++ b/audio/decoders/adpcm.cpp @@ -71,13 +71,19 @@ int Oki_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) { int samples; byte data; - assert(numSamples % 2 == 0); + for (samples = 0; samples < numSamples && !endOfData(); samples++) { + if (_decodedSampleCount == 0) { + data = _stream->readByte(); + _decodedSamples[0] = decodeOKI((data >> 4) & 0x0f); + _decodedSamples[1] = decodeOKI((data >> 0) & 0x0f); + _decodedSampleCount = 2; + } - for (samples = 0; samples < numSamples && !_stream->eos() && _stream->pos() < _endpos; samples += 2) { - data = _stream->readByte(); - buffer[samples] = decodeOKI((data >> 4) & 0x0f); - buffer[samples + 1] = decodeOKI(data & 0x0f); + // (1 - (count - 1)) ensures that _decodedSamples acts as a FIFO of depth 2 + buffer[samples] = _decodedSamples[1 - (_decodedSampleCount - 1)]; + _decodedSampleCount--; } + return samples; } @@ -117,13 +123,19 @@ int DVI_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) { int samples; byte data; - assert(numSamples % 2 == 0); + for (samples = 0; samples < numSamples && !endOfData(); samples++) { + if (_decodedSampleCount == 0) { + data = _stream->readByte(); + _decodedSamples[0] = decodeIMA((data >> 4) & 0x0f, 0); + _decodedSamples[1] = decodeIMA((data >> 0) & 0x0f, _channels == 2 ? 1 : 0); + _decodedSampleCount = 2; + } - for (samples = 0; samples < numSamples && !_stream->eos() && _stream->pos() < _endpos; samples += 2) { - data = _stream->readByte(); - buffer[samples] = decodeIMA((data >> 4) & 0x0f); - buffer[samples + 1] = decodeIMA(data & 0x0f, _channels == 2 ? 1 : 0); + // (1 - (count - 1)) ensures that _decodedSamples acts as a FIFO of depth 2 + buffer[samples] = _decodedSamples[1 - (_decodedSampleCount - 1)]; + _decodedSampleCount--; } + return samples; } @@ -256,7 +268,6 @@ static const int MSADPCMAdaptationTable[] = { 768, 614, 512, 409, 307, 230, 230, 230 }; - int16 MS_ADPCMStream::decodeMS(ADPCMChannelStatus *c, byte code) { int32 predictor; @@ -278,40 +289,42 @@ int16 MS_ADPCMStream::decodeMS(ADPCMChannelStatus *c, byte code) { int MS_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) { int samples; byte data; - int i = 0; - - samples = 0; + int i; - while (samples < numSamples && !_stream->eos() && _stream->pos() < _endpos) { - if (_blockPos[0] == _blockAlign) { - // read block header - for (i = 0; i < _channels; i++) { - _status.ch[i].predictor = CLIP(_stream->readByte(), (byte)0, (byte)6); - _status.ch[i].coeff1 = MSADPCMAdaptCoeff1[_status.ch[i].predictor]; - _status.ch[i].coeff2 = MSADPCMAdaptCoeff2[_status.ch[i].predictor]; - } + for (samples = 0; samples < numSamples && !endOfData(); samples++) { + if (_decodedSampleCount == 0) { + if (_blockPos[0] == _blockAlign) { + // read block header + for (i = 0; i < _channels; i++) { + _status.ch[i].predictor = CLIP(_stream->readByte(), (byte)0, (byte)6); + _status.ch[i].coeff1 = MSADPCMAdaptCoeff1[_status.ch[i].predictor]; + _status.ch[i].coeff2 = MSADPCMAdaptCoeff2[_status.ch[i].predictor]; + } - for (i = 0; i < _channels; i++) - _status.ch[i].delta = _stream->readSint16LE(); + for (i = 0; i < _channels; i++) + _status.ch[i].delta = _stream->readSint16LE(); - for (i = 0; i < _channels; i++) - _status.ch[i].sample1 = _stream->readSint16LE(); + for (i = 0; i < _channels; i++) + _status.ch[i].sample1 = _stream->readSint16LE(); - for (i = 0; i < _channels; i++) - buffer[samples++] = _status.ch[i].sample2 = _stream->readSint16LE(); + for (i = 0; i < _channels; i++) + _decodedSamples[_decodedSampleCount++] = _status.ch[i].sample2 = _stream->readSint16LE(); - for (i = 0; i < _channels; i++) - buffer[samples++] = _status.ch[i].sample1; + for (i = 0; i < _channels; i++) + _decodedSamples[_decodedSampleCount++] = _status.ch[i].sample1; - _blockPos[0] = _channels * 7; + _blockPos[0] = _channels * 7; + } else { + data = _stream->readByte(); + _blockPos[0]++; + _decodedSamples[_decodedSampleCount++] = decodeMS(&_status.ch[0], (data >> 4) & 0x0f); + _decodedSamples[_decodedSampleCount++] = decodeMS(&_status.ch[_channels - 1], data & 0x0f); + } } - for (; samples < numSamples && _blockPos[0] < _blockAlign && !_stream->eos() && _stream->pos() < _endpos; samples += 2) { - data = _stream->readByte(); - _blockPos[0]++; - buffer[samples] = decodeMS(&_status.ch[0], (data >> 4) & 0x0f); - buffer[samples + 1] = decodeMS(&_status.ch[_channels - 1], data & 0x0f); - } + // (1 - (count - 1)) ensures that _decodedSamples acts as a FIFO of depth 2 + buffer[samples] = _decodedSamples[1 - (_decodedSampleCount - 1)]; + _decodedSampleCount--; } return samples; diff --git a/audio/decoders/adpcm_intern.h b/audio/decoders/adpcm_intern.h index 31747aabaf..66a1aa605f 100644 --- a/audio/decoders/adpcm_intern.h +++ b/audio/decoders/adpcm_intern.h @@ -37,7 +37,6 @@ #include "common/stream.h" #include "common/textconsole.h" - namespace Audio { class ADPCMStream : public RewindableAudioStream { @@ -64,12 +63,11 @@ public: ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign); virtual bool endOfData() const { return (_stream->eos() || _stream->pos() >= _endpos); } - virtual bool isStereo() const { return _channels == 2; } - virtual int getRate() const { return _rate; } + virtual bool isStereo() const { return _channels == 2; } + virtual int getRate() const { return _rate; } virtual bool rewind(); - /** * This table is used by some ADPCM variants (IMA and OKI) to adjust the * step for use on the next sample. @@ -83,12 +81,18 @@ public: class Oki_ADPCMStream : public ADPCMStream { public: Oki_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign) - : ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) {} + : ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) { _decodedSampleCount = 0; } + + virtual bool endOfData() const { return (_stream->eos() || _stream->pos() >= _endpos) && (_decodedSampleCount == 0); } virtual int readBuffer(int16 *buffer, const int numSamples); protected: int16 decodeOKI(byte); + +private: + uint8 _decodedSampleCount; + int16 _decodedSamples[2]; }; class Ima_ADPCMStream : public ADPCMStream { @@ -108,9 +112,15 @@ public: class DVI_ADPCMStream : public Ima_ADPCMStream { public: DVI_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign) - : Ima_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) {} + : Ima_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) { _decodedSampleCount = 0; } + + virtual bool endOfData() const { return (_stream->eos() || _stream->pos() >= _endpos) && (_decodedSampleCount == 0); } virtual int readBuffer(int16 *buffer, const int numSamples); + +private: + uint8 _decodedSampleCount; + int16 _decodedSamples[2]; }; class Apple_ADPCMStream : public Ima_ADPCMStream { @@ -196,12 +206,19 @@ public: if (blockAlign == 0) error("MS_ADPCMStream(): blockAlign isn't specified for MS ADPCM"); memset(&_status, 0, sizeof(_status)); + _decodedSampleCount = 0; } + virtual bool endOfData() const { return (_stream->eos() || _stream->pos() >= _endpos) && (_decodedSampleCount == 0); } + virtual int readBuffer(int16 *buffer, const int numSamples); protected: int16 decodeMS(ADPCMChannelStatus *c, byte); + +private: + uint8 _decodedSampleCount; + int16 _decodedSamples[4]; }; // Duck DK3 IMA ADPCM Decoder diff --git a/audio/decoders/aiff.h b/audio/decoders/aiff.h index 59664bb85a..0d96e73c26 100644 --- a/audio/decoders/aiff.h +++ b/audio/decoders/aiff.h @@ -23,6 +23,7 @@ /** * @file * Sound decoder used in engines: + * - pegasus * - saga * - sci * - sword1 @@ -47,7 +48,7 @@ class SeekableAudioStream; * successful. In that case, the stream's seek position will be set to the * start of the audio data, and size, rate and flags contain information * necessary for playback. Currently this function only supports uncompressed - * raw PCM data as well as IMA ADPCM. + * raw PCM. */ extern bool loadAIFFFromStream(Common::SeekableReadStream &stream, int &size, int &rate, byte &flags); diff --git a/audio/decoders/qdm2.cpp b/audio/decoders/qdm2.cpp index 31405d3ab1..732de311aa 100644 --- a/audio/decoders/qdm2.cpp +++ b/audio/decoders/qdm2.cpp @@ -689,7 +689,7 @@ static int getVlc2(Common::BitStream *s, int16 (*table)[2], int bits, int maxDep code = table[index][0]; n = table[index][1]; - if(maxDepth > 2 && n < 0) { + if (maxDepth > 2 && n < 0) { s->skip(nbBits); index = s->getBits(-n) + code; code = table[index][0]; @@ -861,9 +861,9 @@ void initVlcSparse(VLC *vlc, int nb_bits, int nb_codes, const void *symbols, int symbols_wrap, int symbols_size) { vlc->bits = nb_bits; - if(vlc->table_size && vlc->table_size == vlc->table_allocated) { + if (vlc->table_size && vlc->table_size == vlc->table_allocated) { return; - } else if(vlc->table_size) { + } else if (vlc->table_size) { error("called on a partially initialized table"); } @@ -1353,7 +1353,7 @@ void QDM2Stream::fix_coding_method_array(int sb, int channels, sb_int8_array cod for (ch = 0; ch < channels; ch++) { for (j = 0; j < 64; ) { - if((coding_method[ch][sb][j] - 8) > 22) { + if ((coding_method[ch][sb][j] - 8) > 22) { run = 1; case_val = 8; } else { diff --git a/audio/decoders/quicktime.cpp b/audio/decoders/quicktime.cpp index 8874a61c2e..787b547495 100644 --- a/audio/decoders/quicktime.cpp +++ b/audio/decoders/quicktime.cpp @@ -62,41 +62,6 @@ private: }; /** - * An AudioStream wrapper that cuts off the amount of samples read after a - * given time length is reached. - */ -class LimitingAudioStream : public AudioStream { -public: - LimitingAudioStream(AudioStream *parentStream, const Audio::Timestamp &length, - DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES) : - _parentStream(parentStream), _samplesRead(0), _disposeAfterUse(disposeAfterUse), - _totalSamples(length.convertToFramerate(getRate()).totalNumberOfFrames() * getChannels()) {} - - ~LimitingAudioStream() { - if (_disposeAfterUse == DisposeAfterUse::YES) - delete _parentStream; - } - - int readBuffer(int16 *buffer, const int numSamples) { - // Cap us off so we don't read past _totalSamples - int samplesRead = _parentStream->readBuffer(buffer, MIN<int>(numSamples, _totalSamples - _samplesRead)); - _samplesRead += samplesRead; - return samplesRead; - } - - bool endOfData() const { return _parentStream->endOfData() || _samplesRead >= _totalSamples; } - bool isStereo() const { return _parentStream->isStereo(); } - int getRate() const { return _parentStream->getRate(); } - -private: - int getChannels() const { return isStereo() ? 2 : 1; } - - AudioStream *_parentStream; - DisposeAfterUse::Flag _disposeAfterUse; - uint32 _totalSamples, _samplesRead; -}; - -/** * An AudioStream wrapper that forces audio to be played in mono. * It currently just ignores the right channel if stereo. */ @@ -169,7 +134,7 @@ void QuickTimeAudioDecoder::init() { _audioTracks.push_back(new QuickTimeAudioTrack(this, _tracks[i])); } -Common::QuickTimeParser::SampleDesc *QuickTimeAudioDecoder::readSampleDesc(Track *track, uint32 format) { +Common::QuickTimeParser::SampleDesc *QuickTimeAudioDecoder::readSampleDesc(Track *track, uint32 format, uint32 descSize) { if (track->codecType == CODEC_TYPE_AUDIO) { debug(0, "Audio Codec FourCC: \'%s\'", tag2str(format)); @@ -230,7 +195,7 @@ QuickTimeAudioDecoder::QuickTimeAudioTrack::QuickTimeAudioTrack(QuickTimeAudioDe if (entry->getCodecTag() == MKTAG('r', 'a', 'w', ' ') || entry->getCodecTag() == MKTAG('t', 'w', 'o', 's')) _parentTrack->sampleSize = (entry->_bitsPerSample / 8) * entry->_channels; - + // Initialize our edit parser too _curEdit = 0; enterNewEdit(Timestamp()); @@ -263,7 +228,7 @@ void QuickTimeAudioDecoder::QuickTimeAudioTrack::queueAudio(const Timestamp &len _skipSamples = Timestamp(); } - queueStream(new LimitingAudioStream(new SilentAudioStream(getRate(), isStereo()), editLength), editLength); + queueStream(makeLimitingAudioStream(new SilentAudioStream(getRate(), isStereo()), editLength), editLength); _curEdit++; enterNewEdit(nextEditTime); } else { @@ -289,7 +254,7 @@ void QuickTimeAudioDecoder::QuickTimeAudioTrack::queueAudio(const Timestamp &len // we move on to the next edit if (trackPosition >= nextEditTime || _curChunk >= _parentTrack->chunkCount) { chunkLength = nextEditTime.convertToFramerate(getRate()) - getCurrentTrackTime(); - stream = new LimitingAudioStream(stream, chunkLength); + stream = makeLimitingAudioStream(stream, chunkLength); _curEdit++; enterNewEdit(nextEditTime); @@ -430,9 +395,9 @@ AudioStream *QuickTimeAudioDecoder::QuickTimeAudioTrack::readAudioChunk(uint chu } void QuickTimeAudioDecoder::QuickTimeAudioTrack::skipSamples(const Timestamp &length, AudioStream *stream) { - uint32 sampleCount = length.convertToFramerate(getRate()).totalNumberOfFrames(); + int32 sampleCount = length.convertToFramerate(getRate()).totalNumberOfFrames(); - if (sampleCount == 0) + if (sampleCount <= 0) return; if (isStereo()) @@ -461,7 +426,7 @@ void QuickTimeAudioDecoder::QuickTimeAudioTrack::enterNewEdit(const Timestamp &p // If we're at the end of the edit list, there's nothing else for us to do here if (allDataRead()) return; - + // For an empty edit, we may need to adjust the start time if (_parentTrack->editList[_curEdit].mediaTime == -1) { // Just invalidate the current media position (and make sure the scale diff --git a/audio/decoders/quicktime.h b/audio/decoders/quicktime.h index 4dd1a57710..4c0b93488e 100644 --- a/audio/decoders/quicktime.h +++ b/audio/decoders/quicktime.h @@ -25,6 +25,7 @@ * Sound decoder used in engines: * - groovie * - mohawk + * - pegasus * - sci */ diff --git a/audio/decoders/quicktime_intern.h b/audio/decoders/quicktime_intern.h index efc97cbd13..bb5ff0cf5c 100644 --- a/audio/decoders/quicktime_intern.h +++ b/audio/decoders/quicktime_intern.h @@ -88,7 +88,7 @@ protected: private: QuickTimeAudioDecoder *_decoder; - Track *_parentTrack; + Track *_parentTrack; QueuingAudioStream *_queue; uint _curChunk; Timestamp _curMediaPos, _skipSamples; @@ -115,7 +115,7 @@ protected: ~AudioSampleDesc(); bool isAudioCodecSupported() const; - + AudioStream *createAudioStream(Common::SeekableReadStream *stream) const; void initCodec(); @@ -131,7 +131,7 @@ protected: }; // Common::QuickTimeParser API - virtual Common::QuickTimeParser::SampleDesc *readSampleDesc(Track *track, uint32 format); + virtual Common::QuickTimeParser::SampleDesc *readSampleDesc(Track *track, uint32 format, uint32 descSize); void init(); diff --git a/audio/fmopl.h b/audio/fmopl.h index 323cc3d028..ad1794d873 100644 --- a/audio/fmopl.h +++ b/audio/fmopl.h @@ -129,7 +129,9 @@ public: /** * Function to directly write to a specific OPL register. - * This writes to *both* chips for a Dual OPL2. + * This writes to *both* chips for a Dual OPL2. We allow + * writing to secondary OPL registers by using register + * values >= 0x100. * * @param r hardware register number to write to * @param v value, which will be written diff --git a/audio/mididrv.cpp b/audio/mididrv.cpp index 0518915e81..dea07a739b 100644 --- a/audio/mididrv.cpp +++ b/audio/mididrv.cpp @@ -240,7 +240,7 @@ MidiDriver::DeviceHandle MidiDriver::detectDevice(int flags) { devStr = ConfMan.hasKey("gm_device") ? ConfMan.get("gm_device") : Common::String("null"); else devStr = "auto"; - + // Default to Null device here, since we also register a default null setting for // the MT32 or GM device in the config manager. hdl = getDeviceHandle(devStr.empty() ? Common::String("null") : devStr); diff --git a/audio/mididrv.h b/audio/mididrv.h index fb3e29bd60..56b4a265cb 100644 --- a/audio/mididrv.h +++ b/audio/mididrv.h @@ -194,7 +194,9 @@ public: enum { // PROP_TIMEDIV = 1, PROP_OLD_ADLIB = 2, - PROP_CHANNEL_MASK = 3 + PROP_CHANNEL_MASK = 3, + // HACK: Not so nice, but our SCUMM AdLib code is in audio/ + PROP_SCUMM_OPL3 = 4 }; /** diff --git a/audio/midiparser.cpp b/audio/midiparser.cpp index 943a6067a4..eec32c05d1 100644 --- a/audio/midiparser.cpp +++ b/audio/midiparser.cpp @@ -32,24 +32,24 @@ ////////////////////////////////////////////////// MidiParser::MidiParser() : -_hanging_notes_count(0), +_hangingNotesCount(0), _driver(0), -_timer_rate(0x4A0000), +_timerRate(0x4A0000), _ppqn(96), _tempo(500000), -_psec_per_tick(5208), // 500000 / 96 +_psecPerTick(5208), // 500000 / 96 _autoLoop(false), _smartJump(false), _centerPitchWheelOnUnload(false), _sendSustainOffOnNotesOff(false), -_num_tracks(0), -_active_track(255), -_abort_parse(0) { - memset(_active_notes, 0, sizeof(_active_notes)); - _next_event.start = NULL; - _next_event.delta = 0; - _next_event.event = 0; - _next_event.length = 0; +_numTracks(0), +_activeTrack(255), +_abortParse(0) { + memset(_activeNotes, 0, sizeof(_activeNotes)); + _nextEvent.start = NULL; + _nextEvent.delta = 0; + _nextEvent.event = 0; + _nextEvent.length = 0; } void MidiParser::property(int prop, int value) { @@ -76,7 +76,7 @@ void MidiParser::sendToDriver(uint32 b) { void MidiParser::setTempo(uint32 tempo) { _tempo = tempo; if (_ppqn) - _psec_per_tick = (tempo + (_ppqn >> 2)) / _ppqn; + _psecPerTick = (tempo + (_ppqn >> 2)) / _ppqn; } // This is the conventional (i.e. SMF) variable length quantity @@ -100,44 +100,44 @@ void MidiParser::activeNote(byte channel, byte note, bool active) { return; if (active) - _active_notes[note] |= (1 << channel); + _activeNotes[note] |= (1 << channel); else - _active_notes[note] &= ~(1 << channel); + _activeNotes[note] &= ~(1 << channel); // See if there are hanging notes that we can cancel - NoteTimer *ptr = _hanging_notes; + NoteTimer *ptr = _hangingNotes; int i; - for (i = ARRAYSIZE(_hanging_notes); i; --i, ++ptr) { - if (ptr->channel == channel && ptr->note == note && ptr->time_left) { - ptr->time_left = 0; - --_hanging_notes_count; + for (i = ARRAYSIZE(_hangingNotes); i; --i, ++ptr) { + if (ptr->channel == channel && ptr->note == note && ptr->timeLeft) { + ptr->timeLeft = 0; + --_hangingNotesCount; break; } } } -void MidiParser::hangingNote(byte channel, byte note, uint32 time_left, bool recycle) { +void MidiParser::hangingNote(byte channel, byte note, uint32 timeLeft, bool recycle) { NoteTimer *best = 0; - NoteTimer *ptr = _hanging_notes; + NoteTimer *ptr = _hangingNotes; int i; - if (_hanging_notes_count >= ARRAYSIZE(_hanging_notes)) { + if (_hangingNotesCount >= ARRAYSIZE(_hangingNotes)) { warning("MidiParser::hangingNote(): Exceeded polyphony"); return; } - for (i = ARRAYSIZE(_hanging_notes); i; --i, ++ptr) { + for (i = ARRAYSIZE(_hangingNotes); i; --i, ++ptr) { if (ptr->channel == channel && ptr->note == note) { - if (ptr->time_left && ptr->time_left < time_left && recycle) + if (ptr->timeLeft && ptr->timeLeft < timeLeft && recycle) return; best = ptr; - if (ptr->time_left) { + if (ptr->timeLeft) { if (recycle) sendToDriver(0x80 | channel, note, 0); - --_hanging_notes_count; + --_hangingNotesCount; } break; - } else if (!best && ptr->time_left == 0) { + } else if (!best && ptr->timeLeft == 0) { best = ptr; } } @@ -146,14 +146,14 @@ void MidiParser::hangingNote(byte channel, byte note, uint32 time_left, bool rec // length, if the note should be turned on and off in // the same iteration. For now just set it to 1 and // we'll turn it off in the next cycle. - if (!time_left || time_left & 0x80000000) - time_left = 1; + if (!timeLeft || timeLeft & 0x80000000) + timeLeft = 1; if (best) { best->channel = channel; best->note = note; - best->time_left = time_left; - ++_hanging_notes_count; + best->timeLeft = timeLeft; + ++_hangingNotesCount; } else { // We checked this up top. We should never get here! warning("MidiParser::hangingNote(): Internal error"); @@ -161,45 +161,45 @@ void MidiParser::hangingNote(byte channel, byte note, uint32 time_left, bool rec } void MidiParser::onTimer() { - uint32 end_time; - uint32 event_time; + uint32 endTime; + uint32 eventTime; - if (!_position._play_pos || !_driver) + if (!_position._playPos || !_driver) return; - _abort_parse = false; - end_time = _position._play_time + _timer_rate; + _abortParse = false; + endTime = _position._playTime + _timerRate; // Scan our hanging notes for any // that should be turned off. - if (_hanging_notes_count) { - NoteTimer *ptr = &_hanging_notes[0]; + if (_hangingNotesCount) { + NoteTimer *ptr = &_hangingNotes[0]; int i; - for (i = ARRAYSIZE(_hanging_notes); i; --i, ++ptr) { - if (ptr->time_left) { - if (ptr->time_left <= _timer_rate) { + for (i = ARRAYSIZE(_hangingNotes); i; --i, ++ptr) { + if (ptr->timeLeft) { + if (ptr->timeLeft <= _timerRate) { sendToDriver(0x80 | ptr->channel, ptr->note, 0); - ptr->time_left = 0; - --_hanging_notes_count; + ptr->timeLeft = 0; + --_hangingNotesCount; } else { - ptr->time_left -= _timer_rate; + ptr->timeLeft -= _timerRate; } } } } - while (!_abort_parse) { - EventInfo &info = _next_event; + while (!_abortParse) { + EventInfo &info = _nextEvent; - event_time = _position._last_event_time + info.delta * _psec_per_tick; - if (event_time > end_time) + eventTime = _position._lastEventTime + info.delta * _psecPerTick; + if (eventTime > endTime) break; // Process the next info. - _position._last_event_tick += info.delta; + _position._lastEventTick += info.delta; if (info.event < 0x80) { warning("Bad command or running status %02X", info.event); - _position._play_pos = 0; + _position._playPos = 0; return; } @@ -217,7 +217,7 @@ void MidiParser::onTimer() { // as well as sending it to the output device. if (_autoLoop) { jumpToTick(0); - parseNextEvent(_next_event); + parseNextEvent(_nextEvent); } else { stopPlaying(); _driver->metaEvent(info.ext.type, info.ext.data, (uint16)info.length); @@ -234,7 +234,7 @@ void MidiParser::onTimer() { activeNote(info.channel(), info.basic.param1, false); } else if (info.command() == 0x9) { if (info.length > 0) - hangingNote(info.channel(), info.basic.param1, info.length * _psec_per_tick - (end_time - event_time)); + hangingNote(info.channel(), info.basic.param1, info.length * _psecPerTick - (endTime - eventTime)); else activeNote(info.channel(), info.basic.param1, true); } @@ -242,15 +242,15 @@ void MidiParser::onTimer() { } - if (!_abort_parse) { - _position._last_event_time = event_time; - parseNextEvent(_next_event); + if (!_abortParse) { + _position._lastEventTime = eventTime; + parseNextEvent(_nextEvent); } } - if (!_abort_parse) { - _position._play_time = end_time; - _position._play_tick = (_position._play_time - _position._last_event_time) / _psec_per_tick + _position._last_event_tick; + if (!_abortParse) { + _position._playTime = endTime; + _position._playTick = (_position._playTime - _position._lastEventTime) / _psecPerTick + _position._lastEventTick; } } @@ -263,20 +263,20 @@ void MidiParser::allNotesOff() { // Turn off all active notes for (i = 0; i < 128; ++i) { for (j = 0; j < 16; ++j) { - if (_active_notes[i] & (1 << j)) { + if (_activeNotes[i] & (1 << j)) { sendToDriver(0x80 | j, i, 0); } } } // Turn off all hanging notes - for (i = 0; i < ARRAYSIZE(_hanging_notes); i++) { - if (_hanging_notes[i].time_left) { - sendToDriver(0x80 | _hanging_notes[i].channel, _hanging_notes[i].note, 0); - _hanging_notes[i].time_left = 0; + for (i = 0; i < ARRAYSIZE(_hangingNotes); i++) { + if (_hangingNotes[i].timeLeft) { + sendToDriver(0x80 | _hangingNotes[i].channel, _hangingNotes[i].note, 0); + _hangingNotes[i].timeLeft = 0; } } - _hanging_notes_count = 0; + _hangingNotesCount = 0; // To be sure, send an "All Note Off" event (but not all MIDI devices // support this...). @@ -287,7 +287,7 @@ void MidiParser::allNotesOff() { sendToDriver(0xB0 | i, 0x40, 0); // Also send a sustain off event (bug #3116608) } - memset(_active_notes, 0, sizeof(_active_notes)); + memset(_activeNotes, 0, sizeof(_activeNotes)); } void MidiParser::resetTracking() { @@ -295,7 +295,7 @@ void MidiParser::resetTracking() { } bool MidiParser::setTrack(int track) { - if (track < 0 || track >= _num_tracks) + if (track < 0 || track >= _numTracks) return false; // We allow restarting the track via setTrack when // it isn't playing anymore. This allows us to reuse @@ -308,7 +308,7 @@ bool MidiParser::setTrack(int track) { // TODO: Check if any engine has problem with this // handling, if so we need to find a better way to handle // track restarts. (KYRA relies on this working) - else if (track == _active_track && isPlaying()) + else if (track == _activeTrack && isPlaying()) return true; if (_smartJump) @@ -317,10 +317,10 @@ bool MidiParser::setTrack(int track) { allNotesOff(); resetTracking(); - memset(_active_notes, 0, sizeof(_active_notes)); - _active_track = track; - _position._play_pos = _tracks[track]; - parseNextEvent(_next_event); + memset(_activeNotes, 0, sizeof(_activeNotes)); + _activeTrack = track; + _position._playPos = _tracks[track]; + parseNextEvent(_nextEvent); return true; } @@ -332,29 +332,29 @@ void MidiParser::stopPlaying() { void MidiParser::hangAllActiveNotes() { // Search for note off events until we have // accounted for every active note. - uint16 temp_active[128]; - memcpy(temp_active, _active_notes, sizeof (temp_active)); + uint16 tempActive[128]; + memcpy(tempActive, _activeNotes, sizeof (tempActive)); - uint32 advance_tick = _position._last_event_tick; + uint32 advanceTick = _position._lastEventTick; while (true) { int i; for (i = 0; i < 128; ++i) - if (temp_active[i] != 0) + if (tempActive[i] != 0) break; if (i == 128) break; - parseNextEvent(_next_event); - advance_tick += _next_event.delta; - if (_next_event.command() == 0x8) { - if (temp_active[_next_event.basic.param1] & (1 << _next_event.channel())) { - hangingNote(_next_event.channel(), _next_event.basic.param1, (advance_tick - _position._last_event_tick) * _psec_per_tick, false); - temp_active[_next_event.basic.param1] &= ~(1 << _next_event.channel()); + parseNextEvent(_nextEvent); + advanceTick += _nextEvent.delta; + if (_nextEvent.command() == 0x8) { + if (tempActive[_nextEvent.basic.param1] & (1 << _nextEvent.channel())) { + hangingNote(_nextEvent.channel(), _nextEvent.basic.param1, (advanceTick - _position._lastEventTick) * _psecPerTick, false); + tempActive[_nextEvent.basic.param1] &= ~(1 << _nextEvent.channel()); } - } else if (_next_event.event == 0xFF && _next_event.ext.type == 0x2F) { + } else if (_nextEvent.event == 0xFF && _nextEvent.ext.type == 0x2F) { // warning("MidiParser::hangAllActiveNotes(): Hit End of Track with active notes left"); for (i = 0; i < 128; ++i) { for (int j = 0; j < 16; ++j) { - if (temp_active[i] & (1 << j)) { + if (tempActive[i] & (1 << j)) { activeNote(j, i, false); sendToDriver(0x80 | j, i, 0); } @@ -366,33 +366,33 @@ void MidiParser::hangAllActiveNotes() { } bool MidiParser::jumpToTick(uint32 tick, bool fireEvents, bool stopNotes, bool dontSendNoteOn) { - if (_active_track >= _num_tracks) + if (_activeTrack >= _numTracks) return false; Tracker currentPos(_position); - EventInfo currentEvent(_next_event); + EventInfo currentEvent(_nextEvent); resetTracking(); - _position._play_pos = _tracks[_active_track]; - parseNextEvent(_next_event); + _position._playPos = _tracks[_activeTrack]; + parseNextEvent(_nextEvent); if (tick > 0) { while (true) { - EventInfo &info = _next_event; - if (_position._last_event_tick + info.delta >= tick) { - _position._play_time += (tick - _position._last_event_tick) * _psec_per_tick; - _position._play_tick = tick; + EventInfo &info = _nextEvent; + if (_position._lastEventTick + info.delta >= tick) { + _position._playTime += (tick - _position._lastEventTick) * _psecPerTick; + _position._playTick = tick; break; } - _position._last_event_tick += info.delta; - _position._last_event_time += info.delta * _psec_per_tick; - _position._play_tick = _position._last_event_tick; - _position._play_time = _position._last_event_time; + _position._lastEventTick += info.delta; + _position._lastEventTime += info.delta * _psecPerTick; + _position._playTick = _position._lastEventTick; + _position._playTime = _position._lastEventTime; if (info.event == 0xFF) { if (info.ext.type == 0x2F) { // End of track _position = currentPos; - _next_event = currentEvent; + _nextEvent = currentEvent; return false; } else { if (info.ext.type == 0x51 && info.length >= 3) // Tempo @@ -419,36 +419,36 @@ bool MidiParser::jumpToTick(uint32 tick, bool fireEvents, bool stopNotes, bool d } } - parseNextEvent(_next_event); + parseNextEvent(_nextEvent); } } if (stopNotes) { - if (!_smartJump || !currentPos._play_pos) { + if (!_smartJump || !currentPos._playPos) { allNotesOff(); } else { - EventInfo targetEvent(_next_event); + EventInfo targetEvent(_nextEvent); Tracker targetPosition(_position); _position = currentPos; - _next_event = currentEvent; + _nextEvent = currentEvent; hangAllActiveNotes(); - _next_event = targetEvent; + _nextEvent = targetEvent; _position = targetPosition; } } - _abort_parse = true; + _abortParse = true; return true; } void MidiParser::unloadMusic() { resetTracking(); allNotesOff(); - _num_tracks = 0; - _active_track = 255; - _abort_parse = true; + _numTracks = 0; + _activeTrack = 255; + _abortParse = true; if (_centerPitchWheelOnUnload) { // Center the pitch wheels in preparation for the next piece of diff --git a/audio/midiparser.h b/audio/midiparser.h index c935969e72..bb9749b97f 100644 --- a/audio/midiparser.h +++ b/audio/midiparser.h @@ -49,33 +49,33 @@ class MidiDriver_BASE; * each Tracker location. */ struct Tracker { - byte * _play_pos; ///< A pointer to the next event to be parsed - uint32 _play_time; ///< Current time in microseconds; may be in between event times - uint32 _play_tick; ///< Current MIDI tick; may be in between event ticks - uint32 _last_event_time; ///< The time, in microseconds, of the last event that was parsed - uint32 _last_event_tick; ///< The tick at which the last parsed event occurs - byte _running_status; ///< Cached MIDI command, for MIDI streams that rely on implied event codes + byte * _playPos; ///< A pointer to the next event to be parsed + uint32 _playTime; ///< Current time in microseconds; may be in between event times + uint32 _playTick; ///< Current MIDI tick; may be in between event ticks + uint32 _lastEventTime; ///< The time, in microseconds, of the last event that was parsed + uint32 _lastEventTick; ///< The tick at which the last parsed event occurs + byte _runningStatus; ///< Cached MIDI command, for MIDI streams that rely on implied event codes Tracker() { clear(); } /// Copy constructor for each duplication of Tracker information. Tracker(const Tracker ©) : - _play_pos(copy._play_pos), - _play_time(copy._play_time), - _play_tick(copy._play_tick), - _last_event_time(copy._last_event_time), - _last_event_tick(copy._last_event_tick), - _running_status(copy._running_status) + _playPos(copy._playPos), + _playTime(copy._playTime), + _playTick(copy._playTick), + _lastEventTime(copy._lastEventTime), + _lastEventTick(copy._lastEventTick), + _runningStatus(copy._runningStatus) { } /// Clears all data; used by the constructor for initialization. void clear() { - _play_pos = 0; - _play_time = 0; - _play_tick = 0; - _last_event_time = 0; - _last_event_tick = 0; - _running_status = 0; + _playPos = 0; + _playTime = 0; + _playTick = 0; + _lastEventTime = 0; + _lastEventTick = 0; + _runningStatus = 0; } }; @@ -119,8 +119,8 @@ struct EventInfo { struct NoteTimer { byte channel; ///< The MIDI channel on which the note was played byte note; ///< The note number for the active note - uint32 time_left; ///< The time, in microseconds, remaining before the note should be turned off - NoteTimer() : channel(0), note(0), time_left(0) {} + uint32 timeLeft; ///< The time, in microseconds, remaining before the note should be turned off + NoteTimer() : channel(0), note(0), timeLeft(0) {} }; @@ -264,29 +264,29 @@ struct NoteTimer { */ class MidiParser { protected: - uint16 _active_notes[128]; ///< Each uint16 is a bit mask for channels that have that note on. - NoteTimer _hanging_notes[32]; ///< Maintains expiration info for up to 32 notes. + uint16 _activeNotes[128]; ///< Each uint16 is a bit mask for channels that have that note on. + NoteTimer _hangingNotes[32]; ///< Maintains expiration info for up to 32 notes. ///< Used for "Smart Jump" and MIDI formats that do not include explicit Note Off events. - byte _hanging_notes_count; ///< Count of hanging notes, used to optimize expiration. + byte _hangingNotesCount; ///< Count of hanging notes, used to optimize expiration. MidiDriver_BASE *_driver; ///< The device to which all events will be transmitted. - uint32 _timer_rate; ///< The time in microseconds between onTimer() calls. Obtained from the MidiDriver. + uint32 _timerRate; ///< The time in microseconds between onTimer() calls. Obtained from the MidiDriver. uint32 _ppqn; ///< Pulses Per Quarter Note. (We refer to "pulses" as "ticks".) uint32 _tempo; ///< Microseconds per quarter note. - uint32 _psec_per_tick; ///< Microseconds per tick (_tempo / _ppqn). + uint32 _psecPerTick; ///< Microseconds per tick (_tempo / _ppqn). bool _autoLoop; ///< For lightweight clients that don't provide their own flow control. bool _smartJump; ///< Support smart expiration of hanging notes when jumping bool _centerPitchWheelOnUnload; ///< Center the pitch wheels when unloading a song bool _sendSustainOffOnNotesOff; ///< Send a sustain off on a notes off event, stopping hanging notes byte *_tracks[120]; ///< Multi-track MIDI formats are supported, up to 120 tracks. - byte _num_tracks; ///< Count of total tracks for multi-track MIDI formats. 1 for single-track formats. - byte _active_track; ///< Keeps track of the currently active track, in multi-track formats. + byte _numTracks; ///< Count of total tracks for multi-track MIDI formats. 1 for single-track formats. + byte _activeTrack; ///< Keeps track of the currently active track, in multi-track formats. Tracker _position; ///< The current time/position in the active track. - EventInfo _next_event; ///< The next event to transmit. Events are preparsed + EventInfo _nextEvent; ///< The next event to transmit. Events are preparsed ///< so each event is parsed only once; this permits ///< simulated events in certain formats. - bool _abort_parse; ///< If a jump or other operation interrupts parsing, flag to abort. + bool _abortParse; ///< If a jump or other operation interrupts parsing, flag to abort. protected: static uint32 readVLQ(byte * &data); @@ -295,7 +295,7 @@ protected: virtual void parseNextEvent(EventInfo &info) = 0; void activeNote(byte channel, byte note, bool active); - void hangingNote(byte channel, byte note, uint32 ticks_left, bool recycle = true); + void hangingNote(byte channel, byte note, uint32 ticksLeft, bool recycle = true); void hangAllActiveNotes(); virtual void sendToDriver(uint32 b); @@ -377,23 +377,24 @@ public: virtual void property(int prop, int value); void setMidiDriver(MidiDriver_BASE *driver) { _driver = driver; } - void setTimerRate(uint32 rate) { _timer_rate = rate; } + void setTimerRate(uint32 rate) { _timerRate = rate; } void setTempo(uint32 tempo); void onTimer(); - bool isPlaying() const { return (_position._play_pos != 0); } + bool isPlaying() const { return (_position._playPos != 0); } void stopPlaying(); bool setTrack(int track); bool jumpToTick(uint32 tick, bool fireEvents = false, bool stopNotes = true, bool dontSendNoteOn = false); uint32 getPPQN() { return _ppqn; } - virtual uint32 getTick() { return _position._play_tick; } + virtual uint32 getTick() { return _position._playTick; } static void defaultXMidiCallback(byte eventData, void *refCon); static MidiParser *createParser_SMF(); static MidiParser *createParser_XMIDI(XMidiCallbackProc proc = defaultXMidiCallback, void *refCon = 0); + static MidiParser *createParser_QT(); static void timerCallback(void *data) { ((MidiParser *) data)->onTimer(); } }; diff --git a/audio/midiparser_qt.cpp b/audio/midiparser_qt.cpp new file mode 100644 index 0000000000..6214d28f95 --- /dev/null +++ b/audio/midiparser_qt.cpp @@ -0,0 +1,496 @@ +/* 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 "audio/midiparser_qt.h" +#include "common/debug.h" +#include "common/memstream.h" + +bool MidiParser_QT::loadMusic(byte *data, uint32 size) { + if (size < 8) + return false; + + Common::SeekableReadStream *stream = new Common::MemoryReadStream(data, size, DisposeAfterUse::NO); + + // Attempt to detect what format we have + bool result; + if (READ_BE_UINT32(data + 4) == MKTAG('m', 'u', 's', 'i')) + result = loadFromTune(stream); + else + result = loadFromContainerStream(stream); + + if (!result) { + delete stream; + return false; + } + + return true; +} + +void MidiParser_QT::unloadMusic() { + MidiParser::unloadMusic(); + close(); + + // Unlike those lesser formats, we *do* hold track data + for (uint i = 0; i < _trackInfo.size(); i++) + free(_trackInfo[i].data); + + _trackInfo.clear(); +} + +bool MidiParser_QT::loadFromTune(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) { + unloadMusic(); + + // a tune starts off with a sample description + stream->readUint32BE(); // header size + + if (stream->readUint32BE() != MKTAG('m', 'u', 's', 'i')) + return false; + + stream->readUint32BE(); // reserved + stream->readUint16BE(); // reserved + stream->readUint16BE(); // index + + stream->readUint32BE(); // flags, ignore + + MIDITrackInfo trackInfo; + trackInfo.size = stream->size() - stream->pos(); + assert(trackInfo.size > 0); + + trackInfo.data = (byte *)malloc(trackInfo.size); + stream->read(trackInfo.data, trackInfo.size); + + trackInfo.timeScale = 600; // the default + _trackInfo.push_back(trackInfo); + + initCommon(); + return true; +} + +bool MidiParser_QT::loadFromContainerStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) { + unloadMusic(); + + if (!parseStream(stream, disposeAfterUse)) + return false; + + initFromContainerTracks(); + return true; +} + +bool MidiParser_QT::loadFromContainerFile(const Common::String &fileName) { + unloadMusic(); + + if (!parseFile(fileName)) + return false; + + initFromContainerTracks(); + return true; +} + +void MidiParser_QT::parseNextEvent(EventInfo &info) { + uint32 delta = 0; + + while (_queuedEvents.empty()) + delta += readNextEvent(); + + info = _queuedEvents.pop(); + info.delta = delta; +} + +uint32 MidiParser_QT::readNextEvent() { + if (_position._playPos >= _trackInfo[_activeTrack].data + _trackInfo[_activeTrack].size) { + // Manually insert end of track when we reach the end + EventInfo info; + info.event = 0xFF; + info.ext.type = 0x2F; + _queuedEvents.push(info); + return 0; + } + + uint32 control = readUint32(); + + switch (control >> 28) { + case 0x0: + case 0x1: + // Rest + // We handle this by recursively adding up all the rests into the + // next event's delta + return readNextEvent() + (control & 0xFFFFFF); + case 0x2: + case 0x3: + // Note event + handleNoteEvent((control >> 24) & 0x1F, ((control >> 18) & 0x3F) + 32, (control >> 11) & 0x7F, control & 0x7FF); + break; + case 0x4: + case 0x5: + // Controller + handleControllerEvent((control >> 16) & 0xFF, (control >> 24) & 0x1F, (control >> 8) & 0xFF, control & 0xFF); + break; + case 0x6: + case 0x7: + // Marker + // Used for editing only, so we don't need to care about this + break; + case 0x9: { + // Extended note event + uint32 extra = readUint32(); + handleNoteEvent((control >> 16) & 0xFFF, (control >> 8) & 0xFF, (extra >> 22) & 0x7F, extra & 0x3FFFFF); + break; + } + case 0xA: { + // Extended controller + uint32 extra = readUint32(); + handleControllerEvent((extra >> 16) & 0x3FFF, (control >> 16) & 0xFFF, (extra >> 8) & 0xFF, extra & 0xFF); + break; + } + case 0xB: + // Knob + error("Encountered knob event in QuickTime MIDI"); + break; + case 0x8: + case 0xC: + case 0xD: + case 0xE: + // Reserved + readUint32(); + break; + case 0xF: + // General + handleGeneralEvent(control); + break; + } + + return 0; +} + +void MidiParser_QT::handleNoteEvent(uint32 part, byte pitch, byte velocity, uint32 length) { + byte channel = getChannel(part); + + EventInfo info; + info.event = 0x90 | channel; + info.basic.param1 = pitch; + info.basic.param2 = velocity; + info.length = (velocity == 0) ? 0 : length; + _queuedEvents.push(info); +} + +void MidiParser_QT::handleControllerEvent(uint32 control, uint32 part, byte intPart, byte fracPart) { + byte channel = getChannel(part); + EventInfo info; + + if (control == 0) { + // "Bank select" + // QuickTime docs don't list this, but IHNM Mac calls this anyway + // We have to ignore this. + return; + } else if (control == 32) { + // Pitch bend + info.event = 0xE0 | channel; + + // Actually an 8.8 fixed point number + int16 value = (int16)((intPart << 8) | fracPart); + + if (value < -0x200 || value > 0x1FF) { + warning("QuickTime MIDI pitch bend value (%d) out of range, clipping", value); + value = CLIP<int16>(value, -0x200, 0x1FF); + } + + // Now convert the value to 'normal' MIDI values + value += 0x200; + value *= 16; + + // param1 holds the low 7 bits, param2 holds the high 7 bits + info.basic.param1 = value & 0x7F; + info.basic.param2 = value >> 7; + + _partMap[part].pitchBend = value; + } else { + // Regular controller + info.event = 0xB0 | channel; + info.basic.param1 = control; + info.basic.param2 = intPart; + + // TODO: Parse more controls to hold their status + switch (control) { + case 7: + _partMap[part].volume = intPart; + break; + case 10: + _partMap[part].pan = intPart; + break; + } + } + + _queuedEvents.push(info); +} + +void MidiParser_QT::handleGeneralEvent(uint32 control) { + uint32 part = (control >> 16) & 0xFFF; + uint32 dataSize = ((control & 0xFFFF) - 2) * 4; + byte subType = READ_BE_UINT16(_position._playPos + dataSize) & 0x3FFF; + + switch (subType) { + case 1: + // Note Request + // Currently we're only using the GM number from the request + assert(dataSize == 84); + + // We have to remap channels because GM needs percussion to be on the + // percussion channel but QuickTime can have that anywhere. + definePart(part, READ_BE_UINT32(_position._playPos + 80)); + break; + case 5: // Tune Difference + case 8: // MIDI Channel + case 10: // No-op + case 11: // Used Notes + // Should be safe to skip these + break; + default: + warning("Unhandled general event %d", subType); + } + + _position._playPos += dataSize + 4; +} + +void MidiParser_QT::definePart(uint32 part, uint32 instrument) { + if (_partMap.contains(part)) + warning("QuickTime MIDI part %d being redefined", part); + + PartStatus partStatus; + partStatus.instrument = instrument; + partStatus.volume = 127; + partStatus.pan = 64; + partStatus.pitchBend = 0x2000; + _partMap[part] = partStatus; +} + +byte MidiParser_QT::getChannel(uint32 part) { + // If we already mapped it, just go with it + if (!_channelMap.contains(part)) { + byte newChannel = findFreeChannel(part); + _channelMap[part] = newChannel; + setupPart(part); + } + + return _channelMap[part]; +} + +byte MidiParser_QT::findFreeChannel(uint32 part) { + if (_partMap[part].instrument != 0x4001) { + // Normal Instrument -> First Free Channel + if (allChannelsAllocated()) + deallocateFreeChannel(); + + for (int i = 0; i < 16; i++) + if (i != 9 && !isChannelAllocated(i)) // 9 is reserved for Percussion + return i; + + // Can't actually get here + } + + // Drum Kit -> Percussion Channel + deallocateChannel(9); + return 9; +} + +void MidiParser_QT::deallocateFreeChannel() { + for (int i = 0; i < 16; i++) { + if (i != 9 && !_activeNotes[i]) { + // TODO: Improve this by looking for the channel with the longest + // time since the last note. + deallocateChannel(i); + return; + } + } + + error("Exceeded QuickTime MIDI channel polyphony"); +} + +void MidiParser_QT::deallocateChannel(byte channel) { + for (ChannelMap::iterator it = _channelMap.begin(); it != _channelMap.end(); it++) { + if (it->_value == channel) { + _channelMap.erase(it); + return; + } + } +} + +bool MidiParser_QT::isChannelAllocated(byte channel) const { + for (ChannelMap::const_iterator it = _channelMap.begin(); it != _channelMap.end(); it++) + if (it->_value == channel) + return true; + + return false; +} + +bool MidiParser_QT::allChannelsAllocated() const { + // Less than 15? We definitely have room + if (_channelMap.size() < 15) + return false; + + // 15? One of the allocated channels might be the percussion one + if (_channelMap.size() == 15) + for (ChannelMap::const_iterator it = _channelMap.begin(); it != _channelMap.end(); it++) + if (it->_value == 9) + return false; + + // 16 -> definitely all allocated + return true; +} + +void MidiParser_QT::setupPart(uint32 part) { + PartStatus &status = _partMap[part]; + byte channel = _channelMap[part]; + EventInfo info; + + // First, the program change + if (channel != 9) { + // 9 is always percussion + info.event = 0xC0 | channel; + info.basic.param1 = status.instrument; + _queuedEvents.push(info); + } + + // Volume + info.event = 0xB0 | channel; + info.basic.param1 = 7; + info.basic.param2 = status.volume; + _queuedEvents.push(info); + + // Pan + info.event = 0xB0 | channel; + info.basic.param1 = 10; + info.basic.param2 = status.pan; + _queuedEvents.push(info); + + // Pitch Bend + info.event = 0xE0 | channel; + info.basic.param1 = status.pitchBend & 0x7F; + info.basic.param2 = status.pitchBend >> 7; + _queuedEvents.push(info); +} + +void MidiParser_QT::resetTracking() { + MidiParser::resetTracking(); + _channelMap.clear(); + _queuedEvents.clear(); + _partMap.clear(); +} + +Common::QuickTimeParser::SampleDesc *MidiParser_QT::readSampleDesc(Track *track, uint32 format, uint32 descSize) { + if (track->codecType == CODEC_TYPE_MIDI) { + debug(0, "MIDI Codec FourCC '%s'", tag2str(format)); + + _fd->readUint32BE(); // flags, ignore + descSize -= 4; + + MIDISampleDesc *entry = new MIDISampleDesc(track, format); + entry->_requestSize = descSize; + entry->_requestData = (byte *)malloc(descSize); + _fd->read(entry->_requestData, descSize); + return entry; + } + + return 0; +} + +MidiParser_QT::MIDISampleDesc::MIDISampleDesc(Common::QuickTimeParser::Track *parentTrack, uint32 codecTag) : + Common::QuickTimeParser::SampleDesc(parentTrack, codecTag) { +} + +void MidiParser_QT::initFromContainerTracks() { + const Common::Array<Common::QuickTimeParser::Track *> &tracks = Common::QuickTimeParser::_tracks; + + for (uint32 i = 0; i < tracks.size(); i++) { + if (tracks[i]->codecType == CODEC_TYPE_MIDI) { + assert(tracks[i]->sampleDescs.size() == 1); + + if (tracks[i]->editCount != 1) + warning("Unhandled QuickTime MIDI edit lists, things may go awry"); + + MIDITrackInfo trackInfo; + trackInfo.data = readWholeTrack(tracks[i], trackInfo.size); + trackInfo.timeScale = tracks[i]->timeScale; + _trackInfo.push_back(trackInfo); + } + } + + initCommon(); +} + +void MidiParser_QT::initCommon() { + // Now we have all our info needed in _trackInfo from whatever container + // form, we can fill in the MidiParser tracks. + + _numTracks = _trackInfo.size(); + assert(_numTracks > 0); + + for (uint32 i = 0; i < _trackInfo.size(); i++) + MidiParser::_tracks[i] = _trackInfo[i].data; + + _ppqn = _trackInfo[0].timeScale; + resetTracking(); + setTempo(1000000); + setTrack(0); +} + +byte *MidiParser_QT::readWholeTrack(Common::QuickTimeParser::Track *track, uint32 &trackSize) { + // This just goes through all chunks and appends them together + + Common::MemoryWriteStreamDynamic output; + uint32 curSample = 0; + + // Read in the note request data first + MIDISampleDesc *entry = (MIDISampleDesc *)track->sampleDescs[0]; + output.write(entry->_requestData, entry->_requestSize); + + for (uint i = 0; i < track->chunkCount; i++) { + _fd->seek(track->chunkOffsets[i]); + + uint32 sampleCount = 0; + + for (uint32 j = 0; j < track->sampleToChunkCount; j++) + if (i >= track->sampleToChunk[j].first) + sampleCount = track->sampleToChunk[j].count; + + for (uint32 j = 0; j < sampleCount; j++, curSample++) { + uint32 size = (track->sampleSize != 0) ? track->sampleSize : track->sampleSizes[curSample]; + + byte *data = new byte[size]; + _fd->read(data, size); + output.write(data, size); + delete[] data; + } + } + + trackSize = output.size(); + return output.getData(); +} + +uint32 MidiParser_QT::readUint32() { + uint32 value = READ_BE_UINT32(_position._playPos); + _position._playPos += 4; + return value; +} + +MidiParser *MidiParser::createParser_QT() { + return new MidiParser_QT(); +} diff --git a/audio/midiparser_qt.h b/audio/midiparser_qt.h new file mode 100644 index 0000000000..d6d0f40a48 --- /dev/null +++ b/audio/midiparser_qt.h @@ -0,0 +1,134 @@ +/* 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 AUDIO_MIDIPARSER_QT_H +#define AUDIO_MIDIPARSER_QT_H + +#include "audio/midiparser.h" +#include "common/array.h" +#include "common/hashmap.h" +#include "common/queue.h" +#include "common/quicktime.h" + +/** + * The QuickTime Music version of MidiParser. + * + * QuickTime Music is actually a superset of MIDI. It has its own custom + * instruments and supports more than 15 non-percussion channels. It also + * has custom control changes and a more advanced pitch bend (which we + * convert to GM pitch bend as best as possible). We then use the fallback + * GM instrument that each QuickTime instrument definition has to provide. + * + * Furthermore, Apple's documentation on this is terrible. You know + * documentation is bad when it contradicts itself three times on the same + * subject (like about setting the GM instrument field to percussion). + * + * This is as close to a proper QuickTime Music parser as we can currently + * implement using our MidiParser interface. + */ +class MidiParser_QT : public MidiParser, public Common::QuickTimeParser { +public: + MidiParser_QT() {} + ~MidiParser_QT() {} + + // MidiParser + bool loadMusic(byte *data, uint32 size); + void unloadMusic(); + + /** + * Load the MIDI from a 'Tune' resource + */ + bool loadFromTune(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES); + + /** + * Load the MIDI from a QuickTime stream + */ + bool loadFromContainerStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES); + + /** + * Load the MIDI from a QuickTime file + */ + bool loadFromContainerFile(const Common::String &fileName); + +protected: + // MidiParser + void parseNextEvent(EventInfo &info); + void resetTracking(); + + // QuickTimeParser + SampleDesc *readSampleDesc(Track *track, uint32 format, uint32 descSize); + +private: + struct MIDITrackInfo { + byte *data; + uint32 size; + uint32 timeScale; + }; + + struct PartStatus { + uint32 instrument; + byte volume; + byte pan; + uint16 pitchBend; + }; + + class MIDISampleDesc : public SampleDesc { + public: + MIDISampleDesc(Common::QuickTimeParser::Track *parentTrack, uint32 codecTag); + ~MIDISampleDesc() {} + + byte *_requestData; + uint32 _requestSize; + }; + + uint32 readNextEvent(); + void handleGeneralEvent(uint32 control); + void handleControllerEvent(uint32 control, uint32 part, byte intPart, byte fracPart); + void handleNoteEvent(uint32 part, byte pitch, byte velocity, uint32 length); + + void definePart(uint32 part, uint32 instrument); + void setupPart(uint32 part); + + byte getChannel(uint32 part); + bool isChannelAllocated(byte channel) const; + byte findFreeChannel(uint32 part); + void deallocateFreeChannel(); + void deallocateChannel(byte channel); + bool allChannelsAllocated() const; + + byte *readWholeTrack(Common::QuickTimeParser::Track *track, uint32 &trackSize); + + Common::Array<MIDITrackInfo> _trackInfo; + Common::Queue<EventInfo> _queuedEvents; + + typedef Common::HashMap<uint, PartStatus> PartMap; + PartMap _partMap; + + typedef Common::HashMap<uint, byte> ChannelMap; + ChannelMap _channelMap; + + void initFromContainerTracks(); + void initCommon(); + uint32 readUint32(); +}; + +#endif diff --git a/audio/midiparser_smf.cpp b/audio/midiparser_smf.cpp index e883471b54..4b0913cbfe 100644 --- a/audio/midiparser_smf.cpp +++ b/audio/midiparser_smf.cpp @@ -45,8 +45,8 @@ public: }; -static const byte command_lengths[8] = { 3, 3, 3, 3, 2, 2, 3, 0 }; -static const byte special_lengths[16] = { 0, 2, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 }; +static const byte commandLengths[8] = { 3, 3, 3, 3, 2, 2, 3, 0 }; +static const byte specialLengths[16] = { 0, 2, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 }; MidiParser_SMF::~MidiParser_SMF() { free(_buffer); @@ -62,8 +62,8 @@ void MidiParser_SMF::property(int prop, int value) { } void MidiParser_SMF::parseNextEvent(EventInfo &info) { - info.start = _position._play_pos; - info.delta = readVLQ(_position._play_pos); + info.start = _position._playPos; + info.delta = readVLQ(_position._playPos); // Process the next info. If mpMalformedPitchBends // was set, we must skip over any pitch bend events @@ -71,19 +71,19 @@ void MidiParser_SMF::parseNextEvent(EventInfo &info) { // real pitch bend events, they're just two-byte // prefixes before the real info. do { - if ((_position._play_pos[0] & 0xF0) >= 0x80) - info.event = *(_position._play_pos++); + if ((_position._playPos[0] & 0xF0) >= 0x80) + info.event = *(_position._playPos++); else - info.event = _position._running_status; - } while (_malformedPitchBends && (info.event & 0xF0) == 0xE0 && _position._play_pos++); + info.event = _position._runningStatus; + } while (_malformedPitchBends && (info.event & 0xF0) == 0xE0 && _position._playPos++); if (info.event < 0x80) return; - _position._running_status = info.event; + _position._runningStatus = info.event; switch (info.command()) { case 0x9: // Note On - info.basic.param1 = *(_position._play_pos++); - info.basic.param2 = *(_position._play_pos++); + info.basic.param1 = *(_position._playPos++); + info.basic.param2 = *(_position._playPos++); if (info.basic.param2 == 0) info.event = info.channel() | 0x80; info.length = 0; @@ -91,7 +91,7 @@ void MidiParser_SMF::parseNextEvent(EventInfo &info) { case 0xC: case 0xD: - info.basic.param1 = *(_position._play_pos++); + info.basic.param1 = *(_position._playPos++); info.basic.param2 = 0; break; @@ -99,20 +99,20 @@ void MidiParser_SMF::parseNextEvent(EventInfo &info) { case 0xA: case 0xB: case 0xE: - info.basic.param1 = *(_position._play_pos++); - info.basic.param2 = *(_position._play_pos++); + info.basic.param1 = *(_position._playPos++); + info.basic.param2 = *(_position._playPos++); info.length = 0; break; case 0xF: // System Common, Meta or SysEx event switch (info.event & 0x0F) { case 0x2: // Song Position Pointer - info.basic.param1 = *(_position._play_pos++); - info.basic.param2 = *(_position._play_pos++); + info.basic.param1 = *(_position._playPos++); + info.basic.param2 = *(_position._playPos++); break; case 0x3: // Song Select - info.basic.param1 = *(_position._play_pos++); + info.basic.param1 = *(_position._playPos++); info.basic.param2 = 0; break; @@ -126,16 +126,16 @@ void MidiParser_SMF::parseNextEvent(EventInfo &info) { break; case 0x0: // SysEx - info.length = readVLQ(_position._play_pos); - info.ext.data = _position._play_pos; - _position._play_pos += info.length; + info.length = readVLQ(_position._playPos); + info.ext.data = _position._playPos; + _position._playPos += info.length; break; case 0xF: // META event - info.ext.type = *(_position._play_pos++); - info.length = readVLQ(_position._play_pos); - info.ext.data = _position._play_pos; - _position._play_pos += info.length; + info.ext.type = *(_position._playPos++); + info.length = readVLQ(_position._playPos); + info.ext.data = _position._playPos; + _position._playPos += info.length; break; default: @@ -146,8 +146,8 @@ void MidiParser_SMF::parseNextEvent(EventInfo &info) { bool MidiParser_SMF::loadMusic(byte *data, uint32 size) { uint32 len; - byte midi_type; - uint32 total_size; + byte midiType; + uint32 totalSize; bool isGMF; unloadMusic(); @@ -171,10 +171,10 @@ bool MidiParser_SMF::loadMusic(byte *data, uint32 size) { // Verify that this MIDI either is a Type 2 // or has only 1 track. We do not support // multitrack Type 1 files. - _num_tracks = pos[2] << 8 | pos[3]; - midi_type = pos[1]; - if (midi_type > 2 /*|| (midi_type < 2 && _num_tracks > 1)*/) { - warning("No support for a Type %d MIDI with %d tracks", (int)midi_type, (int)_num_tracks); + _numTracks = pos[2] << 8 | pos[3]; + midiType = pos[1]; + if (midiType > 2 /*|| (midiType < 2 && _numTracks > 1)*/) { + warning("No support for a Type %d MIDI with %d tracks", (int)midiType, (int)_numTracks); return false; } _ppqn = pos[4] << 8 | pos[5]; @@ -183,8 +183,8 @@ bool MidiParser_SMF::loadMusic(byte *data, uint32 size) { // Older GMD/MUS file with no header info. // Assume 1 track, 192 PPQN, and no MTrk headers. isGMF = true; - midi_type = 0; - _num_tracks = 1; + midiType = 0; + _numTracks = 1; _ppqn = 192; pos += 7; // 'GMD\x1' + 3 bytes of useless (translate: unknown) information } else { @@ -193,14 +193,14 @@ bool MidiParser_SMF::loadMusic(byte *data, uint32 size) { } // Now we identify and store the location for each track. - if (_num_tracks > ARRAYSIZE(_tracks)) { - warning("Can only handle %d tracks but was handed %d", (int)ARRAYSIZE(_tracks), (int)_num_tracks); + if (_numTracks > ARRAYSIZE(_tracks)) { + warning("Can only handle %d tracks but was handed %d", (int)ARRAYSIZE(_tracks), (int)_numTracks); return false; } - total_size = 0; - int tracks_read = 0; - while (tracks_read < _num_tracks) { + totalSize = 0; + int tracksRead = 0; + while (tracksRead < _numTracks) { if (memcmp(pos, "MTrk", 4) && !isGMF) { warning("Position: %p ('%c')", pos, *pos); warning("Hit invalid block '%c%c%c%c' while scanning for track locations", pos[0], pos[1], pos[2], pos[3]); @@ -208,11 +208,11 @@ bool MidiParser_SMF::loadMusic(byte *data, uint32 size) { } // If needed, skip the MTrk and length bytes - _tracks[tracks_read] = pos + (isGMF ? 0 : 8); + _tracks[tracksRead] = pos + (isGMF ? 0 : 8); if (!isGMF) { pos += 4; len = read4high(pos); - total_size += len; + totalSize += len; pos += len; } else { // An SMF End of Track meta event must be placed @@ -222,7 +222,7 @@ bool MidiParser_SMF::loadMusic(byte *data, uint32 size) { data[size++] = 0x00; data[size++] = 0x00; } - ++tracks_read; + ++tracksRead; } // If this is a Type 1 MIDI, we need to now compress @@ -230,13 +230,13 @@ bool MidiParser_SMF::loadMusic(byte *data, uint32 size) { free(_buffer); _buffer = 0; - if (midi_type == 1) { + if (midiType == 1) { // FIXME: Doubled the buffer size to prevent crashes with the // Inherit the Earth MIDIs. Jamieson630 said something about a // better fix, but this will have to do in the meantime. _buffer = (byte *)malloc(size * 2); compressToType0(); - _num_tracks = 1; + _numTracks = 1; _tracks[0] = _buffer; } @@ -253,48 +253,48 @@ void MidiParser_SMF::compressToType0() { // We assume that _buffer has been allocated // to sufficient size for this operation. - // using 0xFF since it could write track_pos[0 to _num_tracks] here + // using 0xFF since it could write trackPos[0 to _numTracks] here // this would cause some illegal writes and could lead to segfaults // (it crashed for some midis for me, they're not used in any game // scummvm supports though). *Maybe* handle this in another way, // it's at the moment only to be sure, that nothing goes wrong. - byte *track_pos[0xFF]; - byte running_status[0xFF]; - uint32 track_timer[0xFF]; + byte *trackPos[0xFF]; + byte runningStatus[0xFF]; + uint32 trackTimer[0xFF]; uint32 delta; int i; - for (i = 0; i < _num_tracks; ++i) { - running_status[i] = 0; - track_pos[i] = _tracks[i]; - track_timer[i] = readVLQ(track_pos[i]); - running_status[i] = 0; + for (i = 0; i < _numTracks; ++i) { + runningStatus[i] = 0; + trackPos[i] = _tracks[i]; + trackTimer[i] = readVLQ(trackPos[i]); + runningStatus[i] = 0; } - int best_i; + int bestTrack; uint32 length; byte *output = _buffer; byte *pos, *pos2; byte event; - uint32 copy_bytes; + uint32 copyBytes; bool write; - byte active_tracks = (byte)_num_tracks; + byte activeTracks = (byte)_numTracks; - while (active_tracks) { + while (activeTracks) { write = true; - best_i = 255; - for (i = 0; i < _num_tracks; ++i) { - if (track_pos[i] && (best_i == 255 || track_timer[i] < track_timer[best_i])) - best_i = i; + bestTrack = 255; + for (i = 0; i < _numTracks; ++i) { + if (trackPos[i] && (bestTrack == 255 || trackTimer[i] < trackTimer[bestTrack])) + bestTrack = i; } - if (best_i == 255) { + if (bestTrack == 255) { warning("Premature end of tracks"); break; } // Initial VLQ delta computation delta = 0; - length = track_timer[best_i]; + length = trackTimer[bestTrack]; for (i = 0; length; ++i) { delta = (delta << 8) | (length & 0x7F) | (i ? 0x80 : 0); length >>= 7; @@ -302,55 +302,55 @@ void MidiParser_SMF::compressToType0() { // Process MIDI event. bool implicitEvent = false; - copy_bytes = 0; - pos = track_pos[best_i]; + copyBytes = 0; + pos = trackPos[bestTrack]; do { event = *(pos++); if (event < 0x80) { - event = running_status[best_i]; + event = runningStatus[bestTrack]; implicitEvent = true; } } while (_malformedPitchBends && (event & 0xF0) == 0xE0 && pos++); - running_status[best_i] = event; + runningStatus[bestTrack] = event; - if (command_lengths[(event >> 4) - 8] > 0) { - copy_bytes = command_lengths[(event >> 4) - 8]; - } else if (special_lengths[(event & 0x0F)] > 0) { - copy_bytes = special_lengths[(event & 0x0F)]; + if (commandLengths[(event >> 4) - 8] > 0) { + copyBytes = commandLengths[(event >> 4) - 8]; + } else if (specialLengths[(event & 0x0F)] > 0) { + copyBytes = specialLengths[(event & 0x0F)]; } else if (event == 0xF0) { // SysEx pos2 = pos; length = readVLQ(pos); - copy_bytes = 1 + (pos - pos2) + length; + copyBytes = 1 + (pos - pos2) + length; } else if (event == 0xFF) { // META event = *(pos++); - if (event == 0x2F && active_tracks > 1) { - track_pos[best_i] = 0; + if (event == 0x2F && activeTracks > 1) { + trackPos[bestTrack] = 0; write = false; } else { pos2 = pos; length = readVLQ(pos); - copy_bytes = 2 + (pos - pos2) + length; + copyBytes = 2 + (pos - pos2) + length; } if (event == 0x2F) - --active_tracks; + --activeTracks; } else { warning("Bad MIDI command %02X", (int)event); - track_pos[best_i] = 0; + trackPos[bestTrack] = 0; } // Update all tracks' deltas if (write) { - for (i = 0; i < _num_tracks; ++i) { - if (track_pos[i] && i != best_i) - track_timer[i] -= track_timer[best_i]; + for (i = 0; i < _numTracks; ++i) { + if (trackPos[i] && i != bestTrack) + trackTimer[i] -= trackTimer[bestTrack]; } } - if (track_pos[best_i]) { + if (trackPos[bestTrack]) { if (write) { - track_timer[best_i] = 0; + trackTimer[bestTrack] = 0; // Write VLQ delta while (delta & 0x80) { @@ -361,17 +361,17 @@ void MidiParser_SMF::compressToType0() { // Write MIDI data if (!implicitEvent) - ++track_pos[best_i]; - --copy_bytes; - *output++ = running_status[best_i]; - memcpy(output, track_pos[best_i], copy_bytes); - output += copy_bytes; + ++trackPos[bestTrack]; + --copyBytes; + *output++ = runningStatus[bestTrack]; + memcpy(output, trackPos[bestTrack], copyBytes); + output += copyBytes; } // Fetch new VLQ delta for winning track - track_pos[best_i] += copy_bytes; - if (active_tracks) - track_timer[best_i] += readVLQ(track_pos[best_i]); + trackPos[bestTrack] += copyBytes; + if (activeTracks) + trackTimer[bestTrack] += readVLQ(trackPos[bestTrack]); } } diff --git a/audio/midiparser_xmidi.cpp b/audio/midiparser_xmidi.cpp index 85491faaf8..11690b0214 100644 --- a/audio/midiparser_xmidi.cpp +++ b/audio/midiparser_xmidi.cpp @@ -32,9 +32,6 @@ */ class MidiParser_XMIDI : public MidiParser { protected: - NoteTimer _notes_cache[32]; - uint32 _inserted_delta; // Track simulated deltas for note-off events - struct Loop { byte *pos; byte repeat; @@ -48,11 +45,10 @@ protected: protected: uint32 readVLQ2(byte * &data); - void resetTracking(); void parseNextEvent(EventInfo &info); public: - MidiParser_XMIDI(XMidiCallbackProc proc, void *data) : _inserted_delta(0), _callbackProc(proc), _callbackData(data) {} + MidiParser_XMIDI(XMidiCallbackProc proc, void *data) : _callbackProc(proc), _callbackData(data) {} ~MidiParser_XMIDI() { } bool loadMusic(byte *data, uint32 size); @@ -69,17 +65,16 @@ uint32 MidiParser_XMIDI::readVLQ2(byte * &pos) { } void MidiParser_XMIDI::parseNextEvent(EventInfo &info) { - info.start = _position._play_pos; - info.delta = readVLQ2(_position._play_pos) - _inserted_delta; + info.start = _position._playPos; + info.delta = readVLQ2(_position._playPos); // Process the next event. - _inserted_delta = 0; - info.event = *(_position._play_pos++); + info.event = *(_position._playPos++); switch (info.event >> 4) { case 0x9: // Note On - info.basic.param1 = *(_position._play_pos++); - info.basic.param2 = *(_position._play_pos++); - info.length = readVLQ(_position._play_pos); + info.basic.param1 = *(_position._playPos++); + info.basic.param2 = *(_position._playPos++); + info.length = readVLQ(_position._playPos); if (info.basic.param2 == 0) { info.event = info.channel() | 0x80; info.length = 0; @@ -88,20 +83,20 @@ void MidiParser_XMIDI::parseNextEvent(EventInfo &info) { case 0xC: case 0xD: - info.basic.param1 = *(_position._play_pos++); + info.basic.param1 = *(_position._playPos++); info.basic.param2 = 0; break; case 0x8: case 0xA: case 0xE: - info.basic.param1 = *(_position._play_pos++); - info.basic.param2 = *(_position._play_pos++); + info.basic.param1 = *(_position._playPos++); + info.basic.param2 = *(_position._playPos++); break; case 0xB: - info.basic.param1 = *(_position._play_pos++); - info.basic.param2 = *(_position._play_pos++); + info.basic.param1 = *(_position._playPos++); + info.basic.param2 = *(_position._playPos++); // This isn't a full XMIDI implementation, but it should // hopefully be "good enough" for most things. @@ -109,7 +104,7 @@ void MidiParser_XMIDI::parseNextEvent(EventInfo &info) { switch (info.basic.param1) { // Simplified XMIDI looping. case 0x74: { // XMIDI_CONTROLLER_FOR_LOOP - byte *pos = _position._play_pos; + byte *pos = _position._playPos; if (_loopCount < ARRAYSIZE(_loop) - 1) _loopCount++; else @@ -131,9 +126,9 @@ void MidiParser_XMIDI::parseNextEvent(EventInfo &info) { if (--_loop[_loopCount].repeat == 0) _loopCount--; else - _position._play_pos = _loop[_loopCount].pos; + _position._playPos = _loop[_loopCount].pos; } else { - _position._play_pos = _loop[_loopCount].pos; + _position._playPos = _loop[_loopCount].pos; } } } @@ -169,12 +164,12 @@ void MidiParser_XMIDI::parseNextEvent(EventInfo &info) { case 0xF: // Meta or SysEx event switch (info.event & 0x0F) { case 0x2: // Song Position Pointer - info.basic.param1 = *(_position._play_pos++); - info.basic.param2 = *(_position._play_pos++); + info.basic.param1 = *(_position._playPos++); + info.basic.param2 = *(_position._playPos++); break; case 0x3: // Song Select - info.basic.param1 = *(_position._play_pos++); + info.basic.param1 = *(_position._playPos++); info.basic.param2 = 0; break; @@ -188,16 +183,16 @@ void MidiParser_XMIDI::parseNextEvent(EventInfo &info) { break; case 0x0: // SysEx - info.length = readVLQ(_position._play_pos); - info.ext.data = _position._play_pos; - _position._play_pos += info.length; + info.length = readVLQ(_position._playPos); + info.ext.data = _position._playPos; + _position._playPos += info.length; break; case 0xF: // META event - info.ext.type = *(_position._play_pos++); - info.length = readVLQ(_position._play_pos); - info.ext.data = _position._play_pos; - _position._play_pos += info.length; + info.ext.type = *(_position._playPos++); + info.length = readVLQ(_position._playPos); + info.ext.data = _position._playPos; + _position._playPos += info.length; if (info.ext.type == 0x51 && info.length == 3) { // Tempo event. We want to make these constant 500,000. info.ext.data[0] = 0x07; @@ -216,7 +211,7 @@ bool MidiParser_XMIDI::loadMusic(byte *data, uint32 size) { uint32 i = 0; byte *start; uint32 len; - uint32 chunk_len; + uint32 chunkLen; char buf[32]; _loopCount = -1; @@ -235,7 +230,7 @@ bool MidiParser_XMIDI::loadMusic(byte *data, uint32 size) { if (!memcmp(pos, "XMID", 4)) { warning("XMIDI doesn't have XDIR"); pos += 4; - _num_tracks = 1; + _numTracks = 1; } else if (memcmp(pos, "XDIR", 4)) { // Not an XMIDI that we recognize warning("Expected 'XDIR' but found '%c%c%c%c'", pos[0], pos[1], pos[2], pos[3]); @@ -243,7 +238,7 @@ bool MidiParser_XMIDI::loadMusic(byte *data, uint32 size) { } else { // Seems Valid pos += 4; - _num_tracks = 0; + _numTracks = 0; for (i = 4; i < len; i++) { // Read 4 bytes of type @@ -251,34 +246,34 @@ bool MidiParser_XMIDI::loadMusic(byte *data, uint32 size) { pos += 4; // Read length of chunk - chunk_len = read4high(pos); + chunkLen = read4high(pos); // Add eight bytes i += 8; if (memcmp(buf, "INFO", 4) == 0) { // Must be at least 2 bytes long - if (chunk_len < 2) { - warning("Invalid chunk length %d for 'INFO' block", (int)chunk_len); + if (chunkLen < 2) { + warning("Invalid chunk length %d for 'INFO' block", (int)chunkLen); return false; } - _num_tracks = (byte)read2low(pos); + _numTracks = (byte)read2low(pos); - if (chunk_len > 2) { - warning("Chunk length %d is greater than 2", (int)chunk_len); - //pos += chunk_len - 2; + if (chunkLen > 2) { + warning("Chunk length %d is greater than 2", (int)chunkLen); + //pos += chunkLen - 2; } break; } // Must align - pos += (chunk_len + 1) & ~1; - i += (chunk_len + 1) & ~1; + pos += (chunkLen + 1) & ~1; + i += (chunkLen + 1) & ~1; } // Didn't get to fill the header - if (_num_tracks == 0) { + if (_numTracks == 0) { warning("Didn't find a valid track count"); return false; } @@ -308,13 +303,13 @@ bool MidiParser_XMIDI::loadMusic(byte *data, uint32 size) { // Ok it's an XMIDI. // We're going to identify and store the location for each track. - if (_num_tracks > ARRAYSIZE(_tracks)) { - warning("Can only handle %d tracks but was handed %d", (int)ARRAYSIZE(_tracks), (int)_num_tracks); + if (_numTracks > ARRAYSIZE(_tracks)) { + warning("Can only handle %d tracks but was handed %d", (int)ARRAYSIZE(_tracks), (int)_numTracks); return false; } - int tracks_read = 0; - while (tracks_read < _num_tracks) { + int tracksRead = 0; + while (tracksRead < _numTracks) { if (!memcmp(pos, "FORM", 4)) { // Skip this plus the 4 bytes after it. pos += 8; @@ -330,11 +325,11 @@ bool MidiParser_XMIDI::loadMusic(byte *data, uint32 size) { pos += (len + 1) & ~1; } else if (!memcmp(pos, "EVNT", 4)) { // Ahh! What we're looking for at last. - _tracks[tracks_read] = pos + 8; // Skip the EVNT and length bytes + _tracks[tracksRead] = pos + 8; // Skip the EVNT and length bytes pos += 4; len = read4high(pos); pos += (len + 1) & ~1; - ++tracks_read; + ++tracksRead; } else { warning("Hit invalid block '%c%c%c%c' while scanning for track locations", pos[0], pos[1], pos[2], pos[3]); return false; @@ -349,7 +344,6 @@ bool MidiParser_XMIDI::loadMusic(byte *data, uint32 size) { _ppqn = 60; resetTracking(); setTempo(500000); - _inserted_delta = 0; setTrack(0); return true; } @@ -357,11 +351,6 @@ bool MidiParser_XMIDI::loadMusic(byte *data, uint32 size) { return false; } -void MidiParser_XMIDI::resetTracking() { - MidiParser::resetTracking(); - _inserted_delta = 0; -} - void MidiParser::defaultXMidiCallback(byte eventData, void *data) { warning("MidiParser: defaultXMidiCallback(%d)", eventData); } diff --git a/audio/mods/maxtrax.cpp b/audio/mods/maxtrax.cpp index 8ed51ae5c3..6f55d21839 100644 --- a/audio/mods/maxtrax.cpp +++ b/audio/mods/maxtrax.cpp @@ -211,7 +211,7 @@ void MaxTrax::interrupt() { goto endOfEventLoop; case 0xA0: // SPECIAL - switch (curEvent->stopTime >> 8){ + switch (curEvent->stopTime >> 8) { case 0x01: // SPECIAL_SYNC _playerCtx.syncCallBack(curEvent->stopTime & 0xFF); break; diff --git a/audio/module.mk b/audio/module.mk index e3aa0aaa81..4e1c031c83 100644 --- a/audio/module.mk +++ b/audio/module.mk @@ -4,6 +4,7 @@ MODULE_OBJS := \ audiostream.o \ fmopl.o \ mididrv.o \ + midiparser_qt.o \ midiparser_smf.o \ midiparser_xmidi.o \ midiparser.o \ diff --git a/audio/softsynth/adlib.cpp b/audio/softsynth/adlib.cpp index 32a5f4a910..0cadea7f22 100644 --- a/audio/softsynth/adlib.cpp +++ b/audio/softsynth/adlib.cpp @@ -32,7 +32,13 @@ #include "common/translation.h" #ifdef DEBUG_ADLIB -static int tick; +static int g_tick; +#endif + +// Only include OPL3 when we actually have an AdLib emulator builtin, which +// supports OPL3. +#ifndef DISABLE_DOSBOX_OPL +#define ENABLE_OPL3 #endif class MidiDriver_ADLIB; @@ -52,21 +58,21 @@ struct InstrumentExtra { } PACKED_STRUCT; struct AdLibInstrument { - byte mod_characteristic; - byte mod_scalingOutputLevel; - byte mod_attackDecay; - byte mod_sustainRelease; - byte mod_waveformSelect; - byte car_characteristic; - byte car_scalingOutputLevel; - byte car_attackDecay; - byte car_sustainRelease; - byte car_waveformSelect; + byte modCharacteristic; + byte modScalingOutputLevel; + byte modAttackDecay; + byte modSustainRelease; + byte modWaveformSelect; + byte carCharacteristic; + byte carScalingOutputLevel; + byte carAttackDecay; + byte carSustainRelease; + byte carWaveformSelect; byte feedback; - byte flags_a; - InstrumentExtra extra_a; - byte flags_b; - InstrumentExtra extra_b; + byte flagsA; + InstrumentExtra extraA; + byte flagsB; + InstrumentExtra extraB; byte duration; } PACKED_STRUCT; #include "common/pack-end.h" @@ -77,16 +83,20 @@ class AdLibPart : public MidiChannel { protected: // AdLibPart *_prev, *_next; AdLibVoice *_voice; - int16 _pitchbend; - byte _pitchbend_factor; - int8 _transpose_eff; - byte _vol_eff; - int8 _detune_eff; - byte _modwheel; + int16 _pitchBend; + byte _pitchBendFactor; + //int8 _transposeEff; + byte _volEff; + int8 _detuneEff; + byte _modWheel; bool _pedal; byte _program; - byte _pri_eff; - AdLibInstrument _part_instr; + byte _priEff; + byte _pan; + AdLibInstrument _partInstr; +#ifdef ENABLE_OPL3 + AdLibInstrument _partInstrSecondary; +#endif protected: MidiDriver_ADLIB *_owner; @@ -99,21 +109,25 @@ protected: public: AdLibPart() { _voice = 0; - _pitchbend = 0; - _pitchbend_factor = 2; - _transpose_eff = 0; - _vol_eff = 0; - _detune_eff = 0; - _modwheel = 0; + _pitchBend = 0; + _pitchBendFactor = 2; + //_transposeEff = 0; + _volEff = 0; + _detuneEff = 0; + _modWheel = 0; _pedal = 0; _program = 0; - _pri_eff = 0; + _priEff = 0; + _pan = 64; _owner = 0; _allocated = false; _channel = 0; - memset(&_part_instr, 0, sizeof(_part_instr)); + memset(&_partInstr, 0, sizeof(_partInstr)); +#ifdef ENABLE_OPL3 + memset(&_partInstrSecondary, 0, sizeof(_partInstrSecondary)); +#endif } MidiDriver *device(); @@ -132,7 +146,7 @@ public: void controlChange(byte control, byte value); void modulationWheel(byte value); void volume(byte value); - void panPosition(byte value) { return; } // Not supported + void panPosition(byte value); void pitchBendFactor(byte value); void detune(byte value); void priority(byte value); @@ -162,7 +176,6 @@ public: void noteOff(byte note); void noteOn(byte note, byte velocity); void programChange(byte program) { } - void pitchBend(int16 bend) { } // Control Change messages void modulationWheel(byte value) { } @@ -181,26 +194,26 @@ private: struct Struct10 { byte active; - int16 cur_val; + int16 curVal; int16 count; - uint16 max_value; - int16 start_value; + uint16 maxValue; + int16 startValue; byte loop; - byte table_a[4]; - byte table_b[4]; + byte tableA[4]; + byte tableB[4]; int8 unk3; - int8 modwheel; - int8 modwheel_last; - uint16 speed_lo_max; - uint16 num_steps; - int16 speed_hi; + int8 modWheel; + int8 modWheelLast; + uint16 speedLoMax; + uint16 numSteps; + int16 speedHi; int8 direction; - uint16 speed_lo; - uint16 speed_lo_counter; + uint16 speedLo; + uint16 speedLoCounter; }; struct Struct11 { - int16 modify_val; + int16 modifyVal; byte param, flag0x40, flag0x10; Struct10 *s10; }; @@ -208,11 +221,11 @@ struct Struct11 { struct AdLibVoice { AdLibPart *_part; AdLibVoice *_next, *_prev; - byte _waitforpedal; + byte _waitForPedal; byte _note; byte _channel; - byte _twochan; - byte _vol_1, _vol_2; + byte _twoChan; + byte _vol1, _vol2; int16 _duration; Struct10 _s10a; @@ -220,26 +233,34 @@ struct AdLibVoice { Struct10 _s10b; Struct11 _s11b; +#ifdef ENABLE_OPL3 + byte _secTwoChan; + byte _secVol1, _secVol2; +#endif + AdLibVoice() { memset(this, 0, sizeof(AdLibVoice)); } }; struct AdLibSetParams { - byte a, b, c, d; + byte registerBase; + byte shift; + byte mask; + byte inversion; }; -static const byte channel_mappings[9] = { +static const byte g_operator1Offsets[9] = { 0, 1, 2, 8, 9, 10, 16, 17, 18 }; -static const byte channel_mappings_2[9] = { +static const byte g_operator2Offsets[9] = { 3, 4, 5, 11, 12, 13, 19, 20, 21 }; -static const AdLibSetParams adlib_setparam_table[] = { +static const AdLibSetParams g_setParamTable[] = { {0x40, 0, 63, 63}, // level {0xE0, 2, 0, 0}, // unused {0x40, 6, 192, 0}, // level key scaling @@ -257,21 +278,21 @@ static const AdLibSetParams adlib_setparam_table[] = { {0xC0, 1, 14, 0} // feedback }; -static const byte param_table_1[16] = { +static const byte g_paramTable1[16] = { 29, 28, 27, 0, 3, 4, 7, 8, 13, 16, 17, 20, 21, 30, 31, 0 }; -static const uint16 maxval_table[16] = { +static const uint16 g_maxValTable[16] = { 0x2FF, 0x1F, 0x7, 0x3F, 0x0F, 0x0F, 0x0F, 0x3, 0x3F, 0x0F, 0x0F, 0x0F, 0x3, 0x3E, 0x1F, 0 }; -static const uint16 num_steps_table[] = { +static const uint16 g_numStepsTable[] = { 1, 2, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16, @@ -282,7 +303,7 @@ static const uint16 num_steps_table[] = { 600, 860, 1200, 1600 }; -static const byte note_to_f_num[] = { +static const byte g_noteFrequencies[] = { 90, 91, 92, 92, 93, 94, 94, 95, 96, 96, 97, 98, 98, 99, 100, 101, 101, 102, 103, 104, 104, 105, 106, 107, @@ -303,188 +324,530 @@ static const byte note_to_f_num[] = { 242, 243, 245, 247, 249, 251, 252, 254 }; -static const AdLibInstrument map_gm_to_fm[128] = { +static const AdLibInstrument g_gmInstruments[128] = { // 0x00 -{ 0xC2, 0xC5, 0x2B, 0x99, 0x58, 0xC2, 0x1F, 0x1E, 0xC8, 0x7C, 0x0A, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x23 }, -{ 0x22, 0x53, 0x0E, 0x8A, 0x30, 0x14, 0x06, 0x1D, 0x7A, 0x5C, 0x06, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0x06, 0x00, 0x1C, 0x79, 0x40, 0x02, 0x00, 0x4B, 0x79, 0x58, 0x08, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xC2, 0x89, 0x2A, 0x89, 0x49, 0xC2, 0x16, 0x1C, 0xB8, 0x7C, 0x04, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x23 }, -{ 0xC2, 0x17, 0x3D, 0x6A, 0x00, 0xC4, 0x2E, 0x2D, 0xC9, 0x20, 0x00, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0x06, 0x1E, 0x1C, 0x99, 0x00, 0x02, 0x3A, 0x4C, 0x79, 0x00, 0x0C, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0x84, 0x40, 0x3B, 0x5A, 0x6F, 0x81, 0x0E, 0x3B, 0x5A, 0x7F, 0x0B, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0x84, 0x40, 0x3B, 0x5A, 0x63, 0x81, 0x00, 0x3B, 0x5A, 0x7F, 0x01, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0x8C, 0x80, 0x05, 0xEA, 0x59, 0x82, 0x0A, 0x3C, 0xAA, 0x64, 0x07, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0x85, 0x40, 0x0D, 0xEC, 0x71, 0x84, 0x58, 0x3E, 0xCB, 0x7C, 0x01, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0x8A, 0xC0, 0x0C, 0xDC, 0x50, 0x88, 0x58, 0x3D, 0xDA, 0x7C, 0x01, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xC9, 0x40, 0x2B, 0x78, 0x42, 0xC2, 0x04, 0x4C, 0x8A, 0x7C, 0x00, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x1A }, -{ 0x2A, 0x0E, 0x17, 0x89, 0x28, 0x22, 0x0C, 0x1B, 0x09, 0x70, 0x0A, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE7, 0x9B, 0x08, 0x08, 0x26, 0xE2, 0x06, 0x0A, 0x08, 0x70, 0x0A, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xC5, 0x05, 0x00, 0xFC, 0x40, 0x84, 0x00, 0x00, 0xDC, 0x50, 0x08, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0x86, 0x40, 0x5D, 0x5A, 0x41, 0x81, 0x00, 0x0B, 0x5A, 0x7F, 0x00, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, + { 0xC2, 0xC5, 0x2B, 0x99, 0x58, 0xC2, 0x1F, 0x1E, 0xC8, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x23 }, + { 0x22, 0x53, 0x0E, 0x8A, 0x30, 0x14, 0x06, 0x1D, 0x7A, 0x5C, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x06, 0x00, 0x1C, 0x79, 0x40, 0x02, 0x00, 0x4B, 0x79, 0x58, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC2, 0x89, 0x2A, 0x89, 0x49, 0xC2, 0x16, 0x1C, 0xB8, 0x7C, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x23 }, + { 0xC2, 0x17, 0x3D, 0x6A, 0x00, 0xC4, 0x2E, 0x2D, 0xC9, 0x20, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x06, 0x1E, 0x1C, 0x99, 0x00, 0x02, 0x3A, 0x4C, 0x79, 0x00, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x84, 0x40, 0x3B, 0x5A, 0x6F, 0x81, 0x0E, 0x3B, 0x5A, 0x7F, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x84, 0x40, 0x3B, 0x5A, 0x63, 0x81, 0x00, 0x3B, 0x5A, 0x7F, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x8C, 0x80, 0x05, 0xEA, 0x59, 0x82, 0x0A, 0x3C, 0xAA, 0x64, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x85, 0x40, 0x0D, 0xEC, 0x71, 0x84, 0x58, 0x3E, 0xCB, 0x7C, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x8A, 0xC0, 0x0C, 0xDC, 0x50, 0x88, 0x58, 0x3D, 0xDA, 0x7C, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC9, 0x40, 0x2B, 0x78, 0x42, 0xC2, 0x04, 0x4C, 0x8A, 0x7C, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x1A }, + { 0x2A, 0x0E, 0x17, 0x89, 0x28, 0x22, 0x0C, 0x1B, 0x09, 0x70, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE7, 0x9B, 0x08, 0x08, 0x26, 0xE2, 0x06, 0x0A, 0x08, 0x70, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC5, 0x05, 0x00, 0xFC, 0x40, 0x84, 0x00, 0x00, 0xDC, 0x50, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x86, 0x40, 0x5D, 0x5A, 0x41, 0x81, 0x00, 0x0B, 0x5A, 0x7F, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, // 0x10 -{ 0xED, 0x00, 0x7B, 0xC8, 0x40, 0xE1, 0x99, 0x4A, 0xE9, 0x7E, 0x07, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE8, 0x4F, 0x3A, 0xD7, 0x7C, 0xE2, 0x97, 0x49, 0xF9, 0x7D, 0x05, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE1, 0x10, 0x2F, 0xF7, 0x7D, 0xF3, 0x45, 0x8F, 0xC7, 0x62, 0x07, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0x01, 0x8C, 0x9F, 0xDA, 0x70, 0xE4, 0x50, 0x9F, 0xDA, 0x6A, 0x09, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0x08, 0xD5, 0x9D, 0xA5, 0x45, 0xE2, 0x3F, 0x9F, 0xD6, 0x49, 0x07, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE5, 0x0F, 0x7D, 0xB8, 0x2E, 0xA2, 0x0F, 0x7C, 0xC7, 0x61, 0x04, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xF2, 0x2A, 0x9F, 0xDB, 0x01, 0xE1, 0x04, 0x8F, 0xD7, 0x62, 0x0A, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE4, 0x88, 0x9C, 0x50, 0x64, 0xE2, 0x18, 0x70, 0xC4, 0x7C, 0x0B, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0x02, 0xA3, 0x0D, 0xDA, 0x01, 0xC2, 0x35, 0x5D, 0x58, 0x00, 0x06, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x18 }, -{ 0x42, 0x55, 0x3E, 0xEB, 0x24, 0xD4, 0x08, 0x0D, 0xA9, 0x71, 0x04, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x18 }, -{ 0xC2, 0x00, 0x2B, 0x17, 0x51, 0xC2, 0x1E, 0x4D, 0x97, 0x7C, 0x00, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x19 }, -{ 0xC6, 0x01, 0x2D, 0xA7, 0x44, 0xC2, 0x06, 0x0E, 0xA7, 0x79, 0x06, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xC2, 0x0C, 0x06, 0x06, 0x55, 0xC2, 0x3F, 0x09, 0x86, 0x7D, 0x0A, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x0A }, -{ 0xC2, 0x2E, 0x4F, 0x77, 0x00, 0xC4, 0x08, 0x0E, 0x98, 0x59, 0x0A, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xC2, 0x30, 0x4F, 0xCA, 0x01, 0xC4, 0x0D, 0x0E, 0xB8, 0x7F, 0x08, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xC4, 0x29, 0x4F, 0xCA, 0x03, 0xC8, 0x0D, 0x0C, 0xB7, 0x7D, 0x00, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x0B }, + { 0xED, 0x00, 0x7B, 0xC8, 0x40, 0xE1, 0x99, 0x4A, 0xE9, 0x7E, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE8, 0x4F, 0x3A, 0xD7, 0x7C, 0xE2, 0x97, 0x49, 0xF9, 0x7D, 0x05, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE1, 0x10, 0x2F, 0xF7, 0x7D, 0xF3, 0x45, 0x8F, 0xC7, 0x62, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x01, 0x8C, 0x9F, 0xDA, 0x70, 0xE4, 0x50, 0x9F, 0xDA, 0x6A, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x08, 0xD5, 0x9D, 0xA5, 0x45, 0xE2, 0x3F, 0x9F, 0xD6, 0x49, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE5, 0x0F, 0x7D, 0xB8, 0x2E, 0xA2, 0x0F, 0x7C, 0xC7, 0x61, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xF2, 0x2A, 0x9F, 0xDB, 0x01, 0xE1, 0x04, 0x8F, 0xD7, 0x62, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x88, 0x9C, 0x50, 0x64, 0xE2, 0x18, 0x70, 0xC4, 0x7C, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x02, 0xA3, 0x0D, 0xDA, 0x01, 0xC2, 0x35, 0x5D, 0x58, 0x00, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x18 }, + { 0x42, 0x55, 0x3E, 0xEB, 0x24, 0xD4, 0x08, 0x0D, 0xA9, 0x71, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x18 }, + { 0xC2, 0x00, 0x2B, 0x17, 0x51, 0xC2, 0x1E, 0x4D, 0x97, 0x7C, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x19 }, + { 0xC6, 0x01, 0x2D, 0xA7, 0x44, 0xC2, 0x06, 0x0E, 0xA7, 0x79, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC2, 0x0C, 0x06, 0x06, 0x55, 0xC2, 0x3F, 0x09, 0x86, 0x7D, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0A }, + { 0xC2, 0x2E, 0x4F, 0x77, 0x00, 0xC4, 0x08, 0x0E, 0x98, 0x59, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC2, 0x30, 0x4F, 0xCA, 0x01, 0xC4, 0x0D, 0x0E, 0xB8, 0x7F, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC4, 0x29, 0x4F, 0xCA, 0x03, 0xC8, 0x0D, 0x0C, 0xB7, 0x7D, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0B }, // 0x20 -{ 0xC2, 0x40, 0x3C, 0x96, 0x58, 0xC4, 0xDE, 0x0E, 0xC7, 0x7C, 0x00, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x20 }, -{ 0x31, 0x13, 0x2D, 0xD7, 0x3C, 0xE2, 0x18, 0x2E, 0xB8, 0x7C, 0x08, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0x22, 0x86, 0x0D, 0xD7, 0x50, 0xE4, 0x18, 0x5E, 0xB8, 0x7C, 0x06, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x28 }, -{ 0xF2, 0x0A, 0x0D, 0xD7, 0x40, 0xE4, 0x1F, 0x5E, 0xB8, 0x7C, 0x0A, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xF2, 0x09, 0x4B, 0xD6, 0x48, 0xE4, 0x1F, 0x1C, 0xB8, 0x7C, 0x0A, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x28 }, -{ 0x62, 0x11, 0x0C, 0xE6, 0x3C, 0xE4, 0x1F, 0x0C, 0xC8, 0x7C, 0x0A, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE2, 0x12, 0x3D, 0xE6, 0x34, 0xE4, 0x1F, 0x7D, 0xB8, 0x7C, 0x0A, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE2, 0x13, 0x3D, 0xE6, 0x34, 0xE4, 0x1F, 0x5D, 0xB8, 0x7D, 0x08, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xA2, 0x40, 0x5D, 0xBA, 0x3F, 0xE2, 0x00, 0x8F, 0xD8, 0x79, 0x00, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE2, 0x40, 0x3D, 0xDA, 0x3B, 0xE1, 0x00, 0x7E, 0xD8, 0x7A, 0x04, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0x62, 0x00, 0x6D, 0xFA, 0x5D, 0xE2, 0x00, 0x8F, 0xC8, 0x79, 0x04, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE1, 0x00, 0x4E, 0xDB, 0x4A, 0xE3, 0x18, 0x6F, 0xE9, 0x7E, 0x00, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE1, 0x00, 0x4E, 0xDB, 0x66, 0xE2, 0x00, 0x7F, 0xE9, 0x7E, 0x06, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0x02, 0x0F, 0x66, 0xAA, 0x51, 0x02, 0x64, 0x29, 0xF9, 0x7C, 0x08, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x04 }, -{ 0x16, 0x4A, 0x04, 0xBA, 0x39, 0xC2, 0x58, 0x2D, 0xCA, 0x7C, 0x0A, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x03 }, -{ 0x02, 0x00, 0x01, 0x7A, 0x79, 0x02, 0x3F, 0x28, 0xEA, 0x7C, 0x08, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x02 }, + { 0xC2, 0x40, 0x3C, 0x96, 0x58, 0xC4, 0xDE, 0x0E, 0xC7, 0x7C, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x20 }, + { 0x31, 0x13, 0x2D, 0xD7, 0x3C, 0xE2, 0x18, 0x2E, 0xB8, 0x7C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x22, 0x86, 0x0D, 0xD7, 0x50, 0xE4, 0x18, 0x5E, 0xB8, 0x7C, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x28 }, + { 0xF2, 0x0A, 0x0D, 0xD7, 0x40, 0xE4, 0x1F, 0x5E, 0xB8, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xF2, 0x09, 0x4B, 0xD6, 0x48, 0xE4, 0x1F, 0x1C, 0xB8, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x28 }, + { 0x62, 0x11, 0x0C, 0xE6, 0x3C, 0xE4, 0x1F, 0x0C, 0xC8, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x12, 0x3D, 0xE6, 0x34, 0xE4, 0x1F, 0x7D, 0xB8, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x13, 0x3D, 0xE6, 0x34, 0xE4, 0x1F, 0x5D, 0xB8, 0x7D, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xA2, 0x40, 0x5D, 0xBA, 0x3F, 0xE2, 0x00, 0x8F, 0xD8, 0x79, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x40, 0x3D, 0xDA, 0x3B, 0xE1, 0x00, 0x7E, 0xD8, 0x7A, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x62, 0x00, 0x6D, 0xFA, 0x5D, 0xE2, 0x00, 0x8F, 0xC8, 0x79, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE1, 0x00, 0x4E, 0xDB, 0x4A, 0xE3, 0x18, 0x6F, 0xE9, 0x7E, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE1, 0x00, 0x4E, 0xDB, 0x66, 0xE2, 0x00, 0x7F, 0xE9, 0x7E, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x02, 0x0F, 0x66, 0xAA, 0x51, 0x02, 0x64, 0x29, 0xF9, 0x7C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 }, + { 0x16, 0x4A, 0x04, 0xBA, 0x39, 0xC2, 0x58, 0x2D, 0xCA, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0x02, 0x00, 0x01, 0x7A, 0x79, 0x02, 0x3F, 0x28, 0xEA, 0x7C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, // 0x30 -{ 0x62, 0x53, 0x9C, 0xBA, 0x31, 0x62, 0x5B, 0xAD, 0xC9, 0x55, 0x04, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xF2, 0x40, 0x6E, 0xDA, 0x49, 0xE2, 0x13, 0x8F, 0xF9, 0x7D, 0x08, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE2, 0x40, 0x8F, 0xFA, 0x50, 0xF2, 0x04, 0x7F, 0xFA, 0x7D, 0x0A, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE4, 0xA0, 0xCE, 0x5B, 0x02, 0xE2, 0x32, 0x7F, 0xFB, 0x3D, 0x04, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE6, 0x80, 0x9C, 0x99, 0x42, 0xE2, 0x04, 0x7D, 0x78, 0x60, 0x04, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xEA, 0xA0, 0xAC, 0x67, 0x02, 0xE2, 0x00, 0x7C, 0x7A, 0x7C, 0x06, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE7, 0x94, 0xAD, 0xB7, 0x03, 0xE2, 0x00, 0x7C, 0xBA, 0x7C, 0x00, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xC3, 0x3F, 0x4B, 0xE9, 0x7E, 0xC1, 0x3F, 0x9B, 0xF9, 0x7F, 0x0B, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x06 }, -{ 0xB2, 0x20, 0xAD, 0xE9, 0x00, 0x62, 0x05, 0x8F, 0xC8, 0x68, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xF2, 0x00, 0x8F, 0xFB, 0x50, 0xF6, 0x47, 0x8F, 0xE9, 0x68, 0x08, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xF2, 0x00, 0xAF, 0x88, 0x58, 0xF2, 0x54, 0x6E, 0xC9, 0x7C, 0x0A, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xF2, 0x2A, 0x9F, 0x98, 0x01, 0xE2, 0x84, 0x4E, 0x78, 0x6C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE2, 0x02, 0x9F, 0xB8, 0x48, 0x22, 0x89, 0x9F, 0xE8, 0x7C, 0x00, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE2, 0x2A, 0x7F, 0xB8, 0x01, 0xE4, 0x00, 0x0D, 0xC5, 0x7C, 0x0C, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE4, 0x28, 0x8E, 0xE8, 0x01, 0xF2, 0x00, 0x4D, 0xD6, 0x7D, 0x0C, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0x62, 0x23, 0x8F, 0xEA, 0x00, 0xF2, 0x00, 0x5E, 0xD9, 0x7C, 0x0C, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, + { 0x62, 0x53, 0x9C, 0xBA, 0x31, 0x62, 0x5B, 0xAD, 0xC9, 0x55, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xF2, 0x40, 0x6E, 0xDA, 0x49, 0xE2, 0x13, 0x8F, 0xF9, 0x7D, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x40, 0x8F, 0xFA, 0x50, 0xF2, 0x04, 0x7F, 0xFA, 0x7D, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0xA0, 0xCE, 0x5B, 0x02, 0xE2, 0x32, 0x7F, 0xFB, 0x3D, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE6, 0x80, 0x9C, 0x99, 0x42, 0xE2, 0x04, 0x7D, 0x78, 0x60, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xEA, 0xA0, 0xAC, 0x67, 0x02, 0xE2, 0x00, 0x7C, 0x7A, 0x7C, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE7, 0x94, 0xAD, 0xB7, 0x03, 0xE2, 0x00, 0x7C, 0xBA, 0x7C, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC3, 0x3F, 0x4B, 0xE9, 0x7E, 0xC1, 0x3F, 0x9B, 0xF9, 0x7F, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 }, + { 0xB2, 0x20, 0xAD, 0xE9, 0x00, 0x62, 0x05, 0x8F, 0xC8, 0x68, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xF2, 0x00, 0x8F, 0xFB, 0x50, 0xF6, 0x47, 0x8F, 0xE9, 0x68, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xF2, 0x00, 0xAF, 0x88, 0x58, 0xF2, 0x54, 0x6E, 0xC9, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xF2, 0x2A, 0x9F, 0x98, 0x01, 0xE2, 0x84, 0x4E, 0x78, 0x6C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x02, 0x9F, 0xB8, 0x48, 0x22, 0x89, 0x9F, 0xE8, 0x7C, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x2A, 0x7F, 0xB8, 0x01, 0xE4, 0x00, 0x0D, 0xC5, 0x7C, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x28, 0x8E, 0xE8, 0x01, 0xF2, 0x00, 0x4D, 0xD6, 0x7D, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x62, 0x23, 0x8F, 0xEA, 0x00, 0xF2, 0x00, 0x5E, 0xD9, 0x7C, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, // 0x40 -{ 0xB4, 0x26, 0x6E, 0x98, 0x01, 0x62, 0x00, 0x7D, 0xC8, 0x7D, 0x00, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE2, 0x2E, 0x20, 0xD9, 0x01, 0xF2, 0x0F, 0x90, 0xF8, 0x78, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE4, 0x28, 0x7E, 0xF8, 0x01, 0xE2, 0x23, 0x8E, 0xE8, 0x7D, 0x08, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xB8, 0x28, 0x9E, 0x98, 0x01, 0x62, 0x00, 0x3D, 0xC8, 0x7D, 0x08, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0x62, 0x00, 0x8E, 0xC9, 0x3D, 0xE6, 0x00, 0x7E, 0xD8, 0x68, 0x0A, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE2, 0x00, 0x5F, 0xF9, 0x48, 0xE6, 0x98, 0x8F, 0xF8, 0x7D, 0x08, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0x62, 0x0C, 0x6E, 0xD8, 0x3D, 0x2A, 0x06, 0x7D, 0xD8, 0x58, 0x04, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE4, 0x00, 0x7E, 0x89, 0x38, 0xE6, 0x84, 0x80, 0xF8, 0x68, 0x0C, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE4, 0x80, 0x6C, 0xD9, 0x30, 0xE2, 0x00, 0x8D, 0xC8, 0x7C, 0x00, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE2, 0x80, 0x88, 0x48, 0x40, 0xE2, 0x0A, 0x7D, 0xA8, 0x7C, 0x08, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE4, 0x00, 0x77, 0xC5, 0x54, 0xE2, 0x00, 0x9E, 0xD7, 0x70, 0x06, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE4, 0x80, 0x86, 0xB9, 0x64, 0xE2, 0x05, 0x9F, 0xD7, 0x78, 0x0A, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE2, 0x00, 0x68, 0x68, 0x56, 0xE2, 0x08, 0x9B, 0xB3, 0x7C, 0x08, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE4, 0x00, 0xA6, 0x87, 0x41, 0xE2, 0x0A, 0x7E, 0xC9, 0x7C, 0x06, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE4, 0x80, 0x9A, 0xB8, 0x48, 0xE2, 0x00, 0x9E, 0xF9, 0x60, 0x09, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE2, 0x80, 0x8E, 0x64, 0x68, 0xE2, 0x28, 0x6F, 0x73, 0x7C, 0x01, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, + { 0xB4, 0x26, 0x6E, 0x98, 0x01, 0x62, 0x00, 0x7D, 0xC8, 0x7D, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x2E, 0x20, 0xD9, 0x01, 0xF2, 0x0F, 0x90, 0xF8, 0x78, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x28, 0x7E, 0xF8, 0x01, 0xE2, 0x23, 0x8E, 0xE8, 0x7D, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xB8, 0x28, 0x9E, 0x98, 0x01, 0x62, 0x00, 0x3D, 0xC8, 0x7D, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x62, 0x00, 0x8E, 0xC9, 0x3D, 0xE6, 0x00, 0x7E, 0xD8, 0x68, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x00, 0x5F, 0xF9, 0x48, 0xE6, 0x98, 0x8F, 0xF8, 0x7D, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x62, 0x0C, 0x6E, 0xD8, 0x3D, 0x2A, 0x06, 0x7D, 0xD8, 0x58, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x00, 0x7E, 0x89, 0x38, 0xE6, 0x84, 0x80, 0xF8, 0x68, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x80, 0x6C, 0xD9, 0x30, 0xE2, 0x00, 0x8D, 0xC8, 0x7C, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x80, 0x88, 0x48, 0x40, 0xE2, 0x0A, 0x7D, 0xA8, 0x7C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x00, 0x77, 0xC5, 0x54, 0xE2, 0x00, 0x9E, 0xD7, 0x70, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x80, 0x86, 0xB9, 0x64, 0xE2, 0x05, 0x9F, 0xD7, 0x78, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x00, 0x68, 0x68, 0x56, 0xE2, 0x08, 0x9B, 0xB3, 0x7C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x00, 0xA6, 0x87, 0x41, 0xE2, 0x0A, 0x7E, 0xC9, 0x7C, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x80, 0x9A, 0xB8, 0x48, 0xE2, 0x00, 0x9E, 0xF9, 0x60, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x80, 0x8E, 0x64, 0x68, 0xE2, 0x28, 0x6F, 0x73, 0x7C, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, // 0x50 -{ 0xE8, 0x00, 0x7D, 0x99, 0x54, 0xE6, 0x80, 0x80, 0xF8, 0x7C, 0x0C, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE6, 0x00, 0x9F, 0xB9, 0x6D, 0xE1, 0x00, 0x8F, 0xC8, 0x7D, 0x02, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE4, 0x00, 0x09, 0x68, 0x4A, 0xE2, 0x2B, 0x9E, 0xF3, 0x7C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xC4, 0x00, 0x99, 0xE8, 0x3B, 0xE2, 0x25, 0x6F, 0x93, 0x7C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE6, 0x00, 0x6F, 0xDA, 0x69, 0xE2, 0x05, 0x2F, 0xD8, 0x6A, 0x08, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xEC, 0x60, 0x9D, 0xC7, 0x00, 0xE2, 0x21, 0x7F, 0xC9, 0x7C, 0x06, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE3, 0x00, 0x0F, 0xF7, 0x7D, 0xE1, 0x3F, 0x0F, 0xA7, 0x01, 0x0D, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE4, 0xA9, 0x0F, 0xA8, 0x02, 0xE2, 0x3C, 0x5F, 0xDA, 0x3C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE8, 0x40, 0x0D, 0x89, 0x7D, 0xE2, 0x17, 0x7E, 0xD9, 0x7C, 0x07, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE1, 0x00, 0xDF, 0x8A, 0x56, 0xE2, 0x5E, 0xCF, 0xBA, 0x7E, 0x08, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE2, 0x00, 0x0B, 0x68, 0x60, 0xE2, 0x01, 0x9E, 0xB8, 0x7C, 0x0A, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xEA, 0x00, 0xAE, 0xAB, 0x49, 0xE2, 0x00, 0xAE, 0xBA, 0x6C, 0x08, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xEB, 0x80, 0x8C, 0xCB, 0x3A, 0xE2, 0x86, 0xAF, 0xCA, 0x7C, 0x08, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE5, 0x40, 0xDB, 0x3B, 0x3C, 0xE2, 0x80, 0xBE, 0xCA, 0x71, 0x00, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE4, 0x00, 0x9E, 0xAA, 0x3D, 0xE1, 0x43, 0x0F, 0xBA, 0x7E, 0x04, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE7, 0x40, 0xEC, 0xCA, 0x44, 0xE2, 0x03, 0xBF, 0xBA, 0x66, 0x02, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, + { 0xE8, 0x00, 0x7D, 0x99, 0x54, 0xE6, 0x80, 0x80, 0xF8, 0x7C, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE6, 0x00, 0x9F, 0xB9, 0x6D, 0xE1, 0x00, 0x8F, 0xC8, 0x7D, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x00, 0x09, 0x68, 0x4A, 0xE2, 0x2B, 0x9E, 0xF3, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC4, 0x00, 0x99, 0xE8, 0x3B, 0xE2, 0x25, 0x6F, 0x93, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE6, 0x00, 0x6F, 0xDA, 0x69, 0xE2, 0x05, 0x2F, 0xD8, 0x6A, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xEC, 0x60, 0x9D, 0xC7, 0x00, 0xE2, 0x21, 0x7F, 0xC9, 0x7C, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE3, 0x00, 0x0F, 0xF7, 0x7D, 0xE1, 0x3F, 0x0F, 0xA7, 0x01, 0x0D, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0xA9, 0x0F, 0xA8, 0x02, 0xE2, 0x3C, 0x5F, 0xDA, 0x3C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE8, 0x40, 0x0D, 0x89, 0x7D, 0xE2, 0x17, 0x7E, 0xD9, 0x7C, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE1, 0x00, 0xDF, 0x8A, 0x56, 0xE2, 0x5E, 0xCF, 0xBA, 0x7E, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x00, 0x0B, 0x68, 0x60, 0xE2, 0x01, 0x9E, 0xB8, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xEA, 0x00, 0xAE, 0xAB, 0x49, 0xE2, 0x00, 0xAE, 0xBA, 0x6C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xEB, 0x80, 0x8C, 0xCB, 0x3A, 0xE2, 0x86, 0xAF, 0xCA, 0x7C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE5, 0x40, 0xDB, 0x3B, 0x3C, 0xE2, 0x80, 0xBE, 0xCA, 0x71, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x00, 0x9E, 0xAA, 0x3D, 0xE1, 0x43, 0x0F, 0xBA, 0x7E, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE7, 0x40, 0xEC, 0xCA, 0x44, 0xE2, 0x03, 0xBF, 0xBA, 0x66, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, // 0x60 -{ 0xEA, 0x00, 0x68, 0xB8, 0x48, 0xE2, 0x0A, 0x8E, 0xB8, 0x7C, 0x0C, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0x61, 0x00, 0xBE, 0x99, 0x7E, 0xE3, 0x40, 0xCF, 0xCA, 0x7D, 0x09, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xCD, 0x00, 0x0B, 0x00, 0x48, 0xC2, 0x58, 0x0C, 0x00, 0x7C, 0x0C, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x1C }, -{ 0xE2, 0x00, 0x0E, 0x00, 0x52, 0xE2, 0x58, 0x5F, 0xD0, 0x7D, 0x08, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xCC, 0x00, 0x7D, 0xDA, 0x40, 0xC2, 0x00, 0x5E, 0x9B, 0x58, 0x0C, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE9, 0xC0, 0xEE, 0xD8, 0x43, 0xE2, 0x05, 0xDD, 0xAA, 0x70, 0x06, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xDA, 0x00, 0x8F, 0xAC, 0x4A, 0x22, 0x05, 0x8D, 0x8A, 0x75, 0x02, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0x62, 0x8A, 0xCB, 0x7A, 0x74, 0xE6, 0x56, 0xAF, 0xDB, 0x70, 0x02, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xC2, 0x41, 0xAC, 0x5B, 0x5B, 0xC2, 0x80, 0x0D, 0xCB, 0x7D, 0x0A, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x12 }, -{ 0x75, 0x00, 0x0E, 0xCB, 0x5A, 0xE2, 0x1E, 0x0A, 0xC9, 0x7D, 0x0A, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x10 }, -{ 0x41, 0x00, 0x0E, 0xEA, 0x53, 0xC2, 0x00, 0x08, 0xCA, 0x7C, 0x08, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x07 }, -{ 0xC1, 0x40, 0x0C, 0x59, 0x6A, 0xC2, 0x80, 0x3C, 0xAB, 0x7C, 0x08, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x0D }, -{ 0x4B, 0x00, 0x0A, 0xF5, 0x61, 0xC2, 0x19, 0x0C, 0xE9, 0x7C, 0x08, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x07 }, -{ 0x62, 0x00, 0x7F, 0xD8, 0x54, 0xEA, 0x00, 0x8F, 0xD8, 0x7D, 0x0A, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE1, 0x00, 0x7F, 0xD9, 0x56, 0xE1, 0x00, 0x8F, 0xD8, 0x7E, 0x06, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE1, 0x00, 0x7F, 0xD9, 0x56, 0xE1, 0x00, 0x8F, 0xD8, 0x7E, 0x06, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, + { 0xEA, 0x00, 0x68, 0xB8, 0x48, 0xE2, 0x0A, 0x8E, 0xB8, 0x7C, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x61, 0x00, 0xBE, 0x99, 0x7E, 0xE3, 0x40, 0xCF, 0xCA, 0x7D, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xCD, 0x00, 0x0B, 0x00, 0x48, 0xC2, 0x58, 0x0C, 0x00, 0x7C, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x1C }, + { 0xE2, 0x00, 0x0E, 0x00, 0x52, 0xE2, 0x58, 0x5F, 0xD0, 0x7D, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xCC, 0x00, 0x7D, 0xDA, 0x40, 0xC2, 0x00, 0x5E, 0x9B, 0x58, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE9, 0xC0, 0xEE, 0xD8, 0x43, 0xE2, 0x05, 0xDD, 0xAA, 0x70, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xDA, 0x00, 0x8F, 0xAC, 0x4A, 0x22, 0x05, 0x8D, 0x8A, 0x75, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x62, 0x8A, 0xCB, 0x7A, 0x74, 0xE6, 0x56, 0xAF, 0xDB, 0x70, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC2, 0x41, 0xAC, 0x5B, 0x5B, 0xC2, 0x80, 0x0D, 0xCB, 0x7D, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x12 }, + { 0x75, 0x00, 0x0E, 0xCB, 0x5A, 0xE2, 0x1E, 0x0A, 0xC9, 0x7D, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x10 }, + { 0x41, 0x00, 0x0E, 0xEA, 0x53, 0xC2, 0x00, 0x08, 0xCA, 0x7C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 }, + { 0xC1, 0x40, 0x0C, 0x59, 0x6A, 0xC2, 0x80, 0x3C, 0xAB, 0x7C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0D }, + { 0x4B, 0x00, 0x0A, 0xF5, 0x61, 0xC2, 0x19, 0x0C, 0xE9, 0x7C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 }, + { 0x62, 0x00, 0x7F, 0xD8, 0x54, 0xEA, 0x00, 0x8F, 0xD8, 0x7D, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE1, 0x00, 0x7F, 0xD9, 0x56, 0xE1, 0x00, 0x8F, 0xD8, 0x7E, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE1, 0x00, 0x7F, 0xD9, 0x56, 0xE1, 0x00, 0x8F, 0xD8, 0x7E, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, // 0x70 -{ 0xCF, 0x40, 0x09, 0xEA, 0x54, 0xC4, 0x00, 0x0C, 0xDB, 0x64, 0x08, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x02 }, -{ 0xCF, 0x40, 0x0C, 0xAA, 0x54, 0xC4, 0x00, 0x18, 0xF9, 0x64, 0x0C, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x02 }, -{ 0xC9, 0x0E, 0x88, 0xD9, 0x3E, 0xC2, 0x08, 0x1A, 0xEA, 0x6C, 0x0C, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x05 }, -{ 0x03, 0x00, 0x15, 0x00, 0x64, 0x02, 0x00, 0x08, 0x00, 0x7C, 0x09, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x02 }, -{ 0x01, 0x00, 0x47, 0xD7, 0x6C, 0x01, 0x3F, 0x0C, 0xFB, 0x7C, 0x0A, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x04 }, -{ 0x00, 0x00, 0x36, 0x67, 0x7C, 0x01, 0x3F, 0x0E, 0xFA, 0x7C, 0x00, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x05 }, -{ 0x02, 0x00, 0x36, 0x68, 0x7C, 0x01, 0x3F, 0x0E, 0xFA, 0x7C, 0x00, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x05 }, -{ 0xCB, 0x00, 0xAF, 0x00, 0x7E, 0xC0, 0x00, 0xC0, 0x06, 0x7F, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x0F }, -{ 0x05, 0x0D, 0x80, 0xA6, 0x7F, 0x0B, 0x38, 0xA9, 0xD8, 0x00, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x04 }, -{ 0x0F, 0x00, 0x90, 0xFA, 0x68, 0x06, 0x00, 0xA7, 0x39, 0x54, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x06 }, -{ 0xC9, 0x15, 0xDD, 0xFF, 0x7C, 0x00, 0x00, 0xE7, 0xFC, 0x6C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x38 }, -{ 0x48, 0x3C, 0x30, 0xF6, 0x03, 0x0A, 0x38, 0x97, 0xE8, 0x00, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x04 }, -{ 0x07, 0x80, 0x0B, 0xC8, 0x65, 0x02, 0x3F, 0x0C, 0xEA, 0x7C, 0x0F, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x05 }, -{ 0x00, 0x21, 0x66, 0x40, 0x03, 0x00, 0x3F, 0x47, 0x00, 0x00, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x02 }, -{ 0x08, 0x00, 0x0B, 0x3C, 0x7C, 0x08, 0x3F, 0x06, 0xF3, 0x00, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x02 }, -{ 0x00, 0x3F, 0x4C, 0xFB, 0x00, 0x00, 0x3F, 0x0A, 0xE9, 0x7C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x05 } + { 0xCF, 0x40, 0x09, 0xEA, 0x54, 0xC4, 0x00, 0x0C, 0xDB, 0x64, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0xCF, 0x40, 0x0C, 0xAA, 0x54, 0xC4, 0x00, 0x18, 0xF9, 0x64, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0xC9, 0x0E, 0x88, 0xD9, 0x3E, 0xC2, 0x08, 0x1A, 0xEA, 0x6C, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 }, + { 0x03, 0x00, 0x15, 0x00, 0x64, 0x02, 0x00, 0x08, 0x00, 0x7C, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x01, 0x00, 0x47, 0xD7, 0x6C, 0x01, 0x3F, 0x0C, 0xFB, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 }, + { 0x00, 0x00, 0x36, 0x67, 0x7C, 0x01, 0x3F, 0x0E, 0xFA, 0x7C, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 }, + { 0x02, 0x00, 0x36, 0x68, 0x7C, 0x01, 0x3F, 0x0E, 0xFA, 0x7C, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 }, + { 0xCB, 0x00, 0xAF, 0x00, 0x7E, 0xC0, 0x00, 0xC0, 0x06, 0x7F, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0F }, + { 0x05, 0x0D, 0x80, 0xA6, 0x7F, 0x0B, 0x38, 0xA9, 0xD8, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 }, + { 0x0F, 0x00, 0x90, 0xFA, 0x68, 0x06, 0x00, 0xA7, 0x39, 0x54, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 }, + { 0xC9, 0x15, 0xDD, 0xFF, 0x7C, 0x00, 0x00, 0xE7, 0xFC, 0x6C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x38 }, + { 0x48, 0x3C, 0x30, 0xF6, 0x03, 0x0A, 0x38, 0x97, 0xE8, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 }, + { 0x07, 0x80, 0x0B, 0xC8, 0x65, 0x02, 0x3F, 0x0C, 0xEA, 0x7C, 0x0F, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 }, + { 0x00, 0x21, 0x66, 0x40, 0x03, 0x00, 0x3F, 0x47, 0x00, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x08, 0x00, 0x0B, 0x3C, 0x7C, 0x08, 0x3F, 0x06, 0xF3, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x00, 0x3F, 0x4C, 0xFB, 0x00, 0x00, 0x3F, 0x0A, 0xE9, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 } }; -static AdLibInstrument gm_percussion_to_fm[39] = { -{ 0x1A, 0x3F, 0x15, 0x05, 0x7C, 0x02, 0x21, 0x2B, 0xE4, 0x7C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x06 }, -{ 0x11, 0x12, 0x04, 0x07, 0x7C, 0x02, 0x23, 0x0B, 0xE5, 0x7C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x05 }, -{ 0x0A, 0x3F, 0x0B, 0x01, 0x7C, 0x1F, 0x1C, 0x46, 0xD0, 0x7C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x01 }, -{ 0x00, 0x3F, 0x0F, 0x00, 0x7C, 0x10, 0x12, 0x07, 0x00, 0x7C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x02 }, -{ 0x0F, 0x3F, 0x0B, 0x00, 0x7C, 0x1F, 0x0F, 0x19, 0xD0, 0x7C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x02 }, -{ 0x00, 0x3F, 0x1F, 0x00, 0x7E, 0x1F, 0x16, 0x07, 0x00, 0x7C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x03 }, -{ 0x12, 0x3F, 0x05, 0x06, 0x7C, 0x03, 0x1F, 0x4A, 0xD9, 0x7C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x03 }, -{ 0xCF, 0x7F, 0x08, 0xFF, 0x7E, 0x00, 0xC7, 0x2D, 0xF7, 0x73, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x02 }, -{ 0x12, 0x3F, 0x05, 0x06, 0x7C, 0x43, 0x21, 0x0C, 0xE9, 0x7C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x03 }, -{ 0xCF, 0x7F, 0x08, 0xCF, 0x7E, 0x00, 0x45, 0x2A, 0xF8, 0x4B, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x0C }, -{ 0x12, 0x3F, 0x06, 0x17, 0x7C, 0x03, 0x27, 0x0B, 0xE9, 0x7C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x03 }, -{ 0xCF, 0x7F, 0x08, 0xCD, 0x7E, 0x00, 0x40, 0x1A, 0x69, 0x63, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x0C }, -{ 0x13, 0x3F, 0x05, 0x06, 0x7C, 0x03, 0x17, 0x0A, 0xD9, 0x7C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x03 }, -{ 0x15, 0x3F, 0x05, 0x06, 0x7C, 0x03, 0x21, 0x0C, 0xE9, 0x7C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x03 }, -{ 0xCF, 0x3F, 0x2B, 0xFB, 0x7E, 0xC0, 0x1E, 0x1A, 0xCA, 0x7F, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x10 }, -{ 0x17, 0x3F, 0x04, 0x09, 0x7C, 0x03, 0x22, 0x0D, 0xE9, 0x7C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x03 }, -{ 0xCF, 0x3F, 0x0F, 0x5E, 0x7C, 0xC6, 0x13, 0x00, 0xCA, 0x7F, 0x04, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x03 }, -{ 0xCF, 0x3F, 0x7E, 0x9D, 0x7C, 0xC8, 0xC0, 0x0A, 0xBA, 0x74, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x06 }, -{ 0xCF, 0x3F, 0x4D, 0x9F, 0x7C, 0xC6, 0x00, 0x08, 0xDA, 0x5B, 0x04, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x04 }, -{ 0xCF, 0x3F, 0x5D, 0xAA, 0x7A, 0xC0, 0xA4, 0x67, 0x99, 0x7C, 0x0A, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x02 }, -{ 0xCF, 0x3F, 0x4A, 0xFD, 0x7C, 0xCF, 0x00, 0x59, 0xEA, 0x7C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x02 }, -{ 0x0F, 0x18, 0x0A, 0xFA, 0x57, 0x06, 0x07, 0x06, 0x39, 0x7C, 0x0A, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x02 }, -{ 0xCF, 0x3F, 0x2B, 0xFC, 0x7C, 0xCC, 0xC6, 0x0B, 0xEA, 0x7F, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x10 }, -{ 0x05, 0x1A, 0x04, 0x00, 0x7C, 0x12, 0x10, 0x0C, 0xEA, 0x7C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x07 }, -{ 0x04, 0x19, 0x04, 0x00, 0x7C, 0x12, 0x10, 0x2C, 0xEA, 0x7C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x04 }, -{ 0x04, 0x0A, 0x04, 0x00, 0x6C, 0x01, 0x07, 0x0D, 0xFA, 0x74, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x07 }, -{ 0x15, 0x14, 0x05, 0x00, 0x7D, 0x01, 0x07, 0x5C, 0xE9, 0x7C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x05 }, -{ 0x10, 0x10, 0x05, 0x08, 0x7C, 0x01, 0x08, 0x0D, 0xEA, 0x7C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x05 }, -{ 0x11, 0x00, 0x06, 0x87, 0x7F, 0x02, 0x40, 0x09, 0x59, 0x68, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x08 }, -{ 0x13, 0x26, 0x04, 0x6A, 0x7F, 0x01, 0x00, 0x08, 0x5A, 0x7C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x08 }, -{ 0xCF, 0x4E, 0x0C, 0xAA, 0x50, 0xC4, 0x00, 0x18, 0xF9, 0x54, 0x04, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x02 }, -{ 0xCF, 0x4E, 0x0C, 0xAA, 0x50, 0xC3, 0x00, 0x18, 0xF8, 0x54, 0x04, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x02 }, -{ 0xCB, 0x3F, 0x8F, 0x00, 0x7E, 0xC5, 0x00, 0x98, 0xD6, 0x5F, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x0D }, -{ 0x0C, 0x18, 0x87, 0xB3, 0x7F, 0x19, 0x10, 0x55, 0x75, 0x7C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x02 }, -{ 0x05, 0x11, 0x15, 0x00, 0x64, 0x02, 0x08, 0x08, 0x00, 0x5C, 0x09, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x02 }, -{ 0x04, 0x08, 0x15, 0x00, 0x48, 0x01, 0x08, 0x08, 0x00, 0x60, 0x08, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x02 }, -{ 0xDA, 0x00, 0x53, 0x30, 0x68, 0x07, 0x1E, 0x49, 0xC4, 0x7E, 0x03, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0x1C, 0x00, 0x07, 0xBC, 0x6C, 0x0C, 0x14, 0x0B, 0x6A, 0x7E, 0x0B, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x03 }, -{ 0x0A, 0x0E, 0x7F, 0x00, 0x7D, 0x13, 0x20, 0x28, 0x03, 0x7C, 0x06, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 } +static AdLibInstrument g_gmPercussionInstruments[39] = { + { 0x1A, 0x3F, 0x15, 0x05, 0x7C, 0x02, 0x21, 0x2B, 0xE4, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 }, + { 0x11, 0x12, 0x04, 0x07, 0x7C, 0x02, 0x23, 0x0B, 0xE5, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 }, + { 0x0A, 0x3F, 0x0B, 0x01, 0x7C, 0x1F, 0x1C, 0x46, 0xD0, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x01 }, + { 0x00, 0x3F, 0x0F, 0x00, 0x7C, 0x10, 0x12, 0x07, 0x00, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x0F, 0x3F, 0x0B, 0x00, 0x7C, 0x1F, 0x0F, 0x19, 0xD0, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x00, 0x3F, 0x1F, 0x00, 0x7E, 0x1F, 0x16, 0x07, 0x00, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0x12, 0x3F, 0x05, 0x06, 0x7C, 0x03, 0x1F, 0x4A, 0xD9, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0xCF, 0x7F, 0x08, 0xFF, 0x7E, 0x00, 0xC7, 0x2D, 0xF7, 0x73, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x12, 0x3F, 0x05, 0x06, 0x7C, 0x43, 0x21, 0x0C, 0xE9, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0xCF, 0x7F, 0x08, 0xCF, 0x7E, 0x00, 0x45, 0x2A, 0xF8, 0x4B, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0C }, + { 0x12, 0x3F, 0x06, 0x17, 0x7C, 0x03, 0x27, 0x0B, 0xE9, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0xCF, 0x7F, 0x08, 0xCD, 0x7E, 0x00, 0x40, 0x1A, 0x69, 0x63, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0C }, + { 0x13, 0x3F, 0x05, 0x06, 0x7C, 0x03, 0x17, 0x0A, 0xD9, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0x15, 0x3F, 0x05, 0x06, 0x7C, 0x03, 0x21, 0x0C, 0xE9, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0xCF, 0x3F, 0x2B, 0xFB, 0x7E, 0xC0, 0x1E, 0x1A, 0xCA, 0x7F, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x10 }, + { 0x17, 0x3F, 0x04, 0x09, 0x7C, 0x03, 0x22, 0x0D, 0xE9, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0xCF, 0x3F, 0x0F, 0x5E, 0x7C, 0xC6, 0x13, 0x00, 0xCA, 0x7F, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0xCF, 0x3F, 0x7E, 0x9D, 0x7C, 0xC8, 0xC0, 0x0A, 0xBA, 0x74, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 }, + { 0xCF, 0x3F, 0x4D, 0x9F, 0x7C, 0xC6, 0x00, 0x08, 0xDA, 0x5B, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 }, + { 0xCF, 0x3F, 0x5D, 0xAA, 0x7A, 0xC0, 0xA4, 0x67, 0x99, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0xCF, 0x3F, 0x4A, 0xFD, 0x7C, 0xCF, 0x00, 0x59, 0xEA, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x0F, 0x18, 0x0A, 0xFA, 0x57, 0x06, 0x07, 0x06, 0x39, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0xCF, 0x3F, 0x2B, 0xFC, 0x7C, 0xCC, 0xC6, 0x0B, 0xEA, 0x7F, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x10 }, + { 0x05, 0x1A, 0x04, 0x00, 0x7C, 0x12, 0x10, 0x0C, 0xEA, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 }, + { 0x04, 0x19, 0x04, 0x00, 0x7C, 0x12, 0x10, 0x2C, 0xEA, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 }, + { 0x04, 0x0A, 0x04, 0x00, 0x6C, 0x01, 0x07, 0x0D, 0xFA, 0x74, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 }, + { 0x15, 0x14, 0x05, 0x00, 0x7D, 0x01, 0x07, 0x5C, 0xE9, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 }, + { 0x10, 0x10, 0x05, 0x08, 0x7C, 0x01, 0x08, 0x0D, 0xEA, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 }, + { 0x11, 0x00, 0x06, 0x87, 0x7F, 0x02, 0x40, 0x09, 0x59, 0x68, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x08 }, + { 0x13, 0x26, 0x04, 0x6A, 0x7F, 0x01, 0x00, 0x08, 0x5A, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x08 }, + { 0xCF, 0x4E, 0x0C, 0xAA, 0x50, 0xC4, 0x00, 0x18, 0xF9, 0x54, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0xCF, 0x4E, 0x0C, 0xAA, 0x50, 0xC3, 0x00, 0x18, 0xF8, 0x54, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0xCB, 0x3F, 0x8F, 0x00, 0x7E, 0xC5, 0x00, 0x98, 0xD6, 0x5F, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0D }, + { 0x0C, 0x18, 0x87, 0xB3, 0x7F, 0x19, 0x10, 0x55, 0x75, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x05, 0x11, 0x15, 0x00, 0x64, 0x02, 0x08, 0x08, 0x00, 0x5C, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x04, 0x08, 0x15, 0x00, 0x48, 0x01, 0x08, 0x08, 0x00, 0x60, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0xDA, 0x00, 0x53, 0x30, 0x68, 0x07, 0x1E, 0x49, 0xC4, 0x7E, 0x03, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x1C, 0x00, 0x07, 0xBC, 0x6C, 0x0C, 0x14, 0x0B, 0x6A, 0x7E, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0x0A, 0x0E, 0x7F, 0x00, 0x7D, 0x13, 0x20, 0x28, 0x03, 0x7C, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }; -static const byte gm_percussion_lookup[128] = { +#ifdef ENABLE_OPL3 +static const AdLibInstrument g_gmInstrumentsOPL3[128][2] = { + { { 0xC2, 0xC2, 0x0A, 0x6B, 0xA0, 0xC2, 0x08, 0x0D, 0x88, 0xC8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x23 }, + { 0x02, 0x00, 0x0C, 0x78, 0x61, 0x04, 0x4C, 0x0B, 0x9A, 0xC8, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x23 } }, + { { 0x22, 0x53, 0x0E, 0x8A, 0x60, 0x14, 0x06, 0x1D, 0x7A, 0xB8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x22, 0x5A, 0x0E, 0x8A, 0x40, 0x14, 0x2F, 0x0E, 0x7A, 0x88, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x06, 0x00, 0x1C, 0x79, 0x70, 0x02, 0x00, 0x4B, 0x79, 0xA8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x06, 0x00, 0x1A, 0x79, 0x60, 0x02, 0x00, 0x4C, 0xA9, 0xC8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xC2, 0x80, 0x0B, 0x89, 0x90, 0xC2, 0x06, 0x1B, 0xA8, 0xB0, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x23 }, + { 0x04, 0x28, 0x5D, 0xB8, 0x01, 0x02, 0x00, 0x3C, 0x70, 0x88, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xC2, 0x17, 0x3D, 0x6A, 0x00, 0xC4, 0x2E, 0x2D, 0xC9, 0x40, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC2, 0x17, 0x3D, 0x6A, 0x00, 0xC4, 0x2E, 0x2D, 0xC9, 0x40, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x06, 0x1E, 0x1C, 0x99, 0x00, 0x02, 0x3A, 0x4C, 0x79, 0x00, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x06, 0x1E, 0x1C, 0x99, 0x00, 0x02, 0x3A, 0x4C, 0x79, 0x00, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x84, 0x40, 0x3B, 0x5A, 0x63, 0x81, 0x00, 0x3B, 0x5A, 0xD3, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x87, 0x40, 0x3A, 0x5A, 0x94, 0x82, 0x04, 0x3D, 0x59, 0xAC, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x84, 0x40, 0x3B, 0x5A, 0xC3, 0x81, 0x00, 0x3B, 0x5A, 0xFB, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x84, 0x40, 0x3B, 0x5A, 0xC3, 0x81, 0x00, 0x3B, 0x5A, 0xFB, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x8C, 0x80, 0x05, 0xEA, 0xA9, 0x82, 0x04, 0x3D, 0xAA, 0xB0, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x8C, 0x80, 0x06, 0x98, 0xA9, 0x86, 0x10, 0x36, 0x7A, 0xFD, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x85, 0x40, 0x0D, 0xEC, 0xE1, 0x84, 0x58, 0x3E, 0xCB, 0xF8, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x84, 0x40, 0x0D, 0xEB, 0xE0, 0x84, 0x48, 0x3E, 0xCA, 0xC0, 0x05, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x8A, 0xC0, 0x0C, 0xDC, 0xA0, 0x88, 0x58, 0x3D, 0xDA, 0xF8, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x8A, 0xC0, 0x0C, 0xDC, 0xA0, 0x88, 0x58, 0x3D, 0xDA, 0xF8, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xC9, 0x40, 0x2B, 0x78, 0x8A, 0xC2, 0x0A, 0x4C, 0x8A, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x1A }, + { 0xCA, 0x40, 0x47, 0xCA, 0xB4, 0xC2, 0x00, 0x57, 0x8A, 0xB8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x1A } }, + { { 0x2A, 0x0E, 0x17, 0x89, 0x50, 0x22, 0x0C, 0x1B, 0x09, 0xE0, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x2A, 0x1A, 0x19, 0x8A, 0x00, 0x22, 0x38, 0x0B, 0x0A, 0x00, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE7, 0x9B, 0x08, 0x08, 0x4A, 0xE2, 0x06, 0x0A, 0x08, 0xE0, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE7, 0x9B, 0x08, 0x08, 0x4A, 0xE2, 0x2F, 0x0A, 0x08, 0x68, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xC5, 0x0A, 0x05, 0xDC, 0xB8, 0x84, 0x06, 0x00, 0xEC, 0xC0, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x09, 0x10, 0x04, 0x5B, 0xA5, 0x02, 0x08, 0x00, 0xEC, 0x70, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x86, 0x40, 0x5D, 0x5A, 0x81, 0x81, 0x00, 0x0B, 0x5A, 0xFB, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x86, 0x40, 0x5D, 0x5A, 0x81, 0x81, 0x00, 0x0B, 0x5A, 0xFB, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xED, 0x0F, 0x5B, 0xC8, 0xC8, 0xE2, 0x9F, 0x4A, 0xE9, 0xF9, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE6, 0x40, 0x0A, 0xA7, 0x64, 0xE2, 0x8B, 0x6A, 0x79, 0xB1, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE8, 0x4F, 0x3A, 0xD7, 0xF8, 0xE2, 0x97, 0x49, 0xF9, 0xF9, 0x05, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC9, 0x02, 0x16, 0x9A, 0xAB, 0xC4, 0x15, 0x46, 0xBA, 0xF8, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE1, 0x08, 0x2F, 0xF7, 0xE1, 0xF3, 0x42, 0x8F, 0xC7, 0xC2, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE3, 0x00, 0x2D, 0xF7, 0xC1, 0xE4, 0x40, 0x7F, 0xC7, 0xD2, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x01, 0x8C, 0x9F, 0xDA, 0xE8, 0xE4, 0x50, 0x9F, 0xDA, 0xF2, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x02, 0x80, 0x9F, 0xDA, 0x00, 0xE3, 0x50, 0x9F, 0xD9, 0xFA, 0x03, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x08, 0xD5, 0x9D, 0xA5, 0x89, 0xE2, 0x3F, 0x9F, 0xD6, 0x91, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x08, 0xD5, 0x9D, 0xA5, 0x89, 0xE2, 0x3F, 0x9F, 0xD6, 0x91, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE5, 0x0F, 0x7D, 0xB8, 0x5A, 0xA2, 0x0C, 0x7C, 0xC7, 0xC1, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x06, 0x4C, 0xAC, 0x56, 0x31, 0x02, 0x08, 0x8D, 0x46, 0xDC, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xF2, 0x2A, 0x9F, 0xDB, 0x01, 0xE1, 0x04, 0x8F, 0xD7, 0xC2, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xF2, 0x00, 0x9F, 0xDB, 0xA9, 0xE1, 0x00, 0x8F, 0xD7, 0xBA, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE4, 0x88, 0x9C, 0x50, 0xC8, 0xE2, 0x18, 0x70, 0xC4, 0xF8, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE6, 0x00, 0x9C, 0x50, 0xB0, 0xE4, 0x00, 0x70, 0xC4, 0xA0, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x02, 0xA3, 0x0D, 0xDA, 0x01, 0xC2, 0x35, 0x5D, 0x58, 0x00, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x18 }, + { 0x02, 0xA3, 0x0D, 0xDA, 0x01, 0xC2, 0x35, 0x5D, 0x58, 0x00, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x18 } }, + { { 0x42, 0x53, 0x3E, 0xEB, 0x48, 0xD4, 0x05, 0x1D, 0xA9, 0xC9, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x18 }, + { 0x42, 0x54, 0x6F, 0xEB, 0x61, 0xD4, 0x02, 0x2E, 0xA9, 0xC8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x18 } }, + { { 0xC2, 0x00, 0x59, 0x17, 0xB1, 0xC2, 0x1E, 0x6D, 0x98, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x19 }, + { 0xC2, 0x00, 0x08, 0xB3, 0x99, 0xC2, 0x06, 0x2B, 0x58, 0xFA, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x19 } }, + { { 0xC6, 0x01, 0x2D, 0xA7, 0x88, 0xC2, 0x08, 0x0E, 0xA7, 0xC1, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC4, 0x00, 0x2D, 0xA7, 0x91, 0xC2, 0x02, 0x0E, 0xA7, 0xD1, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xC2, 0x0C, 0x06, 0x06, 0xA9, 0xC2, 0x3F, 0x08, 0xB8, 0xF9, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0A }, + { 0xC1, 0x00, 0x68, 0x50, 0xB8, 0xC2, 0x00, 0x48, 0x84, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0A } }, + { { 0xC2, 0x2E, 0x4F, 0x77, 0x00, 0xC4, 0x08, 0x0E, 0x98, 0xB1, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC2, 0x2F, 0x6F, 0x79, 0x00, 0xC8, 0x0F, 0x5E, 0x98, 0xB9, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xC2, 0x30, 0x4F, 0xCA, 0x01, 0xC4, 0x0D, 0x0E, 0xB8, 0xFB, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC2, 0x30, 0x4F, 0xCA, 0x01, 0xC4, 0x0D, 0x0E, 0xB8, 0xFB, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xC4, 0x29, 0x4F, 0xCA, 0x03, 0xC8, 0x0D, 0x0C, 0xB7, 0xF9, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0B }, + { 0xC4, 0x29, 0x4F, 0xCA, 0x03, 0xC8, 0x0D, 0x0C, 0xB7, 0xF9, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0B } }, + { { 0xC2, 0x41, 0x3D, 0x96, 0x88, 0xC4, 0xCA, 0x0E, 0xC7, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x20 }, + { 0xC2, 0x04, 0x58, 0xC9, 0x90, 0xC2, 0x94, 0x2C, 0xB9, 0xF0, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x20 } }, + { { 0x31, 0x13, 0x2D, 0xD7, 0x78, 0xE2, 0x18, 0x2E, 0xB8, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x31, 0x13, 0x2D, 0xD7, 0x78, 0xE2, 0x18, 0x2E, 0xB8, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x22, 0x86, 0x0D, 0xD7, 0xA0, 0xE4, 0x18, 0x5E, 0xB8, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x28 }, + { 0x22, 0x86, 0x0D, 0xD7, 0xA0, 0xE4, 0x18, 0x5E, 0xB8, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x28 } }, + { { 0xF2, 0x0A, 0x0D, 0xD7, 0x80, 0xE4, 0x1F, 0x5E, 0xB8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xD2, 0x06, 0x9A, 0xD7, 0xA0, 0xC2, 0x1F, 0x59, 0xB8, 0xF8, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xF2, 0x09, 0x4B, 0xD6, 0x90, 0xE4, 0x1F, 0x1C, 0xB8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x28 }, + { 0xF2, 0x09, 0x4B, 0xD6, 0x90, 0xE4, 0x1F, 0x1C, 0xB8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x28 } }, + { { 0x62, 0x11, 0x0C, 0xE6, 0x78, 0xE4, 0x1F, 0x0C, 0xC8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x62, 0x11, 0x0C, 0xE6, 0x78, 0xE4, 0x1F, 0x0C, 0xC8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE2, 0x12, 0x3D, 0xE6, 0x68, 0xE4, 0x1F, 0x7D, 0xB8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x12, 0x3D, 0xE6, 0x68, 0xE4, 0x1F, 0x7D, 0xB8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE2, 0x13, 0x3D, 0xE6, 0x68, 0xE4, 0x1F, 0x5D, 0xB8, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x13, 0x3D, 0xE6, 0x68, 0xE4, 0x1F, 0x5D, 0xB8, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xA2, 0x40, 0x5D, 0xBA, 0x7B, 0xE2, 0x00, 0x8F, 0xD8, 0xF1, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xA2, 0x40, 0x5D, 0xBA, 0x7B, 0xE2, 0x00, 0x8F, 0xD8, 0xF1, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE2, 0x40, 0x3D, 0xDA, 0x73, 0xE1, 0x00, 0x7E, 0xD8, 0xF2, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x40, 0x3D, 0xDA, 0x73, 0xE1, 0x00, 0x7E, 0xD8, 0xF2, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x62, 0x00, 0x6D, 0xFA, 0xB9, 0xE2, 0x00, 0x8F, 0xC8, 0xF1, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x62, 0x00, 0x6D, 0xFA, 0xB9, 0xE2, 0x00, 0x8F, 0xC8, 0xF1, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE1, 0x00, 0x4E, 0xDB, 0x92, 0xE3, 0x18, 0x6F, 0xE9, 0xFA, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE1, 0x00, 0x4E, 0xDB, 0xCA, 0xE2, 0x00, 0x6F, 0xE9, 0xFA, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE1, 0x00, 0x4E, 0xDB, 0xCA, 0xE2, 0x00, 0x7F, 0xE9, 0xFA, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE1, 0x00, 0x4E, 0xDB, 0xCA, 0xE2, 0x00, 0x7F, 0xE9, 0xFA, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x02, 0x0F, 0x66, 0xAA, 0xA1, 0x02, 0x64, 0x29, 0xF9, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 }, + { 0x02, 0x00, 0x65, 0xAA, 0xF1, 0x02, 0x4A, 0x28, 0xF9, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 } }, + { { 0x16, 0x4A, 0x04, 0xBA, 0x71, 0xC2, 0x48, 0x2E, 0xCA, 0xF0, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0x14, 0xC0, 0x66, 0x08, 0x90, 0xC2, 0x48, 0x2C, 0x0A, 0xA0, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } }, + { { 0x02, 0x0A, 0x01, 0x7A, 0xB1, 0x02, 0x12, 0x2A, 0xEA, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x02, 0x06, 0x75, 0x05, 0xB1, 0x01, 0x3F, 0x28, 0xEA, 0xF9, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x16 } }, + { { 0x62, 0x53, 0x9C, 0xBA, 0x61, 0x62, 0x5A, 0xAD, 0xCA, 0xC1, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xF2, 0x40, 0x9F, 0x8A, 0x98, 0xE2, 0x11, 0x7F, 0xB8, 0xFA, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xF2, 0x40, 0x6E, 0xDA, 0x91, 0xE2, 0x13, 0x8F, 0xF9, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xF2, 0x40, 0x6E, 0xDA, 0x91, 0xE2, 0x13, 0x8F, 0xF9, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE2, 0x40, 0x8F, 0xFA, 0xA0, 0xF2, 0x04, 0x7F, 0xFA, 0xF9, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x40, 0x8F, 0xFA, 0xA0, 0xF2, 0x04, 0x7F, 0xFA, 0xF9, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE4, 0xA0, 0xCE, 0x5B, 0x02, 0xE2, 0x32, 0x7F, 0xFB, 0x79, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0xA0, 0xCE, 0x5B, 0x02, 0xE2, 0x32, 0x7F, 0xFB, 0x79, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE6, 0x80, 0x9C, 0x99, 0x82, 0xE2, 0x04, 0x8D, 0x78, 0xC0, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE0, 0x44, 0x8A, 0xA9, 0x5B, 0xE1, 0x06, 0x8D, 0x79, 0xBA, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE8, 0xA0, 0xAC, 0x67, 0x02, 0xE2, 0x06, 0x7C, 0x7A, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xEA, 0xA0, 0xAC, 0x67, 0x02, 0xE2, 0x00, 0x7C, 0x7A, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE7, 0x94, 0xAD, 0xB7, 0x03, 0xE2, 0x00, 0x7C, 0xBA, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE7, 0x94, 0xAD, 0xB7, 0x03, 0xE2, 0x00, 0x7C, 0xBA, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xC3, 0x3F, 0x4B, 0xE9, 0xFA, 0xC1, 0x3F, 0x9B, 0xF9, 0xFB, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 }, + { 0xC3, 0x3F, 0x4B, 0xE9, 0xFA, 0xC1, 0x3F, 0x9B, 0xF9, 0xFB, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 } }, + { { 0xB2, 0x20, 0xAD, 0xE9, 0x00, 0x62, 0x05, 0x8F, 0xC8, 0xD0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xB2, 0x25, 0xAD, 0xE9, 0x00, 0x62, 0x00, 0x8F, 0xC8, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xF2, 0x02, 0xAF, 0xFB, 0x90, 0xF6, 0x54, 0x8F, 0xE9, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x00, 0x9F, 0xFA, 0xB0, 0xF2, 0x58, 0x7F, 0xEA, 0xF8, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xF2, 0x00, 0xAF, 0x88, 0xA8, 0xF2, 0x46, 0x6E, 0xC9, 0xE0, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xD2, 0x00, 0x7B, 0x88, 0xA8, 0xD2, 0x4C, 0x69, 0xE9, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xF2, 0x2A, 0x9F, 0x98, 0x01, 0xE2, 0x8F, 0x4E, 0x78, 0xC0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xD2, 0x02, 0x85, 0x89, 0xC8, 0xD2, 0x94, 0x77, 0x49, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE2, 0x02, 0x9F, 0xB8, 0x90, 0x22, 0x8A, 0x9F, 0xE8, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC2, 0x00, 0x86, 0xB8, 0x98, 0x02, 0x8F, 0x89, 0xE8, 0xF9, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE2, 0x2A, 0x7F, 0xB8, 0x01, 0xE4, 0x00, 0x0D, 0xC5, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x2A, 0x7F, 0xB8, 0x01, 0xE4, 0x00, 0x0D, 0xC5, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE4, 0x28, 0x8E, 0xE8, 0x01, 0xF2, 0x00, 0x4D, 0xD6, 0xF9, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x28, 0x8E, 0xE8, 0x01, 0xF2, 0x00, 0x4D, 0xD6, 0xF9, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x62, 0x23, 0x8F, 0xEA, 0x00, 0xF2, 0x00, 0x5E, 0xD9, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x62, 0x23, 0x8F, 0xEA, 0x00, 0xF2, 0x00, 0x5E, 0xD9, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xB4, 0x26, 0x6E, 0x98, 0x01, 0x62, 0x00, 0x7D, 0xC8, 0xF9, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xB4, 0x26, 0x6E, 0x98, 0x01, 0x62, 0x00, 0x7D, 0xC8, 0xF9, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE2, 0x2E, 0x20, 0xD9, 0x01, 0xF2, 0x1A, 0x90, 0xF8, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xD2, 0x10, 0x69, 0x18, 0xCF, 0xD4, 0x14, 0x5B, 0x04, 0xFD, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE4, 0x28, 0x7E, 0xF8, 0x01, 0xE2, 0x23, 0x8E, 0xE8, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x28, 0x7E, 0xF8, 0x01, 0xE2, 0x23, 0x8E, 0xE8, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xB8, 0x28, 0x9E, 0x98, 0x01, 0x62, 0x00, 0x3D, 0xC8, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xB8, 0x28, 0x9E, 0x98, 0x01, 0x62, 0x00, 0x3D, 0xC8, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x62, 0x00, 0x8E, 0xC9, 0x79, 0xE6, 0x00, 0x7E, 0xD8, 0xD0, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x62, 0x00, 0x8E, 0xC9, 0x79, 0xE6, 0x00, 0x7E, 0xD8, 0xD0, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE2, 0x00, 0x5F, 0xF9, 0x88, 0xE4, 0x9E, 0x8F, 0xF8, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC2, 0x00, 0x97, 0xF9, 0x90, 0xC9, 0x80, 0x69, 0x98, 0xA0, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x62, 0x0C, 0x6E, 0xD8, 0x79, 0x2A, 0x09, 0x7D, 0xD8, 0xC0, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x02, 0x04, 0x8A, 0xD8, 0x80, 0x0C, 0x12, 0x85, 0xD8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE4, 0x00, 0x7E, 0x89, 0x70, 0xE6, 0x8F, 0x80, 0xF8, 0xF0, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC4, 0x00, 0x67, 0x59, 0x70, 0xC6, 0x8A, 0x77, 0xA8, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE4, 0x80, 0x6C, 0xD9, 0x60, 0xE2, 0x00, 0x8D, 0xC8, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x80, 0x6C, 0xD9, 0x60, 0xE2, 0x00, 0x8D, 0xC8, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE2, 0x80, 0x88, 0x48, 0x98, 0xE2, 0x1E, 0x8E, 0xC9, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xF2, 0x40, 0xA8, 0xB9, 0x80, 0xE2, 0x0C, 0x89, 0x09, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE4, 0x00, 0x77, 0xC5, 0xA8, 0xE2, 0x00, 0x9E, 0xD7, 0xE0, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x00, 0x77, 0xC5, 0xA8, 0xE2, 0x00, 0x9E, 0xD7, 0xE0, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE4, 0x80, 0x86, 0xB9, 0xA8, 0xE2, 0x14, 0x9F, 0xD7, 0xB0, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC2, 0x80, 0x94, 0x09, 0x78, 0xC2, 0x00, 0x97, 0x97, 0xF8, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE2, 0x00, 0x68, 0x68, 0xAA, 0xE2, 0x0A, 0x9B, 0xB3, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC2, 0x00, 0x86, 0x68, 0xA0, 0xC2, 0x00, 0x77, 0x47, 0xE0, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE4, 0x00, 0xA6, 0x87, 0x81, 0xE2, 0x0A, 0x7E, 0xC9, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x00, 0x89, 0x40, 0x79, 0xE2, 0x00, 0x7E, 0xC9, 0x90, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE4, 0x80, 0xAA, 0xB8, 0x90, 0xE2, 0x00, 0x9E, 0xF9, 0xC0, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE6, 0x80, 0x9D, 0xB8, 0x51, 0xE2, 0x00, 0x9E, 0xF9, 0xA0, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE2, 0x80, 0x8E, 0x64, 0xD0, 0xE2, 0x28, 0x6F, 0x73, 0xF8, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x80, 0x8E, 0x64, 0xD0, 0xE2, 0x28, 0x6F, 0x73, 0xF8, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE8, 0x00, 0x7D, 0x99, 0xA8, 0xE6, 0x80, 0x80, 0xF8, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE8, 0x00, 0x7D, 0x99, 0xA8, 0xE6, 0x80, 0x80, 0xF8, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE6, 0x00, 0x9F, 0xB9, 0xD9, 0xE1, 0x00, 0x8F, 0xC8, 0xF9, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE6, 0x00, 0x9F, 0xB9, 0xD9, 0xE1, 0x00, 0x8F, 0xC8, 0xF9, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE4, 0x00, 0x09, 0x68, 0x92, 0xE2, 0x2B, 0x9E, 0xF3, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x00, 0x09, 0x68, 0x92, 0xE2, 0x2B, 0x9E, 0xF3, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xC4, 0x00, 0x99, 0xE8, 0x73, 0xE2, 0x25, 0x6F, 0x93, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC4, 0x00, 0x99, 0xE8, 0x73, 0xE2, 0x25, 0x6F, 0x93, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE6, 0x00, 0x6F, 0xDA, 0xC9, 0xE2, 0x05, 0x2F, 0xD8, 0xAA, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x00, 0x4F, 0xDA, 0xC8, 0xE2, 0x00, 0x0F, 0xD8, 0xD0, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xEC, 0x60, 0x9D, 0xC7, 0x00, 0xE2, 0x21, 0x7F, 0xC9, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xEC, 0x60, 0x9D, 0xC7, 0x00, 0xE2, 0x21, 0x7F, 0xC9, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE3, 0x00, 0x0F, 0xF7, 0xF9, 0xE1, 0x3F, 0x0F, 0xA7, 0x01, 0x0D, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE3, 0x00, 0x0F, 0xF7, 0xF9, 0xE1, 0x3F, 0x0F, 0xA7, 0x01, 0x0D, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE4, 0xA9, 0x0F, 0xA8, 0x02, 0xE2, 0x3C, 0x5F, 0xDA, 0x78, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0xA9, 0x0F, 0xA8, 0x02, 0xE2, 0x3C, 0x5F, 0xDA, 0x78, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE8, 0x40, 0x0D, 0x89, 0xF9, 0xE2, 0x17, 0x7E, 0xD9, 0xF8, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE8, 0x40, 0x0D, 0x89, 0xF9, 0xE2, 0x17, 0x7E, 0xD9, 0xF8, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE1, 0x00, 0xDF, 0x8A, 0xAA, 0xE2, 0x5E, 0xCF, 0xBA, 0xFA, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE1, 0x00, 0xDF, 0x8A, 0xAA, 0xE2, 0x5E, 0xCF, 0xBA, 0xFA, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE2, 0x00, 0x0B, 0x68, 0xC0, 0xE2, 0x01, 0x9E, 0xB8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x00, 0x0B, 0x68, 0xC0, 0xE2, 0x01, 0x9E, 0xB8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xEA, 0x00, 0xAE, 0xAB, 0x91, 0xE2, 0x00, 0xAE, 0xBA, 0xD8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xEA, 0x00, 0xAE, 0xAB, 0x91, 0xE2, 0x00, 0xAE, 0xBA, 0xD8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xEB, 0x80, 0x8C, 0xCB, 0x72, 0xE2, 0x86, 0xAF, 0xCA, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xEB, 0xC3, 0x9C, 0xCB, 0xA2, 0xE2, 0x4C, 0xAE, 0xCA, 0xFA, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE5, 0x40, 0xDB, 0x3B, 0x78, 0xE2, 0x80, 0xBE, 0xCA, 0xE1, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x80, 0x8E, 0xCB, 0xC0, 0xE2, 0x90, 0xAE, 0xCA, 0xFB, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE4, 0x00, 0x9E, 0xAA, 0x79, 0xE1, 0x43, 0x0F, 0xBA, 0xFA, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x00, 0x9E, 0xAA, 0x79, 0xE1, 0x43, 0x0F, 0xBA, 0xFA, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE7, 0x40, 0xEB, 0xCA, 0x80, 0xE2, 0x03, 0xBF, 0xBA, 0xC2, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE3, 0x80, 0xDB, 0xCA, 0x40, 0xE2, 0x08, 0xDF, 0xBA, 0xC1, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xEA, 0x00, 0x68, 0xB8, 0x90, 0xE2, 0x0A, 0x8E, 0xB8, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xEA, 0x00, 0x68, 0xB8, 0x90, 0xE2, 0x0A, 0x8E, 0xB8, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x61, 0x00, 0xBE, 0x99, 0xFA, 0xE3, 0x40, 0xCF, 0xCA, 0xF9, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x62, 0x00, 0xCE, 0x9A, 0xA8, 0xE2, 0x45, 0xCF, 0xCA, 0xA0, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xCD, 0x00, 0x0B, 0x00, 0x90, 0xC2, 0x58, 0x0C, 0x00, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x1C }, + { 0xCD, 0x00, 0x0B, 0x00, 0x90, 0xC2, 0x58, 0x0C, 0x00, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x1C } }, + { { 0xE2, 0x00, 0x0E, 0x00, 0xA2, 0xE2, 0x58, 0x5F, 0xD0, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x00, 0x0E, 0x00, 0xA2, 0xE2, 0x58, 0x5F, 0xD0, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xEC, 0x00, 0x7D, 0xDA, 0x80, 0xE2, 0x00, 0x5E, 0x9B, 0xA8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE6, 0x0A, 0x4C, 0xC9, 0x60, 0xE2, 0x07, 0x0C, 0x7A, 0xB8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE9, 0xC0, 0xEE, 0xD8, 0x83, 0xE2, 0x05, 0xDD, 0xAA, 0xE0, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xED, 0x48, 0xDE, 0xD8, 0xB4, 0xE1, 0x00, 0xDD, 0xAA, 0xA9, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xDA, 0x00, 0x8F, 0xAC, 0x92, 0x22, 0x05, 0x8D, 0x8A, 0xE9, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xEF, 0x00, 0x8C, 0xAA, 0x67, 0x25, 0x00, 0x9D, 0xAB, 0xC1, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x62, 0x82, 0xCB, 0x7A, 0xD8, 0xE6, 0x56, 0xAF, 0xDB, 0xE0, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x62, 0x84, 0xBB, 0xAA, 0xCA, 0xCF, 0x41, 0xAC, 0xDA, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xC2, 0x41, 0xAC, 0xBB, 0xBB, 0xC2, 0x85, 0x0E, 0xCB, 0xF9, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x12 }, + { 0xC2, 0x03, 0x6A, 0x5B, 0xA4, 0xC2, 0x0D, 0x2A, 0xBB, 0xFC, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x12 } }, + { { 0x75, 0x00, 0x0E, 0xBB, 0xB2, 0xE2, 0x1E, 0x0A, 0xA9, 0xF9, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x10 }, + { 0x62, 0x00, 0x04, 0x9A, 0xE8, 0xE2, 0x00, 0x0A, 0x48, 0xFD, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x10 } }, + { { 0x41, 0x00, 0x0E, 0xEA, 0xA3, 0xC2, 0x00, 0x08, 0xCA, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 }, + { 0x41, 0x00, 0x0E, 0xEA, 0xA3, 0xC2, 0x00, 0x08, 0xCA, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 } }, + { { 0xC1, 0x40, 0x0C, 0x59, 0xD2, 0xC2, 0x80, 0x3C, 0xAB, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0D }, + { 0xC1, 0x40, 0x0C, 0x59, 0xD2, 0xC2, 0x80, 0x3C, 0xAB, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0D } }, + { { 0x4B, 0x00, 0x0A, 0xF5, 0xC1, 0xC2, 0x19, 0x0C, 0xE9, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 }, + { 0x4B, 0x00, 0x0A, 0xF5, 0xC1, 0xC2, 0x19, 0x0C, 0xE9, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 } }, + { { 0x62, 0x00, 0x7F, 0xD8, 0xA8, 0xEA, 0x00, 0x8F, 0xD8, 0xF9, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x62, 0x00, 0x7F, 0xD8, 0xA8, 0xEA, 0x00, 0x8F, 0xD8, 0xF9, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE1, 0x00, 0x7F, 0xD9, 0xAA, 0xE1, 0x00, 0x8F, 0xD8, 0xFA, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE1, 0x00, 0x7F, 0xD9, 0xAA, 0xE1, 0x00, 0x8F, 0xD8, 0xFA, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE1, 0x00, 0x7F, 0xD9, 0xAA, 0xE1, 0x00, 0x8F, 0xD8, 0xFA, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE1, 0x00, 0x7F, 0xD9, 0xAA, 0xE1, 0x00, 0x8F, 0xD8, 0xFA, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xCF, 0x40, 0x09, 0xEA, 0xA8, 0xC4, 0x00, 0x0C, 0xDB, 0xC8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0xCF, 0x40, 0x09, 0xEA, 0xA8, 0xC4, 0x00, 0x0C, 0xDB, 0xC8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } }, + { { 0xCF, 0x40, 0x0C, 0xAA, 0xA8, 0xC4, 0x00, 0x18, 0xF9, 0xC8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0xCF, 0x40, 0x0C, 0xAA, 0xA8, 0xC4, 0x00, 0x18, 0xF9, 0xC8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } }, + { { 0xC9, 0x0C, 0x88, 0xD9, 0x6A, 0xC2, 0x14, 0x3A, 0xEA, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 }, + { 0xC5, 0x00, 0x98, 0xD9, 0x92, 0xC1, 0x16, 0x6E, 0xF9, 0xE8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 } }, + { { 0x03, 0x00, 0x15, 0x00, 0xC8, 0x02, 0x00, 0x08, 0x00, 0xF8, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x03, 0x00, 0x15, 0x00, 0xC8, 0x02, 0x00, 0x08, 0x00, 0xF8, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } }, + { { 0x01, 0x0C, 0x44, 0xE6, 0xE8, 0x01, 0x3F, 0x0C, 0xEA, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 }, + { 0x02, 0x3F, 0x05, 0x08, 0xF8, 0x03, 0x3F, 0x3C, 0xF9, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 } }, + { { 0x00, 0x00, 0x36, 0x67, 0xF8, 0x01, 0x3F, 0x0E, 0xFA, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 }, + { 0x00, 0x00, 0x36, 0x67, 0xF8, 0x01, 0x3F, 0x0E, 0xFA, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 } }, + { { 0x02, 0x00, 0x36, 0x68, 0xF8, 0x01, 0x3F, 0x0E, 0xFA, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 }, + { 0x02, 0x00, 0x36, 0x68, 0xF8, 0x01, 0x3F, 0x0E, 0xFA, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 } }, + { { 0xCB, 0x00, 0xAF, 0x00, 0xFA, 0xC0, 0x00, 0xC0, 0x06, 0xFB, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0F }, + { 0xCB, 0x00, 0xAF, 0x00, 0xFA, 0xC0, 0x00, 0xC0, 0x06, 0xFB, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0F } }, + { { 0x05, 0x0D, 0x80, 0xA6, 0xFB, 0x0B, 0x38, 0xA9, 0xD8, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 }, + { 0x05, 0x0D, 0x80, 0xA6, 0xFB, 0x0B, 0x38, 0xA9, 0xD8, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 } }, + { { 0x0F, 0x00, 0x90, 0xFA, 0xD0, 0x06, 0x00, 0xA7, 0x39, 0xA8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 }, + { 0x0F, 0x00, 0x90, 0xFA, 0xD0, 0x06, 0x00, 0xA7, 0x39, 0xA8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 } }, + { { 0xC9, 0x15, 0xDD, 0xFF, 0xF8, 0x00, 0x00, 0xE7, 0xFC, 0xD8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x38 }, + { 0xC9, 0x15, 0xDD, 0xFF, 0xF8, 0x00, 0x00, 0xE7, 0xFC, 0xD8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x38 } }, + { { 0x48, 0x3C, 0x30, 0xF6, 0x03, 0x0A, 0x38, 0x97, 0xE8, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 }, + { 0x48, 0x3C, 0x30, 0xF6, 0x03, 0x0A, 0x38, 0x97, 0xE8, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 } }, + { { 0x07, 0x80, 0x0B, 0xC8, 0xC9, 0x02, 0x3F, 0x0C, 0xEA, 0xF8, 0x0F, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 }, + { 0x07, 0x80, 0x0B, 0xC8, 0xC9, 0x02, 0x3F, 0x0C, 0xEA, 0xF8, 0x0F, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 } }, + { { 0x00, 0x21, 0x66, 0x40, 0x03, 0x00, 0x3F, 0x47, 0x00, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x00, 0x21, 0x66, 0x40, 0x03, 0x00, 0x3F, 0x47, 0x00, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } }, + { { 0x08, 0x00, 0x0B, 0x3C, 0xF8, 0x08, 0x3F, 0x06, 0xF3, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x08, 0x00, 0x0B, 0x3C, 0xF8, 0x08, 0x3F, 0x06, 0xF3, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } }, + { { 0x00, 0x3F, 0x4C, 0xFB, 0x00, 0x00, 0x3F, 0x0A, 0xE9, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 }, + { 0x00, 0x3F, 0x4C, 0xFB, 0x00, 0x00, 0x3F, 0x0A, 0xE9, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 } } +}; + +static const AdLibInstrument g_gmPercussionInstrumentsOPL3[39][2] = { + { { 0x1A, 0x3F, 0x15, 0x05, 0xF8, 0x02, 0x21, 0x2B, 0xE4, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 }, + { 0x11, 0x18, 0x15, 0x00, 0xF8, 0x12, 0x00, 0x2B, 0x03, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 } }, + { { 0x11, 0x12, 0x04, 0x07, 0xF8, 0x02, 0x18, 0x0B, 0xE5, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 }, + { 0x11, 0x28, 0x06, 0x04, 0xF8, 0x02, 0x1E, 0x1B, 0x02, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 } }, + { { 0x0A, 0x3F, 0x0B, 0x01, 0xF8, 0x1F, 0x13, 0x46, 0xD0, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x01 }, + { 0x04, 0x18, 0x06, 0x01, 0xB0, 0x10, 0x00, 0x07, 0x00, 0x90, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x01 } }, + { { 0x00, 0x3F, 0x0F, 0x00, 0xF8, 0x10, 0x0A, 0x07, 0x00, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x02, 0x14, 0x04, 0x00, 0xC0, 0x11, 0x08, 0x07, 0x00, 0xC6, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } }, + { { 0x0F, 0x3F, 0x0B, 0x00, 0xF8, 0x1F, 0x07, 0x19, 0xD0, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x0E, 0x32, 0x76, 0x03, 0xF8, 0x1F, 0x0F, 0x77, 0xD4, 0xFC, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } }, + { { 0x00, 0x3F, 0x1F, 0x00, 0xFA, 0x1F, 0x0C, 0x07, 0x00, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0x07, 0x11, 0x13, 0x00, 0xA0, 0x13, 0x00, 0x07, 0x00, 0xC8, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } }, + { { 0x12, 0x3F, 0x05, 0x06, 0xF8, 0x03, 0x16, 0x4A, 0xD9, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0x02, 0x22, 0x05, 0xB6, 0xF8, 0x04, 0x0A, 0x59, 0x03, 0xF8, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } }, + { { 0xCF, 0x7F, 0x08, 0xFF, 0xFA, 0x00, 0xC0, 0x2D, 0xF7, 0xE3, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0xD2, 0x7F, 0x04, 0x0F, 0xFA, 0x10, 0xCD, 0x24, 0x07, 0xFB, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } }, + { { 0x12, 0x3F, 0x05, 0x06, 0xF8, 0x43, 0x17, 0x0C, 0xE9, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0x12, 0x13, 0x09, 0x96, 0xF8, 0x44, 0x0A, 0x07, 0x03, 0xF8, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } }, + { { 0xCF, 0x7F, 0x08, 0xCF, 0xFA, 0x00, 0x40, 0x2A, 0xF8, 0x8B, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0C }, + { 0xCF, 0x7F, 0x05, 0x07, 0xFA, 0x00, 0x40, 0x25, 0x08, 0xC3, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0C } }, + { { 0x12, 0x3F, 0x06, 0x17, 0xF8, 0x03, 0x1D, 0x0B, 0xE9, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0x12, 0x1A, 0x08, 0x96, 0xF8, 0x44, 0x00, 0x08, 0x03, 0xF8, 0x05, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } }, + { { 0xCF, 0x7F, 0x08, 0xCD, 0xFA, 0x00, 0x40, 0x1A, 0x69, 0xB3, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0C }, + { 0xCD, 0x3F, 0x36, 0x05, 0xFC, 0x0F, 0x47, 0x46, 0x06, 0xDF, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0C } }, + { { 0x13, 0x3F, 0x05, 0x06, 0xF8, 0x03, 0x0D, 0x0A, 0xD9, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0x12, 0x14, 0x09, 0x96, 0xF8, 0x44, 0x02, 0x07, 0x03, 0xF8, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } }, + { { 0x15, 0x3F, 0x05, 0x06, 0xF8, 0x03, 0x16, 0x0C, 0xE9, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0x12, 0x00, 0x07, 0x96, 0xE8, 0x44, 0x02, 0x08, 0x03, 0xF8, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } }, + { { 0xCF, 0x3F, 0x2B, 0xFB, 0xFA, 0xC0, 0x16, 0x1A, 0xCA, 0xFB, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x10 }, + { 0xCF, 0x3F, 0x2B, 0xFB, 0xFA, 0xC0, 0x1E, 0x1A, 0xCA, 0xFB, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x10 } }, + { { 0x17, 0x3F, 0x04, 0x09, 0xF8, 0x03, 0x18, 0x0D, 0xE9, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0x12, 0x00, 0x07, 0x96, 0xF8, 0x44, 0x02, 0x08, 0xF9, 0xF8, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } }, + { { 0xCF, 0x3F, 0x0F, 0x5E, 0xF8, 0xC6, 0x0C, 0x00, 0xCA, 0xFB, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0xCF, 0x3F, 0x04, 0x57, 0xF8, 0xC5, 0x13, 0x06, 0x05, 0xFF, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } }, + { { 0xCF, 0x3F, 0x7E, 0x9D, 0xF8, 0xC8, 0xC0, 0x0A, 0xBA, 0xD0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 }, + { 0xCF, 0x3F, 0x77, 0x09, 0xF8, 0xC2, 0xC0, 0x08, 0xB5, 0xEA, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 } }, + { { 0xCF, 0x3F, 0x4D, 0x9F, 0xF8, 0xC6, 0x00, 0x08, 0xDA, 0xAB, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 }, + { 0xCF, 0x3F, 0x47, 0x06, 0xF8, 0xCD, 0x00, 0x07, 0x05, 0xB3, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 } }, + { { 0xCF, 0x3F, 0x5D, 0xAA, 0xF2, 0xC0, 0x8A, 0x67, 0x99, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0xCF, 0x3F, 0x9A, 0x69, 0xF8, 0xCF, 0x88, 0x88, 0x48, 0xFA, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } }, + { { 0xCF, 0x3F, 0x4A, 0xFD, 0xF8, 0xCF, 0x00, 0x59, 0xEA, 0xD8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0xCF, 0x3F, 0x48, 0x06, 0xF8, 0xCF, 0x00, 0x54, 0x04, 0xF9, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } }, + { { 0x0F, 0x18, 0x0A, 0xFA, 0xAB, 0x06, 0x06, 0x06, 0x39, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x03, 0x18, 0x04, 0x09, 0xAC, 0x05, 0x07, 0x08, 0x07, 0xF8, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } }, + { { 0xCF, 0x3F, 0x2B, 0xFC, 0xF8, 0xCC, 0xC4, 0x0B, 0xEA, 0xFB, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x10 }, + { 0xCF, 0x3F, 0x25, 0x06, 0xF8, 0xCC, 0xD7, 0x05, 0x02, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x10 } }, + { { 0x05, 0x1A, 0x04, 0x00, 0xF8, 0x12, 0x08, 0x0C, 0xEA, 0xE0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 }, + { 0x01, 0x00, 0x09, 0x08, 0x40, 0x13, 0x00, 0x2A, 0x0A, 0xD8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 } }, + { { 0x04, 0x19, 0x04, 0x00, 0xF8, 0x12, 0x08, 0x2C, 0xEA, 0xE0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 }, + { 0x04, 0x00, 0x07, 0x08, 0x40, 0x12, 0x00, 0x29, 0x08, 0xE0, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 } }, + { { 0x04, 0x0A, 0x04, 0x00, 0xD8, 0x01, 0x02, 0x0D, 0xFA, 0xE0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 }, + { 0x04, 0x00, 0x03, 0x09, 0x93, 0x02, 0x00, 0x28, 0x09, 0xE8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 } }, + { { 0x15, 0x14, 0x05, 0x00, 0xF9, 0x01, 0x03, 0x5C, 0xE9, 0xD8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 }, + { 0x05, 0x00, 0x03, 0x03, 0x49, 0x02, 0x00, 0x58, 0x08, 0xE0, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 } }, + { { 0x10, 0x10, 0x05, 0x08, 0xF8, 0x01, 0x03, 0x0D, 0xEA, 0xE8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 }, + { 0x10, 0x00, 0x0C, 0x0C, 0x48, 0x02, 0x00, 0x08, 0xB9, 0xE0, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 } }, + { { 0x11, 0x00, 0x06, 0x87, 0xFB, 0x02, 0x40, 0x09, 0x59, 0xC0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x08 }, + { 0x15, 0x00, 0x04, 0x87, 0xFB, 0x02, 0x40, 0x09, 0x59, 0xD0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x08 } }, + { { 0x13, 0x26, 0x04, 0x6A, 0xFB, 0x01, 0x00, 0x08, 0x5A, 0xE0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x08 }, + { 0x12, 0x26, 0x03, 0x6A, 0xFB, 0x02, 0x00, 0x06, 0x5A, 0xC0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x08 } }, + { { 0xCF, 0x4D, 0x0C, 0xAA, 0xA0, 0xC4, 0x00, 0x18, 0xF9, 0x90, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0xCF, 0x4E, 0x05, 0xA6, 0xA0, 0xC6, 0x00, 0x16, 0xF8, 0x60, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } }, + { { 0xCF, 0x4D, 0x0C, 0xAA, 0xA0, 0xC3, 0x00, 0x18, 0xF8, 0x98, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0xCF, 0x4E, 0x06, 0xAA, 0xA0, 0xC5, 0x00, 0x19, 0xF9, 0x90, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } }, + { { 0xCB, 0x3F, 0x8F, 0x00, 0xFA, 0xC5, 0x06, 0x98, 0xD6, 0xBB, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0D }, + { 0xC0, 0x00, 0xF0, 0x00, 0x00, 0xC0, 0x00, 0xF0, 0x00, 0x00, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0D } }, + { { 0x0C, 0x18, 0x87, 0xB3, 0xFB, 0x19, 0x0B, 0x55, 0x75, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x0C, 0x18, 0x87, 0xB3, 0xFB, 0x1B, 0x10, 0x57, 0x75, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } }, + { { 0x05, 0x11, 0x15, 0x00, 0xC8, 0x02, 0x00, 0x08, 0x00, 0xA8, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x02, 0x11, 0x13, 0x00, 0xC8, 0x02, 0x00, 0x05, 0x00, 0x80, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } }, + { { 0x04, 0x08, 0x15, 0x00, 0x90, 0x01, 0x00, 0x08, 0x00, 0xC0, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x03, 0x08, 0x14, 0x00, 0x90, 0x02, 0x00, 0x07, 0x00, 0xA8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } }, + { { 0xDA, 0x00, 0x53, 0x30, 0xC0, 0x07, 0x10, 0x49, 0xC4, 0xDA, 0x03, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xD2, 0x00, 0x56, 0x30, 0x90, 0x06, 0x00, 0x46, 0x56, 0x62, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x1C, 0x00, 0x07, 0xBC, 0xC8, 0x0C, 0x0A, 0x0B, 0x6A, 0xF2, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0x18, 0x00, 0x07, 0xBC, 0x88, 0x09, 0x00, 0x0B, 0x6A, 0xBA, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } }, + { { 0x0A, 0x0E, 0x7F, 0x00, 0xF9, 0x13, 0x16, 0x28, 0x03, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x01, 0x0E, 0x54, 0x00, 0xF9, 0x15, 0x03, 0x27, 0x03, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } } +}; +#endif + +static const byte g_gmPercussionInstrumentMap[128] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, @@ -495,9 +858,9 @@ static const byte gm_percussion_lookup[128] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; -static byte lookup_table[64][32]; +static byte g_volumeLookupTable[64][32]; -static const byte volume_table[] = { +static const byte g_volumeTable[] = { 0, 4, 7, 11, 13, 16, 18, 20, 22, 24, 26, 27, @@ -516,7 +879,7 @@ static const byte volume_table[] = { 62, 63, 63, 63 }; -static int lookup_volume(int a, int b) { +static int lookupVolume(int a, int b) { if (b == 0) return 0; @@ -529,32 +892,32 @@ static int lookup_volume(int a, int b) { if (b < 0) { if (a < 0) { - return lookup_table[-a][-b]; + return g_volumeLookupTable[-a][-b]; } else { - return -lookup_table[a][-b]; + return -g_volumeLookupTable[a][-b]; } } else { if (a < 0) { - return -lookup_table[-a][b]; + return -g_volumeLookupTable[-a][b]; } else { - return lookup_table[a][b]; + return g_volumeLookupTable[a][b]; } } } -static void create_lookup_table() { +static void createLookupTable() { int i, j; int sum; for (i = 0; i < 64; i++) { sum = i; for (j = 0; j < 32; j++) { - lookup_table[i][j] = sum >> 5; + g_volumeLookupTable[i][j] = sum >> 5; sum += i; } } for (i = 0; i < 64; i++) - lookup_table[i][0] = 0; + g_volumeLookupTable[i][0] = 0; } //////////////////////////////////////// @@ -584,58 +947,75 @@ public: // AudioStream API - bool isStereo() const { return false; } + bool isStereo() const { return _opl->isStereo(); } int getRate() const { return _mixer->getOutputRate(); } private: - bool _scummSmallHeader; // FIXME: This flag controls a special mode for SCUMM V3 games + bool _scummSmallHeader; // FIXME: This flag controls a special mode for SCUMM V3 games +#ifdef ENABLE_OPL3 + bool _opl3Mode; +#endif - FM_OPL *_opl; - byte *_adlib_reg_cache; + OPL::OPL *_opl; + byte *_regCache; +#ifdef ENABLE_OPL3 + byte *_regCacheSecondary; +#endif - int _adlib_timer_counter; + int _timerCounter; - uint16 channel_table_2[9]; - int _voice_index; - int _timer_p; - int _timer_q; - uint16 curnote_table[9]; + uint16 _channelTable2[9]; + int _voiceIndex; + int _timerIncrease; + int _timerThreshold; + uint16 _curNotTable[9]; AdLibVoice _voices[9]; AdLibPart _parts[32]; AdLibPercussionChannel _percussion; void generateSamples(int16 *buf, int len); void onTimer(); - void part_key_on(AdLibPart *part, AdLibInstrument *instr, byte note, byte velocity); - void part_key_off(AdLibPart *part, byte note); - - void adlib_key_off(int chan); - void adlib_note_on(int chan, byte note, int mod); - void adlib_note_on_ex(int chan, byte note, int mod); - int adlib_get_reg_value_param(int chan, byte data); - void adlib_setup_channel(int chan, AdLibInstrument *instr, byte vol_1, byte vol_2); - byte adlib_get_reg_value(byte reg) { - return _adlib_reg_cache[reg]; - } - void adlib_set_param(int channel, byte param, int value); - void adlib_key_onoff(int channel); - void adlib_write(byte reg, byte value); - void adlib_playnote(int channel, int note); - - AdLibVoice *allocate_voice(byte pri); - - void mc_off(AdLibVoice *voice); - - static void link_mc(AdLibPart *part, AdLibVoice *voice); - void mc_inc_stuff(AdLibVoice *voice, Struct10 *s10, Struct11 *s11); - void mc_init_stuff(AdLibVoice *voice, Struct10 *s10, Struct11 *s11, byte flags, - InstrumentExtra *ie); - - void struct10_init(Struct10 *s10, InstrumentExtra *ie); - static byte struct10_ontimer(Struct10 *s10, Struct11 *s11); - static void struct10_setup(Struct10 *s10); - static int random_nr(int a); - void mc_key_on(AdLibVoice *voice, AdLibInstrument *instr, byte note, byte velocity); + void partKeyOn(AdLibPart *part, const AdLibInstrument *instr, byte note, byte velocity, const AdLibInstrument *second, byte pan); + void partKeyOff(AdLibPart *part, byte note); + + void adlibKeyOff(int chan); + void adlibNoteOn(int chan, byte note, int mod); + void adlibNoteOnEx(int chan, byte note, int mod); + int adlibGetRegValueParam(int chan, byte data); + void adlibSetupChannel(int chan, const AdLibInstrument *instr, byte vol1, byte vol2); +#ifdef ENABLE_OPL3 + void adlibSetupChannelSecondary(int chan, const AdLibInstrument *instr, byte vol1, byte vol2, byte pan); +#endif + byte adlibGetRegValue(byte reg) { + return _regCache[reg]; + } +#ifdef ENABLE_OPL3 + byte adlibGetRegValueSecondary(byte reg) { + return _regCacheSecondary[reg]; + } +#endif + void adlibSetParam(int channel, byte param, int value, bool primary = true); + void adlibKeyOnOff(int channel); + void adlibWrite(byte reg, byte value); +#ifdef ENABLE_OPL3 + void adlibWriteSecondary(byte reg, byte value); +#endif + void adlibPlayNote(int channel, int note); + + AdLibVoice *allocateVoice(byte pri); + + void mcOff(AdLibVoice *voice); + + static void linkMc(AdLibPart *part, AdLibVoice *voice); + void mcIncStuff(AdLibVoice *voice, Struct10 *s10, Struct11 *s11); + void mcInitStuff(AdLibVoice *voice, Struct10 *s10, Struct11 *s11, byte flags, + const InstrumentExtra *ie); + + void struct10Init(Struct10 *s10, const InstrumentExtra *ie); + static byte struct10OnTimer(Struct10 *s10, Struct11 *s11); + static void struct10Setup(Struct10 *s10); + static int randomNr(int a); + void mcKeyOn(AdLibVoice *voice, const AdLibInstrument *instr, byte note, byte velocity, const AdLibInstrument *second, byte pan); }; // MidiChannel method implementations @@ -643,7 +1023,7 @@ private: void AdLibPart::init(MidiDriver_ADLIB *owner, byte channel) { _owner = owner; _channel = channel; - _pri_eff = 127; + _priEff = 127; programChange(0); } @@ -657,41 +1037,64 @@ void AdLibPart::send(uint32 b) { void AdLibPart::noteOff(byte note) { #ifdef DEBUG_ADLIB - debug(6, "%10d: noteOff(%d)", tick, note); + debug(6, "%10d: noteOff(%d)", g_tick, note); #endif - _owner->part_key_off(this, note); + _owner->partKeyOff(this, note); } void AdLibPart::noteOn(byte note, byte velocity) { #ifdef DEBUG_ADLIB - debug(6, "%10d: noteOn(%d,%d)", tick, note, velocity); + debug(6, "%10d: noteOn(%d,%d)", g_tick, note, velocity); +#endif + _owner->partKeyOn(this, &_partInstr, note, velocity, +#ifdef ENABLE_OPL3 + &_partInstrSecondary, +#else + NULL, #endif - _owner->part_key_on(this, &_part_instr, note, velocity); + _pan); } void AdLibPart::programChange(byte program) { if (program > 127) return; -/* + /* uint i; uint count = 0; - for (i = 0; i < ARRAYSIZE(map_gm_to_fm[0]); ++i) - count += map_gm_to_fm[program][i]; + for (i = 0; i < ARRAYSIZE(g_gmInstruments[0]); ++i) + count += g_gmInstruments[program][i]; if (!count) warning("No AdLib instrument defined for GM program %d", (int)program); -*/ + */ _program = program; - memcpy(&_part_instr, &map_gm_to_fm[program], sizeof(AdLibInstrument)); +#ifdef ENABLE_OPL3 + if (!_owner->_opl3Mode) { +#endif + memcpy(&_partInstr, &g_gmInstruments[program], sizeof(AdLibInstrument)); +#ifdef ENABLE_OPL3 + } else { + memcpy(&_partInstr, &g_gmInstrumentsOPL3[program][0], sizeof(AdLibInstrument)); + memcpy(&_partInstrSecondary, &g_gmInstrumentsOPL3[program][1], sizeof(AdLibInstrument)); + } +#endif } void AdLibPart::pitchBend(int16 bend) { AdLibVoice *voice; - _pitchbend = bend; + _pitchBend = bend; for (voice = _voice; voice; voice = voice->_next) { - _owner->adlib_note_on(voice->_channel, voice->_note + _transpose_eff, - (_pitchbend * _pitchbend_factor >> 6) + _detune_eff); +#ifdef ENABLE_OPL3 + if (!_owner->_opl3Mode) { +#endif + _owner->adlibNoteOn(voice->_channel, voice->_note/* + _transposeEff*/, + (_pitchBend * _pitchBendFactor >> 6) + _detuneEff); +#ifdef ENABLE_OPL3 + } else { + _owner->adlibNoteOn(voice->_channel, voice->_note, _pitchBend >> 1); + } +#endif } } @@ -699,75 +1102,137 @@ void AdLibPart::controlChange(byte control, byte value) { switch (control) { case 0: case 32: - break; // Bank select. Not supported - case 1: modulationWheel(value); break; - case 7: volume(value); break; - case 10: break; // Pan position. Not supported. - case 16: pitchBendFactor(value); break; - case 17: detune(value); break; - case 18: priority(value); break; - case 64: sustain(value > 0); break; - case 91: break; // Effects level. Not supported. - case 93: break; // Chorus level. Not supported. - case 119: break; // Unknown, used in Simon the Sorcerer 2 - case 121: // reset all controllers + // Bank select. Not supported + break; + case 1: + modulationWheel(value); + break; + case 7: + volume(value); + break; + case 10: + panPosition(value); + break; + case 16: + pitchBendFactor(value); + break; + case 17: + detune(value); + break; + case 18: + priority(value); + break; + case 64: + sustain(value > 0); + break; + case 91: + // Effects level. Not supported. + break; + case 93: + // Chorus level. Not supported. + break; + case 119: + // Unknown, used in Simon the Sorcerer 2 + break; + case 121: + // reset all controllers modulationWheel(0); pitchBendFactor(0); detune(0); sustain(0); break; - case 123: allNotesOff(); break; + case 123: + allNotesOff(); + break; default: - warning("AdLib: Unknown control change message %d (%d)", (int) control, (int)value); + warning("AdLib: Unknown control change message %d (%d)", (int)control, (int)value); } } void AdLibPart::modulationWheel(byte value) { AdLibVoice *voice; - _modwheel = value; + _modWheel = value; for (voice = _voice; voice; voice = voice->_next) { if (voice->_s10a.active && voice->_s11a.flag0x40) - voice->_s10a.modwheel = _modwheel >> 2; + voice->_s10a.modWheel = _modWheel >> 2; if (voice->_s10b.active && voice->_s11b.flag0x40) - voice->_s10b.modwheel = _modwheel >> 2; + voice->_s10b.modWheel = _modWheel >> 2; } } void AdLibPart::volume(byte value) { AdLibVoice *voice; - _vol_eff = value; + _volEff = value; for (voice = _voice; voice; voice = voice->_next) { - _owner->adlib_set_param(voice->_channel, 0, volume_table[lookup_table[voice->_vol_2][_vol_eff >> 2]]); - if (voice->_twochan) { - _owner->adlib_set_param(voice->_channel, 13, volume_table[lookup_table[voice->_vol_1][_vol_eff >> 2]]); +#ifdef ENABLE_OPL3 + if (!_owner->_opl3Mode) { +#endif + _owner->adlibSetParam(voice->_channel, 0, g_volumeTable[g_volumeLookupTable[voice->_vol2][_volEff >> 2]]); + if (voice->_twoChan) { + _owner->adlibSetParam(voice->_channel, 13, g_volumeTable[g_volumeLookupTable[voice->_vol1][_volEff >> 2]]); + } +#ifdef ENABLE_OPL3 + } else { + _owner->adlibSetParam(voice->_channel, 0, g_volumeTable[((voice->_vol2 + 1) * _volEff) >> 7], true); + _owner->adlibSetParam(voice->_channel, 0, g_volumeTable[((voice->_secVol2 + 1) * _volEff) >> 7], false); + if (voice->_twoChan) { + _owner->adlibSetParam(voice->_channel, 13, g_volumeTable[((voice->_vol1 + 1) * _volEff) >> 7], true); + } + if (voice->_secTwoChan) { + _owner->adlibSetParam(voice->_channel, 13, g_volumeTable[((voice->_secVol1 + 1) * _volEff) >> 7], false); + } } +#endif } } +void AdLibPart::panPosition(byte value) { + _pan = value; +} + void AdLibPart::pitchBendFactor(byte value) { +#ifdef ENABLE_OPL3 + // Not supported in OPL3 mode. + if (_owner->_opl3Mode) { + return; + } +#endif + AdLibVoice *voice; - _pitchbend_factor = value; + _pitchBendFactor = value; for (voice = _voice; voice; voice = voice->_next) { - _owner->adlib_note_on(voice->_channel, voice->_note + _transpose_eff, - (_pitchbend * _pitchbend_factor >> 6) + _detune_eff); + _owner->adlibNoteOn(voice->_channel, voice->_note/* + _transposeEff*/, + (_pitchBend * _pitchBendFactor >> 6) + _detuneEff); } } void AdLibPart::detune(byte value) { + // Sam&Max's OPL3 driver uses this for a completly different purpose. It + // is related to voice allocation. We ignore this for now. + // TODO: We probably need to look how the interpreter side of Sam&Max's + // iMuse version handles all this too. Implementing the driver side here + // would be not that hard. +#ifdef ENABLE_OPL3 + if (_owner->_opl3Mode) { + //_maxNotes = value; + return; + } +#endif + AdLibVoice *voice; - _detune_eff = value; + _detuneEff = value; for (voice = _voice; voice; voice = voice->_next) { - _owner->adlib_note_on(voice->_channel, voice->_note + _transpose_eff, - (_pitchbend * _pitchbend_factor >> 6) + _detune_eff); + _owner->adlibNoteOn(voice->_channel, voice->_note/* + _transposeEff*/, + (_pitchBend * _pitchBendFactor >> 6) + _detuneEff); } } void AdLibPart::priority(byte value) { - _pri_eff = value; + _priEff = value; } void AdLibPart::sustain(bool value) { @@ -776,20 +1241,29 @@ void AdLibPart::sustain(bool value) { _pedal = value; if (!value) { for (voice = _voice; voice; voice = voice->_next) { - if (voice->_waitforpedal) - _owner->mc_off(voice); + if (voice->_waitForPedal) + _owner->mcOff(voice); } } } void AdLibPart::allNotesOff() { while (_voice) - _owner->mc_off(_voice); + _owner->mcOff(_voice); } void AdLibPart::sysEx_customInstrument(uint32 type, const byte *instr) { + // Sam&Max allows for instrument overwrites, but we will not support it + // until we can find any track actually using it. +#ifdef ENABLE_OPL3 + if (_owner->_opl3Mode) { + warning("AdLibPart::sysEx_customInstrument: Used in OPL3 mode"); + return; + } +#endif + if (type == 'ADL ') { - memcpy(&_part_instr, instr, sizeof(AdLibInstrument)); + memcpy(&_partInstr, instr, sizeof(AdLibInstrument)); } } @@ -803,8 +1277,8 @@ AdLibPercussionChannel::~AdLibPercussionChannel() { void AdLibPercussionChannel::init(MidiDriver_ADLIB *owner, byte channel) { AdLibPart::init(owner, channel); - _pri_eff = 0; - _vol_eff = 127; + _priEff = 0; + _volEff = 127; // Initialize the custom instruments data memset(_notes, 0, sizeof(_notes)); @@ -812,33 +1286,49 @@ void AdLibPercussionChannel::init(MidiDriver_ADLIB *owner, byte channel) { } void AdLibPercussionChannel::noteOff(byte note) { - // Jamieson630: Unless I run into a specific instrument that - // may require a key off, I'm going to ignore this message. - // The rationale is that a percussion instrument should - // fade out of its own accord, and the AdLib instrument - // definitions used should follow this rule. Since - // percussion voices are allocated at the lowest priority - // anyway, we know that "hanging" percussion sounds will - // not prevent later musical instruments (or even other - // percussion sounds) from playing. -/* - _owner->part_key_off(this, note); -*/ + if (_customInstruments[note]) { + note = _notes[note]; + } + + // This used to ignore note off events, since the builtin percussion + // instrument data has a duration value, which causes the percussion notes + // to stop automatically. This is not the case for (Groovie's) custom + // percussion instruments though. Also the OPL3 driver of Sam&Max actually + // does not handle the duration value, so we need it there too. + _owner->partKeyOff(this, note); } void AdLibPercussionChannel::noteOn(byte note, byte velocity) { - AdLibInstrument *inst = NULL; + const AdLibInstrument *inst = NULL; + const AdLibInstrument *sec = NULL; // The custom instruments have priority over the default mapping - inst = _customInstruments[note]; - if (inst) - note = _notes[note]; + // We do not support custom instruments in OPL3 mode though. +#ifdef ENABLE_OPL3 + if (!_owner->_opl3Mode) { +#endif + inst = _customInstruments[note]; + if (inst) + note = _notes[note]; +#ifdef ENABLE_OPL3 + } +#endif if (!inst) { - // Use the default GM to FM mapping as a fallback as a fallback - byte key = gm_percussion_lookup[note]; - if (key != 0xFF) - inst = &gm_percussion_to_fm[key]; + // Use the default GM to FM mapping as a fallback + byte key = g_gmPercussionInstrumentMap[note]; + if (key != 0xFF) { +#ifdef ENABLE_OPL3 + if (!_owner->_opl3Mode) { +#endif + inst = &g_gmPercussionInstruments[key]; +#ifdef ENABLE_OPL3 + } else { + inst = &g_gmPercussionInstrumentsOPL3[key][0]; + sec = &g_gmPercussionInstrumentsOPL3[key][1]; + } +#endif + } } if (!inst) { @@ -846,10 +1336,18 @@ void AdLibPercussionChannel::noteOn(byte note, byte velocity) { return; } - _owner->part_key_on(this, inst, note, velocity); + _owner->partKeyOn(this, inst, note, velocity, sec, _pan); } void AdLibPercussionChannel::sysEx_customInstrument(uint32 type, const byte *instr) { + // We do not allow custom instruments in OPL3 mode right now. +#ifdef ENABLE_OPL3 + if (_owner->_opl3Mode) { + warning("AdLibPercussionChannel::sysEx_customInstrument: Used in OPL3 mode"); + return; + } +#endif + if (type == 'ADLP') { byte note = instr[0]; _notes[note] = instr[1]; @@ -861,16 +1359,16 @@ void AdLibPercussionChannel::sysEx_customInstrument(uint32 type, const byte *ins } // Save the new instrument data - _customInstruments[note]->mod_characteristic = instr[2]; - _customInstruments[note]->mod_scalingOutputLevel = instr[3]; - _customInstruments[note]->mod_attackDecay = instr[4]; - _customInstruments[note]->mod_sustainRelease = instr[5]; - _customInstruments[note]->mod_waveformSelect = instr[6]; - _customInstruments[note]->car_characteristic = instr[7]; - _customInstruments[note]->car_scalingOutputLevel = instr[8]; - _customInstruments[note]->car_attackDecay = instr[9]; - _customInstruments[note]->car_sustainRelease = instr[10]; - _customInstruments[note]->car_waveformSelect = instr[11]; + _customInstruments[note]->modCharacteristic = instr[2]; + _customInstruments[note]->modScalingOutputLevel = instr[3]; + _customInstruments[note]->modAttackDecay = instr[4]; + _customInstruments[note]->modSustainRelease = instr[5]; + _customInstruments[note]->modWaveformSelect = instr[6]; + _customInstruments[note]->carCharacteristic = instr[7]; + _customInstruments[note]->carScalingOutputLevel = instr[8]; + _customInstruments[note]->carAttackDecay = instr[9]; + _customInstruments[note]->carSustainRelease = instr[10]; + _customInstruments[note]->carWaveformSelect = instr[11]; _customInstruments[note]->feedback = instr[12]; } } @@ -882,21 +1380,28 @@ MidiDriver_ADLIB::MidiDriver_ADLIB(Audio::Mixer *mixer) uint i; _scummSmallHeader = false; +#ifdef ENABLE_OPL3 + _opl3Mode = false; +#endif - _adlib_reg_cache = 0; + _regCache = 0; +#ifdef ENABLE_OPL3 + _regCacheSecondary = 0; +#endif - _adlib_timer_counter = 0; - _voice_index = 0; - for (i = 0; i < ARRAYSIZE(curnote_table); ++i) { - curnote_table[i] = 0; + _timerCounter = 0; + _voiceIndex = -1; + for (i = 0; i < ARRAYSIZE(_curNotTable); ++i) { + _curNotTable[i] = 0; } for (i = 0; i < ARRAYSIZE(_parts); ++i) { _parts[i].init(this, i + ((i >= 9) ? 1 : 0)); } _percussion.init(this, 9); - _timer_p = 0xD69; - _timer_q = 0x411B; + _timerIncrease = 0xD69; + _timerThreshold = 0x411B; + _opl = 0; } int MidiDriver_ADLIB::open() { @@ -914,14 +1419,37 @@ int MidiDriver_ADLIB::open() { voice->_s11b.s10 = &voice->_s10a; } - _adlib_reg_cache = (byte *)calloc(256, 1); + // Try to use OPL3 when requested. +#ifdef ENABLE_OPL3 + if (_opl3Mode) { + _opl = OPL::Config::create(OPL::Config::kOpl3); + } - _opl = makeAdLibOPL(getRate()); + // Initialize plain OPL2 when no OPL3 is intiailized already. + if (!_opl) { +#endif + _opl = OPL::Config::create(); +#ifdef ENABLE_OPL3 + _opl3Mode = false; + } +#endif + _opl->init(getRate()); - adlib_write(1, 0x20); - adlib_write(8, 0x40); - adlib_write(0xBD, 0x00); - create_lookup_table(); + _regCache = (byte *)calloc(256, 1); + + adlibWrite(8, 0x40); + adlibWrite(0xBD, 0x00); +#ifdef ENABLE_OPL3 + if (!_opl3Mode) { +#endif + adlibWrite(1, 0x20); + createLookupTable(); +#ifdef ENABLE_OPL3 + } else { + _regCacheSecondary = (byte *)calloc(256, 1); + adlibWriteSecondary(5, 1); + } +#endif _mixer->playStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); @@ -938,14 +1466,17 @@ void MidiDriver_ADLIB::close() { uint i; for (i = 0; i < ARRAYSIZE(_voices); ++i) { if (_voices[i]._part) - mc_off(&_voices[i]); + mcOff(&_voices[i]); } // Turn off the OPL emulation - OPLDestroy(_opl); -// YM3812Shutdown(); + delete _opl; + _opl = 0; - free(_adlib_reg_cache); + free(_regCache); +#ifdef ENABLE_OPL3 + free(_regCacheSecondary); +#endif } void MidiDriver_ADLIB::send(uint32 b) { @@ -954,9 +1485,9 @@ void MidiDriver_ADLIB::send(uint32 b) { void MidiDriver_ADLIB::send(byte chan, uint32 b) { //byte param3 = (byte) ((b >> 24) & 0xFF); - byte param2 = (byte) ((b >> 16) & 0xFF); - byte param1 = (byte) ((b >> 8) & 0xFF); - byte cmd = (byte) (b & 0xF0); + byte param2 = (byte)((b >> 16) & 0xFF); + byte param1 = (byte)((b >> 8) & 0xFF); + byte cmd = (byte)(b & 0xF0); AdLibPart *part; if (chan == 9) @@ -997,29 +1528,42 @@ void MidiDriver_ADLIB::send(byte chan, uint32 b) { uint32 MidiDriver_ADLIB::property(int prop, uint32 param) { switch (prop) { - case PROP_OLD_ADLIB: // Older games used a different operator volume algorithm - _scummSmallHeader = (param > 0); - if (_scummSmallHeader) { - _timer_p = 473; - _timer_q = 1000; - } else { - _timer_p = 0xD69; - _timer_q = 0x411B; - } - return 1; + case PROP_OLD_ADLIB: // Older games used a different operator volume algorithm + _scummSmallHeader = (param > 0); + if (_scummSmallHeader) { + _timerIncrease = 473; + _timerThreshold = 1000; + } else { + _timerIncrease = 0xD69; + _timerThreshold = 0x411B; + } + return 1; + + case PROP_SCUMM_OPL3: // Sam&Max OPL3 support. +#ifdef ENABLE_OPL3 + _opl3Mode = (param > 0); +#endif + return 1; } return 0; } void MidiDriver_ADLIB::setPitchBendRange(byte channel, uint range) { +#ifdef ENABLE_OPL3 + // Not supported in OPL3 mode. + if (_opl3Mode) { + return; + } +#endif + AdLibVoice *voice; AdLibPart *part = &_parts[channel]; - part->_pitchbend_factor = range; + part->_pitchBendFactor = range; for (voice = part->_voice; voice; voice = voice->_next) { - adlib_note_on(voice->_channel, voice->_note + part->_transpose_eff, - (part->_pitchbend * part->_pitchbend_factor >> 6) + part->_detune_eff); + adlibNoteOn(voice->_channel, voice->_note/* + part->_transposeEff*/, + (part->_pitchBend * part->_pitchBendFactor >> 6) + part->_detuneEff); } } @@ -1043,54 +1587,77 @@ MidiChannel *MidiDriver_ADLIB::allocateChannel() { // All the code brought over from IMuseAdLib -void MidiDriver_ADLIB::adlib_write(byte reg, byte value) { - if (_adlib_reg_cache[reg] == value) +void MidiDriver_ADLIB::adlibWrite(byte reg, byte value) { + if (_regCache[reg] == value) { return; + } #ifdef DEBUG_ADLIB - debug(6, "%10d: adlib_write[%x] = %x", tick, reg, value); + debug(6, "%10d: adlibWrite[%x] = %x", g_tick, reg, value); #endif - _adlib_reg_cache[reg] = value; + _regCache[reg] = value; - OPLWriteReg(_opl, reg, value); + _opl->writeReg(reg, value); } +#ifdef ENABLE_OPL3 +void MidiDriver_ADLIB::adlibWriteSecondary(byte reg, byte value) { + assert(_opl3Mode); + + if (_regCacheSecondary[reg] == value) { + return; + } +#ifdef DEBUG_ADLIB + debug(6, "%10d: adlibWriteSecondary[%x] = %x", g_tick, reg, value); +#endif + _regCacheSecondary[reg] = value; + + _opl->writeReg(reg | 0x100, value); +} +#endif + void MidiDriver_ADLIB::generateSamples(int16 *data, int len) { - memset(data, 0, sizeof(int16) * len); - YM3812UpdateOne(_opl, data, len); + if (_opl->isStereo()) { + len *= 2; + } + _opl->readBuffer(data, len); } void MidiDriver_ADLIB::onTimer() { - AdLibVoice *voice; - int i; - - _adlib_timer_counter += _timer_p; - while (_adlib_timer_counter >= _timer_q) { - _adlib_timer_counter -= _timer_q; + _timerCounter += _timerIncrease; + while (_timerCounter >= _timerThreshold) { + _timerCounter -= _timerThreshold; #ifdef DEBUG_ADLIB - tick++; + g_tick++; #endif - voice = _voices; - for (i = 0; i != ARRAYSIZE(_voices); i++, voice++) { - if (!voice->_part) - continue; - if (voice->_duration && (voice->_duration -= 0x11) <= 0) { - mc_off(voice); - return; - } - if (voice->_s10a.active) { - mc_inc_stuff(voice, &voice->_s10a, &voice->_s11a); - } - if (voice->_s10b.active) { - mc_inc_stuff(voice, &voice->_s10b, &voice->_s11b); + // Sam&Max's OPL3 driver does not have any timer handling like this. +#ifdef ENABLE_OPL3 + if (!_opl3Mode) { +#endif + AdLibVoice *voice = _voices; + for (int i = 0; i != ARRAYSIZE(_voices); i++, voice++) { + if (!voice->_part) + continue; + if (voice->_duration && (voice->_duration -= 0x11) <= 0) { + mcOff(voice); + return; + } + if (voice->_s10a.active) { + mcIncStuff(voice, &voice->_s10a, &voice->_s11a); + } + if (voice->_s10b.active) { + mcIncStuff(voice, &voice->_s10b, &voice->_s11b); + } } +#ifdef ENABLE_OPL3 } +#endif } } -void MidiDriver_ADLIB::mc_off(AdLibVoice *voice) { +void MidiDriver_ADLIB::mcOff(AdLibVoice *voice) { AdLibVoice *tmp; - adlib_key_off(voice->_channel); + adlibKeyOff(voice->_channel); tmp = voice->_prev; @@ -1103,57 +1670,62 @@ void MidiDriver_ADLIB::mc_off(AdLibVoice *voice) { voice->_part = NULL; } -void MidiDriver_ADLIB::mc_inc_stuff(AdLibVoice *voice, Struct10 *s10, Struct11 *s11) { +void MidiDriver_ADLIB::mcIncStuff(AdLibVoice *voice, Struct10 *s10, Struct11 *s11) { byte code; AdLibPart *part = voice->_part; - code = struct10_ontimer(s10, s11); + code = struct10OnTimer(s10, s11); if (code & 1) { switch (s11->param) { case 0: - voice->_vol_2 = s10->start_value + s11->modify_val; + voice->_vol2 = s10->startValue + s11->modifyVal; if (!_scummSmallHeader) { - adlib_set_param(voice->_channel, 0, - volume_table[lookup_table[voice->_vol_2] - [part->_vol_eff >> 2]]); + adlibSetParam(voice->_channel, 0, + g_volumeTable[g_volumeLookupTable[voice->_vol2] + [part->_volEff >> 2]]); } else { - adlib_set_param(voice->_channel, 0, voice->_vol_2); + adlibSetParam(voice->_channel, 0, voice->_vol2); } break; case 13: - voice->_vol_1 = s10->start_value + s11->modify_val; - if (voice->_twochan && !_scummSmallHeader) { - adlib_set_param(voice->_channel, 13, - volume_table[lookup_table[voice->_vol_1] - [part->_vol_eff >> 2]]); + voice->_vol1 = s10->startValue + s11->modifyVal; + if (voice->_twoChan && !_scummSmallHeader) { + adlibSetParam(voice->_channel, 13, + g_volumeTable[g_volumeLookupTable[voice->_vol1] + [part->_volEff >> 2]]); } else { - adlib_set_param(voice->_channel, 13, voice->_vol_1); + adlibSetParam(voice->_channel, 13, voice->_vol1); } break; case 30: - s11->s10->modwheel = (char)s11->modify_val; + s11->s10->modWheel = (char)s11->modifyVal; break; case 31: - s11->s10->unk3 = (char)s11->modify_val; + s11->s10->unk3 = (char)s11->modifyVal; break; default: - adlib_set_param(voice->_channel, s11->param, - s10->start_value + s11->modify_val); + adlibSetParam(voice->_channel, s11->param, + s10->startValue + s11->modifyVal); break; } } if (code & 2 && s11->flag0x10) - adlib_key_onoff(voice->_channel); + adlibKeyOnOff(voice->_channel); } -void MidiDriver_ADLIB::adlib_key_off(int chan){ +void MidiDriver_ADLIB::adlibKeyOff(int chan) { byte reg = chan + 0xB0; - adlib_write(reg, adlib_get_reg_value(reg) & ~0x20); + adlibWrite(reg, adlibGetRegValue(reg) & ~0x20); +#ifdef ENABLE_OPL3 + if (_opl3Mode) { + adlibWriteSecondary(reg, adlibGetRegValueSecondary(reg) & ~0x20); + } +#endif } -byte MidiDriver_ADLIB::struct10_ontimer(Struct10 *s10, Struct11 *s11) { +byte MidiDriver_ADLIB::struct10OnTimer(Struct10 *s10, Struct11 *s11) { byte result = 0; int i; @@ -1162,51 +1734,54 @@ byte MidiDriver_ADLIB::struct10_ontimer(Struct10 *s10, Struct11 *s11) { return 0; } - i = s10->cur_val + s10->speed_hi; - s10->speed_lo_counter += s10->speed_lo; - if (s10->speed_lo_counter >= s10->speed_lo_max) { - s10->speed_lo_counter -= s10->speed_lo_max; + i = s10->curVal + s10->speedHi; + s10->speedLoCounter += s10->speedLo; + if (s10->speedLoCounter >= s10->speedLoMax) { + s10->speedLoCounter -= s10->speedLoMax; i += s10->direction; } - if (s10->cur_val != i || s10->modwheel != s10->modwheel_last) { - s10->cur_val = i; - s10->modwheel_last = s10->modwheel; - i = lookup_volume(i, s10->modwheel_last); - if (i != s11->modify_val) { - s11->modify_val = i; + if (s10->curVal != i || s10->modWheel != s10->modWheelLast) { + s10->curVal = i; + s10->modWheelLast = s10->modWheel; + i = lookupVolume(i, s10->modWheelLast); + if (i != s11->modifyVal) { + s11->modifyVal = i; result = 1; } } - if (!--s10->num_steps) { + if (!--s10->numSteps) { s10->active++; if (s10->active > 4) { if (s10->loop) { s10->active = 1; result |= 2; - struct10_setup(s10); + struct10Setup(s10); } else { s10->active = 0; } } else { - struct10_setup(s10); + struct10Setup(s10); } } return result; } -void MidiDriver_ADLIB::adlib_set_param(int channel, byte param, int value) { +void MidiDriver_ADLIB::adlibSetParam(int channel, byte param, int value, bool primary) { const AdLibSetParams *as; byte reg; assert(channel >= 0 && channel < 9); +#ifdef ENABLE_OPL3 + assert(!_opl3Mode || (param == 0 || param == 13)); +#endif if (param <= 12) { - reg = channel_mappings_2[channel]; + reg = g_operator2Offsets[channel]; } else if (param <= 25) { param -= 13; - reg = channel_mappings[channel]; + reg = g_operator1Offsets[channel]; } else if (param <= 27) { param -= 13; reg = channel; @@ -1216,54 +1791,66 @@ void MidiDriver_ADLIB::adlib_set_param(int channel, byte param, int value) { else value -= 383; value <<= 4; - channel_table_2[channel] = value; - adlib_playnote(channel, curnote_table[channel] + value); + _channelTable2[channel] = value; + adlibPlayNote(channel, _curNotTable[channel] + value); return; } else { return; } - as = &adlib_setparam_table[param]; - if (as->d) - value = as->d - value; - reg += as->a; - adlib_write(reg, (adlib_get_reg_value(reg) & ~as->c) | (((byte)value) << as->b)); + as = &g_setParamTable[param]; + if (as->inversion) + value = as->inversion - value; + reg += as->registerBase; +#ifdef ENABLE_OPL3 + if (primary) { +#endif + adlibWrite(reg, (adlibGetRegValue(reg) & ~as->mask) | (((byte)value) << as->shift)); +#ifdef ENABLE_OPL3 + } else { + adlibWriteSecondary(reg, (adlibGetRegValueSecondary(reg) & ~as->mask) | (((byte)value) << as->shift)); + } +#endif } -void MidiDriver_ADLIB::adlib_key_onoff(int channel) { +void MidiDriver_ADLIB::adlibKeyOnOff(int channel) { +#ifdef ENABLE_OPL3 + assert(!_opl3Mode); +#endif + byte val; byte reg = channel + 0xB0; assert(channel >= 0 && channel < 9); - val = adlib_get_reg_value(reg); - adlib_write(reg, val & ~0x20); - adlib_write(reg, val | 0x20); + val = adlibGetRegValue(reg); + adlibWrite(reg, val & ~0x20); + adlibWrite(reg, val | 0x20); } -void MidiDriver_ADLIB::struct10_setup(Struct10 *s10) { +void MidiDriver_ADLIB::struct10Setup(Struct10 *s10) { int b, c, d, e, f, g, h; byte t; b = s10->unk3; f = s10->active - 1; - t = s10->table_a[f]; - e = num_steps_table[lookup_table[t & 0x7F][b]]; + t = s10->tableA[f]; + e = g_numStepsTable[g_volumeLookupTable[t & 0x7F][b]]; if (t & 0x80) { - e = random_nr(e); + e = randomNr(e); } if (e == 0) e++; - s10->num_steps = s10->speed_lo_max = e; + s10->numSteps = s10->speedLoMax = e; if (f != 2) { - c = s10->max_value; - g = s10->start_value; - t = s10->table_b[f]; - d = lookup_volume(c, (t & 0x7F) - 31); + c = s10->maxValue; + g = s10->startValue; + t = s10->tableB[f]; + d = lookupVolume(c, (t & 0x7F) - 31); if (t & 0x80) { - d = random_nr(d); + d = randomNr(d); } if (d + g > c) { h = c - g; @@ -1272,12 +1859,12 @@ void MidiDriver_ADLIB::struct10_setup(Struct10 *s10) { if (d + g < 0) h = -g; } - h -= s10->cur_val; + h -= s10->curVal; } else { h = 0; } - s10->speed_hi = h / e; + s10->speedHi = h / e; if (h < 0) { h = -h; s10->direction = -1; @@ -1285,11 +1872,11 @@ void MidiDriver_ADLIB::struct10_setup(Struct10 *s10) { s10->direction = 1; } - s10->speed_lo = h % e; - s10->speed_lo_counter = 0; + s10->speedLo = h % e; + s10->speedLoCounter = 0; } -void MidiDriver_ADLIB::adlib_playnote(int channel, int note) { +void MidiDriver_ADLIB::adlibPlayNote(int channel, int note) { byte old, oct, notex; int note2; int i; @@ -1304,7 +1891,7 @@ void MidiDriver_ADLIB::adlib_playnote(int channel, int note) { oct <<= 2; notex = note2 % 12 + 3; - old = adlib_get_reg_value(channel + 0xB0); + old = adlibGetRegValue(channel + 0xB0); if (old & 0x20) { old &= ~0x20; if (oct > old) { @@ -1321,58 +1908,58 @@ void MidiDriver_ADLIB::adlib_playnote(int channel, int note) { } i = (notex << 3) + ((note >> 4) & 0x7); - adlib_write(channel + 0xA0, note_to_f_num[i]); - adlib_write(channel + 0xB0, oct | 0x20); + adlibWrite(channel + 0xA0, g_noteFrequencies[i]); + adlibWrite(channel + 0xB0, oct | 0x20); } -int MidiDriver_ADLIB::random_nr(int a) { - static byte _rand_seed = 1; - if (_rand_seed & 1) { - _rand_seed >>= 1; - _rand_seed ^= 0xB8; +int MidiDriver_ADLIB::randomNr(int a) { + static byte _randSeed = 1; + if (_randSeed & 1) { + _randSeed >>= 1; + _randSeed ^= 0xB8; } else { - _rand_seed >>= 1; + _randSeed >>= 1; } - return _rand_seed * a >> 8; + return _randSeed * a >> 8; } -void MidiDriver_ADLIB::part_key_off(AdLibPart *part, byte note) { +void MidiDriver_ADLIB::partKeyOff(AdLibPart *part, byte note) { AdLibVoice *voice; for (voice = part->_voice; voice; voice = voice->_next) { if (voice->_note == note) { if (part->_pedal) - voice->_waitforpedal = true; + voice->_waitForPedal = true; else - mc_off(voice); + mcOff(voice); } } } -void MidiDriver_ADLIB::part_key_on(AdLibPart *part, AdLibInstrument *instr, byte note, byte velocity) { +void MidiDriver_ADLIB::partKeyOn(AdLibPart *part, const AdLibInstrument *instr, byte note, byte velocity, const AdLibInstrument *second, byte pan) { AdLibVoice *voice; - voice = allocate_voice(part->_pri_eff); + voice = allocateVoice(part->_priEff); if (!voice) return; - link_mc(part, voice); - mc_key_on(voice, instr, note, velocity); + linkMc(part, voice); + mcKeyOn(voice, instr, note, velocity, second, pan); } -AdLibVoice *MidiDriver_ADLIB::allocate_voice(byte pri) { +AdLibVoice *MidiDriver_ADLIB::allocateVoice(byte pri) { AdLibVoice *ac, *best = NULL; int i; for (i = 0; i < 9; i++) { - if (++_voice_index >= 9) - _voice_index = 0; - ac = &_voices[_voice_index]; + if (++_voiceIndex >= 9) + _voiceIndex = 0; + ac = &_voices[_voiceIndex]; if (!ac->_part) return ac; if (!ac->_next) { - if (ac->_part->_pri_eff <= pri) { - pri = ac->_part->_pri_eff; + if (ac->_part->_priEff <= pri) { + pri = ac->_part->_priEff; best = ac; } } @@ -1383,11 +1970,11 @@ AdLibVoice *MidiDriver_ADLIB::allocate_voice(byte pri) { return NULL; if (best) - mc_off(best); + mcOff(best); return best; } -void MidiDriver_ADLIB::link_mc(AdLibPart *part, AdLibVoice *voice) { +void MidiDriver_ADLIB::linkMc(AdLibPart *part, AdLibVoice *voice) { voice->_part = part; voice->_next = (AdLibVoice *)part->_voice; part->_voice = voice; @@ -1397,153 +1984,229 @@ void MidiDriver_ADLIB::link_mc(AdLibPart *part, AdLibVoice *voice) { voice->_next->_prev = voice; } -void MidiDriver_ADLIB::mc_key_on(AdLibVoice *voice, AdLibInstrument *instr, byte note, byte velocity) { +void MidiDriver_ADLIB::mcKeyOn(AdLibVoice *voice, const AdLibInstrument *instr, byte note, byte velocity, const AdLibInstrument *second, byte pan) { AdLibPart *part = voice->_part; - int c; - byte vol_1, vol_2; + byte vol1, vol2; +#ifdef ENABLE_OPL3 + byte secVol1 = 0, secVol2 = 0; +#endif - voice->_twochan = instr->feedback & 1; + voice->_twoChan = instr->feedback & 1; voice->_note = note; - voice->_waitforpedal = false; + voice->_waitForPedal = false; voice->_duration = instr->duration; if (voice->_duration != 0) voice->_duration *= 63; - if (!_scummSmallHeader) - vol_1 = (instr->mod_scalingOutputLevel & 0x3F) + lookup_table[velocity >> 1][instr->mod_waveformSelect >> 2]; - else - vol_1 = 0x3f - (instr->mod_scalingOutputLevel & 0x3F); - if (vol_1 > 0x3F) - vol_1 = 0x3F; - voice->_vol_1 = vol_1; - - if (!_scummSmallHeader) - vol_2 = (instr->car_scalingOutputLevel & 0x3F) + lookup_table[velocity >> 1][instr->car_waveformSelect >> 2]; - else - vol_2 = 0x3f - (instr->car_scalingOutputLevel & 0x3F); - if (vol_2 > 0x3F) - vol_2 = 0x3F; - voice->_vol_2 = vol_2; + if (!_scummSmallHeader) { +#ifdef ENABLE_OPL3 + if (_opl3Mode) + vol1 = (instr->modScalingOutputLevel & 0x3F) + (velocity * ((instr->modWaveformSelect >> 3) + 1)) / 64; + else +#endif + vol1 = (instr->modScalingOutputLevel & 0x3F) + g_volumeLookupTable[velocity >> 1][instr->modWaveformSelect >> 2]; + } else { + vol1 = 0x3f - (instr->modScalingOutputLevel & 0x3F); + } + if (vol1 > 0x3F) + vol1 = 0x3F; + voice->_vol1 = vol1; - c = part->_vol_eff >> 2; + if (!_scummSmallHeader) { +#ifdef ENABLE_OPL3 + if (_opl3Mode) + vol2 = (instr->carScalingOutputLevel & 0x3F) + (velocity * ((instr->carWaveformSelect >> 3) + 1)) / 64; + else +#endif + vol2 = (instr->carScalingOutputLevel & 0x3F) + g_volumeLookupTable[velocity >> 1][instr->carWaveformSelect >> 2]; + } else { + vol2 = 0x3f - (instr->carScalingOutputLevel & 0x3F); + } + if (vol2 > 0x3F) + vol2 = 0x3F; + voice->_vol2 = vol2; + +#ifdef ENABLE_OPL3 + if (_opl3Mode) { + voice->_secTwoChan = second->feedback & 1; + secVol1 = (second->modScalingOutputLevel & 0x3F) + (velocity * ((second->modWaveformSelect >> 3) + 1)) / 64; + if (secVol1 > 0x3F) { + secVol1 = 0x3F; + } + voice->_secVol1 = secVol1; + secVol2 = (second->carScalingOutputLevel & 0x3F) + (velocity * ((second->carWaveformSelect >> 3) + 1)) / 64; + if (secVol2 > 0x3F) { + secVol2 = 0x3F; + } + voice->_secVol2 = secVol2; + } +#endif if (!_scummSmallHeader) { - vol_2 = volume_table[lookup_table[vol_2][c]]; - if (voice->_twochan) - vol_1 = volume_table[lookup_table[vol_1][c]]; +#ifdef ENABLE_OPL3 + if (!_opl3Mode) { +#endif + int c = part->_volEff >> 2; + vol2 = g_volumeTable[g_volumeLookupTable[vol2][c]]; + if (voice->_twoChan) + vol1 = g_volumeTable[g_volumeLookupTable[vol1][c]]; +#ifdef ENABLE_OPL3 + } else { + vol2 = g_volumeTable[((vol2 + 1) * part->_volEff) >> 7]; + secVol2 = g_volumeTable[((secVol2 + 1) * part->_volEff) >> 7]; + if (voice->_twoChan) + vol1 = g_volumeTable[((vol1 + 1) * part->_volEff) >> 7]; + if (voice->_secTwoChan) + secVol1 = g_volumeTable[((secVol1 + 1) * part->_volEff) >> 7]; + } +#endif } - adlib_setup_channel(voice->_channel, instr, vol_1, vol_2); - adlib_note_on_ex(voice->_channel, part->_transpose_eff + note, part->_detune_eff + (part->_pitchbend * part->_pitchbend_factor >> 6)); + adlibSetupChannel(voice->_channel, instr, vol1, vol2); +#ifdef ENABLE_OPL3 + if (!_opl3Mode) { +#endif + adlibNoteOnEx(voice->_channel, /*part->_transposeEff + */note, part->_detuneEff + (part->_pitchBend * part->_pitchBendFactor >> 6)); - if (instr->flags_a & 0x80) { - mc_init_stuff(voice, &voice->_s10a, &voice->_s11a, instr->flags_a, &instr->extra_a); - } else { - voice->_s10a.active = 0; - } + if (instr->flagsA & 0x80) { + mcInitStuff(voice, &voice->_s10a, &voice->_s11a, instr->flagsA, &instr->extraA); + } else { + voice->_s10a.active = 0; + } - if (instr->flags_b & 0x80) { - mc_init_stuff(voice, &voice->_s10b, &voice->_s11b, instr->flags_b, &instr->extra_b); + if (instr->flagsB & 0x80) { + mcInitStuff(voice, &voice->_s10b, &voice->_s11b, instr->flagsB, &instr->extraB); + } else { + voice->_s10b.active = 0; + } +#ifdef ENABLE_OPL3 } else { - voice->_s10b.active = 0; + adlibSetupChannelSecondary(voice->_channel, second, secVol1, secVol2, pan); + adlibNoteOnEx(voice->_channel, note, part->_pitchBend >> 1); } +#endif } -void MidiDriver_ADLIB::adlib_setup_channel(int chan, AdLibInstrument *instr, byte vol_1, byte vol_2) { - byte channel; - +void MidiDriver_ADLIB::adlibSetupChannel(int chan, const AdLibInstrument *instr, byte vol1, byte vol2) { assert(chan >= 0 && chan < 9); - channel = channel_mappings[chan]; - adlib_write(channel + 0x20, instr->mod_characteristic); - adlib_write(channel + 0x40, (instr->mod_scalingOutputLevel | 0x3F) - vol_1 ); - adlib_write(channel + 0x60, 0xff & (~instr->mod_attackDecay)); - adlib_write(channel + 0x80, 0xff & (~instr->mod_sustainRelease)); - adlib_write(channel + 0xE0, instr->mod_waveformSelect); - - channel = channel_mappings_2[chan]; - adlib_write(channel + 0x20, instr->car_characteristic); - adlib_write(channel + 0x40, (instr->car_scalingOutputLevel | 0x3F) - vol_2 ); - adlib_write(channel + 0x60, 0xff & (~instr->car_attackDecay)); - adlib_write(channel + 0x80, 0xff & (~instr->car_sustainRelease)); - adlib_write(channel + 0xE0, instr->car_waveformSelect); - - adlib_write((byte)chan + 0xC0, instr->feedback); + byte channel = g_operator1Offsets[chan]; + adlibWrite(channel + 0x20, instr->modCharacteristic); + adlibWrite(channel + 0x40, (instr->modScalingOutputLevel | 0x3F) - vol1); + adlibWrite(channel + 0x60, 0xff & (~instr->modAttackDecay)); + adlibWrite(channel + 0x80, 0xff & (~instr->modSustainRelease)); + adlibWrite(channel + 0xE0, instr->modWaveformSelect); + + channel = g_operator2Offsets[chan]; + adlibWrite(channel + 0x20, instr->carCharacteristic); + adlibWrite(channel + 0x40, (instr->carScalingOutputLevel | 0x3F) - vol2); + adlibWrite(channel + 0x60, 0xff & (~instr->carAttackDecay)); + adlibWrite(channel + 0x80, 0xff & (~instr->carSustainRelease)); + adlibWrite(channel + 0xE0, instr->carWaveformSelect); + + adlibWrite((byte)chan + 0xC0, instr->feedback +#ifdef ENABLE_OPL3 + | (_opl3Mode ? 0x30 : 0) +#endif + ); } -void MidiDriver_ADLIB::adlib_note_on_ex(int chan, byte note, int mod) { - int code; +#ifdef ENABLE_OPL3 +void MidiDriver_ADLIB::adlibSetupChannelSecondary(int chan, const AdLibInstrument *instr, byte vol1, byte vol2, byte pan) { assert(chan >= 0 && chan < 9); - code = (note << 7) + mod; - curnote_table[chan] = code; - channel_table_2[chan] = 0; - adlib_playnote(chan, code); + assert(_opl3Mode); + + byte channel = g_operator1Offsets[chan]; + adlibWriteSecondary(channel + 0x20, instr->modCharacteristic); + adlibWriteSecondary(channel + 0x40, (instr->modScalingOutputLevel | 0x3F) - vol1); + adlibWriteSecondary(channel + 0x60, 0xff & (~instr->modAttackDecay)); + adlibWriteSecondary(channel + 0x80, 0xff & (~instr->modSustainRelease)); + adlibWriteSecondary(channel + 0xE0, instr->modWaveformSelect); + + channel = g_operator2Offsets[chan]; + adlibWriteSecondary(channel + 0x20, instr->carCharacteristic); + adlibWriteSecondary(channel + 0x40, (instr->carScalingOutputLevel | 0x3F) - vol2); + adlibWriteSecondary(channel + 0x60, 0xff & (~instr->carAttackDecay)); + adlibWriteSecondary(channel + 0x80, 0xff & (~instr->carSustainRelease)); + adlibWriteSecondary(channel + 0xE0, instr->carWaveformSelect); + + // The original uses the following (strange) behavior: +#if 0 + if (instr->feedback | (pan > 64)) { + adlibWriteSecondary((byte)chan + 0xC0, 0x20); + } else { + adlibWriteSecondary((byte)chan + 0xC0, 0x10); + } +#else + adlibWriteSecondary((byte)chan + 0xC0, instr->feedback | ((pan > 64) ? 0x20 : 0x10)); +#endif } +#endif -void MidiDriver_ADLIB::mc_init_stuff(AdLibVoice *voice, Struct10 *s10, - Struct11 *s11, byte flags, InstrumentExtra *ie) { +void MidiDriver_ADLIB::mcInitStuff(AdLibVoice *voice, Struct10 *s10, + Struct11 *s11, byte flags, const InstrumentExtra *ie) { AdLibPart *part = voice->_part; - s11->modify_val = 0; + s11->modifyVal = 0; s11->flag0x40 = flags & 0x40; s10->loop = flags & 0x20; s11->flag0x10 = flags & 0x10; - s11->param = param_table_1[flags & 0xF]; - s10->max_value = maxval_table[flags & 0xF]; + s11->param = g_paramTable1[flags & 0xF]; + s10->maxValue = g_maxValTable[flags & 0xF]; s10->unk3 = 31; if (s11->flag0x40) { - s10->modwheel = part->_modwheel >> 2; + s10->modWheel = part->_modWheel >> 2; } else { - s10->modwheel = 31; + s10->modWheel = 31; } switch (s11->param) { case 0: - s10->start_value = voice->_vol_2; + s10->startValue = voice->_vol2; break; case 13: - s10->start_value = voice->_vol_1; + s10->startValue = voice->_vol1; break; case 30: - s10->start_value = 31; - s11->s10->modwheel = 0; + s10->startValue = 31; + s11->s10->modWheel = 0; break; case 31: - s10->start_value = 0; + s10->startValue = 0; s11->s10->unk3 = 0; break; default: - s10->start_value = adlib_get_reg_value_param(voice->_channel, s11->param); + s10->startValue = adlibGetRegValueParam(voice->_channel, s11->param); } - struct10_init(s10, ie); + struct10Init(s10, ie); } -void MidiDriver_ADLIB::struct10_init(Struct10 *s10, InstrumentExtra *ie) { +void MidiDriver_ADLIB::struct10Init(Struct10 *s10, const InstrumentExtra *ie) { s10->active = 1; if (!_scummSmallHeader) { - s10->cur_val = 0; + s10->curVal = 0; } else { - s10->cur_val = s10->start_value; - s10->start_value = 0; + s10->curVal = s10->startValue; + s10->startValue = 0; } - s10->modwheel_last = 31; + s10->modWheelLast = 31; s10->count = ie->a; if (s10->count) s10->count *= 63; - s10->table_a[0] = ie->b; - s10->table_a[1] = ie->d; - s10->table_a[2] = ie->f; - s10->table_a[3] = ie->g; + s10->tableA[0] = ie->b; + s10->tableA[1] = ie->d; + s10->tableA[2] = ie->f; + s10->tableA[3] = ie->g; - s10->table_b[0] = ie->c; - s10->table_b[1] = ie->e; - s10->table_b[2] = 0; - s10->table_b[3] = ie->h; + s10->tableB[0] = ie->c; + s10->tableB[1] = ie->e; + s10->tableB[2] = 0; + s10->tableB[3] = ie->h; - struct10_setup(s10); + struct10Setup(s10); } -int MidiDriver_ADLIB::adlib_get_reg_value_param(int chan, byte param) { +int MidiDriver_ADLIB::adlibGetRegValueParam(int chan, byte param) { const AdLibSetParams *as; byte val; byte channel; @@ -1551,10 +2214,10 @@ int MidiDriver_ADLIB::adlib_get_reg_value_param(int chan, byte param) { assert(chan >= 0 && chan < 9); if (param <= 12) { - channel = channel_mappings_2[chan]; + channel = g_operator2Offsets[chan]; } else if (param <= 25) { param -= 13; - channel = channel_mappings[chan]; + channel = g_operator1Offsets[chan]; } else if (param <= 27) { param -= 13; channel = chan; @@ -1566,24 +2229,52 @@ int MidiDriver_ADLIB::adlib_get_reg_value_param(int chan, byte param) { return 0; } - as = &adlib_setparam_table[param]; - val = adlib_get_reg_value(channel + as->a); - val &= as->c; - val >>= as->b; - if (as->d) - val = as->d - val; + as = &g_setParamTable[param]; + val = adlibGetRegValue(channel + as->registerBase); + val &= as->mask; + val >>= as->shift; + if (as->inversion) + val = as->inversion - val; return val; } -void MidiDriver_ADLIB::adlib_note_on(int chan, byte note, int mod) { - int code; +void MidiDriver_ADLIB::adlibNoteOn(int chan, byte note, int mod) { +#ifdef ENABLE_OPL3 + if (_opl3Mode) { + adlibNoteOnEx(chan, note, mod); + return; + } +#endif + assert(chan >= 0 && chan < 9); - code = (note << 7) + mod; - curnote_table[chan] = code; - adlib_playnote(chan, (int16) channel_table_2[chan] + code); + int code = (note << 7) + mod; + _curNotTable[chan] = code; + adlibPlayNote(chan, (int16)_channelTable2[chan] + code); } +void MidiDriver_ADLIB::adlibNoteOnEx(int chan, byte note, int mod) { + assert(chan >= 0 && chan < 9); + +#ifdef ENABLE_OPL3 + if (_opl3Mode) { + const int noteAdjusted = note + (mod >> 8) - 7; + const int pitchAdjust = (mod >> 5) & 7; + + adlibWrite(0xA0 + chan, g_noteFrequencies[(noteAdjusted % 12) * 8 + pitchAdjust + 6 * 8]); + adlibWriteSecondary(0xA0 + chan, g_noteFrequencies[(noteAdjusted % 12) * 8 + pitchAdjust + 6 * 8]); + adlibWrite(0xB0 + chan, (CLIP(noteAdjusted / 12, 0, 7) << 2) | 0x20); + adlibWriteSecondary(0xB0 + chan, (CLIP(noteAdjusted / 12, 0, 7) << 2) | 0x20); + } else { +#endif + int code = (note << 7) + mod; + _curNotTable[chan] = code; + _channelTable2[chan] = 0; + adlibPlayNote(chan, code); +#ifdef ENABLE_OPL3 + } +#endif +} // Plugin interface diff --git a/audio/softsynth/fluidsynth.cpp b/audio/softsynth/fluidsynth.cpp index 2451336784..518e260175 100644 --- a/audio/softsynth/fluidsynth.cpp +++ b/audio/softsynth/fluidsynth.cpp @@ -127,12 +127,54 @@ int MidiDriver_FluidSynth::open() { _synth = new_fluid_synth(_settings); - // In theory, this ought to reduce CPU load... but it doesn't make any - // noticeable difference for me, so disable it for now. + if (ConfMan.getBool("fluidsynth_chorus_activate")) { + fluid_synth_set_chorus_on(_synth, 1); + + int chorusNr = ConfMan.getInt("fluidsynth_chorus_nr"); + double chorusLevel = (double)ConfMan.getInt("fluidsynth_chorus_level") / 100.0; + double chorusSpeed = (double)ConfMan.getInt("fluidsynth_chorus_speed") / 100.0; + double chorusDepthMs = (double)ConfMan.getInt("fluidsynth_chorus_depth") / 10.0; + + Common::String chorusWaveForm = ConfMan.get("fluidsynth_chorus_waveform"); + int chorusType = FLUID_CHORUS_MOD_SINE; + if (chorusWaveForm == "sine") { + chorusType = FLUID_CHORUS_MOD_SINE; + } else { + chorusType = FLUID_CHORUS_MOD_TRIANGLE; + } + + fluid_synth_set_chorus(_synth, chorusNr, chorusLevel, chorusSpeed, chorusDepthMs, chorusType); + } else { + fluid_synth_set_chorus_on(_synth, 0); + } + + if (ConfMan.getBool("fluidsynth_reverb_activate")) { + fluid_synth_set_reverb_on(_synth, 1); + + double reverbRoomSize = (double)ConfMan.getInt("fluidsynth_reverb_roomsize") / 100.0; + double reverbDamping = (double)ConfMan.getInt("fluidsynth_reverb_damping") / 100.0; + int reverbWidth = ConfMan.getInt("fluidsynth_reverb_width"); + double reverbLevel = (double)ConfMan.getInt("fluidsynth_reverb_level") / 100.0; + + fluid_synth_set_reverb(_synth, reverbRoomSize, reverbDamping, reverbWidth, reverbLevel); + } else { + fluid_synth_set_reverb_on(_synth, 0); + } + + Common::String interpolation = ConfMan.get("fluidsynth_misc_interpolation"); + int interpMethod = FLUID_INTERP_4THORDER; + + if (interpolation == "none") { + interpMethod = FLUID_INTERP_NONE; + } else if (interpolation == "linear") { + interpMethod = FLUID_INTERP_LINEAR; + } else if (interpolation == "4th") { + interpMethod = FLUID_INTERP_4THORDER; + } else if (interpolation == "7th") { + interpMethod = FLUID_INTERP_7THORDER; + } - // fluid_synth_set_interp_method(_synth, -1, FLUID_INTERP_LINEAR); - // fluid_synth_set_reverb_on(_synth, 0); - // fluid_synth_set_chorus_on(_synth, 0); + fluid_synth_set_interp_method(_synth, -1, interpMethod); const char *soundfont = ConfMan.get("soundfont").c_str(); diff --git a/audio/softsynth/fmtowns_pc98/towns_midi.cpp b/audio/softsynth/fmtowns_pc98/towns_midi.cpp index b8203944c0..46b1a6406d 100644 --- a/audio/softsynth/fmtowns_pc98/towns_midi.cpp +++ b/audio/softsynth/fmtowns_pc98/towns_midi.cpp @@ -147,17 +147,10 @@ private: TownsMidiOutputChannel *_out; uint8 *_instrument; - uint8 _prg; uint8 _chanIndex; - uint8 _effectLevel; uint8 _priority; - uint8 _ctrlVolume; uint8 _tl; - uint8 _pan; - uint8 _panEff; - uint8 _percS; int8 _transpose; - uint8 _fld_1f; int8 _detune; int8 _modWheel; uint8 _sustain; @@ -659,9 +652,8 @@ const uint16 TownsMidiOutputChannel::_freqLSB[] = { 0x055B, 0x055B, 0x055B, 0x055B, 0x055B, 0x055B, 0x055B, 0x055B }; -TownsMidiInputChannel::TownsMidiInputChannel(MidiDriver_TOWNS *driver, int chanIndex) : MidiChannel(), _driver(driver), _out(0), _prg(0), _chanIndex(chanIndex), - _effectLevel(0), _priority(0), _ctrlVolume(0), _tl(0), _pan(0), _panEff(0), _transpose(0), _percS(0), _pitchBendFactor(0), _pitchBend(0), _sustain(0), _freqLSB(0), - _fld_1f(0), _detune(0), _modWheel(0), _allocated(false) { +TownsMidiInputChannel::TownsMidiInputChannel(MidiDriver_TOWNS *driver, int chanIndex) : MidiChannel(), _driver(driver), _out(0), _chanIndex(chanIndex), + _priority(0), _tl(0), _transpose(0), _pitchBendFactor(0), _pitchBend(0), _sustain(0), _freqLSB(0), _detune(0), _modWheel(0), _allocated(false) { _instrument = new uint8[30]; memset(_instrument, 0, 30); } diff --git a/audio/softsynth/mt32.cpp b/audio/softsynth/mt32.cpp index 186118262f..6813363fd5 100644 --- a/audio/softsynth/mt32.cpp +++ b/audio/softsynth/mt32.cpp @@ -25,6 +25,7 @@ #ifdef USE_MT32EMU #include "audio/softsynth/mt32/mt32emu.h" +#include "audio/softsynth/mt32/ROMInfo.h" #include "audio/softsynth/emumidi.h" #include "audio/musicplugin.h" @@ -47,6 +48,50 @@ #include "graphics/palette.h" #include "graphics/font.h" +#include "gui/message.h" + +namespace MT32Emu { + +class ReportHandlerScummVM : public ReportHandler { +friend class Synth; + +public: + virtual ~ReportHandlerScummVM() {} + +protected: + + // Callback for debug messages, in vprintf() format + void printDebug(const char *fmt, va_list list) { + debug(4, fmt, list); + } + + // Callbacks for reporting various errors and information + void onErrorControlROM() { + GUI::MessageDialog dialog("MT32emu: Init Error - Missing or invalid Control ROM image", "OK"); + dialog.runModal(); + error("MT32emu: Init Error - Missing or invalid Control ROM image"); + } + void onErrorPCMROM() { + GUI::MessageDialog dialog("MT32emu: Init Error - Missing PCM ROM image", "OK"); + dialog.runModal(); + error("MT32emu: Init Error - Missing PCM ROM image"); + } + void showLCDMessage(const char *message) { + g_system->displayMessageOnOSD(message); + } + void onDeviceReset() {} + void onDeviceReconfig() {} + void onNewReverbMode(Bit8u /* mode */) {} + void onNewReverbTime(Bit8u /* time */) {} + void onNewReverbLevel(Bit8u /* level */) {} + void onPartStateChanged(int /* partNum */, bool /* isActive */) {} + void onPolyStateChanged(int /* partNum */) {} + void onPartialStateChanged(int /* partialNum */, int /* oldPartialPhase */, int /* newPartialPhase */) {} + void onProgramChanged(int /* partNum */, char * /* patchName */) {} +}; + +} // end of namespace MT32Emu + class MidiChannel_MT32 : public MidiChannel_MPU401 { void effectLevel(byte value) { } void chorusLevel(byte value) { } @@ -57,6 +102,10 @@ private: MidiChannel_MT32 _midiChannels[16]; uint16 _channelMask; MT32Emu::Synth *_synth; + MT32Emu::ReportHandlerScummVM *_reportHandler; + const MT32Emu::ROMImage *_controlROM, *_pcmROM; + Common::File *_controlFile, *_pcmFile; + void deleteMuntStructures(); int _outputRate; @@ -84,149 +133,6 @@ public: int getRate() const { return _outputRate; } }; -static int eatSystemEvents() { - Common::Event event; - Common::EventManager *eventMan = g_system->getEventManager(); - while (eventMan->pollEvent(event)) { - switch (event.type) { - case Common::EVENT_QUIT: - return 1; - default: - break; - } - } - return 0; -} - -static void drawProgress(float progress) { - const Graphics::Font &font(*FontMan.getFontByUsage(Graphics::FontManager::kGUIFont)); - Graphics::Surface *screen = g_system->lockScreen(); - - assert(screen); - assert(screen->pixels); - - Graphics::PixelFormat screenFormat = g_system->getScreenFormat(); - - int16 w = g_system->getWidth() / 7 * 5; - int16 h = font.getFontHeight(); - int16 x = g_system->getWidth() / 7; - int16 y = g_system->getHeight() / 2 - h / 2; - - Common::Rect r(x, y, x + w, y + h); - - uint32 col; - - if (screenFormat.bytesPerPixel > 1) - col = screenFormat.RGBToColor(0, 171, 0); - else - col = 1; - - screen->frameRect(r, col); - - r.grow(-1); - r.setWidth(uint16(progress * w)); - - if (screenFormat.bytesPerPixel > 1) - col = screenFormat.RGBToColor(171, 0, 0); - else - col = 2; - - screen->fillRect(r, col); - - g_system->unlockScreen(); - g_system->updateScreen(); -} - -static void drawMessage(int offset, const Common::String &text) { - const Graphics::Font &font(*FontMan.getFontByUsage(Graphics::FontManager::kGUIFont)); - Graphics::Surface *screen = g_system->lockScreen(); - - assert(screen); - assert(screen->pixels); - - Graphics::PixelFormat screenFormat = g_system->getScreenFormat(); - - uint16 h = font.getFontHeight(); - uint16 y = g_system->getHeight() / 2 - h / 2 + offset * (h + 1); - - uint32 col; - - if (screenFormat.bytesPerPixel > 1) - col = screenFormat.RGBToColor(0, 0, 0); - else - col = 0; - - Common::Rect r(0, y, screen->w, y + h); - screen->fillRect(r, col); - - if (screenFormat.bytesPerPixel > 1) - col = screenFormat.RGBToColor(0, 171, 0); - else - col = 1; - - font.drawString(screen, text, 0, y, screen->w, col, Graphics::kTextAlignCenter); - - g_system->unlockScreen(); - g_system->updateScreen(); -} - -static Common::File *MT32_OpenFile(void *userData, const char *filename) { - Common::File *file = new Common::File(); - if (!file->open(filename)) { - delete file; - return NULL; - } - return file; -} - -static void MT32_PrintDebug(void *userData, const char *fmt, va_list list) { - if (((MidiDriver_MT32 *)userData)->_initializing) { - char buf[512]; - - vsnprintf(buf, 512, fmt, list); - buf[70] = 0; // Truncate to a reasonable length - - drawMessage(1, buf); - } - - //vdebug(0, fmt, list); // FIXME: Use a higher debug level -} - -static int MT32_Report(void *userData, MT32Emu::ReportType type, const void *reportData) { - switch (type) { - case MT32Emu::ReportType_lcdMessage: - g_system->displayMessageOnOSD((const char *)reportData); - break; - case MT32Emu::ReportType_errorControlROM: - error("Failed to load MT32_CONTROL.ROM"); - break; - case MT32Emu::ReportType_errorPCMROM: - error("Failed to load MT32_PCM.ROM"); - break; - case MT32Emu::ReportType_progressInit: - if (((MidiDriver_MT32 *)userData)->_initializing) { - drawProgress(*((const float *)reportData)); - return eatSystemEvents(); - } - break; - case MT32Emu::ReportType_availableSSE: - debug(1, "MT32emu: SSE is available"); - break; - case MT32Emu::ReportType_usingSSE: - debug(1, "MT32emu: using SSE"); - break; - case MT32Emu::ReportType_available3DNow: - debug(1, "MT32emu: 3DNow! is available"); - break; - case MT32Emu::ReportType_using3DNow: - debug(1, "MT32emu: using 3DNow!"); - break; - default: - break; - } - return 0; -} - //////////////////////////////////////// // // MidiDriver_MT32 @@ -239,6 +145,7 @@ MidiDriver_MT32::MidiDriver_MT32(Audio::Mixer *mixer) : MidiDriver_Emulated(mixe for (i = 0; i < ARRAYSIZE(_midiChannels); ++i) { _midiChannels[i].init(this, i); } + _reportHandler = NULL; _synth = NULL; // A higher baseFreq reduces the length used in generateSamples(), // and means that the timer callback will be called more often. @@ -252,30 +159,35 @@ MidiDriver_MT32::MidiDriver_MT32(Audio::Mixer *mixer) : MidiDriver_Emulated(mixe } MidiDriver_MT32::~MidiDriver_MT32() { + deleteMuntStructures(); +} + +void MidiDriver_MT32::deleteMuntStructures() { delete _synth; + _synth = NULL; + delete _reportHandler; + _reportHandler = NULL; + + if (_controlROM) + MT32Emu::ROMImage::freeROMImage(_controlROM); + _controlROM = NULL; + if (_pcmROM) + MT32Emu::ROMImage::freeROMImage(_pcmROM); + _pcmROM = NULL; + + delete _controlFile; + _controlFile = NULL; + delete _pcmFile; + _pcmFile = NULL; } int MidiDriver_MT32::open() { - MT32Emu::SynthProperties prop; - if (_isOpen) return MERR_ALREADY_OPEN; MidiDriver_Emulated::open(); - - memset(&prop, 0, sizeof(prop)); - prop.sampleRate = getRate(); - prop.useReverb = true; - prop.useDefaultReverb = false; - prop.reverbType = 0; - prop.reverbTime = 5; - prop.reverbLevel = 3; - prop.userData = this; - prop.printDebug = MT32_PrintDebug; - prop.report = MT32_Report; - prop.openFile = MT32_OpenFile; - - _synth = new MT32Emu::Synth(); + _reportHandler = new MT32Emu::ReportHandlerScummVM(); + _synth = new MT32Emu::Synth(_reportHandler); Graphics::PixelFormat screenFormat = g_system->getScreenFormat(); @@ -290,8 +202,16 @@ int MidiDriver_MT32::open() { } _initializing = true; - drawMessage(-1, _s("Initializing MT-32 Emulator")); - if (!_synth->open(prop)) + debug(4, _s("Initializing MT-32 Emulator")); + _controlFile = new Common::File(); + if (!_controlFile->open("MT32_CONTROL.ROM")) + error("Error opening MT32_CONTROL.ROM"); + _pcmFile = new Common::File(); + if (!_pcmFile->open("MT32_PCM.ROM")) + error("Error opening MT32_PCM.ROM"); + _controlROM = MT32Emu::ROMImage::makeROMImage(_controlFile); + _pcmROM = MT32Emu::ROMImage::makeROMImage(_pcmFile); + if (!_synth->open(*_controlROM, *_pcmROM)) return MERR_DEVICE_NOT_AVAILABLE; double gain = (double)ConfMan.getInt("midi_gain") / 100.0; @@ -352,8 +272,7 @@ void MidiDriver_MT32::close() { _mixer->stopHandle(_mixerSoundHandle); _synth->close(); - delete _synth; - _synth = NULL; + deleteMuntStructures(); } void MidiDriver_MT32::generateSamples(int16 *data, int len) { diff --git a/audio/softsynth/mt32/AReverbModel.cpp b/audio/softsynth/mt32/AReverbModel.cpp index 4ee6c87943..595b286a31 100644 --- a/audio/softsynth/mt32/AReverbModel.cpp +++ b/audio/softsynth/mt32/AReverbModel.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011, 2012 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -16,64 +16,97 @@ */ #include "mt32emu.h" -#include "AReverbModel.h" - -using namespace MT32Emu; - -// Default reverb settings for modes 0-2 - -static const unsigned int NUM_ALLPASSES = 6; -static const unsigned int NUM_DELAYS = 5; -static const Bit32u MODE_0_ALLPASSES[] = {729, 78, 394, 994, 1250, 1889}; -static const Bit32u MODE_0_DELAYS[] = {846, 4, 1819, 778, 346}; -static const float MODE_0_TIMES[] = {0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.9f}; -static const float MODE_0_LEVELS[] = {0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 1.01575f}; +#if MT32EMU_USE_REVERBMODEL == 1 -static const Bit32u MODE_1_ALLPASSES[] = {176, 809, 1324, 1258}; -static const Bit32u MODE_1_DELAYS[] = {2262, 124, 974, 2516, 356}; -static const float MODE_1_TIMES[] = {0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.95f}; -static const float MODE_1_LEVELS[] = {0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 1.01575f}; - -static const Bit32u MODE_2_ALLPASSES[] = {78, 729, 994, 389}; -static const Bit32u MODE_2_DELAYS[] = {846, 4, 1819, 778, 346}; -static const float MODE_2_TIMES[] = {0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f}; -static const float MODE_2_LEVELS[] = {0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f}; - -const AReverbSettings AReverbModel::REVERB_MODE_0_SETTINGS = {MODE_0_ALLPASSES, MODE_0_DELAYS, MODE_0_TIMES, MODE_0_LEVELS, 0.687770909f, 0.5f, 0.5f}; -const AReverbSettings AReverbModel::REVERB_MODE_1_SETTINGS = {MODE_1_ALLPASSES, MODE_1_DELAYS, MODE_1_TIMES, MODE_1_LEVELS, 0.712025098f, 0.375f, 0.625f}; -const AReverbSettings AReverbModel::REVERB_MODE_2_SETTINGS = {MODE_2_ALLPASSES, MODE_2_DELAYS, MODE_2_TIMES, MODE_2_LEVELS, 0.939522749f, 0.0f, 0.0f}; +#include "AReverbModel.h" -RingBuffer::RingBuffer(Bit32u newsize) { - index = 0; - size = newsize; +// Analysing of state of reverb RAM address lines gives exact sizes of the buffers of filters used. This also indicates that +// the reverb model implemented in the real devices consists of three series allpass filters preceded by a non-feedback comb (or a delay with a LPF) +// and followed by three parallel comb filters + +namespace MT32Emu { + +// Because LA-32 chip makes it's output available to process by the Boss chip with a significant delay, +// the Boss chip puts to the buffer the LA32 dry output when it is ready and performs processing of the _previously_ latched data. +// Of course, the right way would be to use a dedicated variable for this, but our reverb model is way higher level, +// so we can simply increase the input buffer size. +static const Bit32u PROCESS_DELAY = 1; + +// Default reverb settings for modes 0-2. These correspond to CM-32L / LAPC-I "new" reverb settings. MT-32 reverb is a bit different. +// Found by tracing reverb RAM data lines (thanks go to Lord_Nightmare & balrog). + +static const Bit32u NUM_ALLPASSES = 3; +static const Bit32u NUM_COMBS = 4; // Well, actually there are 3 comb filters, but the entrance LPF + delay can be perfectly processed via a comb here. + +static const Bit32u MODE_0_ALLPASSES[] = {994, 729, 78}; +static const Bit32u MODE_0_COMBS[] = {705 + PROCESS_DELAY, 2349, 2839, 3632}; +static const Bit32u MODE_0_OUTL[] = {2349, 141, 1960}; +static const Bit32u MODE_0_OUTR[] = {1174, 1570, 145}; +static const Bit32u MODE_0_COMB_FACTOR[] = {0x3C, 0x60, 0x60, 0x60}; +static const Bit32u MODE_0_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, + 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, + 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98}; +static const Bit32u MODE_0_LEVELS[] = {10*1, 10*3, 10*5, 10*7, 11*9, 11*12, 11*15, 13*15}; +static const Bit32u MODE_0_LPF_AMP = 6; + +static const Bit32u MODE_1_ALLPASSES[] = {1324, 809, 176}; +static const Bit32u MODE_1_COMBS[] = {961 + PROCESS_DELAY, 2619, 3545, 4519}; +static const Bit32u MODE_1_OUTL[] = {2618, 1760, 4518}; +static const Bit32u MODE_1_OUTR[] = {1300, 3532, 2274}; +static const Bit32u MODE_1_COMB_FACTOR[] = {0x30, 0x60, 0x60, 0x60}; +static const Bit32u MODE_1_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x28, 0x48, 0x60, 0x70, 0x78, 0x80, 0x90, 0x98, + 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, + 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98}; +static const Bit32u MODE_1_LEVELS[] = {10*1, 10*3, 11*5, 11*7, 11*9, 11*12, 11*15, 14*15}; +static const Bit32u MODE_1_LPF_AMP = 6; + +static const Bit32u MODE_2_ALLPASSES[] = {969, 644, 157}; +static const Bit32u MODE_2_COMBS[] = {116 + PROCESS_DELAY, 2259, 2839, 3539}; +static const Bit32u MODE_2_OUTL[] = {2259, 718, 1769}; +static const Bit32u MODE_2_OUTR[] = {1136, 2128, 1}; +static const Bit32u MODE_2_COMB_FACTOR[] = {0, 0x20, 0x20, 0x20}; +static const Bit32u MODE_2_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0, + 0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0, + 0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0}; +static const Bit32u MODE_2_LEVELS[] = {10*1, 10*3, 11*5, 11*7, 11*9, 11*12, 12*15, 14*15}; +static const Bit32u MODE_2_LPF_AMP = 8; + +static const AReverbSettings REVERB_MODE_0_SETTINGS = {MODE_0_ALLPASSES, MODE_0_COMBS, MODE_0_OUTL, MODE_0_OUTR, MODE_0_COMB_FACTOR, MODE_0_COMB_FEEDBACK, MODE_0_LEVELS, MODE_0_LPF_AMP}; +static const AReverbSettings REVERB_MODE_1_SETTINGS = {MODE_1_ALLPASSES, MODE_1_COMBS, MODE_1_OUTL, MODE_1_OUTR, MODE_1_COMB_FACTOR, MODE_1_COMB_FEEDBACK, MODE_1_LEVELS, MODE_1_LPF_AMP}; +static const AReverbSettings REVERB_MODE_2_SETTINGS = {MODE_2_ALLPASSES, MODE_2_COMBS, MODE_2_OUTL, MODE_2_OUTR, MODE_2_COMB_FACTOR, MODE_2_COMB_FEEDBACK, MODE_2_LEVELS, MODE_2_LPF_AMP}; + +static const AReverbSettings * const REVERB_SETTINGS[] = {&REVERB_MODE_0_SETTINGS, &REVERB_MODE_1_SETTINGS, &REVERB_MODE_2_SETTINGS, &REVERB_MODE_0_SETTINGS}; + +RingBuffer::RingBuffer(const Bit32u newsize) : size(newsize), index(0) { buffer = new float[size]; } RingBuffer::~RingBuffer() { delete[] buffer; buffer = NULL; - size = 0; } float RingBuffer::next() { - index++; - if (index >= size) { + if (++index >= size) { index = 0; } return buffer[index]; } -bool RingBuffer::isEmpty() { +bool RingBuffer::isEmpty() const { if (buffer == NULL) return true; float *buf = buffer; - float total = 0; + float max = 0.001f; for (Bit32u i = 0; i < size; i++) { - total += (*buf < 0 ? -*buf : *buf); + if ((*buf < -max) || (*buf > max)) return false; buf++; } - return ((total / size) < .0002 ? true : false); + return true; } void RingBuffer::mute() { @@ -83,59 +116,66 @@ void RingBuffer::mute() { } } -AllpassFilter::AllpassFilter(Bit32u useSize) : RingBuffer(useSize) { -} - -Delay::Delay(Bit32u useSize) : RingBuffer(useSize) { -} +AllpassFilter::AllpassFilter(const Bit32u useSize) : RingBuffer(useSize) {} -float AllpassFilter::process(float in) { - // This model corresponds to the allpass filter implementation in the real CM-32L device +float AllpassFilter::process(const float in) { + // This model corresponds to the allpass filter implementation of the real CM-32L device // found from sample analysis - float out; - - out = next(); + const float bufferOut = next(); // store input - feedback / 2 - buffer[index] = in - 0.5f * out; + buffer[index] = in - 0.5f * bufferOut; // return buffer output + feedforward / 2 - return out + 0.5f * buffer[index]; + return bufferOut + 0.5f * buffer[index]; } -float Delay::process(float in) { - // Implements a very simple delay +CombFilter::CombFilter(const Bit32u useSize) : RingBuffer(useSize) {} - float out; +void CombFilter::process(const float in) { + // This model corresponds to the comb filter implementation of the real CM-32L device + // found from sample analysis + + // the previously stored value + float last = buffer[index]; - out = next(); + // prepare input + feedback + float filterIn = in + next() * feedbackFactor; - // store input - buffer[index] = in; + // store input + feedback processed by a low-pass filter + buffer[index] = filterFactor * last - filterIn; +} - // return buffer output - return out; +float CombFilter::getOutputAt(const Bit32u outIndex) const { + return buffer[(size + index - outIndex) % size]; } -AReverbModel::AReverbModel(const AReverbSettings *useSettings) : allpasses(NULL), delays(NULL), currentSettings(useSettings) { +void CombFilter::setFeedbackFactor(const float useFeedbackFactor) { + feedbackFactor = useFeedbackFactor; } +void CombFilter::setFilterFactor(const float useFilterFactor) { + filterFactor = useFilterFactor; +} + +AReverbModel::AReverbModel(const ReverbMode mode) : allpasses(NULL), combs(NULL), currentSettings(*REVERB_SETTINGS[mode]) {} + AReverbModel::~AReverbModel() { close(); } -void AReverbModel::open(unsigned int /*sampleRate*/) { - // FIXME: filter sizes must be multiplied by sample rate to 32000Hz ratio - // IIR filter values depend on sample rate as well +void AReverbModel::open() { allpasses = new AllpassFilter*[NUM_ALLPASSES]; for (Bit32u i = 0; i < NUM_ALLPASSES; i++) { - allpasses[i] = new AllpassFilter(currentSettings->allpassSizes[i]); + allpasses[i] = new AllpassFilter(currentSettings.allpassSizes[i]); } - delays = new Delay*[NUM_DELAYS]; - for (Bit32u i = 0; i < NUM_DELAYS; i++) { - delays[i] = new Delay(currentSettings->delaySizes[i]); + combs = new CombFilter*[NUM_COMBS]; + for (Bit32u i = 0; i < NUM_COMBS; i++) { + combs[i] = new CombFilter(currentSettings.combSizes[i]); + combs[i]->setFilterFactor(currentSettings.filterFactor[i] / 256.0f); } + lpfAmp = currentSettings.lpfAmp / 16.0f; mute(); } @@ -150,84 +190,80 @@ void AReverbModel::close() { delete[] allpasses; allpasses = NULL; } - if (delays != NULL) { - for (Bit32u i = 0; i < NUM_DELAYS; i++) { - if (delays[i] != NULL) { - delete delays[i]; - delays[i] = NULL; + if (combs != NULL) { + for (Bit32u i = 0; i < NUM_COMBS; i++) { + if (combs[i] != NULL) { + delete combs[i]; + combs[i] = NULL; } } - delete[] delays; - delays = NULL; + delete[] combs; + combs = NULL; } } void AReverbModel::mute() { + if (allpasses == NULL || combs == NULL) return; for (Bit32u i = 0; i < NUM_ALLPASSES; i++) { allpasses[i]->mute(); } - for (Bit32u i = 0; i < NUM_DELAYS; i++) { - delays[i]->mute(); + for (Bit32u i = 0; i < NUM_COMBS; i++) { + combs[i]->mute(); } - filterhist1 = 0; - filterhist2 = 0; - combhist = 0; } void AReverbModel::setParameters(Bit8u time, Bit8u level) { // FIXME: wetLevel definitely needs ramping when changed // Although, most games don't set reverb level during MIDI playback - decayTime = currentSettings->decayTimes[time]; - wetLevel = currentSettings->wetLevels[level]; + if (combs == NULL) return; + level &= 7; + time &= 7; + for (Bit32u i = 0; i < NUM_COMBS; i++) { + combs[i]->setFeedbackFactor(currentSettings.decayTimes[(i << 3) + time] / 256.0f); + } + wetLevel = (level == 0 && time == 0) ? 0.0f : 0.5f * lpfAmp * currentSettings.wetLevels[level] / 256.0f; } bool AReverbModel::isActive() const { - bool bActive = false; for (Bit32u i = 0; i < NUM_ALLPASSES; i++) { - bActive |= !allpasses[i]->isEmpty(); + if (!allpasses[i]->isEmpty()) return true; } - for (Bit32u i = 0; i < NUM_DELAYS; i++) { - bActive |= !delays[i]->isEmpty(); + for (Bit32u i = 0; i < NUM_COMBS; i++) { + if (!combs[i]->isEmpty()) return true; } - return bActive; + return false; } void AReverbModel::process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples) { -// Three series allpass filters followed by a delay, fourth allpass filter and another delay - float dry, link, outL1, outL2, outR1, outR2; + float dry, link, outL1; for (unsigned long i = 0; i < numSamples; i++) { - dry = *inLeft + *inRight; + dry = wetLevel * (*inLeft + *inRight); - // Implementation of 2-stage IIR single-pole low-pass filter - // found at the entrance of reverb processing on real devices - filterhist1 += (dry - filterhist1) * currentSettings->filtVal; - filterhist2 += (filterhist1 - filterhist2) * currentSettings->filtVal; + // Get the last stored sample before processing in order not to loose it + link = combs[0]->getOutputAt(currentSettings.combSizes[0] - 1); - link = allpasses[0]->process(-filterhist2); - link = allpasses[1]->process(link); + combs[0]->process(-dry); - // this implements a comb filter cross-linked with the fourth allpass filter - link += combhist * decayTime; + link = allpasses[0]->process(link); + link = allpasses[1]->process(link); link = allpasses[2]->process(link); - link = delays[0]->process(link); - outL1 = link; - link = allpasses[3]->process(link); - link = delays[1]->process(link); - outR1 = link; - link = allpasses[4]->process(link); - link = delays[2]->process(link); - outL2 = link; - link = allpasses[5]->process(link); - link = delays[3]->process(link); - outR2 = link; - link = delays[4]->process(link); - - // comb filter end point - combhist = combhist * currentSettings->damp1 + link * currentSettings->damp2; - - *outLeft = (outL1 + outL2) * wetLevel; - *outRight = (outR1 + outR2) * wetLevel; + + // If the output position is equal to the comb size, get it now in order not to loose it + outL1 = 1.5f * combs[1]->getOutputAt(currentSettings.outLPositions[0] - 1); + + combs[1]->process(link); + combs[2]->process(link); + combs[3]->process(link); + + link = outL1 + 1.5f * combs[2]->getOutputAt(currentSettings.outLPositions[1]); + link += combs[3]->getOutputAt(currentSettings.outLPositions[2]); + *outLeft = link; + + link = 1.5f * combs[1]->getOutputAt(currentSettings.outRPositions[0]); + link += 1.5f * combs[2]->getOutputAt(currentSettings.outRPositions[1]); + link += combs[3]->getOutputAt(currentSettings.outRPositions[2]); + *outRight = link; inLeft++; inRight++; @@ -235,3 +271,7 @@ void AReverbModel::process(const float *inLeft, const float *inRight, float *out outRight++; } } + +} + +#endif diff --git a/audio/softsynth/mt32/AReverbModel.h b/audio/softsynth/mt32/AReverbModel.h index 3fae08c34c..be1ca4916b 100644 --- a/audio/softsynth/mt32/AReverbModel.h +++ b/audio/softsynth/mt32/AReverbModel.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011, 2012 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -21,66 +21,67 @@ namespace MT32Emu { struct AReverbSettings { - const Bit32u *allpassSizes; - const Bit32u *delaySizes; - const float *decayTimes; - const float *wetLevels; - float filtVal; - float damp1; - float damp2; + const Bit32u * const allpassSizes; + const Bit32u * const combSizes; + const Bit32u * const outLPositions; + const Bit32u * const outRPositions; + const Bit32u * const filterFactor; + const Bit32u * const decayTimes; + const Bit32u * const wetLevels; + const Bit32u lpfAmp; }; class RingBuffer { protected: float *buffer; - Bit32u size; + const Bit32u size; Bit32u index; + public: - RingBuffer(Bit32u size); + RingBuffer(const Bit32u size); virtual ~RingBuffer(); float next(); - bool isEmpty(); + bool isEmpty() const; void mute(); }; class AllpassFilter : public RingBuffer { public: - AllpassFilter(Bit32u size); - float process(float in); + AllpassFilter(const Bit32u size); + float process(const float in); }; -class Delay : public RingBuffer { +class CombFilter : public RingBuffer { + float feedbackFactor; + float filterFactor; + public: - Delay(Bit32u size); - float process(float in); + CombFilter(const Bit32u size); + void process(const float in); + float getOutputAt(const Bit32u outIndex) const; + void setFeedbackFactor(const float useFeedbackFactor); + void setFilterFactor(const float useFilterFactor); }; class AReverbModel : public ReverbModel { AllpassFilter **allpasses; - Delay **delays; + CombFilter **combs; - const AReverbSettings *currentSettings; - float decayTime; + const AReverbSettings ¤tSettings; + float lpfAmp; float wetLevel; - float filterhist1, filterhist2; - float combhist; void mute(); + public: - AReverbModel(const AReverbSettings *newSettings); + AReverbModel(const ReverbMode mode); ~AReverbModel(); - void open(unsigned int sampleRate); + void open(); void close(); void setParameters(Bit8u time, Bit8u level); void process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples); bool isActive() const; - - static const AReverbSettings REVERB_MODE_0_SETTINGS; - static const AReverbSettings REVERB_MODE_1_SETTINGS; - static const AReverbSettings REVERB_MODE_2_SETTINGS; }; -// Default reverb settings for modes 0-2 - } #endif diff --git a/audio/softsynth/mt32/BReverbModel.cpp b/audio/softsynth/mt32/BReverbModel.cpp new file mode 100644 index 0000000000..2570424187 --- /dev/null +++ b/audio/softsynth/mt32/BReverbModel.cpp @@ -0,0 +1,393 @@ +/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher + * Copyright (C) 2011, 2012 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "mt32emu.h" + +#if MT32EMU_USE_REVERBMODEL == 2 + +#include "BReverbModel.h" + +// Analysing of state of reverb RAM address lines gives exact sizes of the buffers of filters used. This also indicates that +// the reverb model implemented in the real devices consists of three series allpass filters preceded by a non-feedback comb (or a delay with a LPF) +// and followed by three parallel comb filters + +namespace MT32Emu { + +// Because LA-32 chip makes it's output available to process by the Boss chip with a significant delay, +// the Boss chip puts to the buffer the LA32 dry output when it is ready and performs processing of the _previously_ latched data. +// Of course, the right way would be to use a dedicated variable for this, but our reverb model is way higher level, +// so we can simply increase the input buffer size. +static const Bit32u PROCESS_DELAY = 1; + +static const Bit32u MODE_3_ADDITIONAL_DELAY = 1; +static const Bit32u MODE_3_FEEDBACK_DELAY = 1; + +// Default reverb settings for modes 0-2. These correspond to CM-32L / LAPC-I "new" reverb settings. MT-32 reverb is a bit different. +// Found by tracing reverb RAM data lines (thanks go to Lord_Nightmare & balrog). + +static const Bit32u MODE_0_NUMBER_OF_ALLPASSES = 3; +static const Bit32u MODE_0_ALLPASSES[] = {994, 729, 78}; +static const Bit32u MODE_0_NUMBER_OF_COMBS = 4; // Well, actually there are 3 comb filters, but the entrance LPF + delay can be processed via a hacked comb. +static const Bit32u MODE_0_COMBS[] = {705 + PROCESS_DELAY, 2349, 2839, 3632}; +static const Bit32u MODE_0_OUTL[] = {2349, 141, 1960}; +static const Bit32u MODE_0_OUTR[] = {1174, 1570, 145}; +static const Bit32u MODE_0_COMB_FACTOR[] = {0xA0, 0x60, 0x60, 0x60}; +static const Bit32u MODE_0_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, + 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, + 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98}; +static const Bit32u MODE_0_DRY_AMP[] = {0xA0, 0xA0, 0xA0, 0xA0, 0xB0, 0xB0, 0xB0, 0xD0}; +static const Bit32u MODE_0_WET_AMP[] = {0x10, 0x30, 0x50, 0x70, 0x90, 0xC0, 0xF0, 0xF0}; +static const Bit32u MODE_0_LPF_AMP = 0x60; + +static const Bit32u MODE_1_NUMBER_OF_ALLPASSES = 3; +static const Bit32u MODE_1_ALLPASSES[] = {1324, 809, 176}; +static const Bit32u MODE_1_NUMBER_OF_COMBS = 4; // Same as for mode 0 above +static const Bit32u MODE_1_COMBS[] = {961 + PROCESS_DELAY, 2619, 3545, 4519}; +static const Bit32u MODE_1_OUTL[] = {2618, 1760, 4518}; +static const Bit32u MODE_1_OUTR[] = {1300, 3532, 2274}; +static const Bit32u MODE_1_COMB_FACTOR[] = {0x80, 0x60, 0x60, 0x60}; +static const Bit32u MODE_1_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x28, 0x48, 0x60, 0x70, 0x78, 0x80, 0x90, 0x98, + 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, + 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98}; +static const Bit32u MODE_1_DRY_AMP[] = {0xA0, 0xA0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xE0}; +static const Bit32u MODE_1_WET_AMP[] = {0x10, 0x30, 0x50, 0x70, 0x90, 0xC0, 0xF0, 0xF0}; +static const Bit32u MODE_1_LPF_AMP = 0x60; + +static const Bit32u MODE_2_NUMBER_OF_ALLPASSES = 3; +static const Bit32u MODE_2_ALLPASSES[] = {969, 644, 157}; +static const Bit32u MODE_2_NUMBER_OF_COMBS = 4; // Same as for mode 0 above +static const Bit32u MODE_2_COMBS[] = {116 + PROCESS_DELAY, 2259, 2839, 3539}; +static const Bit32u MODE_2_OUTL[] = {2259, 718, 1769}; +static const Bit32u MODE_2_OUTR[] = {1136, 2128, 1}; +static const Bit32u MODE_2_COMB_FACTOR[] = {0, 0x20, 0x20, 0x20}; +static const Bit32u MODE_2_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0, + 0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0, + 0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0}; +static const Bit32u MODE_2_DRY_AMP[] = {0xA0, 0xA0, 0xB0, 0xB0, 0xB0, 0xB0, 0xC0, 0xE0}; +static const Bit32u MODE_2_WET_AMP[] = {0x10, 0x30, 0x50, 0x70, 0x90, 0xC0, 0xF0, 0xF0}; +static const Bit32u MODE_2_LPF_AMP = 0x80; + +static const Bit32u MODE_3_NUMBER_OF_ALLPASSES = 0; +static const Bit32u MODE_3_NUMBER_OF_COMBS = 1; +static const Bit32u MODE_3_DELAY[] = {16000 + MODE_3_FEEDBACK_DELAY + PROCESS_DELAY + MODE_3_ADDITIONAL_DELAY}; +static const Bit32u MODE_3_OUTL[] = {400, 624, 960, 1488, 2256, 3472, 5280, 8000}; +static const Bit32u MODE_3_OUTR[] = {800, 1248, 1920, 2976, 4512, 6944, 10560, 16000}; +static const Bit32u MODE_3_COMB_FACTOR[] = {0x68}; +static const Bit32u MODE_3_COMB_FEEDBACK[] = {0x68, 0x60}; +static const Bit32u MODE_3_DRY_AMP[] = {0x20, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50}; +static const Bit32u MODE_3_WET_AMP[] = {0x18, 0x18, 0x28, 0x40, 0x60, 0x80, 0xA8, 0xF8}; + +static const BReverbSettings REVERB_MODE_0_SETTINGS = {MODE_0_NUMBER_OF_ALLPASSES, MODE_0_ALLPASSES, MODE_0_NUMBER_OF_COMBS, MODE_0_COMBS, MODE_0_OUTL, MODE_0_OUTR, MODE_0_COMB_FACTOR, MODE_0_COMB_FEEDBACK, MODE_0_DRY_AMP, MODE_0_WET_AMP, MODE_0_LPF_AMP}; +static const BReverbSettings REVERB_MODE_1_SETTINGS = {MODE_1_NUMBER_OF_ALLPASSES, MODE_1_ALLPASSES, MODE_1_NUMBER_OF_COMBS, MODE_1_COMBS, MODE_1_OUTL, MODE_1_OUTR, MODE_1_COMB_FACTOR, MODE_1_COMB_FEEDBACK, MODE_1_DRY_AMP, MODE_1_WET_AMP, MODE_1_LPF_AMP}; +static const BReverbSettings REVERB_MODE_2_SETTINGS = {MODE_2_NUMBER_OF_ALLPASSES, MODE_2_ALLPASSES, MODE_2_NUMBER_OF_COMBS, MODE_2_COMBS, MODE_2_OUTL, MODE_2_OUTR, MODE_2_COMB_FACTOR, MODE_2_COMB_FEEDBACK, MODE_2_DRY_AMP, MODE_2_WET_AMP, MODE_2_LPF_AMP}; +static const BReverbSettings REVERB_MODE_3_SETTINGS = {MODE_3_NUMBER_OF_ALLPASSES, NULL, MODE_3_NUMBER_OF_COMBS, MODE_3_DELAY, MODE_3_OUTL, MODE_3_OUTR, MODE_3_COMB_FACTOR, MODE_3_COMB_FEEDBACK, MODE_3_DRY_AMP, MODE_3_WET_AMP, 0}; + +static const BReverbSettings * const REVERB_SETTINGS[] = {&REVERB_MODE_0_SETTINGS, &REVERB_MODE_1_SETTINGS, &REVERB_MODE_2_SETTINGS, &REVERB_MODE_3_SETTINGS}; + +// This algorithm tries to emulate exactly Boss multiplication operation (at least this is what we see on reverb RAM data lines). +// Also LA32 is suspected to use the similar one to perform PCM interpolation and ring modulation. +static Bit32s weirdMul(Bit32s a, Bit8u addMask, Bit8u carryMask) { + Bit8u mask = 0x80; + Bit32s res = 0; + for (int i = 0; i < 8; i++) { + Bit32s carry = (a < 0) && (mask & carryMask) > 0 ? a & 1 : 0; + a >>= 1; + res += (mask & addMask) > 0 ? a + carry : 0; + mask >>= 1; + } + return res; +} + +RingBuffer::RingBuffer(Bit32u newsize) : size(newsize), index(0) { + buffer = new Bit16s[size]; +} + +RingBuffer::~RingBuffer() { + delete[] buffer; + buffer = NULL; +} + +Bit32s RingBuffer::next() { + if (++index >= size) { + index = 0; + } + return buffer[index]; +} + +bool RingBuffer::isEmpty() const { + if (buffer == NULL) return true; + + Bit16s *buf = buffer; + for (Bit32u i = 0; i < size; i++) { + if (*buf < -8 || *buf > 8) return false; + buf++; + } + return true; +} + +void RingBuffer::mute() { + Bit16s *buf = buffer; + for (Bit32u i = 0; i < size; i++) { + *buf++ = 0; + } +} + +AllpassFilter::AllpassFilter(const Bit32u useSize) : RingBuffer(useSize) {} + +Bit32s AllpassFilter::process(const Bit32s in) { + // This model corresponds to the allpass filter implementation of the real CM-32L device + // found from sample analysis + + Bit16s bufferOut = next(); + + // store input - feedback / 2 + buffer[index] = in - (bufferOut >> 1); + + // return buffer output + feedforward / 2 + return bufferOut + (buffer[index] >> 1); +} + +CombFilter::CombFilter(const Bit32u useSize, const Bit32u useFilterFactor) : RingBuffer(useSize), filterFactor(useFilterFactor) {} + +void CombFilter::process(const Bit32s in) { + // This model corresponds to the comb filter implementation of the real CM-32L device + + // the previously stored value + Bit32s last = buffer[index]; + + // prepare input + feedback + Bit32s filterIn = in + weirdMul(next(), feedbackFactor, 0xF0 /* Maybe 0x80 ? */); + + // store input + feedback processed by a low-pass filter + buffer[index] = weirdMul(last, filterFactor, 0x40) - filterIn; +} + +Bit32s CombFilter::getOutputAt(const Bit32u outIndex) const { + return buffer[(size + index - outIndex) % size]; +} + +void CombFilter::setFeedbackFactor(const Bit32u useFeedbackFactor) { + feedbackFactor = useFeedbackFactor; +} + +DelayWithLowPassFilter::DelayWithLowPassFilter(const Bit32u useSize, const Bit32u useFilterFactor, const Bit32u useAmp) + : CombFilter(useSize, useFilterFactor), amp(useAmp) {} + +void DelayWithLowPassFilter::process(const Bit32s in) { + // the previously stored value + Bit32s last = buffer[index]; + + // move to the next index + next(); + + // low-pass filter process + Bit32s lpfOut = weirdMul(last, filterFactor, 0xFF) + in; + + // store lpfOut multiplied by LPF amp factor + buffer[index] = weirdMul(lpfOut, amp, 0xFF); +} + +TapDelayCombFilter::TapDelayCombFilter(const Bit32u useSize, const Bit32u useFilterFactor) : CombFilter(useSize, useFilterFactor) {} + +void TapDelayCombFilter::process(const Bit32s in) { + // the previously stored value + Bit32s last = buffer[index]; + + // move to the next index + next(); + + // prepare input + feedback + // Actually, the size of the filter varies with the TIME parameter, the feedback sample is taken from the position just below the right output + Bit32s filterIn = in + weirdMul(getOutputAt(outR + MODE_3_FEEDBACK_DELAY), feedbackFactor, 0xF0); + + // store input + feedback processed by a low-pass filter + buffer[index] = weirdMul(last, filterFactor, 0xF0) - filterIn; +} + +Bit32s TapDelayCombFilter::getLeftOutput() const { + return getOutputAt(outL + PROCESS_DELAY + MODE_3_ADDITIONAL_DELAY); +} + +Bit32s TapDelayCombFilter::getRightOutput() const { + return getOutputAt(outR + PROCESS_DELAY + MODE_3_ADDITIONAL_DELAY); +} + +void TapDelayCombFilter::setOutputPositions(const Bit32u useOutL, const Bit32u useOutR) { + outL = useOutL; + outR = useOutR; +} + +BReverbModel::BReverbModel(const ReverbMode mode) + : allpasses(NULL), combs(NULL), currentSettings(*REVERB_SETTINGS[mode]), tapDelayMode(mode == REVERB_MODE_TAP_DELAY) {} + +BReverbModel::~BReverbModel() { + close(); +} + +void BReverbModel::open() { + if (currentSettings.numberOfAllpasses > 0) { + allpasses = new AllpassFilter*[currentSettings.numberOfAllpasses]; + for (Bit32u i = 0; i < currentSettings.numberOfAllpasses; i++) { + allpasses[i] = new AllpassFilter(currentSettings.allpassSizes[i]); + } + } + combs = new CombFilter*[currentSettings.numberOfCombs]; + if (tapDelayMode) { + *combs = new TapDelayCombFilter(*currentSettings.combSizes, *currentSettings.filterFactors); + } else { + combs[0] = new DelayWithLowPassFilter(currentSettings.combSizes[0], currentSettings.filterFactors[0], currentSettings.lpfAmp); + for (Bit32u i = 1; i < currentSettings.numberOfCombs; i++) { + combs[i] = new CombFilter(currentSettings.combSizes[i], currentSettings.filterFactors[i]); + } + } + mute(); +} + +void BReverbModel::close() { + if (allpasses != NULL) { + for (Bit32u i = 0; i < currentSettings.numberOfAllpasses; i++) { + if (allpasses[i] != NULL) { + delete allpasses[i]; + allpasses[i] = NULL; + } + } + delete[] allpasses; + allpasses = NULL; + } + if (combs != NULL) { + for (Bit32u i = 0; i < currentSettings.numberOfCombs; i++) { + if (combs[i] != NULL) { + delete combs[i]; + combs[i] = NULL; + } + } + delete[] combs; + combs = NULL; + } +} + +void BReverbModel::mute() { + if (allpasses != NULL) { + for (Bit32u i = 0; i < currentSettings.numberOfAllpasses; i++) { + allpasses[i]->mute(); + } + } + if (combs != NULL) { + for (Bit32u i = 0; i < currentSettings.numberOfCombs; i++) { + combs[i]->mute(); + } + } +} + +void BReverbModel::setParameters(Bit8u time, Bit8u level) { + if (combs == NULL) return; + level &= 7; + time &= 7; + if (tapDelayMode) { + TapDelayCombFilter *comb = static_cast<TapDelayCombFilter *> (*combs); + comb->setOutputPositions(currentSettings.outLPositions[time], currentSettings.outRPositions[time & 7]); + comb->setFeedbackFactor(currentSettings.feedbackFactors[((level < 3) || (time < 6)) ? 0 : 1]); + } else { + for (Bit32u i = 0; i < currentSettings.numberOfCombs; i++) { + combs[i]->setFeedbackFactor(currentSettings.feedbackFactors[(i << 3) + time]); + } + } + if (time == 0 && level == 0) { + dryAmp = wetLevel = 0; + } else { + dryAmp = currentSettings.dryAmps[level]; + wetLevel = currentSettings.wetLevels[level]; + } +} + +bool BReverbModel::isActive() const { + for (Bit32u i = 0; i < currentSettings.numberOfAllpasses; i++) { + if (!allpasses[i]->isEmpty()) return true; + } + for (Bit32u i = 0; i < currentSettings.numberOfCombs; i++) { + if (!combs[i]->isEmpty()) return true; + } + return false; +} + +void BReverbModel::process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples) { + Bit32s dry, link, outL1, outR1; + + for (unsigned long i = 0; i < numSamples; i++) { + if (tapDelayMode) { + dry = Bit32s(*inLeft * 8192.0f) + Bit32s(*inRight * 8192.0f); + } else { + dry = Bit32s(*inLeft * 8192.0f) / 2 + Bit32s(*inRight * 8192.0f) / 2; + } + + // Looks like dryAmp doesn't change in MT-32 but it does in CM-32L / LAPC-I + dry = weirdMul(dry, dryAmp, 0xFF); + + if (tapDelayMode) { + TapDelayCombFilter *comb = static_cast<TapDelayCombFilter *> (*combs); + comb->process(dry); + *outLeft = weirdMul(comb->getLeftOutput(), wetLevel, 0xFF) / 8192.0f; + *outRight = weirdMul(comb->getRightOutput(), wetLevel, 0xFF) / 8192.0f; + } else { + // Get the last stored sample before processing in order not to loose it + link = combs[0]->getOutputAt(currentSettings.combSizes[0] - 1); + + // Entrance LPF. Note, comb.process() differs a bit here. + combs[0]->process(dry); + + // This introduces reverb noise which actually makes output from the real Boss chip nondeterministic + link = link - 1; + link = allpasses[0]->process(link); + link = allpasses[1]->process(link); + link = allpasses[2]->process(link); + + // If the output position is equal to the comb size, get it now in order not to loose it + outL1 = combs[1]->getOutputAt(currentSettings.outLPositions[0] - 1); + outL1 += outL1 >> 1; + + combs[1]->process(link); + combs[2]->process(link); + combs[3]->process(link); + + link = combs[2]->getOutputAt(currentSettings.outLPositions[1]); + link += link >> 1; + link += outL1; + link += combs[3]->getOutputAt(currentSettings.outLPositions[2]); + *outLeft = weirdMul(link, wetLevel, 0xFF) / 8192.0f; + + outR1 = combs[1]->getOutputAt(currentSettings.outRPositions[0]); + outR1 += outR1 >> 1; + link = combs[2]->getOutputAt(currentSettings.outRPositions[1]); + link += link >> 1; + link += outR1; + link += combs[3]->getOutputAt(currentSettings.outRPositions[2]); + *outRight = weirdMul(link, wetLevel, 0xFF) / 8192.0f; + } + + inLeft++; + inRight++; + outLeft++; + outRight++; + } +} + +} + +#endif diff --git a/audio/softsynth/mt32/BReverbModel.h b/audio/softsynth/mt32/BReverbModel.h new file mode 100644 index 0000000000..f728d1a99c --- /dev/null +++ b/audio/softsynth/mt32/BReverbModel.h @@ -0,0 +1,112 @@ +/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher + * Copyright (C) 2011, 2012 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef MT32EMU_B_REVERB_MODEL_H +#define MT32EMU_B_REVERB_MODEL_H + +namespace MT32Emu { + +struct BReverbSettings { + const Bit32u numberOfAllpasses; + const Bit32u * const allpassSizes; + const Bit32u numberOfCombs; + const Bit32u * const combSizes; + const Bit32u * const outLPositions; + const Bit32u * const outRPositions; + const Bit32u * const filterFactors; + const Bit32u * const feedbackFactors; + const Bit32u * const dryAmps; + const Bit32u * const wetLevels; + const Bit32u lpfAmp; +}; + +class RingBuffer { +protected: + Bit16s *buffer; + const Bit32u size; + Bit32u index; + +public: + RingBuffer(const Bit32u size); + virtual ~RingBuffer(); + Bit32s next(); + bool isEmpty() const; + void mute(); +}; + +class AllpassFilter : public RingBuffer { +public: + AllpassFilter(const Bit32u size); + Bit32s process(const Bit32s in); +}; + +class CombFilter : public RingBuffer { +protected: + const Bit32u filterFactor; + Bit32u feedbackFactor; + +public: + CombFilter(const Bit32u size, const Bit32u useFilterFactor); + virtual void process(const Bit32s in); // Actually, no need to make it virtual, but for sure + Bit32s getOutputAt(const Bit32u outIndex) const; + void setFeedbackFactor(const Bit32u useFeedbackFactor); +}; + +class DelayWithLowPassFilter : public CombFilter { + Bit32u amp; + +public: + DelayWithLowPassFilter(const Bit32u useSize, const Bit32u useFilterFactor, const Bit32u useAmp); + void process(const Bit32s in); + void setFeedbackFactor(const Bit32u) {} +}; + +class TapDelayCombFilter : public CombFilter { + Bit32u outL; + Bit32u outR; + +public: + TapDelayCombFilter(const Bit32u useSize, const Bit32u useFilterFactor); + void process(const Bit32s in); + Bit32s getLeftOutput() const; + Bit32s getRightOutput() const; + void setOutputPositions(const Bit32u useOutL, const Bit32u useOutR); +}; + +class BReverbModel : public ReverbModel { + AllpassFilter **allpasses; + CombFilter **combs; + + const BReverbSettings ¤tSettings; + const bool tapDelayMode; + Bit32u dryAmp; + Bit32u wetLevel; + void mute(); + +public: + BReverbModel(const ReverbMode mode); + ~BReverbModel(); + void open(); + void close(); + void setParameters(Bit8u time, Bit8u level); + void process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples); + bool isActive() const; +}; + +} + +#endif diff --git a/audio/softsynth/mt32/DelayReverb.cpp b/audio/softsynth/mt32/DelayReverb.cpp index 89eebf0d79..bf925f8419 100644 --- a/audio/softsynth/mt32/DelayReverb.cpp +++ b/audio/softsynth/mt32/DelayReverb.cpp @@ -20,15 +20,14 @@ #include "mt32emu.h" #include "DelayReverb.h" -using namespace MT32Emu; +namespace MT32Emu { - -// CONFIRMED: The values below are found via analysis of digital samples. Checked with all time and level combinations. +// CONFIRMED: The values below are found via analysis of digital samples and tracing reverb RAM address / data lines. Checked with all time and level combinations. // Obviously: // rightDelay = (leftDelay - 2) * 2 + 2 // echoDelay = rightDelay - 1 // Leaving these separate in case it's useful for work on other reverb modes... -const Bit32u REVERB_TIMINGS[8][3]= { +static const Bit32u REVERB_TIMINGS[8][3]= { // {leftDelay, rightDelay, feedbackDelay} {402, 802, 801}, {626, 1250, 1249}, @@ -40,14 +39,16 @@ const Bit32u REVERB_TIMINGS[8][3]= { {8002, 16002, 16001} }; -const float REVERB_FADE[8] = {0.0f, -0.049400051f, -0.08220577f, -0.131861118f, -0.197344907f, -0.262956344f, -0.345162114f, -0.509508615f}; -const float REVERB_FEEDBACK67 = -0.629960524947437f; // = -EXP2F(-2 / 3) -const float REVERB_FEEDBACK = -0.682034520443118f; // = -EXP2F(-53 / 96) -const float LPF_VALUE = 0.594603558f; // = EXP2F(-0.75f) +// Reverb amp is found as dryAmp * wetAmp +static const Bit32u REVERB_AMP[8] = {0x20*0x18, 0x50*0x18, 0x50*0x28, 0x50*0x40, 0x50*0x60, 0x50*0x80, 0x50*0xA8, 0x50*0xF8}; +static const Bit32u REVERB_FEEDBACK67 = 0x60; +static const Bit32u REVERB_FEEDBACK = 0x68; +static const float LPF_VALUE = 0x68 / 256.0f; + +static const Bit32u BUFFER_SIZE = 16384; DelayReverb::DelayReverb() { buf = NULL; - sampleRate = 0; setParameters(0, 0); } @@ -55,27 +56,22 @@ DelayReverb::~DelayReverb() { delete[] buf; } -void DelayReverb::open(unsigned int newSampleRate) { - if (newSampleRate != sampleRate || buf == NULL) { - sampleRate = newSampleRate; - +void DelayReverb::open() { + if (buf == NULL) { delete[] buf; - // If we ever need a speedup, set bufSize to EXP2F(ceil(log2(bufSize))) and use & instead of % to find buf indexes - bufSize = 16384 * sampleRate / 32000; - buf = new float[bufSize]; + buf = new float[BUFFER_SIZE]; recalcParameters(); // mute buffer bufIx = 0; if (buf != NULL) { - for (unsigned int i = 0; i < bufSize; i++) { + for (unsigned int i = 0; i < BUFFER_SIZE; i++) { buf[i] = 0.0f; } } } - // FIXME: IIR filter value depends on sample rate as well } void DelayReverb::close() { @@ -92,59 +88,55 @@ void DelayReverb::setParameters(Bit8u newTime, Bit8u newLevel) { void DelayReverb::recalcParameters() { // Number of samples between impulse and eventual appearance on the left channel - delayLeft = REVERB_TIMINGS[time][0] * sampleRate / 32000; + delayLeft = REVERB_TIMINGS[time][0]; // Number of samples between impulse and eventual appearance on the right channel - delayRight = REVERB_TIMINGS[time][1] * sampleRate / 32000; + delayRight = REVERB_TIMINGS[time][1]; // Number of samples between a response and that response feeding back/echoing - delayFeedback = REVERB_TIMINGS[time][2] * sampleRate / 32000; + delayFeedback = REVERB_TIMINGS[time][2]; - if (time < 6) { - feedback = REVERB_FEEDBACK; + if (level < 3 || time < 6) { + feedback = REVERB_FEEDBACK / 256.0f; } else { - feedback = REVERB_FEEDBACK67; + feedback = REVERB_FEEDBACK67 / 256.0f; } - // Fading speed, i.e. amplitude ratio of neighbor responses - fade = REVERB_FADE[level]; + // Overall output amp + amp = (level == 0 && time == 0) ? 0.0f : REVERB_AMP[level] / 65536.0f; } void DelayReverb::process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples) { - if (buf == NULL) { - return; - } + if (buf == NULL) return; for (unsigned int sampleIx = 0; sampleIx < numSamples; sampleIx++) { // The ring buffer write index moves backwards; reads are all done with positive offsets. - Bit32u bufIxPrev = (bufIx + 1) % bufSize; - Bit32u bufIxLeft = (bufIx + delayLeft) % bufSize; - Bit32u bufIxRight = (bufIx + delayRight) % bufSize; - Bit32u bufIxFeedback = (bufIx + delayFeedback) % bufSize; + Bit32u bufIxPrev = (bufIx + 1) % BUFFER_SIZE; + Bit32u bufIxLeft = (bufIx + delayLeft) % BUFFER_SIZE; + Bit32u bufIxRight = (bufIx + delayRight) % BUFFER_SIZE; + Bit32u bufIxFeedback = (bufIx + delayFeedback) % BUFFER_SIZE; // Attenuated input samples and feedback response are directly added to the current ring buffer location - float sample = fade * (inLeft[sampleIx] + inRight[sampleIx]) + feedback * buf[bufIxFeedback]; + float lpfIn = amp * (inLeft[sampleIx] + inRight[sampleIx]) + feedback * buf[bufIxFeedback]; // Single-pole IIR filter found on real devices - buf[bufIx] = buf[bufIxPrev] + (sample - buf[bufIxPrev]) * LPF_VALUE; + buf[bufIx] = buf[bufIxPrev] * LPF_VALUE - lpfIn; outLeft[sampleIx] = buf[bufIxLeft]; outRight[sampleIx] = buf[bufIxRight]; - bufIx = (bufSize + bufIx - 1) % bufSize; + bufIx = (BUFFER_SIZE + bufIx - 1) % BUFFER_SIZE; } } bool DelayReverb::isActive() const { - // Quick hack: Return true iff all samples in the left buffer are the same and - // all samples in the right buffers are the same (within the sample output threshold). - if (buf == NULL) { - return false; - } - float last = buf[0] * 8192.0f; - for (unsigned int i = 1; i < bufSize; i++) { - float s = (buf[i] * 8192.0f); - if (fabs(s - last) > 1.0f) { - return true; - } + if (buf == NULL) return false; + + float *b = buf; + float max = 0.001f; + for (Bit32u i = 0; i < BUFFER_SIZE; i++) { + if ((*b < -max) || (*b > max)) return true; + b++; } return false; } + +} diff --git a/audio/softsynth/mt32/DelayReverb.h b/audio/softsynth/mt32/DelayReverb.h index 7c030fb839..1abb49f128 100644 --- a/audio/softsynth/mt32/DelayReverb.h +++ b/audio/softsynth/mt32/DelayReverb.h @@ -25,17 +25,14 @@ private: Bit8u time; Bit8u level; - unsigned int sampleRate; - Bit32u bufSize; Bit32u bufIx; - float *buf; Bit32u delayLeft; Bit32u delayRight; Bit32u delayFeedback; - float fade; + float amp; float feedback; void recalcParameters(); @@ -43,7 +40,7 @@ private: public: DelayReverb(); ~DelayReverb(); - void open(unsigned int sampleRate); + void open(); void close(); void setParameters(Bit8u time, Bit8u level); void process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples); diff --git a/audio/softsynth/mt32/FreeverbModel.cpp b/audio/softsynth/mt32/FreeverbModel.cpp index c11fa859d8..2ed302fddc 100644 --- a/audio/softsynth/mt32/FreeverbModel.cpp +++ b/audio/softsynth/mt32/FreeverbModel.cpp @@ -20,7 +20,7 @@ #include "freeverb.h" -using namespace MT32Emu; +namespace MT32Emu { FreeverbModel::FreeverbModel(float useScaleTuning, float useFiltVal, float useWet, Bit8u useRoom, float useDamp) { freeverb = NULL; @@ -35,9 +35,7 @@ FreeverbModel::~FreeverbModel() { delete freeverb; } -void FreeverbModel::open(unsigned int /*sampleRate*/) { - // FIXME: scaleTuning must be multiplied by sample rate to 32000Hz ratio - // IIR filter values depend on sample rate as well +void FreeverbModel::open() { if (freeverb == NULL) { freeverb = new revmodel(scaleTuning); } @@ -76,3 +74,5 @@ bool FreeverbModel::isActive() const { // FIXME: Not bothering to do this properly since we'll be replacing Freeverb soon... return false; } + +} diff --git a/audio/softsynth/mt32/FreeverbModel.h b/audio/softsynth/mt32/FreeverbModel.h index 925b2dbf96..bf1102b14a 100644 --- a/audio/softsynth/mt32/FreeverbModel.h +++ b/audio/softsynth/mt32/FreeverbModel.h @@ -32,7 +32,7 @@ class FreeverbModel : public ReverbModel { public: FreeverbModel(float useScaleTuning, float useFiltVal, float useWet, Bit8u useRoom, float useDamp); ~FreeverbModel(); - void open(unsigned int sampleRate); + void open(); void close(); void setParameters(Bit8u time, Bit8u level); void process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples); diff --git a/audio/softsynth/mt32/LA32Ramp.cpp b/audio/softsynth/mt32/LA32Ramp.cpp index 9f1f01c3c2..4e4d6b4f30 100644 --- a/audio/softsynth/mt32/LA32Ramp.cpp +++ b/audio/softsynth/mt32/LA32Ramp.cpp @@ -79,11 +79,12 @@ LA32Ramp::LA32Ramp() : void LA32Ramp::startRamp(Bit8u target, Bit8u increment) { // CONFIRMED: From sample analysis, this appears to be very accurate. - // FIXME: We could use a table for this in future if (increment == 0) { largeIncrement = 0; } else { - largeIncrement = (unsigned int)(EXP2F(((increment & 0x7F) + 24) / 8.0f) + 0.125f); + // Using integer argument here, no precision loss: + // (unsigned int)(EXP2F(((increment & 0x7F) + 24) / 8.0f) + 0.125f) + largeIncrement = (unsigned int)(EXP2I(((increment & 0x7F) + 24) << 9) + 0.125f); } descending = (increment & 0x80) != 0; if (descending) { diff --git a/audio/softsynth/mt32/Part.cpp b/audio/softsynth/mt32/Part.cpp index c9bd86b54a..0c9e576100 100644 --- a/audio/softsynth/mt32/Part.cpp +++ b/audio/softsynth/mt32/Part.cpp @@ -209,6 +209,7 @@ void RhythmPart::setTimbre(TimbreParam * /*timbre*/) { void Part::setTimbre(TimbreParam *timbre) { *timbreTemp = *timbre; + synth->newTimbreSet(partNum, timbre->common.name); } unsigned int RhythmPart::getAbsTimbreNum() const { @@ -245,7 +246,7 @@ void Part::backupCacheToPartials(PatchCache cache[4]) { // if so then duplicate the cached data from the part to the partial so that // we can change the part's cache without affecting the partial. // We delay this until now to avoid a copy operation with every note played - for (Common::List<Poly *>::iterator polyIt = activePolys.begin(); polyIt != activePolys.end(); polyIt++) { + for (PolyList::iterator polyIt = activePolys.begin(); polyIt != activePolys.end(); polyIt++) { (*polyIt)->backupCacheToPartials(cache); } } @@ -411,7 +412,7 @@ void RhythmPart::noteOn(unsigned int midiKey, unsigned int velocity) { // According to info from Mok, keyShift does not appear to affect anything on rhythm part on LAPC-I, but may do on MT-32 - needs investigation synth->printDebug(" Patch: (timbreGroup %u), (timbreNum %u), (keyShift %u), fineTune %u, benderRange %u, assignMode %u, (reverbSwitch %u)", patchTemp->patch.timbreGroup, patchTemp->patch.timbreNum, patchTemp->patch.keyShift, patchTemp->patch.fineTune, patchTemp->patch.benderRange, patchTemp->patch.assignMode, patchTemp->patch.reverbSwitch); synth->printDebug(" PatchTemp: outputLevel %u, (panpot %u)", patchTemp->outputLevel, patchTemp->panpot); - synth->printDebug(" RhythmTemp: timbre %u, outputLevel %u, panpot %u, reverbSwitch %u", rhythmTemp[drumNum].timbre, rhythmTemp[drumNum].outputLevel, rhythmTemp[drumNum].panpot, rhythmTemp[drumNum].reverbSwitch); + synth->printDebug(" RhythmTemp: timbre %u, outputLevel %u, panpot %u, reverbSwitch %u", rhythmTemp[drumNum].timbre, rhythmTemp[drumNum].outputLevel, rhythmTemp[drumNum].panpot, rhythmTemp[drumNum].reverbSwitch); #endif #endif playPoly(drumCache[drumNum], &rhythmTemp[drumNum], midiKey, key, velocity); @@ -445,7 +446,7 @@ void Part::abortPoly(Poly *poly) { } bool Part::abortFirstPoly(unsigned int key) { - for (Common::List<Poly *>::iterator polyIt = activePolys.begin(); polyIt != activePolys.end(); polyIt++) { + for (PolyList::iterator polyIt = activePolys.begin(); polyIt != activePolys.end(); polyIt++) { Poly *poly = *polyIt; if (poly->getKey() == key) { abortPoly(poly); @@ -456,7 +457,7 @@ bool Part::abortFirstPoly(unsigned int key) { } bool Part::abortFirstPoly(PolyState polyState) { - for (Common::List<Poly *>::iterator polyIt = activePolys.begin(); polyIt != activePolys.end(); polyIt++) { + for (PolyList::iterator polyIt = activePolys.begin(); polyIt != activePolys.end(); polyIt++) { Poly *poly = *polyIt; if (poly->getState() == polyState) { abortPoly(poly); @@ -537,16 +538,21 @@ void Part::playPoly(const PatchCache cache[4], const MemParams::RhythmTemp *rhyt #if MT32EMU_MONITOR_PARTIALS > 1 synth->printPartialUsage(); #endif + synth->partStateChanged(partNum, true); + synth->polyStateChanged(partNum); } void Part::allNotesOff() { // The MIDI specification states - and Mok confirms - that all notes off (0x7B) // should treat the hold pedal as usual. - for (Common::List<Poly *>::iterator polyIt = activePolys.begin(); polyIt != activePolys.end(); polyIt++) { + for (PolyList::iterator polyIt = activePolys.begin(); polyIt != activePolys.end(); polyIt++) { Poly *poly = *polyIt; - // FIXME: This has special handling of key 0 in NoteOff that Mok has not yet confirmed - // applies to AllNotesOff. - poly->noteOff(holdpedal); + // FIXME: This has special handling of key 0 in NoteOff that Mok has not yet confirmed applies to AllNotesOff. + // if (poly->canSustain() || poly->getKey() == 0) { + // FIXME: The real devices are found to be ignoring non-sustaining polys while processing AllNotesOff. Need to be confirmed. + if (poly->canSustain()) { + poly->noteOff(holdpedal); + } } } @@ -554,14 +560,14 @@ void Part::allSoundOff() { // MIDI "All sound off" (0x78) should release notes immediately regardless of the hold pedal. // This controller is not actually implemented by the synths, though (according to the docs and Mok) - // we're only using this method internally. - for (Common::List<Poly *>::iterator polyIt = activePolys.begin(); polyIt != activePolys.end(); polyIt++) { + for (PolyList::iterator polyIt = activePolys.begin(); polyIt != activePolys.end(); polyIt++) { Poly *poly = *polyIt; poly->startDecay(); } } void Part::stopPedalHold() { - for (Common::List<Poly *>::iterator polyIt = activePolys.begin(); polyIt != activePolys.end(); polyIt++) { + for (PolyList::iterator polyIt = activePolys.begin(); polyIt != activePolys.end(); polyIt++) { Poly *poly = *polyIt; poly->stopPedalHold(); } @@ -580,7 +586,7 @@ void Part::stopNote(unsigned int key) { synth->printDebug("%s (%s): stopping key %d", name, currentInstr, key); #endif - for (Common::List<Poly *>::iterator polyIt = activePolys.begin(); polyIt != activePolys.end(); polyIt++) { + for (PolyList::iterator polyIt = activePolys.begin(); polyIt != activePolys.end(); polyIt++) { Poly *poly = *polyIt; // Generally, non-sustaining instruments ignore note off. They die away eventually anyway. // Key 0 (only used by special cases on rhythm part) reacts to note off even if non-sustaining or pedal held. @@ -602,7 +608,7 @@ unsigned int Part::getActivePartialCount() const { unsigned int Part::getActiveNonReleasingPartialCount() const { unsigned int activeNonReleasingPartialCount = 0; - for (Common::List<Poly *>::const_iterator polyIt = activePolys.begin(); polyIt != activePolys.end(); polyIt++) { + for (PolyList::const_iterator polyIt = activePolys.begin(); polyIt != activePolys.end(); polyIt++) { Poly *poly = *polyIt; if (poly->getState() != POLY_Releasing) { activeNonReleasingPartialCount += poly->getActivePartialCount(); @@ -616,6 +622,10 @@ void Part::partialDeactivated(Poly *poly) { if (!poly->isActive()) { activePolys.remove(poly); freePolys.push_front(poly); + synth->polyStateChanged(partNum); + } + if (activePartialCount == 0) { + synth->partStateChanged(partNum, false); } } diff --git a/audio/softsynth/mt32/Part.h b/audio/softsynth/mt32/Part.h index 5c59c6d61f..7ae73a818c 100644 --- a/audio/softsynth/mt32/Part.h +++ b/audio/softsynth/mt32/Part.h @@ -24,6 +24,7 @@ namespace MT32Emu { class PartialManager; class Synth; +typedef Common::List<Poly *> PolyList; class Part { private: @@ -37,8 +38,8 @@ private: unsigned int activePartialCount; PatchCache patchCache[4]; - Common::List<Poly *> freePolys; - Common::List<Poly *> activePolys; + PolyList freePolys; + PolyList activePolys; void setPatch(const PatchParam *patch); unsigned int midiKeyToKey(unsigned int midiKey); diff --git a/audio/softsynth/mt32/Partial.cpp b/audio/softsynth/mt32/Partial.cpp index 03bec560b8..a4d1ab03fa 100644 --- a/audio/softsynth/mt32/Partial.cpp +++ b/audio/softsynth/mt32/Partial.cpp @@ -22,7 +22,7 @@ #include "mt32emu.h" #include "mmath.h" -using namespace MT32Emu; +namespace MT32Emu { #ifdef INACCURATE_SMOOTH_PAN // Mok wanted an option for smoother panning, and we love Mok. @@ -85,6 +85,7 @@ void Partial::deactivate() { pair->pair = NULL; } } + synth->partialStateChanged(this, tva->getPhase(), TVA_PHASE_DEAD); #if MT32EMU_MONITOR_PARTIALS > 2 synth->printDebug("[+%lu] [Partial %d] Deactivated", sampleNum, debugPartialNum); synth->printPartialUsage(sampleNum); @@ -133,6 +134,25 @@ void Partial::startPartial(const Part *part, Poly *usePoly, const PatchCache *us stereoVolume.leftVol = panVal / 7.0f; stereoVolume.rightVol = 1.0f - stereoVolume.leftVol; + // SEMI-CONFIRMED: From sample analysis: + // Found that timbres with 3 or 4 partials (i.e. one using two partial pairs) are mixed in two different ways. + // Either partial pairs are added or subtracted, it depends on how the partial pairs are allocated. + // It seems that partials are grouped into quarters and if the partial pairs are allocated in different quarters the subtraction happens. + // Though, this matters little for the majority of timbres, it becomes crucial for timbres which contain several partials that sound very close. + // In this case that timbre can sound totally different depending of the way it is mixed up. + // Most easily this effect can be displayed with the help of a special timbre consisting of several identical square wave partials (3 or 4). + // Say, it is 3-partial timbre. Just play any two notes simultaneously and the polys very probably are mixed differently. + // Moreover, the partial allocator retains the last partial assignment it did and all the subsequent notes will sound the same as the last released one. + // The situation is better with 4-partial timbres since then a whole quarter is assigned for each poly. However, if a 3-partial timbre broke the normal + // whole-quarter assignment or after some partials got aborted, even 4-partial timbres can be found sounding differently. + // This behaviour is also confirmed with two more special timbres: one with identical sawtooth partials, and one with PCM wave 02. + // For my personal taste, this behaviour rather enriches the sounding and should be emulated. + // Also, the current partial allocator model probably needs to be refined. + if (debugPartialNum & 8) { + stereoVolume.leftVol = -stereoVolume.leftVol; + stereoVolume.rightVol = -stereoVolume.rightVol; + } + if (patchCache->PCMPartial) { pcmNum = patchCache->pcm; if (synth->controlROMMap->pcmCount > 128) { @@ -149,7 +169,7 @@ void Partial::startPartial(const Part *part, Poly *usePoly, const PatchCache *us } // CONFIRMED: pulseWidthVal calculation is based on information from Mok - pulseWidthVal = (poly->getVelocity() - 64) * (patchCache->srcPartial.wg.pulseWidthVeloSensitivity - 7) + synth->tables.pulseWidth100To255[patchCache->srcPartial.wg.pulseWidth]; + pulseWidthVal = (poly->getVelocity() - 64) * (patchCache->srcPartial.wg.pulseWidthVeloSensitivity - 7) + Tables::getInstance().pulseWidth100To255[patchCache->srcPartial.wg.pulseWidth]; if (pulseWidthVal < 0) { pulseWidthVal = 0; } else if (pulseWidthVal > 255) { @@ -175,6 +195,7 @@ float Partial::getPCMSample(unsigned int position) { } unsigned long Partial::generateSamples(float *partialBuf, unsigned long length) { + const Tables &tables = Tables::getInstance(); if (!isActive() || alreadyOutputed) { return 0; } @@ -197,6 +218,9 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length) deactivate(); break; } + + Bit16u pitch = tvp->nextPitch(); + // SEMI-CONFIRMED: From sample analysis: // (1) Tested with a single partial playing PCM wave 77 with pitchCoarse 36 and no keyfollow, velocity follow, etc. // This gives results within +/- 2 at the output (before any DAC bitshifting) @@ -206,10 +230,17 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length) // positive amps, so negative still needs to be explored, as well as lower levels. // // Also still partially unconfirmed is the behaviour when ramping between levels, as well as the timing. + +#if MT32EMU_ACCURATE_WG == 1 float amp = EXP2F((32772 - ampRampVal / 2048) / -2048.0f); + float freq = EXP2F(pitch / 4096.0f - 16.0f) * SAMPLE_RATE; +#else + static const float ampFactor = EXP2F(32772 / -2048.0f); + float amp = EXP2I(ampRampVal >> 10) * ampFactor; - Bit16u pitch = tvp->nextPitch(); - float freq = synth->tables.pitchToFreq[pitch]; + static const float freqFactor = EXP2F(-16.0f) * SAMPLE_RATE; + float freq = EXP2I(pitch) * freqFactor; +#endif if (patchCache->PCMPartial) { // Render PCM waveform @@ -221,12 +252,18 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length) break; } Bit32u pcmAddr = pcmWave->addr; - float positionDelta = freq * 2048.0f / synth->myProp.sampleRate; + float positionDelta = freq * 2048.0f / SAMPLE_RATE; // Linear interpolation float firstSample = synth->pcmROMData[pcmAddr + intPCMPosition]; - float nextSample = getPCMSample(intPCMPosition + 1); - sample = firstSample + (nextSample - firstSample) * (pcmPosition - intPCMPosition); + // We observe that for partial structures with ring modulation the interpolation is not applied to the slave PCM partial. + // It's assumed that the multiplication circuitry intended to perform the interpolation on the slave PCM partial + // is borrowed by the ring modulation circuit (or the LA32 chip has a similar lack of resources assigned to each partial pair). + if (pair == NULL || mixType == 0 || structurePosition == 0) { + sample = firstSample + (getPCMSample(intPCMPosition + 1) - firstSample) * (pcmPosition - intPCMPosition); + } else { + sample = firstSample; + } float newPCMPosition = pcmPosition + positionDelta; if (pcmWave->loop) { @@ -247,8 +284,12 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length) // res corresponds to a value set in an LA32 register Bit8u res = patchCache->srcPartial.tvf.resonance + 1; - // EXP2F(1.0f - (32 - res) / 4.0f); - float resAmp = synth->tables.resAmpMax[res]; + float resAmp; + { + // resAmp = EXP2F(1.0f - (32 - res) / 4.0f); + static const float resAmpFactor = EXP2F(-7); + resAmp = EXP2I(res << 10) * resAmpFactor; + } // The cutoffModifier may not be supposed to be directly added to the cutoff - // it may for example need to be multiplied in some way. @@ -260,7 +301,7 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length) } // Wave length in samples - float waveLen = synth->myProp.sampleRate / freq; + float waveLen = SAMPLE_RATE / freq; // Init cosineLen float cosineLen = 0.5f * waveLen; @@ -268,7 +309,8 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length) #if MT32EMU_ACCURATE_WG == 1 cosineLen *= EXP2F((cutoffVal - 128.0f) / -16.0f); // found from sample analysis #else - cosineLen *= synth->tables.cutoffToCosineLen[Bit32u((cutoffVal - 128.0f) * 8.0f)]; + static const float cosineLenFactor = EXP2F(128.0f / -16.0f); + cosineLen *= EXP2I(Bit32u((256.0f - cutoffVal) * 256.0f)) * cosineLenFactor; #endif } @@ -279,23 +321,26 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length) relWavePos -= waveLen; } + // Ratio of positive segment to wave length float pulseLen = 0.5f; if (pulseWidthVal > 128) { - pulseLen += synth->tables.pulseLenFactor[pulseWidthVal - 128]; + // pulseLen = EXP2F((64 - pulseWidthVal) / 64); + static const float pulseLenFactor = EXP2F(-192 / 64); + pulseLen = EXP2I((256 - pulseWidthVal) << 6) * pulseLenFactor; } pulseLen *= waveLen; - float lLen = pulseLen - cosineLen; + float hLen = pulseLen - cosineLen; // Ignore pulsewidths too high for given freq - if (lLen < 0.0f) { - lLen = 0.0f; + if (hLen < 0.0f) { + hLen = 0.0f; } // Ignore pulsewidths too high for given freq and cutoff - float hLen = waveLen - lLen - 2 * cosineLen; - if (hLen < 0.0f) { - hLen = 0.0f; + float lLen = waveLen - hLen - 2 * cosineLen; + if (lLen < 0.0f) { + lLen = 0.0f; } // Correct resAmp for cutoff in range 50..66 @@ -303,7 +348,7 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length) #if MT32EMU_ACCURATE_WG == 1 resAmp *= sinf(FLOAT_PI * (cutoffVal - 128.0f) / 32.0f); #else - resAmp *= synth->tables.sinf10[Bit32u(64 * (cutoffVal - 128.0f))]; + resAmp *= tables.sinf10[Bit32u(64 * (cutoffVal - 128.0f))]; #endif } @@ -314,7 +359,7 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length) #if MT32EMU_ACCURATE_WG == 1 sample = -cosf(FLOAT_PI * relWavePos / cosineLen); #else - sample = -synth->tables.sinf10[Bit32u(2048.0f * relWavePos / cosineLen) + 1024]; + sample = -tables.sinf10[Bit32u(2048.0f * relWavePos / cosineLen) + 1024]; #endif } else @@ -328,7 +373,7 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length) #if MT32EMU_ACCURATE_WG == 1 sample = cosf(FLOAT_PI * (relWavePos - (cosineLen + hLen)) / cosineLen); #else - sample = synth->tables.sinf10[Bit32u(2048.0f * (relWavePos - (cosineLen + hLen)) / cosineLen) + 1024]; + sample = tables.sinf10[Bit32u(2048.0f * (relWavePos - (cosineLen + hLen)) / cosineLen) + 1024]; #endif } else { @@ -343,7 +388,8 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length) #if MT32EMU_ACCURATE_WG == 1 sample *= EXP2F(-0.125f * (128.0f - cutoffVal)); #else - sample *= synth->tables.cutoffToFilterAmp[Bit32u(cutoffVal * 8.0f)]; + static const float cutoffAttenuationFactor = EXP2F(-0.125f * 128.0f); + sample *= EXP2I(Bit32u(512.0f * cutoffVal)) * cutoffAttenuationFactor; #endif } else { @@ -363,11 +409,17 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length) #if MT32EMU_ACCURATE_WG == 1 resSample *= sinf(FLOAT_PI * relWavePos / cosineLen); #else - resSample *= synth->tables.sinf10[Bit32u(2048.0f * relWavePos / cosineLen) & 4095]; + resSample *= tables.sinf10[Bit32u(2048.0f * relWavePos / cosineLen) & 4095]; #endif // Resonance sine amp - float resAmpFade = EXP2F(-synth->tables.resAmpFadeFactor[res >> 2] * (relWavePos / cosineLen)); // seems to be exact + float resAmpFadeLog2 = -tables.resAmpFadeFactor[res >> 2] * (relWavePos / cosineLen); // seems to be exact +#if MT32EMU_ACCURATE_WG == 1 + float resAmpFade = EXP2F(resAmpFadeLog2); +#else + static const float resAmpFadeFactor = EXP2F(-30.0f); + float resAmpFade = (resAmpFadeLog2 < -30.0f) ? 0.0f : EXP2I(Bit32u((30.0f + resAmpFadeLog2) * 4096.0f)) * resAmpFadeFactor; +#endif // Now relWavePos set negative to the left from center of any cosine relWavePos = wavePos; @@ -388,7 +440,7 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length) #if MT32EMU_ACCURATE_WG == 1 resAmpFade *= 0.5f * (1.0f - cosf(FLOAT_PI * relWavePos / (0.5f * cosineLen))); #else - resAmpFade *= 0.5f * (1.0f + synth->tables.sinf10[Bit32s(2048.0f * relWavePos / (0.5f * cosineLen)) + 3072]); + resAmpFade *= 0.5f * (1.0f + tables.sinf10[Bit32s(2048.0f * relWavePos / (0.5f * cosineLen)) + 3072]); #endif } @@ -400,7 +452,7 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length) #if MT32EMU_ACCURATE_WG == 1 sample *= cosf(FLOAT_2PI * wavePos / waveLen); #else - sample *= synth->tables.sinf10[(Bit32u(4096.0f * wavePos / waveLen) & 4095) + 1024]; + sample *= tables.sinf10[(Bit32u(4096.0f * wavePos / waveLen) & 4095) + 1024]; #endif } @@ -516,10 +568,9 @@ bool Partial::produceOutput(float *leftBuf, float *rightBuf, unsigned long lengt } } if (numGenerated > pairNumGenerated) { - if (mixType == 1) { - mixBuffersRingMix(partialBuf + pairNumGenerated, NULL, numGenerated - pairNumGenerated); - } else { - mixBuffersRing(partialBuf + pairNumGenerated, NULL, numGenerated - pairNumGenerated); + if (mixType == 2) { + numGenerated = pairNumGenerated; + deactivate(); } } } @@ -555,3 +606,5 @@ void Partial::startDecayAll() { tvp->startDecay(); tvf->startDecay(); } + +} diff --git a/audio/softsynth/mt32/Partial.h b/audio/softsynth/mt32/Partial.h index 95218c858c..5e250769ec 100644 --- a/audio/softsynth/mt32/Partial.h +++ b/audio/softsynth/mt32/Partial.h @@ -37,7 +37,7 @@ private: const int debugPartialNum; // Only used for debugging // Number of the sample currently being rendered by generateSamples(), or 0 if no run is in progress // This is only kept available for debugging purposes. - unsigned long sampleNum; + unsigned long sampleNum; int ownerPart; // -1 if unassigned int mixType; diff --git a/audio/softsynth/mt32/PartialManager.cpp b/audio/softsynth/mt32/PartialManager.cpp index 42a3eaa179..f8c2dbcd48 100644 --- a/audio/softsynth/mt32/PartialManager.cpp +++ b/audio/softsynth/mt32/PartialManager.cpp @@ -20,7 +20,7 @@ #include "mt32emu.h" #include "PartialManager.h" -using namespace MT32Emu; +namespace MT32Emu { PartialManager::PartialManager(Synth *useSynth, Part **useParts) { synth = useSynth; @@ -148,7 +148,7 @@ bool PartialManager::abortFirstPolyPreferHeldWhereReserveExceeded(int minPart) { bool PartialManager::freePartials(unsigned int needed, int partNum) { // CONFIRMED: Barring bugs, this matches the real LAPC-I according to information from Mok. - // BUG: There's a bug in the LAPC-I implementation: + // BUG: There's a bug in the LAPC-I implementation: // When allocating for rhythm part, or when allocating for a part that is using fewer partials than it has reserved, // held and playing polys on the rhythm part can potentially be aborted before releasing polys on the rhythm part. // This bug isn't present on MT-32. @@ -248,3 +248,5 @@ const Partial *PartialManager::getPartial(unsigned int partialNum) const { } return partialTable[partialNum]; } + +} diff --git a/audio/softsynth/mt32/Poly.cpp b/audio/softsynth/mt32/Poly.cpp index a2f00db73c..c45391f672 100644 --- a/audio/softsynth/mt32/Poly.cpp +++ b/audio/softsynth/mt32/Poly.cpp @@ -58,6 +58,9 @@ bool Poly::noteOff(bool pedalHeld) { return false; } if (pedalHeld) { + if (state == POLY_Held) { + return false; + } state = POLY_Held; } else { startDecay(); diff --git a/audio/softsynth/mt32/ROMInfo.cpp b/audio/softsynth/mt32/ROMInfo.cpp new file mode 100644 index 0000000000..514bc23496 --- /dev/null +++ b/audio/softsynth/mt32/ROMInfo.cpp @@ -0,0 +1,111 @@ +/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher + * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +//#include <cstring> +#include "ROMInfo.h" + +namespace MT32Emu { + +// Known ROMs +static const ROMInfo CTRL_MT32_V1_04 = {65536, "5a5cb5a77d7d55ee69657c2f870416daed52dea7", ROMInfo::Control, "ctrl_mt32_1_04", "MT-32 Control v1.04", ROMInfo::Full, NULL, NULL}; +static const ROMInfo CTRL_MT32_V1_05 = {65536, "e17a3a6d265bf1fa150312061134293d2b58288c", ROMInfo::Control, "ctrl_mt32_1_05", "MT-32 Control v1.05", ROMInfo::Full, NULL, NULL}; +static const ROMInfo CTRL_MT32_V1_06 = {65536, "a553481f4e2794c10cfe597fef154eef0d8257de", ROMInfo::Control, "ctrl_mt32_1_06", "MT-32 Control v1.06", ROMInfo::Full, NULL, NULL}; +static const ROMInfo CTRL_MT32_V1_07 = {65536, "b083518fffb7f66b03c23b7eb4f868e62dc5a987", ROMInfo::Control, "ctrl_mt32_1_07", "MT-32 Control v1.07", ROMInfo::Full, NULL, NULL}; +static const ROMInfo CTRL_MT32_BLUER = {65536, "7b8c2a5ddb42fd0732e2f22b3340dcf5360edf92", ROMInfo::Control, "ctrl_mt32_bluer", "MT-32 Control BlueRidge", ROMInfo::Full, NULL, NULL}; + +static const ROMInfo CTRL_CM32L_V1_00 = {65536, "73683d585cd6948cc19547942ca0e14a0319456d", ROMInfo::Control, "ctrl_cm32l_1_00", "CM-32L/LAPC-I Control v1.00", ROMInfo::Full, NULL, NULL}; +static const ROMInfo CTRL_CM32L_V1_02 = {65536, "a439fbb390da38cada95a7cbb1d6ca199cd66ef8", ROMInfo::Control, "ctrl_cm32l_1_02", "CM-32L/LAPC-I Control v1.02", ROMInfo::Full, NULL, NULL}; + +static const ROMInfo PCM_MT32 = {524288, "f6b1eebc4b2d200ec6d3d21d51325d5b48c60252", ROMInfo::PCM, "pcm_mt32", "MT-32 PCM ROM", ROMInfo::Full, NULL, NULL}; +static const ROMInfo PCM_CM32L = {1048576, "289cc298ad532b702461bfc738009d9ebe8025ea", ROMInfo::PCM, "pcm_cm32l", "CM-32L/CM-64/LAPC-I PCM ROM", ROMInfo::Full, NULL, NULL}; + +static const ROMInfo * const ROM_INFOS[] = { + &CTRL_MT32_V1_04, + &CTRL_MT32_V1_05, + &CTRL_MT32_V1_06, + &CTRL_MT32_V1_07, + &CTRL_MT32_BLUER, + &CTRL_CM32L_V1_00, + &CTRL_CM32L_V1_02, + &PCM_MT32, + &PCM_CM32L, + NULL}; + +const ROMInfo* ROMInfo::getROMInfo(Common::File *file) { + size_t fileSize = file->size(); + // We haven't added the SHA1 checksum code in ScummVM, as the file size + // suffices for our needs for now. + //const char *fileDigest = file->getSHA1(); + for (int i = 0; ROM_INFOS[i] != NULL; i++) { + const ROMInfo *romInfo = ROM_INFOS[i]; + if (fileSize == romInfo->fileSize /*&& !strcmp(fileDigest, romInfo->sha1Digest)*/) { + return romInfo; + } + } + return NULL; +} + +void ROMInfo::freeROMInfo(const ROMInfo *romInfo) { + (void) romInfo; +} + +static int getROMCount() { + int count; + for(count = 0; ROM_INFOS[count] != NULL; count++) { + } + return count; +} + +const ROMInfo** ROMInfo::getROMInfoList(unsigned int types, unsigned int pairTypes) { + const ROMInfo **romInfoList = new const ROMInfo*[getROMCount() + 1]; + const ROMInfo **currentROMInList = romInfoList; + for(int i = 0; ROM_INFOS[i] != NULL; i++) { + const ROMInfo *romInfo = ROM_INFOS[i]; + if ((types & (1 << romInfo->type)) && (pairTypes & (1 << romInfo->pairType))) { + *currentROMInList++ = romInfo; + } + } + *currentROMInList = NULL; + return romInfoList; +} + +void ROMInfo::freeROMInfoList(const ROMInfo **romInfoList) { + delete[] romInfoList; +} + +const ROMImage* ROMImage::makeROMImage(Common::File *file) { + ROMImage *romImage = new ROMImage; + romImage->file = file; + romImage->romInfo = ROMInfo::getROMInfo(romImage->file); + return romImage; +} + +void ROMImage::freeROMImage(const ROMImage *romImage) { + ROMInfo::freeROMInfo(romImage->romInfo); + delete romImage; +} + + +Common::File* ROMImage::getFile() const { + return file; +} + +const ROMInfo* ROMImage::getROMInfo() const { + return romInfo; +} + +} diff --git a/audio/softsynth/mt32/ROMInfo.h b/audio/softsynth/mt32/ROMInfo.h new file mode 100644 index 0000000000..2ffd2b72c6 --- /dev/null +++ b/audio/softsynth/mt32/ROMInfo.h @@ -0,0 +1,77 @@ +/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher + * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef MT32EMU_ROMINFO_H +#define MT32EMU_ROMINFO_H + +//#include <cstddef> +#include "common/file.h" + +namespace MT32Emu { + +// Defines vital info about ROM file to be used by synth and applications + +struct ROMInfo { +public: + size_t fileSize; + const char *sha1Digest; + enum Type {PCM, Control, Reverb} type; + const char *shortName; + const char *description; + enum PairType {Full, FirstHalf, SecondHalf, Mux0, Mux1} pairType; + ROMInfo *pairROMInfo; + void *controlROMInfo; + + // Returns a ROMInfo struct by inspecting the size and the SHA1 hash + static const ROMInfo* getROMInfo(Common::File *file); + + // Currently no-op + static void freeROMInfo(const ROMInfo *romInfo); + + // Allows retrieving a NULL-terminated list of ROMInfos for a range of types and pairTypes + // (specified by bitmasks) + // Useful for GUI/console app to output information on what ROMs it supports + static const ROMInfo** getROMInfoList(unsigned int types, unsigned int pairTypes); + + // Frees the list of ROMInfos given + static void freeROMInfoList(const ROMInfo **romInfos); +}; + +// Synth::open() is to require a full control ROMImage and a full PCM ROMImage to work + +class ROMImage { +private: + Common::File *file; + const ROMInfo *romInfo; + +public: + + // Creates a ROMImage object given a ROMInfo and a File. Keeps a reference + // to the File and ROMInfo given, which must be freed separately by the user + // after the ROMImage is freed + static const ROMImage* makeROMImage(Common::File *file); + + // Must only be done after all Synths using the ROMImage are deleted + static void freeROMImage(const ROMImage *romImage); + + Common::File *getFile() const; + const ROMInfo *getROMInfo() const; +}; + +} + +#endif diff --git a/audio/softsynth/mt32/Synth.cpp b/audio/softsynth/mt32/Synth.cpp index 0861053b5c..7f4ba44999 100644 --- a/audio/softsynth/mt32/Synth.cpp +++ b/audio/softsynth/mt32/Synth.cpp @@ -27,8 +27,10 @@ #include "mmath.h" #include "PartialManager.h" -#if MT32EMU_USE_AREVERBMODEL == 1 +#if MT32EMU_USE_REVERBMODEL == 1 #include "AReverbModel.h" +#elif MT32EMU_USE_REVERBMODEL == 2 +#include "BReverbModel.h" #else #include "FreeverbModel.h" #endif @@ -36,7 +38,7 @@ namespace MT32Emu { -const ControlROMMap ControlROMMaps[7] = { +static const ControlROMMap ControlROMMaps[7] = { // ID IDc IDbytes PCMmap PCMc tmbrA tmbrAO, tmbrAC tmbrB tmbrBO, tmbrBC tmbrR trC rhythm rhyC rsrv panpot prog rhyMax patMax sysMax timMax {0x4014, 22, "\000 ver1.04 14 July 87 ", 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x73A6, 85, 0x57C7, 0x57E2, 0x57D0, 0x5252, 0x525E, 0x526E, 0x520A}, {0x4014, 22, "\000 ver1.05 06 Aug, 87 ", 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x7414, 85, 0x57C7, 0x57E2, 0x57D0, 0x5252, 0x525E, 0x526E, 0x520A}, @@ -140,22 +142,36 @@ Bit8u Synth::calcSysexChecksum(const Bit8u *data, Bit32u len, Bit8u checksum) { return checksum; } -Synth::Synth() { +Synth::Synth(ReportHandler *useReportHandler) { isOpen = false; reverbEnabled = true; reverbOverridden = false; -#if MT32EMU_USE_AREVERBMODEL == 1 - reverbModels[0] = new AReverbModel(&AReverbModel::REVERB_MODE_0_SETTINGS); - reverbModels[1] = new AReverbModel(&AReverbModel::REVERB_MODE_1_SETTINGS); - reverbModels[2] = new AReverbModel(&AReverbModel::REVERB_MODE_2_SETTINGS); + if (useReportHandler == NULL) { + reportHandler = new ReportHandler; + isDefaultReportHandler = true; + } else { + reportHandler = useReportHandler; + isDefaultReportHandler = false; + } + +#if MT32EMU_USE_REVERBMODEL == 1 + reverbModels[REVERB_MODE_ROOM] = new AReverbModel(REVERB_MODE_ROOM); + reverbModels[REVERB_MODE_HALL] = new AReverbModel(REVERB_MODE_HALL); + reverbModels[REVERB_MODE_PLATE] = new AReverbModel(REVERB_MODE_PLATE); + reverbModels[REVERB_MODE_TAP_DELAY] = new DelayReverb(); +#elif MT32EMU_USE_REVERBMODEL == 2 + reverbModels[REVERB_MODE_ROOM] = new BReverbModel(REVERB_MODE_ROOM); + reverbModels[REVERB_MODE_HALL] = new BReverbModel(REVERB_MODE_HALL); + reverbModels[REVERB_MODE_PLATE] = new BReverbModel(REVERB_MODE_PLATE); + reverbModels[REVERB_MODE_TAP_DELAY] = new BReverbModel(REVERB_MODE_TAP_DELAY); #else - reverbModels[0] = new FreeverbModel(0.76f, 0.687770909f, 0.63f, 0, 0.5f); - reverbModels[1] = new FreeverbModel(2.0f, 0.712025098f, 0.86f, 1, 0.5f); - reverbModels[2] = new FreeverbModel(0.4f, 0.939522749f, 0.38f, 2, 0.05f); + reverbModels[REVERB_MODE_ROOM] = new FreeverbModel(0.76f, 0.687770909f, 0.63f, 0, 0.5f); + reverbModels[REVERB_MODE_HALL] = new FreeverbModel(2.0f, 0.712025098f, 0.86f, 1, 0.5f); + reverbModels[REVERB_MODE_PLATE] = new FreeverbModel(0.4f, 0.939522749f, 0.38f, 2, 0.05f); + reverbModels[REVERB_MODE_TAP_DELAY] = new DelayReverb(); #endif - reverbModels[3] = new DelayReverb(); reverbModel = NULL; setDACInputMode(DACInputMode_NICE); setOutputGain(1.0f); @@ -170,31 +186,49 @@ Synth::~Synth() { for (int i = 0; i < 4; i++) { delete reverbModels[i]; } + if (isDefaultReportHandler) { + delete reportHandler; + } +} + +void ReportHandler::showLCDMessage(const char *data) { + printf("WRITE-LCD: %s", data); + printf("\n"); +} + +void ReportHandler::printDebug(const char *fmt, va_list list) { + vprintf(fmt, list); + printf("\n"); } -int Synth::report(ReportType type, const void *data) { - if (myProp.report != NULL) { - return myProp.report(myProp.userData, type, data); +void Synth::partStateChanged(int partNum, bool isPartActive) { + reportHandler->onPartStateChanged(partNum, isPartActive); +} + +void Synth::polyStateChanged(int partNum) { + reportHandler->onPolyStateChanged(partNum); +} + +void Synth::partialStateChanged(const Partial * const partial, int oldPartialPhase, int newPartialPhase) { + for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) { + if (getPartial(i) == partial) { + reportHandler->onPartialStateChanged(i, oldPartialPhase, newPartialPhase); + break; + } } - return 0; } -unsigned int Synth::getSampleRate() const { - return myProp.sampleRate; +void Synth::newTimbreSet(int partNum, char patchName[]) { + reportHandler->onProgramChanged(partNum, patchName); } void Synth::printDebug(const char *fmt, ...) { va_list ap; va_start(ap, fmt); - if (myProp.printDebug != NULL) { - myProp.printDebug(myProp.userData, fmt, ap); - } else { #if MT32EMU_DEBUG_SAMPLESTAMPS > 0 - printf("[%u] ", renderedSampleCount); + reportHandler->printDebug("[%u] ", renderedSampleCount); #endif - vprintf(fmt, ap); - printf("\n"); - } + reportHandler->printDebug(fmt, ap); va_end(ap); } @@ -244,80 +278,60 @@ void Synth::setReverbOutputGain(float newReverbOutputGain) { reverbOutputGain = newReverbOutputGain; } -Common::File *Synth::openFile(const char *filename) { - if (myProp.openFile != NULL) { - return myProp.openFile(myProp.userData, filename); - } - char pathBuf[2048]; - if (myProp.baseDir != NULL) { - strcpy(&pathBuf[0], myProp.baseDir); - strcat(&pathBuf[0], filename); - filename = pathBuf; - } - Common::File *file = new Common::File(); - if (!file->open(filename)) { - delete file; - return NULL; - } - return file; -} - -void Synth::closeFile(Common::File *file) { - if (myProp.closeFile != NULL) { - myProp.closeFile(myProp.userData, file); - } else { - file->close(); - delete file; - } -} - -LoadResult Synth::loadControlROM(const char *filename) { - Common::File *file = openFile(filename); // ROM File - if (file == NULL) { - return LoadResult_NotFound; - } - size_t fileSize = file->size(); - if (fileSize != CONTROL_ROM_SIZE) { - printDebug("Control ROM file %s size mismatch: %i", filename, fileSize); +bool Synth::loadControlROM(const ROMImage &controlROMImage) { + if (&controlROMImage == NULL) return false; + Common::File *file = controlROMImage.getFile(); + const ROMInfo *controlROMInfo = controlROMImage.getROMInfo(); + if ((controlROMInfo == NULL) + || (controlROMInfo->type != ROMInfo::Control) + || (controlROMInfo->pairType != ROMInfo::Full)) { + return false; } +#if MT32EMU_MONITOR_INIT + printDebug("Found Control ROM: %s, %s", controlROMInfo->shortName, controlROMInfo->description); +#endif file->read(controlROMData, CONTROL_ROM_SIZE); - if (file->err()) { - closeFile(file); - return LoadResult_Unreadable; - } - closeFile(file); // Control ROM successfully loaded, now check whether it's a known type controlROMMap = NULL; for (unsigned int i = 0; i < sizeof(ControlROMMaps) / sizeof(ControlROMMaps[0]); i++) { if (memcmp(&controlROMData[ControlROMMaps[i].idPos], ControlROMMaps[i].idBytes, ControlROMMaps[i].idLen) == 0) { controlROMMap = &ControlROMMaps[i]; - return LoadResult_OK; + return true; } } - printDebug("%s does not match a known control ROM type", filename); - return LoadResult_Invalid; +#if MT32EMU_MONITOR_INIT + printDebug("Control ROM failed to load"); +#endif + return false; } -LoadResult Synth::loadPCMROM(const char *filename) { - Common::File *file = openFile(filename); // ROM File - if (file == NULL) { - return LoadResult_NotFound; +bool Synth::loadPCMROM(const ROMImage &pcmROMImage) { + if (&pcmROMImage == NULL) return false; + Common::File *file = pcmROMImage.getFile(); + const ROMInfo *pcmROMInfo = pcmROMImage.getROMInfo(); + if ((pcmROMInfo == NULL) + || (pcmROMInfo->type != ROMInfo::PCM) + || (pcmROMInfo->pairType != ROMInfo::Full)) { + return false; } +#if MT32EMU_MONITOR_INIT + printDebug("Found PCM ROM: %s, %s", pcmROMInfo->shortName, pcmROMInfo->description); +#endif size_t fileSize = file->size(); - if (fileSize < (size_t)(2 * pcmROMSize)) { - printDebug("PCM ROM file is too short (expected %d, got %d)", 2 * pcmROMSize, fileSize); - closeFile(file); - return LoadResult_Invalid; - } - if (file->err()) { - closeFile(file); - return LoadResult_Unreadable; + if (fileSize != (2 * pcmROMSize)) { +#if MT32EMU_MONITOR_INIT + printDebug("PCM ROM file has wrong size (expected %d, got %d)", 2 * pcmROMSize, fileSize); +#endif + return false; } - LoadResult rc = LoadResult_OK; - for (int i = 0; i < pcmROMSize; i++) { - Bit8u s = file->readByte(); - Bit8u c = file->readByte(); + + byte *buffer = new byte[file->size()]; + file->read(buffer, file->size()); + const byte *fileData = buffer; + for (size_t i = 0; i < pcmROMSize; i++) { + Bit8u s = *(fileData++); + Bit8u c = *(fileData++); int order[16] = {0, 9, 1, 2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15, 8}; @@ -343,16 +357,18 @@ LoadResult Synth::loadPCMROM(const char *filename) { pcmROMData[i] = lin; } - closeFile(file); - return rc; + + delete[] buffer; + + return true; } bool Synth::initPCMList(Bit16u mapAddress, Bit16u count) { ControlROMPCMStruct *tps = (ControlROMPCMStruct *)&controlROMData[mapAddress]; for (int i = 0; i < count; i++) { - int rAddr = tps[i].pos * 0x800; - int rLenExp = (tps[i].len & 0x70) >> 4; - int rLen = 0x800 << rLenExp; + size_t rAddr = tps[i].pos * 0x800; + size_t rLenExp = (tps[i].len & 0x70) >> 4; + size_t rLen = 0x800 << rLenExp; if (rAddr + rLen > pcmROMSize) { printDebug("Control ROM error: Wave map entry %d points to invalid PCM address 0x%04X, length 0x%04X", i, rAddr, rLen); return false; @@ -414,26 +430,19 @@ bool Synth::initTimbres(Bit16u mapAddress, Bit16u offset, int count, int startTi return true; } -bool Synth::open(SynthProperties &useProp) { +bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage) { if (isOpen) { return false; } prerenderReadIx = prerenderWriteIx = 0; - myProp = useProp; #if MT32EMU_MONITOR_INIT printDebug("Initialising Constant Tables"); #endif - tables.init(); #if !MT32EMU_REDUCE_REVERB_MEMORY for (int i = 0; i < 4; i++) { reverbModels[i]->open(useProp.sampleRate); } #endif - if (useProp.baseDir != NULL) { - char *baseDirCopy = new char[strlen(useProp.baseDir) + 1]; - strcpy(baseDirCopy, useProp.baseDir); - myProp.baseDir = baseDirCopy; - } // This is to help detect bugs memset(&mt32ram, '?', sizeof(mt32ram)); @@ -441,12 +450,10 @@ bool Synth::open(SynthProperties &useProp) { #if MT32EMU_MONITOR_INIT printDebug("Loading Control ROM"); #endif - if (loadControlROM("CM32L_CONTROL.ROM") != LoadResult_OK) { - if (loadControlROM("MT32_CONTROL.ROM") != LoadResult_OK) { - printDebug("Init Error - Missing or invalid MT32_CONTROL.ROM"); - //report(ReportType_errorControlROM, &errno); - return false; - } + if (!loadControlROM(controlROMImage)) { + printDebug("Init Error - Missing or invalid Control ROM image"); + reportHandler->onErrorControlROM(); + return false; } initMemoryRegions(); @@ -460,12 +467,10 @@ bool Synth::open(SynthProperties &useProp) { #if MT32EMU_MONITOR_INIT printDebug("Loading PCM ROM"); #endif - if (loadPCMROM("CM32L_PCM.ROM") != LoadResult_OK) { - if (loadPCMROM("MT32_PCM.ROM") != LoadResult_OK) { - printDebug("Init Error - Missing MT32_PCM.ROM"); - //report(ReportType_errorPCMROM, &errno); - return false; - } + if (!loadPCMROM(pcmROMImage)) { + printDebug("Init Error - Missing PCM ROM image"); + reportHandler->onErrorPCMROM(); + return false; } #if MT32EMU_MONITOR_INIT @@ -594,9 +599,6 @@ void Synth::close() { parts[i] = NULL; } - delete[] myProp.baseDir; - myProp.baseDir = NULL; - delete[] pcmWaves; delete[] pcmROMData; @@ -627,6 +629,11 @@ void Synth::playMsg(Bit32u msg) { return; } playMsgOnPart(part, code, note, velocity); + + // This ensures minimum 1-sample delay between sequential MIDI events + // Without this, a sequence of NoteOn and immediately succeeding NoteOff messages is always silent + // Technically, it's also impossible to send events through the MIDI interface faster than about each millisecond + prerender(); } void Synth::playMsgOnPart(unsigned char part, unsigned char code, unsigned char note, unsigned char velocity) { @@ -1156,7 +1163,7 @@ void Synth::writeMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u le DT(partial[x].tva.envLevel[0]); \ DT(partial[x].tva.envLevel[1]); \ DT(partial[x].tva.envLevel[2]); \ - DT(partial[x].tva.envLevel[3]); + DT(partial[x].tva.envLevel[3]); DTP(0); DTP(1); @@ -1178,7 +1185,7 @@ void Synth::writeMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u le case MR_System: region->write(0, off, data, len); - report(ReportType_devReconfig, NULL); + reportHandler->onDeviceReconfig(); // FIXME: We haven't properly confirmed any of this behaviour // In particular, we tend to reset things such as reverb even if the write contained // the same parameters as were already set, which may be wrong. @@ -1216,7 +1223,7 @@ void Synth::writeMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u le #if MT32EMU_MONITOR_SYSEX > 0 printDebug("WRITE-LCD: %s", buf); #endif - report(ReportType_lcdMessage, buf); + reportHandler->showLCDMessage(buf); break; case MR_Reset: reset(); @@ -1244,9 +1251,9 @@ void Synth::refreshSystemReverbParameters() { #endif return; } - report(ReportType_newReverbMode, &mt32ram.system.reverbMode); - report(ReportType_newReverbTime, &mt32ram.system.reverbTime); - report(ReportType_newReverbLevel, &mt32ram.system.reverbLevel); + reportHandler->onNewReverbMode(mt32ram.system.reverbMode); + reportHandler->onNewReverbTime(mt32ram.system.reverbTime); + reportHandler->onNewReverbLevel(mt32ram.system.reverbLevel); ReverbModel *newReverbModel = reverbModels[mt32ram.system.reverbMode]; #if MT32EMU_REDUCE_REVERB_MEMORY @@ -1254,7 +1261,7 @@ void Synth::refreshSystemReverbParameters() { if (reverbModel != NULL) { reverbModel->close(); } - newReverbModel->open(myProp.sampleRate); + newReverbModel->open(); } #endif reverbModel = newReverbModel; @@ -1309,7 +1316,7 @@ void Synth::reset() { #if MT32EMU_MONITOR_SYSEX > 0 printDebug("RESET"); #endif - report(ReportType_devReset, NULL); + reportHandler->onDeviceReset(); partialManager->deactivateAll(); mt32ram = mt32default; for (int i = 0; i < 9; i++) { diff --git a/audio/softsynth/mt32/Synth.h b/audio/softsynth/mt32/Synth.h index ccabce7282..91375c0fc0 100644 --- a/audio/softsynth/mt32/Synth.h +++ b/audio/softsynth/mt32/Synth.h @@ -26,6 +26,7 @@ class TableInitialiser; class Partial; class PartialManager; class Part; +class ROMImage; /** * Methods for emulating the connection between the LA32 and the DAC, which involves @@ -57,71 +58,6 @@ enum DACInputMode { DACInputMode_GENERATION2 }; -enum ReportType { - // Errors - ReportType_errorControlROM = 1, - ReportType_errorPCMROM, - ReportType_errorSampleRate, - - // Progress - ReportType_progressInit, - - // HW spec - ReportType_availableSSE, - ReportType_available3DNow, - ReportType_usingSSE, - ReportType_using3DNow, - - // General info - ReportType_lcdMessage, - ReportType_devReset, - ReportType_devReconfig, - ReportType_newReverbMode, - ReportType_newReverbTime, - ReportType_newReverbLevel -}; - -enum LoadResult { - LoadResult_OK, - LoadResult_NotFound, - LoadResult_Unreadable, - LoadResult_Invalid -}; - -struct SynthProperties { - // Sample rate to use in mixing - unsigned int sampleRate; - - // Deprecated - ignored. Use Synth::setReverbEnabled() instead. - bool useReverb; - // Deprecated - ignored. Use Synth::setReverbOverridden() instead. - bool useDefaultReverb; - // Deprecated - ignored. Use Synth::playSysex*() to configure reverb instead. - unsigned char reverbType; - // Deprecated - ignored. Use Synth::playSysex*() to configure reverb instead. - unsigned char reverbTime; - // Deprecated - ignored. Use Synth::playSysex*() to configure reverb instead. - unsigned char reverbLevel; - // The name of the directory in which the ROM and data files are stored (with trailing slash/backslash) - // Not used if "openFile" is set. May be NULL in any case. - const char *baseDir; - // This is used as the first argument to all callbacks - void *userData; - // Callback for reporting various errors and information. May be NULL - int (*report)(void *userData, ReportType type, const void *reportData); - // Callback for debug messages, in vprintf() format - void (*printDebug)(void *userData, const char *fmt, va_list list); - // Callback for providing an implementation of File, opened and ready for use - // May be NULL, in which case a default implementation will be used. - Common::File *(*openFile)(void *userData, const char *filename); - // Callback for closing a File. May be NULL, in which case the File will automatically be close()d/deleted. - void (*closeFile)(void *userData, Common::File *file); -}; - -// This is the specification of the Callback routine used when calling the RecalcWaveforms -// function -typedef void (*recalcStatusCallback)(int percDone); - typedef void (*FloatToBit16sFunc)(Bit16s *target, const float *source, Bit32u len, float outputGain); const Bit8u SYSEX_MANUFACTURER_ROLAND = 0x41; @@ -179,6 +115,13 @@ enum MemoryRegionType { MR_PatchTemp, MR_RhythmTemp, MR_TimbreTemp, MR_Patches, MR_Timbres, MR_System, MR_Display, MR_Reset }; +enum ReverbMode { + REVERB_MODE_ROOM, + REVERB_MODE_HALL, + REVERB_MODE_PLATE, + REVERB_MODE_TAP_DELAY +}; + class MemoryRegion { private: Synth *synth; @@ -278,7 +221,7 @@ class ReverbModel { public: virtual ~ReverbModel() {} // After construction or a close(), open() will be called at least once before any other call (with the exception of close()). - virtual void open(unsigned int sampleRate) = 0; + virtual void open() = 0; // May be called multiple times without an open() in between. virtual void close() = 0; virtual void setParameters(Bit8u time, Bit8u level) = 0; @@ -286,6 +229,32 @@ public: virtual bool isActive() const = 0; }; +class ReportHandler { +friend class Synth; + +public: + virtual ~ReportHandler() {} + +protected: + + // Callback for debug messages, in vprintf() format + virtual void printDebug(const char *fmt, va_list list); + + // Callbacks for reporting various errors and information + virtual void onErrorControlROM() {} + virtual void onErrorPCMROM() {} + virtual void showLCDMessage(const char *message); + virtual void onDeviceReset() {} + virtual void onDeviceReconfig() {} + virtual void onNewReverbMode(Bit8u /* mode */) {} + virtual void onNewReverbTime(Bit8u /* time */) {} + virtual void onNewReverbLevel(Bit8u /* level */) {} + virtual void onPartStateChanged(int /* partNum */, bool /* isActive */) {} + virtual void onPolyStateChanged(int /* partNum */) {} + virtual void onPartialStateChanged(int /* partialNum */, int /* oldPartialPhase */, int /* newPartialPhase */) {} + virtual void onProgramChanged(int /* partNum */, char * /* patchName */) {} +}; + class Synth { friend class Part; friend class RhythmPart; @@ -315,13 +284,12 @@ private: const ControlROMMap *controlROMMap; Bit8u controlROMData[CONTROL_ROM_SIZE]; float *pcmROMData; - int pcmROMSize; // This is in 16-bit samples, therefore half the number of bytes in the ROM + size_t pcmROMSize; // This is in 16-bit samples, therefore half the number of bytes in the ROM Bit8s chantable[32]; Bit32u renderedSampleCount; - Tables tables; MemParams mt32ram, mt32default; @@ -337,6 +305,9 @@ private: bool isOpen; + bool isDefaultReportHandler; + ReportHandler *reportHandler; + PartialManager *partialManager; Part *parts[9]; @@ -369,8 +340,6 @@ private: int prerenderReadIx; int prerenderWriteIx; - SynthProperties myProp; - bool prerender(); void copyPrerender(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u pos, Bit32u len); void checkPrerender(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u &pos, Bit32u &len); @@ -384,8 +353,8 @@ private: void writeMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u len, const Bit8u *data); void readMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u len, Bit8u *data); - LoadResult loadControlROM(const char *filename); - LoadResult loadPCMROM(const char *filename); + bool loadControlROM(const ROMImage &controlROMImage); + bool loadPCMROM(const ROMImage &pcmROMImage); bool initPCMList(Bit16u mapAddress, Bit16u count); bool initTimbres(Bit16u mapAddress, Bit16u offset, int timbreCount, int startTimbre, bool compressed); @@ -399,24 +368,25 @@ private: void refreshSystem(); void reset(); - unsigned int getSampleRate() const; - void printPartialUsage(unsigned long sampleOffset = 0); -protected: - int report(ReportType type, const void *reportData); - Common::File *openFile(const char *filename); - void closeFile(Common::File *file); + + void partStateChanged(int partNum, bool isPartActive); + void polyStateChanged(int partNum); + void partialStateChanged(const Partial * const partial, int oldPartialPhase, int newPartialPhase); + void newTimbreSet(int partNum, char patchName[]); void printDebug(const char *fmt, ...); public: static Bit8u calcSysexChecksum(const Bit8u *data, Bit32u len, Bit8u checksum); - Synth(); + // Optionally sets callbacks for reporting various errors, information and debug messages + Synth(ReportHandler *useReportHandler = NULL); ~Synth(); // Used to initialise the MT-32. Must be called before any other function. // Returns true if initialization was sucessful, otherwise returns false. - bool open(SynthProperties &useProp); + // controlROMImage and pcmROMImage represent Control and PCM ROM images for use by synth. + bool open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage); // Closes the MT-32 and deallocates any memory used by the synthesizer void close(void); diff --git a/audio/softsynth/mt32/TVA.cpp b/audio/softsynth/mt32/TVA.cpp index c3be6db591..19a01cfc73 100644 --- a/audio/softsynth/mt32/TVA.cpp +++ b/audio/softsynth/mt32/TVA.cpp @@ -30,10 +30,13 @@ namespace MT32Emu { static Bit8u biasLevelToAmpSubtractionCoeff[13] = {255, 187, 137, 100, 74, 54, 40, 29, 21, 15, 10, 5, 0}; TVA::TVA(const Partial *usePartial, LA32Ramp *useAmpRamp) : - partial(usePartial), ampRamp(useAmpRamp), system_(&usePartial->getSynth()->mt32ram.system) { + partial(usePartial), ampRamp(useAmpRamp), system_(&usePartial->getSynth()->mt32ram.system), phase(TVA_PHASE_DEAD) { } void TVA::startRamp(Bit8u newTarget, Bit8u newIncrement, int newPhase) { + if (newPhase != phase) { + partial->getSynth()->partialStateChanged(partial, phase, newPhase); + } target = newTarget; phase = newPhase; ampRamp->startRamp(newTarget, newIncrement); @@ -43,6 +46,9 @@ void TVA::startRamp(Bit8u newTarget, Bit8u newIncrement, int newPhase) { } void TVA::end(int newPhase) { + if (newPhase != phase) { + partial->getSynth()->partialStateChanged(partial, phase, newPhase); + } phase = newPhase; playing = false; #if MT32EMU_MONITOR_TVA >= 1 @@ -154,7 +160,7 @@ void TVA::reset(const Part *newPart, const TimbreParam::PartialParam *newPartial playing = true; - Tables *tables = &partial->getSynth()->tables; + const Tables *tables = &Tables::getInstance(); int key = partial->getPoly()->getKey(); int velocity = partial->getPoly()->getVelocity(); @@ -215,7 +221,7 @@ void TVA::recalcSustain() { return; } // We're sustaining. Recalculate all the values - Tables *tables = &partial->getSynth()->tables; + const Tables *tables = &Tables::getInstance(); int newTarget = calcBasicAmp(tables, partial, system_, partialParam, patchTemp, rhythmTemp, biasAmpSubtraction, veloAmpSubtraction, part->getExpression()); newTarget += partialParam->tva.envLevel[3]; // Since we're in TVA_PHASE_SUSTAIN at this point, we know that target has been reached and an interrupt fired, so we can rely on it being the current amp. @@ -241,7 +247,7 @@ int TVA::getPhase() const { } void TVA::nextPhase() { - Tables *tables = &partial->getSynth()->tables; + const Tables *tables = &Tables::getInstance(); if (phase >= TVA_PHASE_DEAD || !playing) { partial->getSynth()->printDebug("TVA::nextPhase(): Shouldn't have got here with phase %d, playing=%s", phase, playing ? "true" : "false"); @@ -274,7 +280,7 @@ void TVA::nextPhase() { } int newTarget; - int newIncrement; + int newIncrement = 0; // Initialised to please compilers int envPointIndex = phase; if (!allLevelsZeroFromNowOn) { diff --git a/audio/softsynth/mt32/TVF.cpp b/audio/softsynth/mt32/TVF.cpp index 58f72e5a9b..47c4917632 100644 --- a/audio/softsynth/mt32/TVF.cpp +++ b/audio/softsynth/mt32/TVF.cpp @@ -64,11 +64,11 @@ static int calcBaseCutoff(const TimbreParam::PartialParam *partialParam, Bit32u int biasPoint = partialParam->tvf.biasPoint; if ((biasPoint & 0x40) == 0) { // biasPoint range here: 0 to 63 - int bias = biasPoint + 33 - key; // bias range here: -75 to 84 + int bias = biasPoint + 33 - key; // bias range here: -75 to 84 if (bias > 0) { bias = -bias; // bias range here: -1 to -84 baseCutoff += bias * biasLevelToBiasMult[partialParam->tvf.biasLevel]; // Calculation range: -7140 to 7140 - // baseCutoff range now: -10164 to 10164 + // baseCutoff range now: -10164 to 10164 } } else { // biasPoint range here: 64 to 127 @@ -117,7 +117,7 @@ void TVF::reset(const TimbreParam::PartialParam *newPartialParam, unsigned int b unsigned int key = partial->getPoly()->getKey(); unsigned int velocity = partial->getPoly()->getVelocity(); - Tables *tables = &partial->getSynth()->tables; + const Tables *tables = &Tables::getInstance(); baseCutoff = calcBaseCutoff(newPartialParam, basePitch, key); #if MT32EMU_MONITOR_TVF >= 1 @@ -179,7 +179,7 @@ void TVF::startDecay() { } void TVF::nextPhase() { - Tables *tables = &partial->getSynth()->tables; + const Tables *tables = &Tables::getInstance(); int newPhase = phase + 1; switch (newPhase) { diff --git a/audio/softsynth/mt32/TVP.cpp b/audio/softsynth/mt32/TVP.cpp index 0b339e8d71..5dc4ca6b66 100644 --- a/audio/softsynth/mt32/TVP.cpp +++ b/audio/softsynth/mt32/TVP.cpp @@ -47,12 +47,11 @@ static Bit16u keyToPitchTable[] = { TVP::TVP(const Partial *usePartial) : partial(usePartial), system_(&usePartial->getSynth()->mt32ram.system) { - unsigned int sampleRate = usePartial->getSynth()->myProp.sampleRate; // We want to do processing 4000 times per second. FIXME: This is pretty arbitrary. - maxCounter = sampleRate / 4000; + maxCounter = SAMPLE_RATE / 4000; // The timer runs at 500kHz. We only need to bother updating it every maxCounter samples, before we do processing. // This is how much to increment it by every maxCounter samples. - processTimerIncrement = 500000 * maxCounter / sampleRate; + processTimerIncrement = 500000 * maxCounter / SAMPLE_RATE; } static Bit16s keyToPitch(unsigned int key) { @@ -171,9 +170,14 @@ void TVP::updatePitch() { if (newPitch < 0) { newPitch = 0; } + +// Note: Temporary #ifdef until we have proper "quirk" configuration +// This is about right emulation of MT-32 GEN0 quirk exploited in Colonel's Bequest timbre "Lightning" +#ifndef MT32EMU_QUIRK_PITCH_ENVELOPE_OVERFLOW_MT32 if (newPitch > 59392) { newPitch = 59392; } +#endif pitch = (Bit16u)newPitch; // FIXME: We're doing this here because that's what the CM-32L does - we should probably move this somewhere more appropriate in future. diff --git a/audio/softsynth/mt32/Tables.cpp b/audio/softsynth/mt32/Tables.cpp index c9bd40b7a4..5353a74079 100644 --- a/audio/softsynth/mt32/Tables.cpp +++ b/audio/softsynth/mt32/Tables.cpp @@ -22,18 +22,14 @@ #include "mt32emu.h" #include "mmath.h" -using namespace MT32Emu; +namespace MT32Emu { -Tables::Tables() { - initialised = false; +const Tables &Tables::getInstance() { + static const Tables instance; + return instance; } -void Tables::init() { - if (initialised) { - return; - } - initialised = true; - +Tables::Tables() { int lf; for (lf = 0; lf <= 100; lf++) { // CONFIRMED:KG: This matches a ROM table found by Mok @@ -76,31 +72,9 @@ void Tables::init() { //synth->printDebug("%d: %d", i, pulseWidth100To255[i]); } - // Ratio of negative segment to wave length - for (int i = 0; i < 128; i++) { - // Formula determined from sample analysis. - float pt = 0.5f / 127.0f * i; - pulseLenFactor[i] = (1.241857812f - pt) * pt; // seems to be 2 ^ (5 / 16) = 1.241857812f - } - - for (int i = 0; i < 65536; i++) { - // Aka (slightly slower): EXP2F(pitchVal / 4096.0f - 16.0f) * 32000.0f - pitchToFreq[i] = EXP2F(i / 4096.0f - 1.034215715f); - } - - // found from sample analysis - for (int i = 0; i < 1024; i++) { - cutoffToCosineLen[i] = EXP2F(i / -128.0f); - } - - // found from sample analysis - for (int i = 0; i < 1024; i++) { - cutoffToFilterAmp[i] = EXP2F(-0.125f * (128.0f - i / 8.0f)); - } - - // found from sample analysis - for (int i = 0; i < 32; i++) { - resAmpMax[i] = EXP2F(1.0f - (32 - i) / 4.0f); + // The LA32 chip presumably has such a table inside as the internal computaions seem to be performed using fixed point math with 12-bit fractions + for (int i = 0; i < 4096; i++) { + exp2[i] = EXP2F(i / 4096.0f); } // found from sample analysis @@ -117,3 +91,5 @@ void Tables::init() { sinf10[i] = sin(FLOAT_PI * i / 2048.0f); } } + +} diff --git a/audio/softsynth/mt32/Tables.h b/audio/softsynth/mt32/Tables.h index a2b5ff5d56..c3e80e7e9b 100644 --- a/audio/softsynth/mt32/Tables.h +++ b/audio/softsynth/mt32/Tables.h @@ -20,14 +20,23 @@ namespace MT32Emu { +// Sample rate to use in mixing. With the progress of development, we've found way too many thing dependent. +// In order to achieve further advance in emulation accuracy, sample rate made fixed throughout the emulator. +// The output from the synth is supposed to be resampled to convert the sample rate. +const unsigned int SAMPLE_RATE = 32000; + const int MIDDLEC = 60; class Synth; class Tables { - bool initialised; +private: + Tables(); + Tables(Tables &); public: + static const Tables &getInstance(); + // Constant LUTs // CONFIRMED: This is used to convert several parameters to amp-modifying values in the TVA envelope: @@ -47,16 +56,9 @@ public: // CONFIRMED: Bit8u pulseWidth100To255[101]; - float pulseLenFactor[128]; - float pitchToFreq[65536]; - float cutoffToCosineLen[1024]; - float cutoffToFilterAmp[1024]; - float resAmpMax[32]; + float exp2[4096]; float resAmpFadeFactor[8]; float sinf10[5120]; - - Tables(); - void init(); }; } diff --git a/audio/softsynth/mt32/freeverb.cpp b/audio/softsynth/mt32/freeverb.cpp index de8f2632cb..181b878596 100644 --- a/audio/softsynth/mt32/freeverb.cpp +++ b/audio/softsynth/mt32/freeverb.cpp @@ -202,7 +202,7 @@ void revmodel::process(const float *inputL, const float *inputR, float *outputL, // Calculate output REPLACING anything already there *outputL = outL*wet1 + outR*wet2; *outputR = outR*wet1 + outL*wet2; - + inputL++; inputR++; outputL++; diff --git a/audio/softsynth/mt32/mmath.h b/audio/softsynth/mt32/mmath.h index 226d73e27e..25c79d57a9 100644 --- a/audio/softsynth/mt32/mmath.h +++ b/audio/softsynth/mt32/mmath.h @@ -52,6 +52,10 @@ static inline float EXP2F(float x) { #endif } +static inline float EXP2I(unsigned int i) { + return float(1 << (i >> 12)) * Tables::getInstance().exp2[i & 0x0FFF]; +} + static inline float EXP10F(float x) { return exp(FLOAT_LN_10 * x); } diff --git a/audio/softsynth/mt32/module.mk b/audio/softsynth/mt32/module.mk index 995e450076..ed6b0d33ed 100644 --- a/audio/softsynth/mt32/module.mk +++ b/audio/softsynth/mt32/module.mk @@ -2,6 +2,7 @@ MODULE := audio/softsynth/mt32 MODULE_OBJS := \ AReverbModel.o \ + BReverbModel.o \ DelayReverb.o \ FreeverbModel.o \ LA32Ramp.o \ @@ -9,6 +10,7 @@ MODULE_OBJS := \ Partial.o \ PartialManager.o \ Poly.o \ + ROMInfo.o \ Synth.o \ TVA.o \ TVF.o \ diff --git a/audio/softsynth/mt32/mt32emu.h b/audio/softsynth/mt32/mt32emu.h index 091819b95c..ae5f4955b1 100644 --- a/audio/softsynth/mt32/mt32emu.h +++ b/audio/softsynth/mt32/mt32emu.h @@ -59,13 +59,18 @@ #define MT32EMU_MONITOR_TVA 0 #define MT32EMU_MONITOR_TVF 0 - -// 0: Use LUTs to speedup WG -// 1: Use precise float math +// The WG algorithm involves dozens of transcendent maths, e.g. exponents and trigonometry. +// Unfortunately, the majority of systems perform such computations inefficiently, +// standard math libs and FPUs make no optimisations for single precision floats, +// and use no LUTs to speedup computing internal taylor series. Though, there're rare exceptions, +// and there's a hope it will become common soon. +// So, this is the crucial point of speed optimisations. We have now eliminated all the transcendent maths +// within the critical path and use LUTs instead. +// Besides, since the LA32 chip is assumed to use similar LUTs inside, the overall emulation accuracy should be better. +// 0: Use LUTs to speedup WG. Most common setting. You can expect about 50% performance boost. +// 1: Use precise float math. Use this setting to achieve more accurate wave generator. If your system performs better with this setting, it is really notable. :) #define MT32EMU_ACCURATE_WG 0 -#define MT32EMU_USE_EXTINT 0 - // Configuration // The maximum number of partials playing simultaneously #define MT32EMU_MAX_PARTIALS 32 @@ -77,9 +82,10 @@ // If zero, keeps reverb buffers for all modes around all the time to avoid allocating/freeing in the critical path. #define MT32EMU_REDUCE_REVERB_MEMORY 1 -// 0: Use standard Freeverb -// 1: Use AReverb (currently not properly tuned) -#define MT32EMU_USE_AREVERBMODEL 0 +// 0: Use legacy Freeverb +// 1: Use Accurate Reverb model aka AReverb +// 2: Use Bit-perfect Boss Reverb model aka BReverb (for developers, not much practical use) +#define MT32EMU_USE_REVERBMODEL 1 namespace MT32Emu { @@ -109,6 +115,7 @@ const unsigned int MAX_PRERENDER_SAMPLES = 1024; #include "TVF.h" #include "Partial.h" #include "Part.h" +#include "ROMInfo.h" #include "Synth.h" #endif diff --git a/audio/softsynth/opl/dosbox.cpp b/audio/softsynth/opl/dosbox.cpp index e039845b8f..a1a736f9de 100644 --- a/audio/softsynth/opl/dosbox.cpp +++ b/audio/softsynth/opl/dosbox.cpp @@ -247,7 +247,7 @@ byte OPL::read(int port) { } void OPL::writeReg(int r, int v) { - byte tempReg = 0; + int tempReg = 0; switch (_type) { case Config::kOpl2: case Config::kDualOpl2: @@ -257,12 +257,27 @@ void OPL::writeReg(int r, int v) { // Backup old setup register tempReg = _reg.normal; - // We need to set the register we want to write to via port 0x388 - write(0x388, r); - // Do the real writing to the register - write(0x389, v); + // We directly allow writing to secondary OPL3 registers by using + // register values >= 0x100. + if (_type == Config::kOpl3 && r >= 0x100) { + // We need to set the register we want to write to via port 0x222, + // since we want to write to the secondary register set. + write(0x222, r); + // Do the real writing to the register + write(0x223, v); + } else { + // We need to set the register we want to write to via port 0x388 + write(0x388, r); + // Do the real writing to the register + write(0x389, v); + } + // Restore the old register - write(0x388, tempReg); + if (_type == Config::kOpl3 && tempReg >= 0x100) { + write(0x222, tempReg & ~0x100); + } else { + write(0x388, tempReg); + } break; }; } diff --git a/audio/softsynth/opl/mame.cpp b/audio/softsynth/opl/mame.cpp index c54f620a10..2db7d421b6 100644 --- a/audio/softsynth/opl/mame.cpp +++ b/audio/softsynth/opl/mame.cpp @@ -223,7 +223,7 @@ static int *ENV_CURVE; /* multiple table */ -#define ML(a) (int)(a * 2) +#define ML(a) (uint)(a * 2) static const uint MUL_TABLE[16]= { /* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 */ ML(0.50), ML(1.00), ML(2.00), ML(3.00), ML(4.00), ML(5.00), ML(6.00), ML(7.00), diff --git a/audio/softsynth/sid.cpp b/audio/softsynth/sid.cpp index 1ad822b86a..b6f1c87c4b 100644 --- a/audio/softsynth/sid.cpp +++ b/audio/softsynth/sid.cpp @@ -512,7 +512,7 @@ void Filter::enable_filter(bool enable) { enabled = enable; } -void Filter::reset(){ +void Filter::reset() { fc = 0; res = 0; diff --git a/backends/events/gph/gph-events.cpp b/backends/events/gph/gph-events.cpp index b4e106b790..91118d36c1 100644 --- a/backends/events/gph/gph-events.cpp +++ b/backends/events/gph/gph-events.cpp @@ -161,49 +161,6 @@ GPHEventSource::GPHEventSource() : _buttonStateL(false) { } -void GPHEventSource::moveStick() { - bool stickBtn[32]; - - memcpy(stickBtn, _stickBtn, sizeof(stickBtn)); - - if ((stickBtn[0]) || (stickBtn[2]) || (stickBtn[4]) || (stickBtn[6])) - stickBtn[1] = stickBtn[3] = stickBtn[5] = stickBtn[7] = 0; - - if ((stickBtn[1]) || (stickBtn[2]) || (stickBtn[3])) { - if (_km.x_down_count != 2) { - _km.x_vel = -1; - _km.x_down_count = 1; - } else - _km.x_vel = -4; - } else if ((stickBtn[5]) || (stickBtn[6]) || (stickBtn[7])) { - if (_km.x_down_count != 2) { - _km.x_vel = 1; - _km.x_down_count = 1; - } else - _km.x_vel = 4; - } else { - _km.x_vel = 0; - _km.x_down_count = 0; - } - - if ((stickBtn[0]) || (stickBtn[1]) || (stickBtn[7])) { - if (_km.y_down_count != 2) { - _km.y_vel = -1; - _km.y_down_count = 1; - } else - _km.y_vel = -4; - } else if ((stickBtn[3]) || (stickBtn[4]) || (stickBtn[5])) { - if (_km.y_down_count != 2) { - _km.y_vel = 1; - _km.y_down_count = 1; - } else - _km.y_vel = 4; - } else { - _km.y_vel = 0; - _km.y_down_count = 0; - } -} - /* Custom handleMouseButtonDown/handleMouseButtonUp to deal with 'Tap Mode' for the touchscreen */ bool GPHEventSource::handleMouseButtonDown(SDL_Event &ev, Common::Event &event) { @@ -268,19 +225,110 @@ bool GPHEventSource::handleMouseButtonUp(SDL_Event &ev, Common::Event &event) { bool GPHEventSource::handleJoyButtonDown(SDL_Event &ev, Common::Event &event) { - _stickBtn[ev.jbutton.button] = 1; event.kbd.flags = 0; switch (ev.jbutton.button) { case BUTTON_UP: - case BUTTON_UPLEFT: - case BUTTON_LEFT: - case BUTTON_DOWNLEFT: + if (_km.y_down_count != 2) { + _km.y_vel = -1; + _km.y_down_count = 1; + } else { + _km.y_vel = -4; + } + event.type = Common::EVENT_MOUSEMOVE; + processMouseEvent(event, _km.x, _km.y); + break; case BUTTON_DOWN: - case BUTTON_DOWNRIGHT: + if (_km.y_down_count != 2) { + _km.y_vel = 1; + _km.y_down_count = 1; + } else { + _km.y_vel = 4; + } + event.type = Common::EVENT_MOUSEMOVE; + processMouseEvent(event, _km.x, _km.y); + break; + case BUTTON_LEFT: + if (_km.x_down_count != 2) { + _km.x_vel = -1; + _km.x_down_count = 1; + } else { + _km.x_vel = -4; + } + event.type = Common::EVENT_MOUSEMOVE; + processMouseEvent(event, _km.x, _km.y); + break; case BUTTON_RIGHT: + if (_km.x_down_count != 3) { + _km.x_vel = 1; + _km.x_down_count = 1; + } else { + _km.x_vel = 4; + } + event.type = Common::EVENT_MOUSEMOVE; + processMouseEvent(event, _km.x, _km.y); + break; + case BUTTON_UPLEFT: + if (_km.x_down_count != 2) { + _km.x_vel = -1; + _km.x_down_count = 1; + } else { + _km.x_vel = -4; + } + if (_km.y_down_count != 2) { + _km.y_vel = -1; + _km.y_down_count = 1; + } else { + _km.y_vel = -4; + } + event.type = Common::EVENT_MOUSEMOVE; + processMouseEvent(event, _km.x, _km.y); + break; case BUTTON_UPRIGHT: - moveStick(); + if (_km.x_down_count != 2) { + _km.x_vel = 1; + _km.x_down_count = 1; + } else { + _km.x_vel = 4; + } + if (_km.y_down_count != 2) { + _km.y_vel = -1; + _km.y_down_count = 1; + } else { + _km.y_vel = -4; + } + event.type = Common::EVENT_MOUSEMOVE; + processMouseEvent(event, _km.x, _km.y); + break; + case BUTTON_DOWNLEFT: + if (_km.x_down_count != 2) { + _km.x_vel = -1; + _km.x_down_count = 1; + } else { + _km.x_vel = -4; + } + if (_km.y_down_count != 2) { + _km.y_vel = 1; + _km.y_down_count = 1; + } else { + _km.y_vel = 4; + } + event.type = Common::EVENT_MOUSEMOVE; + processMouseEvent(event, _km.x, _km.y); + break; + case BUTTON_DOWNRIGHT: + if (_km.x_down_count != 2) { + _km.x_vel = 1; + _km.x_down_count = 1; + } else { + _km.x_vel = 4; + } + if (_km.y_down_count != 2) { + _km.y_vel = 1; + _km.y_down_count = 1; + } else { + _km.y_vel = 4; + } event.type = Common::EVENT_MOUSEMOVE; processMouseEvent(event, _km.x, _km.y); break; @@ -391,7 +439,6 @@ bool GPHEventSource::handleJoyButtonDown(SDL_Event &ev, Common::Event &event) { bool GPHEventSource::handleJoyButtonUp(SDL_Event &ev, Common::Event &event) { - _stickBtn[ev.jbutton.button] = 0; event.kbd.flags = 0; switch (ev.jbutton.button) { @@ -403,7 +450,10 @@ bool GPHEventSource::handleJoyButtonUp(SDL_Event &ev, Common::Event &event) { case BUTTON_DOWNRIGHT: case BUTTON_RIGHT: case BUTTON_UPRIGHT: - moveStick(); + _km.y_vel = 0; + _km.y_down_count = 0; + _km.x_vel = 0; + _km.x_down_count = 0; event.type = Common::EVENT_MOUSEMOVE; processMouseEvent(event, _km.x, _km.y); break; diff --git a/backends/events/gph/gph-events.h b/backends/events/gph/gph-events.h index 7672bffed2..3b1e6f090a 100644 --- a/backends/events/gph/gph-events.h +++ b/backends/events/gph/gph-events.h @@ -34,18 +34,11 @@ public: GPHEventSource(); protected: - bool _stickBtn[32]; - /** * Button state for L button modifier */ bool _buttonStateL; - /** - * Handles the stick movement - */ - void moveStick(); - bool handleJoyButtonDown(SDL_Event &ev, Common::Event &event); bool handleJoyButtonUp(SDL_Event &ev, Common::Event &event); bool handleMouseButtonDown(SDL_Event &ev, Common::Event &event); diff --git a/backends/events/openpandora/op-events.cpp b/backends/events/openpandora/op-events.cpp index c1d6362fcb..fc63cdf74f 100644 --- a/backends/events/openpandora/op-events.cpp +++ b/backends/events/openpandora/op-events.cpp @@ -44,7 +44,8 @@ enum { /* Touchscreen TapMode */ TAPMODE_LEFT = 0, TAPMODE_RIGHT = 1, - TAPMODE_HOVER = 2 + TAPMODE_HOVER = 2, + TAPMODE_HOVER_DPAD = 3 }; OPEventSource::OPEventSource() @@ -63,6 +64,8 @@ bool OPEventSource::handleMouseButtonDown(SDL_Event &ev, Common::Event &event) { event.type = Common::EVENT_RBUTTONDOWN; else if (OP::tapmodeLevel == TAPMODE_HOVER) /* TAPMODE_HOVER = Hover (No Click) Tap Mode */ event.type = Common::EVENT_MOUSEMOVE; + else if (OP::tapmodeLevel == TAPMODE_HOVER_DPAD) /* TAPMODE_HOVER_DPAD = Hover (DPad Clicks) Tap Mode */ + event.type = Common::EVENT_MOUSEMOVE; else event.type = Common::EVENT_LBUTTONDOWN; /* For normal mice etc. */ } else if (ev.button.button == SDL_BUTTON_RIGHT) @@ -95,6 +98,8 @@ bool OPEventSource::handleMouseButtonUp(SDL_Event &ev, Common::Event &event) { event.type = Common::EVENT_RBUTTONUP; else if (OP::tapmodeLevel == TAPMODE_HOVER) /* TAPMODE_HOVER = Hover (No Click) Tap Mode */ event.type = Common::EVENT_MOUSEMOVE; + else if (OP::tapmodeLevel == TAPMODE_HOVER_DPAD) /* TAPMODE_HOVER_DPAD = Hover (DPad Clicks) Tap Mode */ + event.type = Common::EVENT_MOUSEMOVE; else event.type = Common::EVENT_LBUTTONUP; /* For normal mice etc. */ } else if (ev.button.button == SDL_BUTTON_RIGHT) @@ -117,6 +122,30 @@ bool OPEventSource::handleMouseButtonUp(SDL_Event &ev, Common::Event &event) { bool OPEventSource::remapKey(SDL_Event &ev, Common::Event &event) { + if (OP::tapmodeLevel == TAPMODE_HOVER_DPAD) { + switch (ev.key.keysym.sym) { + case SDLK_LEFT: + event.type = (ev.type == SDL_KEYDOWN) ? Common::EVENT_LBUTTONDOWN : Common::EVENT_LBUTTONUP; + processMouseEvent(event, _km.x, _km.y); + return true; + break; + case SDLK_RIGHT: + event.type = (ev.type == SDL_KEYDOWN) ? Common::EVENT_RBUTTONDOWN : Common::EVENT_RBUTTONUP; + processMouseEvent(event, _km.x, _km.y); + return true; + break; +#if defined(SDL_BUTTON_MIDDLE) + case SDLK_UP: + event.type = (ev.type == SDL_KEYDOWN) ? Common::EVENT_MBUTTONDOWN : Common::EVENT_MBUTTONUP; + processMouseEvent(event, _km.x, _km.y); + return true; + break; +#endif + default: + break; + } + } + if (ev.type == SDL_KEYDOWN) { switch (ev.key.keysym.sym) { case SDLK_HOME: @@ -141,6 +170,8 @@ bool OPEventSource::remapKey(SDL_Event &ev, Common::Event &event) { g_system->displayMessageOnOSD(_("Touchscreen 'Tap Mode' - Right Click")); } else if (OP::tapmodeLevel == TAPMODE_HOVER) { g_system->displayMessageOnOSD(_("Touchscreen 'Tap Mode' - Hover (No Click)")); + } else if (OP::tapmodeLevel == TAPMODE_HOVER_DPAD) { + g_system->displayMessageOnOSD(_("Touchscreen 'Tap Mode' - Hover (DPad Clicks)")); } break; case SDLK_RSHIFT: diff --git a/backends/events/sdl/sdl-events.cpp b/backends/events/sdl/sdl-events.cpp index f94171646a..0ca5bbb059 100644 --- a/backends/events/sdl/sdl-events.cpp +++ b/backends/events/sdl/sdl-events.cpp @@ -191,6 +191,8 @@ void SdlEventSource::SDLModToOSystemKeyFlags(SDLMod mod, Common::Event &event) { #endif if (mod & KMOD_CTRL) event.kbd.flags |= Common::KBD_CTRL; + if (mod & KMOD_META) + event.kbd.flags |= Common::KBD_META; // Sticky flags if (mod & KMOD_NUM) diff --git a/backends/events/sdl/sdl-events.h b/backends/events/sdl/sdl-events.h index 2ba88c702b..ca4835126f 100644 --- a/backends/events/sdl/sdl-events.h +++ b/backends/events/sdl/sdl-events.h @@ -116,7 +116,7 @@ protected: //@} /** - * Assigns the mouse coords to the mouse event. Furthermore notify the + * Assigns the mouse coords to the mouse event. Furthermore notify the * graphics manager about the position change. */ virtual void processMouseEvent(Common::Event &event, int x, int y); diff --git a/backends/events/webossdl/webossdl-events.h b/backends/events/webossdl/webossdl-events.h index 99ed3105f8..1ba5c6fcbf 100644 --- a/backends/events/webossdl/webossdl-events.h +++ b/backends/events/webossdl/webossdl-events.h @@ -73,10 +73,10 @@ protected: // The current mouse position on the screen. int _curX, _curY; - + // The current screen dimensions int _screenX, _screenY; - + // The drag distance for linear gestures int _swipeDistX, _swipeDistY; @@ -107,7 +107,7 @@ protected: virtual bool handleMouseButtonUp(SDL_Event &ev, Common::Event &event); virtual bool handleMouseMotion(SDL_Event &ev, Common::Event &event); virtual bool pollEvent(Common::Event &event); - + // Utility functions void calculateDimensions(); }; diff --git a/backends/graphics/dinguxsdl/dinguxsdl-graphics.cpp b/backends/graphics/dinguxsdl/dinguxsdl-graphics.cpp index 205dcfdbec..bd87c9fafd 100644 --- a/backends/graphics/dinguxsdl/dinguxsdl-graphics.cpp +++ b/backends/graphics/dinguxsdl/dinguxsdl-graphics.cpp @@ -432,7 +432,7 @@ bool DINGUXSdlGraphicsManager::loadGFXMode() { // Forcefully disable aspect ratio correction for games // which starts with a native 240px height resolution. // This fixes games with weird resolutions, like MM Nes (256x240) - if(_videoMode.screenHeight == 240) { + if (_videoMode.screenHeight == 240) { _videoMode.aspectRatioCorrection = false; } @@ -479,7 +479,7 @@ void DINGUXSdlGraphicsManager::setFeatureState(OSystem::Feature f, bool enable) case OSystem::kFeatureCursorPalette: _cursorPaletteDisabled = !enable; blitCursor(); - break; + break; default: break; } diff --git a/backends/graphics/gph/gph-graphics.cpp b/backends/graphics/gph/gph-graphics.cpp index 8521e88eaf..92553564bf 100644 --- a/backends/graphics/gph/gph-graphics.cpp +++ b/backends/graphics/gph/gph-graphics.cpp @@ -486,7 +486,13 @@ bool GPHGraphicsManager::loadGFXMode() { if (_videoMode.aspectRatioCorrection) _videoMode.overlayHeight = real2Aspect(_videoMode.overlayHeight); } - return SurfaceSdlGraphicsManager::loadGFXMode(); + SurfaceSdlGraphicsManager::loadGFXMode(); + + // The old GP2X hacked SDL needs this after any call to SDL_SetVideoMode + // and it does not hurt other devices. + SDL_ShowCursor(SDL_DISABLE); + + return true; } bool GPHGraphicsManager::hasFeature(OSystem::Feature f) { diff --git a/backends/graphics/opengl/gltexture.cpp b/backends/graphics/opengl/gltexture.cpp index ce69dc4aab..ca674563df 100644 --- a/backends/graphics/opengl/gltexture.cpp +++ b/backends/graphics/opengl/gltexture.cpp @@ -108,9 +108,18 @@ void GLTexture::allocBuffer(GLuint w, GLuint h) { _realWidth = w; _realHeight = h; - if (w <= _textureWidth && h <= _textureHeight && !_refresh) - // Already allocated a sufficiently large buffer - return; + if (!_refresh) { + if (npot_supported && _filter == GL_LINEAR) { + // Check if we already allocated a correctly-sized buffer + // This is so we don't need to duplicate the last row/column + if (w == _textureWidth && h == _textureHeight) + return; + } else { + // Check if we already have a large enough buffer + if (w <= _textureWidth && h <= _textureHeight) + return; + } + } if (npot_supported) { _textureWidth = w; @@ -151,12 +160,37 @@ void GLTexture::updateBuffer(const void *buf, int pitch, GLuint x, GLuint y, GLu } else { // Update the texture row by row const byte *src = (const byte *)buf; + GLuint curY = y; + GLuint height = h; do { - glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, + glTexSubImage2D(GL_TEXTURE_2D, 0, x, curY, w, 1, _glFormat, _glType, src); CHECK_GL_ERROR(); - ++y; + curY++; src += pitch; - } while (--h); + } while (--height); + } + + // If we're in linear filter mode, repeat the last row/column if the real dimensions + // doesn't match the texture dimensions. + if (_filter == GL_LINEAR) { + if (_realWidth != _textureWidth && x + w == _realWidth) { + const byte *src = (const byte *)buf + (w - 1) * _bytesPerPixel; + GLuint curY = y; + GLuint height = h; + + do { + glTexSubImage2D(GL_TEXTURE_2D, 0, x + w, + curY, 1, 1, _glFormat, _glType, src); CHECK_GL_ERROR(); + + curY++; + src += pitch; + } while (--height); + } + + if (_realHeight != _textureHeight && y + h == _realHeight) { + glTexSubImage2D(GL_TEXTURE_2D, 0, x, y + h, + w, 1, _glFormat, _glType, (const byte *)buf + pitch * (h - 1)); CHECK_GL_ERROR(); + } } } @@ -177,10 +211,10 @@ void GLTexture::drawTexture(GLshort x, GLshort y, GLshort w, GLshort h) { // Calculate the screen rect where the texture will be drawn const GLshort vertices[] = { - x, y, - x + w, y, - x, y + h, - x + w, y + h, + x, y, + (GLshort)(x + w), y, + x, (GLshort)(y + h), + (GLshort)(x + w), (GLshort)(y + h), }; glVertexPointer(2, GL_SHORT, 0, vertices); CHECK_GL_ERROR(); diff --git a/backends/graphics/opengl/opengl-graphics.cpp b/backends/graphics/opengl/opengl-graphics.cpp index dce902d894..48e2663d44 100644 --- a/backends/graphics/opengl/opengl-graphics.cpp +++ b/backends/graphics/opengl/opengl-graphics.cpp @@ -902,7 +902,7 @@ void OpenGLGraphicsManager::getGLPixelFormat(Graphics::PixelFormat pixelFormat, bpp = 4; intFormat = GL_RGBA; glFormat = GL_RGBA; - gltype = GL_UNSIGNED_BYTE; + gltype = GL_UNSIGNED_INT_8_8_8_8; } else if (pixelFormat == Graphics::PixelFormat(3, 8, 8, 8, 0, 16, 8, 0, 0)) { // RGB888 bpp = 3; intFormat = GL_RGB; @@ -918,11 +918,6 @@ void OpenGLGraphicsManager::getGLPixelFormat(Graphics::PixelFormat pixelFormat, intFormat = GL_RGBA; glFormat = GL_RGBA; gltype = GL_UNSIGNED_SHORT_5_5_5_1; - } else if (pixelFormat == Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0)) { // RGB555 - bpp = 2; - intFormat = GL_RGB; - glFormat = GL_BGRA; - gltype = GL_UNSIGNED_SHORT_1_5_5_5_REV; } else if (pixelFormat == Graphics::PixelFormat(2, 4, 4, 4, 4, 12, 8, 4, 0)) { // RGBA4444 bpp = 2; intFormat = GL_RGBA; @@ -936,6 +931,13 @@ void OpenGLGraphicsManager::getGLPixelFormat(Graphics::PixelFormat pixelFormat, glFormat = GL_RGB; gltype = GL_UNSIGNED_BYTE; #ifndef USE_GLES + } else if (pixelFormat == Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0)) { // RGB555 + // GL_BGRA does not exist in every GLES implementation so should not be configured if + // USE_GLES is set. + bpp = 2; + intFormat = GL_RGB; + glFormat = GL_BGRA; + gltype = GL_UNSIGNED_SHORT_1_5_5_5_REV; } else if (pixelFormat == Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24)) { // ARGB8888 bpp = 4; intFormat = GL_RGBA; diff --git a/backends/graphics/openglsdl/openglsdl-graphics.cpp b/backends/graphics/openglsdl/openglsdl-graphics.cpp index 67041ae17b..c5605cae87 100644 --- a/backends/graphics/openglsdl/openglsdl-graphics.cpp +++ b/backends/graphics/openglsdl/openglsdl-graphics.cpp @@ -460,6 +460,10 @@ void OpenGLSdlGraphicsManager::toggleFullScreen(int loop) { _activeFullscreenMode = -2; setFullscreenMode(!isFullscreen); } + + // HACK: We need to force a refresh here, since we change the + // fullscreen mode. + _transactionDetails.needRefresh = true; endGFXTransaction(); // Ignore resize events for the next 10 frames @@ -665,7 +669,7 @@ void OpenGLSdlGraphicsManager::notifyResize(const uint width, const uint height) void OpenGLSdlGraphicsManager::transformMouseCoordinates(Common::Point &point) { adjustMousePosition(point.x, point.y); } - + void OpenGLSdlGraphicsManager::notifyMousePos(Common::Point mouse) { setMousePosition(mouse.x, mouse.y); } diff --git a/backends/graphics/openpandora/op-graphics.cpp b/backends/graphics/openpandora/op-graphics.cpp index 5f0301a0c8..f371081fde 100644 --- a/backends/graphics/openpandora/op-graphics.cpp +++ b/backends/graphics/openpandora/op-graphics.cpp @@ -26,28 +26,59 @@ #include "backends/graphics/openpandora/op-graphics.h" #include "backends/events/openpandora/op-events.h" -//#include "backends/platform/openpandora/op-sdl.h" #include "graphics/scaler/aspect.h" #include "common/mutex.h" #include "common/textconsole.h" +static SDL_Cursor *hiddenCursor; + OPGraphicsManager::OPGraphicsManager(SdlEventSource *sdlEventSource) : SurfaceSdlGraphicsManager(sdlEventSource) { } bool OPGraphicsManager::loadGFXMode() { - /* FIXME: For now we just cheat and set the overlay to 640*480 not 800*480 and let SDL - deal with the boarders (it saves cleaning up the overlay when the game screen is - smaller than the overlay ;) + + uint8_t hiddenCursorData = 0; + hiddenCursor = SDL_CreateCursor(&hiddenCursorData, &hiddenCursorData, 8, 1, 0, 0); + + /* On the OpenPandora we need to work around an SDL assumption that + returns relative mouse coordinates when you get to the screen + edges using the touchscreen. The workaround is to set a blank + SDL cursor and not disable it (Hackish I know). + + The root issues likes in the Windows Manager GRAB code in SDL. + That is why the issue is not seen on framebuffer devices like the + GP2X (there is no X window manager ;)). */ - _videoMode.overlayWidth = 640; - _videoMode.overlayHeight = 480; + SDL_ShowCursor(SDL_ENABLE); + SDL_SetCursor(hiddenCursor); + _videoMode.fullscreen = true; + _videoMode.overlayWidth = _videoMode.screenWidth * _videoMode.scaleFactor; + _videoMode.overlayHeight = _videoMode.screenHeight * _videoMode.scaleFactor; + if (_videoMode.screenHeight != 200 && _videoMode.screenHeight != 400) _videoMode.aspectRatioCorrection = false; + if (_videoMode.aspectRatioCorrection) + _videoMode.overlayHeight = real2Aspect(_videoMode.overlayHeight); + + _videoMode.hardwareWidth = _videoMode.screenWidth * _videoMode.scaleFactor; + _videoMode.hardwareHeight = effectiveScreenHeight(); + return SurfaceSdlGraphicsManager::loadGFXMode(); } +void OPGraphicsManager::unloadGFXMode() { + + uint8_t hiddenCursorData = 0; + hiddenCursor = SDL_CreateCursor(&hiddenCursorData, &hiddenCursorData, 8, 1, 0, 0); + + // Free the hidden SDL cursor created in loadGFXMode + SDL_FreeCursor(hiddenCursor); + + SurfaceSdlGraphicsManager::unloadGFXMode(); +} + #endif diff --git a/backends/graphics/openpandora/op-graphics.h b/backends/graphics/openpandora/op-graphics.h index 0b3eeae8ec..2e3d63e3ad 100644 --- a/backends/graphics/openpandora/op-graphics.h +++ b/backends/graphics/openpandora/op-graphics.h @@ -24,7 +24,6 @@ #define BACKENDS_GRAPHICS_OP_H #include "backends/graphics/surfacesdl/surfacesdl-graphics.h" -#include "graphics/scaler/aspect.h" // for aspect2Real #include "graphics/scaler/downscaler.h" enum { @@ -35,28 +34,8 @@ class OPGraphicsManager : public SurfaceSdlGraphicsManager { public: OPGraphicsManager(SdlEventSource *sdlEventSource); -// bool hasFeature(OSystem::Feature f); -// void setFeatureState(OSystem::Feature f, bool enable); -// bool getFeatureState(OSystem::Feature f); -// int getDefaultGraphicsMode() const; - -// void initSize(uint w, uint h, const Graphics::PixelFormat *format = NULL); -// const OSystem::GraphicsMode *getSupportedGraphicsModes() const; -// bool setGraphicsMode(const char *name); -// bool setGraphicsMode(int mode); -// void setGraphicsModeIntern(); -// void internUpdateScreen(); -// void showOverlay(); -// void hideOverlay(); bool loadGFXMode(); -// void drawMouse(); -// void undrawMouse(); -// virtual void warpMouse(int x, int y); - -// SurfaceSdlGraphicsManager::MousePos *getMouseCurState(); -// SurfaceSdlGraphicsManager::VideoState *getVideoMode(); - -// virtual void adjustMouseEvent(const Common::Event &event); + void unloadGFXMode(); }; #endif /* BACKENDS_GRAPHICS_OP_H */ diff --git a/backends/graphics/surfacesdl/surfacesdl-graphics.cpp b/backends/graphics/surfacesdl/surfacesdl-graphics.cpp index fb964d6951..02e58ab319 100644 --- a/backends/graphics/surfacesdl/surfacesdl-graphics.cpp +++ b/backends/graphics/surfacesdl/surfacesdl-graphics.cpp @@ -968,7 +968,7 @@ void SurfaceSdlGraphicsManager::internUpdateScreen() { // If the shake position changed, fill the dirty area with blackness if (_currentShakePos != _newShakePos || (_mouseNeedsRedraw && _mouseBackup.y <= _currentShakePos)) { - SDL_Rect blackrect = {0, 0, _videoMode.screenWidth * _videoMode.scaleFactor, _newShakePos * _videoMode.scaleFactor}; + SDL_Rect blackrect = {0, 0, (Uint16)(_videoMode.screenWidth * _videoMode.scaleFactor), (Uint16)(_newShakePos * _videoMode.scaleFactor)}; if (_videoMode.aspectRatioCorrection && !_overlayVisible) blackrect.h = real2Aspect(blackrect.h - 1) + 1; diff --git a/backends/midi/coreaudio.cpp b/backends/midi/coreaudio.cpp index 43c801287d..94262d0d92 100644 --- a/backends/midi/coreaudio.cpp +++ b/backends/midi/coreaudio.cpp @@ -172,10 +172,15 @@ int MidiDriver_CORE::open() { // Load custom soundfont, if specified if (ConfMan.hasKey("soundfont")) { - FSRef fsref; - FSSpec fsSpec; const char *soundfont = ConfMan.get("soundfont").c_str(); + // TODO: We should really check whether the file contains an + // actual soundfont... + +#if USE_DEPRECATED_COREAUDIO_API + // Before 10.5, we need to use kMusicDeviceProperty_SoundBankFSSpec + FSRef fsref; + FSSpec fsSpec; err = FSPathMakeRef ((const byte *)soundfont, &fsref, NULL); if (err == noErr) { @@ -183,8 +188,6 @@ int MidiDriver_CORE::open() { } if (err == noErr) { - // TODO: We should really check here whether the file contains an - // actual soundfont... err = AudioUnitSetProperty ( _synth, kMusicDeviceProperty_SoundBankFSSpec, kAudioUnitScope_Global, @@ -192,9 +195,27 @@ int MidiDriver_CORE::open() { &fsSpec, sizeof(fsSpec) ); } +#else + // kMusicDeviceProperty_SoundBankFSSpec is present on 10.6+, but broken + // kMusicDeviceProperty_SoundBankURL was added in 10.5 as a replacement + CFURLRef url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8 *)soundfont, strlen(soundfont), false); + + if (url) { + err = AudioUnitSetProperty ( + _synth, + kMusicDeviceProperty_SoundBankURL, kAudioUnitScope_Global, + 0, + &url, sizeof(url) + ); + + CFRelease(url); + } else { + warning("Failed to allocate CFURLRef from '%s'", soundfont); + } +#endif if (err != noErr) - warning("Failed loading custom sound font '%s' (error %ld)\n", soundfont, (long)err); + error("Failed loading custom sound font '%s' (error %ld)", soundfont, (long)err); } #ifdef COREAUDIO_DISABLE_REVERB diff --git a/backends/midi/seq.cpp b/backends/midi/seq.cpp index 4efad9ceae..d48b80c40b 100644 --- a/backends/midi/seq.cpp +++ b/backends/midi/seq.cpp @@ -90,9 +90,9 @@ int MidiDriver_SEQ::open() { if ((device_name == NULL) || (device < 0)) { if (device_name == NULL) - warning("Opening /dev/null (no music will be heard) "); + warning("Opening /dev/null (no music will be heard)"); else - warning("Cannot open rawmidi device %s - using /dev/null (no music will be heard) ", + warning("Cannot open rawmidi device %s - using /dev/null (no music will be heard)", device_name); device = (::open(("/dev/null"), O_RDWR, 0)); if (device < 0) @@ -145,7 +145,7 @@ void MidiDriver_SEQ::send(uint32 b) { buf[position++] = 0; break; default: - warning("MidiDriver_SEQ::send: unknown : %08x", (int)b); + warning("MidiDriver_SEQ::send: unknown: %08x", (int)b); break; } if (write(device, buf, position) == -1) diff --git a/backends/midi/sndio.cpp b/backends/midi/sndio.cpp index 21c9ea4fec..a065a658e1 100644 --- a/backends/midi/sndio.cpp +++ b/backends/midi/sndio.cpp @@ -81,7 +81,7 @@ void MidiDriver_Sndio::send(uint32 b) { if (!hdl) return; - buf[0] = b & 0xff; + buf[0] = b & 0xff; buf[1] = (b >> 8) & 0xff; buf[2] = (b >> 16) & 0xff; buf[3] = (b >> 24) & 0xff; @@ -101,7 +101,7 @@ void MidiDriver_Sndio::send(uint32 b) { void MidiDriver_Sndio::sysEx(const byte *msg, uint16 length) { if (!hdl) return; - + unsigned char buf[266]; assert(length + 2 <= ARRAYSIZE(buf)); diff --git a/backends/mixer/sdl/sdl-mixer.cpp b/backends/mixer/sdl/sdl-mixer.cpp index 001309a777..3c79290b50 100644 --- a/backends/mixer/sdl/sdl-mixer.cpp +++ b/backends/mixer/sdl/sdl-mixer.cpp @@ -153,7 +153,7 @@ void SdlMixerManager::suspendAudio() { int SdlMixerManager::resumeAudio() { if (!_audioSuspended) return -2; - if (SDL_OpenAudio(&_obtained, NULL) < 0){ + if (SDL_OpenAudio(&_obtained, NULL) < 0) { return -1; } SDL_PauseAudio(0); diff --git a/backends/mixer/sdl13/sdl13-mixer.cpp b/backends/mixer/sdl13/sdl13-mixer.cpp index 84777c8bab..24d3434fde 100644 --- a/backends/mixer/sdl13/sdl13-mixer.cpp +++ b/backends/mixer/sdl13/sdl13-mixer.cpp @@ -69,13 +69,13 @@ void Sdl13MixerManager::init() { warning("Could not open audio device: %s", SDL_GetError()); _mixer = new Audio::MixerImpl(g_system, desired.freq); - assert(_mixer); + assert(_mixer); _mixer->setReady(false); } else { debug(1, "Output sample rate: %d Hz", _obtained.freq); _mixer = new Audio::MixerImpl(g_system, _obtained.freq); - assert(_mixer); + assert(_mixer); _mixer->setReady(true); startAudio(); diff --git a/backends/platform/android/android.h b/backends/platform/android/android.h index 4b13ca4b0f..5f2f40b726 100644 --- a/backends/platform/android/android.h +++ b/backends/platform/android/android.h @@ -234,7 +234,7 @@ private: int _fingersDown; void clipMouse(Common::Point &p); - void scaleMouse(Common::Point &p, int x, int y, bool deductDrawRect = true); + void scaleMouse(Common::Point &p, int x, int y, bool deductDrawRect = true, bool touchpadMode = false); void updateEventScale(); void disableCursorPalette(); diff --git a/backends/platform/android/android.mk b/backends/platform/android/android.mk index 9292a16595..f498c671de 100644 --- a/backends/platform/android/android.mk +++ b/backends/platform/android/android.mk @@ -10,6 +10,7 @@ JAVA_FILES = \ ScummVMApplication.java \ ScummVMActivity.java \ EditableSurfaceView.java \ + MouseHelper.java \ Unpacker.java JAVA_FILES_PLUGIN = \ @@ -47,15 +48,9 @@ APKBUILDER = $(ANDROID_SDK)/tools/apkbuilder JAVAC ?= javac JAVACFLAGS = -source 1.5 -target 1.5 -# This is a bit silly. I want to compile against the 1.6 android.jar, -# to make the compiler check that I don't use something that requires -# a newer Android. However, in order to use android:installLocation, -# we need to give aapt a version >=8 android.jar - even though the -# result will work ok on 1.5+. -ANDROID_JAR = $(ANDROID_SDK)/platforms/android-4/android.jar -ANDROID_JAR8 = $(ANDROID_SDK)/platforms/android-8/android.jar +ANDROID_JAR = $(ANDROID_SDK)/platforms/android-14/android.jar -PATH_BUILD = build.tmp +PATH_BUILD = ./build.tmp PATH_BUILD_ASSETS = $(PATH_BUILD)/assets PATH_BUILD_CLASSES_MAIN_TOP = $(PATH_BUILD)/classes.main PATH_BUILD_CLASSES_PLUGIN_TOP = $(PATH_BUILD)/classes.plugin @@ -92,9 +87,9 @@ $(FILE_MANIFEST): $(FILE_MANIFEST_SRC) @$(MKDIR) -p $(@D) sed "s/@ANDROID_VERSIONCODE@/$(ANDROID_VERSIONCODE)/" < $< > $@ -$(SRC_GEN): $(FILE_MANIFEST) $(filter %.xml,$(RESOURCES)) $(ANDROID_JAR8) +$(SRC_GEN): $(FILE_MANIFEST) $(filter %.xml,$(RESOURCES)) $(ANDROID_JAR) @$(MKDIR) -p $(PATH_GEN_TOP) - $(AAPT) package -m -J $(PATH_GEN_TOP) -M $< -S $(PATH_RESOURCES) -I $(ANDROID_JAR8) + $(AAPT) package -m -J $(PATH_GEN_TOP) -M $< -S $(PATH_RESOURCES) -I $(ANDROID_JAR) $(PATH_CLASSES_MAIN)/%.class: $(PATH_GEN)/%.java $(SRC_GEN) @$(MKDIR) -p $(@D) @@ -127,13 +122,13 @@ $(PATH_STAGE_PREFIX).%/res/drawable/scummvm.png: $(PATH_RESOURCES)/drawable/scum @$(MKDIR) -p $(@D) $(CP) $< $@ -$(FILE_RESOURCES_MAIN): $(FILE_MANIFEST) $(RESOURCES) $(ANDROID_JAR8) $(DIST_FILES_THEMES) $(DIST_FILES_ENGINEDATA) +$(FILE_RESOURCES_MAIN): $(FILE_MANIFEST) $(RESOURCES) $(ANDROID_JAR) $(DIST_FILES_THEMES) $(DIST_FILES_ENGINEDATA) $(INSTALL) -d $(PATH_BUILD_ASSETS) $(INSTALL) -c -m 644 $(DIST_FILES_THEMES) $(DIST_FILES_ENGINEDATA) $(PATH_BUILD_ASSETS)/ work_dir=`pwd`; \ for i in $(PATH_BUILD_ASSETS)/*.zip; do \ echo "recompress $$i"; \ - cd $$work_dir; \ + cd "$$work_dir"; \ $(RM) -rf $(PATH_BUILD_ASSETS)/tmp; \ $(MKDIR) $(PATH_BUILD_ASSETS)/tmp; \ unzip -q $$i -d $(PATH_BUILD_ASSETS)/tmp; \ @@ -141,10 +136,10 @@ $(FILE_RESOURCES_MAIN): $(FILE_MANIFEST) $(RESOURCES) $(ANDROID_JAR8) $(DIST_FIL zip -r ../`basename $$i` *; \ done @$(RM) -rf $(PATH_BUILD_ASSETS)/tmp - $(AAPT) package -f -0 zip -M $< -S $(PATH_RESOURCES) -A $(PATH_BUILD_ASSETS) -I $(ANDROID_JAR8) -F $@ + $(AAPT) package -f -0 zip -M $< -S $(PATH_RESOURCES) -A $(PATH_BUILD_ASSETS) -I $(ANDROID_JAR) -F $@ -$(PATH_BUILD)/%/$(FILE_RESOURCES): $(PATH_BUILD)/%/AndroidManifest.xml $(PATH_STAGE_PREFIX).%/res/values/strings.xml $(PATH_STAGE_PREFIX).%/res/drawable/scummvm.png plugins/lib%.so $(ANDROID_JAR8) - $(AAPT) package -f -M $< -S $(PATH_STAGE_PREFIX).$*/res -I $(ANDROID_JAR8) -F $@ +$(PATH_BUILD)/%/$(FILE_RESOURCES): $(PATH_BUILD)/%/AndroidManifest.xml $(PATH_STAGE_PREFIX).%/res/values/strings.xml $(PATH_STAGE_PREFIX).%/res/drawable/scummvm.png plugins/lib%.so $(ANDROID_JAR) + $(AAPT) package -f -M $< -S $(PATH_STAGE_PREFIX).$*/res -I $(ANDROID_JAR) -F $@ # Package installer won't delete old libscummvm.so on upgrade so # replace it with a zero size file diff --git a/backends/platform/android/events.cpp b/backends/platform/android/events.cpp index 21d2344fa7..db1261e432 100644 --- a/backends/platform/android/events.cpp +++ b/backends/platform/android/events.cpp @@ -59,6 +59,11 @@ enum { JE_DOUBLE_TAP = 6, JE_MULTI = 7, JE_BALL = 8, + JE_LMB_DOWN = 9, + JE_LMB_UP = 10, + JE_RMB_DOWN = 11, + JE_RMB_UP = 12, + JE_MOUSE_MOVE = 13, JE_QUIT = 0x1000 }; @@ -272,7 +277,7 @@ void OSystem_Android::clipMouse(Common::Point &p) { } void OSystem_Android::scaleMouse(Common::Point &p, int x, int y, - bool deductDrawRect) { + bool deductDrawRect, bool touchpadMode) { const GLESBaseTexture *tex; if (_show_overlay) @@ -282,7 +287,7 @@ void OSystem_Android::scaleMouse(Common::Point &p, int x, int y, const Common::Rect &r = tex->getDrawRect(); - if (_touchpad_mode) { + if (touchpadMode) { x = x * 100 / _touchpad_scale; y = y * 100 / _touchpad_scale; } @@ -327,11 +332,16 @@ void OSystem_Android::pushEvent(int type, int arg1, int arg2, int arg3, } switch (arg2) { + + // special case. we'll only get it's up event case JKEYCODE_BACK: e.kbd.keycode = Common::KEYCODE_ESCAPE; e.kbd.ascii = Common::ASCII_ESCAPE; lockMutex(_event_queue_lock); + e.type = Common::EVENT_KEYDOWN; + _event_queue.push(e); + e.type = Common::EVENT_KEYUP; _event_queue.push(e); unlockMutex(_event_queue_lock); @@ -554,7 +564,7 @@ void OSystem_Android::pushEvent(int type, int arg1, int arg2, int arg3, } scaleMouse(e.mouse, arg3 - _touch_pt_scroll.x, - arg4 - _touch_pt_scroll.y, false); + arg4 - _touch_pt_scroll.y, false, true); e.mouse += _touch_pt_down; clipMouse(e.mouse); } else { @@ -652,7 +662,7 @@ void OSystem_Android::pushEvent(int type, int arg1, int arg2, int arg3, if (_touchpad_mode) { scaleMouse(e.mouse, arg1 - _touch_pt_dt.x, - arg2 - _touch_pt_dt.y, false); + arg2 - _touch_pt_dt.y, false, true); e.mouse += _touch_pt_down; clipMouse(e.mouse); @@ -757,6 +767,66 @@ void OSystem_Android::pushEvent(int type, int arg1, int arg2, int arg3, return; + case JE_MOUSE_MOVE: + e.type = Common::EVENT_MOUSEMOVE; + + scaleMouse(e.mouse, arg1, arg2); + clipMouse(e.mouse); + + lockMutex(_event_queue_lock); + _event_queue.push(e); + unlockMutex(_event_queue_lock); + + return; + + case JE_LMB_DOWN: + e.type = Common::EVENT_LBUTTONDOWN; + + scaleMouse(e.mouse, arg1, arg2); + clipMouse(e.mouse); + + lockMutex(_event_queue_lock); + _event_queue.push(e); + unlockMutex(_event_queue_lock); + + return; + + case JE_LMB_UP: + e.type = Common::EVENT_LBUTTONUP; + + scaleMouse(e.mouse, arg1, arg2); + clipMouse(e.mouse); + + lockMutex(_event_queue_lock); + _event_queue.push(e); + unlockMutex(_event_queue_lock); + + return; + + case JE_RMB_DOWN: + e.type = Common::EVENT_RBUTTONDOWN; + + scaleMouse(e.mouse, arg1, arg2); + clipMouse(e.mouse); + + lockMutex(_event_queue_lock); + _event_queue.push(e); + unlockMutex(_event_queue_lock); + + return; + + case JE_RMB_UP: + e.type = Common::EVENT_RBUTTONUP; + + scaleMouse(e.mouse, arg1, arg2); + clipMouse(e.mouse); + + lockMutex(_event_queue_lock); + _event_queue.push(e); + unlockMutex(_event_queue_lock); + + return; + case JE_QUIT: e.type = Common::EVENT_QUIT; diff --git a/backends/platform/android/org/scummvm/scummvm/MouseHelper.java b/backends/platform/android/org/scummvm/scummvm/MouseHelper.java new file mode 100644 index 0000000000..999815593f --- /dev/null +++ b/backends/platform/android/org/scummvm/scummvm/MouseHelper.java @@ -0,0 +1,129 @@ +package org.scummvm.scummvm; + +import android.view.InputDevice; +import android.view.MotionEvent; +import android.view.SurfaceView; +import android.view.View; + +/** + * Contains helper methods for mouse/hover events that were introduced in Android 4.0. + */ +public class MouseHelper { + private View.OnHoverListener _listener; + private ScummVM _scummvm; + private long _rmbGuardTime; + private boolean _rmbPressed; + private boolean _lmbPressed; + + /** + * Class initialization fails when this throws an exception. + * Checking hover availability is done on static class initialization for Android 1.6 compatibility. + */ + static { + try { + Class.forName("android.view.View$OnHoverListener"); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + + /** + * Calling this forces class initialization + */ + public static void checkHoverAvailable() {} + + public MouseHelper(ScummVM scummvm) { + _scummvm = scummvm; + _listener = createListener(); + } + + private View.OnHoverListener createListener() { + return new View.OnHoverListener() { + @Override + public boolean onHover(View view, MotionEvent e) { + return onMouseEvent(e, true); + } + }; + } + + public void attach(SurfaceView main_surface) { + main_surface.setOnHoverListener(_listener); + } + + public static boolean isMouse(MotionEvent e) { + if (e == null) { + return false; + } + + InputDevice device = e.getDevice(); + + if (device == null) { + return false; + } + + int sources = device.getSources(); + + return ((sources & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) || + ((sources & InputDevice.SOURCE_STYLUS) == InputDevice.SOURCE_STYLUS) || + ((sources & InputDevice.SOURCE_TOUCHPAD) == InputDevice.SOURCE_TOUCHPAD); + } + + public boolean onMouseEvent(MotionEvent e, boolean hover) { + _scummvm.pushEvent(ScummVMEvents.JE_MOUSE_MOVE, (int)e.getX(), (int)e.getY(), 0, 0, 0); + + int buttonState = e.getButtonState(); + + boolean lmbDown = (buttonState & MotionEvent.BUTTON_PRIMARY) == MotionEvent.BUTTON_PRIMARY; + + if (!hover && e.getAction() != MotionEvent.ACTION_UP && buttonState == 0) { + // On some device types, ButtonState is 0 even when tapping on the touchpad or using the stylus on the screen etc. + lmbDown = true; + } + + if (lmbDown) { + if (!_lmbPressed) { + // left mouse button was pressed just now + _scummvm.pushEvent(ScummVMEvents.JE_LMB_DOWN, (int)e.getX(), (int)e.getY(), e.getButtonState(), 0, 0); + } + + _lmbPressed = true; + } else { + if (_lmbPressed) { + // left mouse button was released just now + _scummvm.pushEvent(ScummVMEvents.JE_LMB_UP, (int)e.getX(), (int)e.getY(), e.getButtonState(), 0, 0); + } + + _lmbPressed = false; + } + + boolean rmbDown = (buttonState & MotionEvent.BUTTON_SECONDARY) == MotionEvent.BUTTON_SECONDARY; + if (rmbDown) { + if (!_rmbPressed) { + // right mouse button was pressed just now + _scummvm.pushEvent(ScummVMEvents.JE_RMB_DOWN, (int)e.getX(), (int)e.getY(), e.getButtonState(), 0, 0); + } + + _rmbPressed = true; + } else { + if (_rmbPressed) { + // right mouse button was released just now + _scummvm.pushEvent(ScummVMEvents.JE_RMB_UP, (int)e.getX(), (int)e.getY(), e.getButtonState(), 0, 0); + _rmbGuardTime = System.currentTimeMillis(); + } + + _rmbPressed = false; + } + + return true; + } + + /** + * Checks whether right mouse button is pressed or was pressed just previously. This is used to prevent sending + * extra back key on right mouse click which is the default behaviour in some platforms. + * + * @return true if right mouse button is (or was in the last 200ms) pressed + */ + public boolean getRmbGuard() { + return _rmbPressed || _rmbGuardTime + 200 > System.currentTimeMillis(); + } +} diff --git a/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java b/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java index fbd6513761..829a948435 100644 --- a/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java +++ b/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java @@ -18,6 +18,18 @@ import java.io.File; public class ScummVMActivity extends Activity { + /* Establish whether the hover events are available */ + private static boolean _hoverAvailable; + + static { + try { + MouseHelper.checkHoverAvailable(); // this throws exception if we're on too old version + _hoverAvailable = true; + } catch (Throwable t) { + _hoverAvailable = false; + } + } + private class MyScummVM extends ScummVM { private boolean usingSmallScreen() { // Multiple screen sizes came in with Android 1.6. Have @@ -94,6 +106,7 @@ public class ScummVMActivity extends Activity { private MyScummVM _scummvm; private ScummVMEvents _events; + private MouseHelper _mouseHelper; private Thread _scummvm_thread; @Override @@ -147,11 +160,16 @@ public class ScummVMActivity extends Activity { "ScummVM", "--config=" + getFileStreamPath("scummvmrc").getPath(), "--path=" + Environment.getExternalStorageDirectory().getPath(), - "--gui-theme=scummmodern", "--savepath=" + savePath }); - _events = new ScummVMEvents(this, _scummvm); + Log.d(ScummVM.LOG_TAG, "Hover available: " + _hoverAvailable); + if (_hoverAvailable) { + _mouseHelper = new MouseHelper(_scummvm); + _mouseHelper.attach(main_surface); + } + + _events = new ScummVMEvents(this, _scummvm, _mouseHelper); main_surface.setOnKeyListener(_events); main_surface.setOnTouchListener(_events); diff --git a/backends/platform/android/org/scummvm/scummvm/ScummVMEvents.java b/backends/platform/android/org/scummvm/scummvm/ScummVMEvents.java index 86227b9352..5f51ffac6c 100644 --- a/backends/platform/android/org/scummvm/scummvm/ScummVMEvents.java +++ b/backends/platform/android/org/scummvm/scummvm/ScummVMEvents.java @@ -2,7 +2,6 @@ package org.scummvm.scummvm; import android.os.Handler; import android.os.Message; -import android.util.Log; import android.content.Context; import android.view.KeyEvent; import android.view.KeyCharacterMap; @@ -27,16 +26,23 @@ public class ScummVMEvents implements public static final int JE_DOUBLE_TAP = 6; public static final int JE_MULTI = 7; public static final int JE_BALL = 8; + public static final int JE_LMB_DOWN = 9; + public static final int JE_LMB_UP = 10; + public static final int JE_RMB_DOWN = 11; + public static final int JE_RMB_UP = 12; + public static final int JE_MOUSE_MOVE = 13; public static final int JE_QUIT = 0x1000; final protected Context _context; final protected ScummVM _scummvm; final protected GestureDetector _gd; final protected int _longPress; + final protected MouseHelper _mouseHelper; - public ScummVMEvents(Context context, ScummVM scummvm) { + public ScummVMEvents(Context context, ScummVM scummvm, MouseHelper mouseHelper) { _context = context; _scummvm = scummvm; + _mouseHelper = mouseHelper; _gd = new GestureDetector(context, this); _gd.setOnDoubleTapListener(this); @@ -64,7 +70,7 @@ public class ScummVMEvents implements public void handleMessage(Message msg) { if (msg.what == MSG_MENU_LONG_PRESS) { InputMethodManager imm = (InputMethodManager) - _context.getSystemService(_context.INPUT_METHOD_SERVICE); + _context.getSystemService(Context.INPUT_METHOD_SERVICE); if (imm != null) imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); @@ -73,9 +79,30 @@ public class ScummVMEvents implements }; // OnKeyListener + @Override final public boolean onKey(View v, int keyCode, KeyEvent e) { final int action = e.getAction(); + if (keyCode == 238) { + // this (undocumented) event is sent when ACTION_HOVER_ENTER or ACTION_HOVER_EXIT occurs + return false; + } + + if (keyCode == KeyEvent.KEYCODE_BACK) { + if (action != KeyEvent.ACTION_UP) { + // only send event from back button on up event, since down event is sent on right mouse click and + // cannot be caught (thus rmb click would send escape key first) + return true; + } + + if (_mouseHelper != null) { + if (_mouseHelper.getRmbGuard()) { + // right mouse button was just clicked which sends an extra back button press + return true; + } + } + } + if (e.isSystem()) { // filter what we handle switch (keyCode) { @@ -160,7 +187,16 @@ public class ScummVMEvents implements } // OnTouchListener + @Override final public boolean onTouch(View v, MotionEvent e) { + if (_mouseHelper != null) { + boolean isMouse = MouseHelper.isMouse(e); + if (isMouse) { + // mouse button is pressed + return _mouseHelper.onMouseEvent(e, false); + } + } + final int action = e.getAction(); // constants from APIv5: @@ -177,11 +213,13 @@ public class ScummVMEvents implements } // OnGestureListener + @Override final public boolean onDown(MotionEvent e) { _scummvm.pushEvent(JE_DOWN, (int)e.getX(), (int)e.getY(), 0, 0, 0); return true; } + @Override final public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { //Log.d(ScummVM.LOG_TAG, String.format("onFling: %s -> %s (%.3f %.3f)", @@ -191,10 +229,12 @@ public class ScummVMEvents implements return true; } + @Override final public void onLongPress(MotionEvent e) { // disabled, interferes with drag&drop } + @Override final public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { _scummvm.pushEvent(JE_SCROLL, (int)e1.getX(), (int)e1.getY(), @@ -203,9 +243,11 @@ public class ScummVMEvents implements return true; } + @Override final public void onShowPress(MotionEvent e) { } + @Override final public boolean onSingleTapUp(MotionEvent e) { _scummvm.pushEvent(JE_TAP, (int)e.getX(), (int)e.getY(), (int)(e.getEventTime() - e.getDownTime()), 0, 0); @@ -214,10 +256,12 @@ public class ScummVMEvents implements } // OnDoubleTapListener + @Override final public boolean onDoubleTap(MotionEvent e) { return true; } + @Override final public boolean onDoubleTapEvent(MotionEvent e) { _scummvm.pushEvent(JE_DOUBLE_TAP, (int)e.getX(), (int)e.getY(), e.getAction(), 0, 0); @@ -225,6 +269,7 @@ public class ScummVMEvents implements return true; } + @Override final public boolean onSingleTapConfirmed(MotionEvent e) { return true; } diff --git a/backends/platform/android/texture.cpp b/backends/platform/android/texture.cpp index 95c96e0d25..b174e93191 100644 --- a/backends/platform/android/texture.cpp +++ b/backends/platform/android/texture.cpp @@ -52,9 +52,6 @@ // Supported GL extensions static bool npot_supported = false; -#ifdef GL_OES_draw_texture -static bool draw_tex_supported = false; -#endif static inline GLfixed xdiv(int numerator, int denominator) { assert(numerator < (1 << 16)); @@ -85,11 +82,6 @@ void GLESBaseTexture::initGLExtensions() { if (token == "GL_ARB_texture_non_power_of_two") npot_supported = true; - -#ifdef GL_OES_draw_texture - if (token == "GL_OES_draw_texture") - draw_tex_supported = true; -#endif } } @@ -180,45 +172,28 @@ void GLESBaseTexture::allocBuffer(GLuint w, GLuint h) { void GLESBaseTexture::drawTexture(GLshort x, GLshort y, GLshort w, GLshort h) { GLCALL(glBindTexture(GL_TEXTURE_2D, _texture_name)); -#ifdef GL_OES_draw_texture - // Great extension, but only works under specific conditions. - // Still a work-in-progress - disabled for now. - if (false && draw_tex_supported && !hasPalette()) { - //GLCALL(glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE)); - const GLint crop[4] = { 0, _surface.h, _surface.w, -_surface.h }; + const GLfixed tex_width = xdiv(_surface.w, _texture_width); + const GLfixed tex_height = xdiv(_surface.h, _texture_height); + const GLfixed texcoords[] = { + 0, 0, + tex_width, 0, + 0, tex_height, + tex_width, tex_height, + }; - GLCALL(glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop)); + GLCALL(glTexCoordPointer(2, GL_FIXED, 0, texcoords)); - // Android GLES bug? - GLCALL(glColor4ub(0xff, 0xff, 0xff, 0xff)); + const GLshort vertices[] = { + x, y, + x + w, y, + x, y + h, + x + w, y + h, + }; - GLCALL(glDrawTexiOES(x, y, 0, w, h)); - } else -#endif - { - const GLfixed tex_width = xdiv(_surface.w, _texture_width); - const GLfixed tex_height = xdiv(_surface.h, _texture_height); - const GLfixed texcoords[] = { - 0, 0, - tex_width, 0, - 0, tex_height, - tex_width, tex_height, - }; - - GLCALL(glTexCoordPointer(2, GL_FIXED, 0, texcoords)); - - const GLshort vertices[] = { - x, y, - x + w, y, - x, y + h, - x + w, y + h, - }; - - GLCALL(glVertexPointer(2, GL_SHORT, 0, vertices)); - - assert(ARRAYSIZE(vertices) == ARRAYSIZE(texcoords)); - GLCALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, ARRAYSIZE(vertices) / 2)); - } + GLCALL(glVertexPointer(2, GL_SHORT, 0, vertices)); + + assert(ARRAYSIZE(vertices) == ARRAYSIZE(texcoords)); + GLCALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, ARRAYSIZE(vertices) / 2)); clearDirty(); } diff --git a/backends/platform/bada/application.cpp b/backends/platform/bada/application.cpp index ba8e544983..e761649245 100644 --- a/backends/platform/bada/application.cpp +++ b/backends/platform/bada/application.cpp @@ -103,7 +103,7 @@ void BadaScummVM::pauseGame(bool pause) { if (pause && g_engine && !g_engine->isPaused()) { _appForm->pushKey(Common::KEYCODE_SPACE); } - + if (g_system) { ((BadaSystem *)g_system)->setMute(pause); } diff --git a/backends/platform/bada/sscanf.cpp b/backends/platform/bada/sscanf.cpp index b5e5b88cb5..aa846698f6 100644 --- a/backends/platform/bada/sscanf.cpp +++ b/backends/platform/bada/sscanf.cpp @@ -56,7 +56,7 @@ bool scanInt(const char **in, va_list *ap, int max) { bool err = false; if (end == *in || (max > 0 && (end - *in) > max)) { - err = true; + err = true; } else { *arg = (int)n; *in = end; diff --git a/backends/platform/gph/gph-backend.cpp b/backends/platform/gph/gph-backend.cpp index 49a1edf411..485780b472 100644 --- a/backends/platform/gph/gph-backend.cpp +++ b/backends/platform/gph/gph-backend.cpp @@ -20,6 +20,8 @@ * */ +#if defined(GPH_DEVICE) + // Disable symbol overrides so that we can use system headers. #define FORBIDDEN_SYMBOL_ALLOW_ALL @@ -32,8 +34,6 @@ #include "backends/saves/default/default-saves.h" #include "backends/timer/default/default-timer.h" -#include "base/main.h" - #include "common/archive.h" #include "common/config-manager.h" #include "common/debug.h" @@ -64,23 +64,6 @@ void OSystem_GPH::initBackend() { assert(!_inited); - // Create the events manager - if (_eventSource == 0) - _eventSource = new GPHEventSource(); - - // Create the graphics manager - if (_graphicsManager == 0) { - _graphicsManager = new GPHGraphicsManager(_eventSource); - } - - // Create the mixer manager - if (_mixer == 0) { - _mixerManager = new DoubleBufferSDLMixerManager(); - - // Setup and start mixer - _mixerManager->init(); - } - /* Setup default save path to be workingdir/saves */ char savePath[PATH_MAX+1]; @@ -165,16 +148,42 @@ void OSystem_GPH::initBackend() { /* Make sure that aspect ratio correction is enabled on the 1st run to stop users asking me what the 'wasted space' at the bottom is ;-). */ ConfMan.registerDefault("aspect_ratio", true); + ConfMan.registerDefault("fullscreen", true); /* Make sure SDL knows that we have a joystick we want to use. */ ConfMan.setInt("joystick_num", 0); + // Create the events manager + if (_eventSource == 0) + _eventSource = new GPHEventSource(); + + // Create the graphics manager + if (_graphicsManager == 0) { + _graphicsManager = new GPHGraphicsManager(_eventSource); + } + /* Pass to POSIX method to do the heavy lifting */ OSystem_POSIX::initBackend(); _inited = true; } +void OSystem_GPH::initSDL() { + // Check if SDL has not been initialized + if (!_initedSDL) { + + uint32 sdlFlags = SDL_INIT_EVENTTHREAD; + if (ConfMan.hasKey("disable_sdl_parachute")) + sdlFlags |= SDL_INIT_NOPARACHUTE; + + // Initialize SDL (SDL Subsystems are initiliazed in the corresponding sdl managers) + if (SDL_Init(sdlFlags) == -1) + error("Could not initialize SDL: %s", SDL_GetError()); + + _initedSDL = true; + } +} + void OSystem_GPH::addSysArchivesToSearchSet(Common::SearchSet &s, int priority) { /* Setup default extra data paths for engine data files and plugins */ @@ -222,3 +231,5 @@ void OSystem_GPH::quit() { OSystem_POSIX::quit(); } + +#endif diff --git a/backends/platform/gph/gph-main.cpp b/backends/platform/gph/gph-main.cpp index 2c43af151f..876de0f358 100644 --- a/backends/platform/gph/gph-main.cpp +++ b/backends/platform/gph/gph-main.cpp @@ -21,7 +21,7 @@ */ #include "backends/platform/gph/gph.h" -#include "backends/plugins/posix/posix-provider.h" +#include "backends/plugins/sdl/sdl-provider.h" #include "base/main.h" #if defined(GPH_DEVICE) @@ -36,7 +36,7 @@ int main(int argc, char *argv[]) { ((OSystem_GPH *)g_system)->init(); #ifdef DYNAMIC_MODULES - PluginManager::instance().addPluginProvider(new POSIXPluginProvider()); + PluginManager::instance().addPluginProvider(new SDLPluginProvider()); #endif // Invoke the actual ScummVM main entry point: diff --git a/backends/platform/gph/gph.h b/backends/platform/gph/gph.h index 80f43f0bab..90a798154f 100644 --- a/backends/platform/gph/gph.h +++ b/backends/platform/gph/gph.h @@ -26,13 +26,11 @@ #if defined(GPH_DEVICE) #include "backends/base-backend.h" -#include "backends/platform/sdl/sdl.h" +#include "backends/platform/sdl/sdl-sys.h" #include "backends/platform/sdl/posix/posix.h" #include "backends/events/gph/gph-events.h" #include "backends/graphics/gph/gph-graphics.h" -#define __GP2XWIZ__ - #ifndef PATH_MAX #define PATH_MAX 255 #endif @@ -45,6 +43,11 @@ public: void addSysArchivesToSearchSet(Common::SearchSet &s, int priority); void initBackend(); void quit(); + +protected: + bool _inited; + bool _initedSDL; + virtual void initSDL(); }; #endif diff --git a/backends/platform/iphone/iphone_video.mm b/backends/platform/iphone/iphone_video.mm index 7877bc6430..0bfae30fc7 100644 --- a/backends/platform/iphone/iphone_video.mm +++ b/backends/platform/iphone/iphone_video.mm @@ -161,9 +161,9 @@ const char *iPhone_getDocumentsDir() { - (id)initWithFrame:(struct CGRect)frame { self = [super initWithFrame: frame]; - if ([[UIScreen mainScreen] respondsToSelector: NSSelectorFromString(@"scale")]) { - if ([self respondsToSelector: NSSelectorFromString(@"contentScaleFactor")]) { - //self.contentScaleFactor = [[UIScreen mainScreen] scale]; + if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) { + if ([self respondsToSelector:@selector(setContentScaleFactor:)]) { + [self setContentScaleFactor:[[UIScreen mainScreen] scale]]; } } @@ -268,6 +268,11 @@ const char *iPhone_getDocumentsDir() { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); printOpenGLError(); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter); printOpenGLError(); + // We use GL_CLAMP_TO_EDGE here to avoid artifacts when linear filtering + // is used. If we would not use this for example the cursor in Loom would + // have a line/border artifact on the right side of the covered rect. + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); printOpenGLError(); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); printOpenGLError(); } - (void)setGraphicsMode { @@ -476,7 +481,7 @@ const char *iPhone_getDocumentsDir() { else if (_videoContext.screenWidth == 640 && _videoContext.screenHeight == 400) adjustedHeight = 480; } - + float overlayPortraitRatio; if (_orientation == UIDeviceOrientationLandscapeLeft || _orientation == UIDeviceOrientationLandscapeRight) { diff --git a/backends/platform/iphone/osys_main.cpp b/backends/platform/iphone/osys_main.cpp index f9b2a81ce6..ed2c886213 100644 --- a/backends/platform/iphone/osys_main.cpp +++ b/backends/platform/iphone/osys_main.cpp @@ -65,6 +65,10 @@ OSystem_IPHONE::OSystem_IPHONE() : _touchpadModeEnabled = !iPhone_isHighResDevice(); _fsFactory = new POSIXFilesystemFactory(); initVideoContext(); + + memset(_gamePalette, 0, sizeof(_gamePalette)); + memset(_gamePaletteRGBA5551, 0, sizeof(_gamePaletteRGBA5551)); + memset(_mouseCursorPalette, 0, sizeof(_mouseCursorPalette)); } OSystem_IPHONE::~OSystem_IPHONE() { diff --git a/backends/platform/iphone/osys_video.mm b/backends/platform/iphone/osys_video.mm index aa1856490f..a11bf32c54 100644 --- a/backends/platform/iphone/osys_video.mm +++ b/backends/platform/iphone/osys_video.mm @@ -148,6 +148,11 @@ void OSystem_IPHONE::setPalette(const byte *colors, uint start, uint num) { } dirtyFullScreen(); + + // Automatically update the mouse texture when the palette changes while the + // cursor palette is disabled. + if (!_mouseCursorPaletteEnabled && _mouseBuffer.format.bytesPerPixel == 1) + _mouseDirty = _mouseNeedTextureUpdate = true; } void OSystem_IPHONE::grabPalette(byte *colors, uint start, uint num) { @@ -353,7 +358,7 @@ void OSystem_IPHONE::copyRectToOverlay(const void *buf, int pitch, int x, int y, } byte *dst = (byte *)_videoContext->overlayTexture.getBasePtr(x, y); - do { + do { memcpy(dst, src, w * sizeof(uint16)); src += pitch; dst += _videoContext->overlayTexture.pitch; @@ -435,7 +440,7 @@ void OSystem_IPHONE::setCursorPalette(const byte *colors, uint start, uint num) for (uint i = start; i < start + num; ++i, colors += 3) _mouseCursorPalette[i] = Graphics::RGBToColor<Graphics::ColorMasks<5551> >(colors[0], colors[1], colors[2]); - + // FIXME: This is just stupid, our client code seems to assume that this // automatically enables the cursor palette. _mouseCursorPaletteEnabled = true; diff --git a/backends/platform/maemo/debian/changelog b/backends/platform/maemo/debian/changelog index f3e4c4eadb..ea44574e96 100644 --- a/backends/platform/maemo/debian/changelog +++ b/backends/platform/maemo/debian/changelog @@ -8,7 +8,7 @@ scummvm (1.5.0) unstable; urgency=low * 1.5.0 release - -- Tarek Soliman <tsoliman@scummvm.org> Tue, 10 Jul 2012 22:57:32 -0500 + -- Tarek Soliman <tsoliman@scummvm.org> Fri, 20 Jul 2012 14:48:44 -0500 scummvm (1.4.1) unstable; urgency=low diff --git a/backends/platform/maemo/debian/control b/backends/platform/maemo/debian/control index 6e1dfe2fd4..bdaccd2359 100644 --- a/backends/platform/maemo/debian/control +++ b/backends/platform/maemo/debian/control @@ -2,7 +2,7 @@ Source: scummvm Section: user/games Priority: optional Maintainer: Tarek Soliman <tsoliman@scummvm.org> -Build-Depends: debhelper (>> 4.0.0), libsdl1.2-dev, libmad0-dev, libasound2-dev, libvorbisidec-dev, libmpeg2-4-dev, libflac-dev (>= 1.1.2), libz-dev, quilt +Build-Depends: debhelper (>> 4.0.0), libsdl1.2-dev, libmad0-dev, libasound2-dev, libvorbisidec-dev, libmpeg2-4-dev, libflac-dev (>= 1.1.2), libfreetype6-dev, libz-dev, quilt Standards-Version: 3.6.1.1 Package: scummvm diff --git a/backends/platform/maemo/debian/rules b/backends/platform/maemo/debian/rules index 64add08de8..c713403876 100755 --- a/backends/platform/maemo/debian/rules +++ b/backends/platform/maemo/debian/rules @@ -47,10 +47,10 @@ install: build install -m0644 gui/themes/scummclassic.zip gui/themes/scummmodern.zip debian/scummvm/opt/scummvm/share install -m0644 backends/vkeybd/packs/vkeybd_default.zip debian/scummvm/opt/scummvm/share # for optified version we can also add engine datafiles - install -m0644 dists/engine-data/drascula.dat dists/engine-data/hugo.dat dists/engine-data/kyra.dat dists/engine-data/lure.dat dists/engine-data/queen.tbl dists/engine-data/sky.cpt dists/engine-data/teenagent.dat dists/engine-data/toon.dat debian/scummvm/opt/scummvm/share + install -m0644 dists/engine-data/drascula.dat dists/engine-data/hugo.dat dists/engine-data/kyra.dat dists/engine-data/lure.dat dists/engine-data/queen.tbl dists/engine-data/sky.cpt dists/engine-data/teenagent.dat dists/engine-data/tony.dat dists/engine-data/toon.dat debian/scummvm/opt/scummvm/share install -m0644 -d debian/scummvm/usr/share/doc/scummvm - install -m0644 NEWS README COPYRIGHT debian/scummvm/usr/share/doc/scummvm + install -m0644 AUTHORS COPYING COPYING.BSD COPYING.FREEFONT COPYING.LGPL COPYRIGHT NEWS README debian/scummvm/usr/share/doc/scummvm binary: binary-arch binary-arch: build install diff --git a/backends/platform/maemo/maemo.cpp b/backends/platform/maemo/maemo.cpp index e296d4787c..6bd229177b 100644 --- a/backends/platform/maemo/maemo.cpp +++ b/backends/platform/maemo/maemo.cpp @@ -43,6 +43,7 @@ namespace Maemo { OSystem_SDL_Maemo::OSystem_SDL_Maemo() : + _eventObserver(0), OSystem_POSIX() { } @@ -84,6 +85,9 @@ static void registerDefaultKeyBindings(Common::KeymapperDefaultBindings *_keymap #endif void OSystem_SDL_Maemo::initBackend() { + ConfMan.registerDefault("fullscreen", true); + ConfMan.registerDefault("aspect_ratio", true); + // Create the events manager if (_eventSource == 0) _eventSource = new MaemoSdlEventSource(); @@ -180,6 +184,7 @@ void OSystem_SDL_Maemo::setupIcon() { // http://bugzilla.libsdl.org/show_bug.cgi?id=586 } +#ifdef ENABLE_KEYMAPPER static const Common::KeyTableEntry maemoKeys[] = { // Function keys {"MENU", Common::KEYCODE_F11, 0, "Menu", false}, @@ -191,7 +196,6 @@ static const Common::KeyTableEntry maemoKeys[] = { {0, Common::KEYCODE_INVALID, 0, 0, false} }; -#ifdef ENABLE_KEYMAPPER Common::HardwareInputSet *OSystem_SDL_Maemo::getHardwareInputSet() { return new Common::HardwareInputSet(true, maemoKeys); } diff --git a/backends/platform/n64/osys_n64.h b/backends/platform/n64/osys_n64.h index 249f72d8fc..bc6b3cb1a5 100644 --- a/backends/platform/n64/osys_n64.h +++ b/backends/platform/n64/osys_n64.h @@ -104,9 +104,7 @@ protected: bool _cursorPaletteDisabled; bool _dirtyPalette; - // FIXME: This must be left as "int" for now, to fix the sign-comparison problem - // there is a little more work involved than an int->uint change - int _cursorWidth, _cursorHeight; + uint _cursorWidth, _cursorHeight; int _cursorKeycolor; diff --git a/backends/platform/n64/osys_n64_base.cpp b/backends/platform/n64/osys_n64_base.cpp index 7d6f8f0b5c..1e2aca9e51 100644 --- a/backends/platform/n64/osys_n64_base.cpp +++ b/backends/platform/n64/osys_n64_base.cpp @@ -100,8 +100,8 @@ OSystem_N64::OSystem_N64() { _cursor_pal = NULL; _cursor_hic = NULL; - _cursorWidth = -1; - _cursorHeight = -1; + _cursorWidth = 0; + _cursorHeight = 0; _cursorKeycolor = -1; _mouseHotspotX = _mouseHotspotY = -1; @@ -575,19 +575,20 @@ void OSystem_N64::updateScreen() { horiz_pix_skip = skip_pixels; } - int mX = _mouseX - _mouseHotspotX; - int mY = _mouseY - _mouseHotspotY; + for (uint h = 0; h < _cursorHeight; h++) { + for (uint w = 0; w < _cursorWidth; w++) { + int posX = (_mouseX - _mouseHotspotX) + w; + int posY = (_mouseY - _mouseHotspotY) + h; - for (int h = 0; h < _cursorHeight; h++) - for (int w = 0; w < _cursorWidth; w++) { // Draw pixel - if (((mY + h) >= 0) && ((mY + h) < _mouseMaxY) && ((mX + w) >= 0) && ((mX + w) < _mouseMaxX)) { + if ((posY >= 0) && (posY < _mouseMaxY) && (posX >= 0) && (posX < _mouseMaxX)) { uint16 cursor_pixel_hic = _cursor_hic[(h * _cursorWidth) + w]; if (!(cursor_pixel_hic & 0x00001)) - mouse_framebuffer[((mY + h) * _frameBufferWidth) + ((mX + w) + _offscrPixels + horiz_pix_skip)] = cursor_pixel_hic; + mouse_framebuffer[(posY * _frameBufferWidth) + (posX + _offscrPixels + horiz_pix_skip)] = cursor_pixel_hic; } } + } } #ifndef _ENABLE_DEBUG_ @@ -724,7 +725,7 @@ void OSystem_N64::copyRectToOverlay(const void *buf, int pitch, int x, int y, in uint16 *dst = _overlayBuffer + (y * _overlayWidth + x); - if (_overlayWidth == w && pitch == _overlayWidth * sizeof(uint16)) { + if (_overlayWidth == (uint16)w && (uint16)pitch == _overlayWidth * sizeof(uint16)) { memcpy(dst, src, h * pitch); } else { do { diff --git a/backends/platform/openpandora/op-backend.cpp b/backends/platform/openpandora/op-backend.cpp index dcec387f97..354aa24b24 100644 --- a/backends/platform/openpandora/op-backend.cpp +++ b/backends/platform/openpandora/op-backend.cpp @@ -54,53 +54,15 @@ /* Dump console info to files. */ #define DUMP_STDOUT -static SDL_Cursor *hiddenCursor; - OSystem_OP::OSystem_OP() : OSystem_POSIX() { } -//static Uint32 timer_handler(Uint32 interval, void *param) { -// ((DefaultTimerManager *)param)->handler(); -// return interval; -//} - void OSystem_OP::initBackend() { assert(!_inited); - // Create the events manager - if (_eventSource == 0) - _eventSource = new OPEventSource(); - - // Create the graphics manager - if (_graphicsManager == 0) { - _graphicsManager = new OPGraphicsManager(_eventSource); - } - -// int joystick_num = ConfMan.getInt("joystick_num"); -// uint32 sdlFlags = SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER; -// -// if (ConfMan.hasKey("disable_sdl_parachute")) -// sdlFlags |= SDL_INIT_NOPARACHUTE; -// -// if (joystick_num > -1) -// sdlFlags |= SDL_INIT_JOYSTICK; -// -// if (SDL_Init(sdlFlags) == -1) { -// error("Could not initialize SDL: %s", SDL_GetError()); -// } -// - - // Create the mixer manager -// if (_mixer == 0) { -// _mixerManager = new DoubleBufferSDLMixerManager(); - - // Setup and start mixer -// _mixerManager->init(); -// } - /* Setup default save path to be workingdir/saves */ char savePath[PATH_MAX+1]; @@ -179,7 +141,14 @@ void OSystem_OP::initBackend() { /* Make sure SDL knows that we have a joystick we want to use. */ ConfMan.setInt("joystick_num", 0); -// _graphicsMutex = createMutex(); + // Create the events manager + if (_eventSource == 0) + _eventSource = new OPEventSource(); + + // Create the graphics manager + if (_graphicsManager == 0) { + _graphicsManager = new OPGraphicsManager(_eventSource); + } /* Pass to POSIX method to do the heavy lifting */ OSystem_POSIX::initBackend(); @@ -187,24 +156,6 @@ void OSystem_OP::initBackend() { _inited = true; } -// enable joystick -// if (joystick_num > -1 && SDL_NumJoysticks() > 0) { -// printf("Using joystick: %s\n", SDL_JoystickName(0)); -// _joystick = SDL_JoystickOpen(joystick_num); -// } -// -// setupMixer(); - -// Note: We could implement a custom SDLTimerManager by using -// SDL_AddTimer. That might yield better timer resolution, but it would -// also change the semantics of a timer: Right now, ScummVM timers -// *never* run in parallel, due to the way they are implemented. If we -// switched to SDL_AddTimer, each timer might run in a separate thread. -// However, not all our code is prepared for that, so we can't just -// switch. Still, it's a potential future change to keep in mind. -// _timer = new DefaultTimerManager(); -// _timerID = SDL_AddTimer(10, &timer_handler, _timer); - void OSystem_OP::initSDL() { // Check if SDL has not been initialized if (!_initedSDL) { @@ -217,38 +168,7 @@ void OSystem_OP::initSDL() { if (SDL_Init(sdlFlags) == -1) error("Could not initialize SDL: %s", SDL_GetError()); - uint8_t hiddenCursorData = 0; - - hiddenCursor = SDL_CreateCursor(&hiddenCursorData, &hiddenCursorData, 8, 1, 0, 0); - - /* On the OpenPandora we need to work around an SDL assumption that - returns relative mouse coordinates when you get to the screen - edges using the touchscreen. The workaround is to set a blank - SDL cursor and not disable it (Hackish I know). - - The root issues likes in the Windows Manager GRAB code in SDL. - That is why the issue is not seen on framebuffer devices like the - GP2X (there is no X window manager ;)). - */ - SDL_ShowCursor(SDL_ENABLE); - SDL_SetCursor(hiddenCursor); - SDL_EnableUNICODE(1); - -// memset(&_oldVideoMode, 0, sizeof(_oldVideoMode)); -// memset(&_videoMode, 0, sizeof(_videoMode)); -// memset(&_transactionDetails, 0, sizeof(_transactionDetails)); - -// _videoMode.mode = GFX_DOUBLESIZE; -// _videoMode.scaleFactor = 2; -// _videoMode.aspectRatioCorrection = ConfMan.getBool("aspect_ratio"); -// _scalerProc = Normal2x; -// _scalerType = 0; - -// _videoMode.fullscreen = true; - _initedSDL = true; - -// OSystem_POSIX::initSDL(); } } @@ -275,8 +195,6 @@ void OSystem_OP::addSysArchivesToSearchSet(Common::SearchSet &s, int priority) { void OSystem_OP::quit() { - SDL_FreeCursor(hiddenCursor); - #ifdef DUMP_STDOUT printf("%s\n", "Debug: STDOUT and STDERR text files closed."); fclose(stdout); diff --git a/backends/platform/openpandora/op-options.cpp b/backends/platform/openpandora/op-options.cpp index 58f0fb7188..005a76b76c 100644 --- a/backends/platform/openpandora/op-options.cpp +++ b/backends/platform/openpandora/op-options.cpp @@ -33,7 +33,8 @@ enum { /* Touchscreen TapMode */ TAPMODE_LEFT = 0, TAPMODE_RIGHT = 1, - TAPMODE_HOVER = 2 + TAPMODE_HOVER = 2, + TAPMODE_HOVER_DPAD = 3 }; int tapmodeLevel = TAPMODE_LEFT; @@ -44,6 +45,8 @@ void ToggleTapMode() { } else if (tapmodeLevel == TAPMODE_RIGHT) { tapmodeLevel = TAPMODE_HOVER; } else if (tapmodeLevel == TAPMODE_HOVER) { + tapmodeLevel = TAPMODE_HOVER_DPAD; + } else if (tapmodeLevel == TAPMODE_HOVER_DPAD) { tapmodeLevel = TAPMODE_LEFT; } else { tapmodeLevel = TAPMODE_LEFT; diff --git a/backends/platform/openpandora/op-sdl.h b/backends/platform/openpandora/op-sdl.h index 8cccbb5f86..1eddad5c4a 100644 --- a/backends/platform/openpandora/op-sdl.h +++ b/backends/platform/openpandora/op-sdl.h @@ -31,8 +31,6 @@ #include "backends/events/openpandora/op-events.h" #include "backends/graphics/openpandora/op-graphics.h" -//#define MIXER_DOUBLE_BUFFERING 1 - #ifndef PATH_MAX #define PATH_MAX 255 #endif diff --git a/backends/platform/ps2/Gs2dScreen.cpp b/backends/platform/ps2/Gs2dScreen.cpp index f93166ef67..e818305c29 100644 --- a/backends/platform/ps2/Gs2dScreen.cpp +++ b/backends/platform/ps2/Gs2dScreen.cpp @@ -102,8 +102,7 @@ int vblankEndHandler(int cause) { void createAnimThread(Gs2dScreen *screen); -Gs2dScreen::Gs2dScreen(uint16 width, uint16 height, TVMode tvMode) { - +Gs2dScreen::Gs2dScreen(uint16 width, uint16 height, TVMode mode) { _systemQuit = false; ee_sema_t newSema; newSema.init_count = 1; @@ -118,7 +117,7 @@ Gs2dScreen::Gs2dScreen(uint16 width, uint16 height, TVMode tvMode) { _vblankStartId = AddIntcHandler(INT_VBLANK_START, vblankStartHandler, 0); _vblankEndId = AddIntcHandler(INT_VBLANK_END, vblankEndHandler, 0); - _dmacId = AddDmacHandler(2, dmacHandler, 0); + _dmacId = AddDmacHandler(2, dmacHandler, 0); _dmaPipe = new DmaPipe(0x2000); @@ -139,7 +138,7 @@ Gs2dScreen::Gs2dScreen(uint16 width, uint16 height, TVMode tvMode) { _clut[1] = GS_RGBA(0xC0, 0xC0, 0xC0, 0); clearOverlay(); - if (tvMode == TV_DONT_CARE) { + if (mode == TV_DONT_CARE) { #if 1 char romver[8]; int fd = fioOpen("rom0:ROMVER", O_RDONLY); @@ -157,12 +156,12 @@ Gs2dScreen::Gs2dScreen(uint16 width, uint16 height, TVMode tvMode) { _tvMode = TV_NTSC; #endif } else - _tvMode = tvMode; + _tvMode = mode; // _tvMode = TV_NTSC; printf("Setting up %s mode\n", (_tvMode == TV_PAL) ? "PAL" : "NTSC"); - // set screen size, 640x512 for pal, 640x448 for ntsc + // set screen size, 640x512 for pal, 640x448 for ntsc _tvWidth = 640; _tvHeight = ((_tvMode == TV_PAL) ? 512 /*544*/ : 448); kFullScreen[0].z = kFullScreen[1].z = 0; @@ -186,8 +185,8 @@ Gs2dScreen::Gs2dScreen(uint16 width, uint16 height, TVMode tvMode) { _clutPtrs[MOUSE] = _clutPtrs[SCREEN] + 0x1000; // the cluts in PSMCT32 take up half a memory page each _clutPtrs[TEXT] = _clutPtrs[SCREEN] + 0x2000; _texPtrs[SCREEN] = _clutPtrs[SCREEN] + 0x3000; - _texPtrs[TEXT] = 0; // these buffers are stored in the alpha gaps of the frame buffers - _texPtrs[MOUSE] = 128 * 256 * 4; + _texPtrs[TEXT] = 0; // these buffers are stored in the alpha gaps of the frame buffers + _texPtrs[MOUSE] = 128 * 256 * 4; _texPtrs[PRINTF] = _texPtrs[MOUSE] + M_SIZE * M_SIZE * 4; _showOverlay = false; @@ -201,14 +200,14 @@ Gs2dScreen::Gs2dScreen(uint16 width, uint16 height, TVMode tvMode) { _overlayFormat.bytesPerPixel = 2; _overlayFormat.rLoss = 3; - _overlayFormat.gLoss = 3; - _overlayFormat.bLoss = 3; - _overlayFormat.aLoss = 7; + _overlayFormat.gLoss = 3; + _overlayFormat.bLoss = 3; + _overlayFormat.aLoss = 7; - _overlayFormat.rShift = 0; - _overlayFormat.gShift = 5; - _overlayFormat.bShift = 10; - _overlayFormat.aShift = 15; + _overlayFormat.rShift = 0; + _overlayFormat.gShift = 5; + _overlayFormat.bShift = 10; + _overlayFormat.aShift = 15; // setup hardware now. GS_CSR = CSR_RESET; // Reset GS @@ -249,18 +248,18 @@ Gs2dScreen::Gs2dScreen(uint16 width, uint16 height, TVMode tvMode) { createAnimTextures(); - // create anim thread - ee_thread_t animThread, thisThread; + // create animation thread + ee_thread_t animationThread, thisThread; ReferThreadStatus(GetThreadId(), &thisThread); _animStack = malloc(ANIM_STACK_SIZE); - animThread.initial_priority = thisThread.current_priority - 3; - animThread.stack = _animStack; - animThread.stack_size = ANIM_STACK_SIZE; - animThread.func = (void *)runAnimThread; - animThread.gp_reg = &_gp; + animationThread.initial_priority = thisThread.current_priority - 3; + animationThread.stack = _animStack; + animationThread.stack_size = ANIM_STACK_SIZE; + animationThread.func = (void *)runAnimThread; + animationThread.gp_reg = &_gp; - _animTid = CreateThread(&animThread); + _animTid = CreateThread(&animationThread); assert(_animTid >= 0); StartThread(_animTid, this); } @@ -268,13 +267,13 @@ Gs2dScreen::Gs2dScreen(uint16 width, uint16 height, TVMode tvMode) { void Gs2dScreen::quit(void) { _systemQuit = true; ee_thread_t statAnim; - do { // wait until thread called ExitThread() + do { // wait until thread called ExitThread() SignalSema(g_AnimSema); ReferThreadStatus(_animTid, &statAnim); } while (statAnim.status != 0x10); DeleteThread(_animTid); free(_animStack); - _dmaPipe->waitForDma(); // wait for dmac and vblank for the last time + _dmaPipe->waitForDma(); // wait for dmac and vblank for the last time while (g_DmacCmd || g_VblankCmd); sioprintf("kill handlers\n"); @@ -606,7 +605,7 @@ void Gs2dScreen::grabOverlay(byte *buf, uint16 pitch) { for (uint32 cnt = 0; cnt < _height; cnt++) { memcpy(buf, src, _width * 2); buf += pitch; - src += _width; + src += _width; } } diff --git a/backends/platform/ps2/Gs2dScreen.h b/backends/platform/ps2/Gs2dScreen.h index 005dabc809..1a70dad170 100644 --- a/backends/platform/ps2/Gs2dScreen.h +++ b/backends/platform/ps2/Gs2dScreen.h @@ -29,7 +29,6 @@ #include "backends/platform/ps2/DmaPipe.h" #include "graphics/surface.h" - enum TVMode { TV_DONT_CARE = 0, TV_PAL, @@ -41,10 +40,9 @@ enum GsInterlace { GS_INTERLACED }; - class Gs2dScreen { public: - Gs2dScreen(uint16 width, uint16 height, TVMode tvMode); + Gs2dScreen(uint16 width, uint16 height, TVMode mode); ~Gs2dScreen(void); void newScreenSize(uint16 width, uint16 height); uint8 tvMode(void); @@ -94,27 +92,26 @@ private: uint8 _curDrawBuf; uint32 _frameBufPtr[2]; // - uint32 _clutPtrs[3]; // vram pointers + uint32 _clutPtrs[3]; // vram pointers uint32 _texPtrs[4]; // Graphics::Surface _framebuffer; - /* TODO : check if we do need this */ - struct VideoState { - bool setup; + // TODO : check if we do need this + struct VideoState { + bool setup; - bool fullscreen; - bool aspectRatio; + bool fullscreen; + bool aspectRatio; - int mode; - int scaleFactor; + int mode; + int scaleFactor; - int screenWidth, screenHeight; - int overlayWidth, overlayHeight; - }; + int screenWidth, screenHeight; + int overlayWidth, overlayHeight; + }; VideoState _videoMode; - /* */ uint16 _width, _height, _pitch; int16 _mouseX, _mouseY, _hotSpotX, _hotSpotY; diff --git a/backends/platform/ps2/ps2pad.cpp b/backends/platform/ps2/ps2pad.cpp index b6afc217e6..607b614691 100644 --- a/backends/platform/ps2/ps2pad.cpp +++ b/backends/platform/ps2/ps2pad.cpp @@ -51,6 +51,9 @@ void Ps2Pad::initPad(void) { } else { if (checkPadReady(_port, _slot)) { switch (_padStatus) { + case STAT_NONE: + printf("Pad Status is None. Shouldn't happen\n"); + break; case STAT_OPEN: _padStatus = STAT_DETECT; break; diff --git a/backends/platform/sdl/macosx/appmenu_osx.mm b/backends/platform/sdl/macosx/appmenu_osx.mm index 97c7edba3e..0d2a2ab7f2 100644 --- a/backends/platform/sdl/macosx/appmenu_osx.mm +++ b/backends/platform/sdl/macosx/appmenu_osx.mm @@ -35,9 +35,8 @@ - (void)setAppleMenu:(NSMenu *)menu; @end -NSString *constructNSStringFromCString(const char* rawCString, NSStringEncoding stringEncoding) { - NSData *nsData = [NSData dataWithBytes:rawCString length:strlen(rawCString)]; - return [[NSString alloc] initWithData:nsData encoding:stringEncoding]; +NSString *constructNSStringFromCString(const char *rawCString, CFStringEncoding stringEncoding) { + return (NSString *)CFStringCreateWithCString(NULL, rawCString, stringEncoding); } void replaceApplicationMenuItems() { @@ -59,11 +58,11 @@ void replaceApplicationMenuItems() { // Get current encoding #ifdef USE_TRANSLATION - nsString = constructNSStringFromCString((TransMan.getCurrentCharset()).c_str(), NSASCIIStringEncoding); - NSStringEncoding stringEncoding = CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding((CFStringRef)nsString)); + nsString = constructNSStringFromCString(TransMan.getCurrentCharset().c_str(), NSASCIIStringEncoding); + CFStringEncoding stringEncoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)nsString); [nsString release]; #else - NSStringEncoding stringEncoding = NSASCIIStringEncoding; + CFStringEncoding stringEncoding = kCFStringEncodingASCII; #endif // Add "About ScummVM" menu item diff --git a/backends/platform/sdl/macosx/macosx.cpp b/backends/platform/sdl/macosx/macosx.cpp index 639bd980f6..85342d62fd 100644 --- a/backends/platform/sdl/macosx/macosx.cpp +++ b/backends/platform/sdl/macosx/macosx.cpp @@ -39,7 +39,6 @@ #include "ApplicationServices/ApplicationServices.h" // for LSOpenFSRef #include "CoreFoundation/CoreFoundation.h" // for CF* stuff -#include "CoreServices/CoreServices.h" // for FSPathMakeRef OSystem_MacOSX::OSystem_MacOSX() : @@ -107,13 +106,9 @@ bool OSystem_MacOSX::displayLogFile() { if (_logFilePath.empty()) return false; - FSRef ref; - OSStatus err; - - err = FSPathMakeRef((const UInt8 *)_logFilePath.c_str(), &ref, NULL); - if (err == noErr) { - err = LSOpenFSRef(&ref, NULL); - } + CFURLRef url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8 *)_logFilePath.c_str(), _logFilePath.size(), false); + OSStatus err = LSOpenCFURLRef(url, NULL); + CFRelease(url); return err != noErr; } @@ -156,7 +151,7 @@ Common::String OSystem_MacOSX::getSystemLanguage() const { } CFRelease(preferredLocalizations); } - + } // Falback to POSIX implementation return OSystem_POSIX::getSystemLanguage(); diff --git a/backends/platform/sdl/macosx/macosx.h b/backends/platform/sdl/macosx/macosx.h index 4837e643b3..d9cb28b973 100644 --- a/backends/platform/sdl/macosx/macosx.h +++ b/backends/platform/sdl/macosx/macosx.h @@ -32,7 +32,7 @@ public: virtual bool hasFeature(Feature f); virtual bool displayLogFile(); - + virtual Common::String getSystemLanguage() const; virtual void initBackend(); diff --git a/backends/platform/sdl/main.cpp b/backends/platform/sdl/main.cpp deleted file mode 100644 index 040028079d..0000000000 --- a/backends/platform/sdl/main.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* 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 "common/scummsys.h" - -// Several SDL based ports use a custom main, and hence do not want to compile -// of this file. The following "#if" ensures that. -#if !defined(POSIX) && \ - !defined(WIN32) && \ - !defined(MAEMO) && \ - !defined(__SYMBIAN32__) && \ - !defined(_WIN32_WCE) && \ - !defined(__amigaos4__) && \ - !defined(DINGUX) && \ - !defined(CAANOO) && \ - !defined(LINUXMOTO) && \ - !defined(SAMSUNGTV) && \ - !defined(PLAYSTATION3) && \ - !defined(OPENPANDORA) - -#include "backends/platform/sdl/sdl.h" -#include "backends/plugins/sdl/sdl-provider.h" -#include "base/main.h" - -int main(int argc, char *argv[]) { - - // Create our OSystem instance - g_system = new OSystem_SDL(); - assert(g_system); - - // Pre initialize the backend - ((OSystem_SDL *)g_system)->init(); - -#ifdef DYNAMIC_MODULES - PluginManager::instance().addPluginProvider(new SDLPluginProvider()); -#endif - - // Invoke the actual ScummVM main entry point: - int res = scummvm_main(argc, argv); - - // Free OSystem - delete (OSystem_SDL *)g_system; - - return res; -} - -#endif diff --git a/backends/platform/sdl/module.mk b/backends/platform/sdl/module.mk index 98a8265301..a17a326889 100644 --- a/backends/platform/sdl/module.mk +++ b/backends/platform/sdl/module.mk @@ -1,7 +1,6 @@ MODULE := backends/platform/sdl MODULE_OBJS := \ - main.o \ sdl.o ifdef POSIX diff --git a/backends/platform/symbian/README b/backends/platform/symbian/README index 31bc3d8fce..58cbc7814a 100644 --- a/backends/platform/symbian/README +++ b/backends/platform/symbian/README @@ -1,7 +1,7 @@ ScummVM - ScummVM ported to EPOC/SymbianOS - Copyright (C) 2008-2012 ScummVM Team + Copyright (C) 2008-2013 ScummVM Team Copyright (C) 2003-2008 Lars 'AnotherGuest' Persson Copyright (C) 2002-2008 Jurgen 'SumthinWicked' Braam diff --git a/backends/platform/symbian/S60/ScummVM_S60.mmp.in b/backends/platform/symbian/S60/ScummVM_S60.mmp.in index 81068ba073..28bd11dec6 100644 --- a/backends/platform/symbian/S60/ScummVM_S60.mmp.in +++ b/backends/platform/symbian/S60/ScummVM_S60.mmp.in @@ -2,7 +2,7 @@ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL * Copyright (C) 2003-2005 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System * Copyright (C) 2005 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer - * Copyright (C) 2005-2012 The ScummVM project + * Copyright (C) 2005-2013 The ScummVM project * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT diff --git a/backends/platform/symbian/S60/ScummVM_S60_App.mmp b/backends/platform/symbian/S60/ScummVM_S60_App.mmp index e00987e2ad..38d6f19590 100644 --- a/backends/platform/symbian/S60/ScummVM_S60_App.mmp +++ b/backends/platform/symbian/S60/ScummVM_S60_App.mmp @@ -2,7 +2,7 @@ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL * Copyright (C) 2003-2005 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System * Copyright (C) 2005 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer - * Copyright (C) 2005-2012 The ScummVM project + * Copyright (C) 2005-2013 The ScummVM project * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT diff --git a/backends/platform/symbian/S60v3/ScummVM_A0000658_S60v3.mmp.in b/backends/platform/symbian/S60v3/ScummVM_A0000658_S60v3.mmp.in index ccf38818dc..93636aca25 100644 --- a/backends/platform/symbian/S60v3/ScummVM_A0000658_S60v3.mmp.in +++ b/backends/platform/symbian/S60v3/ScummVM_A0000658_S60v3.mmp.in @@ -2,7 +2,7 @@ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL * Copyright (C) 2003-2005 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System * Copyright (C) 2005 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer - * Copyright (C) 2005-2012 The ScummVM project + * Copyright (C) 2005-2013 The ScummVM project * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT diff --git a/backends/platform/symbian/S60v3/ScummVM_S60v3.mmp.in b/backends/platform/symbian/S60v3/ScummVM_S60v3.mmp.in index 0162061284..c09934a46d 100644 --- a/backends/platform/symbian/S60v3/ScummVM_S60v3.mmp.in +++ b/backends/platform/symbian/S60v3/ScummVM_S60v3.mmp.in @@ -2,7 +2,7 @@ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL * Copyright (C) 2003-2005 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System * Copyright (C) 2005 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer - * Copyright (C) 2005-2012 The ScummVM project + * Copyright (C) 2005-2013 The ScummVM project * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT diff --git a/backends/platform/symbian/S80/ScummVM_S80.mmp.in b/backends/platform/symbian/S80/ScummVM_S80.mmp.in index 7987ccd639..d9b9a5c948 100644 --- a/backends/platform/symbian/S80/ScummVM_S80.mmp.in +++ b/backends/platform/symbian/S80/ScummVM_S80.mmp.in @@ -2,7 +2,7 @@ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL * Copyright (C) 2003-2005 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System * Copyright (C) 2005 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer - * Copyright (C) 2005-2012 The ScummVM project + * Copyright (C) 2005-2013 The ScummVM project * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT diff --git a/backends/platform/symbian/S80/ScummVM_S80_App.mmp b/backends/platform/symbian/S80/ScummVM_S80_App.mmp index b66bef7518..30b1c3f58b 100644 --- a/backends/platform/symbian/S80/ScummVM_S80_App.mmp +++ b/backends/platform/symbian/S80/ScummVM_S80_App.mmp @@ -2,7 +2,7 @@ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL * Copyright (C) 2003-2005 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System * Copyright (C) 2005 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer - * Copyright (C) 2005-2012 The ScummVM project + * Copyright (C) 2005-2013 The ScummVM project * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT diff --git a/backends/platform/symbian/S90/Scummvm_S90.mmp.in b/backends/platform/symbian/S90/Scummvm_S90.mmp.in index d803ce5647..790dca14f0 100644 --- a/backends/platform/symbian/S90/Scummvm_S90.mmp.in +++ b/backends/platform/symbian/S90/Scummvm_S90.mmp.in @@ -2,7 +2,7 @@ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL * Copyright (C) 2003-2005 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System * Copyright (C) 2005 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer - * Copyright (C) 2005-2012 The ScummVM project + * Copyright (C) 2005-2013 The ScummVM project * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT diff --git a/backends/platform/symbian/S90/Scummvm_S90_App.mmp b/backends/platform/symbian/S90/Scummvm_S90_App.mmp index 0d8d2b8710..cf17f103ef 100644 --- a/backends/platform/symbian/S90/Scummvm_S90_App.mmp +++ b/backends/platform/symbian/S90/Scummvm_S90_App.mmp @@ -2,7 +2,7 @@ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL * Copyright (C) 2003-2005 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System * Copyright (C) 2005 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer - * Copyright (C) 2005-2012 The ScummVM project + * Copyright (C) 2005-2013 The ScummVM project * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT diff --git a/backends/platform/symbian/UIQ2/ScummVM.rss b/backends/platform/symbian/UIQ2/ScummVM.rss index a6ba4021e4..bfdd07e898 100644 --- a/backends/platform/symbian/UIQ2/ScummVM.rss +++ b/backends/platform/symbian/UIQ2/ScummVM.rss @@ -2,7 +2,7 @@ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL * Copyright (C) 2003-2005 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System * Copyright (C) 2005 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer - * Copyright (C) 2005-2012 The ScummVM project + * Copyright (C) 2005-2013 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 diff --git a/backends/platform/symbian/UIQ3/ScummVM.rss b/backends/platform/symbian/UIQ3/ScummVM.rss index 00ed4e3b5c..b7f0a17113 100644 --- a/backends/platform/symbian/UIQ3/ScummVM.rss +++ b/backends/platform/symbian/UIQ3/ScummVM.rss @@ -2,7 +2,7 @@ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL * Copyright (C) 2003-2005 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System * Copyright (C) 2005 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer - * Copyright (C) 2005-2012 The ScummVM project + * Copyright (C) 2005-2013 The ScummVM project * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT diff --git a/backends/platform/symbian/UIQ3/ScummVM_A0000658.rss b/backends/platform/symbian/UIQ3/ScummVM_A0000658.rss index 00ed4e3b5c..b7f0a17113 100644 --- a/backends/platform/symbian/UIQ3/ScummVM_A0000658.rss +++ b/backends/platform/symbian/UIQ3/ScummVM_A0000658.rss @@ -2,7 +2,7 @@ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL * Copyright (C) 2003-2005 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System * Copyright (C) 2005 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer - * Copyright (C) 2005-2012 The ScummVM project + * Copyright (C) 2005-2013 The ScummVM project * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT diff --git a/backends/platform/symbian/UIQ3/ScummVM_A0000658_UIQ3.mmp.in b/backends/platform/symbian/UIQ3/ScummVM_A0000658_UIQ3.mmp.in index 9e419ad6d9..255bc0f862 100644 --- a/backends/platform/symbian/UIQ3/ScummVM_A0000658_UIQ3.mmp.in +++ b/backends/platform/symbian/UIQ3/ScummVM_A0000658_UIQ3.mmp.in @@ -2,7 +2,7 @@ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL * Copyright (C) 2003-2009 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System * Copyright (C) 2009 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer - * Copyright (C) 2005-2012 The ScummVM project + * Copyright (C) 2005-2013 The ScummVM project * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT diff --git a/backends/platform/symbian/UIQ3/ScummVM_UIQ3.mmp.in b/backends/platform/symbian/UIQ3/ScummVM_UIQ3.mmp.in index 41452127ca..83d782d1a6 100644 --- a/backends/platform/symbian/UIQ3/ScummVM_UIQ3.mmp.in +++ b/backends/platform/symbian/UIQ3/ScummVM_UIQ3.mmp.in @@ -2,7 +2,7 @@ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL * Copyright (C) 2003-2005 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System * Copyright (C) 2005 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer - * Copyright (C) 2005-2012 The ScummVM project + * Copyright (C) 2005-2013 The ScummVM project * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT diff --git a/backends/platform/symbian/UIQ3/scummvm_A0000658_loc.rss b/backends/platform/symbian/UIQ3/scummvm_A0000658_loc.rss index 9af9a33a75..47e7c44642 100644 --- a/backends/platform/symbian/UIQ3/scummvm_A0000658_loc.rss +++ b/backends/platform/symbian/UIQ3/scummvm_A0000658_loc.rss @@ -2,7 +2,7 @@ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL * Copyright (C) 2003-2005 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System * Copyright (C) 2005 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer - * Copyright (C) 2005-2012 The ScummVM project + * Copyright (C) 2005-2013 The ScummVM project * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT diff --git a/backends/platform/symbian/mmp/scummvm_agi.mmp.in b/backends/platform/symbian/mmp/scummvm_agi.mmp.in index 7d197f786f..892ed57732 100644 --- a/backends/platform/symbian/mmp/scummvm_agi.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_agi.mmp.in @@ -2,7 +2,7 @@ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL * Copyright (C) 2003-2005 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System * Copyright (C) 2005 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer - * Copyright (C) 2005-2012 The ScummVM project + * Copyright (C) 2005-2013 The ScummVM project * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT diff --git a/backends/platform/symbian/mmp/scummvm_agos.mmp.in b/backends/platform/symbian/mmp/scummvm_agos.mmp.in index 587d1f0b69..d3bc84ed51 100644 --- a/backends/platform/symbian/mmp/scummvm_agos.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_agos.mmp.in @@ -2,7 +2,7 @@ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL * Copyright (C) 2003-2005 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System * Copyright (C) 2005 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer - * Copyright (C) 2005-2012 The ScummVM project + * Copyright (C) 2005-2013 The ScummVM project * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT diff --git a/backends/platform/symbian/mmp/scummvm_base.mmp.in b/backends/platform/symbian/mmp/scummvm_base.mmp.in index 05cf526233..d6dfafd014 100644 --- a/backends/platform/symbian/mmp/scummvm_base.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_base.mmp.in @@ -2,7 +2,7 @@ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL * Copyright (C) 2003-2005 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System * Copyright (C) 2005 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer - * Copyright (C) 2005-2012 The ScummVM project + * Copyright (C) 2005-2013 The ScummVM project * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT diff --git a/backends/platform/symbian/mmp/scummvm_cine.mmp.in b/backends/platform/symbian/mmp/scummvm_cine.mmp.in index 79806eb8c2..cb7143b837 100644 --- a/backends/platform/symbian/mmp/scummvm_cine.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_cine.mmp.in @@ -2,7 +2,7 @@ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL * Copyright (C) 2003-2005 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System * Copyright (C) 2005 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer - * Copyright (C) 2005-2012 The ScummVM project + * Copyright (C) 2005-2013 The ScummVM project * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT diff --git a/backends/platform/symbian/mmp/scummvm_cruise.mmp.in b/backends/platform/symbian/mmp/scummvm_cruise.mmp.in index 53d52c80e7..c26e93dedc 100644 --- a/backends/platform/symbian/mmp/scummvm_cruise.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_cruise.mmp.in @@ -2,7 +2,7 @@ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL * Copyright (C) 2003-2005 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System * Copyright (C) 2005 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer - * Copyright (C) 2005-2012 The ScummVM project + * Copyright (C) 2005-2013 The ScummVM project * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT diff --git a/backends/platform/symbian/mmp/scummvm_draci.mmp.in b/backends/platform/symbian/mmp/scummvm_draci.mmp.in index 9a7c87c963..52f862bc6f 100644 --- a/backends/platform/symbian/mmp/scummvm_draci.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_draci.mmp.in @@ -2,7 +2,7 @@ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL * Copyright (C) 2003-2005 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System * Copyright (C) 2005 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer - * Copyright (C) 2005-2012 The ScummVM project + * Copyright (C) 2005-2013 The ScummVM project * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT diff --git a/backends/platform/symbian/mmp/scummvm_drascula.mmp.in b/backends/platform/symbian/mmp/scummvm_drascula.mmp.in index fcd7ce7585..9ea02fefe8 100644 --- a/backends/platform/symbian/mmp/scummvm_drascula.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_drascula.mmp.in @@ -2,7 +2,7 @@ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL * Copyright (C) 2003-2005 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System * Copyright (C) 2005 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer - * Copyright (C) 2005-2012 The ScummVM project + * Copyright (C) 2005-2013 The ScummVM project * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT diff --git a/backends/platform/symbian/mmp/scummvm_gob.mmp.in b/backends/platform/symbian/mmp/scummvm_gob.mmp.in index 23f110bc7d..906d54b487 100644 --- a/backends/platform/symbian/mmp/scummvm_gob.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_gob.mmp.in @@ -2,7 +2,7 @@ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL * Copyright (C) 2003-2005 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System * Copyright (C) 2005 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer - * Copyright (C) 2005-2012 The ScummVM project + * Copyright (C) 2005-2013 The ScummVM project * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT diff --git a/backends/platform/symbian/mmp/scummvm_groovie.mmp.in b/backends/platform/symbian/mmp/scummvm_groovie.mmp.in index 6bdeb06b10..e7f70bc110 100644 --- a/backends/platform/symbian/mmp/scummvm_groovie.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_groovie.mmp.in @@ -2,7 +2,7 @@ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL * Copyright (C) 2003-2005 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System * Copyright (C) 2005 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer - * Copyright (C) 2005-2012 The ScummVM project + * Copyright (C) 2005-2013 The ScummVM project * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT diff --git a/backends/platform/symbian/mmp/scummvm_hugo.mmp.in b/backends/platform/symbian/mmp/scummvm_hugo.mmp.in index 69888bb0ee..13dd7efa1e 100644 --- a/backends/platform/symbian/mmp/scummvm_hugo.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_hugo.mmp.in @@ -2,7 +2,7 @@ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL * Copyright (C) 2003-2005 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System * Copyright (C) 2005 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer - * Copyright (C) 2005-2012 The ScummVM project + * Copyright (C) 2005-2013 The ScummVM project * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT diff --git a/backends/platform/symbian/mmp/scummvm_kyra.mmp.in b/backends/platform/symbian/mmp/scummvm_kyra.mmp.in index 4a2a87216e..5772bfaad0 100644 --- a/backends/platform/symbian/mmp/scummvm_kyra.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_kyra.mmp.in @@ -2,7 +2,7 @@ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL * Copyright (C) 2003-2005 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System * Copyright (C) 2005 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer - * Copyright (C) 2005-2012 The ScummVM project + * Copyright (C) 2005-2013 The ScummVM project * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT diff --git a/backends/platform/symbian/mmp/scummvm_lastexpress.mmp.in b/backends/platform/symbian/mmp/scummvm_lastexpress.mmp.in index 27ec0b2148..b8db95ea0c 100644 --- a/backends/platform/symbian/mmp/scummvm_lastexpress.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_lastexpress.mmp.in @@ -2,7 +2,7 @@ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL * Copyright (C) 2003-2005 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System * Copyright (C) 2005 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer - * Copyright (C) 2005-2012 The ScummVM project + * Copyright (C) 2005-2013 The ScummVM project * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT diff --git a/backends/platform/symbian/mmp/scummvm_lure.mmp.in b/backends/platform/symbian/mmp/scummvm_lure.mmp.in index 20b938a83f..84c3eecd5e 100644 --- a/backends/platform/symbian/mmp/scummvm_lure.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_lure.mmp.in @@ -2,7 +2,7 @@ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL * Copyright (C) 2003-2005 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System * Copyright (C) 2005 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer - * Copyright (C) 2005-2012 The ScummVM project + * Copyright (C) 2005-2013 The ScummVM project * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT diff --git a/backends/platform/symbian/mmp/scummvm_m4.mmp.in b/backends/platform/symbian/mmp/scummvm_m4.mmp.in index fafd5e1e5f..e69b40ceb0 100644 --- a/backends/platform/symbian/mmp/scummvm_m4.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_m4.mmp.in @@ -2,7 +2,7 @@ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL * Copyright (C) 2003-2005 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System * Copyright (C) 2005 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer - * Copyright (C) 2005-2012 The ScummVM project + * Copyright (C) 2005-2013 The ScummVM project * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT diff --git a/backends/platform/symbian/mmp/scummvm_made.mmp.in b/backends/platform/symbian/mmp/scummvm_made.mmp.in index 4d5ab6cc33..b52d9cc6cb 100644 --- a/backends/platform/symbian/mmp/scummvm_made.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_made.mmp.in @@ -2,7 +2,7 @@ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL * Copyright (C) 2003-2005 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System * Copyright (C) 2005 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer - * Copyright (C) 2005-2012 The ScummVM project + * Copyright (C) 2005-2013 The ScummVM project * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT diff --git a/backends/platform/symbian/mmp/scummvm_mohawk.mmp.in b/backends/platform/symbian/mmp/scummvm_mohawk.mmp.in index 3fc7c4ca5b..5f7bd4e144 100644 --- a/backends/platform/symbian/mmp/scummvm_mohawk.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_mohawk.mmp.in @@ -2,7 +2,7 @@ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL * Copyright (C) 2003-2005 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System * Copyright (C) 2005 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer - * Copyright (C) 2005-2012 The ScummVM project + * Copyright (C) 2005-2013 The ScummVM project * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT diff --git a/backends/platform/symbian/mmp/scummvm_parallaction.mmp.in b/backends/platform/symbian/mmp/scummvm_parallaction.mmp.in index 05578b5994..f8ee389577 100644 --- a/backends/platform/symbian/mmp/scummvm_parallaction.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_parallaction.mmp.in @@ -2,7 +2,7 @@ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL * Copyright (C) 2003-2005 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System * Copyright (C) 2005 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer - * Copyright (C) 2005-2012 The ScummVM project + * Copyright (C) 2005-2013 The ScummVM project * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT diff --git a/backends/platform/symbian/mmp/scummvm_queen.mmp.in b/backends/platform/symbian/mmp/scummvm_queen.mmp.in index bfc0a2f760..f507f482f9 100644 --- a/backends/platform/symbian/mmp/scummvm_queen.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_queen.mmp.in @@ -2,7 +2,7 @@ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL * Copyright (C) 2003-2005 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System * Copyright (C) 2005 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer - * Copyright (C) 2005-2012 The ScummVM project + * Copyright (C) 2005-2013 The ScummVM project * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT diff --git a/backends/platform/symbian/mmp/scummvm_saga.mmp.in b/backends/platform/symbian/mmp/scummvm_saga.mmp.in index 831f02bdb6..cd158556dc 100644 --- a/backends/platform/symbian/mmp/scummvm_saga.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_saga.mmp.in @@ -2,7 +2,7 @@ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL * Copyright (C) 2003-2005 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System * Copyright (C) 2005 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer - * Copyright (C) 2005-2012 The ScummVM project + * Copyright (C) 2005-2013 The ScummVM project * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT diff --git a/backends/platform/symbian/mmp/scummvm_sci.mmp.in b/backends/platform/symbian/mmp/scummvm_sci.mmp.in index 705f8d0c43..5749c66e10 100644 --- a/backends/platform/symbian/mmp/scummvm_sci.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_sci.mmp.in @@ -2,7 +2,7 @@ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL * Copyright (C) 2003-2005 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System * Copyright (C) 2005 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer - * Copyright (C) 2005-2012 The ScummVM project + * Copyright (C) 2005-2013 The ScummVM project * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT diff --git a/backends/platform/symbian/mmp/scummvm_scumm.mmp.in b/backends/platform/symbian/mmp/scummvm_scumm.mmp.in index 6b2ad35594..c07725e002 100644 --- a/backends/platform/symbian/mmp/scummvm_scumm.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_scumm.mmp.in @@ -2,7 +2,7 @@ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL * Copyright (C) 2003-2005 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System * Copyright (C) 2005 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer - * Copyright (C) 2005-2012 The ScummVM project + * Copyright (C) 2005-2013 The ScummVM project * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT diff --git a/backends/platform/symbian/mmp/scummvm_sky.mmp.in b/backends/platform/symbian/mmp/scummvm_sky.mmp.in index 5fdfb56320..b5048106dc 100644 --- a/backends/platform/symbian/mmp/scummvm_sky.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_sky.mmp.in @@ -2,7 +2,7 @@ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL * Copyright (C) 2003-2005 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System * Copyright (C) 2005 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer - * Copyright (C) 2005-2012 The ScummVM project + * Copyright (C) 2005-2013 The ScummVM project * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT diff --git a/backends/platform/symbian/mmp/scummvm_sword1.mmp.in b/backends/platform/symbian/mmp/scummvm_sword1.mmp.in index 075968cf98..578839e8c4 100644 --- a/backends/platform/symbian/mmp/scummvm_sword1.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_sword1.mmp.in @@ -2,7 +2,7 @@ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL * Copyright (C) 2003-2005 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System * Copyright (C) 2005 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer - * Copyright (C) 2005-2012 The ScummVM project + * Copyright (C) 2005-2013 The ScummVM project * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT diff --git a/backends/platform/symbian/mmp/scummvm_sword2.mmp.in b/backends/platform/symbian/mmp/scummvm_sword2.mmp.in index 32ab259ee4..23a055c99c 100644 --- a/backends/platform/symbian/mmp/scummvm_sword2.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_sword2.mmp.in @@ -2,7 +2,7 @@ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL * Copyright (C) 2003-2005 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System * Copyright (C) 2005 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer - * Copyright (C) 2005-2012 The ScummVM project + * Copyright (C) 2005-2013 The ScummVM project * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT diff --git a/backends/platform/symbian/mmp/scummvm_teenagent.mmp.in b/backends/platform/symbian/mmp/scummvm_teenagent.mmp.in index 61c50bd307..58bfa4c337 100644 --- a/backends/platform/symbian/mmp/scummvm_teenagent.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_teenagent.mmp.in @@ -2,7 +2,7 @@ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL * Copyright (C) 2003-2005 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System * Copyright (C) 2005 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer - * Copyright (C) 2005-2012 The ScummVM project + * Copyright (C) 2005-2013 The ScummVM project * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT diff --git a/backends/platform/symbian/mmp/scummvm_tinsel.mmp.in b/backends/platform/symbian/mmp/scummvm_tinsel.mmp.in index 375d948190..4cdd0bd774 100644 --- a/backends/platform/symbian/mmp/scummvm_tinsel.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_tinsel.mmp.in @@ -2,7 +2,7 @@ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL * Copyright (C) 2003-2005 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System * Copyright (C) 2005 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer - * Copyright (C) 2005-2012 The ScummVM project + * Copyright (C) 2005-2013 The ScummVM project * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT diff --git a/backends/platform/symbian/mmp/scummvm_toon.mmp.in b/backends/platform/symbian/mmp/scummvm_toon.mmp.in index d105156107..00f84d2bec 100644 --- a/backends/platform/symbian/mmp/scummvm_toon.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_toon.mmp.in @@ -2,7 +2,7 @@ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL * Copyright (C) 2003-2005 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System * Copyright (C) 2005 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer - * Copyright (C) 2005-2012 The ScummVM project + * Copyright (C) 2005-2013 The ScummVM project * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT diff --git a/backends/platform/symbian/mmp/scummvm_touche.mmp.in b/backends/platform/symbian/mmp/scummvm_touche.mmp.in index 36588e051f..e59dd0cd00 100644 --- a/backends/platform/symbian/mmp/scummvm_touche.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_touche.mmp.in @@ -2,7 +2,7 @@ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL * Copyright (C) 2003-2005 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System * Copyright (C) 2005 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer - * Copyright (C) 2005-2012 The ScummVM project + * Copyright (C) 2005-2013 The ScummVM project * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT diff --git a/backends/platform/symbian/mmp/scummvm_tsage.mmp.in b/backends/platform/symbian/mmp/scummvm_tsage.mmp.in index fb9b075435..cd4aa158dc 100644 --- a/backends/platform/symbian/mmp/scummvm_tsage.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_tsage.mmp.in @@ -2,7 +2,7 @@ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL * Copyright (C) 2003-2005 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System * Copyright (C) 2005 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer - * Copyright (C) 2005-2012 The ScummVM project + * Copyright (C) 2005-2013 The ScummVM project * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT diff --git a/backends/platform/symbian/mmp/scummvm_tucker.mmp.in b/backends/platform/symbian/mmp/scummvm_tucker.mmp.in index f8954e6d21..0ff8eb9920 100644 --- a/backends/platform/symbian/mmp/scummvm_tucker.mmp.in +++ b/backends/platform/symbian/mmp/scummvm_tucker.mmp.in @@ -2,7 +2,7 @@ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL * Copyright (C) 2003-2005 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System * Copyright (C) 2005 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer - * Copyright (C) 2005-2012 The ScummVM project + * Copyright (C) 2005-2013 The ScummVM project * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT diff --git a/backends/platform/symbian/res/ScummVmAif.rss b/backends/platform/symbian/res/ScummVmAif.rss index 3e7a86a3bc..fab2cadbb8 100644 --- a/backends/platform/symbian/res/ScummVmAif.rss +++ b/backends/platform/symbian/res/ScummVmAif.rss @@ -2,7 +2,7 @@ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL * Copyright (C) 2003-2005 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System * Copyright (C) 2005 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer - * Copyright (C) 2005-2012 The ScummVM project + * Copyright (C) 2005-2013 The ScummVM project * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT diff --git a/backends/platform/symbian/res/scummvm.rss b/backends/platform/symbian/res/scummvm.rss index 361f831e3c..7e667f1cf3 100644 --- a/backends/platform/symbian/res/scummvm.rss +++ b/backends/platform/symbian/res/scummvm.rss @@ -2,7 +2,7 @@ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL * Copyright (C) 2003-2005 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System * Copyright (C) 2005 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer - * Copyright (C) 2005-2012 The ScummVM project + * Copyright (C) 2005-2013 The ScummVM project * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT diff --git a/backends/platform/symbian/res/scummvm_A0000658.rss b/backends/platform/symbian/res/scummvm_A0000658.rss index 14d591c990..3325d72249 100644 --- a/backends/platform/symbian/res/scummvm_A0000658.rss +++ b/backends/platform/symbian/res/scummvm_A0000658.rss @@ -2,7 +2,7 @@ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL * Copyright (C) 2003-2005 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System * Copyright (C) 2005 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer - * Copyright (C) 2005-2012 The ScummVM project + * Copyright (C) 2005-2013 The ScummVM project * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT diff --git a/backends/platform/symbian/src/ScummVm.hrh b/backends/platform/symbian/src/ScummVm.hrh index a84664f995..c43a9da118 100644 --- a/backends/platform/symbian/src/ScummVm.hrh +++ b/backends/platform/symbian/src/ScummVm.hrh @@ -2,7 +2,7 @@ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL * Copyright (C) 2003-2005 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System * Copyright (C) 2005 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer - * Copyright (C) 2005-2012 The ScummVM project + * Copyright (C) 2005-2013 The ScummVM project * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT diff --git a/backends/platform/webos/webos.cpp b/backends/platform/webos/webos.cpp index 4ec153a7e9..fc18628235 100644 --- a/backends/platform/webos/webos.cpp +++ b/backends/platform/webos/webos.cpp @@ -45,24 +45,4 @@ void OSystem_SDL_WebOS::initBackend() { OSystem_SDL::initBackend(); } -/** - * Gets the original SDL hardware key set, adds WebOS specific keys and - * returns the new key set. - * - * @return The hardware key set with added webOS specific keys. - */ -#ifdef ENABLE_KEYMAPPER -HardwareInputSet *OSystem_SDL_WebOS::getHardwareInputSet() { - // Get the original SDL hardware key set - HardwareInputSet *inputSet = OSystem_SDL::getHardwareInputSet(); - - // Add WebOS specific keys - inputSet->addHardwareInput(new HardwareInput("FORWARD", - KeyState((KeyCode) 229, 229, 0), "Forward")); - - // Return the modified hardware key set - return inputSet; -} -#endif - #endif diff --git a/backends/platform/webos/webos.h b/backends/platform/webos/webos.h index 8dfa43239c..dda56a70da 100644 --- a/backends/platform/webos/webos.h +++ b/backends/platform/webos/webos.h @@ -31,9 +31,6 @@ public: OSystem_SDL_WebOS(); virtual void initBackend(); -#ifdef ENABLE_KEYMAPPER - virtual Common::HardwareInputSet *getHardwareInputSet(); -#endif }; #endif diff --git a/backends/platform/wii/osystem.cpp b/backends/platform/wii/osystem.cpp index 681675529a..22a6495f8f 100644 --- a/backends/platform/wii/osystem.cpp +++ b/backends/platform/wii/osystem.cpp @@ -39,7 +39,7 @@ OSystem_Wii::OSystem_Wii() : _startup_time(0), - _cursorScale(1), + _cursorDontScale(true), _cursorPaletteDisabled(true), _cursorPalette(NULL), _cursorPaletteDirty(false), diff --git a/backends/platform/wii/osystem.h b/backends/platform/wii/osystem.h index abafa7f642..5d6998d0b6 100644 --- a/backends/platform/wii/osystem.h +++ b/backends/platform/wii/osystem.h @@ -56,7 +56,7 @@ class OSystem_Wii : public EventsBaseBackend, public PaletteManager { private: s64 _startup_time; - int _cursorScale; + bool _cursorDontScale; bool _cursorPaletteDisabled; u16 *_cursorPalette; bool _cursorPaletteDirty; diff --git a/backends/platform/wii/osystem_gfx.cpp b/backends/platform/wii/osystem_gfx.cpp index 90e4d98c6b..a9bcdbb8d1 100644 --- a/backends/platform/wii/osystem_gfx.cpp +++ b/backends/platform/wii/osystem_gfx.cpp @@ -451,7 +451,7 @@ bool OSystem_Wii::needsScreenUpdate() { void OSystem_Wii::updateScreen() { static f32 ar; static gfx_screen_coords_t cc; - static int cs; + static f32 csx, csy; u32 now = getMillis(); if (now - _lastScreenUpdate < 1000 / MAX_FPS) @@ -466,7 +466,6 @@ void OSystem_Wii::updateScreen() { wii_memstats(); #endif - cs = _cursorScale; _lastScreenUpdate = now; if (_overlayVisible || _consoleVisible) @@ -488,12 +487,6 @@ void OSystem_Wii::updateScreen() { if (_gameRunning) ar = gfx_set_ar(4.0 / 3.0); - // ugly, but the modern theme sets a factor of 3, only god knows why - if (cs > 2) - cs = 1; - else - cs *= 2; - if (_overlayDirty) { gfx_tex_convert(&_texOverlay, _overlayPixels); _overlayDirty = false; @@ -503,10 +496,18 @@ void OSystem_Wii::updateScreen() { } if (_mouseVisible) { - cc.x = f32(_mouseX - cs * _mouseHotspotX) * _currentXScale; - cc.y = f32(_mouseY - cs * _mouseHotspotY) * _currentYScale; - cc.w = f32(_texMouse.width) * _currentXScale * cs; - cc.h = f32(_texMouse.height) * _currentYScale * cs; + if (_cursorDontScale) { + csx = 1.0f / _currentXScale; + csy = 1.0f / _currentYScale; + } else { + csx = 1.0f; + csy = 1.0f; + } + + cc.x = f32(_mouseX - csx * _mouseHotspotX) * _currentXScale; + cc.y = f32(_mouseY - csy * _mouseHotspotY) * _currentYScale; + cc.w = f32(_texMouse.width) * _currentXScale * csx; + cc.h = f32(_texMouse.height) * _currentYScale * csy; if (_texMouse.palette && _cursorPaletteDirty) { _texMouse.palette[_mouseKeyColor] = 0; @@ -608,7 +609,7 @@ void OSystem_Wii::copyRectToOverlay(const void *buf, int pitch, int x, return; uint16 *dst = _overlayPixels + (y * _overlayWidth + x); - if (_overlayWidth == w && pitch == _overlayWidth * sizeof(uint16)) { + if (_overlayWidth == (uint16)w && (uint16)pitch == _overlayWidth * sizeof(uint16)) { memcpy(dst, src, h * pitch); } else { do { @@ -745,8 +746,7 @@ void OSystem_Wii::setMouseCursor(const void *buf, uint w, uint h, int hotspotX, _mouseHotspotX = hotspotX; _mouseHotspotY = hotspotY; - // TODO: Adapt to new dontScale logic! - _cursorScale = 1; + _cursorDontScale = dontScale; if ((_texMouse.palette) && (oldKeycolor != _mouseKeyColor)) _cursorPaletteDirty = true; diff --git a/backends/platform/wince/CEgui/GUIElement.cpp b/backends/platform/wince/CEgui/GUIElement.cpp index 241cf514f3..c8e68b87fd 100644 --- a/backends/platform/wince/CEgui/GUIElement.cpp +++ b/backends/platform/wince/CEgui/GUIElement.cpp @@ -98,19 +98,19 @@ bool GUIElement::drawn() { return _drawn; } -int GUIElement::x() { +int GUIElement::getX() { return _x; } -int GUIElement::y() { +int GUIElement::getY() { return _y; } -int GUIElement::width() { +int GUIElement::getWidth() { return _width; } -int GUIElement::height() { +int GUIElement::getHeight() { return _height; } diff --git a/backends/platform/wince/CEgui/GUIElement.h b/backends/platform/wince/CEgui/GUIElement.h index 2639de38ca..44c4b3f275 100644 --- a/backends/platform/wince/CEgui/GUIElement.h +++ b/backends/platform/wince/CEgui/GUIElement.h @@ -40,10 +40,10 @@ public: virtual bool draw(SDL_Surface *surface); virtual ~GUIElement(); void move(int x, int y); - int width(); - int height(); - int x(); - int y(); + int getWidth(); + int getHeight(); + int getX(); + int getY(); virtual bool action(int x, int y, bool pushed) = 0; bool visible(); bool drawn(); diff --git a/backends/platform/wince/CEgui/ToolbarHandler.cpp b/backends/platform/wince/CEgui/ToolbarHandler.cpp index 6f4ac317cd..f3e42e11fa 100644 --- a/backends/platform/wince/CEgui/ToolbarHandler.cpp +++ b/backends/platform/wince/CEgui/ToolbarHandler.cpp @@ -96,10 +96,10 @@ bool ToolbarHandler::draw(SDL_Surface *surface, SDL_Rect *rect) { if (_active) { bool result = _active->draw(surface); if (result) { - rect->x = _active->x(); - rect->y = _active->y(); - rect->w = _active->width(); - rect->h = _active->height(); + rect->x = _active->getX(); + rect->y = _active->getY(); + rect->w = _active->getWidth(); + rect->h = _active->getHeight(); } return result; } else diff --git a/backends/saves/windows/windows-saves.cpp b/backends/saves/windows/windows-saves.cpp index 87348c3416..d520632394 100644 --- a/backends/saves/windows/windows-saves.cpp +++ b/backends/saves/windows/windows-saves.cpp @@ -26,8 +26,12 @@ #if defined(WIN32) && !defined(_WIN32_WCE) && !defined(DISABLE_DEFAULT_SAVEFILEMANAGER) +#if defined(ARRAYSIZE) +#undef ARRAYSIZE +#endif #define WIN32_LEAN_AND_MEAN #include <windows.h> +#undef ARRAYSIZE // winnt.h defines ARRAYSIZE, but we want our own one... #include "common/config-manager.h" #include "common/savefile.h" diff --git a/backends/taskbar/win32/mingw-compat.h b/backends/taskbar/win32/mingw-compat.h index 55105407c6..f6151936f1 100644 --- a/backends/taskbar/win32/mingw-compat.h +++ b/backends/taskbar/win32/mingw-compat.h @@ -70,7 +70,7 @@ DECLARE_INTERFACE_(IPropertyStore, IUnknown) { STDMETHOD (GetValue) (REFPROPERTYKEY key, PROPVARIANT *pv) PURE; STDMETHOD (SetValue) (REFPROPERTYKEY key, REFPROPVARIANT propvar) PURE; STDMETHOD (Commit) (void) PURE; - + private: ~IPropertyStore(); }; @@ -137,7 +137,7 @@ DECLARE_INTERFACE_(ITaskbarList3, IUnknown) { STDMETHOD (SetOverlayIcon) (THIS_ HWND hwnd, HICON hIcon, LPCWSTR pszDescription) PURE; STDMETHOD (SetThumbnailTooltip) (THIS_ HWND hwnd, LPCWSTR pszTip) PURE; STDMETHOD (SetThumbnailClip) (THIS_ HWND hwnd, RECT *prcClip) PURE; - + private: ~ITaskbarList3(); }; diff --git a/backends/timer/bada/timer.cpp b/backends/timer/bada/timer.cpp index f030e0695f..e41ecd4864 100644 --- a/backends/timer/bada/timer.cpp +++ b/backends/timer/bada/timer.cpp @@ -1,115 +1,115 @@ -/* 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.
- *
- */
-
-#if defined(BADA)
-
-#include "backends/timer/bada/timer.h"
-
-//
-// TimerSlot
-//
-TimerSlot::TimerSlot(Common::TimerManager::TimerProc callback,
- uint32 interval, void *refCon) :
- _timer(0),
- _callback(callback),
- _interval(interval),
- _refCon(refCon) {
-}
-
-TimerSlot::~TimerSlot() {
-}
-
-bool TimerSlot::OnStart() {
- _timer = new Osp::Base::Runtime::Timer();
- if (!_timer || IsFailed(_timer->Construct(*this))) {
- AppLog("Failed to create timer");
- return false;
- }
-
- if (IsFailed(_timer->Start(_interval))) {
- AppLog("failed to start timer");
- return false;
- }
-
- AppLog("started timer %d", _interval);
- return true;
-}
-
-void TimerSlot::OnStop() {
- AppLog("timer stopped");
- if (_timer) {
- _timer->Cancel();
- delete _timer;
- _timer = NULL;
- }
-}
-
-void TimerSlot::OnTimerExpired(Timer &timer) {
- _callback(_refCon);
- timer.Start(_interval);
-}
-
-//
-// BadaTimerManager
-//
-BadaTimerManager::BadaTimerManager() {
-}
-
-BadaTimerManager::~BadaTimerManager() {
- for (Common::List<TimerSlot>::iterator slot = _timers.begin();
- slot != _timers.end(); ) {
- slot->Stop();
- slot = _timers.erase(slot);
- }
-}
-
-bool BadaTimerManager::installTimerProc(TimerProc proc, int32 interval, void *refCon,
- const Common::String &id) {
- TimerSlot *slot = new TimerSlot(proc, interval / 1000, refCon);
-
- if (IsFailed(slot->Construct(THREAD_TYPE_EVENT_DRIVEN))) {
- AppLog("Failed to create timer thread");
- delete slot;
- return false;
- }
-
- if (IsFailed(slot->Start())) {
- delete slot;
- AppLog("Failed to start timer thread");
- return false;
- }
-
- _timers.push_back(*slot);
- return true;
-}
-
-void BadaTimerManager::removeTimerProc(TimerProc proc) {
- for (Common::List<TimerSlot>::iterator slot = _timers.begin();
- slot != _timers.end(); ++slot) {
- if (slot->_callback == proc) {
- slot->Stop();
- slot = _timers.erase(slot);
- }
- }
-}
-
-#endif
+/* 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. + * + */ + +#if defined(BADA) + +#include "backends/timer/bada/timer.h" + +// +// TimerSlot +// +TimerSlot::TimerSlot(Common::TimerManager::TimerProc callback, + uint32 interval, void *refCon) : + _timer(0), + _callback(callback), + _interval(interval), + _refCon(refCon) { +} + +TimerSlot::~TimerSlot() { +} + +bool TimerSlot::OnStart() { + _timer = new Osp::Base::Runtime::Timer(); + if (!_timer || IsFailed(_timer->Construct(*this))) { + AppLog("Failed to create timer"); + return false; + } + + if (IsFailed(_timer->Start(_interval))) { + AppLog("failed to start timer"); + return false; + } + + AppLog("started timer %d", _interval); + return true; +} + +void TimerSlot::OnStop() { + AppLog("timer stopped"); + if (_timer) { + _timer->Cancel(); + delete _timer; + _timer = NULL; + } +} + +void TimerSlot::OnTimerExpired(Timer &timer) { + _callback(_refCon); + timer.Start(_interval); +} + +// +// BadaTimerManager +// +BadaTimerManager::BadaTimerManager() { +} + +BadaTimerManager::~BadaTimerManager() { + for (Common::List<TimerSlot>::iterator slot = _timers.begin(); + slot != _timers.end(); ) { + slot->Stop(); + slot = _timers.erase(slot); + } +} + +bool BadaTimerManager::installTimerProc(TimerProc proc, int32 interval, void *refCon, + const Common::String &id) { + TimerSlot *slot = new TimerSlot(proc, interval / 1000, refCon); + + if (IsFailed(slot->Construct(THREAD_TYPE_EVENT_DRIVEN))) { + AppLog("Failed to create timer thread"); + delete slot; + return false; + } + + if (IsFailed(slot->Start())) { + delete slot; + AppLog("Failed to start timer thread"); + return false; + } + + _timers.push_back(*slot); + return true; +} + +void BadaTimerManager::removeTimerProc(TimerProc proc) { + for (Common::List<TimerSlot>::iterator slot = _timers.begin(); + slot != _timers.end(); ++slot) { + if (slot->_callback == proc) { + slot->Stop(); + slot = _timers.erase(slot); + } + } +} + +#endif diff --git a/backends/timer/bada/timer.h b/backends/timer/bada/timer.h index 04ca771c26..826064d7ff 100644 --- a/backends/timer/bada/timer.h +++ b/backends/timer/bada/timer.h @@ -1,62 +1,62 @@ -/* 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 BADA_TIMER_H
-#define BADA_TIMER_H
-
-#include <FBase.h>
-
-#include "common/timer.h"
-#include "common/list.h"
-
-using namespace Osp::Base::Runtime;
-
-struct TimerSlot: public ITimerEventListener, public Thread {
- TimerSlot(Common::TimerManager::TimerProc callback,
- uint32 interval,
- void *refCon);
- ~TimerSlot();
-
- bool OnStart(void);
- void OnStop(void);
- void OnTimerExpired(Timer &timer);
-
- Timer *_timer;
- Common::TimerManager::TimerProc _callback;
- uint32 _interval; // in microseconds
- void *_refCon;
-};
-
-class BadaTimerManager : public Common::TimerManager {
-public:
- BadaTimerManager();
- ~BadaTimerManager();
-
- bool installTimerProc(TimerProc proc, int32 interval, void *refCon,
- const Common::String &id);
- void removeTimerProc(TimerProc proc);
-
-private:
- Common::List<TimerSlot> _timers;
-};
-
-#endif
+/* 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 BADA_TIMER_H +#define BADA_TIMER_H + +#include <FBase.h> + +#include "common/timer.h" +#include "common/list.h" + +using namespace Osp::Base::Runtime; + +struct TimerSlot: public ITimerEventListener, public Thread { + TimerSlot(Common::TimerManager::TimerProc callback, + uint32 interval, + void *refCon); + ~TimerSlot(); + + bool OnStart(void); + void OnStop(void); + void OnTimerExpired(Timer &timer); + + Timer *_timer; + Common::TimerManager::TimerProc _callback; + uint32 _interval; // in microseconds + void *_refCon; +}; + +class BadaTimerManager : public Common::TimerManager { +public: + BadaTimerManager(); + ~BadaTimerManager(); + + bool installTimerProc(TimerProc proc, int32 interval, void *refCon, + const Common::String &id); + void removeTimerProc(TimerProc proc); + +private: + Common::List<TimerSlot> _timers; +}; + +#endif diff --git a/backends/timer/default/default-timer.cpp b/backends/timer/default/default-timer.cpp index 9cd803f148..9f56d58b12 100644 --- a/backends/timer/default/default-timer.cpp +++ b/backends/timer/default/default-timer.cpp @@ -156,7 +156,7 @@ void DefaultTimerManager::removeTimerProc(TimerProc callback) { } // We need to remove all names referencing the timer proc here. - // + // // Else we run into troubles, when the client code removes and readds timer // callbacks. // diff --git a/backends/updates/macosx/macosx-updates.mm b/backends/updates/macosx/macosx-updates.mm index 741e89891c..f3b221cabf 100644 --- a/backends/updates/macosx/macosx-updates.mm +++ b/backends/updates/macosx/macosx-updates.mm @@ -59,10 +59,14 @@ MacOSXUpdateManager::MacOSXUpdateManager() { [sparkleUpdater setFeedURL:[NSURL URLWithString:feedbackURL]]; // Get current encoding - NSStringEncoding stringEncoding = CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding((CFStringRef)[NSString stringWithCString:(TransMan.getCurrentCharset()).c_str() encoding:NSASCIIStringEncoding])); + CFStringRef encStr = CFStringCreateWithCString(NULL, TransMan.getCurrentCharset().c_str(), kCFStringEncodingASCII); + CFStringEncoding stringEncoding = CFStringConvertIANACharSetNameToEncoding(encStr); + CFRelease(encStr); // Add "Check for Updates..." menu item - NSMenuItem *updateMenuItem = [applicationMenu insertItemWithTitle:[NSString stringWithCString:_("Check for Updates...") encoding:stringEncoding] action:@selector(checkForUpdates:) keyEquivalent:@"" atIndex:1]; + CFStringRef title = CFStringCreateWithCString(NULL, _("Check for Updates..."), stringEncoding); + NSMenuItem *updateMenuItem = [applicationMenu insertItemWithTitle:(NSString *)title action:@selector(checkForUpdates:) keyEquivalent:@"" atIndex:1]; + CFRelease(title); // Set the target of the new menu item [updateMenuItem setTarget:sparkleUpdater]; diff --git a/base/commandLine.cpp b/base/commandLine.cpp index 6fd020cb15..c5c9fe3c90 100644 --- a/base/commandLine.cpp +++ b/base/commandLine.cpp @@ -145,7 +145,7 @@ static const char HELP_STRING[] = static const char *s_appName = "scummvm"; -static void usage(const char *s, ...) GCC_PRINTF(1, 2); +static void NORETURN_PRE usage(const char *s, ...) GCC_PRINTF(1, 2) NORETURN_POST; static void usage(const char *s, ...) { char buf[STRINGBUFLEN]; @@ -237,6 +237,28 @@ void registerDefaults() { ConfMan.registerDefault("record_temp_file_name", "record.tmp"); ConfMan.registerDefault("record_time_file_name", "record.time"); + ConfMan.registerDefault("gui_saveload_chooser", "grid"); + ConfMan.registerDefault("gui_saveload_last_pos", "0"); + +#ifdef USE_FLUIDSYNTH + // The settings are deliberately stored the same way as in Qsynth. The + // FluidSynth music driver is responsible for transforming them into + // their appropriate values. + ConfMan.registerDefault("fluidsynth_chorus_activate", true); + ConfMan.registerDefault("fluidsynth_chorus_nr", 3); + ConfMan.registerDefault("fluidsynth_chorus_level", 100); + ConfMan.registerDefault("fluidsynth_chorus_speed", 30); + ConfMan.registerDefault("fluidsynth_chorus_depth", 80); + ConfMan.registerDefault("fluidsynth_chorus_waveform", "sine"); + + ConfMan.registerDefault("fluidsynth_reverb_activate", true); + ConfMan.registerDefault("fluidsynth_reverb_roomsize", 20); + ConfMan.registerDefault("fluidsynth_reverb_damping", 0); + ConfMan.registerDefault("fluidsynth_reverb_width", 1); + ConfMan.registerDefault("fluidsynth_reverb_level", 90); + + ConfMan.registerDefault("fluidsynth_misc_interpolation", "4th"); +#endif } // @@ -312,8 +334,11 @@ void registerDefaults() { Common::String parseCommandLine(Common::StringMap &settings, int argc, const char * const *argv) { const char *s, *s2; + if (!argv) + return Common::String(); + // argv[0] contains the name of the executable. - if (argv && argv[0]) { + if (argv[0]) { s = strrchr(argv[0], '/'); s_appName = s ? (s+1) : argv[0]; } @@ -577,8 +602,7 @@ static void listGames() { "-------------------- ------------------------------------------------------\n"); const EnginePlugin::List &plugins = EngineMan.getPlugins(); - EnginePlugin::List::const_iterator iter = plugins.begin(); - for (iter = plugins.begin(); iter != plugins.end(); ++iter) { + for (EnginePlugin::List::const_iterator iter = plugins.begin(); iter != plugins.end(); ++iter) { GameList list = (**iter)->getSupportedGames(); for (GameList::iterator v = list.begin(); v != list.end(); ++v) { printf("%-20s %s\n", v->gameid().c_str(), v->description().c_str()); diff --git a/base/main.cpp b/base/main.cpp index 25e1b881cc..355a65f883 100644 --- a/base/main.cpp +++ b/base/main.cpp @@ -57,6 +57,7 @@ #include "graphics/cursorman.h" #include "graphics/fontman.h" +#include "graphics/yuv_to_rgb.h" #ifdef USE_FREETYPE2 #include "graphics/fonts/ttf.h" #endif @@ -511,6 +512,8 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) { #ifdef USE_FREETYPE2 Graphics::shutdownTTF(); #endif + EngineManager::destroy(); + Graphics::YUVToRGBManager::destroy(); return 0; } diff --git a/base/plugins.h b/base/plugins.h index fffb5fb910..4409c9eaea 100644 --- a/base/plugins.h +++ b/base/plugins.h @@ -205,6 +205,10 @@ typedef Common::Array<Plugin *> PluginList; template<class PO_t> class PluginSubclass : public Plugin { public: + PO_t &operator*() const { + return *(PO_t *)_pluginObject; + } + PO_t *operator->() const { return (PO_t *)_pluginObject; } diff --git a/common/array.h b/common/array.h index a2c3023362..ca89523a0b 100644 --- a/common/array.h +++ b/common/array.h @@ -332,7 +332,7 @@ protected: // Copy a part of the new data to the position inside the // initialized space. copy(first, first + (_size - idx), pos); - + // Copy a part of the new data to the position inside the // uninitialized space. uninitialized_copy(first + (_size - idx), last, _storage + _size); diff --git a/common/c++11-compat.h b/common/c++11-compat.h new file mode 100644 index 0000000000..50d79bd79e --- /dev/null +++ b/common/c++11-compat.h @@ -0,0 +1,42 @@ +/* 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 COMMON_CPP11_COMPAT_H +#define COMMON_CPP11_COMPAT_H + +#if __cplusplus >= 201103L +#error "c++11-compat.h included when C++11 is available" +#endif + +// +// Custom nullptr replacement. This is not type safe as the real C++11 nullptr +// though. +// +#define nullptr 0 + +// +// Replacement for the override keyword. This allows compilation of code +// which uses it, but does not feature any semantic. +// +#define override + +#endif diff --git a/common/coroutines.cpp b/common/coroutines.cpp index 042b15b5d7..849b881177 100644 --- a/common/coroutines.cpp +++ b/common/coroutines.cpp @@ -433,9 +433,9 @@ void CoroutineScheduler::waitForMultipleObjects(CORO_PARAM, int nCount, uint32 * // Determine the signalled state _ctx->pidSignalled = (_ctx->pProcess) || !_ctx->pEvent ? false : _ctx->pEvent->signalled; - if (bWaitAll && _ctx->pidSignalled) + if (bWaitAll && !_ctx->pidSignalled) _ctx->signalled = false; - else if (!bWaitAll & _ctx->pidSignalled) + else if (!bWaitAll && _ctx->pidSignalled) _ctx->signalled = true; } @@ -445,7 +445,7 @@ void CoroutineScheduler::waitForMultipleObjects(CORO_PARAM, int nCount, uint32 * for (_ctx->i = 0; _ctx->i < nCount; ++_ctx->i) { _ctx->pEvent = getEvent(pidList[_ctx->i]); - if (_ctx->pEvent->manualReset) + if (!_ctx->pEvent->manualReset) _ctx->pEvent->signalled = false; } @@ -717,12 +717,12 @@ void CoroutineScheduler::pulseEvent(uint32 pidEvent) { EVENT *evt = getEvent(pidEvent); if (!evt) return; - + // Set the event as signalled and pulsing evt->signalled = true; evt->pulsing = true; - // If there's an active process, and it's not the first in the queue, then reschedule all + // If there's an active process, and it's not the first in the queue, then reschedule all // the other prcoesses in the queue to run again this frame if (pCurrent && pCurrent != active->pNext) rescheduleAll(); diff --git a/common/cosinetables.cpp b/common/cosinetables.cpp index bf158e904f..fe8f454e14 100644 --- a/common/cosinetables.cpp +++ b/common/cosinetables.cpp @@ -36,7 +36,7 @@ CosineTable::CosineTable(int bitPrecision) { double freq = 2 * M_PI / m; _table = new float[m]; - // Table contains cos(2*pi*x/n) for 0<=x<=n/4, + // Table contains cos(2*pi*x/n) for 0<=x<=n/4, // followed by its reverse for (int i = 0; i <= m / 4; i++) _table[i] = cos(i * freq); diff --git a/common/endian.h b/common/endian.h index 394437ec67..759513efef 100644 --- a/common/endian.h +++ b/common/endian.h @@ -146,6 +146,12 @@ */ #define MKTAG(a0,a1,a2,a3) ((uint32)((a3) | ((a2) << 8) | ((a1) << 16) | ((a0) << 24))) +/** + * A wrapper macro used around two character constants, like 'wb', to + * ensure portability. Typical usage: MKTAG16('w','b'). + */ +#define MKTAG16(a0,a1) ((uint16)((a1) | ((a0) << 8))) + // Functions for reading/writing native integers. // They also transparently handle the need for alignment. diff --git a/common/forbidden.h b/common/forbidden.h index eec80bba59..9050114442 100644 --- a/common/forbidden.h +++ b/common/forbidden.h @@ -333,11 +333,21 @@ #define isalpha(a) FORBIDDEN_SYMBOL_REPLACEMENT #endif + #ifndef FORBIDDEN_SYMBOL_EXCEPTION_iscntrl + #undef iscntrl + #define iscntrl(a) FORBIDDEN_SYMBOL_REPLACEMENT + #endif + #ifndef FORBIDDEN_SYMBOL_EXCEPTION_isdigit #undef isdigit #define isdigit(a) FORBIDDEN_SYMBOL_REPLACEMENT #endif + #ifndef FORBIDDEN_SYMBOL_EXCEPTION_isgraph + #undef isgraph + #define isgraph(a) FORBIDDEN_SYMBOL_REPLACEMENT + #endif + #ifndef FORBIDDEN_SYMBOL_EXCEPTION_isnumber #undef isnumber #define isnumber(a) FORBIDDEN_SYMBOL_REPLACEMENT @@ -348,6 +358,16 @@ #define islower(a) FORBIDDEN_SYMBOL_REPLACEMENT #endif + #ifndef FORBIDDEN_SYMBOL_EXCEPTION_isprint + #undef isprint + #define isprint(a) FORBIDDEN_SYMBOL_REPLACEMENT + #endif + + #ifndef FORBIDDEN_SYMBOL_EXCEPTION_ispunct + #undef ispunct + #define ispunct(a) FORBIDDEN_SYMBOL_REPLACEMENT + #endif + #ifndef FORBIDDEN_SYMBOL_EXCEPTION_isspace #undef isspace #define isspace(a) FORBIDDEN_SYMBOL_REPLACEMENT @@ -358,6 +378,11 @@ #define isupper(a) FORBIDDEN_SYMBOL_REPLACEMENT #endif + #ifndef FORBIDDEN_SYMBOL_EXCEPTION_isxdigit + #undef isxdigit + #define isxdigit(a) FORBIDDEN_SYMBOL_REPLACEMENT + #endif + #endif // FORBIDDEN_SYMBOL_EXCEPTION_ctype_h #ifndef FORBIDDEN_SYMBOL_EXCEPTION_mkdir diff --git a/common/gui_options.h b/common/gui_options.h index 9da19b1c3e..447fff43ed 100644 --- a/common/gui_options.h +++ b/common/gui_options.h @@ -55,7 +55,7 @@ #define GUIO_RENDERPC9821 "\037" #define GUIO_RENDERPC9801 "\040" -// Special GUIO flags for the AdvancedDetector's caching of game specific +// Special GUIO flags for the AdvancedDetector's caching of game specific // options. #define GUIO_GAMEOPTIONS1 "\041" #define GUIO_GAMEOPTIONS2 "\042" diff --git a/common/iff_container.cpp b/common/iff_container.cpp index 7bcbf86e0f..ffaa5c6d06 100644 --- a/common/iff_container.cpp +++ b/common/iff_container.cpp @@ -22,6 +22,7 @@ #include "common/iff_container.h" #include "common/substream.h" +#include "common/util.h" namespace Common { @@ -75,4 +76,50 @@ void IFFParser::parse(IFFCallback &callback) { } while (!stop); } + +PackBitsReadStream::PackBitsReadStream(Common::ReadStream &input) : _input(&input) { +} + +PackBitsReadStream::~PackBitsReadStream() { +} + +bool PackBitsReadStream::eos() const { + return _input->eos(); +} + +uint32 PackBitsReadStream::read(void *dataPtr, uint32 dataSize) { + byte *out = (byte *)dataPtr; + uint32 left = dataSize; + + uint32 lenR = 0, lenW = 0; + while (left > 0 && !_input->eos()) { + lenR = _input->readByte(); + + if (lenR == 128) { + // no-op + lenW = 0; + } else if (lenR <= 127) { + // literal run + lenR++; + lenW = MIN(lenR, left); + for (uint32 j = 0; j < lenW; j++) { + *out++ = _input->readByte(); + } + for (; lenR > lenW; lenR--) { + _input->readByte(); + } + } else { // len > 128 + // expand run + lenW = MIN((256 - lenR) + 1, left); + byte val = _input->readByte(); + memset(out, val, lenW); + out += lenW; + } + + left -= lenW; + } + + return dataSize - left; +} + } // End of namespace Common diff --git a/common/iff_container.h b/common/iff_container.h index 104ecf0f36..a730930b2c 100644 --- a/common/iff_container.h +++ b/common/iff_container.h @@ -77,22 +77,22 @@ page 376) */ #define ID_copy MKTAG('(','c',')',' ') /* EA IFF 85 Generic Copyright text chunk */ -/* ILBM chunks */ +/* IFF chunks */ #define ID_BMHD MKTAG('B','M','H','D') -/* ILBM BitmapHeader */ +/* IFF BitmapHeader */ #define ID_CMAP MKTAG('C','M','A','P') -/* ILBM 8bit RGB colormap */ +/* IFF 8bit RGB colormap */ #define ID_GRAB MKTAG('G','R','A','B') -/* ILBM "hotspot" coordiantes */ +/* IFF "hotspot" coordiantes */ #define ID_DEST MKTAG('D','E','S','T') -/* ILBM destination image info */ +/* IFF destination image info */ #define ID_SPRT MKTAG('S','P','R','T') -/* ILBM sprite identifier */ +/* IFF sprite identifier */ #define ID_CAMG MKTAG('C','A','M','G') /* Amiga viewportmodes */ #define ID_BODY MKTAG('B','O','D','Y') -/* ILBM image data */ +/* IFF image data */ #define ID_CRNG MKTAG('C','R','N','G') /* color cycling */ #define ID_CCRT MKTAG('C','C','R','T') @@ -114,7 +114,7 @@ page 376) */ #define ID_PCHG MKTAG('P','C','H','G') /* Line by line palette control information (Sebastiano Vigna) */ #define ID_PRVW MKTAG('P','R','V','W') -/* A mini duplicate ILBM used for preview (Gary Bonham) */ +/* A mini duplicate IFF used for preview (Gary Bonham) */ #define ID_XBMI MKTAG('X','B','M','I') /* eXtended BitMap Information (Soft-Logik) */ #define ID_CTBL MKTAG('C','T','B','L') @@ -239,6 +239,31 @@ public: }; +/** + * Decode a given PackBits encoded stream. + * + * PackBits is an RLE compression algorithm introduced by Apple. It is also + * used to encode ILBM and PBM subtypes of IFF files, and some flavors of + * TIFF. + * + * As there is no compression across row boundaries in the above formats, + * read() will extract a *new* line on each call, discarding any alignment + * or padding. + */ +class PackBitsReadStream : public Common::ReadStream { + +protected: + Common::ReadStream *_input; + +public: + PackBitsReadStream(Common::ReadStream &input); + ~PackBitsReadStream(); + + virtual bool eos() const; + + uint32 read(void *dataPtr, uint32 dataSize); +}; + } // namespace Common #endif diff --git a/engines/agos/installshield_cab.cpp b/common/installshield_cab.cpp index d4e636f7b3..e25d14741a 100644 --- a/engines/agos/installshield_cab.cpp +++ b/common/installshield_cab.cpp @@ -43,27 +43,25 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#include "agos/installshield_cab.h" - +#include "common/archive.h" #include "common/debug.h" -#include "common/file.h" +#include "common/hash-str.h" +#include "common/installshield_cab.h" #include "common/memstream.h" #include "common/zlib.h" -namespace AGOS { - -class InstallShieldCabinet : public Common::Archive { - Common::String _installShieldFilename; +namespace Common { +class InstallShieldCabinet : public Archive { public: - InstallShieldCabinet(const Common::String &filename); + InstallShieldCabinet(SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse); ~InstallShieldCabinet(); - // Common::Archive API implementation - bool hasFile(const Common::String &name) const; - int listMembers(Common::ArchiveMemberList &list) const; - const Common::ArchiveMemberPtr getMember(const Common::String &name) const; - Common::SeekableReadStream *createReadStreamForMember(const Common::String &name) const; + // Archive API implementation + bool hasFile(const String &name) const; + int listMembers(ArchiveMemberList &list) const; + const ArchiveMemberPtr getMember(const String &name) const; + SeekableReadStream *createReadStreamForMember(const String &name) const; private: struct FileEntry { @@ -73,53 +71,51 @@ private: uint16 flags; }; - typedef Common::HashMap<Common::String, FileEntry, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> FileMap; + typedef HashMap<String, FileEntry, IgnoreCase_Hash, IgnoreCase_EqualTo> FileMap; FileMap _map; + Common::SeekableReadStream *_stream; + DisposeAfterUse::Flag _disposeAfterUse; }; InstallShieldCabinet::~InstallShieldCabinet() { _map.clear(); -} - -InstallShieldCabinet::InstallShieldCabinet(const Common::String &filename) : _installShieldFilename(filename) { - Common::File installShieldFile; - if (!installShieldFile.open(_installShieldFilename)) { - warning("InstallShieldCabinet::InstallShieldCabinet(): Could not find the archive file %s", _installShieldFilename.c_str()); - return; - } + if (_disposeAfterUse == DisposeAfterUse::YES) + delete _stream; +} +InstallShieldCabinet::InstallShieldCabinet(SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) : _stream(stream), _disposeAfterUse(disposeAfterUse) { // Note that we only support a limited subset of cabinet files // Only single cabinet files and ones without data shared between // cabinets. // Check for the magic uint32 - if (installShieldFile.readUint32LE() != 0x28635349) { + if (_stream->readUint32LE() != 0x28635349) { warning("InstallShieldCabinet::InstallShieldCabinet(): Magic ID doesn't match"); return; } - uint32 version = installShieldFile.readUint32LE(); + uint32 version = _stream->readUint32LE(); if (version != 0x01000004) { warning("Unsupported CAB version %08x", version); return; } - /* uint32 volumeInfo = */ installShieldFile.readUint32LE(); - uint32 cabDescriptorOffset = installShieldFile.readUint32LE(); - /* uint32 cabDescriptorSize = */ installShieldFile.readUint32LE(); + /* uint32 volumeInfo = */ _stream->readUint32LE(); + uint32 cabDescriptorOffset = _stream->readUint32LE(); + /* uint32 cabDescriptorSize = */ _stream->readUint32LE(); - installShieldFile.seek(cabDescriptorOffset); + _stream->seek(cabDescriptorOffset); - installShieldFile.skip(12); - uint32 fileTableOffset = installShieldFile.readUint32LE(); - installShieldFile.skip(4); - uint32 fileTableSize = installShieldFile.readUint32LE(); - uint32 fileTableSize2 = installShieldFile.readUint32LE(); - uint32 directoryCount = installShieldFile.readUint32LE(); - installShieldFile.skip(8); - uint32 fileCount = installShieldFile.readUint32LE(); + _stream->skip(12); + uint32 fileTableOffset = _stream->readUint32LE(); + _stream->skip(4); + uint32 fileTableSize = _stream->readUint32LE(); + uint32 fileTableSize2 = _stream->readUint32LE(); + uint32 directoryCount = _stream->readUint32LE(); + _stream->skip(8); + uint32 fileCount = _stream->readUint32LE(); if (fileTableSize != fileTableSize2) warning("file table sizes do not match"); @@ -127,33 +123,33 @@ InstallShieldCabinet::InstallShieldCabinet(const Common::String &filename) : _in // We're ignoring file groups and components since we // should not need them. Moving on to the files... - installShieldFile.seek(cabDescriptorOffset + fileTableOffset); + _stream->seek(cabDescriptorOffset + fileTableOffset); uint32 fileTableCount = directoryCount + fileCount; uint32 *fileTableOffsets = new uint32[fileTableCount]; for (uint32 i = 0; i < fileTableCount; i++) - fileTableOffsets[i] = installShieldFile.readUint32LE(); + fileTableOffsets[i] = _stream->readUint32LE(); for (uint32 i = directoryCount; i < fileCount + directoryCount; i++) { - installShieldFile.seek(cabDescriptorOffset + fileTableOffset + fileTableOffsets[i]); - uint32 nameOffset = installShieldFile.readUint32LE(); - /* uint32 directoryIndex = */ installShieldFile.readUint32LE(); + _stream->seek(cabDescriptorOffset + fileTableOffset + fileTableOffsets[i]); + uint32 nameOffset = _stream->readUint32LE(); + /* uint32 directoryIndex = */ _stream->readUint32LE(); // First read in data needed by us to get at the file data FileEntry entry; - entry.flags = installShieldFile.readUint16LE(); - entry.uncompressedSize = installShieldFile.readUint32LE(); - entry.compressedSize = installShieldFile.readUint32LE(); - installShieldFile.skip(20); - entry.offset = installShieldFile.readUint32LE(); + entry.flags = _stream->readUint16LE(); + entry.uncompressedSize = _stream->readUint32LE(); + entry.compressedSize = _stream->readUint32LE(); + _stream->skip(20); + entry.offset = _stream->readUint32LE(); // Then let's get the string - installShieldFile.seek(cabDescriptorOffset + fileTableOffset + nameOffset); - Common::String fileName; + _stream->seek(cabDescriptorOffset + fileTableOffset + nameOffset); + String fileName; - char c = installShieldFile.readByte(); + char c = _stream->readByte(); while (c) { fileName += c; - c = installShieldFile.readByte(); + c = _stream->readByte(); } _map[fileName] = entry; } @@ -161,43 +157,39 @@ InstallShieldCabinet::InstallShieldCabinet(const Common::String &filename) : _in delete[] fileTableOffsets; } -bool InstallShieldCabinet::hasFile(const Common::String &name) const { +bool InstallShieldCabinet::hasFile(const String &name) const { return _map.contains(name); } -int InstallShieldCabinet::listMembers(Common::ArchiveMemberList &list) const { +int InstallShieldCabinet::listMembers(ArchiveMemberList &list) const { for (FileMap::const_iterator it = _map.begin(); it != _map.end(); it++) list.push_back(getMember(it->_key)); return _map.size(); } -const Common::ArchiveMemberPtr InstallShieldCabinet::getMember(const Common::String &name) const { - return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(name, this)); +const ArchiveMemberPtr InstallShieldCabinet::getMember(const String &name) const { + return ArchiveMemberPtr(new GenericArchiveMember(name, this)); } -Common::SeekableReadStream *InstallShieldCabinet::createReadStreamForMember(const Common::String &name) const { +SeekableReadStream *InstallShieldCabinet::createReadStreamForMember(const String &name) const { if (!_map.contains(name)) return 0; const FileEntry &entry = _map[name]; - Common::File archiveFile; - archiveFile.open(_installShieldFilename); - archiveFile.seek(entry.offset); + _stream->seek(entry.offset); - if (!(entry.flags & 0x04)) { - // Not compressed - return archiveFile.readStream(entry.uncompressedSize); - } + if (!(entry.flags & 0x04)) // Not compressed + return _stream->readStream(entry.uncompressedSize); #ifdef USE_ZLIB byte *src = (byte *)malloc(entry.compressedSize); byte *dst = (byte *)malloc(entry.uncompressedSize); - archiveFile.read(src, entry.compressedSize); + _stream->read(src, entry.compressedSize); - bool result = Common::inflateZlibHeaderless(dst, entry.uncompressedSize, src, entry.compressedSize); + bool result = inflateZlibInstallShield(dst, entry.uncompressedSize, src, entry.compressedSize); free(src); if (!result) { @@ -206,15 +198,15 @@ Common::SeekableReadStream *InstallShieldCabinet::createReadStreamForMember(cons return 0; } - return new Common::MemoryReadStream(dst, entry.uncompressedSize, DisposeAfterUse::YES); + return new MemoryReadStream(dst, entry.uncompressedSize, DisposeAfterUse::YES); #else warning("zlib required to extract compressed CAB file '%s'", name.c_str()); return 0; #endif } -Common::Archive *makeInstallShieldArchive(const Common::String &name) { - return new InstallShieldCabinet(name); +Archive *makeInstallShieldArchive(SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) { + return new InstallShieldCabinet(stream, disposeAfterUse); } } // End of namespace AGOS diff --git a/common/installshield_cab.h b/common/installshield_cab.h new file mode 100644 index 0000000000..7c4f294578 --- /dev/null +++ b/common/installshield_cab.h @@ -0,0 +1,43 @@ +/* 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 COMMON_INSTALLSHIELD_CAB_H +#define COMMON_INSTALLSHIELD_CAB_H + +#include "common/types.h" + +namespace Common { + +class Archive; +class SeekableReadStream; + +/** + * This factory method creates an Archive instance corresponding to the content + * of the InstallShield compressed stream. + * + * May return 0 in case of a failure. + */ +Archive *makeInstallShieldArchive(SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES); + +} // End of namespace Common + +#endif diff --git a/common/keyboard.h b/common/keyboard.h index f9e94e6656..3262a15c3f 100644 --- a/common/keyboard.h +++ b/common/keyboard.h @@ -224,12 +224,13 @@ enum { KBD_CTRL = 1 << 0, KBD_ALT = 1 << 1, KBD_SHIFT = 1 << 2, - KBD_NON_STICKY = (KBD_CTRL|KBD_ALT|KBD_SHIFT), + KBD_META = 1 << 3, + KBD_NON_STICKY = (KBD_CTRL|KBD_ALT|KBD_SHIFT|KBD_META), // Sticky modifier flags - KBD_NUM = 1 << 3, - KBD_CAPS = 1 << 4, - KBD_SCRL = 1 << 5, + KBD_NUM = 1 << 4, + KBD_CAPS = 1 << 5, + KBD_SCRL = 1 << 6, KBD_STICKY = (KBD_NUM|KBD_CAPS|KBD_SCRL) }; diff --git a/common/macresman.cpp b/common/macresman.cpp index 14bdfa7080..00562f746a 100644 --- a/common/macresman.cpp +++ b/common/macresman.cpp @@ -124,7 +124,7 @@ bool MacResManager::open(String filename) { File *file = new File(); // First, let's try to see if the Mac converted name exists - if (file->open("._" + filename) && loadFromAppleDouble(*file)) { + if (file->open(constructAppleDoubleName(filename)) && loadFromAppleDouble(*file)) { _baseFileName = filename; return true; } @@ -185,7 +185,7 @@ bool MacResManager::open(FSNode path, String filename) { #endif // First, let's try to see if the Mac converted name exists - FSNode fsNode = path.getChild("._" + filename); + FSNode fsNode = path.getChild(constructAppleDoubleName(filename)); if (fsNode.exists() && !fsNode.isDirectory()) { SeekableReadStream *stream = fsNode.createReadStream(); if (loadFromAppleDouble(*stream)) { @@ -253,7 +253,7 @@ bool MacResManager::exists(const String &filename) { return true; // Check if we have an AppleDouble file - if (tempFile.open("._" + filename) && tempFile.readUint32BE() == 0x00051607) + if (tempFile.open(constructAppleDoubleName(filename)) && tempFile.readUint32BE() == 0x00051607) return true; return false; @@ -360,8 +360,8 @@ bool MacResManager::load(SeekableReadStream &stream) { _mapLength = stream.readUint32BE(); // do sanity check - if (_dataOffset >= (uint32)stream.size() || _mapOffset >= (uint32)stream.size() || - _dataLength + _mapLength > (uint32)stream.size()) { + if (stream.eos() || _dataOffset >= (uint32)stream.size() || _mapOffset >= (uint32)stream.size() || + _dataLength + _mapLength > (uint32)stream.size()) { _resForkOffset = -1; _mode = kResForkNone; return false; @@ -574,4 +574,20 @@ void MacResManager::readMap() { } } +Common::String MacResManager::constructAppleDoubleName(Common::String name) { + // Insert "._" before the last portion of a path name + for (int i = name.size() - 1; i >= 0; i--) { + if (i == 0) { + name.insertChar('_', 0); + name.insertChar('.', 0); + } else if (name[i] == '/') { + name.insertChar('_', i + 1); + name.insertChar('.', i + 1); + break; + } + } + + return name; +} + } // End of namespace Common diff --git a/common/macresman.h b/common/macresman.h index 6820106925..ed74da9cc6 100644 --- a/common/macresman.h +++ b/common/macresman.h @@ -25,6 +25,7 @@ * Macintosh resource fork manager used in engines: * - groovie * - mohawk + * - pegasus * - sci * - scumm */ @@ -175,6 +176,8 @@ private: bool loadFromMacBinary(SeekableReadStream &stream); bool loadFromAppleDouble(SeekableReadStream &stream); + static Common::String constructAppleDoubleName(Common::String name); + enum { kResForkNone = 0, kResForkRaw, diff --git a/common/memstream.h b/common/memstream.h index 69fe6ec18e..497a178ab9 100644 --- a/common/memstream.h +++ b/common/memstream.h @@ -92,13 +92,17 @@ private: byte *_ptr; const uint32 _bufSize; uint32 _pos; + bool _err; public: - MemoryWriteStream(byte *buf, uint32 len) : _ptr(buf), _bufSize(len), _pos(0) {} + MemoryWriteStream(byte *buf, uint32 len) : _ptr(buf), _bufSize(len), _pos(0), _err(false) {} uint32 write(const void *dataPtr, uint32 dataSize) { // Write at most as many bytes as are still available... - if (dataSize > _bufSize - _pos) + if (dataSize > _bufSize - _pos) { dataSize = _bufSize - _pos; + // We couldn't write all the data => set error indicator + _err = true; + } memcpy(_ptr, dataPtr, dataSize); _ptr += dataSize; _pos += dataSize; @@ -107,6 +111,9 @@ public: uint32 pos() const { return _pos; } uint32 size() const { return _bufSize; } + + virtual bool err() const { return _err; } + virtual void clearErr() { _err = false; } }; /** diff --git a/common/module.mk b/common/module.mk index 92279740e5..d96b11ee40 100644 --- a/common/module.mk +++ b/common/module.mk @@ -16,6 +16,7 @@ MODULE_OBJS := \ gui_options.o \ hashmap.o \ iff_container.o \ + installshield_cab.o \ language.o \ localization.o \ macresman.o \ diff --git a/common/quicktime.cpp b/common/quicktime.cpp index 173d3c6a97..a3efc2b443 100644 --- a/common/quicktime.cpp +++ b/common/quicktime.cpp @@ -165,6 +165,8 @@ void QuickTimeParser::initParseTable() { { &QuickTimeParser::readWAVE, MKTAG('w', 'a', 'v', 'e') }, { &QuickTimeParser::readESDS, MKTAG('e', 's', 'd', 's') }, { &QuickTimeParser::readSMI, MKTAG('S', 'M', 'I', ' ') }, + { &QuickTimeParser::readDefault, MKTAG('g', 'm', 'h', 'd') }, + { &QuickTimeParser::readLeaf, MKTAG('g', 'm', 'i', 'n') }, { 0, 0 } }; @@ -442,7 +444,7 @@ int QuickTimeParser::readELST(Atom atom) { uint32 offset = 0; - for (uint32 i = 0; i < track->editCount; i++){ + for (uint32 i = 0; i < track->editCount; i++) { track->editList[i].trackDuration = _fd->readUint32BE(); track->editList[i].mediaTime = _fd->readSint32BE(); track->editList[i].mediaRate = Rational(_fd->readUint32BE(), 0x10000); @@ -477,6 +479,8 @@ int QuickTimeParser::readHDLR(Atom atom) { track->codecType = CODEC_TYPE_VIDEO; else if (type == MKTAG('s', 'o', 'u', 'n')) track->codecType = CODEC_TYPE_AUDIO; + else if (type == MKTAG('m', 'u', 's', 'i')) + track->codecType = CODEC_TYPE_MIDI; _fd->readUint32BE(); // component manufacture _fd->readUint32BE(); // component flags @@ -540,7 +544,7 @@ int QuickTimeParser::readSTSD(Atom atom) { _fd->readUint16BE(); // reserved _fd->readUint16BE(); // index - track->sampleDescs[i] = readSampleDesc(track, format); + track->sampleDescs[i] = readSampleDesc(track, format, size - 16); debug(0, "size=%d 4CC= %s codec_type=%d", size, tag2str(format), track->codecType); diff --git a/common/quicktime.h b/common/quicktime.h index 974502d075..caa92578b1 100644 --- a/common/quicktime.h +++ b/common/quicktime.h @@ -35,6 +35,7 @@ #include "common/scummsys.h" #include "common/stream.h" #include "common/rational.h" +#include "common/types.h" namespace Common { class MacResManager; @@ -119,7 +120,8 @@ protected: enum CodecType { CODEC_TYPE_MOV_OTHER, CODEC_TYPE_VIDEO, - CODEC_TYPE_AUDIO + CODEC_TYPE_AUDIO, + CODEC_TYPE_MIDI }; struct Track { @@ -160,14 +162,14 @@ protected: byte objectTypeMP4; }; - virtual SampleDesc *readSampleDesc(Track *track, uint32 format) = 0; + virtual SampleDesc *readSampleDesc(Track *track, uint32 format, uint32 descSize) = 0; uint32 _timeScale; uint32 _duration; Rational _scaleFactorX; Rational _scaleFactorY; Array<Track *> _tracks; - + void init(); private: diff --git a/common/rational.h b/common/rational.h index 45aa6a7a20..8270d2194e 100644 --- a/common/rational.h +++ b/common/rational.h @@ -80,6 +80,9 @@ public: double toDouble() const; frac_t toFrac() const; + int getNumerator() const { return _num; } + int getDenominator() const { return _denom; } + void debugPrint(int debuglevel = 0, const char *caption = "Rational:") const; private: diff --git a/common/rect.h b/common/rect.h index 2bd3affafe..8d1243f7e4 100644 --- a/common/rect.h +++ b/common/rect.h @@ -170,6 +170,20 @@ struct Rect { } /** + * Find the intersecting rectangle between this rectangle and the given rectangle + * + * @param r the intersecting rectangle + * + * @return the intersection of the rectangles or an empty rectangle if not intersecting + */ + Rect findIntersectingRect(const Rect &r) const { + if (!intersects(r)) + return Rect(); + + return Rect(MAX(r.left, left), MAX(r.top, top), MIN(r.right, right), MIN(r.bottom, bottom)); + } + + /** * Extend this rectangle so that it contains r * * @param r the rectangle to extend by diff --git a/common/savefile.h b/common/savefile.h index da787289ee..19536da54f 100644 --- a/common/savefile.h +++ b/common/savefile.h @@ -109,12 +109,12 @@ public: * * Saved games are compressed by default, and engines are expected to * always write compressed saves. - * + * * A notable exception is if uncompressed files are needed for * compatibility with games not supported by ScummVM, such as character * exports from the Quest for Glory series. QfG5 is a 3D game and won't be * supported by ScummVM. - * + * * @param name the name of the savefile * @param compress toggles whether to compress the resulting save file * (default) or not. diff --git a/common/scummsys.h b/common/scummsys.h index 2f4efe702f..cd8a949ce7 100644 --- a/common/scummsys.h +++ b/common/scummsys.h @@ -144,7 +144,10 @@ #endif #endif - +// Include our C++11 compatability header for pre-C++11 compilers. +#if __cplusplus < 201103L +#include "common/c++11-compat.h" +#endif // Use config.h, generated by configure #if defined(HAVE_CONFIG_H) diff --git a/common/sinetables.cpp b/common/sinetables.cpp index a4467383cc..a6ec99469d 100644 --- a/common/sinetables.cpp +++ b/common/sinetables.cpp @@ -36,7 +36,7 @@ SineTable::SineTable(int bitPrecision) { double freq = 2 * M_PI / m; _table = new float[m]; - // Table contains sin(2*pi*x/n) for 0<=x<=n/4, + // Table contains sin(2*pi*x/n) for 0<=x<=n/4, // followed by its reverse for (int i = 0; i <= m / 4; i++) _table[i] = sin(i * freq); diff --git a/common/str.cpp b/common/str.cpp index 84805082ac..8210ca6bb8 100644 --- a/common/str.cpp +++ b/common/str.cpp @@ -764,7 +764,7 @@ String tag2string(uint32 tag) { str[4] = '\0'; // Replace non-printable chars by dot for (int i = 0; i < 4; ++i) { - if (!isprint((unsigned char)str[i])) + if (!Common::isPrint(str[i])) str[i] = '.'; } return String(str); diff --git a/common/util.cpp b/common/util.cpp index 4d9ff11c5c..3d40fffff5 100644 --- a/common/util.cpp +++ b/common/util.cpp @@ -26,6 +26,7 @@ #define FORBIDDEN_SYMBOL_EXCEPTION_islower #define FORBIDDEN_SYMBOL_EXCEPTION_isspace #define FORBIDDEN_SYMBOL_EXCEPTION_isupper +#define FORBIDDEN_SYMBOL_EXCEPTION_isprint #include "common/util.h" @@ -144,4 +145,8 @@ bool isUpper(int c) { return isupper((byte)c); } +bool isPrint(int c) { + ENSURE_ASCII_CHAR(c); + return isprint((byte)c); +} } // End of namespace Common diff --git a/common/util.h b/common/util.h index 78340980d5..4ca1c42929 100644 --- a/common/util.h +++ b/common/util.h @@ -165,6 +165,17 @@ bool isSpace(int c); */ bool isUpper(int c); -} // End of namespace Common +/** + * Test whether the given character is printable. This includes the space + * character (' '). + * + * If the parameter is outside the range of a signed or unsigned char, then + * false is returned. + * + * @param c the character to test + * @return true if the character is printable, false otherwise. + */ +bool isPrint(int c); +} // End of namespace Common #endif diff --git a/common/winexe_pe.cpp b/common/winexe_pe.cpp index 6c0f9c9962..b3c45ffe73 100644 --- a/common/winexe_pe.cpp +++ b/common/winexe_pe.cpp @@ -64,7 +64,7 @@ bool PEResources::loadFromEXE(SeekableReadStream *stream) { if (!stream) return false; - if (stream->readUint16BE() != 'MZ') + if (stream->readUint16BE() != MKTAG16('M', 'Z')) return false; stream->skip(58); diff --git a/common/xmlparser.cpp b/common/xmlparser.cpp index ea3d44cf87..f0b7f1cc81 100644 --- a/common/xmlparser.cpp +++ b/common/xmlparser.cpp @@ -20,15 +20,11 @@ * */ -// FIXME: Avoid using fprintf -#define FORBIDDEN_SYMBOL_EXCEPTION_fprintf -#define FORBIDDEN_SYMBOL_EXCEPTION_stderr - - #include "common/xmlparser.h" #include "common/archive.h" #include "common/fs.h" #include "common/memstream.h" +#include "common/system.h" namespace Common { @@ -123,17 +119,19 @@ bool XMLParser::parserError(const String &errStr) { keyClosing = currentPosition; } - fprintf(stderr, "\n File <%s>, line %d:\n", _fileName.c_str(), lineCount); + Common::String errorMessage = Common::String::format("\n File <%s>, line %d:\n", _fileName.c_str(), lineCount); currentPosition = (keyClosing - keyOpening); _stream->seek(keyOpening, SEEK_SET); while (currentPosition--) - fprintf(stderr, "%c", _stream->readByte()); + errorMessage += (char)_stream->readByte(); + + errorMessage += "\n\nParser error: "; + errorMessage += errStr; + errorMessage += "\n\n"; - fprintf(stderr, "\n\nParser error: "); - fprintf(stderr, "%s", errStr.c_str()); - fprintf(stderr, "\n\n"); + g_system->logMessage(LogMessageType::kError, errorMessage.c_str()); return false; } diff --git a/common/zlib.cpp b/common/zlib.cpp index 7d765fc539..fc8f351054 100644 --- a/common/zlib.cpp +++ b/common/zlib.cpp @@ -85,6 +85,60 @@ bool inflateZlibHeaderless(byte *dst, uint dstLen, const byte *src, uint srcLen, return true; } +enum { + kTempBufSize = 65536 +}; + +bool inflateZlibInstallShield(byte *dst, uint dstLen, const byte *src, uint srcLen) { + if (!dst || !dstLen || !src || !srcLen) + return false; + + // See if we have sync bytes. If so, just use our function for that. + if (srcLen >= 4 && READ_BE_UINT32(src + srcLen - 4) == 0xFFFF) + return inflateZlibHeaderless(dst, dstLen, src, srcLen); + + // Otherwise, we have some custom code we get to use here. + + byte *temp = (byte *)malloc(kTempBufSize); + + uint32 bytesRead = 0, bytesProcessed = 0; + while (bytesRead < srcLen) { + uint16 chunkSize = READ_LE_UINT16(src + bytesRead); + bytesRead += 2; + + // Initialize zlib + z_stream stream; + stream.next_in = const_cast<byte *>(src + bytesRead); + stream.avail_in = chunkSize; + stream.next_out = temp; + stream.avail_out = kTempBufSize; + stream.zalloc = Z_NULL; + stream.zfree = Z_NULL; + stream.opaque = Z_NULL; + + // Negative MAX_WBITS tells zlib there's no zlib header + int err = inflateInit2(&stream, -MAX_WBITS); + if (err != Z_OK) + return false; + + err = inflate(&stream, Z_FINISH); + if (err != Z_OK && err != Z_STREAM_END) { + inflateEnd(&stream); + free(temp); + return false; + } + + memcpy(dst + bytesProcessed, temp, stream.total_out); + bytesProcessed += stream.total_out; + + inflateEnd(&stream); + bytesRead += chunkSize; + } + + free(temp); + return true; +} + /** * A simple wrapper class which can be used to wrap around an arbitrary * other SeekableReadStream and will then provide on-the-fly decompression support. @@ -107,7 +161,7 @@ protected: public: - GZipReadStream(SeekableReadStream *w) : _wrapped(w), _stream() { + GZipReadStream(SeekableReadStream *w, uint32 knownSize = 0) : _wrapped(w), _stream() { assert(w != 0); // Verify file header is correct @@ -122,7 +176,8 @@ public: _origSize = w->readUint32LE(); } else { // Original size not available in zlib format - _origSize = 0; + // use an otherwise known size if supplied. + _origSize = knownSize; } _pos = 0; w->seek(0, SEEK_SET); @@ -336,7 +391,7 @@ public: #endif // USE_ZLIB -SeekableReadStream *wrapCompressedReadStream(SeekableReadStream *toBeWrapped) { +SeekableReadStream *wrapCompressedReadStream(SeekableReadStream *toBeWrapped, uint32 knownSize) { #if defined(USE_ZLIB) if (toBeWrapped) { uint16 header = toBeWrapped->readUint16BE(); @@ -345,7 +400,7 @@ SeekableReadStream *wrapCompressedReadStream(SeekableReadStream *toBeWrapped) { header % 31 == 0)); toBeWrapped->seek(-2, SEEK_CUR); if (isCompressed) - return new GZipReadStream(toBeWrapped); + return new GZipReadStream(toBeWrapped, knownSize); } #endif return toBeWrapped; diff --git a/common/zlib.h b/common/zlib.h index 61322c286a..6a840f5fdc 100644 --- a/common/zlib.h +++ b/common/zlib.h @@ -77,6 +77,25 @@ bool uncompress(byte *dst, unsigned long *dstLen, const byte *src, unsigned long */ bool inflateZlibHeaderless(byte *dst, uint dstLen, const byte *src, uint srcLen, const byte *dict = 0, uint dictLen = 0); +/** + * Wrapper around zlib's inflate functions. This function will call the + * necessary inflate functions to uncompress data compressed for InstallShield + * cabinet files. + * + * Decompresses the src buffer into the dst buffer. + * srcLen is the byte length of the source buffer, dstLen is the byte + * length of the output buffer. + * It decompress as much data as possible, up to dstLen bytes. + * + * @param dst the buffer to store into. + * @param dstLen the size of the destination buffer. + * @param src the data to be decompressed. + * @param dstLen the size of the compressed data. + * + * @return true on success (Z_OK or Z_STREAM_END), false otherwise. + */ +bool inflateZlibInstallShield(byte *dst, uint dstLen, const byte *src, uint srcLen); + #endif /** @@ -86,10 +105,18 @@ bool inflateZlibHeaderless(byte *dst, uint dstLen, const byte *src, uint srcLen, * format. In the former case, the original stream is returned unmodified * (and in particular, not wrapped). * + * Certain GZip-formats don't supply an easily readable length, if you + * still need the length carried along with the stream, and you know + * the decompressed length at wrap-time, then it can be supplied as knownSize + * here. knownSize will be ignored if the GZip-stream DOES include a length. + * * It is safe to call this with a NULL parameter (in this case, NULL is * returned). + * + * @param toBeWrapped the stream to be wrapped (if it is in gzip-format) + * @param knownSize a supplied length of the compressed data (if not available directly) */ -SeekableReadStream *wrapCompressedReadStream(SeekableReadStream *toBeWrapped); +SeekableReadStream *wrapCompressedReadStream(SeekableReadStream *toBeWrapped, uint32 knownSize = 0); /** * Take an arbitrary WriteStream and wrap it in a custom stream which provides @@ -62,7 +62,7 @@ get_var() { eval echo \$${1} } -# Add an engine: id name build subengines +# Add an engine: id name build subengines base-games dependencies add_engine() { _engines="${_engines} ${1}" if test "${3}" = "no" ; then @@ -72,11 +72,22 @@ add_engine() { set_var _engine_${1}_build "${3}" set_var _engine_${1}_build_default "${3}" set_var _engine_${1}_subengines "${4}" + set_var _engine_${1}_base "${5}" + set_var _engine_${1}_deps "${6}" for sub in ${4}; do set_var _engine_${sub}_sub "yes" + set_var _engine_${sub}_parent "${1}" done } +# Add a feature: id name settings-list +add_feature() { + set_var _feature_${1}_name "${2}" + # This is a list of settings, where one must be "yes" for the feature to + # be enabled + set_var _feature_${1}_settings "${3}" +} + _srcdir=`dirname $0` # Read list of engines @@ -85,7 +96,7 @@ _srcdir=`dirname $0` # # Default settings # -# Default lib behaviour yes/no/auto +# Default lib behavior yes/no/auto _vorbis=auto _tremor=auto _tremolo=no @@ -97,7 +108,7 @@ _sndio=auto _timidity=auto _zlib=auto _sparkle=auto -_png=no +_png=auto _theoradec=auto _faad=auto _fluidsynth=auto @@ -108,10 +119,11 @@ _freetype2=auto _taskbar=yes _updates=no _libunity=auto -# Default option behaviour yes/no +# Default option behavior yes/no _debug_build=auto _release_build=auto _optimizations=auto +_use_cxx11=no _verbose_build=no _text_console=no _mt32emu=yes @@ -162,6 +174,17 @@ _endian=unknown _need_memalign=yes _have_x86=no +# Add (virtual) features +add_feature 16bit "16bit color" "_16bit" +add_feature faad "libfaad" "_faad" +add_feature flac "FLAC" "_flac" +add_feature freetype2 "FreeType2" "_freetype2" +add_feature mad "MAD" "_mad" +add_feature png "PNG" "_png" +add_feature theoradec "libtheoradec" "_theoradec" +add_feature vorbis "Vorbis file support" "_vorbis _tremor" +add_feature zlib "zlib" "_zlib" + # Directories for installing ScummVM. @@ -439,6 +462,26 @@ Try \`$0 --help' for more information." >&2 } +# +# Feature handling functions +# + +# Get the name of the feature +get_feature_name() { + get_var _feature_$1_name +} + +# Check whether the feature is enabled +get_feature_state() { + for i in `get_var _feature_$1_settings`; do + if test `get_var $i` = "yes"; then + echo "yes" + return + fi + done + echo "no" +} + # # Engine handling functions @@ -464,6 +507,16 @@ get_engine_subengines() { get_var _engine_$1_subengines } +# Get the dependencies +get_engine_dependencies() { + get_var _engine_$1_deps +} + +# Get the base engine game support description +get_engine_base() { + get_var _engine_$1_base +} + # Ask if this is a subengine get_engine_sub() { sub=`get_var _engine_$1_sub` @@ -473,6 +526,11 @@ get_engine_sub() { echo $sub } +# Get a subengine's parent (undefined for non-subengines) +get_subengine_parent() { + get_var _engine_$1_parent +} + # Enable *all* engines engine_enable_all() { for engine in $_engines; do @@ -500,9 +558,15 @@ engine_enable() { engine=`echo $eng | sed 's/-/_/g'` # Filter the parameter for the subengines - if test "`get_engine_sub ${engine}`" != "no" -a "$opt" != "yes" ; then - subengine_option_error ${engine} - return + if test "`get_engine_sub ${engine}`" != "no" ; then + if test "$opt" != "yes" ; then + subengine_option_error ${engine} + return + fi + parent=`get_subengine_parent ${engine}` + if test `get_engine_build ${parent}` = "no" ; then + set_var _engine_${parent}_build "yes" + fi fi if test "$opt" = "static" -o "$opt" = "dynamic" -o "$opt" = "yes" ; then @@ -532,6 +596,29 @@ engine_disable() { fi } +# Check whether the engine's dependencies are met +# If that is not the case disable the engine +check_engine_deps() { + unmet_deps="" + + # Check whether the engine is enabled + if test `get_engine_build $1` != "no" ; then + # Collect unmet dependencies + for dep in `get_engine_dependencies $1`; do + if test `get_feature_state $dep` = "no"; then + feature_name=`get_feature_name $dep` + unmet_deps="${unmet_deps}${feature_name} " + fi + done + + # Check whether there is any unmet dependency + if test -n "$unmet_deps"; then + echo "WARNING: Disabling engine "`get_engine_name $1`" because the following dependencies are unmet: "$unmet_deps + engine_disable $1 + fi + fi +} + # Show the configure help line for a given engine show_engine_help() { name=`get_engine_name $1` @@ -574,27 +661,37 @@ prepare_engine_build_strings() { # Get the string about building an engine get_engine_build_string() { + engine=$1 + request_status=$2 engine_string="" engine_build=`get_engine_build $1` - engine_build_default=`get_engine_build_default $1` + engine_build_default=`get_engine_build_default $engine` show=no + # Convert static/dynamic to yes to ease the check of subengines + if test $engine_build = no; then + subengine_filter=no + else + subengine_filter=yes + fi + # Check if the current engine should be shown for the current status - if test $engine_build = $2 ; then + if test $engine_build = $request_status ; then show=yes else # Test for disabled sub-engines - if test $2 = no ; then - for subeng in `get_engine_subengines $1` ; do + if test $request_status = no ; then + for subeng in `get_engine_subengines $engine` ; do if test `get_engine_build $subeng` = no ; then - engine_build=no + # In this case we to display _disabled_ subengines + subengine_filter=no show=yes fi done fi # Test for enabled wip sub-engines - if test $2 = wip ; then - for subeng in `get_engine_subengines $1` ; do + if test $request_status = wip ; then + for subeng in `get_engine_subengines $engine` ; do if test `get_engine_build $subeng` != no -a `get_engine_build_default $subeng` = no ; then show=yes fi @@ -602,85 +699,82 @@ get_engine_build_string() { fi fi - # Convert static/dynamic to yes to ease the check of subengines - if test $engine_build != no ; then - engine_build=yes - fi # Check if it is a wip engine - if test "$2" = "wip" -a "$engine_build" != "no" -a "$engine_build_default" = no; then + if test "$request_status" = "wip" -a "$engine_build" != "no" -a "$engine_build_default" = no; then show=yes fi # The engine should be shown, build the string if test $show = yes ; then - build_string_func=get_${1}_build_string - if ( type $build_string_func | grep function ) 2> /dev/null > /dev/null ; then - engine_string=`$build_string_func $1 $engine_build $2` - else - engine_string=`get_subengines_build_string $1 $engine_build "" $2` - fi - - engine_string="`get_engine_name $1` $engine_string" + engine_string=`get_subengines_build_string $engine $subengine_filter $request_status` + engine_string="`get_engine_name $engine` $engine_string" fi - echo $engine_string + echo "$engine_string" } # Get the string about building subengines get_subengines_build_string() { - all=yes parent_engine=$1 - subengine_string=$3 - parent_status=$4 + subengine_filter=$2 + request_status=$3 parent_engine_build_default=`get_engine_build_default $parent_engine` + subengine_string="" - for subeng in `get_engine_subengines $parent_engine` ; do - subengine_build=`get_engine_build $subeng` - subengine_build_default=`get_engine_build_default $subeng` - if test \( $subengine_build = $2 -a "$parent_status" != wip \) -o \( "$parent_status" = wip -a $subengine_build != no -a "$subengine_build_default" = no \) ; then - subengine_string="$subengine_string [`get_engine_name $subeng`]" - else - all=no - fi + # If the base engine isn't built at all, no need to list subengines + # in any of the possible categories. + if test `get_engine_build $parent_engine` = no; then + return + fi - # handle engines that are on by default and have a single subengine that is off by default - if test "$parent_status" = wip ; then - if test $parent_engine_build_default = yes -a subengine ; then - all=no - fi + all=yes + # If there are no subengines, never display "[all games]" (for brevity). + if test -z "`get_engine_subengines $parent_engine`"; then + all=no + fi + # If the base engine does not fit the category we're displaying here + # (WIP or Skipped), we should never show "[all games]" + if test "$request_status" = wip; then + if test $parent_engine_build_default = yes; then + all=no fi + fi + if test "$request_status" = no; then + # If we're here, the parent engine is built, so no need to check that. + all=no + fi - done - if test $2 != no ; then - if test -n "$subengine_string" ; then - if test $all = yes ; then - subengine_string="[all games]" - fi - fi + # In the static/dynamic categories, also display the engine's base games. + if test -n "`get_engine_subengines $parent_engine`" -a $request_status != no -a $request_status != wip; then + subengine_string="[`get_engine_base $parent_engine`]" fi - echo $subengine_string -} + for subeng in `get_engine_subengines $parent_engine` ; do + subengine_build=`get_engine_build $subeng` + subengine_build_default=`get_engine_build_default $subeng` -# Engine specific build strings -get_scumm_build_string() { - if test `get_engine_build $1` != no ; then - if test $2 != no -a "$3" != wip ; then - base="[v0-v6 games]" + # Display this subengine if it matches the filter, unless it is + # a stable subengine in the WIP request. + if test $subengine_build = $subengine_filter -a \! \( "$request_status" = wip -a "$subengine_build_default" = yes \) ; then + s="[`get_engine_name $subeng`]" + if test -n "$subengine_string"; then + subengine_string="$subengine_string $s" + else + subengine_string="$s" + fi + else + all=no fi - get_subengines_build_string $1 $2 "$base" $3 - fi -} + done -get_saga_build_string() { - if test `get_engine_build $1` != no ; then - if test $2 != no -a "$3" != wip; then - base="[ITE]" - fi - get_subengines_build_string $1 $2 "$base" $3 + # Summarize the full list, where applicable + if test $all = yes ; then + subengine_string="[all games]" fi + + echo "$subengine_string" } # @@ -712,7 +806,7 @@ Usage: $0 [OPTIONS]... Configuration: -h, --help display this help and exit - --backend=BACKEND backend to build (android, bada, dc, dingux, ds, gp2x, gph, + --backend=BACKEND backend to build (android, bada, dc, dingux, ds, gph, iphone, linuxmoto, maemo, n64, null, openpandora, ps2, psp, samsungtv, sdl, webos, wii, wince) [sdl] @@ -777,6 +871,7 @@ Game engines: The values of <engine name> for these options are as follows: $engines_help Optional Features: + --enable-c++11 build as C++11 if the compiler allows that --disable-debug disable building with debugging symbols --enable-Werror treat warnings as errors --enable-release enable building in release mode (this activates @@ -1016,6 +1111,12 @@ for ac_option in $@; do --backend=*) _backend=`echo $ac_option | cut -d '=' -f 2` ;; + --enable-c++11) + _use_cxx11=yes + ;; + --disable-c++11) + _use_cxx11=no + ;; --enable-debug) _debug_build=yes ;; @@ -1602,21 +1703,36 @@ if test "$cxx_verc_fail" = yes ; then fi # +# Check whether the compiler supports C++11 +# +have_cxx11=no +cat > $TMPC << EOF +int main(int argc, char *argv[]) { if (argv == nullptr) return -1; else return 0; } +EOF +cc_check -std=c++11 && have_cxx11=yes +if test "$_use_cxx11" = "yes" ; then + _use_cxx11=$have_cxx11 +fi + +# # Setup compiler specific CXXFLAGS now that we know the compiler version. # Foremost, this means enabling various warnings. # In addition, we set CXX_UPDATE_DEP_FLAG for GCC >= 3.0 and for ICC. # if test "$have_gcc" = yes ; then if test "$_cxx_major" -ge "3" ; then - case $_host_os in - # newlib-based system include files suppress non-C89 function - # declarations under __STRICT_ANSI__ - amigaos* | android | bada | dreamcast | ds | gamecube | mingw* | n64 | psp | ps2 | ps3 | wii | wince ) - ;; - *) - CXXFLAGS="$CXXFLAGS -ansi" - ;; - esac + # Try to use ANSI mode when C++11 is disabled. + if test "$_use_cxx11" = "no" ; then + case $_host_os in + # newlib-based system include files suppress non-C89 function + # declarations under __STRICT_ANSI__ + amigaos* | android | bada | dreamcast | ds | gamecube | mingw* | n64 | psp | ps2 | ps3 | wii | wince ) + ;; + *) + CXXFLAGS="$CXXFLAGS -ansi" + ;; + esac + fi CXXFLAGS="$CXXFLAGS -W -Wno-unused-parameter" add_line_to_config_mk 'HAVE_GCC3 = 1' add_line_to_config_mk 'CXX_UPDATE_DEP_FLAG = -MMD -MF "$(*D)/$(DEPDIR)/$(*F).d" -MQ "$@" -MP' @@ -1632,6 +1748,15 @@ elif test "$have_icc" = yes ; then add_line_to_config_mk 'CXX_UPDATE_DEP_FLAG = -MMD -MF "$(*D)/$(DEPDIR)/$(*F).d" -MQ "$@" -MP' fi; +# +# Update status about C++11 mode +# +echo_n "Building as C++11... " +if test "$_use_cxx11" = "yes" ; then + CXXFLAGS="$CXXFLAGS -std=c++11" +fi +echo $_use_cxx11 + # By default, we add -pedantic to the CXXFLAGS to catch some potentially # non-portable constructs, like use of GNU extensions. # However, some platforms use GNU extensions in system header files, so @@ -1811,7 +1936,7 @@ cc_check_clean tmp_find_type_with_size.cpp # for the smaller sizes. echo_n "Alignment required... " case $_host_cpu in - i[3-6]86 | x86_64 | ppc*) + i[3-6]86 | amd64 | x86_64 | ppc*) # Unaligned access should work _need_memalign=no ;; @@ -1839,7 +1964,9 @@ case $_host_cpu in define_in_config_if_yes yes 'USE_ARM_SOUND_ASM' define_in_config_if_yes yes 'USE_ARM_SMUSH_ASM' define_in_config_if_yes yes 'USE_ARM_GFX_ASM' - define_in_config_if_yes yes 'USE_ARM_COSTUME_ASM' + # FIXME: The following feature exhibits a bug during the intro scene of Indy 4 + # (on Pandora and iPhone at least) + #define_in_config_if_yes yes 'USE_ARM_COSTUME_ASM' DEFINES="$DEFINES -DARM_TARGET" ;; @@ -1856,7 +1983,7 @@ case $_host_cpu in echo "PowerPC" DEFINES="$DEFINES -DPPC_TARGET" ;; - x86_64) + amd64 | x86_64) echo "x86_64" ;; *) @@ -2079,8 +2206,9 @@ case $_host_os in DEFINES="$DEFINES -D__PLAYSTATION2__" ;; ps3) - # Force use of SDL from the ps3 toolchain + # Force use of SDL and freetype from the ps3 toolchain _sdlpath="$PS3DEV/portlibs/ppu:$PS3DEV/portlibs/ppu/bin" + _freetypepath="$PS3DEV/portlibs/ppu:$PS3DEV/portlibs/ppu/bin" DEFINES="$DEFINES -DPLAYSTATION3" CXXFLAGS="$CXXFLAGS -mcpu=cell -mminimal-toc -I$PSL1GHT/ppu/include -I$PS3DEV/portlibs/ppu/include" @@ -2202,13 +2330,8 @@ if test -n "$_host"; then bfin*) ;; caanoo) - # This uses the GPH backend. - DEFINES="$DEFINES -DGPH_DEVICE" DEFINES="$DEFINES -DCAANOO" - DEFINES="$DEFINES -DREDUCE_MEMORY_USAGE" - if test "$_debug_build" = yes; then - DEFINES="$DEFINES -DGPH_DEBUG" - else + if test "$_debug_build" = no; then # Use -O3 on the Caanoo for non-debug builds. _optimization_level=-O3 fi @@ -2299,13 +2422,7 @@ if test -n "$_host"; then add_line_to_config_h "#define USE_WII_DI" ;; gp2x) - # This uses the GPH backend. - DEFINES="$DEFINES -DGPH_DEVICE" DEFINES="$DEFINES -DGP2X" - DEFINES="$DEFINES -DREDUCE_MEMORY_USAGE" - if test "$_debug_build" = yes; then - DEFINES="$DEFINES -DGPH_DEBUG" - fi CXXFLAGS="$CXXFLAGS -march=armv4t" ASFLAGS="$ASFLAGS -mfloat-abi=soft" LDFLAGS="$LDFLAGS -static" @@ -2319,13 +2436,7 @@ if test -n "$_host"; then _port_mk="backends/platform/gph/gp2x-bundle.mk" ;; gp2xwiz) - # This uses the GPH backend. - DEFINES="$DEFINES -DGPH_DEVICE" DEFINES="$DEFINES -DGP2XWIZ" - DEFINES="$DEFINES -DREDUCE_MEMORY_USAGE" - if test "$_debug_build" = yes; then - DEFINES="$DEFINES -DGPH_DEBUG" - fi CXXFLAGS="$CXXFLAGS -mcpu=arm926ej-s" CXXFLAGS="$CXXFLAGS -mtune=arm926ej-s" ASFLAGS="$ASFLAGS -mfloat-abi=soft" @@ -2446,10 +2557,13 @@ if test -n "$_host"; then # Use -O3 on the OpenPandora for non-debug builds. _optimization_level=-O3 fi + define_in_config_if_yes yes 'USE_ARM_NEON_ASPECT_CORRECTOR' CXXFLAGS="$CXXFLAGS -march=armv7-a" CXXFLAGS="$CXXFLAGS -mtune=cortex-a8" + CXXFLAGS="$CXXFLAGS -mfloat-abi=softfp" CXXFLAGS="$CXXFLAGS -mfpu=neon" - ASFLAGS="$ASFLAGS -mfloat-abi=soft" + CXXFLAGS="$CXXFLAGS -fsingle-precision-constant" + ASFLAGS="$ASFLAGS -mfloat-abi=softfp" _backend="openpandora" _build_hq_scalers=yes _vkeybd=no @@ -2458,6 +2572,16 @@ if test -n "$_host"; then _port_mk="backends/platform/openpandora/op-bundle.mk" ;; ppc-amigaos) + # Only static builds link successfully on buildbot + LDFLAGS=`echo $LDFLAGS | sed 's/-use-dynld//'` + LDFLAGS="$LDFLAGS -static" + + # toolchain binaries prefixed by host + _ranlib=$_host-ranlib + _strip=$_host-strip + _ar="$_host-ar cru" + _as="$_host-as" + _windres=$_host-windres ;; ps2) DEFINES="$DEFINES -DDISABLE_TEXT_CONSOLE" @@ -2611,9 +2735,14 @@ case $_backend in INCLUDES="$INCLUDES "'-I$(srcdir)/backends/platform/ds/commoninclude' INCLUDES="$INCLUDES "'-Ibackends/platform/ds/arm9/data' ;; - gp2x) - ;; gph) + # On the GPH devices we want fancy themes but do not want the load/save thumbnail grid. + DEFINES="$DEFINES -DDISABLE_SAVELOADCHOOSER_GRID" + DEFINES="$DEFINES -DGPH_DEVICE" + DEFINES="$DEFINES -DREDUCE_MEMORY_USAGE" + if test "$_debug_build" = yes; then + DEFINES="$DEFINES -DGPH_DEBUG" + fi ;; iphone) LIBS="$LIBS -lobjc -framework UIKit -framework CoreGraphics -framework OpenGLES" @@ -2709,7 +2838,7 @@ MODULES="$MODULES backends/platform/$_backend" # Setup SDL specifics for SDL based backends # case $_backend in - dingux | gp2x | gph | linuxmoto | maemo | openpandora | samsungtv | sdl) + dingux | gph | linuxmoto | maemo | openpandora | samsungtv | sdl) find_sdlconfig INCLUDES="$INCLUDES `$_sdlconfig --prefix="$_sdlpath" --cflags`" LIBS="$LIBS `$_sdlconfig --prefix="$_sdlpath" --libs`" @@ -2893,7 +3022,7 @@ PLUGIN_LDFLAGS += -Wl,-T$(srcdir)/backends/plugins/ds/plugin.ld -mthumb-interwo freebsd*) _plugin_prefix="lib" _plugin_suffix=".so" - CXXFLAGS="$CXXFLAGS -fpic" + CXXFLAGS="$CXXFLAGS -fPIC" _mak_plugins=' PLUGIN_EXTRA_DEPS = PLUGIN_LDFLAGS += -shared @@ -2937,7 +3066,7 @@ POST_OBJS_FLAGS := -Wl,-no-whole-archive linux*) _plugin_prefix="lib" _plugin_suffix=".so" - CXXFLAGS="$CXXFLAGS -fpic" + CXXFLAGS="$CXXFLAGS -fPIC" LIBS="$LIBS -ldl" _mak_plugins=' PLUGIN_EXTRA_DEPS = @@ -3323,11 +3452,6 @@ fi define_in_config_if_yes "$_zlib" 'USE_ZLIB' echo "$_zlib" -if test `get_engine_build sword25` = yes && test ! "$_zlib" = yes ; then - echo "...disabling Broken Sword 2.5 engine. ZLib is required" - engine_disable sword25 -fi - # # Check for Sparkle if updates support is enabled # @@ -3375,7 +3499,7 @@ if test "$_fluidsynth" = yes ; then esac INCLUDES="$INCLUDES $FLUIDSYNTH_CFLAGS" fi -define_in_config_h_if_yes "$_fluidsynth" 'USE_FLUIDSYNTH' +define_in_config_if_yes "$_fluidsynth" 'USE_FLUIDSYNTH' echo "$_fluidsynth" # @@ -3501,6 +3625,21 @@ define_in_config_if_yes "$_freetype2" "USE_FREETYPE2" # Check for OpenGL (ES) # echocheck "OpenGL" + +case $_backend in + openpandora) + # Only enable OpenGL ES on the OpanPandora if --enable-opengl is passed in explicitly. + if test "$_opengl" = yes ; then + _opengl=yes + _opengles=yes + OPENGL_LIBS="-lGLES_CM -lEGL -lX11" + OPENGL_CFLAGS="$OPENGL_LIBS" + LIBS="$LIBS $OPENGL_LIBS" + INCLUDES="$INCLUDES $OPENGL_CFLAGS" + fi + ;; +esac + if test "$_opengl" = auto ; then _opengl=no if test "$_backend" = "sdl" ; then @@ -3859,6 +3998,9 @@ sh -c " fi" 2>/dev/null & for engine in $_engines; do + # Check whether all dependencies are available + check_engine_deps $engine + if test "`get_engine_sub $engine`" = "no" ; then # It's a main engine if test `get_engine_build $engine` = no ; then @@ -3884,9 +4026,6 @@ for engine in $_engines; do isbuilt=STATIC_PLUGIN fi fi - - # Prepare the information to be shown - prepare_engine_build_strings $engine else # It's a subengine, just say yes or no if test "`get_engine_build $engine`" = "no" ; then @@ -3905,6 +4044,14 @@ for engine in $_engines; do fi done +# Prepare the information to be shown +for engine in $_engines; do + if test "`get_engine_sub $engine`" = "no" ; then + # It's a main engine + prepare_engine_build_strings $engine + fi +done + # # Detection of WIP/unstable engines # @@ -3932,28 +4079,28 @@ fi echo if test -n "$_engines_built_static" ; then echo "Engines (builtin):" - echo $_engines_built_static | sed 's/@/\ + echo "$_engines_built_static" | sed 's/@/\ /g s/#/ /g' fi if test -n "$_engines_built_dynamic" ; then echo "Engines (plugins):" - echo $_engines_built_dynamic | sed 's/@/\ + echo "$_engines_built_dynamic" | sed 's/@/\ /g s/#/ /g' fi if test -n "$_engines_skipped" ; then echo "Engines Skipped:" - echo $_engines_skipped | sed 's/@/\ + echo "$_engines_skipped" | sed 's/@/\ /g s/#/ /g' fi if test -n "$_engines_built_wip" ; then echo "WARNING: This ScummVM build contains the following UNSTABLE engines:" - echo $_engines_built_wip | sed 's/@/\ + echo "$_engines_built_wip" | sed 's/@/\ /g s/#/ /g' fi diff --git a/devtools/README b/devtools/README index 7db5259e7c..c7f08d6dfa 100644 --- a/devtools/README +++ b/devtools/README @@ -65,7 +65,7 @@ create_lure (dreammaster) create_project (LordHoto, Littleboy) -------------- - Creates project files for Visual Studio 2005, 2008, 2010, Xcode and + Creates project files for Visual Studio 2005, 2008, 2010, 2012, Xcode and Code::Blocks out of the configure / Makefile based build system. It also offers a way to enable or disable certain engines and the use of external libraries similar to configure. Run the tool without diff --git a/devtools/create_kyradat/create_kyradat.cpp b/devtools/create_kyradat/create_kyradat.cpp index a87bde3e26..1a3d406af1 100644 --- a/devtools/create_kyradat/create_kyradat.cpp +++ b/devtools/create_kyradat/create_kyradat.cpp @@ -47,7 +47,7 @@ #include <map> enum { - kKyraDatVersion = 82 + kKyraDatVersion = 84 }; const ExtractFilename extractFilenames[] = { @@ -606,7 +606,7 @@ const ExtractFilename extractFilenames[] = { // LANDS OF LORE // Ingame - { kLoLIngamePakFiles, kTypeStringList, false }, + { kLoLIngamePakFiles, k2TypeSfxList, false }, { kLoLCharacterDefs, kLoLTypeCharData, true }, { kLoLIngameSfxFiles, k2TypeSfxList, false }, @@ -625,7 +625,10 @@ const ExtractFilename extractFilenames[] = { { kLoLCharDefsKieran, kLoLTypeRaw16, false }, { kLoLCharDefsAkshel, kLoLTypeRaw16, false }, { kLoLExpRequirements, kLoLTypeRaw32, false }, - { kLoLMonsterModifiers, kLoLTypeRaw16, false }, + { kLoLMonsterModifiers1, kLoLTypeRaw16, false }, + { kLoLMonsterModifiers2, kLoLTypeRaw16, false }, + { kLoLMonsterModifiers3, kLoLTypeRaw16, false }, + { kLoLMonsterModifiers4, kLoLTypeRaw16, false }, { kLoLMonsterShiftOffsets, kTypeRawData, false }, { kLoLMonsterDirFlags, kTypeRawData, false }, { kLoLMonsterScaleY, kTypeRawData, false }, @@ -633,8 +636,8 @@ const ExtractFilename extractFilenames[] = { { kLoLMonsterScaleWH, kLoLTypeRaw16, false }, { kLoLFlyingObjectShp, kLoLTypeFlightShpData, false }, { kLoLInventoryDesc, kLoLTypeRaw16, false }, - { kLoLLevelShpList, kTypeStringList, false }, - { kLoLLevelDatList, kTypeStringList, false }, + { kLoLLevelShpList, k2TypeSfxList, false }, + { kLoLLevelDatList, k2TypeSfxList, false }, { kLoLCompassDefs, kLoLTypeCompassData, false }, { kLoLItemPrices, kLoLTypeRaw16, false }, { kLoLStashSetup, kTypeRawData, false }, @@ -671,14 +674,14 @@ const ExtractFilename extractFilenames[] = { { kLoLScrollYBottom, k3TypeRaw16to8, false }, { kLoLButtonDefs, kLoLTypeButtonDef, false }, - { kLoLButtonList1, kLoLTypeRaw16, false }, - { kLoLButtonList2, kLoLTypeRaw16, false }, - { kLoLButtonList3, kLoLTypeRaw16, false }, - { kLoLButtonList4, kLoLTypeRaw16, false }, - { kLoLButtonList5, kLoLTypeRaw16, false }, - { kLoLButtonList6, kLoLTypeRaw16, false }, - { kLoLButtonList7, kLoLTypeRaw16, false }, - { kLoLButtonList8, kLoLTypeRaw16, false }, + { kLoLButtonList1, k3TypeRaw16to8, false }, + { kLoLButtonList2, k3TypeRaw16to8, false }, + { kLoLButtonList3, k3TypeRaw16to8, false }, + { kLoLButtonList4, k3TypeRaw16to8, false }, + { kLoLButtonList5, k3TypeRaw16to8, false }, + { kLoLButtonList6, k3TypeRaw16to8, false }, + { kLoLButtonList7, k3TypeRaw16to8, false }, + { kLoLButtonList8, k3TypeRaw16to8, false }, { kLoLLegendData, kTypeRawData, false }, { kLoLMapCursorOvl, kTypeRawData, false }, @@ -2111,8 +2114,14 @@ const char *getIdString(const int id) { return "kLoLCharDefsAkshel"; case kLoLExpRequirements: return "kLoLExpRequirements"; - case kLoLMonsterModifiers: - return "kLoLMonsterModifiers"; + case kLoLMonsterModifiers1: + return "kLoLMonsterModifiers1"; + case kLoLMonsterModifiers2: + return "kLoLMonsterModifiers2"; + case kLoLMonsterModifiers3: + return "kLoLMonsterModifiers3"; + case kLoLMonsterModifiers4: + return "kLoLMonsterModifiers4"; case kLoLMonsterShiftOffsets: return "kLoLMonsterShiftOffsets"; case kLoLMonsterDirFlags: @@ -2229,6 +2238,8 @@ const char *getIdString(const int id) { return "kLoLLightningDefs"; case kLoLFireballCoords: return "kLoLFireballCoords"; + case kLoLCredits: + return "kLoLCredits"; case kLoLHistory: return "kLoLHistory"; default: diff --git a/devtools/create_kyradat/create_kyradat.h b/devtools/create_kyradat/create_kyradat.h index c2a69cfd79..6d5059cabe 100644 --- a/devtools/create_kyradat/create_kyradat.h +++ b/devtools/create_kyradat/create_kyradat.h @@ -606,7 +606,10 @@ enum kExtractID { kLoLCharDefsKieran, kLoLCharDefsAkshel, kLoLExpRequirements, - kLoLMonsterModifiers, + kLoLMonsterModifiers1, + kLoLMonsterModifiers2, + kLoLMonsterModifiers3, + kLoLMonsterModifiers4, kLoLMonsterShiftOffsets, kLoLMonsterDirFlags, kLoLMonsterScaleY, diff --git a/devtools/create_kyradat/extract.cpp b/devtools/create_kyradat/extract.cpp index 86244fce42..748bd36248 100644 --- a/devtools/create_kyradat/extract.cpp +++ b/devtools/create_kyradat/extract.cpp @@ -53,6 +53,7 @@ bool extractMrShapeAnimData(PAKFile &out, const ExtractInformation *info, const bool extractRaw16(PAKFile &out, const ExtractInformation *info, const byte *data, const uint32 size, const char *filename, int id); bool extractRaw32(PAKFile &out, const ExtractInformation *info, const byte *data, const uint32 size, const char *filename, int id); bool extractLoLButtonDefs(PAKFile &out, const ExtractInformation *info, const byte *data, const uint32 size, const char *filename, int id); +bool extractLoLFlyingShpDefs(PAKFile &out, const ExtractInformation *info, const byte *data, const uint32 size, const char *filename, int id); bool extractEoB2SeqData(PAKFile &out, const ExtractInformation *info, const byte *data, const uint32 size, const char *filename, int id); bool extractEoB2ShapeData(PAKFile &out, const ExtractInformation *info, const byte *data, const uint32 size, const char *filename, int id); @@ -81,7 +82,7 @@ const ExtractType extractTypeTable[] = { { kLoLTypeCharData, extractRaw }, { kLoLTypeSpellData, extractRaw }, { kLoLTypeCompassData, extractRaw16to8 }, - { kLoLTypeFlightShpData, extractRaw16to8 }, + { kLoLTypeFlightShpData, extractLoLFlyingShpDefs }, { kLoLTypeRaw16, extractRaw16 }, { kLoLTypeRaw32, extractRaw32 }, { kLoLTypeButtonDef, extractLoLButtonDefs }, @@ -965,18 +966,23 @@ bool extractPaddedStrings(PAKFile &out, const ExtractInformation *info, const by src++; while (*src && src < fin) *dst++ = *src++; - - *dst++ = '\0'; + if (src < fin) + *dst++ = *src++; entries++; } WRITE_BE_UINT32(buffer, entries); + outsize = dst - buffer; return out.addFile(filename, buffer, outsize); } bool extractRaw16to8(PAKFile &out, const ExtractInformation *info, const byte *data, const uint32 size, const char *filename, int id) { + // Hack for some LOL FM-Towns entries + if (info->platform == Common::kPlatformFMTowns && ((id >= kLoLButtonList1 && id <= kLoLButtonList8) || id == kLoLCharInvIndex)) + return extractRaw(out, info, data, size, filename, id); + int outsize = size >> 1; uint8 *buffer = new uint8[outsize]; const uint8 *src = data; @@ -1049,6 +1055,30 @@ bool extractLoLButtonDefs(PAKFile &out, const ExtractInformation *info, const by return out.addFile(filename, buffer, outsize); } +bool extractLoLFlyingShpDefs(PAKFile &out, const ExtractInformation *info, const byte *data, const uint32 size, const char *filename, int id) { + if (info->platform != Common::kPlatformFMTowns) + return extractRaw16to8(out, info, data, size, filename, id); + + int outsize = size / 9 * 5; + uint8 *buffer = new uint8[outsize]; + const uint8 *src = data; + uint8 *dst = buffer; + + for (int i = outsize / 5; i; --i) { + *dst++ = *src++; + src++; + *dst++ = *src++; + src++; + *dst++ = *src++; + src++; + *dst++ = *src++; + *dst++ = *src++; + src++; + } + + return out.addFile(filename, buffer, outsize); +} + bool extractEoB2SeqData(PAKFile &out, const ExtractInformation *info, const byte *data, const uint32 size, const char *filename, int id) { int num = size / 11; uint8 *buffer = new uint8[size]; diff --git a/devtools/create_kyradat/games.cpp b/devtools/create_kyradat/games.cpp index a2759b1e53..1b62155da0 100644 --- a/devtools/create_kyradat/games.cpp +++ b/devtools/create_kyradat/games.cpp @@ -119,10 +119,14 @@ const Game lolGames[] = { { kLoL, { EN_ANY, -1, -1 }, kPlatformPC, kNoSpecial, { "0cc764a204f7ba8cefe1a5f14c479619", 0 } }, { kLoL, { RU_RUS, -1, -1 }, kPlatformPC, kNoSpecial, { "80a9f9bf243bc6ed36d98584fc6988c4", 0 } }, { kLoL, { DE_DEU, -1, -1 }, kPlatformPC, kNoSpecial, { "6b843869772c1b779e1386be868c15dd", 0 } }, + { kLoL, { FR_FRA, -1, -1 }, kPlatformPC, kNoSpecial, { "6b843869772c1b779e1386be868c15dd", 0 } }, // PC98 (no language specifc strings) { kLoL, { JA_JPN, -1, -1 }, kPlatformPC98, kNoSpecial, { "6d5bd4a2f5ce433365734ca6b7a8d984", "1b0a457c48ae6908da301b656fe0aab4" } }, + // FM-Towns (no language specifc strings) + { kLoL, { JA_JPN, -1, -1 }, kPlatformFMTowns, kNoSpecial, { "a281c7143bf2b6c5d4daa107a4b0427e", "34b4cecce179990e3bcaaa2d31484a90"} }, + // DOS CD (multi language version, with no language specific strings) { kLoL, { EN_ANY, FR_FRA, DE_DEU }, kPlatformPC, kTalkieVersion, { "9d1778314de80598c0b0d032e2a1a1cf", "263998ec600afca1cc7b935c473df670" } }, { kLoL, { IT_ITA, FR_FRA, DE_DEU }, kPlatformPC, kTalkieVersion, { "9d1778314de80598c0b0d032e2a1a1cf", "f2af366e00f79dbf832fa19701d71ed9" } }, // Italian fan translation @@ -799,7 +803,9 @@ const int lolFloppyNeed[] = { kLoLCharDefsKieran, kLoLCharDefsAkshel, kLoLExpRequirements, - kLoLMonsterModifiers, + kLoLMonsterModifiers1, + kLoLMonsterModifiers2, + kLoLMonsterModifiers3, kLoLMonsterShiftOffsets, kLoLMonsterDirFlags, kLoLMonsterScaleY, @@ -843,7 +849,6 @@ const int lolFloppyNeed[] = { kLoLButtonDefs, kLoLButtonList1, - kLoLButtonList1, kLoLButtonList2, kLoLButtonList3, kLoLButtonList4, @@ -881,7 +886,9 @@ const int lolPC98Need[] = { kLoLCharDefsKieran, kLoLCharDefsAkshel, kLoLExpRequirements, - kLoLMonsterModifiers, + kLoLMonsterModifiers1, + kLoLMonsterModifiers2, + kLoLMonsterModifiers3, kLoLMonsterShiftOffsets, kLoLMonsterDirFlags, kLoLMonsterScaleY, @@ -925,6 +932,89 @@ const int lolPC98Need[] = { kLoLButtonDefs, kLoLButtonList1, + kLoLButtonList2, + kLoLButtonList3, + kLoLButtonList4, + kLoLButtonList5, + kLoLButtonList6, + kLoLButtonList7, + kLoLButtonList8, + + kLoLLegendData, + kLoLMapStringId, + + kLoLSpellbookAnim, + kLoLSpellbookCoords, + kLoLHealShapeFrames, + kLoLLightningDefs, + kLoLFireballCoords, + + kLoLCredits, + + -1 +}; + +const int lolFMTownsNeed[] = { + kLoLIngamePakFiles, + + kLoLCharacterDefs, + kLoLIngameSfxFiles, + kLoLIngameSfxIndex, + kLoLSpellProperties, + kLoLGameShapeMap, + kLoLSceneItemOffs, + kLoLCharInvIndex, + kLoLCharInvDefs, + kLoLCharDefsMan, + kLoLCharDefsWoman, + kLoLCharDefsKieran, + kLoLCharDefsAkshel, + kLoLExpRequirements, + kLoLMonsterModifiers1, + kLoLMonsterModifiers2, + kLoLMonsterModifiers3, + kLoLMonsterShiftOffsets, + kLoLMonsterDirFlags, + kLoLMonsterScaleY, + kLoLMonsterScaleX, + kLoLMonsterScaleWH, + kLoLFlyingObjectShp, + kLoLInventoryDesc, + + kLoLLevelShpList, + kLoLLevelDatList, + kLoLCompassDefs, + kLoLStashSetup, + kLoLDscWalls, + kRpgCommonDscShapeIndex, + kLoLDscOvlMap, + kLoLDscScaleWidthData, + kLoLDscScaleHeightData, + kRpgCommonDscX, + kLoLDscY, + kRpgCommonDscTileIndex, + kRpgCommonDscUnk2, + kRpgCommonDscDoorShapeIndex, + kRpgCommonDscDimData1, + kRpgCommonDscDimData2, + kRpgCommonDscBlockMap, + kRpgCommonDscDimMap, + kLoLDscOvlIndex, + kRpgCommonDscBlockIndex, + kRpgCommonDscDoorY2, + kRpgCommonDscDoorFrameY1, + kRpgCommonDscDoorFrameY2, + kLoLDscDoorScale, + kLoLDscDoor4, + kLoLDscDoorX, + kLoLDscDoorY, + + kLoLScrollXTop, + kLoLScrollYTop, + kLoLScrollXBottom, + kLoLScrollYBottom, + + kLoLButtonDefs, kLoLButtonList1, kLoLButtonList2, kLoLButtonList3, @@ -967,7 +1057,10 @@ const int lolCDNeed[] = { kLoLCharDefsKieran, kLoLCharDefsAkshel, kLoLExpRequirements, - kLoLMonsterModifiers, + kLoLMonsterModifiers1, + kLoLMonsterModifiers2, + kLoLMonsterModifiers3, + kLoLMonsterModifiers4, kLoLMonsterShiftOffsets, kLoLMonsterDirFlags, kLoLMonsterScaleY, @@ -1012,7 +1105,6 @@ const int lolCDNeed[] = { kLoLButtonDefs, kLoLButtonList1, - kLoLButtonList1, kLoLButtonList2, kLoLButtonList3, kLoLButtonList4, @@ -1694,6 +1786,7 @@ const GameNeed gameNeedTable[] = { { kLoL, kPlatformPC, kNoSpecial, lolFloppyNeed }, { kLoL, kPlatformPC98, kNoSpecial, lolPC98Need }, + { kLoL, kPlatformFMTowns, kNoSpecial, lolFMTownsNeed }, { kLoL, kPlatformPC, kTalkieVersion, lolCDNeed }, diff --git a/devtools/create_kyradat/tables.cpp b/devtools/create_kyradat/tables.cpp index 1b9f90f18f..09d70bc448 100644 --- a/devtools/create_kyradat/tables.cpp +++ b/devtools/create_kyradat/tables.cpp @@ -3341,7 +3341,7 @@ const ExtractEntrySearchData kEoB2WallOfForceShpIdProvider[] = { const ExtractEntrySearchData kLoLIngamePakFilesProvider[] = { { UNK_LANG, kPlatformPC, { 0x00000088, 0x0000224F, { { 0xDA, 0x24, 0x18, 0xA3, 0xEF, 0x16, 0x70, 0x8F, 0xA8, 0xC2, 0x2E, 0xC2, 0xED, 0x39, 0x03, 0xD1 } } } }, { UNK_LANG, kPlatformPC98, { 0x00000084, 0x00002125, { { 0x7A, 0x89, 0xE2, 0x36, 0xEC, 0x6F, 0x52, 0x2B, 0xEF, 0xBA, 0x3D, 0x28, 0x54, 0xDA, 0xFB, 0x72 } } } }, - + { UNK_LANG, kPlatformFMTowns, { 0x0000009D, 0x00002179, { { 0x7D, 0x7A, 0xE1, 0xD9, 0x69, 0x23, 0x9D, 0xFF, 0x83, 0x39, 0x73, 0xEC, 0xF4, 0x26, 0x20, 0x8E } } } }, EXTRACT_END_ENTRY }; @@ -3349,12 +3349,14 @@ const ExtractEntrySearchData kLoLCharacterDefsProvider[] = { { RU_RUS, kPlatformPC, { 0x00000492, 0x000052BA, { { 0x52, 0x29, 0x0D, 0x49, 0xFD, 0x17, 0xD7, 0x70, 0x6D, 0xCA, 0xEB, 0xB6, 0x7E, 0xFA, 0xBE, 0x08 } } } }, // floppy { EN_ANY, kPlatformPC, { 0x00000492, 0x000046B0, { { 0x7A, 0x94, 0x8B, 0xC6, 0xF7, 0xF1, 0x2F, 0xF3, 0xBC, 0x1B, 0x0B, 0x4E, 0x00, 0xC9, 0x44, 0x58 } } } }, // floppy { DE_DEU, kPlatformPC, { 0x00000492, 0x000047FD, { { 0x8C, 0x0B, 0x8B, 0xCE, 0xE0, 0xB0, 0x8F, 0xA9, 0x06, 0xC3, 0x98, 0xE6, 0x2E, 0x09, 0xB6, 0x93 } } } }, // floppy + { FR_FRA, kPlatformPC, { 0x00000492, 0x000047FD, { { 0x8C, 0x0B, 0x8B, 0xCE, 0xE0, 0xB0, 0x8F, 0xA9, 0x06, 0xC3, 0x98, 0xE6, 0x2E, 0x09, 0xB6, 0x93 } } } }, // floppy { EN_ANY, kPlatformPC, { 0x00000492, 0x00004ACD, { { 0xDF, 0x87, 0xFE, 0x89, 0x59, 0xCC, 0x01, 0xD7, 0xC7, 0xEB, 0x16, 0xA4, 0x09, 0xAF, 0x5D, 0xC0 } } } }, // CD { DE_DEU, kPlatformPC, { 0x00000492, 0x00004ACD, { { 0xDF, 0x87, 0xFE, 0x89, 0x59, 0xCC, 0x01, 0xD7, 0xC7, 0xEB, 0x16, 0xA4, 0x09, 0xAF, 0x5D, 0xC0 } } } }, // CD { FR_FRA, kPlatformPC, { 0x00000492, 0x00004ACD, { { 0xDF, 0x87, 0xFE, 0x89, 0x59, 0xCC, 0x01, 0xD7, 0xC7, 0xEB, 0x16, 0xA4, 0x09, 0xAF, 0x5D, 0xC0 } } } }, // CD { RU_RUS, kPlatformPC, { 0x00000492, 0x00004ACD, { { 0xDF, 0x87, 0xFE, 0x89, 0x59, 0xCC, 0x01, 0xD7, 0xC7, 0xEB, 0x16, 0xA4, 0x09, 0xAF, 0x5D, 0xC0 } } } }, // CD { IT_ITA, kPlatformPC, { 0x00000492, 0x00004ACD, { { 0xDF, 0x87, 0xFE, 0x89, 0x59, 0xCC, 0x01, 0xD7, 0xC7, 0xEB, 0x16, 0xA4, 0x09, 0xAF, 0x5D, 0xC0 } } } }, // CD { JA_JPN, kPlatformPC98, { 0x00000492, 0x00005893, { { 0x7C, 0x7E, 0xFB, 0x80, 0xD9, 0xB6, 0x16, 0x87, 0x80, 0xB7, 0x46, 0x9B, 0x96, 0x1A, 0x6A, 0xBE } } } }, + { JA_JPN, kPlatformFMTowns, { 0x00000492, 0x00005041, { { 0xAB, 0x07, 0x37, 0xFE, 0xC2, 0x4B, 0x5D, 0x16, 0xE4, 0xC4, 0x2C, 0x8C, 0xC3, 0x78, 0xCB, 0xCB } } } }, EXTRACT_END_ENTRY }; @@ -3363,7 +3365,7 @@ const ExtractEntrySearchData kLoLIngameSfxFilesProvider[] = { { UNK_LANG, kPlatformPC, { 0x000008F2, 0x0001E5B6, { { 0x63, 0x5E, 0x37, 0xAA, 0x27, 0x80, 0x4C, 0x85, 0xB1, 0x9D, 0x7B, 0x1D, 0x64, 0xA3, 0xEB, 0x97 } } } }, // floppy { UNK_LANG, kPlatformPC, { 0x000008F2, 0x0001E5B7, { { 0x9E, 0xC8, 0xE8, 0x19, 0x2F, 0x58, 0x0B, 0xC7, 0x2D, 0x41, 0x72, 0xE7, 0xF4, 0x80, 0x03, 0xCB } } } }, // CD { UNK_LANG, kPlatformPC98, { 0x000008EF, 0x0001E585, { { 0x85, 0x81, 0x5C, 0xA4, 0x34, 0x44, 0xF4, 0x58, 0xF9, 0x82, 0xEE, 0x0F, 0x6A, 0x0D, 0xA2, 0x7F } } } }, - + { UNK_LANG, kPlatformFMTowns, { 0x000008F0, 0x0001E585, { { 0xB7, 0x82, 0xFF, 0xAB, 0x71, 0x54, 0xEB, 0x52, 0x8D, 0xAC, 0x9A, 0xB4, 0x9E, 0x33, 0x00, 0x95 } } } }, EXTRACT_END_ENTRY }; @@ -3418,6 +3420,7 @@ const ExtractEntrySearchData kLoLSceneItemOffsProvider[] = { const ExtractEntrySearchData kLoLCharInvIndexProvider[] = { { UNK_LANG, kPlatformUnknown, { 0x0000000A, 0x00000006, { { 0x19, 0x79, 0x4E, 0xFC, 0x05, 0x14, 0x89, 0x23, 0xEB, 0xCA, 0x94, 0x50, 0xE8, 0xD3, 0x81, 0x24 } } } }, + { UNK_LANG, kPlatformFMTowns, { 0x00000005, 0x00000006, { { 0x54, 0x11, 0x01, 0x79, 0x4D, 0xED, 0xF9, 0xEA, 0xDF, 0x03, 0x51, 0xAB, 0x8D, 0x9D, 0x2F, 0x34 } } } }, EXTRACT_END_ENTRY }; @@ -3458,10 +3461,24 @@ const ExtractEntrySearchData kLoLExpRequirementsProvider[] = { EXTRACT_END_ENTRY }; -const ExtractEntrySearchData kLoLMonsterModifiersProvider[] = { - { UNK_LANG, kPlatformUnknown, { 0x00000018, 0x000002C6, { { 0x38, 0x9A, 0x8B, 0x50, 0xD2, 0x9B, 0x95, 0x38, 0x91, 0x02, 0xA9, 0xBE, 0x78, 0xE5, 0x89, 0x65 } } } }, // floppy + PC98 - { UNK_LANG, kPlatformPC, { 0x00000018, 0x000002EE, { { 0x4E, 0x37, 0x56, 0xE3, 0x42, 0xB3, 0x15, 0x2C, 0x7E, 0x9B, 0x7E, 0x50, 0x32, 0x91, 0x55, 0xBE } } } }, // CD +const ExtractEntrySearchData kLoLMonsterModifiers1Provider[] = { + { UNK_LANG, kPlatformUnknown, { 0x00000006, 0x00000142, { { 0x62, 0x4B, 0x5E, 0x46, 0x64, 0xA4, 0x3A, 0xB7, 0x11, 0x14, 0xA8, 0x41, 0xAF, 0x4E, 0xE6, 0x58 } } } }, // floppy + PC98 + FM-TOWNS + { UNK_LANG, kPlatformPC, { 0x00000006, 0x000000E8, { { 0x94, 0xCB, 0xD2, 0xE4, 0xF4, 0xA8, 0x4D, 0x46, 0x2E, 0x84, 0x8C, 0x6F, 0xF9, 0x75, 0xD7, 0x28 } } } }, // CD + EXTRACT_END_ENTRY +}; +const ExtractEntrySearchData kLoLMonsterModifiers2Provider[] = { + { UNK_LANG, kPlatformUnknown, { 0x00000006, 0x000000C2, { { 0x89, 0x12, 0xA7, 0x0D, 0xD9, 0xC7, 0x5B, 0x03, 0xD4, 0x21, 0x6F, 0x0A, 0x1D, 0x83, 0x1B, 0x98 } } } }, + EXTRACT_END_ENTRY +}; + +const ExtractEntrySearchData kLoLMonsterModifiers3Provider[] = { + { UNK_LANG, kPlatformUnknown, { 0x00000006, 0x000000C2, { { 0x56, 0x4D, 0x82, 0xCC, 0x2C, 0x00, 0x1E, 0x9D, 0xF7, 0x64, 0xB7, 0x60, 0x63, 0x0A, 0x03, 0xD7 } } } }, + EXTRACT_END_ENTRY +}; + +const ExtractEntrySearchData kLoLMonsterModifiers4Provider[] = { + { UNK_LANG, kPlatformPC, { 0x00000006, 0x00000082, { { 0xA8, 0xFC, 0xBB, 0x1B, 0xC0, 0x85, 0x3B, 0xEF, 0xDB, 0xDE, 0xB0, 0x98, 0x58, 0x34, 0x75, 0xE9 } } } }, // CD EXTRACT_END_ENTRY }; @@ -3486,6 +3503,7 @@ const ExtractEntrySearchData kLoLMonsterScaleYProvider[] = { const ExtractEntrySearchData kLoLMonsterScaleXProvider[] = { { UNK_LANG, kPlatformPC, { 0x00000020, 0x00000918, { { 0xF6, 0x14, 0xE6, 0x48, 0x4E, 0x5B, 0x43, 0xCC, 0xCE, 0x4E, 0x98, 0x71, 0x5A, 0xC2, 0x00, 0x1E } } } }, { UNK_LANG, kPlatformPC98, { 0x0000001D, 0x000008D2, { { 0x1C, 0x25, 0x38, 0xE2, 0xBB, 0xB2, 0xDB, 0x93, 0x1B, 0x25, 0xB6, 0x89, 0xA9, 0x9B, 0x0A, 0xFE } } } }, + { UNK_LANG, kPlatformFMTowns, { 0x0000001D, 0x000008D2, { { 0x1C, 0x25, 0x38, 0xE2, 0xBB, 0xB2, 0xDB, 0x93, 0x1B, 0x25, 0xB6, 0x89, 0xA9, 0x9B, 0x0A, 0xFE } } } }, EXTRACT_END_ENTRY }; @@ -3498,6 +3516,7 @@ const ExtractEntrySearchData kLoLMonsterScaleWHProvider[] = { const ExtractEntrySearchData kLoLFlyingObjectShpProvider[] = { { UNK_LANG, kPlatformUnknown, { 0x00000082, 0x00000252, { { 0xDE, 0x9D, 0x89, 0xAF, 0x0F, 0x50, 0x14, 0x60, 0x68, 0xAF, 0x19, 0xD8, 0x54, 0x8A, 0x36, 0x27 } } } }, + { UNK_LANG, kPlatformFMTowns, { 0x00000075, 0x00000252, { { 0xD7, 0xE5, 0x13, 0x67, 0xDB, 0x9C, 0xD4, 0x12, 0x0E, 0x99, 0x0D, 0x2A, 0x70, 0x17, 0x95, 0x89 } } } }, EXTRACT_END_ENTRY }; @@ -3510,13 +3529,13 @@ const ExtractEntrySearchData kLoLInventoryDescProvider[] = { const ExtractEntrySearchData kLoLLevelShpListProvider[] = { { UNK_LANG, kPlatformUnknown, { 0x0000007F, 0x00002090, { { 0x17, 0x31, 0x8A, 0xB5, 0x9B, 0x3A, 0xDA, 0x16, 0x9E, 0xE3, 0xD1, 0x5F, 0xB4, 0x7B, 0xB2, 0x25 } } } }, - + { UNK_LANG, kPlatformFMTowns, { 0x00000091, 0x00002090, { { 0x51, 0x79, 0x1D, 0x60, 0xB0, 0x71, 0xB8, 0xF2, 0xDD, 0xD4, 0x36, 0x1B, 0xF8, 0x15, 0xBF, 0xB7 } } } }, EXTRACT_END_ENTRY }; const ExtractEntrySearchData kLoLLevelDatListProvider[] = { - { UNK_LANG, kPlatformUnknown, { 0x0000007F, 0x00001FB8, { { 0xF6, 0xE9, 0x98, 0x79, 0x51, 0xCA, 0xA0, 0x35, 0xE4, 0xD0, 0xA1, 0xCD, 0x23, 0x89, 0x7D, 0x11 } } } }, // floppy + PC98 - { UNK_LANG, kPlatformPC, { 0x000000FF, 0x000047EC, { { 0x0D, 0xA5, 0xFD, 0x8A, 0x33, 0xDB, 0x93, 0x43, 0xE2, 0x57, 0x35, 0xEC, 0xA6, 0xCF, 0x7A, 0xA1 } } } }, // CD + { UNK_LANG, kPlatformUnknown, { 0x0000007F, 0x00001FB8, { { 0xF6, 0xE9, 0x98, 0x79, 0x51, 0xCA, 0xA0, 0x35, 0xE4, 0xD0, 0xA1, 0xCD, 0x23, 0x89, 0x7D, 0x11 } } } }, + { UNK_LANG, kPlatformFMTowns, { 0x00000091, 0x00001FB8, { { 0x65, 0x1A, 0x3E, 0x96, 0x96, 0xA9, 0x94, 0xD6, 0xD5, 0x21, 0xBE, 0x60, 0xB5, 0x83, 0xF0, 0xE5 } } } }, EXTRACT_END_ENTRY }; @@ -3679,7 +3698,7 @@ const ExtractEntrySearchData kLoLDscDoorScaleProvider[] = { const ExtractEntrySearchData kLoLDscDoor4Provider[] = { { UNK_LANG, kPlatformUnknown, { 0x00000008, 0x00000103, { { 0x29, 0xC0, 0x4B, 0x7F, 0x36, 0x23, 0xBB, 0x38, 0x4C, 0x83, 0xC6, 0x9D, 0xB4, 0x8F, 0x29, 0x2E } } } }, - + { UNK_LANG, kPlatformFMTowns, { 0x00000008, 0x0000000F, { { 0x9C, 0x6B, 0xAF, 0x69, 0x42, 0xC9, 0xC9, 0xA0, 0xD9, 0xF3, 0x54, 0xD9, 0x9A, 0xAF, 0xCF, 0xD8 } } } }, EXTRACT_END_ENTRY }; @@ -3724,55 +3743,56 @@ const ExtractEntrySearchData kLoLButtonDefsProvider[] = { { UNK_LANG, kPlatformPC, { 0x0000082A, 0x0000C34E, { { 0x7F, 0x9A, 0x0F, 0x28, 0x1A, 0x8F, 0x03, 0x46, 0x48, 0xEB, 0xC9, 0xB9, 0x23, 0x29, 0x5E, 0x50 } } } }, // floppy { UNK_LANG, kPlatformPC, { 0x0000082A, 0x0000C47B, { { 0xDF, 0x1A, 0x18, 0x1F, 0x58, 0x05, 0x1F, 0x56, 0xD8, 0x6D, 0xBB, 0x93, 0xEC, 0x35, 0x9D, 0xA5 } } } }, // CD { UNK_LANG, kPlatformPC98, { 0x0000082A, 0x0000AB58, { { 0xDD, 0x2B, 0xA9, 0x54, 0x60, 0x25, 0x2C, 0x74, 0xF8, 0x5D, 0xC6, 0xD2, 0x2C, 0x1A, 0x24, 0x44 } } } }, - + { UNK_LANG, kPlatformFMTowns, { 0x0000082A, 0x0000D271, { { 0xAF, 0xAD, 0x11, 0xF9, 0xDC, 0x41, 0x94, 0xB3, 0x0E, 0x48, 0x69, 0xB3, 0x32, 0x89, 0x7C, 0xDD } } } }, EXTRACT_END_ENTRY }; const ExtractEntrySearchData kLoLButtonList1Provider[] = { { UNK_LANG, kPlatformUnknown, { 0x00000050, 0x00000A37, { { 0x0F, 0x73, 0xEC, 0xDD, 0xAB, 0xFF, 0x49, 0x46, 0x5E, 0x8F, 0x0D, 0xC3, 0xE7, 0x1B, 0x89, 0x51 } } } }, + { UNK_LANG, kPlatformFMTowns, { 0x00000028, 0x00000938, { { 0x4B, 0xD9, 0x4A, 0x57, 0x58, 0xEC, 0x01, 0xE5, 0xA1, 0x25, 0x6A, 0x1A, 0x9C, 0x5D, 0x79, 0x19 } } } }, EXTRACT_END_ENTRY }; const ExtractEntrySearchData kLoLButtonList2Provider[] = { { UNK_LANG, kPlatformUnknown, { 0x0000001E, 0x00000522, { { 0xEA, 0x41, 0x46, 0xE2, 0xFE, 0xAA, 0x7D, 0x5E, 0x89, 0x7F, 0xBF, 0x9B, 0x30, 0x60, 0x74, 0xF3 } } } }, - + { UNK_LANG, kPlatformFMTowns, { 0x0000000F, 0x00000423, { { 0xD3, 0xA8, 0xD4, 0xFB, 0x1A, 0x56, 0x21, 0x8C, 0x01, 0xED, 0xF8, 0x54, 0xA9, 0xC5, 0x97, 0x04 } } } }, EXTRACT_END_ENTRY }; const ExtractEntrySearchData kLoLButtonList3Provider[] = { { UNK_LANG, kPlatformUnknown, { 0x00000004, 0x0000023E, { { 0x70, 0xAA, 0xCA, 0xAC, 0x5C, 0x21, 0xCF, 0xA5, 0xBF, 0x7F, 0x5F, 0xBC, 0xF1, 0x24, 0x8A, 0xAF } } } }, - + { UNK_LANG, kPlatformFMTowns, { 0x00000002, 0x0000013F, { { 0xDF, 0xE4, 0x3D, 0x18, 0x94, 0x18, 0xA5, 0x74, 0xBA, 0x26, 0x7B, 0x31, 0x87, 0xAE, 0xEE, 0x22 } } } }, EXTRACT_END_ENTRY }; const ExtractEntrySearchData kLoLButtonList4Provider[] = { { UNK_LANG, kPlatformUnknown, { 0x0000001E, 0x0000054D, { { 0x19, 0x2A, 0xBE, 0x7F, 0x94, 0x10, 0xA0, 0x60, 0x2A, 0x33, 0xD6, 0x11, 0x85, 0xF0, 0xA4, 0xA9 } } } }, - + { UNK_LANG, kPlatformFMTowns, { 0x0000000F, 0x0000044E, { { 0x18, 0x1E, 0xBB, 0x7D, 0xAC, 0xA1, 0x87, 0x0F, 0x32, 0xA3, 0xBF, 0x5F, 0xBC, 0xBB, 0x90, 0xA4 } } } }, EXTRACT_END_ENTRY }; const ExtractEntrySearchData kLoLButtonList5Provider[] = { { UNK_LANG, kPlatformUnknown, { 0x00000020, 0x0000045D, { { 0xE3, 0x7C, 0xC2, 0x36, 0x21, 0x46, 0xDB, 0xF3, 0xDD, 0x38, 0x4B, 0x40, 0xE0, 0x35, 0x09, 0xC3 } } } }, - + { UNK_LANG, kPlatformFMTowns, { 0x00000010, 0x0000035E, { { 0x4E, 0xE2, 0xD6, 0x93, 0xA3, 0xEF, 0xD0, 0xEA, 0x28, 0xE6, 0xE7, 0xDD, 0xFC, 0x44, 0xE2, 0xB9 } } } }, EXTRACT_END_ENTRY }; const ExtractEntrySearchData kLoLButtonList6Provider[] = { { UNK_LANG, kPlatformUnknown, { 0x0000001C, 0x000004C4, { { 0x21, 0x7C, 0x29, 0x3F, 0x95, 0x6F, 0x91, 0x8C, 0xB2, 0x30, 0x09, 0xA6, 0x7B, 0x48, 0x44, 0x8F } } } }, - + { UNK_LANG, kPlatformFMTowns, { 0x0000000E, 0x000003C5, { { 0x05, 0x10, 0x83, 0x1E, 0x18, 0x11, 0xC4, 0x43, 0x01, 0xE3, 0xE0, 0xD7, 0x79, 0x29, 0xA5, 0x86 } } } }, EXTRACT_END_ENTRY }; const ExtractEntrySearchData kLoLButtonList7Provider[] = { { UNK_LANG, kPlatformUnknown, { 0x00000006, 0x0000021D, { { 0xDC, 0xCE, 0x1B, 0xEB, 0x11, 0x6D, 0xDE, 0x37, 0x17, 0xC8, 0x06, 0x51, 0xC3, 0x0C, 0xCB, 0xA6 } } } }, - + { UNK_LANG, kPlatformFMTowns, { 0x00000003, 0x0000011E, { { 0xCF, 0x37, 0xEF, 0x83, 0xEC, 0x0D, 0x65, 0x41, 0xC8, 0x1D, 0xD1, 0x20, 0x82, 0x6B, 0xB5, 0x9B } } } }, EXTRACT_END_ENTRY }; const ExtractEntrySearchData kLoLButtonList8Provider[] = { { UNK_LANG, kPlatformUnknown, { 0x00000004, 0x00000253, { { 0x0C, 0x7B, 0x10, 0x99, 0x93, 0xD0, 0x33, 0xCA, 0xAB, 0x8D, 0x7E, 0x24, 0xE5, 0x7E, 0x6C, 0x91 } } } }, - + { UNK_LANG, kPlatformFMTowns, { 0x00000002, 0x00000154, { { 0xE9, 0x6B, 0x8A, 0xD7, 0x8E, 0xCF, 0x66, 0x07, 0xDC, 0xF1, 0xC0, 0xAA, 0x81, 0x88, 0xB8, 0xB9 } } } }, EXTRACT_END_ENTRY }; @@ -3827,7 +3847,7 @@ const ExtractEntrySearchData kLoLFireballCoordsProvider[] = { const ExtractEntrySearchData kLoLCreditsProvider[] = { { JA_JPN , kPlatformPC98, { 0x000005E7, 0x0001A1B0, { { 0x2A, 0xD0, 0x38, 0x84, 0x0C, 0x38, 0xCB, 0x52, 0x5D, 0x82, 0xBE, 0x03, 0x76, 0xFA, 0x0A, 0x4A } } } }, - + { JA_JPN , kPlatformFMTowns, { 0x000005EC, 0x0001A219, { { 0x03, 0xBC, 0x67, 0x19, 0xA1, 0x99, 0x70, 0x10, 0x7A, 0x73, 0x85, 0xDA, 0xB4, 0x59, 0x49, 0xB0 } } } }, EXTRACT_END_ENTRY }; @@ -4376,7 +4396,10 @@ const ExtractEntry extractProviders[] = { { kLoLCharDefsKieran, kLoLCharDefsKieranProvider }, { kLoLCharDefsAkshel, kLoLCharDefsAkshelProvider }, { kLoLExpRequirements, kLoLExpRequirementsProvider }, - { kLoLMonsterModifiers, kLoLMonsterModifiersProvider }, + { kLoLMonsterModifiers1, kLoLMonsterModifiers1Provider }, + { kLoLMonsterModifiers2, kLoLMonsterModifiers2Provider }, + { kLoLMonsterModifiers3, kLoLMonsterModifiers3Provider }, + { kLoLMonsterModifiers4, kLoLMonsterModifiers4Provider }, { kLoLMonsterShiftOffsets, kLoLMonsterShiftOffsetsProvider }, { kLoLMonsterDirFlags, kLoLMonsterDirFlagsProvider }, { kLoLMonsterScaleY, kLoLMonsterScaleYProvider }, diff --git a/devtools/create_project/config.h b/devtools/create_project/config.h index 20c1391cef..de4703a47d 100644 --- a/devtools/create_project/config.h +++ b/devtools/create_project/config.h @@ -28,7 +28,10 @@ #define LIBS_DEFINE "SCUMMVM_LIBS" // Name of the include environment variable #define REVISION_DEFINE "SCUMMVM_INTERNAL_REVISION" -//#define ADDITIONAL_LIBRARY "" -#define NEEDS_RTTI 0 +#define ENABLE_LANGUAGE_EXTENSIONS "" // Comma separated list of projects that need language extensions +#define DISABLE_EDIT_AND_CONTINUE "tinsel,tony" // Comma separated list of projects that need Edit&Continue to be disabled for co-routine support (the main project is automatically added) + +//#define ADDITIONAL_LIBRARY "" // Add a single library to the list of externally linked libraries +#define NEEDS_RTTI 0 // Enable RTTI globally #endif // TOOLS_CREATE_PROJECT_CONFIG_H diff --git a/devtools/create_project/create_project.cpp b/devtools/create_project/create_project.cpp index df220f0934..a8e09ff5eb 100644 --- a/devtools/create_project/create_project.cpp +++ b/devtools/create_project/create_project.cpp @@ -76,14 +76,6 @@ namespace { std::string unifyPath(const std::string &path); /** - * Returns the last path component. - * - * @param path Path string. - * @return Last path component. - */ -std::string getLastPathComponent(const std::string &path); - -/** * Display the help text for the program. * * @param exe Name of the executable. @@ -105,30 +97,6 @@ struct FSNode { }; typedef std::list<FSNode> FileList; - -typedef StringList TokenList; - -/** - * Takes a given input line and creates a list of tokens out of it. - * - * A token in this context is separated by whitespaces. A special case - * are quotation marks though. A string inside quotation marks is treated - * as single token, even when it contains whitespaces. - * - * Thus for example the input: - * foo bar "1 2 3 4" ScummVM - * will create a list with the following entries: - * "foo", "bar", "1 2 3 4", "ScummVM" - * As you can see the quotation marks will get *removed* too. - * - * You can also use this with non-whitespace by passing another separator - * character (e.g. ','). - * - * @param input The text to be tokenized. - * @param separator The token separator. - * @return A list of tokens. - */ -TokenList tokenize(const std::string &input, char separator = ' '); } // End of anonymous namespace enum ProjectType { @@ -221,7 +189,7 @@ int main(int argc, char *argv[]) { msvcVersion = atoi(argv[++i]); - if (msvcVersion != 8 && msvcVersion != 9 && msvcVersion != 10) { + if (msvcVersion != 8 && msvcVersion != 9 && msvcVersion != 10 && msvcVersion != 11) { std::cerr << "ERROR: Unsupported version: \"" << msvcVersion << "\" passed to \"--msvc-version\"!\n"; return -1; } @@ -534,7 +502,7 @@ int main(int argc, char *argv[]) { projectWarnings["agos"].push_back("4511"); projectWarnings["dreamweb"].push_back("4355"); - + projectWarnings["lure"].push_back("4189"); projectWarnings["lure"].push_back("4355"); @@ -606,14 +574,6 @@ std::string unifyPath(const std::string &path) { return result; } -std::string getLastPathComponent(const std::string &path) { - std::string::size_type pos = path.find_last_of('/'); - if (pos == std::string::npos) - return path; - else - return path.substr(pos + 1); -} - void displayHelp(const char *exe) { using std::cout; @@ -643,6 +603,7 @@ void displayHelp(const char *exe) { " 8 stands for \"Visual Studio 2005\"\n" " 9 stands for \"Visual Studio 2008\"\n" " 10 stands for \"Visual Studio 2010\"\n" + " 11 stands for \"Visual Studio 2012\"\n" " The default is \"9\", thus \"Visual Studio 2008\"\n" " --build-events Run custom build events as part of the build\n" " (default: false)\n" @@ -802,6 +763,7 @@ bool parseEngine(const std::string &line, EngineDesc &engine) { return true; } +} // End of anonymous namespace TokenList tokenize(const std::string &input, char separator) { TokenList result; @@ -834,7 +796,6 @@ TokenList tokenize(const std::string &input, char separator) { return result; } -} // End of anonymous namespace namespace { const Feature s_features[] = { @@ -1000,7 +961,7 @@ bool isInList(const std::string &dir, const std::string &fileName, const StringL continue; } - const std::string lastPathComponent = getLastPathComponent(*i); + const std::string lastPathComponent = ProjectProvider::getLastPathComponent(*i); if (extensionName == "o") { return false; } else if (!producesObjectFile(fileName) && extensionName != "h") { @@ -1303,6 +1264,14 @@ std::string ProjectProvider::createUUID() const { #endif } +std::string ProjectProvider::getLastPathComponent(const std::string &path) { + std::string::size_type pos = path.find_last_of('/'); + if (pos == std::string::npos) + return path; + else + return path.substr(pos + 1); +} + void ProjectProvider::addFilesToProject(const std::string &dir, std::ofstream &projectFile, const StringList &includeList, const StringList &excludeList, const std::string &filePrefix) { diff --git a/devtools/create_project/create_project.h b/devtools/create_project/create_project.h index 8719143f4a..de77793ee7 100644 --- a/devtools/create_project/create_project.h +++ b/devtools/create_project/create_project.h @@ -31,6 +31,30 @@ typedef std::list<std::string> StringList; +typedef StringList TokenList; + +/** + * Takes a given input line and creates a list of tokens out of it. + * + * A token in this context is separated by whitespaces. A special case + * are quotation marks though. A string inside quotation marks is treated + * as single token, even when it contains whitespaces. + * + * Thus for example the input: + * foo bar "1 2 3 4" ScummVM + * will create a list with the following entries: + * "foo", "bar", "1 2 3 4", "ScummVM" + * As you can see the quotation marks will get *removed* too. + * + * You can also use this with non-whitespace by passing another separator + * character (e.g. ','). + * + * @param input The text to be tokenized. + * @param separator The token separator. + * @return A list of tokens. + */ +TokenList tokenize(const std::string &input, char separator = ' '); + /** * Structure to describe a game engine to be built into ScummVM. * @@ -317,6 +341,14 @@ public: */ void createProject(const BuildSetup &setup); + /** + * Returns the last path component. + * + * @param path Path string. + * @return Last path component. + */ + static std::string getLastPathComponent(const std::string &path); + protected: const int _version; ///< Target project version StringList &_globalWarnings; ///< Global warnings diff --git a/devtools/create_project/msbuild.cpp b/devtools/create_project/msbuild.cpp index dfd3f1d1c7..0f77d91852 100644 --- a/devtools/create_project/msbuild.cpp +++ b/devtools/create_project/msbuild.cpp @@ -46,7 +46,13 @@ const char *MSBuildProvider::getPropertiesExtension() { } int MSBuildProvider::getVisualStudioVersion() { - return 2010; + if (_version == 10) + return 2010; + + if (_version == 11) + return 2012; + + error("Unsupported version passed to getVisualStudioVersion"); } namespace { @@ -58,9 +64,10 @@ inline void outputConfiguration(std::ostream &project, const std::string &config "\t\t</ProjectConfiguration>\n"; } -inline void outputConfigurationType(const BuildSetup &setup, std::ostream &project, const std::string &name, const std::string &config) { +inline void outputConfigurationType(const BuildSetup &setup, std::ostream &project, const std::string &name, const std::string &config, int version) { project << "\t<PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='" << config << "'\" Label=\"Configuration\">\n" "\t\t<ConfigurationType>" << ((name == setup.projectName || setup.devTools) ? "Application" : "StaticLibrary") << "</ConfigurationType>\n" + "\t\t<PlatformToolset>v" << version << "0</PlatformToolset>\n" "\t</PropertyGroup>\n"; } @@ -98,17 +105,18 @@ void MSBuildProvider::createProjectFile(const std::string &name, const std::stri "\t\t<ProjectGuid>{" << uuid << "}</ProjectGuid>\n" "\t\t<RootNamespace>" << name << "</RootNamespace>\n" "\t\t<Keyword>Win32Proj</Keyword>\n" + "\t\t<VCTargetsPath Condition=\"'$(VCTargetsPath11)' != '' and '$(VSVersion)' == '' and $(VisualStudioVersion) == ''\">$(VCTargetsPath11)</VCTargetsPath>\n" "\t</PropertyGroup>\n"; // Shared configuration project << "\t<Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\n"; - outputConfigurationType(setup, project, name, "Release|Win32"); - outputConfigurationType(setup, project, name, "Analysis|Win32"); - outputConfigurationType(setup, project, name, "Debug|Win32"); - outputConfigurationType(setup, project, name, "Release|x64"); - outputConfigurationType(setup, project, name, "Analysis|x64"); - outputConfigurationType(setup, project, name, "Debug|x64"); + outputConfigurationType(setup, project, name, "Release|Win32", _version); + outputConfigurationType(setup, project, name, "Analysis|Win32", _version); + outputConfigurationType(setup, project, name, "Debug|Win32", _version); + outputConfigurationType(setup, project, name, "Release|x64", _version); + outputConfigurationType(setup, project, name, "Analysis|x64", _version); + outputConfigurationType(setup, project, name, "Debug|x64", _version); project << "\t<Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\n" "\t<ImportGroup Label=\"ExtensionSettings\">\n" @@ -233,9 +241,11 @@ void MSBuildProvider::outputProjectSettings(std::ofstream &project, const std::s // Check for project-specific warnings: std::map<std::string, StringList>::iterator warningsIterator = _projectWarnings.find(name); + bool enableLanguageExtensions = find(_enableLanguageExtensions.begin(), _enableLanguageExtensions.end(), name) != _enableLanguageExtensions.end(); + bool disableEditAndContinue = find(_disableEditAndContinue.begin(), _disableEditAndContinue.end(), name) != _disableEditAndContinue.end(); // Nothing to add here, move along! - if (!setup.devTools && name != setup.projectName && name != "sword25" && name != "scummvm" && name != "grim" && warningsIterator == _projectWarnings.end()) + if (!setup.devTools && name != setup.projectName && !enableLanguageExtensions && !disableEditAndContinue && warningsIterator == _projectWarnings.end()) return; std::string warnings = ""; @@ -246,16 +256,17 @@ void MSBuildProvider::outputProjectSettings(std::ofstream &project, const std::s project << "\t<ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='" << configuration << "|" << (isWin32 ? "Win32" : "x64") << "'\">\n" "\t\t<ClCompile>\n"; - // Compile configuration - if (setup.devTools || name == setup.projectName || name == "sword25" || name == "grim") { + // Language Extensions + if (setup.devTools || name == setup.projectName || enableLanguageExtensions) project << "\t\t\t<DisableLanguageExtensions>false</DisableLanguageExtensions>\n"; - } else { - if (name == "scummvm" && !isRelease) - project << "\t\t\t<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>\n"; - if (warningsIterator != _projectWarnings.end()) - project << "\t\t\t<DisableSpecificWarnings>" << warnings << ";%(DisableSpecificWarnings)</DisableSpecificWarnings>\n"; - } + // Edit and Continue + if ((name == setup.projectName || disableEditAndContinue) && !isRelease) + project << "\t\t\t<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>\n"; + + // Warnings + if (warningsIterator != _projectWarnings.end()) + project << "\t\t\t<DisableSpecificWarnings>" << warnings << ";%(DisableSpecificWarnings)</DisableSpecificWarnings>\n"; project << "\t\t</ClCompile>\n"; @@ -395,6 +406,7 @@ void MSBuildProvider::createBuildProp(const BuildSetup &setup, bool isRelease, b "\t\t</ClCompile>\n" "\t\t<Link>\n" "\t\t\t<GenerateDebugInformation>true</GenerateDebugInformation>\n" + "\t\t\t<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>\n" "\t\t\t<IgnoreSpecificDefaultLibraries>libcmt.lib;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>\n"; } diff --git a/devtools/create_project/msvc.cpp b/devtools/create_project/msvc.cpp index 96eaf643d1..b8d2401af9 100644 --- a/devtools/create_project/msvc.cpp +++ b/devtools/create_project/msvc.cpp @@ -33,6 +33,9 @@ namespace CreateProjectTool { ////////////////////////////////////////////////////////////////////////// MSVCProvider::MSVCProvider(StringList &global_warnings, std::map<std::string, StringList> &project_warnings, const int version) : ProjectProvider(global_warnings, project_warnings, version) { + + _enableLanguageExtensions = tokenize(ENABLE_LANGUAGE_EXTENSIONS, ','); + _disableEditAndContinue = tokenize(DISABLE_EDIT_AND_CONTINUE, ','); } void MSVCProvider::createWorkspace(const BuildSetup &setup) { @@ -75,10 +78,10 @@ void MSVCProvider::createWorkspace(const BuildSetup &setup) { solution << "Global\n" "\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n" "\t\tDebug|Win32 = Debug|Win32\n" - "\t\tAnalysis|Win32 = Analysis|Win32\n" + "\t\tAnalysis|Win32 = Analysis|Win32\n" "\t\tRelease|Win32 = Release|Win32\n" "\t\tDebug|x64 = Debug|x64\n" - "\t\tAnalysis|x64 = Analysis|x64\n" + "\t\tAnalysis|x64 = Analysis|x64\n" "\t\tRelease|x64 = Release|x64\n" "\tEndGlobalSection\n" "\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n"; @@ -86,14 +89,14 @@ void MSVCProvider::createWorkspace(const BuildSetup &setup) { for (UUIDMap::const_iterator i = _uuidMap.begin(); i != _uuidMap.end(); ++i) { solution << "\t\t{" << i->second << "}.Debug|Win32.ActiveCfg = Debug|Win32\n" "\t\t{" << i->second << "}.Debug|Win32.Build.0 = Debug|Win32\n" - "\t\t{" << i->second << "}.Analysis|Win32.ActiveCfg = Analysis|Win32\n" - "\t\t{" << i->second << "}.Analysis|Win32.Build.0 = Analysis|Win32\n" + "\t\t{" << i->second << "}.Analysis|Win32.ActiveCfg = Analysis|Win32\n" + "\t\t{" << i->second << "}.Analysis|Win32.Build.0 = Analysis|Win32\n" "\t\t{" << i->second << "}.Release|Win32.ActiveCfg = Release|Win32\n" "\t\t{" << i->second << "}.Release|Win32.Build.0 = Release|Win32\n" "\t\t{" << i->second << "}.Debug|x64.ActiveCfg = Debug|x64\n" "\t\t{" << i->second << "}.Debug|x64.Build.0 = Debug|x64\n" - "\t\t{" << i->second << "}.Analysis|x64.ActiveCfg = Analysis|x64\n" - "\t\t{" << i->second << "}.Analysis|x64.Build.0 = Analysis|x64\n" + "\t\t{" << i->second << "}.Analysis|x64.ActiveCfg = Analysis|x64\n" + "\t\t{" << i->second << "}.Analysis|x64.Build.0 = Analysis|x64\n" "\t\t{" << i->second << "}.Release|x64.ActiveCfg = Release|x64\n" "\t\t{" << i->second << "}.Release|x64.Build.0 = Release|x64\n"; } @@ -139,7 +142,7 @@ void MSVCProvider::createGlobalProp(const BuildSetup &setup) { StringList x64EngineDefines = getEngineDefines(setup.engines); x64Defines.splice(x64Defines.end(), x64EngineDefines); - // HACK: This definitly should not be here, but otherwise we would not define SDL_BACKEND for x64. + // HACK: This definitely should not be here, but otherwise we would not define SDL_BACKEND for x64. x64Defines.push_back("WIN32"); x64Defines.push_back("SDL_BACKEND"); @@ -168,7 +171,7 @@ std::string MSVCProvider::getPostBuildEvent(bool isWin32, bool createInstaller) cmdLine += (isWin32) ? "x86" : "x64"; - cmdLine += " %SCUMMVM_LIBS% "; + cmdLine += " %" LIBS_DEFINE "% "; // Specify if installer needs to be built or not cmdLine += (createInstaller ? "1" : "0"); diff --git a/devtools/create_project/msvc.h b/devtools/create_project/msvc.h index 0a994667fa..5a854b596a 100644 --- a/devtools/create_project/msvc.h +++ b/devtools/create_project/msvc.h @@ -32,6 +32,8 @@ public: MSVCProvider(StringList &global_warnings, std::map<std::string, StringList> &project_warnings, const int version); protected: + StringList _enableLanguageExtensions; + StringList _disableEditAndContinue; void createWorkspace(const BuildSetup &setup); diff --git a/devtools/create_project/msvc11/create_project.sln b/devtools/create_project/msvc11/create_project.sln new file mode 100644 index 0000000000..1552c9f502 --- /dev/null +++ b/devtools/create_project/msvc11/create_project.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2012 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "create_project", "create_project.vcxproj", "{CF177559-077D-4A08-AABE-BE0FD35F6C63}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CF177559-077D-4A08-AABE-BE0FD35F6C63}.Debug|Win32.ActiveCfg = Debug|Win32 + {CF177559-077D-4A08-AABE-BE0FD35F6C63}.Debug|Win32.Build.0 = Debug|Win32 + {CF177559-077D-4A08-AABE-BE0FD35F6C63}.Release|Win32.ActiveCfg = Release|Win32 + {CF177559-077D-4A08-AABE-BE0FD35F6C63}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/devtools/create_project/msvc11/create_project.vcxproj b/devtools/create_project/msvc11/create_project.vcxproj new file mode 100644 index 0000000000..c87461c049 --- /dev/null +++ b/devtools/create_project/msvc11/create_project.vcxproj @@ -0,0 +1,131 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{CF177559-077D-4A08-AABE-BE0FD35F6C63}</ProjectGuid> + <RootNamespace>create_project</RootNamespace> + <VCTargetsPath Condition="'$(VCTargetsPath11)' != '' and '$(VSVersion)' == '' and $(VisualStudioVersion) == ''">$(VCTargetsPath11)</VCTargetsPath> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <CharacterSet>MultiByte</CharacterSet> + <WholeProgramOptimization>true</WholeProgramOptimization> + <PlatformToolset>v110</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <CharacterSet>MultiByte</CharacterSet> + <PlatformToolset>v110</PlatformToolset> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup> + <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion> + <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(SolutionDir)$(Configuration)\</OutDir> + <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(Configuration)\</IntDir> + <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(SolutionDir)$(Configuration)\</OutDir> + <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(Configuration)\</IntDir> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <Optimization>Disabled</Optimization> + <MinimalRebuild>true</MinimalRebuild> + <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> + <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> + <WarningLevel>Level4</WarningLevel> + <DebugInformationFormat>EditAndContinue</DebugInformationFormat> + <DisableLanguageExtensions>false</DisableLanguageExtensions> + <DisableSpecificWarnings>4003;4512;4127</DisableSpecificWarnings> + </ClCompile> + <Link> + <AdditionalDependencies>Rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies> + <GenerateDebugInformation>true</GenerateDebugInformation> + <TargetMachine>MachineX86</TargetMachine> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + </Link> + <PostBuildEvent> + <Command>@echo off +xcopy /Y "$(TargetPath)" "$(SolutionDir)\..\..\..\dists\msvc11\" +xcopy /Y "$(TargetPath)" "$(SolutionDir)\..\..\..\dists\msvc10\" +xcopy /Y "$(TargetPath)" "$(SolutionDir)\..\..\..\dists\msvc9\" +xcopy /Y "$(TargetPath)" "$(SolutionDir)\..\..\..\dists\msvc8\" +xcopy /Y "$(TargetPath)" "$(SolutionDir)\..\..\..\dists\codeblocks\" +xcopy /Y "$(TargetPath)" "$(SolutionDir)\..\..\..\dists\iphone\"</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <Optimization>MaxSpeed</Optimization> + <IntrinsicFunctions>true</IntrinsicFunctions> + <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> + <FunctionLevelLinking>true</FunctionLevelLinking> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <DisableSpecificWarnings>4003;4512;4127</DisableSpecificWarnings> + </ClCompile> + <Link> + <AdditionalDependencies>Rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies> + <GenerateDebugInformation>true</GenerateDebugInformation> + <OptimizeReferences>true</OptimizeReferences> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <TargetMachine>MachineX86</TargetMachine> + </Link> + <PostBuildEvent> + <Command>@echo off +xcopy /Y "$(TargetPath)" "$(SolutionDir)\..\..\..\dists\msvc11\" +xcopy /Y "$(TargetPath)" "$(SolutionDir)\..\..\..\dists\msvc10\" +xcopy /Y "$(TargetPath)" "$(SolutionDir)\..\..\..\dists\msvc9\" +xcopy /Y "$(TargetPath)" "$(SolutionDir)\..\..\..\dists\msvc8\" +xcopy /Y "$(TargetPath)" "$(SolutionDir)\..\..\..\dists\codeblocks\" +xcopy /Y "$(TargetPath)" "$(SolutionDir)\..\..\..\dists\iphone\"</Command> + </PostBuildEvent> + <PreBuildEvent> + <Command> + </Command> + </PreBuildEvent> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="..\codeblocks.cpp" /> + <ClCompile Include="..\create_project.cpp" /> + <ClCompile Include="..\msbuild.cpp" /> + <ClCompile Include="..\msvc.cpp" /> + <ClCompile Include="..\visualstudio.cpp" /> + <ClCompile Include="..\xcode.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\codeblocks.h" /> + <ClInclude Include="..\config.h" /> + <ClInclude Include="..\create_project.h" /> + <ClInclude Include="..\msbuild.h" /> + <ClInclude Include="..\msvc.h" /> + <ClInclude Include="..\visualstudio.h" /> + <ClInclude Include="..\xcode.h" /> + </ItemGroup> + <ItemGroup> + <None Include="..\scripts\installer.vbs" /> + <None Include="..\scripts\postbuild.cmd" /> + <None Include="..\scripts\prebuild.cmd" /> + <None Include="..\scripts\revision.vbs" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project>
\ No newline at end of file diff --git a/devtools/create_project/msvc11/create_project.vcxproj.filters b/devtools/create_project/msvc11/create_project.vcxproj.filters new file mode 100644 index 0000000000..b4f0b18774 --- /dev/null +++ b/devtools/create_project/msvc11/create_project.vcxproj.filters @@ -0,0 +1,71 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Header Files"> + <UniqueIdentifier>{2e3580c8-ec3a-4c81-8351-b668c668db2a}</UniqueIdentifier> + </Filter> + <Filter Include="Source Files"> + <UniqueIdentifier>{31aaf58c-d3cb-4ed6-8eca-163b4a9b31a6}</UniqueIdentifier> + </Filter> + <Filter Include="scripts"> + <UniqueIdentifier>{f980f6fb-41b6-4161-b035-58b200c85cad}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\codeblocks.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\create_project.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\msvc.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\msbuild.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\visualstudio.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\xcode.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\config.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <ClCompile Include="..\codeblocks.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\create_project.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\msvc.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\msbuild.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\visualstudio.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\xcode.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <None Include="..\scripts\prebuild.cmd"> + <Filter>scripts</Filter> + </None> + <None Include="..\scripts\revision.vbs"> + <Filter>scripts</Filter> + </None> + <None Include="..\scripts\postbuild.cmd"> + <Filter>scripts</Filter> + </None> + <None Include="..\scripts\installer.vbs"> + <Filter>scripts</Filter> + </None> + </ItemGroup> +</Project> diff --git a/devtools/create_project/scripts/postbuild.cmd b/devtools/create_project/scripts/postbuild.cmd index dd52c0217c..8b70ec3dd8 100644 --- a/devtools/create_project/scripts/postbuild.cmd +++ b/devtools/create_project/scripts/postbuild.cmd @@ -24,7 +24,10 @@ echo Copying data files echo.
xcopy /F /Y "%~4/lib/%~3/SDL.dll" "%~2" 1>NUL 2>&1
+xcopy /F /Y "%~4/lib/%~3/freetype6.dll" "%~2" 1>NUL 2>&1
xcopy /F /Y "%~1/backends/vkeybd/packs/vkeybd_default.zip" "%~2" 1>NUL 2>&1
+xcopy /F /Y "%~1/gui/themes/translations.dat" "%~2" 1>NUL 2>&1
+
if "%~5"=="0" goto done
diff --git a/devtools/create_project/visualstudio.cpp b/devtools/create_project/visualstudio.cpp index 62b30ddcd0..de2df96d78 100644 --- a/devtools/create_project/visualstudio.cpp +++ b/devtools/create_project/visualstudio.cpp @@ -103,6 +103,9 @@ void VisualStudioProvider::createProjectFile(const std::string &name, const std: outputConfiguration(project, setup, libraries, "Release", "x64", "64", false); } else { + bool enableLanguageExtensions = find(_enableLanguageExtensions.begin(), _enableLanguageExtensions.end(), name) != _enableLanguageExtensions.end(); + bool disableEditAndContinue = find(_disableEditAndContinue.begin(), _disableEditAndContinue.end(), name) != _disableEditAndContinue.end(); + std::string warnings = ""; if (warningsIterator != _projectWarnings.end()) for (StringList::const_iterator i = warningsIterator->second.begin(); i != warningsIterator->second.end(); ++i) @@ -110,9 +113,8 @@ void VisualStudioProvider::createProjectFile(const std::string &name, const std: std::string toolConfig; toolConfig = (!warnings.empty() ? "DisableSpecificWarnings=\"" + warnings + "\"" : ""); - toolConfig += (name == "scummvm" ? "DebugInformationFormat=\"3\" " : ""); - toolConfig += (name == "sword25" ? "DisableLanguageExtensions=\"false\" " : ""); - toolConfig += (name == "grim" ? "DisableLanguageExtensions=\"false\" " : ""); + toolConfig += (disableEditAndContinue ? "DebugInformationFormat=\"3\" " : ""); + toolConfig += (enableLanguageExtensions ? "DisableLanguageExtensions=\"false\" " : ""); // Win32 outputConfiguration(setup, project, toolConfig, "Debug", "Win32", ""); diff --git a/devtools/create_project/xcode.cpp b/devtools/create_project/xcode.cpp index 9784bb0bf5..a9b8e7a752 100644 --- a/devtools/create_project/xcode.cpp +++ b/devtools/create_project/xcode.cpp @@ -202,18 +202,38 @@ void XCodeProvider::writeFileListToProject(const FileNode &dir, std::ofstream &p // Init root group _groups.comment = "PBXGroup"; - Object *group = new Object(this, "PBXGroup", "PBXGroup", "PBXGroup", "", ""); - //Property children; - //children.flags = SettingsAsList; - //group->properties["children"] = children; - group->addProperty("children", "", "", SettingsNoValue|SettingsAsList); + // Create group + std::string name = getLastPathComponent(dir.name); + Object *group = new Object(this, "PBXGroup_" + name , "PBXGroup", "PBXGroup", "", name); + // List of children + Property children; + children.hasOrder = true; + children.flags = SettingsAsList; + + group->addProperty("name", name, "", SettingsNoValue|SettingsQuoteVariable); group->addProperty("sourceTree", "<group>", "", SettingsNoValue|SettingsQuoteVariable); - _groups.add(group); + int order = 0; + for (FileNode::NodeList::const_iterator i = dir.children.begin(); i != dir.children.end(); ++i) { + const FileNode *node = *i; + + std::string id = "FileReference_" + node->name; + FileProperty property = FileProperty(node->name, node->name, node->name, "<group>"); + + ADD_SETTING_ORDER_NOVALUE(children, getHash(id), node->name, order++); + ADD_BUILD_FILE(id, node->name, node->name + " in Sources"); + ADD_FILE_REFERENCE(node->name, property); - // TODO Add files + // Process child nodes + if (!node->children.empty()) + writeFileListToProject(*node, projectFile, indentation + 1, duplicate, objPrefix + node->name + '_', filePrefix + node->name + '/'); + } + + group->properties["children"] = children; + + _groups.add(group); } ////////////////////////////////////////////////////////////////////////// @@ -717,6 +737,7 @@ void XCodeProvider::setupBuildConfiguration() { ADD_SETTING_QUOTE(scummvmSimulator_Debug, "FRAMEWORK_SEARCH_PATHS", "$(inherited)"); ADD_SETTING_LIST(scummvmSimulator_Debug, "GCC_PREPROCESSOR_DEFINITIONS", scummvm_defines, SettingsNoQuote|SettingsAsList, 5); ADD_SETTING(scummvmSimulator_Debug, "SDKROOT", "iphonesimulator3.2"); + ADD_SETTING_QUOTE(scummvmSimulator_Debug, "VALID_ARCHS", "i386 x86_64"); REMOVE_SETTING(scummvmSimulator_Debug, "TARGETED_DEVICE_FAMILY"); scummvmSimulator_Debug_Object->addProperty("name", "Debug", "", SettingsNoValue); @@ -726,6 +747,7 @@ void XCodeProvider::setupBuildConfiguration() { Object *scummvmSimulator_Release_Object = new Object(this, "XCBuildConfiguration_" PROJECT_DESCRIPTION "-Simulator_Release", _targets[2] /* ScummVM-Simulator */, "XCBuildConfiguration", "PBXNativeTarget", "Release"); Property scummvmSimulator_Release(scummvmSimulator_Debug); ADD_SETTING(scummvmSimulator_Release, "COPY_PHASE_STRIP", "YES"); + ADD_SETTING(scummvmSimulator_Release, "GCC_OPTIMIZATION_LEVEL", "3"); REMOVE_SETTING(scummvmSimulator_Release, "GCC_DYNAMIC_NO_PIC"); ADD_SETTING(scummvmSimulator_Release, "WRAPPER_EXTENSION", "app"); @@ -895,7 +917,7 @@ std::string XCodeProvider::writeSetting(const std::string &variable, const Setti for (unsigned int i = 0, count = 0; i < setting.entries.size(); ++i) { std::string value = setting.entries.at(i).value; - if(!value.empty()) { + if (!value.empty()) { if (count++ > 0) output += "," + newline; diff --git a/devtools/create_teenagent/create_teenagent.cpp b/devtools/create_teenagent/create_teenagent.cpp index 9551acbaea..79c61900f3 100644 --- a/devtools/create_teenagent/create_teenagent.cpp +++ b/devtools/create_teenagent/create_teenagent.cpp @@ -32,81 +32,111 @@ #include <stdio.h> #include <stdlib.h> #include <assert.h> -#include "md5.h" +#include "util.h" +#include "static_tables.h" -static void print_hex(FILE * f, const uint8 * data, size_t len) { - for (size_t i = 0; i < len; ++i) { - fprintf(f, "%02x", data[i]); - } -} - -static void extract(FILE * fout, FILE *fin, size_t pos, size_t size, const char *what) { - char buf[0x10000]; - assert(size < sizeof(buf)); - - if (fseek(fin, pos, SEEK_SET) != 0) { - perror(what); - exit(1); - } +int main(int argc, char *argv[]) { + const char *dat_name = "teenagent.dat"; - if (fread(buf, size, 1, fin) != 1) { - perror(what); + FILE *fout = fopen(dat_name, "wb"); + if (fout == NULL) { + perror("opening output file"); exit(1); } - if (fwrite(buf, size, 1, fout) != 1) { - perror(what); + if (fwrite(cseg, CSEG_SIZE, 1, fout) != 1) { + perror("Writing code segment"); exit(1); } -} -int main(int argc, char *argv[]) { - if (argc < 2) { - fprintf(stderr, "usage: %s: Teenagnt.exe (unpacked one)\n", argv[0]); + if (fwrite(dsegStartBlock, DSEG_STARTBLK_SIZE, 1, fout) != 1) { + perror("Writing data segment start block"); exit(1); } - const char * fname = argv[1]; - uint8 digest[16]; - if (!md5_file(fname, digest, 0)) { - fprintf(stderr, "cannot calculate md5 for %s", fname); - exit(1); + // Write out message string block + for (uint i = 0; i < (sizeof(messages)/sizeof(char*)); i++) { + if (i == 0) { + // Write out reject message pointer block + uint16 off = DSEG_STARTBLK_SIZE + (4 * 2); + writeUint16LE(fout, off); + off += strlen(messages[0]) + 2; + writeUint16LE(fout, off); + off += strlen(messages[1]) + 2; + writeUint16LE(fout, off); + off += strlen(messages[2]) + 2; + writeUint16LE(fout, off); + } + + if (i == 327) { + // Write out book color pointer block + uint16 off = DSEG_STARTBLK_SIZE + (4 * 2); + for (uint k = 0; k < 327; k++) + off += strlen(messages[k]) + 2; + off += (6 * 2); + writeUint16LE(fout, off); + off += strlen(messages[327]) + 2; + writeUint16LE(fout, off); + off += strlen(messages[328]) + 2; + writeUint16LE(fout, off); + off += strlen(messages[329]) + 2; + writeUint16LE(fout, off); + off += strlen(messages[330]) + 2; + writeUint16LE(fout, off); + off += strlen(messages[331]) + 2; + writeUint16LE(fout, off); + } + for (uint j = 0; j < strlen(messages[i]); j++) { + if (messages[i][j] == '\n') + writeByte(fout, '\0'); + else + writeByte(fout, messages[i][j]); + } + writeByte(fout, '\0'); + writeByte(fout, '\0'); } - const uint8 ethalon[16] = { - 0x51, 0xb6, 0xd6, 0x47, 0x21, 0xf7, 0xc4, 0xb4, - 0x98, 0xbf, 0xc0, 0xf3, 0x23, 0x01, 0x3e, 0x36, - }; - - if (memcmp(digest, ethalon, 16) != 0) { - fprintf(stderr, "cannot extract data, your md5: "); - print_hex(stderr, digest, 16); - fprintf(stderr, ", need md5: "); - print_hex(stderr, ethalon, 16); - fprintf(stderr, ", sorry\n"); - exit(1); - } - FILE *fin = fopen(fname, "rb"); - if (fin == NULL) { - perror("opening input file"); + if (fwrite(dsegEndBlock, DSEG_ENDBLK_SIZE, 1, fout) != 1) { + perror("Writing data segment end block"); exit(1); } - const char * dat_name = "teenagent.dat"; - FILE *fout = fopen(dat_name, "wb"); - if (fout == NULL) { - perror("opening output file"); - exit(1); + // Write out dialog string block + static const char nulls[6] = "\0\0\0\0\0"; + for (uint i = 0; i < (sizeof(dialogs)/sizeof(char**)); i++) { + //printf("Writing Dialog #%d\n", i); + bool dialogEnd = false; + uint j = 0; + while (!dialogEnd) { + uint nullCount = 0; + if (strcmp(dialogs[i][j], NEW_LINE) == 0) { + nullCount = 1; + } else if (strcmp(dialogs[i][j], DISPLAY_MESSAGE) == 0) { + nullCount = 2; + } else if (strcmp(dialogs[i][j], CHANGE_CHARACTER) == 0) { + nullCount = 3; + } else if (strcmp(dialogs[i][j], END_DIALOG) == 0) { + nullCount = 4; + dialogEnd = true; + } else { // Deals with normal dialogue and ANIM_WAIT cases + if (fwrite(dialogs[i][j], 1, strlen(dialogs[i][j]), fout) != strlen(dialogs[i][j])) { + perror("Writing dialog string"); + exit(1); + } + } + + if (nullCount != 0 && nullCount < 5) { + if (fwrite(nulls, 1, nullCount, fout) != nullCount) { + perror("Writing dialog string nulls"); + exit(1); + } + } + + j++; + } } - //0x0200, 0xb5b0, 0x1c890 - extract(fout, fin, 0x00200, 0xb3b0, "extracting code segment"); - extract(fout, fin, 0x0b5b0, 0xe790, "extracting data segment"); - extract(fout, fin, 0x1c890, 0x8be2, "extracting second data segment"); - fclose(fin); fclose(fout); - fprintf(stderr, "please run \"gzip -n %s\"\n", dat_name); - return 0; } diff --git a/devtools/create_teenagent/md5.cpp b/devtools/create_teenagent/md5.cpp deleted file mode 100644 index 9f90122981..0000000000 --- a/devtools/create_teenagent/md5.cpp +++ /dev/null @@ -1,264 +0,0 @@ -/* 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. - * - */ - -// Disable symbol overrides so that we can use system headers. -#define FORBIDDEN_SYMBOL_ALLOW_ALL - -#include "md5.h" - -#define GET_UINT32(n, b, i) (n) = READ_LE_UINT32(b + i) -#define PUT_UINT32(n, b, i) WRITE_LE_UINT32(b + i, n) - -void md5_starts(md5_context *ctx) { - ctx->total[0] = 0; - ctx->total[1] = 0; - - ctx->state[0] = 0x67452301; - ctx->state[1] = 0xEFCDAB89; - ctx->state[2] = 0x98BADCFE; - ctx->state[3] = 0x10325476; -} - -static void md5_process(md5_context *ctx, const uint8 data[64]) { - uint32 X[16], A, B, C, D; - - GET_UINT32(X[0], data, 0); - GET_UINT32(X[1], data, 4); - GET_UINT32(X[2], data, 8); - GET_UINT32(X[3], data, 12); - GET_UINT32(X[4], data, 16); - GET_UINT32(X[5], data, 20); - GET_UINT32(X[6], data, 24); - GET_UINT32(X[7], data, 28); - GET_UINT32(X[8], data, 32); - GET_UINT32(X[9], data, 36); - GET_UINT32(X[10], data, 40); - GET_UINT32(X[11], data, 44); - GET_UINT32(X[12], data, 48); - GET_UINT32(X[13], data, 52); - GET_UINT32(X[14], data, 56); - GET_UINT32(X[15], data, 60); - -#define S(x, n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n))) - -#define P(a, b, c, d, k, s, t) \ -{ \ - a += F(b,c,d) + X[k] + t; a = S(a,s) + b; \ -} - - A = ctx->state[0]; - B = ctx->state[1]; - C = ctx->state[2]; - D = ctx->state[3]; - -#define F(x, y, z) (z ^ (x & (y ^ z))) - - P(A, B, C, D, 0, 7, 0xD76AA478); - P(D, A, B, C, 1, 12, 0xE8C7B756); - P(C, D, A, B, 2, 17, 0x242070DB); - P(B, C, D, A, 3, 22, 0xC1BDCEEE); - P(A, B, C, D, 4, 7, 0xF57C0FAF); - P(D, A, B, C, 5, 12, 0x4787C62A); - P(C, D, A, B, 6, 17, 0xA8304613); - P(B, C, D, A, 7, 22, 0xFD469501); - P(A, B, C, D, 8, 7, 0x698098D8); - P(D, A, B, C, 9, 12, 0x8B44F7AF); - P(C, D, A, B, 10, 17, 0xFFFF5BB1); - P(B, C, D, A, 11, 22, 0x895CD7BE); - P(A, B, C, D, 12, 7, 0x6B901122); - P(D, A, B, C, 13, 12, 0xFD987193); - P(C, D, A, B, 14, 17, 0xA679438E); - P(B, C, D, A, 15, 22, 0x49B40821); - -#undef F - -#define F(x, y, z) (y ^ (z & (x ^ y))) - - P(A, B, C, D, 1, 5, 0xF61E2562); - P(D, A, B, C, 6, 9, 0xC040B340); - P(C, D, A, B, 11, 14, 0x265E5A51); - P(B, C, D, A, 0, 20, 0xE9B6C7AA); - P(A, B, C, D, 5, 5, 0xD62F105D); - P(D, A, B, C, 10, 9, 0x02441453); - P(C, D, A, B, 15, 14, 0xD8A1E681); - P(B, C, D, A, 4, 20, 0xE7D3FBC8); - P(A, B, C, D, 9, 5, 0x21E1CDE6); - P(D, A, B, C, 14, 9, 0xC33707D6); - P(C, D, A, B, 3, 14, 0xF4D50D87); - P(B, C, D, A, 8, 20, 0x455A14ED); - P(A, B, C, D, 13, 5, 0xA9E3E905); - P(D, A, B, C, 2, 9, 0xFCEFA3F8); - P(C, D, A, B, 7, 14, 0x676F02D9); - P(B, C, D, A, 12, 20, 0x8D2A4C8A); - -#undef F - -#define F(x, y, z) (x ^ y ^ z) - - P(A, B, C, D, 5, 4, 0xFFFA3942); - P(D, A, B, C, 8, 11, 0x8771F681); - P(C, D, A, B, 11, 16, 0x6D9D6122); - P(B, C, D, A, 14, 23, 0xFDE5380C); - P(A, B, C, D, 1, 4, 0xA4BEEA44); - P(D, A, B, C, 4, 11, 0x4BDECFA9); - P(C, D, A, B, 7, 16, 0xF6BB4B60); - P(B, C, D, A, 10, 23, 0xBEBFBC70); - P(A, B, C, D, 13, 4, 0x289B7EC6); - P(D, A, B, C, 0, 11, 0xEAA127FA); - P(C, D, A, B, 3, 16, 0xD4EF3085); - P(B, C, D, A, 6, 23, 0x04881D05); - P(A, B, C, D, 9, 4, 0xD9D4D039); - P(D, A, B, C, 12, 11, 0xE6DB99E5); - P(C, D, A, B, 15, 16, 0x1FA27CF8); - P(B, C, D, A, 2, 23, 0xC4AC5665); - -#undef F - -#define F(x, y, z) (y ^ (x | ~z)) - - P(A, B, C, D, 0, 6, 0xF4292244); - P(D, A, B, C, 7, 10, 0x432AFF97); - P(C, D, A, B, 14, 15, 0xAB9423A7); - P(B, C, D, A, 5, 21, 0xFC93A039); - P(A, B, C, D, 12, 6, 0x655B59C3); - P(D, A, B, C, 3, 10, 0x8F0CCC92); - P(C, D, A, B, 10, 15, 0xFFEFF47D); - P(B, C, D, A, 1, 21, 0x85845DD1); - P(A, B, C, D, 8, 6, 0x6FA87E4F); - P(D, A, B, C, 15, 10, 0xFE2CE6E0); - P(C, D, A, B, 6, 15, 0xA3014314); - P(B, C, D, A, 13, 21, 0x4E0811A1); - P(A, B, C, D, 4, 6, 0xF7537E82); - P(D, A, B, C, 11, 10, 0xBD3AF235); - P(C, D, A, B, 2, 15, 0x2AD7D2BB); - P(B, C, D, A, 9, 21, 0xEB86D391); - -#undef F - - ctx->state[0] += A; - ctx->state[1] += B; - ctx->state[2] += C; - ctx->state[3] += D; -} - -void md5_update(md5_context *ctx, const uint8 *input, uint32 length) { - uint32 left, fill; - - if (!length) - return; - - left = ctx->total[0] & 0x3F; - fill = 64 - left; - - ctx->total[0] += length; - ctx->total[0] &= 0xFFFFFFFF; - - if (ctx->total[0] < length) - ctx->total[1]++; - - if (left && length >= fill) { - memcpy((void *)(ctx->buffer + left), (const void *)input, fill); - md5_process(ctx, ctx->buffer); - length -= fill; - input += fill; - left = 0; - } - - while (length >= 64) { - md5_process(ctx, input); - length -= 64; - input += 64; - } - - if (length) { - memcpy((void *)(ctx->buffer + left), (const void *)input, length); - } -} - -static const uint8 md5_padding[64] = { - 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - -void md5_finish(md5_context *ctx, uint8 digest[16]) { - uint32 last, padn; - uint32 high, low; - uint8 msglen[8]; - - high = (ctx->total[0] >> 29) | (ctx->total[1] << 3); - low = (ctx->total[0] << 3); - - PUT_UINT32(low, msglen, 0); - PUT_UINT32(high, msglen, 4); - - last = ctx->total[0] & 0x3F; - padn = (last < 56) ? (56 - last) : (120 - last); - - md5_update(ctx, md5_padding, padn); - md5_update(ctx, msglen, 8); - - PUT_UINT32(ctx->state[0], digest, 0); - PUT_UINT32(ctx->state[1], digest, 4); - PUT_UINT32(ctx->state[2], digest, 8); - PUT_UINT32(ctx->state[3], digest, 12); -} - -bool md5_file(const char *name, uint8 digest[16], uint32 length) { - FILE *f; - - f = fopen(name, "rb"); - if (f == NULL) { - printf("md5_file couldn't open '%s'\n", name); - return false; - } - - md5_context ctx; - uint32 i; - unsigned char buf[1000]; - bool restricted = (length != 0); - int readlen; - - if (!restricted || sizeof(buf) <= length) - readlen = sizeof(buf); - else - readlen = length; - - md5_starts(&ctx); - - - while ((i = (uint32)fread(buf, 1, readlen, f)) > 0) { - md5_update(&ctx, buf, i); - - length -= i; - if (restricted && length == 0) - break; - - if (restricted && sizeof(buf) > length) - readlen = length; - } - - md5_finish(&ctx, digest); - fclose(f); - return true; -} diff --git a/devtools/create_teenagent/module.mk b/devtools/create_teenagent/module.mk index a9d102addb..7d01a2ba85 100644 --- a/devtools/create_teenagent/module.mk +++ b/devtools/create_teenagent/module.mk @@ -3,7 +3,7 @@ MODULE := devtools/create_teenagent MODULE_OBJS := \ create_teenagent.o \ - md5.o + util.o # Set the name of the executable TOOL_EXECUTABLE := create_teenagent diff --git a/devtools/create_teenagent/static_tables.h b/devtools/create_teenagent/static_tables.h new file mode 100644 index 0000000000..6e7fdfe91c --- /dev/null +++ b/devtools/create_teenagent/static_tables.h @@ -0,0 +1,16317 @@ +/* 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 STATIC_TABLES_H +#define STATIC_TABLES_H + +// Static data tables for Teenagent engine + +// Unpacked Executable MD5sum - 51b6d64721f7c4b498bfc0f323013e36 + +// Code Segment +// starts at offset 0x0200 in original executable +#define CSEG_SIZE 46000 // 0xb3b0 + +const static uint8 cseg[CSEG_SIZE] = { + 0xb8, 0x3b, 0x0b, 0x8e, 0xd8, 0x8e, 0xc0, 0xe9, + 0xd2, 0x00, 0x9c, 0xfa, 0x60, 0x1e, 0x06, 0xb8, + 0x3b, 0x0b, 0x8e, 0xd8, 0xeb, 0x0e, 0xa0, 0x48, + 0x32, 0xb4, 0x03, 0xff, 0x1e, 0x4a, 0x32, 0xb8, + 0x3b, 0x0b, 0x8e, 0xd8, 0x8e, 0xc0, 0xbb, 0x86, + 0x32, 0xff, 0x07, 0x75, 0x03, 0xff, 0x47, 0x02, + 0x33, 0xc0, 0xe8, 0xce, 0xa4, 0xff, 0x0e, 0x7f, + 0x32, 0x75, 0x14, 0xa1, 0x81, 0x32, 0xa3, 0x7f, + 0x32, 0x33, 0xc0, 0x8e, 0xd8, 0xbb, 0x6c, 0x04, + 0xff, 0x07, 0x75, 0x03, 0xff, 0x47, 0x02, 0xb0, + 0x20, 0xe6, 0x20, 0x07, 0x1f, 0x61, 0xfb, 0x9d, + 0xcf, 0x9c, 0xfa, 0x60, 0x1e, 0x06, 0xb8, 0x3b, + 0x0b, 0x8e, 0xd8, 0xeb, 0x15, 0x80, 0x3e, 0x47, + 0x32, 0x00, 0x74, 0x0e, 0xa0, 0x48, 0x32, 0xb4, + 0x03, 0xff, 0x1e, 0x4a, 0x32, 0xb8, 0x3b, 0x0b, + 0x8e, 0xd8, 0x8e, 0xc0, 0xfe, 0x0e, 0x83, 0x32, + 0x75, 0x14, 0xc6, 0x06, 0x83, 0x32, 0x6e, 0xbb, + 0x86, 0x32, 0xff, 0x07, 0x75, 0x03, 0xff, 0x47, + 0x02, 0x33, 0xc0, 0xe8, 0x6d, 0xa4, 0xff, 0x0e, + 0x7f, 0x32, 0x75, 0x14, 0xa1, 0x81, 0x32, 0xa3, + 0x7f, 0x32, 0x33, 0xc0, 0x8e, 0xd8, 0xbb, 0x6c, + 0x04, 0xff, 0x07, 0x75, 0x03, 0xff, 0x47, 0x02, + 0xb0, 0x20, 0xe6, 0x20, 0x07, 0x1f, 0x61, 0xfb, + 0x9d, 0xcf, 0x9c, 0x50, 0xe4, 0x60, 0x2e, 0xa2, + 0xd7, 0x00, 0xe4, 0x61, 0x8a, 0xe0, 0x0c, 0x80, + 0xe6, 0x61, 0x86, 0xe0, 0xe6, 0x61, 0xeb, 0x00, + 0xb0, 0x20, 0xe6, 0x20, 0x58, 0x9d, 0xcf, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xb4, 0x2c, 0xcd, 0x21, + 0xbb, 0x5f, 0xb1, 0x2e, 0x89, 0x0f, 0x2e, 0x89, + 0x57, 0x02, 0xe8, 0xfc, 0xa3, 0xe8, 0xb1, 0xb0, + 0xe8, 0xce, 0xaf, 0xe8, 0xee, 0xb0, 0xe8, 0x18, + 0xa8, 0xe8, 0x4d, 0xb2, 0xe8, 0xde, 0xa4, 0xb9, + 0x01, 0x00, 0xe8, 0x46, 0xb0, 0xfa, 0xb8, 0x88, + 0xc0, 0xbb, 0x14, 0x00, 0x2e, 0x89, 0x07, 0xbb, + 0x63, 0x00, 0x2e, 0x89, 0x07, 0xfb, 0xe8, 0x38, + 0x0a, 0xe8, 0x49, 0x0a, 0xe8, 0x6f, 0x0a, 0xe8, + 0x79, 0x0a, 0xe8, 0x9a, 0x0a, 0xe8, 0xb5, 0x0a, + 0xb8, 0x3b, 0x0b, 0xa3, 0xac, 0x00, 0xb8, 0x3a, + 0x01, 0xa3, 0xaa, 0x00, 0xb9, 0x07, 0x00, 0xba, + 0x54, 0x00, 0xe8, 0x33, 0xae, 0xa1, 0xbf, 0x32, + 0xa3, 0xac, 0x00, 0x33, 0xc0, 0xa3, 0xaa, 0x00, + 0xb9, 0x01, 0x00, 0xba, 0x54, 0x00, 0xe8, 0x1f, + 0xae, 0xe8, 0x82, 0x05, 0xe8, 0xf5, 0x00, 0xb9, + 0x04, 0x00, 0xe8, 0xee, 0xaf, 0xb8, 0x88, 0x00, + 0xbb, 0x99, 0x00, 0xbf, 0x8b, 0x00, 0xbe, 0x9c, + 0x00, 0xb5, 0x03, 0xb1, 0x0b, 0xc7, 0x06, 0xf3, + 0xb4, 0x0a, 0x00, 0xe8, 0x6d, 0xaa, 0xe8, 0x86, + 0xa4, 0xe8, 0x04, 0x07, 0xe8, 0xa0, 0x1e, 0xe8, + 0x03, 0x2a, 0xe8, 0x94, 0x33, 0xe8, 0xc8, 0x34, + 0xe8, 0x22, 0x33, 0xe8, 0x57, 0x33, 0xe8, 0xe5, + 0x1d, 0xe8, 0x12, 0x27, 0xe8, 0x0d, 0xa5, 0xb3, + 0x01, 0x72, 0x07, 0xe8, 0x35, 0xa5, 0xb3, 0x00, + 0x73, 0xd7, 0xe8, 0xf6, 0xa1, 0xeb, 0xd2, 0xb8, + 0x03, 0x00, 0xcd, 0x10, 0xb4, 0x02, 0xff, 0x1e, + 0x4a, 0x32, 0xb8, 0x3b, 0x0b, 0x8e, 0xd8, 0x8e, + 0xc0, 0xe9, 0xd8, 0xb1, 0xe8, 0x86, 0x9a, 0xe8, + 0x77, 0x00, 0xe8, 0x8f, 0x9a, 0xbb, 0x88, 0xe4, + 0x33, 0xf6, 0x33, 0xff, 0x06, 0xa1, 0xb1, 0x32, + 0x8e, 0xc0, 0x53, 0xe8, 0x26, 0x00, 0xe8, 0x19, + 0x00, 0x5b, 0x53, 0xe8, 0xca, 0xa2, 0xbe, 0x40, + 0x01, 0x2b, 0xf0, 0xd1, 0xee, 0x5b, 0xe8, 0x6c, + 0xa2, 0x43, 0x8a, 0x07, 0x0a, 0xc0, 0x75, 0xe2, + 0x07, 0xc3, 0xa1, 0xb1, 0x32, 0xb9, 0xe0, 0x06, + 0xe8, 0x05, 0xab, 0xc3, 0x1e, 0x06, 0x8c, 0xc0, + 0x8e, 0xd8, 0xb8, 0x00, 0xa0, 0x8e, 0xc0, 0x33, + 0xf6, 0xbf, 0xbf, 0xf8, 0xb9, 0x0b, 0x00, 0x51, + 0xb9, 0xa0, 0x00, 0x57, 0xf3, 0xa5, 0x56, 0xe8, + 0x08, 0x00, 0x5e, 0x5f, 0x59, 0xe2, 0xf0, 0x07, + 0x1f, 0xc3, 0xe8, 0xeb, 0xa4, 0xe8, 0xe8, 0xa4, + 0x1e, 0x8c, 0xc0, 0x8e, 0xd8, 0x33, 0xff, 0xbe, + 0x40, 0x01, 0xb9, 0x00, 0x7d, 0xf3, 0xa5, 0x1f, + 0xc3, 0xb8, 0x00, 0xa0, 0xb9, 0x00, 0x7d, 0xe8, + 0xbe, 0xaa, 0xa1, 0xb1, 0x32, 0xb9, 0x00, 0x7d, + 0xe8, 0xb5, 0xaa, 0xc3, 0xe8, 0x7c, 0x03, 0xe8, + 0x6c, 0x03, 0xc7, 0x06, 0xf3, 0xb4, 0x29, 0x00, + 0xb8, 0x8b, 0x00, 0xbb, 0x9c, 0x00, 0x8b, 0xf8, + 0x8b, 0xf3, 0xb5, 0x03, 0xb1, 0x0b, 0xe8, 0x8c, + 0xa9, 0xe8, 0xfc, 0xa9, 0xe8, 0x5c, 0x03, 0xe8, + 0x4c, 0x03, 0xe8, 0x97, 0xaa, 0xe8, 0x46, 0x03, + 0xb8, 0xc8, 0x00, 0xe8, 0xbc, 0x1c, 0xe8, 0x3d, + 0x03, 0xb9, 0x29, 0x00, 0xb0, 0x0c, 0xb4, 0x04, + 0xe8, 0x72, 0xa7, 0xbb, 0x47, 0x33, 0xc7, 0x47, + 0x02, 0x90, 0x03, 0xb0, 0x02, 0xe8, 0xca, 0x17, + 0xe8, 0x23, 0x03, 0xe8, 0x4a, 0x3d, 0xc6, 0x07, + 0x6c, 0xe8, 0xfe, 0xab, 0xb9, 0x3e, 0x00, 0xb0, + 0x08, 0xb4, 0x04, 0xe8, 0x4f, 0xa7, 0xb9, 0x3a, + 0x00, 0xb0, 0x28, 0xb4, 0x0a, 0xe8, 0x4e, 0xa7, + 0xbb, 0x47, 0x33, 0xc7, 0x47, 0x02, 0x91, 0x03, + 0xb0, 0x02, 0xe8, 0x9d, 0x17, 0xe8, 0xf6, 0x02, + 0xe8, 0x1d, 0x3d, 0xc6, 0x47, 0x01, 0x6d, 0xe8, + 0xd0, 0xab, 0xc6, 0x06, 0x45, 0x33, 0xe7, 0xc6, + 0x06, 0x46, 0x33, 0xd7, 0xb0, 0x02, 0xb4, 0x01, + 0xbe, 0x92, 0x03, 0xbf, 0x93, 0x03, 0xbb, 0x8e, + 0x74, 0xe8, 0x82, 0x11, 0xe8, 0xcf, 0x02, 0xbb, + 0xc2, 0xe3, 0xe8, 0xe9, 0x02, 0xbb, 0x47, 0x33, + 0xc7, 0x47, 0x02, 0x94, 0x03, 0xc7, 0x06, 0xf3, + 0xb4, 0x2a, 0x00, 0xe8, 0x06, 0xaa, 0xe8, 0xb5, + 0x02, 0xb9, 0x0f, 0x00, 0xb0, 0x14, 0xb4, 0x03, + 0xe8, 0xea, 0xa6, 0xbb, 0x47, 0x33, 0xc7, 0x47, + 0x02, 0x94, 0x03, 0xb0, 0x02, 0xe8, 0x42, 0x17, + 0xe8, 0x9b, 0x02, 0xb9, 0x28, 0x00, 0xb0, 0x12, + 0xb4, 0x0e, 0xe8, 0xd0, 0xa6, 0xb0, 0x16, 0xe8, + 0xd4, 0xa6, 0xb0, 0x1b, 0xe8, 0xd6, 0xa6, 0xb0, + 0x1d, 0xe8, 0xd8, 0xa6, 0xb0, 0x1f, 0xe8, 0xda, + 0xa6, 0xb0, 0x21, 0xe8, 0xe3, 0xa6, 0xb0, 0x23, + 0xe8, 0xe5, 0xa6, 0xb0, 0x25, 0xe8, 0xe7, 0xa6, + 0xb9, 0x1d, 0x00, 0xb0, 0x2c, 0xe8, 0xe6, 0xa6, + 0xbb, 0x47, 0x33, 0xc7, 0x07, 0x96, 0x03, 0xc7, + 0x47, 0x02, 0x95, 0x03, 0xb0, 0x01, 0xe8, 0xf9, + 0x16, 0xe8, 0x52, 0x02, 0xbb, 0xe6, 0xe3, 0xe8, + 0x6c, 0x02, 0xb9, 0x03, 0x00, 0xe8, 0xd3, 0xad, + 0xbb, 0x47, 0x33, 0xc7, 0x07, 0x98, 0x03, 0xc7, + 0x47, 0x02, 0x9c, 0x03, 0xc7, 0x06, 0xf3, 0xb4, + 0x28, 0x00, 0xe8, 0x7f, 0xa9, 0xe8, 0x2e, 0x02, + 0xc6, 0x06, 0x45, 0x33, 0xe7, 0xc6, 0x06, 0x46, + 0x33, 0xeb, 0xb0, 0x01, 0xb4, 0x02, 0xbe, 0x98, + 0x03, 0xbf, 0x9c, 0x03, 0xbb, 0x0d, 0x75, 0xe8, + 0xc4, 0x10, 0xe8, 0x11, 0x02, 0xb9, 0x1a, 0x00, + 0xb0, 0x32, 0xb4, 0x0a, 0xe8, 0x46, 0xa6, 0xbb, + 0x47, 0x33, 0xc7, 0x07, 0x9d, 0x03, 0xc7, 0x47, + 0x02, 0x9e, 0x03, 0xb0, 0x01, 0xe8, 0x9a, 0x16, + 0xe8, 0xf3, 0x01, 0xc6, 0x06, 0x45, 0x33, 0xeb, + 0xb0, 0x02, 0xb4, 0x01, 0xbe, 0x9f, 0x03, 0xbf, + 0x98, 0x03, 0xbb, 0xa6, 0x78, 0xe8, 0x8e, 0x10, + 0xe8, 0xdb, 0x01, 0xbb, 0xff, 0xe3, 0xe8, 0xf5, + 0x01, 0xb9, 0x0b, 0x00, 0xe8, 0x5c, 0xad, 0xc7, + 0x06, 0xf3, 0xb4, 0x27, 0x00, 0xe8, 0x14, 0xa9, + 0xe8, 0xc3, 0x01, 0xb8, 0xc8, 0x00, 0xe8, 0x39, + 0x1b, 0xe8, 0xba, 0x01, 0xb9, 0x51, 0x00, 0xb0, + 0x02, 0xb4, 0x0e, 0xe8, 0xef, 0xa5, 0xb0, 0x05, + 0xb4, 0x0b, 0xe8, 0xf1, 0xa5, 0xb0, 0x08, 0xb4, + 0x08, 0xe8, 0xf1, 0xa5, 0xb0, 0x0b, 0xb4, 0x06, + 0xe8, 0xf1, 0xa5, 0xb0, 0x0e, 0xb4, 0x05, 0xe8, + 0xf1, 0xa5, 0xb0, 0x10, 0xb4, 0x03, 0xe8, 0xf1, + 0xa5, 0xb0, 0x10, 0xe8, 0xf3, 0xa5, 0xb0, 0x12, + 0xe8, 0xf5, 0xa5, 0xb0, 0x14, 0xe8, 0xf7, 0xa5, + 0xb0, 0x15, 0xe8, 0xf9, 0xa5, 0xbb, 0x47, 0x33, + 0xc7, 0x47, 0x02, 0xa0, 0x03, 0xb0, 0x02, 0xe8, + 0x10, 0x16, 0xe8, 0x49, 0xa6, 0xe8, 0x66, 0x01, + 0xe8, 0x8d, 0x3b, 0xc6, 0x07, 0x70, 0xe8, 0x41, + 0xaa, 0xe8, 0xf1, 0x1a, 0xe8, 0x57, 0x01, 0xc6, + 0x06, 0x45, 0x33, 0xd1, 0xb0, 0x01, 0xbe, 0xa1, + 0x03, 0xbb, 0xe1, 0x78, 0xe8, 0x80, 0x11, 0xe8, + 0x44, 0x01, 0xe8, 0xd8, 0x1a, 0xe8, 0x3e, 0x01, + 0xc6, 0x06, 0xdc, 0x1c, 0x02, 0xc7, 0x06, 0xaf, + 0x64, 0x3f, 0x01, 0xc7, 0x06, 0xb1, 0x64, 0x96, + 0x00, 0xbe, 0x3f, 0x00, 0xbf, 0x96, 0x00, 0xc6, + 0x06, 0xc3, 0x64, 0x01, 0xe8, 0x8c, 0x31, 0xe8, + 0x1c, 0x01, 0xc6, 0x06, 0x35, 0x33, 0x12, 0xc6, + 0x06, 0x36, 0x33, 0x24, 0xb8, 0xa8, 0x5d, 0xa3, + 0x37, 0x33, 0xb8, 0x84, 0x4d, 0xa3, 0x39, 0x33, + 0xbb, 0x47, 0x33, 0xc7, 0x07, 0x53, 0x03, 0xb9, + 0xa2, 0x03, 0xe8, 0xaa, 0x16, 0xe8, 0xf6, 0x00, + 0xb9, 0x18, 0x00, 0xb0, 0x0b, 0xb4, 0x02, 0xe8, + 0x2b, 0xa5, 0xb9, 0xa3, 0x03, 0xe8, 0x1a, 0x16, + 0xe8, 0xb8, 0xa6, 0xe8, 0xe0, 0x00, 0xbb, 0x2f, + 0xe4, 0xe8, 0xfa, 0x00, 0xb9, 0x03, 0x00, 0xe8, + 0x61, 0xac, 0xc7, 0x06, 0xaf, 0x64, 0x32, 0x00, + 0xc7, 0x06, 0xb1, 0x64, 0xba, 0x00, 0xc6, 0x06, + 0xcc, 0x64, 0x01, 0xc6, 0x06, 0xdc, 0x64, 0x00, + 0xc6, 0x06, 0xcb, 0x64, 0x00, 0xe8, 0x71, 0x39, + 0xbb, 0x47, 0x33, 0xc7, 0x07, 0x97, 0x03, 0xc7, + 0x06, 0xf3, 0xb4, 0x28, 0x00, 0xe8, 0xd0, 0x3a, + 0xc6, 0x07, 0x71, 0xe8, 0xf7, 0xa7, 0xe8, 0x9d, + 0x00, 0xc6, 0x06, 0x45, 0x33, 0xe7, 0xb0, 0x01, + 0xbe, 0x97, 0x03, 0xbb, 0xf1, 0x78, 0xe8, 0xc6, + 0x10, 0xe8, 0x8a, 0x00, 0xbe, 0xc6, 0x00, 0xbf, + 0xba, 0x00, 0xc6, 0x06, 0xc3, 0x64, 0x01, 0xe8, + 0xe9, 0x30, 0xe8, 0x79, 0x00, 0xe8, 0x8f, 0x11, + 0xbb, 0x58, 0x79, 0xe8, 0x77, 0x0c, 0xb9, 0xa4, + 0x03, 0xe8, 0xc3, 0x15, 0xe8, 0x67, 0x00, 0xe8, + 0x7d, 0x11, 0xbb, 0x07, 0x7e, 0xe8, 0x65, 0x0c, + 0xb9, 0xa4, 0x03, 0xe8, 0xb1, 0x15, 0xe8, 0x55, + 0x00, 0xe8, 0x6b, 0x11, 0xbb, 0x1a, 0x7e, 0xe8, + 0x53, 0x0c, 0xb9, 0xa4, 0x03, 0xe8, 0x9f, 0x15, + 0xe8, 0x43, 0x00, 0xe8, 0x7c, 0x11, 0xbb, 0x2c, + 0x7e, 0xe8, 0x41, 0x0c, 0xb9, 0xa5, 0x03, 0xe8, + 0x8d, 0x15, 0xe8, 0x31, 0x00, 0xe8, 0x47, 0x11, + 0xbb, 0x70, 0x7e, 0xe8, 0x2f, 0x0c, 0xbe, 0xae, + 0x00, 0xbf, 0xba, 0x00, 0xc6, 0x06, 0xc3, 0x64, + 0x01, 0xe8, 0x87, 0x30, 0xbb, 0x47, 0x33, 0xc7, + 0x07, 0x53, 0x03, 0xb9, 0xa6, 0x03, 0xe8, 0x66, + 0x15, 0xe8, 0x91, 0x96, 0xe8, 0x14, 0x00, 0xb8, + 0x64, 0x00, 0xe8, 0x9f, 0x19, 0xc3, 0x2e, 0x80, + 0x3e, 0xd7, 0x00, 0x01, 0x75, 0x04, 0x58, 0xe8, + 0xd4, 0xa4, 0xc3, 0x06, 0xa1, 0xb3, 0x32, 0x8e, + 0xc0, 0xbf, 0x00, 0xfa, 0xb9, 0x80, 0x01, 0x33, + 0xc0, 0xfc, 0xf3, 0xab, 0x07, 0xc3, 0x53, 0xe8, + 0x63, 0x96, 0xe8, 0x54, 0xfc, 0xa1, 0xb5, 0x32, + 0xa3, 0xac, 0x00, 0x33, 0xc0, 0xa3, 0xaa, 0x00, + 0xb9, 0x08, 0x00, 0xba, 0x54, 0x00, 0xe8, 0x77, + 0xa9, 0x5b, 0x8b, 0x17, 0x83, 0xc3, 0x02, 0x52, + 0x8a, 0x07, 0x32, 0xe4, 0xf7, 0x26, 0xb6, 0x00, + 0x8b, 0xf0, 0x53, 0xe8, 0x9b, 0x00, 0x5b, 0x5a, + 0x2d, 0x40, 0x01, 0xf7, 0xd8, 0xd1, 0xe8, 0x03, + 0xf0, 0x43, 0xe8, 0x25, 0x00, 0x43, 0x80, 0x3f, + 0x00, 0x75, 0xdc, 0xe8, 0x2e, 0x96, 0xbb, 0x92, + 0x32, 0xb9, 0x90, 0x01, 0xe8, 0xb4, 0xa2, 0xbb, + 0x92, 0x32, 0xe8, 0xc1, 0xa2, 0x72, 0x0a, 0xe8, + 0x99, 0xa0, 0x72, 0x05, 0xe8, 0x65, 0xa0, 0x73, + 0xee, 0xc3, 0x1e, 0x06, 0xb8, 0x00, 0xa0, 0x8e, + 0xc0, 0xa1, 0xb5, 0x32, 0x8e, 0xd8, 0xe8, 0x03, + 0x00, 0x07, 0x1f, 0xc3, 0x1e, 0xb8, 0x3b, 0x0b, + 0x8e, 0xd8, 0x8a, 0x0f, 0x1f, 0x0a, 0xc9, 0x74, + 0x47, 0x80, 0xe9, 0x1f, 0xb5, 0x00, 0xbf, 0xfe, + 0xff, 0xd1, 0xe1, 0x03, 0xf9, 0x8b, 0x3d, 0x8b, + 0x0d, 0x83, 0xc7, 0x02, 0x51, 0x56, 0x51, 0x56, + 0x8a, 0x05, 0x0a, 0xc0, 0x74, 0x0d, 0xfe, 0xc8, + 0x0a, 0xc0, 0x8a, 0xc2, 0x74, 0x02, 0x8a, 0xc6, + 0x26, 0x88, 0x04, 0x47, 0x46, 0xfe, 0xcd, 0x75, + 0xe7, 0x5e, 0x59, 0x81, 0xc6, 0x40, 0x01, 0xfe, + 0xc9, 0x75, 0xdb, 0x5e, 0x59, 0x8a, 0xc5, 0xb4, + 0x00, 0x03, 0xf0, 0x46, 0x46, 0x43, 0xeb, 0xac, + 0xc3, 0x33, 0xc0, 0x8a, 0x0f, 0x0a, 0xc9, 0x74, + 0x24, 0x42, 0x80, 0xe9, 0x1f, 0xb5, 0x00, 0xbf, + 0xfe, 0xff, 0xd1, 0xe1, 0x03, 0xf9, 0x1e, 0x8b, + 0x16, 0xb5, 0x32, 0x8e, 0xda, 0x8b, 0x3d, 0x8b, + 0x0d, 0x1f, 0x8a, 0xcd, 0xb5, 0x00, 0x03, 0xc1, + 0x40, 0x40, 0x43, 0xeb, 0xd6, 0xc3, 0xe8, 0x60, + 0xfb, 0xa1, 0xb5, 0x32, 0xa3, 0xac, 0x00, 0x33, + 0xc0, 0xa3, 0xaa, 0x00, 0xb9, 0x06, 0x00, 0xba, + 0x54, 0x00, 0xe8, 0x83, 0xa8, 0xa1, 0xb3, 0x32, + 0xa3, 0xac, 0x00, 0xb8, 0x00, 0xfa, 0xa3, 0xaa, + 0x00, 0xb9, 0x05, 0x00, 0xba, 0x54, 0x00, 0xe8, + 0x6e, 0xa8, 0x8b, 0x16, 0xb3, 0x32, 0xbe, 0x00, + 0xfa, 0xb0, 0x40, 0xe8, 0x14, 0xa0, 0xa1, 0xb3, + 0x32, 0xa3, 0xac, 0x00, 0x2e, 0xa3, 0x53, 0x08, + 0xb8, 0x00, 0x1e, 0xa3, 0xaa, 0x00, 0xb9, 0x09, + 0x00, 0xba, 0x54, 0x00, 0xe8, 0x49, 0xa8, 0x1e, + 0x06, 0x8b, 0x2e, 0xb5, 0x32, 0xa1, 0xb1, 0x32, + 0x8e, 0xc0, 0x8e, 0xd8, 0xfc, 0xb9, 0x58, 0x02, + 0xe8, 0x0f, 0x00, 0x07, 0x1f, 0x8b, 0x16, 0xb3, + 0x32, 0xbe, 0x00, 0xfa, 0xb0, 0xc0, 0xe8, 0xd9, + 0x9f, 0xc3, 0x2e, 0x80, 0x3e, 0xd7, 0x00, 0x00, + 0x75, 0x07, 0x51, 0xe8, 0x04, 0x00, 0x59, 0xe2, + 0xf1, 0xc3, 0xb8, 0xa0, 0x00, 0xbf, 0x00, 0x00, + 0x03, 0xf8, 0x03, 0xf8, 0x8b, 0xf7, 0x03, 0xf0, + 0xb9, 0x90, 0x10, 0xf3, 0xa5, 0x83, 0xc7, 0x00, + 0xb9, 0x11, 0x00, 0x51, 0x57, 0xb8, 0x9f, 0x00, + 0xe8, 0xe8, 0xa9, 0x03, 0xf8, 0xb8, 0xb9, 0x00, + 0xe8, 0xe0, 0xa9, 0x04, 0x05, 0x8a, 0xe0, 0x89, + 0x05, 0x5f, 0x59, 0xe2, 0xe6, 0xb9, 0x01, 0x00, + 0xbe, 0x01, 0x00, 0x8b, 0xc6, 0xbb, 0xa0, 0x00, + 0xf7, 0xe3, 0x8b, 0xf8, 0x03, 0xf9, 0x33, 0xc0, + 0x33, 0xdb, 0x8a, 0x9d, 0x5f, 0xff, 0x8a, 0x85, + 0x60, 0xff, 0x03, 0xd8, 0x8a, 0x85, 0x61, 0xff, + 0x03, 0xd8, 0x8a, 0x45, 0xff, 0x03, 0xd8, 0x8a, + 0x45, 0x01, 0x03, 0xd8, 0x8a, 0x85, 0x9f, 0x00, + 0x03, 0xd8, 0x8a, 0x85, 0xa0, 0x00, 0x03, 0xd8, + 0x8a, 0x85, 0xa1, 0x00, 0x03, 0xd8, 0xc1, 0xeb, + 0x03, 0x88, 0x9d, 0x00, 0x23, 0x41, 0x81, 0xf9, + 0xa0, 0x00, 0x72, 0xb7, 0xb9, 0x01, 0x00, 0x46, + 0x83, 0xfe, 0x38, 0x72, 0xae, 0xbf, 0x00, 0x00, + 0xbe, 0x00, 0x23, 0xb9, 0x80, 0x11, 0xf3, 0xa5, + 0x06, 0xb8, 0x00, 0xa0, 0x8e, 0xc0, 0x33, 0xdb, + 0xbe, 0x00, 0x00, 0xbf, 0xc0, 0x6c, 0xb9, 0x2c, + 0x00, 0xb8, 0x40, 0x01, 0xf7, 0xe1, 0x03, 0xf8, + 0xd1, 0xe9, 0x8b, 0xd1, 0x83, 0xea, 0x04, 0xb8, + 0xa0, 0x00, 0xf7, 0xe2, 0x03, 0xf0, 0xb8, 0x38, + 0x00, 0x2b, 0xc1, 0x8b, 0xc8, 0xe8, 0xf0, 0x9e, + 0x56, 0xe8, 0x0b, 0x00, 0x5e, 0xe8, 0x07, 0x00, + 0xe2, 0xf6, 0xe8, 0x28, 0x00, 0x07, 0xc3, 0xba, + 0xa0, 0x00, 0x8a, 0x04, 0x1e, 0x8e, 0xdd, 0x80, + 0x3f, 0x01, 0x75, 0x03, 0x26, 0x88, 0x05, 0x43, + 0x47, 0x80, 0x3f, 0x01, 0x1f, 0x75, 0x03, 0x26, + 0x88, 0x05, 0x43, 0x47, 0x46, 0x4a, 0x75, 0xe2, + 0xc3, 0x80, 0x66, 0x00, 0x00, 0x2e, 0x81, 0x2e, + 0x51, 0x08, 0x80, 0x02, 0x73, 0x08, 0x2e, 0x81, + 0x06, 0x51, 0x08, 0x80, 0x02, 0xc3, 0x2e, 0x8b, + 0x36, 0x51, 0x08, 0x1e, 0x2e, 0xa1, 0x53, 0x08, + 0x8e, 0xd8, 0x33, 0xff, 0xfc, 0xb9, 0x80, 0x66, + 0x2b, 0xce, 0xd1, 0xe9, 0xf3, 0xa5, 0x1f, 0xc3, + 0x80, 0x3e, 0x95, 0x60, 0x01, 0x74, 0x0a, 0xe8, + 0x32, 0x00, 0x80, 0x3e, 0x95, 0x60, 0x01, 0x75, + 0x2a, 0xc6, 0x06, 0x95, 0x60, 0x00, 0xe8, 0xa1, + 0x18, 0xe8, 0xf8, 0x06, 0xe8, 0x35, 0x00, 0xe8, + 0x05, 0xa0, 0xc6, 0x06, 0xdc, 0x1c, 0x03, 0xc6, + 0x06, 0xda, 0x1c, 0x00, 0xe8, 0xc7, 0x1f, 0xe8, + 0x5e, 0x9e, 0xe8, 0x27, 0xa0, 0xc7, 0x06, 0x52, + 0x72, 0x00, 0x00, 0xc3, 0x2e, 0xa0, 0xd7, 0x00, + 0x3c, 0x3b, 0x72, 0x0a, 0x3c, 0x44, 0x77, 0x06, + 0xc6, 0x06, 0x95, 0x60, 0x01, 0xc3, 0xc6, 0x06, + 0x95, 0x60, 0x00, 0xc3, 0xc7, 0x06, 0x96, 0x60, + 0x00, 0x00, 0xc6, 0x06, 0x98, 0x60, 0x00, 0xbb, + 0x99, 0x60, 0xe8, 0x0e, 0x00, 0x72, 0x01, 0xc3, + 0xe8, 0x2d, 0x01, 0x80, 0x3e, 0x98, 0x60, 0x01, + 0x75, 0xe2, 0xc3, 0x83, 0xeb, 0x05, 0x89, 0x1e, + 0x93, 0x60, 0x2e, 0x80, 0x3e, 0xd7, 0x00, 0x01, + 0x75, 0x03, 0xe9, 0xb4, 0x00, 0x33, 0xc9, 0x8b, + 0x1e, 0x93, 0x60, 0x41, 0x83, 0xc3, 0x05, 0x8a, + 0x47, 0x04, 0x3c, 0x03, 0x74, 0xf5, 0x8b, 0x07, + 0x0a, 0xc0, 0x75, 0x28, 0x8b, 0x0e, 0x96, 0x60, + 0x0b, 0xc9, 0x74, 0xd6, 0x8b, 0x1e, 0x93, 0x60, + 0x83, 0xc3, 0x05, 0xe2, 0xfb, 0x8b, 0x07, 0xe8, + 0x8f, 0x00, 0x8a, 0x57, 0x02, 0xb6, 0x00, 0xb4, + 0xe0, 0xe8, 0x96, 0x00, 0xc7, 0x06, 0x96, 0x60, + 0x00, 0x00, 0xeb, 0xb6, 0x53, 0x51, 0xe8, 0x78, + 0x00, 0x8a, 0x4f, 0x02, 0xbb, 0x0e, 0x00, 0xe8, + 0x24, 0x1b, 0x59, 0x5b, 0x72, 0x02, 0xeb, 0xb3, + 0xa1, 0x96, 0x60, 0x3b, 0xc8, 0x74, 0x37, 0x0b, + 0xc0, 0x74, 0x1e, 0x53, 0x51, 0x8b, 0xc8, 0x8b, + 0x1e, 0x93, 0x60, 0x83, 0xc3, 0x05, 0xe2, 0xfb, + 0x8b, 0x07, 0xe8, 0x4c, 0x00, 0x8a, 0x57, 0x02, + 0xb6, 0x00, 0xb4, 0xe0, 0xe8, 0x53, 0x00, 0x59, + 0x5b, 0x89, 0x0e, 0x96, 0x60, 0x8b, 0x07, 0xe8, + 0x37, 0x00, 0x8a, 0x57, 0x02, 0xb6, 0x00, 0xb4, + 0xd1, 0x56, 0xe8, 0x3d, 0x00, 0x5e, 0xe8, 0x3a, + 0x9d, 0x72, 0x08, 0xe8, 0x06, 0x9d, 0x72, 0x03, + 0xe9, 0x57, 0xff, 0x8b, 0x0e, 0x96, 0x60, 0x8b, + 0x1e, 0x93, 0x60, 0x83, 0xc3, 0x05, 0xe2, 0xfb, + 0x8b, 0x47, 0x03, 0x3c, 0x0a, 0x74, 0x02, 0xf9, + 0xc3, 0xc7, 0x06, 0x96, 0x60, 0x00, 0x00, 0xf8, + 0xc3, 0x8a, 0xc8, 0xb5, 0x00, 0x8a, 0xc4, 0xb4, + 0x00, 0xf7, 0x26, 0xb6, 0x00, 0x03, 0xc1, 0x8b, + 0xf0, 0xc3, 0x06, 0x8b, 0x0e, 0xb1, 0x32, 0x8e, + 0xc1, 0x50, 0x56, 0xe8, 0x0f, 0x00, 0x5e, 0x58, + 0xb9, 0x00, 0xa0, 0x8e, 0xc1, 0xe8, 0x05, 0x00, + 0xe8, 0xec, 0x9b, 0x07, 0xc3, 0x8b, 0xca, 0x26, + 0x88, 0x24, 0x46, 0xe2, 0xfa, 0x81, 0xc6, 0x40, + 0x01, 0x2b, 0xf2, 0xb9, 0x0c, 0x00, 0x26, 0x88, + 0x24, 0x03, 0xf2, 0x26, 0x88, 0x64, 0xff, 0x2b, + 0xf2, 0x81, 0xc6, 0x40, 0x01, 0xe2, 0xef, 0x8b, + 0xca, 0x26, 0x88, 0x24, 0x46, 0xe2, 0xfa, 0xc3, + 0xbb, 0x94, 0x60, 0x83, 0xc3, 0x05, 0x3a, 0x47, + 0x03, 0x75, 0xf8, 0x80, 0x7f, 0x04, 0x03, 0x74, + 0x06, 0x80, 0x7f, 0x04, 0x00, 0x75, 0xec, 0x50, + 0x8b, 0x07, 0xe8, 0x8c, 0xff, 0x58, 0xb9, 0x0e, + 0x00, 0x8a, 0x57, 0x02, 0xb6, 0x00, 0x3c, 0x01, + 0x75, 0x04, 0xe8, 0xb9, 0x01, 0xc3, 0x3c, 0x02, + 0x75, 0x04, 0xe8, 0x8f, 0x02, 0xc3, 0x3c, 0x03, + 0x75, 0x24, 0x80, 0xfc, 0x01, 0x75, 0x0d, 0xa0, + 0xa8, 0x64, 0x3c, 0x0a, 0x74, 0x17, 0xfe, 0x06, + 0xa8, 0x64, 0xeb, 0x0b, 0xa0, 0xa8, 0x64, 0x0a, + 0xc0, 0x74, 0x0a, 0xfe, 0x0e, 0xa8, 0x64, 0xe8, + 0xa4, 0x05, 0xe8, 0xdc, 0x00, 0xc3, 0x3c, 0x04, + 0x75, 0x24, 0x80, 0xfc, 0x01, 0x75, 0x0d, 0xa0, + 0xa9, 0x64, 0x3c, 0x0a, 0x74, 0x17, 0xfe, 0x06, + 0xa9, 0x64, 0xeb, 0x0b, 0xa0, 0xa9, 0x64, 0x0a, + 0xc0, 0x74, 0x0a, 0xfe, 0x0e, 0xa9, 0x64, 0xe8, + 0x9e, 0x05, 0xe8, 0xc8, 0x00, 0xc3, 0x3c, 0x05, + 0x75, 0x24, 0x80, 0xfc, 0x01, 0x75, 0x0d, 0xa0, + 0xaa, 0x64, 0x3c, 0x03, 0x74, 0x17, 0xfe, 0x06, + 0xaa, 0x64, 0xeb, 0x0b, 0xa0, 0xaa, 0x64, 0x0a, + 0xc0, 0x74, 0x0a, 0xfe, 0x0e, 0xaa, 0x64, 0xe8, + 0x98, 0x05, 0xe8, 0xc9, 0x00, 0xc3, 0x3c, 0x06, + 0x75, 0x24, 0x80, 0xfc, 0x01, 0x75, 0x0d, 0xa0, + 0xab, 0x64, 0x3c, 0x03, 0x74, 0x17, 0xfe, 0x06, + 0xab, 0x64, 0xeb, 0x0b, 0xa0, 0xab, 0x64, 0x0a, + 0xc0, 0x74, 0x0a, 0xfe, 0x0e, 0xab, 0x64, 0xe8, + 0x92, 0x05, 0xe8, 0xd2, 0x00, 0xc3, 0x3c, 0x07, + 0x75, 0x24, 0x80, 0xfc, 0x01, 0x75, 0x0d, 0xa0, + 0xac, 0x64, 0x3c, 0x04, 0x74, 0x17, 0xfe, 0x06, + 0xac, 0x64, 0xeb, 0x0b, 0xa0, 0xac, 0x64, 0x0a, + 0xc0, 0x74, 0x0a, 0xfe, 0x0e, 0xac, 0x64, 0xe8, + 0x8c, 0x05, 0xe8, 0x86, 0x00, 0xc3, 0x3c, 0x08, + 0x75, 0x24, 0x80, 0xfc, 0x01, 0x75, 0x0d, 0xa0, + 0xad, 0x64, 0x3c, 0x01, 0x74, 0x17, 0xfe, 0x06, + 0xad, 0x64, 0xeb, 0x0b, 0xa0, 0xad, 0x64, 0x0a, + 0xc0, 0x74, 0x0a, 0xfe, 0x0e, 0xad, 0x64, 0xe8, + 0x86, 0x05, 0xe8, 0xa0, 0x00, 0xc3, 0x3c, 0x0b, + 0x75, 0x04, 0xe8, 0x9c, 0x03, 0xc3, 0x3c, 0x0d, + 0x75, 0x02, 0xc3, 0xc3, 0xb0, 0x3c, 0xe9, 0x17, + 0xa8, 0xa0, 0xa8, 0x64, 0x0a, 0xc0, 0x75, 0x06, + 0xc6, 0x06, 0x48, 0x32, 0x00, 0xc3, 0xe8, 0x22, + 0x00, 0xa2, 0x48, 0x32, 0xc3, 0xa0, 0xa9, 0x64, + 0x0a, 0xc0, 0x75, 0x0b, 0xc6, 0x06, 0x49, 0x32, + 0x00, 0xc6, 0x06, 0x47, 0x32, 0x00, 0xc3, 0xc6, + 0x06, 0x47, 0x32, 0x01, 0xe8, 0x04, 0x00, 0xa2, + 0x49, 0x32, 0xc3, 0x8a, 0xc8, 0xb5, 0x00, 0xb0, + 0x04, 0x04, 0x06, 0xe2, 0xfc, 0xc3, 0xb0, 0x03, + 0x2a, 0x06, 0xaa, 0x64, 0xc0, 0xe0, 0x02, 0xa2, + 0x8e, 0x32, 0xc3, 0xa0, 0xac, 0x64, 0x0a, 0xc0, + 0x74, 0x19, 0x8a, 0xe0, 0xb0, 0x64, 0x80, 0xfc, + 0x01, 0x74, 0x10, 0xb0, 0x32, 0x80, 0xfc, 0x02, + 0x74, 0x09, 0xb0, 0x14, 0x80, 0xfc, 0x03, 0x74, + 0x02, 0xb0, 0x01, 0xa2, 0x96, 0x32, 0xc3, 0x8a, + 0x26, 0xab, 0x64, 0xb0, 0x10, 0x0a, 0xe4, 0x74, + 0x10, 0xb0, 0x0b, 0x80, 0xfc, 0x01, 0x74, 0x09, + 0xb0, 0x05, 0x80, 0xfc, 0x02, 0x74, 0x02, 0xb0, + 0x01, 0xa2, 0x90, 0x32, 0xc3, 0x80, 0x3e, 0xad, + 0x64, 0x01, 0x75, 0x0e, 0x8b, 0x16, 0xb3, 0x32, + 0xbe, 0x00, 0xfa, 0xb0, 0x40, 0xe8, 0x32, 0x9b, + 0xeb, 0x03, 0xe8, 0xcc, 0xa0, 0xc6, 0x06, 0x08, + 0x66, 0x00, 0xe8, 0xdc, 0x9b, 0xc3, 0xc7, 0x06, + 0x96, 0x60, 0x00, 0x00, 0xe8, 0x23, 0x02, 0xbb, + 0x32, 0x64, 0xe8, 0xe6, 0xfc, 0x72, 0x0a, 0xc7, + 0x06, 0x96, 0x60, 0x00, 0x00, 0xe8, 0x7c, 0x03, + 0xc3, 0x8b, 0x0e, 0x96, 0x60, 0x49, 0xe8, 0xa5, + 0x02, 0x0b, 0xc9, 0x74, 0xd9, 0xb8, 0xa6, 0x64, + 0x8b, 0xd8, 0x8b, 0x1f, 0x3b, 0xc3, 0x75, 0x1e, + 0xbb, 0xa4, 0x64, 0x8b, 0x07, 0xbb, 0xaf, 0xb3, + 0x81, 0xeb, 0x00, 0x00, 0x3b, 0xc3, 0x75, 0x0e, + 0xb8, 0xf2, 0xdb, 0xbb, 0x78, 0x64, 0x2b, 0xc3, + 0x3b, 0xc1, 0x75, 0x02, 0xeb, 0x35, 0xb8, 0xa6, + 0x64, 0xa3, 0xa6, 0x64, 0xb8, 0xaf, 0xb3, 0xa3, + 0xa4, 0x64, 0x06, 0xa1, 0xb1, 0x32, 0x8e, 0xc0, + 0xbe, 0x79, 0x51, 0xb0, 0xdb, 0xb4, 0x00, 0xb9, + 0x46, 0x00, 0xba, 0xce, 0x00, 0xe8, 0x07, 0x05, + 0xbb, 0xc3, 0x62, 0xbe, 0xbb, 0x62, 0xe8, 0x9f, + 0x04, 0xe8, 0x60, 0x9c, 0x07, 0xe8, 0x82, 0x9a, + 0xe9, 0x7b, 0xff, 0x89, 0x1e, 0xaa, 0x00, 0x8c, + 0x1e, 0xac, 0x00, 0xba, 0x95, 0x00, 0xe8, 0x56, + 0xa3, 0x58, 0x81, 0xfc, 0x00, 0x02, 0x72, 0xf9, + 0xe8, 0xb6, 0xfe, 0xe8, 0xc7, 0xfe, 0xe8, 0xed, + 0xfe, 0xe8, 0xf7, 0xfe, 0xe8, 0x18, 0xff, 0xe8, + 0x33, 0xff, 0x8b, 0x0e, 0x90, 0xdb, 0xe8, 0x9a, + 0xa4, 0x83, 0x3e, 0xf3, 0xb4, 0x18, 0x75, 0x0b, + 0xe8, 0xe0, 0x4c, 0xc6, 0x06, 0xdc, 0x1c, 0x02, + 0xe9, 0xb6, 0xf4, 0xa1, 0xaf, 0x64, 0x8b, 0x1e, + 0xb1, 0x64, 0x8b, 0xf8, 0x8b, 0xf3, 0x8a, 0x0e, + 0x07, 0x66, 0x8a, 0x2e, 0xc3, 0x64, 0xe8, 0x0a, + 0x9f, 0xe9, 0x9d, 0xf4, 0xc7, 0x06, 0x96, 0x60, + 0x00, 0x00, 0xe8, 0x45, 0x01, 0xbb, 0x32, 0x64, + 0xe8, 0x08, 0xfc, 0x72, 0x0a, 0xc7, 0x06, 0x96, + 0x60, 0x00, 0x00, 0xe8, 0x9e, 0x02, 0xc3, 0x56, + 0xe8, 0xe2, 0x98, 0x5e, 0xe8, 0x38, 0x00, 0x73, + 0x29, 0xba, 0x95, 0x00, 0x8b, 0xda, 0xa1, 0x96, + 0x60, 0x48, 0x04, 0x30, 0x88, 0x47, 0x07, 0xb8, + 0x78, 0x64, 0xb9, 0xf2, 0xdb, 0x2b, 0xc8, 0xa3, + 0xaa, 0x00, 0x8c, 0x1e, 0xac, 0x00, 0xe8, 0x6d, + 0xa3, 0xc6, 0x06, 0x98, 0x60, 0x01, 0xe8, 0xd6, + 0x98, 0xc3, 0xc7, 0x06, 0x96, 0x60, 0x00, 0x00, + 0xe8, 0x61, 0x02, 0xe8, 0xc9, 0x98, 0xc3, 0x81, + 0xc6, 0x85, 0x02, 0x8b, 0x0e, 0x96, 0x60, 0x49, + 0x56, 0xc7, 0x06, 0xa6, 0x00, 0x18, 0x00, 0xe8, + 0x30, 0x01, 0x5e, 0xbb, 0x78, 0x64, 0x80, 0x3f, + 0x28, 0x74, 0x0b, 0x33, 0xc9, 0x41, 0x43, 0x80, + 0x3f, 0x00, 0x75, 0xf9, 0xeb, 0x08, 0xbb, 0x78, + 0x64, 0xc6, 0x07, 0x00, 0x33, 0xc9, 0xe8, 0x48, + 0x00, 0x33, 0xc0, 0x2e, 0xa0, 0xd7, 0x00, 0x3a, + 0xc4, 0x8a, 0xe0, 0x74, 0xf6, 0x3c, 0x80, 0x73, + 0xf2, 0x3c, 0x1c, 0x74, 0x1c, 0x3c, 0x01, 0x74, + 0x14, 0x3c, 0x0e, 0x74, 0x1a, 0x83, 0xf9, 0x16, + 0x73, 0xe1, 0xe8, 0x61, 0x00, 0x73, 0xdc, 0x41, + 0xe8, 0x1e, 0x00, 0xeb, 0xd6, 0x33, 0xc9, 0xf8, + 0xc3, 0x0b, 0xc9, 0x74, 0xce, 0xf9, 0xc3, 0x0b, + 0xc9, 0x74, 0xc8, 0x49, 0xbb, 0x78, 0x64, 0x03, + 0xd9, 0xc6, 0x07, 0x00, 0xe8, 0x02, 0x00, 0xeb, + 0xba, 0x50, 0x51, 0x06, 0xb8, 0x00, 0xa0, 0x8e, + 0xc0, 0x56, 0xe8, 0x13, 0x00, 0x5e, 0x56, 0xbb, + 0x78, 0x64, 0xe8, 0x90, 0x96, 0xbb, 0x90, 0x64, + 0xe8, 0x8a, 0x96, 0x5e, 0x07, 0x59, 0x58, 0xc3, + 0xbb, 0x78, 0x64, 0xe8, 0xd2, 0x96, 0x03, 0xf0, + 0xb9, 0x0b, 0x00, 0x51, 0xb9, 0x14, 0x00, 0x26, + 0xc6, 0x04, 0xe8, 0x46, 0xe2, 0xf9, 0x59, 0x81, + 0xc6, 0x2c, 0x01, 0xe2, 0xee, 0xc3, 0xbb, 0x3f, + 0x60, 0x3a, 0x07, 0x74, 0x0b, 0x83, 0xc3, 0x02, + 0x81, 0xfb, 0x93, 0x60, 0x72, 0xf3, 0xf8, 0xc3, + 0x50, 0x53, 0x51, 0x06, 0x56, 0xb8, 0x00, 0xa0, + 0x8e, 0xc0, 0xe8, 0xc3, 0xff, 0x5e, 0x56, 0xbb, + 0x78, 0x64, 0xe8, 0x40, 0x96, 0x5e, 0x07, 0x59, + 0x5b, 0x58, 0x8a, 0x5f, 0x01, 0xbf, 0x78, 0x64, + 0x03, 0xf9, 0x88, 0x1d, 0xc6, 0x45, 0x01, 0x00, + 0xf9, 0xc3, 0x06, 0xa1, 0xb1, 0x32, 0x8e, 0xc0, + 0xe8, 0xb7, 0x02, 0xbb, 0x70, 0x64, 0xbe, 0x6e, + 0x64, 0xe8, 0xdc, 0x02, 0xe8, 0x0b, 0x00, 0xbb, + 0x32, 0x64, 0xe8, 0xb6, 0x02, 0xe8, 0x94, 0x9a, + 0x07, 0xc3, 0x33, 0xc9, 0x51, 0xc7, 0x06, 0xa6, + 0x00, 0x18, 0x00, 0xe8, 0x24, 0x00, 0x59, 0x51, + 0xbe, 0x3b, 0x0f, 0x8b, 0xc1, 0xb9, 0x0f, 0x00, + 0xf7, 0xe1, 0xf7, 0x26, 0xb6, 0x00, 0x03, 0xf0, + 0x81, 0xc6, 0x85, 0x02, 0xbb, 0x78, 0x64, 0xe8, + 0xe3, 0x95, 0x59, 0x41, 0x83, 0xf9, 0x0a, 0x72, + 0xd3, 0xc3, 0xba, 0x95, 0x00, 0x8b, 0xda, 0x8a, + 0xc1, 0x04, 0x30, 0x88, 0x47, 0x07, 0xb8, 0x00, + 0x3d, 0xb1, 0x00, 0xcd, 0x21, 0x72, 0x1c, 0x8b, + 0xd8, 0xb4, 0x3e, 0xcd, 0x21, 0x72, 0x2c, 0x8c, + 0x1e, 0xac, 0x00, 0xb8, 0x78, 0x64, 0xa3, 0xaa, + 0x00, 0xc7, 0x06, 0xa8, 0x00, 0x00, 0x00, 0xe8, + 0x9d, 0xa1, 0xc3, 0x3d, 0x02, 0x00, 0x75, 0x13, + 0xbb, 0x78, 0x64, 0xc6, 0x07, 0x28, 0xc6, 0x47, + 0x01, 0x2d, 0xc6, 0x47, 0x02, 0x29, 0xc6, 0x47, + 0x03, 0x00, 0xc3, 0xe9, 0xa2, 0xa4, 0xb8, 0x2f, + 0x00, 0xa3, 0xa6, 0x00, 0xe8, 0xab, 0xff, 0xbb, + 0x78, 0x64, 0x80, 0x3f, 0x28, 0x75, 0x03, 0x33, + 0xc9, 0xc3, 0xba, 0x95, 0x00, 0xe8, 0x48, 0xa0, + 0xc3, 0xe8, 0xf9, 0x96, 0xe8, 0x1e, 0x12, 0xe8, + 0x15, 0x97, 0x06, 0xa1, 0xb1, 0x32, 0x8e, 0xc0, + 0xbe, 0x26, 0x54, 0xb0, 0xe8, 0xb4, 0x00, 0xb9, + 0x42, 0x00, 0xba, 0x74, 0x00, 0xe8, 0x77, 0x02, + 0xbb, 0x27, 0x62, 0xbe, 0x21, 0x62, 0xe8, 0x0f, + 0x02, 0xe8, 0xd0, 0x99, 0x07, 0x2e, 0x8a, 0x26, + 0xd7, 0x00, 0xe8, 0x8f, 0x97, 0x72, 0x1c, 0xe8, + 0xb9, 0x97, 0x72, 0x17, 0x2e, 0xa0, 0xd7, 0x00, + 0x3c, 0x80, 0x73, 0xee, 0x0a, 0xc0, 0x74, 0xea, + 0x3a, 0xc4, 0x74, 0xe6, 0x3c, 0x15, 0x75, 0x03, + 0xe9, 0x74, 0xf2, 0xc7, 0x06, 0x96, 0x60, 0x00, + 0x00, 0xe8, 0xa1, 0x96, 0xe8, 0xfb, 0x11, 0xe8, + 0xbd, 0x96, 0xe8, 0x4f, 0x00, 0xc3, 0xe8, 0x94, + 0x96, 0xe8, 0xb9, 0x11, 0xe8, 0xb0, 0x96, 0x06, + 0xa1, 0xb1, 0x32, 0x8e, 0xc0, 0xbe, 0x6d, 0x51, + 0xb0, 0xe8, 0xb4, 0x00, 0xb9, 0x46, 0x00, 0xba, + 0xe6, 0x00, 0xe8, 0x12, 0x02, 0xbb, 0x52, 0x62, + 0xbe, 0x4a, 0x62, 0xe8, 0xaa, 0x01, 0xe8, 0x6b, + 0x99, 0x07, 0x2e, 0x8a, 0x26, 0xd7, 0x00, 0xe8, + 0x2a, 0x97, 0x72, 0x05, 0xe8, 0x54, 0x97, 0x73, + 0xf6, 0xc7, 0x06, 0x96, 0x60, 0x00, 0x00, 0xe8, + 0x53, 0x96, 0xe8, 0xad, 0x11, 0xe8, 0x6f, 0x96, + 0xe8, 0x01, 0x00, 0xc3, 0xc6, 0x06, 0xe6, 0x1c, + 0xd1, 0x06, 0xa1, 0xb1, 0x32, 0x8e, 0xc0, 0xe8, + 0x48, 0x01, 0xbb, 0x29, 0x61, 0xbe, 0x11, 0x61, + 0xe8, 0x6d, 0x01, 0xbb, 0x99, 0x60, 0xe8, 0x4a, + 0x01, 0xe8, 0x14, 0x00, 0xe8, 0x1e, 0x00, 0xe8, + 0x28, 0x00, 0xe8, 0x32, 0x00, 0xe8, 0x3c, 0x00, + 0xe8, 0x46, 0x00, 0xe8, 0x16, 0x99, 0x07, 0xc3, + 0xb0, 0x06, 0xbb, 0x29, 0x63, 0x8a, 0x0e, 0xa8, + 0x64, 0xe8, 0x54, 0x01, 0xc3, 0xb0, 0x06, 0xbb, + 0x6b, 0x63, 0x8a, 0x0e, 0xa9, 0x64, 0xe8, 0x47, + 0x01, 0xc3, 0xb0, 0x09, 0xbb, 0xad, 0x63, 0x8a, + 0x0e, 0xaa, 0x64, 0xe8, 0x3a, 0x01, 0xc3, 0xb0, + 0x09, 0xbb, 0xd1, 0x63, 0x8a, 0x0e, 0xab, 0x64, + 0xe8, 0x2d, 0x01, 0xc3, 0xb0, 0x09, 0xbb, 0xf5, + 0x63, 0x8a, 0x0e, 0xac, 0x64, 0xe8, 0x20, 0x01, + 0xc3, 0xb0, 0x08, 0xbb, 0x22, 0x64, 0x8a, 0x0e, + 0xad, 0x64, 0xe8, 0x13, 0x01, 0xc3, 0x06, 0xa1, + 0xb1, 0x32, 0x8e, 0xc0, 0x51, 0x52, 0x56, 0xe8, + 0xc0, 0x00, 0xe8, 0xa3, 0xff, 0xb8, 0x00, 0xa0, + 0x8e, 0xc0, 0x5e, 0x5a, 0x59, 0xe8, 0xb2, 0x00, + 0xe8, 0x95, 0xff, 0x07, 0xe8, 0xa0, 0x95, 0xc3, + 0x06, 0xa1, 0xb1, 0x32, 0x8e, 0xc0, 0x51, 0x52, + 0x56, 0xe8, 0x9e, 0x00, 0xe8, 0x8e, 0xff, 0xb8, + 0x00, 0xa0, 0x8e, 0xc0, 0x5e, 0x5a, 0x59, 0xe8, + 0x90, 0x00, 0xe8, 0x80, 0xff, 0x07, 0xe8, 0x7e, + 0x95, 0xc3, 0x06, 0xa1, 0xb1, 0x32, 0x8e, 0xc0, + 0x51, 0x52, 0x56, 0xe8, 0x7c, 0x00, 0xe8, 0x79, + 0xff, 0xb8, 0x00, 0xa0, 0x8e, 0xc0, 0x5e, 0x5a, + 0x59, 0xe8, 0x6e, 0x00, 0xe8, 0x6b, 0xff, 0x07, + 0xe8, 0x5c, 0x95, 0xc3, 0x06, 0xa1, 0xb1, 0x32, + 0x8e, 0xc0, 0x51, 0x52, 0x56, 0xe8, 0x5a, 0x00, + 0xe8, 0x64, 0xff, 0xb8, 0x00, 0xa0, 0x8e, 0xc0, + 0x5e, 0x5a, 0x59, 0xe8, 0x4c, 0x00, 0xe8, 0x56, + 0xff, 0x07, 0xe8, 0x3a, 0x95, 0xc3, 0x06, 0xa1, + 0xb1, 0x32, 0x8e, 0xc0, 0x51, 0x52, 0x56, 0xe8, + 0x38, 0x00, 0xe8, 0x4f, 0xff, 0xb8, 0x00, 0xa0, + 0x8e, 0xc0, 0x5e, 0x5a, 0x59, 0xe8, 0x2a, 0x00, + 0xe8, 0x41, 0xff, 0x07, 0xe8, 0x18, 0x95, 0xc3, + 0x06, 0xa1, 0xb1, 0x32, 0x8e, 0xc0, 0x51, 0x52, + 0x56, 0xe8, 0x16, 0x00, 0xe8, 0x3a, 0xff, 0xb8, + 0x00, 0xa0, 0x8e, 0xc0, 0x5e, 0x5a, 0x59, 0xe8, + 0x08, 0x00, 0xe8, 0x2c, 0xff, 0x07, 0xe8, 0xf6, + 0x94, 0xc3, 0xb0, 0xe8, 0xb4, 0xe0, 0xe8, 0x8e, + 0x00, 0xc3, 0xbe, 0x32, 0x05, 0xb0, 0xe8, 0xb4, + 0x00, 0xb9, 0xc0, 0x00, 0xba, 0xdc, 0x00, 0xe8, + 0x7d, 0x00, 0xc3, 0x53, 0x8b, 0x07, 0xe8, 0xc0, + 0xf8, 0xb9, 0x0e, 0x00, 0x8a, 0x57, 0x02, 0xb6, + 0x00, 0xb4, 0xe0, 0xe8, 0x38, 0x00, 0x5b, 0x83, + 0xc3, 0x05, 0x80, 0x3f, 0x00, 0x75, 0xe4, 0xc3, + 0x56, 0x8b, 0x34, 0xe8, 0x37, 0x93, 0x5e, 0x43, + 0x46, 0x46, 0x80, 0x3f, 0x00, 0x75, 0xf1, 0xc3, + 0xb4, 0x00, 0xb5, 0x00, 0x2b, 0xd8, 0x41, 0x03, + 0xd8, 0xe2, 0xfc, 0x8a, 0x47, 0x01, 0xf7, 0x26, + 0xb6, 0x00, 0x8a, 0x0f, 0x03, 0xc1, 0x8b, 0xf0, + 0x43, 0x43, 0xe8, 0x10, 0x93, 0xc3, 0x8b, 0xda, + 0x26, 0x88, 0x24, 0x46, 0x4a, 0x75, 0xf9, 0x81, + 0xc6, 0x40, 0x01, 0x2b, 0xf3, 0x83, 0xe9, 0x02, + 0x8b, 0xd3, 0x26, 0x88, 0x24, 0x46, 0x83, 0xea, + 0x02, 0x03, 0xf2, 0x26, 0x88, 0x24, 0x46, 0x81, + 0xc6, 0x40, 0x01, 0x2b, 0xf3, 0xe2, 0xe9, 0x26, + 0x88, 0x24, 0x46, 0x4b, 0x75, 0xf9, 0xc3, 0x8b, + 0xda, 0x26, 0x88, 0x24, 0x46, 0x4a, 0x75, 0xf9, + 0x81, 0xc6, 0x40, 0x01, 0x2b, 0xf3, 0x83, 0xe9, + 0x02, 0x8b, 0xd3, 0x26, 0x88, 0x24, 0x46, 0x83, + 0xea, 0x02, 0x26, 0x88, 0x04, 0x46, 0x4a, 0x75, + 0xf9, 0x26, 0x88, 0x24, 0x46, 0x81, 0xc6, 0x40, + 0x01, 0x2b, 0xf3, 0xe2, 0xe4, 0x26, 0x88, 0x24, + 0x46, 0x4b, 0x75, 0xf9, 0xc3, 0x8b, 0x07, 0x0b, + 0xc0, 0x74, 0x0d, 0x83, 0x7f, 0x02, 0xff, 0x74, + 0x04, 0xc7, 0x07, 0x00, 0x00, 0x8b, 0xd8, 0xc3, + 0x83, 0xc3, 0x02, 0xeb, 0xe8, 0xe8, 0xed, 0x00, + 0xbb, 0xf2, 0xdb, 0x80, 0x3f, 0x00, 0x74, 0x2f, + 0x83, 0xeb, 0x02, 0x83, 0xc3, 0x02, 0x53, 0x80, + 0x3f, 0xff, 0x75, 0x05, 0x53, 0xe8, 0x75, 0x0d, + 0x5b, 0xe8, 0x71, 0x8e, 0x5b, 0x2e, 0x80, 0x3e, + 0xd7, 0x00, 0x01, 0x74, 0x09, 0xe8, 0xad, 0x00, + 0x80, 0x7f, 0x03, 0x00, 0x75, 0x03, 0xe9, 0xa0, + 0x00, 0x80, 0x7f, 0x02, 0x00, 0x75, 0xd4, 0x53, + 0x8a, 0x26, 0x96, 0xda, 0xa0, 0x97, 0xda, 0xe8, + 0x04, 0x0d, 0xe8, 0x4f, 0x98, 0xa0, 0x96, 0xda, + 0x8b, 0x0e, 0x98, 0xda, 0x8b, 0x16, 0x9a, 0xda, + 0xe8, 0xd3, 0x00, 0x5b, 0x43, 0xe8, 0x4d, 0x0d, + 0x83, 0xc3, 0x02, 0x53, 0xa0, 0x96, 0xda, 0x8b, + 0x0e, 0x9c, 0xda, 0x8b, 0x16, 0x9e, 0xda, 0x80, + 0x3f, 0xff, 0x75, 0x03, 0xe8, 0x74, 0x00, 0xe8, + 0xb7, 0x01, 0x5b, 0x2e, 0x80, 0x3e, 0xd7, 0x00, + 0x01, 0x74, 0x3b, 0xe8, 0x57, 0x00, 0x80, 0x7f, + 0x02, 0x00, 0x75, 0xd4, 0x80, 0x7f, 0x03, 0x00, + 0x74, 0x2c, 0xe8, 0x18, 0x0d, 0x43, 0x83, 0xc3, + 0x02, 0x53, 0x80, 0x3f, 0xff, 0x75, 0x03, 0xe8, + 0x49, 0x00, 0xe8, 0x16, 0x8f, 0x5b, 0x2e, 0x80, + 0x3e, 0xd7, 0x00, 0x01, 0x74, 0x10, 0xe8, 0x2c, + 0x00, 0x80, 0x7f, 0x02, 0x00, 0x75, 0xdf, 0x43, + 0x80, 0x7f, 0x02, 0x00, 0x75, 0x9f, 0xa0, 0x96, + 0xda, 0x8b, 0x0e, 0xa0, 0xda, 0x8b, 0x16, 0xa2, + 0xda, 0xe8, 0x62, 0x00, 0xe8, 0x0f, 0x98, 0xc6, + 0x06, 0x33, 0x33, 0x01, 0xc6, 0x06, 0xdd, 0x1c, + 0x02, 0xe8, 0x50, 0x98, 0xc3, 0x4b, 0x43, 0x80, + 0x3f, 0x00, 0x75, 0xfa, 0x80, 0x7f, 0x01, 0x00, + 0x75, 0xf4, 0xc3, 0x60, 0xb8, 0x5a, 0x00, 0xe8, + 0xaa, 0x0c, 0x61, 0x43, 0xc3, 0x06, 0xb8, 0x69, + 0x1c, 0x8e, 0xc0, 0xbe, 0xf2, 0xdb, 0x33, 0xc9, + 0x26, 0x8a, 0x07, 0x8a, 0xe0, 0x26, 0x0a, 0x67, + 0x01, 0x26, 0x0a, 0x67, 0x02, 0x26, 0x0a, 0x67, + 0x03, 0x74, 0x12, 0x88, 0x04, 0x41, 0x81, 0xf9, + 0xcc, 0x07, 0x72, 0x05, 0xb0, 0x39, 0xe9, 0x87, + 0xa0, 0x43, 0x46, 0xeb, 0xdb, 0x33, 0xc0, 0x89, + 0x04, 0x89, 0x44, 0x02, 0x07, 0xc3, 0xe8, 0x0e, + 0x00, 0xc6, 0x06, 0xdc, 0x1c, 0x02, 0xe8, 0x7d, + 0x15, 0xc6, 0x06, 0xdc, 0x1c, 0x03, 0xc3, 0xbf, + 0xc7, 0x32, 0xb3, 0x1b, 0x48, 0xf6, 0xe3, 0x03, + 0xf8, 0x89, 0x0d, 0xc6, 0x06, 0x34, 0x33, 0x00, + 0x52, 0x57, 0xe8, 0x9e, 0x96, 0x5f, 0x5a, 0x52, + 0x57, 0xe8, 0x69, 0x18, 0x5f, 0x57, 0xa1, 0x7a, + 0x32, 0x0b, 0xc0, 0x74, 0x1f, 0x40, 0x3b, 0x05, + 0x75, 0x1a, 0xe8, 0x48, 0x96, 0xa0, 0x49, 0x32, + 0xb4, 0x04, 0x8a, 0x2e, 0x7c, 0x32, 0x06, 0xff, + 0x1e, 0x4a, 0x32, 0x07, 0xb8, 0x3b, 0x0b, 0x8e, + 0xd8, 0xe8, 0x6f, 0x96, 0x5f, 0x5a, 0x39, 0x15, + 0x76, 0xcd, 0xc6, 0x06, 0x34, 0x33, 0x01, 0xc7, + 0x06, 0x7a, 0x32, 0x00, 0x00, 0xc3, 0x60, 0xe8, + 0xa5, 0x92, 0x61, 0xbf, 0xc7, 0x32, 0xb3, 0x1b, + 0x48, 0xf6, 0xe3, 0x03, 0xf8, 0x89, 0x0d, 0xc6, + 0x06, 0x34, 0x33, 0x00, 0x52, 0x57, 0xe8, 0x42, + 0x96, 0x5f, 0x5a, 0x52, 0x57, 0xe8, 0x08, 0xf5, + 0xe8, 0xa4, 0x0c, 0xe8, 0x07, 0x18, 0xe8, 0x25, + 0x15, 0x5f, 0x57, 0xa1, 0x7a, 0x32, 0x0b, 0xc0, + 0x74, 0x1f, 0x40, 0x3b, 0x05, 0x75, 0x1a, 0xe8, + 0xe3, 0x95, 0xa0, 0x49, 0x32, 0xb4, 0x04, 0x8a, + 0x2e, 0x7c, 0x32, 0x06, 0xff, 0x1e, 0x4a, 0x32, + 0x07, 0xb8, 0x3b, 0x0b, 0x8e, 0xd8, 0xe8, 0x0a, + 0x96, 0x5f, 0x5a, 0x33, 0xc0, 0x80, 0x3e, 0xe1, + 0xc3, 0x00, 0x75, 0x1f, 0x8b, 0x05, 0xe8, 0x1a, + 0x93, 0xc6, 0x06, 0xcf, 0x00, 0x04, 0x72, 0x13, + 0xe8, 0xe1, 0x92, 0xc6, 0x06, 0xcf, 0x00, 0x02, + 0x72, 0x09, 0x33, 0xc0, 0xa2, 0xcf, 0x00, 0x39, + 0x15, 0x76, 0xa0, 0x50, 0xc6, 0x06, 0x34, 0x33, + 0x01, 0xc7, 0x06, 0x7a, 0x32, 0x00, 0x00, 0xc6, + 0x06, 0xdc, 0x1c, 0x02, 0xe8, 0x8f, 0x14, 0xc6, + 0x06, 0xdc, 0x1c, 0x03, 0xe8, 0xee, 0x91, 0x58, + 0xc3, 0x53, 0xbf, 0xc7, 0x32, 0xb3, 0x1b, 0x48, + 0xf6, 0xe3, 0x03, 0xf8, 0x89, 0x0d, 0x5b, 0x51, + 0x52, 0x57, 0xe8, 0xc6, 0x8d, 0x5f, 0x5a, 0x59, + 0x51, 0x52, 0x57, 0xe8, 0x77, 0x17, 0x2e, 0x80, + 0x3e, 0xd7, 0x00, 0x01, 0x74, 0x24, 0x80, 0x3e, + 0x96, 0x32, 0x00, 0x74, 0x08, 0xbb, 0x92, 0x32, + 0xe8, 0xd3, 0x94, 0x72, 0x15, 0xe8, 0xab, 0x92, + 0x72, 0x10, 0xe8, 0x77, 0x92, 0x72, 0x0b, 0x5f, + 0x5a, 0x59, 0x39, 0x15, 0x76, 0xd2, 0x89, 0x0d, + 0xeb, 0xce, 0x5f, 0x5a, 0x59, 0xa1, 0xa4, 0xda, + 0x89, 0x05, 0xc6, 0x06, 0x33, 0x33, 0x01, 0xc6, + 0x06, 0xdd, 0x1c, 0x02, 0xc6, 0x06, 0xdb, 0x1c, + 0x01, 0xc6, 0x06, 0xdc, 0x1c, 0x02, 0xe8, 0x1d, + 0x14, 0xc6, 0x06, 0x33, 0x33, 0x00, 0xc6, 0x06, + 0xdc, 0x1c, 0x03, 0xc6, 0x06, 0xdd, 0x1c, 0x03, + 0xc6, 0x06, 0xdb, 0x1c, 0x00, 0xc3, 0x50, 0x57, + 0x56, 0xe8, 0x41, 0xfe, 0x5e, 0x5f, 0x58, 0x50, + 0x33, 0xd2, 0x8a, 0xd0, 0x4a, 0xd1, 0xe2, 0xbb, + 0x47, 0x33, 0x03, 0xda, 0x89, 0x37, 0x2b, 0xda, + 0x8a, 0xd4, 0x4a, 0xd1, 0xe2, 0x03, 0xda, 0x89, + 0x3f, 0xe8, 0xc8, 0x95, 0xc6, 0x06, 0x33, 0x33, + 0x01, 0xc6, 0x06, 0xdd, 0x1c, 0x02, 0xe8, 0xd5, + 0x13, 0xc6, 0x06, 0x33, 0x33, 0x00, 0xc6, 0x06, + 0xdd, 0x1c, 0x03, 0x58, 0x50, 0x98, 0xbf, 0xc7, + 0x32, 0xb3, 0x1b, 0x48, 0xf6, 0xe3, 0x03, 0xf8, + 0x89, 0x3e, 0x41, 0x33, 0xc7, 0x05, 0x01, 0x00, + 0x58, 0x8a, 0xc4, 0x98, 0xbf, 0xc7, 0x32, 0xb3, + 0x1b, 0x48, 0xf6, 0xe3, 0x03, 0xf8, 0x89, 0x3e, + 0x43, 0x33, 0xc7, 0x05, 0x01, 0x00, 0xbb, 0xf2, + 0xdb, 0x83, 0xeb, 0x02, 0x83, 0xc3, 0x02, 0x53, + 0xa0, 0x45, 0x33, 0xa2, 0xe7, 0x1c, 0xe8, 0x6e, + 0x06, 0x80, 0x3f, 0xff, 0x75, 0x03, 0xe8, 0xc5, + 0x00, 0x8b, 0x3e, 0x41, 0x33, 0xe8, 0xd3, 0x8c, + 0x5b, 0x53, 0xe8, 0x88, 0x16, 0x8b, 0x3e, 0x43, + 0x33, 0xc7, 0x05, 0x01, 0x00, 0x2e, 0x80, 0x3e, + 0xd7, 0x00, 0x01, 0x74, 0x19, 0x80, 0x3e, 0x96, + 0x32, 0x00, 0x74, 0x08, 0xbb, 0x92, 0x32, 0xe8, + 0xdc, 0x93, 0x72, 0x0a, 0xe8, 0xb4, 0x91, 0x72, + 0x05, 0xe8, 0x80, 0x91, 0x73, 0xd4, 0x5b, 0x2e, + 0x80, 0x3e, 0xd7, 0x00, 0x01, 0x74, 0x76, 0xe8, + 0x6b, 0xfd, 0x80, 0x7f, 0x02, 0x00, 0x75, 0xa4, + 0x80, 0x7f, 0x03, 0x00, 0x74, 0x67, 0x43, 0x83, + 0xc3, 0x02, 0x53, 0xa0, 0x46, 0x33, 0xa2, 0xe7, + 0x1c, 0xe8, 0x0b, 0x06, 0x80, 0x3f, 0xff, 0x75, + 0x03, 0xe8, 0x62, 0x00, 0x8b, 0x3e, 0x43, 0x33, + 0xe8, 0x70, 0x8c, 0x5b, 0x53, 0xe8, 0x25, 0x16, + 0x8b, 0x3e, 0x41, 0x33, 0xc7, 0x05, 0x01, 0x00, + 0x2e, 0x80, 0x3e, 0xd7, 0x00, 0x01, 0x74, 0x19, + 0x80, 0x3e, 0x96, 0x32, 0x00, 0x74, 0x08, 0xbb, + 0x92, 0x32, 0xe8, 0x79, 0x93, 0x72, 0x0a, 0xe8, + 0x51, 0x91, 0x72, 0x05, 0xe8, 0x1d, 0x91, 0x73, + 0xd4, 0x5b, 0x2e, 0x80, 0x3e, 0xd7, 0x00, 0x01, + 0x74, 0x13, 0xe8, 0x08, 0xfd, 0x80, 0x7f, 0x02, + 0x00, 0x75, 0xa4, 0x43, 0x80, 0x7f, 0x02, 0x00, + 0x74, 0x03, 0xe9, 0x37, 0xff, 0xe8, 0xaf, 0x05, + 0xe8, 0xf3, 0x94, 0xc6, 0x06, 0x33, 0x33, 0x01, + 0xc6, 0x06, 0xdd, 0x1c, 0x02, 0xc3, 0x60, 0x8b, + 0x3e, 0x41, 0x33, 0xc7, 0x05, 0x01, 0x00, 0x8b, + 0x3e, 0x43, 0x33, 0xc7, 0x05, 0x01, 0x00, 0xc6, + 0x06, 0x33, 0x33, 0x01, 0xc6, 0x06, 0xdd, 0x1c, + 0x02, 0xe8, 0xa2, 0x12, 0xc6, 0x06, 0x33, 0x33, + 0x00, 0xc6, 0x06, 0xdd, 0x1c, 0x03, 0xb8, 0x5a, + 0x00, 0xe8, 0x78, 0x09, 0x8b, 0x3e, 0x41, 0x33, + 0xc7, 0x05, 0x01, 0x00, 0x8b, 0x3e, 0x43, 0x33, + 0xc7, 0x05, 0x01, 0x00, 0x61, 0x43, 0xc3, 0x98, + 0x50, 0x56, 0xe8, 0xb8, 0xfc, 0x5e, 0x58, 0x50, + 0x48, 0xd1, 0xe0, 0xbb, 0x47, 0x33, 0x03, 0xd8, + 0x89, 0x37, 0xe8, 0x4f, 0x94, 0xc6, 0x06, 0x33, + 0x33, 0x01, 0xc6, 0x06, 0xdd, 0x1c, 0x02, 0xe8, + 0x5c, 0x12, 0xc6, 0x06, 0x33, 0x33, 0x00, 0xc6, + 0x06, 0xdd, 0x1c, 0x03, 0x58, 0xbf, 0xc7, 0x32, + 0xb3, 0x1b, 0x48, 0xf6, 0xe3, 0x03, 0xf8, 0x89, + 0x3e, 0x41, 0x33, 0xc7, 0x05, 0x01, 0x00, 0xbb, + 0xf0, 0xdb, 0x83, 0xc3, 0x02, 0x53, 0xa0, 0x45, + 0x33, 0xa2, 0xe7, 0x1c, 0xe8, 0x10, 0x05, 0x80, + 0x3f, 0xff, 0x75, 0x03, 0xe8, 0x53, 0x00, 0x8b, + 0x3e, 0x41, 0x33, 0xe8, 0x75, 0x8b, 0x5b, 0x53, + 0xe8, 0x2a, 0x15, 0x2e, 0x80, 0x3e, 0xd7, 0x00, + 0x01, 0x74, 0x19, 0x80, 0x3e, 0x96, 0x32, 0x00, + 0x74, 0x08, 0xbb, 0x92, 0x32, 0xe8, 0x86, 0x92, + 0x72, 0x0a, 0xe8, 0x5e, 0x90, 0x72, 0x05, 0xe8, + 0x2a, 0x90, 0x73, 0xdc, 0x5b, 0x2e, 0x80, 0x3e, + 0xd7, 0x00, 0x01, 0x74, 0x09, 0xe8, 0x15, 0xfc, + 0x80, 0x7f, 0x02, 0x00, 0x75, 0xac, 0xe8, 0x26, + 0x00, 0xe8, 0xc3, 0x04, 0xe8, 0x07, 0x94, 0xc6, + 0x06, 0x33, 0x33, 0x01, 0xc6, 0x06, 0xdd, 0x1c, + 0x02, 0xc3, 0x60, 0xe8, 0x11, 0x00, 0xb8, 0x5a, + 0x00, 0xe8, 0xb0, 0x08, 0x8b, 0x3e, 0x41, 0x33, + 0xc7, 0x05, 0x01, 0x00, 0x61, 0x43, 0xc3, 0x8b, + 0x3e, 0x41, 0x33, 0xc7, 0x05, 0x01, 0x00, 0xc6, + 0x06, 0x33, 0x33, 0x01, 0xc6, 0x06, 0xdd, 0x1c, + 0x02, 0xe8, 0xaa, 0x11, 0xc6, 0x06, 0x33, 0x33, + 0x00, 0xc6, 0x06, 0xdd, 0x1c, 0x03, 0xc3, 0xb0, + 0xe7, 0xb4, 0x00, 0xbf, 0x01, 0x00, 0xb3, 0x01, + 0xb7, 0x01, 0xb1, 0x01, 0xb5, 0x15, 0xb2, 0x01, + 0xb6, 0x01, 0xe8, 0x9b, 0x02, 0xbb, 0x47, 0x33, + 0xc7, 0x07, 0x98, 0x03, 0xc6, 0x06, 0x96, 0xda, + 0x01, 0xc3, 0xe8, 0xda, 0xff, 0xbb, 0x47, 0x33, + 0xc7, 0x07, 0x9a, 0x03, 0xc3, 0xe8, 0xcf, 0xff, + 0xbb, 0x47, 0x33, 0xc7, 0x07, 0x99, 0x03, 0xc3, + 0xb0, 0xef, 0xb4, 0x00, 0xbf, 0x01, 0x00, 0xb3, + 0x01, 0xb7, 0x01, 0xb1, 0x01, 0xb5, 0x14, 0xb2, + 0x01, 0xb6, 0x01, 0xe8, 0x62, 0x02, 0xbb, 0x47, + 0x33, 0xc7, 0x07, 0x59, 0x03, 0xc6, 0x06, 0x96, + 0xda, 0x01, 0xc3, 0xb0, 0xec, 0xb4, 0x01, 0xbf, + 0x01, 0x00, 0xb3, 0x01, 0xb7, 0x01, 0xb1, 0x01, + 0xb5, 0x14, 0xb2, 0x01, 0xb6, 0x01, 0xe8, 0x3f, + 0x02, 0xbb, 0x47, 0x33, 0xc7, 0x07, 0x2d, 0x03, + 0xc6, 0x06, 0x96, 0xda, 0x01, 0xc3, 0xb0, 0xec, + 0xb4, 0x00, 0xbf, 0x01, 0x00, 0xb3, 0x01, 0xb7, + 0x01, 0xb1, 0x01, 0xb5, 0x14, 0xb2, 0x01, 0xb6, + 0x01, 0xe8, 0x1c, 0x02, 0xbb, 0x47, 0x33, 0xc7, + 0x07, 0x2e, 0x03, 0xc6, 0x06, 0x96, 0xda, 0x01, + 0xc3, 0xe8, 0xda, 0xff, 0xbb, 0x47, 0x33, 0xc7, + 0x07, 0x40, 0x03, 0xc3, 0xe8, 0xcf, 0xff, 0xbb, + 0x47, 0x33, 0xc7, 0x07, 0x58, 0x03, 0xc3, 0xe8, + 0xc4, 0xff, 0xbb, 0x47, 0x33, 0xc7, 0x07, 0xaa, + 0x03, 0xc3, 0xb0, 0xd0, 0xb4, 0x02, 0xbf, 0x01, + 0x00, 0xb3, 0x01, 0xb7, 0x01, 0xb1, 0x01, 0xb5, + 0x22, 0xb2, 0x01, 0xb6, 0x01, 0xe8, 0xd8, 0x01, + 0xbb, 0x47, 0x33, 0xc7, 0x07, 0x29, 0x03, 0xc6, + 0x06, 0x96, 0xda, 0x01, 0xc3, 0xb0, 0xe5, 0xb4, + 0x01, 0xb3, 0x01, 0xb7, 0x01, 0xb1, 0x02, 0xb5, + 0x13, 0xb2, 0x14, 0xb6, 0x14, 0xbf, 0x01, 0x00, + 0xe8, 0xb5, 0x01, 0xbb, 0x47, 0x33, 0xc7, 0x07, + 0x0b, 0x02, 0xc6, 0x06, 0x96, 0xda, 0x01, 0xc3, + 0xb0, 0xd8, 0xb4, 0x01, 0xb3, 0x01, 0xb7, 0x01, + 0xb1, 0x01, 0xb5, 0x18, 0xb2, 0x18, 0xb6, 0x18, + 0xbf, 0x01, 0x00, 0xe8, 0x92, 0x01, 0xbb, 0x47, + 0x33, 0xc7, 0x07, 0x0a, 0x02, 0xc6, 0x06, 0x96, + 0xda, 0x01, 0xc3, 0xb0, 0xe5, 0xb4, 0x07, 0xb3, + 0x01, 0xb7, 0x02, 0xb1, 0x03, 0xb5, 0x20, 0xb2, + 0x21, 0xb6, 0x22, 0xbf, 0x03, 0x00, 0xe8, 0x6f, + 0x01, 0xbb, 0x47, 0x33, 0xc7, 0x07, 0xf6, 0x01, + 0xc6, 0x06, 0x96, 0xda, 0x01, 0xc3, 0xb0, 0xd9, + 0xb4, 0x01, 0xb3, 0x01, 0xb7, 0x01, 0xb1, 0x01, + 0xb5, 0x14, 0xb2, 0x15, 0xb6, 0x15, 0xbf, 0x01, + 0x00, 0xe8, 0x4c, 0x01, 0xbb, 0x47, 0x33, 0xc7, + 0x07, 0x11, 0x02, 0xc6, 0x06, 0x96, 0xda, 0x01, + 0xc3, 0xb0, 0xd9, 0xb4, 0x00, 0xb3, 0x01, 0xb7, + 0x01, 0xb1, 0x01, 0xb5, 0x09, 0xb2, 0x09, 0xb6, + 0x09, 0xbf, 0x01, 0x00, 0xe8, 0x29, 0x01, 0xbb, + 0x47, 0x33, 0xc7, 0x07, 0x22, 0x02, 0xc6, 0x06, + 0x96, 0xda, 0x01, 0xc3, 0xb0, 0xe5, 0xb4, 0x01, + 0xbf, 0x01, 0x00, 0xb3, 0x01, 0xb7, 0x01, 0xb1, + 0x01, 0xb5, 0x14, 0xb2, 0x15, 0xb6, 0x15, 0xe8, + 0x06, 0x01, 0xbb, 0x47, 0x33, 0xc7, 0x47, 0x02, + 0x0c, 0x02, 0xc6, 0x06, 0x96, 0xda, 0x02, 0xc3, + 0xb0, 0xe5, 0xb4, 0x04, 0xbf, 0x01, 0x00, 0xb3, + 0x01, 0xb7, 0x01, 0xb1, 0x01, 0xb5, 0x1f, 0xb2, + 0x01, 0xb6, 0x01, 0xe8, 0xe2, 0x00, 0xbb, 0x47, + 0x33, 0xc7, 0x47, 0x02, 0xa0, 0x02, 0xc6, 0x06, + 0x96, 0xda, 0x02, 0xc3, 0xb0, 0xd0, 0xb4, 0x01, + 0xbf, 0x03, 0x00, 0xb3, 0x03, 0xb7, 0x03, 0xb1, + 0x01, 0xb5, 0x0d, 0xb2, 0x03, 0xb6, 0x03, 0xe8, + 0xbe, 0x00, 0xbb, 0x47, 0x33, 0xc7, 0x47, 0x02, + 0x9a, 0x02, 0xc6, 0x06, 0x96, 0xda, 0x02, 0xc3, + 0xb0, 0xd0, 0xb4, 0x06, 0xbf, 0x01, 0x00, 0xb3, + 0x01, 0xb7, 0x01, 0xb1, 0x01, 0xb5, 0x12, 0xb2, + 0x01, 0xb6, 0x01, 0xe8, 0x9a, 0x00, 0xbb, 0x47, + 0x33, 0xc7, 0x07, 0x1d, 0x03, 0xc6, 0x06, 0x96, + 0xda, 0x01, 0xc3, 0xb0, 0xd0, 0xb4, 0x01, 0xbf, + 0x01, 0x00, 0xb3, 0x01, 0xb7, 0x01, 0xb1, 0x01, + 0xb5, 0x14, 0xb2, 0x01, 0xb6, 0x01, 0xe8, 0x77, + 0x00, 0xbb, 0x47, 0x33, 0xc7, 0x07, 0xbe, 0x02, + 0xc6, 0x06, 0x96, 0xda, 0x01, 0xc3, 0xb0, 0xef, + 0xb4, 0x01, 0xbf, 0x01, 0x00, 0xb3, 0x01, 0xb7, + 0x01, 0xb1, 0x01, 0xb5, 0x14, 0xb2, 0x01, 0xb6, + 0x01, 0xe8, 0x54, 0x00, 0xbb, 0x47, 0x33, 0xc7, + 0x07, 0xc5, 0x02, 0xc6, 0x06, 0x96, 0xda, 0x01, + 0xc3, 0xb0, 0xef, 0xb4, 0x01, 0xbf, 0x01, 0x00, + 0xb3, 0x01, 0xb7, 0x01, 0xb1, 0x01, 0xb5, 0x03, + 0xb2, 0x01, 0xb6, 0x01, 0xe8, 0x31, 0x00, 0xbb, + 0x47, 0x33, 0xc7, 0x07, 0xd7, 0x02, 0xc6, 0x06, + 0x96, 0xda, 0x01, 0xc3, 0xb0, 0xd0, 0xb4, 0x00, + 0xb3, 0x01, 0xb7, 0x01, 0xb1, 0x01, 0xb5, 0x18, + 0xb2, 0x01, 0xb6, 0x01, 0xbf, 0x01, 0x00, 0xe8, + 0x0e, 0x00, 0xbb, 0x47, 0x33, 0xc7, 0x47, 0x06, + 0x17, 0x03, 0xc6, 0x06, 0x96, 0xda, 0x04, 0xc3, + 0xa2, 0xe7, 0x1c, 0x88, 0x26, 0x97, 0xda, 0x89, + 0x3e, 0xa4, 0xda, 0x88, 0x1e, 0x98, 0xda, 0x88, + 0x3e, 0x9a, 0xda, 0x88, 0x0e, 0x9c, 0xda, 0x88, + 0x2e, 0x9e, 0xda, 0x88, 0x16, 0xa0, 0xda, 0x88, + 0x36, 0xa2, 0xda, 0xc3, 0x26, 0x8b, 0x0f, 0x26, + 0x8b, 0x57, 0x02, 0x26, 0x8b, 0x77, 0x04, 0x8b, + 0xf9, 0x83, 0xc3, 0x06, 0x1e, 0x8e, 0xd8, 0x8b, + 0xcf, 0x26, 0x8a, 0x07, 0x3c, 0xff, 0x74, 0x02, + 0x88, 0x04, 0x43, 0x46, 0xe2, 0xf3, 0x81, 0xc6, + 0x40, 0x01, 0x2b, 0xf7, 0x4a, 0x75, 0xe8, 0x1f, + 0xc3, 0x50, 0x32, 0xe4, 0xe8, 0x66, 0x8a, 0x58, + 0xc6, 0x47, 0x12, 0x01, 0xc3, 0x50, 0x32, 0xe4, + 0xe8, 0x5a, 0x8a, 0x58, 0xc6, 0x47, 0x12, 0x00, + 0xc3, 0xb4, 0x00, 0xe8, 0x4f, 0x8a, 0x83, 0xc3, + 0x13, 0xfc, 0xac, 0x3c, 0xff, 0x74, 0x05, 0x88, + 0x07, 0x43, 0xeb, 0xf6, 0xc3, 0x50, 0x51, 0x52, + 0xb4, 0x00, 0xe8, 0x38, 0x8a, 0x5a, 0x59, 0x58, + 0x89, 0x4f, 0x09, 0x89, 0x57, 0x0b, 0x89, 0x4f, + 0x0d, 0x89, 0x57, 0x0f, 0x88, 0x67, 0x11, 0xc3, + 0xe8, 0x04, 0x00, 0xe8, 0xc8, 0xf8, 0xc3, 0x32, + 0xe4, 0x50, 0xe8, 0x2f, 0x90, 0x58, 0x50, 0xbe, + 0xc7, 0x32, 0xbb, 0x47, 0x33, 0x48, 0xb1, 0x1b, + 0xf6, 0xe1, 0x03, 0xf0, 0x06, 0x8b, 0x44, 0x16, + 0x8e, 0xc0, 0xba, 0x02, 0x00, 0xbe, 0x00, 0x00, + 0x26, 0x8b, 0x34, 0x2b, 0xf2, 0x8b, 0xc6, 0xb1, + 0x03, 0xf6, 0xf1, 0x07, 0x8b, 0xd0, 0xb9, 0x01, + 0x00, 0x51, 0x52, 0xe8, 0x75, 0x8f, 0x5a, 0x59, + 0x58, 0xc3, 0xe8, 0xc2, 0xff, 0xe8, 0x97, 0xf8, + 0xc3, 0xe8, 0xeb, 0x00, 0xe8, 0xb1, 0xff, 0xe8, + 0x2c, 0x90, 0xc6, 0x06, 0x33, 0x33, 0x01, 0xc6, + 0x06, 0xdd, 0x1c, 0x02, 0xc3, 0xc6, 0x06, 0x27, + 0xc4, 0x00, 0xe8, 0xd2, 0x00, 0xe8, 0x9f, 0xff, + 0xe8, 0xcb, 0xf8, 0xa3, 0x3f, 0x33, 0x0b, 0xc0, + 0x74, 0x4d, 0xa1, 0x52, 0x72, 0xa3, 0x50, 0x72, + 0xa1, 0x50, 0x72, 0x3b, 0x06, 0x23, 0xc4, 0x75, + 0x3e, 0xa1, 0x1f, 0xc4, 0xe8, 0xbd, 0x0d, 0x3b, + 0x06, 0x25, 0xc4, 0x75, 0x32, 0xa0, 0xce, 0x00, + 0x0a, 0xc0, 0x74, 0x06, 0x3a, 0x06, 0xcf, 0x00, + 0x75, 0x25, 0xc6, 0x06, 0x27, 0xc4, 0x01, 0xa1, + 0x3f, 0x33, 0x3b, 0x06, 0x3b, 0x33, 0x72, 0x17, + 0x3b, 0x06, 0x3d, 0x33, 0x77, 0x11, 0xe8, 0x0e, + 0x00, 0xa3, 0x52, 0x72, 0xa3, 0x50, 0x72, 0xa3, + 0x1f, 0xc4, 0xa2, 0xcf, 0x00, 0xf9, 0xc3, 0x33, + 0xc0, 0xa3, 0x23, 0xc4, 0xa3, 0x25, 0xc4, 0xa3, + 0x3b, 0x33, 0xa3, 0x3d, 0x33, 0xf8, 0xc3, 0xe8, + 0x65, 0x00, 0xe8, 0x50, 0x8f, 0xe8, 0x64, 0x8f, + 0xe8, 0xd8, 0x8e, 0xe8, 0x38, 0x01, 0xc3, 0xe8, + 0x55, 0x00, 0xc6, 0x06, 0xdc, 0x64, 0x00, 0xe8, + 0xe8, 0xff, 0xc6, 0x06, 0xdc, 0x64, 0x01, 0xc3, + 0xe8, 0xdc, 0xff, 0xe8, 0x16, 0x00, 0xc3, 0xe8, + 0xd8, 0xff, 0xe8, 0x0f, 0x00, 0xc3, 0xe8, 0xde, + 0xff, 0xe8, 0x08, 0x00, 0xc3, 0xe8, 0xda, 0xff, + 0xe8, 0x01, 0x00, 0xc3, 0xe8, 0x64, 0x90, 0xe8, + 0x6c, 0x8f, 0xe8, 0x44, 0x23, 0xc6, 0x06, 0x33, + 0x33, 0x01, 0xc6, 0x06, 0xdd, 0x1c, 0x02, 0xc6, + 0x06, 0xdc, 0x1c, 0x02, 0xe8, 0x2f, 0x0d, 0xc6, + 0x06, 0x33, 0x33, 0x00, 0xc6, 0x06, 0xdd, 0x1c, + 0x03, 0xc6, 0x06, 0xdc, 0x1c, 0x03, 0xc3, 0xc6, + 0x06, 0xda, 0x1c, 0x01, 0xc6, 0x06, 0xdb, 0x1c, + 0x01, 0xe8, 0x12, 0x0d, 0xc6, 0x06, 0xda, 0x1c, + 0x00, 0xc6, 0x06, 0xdb, 0x1c, 0x00, 0xc3, 0xe8, + 0xd3, 0x8e, 0xe8, 0xe7, 0x8e, 0xe8, 0x5b, 0x8e, + 0xe8, 0x47, 0x24, 0xc6, 0x06, 0x07, 0x66, 0x01, + 0xa1, 0x7a, 0x32, 0x3a, 0x06, 0x07, 0x66, 0x75, + 0x03, 0xe8, 0xe9, 0x8d, 0xbb, 0xa4, 0x32, 0xe8, + 0x64, 0x8d, 0x73, 0xf8, 0x8b, 0x0e, 0x90, 0x32, + 0xe8, 0x48, 0x8d, 0xc6, 0x06, 0x33, 0x33, 0x01, + 0xc6, 0x06, 0xdd, 0x1c, 0x02, 0xc6, 0x06, 0xdc, + 0x1c, 0x02, 0xe8, 0x1f, 0x13, 0xe8, 0xfc, 0x0f, + 0xe8, 0x13, 0x0f, 0xe8, 0x4d, 0x13, 0x72, 0x77, + 0xe8, 0x40, 0x10, 0xe8, 0xa9, 0x11, 0xa0, 0x07, + 0x66, 0xfe, 0xc8, 0x3a, 0x06, 0x35, 0x33, 0x75, + 0x12, 0x8b, 0x1e, 0x37, 0x33, 0x8b, 0x36, 0x39, + 0x33, 0xe8, 0xfb, 0x86, 0xc6, 0x06, 0xdb, 0x1c, + 0x02, 0xeb, 0x0b, 0x3a, 0x06, 0x36, 0x33, 0x75, + 0x05, 0xc6, 0x06, 0xdb, 0x1c, 0x01, 0xe8, 0xf5, + 0x0e, 0xa1, 0x7a, 0x32, 0x0b, 0xc0, 0x74, 0x1e, + 0x40, 0x3a, 0x06, 0x07, 0x66, 0x75, 0x17, 0xa0, + 0x49, 0x32, 0xb4, 0x04, 0x8a, 0x2e, 0x7c, 0x32, + 0xff, 0x1e, 0x4a, 0x32, 0xb8, 0x3b, 0x0b, 0x8e, + 0xd8, 0x8e, 0xc0, 0xe8, 0xa5, 0x8d, 0xe8, 0xff, + 0x8a, 0xe8, 0x45, 0x18, 0xe8, 0xf8, 0x11, 0xe8, + 0x10, 0x0f, 0xe8, 0xba, 0x89, 0xc6, 0x06, 0x33, + 0x33, 0x00, 0xc6, 0x06, 0xdd, 0x1c, 0x03, 0xc6, + 0x06, 0xdc, 0x1c, 0x03, 0xe9, 0x51, 0xff, 0xc7, + 0x06, 0x7a, 0x32, 0x00, 0x00, 0xc3, 0xe8, 0x7d, + 0x90, 0xe8, 0x86, 0x23, 0xc6, 0x06, 0x07, 0x66, + 0x01, 0xa1, 0x7a, 0x32, 0x3a, 0x06, 0x07, 0x66, + 0x75, 0x03, 0xe8, 0x28, 0x8d, 0xbb, 0xa4, 0x32, + 0xe8, 0xa3, 0x8c, 0x73, 0xf8, 0x8b, 0x0e, 0x90, + 0x32, 0xe8, 0x87, 0x8c, 0xc6, 0x06, 0x33, 0x33, + 0x01, 0xc6, 0x06, 0xdd, 0x1c, 0x02, 0xc6, 0x06, + 0xdc, 0x1c, 0x02, 0xe8, 0x5e, 0x12, 0xe8, 0x3b, + 0x0f, 0xe8, 0x8f, 0x12, 0x72, 0x48, 0xe8, 0x82, + 0x0f, 0xe8, 0xeb, 0x10, 0xa1, 0x7a, 0x32, 0x0b, + 0xc0, 0x74, 0x1e, 0x40, 0x3a, 0x06, 0x07, 0x66, + 0x75, 0x17, 0xa0, 0x49, 0x32, 0xb4, 0x04, 0x8a, + 0x2e, 0x7c, 0x32, 0xff, 0x1e, 0x4a, 0x32, 0xb8, + 0x3b, 0x0b, 0x8e, 0xd8, 0x8e, 0xc0, 0xe8, 0x12, + 0x8d, 0xe8, 0x6c, 0x8a, 0xe8, 0xb2, 0x17, 0xe8, + 0x65, 0x11, 0xe8, 0x2a, 0x89, 0xc6, 0x06, 0x33, + 0x33, 0x00, 0xc6, 0x06, 0xdd, 0x1c, 0x03, 0xc6, + 0x06, 0xdc, 0x1c, 0x03, 0xeb, 0x83, 0xc7, 0x06, + 0x7a, 0x32, 0x00, 0x00, 0xc3, 0xc6, 0x06, 0x27, + 0xc4, 0x00, 0xe8, 0x32, 0x89, 0xe8, 0xf2, 0x22, + 0xc6, 0x06, 0x07, 0x66, 0x01, 0xa1, 0x7a, 0x32, + 0x3a, 0x06, 0x07, 0x66, 0x75, 0x03, 0xe8, 0x94, + 0x8c, 0xc6, 0x06, 0xdd, 0x1c, 0x03, 0xc6, 0x06, + 0xdc, 0x1c, 0x03, 0xc6, 0x06, 0xda, 0x1c, 0x01, + 0xe8, 0xe0, 0x0b, 0x3d, 0x01, 0x00, 0x76, 0x05, + 0xc6, 0x06, 0xda, 0x1c, 0x02, 0xe8, 0xcc, 0x11, + 0xe8, 0xa9, 0x0e, 0xe8, 0xff, 0x0c, 0xe8, 0x5e, + 0x10, 0xe8, 0x11, 0x0d, 0xe8, 0x01, 0x8a, 0xe8, + 0x47, 0x17, 0xe8, 0xfa, 0x10, 0xe8, 0x48, 0x0d, + 0xe8, 0xbc, 0x88, 0xa0, 0x07, 0x66, 0x32, 0xe4, + 0xe8, 0xb0, 0x89, 0xc6, 0x06, 0xcf, 0x00, 0x04, + 0x73, 0x03, 0xe9, 0x8f, 0x00, 0xe8, 0x74, 0x89, + 0xc6, 0x06, 0xcf, 0x00, 0x02, 0x73, 0x03, 0xe9, + 0x82, 0x00, 0x33, 0xc0, 0xa2, 0xcf, 0x00, 0xbb, + 0xa4, 0x32, 0xe8, 0xb1, 0x8b, 0x73, 0x9a, 0x50, + 0x8b, 0x0e, 0x90, 0x32, 0xe8, 0x94, 0x8b, 0xc6, + 0x06, 0x33, 0x33, 0x01, 0xc6, 0x06, 0xdd, 0x1c, + 0x02, 0xc6, 0x06, 0xdc, 0x1c, 0x02, 0xe8, 0x6b, + 0x11, 0xe8, 0x48, 0x0e, 0xe8, 0x9e, 0x0c, 0xe8, + 0x99, 0x11, 0x58, 0x72, 0x4f, 0xe8, 0x8b, 0x0e, + 0xe8, 0xf4, 0x0f, 0xe8, 0xa7, 0x0c, 0xa1, 0x7a, + 0x32, 0x0b, 0xc0, 0x74, 0x1e, 0x40, 0x3a, 0x06, + 0x07, 0x66, 0x75, 0x17, 0xa0, 0x49, 0x32, 0xb4, + 0x04, 0x8a, 0x2e, 0x7c, 0x32, 0xff, 0x1e, 0x4a, + 0x32, 0xb8, 0x3b, 0x0b, 0x8e, 0xd8, 0x8e, 0xc0, + 0xe8, 0x18, 0x8c, 0xe8, 0x72, 0x89, 0xe8, 0xb8, + 0x16, 0xe8, 0x6b, 0x10, 0xe8, 0xb9, 0x0c, 0xe8, + 0x2d, 0x88, 0xc6, 0x06, 0x33, 0x33, 0x00, 0xc6, + 0x06, 0xdd, 0x1c, 0x03, 0xc6, 0x06, 0xdc, 0x1c, + 0x03, 0xe9, 0x19, 0xff, 0x50, 0xc6, 0x06, 0xda, + 0x1c, 0x01, 0xe8, 0x40, 0x0c, 0xe8, 0x55, 0x0c, + 0xe8, 0x45, 0x89, 0xe8, 0x92, 0x0c, 0xe8, 0x06, + 0x88, 0xe8, 0x09, 0x88, 0x58, 0x8b, 0x0e, 0x52, + 0x72, 0x8b, 0x16, 0x3d, 0x33, 0x51, 0x52, 0xe8, + 0xa9, 0xfc, 0x72, 0x07, 0xe8, 0x5a, 0xfe, 0x5a, + 0x59, 0xf8, 0xc3, 0x89, 0x16, 0x3d, 0x33, 0xa1, + 0x7a, 0x32, 0x3a, 0x06, 0x07, 0x66, 0x75, 0x03, + 0xe8, 0x7a, 0x8b, 0xbb, 0xa4, 0x32, 0xe8, 0xf5, + 0x8a, 0x73, 0xf8, 0x8b, 0x0e, 0x90, 0x32, 0xe8, + 0xd9, 0x8a, 0xc6, 0x06, 0x33, 0x33, 0x01, 0xc6, + 0x06, 0xdd, 0x1c, 0x02, 0xc6, 0x06, 0xdc, 0x1c, + 0x02, 0xe8, 0xb0, 0x10, 0xe8, 0x8d, 0x0d, 0xe8, + 0xe1, 0x10, 0x72, 0x54, 0xa0, 0x07, 0x66, 0x32, + 0xe4, 0x3b, 0x06, 0x3d, 0x33, 0x73, 0x49, 0xe8, + 0xc9, 0x0d, 0xe8, 0x32, 0x0f, 0xa1, 0x7a, 0x32, + 0x0b, 0xc0, 0x74, 0x1e, 0x40, 0x3a, 0x06, 0x07, + 0x66, 0x75, 0x17, 0xa0, 0x49, 0x32, 0xb4, 0x04, + 0x8a, 0x2e, 0x7c, 0x32, 0xff, 0x1e, 0x4a, 0x32, + 0xb8, 0x3b, 0x0b, 0x8e, 0xd8, 0x8e, 0xc0, 0xe8, + 0x59, 0x8b, 0xe8, 0xb3, 0x88, 0xe8, 0xf9, 0x15, + 0xe8, 0xac, 0x0f, 0xe8, 0x71, 0x87, 0xc6, 0x06, + 0x33, 0x33, 0x00, 0xc6, 0x06, 0xdd, 0x1c, 0x03, + 0xc6, 0x06, 0xdc, 0x1c, 0x03, 0xe9, 0x77, 0xff, + 0x33, 0xc0, 0xa3, 0x7a, 0x32, 0xa3, 0x3d, 0x33, + 0x5a, 0x59, 0xf9, 0xc3, 0xb9, 0xe8, 0x03, 0xbb, + 0xac, 0x32, 0xe8, 0x56, 0x8a, 0xc7, 0x06, 0x52, + 0x72, 0x00, 0x00, 0xc7, 0x06, 0x50, 0x72, 0x00, + 0x00, 0xc7, 0x06, 0x1f, 0xc4, 0x00, 0x00, 0xe8, + 0x5d, 0x87, 0xe8, 0x7a, 0x01, 0x73, 0x09, 0xb9, + 0xe8, 0x03, 0xbb, 0xac, 0x32, 0xe8, 0x33, 0x8a, + 0xe8, 0xd2, 0x0c, 0xe8, 0xf0, 0x09, 0x33, 0xc0, + 0xa2, 0xcf, 0x00, 0xbb, 0xac, 0x32, 0xe8, 0x35, + 0x8a, 0x72, 0x1c, 0xe8, 0x0d, 0x88, 0xc6, 0x06, + 0xcf, 0x00, 0x04, 0x72, 0x0a, 0xe8, 0xd4, 0x87, + 0xc6, 0x06, 0xcf, 0x00, 0x02, 0x73, 0xcb, 0xe8, + 0x03, 0x87, 0xe8, 0x7a, 0xfc, 0xf9, 0xc3, 0xe8, + 0xfb, 0x86, 0xe8, 0x72, 0xfc, 0xf8, 0xc3, 0x0b, + 0xc0, 0x75, 0x01, 0xc3, 0x33, 0xdb, 0x8a, 0xd8, + 0x43, 0xbf, 0xc7, 0x32, 0xb0, 0x1b, 0xfe, 0xcc, + 0xf6, 0xe4, 0x03, 0xf8, 0x53, 0x57, 0x8b, 0x05, + 0x3b, 0xc3, 0x74, 0x07, 0xe8, 0x7e, 0x0c, 0x5f, + 0x5b, 0xeb, 0xf1, 0x5f, 0x5b, 0xc3, 0x0b, 0xc0, + 0x75, 0x01, 0xc3, 0x0a, 0xc0, 0x75, 0x01, 0xc3, + 0x33, 0xdb, 0x8a, 0xd8, 0xbf, 0xc7, 0x32, 0xb0, + 0x1b, 0xfe, 0xcc, 0xf6, 0xe4, 0x03, 0xf8, 0x53, + 0x57, 0x8a, 0x45, 0x1a, 0x3a, 0xc3, 0x74, 0x07, + 0xe8, 0x52, 0x0c, 0x5f, 0x5b, 0xeb, 0xf0, 0x5f, + 0x5b, 0xc3, 0xbb, 0xa8, 0x32, 0x8b, 0xc8, 0xe8, + 0xa1, 0x89, 0xe8, 0x40, 0x0c, 0xbb, 0xa8, 0x32, + 0xe8, 0xab, 0x89, 0x73, 0xf5, 0xc3, 0xb8, 0x32, + 0x00, 0xe8, 0xe6, 0xff, 0xc3, 0xb8, 0x64, 0x00, + 0xe8, 0xdf, 0xff, 0xc3, 0xbb, 0xa8, 0x32, 0x8b, + 0xc8, 0xe8, 0x7f, 0x89, 0xbb, 0xa8, 0x32, 0xe8, + 0x8c, 0x89, 0x73, 0xf8, 0xc3, 0x53, 0xb8, 0x10, + 0x00, 0xe8, 0xe8, 0xff, 0x5b, 0xc3, 0xa0, 0xe1, + 0xc3, 0x0a, 0xc0, 0x74, 0x14, 0xbb, 0x6f, 0xbb, + 0x83, 0xc3, 0x03, 0x8a, 0x27, 0x3a, 0xc4, 0x75, + 0xf7, 0x8b, 0x47, 0x01, 0xa3, 0xdc, 0xbb, 0xeb, + 0x1d, 0x80, 0x3e, 0x3d, 0x66, 0x06, 0x74, 0x01, + 0xc3, 0x80, 0x3e, 0xdb, 0xbb, 0x01, 0x74, 0x0e, + 0xbb, 0x57, 0x34, 0xe8, 0xaf, 0x80, 0xc7, 0x06, + 0x1f, 0xc4, 0x00, 0x00, 0xeb, 0x10, 0xe8, 0x2c, + 0x86, 0xc7, 0x06, 0x1f, 0xc4, 0x00, 0x00, 0xff, + 0x16, 0xdc, 0xbb, 0xe8, 0x41, 0x86, 0xc6, 0x06, + 0x3d, 0x66, 0x00, 0x33, 0xc0, 0xa3, 0x50, 0x72, + 0xa3, 0x52, 0x72, 0xa2, 0xe1, 0xc3, 0xe8, 0x7f, + 0x15, 0xc3, 0xc6, 0x06, 0xdb, 0xbb, 0x00, 0xbb, + 0x87, 0xbb, 0xe8, 0xfd, 0x1f, 0x8a, 0x07, 0x0a, + 0xc0, 0x74, 0x31, 0x8a, 0xc8, 0xa1, 0x1f, 0xc4, + 0xe8, 0x79, 0x08, 0x3a, 0xc1, 0x75, 0x26, 0x8a, + 0x67, 0x01, 0x3a, 0x26, 0x50, 0x72, 0x75, 0x1d, + 0xc6, 0x06, 0xdb, 0xbb, 0x01, 0x8b, 0x77, 0x03, + 0x8b, 0x7f, 0x05, 0x8a, 0x47, 0x02, 0xa2, 0xc3, + 0x64, 0x8b, 0x47, 0x07, 0xa3, 0xdc, 0xbb, 0xe8, + 0xc8, 0x1b, 0x72, 0x06, 0xc3, 0x83, 0xc3, 0x09, + 0xeb, 0xc3, 0xb0, 0x36, 0xe9, 0x49, 0x93, 0x83, + 0x3e, 0xc2, 0x00, 0x00, 0x74, 0x05, 0xe8, 0x21, + 0x00, 0xf8, 0xc3, 0xbb, 0x98, 0x32, 0x8b, 0x07, + 0x0b, 0x47, 0x02, 0x75, 0x06, 0xb9, 0x32, 0x00, + 0xe8, 0xa8, 0x88, 0xe8, 0xb8, 0x88, 0x72, 0x02, + 0xf8, 0xc3, 0xe8, 0x05, 0x00, 0xe8, 0x66, 0x00, + 0xf9, 0xc3, 0xbb, 0x98, 0x32, 0x33, 0xc0, 0x89, + 0x07, 0x89, 0x47, 0x02, 0xc3, 0xb8, 0x11, 0x00, + 0xba, 0x0c, 0x00, 0x8b, 0x1e, 0xc0, 0x00, 0x8b, + 0x0e, 0xc2, 0x00, 0x3b, 0xd8, 0x72, 0x1d, 0x3b, + 0xca, 0x72, 0x19, 0x05, 0x1e, 0x01, 0x81, 0xc2, + 0x8d, 0x00, 0x3b, 0xd8, 0x77, 0x0e, 0x3b, 0xca, + 0x77, 0x0a, 0xc6, 0x06, 0x6e, 0xc4, 0x01, 0xe8, + 0xc8, 0xff, 0xf8, 0xc3, 0xbb, 0x6e, 0xc4, 0x80, + 0x3f, 0x01, 0x74, 0x05, 0xc6, 0x07, 0x00, 0xf8, + 0xc3, 0xbb, 0x98, 0x32, 0x8b, 0x07, 0x0b, 0x47, + 0x02, 0xe8, 0xae, 0xff, 0xc6, 0x06, 0x6e, 0xc4, + 0x00, 0xf9, 0xc3, 0xb9, 0x19, 0x00, 0xbb, 0x98, + 0x32, 0xe8, 0x37, 0x88, 0xf8, 0xc3, 0xc6, 0x06, + 0xe1, 0xc3, 0x00, 0xc6, 0x06, 0xda, 0x1c, 0x01, + 0xe8, 0x4a, 0x09, 0xe8, 0x2d, 0x09, 0xe8, 0x79, + 0x00, 0xbf, 0x75, 0xc4, 0xb0, 0x01, 0xb9, 0x18, + 0x00, 0xfc, 0xf3, 0xaa, 0xa1, 0xbf, 0x32, 0xa3, + 0xac, 0x00, 0x33, 0xc0, 0xa3, 0xaa, 0x00, 0xb9, + 0x04, 0x00, 0xba, 0x54, 0x00, 0xe8, 0x90, 0x8e, + 0xe8, 0x9c, 0x00, 0xe8, 0x45, 0x02, 0xe8, 0xf4, + 0x84, 0xe8, 0xbc, 0x8a, 0xe8, 0xa4, 0x8a, 0xe8, + 0x13, 0x00, 0xe8, 0x1b, 0x86, 0xe8, 0x9a, 0x00, + 0xe8, 0x6f, 0x00, 0xe8, 0x01, 0x85, 0xc7, 0x06, + 0x52, 0x72, 0x00, 0x00, 0xc3, 0xe8, 0x9f, 0x87, + 0x80, 0x3e, 0xb0, 0x32, 0x01, 0x74, 0x05, 0xc6, + 0x06, 0xdc, 0x1c, 0x03, 0xc6, 0x06, 0xda, 0x1c, + 0x01, 0x83, 0x3e, 0x1f, 0xc4, 0x00, 0x74, 0x05, + 0xc6, 0x06, 0xda, 0x1c, 0x02, 0xe8, 0x4e, 0x07, + 0xc6, 0x06, 0xda, 0x1c, 0x00, 0x83, 0x3e, 0x1f, + 0xc4, 0x00, 0x74, 0x05, 0xc6, 0x06, 0xda, 0x1c, + 0x03, 0xc3, 0xc6, 0x06, 0x3d, 0x66, 0x00, 0xc7, + 0x06, 0x1f, 0xc4, 0x00, 0x00, 0xc6, 0x06, 0xc3, + 0x64, 0x00, 0xe8, 0x75, 0x1e, 0x80, 0x3e, 0xb0, + 0x32, 0x01, 0x74, 0x15, 0xe8, 0x22, 0x1d, 0xc6, + 0x06, 0xdc, 0x1c, 0x02, 0xc6, 0x06, 0xda, 0x1c, + 0x01, 0xe8, 0x12, 0x07, 0xc6, 0x06, 0xdc, 0x1c, + 0x03, 0xc3, 0x06, 0x1e, 0xb8, 0x00, 0xa0, 0x8e, + 0xc0, 0xa1, 0xb1, 0x32, 0x8e, 0xd8, 0xfc, 0xf3, + 0xa5, 0x1f, 0xe8, 0x5a, 0x84, 0x07, 0xc3, 0xe8, + 0x13, 0x01, 0xe8, 0x4c, 0x00, 0xe8, 0x2f, 0x01, + 0xe8, 0x07, 0x00, 0xe8, 0x82, 0x85, 0xe8, 0xd9, + 0xff, 0xc3, 0xb8, 0x0c, 0x00, 0xbb, 0x40, 0x01, + 0xf7, 0xe3, 0x8b, 0xf0, 0x8b, 0xf8, 0xb8, 0x0c, + 0x00, 0x05, 0x8d, 0x00, 0xf7, 0xe3, 0x2b, 0xc6, + 0x8b, 0xc8, 0xd1, 0xe9, 0xc3, 0xe8, 0x60, 0x85, + 0xe8, 0x2a, 0x84, 0xe8, 0xdc, 0xff, 0xe8, 0xb1, + 0xff, 0xe8, 0x43, 0x84, 0xc7, 0x06, 0x52, 0x72, + 0x00, 0x00, 0xbf, 0x75, 0xc4, 0xb0, 0x01, 0xb9, + 0x18, 0x00, 0xfc, 0xf3, 0xaa, 0xe8, 0xaf, 0xff, + 0xc3, 0x06, 0xa1, 0xb1, 0x32, 0x8e, 0xc0, 0x33, + 0xc9, 0x3b, 0x0e, 0x73, 0xc4, 0x73, 0x20, 0x41, + 0x51, 0xe8, 0x1d, 0x00, 0x89, 0x36, 0x71, 0xc4, + 0xb9, 0x1a, 0x00, 0xba, 0x28, 0x00, 0xe8, 0x4f, + 0x00, 0xb4, 0xea, 0xe8, 0x37, 0x02, 0xc7, 0x06, + 0x71, 0xc4, 0x00, 0x00, 0x59, 0xeb, 0xda, 0x07, + 0xc3, 0x33, 0xd2, 0x49, 0xfe, 0xce, 0xfe, 0xc6, + 0x83, 0xe9, 0x06, 0x73, 0xf9, 0x83, 0xc1, 0x06, + 0xbe, 0x11, 0x0f, 0x81, 0xc6, 0x45, 0x06, 0x81, + 0xc6, 0x45, 0x06, 0x33, 0xc0, 0x0a, 0xf6, 0x74, + 0x0a, 0x05, 0x1a, 0x00, 0x05, 0x05, 0x00, 0xfe, + 0xce, 0xeb, 0xf2, 0xf7, 0x26, 0xb6, 0x00, 0x03, + 0xf0, 0x0a, 0xc9, 0x74, 0x0a, 0x83, 0xc6, 0x28, + 0x83, 0xc6, 0x05, 0xfe, 0xc9, 0xeb, 0xf2, 0xc3, + 0x1e, 0x52, 0xbb, 0x40, 0x01, 0xa1, 0xb5, 0x32, + 0x8e, 0xd8, 0x8b, 0xc6, 0x2d, 0x11, 0x0f, 0x33, + 0xd2, 0xf7, 0xf3, 0x8b, 0xfa, 0x83, 0xc7, 0x06, + 0xbb, 0x1e, 0x01, 0xf7, 0xe3, 0x03, 0xf8, 0x5a, + 0x81, 0xc6, 0x41, 0x01, 0x81, 0xc7, 0x1f, 0x01, + 0x49, 0x49, 0x4a, 0x4a, 0x8b, 0xd9, 0x8b, 0xca, + 0x8a, 0x05, 0x3c, 0xe8, 0x75, 0x06, 0x26, 0xc6, + 0x04, 0xd6, 0xeb, 0x04, 0x26, 0xc6, 0x04, 0xe0, + 0x46, 0x47, 0xe2, 0xec, 0x81, 0xc6, 0x40, 0x01, + 0x2b, 0xf2, 0x81, 0xc7, 0x1e, 0x01, 0x2b, 0xfa, + 0x4b, 0x75, 0xdb, 0x1f, 0xc3, 0x06, 0xa1, 0xb5, + 0x32, 0x8e, 0xc0, 0xa3, 0xac, 0x00, 0xc7, 0x06, + 0xaa, 0x00, 0x00, 0x00, 0xb9, 0x03, 0x00, 0xba, + 0x54, 0x00, 0xe8, 0xc3, 0x8c, 0xa1, 0xb1, 0x32, + 0x33, 0xdb, 0xe8, 0xf7, 0xf6, 0x07, 0xc3, 0x06, + 0xa1, 0xbf, 0x32, 0x8e, 0xc0, 0x33, 0xc9, 0x3b, + 0x0e, 0x73, 0xc4, 0x73, 0x45, 0x41, 0x51, 0xbb, + 0x8c, 0xc4, 0x03, 0xd9, 0x33, 0xc9, 0x8a, 0x0f, + 0xbf, 0xfe, 0xff, 0xbe, 0xa4, 0xc4, 0xd1, 0xe1, + 0x03, 0xf9, 0x03, 0xf1, 0xd1, 0xe9, 0x8b, 0x34, + 0x47, 0x26, 0x8b, 0x3d, 0x80, 0x7c, 0x01, 0x00, + 0x74, 0x04, 0x26, 0x03, 0x7d, 0x01, 0x59, 0x51, + 0xe8, 0x0e, 0xff, 0x26, 0x8b, 0x0d, 0x26, 0x8b, + 0x55, 0x02, 0x26, 0x03, 0x75, 0x04, 0x83, 0xc7, + 0x06, 0xa1, 0xb1, 0x32, 0xe8, 0x05, 0x00, 0x59, + 0xeb, 0xb5, 0x07, 0xc3, 0x1e, 0x8e, 0xd8, 0x8b, + 0xd9, 0x8b, 0xcb, 0x26, 0x8a, 0x05, 0x3c, 0xff, + 0x74, 0x02, 0x88, 0x04, 0x47, 0x46, 0xe2, 0xf3, + 0x81, 0xc6, 0x40, 0x01, 0x2b, 0xf3, 0x4a, 0x75, + 0xe8, 0x1f, 0xc3, 0xbb, 0xa0, 0x32, 0xb9, 0x0b, + 0x00, 0xe8, 0xaf, 0x85, 0xe8, 0x91, 0x00, 0xe8, + 0x20, 0x02, 0xe8, 0x18, 0xfd, 0x72, 0x5b, 0xe8, + 0x91, 0x83, 0x72, 0x35, 0xe8, 0x5d, 0x83, 0x73, + 0xeb, 0xe8, 0x4f, 0x00, 0x72, 0xe6, 0x83, 0x3e, + 0x1f, 0xc4, 0x00, 0x74, 0x14, 0xa1, 0x6f, 0xc4, + 0x3b, 0x06, 0x1f, 0xc4, 0x74, 0xd6, 0xa3, 0x21, + 0xc4, 0xe8, 0x80, 0x04, 0xe8, 0x45, 0x00, 0xeb, + 0xcb, 0xa1, 0x6f, 0xc4, 0xa3, 0x1f, 0xc4, 0xe8, + 0x20, 0x04, 0x72, 0x26, 0xe8, 0x27, 0x01, 0xeb, + 0xbb, 0xe8, 0x1f, 0x00, 0x72, 0xb6, 0x83, 0x3e, + 0x1f, 0xc4, 0x00, 0x75, 0xd0, 0xe8, 0x3f, 0x04, + 0x72, 0x10, 0xe8, 0xb9, 0x02, 0xb4, 0xea, 0xe8, + 0x9b, 0x00, 0xc7, 0x06, 0x6f, 0xc4, 0x00, 0x00, + 0xeb, 0x9a, 0xc3, 0x83, 0x3e, 0x6f, 0xc4, 0x00, + 0x74, 0x02, 0xf8, 0xc3, 0xe8, 0x05, 0x00, 0xe8, + 0xe9, 0x06, 0xf9, 0xc3, 0x33, 0xc0, 0xa3, 0x6f, + 0xc4, 0xa3, 0x1f, 0xc4, 0xa3, 0x21, 0xc4, 0xc3, + 0xbb, 0xe2, 0xc3, 0xe8, 0x01, 0x00, 0xc3, 0x53, + 0xe8, 0x7e, 0x07, 0x5b, 0xe8, 0x93, 0x02, 0xc3, + 0x33, 0xc9, 0x3b, 0x0e, 0x73, 0xc4, 0x73, 0x13, + 0x41, 0x51, 0xe8, 0x2c, 0xfe, 0xbb, 0x1a, 0x00, + 0xb9, 0x28, 0x00, 0xe8, 0x98, 0x00, 0x59, 0x72, + 0x2b, 0xeb, 0xe7, 0x8b, 0x0e, 0x6f, 0xc4, 0x0b, + 0xc9, 0x74, 0x20, 0xb4, 0xea, 0xe8, 0x3d, 0x00, + 0xc7, 0x06, 0x6f, 0xc4, 0x00, 0x00, 0xc7, 0x06, + 0x71, 0xc4, 0x00, 0x00, 0x83, 0x3e, 0x1f, 0xc4, + 0x00, 0x74, 0x05, 0xe8, 0x98, 0x00, 0xeb, 0x03, + 0xe8, 0x88, 0x06, 0xc3, 0x3b, 0x0e, 0x6f, 0xc4, + 0x75, 0x01, 0xc3, 0x51, 0x56, 0xb4, 0xea, 0xe8, + 0x13, 0x00, 0x5e, 0x59, 0x89, 0x0e, 0x6f, 0xc4, + 0x89, 0x36, 0x71, 0xc4, 0xb4, 0xe9, 0xe8, 0x04, + 0x00, 0xe8, 0x72, 0x00, 0xc3, 0x8b, 0x0e, 0xb1, + 0x32, 0x50, 0xe8, 0x0b, 0x00, 0x58, 0xb9, 0x00, + 0xa0, 0xe8, 0x04, 0x00, 0xe8, 0x98, 0x81, 0xc3, + 0x06, 0x8e, 0xc1, 0x8b, 0x36, 0x71, 0xc4, 0x0b, + 0xf6, 0x74, 0x29, 0xb9, 0x28, 0x00, 0x26, 0x88, + 0x24, 0x46, 0xe2, 0xfa, 0x81, 0xc6, 0x40, 0x01, + 0x83, 0xee, 0x28, 0xb9, 0x18, 0x00, 0x26, 0x88, + 0x24, 0x26, 0x88, 0x64, 0x27, 0x81, 0xc6, 0x40, + 0x01, 0xe2, 0xf3, 0xb9, 0x28, 0x00, 0x26, 0x88, + 0x24, 0x46, 0xe2, 0xfa, 0x07, 0xc3, 0x8b, 0xc6, + 0x33, 0xd2, 0xf7, 0x36, 0xb6, 0x00, 0x3b, 0x06, + 0xc2, 0x00, 0x77, 0x18, 0x3b, 0x16, 0xc0, 0x00, + 0x77, 0x12, 0x03, 0xc3, 0x3b, 0x06, 0xc2, 0x00, + 0x76, 0x0a, 0x03, 0xd1, 0x3b, 0x16, 0xc0, 0x00, + 0x76, 0x02, 0xf9, 0xc3, 0xf8, 0xc3, 0xe8, 0xf2, + 0x05, 0xe8, 0x47, 0x05, 0xe8, 0x48, 0x00, 0xbb, + 0x28, 0xc4, 0xbe, 0xe8, 0x1c, 0x81, 0xc6, 0x85, + 0x02, 0x56, 0xc6, 0x06, 0xe6, 0x1c, 0xd1, 0xe8, + 0x9b, 0x7f, 0x8b, 0xc6, 0x5e, 0x2b, 0xc6, 0xbe, + 0x05, 0x00, 0x03, 0xc6, 0x03, 0xc6, 0x40, 0x0c, + 0x01, 0x3d, 0x40, 0x01, 0x76, 0x03, 0xb8, 0x40, + 0x01, 0xa3, 0xe0, 0x1c, 0xb9, 0x40, 0x01, 0x2b, + 0xc8, 0xd1, 0xe9, 0xb8, 0xb4, 0x00, 0xbb, 0x40, + 0x01, 0xf7, 0xe3, 0x03, 0xc1, 0xa3, 0xde, 0x1c, + 0xb8, 0x00, 0xa0, 0xe8, 0x3f, 0x05, 0xc3, 0xbf, + 0x28, 0xc4, 0xb9, 0x46, 0x00, 0x32, 0xc0, 0xfc, + 0xf3, 0xaa, 0x8b, 0x0e, 0x6f, 0xc4, 0x0b, 0xc9, + 0x74, 0x06, 0xe8, 0x44, 0x00, 0x83, 0xc3, 0x02, + 0xbe, 0x28, 0xc4, 0x8b, 0x0e, 0x1f, 0xc4, 0x0b, + 0xc9, 0x74, 0x2b, 0x53, 0xe8, 0x32, 0x00, 0x83, + 0xc3, 0x02, 0xe8, 0x21, 0x00, 0x4e, 0xb0, 0x20, + 0xb4, 0x26, 0x88, 0x04, 0x88, 0x64, 0x01, 0x88, + 0x44, 0x02, 0x83, 0xc6, 0x03, 0x5b, 0xa1, 0x1f, + 0xc4, 0x8b, 0x0e, 0x6f, 0xc4, 0x0b, 0xc9, 0x74, + 0x04, 0x3b, 0xc1, 0x75, 0x01, 0xc3, 0x8a, 0x07, + 0x88, 0x04, 0x43, 0x46, 0x0a, 0xc0, 0x75, 0xf6, + 0xc3, 0xbb, 0x8c, 0xc4, 0x03, 0xd9, 0x8a, 0x0f, + 0xbb, 0xa4, 0xc4, 0xd1, 0xe1, 0x03, 0xd9, 0x8b, + 0x1f, 0xc3, 0xbb, 0xa0, 0x32, 0xe8, 0x96, 0x83, + 0x72, 0x01, 0xc3, 0xb9, 0x0b, 0x00, 0xe8, 0x7a, + 0x83, 0x06, 0xa1, 0xbf, 0x32, 0x8e, 0xc0, 0x33, + 0xc9, 0x3b, 0x0e, 0x73, 0xc4, 0x73, 0x6d, 0x41, + 0x51, 0xbb, 0x8c, 0xc4, 0x03, 0xd9, 0x8a, 0x07, + 0xbb, 0x74, 0xc4, 0x03, 0xd9, 0x33, 0xc9, 0x8a, + 0xc8, 0xbf, 0xfe, 0xff, 0xbe, 0xa4, 0xc4, 0xd1, + 0xe1, 0x03, 0xf9, 0x03, 0xf1, 0x8b, 0x34, 0x47, + 0x26, 0x8b, 0x3d, 0x80, 0x7c, 0x01, 0x00, 0x74, + 0x40, 0x33, 0xc9, 0x8a, 0x0f, 0xfe, 0xc1, 0x26, + 0x3a, 0x0d, 0x76, 0x02, 0xb1, 0x01, 0x88, 0x0f, + 0x49, 0xd1, 0xe1, 0x03, 0xf9, 0x26, 0x03, 0x7d, + 0x01, 0x2b, 0xf9, 0x59, 0x51, 0xe8, 0x41, 0xfc, + 0xe8, 0x23, 0x00, 0x56, 0x26, 0x8b, 0x0d, 0x26, + 0x8b, 0x55, 0x02, 0x26, 0x03, 0x75, 0x04, 0x83, + 0xc7, 0x06, 0xa1, 0xb1, 0x32, 0xe8, 0x34, 0xfd, + 0x5e, 0x56, 0xe8, 0x1e, 0x00, 0x5e, 0xe8, 0x44, + 0x00, 0x59, 0xeb, 0x8d, 0x07, 0xc3, 0x57, 0x56, + 0x06, 0xa1, 0xb1, 0x32, 0x8e, 0xc0, 0xb9, 0x1a, + 0x00, 0xba, 0x28, 0x00, 0xe8, 0x49, 0xfc, 0x07, + 0x5e, 0x5f, 0xc3, 0x1e, 0x06, 0x81, 0xc6, 0x41, + 0x01, 0x8b, 0xfe, 0xb8, 0x00, 0xa0, 0x8e, 0xc0, + 0xa1, 0xb1, 0x32, 0x8e, 0xd8, 0xfc, 0xb9, 0x18, + 0x00, 0x51, 0xb9, 0x26, 0x00, 0xf3, 0xa4, 0x81, + 0xc6, 0x1a, 0x01, 0x81, 0xc7, 0x1a, 0x01, 0x59, + 0xe2, 0xef, 0x07, 0x1f, 0xc3, 0xbb, 0x1a, 0x00, + 0xb9, 0x28, 0x00, 0xb8, 0x0c, 0x00, 0x03, 0xd8, + 0xf7, 0x26, 0xb6, 0x00, 0x2b, 0xf0, 0xb8, 0x08, + 0x00, 0x2b, 0xf0, 0x03, 0xc8, 0xe8, 0x36, 0xfe, + 0x73, 0x03, 0xe8, 0x92, 0x7f, 0xc3, 0xe8, 0xf8, + 0x04, 0x8b, 0x0e, 0x6f, 0xc4, 0xe8, 0xf9, 0xfe, + 0x83, 0xc3, 0x02, 0x43, 0x80, 0x3f, 0x00, 0x75, + 0xfa, 0x43, 0x06, 0xb8, 0xd4, 0x19, 0x8e, 0xc0, + 0x33, 0xc0, 0x26, 0xa3, 0x0c, 0x00, 0x26, 0xa3, + 0x0a, 0x00, 0xbe, 0x04, 0x00, 0x53, 0xe8, 0x37, + 0x7e, 0x26, 0x01, 0x0e, 0x0c, 0x00, 0x26, 0x89, + 0x04, 0x26, 0xff, 0x06, 0x0a, 0x00, 0x83, 0xc6, + 0x02, 0x43, 0x8a, 0x07, 0x0a, 0xc0, 0x75, 0xe6, + 0x5b, 0x8b, 0xce, 0x33, 0xc0, 0x83, 0xee, 0x02, + 0x81, 0xfe, 0x04, 0x00, 0x72, 0x0b, 0x26, 0x8b, + 0x14, 0x3b, 0xc2, 0x73, 0xf0, 0x8b, 0xc2, 0xeb, + 0xec, 0x8b, 0xf1, 0xb9, 0x40, 0x01, 0x2b, 0xc8, + 0xd1, 0xe9, 0x81, 0xc1, 0xc0, 0xcb, 0x26, 0x89, + 0x0e, 0x00, 0x00, 0x83, 0xee, 0x02, 0x81, 0xfe, + 0x04, 0x00, 0x72, 0x0e, 0x26, 0x8b, 0x14, 0x8b, + 0xc8, 0x2b, 0xca, 0xd1, 0xe9, 0x26, 0x89, 0x0c, + 0xeb, 0xe9, 0xbf, 0x04, 0x00, 0xbe, 0x0e, 0x00, + 0x57, 0x56, 0x26, 0x03, 0x35, 0xc6, 0x06, 0xe6, + 0x1c, 0xd1, 0xe8, 0x78, 0x7d, 0x5e, 0x5f, 0x81, + 0xc6, 0xc0, 0x0d, 0x83, 0xc7, 0x02, 0x43, 0x8a, + 0x07, 0x0a, 0xc0, 0x75, 0xe3, 0xe8, 0xed, 0x7e, + 0xe8, 0x1d, 0x80, 0xe8, 0x9d, 0x03, 0xb8, 0x00, + 0xa0, 0xe8, 0xf2, 0x03, 0xbb, 0x92, 0x32, 0xa1, + 0x96, 0x32, 0x26, 0x8b, 0x0e, 0x0c, 0x00, 0xf7, + 0xe1, 0x8b, 0xc8, 0xc1, 0xe9, 0x03, 0x83, 0xc1, + 0x3c, 0xe8, 0xcf, 0x81, 0xe8, 0x43, 0xfe, 0x80, + 0x3e, 0x96, 0x32, 0x00, 0x74, 0x08, 0xbb, 0x92, + 0x32, 0xe8, 0xd2, 0x81, 0x72, 0x0a, 0xe8, 0xaa, + 0x7f, 0x72, 0x05, 0xe8, 0x76, 0x7f, 0x73, 0xe4, + 0xe8, 0x04, 0x04, 0xe8, 0xc9, 0x7e, 0x07, 0xc3, + 0xbb, 0x8d, 0xc4, 0x8b, 0x0e, 0x73, 0xc4, 0x03, + 0xd9, 0x88, 0x07, 0xff, 0x06, 0x73, 0xc4, 0xc3, + 0xbb, 0x8d, 0xc4, 0x8b, 0x0e, 0x73, 0xc4, 0x3a, + 0x07, 0x74, 0x08, 0x43, 0xe2, 0xf9, 0xb0, 0x3b, + 0xe9, 0x0d, 0x8c, 0x8a, 0x47, 0x01, 0x88, 0x07, + 0x43, 0xe2, 0xf8, 0xc6, 0x47, 0x01, 0x00, 0xff, + 0x0e, 0x73, 0xc4, 0xc3, 0xbb, 0x8d, 0xc4, 0x8b, + 0x0e, 0x73, 0xc4, 0x3a, 0x07, 0x74, 0x05, 0x43, + 0xe2, 0xf9, 0xf8, 0xc3, 0xf9, 0xc3, 0xbb, 0x8d, + 0xc4, 0x8b, 0x0e, 0x73, 0xc4, 0xc6, 0x07, 0x00, + 0x43, 0xe2, 0xfa, 0xc7, 0x06, 0x73, 0xc4, 0x00, + 0x00, 0xc3, 0xa1, 0x1f, 0xc4, 0xe8, 0xcc, 0x00, + 0x3c, 0x03, 0x74, 0x1e, 0x3c, 0x29, 0x74, 0x1a, + 0x3c, 0x25, 0x74, 0x16, 0x3c, 0x48, 0x74, 0x12, + 0xb0, 0xff, 0x83, 0x3e, 0xf3, 0xb4, 0x18, 0x75, + 0x07, 0x80, 0x3e, 0xa4, 0xdb, 0x00, 0x74, 0x02, + 0xf8, 0xc3, 0xa2, 0xe1, 0xc3, 0xe8, 0x8a, 0xf8, + 0xc6, 0x06, 0x6e, 0xc4, 0x00, 0xf9, 0xc3, 0xa1, + 0x6f, 0xc4, 0xe8, 0x97, 0x00, 0x3c, 0x04, 0x74, + 0x06, 0x3c, 0x33, 0x74, 0x02, 0xf8, 0xc3, 0xa2, + 0xe1, 0xc3, 0xe8, 0x6d, 0xf8, 0xc6, 0x06, 0x6e, + 0xc4, 0x00, 0xf9, 0xc3, 0xa1, 0x1f, 0xc4, 0xe8, + 0x7a, 0x00, 0x8a, 0xc8, 0xa1, 0x21, 0xc4, 0xe8, + 0x72, 0x00, 0x8a, 0xd0, 0xbb, 0x35, 0xc3, 0x8b, + 0x07, 0x3a, 0xc1, 0x75, 0x04, 0x3a, 0xe2, 0x74, + 0x08, 0x3a, 0xc2, 0x75, 0x53, 0x3a, 0xe1, 0x75, + 0x4f, 0x80, 0x7f, 0x02, 0x00, 0x74, 0x42, 0x60, + 0xb9, 0x45, 0x00, 0xb0, 0x01, 0xb4, 0x05, 0xff, + 0x1e, 0x4a, 0x32, 0xb8, 0x3b, 0x0b, 0x8e, 0xd8, + 0x8e, 0xc0, 0xa0, 0x49, 0x32, 0xb4, 0x04, 0xb5, + 0x07, 0xff, 0x1e, 0x4a, 0x32, 0xb8, 0x3b, 0x0b, + 0x8e, 0xd8, 0x8e, 0xc0, 0x61, 0x53, 0x8a, 0x07, + 0xe8, 0x0d, 0xff, 0x5b, 0x53, 0x8a, 0x47, 0x01, + 0xe8, 0x05, 0xff, 0x5b, 0x53, 0x8a, 0x47, 0x02, + 0xe8, 0xed, 0xfe, 0x5b, 0x53, 0xe8, 0x5d, 0xf9, + 0x5b, 0x8b, 0x5f, 0x03, 0xe8, 0x68, 0xfb, 0xc3, + 0x83, 0xc3, 0x05, 0x83, 0x3f, 0x00, 0x75, 0x97, + 0xe8, 0x55, 0xfb, 0xc3, 0x0b, 0xc0, 0x75, 0x01, + 0xc3, 0x53, 0x48, 0xbb, 0x8d, 0xc4, 0x03, 0xd8, + 0x8a, 0x07, 0x32, 0xe4, 0x5b, 0xc3, 0x60, 0xe8, + 0x52, 0x06, 0xe8, 0x2f, 0x03, 0xe8, 0x85, 0x01, + 0xe8, 0x43, 0x02, 0xe8, 0xf2, 0x06, 0xe8, 0x72, + 0x03, 0xe8, 0xdb, 0x04, 0xe8, 0x8e, 0x01, 0xe8, + 0x4c, 0x02, 0xe8, 0x7b, 0x7e, 0xe8, 0xc1, 0x0b, + 0xe8, 0x74, 0x05, 0xe8, 0xc2, 0x01, 0xe8, 0x89, + 0x02, 0xe8, 0x33, 0x7d, 0x61, 0xc3, 0xe8, 0x2a, + 0x00, 0x0b, 0xc0, 0x74, 0x17, 0x3d, 0x03, 0x00, + 0x74, 0x12, 0x3d, 0x01, 0x00, 0x74, 0x0e, 0xc6, + 0x06, 0xda, 0x1c, 0x02, 0xe8, 0xb7, 0xff, 0xc6, + 0x06, 0xda, 0x1c, 0x03, 0xc3, 0xc6, 0x06, 0xda, + 0x1c, 0x01, 0xe8, 0xa9, 0xff, 0xc6, 0x06, 0xda, + 0x1c, 0x00, 0xc3, 0xbb, 0x54, 0x72, 0xe8, 0x01, + 0x17, 0x8b, 0xf3, 0x8b, 0x1c, 0x0b, 0xdb, 0x74, + 0x0a, 0xe8, 0xd3, 0x00, 0x72, 0x20, 0x83, 0xc6, + 0x02, 0xeb, 0xf0, 0x83, 0x3e, 0x52, 0x72, 0x00, + 0x75, 0x03, 0x33, 0xc0, 0xc3, 0xc7, 0x06, 0x52, + 0x72, 0x00, 0x00, 0x83, 0x3e, 0x1f, 0xc4, 0x00, + 0x75, 0x11, 0xb8, 0x01, 0x00, 0xc3, 0x3b, 0x06, + 0x52, 0x72, 0x75, 0x04, 0xb8, 0x03, 0x00, 0xc3, + 0xa3, 0x52, 0x72, 0xa1, 0xde, 0x1c, 0xa3, 0xe2, + 0x1c, 0xa1, 0xe0, 0x1c, 0xa3, 0xe4, 0x1c, 0x53, + 0xc6, 0x06, 0xda, 0x1c, 0x01, 0xe8, 0xdd, 0x00, + 0xe8, 0xc0, 0x00, 0x5b, 0x83, 0xc3, 0x13, 0xe8, + 0x45, 0x00, 0xbb, 0x28, 0xc4, 0xbe, 0xe8, 0x1c, + 0x81, 0xc6, 0x85, 0x02, 0x56, 0xc6, 0x06, 0xe6, + 0x1c, 0xd1, 0xe8, 0x10, 0x7b, 0x8b, 0xc6, 0x5e, + 0x2b, 0xc6, 0xbe, 0x05, 0x00, 0x03, 0xc6, 0x03, + 0xc6, 0x40, 0x0c, 0x01, 0x3d, 0x40, 0x01, 0x76, + 0x03, 0xb8, 0x40, 0x01, 0xa3, 0xe0, 0x1c, 0xb9, + 0x40, 0x01, 0x2b, 0xc8, 0xd1, 0xe9, 0xb8, 0xb4, + 0x00, 0xbb, 0x40, 0x01, 0xf7, 0xe3, 0x03, 0xc1, + 0xa3, 0xde, 0x1c, 0xb8, 0x02, 0x00, 0xc3, 0xbf, + 0x28, 0xc4, 0xb9, 0x46, 0x00, 0x32, 0xc0, 0xfc, + 0xf3, 0xaa, 0xbe, 0x28, 0xc4, 0x8b, 0x0e, 0x1f, + 0xc4, 0x0b, 0xc9, 0x74, 0x26, 0x53, 0xe8, 0xb8, + 0xfb, 0x83, 0xc3, 0x02, 0xe8, 0xa7, 0xfb, 0x4e, + 0xb0, 0x20, 0xb4, 0x26, 0x88, 0x04, 0x88, 0x64, + 0x01, 0x88, 0x44, 0x02, 0x83, 0xc6, 0x03, 0x5b, + 0xa1, 0x1f, 0xc4, 0x8b, 0x0e, 0x52, 0x72, 0x0b, + 0xc9, 0x74, 0x03, 0xe8, 0x88, 0xfb, 0xc3, 0x8a, + 0x47, 0x12, 0x0a, 0xc0, 0x74, 0x29, 0xa1, 0xc0, + 0x00, 0x8b, 0x16, 0xc2, 0x00, 0x8b, 0x4f, 0x01, + 0x3b, 0xc1, 0x72, 0x1b, 0x8b, 0x4f, 0x03, 0x3b, + 0xd1, 0x72, 0x14, 0x8b, 0x4f, 0x05, 0x3b, 0xc1, + 0x77, 0x0d, 0x8b, 0x4f, 0x07, 0x3b, 0xd1, 0x77, + 0x06, 0x33, 0xc0, 0x8a, 0x07, 0xf9, 0xc3, 0x33, + 0xc0, 0xf8, 0xc3, 0xbe, 0xe8, 0x1c, 0xb9, 0x0f, + 0x00, 0xb0, 0xff, 0xb4, 0xe0, 0xba, 0x40, 0x01, + 0x88, 0x04, 0x86, 0xc4, 0x46, 0x4a, 0x75, 0xf8, + 0x86, 0xc4, 0xe2, 0xf1, 0xc3, 0xa0, 0xda, 0x1c, + 0x3c, 0x00, 0x75, 0x01, 0xc3, 0x3c, 0x03, 0x75, + 0x01, 0xc3, 0xa1, 0xb3, 0x32, 0x8b, 0x1e, 0xb1, + 0x32, 0xe8, 0x81, 0x00, 0xc3, 0xa0, 0xda, 0x1c, + 0x3c, 0x00, 0x75, 0x01, 0xc3, 0x3c, 0x01, 0x75, + 0x01, 0xc3, 0xa1, 0xb1, 0x32, 0x06, 0x8e, 0xc0, + 0x8b, 0x36, 0xde, 0x1c, 0x8b, 0x16, 0xe0, 0x1c, + 0xbf, 0xe8, 0x1c, 0xb9, 0x0f, 0x00, 0x8b, 0xda, + 0x8a, 0x05, 0x3c, 0xff, 0x74, 0x03, 0x26, 0x88, + 0x04, 0x46, 0x47, 0x4a, 0x75, 0xf2, 0x8b, 0xd3, + 0x81, 0xc6, 0x40, 0x01, 0x2b, 0xf2, 0x81, 0xc7, + 0x40, 0x01, 0x2b, 0xfa, 0xe2, 0xe0, 0x07, 0xc3, + 0xa0, 0xda, 0x1c, 0x3c, 0x00, 0x75, 0x01, 0xc3, + 0x3c, 0x03, 0x75, 0x01, 0xc3, 0xa1, 0xe4, 0x1c, + 0x3b, 0x06, 0xe0, 0x1c, 0x72, 0x1d, 0xff, 0x36, + 0xe0, 0x1c, 0xff, 0x36, 0xde, 0x1c, 0xa3, 0xe0, + 0x1c, 0xa1, 0xe2, 0x1c, 0xa3, 0xde, 0x1c, 0xe8, + 0x09, 0x00, 0x8f, 0x06, 0xde, 0x1c, 0x8f, 0x06, + 0xe0, 0x1c, 0xc3, 0xa1, 0xb1, 0x32, 0xbb, 0x00, + 0xa0, 0xe8, 0x01, 0x00, 0xc3, 0x8b, 0x36, 0xde, + 0x1c, 0x8b, 0x16, 0xe0, 0x1c, 0xb9, 0x0f, 0x00, + 0x1e, 0x06, 0x8e, 0xdb, 0x8e, 0xc0, 0x8b, 0xda, + 0x26, 0x8a, 0x04, 0x88, 0x04, 0x46, 0x4a, 0x75, + 0xf7, 0x8b, 0xd3, 0x81, 0xc6, 0x40, 0x01, 0x2b, + 0xf2, 0xe2, 0xeb, 0x07, 0x1f, 0xc3, 0xa0, 0xdb, + 0x1c, 0x3c, 0x00, 0x75, 0x01, 0xc3, 0x3c, 0x03, + 0x75, 0x01, 0xc3, 0xa1, 0xb3, 0x32, 0x8b, 0x1e, + 0xb1, 0x32, 0xe8, 0x78, 0x00, 0xc3, 0xa0, 0xdb, + 0x1c, 0x3c, 0x00, 0x75, 0x01, 0xc3, 0x3c, 0x01, + 0x75, 0x01, 0xc3, 0xa1, 0xb1, 0x32, 0x1e, 0x06, + 0xba, 0xd4, 0x19, 0x8e, 0xc2, 0x26, 0x8b, 0x36, + 0x00, 0x00, 0x26, 0x8b, 0x16, 0x02, 0x00, 0xbf, + 0x0e, 0x00, 0xb9, 0x21, 0x00, 0x8e, 0xd8, 0x8b, + 0xda, 0x26, 0x8a, 0x05, 0x3c, 0xff, 0x74, 0x02, + 0x88, 0x04, 0x46, 0x47, 0x4a, 0x75, 0xf2, 0x8b, + 0xd3, 0x81, 0xc6, 0x40, 0x01, 0x2b, 0xf2, 0x81, + 0xc7, 0x40, 0x01, 0x2b, 0xfa, 0xe2, 0xe0, 0x07, + 0x1f, 0xc3, 0xa0, 0xdb, 0x1c, 0x3c, 0x00, 0x75, + 0x01, 0xc3, 0x3c, 0x03, 0x75, 0x01, 0xc3, 0xa1, + 0xb1, 0x32, 0xbb, 0x00, 0xa0, 0xe8, 0x15, 0x00, + 0xc3, 0x1e, 0xb9, 0xd4, 0x19, 0x8e, 0xd9, 0xbb, + 0x0e, 0x00, 0xb9, 0x40, 0x29, 0xc6, 0x07, 0xff, + 0x43, 0xe2, 0xfa, 0x1f, 0xc3, 0x1e, 0xb9, 0xd4, + 0x19, 0x8e, 0xd9, 0x8b, 0x36, 0x00, 0x00, 0x8b, + 0x16, 0x02, 0x00, 0xb9, 0x21, 0x00, 0x1f, 0x1e, + 0x06, 0x8e, 0xdb, 0x8e, 0xc0, 0x8b, 0xda, 0x26, + 0x8a, 0x04, 0x88, 0x04, 0x46, 0x4a, 0x75, 0xf7, + 0x8b, 0xd3, 0x81, 0xc6, 0x40, 0x01, 0x2b, 0xf2, + 0xe2, 0xeb, 0x07, 0x1f, 0xc3, 0xbb, 0xa4, 0x32, + 0xe8, 0x6b, 0x7d, 0x73, 0x1e, 0x8b, 0x0e, 0x90, + 0x32, 0xe8, 0x4f, 0x7d, 0xc6, 0x06, 0x33, 0x33, + 0x01, 0xc6, 0x06, 0xdd, 0x1c, 0x02, 0xe8, 0xd5, + 0xfc, 0xc6, 0x06, 0x33, 0x33, 0x00, 0xc6, 0x06, + 0xdd, 0x1c, 0x03, 0xc3, 0xb9, 0x01, 0x00, 0xbb, + 0xc7, 0x32, 0x53, 0x51, 0x49, 0xb0, 0x1b, 0xf6, + 0xe1, 0x03, 0xd8, 0x8b, 0x07, 0x0b, 0xc0, 0x74, + 0x0c, 0x8b, 0x7f, 0x12, 0x8b, 0x4f, 0x14, 0x8b, + 0x5f, 0x10, 0xe8, 0x09, 0x00, 0x59, 0x5b, 0x41, + 0x80, 0xf9, 0x04, 0x76, 0xdd, 0xc3, 0x1e, 0x06, + 0xa1, 0xb3, 0x32, 0x8e, 0xc0, 0xa1, 0xb1, 0x32, + 0x8e, 0xd8, 0x8b, 0xd1, 0x8b, 0xcf, 0x26, 0x8a, + 0x07, 0x88, 0x07, 0x43, 0xe2, 0xf8, 0x8b, 0xca, + 0x81, 0xc3, 0x40, 0x01, 0x2b, 0xdf, 0xe2, 0xea, + 0x07, 0x1f, 0xc3, 0x80, 0x3e, 0x33, 0x33, 0x01, + 0x74, 0x01, 0xc3, 0xb9, 0x01, 0x00, 0xbf, 0xc7, + 0x32, 0x57, 0x51, 0x49, 0xb0, 0x1b, 0xf6, 0xe1, + 0x03, 0xf8, 0x8b, 0x05, 0x0b, 0xc0, 0x74, 0x06, + 0x8b, 0x45, 0x16, 0xe8, 0x09, 0x00, 0x59, 0x5f, + 0x41, 0x80, 0xf9, 0x04, 0x76, 0xe3, 0xc3, 0x06, + 0x8e, 0xc0, 0x51, 0xbb, 0xff, 0xff, 0x8b, 0x05, + 0xb9, 0x03, 0x00, 0xf7, 0xe1, 0x03, 0xd8, 0xbe, + 0x00, 0x00, 0x26, 0x8b, 0x34, 0x8b, 0xd6, 0x3b, + 0xde, 0x72, 0x06, 0xc7, 0x05, 0x01, 0x00, 0xeb, + 0xe2, 0xff, 0x05, 0x33, 0xc9, 0x26, 0x8a, 0x0f, + 0x88, 0x4d, 0x1a, 0x26, 0x8b, 0x77, 0x01, 0x89, + 0x75, 0x0a, 0x8b, 0xda, 0x43, 0x83, 0xeb, 0x02, + 0xd1, 0xe1, 0x03, 0xd9, 0x26, 0x8b, 0x1f, 0x03, + 0xda, 0x26, 0x8b, 0x07, 0x89, 0x45, 0x0c, 0x26, + 0x8b, 0x47, 0x02, 0x89, 0x45, 0x0e, 0x83, 0xc3, + 0x04, 0x89, 0x5d, 0x18, 0x33, 0xd2, 0x8b, 0xc6, + 0xf7, 0x36, 0xb6, 0x00, 0x89, 0x16, 0x4f, 0x33, + 0xa3, 0x57, 0x33, 0x03, 0x55, 0x0c, 0x03, 0x45, + 0x0e, 0x89, 0x16, 0x51, 0x33, 0xa3, 0x59, 0x33, + 0x59, 0xe8, 0x85, 0x00, 0x33, 0xd2, 0x8b, 0x45, + 0x10, 0xf7, 0x36, 0xb6, 0x00, 0x89, 0x16, 0x53, + 0x33, 0xa3, 0x5b, 0x33, 0x03, 0x55, 0x12, 0x03, + 0x45, 0x14, 0x89, 0x16, 0x55, 0x33, 0xa3, 0x5d, + 0x33, 0xa1, 0x4f, 0x33, 0x8b, 0x1e, 0x53, 0x33, + 0x3b, 0xc3, 0x76, 0x04, 0x89, 0x1e, 0x4f, 0x33, + 0xa1, 0x51, 0x33, 0x8b, 0x1e, 0x55, 0x33, 0x3b, + 0xc3, 0x77, 0x04, 0x89, 0x1e, 0x51, 0x33, 0xa1, + 0x57, 0x33, 0x8b, 0x1e, 0x5b, 0x33, 0x3b, 0xc3, + 0x76, 0x04, 0x89, 0x1e, 0x57, 0x33, 0xa1, 0x59, + 0x33, 0x8b, 0x1e, 0x5d, 0x33, 0x3b, 0xc3, 0x77, + 0x04, 0x89, 0x1e, 0x59, 0x33, 0x07, 0xa1, 0x4f, + 0x33, 0x89, 0x45, 0x02, 0xa1, 0x57, 0x33, 0x89, + 0x45, 0x04, 0xa1, 0x51, 0x33, 0x89, 0x45, 0x06, + 0xa1, 0x59, 0x33, 0x89, 0x45, 0x08, 0x8b, 0x45, + 0x0c, 0x89, 0x45, 0x12, 0x8b, 0x45, 0x0e, 0x89, + 0x45, 0x14, 0x8b, 0x45, 0x0a, 0x89, 0x45, 0x10, + 0xc3, 0x80, 0x3e, 0x34, 0x33, 0x01, 0x74, 0x01, + 0xc3, 0xbb, 0x9e, 0xd8, 0x8b, 0x16, 0xf3, 0xb4, + 0x4a, 0xc1, 0xe2, 0x02, 0x03, 0xda, 0x03, 0xd9, + 0x33, 0xc9, 0x8a, 0x0f, 0x80, 0xf9, 0xff, 0x74, + 0x25, 0x49, 0xbb, 0x54, 0x72, 0xe8, 0x92, 0x12, + 0xd1, 0xe1, 0x03, 0xd9, 0x8b, 0x1f, 0x43, 0xa1, + 0x4f, 0x33, 0x89, 0x07, 0xa1, 0x57, 0x33, 0x89, + 0x47, 0x02, 0xa1, 0x51, 0x33, 0x89, 0x47, 0x04, + 0xa1, 0x59, 0x33, 0x89, 0x47, 0x06, 0xc3, 0xb9, + 0x01, 0x00, 0xbf, 0xc7, 0x32, 0x57, 0x51, 0x49, + 0xb0, 0x1b, 0xf6, 0xe1, 0x03, 0xf8, 0x8b, 0x05, + 0x0b, 0xc0, 0x74, 0x15, 0x8b, 0x45, 0x0a, 0x33, + 0xd2, 0xf7, 0x36, 0xb6, 0x00, 0x03, 0x45, 0x0e, + 0x3b, 0x06, 0xb1, 0x64, 0x73, 0x03, 0xe8, 0x48, + 0x00, 0x59, 0x5f, 0x41, 0x80, 0xf9, 0x04, 0x76, + 0xd4, 0x80, 0x3e, 0xdc, 0x1c, 0x01, 0x74, 0x03, + 0xe8, 0x6c, 0x03, 0xe8, 0x9f, 0x05, 0xb9, 0x01, + 0x00, 0xbf, 0xc7, 0x32, 0x57, 0x51, 0x49, 0xb0, + 0x1b, 0xf6, 0xe1, 0x03, 0xf8, 0x8b, 0x05, 0x0b, + 0xc0, 0x74, 0x15, 0x8b, 0x45, 0x0a, 0x33, 0xd2, + 0xf7, 0x36, 0xb6, 0x00, 0x03, 0x45, 0x0e, 0x3b, + 0x06, 0xb1, 0x64, 0x72, 0x03, 0xe8, 0x09, 0x00, + 0x59, 0x5f, 0x41, 0x80, 0xf9, 0x04, 0x76, 0xd4, + 0xc3, 0x8b, 0x45, 0x16, 0x8b, 0x5d, 0x0a, 0x8b, + 0x55, 0x0c, 0x8b, 0x4d, 0x0e, 0x8b, 0x75, 0x18, + 0x1e, 0x06, 0x8e, 0xc0, 0xa1, 0xb1, 0x32, 0x8e, + 0xd8, 0x8b, 0xf9, 0x8b, 0xca, 0x26, 0x8a, 0x04, + 0x3c, 0xff, 0x74, 0x02, 0x88, 0x07, 0x46, 0x43, + 0xe2, 0xf3, 0x8b, 0xcf, 0x81, 0xc3, 0x40, 0x01, + 0x2b, 0xda, 0xe2, 0xe5, 0x07, 0x1f, 0xc3, 0x80, + 0x3e, 0xdd, 0x1c, 0x02, 0x74, 0x01, 0xc3, 0xb9, + 0x01, 0x00, 0xbf, 0xc7, 0x32, 0x57, 0x51, 0x49, + 0xb0, 0x1b, 0xf6, 0xe1, 0x03, 0xf8, 0x8b, 0x05, + 0x0b, 0xc0, 0x74, 0x0f, 0x8b, 0x45, 0x04, 0x8b, + 0x5d, 0x02, 0x8b, 0x4d, 0x08, 0x8b, 0x7d, 0x06, + 0xe8, 0x09, 0x00, 0x59, 0x5f, 0x41, 0x80, 0xf9, + 0x04, 0x76, 0xda, 0xc3, 0x1e, 0x06, 0x8b, 0x16, + 0xb1, 0x32, 0x8e, 0xc2, 0x50, 0xba, 0x40, 0x01, + 0xf7, 0xe2, 0x03, 0xc3, 0x2b, 0xfb, 0x5a, 0x2b, + 0xca, 0x8b, 0xd8, 0xb8, 0x00, 0xa0, 0x8e, 0xd8, + 0x8b, 0xd1, 0x8b, 0xcf, 0x26, 0x8a, 0x07, 0x88, + 0x07, 0x43, 0xe2, 0xf8, 0x8b, 0xca, 0x81, 0xc3, + 0x40, 0x01, 0x2b, 0xdf, 0xe2, 0xea, 0x07, 0x1f, + 0xc3, 0x50, 0x32, 0xe4, 0x48, 0xbb, 0x9e, 0xd8, + 0x8b, 0x16, 0xf3, 0xb4, 0x4a, 0xc1, 0xe2, 0x02, + 0x03, 0xda, 0x03, 0xd8, 0x58, 0x88, 0x27, 0xc3, + 0x50, 0xe8, 0x18, 0xfd, 0xe8, 0x35, 0x00, 0x58, + 0x32, 0xe4, 0xbb, 0xc7, 0x32, 0xb1, 0x1b, 0x48, + 0xf6, 0xe1, 0x03, 0xd8, 0xc7, 0x07, 0x00, 0x00, + 0x53, 0xe8, 0xcc, 0x00, 0xe8, 0xb8, 0xfe, 0xe8, + 0x5e, 0x78, 0x5b, 0x53, 0xff, 0x07, 0xff, 0x36, + 0xdd, 0x1c, 0xc6, 0x06, 0xdd, 0x1c, 0x02, 0xe8, + 0x4d, 0xff, 0x8f, 0x06, 0xdd, 0x1c, 0x5b, 0xc7, + 0x07, 0x00, 0x00, 0xc3, 0x8b, 0x1e, 0x30, 0x32, + 0x8b, 0x3e, 0x2e, 0x32, 0x8b, 0x0e, 0x2c, 0x32, + 0x1e, 0x06, 0xa1, 0xb3, 0x32, 0x8e, 0xc0, 0xa1, + 0xb1, 0x32, 0x8e, 0xd8, 0x8b, 0xd1, 0x8b, 0xcf, + 0x81, 0xfb, 0xff, 0xf9, 0x77, 0x05, 0x26, 0x8a, + 0x07, 0x88, 0x07, 0x43, 0xe2, 0xf2, 0x8b, 0xca, + 0x81, 0xc3, 0x40, 0x01, 0x2b, 0xdf, 0xe2, 0xe4, + 0x07, 0x1f, 0xc3, 0x06, 0xa1, 0xbf, 0x32, 0x8e, + 0xc0, 0x33, 0xc0, 0xa3, 0x28, 0x32, 0xa3, 0x2a, + 0x32, 0xbb, 0xff, 0xff, 0x8a, 0x0e, 0x07, 0x66, + 0xb8, 0x03, 0x00, 0xf6, 0xe1, 0x03, 0xd8, 0xbe, + 0x00, 0x00, 0x26, 0x8b, 0x34, 0x8b, 0xd6, 0x3b, + 0xde, 0x72, 0x03, 0x07, 0xf9, 0xc3, 0xfe, 0x06, + 0x07, 0x66, 0x33, 0xc9, 0x26, 0x8a, 0x0f, 0x26, + 0x8b, 0x77, 0x01, 0x8b, 0xda, 0x43, 0x83, 0xeb, + 0x02, 0xd1, 0xe1, 0x03, 0xd9, 0x26, 0x8b, 0x1f, + 0x03, 0xda, 0x26, 0x8b, 0x3f, 0x26, 0x8b, 0x4f, + 0x02, 0x83, 0xc3, 0x04, 0x89, 0x1e, 0xde, 0x64, + 0x89, 0x0e, 0xe2, 0x64, 0x89, 0x3e, 0xe0, 0x64, + 0x89, 0x36, 0xe4, 0x64, 0x51, 0xe8, 0x31, 0x01, + 0x59, 0x89, 0x0e, 0x2c, 0x32, 0x89, 0x3e, 0x2e, + 0x32, 0x89, 0x36, 0x30, 0x32, 0x07, 0xf8, 0xc3, + 0x06, 0xa1, 0xbf, 0x32, 0x8e, 0xc0, 0x8b, 0x1e, + 0xe6, 0x64, 0x26, 0x8b, 0x3f, 0x43, 0x43, 0x26, + 0x8b, 0x0f, 0x43, 0x43, 0xa1, 0xb1, 0x64, 0x2d, + 0x3e, 0x00, 0xba, 0x40, 0x01, 0xf7, 0xe2, 0x03, + 0x06, 0xaf, 0x64, 0x8b, 0xd7, 0xd1, 0xea, 0x52, + 0x2b, 0xc2, 0x8b, 0xf0, 0x26, 0x8b, 0x07, 0x03, + 0xf0, 0x33, 0xd2, 0xf7, 0x36, 0xb6, 0x00, 0x89, + 0x16, 0x28, 0x32, 0xa3, 0x2a, 0x32, 0x5a, 0x43, + 0x43, 0x89, 0x1e, 0xde, 0x64, 0x89, 0x0e, 0xe2, + 0x64, 0x89, 0x3e, 0xe0, 0x64, 0x89, 0x36, 0xe4, + 0x64, 0x51, 0x57, 0x56, 0x51, 0x52, 0xe8, 0xc8, + 0x00, 0x5a, 0x59, 0xc6, 0x06, 0xae, 0x64, 0x00, + 0xc7, 0x06, 0xba, 0x00, 0x3f, 0x01, 0xc7, 0x06, + 0xb8, 0x00, 0x00, 0x00, 0xc7, 0x06, 0xbc, 0x00, + 0x00, 0x00, 0xc7, 0x06, 0xbe, 0x00, 0xc7, 0x00, + 0xa1, 0xaf, 0x64, 0x2b, 0xc2, 0x72, 0x09, 0x03, + 0x06, 0x28, 0x32, 0x3d, 0x00, 0x00, 0x73, 0x1b, + 0xc6, 0x06, 0xae, 0x64, 0x01, 0xa1, 0xaf, 0x64, + 0x03, 0xc2, 0x03, 0x06, 0x28, 0x32, 0x8b, 0x1e, + 0x34, 0x32, 0x3b, 0xc3, 0x76, 0x02, 0x8b, 0xc3, + 0xa3, 0xba, 0x00, 0xa1, 0xaf, 0x64, 0x03, 0xc2, + 0x03, 0x06, 0x28, 0x32, 0x3d, 0x3f, 0x01, 0x76, + 0x16, 0xc6, 0x06, 0xae, 0x64, 0x01, 0x2b, 0xc2, + 0x2b, 0xc2, 0x8b, 0x1e, 0x32, 0x32, 0x3b, 0xc3, + 0x76, 0x02, 0x8b, 0xc3, 0xa3, 0xb8, 0x00, 0xa1, + 0xb1, 0x64, 0x2b, 0xc1, 0x72, 0x09, 0x03, 0x06, + 0x2a, 0x32, 0x3d, 0x00, 0x00, 0x73, 0x19, 0xc6, + 0x06, 0xae, 0x64, 0x01, 0xa1, 0xb1, 0x64, 0x03, + 0x06, 0x2a, 0x32, 0x8b, 0x1e, 0x3c, 0x32, 0x3b, + 0xc3, 0x76, 0x02, 0x8b, 0xc3, 0xa3, 0xbe, 0x00, + 0xa1, 0xb1, 0x64, 0x03, 0x06, 0x2a, 0x32, 0x3d, + 0xc7, 0x00, 0x76, 0x14, 0xc6, 0x06, 0xae, 0x64, + 0x01, 0x2b, 0xc1, 0x8b, 0x1e, 0x3a, 0x32, 0x3b, + 0xc3, 0x73, 0x02, 0x8b, 0xc3, 0xa3, 0xbc, 0x00, + 0x5e, 0x5f, 0x59, 0x89, 0x0e, 0x2c, 0x32, 0x89, + 0x3e, 0x2e, 0x32, 0x89, 0x36, 0x30, 0x32, 0x07, + 0xc3, 0x33, 0xd2, 0x8b, 0xc6, 0xf7, 0x36, 0xb6, + 0x00, 0x89, 0x16, 0x32, 0x32, 0xa3, 0x3a, 0x32, + 0x03, 0xd7, 0x03, 0xc1, 0x89, 0x16, 0x34, 0x32, + 0xa3, 0x3c, 0x32, 0x33, 0xd2, 0xa1, 0x30, 0x32, + 0xf7, 0x36, 0xb6, 0x00, 0x89, 0x16, 0x36, 0x32, + 0xa3, 0x3e, 0x32, 0x03, 0x16, 0x2e, 0x32, 0x03, + 0x06, 0x2c, 0x32, 0x89, 0x16, 0x38, 0x32, 0xa3, + 0x40, 0x32, 0xa1, 0x32, 0x32, 0x8b, 0x1e, 0x36, + 0x32, 0x3b, 0xc3, 0x76, 0x04, 0x89, 0x1e, 0x32, + 0x32, 0xa1, 0x34, 0x32, 0x8b, 0x1e, 0x38, 0x32, + 0x3b, 0xc3, 0x77, 0x04, 0x89, 0x1e, 0x34, 0x32, + 0xa1, 0x3a, 0x32, 0x8b, 0x1e, 0x3e, 0x32, 0x3b, + 0xc3, 0x76, 0x04, 0x89, 0x1e, 0x3a, 0x32, 0xa1, + 0x3c, 0x32, 0x8b, 0x1e, 0x40, 0x32, 0x3b, 0xc3, + 0x77, 0x04, 0x89, 0x1e, 0x3c, 0x32, 0xc3, 0x55, + 0x1e, 0x06, 0xe8, 0x04, 0x00, 0x07, 0x1f, 0x5d, + 0xc3, 0x8b, 0x1e, 0xde, 0x64, 0x8b, 0x0e, 0xe2, + 0x64, 0x8b, 0x3e, 0xe0, 0x64, 0x8b, 0x36, 0xe4, + 0x64, 0xa1, 0xbf, 0x32, 0x8e, 0xc0, 0x8b, 0xc1, + 0x03, 0xdf, 0x81, 0xc6, 0x40, 0x01, 0xe2, 0xf8, + 0x8b, 0xc8, 0x81, 0xee, 0x40, 0x01, 0x03, 0xf7, + 0x4e, 0x4b, 0x8b, 0xc7, 0xf7, 0x26, 0xd8, 0x64, + 0x33, 0xd2, 0xf7, 0x36, 0xda, 0x64, 0xd1, 0xe8, + 0x2b, 0xf0, 0xa0, 0xae, 0x64, 0x8a, 0x26, 0xdc, + 0x64, 0x8a, 0x16, 0xcb, 0x64, 0x8b, 0x2e, 0xb1, + 0x32, 0x8e, 0xdd, 0xbd, 0x00, 0xfa, 0x0a, 0xc0, + 0x74, 0x03, 0xe9, 0xa8, 0x00, 0x0a, 0xe4, 0x74, + 0x56, 0x80, 0xfa, 0x01, 0x74, 0x51, 0x3e, 0x8b, + 0x56, 0x00, 0x3e, 0x03, 0x56, 0x02, 0x43, 0x2b, + 0xdf, 0x51, 0x56, 0x8b, 0xcf, 0x83, 0xea, 0x64, + 0x77, 0x0c, 0x3e, 0x03, 0x56, 0x00, 0x3e, 0x03, + 0x56, 0x02, 0x5e, 0x59, 0xeb, 0x2e, 0x52, 0x3e, + 0x8b, 0x56, 0x00, 0x3e, 0x03, 0x56, 0x02, 0x83, + 0xea, 0x64, 0x77, 0x0a, 0x3e, 0x03, 0x56, 0x00, + 0x3e, 0x03, 0x56, 0x02, 0xeb, 0x0a, 0x26, 0x8a, + 0x07, 0x3c, 0xff, 0x74, 0x02, 0x88, 0x04, 0x4e, + 0x43, 0xe2, 0xe4, 0x5a, 0x5e, 0x59, 0x81, 0xee, + 0x40, 0x01, 0x2b, 0xdf, 0xe2, 0xb9, 0xc3, 0x3e, + 0x8b, 0x56, 0x00, 0x3e, 0x03, 0x56, 0x02, 0x51, + 0x56, 0x8b, 0xcf, 0x83, 0xea, 0x64, 0x77, 0x0e, + 0x3e, 0x03, 0x56, 0x00, 0x3e, 0x03, 0x56, 0x02, + 0x2b, 0xd9, 0x5e, 0x59, 0xeb, 0x2c, 0x52, 0x3e, + 0x8b, 0x56, 0x00, 0x3e, 0x03, 0x56, 0x02, 0x83, + 0xea, 0x64, 0x77, 0x0a, 0x3e, 0x03, 0x56, 0x00, + 0x3e, 0x03, 0x56, 0x02, 0xeb, 0x0a, 0x26, 0x8a, + 0x07, 0x3c, 0xff, 0x74, 0x02, 0x88, 0x04, 0x4e, + 0x4b, 0xe2, 0xe4, 0x5a, 0x5e, 0x59, 0x81, 0xee, + 0x40, 0x01, 0xe2, 0xbb, 0xc3, 0x0a, 0xe4, 0x75, + 0x03, 0xe9, 0x8d, 0x00, 0x80, 0xfa, 0x01, 0x75, + 0x03, 0xe9, 0x85, 0x00, 0xe8, 0x04, 0x01, 0x43, + 0xba, 0x3b, 0x0b, 0x8e, 0xda, 0x8b, 0x16, 0xb1, + 0x32, 0x8e, 0xda, 0x2b, 0xdf, 0x51, 0x56, 0x8b, + 0xcf, 0x3e, 0x83, 0x6e, 0x04, 0x64, 0x77, 0x07, + 0xe8, 0xf5, 0x00, 0x5e, 0x59, 0xeb, 0x5f, 0x3e, + 0xff, 0x76, 0x04, 0xe8, 0xdd, 0x00, 0x1e, 0x3e, + 0x83, 0x6e, 0x04, 0x64, 0x77, 0x05, 0xe8, 0xdf, + 0x00, 0xeb, 0x3b, 0x26, 0x8a, 0x07, 0x3c, 0xff, + 0x74, 0x33, 0xba, 0x3b, 0x0b, 0x8e, 0xda, 0xa2, + 0x27, 0x32, 0x8b, 0xc6, 0x33, 0xd2, 0xf7, 0x36, + 0xb6, 0x00, 0x3b, 0x16, 0xba, 0x00, 0x77, 0x1d, + 0x3b, 0x16, 0xb8, 0x00, 0x72, 0x17, 0x3b, 0x06, + 0xbe, 0x00, 0x77, 0x11, 0x3b, 0x06, 0xbc, 0x00, + 0x72, 0x0b, 0xa0, 0x27, 0x32, 0x8b, 0x16, 0xb1, + 0x32, 0x8e, 0xda, 0x88, 0x04, 0x4e, 0x43, 0x1f, + 0xe2, 0xb4, 0x3e, 0x8f, 0x46, 0x04, 0x5e, 0x59, + 0x81, 0xee, 0x40, 0x01, 0x2b, 0xdf, 0xe2, 0x80, + 0xc3, 0xe8, 0x7f, 0x00, 0xba, 0x3b, 0x0b, 0x8e, + 0xda, 0x8b, 0x16, 0xb1, 0x32, 0x8e, 0xda, 0x51, + 0x56, 0x8b, 0xcf, 0x3e, 0x83, 0x6e, 0x04, 0x64, + 0x77, 0x09, 0xe8, 0x73, 0x00, 0x2b, 0xd9, 0x5e, + 0x59, 0xeb, 0x5d, 0x3e, 0xff, 0x76, 0x04, 0xe8, + 0x59, 0x00, 0x1e, 0x3e, 0x83, 0x6e, 0x04, 0x64, + 0x77, 0x05, 0xe8, 0x5b, 0x00, 0xeb, 0x3b, 0x26, + 0x8a, 0x07, 0x3c, 0xff, 0x74, 0x33, 0xba, 0x3b, + 0x0b, 0x8e, 0xda, 0xa2, 0x27, 0x32, 0x8b, 0xc6, + 0x33, 0xd2, 0xf7, 0x36, 0xb6, 0x00, 0x3b, 0x16, + 0xba, 0x00, 0x77, 0x1d, 0x3b, 0x16, 0xb8, 0x00, + 0x72, 0x17, 0x3b, 0x06, 0xbe, 0x00, 0x77, 0x11, + 0x3b, 0x06, 0xbc, 0x00, 0x72, 0x0b, 0xa0, 0x27, + 0x32, 0x8b, 0x16, 0xb1, 0x32, 0x8e, 0xda, 0x88, + 0x04, 0x4e, 0x4b, 0x1f, 0xe2, 0xb4, 0x3e, 0x8f, + 0x46, 0x04, 0x5e, 0x59, 0x81, 0xee, 0x40, 0x01, + 0xe2, 0x82, 0xc3, 0x3e, 0x8b, 0x56, 0x00, 0x3e, + 0x03, 0x56, 0x02, 0x3e, 0x89, 0x56, 0x04, 0xc3, + 0x3e, 0x8b, 0x56, 0x00, 0x3e, 0x03, 0x56, 0x02, + 0x3e, 0x01, 0x56, 0x04, 0xc3, 0x06, 0xa1, 0xb5, + 0x32, 0x8e, 0xc0, 0x33, 0xdb, 0x26, 0x8a, 0x0f, + 0xb5, 0x00, 0x43, 0x51, 0x0a, 0xc9, 0x75, 0x03, + 0xe9, 0xfb, 0x00, 0x53, 0x26, 0x8b, 0x1f, 0x26, + 0x8b, 0x47, 0x04, 0x33, 0xd2, 0xf7, 0x36, 0xb6, + 0x00, 0x26, 0x8b, 0x37, 0x03, 0xf2, 0x4e, 0x26, + 0x8b, 0x7f, 0x02, 0x03, 0xf8, 0x4f, 0xe8, 0x10, + 0x0c, 0x72, 0x03, 0xe9, 0xd0, 0x00, 0x53, 0x8a, + 0x1e, 0xae, 0x64, 0x89, 0x16, 0xb7, 0x32, 0x8b, + 0x0e, 0x32, 0x32, 0x0a, 0xdb, 0x74, 0x04, 0x8b, + 0x0e, 0xb8, 0x00, 0x3b, 0xca, 0x72, 0x04, 0x89, + 0x0e, 0xb7, 0x32, 0xa3, 0xb9, 0x32, 0x8b, 0x0e, + 0x3a, 0x32, 0x0a, 0xdb, 0x74, 0x04, 0x8b, 0x0e, + 0xbc, 0x00, 0x3b, 0xc8, 0x72, 0x04, 0x89, 0x0e, + 0xb9, 0x32, 0x89, 0x36, 0xbb, 0x32, 0x8b, 0x0e, + 0x34, 0x32, 0x0a, 0xdb, 0x74, 0x04, 0x8b, 0x0e, + 0xba, 0x00, 0x3b, 0xce, 0x77, 0x04, 0x89, 0x0e, + 0xbb, 0x32, 0x89, 0x3e, 0xbd, 0x32, 0x8b, 0x0e, + 0x3c, 0x32, 0x0a, 0xdb, 0x74, 0x04, 0x8b, 0x0e, + 0xbe, 0x00, 0x3b, 0xcf, 0x77, 0x04, 0x89, 0x0e, + 0xbd, 0x32, 0x5b, 0x26, 0x8b, 0x3f, 0x83, 0xc3, + 0x06, 0x8b, 0xc8, 0x3b, 0x0e, 0xb9, 0x32, 0x73, + 0x05, 0x03, 0xdf, 0x41, 0xeb, 0xf5, 0x8b, 0xca, + 0x3b, 0x0e, 0xb7, 0x32, 0x73, 0x04, 0x43, 0x41, + 0xeb, 0xf6, 0xa1, 0xb9, 0x32, 0xf7, 0x26, 0xb6, + 0x00, 0x03, 0x06, 0xb7, 0x32, 0x8b, 0xf0, 0x55, + 0xa1, 0xbd, 0x32, 0x8b, 0x0e, 0xb9, 0x32, 0x8b, + 0x16, 0xbb, 0x32, 0x8b, 0x2e, 0xb7, 0x32, 0x1e, + 0x50, 0xa1, 0xb1, 0x32, 0x8e, 0xd8, 0x58, 0x50, + 0x53, 0x51, 0x56, 0x8b, 0xcd, 0x26, 0x8a, 0x07, + 0x3c, 0xff, 0x74, 0x02, 0x88, 0x04, 0x46, 0x43, + 0x41, 0x3b, 0xca, 0x76, 0xf0, 0x5e, 0x59, 0x5b, + 0x58, 0x81, 0xc6, 0x40, 0x01, 0x03, 0xdf, 0x41, + 0x3b, 0xc8, 0x76, 0xdb, 0x1f, 0x5d, 0x5b, 0x43, + 0x43, 0x59, 0x49, 0xe9, 0xfd, 0xfe, 0x59, 0x07, + 0xc3, 0xe8, 0x7d, 0x73, 0xa0, 0xdc, 0x1c, 0x3c, + 0x02, 0x74, 0x01, 0xc3, 0x1e, 0x06, 0xa1, 0xb1, + 0x32, 0x8e, 0xc0, 0xa1, 0x3a, 0x32, 0x8b, 0x1e, + 0x32, 0x32, 0x8b, 0x0e, 0x3c, 0x32, 0x8b, 0x3e, + 0x34, 0x32, 0xba, 0x40, 0x01, 0xf7, 0xe2, 0x03, + 0xc3, 0x2b, 0xfb, 0x2b, 0x0e, 0x3a, 0x32, 0x8b, + 0xd8, 0xb8, 0x00, 0xa0, 0x8e, 0xd8, 0x8b, 0xd1, + 0x8b, 0xcf, 0x81, 0xfb, 0xff, 0xf9, 0x77, 0x05, + 0x26, 0x8a, 0x07, 0x88, 0x07, 0x43, 0xe2, 0xf2, + 0x8b, 0xca, 0x81, 0xc3, 0x40, 0x01, 0x2b, 0xdf, + 0xe2, 0xe4, 0x07, 0x1f, 0xc3, 0x80, 0x3e, 0x3d, + 0x66, 0x02, 0x74, 0x01, 0xc3, 0xbb, 0xce, 0xb5, + 0xe8, 0x1f, 0x0b, 0x8b, 0x0e, 0x50, 0x72, 0x49, + 0xd1, 0xe1, 0x03, 0xd9, 0x83, 0x3f, 0x00, 0x74, + 0x04, 0xff, 0x17, 0xeb, 0x03, 0xe8, 0x68, 0x6b, + 0xc6, 0x06, 0x3d, 0x66, 0x00, 0xc7, 0x06, 0x50, + 0x72, 0x00, 0x00, 0xc7, 0x06, 0x52, 0x72, 0x00, + 0x00, 0xe8, 0x6c, 0x00, 0xc3, 0x80, 0x3e, 0x3d, + 0x66, 0x04, 0x74, 0x01, 0xc3, 0xe8, 0xed, 0x70, + 0xbb, 0x9c, 0xb8, 0xe8, 0xe4, 0x0a, 0x8b, 0x0e, + 0x50, 0x72, 0x49, 0xd1, 0xe1, 0x03, 0xd9, 0xff, + 0x17, 0xe8, 0xfb, 0x70, 0xc6, 0x06, 0x3d, 0x66, + 0x00, 0xc7, 0x06, 0x50, 0x72, 0x00, 0x00, 0xc7, + 0x06, 0x52, 0x72, 0xff, 0x00, 0xe8, 0x38, 0x00, + 0xc3, 0xa1, 0xaf, 0x64, 0x3b, 0x06, 0xbb, 0x64, + 0x74, 0x04, 0xe8, 0x2b, 0x00, 0xc3, 0xa1, 0xb1, + 0x64, 0x3b, 0x06, 0xbd, 0x64, 0x74, 0x04, 0xe8, + 0x1e, 0x00, 0xc3, 0xbb, 0x9c, 0x32, 0x8b, 0x07, + 0x0b, 0x47, 0x02, 0x75, 0x06, 0xb9, 0xf4, 0x01, + 0xe8, 0xa0, 0x73, 0xe8, 0xb0, 0x73, 0x72, 0x01, + 0xc3, 0xe8, 0x0f, 0x00, 0xe8, 0x01, 0x00, 0xc3, + 0xbb, 0x9c, 0x32, 0x33, 0xc0, 0x89, 0x07, 0x89, + 0x47, 0x02, 0xc3, 0xa1, 0xbf, 0x32, 0xa3, 0xac, + 0x00, 0x33, 0xc0, 0xa3, 0xaa, 0x00, 0xb9, 0x02, + 0x00, 0xba, 0x54, 0x00, 0xe8, 0x01, 0x7a, 0xbb, + 0x8a, 0x32, 0x33, 0xc0, 0x89, 0x07, 0x89, 0x47, + 0x02, 0xc6, 0x06, 0xcd, 0x64, 0x01, 0xc6, 0x06, + 0xcb, 0x64, 0x01, 0xc6, 0x06, 0x3f, 0x65, 0x01, + 0xe8, 0x22, 0x08, 0x83, 0x3e, 0xc2, 0x00, 0x00, + 0x74, 0x05, 0xe8, 0xb5, 0xea, 0xeb, 0x15, 0xbb, + 0x98, 0x32, 0x8b, 0x07, 0x0b, 0x47, 0x02, 0x75, + 0x06, 0xb9, 0x32, 0x00, 0xe8, 0x3c, 0x73, 0xe8, + 0x4c, 0x73, 0x72, 0x21, 0xe8, 0x32, 0x00, 0xe8, + 0xd3, 0xf5, 0xe8, 0xf1, 0xf2, 0xe8, 0x04, 0xd3, + 0x80, 0x3e, 0x95, 0x60, 0x01, 0x74, 0x0e, 0x80, + 0x3e, 0xca, 0x00, 0x01, 0x74, 0x07, 0x80, 0x3e, + 0xcb, 0x00, 0x01, 0x75, 0xbe, 0xe8, 0xc3, 0x75, + 0xe8, 0xa6, 0x08, 0xc6, 0x06, 0xdc, 0x1c, 0x02, + 0xe8, 0x9b, 0xf2, 0xc6, 0x06, 0xdc, 0x1c, 0x03, + 0xc3, 0x06, 0xbb, 0x8a, 0x32, 0xe8, 0x0e, 0x73, + 0x73, 0x1a, 0x8b, 0x0e, 0x90, 0x32, 0xe8, 0xf2, + 0x72, 0xe8, 0x1a, 0x06, 0xe8, 0x8d, 0x07, 0xc6, + 0x06, 0xdc, 0x1c, 0x02, 0xe8, 0x77, 0xf2, 0xc6, + 0x06, 0xdc, 0x1c, 0x03, 0x07, 0xc3, 0x06, 0xbb, + 0x8a, 0x32, 0xe8, 0xe9, 0x72, 0x73, 0x1a, 0x8b, + 0x0e, 0x90, 0x32, 0xe8, 0xcd, 0x72, 0xe8, 0xf5, + 0x05, 0xe8, 0xa4, 0x07, 0xc6, 0x06, 0xdc, 0x1c, + 0x02, 0xe8, 0x52, 0xf2, 0xc6, 0x06, 0xdc, 0x1c, + 0x03, 0x07, 0xc3, 0xe8, 0xcc, 0x6d, 0xe8, 0x54, + 0xf5, 0xe8, 0x1c, 0x00, 0x80, 0x3e, 0xc6, 0x64, + 0x00, 0x75, 0xf3, 0xc3, 0xe8, 0xbb, 0x6d, 0xe8, + 0x43, 0xf5, 0xe8, 0x0b, 0x00, 0xe8, 0x5e, 0xf2, + 0x80, 0x3e, 0xc6, 0x64, 0x00, 0x75, 0xf0, 0xc3, + 0x06, 0xa0, 0xc6, 0x64, 0x3c, 0x00, 0x75, 0x03, + 0xe9, 0xc3, 0x00, 0x3c, 0x03, 0x74, 0x39, 0xe8, + 0x0c, 0x02, 0x73, 0x0d, 0xe8, 0x84, 0x01, 0x72, + 0xf6, 0xc6, 0x06, 0xc6, 0x64, 0x00, 0xe9, 0xad, + 0x00, 0xc6, 0x06, 0xc4, 0x64, 0x00, 0xc6, 0x06, + 0xc6, 0x64, 0x03, 0xe8, 0x22, 0x08, 0x73, 0x0e, + 0xc6, 0x06, 0xc6, 0x64, 0x02, 0xc6, 0x06, 0xe2, + 0x65, 0x01, 0xff, 0x06, 0xce, 0x64, 0xbb, 0x8a, + 0x32, 0x33, 0xc0, 0x89, 0x07, 0x89, 0x47, 0x02, + 0xbb, 0x8a, 0x32, 0xe8, 0x58, 0x72, 0x72, 0x02, + 0xeb, 0x7c, 0x8b, 0x0e, 0x8e, 0x32, 0xe8, 0x3a, + 0x72, 0xe8, 0xbd, 0x05, 0xe8, 0x5f, 0x05, 0x72, + 0xae, 0xe8, 0xb0, 0x02, 0x72, 0xa9, 0xe8, 0x6f, + 0x06, 0xc6, 0x06, 0xc6, 0x64, 0x03, 0xff, 0x0e, + 0xce, 0x64, 0x7f, 0x4d, 0x80, 0x3e, 0xc5, 0x64, + 0x01, 0x75, 0x2e, 0x8b, 0x3e, 0xb7, 0x64, 0x8b, + 0x36, 0xb9, 0x64, 0x8b, 0x0e, 0xbb, 0x64, 0x8b, + 0x16, 0xbd, 0x64, 0x3b, 0xf9, 0x75, 0x04, 0x3b, + 0xf2, 0x74, 0x16, 0xe8, 0x3a, 0x00, 0x89, 0x3e, + 0xaf, 0x64, 0x89, 0x36, 0xb1, 0x64, 0x89, 0x0e, + 0xb7, 0x64, 0x89, 0x16, 0xb9, 0x64, 0xe9, 0x66, + 0xff, 0xe8, 0xc6, 0x08, 0xe8, 0x7c, 0x00, 0xe8, + 0x77, 0x07, 0xa1, 0xb7, 0x64, 0xa3, 0xaf, 0x64, + 0xa1, 0xb9, 0x64, 0xa3, 0xb1, 0x64, 0xe8, 0xb4, + 0x00, 0xc6, 0x06, 0xdc, 0x1c, 0x02, 0xe8, 0x5d, + 0xf1, 0xc6, 0x06, 0xdc, 0x1c, 0x03, 0x07, 0xc3, + 0x8b, 0x1e, 0x44, 0x67, 0x43, 0x43, 0xa0, 0x40, + 0x67, 0x3c, 0x00, 0x75, 0x1b, 0x3b, 0xd6, 0x76, + 0x0e, 0x8b, 0xd6, 0x3b, 0x3f, 0x77, 0x09, 0x8b, + 0x4f, 0x04, 0xc6, 0x06, 0x40, 0x67, 0x01, 0xc3, + 0x8b, 0x0f, 0xc6, 0x06, 0x40, 0x67, 0x03, 0xc3, + 0x3c, 0x02, 0x75, 0x05, 0x3b, 0xd6, 0x72, 0xe1, + 0xc3, 0x3c, 0x01, 0x75, 0x1d, 0x3b, 0xcf, 0x73, + 0xe6, 0x8b, 0xcf, 0x3b, 0x77, 0x02, 0x77, 0x09, + 0x8b, 0x57, 0x06, 0xc6, 0x06, 0x40, 0x67, 0x02, + 0xc3, 0x8b, 0x57, 0x02, 0xc6, 0x06, 0x40, 0x67, + 0x00, 0xc3, 0x3c, 0x03, 0x75, 0xc9, 0x3b, 0xcf, + 0x77, 0xdf, 0xc3, 0xa0, 0xc3, 0x64, 0x0a, 0xc0, + 0x74, 0x42, 0x3c, 0x01, 0x75, 0x0a, 0xc6, 0x06, + 0xcb, 0x64, 0x01, 0xc6, 0x06, 0xcd, 0x64, 0x00, + 0x3c, 0x03, 0x75, 0x0a, 0xc6, 0x06, 0xcb, 0x64, + 0x01, 0xc6, 0x06, 0xcd, 0x64, 0x01, 0x3c, 0x02, + 0x75, 0x0f, 0xc6, 0x06, 0xcb, 0x64, 0x00, 0xc6, + 0x06, 0xcc, 0x64, 0x01, 0xc6, 0x06, 0xdc, 0x64, + 0x00, 0x3c, 0x04, 0x75, 0x0f, 0xc6, 0x06, 0xcb, + 0x64, 0x00, 0xc6, 0x06, 0xcc, 0x64, 0x00, 0xc6, + 0x06, 0xdc, 0x64, 0x01, 0xc3, 0xc6, 0x06, 0xc3, + 0x64, 0x00, 0xa0, 0x3d, 0x66, 0x3c, 0x01, 0x75, + 0x06, 0xc6, 0x06, 0x3d, 0x66, 0x02, 0xc3, 0x3c, + 0x03, 0x75, 0x06, 0xc6, 0x06, 0x3d, 0x66, 0x04, + 0xc3, 0x3c, 0x05, 0x75, 0x05, 0xc6, 0x06, 0x3d, + 0x66, 0x06, 0xc3, 0x8a, 0x0e, 0xc3, 0x64, 0x0a, + 0xc9, 0x74, 0x69, 0xa0, 0xcc, 0x64, 0x8a, 0x26, + 0xcd, 0x64, 0x8a, 0x2e, 0xcb, 0x64, 0x80, 0xf9, + 0x01, 0x75, 0x13, 0x80, 0xfc, 0x00, 0x75, 0x05, + 0x80, 0xfd, 0x01, 0x74, 0x4f, 0xe8, 0x51, 0x00, + 0xff, 0x06, 0xb1, 0x64, 0xf9, 0xc3, 0x80, 0xf9, + 0x03, 0x75, 0x13, 0x80, 0xfc, 0x01, 0x75, 0x05, + 0x80, 0xfd, 0x01, 0x74, 0x37, 0xe8, 0x39, 0x00, + 0xff, 0x0e, 0xb1, 0x64, 0xf9, 0xc3, 0x80, 0xf9, + 0x02, 0x75, 0x12, 0x3c, 0x01, 0x75, 0x05, 0x80, + 0xfd, 0x00, 0x74, 0x20, 0xe8, 0x22, 0x00, 0xff, + 0x0e, 0xaf, 0x64, 0xf9, 0xc3, 0x80, 0xf9, 0x04, + 0x75, 0x12, 0x3c, 0x00, 0x75, 0x05, 0x80, 0xfd, + 0x00, 0x74, 0x09, 0xe8, 0x0b, 0x00, 0xff, 0x06, + 0xaf, 0x64, 0xf9, 0xc3, 0xe8, 0x66, 0xff, 0xf8, + 0xc3, 0xa1, 0xaf, 0x64, 0xa3, 0xb7, 0x64, 0xa1, + 0xb1, 0x64, 0xa3, 0xb9, 0x64, 0xc3, 0x33, 0xc9, + 0xa1, 0xb7, 0x64, 0x8b, 0x1e, 0xaf, 0x64, 0x3b, + 0xc3, 0x77, 0x03, 0xfe, 0xc1, 0x93, 0x2b, 0xc3, + 0xa3, 0xc7, 0x64, 0xa1, 0xb9, 0x64, 0x8b, 0x1e, + 0xb1, 0x64, 0x3b, 0xc3, 0x77, 0x03, 0xfe, 0xc5, + 0x93, 0x2b, 0xc3, 0xa3, 0xc9, 0x64, 0x8b, 0x1e, + 0xc7, 0x64, 0xa1, 0xc9, 0x64, 0x0b, 0xdb, 0x74, + 0x0d, 0xc6, 0x06, 0xcc, 0x64, 0x01, 0x0a, 0xc9, + 0x74, 0x04, 0xfe, 0x0e, 0xcc, 0x64, 0x0b, 0xc0, + 0x74, 0x0d, 0xc6, 0x06, 0xcd, 0x64, 0x01, 0x0a, + 0xed, 0x74, 0x04, 0xfe, 0x0e, 0xcd, 0x64, 0x8b, + 0xd0, 0x0b, 0xc0, 0x0b, 0xc3, 0x75, 0x02, 0xf9, + 0xc3, 0x8b, 0xc2, 0xb9, 0x10, 0x00, 0xf7, 0xe1, + 0xb9, 0x0a, 0x00, 0xf7, 0xf1, 0x3b, 0xc3, 0x77, + 0x3e, 0xa0, 0xdc, 0x64, 0xa2, 0xdd, 0x64, 0xc6, + 0x06, 0xdc, 0x64, 0x00, 0xc6, 0x06, 0xcb, 0x64, + 0x00, 0x80, 0x3e, 0xcc, 0x64, 0x01, 0x74, 0x05, + 0xc6, 0x06, 0xdc, 0x64, 0x01, 0xa1, 0xc7, 0x64, + 0x8b, 0x1e, 0xb5, 0x64, 0x33, 0xd2, 0xf7, 0xf3, + 0xa3, 0xce, 0x64, 0xa1, 0xb5, 0x64, 0x8b, 0x1e, + 0xc9, 0x64, 0xb9, 0xe8, 0x03, 0xf7, 0xe3, 0xf7, + 0xe1, 0x8b, 0x1e, 0xc7, 0x64, 0xeb, 0x25, 0xc6, + 0x06, 0xcb, 0x64, 0x01, 0xa1, 0xc9, 0x64, 0x8b, + 0x1e, 0xb3, 0x64, 0x33, 0xd2, 0xf7, 0xf3, 0xa3, + 0xce, 0x64, 0xa1, 0xb3, 0x64, 0x8b, 0x1e, 0xc7, + 0x64, 0xb9, 0xe8, 0x03, 0xf7, 0xe3, 0xf7, 0xe1, + 0x8b, 0x1e, 0xc9, 0x64, 0xf7, 0xf3, 0xbb, 0xd0, + 0x64, 0x89, 0x07, 0xc7, 0x47, 0x02, 0x00, 0x00, + 0xbb, 0xd4, 0x64, 0x89, 0x07, 0xc7, 0x47, 0x02, + 0x00, 0x00, 0x80, 0x3e, 0xc4, 0x64, 0x01, 0x75, + 0x05, 0xc6, 0x06, 0xe2, 0x65, 0x01, 0xff, 0x06, + 0xce, 0x64, 0xf8, 0xc3, 0x83, 0x3e, 0xce, 0x64, + 0x01, 0x75, 0x02, 0xf8, 0xc3, 0x8b, 0x36, 0xaf, + 0x64, 0x8b, 0x3e, 0xb1, 0x64, 0xe8, 0x2e, 0x02, + 0x72, 0x01, 0xc3, 0x8b, 0x36, 0x44, 0x67, 0x3b, + 0xde, 0x74, 0x0f, 0x89, 0x1e, 0x44, 0x67, 0x8a, + 0x04, 0x3a, 0x07, 0x74, 0x05, 0xc6, 0x06, 0x40, + 0x67, 0xff, 0xc6, 0x06, 0xc5, 0x64, 0x01, 0xa0, + 0xcc, 0x64, 0x8a, 0x26, 0xcd, 0x64, 0x0a, 0xe4, + 0x74, 0x15, 0xc6, 0x06, 0x42, 0x67, 0x01, 0xc6, + 0x06, 0x43, 0x67, 0x01, 0x0a, 0xc0, 0x74, 0x1a, + 0xc6, 0x06, 0x43, 0x67, 0x00, 0xeb, 0x13, 0xc6, + 0x06, 0x42, 0x67, 0x00, 0xc6, 0x06, 0x43, 0x67, + 0x01, 0x0a, 0xc0, 0x74, 0x05, 0xc6, 0x06, 0x43, + 0x67, 0x00, 0x43, 0x43, 0x53, 0x8b, 0x0f, 0x41, + 0x8b, 0x7f, 0x02, 0x47, 0x8b, 0x77, 0x04, 0x4e, + 0x8b, 0x5f, 0x06, 0x4b, 0xa1, 0xbf, 0x64, 0x8b, + 0x16, 0xc1, 0x64, 0x3b, 0xc1, 0x72, 0x17, 0x3b, + 0xc6, 0x77, 0x13, 0xc6, 0x06, 0x41, 0x67, 0x00, + 0x80, 0x3e, 0x42, 0x67, 0x01, 0x74, 0x4f, 0xc6, + 0x06, 0x41, 0x67, 0x02, 0xeb, 0x48, 0x3b, 0xd7, + 0x72, 0x17, 0x3b, 0xd3, 0x77, 0x13, 0xc6, 0x06, + 0x41, 0x67, 0x01, 0x80, 0x3e, 0x43, 0x67, 0x01, + 0x74, 0x34, 0xc6, 0x06, 0x41, 0x67, 0x03, 0xeb, + 0x2d, 0x80, 0x3e, 0xcb, 0x64, 0x00, 0x75, 0x13, + 0xc6, 0x06, 0x41, 0x67, 0x01, 0x80, 0x3e, 0x43, + 0x67, 0x01, 0x74, 0x1a, 0xc6, 0x06, 0x41, 0x67, + 0x03, 0xeb, 0x13, 0xc6, 0x06, 0x41, 0x67, 0x00, + 0x80, 0x3e, 0x42, 0x67, 0x01, 0x74, 0x07, 0xc6, + 0x06, 0x41, 0x67, 0x02, 0xeb, 0x00, 0x5b, 0x53, + 0x83, 0xc3, 0x08, 0xa0, 0x41, 0x67, 0xb4, 0x00, + 0x03, 0xd8, 0x8a, 0x07, 0x5b, 0x8a, 0x36, 0x40, + 0x67, 0x0a, 0xc0, 0x74, 0x18, 0x3c, 0x01, 0x74, + 0x35, 0x3c, 0x02, 0x74, 0x56, 0x3c, 0x03, 0x74, + 0x77, 0x3c, 0x04, 0x75, 0x03, 0xe9, 0x95, 0x00, + 0xb0, 0x34, 0xe9, 0xf3, 0x78, 0xa0, 0xcd, 0x64, + 0x8a, 0x26, 0xcc, 0x64, 0x8a, 0x16, 0x41, 0x67, + 0x80, 0xfa, 0x01, 0x74, 0x05, 0x80, 0xfa, 0x03, + 0x75, 0x06, 0x0a, 0xc0, 0x74, 0x08, 0xeb, 0x50, + 0x0a, 0xe4, 0x75, 0x27, 0xeb, 0x6f, 0x80, 0xfe, + 0x02, 0x75, 0x09, 0x80, 0x3e, 0xcc, 0x64, 0x01, + 0x74, 0x19, 0xeb, 0x61, 0x80, 0xfe, 0x00, 0x74, + 0xf2, 0xa1, 0xbf, 0x64, 0xa3, 0xb7, 0x64, 0x83, + 0xc3, 0x02, 0x8b, 0x07, 0xa3, 0xb9, 0x64, 0xb0, + 0x00, 0xeb, 0x6a, 0x80, 0xfe, 0x03, 0x75, 0x09, + 0x80, 0x3e, 0xcd, 0x64, 0x01, 0x75, 0xcf, 0xeb, + 0x17, 0x80, 0xfe, 0x01, 0x74, 0xf2, 0xa1, 0xc1, + 0x64, 0xa3, 0xb9, 0x64, 0x83, 0xc3, 0x04, 0x8b, + 0x07, 0xa3, 0xb7, 0x64, 0xb0, 0x01, 0xeb, 0x45, + 0x80, 0xfe, 0x00, 0x75, 0x09, 0x80, 0x3e, 0xcc, + 0x64, 0x01, 0x75, 0x19, 0xeb, 0xcd, 0x80, 0xfe, + 0x02, 0x74, 0xf2, 0xa1, 0xbf, 0x64, 0xa3, 0xb7, + 0x64, 0x83, 0xc3, 0x06, 0x8b, 0x07, 0xa3, 0xb9, + 0x64, 0xb0, 0x02, 0xeb, 0x20, 0x80, 0xfe, 0x01, + 0x75, 0x09, 0x80, 0x3e, 0xcd, 0x64, 0x01, 0x74, + 0xcf, 0xeb, 0x83, 0x80, 0xfe, 0x03, 0x74, 0xf2, + 0xa1, 0xc1, 0x64, 0xa3, 0xb9, 0x64, 0x8b, 0x07, + 0xa3, 0xb7, 0x64, 0xb0, 0x03, 0xa2, 0x40, 0x67, + 0xa1, 0xbf, 0x64, 0xa3, 0xaf, 0x64, 0xa1, 0xc1, + 0x64, 0xa3, 0xb1, 0x64, 0xf9, 0xc3, 0x81, 0xfe, + 0x3f, 0x01, 0x75, 0x01, 0x4e, 0x0b, 0xf6, 0x75, + 0x01, 0x46, 0x81, 0xff, 0xc7, 0x00, 0x75, 0x01, + 0x4f, 0x0b, 0xff, 0x75, 0x01, 0x47, 0xe8, 0x89, + 0x00, 0x73, 0x29, 0x43, 0x8a, 0x07, 0x0a, 0xc0, + 0x74, 0x08, 0xe8, 0x20, 0x00, 0xe8, 0x7a, 0x00, + 0x73, 0x1a, 0xe8, 0x14, 0x03, 0xc6, 0x06, 0xdc, + 0x1c, 0x02, 0xe8, 0x09, 0xed, 0xc6, 0x06, 0xdc, + 0x1c, 0x03, 0xa1, 0xaf, 0x64, 0x8b, 0xf0, 0xa1, + 0xb1, 0x64, 0x8b, 0xf8, 0xc3, 0x43, 0x3c, 0x01, + 0x75, 0x05, 0x8b, 0x7f, 0x02, 0x4f, 0xc3, 0x3c, + 0x03, 0x75, 0x05, 0x8b, 0x7f, 0x06, 0x47, 0xc3, + 0x3c, 0x02, 0x75, 0x05, 0x8b, 0x77, 0x04, 0x46, + 0xc3, 0x3c, 0x04, 0x75, 0x04, 0x8b, 0x37, 0x4e, + 0xc3, 0xb0, 0x3a, 0xe9, 0xc2, 0x77, 0xbb, 0x46, + 0x67, 0xe8, 0x2e, 0x04, 0x8a, 0x0f, 0x0a, 0xc9, + 0x74, 0x24, 0xb5, 0x00, 0x43, 0x8b, 0x47, 0x02, + 0x8b, 0x57, 0x06, 0x3b, 0xf0, 0x76, 0x12, 0x3b, + 0xf2, 0x73, 0x0e, 0x8b, 0x47, 0x04, 0x8b, 0x57, + 0x08, 0x3b, 0xf8, 0x76, 0x04, 0x3b, 0xfa, 0x72, + 0x07, 0x83, 0xc3, 0x0e, 0xe2, 0xdf, 0xf8, 0xc3, + 0xf9, 0xc3, 0xbb, 0x46, 0x67, 0xe8, 0xfa, 0x03, + 0x8a, 0x0f, 0x0a, 0xc9, 0x74, 0x24, 0xb5, 0x00, + 0x43, 0x8b, 0x47, 0x02, 0x8b, 0x57, 0x06, 0x3b, + 0xf0, 0x72, 0x12, 0x3b, 0xf2, 0x77, 0x0e, 0x8b, + 0x47, 0x04, 0x8b, 0x57, 0x08, 0x3b, 0xf8, 0x72, + 0x04, 0x3b, 0xfa, 0x76, 0x07, 0x83, 0xc3, 0x0e, + 0xe2, 0xdf, 0xf8, 0xc3, 0xf9, 0xc3, 0xbb, 0xf4, + 0x70, 0xe8, 0xc6, 0x03, 0x8b, 0x07, 0x3d, 0xff, + 0xff, 0x74, 0x4c, 0x43, 0x43, 0x8b, 0x0e, 0xb1, + 0x64, 0x3a, 0xc8, 0x77, 0xef, 0x8a, 0xc4, 0xb4, + 0x00, 0xa3, 0xd8, 0x64, 0xe8, 0x25, 0x03, 0x83, + 0x3e, 0xce, 0x64, 0x02, 0x76, 0x31, 0x83, 0x3e, + 0xd8, 0x64, 0x1e, 0x72, 0x15, 0x83, 0x3e, 0xb3, + 0x64, 0x01, 0x74, 0x23, 0xc7, 0x06, 0xb3, 0x64, + 0x01, 0x00, 0xc7, 0x06, 0xb5, 0x64, 0x04, 0x00, + 0xf9, 0xc3, 0x83, 0x3e, 0xb3, 0x64, 0x03, 0x74, + 0x0e, 0xc7, 0x06, 0xb3, 0x64, 0x03, 0x00, 0xc7, + 0x06, 0xb5, 0x64, 0x08, 0x00, 0xf9, 0xc3, 0xf8, + 0xc3, 0x80, 0x3e, 0xc6, 0x64, 0x02, 0x75, 0x01, + 0xc3, 0xa1, 0xaf, 0x64, 0xa3, 0xbf, 0x64, 0xa1, + 0xb1, 0x64, 0xa3, 0xc1, 0x64, 0x80, 0x3e, 0xcb, + 0x64, 0x00, 0x75, 0x36, 0xa1, 0xb5, 0x64, 0x80, + 0x3e, 0xcc, 0x64, 0x01, 0x75, 0x06, 0x01, 0x06, + 0xaf, 0x64, 0xeb, 0x04, 0x29, 0x06, 0xaf, 0x64, + 0xbb, 0xd4, 0x64, 0x8b, 0x07, 0x8b, 0x57, 0x02, + 0xb9, 0xe8, 0x03, 0xf7, 0xf1, 0x80, 0x3e, 0xcd, + 0x64, 0x01, 0x75, 0x06, 0x01, 0x06, 0xb1, 0x64, + 0xeb, 0x04, 0x29, 0x06, 0xb1, 0x64, 0xe8, 0x37, + 0x00, 0xc3, 0xa1, 0xb3, 0x64, 0x80, 0x3e, 0xcd, + 0x64, 0x01, 0x75, 0x06, 0x01, 0x06, 0xb1, 0x64, + 0xeb, 0x04, 0x29, 0x06, 0xb1, 0x64, 0xbb, 0xd4, + 0x64, 0x8b, 0x07, 0x8b, 0x57, 0x02, 0xb9, 0xe8, + 0x03, 0xf7, 0xf1, 0x80, 0x3e, 0xcc, 0x64, 0x01, + 0x75, 0x06, 0x01, 0x06, 0xaf, 0x64, 0xeb, 0x04, + 0x29, 0x06, 0xaf, 0x64, 0xe8, 0x01, 0x00, 0xc3, + 0x50, 0xbb, 0xd4, 0x64, 0x8b, 0x07, 0x8b, 0x57, + 0x02, 0xbe, 0xd0, 0x64, 0x8b, 0x1c, 0x8b, 0x4c, + 0x02, 0x03, 0xc3, 0x83, 0xd2, 0x00, 0x03, 0xd1, + 0xbb, 0xd4, 0x64, 0x89, 0x07, 0x89, 0x57, 0x02, + 0x58, 0xb9, 0xe8, 0x03, 0xf7, 0xe1, 0x8b, 0xc8, + 0x8b, 0x07, 0x8b, 0x57, 0x02, 0x2b, 0xc1, 0x83, + 0xda, 0x00, 0x89, 0x07, 0x89, 0x57, 0x02, 0xc3, + 0x8a, 0x0e, 0x07, 0x66, 0x80, 0x3e, 0xc6, 0x64, + 0x02, 0x74, 0x4d, 0x80, 0x3e, 0xcb, 0x64, 0x00, + 0x75, 0x0f, 0xbb, 0xed, 0x65, 0x80, 0x3e, 0xcc, + 0x64, 0x01, 0x74, 0x1e, 0xbb, 0xe3, 0x65, 0xeb, + 0x19, 0x80, 0x3e, 0xe2, 0x65, 0x07, 0x76, 0x05, + 0xc6, 0x06, 0xe2, 0x65, 0x01, 0xbb, 0xff, 0x65, + 0x80, 0x3e, 0xcd, 0x64, 0x01, 0x74, 0x03, 0xbb, + 0xf7, 0x65, 0x8a, 0x0e, 0xe2, 0x65, 0xb5, 0x00, + 0x49, 0x03, 0xd9, 0x8a, 0x0f, 0x0a, 0xc9, 0x75, + 0x07, 0xc6, 0x06, 0xe2, 0x65, 0x01, 0xeb, 0xbb, + 0x88, 0x0e, 0x07, 0x66, 0xfe, 0x06, 0xe2, 0x65, + 0xe8, 0xdd, 0x00, 0xc3, 0x8b, 0x1e, 0x3d, 0x65, + 0x8a, 0x0e, 0x3f, 0x65, 0xb5, 0x00, 0x49, 0x03, + 0xd9, 0x8a, 0x0f, 0x0a, 0xc9, 0x75, 0x0a, 0xc6, + 0x06, 0x3f, 0x65, 0x04, 0xe8, 0x0e, 0x00, 0xeb, + 0xe3, 0x88, 0x0e, 0x07, 0x66, 0xfe, 0x06, 0x3f, + 0x65, 0xe8, 0xb4, 0x00, 0xc3, 0xb8, 0x03, 0x00, + 0xe8, 0xb0, 0x73, 0xbb, 0x40, 0x65, 0xd1, 0xe0, + 0x03, 0xd8, 0x8b, 0x07, 0xa3, 0x3d, 0x65, 0xc3, + 0x80, 0x3e, 0xcb, 0x64, 0x00, 0x75, 0x13, 0xbb, + 0xfe, 0x64, 0xb2, 0x01, 0x80, 0x3e, 0xcc, 0x64, + 0x01, 0x74, 0x18, 0xbb, 0xe9, 0x64, 0xb2, 0x01, + 0xeb, 0x11, 0xbb, 0x28, 0x65, 0xb2, 0x0b, 0x80, + 0x3e, 0xcd, 0x64, 0x01, 0x74, 0x05, 0xbb, 0x13, + 0x65, 0xb2, 0x13, 0x8a, 0x0e, 0xe8, 0x64, 0xb5, + 0x00, 0x49, 0x03, 0xd9, 0x8a, 0x0f, 0x0a, 0xc9, + 0x75, 0x07, 0xc6, 0x06, 0xe8, 0x64, 0x01, 0xeb, + 0xbf, 0xfe, 0x06, 0xe8, 0x64, 0xe8, 0x58, 0x00, + 0x1e, 0x53, 0x8a, 0xca, 0x88, 0x0e, 0x07, 0x66, + 0xe8, 0x4d, 0x00, 0x8b, 0xf3, 0x5b, 0xa1, 0xbf, + 0x32, 0x8e, 0xd8, 0x8b, 0x07, 0x8b, 0x4f, 0x02, + 0xf7, 0xe1, 0x8b, 0xc8, 0xb8, 0x06, 0x00, 0x03, + 0xd8, 0x03, 0xf0, 0x8a, 0x07, 0x88, 0x04, 0x43, + 0x46, 0xe2, 0xf8, 0x1f, 0xc3, 0x33, 0xc9, 0x80, + 0x3e, 0xcb, 0x64, 0x00, 0x75, 0x11, 0xb2, 0x01, + 0xb1, 0x28, 0x80, 0x3e, 0xcc, 0x64, 0x01, 0x74, + 0xb8, 0xb2, 0x01, 0xb1, 0x28, 0xeb, 0xb2, 0xb2, + 0x0b, 0xb1, 0x29, 0x80, 0x3e, 0xcd, 0x64, 0x01, + 0x74, 0xa7, 0xb2, 0x13, 0xb1, 0x2a, 0xeb, 0xa1, + 0x06, 0xa1, 0xbf, 0x32, 0x8e, 0xc0, 0x32, 0xed, + 0xbb, 0x01, 0x00, 0x49, 0xd1, 0xe1, 0x03, 0xd9, + 0x26, 0x8b, 0x1f, 0x89, 0x1e, 0xe6, 0x64, 0x07, + 0xc3, 0x80, 0x3e, 0xcb, 0x64, 0x00, 0x75, 0x0d, + 0xb1, 0x01, 0x80, 0x3e, 0xcc, 0x64, 0x01, 0x74, + 0x0f, 0xb1, 0x01, 0xeb, 0x0b, 0xb1, 0x0b, 0x80, + 0x3e, 0xcd, 0x64, 0x01, 0x74, 0x02, 0xb1, 0x13, + 0x88, 0x0e, 0x07, 0x66, 0xe8, 0xc1, 0xff, 0xc3, + 0xa0, 0x07, 0x66, 0x8a, 0x26, 0xcb, 0x64, 0x8a, + 0x1e, 0xcc, 0x64, 0x8a, 0x3e, 0xcd, 0x64, 0x83, + 0x3e, 0xce, 0x64, 0x01, 0x75, 0x44, 0x3c, 0x13, + 0x75, 0x0f, 0x80, 0xfc, 0x01, 0x75, 0x3b, 0x80, + 0xff, 0x00, 0x75, 0x36, 0xb1, 0x14, 0xe9, 0x85, + 0x00, 0x3c, 0x0b, 0x75, 0x0e, 0x80, 0xfc, 0x01, + 0x75, 0x28, 0x80, 0xff, 0x01, 0x75, 0x23, 0xb1, + 0x0c, 0xeb, 0x73, 0x3c, 0x01, 0x75, 0x1b, 0xa0, + 0xdd, 0x64, 0x3a, 0x06, 0xdc, 0x64, 0x75, 0x12, + 0x80, 0xfc, 0x00, 0x75, 0x0d, 0x80, 0xfb, 0x01, + 0x75, 0x04, 0xb1, 0x02, 0xeb, 0x58, 0xb1, 0x02, + 0xeb, 0x54, 0xa0, 0x07, 0x66, 0x3c, 0x13, 0x72, + 0x12, 0x3c, 0x1a, 0x77, 0x0e, 0x80, 0xfc, 0x01, + 0x75, 0x42, 0x80, 0xff, 0x01, 0x75, 0x3d, 0xb1, + 0x02, 0xeb, 0x3b, 0x3c, 0x01, 0x72, 0x1f, 0x3c, + 0x0a, 0x77, 0x1b, 0x80, 0xfc, 0x00, 0x75, 0x2c, + 0xa0, 0xdd, 0x64, 0x38, 0x06, 0xdc, 0x64, 0x74, + 0x23, 0x80, 0xfb, 0x01, 0x75, 0x04, 0xb1, 0x0c, + 0xeb, 0x1c, 0xb1, 0x0c, 0xeb, 0x18, 0x3c, 0x0b, + 0x72, 0x12, 0x3c, 0x12, 0x77, 0x0e, 0x80, 0xfc, + 0x01, 0x75, 0x09, 0x80, 0xff, 0x00, 0x75, 0x04, + 0xb1, 0x02, 0xeb, 0x02, 0xf8, 0xc3, 0x88, 0x0e, + 0x07, 0x66, 0xf9, 0xc3, 0xa1, 0xd8, 0x64, 0x3d, + 0x64, 0x00, 0x77, 0x28, 0x53, 0x1e, 0x8b, 0x16, + 0xb1, 0x32, 0x8e, 0xda, 0xbb, 0x00, 0xfa, 0xba, + 0xff, 0xff, 0x0b, 0xc0, 0x74, 0x09, 0x8b, 0xc8, + 0xb8, 0x64, 0x00, 0x33, 0xd2, 0xf7, 0xf1, 0x89, + 0x57, 0x02, 0xb9, 0x64, 0x00, 0xf7, 0xe1, 0x89, + 0x07, 0x1f, 0x5b, 0xc3, 0xb0, 0x35, 0xe9, 0xdf, + 0x73, 0x80, 0x3e, 0xae, 0x64, 0x01, 0x75, 0x08, + 0x81, 0x3e, 0xba, 0x00, 0x3f, 0x01, 0x75, 0x18, + 0x8b, 0x0e, 0x32, 0x32, 0x3b, 0xca, 0x77, 0x0a, + 0x8b, 0x0e, 0x34, 0x32, 0x3b, 0xca, 0x73, 0x08, + 0xf8, 0xc3, 0x3b, 0xce, 0x76, 0x02, 0xf8, 0xc3, + 0x8b, 0x0e, 0xb1, 0x64, 0x3b, 0xcf, 0x76, 0x02, + 0xf8, 0xc3, 0x3b, 0xc8, 0x73, 0x02, 0xf8, 0xc3, + 0xf9, 0xc3, 0xc6, 0x06, 0xc6, 0x64, 0x00, 0xc6, + 0x06, 0x40, 0x67, 0xff, 0xc6, 0x06, 0xc4, 0x64, + 0x01, 0xc6, 0x06, 0xc5, 0x64, 0x00, 0xe8, 0x77, + 0xf5, 0xc3, 0x51, 0x8b, 0x0e, 0xf3, 0xb4, 0x49, + 0xd1, 0xe1, 0x03, 0xd9, 0x8b, 0x1f, 0x59, 0xc3, + 0xbb, 0xf5, 0xb4, 0xeb, 0xed, 0xc7, 0x06, 0xf3, + 0xb4, 0x03, 0x00, 0xb8, 0x3f, 0x01, 0xbb, 0x68, + 0x00, 0xbf, 0x22, 0x01, 0xbe, 0x76, 0x00, 0xb1, + 0x01, 0xb5, 0x04, 0xe8, 0xdd, 0x6b, 0xc3, 0xc7, + 0x06, 0xf3, 0xb4, 0x05, 0x00, 0xb8, 0x2c, 0x01, + 0xbb, 0x83, 0x00, 0xbf, 0x2c, 0x01, 0xbe, 0x87, + 0x00, 0xb1, 0x0b, 0xb5, 0x03, 0xe8, 0xc3, 0x6b, + 0xc3, 0x80, 0x3e, 0xe1, 0xdb, 0x01, 0x74, 0x07, + 0xbb, 0x0f, 0x57, 0xe8, 0x27, 0x60, 0xc3, 0xb9, + 0x4c, 0x03, 0xe8, 0xba, 0xda, 0xb9, 0x4e, 0x03, + 0xe8, 0xb7, 0xda, 0xb9, 0x4d, 0x03, 0xe8, 0xd6, + 0xda, 0xbb, 0x96, 0x56, 0xe8, 0x0e, 0x60, 0xc3, + 0xbb, 0xac, 0x56, 0xe8, 0x07, 0x60, 0xc3, 0xbb, + 0xf7, 0x56, 0xe8, 0x00, 0x60, 0xc3, 0xbb, 0x7a, + 0xdb, 0xe8, 0x51, 0xd1, 0xe8, 0x66, 0xd1, 0xc3, + 0xc7, 0x06, 0xf3, 0xb4, 0x02, 0x00, 0xb8, 0x00, + 0x00, 0xbb, 0xb4, 0x00, 0xbf, 0x38, 0x00, 0xbe, + 0xb4, 0x00, 0xb1, 0x01, 0xb5, 0x02, 0xe8, 0x6a, + 0x6b, 0xc3, 0xc7, 0x06, 0xf3, 0xb4, 0x04, 0x00, + 0xb8, 0x3f, 0x01, 0xbb, 0x80, 0x00, 0xbf, 0x13, + 0x01, 0xbe, 0x89, 0x00, 0xb1, 0x01, 0xb5, 0x04, + 0xe8, 0x50, 0x6b, 0xc3, 0x80, 0x3e, 0xe4, 0xdb, + 0x01, 0x75, 0x07, 0xbb, 0xb2, 0x57, 0xe8, 0xb4, + 0x5f, 0xc3, 0xb9, 0x4c, 0x00, 0xb0, 0x0b, 0xb4, + 0x07, 0xe8, 0x51, 0x69, 0xb0, 0x0f, 0xe8, 0x55, + 0x69, 0xb0, 0x13, 0xe8, 0x57, 0x69, 0xb0, 0x17, + 0xe8, 0x59, 0x69, 0xb0, 0x1b, 0xe8, 0x5b, 0x69, + 0xb9, 0x38, 0x00, 0xb0, 0x23, 0xe8, 0x5a, 0x69, + 0xb9, 0x13, 0x00, 0xb0, 0x3b, 0xe8, 0x59, 0x69, + 0xb9, 0x60, 0x03, 0xe8, 0x19, 0xda, 0xe8, 0xba, + 0x6a, 0xc6, 0x06, 0xdc, 0x1c, 0x01, 0xbb, 0x47, + 0x33, 0xc7, 0x47, 0x02, 0x61, 0x03, 0xb0, 0x02, + 0xe8, 0x3c, 0xd9, 0xc6, 0x06, 0xb0, 0x32, 0x01, + 0xe8, 0x91, 0xdd, 0xc6, 0x06, 0xb0, 0x32, 0x00, + 0x9c, 0xe8, 0xa2, 0x69, 0xb9, 0x62, 0x03, 0xc6, + 0x06, 0xdc, 0x1c, 0x02, 0xe8, 0xeb, 0xd9, 0x9d, + 0x73, 0x4c, 0xa1, 0x1f, 0xc4, 0xe8, 0x54, 0xe7, + 0x3c, 0x55, 0x75, 0x42, 0x83, 0x3e, 0x52, 0x72, + 0x05, 0x75, 0x3b, 0xc6, 0x06, 0xcf, 0x00, 0x00, + 0xc7, 0x06, 0x1f, 0xc4, 0x00, 0x00, 0xb9, 0x05, + 0x00, 0xb0, 0x04, 0xb4, 0x06, 0xe8, 0xcd, 0x68, + 0xb0, 0x13, 0xe8, 0xd8, 0x68, 0xb9, 0x40, 0x00, + 0xb0, 0x0b, 0xe8, 0xc9, 0x68, 0xb9, 0x63, 0x03, + 0xe8, 0xaf, 0xd9, 0xb0, 0x55, 0xe8, 0x00, 0xe6, + 0xb0, 0x56, 0xe8, 0xeb, 0xe5, 0xc6, 0x06, 0xe4, + 0xdb, 0x01, 0xe8, 0x1e, 0x00, 0xc3, 0xe8, 0x1a, + 0x00, 0xff, 0x36, 0xcf, 0x00, 0xc6, 0x06, 0xcf, + 0x00, 0x00, 0xbb, 0x72, 0xdb, 0xe8, 0x45, 0xd0, + 0xe8, 0x5a, 0xd0, 0x8f, 0x06, 0xcf, 0x00, 0xe8, + 0xdc, 0xdd, 0xc3, 0xc7, 0x06, 0xaf, 0x64, 0x56, + 0x00, 0xc7, 0x06, 0xb1, 0x64, 0xc3, 0x00, 0xc6, + 0x06, 0xcc, 0x64, 0x00, 0xc6, 0x06, 0xcd, 0x64, + 0x01, 0xc6, 0x06, 0xcb, 0x64, 0x01, 0xb9, 0x64, + 0x03, 0xe8, 0x83, 0xd9, 0xc3, 0xbb, 0xb7, 0x58, + 0xe8, 0xba, 0x5e, 0xc3, 0xb9, 0x38, 0x00, 0xb0, + 0x0a, 0xb4, 0x09, 0xe8, 0x57, 0x68, 0xe8, 0xae, + 0xd9, 0xe8, 0x3c, 0xfe, 0xc6, 0x07, 0x00, 0xe8, + 0xc2, 0x6c, 0xb9, 0x6b, 0x03, 0xe8, 0x6d, 0xd9, + 0xb0, 0x06, 0xe8, 0x28, 0xd8, 0xb0, 0x5c, 0xe8, + 0x76, 0xe5, 0xc3, 0xbb, 0x0f, 0x57, 0xe8, 0x8c, + 0x5e, 0xc3, 0xe8, 0x09, 0x63, 0xc3, 0xe8, 0x86, + 0xd9, 0xbb, 0x46, 0x67, 0xe8, 0x03, 0xfe, 0x83, + 0xc3, 0x03, 0xff, 0x37, 0x53, 0xc7, 0x07, 0x3f, + 0x01, 0xbe, 0xc5, 0x00, 0xbf, 0x9f, 0x00, 0xc6, + 0x06, 0xc3, 0x64, 0x04, 0xe8, 0x3c, 0xf4, 0xe8, + 0xf6, 0xfd, 0xc6, 0x07, 0x00, 0xe8, 0x7c, 0x6c, + 0xb9, 0x47, 0x00, 0xb0, 0x08, 0xb4, 0x07, 0xe8, + 0xfb, 0x67, 0xb9, 0x05, 0x00, 0xb0, 0x0d, 0xe8, + 0xfc, 0x67, 0xb9, 0x41, 0x03, 0xe8, 0x15, 0xd9, + 0xbe, 0xe1, 0x00, 0xbf, 0x9f, 0x00, 0xc6, 0x06, + 0xc3, 0x64, 0x04, 0xe8, 0x0d, 0xf4, 0x5b, 0x8f, + 0x07, 0xb0, 0x4e, 0xe8, 0x12, 0xe5, 0xb0, 0x03, + 0xe8, 0xba, 0xd7, 0xc3, 0xbb, 0xc8, 0x58, 0xe8, + 0x23, 0x5e, 0xc3, 0xc7, 0x06, 0xf3, 0xb4, 0x03, + 0x00, 0xb8, 0xd6, 0x00, 0xbb, 0xc7, 0x00, 0xbf, + 0xda, 0x00, 0xbe, 0xbf, 0x00, 0xb1, 0x13, 0xb5, + 0x01, 0xe8, 0x97, 0x69, 0xc3, 0xc7, 0x06, 0xf3, + 0xb4, 0x05, 0x00, 0xb8, 0x00, 0x00, 0xbb, 0xae, + 0x00, 0xbf, 0x23, 0x00, 0xbe, 0xae, 0x00, 0xb1, + 0x01, 0xb5, 0x02, 0xe8, 0x7d, 0x69, 0xc3, 0xe8, + 0xed, 0xd8, 0xc6, 0x06, 0xdc, 0x64, 0x00, 0xb9, + 0x17, 0x00, 0xb0, 0x08, 0xb4, 0x07, 0xe8, 0x84, + 0x67, 0xb9, 0x18, 0x00, 0xb0, 0x0d, 0xe8, 0x85, + 0x67, 0xe8, 0x64, 0xfd, 0xc6, 0x47, 0x01, 0x00, + 0x53, 0xe8, 0xe8, 0x6b, 0xb9, 0x49, 0x03, 0xe8, + 0x60, 0xd8, 0x5b, 0xc6, 0x47, 0x01, 0x61, 0xc6, + 0x47, 0x02, 0x00, 0xe8, 0x92, 0x6b, 0xe8, 0x84, + 0x00, 0xe8, 0xb1, 0xdc, 0xb9, 0x3f, 0x00, 0xb0, + 0x0c, 0xb4, 0x07, 0xe8, 0x4f, 0x67, 0xb9, 0x05, + 0x00, 0xb0, 0x1a, 0xe8, 0x50, 0x67, 0xb9, 0x4a, + 0x03, 0xe8, 0x36, 0xd8, 0xe8, 0x66, 0x00, 0xe8, + 0x93, 0xdc, 0xc6, 0x06, 0xe6, 0x1c, 0xd1, 0xbb, + 0x56, 0x56, 0xbe, 0x10, 0x55, 0xe8, 0x2e, 0x60, + 0xe8, 0x7b, 0xdc, 0xbb, 0x7a, 0x56, 0xbe, 0x5c, + 0x55, 0xe8, 0x22, 0x60, 0xe8, 0x6f, 0xdc, 0xbb, + 0x82, 0x56, 0xbe, 0x3e, 0x55, 0xe8, 0x16, 0x60, + 0xe8, 0x63, 0xdc, 0xb9, 0x4b, 0x03, 0xe8, 0x01, + 0xd8, 0xc7, 0x06, 0xaf, 0x64, 0xdf, 0x00, 0xc7, + 0x06, 0xb1, 0x64, 0x95, 0x00, 0xc6, 0x06, 0xcb, + 0x64, 0x00, 0xc6, 0x06, 0xcc, 0x64, 0x01, 0xc6, + 0x06, 0xcd, 0x64, 0x00, 0xe8, 0x1d, 0xd8, 0xb0, + 0x07, 0xe8, 0xd1, 0xd6, 0xb0, 0x01, 0xe8, 0xcc, + 0xd6, 0xb0, 0x51, 0xe8, 0x1a, 0xe4, 0xbb, 0x46, + 0x56, 0xe8, 0x31, 0x5d, 0xc3, 0xe8, 0x6b, 0x68, + 0xc6, 0x06, 0xdc, 0x1c, 0x01, 0xe8, 0x9c, 0xeb, + 0xe8, 0x45, 0xec, 0xe8, 0xda, 0x63, 0xe8, 0x2b, + 0xf1, 0xc3, 0xc7, 0x06, 0xf3, 0xb4, 0x0a, 0x00, + 0xb8, 0x3f, 0x01, 0xbb, 0xb7, 0x00, 0xbf, 0x0d, + 0x01, 0xbe, 0xaf, 0x00, 0xb1, 0x01, 0xb5, 0x04, + 0xe8, 0x90, 0x68, 0xc3, 0xc7, 0x06, 0xf3, 0xb4, + 0x04, 0x00, 0xb8, 0x3f, 0x01, 0xbb, 0xb9, 0x00, + 0xbf, 0x18, 0x01, 0xbe, 0xb9, 0x00, 0xb1, 0x01, + 0xb5, 0x04, 0xe8, 0x76, 0x68, 0xc3, 0xc7, 0x06, + 0xf3, 0xb4, 0x02, 0x00, 0xb8, 0xdd, 0x00, 0xbb, + 0xc7, 0x00, 0xbf, 0xd9, 0x00, 0xbe, 0xc1, 0x00, + 0xb1, 0x13, 0xb5, 0x01, 0xe8, 0x5c, 0x68, 0xc3, + 0xb9, 0x50, 0x00, 0xb0, 0x04, 0xb4, 0x02, 0xe8, + 0x6b, 0x66, 0xb9, 0xc1, 0x03, 0xe8, 0x57, 0xd7, + 0xe8, 0xf8, 0x67, 0xc7, 0x06, 0xf3, 0xb4, 0x08, + 0x00, 0xb8, 0x9b, 0x00, 0xbb, 0xc7, 0x00, 0xbf, + 0x9e, 0x00, 0xbe, 0xb8, 0x00, 0xb1, 0x13, 0xb5, + 0x01, 0xe8, 0x2f, 0x68, 0xc3, 0xb9, 0x46, 0x00, + 0xb0, 0x06, 0xb4, 0x09, 0xe8, 0x3e, 0x66, 0xb9, + 0xc2, 0x03, 0xe8, 0x2a, 0xd7, 0xe8, 0xcb, 0x67, + 0xc7, 0x06, 0xf3, 0xb4, 0x07, 0x00, 0xb8, 0x1e, + 0x00, 0xbb, 0xb8, 0x00, 0xbf, 0x32, 0x00, 0xbe, + 0xb8, 0x00, 0xb1, 0x01, 0xb5, 0x02, 0xe8, 0x02, + 0x68, 0x80, 0x3e, 0xdf, 0xdb, 0x02, 0x72, 0x01, + 0xc3, 0xb8, 0x96, 0x00, 0xe8, 0x4b, 0xdb, 0xbe, + 0x86, 0x00, 0xbf, 0xa7, 0x00, 0xc6, 0x06, 0xc3, + 0x64, 0x02, 0xe8, 0x2e, 0xf2, 0xbb, 0xf7, 0x54, + 0xe8, 0x52, 0x5c, 0xbb, 0x47, 0x33, 0xc7, 0x07, + 0x2c, 0x03, 0xb9, 0x2b, 0x03, 0xe8, 0xdf, 0xd6, + 0xe8, 0x80, 0x67, 0xbb, 0x47, 0x33, 0xc7, 0x07, + 0x2d, 0x03, 0xb0, 0x01, 0xe8, 0x08, 0xd6, 0xe8, + 0x11, 0xd3, 0xbb, 0x17, 0x61, 0xe8, 0x9d, 0xcd, + 0xc7, 0x06, 0xaf, 0x64, 0xe6, 0x00, 0xc7, 0x06, + 0xb1, 0x64, 0xb8, 0x00, 0xbb, 0x47, 0x33, 0xc7, + 0x07, 0x2e, 0x03, 0xc7, 0x06, 0xf3, 0xb4, 0x06, + 0x00, 0xe8, 0xd1, 0x68, 0xe8, 0x0f, 0xd3, 0xbb, + 0x6a, 0x62, 0xe8, 0x78, 0xcd, 0xb9, 0x04, 0x00, + 0xb0, 0x0e, 0xb4, 0x0e, 0xe8, 0xa6, 0x65, 0xbb, + 0x47, 0x33, 0xc7, 0x07, 0x2f, 0x03, 0xb0, 0x01, + 0xe8, 0xbd, 0xd5, 0xe8, 0x82, 0xfb, 0xc6, 0x47, + 0x01, 0x00, 0xe8, 0x07, 0x6a, 0xe8, 0x2e, 0x66, + 0xbb, 0xdc, 0x62, 0xe8, 0x4f, 0xcd, 0xc6, 0x06, + 0xdf, 0xdb, 0x01, 0xb9, 0x05, 0x00, 0xe8, 0xca, + 0x6c, 0xc3, 0x80, 0x3e, 0xdf, 0xdb, 0x00, 0x75, + 0x0d, 0xb9, 0xc8, 0x03, 0xe8, 0x8f, 0xd6, 0xbb, + 0x11, 0x55, 0xe8, 0xc0, 0x5b, 0xc3, 0xb9, 0x50, + 0x00, 0xb0, 0x03, 0xb4, 0x0c, 0xe8, 0x5d, 0x65, + 0xb9, 0x4f, 0x00, 0xb0, 0x04, 0xe8, 0x5e, 0x65, + 0xb9, 0xc8, 0x03, 0xe8, 0x51, 0xd6, 0xe8, 0xe2, + 0x66, 0xc7, 0x06, 0xf3, 0xb4, 0x06, 0x00, 0xb8, + 0x18, 0x01, 0xbb, 0xba, 0x00, 0xbf, 0x09, 0x01, + 0xbe, 0xb4, 0x00, 0xb1, 0x01, 0xb5, 0x04, 0xe8, + 0x19, 0x67, 0xc3, 0x80, 0x3e, 0xe5, 0xdb, 0x01, + 0x75, 0x07, 0xbb, 0xc0, 0x57, 0xe8, 0x7d, 0x5b, + 0xc3, 0xb9, 0x31, 0x00, 0xb0, 0x0e, 0xb4, 0x07, + 0xe8, 0x1a, 0x65, 0xb9, 0x05, 0x00, 0xb0, 0x15, + 0xe8, 0x1b, 0x65, 0xb9, 0x65, 0x03, 0xe8, 0x2d, + 0xd6, 0xb0, 0x58, 0xe8, 0x42, 0xe2, 0xc6, 0x06, + 0xe5, 0xdb, 0x01, 0xc3, 0xb9, 0x35, 0x00, 0xb0, + 0x14, 0xb4, 0x05, 0xe8, 0xf7, 0x64, 0xb9, 0x18, + 0x00, 0xb0, 0x1b, 0xb4, 0x0e, 0xe8, 0xf6, 0x64, + 0xb9, 0x05, 0x00, 0xb0, 0x24, 0xe8, 0xf5, 0x64, + 0xb9, 0x47, 0x03, 0xe8, 0xe1, 0xd5, 0xc7, 0x06, + 0xaf, 0x64, 0x16, 0x01, 0xe8, 0x05, 0xd6, 0xb0, + 0x50, 0xe8, 0x0c, 0xe2, 0xb0, 0x01, 0xe8, 0xb4, + 0xd4, 0xc3, 0xbb, 0xe3, 0x58, 0xe8, 0x1d, 0x5b, + 0xc3, 0x80, 0x3e, 0xdd, 0xdb, 0x02, 0x74, 0x7f, + 0xbb, 0x5c, 0xdb, 0xe8, 0x67, 0xcc, 0x53, 0xe8, + 0x7b, 0xcc, 0x5b, 0x81, 0xfb, 0x6b, 0x63, 0x75, + 0x6d, 0xe8, 0x01, 0xda, 0xb9, 0x34, 0x00, 0xb0, + 0x08, 0xb4, 0x0c, 0xe8, 0x9f, 0x64, 0xb0, 0x0d, + 0xe8, 0xa3, 0x64, 0xbb, 0x47, 0x33, 0xc7, 0x07, + 0x34, 0x03, 0xb0, 0x01, 0xe8, 0xb1, 0xd4, 0xe8, + 0x76, 0xfa, 0xc6, 0x47, 0x03, 0x59, 0xe8, 0xfb, + 0x68, 0xe8, 0x22, 0x65, 0xc6, 0x06, 0x33, 0x33, + 0x01, 0xc6, 0x06, 0xdd, 0x1c, 0x02, 0xe8, 0xc5, + 0xd9, 0x8b, 0x36, 0xaf, 0x64, 0x8b, 0x3e, 0xb1, + 0x64, 0x56, 0x57, 0xc6, 0x06, 0xc3, 0x64, 0x03, + 0xe8, 0x90, 0xf0, 0xe8, 0xb7, 0xd9, 0xe8, 0xad, + 0xd9, 0x5f, 0x5e, 0xc6, 0x06, 0xc3, 0x64, 0x02, + 0xe8, 0x80, 0xf0, 0xe8, 0xa7, 0xd9, 0xbb, 0x1f, + 0x55, 0xe8, 0xa1, 0x5a, 0xb0, 0x04, 0xe8, 0x20, + 0xd4, 0xc6, 0x06, 0xdc, 0xdb, 0x01, 0xc3, 0xbb, + 0xc0, 0x55, 0xe8, 0x90, 0x5a, 0xbe, 0x32, 0x01, + 0xbf, 0xc4, 0x00, 0xc6, 0x06, 0xc3, 0x64, 0x02, + 0xe8, 0x58, 0xf0, 0xe8, 0x78, 0xd9, 0xbb, 0x47, + 0x33, 0xc7, 0x07, 0x39, 0x03, 0xb0, 0x01, 0xe8, + 0x45, 0xd4, 0xe8, 0x07, 0x65, 0xe8, 0x66, 0xd9, + 0xb9, 0x47, 0x00, 0xb0, 0x04, 0xb4, 0x0e, 0xe8, + 0x0b, 0x64, 0xb9, 0x37, 0x03, 0xe8, 0x1f, 0xd5, + 0xb9, 0x4a, 0x00, 0xb0, 0x01, 0xb4, 0x0a, 0xe8, + 0xfb, 0x63, 0xb0, 0x03, 0xe8, 0xff, 0x63, 0xb0, + 0x06, 0xe8, 0x01, 0x64, 0xbb, 0x47, 0x33, 0xc7, + 0x07, 0x3a, 0x03, 0xc7, 0x06, 0xf3, 0xb4, 0x05, + 0x00, 0xbb, 0x47, 0x33, 0xc7, 0x07, 0x3a, 0x03, + 0xe8, 0xe9, 0x66, 0xb0, 0x01, 0xe8, 0x3a, 0xd4, + 0xe8, 0x73, 0x64, 0xc7, 0x06, 0xf3, 0xb4, 0x06, + 0x00, 0xe8, 0xb4, 0xf9, 0xc6, 0x47, 0x03, 0x5b, + 0xe8, 0xda, 0x66, 0x8b, 0x36, 0xaf, 0x64, 0x8b, + 0x3e, 0xb1, 0x64, 0xc6, 0x06, 0xc3, 0x64, 0x01, + 0xe8, 0xe0, 0xef, 0xe8, 0x00, 0xd9, 0xbb, 0xdb, + 0x55, 0xe8, 0x01, 0x5a, 0xc6, 0x06, 0xdd, 0xdb, + 0x03, 0xb0, 0x04, 0xbe, 0xc7, 0x78, 0xe8, 0x90, + 0xd3, 0xc3, 0x80, 0x3e, 0xdd, 0xdb, 0x03, 0x74, + 0x07, 0xbb, 0x05, 0x59, 0xe8, 0xe6, 0x59, 0xc3, + 0x80, 0x3e, 0xde, 0xdb, 0x01, 0x74, 0x31, 0xbe, + 0x18, 0x01, 0xbf, 0xb3, 0x00, 0xc6, 0x06, 0xc3, + 0x64, 0x02, 0xe8, 0xa6, 0xef, 0xb9, 0x31, 0x00, + 0xb0, 0x07, 0xb4, 0x0c, 0xe8, 0x6e, 0x63, 0xb9, + 0x05, 0x00, 0xb0, 0x11, 0xe8, 0x6f, 0x63, 0xb9, + 0x3b, 0x03, 0xe8, 0x73, 0xd4, 0xb0, 0x4d, 0xe8, + 0x96, 0xe0, 0xc6, 0x06, 0xde, 0xdb, 0x01, 0xc3, + 0xbb, 0x08, 0x56, 0xe8, 0xa7, 0x59, 0xc3, 0x80, + 0x3e, 0xdc, 0xdb, 0x01, 0x74, 0x07, 0xbb, 0x28, + 0x55, 0xe8, 0x99, 0x59, 0xc3, 0xe8, 0x97, 0xd4, + 0xbb, 0x5d, 0x55, 0xe8, 0x8f, 0x59, 0xe8, 0x1f, + 0xf9, 0xc6, 0x47, 0x02, 0x00, 0xe8, 0xa4, 0x67, + 0xb9, 0x35, 0x03, 0xe8, 0x1c, 0xd4, 0xe8, 0x0f, + 0xf9, 0xc6, 0x47, 0x02, 0x5a, 0xc6, 0x47, 0x03, + 0x00, 0xe8, 0x4c, 0x67, 0xb9, 0x16, 0x00, 0xb0, + 0x02, 0xb4, 0x0b, 0xe8, 0x0f, 0x63, 0xb9, 0x36, + 0x03, 0xe8, 0x23, 0xd4, 0xbb, 0x77, 0x55, 0xe8, + 0x5b, 0x59, 0xb0, 0x05, 0xe8, 0xe6, 0xd2, 0xc6, + 0x06, 0xdd, 0xdb, 0x01, 0xc3, 0xe8, 0x4f, 0xd4, + 0xe8, 0x43, 0xd8, 0xbe, 0x90, 0x00, 0xbf, 0xb9, + 0x00, 0xc6, 0x06, 0xc3, 0x64, 0x04, 0xe8, 0x12, + 0xef, 0xe8, 0xcc, 0xf8, 0xc6, 0x07, 0x56, 0xe8, + 0x52, 0x67, 0xb9, 0x38, 0x00, 0xb0, 0x0f, 0xb4, + 0x07, 0xe8, 0xd1, 0x62, 0xbb, 0x47, 0x33, 0xc7, + 0x07, 0x31, 0x03, 0xb9, 0x30, 0x03, 0xe8, 0xc9, + 0xd3, 0xe8, 0xac, 0xf8, 0xc6, 0x07, 0x57, 0xe8, + 0xee, 0x66, 0xb9, 0x22, 0x00, 0xb0, 0x01, 0xb4, + 0x08, 0xe8, 0xb1, 0x62, 0xb9, 0x05, 0x00, 0xb0, + 0x0f, 0xe8, 0xb2, 0x62, 0xbb, 0x47, 0x33, 0xc7, + 0x07, 0x33, 0x03, 0xb9, 0x32, 0x03, 0xe8, 0xa1, + 0xd3, 0xe8, 0x84, 0xf8, 0xc6, 0x47, 0x02, 0x58, + 0xe8, 0xc5, 0x66, 0xc7, 0x06, 0xaf, 0x64, 0xa0, + 0x00, 0xc7, 0x06, 0xb1, 0x64, 0xbc, 0x00, 0xc6, + 0x06, 0xcc, 0x64, 0x01, 0xc6, 0x06, 0xdc, 0x64, + 0x00, 0xe8, 0xa8, 0xd3, 0xb0, 0x06, 0xe8, 0x5c, + 0xd2, 0xb0, 0x05, 0xe8, 0x4b, 0xd2, 0xb0, 0x4c, + 0xe8, 0xa5, 0xdf, 0xc3, 0x80, 0x3e, 0xdf, 0xdb, + 0x02, 0x73, 0x07, 0xbb, 0x2f, 0x59, 0xe8, 0xb4, + 0x58, 0xc3, 0xc7, 0x06, 0xf3, 0xb4, 0x05, 0x00, + 0xb8, 0x1f, 0x01, 0xbb, 0x8f, 0x00, 0xbf, 0x22, + 0x01, 0xbe, 0x8f, 0x00, 0xb1, 0x01, 0xb5, 0x02, + 0xe8, 0x28, 0x64, 0xc3, 0xb9, 0x4f, 0x00, 0xb0, + 0x05, 0xb4, 0x0b, 0xe8, 0x37, 0x62, 0xb9, 0x01, + 0x00, 0xb0, 0x0e, 0xe8, 0x38, 0x62, 0xb9, 0xc6, + 0x03, 0xe8, 0x3c, 0xd3, 0xbb, 0x55, 0x59, 0xe8, + 0x7b, 0x58, 0xc3, 0x80, 0x3e, 0xdf, 0xdb, 0x01, + 0x74, 0x07, 0xbb, 0xf6, 0x52, 0xe8, 0x6d, 0x58, + 0xc3, 0xb9, 0x47, 0x00, 0xb0, 0x04, 0xb4, 0x0e, + 0xe8, 0x0a, 0x62, 0xb9, 0x37, 0x03, 0xe8, 0x17, + 0xd3, 0x80, 0x3e, 0xdd, 0xdb, 0x00, 0x75, 0x07, + 0xbb, 0x80, 0x4d, 0xe8, 0x4f, 0x58, 0xc3, 0xb9, + 0x4a, 0x00, 0xb0, 0x01, 0xb4, 0x0e, 0xe8, 0xec, + 0x61, 0xbb, 0x47, 0x33, 0xc7, 0x07, 0x38, 0x03, + 0xb0, 0x01, 0xe8, 0x03, 0xd2, 0xe8, 0x7e, 0x62, + 0x80, 0x3e, 0xdd, 0xdb, 0x01, 0x75, 0x0e, 0xe8, + 0x2b, 0xd7, 0xbb, 0x9a, 0x55, 0xe8, 0x25, 0x58, + 0xc6, 0x06, 0xdd, 0xdb, 0x02, 0xc3, 0xe8, 0x9d, + 0x5c, 0xc3, 0xbb, 0x82, 0xdb, 0xe8, 0x6d, 0xc9, + 0xe8, 0x82, 0xc9, 0xc3, 0xb9, 0x50, 0x00, 0xb0, + 0x04, 0xb4, 0x02, 0xe8, 0xaf, 0x61, 0xb9, 0xc3, + 0x03, 0xe8, 0xab, 0xd2, 0xe8, 0x3c, 0x63, 0xc7, + 0x06, 0xf3, 0xb4, 0x05, 0x00, 0xb8, 0xa6, 0x00, + 0xbb, 0x9e, 0x00, 0xbf, 0xa6, 0x00, 0xbe, 0xa1, + 0x00, 0xb1, 0x0b, 0xb5, 0x03, 0xe8, 0x73, 0x63, + 0xc3, 0xb9, 0xc5, 0x03, 0xe8, 0x99, 0xd2, 0xbb, + 0x11, 0x55, 0xe8, 0xd8, 0x57, 0xc3, 0xbb, 0x62, + 0x59, 0xe8, 0xd1, 0x57, 0xc3, 0xbb, 0x9d, 0x59, + 0xe8, 0xca, 0x57, 0xc3, 0xbb, 0xb6, 0x59, 0xe8, + 0xc3, 0x57, 0xc3, 0x80, 0x3e, 0xe6, 0xdb, 0x01, + 0x75, 0x2d, 0xc6, 0x06, 0xe6, 0xdb, 0x02, 0xe8, + 0xb5, 0xd2, 0xe8, 0x43, 0xf7, 0xc6, 0x47, 0x01, + 0x67, 0xe8, 0xc8, 0x65, 0xb9, 0x05, 0x00, 0xb0, + 0x09, 0xb4, 0x09, 0xe8, 0x47, 0x61, 0xb9, 0x68, + 0x03, 0xe8, 0x5b, 0xd2, 0xb0, 0x5a, 0xe8, 0x77, + 0xde, 0xb0, 0x07, 0xe8, 0x1f, 0xd1, 0xc3, 0xb9, + 0xc4, 0x03, 0xe8, 0x43, 0xd2, 0xbb, 0x11, 0x55, + 0xe8, 0x82, 0x57, 0xc3, 0xbb, 0xdb, 0x59, 0xe8, + 0x7b, 0x57, 0xc3, 0xbb, 0x01, 0x5a, 0xe8, 0x74, + 0x57, 0xc3, 0xe8, 0xf1, 0x5b, 0xc3, 0xe8, 0xed, + 0x5b, 0xc3, 0xe8, 0x6a, 0xd2, 0xe8, 0xf8, 0xf6, + 0xc6, 0x07, 0x00, 0xe8, 0x7e, 0x65, 0xb9, 0x05, + 0x00, 0xb0, 0x09, 0xb4, 0x07, 0xe8, 0xfd, 0x60, + 0xb9, 0x44, 0x03, 0xe8, 0x11, 0xd2, 0xb0, 0x4f, + 0xe8, 0x2d, 0xde, 0xb0, 0x0c, 0xe8, 0xd5, 0xd0, + 0xc3, 0xbb, 0x20, 0x5a, 0xe8, 0x3e, 0x57, 0xc3, + 0x80, 0x3e, 0xe7, 0xdb, 0x01, 0x74, 0x03, 0xe9, + 0x83, 0x00, 0xbe, 0x8c, 0x00, 0xbf, 0x98, 0x00, + 0xc6, 0x06, 0xc3, 0x64, 0x01, 0xe8, 0xfb, 0xec, + 0x80, 0x3e, 0xe8, 0xdb, 0x01, 0x74, 0x07, 0xbb, + 0x55, 0x58, 0xe8, 0x18, 0x57, 0xc3, 0xbb, 0x20, + 0x6f, 0xe8, 0x81, 0xc8, 0xc6, 0x06, 0xe6, 0x1c, + 0xef, 0xbe, 0xe0, 0x53, 0xbb, 0x83, 0x58, 0xe8, + 0xac, 0x59, 0xbb, 0x47, 0x33, 0xc7, 0x07, 0x72, + 0x03, 0xb0, 0x01, 0xe8, 0xc2, 0xd0, 0xe8, 0x87, + 0xf6, 0xc6, 0x47, 0x02, 0x00, 0xe8, 0x0c, 0x65, + 0xb9, 0x4b, 0x00, 0xb0, 0x0a, 0xb4, 0x0a, 0xe8, + 0x8b, 0x60, 0xb9, 0x18, 0x00, 0xb0, 0x0f, 0xe8, + 0x8c, 0x60, 0xbb, 0x47, 0x33, 0xc7, 0x07, 0x73, + 0x03, 0xb0, 0x01, 0xe8, 0x9a, 0xd0, 0xe8, 0x63, + 0x61, 0xe8, 0x87, 0x00, 0xb8, 0x01, 0x00, 0xe8, + 0xe7, 0xe4, 0xe8, 0xfb, 0xe4, 0xb0, 0x02, 0xe8, + 0x4b, 0xd0, 0xb0, 0x01, 0xe8, 0x46, 0xd0, 0xc6, + 0x06, 0xe9, 0xdb, 0x01, 0xc3, 0xe8, 0x68, 0xcd, + 0x80, 0x3e, 0xdf, 0xdb, 0x03, 0x74, 0x0a, 0xbb, + 0x68, 0xdb, 0xe8, 0xf8, 0xc7, 0xe8, 0x0d, 0xc8, + 0xc3, 0x80, 0x3e, 0xe3, 0xdb, 0x01, 0x75, 0x07, + 0xbb, 0xd6, 0x6b, 0xe8, 0xff, 0xc7, 0xc3, 0xbb, + 0xb5, 0x69, 0xe8, 0xf8, 0xc7, 0xbb, 0x47, 0x33, + 0xc7, 0x07, 0x5a, 0x03, 0xb9, 0x5b, 0x03, 0xe8, + 0x18, 0xd1, 0xe8, 0xc1, 0x60, 0xb9, 0x4b, 0x00, + 0xb0, 0x06, 0xb4, 0x08, 0xe8, 0x16, 0x60, 0xb0, + 0x0a, 0xe8, 0x1a, 0x60, 0xb9, 0x05, 0x00, 0xb0, + 0x0f, 0xe8, 0x19, 0x60, 0xb9, 0x5c, 0x03, 0xe8, + 0x1d, 0xd1, 0xe8, 0x13, 0xcd, 0xbb, 0xc2, 0x69, + 0xe8, 0xc2, 0xc7, 0xb0, 0x55, 0xe8, 0x30, 0xdd, + 0xc6, 0x06, 0xe3, 0xdb, 0x01, 0xc6, 0x06, 0xf0, + 0xdb, 0x00, 0xc3, 0xe8, 0x00, 0x00, 0x1e, 0x06, + 0xa1, 0xb1, 0x32, 0x8e, 0xd8, 0xb8, 0x00, 0xa0, + 0x8e, 0xc0, 0xfc, 0xe8, 0xea, 0x5c, 0xe8, 0xe7, + 0x5c, 0xbe, 0x00, 0x0a, 0x33, 0xff, 0xb9, 0x00, + 0x78, 0xf3, 0xa5, 0xe8, 0xda, 0x5c, 0xe8, 0xd7, + 0x5c, 0x33, 0xf6, 0x33, 0xff, 0xb9, 0x00, 0x7d, + 0xf3, 0xa5, 0xe8, 0xcb, 0x5c, 0xe8, 0xc8, 0x5c, + 0xbe, 0x00, 0x05, 0x33, 0xff, 0xb9, 0x80, 0x7a, + 0xf3, 0xa5, 0xe8, 0xbb, 0x5c, 0xe8, 0xb8, 0x5c, + 0x33, 0xf6, 0x33, 0xff, 0xb9, 0x00, 0x7d, 0xf3, + 0xa5, 0x07, 0x1f, 0xc3, 0x80, 0x3e, 0xf0, 0xdb, + 0x01, 0x75, 0x07, 0xbb, 0x25, 0x5e, 0xe8, 0xe4, + 0x55, 0xc3, 0xc7, 0x06, 0xf3, 0xb4, 0x05, 0x00, + 0xb8, 0x23, 0x00, 0xbb, 0xa2, 0x00, 0xbf, 0x23, + 0x00, 0xbe, 0xae, 0x00, 0xb1, 0x0b, 0xb5, 0x03, + 0xe8, 0x58, 0x61, 0xc3, 0x80, 0x3e, 0xe9, 0xdb, + 0x01, 0x75, 0x3f, 0xb9, 0x59, 0x00, 0xb0, 0x05, + 0xb4, 0x02, 0xe8, 0x60, 0x5f, 0xb9, 0xbe, 0x03, + 0xe8, 0x4c, 0xd0, 0xe8, 0xed, 0x60, 0xc6, 0x06, + 0xcc, 0x64, 0x00, 0xc6, 0x06, 0xdc, 0x64, 0x01, + 0xc6, 0x06, 0xcb, 0x64, 0x00, 0xe8, 0xc1, 0xf3, + 0xc7, 0x06, 0xf3, 0xb4, 0x09, 0x00, 0xb8, 0xf0, + 0x00, 0xbb, 0xb6, 0x00, 0xbf, 0xe0, 0x00, 0xbe, + 0xb6, 0x00, 0xb1, 0x01, 0xb5, 0x04, 0xe8, 0x12, + 0x61, 0xc3, 0x80, 0x3e, 0xe7, 0xdb, 0x01, 0x74, + 0x0d, 0xe8, 0x34, 0xcc, 0xbb, 0x8a, 0xdb, 0xe8, + 0xcb, 0xc6, 0xe8, 0xe0, 0xc6, 0xc3, 0xbb, 0x94, + 0x58, 0xe8, 0x69, 0x55, 0xc3, 0xbb, 0x1e, 0x43, + 0xe8, 0x62, 0x55, 0xc3, 0xe8, 0x60, 0xd0, 0xe8, + 0xee, 0xf4, 0xc6, 0x07, 0x00, 0xe8, 0x74, 0x63, + 0xb9, 0x31, 0x00, 0xb0, 0x06, 0xb4, 0x0b, 0xe8, + 0xf3, 0x5e, 0xb9, 0x05, 0x00, 0xb0, 0x0d, 0xe8, + 0xf4, 0x5e, 0xb9, 0x5d, 0x03, 0xe8, 0xff, 0xcf, + 0xb0, 0x57, 0xe8, 0x1b, 0xdc, 0xb0, 0x06, 0xe8, + 0xc3, 0xce, 0xc3, 0xe8, 0xb0, 0x59, 0xc3, 0xbb, + 0x51, 0x5a, 0xe8, 0x28, 0x55, 0xc3, 0xbb, 0x98, + 0x5a, 0xe8, 0x21, 0x55, 0xc3, 0xb9, 0x0f, 0x00, + 0xb0, 0x07, 0xb4, 0x04, 0xe8, 0xbe, 0x5e, 0xb9, + 0x74, 0x03, 0xe8, 0xcb, 0xcf, 0xb9, 0x37, 0x00, + 0xb0, 0x01, 0xb4, 0x04, 0xe8, 0xae, 0x5e, 0xb9, + 0x18, 0x00, 0xb0, 0x0c, 0xe8, 0xaf, 0x5e, 0xbb, + 0x47, 0x33, 0xc7, 0x07, 0x75, 0x03, 0xb0, 0x01, + 0xe8, 0xbd, 0xce, 0xc6, 0x06, 0x45, 0x33, 0xd0, + 0xb0, 0x01, 0xbe, 0x76, 0x03, 0xbb, 0xe5, 0x67, + 0xe8, 0x84, 0xca, 0xc7, 0x06, 0xaf, 0x64, 0xc6, + 0x00, 0xc7, 0x06, 0xb1, 0x64, 0xba, 0x00, 0xc6, + 0x06, 0xcd, 0x64, 0x00, 0xc6, 0x06, 0xdc, 0x64, + 0x00, 0xc6, 0x06, 0xcb, 0x64, 0x01, 0xe8, 0xe8, + 0xf2, 0xb9, 0x03, 0x00, 0xe8, 0xb4, 0x65, 0xe8, + 0xab, 0x50, 0xe8, 0x2e, 0xba, 0xb8, 0x64, 0x00, + 0xe8, 0xb9, 0xd3, 0xc7, 0x06, 0xf3, 0xb4, 0x28, + 0x00, 0xe8, 0x69, 0x61, 0xe8, 0x28, 0xcb, 0xbb, + 0x20, 0x7f, 0xe8, 0x10, 0xc6, 0xe8, 0xc6, 0xdb, + 0xb0, 0x1d, 0xe8, 0x7b, 0xdb, 0xbb, 0x5c, 0xe4, + 0xe8, 0x1b, 0xba, 0xb9, 0x06, 0x00, 0xe8, 0x82, + 0x65, 0xbb, 0x47, 0x33, 0xc7, 0x07, 0xbc, 0x03, + 0xc7, 0x06, 0xf3, 0xb4, 0x01, 0x00, 0xe8, 0x33, + 0x61, 0xb0, 0x01, 0xe8, 0x84, 0xce, 0xc6, 0x06, + 0x45, 0x33, 0xd1, 0xb0, 0x01, 0xbe, 0xbd, 0x03, + 0xbb, 0xc4, 0x8b, 0xe8, 0x09, 0xca, 0xe8, 0xad, + 0x5e, 0xe8, 0x60, 0x0c, 0xc3, 0xb9, 0xbf, 0x03, + 0xe8, 0xf4, 0xce, 0xe8, 0x95, 0x5f, 0xc7, 0x06, + 0xf3, 0xb4, 0x08, 0x00, 0xb8, 0x28, 0x00, 0xbb, + 0x98, 0x00, 0xbf, 0x28, 0x00, 0xbe, 0x9b, 0x00, + 0xb1, 0x0b, 0xb5, 0x03, 0xe8, 0xcc, 0x5f, 0xc3, + 0xe8, 0xbb, 0x58, 0xc3, 0xb9, 0xc0, 0x03, 0xe8, + 0xee, 0xce, 0xbb, 0x11, 0x55, 0xe8, 0x2d, 0x54, + 0xc3, 0xbb, 0xc6, 0x5a, 0xe8, 0x26, 0x54, 0xc3, + 0xbb, 0xed, 0x5a, 0xe8, 0x1f, 0x54, 0xc3, 0xbb, + 0xed, 0x5a, 0xe8, 0x18, 0x54, 0xc3, 0xb9, 0x20, + 0x00, 0xb0, 0x18, 0xb4, 0x05, 0xe8, 0xb5, 0x5d, + 0xb9, 0x5e, 0x03, 0xe8, 0xa1, 0xce, 0xe8, 0x42, + 0x5f, 0xbb, 0x47, 0x33, 0xc7, 0x07, 0x5f, 0x03, + 0xb0, 0x01, 0xe8, 0xca, 0xcd, 0xe8, 0x8c, 0x5e, + 0xb0, 0x54, 0xe8, 0xd3, 0xda, 0xb0, 0x01, 0xe8, + 0x7b, 0xcd, 0xc6, 0x06, 0xe2, 0xdb, 0x02, 0xc3, + 0x80, 0x3e, 0xe2, 0xdb, 0x00, 0x74, 0x10, 0xbb, + 0x22, 0x57, 0xe8, 0xd8, 0x53, 0xe8, 0xd5, 0xd2, + 0xbb, 0x2a, 0x57, 0xe8, 0xcf, 0x53, 0xc3, 0xe8, + 0x10, 0xcb, 0x80, 0x3e, 0xda, 0xdb, 0x01, 0x74, + 0x0a, 0xbb, 0x40, 0xdb, 0xe8, 0x16, 0xc5, 0xe8, + 0x2b, 0xc5, 0xc3, 0xbb, 0x4c, 0xdb, 0xe8, 0x0c, + 0xc5, 0xe8, 0x21, 0xc5, 0xc3, 0xbb, 0x27, 0x5b, + 0xe8, 0xaa, 0x53, 0xc3, 0x80, 0x3e, 0xda, 0xdb, + 0x01, 0x75, 0x1a, 0xc7, 0x06, 0xf3, 0xb4, 0x05, + 0x00, 0xb8, 0x78, 0x00, 0xbb, 0xc7, 0x00, 0xbf, + 0x80, 0x00, 0xbe, 0xb0, 0x00, 0xb1, 0x13, 0xb5, + 0x01, 0xe8, 0x17, 0x5f, 0xc3, 0xe8, 0xca, 0xca, + 0xbb, 0xe9, 0x5f, 0xe8, 0xef, 0xc4, 0xbe, 0x0d, + 0x01, 0xbf, 0xaf, 0x00, 0xc6, 0x06, 0xc3, 0x64, + 0x04, 0xe8, 0x47, 0xe9, 0xe8, 0xb3, 0xca, 0xbb, + 0x56, 0xdb, 0xe8, 0xc0, 0xc4, 0xe8, 0xd5, 0xc4, + 0xc3, 0xe8, 0x32, 0xcb, 0xbb, 0xa6, 0xda, 0xe8, + 0xb3, 0xc4, 0x53, 0xe8, 0xc7, 0xc4, 0x5b, 0x81, + 0xfb, 0xb4, 0x01, 0x75, 0x50, 0x8b, 0x3e, 0xb1, + 0x64, 0x8b, 0x36, 0xaf, 0x64, 0x57, 0x56, 0xbf, + 0x9f, 0x00, 0xbe, 0xbd, 0x00, 0xe8, 0x13, 0xe9, + 0xb8, 0x01, 0x01, 0xe8, 0xf0, 0xd1, 0xb9, 0x05, + 0x00, 0xb0, 0x02, 0xb4, 0x0a, 0xe8, 0xd5, 0x5c, + 0xb0, 0x13, 0xe8, 0xd9, 0x5c, 0xbb, 0x47, 0x33, + 0xc7, 0x07, 0x27, 0x02, 0xb9, 0x26, 0x02, 0xe8, + 0xd6, 0xcd, 0x5e, 0x5f, 0xc6, 0x06, 0xc3, 0x64, + 0x02, 0xe8, 0xe7, 0xe8, 0xb0, 0x13, 0xe8, 0xef, + 0xd9, 0xe8, 0xda, 0xca, 0xbb, 0xa6, 0xda, 0xe8, + 0x5b, 0xc4, 0xe8, 0x70, 0xc4, 0xc3, 0xe8, 0xfe, + 0xcd, 0xb0, 0x10, 0xe8, 0xda, 0xd9, 0xb0, 0x02, + 0xe8, 0x82, 0xcc, 0xe8, 0x82, 0xf2, 0xc6, 0x07, + 0x00, 0xe8, 0x08, 0x61, 0xb9, 0x05, 0x00, 0xb0, + 0x0c, 0xb4, 0x06, 0xe8, 0x87, 0x5c, 0xb9, 0x23, + 0x02, 0xe8, 0x9b, 0xcd, 0xc3, 0xe8, 0x56, 0x57, + 0xc3, 0xe8, 0x52, 0x57, 0xc3, 0xe8, 0x4e, 0x57, + 0xc3, 0xbb, 0xd6, 0x3e, 0xe8, 0xc6, 0x52, 0xc3, + 0xe8, 0x43, 0x57, 0xc3, 0xe8, 0x1d, 0xcb, 0xbb, + 0xbd, 0x33, 0xe8, 0x28, 0xc4, 0xc6, 0x06, 0xe6, + 0x1c, 0xd0, 0xbe, 0xe4, 0x61, 0xbb, 0xae, 0x49, + 0xe8, 0xd5, 0x54, 0xb9, 0x05, 0x00, 0xb0, 0x03, + 0xb4, 0x07, 0xe8, 0x48, 0x5c, 0xbb, 0x47, 0x33, + 0xc7, 0x47, 0x02, 0x9b, 0x02, 0xb0, 0x02, 0xe8, + 0x5e, 0xcc, 0xbb, 0x47, 0x33, 0xc7, 0x47, 0x02, + 0x9c, 0x02, 0xb0, 0x02, 0xe8, 0x51, 0xcc, 0xe8, + 0xcc, 0x5c, 0xc6, 0x06, 0x33, 0x33, 0x01, 0xc6, + 0x06, 0xdd, 0x1c, 0x02, 0xc6, 0x06, 0xdb, 0x1c, + 0x01, 0xe8, 0x04, 0xf2, 0xc6, 0x47, 0x02, 0x32, + 0xe8, 0x89, 0x60, 0xc7, 0x06, 0x30, 0x32, 0x78, + 0x00, 0xc7, 0x06, 0x2e, 0x32, 0x5a, 0x00, 0xc7, + 0x06, 0x2c, 0x32, 0xae, 0x00, 0xe8, 0x76, 0xda, + 0xe8, 0xbd, 0x5a, 0xe8, 0xae, 0xca, 0xbb, 0xc7, + 0x36, 0xe8, 0xb9, 0xc3, 0xb9, 0x52, 0x00, 0xb0, + 0x13, 0xb4, 0x0e, 0xe8, 0xe7, 0x5b, 0xbb, 0x47, + 0x33, 0xc7, 0x47, 0x02, 0x9d, 0x02, 0xb0, 0x02, + 0xe8, 0x3f, 0xcc, 0xe8, 0xc2, 0xf1, 0xc6, 0x47, + 0x03, 0x00, 0xe8, 0x75, 0x60, 0xc7, 0x06, 0xec, + 0xdb, 0x00, 0x00, 0xe8, 0x68, 0x5c, 0xbb, 0x79, + 0x37, 0xe8, 0x89, 0xc3, 0xb0, 0x0f, 0xe8, 0x98, + 0xcb, 0xb0, 0x08, 0xe8, 0x9f, 0xcb, 0xc3, 0xc7, + 0x06, 0xf3, 0xb4, 0x0d, 0x00, 0xb8, 0x00, 0x00, + 0xbb, 0xac, 0x00, 0xbf, 0x12, 0x00, 0xbe, 0xad, + 0x00, 0xb1, 0x01, 0xb5, 0x02, 0xe8, 0x83, 0x5d, + 0xc3, 0xc6, 0x06, 0xdc, 0x64, 0x01, 0xc6, 0x06, + 0xcb, 0x64, 0x00, 0xc7, 0x06, 0xf3, 0xb4, 0x0e, + 0x00, 0xb8, 0x18, 0x01, 0xbb, 0xc6, 0x00, 0xbf, + 0xe3, 0x00, 0xbe, 0xb8, 0x00, 0xb1, 0x01, 0xb5, + 0x04, 0xe8, 0x5f, 0x5d, 0xc3, 0xc7, 0x06, 0xf3, + 0xb4, 0x0f, 0x00, 0xb8, 0x3f, 0x01, 0xbb, 0xa2, + 0x00, 0xbf, 0x06, 0x01, 0xbe, 0xb0, 0x00, 0xb1, + 0x01, 0xb5, 0x04, 0xe8, 0x45, 0x5d, 0xc3, 0xc7, + 0x06, 0xf3, 0xb4, 0x0c, 0x00, 0xb8, 0x3f, 0x01, + 0xbb, 0x98, 0x00, 0xbf, 0x2d, 0x01, 0xbe, 0xaf, + 0x00, 0xb1, 0x01, 0xb5, 0x04, 0xe8, 0x2b, 0x5d, + 0xc3, 0xe8, 0x9b, 0xcc, 0xb9, 0x05, 0x00, 0xb0, + 0x0c, 0xb4, 0x09, 0xe8, 0x37, 0x5b, 0xb0, 0x12, + 0xe8, 0x6d, 0xd8, 0xb0, 0x0d, 0xe8, 0x15, 0xcb, + 0xe8, 0x15, 0xf1, 0xc6, 0x47, 0x01, 0x00, 0xe8, + 0x9a, 0x5f, 0xb9, 0x25, 0x02, 0xe8, 0x37, 0xcc, + 0xc3, 0xe8, 0xf2, 0x55, 0xc3, 0xe8, 0x6f, 0xcc, + 0xe8, 0xfd, 0xf0, 0xc6, 0x47, 0x02, 0x00, 0xe8, + 0x82, 0x5f, 0xb9, 0x05, 0x00, 0xb0, 0x0c, 0xb4, + 0x07, 0xe8, 0x01, 0x5b, 0xb9, 0xa4, 0x02, 0xe8, + 0x15, 0xcc, 0xbb, 0xb0, 0x4a, 0xe8, 0x4d, 0x51, + 0xb0, 0x0f, 0xe8, 0xd8, 0xca, 0xb0, 0x33, 0xe8, + 0x26, 0xd8, 0xc3, 0xc3, 0xbb, 0x0f, 0x37, 0x80, + 0x3e, 0xa1, 0xdb, 0x01, 0x74, 0x03, 0xbb, 0xc2, + 0x36, 0xe8, 0x31, 0x51, 0xc3, 0xb9, 0xc7, 0x03, + 0xe8, 0xe5, 0xcb, 0xbb, 0x42, 0x35, 0xe8, 0x24, + 0x51, 0xc3, 0x80, 0x3e, 0xa1, 0xdb, 0x01, 0x74, + 0x53, 0xb9, 0x38, 0x00, 0xb0, 0x08, 0xb4, 0x0e, + 0xe8, 0xba, 0x5a, 0xb0, 0x0c, 0xe8, 0xc5, 0x5a, + 0xb9, 0x31, 0x00, 0xb0, 0x0a, 0xe8, 0xb6, 0x5a, + 0xc6, 0x06, 0x35, 0x33, 0x10, 0xc6, 0x06, 0x36, + 0x33, 0x18, 0xb8, 0x52, 0x46, 0xa3, 0x37, 0x33, + 0xb8, 0x5b, 0x7b, 0xa3, 0x39, 0x33, 0xe8, 0xee, + 0xcb, 0xb9, 0x4b, 0x02, 0xe8, 0x00, 0xcc, 0xe8, + 0xba, 0xcb, 0x8b, 0x36, 0xaf, 0x64, 0x8b, 0x3e, + 0xb1, 0x64, 0xc6, 0x06, 0xc3, 0x64, 0x02, 0xe8, + 0xa9, 0xe6, 0xe8, 0xd0, 0xcf, 0xbb, 0x68, 0x36, + 0xe8, 0xca, 0x50, 0xc3, 0x80, 0x3e, 0xa2, 0xdb, + 0x01, 0x74, 0x73, 0xb9, 0x1a, 0x00, 0xb0, 0x0d, + 0xb4, 0x0c, 0xe8, 0x60, 0x5a, 0xb0, 0x0f, 0xe8, + 0x64, 0x5a, 0xb0, 0x17, 0xe8, 0x66, 0x5a, 0xb0, + 0x19, 0xe8, 0x68, 0x5a, 0xb0, 0x20, 0xe8, 0x6a, + 0x5a, 0xb0, 0x22, 0xe8, 0x6c, 0x5a, 0xb0, 0x24, + 0xe8, 0x6e, 0x5a, 0xb9, 0x4e, 0x02, 0xe8, 0x2e, + 0xcb, 0xc7, 0x06, 0xaf, 0x64, 0xcc, 0x00, 0xc7, + 0x06, 0xb1, 0x64, 0xb2, 0x00, 0xc6, 0x06, 0xc3, + 0x64, 0x03, 0xc6, 0x06, 0xcb, 0x64, 0x01, 0xc6, + 0x06, 0xcd, 0x64, 0x01, 0xb9, 0x3b, 0x00, 0xb0, + 0x01, 0xb4, 0x0a, 0xe8, 0x17, 0x5a, 0xb9, 0x3c, + 0x00, 0xb0, 0x10, 0xe8, 0x18, 0x5a, 0xb9, 0x4f, + 0x02, 0xe8, 0x23, 0xcb, 0xe8, 0x57, 0xcf, 0xbb, + 0x2e, 0x37, 0xe8, 0x58, 0x50, 0xc6, 0x06, 0xa2, + 0xdb, 0x01, 0xe8, 0x40, 0x4d, 0xc3, 0xbb, 0x66, + 0x37, 0xe8, 0x49, 0x50, 0xc3, 0x80, 0x3e, 0x9e, + 0xdb, 0x01, 0x75, 0x07, 0xbb, 0xe8, 0x35, 0xe8, + 0x3b, 0x50, 0xc3, 0xc6, 0x06, 0x9e, 0xdb, 0x01, + 0xe8, 0x34, 0xcb, 0xe8, 0xc2, 0xef, 0xc6, 0x47, + 0x02, 0x00, 0x53, 0xe8, 0x46, 0x5e, 0xb9, 0x15, + 0x00, 0xb0, 0x09, 0xb4, 0x02, 0xe8, 0xc5, 0x59, + 0xb9, 0x22, 0x00, 0xb0, 0x15, 0xe8, 0xc6, 0x59, + 0xb9, 0x1a, 0x00, 0xb0, 0x1e, 0xe8, 0xc5, 0x59, + 0xb9, 0x28, 0x02, 0xe8, 0xb4, 0xca, 0x5b, 0xc6, + 0x47, 0x02, 0x12, 0xe8, 0xda, 0x5d, 0xe8, 0xd3, + 0xca, 0xb0, 0x14, 0xe8, 0xda, 0xd6, 0xc3, 0xbb, + 0x2a, 0x3f, 0xe8, 0xf0, 0x4f, 0xc3, 0xc7, 0x06, + 0xf3, 0xb4, 0x0b, 0x00, 0xb8, 0xe4, 0x00, 0xbb, + 0x6d, 0x00, 0xbf, 0x8a, 0x00, 0xbe, 0xa3, 0x00, + 0xb1, 0x0b, 0xb5, 0x03, 0xe8, 0x64, 0x5b, 0xc3, + 0xc7, 0x06, 0xf3, 0xb4, 0x0d, 0x00, 0xb8, 0x3f, + 0x01, 0xbb, 0xb5, 0x00, 0xbf, 0x05, 0x01, 0xbe, + 0xb7, 0x00, 0xb1, 0x01, 0xb5, 0x04, 0xe8, 0x4a, + 0x5b, 0xc3, 0xe8, 0x28, 0x00, 0xe8, 0xb7, 0xca, + 0xe8, 0x45, 0xef, 0xc6, 0x47, 0x01, 0x00, 0xe8, + 0xca, 0x5d, 0xb9, 0x05, 0x00, 0xb0, 0x0a, 0xb4, + 0x09, 0xe8, 0x49, 0x59, 0xb9, 0x1f, 0x02, 0xe8, + 0x5d, 0xca, 0xb0, 0x0f, 0xe8, 0x79, 0xd6, 0xb0, + 0x09, 0xe8, 0x21, 0xc9, 0xc3, 0xbe, 0xcb, 0x00, + 0xbf, 0xa9, 0x00, 0x81, 0x3e, 0xaf, 0x64, 0xcb, + 0x00, 0x75, 0x1a, 0x81, 0x3e, 0xb1, 0x64, 0xab, + 0x00, 0x75, 0x12, 0xc6, 0x06, 0xc3, 0x64, 0x01, + 0x57, 0x56, 0xe8, 0x46, 0xe5, 0xb8, 0x09, 0x00, + 0xe8, 0x4f, 0xce, 0x5e, 0x5f, 0xc6, 0x06, 0xc3, + 0x64, 0x02, 0xe8, 0x36, 0xe5, 0xc3, 0xbb, 0x54, + 0x3f, 0xe8, 0x59, 0x4f, 0xc3, 0xbb, 0x85, 0x3f, + 0xe8, 0x52, 0x4f, 0xc3, 0xc7, 0x06, 0xf3, 0xb4, + 0x0b, 0x00, 0xb8, 0x3f, 0x01, 0xbb, 0xc6, 0x00, + 0xbf, 0x2c, 0x01, 0xbe, 0xc4, 0x00, 0xb1, 0x01, + 0xb5, 0x04, 0xe8, 0xc6, 0x5a, 0x80, 0x3e, 0x9c, + 0xdb, 0x01, 0x74, 0x64, 0xe8, 0xd8, 0x54, 0xe8, + 0x26, 0xe5, 0x80, 0x3e, 0xc6, 0x64, 0x00, 0x75, + 0xf6, 0xbb, 0x47, 0x33, 0xc7, 0x07, 0x20, 0x02, + 0xb0, 0x01, 0xc7, 0x06, 0x23, 0xc4, 0x01, 0x00, + 0xc7, 0x06, 0x3b, 0x33, 0x15, 0x00, 0xc7, 0x06, + 0x3d, 0x33, 0x2a, 0x00, 0xc6, 0x06, 0xce, 0x00, + 0x02, 0xe8, 0x29, 0xc9, 0x72, 0x33, 0xa1, 0x3f, + 0x33, 0x0b, 0xc0, 0x74, 0x10, 0x3d, 0x30, 0x00, + 0x77, 0x0b, 0xb0, 0x01, 0xb9, 0x2c, 0x00, 0xba, + 0x33, 0x00, 0xe8, 0x81, 0xc1, 0xe8, 0x2e, 0x59, + 0x80, 0x3e, 0x27, 0xc4, 0x01, 0x75, 0x11, 0xc6, + 0x06, 0xcf, 0x00, 0x00, 0xe8, 0xca, 0xc6, 0xbb, + 0xb2, 0xda, 0xe8, 0x28, 0xc0, 0xe8, 0x3d, 0xc0, + 0xc3, 0xc6, 0x06, 0x9c, 0xdb, 0x01, 0xbb, 0x63, + 0x35, 0xb9, 0x3c, 0x00, 0xe8, 0xbd, 0x4f, 0xe8, + 0x04, 0x59, 0xb9, 0x11, 0x00, 0xb0, 0x05, 0xb4, + 0x08, 0xe8, 0x59, 0x58, 0xbb, 0x47, 0x33, 0xc7, + 0x07, 0x21, 0x02, 0xb0, 0x01, 0xe8, 0xb9, 0xc8, + 0xe8, 0x35, 0xee, 0xc6, 0x07, 0x10, 0xe8, 0xbb, + 0x5c, 0xb0, 0x02, 0xe8, 0x1b, 0xc8, 0xe8, 0x88, + 0xc6, 0xbb, 0x17, 0x09, 0xe8, 0xfe, 0xbf, 0xc3, + 0xbb, 0x2a, 0x3f, 0xe8, 0x87, 0x4e, 0xc3, 0xc7, + 0x06, 0xf3, 0xb4, 0x0c, 0x00, 0xb8, 0x00, 0x00, + 0xbb, 0xbd, 0x00, 0xbf, 0x1e, 0x00, 0xbe, 0xbd, + 0x00, 0xb1, 0x01, 0xb5, 0x02, 0xe8, 0xfb, 0x59, + 0xc3, 0xbb, 0xec, 0x3f, 0xe8, 0x66, 0x4e, 0xc3, + 0xe8, 0x64, 0xc9, 0xe8, 0xf2, 0xed, 0xc6, 0x07, + 0x00, 0xe8, 0x78, 0x5c, 0xb9, 0x05, 0x00, 0xb0, + 0x0b, 0xb4, 0x09, 0xe8, 0xf7, 0x57, 0xb9, 0x7d, + 0x02, 0xe8, 0x0b, 0xc9, 0xb0, 0x07, 0xe8, 0xd4, + 0xc7, 0xb0, 0x31, 0xe8, 0x22, 0xd5, 0xc3, 0x80, + 0x3e, 0x9f, 0xdb, 0x01, 0x74, 0x07, 0xbb, 0xe1, + 0x34, 0xe8, 0x31, 0x4e, 0xc3, 0xbb, 0x2e, 0x40, + 0xe8, 0x2a, 0x4e, 0xc3, 0xbb, 0xe1, 0x34, 0xe8, + 0x23, 0x4e, 0xc3, 0xe8, 0xa0, 0x52, 0xc3, 0x80, + 0x3e, 0x9f, 0xdb, 0x01, 0x74, 0x07, 0xbb, 0xe1, + 0x34, 0xe8, 0x11, 0x4e, 0xc3, 0x80, 0x3e, 0xa0, + 0xdb, 0x01, 0x75, 0x07, 0xbb, 0x31, 0x3e, 0xe8, + 0x03, 0x4e, 0xc3, 0xbe, 0xad, 0x00, 0xbf, 0x8a, + 0x00, 0xc6, 0x06, 0xc3, 0x64, 0x02, 0xe8, 0xca, + 0xe3, 0xb9, 0x1c, 0x00, 0xb0, 0x05, 0xb4, 0x08, + 0xe8, 0x92, 0x57, 0xb9, 0x47, 0x02, 0xe8, 0x7e, + 0xc8, 0xb9, 0x48, 0x02, 0xe8, 0x7b, 0xc8, 0xe8, + 0x52, 0x5a, 0xb8, 0x00, 0xa0, 0xb9, 0x00, 0x7d, + 0xe8, 0x7d, 0x5a, 0xa1, 0xb1, 0x32, 0xb9, 0x00, + 0x7d, 0xe8, 0x74, 0x5a, 0xa1, 0xb3, 0x32, 0xb9, + 0x00, 0x7d, 0xe8, 0x6b, 0x5a, 0xa1, 0xb5, 0x32, + 0xb9, 0x00, 0x7d, 0xe8, 0x62, 0x5a, 0xb8, 0x64, + 0x00, 0xe8, 0xb8, 0xcc, 0xe8, 0x32, 0x5a, 0xb9, + 0x48, 0x00, 0xb0, 0x12, 0xb4, 0x08, 0xe8, 0x4c, + 0x57, 0xb9, 0x49, 0x00, 0xb0, 0x27, 0xe8, 0x4d, + 0x57, 0xb9, 0x49, 0x02, 0xe8, 0x33, 0xc8, 0xe8, + 0xd1, 0x58, 0xc7, 0x06, 0xf3, 0xb4, 0x0b, 0x00, + 0xc6, 0x06, 0xdc, 0x1c, 0x01, 0xe8, 0x57, 0x59, + 0xc7, 0x06, 0xaf, 0x64, 0xc2, 0x00, 0xc7, 0x06, + 0xb1, 0x64, 0xa0, 0x00, 0xc6, 0x06, 0xc3, 0x64, + 0x02, 0xc6, 0x06, 0xcb, 0x64, 0x00, 0xc6, 0x06, + 0xcd, 0x64, 0x00, 0xe8, 0x20, 0xe9, 0xb9, 0x1c, + 0x00, 0xb0, 0x02, 0xb4, 0x08, 0xe8, 0x05, 0x57, + 0xb9, 0x4a, 0x02, 0xc6, 0x06, 0xdc, 0x1c, 0x01, + 0xe8, 0x0d, 0xc8, 0xbe, 0x8a, 0x00, 0xbf, 0xa3, + 0x00, 0xc6, 0x06, 0xc3, 0x64, 0x03, 0xe8, 0x1a, + 0xe3, 0xbb, 0x50, 0x36, 0xe8, 0x3e, 0x4d, 0xc6, + 0x06, 0xa0, 0xdb, 0x01, 0xe8, 0x26, 0x4a, 0xc3, + 0xc7, 0x06, 0xf3, 0xb4, 0x0b, 0x00, 0xb8, 0x00, + 0x00, 0xbb, 0x7c, 0x00, 0xbf, 0x3c, 0x00, 0xbe, + 0xaa, 0x00, 0xb1, 0x01, 0xb5, 0x02, 0xe8, 0xaa, + 0x58, 0xc3, 0xbb, 0x47, 0x40, 0xe8, 0x15, 0x4d, + 0xc3, 0xbb, 0x6d, 0x40, 0xe8, 0x0e, 0x4d, 0xc3, + 0x80, 0x3e, 0x99, 0xdb, 0x01, 0x75, 0x07, 0xbb, + 0x1f, 0x35, 0xe8, 0x00, 0x4d, 0xc3, 0xc6, 0x06, + 0x99, 0xdb, 0x01, 0xb9, 0x39, 0x00, 0xb0, 0x06, + 0xb4, 0x06, 0xe8, 0x98, 0x56, 0xb0, 0x0c, 0xe8, + 0x9c, 0x56, 0xb9, 0x3a, 0x00, 0xb0, 0x11, 0xe8, + 0x9b, 0x56, 0xb9, 0x05, 0x00, 0xb0, 0x25, 0xe8, + 0x9a, 0x56, 0xb9, 0x18, 0x02, 0xe8, 0x7f, 0xc7, + 0xe8, 0xa9, 0xc7, 0xb8, 0x0a, 0x00, 0xe8, 0xb1, + 0xcb, 0x8b, 0x3e, 0xb1, 0x64, 0x47, 0x8b, 0x36, + 0xaf, 0x64, 0xe8, 0x96, 0xe2, 0xbb, 0xc3, 0x30, + 0xe8, 0x2a, 0xbe, 0xb0, 0x08, 0xe8, 0x98, 0xd3, + 0xc3, 0x80, 0x3e, 0xb2, 0xdb, 0x01, 0x75, 0x0a, + 0xbb, 0x1d, 0x41, 0xe8, 0xa7, 0x4c, 0xe8, 0x43, + 0x00, 0xc3, 0xbb, 0x8a, 0x40, 0xe8, 0x9d, 0x4c, + 0xe8, 0x39, 0x00, 0xbb, 0x98, 0x40, 0xe8, 0x94, + 0x4c, 0xbb, 0xa7, 0x40, 0xbe, 0xe1, 0xcd, 0xe8, + 0x30, 0x00, 0xbb, 0xb6, 0x40, 0xe8, 0x85, 0x4c, + 0xbb, 0xce, 0x40, 0xbe, 0xac, 0xcd, 0xe8, 0x21, + 0x00, 0xbb, 0xe8, 0x40, 0xe8, 0x76, 0x4c, 0xbb, + 0x0f, 0x41, 0xbe, 0xe8, 0xcd, 0xe8, 0x12, 0x00, + 0xe8, 0x6a, 0xcb, 0xe8, 0x06, 0x00, 0xc6, 0x06, + 0xb2, 0xdb, 0x01, 0xc3, 0xbb, 0x91, 0x40, 0xbe, + 0xf8, 0xcd, 0xc6, 0x06, 0xe6, 0x1c, 0xe5, 0xe8, + 0xfc, 0x4e, 0xc3, 0xbb, 0x33, 0x41, 0xe8, 0x4c, + 0x4c, 0xc3, 0xc7, 0x06, 0xf3, 0xb4, 0x14, 0x00, + 0xb8, 0x00, 0x00, 0xbb, 0xb9, 0x00, 0xbf, 0x14, + 0x00, 0xbe, 0xb9, 0x00, 0xb1, 0x01, 0xb5, 0x02, + 0xe8, 0xc0, 0x57, 0xc3, 0xc7, 0x06, 0xf3, 0xb4, + 0x0b, 0x00, 0xb8, 0x00, 0x00, 0xbb, 0xaa, 0x00, + 0xbf, 0x3c, 0x00, 0xbe, 0xaa, 0x00, 0xb1, 0x01, + 0xb5, 0x02, 0xe8, 0xa6, 0x57, 0xc3, 0xc7, 0x06, + 0xf3, 0xb4, 0x12, 0x00, 0xb8, 0xf6, 0x00, 0xbb, + 0xc7, 0x00, 0xbf, 0xc9, 0x00, 0xbe, 0xc0, 0x00, + 0xb1, 0x01, 0xb5, 0x04, 0xe8, 0x8c, 0x57, 0xc3, + 0xe8, 0xfc, 0xc6, 0xe8, 0x8a, 0xeb, 0xc6, 0x47, + 0x02, 0x00, 0xe8, 0x0f, 0x5a, 0xb9, 0x22, 0x00, + 0xb0, 0x06, 0xb4, 0x06, 0xe8, 0x8e, 0x55, 0xb9, + 0x17, 0x02, 0xe8, 0xa2, 0xc6, 0xb0, 0x0b, 0xe8, + 0xbe, 0xd2, 0xb0, 0x01, 0xe8, 0x66, 0xc5, 0xc7, + 0x06, 0xf3, 0xb4, 0x0f, 0x00, 0xe8, 0x60, 0xeb, + 0xc6, 0x07, 0x00, 0xc7, 0x06, 0xf3, 0xb4, 0x10, + 0x00, 0xe8, 0x01, 0x00, 0xc3, 0xfe, 0x06, 0x98, + 0xdb, 0x80, 0x3e, 0x98, 0xdb, 0x02, 0x73, 0x01, + 0xc3, 0xc7, 0x06, 0xf3, 0xb4, 0x0f, 0x00, 0xb0, + 0x01, 0xe8, 0x39, 0xc5, 0xc7, 0x06, 0xf3, 0xb4, + 0x10, 0x00, 0xc3, 0xe8, 0xa1, 0xc6, 0xe8, 0x2f, + 0xeb, 0xc6, 0x47, 0x01, 0x00, 0xc6, 0x47, 0x03, + 0x00, 0xe8, 0xb0, 0x59, 0xb9, 0x21, 0x00, 0xb0, + 0x07, 0xb4, 0x09, 0xe8, 0x2f, 0x55, 0xb9, 0x16, + 0x02, 0xe8, 0x2e, 0xc6, 0xb0, 0x0a, 0xe8, 0x5f, + 0xd2, 0xb0, 0x02, 0xe8, 0x07, 0xc5, 0xe8, 0x07, + 0xeb, 0xc6, 0x47, 0x01, 0x0a, 0xe8, 0x48, 0x59, + 0xe8, 0x41, 0xc6, 0xc7, 0x06, 0xf3, 0xb4, 0x0f, + 0x00, 0xe8, 0xf4, 0xea, 0xc6, 0x47, 0x01, 0x00, + 0xc7, 0x06, 0xf3, 0xb4, 0x10, 0x00, 0xe8, 0x94, + 0xff, 0xc3, 0xe8, 0xe3, 0xea, 0xb0, 0x00, 0x88, + 0x07, 0xc7, 0x06, 0xf3, 0xb4, 0x0f, 0x00, 0xc6, + 0x06, 0xdc, 0x1c, 0x01, 0xe8, 0x10, 0x57, 0xb9, + 0x38, 0x02, 0xc6, 0x06, 0xdc, 0x1c, 0x01, 0xe8, + 0xcd, 0xc5, 0xc7, 0x06, 0xaf, 0x64, 0x73, 0x00, + 0xc7, 0x06, 0xb1, 0x64, 0xb4, 0x00, 0xc6, 0x06, + 0xc3, 0x64, 0x03, 0xc6, 0x06, 0xcb, 0x64, 0x01, + 0xc6, 0x06, 0xcd, 0x64, 0x01, 0xe8, 0xec, 0xc5, + 0xb9, 0x06, 0x00, 0xe8, 0x05, 0x5c, 0xc3, 0xc7, + 0x06, 0xf3, 0xb4, 0x0f, 0x00, 0xb8, 0x00, 0x00, + 0xbb, 0xac, 0x00, 0xbf, 0x1e, 0x00, 0xbe, 0xac, + 0x00, 0xb1, 0x01, 0xb5, 0x02, 0xe8, 0x83, 0x56, + 0xc3, 0xe8, 0x1a, 0x00, 0xc7, 0x06, 0xf3, 0xb4, + 0x13, 0x00, 0xb8, 0xdf, 0x00, 0xbb, 0xc7, 0x00, + 0xbf, 0xdf, 0x00, 0xbe, 0xc1, 0x00, 0xb1, 0x13, + 0xb5, 0x01, 0xe8, 0x66, 0x56, 0xc3, 0xe8, 0x7e, + 0x50, 0x81, 0x3e, 0xb1, 0x64, 0x95, 0x00, 0x76, + 0x09, 0xbe, 0x33, 0x00, 0xbf, 0x95, 0x00, 0xe8, + 0xaa, 0xe0, 0xbe, 0x5e, 0x00, 0xbf, 0x73, 0x00, + 0xc6, 0x06, 0xc3, 0x64, 0x04, 0xe8, 0x9c, 0xe0, + 0xc3, 0xe8, 0xda, 0xff, 0xe8, 0x2f, 0x4f, 0xc3, + 0xbb, 0x08, 0xdb, 0xe8, 0xff, 0xbb, 0x53, 0xe8, + 0x13, 0xbc, 0x5b, 0x81, 0xfb, 0x5d, 0x2c, 0x74, + 0x2e, 0x81, 0xfb, 0x9b, 0x2c, 0x75, 0x01, 0xc3, + 0xb8, 0x23, 0x01, 0xe8, 0x48, 0xc9, 0xb9, 0x34, + 0x00, 0xb0, 0x09, 0xb4, 0x0a, 0xe8, 0x2d, 0x54, + 0xb0, 0x0b, 0xe8, 0x31, 0x54, 0xb0, 0x0d, 0xe8, + 0x33, 0x54, 0xbb, 0x47, 0x33, 0xc7, 0x07, 0x39, + 0x02, 0xb0, 0x01, 0xe8, 0x83, 0xc4, 0xc3, 0xb8, + 0x23, 0x01, 0xe8, 0x21, 0xc9, 0xe8, 0xf8, 0xe9, + 0xc6, 0x07, 0x00, 0xe8, 0x7e, 0x58, 0xb9, 0x34, + 0x00, 0xb0, 0x09, 0xb4, 0x0a, 0xe8, 0xfd, 0x53, + 0xb0, 0x0b, 0xe8, 0x01, 0x54, 0xb0, 0x0d, 0xe8, + 0x03, 0x54, 0xb9, 0x35, 0x00, 0xb0, 0x20, 0xb4, + 0x0a, 0xe8, 0x00, 0x54, 0xbb, 0x47, 0x33, 0xc7, + 0x07, 0x3a, 0x02, 0xb0, 0x01, 0xe8, 0x49, 0xc4, + 0xe8, 0x2b, 0xc9, 0xbb, 0x1f, 0x55, 0xe8, 0x2c, + 0x4a, 0xb0, 0x05, 0xe8, 0xb7, 0xc3, 0xc6, 0x06, + 0xb0, 0xdb, 0x01, 0xc3, 0xbb, 0x0f, 0x57, 0xe8, + 0x1b, 0x4a, 0xc3, 0xe8, 0x19, 0xc5, 0xe8, 0xa7, + 0xe9, 0xc6, 0x47, 0x01, 0x00, 0xe8, 0x2c, 0x58, + 0xb9, 0x05, 0x00, 0xb0, 0x0a, 0xb4, 0x08, 0xe8, + 0xab, 0x53, 0xb9, 0x31, 0x02, 0xe8, 0xcd, 0xc4, + 0xb0, 0x1a, 0xe8, 0xdb, 0xd0, 0xb0, 0x06, 0xe8, + 0x83, 0xc3, 0xc3, 0xbb, 0x7e, 0x41, 0x80, 0x3e, + 0xb0, 0xdb, 0x01, 0x75, 0x03, 0xbb, 0xb1, 0x41, + 0xe8, 0xe2, 0x49, 0xc3, 0xc7, 0x06, 0xf3, 0xb4, + 0x12, 0x00, 0xb8, 0x5e, 0x00, 0xbb, 0x73, 0x00, + 0xbf, 0x5e, 0x00, 0xbe, 0x73, 0x00, 0xb1, 0x0b, + 0xb5, 0x03, 0xe8, 0x56, 0x55, 0xc3, 0xbb, 0xef, + 0x41, 0xe8, 0xc1, 0x49, 0xc3, 0xbb, 0x64, 0x41, + 0xe8, 0xba, 0x49, 0xc3, 0xb9, 0x43, 0x00, 0xb0, + 0x05, 0xb4, 0x03, 0xe8, 0x57, 0x53, 0xb9, 0xd7, + 0x03, 0xe8, 0x64, 0xc4, 0xbb, 0x55, 0x59, 0xe8, + 0xa3, 0x49, 0xc3, 0xe8, 0x20, 0x4e, 0xc3, 0xb9, + 0x42, 0x00, 0xb0, 0x05, 0xb4, 0x08, 0xe8, 0x3c, + 0x53, 0xb9, 0x43, 0x00, 0xb0, 0x0b, 0xe8, 0x3d, + 0x53, 0xb9, 0xd8, 0x03, 0xe8, 0x41, 0xc4, 0xbb, + 0x55, 0x59, 0xe8, 0x80, 0x49, 0xc3, 0xbb, 0xff, + 0x41, 0xe8, 0x79, 0x49, 0xc3, 0xbb, 0x3f, 0x42, + 0xe8, 0x72, 0x49, 0xc3, 0xbb, 0x51, 0x42, 0xe8, + 0x6b, 0x49, 0xc3, 0xbb, 0x67, 0x42, 0xe8, 0x64, + 0x49, 0xc3, 0xe8, 0xe1, 0x4d, 0xc3, 0xb9, 0x20, + 0x00, 0xb0, 0x07, 0xb4, 0x04, 0xe8, 0xfd, 0x52, + 0xb9, 0x72, 0x02, 0xe8, 0x0a, 0xc4, 0xe8, 0xdf, + 0xe8, 0xc6, 0x47, 0x01, 0x00, 0xe8, 0x64, 0x57, + 0xb0, 0x0c, 0xe8, 0xd0, 0xc2, 0xb0, 0x2d, 0xe8, + 0x1e, 0xd0, 0xbb, 0x04, 0x3b, 0xe8, 0x35, 0x49, + 0xc3, 0xbb, 0x3f, 0x42, 0xe8, 0x2e, 0x49, 0xc3, + 0xe8, 0x2c, 0xc4, 0xb0, 0x0d, 0xe8, 0x08, 0xd0, + 0xb0, 0x0e, 0xe8, 0xb0, 0xc2, 0xb9, 0x05, 0x00, + 0xb0, 0x0a, 0xb4, 0x08, 0xe8, 0xbe, 0x52, 0xe8, + 0xa6, 0xe8, 0xc6, 0x07, 0x00, 0xe8, 0x2c, 0x57, + 0xb9, 0x36, 0x02, 0xe8, 0xd7, 0xc3, 0xc3, 0xbb, + 0x89, 0x42, 0xe8, 0x00, 0x49, 0xc3, 0xb9, 0x0b, + 0x00, 0xb0, 0x04, 0xb4, 0x0c, 0xe8, 0x9d, 0x52, + 0xb9, 0x02, 0x02, 0xe8, 0x89, 0xc3, 0xe8, 0x7f, + 0xe8, 0xb0, 0x05, 0x88, 0x47, 0x02, 0x53, 0xe8, + 0x88, 0x57, 0x5b, 0xb0, 0x08, 0x88, 0x47, 0x04, + 0xe8, 0xa6, 0x57, 0xe8, 0xae, 0xc3, 0xb0, 0x0e, + 0xe8, 0x56, 0xc2, 0xb0, 0x0f, 0xe8, 0x51, 0xc2, + 0xb0, 0x10, 0xe8, 0x4c, 0xc2, 0xb0, 0x01, 0xe8, + 0x53, 0xc2, 0xc3, 0x80, 0x3e, 0x94, 0xdb, 0x01, + 0x74, 0x07, 0xbb, 0x63, 0x3e, 0xe8, 0xb5, 0x48, + 0xc3, 0x80, 0x3e, 0x95, 0xdb, 0x01, 0x75, 0x07, + 0xbb, 0x75, 0x3e, 0xe8, 0xa7, 0x48, 0xc3, 0xc6, + 0x06, 0x95, 0xdb, 0x01, 0xbb, 0x46, 0x67, 0xe8, + 0x20, 0xe8, 0x83, 0xc3, 0x03, 0xff, 0x37, 0x53, + 0xc7, 0x07, 0x3f, 0x01, 0xbe, 0xbc, 0x00, 0xbf, + 0xb3, 0x00, 0xe8, 0x5e, 0xde, 0xb9, 0x07, 0x00, + 0xb0, 0x10, 0xb4, 0x07, 0xe8, 0x26, 0x52, 0xb9, + 0x07, 0x02, 0xe8, 0x33, 0xc3, 0xb8, 0x96, 0x00, + 0xe8, 0x57, 0xc7, 0xbe, 0xa8, 0x00, 0xbf, 0xb3, + 0x00, 0xc6, 0x06, 0xc3, 0x64, 0x02, 0xe8, 0x3a, + 0xde, 0x5b, 0x8f, 0x07, 0xb0, 0x03, 0xe8, 0x3f, + 0xcf, 0xc3, 0xbb, 0x3f, 0x42, 0xe8, 0x55, 0x48, + 0xc3, 0xe8, 0xd2, 0x4c, 0xc3, 0xb9, 0x46, 0x00, + 0xb0, 0x06, 0xb4, 0x06, 0xe8, 0xee, 0x51, 0xb9, + 0x01, 0x02, 0xe8, 0xda, 0xc2, 0xe8, 0x7b, 0x53, + 0xc7, 0x06, 0xf3, 0xb4, 0x16, 0x00, 0xb8, 0x33, + 0x00, 0xbb, 0xb4, 0x00, 0xbf, 0x4e, 0x00, 0xbe, + 0xb4, 0x00, 0xb1, 0x01, 0xb5, 0x02, 0xe8, 0xb2, + 0x53, 0xc3, 0xb9, 0xfd, 0x01, 0xe8, 0xd8, 0xc2, + 0xbb, 0xce, 0x5d, 0xe8, 0x17, 0x48, 0xc3, 0xbb, + 0x64, 0x41, 0xe8, 0x10, 0x48, 0xc3, 0xbb, 0x64, + 0x41, 0xe8, 0x09, 0x48, 0xc3, 0xbb, 0x0f, 0x57, + 0xe8, 0x02, 0x48, 0xc3, 0xc7, 0x06, 0xf3, 0xb4, + 0x0f, 0x00, 0xb8, 0x9d, 0x00, 0xbb, 0xc7, 0x00, + 0xbf, 0x9d, 0x00, 0xbe, 0xb4, 0x00, 0xb1, 0x13, + 0xb5, 0x01, 0xe8, 0x76, 0x53, 0xc3, 0xc7, 0x06, + 0xf3, 0xb4, 0x15, 0x00, 0xb8, 0x00, 0x00, 0xbb, + 0xbb, 0x00, 0xbf, 0x30, 0x00, 0xbe, 0xbe, 0x00, + 0xb1, 0x01, 0xb5, 0x02, 0xe8, 0x5c, 0x53, 0xc3, + 0xc7, 0x06, 0xf3, 0xb4, 0x1b, 0x00, 0xb8, 0x54, + 0x00, 0xbb, 0xc7, 0x00, 0xbf, 0x84, 0x00, 0xbe, + 0xbe, 0x00, 0xb1, 0x01, 0xb5, 0x02, 0xe8, 0x42, + 0x53, 0xc3, 0xe8, 0x5e, 0xbf, 0xbb, 0xba, 0xda, + 0xe8, 0x02, 0xb9, 0xe8, 0x17, 0xb9, 0xbe, 0xe5, + 0x92, 0xb0, 0x0d, 0xe8, 0x3b, 0xc1, 0xc3, 0xb0, + 0x0e, 0xe8, 0x29, 0xc1, 0xb0, 0x06, 0xe8, 0x77, + 0xce, 0xb9, 0x05, 0x00, 0xb0, 0x07, 0xb4, 0x0c, + 0xe8, 0x32, 0x51, 0xb9, 0x09, 0x02, 0xe8, 0x1e, + 0xc2, 0xe8, 0x14, 0xe7, 0xc6, 0x47, 0x04, 0x00, + 0xe8, 0x4e, 0x55, 0xe8, 0x4e, 0xc2, 0xc3, 0xc6, + 0x06, 0x94, 0xdb, 0x01, 0xb9, 0x06, 0x00, 0xb0, + 0x04, 0xb4, 0x0b, 0xe8, 0x0f, 0x51, 0xb9, 0x03, + 0x02, 0xe8, 0xfb, 0xc1, 0xe8, 0xf1, 0xe6, 0xb0, + 0x06, 0x88, 0x47, 0x03, 0xe8, 0x22, 0x56, 0xe8, + 0x2a, 0xc2, 0xc3, 0xbb, 0xac, 0x42, 0xe8, 0x4c, + 0x47, 0xc3, 0xc7, 0x06, 0xf3, 0xb4, 0x14, 0x00, + 0xb8, 0x3f, 0x01, 0xbb, 0xbe, 0x00, 0xbf, 0x21, + 0x01, 0xbe, 0xbe, 0x00, 0xb1, 0x01, 0xb5, 0x04, + 0xe8, 0xc0, 0x52, 0xc3, 0xc7, 0x06, 0xf3, 0xb4, + 0x19, 0x00, 0xb8, 0x3f, 0x01, 0xbb, 0x92, 0x00, + 0xbf, 0x14, 0x01, 0xbe, 0x92, 0x00, 0xb1, 0x01, + 0xb5, 0x04, 0xe8, 0xa6, 0x52, 0xc3, 0xb9, 0x59, + 0x00, 0xb0, 0x04, 0xb4, 0x06, 0xe8, 0xb5, 0x50, + 0xb9, 0xd4, 0x03, 0xe8, 0xa1, 0xc1, 0xe8, 0x42, + 0x52, 0x80, 0x3e, 0xee, 0xdb, 0x01, 0x75, 0x06, + 0xb9, 0x07, 0x00, 0xe8, 0xed, 0x57, 0xc7, 0x06, + 0xf3, 0xb4, 0x17, 0x00, 0xb8, 0x4c, 0x00, 0xbb, + 0xc7, 0x00, 0xbf, 0x5e, 0x00, 0xbe, 0xbe, 0x00, + 0xb1, 0x13, 0xb5, 0x01, 0xe8, 0x6c, 0x52, 0xc3, + 0xbb, 0xc7, 0x42, 0xe8, 0xd7, 0x46, 0xc3, 0xbb, + 0x64, 0x41, 0xe8, 0xd0, 0x46, 0xc3, 0xbb, 0x64, + 0x41, 0xe8, 0xc9, 0x46, 0xc3, 0xbb, 0x64, 0x41, + 0xe8, 0xc2, 0x46, 0xc3, 0xe8, 0x04, 0x00, 0xe8, + 0x87, 0x00, 0xc3, 0xc7, 0x06, 0xf3, 0xb4, 0x18, + 0x00, 0xc6, 0x06, 0xdc, 0x1c, 0x01, 0xc6, 0x06, + 0xc5, 0x64, 0x00, 0xc6, 0x06, 0x3d, 0x66, 0x00, + 0xc6, 0x06, 0x40, 0x67, 0xff, 0xc6, 0x06, 0xc4, + 0x64, 0x01, 0xe8, 0x93, 0xdb, 0x80, 0x3e, 0xad, + 0x64, 0x01, 0x75, 0x05, 0xe8, 0x05, 0x53, 0xeb, + 0x03, 0xe8, 0x1a, 0x53, 0xe8, 0x3e, 0x54, 0x80, + 0x3e, 0xa4, 0xdb, 0x01, 0x74, 0x1c, 0xa1, 0xb3, + 0x32, 0xbb, 0x00, 0xfa, 0x1e, 0x53, 0x8e, 0xd8, + 0xb9, 0x70, 0x02, 0xe8, 0x2d, 0x00, 0x5b, 0x81, + 0xc3, 0xd6, 0x02, 0xb9, 0x27, 0x00, 0xe8, 0x22, + 0x00, 0x1f, 0xe8, 0x7a, 0x52, 0x80, 0x3e, 0xad, + 0x64, 0x01, 0x75, 0x04, 0xe8, 0xda, 0x52, 0xc3, + 0xb8, 0x00, 0xa0, 0xb9, 0x00, 0x7d, 0xe8, 0xf7, + 0x52, 0xe8, 0xe7, 0x52, 0xe8, 0xb2, 0x52, 0xe8, + 0xae, 0x4e, 0xc3, 0x8a, 0x07, 0x2c, 0x20, 0x73, + 0x02, 0xb0, 0x00, 0x88, 0x07, 0x43, 0xe2, 0xf3, + 0xc3, 0xc7, 0x06, 0xaf, 0x64, 0xe6, 0x00, 0xc7, + 0x06, 0xb1, 0x64, 0xaa, 0x00, 0xc6, 0x06, 0xc3, + 0x64, 0x01, 0xc6, 0x06, 0xcb, 0x64, 0x01, 0xc6, + 0x06, 0xcd, 0x64, 0x01, 0xb9, 0x34, 0x00, 0xb0, + 0x03, 0xb4, 0x0b, 0xe8, 0xb7, 0x4f, 0xb0, 0x07, + 0xe8, 0xbb, 0x4f, 0xb0, 0x0b, 0xe8, 0xbd, 0x4f, + 0xb0, 0x0e, 0xe8, 0xbf, 0x4f, 0xb0, 0x12, 0xe8, + 0xc1, 0x4f, 0xb0, 0x15, 0xe8, 0xc3, 0x4f, 0xb0, + 0x19, 0xe8, 0xc5, 0x4f, 0xb9, 0x59, 0x02, 0xc6, + 0x06, 0xdc, 0x1c, 0x01, 0xe8, 0xa1, 0xc0, 0xbe, + 0xe6, 0x00, 0xbf, 0xb3, 0x00, 0xc6, 0x06, 0xc3, + 0x64, 0x03, 0xe8, 0xae, 0xdb, 0x80, 0x3e, 0xa4, + 0xdb, 0x01, 0x74, 0x06, 0xbb, 0xea, 0x37, 0xe8, + 0xcb, 0x45, 0xc3, 0x80, 0x3e, 0xad, 0xdb, 0x01, + 0x74, 0x32, 0xb9, 0x2b, 0x00, 0xb0, 0x04, 0xb4, + 0x0b, 0xe8, 0x61, 0x4f, 0xb9, 0x2a, 0x00, 0xb0, + 0x0f, 0xe8, 0x62, 0x4f, 0xb0, 0x11, 0xe8, 0x64, + 0x4f, 0xb0, 0x13, 0xe8, 0x66, 0x4f, 0xbb, 0x47, + 0x33, 0xc7, 0x07, 0x90, 0x02, 0xb0, 0x01, 0xe8, + 0xaf, 0xbf, 0xe8, 0x91, 0xc4, 0xbb, 0x16, 0x3c, + 0xe8, 0x92, 0x45, 0xc3, 0x80, 0x3e, 0xa3, 0xdb, + 0x01, 0x74, 0x29, 0xb9, 0x1c, 0x00, 0xb0, 0x03, + 0xb4, 0x0e, 0xe8, 0x28, 0x4f, 0xb9, 0x54, 0x02, + 0xe8, 0x14, 0xc0, 0xe8, 0x0a, 0xe5, 0xb0, 0x1e, + 0x88, 0x47, 0x01, 0xe8, 0x3b, 0x54, 0xe8, 0x43, + 0xc0, 0xc6, 0x06, 0xa3, 0xdb, 0x01, 0xb0, 0x08, + 0xe8, 0xe6, 0xbe, 0xc3, 0xe8, 0x60, 0xc0, 0xe8, + 0xee, 0xe4, 0xc6, 0x47, 0x01, 0x00, 0xe8, 0x73, + 0x53, 0xb9, 0x04, 0x00, 0xb0, 0x04, 0xb4, 0x0e, + 0xe8, 0xf2, 0x4e, 0xb9, 0x55, 0x02, 0xe8, 0x06, + 0xc0, 0xc6, 0x06, 0xa3, 0xdb, 0x00, 0xb0, 0x08, + 0xe8, 0xca, 0xbe, 0xbb, 0xb8, 0x37, 0xe8, 0x34, + 0x45, 0xc7, 0x06, 0xf3, 0xb4, 0x18, 0x00, 0xe8, + 0xbe, 0xe4, 0xc6, 0x47, 0x01, 0x20, 0xb0, 0x04, + 0xe8, 0xa6, 0xbe, 0xc7, 0x06, 0xf3, 0xb4, 0x15, + 0x00, 0xc3, 0xbb, 0x14, 0xdb, 0xe8, 0x6d, 0xb6, + 0xe8, 0x82, 0xb6, 0xc3, 0xb9, 0xfe, 0x01, 0xe8, + 0xc6, 0xbf, 0xc6, 0x06, 0xe6, 0x1c, 0xe5, 0xbb, + 0xc2, 0x5d, 0xbe, 0x8c, 0x7d, 0xe8, 0xa6, 0x47, + 0xc3, 0xbb, 0xbd, 0x2c, 0xe8, 0x66, 0xb6, 0xb8, + 0x96, 0x00, 0xe8, 0xd5, 0xc3, 0xbb, 0xc2, 0x2d, + 0xe8, 0x5a, 0xb6, 0x8b, 0x36, 0xaf, 0x64, 0x8b, + 0x3e, 0xb1, 0x64, 0x83, 0xef, 0x0c, 0xe8, 0xb2, + 0xda, 0xb9, 0x22, 0x00, 0xb0, 0x05, 0xb4, 0x03, + 0xe8, 0x7a, 0x4e, 0xb9, 0x5f, 0x02, 0xe8, 0x66, + 0xbf, 0xe8, 0x5c, 0xe4, 0xc6, 0x07, 0x00, 0xe8, + 0x9e, 0x52, 0xb9, 0x60, 0x02, 0xe8, 0x5a, 0xbf, + 0xb9, 0x61, 0x02, 0xe8, 0x54, 0xbf, 0xb9, 0x62, + 0x02, 0xe8, 0x4e, 0xbf, 0xb9, 0x05, 0x00, 0xb0, + 0x19, 0xb4, 0x00, 0xe8, 0x4f, 0x4e, 0xb9, 0x63, + 0x02, 0xe8, 0x3e, 0xbf, 0xc7, 0x06, 0xaf, 0x64, + 0x10, 0x00, 0xc6, 0x06, 0xcc, 0x64, 0x00, 0xc6, + 0x06, 0xcd, 0x64, 0x00, 0xc6, 0x06, 0xcb, 0x64, + 0x00, 0xc6, 0x06, 0xc3, 0x64, 0x04, 0xc6, 0x06, + 0xdc, 0x64, 0x01, 0xe8, 0x56, 0xbf, 0xb0, 0x26, + 0xe8, 0x5d, 0xcb, 0xb0, 0x0c, 0xe8, 0x05, 0xbe, + 0xc3, 0xe8, 0xfc, 0xbb, 0xbb, 0xc4, 0xda, 0xe8, + 0xc3, 0xb5, 0xe8, 0xd8, 0xb5, 0xc3, 0xb9, 0x59, + 0x00, 0xb0, 0x05, 0xb4, 0x06, 0xe8, 0x05, 0x4e, + 0xb9, 0x43, 0x00, 0xb0, 0x0b, 0xe8, 0x06, 0x4e, + 0xb9, 0xd6, 0x03, 0xe8, 0x0a, 0xbf, 0xbb, 0x55, + 0x59, 0xe8, 0x49, 0x44, 0xc3, 0x80, 0x3e, 0xa7, + 0xdb, 0x01, 0x75, 0x07, 0xbb, 0xac, 0x3b, 0xe8, + 0x3b, 0x44, 0xc3, 0x80, 0x3e, 0x92, 0xdb, 0x01, + 0x74, 0x25, 0x8b, 0x36, 0xaf, 0x64, 0x8b, 0x3e, + 0xb1, 0x64, 0x56, 0x57, 0xc6, 0x06, 0xc3, 0x64, + 0x03, 0xe8, 0xf7, 0xd9, 0xe8, 0xa9, 0xbb, 0xbb, + 0xa0, 0x15, 0xe8, 0x88, 0xb5, 0x5f, 0x5e, 0xc6, + 0x06, 0xc3, 0x64, 0x01, 0xe8, 0xe4, 0xd9, 0xb9, + 0x42, 0x00, 0xb0, 0x05, 0xb4, 0x07, 0xe8, 0xac, + 0x4d, 0xb9, 0x43, 0x00, 0xb0, 0x14, 0xe8, 0xad, + 0x4d, 0xb9, 0x05, 0x00, 0xb0, 0x17, 0xe8, 0xac, + 0x4d, 0xb9, 0x77, 0x02, 0xe8, 0xa9, 0xbe, 0xb0, + 0x2f, 0xe8, 0xcc, 0xca, 0xc6, 0x06, 0xa7, 0xdb, + 0x01, 0xc3, 0xbb, 0x06, 0x43, 0xe8, 0xdd, 0x43, + 0xc3, 0xbb, 0x64, 0x41, 0xe8, 0xd6, 0x43, 0xc3, + 0xe8, 0x53, 0x48, 0xc3, 0x80, 0x3e, 0x92, 0xdb, + 0x01, 0x74, 0x29, 0x8b, 0x36, 0xaf, 0x64, 0x8b, + 0x3e, 0xb1, 0x64, 0xc6, 0x06, 0xc3, 0x64, 0x03, + 0x57, 0x56, 0xe8, 0x8e, 0xd9, 0xe8, 0x40, 0xbb, + 0xbb, 0xce, 0xda, 0xe8, 0x07, 0xb5, 0xe8, 0x1c, + 0xb5, 0x5e, 0x5f, 0xc6, 0x06, 0xc3, 0x64, 0x01, + 0xe8, 0x78, 0xd9, 0xc3, 0xe8, 0xa0, 0xbe, 0xb0, + 0x02, 0xe8, 0x7c, 0xca, 0xb0, 0x07, 0xe8, 0x24, + 0xbd, 0xe8, 0x24, 0xe3, 0xc6, 0x07, 0x00, 0xe8, + 0xaa, 0x51, 0xb9, 0x20, 0x00, 0xb0, 0x07, 0xb4, + 0x09, 0xe8, 0x29, 0x4d, 0xb9, 0x08, 0x02, 0xe8, + 0x3d, 0xbe, 0xc3, 0xbb, 0x3f, 0x42, 0xe8, 0x74, + 0x43, 0xc3, 0xbb, 0x3f, 0x42, 0xe8, 0x6d, 0x43, + 0xc3, 0xbb, 0x1e, 0x43, 0xe8, 0x66, 0x43, 0xc3, + 0xb9, 0x58, 0x00, 0xb0, 0x05, 0xb4, 0x03, 0xe8, + 0x03, 0x4d, 0xb9, 0xd5, 0x03, 0xe8, 0xff, 0xbd, + 0xe8, 0x90, 0x4e, 0xc7, 0x06, 0xf3, 0xb4, 0x14, + 0x00, 0xb8, 0xa0, 0x00, 0xbb, 0xa5, 0x00, 0xbf, + 0xa0, 0x00, 0xbe, 0xb9, 0x00, 0xb1, 0x0b, 0xb5, + 0x03, 0xe8, 0xc7, 0x4e, 0xc3, 0xbb, 0xff, 0x41, + 0xe8, 0x32, 0x43, 0xc3, 0x80, 0x3e, 0x92, 0xdb, + 0x01, 0x74, 0x29, 0x8b, 0x36, 0xaf, 0x64, 0x8b, + 0x3e, 0xb1, 0x64, 0x56, 0x57, 0xc6, 0x06, 0xc3, + 0x64, 0x03, 0xe8, 0xee, 0xd8, 0xe8, 0xa0, 0xba, + 0xbb, 0xd4, 0xda, 0xe8, 0x67, 0xb4, 0xe8, 0x7c, + 0xb4, 0x5f, 0x5e, 0xc6, 0x06, 0xc3, 0x64, 0x01, + 0xe8, 0xd8, 0xd8, 0xc3, 0xe8, 0x00, 0xbe, 0xb8, + 0x02, 0x00, 0xe8, 0x1c, 0xd1, 0x32, 0xe4, 0xbb, + 0xc7, 0x32, 0xb1, 0x1b, 0x48, 0xf6, 0xe1, 0x03, + 0xd8, 0xc7, 0x07, 0x00, 0x00, 0xb9, 0x20, 0x00, + 0xb0, 0x07, 0xb4, 0x08, 0xe8, 0x86, 0x4c, 0xb9, + 0xfc, 0x01, 0xe8, 0x9a, 0xbd, 0xb0, 0x0d, 0xe8, + 0x63, 0xbc, 0xb0, 0x07, 0xe8, 0xb1, 0xc9, 0xc3, + 0x80, 0x3e, 0xa5, 0xdb, 0x01, 0x74, 0x37, 0xe8, + 0x2b, 0xba, 0xbb, 0xda, 0xda, 0xe8, 0x15, 0xb4, + 0x53, 0xe8, 0x29, 0xb4, 0x5b, 0x81, 0xfb, 0x13, + 0x19, 0x75, 0x22, 0xb8, 0x64, 0x00, 0xe8, 0x91, + 0xc1, 0x8b, 0x3e, 0xb1, 0x64, 0x8b, 0x36, 0xaf, + 0x64, 0xc6, 0x06, 0xc3, 0x64, 0x03, 0xe8, 0x72, + 0xd8, 0xe8, 0x92, 0xc1, 0xbb, 0xd5, 0x34, 0xe8, + 0x93, 0x42, 0xe8, 0x89, 0xc1, 0xc3, 0xc6, 0x06, + 0xa5, 0xdb, 0x02, 0xe8, 0xef, 0xb9, 0xbb, 0x4f, + 0x1f, 0xe8, 0xf1, 0xb3, 0xb8, 0x01, 0x01, 0xe8, + 0x34, 0xc1, 0xbb, 0x47, 0x33, 0xc7, 0x07, 0x5c, + 0x02, 0xb0, 0x01, 0xe8, 0x3a, 0xbc, 0xe8, 0x03, + 0x4d, 0xc7, 0x06, 0xf3, 0xb4, 0x15, 0x00, 0xb0, + 0x04, 0xe8, 0xf1, 0xbb, 0xb0, 0x0c, 0xe8, 0xe0, + 0xbb, 0xe8, 0xec, 0xe1, 0x53, 0xc6, 0x07, 0x00, + 0xbb, 0x47, 0x33, 0xc7, 0x47, 0x02, 0x5e, 0x02, + 0xe8, 0x01, 0x4f, 0xb9, 0x2e, 0x00, 0xb0, 0x05, + 0xb4, 0x02, 0xe8, 0xe8, 0x4b, 0xb0, 0x02, 0xe8, + 0x48, 0xbc, 0xe8, 0xcf, 0x4c, 0x5b, 0xc6, 0x07, + 0x21, 0xbb, 0x47, 0x33, 0xc7, 0x07, 0x5d, 0x02, + 0xc7, 0x06, 0xf3, 0xb4, 0x17, 0x00, 0xe8, 0xe4, + 0x4e, 0xb0, 0x01, 0xe8, 0x2c, 0xbc, 0xe8, 0x65, + 0x4c, 0xc6, 0x06, 0x33, 0x33, 0x01, 0xc6, 0x06, + 0xdd, 0x1c, 0x02, 0xe8, 0x77, 0xb9, 0xbb, 0x02, + 0x20, 0xe8, 0x79, 0xb3, 0xc3, 0x80, 0x3e, 0x97, + 0xdb, 0x00, 0x74, 0x07, 0xbb, 0x59, 0x3d, 0xe8, + 0xfb, 0x41, 0xc3, 0xbe, 0xf5, 0x00, 0xbf, 0xc6, + 0x00, 0xc6, 0x06, 0xc3, 0x64, 0x01, 0xe8, 0xc2, + 0xd7, 0xe8, 0x00, 0xba, 0xbb, 0xd7, 0x21, 0xe8, + 0x53, 0xb3, 0xc6, 0x06, 0x97, 0xdb, 0x01, 0xb8, + 0x01, 0x02, 0xe8, 0x91, 0xc0, 0xb9, 0x0d, 0x00, + 0xb0, 0x0a, 0xb4, 0x0c, 0xe8, 0x76, 0x4b, 0xb0, + 0x0c, 0xe8, 0x7a, 0x4b, 0xb0, 0x0e, 0xe8, 0x7c, + 0x4b, 0xb0, 0x10, 0xe8, 0x7e, 0x4b, 0xb0, 0x12, + 0xe8, 0x80, 0x4b, 0xb0, 0x14, 0xe8, 0x82, 0x4b, + 0xbb, 0x47, 0x33, 0xc7, 0x47, 0x02, 0x10, 0x02, + 0xb0, 0x02, 0xe8, 0xbc, 0xbb, 0xb8, 0x32, 0x00, + 0xe8, 0x87, 0xc0, 0xb9, 0x07, 0x00, 0xe8, 0x92, + 0x52, 0xc6, 0x06, 0xee, 0xdb, 0x01, 0xb9, 0x38, + 0x00, 0xb0, 0x03, 0xb4, 0x0a, 0xe8, 0x35, 0x4b, + 0xb0, 0x05, 0xe8, 0x39, 0x4b, 0xb0, 0x07, 0xe8, + 0x3b, 0x4b, 0xb0, 0x09, 0xe8, 0x3d, 0x4b, 0xb0, + 0x0b, 0xe8, 0x3f, 0x4b, 0xb0, 0x0d, 0xe8, 0x41, + 0x4b, 0xb0, 0x0f, 0xe8, 0x43, 0x4b, 0xb0, 0x11, + 0xe8, 0x45, 0x4b, 0xb9, 0x0d, 0x02, 0xe8, 0xfe, + 0xbb, 0xb9, 0x38, 0x00, 0xb0, 0x01, 0xb4, 0x0a, + 0xe8, 0x02, 0x4b, 0xb0, 0x03, 0xe8, 0x06, 0x4b, + 0xb0, 0x05, 0xe8, 0x08, 0x4b, 0xb0, 0x07, 0xe8, + 0x0a, 0x4b, 0xb0, 0x09, 0xe8, 0x0c, 0x4b, 0xb0, + 0x0b, 0xe8, 0x0e, 0x4b, 0xb0, 0x0d, 0xe8, 0x10, + 0x4b, 0xb9, 0x28, 0x00, 0xb0, 0x0f, 0xe8, 0x0f, + 0x4b, 0xb0, 0x12, 0xe8, 0x11, 0x4b, 0xb0, 0x16, + 0xe8, 0x13, 0x4b, 0xb9, 0x0e, 0x02, 0xe8, 0xc1, + 0xbb, 0xb9, 0x36, 0x00, 0xb0, 0x01, 0xb4, 0x0a, + 0xe8, 0xc2, 0x4a, 0xb9, 0x37, 0x00, 0xb0, 0x05, + 0xe8, 0xc3, 0x4a, 0xb9, 0x38, 0x00, 0xb0, 0x09, + 0xe8, 0xc2, 0x4a, 0xb0, 0x0d, 0xe8, 0xc4, 0x4a, + 0xb9, 0x18, 0x00, 0xb0, 0x10, 0xe8, 0xc3, 0x4a, + 0xb9, 0x0f, 0x02, 0xe8, 0x94, 0xbb, 0xe8, 0xcb, + 0xbb, 0xe8, 0xea, 0xbf, 0xbb, 0x19, 0x22, 0xe8, + 0x5b, 0xb2, 0xb0, 0x02, 0xbe, 0x20, 0x98, 0xe8, + 0x7f, 0xba, 0xc3, 0x80, 0x3e, 0xee, 0xdb, 0x01, + 0x75, 0x06, 0xb9, 0x06, 0x00, 0xe8, 0xcb, 0x51, + 0xc7, 0x06, 0xf3, 0xb4, 0x15, 0x00, 0xb8, 0x63, + 0x00, 0xbb, 0xb4, 0x00, 0xbf, 0x63, 0x00, 0xbe, + 0xba, 0x00, 0xb1, 0x0b, 0xb5, 0x03, 0xe8, 0x4a, + 0x4c, 0xc3, 0xbb, 0x64, 0x41, 0xe8, 0xb5, 0x40, + 0xc3, 0xbb, 0x64, 0x41, 0xe8, 0xae, 0x40, 0xc3, + 0xbb, 0xff, 0x41, 0xe8, 0xa7, 0x40, 0xc3, 0xbb, + 0x4e, 0x43, 0xe8, 0xa0, 0x40, 0xc3, 0xe8, 0x1d, + 0x45, 0xc3, 0x80, 0x3e, 0xb1, 0xdb, 0x01, 0x75, + 0x07, 0xbb, 0x80, 0x43, 0xe8, 0x8e, 0x40, 0xc3, + 0x8b, 0x36, 0xaf, 0x64, 0x8b, 0x3e, 0xb1, 0x64, + 0xc6, 0x06, 0xc3, 0x64, 0x04, 0xe8, 0x53, 0xd6, + 0xe8, 0xe2, 0xb7, 0xbb, 0xfc, 0xda, 0xe8, 0xcc, + 0xb1, 0xe8, 0xe1, 0xb1, 0xc3, 0xbb, 0xac, 0x43, + 0xe8, 0x6a, 0x40, 0xc3, 0xbb, 0xce, 0x43, 0xe8, + 0x63, 0x40, 0xc3, 0x80, 0x3e, 0x9a, 0xdb, 0x00, + 0x75, 0x0d, 0xe8, 0xc0, 0xb7, 0xbb, 0xf6, 0xda, + 0xe8, 0xaa, 0xb1, 0xe8, 0xbf, 0xb1, 0xc3, 0xe8, + 0xb3, 0xb7, 0xbb, 0x1e, 0x1e, 0xe8, 0xb5, 0xb1, + 0xe8, 0x3b, 0xbf, 0xe8, 0x41, 0xbb, 0xb0, 0x0c, + 0xe8, 0x1d, 0xc7, 0xb0, 0x0c, 0xe8, 0xc5, 0xb9, + 0xe8, 0xc5, 0xdf, 0xc6, 0x07, 0x00, 0xe8, 0x4b, + 0x4e, 0xb9, 0x05, 0x00, 0xb0, 0x06, 0xb4, 0x04, + 0xe8, 0xca, 0x49, 0xb9, 0x1d, 0x02, 0xe8, 0xec, + 0xba, 0xc3, 0xe8, 0x99, 0x44, 0xc3, 0x80, 0x3e, + 0xb3, 0xdb, 0x01, 0x75, 0x07, 0xbb, 0xa7, 0x44, + 0xe8, 0x0a, 0x40, 0xc3, 0xbb, 0x12, 0x44, 0xe8, + 0x18, 0x00, 0xbb, 0x4f, 0x44, 0xe8, 0x12, 0x00, + 0xbb, 0x6b, 0x44, 0xe8, 0x0c, 0x00, 0xbb, 0x92, + 0x44, 0xe8, 0xf1, 0x3f, 0xc6, 0x06, 0xb3, 0xdb, + 0x01, 0xc3, 0xe8, 0xe8, 0x3f, 0xb8, 0x96, 0x00, + 0xe8, 0xc7, 0xbe, 0xc3, 0xe8, 0x5f, 0x44, 0xc3, + 0xbb, 0x1e, 0x43, 0xe8, 0xd7, 0x3f, 0xc3, 0xe8, + 0x33, 0x00, 0xc3, 0x80, 0x3e, 0xa4, 0xdb, 0x01, + 0x74, 0x04, 0xe8, 0x28, 0x00, 0xc3, 0xe8, 0xc6, + 0xba, 0xe8, 0x54, 0xdf, 0xc6, 0x07, 0x00, 0xe8, + 0xda, 0x4d, 0xb9, 0x38, 0x00, 0xb0, 0x0a, 0xb4, + 0x02, 0xe8, 0x59, 0x49, 0xb9, 0x57, 0x02, 0xe8, + 0x7b, 0xba, 0xb0, 0x25, 0xe8, 0x89, 0xc6, 0xb0, + 0x02, 0xe8, 0x31, 0xb9, 0xc3, 0x80, 0x3e, 0xae, + 0xdb, 0x01, 0x75, 0x0a, 0xe8, 0x1a, 0x00, 0xbb, + 0xdd, 0x2f, 0xe8, 0x00, 0xb1, 0xc3, 0xbb, 0x41, + 0x2e, 0xe8, 0xf9, 0xb0, 0xe8, 0x0a, 0x00, 0xe8, + 0x83, 0xbe, 0xbb, 0x6d, 0x2e, 0xe8, 0xed, 0xb0, + 0xc3, 0xa0, 0xab, 0x64, 0x8a, 0x26, 0xaa, 0x64, + 0x50, 0xc6, 0x06, 0xab, 0x64, 0x02, 0xc6, 0x06, + 0xaa, 0x64, 0x02, 0xe8, 0xd1, 0xaa, 0xe8, 0x9d, + 0xaa, 0xb8, 0x03, 0x00, 0xe8, 0x4e, 0x43, 0x8b, + 0x77, 0x0d, 0x8b, 0x7f, 0x0f, 0x8a, 0x47, 0x11, + 0xa2, 0xc3, 0x64, 0xe8, 0x25, 0xd5, 0xe8, 0x22, + 0x00, 0xbe, 0x30, 0x00, 0xbf, 0xbe, 0x00, 0xc6, + 0x06, 0xc3, 0x64, 0x03, 0xe8, 0x14, 0xd5, 0x58, + 0xa2, 0xab, 0x64, 0x88, 0x26, 0xaa, 0x64, 0xe8, + 0x9d, 0xaa, 0xe8, 0x69, 0xaa, 0xc6, 0x06, 0xae, + 0xdb, 0x01, 0xc3, 0xb9, 0x34, 0x00, 0xb0, 0x0a, + 0xb4, 0x0b, 0xe8, 0xc8, 0x48, 0xb0, 0x0e, 0xe8, + 0xcc, 0x48, 0xb0, 0x12, 0xe8, 0xce, 0x48, 0xb0, + 0x15, 0xe8, 0xd0, 0x48, 0xb0, 0x19, 0xe8, 0xd2, + 0x48, 0xb0, 0x1c, 0xe8, 0xd4, 0x48, 0xb0, 0x20, + 0xe8, 0xd6, 0x48, 0xb9, 0x58, 0x02, 0xe8, 0x96, + 0xb9, 0xe8, 0x37, 0x4a, 0xc7, 0x06, 0xf3, 0xb4, + 0x15, 0x00, 0xb8, 0x29, 0x01, 0xbb, 0xb2, 0x00, + 0xbf, 0x29, 0x01, 0xbe, 0xb5, 0x00, 0xb1, 0x0b, + 0xb5, 0x03, 0xe8, 0x6e, 0x4a, 0xc3, 0x80, 0x3e, + 0xa4, 0xdb, 0x01, 0x75, 0x07, 0xbb, 0x01, 0x38, + 0xe8, 0xd2, 0x3e, 0xc3, 0xb9, 0x47, 0x00, 0xb0, + 0x06, 0xb4, 0x0c, 0xe8, 0x6f, 0x48, 0xb9, 0x56, + 0x02, 0xe8, 0x5b, 0xb9, 0xe8, 0x76, 0x4c, 0x80, + 0x3e, 0xad, 0x64, 0x01, 0x75, 0x0e, 0x8b, 0x16, + 0xb3, 0x32, 0xbe, 0x00, 0xfa, 0xb0, 0x40, 0xe8, + 0x78, 0x45, 0xeb, 0x0c, 0x8b, 0x16, 0xb3, 0x32, + 0xbe, 0x00, 0xfa, 0xb0, 0x40, 0xe8, 0x6a, 0x45, + 0xe8, 0x71, 0xb9, 0xe8, 0x2a, 0xde, 0xc6, 0x47, + 0x02, 0x00, 0xe8, 0xaf, 0x4c, 0xbb, 0x47, 0x33, + 0xc7, 0x07, 0x94, 0x02, 0xb0, 0x01, 0xe8, 0x4f, + 0xb8, 0xe8, 0x18, 0x49, 0xb0, 0x01, 0xe8, 0x0c, + 0xb8, 0xb0, 0x01, 0xb4, 0x00, 0xe8, 0x99, 0xcc, + 0xe8, 0xad, 0xcc, 0xc6, 0x06, 0xa4, 0xdb, 0x01, + 0xc3, 0x80, 0x3e, 0xa4, 0xdb, 0x01, 0x74, 0x04, + 0xe8, 0x0b, 0x00, 0xc3, 0xbb, 0x1e, 0xdb, 0xe8, + 0xb3, 0xaf, 0xe8, 0xc8, 0xaf, 0xc3, 0xbb, 0x1d, + 0x46, 0xe8, 0x51, 0x3e, 0xc3, 0x80, 0x3e, 0xa4, + 0xdb, 0x01, 0x74, 0x04, 0xe8, 0xef, 0xff, 0xc3, + 0xbb, 0x0e, 0x45, 0xe8, 0x3f, 0x3e, 0xc3, 0x80, + 0x3e, 0xa4, 0xdb, 0x01, 0x74, 0x04, 0xe8, 0xdd, + 0xff, 0xc3, 0xbb, 0xd6, 0x44, 0xe8, 0x2d, 0x3e, + 0xc3, 0x80, 0x3e, 0xa4, 0xdb, 0x01, 0x74, 0x04, + 0xe8, 0xcb, 0xff, 0xc3, 0xa0, 0xb4, 0xdb, 0x0a, + 0xc0, 0x74, 0x20, 0x3c, 0x01, 0x74, 0x07, 0xbb, + 0x03, 0x46, 0xe8, 0x10, 0x3e, 0xc3, 0xbb, 0xb8, + 0x45, 0xe8, 0x09, 0x3e, 0xe8, 0x06, 0xbd, 0xbb, + 0xda, 0x45, 0xe8, 0x00, 0x3e, 0xc6, 0x06, 0xb4, + 0xdb, 0x02, 0xc3, 0xbb, 0x32, 0x45, 0xe8, 0xf4, + 0x3d, 0x8b, 0x36, 0xaf, 0x64, 0x8b, 0x3e, 0xb1, + 0x64, 0x83, 0xee, 0x1e, 0xc6, 0x06, 0xc3, 0x64, + 0x01, 0xe8, 0xb7, 0xd3, 0xbb, 0x55, 0x45, 0xe8, + 0xdb, 0x3d, 0xbb, 0x68, 0x45, 0xe8, 0x28, 0x00, + 0xbb, 0x7b, 0x45, 0xe8, 0x22, 0x00, 0xbb, 0x8e, + 0x45, 0xe8, 0x1c, 0x00, 0x8b, 0x36, 0xaf, 0x64, + 0x8b, 0x3e, 0xb1, 0x64, 0xc6, 0x06, 0xc3, 0x64, + 0x03, 0xe8, 0x8f, 0xd3, 0xbb, 0x9f, 0x45, 0xe8, + 0xb3, 0x3d, 0xc6, 0x06, 0xb4, 0xdb, 0x01, 0xc3, + 0x53, 0x8b, 0x36, 0xaf, 0x64, 0x8b, 0x3e, 0xb1, + 0x64, 0x83, 0xc6, 0x14, 0xc6, 0x06, 0xc3, 0x64, + 0x01, 0xe8, 0x6f, 0xd3, 0x5b, 0xe8, 0x95, 0x3d, + 0xc3, 0x80, 0x3e, 0xa4, 0xdb, 0x01, 0x74, 0x04, + 0xe8, 0x33, 0xff, 0xc3, 0xe8, 0x69, 0x3d, 0xc3, + 0xe8, 0x84, 0xb8, 0xb9, 0x1a, 0x00, 0xb0, 0x06, + 0xb4, 0x09, 0xe8, 0x20, 0x47, 0xb0, 0x0a, 0xe8, + 0x24, 0x47, 0xb9, 0x18, 0x00, 0xb0, 0x0d, 0xe8, + 0x23, 0x47, 0xb9, 0x2e, 0x00, 0xb0, 0x25, 0xe8, + 0x22, 0x47, 0xe8, 0xf3, 0xdc, 0xc6, 0x07, 0x00, + 0x53, 0xe8, 0x34, 0x4b, 0xc6, 0x06, 0x35, 0x33, + 0x10, 0xc6, 0x06, 0x36, 0x33, 0x18, 0xb8, 0x3c, + 0x46, 0xa3, 0x37, 0x33, 0xb8, 0xda, 0x78, 0xa3, + 0x39, 0x33, 0xc6, 0x06, 0xdc, 0x64, 0x00, 0xb9, + 0x52, 0x02, 0xe8, 0x52, 0xb8, 0xc6, 0x06, 0xdc, + 0x64, 0x01, 0x5b, 0xc6, 0x07, 0x1d, 0xe8, 0x07, + 0x4b, 0xb9, 0x05, 0x00, 0xb0, 0x02, 0xb4, 0x0a, + 0xe8, 0xca, 0x46, 0xb9, 0x53, 0x02, 0xe8, 0xec, + 0xb7, 0xb0, 0x01, 0xe8, 0xa7, 0xb6, 0xb0, 0x24, + 0xe8, 0xf5, 0xc3, 0xbb, 0x90, 0x37, 0xe8, 0x0c, + 0x3d, 0xc3, 0xbb, 0x5e, 0x46, 0xe8, 0x05, 0x3d, + 0xc3, 0x80, 0x3e, 0xac, 0xdb, 0x01, 0x74, 0x07, + 0xbb, 0xd2, 0x3b, 0xe8, 0xf7, 0x3c, 0xc3, 0xb9, + 0x08, 0x00, 0xe8, 0xe6, 0x4d, 0xc7, 0x06, 0xf3, + 0xb4, 0x1a, 0x00, 0xb8, 0x3f, 0x01, 0xbb, 0xa9, + 0x00, 0xbf, 0x14, 0x01, 0xbe, 0xa9, 0x00, 0xb1, + 0x01, 0xb5, 0x04, 0xe8, 0x65, 0x48, 0xc3, 0xc7, + 0x06, 0xf3, 0xb4, 0x15, 0x00, 0xb8, 0xce, 0x00, + 0xbb, 0xc7, 0x00, 0xbf, 0xc4, 0x00, 0xbe, 0xc2, + 0x00, 0xb1, 0x13, 0xb5, 0x01, 0xe8, 0x4b, 0x48, + 0xc3, 0xbb, 0xf7, 0x46, 0xe8, 0xb6, 0x3c, 0xc3, + 0xbb, 0x3d, 0x47, 0xe8, 0xaf, 0x3c, 0xc3, 0xbb, + 0x3d, 0x47, 0xe8, 0xa8, 0x3c, 0xc3, 0xbb, 0x7b, + 0x47, 0xe8, 0xa1, 0x3c, 0xc3, 0xbb, 0x98, 0x47, + 0xe8, 0x9a, 0x3c, 0xc3, 0xb9, 0x06, 0x00, 0xe8, + 0x89, 0x4d, 0xc7, 0x06, 0xf3, 0xb4, 0x19, 0x00, + 0xb8, 0x97, 0x00, 0xbb, 0x9c, 0x00, 0xbf, 0xea, + 0x00, 0xbe, 0x98, 0x00, 0xb1, 0x01, 0xb5, 0x02, + 0xe8, 0x08, 0x48, 0xc3, 0xbb, 0x75, 0x33, 0xe8, + 0xe3, 0xad, 0xc3, 0xbb, 0xbf, 0x47, 0xe8, 0x6c, + 0x3c, 0xc3, 0xb9, 0x05, 0x00, 0xb0, 0x0a, 0xb4, + 0x0c, 0xe8, 0x09, 0x46, 0xe8, 0x60, 0xb7, 0xe8, + 0xee, 0xdb, 0xc6, 0x07, 0x00, 0xe8, 0x74, 0x4a, + 0xb9, 0x80, 0x02, 0xe8, 0x11, 0xb7, 0xb0, 0x32, + 0xe8, 0x2d, 0xc3, 0xb0, 0x06, 0xe8, 0xd5, 0xb5, + 0xc3, 0x80, 0x3e, 0x93, 0xdb, 0x01, 0x75, 0x07, + 0xbb, 0x08, 0x3e, 0xe8, 0x37, 0x3c, 0xc3, 0xc6, + 0x06, 0x93, 0xdb, 0x01, 0xbb, 0xc6, 0x3d, 0xe8, + 0x2b, 0x3c, 0xb9, 0x1e, 0x00, 0xb0, 0x1a, 0xb4, + 0x0a, 0xe8, 0xc9, 0x45, 0xb9, 0x1d, 0x00, 0xb0, + 0x31, 0xb4, 0x0f, 0xe8, 0xc8, 0x45, 0xe8, 0x16, + 0xb7, 0xb4, 0x01, 0xb0, 0x57, 0xe8, 0x9f, 0xba, + 0xbb, 0x47, 0x33, 0xc7, 0x07, 0xf5, 0x01, 0xb9, + 0xf4, 0x01, 0xe8, 0x9d, 0xb6, 0xe8, 0x90, 0xdb, + 0xb0, 0x01, 0x88, 0x07, 0xe8, 0xc2, 0x4a, 0xb0, + 0x0e, 0xe8, 0x75, 0xb5, 0xe8, 0xc5, 0xb6, 0xbb, + 0xf4, 0x3d, 0xe8, 0xe8, 0x3b, 0xc3, 0xbb, 0xc1, + 0x32, 0xe8, 0x51, 0xad, 0xc3, 0xe8, 0xf6, 0xff, + 0xc3, 0xbb, 0x5e, 0x32, 0xe8, 0x46, 0xad, 0xc3, + 0x80, 0x3e, 0x96, 0xdb, 0x01, 0x74, 0x07, 0xbb, + 0xb2, 0x3e, 0xe8, 0xc8, 0x3b, 0xc3, 0xb9, 0x38, + 0x00, 0xb0, 0x07, 0xb4, 0x09, 0xe8, 0x65, 0x45, + 0xb9, 0x05, 0x00, 0xb0, 0x0f, 0xe8, 0x66, 0x45, + 0xb9, 0x65, 0x02, 0xe8, 0x49, 0xb6, 0xe8, 0x3f, + 0xdb, 0xc6, 0x47, 0x03, 0x24, 0xe8, 0x80, 0x49, + 0xe8, 0x79, 0xb6, 0xb0, 0x27, 0xe8, 0x80, 0xc2, + 0xb0, 0x05, 0xe8, 0x28, 0xb5, 0xbb, 0x7c, 0x38, + 0xe8, 0x92, 0x3b, 0xc3, 0x80, 0x3e, 0x96, 0xdb, + 0x01, 0x74, 0x07, 0xbb, 0xb2, 0x3e, 0xe8, 0x84, + 0x3b, 0xc3, 0xe8, 0x82, 0xb6, 0x83, 0x06, 0xaf, + 0x64, 0x04, 0xe8, 0x0b, 0xdb, 0xc6, 0x47, 0x02, + 0x23, 0xe8, 0x90, 0x49, 0xb9, 0x3f, 0x00, 0xb0, + 0x08, 0xb4, 0x09, 0xe8, 0x0f, 0x45, 0xb9, 0x18, + 0x00, 0xb0, 0x0a, 0xe8, 0x10, 0x45, 0xb9, 0x64, + 0x02, 0xe8, 0x1b, 0xb6, 0xb0, 0x28, 0xe8, 0x37, + 0xc2, 0xb0, 0x06, 0xe8, 0xdf, 0xb4, 0xc3, 0x80, + 0x3e, 0x96, 0xdb, 0x01, 0x74, 0x07, 0xbb, 0xe7, + 0x47, 0xe8, 0x41, 0x3b, 0xc3, 0xe8, 0xbe, 0x3f, + 0xc3, 0xe8, 0x3b, 0xb6, 0xe8, 0xc9, 0xda, 0xc6, + 0x47, 0x04, 0x00, 0xe8, 0x4e, 0x49, 0xb9, 0x05, + 0x00, 0xb0, 0x0b, 0xb4, 0x05, 0xe8, 0xcd, 0x44, + 0xb9, 0x71, 0x02, 0xe8, 0xef, 0xb5, 0xb0, 0x2c, + 0xe8, 0xfd, 0xc1, 0xb0, 0x08, 0xe8, 0xa5, 0xb4, + 0xc3, 0xc7, 0x06, 0xf3, 0xb4, 0x14, 0x00, 0xb8, + 0x05, 0x00, 0xbb, 0x83, 0x00, 0xbf, 0x0e, 0x00, + 0xbe, 0xb9, 0x00, 0xb1, 0x0b, 0xb5, 0x03, 0xe8, + 0x89, 0x46, 0xc3, 0x80, 0x3e, 0x9d, 0xdb, 0x01, + 0x75, 0x07, 0xbb, 0x6a, 0x35, 0xe8, 0xed, 0x3a, + 0xc3, 0xc6, 0x06, 0x9d, 0xdb, 0x01, 0xb0, 0x11, + 0xe8, 0xc5, 0xc1, 0xb9, 0x31, 0x00, 0xb0, 0x03, + 0xb4, 0x05, 0xe8, 0x80, 0x44, 0xb9, 0x05, 0x00, + 0xb0, 0x1a, 0xe8, 0x81, 0x44, 0xb9, 0x24, 0x02, + 0xe8, 0x93, 0xb5, 0xbb, 0xb2, 0x35, 0xe8, 0xc4, + 0x3a, 0xc3, 0xe8, 0xc2, 0xb5, 0xb9, 0x12, 0x00, + 0xb0, 0x0a, 0xb4, 0x05, 0xe8, 0x5e, 0x44, 0xe8, + 0x46, 0xda, 0xc6, 0x47, 0x01, 0x00, 0xe8, 0xcb, + 0x48, 0xb9, 0x29, 0x02, 0xe8, 0x76, 0xb5, 0xb0, + 0x15, 0xe8, 0x84, 0xc1, 0xb0, 0x0b, 0xe8, 0x2c, + 0xb4, 0xe8, 0x92, 0xb9, 0xbb, 0x05, 0x36, 0xe8, + 0x93, 0x3a, 0xc3, 0x80, 0x3e, 0xa9, 0xdb, 0x01, + 0x74, 0x07, 0xbb, 0x08, 0x48, 0xe8, 0x85, 0x3a, + 0xc3, 0xb9, 0x7b, 0x02, 0xe8, 0x28, 0xb5, 0xe8, + 0x0e, 0xda, 0xc6, 0x47, 0x05, 0x00, 0xe8, 0x4f, + 0x48, 0xb9, 0x3f, 0x00, 0xb0, 0x0b, 0xb4, 0x06, + 0xe8, 0x12, 0x44, 0xb9, 0x0f, 0x00, 0xb0, 0x14, + 0xe8, 0x13, 0x44, 0xb9, 0x20, 0x00, 0xb0, 0x1f, + 0xb4, 0x09, 0xe8, 0x10, 0x44, 0xb9, 0x7c, 0x02, + 0xe8, 0xff, 0xb4, 0xff, 0x0e, 0xaf, 0x64, 0xc7, + 0x06, 0xb1, 0x64, 0x8b, 0x00, 0xc6, 0x06, 0xcc, + 0x64, 0x01, 0xc6, 0x06, 0xdc, 0x64, 0x00, 0xe8, + 0x12, 0xb5, 0xb0, 0x30, 0xe8, 0x19, 0xc1, 0xb0, + 0x2f, 0xe8, 0x14, 0xc1, 0xbb, 0x83, 0x3b, 0xe8, + 0x2b, 0x3a, 0xc6, 0x06, 0xa9, 0xdb, 0x02, 0xc6, + 0x06, 0xa8, 0xdb, 0x00, 0xc3, 0xbb, 0x28, 0x48, + 0xe8, 0x1a, 0x3a, 0xc3, 0xe8, 0x18, 0xb5, 0xe8, + 0xa6, 0xd9, 0xc6, 0x07, 0x00, 0xe8, 0x2c, 0x48, + 0xb9, 0x05, 0x00, 0xb0, 0x09, 0xb4, 0x0b, 0xe8, + 0xab, 0x43, 0xb9, 0xff, 0x01, 0xe8, 0xbf, 0xb4, + 0xb0, 0x01, 0xe8, 0xdb, 0xc0, 0xb0, 0x0e, 0xe8, + 0x83, 0xb3, 0xc3, 0xbb, 0x28, 0x48, 0xe8, 0xec, + 0x39, 0xc3, 0xbb, 0x60, 0x48, 0xe8, 0xe5, 0x39, + 0xc3, 0xbb, 0x3f, 0x5b, 0xe8, 0xde, 0x39, 0xc3, + 0xe8, 0x5b, 0x3e, 0xc3, 0xb9, 0x46, 0x00, 0xb0, + 0x04, 0xb4, 0x0c, 0xe8, 0x77, 0x43, 0xb9, 0xcc, + 0x03, 0xe8, 0x63, 0xb4, 0xe8, 0x04, 0x45, 0xc7, + 0x06, 0xf3, 0xb4, 0x1d, 0x00, 0xb8, 0xa0, 0x00, + 0xbb, 0xc7, 0x00, 0xbf, 0xa0, 0x00, 0xbe, 0xbc, + 0x00, 0xb1, 0x13, 0xb5, 0x01, 0xe8, 0x3b, 0x45, + 0xc3, 0xbb, 0x7e, 0x4a, 0xe8, 0xa6, 0x39, 0xb0, + 0x04, 0xe8, 0x31, 0xb3, 0xc3, 0xb9, 0x59, 0x00, + 0xb0, 0x05, 0xb4, 0x0b, 0xe8, 0x3e, 0x43, 0xb9, + 0xc9, 0x03, 0xe8, 0x3a, 0xb4, 0xe8, 0xcb, 0x44, + 0xc7, 0x06, 0xf3, 0xb4, 0x21, 0x00, 0xb8, 0x3f, + 0x01, 0xbb, 0xb5, 0x00, 0xbf, 0x2d, 0x01, 0xbe, + 0xb5, 0x00, 0xb1, 0x01, 0xb5, 0x04, 0xe8, 0x02, + 0x45, 0xc3, 0xb9, 0x58, 0x00, 0xb0, 0x04, 0xb4, + 0x02, 0xe8, 0x11, 0x43, 0xb9, 0xca, 0x03, 0xe8, + 0xfd, 0xb3, 0xe8, 0x9e, 0x44, 0xc7, 0x06, 0xf3, + 0xb4, 0x23, 0x00, 0xb8, 0xa0, 0x00, 0xbb, 0xc7, + 0x00, 0xbf, 0xa0, 0x00, 0xbe, 0xbb, 0x00, 0xb1, + 0x13, 0xb5, 0x01, 0xe8, 0xd5, 0x44, 0xc3, 0xbb, + 0x51, 0x5b, 0xe8, 0x40, 0x39, 0xc3, 0xbb, 0x0f, + 0x57, 0xe8, 0x39, 0x39, 0xc3, 0xbb, 0x46, 0x67, + 0xe8, 0xb7, 0xd8, 0x83, 0xc3, 0x03, 0xff, 0x37, + 0x53, 0xc7, 0x07, 0x3f, 0x01, 0xbe, 0x99, 0x00, + 0xbf, 0xa3, 0x00, 0xc6, 0x06, 0xc3, 0x64, 0x04, + 0xe8, 0xf0, 0xce, 0xb9, 0xcd, 0x03, 0xe8, 0xbe, + 0xb3, 0xe8, 0x4f, 0x44, 0x5b, 0x8f, 0x07, 0x80, + 0x3e, 0xc1, 0xdb, 0x00, 0x75, 0x0a, 0xb8, 0x06, + 0x00, 0xe8, 0x0f, 0x4a, 0x40, 0xa2, 0xc1, 0xdb, + 0xc7, 0x06, 0xf3, 0xb4, 0x1e, 0x00, 0xb8, 0x12, + 0x00, 0xbb, 0x9f, 0x00, 0xbf, 0x31, 0x00, 0xbe, + 0xb4, 0x00, 0xb1, 0x01, 0xb5, 0x02, 0xe8, 0x72, + 0x44, 0xc3, 0xc7, 0x06, 0xf3, 0xb4, 0x1f, 0x00, + 0xb8, 0x00, 0x00, 0xbb, 0xbc, 0x00, 0xbf, 0x28, + 0x00, 0xbe, 0xbc, 0x00, 0xb1, 0x01, 0xb5, 0x02, + 0xe8, 0x58, 0x44, 0xc3, 0xc7, 0x06, 0xf3, 0xb4, + 0x1c, 0x00, 0xb8, 0xd0, 0x00, 0xbb, 0x99, 0x00, + 0xbf, 0xaa, 0x00, 0xbe, 0x99, 0x00, 0xb1, 0x01, + 0xb5, 0x04, 0xe8, 0x3e, 0x44, 0xc3, 0xc7, 0x06, + 0xaf, 0x64, 0x95, 0x00, 0xc7, 0x06, 0xb1, 0x64, + 0xa3, 0x00, 0xc6, 0x06, 0xcc, 0x64, 0x01, 0xc6, + 0x06, 0xdc, 0x64, 0x00, 0xc6, 0x06, 0xcb, 0x64, + 0x00, 0xe8, 0xb5, 0xd6, 0xc7, 0x06, 0xf3, 0xb4, + 0x1d, 0x00, 0xc6, 0x06, 0xdc, 0x1c, 0x01, 0xe8, + 0x55, 0x44, 0xbb, 0x46, 0x67, 0xe8, 0x02, 0xd8, + 0x83, 0xc3, 0x03, 0xff, 0x37, 0x53, 0xc7, 0x07, + 0x3f, 0x01, 0xb9, 0xce, 0x03, 0xc6, 0x06, 0xdc, + 0x1c, 0x01, 0xe8, 0x23, 0xb3, 0xbe, 0xa0, 0x00, + 0xbf, 0xbc, 0x00, 0xe8, 0x35, 0xce, 0x5b, 0x8f, + 0x07, 0xc3, 0xe8, 0xd9, 0x3c, 0xc3, 0xbb, 0xab, + 0x5b, 0xe8, 0x51, 0x38, 0xc3, 0xe8, 0x41, 0x00, + 0xb9, 0x20, 0x00, 0xb0, 0x0c, 0xb4, 0x09, 0xe8, + 0xeb, 0x41, 0xb9, 0xb6, 0x02, 0xe8, 0xf8, 0xb2, + 0xb9, 0x0f, 0x00, 0xb0, 0x08, 0xb4, 0x09, 0xe8, + 0xdb, 0x41, 0xbb, 0x47, 0x33, 0xc7, 0x07, 0xb5, + 0x02, 0xb0, 0x01, 0xe8, 0x3b, 0xb2, 0xe8, 0xb7, + 0xd7, 0xc6, 0x47, 0x06, 0x00, 0xe8, 0x3c, 0x46, + 0xbb, 0xc7, 0x4c, 0xe8, 0x17, 0x38, 0xb0, 0x36, + 0xe8, 0xf5, 0xbe, 0xb0, 0x04, 0xe8, 0x9d, 0xb1, + 0xc3, 0xbe, 0xd3, 0x00, 0xbf, 0x97, 0x00, 0x81, + 0x3e, 0xaf, 0x64, 0xd0, 0x00, 0x75, 0x1a, 0x81, + 0x3e, 0xb1, 0x64, 0x97, 0x00, 0x75, 0x12, 0xc6, + 0x06, 0xc3, 0x64, 0x02, 0x57, 0x56, 0xe8, 0xc2, + 0xcd, 0xb8, 0x09, 0x00, 0xe8, 0xcb, 0xb6, 0x5e, + 0x5f, 0xc6, 0x06, 0xc3, 0x64, 0x01, 0xe8, 0xb2, + 0xcd, 0xc3, 0x80, 0x3e, 0xc2, 0xdb, 0x01, 0x75, + 0x07, 0xbb, 0xa0, 0x4c, 0xe8, 0xce, 0x37, 0xc3, + 0xb9, 0x31, 0x00, 0xb0, 0x05, 0xb4, 0x09, 0xe8, + 0x6b, 0x41, 0xb0, 0x11, 0xe8, 0x6f, 0x41, 0xb9, + 0xb3, 0x02, 0xe8, 0x73, 0xb2, 0xe8, 0x46, 0x00, + 0x73, 0x31, 0xb9, 0x0f, 0x00, 0xb0, 0x08, 0xb4, + 0x09, 0xe8, 0x51, 0x41, 0xbb, 0x47, 0x33, 0xc7, + 0x07, 0xb4, 0x02, 0xb0, 0x01, 0xe8, 0xb1, 0xb1, + 0xe8, 0x2d, 0xd7, 0xc6, 0x47, 0x06, 0x3b, 0xe8, + 0xb2, 0x45, 0xb0, 0x04, 0xe8, 0x12, 0xb1, 0xbb, + 0x84, 0x4c, 0xe8, 0x88, 0x37, 0xc6, 0x06, 0xc2, + 0xdb, 0x01, 0xc3, 0x80, 0x3e, 0xc0, 0xdb, 0x01, + 0x74, 0x0b, 0xc6, 0x06, 0xc0, 0xdb, 0x01, 0xbb, + 0x61, 0x4c, 0xe8, 0x70, 0x37, 0xc3, 0xa0, 0xc1, + 0xdb, 0x98, 0x48, 0xbb, 0xb7, 0xdb, 0x53, 0x03, + 0xd8, 0x80, 0x3f, 0x01, 0x5b, 0x75, 0x18, 0x8b, + 0x07, 0x8b, 0x4f, 0x02, 0x8b, 0x57, 0x04, 0x02, + 0xc4, 0x02, 0xc1, 0x02, 0xc5, 0x02, 0xc2, 0x02, + 0xc6, 0x3c, 0x01, 0x75, 0x02, 0xf9, 0xc3, 0xf8, + 0xc3, 0xbb, 0xe7, 0x5b, 0xe8, 0x3e, 0x37, 0xc3, + 0xb0, 0x37, 0xe8, 0x4f, 0xbe, 0x73, 0x07, 0xbb, + 0xd9, 0x4c, 0xe8, 0x30, 0x37, 0xc3, 0x80, 0x3e, + 0xc3, 0xdb, 0x01, 0x74, 0x11, 0xc6, 0x06, 0xc3, + 0xdb, 0x01, 0xb9, 0xb7, 0x02, 0xe8, 0xe6, 0xb1, + 0xbb, 0x6a, 0x38, 0xe8, 0x87, 0xa8, 0xb9, 0x05, + 0x00, 0xb0, 0x0b, 0xb4, 0x0a, 0xe8, 0xb5, 0x40, + 0xb9, 0xb8, 0x02, 0xe8, 0xd0, 0xb1, 0xb0, 0x37, + 0xe8, 0xe5, 0xbd, 0xc3, 0xbb, 0x0b, 0x5c, 0xe8, + 0xfb, 0x36, 0xc3, 0xbb, 0x26, 0x5c, 0xe8, 0xf4, + 0x36, 0xc3, 0x80, 0x3e, 0xb7, 0xdb, 0x01, 0x74, + 0x2d, 0x80, 0x3e, 0xb8, 0xdb, 0x01, 0x75, 0x04, + 0xe8, 0x13, 0x02, 0xc3, 0xb9, 0x42, 0x00, 0xb0, + 0x04, 0xb4, 0x07, 0xe8, 0x7f, 0x40, 0xb9, 0xa5, + 0x02, 0xe8, 0x6b, 0xb1, 0xe8, 0x61, 0xd6, 0xc6, + 0x07, 0x35, 0xe8, 0xa3, 0x44, 0xe8, 0x9c, 0xb1, + 0xc6, 0x06, 0xb7, 0xdb, 0x01, 0xc3, 0xe8, 0xbe, + 0xb1, 0xe8, 0x4c, 0xd6, 0xc6, 0x07, 0x00, 0xe8, + 0xd2, 0x44, 0xb9, 0x43, 0x00, 0xb0, 0x04, 0xb4, + 0x07, 0xe8, 0x51, 0x40, 0xb9, 0xa6, 0x02, 0xe8, + 0x65, 0xb1, 0xc6, 0x06, 0xb7, 0xdb, 0x00, 0xc3, + 0x80, 0x3e, 0xb8, 0xdb, 0x01, 0x74, 0x39, 0x80, + 0x3e, 0xb7, 0xdb, 0x01, 0x75, 0x04, 0xe8, 0xbd, + 0x01, 0xc3, 0x80, 0x3e, 0xb9, 0xdb, 0x01, 0x75, + 0x04, 0xe8, 0xb2, 0x01, 0xc3, 0xb9, 0x42, 0x00, + 0xb0, 0x05, 0xb4, 0x07, 0xe8, 0x1e, 0x40, 0xb9, + 0xa7, 0x02, 0xe8, 0x0a, 0xb1, 0xe8, 0x00, 0xd6, + 0xc6, 0x47, 0x01, 0x36, 0xe8, 0x41, 0x44, 0xe8, + 0x3a, 0xb1, 0xc6, 0x06, 0xb8, 0xdb, 0x01, 0xc3, + 0xe8, 0x5c, 0xb1, 0xe8, 0xea, 0xd5, 0xc6, 0x47, + 0x01, 0x00, 0xe8, 0x6f, 0x44, 0xb9, 0x43, 0x00, + 0xb0, 0x04, 0xb4, 0x07, 0xe8, 0xee, 0x3f, 0xb9, + 0xa8, 0x02, 0xe8, 0x02, 0xb1, 0xc6, 0x06, 0xb8, + 0xdb, 0x00, 0xc3, 0x80, 0x3e, 0xb9, 0xdb, 0x01, + 0x74, 0x2e, 0x80, 0x3e, 0xb8, 0xdb, 0x01, 0x75, + 0x04, 0xe8, 0x5a, 0x01, 0xc3, 0xb9, 0x43, 0x00, + 0xb0, 0x05, 0xb4, 0x07, 0xe8, 0xc6, 0x3f, 0xb9, + 0xa9, 0x02, 0xe8, 0xb2, 0xb0, 0xe8, 0xa8, 0xd5, + 0xc6, 0x47, 0x02, 0x37, 0xe8, 0xe9, 0x43, 0xe8, + 0xe2, 0xb0, 0xc6, 0x06, 0xb9, 0xdb, 0x01, 0xc3, + 0xe8, 0x04, 0xb1, 0xe8, 0x92, 0xd5, 0xc6, 0x47, + 0x02, 0x00, 0xe8, 0x17, 0x44, 0xb9, 0x43, 0x00, + 0xb0, 0x05, 0xb4, 0x07, 0xe8, 0x96, 0x3f, 0xb9, + 0xaa, 0x02, 0xe8, 0xaa, 0xb0, 0xc6, 0x06, 0xb9, + 0xdb, 0x00, 0xc3, 0x80, 0x3e, 0xba, 0xdb, 0x01, + 0x74, 0x2e, 0x80, 0x3e, 0xbb, 0xdb, 0x01, 0x75, + 0x04, 0xe8, 0x02, 0x01, 0xc3, 0xb9, 0x42, 0x00, + 0xb0, 0x04, 0xb4, 0x07, 0xe8, 0x6e, 0x3f, 0xb9, + 0xab, 0x02, 0xe8, 0x5a, 0xb0, 0xe8, 0x50, 0xd5, + 0xc6, 0x47, 0x03, 0x38, 0xe8, 0x91, 0x43, 0xe8, + 0x8a, 0xb0, 0xc6, 0x06, 0xba, 0xdb, 0x01, 0xc3, + 0xe8, 0xac, 0xb0, 0xe8, 0x3a, 0xd5, 0xc6, 0x47, + 0x03, 0x00, 0xe8, 0xbf, 0x43, 0xb9, 0x43, 0x00, + 0xb0, 0x04, 0xb4, 0x07, 0xe8, 0x3e, 0x3f, 0xb9, + 0xac, 0x02, 0xe8, 0x52, 0xb0, 0xc6, 0x06, 0xba, + 0xdb, 0x00, 0xc3, 0x80, 0x3e, 0xbb, 0xdb, 0x01, + 0x74, 0x39, 0x80, 0x3e, 0xba, 0xdb, 0x01, 0x75, + 0x04, 0xe8, 0xaa, 0x00, 0xc3, 0x80, 0x3e, 0xbc, + 0xdb, 0x01, 0x75, 0x04, 0xe8, 0x9f, 0x00, 0xc3, + 0xb9, 0x42, 0x00, 0xb0, 0x05, 0xb4, 0x07, 0xe8, + 0x0b, 0x3f, 0xb9, 0xad, 0x02, 0xe8, 0xf7, 0xaf, + 0xe8, 0xed, 0xd4, 0xc6, 0x47, 0x04, 0x39, 0xe8, + 0x2e, 0x43, 0xe8, 0x27, 0xb0, 0xc6, 0x06, 0xbb, + 0xdb, 0x01, 0xc3, 0xe8, 0x49, 0xb0, 0xe8, 0xd7, + 0xd4, 0xc6, 0x47, 0x04, 0x00, 0xe8, 0x5c, 0x43, + 0xb9, 0x43, 0x00, 0xb0, 0x04, 0xb4, 0x07, 0xe8, + 0xdb, 0x3e, 0xb9, 0xae, 0x02, 0xe8, 0xef, 0xaf, + 0xc6, 0x06, 0xbb, 0xdb, 0x00, 0xc3, 0x80, 0x3e, + 0xbc, 0xdb, 0x01, 0x74, 0x2e, 0x80, 0x3e, 0xbb, + 0xdb, 0x01, 0x75, 0x04, 0xe8, 0x47, 0x00, 0xc3, + 0xb9, 0x42, 0x00, 0xb0, 0x06, 0xb4, 0x07, 0xe8, + 0xb3, 0x3e, 0xb9, 0xaf, 0x02, 0xe8, 0x9f, 0xaf, + 0xe8, 0x95, 0xd4, 0xc6, 0x47, 0x05, 0x3a, 0xe8, + 0xd6, 0x42, 0xe8, 0xcf, 0xaf, 0xc6, 0x06, 0xbc, + 0xdb, 0x01, 0xc3, 0xe8, 0xf1, 0xaf, 0xe8, 0x7f, + 0xd4, 0xc6, 0x47, 0x05, 0x00, 0xe8, 0x04, 0x43, + 0xb9, 0x43, 0x00, 0xb0, 0x05, 0xb4, 0x07, 0xe8, + 0x83, 0x3e, 0xb9, 0xb0, 0x02, 0xe8, 0x97, 0xaf, + 0xc6, 0x06, 0xbc, 0xdb, 0x00, 0xc3, 0x80, 0x3e, + 0xbd, 0xdb, 0x01, 0x74, 0x12, 0xbb, 0xcd, 0x4a, + 0xe8, 0xc2, 0x34, 0xbb, 0x0d, 0x4b, 0xe8, 0xbc, + 0x34, 0xc6, 0x06, 0xbd, 0xdb, 0x01, 0xc3, 0xbb, + 0x39, 0x4b, 0xe8, 0xb0, 0x34, 0xc3, 0xbb, 0x46, + 0x5c, 0xe8, 0xa9, 0x34, 0xc3, 0xb9, 0x50, 0x00, + 0xb0, 0x04, 0xb4, 0x0a, 0xe8, 0x46, 0x3e, 0xb9, + 0xcb, 0x03, 0xe8, 0x32, 0xaf, 0xe8, 0xd3, 0x3f, + 0xc7, 0x06, 0xf3, 0xb4, 0x20, 0x00, 0xb8, 0x8b, + 0x00, 0xbb, 0xc7, 0x00, 0xbf, 0x8b, 0x00, 0xbe, + 0xbc, 0x00, 0xb1, 0x13, 0xb5, 0x01, 0xe8, 0x0a, + 0x40, 0xc3, 0xbb, 0x65, 0x5b, 0xe8, 0x75, 0x34, + 0xc3, 0x80, 0x3e, 0xd9, 0xdb, 0x01, 0x75, 0x07, + 0xbb, 0x26, 0x53, 0xe8, 0x67, 0x34, 0xc3, 0xb9, + 0x58, 0x00, 0xb0, 0x04, 0xb4, 0x0e, 0xe8, 0x04, + 0x3e, 0xb9, 0x28, 0x03, 0xe8, 0xf0, 0xae, 0xe8, + 0x91, 0x3f, 0xc7, 0x06, 0xf3, 0xb4, 0x24, 0x00, + 0xb8, 0x29, 0x00, 0xbb, 0xc3, 0x00, 0xbf, 0x32, + 0x00, 0xbe, 0xc3, 0x00, 0xb1, 0x01, 0xb5, 0x02, + 0xe8, 0xc8, 0x3f, 0xc3, 0xe8, 0xb7, 0x38, 0xc3, + 0xe8, 0xb3, 0x38, 0xc3, 0xbb, 0x51, 0x5b, 0xe8, + 0x2b, 0x34, 0xc3, 0xc7, 0x06, 0xf3, 0xb4, 0x1d, + 0x00, 0xb8, 0x3f, 0x01, 0xbb, 0xbc, 0x00, 0xbf, + 0x18, 0x01, 0xbe, 0xbc, 0x00, 0xb1, 0x01, 0xb5, + 0x04, 0xe8, 0x9f, 0x3f, 0xc3, 0xb9, 0x59, 0x00, + 0xb0, 0x06, 0xb4, 0x0c, 0xe8, 0xae, 0x3d, 0xb9, + 0x26, 0x03, 0x80, 0x3e, 0xef, 0xdb, 0x01, 0x75, + 0x03, 0xb9, 0xd9, 0x03, 0xe8, 0x90, 0xae, 0xe8, + 0x31, 0x3f, 0xc7, 0x06, 0xf3, 0xb4, 0x22, 0x00, + 0xb8, 0x28, 0x00, 0xbb, 0x85, 0x00, 0xbf, 0x34, + 0x00, 0xbe, 0x85, 0x00, 0xb1, 0x01, 0xb5, 0x02, + 0xe8, 0x68, 0x3f, 0xc3, 0xbb, 0x80, 0x5b, 0xe8, + 0xd3, 0x33, 0xc3, 0xe8, 0xbb, 0xac, 0xbb, 0x2e, + 0xdb, 0xe8, 0x21, 0xa5, 0xe8, 0x36, 0xa5, 0xb0, + 0x01, 0xbe, 0x94, 0xaa, 0xe8, 0x5a, 0xad, 0xc6, + 0x06, 0xd1, 0xdb, 0x01, 0xc3, 0xe8, 0xb7, 0xae, + 0xe8, 0x45, 0xd3, 0xc6, 0x07, 0x47, 0xe8, 0xcb, + 0x41, 0xb9, 0x20, 0x00, 0xb0, 0x05, 0xb4, 0x04, + 0xe8, 0x4a, 0x3d, 0xb9, 0xdc, 0x02, 0xe8, 0x5e, + 0xae, 0xb0, 0x02, 0xe8, 0x27, 0xad, 0xb0, 0x48, + 0xe8, 0x75, 0xba, 0xc3, 0xb9, 0xf2, 0x02, 0xe8, + 0x25, 0xae, 0xe8, 0xc6, 0x3e, 0xbe, 0xde, 0x76, + 0xbb, 0x7b, 0x51, 0x06, 0x53, 0x56, 0xb8, 0x00, + 0xa0, 0x8e, 0xc0, 0xe8, 0x67, 0x2f, 0xb8, 0x00, + 0xa0, 0xb9, 0x00, 0x7d, 0xe8, 0x19, 0x40, 0x5e, + 0x5b, 0xe8, 0x69, 0x37, 0xe8, 0x65, 0x2f, 0xb8, + 0x0e, 0x01, 0xe8, 0x67, 0xb2, 0xe8, 0x4d, 0x2f, + 0xa1, 0xb3, 0x32, 0x8e, 0xc0, 0xbf, 0x00, 0xfa, + 0xb9, 0x80, 0x01, 0x33, 0xc0, 0xfc, 0xf3, 0xab, + 0x07, 0xb9, 0x03, 0x00, 0xe8, 0x3c, 0x44, 0xc7, + 0x06, 0xf3, 0xb4, 0x0b, 0x00, 0xe8, 0xf4, 0x3f, + 0xbb, 0x47, 0x33, 0xc7, 0x47, 0x02, 0xee, 0x02, + 0xb0, 0x02, 0xe8, 0x3d, 0xad, 0xc6, 0x06, 0x45, + 0x33, 0xe5, 0xc6, 0x06, 0x46, 0x33, 0xd9, 0xb0, + 0x02, 0xb4, 0x01, 0xbe, 0xef, 0x02, 0xbf, 0x11, + 0x02, 0xbb, 0x50, 0x4f, 0xe8, 0x2f, 0xa7, 0xbb, + 0x47, 0x33, 0xc7, 0x07, 0xf0, 0x02, 0xc7, 0x47, + 0x02, 0xf1, 0x02, 0xb0, 0x01, 0xe8, 0x12, 0xad, + 0xc6, 0x06, 0x45, 0x33, 0xd9, 0xc6, 0x06, 0x46, + 0x33, 0xe5, 0xb0, 0x01, 0xb4, 0x02, 0xbe, 0x11, + 0x02, 0xbf, 0xef, 0x02, 0xbb, 0x68, 0x51, 0xe8, + 0x04, 0xa7, 0xbb, 0x47, 0x33, 0xc7, 0x47, 0x02, + 0xdd, 0x02, 0xc7, 0x47, 0x04, 0xde, 0x02, 0xc7, + 0x06, 0xf3, 0xb4, 0x1e, 0x00, 0xe8, 0x8c, 0x3f, + 0xc6, 0x06, 0x45, 0x33, 0xe5, 0xc6, 0x06, 0x46, + 0x33, 0xd0, 0xb0, 0x02, 0xb4, 0x03, 0xbe, 0xdd, + 0x02, 0xbf, 0xde, 0x02, 0xbb, 0x9e, 0x44, 0xe8, + 0xd4, 0xa6, 0xb9, 0x4b, 0x00, 0xb0, 0x0d, 0xb4, + 0x04, 0xe8, 0x59, 0x3c, 0xb9, 0x20, 0x00, 0xb0, + 0x16, 0xe8, 0x5a, 0x3c, 0xbb, 0x47, 0x33, 0xc7, + 0x47, 0x02, 0xdf, 0x02, 0xc7, 0x47, 0x04, 0xe0, + 0x02, 0xb0, 0x02, 0xe8, 0xa4, 0xac, 0xc6, 0x06, + 0x46, 0x33, 0xe5, 0xc6, 0x06, 0x45, 0x33, 0xd0, + 0xb0, 0x03, 0xb4, 0x02, 0xbe, 0xe1, 0x02, 0xbf, + 0xe2, 0x02, 0xbb, 0xcf, 0x46, 0xe8, 0x96, 0xa6, + 0xb9, 0x20, 0x00, 0xb0, 0x01, 0xb4, 0x04, 0xe8, + 0x1b, 0x3c, 0xbb, 0x47, 0x33, 0xc7, 0x47, 0x02, + 0xe3, 0x02, 0xc7, 0x47, 0x04, 0xe4, 0x02, 0xb0, + 0x02, 0xe8, 0x6e, 0xac, 0xc6, 0x06, 0x45, 0x33, + 0xe5, 0xc6, 0x06, 0x46, 0x33, 0xd0, 0xb0, 0x02, + 0xb4, 0x03, 0xbe, 0xdd, 0x02, 0xbf, 0xde, 0x02, + 0xbb, 0x72, 0x47, 0xe8, 0x60, 0xa6, 0xbb, 0x47, + 0x33, 0xc7, 0x47, 0x02, 0xe6, 0x02, 0xc7, 0x47, + 0x04, 0xe5, 0x02, 0xb0, 0x02, 0xe8, 0x42, 0xac, + 0xc6, 0x06, 0x46, 0x33, 0xe5, 0xc6, 0x06, 0x45, + 0x33, 0xd0, 0xb0, 0x03, 0xb4, 0x02, 0xbe, 0xe7, + 0x02, 0xbf, 0xdd, 0x02, 0xbb, 0x1c, 0x48, 0xe8, + 0x34, 0xa6, 0xbb, 0x47, 0x33, 0xc7, 0x47, 0x02, + 0xe8, 0x02, 0xc7, 0x47, 0x04, 0xe9, 0x02, 0xb0, + 0x02, 0xe8, 0x16, 0xac, 0xb0, 0x03, 0xb4, 0x02, + 0xbe, 0xde, 0x02, 0xbf, 0xdd, 0x02, 0xbb, 0x73, + 0x48, 0xe8, 0x12, 0xa6, 0xbb, 0x47, 0x33, 0xc7, + 0x47, 0x02, 0xea, 0x02, 0xc7, 0x47, 0x04, 0xeb, + 0x02, 0xb0, 0x02, 0xe8, 0xf4, 0xab, 0xe8, 0x2d, + 0x3c, 0xb0, 0x03, 0xbe, 0xde, 0x02, 0xbb, 0xa5, + 0x4d, 0xe8, 0x7b, 0xa7, 0xb0, 0x03, 0xbe, 0xec, + 0x02, 0xbb, 0xb9, 0x4e, 0xe8, 0x70, 0xa7, 0xb0, + 0x03, 0xbe, 0xed, 0x02, 0xbb, 0x15, 0x4f, 0xe8, + 0x65, 0xa7, 0xb0, 0x03, 0xbe, 0xec, 0x02, 0xbb, + 0x2f, 0x4f, 0xe8, 0x5a, 0xa7, 0xb9, 0x0a, 0x00, + 0xe8, 0xa8, 0x42, 0xc7, 0x06, 0xf3, 0xb4, 0x20, + 0x00, 0xe8, 0x60, 0x3e, 0xb9, 0x1a, 0x00, 0xb0, + 0x0a, 0xb4, 0x04, 0xe8, 0x47, 0x3b, 0xb9, 0xf3, + 0x02, 0xe8, 0x5b, 0xac, 0x8b, 0x36, 0xaf, 0x64, + 0x8b, 0x3e, 0xb1, 0x64, 0xc6, 0x06, 0xc3, 0x64, + 0x03, 0xe8, 0x5f, 0xc7, 0xbb, 0xbf, 0x51, 0xe8, + 0xf3, 0xa2, 0xbb, 0x47, 0x33, 0xc7, 0x07, 0xfb, + 0x02, 0xc7, 0x47, 0x02, 0xfc, 0x02, 0xc7, 0x06, + 0xf3, 0xb4, 0x1f, 0x00, 0xe8, 0x25, 0x3e, 0xc6, + 0x06, 0x45, 0x33, 0xd9, 0xc6, 0x06, 0x46, 0x33, + 0xd0, 0xb0, 0x01, 0xb4, 0x02, 0xbe, 0xfb, 0x02, + 0xbf, 0xfc, 0x02, 0xbb, 0x9f, 0x53, 0xe8, 0x6d, + 0xa5, 0xc7, 0x06, 0xf3, 0xb4, 0x20, 0x00, 0xe8, + 0x1d, 0x3d, 0xbb, 0xc3, 0x52, 0xe8, 0xb5, 0xa2, + 0xb0, 0x03, 0xe8, 0xd0, 0xaa, 0xb0, 0x07, 0xe8, + 0xbf, 0xaa, 0xc6, 0x06, 0xd5, 0xdb, 0x01, 0xc3, + 0x80, 0x3e, 0xd5, 0xdb, 0x01, 0x75, 0x07, 0xbb, + 0xa7, 0x51, 0xe8, 0x28, 0x31, 0xc3, 0xe8, 0xa5, + 0x35, 0xc3, 0x80, 0x3e, 0xd5, 0xdb, 0x01, 0x75, + 0x07, 0xbb, 0xa7, 0x51, 0xe8, 0x16, 0x31, 0xc3, + 0xb9, 0xd1, 0x03, 0xe8, 0xca, 0xab, 0xbb, 0x11, + 0x55, 0xe8, 0x09, 0x31, 0xc3, 0x80, 0x3e, 0xd5, + 0xdb, 0x01, 0x75, 0x07, 0xbb, 0xbb, 0x51, 0xe8, + 0xfb, 0x30, 0xc3, 0xc7, 0x06, 0xf3, 0xb4, 0x1f, + 0x00, 0xb8, 0x8b, 0x00, 0xbb, 0xac, 0x00, 0xbf, + 0x8b, 0x00, 0xbe, 0xb5, 0x00, 0xb1, 0x0b, 0xb5, + 0x03, 0xe8, 0x6f, 0x3c, 0xc3, 0xe8, 0xdf, 0xab, + 0x8b, 0x36, 0xaf, 0x64, 0x8b, 0x3e, 0xb1, 0x64, + 0xc6, 0x06, 0xc3, 0x64, 0x03, 0xe8, 0xa3, 0xc6, + 0xb9, 0xf4, 0x02, 0xe8, 0x64, 0xab, 0xe8, 0x02, + 0x3c, 0xc6, 0x06, 0xdc, 0x1c, 0x01, 0xbb, 0x47, + 0x33, 0xc7, 0x47, 0x02, 0xf6, 0x02, 0xb0, 0x02, + 0xe8, 0xbf, 0xaa, 0xc6, 0x06, 0x45, 0x33, 0xd0, + 0xb0, 0x02, 0xbe, 0xf7, 0x02, 0xbb, 0xe6, 0x52, + 0xe8, 0x44, 0xa6, 0xb9, 0x28, 0x00, 0xb0, 0x05, + 0xb4, 0x04, 0xe8, 0x40, 0x3a, 0xb9, 0x34, 0x00, + 0xb0, 0x0d, 0xe8, 0x41, 0x3a, 0xb0, 0x11, 0xe8, + 0x43, 0x3a, 0xb0, 0x15, 0xe8, 0x45, 0x3a, 0xbb, + 0x47, 0x33, 0xc7, 0x47, 0x02, 0xf8, 0x02, 0xb0, + 0x02, 0xe8, 0x86, 0xaa, 0xe8, 0x0d, 0x3b, 0xe8, + 0x06, 0xd0, 0xc6, 0x47, 0x01, 0x48, 0xc6, 0x47, + 0x02, 0x49, 0xbb, 0x47, 0x33, 0xc7, 0x07, 0xfd, + 0x02, 0xc7, 0x06, 0xf3, 0xb4, 0x1f, 0x00, 0xe8, + 0x12, 0x3d, 0xb9, 0x3a, 0x00, 0xb0, 0x05, 0xb4, + 0x07, 0xe8, 0xf9, 0x39, 0xb0, 0x08, 0xe8, 0xfd, + 0x39, 0xb0, 0x0a, 0xe8, 0xff, 0x39, 0xb0, 0x0c, + 0xe8, 0x01, 0x3a, 0xb0, 0x0e, 0xe8, 0x03, 0x3a, + 0xb0, 0x01, 0xe8, 0x45, 0xaa, 0xc6, 0x06, 0x45, + 0x33, 0xd9, 0xb0, 0x01, 0xbe, 0xfe, 0x02, 0xbb, + 0x43, 0x54, 0xe8, 0xca, 0xa5, 0xbb, 0x47, 0x33, + 0xc7, 0x47, 0x02, 0xf9, 0x02, 0xc7, 0x06, 0xf3, + 0xb4, 0x20, 0x00, 0xe8, 0xce, 0x3c, 0xc6, 0x06, + 0x45, 0x33, 0xd0, 0xb0, 0x02, 0xbe, 0xf9, 0x02, + 0xbb, 0x58, 0x53, 0xe8, 0xa9, 0xa5, 0xbb, 0x47, + 0x33, 0xc7, 0x47, 0x02, 0xfa, 0x02, 0xb0, 0x02, + 0xe8, 0x07, 0xaa, 0xe8, 0x40, 0x3a, 0xe8, 0x87, + 0xcf, 0xc6, 0x47, 0x02, 0x00, 0xe8, 0xa5, 0x3d, + 0xb9, 0xf5, 0x02, 0xe8, 0x84, 0xaa, 0xc6, 0x06, + 0xcd, 0x64, 0x01, 0xc6, 0x06, 0xcc, 0x64, 0x01, + 0xc6, 0x06, 0xcb, 0x64, 0x01, 0xe8, 0xac, 0xaa, + 0xbb, 0xe7, 0x51, 0xe8, 0xcf, 0x2f, 0xb0, 0x08, + 0xe8, 0x4e, 0xa9, 0xb0, 0x07, 0xe8, 0x55, 0xa9, + 0xc6, 0x06, 0xd5, 0xdb, 0x00, 0xc3, 0xb9, 0x20, + 0x00, 0xb0, 0x05, 0xb4, 0x08, 0xe8, 0x5d, 0x39, + 0xb9, 0xff, 0x02, 0xe8, 0x6a, 0xaa, 0xe8, 0x3f, + 0xcf, 0xc6, 0x47, 0x01, 0x00, 0xe8, 0xc4, 0x3d, + 0xb0, 0x49, 0xe8, 0x83, 0xb6, 0xb0, 0x08, 0xe8, + 0x2b, 0xa9, 0xc3, 0xe8, 0x60, 0xa8, 0xbb, 0x24, + 0xdb, 0xe8, 0xe9, 0xa0, 0xe8, 0xfe, 0xa0, 0xc3, + 0xe8, 0x15, 0x00, 0xbe, 0x51, 0x00, 0xbf, 0xa0, + 0x00, 0xc6, 0x06, 0xc3, 0x64, 0x04, 0xe8, 0x52, + 0xc5, 0xbb, 0xac, 0x5c, 0xe8, 0x76, 0x2f, 0xc3, + 0x80, 0x3e, 0xcc, 0xdb, 0x01, 0x74, 0x07, 0xbb, + 0xce, 0x4e, 0xe8, 0x68, 0x2f, 0x58, 0xc3, 0xe8, + 0xee, 0xff, 0xbb, 0x46, 0x50, 0xe8, 0x5d, 0x2f, + 0xc3, 0xb0, 0x44, 0xe8, 0x6e, 0xb6, 0x73, 0x12, + 0xb0, 0x44, 0xe8, 0x43, 0xb6, 0xe8, 0x0a, 0x00, + 0xe8, 0x23, 0xc5, 0xbb, 0x0a, 0x50, 0xe8, 0x44, + 0x2f, 0xc3, 0xc7, 0x06, 0xf3, 0xb4, 0x1d, 0x00, + 0xb8, 0x28, 0x00, 0xbb, 0xb0, 0x00, 0xbf, 0x3e, + 0x00, 0xbe, 0xb7, 0x00, 0xb1, 0x01, 0xb5, 0x02, + 0xe8, 0xb8, 0x3a, 0xc3, 0xe8, 0xb1, 0xff, 0x80, + 0x3e, 0xcf, 0xdb, 0x01, 0x74, 0x48, 0xe8, 0x1e, + 0xaa, 0xc6, 0x06, 0xe6, 0x1c, 0xe5, 0xc6, 0x06, + 0x35, 0x33, 0x0b, 0xc6, 0x06, 0x36, 0x33, 0x23, + 0xb8, 0xcb, 0x4f, 0xa3, 0x37, 0x33, 0xb8, 0x70, + 0x87, 0xa3, 0x39, 0x33, 0xb9, 0x59, 0x00, 0xb0, + 0x04, 0xb4, 0x09, 0xe8, 0x9f, 0x38, 0xb9, 0x57, + 0x00, 0xb0, 0x2d, 0xe8, 0xa0, 0x38, 0xb9, 0xce, + 0x02, 0xe8, 0x03, 0xaa, 0xe8, 0xbd, 0xa9, 0xe8, + 0xe3, 0xad, 0xbb, 0xe2, 0x4f, 0xe8, 0xdd, 0x2e, + 0xc6, 0x06, 0xcf, 0xdb, 0x01, 0xc3, 0xb9, 0x59, + 0x00, 0xb0, 0x04, 0xb4, 0x09, 0xe8, 0x75, 0x38, + 0xb9, 0xcf, 0x02, 0xe8, 0x61, 0xa9, 0xe8, 0x57, + 0xce, 0xc6, 0x47, 0x04, 0x43, 0xe8, 0x98, 0x3c, + 0xe8, 0x91, 0xa9, 0xbb, 0x46, 0x67, 0xe8, 0x39, + 0xce, 0xfe, 0x07, 0xb0, 0x05, 0xe8, 0x3d, 0xa8, + 0xb0, 0x0c, 0xe8, 0x2c, 0xa8, 0xc3, 0x80, 0x3e, + 0xcd, 0xdb, 0x01, 0x74, 0x07, 0xbb, 0x9d, 0x3c, + 0xe8, 0x0a, 0xa0, 0xc3, 0x80, 0x3e, 0xce, 0xdb, + 0x01, 0x75, 0x07, 0xbb, 0x9b, 0x4f, 0xe8, 0x8c, + 0x2e, 0xc3, 0xbb, 0xb1, 0x4f, 0xe8, 0x85, 0x2e, + 0xb9, 0x20, 0x00, 0xb0, 0x06, 0xb4, 0x07, 0xe8, + 0x23, 0x38, 0xb9, 0xcd, 0x02, 0xe8, 0x30, 0xa9, + 0xb0, 0x42, 0xe8, 0x53, 0xb5, 0xc6, 0x06, 0xce, + 0xdb, 0x01, 0xc3, 0xe8, 0x69, 0xa9, 0xe8, 0xf7, + 0xcd, 0xc6, 0x47, 0x02, 0x00, 0xe8, 0x7c, 0x3c, + 0xb9, 0x20, 0x00, 0xb0, 0x07, 0xb4, 0x08, 0xe8, + 0xfb, 0x37, 0xb9, 0xc6, 0x02, 0xe8, 0x0f, 0xa9, + 0xb0, 0x3e, 0xe8, 0x2b, 0xb5, 0xb0, 0x07, 0xe8, + 0xd3, 0xa7, 0xb0, 0x08, 0xe8, 0xc2, 0xa7, 0xc3, + 0xe8, 0xbb, 0x32, 0xc3, 0xe8, 0x38, 0xa9, 0xe8, + 0xc6, 0xcd, 0xc6, 0x47, 0x03, 0x00, 0xe8, 0x4b, + 0x3c, 0xb9, 0x20, 0x00, 0xb0, 0x07, 0xb4, 0x0b, + 0xe8, 0xca, 0x37, 0xb9, 0xcb, 0x02, 0xe8, 0xde, + 0xa8, 0xb0, 0x3f, 0xe8, 0xfa, 0xb4, 0xb0, 0x09, + 0xe8, 0xa2, 0xa7, 0xc3, 0xe8, 0x99, 0xfe, 0xbb, + 0x60, 0x5c, 0xe8, 0x08, 0x2e, 0xc3, 0xbb, 0x82, + 0x5c, 0xe8, 0x01, 0x2e, 0xc3, 0x80, 0x3e, 0xd0, + 0xdb, 0x01, 0x74, 0x0d, 0xb9, 0xd1, 0x02, 0xe8, + 0xae, 0xa8, 0xbb, 0x5e, 0x50, 0xe8, 0xed, 0x2d, + 0xc3, 0xe8, 0xeb, 0xa8, 0xe8, 0x79, 0xcd, 0xc6, + 0x47, 0x04, 0x45, 0xe8, 0xfe, 0x3b, 0xb9, 0x20, + 0x00, 0xb0, 0x05, 0xb4, 0x08, 0xe8, 0x7d, 0x37, + 0xb9, 0xd5, 0x02, 0xe8, 0x91, 0xa8, 0xb0, 0x0c, + 0xe8, 0x5a, 0xa7, 0xb0, 0x45, 0xe8, 0xa8, 0xb4, + 0xc3, 0xb9, 0x59, 0x00, 0xb0, 0x03, 0xb4, 0x01, + 0xe8, 0x62, 0x37, 0xb9, 0xcf, 0x03, 0xe8, 0x5e, + 0xa8, 0xe8, 0xef, 0x38, 0xc7, 0x06, 0xf3, 0xb4, + 0x1f, 0x00, 0xb8, 0x2a, 0x01, 0xbb, 0xb1, 0x00, + 0xbf, 0x16, 0x01, 0xbe, 0xb1, 0x00, 0xb1, 0x01, + 0xb5, 0x04, 0xe8, 0x26, 0x39, 0xc3, 0xe8, 0x15, + 0x32, 0xc3, 0x80, 0x3e, 0xd6, 0xdb, 0x02, 0x75, + 0x07, 0xbb, 0x2c, 0x52, 0xe8, 0x86, 0x2d, 0xc3, + 0xb9, 0x4f, 0x00, 0xb0, 0x06, 0xb4, 0x0e, 0xe8, + 0x23, 0x37, 0xb9, 0x54, 0x00, 0xb0, 0x09, 0xe8, + 0x24, 0x37, 0xb9, 0x21, 0x03, 0xe8, 0x28, 0xa8, + 0xe8, 0x63, 0xac, 0x80, 0x3e, 0xd6, 0xdb, 0x01, + 0x74, 0x07, 0xbb, 0x72, 0x53, 0xe8, 0x5d, 0x2d, + 0xc3, 0xbb, 0x8d, 0x53, 0xe8, 0x56, 0x2d, 0xc6, + 0x06, 0xd6, 0xdb, 0x02, 0xc3, 0xe8, 0xce, 0x31, + 0xc3, 0xb9, 0x42, 0x00, 0xb0, 0x05, 0xb4, 0x03, + 0xe8, 0xea, 0x36, 0xb9, 0x43, 0x00, 0xb0, 0x0b, + 0xe8, 0xeb, 0x36, 0xb9, 0xd0, 0x03, 0xe8, 0xef, + 0xa7, 0xbb, 0x55, 0x59, 0xe8, 0x2e, 0x2d, 0xc3, + 0xbb, 0xdb, 0x5c, 0xe8, 0x27, 0x2d, 0xc3, 0xbb, + 0xfd, 0x5c, 0xe8, 0x20, 0x2d, 0xc3, 0xe8, 0x1e, + 0xa8, 0xe8, 0xac, 0xcc, 0xc6, 0x47, 0x01, 0x00, + 0xe8, 0x31, 0x3b, 0xb9, 0x05, 0x00, 0xb0, 0x2a, + 0xb4, 0x07, 0xe8, 0xb0, 0x36, 0xc6, 0x06, 0x35, + 0x33, 0x14, 0xc6, 0x06, 0x36, 0x33, 0x26, 0xb8, + 0x02, 0x4d, 0xa3, 0x37, 0x33, 0xb8, 0x82, 0x7f, + 0xa3, 0x39, 0x33, 0xc6, 0x06, 0xdc, 0x64, 0x00, + 0xb9, 0xb9, 0x02, 0xe8, 0x01, 0xa8, 0xc6, 0x06, + 0xdc, 0x64, 0x01, 0xe8, 0xb6, 0xa7, 0xb0, 0x38, + 0xe8, 0xbd, 0xb3, 0xb0, 0x01, 0xe8, 0x65, 0xa6, + 0xc3, 0xb9, 0x05, 0x00, 0xb0, 0x0c, 0xb4, 0x07, + 0xe8, 0x72, 0x36, 0xb9, 0xc0, 0x02, 0xe8, 0x8d, + 0xa7, 0xb0, 0x02, 0xe8, 0x4f, 0xa6, 0xb0, 0x3a, + 0xe8, 0x9d, 0xb3, 0xc3, 0xbb, 0x2c, 0x5d, 0xe8, + 0xb3, 0x2c, 0xc3, 0x80, 0x3e, 0xc5, 0xdb, 0x01, + 0x74, 0x20, 0xc6, 0x06, 0xc5, 0xdb, 0x01, 0x80, + 0x3e, 0xc6, 0xdb, 0x00, 0x74, 0x05, 0xb8, 0xbe, + 0x02, 0xeb, 0x03, 0xb8, 0xbd, 0x02, 0xe8, 0x25, + 0x00, 0xb0, 0x01, 0xe8, 0x61, 0xa6, 0xe8, 0x23, + 0x37, 0xc3, 0xc6, 0x06, 0xc5, 0xdb, 0x00, 0xb8, + 0xbd, 0x02, 0xe8, 0x11, 0x00, 0xe8, 0xc6, 0x36, + 0x80, 0x3e, 0xc6, 0xdb, 0x01, 0x75, 0x06, 0xbb, + 0xa6, 0x4d, 0xe8, 0x70, 0x2c, 0xc3, 0x50, 0xb9, + 0x47, 0x00, 0xb0, 0x03, 0xb4, 0x06, 0xe8, 0x0c, + 0x36, 0xb9, 0xbc, 0x02, 0xe8, 0xf8, 0xa6, 0xe8, + 0x99, 0x37, 0x58, 0xbb, 0x47, 0x33, 0x89, 0x07, + 0xc3, 0xbb, 0x41, 0x5d, 0xe8, 0x4e, 0x2c, 0xc3, + 0x80, 0x3e, 0xc4, 0xdb, 0x01, 0x75, 0x07, 0xbb, + 0x2a, 0x4d, 0xe8, 0x40, 0x2c, 0xc3, 0xe8, 0x3e, + 0xa7, 0xe8, 0xcc, 0xcb, 0xc6, 0x07, 0x00, 0xe8, + 0x52, 0x3a, 0xb9, 0x1a, 0x00, 0xb0, 0x11, 0xb4, + 0x0a, 0xe8, 0xd1, 0x35, 0xb0, 0x17, 0xe8, 0xd5, + 0x35, 0xb0, 0x1e, 0xe8, 0xd7, 0x35, 0xb0, 0x25, + 0xe8, 0xe0, 0x35, 0xb0, 0x2b, 0xe8, 0xe2, 0x35, + 0xb9, 0x34, 0x00, 0xb0, 0x22, 0xe8, 0xcc, 0x35, + 0xb9, 0xba, 0x02, 0xe8, 0xa4, 0xa6, 0xe8, 0x97, + 0xcb, 0xc6, 0x07, 0x34, 0xc6, 0x47, 0x02, 0x3d, + 0xe8, 0xd5, 0x39, 0xe8, 0xce, 0xa6, 0xbb, 0xb6, + 0x38, 0xe8, 0x61, 0x9d, 0xb0, 0x0b, 0xe8, 0x70, + 0xa5, 0xc6, 0x06, 0xc4, 0xdb, 0x01, 0xc3, 0xbb, + 0x6e, 0x5d, 0xe8, 0xe0, 0x2b, 0xc3, 0x80, 0x3e, + 0xc9, 0xdb, 0x01, 0x75, 0x07, 0xbb, 0xbb, 0x4d, + 0xe8, 0xd2, 0x2b, 0xc3, 0xc6, 0x06, 0xc9, 0xdb, + 0x01, 0xbb, 0xca, 0x3a, 0xe8, 0x36, 0x9d, 0xb9, + 0x3d, 0x00, 0xb0, 0x05, 0xb4, 0x09, 0xe8, 0x64, + 0x35, 0xb9, 0x05, 0x00, 0xb0, 0x0e, 0xe8, 0x65, + 0x35, 0xb9, 0xc1, 0x02, 0xe8, 0x69, 0xa6, 0xbb, + 0xd3, 0x4d, 0xe8, 0xa8, 0x2b, 0xb0, 0x3b, 0xe8, + 0x86, 0xb2, 0xc3, 0xc7, 0x06, 0xf3, 0xb4, 0x1d, + 0x00, 0xb8, 0x00, 0x01, 0xbb, 0xab, 0x00, 0xbf, + 0x00, 0x01, 0xbe, 0xb2, 0x00, 0xb1, 0x0b, 0xb5, + 0x03, 0xe8, 0x17, 0x37, 0xc3, 0xe8, 0x06, 0x30, + 0xc3, 0xe8, 0x83, 0xa6, 0xe8, 0x11, 0xcb, 0xc6, + 0x47, 0x02, 0x00, 0xe8, 0x96, 0x39, 0xb9, 0x05, + 0x00, 0xb0, 0x0c, 0xb4, 0x07, 0xe8, 0x15, 0x35, + 0xb9, 0xbb, 0x02, 0xe8, 0x29, 0xa6, 0xb0, 0x39, + 0xe8, 0x45, 0xb2, 0xb0, 0x0b, 0xe8, 0xed, 0xa4, + 0xc3, 0xc7, 0x06, 0xf3, 0xb4, 0x26, 0x00, 0xb8, + 0xa0, 0x00, 0xbb, 0xc7, 0x00, 0xbf, 0xa0, 0x00, + 0xbe, 0xbe, 0x00, 0xb1, 0x13, 0xb5, 0x01, 0xe8, + 0xd1, 0x36, 0xc3, 0x80, 0x3e, 0xd7, 0xdb, 0x01, + 0x74, 0x07, 0xbb, 0xcb, 0x52, 0xe8, 0x35, 0x2b, + 0xc3, 0x80, 0x3e, 0xd8, 0xdb, 0x01, 0x74, 0x07, + 0xbb, 0xfe, 0x52, 0xe8, 0x27, 0x2b, 0xc3, 0xb9, + 0x58, 0x00, 0xb0, 0x04, 0xb4, 0x0c, 0xe8, 0xc4, + 0x34, 0xb9, 0xd3, 0x03, 0xe8, 0xb0, 0xa5, 0xe8, + 0x51, 0x36, 0xc7, 0x06, 0xaf, 0x64, 0x33, 0x00, + 0xc7, 0x06, 0xb1, 0x64, 0xb7, 0x00, 0xbb, 0x47, + 0x33, 0xc7, 0x07, 0x00, 0x03, 0xc7, 0x47, 0x02, + 0x01, 0x03, 0xc7, 0x06, 0xf3, 0xb4, 0x25, 0x00, + 0xe8, 0xb2, 0x37, 0xc6, 0x06, 0x45, 0x33, 0xd9, + 0xc6, 0x06, 0x46, 0x33, 0xe5, 0xb0, 0x01, 0xb4, + 0x02, 0xbe, 0x00, 0x03, 0xbf, 0x01, 0x03, 0xbb, + 0xea, 0x54, 0xe8, 0xf1, 0x9e, 0xbb, 0x47, 0x33, + 0xc7, 0x07, 0x02, 0x03, 0xc7, 0x47, 0x02, 0x03, + 0x03, 0xe8, 0xd0, 0x34, 0xbb, 0x23, 0x55, 0xe8, + 0x33, 0x9c, 0xb9, 0x05, 0x00, 0xb0, 0x03, 0xb4, + 0x02, 0xe8, 0x61, 0x34, 0xb9, 0x38, 0x00, 0xb0, + 0x0c, 0xe8, 0x62, 0x34, 0xb9, 0x17, 0x00, 0xb0, + 0x14, 0xe8, 0x61, 0x34, 0xb9, 0x4b, 0x00, 0xb0, + 0x19, 0xe8, 0x60, 0x34, 0xb9, 0x04, 0x03, 0xe8, + 0x38, 0xa5, 0xbb, 0x47, 0x33, 0xc7, 0x07, 0x06, + 0x03, 0xb9, 0x05, 0x03, 0xe8, 0x2b, 0xa5, 0xe8, + 0xc9, 0x35, 0xc6, 0x06, 0xdc, 0x1c, 0x01, 0xe8, + 0x16, 0xca, 0xc6, 0x07, 0x4a, 0xe8, 0xeb, 0x38, + 0xe8, 0xcd, 0x35, 0xb0, 0x01, 0xbe, 0x07, 0x03, + 0xbb, 0x56, 0x55, 0xe8, 0x11, 0xa0, 0xbb, 0x47, + 0x33, 0xc7, 0x07, 0x08, 0x03, 0xb0, 0x01, 0xe8, + 0x70, 0xa4, 0xb0, 0x01, 0xb4, 0x02, 0xbe, 0x09, + 0x03, 0xbf, 0x0a, 0x03, 0xbb, 0xf7, 0x55, 0xe8, + 0x6c, 0x9e, 0xbb, 0x47, 0x33, 0xc7, 0x07, 0x0b, + 0x03, 0xc7, 0x47, 0x02, 0x0c, 0x03, 0xe8, 0x4b, + 0x34, 0xe8, 0xd8, 0x34, 0xb9, 0x3a, 0x00, 0xb0, + 0x01, 0xb4, 0x04, 0xe8, 0xdf, 0x33, 0xb0, 0x02, + 0xe8, 0xe3, 0x33, 0xb0, 0x03, 0xe8, 0xe5, 0x33, + 0xb0, 0x04, 0xe8, 0xe7, 0x33, 0xb0, 0x05, 0xe8, + 0xe9, 0x33, 0xb0, 0x06, 0xe8, 0xeb, 0x33, 0xb0, + 0x0a, 0xe8, 0xf4, 0x33, 0xb9, 0x02, 0x00, 0xb0, + 0x07, 0xe8, 0xe5, 0x33, 0xb9, 0x37, 0x00, 0xb0, + 0x0b, 0xe8, 0xeb, 0x33, 0xb9, 0x36, 0x00, 0xb0, + 0x0f, 0xb4, 0x0a, 0xe8, 0xe8, 0x33, 0xbb, 0x47, + 0x33, 0xc7, 0x47, 0x04, 0x0d, 0x03, 0xc7, 0x47, + 0x06, 0x0e, 0x03, 0xb0, 0x03, 0xe8, 0xfa, 0xa3, + 0xe8, 0x7d, 0xc9, 0xc6, 0x47, 0x01, 0x4b, 0xc6, + 0x47, 0x02, 0x4c, 0xe8, 0x4d, 0x38, 0xe8, 0x2f, + 0x35, 0xb9, 0x3a, 0x00, 0xb0, 0x01, 0xb4, 0x04, + 0xe8, 0x7a, 0x33, 0xb0, 0x02, 0xe8, 0x7e, 0x33, + 0xb0, 0x03, 0xe8, 0x80, 0x33, 0xb0, 0x04, 0xe8, + 0x82, 0x33, 0xb0, 0x05, 0xe8, 0x84, 0x33, 0xb0, + 0x06, 0xe8, 0x86, 0x33, 0xb0, 0x09, 0xe8, 0x8f, + 0x33, 0xb9, 0x02, 0x00, 0xb0, 0x07, 0xe8, 0x80, + 0x33, 0xb0, 0x0f, 0xb4, 0x07, 0xe8, 0x8e, 0x33, + 0xb9, 0x37, 0x00, 0xb0, 0x0a, 0xe8, 0x7f, 0x33, + 0xbb, 0x47, 0x33, 0xc7, 0x47, 0x04, 0x0f, 0x03, + 0xc7, 0x47, 0x06, 0x10, 0x03, 0xb0, 0x03, 0xe8, + 0x98, 0xa3, 0xe8, 0x1b, 0xc9, 0xc6, 0x47, 0x01, + 0x4d, 0xc6, 0x47, 0x02, 0x4e, 0xe8, 0xeb, 0x37, + 0xe8, 0xcd, 0x34, 0xbb, 0x47, 0x33, 0xc7, 0x47, + 0x04, 0x11, 0x03, 0xc7, 0x47, 0x06, 0x12, 0x03, + 0xb0, 0x03, 0xe8, 0x75, 0xa3, 0xbb, 0x47, 0x33, + 0xc7, 0x07, 0xff, 0xff, 0xe8, 0xa7, 0x33, 0xc7, + 0x06, 0xaf, 0x64, 0x70, 0x00, 0xc6, 0x06, 0xdc, + 0x1c, 0x02, 0xe8, 0xe3, 0xc8, 0xc6, 0x47, 0x03, + 0x4f, 0xc6, 0x07, 0x00, 0xe8, 0xb4, 0x37, 0xe8, + 0x96, 0x34, 0xe8, 0x5f, 0x37, 0xc7, 0x06, 0x30, + 0x32, 0x96, 0x97, 0xc7, 0x06, 0x2e, 0x32, 0x2a, + 0x00, 0xc7, 0x06, 0x2c, 0x32, 0x3e, 0x00, 0xe8, + 0x4c, 0xb1, 0xe8, 0x93, 0x31, 0xbb, 0x47, 0x33, + 0xc7, 0x47, 0x04, 0x13, 0x03, 0xc7, 0x47, 0x06, + 0x14, 0x03, 0xb0, 0x03, 0xe8, 0x23, 0xa3, 0xbb, + 0x47, 0x33, 0xc7, 0x47, 0x02, 0xff, 0xff, 0xe8, + 0x54, 0x33, 0xb9, 0x20, 0x00, 0xb0, 0x02, 0xb4, + 0x0c, 0xe8, 0xa9, 0x32, 0xb9, 0x18, 0x00, 0xb0, + 0x07, 0xe8, 0xaa, 0x32, 0xbb, 0x47, 0x33, 0xc7, + 0x07, 0x15, 0x03, 0xc7, 0x47, 0x06, 0x16, 0x03, + 0xb0, 0x01, 0xe8, 0xf5, 0xa2, 0xbb, 0x47, 0x33, + 0xe8, 0x2b, 0x33, 0xe8, 0x72, 0xc8, 0xc6, 0x07, + 0x50, 0xe8, 0x47, 0x37, 0xe8, 0x29, 0x34, 0xe8, + 0xe2, 0xa1, 0xbb, 0x65, 0x56, 0xe8, 0x3d, 0x9a, + 0xbb, 0x47, 0x33, 0xc7, 0x47, 0x06, 0x18, 0x03, + 0xe8, 0xc9, 0x32, 0xe8, 0x56, 0x33, 0xbe, 0x28, + 0x00, 0xbf, 0xab, 0x00, 0xc6, 0x06, 0xc3, 0x64, + 0x04, 0xe8, 0x87, 0xbe, 0xc7, 0x06, 0xf3, 0xb4, + 0x23, 0x00, 0xe8, 0x3b, 0xc8, 0xc6, 0x47, 0x03, + 0x51, 0xb0, 0x0c, 0xe8, 0x23, 0xa2, 0xc7, 0x06, + 0xf3, 0xb4, 0x1f, 0x00, 0xb8, 0x2a, 0x01, 0xbb, + 0xb1, 0x00, 0xbf, 0x16, 0x01, 0xbe, 0xb1, 0x00, + 0xb1, 0x01, 0xb5, 0x04, 0xe8, 0x14, 0x34, 0xc6, + 0x06, 0xd9, 0xdb, 0x01, 0xc3, 0xb9, 0x59, 0x00, + 0xb0, 0x06, 0xb4, 0x02, 0xe8, 0x1e, 0x32, 0xb9, + 0xd2, 0x03, 0xe8, 0x1a, 0xa3, 0xe8, 0xab, 0x33, + 0xc7, 0x06, 0xf3, 0xb4, 0x1f, 0x00, 0xb8, 0x2a, + 0x01, 0xbb, 0xb1, 0x00, 0xbf, 0x16, 0x01, 0xbe, + 0xb1, 0x00, 0xb1, 0x01, 0xb5, 0x04, 0xe8, 0xe2, + 0x33, 0xc3, 0x80, 0x3e, 0xd7, 0xdb, 0x01, 0x74, + 0x07, 0xbb, 0xcb, 0x52, 0xe8, 0x46, 0x28, 0xc3, + 0x80, 0x3e, 0xd8, 0xdb, 0x01, 0x75, 0x07, 0xbb, + 0xf6, 0x52, 0xe8, 0x38, 0x28, 0xc3, 0xb9, 0x47, + 0x00, 0xb0, 0x04, 0xb4, 0x0c, 0xe8, 0xd5, 0x31, + 0xb9, 0x1c, 0x03, 0xe8, 0xe2, 0xa2, 0xb8, 0x01, + 0x00, 0xe8, 0x45, 0xb6, 0xe8, 0x59, 0xb6, 0xc6, + 0x06, 0xd8, 0xdb, 0x01, 0xc3, 0xe8, 0x98, 0xa0, + 0xbb, 0x36, 0xdb, 0xe8, 0x67, 0x99, 0xe8, 0x7c, + 0x99, 0xc3, 0xbb, 0x87, 0x5d, 0xe8, 0x05, 0x28, + 0xc3, 0xbb, 0x11, 0x55, 0xe8, 0xfe, 0x27, 0xc3, + 0xbb, 0x11, 0x55, 0xe8, 0xf7, 0x27, 0xc3, 0xbb, + 0x0f, 0x57, 0xe8, 0xf0, 0x27, 0xc3, 0x80, 0x3e, + 0xdd, 0xdb, 0x03, 0x75, 0x07, 0xbb, 0xff, 0x55, + 0xe8, 0xe2, 0x27, 0xc3, 0xe8, 0xc1, 0x27, 0xc3, + 0xfe, 0x06, 0xdb, 0xdb, 0xa0, 0xdb, 0xdb, 0xbb, + 0x11, 0x54, 0x3c, 0x01, 0x74, 0x1f, 0xbb, 0x63, + 0x54, 0x3c, 0x02, 0x74, 0x18, 0xbb, 0x75, 0x54, + 0x3c, 0x03, 0x74, 0x11, 0xbb, 0x84, 0x54, 0x3c, + 0x04, 0x74, 0x0a, 0xbb, 0xc4, 0x54, 0x3c, 0x05, + 0x74, 0x03, 0xbb, 0xd5, 0x54, 0xe8, 0xad, 0x27, + 0xc3, 0x80, 0x3e, 0xe6, 0xdb, 0x01, 0x75, 0x07, + 0xbb, 0x27, 0x58, 0xe8, 0x9f, 0x27, 0xc3, 0xe8, + 0x7e, 0x27, 0xc3, 0x80, 0x3e, 0xe8, 0xdb, 0x01, + 0x74, 0x04, 0xe8, 0x73, 0x27, 0xc3, 0xbb, 0xb0, + 0x58, 0xe8, 0x89, 0x27, 0xc3, 0xc3, 0x80, 0x3e, + 0xa1, 0xdb, 0x01, 0x74, 0x07, 0xbb, 0x94, 0x36, + 0xe8, 0x7a, 0x27, 0xc3, 0xe8, 0x59, 0x27, 0xc3, + 0xe8, 0xe2, 0xd7, 0xe8, 0x52, 0x27, 0xc3, 0xe8, + 0x94, 0xdc, 0xe8, 0x4b, 0x27, 0xc3, 0xe8, 0x8d, + 0xdc, 0xe8, 0x44, 0x27, 0xc3, 0x80, 0x3e, 0x95, + 0xdb, 0x01, 0x75, 0x07, 0xbb, 0x75, 0x3e, 0xe8, + 0x53, 0x27, 0xc3, 0xe8, 0x32, 0x27, 0xc3, 0x80, + 0x3e, 0x94, 0xdb, 0x01, 0x75, 0x07, 0xbb, 0x4f, + 0x3e, 0xe8, 0x41, 0x27, 0xc3, 0xe8, 0x20, 0x27, + 0xc3, 0x80, 0x3e, 0xa5, 0xdb, 0x01, 0x75, 0x07, + 0xbb, 0x98, 0x3e, 0xe8, 0x2f, 0x27, 0xc3, 0xe8, + 0x0e, 0x27, 0xc3, 0xbe, 0xf5, 0x00, 0xbf, 0xc6, + 0x00, 0xc6, 0x06, 0xc3, 0x64, 0x01, 0xe8, 0xf2, + 0xbc, 0x80, 0x3e, 0xaf, 0xdb, 0x01, 0x74, 0x0c, + 0xbb, 0x93, 0x21, 0xe8, 0x7f, 0x98, 0xc6, 0x06, + 0xaf, 0xdb, 0x01, 0xc3, 0xe8, 0xe9, 0x26, 0xc3, + 0x80, 0x3e, 0xb1, 0xdb, 0x01, 0x75, 0x07, 0xbb, + 0xaf, 0x3d, 0xe8, 0xf8, 0x26, 0xc3, 0xe8, 0xd7, + 0x26, 0xc3, 0xe8, 0x50, 0xe7, 0xc3, 0x80, 0x3e, + 0xa4, 0xdb, 0x01, 0x74, 0x04, 0xe8, 0x45, 0xe7, + 0xc3, 0xe8, 0xc4, 0x26, 0xc3, 0x80, 0x3e, 0xa4, + 0xdb, 0x01, 0x74, 0x07, 0xbb, 0x32, 0x38, 0xe8, + 0xd3, 0x26, 0xc3, 0xe8, 0xb2, 0x26, 0xc3, 0x80, + 0x3e, 0xa4, 0xdb, 0x01, 0x74, 0x04, 0xe8, 0x6d, + 0xe8, 0xc3, 0xe8, 0xa3, 0x26, 0xc3, 0x80, 0x3e, + 0xa4, 0xdb, 0x01, 0x74, 0x04, 0xe8, 0x5e, 0xe8, + 0xc3, 0xe8, 0x94, 0x26, 0xc3, 0x80, 0x3e, 0xa4, + 0xdb, 0x01, 0x74, 0x04, 0xe8, 0x4f, 0xe8, 0xc3, + 0xe8, 0x85, 0x26, 0xc3, 0x80, 0x3e, 0xa4, 0xdb, + 0x01, 0x74, 0x04, 0xe8, 0x40, 0xe8, 0xc3, 0xe8, + 0x76, 0x26, 0xc3, 0x80, 0x3e, 0xa4, 0xdb, 0x01, + 0x74, 0x04, 0xe8, 0x31, 0xe8, 0xc3, 0xe8, 0x67, + 0x26, 0xc3, 0x80, 0x3e, 0x9d, 0xdb, 0x01, 0x75, + 0x07, 0xbb, 0x90, 0x35, 0xe8, 0x76, 0x26, 0xc3, + 0xe8, 0x55, 0x26, 0xc3, 0xe8, 0x62, 0xee, 0xe8, + 0x4e, 0x26, 0xc3, 0xa0, 0xc1, 0xdb, 0x98, 0x48, + 0xd1, 0xe0, 0xbb, 0x3c, 0x5f, 0x03, 0xd8, 0x8b, + 0x1f, 0xe8, 0x59, 0x26, 0xc3, 0x80, 0x3e, 0xb7, + 0xdb, 0x01, 0x74, 0x04, 0xe8, 0x31, 0x26, 0xc3, + 0xbb, 0x6c, 0x4b, 0xe8, 0x47, 0x26, 0xc3, 0x80, + 0x3e, 0xb8, 0xdb, 0x01, 0x74, 0x04, 0xe8, 0x1f, + 0x26, 0xc3, 0x80, 0x3e, 0xbf, 0xdb, 0x01, 0x74, + 0x21, 0xbb, 0x32, 0x4c, 0xe8, 0x2e, 0x26, 0xb9, + 0x05, 0x00, 0xb0, 0x0b, 0xb4, 0x07, 0xe8, 0xcc, + 0x2f, 0xb9, 0xb2, 0x02, 0xe8, 0xd9, 0xa0, 0xb0, + 0x35, 0xe8, 0xfc, 0xac, 0xc6, 0x06, 0xbf, 0xdb, + 0x01, 0xc3, 0xbb, 0x87, 0x4b, 0xe8, 0x0d, 0x26, + 0xc3, 0x80, 0x3e, 0xb9, 0xdb, 0x01, 0x74, 0x04, + 0xe8, 0xe5, 0x25, 0xc3, 0xbb, 0xa1, 0x4b, 0xe8, + 0xfb, 0x25, 0xc3, 0x80, 0x3e, 0xba, 0xdb, 0x01, + 0x74, 0x04, 0xe8, 0xd3, 0x25, 0xc3, 0xbb, 0xbc, + 0x4b, 0xe8, 0xe9, 0x25, 0xc3, 0x80, 0x3e, 0xbb, + 0xdb, 0x01, 0x74, 0x04, 0xe8, 0xc1, 0x25, 0xc3, + 0xbb, 0xd8, 0x4b, 0xe8, 0xd7, 0x25, 0xc3, 0x80, + 0x3e, 0xbc, 0xdb, 0x01, 0x74, 0x04, 0xe8, 0xaf, + 0x25, 0xc3, 0x80, 0x3e, 0xbe, 0xdb, 0x01, 0x74, + 0x21, 0xbb, 0x0f, 0x4c, 0xe8, 0xbe, 0x25, 0xb9, + 0x05, 0x00, 0xb0, 0x0c, 0xb4, 0x07, 0xe8, 0x5c, + 0x2f, 0xb9, 0xb1, 0x02, 0xe8, 0x69, 0xa0, 0xb0, + 0x34, 0xe8, 0x8c, 0xac, 0xc6, 0x06, 0xbe, 0xdb, + 0x01, 0xc3, 0xbb, 0xf4, 0x4b, 0xe8, 0x9d, 0x25, + 0xc3, 0x80, 0x3e, 0xb6, 0xdb, 0x01, 0x74, 0x0c, + 0xbb, 0xd0, 0x37, 0xe8, 0xff, 0x96, 0xc6, 0x06, + 0xb6, 0xdb, 0x01, 0xc3, 0xe8, 0x69, 0x25, 0xc3, + 0xe8, 0x0d, 0xf6, 0xe8, 0x62, 0x25, 0xc3, 0xe8, + 0x06, 0xf6, 0xe8, 0x5b, 0x25, 0xc3, 0x80, 0x3e, + 0xcd, 0xdb, 0x01, 0x74, 0x04, 0xe8, 0x50, 0x25, + 0xc3, 0xbb, 0x69, 0x4f, 0xe8, 0x66, 0x25, 0xc3, + 0xe8, 0xed, 0xf5, 0xe8, 0x42, 0x25, 0xc3, 0x80, + 0x3e, 0xd0, 0xdb, 0x01, 0x74, 0x04, 0xe8, 0x37, + 0x25, 0xc3, 0xbb, 0x82, 0x50, 0xe8, 0x4d, 0x25, + 0xc3, 0xa0, 0xd6, 0xdb, 0x3c, 0x01, 0x74, 0x08, + 0x3c, 0x02, 0x74, 0x0b, 0xe8, 0x21, 0x25, 0xc3, + 0xbb, 0xf8, 0x51, 0xe8, 0x37, 0x25, 0xc3, 0xbb, + 0x8d, 0x53, 0xe8, 0x30, 0x25, 0xc3, 0xe8, 0x2e, + 0xa0, 0xe8, 0xbc, 0xc4, 0xc6, 0x07, 0x00, 0xe8, + 0x42, 0x33, 0xb9, 0x05, 0x00, 0xb0, 0x02, 0xb4, + 0x0c, 0xe8, 0xc1, 0x2e, 0xb9, 0x33, 0x00, 0xb0, + 0x0b, 0xe8, 0xc2, 0x2e, 0xb0, 0x17, 0xe8, 0xc4, + 0x2e, 0xb9, 0x45, 0x03, 0xe8, 0xa3, 0x9f, 0xb9, + 0x33, 0x00, 0xb0, 0x03, 0xb4, 0x0d, 0xe8, 0xa4, + 0x2e, 0xb0, 0x13, 0xe8, 0xa8, 0x2e, 0xb9, 0x17, + 0x00, 0xb0, 0x1a, 0xe8, 0xa7, 0x2e, 0xb9, 0x46, + 0x03, 0xe8, 0x86, 0x9f, 0xe8, 0x79, 0xc4, 0xc6, + 0x07, 0x60, 0xe8, 0xbb, 0x32, 0xc7, 0x06, 0xaf, + 0x64, 0x19, 0x01, 0xe8, 0xae, 0x9f, 0xb0, 0x04, + 0xe8, 0x62, 0x9e, 0xc6, 0x06, 0xe1, 0xdb, 0x01, + 0xc3, 0x80, 0x3e, 0xe1, 0xdb, 0x01, 0x74, 0x07, + 0xbb, 0xda, 0x56, 0xe8, 0xbf, 0x24, 0xc3, 0xb9, + 0x4c, 0x03, 0xe8, 0x52, 0x9f, 0xe8, 0x48, 0xc4, + 0xc6, 0x47, 0x01, 0x00, 0x53, 0xe8, 0x88, 0x32, + 0xb9, 0x05, 0x00, 0xb0, 0x05, 0xb4, 0x0a, 0xe8, + 0x4b, 0x2e, 0xb9, 0x1a, 0x00, 0xb0, 0x13, 0xe8, + 0x4c, 0x2e, 0xb9, 0x18, 0x00, 0xb0, 0x19, 0xe8, + 0x4b, 0x2e, 0xb9, 0x4f, 0x03, 0xe8, 0x2a, 0x9f, + 0xb9, 0x05, 0x00, 0xb0, 0x0b, 0xb4, 0x09, 0xe8, + 0x2b, 0x2e, 0xb9, 0x50, 0x03, 0xe8, 0x1a, 0x9f, + 0x5b, 0xc6, 0x47, 0x01, 0x64, 0xe8, 0x50, 0x32, + 0xb9, 0x4d, 0x03, 0xe8, 0x31, 0x9f, 0xb0, 0x03, + 0xe8, 0xfa, 0x9d, 0xb0, 0x52, 0xe8, 0x48, 0xab, + 0xb0, 0x51, 0xe8, 0x53, 0xab, 0xc3, 0xbb, 0x54, + 0x5e, 0xe8, 0x59, 0x24, 0xc3, 0xb9, 0x4c, 0x00, + 0xb0, 0x12, 0xb4, 0x08, 0xe8, 0xf6, 0x2d, 0xb0, + 0x16, 0xe8, 0xfa, 0x2d, 0xb0, 0x1a, 0xe8, 0xfc, + 0x2d, 0xb0, 0x1e, 0xe8, 0xfe, 0x2d, 0xb0, 0x22, + 0xe8, 0x00, 0x2e, 0xb0, 0x2f, 0xe8, 0x02, 0x2e, + 0xb0, 0x33, 0xe8, 0x04, 0x2e, 0xb0, 0x37, 0xe8, + 0x06, 0x2e, 0xb0, 0x3b, 0xe8, 0x08, 0x2e, 0xb0, + 0x3f, 0xe8, 0x0a, 0x2e, 0xb9, 0x69, 0x03, 0xe8, + 0xd6, 0x9e, 0xbe, 0xf0, 0x00, 0xbf, 0xa3, 0x00, + 0xc6, 0x06, 0xc3, 0x64, 0x04, 0xe8, 0xe3, 0xb9, + 0xbb, 0x37, 0x58, 0xe8, 0x07, 0x24, 0xb4, 0x01, + 0xb0, 0x22, 0xe8, 0xb9, 0xa2, 0xb9, 0x4d, 0x00, + 0xb0, 0x02, 0xb4, 0x08, 0xe8, 0x9e, 0x2d, 0xb0, + 0x0c, 0xe8, 0xa2, 0x2d, 0xb0, 0x10, 0xe8, 0xa4, + 0x2d, 0xb0, 0x14, 0xe8, 0xa6, 0x2d, 0xb0, 0x22, + 0xe8, 0xa8, 0x2d, 0xb9, 0x4e, 0x00, 0xb0, 0x29, + 0xe8, 0xa7, 0x2d, 0xb0, 0x33, 0xe8, 0xa9, 0x2d, + 0xb9, 0x38, 0x00, 0xb0, 0x3f, 0xe8, 0xa8, 0x2d, + 0xb9, 0x18, 0x00, 0xb0, 0x43, 0xe8, 0xa7, 0x2d, + 0xb9, 0x17, 0x00, 0xb0, 0x4c, 0xe8, 0xa6, 0x2d, + 0xbb, 0x47, 0x33, 0xc7, 0x07, 0x6a, 0x03, 0xb0, + 0x01, 0xe8, 0x7c, 0x9d, 0xe8, 0x45, 0x2e, 0xe8, + 0x3e, 0xc3, 0xc6, 0x07, 0x68, 0xe8, 0xc4, 0x31, + 0xb8, 0x01, 0x00, 0xe8, 0xc3, 0xb1, 0xe8, 0xd7, + 0xb1, 0xb0, 0x5b, 0xe8, 0x8a, 0xaa, 0xb0, 0x06, + 0xe8, 0x16, 0x9d, 0xb0, 0x01, 0xe8, 0x1d, 0x9d, + 0xc3, 0xbb, 0xbe, 0x5e, 0xe8, 0x86, 0x23, 0xc3, + 0xbb, 0xbe, 0x5e, 0xe8, 0x7f, 0x23, 0xc3, 0xbb, + 0xe6, 0x5e, 0xe8, 0x78, 0x23, 0xc3, 0xbb, 0x11, + 0x5f, 0xe8, 0x71, 0x23, 0xc3, 0xb9, 0x05, 0x00, + 0xb0, 0x02, 0xb4, 0x07, 0xe8, 0x0e, 0x2d, 0xb9, + 0x13, 0x00, 0xb0, 0x0b, 0xe8, 0x0f, 0x2d, 0xb9, + 0x48, 0x03, 0xe8, 0x02, 0x9e, 0xe8, 0xe8, 0xc2, + 0xc6, 0x47, 0x01, 0x61, 0xe8, 0x29, 0x31, 0xe8, + 0x22, 0x9e, 0xb0, 0x50, 0xe8, 0x39, 0xaa, 0xb0, + 0x02, 0xe8, 0xd1, 0x9c, 0xb0, 0x07, 0xe8, 0xc0, + 0x9c, 0xc3, 0x80, 0x3e, 0xdf, 0xdb, 0x01, 0x76, + 0x07, 0xbb, 0xf6, 0x52, 0xe8, 0x2e, 0x23, 0xc3, + 0xb9, 0x05, 0x00, 0xb0, 0x02, 0xb4, 0x0c, 0xe8, + 0xcb, 0x2c, 0xb9, 0x39, 0x00, 0xb0, 0x0c, 0xe8, + 0xcc, 0x2c, 0xb9, 0x46, 0x00, 0xb0, 0x13, 0xe8, + 0xcb, 0x2c, 0xb9, 0x3c, 0x03, 0xe8, 0xa7, 0x9d, + 0xe8, 0x48, 0x2e, 0xc7, 0x06, 0xaf, 0x64, 0x06, + 0x01, 0xc7, 0x06, 0xb1, 0x64, 0xa0, 0x00, 0xc6, + 0x06, 0xcc, 0x64, 0x01, 0xc6, 0x06, 0xcd, 0x64, + 0x00, 0xc6, 0x06, 0xcb, 0x64, 0x01, 0xe8, 0x10, + 0xc1, 0xb0, 0x04, 0xe8, 0x77, 0x9c, 0xb0, 0x03, + 0xe8, 0x72, 0x9c, 0xe8, 0x72, 0xc2, 0xc6, 0x07, + 0x00, 0xc6, 0x47, 0x02, 0x00, 0xc6, 0x47, 0x03, + 0x00, 0xc6, 0x47, 0x01, 0x55, 0xbb, 0x47, 0x33, + 0xc7, 0x07, 0x3d, 0x03, 0xc7, 0x06, 0xf3, 0xb4, + 0x05, 0x00, 0xe8, 0x53, 0xc2, 0xc6, 0x07, 0x5c, + 0xe8, 0x7a, 0x2f, 0xe8, 0x4a, 0xc2, 0xc6, 0x07, + 0x00, 0xb8, 0xc8, 0x00, 0xe8, 0x93, 0xa1, 0xc6, + 0x06, 0x45, 0x33, 0xd0, 0xb0, 0x01, 0xbe, 0x3e, + 0x03, 0xbb, 0xa5, 0x63, 0xe8, 0x40, 0x98, 0xb9, + 0x04, 0x00, 0xe8, 0x8e, 0x33, 0xbb, 0x47, 0x33, + 0xc7, 0x07, 0x3f, 0x03, 0xc7, 0x06, 0xf3, 0xb4, + 0x07, 0x00, 0xe8, 0x3f, 0x2f, 0xc7, 0x06, 0xaf, + 0x64, 0x82, 0x00, 0xc7, 0x06, 0xb1, 0x64, 0xc3, + 0x00, 0xc6, 0x06, 0xdc, 0x1c, 0x02, 0xe8, 0x95, + 0xaa, 0xe8, 0x71, 0xa1, 0xe8, 0x92, 0x99, 0xbb, + 0x06, 0x64, 0xe8, 0xd8, 0x93, 0xbb, 0x47, 0x33, + 0xc7, 0x07, 0x3f, 0x03, 0xb0, 0x01, 0xe8, 0x2e, + 0x9c, 0xe8, 0xf0, 0x2c, 0xc6, 0x06, 0xdf, 0xdb, + 0x02, 0xc3, 0x80, 0x3e, 0xe0, 0xdb, 0x01, 0x75, + 0x07, 0xbb, 0x32, 0x56, 0xe8, 0x46, 0x22, 0xc3, + 0xb9, 0x05, 0x00, 0xb0, 0x06, 0xb4, 0x05, 0xe8, + 0xe3, 0x2b, 0xb9, 0x1b, 0x00, 0xb0, 0x31, 0xe8, + 0xe4, 0x2b, 0xbb, 0x47, 0x33, 0xc7, 0x07, 0x43, + 0x03, 0xb9, 0x42, 0x03, 0xe8, 0xc0, 0x9c, 0xe8, + 0xb6, 0xc1, 0xc6, 0x07, 0x5e, 0xe8, 0xf8, 0x2f, + 0xe8, 0xf1, 0x9c, 0xe8, 0x3b, 0x99, 0xbb, 0xe9, + 0x65, 0xe8, 0x81, 0x93, 0xb0, 0x0c, 0xe8, 0x90, + 0x9b, 0xc6, 0x06, 0xe0, 0xdb, 0x01, 0xc3, 0xe8, + 0x32, 0x99, 0xbb, 0xc0, 0x66, 0xe8, 0x6d, 0x93, + 0xb9, 0x05, 0x00, 0xb0, 0x03, 0xb4, 0x03, 0xe8, + 0x9b, 0x2b, 0xb9, 0x54, 0x03, 0xe8, 0x8a, 0x9c, + 0xe8, 0x28, 0x2d, 0xc6, 0x06, 0xdc, 0x1c, 0x01, + 0xbb, 0x47, 0x33, 0xc7, 0x07, 0x55, 0x03, 0xb0, + 0x01, 0xe8, 0xab, 0x9b, 0xe8, 0x6d, 0x2c, 0xa0, + 0xac, 0x64, 0x50, 0xc6, 0x06, 0xac, 0x64, 0x04, + 0xe8, 0x10, 0x8d, 0xbb, 0x42, 0x57, 0xbe, 0x13, + 0x8c, 0xe8, 0x6a, 0x24, 0xbb, 0x57, 0x57, 0xbe, + 0x12, 0x8c, 0xe8, 0x61, 0x24, 0xbb, 0x70, 0x57, + 0xbe, 0x1d, 0x8c, 0xe8, 0x58, 0x24, 0xbb, 0x82, + 0x57, 0xbe, 0x0f, 0x8c, 0xe8, 0x4f, 0x24, 0xbb, + 0x99, 0x57, 0xbe, 0x07, 0x8c, 0xe8, 0x46, 0x24, + 0xbb, 0x47, 0x33, 0xc7, 0x07, 0x58, 0x03, 0xb0, + 0x01, 0xe8, 0x63, 0x9b, 0xb9, 0x05, 0x00, 0xb0, + 0x03, 0xb4, 0x03, 0xe8, 0x2f, 0x2b, 0xb9, 0x56, + 0x03, 0xe8, 0x1e, 0x9c, 0xe8, 0xbc, 0x2c, 0x58, + 0xa2, 0xac, 0x64, 0xe8, 0xbd, 0x8c, 0xe8, 0xa3, + 0x98, 0xbb, 0xfe, 0x66, 0xe8, 0xde, 0x92, 0xbb, + 0x47, 0x33, 0xc7, 0x07, 0x57, 0x03, 0xb0, 0x01, + 0xe8, 0x34, 0x9b, 0xb8, 0xc8, 0x00, 0xe8, 0x41, + 0xa0, 0xbe, 0x1e, 0x00, 0xbf, 0xb5, 0x00, 0xe8, + 0x29, 0xb7, 0xe8, 0x99, 0x2b, 0xb0, 0x01, 0xe8, + 0xdb, 0x9a, 0xb8, 0x01, 0x00, 0xe8, 0x69, 0xaf, + 0xc6, 0x06, 0xdf, 0xdb, 0x03, 0xc6, 0x06, 0xf0, + 0xdb, 0x01, 0xc6, 0x06, 0xdc, 0x1c, 0x02, 0xe8, + 0x79, 0xc4, 0xc3, 0x80, 0x3e, 0xdf, 0xdb, 0x03, + 0x74, 0x07, 0xbb, 0xe2, 0x5d, 0xe8, 0x25, 0x21, + 0xc3, 0xb9, 0x05, 0x00, 0xb0, 0x03, 0xb4, 0x09, + 0xe8, 0xc2, 0x2a, 0xb9, 0x3a, 0x00, 0xb0, 0x0b, + 0xe8, 0xc3, 0x2a, 0xb9, 0x2e, 0x00, 0xb0, 0x38, + 0xe8, 0xc2, 0x2a, 0xb0, 0x55, 0xe8, 0xc4, 0x2a, + 0xb0, 0x75, 0xe8, 0xc6, 0x2a, 0xb9, 0x66, 0x03, + 0xe8, 0x94, 0x9b, 0xb9, 0x36, 0x00, 0xb0, 0x0f, + 0xb4, 0x09, 0xe8, 0x98, 0x2a, 0xb9, 0x67, 0x03, + 0xe8, 0x87, 0x9b, 0xe8, 0x25, 0x2c, 0xc6, 0x06, + 0xe6, 0xdb, 0x01, 0xe8, 0x72, 0xc0, 0xc6, 0x47, + 0x01, 0x66, 0xe8, 0xf7, 0x2e, 0xb8, 0x32, 0x00, + 0xe8, 0xd9, 0x9f, 0xc7, 0x06, 0xaf, 0x64, 0xe0, + 0x00, 0xc7, 0x06, 0xb1, 0x64, 0xc2, 0x00, 0xc6, + 0x06, 0xcc, 0x64, 0x00, 0xc6, 0x06, 0xdc, 0x64, + 0x01, 0xc6, 0x06, 0xcb, 0x64, 0x00, 0xe8, 0xd8, + 0xbe, 0xc6, 0x06, 0xdc, 0x1c, 0x03, 0xe8, 0xcd, + 0xa8, 0xbe, 0xd7, 0x76, 0xbb, 0xdf, 0x57, 0xc6, + 0x06, 0xdc, 0x1c, 0x02, 0xe8, 0x5e, 0x1c, 0xb0, + 0x59, 0xe8, 0x8c, 0xa7, 0xc3, 0xb9, 0x05, 0x00, + 0xb0, 0x03, 0xb4, 0x06, 0xe8, 0x36, 0x2a, 0xb9, + 0x6c, 0x03, 0xe8, 0x32, 0x9b, 0xe8, 0xc3, 0x2b, + 0xe8, 0x15, 0xc0, 0xc6, 0x47, 0x01, 0x00, 0xe8, + 0x9a, 0x2e, 0xb9, 0x1a, 0x00, 0xb0, 0x07, 0xb4, + 0x04, 0xe8, 0x19, 0x2a, 0xb9, 0x4f, 0x00, 0xb0, + 0x0f, 0xe8, 0x1a, 0x2a, 0xbb, 0x47, 0x33, 0xc7, + 0x47, 0x02, 0x6d, 0x03, 0xb0, 0x02, 0xe8, 0x27, + 0x9a, 0xbb, 0x47, 0x33, 0xc7, 0x47, 0x02, 0x70, + 0x03, 0xb0, 0x02, 0xe8, 0x21, 0x9a, 0xe8, 0xe3, + 0x2a, 0xe8, 0x04, 0x97, 0xbb, 0x0e, 0x6f, 0xe8, + 0xb3, 0x91, 0xe8, 0xd3, 0xbf, 0xc6, 0x47, 0x02, + 0x6a, 0xe8, 0x58, 0x2e, 0xbb, 0x47, 0x33, 0xc7, + 0x07, 0x6e, 0x03, 0xb0, 0x01, 0xe8, 0xf8, 0x99, + 0xbb, 0x47, 0x33, 0xc7, 0x07, 0x6f, 0x03, 0xb0, + 0x01, 0xe8, 0xf3, 0x99, 0xe8, 0xb5, 0x2a, 0xb0, + 0x5c, 0xe8, 0x0c, 0xa7, 0xb0, 0x01, 0xe8, 0x98, + 0x99, 0xc6, 0x06, 0xe7, 0xdb, 0x01, 0xc3, 0xb9, + 0x20, 0x00, 0xb0, 0x05, 0xb4, 0x0a, 0xe8, 0xac, + 0x29, 0xb9, 0x05, 0x00, 0xb0, 0x11, 0xe8, 0xad, + 0x29, 0xb9, 0x34, 0x00, 0xb0, 0x17, 0xe8, 0xac, + 0x29, 0xb9, 0x71, 0x03, 0xe8, 0x88, 0x9a, 0xe8, + 0x7e, 0xbf, 0xc6, 0x47, 0x02, 0x6b, 0xe8, 0xbf, + 0x2d, 0xe8, 0xb8, 0x9a, 0xb0, 0x56, 0xe8, 0xcf, + 0xa6, 0xb0, 0x55, 0xe8, 0xba, 0xa6, 0xc6, 0x06, + 0xe8, 0xdb, 0x01, 0xc3, 0xbb, 0x8f, 0x5e, 0xe8, + 0xcb, 0x1f, 0xc3, 0x80, 0x3e, 0xda, 0xdb, 0x01, + 0x75, 0x07, 0xbb, 0xf2, 0x53, 0xe8, 0xbd, 0x1f, + 0xc3, 0xbb, 0xdd, 0x53, 0xe8, 0xb6, 0x1f, 0xb9, + 0x05, 0x00, 0xb0, 0x02, 0xb4, 0x07, 0xe8, 0x54, + 0x29, 0xb0, 0x12, 0xe8, 0x58, 0x29, 0xb9, 0x2a, + 0x03, 0xe8, 0x5c, 0x9a, 0xe8, 0xe3, 0x96, 0xbb, + 0xbf, 0x60, 0xe8, 0x08, 0x91, 0xc6, 0x06, 0xda, + 0xdb, 0x01, 0xc3, 0xe8, 0xd4, 0x96, 0xbb, 0x11, + 0x68, 0xe8, 0xf9, 0x90, 0xb9, 0x05, 0x00, 0xb0, + 0x03, 0xb4, 0x05, 0xe8, 0x27, 0x29, 0xb0, 0x1e, + 0xe8, 0x32, 0x29, 0xb9, 0x1a, 0x00, 0xb0, 0x0e, + 0xe8, 0x23, 0x29, 0xbb, 0x47, 0x33, 0xc7, 0x07, + 0x53, 0x03, 0xb9, 0x51, 0x03, 0xe8, 0x02, 0x9a, + 0xe8, 0xa0, 0x2a, 0xbb, 0x47, 0x33, 0xc7, 0x07, + 0x52, 0x03, 0xb0, 0x01, 0xe8, 0x28, 0x99, 0xe8, + 0xea, 0x29, 0xb0, 0x53, 0xe8, 0x31, 0xa6, 0xb0, + 0x52, 0xe8, 0x3c, 0xa6, 0xb0, 0x01, 0xe8, 0xc8, + 0x98, 0xc6, 0x06, 0xe2, 0xdb, 0x01, 0xc3, 0xe8, + 0x0c, 0x97, 0xbb, 0x41, 0x0a, 0xe8, 0xa5, 0x90, + 0xb9, 0x05, 0x00, 0xb0, 0x02, 0xb4, 0x0a, 0xe8, + 0xd3, 0x28, 0xb0, 0x2c, 0xe8, 0xd7, 0x28, 0xbb, + 0x47, 0x33, 0xc7, 0x07, 0x82, 0x02, 0xb9, 0x81, + 0x02, 0xe8, 0xd4, 0x99, 0xe8, 0xe7, 0x96, 0xbb, + 0xff, 0x0a, 0xe8, 0x80, 0x90, 0xb8, 0xaa, 0x00, + 0xe8, 0xef, 0x9d, 0xe8, 0xd8, 0x96, 0xbb, 0xa0, + 0x0b, 0xe8, 0x71, 0x90, 0x8b, 0x36, 0xaf, 0x64, + 0xbb, 0xb1, 0x64, 0x8b, 0x3f, 0xff, 0x0f, 0xe8, + 0xc9, 0xb4, 0xe8, 0xf0, 0x9d, 0xbb, 0x10, 0x0c, + 0xe8, 0x5a, 0x90, 0xb0, 0x32, 0xe8, 0xd8, 0xa5, + 0xe8, 0xd2, 0x1b, 0xc3, 0xe8, 0xe0, 0x99, 0xe8, + 0x6e, 0xbe, 0xc6, 0x07, 0x00, 0x53, 0xe8, 0xf3, + 0x2c, 0xb9, 0x05, 0x00, 0xb0, 0x02, 0xb4, 0x09, + 0xe8, 0x72, 0x28, 0xb0, 0x05, 0xe8, 0x76, 0x28, + 0xb0, 0x09, 0xe8, 0x78, 0x28, 0xb9, 0x0e, 0x00, + 0xb0, 0x13, 0xe8, 0x77, 0x28, 0xb9, 0x05, 0x00, + 0xb0, 0x32, 0xe8, 0x76, 0x28, 0xb9, 0x1e, 0x02, + 0xe8, 0x47, 0x99, 0x5b, 0xb0, 0x0f, 0x88, 0x47, + 0x01, 0xe8, 0x6d, 0x2d, 0xb0, 0x03, 0xe8, 0x2c, + 0x98, 0xb0, 0x09, 0xe8, 0x1b, 0x98, 0xe8, 0x6b, + 0x99, 0xc3, 0xb9, 0x38, 0x00, 0xb0, 0x0b, 0xb4, + 0x08, 0xe8, 0x31, 0x28, 0xb9, 0x24, 0x00, 0xb0, + 0x0d, 0xb4, 0x0c, 0xe8, 0x30, 0x28, 0xb9, 0x30, + 0x00, 0xb0, 0x16, 0xb4, 0x0a, 0xe8, 0x2d, 0x28, + 0xb9, 0x38, 0x00, 0xb0, 0x39, 0xb4, 0x08, 0xe8, + 0x2a, 0x28, 0xb9, 0x24, 0x00, 0xb0, 0x3b, 0xb4, + 0x0c, 0xe8, 0x27, 0x28, 0xb9, 0x30, 0x00, 0xb0, + 0x44, 0xb4, 0x0a, 0xe8, 0x24, 0x28, 0xb9, 0x36, + 0x00, 0xb0, 0x78, 0xb4, 0x08, 0xe8, 0x21, 0x28, + 0xb9, 0x38, 0x00, 0xb0, 0x8d, 0xb4, 0x0a, 0xe8, + 0x1e, 0x28, 0xb0, 0x90, 0xe8, 0x20, 0x28, 0xb0, + 0x93, 0xe8, 0x22, 0x28, 0xbb, 0x47, 0x33, 0xc7, + 0x07, 0x4d, 0x02, 0xb9, 0x4c, 0x02, 0xe8, 0xe7, + 0x98, 0xe8, 0x22, 0x9d, 0xbb, 0x7f, 0x36, 0xe8, + 0x23, 0x1e, 0xb0, 0x22, 0xe8, 0x11, 0xa5, 0xc6, + 0x06, 0xa1, 0xdb, 0x01, 0xc3, 0xb9, 0x05, 0x00, + 0xb0, 0x03, 0xb4, 0x03, 0xe8, 0xb6, 0x27, 0xb9, + 0x41, 0x00, 0xb0, 0x10, 0xe8, 0xb7, 0x27, 0xb0, + 0x12, 0xe8, 0xb9, 0x27, 0xb0, 0x14, 0xe8, 0xbb, + 0x27, 0xb0, 0x16, 0xe8, 0xbd, 0x27, 0xb0, 0x18, + 0xe8, 0xbf, 0x27, 0xb0, 0x1a, 0xe8, 0xc1, 0x27, + 0xb0, 0x1c, 0xe8, 0xc3, 0x27, 0xb9, 0x2f, 0x00, + 0xb0, 0x21, 0xe8, 0xc2, 0x27, 0xb9, 0x6c, 0x02, + 0xe8, 0x74, 0x98, 0xb9, 0x38, 0x00, 0xb0, 0x03, + 0xb4, 0x03, 0xe8, 0x78, 0x27, 0xb0, 0x06, 0xe8, + 0x7c, 0x27, 0xb0, 0x09, 0xe8, 0x7e, 0x27, 0xb0, + 0x0c, 0xe8, 0x80, 0x27, 0xb0, 0x0f, 0xe8, 0x82, + 0x27, 0xb0, 0x12, 0xe8, 0x84, 0x27, 0xc6, 0x06, + 0xe6, 0x1c, 0xd9, 0xc6, 0x06, 0x35, 0x33, 0x14, + 0xc6, 0x06, 0x36, 0x33, 0x25, 0xb8, 0xce, 0x3a, + 0xa3, 0x37, 0x33, 0xb8, 0x26, 0x0f, 0xa3, 0x39, + 0x33, 0xbb, 0x47, 0x33, 0xc7, 0x07, 0x6f, 0x02, + 0xb9, 0x6d, 0x02, 0xe8, 0xa9, 0x98, 0xc6, 0x06, + 0x35, 0x33, 0x01, 0xc6, 0x06, 0x36, 0x33, 0x09, + 0xb8, 0xe6, 0x3a, 0xa3, 0x37, 0x33, 0xb8, 0x1e, + 0x0f, 0xa3, 0x39, 0x33, 0xb9, 0x23, 0x00, 0xb0, + 0x01, 0xb4, 0x03, 0xe8, 0x17, 0x27, 0xbb, 0x47, + 0x33, 0xc7, 0x07, 0x70, 0x02, 0xb9, 0x6e, 0x02, + 0xe8, 0x7c, 0x98, 0xe8, 0x36, 0x98, 0xb8, 0x96, + 0x00, 0xe8, 0x3e, 0x9c, 0xbb, 0xfd, 0x3a, 0xe8, + 0x53, 0x1d, 0xb0, 0x2b, 0xe8, 0x41, 0xa4, 0xe8, + 0x3b, 0x1a, 0xc3, 0xbb, 0xa1, 0x46, 0xe8, 0x44, + 0x1d, 0xc3, 0xbb, 0x00, 0x30, 0xe8, 0xad, 0x8e, + 0xb9, 0x05, 0x00, 0xb0, 0x18, 0xb4, 0x03, 0xe8, + 0xdb, 0x26, 0xb9, 0x1a, 0x00, 0xb0, 0x20, 0xe8, + 0xdc, 0x26, 0xb9, 0x05, 0x00, 0xb0, 0x2a, 0xe8, + 0xdb, 0x26, 0xb9, 0x0f, 0x00, 0xb0, 0x4d, 0xe8, + 0xda, 0x26, 0xb0, 0x4f, 0xe8, 0xdc, 0x26, 0xb0, + 0x52, 0xe8, 0xde, 0x26, 0xb9, 0x16, 0x00, 0xb0, + 0x5b, 0xe8, 0xdd, 0x26, 0xb9, 0x16, 0x00, 0xb0, + 0x66, 0xe8, 0xdc, 0x26, 0xb9, 0x1a, 0x00, 0xb0, + 0x72, 0xe8, 0xdb, 0x26, 0xb9, 0x18, 0x00, 0xb0, + 0x7c, 0xe8, 0xda, 0x26, 0xb0, 0x1a, 0xb4, 0x01, + 0xe8, 0xa3, 0x9b, 0xbb, 0x47, 0x33, 0xc7, 0x07, + 0x33, 0x02, 0xb9, 0x32, 0x02, 0xe8, 0xa6, 0x97, + 0xb0, 0x06, 0xe8, 0x68, 0x96, 0xb0, 0x01, 0xb4, + 0x00, 0xe8, 0xf5, 0xaa, 0xe8, 0x09, 0xab, 0xbb, + 0x3f, 0x36, 0xe8, 0xc8, 0x1c, 0xb0, 0x1b, 0xe8, + 0xb6, 0xa3, 0xb0, 0x1c, 0xe8, 0xa1, 0xa3, 0xc3, + 0xbb, 0x13, 0x49, 0xe8, 0xb7, 0x1c, 0xc3, 0xb0, + 0x20, 0xe8, 0xa4, 0xa3, 0xb9, 0x25, 0x00, 0xb0, + 0x0e, 0xb4, 0x04, 0xe8, 0x4f, 0x26, 0xb9, 0x10, + 0x00, 0xb0, 0x11, 0xb4, 0x09, 0xe8, 0x4e, 0x26, + 0xbb, 0x47, 0x33, 0xc7, 0x47, 0x02, 0x35, 0x02, + 0xb9, 0x34, 0x02, 0xe8, 0x29, 0x97, 0xe8, 0x1f, + 0xbc, 0xc6, 0x07, 0x18, 0xe8, 0x5a, 0x2a, 0xe8, + 0xcc, 0x26, 0xb9, 0x27, 0x00, 0xb0, 0x05, 0xb4, + 0x00, 0xe8, 0x21, 0x26, 0xb9, 0x46, 0x02, 0xe8, + 0x10, 0x97, 0xe8, 0x47, 0x97, 0xbe, 0x3f, 0x00, + 0xbf, 0xc3, 0x00, 0xc6, 0x06, 0xc3, 0x64, 0x01, + 0xe8, 0x38, 0xb2, 0xbb, 0x47, 0x33, 0xc7, 0x47, + 0x02, 0x3b, 0x02, 0xb0, 0x02, 0xe8, 0x20, 0x96, + 0xbb, 0x47, 0x33, 0xc7, 0x47, 0x02, 0x3c, 0x02, + 0xb0, 0x02, 0xe8, 0x13, 0x96, 0xbb, 0x47, 0x33, + 0xc7, 0x47, 0x02, 0x3d, 0x02, 0xb0, 0x02, 0xe8, + 0x06, 0x96, 0xb9, 0x28, 0x00, 0xb0, 0x01, 0xb4, + 0x08, 0xe8, 0xd9, 0x25, 0xb0, 0x03, 0xe8, 0xdd, + 0x25, 0xb0, 0x05, 0xe8, 0xdf, 0x25, 0xb0, 0x07, + 0xe8, 0xe1, 0x25, 0xbb, 0x47, 0x33, 0xc7, 0x47, + 0x02, 0x3e, 0x02, 0xb0, 0x02, 0xe8, 0xe0, 0x95, + 0xb0, 0x01, 0xb4, 0x00, 0xe8, 0x32, 0xaa, 0xe8, + 0x46, 0xaa, 0xbb, 0x47, 0x33, 0xc7, 0x47, 0x02, + 0x3f, 0x02, 0xb0, 0x02, 0xe8, 0xc9, 0x95, 0xbb, + 0x47, 0x33, 0xc7, 0x47, 0x02, 0x40, 0x02, 0xb0, + 0x02, 0xe8, 0xbc, 0x95, 0xbb, 0x47, 0x33, 0xc7, + 0x47, 0x02, 0x41, 0x02, 0xb0, 0x02, 0xe8, 0xaf, + 0x95, 0xbb, 0x47, 0x33, 0xc7, 0x47, 0x02, 0x42, + 0x02, 0xb0, 0x02, 0xe8, 0xa2, 0x95, 0xbb, 0x47, + 0x33, 0xc7, 0x47, 0x02, 0x43, 0x02, 0xb0, 0x02, + 0xe8, 0x95, 0x95, 0xbb, 0x47, 0x33, 0xc7, 0x47, + 0x02, 0x44, 0x02, 0xb0, 0x02, 0xe8, 0x88, 0x95, + 0xb9, 0x37, 0x00, 0xb0, 0x12, 0xb4, 0x01, 0xe8, + 0x5b, 0x25, 0xbb, 0x47, 0x33, 0xc7, 0x47, 0x02, + 0x45, 0x02, 0xb0, 0x02, 0xe8, 0x71, 0x95, 0xe8, + 0xec, 0x25, 0xc6, 0x06, 0x33, 0x33, 0x01, 0xc6, + 0x06, 0xdd, 0x1c, 0x02, 0xb0, 0x02, 0xe8, 0x24, + 0x95, 0xc6, 0x06, 0x9f, 0xdb, 0x01, 0xc3, 0xb9, + 0x14, 0x00, 0xb0, 0x09, 0xb4, 0x06, 0xe8, 0x2c, + 0x25, 0xb9, 0x12, 0x02, 0xe8, 0x18, 0x96, 0xe8, + 0xb9, 0x26, 0xc7, 0x06, 0xf3, 0xb4, 0x10, 0x00, + 0xc6, 0x06, 0xdc, 0x1c, 0x01, 0xe8, 0x3f, 0x27, + 0xb9, 0x13, 0x02, 0xc6, 0x06, 0xdc, 0x1c, 0x01, + 0xe8, 0xfc, 0x95, 0xb9, 0x24, 0x00, 0xb0, 0x04, + 0xb4, 0x0c, 0xe8, 0x00, 0x25, 0xb9, 0x14, 0x02, + 0xe8, 0xef, 0x95, 0xb9, 0x15, 0x02, 0xe8, 0xe9, + 0x95, 0xe8, 0xdc, 0xba, 0xb0, 0x09, 0x88, 0x07, + 0xe8, 0x0e, 0x2a, 0xc7, 0x06, 0xaf, 0x64, 0xec, + 0x00, 0xc7, 0x06, 0xb1, 0x64, 0x5f, 0x00, 0xc6, + 0x06, 0xc3, 0x64, 0x01, 0xc6, 0x06, 0xcb, 0x64, + 0x01, 0xc6, 0x06, 0xcd, 0x64, 0x00, 0xe8, 0xfb, + 0x95, 0xb9, 0x09, 0x00, 0xe8, 0x14, 0x2c, 0xc3, + 0xbb, 0x46, 0x67, 0xe8, 0x9c, 0xba, 0x83, 0xc3, + 0x03, 0xff, 0x37, 0x53, 0xc7, 0x07, 0x3f, 0x01, + 0xbe, 0xec, 0x00, 0xbf, 0xbe, 0x00, 0xe8, 0xda, + 0xb0, 0xb9, 0x05, 0x00, 0xb0, 0x04, 0xb4, 0x0b, + 0xe8, 0xa2, 0x24, 0xb9, 0x0e, 0x00, 0xb0, 0x0e, + 0xe8, 0xa3, 0x24, 0xb0, 0x21, 0xe8, 0xa5, 0x24, + 0xb9, 0x05, 0x00, 0xb0, 0x2b, 0xe8, 0xa4, 0x24, + 0xe8, 0xe4, 0x95, 0xe8, 0x72, 0xba, 0x53, 0xc6, + 0x47, 0x02, 0x00, 0xe8, 0xf6, 0x28, 0xe8, 0x30, + 0x00, 0xb9, 0x83, 0x02, 0xe8, 0x6b, 0x95, 0x5b, + 0xc6, 0x47, 0x02, 0x2b, 0xe8, 0xa1, 0x28, 0xe8, + 0x32, 0x00, 0xe8, 0x97, 0x95, 0xbe, 0xec, 0x00, + 0xbf, 0xb3, 0x00, 0xc6, 0x06, 0xc3, 0x64, 0x03, + 0xe8, 0x88, 0xb0, 0x5b, 0x8f, 0x07, 0xb0, 0x2c, + 0xe8, 0x9d, 0xa1, 0xb0, 0x2e, 0xe8, 0x88, 0xa1, + 0xc3, 0x1e, 0xa1, 0xb5, 0x32, 0x8e, 0xd8, 0x33, + 0xdb, 0x8a, 0x07, 0xc6, 0x07, 0x00, 0x1f, 0x2e, + 0xa2, 0xd5, 0x85, 0xc3, 0x2e, 0xa0, 0xd5, 0x85, + 0x1e, 0x8b, 0x1e, 0xb5, 0x32, 0x8e, 0xdb, 0x33, + 0xdb, 0x88, 0x07, 0x1f, 0xc3, 0x00, 0xbb, 0xbf, + 0x48, 0xe8, 0x79, 0x1a, 0xc3, 0xbb, 0xd6, 0x48, + 0xe8, 0x72, 0x1a, 0xc3, 0xbb, 0x5c, 0x49, 0xe8, + 0x6b, 0x1a, 0xc3, 0x80, 0x3e, 0xb0, 0xdb, 0x01, + 0x74, 0x07, 0xbb, 0x86, 0x3d, 0xe8, 0x5d, 0x1a, + 0xc3, 0xb0, 0x06, 0xe8, 0xdb, 0x93, 0xb9, 0x19, + 0x00, 0xb0, 0x0a, 0xb4, 0x0a, 0xe8, 0xf5, 0x23, + 0xb0, 0x0e, 0xe8, 0xf9, 0x23, 0xb0, 0x12, 0xe8, + 0xfb, 0x23, 0xb9, 0x2f, 0x02, 0xe8, 0xe7, 0x94, + 0xe8, 0xcd, 0xb9, 0xb0, 0x17, 0x88, 0x47, 0x01, + 0xe8, 0xfe, 0x28, 0xe8, 0x06, 0x95, 0xc6, 0x06, + 0xb0, 0xdb, 0x02, 0xc3, 0xbb, 0x57, 0x34, 0x80, + 0x3e, 0xb0, 0xdb, 0x01, 0x75, 0x03, 0xbb, 0x82, + 0x48, 0xe8, 0x19, 0x1a, 0xc3, 0xb9, 0x0c, 0x00, + 0xb0, 0x04, 0xb4, 0x08, 0xe8, 0xb6, 0x23, 0xb9, + 0x32, 0x00, 0xb0, 0x14, 0xe8, 0xb7, 0x23, 0xb0, + 0x1d, 0xe8, 0xb9, 0x23, 0xb9, 0x2a, 0x02, 0xe8, + 0xb6, 0x94, 0xb0, 0x13, 0xe8, 0xe9, 0xa0, 0xb0, + 0x16, 0xe8, 0xd4, 0xa0, 0xc3, 0xb9, 0x05, 0x00, + 0xb0, 0x03, 0xb4, 0x0c, 0xe8, 0x8e, 0x23, 0xb9, + 0x38, 0x00, 0xb0, 0x0c, 0xe8, 0x8f, 0x23, 0xb0, + 0x0e, 0xe8, 0x91, 0x23, 0xb0, 0x10, 0xe8, 0x93, + 0x23, 0xb0, 0x12, 0xe8, 0x95, 0x23, 0xb0, 0x16, + 0xe8, 0x97, 0x23, 0xb0, 0x18, 0xe8, 0x99, 0x23, + 0xb9, 0x05, 0x00, 0xb0, 0x1c, 0xe8, 0x98, 0x23, + 0xb9, 0x37, 0x02, 0xe8, 0x72, 0x94, 0xb0, 0x0c, + 0xe8, 0xa5, 0xa0, 0xb0, 0x21, 0xe8, 0x90, 0xa0, + 0xc3, 0x80, 0x3e, 0x92, 0xdb, 0x01, 0x75, 0x07, + 0xbb, 0x40, 0x3d, 0xe8, 0x9f, 0x19, 0xc3, 0xc6, + 0x06, 0x92, 0xdb, 0x01, 0xe8, 0x44, 0x91, 0xbb, + 0xcd, 0x0f, 0xe8, 0x00, 0x8b, 0xc6, 0x06, 0xe6, + 0x1c, 0xd1, 0xb8, 0x07, 0x01, 0xe8, 0x3e, 0x98, + 0xb9, 0x05, 0x00, 0xb0, 0x10, 0xb4, 0x06, 0xe8, + 0x23, 0x23, 0xb9, 0x01, 0x00, 0xb0, 0x19, 0xe8, + 0x24, 0x23, 0xb0, 0x1d, 0xe8, 0x26, 0x23, 0xb0, + 0x22, 0xe8, 0x28, 0x23, 0xc6, 0x06, 0xdc, 0x64, + 0x00, 0xbb, 0x47, 0x33, 0xc7, 0x07, 0xfa, 0x01, + 0xb9, 0xf8, 0x01, 0xe8, 0xf4, 0x93, 0xe8, 0xe7, + 0xb8, 0xc6, 0x07, 0x00, 0xe8, 0x29, 0x27, 0xc7, + 0x06, 0x30, 0x32, 0x00, 0x00, 0xc7, 0x06, 0x2e, + 0x32, 0x72, 0x00, 0xc7, 0x06, 0x2c, 0x32, 0xae, + 0x00, 0xb9, 0x18, 0x00, 0xb0, 0x02, 0xb4, 0x06, + 0xe8, 0xda, 0x22, 0xb9, 0x16, 0x00, 0xb0, 0x18, + 0xe8, 0xdb, 0x22, 0xb9, 0x01, 0x00, 0xb0, 0x1c, + 0xe8, 0xda, 0x22, 0xb0, 0x20, 0xe8, 0xdc, 0x22, + 0xb0, 0x25, 0xe8, 0xde, 0x22, 0xb9, 0x05, 0x00, + 0xb0, 0x2b, 0xe8, 0xdd, 0x22, 0xb9, 0x3d, 0x00, + 0xb0, 0x46, 0xe8, 0xdc, 0x22, 0xb9, 0x3d, 0x00, + 0xb0, 0x5b, 0xe8, 0xdb, 0x22, 0xc6, 0x06, 0x35, + 0x33, 0x06, 0xc6, 0x06, 0x36, 0x33, 0x11, 0xb8, + 0xfb, 0x3c, 0xa3, 0x37, 0x33, 0xb8, 0xcd, 0x70, + 0xa3, 0x39, 0x33, 0xbb, 0x47, 0x33, 0xc7, 0x07, + 0xfb, 0x01, 0xb9, 0xf9, 0x01, 0xe8, 0xf7, 0x93, + 0xe8, 0x6d, 0xb8, 0xb0, 0x04, 0x88, 0x07, 0xe8, + 0x9f, 0x27, 0xb8, 0x03, 0x00, 0xe8, 0xbd, 0x1c, + 0xb8, 0x14, 0x00, 0x01, 0x47, 0x03, 0x01, 0x47, + 0x07, 0xc6, 0x06, 0xcd, 0x64, 0x00, 0xc6, 0x06, + 0xcc, 0x64, 0x01, 0xc6, 0x06, 0xcb, 0x64, 0x01, + 0xff, 0x06, 0xaf, 0x64, 0xe8, 0xec, 0x23, 0xe8, + 0xcf, 0xb6, 0xb9, 0x0a, 0x00, 0xb0, 0x03, 0xb4, + 0x02, 0xe8, 0x49, 0x22, 0xbb, 0x47, 0x33, 0xc7, + 0x07, 0xf7, 0x01, 0xb0, 0x01, 0xe8, 0x60, 0x92, + 0xe8, 0xdb, 0x22, 0xc7, 0x06, 0xf3, 0xb4, 0x16, + 0x00, 0xb8, 0x01, 0x00, 0xe8, 0xaa, 0xa6, 0xb0, + 0x01, 0xe8, 0x11, 0x92, 0xc7, 0x06, 0xf3, 0xb4, + 0x14, 0x00, 0xb0, 0x0d, 0xe8, 0x06, 0x92, 0xb8, + 0x01, 0x00, 0xe8, 0x94, 0xa6, 0xe8, 0xa8, 0xa6, + 0xb0, 0x01, 0xe8, 0xf8, 0x91, 0xb0, 0x02, 0xe8, + 0xf3, 0x91, 0xb0, 0x0e, 0xe8, 0xee, 0x91, 0xb0, + 0x0f, 0xe8, 0xe9, 0x91, 0xb0, 0x10, 0xe8, 0xe4, + 0x91, 0xe8, 0x41, 0x14, 0xb8, 0x32, 0x00, 0xe8, + 0x52, 0x97, 0xe8, 0x47, 0x14, 0xbe, 0xa2, 0x00, + 0xbf, 0xa4, 0x00, 0xc6, 0x06, 0xc3, 0x64, 0x02, + 0xe8, 0x10, 0xae, 0xc6, 0x06, 0xe6, 0x1c, 0xe5, + 0xbb, 0x01, 0x3d, 0xbe, 0x46, 0x5f, 0xe8, 0xd5, + 0x1a, 0xc6, 0x06, 0xe6, 0x1c, 0xd8, 0xbb, 0x20, + 0x3d, 0xbe, 0x5a, 0x5f, 0xe8, 0xc7, 0x1a, 0xbe, + 0xa2, 0x00, 0xbf, 0xbf, 0x00, 0xc6, 0x06, 0xc3, + 0x64, 0x02, 0xe8, 0xe6, 0xad, 0xe8, 0xfd, 0x13, + 0xb8, 0x32, 0x00, 0xe8, 0x0e, 0x97, 0xe8, 0x97, + 0xb7, 0x32, 0xc0, 0x88, 0x47, 0x01, 0x88, 0x47, + 0x02, 0x88, 0x47, 0x03, 0x88, 0x47, 0x04, 0xc6, + 0x06, 0xdc, 0x1c, 0x03, 0xe8, 0x0d, 0x26, 0xbb, + 0x46, 0x67, 0xe8, 0x6d, 0xb7, 0x43, 0x33, 0xc0, + 0x89, 0x47, 0x02, 0x89, 0x47, 0x04, 0x89, 0x47, + 0x06, 0x89, 0x47, 0x08, 0xb9, 0x3e, 0x00, 0xb0, + 0x01, 0xb4, 0x0e, 0xe8, 0x77, 0x21, 0xb9, 0x09, + 0x00, 0xb0, 0x08, 0xe8, 0x78, 0x21, 0xe8, 0xc3, + 0x13, 0xbb, 0x47, 0x33, 0xc7, 0x07, 0x00, 0x02, + 0xb0, 0x01, 0xe8, 0x83, 0x91, 0xe8, 0x4c, 0x22, + 0xb0, 0x01, 0xb4, 0x00, 0xe8, 0xd2, 0xa5, 0xe8, + 0xe6, 0xa5, 0xe8, 0xa8, 0x96, 0xbb, 0x3a, 0x3d, + 0xe8, 0xa2, 0x17, 0xb0, 0x07, 0xb4, 0x01, 0xb9, + 0xe4, 0x00, 0xba, 0xab, 0x00, 0xe8, 0x45, 0x91, + 0xb0, 0x08, 0xb9, 0x22, 0x01, 0xe8, 0x3d, 0x91, + 0xc3, 0x80, 0x3e, 0x9a, 0xdb, 0x01, 0x74, 0x3b, + 0xe8, 0x0b, 0x00, 0xb0, 0x0a, 0xe8, 0x70, 0x9e, + 0xc6, 0x06, 0x9a, 0xdb, 0x01, 0xc3, 0xe8, 0xdc, + 0x8e, 0xbb, 0x5f, 0x1b, 0xe8, 0xde, 0x88, 0xb9, + 0x05, 0x00, 0xb0, 0x02, 0xb4, 0x03, 0xe8, 0x0c, + 0x21, 0xbb, 0x47, 0x33, 0xc7, 0x07, 0x1a, 0x02, + 0xb9, 0x19, 0x02, 0xe8, 0x27, 0x92, 0xe8, 0x54, + 0x96, 0xe8, 0xb9, 0x8e, 0xbb, 0xe0, 0x1b, 0xe8, + 0xbb, 0x88, 0xc3, 0xe8, 0xaf, 0x8e, 0xbb, 0xf0, + 0xda, 0xe8, 0x99, 0x88, 0xe8, 0xae, 0x88, 0xc3, + 0x80, 0x3e, 0x9a, 0xdb, 0x01, 0x74, 0xec, 0xe8, + 0xbc, 0xff, 0xb0, 0x0b, 0xe8, 0x21, 0x9e, 0xc6, + 0x06, 0x9a, 0xdb, 0x01, 0xc3, 0x80, 0x3e, 0x9b, + 0xdb, 0x01, 0x74, 0x76, 0xe8, 0x0b, 0x00, 0xb0, + 0x0a, 0xe8, 0x0c, 0x9e, 0xc6, 0x06, 0x9b, 0xdb, + 0x01, 0xc3, 0xe8, 0x27, 0x8f, 0xbb, 0x93, 0x22, + 0xe8, 0x7a, 0x88, 0xb9, 0x05, 0x00, 0xb0, 0x0a, + 0xb4, 0x03, 0xe8, 0xa8, 0x20, 0xbb, 0x47, 0x33, + 0xc7, 0x47, 0x02, 0x1b, 0x02, 0xb9, 0x1c, 0x02, + 0xe8, 0xc2, 0x91, 0xb8, 0x64, 0x00, 0xe8, 0xd1, + 0x95, 0xe8, 0x00, 0x8f, 0xbb, 0xb1, 0x24, 0xe8, + 0x53, 0x88, 0xe8, 0xd9, 0x95, 0xe8, 0x45, 0x8e, + 0xbb, 0xd7, 0x24, 0xe8, 0x47, 0x88, 0xe8, 0xeb, + 0x8e, 0xbb, 0x14, 0x25, 0xe8, 0x3e, 0x88, 0xe8, + 0xc4, 0x95, 0x8b, 0x3e, 0xb1, 0x64, 0x8b, 0x36, + 0xaf, 0x64, 0x57, 0x56, 0x47, 0xe8, 0x93, 0xac, + 0xe8, 0xb3, 0x95, 0xbb, 0x70, 0x25, 0xe8, 0x24, + 0x88, 0xe8, 0xaa, 0x95, 0x5e, 0x5f, 0xe8, 0x82, + 0xac, 0xc3, 0xe8, 0xbf, 0x8e, 0xbb, 0x02, 0xdb, + 0xe8, 0xfa, 0x87, 0xe8, 0x0f, 0x88, 0xc3, 0x80, + 0x3e, 0x9b, 0xdb, 0x01, 0x74, 0xec, 0xe8, 0x81, + 0xff, 0xb0, 0x0b, 0xe8, 0x82, 0x9d, 0xc6, 0x06, + 0x9b, 0xdb, 0x01, 0xc3, 0xb0, 0x17, 0xe8, 0x77, + 0x9d, 0xe8, 0x98, 0x8e, 0xbb, 0x34, 0x26, 0xe8, + 0xeb, 0x87, 0xb9, 0x05, 0x00, 0xb0, 0x06, 0xb4, + 0x0c, 0xe8, 0x19, 0x20, 0xbb, 0x47, 0x33, 0xc7, + 0x47, 0x02, 0x2c, 0x02, 0xb9, 0x2b, 0x02, 0xe8, + 0x33, 0x91, 0xe8, 0x77, 0x8e, 0xbb, 0x0a, 0x28, + 0xe8, 0xca, 0x87, 0xb9, 0x05, 0x00, 0xb0, 0x0a, + 0xb4, 0x0c, 0xe8, 0xf8, 0x1f, 0xbb, 0x47, 0x33, + 0xc7, 0x47, 0x02, 0x2e, 0x02, 0xb9, 0x2d, 0x02, + 0xe8, 0x12, 0x91, 0xe8, 0x56, 0x8e, 0xbb, 0x71, + 0x29, 0xe8, 0xa9, 0x87, 0xb0, 0x18, 0xe8, 0x17, + 0x9d, 0xc3, 0xb9, 0x2d, 0x00, 0xb0, 0x10, 0xb4, + 0x06, 0xe8, 0xd1, 0x1f, 0xe8, 0x28, 0x91, 0xe8, + 0x7f, 0xfb, 0xb9, 0x30, 0x02, 0xe8, 0xba, 0x90, + 0xe8, 0x89, 0xfb, 0xe8, 0xee, 0x90, 0xb0, 0x1a, + 0xe8, 0x05, 0x9d, 0xb0, 0x1b, 0xe8, 0xf0, 0x9c, + 0xe8, 0x03, 0x95, 0xe8, 0x6f, 0x8d, 0xbb, 0xcd, + 0x1e, 0xe8, 0x71, 0x87, 0x8b, 0x3e, 0xb1, 0x64, + 0x8b, 0x36, 0xaf, 0x64, 0x4e, 0xe8, 0xcb, 0xab, + 0xe8, 0x5a, 0x8d, 0xbb, 0x09, 0x1f, 0xe8, 0x5c, + 0x87, 0xc6, 0x06, 0xb1, 0xdb, 0x01, 0xc3, 0x80, + 0x3e, 0xb5, 0xdb, 0x01, 0x74, 0x07, 0xbb, 0x29, + 0x4a, 0xe8, 0xd9, 0x15, 0xc3, 0xe8, 0xec, 0x8d, + 0xbb, 0x92, 0x29, 0xe8, 0x3f, 0x87, 0xb9, 0x05, + 0x00, 0xb0, 0x03, 0xb4, 0x0e, 0xe8, 0x6d, 0x1f, + 0xb0, 0x14, 0xe8, 0x71, 0x1f, 0xbb, 0x47, 0x33, + 0xc7, 0x47, 0x02, 0x9f, 0x02, 0xb9, 0x9e, 0x02, + 0xe8, 0x5f, 0x90, 0xe8, 0xed, 0x20, 0xe8, 0xcb, + 0x00, 0xe8, 0xe4, 0x8d, 0xbb, 0x00, 0x2a, 0xe8, + 0x13, 0x87, 0xe8, 0xbf, 0x00, 0xc6, 0x06, 0xe6, + 0x1c, 0xe5, 0xc6, 0x06, 0x35, 0x33, 0x17, 0xc6, + 0x06, 0x36, 0x33, 0x26, 0xb8, 0x5b, 0x4a, 0xa3, + 0x37, 0x33, 0xb8, 0x4c, 0x8f, 0xa3, 0x39, 0x33, + 0xb9, 0x53, 0x00, 0xb0, 0x0c, 0xb4, 0x0c, 0xe8, + 0x23, 0x1f, 0xc6, 0x06, 0xdc, 0x64, 0x00, 0xb9, + 0xa1, 0x02, 0xe8, 0x8a, 0x90, 0xe8, 0xab, 0x20, + 0xe8, 0x01, 0x20, 0xc7, 0x06, 0xf3, 0xb4, 0x0b, + 0x00, 0xc6, 0x06, 0xdc, 0x1c, 0x01, 0xe8, 0x2e, + 0x21, 0xb9, 0x18, 0x00, 0xb0, 0x1f, 0xb4, 0x09, + 0xe8, 0xfa, 0x1e, 0xb0, 0x30, 0xe8, 0xfe, 0x1e, + 0xb9, 0x4f, 0x00, 0xb0, 0x32, 0xb4, 0x0c, 0xe8, + 0xfb, 0x1e, 0xbb, 0x47, 0x33, 0xc7, 0x07, 0xa3, + 0x02, 0xb9, 0xa2, 0x02, 0xc6, 0x06, 0xdc, 0x1c, + 0x01, 0xe8, 0xce, 0x8f, 0xe8, 0x6c, 0x20, 0xe8, + 0xc2, 0x1f, 0xc7, 0x06, 0xf3, 0xb4, 0x1c, 0x00, + 0xc6, 0x06, 0xdc, 0x1c, 0x01, 0xe8, 0xef, 0x20, + 0xc7, 0x06, 0xaf, 0x64, 0x00, 0x00, 0xc7, 0x06, + 0xb1, 0x64, 0xa7, 0x00, 0xc6, 0x06, 0xc3, 0x64, + 0x02, 0xc6, 0x06, 0xcb, 0x64, 0x00, 0xc6, 0x06, + 0xcd, 0x64, 0x00, 0xe8, 0xd6, 0x8f, 0xbe, 0x42, + 0x00, 0xbf, 0xa7, 0x00, 0xe8, 0xcc, 0xaa, 0xbb, + 0x6f, 0x4a, 0xe8, 0xf0, 0x14, 0xe8, 0x16, 0x9c, + 0xb0, 0x1d, 0xe8, 0xcb, 0x9b, 0xb9, 0x0a, 0x00, + 0xe8, 0xd8, 0x25, 0xc3, 0xbb, 0x47, 0x33, 0xc7, + 0x47, 0x02, 0xa0, 0x02, 0xb0, 0x02, 0xe8, 0xa6, + 0x8e, 0xc3, 0xe8, 0xd2, 0x8f, 0xe8, 0x60, 0xb4, + 0xc6, 0x07, 0x00, 0xe8, 0xe6, 0x22, 0xb9, 0x05, + 0x00, 0xb0, 0x03, 0xb4, 0x05, 0xe8, 0x65, 0x1e, + 0xb0, 0x06, 0xe8, 0x69, 0x1e, 0xb0, 0x0a, 0xe8, + 0x6b, 0x1e, 0xb9, 0x5c, 0x00, 0xb0, 0x14, 0xe8, + 0x6a, 0x1e, 0xb0, 0x26, 0xe8, 0x6c, 0x1e, 0xb0, + 0x3a, 0xe8, 0x6e, 0x1e, 0xc6, 0x06, 0x35, 0x33, + 0x3a, 0xc6, 0x06, 0x36, 0x33, 0x43, 0xb8, 0x4a, + 0x46, 0xa3, 0x37, 0x33, 0xb8, 0x9e, 0x8e, 0xa3, + 0x39, 0x33, 0xc6, 0x06, 0xdc, 0x64, 0x00, 0xb9, + 0x5a, 0x02, 0xe8, 0x9a, 0x8f, 0xc6, 0x06, 0xdc, + 0x64, 0x01, 0xb9, 0x05, 0x00, 0xb0, 0x03, 0xb4, + 0x04, 0xe8, 0x19, 0x1e, 0xb9, 0x5b, 0x02, 0xe8, + 0x18, 0x8f, 0xe8, 0xfb, 0xb3, 0xc6, 0x07, 0x1b, + 0xe8, 0x3d, 0x22, 0xe8, 0x36, 0x8f, 0xc6, 0x06, + 0xa5, 0xdb, 0x01, 0xc3, 0xbb, 0x31, 0x3c, 0xe8, + 0x53, 0x14, 0xb9, 0x05, 0x00, 0xb0, 0x03, 0xb4, + 0x0a, 0xe8, 0xf1, 0x1d, 0xb9, 0x1a, 0x00, 0xb0, + 0x0d, 0xe8, 0xf2, 0x1d, 0xbb, 0x47, 0x33, 0xc7, + 0x07, 0x92, 0x02, 0xb9, 0x91, 0x02, 0xe8, 0xce, + 0x8e, 0xe8, 0x6f, 0x1f, 0xbb, 0x47, 0x33, 0xc7, + 0x07, 0x93, 0x02, 0xb0, 0x01, 0xe8, 0xf7, 0x8d, + 0xe8, 0xb9, 0x1e, 0xe8, 0x1f, 0x93, 0xbb, 0x3d, + 0x3c, 0xe8, 0x19, 0x14, 0xb0, 0x24, 0xe8, 0x07, + 0x9b, 0xb0, 0x09, 0xe8, 0x1c, 0x00, 0xb0, 0x07, + 0xe8, 0x17, 0x00, 0xbb, 0x46, 0x67, 0xe8, 0x89, + 0xb3, 0x43, 0xc7, 0x47, 0x06, 0x0a, 0x01, 0xc7, + 0x47, 0x08, 0xc1, 0x00, 0xc6, 0x06, 0xad, 0xdb, + 0x01, 0xc3, 0xb4, 0x01, 0xb9, 0x29, 0x01, 0xba, + 0xb5, 0x00, 0xe8, 0x98, 0x8d, 0xc3, 0xb9, 0x05, + 0x00, 0xb0, 0x03, 0xb4, 0x09, 0xe8, 0x85, 0x1d, + 0xb9, 0x1a, 0x00, 0xb0, 0x0d, 0xe8, 0x86, 0x1d, + 0xb9, 0x18, 0x00, 0xb0, 0x16, 0xe8, 0x85, 0x1d, + 0xb9, 0x50, 0x02, 0xe8, 0x71, 0x8e, 0xe8, 0x57, + 0xb3, 0xc6, 0x07, 0x00, 0xe8, 0x99, 0x21, 0xb9, + 0x01, 0x00, 0xb0, 0x05, 0xb4, 0x09, 0xe8, 0x5c, + 0x1d, 0xb0, 0x09, 0xe8, 0x60, 0x1d, 0xb0, 0x0d, + 0xe8, 0x62, 0x1d, 0xb9, 0x51, 0x02, 0xe8, 0x51, + 0x8e, 0xe8, 0x34, 0xb3, 0xc6, 0x07, 0x1c, 0xe8, + 0x76, 0x21, 0xe8, 0x6f, 0x8e, 0xb0, 0x23, 0xe8, + 0x86, 0x9a, 0xb0, 0x01, 0xe8, 0x12, 0x8d, 0xc3, + 0xb9, 0x05, 0x00, 0xb0, 0x03, 0xb4, 0x09, 0xe8, + 0x2b, 0x1d, 0xb9, 0x84, 0x02, 0xe8, 0x27, 0x8e, + 0xe8, 0x0d, 0xb3, 0xc6, 0x47, 0x01, 0x2d, 0xe8, + 0x4e, 0x21, 0xb9, 0x38, 0x00, 0xb0, 0x02, 0xb4, + 0x08, 0xe8, 0x11, 0x1d, 0xb9, 0x1a, 0x00, 0xb0, + 0x04, 0xe8, 0x12, 0x1d, 0xb9, 0x85, 0x02, 0xe8, + 0x08, 0x8e, 0xb9, 0x38, 0x00, 0xb0, 0x01, 0xb4, + 0x08, 0xe8, 0xf9, 0x1c, 0xb0, 0x06, 0xe8, 0x04, + 0x1d, 0xb9, 0x1a, 0x00, 0xb0, 0x03, 0xe8, 0xf5, + 0x1c, 0xb0, 0x08, 0xe8, 0xfe, 0x1c, 0xb9, 0x86, + 0x02, 0xe8, 0xe6, 0x8d, 0xb9, 0x05, 0x00, 0xb0, + 0x15, 0xb4, 0x09, 0xe8, 0xd7, 0x1c, 0xb9, 0x87, + 0x02, 0xe8, 0xf9, 0x8d, 0xb0, 0x02, 0xe8, 0xb4, + 0x8c, 0xb8, 0x03, 0x00, 0xe8, 0x0e, 0x17, 0xc7, + 0x47, 0x0d, 0x9c, 0x00, 0xc6, 0x06, 0xac, 0xdb, + 0x01, 0xc3, 0xbb, 0x0b, 0x5e, 0xe8, 0x0d, 0x13, + 0xc3, 0xbb, 0xa1, 0x46, 0xe8, 0x06, 0x13, 0xc3, + 0xbb, 0xc3, 0x46, 0xe8, 0xff, 0x12, 0xc3, 0xb9, + 0x05, 0x00, 0xb0, 0x02, 0xb4, 0x07, 0xe8, 0x9c, + 0x1c, 0xb9, 0x0f, 0x00, 0xb0, 0x0c, 0xe8, 0x9d, + 0x1c, 0xb9, 0x7e, 0x02, 0xe8, 0xa1, 0x8d, 0xb0, + 0x30, 0xe8, 0xd4, 0x99, 0xe8, 0x0d, 0x91, 0x72, + 0x1e, 0xb0, 0x30, 0xe8, 0xba, 0x99, 0xb9, 0x18, + 0x00, 0xb0, 0x1a, 0xb4, 0x07, 0xe8, 0x75, 0x1c, + 0xbb, 0x47, 0x33, 0xc7, 0x47, 0x04, 0x8b, 0x02, + 0xb9, 0x8a, 0x02, 0xe8, 0x7a, 0x8d, 0xc3, 0xa1, + 0x1f, 0xc4, 0xe8, 0xc7, 0x9a, 0x3c, 0x31, 0x75, + 0xd8, 0x83, 0x3e, 0x52, 0x72, 0x05, 0x75, 0xd1, + 0xc6, 0x06, 0xcf, 0x00, 0x00, 0xb9, 0x05, 0x00, + 0xb0, 0x02, 0xb4, 0x07, 0xe8, 0x46, 0x1c, 0xb9, + 0x34, 0x00, 0xb0, 0x0d, 0xe8, 0x47, 0x1c, 0xb9, + 0x88, 0x02, 0xe8, 0x2a, 0x8d, 0xe8, 0x20, 0xb2, + 0xc6, 0x47, 0x01, 0x2e, 0xe8, 0x61, 0x20, 0xe8, + 0x5a, 0x8d, 0xb0, 0x31, 0xe8, 0x71, 0x99, 0xe8, + 0xaa, 0x90, 0x73, 0x03, 0xe9, 0x91, 0x00, 0xb9, + 0x1b, 0x00, 0xb0, 0x05, 0xb4, 0x07, 0xe8, 0x14, + 0x1c, 0xe8, 0xfc, 0xb1, 0xc6, 0x47, 0x01, 0x00, + 0xe8, 0x81, 0x20, 0xb9, 0x8c, 0x02, 0xe8, 0xf9, + 0x8c, 0xb9, 0x18, 0x00, 0xb0, 0x04, 0xb4, 0x07, + 0xe8, 0xfa, 0x1b, 0xb9, 0x05, 0x00, 0xb0, 0x0c, + 0xe8, 0xfb, 0x1b, 0xc7, 0x06, 0xaf, 0x64, 0xbb, + 0x00, 0xc7, 0x06, 0xb1, 0x64, 0xb3, 0x00, 0xc6, + 0x06, 0xcc, 0x64, 0x01, 0xc6, 0x06, 0xcb, 0x64, + 0x00, 0xc6, 0x06, 0xdc, 0x64, 0x00, 0xb9, 0x8d, + 0x02, 0xe8, 0xc6, 0x8c, 0xbb, 0x47, 0x33, 0xc7, + 0x47, 0x04, 0x8f, 0x02, 0xb9, 0x8e, 0x02, 0xe8, + 0xdd, 0x8c, 0xff, 0x36, 0xcf, 0x00, 0xc6, 0x06, + 0xcf, 0x00, 0x00, 0xbb, 0xf6, 0x3b, 0xe8, 0x0c, + 0x12, 0x8f, 0x06, 0xcf, 0x00, 0xb0, 0x31, 0xe8, + 0xe6, 0x98, 0xc7, 0x06, 0xf3, 0xb4, 0x1b, 0x00, + 0xb0, 0x02, 0xb4, 0x04, 0xe8, 0x1a, 0xa0, 0xb0, + 0x04, 0xe8, 0x75, 0x8b, 0xc6, 0x06, 0xa9, 0xdb, + 0x00, 0xc7, 0x06, 0xf3, 0xb4, 0x1a, 0x00, 0xc3, + 0xa1, 0x1f, 0xc4, 0xe8, 0xee, 0x99, 0x3c, 0x1d, + 0x74, 0x03, 0xe9, 0x62, 0xff, 0x83, 0x3e, 0x52, + 0x72, 0x05, 0x74, 0x03, 0xe9, 0x58, 0xff, 0xc6, + 0x06, 0xcf, 0x00, 0x00, 0xc7, 0x06, 0x1f, 0xc4, + 0x00, 0x00, 0xb9, 0x05, 0x00, 0xb0, 0x02, 0xb4, + 0x07, 0xe8, 0x61, 0x1b, 0xb9, 0x13, 0x00, 0xb0, + 0x0c, 0xe8, 0x62, 0x1b, 0xb9, 0x89, 0x02, 0xe8, + 0x48, 0x8c, 0xe8, 0x3b, 0xb1, 0xc6, 0x47, 0x01, + 0x2f, 0xe8, 0xc0, 0x1f, 0xe8, 0x75, 0x8c, 0xb8, + 0x2c, 0x01, 0xe8, 0x7d, 0x90, 0xb9, 0x44, 0x00, + 0xb0, 0x01, 0xb4, 0x0c, 0xe8, 0x36, 0x1b, 0xb0, + 0x05, 0xe8, 0x3a, 0x1b, 0xb0, 0x09, 0xe8, 0x3c, + 0x1b, 0xb0, 0x0d, 0xe8, 0x3e, 0x1b, 0xb0, 0x11, + 0xe8, 0x40, 0x1b, 0xb0, 0x15, 0xe8, 0x42, 0x1b, + 0xb0, 0x19, 0xe8, 0x44, 0x1b, 0xb0, 0x1d, 0xe8, + 0x46, 0x1b, 0xb0, 0x21, 0xe8, 0x48, 0x1b, 0xb0, + 0x25, 0xe8, 0x4a, 0x1b, 0xbb, 0x47, 0x33, 0xc7, + 0x47, 0x04, 0x7f, 0x02, 0xb0, 0x03, 0xe8, 0x68, + 0x8b, 0xe8, 0xe4, 0xb0, 0xc6, 0x07, 0x2a, 0xe8, + 0x6a, 0x1f, 0xe8, 0x1f, 0x8c, 0xb0, 0x06, 0xe8, + 0xc7, 0x8a, 0xb0, 0x05, 0xe8, 0xce, 0x8a, 0xc6, + 0x06, 0xab, 0xdb, 0x01, 0xc3, 0xbb, 0xd6, 0x2d, + 0xe8, 0xa2, 0x82, 0xe8, 0x31, 0x8c, 0xb9, 0x38, + 0x00, 0xb0, 0x10, 0xb4, 0x07, 0xe8, 0xcd, 0x1a, + 0xb0, 0x12, 0xe8, 0xd1, 0x1a, 0xb0, 0x14, 0xe8, + 0xd3, 0x1a, 0xb0, 0x16, 0xe8, 0xd5, 0x1a, 0xb0, + 0x18, 0xe8, 0xd7, 0x1a, 0xb0, 0x1a, 0xe8, 0xd9, + 0x1a, 0xb0, 0x1c, 0xe8, 0xdb, 0x1a, 0xb0, 0x1e, + 0xe8, 0xdd, 0x1a, 0xb9, 0x02, 0x00, 0xb0, 0x40, + 0xe8, 0xdc, 0x1a, 0xb9, 0x03, 0x00, 0xb0, 0x4a, + 0xe8, 0xdb, 0x1a, 0xc6, 0x06, 0x35, 0x33, 0x23, + 0xc6, 0x06, 0x36, 0x33, 0x32, 0xb8, 0xc7, 0x34, + 0xa3, 0x37, 0x33, 0xb8, 0xd4, 0x64, 0xa3, 0x39, + 0x33, 0xc6, 0x06, 0xe6, 0x1c, 0xd1, 0xb0, 0x01, + 0xb4, 0x03, 0xe8, 0x62, 0x8f, 0xbb, 0x47, 0x33, + 0xc7, 0x47, 0x04, 0x05, 0x02, 0xc7, 0x47, 0x06, + 0x06, 0x02, 0xb9, 0x04, 0x02, 0xe8, 0xd7, 0x8b, + 0xe8, 0xf8, 0x1b, 0xe8, 0x4e, 0x1b, 0xe8, 0xd8, + 0xae, 0xb0, 0x02, 0xe8, 0x3f, 0x8a, 0xb0, 0x03, + 0xe8, 0x3a, 0x8a, 0xb8, 0x03, 0x00, 0xe8, 0xc8, + 0x9e, 0xe8, 0xdc, 0x9e, 0xb8, 0x04, 0x00, 0xe8, + 0xbf, 0x9e, 0xe8, 0xd3, 0x9e, 0xb0, 0x02, 0xe8, + 0x86, 0x97, 0xc6, 0x06, 0x96, 0xdb, 0x01, 0xc3, + 0xbb, 0x2f, 0x3b, 0xe8, 0x87, 0x10, 0xe8, 0x86, + 0x8b, 0xb4, 0x02, 0xb0, 0x04, 0xe8, 0x36, 0x8f, + 0xb9, 0x05, 0x00, 0xb0, 0x03, 0xb4, 0x08, 0xe8, + 0x1b, 0x1a, 0xbb, 0x47, 0x33, 0xc7, 0x47, 0x02, + 0x75, 0x02, 0xb9, 0x73, 0x02, 0xe8, 0x12, 0x8b, + 0xb9, 0x29, 0x00, 0xb0, 0x0a, 0xb4, 0x06, 0xe8, + 0x03, 0x1a, 0xb0, 0x2f, 0xe8, 0x07, 0x1a, 0xb9, + 0x37, 0x00, 0xb0, 0x34, 0xe8, 0x06, 0x1a, 0x80, + 0x3e, 0xa8, 0xdb, 0x01, 0x74, 0x15, 0xbb, 0x47, + 0x33, 0xc7, 0x47, 0x02, 0x76, 0x02, 0xb9, 0x74, + 0x02, 0xe8, 0x09, 0x8b, 0xbb, 0x59, 0x3b, 0xe8, + 0x33, 0x10, 0xc3, 0xbb, 0x47, 0x33, 0xc7, 0x47, + 0x02, 0x7a, 0x02, 0xb9, 0x74, 0x02, 0xe8, 0xd1, + 0x8a, 0xb0, 0x04, 0xe8, 0xaf, 0x89, 0xe8, 0x5a, + 0x1b, 0xe8, 0x62, 0x1a, 0xe8, 0x3a, 0xae, 0xb8, + 0x02, 0x00, 0xe8, 0x34, 0x9e, 0xe8, 0x48, 0x9e, + 0xbb, 0x6c, 0x3b, 0xe8, 0x07, 0x10, 0xc6, 0x06, + 0xa9, 0xdb, 0x01, 0xc3, 0x80, 0x3e, 0xab, 0xdb, + 0x01, 0x75, 0x07, 0xbb, 0x0b, 0x3c, 0xe8, 0xf4, + 0x0f, 0xc3, 0xb9, 0x05, 0x00, 0xb0, 0x0b, 0xb4, + 0x06, 0xe8, 0x91, 0x19, 0xb9, 0x31, 0x00, 0xb0, + 0x15, 0xe8, 0x92, 0x19, 0xe8, 0xe0, 0x8a, 0xc7, + 0x06, 0xb1, 0x64, 0x63, 0x00, 0xb9, 0x78, 0x02, + 0xe8, 0x7f, 0x8a, 0xe8, 0x62, 0xaf, 0xc6, 0x47, + 0x05, 0x28, 0xe8, 0xa3, 0x1d, 0xc7, 0x06, 0xaf, + 0x64, 0xe9, 0x00, 0xc7, 0x06, 0xb1, 0x64, 0x8b, + 0x00, 0xb9, 0x79, 0x02, 0xe8, 0x86, 0x8a, 0xc6, + 0x06, 0xa8, 0xdb, 0x01, 0xb0, 0x2f, 0xe8, 0x9f, + 0x96, 0x80, 0x3e, 0xaa, 0xdb, 0x01, 0x74, 0x0b, + 0xc6, 0x06, 0xaa, 0xdb, 0x01, 0xbb, 0x8b, 0x3b, + 0xe8, 0x9a, 0x0f, 0xc3, 0xb9, 0x05, 0x00, 0xb0, + 0x03, 0xb4, 0x0c, 0xe8, 0x37, 0x19, 0xb9, 0x06, + 0x00, 0xb0, 0x09, 0xe8, 0x38, 0x19, 0xb9, 0x27, + 0x03, 0xe8, 0x1b, 0x8a, 0xe8, 0x11, 0xaf, 0xc6, + 0x07, 0x53, 0xe8, 0x53, 0x1d, 0xe8, 0x4c, 0x8a, + 0xb0, 0x49, 0xe8, 0x63, 0x96, 0xb0, 0x02, 0xe8, + 0xfb, 0x88, 0xb0, 0x03, 0xe8, 0xea, 0x88, 0xc6, + 0x06, 0xef, 0xdb, 0x01, 0xc3, 0xbb, 0x8e, 0x4e, + 0xe8, 0x5a, 0x0f, 0xc3, 0xe8, 0x67, 0x00, 0x80, + 0x3e, 0xd2, 0xdb, 0x01, 0x75, 0x07, 0xbb, 0xc3, + 0x50, 0xe8, 0x49, 0x0f, 0xc3, 0x80, 0x3e, 0xcb, + 0xdb, 0x01, 0x74, 0x07, 0xbb, 0x01, 0x51, 0xe8, + 0x3b, 0x0f, 0xc3, 0xbb, 0xe1, 0x50, 0xe8, 0x34, + 0x0f, 0xb9, 0x05, 0x00, 0xb0, 0x03, 0xb4, 0x04, + 0xe8, 0xd2, 0x18, 0xb0, 0x27, 0xe8, 0xd6, 0x18, + 0xc6, 0x06, 0xe6, 0x1c, 0xd0, 0xc6, 0x06, 0x35, + 0x33, 0x09, 0xc6, 0x06, 0x36, 0x33, 0x23, 0xb8, + 0x24, 0x51, 0xa3, 0x37, 0x33, 0xb8, 0xc4, 0x9d, + 0xa3, 0x39, 0x33, 0xb9, 0xd8, 0x02, 0xe8, 0x1e, + 0x8a, 0xe8, 0xd8, 0x89, 0xe8, 0xea, 0x87, 0xbb, + 0x17, 0x3d, 0xe8, 0x68, 0x80, 0xc6, 0x06, 0xd2, + 0xdb, 0x01, 0xe8, 0x10, 0x00, 0xc3, 0x80, 0x3e, + 0xd1, 0xdb, 0x01, 0x74, 0x07, 0xbb, 0xa6, 0x50, + 0xe8, 0xe2, 0x0e, 0x58, 0xc3, 0x80, 0x3e, 0xd2, + 0xdb, 0x00, 0x74, 0x4e, 0x80, 0x3e, 0xd3, 0xdb, + 0x00, 0x74, 0x47, 0x80, 0x3e, 0xd4, 0xdb, 0x00, + 0x74, 0x40, 0xb8, 0x01, 0x01, 0xe8, 0x7e, 0x8d, + 0xb9, 0x59, 0x00, 0xb0, 0x02, 0xb4, 0x03, 0xe8, + 0x63, 0x18, 0xbb, 0x47, 0x33, 0xc7, 0x07, 0xdb, + 0x02, 0xb0, 0x01, 0xe8, 0x7a, 0x88, 0xe8, 0x43, + 0x19, 0xe8, 0x3c, 0xae, 0xc6, 0x07, 0x46, 0xe8, + 0xc2, 0x1c, 0xb8, 0x01, 0x00, 0xe8, 0xc1, 0x9c, + 0xe8, 0xd5, 0x9c, 0xb0, 0x01, 0xe8, 0x25, 0x88, + 0xb0, 0x02, 0xe8, 0x14, 0x88, 0xb0, 0x03, 0xe8, + 0x0f, 0x88, 0xc3, 0xe8, 0x98, 0xff, 0x80, 0x3e, + 0xd3, 0xdb, 0x01, 0x75, 0x07, 0xbb, 0xc3, 0x50, + 0xe8, 0x7a, 0x0e, 0xc3, 0xbb, 0x38, 0x51, 0xe8, + 0x73, 0x0e, 0xb9, 0x05, 0x00, 0xb0, 0x03, 0xb4, + 0x04, 0xe8, 0x11, 0x18, 0xb0, 0x17, 0xe8, 0x15, + 0x18, 0xb9, 0xd9, 0x02, 0xe8, 0x19, 0x89, 0xe8, + 0x47, 0x87, 0xbb, 0x70, 0x3d, 0xe8, 0xc5, 0x7f, + 0xc6, 0x06, 0xd3, 0xdb, 0x01, 0xe8, 0x6d, 0xff, + 0xc3, 0xe8, 0x5a, 0xff, 0x80, 0x3e, 0xd4, 0xdb, + 0x01, 0x75, 0x07, 0xbb, 0xc3, 0x50, 0xe8, 0x3c, + 0x0e, 0xc3, 0xbb, 0x61, 0x51, 0xe8, 0x35, 0x0e, + 0xb9, 0x05, 0x00, 0xb0, 0x03, 0xb4, 0x04, 0xe8, + 0xd3, 0x17, 0xb0, 0x19, 0xe8, 0xd7, 0x17, 0xb9, + 0xda, 0x02, 0xe8, 0xdb, 0x88, 0xe8, 0x09, 0x87, + 0xbb, 0xd6, 0x3d, 0xe8, 0x87, 0x7f, 0xc6, 0x06, + 0xd4, 0xdb, 0x01, 0xe8, 0x2f, 0xff, 0xc3, 0xbb, + 0xfa, 0x4e, 0xe8, 0x08, 0x0e, 0xc3, 0xe8, 0x06, + 0x89, 0xe8, 0x94, 0xad, 0xc6, 0x47, 0x02, 0x40, + 0xe8, 0x19, 0x1c, 0xb9, 0x05, 0x00, 0xb0, 0x03, + 0xb4, 0x0a, 0xe8, 0x98, 0x17, 0xb9, 0x34, 0x00, + 0xb0, 0x0a, 0xe8, 0x99, 0x17, 0xb9, 0xc7, 0x02, + 0xe8, 0xa4, 0x88, 0x8b, 0x36, 0xaf, 0x64, 0x8b, + 0x3e, 0xb1, 0x64, 0xc6, 0x06, 0xc3, 0x64, 0x04, + 0xe8, 0xa8, 0xa3, 0xe8, 0x98, 0x86, 0xbb, 0x21, + 0x3b, 0xe8, 0x39, 0x7f, 0xbe, 0x2c, 0x01, 0xbf, + 0xbe, 0x00, 0xc6, 0x06, 0xc3, 0x64, 0x04, 0xe8, + 0x91, 0xa3, 0xb0, 0x40, 0xe8, 0xa9, 0x94, 0xb0, + 0x08, 0xe8, 0x41, 0x87, 0xbb, 0x47, 0x33, 0xc7, + 0x07, 0xc8, 0x02, 0xb0, 0x01, 0xe8, 0x70, 0x87, + 0xe8, 0x35, 0xad, 0xc6, 0x47, 0x02, 0x00, 0xe8, + 0xba, 0x1b, 0xb9, 0x0f, 0x00, 0xb0, 0x1a, 0xb4, + 0x02, 0xe8, 0x39, 0x17, 0xb0, 0x1c, 0xe8, 0x3d, + 0x17, 0xb9, 0x10, 0x00, 0xb0, 0x25, 0xe8, 0x3c, + 0x17, 0xbb, 0x47, 0x33, 0xc7, 0x07, 0xc9, 0x02, + 0xb0, 0x01, 0xe8, 0x43, 0x87, 0xe8, 0xbe, 0x17, + 0xe8, 0x3b, 0x86, 0xbb, 0x0d, 0x3c, 0xe8, 0xdc, + 0x7e, 0xb9, 0x55, 0x00, 0xb0, 0x02, 0xb4, 0x03, + 0xe8, 0x0a, 0x17, 0xbb, 0x47, 0x33, 0xc7, 0x07, + 0xca, 0x02, 0xb0, 0x01, 0xe8, 0x21, 0x87, 0xe8, + 0xea, 0x17, 0xb8, 0x01, 0x00, 0xe8, 0x71, 0x9b, + 0xe8, 0x85, 0x9b, 0xb0, 0x01, 0xe8, 0xd5, 0x86, + 0xb9, 0x51, 0x00, 0xba, 0xa0, 0x00, 0xb0, 0x02, + 0xb4, 0x04, 0xe8, 0xe8, 0x86, 0xb9, 0x3f, 0x00, + 0xba, 0xa8, 0x00, 0xb0, 0x03, 0xb4, 0x04, 0xe8, + 0xdb, 0x86, 0xb9, 0x69, 0x00, 0xba, 0xa0, 0x00, + 0xb0, 0x0a, 0xb4, 0x01, 0xe8, 0xce, 0x86, 0xc6, + 0x06, 0xcc, 0xdb, 0x01, 0xc3, 0xe8, 0xa0, 0xdd, + 0x80, 0x3e, 0xcd, 0xdb, 0x01, 0x75, 0x07, 0xbb, + 0x3d, 0x4f, 0xe8, 0x08, 0x0d, 0xc3, 0xe8, 0x06, + 0x88, 0xe8, 0x94, 0xac, 0xc6, 0x47, 0x01, 0x00, + 0xe8, 0x19, 0x1b, 0xb9, 0x05, 0x00, 0xb0, 0x03, + 0xb4, 0x07, 0xe8, 0x98, 0x16, 0xb0, 0x21, 0xe8, + 0xcd, 0x16, 0xb9, 0x18, 0x00, 0xb0, 0x0d, 0xe8, + 0x94, 0x16, 0xb0, 0x13, 0xe8, 0x96, 0x16, 0xb0, + 0x17, 0xe8, 0x9f, 0x16, 0xb0, 0x1a, 0xe8, 0xa8, + 0x16, 0xb0, 0x1d, 0xe8, 0xaa, 0x16, 0xb9, 0x17, + 0x00, 0xb0, 0x15, 0xe8, 0x86, 0x16, 0xb9, 0x4a, + 0x00, 0xb0, 0x19, 0xe8, 0x8c, 0x16, 0xb9, 0xcc, + 0x02, 0xe8, 0x56, 0x87, 0xe8, 0x49, 0xac, 0xc6, + 0x47, 0x01, 0x42, 0xe8, 0x8a, 0x1a, 0xe8, 0x83, + 0x87, 0xc6, 0x06, 0xcd, 0xdb, 0x01, 0xc3, 0xe8, + 0x2e, 0xdd, 0xb9, 0x05, 0x00, 0xb0, 0x03, 0xb4, + 0x03, 0xe8, 0x41, 0x16, 0xb9, 0x56, 0x00, 0xb0, + 0x0b, 0xe8, 0x42, 0x16, 0xb9, 0xd0, 0x02, 0xe8, + 0x54, 0x87, 0xb0, 0x44, 0xe8, 0x69, 0x93, 0xb0, + 0x37, 0xe8, 0x74, 0x93, 0xc3, 0xe8, 0x7f, 0x87, + 0xe8, 0x0d, 0xac, 0xc6, 0x47, 0x04, 0x00, 0xe8, + 0x92, 0x1a, 0xb9, 0x57, 0x00, 0xb0, 0x07, 0xb4, + 0x08, 0xe8, 0x11, 0x16, 0xb9, 0xd2, 0x02, 0xe8, + 0x25, 0x87, 0xb9, 0x05, 0x00, 0xb0, 0x03, 0xb4, + 0x09, 0xe8, 0x01, 0x16, 0xb9, 0x58, 0x00, 0xb0, + 0x0c, 0xe8, 0x02, 0x16, 0xb9, 0x57, 0x00, 0xb0, + 0x18, 0xe8, 0x01, 0x16, 0xb9, 0xd3, 0x02, 0xe8, + 0x05, 0x87, 0xbb, 0x2b, 0x50, 0xe8, 0x3d, 0x0c, + 0xe8, 0x3a, 0x8b, 0xb9, 0x59, 0x00, 0xb0, 0x04, + 0xb4, 0x08, 0xe8, 0xd8, 0x15, 0xb9, 0xd4, 0x02, + 0xe8, 0xc7, 0x86, 0xe8, 0xba, 0xab, 0xc6, 0x47, + 0x04, 0x44, 0xe8, 0xfb, 0x19, 0xe8, 0xf4, 0x86, + 0xbb, 0x3e, 0x50, 0xe8, 0x17, 0x0c, 0xb0, 0x44, + 0xe8, 0x05, 0x93, 0xc6, 0x06, 0xd0, 0xdb, 0x01, + 0xc3, 0xb9, 0x05, 0x00, 0xb0, 0x04, 0xb4, 0x03, + 0xe8, 0xaa, 0x15, 0xb9, 0x3f, 0x00, 0xb0, 0x0c, + 0xe8, 0xab, 0x15, 0xb9, 0xd6, 0x02, 0xe8, 0xbd, + 0x86, 0xbb, 0x8a, 0x50, 0xe8, 0xee, 0x0b, 0xb0, + 0x45, 0xe8, 0xdc, 0x92, 0xb0, 0x46, 0xe8, 0xc7, + 0x92, 0xc3, 0xb9, 0x05, 0x00, 0xb0, 0x04, 0xb4, + 0x0e, 0xe8, 0x81, 0x15, 0xb9, 0x13, 0x00, 0xb0, + 0x0e, 0xe8, 0x82, 0x15, 0xb9, 0x19, 0x03, 0xe8, + 0x86, 0x86, 0xbb, 0x18, 0x52, 0xe8, 0xc5, 0x0b, + 0xb0, 0x3c, 0xe8, 0xb3, 0x92, 0xc6, 0x06, 0xd6, + 0xdb, 0x01, 0xc3, 0x80, 0x3e, 0xd6, 0xdb, 0x02, + 0x74, 0x07, 0xbb, 0x4f, 0x52, 0xe8, 0xad, 0x0b, + 0xc3, 0xb9, 0x05, 0x00, 0xb0, 0x04, 0xb4, 0x0e, + 0xe8, 0x4a, 0x15, 0xb0, 0x19, 0xe8, 0x4e, 0x15, + 0xb9, 0x22, 0x03, 0xe8, 0x52, 0x86, 0xbb, 0x72, + 0x52, 0xe8, 0x91, 0x0b, 0xb0, 0x3e, 0xe8, 0x7f, + 0x92, 0xb0, 0x4a, 0xe8, 0x6a, 0x92, 0xb0, 0x41, + 0xe8, 0x65, 0x92, 0xc3, 0xb0, 0x46, 0xe8, 0x93, + 0x92, 0x73, 0x39, 0xe8, 0x79, 0x86, 0xe8, 0x07, + 0xab, 0xc6, 0x07, 0x00, 0xe8, 0x8d, 0x19, 0xb9, + 0x05, 0x00, 0xb0, 0x03, 0xb4, 0x04, 0xe8, 0x0c, + 0x15, 0xb0, 0x12, 0xe8, 0x17, 0x15, 0xb9, 0x0d, + 0x00, 0xb0, 0x0c, 0xe8, 0x08, 0x15, 0xb9, 0x23, + 0x03, 0xe8, 0x21, 0x86, 0xb0, 0x07, 0xe8, 0xdc, + 0x84, 0xb0, 0x46, 0xe8, 0x3a, 0x92, 0xb0, 0x47, + 0xe8, 0x25, 0x92, 0xc3, 0xbb, 0xad, 0x53, 0xe8, + 0x3b, 0x0b, 0xc3, 0xb9, 0x05, 0x00, 0xb0, 0x04, + 0xb4, 0x0e, 0xe8, 0xd8, 0x14, 0xb0, 0x16, 0xe8, + 0xdc, 0x14, 0xb9, 0x24, 0x03, 0xe8, 0xe0, 0x85, + 0xbb, 0x8b, 0x52, 0xe8, 0x1f, 0x0b, 0xc3, 0xb9, + 0x05, 0x00, 0xb0, 0x03, 0xb4, 0x04, 0xe8, 0xbc, + 0x14, 0xb0, 0x10, 0xe8, 0xc0, 0x14, 0xb9, 0xbf, + 0x02, 0xe8, 0xc4, 0x85, 0x80, 0x3e, 0xc8, 0xdb, + 0x01, 0x74, 0x07, 0xbb, 0x80, 0x4d, 0xe8, 0xfc, + 0x0a, 0xc3, 0x80, 0x3e, 0xc6, 0xdb, 0x00, 0x74, + 0x22, 0xc6, 0x06, 0xc6, 0xdb, 0x00, 0x80, 0x3e, + 0xc5, 0xdb, 0x01, 0x75, 0x15, 0xbb, 0x47, 0x33, + 0xc7, 0x07, 0xbd, 0x02, 0xb0, 0x01, 0xe8, 0xae, + 0x84, 0xe8, 0x70, 0x15, 0xbb, 0xa6, 0x4d, 0xe8, + 0xd3, 0x0a, 0xc3, 0x80, 0x3e, 0xc5, 0xdb, 0x01, + 0x74, 0x07, 0xbb, 0x5b, 0x4d, 0xe8, 0xc5, 0x0a, + 0xc3, 0x80, 0x3e, 0xc7, 0xdb, 0x01, 0x74, 0x06, + 0xbb, 0x93, 0x4d, 0xe8, 0xb7, 0x0a, 0xbb, 0x47, + 0x33, 0xc7, 0x07, 0xbe, 0x02, 0xb0, 0x01, 0xe8, + 0x7d, 0x84, 0xe8, 0x3f, 0x15, 0xc6, 0x06, 0xc6, + 0xdb, 0x01, 0x80, 0x3e, 0xc7, 0xdb, 0x01, 0x74, + 0x0e, 0xe8, 0x3f, 0x83, 0xbb, 0x2c, 0x39, 0xe8, + 0x03, 0x7c, 0xc6, 0x06, 0xc7, 0xdb, 0x01, 0xc3, + 0xb9, 0x05, 0x00, 0xb0, 0x03, 0xb4, 0x06, 0xe8, + 0x2b, 0x14, 0xb9, 0x5b, 0x00, 0xb0, 0x0c, 0xe8, + 0x2c, 0x14, 0xb9, 0xc2, 0x02, 0xe8, 0x30, 0x85, + 0xb0, 0x36, 0xe8, 0x63, 0x91, 0xc6, 0x06, 0xc8, + 0xdb, 0x01, 0xc3, 0x80, 0x3e, 0xc6, 0xdb, 0x01, + 0x74, 0x07, 0xbb, 0xa5, 0x4e, 0xe8, 0x5d, 0x0a, + 0xc3, 0x80, 0x3e, 0xca, 0xdb, 0x01, 0x75, 0x07, + 0xbb, 0xe6, 0x4d, 0xe8, 0x4f, 0x0a, 0xc3, 0xb9, + 0x05, 0x00, 0xb0, 0x03, 0xb4, 0x04, 0xe8, 0xec, + 0x13, 0xb0, 0x18, 0xe8, 0xf7, 0x13, 0xb9, 0x5a, + 0x00, 0xb0, 0x12, 0xe8, 0xe8, 0x13, 0xb9, 0xc3, + 0x02, 0xe8, 0xec, 0x84, 0xb0, 0x3d, 0xe8, 0x0f, + 0x91, 0xc6, 0x06, 0xca, 0xdb, 0x01, 0xc3, 0x80, + 0x3e, 0xc6, 0xdb, 0x01, 0x74, 0x07, 0xbb, 0xa5, + 0x4e, 0xe8, 0x19, 0x0a, 0xc3, 0x80, 0x3e, 0xcb, + 0xdb, 0x01, 0x75, 0x07, 0xbb, 0x32, 0x4e, 0xe8, + 0x0b, 0x0a, 0xc3, 0xbb, 0x05, 0x4e, 0xe8, 0x04, + 0x0a, 0xb9, 0x05, 0x00, 0xb0, 0x03, 0xb4, 0x04, + 0xe8, 0xa2, 0x13, 0xb0, 0x1b, 0xe8, 0xa6, 0x13, + 0xb9, 0xc4, 0x02, 0xe8, 0xaa, 0x84, 0xc6, 0x06, + 0xcb, 0xdb, 0x01, 0xc3, 0xbb, 0x58, 0x4e, 0xe8, + 0xe3, 0x09, 0xc3, 0xb9, 0x05, 0x00, 0xb0, 0x03, + 0xb4, 0x05, 0xe8, 0x80, 0x13, 0xb9, 0x18, 0x00, + 0xb0, 0x0a, 0xe8, 0x81, 0x13, 0xb9, 0x1e, 0x03, + 0xe8, 0x85, 0x84, 0xb9, 0x3f, 0x00, 0xb0, 0x0b, + 0xb4, 0x05, 0xe8, 0x68, 0x13, 0xb9, 0x13, 0x00, + 0xb0, 0x14, 0xe8, 0x69, 0x13, 0xbb, 0x47, 0x33, + 0xc7, 0x07, 0x1f, 0x03, 0xb0, 0x01, 0xe8, 0x7e, + 0x83, 0x50, 0x52, 0xbe, 0x32, 0x00, 0xbf, 0xaa, + 0x00, 0xc6, 0x06, 0xc3, 0x64, 0x01, 0xe8, 0x72, + 0x9f, 0x5a, 0x58, 0xbf, 0xc7, 0x32, 0xb3, 0x1b, + 0x48, 0xf6, 0xe3, 0x03, 0xf8, 0xe8, 0x43, 0x7c, + 0xbb, 0x47, 0x33, 0xc7, 0x07, 0x20, 0x03, 0xb0, + 0x01, 0xe8, 0x53, 0x83, 0xe8, 0x15, 0x14, 0xe8, + 0x7b, 0x88, 0xb9, 0x25, 0x03, 0xe8, 0x12, 0x84, + 0xe8, 0xb0, 0x14, 0xbe, 0x32, 0x00, 0xbf, 0xaa, + 0x00, 0xc6, 0x06, 0xc3, 0x64, 0x03, 0xe8, 0x3a, + 0x9f, 0xbb, 0x49, 0x53, 0xe8, 0x5e, 0x09, 0xc7, + 0x06, 0xaf, 0x64, 0x69, 0x00, 0xc7, 0x06, 0xb1, + 0x64, 0x9d, 0x00, 0xc6, 0x06, 0xcb, 0x64, 0x00, + 0xc6, 0x06, 0xcc, 0x64, 0x00, 0xc6, 0x06, 0xdc, + 0x64, 0x01, 0xe8, 0x64, 0xa7, 0xb9, 0x03, 0x00, + 0xe8, 0x30, 0x1a, 0xbb, 0x47, 0x33, 0xc7, 0x07, + 0xaa, 0x03, 0xc7, 0x06, 0xf3, 0xb4, 0x0b, 0x00, + 0xe8, 0xea, 0x15, 0xe8, 0x61, 0x80, 0xbb, 0x09, + 0x84, 0xe8, 0x91, 0x7a, 0xbb, 0x47, 0x33, 0xc7, + 0x07, 0xab, 0x03, 0xb9, 0xae, 0x03, 0xe8, 0xc1, + 0x83, 0xbb, 0x47, 0x33, 0xc7, 0x07, 0xab, 0x03, + 0xc7, 0x47, 0x02, 0xa7, 0x03, 0xb9, 0xaf, 0x03, + 0xe8, 0xaf, 0x83, 0xbb, 0x47, 0x33, 0xc7, 0x07, + 0xac, 0x03, 0xc7, 0x47, 0x02, 0xa8, 0x03, 0xb9, + 0xb0, 0x03, 0xe8, 0x9d, 0x83, 0xbb, 0x47, 0x33, + 0xc7, 0x07, 0xad, 0x03, 0xc7, 0x47, 0x02, 0xa9, + 0x03, 0xb9, 0xb1, 0x03, 0xe8, 0x8b, 0x83, 0xbb, + 0x47, 0x33, 0xc7, 0x07, 0x00, 0x00, 0xe8, 0xab, + 0x83, 0xbb, 0x47, 0x33, 0xc7, 0x07, 0xb2, 0x03, + 0xb0, 0x01, 0xe8, 0xd5, 0x82, 0xe8, 0xff, 0x7f, + 0xbb, 0x4f, 0x84, 0xe8, 0x2f, 0x7a, 0xbb, 0x47, + 0x33, 0xc7, 0x07, 0xb2, 0x03, 0xb0, 0x01, 0xe8, + 0xc0, 0x82, 0xe8, 0xea, 0x7f, 0xbb, 0xc7, 0x87, + 0xe8, 0x1a, 0x7a, 0xb9, 0x18, 0x00, 0xb0, 0x07, + 0xb4, 0x05, 0xe8, 0x48, 0x12, 0xbb, 0x47, 0x33, + 0xc7, 0x07, 0xb4, 0x03, 0xb9, 0xb3, 0x03, 0xe8, + 0x40, 0x83, 0xe8, 0xce, 0x13, 0xe8, 0xd6, 0x12, + 0xc7, 0x06, 0xaf, 0x64, 0xc6, 0x00, 0xc7, 0x06, + 0xb1, 0x64, 0xba, 0x00, 0xc6, 0x06, 0xcd, 0x64, + 0x00, 0xc6, 0x06, 0xdc, 0x64, 0x00, 0xc6, 0x06, + 0xcb, 0x64, 0x01, 0xe8, 0x93, 0xa6, 0xc7, 0x06, + 0xf3, 0xb4, 0x28, 0x00, 0xe8, 0x26, 0x15, 0xe8, + 0xe5, 0x7e, 0xbb, 0x90, 0x88, 0xe8, 0xcd, 0x79, + 0xe8, 0x0a, 0x7f, 0xbb, 0x2f, 0x8a, 0xe8, 0xc4, + 0x79, 0xbb, 0x47, 0x33, 0xc7, 0x07, 0x9b, 0x03, + 0xb0, 0x01, 0xe8, 0x55, 0x82, 0xe8, 0xc7, 0x7e, + 0xbb, 0xa7, 0x8a, 0xe8, 0xaf, 0x79, 0xbe, 0xed, + 0x00, 0xbf, 0xba, 0x00, 0xe8, 0x0c, 0x9e, 0xbe, + 0xed, 0x00, 0xbf, 0xb1, 0x00, 0xe8, 0x03, 0x9e, + 0xbe, 0xc0, 0x00, 0xbf, 0xb1, 0x00, 0xe8, 0xfa, + 0x9d, 0xbb, 0x47, 0x33, 0xc7, 0x07, 0xb5, 0x03, + 0xb0, 0x01, 0xe8, 0x25, 0x82, 0xc6, 0x06, 0x45, + 0x33, 0xe7, 0xb0, 0x01, 0xbe, 0xb6, 0x03, 0xbb, + 0xf6, 0x8a, 0xe8, 0xaa, 0x7d, 0xb9, 0x20, 0x00, + 0xb0, 0x05, 0xb4, 0x09, 0xe8, 0xa6, 0x11, 0xb9, + 0x28, 0x00, 0xb0, 0x0e, 0xe8, 0xa7, 0x11, 0xbb, + 0x47, 0x33, 0xc7, 0x07, 0xb7, 0x03, 0xb9, 0xb8, + 0x03, 0xe8, 0x96, 0x82, 0xe8, 0x24, 0x13, 0xb9, + 0x0b, 0x00, 0xe8, 0xd6, 0x18, 0xe8, 0x44, 0x69, + 0xbb, 0x47, 0x33, 0xc7, 0x07, 0xb9, 0x03, 0xc7, + 0x06, 0xf3, 0xb4, 0x27, 0x00, 0xe8, 0x84, 0x14, + 0xc6, 0x06, 0x45, 0x33, 0xe3, 0xb0, 0x01, 0xbe, + 0xb9, 0x03, 0xbb, 0x4d, 0x8b, 0xe8, 0x5f, 0x7d, + 0xb9, 0x05, 0x00, 0xb0, 0x0f, 0xb4, 0x08, 0xe8, + 0x5b, 0x11, 0xbb, 0x47, 0x33, 0xc7, 0x07, 0xba, + 0x03, 0xb0, 0x01, 0xe8, 0xb4, 0x81, 0xb0, 0x01, + 0xbe, 0xbb, 0x03, 0xbb, 0x7a, 0x8b, 0xe8, 0x3e, + 0x7d, 0x8b, 0x16, 0xb3, 0x32, 0xbe, 0x00, 0xfa, + 0xb0, 0xfe, 0xe8, 0x5d, 0x0e, 0xff, 0x36, 0xa8, + 0x64, 0xc6, 0x06, 0xa8, 0x64, 0x00, 0xb8, 0x64, + 0x00, 0xe8, 0x88, 0x86, 0xe8, 0x62, 0x69, 0x8f, + 0x06, 0xa8, 0x64, 0xb9, 0x02, 0x00, 0xe8, 0x6a, + 0x18, 0xbb, 0x7c, 0xe4, 0xe8, 0xf7, 0x6c, 0x2e, + 0x80, 0x3e, 0xd7, 0x00, 0x80, 0x73, 0xf8, 0x2e, + 0x80, 0x3e, 0xd7, 0x00, 0x00, 0x74, 0xf0, 0xe9, + 0xad, 0x68, 0xb0, 0x03, 0xe8, 0x49, 0x8e, 0xb0, + 0x04, 0xe8, 0x34, 0x8e, 0xb0, 0x23, 0xe8, 0x2f, + 0x8e, 0xbb, 0x68, 0x34, 0xe8, 0x46, 0x07, 0xc3, + 0xb0, 0x04, 0xe8, 0x33, 0x8e, 0xb0, 0x05, 0xe8, + 0x1e, 0x8e, 0xbb, 0x90, 0x34, 0xe8, 0x35, 0x07, + 0xc3, 0xa1, 0xf3, 0xb4, 0x3d, 0x0f, 0x00, 0x74, + 0x0f, 0xbb, 0xce, 0x38, 0x3d, 0x10, 0x00, 0x74, + 0x03, 0xbb, 0xa7, 0x38, 0xe8, 0x1e, 0x07, 0xc3, + 0xbe, 0x9c, 0x00, 0xbf, 0xb4, 0x00, 0xc6, 0x06, + 0xc3, 0x64, 0x03, 0xe8, 0xe5, 0x9c, 0xb9, 0x05, + 0x00, 0xb0, 0x03, 0xb4, 0x07, 0xe8, 0xad, 0x10, + 0xb9, 0x26, 0x00, 0xb0, 0x10, 0xe8, 0xae, 0x10, + 0xb0, 0x16, 0xe8, 0xb0, 0x10, 0xb9, 0x66, 0x02, + 0xe8, 0x8c, 0x81, 0xb9, 0x05, 0x00, 0xb0, 0x03, + 0xb4, 0x07, 0xe8, 0x90, 0x10, 0xb9, 0x2c, 0x00, + 0xb0, 0x0a, 0xe8, 0x91, 0x10, 0xb9, 0x14, 0x00, + 0xb0, 0x1a, 0xe8, 0x90, 0x10, 0xb9, 0x67, 0x02, + 0xe8, 0x6f, 0x81, 0xe8, 0x0d, 0x12, 0xc7, 0x06, + 0xf3, 0xb4, 0x11, 0x00, 0xc6, 0x06, 0xdc, 0x1c, + 0x01, 0xe8, 0x93, 0x12, 0xb9, 0x40, 0x00, 0xb4, + 0x07, 0xb0, 0x01, 0xe8, 0x5f, 0x10, 0xb0, 0x15, + 0xe8, 0x63, 0x10, 0xb0, 0x2a, 0xe8, 0x65, 0x10, + 0xb0, 0x3f, 0xe8, 0x67, 0x10, 0xb9, 0x69, 0x02, + 0xc6, 0x06, 0xdc, 0x1c, 0x01, 0xc7, 0x06, 0x1f, + 0xc4, 0x00, 0x00, 0xe8, 0x87, 0x10, 0xe8, 0x9b, + 0x10, 0xe8, 0x0f, 0x10, 0xc7, 0x06, 0x23, 0xc4, + 0x05, 0x00, 0xc7, 0x06, 0x3b, 0x33, 0x00, 0x00, + 0xc7, 0x06, 0x3d, 0x33, 0x14, 0x00, 0xc6, 0x06, + 0xce, 0x00, 0x02, 0xe8, 0xe7, 0x82, 0x73, 0x3d, + 0xb9, 0x40, 0x00, 0xb4, 0x07, 0xb0, 0x01, 0xe8, + 0x13, 0x10, 0xb9, 0x6a, 0x02, 0xe8, 0x02, 0x81, + 0xb0, 0x05, 0xe8, 0xf0, 0x7f, 0xe8, 0xf0, 0xa5, + 0xc6, 0x07, 0x00, 0xe8, 0x32, 0x14, 0xb9, 0x1f, + 0x00, 0xb0, 0x01, 0xb4, 0x07, 0xe8, 0xf5, 0x0f, + 0xb9, 0x6b, 0x02, 0xe8, 0xe4, 0x80, 0xe8, 0x69, + 0x00, 0xb0, 0x2a, 0xe8, 0x22, 0x8d, 0xbb, 0x89, + 0x39, 0xe8, 0x39, 0x06, 0xc3, 0x51, 0xe8, 0x59, + 0x00, 0x59, 0x80, 0x3e, 0xcf, 0x00, 0x00, 0x74, + 0x20, 0x80, 0x3e, 0xcf, 0x00, 0x04, 0x74, 0x0d, + 0xbb, 0x32, 0x39, 0x83, 0xf9, 0x05, 0x74, 0x08, + 0xbb, 0xff, 0x38, 0xeb, 0x03, 0xbb, 0xdb, 0x38, + 0xc6, 0x06, 0xcf, 0x00, 0x00, 0xe8, 0x0d, 0x06, + 0xc3, 0xfe, 0x06, 0xa6, 0xdb, 0xa0, 0xa6, 0xdb, + 0xbb, 0xae, 0x39, 0x3c, 0x01, 0x74, 0x1f, 0xbb, + 0xf6, 0x39, 0x3c, 0x02, 0x74, 0x18, 0xbb, 0x28, + 0x3a, 0x3c, 0x03, 0x74, 0x11, 0xbb, 0x5a, 0x3a, + 0x3c, 0x04, 0x74, 0x0a, 0xbb, 0x85, 0x3a, 0x3c, + 0x05, 0x74, 0x03, 0xbb, 0xb7, 0x3a, 0xe8, 0xdc, + 0x05, 0xc3, 0xe8, 0x16, 0x11, 0xc7, 0x06, 0xf3, + 0xb4, 0x0f, 0x00, 0xe8, 0x86, 0x12, 0xc7, 0x06, + 0xaf, 0x64, 0x9c, 0x00, 0xc7, 0x06, 0xb1, 0x64, + 0xb4, 0x00, 0xc6, 0x06, 0xc3, 0x64, 0x03, 0xc6, + 0x06, 0xcb, 0x64, 0x01, 0xc6, 0x06, 0xcd, 0x64, + 0x01, 0xb9, 0x05, 0x00, 0xb0, 0x05, 0xb4, 0x07, + 0xe8, 0x52, 0x0f, 0xb9, 0x26, 0x00, 0xb0, 0x0e, + 0xe8, 0x53, 0x0f, 0xb9, 0x26, 0x00, 0xb0, 0x14, + 0xe8, 0x52, 0x0f, 0xb9, 0x05, 0x00, 0xb0, 0x19, + 0xe8, 0x51, 0x0f, 0xb9, 0x68, 0x02, 0xe8, 0x47, + 0x80, 0xc3, 0xa1, 0xf3, 0xb4, 0x3d, 0x0d, 0x00, + 0x74, 0x07, 0xbb, 0x58, 0x3c, 0xe8, 0x7d, 0x05, + 0xc3, 0xe8, 0x7b, 0x80, 0xbe, 0xac, 0x00, 0xbf, + 0xb5, 0x00, 0xc6, 0x06, 0xc3, 0x64, 0x01, 0xe8, + 0x41, 0x9b, 0xb9, 0x1a, 0x00, 0xb0, 0x13, 0xb4, + 0x07, 0xe8, 0x09, 0x0f, 0xb0, 0x1e, 0xe8, 0x14, + 0x0f, 0xb0, 0x29, 0xe8, 0x1d, 0x0f, 0xb0, 0x34, + 0xe8, 0x26, 0x0f, 0xb0, 0x3f, 0xe8, 0x2f, 0x0f, + 0xb9, 0x38, 0x00, 0xb0, 0x17, 0xe8, 0xf6, 0x0e, + 0xb0, 0x22, 0xe8, 0xff, 0x0e, 0xb0, 0x2d, 0xe8, + 0x08, 0x0f, 0xb0, 0x38, 0xe8, 0x11, 0x0f, 0xb9, + 0x95, 0x02, 0xe8, 0xcd, 0x7f, 0xbe, 0x14, 0x77, + 0xbb, 0x80, 0x3c, 0xc6, 0x06, 0xdc, 0x1c, 0x01, + 0xe8, 0xe2, 0x00, 0xb9, 0x38, 0x00, 0xb0, 0x0a, + 0xb4, 0x07, 0xe8, 0xc0, 0x0e, 0xb0, 0x15, 0xe8, + 0xc4, 0x0e, 0xb9, 0x08, 0x00, 0xb0, 0x30, 0xe8, + 0xc3, 0x0e, 0xb9, 0x38, 0x00, 0xb0, 0x75, 0xe8, + 0xc2, 0x0e, 0xb0, 0x80, 0xe8, 0xc4, 0x0e, 0xb0, + 0x8b, 0xe8, 0xc6, 0x0e, 0xb0, 0x96, 0xe8, 0xc8, + 0x0e, 0xb0, 0xa1, 0xe8, 0xca, 0x0e, 0xb0, 0xac, + 0xe8, 0xcc, 0x0e, 0xb0, 0xb7, 0xe8, 0xce, 0x0e, + 0x83, 0x2e, 0xb1, 0x64, 0x14, 0xbb, 0x47, 0x33, + 0xc7, 0x47, 0x02, 0x97, 0x02, 0xb9, 0x96, 0x02, + 0xe8, 0x6f, 0x7f, 0xe8, 0x18, 0x0f, 0xe8, 0x5f, + 0xa4, 0xc6, 0x47, 0x01, 0x31, 0xe8, 0xa0, 0x12, + 0xbe, 0xf5, 0x76, 0xbb, 0x9a, 0x3c, 0xc6, 0x06, + 0xdc, 0x1c, 0x01, 0xe8, 0x77, 0x00, 0xc7, 0x06, + 0xaf, 0x64, 0xa2, 0x00, 0xc7, 0x06, 0xb1, 0x64, + 0xb8, 0x00, 0xb9, 0x1a, 0x00, 0xb0, 0x06, 0xb4, + 0x07, 0xe8, 0x49, 0x0e, 0xb0, 0x11, 0xe8, 0x54, + 0x0e, 0xb9, 0x38, 0x00, 0xb0, 0x0a, 0xe8, 0x45, + 0x0e, 0xb0, 0x15, 0xe8, 0x4e, 0x0e, 0xb9, 0x13, + 0x00, 0xb0, 0x1b, 0xe8, 0x4d, 0x0e, 0xb9, 0x18, + 0x00, 0xb0, 0x26, 0xe8, 0x4c, 0x0e, 0xb9, 0x17, + 0x00, 0xb0, 0x2c, 0xe8, 0x4b, 0x0e, 0xb9, 0x98, + 0x02, 0xe8, 0x0e, 0x7f, 0xe8, 0xac, 0x0f, 0xbb, + 0x47, 0x33, 0xc7, 0x47, 0x02, 0x99, 0x02, 0xb0, + 0x02, 0xe8, 0x2c, 0x7e, 0xe8, 0x5e, 0x83, 0xbb, + 0xbc, 0x3c, 0xe8, 0x58, 0x04, 0xe8, 0x55, 0x83, + 0xe8, 0x29, 0x7f, 0xe8, 0x4f, 0x83, 0xbb, 0xea, + 0x3c, 0xe8, 0x49, 0x04, 0xb0, 0x25, 0xe8, 0x37, + 0x8b, 0xe8, 0x31, 0x01, 0xc3, 0x06, 0x53, 0x56, + 0xb8, 0x00, 0xa0, 0x8e, 0xc0, 0xe8, 0x25, 0x00, + 0xb8, 0x00, 0xa0, 0xb9, 0x00, 0x7d, 0xe8, 0xd7, + 0x10, 0x5e, 0x5b, 0xe8, 0x27, 0x08, 0xe8, 0x23, + 0x00, 0xb8, 0x96, 0x00, 0xe8, 0x25, 0x83, 0xe8, + 0x0b, 0x00, 0xe8, 0x2a, 0x91, 0xe8, 0x80, 0x0c, + 0xe8, 0x11, 0x00, 0x07, 0xc3, 0x80, 0x3e, 0xad, + 0x64, 0x01, 0x75, 0x04, 0xe8, 0x7d, 0x10, 0xc3, + 0xe8, 0x93, 0x10, 0xc3, 0x80, 0x3e, 0xad, 0x64, + 0x01, 0x75, 0x04, 0xe8, 0x7b, 0x10, 0xc3, 0xe8, + 0x91, 0x10, 0xe8, 0x5c, 0x10, 0xc3, 0xbb, 0x84, + 0x49, 0xe8, 0xe9, 0x03, 0xc3, 0xbb, 0xd1, 0x49, + 0xe8, 0xe2, 0x03, 0xc6, 0x06, 0xb5, 0xdb, 0x01, + 0xc3, 0x83, 0x3e, 0xf3, 0xb4, 0x24, 0x74, 0x07, + 0xbb, 0xa9, 0x52, 0xe8, 0xcf, 0x03, 0xc3, 0x80, + 0x3e, 0xf1, 0xdb, 0x01, 0x75, 0x07, 0xbb, 0xf6, + 0x52, 0xe8, 0xc1, 0x03, 0xc3, 0xc6, 0x06, 0xf1, + 0xdb, 0x01, 0xe8, 0xba, 0x7e, 0xbe, 0x66, 0x00, + 0xbf, 0xc3, 0x00, 0xc6, 0x06, 0xc3, 0x64, 0x02, + 0xe8, 0x80, 0x99, 0xb9, 0x05, 0x00, 0xb0, 0x03, + 0xb4, 0x05, 0xe8, 0x48, 0x0d, 0xb9, 0x4b, 0x00, + 0xb0, 0x0c, 0xe8, 0x49, 0x0d, 0xb9, 0x1a, 0x03, + 0xe8, 0x54, 0x7e, 0xb8, 0x01, 0x00, 0xe8, 0xc7, + 0x91, 0x53, 0xe8, 0x88, 0x82, 0xbb, 0x46, 0x67, + 0xe8, 0x07, 0xa3, 0x83, 0xc3, 0x03, 0xc7, 0x07, + 0x00, 0x00, 0xc7, 0x47, 0x04, 0x00, 0x00, 0x53, + 0xbe, 0x97, 0x00, 0xbf, 0xc5, 0x00, 0xc6, 0x06, + 0xc3, 0x64, 0x02, 0xe8, 0x3d, 0x99, 0xb9, 0x1b, + 0x03, 0xe8, 0xfe, 0x7d, 0xc7, 0x06, 0xaf, 0x64, + 0xba, 0x00, 0xff, 0x06, 0xb1, 0x64, 0xe8, 0x2b, + 0x7e, 0xbe, 0xdc, 0x00, 0xbf, 0xc6, 0x00, 0xc6, + 0x06, 0xc3, 0x64, 0x04, 0xe8, 0x1c, 0x99, 0x5b, + 0xc7, 0x47, 0x04, 0xc8, 0x00, 0x5b, 0xc7, 0x07, + 0x01, 0x00, 0xb0, 0x02, 0xe8, 0xc6, 0x7c, 0xbb, + 0xa9, 0x58, 0xe8, 0xa0, 0x74, 0xb0, 0x01, 0xb4, + 0x02, 0xb9, 0x0e, 0x01, 0xba, 0xc1, 0x00, 0xe8, + 0xd3, 0x7c, 0xb0, 0x03, 0xb4, 0x01, 0xb9, 0xfe, + 0x00, 0xba, 0xc1, 0x00, 0xe8, 0xc6, 0x7c, 0xc6, + 0x06, 0xd7, 0xdb, 0x01, 0xc3, 0xe8, 0x06, 0x82, + 0xff, 0x06, 0xea, 0xdb, 0xa1, 0xea, 0xdb, 0x3d, + 0x07, 0x00, 0x73, 0x3b, 0x2d, 0x02, 0x00, 0xbb, + 0x35, 0x60, 0xd1, 0xe0, 0x03, 0xd8, 0xff, 0x36, + 0xf3, 0xb4, 0x53, 0xb9, 0x0b, 0x00, 0xe8, 0xe2, + 0x13, 0xe8, 0x85, 0x02, 0x5b, 0xff, 0x17, 0xc6, + 0x06, 0xdc, 0x1c, 0x02, 0x8f, 0x06, 0xf3, 0xb4, + 0xb9, 0x06, 0x00, 0xe8, 0xcd, 0x13, 0x83, 0x3e, + 0xf3, 0xb4, 0x0b, 0x75, 0x07, 0x83, 0x3e, 0xec, + 0xdb, 0x01, 0x74, 0x03, 0xe8, 0x98, 0x0e, 0xc3, + 0xbb, 0x47, 0x33, 0xc7, 0x07, 0xda, 0x03, 0xc7, + 0x47, 0x02, 0xdb, 0x03, 0xc7, 0x06, 0xf3, 0xb4, + 0x22, 0x00, 0xe8, 0x67, 0x0f, 0xbb, 0x47, 0x33, + 0xc7, 0x07, 0xda, 0x03, 0xc7, 0x47, 0x02, 0xdb, + 0x03, 0xb0, 0x01, 0xe8, 0xac, 0x7c, 0xc6, 0x06, + 0x45, 0x33, 0xd9, 0xc6, 0x06, 0x46, 0x33, 0xd0, + 0xb0, 0x01, 0xb4, 0x02, 0xbe, 0xdc, 0x03, 0xbf, + 0xdd, 0x03, 0xbb, 0x60, 0x6f, 0xe8, 0x9e, 0x76, + 0xbb, 0x47, 0x33, 0xc7, 0x07, 0xde, 0x03, 0xc7, + 0x47, 0x02, 0xdf, 0x03, 0xb0, 0x01, 0xe8, 0x81, + 0x7c, 0xe8, 0xba, 0x0c, 0xc3, 0xbb, 0x47, 0x33, + 0xc7, 0x47, 0x02, 0x77, 0x03, 0xc7, 0x47, 0x04, + 0x78, 0x03, 0xc7, 0x06, 0xf3, 0xb4, 0x1e, 0x00, + 0xe8, 0x11, 0x0f, 0xbb, 0x47, 0x33, 0xc7, 0x47, + 0x02, 0x77, 0x03, 0xc7, 0x47, 0x04, 0x78, 0x03, + 0xb0, 0x02, 0xe8, 0x55, 0x7c, 0xc6, 0x06, 0x45, + 0x33, 0xd9, 0xb0, 0x02, 0xbe, 0x79, 0x03, 0xbb, + 0xb8, 0x6f, 0xe8, 0xda, 0x77, 0xb9, 0x1a, 0x00, + 0xb0, 0x03, 0xb4, 0x0a, 0xe8, 0xd6, 0x0b, 0xbb, + 0x47, 0x33, 0xc7, 0x47, 0x02, 0x7b, 0x03, 0xc7, + 0x47, 0x04, 0x7c, 0x03, 0xb0, 0x03, 0xe8, 0x29, + 0x7c, 0xc6, 0x06, 0x45, 0x33, 0xd0, 0xc6, 0x06, + 0x46, 0x33, 0xd9, 0xb0, 0x03, 0xb4, 0x02, 0xbe, + 0x7a, 0x03, 0xbf, 0x79, 0x03, 0xbb, 0xf0, 0x6f, + 0xe8, 0x1b, 0x76, 0xc3, 0xbb, 0x47, 0x33, 0xc7, + 0x47, 0x02, 0x7e, 0x03, 0xc7, 0x47, 0x04, 0x7d, + 0x03, 0xc7, 0x06, 0xf3, 0xb4, 0x20, 0x00, 0xe8, + 0xa2, 0x0e, 0xbb, 0x47, 0x33, 0xc7, 0x47, 0x02, + 0x7e, 0x03, 0xc7, 0x47, 0x04, 0x7d, 0x03, 0xb0, + 0x02, 0xe8, 0xe6, 0x7b, 0xc6, 0x06, 0x45, 0x33, + 0xd9, 0xb0, 0x03, 0xbe, 0x7f, 0x03, 0xbb, 0x6e, + 0x70, 0xe8, 0x6b, 0x77, 0xb9, 0x4b, 0x00, 0xb0, + 0x09, 0xb4, 0x0b, 0xe8, 0x67, 0x0b, 0xbb, 0x47, + 0x33, 0xc7, 0x47, 0x02, 0x82, 0x03, 0xc7, 0x47, + 0x04, 0x81, 0x03, 0xb0, 0x02, 0xe8, 0xba, 0x7b, + 0xc6, 0x06, 0x45, 0x33, 0xd0, 0xc6, 0x06, 0x46, + 0x33, 0xd9, 0xb0, 0x02, 0xb4, 0x03, 0xbe, 0x80, + 0x03, 0xbf, 0x7f, 0x03, 0xbb, 0x96, 0x70, 0xe8, + 0xac, 0x75, 0xc3, 0xbb, 0x47, 0x33, 0xc7, 0x07, + 0x85, 0x03, 0xc7, 0x47, 0x02, 0x84, 0x03, 0xc7, + 0x06, 0xf3, 0xb4, 0x1d, 0x00, 0xe8, 0x34, 0x0e, + 0xbb, 0x47, 0x33, 0xc7, 0x07, 0x85, 0x03, 0xc7, + 0x47, 0x02, 0x84, 0x03, 0xb0, 0x02, 0xe8, 0x79, + 0x7b, 0xc6, 0x06, 0x45, 0x33, 0xd0, 0xc6, 0x06, + 0x46, 0x33, 0xd9, 0xb0, 0x01, 0xb4, 0x02, 0xbe, + 0x87, 0x03, 0xbf, 0x86, 0x03, 0xbb, 0x61, 0x71, + 0xe8, 0x6b, 0x75, 0xb9, 0x38, 0x00, 0xb0, 0x03, + 0xb4, 0x04, 0xe8, 0xf0, 0x0a, 0xb0, 0x05, 0xe8, + 0xf4, 0x0a, 0xb0, 0x07, 0xe8, 0xf6, 0x0a, 0xb0, + 0x09, 0xe8, 0xf8, 0x0a, 0xbb, 0x47, 0x33, 0xc7, + 0x07, 0x89, 0x03, 0xc7, 0x47, 0x02, 0x88, 0x03, + 0xb0, 0x01, 0xe8, 0x35, 0x7b, 0xb0, 0x01, 0xb4, + 0x02, 0xbe, 0x87, 0x03, 0xbf, 0x86, 0x03, 0xbb, + 0xc6, 0x71, 0xe8, 0x31, 0x75, 0xc3, 0xff, 0x36, + 0xf3, 0xb4, 0xbb, 0x47, 0x33, 0xc7, 0x47, 0x02, + 0x8b, 0x03, 0xc7, 0x47, 0x04, 0x8a, 0x03, 0xc7, + 0x06, 0xf3, 0xb4, 0x23, 0x00, 0xe8, 0xb4, 0x0d, + 0xbb, 0x47, 0x33, 0xc7, 0x47, 0x02, 0x8b, 0x03, + 0xc7, 0x47, 0x04, 0x8a, 0x03, 0xb0, 0x03, 0xe8, + 0xf8, 0x7a, 0xc6, 0x06, 0x45, 0x33, 0xd9, 0xc6, + 0x06, 0x46, 0x33, 0xd0, 0xb0, 0x03, 0xb4, 0x02, + 0xbe, 0x8c, 0x03, 0xbf, 0x8d, 0x03, 0xbb, 0x43, + 0x72, 0xe8, 0xea, 0x74, 0xc6, 0x06, 0x46, 0x33, + 0xd9, 0xc6, 0x06, 0x45, 0x33, 0xd0, 0xb0, 0x02, + 0xb4, 0x03, 0xbe, 0x8e, 0x03, 0xbf, 0x8c, 0x03, + 0xbb, 0x18, 0x73, 0xe8, 0xd0, 0x74, 0xc7, 0x06, + 0xf3, 0xb4, 0x0b, 0x00, 0xe8, 0x41, 0xa0, 0xc6, + 0x47, 0x03, 0x33, 0xe8, 0x5e, 0x0d, 0x58, 0x3d, + 0x0b, 0x00, 0x75, 0x08, 0xc6, 0x06, 0xdc, 0x1c, + 0x02, 0xe8, 0xba, 0x88, 0xbb, 0x47, 0x33, 0xc7, + 0x47, 0x02, 0x8f, 0x03, 0xb0, 0x02, 0xe8, 0x99, + 0x7a, 0xbb, 0x47, 0x33, 0xc7, 0x47, 0x02, 0x83, + 0x03, 0xb0, 0x02, 0xe8, 0x51, 0x7a, 0xe8, 0x13, + 0x0b, 0xb8, 0xc8, 0x00, 0xe8, 0x5b, 0x7f, 0xb0, + 0x08, 0xe8, 0xf5, 0x79, 0xb8, 0x02, 0x08, 0xe8, + 0x8f, 0x8e, 0xc7, 0x06, 0xec, 0xdb, 0x01, 0x00, + 0xc3, 0x06, 0xb8, 0x00, 0xa0, 0x8e, 0xc0, 0xe8, + 0x1d, 0x00, 0xb8, 0x00, 0xa0, 0xb9, 0x00, 0x7d, + 0xe8, 0xfd, 0x0c, 0xbe, 0x14, 0x77, 0xbb, 0x0a, + 0x58, 0xe8, 0x49, 0x04, 0xe8, 0x17, 0x00, 0xb8, + 0x96, 0x00, 0xe8, 0x47, 0x7f, 0x07, 0xc3, 0x80, + 0x3e, 0xad, 0x64, 0x01, 0x75, 0x04, 0xe8, 0xab, + 0x0c, 0xc3, 0xe8, 0xc1, 0x0c, 0xc3, 0x80, 0x3e, + 0xad, 0x64, 0x01, 0x75, 0x04, 0xe8, 0xa9, 0x0c, + 0xc3, 0xe8, 0xbf, 0x0c, 0xe8, 0x8a, 0x0c, 0xc3, + 0xa1, 0x50, 0x72, 0xe8, 0x07, 0x04, 0x83, 0xc3, + 0x13, 0x8a, 0x07, 0x43, 0x0a, 0xc0, 0x75, 0xf9, + 0x80, 0x3f, 0x00, 0x74, 0xf4, 0x80, 0x3f, 0x01, + 0x75, 0x03, 0xbb, 0x50, 0x34, 0xe8, 0x04, 0x00, + 0xe8, 0xb4, 0x00, 0xc3, 0x06, 0xb8, 0xd4, 0x19, + 0x8e, 0xc0, 0xe8, 0xe0, 0x02, 0x8b, 0x0e, 0xaf, + 0x64, 0x8b, 0xd0, 0x52, 0xd1, 0xea, 0x2b, 0xca, + 0x73, 0x03, 0xb9, 0x02, 0x00, 0x26, 0x89, 0x0e, + 0x00, 0x00, 0x5a, 0x03, 0xca, 0x81, 0xf9, 0x3f, + 0x01, 0x76, 0x0d, 0xb9, 0x3f, 0x01, 0x2b, 0xca, + 0x83, 0xe9, 0x02, 0x26, 0x89, 0x0e, 0x00, 0x00, + 0x83, 0xee, 0x02, 0x81, 0xfe, 0x04, 0x00, 0x72, + 0x0e, 0x26, 0x8b, 0x14, 0x8b, 0xc8, 0x2b, 0xca, + 0xd1, 0xe9, 0x26, 0x89, 0x0c, 0xeb, 0xe9, 0xc6, + 0x06, 0xe6, 0x1c, 0xd1, 0xe8, 0x77, 0x02, 0xb8, + 0x3e, 0x00, 0x8b, 0xc8, 0xf7, 0x26, 0xd8, 0x64, + 0xf7, 0x36, 0xda, 0x64, 0x2b, 0xc8, 0xa1, 0xb1, + 0x64, 0x2b, 0xc1, 0x72, 0x12, 0x05, 0x08, 0x00, + 0x26, 0x8b, 0x0e, 0x0a, 0x00, 0x41, 0x2d, 0x0b, + 0x00, 0x72, 0x04, 0xe2, 0xf9, 0xeb, 0x03, 0xb8, + 0x01, 0x00, 0xf7, 0x26, 0xb6, 0x00, 0x26, 0x01, + 0x06, 0x00, 0x00, 0xc6, 0x06, 0xdb, 0x1c, 0x02, + 0xe8, 0x8b, 0x87, 0xc6, 0x06, 0xdb, 0x1c, 0x03, + 0xbb, 0x92, 0x32, 0xa1, 0x96, 0x32, 0x26, 0x8b, + 0x0e, 0x0c, 0x00, 0xf7, 0xe1, 0x8b, 0xc8, 0xc1, + 0xe9, 0x03, 0x83, 0xc1, 0x3c, 0xe8, 0xdb, 0x07, + 0x07, 0xc6, 0x06, 0xe8, 0x64, 0x01, 0xc3, 0xe8, + 0xf4, 0x94, 0xe8, 0x70, 0x8a, 0x2e, 0x80, 0x3e, + 0xd7, 0x00, 0x01, 0x74, 0x19, 0x80, 0x3e, 0x96, + 0x32, 0x00, 0x74, 0x08, 0xbb, 0x92, 0x32, 0xe8, + 0xcc, 0x07, 0x72, 0x0a, 0xe8, 0xa4, 0x05, 0x72, + 0x05, 0xe8, 0x70, 0x05, 0x73, 0xd9, 0xe8, 0xfc, + 0x9c, 0xe8, 0x3d, 0x9d, 0xc6, 0x06, 0xdb, 0x1c, + 0x01, 0xc6, 0x06, 0xdc, 0x1c, 0x02, 0xe8, 0x2d, + 0x87, 0xc6, 0x06, 0xdc, 0x1c, 0x03, 0xc6, 0x06, + 0xdb, 0x1c, 0x00, 0xc3, 0x51, 0xe8, 0x04, 0xff, + 0x59, 0xbb, 0x92, 0x32, 0xe8, 0x84, 0x07, 0xe8, + 0xa4, 0x94, 0xe8, 0x20, 0x8a, 0xbb, 0x92, 0x32, + 0xe8, 0x8b, 0x07, 0x72, 0x0a, 0xe8, 0x63, 0x05, + 0x72, 0x05, 0xe8, 0x2f, 0x05, 0x73, 0xe8, 0xe8, + 0xbc, 0xff, 0xc3, 0xa0, 0x96, 0xda, 0xbf, 0xc7, + 0x32, 0xb1, 0x1b, 0x48, 0xf6, 0xe1, 0x03, 0xf8, + 0xa1, 0xa4, 0xda, 0x89, 0x05, 0x57, 0xe8, 0xcb, + 0xfe, 0x5f, 0x57, 0xa1, 0xa4, 0xda, 0x89, 0x05, + 0xe8, 0x6b, 0x94, 0xe8, 0xe7, 0x89, 0x5f, 0x57, + 0xa1, 0xa4, 0xda, 0x89, 0x05, 0x2e, 0x80, 0x3e, + 0xd7, 0x00, 0x01, 0x74, 0x19, 0x80, 0x3e, 0x96, + 0x32, 0x00, 0x74, 0x08, 0xbb, 0x92, 0x32, 0xe8, + 0x3c, 0x07, 0x72, 0x0a, 0xe8, 0x14, 0x05, 0x72, + 0x05, 0xe8, 0xe0, 0x04, 0x73, 0xcb, 0x5f, 0xe8, + 0x6c, 0xff, 0xc3, 0x06, 0xb8, 0xd4, 0x19, 0x8e, + 0xc0, 0x57, 0xe8, 0x70, 0x01, 0x5f, 0x50, 0x8b, + 0x45, 0x0a, 0x33, 0xd2, 0xf7, 0x36, 0xb6, 0x00, + 0x8b, 0x45, 0x0c, 0xd1, 0xe8, 0x03, 0xd0, 0x58, + 0x8b, 0xca, 0x8b, 0xd0, 0x52, 0xd1, 0xea, 0x2b, + 0xca, 0x73, 0x03, 0xb9, 0x02, 0x00, 0x26, 0x89, + 0x0e, 0x00, 0x00, 0x5a, 0x03, 0xca, 0x81, 0xf9, + 0x3f, 0x01, 0x76, 0x0b, 0xb9, 0x3f, 0x01, 0x2b, + 0xca, 0x49, 0x26, 0x89, 0x0e, 0x00, 0x00, 0x83, + 0xee, 0x02, 0x81, 0xfe, 0x04, 0x00, 0x72, 0x0e, + 0x26, 0x8b, 0x14, 0x8b, 0xc8, 0x2b, 0xca, 0xd1, + 0xe9, 0x26, 0x89, 0x0c, 0xeb, 0xe9, 0x57, 0xa0, + 0xe7, 0x1c, 0xa2, 0xe6, 0x1c, 0xe8, 0xf6, 0x00, + 0x5f, 0x8b, 0x45, 0x0a, 0x33, 0xd2, 0xf7, 0x36, + 0xb6, 0x00, 0x05, 0x08, 0x00, 0x26, 0x8b, 0x0e, + 0x0a, 0x00, 0x41, 0x2d, 0x0b, 0x00, 0x72, 0x04, + 0xe2, 0xf9, 0xeb, 0x03, 0xb8, 0x01, 0x00, 0xb9, + 0x40, 0x01, 0xf7, 0xe1, 0x26, 0x01, 0x06, 0x00, + 0x00, 0xc6, 0x06, 0xdb, 0x1c, 0x02, 0xe8, 0x15, + 0x86, 0xc6, 0x06, 0xdb, 0x1c, 0x03, 0xbb, 0x92, + 0x32, 0xa1, 0x96, 0x32, 0x26, 0x8b, 0x0e, 0x0c, + 0x00, 0xf7, 0xe1, 0x8b, 0xc8, 0xc1, 0xe9, 0x03, + 0x83, 0xc1, 0x3c, 0xe8, 0x65, 0x06, 0x07, 0xc3, + 0x06, 0xb8, 0xd4, 0x19, 0x8e, 0xc0, 0x26, 0x89, + 0x36, 0x00, 0x00, 0xe8, 0xb7, 0x00, 0x83, 0xee, + 0x02, 0x81, 0xfe, 0x04, 0x00, 0x72, 0x0e, 0x26, + 0x8b, 0x14, 0x8b, 0xc8, 0x2b, 0xca, 0xd1, 0xe9, + 0x26, 0x89, 0x0c, 0xeb, 0xe9, 0xe8, 0x7e, 0x00, + 0xc6, 0x06, 0xdb, 0x1c, 0x02, 0xe8, 0xc6, 0x85, + 0xc6, 0x06, 0xdb, 0x1c, 0x03, 0xbb, 0x92, 0x32, + 0xa1, 0x96, 0x32, 0x26, 0x8b, 0x0e, 0x0c, 0x00, + 0xf7, 0xe1, 0x8b, 0xc8, 0xc1, 0xe9, 0x03, 0x83, + 0xc1, 0x3c, 0xe8, 0x16, 0x06, 0x07, 0xc3, 0x06, + 0xb8, 0xd4, 0x19, 0x8e, 0xc0, 0x26, 0x89, 0x36, + 0x00, 0x00, 0x53, 0xe8, 0x63, 0x88, 0x5b, 0xe8, + 0x75, 0x00, 0x83, 0xee, 0x02, 0x81, 0xfe, 0x04, + 0x00, 0x72, 0x0e, 0x26, 0x8b, 0x14, 0x8b, 0xc8, + 0x2b, 0xca, 0xd1, 0xe9, 0x26, 0x89, 0x0c, 0xeb, + 0xe9, 0xe8, 0x2a, 0x00, 0x07, 0xc3, 0xe8, 0x7f, + 0xff, 0xe8, 0x81, 0x88, 0x80, 0x3e, 0x96, 0x32, + 0x00, 0x74, 0x08, 0xbb, 0x92, 0x32, 0xe8, 0xe5, + 0x05, 0x72, 0x0a, 0xe8, 0x8e, 0x03, 0x72, 0x05, + 0xe8, 0xb8, 0x03, 0x73, 0xe4, 0xc6, 0x06, 0xdb, + 0x1c, 0x01, 0xe8, 0x51, 0x85, 0xc3, 0xbf, 0x04, + 0x00, 0xbe, 0x0e, 0x00, 0x57, 0x56, 0x26, 0x03, + 0x35, 0xe8, 0x21, 0x01, 0x5e, 0x5f, 0x81, 0xc6, + 0xc0, 0x0d, 0x83, 0xc7, 0x02, 0x43, 0x8a, 0x07, + 0x0a, 0xc0, 0x75, 0xe8, 0xc3, 0x53, 0xc6, 0x06, + 0xda, 0x1c, 0x01, 0xe8, 0x28, 0x85, 0xc6, 0x06, + 0xda, 0x1c, 0x00, 0xe8, 0xeb, 0x87, 0x5b, 0x33, + 0xc0, 0x26, 0xa3, 0x0c, 0x00, 0x26, 0xa3, 0x0a, + 0x00, 0xbe, 0x04, 0x00, 0x53, 0xe8, 0x40, 0x01, + 0x26, 0x01, 0x0e, 0x0c, 0x00, 0x26, 0x89, 0x04, + 0x26, 0xff, 0x06, 0x0a, 0x00, 0x83, 0xc6, 0x02, + 0x43, 0x8a, 0x07, 0x0a, 0xc0, 0x75, 0xe6, 0x5b, + 0x8b, 0xce, 0x33, 0xc0, 0x83, 0xee, 0x02, 0x81, + 0xfe, 0x04, 0x00, 0x72, 0x0b, 0x26, 0x8b, 0x14, + 0x3b, 0xc2, 0x73, 0xf0, 0x8b, 0xc2, 0xeb, 0xec, + 0x8b, 0xf1, 0xc3, 0x8b, 0x0e, 0xc0, 0x00, 0x8b, + 0x16, 0xc2, 0x00, 0xa1, 0x52, 0x72, 0xa3, 0x50, + 0x72, 0x0b, 0xc0, 0x74, 0x54, 0xc6, 0x06, 0x3d, + 0x66, 0x05, 0x83, 0x3e, 0x1f, 0xc4, 0x00, 0x74, + 0x12, 0xe8, 0x16, 0x7c, 0x80, 0x3e, 0xdb, 0xbb, + 0x01, 0x74, 0x4f, 0xa1, 0x50, 0x72, 0xe8, 0x7c, + 0x00, 0xeb, 0x18, 0xc6, 0x06, 0x3d, 0x66, 0x01, + 0x0a, 0xdb, 0x74, 0x05, 0xc6, 0x06, 0x3d, 0x66, + 0x03, 0xe8, 0x69, 0x00, 0x80, 0x3e, 0x3d, 0x66, + 0x01, 0x75, 0x08, 0x8b, 0x77, 0x09, 0x8b, 0x7f, + 0x0b, 0xeb, 0x06, 0x8b, 0x77, 0x0d, 0x8b, 0x7f, + 0x0f, 0x8a, 0x47, 0x11, 0xa2, 0xc3, 0x64, 0xe8, + 0x38, 0x00, 0xe8, 0xa9, 0x97, 0x72, 0x2e, 0xeb, + 0x11, 0xc6, 0x06, 0x3d, 0x66, 0x00, 0xc6, 0x06, + 0xc3, 0x64, 0x00, 0x8b, 0xf1, 0x8b, 0xfa, 0xe8, + 0x24, 0x97, 0xc6, 0x06, 0xc5, 0x64, 0x00, 0xc6, + 0x06, 0xc6, 0x64, 0x01, 0x89, 0x36, 0xb7, 0x64, + 0x89, 0x3e, 0xb9, 0x64, 0x89, 0x36, 0xbb, 0x64, + 0x89, 0x3e, 0xbd, 0x64, 0xc3, 0xb0, 0x36, 0xe9, + 0x36, 0x0f, 0x83, 0xfe, 0xff, 0x75, 0x0d, 0x83, + 0xff, 0xff, 0x75, 0x08, 0x8b, 0x3e, 0xb1, 0x64, + 0x8b, 0x36, 0xaf, 0x64, 0xc3, 0x48, 0xbb, 0x54, + 0x72, 0x50, 0xe8, 0x8d, 0x9b, 0x58, 0xd1, 0xe0, + 0x03, 0xd8, 0x8b, 0x1f, 0xc3, 0x8a, 0x0f, 0x0a, + 0xc9, 0x74, 0x4c, 0x80, 0xe9, 0x1f, 0xb5, 0x00, + 0xbf, 0x38, 0x01, 0xd1, 0xe1, 0x03, 0xf9, 0x8b, + 0x3d, 0x81, 0xc7, 0x3a, 0x01, 0x8b, 0x0d, 0x83, + 0xc7, 0x02, 0x8a, 0x26, 0xe6, 0x1c, 0x51, 0x56, + 0x51, 0x56, 0x8a, 0x05, 0x0a, 0xc0, 0x74, 0x0b, + 0xfe, 0xc8, 0x0a, 0xc0, 0x74, 0x02, 0x8a, 0xc4, + 0x26, 0x88, 0x04, 0x47, 0x46, 0xfe, 0xcd, 0x75, + 0xe9, 0x5e, 0x59, 0x81, 0xc6, 0x40, 0x01, 0xfe, + 0xc9, 0x75, 0xdd, 0x5e, 0x59, 0x8a, 0xc5, 0xb4, + 0x00, 0x03, 0xf0, 0x4e, 0x43, 0xeb, 0xae, 0xc3, + 0x52, 0x33, 0xc0, 0x8b, 0xd0, 0x8a, 0x0f, 0x0a, + 0xc9, 0x74, 0x1f, 0x42, 0x80, 0xe9, 0x1f, 0xb5, + 0x00, 0xbf, 0x38, 0x01, 0xd1, 0xe1, 0x03, 0xf9, + 0x8b, 0x3d, 0x81, 0xc7, 0x3a, 0x01, 0x8b, 0x0d, + 0x8a, 0xcd, 0xb5, 0x00, 0x03, 0xc1, 0x48, 0x43, + 0xeb, 0xdb, 0x8b, 0xca, 0x5a, 0xc3, 0xb8, 0x04, + 0x00, 0xe8, 0x87, 0x0c, 0xbb, 0x9e, 0x33, 0xd1, + 0xe0, 0x03, 0xd8, 0x8b, 0x1f, 0xe8, 0x6d, 0xfb, + 0xc3, 0x33, 0xc0, 0xcd, 0x33, 0x0b, 0xc0, 0x74, + 0x01, 0xc3, 0xc6, 0x06, 0xd9, 0x00, 0x01, 0xba, + 0x5f, 0x33, 0xb4, 0x09, 0xcd, 0x21, 0xb4, 0x08, + 0xcd, 0x21, 0xc3, 0x06, 0xba, 0x00, 0xa0, 0x8e, + 0xc2, 0x50, 0xe8, 0xfe, 0x00, 0x58, 0x80, 0x3e, + 0xd8, 0x00, 0x00, 0x74, 0x02, 0x07, 0xc3, 0x0a, + 0xc0, 0x75, 0x12, 0xa1, 0xc0, 0x00, 0x3b, 0x06, + 0xc4, 0x00, 0x75, 0x09, 0xa1, 0xc2, 0x00, 0x3b, + 0x06, 0xc6, 0x00, 0x74, 0x58, 0xe8, 0x57, 0x00, + 0xba, 0x40, 0x01, 0xa1, 0xc2, 0x00, 0xa3, 0xc6, + 0x00, 0xf7, 0xe2, 0x8b, 0xf0, 0xa1, 0xc0, 0x00, + 0xa3, 0xc4, 0x00, 0x03, 0xf0, 0x89, 0x36, 0xc8, + 0x00, 0xba, 0x08, 0x00, 0xb8, 0x40, 0x01, 0x2b, + 0x06, 0xc0, 0x00, 0x3b, 0xc2, 0x77, 0x02, 0x8b, + 0xd0, 0xbb, 0xda, 0x00, 0xb9, 0x0c, 0x00, 0x8b, + 0xf9, 0x8b, 0xca, 0x8a, 0x07, 0x3c, 0x01, 0x74, + 0x03, 0x26, 0x88, 0x04, 0x43, 0x46, 0xe2, 0xf3, + 0x8b, 0xcf, 0x81, 0xc6, 0x40, 0x01, 0x83, 0xc3, + 0x08, 0x2b, 0xf2, 0x2b, 0xda, 0x81, 0xfe, 0xff, + 0xf9, 0x73, 0x02, 0xe2, 0xda, 0x07, 0xc3, 0x1e, + 0xba, 0x40, 0x01, 0xa1, 0xc6, 0x00, 0xf7, 0xe2, + 0x8b, 0xf0, 0x03, 0x36, 0xc4, 0x00, 0xba, 0x08, + 0x00, 0xb8, 0x40, 0x01, 0x2b, 0x06, 0xc4, 0x00, + 0x3b, 0xc2, 0x77, 0x02, 0x8b, 0xd0, 0x8b, 0x1e, + 0xc8, 0x00, 0xb9, 0x0c, 0x00, 0xa1, 0xb1, 0x32, + 0x8e, 0xd8, 0x8b, 0xf9, 0x8b, 0xca, 0x8a, 0x07, + 0x26, 0x88, 0x04, 0x43, 0x46, 0xe2, 0xf7, 0x8b, + 0xcf, 0x81, 0xc3, 0x40, 0x01, 0x81, 0xc6, 0x40, + 0x01, 0x2b, 0xda, 0x2b, 0xf2, 0x81, 0xfe, 0xff, + 0xf9, 0x73, 0x02, 0xe2, 0xdd, 0x1f, 0xc3, 0xb0, + 0x01, 0xe8, 0x27, 0xff, 0xc3, 0xc6, 0x06, 0xd8, + 0x00, 0x01, 0x06, 0x9c, 0xb8, 0x00, 0xa0, 0x8e, + 0xc0, 0xfa, 0xe8, 0x9a, 0xff, 0xa1, 0xc0, 0x00, + 0xa3, 0xc4, 0x00, 0xa1, 0xc2, 0x00, 0xa3, 0xc6, + 0x00, 0xe8, 0x8b, 0xff, 0x9d, 0x07, 0xc3, 0xc6, + 0x06, 0xd8, 0x00, 0x00, 0x9c, 0xfa, 0xe8, 0xce, + 0xff, 0x9d, 0xc3, 0xc6, 0x06, 0xca, 0x00, 0x00, + 0xc6, 0x06, 0xcb, 0x00, 0x00, 0x8b, 0x0e, 0xc0, + 0x00, 0x8b, 0x16, 0xc2, 0x00, 0x33, 0xdb, 0x2e, + 0xa0, 0xd7, 0x00, 0x3c, 0x4d, 0x75, 0x09, 0x81, + 0xf9, 0x3f, 0x01, 0x73, 0x36, 0x41, 0xeb, 0x33, + 0x3c, 0x4b, 0x75, 0x07, 0x0b, 0xc9, 0x74, 0x2b, + 0x49, 0xeb, 0x28, 0x3c, 0x48, 0x75, 0x07, 0x0b, + 0xd2, 0x74, 0x20, 0x4a, 0xeb, 0x1d, 0x3c, 0x50, + 0x75, 0x09, 0x81, 0xfa, 0xc7, 0x00, 0x73, 0x13, + 0x42, 0xeb, 0x10, 0x3c, 0x1d, 0x75, 0x05, 0x80, + 0xcb, 0x01, 0xeb, 0x07, 0x3c, 0x38, 0x75, 0x19, + 0x80, 0xcb, 0x02, 0x80, 0x3e, 0xd9, 0x00, 0x01, + 0x74, 0x1d, 0x53, 0x51, 0x52, 0xd1, 0xe1, 0xb8, + 0x04, 0x00, 0xcd, 0x33, 0x5a, 0x59, 0x5b, 0xeb, + 0x0e, 0x80, 0x3e, 0xd9, 0x00, 0x01, 0x74, 0x07, + 0xb8, 0x03, 0x00, 0xcd, 0x33, 0xd1, 0xe9, 0x89, + 0x0e, 0xc0, 0x00, 0x89, 0x16, 0xc2, 0x00, 0xf6, + 0xc3, 0x02, 0x74, 0x05, 0xc6, 0x06, 0xcb, 0x00, + 0x01, 0xf6, 0xc3, 0x01, 0x74, 0x05, 0xc6, 0x06, + 0xca, 0x00, 0x01, 0xc3, 0x80, 0x3e, 0xcf, 0x00, + 0x02, 0x75, 0x07, 0xc6, 0x06, 0xcf, 0x00, 0x00, + 0xeb, 0x0e, 0x80, 0x3e, 0xcb, 0x00, 0x00, 0x74, + 0x13, 0x80, 0x3e, 0xcd, 0x00, 0x01, 0x74, 0x11, + 0xc6, 0x06, 0xcb, 0x00, 0x01, 0xc6, 0x06, 0xcd, + 0x00, 0x01, 0xf9, 0xc3, 0xc6, 0x06, 0xcd, 0x00, + 0x00, 0xf8, 0xc3, 0x80, 0x3e, 0xcf, 0x00, 0x04, + 0x75, 0x07, 0xc6, 0x06, 0xcf, 0x00, 0x00, 0xeb, + 0x0e, 0x80, 0x3e, 0xca, 0x00, 0x00, 0x74, 0x13, + 0x80, 0x3e, 0xcc, 0x00, 0x01, 0x74, 0x11, 0xc6, + 0x06, 0xca, 0x00, 0x01, 0xc6, 0x06, 0xcc, 0x00, + 0x01, 0xf9, 0xc3, 0xc6, 0x06, 0xcc, 0x00, 0x00, + 0xf8, 0xc3, 0xe8, 0x06, 0xff, 0xe8, 0xcb, 0xff, + 0x72, 0x05, 0xe8, 0x97, 0xff, 0x73, 0xf3, 0xc3, + 0x50, 0x52, 0xba, 0xda, 0x03, 0xec, 0xa8, 0x08, + 0x75, 0xf8, 0xec, 0xa8, 0x08, 0x74, 0xfb, 0x5a, + 0x58, 0xc3, 0x1e, 0x8e, 0xda, 0xb7, 0x00, 0x2a, + 0xf8, 0x8a, 0xe7, 0xf6, 0xc7, 0x80, 0x74, 0x04, + 0xb4, 0x40, 0x02, 0xe7, 0x50, 0xa8, 0x80, 0x74, + 0x04, 0x34, 0xff, 0xfe, 0xc0, 0xb4, 0x00, 0x8b, + 0xc8, 0xb8, 0x40, 0x00, 0x33, 0xd2, 0xf7, 0xf1, + 0x8b, 0xc8, 0x58, 0xfc, 0x50, 0x51, 0x56, 0xb9, + 0x40, 0x00, 0xfb, 0xe8, 0xba, 0xff, 0xfa, 0xba, + 0xc8, 0x03, 0x32, 0xc0, 0xee, 0x42, 0xfa, 0xac, + 0x2a, 0xc4, 0x73, 0x02, 0x32, 0xc0, 0xee, 0xac, + 0x2a, 0xc4, 0x73, 0x02, 0x32, 0xc0, 0xee, 0xac, + 0x2a, 0xc4, 0x73, 0x02, 0x32, 0xc0, 0xee, 0xac, + 0x2a, 0xc4, 0x73, 0x02, 0x32, 0xc0, 0xee, 0xac, + 0x2a, 0xc4, 0x73, 0x02, 0x32, 0xc0, 0xee, 0xac, + 0x2a, 0xc4, 0x73, 0x02, 0x32, 0xc0, 0xee, 0xe2, + 0xce, 0xfb, 0xb9, 0x40, 0x00, 0xe8, 0x78, 0xff, + 0xfa, 0xac, 0x2a, 0xc4, 0x73, 0x02, 0x32, 0xc0, + 0xee, 0xac, 0x2a, 0xc4, 0x73, 0x02, 0x32, 0xc0, + 0xee, 0xac, 0x2a, 0xc4, 0x73, 0x02, 0x32, 0xc0, + 0xee, 0xac, 0x2a, 0xc4, 0x73, 0x02, 0x32, 0xc0, + 0xee, 0xac, 0x2a, 0xc4, 0x73, 0x02, 0x32, 0xc0, + 0xee, 0xac, 0x2a, 0xc4, 0x73, 0x02, 0x32, 0xc0, + 0xee, 0xe2, 0xce, 0xfb, 0x5e, 0x59, 0x58, 0x02, + 0xe7, 0x49, 0x74, 0x03, 0xe9, 0x75, 0xff, 0x1f, + 0xc3, 0xbb, 0x3e, 0x66, 0xe8, 0xfb, 0x97, 0x8b, + 0x36, 0xaf, 0x64, 0x8b, 0x3e, 0xb1, 0x64, 0x8b, + 0x07, 0x3d, 0xff, 0xff, 0x74, 0x25, 0x53, 0x8b, + 0x4f, 0x04, 0x8b, 0x57, 0x06, 0x8b, 0x5f, 0x02, + 0xe8, 0x1c, 0x00, 0x5b, 0x72, 0x05, 0x83, 0xc3, + 0x09, 0xeb, 0xe4, 0x8a, 0x47, 0x08, 0x3a, 0x06, + 0x08, 0x66, 0x74, 0x06, 0xa2, 0x08, 0x66, 0xe8, + 0x19, 0x00, 0xc3, 0x32, 0xc0, 0xeb, 0xef, 0x3b, + 0xf0, 0x72, 0x0e, 0x3b, 0xf1, 0x77, 0x0a, 0x3b, + 0xfb, 0x72, 0x06, 0x3b, 0xfa, 0x77, 0x02, 0xf9, + 0xc3, 0xf8, 0xc3, 0x80, 0x3e, 0xad, 0x64, 0x00, + 0x74, 0x31, 0xbe, 0x09, 0x66, 0xb9, 0x0d, 0x00, + 0xb3, 0xf2, 0x51, 0x8a, 0x24, 0x8a, 0x6c, 0x01, + 0x8a, 0x4c, 0x02, 0x2a, 0xe0, 0x73, 0x02, 0x32, + 0xe4, 0x2a, 0xe8, 0x73, 0x02, 0x32, 0xed, 0x2a, + 0xc8, 0x73, 0x02, 0x32, 0xc9, 0x50, 0xe8, 0x37, + 0x00, 0x58, 0x83, 0xc6, 0x03, 0xfe, 0xc3, 0x59, + 0xe2, 0xd8, 0xc3, 0xbe, 0x30, 0x66, 0xb9, 0x0d, + 0x00, 0xb3, 0xf2, 0x51, 0x8a, 0x24, 0x8a, 0xec, + 0x8a, 0xcc, 0x2a, 0xe0, 0x73, 0x02, 0x32, 0xe4, + 0x2a, 0xe8, 0x73, 0x02, 0x32, 0xed, 0x2a, 0xc8, + 0x73, 0x02, 0x32, 0xc9, 0x50, 0xe8, 0x08, 0x00, + 0x58, 0x46, 0xfe, 0xc3, 0x59, 0xe2, 0xdc, 0xc3, + 0xba, 0xc8, 0x03, 0x8a, 0xc3, 0xee, 0xeb, 0x00, + 0x42, 0x8a, 0xc4, 0xee, 0xeb, 0x00, 0x8a, 0xc5, + 0xee, 0xeb, 0x00, 0x8a, 0xc1, 0xee, 0xc3, 0x1e, + 0x06, 0xa1, 0xb1, 0x32, 0x8e, 0xc0, 0xa1, 0xb3, + 0x32, 0x8e, 0xd8, 0x33, 0xff, 0x33, 0xf6, 0xb9, + 0x00, 0x7d, 0xfc, 0xf3, 0xa5, 0x07, 0x1f, 0xc3, + 0x1e, 0x06, 0xe8, 0x4b, 0xfe, 0xb8, 0x00, 0xa0, + 0x8e, 0xc0, 0xa1, 0xb1, 0x32, 0x8e, 0xd8, 0x33, + 0xff, 0x33, 0xf6, 0xb9, 0x00, 0x7d, 0xfc, 0xf3, + 0xa5, 0x07, 0x1f, 0xc3, 0xe8, 0xe1, 0xff, 0xe8, + 0xf5, 0xfc, 0xc3, 0xbe, 0x86, 0x32, 0x8b, 0x04, + 0x8b, 0x54, 0x02, 0x03, 0xc1, 0x73, 0x01, 0x42, + 0x89, 0x07, 0x89, 0x57, 0x02, 0xc3, 0xbe, 0x86, + 0x32, 0x8b, 0x04, 0x8b, 0x54, 0x02, 0x8b, 0x4f, + 0x02, 0x8b, 0x37, 0x3b, 0xd1, 0x77, 0x08, 0x72, + 0x04, 0x3b, 0xc6, 0x73, 0x02, 0xf8, 0xc3, 0xf9, + 0xc3, 0x8a, 0x0e, 0x42, 0x32, 0xb5, 0x00, 0x80, + 0xf9, 0x01, 0x75, 0x05, 0xc6, 0x06, 0xa8, 0x64, + 0x04, 0x80, 0xf9, 0x03, 0x75, 0x05, 0xc6, 0x06, + 0xa8, 0x64, 0x05, 0x80, 0xf9, 0x04, 0x75, 0x05, + 0xc6, 0x06, 0xa8, 0x64, 0x05, 0xba, 0x44, 0x00, + 0xa1, 0xc1, 0x32, 0xa3, 0xac, 0x00, 0xc7, 0x06, + 0xaa, 0x00, 0x00, 0x01, 0xe8, 0x29, 0x06, 0xb8, + 0x00, 0x01, 0xa3, 0x4a, 0x32, 0xa1, 0xc1, 0x32, + 0xa3, 0x4c, 0x32, 0xb4, 0x01, 0x8b, 0x1e, 0x43, + 0x32, 0x8b, 0x0e, 0xc3, 0x32, 0x8b, 0x3e, 0xc5, + 0x32, 0x8a, 0x16, 0x45, 0x32, 0x8a, 0x36, 0x46, + 0x32, 0xff, 0x1e, 0x4a, 0x32, 0xb8, 0x3b, 0x0b, + 0x8e, 0xd8, 0x8e, 0xc0, 0xc3, 0xe8, 0x3b, 0x00, + 0xbb, 0x4e, 0x32, 0xba, 0x0a, 0x00, 0x8b, 0x3e, + 0x7d, 0x32, 0x8b, 0x0f, 0x0b, 0xc9, 0x74, 0x25, + 0x8a, 0x47, 0x03, 0xa2, 0x7c, 0x32, 0x33, 0xc0, + 0x89, 0x07, 0x89, 0x47, 0x02, 0x3b, 0xf9, 0x74, + 0x13, 0x89, 0x0e, 0x7d, 0x32, 0xb0, 0x01, 0xb4, + 0x05, 0xff, 0x1e, 0x4a, 0x32, 0xb8, 0x3b, 0x0b, + 0x8e, 0xd8, 0x8e, 0xc0, 0xc3, 0x83, 0xc3, 0x04, + 0x4a, 0x75, 0xcf, 0xc7, 0x06, 0x7a, 0x32, 0x00, + 0x00, 0xba, 0x0a, 0x00, 0xbb, 0x4e, 0x32, 0x8b, + 0x0f, 0x0b, 0xc9, 0x74, 0x07, 0x8a, 0x47, 0x02, + 0xa2, 0x7a, 0x32, 0xc3, 0x83, 0xc3, 0x04, 0x4a, + 0x75, 0xed, 0xc3, 0xe8, 0xdd, 0xff, 0xbb, 0x4e, + 0x32, 0xba, 0x0a, 0x00, 0x8b, 0x0f, 0x0b, 0xc9, + 0x74, 0x14, 0x89, 0x0e, 0x7d, 0x32, 0xb0, 0x01, + 0xb4, 0x05, 0xff, 0x1e, 0x4a, 0x32, 0xb8, 0x3b, + 0x0b, 0x8e, 0xd8, 0x8e, 0xc0, 0xc3, 0x83, 0xc3, + 0x04, 0x4a, 0x75, 0xe0, 0xc3, 0xbb, 0x4e, 0x32, + 0x89, 0x0f, 0x89, 0x47, 0x02, 0xc3, 0x89, 0x4f, + 0x04, 0x89, 0x47, 0x06, 0xc3, 0x89, 0x4f, 0x08, + 0x89, 0x47, 0x0a, 0xc3, 0x89, 0x4f, 0x0c, 0x89, + 0x47, 0x0e, 0xc3, 0x89, 0x4f, 0x10, 0x89, 0x47, + 0x12, 0xc3, 0x89, 0x4f, 0x14, 0x89, 0x47, 0x16, + 0xc3, 0x89, 0x4f, 0x18, 0x89, 0x47, 0x1a, 0xc3, + 0x89, 0x4f, 0x1c, 0x89, 0x47, 0x1e, 0xc3, 0x89, + 0x4f, 0x20, 0x89, 0x47, 0x22, 0xc3, 0x89, 0x4f, + 0x24, 0x89, 0x47, 0x26, 0xc3, 0x81, 0xe9, 0xf3, + 0x01, 0xba, 0x2c, 0x00, 0xa1, 0xbf, 0x32, 0xa3, + 0xac, 0x00, 0xc7, 0x06, 0xaa, 0x00, 0x00, 0x00, + 0xe8, 0x15, 0x05, 0xc3, 0xbe, 0xc7, 0x32, 0xbb, + 0x47, 0x33, 0xb9, 0x01, 0x00, 0x53, 0x51, 0x56, + 0x49, 0xb0, 0x1b, 0xf6, 0xe1, 0x03, 0xf0, 0xd1, + 0xe1, 0x03, 0xd9, 0x8b, 0x0f, 0x0b, 0xc9, 0x74, + 0x1b, 0x81, 0xe9, 0xf3, 0x01, 0xba, 0x2c, 0x00, + 0x8b, 0x44, 0x16, 0xa3, 0xac, 0x00, 0xc7, 0x06, + 0xaa, 0x00, 0x00, 0x00, 0xe8, 0xe1, 0x04, 0xb8, + 0x01, 0x00, 0x89, 0x04, 0x5e, 0x59, 0x5b, 0x41, + 0x80, 0xf9, 0x04, 0x76, 0xc8, 0xc3, 0xbe, 0xc7, + 0x32, 0xbb, 0x47, 0x33, 0xb9, 0x01, 0x00, 0x53, + 0x51, 0x56, 0x8b, 0xd1, 0x49, 0xb0, 0x1b, 0xf6, + 0xe1, 0x03, 0xf0, 0xd1, 0xe1, 0x03, 0xd9, 0x8b, + 0x07, 0x0b, 0xc0, 0x74, 0x26, 0x8b, 0x0e, 0xf3, + 0xb4, 0x49, 0xc1, 0xe1, 0x02, 0x03, 0xca, 0xba, + 0x38, 0x00, 0xe8, 0x2b, 0x00, 0x8b, 0x44, 0x16, + 0xa3, 0xac, 0x00, 0xc7, 0x06, 0xaa, 0x00, 0x00, + 0x00, 0x56, 0xe8, 0x93, 0x04, 0x5e, 0xb8, 0x01, + 0x00, 0x89, 0x04, 0x5e, 0x59, 0x5b, 0x41, 0x80, + 0xf9, 0x04, 0x76, 0xbb, 0xbf, 0x47, 0x33, 0xb9, + 0x08, 0x00, 0x33, 0xc0, 0xfc, 0xf3, 0xab, 0xc3, + 0x83, 0xf9, 0x51, 0x75, 0x0e, 0x80, 0x3e, 0xad, + 0xdb, 0x01, 0x75, 0x07, 0xb9, 0xa0, 0x00, 0xba, + 0x2c, 0x00, 0xc3, 0x81, 0xf9, 0x89, 0x00, 0x75, + 0x1c, 0x80, 0x3e, 0xc5, 0xdb, 0x01, 0x75, 0x15, + 0x80, 0x3e, 0xc6, 0xdb, 0x01, 0x75, 0x07, 0xb9, + 0xcb, 0x00, 0xba, 0x2c, 0x00, 0xc3, 0xb9, 0xca, + 0x00, 0xba, 0x2c, 0x00, 0xc3, 0x83, 0xf9, 0x19, + 0x75, 0x0e, 0x80, 0x3e, 0xdf, 0xdb, 0x02, 0x75, + 0x07, 0xb9, 0x4c, 0x01, 0xba, 0x2c, 0x00, 0xc3, + 0x83, 0xf9, 0x25, 0x75, 0x1c, 0x80, 0x3e, 0xe2, + 0xdb, 0x01, 0x75, 0x07, 0xb9, 0x5f, 0x01, 0xba, + 0x2c, 0x00, 0xc3, 0x80, 0x3e, 0xe2, 0xdb, 0x02, + 0x75, 0x07, 0xb9, 0x6c, 0x01, 0xba, 0x2c, 0x00, + 0xc3, 0x80, 0x3e, 0xe7, 0xdb, 0x01, 0x75, 0x18, + 0x83, 0xf9, 0x1d, 0x75, 0x07, 0xb9, 0x7c, 0x01, + 0xba, 0x2c, 0x00, 0xc3, 0x83, 0xf9, 0x1e, 0x75, + 0x07, 0xb9, 0x7d, 0x01, 0xba, 0x2c, 0x00, 0xc3, + 0x83, 0x3e, 0xec, 0xdb, 0x01, 0x75, 0x0b, 0x83, + 0xf9, 0x2a, 0x75, 0x06, 0xb9, 0x90, 0x01, 0xba, + 0x2c, 0x00, 0xc3, 0xa1, 0xbf, 0x32, 0xa3, 0xac, + 0x00, 0x33, 0xc0, 0xa3, 0xaa, 0x00, 0xb9, 0x01, + 0x00, 0xba, 0x54, 0x00, 0xe8, 0xc9, 0x03, 0xc3, + 0x8b, 0x0e, 0xf3, 0xb4, 0xba, 0x25, 0x00, 0xa1, + 0xb5, 0x32, 0xa3, 0xac, 0x00, 0x33, 0xc0, 0xa3, + 0xaa, 0x00, 0xe8, 0xb3, 0x03, 0x83, 0x3e, 0xf3, + 0xb4, 0x07, 0x75, 0x11, 0xa0, 0xe6, 0xdb, 0x3c, + 0x02, 0x74, 0x0a, 0xe8, 0x08, 0x00, 0x3c, 0x01, + 0x74, 0x03, 0xe8, 0x01, 0x00, 0xc3, 0x1e, 0x8b, + 0x16, 0xb5, 0x32, 0x8e, 0xda, 0x33, 0xdb, 0xff, + 0x0f, 0x1f, 0xc3, 0xe8, 0x0f, 0x00, 0xe8, 0xf0, + 0xfb, 0xe8, 0x3b, 0x00, 0xc6, 0x06, 0x08, 0x66, + 0xfe, 0xe8, 0xe5, 0xfb, 0xc3, 0x89, 0x3e, 0xb7, + 0x64, 0x89, 0x36, 0xb9, 0x64, 0x89, 0x3e, 0xbb, + 0x64, 0x89, 0x36, 0xbd, 0x64, 0xa3, 0xaf, 0x64, + 0x89, 0x1e, 0xb1, 0x64, 0x88, 0x2e, 0xc3, 0x64, + 0x88, 0x0e, 0x07, 0x66, 0xe8, 0x49, 0x92, 0xc6, + 0x06, 0xdc, 0x1c, 0x02, 0xc6, 0x06, 0xda, 0x1c, + 0x01, 0xc6, 0x06, 0xc6, 0x64, 0x01, 0xc3, 0xc6, + 0x06, 0xc5, 0x64, 0x00, 0xc6, 0x06, 0x3d, 0x66, + 0x00, 0xc6, 0x06, 0x40, 0x67, 0xff, 0xc6, 0x06, + 0xc4, 0x64, 0x01, 0xe8, 0x12, 0x89, 0x80, 0x3e, + 0xad, 0x64, 0x01, 0x75, 0x0a, 0xe8, 0x84, 0x00, + 0xe8, 0x1d, 0x00, 0xe8, 0x8b, 0x00, 0xc3, 0xe8, + 0x94, 0x00, 0xe8, 0x13, 0x00, 0xb8, 0x00, 0xa0, + 0xb9, 0x00, 0x7d, 0xe8, 0xa2, 0x00, 0xe8, 0x92, + 0x00, 0xe8, 0x5d, 0x00, 0xe8, 0x59, 0xfc, 0xc3, + 0xe8, 0xa2, 0x01, 0xe8, 0x01, 0x00, 0xc3, 0xe8, + 0xf0, 0x05, 0xe8, 0x0f, 0x01, 0xe8, 0x2f, 0xfc, + 0xe8, 0x45, 0xfc, 0xff, 0x36, 0xce, 0x64, 0xc7, + 0x06, 0xce, 0x64, 0xff, 0xff, 0xe8, 0x86, 0x8f, + 0x8f, 0x06, 0xce, 0x64, 0xc6, 0x06, 0x33, 0x33, + 0x01, 0xe8, 0x22, 0x00, 0xc6, 0x06, 0xdd, 0x1c, + 0x02, 0xe8, 0xda, 0x7b, 0xbb, 0xa4, 0x32, 0x8b, + 0x0e, 0x90, 0x32, 0xe8, 0x3d, 0xfc, 0xc6, 0x06, + 0x33, 0x33, 0x00, 0xc6, 0x06, 0xdc, 0x1c, 0x03, + 0xc6, 0x06, 0xdd, 0x1c, 0x03, 0xc3, 0xbb, 0xa4, + 0x32, 0x33, 0xc0, 0x89, 0x07, 0x89, 0x47, 0x02, + 0xc3, 0xb8, 0x1b, 0x10, 0x33, 0xdb, 0xb9, 0xff, + 0x00, 0xcd, 0x10, 0xc3, 0x8b, 0x16, 0xb3, 0x32, + 0xbe, 0x00, 0xfa, 0xb0, 0xf0, 0xe8, 0x4a, 0xfa, + 0xc3, 0x8b, 0x16, 0xb3, 0x32, 0xbe, 0x00, 0xfa, + 0xb0, 0x10, 0xe8, 0x3d, 0xfa, 0xc3, 0x8b, 0x16, + 0xb3, 0x32, 0xbe, 0x00, 0xfa, 0xb0, 0xc0, 0xe8, + 0x30, 0xfa, 0xc3, 0x8b, 0x16, 0xb3, 0x32, 0xbe, + 0x00, 0xfa, 0xb0, 0x20, 0xe8, 0x23, 0xfa, 0xc3, + 0x06, 0x8e, 0xc0, 0x33, 0xc0, 0x8b, 0xf8, 0xfc, + 0xf3, 0xab, 0x07, 0xc3, 0xc6, 0x06, 0xdc, 0x1c, + 0x01, 0xe8, 0x0f, 0x00, 0xc3, 0xc6, 0x06, 0xdc, + 0x1c, 0x02, 0xe8, 0x06, 0x00, 0xc6, 0x06, 0xdc, + 0x1c, 0x03, 0xc3, 0x80, 0x3e, 0xad, 0x64, 0x01, + 0x75, 0x0a, 0xe8, 0x9f, 0xff, 0xe8, 0x1d, 0x00, + 0xe8, 0xa6, 0xff, 0xc3, 0xe8, 0xaf, 0xff, 0xe8, + 0x13, 0x00, 0xb8, 0x00, 0xa0, 0xb9, 0x00, 0x7d, + 0xe8, 0xbd, 0xff, 0xe8, 0xad, 0xff, 0xe8, 0x78, + 0xff, 0xe8, 0x74, 0xfb, 0xc3, 0xe8, 0xbd, 0x00, + 0xe8, 0x0f, 0x05, 0xe8, 0x2e, 0x00, 0xe8, 0x03, + 0xfd, 0xe8, 0x4b, 0xfb, 0xe8, 0x61, 0xfb, 0xe8, + 0x54, 0xff, 0xc6, 0x06, 0x33, 0x33, 0x01, 0xc6, + 0x06, 0xdd, 0x1c, 0x02, 0xe8, 0x07, 0x7b, 0xbb, + 0xa4, 0x32, 0x8b, 0x0e, 0x90, 0x32, 0xe8, 0x6a, + 0xfb, 0xc6, 0x06, 0x33, 0x33, 0x00, 0xc6, 0x06, + 0xdd, 0x1c, 0x03, 0xc3, 0x55, 0xbf, 0xc7, 0x32, + 0xb9, 0x36, 0x00, 0x33, 0xc0, 0xfc, 0xf3, 0xab, + 0xbe, 0xc7, 0x32, 0xbb, 0x9e, 0xd8, 0xbd, 0x46, + 0xd9, 0xb9, 0x01, 0x00, 0x53, 0x51, 0x56, 0x55, + 0x8b, 0x16, 0xf3, 0xb4, 0x4a, 0xc1, 0xe2, 0x02, + 0x03, 0xda, 0x03, 0xd9, 0x4b, 0xd1, 0xe2, 0x03, + 0xea, 0x49, 0x03, 0xe9, 0x03, 0xe9, 0xb0, 0x1b, + 0xf6, 0xe1, 0x41, 0x03, 0xf0, 0x8a, 0x2f, 0x0a, + 0xed, 0x74, 0x3e, 0x8a, 0xd1, 0xb6, 0x00, 0x8b, + 0x0e, 0xf3, 0xb4, 0x49, 0xc1, 0xe1, 0x02, 0x03, + 0xca, 0x51, 0x3e, 0x8b, 0x5e, 0x00, 0x83, 0xc6, + 0x16, 0xe8, 0x4f, 0x04, 0x59, 0xba, 0x38, 0x00, + 0xe8, 0x15, 0xfd, 0x8b, 0x04, 0xa3, 0xac, 0x00, + 0xc7, 0x06, 0xaa, 0x00, 0x00, 0x00, 0x56, 0xe8, + 0x7e, 0x01, 0x5e, 0x83, 0xee, 0x16, 0xb8, 0x01, + 0x00, 0x89, 0x04, 0x89, 0x44, 0x12, 0x89, 0x44, + 0x14, 0x5d, 0x5e, 0x59, 0x5b, 0x41, 0x80, 0xf9, + 0x04, 0x76, 0x91, 0x5d, 0xc3, 0x8b, 0x0e, 0xf3, + 0xb4, 0xba, 0x1d, 0x00, 0xa1, 0xb3, 0x32, 0xa3, + 0xac, 0x00, 0x33, 0xc0, 0xa3, 0xaa, 0x00, 0xe8, + 0x4e, 0x01, 0xe8, 0x9e, 0x00, 0xe8, 0x80, 0xfd, + 0xc3, 0xe8, 0x04, 0x00, 0xe8, 0x91, 0xfa, 0xc3, + 0xe8, 0xda, 0xff, 0xff, 0x36, 0x33, 0x33, 0xff, + 0x36, 0xdc, 0x1c, 0xff, 0x36, 0xdd, 0x1c, 0xc6, + 0x06, 0x33, 0x33, 0x00, 0xc6, 0x06, 0xdc, 0x1c, + 0x03, 0xc6, 0x06, 0xdd, 0x1c, 0x03, 0xfe, 0x0e, + 0x07, 0x66, 0xe8, 0xae, 0x80, 0xe8, 0xa3, 0x7d, + 0xe8, 0x0c, 0x7f, 0xe8, 0xb2, 0xf8, 0xe8, 0xf8, + 0x85, 0xe8, 0xab, 0x7f, 0xe8, 0x70, 0xf7, 0x8f, + 0x06, 0xdd, 0x1c, 0x8f, 0x06, 0xdc, 0x1c, 0x8f, + 0x06, 0x33, 0x33, 0xc3, 0xe8, 0x96, 0xff, 0xff, + 0x36, 0x33, 0x33, 0xff, 0x36, 0xdc, 0x1c, 0xff, + 0x36, 0xdd, 0x1c, 0xc6, 0x06, 0x33, 0x33, 0x00, + 0xc6, 0x06, 0xdc, 0x1c, 0x03, 0xc6, 0x06, 0xdd, + 0x1c, 0x03, 0xe8, 0xe1, 0x79, 0x8f, 0x06, 0xdd, + 0x1c, 0x8f, 0x06, 0xdc, 0x1c, 0x8f, 0x06, 0x33, + 0x33, 0xc3, 0xe8, 0x68, 0xff, 0xff, 0x36, 0x33, + 0x33, 0xff, 0x36, 0xdd, 0x1c, 0xc6, 0x06, 0x33, + 0x33, 0x00, 0xc6, 0x06, 0xdd, 0x1c, 0x03, 0xe8, + 0xbc, 0x79, 0x8f, 0x06, 0xdd, 0x1c, 0x8f, 0x06, + 0x33, 0x33, 0xc3, 0x06, 0xa1, 0xb5, 0x32, 0x8e, + 0xc0, 0xa3, 0xac, 0x00, 0xc7, 0x06, 0xaa, 0x00, + 0x00, 0x00, 0xe8, 0x13, 0x91, 0x53, 0x8a, 0x0f, + 0x0a, 0xc9, 0x74, 0x17, 0x80, 0xf9, 0xff, 0x74, + 0x16, 0xb5, 0x00, 0xba, 0x4c, 0x00, 0xe8, 0x87, + 0x00, 0xa1, 0xb3, 0x32, 0x8b, 0x1e, 0xaa, 0x00, + 0xe8, 0xb9, 0x6a, 0x5b, 0x43, 0xeb, 0xde, 0x5b, + 0x07, 0xc3, 0x06, 0x8b, 0x16, 0xb5, 0x32, 0x8e, + 0xc2, 0x89, 0x16, 0xac, 0x00, 0xc7, 0x06, 0xaa, + 0x00, 0x00, 0x00, 0x8a, 0xc8, 0xb5, 0x00, 0xba, + 0x4c, 0x00, 0xe8, 0x5b, 0x00, 0xa1, 0xb3, 0x32, + 0x8b, 0x1e, 0xaa, 0x00, 0xe8, 0x8d, 0x6a, 0x07, + 0xc3, 0xe8, 0xd6, 0xff, 0xe8, 0x81, 0xfc, 0xc3, + 0xb8, 0x00, 0x3d, 0xb1, 0x00, 0xcd, 0x21, 0x72, + 0x3c, 0xa3, 0xa2, 0x00, 0xb8, 0x02, 0x42, 0x33, + 0xc9, 0x33, 0xd2, 0x8b, 0x1e, 0xa2, 0x00, 0xcd, + 0x21, 0x72, 0x2a, 0x8b, 0xd8, 0xb0, 0x33, 0x0b, + 0xd2, 0x75, 0x22, 0x89, 0x1e, 0xa4, 0x00, 0xb8, + 0x00, 0x42, 0x33, 0xc9, 0x33, 0xd2, 0x8b, 0x1e, + 0xa2, 0x00, 0xcd, 0x21, 0x72, 0x0f, 0xb4, 0x3e, + 0x8b, 0x1e, 0xa2, 0x00, 0xcd, 0x21, 0x72, 0x05, + 0x8b, 0x0e, 0xa4, 0x00, 0xc3, 0xe9, 0xf8, 0x03, + 0x51, 0xb8, 0x00, 0x3d, 0xb1, 0x00, 0xcd, 0x21, + 0x72, 0x6a, 0xa3, 0xa2, 0x00, 0x59, 0xb8, 0x04, + 0x00, 0xf7, 0xe1, 0x8b, 0xca, 0x8b, 0xd0, 0x8b, + 0x1e, 0xa2, 0x00, 0xb8, 0x00, 0x42, 0xcd, 0x21, + 0x72, 0x52, 0x8b, 0x1e, 0xa2, 0x00, 0xb9, 0x08, + 0x00, 0xba, 0xae, 0x00, 0xb4, 0x3f, 0xcd, 0x21, + 0x72, 0x42, 0xbb, 0xae, 0x00, 0x8b, 0x17, 0x8b, + 0x4f, 0x02, 0x8b, 0x47, 0x04, 0x2b, 0xc2, 0xa3, + 0xa4, 0x00, 0x8b, 0x1e, 0xa2, 0x00, 0xb8, 0x00, + 0x42, 0xcd, 0x21, 0x72, 0x27, 0x8b, 0x1e, 0xa2, + 0x00, 0x8b, 0x0e, 0xa4, 0x00, 0x8b, 0x16, 0xaa, + 0x00, 0xa1, 0xac, 0x00, 0x8e, 0xd8, 0xb4, 0x3f, + 0xcd, 0x21, 0x72, 0x10, 0xb8, 0x3b, 0x0b, 0x8e, + 0xd8, 0xb4, 0x3e, 0x8b, 0x1e, 0xa2, 0x00, 0xcd, + 0x21, 0x72, 0x01, 0xc3, 0xe9, 0x81, 0x03, 0xb8, + 0x00, 0x3d, 0xb1, 0x00, 0xcd, 0x21, 0x72, 0x54, + 0xa3, 0xa2, 0x00, 0xb8, 0x02, 0x42, 0x33, 0xc9, + 0x33, 0xd2, 0x8b, 0x1e, 0xa2, 0x00, 0xcd, 0x21, + 0x72, 0x42, 0x8b, 0xd8, 0xb0, 0x33, 0x0b, 0xd2, + 0x75, 0x3a, 0x89, 0x1e, 0xa4, 0x00, 0xb8, 0x00, + 0x42, 0x33, 0xc9, 0x33, 0xd2, 0x8b, 0x1e, 0xa2, + 0x00, 0xcd, 0x21, 0x72, 0x27, 0x8b, 0x1e, 0xa2, + 0x00, 0x8b, 0x0e, 0xa4, 0x00, 0x8b, 0x16, 0xaa, + 0x00, 0xa1, 0xac, 0x00, 0x8e, 0xd8, 0xb4, 0x3f, + 0xcd, 0x21, 0x72, 0x10, 0xb8, 0x3b, 0x0b, 0x8e, + 0xd8, 0xb4, 0x3e, 0x8b, 0x1e, 0xa2, 0x00, 0xcd, + 0x21, 0x72, 0x01, 0xc3, 0xe9, 0x21, 0x03, 0xb8, + 0x00, 0x3d, 0xb1, 0x00, 0xcd, 0x21, 0x72, 0x3b, + 0xa3, 0xa2, 0x00, 0xb8, 0x00, 0x42, 0x33, 0xc9, + 0x8b, 0x16, 0xa8, 0x00, 0x8b, 0x1e, 0xa2, 0x00, + 0xcd, 0x21, 0x72, 0x27, 0x8b, 0x1e, 0xa2, 0x00, + 0x8b, 0x0e, 0xa6, 0x00, 0x8b, 0x16, 0xaa, 0x00, + 0xa1, 0xac, 0x00, 0x8e, 0xd8, 0xb4, 0x3f, 0xcd, + 0x21, 0x72, 0x10, 0xb8, 0x3b, 0x0b, 0x8e, 0xd8, + 0xb4, 0x3e, 0x8b, 0x1e, 0xa2, 0x00, 0xcd, 0x21, + 0x72, 0x01, 0xc3, 0xe9, 0xda, 0x02, 0x51, 0xb4, + 0x3c, 0xb1, 0x00, 0xcd, 0x21, 0x59, 0x72, 0x26, + 0xa3, 0xa2, 0x00, 0x8b, 0x1e, 0xa2, 0x00, 0x8b, + 0x16, 0xaa, 0x00, 0xa1, 0xac, 0x00, 0x8e, 0xd8, + 0xb4, 0x40, 0xcd, 0x21, 0x72, 0x10, 0xb8, 0x3b, + 0x0b, 0x8e, 0xd8, 0xb4, 0x3e, 0x8b, 0x1e, 0xa2, + 0x00, 0xcd, 0x21, 0x72, 0x01, 0xc3, 0xe9, 0xa7, + 0x02, 0xba, 0x13, 0x00, 0xb8, 0x00, 0x3d, 0xb1, + 0x00, 0xcd, 0x21, 0x72, 0x19, 0x8b, 0xd8, 0xb4, + 0x3e, 0xcd, 0x21, 0x72, 0x73, 0x8c, 0x1e, 0xac, + 0x00, 0xb8, 0x42, 0x32, 0xa3, 0xaa, 0x00, 0xba, + 0x13, 0x00, 0xe8, 0x02, 0xff, 0xc3, 0x3d, 0x02, + 0x00, 0x75, 0x5d, 0xb8, 0x03, 0x00, 0xcd, 0x10, + 0x8c, 0xc8, 0x8e, 0xd8, 0xba, 0x00, 0xb1, 0xb4, + 0x09, 0xcd, 0x21, 0xb8, 0x00, 0x4c, 0xcd, 0x21, + 0x53, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x63, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x20, 0x66, 0x69, 0x6c, 0x65, + 0x3a, 0x20, 0x53, 0x4f, 0x55, 0x4e, 0x44, 0x2e, + 0x53, 0x45, 0x54, 0x20, 0x6e, 0x6f, 0x74, 0x20, + 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x2e, 0x20, 0x50, + 0x6c, 0x65, 0x61, 0x73, 0x65, 0x20, 0x72, 0x75, + 0x6e, 0x20, 0x53, 0x4f, 0x55, 0x4e, 0x44, 0x53, + 0x45, 0x54, 0x2e, 0x45, 0x58, 0x45, 0x2e, 0x24, + 0xe9, 0x1d, 0x02, 0x89, 0x0e, 0x90, 0xdb, 0xb0, + 0x00, 0xb4, 0x05, 0xff, 0x1e, 0x4a, 0x32, 0xb8, + 0x3b, 0x0b, 0x8e, 0xd8, 0x8e, 0xc0, 0xc3, 0x00, + 0x00, 0x00, 0x00, 0x57, 0x8b, 0xc8, 0xbb, 0x5f, + 0xb1, 0x2e, 0x8b, 0x47, 0x02, 0x2e, 0x8b, 0x1f, + 0x8b, 0xf0, 0x8b, 0xfb, 0x8a, 0xd4, 0x8a, 0xe0, + 0x8a, 0xc7, 0x8a, 0xfb, 0x32, 0xdb, 0xd0, 0xda, + 0xd1, 0xd8, 0xd1, 0xdb, 0x03, 0xdf, 0x13, 0xc6, + 0x81, 0xc3, 0xe9, 0x62, 0x15, 0x19, 0x36, 0xbf, + 0x5f, 0xb1, 0x2e, 0x89, 0x1d, 0x2e, 0x89, 0x45, + 0x02, 0x33, 0xd2, 0xf7, 0xf1, 0x8b, 0xc2, 0x5f, + 0xc3, 0xb4, 0x62, 0xcd, 0x21, 0x53, 0xba, 0x28, + 0x25, 0x2b, 0xd3, 0x33, 0xc0, 0xe8, 0x1c, 0x00, + 0x05, 0x0f, 0x00, 0x83, 0xd2, 0x00, 0xbb, 0x10, + 0x00, 0xf7, 0xf3, 0x8b, 0xd8, 0x58, 0x06, 0x8e, + 0xc0, 0xb4, 0x4a, 0xcd, 0x21, 0x07, 0x72, 0x01, + 0xc3, 0xe9, 0x9c, 0x01, 0x53, 0xd1, 0xc2, 0xd1, + 0xc2, 0xd1, 0xc2, 0xd1, 0xc2, 0x8b, 0xda, 0x83, + 0xe2, 0x0f, 0x83, 0xe3, 0xf0, 0x03, 0xc3, 0x83, + 0xd2, 0x00, 0x5b, 0xc3, 0xbb, 0x06, 0xfa, 0xbe, + 0xb1, 0x32, 0xe8, 0x3e, 0x00, 0xbb, 0x00, 0xfd, + 0xbe, 0xb3, 0x32, 0xe8, 0x35, 0x00, 0xbb, 0x00, + 0xfa, 0xbe, 0xb5, 0x32, 0xe8, 0x2c, 0x00, 0xbb, + 0x00, 0xfd, 0xbe, 0xbf, 0x32, 0xe8, 0x23, 0x00, + 0xbb, 0x88, 0x14, 0xbe, 0xc1, 0x32, 0xe8, 0x1a, + 0x00, 0x80, 0x3e, 0x42, 0x32, 0x06, 0x74, 0x12, + 0xbb, 0x4e, 0x80, 0xbe, 0xc3, 0x32, 0xe8, 0x0a, + 0x00, 0xbb, 0xb8, 0x88, 0xbe, 0xc5, 0x32, 0xe8, + 0x01, 0x00, 0xc3, 0x53, 0x56, 0xe8, 0x11, 0x00, + 0xb4, 0x48, 0xcd, 0x21, 0x5e, 0x59, 0x72, 0x06, + 0x89, 0x04, 0xe8, 0x19, 0x00, 0xc3, 0xe9, 0x27, + 0x01, 0x8b, 0xc3, 0x05, 0x0f, 0x00, 0x73, 0x04, + 0xb0, 0x38, 0xeb, 0xf2, 0x33, 0xd2, 0xbb, 0x10, + 0x00, 0xf7, 0xf3, 0x8b, 0xd8, 0xc3, 0x06, 0x8e, + 0xc0, 0x33, 0xc0, 0x33, 0xff, 0xfc, 0xf3, 0xaa, + 0x07, 0xc3, 0x06, 0xbe, 0xc7, 0x32, 0xb9, 0x01, + 0x00, 0x51, 0x56, 0x49, 0xb0, 0x1b, 0xf6, 0xe1, + 0x03, 0xf0, 0x8b, 0x44, 0x16, 0x0b, 0xc0, 0x74, + 0x0b, 0x8e, 0xc0, 0xb4, 0x49, 0xcd, 0x21, 0x73, + 0x03, 0xe9, 0xe4, 0x00, 0x5e, 0x59, 0x41, 0x80, + 0xf9, 0x04, 0x76, 0xdd, 0x07, 0xc3, 0xa0, 0x42, + 0x32, 0x3c, 0x01, 0x74, 0x1d, 0x3c, 0x06, 0x74, + 0x19, 0x3c, 0x07, 0x74, 0x15, 0x3c, 0x03, 0x74, + 0x11, 0x3c, 0x04, 0x74, 0x0d, 0xc7, 0x06, 0x84, + 0x32, 0x6c, 0x00, 0xb8, 0x59, 0x00, 0xa3, 0x04, + 0x00, 0xc3, 0xc7, 0x06, 0x84, 0x32, 0x9c, 0x2e, + 0xb8, 0x0a, 0x00, 0xa3, 0x04, 0x00, 0xc3, 0x33, + 0xd2, 0xb8, 0xff, 0xff, 0xf7, 0x36, 0x84, 0x32, + 0xa3, 0x7f, 0x32, 0xa3, 0x81, 0x32, 0xc3, 0xb0, + 0x36, 0xe6, 0x43, 0xa1, 0x84, 0x32, 0xe6, 0x40, + 0x8a, 0xc4, 0xe6, 0x40, 0xc3, 0xb0, 0x36, 0xe6, + 0x43, 0x32, 0xc0, 0xe6, 0x40, 0xe6, 0x40, 0xc3, + 0x06, 0xb8, 0x08, 0x35, 0xcd, 0x21, 0x89, 0x1e, + 0x00, 0x00, 0x8c, 0x06, 0x02, 0x00, 0x07, 0x8b, + 0x16, 0x04, 0x00, 0x1e, 0x0e, 0x1f, 0xb8, 0x08, + 0x25, 0xcd, 0x21, 0x1f, 0xc3, 0x1e, 0x8b, 0x16, + 0x00, 0x00, 0x8b, 0x1e, 0x02, 0x00, 0x8e, 0xdb, + 0xb8, 0x08, 0x25, 0xcd, 0x21, 0x1f, 0xc3, 0x06, + 0xb8, 0x09, 0x35, 0xcd, 0x21, 0x2e, 0x89, 0x1e, + 0xd8, 0x00, 0x2e, 0x8c, 0x06, 0xda, 0x00, 0x07, + 0x1e, 0x0e, 0x1f, 0xba, 0xba, 0x00, 0xb8, 0x09, + 0x25, 0xcd, 0x21, 0x1f, 0xc3, 0x1e, 0x2e, 0x8b, + 0x16, 0xd8, 0x00, 0x2e, 0x8b, 0x1e, 0xda, 0x00, + 0x8e, 0xdb, 0xb8, 0x09, 0x25, 0xcd, 0x21, 0x1f, + 0xc3, 0xe8, 0x42, 0xff, 0xe8, 0xc8, 0xff, 0xfa, + 0xb0, 0xff, 0xe6, 0x21, 0xe8, 0x91, 0xff, 0xe8, + 0x75, 0xff, 0xb0, 0x00, 0xe6, 0x21, 0xfb, 0xb8, + 0x13, 0x00, 0xcd, 0x10, 0xe8, 0x58, 0xff, 0xc3, + 0x50, 0xb8, 0x03, 0x00, 0xcd, 0x10, 0x58, 0xb4, + 0x00, 0xbb, 0xa6, 0x2f, 0xd1, 0xe0, 0x03, 0xd8, + 0x8b, 0x17, 0xb4, 0x09, 0xcd, 0x21, 0x80, 0x3e, + 0x42, 0x32, 0x00, 0x74, 0x0d, 0xb4, 0x02, 0xff, + 0x1e, 0x4a, 0x32, 0xb8, 0x3b, 0x0b, 0x8e, 0xd8, + 0x8e, 0xc0, 0xeb, 0x00, 0xfa, 0xe4, 0x21, 0x50, + 0xb0, 0xff, 0xe6, 0x21, 0xfa, 0xe8, 0x3d, 0xff, + 0xe8, 0x62, 0xff, 0xe8, 0x8f, 0xff, 0x58, 0xe6, + 0x21, 0xfb, 0xb8, 0x00, 0x4c, 0xcd, 0x21, 0x00 +}; + +// Data Segment +// starts at offset 0xb5b0 in original executable +#define DSEG_STARTBLK_SIZE 13214 + +const static uint8 dsegStartBlock[DSEG_STARTBLK_SIZE] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x73, 0x6f, 0x75, 0x6e, 0x64, + 0x2e, 0x73, 0x65, 0x74, 0x00, 0x6f, 0x66, 0x66, + 0x2e, 0x72, 0x65, 0x73, 0x00, 0x6f, 0x6e, 0x2e, + 0x72, 0x65, 0x73, 0x00, 0x6c, 0x61, 0x6e, 0x5f, + 0x35, 0x30, 0x30, 0x2e, 0x72, 0x65, 0x73, 0x00, + 0x6c, 0x61, 0x6e, 0x5f, 0x30, 0x30, 0x30, 0x2e, + 0x72, 0x65, 0x73, 0x00, 0x73, 0x64, 0x72, 0x2e, + 0x72, 0x65, 0x73, 0x00, 0x6f, 0x6e, 0x73, 0x2e, + 0x72, 0x65, 0x73, 0x00, 0x76, 0x61, 0x72, 0x69, + 0x61, 0x2e, 0x72, 0x65, 0x73, 0x00, 0x6d, 0x6d, + 0x6d, 0x2e, 0x72, 0x65, 0x73, 0x00, 0x73, 0x61, + 0x6d, 0x5f, 0x73, 0x61, 0x6d, 0x2e, 0x72, 0x65, + 0x73, 0x00, 0x73, 0x61, 0x6d, 0x5f, 0x6d, 0x6d, + 0x6d, 0x2e, 0x72, 0x65, 0x73, 0x00, 0x75, 0x6e, + 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x2e, 0x72, 0x65, + 0x73, 0x00, 0x61, 0x64, 0x76, 0x65, 0x72, 0x74, + 0x2e, 0x72, 0x65, 0x73, 0x00, 0x74, 0x65, 0x65, + 0x6e, 0x61, 0x67, 0x65, 0x30, 0x2e, 0x73, 0x61, + 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xa0, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc7, 0x00, 0x00, 0x00, 0x3f, 0x01, + 0x00, 0x00, 0xdf, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0xdf, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0xdf, 0xff, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0xdf, 0xff, 0xff, 0x00, 0x01, 0x01, + 0x01, 0x01, 0xdf, 0xff, 0xff, 0xff, 0x00, 0x01, + 0x01, 0x01, 0xdf, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x01, 0x01, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x01, 0xdf, 0xff, 0xdf, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xdf, 0xdf, 0x01, 0xdf, 0xff, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x01, 0xdf, 0xff, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xdf, 0xff, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xdf, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x32, 0xe1, + 0x40, 0x01, 0x00, 0x00, 0x00, 0x00, 0xd1, 0xe0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x30, 0x25, 0x30, 0x34, 0x30, 0x39, 0x30, + 0x4d, 0x30, 0x52, 0x30, 0x57, 0x30, 0x76, 0x30, + 0x8a, 0x30, 0xa7, 0x30, 0xac, 0x30, 0xb1, 0x30, + 0xb6, 0x30, 0xbb, 0x30, 0xc0, 0x30, 0xc5, 0x30, + 0xca, 0x30, 0xcf, 0x30, 0xd4, 0x30, 0xd9, 0x30, + 0xde, 0x30, 0xe3, 0x30, 0xe8, 0x30, 0xed, 0x30, + 0xf2, 0x30, 0xf7, 0x30, 0xfc, 0x30, 0x01, 0x31, + 0x06, 0x31, 0x0b, 0x31, 0x10, 0x31, 0x15, 0x31, + 0x1a, 0x31, 0x1f, 0x31, 0x24, 0x31, 0x29, 0x31, + 0x2e, 0x31, 0x33, 0x31, 0x38, 0x31, 0x3d, 0x31, + 0x42, 0x31, 0x47, 0x31, 0x4c, 0x31, 0x51, 0x31, + 0x56, 0x31, 0x5b, 0x31, 0x60, 0x31, 0x65, 0x31, + 0x6a, 0x31, 0x6f, 0x31, 0xbd, 0x31, 0xc1, 0x31, + 0xc5, 0x31, 0xc9, 0x31, 0xcd, 0x31, 0xce, 0x31, + 0xd2, 0x31, 0xd6, 0x31, 0xda, 0x31, 0xde, 0x31, + 0x45, 0x30, 0x31, 0x68, 0x24, 0x46, 0x69, 0x6c, + 0x65, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x66, 0x6f, + 0x75, 0x6e, 0x64, 0x24, 0x45, 0x30, 0x33, 0x68, + 0x24, 0x54, 0x6f, 0x6f, 0x20, 0x6d, 0x61, 0x6e, + 0x79, 0x20, 0x6f, 0x70, 0x65, 0x6e, 0x20, 0x66, + 0x69, 0x6c, 0x65, 0x73, 0x24, 0x45, 0x30, 0x35, + 0x68, 0x24, 0x45, 0x30, 0x36, 0x68, 0x24, 0x4d, + 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x20, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x20, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x20, 0x64, 0x65, 0x73, 0x74, + 0x72, 0x6f, 0x79, 0x65, 0x64, 0x24, 0x49, 0x6e, + 0x73, 0x75, 0x66, 0x66, 0x69, 0x63, 0x69, 0x65, + 0x6e, 0x74, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x72, + 0x79, 0x24, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, + 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x61, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x69, + 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x24, 0x45, + 0x30, 0x41, 0x68, 0x24, 0x45, 0x30, 0x42, 0x68, + 0x24, 0x45, 0x30, 0x43, 0x68, 0x24, 0x45, 0x30, + 0x44, 0x68, 0x24, 0x45, 0x30, 0x45, 0x68, 0x24, + 0x45, 0x30, 0x46, 0x68, 0x24, 0x45, 0x31, 0x30, + 0x68, 0x24, 0x45, 0x31, 0x31, 0x68, 0x24, 0x45, + 0x31, 0x32, 0x68, 0x24, 0x45, 0x31, 0x33, 0x68, + 0x24, 0x45, 0x31, 0x34, 0x68, 0x24, 0x45, 0x31, + 0x35, 0x68, 0x24, 0x45, 0x31, 0x36, 0x68, 0x24, + 0x45, 0x31, 0x37, 0x68, 0x24, 0x45, 0x31, 0x38, + 0x68, 0x24, 0x45, 0x31, 0x39, 0x68, 0x24, 0x45, + 0x31, 0x41, 0x68, 0x24, 0x45, 0x31, 0x42, 0x68, + 0x24, 0x45, 0x31, 0x43, 0x68, 0x24, 0x45, 0x31, + 0x44, 0x68, 0x24, 0x45, 0x31, 0x45, 0x68, 0x24, + 0x45, 0x31, 0x46, 0x68, 0x24, 0x45, 0x32, 0x30, + 0x68, 0x24, 0x45, 0x32, 0x31, 0x68, 0x24, 0x45, + 0x32, 0x32, 0x68, 0x24, 0x45, 0x32, 0x33, 0x68, + 0x24, 0x45, 0x32, 0x34, 0x68, 0x24, 0x45, 0x32, + 0x35, 0x68, 0x24, 0x45, 0x32, 0x36, 0x68, 0x24, + 0x45, 0x32, 0x37, 0x68, 0x24, 0x45, 0x32, 0x38, + 0x68, 0x24, 0x45, 0x32, 0x39, 0x68, 0x24, 0x45, + 0x32, 0x41, 0x68, 0x24, 0x45, 0x32, 0x42, 0x68, + 0x24, 0x45, 0x32, 0x43, 0x68, 0x24, 0x45, 0x32, + 0x44, 0x68, 0x24, 0x45, 0x32, 0x45, 0x68, 0x24, + 0x45, 0x32, 0x46, 0x68, 0x24, 0x45, 0x33, 0x30, + 0x68, 0x24, 0x45, 0x33, 0x31, 0x68, 0x24, 0x44, + 0x75, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x6e, + 0x74, 0x69, 0x2d, 0x76, 0x69, 0x72, 0x75, 0x73, + 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x61, 0x64, + 0x65, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x68, 0x61, + 0x76, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x69, 0x6e, + 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x20, 0x74, 0x68, + 0x69, 0x73, 0x20, 0x67, 0x61, 0x6d, 0x65, 0x20, + 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x6f, 0x72, 0x69, + 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x20, 0x64, 0x69, + 0x73, 0x6b, 0x73, 0x2e, 0x24, 0x47, 0x45, 0x31, + 0x24, 0x47, 0x45, 0x32, 0x24, 0x47, 0x45, 0x33, + 0x24, 0x47, 0x45, 0x34, 0x24, 0x24, 0x47, 0x45, + 0x35, 0x24, 0x47, 0x45, 0x36, 0x24, 0x47, 0x45, + 0x37, 0x24, 0x47, 0x45, 0x38, 0x24, 0x47, 0x45, + 0x39, 0x24, 0x43, 0x61, 0x6c, 0x6c, 0x20, 0x79, + 0x6f, 0x75, 0x72, 0x73, 0x65, 0x6c, 0x66, 0x20, + 0x70, 0x69, 0x72, 0x61, 0x74, 0x65, 0x2c, 0x20, + 0x68, 0x61, 0x63, 0x6b, 0x65, 0x72, 0x2c, 0x20, + 0x65, 0x6c, 0x69, 0x74, 0x65, 0x20, 0x6f, 0x72, + 0x20, 0x77, 0x68, 0x61, 0x74, 0x65, 0x76, 0x65, + 0x72, 0x2e, 0x20, 0x42, 0x75, 0x74, 0x20, 0x79, + 0x6f, 0x75, 0x20, 0x61, 0x72, 0x65, 0x20, 0x61, + 0x20, 0x54, 0x48, 0x49, 0x45, 0x46, 0x2e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4d, + 0x6f, 0x75, 0x73, 0x65, 0x20, 0x6e, 0x6f, 0x74, + 0x20, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x3a, 0x20, + 0x75, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x6b, 0x65, + 0x79, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x2e, 0x2e, + 0x2e, 0x0d, 0x0a, 0x50, 0x72, 0x65, 0x73, 0x73, + 0x20, 0x61, 0x6e, 0x79, 0x20, 0x6b, 0x65, 0x79, + 0x20, 0x74, 0x6f, 0x20, 0x63, 0x6f, 0x6e, 0x74, + 0x69, 0x6e, 0x75, 0x65, 0x2e, 0x24 +}; + +const static char* messages[333] = { + "I have no idea what to do with it.", // 0 + "I can't imagine what I could do with\nthis.", // 1 + "I can't figure out what I should do\nwith this.", // 2 + "I can't find any reason to mess\nwith it.", // 3 + "Cool.", // 4 + "That's no good.", // 5 + "Wow! There's a car jack inside!\nGreat!", // 6 + "There's something else inside the toolbox!\nA spanner!", // 7 + "Last\nchance?", // 8 + "I give up.", // 9 + "I'm going to stay at least five meters\naway from these bees!", // 10 + "There's nothing else in the boat.", // 11 + "This wood is too hard to break.", // 12 + "Booo!", // 13 + "I don't think I should push\nmy luck.", // 14 + "Just an ordinary hay stack. Now.", // 15 + "And they say you can't find a needle\nin a hay stack.", // 16 + "There are no more\npotatoes.", // 17 + "Good I always asked mum for\ntrousers with BIG pockets...", // 18 + "Life is brutal.", // 19 + "Life is really brutal.", // 20 + "Something tickled\nme!", // 21 + "At least it's gone.", // 22 + "Who knows what monsters\nmay live in there...", // 23 + "I'd better not put my hands in there.\nSomething might bite them off\n(yuck)!", // 24 + "I can see it's totally\nempty.", // 25 + "One small step for man,\none big pain in the ...head...", // 26 + "I won't take my chances\na second time...", // 27 + "I really hope this is DINOSAUR\nbone...", // 28 + "Wow! This must have shaken\nall the nearby walls!", // 29 + "It's kinda dark here.", // 30 + "I'm not going to wander here\nin the dark again.", // 31 + "Shutting the valve shook the dirt from the wall\nand revealed the switch!", // 32 + "Sorry, buddy, but I need your\nsunglasses.", // 33 + "It's not the best place for diving...", // 34 + "Not here...", // 35 + "I really can't talk underwater!...", // 36 + "I don't think swimming there is worth the effort.", // 37 + "If I want to get this anchor I have to swim there\nwhen I have more air in my lungs...", // 38 + "I was really hooked on this anchor!", // 39 + "This seaweed is just like the flowers\nI gave mum on her last birthday.", // 40 + "I wonder what fish do inside\nthis boat at night.", // 41 + "I think I have to fish out something down\nthere.", // 42 + "At least fish don't worry about the rain.", // 43 + "I hope all this fish stuff is not a red herring.", // 44 + "It's nice down there.", // 45 + "Hey, let go, will ya?!", // 46 + "Aaaaaaaaaaaaaahhh!...", // 47 + "Oops.", // 48 + "People leave food in unbelievable places.", // 49 + "Come here, I've got something for\nyou...", // 50 + "I can't catch it!", // 51 + "The mouse is trapped!", // 52 + "Yikes!", // 53 + "Boy, this mouse has some nerve!", // 54 + "There's nothing else in the drawers.", // 55 + "I must get rid of this bush first.", // 56 + "The mouse has gone!", // 57 + "Nonsense.", // 58 + "I understand. Good doggy.", // 59 + "Here, boy.", // 60 + "I hope we're friends now.", // 61 + "I don't think this is the right place.", // 62 + "Hundred moments later...", // 63 + "Another hundred moments later...", // 64 + "At least I found crude-oil and I'll be\nrich.", // 65 + "That's my life.", // 66 + "!?&!", // 67 + "But grandpa, you promised!...", // 68 + "Oh, all right. Let's go.", // 69 + "Bye.", // 70 + "No need to do it again.", // 71 + "I really don't know how to talk to\ngirls...", // 72 + "I usually don't work without a purpose.", // 73 + "Only the nut is real.", // 74 + "I wonder if hens can fly. Come here,\nbaby...", // 75 + "First test failed.", // 76 + "I'd already got rid of my frustrations.", // 77 + "Nah, it's a road to nowhere.", // 78 + "It opens the boot.", // 79 + "It's shut tight.", // 80 + "There's nothing else in the boot.", // 81 + "The clothes are dry now.", // 82 + "I'm sure these crows will kill me!", // 83 + "If I want to get inside I must get rid of\nthis guard first or find some other\nway.", // 84 + "The wall surface is too smooth to climb.", // 85 + "I could climb it\nif there wasn't\nso much resin.", // 86 + "The only green stuff I like is that rectangular\npiece of paper with some important-looking\nguy on it.", // 87 + "I don't wanna touch it. Its spines could\nhurt my delicate hands.", // 88 + "Thanks, I'm not hungry.", // 89 + "I really don't have such long hands.", // 90 + "It's too far to swim there.", // 91 + "Echo!", // 92 + "ECHO!", // 93 + "Who's there?!", // 94 + "WHO'S THERE?!", // 95 + " DON'T COPY ME!...", // 96 + " DON'T COPY ME!!!", // 97 + "...OR I WILL THROW A ROCK DOWN THERE!", // 98 + "OR I WILL...", // 99 + "Are you still there?", // 100 + "It's not a barrel-organ. And there's\nno bucket.", // 101 + "I don't need to open it.", // 102 + "Hmmm... Grass... Nah, children might\nbe watching.", // 103 + "I won't find the nut just like that.\nThe grass is too dense.", // 104 + "I'm not horny.", // 105 + "No way I can jump so high,\ncause, er..., white men\ncan't jump.", // 106 + "I don't need it.", // 107 + "I'm not Santa Claus.", // 108 + "I don't need plastic imitations.", // 109 + "It's too fragile to carry around.", // 110 + "I'd like to keep it open.", // 111 + "I really don't want to walk around with\nsomeone else's socks.", // 112 + "Thanks, I'm not tired.", // 113 + "It's too big and I doubt if I'll ever\nneed it.", // 114 + "I don't think there's any secret passage\ninside.", // 115 + "There are no more interesting fruits here.", // 116 + "They can jug me if I steal this.", // 117 + "I'd better leave it. Women are really\noversensitive about flowers.", // 118 + "Mirror, mirror on the wall,\nwho's the smartest of them all?", // 119 + "Hey, don't think too long.", // 120 + "A hint: someone in this room,\na male.", // 121 + "OK, take your time.", // 122 + "I'd better not interrupt its\nthought process.", // 123 + "I don't want to have anything in common\nwith dentists.", // 124 + "It's too heavy. Not that I'm wimp.", // 125 + "Let's look what we've got here...", // 126 + "'Strawberry jam'.", // 127 + "'Gooseberry jam'.", // 128 + "'Blackberry jam'.", // 129 + "'Bilberry jam'.", // 130 + "Get me out of this jam!", // 131 + "Oh, and there is 'Rosemary jam'.", // 132 + "I used to know someone called Rosemary.", // 133 + "I don't want those jams.", // 134 + "It's too dark to see clearly.", // 135 + "YEEEOOOWWWW!", // 136 + "(yawn)", // 137 + "(laughter)", // 138 + "I can't remove it with my hands - these\nthorns look really sharp.", // 139 + "There's no fuel in the chainsaw.", // 140 + "Thorns are too thin, the chainsaw\nis useless here.", // 141 + "Yeah, great idea. Let's take this rock and\nwalk around a bit. Gee...", // 142 + "I'd better leave them alone, they make\nthis place beautiful.", // 143 + "I'm not sure if it's alive.", // 144 + "I don't know what language it speaks.", // 145 + "The hole is too narrow to fit my hand.", // 146 + "Hey, you! Wake up! Bird attack!", // 147 + "I don't have a search-warrant.", // 148 + "I don't see anything interesting\nabout this hay stack.", // 149 + "It's more complicated than that.", // 150 + "It's pointless, the nut will slip between\nthe rake's teeth.", // 151 + "The paddle is BROKEN.", // 152 + "This branch is not a paddle. It doesn't\neven look like one.", // 153 + "I'd better try somewhere else - I suppose this\nside is heavily guarded.", // 154 + "I needed to sharpen it, not pulverize.", // 155 + "I can't do anything here, it's too dark.", // 156 + "Here, let's make your\npocket fat.", // 157 + "It's a note from some bank. Strange,\nbut someone has also written \"NEVER! ANNE\"\non it.", // 158 + "If I just show her the money,\nshe might take it.", // 159 + "A hundred bucks!!!", // 160 + "I want blood!", // 161 + "I don't want to leave the mansion,\nI want blood!", // 162 + "I'm a pathetic little wimp.", // 163 + "Strange, but the drawer is stuck\nif the next drawer is open...", // 164 + "Maybe these are not just ordinary\ndrawers!", // 165 + "I cannot open the drawer\nif the next one is open!", // 166 + "It's got a blue interior.", // 167 + "It's got a red interior.", // 168 + "It's got a grey interior.", // 169 + "It's got a green interior.", // 170 + "It's got a brown interior.", // 171 + "It's got a pink interior.", // 172 + "Wow! There's a dictaphone inside!", // 173 + "There's a polaroid inside!\nI might need that.", // 174 + "Something's got hold of the book!", // 175 + "Wow! A secret compartment!", // 176 + "I don't need to mess with it\nanymore.", // 177 + "Fully automatic.", // 178 + "Right now I don't need any more\nsheets.", // 179 + "Nah, I don't want to deprave the kids.", // 180 + "I don't want to read it again.\nI might like it.", // 181 + "I just realised that the TV is off.", // 182 + "Nothing happened.", // 183 + "The tape started!", // 184 + "That's much better.", // 185 + "I don't want to sleep.", // 186 + "It's just a cork.", // 187 + "I don't need any more photos.", // 188 + "Yeah, I can record this and scare\nthe cats.", // 189 + "I already recorded what I wanted to.", // 190 + "I can't record anything until I find some\nbatteries.", // 191 + "No batteries, no fun.", // 192 + "I don't think this is the right moment.", // 193 + "I can't do anything with\nthis cook around.", // 194 + "The bottle's the same, but\nI doubt if it's enough to\nfool anyone.", // 195 + "I wanted to break it, not to\nflatten it!..", // 196 + "I was always curious what's inside\nthese things.", // 197 + "The rest is useless.", // 198 + "Wow! Two 1.5V batteries!", // 199 + "This one's taken, OK?", // 200 + "It finally happened. I'm slightly mad.", // 201 + "The paper burnt out completely!", // 202 + "Burn, baby, burn!", // 203 + "Voila.", // 204 + "It's too hot to touch!", // 205 + "It has frozen hard onto the shelf!", // 206 + "Yummy.", // 207 + "I never liked veal anyway.", // 208 + "There's no reason to do it.", // 209 + "I'd already fooled him once.", // 210 + "Mike, activate the voice test.", // 211 + "I won't cheat Mike with MY voice.", // 212 + "...siiiiinging!...", // 213 + "Mike, let's get on with the scent\ntest.", // 214 + "Mike, run the view test.", // 215 + "'A secret diary of John Noty. Authorised.'", // 216 + "I can't hide here!", // 217 + "There's John Noty outside! I can't go out!", // 218 + "That was close.", // 219 + "The cork is stuck in the hole.", // 220 + "It fits perfectly!", // 221 + "There's enough water in the sink.", // 222 + "There's no hot water in the sink.", // 223 + "The label has come off!", // 224 + "The cork is a bit too small.", // 225 + "There's no need to try them now.", // 226 + "I don't want to turn myself into a salad.", // 227 + "Nah...", // 228 + "I'd better stop this ventilator first.", // 229 + "I'd better catch John Noty first.", // 230 + "Good this red stuff is only a chilli...", // 231 + "The water looks very hot.", // 232 + "The sink is full of hot water.", // 233 + "I don't have anything\nto store these socks in.", // 234 + "Here are my papers.", // 235 + "I already got the permission.", // 236 + "'Saving is a very fine thing. Especially\nwhen your parents have done it for you'", // 237 + "'I love captain'", // 238 + "'Soccer rulz'", // 239 + "'Don't cut the trees, cuz one\nday you may become partisan\ntoo'", // 240 + "'VISA accepted'", // 241 + "The rest of graffiti is obscene.", // 242 + "Sir, I'm Mark. A rookie.", // 243 + "It's locked!", // 244 + "Thanks.", // 245 + "I don't have any idea what\nto do with it right\nnow.", // 246 + "That gives me an idea...", // 247 + "Now I got to check if it works...", // 248 + "I think it's time to call captain...", // 249 + "Hey! I finished the meal!", // 250 + "Wow, he got welded to the bowl!...", // 251 + "Gotcha.", // 252 + "I don't want to touch\nhis pockets again.", // 253 + "That doesn't work.", // 254 + "Piece of cake.", // 255 + "And how am I supposed to get back?", // 256 + "Great.", // 257 + "Oh, yeah, right...", // 258 + "I can't pull it out.", // 259 + "I don't want to touch it - I might get hurt.", // 260 + "The fence blocks the way...", // 261 + "I don't want to sleep.", // 262 + "I can't reach it.", // 263 + "Hello?", // 264 + "He's totally addicted.", // 265 + "What about a new...", // 266 + "...hot off the press...", // 267 + "...full-color...", // 268 + "...special edition...", // 269 + "... of 'Soldier News'?!", // 270 + "Never again!", // 271 + "What am I? A vacuum cleaner?!", // 272 + " Sixty seven rude words later...", // 273 + "Meanwhile in the mansion...", // 274 + "Now it's open.", // 275 + "C'mon, baby, it's all yours!", // 276 + "I've got no reason to talk to him\nright now.", // 277 + "Yeah, right!...", // 278 + "The barman is too close...", // 279 + "Yuck!", // 280 + "I prefer water.", // 281 + "I'm too weak to climb it.", // 282 + "The springs would prick my back.", // 283 + "No, thanks. This food seems still alive.", // 284 + "The door is closed. What a surprise.", // 285 + "It's empty.", // 286 + "I should have paid more attention\nin geography classes...", // 287 + "I don't need this mess.", // 288 + "Thanks, but I've seen softer rocks.", // 289 + "They are too blunt to be of any use.", // 290 + "What's the use of the models?", // 291 + "The barman will surely notice its\ndisappearing.", // 292 + "It'd take too much time to drink it all.\nMaybe after the mission...", // 293 + "\n\nI'm not a thief. And it's empty, by the way.", // 294 + "There are too many of them to search.", // 295 + "Captain surely wouldn't fit them.\nI must look elsewhere.", // 296 + "Chickening? Me? Never!", // 297 + "I can't open it.", // 298 + "I don't need them.", // 299 + "What am I? A Peeping Tom?", // 300 + "I have big pockets, but there are limits.", // 301 + "If I put it on I might have trouble walking\nup the stairs.", // 302 + "I'd need 9 lives to read them all.", // 303 + "Thanks, I'm not so tired.", // 304 + "There's no need to turn it on.", // 305 + "It won't bear my weight.", // 306 + "I never learnt to how use one...", // 307 + "They're so sharp they'd rip my trousers!", // 308 + "Pfui! The cognac really didn't do any good...", // 309 + "I don't have time for pleasures.", // 310 + "I won't touch these socks with my bare hands!", // 311 + "It's not Halloween.", // 312 + "It can't be controlled manually! I hate it!", // 313 + "I have nothing to play.", // 314 + "I can't take it. It's not mine.", // 315 + "Hey! What's the matter?!", // 316 + "It's open!", // 317 + "It's out of order.", // 318 + "With captain watching?...\nBetter not...", // 319 + "The sickle is too blunt.", // 320 + "First I've got some business to take care of.", // 321 + "Digging it out with the knife could\ntake a hundred years.", // 322 + "I don't want to make more\nmess on this table.", // 323 + "Should I throw the crumbs to the bird?", // 324 + "I don't want to waste these tasty crumbs.", // 325 + "Better not... I might slip and fall in...", // 326 + "\"The history of blues\".", // 327 + "\"Manchester United, or the Red Devils story\".", // 328 + "\"Greyhounds and other hunting dogs\".", // 329 + "\"Greenhorn, or my adventures in the Wild West\".", // 330 + "\"Charlie Brown and his company\".", // 331 + "\"Pink Panther: an unauthorised biography\"." // 332 +}; + +#define DSEG_ENDBLK_SIZE 34651 + +const static uint8 dsegEndBlock[DSEG_ENDBLK_SIZE] = { + 0x90, 0x9d, 0xe5, 0x9d, 0x54, 0x9e, 0xc3, 0x9e, + 0x3e, 0x9f, 0x0b, 0x30, 0x02, 0x31, 0x03, 0x32, + 0x04, 0x33, 0x05, 0x34, 0x06, 0x35, 0x07, 0x36, + 0x08, 0x37, 0x09, 0x38, 0x0a, 0x39, 0x0c, 0x2d, + 0x1e, 0x41, 0x30, 0x42, 0x2e, 0x43, 0x20, 0x44, + 0x12, 0x45, 0x21, 0x46, 0x22, 0x47, 0x23, 0x48, + 0x17, 0x49, 0x24, 0x4a, 0x25, 0x4b, 0x26, 0x4c, + 0x32, 0x4d, 0x31, 0x4e, 0x18, 0x4f, 0x19, 0x50, + 0x10, 0x51, 0x13, 0x52, 0x1f, 0x53, 0x14, 0x54, + 0x16, 0x55, 0x2f, 0x56, 0x11, 0x57, 0x2d, 0x58, + 0x15, 0x59, 0x2c, 0x5a, 0x33, 0x2c, 0x34, 0x2e, + 0x35, 0x3f, 0x39, 0x20, 0x28, 0x27, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7c, 0x0a, 0x48, 0x01, + 0x00, 0x7c, 0x19, 0x48, 0x02, 0x00, 0xaf, 0x30, + 0x0e, 0x03, 0x02, 0xbe, 0x30, 0x26, 0x03, 0x03, + 0xe5, 0x30, 0x0e, 0x03, 0x01, 0xaf, 0x3f, 0x0e, + 0x04, 0x02, 0xbe, 0x3f, 0x26, 0x04, 0x03, 0xe5, + 0x3f, 0x0e, 0x04, 0x01, 0x9f, 0x4e, 0x0e, 0x05, + 0x02, 0xae, 0x4e, 0x36, 0x05, 0x03, 0xe5, 0x4e, + 0x0e, 0x05, 0x01, 0x9f, 0x5d, 0x0e, 0x06, 0x02, + 0xae, 0x5d, 0x36, 0x06, 0x03, 0xe5, 0x5d, 0x0e, + 0x06, 0x01, 0x9f, 0x6c, 0x0e, 0x07, 0x02, 0xae, + 0x6c, 0x36, 0x07, 0x03, 0xe5, 0x6c, 0x0e, 0x07, + 0x01, 0xa7, 0x7b, 0x0e, 0x08, 0x02, 0xb6, 0x7b, + 0x2e, 0x08, 0x03, 0xe5, 0x7b, 0x0e, 0x08, 0x01, + 0x8d, 0x94, 0x25, 0x0b, 0x00, 0x6c, 0xa3, 0x69, + 0x0a, 0x00, 0x79, 0xb2, 0x50, 0x0d, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x81, 0x0f, 0x41, 0x22, + 0xd3, 0x3e, 0x92, 0x51, 0x50, 0x64, 0x0e, 0x77, + 0xd0, 0x89, 0x8a, 0x9c, 0x00, 0x00, 0x52, 0xbd, + 0xb1, 0xce, 0x81, 0xe1, 0x4c, 0x6f, 0x61, 0x64, + 0x20, 0x67, 0x61, 0x6d, 0x65, 0x00, 0x53, 0x61, + 0x76, 0x65, 0x20, 0x67, 0x61, 0x6d, 0x65, 0x00, + 0x4d, 0x75, 0x73, 0x69, 0x63, 0x20, 0x76, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x20, 0x20, 0x20, 0x20, + 0x2d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7f, 0x00, 0x53, + 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x76, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x20, 0x20, 0x20, 0x20, 0x2d, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7f, 0x00, 0x48, 0x65, + 0x72, 0x6f, 0x20, 0x73, 0x70, 0x65, 0x65, 0x64, + 0x20, 0x20, 0x20, 0x20, 0x2d, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7f, 0x00, 0x47, + 0x61, 0x6d, 0x65, 0x20, 0x73, 0x70, 0x65, 0x65, + 0x64, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7f, 0x00, + 0x54, 0x65, 0x78, 0x74, 0x20, 0x73, 0x70, 0x65, + 0x65, 0x64, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7f, + 0x00, 0x47, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, + 0x20, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x20, 0x20, + 0x20, 0x2d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7f, 0x00, 0x20, 0x00, 0x51, 0x75, 0x69, 0x74, + 0x00, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x74, 0x6f, 0x20, 0x67, 0x61, 0x6d, 0x65, 0x00, + 0x4f, 0x72, 0x64, 0x65, 0x72, 0x20, 0x69, 0x6e, + 0x66, 0x6f, 0x00, 0x00, 0x77, 0x64, 0x39, 0x77, + 0x0f, 0x8a, 0x44, 0x6f, 0x20, 0x79, 0x6f, 0x75, + 0x20, 0x72, 0x65, 0x61, 0x6c, 0x6c, 0x79, 0x00, + 0x77, 0x61, 0x6e, 0x74, 0x20, 0x74, 0x6f, 0x20, + 0x71, 0x75, 0x69, 0x74, 0x00, 0x28, 0x59, 0x2f, + 0x4e, 0x29, 0x3f, 0x00, 0x00, 0xf7, 0x57, 0x83, + 0x6a, 0x43, 0x7d, 0x03, 0x90, 0x46, 0x6f, 0x72, + 0x20, 0x69, 0x6e, 0x66, 0x6f, 0x20, 0x63, 0x61, + 0x6c, 0x6c, 0x3a, 0x00, 0x55, 0x4b, 0x3a, 0x20, + 0x50, 0x44, 0x20, 0x53, 0x65, 0x6c, 0x65, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x30, 0x31, + 0x34, 0x37, 0x34, 0x20, 0x33, 0x32, 0x35, 0x38, + 0x30, 0x32, 0x00, 0x55, 0x53, 0x41, 0x20, 0x61, + 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, + 0x65, 0x73, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, + 0x3a, 0x00, 0x55, 0x6e, 0x69, 0x6f, 0x6e, 0x20, + 0x4c, 0x6f, 0x67, 0x69, 0x63, 0x20, 0x28, 0x38, + 0x30, 0x30, 0x29, 0x20, 0x35, 0x38, 0x33, 0x2d, + 0x34, 0x38, 0x33, 0x38, 0x00, 0x00, 0x44, 0x5f, + 0x04, 0x77, 0xc4, 0x84, 0x84, 0x92, 0x49, 0x20, + 0x63, 0x61, 0x6e, 0x27, 0x74, 0x20, 0x75, 0x73, + 0x65, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x73, + 0x61, 0x76, 0x65, 0x67, 0x61, 0x6d, 0x65, 0x21, + 0x00, 0x28, 0x54, 0x68, 0x65, 0x20, 0x66, 0x69, + 0x6c, 0x65, 0x20, 0x69, 0x73, 0x20, 0x63, 0x6f, + 0x72, 0x72, 0x75, 0x70, 0x74, 0x65, 0x64, 0x20, + 0x6f, 0x72, 0x20, 0x77, 0x61, 0x73, 0x00, 0x73, + 0x61, 0x76, 0x65, 0x64, 0x20, 0x75, 0x6e, 0x64, + 0x65, 0x72, 0x20, 0x64, 0x69, 0x66, 0x66, 0x65, + 0x72, 0x65, 0x6e, 0x74, 0x20, 0x67, 0x61, 0x6d, + 0x65, 0x00, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x2e, 0x20, 0x53, 0x6f, 0x72, 0x72, 0x79, + 0x2e, 0x29, 0x00, 0x00, 0xc7, 0x32, 0x4f, 0x46, + 0x46, 0x00, 0xc7, 0x32, 0x31, 0x30, 0x80, 0x00, + 0xc6, 0x32, 0x32, 0x30, 0x80, 0x00, 0xc6, 0x32, + 0x33, 0x30, 0x80, 0x00, 0xc6, 0x32, 0x34, 0x30, + 0x80, 0x00, 0xc6, 0x32, 0x35, 0x30, 0x80, 0x00, + 0xc6, 0x32, 0x36, 0x30, 0x80, 0x00, 0xc6, 0x32, + 0x37, 0x30, 0x80, 0x00, 0xc6, 0x32, 0x38, 0x30, + 0x80, 0x00, 0xc6, 0x32, 0x39, 0x30, 0x80, 0x00, + 0xc5, 0x32, 0x4d, 0x41, 0x58, 0x00, 0xc7, 0x41, + 0x4f, 0x46, 0x46, 0x00, 0xc7, 0x41, 0x31, 0x30, + 0x80, 0x00, 0xc6, 0x41, 0x32, 0x30, 0x80, 0x00, + 0xc6, 0x41, 0x33, 0x30, 0x80, 0x00, 0xc6, 0x41, + 0x34, 0x30, 0x80, 0x00, 0xc6, 0x41, 0x35, 0x30, + 0x80, 0x00, 0xc6, 0x41, 0x36, 0x30, 0x80, 0x00, + 0xc6, 0x41, 0x37, 0x30, 0x80, 0x00, 0xc6, 0x41, + 0x38, 0x30, 0x80, 0x00, 0xc6, 0x41, 0x39, 0x30, + 0x80, 0x00, 0xc5, 0x41, 0x4d, 0x41, 0x58, 0x00, + 0xbb, 0x50, 0x53, 0x4c, 0x4f, 0x57, 0x20, 0x20, + 0x00, 0xb4, 0x50, 0x4e, 0x4f, 0x52, 0x4d, 0x41, + 0x4c, 0x00, 0xbb, 0x50, 0x46, 0x41, 0x53, 0x54, + 0x20, 0x20, 0x00, 0xb7, 0x50, 0x43, 0x52, 0x41, + 0x5a, 0x59, 0x20, 0x00, 0xbb, 0x5f, 0x53, 0x4c, + 0x4f, 0x57, 0x20, 0x20, 0x00, 0xb4, 0x5f, 0x4e, + 0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x00, 0xbb, 0x5f, + 0x46, 0x41, 0x53, 0x54, 0x20, 0x20, 0x00, 0xb7, + 0x5f, 0x43, 0x52, 0x41, 0x5a, 0x59, 0x20, 0x00, + 0xb8, 0x6e, 0x43, 0x4c, 0x49, 0x43, 0x4b, 0x20, + 0x00, 0xbb, 0x6e, 0x53, 0x4c, 0x4f, 0x57, 0x20, + 0x20, 0x00, 0xb4, 0x6e, 0x4e, 0x4f, 0x52, 0x4d, + 0x41, 0x4c, 0x00, 0xbb, 0x6e, 0x46, 0x41, 0x53, + 0x54, 0x20, 0x20, 0x00, 0xb7, 0x6e, 0x43, 0x52, + 0x41, 0x5a, 0x59, 0x20, 0x00, 0xbe, 0x7d, 0x4d, + 0x4f, 0x4e, 0x4f, 0x20, 0x00, 0xbb, 0x7d, 0x43, + 0x4f, 0x4c, 0x4f, 0x52, 0x00, 0x3b, 0x0c, 0xca, + 0x0c, 0x00, 0x3b, 0x1b, 0xca, 0x0c, 0x00, 0x3b, + 0x2a, 0xca, 0x0c, 0x00, 0x3b, 0x39, 0xca, 0x0c, + 0x00, 0x3b, 0x48, 0xca, 0x0c, 0x00, 0x3b, 0x57, + 0xca, 0x0c, 0x00, 0x3b, 0x66, 0xca, 0x0c, 0x00, + 0x3b, 0x75, 0xca, 0x0c, 0x00, 0x3b, 0x84, 0xca, + 0x0c, 0x00, 0x3b, 0x93, 0xca, 0x0c, 0x00, 0x6e, + 0xaf, 0x64, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xcc, 0xdd, 0x43, 0x61, 0x6e, 0x63, 0x65, + 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x5f, 0x00, 0x0d, 0x0a, 0x48, + 0x45, 0x4c, 0x4c, 0x4f, 0x2c, 0x20, 0x56, 0x4f, + 0x59, 0x45, 0x55, 0x52, 0x21, 0x0d, 0x0a, 0xaf, + 0xb3, 0xa6, 0x64, 0x00, 0x0a, 0x01, 0x01, 0x02, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x28, 0x1b, 0x1c, 0x28, + 0x1d, 0x1e, 0x28, 0x1b, 0x28, 0x1f, 0x20, 0x28, + 0x1b, 0x1c, 0x28, 0x1d, 0x28, 0x1f, 0x28, 0x1b, + 0x00, 0x28, 0x1b, 0x1c, 0x28, 0x1d, 0x1e, 0x28, + 0x1b, 0x28, 0x1f, 0x20, 0x28, 0x1b, 0x1c, 0x28, + 0x1d, 0x28, 0x1f, 0x28, 0x1b, 0x00, 0x2a, 0x26, + 0x2a, 0x2a, 0x27, 0x2a, 0x27, 0x2a, 0x2a, 0x26, + 0x2a, 0x26, 0x2a, 0x2a, 0x2a, 0x27, 0x26, 0x2a, + 0x2a, 0x27, 0x00, 0x21, 0x22, 0x23, 0x24, 0x29, + 0x25, 0x29, 0x29, 0x25, 0x29, 0x21, 0x22, 0x21, + 0x24, 0x29, 0x21, 0x29, 0x29, 0x21, 0x29, 0x00, + 0x00, 0x00, 0x01, 0x46, 0x65, 0x82, 0x65, 0xb2, + 0x65, 0x01, 0x02, 0x03, 0x04, 0x04, 0x04, 0x04, + 0x05, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x06, + 0x06, 0x06, 0x06, 0x07, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x05, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x00, 0x01, 0x02, 0x03, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x05, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x08, 0x08, 0x08, 0x09, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x00, 0x01, 0x02, 0x03, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x04, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x00, 0x01, 0x02, 0x03, + 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x00, + 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0a, 0x00, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, + 0x1a, 0x00, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, + 0x12, 0x00, 0x00, 0x00, 0x3a, 0x0d, 0x28, 0x2e, + 0x00, 0x20, 0x31, 0x18, 0x10, 0x3a, 0x23, 0x14, + 0x3f, 0x2b, 0x17, 0x3f, 0x30, 0x22, 0x3f, 0x34, + 0x21, 0x3f, 0x00, 0x00, 0x36, 0x00, 0x00, 0x28, + 0x00, 0x00, 0x11, 0x2f, 0x3f, 0x11, 0x24, 0x3f, + 0x11, 0x16, 0x3f, 0x1d, 0x11, 0x1f, 0x28, 0x2f, + 0x33, 0x35, 0x13, 0x10, 0x0c, 0x28, 0x21, 0x19, + 0x00, 0x92, 0x66, 0x94, 0x66, 0x96, 0x66, 0x98, + 0x66, 0x9a, 0x66, 0x9c, 0x66, 0x9e, 0x66, 0xa0, + 0x66, 0xa2, 0x66, 0xa4, 0x66, 0xa6, 0x66, 0xb1, + 0x66, 0xbc, 0x66, 0xbe, 0x66, 0xc9, 0x66, 0xcb, + 0x66, 0xd6, 0x66, 0xd8, 0x66, 0xe3, 0x66, 0xe5, + 0x66, 0xf0, 0x66, 0xf2, 0x66, 0xf4, 0x66, 0xf6, + 0x66, 0xf8, 0x66, 0xfa, 0x66, 0x20, 0x67, 0x22, + 0x67, 0x24, 0x67, 0x26, 0x67, 0x28, 0x67, 0x2a, + 0x67, 0x2c, 0x67, 0x2e, 0x67, 0x30, 0x67, 0x32, + 0x67, 0x34, 0x67, 0x36, 0x67, 0x38, 0x67, 0x3a, + 0x67, 0x3c, 0x67, 0x3e, 0x67, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x5c, 0x00, 0x50, 0x00, 0x97, + 0x00, 0x0f, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x01, 0xab, 0x00, 0x09, 0xff, 0xff, 0xff, + 0xff, 0x1f, 0x00, 0xb7, 0x00, 0x55, 0x00, 0xc4, + 0x00, 0x0c, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xd2, 0x00, 0xc7, 0x00, 0x08, 0xff, + 0xff, 0xff, 0xff, 0x3a, 0x00, 0x9e, 0x00, 0x3f, + 0x01, 0xc7, 0x00, 0x0a, 0xff, 0xff, 0xff, 0xff, + 0x82, 0x00, 0xa0, 0x00, 0x3f, 0x01, 0xa9, 0x00, + 0x0a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x86, 0x00, 0xc7, 0x00, 0x12, 0x00, 0x00, + 0x00, 0x00, 0xaf, 0x00, 0xae, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x00, 0xdb, 0x00, 0xa9, 0x00, 0x0e, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0x01, 0xc7, 0x00, + 0x06, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x9a, 0x67, 0x9b, 0x67, 0xf0, 0x67, 0x29, + 0x68, 0x70, 0x68, 0xc5, 0x68, 0xfe, 0x68, 0x37, + 0x69, 0x8c, 0x69, 0xc5, 0x69, 0x1a, 0x6a, 0x6f, + 0x6a, 0xc4, 0x6a, 0xfd, 0x6a, 0x6e, 0x6b, 0xdf, + 0x6b, 0x18, 0x6c, 0x19, 0x6c, 0x6e, 0x6c, 0xa7, + 0x6c, 0xee, 0x6c, 0x35, 0x6d, 0x7c, 0x6d, 0xb5, + 0x6d, 0xee, 0x6d, 0x19, 0x6e, 0x36, 0x6e, 0xd1, + 0x6e, 0xfc, 0x6e, 0x27, 0x6f, 0x6e, 0x6f, 0x8b, + 0x6f, 0xd2, 0x6f, 0x0b, 0x70, 0x6e, 0x70, 0x99, + 0x70, 0xb6, 0x70, 0xb7, 0x70, 0xf0, 0x70, 0xf1, + 0x70, 0xf2, 0x70, 0xf3, 0x70, 0x00, 0x06, 0x01, + 0x03, 0x00, 0x00, 0x00, 0x00, 0xd2, 0x00, 0xb1, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xe4, 0x00, 0x9b, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xfc, 0x00, 0x8e, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x3f, + 0x01, 0x5e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0xc0, 0x00, 0xa2, 0x00, 0xc7, + 0x00, 0x02, 0x01, 0x00, 0x00, 0x03, 0x00, 0xff, + 0x00, 0x9c, 0x00, 0x3f, 0x01, 0xc7, 0x00, 0x04, + 0x00, 0x00, 0x01, 0x04, 0x01, 0x03, 0x00, 0x00, + 0x00, 0x00, 0xcb, 0x00, 0xbf, 0x00, 0x00, 0x03, + 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xe8, 0x00, 0x99, 0x00, 0x00, 0x03, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x01, + 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0xe5, 0x00, 0xb9, 0x00, 0x3f, 0x01, 0xc7, 0x00, + 0x04, 0x00, 0x00, 0x01, 0x05, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xde, 0x00, 0xaf, 0x00, 0x00, + 0x03, 0x02, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7b, 0x00, 0xc7, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, + 0x01, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, + 0x00, 0x00, 0x00, 0xc0, 0x00, 0x3f, 0x01, 0xc7, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0xf4, + 0x00, 0x93, 0x00, 0x3f, 0x01, 0xb0, 0x00, 0x04, + 0x00, 0x04, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, + 0x00, 0x00, 0xf9, 0x00, 0x9c, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x01, 0x00, 0xf9, 0x00, 0x00, 0x00, + 0x08, 0x01, 0x9b, 0x00, 0x00, 0x03, 0x00, 0x03, + 0x01, 0x00, 0x08, 0x01, 0x00, 0x00, 0x3f, 0x01, + 0x7c, 0x00, 0x00, 0x03, 0x00, 0x03, 0x01, 0x03, + 0x46, 0x00, 0x00, 0x00, 0x6c, 0x00, 0xa4, 0x00, + 0x00, 0x03, 0x00, 0x03, 0x02, 0x01, 0x00, 0x00, + 0xb5, 0x00, 0x6b, 0x00, 0xc7, 0x00, 0x02, 0x01, + 0x00, 0x00, 0x03, 0x00, 0x87, 0x00, 0xb7, 0x00, + 0x3f, 0x01, 0xc7, 0x00, 0x04, 0x00, 0x00, 0x01, + 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, + 0x00, 0xc7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x01, 0xac, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x8f, 0x00, 0xbc, 0x00, 0x00, + 0x03, 0x02, 0x00, 0x01, 0x00, 0x33, 0x01, 0x00, + 0x00, 0x3f, 0x01, 0xc7, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0xc7, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x01, + 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0xfa, 0x00, 0x00, 0x00, 0x3f, 0x01, 0xc7, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x02, 0x00, 0x53, 0x00, + 0xaf, 0x00, 0xe6, 0x00, 0xbe, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x01, 0x00, 0xce, 0x00, 0xb3, + 0x00, 0x3f, 0x01, 0xc1, 0x00, 0x04, 0x00, 0x04, + 0x00, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x3f, + 0x01, 0x97, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0xb6, 0x00, 0x5e, 0x00, 0xc1, + 0x00, 0x02, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x00, 0xc7, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x05, 0x01, 0x00, + 0x00, 0x3f, 0x01, 0xc7, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0xff, 0x00, 0x00, 0x00, 0x3f, + 0x01, 0xc1, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, + 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x5d, 0x00, + 0xb2, 0x00, 0x00, 0x03, 0x02, 0x00, 0x01, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0x01, 0xac, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xf1, 0x00, + 0x00, 0x00, 0x3f, 0x01, 0xbc, 0x00, 0x00, 0x00, + 0x04, 0x03, 0x01, 0x00, 0x16, 0x01, 0x00, 0x00, + 0x3f, 0x01, 0xc7, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, + 0x00, 0x8a, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, + 0x00, 0x59, 0x00, 0x00, 0x00, 0x3f, 0x01, 0x94, + 0x00, 0x00, 0x00, 0x04, 0x03, 0x01, 0x00, 0xec, + 0x00, 0x00, 0x00, 0x3f, 0x01, 0xa2, 0x00, 0x00, + 0x00, 0x04, 0x03, 0x02, 0x00, 0x00, 0x00, 0x94, + 0x00, 0x3e, 0x00, 0xc7, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0xa6, 0x00, 0x71, + 0x00, 0xc7, 0x00, 0x02, 0x01, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0xbd, 0x00, 0x3f, 0x01, 0xc7, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x00, + 0x00, 0x00, 0x91, 0x00, 0x1a, 0x00, 0xa3, 0x00, + 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xb3, 0x00, 0xa4, 0x00, 0xc7, 0x00, 0x02, 0x01, + 0x00, 0x00, 0x02, 0x00, 0xa5, 0x00, 0xbd, 0x00, + 0xc8, 0x00, 0xc7, 0x00, 0x02, 0x01, 0x00, 0x00, + 0x03, 0x00, 0xaf, 0x00, 0x83, 0x00, 0x3f, 0x01, + 0x97, 0x00, 0x04, 0x00, 0x04, 0x00, 0x03, 0x00, + 0xc3, 0x00, 0x98, 0x00, 0x3f, 0x01, 0xb3, 0x00, + 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x42, 0x00, + 0x00, 0x00, 0x7c, 0x00, 0x99, 0x00, 0x00, 0x03, + 0x00, 0x03, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, + 0x00, 0xfc, 0x00, 0xa8, 0x00, 0x00, 0x03, 0x02, + 0x00, 0x01, 0x03, 0x00, 0x00, 0xa9, 0x00, 0x30, + 0x00, 0xb8, 0x00, 0x00, 0x03, 0x02, 0x00, 0x01, + 0x00, 0xfd, 0x00, 0x00, 0x00, 0x1f, 0x01, 0xac, + 0x00, 0x00, 0x03, 0x02, 0x03, 0x01, 0x00, 0x20, + 0x01, 0x00, 0x00, 0x2c, 0x01, 0xa2, 0x00, 0x00, + 0x03, 0x02, 0x03, 0x01, 0x00, 0x2d, 0x01, 0x00, + 0x00, 0x3f, 0x01, 0x87, 0x00, 0x00, 0x03, 0x02, + 0x03, 0x02, 0x01, 0x00, 0x00, 0xc2, 0x00, 0x3f, + 0x01, 0xc7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + 0x01, 0x01, 0x00, 0x00, 0xba, 0x00, 0x4b, 0x00, + 0xc7, 0x00, 0x02, 0x00, 0x02, 0x00, 0x01, 0x01, + 0xe6, 0x00, 0xba, 0x00, 0x3f, 0x01, 0xc7, 0x00, + 0x04, 0x00, 0x04, 0x01, 0x02, 0x00, 0x3e, 0x00, + 0x00, 0x00, 0x70, 0x00, 0xaf, 0x00, 0x00, 0x00, + 0x04, 0x03, 0x02, 0x00, 0x70, 0x00, 0x00, 0x00, + 0x3f, 0x01, 0xb2, 0x00, 0x00, 0x00, 0x04, 0x03, + 0x08, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x3f, + 0x01, 0x6e, 0x00, 0x00, 0x03, 0x04, 0x03, 0x01, + 0x00, 0x7a, 0x00, 0x6f, 0x00, 0x3f, 0x01, 0x89, + 0x00, 0x04, 0x00, 0x04, 0x03, 0x01, 0x00, 0xfc, + 0x00, 0x8d, 0x00, 0x3f, 0x01, 0xb8, 0x00, 0x04, + 0x00, 0x04, 0x03, 0x01, 0x00, 0x1c, 0x01, 0xb9, + 0x00, 0x3f, 0x01, 0xc7, 0x00, 0x04, 0x00, 0x04, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x8a, + 0x00, 0xb2, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, + 0x00, 0x00, 0x00, 0xb3, 0x00, 0x23, 0x00, 0xc7, + 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x24, + 0x00, 0xb3, 0x00, 0x6d, 0x00, 0xbe, 0x00, 0x02, + 0x00, 0x02, 0x00, 0x03, 0x00, 0x77, 0x00, 0xbc, + 0x00, 0xe8, 0x00, 0xc7, 0x00, 0x00, 0x01, 0x00, + 0x01, 0x08, 0x03, 0x01, 0xde, 0x00, 0xb4, 0x00, + 0x2c, 0x01, 0xc7, 0x00, 0x04, 0x00, 0x00, 0x01, + 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x00, + 0xa7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, + 0x6f, 0x00, 0x00, 0x00, 0xef, 0x00, 0xb1, 0x00, + 0x00, 0x03, 0x00, 0x03, 0x01, 0x03, 0xf0, 0x00, + 0x00, 0x00, 0x22, 0x01, 0xa5, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x01, 0x03, 0x23, 0x01, 0x00, 0x00, + 0x3f, 0x01, 0xa1, 0x00, 0x00, 0x00, 0x00, 0x03, + 0x02, 0x01, 0x00, 0x00, 0xb8, 0x00, 0x8b, 0x00, + 0xc7, 0x00, 0x02, 0x01, 0x00, 0x00, 0x03, 0x01, + 0xa3, 0x00, 0xb8, 0x00, 0xdd, 0x00, 0xc7, 0x00, + 0x04, 0x00, 0x00, 0x01, 0x03, 0x01, 0x2d, 0x01, + 0xaf, 0x00, 0x3f, 0x01, 0xc7, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3f, 0x01, 0x50, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5b, + 0x00, 0xc7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x14, 0x01, 0x00, 0x00, 0x3f, 0x01, 0xc7, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x65, 0x00, 0x3f, 0x01, 0xc7, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x00, 0x61, + 0x00, 0x00, 0x00, 0x3f, 0x01, 0xa3, 0x00, 0x00, + 0x00, 0x04, 0x03, 0x01, 0x00, 0x34, 0x00, 0x85, + 0x00, 0x63, 0x00, 0x97, 0x00, 0x04, 0x00, 0x04, + 0x00, 0x01, 0x00, 0xab, 0x00, 0xa4, 0x00, 0x3f, + 0x01, 0xb6, 0x00, 0x00, 0x00, 0x04, 0x03, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x72, + 0x00, 0x00, 0x03, 0x02, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x72, 0x00, 0x19, 0x00, 0xaa, 0x00, 0x02, + 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0xaa, + 0x00, 0x45, 0x00, 0xc7, 0x00, 0x02, 0x01, 0x00, + 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0xa3, 0x00, + 0x2c, 0x00, 0xc7, 0x00, 0x02, 0x01, 0x00, 0x00, + 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x01, + 0xa2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, + 0x0f, 0x01, 0xa3, 0x00, 0x3f, 0x01, 0xbd, 0x00, + 0x04, 0x00, 0x04, 0x03, 0x01, 0x00, 0xe2, 0x00, + 0x00, 0x00, 0x3f, 0x01, 0xaa, 0x00, 0x00, 0x00, + 0x04, 0x03, 0x05, 0x01, 0x03, 0xb0, 0x00, 0xa4, + 0x00, 0x3f, 0x01, 0xbc, 0x00, 0x04, 0x00, 0x04, + 0x00, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0e, + 0x00, 0x82, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, + 0x03, 0x0f, 0x00, 0x00, 0x00, 0x88, 0x00, 0xb4, + 0x00, 0x00, 0x03, 0x00, 0x03, 0x01, 0x03, 0x89, + 0x00, 0x00, 0x00, 0x3f, 0x01, 0xa3, 0x00, 0x00, + 0x00, 0x04, 0x00, 0x02, 0x01, 0x00, 0x00, 0xc4, + 0x00, 0x93, 0x00, 0xc7, 0x00, 0x02, 0x01, 0x00, + 0x00, 0x05, 0x01, 0x00, 0xdc, 0x00, 0x00, 0x00, + 0x3f, 0x01, 0xc7, 0x00, 0x00, 0x03, 0x00, 0x03, + 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, + 0xae, 0x00, 0x00, 0x03, 0x02, 0x00, 0x01, 0x03, + 0x51, 0x00, 0x00, 0x00, 0x78, 0x00, 0xab, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x79, 0x00, + 0x00, 0x00, 0x3f, 0x01, 0xb1, 0x00, 0x00, 0x00, + 0x04, 0x03, 0x02, 0x01, 0x00, 0x00, 0xc0, 0x00, + 0xa9, 0x00, 0xc7, 0x00, 0x02, 0x01, 0x00, 0x00, + 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, + 0x01, 0xa8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0xaa, 0x00, 0x29, 0x00, 0xbf, + 0x00, 0x02, 0x00, 0x02, 0x00, 0x01, 0x01, 0x00, + 0x00, 0xbf, 0x00, 0x9e, 0x00, 0xc7, 0x00, 0x02, + 0x01, 0x00, 0x00, 0x01, 0x01, 0xc5, 0x00, 0xaf, + 0x00, 0x3f, 0x01, 0xc7, 0x00, 0x04, 0x00, 0x00, + 0x01, 0x01, 0x04, 0x00, 0x01, 0xa8, 0x00, 0x3f, + 0x01, 0xae, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, + 0x01, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x52, 0x00, + 0xba, 0x00, 0x00, 0x03, 0x02, 0x00, 0x01, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0x01, 0xa9, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xe0, 0x00, + 0xaa, 0x00, 0x3f, 0x01, 0xb2, 0x00, 0x00, 0x00, + 0x04, 0x03, 0x01, 0x00, 0x12, 0x01, 0xb3, 0x00, + 0x3f, 0x01, 0xc7, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, + 0x00, 0xc7, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, + 0x03, 0x47, 0x00, 0x00, 0x00, 0x12, 0x01, 0xa6, + 0x00, 0x00, 0x03, 0x00, 0x03, 0x01, 0x03, 0x63, + 0x00, 0x00, 0x00, 0xd3, 0x00, 0xaa, 0x00, 0x00, + 0x03, 0x00, 0x03, 0x01, 0x00, 0x12, 0x01, 0x00, + 0x00, 0x3f, 0x01, 0xc7, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x96, 0x00, 0xc7, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x03, 0x79, 0x00, 0x00, 0x00, 0x3f, 0x01, + 0x90, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x01, + 0x79, 0x00, 0xa4, 0x00, 0x3f, 0x01, 0xc7, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x02, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x69, 0x00, 0xc7, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x03, 0x6a, 0x00, 0x00, + 0x00, 0x3f, 0x01, 0xa5, 0x00, 0x00, 0x03, 0x02, + 0x03, 0x0b, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x01, 0x83, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x01, 0x03, 0x00, 0x00, 0x84, 0x00, 0xda, 0x00, + 0x9e, 0x00, 0x02, 0x03, 0x02, 0x00, 0x01, 0x03, + 0x00, 0x00, 0x9f, 0x00, 0xc2, 0x00, 0xa9, 0x00, + 0x00, 0x03, 0x02, 0x00, 0x01, 0x03, 0x00, 0x00, + 0xaa, 0x00, 0xa4, 0x00, 0xaf, 0x00, 0x00, 0x03, + 0x02, 0x00, 0x01, 0x03, 0x00, 0x00, 0xb0, 0x00, + 0x7f, 0x00, 0xbb, 0x00, 0x00, 0x03, 0x02, 0x00, + 0x01, 0x03, 0x00, 0x00, 0xbc, 0x00, 0x5e, 0x00, + 0xc3, 0x00, 0x00, 0x03, 0x02, 0x00, 0x01, 0x02, + 0x00, 0x00, 0xc4, 0x00, 0x4b, 0x00, 0xc7, 0x00, + 0x00, 0x03, 0x02, 0x00, 0x01, 0x03, 0xef, 0x00, + 0x84, 0x00, 0x3f, 0x01, 0xa4, 0x00, 0x04, 0x00, + 0x00, 0x01, 0x01, 0x03, 0xdb, 0x00, 0xa5, 0x00, + 0x3f, 0x01, 0xb4, 0x00, 0x04, 0x00, 0x00, 0x01, + 0x01, 0x03, 0xc2, 0x00, 0xb5, 0x00, 0x3f, 0x01, + 0xbe, 0x00, 0x04, 0x00, 0x00, 0x01, 0x01, 0x04, + 0xa2, 0x00, 0xbf, 0x00, 0x3f, 0x01, 0xc7, 0x00, + 0x04, 0x00, 0x00, 0x01, 0x03, 0x01, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x93, 0x00, 0x8c, 0x00, 0x00, + 0x03, 0x00, 0x03, 0x01, 0x03, 0x94, 0x00, 0x00, + 0x00, 0x3f, 0x01, 0x94, 0x00, 0x00, 0x00, 0x04, + 0x03, 0x01, 0x00, 0xe9, 0x00, 0x95, 0x00, 0x3f, + 0x01, 0xc7, 0x00, 0x04, 0x00, 0x04, 0x00, 0x03, + 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x01, + 0xa5, 0x00, 0x00, 0x03, 0x00, 0x03, 0x01, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0xc7, 0x00, + 0x02, 0x00, 0x02, 0x00, 0x01, 0x03, 0x30, 0x00, + 0x00, 0x00, 0x4f, 0x00, 0xab, 0x00, 0x00, 0x03, + 0x00, 0x03, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3f, 0x01, 0x95, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0xf1, 0x00, 0x00, 0x00, 0x3f, + 0x01, 0x9a, 0x00, 0x00, 0x00, 0x04, 0x03, 0x01, + 0x04, 0x22, 0x01, 0x00, 0x00, 0x3f, 0x01, 0xc7, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x75, + 0x00, 0x9c, 0x00, 0xe9, 0x00, 0xbf, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x11, 0x00, 0xc7, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x01, 0xa5, 0x00, 0x00, 0x03, 0x00, 0x03, + 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x37, 0x00, + 0xb0, 0x00, 0x02, 0x03, 0x02, 0x00, 0x05, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0x00, 0xc7, + 0x00, 0x00, 0x03, 0x02, 0x00, 0x01, 0x03, 0x28, + 0x00, 0x00, 0x00, 0x8e, 0x00, 0xa9, 0x00, 0x00, + 0x03, 0x02, 0x03, 0x01, 0x03, 0x8e, 0x00, 0x00, + 0x00, 0x07, 0x01, 0xa1, 0x00, 0x00, 0x03, 0x00, + 0x03, 0x01, 0x00, 0x08, 0x01, 0x00, 0x00, 0x3f, + 0x01, 0xc7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0xdf, 0x00, 0xb7, 0x00, 0x3f, 0x01, 0xc7, + 0x00, 0x04, 0x00, 0x04, 0x01, 0x03, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xaf, 0x00, + 0x00, 0x03, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x50, 0x00, 0xa7, 0x00, 0x00, 0x03, + 0x02, 0x00, 0x01, 0x03, 0x50, 0x00, 0x00, 0x00, + 0x3f, 0x01, 0x9d, 0x00, 0x00, 0x00, 0x00, 0x03, + 0x01, 0x03, 0x97, 0x00, 0x00, 0x00, 0xb4, 0x00, + 0xa2, 0x00, 0x00, 0x03, 0x00, 0x03, 0x07, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x00, 0x80, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x03, 0x45, + 0x00, 0x00, 0x00, 0x78, 0x00, 0x86, 0x00, 0x00, + 0x03, 0x00, 0x03, 0x01, 0x03, 0x78, 0x00, 0x00, + 0x00, 0x07, 0x01, 0xa8, 0x00, 0x00, 0x03, 0x00, + 0x03, 0x01, 0x03, 0x07, 0x01, 0x00, 0x00, 0x3f, + 0x01, 0x9e, 0x00, 0x00, 0x03, 0x00, 0x03, 0x02, + 0x00, 0x00, 0x00, 0xb4, 0x00, 0x4d, 0x00, 0xc7, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x0c, + 0x01, 0xbe, 0x00, 0x3f, 0x01, 0xc7, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0a, 0x00, 0xc7, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x03, 0x64, 0x00, 0x00, 0x00, + 0x3f, 0x01, 0xa8, 0x00, 0x00, 0x03, 0x00, 0x03, + 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x63, 0x00, + 0xb7, 0x00, 0x00, 0x03, 0x02, 0x00, 0x02, 0x00, + 0xb6, 0x00, 0xb2, 0x00, 0x3f, 0x01, 0xc7, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x02, 0x01, 0x00, 0x7d, + 0x00, 0x00, 0x00, 0x3f, 0x01, 0xc7, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3f, 0x01, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x28, 0x00, 0xc7, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x01, 0x03, 0x29, 0x00, 0x00, 0x00, 0x5f, + 0x00, 0xa2, 0x00, 0x00, 0x03, 0x00, 0x03, 0x01, + 0x03, 0x5f, 0x00, 0x00, 0x00, 0xc8, 0x00, 0x9b, + 0x00, 0x00, 0x03, 0x00, 0x03, 0x01, 0x00, 0xc8, + 0x00, 0x00, 0x00, 0x3f, 0x01, 0xc7, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x48, + 0x71, 0x4c, 0x71, 0x50, 0x71, 0x54, 0x71, 0x58, + 0x71, 0x5c, 0x71, 0x60, 0x71, 0x64, 0x71, 0x68, + 0x71, 0x6c, 0x71, 0x70, 0x71, 0x84, 0x71, 0x88, + 0x71, 0x8c, 0x71, 0x9c, 0x71, 0xa0, 0x71, 0xa4, + 0x71, 0xa8, 0x71, 0xd0, 0x71, 0xd4, 0x71, 0xee, + 0x71, 0xf2, 0x71, 0xf6, 0x71, 0xfa, 0x71, 0xfe, + 0x71, 0x02, 0x72, 0x06, 0x72, 0x0a, 0x72, 0x18, + 0x72, 0x1c, 0x72, 0x20, 0x72, 0x24, 0x72, 0x28, + 0x72, 0x2c, 0x72, 0x30, 0x72, 0x34, 0x72, 0x38, + 0x72, 0x3c, 0x72, 0x40, 0x72, 0x44, 0x72, 0x48, + 0x72, 0x4c, 0x72, 0xc7, 0x00, 0xff, 0xff, 0xc7, + 0x00, 0xff, 0xff, 0xc7, 0x00, 0xff, 0xff, 0xc7, + 0x00, 0xff, 0xff, 0xc7, 0x00, 0xff, 0xff, 0xc7, + 0x00, 0xff, 0xff, 0xc7, 0x00, 0xff, 0xff, 0xc7, + 0x00, 0xff, 0xff, 0xc7, 0x00, 0xff, 0xff, 0xc7, + 0x00, 0xff, 0xff, 0x67, 0x2d, 0x6a, 0x28, 0x70, + 0x23, 0x79, 0x1e, 0x7f, 0x19, 0x82, 0x14, 0x87, + 0x0f, 0x8c, 0x0a, 0xc7, 0x00, 0xff, 0xff, 0xc7, + 0x00, 0xff, 0xff, 0xc7, 0x00, 0xff, 0xff, 0x6e, + 0x17, 0x73, 0x15, 0x78, 0x13, 0x7d, 0x11, 0x82, + 0x0f, 0x85, 0x0c, 0xc7, 0x00, 0xff, 0xff, 0xc7, + 0x00, 0xff, 0xff, 0xc7, 0x00, 0xff, 0xff, 0xc7, + 0x00, 0xff, 0xff, 0x7a, 0x32, 0x7c, 0x30, 0x7e, + 0x2e, 0x80, 0x2c, 0x82, 0x2a, 0x84, 0x28, 0x86, + 0x24, 0x88, 0x20, 0x8a, 0x1d, 0x8c, 0x1a, 0x8e, + 0x17, 0x90, 0x14, 0x92, 0x11, 0x94, 0x0e, 0x96, + 0x0b, 0x98, 0x08, 0x9a, 0x05, 0x9c, 0x03, 0xc7, + 0x00, 0xff, 0xff, 0xc7, 0x00, 0xff, 0xff, 0x83, + 0x12, 0x85, 0x10, 0x87, 0x0e, 0x89, 0x0c, 0x8b, + 0x0b, 0x8d, 0x0a, 0x8f, 0x09, 0x91, 0x08, 0x93, + 0x06, 0x95, 0x04, 0x97, 0x02, 0xc7, 0x00, 0xff, + 0xff, 0xc7, 0x00, 0xff, 0xff, 0xc7, 0x00, 0xff, + 0xff, 0xc7, 0x00, 0xff, 0xff, 0xc7, 0x00, 0xff, + 0xff, 0xc7, 0x00, 0xff, 0xff, 0xc7, 0x00, 0xff, + 0xff, 0xc7, 0x00, 0xff, 0xff, 0x8e, 0x0c, 0x90, + 0x0a, 0x92, 0x08, 0x94, 0x06, 0x96, 0x04, 0xc7, + 0x00, 0xff, 0xff, 0xc7, 0x00, 0xff, 0xff, 0xc7, + 0x00, 0xff, 0xff, 0xc7, 0x00, 0xff, 0xff, 0xc7, + 0x00, 0xff, 0xff, 0xc7, 0x00, 0xff, 0xff, 0xc7, + 0x00, 0xff, 0xff, 0xc7, 0x00, 0xff, 0xff, 0xc7, + 0x00, 0xff, 0xff, 0xc7, 0x00, 0xff, 0xff, 0xc7, + 0x00, 0xff, 0xff, 0xc7, 0x00, 0xff, 0xff, 0xc7, + 0x00, 0xff, 0xff, 0xc7, 0x00, 0xff, 0xff, 0xc7, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xa8, + 0x72, 0xaa, 0x72, 0xc7, 0x73, 0x34, 0x75, 0xca, + 0x76, 0xda, 0x77, 0xbe, 0x79, 0x52, 0x7c, 0xe7, + 0x7e, 0x3e, 0x80, 0x1a, 0x81, 0x2c, 0x84, 0xd9, + 0x85, 0x20, 0x87, 0x06, 0x88, 0xd9, 0x89, 0x6d, + 0x8a, 0xca, 0x8b, 0xec, 0x8c, 0x19, 0x90, 0x6f, + 0x93, 0x2c, 0x95, 0x9c, 0x97, 0x08, 0x9b, 0x22, + 0x9d, 0x00, 0x9f, 0x10, 0xa0, 0xc0, 0xa3, 0x82, + 0xa4, 0xde, 0xa5, 0xc1, 0xa8, 0x5c, 0xaa, 0x12, + 0xac, 0x46, 0xaf, 0x5c, 0xb0, 0xa5, 0xb3, 0xfa, + 0xb3, 0xfc, 0xb3, 0xeb, 0xb4, 0xed, 0xb4, 0xef, + 0xb4, 0xf1, 0xb4, 0x00, 0x00, 0xb6, 0x72, 0xcf, + 0x72, 0xe8, 0x72, 0x38, 0x73, 0x7f, 0x73, 0x00, + 0x00, 0x01, 0x00, 0x00, 0xa6, 0x00, 0x38, 0x00, + 0xc7, 0x00, 0x38, 0x00, 0xb4, 0x00, 0x00, 0x00, + 0xb4, 0x00, 0x04, 0x01, 0x70, 0x61, 0x74, 0x68, + 0x00, 0x01, 0x02, 0xb6, 0x00, 0xb5, 0x00, 0x10, + 0x01, 0xc7, 0x00, 0xd9, 0x00, 0xc1, 0x00, 0xdd, + 0x00, 0xc7, 0x00, 0x03, 0x01, 0x70, 0x61, 0x74, + 0x68, 0x00, 0x01, 0x03, 0x20, 0x01, 0x4c, 0x00, + 0x2f, 0x01, 0x59, 0x00, 0x10, 0x01, 0x64, 0x00, + 0x10, 0x01, 0x64, 0x00, 0x01, 0x01, 0x6d, 0x79, + 0x73, 0x74, 0x65, 0x72, 0x69, 0x6f, 0x75, 0x73, + 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x00, + 0x42, 0x6f, 0x79, 0x2c, 0x20, 0x74, 0x68, 0x61, + 0x74, 0x20, 0x63, 0x6f, 0x75, 0x6c, 0x64, 0x20, + 0x62, 0x65, 0x20, 0x73, 0x6f, 0x6d, 0x65, 0x20, + 0x6b, 0x69, 0x6e, 0x64, 0x20, 0x6f, 0x66, 0x20, + 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, + 0x21, 0x00, 0x00, 0x04, 0xf1, 0x00, 0x0e, 0x00, + 0x3f, 0x01, 0x5f, 0x00, 0x10, 0x01, 0x64, 0x00, + 0x10, 0x01, 0x64, 0x00, 0x01, 0x01, 0x66, 0x65, + 0x6e, 0x63, 0x65, 0x00, 0x54, 0x68, 0x69, 0x73, + 0x20, 0x69, 0x73, 0x20, 0x6d, 0x61, 0x64, 0x65, + 0x20, 0x6f, 0x66, 0x20, 0x62, 0x61, 0x72, 0x62, + 0x65, 0x64, 0x20, 0x77, 0x69, 0x72, 0x65, 0x2c, + 0x00, 0x61, 0x6e, 0x64, 0x20, 0x69, 0x74, 0x27, + 0x73, 0x20, 0x72, 0x75, 0x73, 0x74, 0x79, 0x2e, + 0x00, 0x00, 0x05, 0x40, 0x00, 0x5a, 0x00, 0xb9, + 0x00, 0xa6, 0x00, 0x7c, 0x00, 0xb7, 0x00, 0x7c, + 0x00, 0xb7, 0x00, 0x01, 0x01, 0x74, 0x65, 0x6e, + 0x74, 0x00, 0x54, 0x68, 0x65, 0x20, 0x63, 0x61, + 0x6d, 0x6f, 0x75, 0x66, 0x6c, 0x61, 0x67, 0x65, + 0x20, 0x69, 0x73, 0x6e, 0x27, 0x74, 0x20, 0x71, + 0x75, 0x69, 0x74, 0x65, 0x20, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x2d, 0x6f, 0x66, 0x2d, 0x74, 0x68, + 0x65, 0x2d, 0x61, 0x72, 0x74, 0x2e, 0x2e, 0x2e, + 0x00, 0x00, 0xd5, 0x73, 0xf7, 0x73, 0x10, 0x74, + 0x29, 0x74, 0x5c, 0x74, 0xd0, 0x74, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x00, 0xa3, 0x00, 0xf0, 0x00, 0xa3, + 0x00, 0x04, 0x01, 0x62, 0x69, 0x72, 0x64, 0x00, + 0x42, 0x69, 0x67, 0x20, 0x62, 0x6f, 0x79, 0x2e, + 0x00, 0x00, 0x02, 0x16, 0x01, 0x55, 0x00, 0x3f, + 0x01, 0x7a, 0x00, 0x22, 0x01, 0x76, 0x00, 0x3f, + 0x01, 0x68, 0x00, 0x02, 0x01, 0x70, 0x61, 0x74, + 0x68, 0x00, 0x01, 0x03, 0x50, 0x00, 0xbd, 0x00, + 0xe6, 0x00, 0xc7, 0x00, 0xda, 0x00, 0xbf, 0x00, + 0xd6, 0x00, 0xc7, 0x00, 0x03, 0x01, 0x70, 0x61, + 0x74, 0x68, 0x00, 0x01, 0x04, 0xc6, 0x00, 0x23, + 0x00, 0xdc, 0x00, 0x96, 0x00, 0xd1, 0x00, 0x9a, + 0x00, 0xd1, 0x00, 0x9a, 0x00, 0x01, 0x01, 0x70, + 0x6f, 0x73, 0x74, 0x00, 0x54, 0x68, 0x61, 0x74, + 0x20, 0x6c, 0x6f, 0x6f, 0x6b, 0x73, 0x20, 0x65, + 0x61, 0x73, 0x79, 0x20, 0x74, 0x6f, 0x20, 0x63, + 0x6c, 0x69, 0x6d, 0x62, 0x2e, 0x00, 0x00, 0x05, + 0x38, 0x00, 0x65, 0x00, 0xac, 0x00, 0xbc, 0x00, + 0x73, 0x00, 0xc2, 0x00, 0x73, 0x00, 0xc2, 0x00, + 0x01, 0x01, 0x6d, 0x75, 0x64, 0x20, 0x70, 0x6f, + 0x6f, 0x6c, 0x00, 0x48, 0x65, 0x61, 0x76, 0x65, + 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x57, 0x6f, + 0x6f, 0x64, 0x73, 0x74, 0x6f, 0x63, 0x6b, 0x20, + 0x66, 0x61, 0x6e, 0x73, 0x2c, 0x20, 0x61, 0x6c, + 0x6c, 0x69, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x73, + 0x00, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x6f, + 0x73, 0x65, 0x20, 0x74, 0x6f, 0x75, 0x67, 0x68, + 0x20, 0x77, 0x6f, 0x6d, 0x65, 0x6e, 0x20, 0x66, + 0x69, 0x67, 0x68, 0x74, 0x69, 0x6e, 0x67, 0x20, + 0x6f, 0x6e, 0x20, 0x6c, 0x61, 0x74, 0x65, 0x20, + 0x54, 0x56, 0x00, 0x73, 0x68, 0x6f, 0x77, 0x73, + 0x2e, 0x00, 0x00, 0x06, 0xb8, 0x00, 0x90, 0x00, + 0xe4, 0x00, 0xa0, 0x00, 0xf0, 0x00, 0xa3, 0x00, + 0xdc, 0x00, 0xa1, 0x00, 0x04, 0x00, 0x62, 0x69, + 0x72, 0x64, 0x00, 0x49, 0x74, 0x20, 0x74, 0x6f, + 0x6f, 0x6b, 0x20, 0x73, 0x6f, 0x6d, 0x65, 0x20, + 0x64, 0x72, 0x75, 0x67, 0x73, 0x20, 0x74, 0x68, + 0x65, 0x6e, 0x20, 0x66, 0x65, 0x6c, 0x6c, 0x20, + 0x35, 0x20, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, + 0x2e, 0x00, 0x57, 0x68, 0x6f, 0x20, 0x77, 0x6f, + 0x75, 0x6c, 0x64, 0x6e, 0x27, 0x74, 0x20, 0x74, + 0x61, 0x6b, 0x65, 0x20, 0x61, 0x20, 0x6e, 0x61, + 0x70, 0x20, 0x61, 0x66, 0x74, 0x65, 0x72, 0x20, + 0x74, 0x68, 0x61, 0x74, 0x3f, 0x00, 0x00, 0x44, + 0x75, 0x7d, 0x75, 0xd0, 0x75, 0x0a, 0x76, 0x5a, + 0x76, 0x73, 0x76, 0x8c, 0x76, 0x00, 0x00, 0x01, + 0x04, 0x00, 0x77, 0x00, 0x14, 0x00, 0x8d, 0x00, + 0xca, 0x00, 0xb3, 0x00, 0xca, 0x00, 0xb3, 0x00, + 0x04, 0x01, 0x73, 0x68, 0x6f, 0x76, 0x65, 0x6c, + 0x00, 0x49, 0x74, 0x27, 0x73, 0x20, 0x61, 0x20, + 0x73, 0x6d, 0x61, 0x6c, 0x6c, 0x20, 0x6d, 0x69, + 0x6c, 0x69, 0x74, 0x61, 0x72, 0x79, 0x20, 0x73, + 0x68, 0x6f, 0x76, 0x65, 0x6c, 0x2e, 0x00, 0x00, + 0x02, 0x7c, 0x00, 0xaa, 0x00, 0xcf, 0x00, 0xc0, + 0x00, 0xca, 0x00, 0xb3, 0x00, 0xca, 0x00, 0xb3, + 0x00, 0x04, 0x01, 0x73, 0x6f, 0x6c, 0x69, 0x64, + 0x20, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x00, + 0x49, 0x27, 0x76, 0x65, 0x20, 0x67, 0x6f, 0x74, + 0x20, 0x61, 0x20, 0x66, 0x65, 0x65, 0x6c, 0x69, + 0x6e, 0x67, 0x20, 0x49, 0x27, 0x6d, 0x20, 0x67, + 0x6f, 0x6e, 0x6e, 0x61, 0x20, 0x6e, 0x65, 0x65, + 0x64, 0x20, 0x74, 0x68, 0x69, 0x73, 0x00, 0x73, + 0x6f, 0x6d, 0x65, 0x68, 0x6f, 0x77, 0x2e, 0x2e, + 0x2e, 0x00, 0x00, 0x03, 0xab, 0x00, 0x63, 0x00, + 0xba, 0x00, 0x97, 0x00, 0xe1, 0x00, 0x9f, 0x00, + 0xe1, 0x00, 0x9f, 0x00, 0x04, 0x01, 0x70, 0x6c, + 0x61, 0x6e, 0x74, 0x00, 0x49, 0x74, 0x20, 0x6d, + 0x75, 0x73, 0x74, 0x20, 0x68, 0x61, 0x76, 0x65, + 0x20, 0x61, 0x20, 0x76, 0x65, 0x72, 0x79, 0x20, + 0x73, 0x6f, 0x66, 0x74, 0x20, 0x74, 0x6f, 0x75, + 0x63, 0x68, 0x2e, 0x00, 0x00, 0x04, 0x27, 0x00, + 0x53, 0x00, 0xaa, 0x00, 0xa1, 0x00, 0xca, 0x00, + 0xb3, 0x00, 0xca, 0x00, 0xb3, 0x00, 0x04, 0x01, + 0x62, 0x72, 0x69, 0x63, 0x6b, 0x20, 0x77, 0x61, + 0x6c, 0x6c, 0x00, 0x54, 0x68, 0x65, 0x20, 0x62, + 0x72, 0x69, 0x63, 0x6b, 0x2d, 0x6c, 0x61, 0x79, + 0x65, 0x72, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, + 0x68, 0x61, 0x76, 0x65, 0x20, 0x62, 0x65, 0x65, + 0x6e, 0x00, 0x61, 0x20, 0x50, 0x69, 0x6e, 0x6b, + 0x20, 0x46, 0x6c, 0x6f, 0x79, 0x64, 0x20, 0x66, + 0x61, 0x6e, 0x2e, 0x00, 0x00, 0x05, 0xe0, 0x00, + 0x7a, 0x00, 0x3f, 0x01, 0x9b, 0x00, 0x13, 0x01, + 0x89, 0x00, 0x3f, 0x01, 0x80, 0x00, 0x02, 0x01, + 0x70, 0x61, 0x74, 0x68, 0x00, 0x01, 0x06, 0xfa, + 0x00, 0x9c, 0x00, 0x3f, 0x01, 0xc7, 0x00, 0x18, + 0x01, 0xb9, 0x00, 0x3f, 0x01, 0xb9, 0x00, 0x02, + 0x01, 0x70, 0x61, 0x74, 0x68, 0x00, 0x01, 0x07, + 0xb2, 0x00, 0xa1, 0x00, 0xc0, 0x00, 0xb4, 0x00, + 0xcb, 0x00, 0xb6, 0x00, 0xcb, 0x00, 0xb6, 0x00, + 0x04, 0x00, 0x73, 0x70, 0x72, 0x69, 0x6e, 0x67, + 0x00, 0x49, 0x27, 0x76, 0x65, 0x20, 0x73, 0x65, + 0x65, 0x6e, 0x20, 0x62, 0x65, 0x74, 0x74, 0x65, + 0x72, 0x20, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x73, + 0x20, 0x74, 0x6f, 0x20, 0x73, 0x69, 0x74, 0x20, + 0x6f, 0x6e, 0x2e, 0x00, 0x00, 0xda, 0x76, 0xf3, + 0x76, 0x0c, 0x77, 0x25, 0x77, 0x56, 0x77, 0x6f, + 0x77, 0xb1, 0x77, 0x00, 0x00, 0x01, 0x5c, 0x00, + 0xb4, 0x00, 0x92, 0x00, 0xc7, 0x00, 0x80, 0x00, + 0xb0, 0x00, 0x74, 0x00, 0xc7, 0x00, 0x03, 0x01, + 0x70, 0x61, 0x74, 0x68, 0x00, 0x01, 0x02, 0x00, + 0x00, 0x9c, 0x00, 0x2d, 0x00, 0xb6, 0x00, 0x23, + 0x00, 0xae, 0x00, 0x00, 0x00, 0xae, 0x00, 0x04, + 0x01, 0x70, 0x61, 0x74, 0x68, 0x00, 0x01, 0x03, + 0x23, 0x01, 0x00, 0x00, 0x3f, 0x01, 0x82, 0x00, + 0x2c, 0x01, 0x87, 0x00, 0x2c, 0x01, 0x87, 0x00, + 0x01, 0x01, 0x70, 0x61, 0x74, 0x68, 0x00, 0x01, + 0x04, 0x0b, 0x00, 0x47, 0x00, 0x42, 0x00, 0x9b, + 0x00, 0x28, 0x00, 0x9d, 0x00, 0x28, 0x00, 0x9d, + 0x00, 0x01, 0x01, 0x63, 0x61, 0x6e, 0x74, 0x65, + 0x65, 0x6e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x20, + 0x64, 0x6f, 0x6f, 0x72, 0x00, 0x4e, 0x69, 0x63, + 0x65, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x00, + 0x00, 0x05, 0x8c, 0x00, 0x47, 0x00, 0xbf, 0x00, + 0x9b, 0x00, 0xa6, 0x00, 0x9e, 0x00, 0xa6, 0x00, + 0x9e, 0x00, 0x01, 0x01, 0x64, 0x6f, 0x6f, 0x72, + 0x00, 0x01, 0x06, 0x04, 0x01, 0x35, 0x00, 0x22, + 0x01, 0x94, 0x00, 0x22, 0x01, 0x8f, 0x00, 0x14, + 0x01, 0x8f, 0x00, 0x04, 0x01, 0x6a, 0x61, 0x69, + 0x6c, 0x20, 0x64, 0x6f, 0x6f, 0x72, 0x00, 0x4e, + 0x6f, 0x77, 0x20, 0x74, 0x68, 0x61, 0x74, 0x27, + 0x73, 0x20, 0x77, 0x68, 0x61, 0x74, 0x00, 0x49, + 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x20, 0x61, 0x20, + 0x67, 0x6f, 0x6f, 0x64, 0x00, 0x64, 0x6f, 0x6f, + 0x72, 0x2e, 0x00, 0x00, 0x07, 0x4b, 0x00, 0x65, + 0x00, 0x65, 0x00, 0xa0, 0x00, 0x6d, 0x00, 0xa1, + 0x00, 0x6d, 0x00, 0xa1, 0x00, 0x04, 0x01, 0x74, + 0x72, 0x61, 0x73, 0x68, 0x20, 0x63, 0x61, 0x6e, + 0x00, 0x49, 0x74, 0x20, 0x73, 0x74, 0x69, 0x6e, + 0x6b, 0x73, 0x2e, 0x00, 0x00, 0xf0, 0x77, 0x2c, + 0x78, 0x4f, 0x78, 0x96, 0x78, 0xcd, 0x78, 0x03, + 0x79, 0x3e, 0x79, 0x68, 0x79, 0x86, 0x79, 0xa1, + 0x79, 0x00, 0x00, 0x01, 0x6b, 0x00, 0x9c, 0x00, + 0x84, 0x00, 0xa9, 0x00, 0x91, 0x00, 0xb3, 0x00, + 0x91, 0x00, 0xb3, 0x00, 0x04, 0x01, 0x73, 0x70, + 0x72, 0x69, 0x6e, 0x67, 0x00, 0x59, 0x65, 0x61, + 0x68, 0x2c, 0x20, 0x49, 0x20, 0x64, 0x65, 0x66, + 0x69, 0x6e, 0x65, 0x74, 0x65, 0x6c, 0x79, 0x20, + 0x67, 0x6f, 0x74, 0x74, 0x61, 0x20, 0x73, 0x70, + 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x00, 0x00, 0x02, + 0x1f, 0x00, 0x9c, 0x00, 0x90, 0x00, 0xbb, 0x00, + 0x50, 0x00, 0xc0, 0x00, 0x50, 0x00, 0xc0, 0x00, + 0x01, 0x01, 0x62, 0x65, 0x64, 0x00, 0x4c, 0x6f, + 0x6f, 0x6b, 0x73, 0x20, 0x62, 0x61, 0x64, 0x2e, + 0x00, 0x00, 0x03, 0x1e, 0x01, 0x71, 0x00, 0x32, + 0x01, 0x7e, 0x00, 0x09, 0x01, 0xb4, 0x00, 0x09, + 0x01, 0xb4, 0x00, 0x02, 0x01, 0x63, 0x72, 0x61, + 0x74, 0x65, 0x73, 0x00, 0x54, 0x68, 0x65, 0x20, + 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x20, 0x6c, 0x6f, + 0x6f, 0x6b, 0x73, 0x20, 0x62, 0x65, 0x74, 0x74, + 0x65, 0x72, 0x00, 0x66, 0x72, 0x6f, 0x6d, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x6f, 0x74, 0x68, 0x65, + 0x72, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2e, 0x00, + 0x00, 0x04, 0x1b, 0x01, 0x87, 0x00, 0x2d, 0x01, + 0x92, 0x00, 0x09, 0x01, 0xb4, 0x00, 0x09, 0x01, + 0xb4, 0x00, 0x02, 0x00, 0x62, 0x6f, 0x77, 0x6c, + 0x00, 0x49, 0x74, 0x27, 0x73, 0x20, 0x6d, 0x61, + 0x64, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x6c, + 0x75, 0x6d, 0x69, 0x6e, 0x69, 0x75, 0x6d, 0x2e, + 0x00, 0x00, 0x62, 0x6f, 0x64, 0x79, 0x00, 0xff, + 0x05, 0x0a, 0x01, 0xaa, 0x00, 0x20, 0x01, 0xbe, + 0x00, 0x0a, 0x01, 0xbd, 0x00, 0x0a, 0x01, 0xbd, + 0x00, 0x02, 0x00, 0x6c, 0x69, 0x76, 0x65, 0x20, + 0x63, 0x61, 0x62, 0x6c, 0x65, 0x00, 0x54, 0x68, + 0x65, 0x20, 0x77, 0x69, 0x72, 0x65, 0x73, 0x20, + 0x61, 0x72, 0x65, 0x20, 0x65, 0x78, 0x70, 0x6f, + 0x73, 0x65, 0x64, 0x21, 0x00, 0x00, 0x06, 0x9b, + 0x00, 0x5a, 0x00, 0xa4, 0x00, 0x6b, 0x00, 0x9f, + 0x00, 0xc2, 0x00, 0x9f, 0x00, 0xc2, 0x00, 0x01, + 0x01, 0x62, 0x75, 0x6c, 0x62, 0x00, 0x49, 0x74, + 0x27, 0x73, 0x20, 0x6e, 0x61, 0x6b, 0x65, 0x64, + 0x20, 0x61, 0x6e, 0x64, 0x20, 0x69, 0x74, 0x27, + 0x73, 0x20, 0x6f, 0x75, 0x74, 0x20, 0x6f, 0x66, + 0x20, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2e, 0x00, + 0x00, 0x07, 0x15, 0x01, 0x62, 0x00, 0x35, 0x01, + 0xbd, 0x00, 0x09, 0x01, 0xb4, 0x00, 0x09, 0x01, + 0xb4, 0x00, 0x02, 0x01, 0x64, 0x6f, 0x6f, 0x72, + 0x00, 0x48, 0x65, 0x61, 0x76, 0x79, 0x20, 0x61, + 0x6e, 0x64, 0x20, 0x73, 0x6f, 0x6c, 0x69, 0x64, + 0x2e, 0x00, 0x00, 0x08, 0xfa, 0x00, 0x93, 0x00, + 0x0e, 0x01, 0xad, 0x00, 0xf2, 0x00, 0xad, 0x00, + 0xf2, 0x00, 0xad, 0x00, 0x02, 0x01, 0x74, 0x72, + 0x61, 0x73, 0x68, 0x20, 0x63, 0x61, 0x6e, 0x00, + 0x01, 0x09, 0x38, 0x01, 0x8a, 0x00, 0x3f, 0x01, + 0x91, 0x00, 0x32, 0x01, 0xc4, 0x00, 0x32, 0x01, + 0xc4, 0x00, 0x02, 0x01, 0x73, 0x77, 0x69, 0x74, + 0x63, 0x68, 0x00, 0x01, 0x0a, 0x38, 0x00, 0x7a, + 0x00, 0xd0, 0x00, 0x9a, 0x00, 0x99, 0x00, 0xb2, + 0x00, 0x99, 0x00, 0xb2, 0x00, 0x01, 0x01, 0x67, + 0x72, 0x61, 0x66, 0x66, 0x69, 0x74, 0x69, 0x00, + 0x01, 0xd8, 0x79, 0x08, 0x7a, 0x21, 0x7a, 0x61, + 0x7a, 0x82, 0x7a, 0xbb, 0x7a, 0xf2, 0x7a, 0x1a, + 0x7b, 0x94, 0x7b, 0xd1, 0x7b, 0xed, 0x7b, 0x17, + 0x7c, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0xc3, 0x00, + 0x82, 0x00, 0xc3, 0x00, 0x01, 0x01, 0x63, 0x61, + 0x70, 0x74, 0x61, 0x69, 0x6e, 0x00, 0x48, 0x65, + 0x27, 0x73, 0x20, 0x73, 0x69, 0x74, 0x74, 0x69, + 0x6e, 0x67, 0x20, 0x74, 0x69, 0x67, 0x68, 0x74, + 0x2e, 0x00, 0x00, 0x02, 0x00, 0x00, 0x50, 0x00, + 0x23, 0x00, 0xbc, 0x00, 0x32, 0x00, 0xb8, 0x00, + 0x13, 0x00, 0xb8, 0x00, 0x04, 0x01, 0x64, 0x6f, + 0x6f, 0x72, 0x00, 0x01, 0x03, 0x2c, 0x00, 0x61, + 0x00, 0x4e, 0x00, 0xa4, 0x00, 0x40, 0x00, 0xa6, + 0x00, 0x40, 0x00, 0xa6, 0x00, 0x01, 0x01, 0x6c, + 0x6f, 0x63, 0x6b, 0x65, 0x72, 0x00, 0x49, 0x74, + 0x27, 0x73, 0x20, 0x63, 0x6c, 0x6f, 0x73, 0x65, + 0x64, 0x2e, 0x20, 0x4e, 0x6f, 0x74, 0x20, 0x74, + 0x68, 0x61, 0x74, 0x00, 0x49, 0x27, 0x6d, 0x20, + 0x73, 0x75, 0x72, 0x70, 0x72, 0x69, 0x73, 0x65, + 0x64, 0x2e, 0x00, 0x00, 0x04, 0x6b, 0x00, 0x51, + 0x00, 0xcd, 0x00, 0x8c, 0x00, 0x9e, 0x00, 0xa8, + 0x00, 0x9e, 0x00, 0xa8, 0x00, 0x01, 0x01, 0x6d, + 0x61, 0x70, 0x00, 0x50, 0x69, 0x63, 0x61, 0x73, + 0x73, 0x6f, 0x3f, 0x00, 0x00, 0x05, 0x8c, 0x00, + 0x94, 0x00, 0xd6, 0x00, 0x9e, 0x00, 0xa4, 0x00, + 0xc6, 0x00, 0xa4, 0x00, 0xc6, 0x00, 0x01, 0x01, + 0x64, 0x65, 0x73, 0x6b, 0x00, 0x54, 0x68, 0x61, + 0x74, 0x27, 0x73, 0x20, 0x6a, 0x75, 0x73, 0x74, + 0x20, 0x61, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x20, + 0x6f, 0x66, 0x20, 0x6f, 0x6c, 0x64, 0x20, 0x6a, + 0x75, 0x6e, 0x6b, 0x2e, 0x00, 0x00, 0x06, 0xee, + 0x00, 0x83, 0x00, 0x3f, 0x01, 0xc7, 0x00, 0xf1, + 0x00, 0xb5, 0x00, 0xf1, 0x00, 0xb5, 0x00, 0x02, + 0x01, 0x62, 0x65, 0x64, 0x00, 0x54, 0x68, 0x61, + 0x74, 0x20, 0x64, 0x6f, 0x65, 0x73, 0x6e, 0x27, + 0x74, 0x20, 0x6c, 0x6f, 0x6f, 0x6b, 0x20, 0x63, + 0x6f, 0x6d, 0x66, 0x6f, 0x72, 0x74, 0x61, 0x62, + 0x6c, 0x65, 0x2e, 0x00, 0x00, 0x07, 0xbf, 0x00, + 0xa2, 0x00, 0xd9, 0x00, 0xb9, 0x00, 0xcc, 0x00, + 0xc2, 0x00, 0xcc, 0x00, 0xc2, 0x00, 0x01, 0x01, + 0x64, 0x72, 0x61, 0x77, 0x65, 0x72, 0x00, 0x49, + 0x74, 0x27, 0x73, 0x20, 0x63, 0x6c, 0x6f, 0x73, + 0x65, 0x64, 0x21, 0x00, 0x00, 0x08, 0xd5, 0x00, + 0x5d, 0x00, 0xe8, 0x00, 0x8a, 0x00, 0xf1, 0x00, + 0xb5, 0x00, 0xf1, 0x00, 0xb5, 0x00, 0x01, 0x01, + 0x73, 0x61, 0x62, 0x72, 0x65, 0x73, 0x00, 0x4d, + 0x79, 0x20, 0x67, 0x72, 0x61, 0x6e, 0x64, 0x70, + 0x61, 0x20, 0x68, 0x61, 0x64, 0x20, 0x6f, 0x6e, + 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x6f, + 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x63, 0x65, 0x2e, + 0x00, 0x49, 0x20, 0x65, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x64, 0x20, 0x69, 0x74, 0x20, + 0x66, 0x6f, 0x72, 0x20, 0x61, 0x20, 0x6a, 0x6f, + 0x79, 0x73, 0x74, 0x69, 0x63, 0x6b, 0x2e, 0x00, + 0x47, 0x72, 0x61, 0x6e, 0x64, 0x70, 0x61, 0x20, + 0x64, 0x69, 0x64, 0x6e, 0x27, 0x74, 0x20, 0x6c, + 0x69, 0x6b, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x69, 0x64, 0x65, 0x61, 0x2e, 0x00, 0x00, 0x09, + 0xef, 0x00, 0x5e, 0x00, 0x15, 0x01, 0x7a, 0x00, + 0xf1, 0x00, 0xb5, 0x00, 0xf1, 0x00, 0xb5, 0x00, + 0x01, 0x01, 0x67, 0x75, 0x6e, 0x73, 0x00, 0x57, + 0x68, 0x61, 0x74, 0x20, 0x61, 0x20, 0x70, 0x69, + 0x74, 0x79, 0x20, 0x74, 0x68, 0x65, 0x79, 0x20, + 0x61, 0x72, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79, + 0x00, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x2e, + 0x2e, 0x2e, 0x00, 0x00, 0x0a, 0x2d, 0x00, 0x27, + 0x00, 0x53, 0x00, 0x56, 0x00, 0x32, 0x00, 0xb5, + 0x00, 0x32, 0x00, 0xb5, 0x00, 0x01, 0x01, 0x70, + 0x69, 0x63, 0x74, 0x75, 0x72, 0x65, 0x00, 0x01, + 0x0b, 0xde, 0x00, 0x2b, 0x00, 0x1d, 0x01, 0x53, + 0x00, 0xf1, 0x00, 0xb5, 0x00, 0xf1, 0x00, 0xb5, + 0x00, 0x01, 0x01, 0x70, 0x69, 0x63, 0x74, 0x75, + 0x72, 0x65, 0x00, 0x4e, 0x69, 0x63, 0x65, 0x20, + 0x63, 0x68, 0x6f, 0x70, 0x70, 0x65, 0x72, 0x2e, + 0x00, 0x00, 0x0c, 0x7b, 0x00, 0xac, 0x00, 0x89, + 0x00, 0xbb, 0x00, 0x81, 0x00, 0xbf, 0x00, 0x81, + 0x00, 0xbf, 0x00, 0x01, 0x00, 0x20, 0x53, 0x77, + 0x69, 0x73, 0x73, 0x20, 0x41, 0x72, 0x6d, 0x79, + 0x20, 0x6b, 0x6e, 0x69, 0x66, 0x65, 0x00, 0x54, + 0x68, 0x61, 0x74, 0x27, 0x73, 0x20, 0x6d, 0x6f, + 0x72, 0x65, 0x20, 0x6c, 0x69, 0x6b, 0x65, 0x20, + 0x69, 0x74, 0x2e, 0x00, 0x00, 0x66, 0x7c, 0xa8, + 0x7c, 0xe0, 0x7c, 0xf9, 0x7c, 0x34, 0x7d, 0x9b, + 0x7d, 0xf7, 0x7d, 0x3a, 0x7e, 0xba, 0x7e, 0x00, + 0x00, 0x01, 0xbb, 0x00, 0x63, 0x00, 0xc4, 0x00, + 0x6b, 0x00, 0xc0, 0x00, 0x98, 0x00, 0xc0, 0x00, + 0x98, 0x00, 0x01, 0x00, 0x6d, 0x75, 0x67, 0x00, + 0x54, 0x68, 0x61, 0x74, 0x27, 0x73, 0x20, 0x73, + 0x75, 0x72, 0x70, 0x72, 0x69, 0x73, 0x69, 0x6e, + 0x67, 0x2c, 0x20, 0x62, 0x75, 0x74, 0x20, 0x74, + 0x68, 0x65, 0x72, 0x65, 0x27, 0x73, 0x20, 0x74, + 0x65, 0x61, 0x00, 0x69, 0x6e, 0x20, 0x69, 0x74, + 0x2e, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xb1, 0x00, 0x98, 0x00, + 0xb1, 0x00, 0x98, 0x00, 0x01, 0x01, 0x62, 0x61, + 0x72, 0x6d, 0x61, 0x6e, 0x00, 0x57, 0x68, 0x61, + 0x74, 0x20, 0x61, 0x20, 0x68, 0x61, 0x72, 0x64, + 0x20, 0x77, 0x6f, 0x72, 0x6b, 0x69, 0x6e, 0x67, + 0x20, 0x63, 0x69, 0x74, 0x69, 0x7a, 0x65, 0x6e, + 0x2e, 0x00, 0x00, 0x03, 0x5e, 0x00, 0xb6, 0x00, + 0xce, 0x00, 0xc7, 0x00, 0x9e, 0x00, 0xb8, 0x00, + 0x9b, 0x00, 0xc7, 0x00, 0x03, 0x01, 0x65, 0x78, + 0x69, 0x74, 0x00, 0x01, 0x04, 0x10, 0x00, 0x34, + 0x00, 0x26, 0x00, 0x92, 0x00, 0x28, 0x00, 0x98, + 0x00, 0x28, 0x00, 0x98, 0x00, 0x01, 0x01, 0x64, + 0x6f, 0x6f, 0x72, 0x00, 0x54, 0x68, 0x69, 0x73, + 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x6c, 0x65, + 0x61, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x20, + 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x00, 0x00, 0x05, + 0x34, 0x00, 0x2b, 0x00, 0x59, 0x00, 0x48, 0x00, + 0x4d, 0x00, 0x98, 0x00, 0x4d, 0x00, 0x98, 0x00, + 0x01, 0x01, 0x72, 0x61, 0x64, 0x69, 0x6f, 0x00, + 0x54, 0x68, 0x65, 0x20, 0x72, 0x61, 0x64, 0x69, + 0x6f, 0x20, 0x6c, 0x6f, 0x6f, 0x6b, 0x73, 0x20, + 0x6c, 0x69, 0x6b, 0x65, 0x20, 0x61, 0x20, 0x72, + 0x61, 0x64, 0x69, 0x6f, 0x2c, 0x20, 0x62, 0x75, + 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x65, + 0x72, 0x69, 0x61, 0x6c, 0x00, 0x69, 0x73, 0x20, + 0x6d, 0x61, 0x64, 0x65, 0x20, 0x6f, 0x66, 0x20, + 0x73, 0x6f, 0x6d, 0x65, 0x20, 0x6b, 0x69, 0x6e, + 0x64, 0x20, 0x6f, 0x66, 0x20, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x72, 0x2e, 0x00, 0x00, 0x06, 0x0a, + 0x01, 0x9f, 0x00, 0x17, 0x01, 0xa6, 0x00, 0x04, + 0x01, 0xc2, 0x00, 0x04, 0x01, 0xc2, 0x00, 0x02, + 0x01, 0x63, 0x72, 0x75, 0x6d, 0x62, 0x73, 0x00, + 0x49, 0x74, 0x27, 0x73, 0x20, 0x64, 0x69, 0x73, + 0x67, 0x75, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2e, + 0x20, 0x50, 0x65, 0x6f, 0x70, 0x6c, 0x65, 0x20, + 0x72, 0x65, 0x61, 0x6c, 0x6c, 0x79, 0x20, 0x73, + 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x00, 0x63, 0x6c, + 0x65, 0x61, 0x6e, 0x20, 0x75, 0x70, 0x20, 0x61, + 0x66, 0x74, 0x65, 0x72, 0x20, 0x74, 0x68, 0x65, + 0x6d, 0x73, 0x65, 0x6c, 0x76, 0x65, 0x73, 0x2e, + 0x00, 0x00, 0x07, 0x62, 0x00, 0x23, 0x00, 0x75, + 0x00, 0x35, 0x00, 0x6e, 0x00, 0x98, 0x00, 0x6e, + 0x00, 0x98, 0x00, 0x01, 0x01, 0x63, 0x6c, 0x6f, + 0x63, 0x6b, 0x00, 0x49, 0x74, 0x20, 0x6c, 0x6f, + 0x6f, 0x6b, 0x73, 0x20, 0x6c, 0x69, 0x6b, 0x65, + 0x20, 0x74, 0x68, 0x65, 0x72, 0x65, 0x27, 0x73, + 0x20, 0x61, 0x20, 0x70, 0x72, 0x6f, 0x62, 0x6c, + 0x65, 0x6d, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, + 0x69, 0x74, 0x2e, 0x00, 0x00, 0x08, 0x7e, 0x00, + 0x22, 0x00, 0x00, 0x01, 0x69, 0x00, 0xc8, 0x00, + 0x98, 0x00, 0xc8, 0x00, 0x98, 0x00, 0x01, 0x01, + 0x73, 0x68, 0x65, 0x6c, 0x76, 0x65, 0x73, 0x00, + 0x49, 0x20, 0x77, 0x6f, 0x6e, 0x64, 0x65, 0x72, + 0x20, 0x77, 0x68, 0x79, 0x20, 0x74, 0x68, 0x65, + 0x72, 0x65, 0x27, 0x73, 0x20, 0x73, 0x75, 0x63, + 0x68, 0x20, 0x61, 0x20, 0x6c, 0x61, 0x72, 0x67, + 0x65, 0x20, 0x63, 0x68, 0x6f, 0x69, 0x63, 0x65, + 0x00, 0x6f, 0x66, 0x20, 0x61, 0x6c, 0x63, 0x6f, + 0x68, 0x6f, 0x6c, 0x20, 0x61, 0x74, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x74, 0x72, 0x61, 0x69, 0x6e, + 0x69, 0x6e, 0x67, 0x20, 0x63, 0x61, 0x6d, 0x70, + 0x2e, 0x00, 0x54, 0x6f, 0x20, 0x6b, 0x65, 0x65, + 0x70, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x72, + 0x6f, 0x6f, 0x70, 0x73, 0x20, 0x68, 0x61, 0x70, + 0x70, 0x79, 0x3f, 0x00, 0x00, 0x09, 0x41, 0x00, + 0x59, 0x00, 0x5b, 0x00, 0x69, 0x00, 0x4d, 0x00, + 0x98, 0x00, 0x4d, 0x00, 0x98, 0x00, 0x01, 0x01, + 0x63, 0x61, 0x73, 0x68, 0x20, 0x72, 0x65, 0x67, + 0x69, 0x73, 0x74, 0x65, 0x72, 0x00, 0x49, 0x20, + 0x6c, 0x6f, 0x76, 0x65, 0x20, 0x69, 0x74, 0x2e, + 0x00, 0x00, 0xf7, 0x7e, 0x29, 0x7f, 0x42, 0x7f, + 0x8b, 0x7f, 0xc4, 0x7f, 0xfc, 0x7f, 0x1d, 0x80, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x32, 0x00, 0xb4, 0x00, 0x32, + 0x00, 0xb4, 0x00, 0x01, 0x01, 0x62, 0x6c, 0x69, + 0x6e, 0x6b, 0x69, 0x6e, 0x67, 0x20, 0x68, 0x6f, + 0x6c, 0x65, 0x00, 0x54, 0x68, 0x61, 0x74, 0x27, + 0x73, 0x20, 0x77, 0x65, 0x69, 0x72, 0x64, 0x2e, + 0x2e, 0x2e, 0x00, 0x00, 0x02, 0x0c, 0x01, 0x55, + 0x00, 0x29, 0x01, 0xaa, 0x00, 0xf0, 0x00, 0xb6, + 0x00, 0xf0, 0x00, 0xb6, 0x00, 0x02, 0x01, 0x64, + 0x6f, 0x6f, 0x72, 0x00, 0x01, 0x03, 0x04, 0x00, + 0x6c, 0x00, 0x5b, 0x00, 0xb2, 0x00, 0x32, 0x00, + 0xb4, 0x00, 0x32, 0x00, 0xb4, 0x00, 0x01, 0x01, + 0x62, 0x61, 0x72, 0x72, 0x65, 0x6c, 0x73, 0x00, + 0x49, 0x74, 0x27, 0x73, 0x20, 0x67, 0x6f, 0x6f, + 0x64, 0x20, 0x74, 0x6f, 0x20, 0x6b, 0x6e, 0x6f, + 0x77, 0x20, 0x6f, 0x75, 0x72, 0x20, 0x61, 0x72, + 0x6d, 0x79, 0x20, 0x69, 0x73, 0x20, 0x77, 0x65, + 0x6c, 0x6c, 0x20, 0x65, 0x71, 0x75, 0x69, 0x70, + 0x70, 0x65, 0x64, 0x2e, 0x00, 0x00, 0x04, 0x5a, + 0x00, 0x5f, 0x00, 0x78, 0x00, 0xa8, 0x00, 0x70, + 0x00, 0xad, 0x00, 0x70, 0x00, 0xad, 0x00, 0x01, + 0x01, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x72, 0x00, + 0x49, 0x74, 0x27, 0x73, 0x20, 0x61, 0x20, 0x6c, + 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x20, 0x6b, 0x69, + 0x6e, 0x64, 0x20, 0x6f, 0x66, 0x20, 0x6c, 0x6f, + 0x63, 0x6b, 0x65, 0x72, 0x2e, 0x00, 0x00, 0x05, + 0x96, 0x00, 0x6b, 0x00, 0xd6, 0x00, 0xa4, 0x00, + 0xb7, 0x00, 0xb2, 0x00, 0xb7, 0x00, 0xb2, 0x00, + 0x01, 0x01, 0x6c, 0x6f, 0x74, 0x73, 0x20, 0x6f, + 0x66, 0x20, 0x64, 0x72, 0x61, 0x77, 0x65, 0x72, + 0x73, 0x00, 0x49, 0x20, 0x68, 0x61, 0x74, 0x65, + 0x20, 0x62, 0x75, 0x72, 0x65, 0x61, 0x75, 0x63, + 0x72, 0x61, 0x63, 0x79, 0x2e, 0x00, 0x00, 0x06, + 0x7e, 0x00, 0x8f, 0x00, 0x97, 0x00, 0xaa, 0x00, + 0x8d, 0x00, 0xb1, 0x00, 0x8d, 0x00, 0xb1, 0x00, + 0x01, 0x01, 0x62, 0x6f, 0x78, 0x65, 0x73, 0x00, + 0x27, 0x50, 0x65, 0x70, 0x61, 0x27, 0x00, 0x00, + 0x07, 0xd7, 0x00, 0x81, 0x00, 0xea, 0x00, 0xaa, + 0x00, 0xe0, 0x00, 0xb6, 0x00, 0xe0, 0x00, 0xb6, + 0x00, 0x01, 0x01, 0x62, 0x6f, 0x78, 0x65, 0x73, + 0x00, 0x27, 0x53, 0x61, 0x6c, 0x74, 0x27, 0x00, + 0x00, 0x48, 0x80, 0x87, 0x80, 0xd1, 0x80, 0x01, + 0x81, 0x00, 0x00, 0x01, 0x7e, 0x00, 0x5f, 0x00, + 0x86, 0x00, 0x69, 0x00, 0x8b, 0x00, 0x9c, 0x00, + 0x84, 0x00, 0x95, 0x00, 0x01, 0x00, 0x67, 0x72, + 0x65, 0x6e, 0x61, 0x64, 0x65, 0x00, 0x57, 0x6f, + 0x77, 0x21, 0x20, 0x49, 0x20, 0x61, 0x6c, 0x77, + 0x61, 0x79, 0x73, 0x20, 0x77, 0x61, 0x6e, 0x74, + 0x65, 0x64, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f, + 0x66, 0x20, 0x74, 0x68, 0x65, 0x73, 0x65, 0x2e, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x8b, 0x00, 0x9c, 0x00, 0x8b, + 0x00, 0x9c, 0x00, 0x01, 0x01, 0x67, 0x75, 0x61, + 0x72, 0x64, 0x00, 0x49, 0x20, 0x64, 0x6f, 0x6e, + 0x27, 0x74, 0x20, 0x74, 0x68, 0x69, 0x6e, 0x6b, + 0x20, 0x68, 0x65, 0x27, 0x73, 0x20, 0x6d, 0x61, + 0x6b, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x69, + 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x2e, 0x00, 0x00, 0x03, 0x00, 0x00, 0x6f, + 0x00, 0x54, 0x00, 0xa2, 0x00, 0x53, 0x00, 0x95, + 0x00, 0x53, 0x00, 0x95, 0x00, 0x04, 0x01, 0x77, + 0x61, 0x79, 0x20, 0x6f, 0x75, 0x74, 0x00, 0x48, + 0x6f, 0x6d, 0x65, 0x2c, 0x20, 0x73, 0x77, 0x65, + 0x65, 0x74, 0x20, 0x68, 0x6f, 0x6d, 0x65, 0x2e, + 0x2e, 0x2e, 0x00, 0x00, 0x04, 0x10, 0x01, 0x95, + 0x00, 0x3f, 0x01, 0xc7, 0x00, 0x0d, 0x01, 0xaf, + 0x00, 0x3f, 0x01, 0xb7, 0x00, 0x02, 0x01, 0x70, + 0x61, 0x74, 0x68, 0x00, 0x01, 0x3a, 0x81, 0x79, + 0x81, 0xa3, 0x81, 0xc0, 0x81, 0xdd, 0x81, 0x11, + 0x82, 0x3d, 0x82, 0x7f, 0x82, 0xa4, 0x82, 0xcc, + 0x82, 0x1a, 0x83, 0x3c, 0x83, 0x64, 0x83, 0xb8, + 0x83, 0xea, 0x83, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x00, + 0xa3, 0x00, 0x8a, 0x00, 0xa3, 0x00, 0x02, 0x01, + 0x67, 0x75, 0x61, 0x72, 0x64, 0x00, 0x48, 0x65, + 0x20, 0x6c, 0x6f, 0x6f, 0x6b, 0x73, 0x20, 0x6c, + 0x69, 0x6b, 0x65, 0x20, 0x61, 0x20, 0x70, 0x65, + 0x61, 0x73, 0x61, 0x6e, 0x74, 0x20, 0x69, 0x6e, + 0x00, 0x64, 0x69, 0x73, 0x67, 0x75, 0x69, 0x73, + 0x65, 0x2e, 0x00, 0x00, 0x02, 0x70, 0x00, 0xa0, + 0x00, 0x7b, 0x00, 0xa8, 0x00, 0x68, 0x00, 0xab, + 0x00, 0x68, 0x00, 0xab, 0x00, 0x02, 0x00, 0x62, + 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x00, 0x49, 0x74, + 0x27, 0x73, 0x20, 0x77, 0x68, 0x69, 0x73, 0x6b, + 0x79, 0x21, 0x2e, 0x2e, 0x00, 0x00, 0x03, 0xd2, + 0x00, 0x77, 0x00, 0xf6, 0x00, 0x98, 0x00, 0x8a, + 0x00, 0xa3, 0x00, 0x8a, 0x00, 0xa3, 0x00, 0x02, + 0x01, 0x67, 0x61, 0x72, 0x67, 0x6f, 0x79, 0x6c, + 0x65, 0x00, 0x01, 0x04, 0x09, 0x01, 0x80, 0x00, + 0x2f, 0x01, 0xad, 0x00, 0x8a, 0x00, 0xa3, 0x00, + 0x8a, 0x00, 0xa3, 0x00, 0x02, 0x01, 0x67, 0x61, + 0x72, 0x67, 0x6f, 0x79, 0x6c, 0x65, 0x00, 0x01, + 0x05, 0x12, 0x01, 0x39, 0x00, 0x23, 0x01, 0x48, + 0x00, 0x8a, 0x00, 0xa3, 0x00, 0x8a, 0x00, 0xa3, + 0x00, 0x02, 0x01, 0x73, 0x63, 0x75, 0x6c, 0x70, + 0x74, 0x75, 0x72, 0x65, 0x00, 0x52, 0x65, 0x61, + 0x6c, 0x6c, 0x79, 0x20, 0x73, 0x6f, 0x70, 0x68, + 0x69, 0x73, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, + 0x64, 0x21, 0x00, 0x00, 0x06, 0xfc, 0x00, 0x33, + 0x00, 0x32, 0x01, 0xa1, 0x00, 0x8a, 0x00, 0xa3, + 0x00, 0x8a, 0x00, 0xa3, 0x00, 0x02, 0x01, 0x64, + 0x6f, 0x6f, 0x72, 0x00, 0x53, 0x6f, 0x6c, 0x69, + 0x64, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6d, 0x61, + 0x73, 0x73, 0x69, 0x76, 0x65, 0x2e, 0x00, 0x00, + 0x07, 0xe5, 0x00, 0x00, 0x00, 0x3f, 0x01, 0x98, + 0x00, 0x8a, 0x00, 0xa3, 0x00, 0x8a, 0x00, 0xa3, + 0x00, 0x02, 0x01, 0x6d, 0x61, 0x6e, 0x73, 0x69, + 0x6f, 0x6e, 0x20, 0x77, 0x61, 0x6c, 0x6c, 0x00, + 0x49, 0x74, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, + 0x62, 0x65, 0x20, 0x33, 0x20, 0x6b, 0x69, 0x6c, + 0x6f, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x20, + 0x74, 0x68, 0x69, 0x63, 0x6b, 0x2e, 0x2e, 0x2e, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xb5, 0x00, 0xb0, 0x00, 0xb5, + 0x00, 0xb0, 0x00, 0x01, 0x00, 0x4a, 0x6f, 0x68, + 0x6e, 0x20, 0x4e, 0x6f, 0x74, 0x79, 0x00, 0x46, + 0x61, 0x74, 0x73, 0x6f, 0x2e, 0x00, 0x00, 0x09, + 0x9c, 0x00, 0xae, 0x00, 0x3f, 0x01, 0xc7, 0x00, + 0x8a, 0x00, 0xa3, 0x00, 0x3f, 0x01, 0xc6, 0x00, + 0x02, 0x01, 0x70, 0x61, 0x74, 0x68, 0x20, 0x61, + 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x6d, 0x61, + 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x01, 0x0a, + 0x00, 0x00, 0x73, 0x00, 0x46, 0x00, 0x98, 0x00, + 0x3c, 0x00, 0xab, 0x00, 0x00, 0x00, 0x7c, 0x00, + 0x01, 0x01, 0x70, 0x61, 0x74, 0x68, 0x20, 0x74, + 0x6f, 0x20, 0x6d, 0x65, 0x61, 0x64, 0x6f, 0x77, + 0x00, 0x49, 0x74, 0x20, 0x6c, 0x65, 0x61, 0x64, + 0x73, 0x20, 0x69, 0x6e, 0x73, 0x69, 0x64, 0x65, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x61, 0x72, + 0x6b, 0x20, 0x61, 0x6e, 0x64, 0x00, 0x73, 0x63, + 0x61, 0x72, 0x79, 0x20, 0x66, 0x6f, 0x72, 0x65, + 0x73, 0x74, 0x2e, 0x00, 0x00, 0x0b, 0x00, 0x00, + 0xa1, 0x00, 0x39, 0x00, 0xbe, 0x00, 0x3c, 0x00, + 0xab, 0x00, 0x00, 0x00, 0xab, 0x00, 0x04, 0x01, + 0x70, 0x61, 0x74, 0x68, 0x20, 0x74, 0x6f, 0x20, + 0x73, 0x68, 0x6f, 0x72, 0x65, 0x00, 0x01, 0x0c, + 0x8c, 0x00, 0x68, 0x00, 0xe4, 0x00, 0x98, 0x00, + 0x8a, 0x00, 0xa3, 0x00, 0xe4, 0x00, 0x6d, 0x00, + 0x02, 0x01, 0x70, 0x61, 0x74, 0x68, 0x20, 0x61, + 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x6d, 0x61, + 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x01, 0x0d, + 0xbf, 0x00, 0xa6, 0x00, 0xc8, 0x00, 0xae, 0x00, + 0x8a, 0x00, 0xa3, 0x00, 0xb5, 0x00, 0xb0, 0x00, + 0x02, 0x01, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, + 0x72, 0x00, 0x49, 0x74, 0x27, 0x73, 0x20, 0x61, + 0x20, 0x63, 0x65, 0x6c, 0x6c, 0x6f, 0x70, 0x68, + 0x61, 0x6e, 0x65, 0x20, 0x77, 0x72, 0x61, 0x70, + 0x70, 0x65, 0x72, 0x20, 0x66, 0x72, 0x6f, 0x6d, + 0x20, 0x73, 0x6f, 0x6d, 0x65, 0x20, 0x63, 0x61, + 0x6e, 0x64, 0x79, 0x00, 0x6f, 0x72, 0x20, 0x73, + 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x69, 0x6e, 0x67, + 0x2e, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0xe4, 0x00, 0x98, 0x00, 0x3c, 0x00, 0xab, 0x00, + 0x3c, 0x00, 0xab, 0x00, 0x01, 0x01, 0x66, 0x6f, + 0x72, 0x65, 0x73, 0x74, 0x00, 0x49, 0x74, 0x20, + 0x67, 0x69, 0x76, 0x65, 0x73, 0x20, 0x6d, 0x65, + 0x20, 0x74, 0x68, 0x72, 0x69, 0x6c, 0x6c, 0x73, + 0x2e, 0x2e, 0x2e, 0x00, 0x00, 0x0f, 0x78, 0x00, + 0x9a, 0x00, 0x85, 0x00, 0xa0, 0x00, 0x70, 0x00, + 0xa3, 0x00, 0x70, 0x00, 0xa3, 0x00, 0x02, 0x00, + 0x62, 0x61, 0x6e, 0x6b, 0x6e, 0x6f, 0x74, 0x65, + 0x00, 0x49, 0x27, 0x6d, 0x20, 0x72, 0x65, 0x61, + 0x6c, 0x6c, 0x79, 0x20, 0x70, 0x72, 0x6f, 0x75, + 0x64, 0x20, 0x49, 0x20, 0x64, 0x69, 0x64, 0x6e, + 0x27, 0x74, 0x20, 0x54, 0x41, 0x4b, 0x45, 0x20, + 0x54, 0x48, 0x41, 0x54, 0x2e, 0x00, 0x00, 0x40, + 0x84, 0x56, 0x84, 0x87, 0x84, 0xb6, 0x84, 0xf2, + 0x84, 0x29, 0x85, 0x66, 0x85, 0x8e, 0x85, 0xb6, + 0x85, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x04, 0x01, 0x74, 0x00, 0x13, 0x01, + 0x8a, 0x00, 0x02, 0x01, 0xbd, 0x00, 0x02, 0x01, + 0xbd, 0x00, 0x01, 0x01, 0x68, 0x6f, 0x6c, 0x6c, + 0x6f, 0x77, 0x00, 0x49, 0x74, 0x27, 0x73, 0x20, + 0x65, 0x6d, 0x70, 0x74, 0x79, 0x20, 0x72, 0x69, + 0x67, 0x68, 0x74, 0x20, 0x6e, 0x6f, 0x77, 0x2e, + 0x00, 0x00, 0x03, 0xd0, 0x00, 0x6e, 0x00, 0xf8, + 0x00, 0x7e, 0x00, 0xcb, 0x00, 0xab, 0x00, 0xcb, + 0x00, 0xab, 0x00, 0x02, 0x01, 0x62, 0x72, 0x61, + 0x6e, 0x63, 0x68, 0x00, 0x49, 0x74, 0x20, 0x63, + 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x62, 0x65, 0x20, + 0x75, 0x73, 0x65, 0x66, 0x75, 0x6c, 0x2e, 0x00, + 0x00, 0x04, 0xe4, 0x00, 0x3c, 0x00, 0x2d, 0x01, + 0xaa, 0x00, 0x02, 0x01, 0xbd, 0x00, 0xfc, 0x00, + 0xab, 0x00, 0x01, 0x01, 0x74, 0x72, 0x65, 0x65, + 0x00, 0x49, 0x74, 0x27, 0x73, 0x20, 0x68, 0x69, + 0x67, 0x68, 0x65, 0x72, 0x20, 0x74, 0x68, 0x61, + 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x61, + 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x77, 0x61, + 0x6c, 0x6c, 0x21, 0x00, 0x00, 0x05, 0x0c, 0x00, + 0x8a, 0x00, 0x33, 0x00, 0xad, 0x00, 0x31, 0x00, + 0xac, 0x00, 0x31, 0x00, 0xac, 0x00, 0x04, 0x01, + 0x77, 0x69, 0x6c, 0x64, 0x20, 0x70, 0x6c, 0x61, + 0x6e, 0x74, 0x00, 0x49, 0x74, 0x27, 0x73, 0x20, + 0x73, 0x6f, 0x6d, 0x65, 0x20, 0x77, 0x69, 0x6c, + 0x64, 0x20, 0x70, 0x6f, 0x74, 0x61, 0x74, 0x6f, + 0x65, 0x2e, 0x00, 0x00, 0x06, 0x00, 0x00, 0x22, + 0x00, 0x04, 0x01, 0xa0, 0x00, 0xa6, 0x00, 0xbd, + 0x00, 0xa6, 0x00, 0xbd, 0x00, 0x01, 0x01, 0x77, + 0x61, 0x6c, 0x6c, 0x00, 0x49, 0x74, 0x27, 0x73, + 0x20, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x65, 0x64, + 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x73, 0x6f, + 0x6d, 0x65, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x20, + 0x73, 0x6d, 0x6f, 0x6f, 0x74, 0x68, 0x2e, 0x00, + 0x00, 0x07, 0x25, 0x01, 0x5c, 0x00, 0x3f, 0x01, + 0xaa, 0x00, 0x2a, 0x01, 0xaf, 0x00, 0x3f, 0x01, + 0x98, 0x00, 0x01, 0x01, 0x70, 0x61, 0x74, 0x68, + 0x20, 0x61, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, + 0x6d, 0x61, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x00, + 0x01, 0x08, 0x00, 0x00, 0xae, 0x00, 0x3c, 0x00, + 0xc7, 0x00, 0x1e, 0x00, 0xbd, 0x00, 0x00, 0x00, + 0xbd, 0x00, 0x04, 0x01, 0x70, 0x61, 0x74, 0x68, + 0x20, 0x61, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, + 0x6d, 0x61, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x00, + 0x01, 0x09, 0xd0, 0x00, 0x9c, 0x00, 0xec, 0x00, + 0xa8, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x62, 0x72, 0x61, 0x6e, + 0x63, 0x68, 0x00, 0x47, 0x6f, 0x74, 0x63, 0x68, + 0x61, 0x2e, 0x00, 0x00, 0xe9, 0x85, 0x0d, 0x86, + 0x42, 0x86, 0x6a, 0x86, 0xa7, 0x86, 0xcf, 0x86, + 0x07, 0x87, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x31, 0x00, 0x9d, 0x00, 0x12, 0x00, 0xad, + 0x00, 0x12, 0x00, 0xad, 0x00, 0x01, 0x01, 0x74, + 0x72, 0x65, 0x65, 0x00, 0x49, 0x74, 0x27, 0x73, + 0x20, 0x68, 0x69, 0x67, 0x68, 0x2e, 0x00, 0x00, + 0x02, 0xf6, 0x00, 0x7c, 0x00, 0x35, 0x01, 0xa6, + 0x00, 0x05, 0x01, 0xb7, 0x00, 0x05, 0x01, 0xb7, + 0x00, 0x01, 0x01, 0x77, 0x69, 0x6c, 0x64, 0x20, + 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x00, 0x4f, 0x72, + 0x64, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x20, 0x67, + 0x72, 0x65, 0x65, 0x6e, 0x20, 0x73, 0x74, 0x75, + 0x66, 0x66, 0x2e, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x9e, 0x00, 0x2f, 0x00, 0xba, 0x00, 0x12, 0x00, + 0xad, 0x00, 0x00, 0x00, 0xac, 0x00, 0x04, 0x01, + 0x70, 0x61, 0x74, 0x68, 0x20, 0x61, 0x72, 0x6f, + 0x75, 0x6e, 0x64, 0x20, 0x6d, 0x61, 0x6e, 0x73, + 0x69, 0x6f, 0x6e, 0x00, 0x01, 0x04, 0x00, 0x00, + 0x20, 0x00, 0x3f, 0x01, 0xa6, 0x00, 0xc5, 0x00, + 0xb7, 0x00, 0xc5, 0x00, 0xb7, 0x00, 0x01, 0x01, + 0x77, 0x61, 0x6c, 0x6c, 0x00, 0x49, 0x74, 0x27, + 0x73, 0x20, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x65, + 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x73, + 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x69, 0x6e, 0x67, + 0x20, 0x73, 0x6d, 0x6f, 0x6f, 0x74, 0x68, 0x2e, + 0x00, 0x00, 0x05, 0x06, 0x01, 0xa6, 0x00, 0x3f, + 0x01, 0xc7, 0x00, 0x05, 0x01, 0xb7, 0x00, 0x3f, + 0x01, 0xb5, 0x00, 0x02, 0x01, 0x70, 0x61, 0x74, + 0x68, 0x20, 0x61, 0x72, 0x6f, 0x75, 0x6e, 0x64, + 0x20, 0x6d, 0x61, 0x6e, 0x73, 0x69, 0x6f, 0x6e, + 0x00, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x67, 0x00, 0xc0, 0x00, 0x67, + 0x00, 0xc0, 0x00, 0x04, 0x01, 0x68, 0x65, 0x64, + 0x67, 0x65, 0x68, 0x6f, 0x67, 0x00, 0x49, 0x73, + 0x20, 0x68, 0x61, 0x73, 0x20, 0x61, 0x20, 0x63, + 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x6e, 0x20, 0x69, + 0x74, 0x73, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x2e, + 0x00, 0x00, 0x07, 0xdb, 0x00, 0xba, 0x00, 0xeb, + 0x00, 0xc3, 0x00, 0xd7, 0x00, 0xc3, 0x00, 0xd7, + 0x00, 0xc3, 0x00, 0x02, 0x01, 0x72, 0x6f, 0x63, + 0x6b, 0x00, 0x01, 0x2c, 0x87, 0x59, 0x87, 0x7f, + 0x87, 0xac, 0x87, 0xe2, 0x87, 0x00, 0x00, 0x01, + 0xb2, 0x00, 0x24, 0x00, 0xbe, 0x00, 0x36, 0x00, + 0xbc, 0x00, 0x96, 0x00, 0xbc, 0x00, 0x96, 0x00, + 0x01, 0x01, 0x62, 0x65, 0x65, 0x73, 0x20, 0x6e, + 0x65, 0x73, 0x74, 0x00, 0x48, 0x6f, 0x6e, 0x65, + 0x79, 0x20, 0x62, 0x61, 0x6c, 0x6c, 0x6f, 0x6f, + 0x6e, 0x2e, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0x96, + 0x00, 0xbc, 0x00, 0x96, 0x00, 0x01, 0x01, 0x62, + 0x65, 0x65, 0x73, 0x00, 0x49, 0x20, 0x48, 0x41, + 0x54, 0x45, 0x20, 0x54, 0x48, 0x45, 0x4d, 0x21, + 0x00, 0x00, 0x03, 0x1c, 0x00, 0x5d, 0x00, 0x72, + 0x00, 0xc1, 0x00, 0x3f, 0x00, 0xc3, 0x00, 0x3f, + 0x00, 0xc3, 0x00, 0x01, 0x01, 0x62, 0x75, 0x73, + 0x68, 0x00, 0x4e, 0x69, 0x63, 0x65, 0x20, 0x70, + 0x6c, 0x61, 0x63, 0x65, 0x20, 0x74, 0x6f, 0x20, + 0x68, 0x69, 0x64, 0x65, 0x2e, 0x00, 0x00, 0x04, + 0xa6, 0x00, 0x76, 0x00, 0xe6, 0x00, 0x8d, 0x00, + 0xbc, 0x00, 0x96, 0x00, 0xbc, 0x00, 0x96, 0x00, + 0x01, 0x01, 0x76, 0x61, 0x6c, 0x76, 0x65, 0x00, + 0x49, 0x74, 0x27, 0x73, 0x20, 0x72, 0x75, 0x73, + 0x74, 0x79, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x68, + 0x61, 0x73, 0x20, 0x6e, 0x6f, 0x20, 0x6c, 0x6f, + 0x63, 0x6b, 0x2e, 0x00, 0x00, 0x05, 0xd6, 0x00, + 0xb4, 0x00, 0x3f, 0x01, 0xc7, 0x00, 0xdb, 0x00, + 0xb7, 0x00, 0x0e, 0x01, 0xc7, 0x00, 0x02, 0x01, + 0x70, 0x61, 0x74, 0x68, 0x20, 0x74, 0x6f, 0x20, + 0x6d, 0x61, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x00, + 0x01, 0x18, 0x88, 0x6f, 0x88, 0xad, 0x88, 0xf4, + 0x88, 0x40, 0x89, 0x6e, 0x89, 0x92, 0x89, 0xb6, + 0x89, 0x00, 0x00, 0x01, 0x90, 0x00, 0x0f, 0x00, + 0xa3, 0x00, 0x1d, 0x00, 0x9d, 0x00, 0xb4, 0x00, + 0x9d, 0x00, 0xb4, 0x00, 0x01, 0x01, 0x66, 0x6c, + 0x6f, 0x77, 0x65, 0x72, 0x00, 0x49, 0x74, 0x27, + 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x6f, + 0x73, 0x74, 0x20, 0x62, 0x65, 0x61, 0x75, 0x74, + 0x69, 0x66, 0x75, 0x6c, 0x20, 0x66, 0x6c, 0x6f, + 0x77, 0x65, 0x72, 0x20, 0x49, 0x27, 0x76, 0x65, + 0x20, 0x73, 0x65, 0x65, 0x6e, 0x00, 0x69, 0x6e, + 0x20, 0x6d, 0x79, 0x20, 0x65, 0x6e, 0x74, 0x69, + 0x72, 0x65, 0x20, 0x6c, 0x69, 0x66, 0x65, 0x21, + 0x00, 0x00, 0x02, 0x69, 0x00, 0x00, 0x00, 0xcf, + 0x00, 0x27, 0x00, 0x9d, 0x00, 0xb4, 0x00, 0x9d, + 0x00, 0xb4, 0x00, 0x01, 0x01, 0x69, 0x73, 0x6c, + 0x65, 0x00, 0x4e, 0x65, 0x61, 0x74, 0x20, 0x70, + 0x6c, 0x61, 0x63, 0x65, 0x20, 0x74, 0x6f, 0x20, + 0x73, 0x74, 0x61, 0x72, 0x74, 0x20, 0x65, 0x63, + 0x6f, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, + 0x20, 0x6c, 0x69, 0x66, 0x65, 0x2e, 0x00, 0x00, + 0x03, 0x3c, 0x00, 0x88, 0x00, 0x7b, 0x00, 0xa7, + 0x00, 0x6b, 0x00, 0xa9, 0x00, 0x6b, 0x00, 0xa9, + 0x00, 0x04, 0x01, 0x62, 0x6f, 0x61, 0x74, 0x00, + 0x49, 0x20, 0x63, 0x61, 0x6e, 0x27, 0x74, 0x20, + 0x62, 0x65, 0x6c, 0x69, 0x65, 0x76, 0x65, 0x20, + 0x69, 0x74, 0x2c, 0x20, 0x62, 0x75, 0x74, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x62, 0x6f, 0x61, 0x74, + 0x20, 0x68, 0x61, 0x73, 0x20, 0x6e, 0x6f, 0x20, + 0x68, 0x6f, 0x6c, 0x65, 0x2e, 0x00, 0x00, 0x04, + 0xec, 0x00, 0xa4, 0x00, 0x20, 0x01, 0xc3, 0x00, + 0xec, 0x00, 0xb3, 0x00, 0xec, 0x00, 0xb3, 0x00, + 0x03, 0x01, 0x77, 0x65, 0x6c, 0x6c, 0x00, 0x49, + 0x20, 0x72, 0x65, 0x66, 0x75, 0x73, 0x65, 0x20, + 0x74, 0x6f, 0x20, 0x74, 0x65, 0x6c, 0x6c, 0x20, + 0x63, 0x68, 0x65, 0x61, 0x70, 0x20, 0x6a, 0x6f, + 0x6b, 0x65, 0x73, 0x20, 0x77, 0x69, 0x74, 0x68, + 0x00, 0x74, 0x68, 0x65, 0x20, 0x77, 0x6f, 0x72, + 0x64, 0x20, 0x22, 0x77, 0x65, 0x6c, 0x6c, 0x22, + 0x2e, 0x00, 0x00, 0x05, 0xdd, 0x00, 0x96, 0x00, + 0xe8, 0x00, 0xa2, 0x00, 0xc8, 0x00, 0xb3, 0x00, + 0xc8, 0x00, 0xb3, 0x00, 0x02, 0x01, 0x68, 0x61, + 0x6e, 0x64, 0x6c, 0x65, 0x00, 0x49, 0x20, 0x63, + 0x61, 0x6e, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, + 0x65, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x00, + 0x00, 0x06, 0x88, 0x00, 0xb7, 0x00, 0xa5, 0x00, + 0xc7, 0x00, 0x9d, 0x00, 0xb4, 0x00, 0x99, 0x00, + 0xc7, 0x00, 0x03, 0x01, 0x70, 0x61, 0x74, 0x68, + 0x20, 0x74, 0x6f, 0x20, 0x76, 0x69, 0x6c, 0x6c, + 0x61, 0x67, 0x65, 0x00, 0x01, 0x07, 0x25, 0x01, + 0x96, 0x00, 0x3f, 0x01, 0xb6, 0x00, 0x06, 0x01, + 0xb0, 0x00, 0x3f, 0x01, 0xa2, 0x00, 0x02, 0x01, + 0x70, 0x61, 0x74, 0x68, 0x20, 0x74, 0x6f, 0x20, + 0x6d, 0x61, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x00, + 0x01, 0x08, 0x00, 0x00, 0x96, 0x00, 0x20, 0x00, + 0xb5, 0x00, 0x1e, 0x00, 0xac, 0x00, 0x00, 0x00, + 0xac, 0x00, 0x04, 0x01, 0x70, 0x61, 0x74, 0x68, + 0x20, 0x74, 0x6f, 0x20, 0x66, 0x6f, 0x72, 0x65, + 0x73, 0x74, 0x00, 0x01, 0xe1, 0x89, 0x0c, 0x8a, + 0x3c, 0x8a, 0x00, 0x00, 0x01, 0x7d, 0x00, 0x3b, + 0x00, 0x8c, 0x00, 0x59, 0x00, 0x7b, 0x00, 0x5e, + 0x00, 0x7b, 0x00, 0x5e, 0x00, 0x02, 0x01, 0x66, + 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x00, 0x49, 0x74, + 0x27, 0x73, 0x20, 0x62, 0x65, 0x61, 0x75, 0x74, + 0x69, 0x66, 0x75, 0x6c, 0x21, 0x00, 0x00, 0x02, + 0x8d, 0x00, 0x3b, 0x00, 0x9e, 0x00, 0x57, 0x00, + 0xa0, 0x00, 0x5a, 0x00, 0xa0, 0x00, 0x5a, 0x00, + 0x04, 0x01, 0x66, 0x6c, 0x6f, 0x77, 0x65, 0x72, + 0x00, 0x49, 0x74, 0x20, 0x73, 0x6d, 0x65, 0x6c, + 0x6c, 0x73, 0x20, 0x76, 0x65, 0x72, 0x79, 0x20, + 0x6e, 0x69, 0x63, 0x65, 0x21, 0x00, 0x00, 0x03, + 0xd2, 0x00, 0x4c, 0x00, 0x38, 0x01, 0x7b, 0x00, + 0xec, 0x00, 0x5f, 0x00, 0xec, 0x00, 0x5f, 0x00, + 0x03, 0x01, 0x62, 0x6f, 0x61, 0x74, 0x00, 0x4e, + 0x6f, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x20, 0x6e, + 0x65, 0x77, 0x2e, 0x20, 0x4c, 0x69, 0x74, 0x65, + 0x72, 0x61, 0x6c, 0x6c, 0x79, 0x2e, 0x00, 0x00, + 0x87, 0x8a, 0xa1, 0x8a, 0xbb, 0x8a, 0xd2, 0x8a, + 0xe9, 0x8a, 0x05, 0x8b, 0x3c, 0x8b, 0x1f, 0x8b, + 0x59, 0x8b, 0x76, 0x8b, 0x93, 0x8b, 0xae, 0x8b, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x01, 0x66, 0x69, 0x73, + 0x68, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x66, + 0x69, 0x73, 0x68, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x3f, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x3f, 0x00, 0x00, 0x00, 0x05, 0x21, 0x00, 0xb1, + 0x00, 0x2b, 0x00, 0xbb, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x61, + 0x6e, 0x63, 0x68, 0x6f, 0x72, 0x00, 0x00, 0x00, + 0x06, 0x29, 0x00, 0x90, 0x00, 0x99, 0x00, 0xba, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x01, 0x62, 0x6f, 0x61, 0x74, 0x00, + 0x00, 0x00, 0x08, 0x0f, 0x00, 0x90, 0x00, 0x2d, + 0x00, 0xbc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x01, 0x73, 0x65, 0x61, + 0x77, 0x65, 0x65, 0x64, 0x00, 0x00, 0x00, 0x07, + 0x00, 0x00, 0x56, 0x00, 0x22, 0x00, 0x97, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x01, 0x73, 0x65, 0x61, 0x77, 0x65, 0x65, + 0x64, 0x00, 0x00, 0x00, 0x09, 0xce, 0x00, 0xa3, + 0x00, 0xfc, 0x00, 0xc7, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x73, + 0x65, 0x61, 0x77, 0x65, 0x65, 0x64, 0x00, 0x00, + 0x00, 0x0a, 0xde, 0x00, 0x3c, 0x00, 0x08, 0x01, + 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x01, 0x73, 0x65, 0x61, 0x77, + 0x65, 0x65, 0x64, 0x00, 0x00, 0x00, 0x0b, 0x98, + 0x00, 0x96, 0x00, 0xbb, 0x00, 0xb5, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x01, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x00, 0x00, + 0x00, 0x0c, 0x68, 0x00, 0x7a, 0x00, 0xe0, 0x00, + 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x01, 0x73, 0x74, 0x6f, 0x6e, + 0x65, 0x73, 0x00, 0x00, 0x00, 0xda, 0x8b, 0x01, + 0x8c, 0x39, 0x8c, 0x63, 0x8c, 0x90, 0x8c, 0xb2, + 0x8c, 0xd2, 0x8c, 0x00, 0x00, 0x01, 0xbd, 0x00, + 0xaf, 0x00, 0x3f, 0x01, 0xc7, 0x00, 0xc9, 0x00, + 0xc0, 0x00, 0xec, 0x00, 0xc7, 0x00, 0x02, 0x01, + 0x70, 0x61, 0x74, 0x68, 0x20, 0x74, 0x6f, 0x20, + 0x6c, 0x61, 0x6b, 0x65, 0x20, 0x73, 0x68, 0x6f, + 0x72, 0x65, 0x00, 0x01, 0x02, 0x37, 0x00, 0x50, + 0x00, 0x50, 0x00, 0x69, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x04, 0x01, 0x64, + 0x6f, 0x6f, 0x72, 0x00, 0x54, 0x68, 0x65, 0x72, + 0x65, 0x27, 0x73, 0x20, 0x6e, 0x6f, 0x20, 0x6e, + 0x61, 0x6d, 0x65, 0x20, 0x6f, 0x6e, 0x20, 0x74, + 0x68, 0x65, 0x00, 0x64, 0x6f, 0x6f, 0x72, 0x2e, + 0x2e, 0x2e, 0x00, 0x00, 0x03, 0x67, 0x00, 0x46, + 0x00, 0x85, 0x00, 0x5d, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x02, 0x01, 0x77, + 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x00, 0x49, + 0x20, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x20, + 0x44, 0x4f, 0x53, 0x2e, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc9, + 0x00, 0xc0, 0x00, 0xc9, 0x00, 0xc0, 0x00, 0x01, + 0x01, 0x73, 0x71, 0x75, 0x69, 0x72, 0x72, 0x65, + 0x6c, 0x00, 0x49, 0x74, 0x27, 0x73, 0x20, 0x76, + 0x65, 0x72, 0x79, 0x20, 0x66, 0x61, 0x73, 0x74, + 0x2e, 0x00, 0x00, 0x05, 0x92, 0x00, 0x35, 0x00, + 0x99, 0x00, 0x3c, 0x00, 0xc9, 0x00, 0xc0, 0x00, + 0xc9, 0x00, 0xc0, 0x00, 0x01, 0x01, 0x6e, 0x75, + 0x74, 0x00, 0x49, 0x74, 0x27, 0x73, 0x20, 0x62, + 0x69, 0x67, 0x2e, 0x00, 0x00, 0x06, 0x8c, 0x00, + 0xbb, 0x00, 0x92, 0x00, 0xc1, 0x00, 0x9c, 0x00, + 0xc2, 0x00, 0x9c, 0x00, 0xc2, 0x00, 0x04, 0x00, + 0x6e, 0x75, 0x74, 0x00, 0x47, 0x6f, 0x74, 0x63, + 0x68, 0x61, 0x2e, 0x00, 0x00, 0x07, 0x48, 0x00, + 0xb4, 0x00, 0xaa, 0x00, 0xc7, 0x00, 0xa2, 0x00, + 0xc2, 0x00, 0xa2, 0x00, 0xc2, 0x00, 0x04, 0x01, + 0x67, 0x72, 0x61, 0x73, 0x73, 0x00, 0x01, 0x0c, + 0x8d, 0x28, 0x8d, 0x6f, 0x8d, 0xcb, 0x8d, 0xe8, + 0x8d, 0x22, 0x8e, 0x3f, 0x8e, 0x5e, 0x8e, 0x99, + 0x8e, 0xd7, 0x8e, 0x12, 0x8f, 0x2c, 0x8f, 0x54, + 0x8f, 0x71, 0x8f, 0xcc, 0x8f, 0x00, 0x00, 0x01, + 0x90, 0x00, 0xbd, 0x00, 0x3f, 0x01, 0xc7, 0x00, + 0xdf, 0x00, 0xc1, 0x00, 0xdf, 0x00, 0xc7, 0x00, + 0x03, 0x01, 0x77, 0x61, 0x79, 0x20, 0x6f, 0x75, + 0x74, 0x00, 0x01, 0x02, 0xcc, 0x00, 0x75, 0x00, + 0xe0, 0x00, 0x83, 0x00, 0xd8, 0x00, 0xa7, 0x00, + 0xd8, 0x00, 0xa7, 0x00, 0x01, 0x01, 0x68, 0x6f, + 0x72, 0x6e, 0x00, 0x49, 0x74, 0x20, 0x6d, 0x75, + 0x73, 0x74, 0x20, 0x6d, 0x61, 0x6b, 0x65, 0x20, + 0x61, 0x20, 0x62, 0x69, 0x67, 0x20, 0x69, 0x6d, + 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x20, 0x6f, 0x6e, 0x00, 0x74, 0x68, 0x65, 0x20, + 0x61, 0x6e, 0x69, 0x6d, 0x61, 0x6c, 0x73, 0x2e, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x47, 0x00, 0x1c, + 0x00, 0x94, 0x00, 0x51, 0x00, 0xa7, 0x00, 0x51, + 0x00, 0xa7, 0x00, 0x04, 0x01, 0x77, 0x69, 0x6e, + 0x64, 0x6f, 0x77, 0x00, 0x49, 0x27, 0x6d, 0x20, + 0x6e, 0x6f, 0x74, 0x20, 0x73, 0x75, 0x72, 0x65, + 0x20, 0x69, 0x66, 0x20, 0x69, 0x74, 0x20, 0x73, + 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x6e, 0x27, 0x74, + 0x20, 0x62, 0x65, 0x20, 0x6f, 0x6e, 0x00, 0x74, + 0x68, 0x65, 0x20, 0x6f, 0x74, 0x68, 0x65, 0x72, + 0x20, 0x73, 0x69, 0x64, 0x65, 0x20, 0x6f, 0x66, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x77, 0x61, 0x6c, + 0x6c, 0x2e, 0x2e, 0x2e, 0x00, 0x00, 0x04, 0x2e, + 0x00, 0x86, 0x00, 0x72, 0x00, 0x98, 0x00, 0x4e, + 0x00, 0xa4, 0x00, 0x4e, 0x00, 0xa4, 0x00, 0x01, + 0x01, 0x63, 0x75, 0x70, 0x62, 0x6f, 0x61, 0x72, + 0x64, 0x00, 0x01, 0x05, 0x81, 0x00, 0x67, 0x00, + 0x9a, 0x00, 0x70, 0x00, 0x8b, 0x00, 0xa5, 0x00, + 0x8b, 0x00, 0xa5, 0x00, 0x01, 0x01, 0x68, 0x65, + 0x61, 0x72, 0x74, 0x2d, 0x73, 0x68, 0x61, 0x70, + 0x65, 0x64, 0x20, 0x68, 0x6f, 0x6c, 0x65, 0x00, + 0x57, 0x68, 0x61, 0x74, 0x20, 0x61, 0x20, 0x6c, + 0x6f, 0x76, 0x65, 0x6c, 0x79, 0x20, 0x68, 0x6f, + 0x6c, 0x65, 0x2e, 0x00, 0x00, 0x06, 0x7d, 0x00, + 0x8c, 0x00, 0x9e, 0x00, 0x9b, 0x00, 0x8f, 0x00, + 0xa4, 0x00, 0x8f, 0x00, 0xa4, 0x00, 0x01, 0x01, + 0x63, 0x75, 0x70, 0x62, 0x6f, 0x61, 0x72, 0x64, + 0x00, 0x01, 0x07, 0x89, 0x00, 0x06, 0x00, 0xb6, + 0x00, 0x29, 0x00, 0xa2, 0x00, 0xbb, 0x00, 0xa2, + 0x00, 0xbb, 0x00, 0x01, 0x01, 0x63, 0x68, 0x61, + 0x6e, 0x64, 0x65, 0x6c, 0x69, 0x65, 0x72, 0x00, + 0x01, 0x08, 0x13, 0x01, 0x5e, 0x00, 0x3f, 0x01, + 0x81, 0x00, 0xd8, 0x00, 0xa7, 0x00, 0xd8, 0x00, + 0xa7, 0x00, 0x02, 0x01, 0x70, 0x69, 0x63, 0x74, + 0x75, 0x72, 0x65, 0x00, 0x49, 0x74, 0x20, 0x6c, + 0x6f, 0x6f, 0x6b, 0x73, 0x20, 0x61, 0x6c, 0x6d, + 0x6f, 0x73, 0x74, 0x20, 0x6c, 0x69, 0x6b, 0x65, + 0x20, 0x61, 0x20, 0x77, 0x69, 0x6e, 0x64, 0x6f, + 0x77, 0x2e, 0x00, 0x00, 0x09, 0x11, 0x01, 0x8c, + 0x00, 0x25, 0x01, 0xb7, 0x00, 0x0e, 0x01, 0xb6, + 0x00, 0x0e, 0x01, 0xb6, 0x00, 0x02, 0x01, 0x66, + 0x69, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, + 0x00, 0x54, 0x68, 0x65, 0x72, 0x65, 0x27, 0x73, + 0x20, 0x61, 0x20, 0x6c, 0x6f, 0x74, 0x20, 0x6f, + 0x66, 0x20, 0x73, 0x6f, 0x6f, 0x74, 0x20, 0x69, + 0x6e, 0x20, 0x74, 0x68, 0x65, 0x72, 0x65, 0x2e, + 0x00, 0x00, 0x0a, 0xb9, 0x00, 0x60, 0x00, 0xe8, + 0x00, 0x83, 0x00, 0xd8, 0x00, 0xa7, 0x00, 0xd8, + 0x00, 0xa7, 0x00, 0x01, 0x01, 0x67, 0x75, 0x6e, + 0x73, 0x00, 0x54, 0x68, 0x65, 0x79, 0x20, 0x61, + 0x72, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, + 0x70, 0x6c, 0x61, 0x73, 0x74, 0x69, 0x63, 0x20, + 0x69, 0x6d, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x2e, 0x00, 0x00, 0x0b, 0x37, 0x00, + 0xa5, 0x00, 0x8c, 0x00, 0xc7, 0x00, 0x51, 0x00, + 0xa7, 0x00, 0x51, 0x00, 0xa7, 0x00, 0x03, 0x01, + 0x74, 0x61, 0x62, 0x6c, 0x65, 0x00, 0x01, 0x0c, + 0x68, 0x00, 0x71, 0x00, 0x6f, 0x00, 0x77, 0x00, + 0x6d, 0x00, 0xa4, 0x00, 0x6d, 0x00, 0xa4, 0x00, + 0x01, 0x01, 0x72, 0x6f, 0x74, 0x74, 0x65, 0x6e, + 0x20, 0x63, 0x68, 0x65, 0x65, 0x73, 0x65, 0x00, + 0x50, 0x66, 0x75, 0x69, 0x21, 0x00, 0x00, 0x0d, + 0xa9, 0x00, 0x2f, 0x00, 0x3f, 0x01, 0x5d, 0x00, + 0xd8, 0x00, 0xa7, 0x00, 0xd8, 0x00, 0xa7, 0x00, + 0x01, 0x01, 0x74, 0x72, 0x6f, 0x70, 0x68, 0x69, + 0x65, 0x73, 0x00, 0x01, 0x0e, 0xa2, 0x00, 0x8b, + 0x00, 0xb6, 0x00, 0xa9, 0x00, 0xc1, 0x00, 0xa4, + 0x00, 0xc1, 0x00, 0xa4, 0x00, 0x04, 0x01, 0x63, + 0x68, 0x61, 0x69, 0x6e, 0x73, 0x61, 0x77, 0x00, + 0x49, 0x20, 0x64, 0x6f, 0x6e, 0x27, 0x74, 0x20, + 0x6b, 0x6e, 0x6f, 0x77, 0x20, 0x77, 0x68, 0x79, + 0x20, 0x62, 0x75, 0x74, 0x20, 0x69, 0x74, 0x20, + 0x72, 0x65, 0x6d, 0x69, 0x6e, 0x64, 0x73, 0x20, + 0x6f, 0x66, 0x00, 0x73, 0x6f, 0x6d, 0x65, 0x20, + 0x54, 0x65, 0x78, 0x61, 0x73, 0x20, 0x67, 0x75, + 0x79, 0x20, 0x49, 0x20, 0x6d, 0x65, 0x74, 0x20, + 0x6f, 0x6e, 0x63, 0x65, 0x2e, 0x00, 0x00, 0x0f, + 0x2d, 0x00, 0x6c, 0x00, 0x72, 0x00, 0x84, 0x00, + 0x51, 0x00, 0xa7, 0x00, 0x51, 0x00, 0xa7, 0x00, + 0x01, 0x01, 0x70, 0x6f, 0x72, 0x63, 0x65, 0x6c, + 0x61, 0x69, 0x6e, 0x00, 0x49, 0x27, 0x6d, 0x20, + 0x61, 0x66, 0x72, 0x61, 0x69, 0x64, 0x20, 0x65, + 0x76, 0x65, 0x6e, 0x20, 0x74, 0x6f, 0x20, 0x62, + 0x72, 0x65, 0x61, 0x74, 0x68, 0x20, 0x61, 0x72, + 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, + 0x73, 0x65, 0x00, 0x74, 0x68, 0x69, 0x6e, 0x67, + 0x73, 0x2e, 0x00, 0x00, 0x3b, 0x90, 0x67, 0x90, + 0xaf, 0x90, 0x01, 0x91, 0x55, 0x91, 0x6e, 0x91, + 0xc0, 0x91, 0xdb, 0x91, 0xf6, 0x91, 0x17, 0x92, + 0x38, 0x92, 0x51, 0x92, 0x73, 0x92, 0xf8, 0x92, + 0x20, 0x93, 0x52, 0x93, 0x00, 0x00, 0x01, 0x1d, + 0x01, 0x87, 0x00, 0x3b, 0x01, 0xaa, 0x00, 0x16, + 0x01, 0xbd, 0x00, 0x16, 0x01, 0xbd, 0x00, 0x02, + 0x01, 0x63, 0x61, 0x72, 0x20, 0x64, 0x6f, 0x6f, + 0x72, 0x00, 0x49, 0x74, 0x27, 0x73, 0x20, 0x75, + 0x6e, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x2e, + 0x00, 0x00, 0x02, 0xbf, 0x00, 0x88, 0x00, 0xe6, + 0x00, 0x9c, 0x00, 0xa8, 0x00, 0xb3, 0x00, 0xa8, + 0x00, 0xb3, 0x00, 0x02, 0x01, 0x74, 0x72, 0x75, + 0x6e, 0x6b, 0x00, 0x54, 0x68, 0x65, 0x72, 0x65, + 0x27, 0x73, 0x20, 0x61, 0x6c, 0x77, 0x61, 0x79, + 0x73, 0x20, 0x73, 0x6f, 0x6d, 0x65, 0x74, 0x68, + 0x69, 0x6e, 0x67, 0x20, 0x75, 0x73, 0x65, 0x66, + 0x75, 0x6c, 0x00, 0x69, 0x6e, 0x20, 0x61, 0x20, + 0x74, 0x72, 0x75, 0x6e, 0x6b, 0x2e, 0x2e, 0x2e, + 0x00, 0x00, 0x03, 0x50, 0x00, 0x47, 0x00, 0x64, + 0x00, 0x51, 0x00, 0x51, 0x00, 0xb5, 0x00, 0x51, + 0x00, 0xb5, 0x00, 0x01, 0x01, 0x62, 0x61, 0x73, + 0x6b, 0x65, 0x74, 0x00, 0x49, 0x74, 0x20, 0x67, + 0x69, 0x76, 0x65, 0x73, 0x20, 0x6d, 0x65, 0x20, + 0x61, 0x6d, 0x62, 0x69, 0x76, 0x61, 0x6c, 0x65, + 0x6e, 0x74, 0x20, 0x66, 0x65, 0x65, 0x6c, 0x69, + 0x6e, 0x67, 0x73, 0x3a, 0x00, 0x72, 0x65, 0x6d, + 0x69, 0x6e, 0x64, 0x73, 0x20, 0x6d, 0x65, 0x20, + 0x6f, 0x66, 0x20, 0x73, 0x63, 0x68, 0x6f, 0x6f, + 0x6c, 0x2e, 0x00, 0x00, 0x04, 0x65, 0x00, 0x3c, + 0x00, 0x69, 0x00, 0xa0, 0x00, 0x65, 0x00, 0xb5, + 0x00, 0x65, 0x00, 0xb5, 0x00, 0x01, 0x01, 0x70, + 0x6f, 0x6c, 0x65, 0x00, 0x41, 0x62, 0x6f, 0x75, + 0x74, 0x20, 0x34, 0x20, 0x6d, 0x65, 0x74, 0x65, + 0x72, 0x20, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x6d, + 0x65, 0x74, 0x61, 0x6c, 0x20, 0x70, 0x6f, 0x6c, + 0x65, 0x00, 0x77, 0x69, 0x74, 0x68, 0x20, 0x61, + 0x20, 0x62, 0x61, 0x73, 0x6b, 0x65, 0x74, 0x20, + 0x73, 0x63, 0x72, 0x65, 0x77, 0x65, 0x64, 0x20, + 0x74, 0x6f, 0x20, 0x69, 0x74, 0x2e, 0x00, 0x00, + 0x05, 0x90, 0x00, 0x5a, 0x00, 0xb7, 0x00, 0x9f, + 0x00, 0xa1, 0x00, 0xa5, 0x00, 0xa1, 0x00, 0xa5, + 0x00, 0x01, 0x01, 0x64, 0x6f, 0x6f, 0x72, 0x00, + 0x01, 0x06, 0xbe, 0x00, 0x71, 0x00, 0xc5, 0x00, + 0x7c, 0x00, 0xa1, 0x00, 0xa5, 0x00, 0xa1, 0x00, + 0xa5, 0x00, 0x02, 0x01, 0x64, 0x6f, 0x6f, 0x72, + 0x2d, 0x62, 0x65, 0x6c, 0x6c, 0x00, 0x4f, 0x6e, + 0x65, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x75, 0x73, + 0x65, 0x20, 0x69, 0x74, 0x20, 0x74, 0x6f, 0x20, + 0x73, 0x63, 0x61, 0x72, 0x65, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x70, 0x65, 0x6f, 0x70, 0x6c, 0x65, + 0x00, 0x62, 0x65, 0x68, 0x69, 0x6e, 0x64, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x64, 0x6f, 0x6f, 0x72, + 0x2e, 0x00, 0x00, 0x07, 0xd0, 0x00, 0x4b, 0x00, + 0xfd, 0x00, 0x7e, 0x00, 0xa9, 0x00, 0xae, 0x00, + 0xa9, 0x00, 0xae, 0x00, 0x02, 0x01, 0x77, 0x69, + 0x6e, 0x64, 0x6f, 0x77, 0x00, 0x01, 0x08, 0x10, + 0x01, 0x4b, 0x00, 0x3d, 0x01, 0x7d, 0x00, 0xa9, + 0x00, 0xae, 0x00, 0xa9, 0x00, 0xae, 0x00, 0x02, + 0x01, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x00, + 0x01, 0x09, 0x4b, 0x00, 0x02, 0x00, 0x58, 0x00, + 0x1b, 0x00, 0x51, 0x00, 0xb5, 0x00, 0x51, 0x00, + 0xb5, 0x00, 0x01, 0x01, 0x61, 0x74, 0x74, 0x69, + 0x63, 0x20, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, + 0x00, 0x01, 0x0a, 0x00, 0x00, 0xaa, 0x00, 0x21, + 0x00, 0xc7, 0x00, 0x30, 0x00, 0xbb, 0x00, 0x00, + 0x00, 0xbb, 0x00, 0x04, 0x01, 0x70, 0x61, 0x74, + 0x68, 0x20, 0x74, 0x6f, 0x20, 0x6c, 0x61, 0x6b, + 0x65, 0x00, 0x01, 0x0b, 0x17, 0x01, 0xaa, 0x00, + 0x3f, 0x01, 0xc7, 0x00, 0x21, 0x01, 0xbe, 0x00, + 0x3f, 0x01, 0xbe, 0x00, 0x02, 0x01, 0x70, 0x61, + 0x74, 0x68, 0x00, 0x01, 0x0c, 0x00, 0x00, 0x74, + 0x00, 0x1c, 0x00, 0xa9, 0x00, 0x30, 0x00, 0xbb, + 0x00, 0x05, 0x00, 0x83, 0x00, 0x01, 0x01, 0x70, + 0x61, 0x74, 0x68, 0x20, 0x74, 0x6f, 0x20, 0x66, + 0x69, 0x65, 0x6c, 0x64, 0x00, 0x01, 0x0d, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, + 0x00, 0xbb, 0x00, 0x40, 0x00, 0xbb, 0x00, 0x01, + 0x01, 0x62, 0x6f, 0x79, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x48, 0x65, 0x27, 0x73, 0x20, + 0x74, 0x72, 0x79, 0x69, 0x6e, 0x67, 0x20, 0x68, + 0x61, 0x72, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x73, + 0x63, 0x6f, 0x72, 0x65, 0x2c, 0x00, 0x62, 0x75, + 0x74, 0x20, 0x68, 0x65, 0x27, 0x73, 0x20, 0x74, + 0x6f, 0x6f, 0x20, 0x77, 0x65, 0x61, 0x6b, 0x20, + 0x74, 0x6f, 0x20, 0x74, 0x68, 0x72, 0x6f, 0x77, + 0x00, 0x74, 0x68, 0x65, 0x20, 0x62, 0x61, 0x6c, + 0x6c, 0x20, 0x68, 0x69, 0x67, 0x68, 0x20, 0x65, + 0x6e, 0x6f, 0x75, 0x67, 0x68, 0x21, 0x00, 0x00, + 0x53, 0x6f, 0x6e, 0x6e, 0x79, 0x20, 0x6f, 0x72, + 0x20, 0x77, 0x68, 0x61, 0x74, 0x65, 0x76, 0x65, + 0x72, 0x00, 0xff, 0x0e, 0x1d, 0x01, 0x91, 0x00, + 0x23, 0x01, 0x9b, 0x00, 0x16, 0x01, 0xbd, 0x00, + 0x16, 0x01, 0xbd, 0x00, 0x02, 0x00, 0x63, 0x6f, + 0x6d, 0x62, 0x00, 0x53, 0x74, 0x72, 0x61, 0x6e, + 0x67, 0x65, 0x20, 0x74, 0x68, 0x69, 0x6e, 0x67, + 0x2e, 0x00, 0x00, 0x0f, 0x1e, 0x01, 0xa5, 0x00, + 0x24, 0x01, 0xa9, 0x00, 0x16, 0x01, 0xbd, 0x00, + 0x16, 0x01, 0xbd, 0x00, 0x02, 0x00, 0x6c, 0x65, + 0x76, 0x65, 0x72, 0x00, 0x49, 0x20, 0x77, 0x6f, + 0x6e, 0x64, 0x65, 0x72, 0x20, 0x77, 0x68, 0x61, + 0x74, 0x00, 0x69, 0x74, 0x27, 0x73, 0x20, 0x66, + 0x6f, 0x72, 0x3f, 0x00, 0x00, 0x10, 0x26, 0x01, + 0x85, 0x00, 0x3b, 0x01, 0xac, 0x00, 0x16, 0x01, + 0xbd, 0x00, 0x16, 0x01, 0xbd, 0x00, 0x02, 0x00, + 0x63, 0x61, 0x72, 0x20, 0x64, 0x6f, 0x6f, 0x72, + 0x00, 0x01, 0x89, 0x93, 0xa2, 0x93, 0xc3, 0x93, + 0xdc, 0x93, 0x27, 0x94, 0x42, 0x94, 0x5d, 0x94, + 0x78, 0x94, 0xa6, 0x94, 0xda, 0x94, 0xfa, 0x94, + 0x13, 0x95, 0x00, 0x00, 0x01, 0x00, 0x00, 0xac, + 0x00, 0x2b, 0x00, 0xc3, 0x00, 0x30, 0x00, 0xbe, + 0x00, 0x00, 0x00, 0xbb, 0x00, 0x04, 0x01, 0x70, + 0x61, 0x74, 0x68, 0x00, 0x01, 0x02, 0xb0, 0x00, + 0xb6, 0x00, 0xdd, 0x00, 0xc7, 0x00, 0xc4, 0x00, + 0xc2, 0x00, 0xce, 0x00, 0xc7, 0x00, 0x03, 0x01, + 0x70, 0x61, 0x74, 0x68, 0x20, 0x74, 0x6f, 0x20, + 0x63, 0x61, 0x76, 0x65, 0x00, 0x01, 0x03, 0x50, + 0x00, 0x68, 0x00, 0x79, 0x00, 0xa9, 0x00, 0x64, + 0x00, 0xb0, 0x00, 0x64, 0x00, 0xb0, 0x00, 0x01, + 0x01, 0x64, 0x6f, 0x6f, 0x72, 0x00, 0x01, 0x04, + 0x00, 0x00, 0x82, 0x00, 0x3f, 0x00, 0xab, 0x00, + 0x30, 0x00, 0xbe, 0x00, 0x30, 0x00, 0xbe, 0x00, + 0x04, 0x01, 0x6c, 0x61, 0x75, 0x6e, 0x64, 0x72, + 0x79, 0x00, 0x54, 0x68, 0x65, 0x20, 0x61, 0x69, + 0x72, 0x20, 0x69, 0x73, 0x20, 0x74, 0x6f, 0x6f, + 0x20, 0x64, 0x61, 0x6d, 0x70, 0x20, 0x74, 0x6f, + 0x20, 0x64, 0x72, 0x79, 0x00, 0x74, 0x68, 0x69, + 0x73, 0x20, 0x77, 0x65, 0x74, 0x20, 0x6c, 0x61, + 0x75, 0x6e, 0x64, 0x72, 0x79, 0x2e, 0x2e, 0x2e, + 0x00, 0x00, 0x05, 0x8d, 0x00, 0x58, 0x00, 0xba, + 0x00, 0x90, 0x00, 0x9f, 0x00, 0xbd, 0x00, 0x9f, + 0x00, 0xbd, 0x00, 0x01, 0x01, 0x77, 0x69, 0x6e, + 0x64, 0x6f, 0x77, 0x00, 0x01, 0x06, 0xce, 0x00, + 0x58, 0x00, 0xfb, 0x00, 0x90, 0x00, 0x9f, 0x00, + 0xbd, 0x00, 0x9f, 0x00, 0xbd, 0x00, 0x02, 0x01, + 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x00, 0x01, + 0x07, 0x15, 0x01, 0x55, 0x00, 0x36, 0x01, 0x82, + 0x00, 0xc4, 0x00, 0xc2, 0x00, 0xc4, 0x00, 0xc2, + 0x00, 0x02, 0x01, 0x77, 0x69, 0x6e, 0x64, 0x6f, + 0x77, 0x00, 0x01, 0x08, 0x1a, 0x01, 0x9c, 0x00, + 0x38, 0x01, 0xab, 0x00, 0x29, 0x01, 0xb5, 0x00, + 0x29, 0x01, 0xb2, 0x00, 0x01, 0x00, 0x68, 0x6f, + 0x6c, 0x65, 0x00, 0x41, 0x20, 0x77, 0x61, 0x79, + 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x63, 0x65, 0x6c, 0x6c, 0x61, 0x72, 0x2e, 0x00, + 0x00, 0x09, 0x0b, 0x01, 0x83, 0x00, 0x3f, 0x01, + 0xb0, 0x00, 0xc4, 0x00, 0xc2, 0x00, 0xc4, 0x00, + 0xc2, 0x00, 0x02, 0x01, 0x76, 0x61, 0x6c, 0x76, + 0x65, 0x00, 0x49, 0x74, 0x27, 0x73, 0x20, 0x61, + 0x20, 0x68, 0x65, 0x61, 0x76, 0x79, 0x20, 0x6d, + 0x65, 0x74, 0x61, 0x6c, 0x20, 0x74, 0x68, 0x69, + 0x6e, 0x67, 0x2e, 0x00, 0x00, 0x0a, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc4, 0x00, + 0xc2, 0x00, 0xc4, 0x00, 0xc2, 0x00, 0x02, 0x01, + 0x64, 0x6f, 0x67, 0x00, 0x53, 0x6e, 0x6f, 0x6f, + 0x70, 0x79, 0x2e, 0x00, 0x00, 0x0b, 0x7c, 0x00, + 0x84, 0x00, 0x81, 0x00, 0x8e, 0x00, 0x64, 0x00, + 0xb0, 0x00, 0x64, 0x00, 0xb0, 0x00, 0x02, 0x01, + 0x62, 0x65, 0x6c, 0x6c, 0x00, 0x01, 0x0c, 0x00, + 0x00, 0x82, 0x00, 0x3f, 0x00, 0xa1, 0x00, 0x3f, + 0x00, 0xbb, 0x00, 0x3f, 0x00, 0xbb, 0x00, 0x01, + 0x01, 0x72, 0x6f, 0x70, 0x65, 0x00, 0x01, 0x48, + 0x95, 0x86, 0x95, 0xa3, 0x95, 0xd2, 0x95, 0xfe, + 0x95, 0x2e, 0x96, 0x48, 0x96, 0x88, 0x96, 0xae, + 0x96, 0x0f, 0x97, 0x36, 0x97, 0x4f, 0x97, 0x6e, + 0x97, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xe6, 0x00, 0xac, 0x00, + 0xe6, 0x00, 0xac, 0x00, 0x03, 0x01, 0x6f, 0x6c, + 0x64, 0x20, 0x6d, 0x61, 0x6e, 0x00, 0x48, 0x65, + 0x20, 0x6c, 0x6f, 0x6f, 0x6b, 0x73, 0x20, 0x6c, + 0x69, 0x6b, 0x65, 0x20, 0x61, 0x20, 0x72, 0x65, + 0x74, 0x69, 0x72, 0x65, 0x64, 0x00, 0x73, 0x65, + 0x61, 0x20, 0x77, 0x6f, 0x6c, 0x66, 0x2e, 0x00, + 0x00, 0x02, 0x60, 0x00, 0x5e, 0x00, 0x7a, 0x00, + 0x7f, 0x00, 0x6d, 0x00, 0xa9, 0x00, 0x6d, 0x00, + 0xa9, 0x00, 0x01, 0x01, 0x63, 0x75, 0x70, 0x62, + 0x6f, 0x61, 0x72, 0x64, 0x00, 0x01, 0x03, 0x63, + 0x00, 0x83, 0x00, 0xb2, 0x00, 0x97, 0x00, 0xa3, + 0x00, 0xab, 0x00, 0xa3, 0x00, 0xab, 0x00, 0x01, + 0x01, 0x64, 0x72, 0x61, 0x77, 0x65, 0x72, 0x73, + 0x00, 0x42, 0x6f, 0x79, 0x21, 0x20, 0x4c, 0x6f, + 0x74, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, + 0x65, 0x6d, 0x21, 0x00, 0x00, 0x04, 0x00, 0x00, + 0xb7, 0x00, 0x8e, 0x00, 0xc7, 0x00, 0x63, 0x00, + 0xb4, 0x00, 0x63, 0x00, 0xb4, 0x00, 0x03, 0x01, + 0x62, 0x65, 0x64, 0x00, 0x48, 0x6f, 0x6d, 0x65, + 0x2c, 0x20, 0x73, 0x77, 0x65, 0x65, 0x74, 0x20, + 0x68, 0x6f, 0x6d, 0x65, 0x2e, 0x2e, 0x2e, 0x00, + 0x00, 0x05, 0x13, 0x01, 0x51, 0x00, 0x28, 0x01, + 0x92, 0x00, 0xf4, 0x00, 0xac, 0x00, 0xf4, 0x00, + 0xac, 0x00, 0x02, 0x01, 0x77, 0x69, 0x6e, 0x64, + 0x6f, 0x77, 0x00, 0x41, 0x20, 0x76, 0x69, 0x65, + 0x77, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x2e, 0x00, + 0x00, 0x06, 0xc8, 0x00, 0x87, 0x00, 0xf4, 0x00, + 0x9b, 0x00, 0xd2, 0x00, 0xac, 0x00, 0xd2, 0x00, + 0xac, 0x00, 0x01, 0x01, 0x74, 0x61, 0x62, 0x6c, + 0x65, 0x00, 0x01, 0x07, 0xc0, 0x00, 0x68, 0x00, + 0xec, 0x00, 0x7f, 0x00, 0xd2, 0x00, 0xac, 0x00, + 0xd2, 0x00, 0xac, 0x00, 0x01, 0x01, 0x73, 0x68, + 0x6f, 0x74, 0x67, 0x75, 0x6e, 0x00, 0x47, 0x65, + 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x6d, 0x75, + 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x66, 0x6f, + 0x72, 0x00, 0x64, 0x69, 0x6e, 0x6f, 0x73, 0x61, + 0x75, 0x72, 0x75, 0x73, 0x65, 0x73, 0x21, 0x2e, + 0x2e, 0x00, 0x00, 0x08, 0xbd, 0x00, 0x38, 0x00, + 0xe6, 0x00, 0x65, 0x00, 0xd2, 0x00, 0xac, 0x00, + 0xd2, 0x00, 0xac, 0x00, 0x01, 0x01, 0x70, 0x69, + 0x63, 0x74, 0x75, 0x72, 0x65, 0x00, 0x43, 0x68, + 0x61, 0x72, 0x6d, 0x69, 0x6e, 0x67, 0x2e, 0x00, + 0x00, 0x09, 0x87, 0x00, 0x47, 0x00, 0xb8, 0x00, + 0x6a, 0x00, 0x96, 0x00, 0xac, 0x00, 0x96, 0x00, + 0xac, 0x00, 0x01, 0x01, 0x66, 0x61, 0x6d, 0x69, + 0x6c, 0x79, 0x20, 0x70, 0x69, 0x63, 0x74, 0x75, + 0x72, 0x65, 0x73, 0x00, 0x49, 0x20, 0x77, 0x6f, + 0x75, 0x6c, 0x64, 0x6e, 0x27, 0x74, 0x20, 0x6c, + 0x69, 0x6b, 0x65, 0x20, 0x68, 0x61, 0x76, 0x69, + 0x6e, 0x67, 0x20, 0x6d, 0x79, 0x20, 0x61, 0x6e, + 0x63, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x73, 0x20, + 0x68, 0x61, 0x6e, 0x67, 0x69, 0x6e, 0x67, 0x00, + 0x6c, 0x69, 0x6b, 0x65, 0x20, 0x74, 0x72, 0x6f, + 0x70, 0x68, 0x69, 0x65, 0x73, 0x2e, 0x2e, 0x2e, + 0x00, 0x00, 0x0a, 0x37, 0x00, 0x56, 0x00, 0x57, + 0x00, 0x89, 0x00, 0x46, 0x00, 0xac, 0x00, 0x46, + 0x00, 0xac, 0x00, 0x01, 0x01, 0x66, 0x6c, 0x6f, + 0x77, 0x65, 0x72, 0x00, 0x49, 0x74, 0x27, 0x73, + 0x20, 0x67, 0x72, 0x65, 0x65, 0x6e, 0x2e, 0x00, + 0x00, 0x0b, 0x0d, 0x00, 0x60, 0x00, 0x2c, 0x00, + 0xac, 0x00, 0x3d, 0x00, 0xaf, 0x00, 0x3d, 0x00, + 0xaf, 0x00, 0x04, 0x01, 0x64, 0x6f, 0x6f, 0x72, + 0x00, 0x01, 0x0c, 0x94, 0x00, 0x04, 0x00, 0xb6, + 0x00, 0x37, 0x00, 0xa3, 0x00, 0xab, 0x00, 0xa3, + 0x00, 0xab, 0x00, 0x01, 0x01, 0x63, 0x68, 0x61, + 0x6e, 0x64, 0x65, 0x6c, 0x69, 0x65, 0x72, 0x00, + 0x01, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xb7, 0x00, 0xaa, 0x00, 0xb7, 0x00, + 0xaa, 0x00, 0x01, 0x01, 0x66, 0x61, 0x6e, 0x00, + 0x49, 0x74, 0x27, 0x73, 0x20, 0x62, 0x61, 0x74, + 0x74, 0x65, 0x72, 0x79, 0x20, 0x70, 0x6f, 0x77, + 0x65, 0x72, 0x65, 0x64, 0x2e, 0x00, 0x00, 0xbe, + 0x97, 0xfb, 0x97, 0x26, 0x98, 0x42, 0x98, 0x5d, + 0x98, 0x78, 0x98, 0x97, 0x98, 0xcb, 0x98, 0xe1, + 0x98, 0x23, 0x99, 0x51, 0x99, 0x97, 0x99, 0xdb, + 0x99, 0x3f, 0x9a, 0x7d, 0x9a, 0xe2, 0x9a, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x5d, 0x00, 0xba, 0x00, 0x5d, 0x00, + 0xba, 0x00, 0x04, 0x01, 0x6f, 0x6c, 0x64, 0x20, + 0x6c, 0x61, 0x64, 0x79, 0x00, 0x53, 0x68, 0x65, + 0x20, 0x6b, 0x6e, 0x69, 0x74, 0x73, 0x20, 0x69, + 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x20, 0x74, + 0x65, 0x61, 0x6d, 0x2e, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x01, 0x67, 0x69, 0x72, 0x6c, 0x00, 0x53, 0x68, + 0x65, 0x27, 0x73, 0x20, 0x63, 0x6f, 0x6f, 0x6c, + 0x2e, 0x00, 0x00, 0x41, 0x6e, 0x6e, 0x65, 0x00, + 0xff, 0x03, 0x00, 0x00, 0xbc, 0x00, 0x6b, 0x00, + 0xc7, 0x00, 0x5e, 0x00, 0xbe, 0x00, 0x4c, 0x00, + 0xc7, 0x00, 0x03, 0x01, 0x77, 0x61, 0x79, 0x20, + 0x6f, 0x75, 0x74, 0x00, 0x01, 0x04, 0x02, 0x00, + 0x48, 0x00, 0x30, 0x00, 0x84, 0x00, 0x5d, 0x00, + 0xba, 0x00, 0x5d, 0x00, 0xba, 0x00, 0x01, 0x01, + 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x00, 0x01, + 0x05, 0x84, 0x00, 0x48, 0x00, 0xb0, 0x00, 0x84, + 0x00, 0x9b, 0x00, 0xac, 0x00, 0x9b, 0x00, 0xac, + 0x00, 0x01, 0x01, 0x77, 0x69, 0x6e, 0x64, 0x6f, + 0x77, 0x00, 0x01, 0x06, 0x4b, 0x00, 0x0a, 0x00, + 0x78, 0x00, 0x38, 0x00, 0x5d, 0x00, 0xba, 0x00, + 0x5d, 0x00, 0xba, 0x00, 0x01, 0x01, 0x63, 0x68, + 0x61, 0x6e, 0x64, 0x65, 0x6c, 0x69, 0x65, 0x72, + 0x00, 0x01, 0x07, 0x4d, 0x00, 0x61, 0x00, 0x6f, + 0x00, 0xa5, 0x00, 0x5d, 0x00, 0xab, 0x00, 0x5d, + 0x00, 0xab, 0x00, 0x01, 0x01, 0x63, 0x6c, 0x6f, + 0x63, 0x6b, 0x00, 0x52, 0x65, 0x61, 0x6c, 0x6c, + 0x79, 0x20, 0x67, 0x72, 0x65, 0x61, 0x74, 0x20, + 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x70, 0x69, + 0x65, 0x63, 0x65, 0x2e, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x09, 0x86, 0x00, 0x9f, + 0x00, 0x97, 0x00, 0xad, 0x00, 0x9e, 0x00, 0xc6, + 0x00, 0x9e, 0x00, 0xc6, 0x00, 0x03, 0x01, 0x69, + 0x6d, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x20, 0x66, 0x72, 0x75, 0x69, 0x74, 0x73, 0x00, + 0x54, 0x68, 0x65, 0x20, 0x61, 0x70, 0x70, 0x6c, + 0x65, 0x20, 0x6c, 0x6f, 0x6f, 0x6b, 0x73, 0x20, + 0x61, 0x6c, 0x6d, 0x6f, 0x73, 0x74, 0x00, 0x72, + 0x65, 0x61, 0x6c, 0x21, 0x00, 0x00, 0x0a, 0xac, + 0x00, 0x9f, 0x00, 0xb6, 0x00, 0xae, 0x00, 0x9e, + 0x00, 0xc6, 0x00, 0x9e, 0x00, 0xc6, 0x00, 0x03, + 0x01, 0x6a, 0x75, 0x67, 0x00, 0x49, 0x74, 0x20, + 0x68, 0x6f, 0x6c, 0x64, 0x73, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x66, 0x6c, 0x6f, 0x77, 0x65, 0x72, + 0x73, 0x2e, 0x00, 0x00, 0x0b, 0xa1, 0x00, 0x89, + 0x00, 0xc2, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0xc6, + 0x00, 0x9e, 0x00, 0xc6, 0x00, 0x03, 0x01, 0x66, + 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x73, 0x00, 0x49, + 0x20, 0x68, 0x6f, 0x70, 0x65, 0x20, 0x74, 0x68, + 0x65, 0x73, 0x65, 0x20, 0x61, 0x72, 0x65, 0x20, + 0x6e, 0x6f, 0x74, 0x20, 0x66, 0x72, 0x6f, 0x6d, + 0x20, 0x73, 0x6f, 0x6d, 0x65, 0x00, 0x61, 0x64, + 0x6d, 0x69, 0x72, 0x65, 0x72, 0x2e, 0x2e, 0x2e, + 0x00, 0x00, 0x0c, 0x3c, 0x00, 0xa1, 0x00, 0x4c, + 0x00, 0xa8, 0x00, 0x5d, 0x00, 0xba, 0x00, 0x4e, + 0x00, 0xbc, 0x00, 0x04, 0x01, 0x66, 0x65, 0x61, + 0x74, 0x68, 0x65, 0x72, 0x20, 0x64, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x00, 0x49, 0x74, 0x20, 0x6d, + 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x66, + 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, + 0x75, 0x73, 0x74, 0x20, 0x70, 0x72, 0x6f, 0x62, + 0x6c, 0x65, 0x6d, 0x2e, 0x00, 0x00, 0x0d, 0x7d, + 0x00, 0xa7, 0x00, 0xc2, 0x00, 0xc7, 0x00, 0x9e, + 0x00, 0xc6, 0x00, 0x9e, 0x00, 0xc6, 0x00, 0x03, + 0x01, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x00, 0x49, + 0x74, 0x27, 0x73, 0x20, 0x72, 0x6f, 0x75, 0x6e, + 0x64, 0x2c, 0x20, 0x62, 0x75, 0x74, 0x20, 0x49, + 0x20, 0x64, 0x6f, 0x75, 0x62, 0x74, 0x20, 0x69, + 0x74, 0x20, 0x68, 0x61, 0x73, 0x20, 0x61, 0x6e, + 0x79, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x20, 0x74, + 0x6f, 0x20, 0x64, 0x6f, 0x00, 0x77, 0x69, 0x74, + 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x65, + 0x64, 0x69, 0x65, 0x76, 0x61, 0x6c, 0x20, 0x6c, + 0x65, 0x67, 0x65, 0x6e, 0x64, 0x2e, 0x2e, 0x2e, + 0x00, 0x00, 0x0e, 0xf1, 0x00, 0x60, 0x00, 0x1a, + 0x01, 0x94, 0x00, 0x02, 0x01, 0xb8, 0x00, 0x02, + 0x01, 0xb8, 0x00, 0x01, 0x01, 0x6d, 0x69, 0x72, + 0x72, 0x6f, 0x72, 0x00, 0x49, 0x20, 0x63, 0x61, + 0x6e, 0x20, 0x73, 0x65, 0x65, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x67, 0x69, 0x72, 0x6c, 0x27, 0x73, + 0x00, 0x66, 0x61, 0x63, 0x65, 0x20, 0x69, 0x6e, + 0x20, 0x69, 0x74, 0x2e, 0x2e, 0x2e, 0x00, 0x00, + 0x0f, 0x1d, 0x01, 0x3e, 0x00, 0x3f, 0x01, 0x68, + 0x00, 0x02, 0x01, 0xb8, 0x00, 0x02, 0x01, 0xb8, + 0x00, 0x02, 0x01, 0x70, 0x69, 0x63, 0x74, 0x75, + 0x72, 0x65, 0x00, 0x49, 0x74, 0x27, 0x73, 0x20, + 0x6e, 0x69, 0x63, 0x65, 0x20, 0x74, 0x6f, 0x20, + 0x6c, 0x69, 0x76, 0x65, 0x20, 0x69, 0x6e, 0x20, + 0x61, 0x20, 0x76, 0x69, 0x6c, 0x6c, 0x61, 0x67, + 0x65, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x68, 0x61, + 0x76, 0x65, 0x20, 0x76, 0x69, 0x6c, 0x6c, 0x61, + 0x67, 0x65, 0x00, 0x6c, 0x61, 0x6e, 0x64, 0x73, + 0x63, 0x61, 0x70, 0x65, 0x73, 0x20, 0x6f, 0x6e, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x77, 0x61, 0x6c, + 0x6c, 0x73, 0x2e, 0x00, 0x00, 0x10, 0x18, 0x01, + 0xa1, 0x00, 0x3f, 0x01, 0xc7, 0x00, 0xf5, 0x00, + 0xc6, 0x00, 0xf5, 0x00, 0xc6, 0x00, 0x02, 0x01, + 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x00, 0x49, 0x6d, + 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x76, 0x65, + 0x2e, 0x00, 0x00, 0x1c, 0x9b, 0x3f, 0x9b, 0x77, + 0x9b, 0x9b, 0x9b, 0xd1, 0x9b, 0x2d, 0x9c, 0x47, + 0x9c, 0xa4, 0x9c, 0xf4, 0x9c, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5e, 0x00, 0xaa, 0x00, 0x5e, 0x00, 0xaa, 0x00, + 0x04, 0x01, 0x73, 0x70, 0x69, 0x64, 0x65, 0x72, + 0x00, 0x49, 0x74, 0x27, 0x73, 0x2e, 0x2e, 0x2e, + 0x00, 0x00, 0x02, 0x38, 0x00, 0x86, 0x00, 0x4e, + 0x00, 0xa9, 0x00, 0x5e, 0x00, 0xaa, 0x00, 0x4e, + 0x00, 0xaa, 0x00, 0x04, 0x01, 0x73, 0x68, 0x6f, + 0x76, 0x65, 0x6c, 0x00, 0x49, 0x20, 0x63, 0x6f, + 0x75, 0x6c, 0x64, 0x20, 0x70, 0x6c, 0x61, 0x79, + 0x20, 0x64, 0x69, 0x67, 0x67, 0x65, 0x72, 0x20, + 0x77, 0x69, 0x74, 0x68, 0x20, 0x69, 0x74, 0x2e, + 0x00, 0x00, 0x03, 0xd5, 0x00, 0x15, 0x00, 0xf8, + 0x00, 0xa5, 0x00, 0xe6, 0x00, 0xaa, 0x00, 0xe6, + 0x00, 0xaa, 0x00, 0x01, 0x01, 0x6c, 0x61, 0x64, + 0x64, 0x65, 0x72, 0x00, 0x57, 0x61, 0x79, 0x20, + 0x6f, 0x75, 0x74, 0x2e, 0x00, 0x00, 0x04, 0xf7, + 0x00, 0x70, 0x00, 0xfe, 0x00, 0x7a, 0x00, 0x05, + 0x01, 0xa8, 0x00, 0x05, 0x01, 0xa8, 0x00, 0x01, + 0x00, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x00, + 0x49, 0x74, 0x27, 0x73, 0x20, 0x6c, 0x69, 0x74, + 0x74, 0x6c, 0x65, 0x2c, 0x20, 0x62, 0x75, 0x74, + 0x20, 0x70, 0x6f, 0x77, 0x65, 0x72, 0x66, 0x75, + 0x6c, 0x2e, 0x00, 0x00, 0x05, 0x05, 0x00, 0x5a, + 0x00, 0x1e, 0x00, 0x77, 0x00, 0x53, 0x00, 0xb4, + 0x00, 0x53, 0x00, 0xb4, 0x00, 0x04, 0x01, 0x61, + 0x78, 0x65, 0x00, 0x49, 0x20, 0x68, 0x6f, 0x70, + 0x65, 0x20, 0x74, 0x68, 0x65, 0x73, 0x65, 0x20, + 0x73, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x20, 0x6f, + 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x77, 0x61, + 0x6c, 0x6c, 0x20, 0x68, 0x61, 0x76, 0x65, 0x00, + 0x6e, 0x6f, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x20, + 0x74, 0x6f, 0x20, 0x64, 0x6f, 0x20, 0x77, 0x69, + 0x74, 0x68, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, + 0x61, 0x78, 0x65, 0x2e, 0x2e, 0x2e, 0x00, 0x00, + 0x06, 0x1c, 0x01, 0x38, 0x00, 0x28, 0x01, 0x62, + 0x00, 0xf5, 0x00, 0xb2, 0x00, 0xf5, 0x00, 0xb2, + 0x00, 0x02, 0x01, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x00, 0x01, 0x07, 0x2e, 0x01, 0x50, 0x00, 0x36, + 0x01, 0x62, 0x00, 0xf5, 0x00, 0xb2, 0x00, 0xf5, + 0x00, 0xb2, 0x00, 0x02, 0x01, 0x74, 0x6f, 0x6e, + 0x67, 0x73, 0x00, 0x47, 0x65, 0x65, 0x2c, 0x20, + 0x74, 0x68, 0x69, 0x73, 0x20, 0x70, 0x6c, 0x61, + 0x63, 0x65, 0x20, 0x6c, 0x6f, 0x6f, 0x6b, 0x73, + 0x20, 0x6d, 0x6f, 0x72, 0x65, 0x20, 0x6c, 0x69, + 0x6b, 0x65, 0x20, 0x61, 0x00, 0x74, 0x6f, 0x72, + 0x74, 0x75, 0x72, 0x65, 0x20, 0x63, 0x68, 0x61, + 0x6d, 0x62, 0x65, 0x72, 0x20, 0x74, 0x68, 0x61, + 0x6e, 0x20, 0x61, 0x20, 0x63, 0x65, 0x6c, 0x6c, + 0x61, 0x72, 0x2e, 0x2e, 0x2e, 0x00, 0x00, 0x08, + 0x6b, 0x00, 0x59, 0x00, 0xcc, 0x00, 0xa9, 0x00, + 0x99, 0x00, 0xb0, 0x00, 0x99, 0x00, 0xb0, 0x00, + 0x01, 0x01, 0x73, 0x68, 0x65, 0x6c, 0x76, 0x65, + 0x73, 0x00, 0x4c, 0x6f, 0x74, 0x73, 0x20, 0x6f, + 0x66, 0x20, 0x6a, 0x61, 0x72, 0x73, 0x20, 0x61, + 0x6e, 0x64, 0x20, 0x73, 0x74, 0x75, 0x66, 0x66, + 0x2e, 0x20, 0x4e, 0x6f, 0x74, 0x68, 0x69, 0x6e, + 0x67, 0x00, 0x72, 0x65, 0x61, 0x6c, 0x6c, 0x79, + 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x65, 0x73, + 0x74, 0x69, 0x6e, 0x67, 0x2e, 0x00, 0x00, 0x09, + 0x17, 0x01, 0x8c, 0x00, 0x3f, 0x01, 0xc2, 0x00, + 0xf5, 0x00, 0xb2, 0x00, 0xf5, 0x00, 0xb2, 0x00, + 0x02, 0x01, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, + 0x6e, 0x65, 0x72, 0x73, 0x00, 0x54, 0x68, 0x65, + 0x79, 0x27, 0x72, 0x65, 0x20, 0x65, 0x6d, 0x70, + 0x74, 0x79, 0x2e, 0x00, 0x00, 0x32, 0x9d, 0x90, + 0x9d, 0x07, 0x9e, 0x29, 0x9e, 0x42, 0x9e, 0x9e, + 0x9e, 0xcf, 0x9e, 0x00, 0x00, 0x01, 0xe1, 0x00, + 0x8b, 0x00, 0xf6, 0x00, 0x91, 0x00, 0x0c, 0x01, + 0x91, 0x00, 0x0c, 0x01, 0x91, 0x00, 0x04, 0x00, + 0x62, 0x6f, 0x6e, 0x65, 0x00, 0x44, 0x75, 0x65, + 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x61, 0x67, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x72, 0x6f, 0x63, 0x6b, 0x20, + 0x49, 0x20, 0x74, 0x68, 0x69, 0x6e, 0x6b, 0x00, + 0x69, 0x74, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, + 0x62, 0x65, 0x20, 0x73, 0x6f, 0x6d, 0x65, 0x20, + 0x64, 0x69, 0x6e, 0x6f, 0x73, 0x61, 0x75, 0x72, + 0x20, 0x62, 0x6f, 0x6e, 0x65, 0x2e, 0x2e, 0x2e, + 0x2e, 0x00, 0x00, 0x02, 0x90, 0x00, 0x49, 0x00, + 0xcb, 0x00, 0x9e, 0x00, 0xea, 0x00, 0x98, 0x00, + 0xea, 0x00, 0x98, 0x00, 0x04, 0x01, 0x62, 0x75, + 0x73, 0x68, 0x00, 0x49, 0x74, 0x20, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x73, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x77, 0x61, 0x79, 0x20, 0x74, 0x6f, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x63, 0x61, 0x76, 0x65, + 0x2e, 0x00, 0x4d, 0x61, 0x79, 0x62, 0x65, 0x20, + 0x73, 0x6f, 0x6d, 0x65, 0x6f, 0x6e, 0x65, 0x20, + 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x65, 0x64, 0x20, + 0x69, 0x74, 0x20, 0x68, 0x65, 0x72, 0x65, 0x20, + 0x74, 0x6f, 0x00, 0x6b, 0x65, 0x65, 0x70, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x63, 0x61, 0x76, 0x65, + 0x20, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, + 0x20, 0x73, 0x61, 0x66, 0x65, 0x2e, 0x2e, 0x2e, + 0x00, 0x00, 0x03, 0x79, 0x00, 0x34, 0x00, 0xae, + 0x00, 0x9e, 0x00, 0xea, 0x00, 0x98, 0x00, 0xea, + 0x00, 0x98, 0x00, 0x04, 0x01, 0x63, 0x61, 0x76, + 0x65, 0x20, 0x65, 0x6e, 0x74, 0x72, 0x61, 0x6e, + 0x63, 0x65, 0x00, 0x01, 0x04, 0x15, 0x01, 0x7a, + 0x00, 0x3f, 0x01, 0xa9, 0x00, 0x14, 0x01, 0x93, + 0x00, 0x3f, 0x01, 0x93, 0x00, 0x02, 0x01, 0x70, + 0x61, 0x74, 0x68, 0x00, 0x01, 0x05, 0xdd, 0x00, + 0x7a, 0x00, 0xfe, 0x00, 0x94, 0x00, 0xed, 0x00, + 0x98, 0x00, 0xed, 0x00, 0x98, 0x00, 0x01, 0x01, + 0x72, 0x6f, 0x63, 0x6b, 0x00, 0x49, 0x74, 0x20, + 0x6d, 0x75, 0x73, 0x74, 0x20, 0x68, 0x61, 0x76, + 0x65, 0x20, 0x66, 0x61, 0x6c, 0x6c, 0x65, 0x6e, + 0x20, 0x68, 0x65, 0x72, 0x65, 0x20, 0x61, 0x67, + 0x65, 0x73, 0x20, 0x61, 0x67, 0x6f, 0x2e, 0x00, + 0x49, 0x20, 0x77, 0x6f, 0x6e, 0x64, 0x65, 0x72, + 0x20, 0x69, 0x66, 0x20, 0x61, 0x6e, 0x79, 0x62, + 0x6f, 0x64, 0x79, 0x20, 0x67, 0x6f, 0x74, 0x20, + 0x68, 0x75, 0x72, 0x74, 0x2e, 0x2e, 0x2e, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xed, 0x00, 0x98, 0x00, 0xed, 0x00, + 0x98, 0x00, 0x01, 0x01, 0x62, 0x75, 0x74, 0x74, + 0x65, 0x72, 0x66, 0x6c, 0x79, 0x00, 0x49, 0x73, + 0x6e, 0x27, 0x74, 0x20, 0x74, 0x68, 0x61, 0x74, + 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x6c, 0x79, 0x3f, + 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xed, 0x00, 0x98, 0x00, 0xed, + 0x00, 0x98, 0x00, 0x01, 0x01, 0x62, 0x75, 0x74, + 0x74, 0x65, 0x72, 0x66, 0x6c, 0x79, 0x00, 0x49, + 0x73, 0x6e, 0x27, 0x74, 0x20, 0x74, 0x68, 0x61, + 0x74, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x6c, 0x79, + 0x3f, 0x00, 0x00, 0x0e, 0x9f, 0x29, 0x9f, 0x4e, + 0x9f, 0x6a, 0x9f, 0xbd, 0x9f, 0xea, 0x9f, 0x00, + 0x00, 0x01, 0x03, 0x01, 0xa4, 0x00, 0x1d, 0x01, + 0xb6, 0x00, 0xf8, 0x00, 0xa9, 0x00, 0xf8, 0x00, + 0xa9, 0x00, 0x03, 0x01, 0x6c, 0x69, 0x7a, 0x61, + 0x72, 0x64, 0x00, 0x01, 0x02, 0x3f, 0x00, 0x8a, + 0x00, 0x5c, 0x00, 0xbc, 0x00, 0x6e, 0x00, 0xaa, + 0x00, 0x6e, 0x00, 0xaa, 0x00, 0x03, 0x00, 0x3f, + 0x3f, 0x3f, 0x00, 0x57, 0x68, 0x61, 0x74, 0x20, + 0x49, 0x53, 0x20, 0x49, 0x54, 0x3f, 0x21, 0x00, + 0x00, 0x03, 0x11, 0x01, 0x70, 0x00, 0x3f, 0x01, + 0xc7, 0x00, 0xf8, 0x00, 0xa9, 0x00, 0x3f, 0x01, + 0xa9, 0x00, 0x02, 0x01, 0x77, 0x61, 0x79, 0x20, + 0x6f, 0x75, 0x74, 0x00, 0x01, 0x04, 0x97, 0x00, + 0x54, 0x00, 0xad, 0x00, 0x5d, 0x00, 0x9f, 0x00, + 0xaa, 0x00, 0x9f, 0x00, 0xaa, 0x00, 0x01, 0x01, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x00, + 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x6e, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x20, 0x69, 0x73, 0x20, 0x76, 0x65, 0x72, 0x79, + 0x20, 0x6f, 0x6c, 0x64, 0x2c, 0x20, 0x62, 0x75, + 0x74, 0x20, 0x49, 0x20, 0x63, 0x61, 0x6e, 0x20, + 0x73, 0x74, 0x69, 0x6c, 0x6c, 0x00, 0x72, 0x65, + 0x61, 0x64, 0x20, 0x69, 0x74, 0x2e, 0x00, 0x00, + 0x05, 0x9a, 0x00, 0x5e, 0x00, 0xa6, 0x00, 0x67, + 0x00, 0x9f, 0x00, 0xaa, 0x00, 0x9f, 0x00, 0xaa, + 0x00, 0x01, 0x01, 0x68, 0x6f, 0x6c, 0x65, 0x00, + 0x49, 0x74, 0x20, 0x6c, 0x6f, 0x6f, 0x6b, 0x73, + 0x20, 0x76, 0x65, 0x72, 0x79, 0x20, 0x64, 0x65, + 0x65, 0x70, 0x2e, 0x00, 0x00, 0x06, 0xf9, 0x00, + 0x9d, 0x00, 0xff, 0x00, 0xa3, 0x00, 0xee, 0x00, + 0xa8, 0x00, 0xee, 0x00, 0xa8, 0x00, 0x02, 0x00, + 0x6e, 0x75, 0x67, 0x67, 0x65, 0x74, 0x00, 0x50, + 0x75, 0x72, 0x65, 0x20, 0x67, 0x6f, 0x6c, 0x64, + 0x21, 0x00, 0x00, 0x32, 0xa0, 0x5e, 0xa0, 0x8d, + 0xa0, 0xb9, 0xa0, 0xe6, 0xa0, 0x1c, 0xa1, 0x51, + 0xa1, 0xb1, 0xa1, 0xe3, 0xa1, 0x07, 0xa2, 0x64, + 0xa2, 0xad, 0xa2, 0xe6, 0xa2, 0x21, 0xa3, 0x5f, + 0xa3, 0x7d, 0xa3, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x00, + 0xc3, 0x00, 0x96, 0x00, 0xc3, 0x00, 0x02, 0x01, + 0x68, 0x65, 0x6e, 0x00, 0x41, 0x20, 0x6c, 0x69, + 0x74, 0x74, 0x6c, 0x65, 0x20, 0x73, 0x74, 0x72, + 0x61, 0x6e, 0x67, 0x65, 0x2e, 0x2e, 0x2e, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xe0, 0x00, 0x87, 0x00, 0xe0, 0x00, + 0x87, 0x00, 0x01, 0x01, 0x63, 0x72, 0x6f, 0x77, + 0x00, 0x45, 0x78, 0x74, 0x72, 0x65, 0x6d, 0x65, + 0x6c, 0x79, 0x20, 0x75, 0x6e, 0x66, 0x72, 0x69, + 0x65, 0x6e, 0x64, 0x6c, 0x79, 0x2e, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xe0, 0x00, 0x87, 0x00, 0xe0, 0x00, 0x87, + 0x00, 0x01, 0x01, 0x63, 0x72, 0x6f, 0x77, 0x00, + 0x4c, 0x6f, 0x6f, 0x6b, 0x73, 0x20, 0x64, 0x61, + 0x6e, 0x67, 0x65, 0x72, 0x6f, 0x75, 0x73, 0x2e, + 0x2e, 0x2e, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0x00, 0x8e, + 0x00, 0xec, 0x00, 0x8e, 0x00, 0x04, 0x01, 0x6d, + 0x6f, 0x75, 0x73, 0x65, 0x00, 0x49, 0x74, 0x27, + 0x73, 0x20, 0x76, 0x65, 0x72, 0x79, 0x20, 0x6e, + 0x65, 0x72, 0x76, 0x6f, 0x75, 0x73, 0x2e, 0x00, + 0x00, 0x05, 0xdb, 0x00, 0x41, 0x00, 0xe9, 0x00, + 0x47, 0x00, 0xe0, 0x00, 0x87, 0x00, 0xe0, 0x00, + 0x87, 0x00, 0x01, 0x01, 0x64, 0x69, 0x76, 0x65, + 0x20, 0x6d, 0x61, 0x73, 0x6b, 0x00, 0x59, 0x65, + 0x61, 0x68, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x61, + 0x6d, 0x61, 0x7a, 0x65, 0x73, 0x20, 0x6d, 0x65, + 0x20, 0x74, 0x6f, 0x6f, 0x2e, 0x00, 0x00, 0x06, + 0xc9, 0x00, 0x72, 0x00, 0xf1, 0x00, 0x80, 0x00, + 0xe0, 0x00, 0x87, 0x00, 0xde, 0x00, 0x8f, 0x00, + 0x01, 0x01, 0x66, 0x69, 0x6e, 0x73, 0x00, 0x54, + 0x68, 0x65, 0x79, 0x20, 0x77, 0x69, 0x6c, 0x6c, + 0x20, 0x66, 0x69, 0x74, 0x20, 0x6d, 0x65, 0x20, + 0x70, 0x65, 0x72, 0x66, 0x65, 0x63, 0x74, 0x6c, + 0x79, 0x2e, 0x00, 0x00, 0x07, 0xc8, 0x00, 0x3a, + 0x00, 0xf5, 0x00, 0x80, 0x00, 0xe0, 0x00, 0x87, + 0x00, 0xe0, 0x00, 0x87, 0x00, 0x01, 0x01, 0x73, + 0x63, 0x61, 0x72, 0x65, 0x63, 0x72, 0x6f, 0x77, + 0x00, 0x41, 0x20, 0x74, 0x68, 0x69, 0x6e, 0x67, + 0x20, 0x74, 0x6f, 0x20, 0x73, 0x63, 0x61, 0x72, + 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x72, + 0x6f, 0x77, 0x73, 0x2e, 0x00, 0x41, 0x74, 0x20, + 0x6c, 0x65, 0x61, 0x73, 0x74, 0x20, 0x74, 0x68, + 0x61, 0x74, 0x27, 0x73, 0x20, 0x77, 0x68, 0x61, + 0x74, 0x20, 0x74, 0x68, 0x65, 0x00, 0x74, 0x68, + 0x65, 0x6f, 0x72, 0x79, 0x20, 0x73, 0x61, 0x79, + 0x73, 0x2e, 0x00, 0x00, 0x08, 0x7a, 0x00, 0xa7, + 0x00, 0x87, 0x00, 0xb3, 0x00, 0x90, 0x00, 0xb5, + 0x00, 0x90, 0x00, 0xb5, 0x00, 0x04, 0x01, 0x73, + 0x69, 0x63, 0x6b, 0x6c, 0x65, 0x00, 0x41, 0x20, + 0x76, 0x65, 0x72, 0x79, 0x20, 0x64, 0x61, 0x6e, + 0x67, 0x65, 0x72, 0x6f, 0x75, 0x73, 0x20, 0x74, + 0x6f, 0x6f, 0x6c, 0x2e, 0x00, 0x00, 0x09, 0x3d, + 0x00, 0xb6, 0x00, 0x8d, 0x00, 0xc7, 0x00, 0x84, + 0x00, 0xbe, 0x00, 0x7e, 0x00, 0xc7, 0x00, 0x03, + 0x01, 0x70, 0x61, 0x74, 0x68, 0x20, 0x74, 0x6f, + 0x20, 0x76, 0x69, 0x6c, 0x6c, 0x61, 0x67, 0x65, + 0x00, 0x01, 0x0a, 0x7b, 0x00, 0x7a, 0x00, 0x99, + 0x00, 0xa8, 0x00, 0xa2, 0x00, 0xb5, 0x00, 0xa2, + 0x00, 0xb5, 0x00, 0x04, 0x01, 0x68, 0x61, 0x79, + 0x20, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x00, 0x54, + 0x68, 0x65, 0x72, 0x65, 0x20, 0x6d, 0x75, 0x73, + 0x74, 0x20, 0x62, 0x65, 0x20, 0x73, 0x6f, 0x6d, + 0x65, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x20, 0x6d, + 0x65, 0x74, 0x61, 0x6c, 0x20, 0x69, 0x6e, 0x73, + 0x69, 0x64, 0x65, 0x2c, 0x00, 0x49, 0x20, 0x63, + 0x61, 0x6e, 0x20, 0x73, 0x65, 0x65, 0x20, 0x69, + 0x74, 0x20, 0x67, 0x6c, 0x69, 0x74, 0x74, 0x65, + 0x72, 0x69, 0x6e, 0x67, 0x21, 0x00, 0x00, 0x0b, + 0x6a, 0x00, 0x94, 0x00, 0x7d, 0x00, 0xb3, 0x00, + 0x81, 0x00, 0xbf, 0x00, 0x81, 0x00, 0xbf, 0x00, + 0x04, 0x01, 0x72, 0x61, 0x6b, 0x65, 0x00, 0x49, + 0x74, 0x27, 0x73, 0x20, 0x76, 0x65, 0x72, 0x79, + 0x20, 0x6f, 0x6c, 0x64, 0x20, 0x61, 0x6e, 0x64, + 0x20, 0x74, 0x68, 0x65, 0x72, 0x65, 0x20, 0x61, + 0x72, 0x65, 0x6e, 0x27, 0x74, 0x00, 0x6d, 0x61, + 0x6e, 0x79, 0x20, 0x74, 0x65, 0x65, 0x74, 0x68, + 0x20, 0x6c, 0x65, 0x66, 0x74, 0x2e, 0x00, 0x00, + 0x0c, 0xa2, 0x00, 0x73, 0x00, 0xb6, 0x00, 0x80, + 0x00, 0xec, 0x00, 0x8e, 0x00, 0xec, 0x00, 0x8e, + 0x00, 0x04, 0x01, 0x6d, 0x6f, 0x75, 0x73, 0x65, + 0x20, 0x68, 0x6f, 0x6c, 0x65, 0x00, 0x48, 0x6f, + 0x6d, 0x65, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x70, + 0x61, 0x6e, 0x74, 0x72, 0x79, 0x20, 0x69, 0x6e, + 0x20, 0x6f, 0x6e, 0x65, 0x2e, 0x2e, 0x2e, 0x00, + 0x00, 0x0d, 0xa5, 0x00, 0x5c, 0x00, 0xbf, 0x00, + 0x80, 0x00, 0xec, 0x00, 0x8e, 0x00, 0xec, 0x00, + 0x8e, 0x00, 0x04, 0x01, 0x68, 0x61, 0x79, 0x20, + 0x73, 0x74, 0x61, 0x63, 0x6b, 0x00, 0x54, 0x68, + 0x65, 0x72, 0x65, 0x27, 0x73, 0x20, 0x61, 0x20, + 0x6d, 0x6f, 0x75, 0x73, 0x65, 0x20, 0x68, 0x6f, + 0x6c, 0x65, 0x20, 0x69, 0x6e, 0x73, 0x69, 0x64, + 0x65, 0x21, 0x00, 0x00, 0x0e, 0xae, 0x00, 0xbc, + 0x00, 0xb4, 0x00, 0xc2, 0x00, 0xa1, 0x00, 0xc3, + 0x00, 0xa1, 0x00, 0xc3, 0x00, 0x02, 0x00, 0x66, + 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x00, 0x49, + 0x74, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x68, + 0x61, 0x76, 0x65, 0x20, 0x62, 0x65, 0x65, 0x6e, + 0x20, 0x64, 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64, + 0x20, 0x62, 0x79, 0x20, 0x68, 0x65, 0x6e, 0x2e, + 0x00, 0x00, 0x0f, 0x51, 0x00, 0x89, 0x00, 0x70, + 0x00, 0xb6, 0x00, 0x84, 0x00, 0xbe, 0x00, 0x84, + 0x00, 0xbe, 0x00, 0x04, 0x01, 0x68, 0x61, 0x79, + 0x20, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x00, 0x01, + 0x10, 0x04, 0x01, 0x0d, 0x00, 0x3f, 0x01, 0x5d, + 0x00, 0xe8, 0x00, 0x86, 0x00, 0xe8, 0x00, 0x86, + 0x00, 0x02, 0x01, 0x6d, 0x61, 0x6e, 0x73, 0x69, + 0x6f, 0x6e, 0x00, 0x47, 0x65, 0x65, 0x2c, 0x20, + 0x73, 0x6f, 0x6d, 0x65, 0x20, 0x70, 0x65, 0x6f, + 0x70, 0x6c, 0x65, 0x20, 0x72, 0x65, 0x61, 0x6c, + 0x6c, 0x79, 0x00, 0x68, 0x61, 0x76, 0x65, 0x20, + 0x6e, 0x6f, 0x20, 0x74, 0x61, 0x73, 0x74, 0x65, + 0x2e, 0x00, 0x00, 0xca, 0xa3, 0x0d, 0xa4, 0x3e, + 0xa4, 0x57, 0xa4, 0x00, 0x00, 0x01, 0x55, 0x00, + 0x35, 0x00, 0x79, 0x00, 0x56, 0x00, 0x67, 0x00, + 0x8f, 0x00, 0x67, 0x00, 0x8f, 0x00, 0x01, 0x01, + 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x00, 0x54, + 0x68, 0x65, 0x72, 0x65, 0x27, 0x73, 0x20, 0x61, + 0x20, 0x73, 0x68, 0x75, 0x74, 0x74, 0x65, 0x72, + 0x2c, 0x20, 0x49, 0x20, 0x63, 0x61, 0x6e, 0x27, + 0x74, 0x20, 0x73, 0x65, 0x65, 0x20, 0x61, 0x20, + 0x74, 0x68, 0x69, 0x6e, 0x67, 0x2e, 0x00, 0x00, + 0x02, 0xaa, 0x00, 0x63, 0x00, 0xbd, 0x00, 0x7b, + 0x00, 0x8f, 0x00, 0x8f, 0x00, 0x8f, 0x00, 0x8f, + 0x00, 0x02, 0x01, 0x73, 0x63, 0x75, 0x6c, 0x70, + 0x74, 0x75, 0x72, 0x65, 0x00, 0x54, 0x68, 0x65, + 0x20, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x20, 0x69, + 0x73, 0x20, 0x73, 0x69, 0x63, 0x6b, 0x2e, 0x00, + 0x00, 0x03, 0xc6, 0x00, 0x53, 0x00, 0xf0, 0x00, + 0xa4, 0x00, 0xaa, 0x00, 0x99, 0x00, 0xd1, 0x00, + 0x99, 0x00, 0x02, 0x01, 0x64, 0x6f, 0x6f, 0x72, + 0x00, 0x01, 0x04, 0x00, 0x00, 0x8b, 0x00, 0x22, + 0x00, 0xc7, 0x00, 0x42, 0x00, 0xa7, 0x00, 0x42, + 0x00, 0xa7, 0x00, 0x04, 0x01, 0x77, 0x61, 0x79, + 0x20, 0x6f, 0x75, 0x74, 0x20, 0x6f, 0x66, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x6d, 0x61, 0x6e, 0x73, + 0x69, 0x6f, 0x6e, 0x00, 0x01, 0x92, 0xa4, 0xab, + 0xa4, 0xc4, 0xa4, 0x09, 0xa5, 0x3d, 0xa5, 0x8e, + 0xa5, 0xbd, 0xa5, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x5b, 0x00, 0x1a, 0x00, 0xae, 0x00, 0x2b, 0x00, + 0xaa, 0x00, 0x2b, 0x00, 0xaa, 0x00, 0x04, 0x01, + 0x64, 0x6f, 0x6f, 0x72, 0x00, 0x01, 0x02, 0xe8, + 0x00, 0x5b, 0x00, 0x17, 0x01, 0xa8, 0x00, 0xff, + 0x00, 0xb2, 0x00, 0xff, 0x00, 0xa6, 0x00, 0x01, + 0x01, 0x64, 0x6f, 0x6f, 0x72, 0x00, 0x01, 0x03, + 0x2b, 0x00, 0x74, 0x00, 0x4f, 0x00, 0xae, 0x00, + 0x3e, 0x00, 0xb7, 0x00, 0x3e, 0x00, 0xb7, 0x00, + 0x01, 0x01, 0x66, 0x6c, 0x6f, 0x77, 0x65, 0x72, + 0x73, 0x00, 0x53, 0x75, 0x72, 0x70, 0x72, 0x69, + 0x73, 0x65, 0x2c, 0x20, 0x73, 0x75, 0x72, 0x70, + 0x72, 0x69, 0x73, 0x65, 0x2c, 0x20, 0x74, 0x68, + 0x65, 0x79, 0x20, 0x61, 0x72, 0x65, 0x6e, 0x27, + 0x74, 0x00, 0x70, 0x6c, 0x61, 0x73, 0x74, 0x69, + 0x63, 0x2e, 0x00, 0x00, 0x04, 0x82, 0x00, 0x32, + 0x00, 0xab, 0x00, 0x69, 0x00, 0xa2, 0x00, 0xad, + 0x00, 0xa2, 0x00, 0xad, 0x00, 0x01, 0x01, 0x70, + 0x6c, 0x61, 0x6e, 0x74, 0x00, 0x48, 0x6f, 0x77, + 0x27, 0x73, 0x20, 0x69, 0x74, 0x20, 0x68, 0x61, + 0x6e, 0x67, 0x69, 0x6e, 0x27, 0x2c, 0x20, 0x52, + 0x6f, 0x62, 0x62, 0x69, 0x65, 0x3f, 0x00, 0x00, + 0x05, 0x1e, 0x00, 0x49, 0x00, 0x9d, 0x00, 0xa8, + 0x00, 0xa2, 0x00, 0xad, 0x00, 0xa2, 0x00, 0xad, + 0x00, 0x01, 0x01, 0x73, 0x74, 0x61, 0x69, 0x72, + 0x73, 0x00, 0x54, 0x68, 0x65, 0x79, 0x20, 0x73, + 0x61, 0x79, 0x20, 0x65, 0x76, 0x65, 0x72, 0x79, + 0x20, 0x73, 0x74, 0x65, 0x70, 0x20, 0x6d, 0x61, + 0x6b, 0x65, 0x73, 0x20, 0x79, 0x6f, 0x75, 0x72, + 0x20, 0x6c, 0x69, 0x66, 0x65, 0x00, 0x33, 0x20, + 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x20, + 0x6c, 0x6f, 0x6e, 0x67, 0x65, 0x72, 0x2e, 0x00, + 0x00, 0x06, 0x1f, 0x01, 0xa8, 0x00, 0x3f, 0x01, + 0xc7, 0x00, 0x18, 0x01, 0xbc, 0x00, 0x3f, 0x01, + 0xbc, 0x00, 0x02, 0x01, 0x6f, 0x74, 0x68, 0x65, + 0x72, 0x20, 0x70, 0x61, 0x72, 0x74, 0x20, 0x6f, + 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, + 0x72, 0x72, 0x69, 0x64, 0x6f, 0x72, 0x00, 0x01, + 0x07, 0x00, 0x00, 0xc0, 0x00, 0x3f, 0x01, 0xc7, + 0x00, 0xa0, 0x00, 0xbc, 0x00, 0xa0, 0x00, 0xc7, + 0x00, 0x03, 0x01, 0x77, 0x61, 0x79, 0x20, 0x74, + 0x6f, 0x20, 0x70, 0x6f, 0x72, 0x63, 0x68, 0x00, + 0x01, 0x00, 0xa6, 0x1f, 0xa6, 0x4d, 0xa6, 0x84, + 0xa6, 0xd4, 0xa6, 0xee, 0xa6, 0x11, 0xa7, 0x51, + 0xa7, 0x72, 0xa7, 0x8b, 0xa7, 0xb3, 0xa7, 0xdb, + 0xa7, 0x03, 0xa8, 0x2b, 0xa8, 0x53, 0xa8, 0x7b, + 0xa8, 0x00, 0x00, 0x01, 0x00, 0x00, 0x78, 0x00, + 0x51, 0x00, 0x9c, 0x00, 0x12, 0x00, 0x9f, 0x00, + 0x12, 0x00, 0x9f, 0x00, 0x01, 0x01, 0x64, 0x6f, + 0x77, 0x6e, 0x73, 0x74, 0x61, 0x69, 0x72, 0x73, + 0x00, 0x01, 0x02, 0x14, 0x00, 0x3f, 0x00, 0x55, + 0x00, 0x6a, 0x00, 0x34, 0x00, 0xa5, 0x00, 0x34, + 0x00, 0xa5, 0x00, 0x01, 0x01, 0x70, 0x69, 0x63, + 0x74, 0x75, 0x72, 0x65, 0x00, 0x49, 0x74, 0x27, + 0x73, 0x20, 0x73, 0x6f, 0x6d, 0x65, 0x20, 0x63, + 0x61, 0x73, 0x74, 0x6c, 0x65, 0x2e, 0x00, 0x00, + 0x03, 0x29, 0x01, 0x5f, 0x00, 0x3f, 0x01, 0xb0, + 0x00, 0x0f, 0x01, 0xae, 0x00, 0x0f, 0x01, 0xae, + 0x00, 0x02, 0x01, 0x61, 0x72, 0x6d, 0x6f, 0x75, + 0x72, 0x00, 0x54, 0x68, 0x65, 0x20, 0x62, 0x65, + 0x74, 0x61, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, + 0x74, 0x61, 0x6e, 0x6b, 0x2e, 0x00, 0x00, 0x04, + 0xcf, 0x00, 0x63, 0x00, 0xda, 0x00, 0x67, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x01, 0x00, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x20, + 0x74, 0x61, 0x70, 0x65, 0x00, 0x57, 0x68, 0x61, + 0x74, 0x20, 0x6b, 0x69, 0x6e, 0x64, 0x20, 0x6f, + 0x66, 0x20, 0x6d, 0x6f, 0x76, 0x69, 0x65, 0x20, + 0x6d, 0x75, 0x73, 0x74, 0x20, 0x69, 0x74, 0x20, + 0x62, 0x65, 0x00, 0x68, 0x69, 0x64, 0x64, 0x65, + 0x6e, 0x20, 0x73, 0x6f, 0x20, 0x77, 0x65, 0x6c, + 0x6c, 0x3f, 0x2e, 0x2e, 0x2e, 0x00, 0x00, 0x05, + 0xc3, 0x00, 0x54, 0x00, 0xc7, 0x00, 0x5d, 0x00, + 0xd0, 0x00, 0x97, 0x00, 0xd0, 0x00, 0x97, 0x00, + 0x01, 0x01, 0x62, 0x6f, 0x6f, 0x6b, 0x00, 0x00, + 0x00, 0x06, 0x6f, 0x00, 0x2f, 0x00, 0x3f, 0x01, + 0x6e, 0x00, 0xdd, 0x00, 0x9b, 0x00, 0xdd, 0x00, + 0x9b, 0x00, 0x01, 0x01, 0x62, 0x6f, 0x6f, 0x6b, + 0x73, 0x00, 0x4f, 0x68, 0x2c, 0x20, 0x62, 0x6f, + 0x79, 0x21, 0x00, 0x00, 0x07, 0xd5, 0x00, 0xab, + 0x00, 0xe3, 0x00, 0xbd, 0x00, 0xea, 0x00, 0xbd, + 0x00, 0xea, 0x00, 0xbd, 0x00, 0x04, 0x01, 0x74, + 0x72, 0x61, 0x73, 0x68, 0x20, 0x63, 0x61, 0x6e, + 0x00, 0x54, 0x68, 0x65, 0x72, 0x65, 0x20, 0x61, + 0x72, 0x65, 0x20, 0x61, 0x20, 0x6c, 0x6f, 0x74, + 0x20, 0x6f, 0x66, 0x20, 0x70, 0x61, 0x70, 0x65, + 0x72, 0x73, 0x20, 0x69, 0x6e, 0x73, 0x69, 0x64, + 0x65, 0x2e, 0x00, 0x00, 0x08, 0x98, 0x00, 0x8d, + 0x00, 0xb2, 0x00, 0xc1, 0x00, 0xa5, 0x00, 0xc6, + 0x00, 0xa5, 0x00, 0xc6, 0x00, 0x01, 0x01, 0x63, + 0x68, 0x61, 0x69, 0x72, 0x00, 0x53, 0x6f, 0x66, + 0x74, 0x79, 0x2e, 0x00, 0x00, 0x09, 0x87, 0x00, + 0x86, 0x00, 0xa2, 0x00, 0x99, 0x00, 0x88, 0x00, + 0xc1, 0x00, 0x88, 0x00, 0xc1, 0x00, 0x01, 0x01, + 0x6c, 0x61, 0x6d, 0x70, 0x00, 0x01, 0x0a, 0x7c, + 0x00, 0x9e, 0x00, 0x95, 0x00, 0xa6, 0x00, 0x88, + 0x00, 0xc1, 0x00, 0x88, 0x00, 0xc1, 0x00, 0x01, + 0x01, 0x64, 0x72, 0x61, 0x77, 0x65, 0x72, 0x00, + 0x49, 0x74, 0x27, 0x73, 0x20, 0x63, 0x6c, 0x6f, + 0x73, 0x65, 0x64, 0x2e, 0x00, 0x00, 0x0b, 0x7c, + 0x00, 0xa8, 0x00, 0x95, 0x00, 0xaf, 0x00, 0x88, + 0x00, 0xc1, 0x00, 0x88, 0x00, 0xc1, 0x00, 0x01, + 0x01, 0x64, 0x72, 0x61, 0x77, 0x65, 0x72, 0x00, + 0x49, 0x74, 0x27, 0x73, 0x20, 0x63, 0x6c, 0x6f, + 0x73, 0x65, 0x64, 0x2e, 0x00, 0x00, 0x0c, 0x7c, + 0x00, 0xb1, 0x00, 0x95, 0x00, 0xb8, 0x00, 0x88, + 0x00, 0xc1, 0x00, 0x88, 0x00, 0xc1, 0x00, 0x01, + 0x01, 0x64, 0x72, 0x61, 0x77, 0x65, 0x72, 0x00, + 0x49, 0x74, 0x27, 0x73, 0x20, 0x63, 0x6c, 0x6f, + 0x73, 0x65, 0x64, 0x2e, 0x00, 0x00, 0x0d, 0xb6, + 0x00, 0x9e, 0x00, 0xd0, 0x00, 0xa6, 0x00, 0xc4, + 0x00, 0xc0, 0x00, 0xc4, 0x00, 0xc0, 0x00, 0x01, + 0x01, 0x64, 0x72, 0x61, 0x77, 0x65, 0x72, 0x00, + 0x49, 0x74, 0x27, 0x73, 0x20, 0x63, 0x6c, 0x6f, + 0x73, 0x65, 0x64, 0x2e, 0x00, 0x00, 0x0e, 0xb6, + 0x00, 0xa8, 0x00, 0xd0, 0x00, 0xaf, 0x00, 0xc4, + 0x00, 0xc0, 0x00, 0xc4, 0x00, 0xc0, 0x00, 0x01, + 0x01, 0x64, 0x72, 0x61, 0x77, 0x65, 0x72, 0x00, + 0x49, 0x74, 0x27, 0x73, 0x20, 0x63, 0x6c, 0x6f, + 0x73, 0x65, 0x64, 0x2e, 0x00, 0x00, 0x0f, 0xb6, + 0x00, 0xb1, 0x00, 0xd0, 0x00, 0xb8, 0x00, 0xc4, + 0x00, 0xc0, 0x00, 0xc4, 0x00, 0xc0, 0x00, 0x01, + 0x01, 0x64, 0x72, 0x61, 0x77, 0x65, 0x72, 0x00, + 0x49, 0x74, 0x27, 0x73, 0x20, 0x63, 0x6c, 0x6f, + 0x73, 0x65, 0x64, 0x2e, 0x00, 0x00, 0x10, 0xf3, + 0x00, 0x71, 0x00, 0x18, 0x01, 0x98, 0x00, 0x06, + 0x01, 0x9e, 0x00, 0x06, 0x01, 0x9e, 0x00, 0x01, + 0x01, 0x6c, 0x61, 0x64, 0x64, 0x65, 0x72, 0x00, + 0x49, 0x6e, 0x64, 0x69, 0x73, 0x70, 0x65, 0x6e, + 0x73, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x61, 0x64, + 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, + 0x6f, 0x20, 0x67, 0x69, 0x72, 0x6c, 0x73, 0x20, + 0x69, 0x6e, 0x20, 0x73, 0x6b, 0x69, 0x72, 0x74, + 0x73, 0x2e, 0x00, 0x00, 0xd5, 0xa8, 0xee, 0xa8, + 0x1e, 0xa9, 0x58, 0xa9, 0x96, 0xa9, 0xcc, 0xa9, + 0xf4, 0xa9, 0x1f, 0xaa, 0x38, 0xaa, 0x00, 0x00, + 0x01, 0x73, 0x00, 0x5b, 0x00, 0xa3, 0x00, 0xa7, + 0x00, 0x8c, 0x00, 0xac, 0x00, 0x8c, 0x00, 0xa6, + 0x00, 0x01, 0x01, 0x64, 0x6f, 0x6f, 0x72, 0x00, + 0x01, 0x02, 0x36, 0x01, 0x8c, 0x00, 0x3b, 0x01, + 0x91, 0x00, 0x2c, 0x01, 0xb2, 0x00, 0x2c, 0x01, + 0xb2, 0x00, 0x02, 0x01, 0x68, 0x6f, 0x6c, 0x65, + 0x00, 0x49, 0x74, 0x20, 0x68, 0x61, 0x73, 0x20, + 0x61, 0x20, 0x73, 0x71, 0x75, 0x61, 0x72, 0x65, + 0x20, 0x73, 0x68, 0x61, 0x70, 0x65, 0x2e, 0x00, + 0x00, 0x03, 0x31, 0x01, 0x8b, 0x00, 0x3b, 0x01, + 0x91, 0x00, 0x2c, 0x01, 0xb2, 0x00, 0x2c, 0x01, + 0xb2, 0x00, 0x02, 0x00, 0x68, 0x61, 0x6e, 0x64, + 0x6c, 0x65, 0x00, 0x49, 0x74, 0x20, 0x66, 0x69, + 0x74, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x68, 0x6f, 0x6c, 0x65, 0x20, 0x70, + 0x65, 0x72, 0x66, 0x65, 0x63, 0x74, 0x6c, 0x79, + 0x2e, 0x00, 0x00, 0x04, 0x04, 0x00, 0x3f, 0x00, + 0x64, 0x00, 0x61, 0x00, 0x3b, 0x00, 0xb2, 0x00, + 0x3b, 0x00, 0xb2, 0x00, 0x01, 0x01, 0x70, 0x69, + 0x63, 0x74, 0x75, 0x72, 0x65, 0x00, 0x49, 0x20, + 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x20, 0x6c, + 0x69, 0x6b, 0x65, 0x64, 0x20, 0x53, 0x60, 0x54, + 0x60, 0x41, 0x60, 0x52, 0x60, 0x20, 0x57, 0x60, + 0x41, 0x60, 0x52, 0x60, 0x53, 0x60, 0x2e, 0x00, + 0x00, 0x05, 0xd8, 0x00, 0x4d, 0x00, 0x09, 0x01, + 0x79, 0x00, 0xf3, 0x00, 0xb5, 0x00, 0xf3, 0x00, + 0xb5, 0x00, 0x01, 0x01, 0x70, 0x69, 0x63, 0x74, + 0x75, 0x72, 0x65, 0x00, 0x41, 0x6e, 0x64, 0x20, + 0x49, 0x20, 0x74, 0x6f, 0x75, 0x67, 0x68, 0x74, + 0x20, 0x49, 0x20, 0x77, 0x61, 0x73, 0x20, 0x77, + 0x65, 0x69, 0x72, 0x64, 0x2e, 0x00, 0x00, 0x06, + 0xf3, 0x00, 0x91, 0x00, 0x15, 0x01, 0xc7, 0x00, + 0xf3, 0x00, 0xb5, 0x00, 0xf3, 0x00, 0xb5, 0x00, + 0x03, 0x01, 0x66, 0x6c, 0x6f, 0x77, 0x65, 0x72, + 0x73, 0x00, 0x4e, 0x69, 0x63, 0x65, 0x20, 0x73, + 0x6d, 0x65, 0x6c, 0x6c, 0x2e, 0x00, 0x00, 0x07, + 0x00, 0x00, 0xa8, 0x00, 0x1a, 0x00, 0xc7, 0x00, + 0x28, 0x00, 0xbc, 0x00, 0x00, 0x00, 0xbc, 0x00, + 0x04, 0x01, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x20, + 0x70, 0x61, 0x72, 0x74, 0x20, 0x6f, 0x66, 0x20, + 0x63, 0x6f, 0x72, 0x72, 0x69, 0x64, 0x6f, 0x72, + 0x00, 0x01, 0x08, 0x25, 0x01, 0x5f, 0x00, 0x3f, + 0x01, 0xb6, 0x00, 0x11, 0x01, 0xab, 0x00, 0x11, + 0x01, 0xab, 0x00, 0x02, 0x01, 0x64, 0x6f, 0x6f, + 0x72, 0x00, 0x01, 0x09, 0x0e, 0x00, 0x5a, 0x00, + 0x32, 0x00, 0xb0, 0x00, 0x28, 0x00, 0xbc, 0x00, + 0x28, 0x00, 0xbc, 0x00, 0x01, 0x01, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x65, 0x00, 0x42, 0x69, 0x7a, + 0x61, 0x72, 0x72, 0x65, 0x2e, 0x00, 0x00, 0x6e, + 0xaa, 0x9a, 0xaa, 0xcd, 0xaa, 0x12, 0xab, 0x5d, + 0xab, 0x79, 0xab, 0x92, 0xab, 0xda, 0xab, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x52, 0x00, 0xb6, 0x00, 0x52, 0x00, + 0xb6, 0x00, 0x01, 0x01, 0x72, 0x6f, 0x62, 0x6f, + 0x74, 0x00, 0x49, 0x6d, 0x70, 0x72, 0x65, 0x73, + 0x73, 0x69, 0x76, 0x65, 0x2e, 0x00, 0x00, 0x4d, + 0x69, 0x6b, 0x65, 0x00, 0xff, 0x02, 0x2d, 0x00, + 0x75, 0x00, 0x37, 0x00, 0x83, 0x00, 0x33, 0x00, + 0xaa, 0x00, 0x33, 0x00, 0xaa, 0x00, 0x01, 0x00, + 0x6a, 0x61, 0x72, 0x00, 0x54, 0x68, 0x69, 0x73, + 0x20, 0x6a, 0x61, 0x72, 0x20, 0x69, 0x73, 0x20, + 0x66, 0x75, 0x6c, 0x6c, 0x20, 0x6f, 0x66, 0x20, + 0x70, 0x69, 0x6c, 0x6c, 0x73, 0x2e, 0x00, 0x00, + 0x03, 0x2d, 0x00, 0x75, 0x00, 0x37, 0x00, 0x83, + 0x00, 0x33, 0x00, 0xaa, 0x00, 0x33, 0x00, 0xaa, + 0x00, 0x01, 0x00, 0x62, 0x6f, 0x6f, 0x6b, 0x00, + 0x49, 0x74, 0x27, 0x73, 0x20, 0x62, 0x69, 0x67, + 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6c, 0x6f, 0x6f, + 0x6b, 0x73, 0x00, 0x6c, 0x69, 0x6b, 0x65, 0x20, + 0x73, 0x6f, 0x6d, 0x65, 0x20, 0x6b, 0x69, 0x6e, + 0x64, 0x20, 0x6f, 0x66, 0x00, 0x64, 0x69, 0x61, + 0x72, 0x79, 0x2e, 0x00, 0x00, 0x04, 0x74, 0x00, + 0x2d, 0x00, 0xb9, 0x00, 0x7e, 0x00, 0x98, 0x00, + 0xa5, 0x00, 0x98, 0x00, 0xa5, 0x00, 0x01, 0x01, + 0x70, 0x69, 0x63, 0x74, 0x75, 0x72, 0x65, 0x00, + 0x54, 0x68, 0x61, 0x74, 0x20, 0x6f, 0x6e, 0x65, + 0x27, 0x73, 0x20, 0x67, 0x72, 0x65, 0x61, 0x74, + 0x2e, 0x20, 0x49, 0x27, 0x64, 0x20, 0x6c, 0x69, + 0x6b, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x68, 0x61, + 0x76, 0x65, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6d, + 0x79, 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x00, 0x00, + 0x05, 0x88, 0x00, 0x88, 0x00, 0xa5, 0x00, 0x9f, + 0x00, 0x98, 0x00, 0xa5, 0x00, 0x98, 0x00, 0xa5, + 0x00, 0x01, 0x01, 0x63, 0x61, 0x62, 0x69, 0x6e, + 0x65, 0x74, 0x00, 0x01, 0x06, 0x4e, 0x00, 0xbd, + 0x00, 0xe0, 0x00, 0xc7, 0x00, 0x8b, 0x00, 0xbc, + 0x00, 0x8b, 0x00, 0xc7, 0x00, 0x03, 0x01, 0x65, + 0x78, 0x69, 0x74, 0x00, 0x01, 0x07, 0x00, 0x00, + 0xc7, 0x00, 0x4d, 0x00, 0xc7, 0x00, 0x8b, 0x00, + 0xbc, 0x00, 0x39, 0x00, 0xc6, 0x00, 0x03, 0x00, + 0x6c, 0x65, 0x66, 0x74, 0x20, 0x6c, 0x6f, 0x77, + 0x65, 0x72, 0x20, 0x65, 0x64, 0x67, 0x65, 0x20, + 0x6f, 0x66, 0x20, 0x73, 0x63, 0x72, 0x65, 0x65, + 0x6e, 0x00, 0x49, 0x20, 0x63, 0x6f, 0x75, 0x6c, + 0x64, 0x20, 0x74, 0x72, 0x79, 0x20, 0x74, 0x6f, + 0x20, 0x68, 0x69, 0x64, 0x65, 0x20, 0x68, 0x65, + 0x72, 0x65, 0x21, 0x00, 0x00, 0x08, 0x89, 0x00, + 0x7e, 0x00, 0x93, 0x00, 0x85, 0x00, 0x8f, 0x00, + 0xa3, 0x00, 0x8f, 0x00, 0xa3, 0x00, 0x01, 0x00, + 0x64, 0x6f, 0x6f, 0x72, 0x20, 0x68, 0x61, 0x6e, + 0x64, 0x6c, 0x65, 0x00, 0x4a, 0x6f, 0x68, 0x6e, + 0x20, 0x4e, 0x6f, 0x74, 0x79, 0x20, 0x6c, 0x65, + 0x66, 0x74, 0x20, 0x69, 0x74, 0x20, 0x68, 0x65, + 0x72, 0x65, 0x2e, 0x00, 0x00, 0x2c, 0xac, 0x6b, + 0xac, 0xb8, 0xac, 0xe3, 0xac, 0xff, 0xac, 0x43, + 0xad, 0x8d, 0xad, 0xcc, 0xad, 0x1d, 0xae, 0x6e, + 0xae, 0xbe, 0xae, 0xf8, 0xae, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x8d, 0x00, 0x9f, 0x00, 0x8d, 0x00, 0x9f, 0x00, + 0x04, 0x01, 0x63, 0x6f, 0x6f, 0x6b, 0x00, 0x48, + 0x65, 0x27, 0x73, 0x20, 0x63, 0x6f, 0x6d, 0x70, + 0x6c, 0x65, 0x74, 0x65, 0x6c, 0x79, 0x20, 0x61, + 0x64, 0x64, 0x69, 0x63, 0x74, 0x65, 0x64, 0x20, + 0x74, 0x6f, 0x20, 0x68, 0x69, 0x73, 0x20, 0x77, + 0x6f, 0x72, 0x6b, 0x2e, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x04, + 0x01, 0x73, 0x74, 0x65, 0x77, 0x00, 0x49, 0x74, + 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, + 0x20, 0x73, 0x6f, 0x6d, 0x65, 0x20, 0x6b, 0x69, + 0x6e, 0x64, 0x20, 0x6f, 0x66, 0x20, 0x73, 0x6f, + 0x75, 0x70, 0x2e, 0x00, 0x4e, 0x6f, 0x77, 0x20, + 0x77, 0x69, 0x74, 0x68, 0x20, 0x65, 0x78, 0x74, + 0x72, 0x61, 0x20, 0x70, 0x6f, 0x77, 0x65, 0x72, + 0x2e, 0x00, 0x00, 0x03, 0x26, 0x00, 0x84, 0x00, + 0x30, 0x00, 0x8e, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x04, 0x01, 0x68, 0x6f, + 0x74, 0x20, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x00, + 0x54, 0x68, 0x61, 0x74, 0x27, 0x73, 0x20, 0x43, + 0x4f, 0x4f, 0x4c, 0x2e, 0x00, 0x00, 0x04, 0x1c, + 0x01, 0x9c, 0x00, 0x3f, 0x01, 0xc7, 0x00, 0x0b, + 0x01, 0xb2, 0x00, 0x3f, 0x01, 0xb2, 0x00, 0x02, + 0x01, 0x77, 0x61, 0x79, 0x20, 0x6f, 0x75, 0x74, + 0x00, 0x01, 0x05, 0x9e, 0x00, 0x7f, 0x00, 0xc1, + 0x00, 0x9b, 0x00, 0xc0, 0x00, 0xa0, 0x00, 0xc0, + 0x00, 0xa0, 0x00, 0x01, 0x01, 0x72, 0x65, 0x66, + 0x72, 0x69, 0x67, 0x65, 0x72, 0x61, 0x74, 0x6f, + 0x72, 0x00, 0x4d, 0x79, 0x20, 0x66, 0x61, 0x76, + 0x6f, 0x75, 0x72, 0x69, 0x74, 0x65, 0x20, 0x74, + 0x68, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x6e, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x6b, 0x69, 0x74, 0x63, + 0x68, 0x65, 0x6e, 0x2e, 0x00, 0x00, 0x06, 0x81, + 0x00, 0x74, 0x00, 0x9c, 0x00, 0x7c, 0x00, 0x8d, + 0x00, 0x9f, 0x00, 0x8d, 0x00, 0x9f, 0x00, 0x01, + 0x01, 0x72, 0x61, 0x64, 0x69, 0x6f, 0x00, 0x49, + 0x74, 0x20, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x20, + 0x77, 0x69, 0x74, 0x68, 0x20, 0x62, 0x61, 0x74, + 0x74, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2e, 0x20, + 0x4a, 0x75, 0x73, 0x74, 0x20, 0x6c, 0x69, 0x6b, + 0x65, 0x00, 0x74, 0x68, 0x65, 0x20, 0x77, 0x61, + 0x6c, 0x6b, 0x6d, 0x61, 0x6e, 0x2e, 0x00, 0x00, + 0x07, 0xdb, 0x00, 0x70, 0x00, 0xe0, 0x00, 0x7c, + 0x00, 0xdd, 0x00, 0xa0, 0x00, 0xdd, 0x00, 0xa0, + 0x00, 0x01, 0x01, 0x63, 0x68, 0x69, 0x6c, 0x6c, + 0x69, 0x00, 0x49, 0x74, 0x27, 0x73, 0x20, 0x72, + 0x65, 0x64, 0x2c, 0x20, 0x69, 0x74, 0x27, 0x73, + 0x20, 0x68, 0x6f, 0x74, 0x2c, 0x20, 0x69, 0x74, + 0x27, 0x73, 0x20, 0x2e, 0x2e, 0x2e, 0x43, 0x48, + 0x49, 0x4c, 0x4c, 0x49, 0x21, 0x00, 0x00, 0x08, + 0xd5, 0x00, 0x77, 0x00, 0xe6, 0x00, 0x7e, 0x00, + 0xdd, 0x00, 0xa0, 0x00, 0xdd, 0x00, 0xa0, 0x00, + 0x01, 0x00, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x20, + 0x77, 0x68, 0x65, 0x72, 0x65, 0x20, 0x63, 0x68, + 0x69, 0x6c, 0x6c, 0x69, 0x20, 0x62, 0x6f, 0x74, + 0x74, 0x6c, 0x65, 0x20, 0x73, 0x74, 0x6f, 0x6f, + 0x64, 0x00, 0x49, 0x74, 0x27, 0x73, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x70, 0x6c, 0x61, 0x63, 0x65, + 0x20, 0x77, 0x68, 0x65, 0x72, 0x65, 0x2e, 0x2e, + 0x2e, 0x20, 0x65, 0x74, 0x63, 0x2e, 0x00, 0x00, + 0x09, 0x23, 0x01, 0x77, 0x00, 0x2f, 0x01, 0x7e, + 0x00, 0x28, 0x01, 0xa0, 0x00, 0x28, 0x01, 0xa0, + 0x00, 0x01, 0x01, 0x70, 0x61, 0x73, 0x74, 0x72, + 0x79, 0x20, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, + 0x00, 0x4d, 0x65, 0x6e, 0x20, 0x75, 0x73, 0x65, + 0x20, 0x67, 0x75, 0x6e, 0x73, 0x2e, 0x20, 0x57, + 0x6f, 0x6d, 0x65, 0x6e, 0x20, 0x75, 0x73, 0x65, + 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x00, 0x31, + 0x20, 0x3a, 0x20, 0x30, 0x20, 0x66, 0x6f, 0x72, + 0x20, 0x77, 0x6f, 0x6d, 0x65, 0x6e, 0x2e, 0x00, + 0x00, 0x0a, 0x59, 0x00, 0x70, 0x00, 0x6d, 0x00, + 0x7d, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x01, 0x01, 0x6d, 0x69, 0x63, 0x72, + 0x6f, 0x77, 0x61, 0x76, 0x65, 0x00, 0x49, 0x74, + 0x27, 0x73, 0x20, 0x61, 0x20, 0x6d, 0x61, 0x67, + 0x69, 0x63, 0x20, 0x74, 0x6f, 0x6f, 0x6c, 0x2e, + 0x20, 0x49, 0x74, 0x20, 0x74, 0x75, 0x72, 0x6e, + 0x73, 0x00, 0x61, 0x6e, 0x79, 0x20, 0x64, 0x69, + 0x73, 0x68, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x20, + 0x72, 0x75, 0x62, 0x62, 0x65, 0x72, 0x2e, 0x00, + 0x00, 0x0b, 0xb4, 0x00, 0x67, 0x00, 0xd5, 0x00, + 0x74, 0x00, 0xc7, 0x00, 0xa2, 0x00, 0xc7, 0x00, + 0xa2, 0x00, 0x01, 0x01, 0x6b, 0x6e, 0x69, 0x76, + 0x65, 0x73, 0x00, 0x41, 0x20, 0x76, 0x65, 0x72, + 0x79, 0x20, 0x72, 0x65, 0x73, 0x70, 0x65, 0x63, + 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x63, 0x6f, + 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x2e, 0x00, 0x00, 0x0c, 0xad, 0x00, 0x82, 0x00, + 0xb8, 0x00, 0x8c, 0x00, 0xc0, 0x00, 0xa0, 0x00, + 0xc0, 0x00, 0xa0, 0x00, 0x01, 0x01, 0x6d, 0x65, + 0x61, 0x74, 0x00, 0x49, 0x74, 0x27, 0x73, 0x20, + 0x69, 0x6e, 0x20, 0x61, 0x20, 0x70, 0x6c, 0x61, + 0x73, 0x74, 0x69, 0x63, 0x20, 0x62, 0x61, 0x67, + 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x68, + 0x61, 0x73, 0x20, 0x66, 0x72, 0x6f, 0x7a, 0x65, + 0x6e, 0x20, 0x74, 0x6f, 0x00, 0x74, 0x68, 0x65, + 0x20, 0x73, 0x68, 0x65, 0x6c, 0x66, 0x2e, 0x00, + 0x00, 0x56, 0xaf, 0x6f, 0xaf, 0x97, 0xaf, 0xaf, + 0xaf, 0xe4, 0xaf, 0x00, 0xb0, 0x29, 0xb0, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x2d, 0x00, 0x23, 0x00, + 0x82, 0x00, 0x34, 0x00, 0x85, 0x00, 0x28, 0x00, + 0x80, 0x00, 0x04, 0x01, 0x64, 0x6f, 0x6f, 0x72, + 0x00, 0x01, 0x02, 0x15, 0x01, 0x7d, 0x00, 0x36, + 0x01, 0x9e, 0x00, 0x25, 0x01, 0xa6, 0x00, 0x25, + 0x01, 0xa6, 0x00, 0x01, 0x01, 0x73, 0x69, 0x6e, + 0x6b, 0x00, 0x49, 0x74, 0x20, 0x68, 0x61, 0x73, + 0x20, 0x61, 0x20, 0x68, 0x6f, 0x6c, 0x65, 0x2e, + 0x00, 0x00, 0x03, 0x1e, 0x01, 0x70, 0x00, 0x2c, + 0x01, 0x7c, 0x00, 0x14, 0x01, 0x9f, 0x00, 0x14, + 0x01, 0x9f, 0x00, 0x02, 0x01, 0x74, 0x61, 0x70, + 0x00, 0x01, 0x04, 0x55, 0x00, 0x2e, 0x00, 0x76, + 0x00, 0x67, 0x00, 0x60, 0x00, 0x8c, 0x00, 0x60, + 0x00, 0x8c, 0x00, 0x01, 0x01, 0x6d, 0x69, 0x72, + 0x72, 0x6f, 0x72, 0x00, 0x49, 0x74, 0x27, 0x73, + 0x20, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x65, 0x64, + 0x20, 0x69, 0x6e, 0x20, 0x6f, 0x6c, 0x64, 0x20, + 0x64, 0x69, 0x72, 0x74, 0x2e, 0x00, 0x00, 0x05, + 0x4a, 0x00, 0x6c, 0x00, 0x74, 0x00, 0x86, 0x00, + 0x62, 0x00, 0x87, 0x00, 0x62, 0x00, 0x87, 0x00, + 0x01, 0x01, 0x63, 0x61, 0x62, 0x69, 0x6e, 0x65, + 0x74, 0x00, 0x01, 0x06, 0x7a, 0x00, 0x82, 0x00, + 0xff, 0x00, 0xa4, 0x00, 0xc0, 0x00, 0xae, 0x00, + 0xc0, 0x00, 0xae, 0x00, 0x01, 0x01, 0x62, 0x61, + 0x74, 0x68, 0x00, 0x57, 0x68, 0x6f, 0x61, 0x21, + 0x20, 0x49, 0x74, 0x27, 0x73, 0x20, 0x62, 0x69, + 0x67, 0x21, 0x00, 0x00, 0x07, 0x34, 0x00, 0x72, + 0x00, 0x3a, 0x00, 0x7d, 0x00, 0x44, 0x00, 0x81, + 0x00, 0x44, 0x00, 0x81, 0x00, 0x04, 0x01, 0x73, + 0x6f, 0x63, 0x6b, 0x00, 0x49, 0x74, 0x20, 0x6d, + 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x4a, + 0x6f, 0x68, 0x6e, 0x20, 0x4e, 0x6f, 0x74, 0x79, + 0x27, 0x73, 0x2e, 0x2e, 0x2e, 0x00, 0x00, 0x76, + 0xb0, 0xb4, 0xb0, 0xf9, 0xb0, 0x3b, 0xb1, 0x84, + 0xb1, 0xe6, 0xb1, 0x2a, 0xb2, 0x7d, 0xb2, 0xc3, + 0xb2, 0xdf, 0xb2, 0x1b, 0xb3, 0x6c, 0xb3, 0x00, + 0x00, 0x01, 0x52, 0x00, 0x8a, 0x00, 0x5e, 0x00, + 0x9b, 0x00, 0x68, 0x00, 0xb5, 0x00, 0x68, 0x00, + 0xb5, 0x00, 0x04, 0x01, 0x63, 0x6f, 0x67, 0x6e, + 0x61, 0x63, 0x00, 0x42, 0x6f, 0x74, 0x74, 0x6c, + 0x65, 0x20, 0x61, 0x73, 0x20, 0x62, 0x69, 0x67, + 0x20, 0x61, 0x73, 0x20, 0x4e, 0x61, 0x70, 0x6f, + 0x6c, 0x65, 0x6f, 0x6e, 0x20, 0x68, 0x69, 0x6d, + 0x73, 0x65, 0x6c, 0x66, 0x2e, 0x00, 0x00, 0x02, + 0x52, 0x00, 0x8a, 0x00, 0x5e, 0x00, 0x96, 0x00, + 0x68, 0x00, 0xb5, 0x00, 0x68, 0x00, 0xb5, 0x00, + 0x04, 0x01, 0x70, 0x69, 0x6e, 0x63, 0x65, 0x72, + 0x73, 0x00, 0x54, 0x68, 0x65, 0x72, 0x65, 0x27, + 0x73, 0x20, 0x61, 0x20, 0x70, 0x61, 0x69, 0x72, + 0x20, 0x6f, 0x66, 0x20, 0x70, 0x69, 0x6e, 0x63, + 0x65, 0x72, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x62, 0x75, 0x63, 0x6b, 0x65, + 0x74, 0x2e, 0x00, 0x00, 0x03, 0x71, 0x00, 0x27, + 0x00, 0x91, 0x00, 0x72, 0x00, 0x84, 0x00, 0xaf, + 0x00, 0x84, 0x00, 0xaf, 0x00, 0x01, 0x01, 0x6d, + 0x61, 0x73, 0x6b, 0x00, 0x54, 0x68, 0x69, 0x73, + 0x20, 0x69, 0x73, 0x20, 0x73, 0x6f, 0x6d, 0x65, + 0x20, 0x6b, 0x69, 0x6e, 0x64, 0x20, 0x6f, 0x66, + 0x20, 0x63, 0x6f, 0x6f, 0x6c, 0x20, 0x70, 0x72, + 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x20, + 0x61, 0x72, 0x74, 0x2e, 0x00, 0x00, 0x04, 0x6c, + 0x00, 0x73, 0x00, 0x9e, 0x00, 0x91, 0x00, 0x87, + 0x00, 0xab, 0x00, 0x87, 0x00, 0xab, 0x00, 0x01, + 0x01, 0x54, 0x56, 0x00, 0x4f, 0x6e, 0x65, 0x20, + 0x6d, 0x6f, 0x72, 0x65, 0x20, 0x74, 0x68, 0x69, + 0x6e, 0x67, 0x20, 0x74, 0x6f, 0x20, 0x6b, 0x69, + 0x6c, 0x6c, 0x20, 0x79, 0x6f, 0x75, 0x72, 0x20, + 0x74, 0x69, 0x6d, 0x65, 0x20, 0x61, 0x6e, 0x64, + 0x00, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, + 0x6c, 0x69, 0x74, 0x79, 0x2e, 0x00, 0x00, 0x05, + 0x78, 0x00, 0x94, 0x00, 0x92, 0x00, 0x9a, 0x00, + 0x84, 0x00, 0xaf, 0x00, 0x84, 0x00, 0xaf, 0x00, + 0x01, 0x01, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x20, + 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x00, 0x49, + 0x74, 0x20, 0x68, 0x61, 0x73, 0x20, 0x6e, 0x6f, + 0x20, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x2c, 0x00, 0x62, 0x75, 0x74, 0x20, 0x74, 0x68, + 0x61, 0x74, 0x20, 0x64, 0x6f, 0x65, 0x73, 0x6e, + 0x27, 0x74, 0x20, 0x6d, 0x61, 0x74, 0x74, 0x65, + 0x72, 0x00, 0x74, 0x6f, 0x20, 0x6d, 0x65, 0x20, + 0x61, 0x6e, 0x79, 0x77, 0x61, 0x79, 0x2e, 0x00, + 0x00, 0x06, 0xb9, 0x00, 0x98, 0x00, 0xcb, 0x00, + 0xa4, 0x00, 0xc1, 0x00, 0xa9, 0x00, 0xc1, 0x00, + 0xa9, 0x00, 0x01, 0x01, 0x6e, 0x65, 0x77, 0x73, + 0x70, 0x61, 0x70, 0x65, 0x72, 0x00, 0x54, 0x68, + 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x73, 0x6f, + 0x6d, 0x65, 0x20, 0x6b, 0x69, 0x6e, 0x64, 0x20, + 0x6f, 0x66, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, + 0x20, 0x6e, 0x65, 0x77, 0x73, 0x70, 0x61, 0x70, + 0x65, 0x72, 0x2e, 0x00, 0x00, 0x07, 0xd2, 0x00, + 0x93, 0x00, 0xe4, 0x00, 0xa0, 0x00, 0xdb, 0x00, + 0xaa, 0x00, 0xdb, 0x00, 0xaa, 0x00, 0x01, 0x01, + 0x68, 0x69, 0x2d, 0x66, 0x69, 0x00, 0x57, 0x68, + 0x61, 0x74, 0x20, 0x61, 0x20, 0x62, 0x61, 0x62, + 0x79, 0x21, 0x20, 0x49, 0x74, 0x20, 0x63, 0x6f, + 0x75, 0x6c, 0x64, 0x20, 0x64, 0x72, 0x69, 0x76, + 0x65, 0x00, 0x61, 0x6c, 0x6c, 0x20, 0x6d, 0x79, + 0x20, 0x6e, 0x65, 0x69, 0x67, 0x68, 0x62, 0x6f, + 0x75, 0x72, 0x73, 0x20, 0x69, 0x6e, 0x73, 0x61, + 0x6e, 0x65, 0x21, 0x2e, 0x2e, 0x2e, 0x00, 0x00, + 0x08, 0xc0, 0x00, 0xa4, 0x00, 0x3f, 0x01, 0xc7, + 0x00, 0xa5, 0x00, 0xc4, 0x00, 0xa5, 0x00, 0xc4, + 0x00, 0x02, 0x01, 0x63, 0x6f, 0x75, 0x63, 0x68, + 0x00, 0x49, 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, + 0x20, 0x6e, 0x65, 0x76, 0x65, 0x72, 0x20, 0x6b, + 0x6e, 0x6f, 0x77, 0x20, 0x77, 0x68, 0x65, 0x72, + 0x65, 0x20, 0x74, 0x6f, 0x20, 0x70, 0x6c, 0x61, + 0x63, 0x65, 0x00, 0x61, 0x20, 0x70, 0x69, 0x6c, + 0x6c, 0x6f, 0x77, 0x2e, 0x00, 0x00, 0x09, 0x00, + 0x00, 0xbe, 0x00, 0xbf, 0x00, 0xc7, 0x00, 0x9c, + 0x00, 0xbd, 0x00, 0x9c, 0x00, 0xc7, 0x00, 0x03, + 0x01, 0x77, 0x61, 0x79, 0x20, 0x6f, 0x75, 0x74, + 0x00, 0x01, 0x0a, 0xad, 0x00, 0x25, 0x00, 0x25, + 0x01, 0x79, 0x00, 0xdb, 0x00, 0xaa, 0x00, 0xdb, + 0x00, 0xaa, 0x00, 0x01, 0x01, 0x70, 0x69, 0x63, + 0x74, 0x75, 0x72, 0x65, 0x00, 0x54, 0x68, 0x61, + 0x74, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x69, 0x73, + 0x20, 0x72, 0x65, 0x61, 0x6c, 0x6c, 0x79, 0x20, + 0x69, 0x6d, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, + 0x69, 0x76, 0x65, 0x2e, 0x00, 0x00, 0x0b, 0xa1, + 0x00, 0xa5, 0x00, 0xac, 0x00, 0xaa, 0x00, 0x98, + 0x00, 0xad, 0x00, 0x98, 0x00, 0xad, 0x00, 0x02, + 0x00, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x20, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, + 0x65, 0x72, 0x00, 0x49, 0x74, 0x20, 0x77, 0x61, + 0x73, 0x20, 0x68, 0x69, 0x64, 0x64, 0x65, 0x6e, + 0x20, 0x62, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e, 0x65, 0x77, + 0x73, 0x70, 0x61, 0x70, 0x65, 0x72, 0x20, 0x70, + 0x61, 0x67, 0x65, 0x73, 0x2e, 0x00, 0x00, 0x0c, + 0x2f, 0x00, 0x4f, 0x00, 0x57, 0x00, 0xaf, 0x00, + 0x68, 0x00, 0xb5, 0x00, 0x64, 0x00, 0xb5, 0x00, + 0x04, 0x00, 0x6f, 0x70, 0x65, 0x6e, 0x20, 0x77, + 0x61, 0x72, 0x64, 0x72, 0x6f, 0x62, 0x65, 0x00, + 0x49, 0x74, 0x27, 0x73, 0x20, 0x61, 0x20, 0x73, + 0x65, 0x63, 0x72, 0x65, 0x74, 0x20, 0x70, 0x61, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x21, 0x00, 0x00, + 0xad, 0xb3, 0xc6, 0xb3, 0xdf, 0xb3, 0x00, 0x00, + 0x01, 0x11, 0x01, 0x72, 0x00, 0x3f, 0x01, 0xc7, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x02, 0x01, 0x64, 0x6f, 0x6f, 0x72, 0x00, + 0x01, 0x02, 0x00, 0x00, 0x73, 0x00, 0x2a, 0x00, + 0xc7, 0x00, 0x2e, 0x00, 0xc1, 0x00, 0x2e, 0x00, + 0xc1, 0x00, 0x04, 0x01, 0x64, 0x6f, 0x6f, 0x72, + 0x00, 0x01, 0x03, 0xf2, 0x00, 0x89, 0x00, 0xf6, + 0x00, 0x8e, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x01, 0x73, 0x77, 0x69, + 0x74, 0x63, 0x68, 0x00, 0x01, 0x00, 0x00, 0x08, + 0xb4, 0x4c, 0xb4, 0x74, 0xb4, 0x8d, 0xb4, 0xa6, + 0xb4, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0xa0, 0x00, + 0x78, 0x00, 0xa0, 0x00, 0x04, 0x01, 0x4a, 0x6f, + 0x68, 0x6e, 0x20, 0x4e, 0x6f, 0x74, 0x79, 0x00, + 0x48, 0x65, 0x27, 0x73, 0x20, 0x6f, 0x62, 0x73, + 0x65, 0x73, 0x73, 0x65, 0x64, 0x20, 0x77, 0x69, + 0x74, 0x68, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x69, + 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, + 0x6f, 0x6e, 0x65, 0x79, 0x2e, 0x00, 0x00, 0x02, + 0xcf, 0x00, 0x6c, 0x00, 0x3f, 0x01, 0xc1, 0x00, + 0xc1, 0x00, 0xae, 0x00, 0xc1, 0x00, 0xae, 0x00, + 0x02, 0x01, 0x74, 0x6f, 0x6e, 0x73, 0x20, 0x6f, + 0x66, 0x20, 0x67, 0x6f, 0x6c, 0x64, 0x00, 0x28, + 0x67, 0x75, 0x6c, 0x70, 0x29, 0x00, 0x00, 0x03, + 0x06, 0x00, 0x5c, 0x00, 0x24, 0x00, 0x96, 0x00, + 0x58, 0x00, 0xad, 0x00, 0x58, 0x00, 0xad, 0x00, + 0x04, 0x01, 0x73, 0x61, 0x66, 0x65, 0x00, 0x01, + 0x04, 0x72, 0x00, 0x5e, 0x00, 0xb0, 0x00, 0x8d, + 0x00, 0x8c, 0x00, 0x9e, 0x00, 0x8c, 0x00, 0x9e, + 0x00, 0x01, 0x01, 0x73, 0x61, 0x66, 0x65, 0x00, + 0x01, 0x05, 0x0b, 0x01, 0x36, 0x00, 0x1c, 0x01, + 0x47, 0x00, 0xc1, 0x00, 0xae, 0x00, 0xc1, 0x00, + 0xae, 0x00, 0x02, 0x01, 0x63, 0x61, 0x6d, 0x65, + 0x72, 0x61, 0x00, 0x49, 0x74, 0x20, 0x6d, 0x75, + 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x61, 0x20, + 0x70, 0x61, 0x72, 0x74, 0x20, 0x6f, 0x66, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x63, 0x75, + 0x72, 0x69, 0x74, 0x79, 0x20, 0x73, 0x79, 0x73, + 0x74, 0x65, 0x6d, 0x2e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x49, 0xb5, 0x4a, 0xb5, 0x4d, 0xb5, 0x4f, 0xb5, + 0x53, 0xb5, 0x55, 0xb5, 0x5a, 0xb5, 0x5d, 0xb5, + 0x61, 0xb5, 0x62, 0xb5, 0x63, 0xb5, 0x68, 0xb5, + 0x6c, 0xb5, 0x6f, 0xb5, 0x71, 0xb5, 0x75, 0xb5, + 0x7a, 0xb5, 0x7c, 0xb5, 0x7f, 0xb5, 0x82, 0xb5, + 0x88, 0xb5, 0x8b, 0xb5, 0x8d, 0xb5, 0x8f, 0xb5, + 0x93, 0xb5, 0x96, 0xb5, 0x99, 0xb5, 0xa0, 0xb5, + 0xa1, 0xb5, 0xa2, 0xb5, 0xaa, 0xb5, 0xac, 0xb5, + 0xb0, 0xb5, 0xb6, 0xb5, 0xb8, 0xb5, 0xbd, 0xb5, + 0xbe, 0xb5, 0xc3, 0xb5, 0xc4, 0xb5, 0xc6, 0xb5, + 0xc8, 0xb5, 0xcb, 0xb5, 0xff, 0x5f, 0x63, 0xff, + 0x00, 0xff, 0x5d, 0x00, 0x62, 0xff, 0x00, 0xff, + 0x54, 0x55, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, + 0x65, 0x69, 0x00, 0xff, 0xff, 0xff, 0x00, 0x11, + 0x00, 0x00, 0xff, 0x0e, 0x00, 0x12, 0xff, 0x29, + 0x00, 0xff, 0x00, 0xff, 0x14, 0x15, 0x2b, 0xff, + 0x00, 0x0a, 0x0b, 0x0c, 0xff, 0x25, 0xff, 0x16, + 0x00, 0xff, 0x19, 0x27, 0xff, 0x02, 0x03, 0x00, + 0x00, 0x00, 0xff, 0x1b, 0x00, 0xff, 0x07, 0xff, + 0x0d, 0xff, 0x1f, 0x00, 0x30, 0xff, 0x1a, 0x2c, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x13, 0x22, 0x00, + 0x26, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, + 0x00, 0x00, 0xff, 0x3e, 0x3f, 0x40, 0x41, 0x00, + 0xff, 0x52, 0xff, 0x34, 0x3c, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x6e, 0x6f, + 0xff, 0x22, 0xb6, 0x24, 0xb6, 0x2e, 0xb6, 0x3a, + 0xb6, 0x48, 0xb6, 0x56, 0xb6, 0x6a, 0xb6, 0x7a, + 0xb6, 0x8c, 0xb6, 0x9a, 0xb6, 0xa2, 0xb6, 0xc0, + 0xb6, 0xd2, 0xb6, 0xe0, 0xb6, 0xea, 0xb6, 0xfa, + 0xb6, 0x00, 0xb7, 0x02, 0xb7, 0x10, 0xb7, 0x2e, + 0xb7, 0x4e, 0xb7, 0x66, 0xb7, 0x80, 0xb7, 0xa0, + 0xb7, 0xb2, 0xb7, 0xc0, 0xb7, 0xcc, 0xb7, 0xec, + 0xb7, 0xf4, 0xb7, 0x02, 0xb8, 0x22, 0xb8, 0x34, + 0xb8, 0x44, 0xb8, 0x5c, 0xb8, 0x6a, 0xb8, 0x82, + 0xb8, 0x88, 0xb8, 0x8a, 0xb8, 0x94, 0xb8, 0x96, + 0xb8, 0x98, 0xb8, 0x9a, 0xb8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, + 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x78, 0x78, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xa9, 0x78, 0x00, 0x00, 0xbb, 0x78, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xcd, 0x78, 0xce, 0x78, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xe0, 0x78, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe7, + 0x78, 0xee, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xf5, 0x78, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x79, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, + 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x79, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x79, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x62, 0x79, 0x66, 0x79, 0x00, + 0x00, 0x75, 0x79, 0x87, 0x79, 0x96, 0x79, 0xa5, + 0x79, 0xb4, 0x79, 0xc3, 0x79, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xd2, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xe4, 0x79, 0xeb, 0x79, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, + 0x79, 0x0f, 0x7a, 0x49, 0x7a, 0x5b, 0x7a, 0x6d, + 0x7a, 0x7f, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xb9, 0x7a, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xd0, 0x7a, 0xd7, 0x7a, 0x00, 0x00, 0x00, + 0x00, 0xde, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x7a, 0x00, 0x00, 0xf7, 0x7a, 0x00, + 0x00, 0x09, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, + 0xb8, 0xf2, 0xb8, 0xfc, 0xb8, 0x08, 0xb9, 0x16, + 0xb9, 0x24, 0xb9, 0x38, 0xb9, 0x50, 0xb9, 0x62, + 0xb9, 0x70, 0xb9, 0x78, 0xb9, 0x96, 0xb9, 0xa8, + 0xb9, 0xb6, 0xb9, 0xc0, 0xb9, 0xd0, 0xb9, 0xd6, + 0xb9, 0xd8, 0xb9, 0xe6, 0xb9, 0x04, 0xba, 0x24, + 0xba, 0x3c, 0xba, 0x56, 0xba, 0x76, 0xba, 0x88, + 0xba, 0x96, 0xba, 0xa2, 0xba, 0xc2, 0xba, 0xca, + 0xba, 0xd8, 0xba, 0xf8, 0xba, 0x0a, 0xbb, 0x1a, + 0xbb, 0x32, 0xbb, 0x40, 0xbb, 0x58, 0xbb, 0x5e, + 0xbb, 0x60, 0xbb, 0x6a, 0xbb, 0x6c, 0xbb, 0x6e, + 0xbb, 0x70, 0xbb, 0x00, 0x00, 0xed, 0x3f, 0x07, + 0x40, 0x21, 0x40, 0x48, 0x40, 0x4f, 0x40, 0x56, + 0x40, 0x60, 0x40, 0x7a, 0x40, 0x94, 0x40, 0x95, + 0x41, 0x9c, 0x41, 0xc3, 0x41, 0xca, 0x41, 0xce, + 0x41, 0x2c, 0x42, 0x33, 0x42, 0x4d, 0x42, 0x67, + 0x42, 0x3a, 0x43, 0x54, 0x43, 0x6e, 0x43, 0x88, + 0x43, 0xb5, 0x43, 0x82, 0x44, 0xcb, 0x44, 0xfc, + 0x44, 0x32, 0x45, 0x39, 0x45, 0x62, 0x46, 0xaf, + 0x46, 0x05, 0x47, 0x94, 0x47, 0xbc, 0x47, 0xdb, + 0x47, 0x36, 0x48, 0x3a, 0x48, 0x44, 0x48, 0x71, + 0x48, 0x7e, 0x48, 0x85, 0x48, 0x8c, 0x48, 0x93, + 0x48, 0xd4, 0x48, 0xdb, 0x48, 0xe2, 0x48, 0xe6, + 0x48, 0xea, 0x48, 0x11, 0x49, 0x18, 0x49, 0x64, + 0x4a, 0x8c, 0x4a, 0xed, 0x4a, 0xf4, 0x4a, 0x23, + 0x4b, 0x27, 0x4b, 0x2e, 0x4b, 0x35, 0x4b, 0xf5, + 0x4b, 0x18, 0x4c, 0x1c, 0x4c, 0x29, 0x4c, 0x30, + 0x4c, 0x37, 0x4c, 0x3e, 0x4c, 0x70, 0x4c, 0xa5, + 0x4c, 0xac, 0x4c, 0xf1, 0x4c, 0x56, 0x4d, 0x7d, + 0x4d, 0x81, 0x4d, 0x85, 0x4d, 0x89, 0x4d, 0x90, + 0x4d, 0x94, 0x4d, 0x47, 0x4e, 0x61, 0x4e, 0x85, + 0x4e, 0x9f, 0x4e, 0xb9, 0x4e, 0xe1, 0x4e, 0xe5, + 0x4e, 0x13, 0x4f, 0x14, 0x4f, 0x25, 0x4f, 0x32, + 0x4f, 0x0d, 0x50, 0x5f, 0x50, 0x66, 0x50, 0x80, + 0x50, 0x9a, 0x50, 0xf6, 0x50, 0xfd, 0x50, 0x04, + 0x51, 0xc8, 0x51, 0xcf, 0x51, 0xe9, 0x51, 0xf0, + 0x51, 0x17, 0x52, 0x2c, 0x52, 0x33, 0x52, 0x37, + 0x52, 0x20, 0x53, 0x3a, 0x53, 0x41, 0x53, 0x48, + 0x53, 0xa1, 0x53, 0x03, 0x54, 0x0a, 0x54, 0x24, + 0x54, 0x3e, 0x54, 0x58, 0x54, 0xb3, 0x54, 0x02, + 0x55, 0x00, 0x00, 0x47, 0x55, 0x61, 0x55, 0xa1, + 0x55, 0xa8, 0x55, 0x34, 0x56, 0x3b, 0x56, 0x63, + 0x56, 0x74, 0x56, 0x8e, 0x56, 0x95, 0x56, 0x9c, + 0x56, 0xb3, 0x56, 0xb7, 0x56, 0xd6, 0x56, 0xdd, + 0x56, 0xe4, 0x56, 0xeb, 0x56, 0xf2, 0x56, 0xf6, + 0x56, 0x21, 0x57, 0x28, 0x57, 0x4f, 0x57, 0x56, + 0x57, 0x93, 0x57, 0xfa, 0x57, 0x01, 0x58, 0x05, + 0x58, 0x32, 0x58, 0x3f, 0x58, 0x46, 0x58, 0x4d, + 0x58, 0x54, 0x58, 0x6e, 0x58, 0x88, 0x58, 0xa2, + 0x58, 0xb7, 0x58, 0xdf, 0x58, 0x03, 0x59, 0x0a, + 0x59, 0x24, 0x59, 0x3e, 0x59, 0x78, 0x59, 0x7f, + 0x59, 0x86, 0x59, 0x8d, 0x59, 0x94, 0x59, 0x8b, + 0x5a, 0x3a, 0x5b, 0x44, 0x5b, 0x59, 0x5b, 0xe1, + 0x5b, 0xee, 0x5b, 0x0d, 0x5c, 0x72, 0x5c, 0x79, + 0x5c, 0x80, 0x5c, 0x84, 0x5c, 0xdb, 0x5c, 0xe2, + 0x5c, 0xe9, 0x5c, 0xf0, 0x5c, 0x1d, 0x5d, 0x24, + 0x5d, 0x88, 0x5d, 0x4d, 0x5e, 0x73, 0x5f, 0x9a, + 0x5f, 0xa1, 0x5f, 0xa8, 0x5f, 0xaf, 0x5f, 0xb6, + 0x5f, 0xba, 0x5f, 0xe5, 0x5f, 0xec, 0x5f, 0xf3, + 0x5f, 0x3a, 0x60, 0x3e, 0x60, 0x74, 0x60, 0x78, + 0x60, 0x7f, 0x60, 0x83, 0x60, 0x2b, 0x61, 0x76, + 0x61, 0xe9, 0x61, 0x05, 0x62, 0x17, 0x62, 0x29, + 0x62, 0xc1, 0x62, 0xd0, 0x62, 0x4a, 0x63, 0x51, + 0x63, 0x7f, 0x63, 0x99, 0x63, 0xa0, 0x63, 0xa7, + 0x63, 0xae, 0x63, 0xb5, 0x63, 0xbc, 0x63, 0xdc, + 0x63, 0xe3, 0x63, 0xea, 0x63, 0x11, 0x64, 0x6e, + 0x64, 0x75, 0x64, 0x79, 0x64, 0x80, 0x64, 0xc4, + 0x64, 0x07, 0x65, 0x19, 0x65, 0x41, 0x65, 0x5b, + 0x65, 0x92, 0x65, 0xc3, 0x65, 0x35, 0x66, 0x3c, + 0x66, 0x63, 0x66, 0x6a, 0x66, 0x71, 0x66, 0x78, + 0x66, 0x7c, 0x66, 0xa9, 0x66, 0xb5, 0x66, 0xe2, + 0x66, 0x0f, 0x67, 0x16, 0x67, 0x1d, 0x67, 0x72, + 0x67, 0x8c, 0x67, 0xa6, 0x67, 0xfa, 0x67, 0xfe, + 0x67, 0x05, 0x68, 0x7a, 0x68, 0x11, 0x69, 0x18, + 0x69, 0x54, 0x69, 0x5b, 0x69, 0x62, 0x69, 0xb8, + 0x69, 0x1b, 0x6a, 0x73, 0x6a, 0xcb, 0x6a, 0x2e, + 0x6b, 0xa6, 0x6b, 0xad, 0x6b, 0xda, 0x6b, 0xe1, + 0x6b, 0x1c, 0x6c, 0x20, 0x6c, 0x24, 0x6c, 0x2b, + 0x6c, 0x45, 0x6c, 0x7c, 0x6c, 0x83, 0x6c, 0x9d, + 0x6c, 0xc4, 0x6c, 0x20, 0x6f, 0x32, 0x6f, 0x4d, + 0x6f, 0x75, 0x6f, 0x96, 0x70, 0xbb, 0x70, 0xc8, + 0x70, 0xef, 0x70, 0xf9, 0x70, 0x2c, 0x71, 0xae, + 0x71, 0xeb, 0x71, 0x18, 0x72, 0x1c, 0x72, 0x44, + 0x72, 0x4e, 0x72, 0x55, 0x72, 0x91, 0x72, 0xbe, + 0x72, 0xc2, 0x72, 0x05, 0x73, 0x09, 0x73, 0x28, + 0x73, 0x2f, 0x73, 0x36, 0x73, 0x81, 0x73, 0x9c, + 0x73, 0xa3, 0x73, 0x01, 0x74, 0x08, 0x74, 0x6f, + 0x74, 0x76, 0x74, 0xb3, 0x74, 0xcd, 0x74, 0xd1, + 0x74, 0xf9, 0x74, 0x13, 0x75, 0xd5, 0x77, 0x02, + 0x78, 0x00, 0x00, 0x3d, 0x78, 0x4a, 0x78, 0x51, + 0x78, 0x58, 0x78, 0x5f, 0x78, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xfa, 0x98, + 0x04, 0x10, 0x99, 0x29, 0x21, 0x99, 0x25, 0xca, + 0x9a, 0xff, 0x66, 0x9c, 0x33, 0x6d, 0x9c, 0x48, + 0x79, 0x9c, 0xde, 0xbb, 0xdf, 0xbb, 0xfb, 0xbb, + 0x29, 0xbc, 0x33, 0xbc, 0x34, 0xbc, 0x3e, 0xbc, + 0x5a, 0xbc, 0x76, 0xbc, 0x77, 0xbc, 0x8a, 0xbc, + 0x94, 0xbc, 0xb9, 0xbc, 0xcc, 0xbc, 0xd6, 0xbc, + 0x04, 0xbd, 0x05, 0xbd, 0x06, 0xbd, 0x19, 0xbd, + 0x2c, 0xbd, 0x36, 0xbd, 0x49, 0xbd, 0x4a, 0xbd, + 0x8a, 0xbd, 0x8b, 0xbd, 0xb9, 0xbd, 0xc3, 0xbd, + 0xe8, 0xbd, 0xe9, 0xbd, 0xea, 0xbd, 0xeb, 0xbd, + 0xf5, 0xbd, 0x1a, 0xbe, 0x51, 0xbe, 0x76, 0xbe, + 0xa4, 0xbe, 0xa5, 0xbe, 0xa6, 0xbe, 0xb0, 0xbe, + 0xb1, 0xbe, 0xb2, 0xbe, 0xb3, 0xbe, 0x00, 0x00, + 0x00, 0x00, 0x4f, 0x04, 0x02, 0x00, 0x01, 0x5f, + 0x00, 0x26, 0x7b, 0x51, 0x03, 0x01, 0x10, 0x01, + 0x64, 0x00, 0x89, 0x7b, 0x4f, 0x03, 0x02, 0x19, + 0x01, 0x5f, 0x00, 0xf6, 0x7b, 0x00, 0x5b, 0x04, + 0x01, 0xd1, 0x00, 0x9a, 0x00, 0xfd, 0x7b, 0x5b, + 0x01, 0x04, 0xf0, 0x00, 0xa3, 0x00, 0xc9, 0x7c, + 0x57, 0x01, 0x04, 0xf0, 0x00, 0xa3, 0x00, 0xd0, + 0x7c, 0x57, 0x04, 0x04, 0xd1, 0x00, 0x9a, 0x00, + 0xd7, 0x7c, 0x55, 0x05, 0x01, 0x73, 0x00, 0xc2, + 0x00, 0xde, 0x7c, 0x00, 0x50, 0x02, 0x04, 0xc7, + 0x00, 0xb3, 0x00, 0xe5, 0x7c, 0x00, 0x00, 0x4d, + 0x07, 0x02, 0x23, 0x01, 0xb9, 0x00, 0x1a, 0x7d, + 0x00, 0x4e, 0x01, 0x02, 0x52, 0x00, 0xb8, 0x00, + 0x02, 0x7e, 0x53, 0x01, 0x02, 0x49, 0x00, 0xbc, + 0x00, 0x4f, 0x7e, 0x59, 0x07, 0x01, 0xce, 0x00, + 0xc2, 0x00, 0x23, 0x7f, 0x00, 0x5c, 0x05, 0x04, + 0x6e, 0x00, 0x98, 0x00, 0xbd, 0x7f, 0x56, 0x01, + 0x01, 0xc0, 0x00, 0x98, 0x00, 0x47, 0x80, 0x5a, + 0x06, 0x02, 0x04, 0x01, 0xc2, 0x00, 0x84, 0x80, + 0x00, 0x00, 0x4b, 0x02, 0x01, 0x7a, 0x00, 0x97, + 0x00, 0x8b, 0x80, 0x52, 0x02, 0x01, 0x8b, 0x00, + 0x9c, 0x00, 0xc3, 0x80, 0x00, 0x32, 0x01, 0x02, + 0xbd, 0x00, 0x9f, 0x00, 0x17, 0x81, 0x00, 0x0e, + 0x03, 0x02, 0xcb, 0x00, 0xab, 0x00, 0x74, 0x81, + 0x22, 0x02, 0x02, 0x94, 0x00, 0xbd, 0x00, 0xc2, + 0x81, 0x2b, 0x06, 0x01, 0x5a, 0x00, 0xbc, 0x00, + 0x3d, 0x82, 0x0d, 0x03, 0x02, 0xcb, 0x00, 0xab, + 0x00, 0x0b, 0x83, 0x00, 0x1b, 0x06, 0x04, 0x67, + 0x00, 0xc0, 0x00, 0x12, 0x83, 0x2b, 0x04, 0x01, + 0xc5, 0x00, 0xb7, 0x00, 0x98, 0x83, 0x00, 0x20, + 0x01, 0x02, 0x6e, 0x00, 0xb9, 0x00, 0x9f, 0x83, + 0x00, 0x09, 0x03, 0x02, 0x2c, 0x00, 0xa8, 0x00, + 0xc7, 0x84, 0x2c, 0x04, 0x03, 0xec, 0x00, 0xb3, + 0x00, 0x38, 0x85, 0x08, 0x03, 0x02, 0x2c, 0x00, + 0xa8, 0x00, 0xd6, 0x85, 0x0f, 0x03, 0x02, 0x2c, + 0x00, 0xa8, 0x00, 0xdd, 0x85, 0x2e, 0x04, 0x03, + 0xec, 0x00, 0xb3, 0x00, 0xe4, 0x85, 0x00, 0x00, + 0x00, 0x19, 0x07, 0x04, 0xa2, 0x00, 0xc2, 0x00, + 0xeb, 0x85, 0x15, 0x07, 0x04, 0xa2, 0x00, 0xc2, + 0x00, 0x2c, 0x86, 0x00, 0x13, 0x05, 0x01, 0x8b, + 0x00, 0xa5, 0x00, 0x3d, 0x86, 0x0c, 0x09, 0x02, + 0x0e, 0x01, 0xb6, 0x00, 0x65, 0x86, 0x00, 0x05, + 0x03, 0x04, 0x57, 0x00, 0xb9, 0x00, 0xa9, 0x86, + 0x00, 0x07, 0x04, 0x04, 0x30, 0x00, 0xbe, 0x00, + 0x82, 0x8b, 0x24, 0x0a, 0x02, 0xcf, 0x00, 0xc2, + 0x00, 0xfc, 0x8b, 0x00, 0x00, 0x0a, 0x01, 0x04, + 0x3d, 0x00, 0xbb, 0x00, 0xc9, 0x88, 0x0b, 0x01, + 0x04, 0x3d, 0x00, 0xbb, 0x00, 0x18, 0x89, 0x0a, + 0x02, 0x04, 0x02, 0x01, 0xb5, 0x00, 0x2d, 0x89, + 0x0b, 0x02, 0x04, 0x02, 0x01, 0xb5, 0x00, 0xb7, + 0x89, 0x17, 0x02, 0x04, 0x02, 0x01, 0xb5, 0x00, + 0xcc, 0x89, 0x1a, 0x09, 0x03, 0x9e, 0x00, 0xc6, + 0x00, 0x22, 0x8a, 0x33, 0x02, 0x04, 0x02, 0x01, + 0xb5, 0x00, 0x6f, 0x8a, 0x00, 0x00, 0x23, 0x05, + 0x04, 0x0c, 0x01, 0x91, 0x00, 0x6e, 0x8c, 0x2e, + 0x02, 0x04, 0xcd, 0x00, 0x99, 0x00, 0xc8, 0x8c, + 0x2c, 0x02, 0x04, 0xea, 0x00, 0x98, 0x00, 0x42, + 0x8d, 0x0d, 0x02, 0x04, 0xea, 0x00, 0x98, 0x00, + 0x49, 0x8d, 0x0e, 0x02, 0x04, 0xea, 0x00, 0x98, + 0x00, 0x50, 0x8d, 0x00, 0x30, 0x05, 0x01, 0x9f, + 0x00, 0xa6, 0x00, 0x57, 0x8d, 0x00, 0x02, 0x02, + 0x01, 0xe6, 0x00, 0xa4, 0x00, 0x1d, 0x8f, 0x02, + 0x03, 0x01, 0xe6, 0x00, 0xa4, 0x00, 0x1d, 0x8f, + 0x2d, 0x04, 0x04, 0xec, 0x00, 0x8e, 0x00, 0xc8, + 0x8f, 0x2f, 0x0c, 0x04, 0xec, 0x00, 0x8e, 0x00, + 0x54, 0x90, 0x00, 0x00, 0x00, 0x00, 0x49, 0x02, + 0x02, 0x2c, 0x01, 0xb2, 0x00, 0xbc, 0x90, 0x00, + 0x34, 0x01, 0x01, 0x3d, 0x00, 0xaf, 0x00, 0xf5, + 0x90, 0x43, 0x01, 0x01, 0x3d, 0x00, 0xaf, 0x00, + 0xfc, 0x90, 0x47, 0x01, 0x01, 0x3d, 0x00, 0xaf, + 0x00, 0xcb, 0x91, 0x3d, 0x01, 0x01, 0x3d, 0x00, + 0xaf, 0x00, 0x09, 0x92, 0x00, 0x38, 0x08, 0x01, + 0xdd, 0x00, 0xa0, 0x00, 0x47, 0x92, 0x40, 0x08, + 0x01, 0xdd, 0x00, 0xa0, 0x00, 0x4e, 0x92, 0x3f, + 0x06, 0x01, 0x8d, 0x00, 0x9f, 0x00, 0x3d, 0x93, + 0x37, 0x03, 0x04, 0x3f, 0x00, 0xa8, 0x00, 0xaf, + 0x93, 0x44, 0x0c, 0x01, 0xc0, 0x00, 0xa0, 0x00, + 0xd5, 0x93, 0x45, 0x02, 0x04, 0x51, 0x00, 0xa0, + 0x00, 0x49, 0x94, 0x00, 0x3c, 0x02, 0x02, 0x14, + 0x01, 0x9f, 0x00, 0x72, 0x94, 0x3e, 0x02, 0x02, + 0x14, 0x01, 0x9f, 0x00, 0x9b, 0x94, 0x3a, 0x07, + 0x04, 0x44, 0x00, 0x81, 0x00, 0xd4, 0x94, 0x3b, + 0x02, 0x02, 0x14, 0x01, 0x9f, 0x00, 0x1b, 0x95, + 0x00, 0x39, 0x05, 0x01, 0x91, 0x00, 0xc5, 0x00, + 0x37, 0x95, 0x36, 0x05, 0x01, 0x81, 0x00, 0xad, + 0x00, 0xc8, 0x95, 0x35, 0x04, 0x01, 0x74, 0x00, + 0xbf, 0x00, 0xeb, 0x95, 0x43, 0x04, 0x01, 0x74, + 0x00, 0xbf, 0x00, 0x2f, 0x96, 0x34, 0x04, 0x01, + 0x74, 0x00, 0xbf, 0x00, 0x6c, 0x96, 0x00, 0x00, + 0x00, 0x4a, 0x01, 0x01, 0x3b, 0x00, 0xa3, 0x00, + 0x73, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, + 0x6f, 0x77, 0x2c, 0x20, 0x6e, 0x6f, 0x77, 0x20, + 0x69, 0x74, 0x20, 0x6c, 0x6f, 0x6f, 0x6b, 0x73, + 0x20, 0x6c, 0x69, 0x6b, 0x65, 0x20, 0x69, 0x74, + 0x20, 0x63, 0x61, 0x6d, 0x65, 0x20, 0x73, 0x74, + 0x72, 0x61, 0x69, 0x67, 0x68, 0x74, 0x00, 0x66, + 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x21, 0x00, 0x00, + 0x54, 0x79, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x72, 0x69, 0x62, 0x62, 0x6f, 0x6e, + 0x20, 0x61, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x72, 0x61, 0x6b, 0x65, + 0x20, 0x6e, 0x61, 0x72, 0x72, 0x6f, 0x77, 0x65, + 0x64, 0x00, 0x74, 0x68, 0x65, 0x20, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x20, 0x62, 0x65, 0x74, 0x77, + 0x65, 0x65, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x74, 0x65, 0x65, 0x74, 0x68, 0x21, 0x00, 0x00, + 0x57, 0x69, 0x74, 0x68, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x68, 0x65, 0x6c, 0x70, 0x20, 0x6f, 0x66, + 0x20, 0x73, 0x75, 0x70, 0x65, 0x72, 0x20, 0x67, + 0x6c, 0x75, 0x65, 0x20, 0x49, 0x20, 0x6d, 0x61, + 0x64, 0x65, 0x2e, 0x2e, 0x2e, 0x00, 0x73, 0x6f, + 0x6d, 0x65, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x2e, + 0x2e, 0x2e, 0x00, 0x00, 0x55, 0x73, 0x69, 0x6e, + 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x75, + 0x70, 0x65, 0x72, 0x20, 0x67, 0x6c, 0x75, 0x65, + 0x20, 0x6f, 0x6e, 0x63, 0x65, 0x20, 0x61, 0x67, + 0x61, 0x69, 0x6e, 0x2e, 0x2e, 0x2e, 0x00, 0x00, + 0x54, 0x68, 0x65, 0x20, 0x77, 0x68, 0x69, 0x73, + 0x6b, 0x79, 0x20, 0x69, 0x73, 0x20, 0x73, 0x74, + 0x72, 0x6f, 0x6e, 0x67, 0x20, 0x65, 0x6e, 0x6f, + 0x75, 0x67, 0x68, 0x20, 0x74, 0x6f, 0x20, 0x75, + 0x73, 0x65, 0x20, 0x61, 0x73, 0x00, 0x66, 0x75, + 0x65, 0x6c, 0x2c, 0x20, 0x62, 0x75, 0x74, 0x20, + 0x49, 0x20, 0x77, 0x6f, 0x6e, 0x64, 0x65, 0x72, + 0x20, 0x69, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x63, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x61, 0x77, + 0x00, 0x63, 0x61, 0x6e, 0x20, 0x74, 0x61, 0x6b, + 0x65, 0x20, 0x69, 0x74, 0x2e, 0x2e, 0x2e, 0x00, + 0x00, 0x4f, 0x6e, 0x63, 0x65, 0x20, 0x61, 0x67, + 0x61, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x73, 0x75, 0x70, 0x65, 0x72, 0x2d, 0x67, 0x6c, + 0x75, 0x65, 0x20, 0x63, 0x6f, 0x6d, 0x65, 0x73, + 0x20, 0x69, 0x6e, 0x20, 0x68, 0x61, 0x6e, 0x64, + 0x79, 0x2e, 0x2e, 0x2e, 0x00, 0x00, 0x54, 0x68, + 0x65, 0x20, 0x73, 0x6f, 0x6f, 0x74, 0x20, 0x67, + 0x69, 0x76, 0x65, 0x73, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x70, 0x6f, 0x74, 0x61, 0x74, 0x6f, 0x65, + 0x20, 0x61, 0x20, 0x62, 0x72, 0x61, 0x6e, 0x64, + 0x20, 0x6e, 0x65, 0x77, 0x20, 0x6c, 0x6f, 0x6f, + 0x6b, 0x2e, 0x2e, 0x2e, 0x00, 0x00, 0x4e, 0x6f, + 0x77, 0x20, 0x49, 0x27, 0x6d, 0x20, 0x72, 0x65, + 0x61, 0x64, 0x79, 0x20, 0x74, 0x6f, 0x20, 0x63, + 0x6f, 0x6e, 0x71, 0x75, 0x65, 0x72, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x6c, 0x61, 0x6b, 0x65, 0x21, + 0x00, 0x00, 0x49, 0x74, 0x20, 0x6d, 0x61, 0x6b, + 0x65, 0x73, 0x20, 0x6d, 0x65, 0x20, 0x66, 0x65, + 0x65, 0x6c, 0x20, 0x6c, 0x69, 0x6b, 0x65, 0x20, + 0x61, 0x6e, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x00, + 0x77, 0x61, 0x6e, 0x6e, 0x61, 0x2d, 0x62, 0x65, + 0x20, 0x63, 0x6c, 0x69, 0x66, 0x66, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x72, 0x2e, 0x00, 0x00, 0x49, + 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x6e, 0x27, + 0x74, 0x20, 0x69, 0x6d, 0x70, 0x72, 0x65, 0x73, + 0x73, 0x20, 0x61, 0x6e, 0x79, 0x6f, 0x6e, 0x65, + 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x73, 0x75, + 0x63, 0x68, 0x20, 0x63, 0x61, 0x6e, 0x64, 0x79, + 0x2e, 0x00, 0x00, 0x49, 0x74, 0x27, 0x73, 0x20, + 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x20, 0x65, + 0x6e, 0x6f, 0x75, 0x67, 0x68, 0x2e, 0x00, 0x00, + 0x47, 0x72, 0x65, 0x61, 0x74, 0x20, 0x69, 0x64, + 0x65, 0x61, 0x21, 0x20, 0x42, 0x75, 0x74, 0x2c, + 0x20, 0x79, 0x6f, 0x75, 0x20, 0x73, 0x65, 0x65, + 0x2c, 0x20, 0x65, 0x63, 0x6f, 0x6d, 0x61, 0x6e, + 0x69, 0x61, 0x63, 0x73, 0x20, 0x6d, 0x69, 0x67, + 0x68, 0x74, 0x00, 0x62, 0x65, 0x20, 0x77, 0x61, + 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x2e, 0x2e, + 0x2e, 0x00, 0x00, 0x49, 0x74, 0x20, 0x77, 0x6f, + 0x6e, 0x27, 0x74, 0x20, 0x6c, 0x6f, 0x6f, 0x6b, + 0x20, 0x61, 0x6e, 0x79, 0x20, 0x62, 0x65, 0x74, + 0x74, 0x65, 0x72, 0x20, 0x69, 0x6e, 0x20, 0x61, + 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, + 0x2e, 0x00, 0x00, 0x54, 0x68, 0x65, 0x20, 0x63, + 0x61, 0x6b, 0x65, 0x20, 0x69, 0x73, 0x20, 0x74, + 0x6f, 0x6f, 0x20, 0x62, 0x69, 0x67, 0x20, 0x66, + 0x6f, 0x72, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, + 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x2e, + 0x00, 0x00, 0x49, 0x20, 0x64, 0x6f, 0x6e, 0x27, + 0x74, 0x20, 0x77, 0x61, 0x6e, 0x74, 0x20, 0x74, + 0x6f, 0x20, 0x77, 0x61, 0x73, 0x74, 0x65, 0x20, + 0x74, 0x68, 0x69, 0x73, 0x20, 0x63, 0x61, 0x6e, + 0x64, 0x79, 0x2e, 0x00, 0x00, 0x54, 0x68, 0x65, + 0x20, 0x66, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x20, + 0x69, 0x73, 0x20, 0x62, 0x65, 0x61, 0x75, 0x74, + 0x69, 0x66, 0x75, 0x6c, 0x20, 0x65, 0x6e, 0x6f, + 0x75, 0x67, 0x68, 0x20, 0x77, 0x69, 0x74, 0x68, + 0x6f, 0x75, 0x74, 0x00, 0x61, 0x6e, 0x79, 0x20, + 0x66, 0x61, 0x6e, 0x63, 0x79, 0x20, 0x65, 0x78, + 0x74, 0x72, 0x61, 0x73, 0x2e, 0x00, 0x00, 0x47, + 0x6f, 0x6f, 0x64, 0x20, 0x69, 0x64, 0x65, 0x61, + 0x2c, 0x20, 0x62, 0x75, 0x74, 0x20, 0x49, 0x20, + 0x6e, 0x65, 0x65, 0x64, 0x20, 0x73, 0x6f, 0x6d, + 0x65, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x20, 0x73, + 0x6d, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x00, 0x74, + 0x68, 0x61, 0x6e, 0x20, 0x74, 0x68, 0x69, 0x73, + 0x20, 0x72, 0x6f, 0x70, 0x65, 0x2e, 0x00, 0x00, + 0x49, 0x20, 0x6d, 0x69, 0x67, 0x68, 0x74, 0x20, + 0x6e, 0x65, 0x65, 0x64, 0x20, 0x74, 0x68, 0x69, + 0x73, 0x20, 0x63, 0x68, 0x65, 0x65, 0x73, 0x65, + 0x2e, 0x20, 0x41, 0x67, 0x61, 0x69, 0x6e, 0x2e, + 0x00, 0x00, 0x49, 0x74, 0x27, 0x73, 0x20, 0x6e, + 0x6f, 0x74, 0x20, 0x44, 0x4f, 0x4f, 0x4d, 0x2e, + 0x20, 0x49, 0x74, 0x27, 0x73, 0x20, 0x61, 0x20, + 0x68, 0x61, 0x72, 0x6d, 0x6c, 0x65, 0x73, 0x73, + 0x20, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, + 0x20, 0x61, 0x64, 0x76, 0x65, 0x6e, 0x74, 0x75, + 0x72, 0x65, 0x00, 0x66, 0x6f, 0x72, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x77, 0x68, 0x6f, 0x6c, 0x65, + 0x20, 0x66, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x20, + 0x28, 0x77, 0x65, 0x20, 0x77, 0x61, 0x6e, 0x74, + 0x20, 0x44, 0x4f, 0x4f, 0x4d, 0x21, 0x20, 0x77, + 0x65, 0x20, 0x77, 0x61, 0x6e, 0x74, 0x00, 0x44, + 0x4f, 0x4f, 0x4d, 0x21, 0x29, 0x2e, 0x00, 0x00, + 0x49, 0x20, 0x64, 0x6f, 0x6e, 0x27, 0x74, 0x20, + 0x6e, 0x65, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, + 0x6f, 0x70, 0x65, 0x6e, 0x20, 0x74, 0x68, 0x69, + 0x73, 0x20, 0x6e, 0x75, 0x74, 0x2e, 0x00, 0x00, + 0x4c, 0x65, 0x74, 0x27, 0x73, 0x20, 0x6d, 0x61, + 0x6b, 0x65, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, + 0x63, 0x6f, 0x72, 0x6b, 0x20, 0x6c, 0x61, 0x72, + 0x67, 0x65, 0x72, 0x2e, 0x00, 0x00, 0x4f, 0x6e, + 0x63, 0x65, 0x20, 0x61, 0x67, 0x61, 0x69, 0x6e, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x75, 0x70, + 0x65, 0x72, 0x20, 0x67, 0x6c, 0x75, 0x65, 0x20, + 0x63, 0x6f, 0x6d, 0x65, 0x73, 0x20, 0x69, 0x6e, + 0x20, 0x68, 0x61, 0x6e, 0x64, 0x79, 0x2e, 0x00, + 0x00, 0x54, 0x68, 0x65, 0x20, 0x62, 0x61, 0x74, + 0x74, 0x65, 0x72, 0x69, 0x65, 0x73, 0x20, 0x66, + 0x69, 0x74, 0x21, 0x00, 0x00, 0x49, 0x20, 0x74, + 0x69, 0x65, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x72, 0x6f, 0x70, 0x65, 0x20, 0x74, 0x6f, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x70, 0x69, 0x6e, 0x2e, + 0x00, 0x00, 0x4c, 0x65, 0x74, 0x27, 0x73, 0x20, + 0x6d, 0x61, 0x6b, 0x65, 0x20, 0x69, 0x74, 0x20, + 0x73, 0x70, 0x69, 0x63, 0x79, 0x2e, 0x00, 0x00, + 0x12, 0x16, 0x17, 0xb4, 0xbe, 0x15, 0x18, 0x19, + 0xed, 0xbe, 0x01, 0x1c, 0x1f, 0x35, 0xbf, 0x11, + 0x1c, 0x1e, 0x35, 0xbf, 0x1f, 0x11, 0x20, 0x69, + 0xbf, 0x1e, 0x01, 0x20, 0x69, 0xbf, 0x0d, 0x10, + 0x0e, 0x8d, 0xbf, 0x08, 0x0f, 0x09, 0xe6, 0xbf, + 0x14, 0x21, 0x22, 0x13, 0xc0, 0x27, 0x28, 0x29, + 0x43, 0xc0, 0x26, 0x2a, 0x2b, 0x67, 0xc0, 0x12, + 0x13, 0x00, 0x9c, 0xc0, 0x01, 0x30, 0x00, 0xc8, + 0xc0, 0x10, 0x30, 0x00, 0xdd, 0xc0, 0x12, 0x14, + 0x00, 0x18, 0xc1, 0x12, 0x22, 0x00, 0x18, 0xc1, + 0x12, 0x1a, 0x00, 0x18, 0xc1, 0x12, 0x1c, 0x00, + 0x18, 0xc1, 0x12, 0x31, 0x00, 0x18, 0xc1, 0x12, + 0x13, 0x00, 0x40, 0xc1, 0x13, 0x30, 0x00, 0x67, + 0xc1, 0x18, 0x0a, 0x00, 0x8a, 0xc1, 0x18, 0x0b, + 0x00, 0x8a, 0xc1, 0x15, 0x26, 0x00, 0xc4, 0xc1, + 0x2d, 0x30, 0x00, 0xfd, 0xc1, 0x2c, 0x30, 0x00, + 0x1f, 0xc2, 0x2e, 0x30, 0x00, 0x1f, 0xc2, 0x31, + 0x1a, 0x00, 0x85, 0xc2, 0x31, 0x30, 0x00, 0x1f, + 0xc2, 0x37, 0x3b, 0x3c, 0xa5, 0xc2, 0x41, 0x38, + 0x40, 0xc3, 0xc2, 0x42, 0x34, 0x43, 0xee, 0xc2, + 0x54, 0x58, 0x59, 0x02, 0xc3, 0x57, 0x5a, 0x5b, + 0x1f, 0xc3, 0x00, 0x00, 0x00, 0x55, 0x73, 0x69, + 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x73, 0x65, + 0x20, 0x74, 0x77, 0x6f, 0x20, 0x6f, 0x62, 0x6a, + 0x65, 0x63, 0x74, 0x73, 0x20, 0x74, 0x6f, 0x67, + 0x65, 0x74, 0x68, 0x65, 0x72, 0x20, 0x77, 0x6f, + 0x6e, 0x27, 0x74, 0x00, 0x61, 0x63, 0x63, 0x6f, + 0x6d, 0x70, 0x6c, 0x69, 0x73, 0x68, 0x20, 0x61, + 0x6e, 0x79, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x2e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x4b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x5e, 0xc5, 0x7b, 0xc5, 0x9a, 0xc5, 0xc3, + 0xc5, 0xcf, 0xc5, 0xfd, 0xc5, 0x2c, 0xc6, 0x3f, + 0xc6, 0x62, 0xc6, 0x93, 0xc6, 0xb9, 0xc6, 0xda, + 0xc6, 0x0a, 0xc7, 0x42, 0xc7, 0x75, 0xc7, 0x96, + 0xc7, 0xcb, 0xc7, 0xf4, 0xc7, 0x60, 0xc8, 0xa0, + 0xc8, 0xd3, 0xc8, 0x21, 0xc9, 0x65, 0xc9, 0xa2, + 0xc9, 0xf2, 0xc9, 0x09, 0xca, 0x22, 0xca, 0x6b, + 0xca, 0xe7, 0xca, 0x2d, 0xcb, 0x61, 0xcb, 0x96, + 0xcb, 0xba, 0xcb, 0xf2, 0xcb, 0x5d, 0xcc, 0x79, + 0xcc, 0xdd, 0xcc, 0x0f, 0xcd, 0x25, 0xcd, 0x54, + 0xcd, 0x95, 0xcd, 0xc8, 0xcd, 0xfe, 0xcd, 0x33, + 0xce, 0x75, 0xce, 0xa6, 0xce, 0xe3, 0xce, 0x4f, + 0xcf, 0x6a, 0xcf, 0x9d, 0xcf, 0xf8, 0xcf, 0x05, + 0xd0, 0x32, 0xd0, 0x5c, 0xd0, 0x7b, 0xd0, 0xb1, + 0xd0, 0xe7, 0xd0, 0x30, 0xd1, 0x4e, 0xd1, 0x73, + 0xd1, 0x9d, 0xd1, 0xf4, 0xd1, 0x41, 0xd2, 0x6f, + 0xd2, 0xa7, 0xd2, 0xcb, 0xd2, 0xf6, 0xd2, 0x46, + 0xd3, 0x8a, 0xd3, 0xb3, 0xd3, 0xfb, 0xd3, 0x16, + 0xd4, 0x49, 0xd4, 0x7b, 0xd4, 0xa4, 0xd4, 0xca, + 0xd4, 0xeb, 0xd4, 0x1f, 0xd5, 0x43, 0xd5, 0x7f, + 0xd5, 0xaa, 0xd5, 0xc6, 0xd5, 0x42, 0xd6, 0xb7, + 0xd6, 0x0a, 0xd7, 0x1d, 0xd7, 0x67, 0xd7, 0x92, + 0xd7, 0xae, 0xd7, 0xe6, 0xd7, 0x35, 0xd8, 0x5e, + 0xd8, 0x01, 0x00, 0x66, 0x65, 0x61, 0x74, 0x68, + 0x65, 0x72, 0x00, 0x49, 0x74, 0x27, 0x73, 0x20, + 0x6b, 0x69, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x20, + 0x61, 0x73, 0x73, 0x21, 0x00, 0x00, 0x02, 0x00, + 0x73, 0x68, 0x6f, 0x74, 0x67, 0x75, 0x6e, 0x00, + 0x4c, 0x6f, 0x6f, 0x6b, 0x73, 0x20, 0x69, 0x6d, + 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x76, 0x65, + 0x2e, 0x2e, 0x2e, 0x00, 0x00, 0x03, 0x00, 0x74, + 0x6f, 0x6f, 0x6c, 0x62, 0x6f, 0x78, 0x00, 0x44, + 0x6f, 0x65, 0x73, 0x6e, 0x27, 0x74, 0x20, 0x73, + 0x65, 0x65, 0x6d, 0x20, 0x74, 0x6f, 0x20, 0x62, + 0x65, 0x20, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, + 0x21, 0x2e, 0x2e, 0x2e, 0x00, 0x00, 0x04, 0x00, + 0x74, 0x6f, 0x6f, 0x6c, 0x62, 0x6f, 0x78, 0x00, + 0x00, 0x00, 0x05, 0x00, 0x73, 0x70, 0x61, 0x6e, + 0x6e, 0x65, 0x72, 0x00, 0x54, 0x68, 0x65, 0x73, + 0x65, 0x20, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x73, + 0x20, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x20, + 0x63, 0x6f, 0x6d, 0x65, 0x20, 0x69, 0x6e, 0x20, + 0x68, 0x61, 0x6e, 0x64, 0x79, 0x2e, 0x00, 0x00, + 0x06, 0x00, 0x63, 0x6f, 0x6d, 0x62, 0x00, 0x49, + 0x27, 0x76, 0x65, 0x20, 0x68, 0x65, 0x61, 0x72, + 0x64, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x73, + 0x6f, 0x6d, 0x65, 0x20, 0x70, 0x65, 0x6f, 0x70, + 0x6c, 0x65, 0x20, 0x75, 0x73, 0x65, 0x20, 0x74, + 0x68, 0x65, 0x73, 0x65, 0x2e, 0x00, 0x00, 0x07, + 0x01, 0x66, 0x61, 0x6e, 0x00, 0x52, 0x65, 0x66, + 0x72, 0x65, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x2e, + 0x00, 0x00, 0x08, 0x00, 0x62, 0x72, 0x6f, 0x6b, + 0x65, 0x6e, 0x20, 0x70, 0x61, 0x64, 0x64, 0x6c, + 0x65, 0x00, 0x54, 0x6f, 0x6f, 0x20, 0x73, 0x68, + 0x6f, 0x72, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x75, + 0x73, 0x65, 0x2e, 0x00, 0x00, 0x09, 0x00, 0x70, + 0x61, 0x64, 0x64, 0x6c, 0x65, 0x00, 0x54, 0x68, + 0x65, 0x20, 0x67, 0x6c, 0x75, 0x65, 0x20, 0x6b, + 0x65, 0x65, 0x70, 0x73, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x00, + 0x72, 0x65, 0x61, 0x6c, 0x6c, 0x79, 0x20, 0x68, + 0x61, 0x72, 0x64, 0x2e, 0x00, 0x00, 0x0a, 0x00, + 0x66, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x00, 0x49, + 0x74, 0x20, 0x72, 0x65, 0x61, 0x6c, 0x6c, 0x79, + 0x20, 0x73, 0x6d, 0x65, 0x6c, 0x6c, 0x73, 0x00, + 0x76, 0x65, 0x72, 0x79, 0x20, 0x6e, 0x69, 0x63, + 0x65, 0x2e, 0x00, 0x00, 0x0b, 0x00, 0x66, 0x6c, + 0x6f, 0x77, 0x65, 0x72, 0x00, 0x49, 0x74, 0x27, + 0x73, 0x20, 0x72, 0x65, 0x61, 0x6c, 0x6c, 0x79, + 0x20, 0x62, 0x65, 0x61, 0x75, 0x74, 0x69, 0x66, + 0x75, 0x6c, 0x2e, 0x00, 0x00, 0x0c, 0x00, 0x66, + 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x20, 0x64, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x00, 0x49, 0x20, + 0x63, 0x61, 0x6e, 0x20, 0x70, 0x6c, 0x61, 0x79, + 0x20, 0x6a, 0x61, 0x6e, 0x69, 0x74, 0x6f, 0x72, + 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x74, 0x68, + 0x69, 0x73, 0x2e, 0x00, 0x00, 0x0d, 0x00, 0x63, + 0x68, 0x61, 0x69, 0x6e, 0x73, 0x61, 0x77, 0x00, + 0x49, 0x74, 0x27, 0x73, 0x20, 0x69, 0x6e, 0x20, + 0x67, 0x6f, 0x6f, 0x64, 0x20, 0x63, 0x6f, 0x6e, + 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, + 0x62, 0x75, 0x74, 0x20, 0x69, 0x74, 0x20, 0x68, + 0x61, 0x73, 0x20, 0x6e, 0x6f, 0x20, 0x66, 0x75, + 0x65, 0x6c, 0x2e, 0x00, 0x00, 0x0e, 0x01, 0x64, + 0x72, 0x75, 0x6e, 0x6b, 0x65, 0x6e, 0x20, 0x63, + 0x68, 0x61, 0x69, 0x6e, 0x73, 0x61, 0x77, 0x00, + 0x41, 0x20, 0x62, 0x69, 0x74, 0x20, 0x75, 0x6e, + 0x73, 0x74, 0x65, 0x61, 0x64, 0x79, 0x2c, 0x20, + 0x62, 0x75, 0x74, 0x20, 0x77, 0x69, 0x6c, 0x6c, + 0x20, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x00, 0x00, + 0x0f, 0x00, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, + 0x00, 0x54, 0x68, 0x65, 0x20, 0x77, 0x6f, 0x6f, + 0x64, 0x20, 0x69, 0x73, 0x20, 0x76, 0x65, 0x72, + 0x79, 0x20, 0x68, 0x61, 0x72, 0x64, 0x2e, 0x00, + 0x00, 0x10, 0x00, 0x77, 0x68, 0x69, 0x73, 0x6b, + 0x79, 0x00, 0x54, 0x68, 0x65, 0x20, 0x6c, 0x61, + 0x62, 0x65, 0x6c, 0x20, 0x73, 0x61, 0x79, 0x73, + 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x77, 0x68, + 0x69, 0x73, 0x6b, 0x79, 0x20, 0x69, 0x73, 0x00, + 0x76, 0x65, 0x72, 0x79, 0x20, 0x73, 0x74, 0x72, + 0x6f, 0x6e, 0x67, 0x2e, 0x00, 0x00, 0x11, 0x00, + 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x00, 0x49, + 0x74, 0x27, 0x73, 0x20, 0x71, 0x75, 0x69, 0x74, + 0x65, 0x20, 0x62, 0x69, 0x67, 0x20, 0x66, 0x6f, + 0x72, 0x20, 0x61, 0x20, 0x6e, 0x65, 0x65, 0x64, + 0x6c, 0x65, 0x2e, 0x2e, 0x2e, 0x00, 0x00, 0x12, + 0x00, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, + 0x00, 0x4e, 0x69, 0x63, 0x65, 0x20, 0x64, 0x65, + 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x20, 0x45, 0x73, + 0x70, 0x65, 0x63, 0x69, 0x61, 0x6c, 0x6c, 0x79, + 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x27, 0x4c, + 0x4f, 0x56, 0x45, 0x20, 0x43, 0x41, 0x4e, 0x44, + 0x59, 0x27, 0x20, 0x6c, 0x61, 0x62, 0x65, 0x6c, + 0x2e, 0x00, 0x41, 0x6e, 0x64, 0x20, 0x74, 0x68, + 0x65, 0x72, 0x65, 0x27, 0x73, 0x20, 0x61, 0x20, + 0x68, 0x65, 0x61, 0x72, 0x74, 0x20, 0x70, 0x61, + 0x69, 0x6e, 0x74, 0x65, 0x64, 0x20, 0x6f, 0x6e, + 0x20, 0x69, 0x74, 0x2e, 0x00, 0x48, 0x6f, 0x77, + 0x20, 0x53, 0x57, 0x45, 0x45, 0x54, 0x2e, 0x2e, + 0x2e, 0x00, 0x00, 0x13, 0x00, 0x63, 0x68, 0x6f, + 0x63, 0x6f, 0x6c, 0x61, 0x74, 0x65, 0x20, 0x63, + 0x61, 0x6e, 0x64, 0x79, 0x00, 0x49, 0x74, 0x27, + 0x73, 0x20, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, + 0x63, 0x68, 0x6f, 0x63, 0x6f, 0x6c, 0x61, 0x74, + 0x65, 0x20, 0x63, 0x61, 0x6e, 0x64, 0x79, 0x2e, + 0x00, 0x41, 0x20, 0x67, 0x72, 0x65, 0x61, 0x74, + 0x20, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, + 0x2e, 0x00, 0x00, 0x14, 0x00, 0x77, 0x69, 0x6c, + 0x64, 0x20, 0x70, 0x6f, 0x74, 0x61, 0x74, 0x6f, + 0x65, 0x00, 0x57, 0x6f, 0x77, 0x21, 0x20, 0x49, + 0x74, 0x27, 0x73, 0x20, 0x73, 0x68, 0x61, 0x70, + 0x65, 0x64, 0x20, 0x6c, 0x69, 0x6b, 0x65, 0x20, + 0x61, 0x20, 0x67, 0x72, 0x65, 0x6e, 0x61, 0x64, + 0x65, 0x21, 0x2e, 0x2e, 0x00, 0x00, 0x15, 0x00, + 0x72, 0x61, 0x6b, 0x65, 0x00, 0x54, 0x68, 0x65, + 0x20, 0x73, 0x70, 0x61, 0x63, 0x65, 0x20, 0x62, + 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x74, 0x65, 0x65, 0x74, 0x68, + 0x20, 0x69, 0x73, 0x20, 0x74, 0x6f, 0x6f, 0x20, + 0x6c, 0x61, 0x72, 0x67, 0x65, 0x00, 0x74, 0x6f, + 0x20, 0x6d, 0x61, 0x6b, 0x65, 0x20, 0x74, 0x68, + 0x69, 0x73, 0x20, 0x72, 0x61, 0x6b, 0x65, 0x20, + 0x61, 0x6e, 0x20, 0x75, 0x73, 0x65, 0x66, 0x75, + 0x6c, 0x2e, 0x00, 0x00, 0x16, 0x00, 0x68, 0x65, + 0x61, 0x72, 0x74, 0x2d, 0x73, 0x68, 0x61, 0x70, + 0x65, 0x64, 0x20, 0x63, 0x61, 0x6e, 0x64, 0x79, + 0x00, 0x49, 0x20, 0x64, 0x6f, 0x6e, 0x27, 0x74, + 0x20, 0x74, 0x68, 0x69, 0x6e, 0x6b, 0x20, 0x74, + 0x68, 0x69, 0x73, 0x20, 0x6f, 0x6e, 0x65, 0x20, + 0x69, 0x6d, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x6d, + 0x65, 0x6e, 0x74, 0x20, 0x69, 0x73, 0x20, 0x65, + 0x6e, 0x6f, 0x75, 0x67, 0x68, 0x2e, 0x00, 0x00, + 0x17, 0x00, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, + 0x64, 0x20, 0x63, 0x61, 0x6e, 0x64, 0x79, 0x00, + 0x42, 0x72, 0x61, 0x6e, 0x64, 0x20, 0x6e, 0x65, + 0x77, 0x20, 0x63, 0x61, 0x6e, 0x64, 0x79, 0x2e, + 0x20, 0x41, 0x74, 0x20, 0x6c, 0x65, 0x61, 0x73, + 0x74, 0x20, 0x69, 0x74, 0x20, 0x6c, 0x6f, 0x6f, + 0x6b, 0x73, 0x20, 0x6c, 0x69, 0x6b, 0x65, 0x20, + 0x69, 0x74, 0x2e, 0x00, 0x00, 0x18, 0x00, 0x72, + 0x69, 0x62, 0x62, 0x6f, 0x6e, 0x00, 0x49, 0x74, + 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x72, 0x65, + 0x6d, 0x69, 0x6e, 0x64, 0x20, 0x6d, 0x65, 0x20, + 0x6f, 0x66, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, + 0x62, 0x65, 0x61, 0x74, 0x69, 0x66, 0x75, 0x6c, + 0x20, 0x63, 0x68, 0x69, 0x63, 0x6b, 0x2c, 0x00, + 0x49, 0x20, 0x6d, 0x65, 0x61, 0x6e, 0x2c, 0x20, + 0x66, 0x65, 0x6d, 0x61, 0x6c, 0x65, 0x20, 0x68, + 0x75, 0x6d, 0x61, 0x6e, 0x20, 0x62, 0x65, 0x69, + 0x6e, 0x67, 0x2e, 0x00, 0x00, 0x19, 0x00, 0x72, + 0x61, 0x6b, 0x65, 0x00, 0x52, 0x65, 0x61, 0x64, + 0x79, 0x20, 0x74, 0x6f, 0x20, 0x77, 0x6f, 0x72, + 0x6b, 0x2e, 0x00, 0x00, 0x1a, 0x00, 0x6e, 0x75, + 0x74, 0x00, 0x41, 0x20, 0x72, 0x65, 0x61, 0x6c, + 0x6c, 0x79, 0x20, 0x62, 0x69, 0x67, 0x20, 0x6f, + 0x6e, 0x65, 0x2e, 0x00, 0x00, 0x1b, 0x00, 0x70, + 0x6c, 0x61, 0x73, 0x74, 0x69, 0x63, 0x20, 0x61, + 0x70, 0x70, 0x6c, 0x65, 0x00, 0x49, 0x74, 0x20, + 0x6c, 0x6f, 0x6f, 0x6b, 0x73, 0x20, 0x73, 0x6f, + 0x20, 0x72, 0x65, 0x61, 0x6c, 0x20, 0x74, 0x68, + 0x61, 0x74, 0x20, 0x49, 0x20, 0x63, 0x6f, 0x75, + 0x6c, 0x64, 0x00, 0x65, 0x76, 0x65, 0x6e, 0x20, + 0x73, 0x65, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x70, 0x69, 0x70, 0x73, 0x20, 0x69, 0x6e, 0x73, + 0x69, 0x64, 0x65, 0x2e, 0x00, 0x00, 0x1c, 0x00, + 0x63, 0x6f, 0x6e, 0x65, 0x00, 0x49, 0x74, 0x20, + 0x6c, 0x6f, 0x6f, 0x6b, 0x73, 0x20, 0x6c, 0x69, + 0x6b, 0x65, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f, + 0x66, 0x20, 0x74, 0x68, 0x6f, 0x73, 0x65, 0x20, + 0x48, 0x61, 0x76, 0x61, 0x6e, 0x61, 0x20, 0x67, + 0x6f, 0x6f, 0x64, 0x69, 0x65, 0x73, 0x20, 0x65, + 0x76, 0x65, 0x72, 0x79, 0x00, 0x73, 0x65, 0x6c, + 0x66, 0x2d, 0x72, 0x65, 0x73, 0x70, 0x65, 0x63, + 0x74, 0x69, 0x6e, 0x67, 0x20, 0x62, 0x75, 0x73, + 0x69, 0x6e, 0x65, 0x73, 0x73, 0x6d, 0x61, 0x6e, + 0x20, 0x69, 0x73, 0x20, 0x73, 0x75, 0x70, 0x70, + 0x6f, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, + 0x68, 0x61, 0x76, 0x65, 0x00, 0x67, 0x6c, 0x75, + 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x68, 0x69, + 0x73, 0x20, 0x73, 0x6d, 0x69, 0x6c, 0x65, 0x2e, + 0x00, 0x00, 0x1d, 0x00, 0x73, 0x75, 0x70, 0x65, + 0x72, 0x20, 0x67, 0x6c, 0x75, 0x65, 0x00, 0x49, + 0x74, 0x27, 0x73, 0x20, 0x74, 0x75, 0x72, 0x62, + 0x6f, 0x20, 0x6d, 0x65, 0x67, 0x61, 0x20, 0x67, + 0x69, 0x67, 0x61, 0x20, 0x73, 0x75, 0x70, 0x65, + 0x72, 0x20, 0x75, 0x6c, 0x74, 0x72, 0x61, 0x00, + 0x66, 0x61, 0x73, 0x74, 0x20, 0x64, 0x72, 0x79, + 0x69, 0x6e, 0x67, 0x20, 0x67, 0x6c, 0x75, 0x65, + 0x20, 0x70, 0x6c, 0x75, 0x73, 0x2e, 0x00, 0x00, + 0x1e, 0x00, 0x63, 0x6f, 0x6e, 0x65, 0x20, 0x26, + 0x20, 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x00, + 0x53, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x69, 0x6e, + 0x67, 0x20, 0x69, 0x73, 0x20, 0x73, 0x74, 0x69, + 0x6c, 0x6c, 0x20, 0x6d, 0x69, 0x73, 0x73, 0x69, + 0x6e, 0x67, 0x20, 0x68, 0x65, 0x72, 0x65, 0x2e, + 0x2e, 0x2e, 0x00, 0x00, 0x1f, 0x00, 0x63, 0x6f, + 0x6e, 0x65, 0x20, 0x26, 0x20, 0x66, 0x65, 0x61, + 0x74, 0x68, 0x65, 0x72, 0x00, 0x53, 0x6f, 0x6d, + 0x65, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x20, 0x69, + 0x73, 0x20, 0x73, 0x74, 0x69, 0x6c, 0x6c, 0x20, + 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x20, + 0x68, 0x65, 0x72, 0x65, 0x2e, 0x2e, 0x2e, 0x00, + 0x00, 0x20, 0x00, 0x64, 0x61, 0x72, 0x74, 0x00, + 0x4e, 0x6f, 0x77, 0x20, 0x61, 0x6c, 0x6c, 0x20, + 0x49, 0x20, 0x6e, 0x65, 0x65, 0x64, 0x20, 0x69, + 0x73, 0x20, 0x61, 0x20, 0x74, 0x61, 0x72, 0x67, + 0x65, 0x74, 0x21, 0x00, 0x00, 0x21, 0x00, 0x64, + 0x69, 0x72, 0x74, 0x79, 0x20, 0x66, 0x65, 0x61, + 0x74, 0x68, 0x65, 0x72, 0x20, 0x64, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x00, 0x49, 0x74, 0x27, 0x73, + 0x20, 0x63, 0x6c, 0x61, 0x6d, 0x6d, 0x79, 0x20, + 0x61, 0x6e, 0x64, 0x20, 0x73, 0x6f, 0x69, 0x6c, + 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x73, 0x6f, + 0x6f, 0x74, 0x2e, 0x00, 0x00, 0x22, 0x00, 0x70, + 0x61, 0x69, 0x6e, 0x74, 0x65, 0x64, 0x20, 0x70, + 0x6f, 0x74, 0x61, 0x74, 0x6f, 0x65, 0x00, 0x54, + 0x68, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, + 0x64, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65, 0x6e, + 0x63, 0x65, 0x20, 0x62, 0x65, 0x74, 0x77, 0x65, + 0x65, 0x6e, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, + 0x61, 0x6e, 0x64, 0x20, 0x61, 0x20, 0x67, 0x72, + 0x65, 0x6e, 0x61, 0x64, 0x65, 0x00, 0x69, 0x73, + 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x70, 0x6f, + 0x74, 0x61, 0x74, 0x6f, 0x65, 0x65, 0x73, 0x20, + 0x75, 0x73, 0x75, 0x61, 0x6c, 0x6c, 0x79, 0x20, + 0x64, 0x6f, 0x6e, 0x27, 0x74, 0x20, 0x62, 0x6c, + 0x6f, 0x77, 0x20, 0x75, 0x70, 0x2e, 0x00, 0x00, + 0x23, 0x00, 0x63, 0x61, 0x72, 0x20, 0x6a, 0x61, + 0x63, 0x6b, 0x00, 0x4c, 0x6f, 0x6f, 0x6b, 0x73, + 0x20, 0x72, 0x65, 0x6c, 0x69, 0x61, 0x62, 0x6c, + 0x65, 0x2e, 0x00, 0x00, 0x24, 0x00, 0x64, 0x69, + 0x6e, 0x6f, 0x20, 0x62, 0x6f, 0x6e, 0x65, 0x00, + 0x49, 0x74, 0x27, 0x73, 0x20, 0x62, 0x69, 0x67, + 0x20, 0x61, 0x6e, 0x64, 0x20, 0x77, 0x65, 0x6c, + 0x6c, 0x20, 0x70, 0x72, 0x65, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x64, 0x2e, 0x20, 0x49, 0x74, 0x20, + 0x6d, 0x75, 0x73, 0x74, 0x20, 0x68, 0x61, 0x76, + 0x65, 0x20, 0x62, 0x65, 0x65, 0x6e, 0x00, 0x61, + 0x20, 0x72, 0x65, 0x61, 0x6c, 0x6c, 0x79, 0x20, + 0x62, 0x69, 0x67, 0x2c, 0x20, 0x65, 0x72, 0x2e, + 0x2e, 0x2e, 0x2c, 0x20, 0x61, 0x6e, 0x69, 0x6d, + 0x61, 0x6c, 0x2c, 0x20, 0x49, 0x20, 0x74, 0x68, + 0x69, 0x6e, 0x6b, 0x2e, 0x2e, 0x2e, 0x00, 0x00, + 0x25, 0x00, 0x73, 0x68, 0x6f, 0x76, 0x65, 0x6c, + 0x00, 0x49, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x50, + 0x4c, 0x41, 0x59, 0x20, 0x44, 0x49, 0x47, 0x47, + 0x45, 0x52, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, + 0x69, 0x74, 0x2e, 0x20, 0x53, 0x6f, 0x20, 0x74, + 0x6f, 0x20, 0x73, 0x70, 0x65, 0x61, 0x6b, 0x2e, + 0x00, 0x00, 0x26, 0x00, 0x72, 0x6f, 0x70, 0x65, + 0x00, 0x4c, 0x6f, 0x6f, 0x6b, 0x73, 0x20, 0x73, + 0x74, 0x72, 0x6f, 0x6e, 0x67, 0x2e, 0x00, 0x00, + 0x27, 0x00, 0x6d, 0x61, 0x73, 0x6b, 0x00, 0x49, + 0x74, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x68, + 0x65, 0x6c, 0x70, 0x20, 0x6d, 0x65, 0x20, 0x73, + 0x65, 0x65, 0x20, 0x62, 0x65, 0x74, 0x74, 0x65, + 0x72, 0x20, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x77, + 0x61, 0x74, 0x65, 0x72, 0x2e, 0x00, 0x00, 0x28, + 0x00, 0x66, 0x69, 0x6e, 0x73, 0x00, 0x54, 0x68, + 0x65, 0x79, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, + 0x68, 0x65, 0x6c, 0x70, 0x20, 0x6d, 0x65, 0x20, + 0x66, 0x65, 0x65, 0x6c, 0x20, 0x6d, 0x6f, 0x72, + 0x65, 0x20, 0x6c, 0x69, 0x6b, 0x65, 0x20, 0x6d, + 0x79, 0x00, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x77, + 0x61, 0x74, 0x65, 0x72, 0x20, 0x62, 0x72, 0x6f, + 0x74, 0x68, 0x65, 0x72, 0x73, 0x2e, 0x00, 0x00, + 0x29, 0x00, 0x64, 0x69, 0x76, 0x69, 0x6e, 0x67, + 0x20, 0x65, 0x71, 0x75, 0x69, 0x70, 0x6d, 0x65, + 0x6e, 0x74, 0x00, 0x57, 0x61, 0x74, 0x63, 0x68, + 0x20, 0x6f, 0x75, 0x74, 0x2c, 0x20, 0x77, 0x61, + 0x74, 0x65, 0x72, 0x21, 0x20, 0x48, 0x65, 0x72, + 0x65, 0x20, 0x49, 0x20, 0x63, 0x6f, 0x6d, 0x65, + 0x21, 0x00, 0x00, 0x2a, 0x00, 0x61, 0x6e, 0x63, + 0x68, 0x6f, 0x72, 0x00, 0x4e, 0x6f, 0x74, 0x20, + 0x62, 0x69, 0x67, 0x2c, 0x20, 0x62, 0x75, 0x74, + 0x20, 0x68, 0x65, 0x61, 0x76, 0x79, 0x20, 0x65, + 0x6e, 0x6f, 0x75, 0x67, 0x68, 0x20, 0x74, 0x6f, + 0x20, 0x73, 0x69, 0x6e, 0x6b, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x62, 0x6f, 0x61, 0x74, 0x2e, 0x00, + 0x00, 0x2b, 0x00, 0x67, 0x72, 0x61, 0x70, 0x70, + 0x6c, 0x69, 0x6e, 0x67, 0x20, 0x68, 0x6f, 0x6f, + 0x6b, 0x00, 0x57, 0x61, 0x74, 0x63, 0x68, 0x20, + 0x6f, 0x75, 0x74, 0x2c, 0x20, 0x6d, 0x6f, 0x75, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x2c, 0x20, + 0x68, 0x65, 0x72, 0x65, 0x20, 0x49, 0x20, 0x63, + 0x6f, 0x6d, 0x65, 0x21, 0x00, 0x00, 0x2c, 0x00, + 0x73, 0x69, 0x63, 0x6b, 0x6c, 0x65, 0x00, 0x49, + 0x74, 0x27, 0x73, 0x20, 0x73, 0x6f, 0x20, 0x62, + 0x6c, 0x75, 0x6e, 0x74, 0x2c, 0x20, 0x74, 0x68, + 0x61, 0x74, 0x20, 0x49, 0x20, 0x63, 0x6f, 0x75, + 0x6c, 0x64, 0x6e, 0x27, 0x74, 0x20, 0x65, 0x76, + 0x65, 0x6e, 0x20, 0x63, 0x75, 0x74, 0x00, 0x62, + 0x75, 0x74, 0x74, 0x65, 0x72, 0x20, 0x77, 0x69, + 0x74, 0x68, 0x20, 0x69, 0x74, 0x2e, 0x00, 0x00, + 0x2d, 0x00, 0x73, 0x6f, 0x6d, 0x65, 0x77, 0x68, + 0x61, 0x74, 0x20, 0x72, 0x6f, 0x74, 0x74, 0x65, + 0x6e, 0x20, 0x63, 0x68, 0x65, 0x65, 0x73, 0x65, + 0x00, 0x52, 0x65, 0x6d, 0x69, 0x6e, 0x64, 0x73, + 0x20, 0x6d, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x6d, + 0x79, 0x20, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x00, + 0x00, 0x2e, 0x00, 0x73, 0x68, 0x61, 0x72, 0x70, + 0x65, 0x6e, 0x65, 0x64, 0x20, 0x73, 0x69, 0x63, + 0x6b, 0x6c, 0x65, 0x00, 0x4c, 0x61, 0x6d, 0x62, + 0x73, 0x2c, 0x20, 0x62, 0x65, 0x20, 0x73, 0x69, + 0x6c, 0x65, 0x6e, 0x74, 0x2e, 0x20, 0x48, 0x65, + 0x72, 0x65, 0x20, 0x63, 0x6f, 0x6d, 0x65, 0x73, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x61, 0x69, + 0x6e, 0x2e, 0x2e, 0x2e, 0x00, 0x00, 0x2f, 0x00, + 0x68, 0x61, 0x6e, 0x64, 0x6b, 0x65, 0x72, 0x63, + 0x68, 0x69, 0x65, 0x66, 0x00, 0x54, 0x68, 0x65, + 0x20, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x20, 0x6d, + 0x75, 0x73, 0x74, 0x20, 0x68, 0x61, 0x76, 0x65, + 0x20, 0x73, 0x75, 0x63, 0x68, 0x20, 0x62, 0x69, + 0x67, 0x20, 0x6e, 0x6f, 0x73, 0x65, 0x20, 0x74, + 0x68, 0x61, 0x74, 0x20, 0x68, 0x65, 0x20, 0x6e, + 0x65, 0x65, 0x64, 0x73, 0x00, 0x74, 0x6f, 0x20, + 0x75, 0x73, 0x65, 0x20, 0x61, 0x20, 0x77, 0x68, + 0x6f, 0x6c, 0x65, 0x20, 0x62, 0x61, 0x72, 0x20, + 0x6f, 0x66, 0x20, 0x73, 0x6f, 0x61, 0x70, 0x20, + 0x6a, 0x75, 0x73, 0x74, 0x20, 0x74, 0x6f, 0x20, + 0x77, 0x61, 0x73, 0x68, 0x20, 0x69, 0x74, 0x2e, + 0x00, 0x00, 0x30, 0x01, 0x6d, 0x6f, 0x75, 0x73, + 0x65, 0x00, 0x49, 0x74, 0x27, 0x73, 0x20, 0x76, + 0x65, 0x72, 0x79, 0x20, 0x61, 0x63, 0x74, 0x69, + 0x76, 0x65, 0x2e, 0x00, 0x00, 0x31, 0x00, 0x72, + 0x6f, 0x63, 0x6b, 0x00, 0x49, 0x74, 0x27, 0x73, + 0x20, 0x76, 0x65, 0x72, 0x79, 0x20, 0x72, 0x65, + 0x67, 0x75, 0x6c, 0x61, 0x72, 0x2c, 0x20, 0x6a, + 0x75, 0x73, 0x74, 0x20, 0x6c, 0x69, 0x6b, 0x65, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x6e, 0x6f, + 0x77, 0x62, 0x61, 0x6c, 0x6c, 0x2e, 0x00, 0x00, + 0x32, 0x01, 0x6e, 0x75, 0x67, 0x67, 0x65, 0x74, + 0x00, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x3a, + 0x20, 0x41, 0x75, 0x2c, 0x20, 0x61, 0x74, 0x6f, + 0x6d, 0x69, 0x63, 0x20, 0x6e, 0x6f, 0x3a, 0x20, + 0x37, 0x39, 0x2c, 0x20, 0x61, 0x74, 0x6f, 0x6d, + 0x69, 0x63, 0x20, 0x77, 0x74, 0x2e, 0x3a, 0x20, + 0x31, 0x39, 0x36, 0x2e, 0x39, 0x37, 0x2e, 0x00, + 0x49, 0x6e, 0x20, 0x6f, 0x74, 0x68, 0x65, 0x72, + 0x20, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x3a, 0x20, + 0x47, 0x4f, 0x4c, 0x44, 0x21, 0x21, 0x21, 0x20, + 0x59, 0x65, 0x73, 0x21, 0x20, 0x59, 0x65, 0x73, + 0x21, 0x00, 0x00, 0x33, 0x00, 0x62, 0x61, 0x6e, + 0x6b, 0x6e, 0x6f, 0x74, 0x65, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x64, 0x69, 0x63, 0x74, 0x61, 0x70, + 0x68, 0x6f, 0x6e, 0x65, 0x00, 0x54, 0x68, 0x65, + 0x72, 0x65, 0x20, 0x61, 0x72, 0x65, 0x20, 0x6e, + 0x6f, 0x20, 0x62, 0x61, 0x74, 0x74, 0x65, 0x72, + 0x69, 0x65, 0x73, 0x20, 0x69, 0x6e, 0x73, 0x69, + 0x64, 0x65, 0x2e, 0x00, 0x00, 0x35, 0x00, 0x70, + 0x6f, 0x6c, 0x61, 0x72, 0x6f, 0x69, 0x64, 0x00, + 0x49, 0x74, 0x27, 0x73, 0x20, 0x72, 0x65, 0x61, + 0x64, 0x79, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x61, + 0x6b, 0x65, 0x20, 0x61, 0x20, 0x70, 0x69, 0x63, + 0x74, 0x75, 0x72, 0x65, 0x2e, 0x00, 0x00, 0x36, + 0x00, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x20, 0x74, + 0x61, 0x70, 0x65, 0x00, 0x49, 0x74, 0x20, 0x68, + 0x61, 0x73, 0x20, 0x6e, 0x6f, 0x20, 0x6c, 0x61, + 0x62, 0x65, 0x6c, 0x2e, 0x00, 0x00, 0x37, 0x00, + 0x73, 0x68, 0x65, 0x65, 0x74, 0x20, 0x6f, 0x66, + 0x20, 0x70, 0x61, 0x70, 0x65, 0x72, 0x00, 0x54, + 0x68, 0x65, 0x72, 0x65, 0x27, 0x73, 0x20, 0x6e, + 0x6f, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x20, 0x69, + 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6e, 0x74, + 0x20, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x20, 0x69, + 0x74, 0x2e, 0x00, 0x00, 0x38, 0x00, 0x63, 0x6f, + 0x67, 0x6e, 0x61, 0x63, 0x00, 0x46, 0x61, 0x6e, + 0x63, 0x79, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, + 0x6a, 0x75, 0x73, 0x74, 0x20, 0x66, 0x6f, 0x72, + 0x20, 0x61, 0x6e, 0x20, 0x65, 0x78, 0x63, 0x75, + 0x73, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x64, + 0x72, 0x69, 0x6e, 0x6b, 0x69, 0x6e, 0x67, 0x2e, + 0x00, 0x00, 0x39, 0x00, 0x72, 0x65, 0x6d, 0x6f, + 0x74, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x00, 0x49, 0x74, 0x20, 0x68, 0x61, + 0x73, 0x20, 0x75, 0x73, 0x65, 0x72, 0x2d, 0x66, + 0x72, 0x69, 0x65, 0x6e, 0x64, 0x6c, 0x79, 0x20, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, + 0x65, 0x2e, 0x00, 0x4f, 0x6e, 0x65, 0x20, 0x63, + 0x61, 0x6e, 0x20, 0x70, 0x6c, 0x61, 0x79, 0x20, + 0x61, 0x6e, 0x64, 0x20, 0x73, 0x74, 0x6f, 0x70, + 0x2e, 0x00, 0x00, 0x3a, 0x00, 0x69, 0x63, 0x65, + 0x20, 0x74, 0x6f, 0x6e, 0x67, 0x73, 0x00, 0x56, + 0x65, 0x72, 0x79, 0x20, 0x68, 0x61, 0x6e, 0x64, + 0x79, 0x20, 0x74, 0x6f, 0x6f, 0x6c, 0x2e, 0x00, + 0x00, 0x3b, 0x00, 0x63, 0x6f, 0x72, 0x6b, 0x00, + 0x49, 0x74, 0x27, 0x73, 0x20, 0x61, 0x20, 0x73, + 0x74, 0x6f, 0x70, 0x70, 0x65, 0x72, 0x20, 0x6b, + 0x69, 0x6e, 0x64, 0x20, 0x6f, 0x66, 0x20, 0x63, + 0x6f, 0x72, 0x6b, 0x2e, 0x00, 0x00, 0x3c, 0x00, + 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x20, + 0x63, 0x6f, 0x72, 0x6b, 0x00, 0x4e, 0x6f, 0x77, + 0x20, 0x69, 0x74, 0x27, 0x73, 0x20, 0x6d, 0x6f, + 0x72, 0x65, 0x20, 0x61, 0x70, 0x70, 0x72, 0x6f, + 0x70, 0x69, 0x61, 0x74, 0x65, 0x2e, 0x00, 0x00, + 0x3d, 0x00, 0x70, 0x68, 0x6f, 0x74, 0x6f, 0x00, + 0x49, 0x74, 0x27, 0x73, 0x20, 0x61, 0x20, 0x70, + 0x68, 0x6f, 0x74, 0x6f, 0x20, 0x6f, 0x66, 0x20, + 0x4a, 0x6f, 0x68, 0x6e, 0x20, 0x4e, 0x6f, 0x74, + 0x79, 0x2e, 0x20, 0x49, 0x20, 0x63, 0x61, 0x75, + 0x67, 0x68, 0x74, 0x20, 0x68, 0x69, 0x6d, 0x00, + 0x77, 0x68, 0x65, 0x6e, 0x20, 0x68, 0x65, 0x20, + 0x77, 0x61, 0x73, 0x20, 0x73, 0x69, 0x6e, 0x67, + 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x68, 0x69, 0x67, 0x68, 0x20, 0x43, 0x2e, 0x20, + 0x59, 0x75, 0x63, 0x6b, 0x21, 0x00, 0x00, 0x3e, + 0x00, 0x63, 0x68, 0x69, 0x6c, 0x6c, 0x69, 0x00, + 0x54, 0x68, 0x65, 0x20, 0x6c, 0x61, 0x62, 0x65, + 0x6c, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x62, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x20, + 0x73, 0x61, 0x79, 0x73, 0x20, 0x69, 0x74, 0x27, + 0x73, 0x00, 0x27, 0x4f, 0x52, 0x49, 0x47, 0x49, + 0x4e, 0x41, 0x4c, 0x20, 0x4d, 0x45, 0x58, 0x49, + 0x43, 0x41, 0x4e, 0x20, 0x43, 0x48, 0x49, 0x4c, + 0x4c, 0x49, 0x27, 0x2e, 0x20, 0x53, 0x75, 0x72, + 0x65, 0x2e, 0x00, 0x00, 0x3f, 0x00, 0x70, 0x61, + 0x73, 0x74, 0x72, 0x79, 0x20, 0x72, 0x6f, 0x6c, + 0x6c, 0x65, 0x72, 0x00, 0x49, 0x74, 0x27, 0x73, + 0x20, 0x6d, 0x61, 0x64, 0x65, 0x20, 0x6f, 0x66, + 0x20, 0x76, 0x65, 0x72, 0x79, 0x20, 0x68, 0x61, + 0x72, 0x64, 0x20, 0x77, 0x6f, 0x6f, 0x64, 0x2e, + 0x00, 0x00, 0x40, 0x00, 0x66, 0x61, 0x6b, 0x65, + 0x20, 0x63, 0x68, 0x69, 0x6c, 0x6c, 0x69, 0x00, + 0x4e, 0x6f, 0x77, 0x20, 0x74, 0x68, 0x61, 0x74, + 0x27, 0x73, 0x20, 0x77, 0x68, 0x61, 0x74, 0x20, + 0x49, 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x20, 0x73, + 0x74, 0x72, 0x6f, 0x6e, 0x67, 0x20, 0x73, 0x65, + 0x61, 0x73, 0x6f, 0x6e, 0x69, 0x6e, 0x67, 0x2e, + 0x00, 0x00, 0x41, 0x00, 0x6c, 0x61, 0x62, 0x65, + 0x6c, 0x00, 0x27, 0x4f, 0x52, 0x49, 0x47, 0x49, + 0x4e, 0x41, 0x4c, 0x20, 0x4d, 0x45, 0x58, 0x49, + 0x43, 0x41, 0x4e, 0x20, 0x43, 0x48, 0x49, 0x4c, + 0x4c, 0x49, 0x27, 0x2e, 0x00, 0x00, 0x42, 0x00, + 0x62, 0x61, 0x74, 0x74, 0x65, 0x72, 0x69, 0x65, + 0x73, 0x00, 0x49, 0x74, 0x27, 0x73, 0x20, 0x61, + 0x20, 0x70, 0x61, 0x69, 0x72, 0x20, 0x6f, 0x66, + 0x20, 0x6e, 0x65, 0x77, 0x20, 0x62, 0x61, 0x74, + 0x74, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2e, 0x00, + 0x00, 0x43, 0x00, 0x64, 0x69, 0x63, 0x74, 0x61, + 0x70, 0x68, 0x6f, 0x6e, 0x65, 0x00, 0x27, 0x4f, + 0x6e, 0x65, 0x2d, 0x74, 0x77, 0x6f, 0x2d, 0x6f, + 0x6e, 0x65, 0x2d, 0x74, 0x77, 0x6f, 0x2c, 0x20, + 0x69, 0x74, 0x27, 0x73, 0x20, 0x6d, 0x65, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x6f, 0x6e, 0x65, 0x00, + 0x61, 0x6e, 0x64, 0x20, 0x6f, 0x6e, 0x6c, 0x79, + 0x20, 0x4d, 0x61, 0x72, 0x6b, 0x20, 0x4d, 0x43, + 0x2e, 0x2e, 0x2e, 0x2e, 0x27, 0x00, 0x49, 0x74, + 0x20, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x2e, 0x00, + 0x00, 0x44, 0x01, 0x62, 0x75, 0x72, 0x6e, 0x69, + 0x6e, 0x67, 0x20, 0x70, 0x61, 0x70, 0x65, 0x72, + 0x00, 0x41, 0x6d, 0x61, 0x7a, 0x69, 0x6e, 0x67, + 0x2c, 0x20, 0x69, 0x73, 0x6e, 0x27, 0x74, 0x20, + 0x69, 0x74, 0x3f, 0x00, 0x28, 0x4e, 0x6f, 0x74, + 0x20, 0x74, 0x6f, 0x20, 0x6d, 0x65, 0x6e, 0x74, + 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x75, 0x6e, 0x62, + 0x65, 0x6c, 0x69, 0x65, 0x76, 0x61, 0x62, 0x6c, + 0x65, 0x29, 0x2e, 0x00, 0x00, 0x45, 0x00, 0x6d, + 0x65, 0x61, 0x74, 0x00, 0x54, 0x68, 0x65, 0x72, + 0x65, 0x27, 0x73, 0x20, 0x76, 0x65, 0x61, 0x6c, + 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x70, 0x6c, 0x61, 0x73, 0x74, 0x69, 0x63, 0x20, + 0x62, 0x61, 0x67, 0x2e, 0x00, 0x00, 0x46, 0x00, + 0x70, 0x6c, 0x61, 0x73, 0x74, 0x69, 0x63, 0x20, + 0x62, 0x61, 0x67, 0x00, 0x47, 0x65, 0x65, 0x2c, + 0x20, 0x49, 0x20, 0x68, 0x6f, 0x70, 0x65, 0x20, + 0x69, 0x74, 0x27, 0x73, 0x20, 0x72, 0x65, 0x63, + 0x79, 0x63, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x2e, + 0x20, 0x49, 0x20, 0x63, 0x6f, 0x75, 0x6c, 0x64, + 0x6e, 0x27, 0x74, 0x00, 0x73, 0x6c, 0x65, 0x65, + 0x70, 0x20, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x77, + 0x69, 0x73, 0x65, 0x2e, 0x00, 0x00, 0x47, 0x00, + 0x73, 0x6f, 0x63, 0x6b, 0x73, 0x00, 0x54, 0x68, + 0x65, 0x73, 0x65, 0x20, 0x73, 0x6f, 0x63, 0x6b, + 0x73, 0x20, 0x73, 0x75, 0x63, 0x6b, 0x2e, 0x00, + 0x00, 0x48, 0x00, 0x70, 0x69, 0x6c, 0x6c, 0x73, + 0x00, 0x54, 0x68, 0x65, 0x72, 0x65, 0x20, 0x61, + 0x72, 0x65, 0x20, 0x61, 0x62, 0x6f, 0x75, 0x74, + 0x20, 0x74, 0x77, 0x65, 0x6e, 0x74, 0x79, 0x20, + 0x70, 0x69, 0x6c, 0x6c, 0x73, 0x20, 0x69, 0x6e, + 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x6a, 0x61, + 0x72, 0x2e, 0x00, 0x00, 0x49, 0x00, 0x68, 0x61, + 0x6e, 0x64, 0x6c, 0x65, 0x00, 0x49, 0x74, 0x20, + 0x6c, 0x6f, 0x6f, 0x6b, 0x73, 0x20, 0x6c, 0x69, + 0x6b, 0x65, 0x20, 0x61, 0x20, 0x73, 0x74, 0x61, + 0x6e, 0x64, 0x61, 0x72, 0x64, 0x20, 0x64, 0x6f, + 0x6f, 0x72, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, + 0x65, 0x2e, 0x2e, 0x2e, 0x00, 0x00, 0x4a, 0x00, + 0x63, 0x68, 0x69, 0x6c, 0x6c, 0x69, 0x00, 0x4e, + 0x69, 0x63, 0x65, 0x20, 0x62, 0x6f, 0x74, 0x74, + 0x6c, 0x65, 0x2e, 0x20, 0x49, 0x20, 0x6c, 0x69, + 0x6b, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, + 0x68, 0x61, 0x70, 0x65, 0x2e, 0x00, 0x00, 0x4b, + 0x00, 0x70, 0x61, 0x73, 0x73, 0x00, 0x22, 0x4c, + 0x65, 0x74, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, + 0x67, 0x75, 0x79, 0x20, 0x69, 0x6e, 0x2e, 0x20, + 0x52, 0x47, 0x42, 0x20, 0x43, 0x68, 0x69, 0x65, + 0x66, 0x2e, 0x22, 0x00, 0x00, 0x4c, 0x00, 0x62, + 0x75, 0x6c, 0x62, 0x00, 0x49, 0x74, 0x27, 0x73, + 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, + 0x65, 0x6c, 0x79, 0x20, 0x75, 0x73, 0x65, 0x6c, + 0x65, 0x73, 0x73, 0x2e, 0x00, 0x00, 0x4d, 0x00, + 0x6a, 0x61, 0x69, 0x6c, 0x20, 0x6b, 0x65, 0x79, + 0x00, 0x53, 0x75, 0x72, 0x70, 0x72, 0x69, 0x73, + 0x69, 0x6e, 0x67, 0x6c, 0x79, 0x2c, 0x20, 0x69, + 0x74, 0x27, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x6b, 0x65, 0x79, 0x20, 0x74, 0x6f, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x6a, 0x61, 0x69, 0x6c, 0x2e, + 0x00, 0x00, 0x4e, 0x00, 0x64, 0x65, 0x6c, 0x69, + 0x63, 0x61, 0x74, 0x65, 0x20, 0x70, 0x6c, 0x61, + 0x6e, 0x74, 0x00, 0x57, 0x68, 0x6f, 0x61, 0x2c, + 0x20, 0x69, 0x74, 0x20, 0x74, 0x69, 0x63, 0x6b, + 0x6c, 0x65, 0x73, 0x21, 0x00, 0x00, 0x4f, 0x00, + 0x53, 0x77, 0x69, 0x73, 0x73, 0x20, 0x41, 0x72, + 0x6d, 0x79, 0x20, 0x6b, 0x6e, 0x69, 0x66, 0x65, + 0x00, 0x49, 0x20, 0x77, 0x6f, 0x6e, 0x64, 0x65, + 0x72, 0x20, 0x69, 0x66, 0x20, 0x69, 0x74, 0x27, + 0x73, 0x20, 0x67, 0x6f, 0x74, 0x20, 0x61, 0x20, + 0x54, 0x56, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, + 0x65, 0x73, 0x65, 0x20, 0x74, 0x6f, 0x6f, 0x2e, + 0x00, 0x00, 0x50, 0x00, 0x73, 0x70, 0x72, 0x69, + 0x6e, 0x67, 0x00, 0x49, 0x20, 0x63, 0x6f, 0x75, + 0x6c, 0x64, 0x20, 0x6c, 0x61, 0x75, 0x6e, 0x63, + 0x68, 0x20, 0x61, 0x20, 0x72, 0x6f, 0x63, 0x6b, + 0x65, 0x74, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, + 0x69, 0x74, 0x21, 0x00, 0x00, 0x51, 0x00, 0x73, + 0x68, 0x6f, 0x76, 0x65, 0x6c, 0x00, 0x4e, 0x69, + 0x63, 0x65, 0x2c, 0x20, 0x68, 0x61, 0x6e, 0x64, + 0x79, 0x20, 0x74, 0x6f, 0x6f, 0x6c, 0x2e, 0x00, + 0x00, 0x52, 0x00, 0x6b, 0x61, 0x6c, 0x65, 0x69, + 0x64, 0x6f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x00, + 0x49, 0x20, 0x63, 0x6f, 0x75, 0x6c, 0x64, 0x20, + 0x73, 0x70, 0x65, 0x6e, 0x64, 0x20, 0x6d, 0x79, + 0x20, 0x77, 0x68, 0x6f, 0x6c, 0x65, 0x20, 0x6c, + 0x69, 0x66, 0x65, 0x20, 0x6c, 0x6f, 0x6f, 0x6b, + 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x72, 0x6f, + 0x75, 0x67, 0x68, 0x00, 0x74, 0x68, 0x69, 0x73, + 0x2e, 0x20, 0x57, 0x65, 0x6c, 0x6c, 0x2c, 0x20, + 0x6c, 0x65, 0x74, 0x27, 0x73, 0x20, 0x73, 0x61, + 0x79, 0x20, 0x66, 0x69, 0x76, 0x65, 0x20, 0x6d, + 0x69, 0x6e, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x00, + 0x4f, 0x68, 0x2c, 0x20, 0x61, 0x6c, 0x6c, 0x20, + 0x72, 0x69, 0x67, 0x68, 0x74, 0x2c, 0x20, 0x69, + 0x74, 0x27, 0x73, 0x20, 0x62, 0x6f, 0x72, 0x69, + 0x6e, 0x67, 0x2e, 0x00, 0x00, 0x53, 0x00, 0x22, + 0x53, 0x6f, 0x6c, 0x64, 0x69, 0x65, 0x72, 0x20, + 0x4e, 0x65, 0x77, 0x73, 0x22, 0x00, 0x49, 0x74, + 0x27, 0x73, 0x20, 0x6a, 0x75, 0x73, 0x74, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x73, 0x61, 0x6d, 0x65, + 0x20, 0x61, 0x73, 0x20, 0x77, 0x6f, 0x6d, 0x65, + 0x6e, 0x27, 0x73, 0x20, 0x6d, 0x61, 0x67, 0x61, + 0x7a, 0x69, 0x6e, 0x65, 0x73, 0x2c, 0x00, 0x62, + 0x75, 0x74, 0x20, 0x74, 0x68, 0x65, 0x72, 0x65, + 0x20, 0x61, 0x72, 0x65, 0x20, 0x67, 0x75, 0x6e, + 0x73, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x65, 0x61, + 0x64, 0x20, 0x6f, 0x66, 0x20, 0x70, 0x65, 0x72, + 0x66, 0x75, 0x6d, 0x65, 0x73, 0x2e, 0x00, 0x49, + 0x74, 0x27, 0x73, 0x20, 0x63, 0x6f, 0x6f, 0x6c, + 0x20, 0x61, 0x6e, 0x79, 0x77, 0x61, 0x79, 0x2e, + 0x00, 0x00, 0x54, 0x00, 0x67, 0x72, 0x65, 0x6e, + 0x61, 0x64, 0x65, 0x00, 0x49, 0x20, 0x6d, 0x75, + 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x63, 0x72, + 0x61, 0x7a, 0x79, 0x20, 0x74, 0x6f, 0x20, 0x70, + 0x75, 0x74, 0x20, 0x69, 0x6e, 0x20, 0x6d, 0x79, + 0x20, 0x70, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2e, + 0x00, 0x53, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x69, + 0x6e, 0x67, 0x20, 0x68, 0x6f, 0x72, 0x72, 0x69, + 0x62, 0x6c, 0x65, 0x20, 0x6d, 0x69, 0x67, 0x68, + 0x74, 0x20, 0x68, 0x61, 0x70, 0x70, 0x65, 0x6e, + 0x2e, 0x2e, 0x2e, 0x00, 0x00, 0x55, 0x00, 0x6d, + 0x75, 0x67, 0x00, 0x49, 0x74, 0x27, 0x73, 0x20, + 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x00, 0x00, + 0x56, 0x00, 0x6d, 0x75, 0x67, 0x20, 0x66, 0x75, + 0x6c, 0x6c, 0x20, 0x6f, 0x66, 0x20, 0x6d, 0x75, + 0x64, 0x00, 0x57, 0x68, 0x79, 0x20, 0x64, 0x69, + 0x64, 0x20, 0x49, 0x20, 0x66, 0x69, 0x6c, 0x6c, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x75, 0x67, + 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x6d, 0x75, + 0x64, 0x3f, 0x00, 0x00, 0x54, 0x68, 0x61, 0x74, + 0x20, 0x6d, 0x61, 0x6b, 0x65, 0x73, 0x20, 0x6e, + 0x6f, 0x20, 0x73, 0x65, 0x6e, 0x73, 0x65, 0x21, + 0x00, 0x00, 0x57, 0x00, 0x63, 0x72, 0x75, 0x6d, + 0x62, 0x73, 0x00, 0x54, 0x68, 0x65, 0x73, 0x65, + 0x20, 0x61, 0x72, 0x65, 0x20, 0x73, 0x6f, 0x6d, + 0x65, 0x20, 0x72, 0x65, 0x6d, 0x61, 0x69, 0x6e, + 0x73, 0x20, 0x6f, 0x66, 0x20, 0x62, 0x72, 0x65, + 0x61, 0x64, 0x2e, 0x00, 0x00, 0x58, 0x00, 0x72, + 0x6f, 0x70, 0x65, 0x00, 0x49, 0x74, 0x27, 0x73, + 0x20, 0x74, 0x68, 0x69, 0x6e, 0x20, 0x61, 0x6e, + 0x64, 0x20, 0x6c, 0x6f, 0x6e, 0x67, 0x2e, 0x00, + 0x00, 0x59, 0x00, 0x72, 0x6f, 0x70, 0x65, 0x20, + 0x74, 0x69, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, + 0x67, 0x72, 0x65, 0x6e, 0x61, 0x64, 0x65, 0x00, + 0x4d, 0x79, 0x20, 0x70, 0x61, 0x74, 0x65, 0x6e, + 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x20, + 0x62, 0x6f, 0x6d, 0x62, 0x61, 0x73, 0x74, 0x69, + 0x63, 0x20, 0x79, 0x6f, 0x79, 0x6f, 0x2e, 0x00, + 0x00, 0x5a, 0x00, 0x6d, 0x65, 0x64, 0x69, 0x63, + 0x69, 0x6e, 0x65, 0x00, 0x54, 0x68, 0x65, 0x79, + 0x20, 0x6c, 0x6f, 0x6f, 0x6b, 0x20, 0x6c, 0x69, + 0x6b, 0x65, 0x20, 0x73, 0x6c, 0x65, 0x65, 0x70, + 0x69, 0x6e, 0x67, 0x20, 0x70, 0x69, 0x6c, 0x6c, + 0x73, 0x2e, 0x00, 0x41, 0x6c, 0x74, 0x68, 0x6f, + 0x75, 0x67, 0x68, 0x20, 0x49, 0x20, 0x64, 0x6f, + 0x6e, 0x27, 0x74, 0x20, 0x68, 0x61, 0x76, 0x65, + 0x20, 0x61, 0x6e, 0x79, 0x20, 0x69, 0x64, 0x65, + 0x61, 0x20, 0x77, 0x68, 0x79, 0x2e, 0x00, 0x00, + 0x5b, 0x00, 0x64, 0x72, 0x75, 0x67, 0x67, 0x65, + 0x64, 0x20, 0x66, 0x6f, 0x6f, 0x64, 0x00, 0x53, + 0x6d, 0x65, 0x6c, 0x6c, 0x73, 0x20, 0x6c, 0x69, + 0x6b, 0x65, 0x20, 0x54, 0x65, 0x65, 0x6e, 0x20, + 0x53, 0x70, 0x69, 0x72, 0x69, 0x74, 0x2e, 0x00, + 0x00, 0x5c, 0x01, 0x62, 0x69, 0x72, 0x64, 0x00, + 0x49, 0x74, 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, + 0x20, 0x6d, 0x61, 0x6b, 0x65, 0x20, 0x61, 0x20, + 0x67, 0x72, 0x65, 0x61, 0x74, 0x20, 0x64, 0x69, + 0x6e, 0x6e, 0x2e, 0x2e, 0x2e, 0x20, 0x49, 0x20, + 0x6d, 0x65, 0x61, 0x6e, 0x00, 0x66, 0x72, 0x69, + 0x65, 0x6e, 0x64, 0x2c, 0x20, 0x6f, 0x66, 0x20, + 0x63, 0x6f, 0x75, 0x72, 0x73, 0x65, 0x2e, 0x00, + 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x01, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x01, 0xff, 0x00, 0x00, 0x02, 0xff, 0xff, + 0xff, 0x01, 0xff, 0x00, 0x00, 0x02, 0xff, 0x00, + 0x00, 0x01, 0xff, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x06, 0xff, 0xff, 0x00, 0x02, 0xff, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x01, 0x02, 0xff, 0x00, 0x04, 0xff, 0x00, + 0x00, 0xff, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, + 0x00, 0x0a, 0xff, 0x00, 0x00, 0x01, 0x0d, 0x00, + 0x00, 0x01, 0x02, 0xff, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x06, 0x07, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0x01, 0x04, 0x02, 0x03, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x01, 0xff, 0xff, + 0x00, 0x01, 0x02, 0xff, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x04, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0x01, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, + 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0xf8, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x28, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x67, 0x7c, 0x04, 0x4f, 0x29, 0x00, + 0x00, 0xec, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x2a, 0x66, 0x37, 0x03, 0xca, 0x0c, 0x00, + 0x00, 0x75, 0xbf, 0x91, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x99, 0x5f, 0x87, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x64, 0x34, 0xbe, 0x47, 0xad, 0x03, 0x86, + 0x02, 0x18, 0x20, 0xd4, 0x02, 0x00, 0x00, 0x00, + 0x00, 0xd6, 0x22, 0x58, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x67, 0xac, 0xf3, 0x00, 0x00, 0x00, + 0x00, 0xc3, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xdf, 0x27, 0xc8, 0x4d, 0x64, 0x08, 0x00, + 0x00, 0x4e, 0x7e, 0x5c, 0xe6, 0x00, 0x00, 0x00, + 0x00, 0x69, 0x0d, 0x27, 0x0d, 0x00, 0x00, 0x00, + 0x00, 0x4a, 0x0d, 0x45, 0x0d, 0x00, 0x00, 0x00, + 0x00, 0xb9, 0x48, 0x71, 0x12, 0x04, 0x27, 0x00, + 0x00, 0xdb, 0x73, 0xfe, 0x02, 0x00, 0x00, 0x00, + 0x00, 0xfc, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x5b, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x13, 0x40, 0x51, 0x93, 0x00, 0x00, 0x00, + 0x00, 0x13, 0x4f, 0x4f, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x7a, 0x50, 0x64, 0x5d, 0x6f, 0x04, 0x00, + 0x00, 0xa3, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x88, 0x02, 0x91, 0x02, 0x00, 0x00, 0x00, + 0x00, 0xce, 0x02, 0x77, 0x04, 0x2f, 0x1d, 0x00, + 0x00, 0x08, 0x69, 0x06, 0x23, 0x32, 0x4b, 0x3e, + 0x6c, 0x46, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf6, 0x16, 0xc5, 0x13, 0x00, 0x00, 0x00, + 0x00, 0xfb, 0x0a, 0x21, 0x73, 0x68, 0x3b, 0x00, + 0x00, 0x9b, 0x15, 0x0d, 0x1a, 0x00, 0x00, 0x00, + 0x00, 0xce, 0xf7, 0x25, 0x6b, 0xcb, 0x13, 0x00, + 0x00, 0x33, 0xa5, 0xbf, 0x01, 0xb3, 0x10, 0x00, + 0x00, 0xcb, 0x13, 0x7c, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x7e, 0x0f, 0x14, 0x59, 0xc5, 0x13, 0x00, + 0x00, 0x95, 0x51, 0xce, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x4d, 0x29, 0xfc, 0x14, 0xe1, 0xa4, 0xad, + 0x8e, 0x07, 0x5b, 0x47, 0x02, 0x00, 0x00, 0x00, + 0x00, 0xbf, 0x52, 0x3c, 0x0e, 0xc5, 0x0e, 0x00, + 0x00, 0x44, 0x6b, 0xe0, 0x76, 0x97, 0x00, 0x00, + 0x00, 0x5a, 0x1a, 0xb8, 0xb3, 0x00, 0x00, 0x00, + 0x00, 0x6c, 0x66, 0x8f, 0xdc, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0xb4, 0x01, 0x6f, 0x04, 0xcb, + 0x07, 0x01, 0x09, 0xff, 0xff, 0x36, 0x09, 0x02, + 0x0a, 0x2d, 0x0a, 0xff, 0xff, 0x43, 0x0c, 0x75, + 0x0d, 0x0e, 0x0f, 0xb8, 0x0f, 0xff, 0xff, 0xdb, + 0x10, 0xac, 0x11, 0xd9, 0x12, 0x68, 0x14, 0xff, + 0xff, 0x85, 0x14, 0x28, 0x15, 0xff, 0xff, 0xde, + 0x16, 0x26, 0x17, 0xff, 0xff, 0x52, 0x17, 0x8c, + 0x17, 0x13, 0x19, 0x3e, 0x1a, 0x63, 0x1a, 0x84, + 0x1a, 0xc9, 0x1a, 0xf7, 0x1a, 0x27, 0x1b, 0x4a, + 0x1b, 0xff, 0xff, 0x97, 0x1c, 0xec, 0x1c, 0xff, + 0xff, 0x2c, 0x1d, 0xd8, 0x1d, 0xff, 0xff, 0x41, + 0x20, 0x5f, 0x21, 0xff, 0xff, 0x82, 0x25, 0xfe, + 0x25, 0xff, 0xff, 0x6f, 0x2b, 0xb2, 0x2b, 0xdd, + 0x2b, 0x5d, 0x2c, 0x9b, 0x2c, 0xff, 0xff, 0x02, + 0x31, 0x1b, 0x31, 0x37, 0x31, 0x6f, 0x31, 0xff, + 0xff, 0x7d, 0x31, 0x15, 0x32, 0xff, 0xff, 0x41, + 0x3e, 0xa3, 0x3e, 0x08, 0x3f, 0xea, 0x3f, 0xff, + 0xff, 0x0d, 0x40, 0x39, 0x44, 0x6a, 0x44, 0xff, + 0xff, 0xe2, 0x58, 0xc5, 0x59, 0x9d, 0x5a, 0xd7, + 0x5a, 0xff, 0xff, 0xf3, 0x5a, 0xa9, 0x5b, 0x53, + 0x5c, 0x99, 0x5c, 0x3e, 0x5d, 0xff, 0xff, 0x5b, + 0x5d, 0x0a, 0x5f, 0xae, 0x5f, 0xcf, 0x5f, 0xff, + 0xff, 0xfe, 0x5f, 0x88, 0x60, 0xff, 0xff, 0xf0, + 0x62, 0x18, 0x63, 0x47, 0x63, 0x6b, 0x63, 0x82, + 0x63, 0xff, 0xff, 0xf7, 0x6b, 0x7f, 0x6d, 0x7b, + 0x6e, 0xa4, 0x6e, 0xff, 0xff, 0xc0, 0x6e, 0xe9, + 0x6e, 0x03, 0x6f, 0xff, 0xff, 0x5e, 0x73, 0xb3, + 0x73, 0xe8, 0x73, 0xff, 0xff, 0xfc, 0x64, 0x4c, + 0x65, 0xab, 0x65, 0xff, 0xff, 0x03, 0x74, 0x59, + 0x74, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdd, 0xec, 0x50, + 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, + 0x6e, 0x64, 0x73, 0x00, 0x69, 0x41, 0x4e, 0x44, + 0x52, 0x5a, 0x45, 0x4a, 0x20, 0x44, 0x4f, 0x42, + 0x52, 0x5a, 0x59, 0x3b, 0x53, 0x4b, 0x49, 0x00, + 0x00, 0xe3, 0xd2, 0x50, 0x6d, 0x75, 0x73, 0x69, + 0x63, 0x00, 0x69, 0x52, 0x41, 0x44, 0x45, 0x4b, + 0x20, 0x53, 0x5a, 0x41, 0x4d, 0x52, 0x45, 0x4a, + 0x00, 0x00, 0xd7, 0xe6, 0x50, 0x61, 0x6e, 0x69, + 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, + 0x6e, 0x64, 0x20, 0x67, 0x72, 0x61, 0x70, 0x68, + 0x69, 0x63, 0x73, 0x00, 0x69, 0x47, 0x52, 0x5a, + 0x45, 0x47, 0x4f, 0x52, 0x5a, 0x20, 0x4d, 0x49, + 0x45, 0x43, 0x48, 0x4f, 0x57, 0x53, 0x4b, 0x49, + 0x00, 0x00, 0xd9, 0xe8, 0x50, 0x70, 0x72, 0x6f, + 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x69, 0x6e, 0x67, + 0x20, 0x61, 0x6e, 0x64, 0x20, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x00, 0x69, 0x41, 0x44, 0x52, + 0x49, 0x41, 0x4e, 0x20, 0x43, 0x48, 0x4d, 0x49, + 0x45, 0x4c, 0x41, 0x52, 0x5a, 0x00, 0x00, 0xdd, + 0xec, 0x5c, 0x61, 0x66, 0x74, 0x65, 0x72, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x74, 0x69, 0x72, 0x69, + 0x6e, 0x67, 0x20, 0x6a, 0x6f, 0x75, 0x72, 0x6e, + 0x65, 0x79, 0x2e, 0x2e, 0x2e, 0x00, 0x00, 0xdd, + 0xec, 0x5c, 0x54, 0x48, 0x45, 0x20, 0x45, 0x4e, + 0x44, 0x00, 0x00, 0x70, 0x72, 0x6f, 0x67, 0x72, + 0x61, 0x6d, 0x6d, 0x69, 0x6e, 0x67, 0x00, 0x41, + 0x44, 0x52, 0x49, 0x41, 0x4e, 0x20, 0x43, 0x48, + 0x4d, 0x49, 0x45, 0x4c, 0x41, 0x52, 0x5a, 0x00, + 0x20, 0x00, 0x20, 0x00, 0x61, 0x6e, 0x69, 0x6d, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x47, 0x52, + 0x5a, 0x45, 0x47, 0x4f, 0x52, 0x5a, 0x20, 0x4d, + 0x49, 0x45, 0x43, 0x48, 0x4f, 0x57, 0x53, 0x4b, + 0x49, 0x00, 0x20, 0x00, 0x20, 0x00, 0x61, 0x64, + 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, + 0x20, 0x61, 0x6e, 0x69, 0x6d, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x00, 0x54, 0x4f, 0x4d, 0x41, + 0x53, 0x5a, 0x20, 0x50, 0x49, 0x4c, 0x49, 0x4b, + 0x00, 0x20, 0x00, 0x20, 0x00, 0x62, 0x61, 0x63, + 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x73, + 0x00, 0x41, 0x4e, 0x44, 0x52, 0x5a, 0x45, 0x4a, + 0x20, 0x44, 0x4f, 0x42, 0x52, 0x5a, 0x59, 0x4e, + 0x53, 0x4b, 0x49, 0x00, 0x20, 0x00, 0x20, 0x00, + 0x6d, 0x75, 0x73, 0x69, 0x63, 0x00, 0x52, 0x41, + 0x44, 0x45, 0x4b, 0x20, 0x53, 0x5a, 0x41, 0x4d, + 0x52, 0x45, 0x4a, 0x00, 0x20, 0x00, 0x20, 0x00, + 0x63, 0x6f, 0x76, 0x65, 0x72, 0x20, 0x61, 0x72, + 0x74, 0x00, 0x44, 0x41, 0x52, 0x49, 0x55, 0x53, + 0x5a, 0x20, 0x41, 0x4e, 0x41, 0x43, 0x4b, 0x49, + 0x00, 0x20, 0x00, 0x20, 0x00, 0x74, 0x72, 0x61, + 0x6e, 0x73, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x20, 0x68, 0x65, 0x6c, 0x70, 0x00, 0x50, 0x45, + 0x54, 0x45, 0x52, 0x20, 0x57, 0x45, 0x4c, 0x4c, + 0x53, 0x00, 0x20, 0x00, 0x20, 0x00, 0x62, 0x65, + 0x74, 0x61, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, + 0x73, 0x00, 0x54, 0x4f, 0x4d, 0x41, 0x53, 0x5a, + 0x20, 0x46, 0x55, 0x52, 0x4d, 0x41, 0x4e, 0x49, + 0x55, 0x4b, 0x00, 0x50, 0x41, 0x54, 0x52, 0x59, + 0x4b, 0x20, 0x53, 0x41, 0x57, 0x49, 0x43, 0x4b, + 0x49, 0x00, 0x50, 0x41, 0x57, 0x45, 0x4c, 0x20, + 0x4d, 0x49, 0x45, 0x43, 0x48, 0x4f, 0x57, 0x53, + 0x4b, 0x49, 0x00, 0x4d, 0x41, 0x52, 0x45, 0x4b, + 0x20, 0x43, 0x48, 0x4d, 0x49, 0x45, 0x4c, 0x41, + 0x52, 0x5a, 0x00, 0x4a, 0x45, 0x44, 0x52, 0x45, + 0x4b, 0x20, 0x57, 0x49, 0x43, 0x48, 0x41, 0x00, + 0x4d, 0x52, 0x2e, 0x20, 0x4a, 0x4f, 0x48, 0x4e, + 0x20, 0x44, 0x4f, 0x45, 0x00, 0x4d, 0x41, 0x52, + 0x43, 0x49, 0x4e, 0x20, 0x44, 0x52, 0x45, 0x57, + 0x53, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, + 0x69, 0x64, 0x65, 0x61, 0x73, 0x00, 0x41, 0x44, + 0x52, 0x49, 0x41, 0x4e, 0x20, 0x43, 0x48, 0x4d, + 0x49, 0x45, 0x4c, 0x41, 0x52, 0x5a, 0x00, 0x47, + 0x52, 0x5a, 0x45, 0x47, 0x4f, 0x52, 0x5a, 0x20, + 0x4d, 0x49, 0x45, 0x43, 0x48, 0x4f, 0x57, 0x53, + 0x4b, 0x49, 0x00, 0x41, 0x4e, 0x44, 0x52, 0x5a, + 0x45, 0x4a, 0x20, 0x53, 0x41, 0x57, 0x49, 0x43, + 0x4b, 0x49, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, + 0x00, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x00, 0x4a, + 0x41, 0x52, 0x4f, 0x53, 0x5d, 0x41, 0x57, 0x20, + 0x57, 0x45, 0x49, 0x53, 0x53, 0x00, 0x41, 0x47, + 0x45, 0x4e, 0x43, 0x4a, 0x41, 0x20, 0x53, 0x54, + 0x59, 0x4c, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, + 0x00, 0x74, 0x68, 0x61, 0x6e, 0x6b, 0x73, 0x00, + 0x48, 0x45, 0x4e, 0x52, 0x59, 0x20, 0x4b, 0x55, + 0x54, 0x54, 0x4e, 0x45, 0x52, 0x00, 0x55, 0x2d, + 0x4b, 0x4e, 0x4f, 0x57, 0x2d, 0x57, 0x48, 0x4f, + 0x2d, 0x55, 0x2d, 0x52, 0x2d, 0x42, 0x55, 0x54, + 0x2d, 0x57, 0x41, 0x4e, 0x54, 0x2d, 0x32, 0x2d, + 0x53, 0x54, 0x41, 0x59, 0x2d, 0x49, 0x4e, 0x2d, + 0x53, 0x48, 0x41, 0x44, 0x4f, 0x57, 0x00, 0x45, + 0x50, 0x49, 0x43, 0x20, 0x4d, 0x45, 0x47, 0x41, + 0x47, 0x41, 0x4d, 0x45, 0x53, 0x00, 0x58, 0x4c, + 0x41, 0x4e, 0x44, 0x20, 0x53, 0x4f, 0x46, 0x54, + 0x57, 0x41, 0x52, 0x45, 0x20, 0x50, 0x55, 0x42, + 0x4c, 0x49, 0x53, 0x48, 0x49, 0x4e, 0x47, 0x00, + 0x4b, 0x41, 0x54, 0x41, 0x52, 0x5a, 0x59, 0x4e, + 0x41, 0x20, 0x4d, 0x49, 0x45, 0x43, 0x48, 0x4f, + 0x57, 0x53, 0x4b, 0x41, 0x00, 0x20, 0x00, 0x20, + 0x00, 0x20, 0x00, 0x73, 0x70, 0x65, 0x63, 0x69, + 0x61, 0x6c, 0x20, 0x74, 0x68, 0x61, 0x6e, 0x6b, + 0x73, 0x00, 0x41, 0x4e, 0x44, 0x52, 0x5a, 0x45, + 0x4a, 0x20, 0x4d, 0x49, 0x43, 0x48, 0x41, 0x4c, + 0x41, 0x4b, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, + 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, + 0x00, 0x20, 0x00, 0x20, 0x00, 0x70, 0x72, 0x6f, + 0x64, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x00, + 0x4d, 0x45, 0x54, 0x52, 0x4f, 0x50, 0x4f, 0x4c, + 0x49, 0x53, 0x20, 0x53, 0x4f, 0x46, 0x54, 0x57, + 0x41, 0x52, 0x45, 0x20, 0x48, 0x4f, 0x55, 0x53, + 0x45, 0x00, 0x28, 0x63, 0x29, 0x20, 0x31, 0x39, + 0x39, 0x34, 0x2d, 0x31, 0x39, 0x39, 0x35, 0x00, + 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x41, 0x6c, + 0x6c, 0x20, 0x61, 0x6c, 0x6c, 0x75, 0x73, 0x69, + 0x6f, 0x6e, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, + 0x70, 0x75, 0x6e, 0x73, 0x00, 0x61, 0x72, 0x65, + 0x20, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x69, + 0x6f, 0x6e, 0x61, 0x6c, 0x00, 0x20, 0x00, 0x20, + 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, + 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, + 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, + 0x00, 0x20, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00 +}; + +// Dialog Strings Block + +#define ANIM_WAIT "\xff" +#define NEW_LINE "\n" +#define DISPLAY_MESSAGE "\n\n" +#define CHANGE_CHARACTER "\n\n\n" +#define END_DIALOG "\n\n\n\n" + +const static char* dialog_0[] = { + ANIM_WAIT, + "Good day.", + CHANGE_CHARACTER, + "Yeah.", + CHANGE_CHARACTER, + "Why are you standing here?", + CHANGE_CHARACTER, + "It's a question of gravitation.", + CHANGE_CHARACTER, + "Extremely funny joke.", + DISPLAY_MESSAGE, + "For a soldier.", + CHANGE_CHARACTER, + "I'm not a soldier, although I tried", + NEW_LINE, + "to be.", + DISPLAY_MESSAGE, + "I didn't pass the intell...", + NEW_LINE, + "the physical test.", + DISPLAY_MESSAGE, + "They ordered me to shoot at", + NEW_LINE, + "a thrown coin when jumping", + NEW_LINE, + "from the tree onto a horse.", + CHANGE_CHARACTER, + "Yep, that seems hard.", + CHANGE_CHARACTER, + "Special Forces ain't a piece of cake,", + NEW_LINE, + "you know.", + CHANGE_CHARACTER, + "I'm sorry you didn't make it.", + CHANGE_CHARACTER, + "Yeah, I missed the horse.", + END_DIALOG +}; + +const static char* dialog_1[] = { + "So...", + DISPLAY_MESSAGE, + "What are you doing now?", + CHANGE_CHARACTER, + "Wanna hear a funny answer?", + CHANGE_CHARACTER, + "Please don't bother.", + CHANGE_CHARACTER, + "All right, but you know, there's not much", + NEW_LINE, + "entertainment here.", + DISPLAY_MESSAGE, + "Sometimes I like to joke a bit.", + NEW_LINE, + "Or a byte, heh-heh!", + CHANGE_CHARACTER, + "Well then...", + CHANGE_CHARACTER, + "I'm guarding this place...", + CHANGE_CHARACTER, + "Wow. That's a surprise.", + CHANGE_CHARACTER, + "...And I'm told to kill ANYBODY", + NEW_LINE, + "who tries to get in.", + CHANGE_CHARACTER, + "What about the owner?", + CHANGE_CHARACTER, + "He's not just ANYBODY, you know.", + CHANGE_CHARACTER, + "I guess you won't let ME in then?", + CHANGE_CHARACTER, + "Bingo.", + CHANGE_CHARACTER, + "Even if say I please?", + CHANGE_CHARACTER, + "No way, kiddo.", + CHANGE_CHARACTER, + ANIM_WAIT, + "PLEEEEEASE.", + CHANGE_CHARACTER, + "Forget it.", + NEW_LINE, + "I've got a heart of stone.", + CHANGE_CHARACTER, + "Like your brain.", + CHANGE_CHARACTER, + ANIM_WAIT, + "I don't follow.", + CHANGE_CHARACTER, + "Never mind.", + NEW_LINE, + "How can I soften your stone heart?", + CHANGE_CHARACTER, + "You can't. I'm a really tough guy.", + DISPLAY_MESSAGE, + "But come here, I'll give you", + NEW_LINE, + "a consolation...", + END_DIALOG +}; + +const static char* dialog_2[] = { + "Thanks. What is it?", + CHANGE_CHARACTER, + "Chocolate candy.", + DISPLAY_MESSAGE, + "My employer gave me a few of these", + NEW_LINE, + "for lunch and...", + CHANGE_CHARACTER, + "Is your employer home?!", + CHANGE_CHARACTER, + "Mr. John Noty? Yeah, why?", + CHANGE_CHARACTER, + "Oh, nothing...", + DISPLAY_MESSAGE, + "John Noty...", + DISPLAY_MESSAGE, + "I think I've heard of him...", + CHANGE_CHARACTER, + "You should have. He's making big money,", + NEW_LINE, + "you know.", + DISPLAY_MESSAGE, + "Especially lately...", + CHANGE_CHARACTER, + "Yeah?...", + CHANGE_CHARACTER, + "Well, I don't know how.", + DISPLAY_MESSAGE, + "Maybe it has something to do with", + NEW_LINE, + "the mad scientist that came round", + NEW_LINE, + "one day...", + DISPLAY_MESSAGE, + "Oh, you think you're smart, don't you?!", + NEW_LINE, + "Are you a spy?!", + CHANGE_CHARACTER, + "Hey, I'm just a common homeboy.", + DISPLAY_MESSAGE, + "If you don't wanna talk,", + NEW_LINE, + "don't talk.", + CHANGE_CHARACTER, + "Mr. Bad Gay told me to watch", + NEW_LINE, + "for spies...", + CHANGE_CHARACTER, + "Do I really look like a spy?", + CHANGE_CHARACTER, + "...And kill them immediately...", + CHANGE_CHARACTER, + "Hey, chill...", + CHANGE_CHARACTER, + "...And I haven't killed anybody", + NEW_LINE, + "for a loooong time...", + CHANGE_CHARACTER, + "It's about this candy, isn't it?", + NEW_LINE, + "Do you want it back? No problem.", + CHANGE_CHARACTER, + ANIM_WAIT, + "Sorry, I just got carried away.", + CHANGE_CHARACTER, + "I understand.", + NEW_LINE, + "It's hot around here.", + CHANGE_CHARACTER, + "Yeah.", + END_DIALOG +}; + +const static char* dialog_3[] = { + "Mister guard, I...", + CHANGE_CHARACTER, + "Listen, boy.", + DISPLAY_MESSAGE, + "A spy or not a spy, it's out of", + NEW_LINE, + "the question.", + DISPLAY_MESSAGE, + "You won't get in, no matter if you just want", + NEW_LINE, + "to visit the place, steal something or", + NEW_LINE, + "talk to Mr. John Noty.", + DISPLAY_MESSAGE, + "Also you won't make me talk.", + DISPLAY_MESSAGE, + "One more try and I'll make a few", + NEW_LINE, + "highways for worms.", + DISPLAY_MESSAGE, + "In your body.", + DISPLAY_MESSAGE, + "Got it?", + CHANGE_CHARACTER, + "Got it.", + END_DIALOG +}; + +const static char* dialog_4[] = { + "Nice suit.", + CHANGE_CHARACTER, + "Yeah.", + END_DIALOG +}; + +const static char* dialog_5[] = { + CHANGE_CHARACTER, + "Damn!", + DISPLAY_MESSAGE, + "It's only you!...", + END_DIALOG +}; + +const static char* dialog_6[] = { + "Hey!", + CHANGE_CHARACTER, + "What?", + CHANGE_CHARACTER, + "What's in this bottle?", + CHANGE_CHARACTER, + "You can't prove anything!", + CHANGE_CHARACTER, + "Something hot, I guess?..", + CHANGE_CHARACTER, + "None of your business.", + CHANGE_CHARACTER, + "Drinking on duty, huh?", + CHANGE_CHARACTER, + "You think you've almost got me,", + NEW_LINE, + "don't you?", + DISPLAY_MESSAGE, + "Forget it.", + END_DIALOG +}; + +const static char* dialog_7[] = { + "Hey!", + CHANGE_CHARACTER, + "You've seen nothing.", + DISPLAY_MESSAGE, + "I'm clean.", + END_DIALOG +}; + +const static char* dialog_8[] = { + "Hey!", + CHANGE_CHARACTER, + "Get lost.", + END_DIALOG +}; + +const static char* dialog_9[] = { + "What would you say if I gave you some", + NEW_LINE, + "gold?...", + CHANGE_CHARACTER, + "I'd say thanks.", + CHANGE_CHARACTER, + "Would you let me in?", + CHANGE_CHARACTER, + "I guess so...", + CHANGE_CHARACTER, + "You have to be sure.", + CHANGE_CHARACTER, + "OK, I'm sure. I will let you in.", + CHANGE_CHARACTER, + "All right. Here we go.", + END_DIALOG +}; + +const static char* dialog_10[] = { + "Now please open the door.", + CHANGE_CHARACTER, + "No way. Now buzz off.", + CHANGE_CHARACTER, + "Hey! I gave you the gold, remember?...", + CHANGE_CHARACTER, + "What gold?", + CHANGE_CHARACTER, + "WHAT GOLD?!?", + CHANGE_CHARACTER, + "I don't know nothing 'bout any gold.", + END_DIALOG +}; + +const static char* dialog_11[] = { + "You... You...", + CHANGE_CHARACTER, + "Buzz off.", + CHANGE_CHARACTER, + "You said you'd let me in!", + DISPLAY_MESSAGE, + "But you've let me down!", + CHANGE_CHARACTER, + "Yeah, but I'll let you off.", + END_DIALOG +}; + +const static char* dialog_12[] = { + "You can't even trust corrupt", + NEW_LINE, + "guards these days.", + END_DIALOG +}; + +const static char* dialog_13[] = { + "Hi.", + CHANGE_CHARACTER, + "Hello.", + CHANGE_CHARACTER, + "I'm Mark.", + DISPLAY_MESSAGE, + "What's your name?", + CHANGE_CHARACTER, + "What's my name?", + CHANGE_CHARACTER, + "I don't know, you tell me.", + CHANGE_CHARACTER, + "Me.", + CHANGE_CHARACTER, + "Don't tell me 'me', just say", + NEW_LINE, + "your name!", + CHANGE_CHARACTER, + "Your na..", + CHANGE_CHARACTER, + "Gee!", + DISPLAY_MESSAGE, + "What did your dad call you?", + CHANGE_CHARACTER, + "Sonny.", + CHANGE_CHARACTER, + "Sonny as in the name or sonny", + NEW_LINE, + "as in son?", + CHANGE_CHARACTER, + "Sonny.", + CHANGE_CHARACTER, + "Are you stupid or just rude?", + CHANGE_CHARACTER, + "Sonny, I am.", + CHANGE_CHARACTER, + "(sigh)", + END_DIALOG +}; + +const static char* dialog_14[] = { + "Listen, Sonny or whatever.", + DISPLAY_MESSAGE, + "What are you trying to do", + NEW_LINE, + "with this ball?", + CHANGE_CHARACTER, + "Well, grandpa said he'd take", + NEW_LINE, + "me to ZOO if I score.", + CHANGE_CHARACTER, + "I think you should throw the ball", + NEW_LINE, + "a little bit higher.", + CHANGE_CHARACTER, + "Yeah, I know.", + CHANGE_CHARACTER, + "So?", + CHANGE_CHARACTER, + "So what?", + CHANGE_CHARACTER, + "So why don't you do it?!", + CHANGE_CHARACTER, + "It must be something with", + NEW_LINE, + "my eyes.", + CHANGE_CHARACTER, + "You should wear glassess?", + CHANGE_CHARACTER, + "No, why?", + CHANGE_CHARACTER, + "(sigh)", + DISPLAY_MESSAGE, + "Maybe you're just too weak to", + NEW_LINE, + "send the ball high enough?", + CHANGE_CHARACTER, + "No kidding.", + CHANGE_CHARACTER, + "(sigh)", + END_DIALOG +}; + +const static char* dialog_15[] = { + "Hey, kid!", + DISPLAY_MESSAGE, + "I've got a great idea!", + CHANGE_CHARACTER, + "Yeah?", + CHANGE_CHARACTER, + "Go to your grandpa and", + NEW_LINE, + "say you've scored!", + CHANGE_CHARACTER, + "You mean lie?", + CHANGE_CHARACTER, + "Well, sort of...", + CHANGE_CHARACTER, + "I NEVER LIE!", + CHANGE_CHARACTER, + "Never?!", + CHANGE_CHARACTER, + "NEVER!", + CHANGE_CHARACTER, + "Good boy.", + END_DIALOG +}; + +const static char* dialog_16[] = { + "Hey...", + CHANGE_CHARACTER, + "Go away.", + END_DIALOG +}; + +const static char* dialog_17[] = { + "Hey, boy! It's unbelievable!", + CHANGE_CHARACTER, + "What?", + CHANGE_CHARACTER, + "You might think it's a joke,", + NEW_LINE, + "but there's a hand holding", + NEW_LINE, + "a sword appearing from the lake!", + CHANGE_CHARACTER, + "OK, I'll look but just don't ", + NEW_LINE, + "think you've fooled me.", + NEW_LINE, + "This hand appears every year.", + DISPLAY_MESSAGE, + "Maybe this time will bring me luck", + NEW_LINE, + "at basketball...", + END_DIALOG +}; + +const static char* dialog_18[] = { + "Good day, sir!", + CHANGE_CHARACTER, + "And good day to", + NEW_LINE, + "you, my son.", + CHANGE_CHARACTER, + "My name is Mark, sir.", + CHANGE_CHARACTER, + "Great.", + DISPLAY_MESSAGE, + "What do you want?", + CHANGE_CHARACTER, + "I'm just admiring your arm-chair.", + DISPLAY_MESSAGE, + "It's nice.", + CHANGE_CHARACTER, + "Nice and pretty", + NEW_LINE, + "comfortable.", + CHANGE_CHARACTER, + "And big.", + CHANGE_CHARACTER, + "2-person model.", + END_DIALOG +}; + +const static char* dialog_19[] = { + "Do you know the boy", + NEW_LINE, + "playing with the ball", + NEW_LINE, + "outside?", + CHANGE_CHARACTER, + "Of course, he's", + NEW_LINE, + "my grandson.", + CHANGE_CHARACTER, + "Nice kid.", + CHANGE_CHARACTER, + "Nice and smart.", + CHANGE_CHARACTER, + "Nice and small.", + CHANGE_CHARACTER, + "He'll grow.", + CHANGE_CHARACTER, + "And become a very", + NEW_LINE, + "famous basketball", + NEW_LINE, + "player?...", + CHANGE_CHARACTER, + "I hope he won't.", + CHANGE_CHARACTER, + "Then tell him to stop playing!", + CHANGE_CHARACTER, + "Yeah.", + DISPLAY_MESSAGE, + "Later.", + DISPLAY_MESSAGE, + "Maybe.", + DISPLAY_MESSAGE, + "Right now I need", + NEW_LINE, + "some rest.", + END_DIALOG +}; + +const static char* dialog_20[] = { + "Are you going to sit here", + NEW_LINE, + "all day long?", + CHANGE_CHARACTER, + "Hope so.", + CHANGE_CHARACTER, + "For all of this beautiful day?", + CHANGE_CHARACTER, + "Hope so.", + CHANGE_CHARACTER, + "Aren't you interested", + NEW_LINE, + "in the outside world?!", + CHANGE_CHARACTER, + "Not really.", + CHANGE_CHARACTER, + "Why's that?", + CHANGE_CHARACTER, + "I'm not interested", + NEW_LINE, + "in news.", + CHANGE_CHARACTER, + "But...", + CHANGE_CHARACTER, + "As they say...", + DISPLAY_MESSAGE, + "The best news", + NEW_LINE, + "is no news.", + CHANGE_CHARACTER, + "But people must know about", + NEW_LINE, + "progress and stuff!", + CHANGE_CHARACTER, + "I won't even bother", + NEW_LINE, + "to ask you why...", + CHANGE_CHARACTER, + "Because...", + DISPLAY_MESSAGE, + "Er...", + DISPLAY_MESSAGE, + "Because...", + DISPLAY_MESSAGE, + "Uhm...", + CHANGE_CHARACTER, + "Right.", + NEW_LINE, + " ", + END_DIALOG +}; + +const static char* dialog_21[] = { + "Anything new?", + CHANGE_CHARACTER, + "Hope not.", + END_DIALOG +}; + +const static char* dialog_22[] = { + "May I borrow this shotgun?", + CHANGE_CHARACTER, + "No.", + CHANGE_CHARACTER, + "Pleeeease...", + CHANGE_CHARACTER, + "Young man, this weapon is", + NEW_LINE, + "very old and dangerous...", + DISPLAY_MESSAGE, + "and I'm a responsible man,", + NEW_LINE, + "got it?", + CHANGE_CHARACTER, + "But I will...", + CHANGE_CHARACTER, + "No.", + END_DIALOG +}; + +const static char* dialog_23[] = { + "Maybe you will change your mind", + NEW_LINE, + "about the shotgun?...", + CHANGE_CHARACTER, + "No.", + DISPLAY_MESSAGE, + "Nope.", + DISPLAY_MESSAGE, + "Niet.", + DISPLAY_MESSAGE, + "Nein.", + DISPLAY_MESSAGE, + "Niente.", + DISPLAY_MESSAGE, + "Nie.", + DISPLAY_MESSAGE, + "Ne.", + CHANGE_CHARACTER, + "OK, I got it.", + END_DIALOG +}; + +const static char* dialog_24[] = { + "May I search your drawers?", + CHANGE_CHARACTER, + "Yes.", + CHANGE_CHARACTER, + "YES?!?", + CHANGE_CHARACTER, + "Oh, I forgot to tell you they are all", + NEW_LINE, + "empty.", + DISPLAY_MESSAGE, + "Only the right upper one has a handkerchief", + NEW_LINE, + "in it.", + DISPLAY_MESSAGE, + "You can take if you want, I don't need it.", + CHANGE_CHARACTER, + "Well, thank you.", + DISPLAY_MESSAGE, + "You are very... kind...", + CHANGE_CHARACTER, + "Just don't think I let you take", + NEW_LINE, + "anything else.", + CHANGE_CHARACTER, + "Of course, I wouldn't even dream.", + END_DIALOG +}; + +const static char* dialog_25[] = { + "May I borrow the fan?", + CHANGE_CHARACTER, + "No way. It makes this hot day more", + NEW_LINE, + "bearable.", + END_DIALOG +}; + +const static char* dialog_26[] = { + "About this fan...", + CHANGE_CHARACTER, + "Come back in winter.", + END_DIALOG +}; + +const static char* dialog_27[] = { + "Nice weather we have", + NEW_LINE, + "today...", + CHANGE_CHARACTER, + "Indeed it is,", + NEW_LINE, + "my dear.", + END_DIALOG +}; + +const static char* dialog_28[] = { + "Is it your daughter?", + CHANGE_CHARACTER, + "You are very kind,", + NEW_LINE, + "my dear, making", + NEW_LINE, + "me so young,", + DISPLAY_MESSAGE, + "but of course", + NEW_LINE, + "that sweetie is my", + NEW_LINE, + "grand-daughter.", + CHANGE_CHARACTER, + "Oh, yes! She really", + NEW_LINE, + "looks grand!", + CHANGE_CHARACTER, + "Well, I was once", + NEW_LINE, + "like that...", + CHANGE_CHARACTER, + "But you still are!", + CHANGE_CHARACTER, + "How sweet of you...", + DISPLAY_MESSAGE, + "But nobody sings", + NEW_LINE, + "at my window", + NEW_LINE, + "anymore,", + DISPLAY_MESSAGE, + "if you know", + NEW_LINE, + "what I mean.", + CHANGE_CHARACTER, + "Errr...", + DISPLAY_MESSAGE, + "Yes...", + DISPLAY_MESSAGE, + "I know...", + DISPLAY_MESSAGE, + "I have similiar", + NEW_LINE, + "feelings myself...", + DISPLAY_MESSAGE, + "Sometimes...", + DISPLAY_MESSAGE, + "I guess...", + END_DIALOG +}; + +const static char* dialog_29[] = { + "May I ask what you are", + NEW_LINE, + "doing?", + CHANGE_CHARACTER, + "Yes, you may,", + NEW_LINE, + "my dear.", + CHANGE_CHARACTER, + ANIM_WAIT, + "What are you doing?", + CHANGE_CHARACTER, + "I'm knitting.", + CHANGE_CHARACTER, + "I understand.", + DISPLAY_MESSAGE, + "What are you knitting?", + CHANGE_CHARACTER, + "This time you", + NEW_LINE, + "didn't ask if", + NEW_LINE, + "you may ask.", + CHANGE_CHARACTER, + "Oh, sorry. May I ask?", + CHANGE_CHARACTER, + "Ask about what?", + CHANGE_CHARACTER, + "About what are you", + NEW_LINE, + "knitting.", + CHANGE_CHARACTER, + "You asked me", + NEW_LINE, + "about that before,", + NEW_LINE, + "didn't you?", + END_DIALOG +}; + +const static char* dialog_30[] = { + "Is everything OK?", + CHANGE_CHARACTER, + "Indeed it is.", + END_DIALOG +}; + +const static char* dialog_31[] = { + "Is everything OK?", + CHANGE_CHARACTER, + "You know.", + END_DIALOG +}; + +const static char* dialog_32[] = { + "Is everything OK?", + CHANGE_CHARACTER, + "It's nice you ask,", + NEW_LINE, + "but I've told you", + NEW_LINE, + "already.", + END_DIALOG +}; + +const static char* dialog_33[] = { + "Is everything OK?", + CHANGE_CHARACTER, + "Don't repeat", + NEW_LINE, + "yourself.", + END_DIALOG +}; + +const static char* dialog_34[] = { + "Is everything OK?", + CHANGE_CHARACTER, + "Don't interrupt", + NEW_LINE, + "my work.", + END_DIALOG +}; + +const static char* dialog_35[] = { + "Is everything OK?", + CHANGE_CHARACTER, + "Oh shut up.", + END_DIALOG +}; + +const static char* dialog_36[] = { + "Is everything OK?", + END_DIALOG +}; + +const static char* dialog_37[] = { + "Excuse my", + NEW_LINE, + "immodesty...", + CHANGE_CHARACTER, + "Yes?...", + CHANGE_CHARACTER, + "...but I thought", + NEW_LINE, + "that an innocent", + NEW_LINE, + "flower...", + DISPLAY_MESSAGE, + "...would express", + NEW_LINE, + "my happiness at", + NEW_LINE, + "meeting you.", + END_DIALOG +}; + +const static char* dialog_38[] = { + "I hope you", + NEW_LINE, + "like it...", + CHANGE_CHARACTER, + "Oh, dear!", + DISPLAY_MESSAGE, + "I'm really", + NEW_LINE, + "touched...", + DISPLAY_MESSAGE, + "That's the nicest", + NEW_LINE, + "thing anybody has", + NEW_LINE, + "done for me...", + DISPLAY_MESSAGE, + "...in last ten", + NEW_LINE, + "years!", + DISPLAY_MESSAGE, + "Thank you from all", + NEW_LINE, + "my heart!", + CHANGE_CHARACTER, + "You're welcome.", + END_DIALOG +}; + +const static char* dialog_39[] = { + "Would you care for", + NEW_LINE, + "another flower?", + CHANGE_CHARACTER, + "You're very kind,", + NEW_LINE, + "my boy, but no,", + NEW_LINE, + "thank you.", + END_DIALOG +}; + +const static char* dialog_40[] = { + "Are you sure you don't", + NEW_LINE, + "want another flower?", + CHANGE_CHARACTER, + "Yes. I'm sure.", + END_DIALOG +}; + +const static char* dialog_41[] = { + "May I borrow this", + NEW_LINE, + "duster?", + CHANGE_CHARACTER, + "We don't know each", + NEW_LINE, + "other too well, and", + NEW_LINE, + "I don't...", + DISPLAY_MESSAGE, + "...lend things to", + NEW_LINE, + "anybody who asks", + NEW_LINE, + "for them.", + CHANGE_CHARACTER, + "Don't I look reliable?", + CHANGE_CHARACTER, + "I've said enough.", + END_DIALOG +}; + +const static char* dialog_42[] = { + "Any chances to borrow the", + NEW_LINE, + "feather duster?", + CHANGE_CHARACTER, + "I like it where it is.", + END_DIALOG +}; + +const static char* dialog_43[] = { + "Do you think you could", + NEW_LINE, + "lend me the feather duster", + NEW_LINE, + "now?", + CHANGE_CHARACTER, + "But of course, I can't", + NEW_LINE, + "see why I shouldn't...", + DISPLAY_MESSAGE, + "...help to such a nice", + NEW_LINE, + "young man like you!..", + CHANGE_CHARACTER, + "Thank you very much.", + END_DIALOG +}; + +const static char* dialog_44[] = { + "Ha! I'm even faster than Indy`!", + CHANGE_CHARACTER, + "I've seen it all, boy!", + END_DIALOG +}; + +const static char* dialog_45[] = { + "Er...", + DISPLAY_MESSAGE, + "Uh...", + DISPLAY_MESSAGE, + "I just...", + CHANGE_CHARACTER, + "Don't worry. I hope you killed", + NEW_LINE, + "that fly.", + END_DIALOG +}; + +const static char* dialog_46[] = { + "Excuse me, lady, but I think your", + NEW_LINE, + "laundry is dry now...", + DISPLAY_MESSAGE, + "...and too much sun will distort", + NEW_LINE, + "the clothes...", + CHANGE_CHARACTER, + "How come the laundry dried", + NEW_LINE, + "out so fast?", + DISPLAY_MESSAGE, + "I'd better go and check it.", + END_DIALOG +}; + +const static char* dialog_47[] = { + CHANGE_CHARACTER, + "You were right, young man.", + DISPLAY_MESSAGE, + "Thank you.", + CHANGE_CHARACTER, + "You're welcome.", + END_DIALOG +}; + +const static char* dialog_48[] = { + "Do you need this fake apple?", + CHANGE_CHARACTER, + "It depends. This apple", + NEW_LINE, + "has its own story.", + DISPLAY_MESSAGE, + "I remember how one day", + NEW_LINE, + "my younger sister Mary", + NEW_LINE, + "was making...", + CHANGE_CHARACTER, + "Er, how long is the story?", + CHANGE_CHARACTER, + "Oh, there's no need to rush.", + DISPLAY_MESSAGE, + "We do have hours to talk,", + NEW_LINE, + "don't we?", + CHANGE_CHARACTER, + "I just realised I don't need that", + NEW_LINE, + "apple.", + DISPLAY_MESSAGE, + "Thanks.", + END_DIALOG +}; + +const static char* dialog_49[] = { + "This apple...", + CHANGE_CHARACTER, + "No story, no apple.", + CHANGE_CHARACTER, + ANIM_WAIT, + "No apple.", + END_DIALOG +}; + +const static char* dialog_50[] = { + "Could she be...", + DISPLAY_MESSAGE, + "...the most beautiful girl...", + DISPLAY_MESSAGE, + "in the world?...", + END_DIALOG +}; + +const static char* dialog_51[] = { + "I think it's high time to", + NEW_LINE, + "introduce myself.", + DISPLAY_MESSAGE, + "I'm Mark.", + CHANGE_CHARACTER, + "Anne.", + END_DIALOG +}; + +const static char* dialog_52[] = { + "The moment I saw those eyes", + NEW_LINE, + "was the best moment of my entire", + NEW_LINE, + "life.", + DISPLAY_MESSAGE, + ANIM_WAIT, + "Not counting the time", + NEW_LINE, + "I played doctor with", + NEW_LINE, + "Susie.", + END_DIALOG +}; + +const static char* dialog_53[] = { + "Uh...", + DISPLAY_MESSAGE, + "Er...", + DISPLAY_MESSAGE, + "I wonder...", + DISPLAY_MESSAGE, + "I wonder if", + NEW_LINE, + "you would like", + NEW_LINE, + "to get some...", + DISPLAY_MESSAGE, + "Er...", + DISPLAY_MESSAGE, + "I mean...", + DISPLAY_MESSAGE, + "I have something", + NEW_LINE, + "I would like to give", + NEW_LINE, + "you, because...", + DISPLAY_MESSAGE, + "Uh...", + DISPLAY_MESSAGE, + "I think you", + NEW_LINE, + "are... And...", + CHANGE_CHARACTER, + "Hey! I don't bite!", + DISPLAY_MESSAGE, + "I see you want to tell me", + NEW_LINE, + "something nice.", + DISPLAY_MESSAGE, + "Just use simple", + NEW_LINE, + "words...", + CHANGE_CHARACTER, + "Simple words?!", + CHANGE_CHARACTER, + "Yes, simple words make", + NEW_LINE, + "things simple.", + CHANGE_CHARACTER, + "Oh, yes.", + DISPLAY_MESSAGE, + "OK.", + DISPLAY_MESSAGE, + "Simple words.", + DISPLAY_MESSAGE, + "OK.", + DISPLAY_MESSAGE, + "Here I go.", + DISPLAY_MESSAGE, + "Me like you and", + NEW_LINE, + "want give flower.", + CHANGE_CHARACTER, + ANIM_WAIT, + "Well, maybe you should", + NEW_LINE, + "try something...", + DISPLAY_MESSAGE, + "...more complicated.", + CHANGE_CHARACTER, + "Uh...", + DISPLAY_MESSAGE, + "Sorry...", + DISPLAY_MESSAGE, + "I just...", + DISPLAY_MESSAGE, + "I just brought you", + NEW_LINE, + "a flower.", + CHANGE_CHARACTER, + "Oh?...", + END_DIALOG +}; + +const static char* dialog_54[] = { + "Do you like it?", + CHANGE_CHARACTER, + "You're charming.", + END_DIALOG +}; + +const static char* dialog_55[] = { + "As a matter", + NEW_LINE, + "of fact...", + CHANGE_CHARACTER, + "Simple words, boy!", + NEW_LINE, + "Simple words!", + END_DIALOG +}; + +const static char* dialog_56[] = { + "Oh,yes...", + DISPLAY_MESSAGE, + "I just wanted to say", + NEW_LINE, + "that you're charming", + NEW_LINE, + "too.", + CHANGE_CHARACTER, + "I guess I should say", + NEW_LINE, + "thanks.", + END_DIALOG +}; + +const static char* dialog_57[] = { + "I hate myself.", + END_DIALOG +}; + +const static char* dialog_58[] = { + "I have another", + NEW_LINE, + "flower...", + CHANGE_CHARACTER, + "Oh, let's not", + NEW_LINE, + "exaggerate.", + DISPLAY_MESSAGE, + "And, as you can see, I'm", + NEW_LINE, + "not the only...", + DISPLAY_MESSAGE, + "...woman in this room...", + END_DIALOG +}; + +const static char* dialog_59[] = { + "So you don't want", + NEW_LINE, + "another flower?", + CHANGE_CHARACTER, + "No, thank you.", + END_DIALOG +}; + +const static char* dialog_60[] = { + "Would you like some candy?", + CHANGE_CHARACTER, + "You're nice, but no, thanks.", + NEW_LINE, + "I don't want to get fat.", + CHANGE_CHARACTER, + "Hey, don't worry. Even Obelix", + NEW_LINE, + "has friends.", + CHANGE_CHARACTER, + "Who's Obelix?!", + CHANGE_CHARACTER, + "Er, never mind. It's just that I found", + NEW_LINE, + "this candy...", + CHANGE_CHARACTER, + "You FOUND IT?!", + CHANGE_CHARACTER, + "...I found it's pretty hard to get,", + NEW_LINE, + "of course.", + DISPLAY_MESSAGE, + "It's not some cheap pseudo-chocolate,", + NEW_LINE, + "but the highest quality goodie!", + DISPLAY_MESSAGE, + "It's made only from the things you", + NEW_LINE, + "can find in a natural environment.", + DISPLAY_MESSAGE, + "No preservatives added.", + CHANGE_CHARACTER, + "Oh, all right. If you insist...", + END_DIALOG +}; + +const static char* dialog_61[] = { + ANIM_WAIT, + "Khm...", + CHANGE_CHARACTER, + "Oh yes, I think I should give you", + NEW_LINE, + "something in return...", + CHANGE_CHARACTER, + "Oh, no... You really don't", + NEW_LINE, + "have to...", + CHANGE_CHARACTER, + "OK. Your wish.", + CHANGE_CHARACTER, + ANIM_WAIT, + "Well, on second thoughts...", + CHANGE_CHARACTER, + "I knew it. You boys always want", + NEW_LINE, + "something.", + DISPLAY_MESSAGE, + "You can't do anything for", + NEW_LINE, + "free.", + CHANGE_CHARACTER, + "I cleaned my room once.", + CHANGE_CHARACTER, + ANIM_WAIT, + "Well...", + DISPLAY_MESSAGE, + "Let's forget it.", + DISPLAY_MESSAGE, + "Here's my present for", + NEW_LINE, + "you.", + DISPLAY_MESSAGE, + "It's my ribbon. Think of me", + NEW_LINE, + "sometimes.", + END_DIALOG +}; + +const static char* dialog_62[] = { + "Thanks. I will never", + NEW_LINE, + "wash it.", + END_DIALOG +}; + +const static char* dialog_63[] = { + "I found your name on a banknote", + NEW_LINE, + "some fatso gave me. Do you know", + NEW_LINE, + "anything about it?", + CHANGE_CHARACTER, + "Show me the banknote.", + END_DIALOG +}; + +const static char* dialog_64[] = { + "Hey, what's up?!", + CHANGE_CHARACTER, + "Oh, poor me!...", + CHANGE_CHARACTER, + "Why are you crying?!", + CHANGE_CHARACTER, + "This... this...", + DISPLAY_MESSAGE, + "Oh, poor me!...", + DISPLAY_MESSAGE, + "Our... our neighbour, Mr.", + NEW_LINE, + "John Noty gave me some", + NEW_LINE, + "money one day...", + DISPLAY_MESSAGE, + "...and said that if I gave", + NEW_LINE, + "him a kiss he'd give", + NEW_LINE, + "me more...", + DISPLAY_MESSAGE, + "...but I thought it over", + NEW_LINE, + "and I gave him all the", + NEW_LINE, + "money back.", + CHANGE_CHARACTER, + "You mean, that pig tried", + NEW_LINE, + "to buy you?!", + CHANGE_CHARACTER, + "I'm so unhappy!", + CHANGE_CHARACTER, + "Oh, man! That", + NEW_LINE, + "does it!", + END_DIALOG +}; + +const static char* dialog_65[] = { + "Hey, you!", + DISPLAY_MESSAGE, + "Would you please give me that nut", + NEW_LINE, + "lying next to you?", + END_DIALOG +}; + +const static char* dialog_66[] = { + "Are you gonna give me that nut or not?!", + END_DIALOG +}; + +const static char* dialog_67[] = { + "All right.", + DISPLAY_MESSAGE, + "That's it.", + DISPLAY_MESSAGE, + "Now you'll get what you deserve.", + DISPLAY_MESSAGE, + "I'm gonna insult you until I get that nut.", + DISPLAY_MESSAGE, + "You ugly squirrel you.", + END_DIALOG +}; + +const static char* dialog_68[] = { + "Don't you know it's not politically", + NEW_LINE, + "correct to wear a fur?", + END_DIALOG +}; + +const static char* dialog_69[] = { + "Hey, thanks again for the nut.", + END_DIALOG +}; + +const static char* dialog_70[] = { + "I didn't ask if I could take the rope.", + DISPLAY_MESSAGE, + "It's really rude to take someone else's", + NEW_LINE, + "property without their permission.", + DISPLAY_MESSAGE, + "And I might get caught, of", + NEW_LINE, + "course.", + DISPLAY_MESSAGE, + "And they will put me in jail and", + NEW_LINE, + "nobody will respect me anymore.", + DISPLAY_MESSAGE, + "I could really ruin my life doing that.", + END_DIALOG +}; + +const static char* dialog_71[] = { + "No pain no gain.", + END_DIALOG +}; + +const static char* dialog_72[] = { + "Listen, guys. I want you to get", + NEW_LINE, + "outta here at once!", + DISPLAY_MESSAGE, + "Or I'll have to shoot.", + DISPLAY_MESSAGE, + "With a real gun.", + DISPLAY_MESSAGE, + "I think.", + END_DIALOG +}; + +const static char* dialog_73[] = { + "I can see...", + DISPLAY_MESSAGE, + "...there's a...", + DISPLAY_MESSAGE, + "SPIDER!!!", + END_DIALOG +}; + +const static char* dialog_74[] = { + "Not that I'm chicken.", + DISPLAY_MESSAGE, + "It's just that it could be", + NEW_LINE, + "a mutant spider and it", + NEW_LINE, + "could bite me...", + DISPLAY_MESSAGE, + "...and I'd get mutant too...", + DISPLAY_MESSAGE, + "...and I'd have to wear those", + NEW_LINE, + "funny gloves and stuff...", + DISPLAY_MESSAGE, + "...and I'd have a double life...", + DISPLAY_MESSAGE, + "...and I'd have to fight with Venom`", + NEW_LINE, + "and others and I might get hurt...", + DISPLAY_MESSAGE, + "...and everybody would be making", + NEW_LINE, + "money on me but me...", + DISPLAY_MESSAGE, + "I think I'll pass then.", + END_DIALOG +}; + +const static char* dialog_75[] = { + "This spider gives me thrills...", + END_DIALOG +}; + +const static char* dialog_76[] = { + "Hey, little buddy!", + DISPLAY_MESSAGE, + "I've got a DECENT PROPOSAL for you.", + DISPLAY_MESSAGE, + "A big, fresh and juicy apple for this old cone", + NEW_LINE, + "which presses your back.", + DISPLAY_MESSAGE, + "If you want to make a deal, stand here", + NEW_LINE, + "and shake your muzzle.", + END_DIALOG +}; + +const static char* dialog_77[] = { + "I should have know", + NEW_LINE, + "there's a catch.", + DISPLAY_MESSAGE, + ANIM_WAIT, + "The paddle is broken.", + END_DIALOG +}; + +const static char* dialog_78[] = { + "Hello there, big boy.", + END_DIALOG +}; + +const static char* dialog_79[] = { + "Don't ignore me, please.", + END_DIALOG +}; + +const static char* dialog_80[] = { + "You know, I'm a little bit dog-tired", + NEW_LINE, + "talking to you.", + END_DIALOG +}; + +const static char* dialog_81[] = { + "What's up?", + END_DIALOG +}; + +const static char* dialog_82[] = { + "Yes, I could take this...", + DISPLAY_MESSAGE, + "It's a quiet little village...", + DISPLAY_MESSAGE, + "No police...", + DISPLAY_MESSAGE, + "Noone will hear their screams...", + DISPLAY_MESSAGE, + ANIM_WAIT, + "But I don't have a hockey mask.", + DISPLAY_MESSAGE, + ANIM_WAIT, + "(sigh)", + END_DIALOG +}; + +const static char* dialog_83[] = { + "I don't want my fingerprints on it.", + DISPLAY_MESSAGE, + " Who knows what it was used for.", + END_DIALOG +}; + +const static char* dialog_84[] = { + "I'm afraid that it's too hard", + NEW_LINE, + "to catch a mouse just like", + NEW_LINE, + "that.", + DISPLAY_MESSAGE, + "And what challenge would it be?", + END_DIALOG +}; + +const static char* dialog_85[] = { + "I could try to scare these birds myself", + NEW_LINE, + "if I hadn't watched that Hitchcock", + NEW_LINE, + "movie when I was five.", + DISPLAY_MESSAGE, + "My mum should never let me watch that.", + DISPLAY_MESSAGE, + "Now I'm CHICKEN even when I eat eggs.", + END_DIALOG +}; + +const static char* dialog_86[] = { + "Great. Let's GET THE MESSAGE.", + DISPLAY_MESSAGE, + "\"Gold awaits at the end of the road.\"", + END_DIALOG +}; + +const static char* dialog_87[] = { + "Are you Mr. John Noty?", + CHANGE_CHARACTER, + "How do you do, my friend.", + DISPLAY_MESSAGE, + "My people told me you tried", + NEW_LINE, + "to get inside my mansion.", + NEW_LINE, + "Why?", + CHANGE_CHARACTER, + "Er... You see...", + DISPLAY_MESSAGE, + "I'm here to...", + NEW_LINE, + "To...", + DISPLAY_MESSAGE, + "I mean, I'm here on my vacations,", + NEW_LINE, + "but I got sick of all these green", + NEW_LINE, + "plants and trees around...", + DISPLAY_MESSAGE, + "...and I just wanted to lick some", + NEW_LINE, + "civilisation.", + CHANGE_CHARACTER, + "Well... I can understand you,", + NEW_LINE, + "my friend.", + DISPLAY_MESSAGE, + "I also think that the natural", + NEW_LINE, + "environment for us, people", + NEW_LINE, + "of 20th century...", + DISPLAY_MESSAGE, + "is TV and a bag of pop-corn.", + NEW_LINE, + "But talking about the green", + NEW_LINE, + "stuff...", + DISPLAY_MESSAGE, + "I can't allow you to enter", + NEW_LINE, + "my house, because... uhm...", + NEW_LINE, + "because it's ...being...", + DISPLAY_MESSAGE, + "...under renovation, but as", + NEW_LINE, + "a rich man I'll give you", + NEW_LINE, + "a hundred bucks...", + DISPLAY_MESSAGE, + "so you can buy yourself", + NEW_LINE, + "something that will help", + NEW_LINE, + "you survive here.", + DISPLAY_MESSAGE, + "A walkman for example.", + NEW_LINE, + "What do you say?", + CHANGE_CHARACTER, + "What do you take me for?!", + END_DIALOG +}; + +const static char* dialog_88[] = { + "I will NEVER take this!", + DISPLAY_MESSAGE, + "NEVER!", + CHANGE_CHARACTER, + "Don't get so excited.", + DISPLAY_MESSAGE, + "Pecunia non olet.", + DISPLAY_MESSAGE, + "I'll leave now. Don't be shy", + NEW_LINE, + "and pick up the banknote.", + DISPLAY_MESSAGE, + "Noone has to know...", + DISPLAY_MESSAGE, + "Good bye, my friend.", + END_DIALOG +}; + +const static char* dialog_89[] = { + "I can't believe he treated me", + NEW_LINE, + "like that.", + DISPLAY_MESSAGE, + "By the way...", + DISPLAY_MESSAGE, + ANIM_WAIT, + "Peculiar non omelette?...", + END_DIALOG +}; + +const static char* dialog_90[] = { + "Boy...", + DISPLAY_MESSAGE, + "It's all black...", + DISPLAY_MESSAGE, + "...and it looks like a man...", + DISPLAY_MESSAGE, + "...with some long pole...", + DISPLAY_MESSAGE, + "...and a pot on its head...", + DISPLAY_MESSAGE, + ANIM_WAIT, + "VGA artist shouldn't drink so much.", + END_DIALOG +}; + +const static char* dialog_91[] = { + "Searching trash cans again?", + DISPLAY_MESSAGE, + ANIM_WAIT, + "Oh, all right, there are only some", + NEW_LINE, + "papers.", + END_DIALOG +}; + +const static char* dialog_92[] = { + "The same as usual...", + DISPLAY_MESSAGE, + "Disasters...", + DISPLAY_MESSAGE, + "Corruption...", + DISPLAY_MESSAGE, + "Murders...", + DISPLAY_MESSAGE, + "Puzzle...", + DISPLAY_MESSAGE, + "Half-naked babes...", + DISPLAY_MESSAGE, + ANIM_WAIT, + "I gotta subscribe.", + END_DIALOG +}; + +const static char* dialog_93[] = { + ANIM_WAIT, + "Gee...", + CHANGE_CHARACTER, + "...I'm daaaancing...", + DISPLAY_MESSAGE, + "...and siiiinging...", + CHANGE_CHARACTER, + "It's John Noty...", + CHANGE_CHARACTER, + "...raaain!...", + CHANGE_CHARACTER, + "...singing to the camera!", + CHANGE_CHARACTER, + "...what a beaaaaautifuuuul...", + CHANGE_CHARACTER, + "Although he definitely shouldn't.", + CHANGE_CHARACTER, + "...feeeliiing...", + CHANGE_CHARACTER, + "I can't believe it.", + CHANGE_CHARACTER, + "...haaaappy agaaain!..", + CHANGE_CHARACTER, + "What a horror.", + CHANGE_CHARACTER, + "...just daaancing in the raaain...", + CHANGE_CHARACTER, + "My neighbour's dog will do it better.", + CHANGE_CHARACTER, + "...la, laaaaa!...", + CHANGE_CHARACTER, + "I've had enough.", + CHANGE_CHARACTER, + "...la, la! La, laaaa....", + END_DIALOG +}; + +const static char* dialog_94[] = { + "There's nothing intere...", + DISPLAY_MESSAGE, + "No, wait a minute...", + DISPLAY_MESSAGE, + "There's something under the couch!", + END_DIALOG +}; + +const static char* dialog_95[] = { + "Don't you think you", + NEW_LINE, + "should add a little", + NEW_LINE, + "bit of chilli?", + CHANGE_CHARACTER, + "Add?", + DISPLAY_MESSAGE, + "Why?", + CHANGE_CHARACTER, + "I see you're not happy", + NEW_LINE, + "with the stew you're", + NEW_LINE, + "cooking.", + DISPLAY_MESSAGE, + "Maybe you should try", + NEW_LINE, + "to spice it up a", + NEW_LINE, + "little?", + CHANGE_CHARACTER, + "Well...", + DISPLAY_MESSAGE, + "That's good idea.", + DISPLAY_MESSAGE, + "Luckily I've got something here.", + END_DIALOG +}; + +const static char* dialog_96[] = { + CHANGE_CHARACTER, + "This... hic!...", + DISPLAY_MESSAGE, + "This chilllllleeeeee... hep!", + DISPLAY_MESSAGE, + "...must have been...", + DISPLAY_MESSAGE, + "...fermented...", + DISPLAY_MESSAGE, + "The stew yyys spoiled aaand...", + DISPLAY_MESSAGE, + "...I'm fired!", + DISPLAY_MESSAGE, + "Hic!", + END_DIALOG +}; + +const static char* dialog_97[] = { + "I don't need this radio, but", + NEW_LINE, + "I can use its batteries.", + DISPLAY_MESSAGE, + "Unfortunately I never know", + NEW_LINE, + "where to open those Japanese", + NEW_LINE, + "babies.", + END_DIALOG +}; + +const static char* dialog_98[] = { + CHANGE_CHARACTER, + "Security test: voice, scent, view.", + DISPLAY_MESSAGE, + "Voice positively identified.", + DISPLAY_MESSAGE, + ANIM_WAIT, + "Nice song, man.", + END_DIALOG +}; + +const static char* dialog_99[] = { + CHANGE_CHARACTER, + "Security test: voice, scent, view.", + DISPLAY_MESSAGE, + "Scent positively identified.", + DISPLAY_MESSAGE, + ANIM_WAIT, + "I don't like water too, bro.", + END_DIALOG +}; + +const static char* dialog_100[] = { + CHANGE_CHARACTER, + "Security test: voice, scent, view.", + DISPLAY_MESSAGE, + "View positively identified.", + DISPLAY_MESSAGE, + ANIM_WAIT, + "Next time stand a bit closer, man.", + END_DIALOG +}; + +const static char* dialog_101[] = { + "May I talk with...", + CHANGE_CHARACTER, + "Go away.", + DISPLAY_MESSAGE, + "I'm busy.", + DISPLAY_MESSAGE, + "Working.", + DISPLAY_MESSAGE, + "Dinner soon.", + DISPLAY_MESSAGE, + "Gotta hurry.", + CHANGE_CHARACTER, + "Aye, captain.", + END_DIALOG +}; + +const static char* dialog_102[] = { + "Well, but maybe...", + CHANGE_CHARACTER, + "I...", + DISPLAY_MESSAGE, + "...AM...", + DISPLAY_MESSAGE, + "...BUSY.", + DISPLAY_MESSAGE, + "DON'T...", + DISPLAY_MESSAGE, + "...DISTURB...", + DISPLAY_MESSAGE, + "...ME.", + DISPLAY_MESSAGE, + "OK?", + CHANGE_CHARACTER, + "Okay, okay.", + END_DIALOG +}; + +const static char* dialog_103[] = { + "Last time I ask you...", + CHANGE_CHARACTER, + "BUSY.", + DISPLAY_MESSAGE, + "B like Bill.", + DISPLAY_MESSAGE, + "U like Ulrik.", + DISPLAY_MESSAGE, + "S like Sean.", + DISPLAY_MESSAGE, + "Y like...", + DISPLAY_MESSAGE, + "...like...", + CHANGE_CHARACTER, + "Yeti?", + CHANGE_CHARACTER, + "No. Like...", + CHANGE_CHARACTER, + "Yabbadabbadoo?", + CHANGE_CHARACTER, + "No, no. Like...", + CHANGE_CHARACTER, + "Yoko?", + CHANGE_CHARACTER, + "Yoko.", + DISPLAY_MESSAGE, + "Leave me alone now, PLEASE!", + CHANGE_CHARACTER, + "All right, all right.", + END_DIALOG +}; + +const static char* dialog_104[] = { + "Er...", + CHANGE_CHARACTER, + "Wrrrr...", + CHANGE_CHARACTER, + "Oh, nothing.", + END_DIALOG +}; + +const static char* dialog_105[] = { + "Good day, Mr. Robot.", + CHANGE_CHARACTER, + "Hey, yo, wassup my man,", + NEW_LINE, + "you know what I'm sayin'?", + DISPLAY_MESSAGE, + "Call me Mike, you know", + NEW_LINE, + "what I'm sayin'?", + CHANGE_CHARACTER, + "Er...", + DISPLAY_MESSAGE, + "Are you sure everything's", + NEW_LINE, + "all right with your...", + DISPLAY_MESSAGE, + "...program?...", + CHANGE_CHARACTER, + "What's da problem, man?!", + DISPLAY_MESSAGE, + "Neva seen da rappin' robo-safe,", + NEW_LINE, + "you know what I'm sayin'?", + CHANGE_CHARACTER, + "Actually, never.", + CHANGE_CHARACTER, + "I'm brand new, bro.", + DISPLAY_MESSAGE, + "Fresh stuff, you know what I mean?", + DISPLAY_MESSAGE, + "Smart people sez da robots", + NEW_LINE, + "should be for everybody,", + DISPLAY_MESSAGE, + "you know what I'm sayin',", + NEW_LINE, + "so they gave me human", + NEW_LINE, + "personality,", + DISPLAY_MESSAGE, + "you know what I mean?", + NEW_LINE, + "Cool, ain't that?", + CHANGE_CHARACTER, + "Khm... Yeah, great.", + DISPLAY_MESSAGE, + "So, you're some kind of safe?", + CHANGE_CHARACTER, + "That's right, man.", + DISPLAY_MESSAGE, + "Totally reliable, you know", + NEW_LINE, + "what I'm sayin'?", + DISPLAY_MESSAGE, + "If you wanna get me open, you", + NEW_LINE, + "gotta prove you're da owner.", + DISPLAY_MESSAGE, + "Now check diz (CENSORED) out:", + NEW_LINE, + "I can judge if it's the right", + NEW_LINE, + "homie by three things:", + DISPLAY_MESSAGE, + "...view, scent and da voice.", + NEW_LINE, + "You know what I'm sayin'?", + CHANGE_CHARACTER, + "But will you please open", + NEW_LINE, + "...yourself... just to let me see", + NEW_LINE, + "what you got inside?", + CHANGE_CHARACTER, + "Sorry, bro.", + DISPLAY_MESSAGE, + "You don't look like da owner...", + DISPLAY_MESSAGE, + "...you don't smell like him...", + DISPLAY_MESSAGE, + "...and your voice is kinda different.", + DISPLAY_MESSAGE, + "Now (CENSORED), you know what I mean?", + END_DIALOG +}; + +const static char* dialog_106[] = { + "Sesame, open...", + CHANGE_CHARACTER, + "(CENSORED), you (CENSORED).", + END_DIALOG +}; + +const static char* dialog_107[] = { + "Hi there!", + CHANGE_CHARACTER, + "(PARENTAL GUIDANCE: EXPLICIT LYRICS)", + END_DIALOG +}; + +const static char* dialog_108[] = { + "I'm telling you, it's something great.", + CHANGE_CHARACTER, + "I remember when you killed my", + NEW_LINE, + "servant, testing your bullet-proof", + NEW_LINE, + "T-shirt.", + CHANGE_CHARACTER, + "That was a long time ago...", + CHANGE_CHARACTER, + "Or like you made rapping", + NEW_LINE, + "robo-safe, which goes mad", + NEW_LINE, + "every time...", + DISPLAY_MESSAGE, + "...I ask it to open.", + CHANGE_CHARACTER, + "You got a bad attitude...", + CHANGE_CHARACTER, + "Or like you sold me", + NEW_LINE, + "the recipe for girls'", + NEW_LINE, + "heart-breaking.", + CHANGE_CHARACTER, + "Money didn't work?", + CHANGE_CHARACTER, + "Nope.", + CHANGE_CHARACTER, + "Strange. Usually it works.", + CHANGE_CHARACTER, + "Or when you...", + CHANGE_CHARACTER, + "ALL RIGHT, ALL RIGHT!", + DISPLAY_MESSAGE, + "Let's forget this!", + DISPLAY_MESSAGE, + "I already tested my new", + NEW_LINE, + "invention on myself!", + CHANGE_CHARACTER, + "Really?", + CHANGE_CHARACTER, + "Really.", + DISPLAY_MESSAGE, + "I can demonstrate it.", + CHANGE_CHARACTER, + ANIM_WAIT, + "Ok, I'll take my chance.", + END_DIALOG +}; + +const static char* dialog_109[] = { + ANIM_WAIT, + "Great.", + DISPLAY_MESSAGE, + "Ultimate gnome-maker.", + CHANGE_CHARACTER, + "Khm, it's just a side effect.", + DISPLAY_MESSAGE, + "Sometimes I can't control my", + NEW_LINE, + "inventions.", + DISPLAY_MESSAGE, + "But don't worry, it lasts only", + NEW_LINE, + "a second.", + CHANGE_CHARACTER, + "I hope so.", + END_DIALOG +}; + +const static char* dialog_110[] = { + "Here I am.", + DISPLAY_MESSAGE, + "Well, have you noticed anything", + NEW_LINE, + "else unusual?", + CHANGE_CHARACTER, + "Nope.", + CHANGE_CHARACTER, + "Great!", + DISPLAY_MESSAGE, + "So the pills still work!", + CHANGE_CHARACTER, + "Oh yeah?", + CHANGE_CHARACTER, + "Yes!", + DISPLAY_MESSAGE, + "Check out your wallet!", + CHANGE_CHARACTER, + "My wallet is still...", + END_DIALOG +}; + +const static char* dialog_111[] = { + "Where is my wallet?!", + DISPLAY_MESSAGE, + "You thief!", + DISPLAY_MESSAGE, + "Give it back!", + CHANGE_CHARACTER, + "Take it easy, here's your", + NEW_LINE, + "wallet.", + END_DIALOG +}; + +const static char* dialog_112[] = { + "I demand an explanation.", + CHANGE_CHARACTER, + "Hah!", + DISPLAY_MESSAGE, + "This is the best thing I have", + NEW_LINE, + "ever invented!", + CHANGE_CHARACTER, + "What's that?", + CHANGE_CHARACTER, + "You didn't see me steal", + NEW_LINE, + "your wallet,", + DISPLAY_MESSAGE, + "...because I have eaten...", + DISPLAY_MESSAGE, + "THE TIME PILL!", + CHANGE_CHARACTER, + ANIM_WAIT, + "The time pill?", + CHANGE_CHARACTER, + "Yes!", + DISPLAY_MESSAGE, + "Anyone who eats it, lives", + NEW_LINE, + "1000 times faster than the", + NEW_LINE, + "rest of the world!", + CHANGE_CHARACTER, + "That means...", + CHANGE_CHARACTER, + "That means the world for", + NEW_LINE, + "this person moves 1000", + NEW_LINE, + "times slower!", + DISPLAY_MESSAGE, + "Only for a few seconds,", + NEW_LINE, + "though...", + CHANGE_CHARACTER, + "Well... That's interesting.", + NEW_LINE, + "But what's the use?", + CHANGE_CHARACTER, + "I don't care.", + DISPLAY_MESSAGE, + "Think about it.", + DISPLAY_MESSAGE, + "You could, for example, get in", + NEW_LINE, + "the cinema without a ticket,", + NEW_LINE, + "and nobody would notice you.", + CHANGE_CHARACTER, + "Of course!", + DISPLAY_MESSAGE, + "Great!", + DISPLAY_MESSAGE, + "I want to buy the patent!", + CHANGE_CHARACTER, + "That's the problem...", + DISPLAY_MESSAGE, + "As you know, my uncel Gallagher,", + NEW_LINE, + "used to invent the best things", + NEW_LINE, + "when he was ...let's say...", + CHANGE_CHARACTER, + "...drunk...", + CHANGE_CHARACTER, + "...intoxicated.", + DISPLAY_MESSAGE, + "And the same happened to me", + NEW_LINE, + "(sigh).", + DISPLAY_MESSAGE, + "So last night I woke up with", + NEW_LINE, + "a horrible headache and", + NEW_LINE, + "found those pills.", + DISPLAY_MESSAGE, + "I don't remember how I made them.", + CHANGE_CHARACTER, + "Oh no!", + CHANGE_CHARACTER, + "But I may try to work on them.", + DISPLAY_MESSAGE, + "I need your money for that.", + DISPLAY_MESSAGE, + "Let's make a deal.", + DISPLAY_MESSAGE, + "You build me a new", + NEW_LINE, + "laboratory and stuff...", + DISPLAY_MESSAGE, + "...and I'll give you my rights.", + CHANGE_CHARACTER, + "You'll give me the patent?!", + CHANGE_CHARACTER, + "Yes.", + DISPLAY_MESSAGE, + "All I care about is the", + NEW_LINE, + "respect of the science", + NEW_LINE, + "society respect.", + DISPLAY_MESSAGE, + "And Nobel.", + DISPLAY_MESSAGE, + "You know, honoris causa here", + NEW_LINE, + "and there, interviews...", + CHANGE_CHARACTER, + "All right. You got the deal.", + DISPLAY_MESSAGE, + "Prepare the list of necessary", + NEW_LINE, + "equipment.", + CHANGE_CHARACTER, + "Wonderful.", + END_DIALOG +}; + +const static char* dialog_113[] = { + "This fool trusts me.", + DISPLAY_MESSAGE, + "But I will use him...", + DISPLAY_MESSAGE, + "The time pills...", + DISPLAY_MESSAGE, + "I won't be selling them to", + NEW_LINE, + "those stupid people!", + DISPLAY_MESSAGE, + "I don't care about the", + NEW_LINE, + "patent!", + DISPLAY_MESSAGE, + "I could rob any bank", + NEW_LINE, + "without being seen!", + DISPLAY_MESSAGE, + "Faster than light!", + DISPLAY_MESSAGE, + "I need to steal some money", + NEW_LINE, + "or gold for this mad man's", + NEW_LINE, + "laboratory.", + END_DIALOG +}; + +const static char* dialog_114[] = { + "But soon...", + DISPLAY_MESSAGE, + "I'll get rich.", + DISPLAY_MESSAGE, + "Veeeery rich.", + DISPLAY_MESSAGE, + ANIM_WAIT, + "I feel like I could...", + DISPLAY_MESSAGE, + ANIM_WAIT, + "...like I could...", + END_DIALOG +}; + +const static char* dialog_115[] = { + ANIM_WAIT, + "TAKE ON THE WORLD!...", + END_DIALOG +}; + +const static char* dialog_116[] = { + ANIM_WAIT, + "I always wanted to say that.", + END_DIALOG +}; + +const static char* dialog_117[] = { + "It's me again.", + CHANGE_CHARACTER, + "Goodbye again.", + CHANGE_CHARACTER, + ANIM_WAIT, + "Let's say I haven't heard that.", + DISPLAY_MESSAGE, + "Is Mr. John Noty home?", + CHANGE_CHARACTER, + "Yeah, but he said you can't get in.", + CHANGE_CHARACTER, + "Me?! Why?!", + CHANGE_CHARACTER, + "Your last invention cost him", + NEW_LINE, + "two walls.", + CHANGE_CHARACTER, + "Oh, that time machine...", + DISPLAY_MESSAGE, + "But now I have some...", + CHANGE_CHARACTER, + "Not to mention the disapearance of his cat.", + CHANGE_CHARACTER, + "The cat is happier than", + NEW_LINE, + "any of us now!", + DISPLAY_MESSAGE, + ANIM_WAIT, + "If the world still exists in", + NEW_LINE, + "XXV century.", + DISPLAY_MESSAGE, + "Never mind.", + DISPLAY_MESSAGE, + "You just have to let me in.", + CHANGE_CHARACTER, + "Oh yeah?", + CHANGE_CHARACTER, + "Or I'll tell Mr. John Noty you drink on duty.", + CHANGE_CHARACTER, + "You're bluffing. You have no proof.", + CHANGE_CHARACTER, + "Yeah, but you never know.", + END_DIALOG +}; + +const static char* dialog_118[] = { + "Ok, get in, you filthy terrorist.", + DISPLAY_MESSAGE, + "Just don't tell anybody.", + CHANGE_CHARACTER, + "Of course. Thank you.", + END_DIALOG +}; + +const static char* dialog_119[] = { + "So...", + DISPLAY_MESSAGE, + "That's how it all happened...", + DISPLAY_MESSAGE, + "That's why nobody has seen", + NEW_LINE, + "how things were", + NEW_LINE, + "being stolen...", + DISPLAY_MESSAGE, + "It's a really dangerous", + NEW_LINE, + "invention!", + DISPLAY_MESSAGE, + ANIM_WAIT, + "I HAVE TO stop John Noty!", + DISPLAY_MESSAGE, + ANIM_WAIT, + "Somehow.", + DISPLAY_MESSAGE, + ANIM_WAIT, + "Boy! Those pills I took", + NEW_LINE, + "from Mike must be...", + DISPLAY_MESSAGE, + "Oh, no!", + DISPLAY_MESSAGE, + "I can hear somebody coming!", + END_DIALOG +}; + +const static char* dialog_120[] = { + "I have to hide somewhere!", + DISPLAY_MESSAGE, + "Now!", + END_DIALOG +}; + +const static char* dialog_121[] = { + "I have to buy an old-fashioned safe.", + DISPLAY_MESSAGE, + "That stupid robot went mad again.", + DISPLAY_MESSAGE, + "I hate it.", + DISPLAY_MESSAGE, + "CLOSE, YOU PIECE OF JUNK!", + END_DIALOG +}; + +const static char* dialog_122[] = { + "Third time this week.", + DISPLAY_MESSAGE, + "Oh, all right, all right!...", + DISPLAY_MESSAGE, + "I'm coming!...", + END_DIALOG +}; + +const static char* dialog_123[] = { + "...cover it all.", + DISPLAY_MESSAGE, + "I need more money for the security system.", + DISPLAY_MESSAGE, + "I've got only two men and...", + CHANGE_CHARACTER, + "More and more!", + DISPLAY_MESSAGE, + "It's all I hear!", + CHANGE_CHARACTER, + "All right, I'll explain again...", + END_DIALOG +}; + +const static char* dialog_124[] = { + "Mr. John Noty?", + DISPLAY_MESSAGE, + "I just received some information from", + NEW_LINE, + "the professor.", + DISPLAY_MESSAGE, + "He's asked you to come to the laboratory.", + DISPLAY_MESSAGE, + "He says he's found out the structure of", + NEW_LINE, + "the pills.", + END_DIALOG +}; + +const static char* dialog_125[] = { + "So this is it?!", + CHANGE_CHARACTER, + "Definitely ...hic!... yes.", + CHANGE_CHARACTER, + "Great!", + END_DIALOG +}; + +const static char* dialog_126[] = { + "I have to stop them!", + DISPLAY_MESSAGE, + "There's no time to waste!", + END_DIALOG +}; + +const static char* dialog_127[] = { + "Well, well, well...", + DISPLAY_MESSAGE, + "You really play on my nerves.", + DISPLAY_MESSAGE, + "It was a good idea not to save money", + NEW_LINE, + "on the security system...", + DISPLAY_MESSAGE, + "This force field is indestructible!", + DISPLAY_MESSAGE, + "Hah!", + END_DIALOG +}; + +const static char* dialog_128[] = { + "But I have to kill you anyway.", + CHANGE_CHARACTER, + "No, no!", + CHANGE_CHARACTER, + "Shut up!", + CHANGE_CHARACTER, + "It's not worth it!", + CHANGE_CHARACTER, + "Oh really?", + CHANGE_CHARACTER, + "I don't want to...", + END_DIALOG +}; + +const static char* dialog_129[] = { + CHANGE_CHARACTER, + "The poor professor has fainted...", + CHANGE_CHARACTER, + "But... How...", + DISPLAY_MESSAGE, + "I DON'T UNDERSTAND ANYTHING!!!", + CHANGE_CHARACTER, + "It's very easy.", + DISPLAY_MESSAGE, + "We attached a secret micro-camera", + NEW_LINE, + "to your cap.", + DISPLAY_MESSAGE, + "This way we knew all the time what was", + NEW_LINE, + "happening.", + CHANGE_CHARACTER, + "You have seen when I?...", + CHANGE_CHARACTER, + "I don't want to embarass you.", + DISPLAY_MESSAGE, + "You did a good job for the RGB.", + CHANGE_CHARACTER, + "But John Noty has escaped!", + CHANGE_CHARACTER, + "He's not important.", + DISPLAY_MESSAGE, + "We have the professor, you'll give us the pills.", + DISPLAY_MESSAGE, + "Sorry, but you can't keep them for yourself.", + CHANGE_CHARACTER, + "Okay, but...", + DISPLAY_MESSAGE, + "Could you please give me just a minute?", + DISPLAY_MESSAGE, + "I have some private business to do with", + NEW_LINE, + "this scum.", + CHANGE_CHARACTER, + ANIM_WAIT, + "All right. But hurry up.", + END_DIALOG +}; + +const static char* dialog_130[] = { + ANIM_WAIT, + "(gulp)", + DISPLAY_MESSAGE, + "This time-effect really doesn't last", + NEW_LINE, + "long...", + END_DIALOG +}; + +const static char* dialog_131[] = { + "You've lost, mister!", + DISPLAY_MESSAGE, + "The police are surrounding the building!", + CHANGE_CHARACTER, + "Don't be stupid.", + DISPLAY_MESSAGE, + "Didn't you think I'd have", + NEW_LINE, + "a secret way out?", + CHANGE_CHARACTER, + "Oh yeah?", + DISPLAY_MESSAGE, + "What's that?", + CHANGE_CHARACTER, + "Like I'm going to tell you...", + DISPLAY_MESSAGE, + "Get lost, you little creep.", + DISPLAY_MESSAGE, + "I'm busy.", + END_DIALOG +}; + +const static char* dialog_132[] = { + "I'll have to disarm you.", + DISPLAY_MESSAGE, + "Be nice and surrender without problems.", + CHANGE_CHARACTER, + "I don't have time for jokes.", + DISPLAY_MESSAGE, + "Get the hell out of here,", + NEW_LINE, + "before I point my gun at you", + NEW_LINE, + "again.", + CHANGE_CHARACTER, + "I warn you...", + CHANGE_CHARACTER, + "Where do they sell", + NEW_LINE, + "bores like you?", + END_DIALOG +}; + +const static char* dialog_133[] = { + "I won't give you any more chances...", + CHANGE_CHARACTER, + "Good.", + DISPLAY_MESSAGE, + "And bye.", + END_DIALOG +}; + +const static char* dialog_134[] = { + "Stop packing that money!", + END_DIALOG +}; + +const static char* dialog_135[] = { + "Hi, there!", + CHANGE_CHARACTER, + "Hi.", + DISPLAY_MESSAGE, + "What's your problem?", + CHANGE_CHARACTER, + "I was sent here for some training.", + CHANGE_CHARACTER, + "Another wanna-be secret agent, huh?", + CHANGE_CHARACTER, + "Yep.", + CHANGE_CHARACTER, + "Show me your documents and you'll get in.", + CHANGE_CHARACTER, + "Okey dokey.", + END_DIALOG +}; + +const static char* dialog_136[] = { + "Can't you let me in without all that", + NEW_LINE, + "bureaucracy?", + CHANGE_CHARACTER, + "Sorry, no.", + DISPLAY_MESSAGE, + "Rules are rules.", + CHANGE_CHARACTER, + "And morons are morons.", + CHANGE_CHARACTER, + ANIM_WAIT, + "And dead people are dead people.", + CHANGE_CHARACTER, + ANIM_WAIT, + "Have a good day, sir.", + END_DIALOG +}; + +const static char* dialog_137[] = { + "MAY I PASS, PLEASE?!", + CHANGE_CHARACTER, + "YES, YOU MAY!", + DISPLAY_MESSAGE, + "JUST SHOW ME YOUR DOCUMENTS!", + END_DIALOG +}; + +const static char* dialog_138[] = { + "Let me in!", + CHANGE_CHARACTER, + "Show your documents!", + CHANGE_CHARACTER, + "You take your job really seriously,", + NEW_LINE, + "don't you?", + CHANGE_CHARACTER, + "Are you blind or what?", + DISPLAY_MESSAGE, + "I'm reading a magazine on duty.", + CHANGE_CHARACTER, + ANIM_WAIT, + "Oh, yeah.", + DISPLAY_MESSAGE, + "Sorry.", + END_DIALOG +}; + +const static char* dialog_139[] = { + "I have to...", + CHANGE_CHARACTER, + "Documents!", + END_DIALOG +}; + +const static char* dialog_140[] = { + "What are you reading?", + CHANGE_CHARACTER, + "'Soldier News', of course.", + CHANGE_CHARACTER, + "You love all that military stuff,", + NEW_LINE, + "don't you?", + CHANGE_CHARACTER, + "Are you crazy?", + DISPLAY_MESSAGE, + "I like pictures of cool babes...", + DISPLAY_MESSAGE, + "...crosswords...", + DISPLAY_MESSAGE, + "...the humor page...", + DISPLAY_MESSAGE, + "...rumors...", + DISPLAY_MESSAGE, + "...recipes...", + CHANGE_CHARACTER, + "COOKING?!", + CHANGE_CHARACTER, + "Yes, they try to raise their profile.", + DISPLAY_MESSAGE, + "Gain new readers, you know.", + CHANGE_CHARACTER, + "Oh yeah.", + DISPLAY_MESSAGE, + "Great idea.", + DISPLAY_MESSAGE, + "Is there a knitting page too?", + CHANGE_CHARACTER, + "I get the feeling you're", + NEW_LINE, + "trying to be funny.", + CHANGE_CHARACTER, + "Nah, me?", + DISPLAY_MESSAGE, + "Never.", + CHANGE_CHARACTER, + "Good.", + END_DIALOG +}; + +const static char* dialog_141[] = { + "Would you lend me the magazine?", + CHANGE_CHARACTER, + "And what am I supposed", + NEW_LINE, + "to kill the time with", + NEW_LINE, + "then?", + CHANGE_CHARACTER, + "Erm...", + DISPLAY_MESSAGE, + "You could count the leaves.", + CHANGE_CHARACTER, + "There're 11034 leaves here.", + CHANGE_CHARACTER, + ANIM_WAIT, + "Wow.", + END_DIALOG +}; + +const static char* dialog_142[] = { + "What's up?", + CHANGE_CHARACTER, + "The sky, I hope.", + END_DIALOG +}; + +const static char* dialog_143[] = { + "Keep up the good work.", + END_DIALOG +}; + +const static char* dialog_144[] = { + CHANGE_CHARACTER, + "Hey, get back!", + END_DIALOG +}; + +const static char* dialog_145[] = { + "What's the matter?", + CHANGE_CHARACTER, + "You must show me your pass", + NEW_LINE, + "before I let you enter the", + NEW_LINE, + "camp.", + CHANGE_CHARACTER, + "What if I don't?", + CHANGE_CHARACTER, + "I'll have to shoot you.", + CHANGE_CHARACTER, + "(gulp)", + END_DIALOG +}; + +const static char* dialog_146[] = { + CHANGE_CHARACTER, + "I warn you...", + DISPLAY_MESSAGE, + "My bullets are faster than you...", + END_DIALOG +}; + +const static char* dialog_147[] = { + CHANGE_CHARACTER, + "All right.", + DISPLAY_MESSAGE, + "Report to the captain.", + DISPLAY_MESSAGE, + "He should be around somewhere.", + CHANGE_CHARACTER, + "Thanks, man.", + END_DIALOG +}; + +const static char* dialog_148[] = { + CHANGE_CHARACTER, + "We're gonna turn you into a real man,", + NEW_LINE, + "right, son?!", + CHANGE_CHARACTER, + "Erm...", + CHANGE_CHARACTER, + "Best of the best!...", + CHANGE_CHARACTER, + "Uh...", + CHANGE_CHARACTER, + "By sweat, blood and tears!", + CHANGE_CHARACTER, + "I'd rather...", + CHANGE_CHARACTER, + "I'm glad to see your enthusiasm, son!", + DISPLAY_MESSAGE, + "Let's not waste time!", + DISPLAY_MESSAGE, + "I was told to give you some express training.", + DISPLAY_MESSAGE, + "All right, son!", + DISPLAY_MESSAGE, + "You'll have to pass three trials!", + DISPLAY_MESSAGE, + "Let's begin with the easy one!...", + END_DIALOG +}; + +const static char* dialog_149[] = { + CHANGE_CHARACTER, + "The task is simple.", + DISPLAY_MESSAGE, + "I'll lock up you here...", + DISPLAY_MESSAGE, + "...and you must escape.", + DISPLAY_MESSAGE, + "Is it clear?!", + CHANGE_CHARACTER, + "Sir, I...", + CHANGE_CHARACTER, + "GOOD!!!", + END_DIALOG +}; + +const static char* dialog_150[] = { + ANIM_WAIT, + "Hello?", + DISPLAY_MESSAGE, + ANIM_WAIT, + "Great.", + END_DIALOG +}; + +const static char* dialog_151[] = { + "OK, that was funny.", + DISPLAY_MESSAGE, + "Now let me out!", + END_DIALOG +}; + +const static char* dialog_152[] = { + "Hey! Is there anybody out", + NEW_LINE, + "there?!", + DISPLAY_MESSAGE, + ANIM_WAIT, + "HELP!!!", + END_DIALOG +}; + +const static char* dialog_153[] = { + "Have mercy!", + DISPLAY_MESSAGE, + "I'm gonna die here!", + END_DIALOG +}; + +const static char* dialog_154[] = { + "I'm getting hungry!", + END_DIALOG +}; + +const static char* dialog_155[] = { + "I don't know what to say now...", + END_DIALOG +}; + +const static char* dialog_156[] = { + "I think...", + DISPLAY_MESSAGE, + "...you've passed...", + DISPLAY_MESSAGE, + "...the first test...", + DISPLAY_MESSAGE, + "...Let's get...", + DISPLAY_MESSAGE, + "...to the next one...", + END_DIALOG +}; + +const static char* dialog_157[] = { + CHANGE_CHARACTER, + "Ok, soldier.", + DISPLAY_MESSAGE, + "Let's assume I'm your captive...", + DISPLAY_MESSAGE, + "...and I know some secret password.", + DISPLAY_MESSAGE, + "Your task is to get it from me.", + DISPLAY_MESSAGE, + "CLEAR?!?", + CHANGE_CHARACTER, + "Am I restricted?", + CHANGE_CHARACTER, + "No.", + DISPLAY_MESSAGE, + "Do anything you want.", + CHANGE_CHARACTER, + "May I even spit in your eye?", + CHANGE_CHARACTER, + ANIM_WAIT, + "Yes.", + CHANGE_CHARACTER, + ANIM_WAIT, + "Thanks, just checking.", + END_DIALOG +}; + +const static char* dialog_158[] = { + "Please tell me the password", + NEW_LINE, + "and let's get over it all.", + DISPLAY_MESSAGE, + ANIM_WAIT, + "OK, think about it.", + END_DIALOG +}; + +const static char* dialog_159[] = { + "Hey, talk to me.", + DISPLAY_MESSAGE, + ANIM_WAIT, + "Do you hear me?", + DISPLAY_MESSAGE, + ANIM_WAIT, + "EARTH TO CAPTAIN, EARTH TO CAPTAIN!", + DISPLAY_MESSAGE, + ANIM_WAIT, + "You're hopeless.", + END_DIALOG +}; + +const static char* dialog_160[] = { + "Are you ready to talk?", + DISPLAY_MESSAGE, + ANIM_WAIT, + "OK, I'll give you some more time.", + END_DIALOG +}; + +const static char* dialog_161[] = { + "Now, what is the password?", + CHANGE_CHARACTER, + "Get lost, you pathetic wimp.", + CHANGE_CHARACTER, + "Be nice, or I'll tickle you again.", + CHANGE_CHARACTER, + "Go on, that'll be a pleasure.", + CHANGE_CHARACTER, + ANIM_WAIT, + "You enjoyed that, didn't you?", + CHANGE_CHARACTER, + "Well, you know...", + CHANGE_CHARACTER, + "Ok, I'll find some other way.", + END_DIALOG +}; + +const static char* dialog_162[] = { + "I brought you something...", + CHANGE_CHARACTER, + "You can't bribe me.", + CHANGE_CHARACTER, + "Oh, yeah?", + END_DIALOG +}; + +// Note: +// The usage of this in the engine overlaps the previous dialog i.e. the +// starting offset used is two bytes early, thus implicitly changing the +// first command of this dialog from NEW_LINE to CHANGE_CHARACTER. +const static char* dialog_163[] = { + NEW_LINE, + "OH GIMMIE GIMMIE GIMMIE!!!", + DISPLAY_MESSAGE, + "I'LL DO ANYTHING!!!", + CHANGE_CHARACTER, + "Password...", + CHANGE_CHARACTER, + "The password is 'COFFEE'.", + DISPLAY_MESSAGE, + "Tell it to the barman and", + NEW_LINE, + "he'll give you something.", + DISPLAY_MESSAGE, + "Then he'll tell you about the third task.", + DISPLAY_MESSAGE, + "NOW FREE ME!!!", + CHANGE_CHARACTER, + "I'll think about it.", + END_DIALOG +}; + +const static char* dialog_164[] = { + "You...", + DISPLAY_MESSAGE, + "...you...", + DISPLAY_MESSAGE, + ANIM_WAIT, + "...have passed, sir!", + END_DIALOG +}; + +const static char* dialog_165[] = { + "Would you care for a wonderful kaleidoscope?", + CHANGE_CHARACTER, + "I had one once, but captain saw me", + NEW_LINE, + "playing with it and took it from me.", + DISPLAY_MESSAGE, + "I think he uses it himself,", + NEW_LINE, + "you know.", + CHANGE_CHARACTER, + "If you give me the gazette,", + NEW_LINE, + "I'll give you that kaleidoscope.", + CHANGE_CHARACTER, + "I don't want to know how you got it...", + CHANGE_CHARACTER, + "Good.", + CHANGE_CHARACTER, + "...but what if the captain sees me again?", + CHANGE_CHARACTER, + "Don't worry, he's tied up.", + CHANGE_CHARACTER, + "Oh, test number two, I guess?...", + CHANGE_CHARACTER, + "Yep.", + CHANGE_CHARACTER, + "OK, let's have some fun here.", + END_DIALOG +}; + +const static char* dialog_166[] = { + "'COFFEE'.", + END_DIALOG +}; + +const static char* dialog_167[] = { + CHANGE_CHARACTER, + "Hot, wasn't it?", + CHANGE_CHARACTER, + "Uh-huh.", + DISPLAY_MESSAGE, + "The captain says you're gonna tell me", + NEW_LINE, + "about the third task.", + CHANGE_CHARACTER, + "Again...", + DISPLAY_MESSAGE, + "Well, it's kinda hide'n'seek.", + DISPLAY_MESSAGE, + "The captain hides, you seek.", + CHANGE_CHARACTER, + "But I left him tied to a chair!", + CHANGE_CHARACTER, + "That man and his sick games...", + DISPLAY_MESSAGE, + "He enjoys it more than he should!...", + DISPLAY_MESSAGE, + "He cheated you.", + DISPLAY_MESSAGE, + "Go and check.", + DISPLAY_MESSAGE, + "I'm sure he's already free.", + CHANGE_CHARACTER, + "But I took his knife!", + DISPLAY_MESSAGE, + "How could he cut the ties?!", + CHANGE_CHARACTER, + "Maybe he walked away with the chair", + NEW_LINE, + "tied to his...", + CHANGE_CHARACTER, + "Never mind.", + DISPLAY_MESSAGE, + "Any hints about where he might hide?", + CHANGE_CHARACTER, + "None.", + CHANGE_CHARACTER, + "Oh, c'mon.", + CHANGE_CHARACTER, + "No, boy. Play fair.", + END_DIALOG +}; + +const static char* dialog_168[] = { + "Time for a little hint?", + CHANGE_CHARACTER, + "No.", + END_DIALOG +}; + +const static char* dialog_169[] = { + "Hello, sir. I'm Mark.", + CHANGE_CHARACTER, + "What a pity you're not a dollar.", + DISPLAY_MESSAGE, + "What can I do for you?", + CHANGE_CHARACTER, + ANIM_WAIT, + "You can give me a lot of money...", + DISPLAY_MESSAGE, + "...or you can do a headstand...", + DISPLAY_MESSAGE, + "...or...", + CHANGE_CHARACTER, + "Okay, okay. It's a tie.", + DISPLAY_MESSAGE, + "So?...", + CHANGE_CHARACTER, + "Who's else is in the camp?", + CHANGE_CHARACTER, + "You're a journalist?", + CHANGE_CHARACTER, + "No, I'm a secret agent.", + CHANGE_CHARACTER, + "You too?", + DISPLAY_MESSAGE, + "Anyway, there are only three men.", + DISPLAY_MESSAGE, + "Me, the captain and the guard.", + CHANGE_CHARACTER, + "No women?", + CHANGE_CHARACTER, + "No cry.", + CHANGE_CHARACTER, + ANIM_WAIT, + "Erm, well...", + END_DIALOG +}; + +const static char* dialog_170[] = { + "Not much of a rush on, is there?", + CHANGE_CHARACTER, + "Do you want to order something or not?", + CHANGE_CHARACTER, + "I don't have money.", + CHANGE_CHARACTER, + "Today's free.", + CHANGE_CHARACTER, + "Really?", + CHANGE_CHARACTER, + "Really.", + CHANGE_CHARACTER, + "I want a hot-dog.", + CHANGE_CHARACTER, + "Miss.", + CHANGE_CHARACTER, + "Pizza?", + CHANGE_CHARACTER, + "Miss.", + CHANGE_CHARACTER, + "Toast?", + CHANGE_CHARACTER, + "Miss.", + CHANGE_CHARACTER, + "Anything?", + CHANGE_CHARACTER, + "Miss.", + CHANGE_CHARACTER, + ANIM_WAIT, + "No, thank you.", + DISPLAY_MESSAGE, + "I'm not hungry.", + END_DIALOG +}; + +const static char* dialog_171[] = { + "What are you drinking?", + CHANGE_CHARACTER, + "Tea.", + CHANGE_CHARACTER, + "Sure.", + END_DIALOG +}; + +const static char* dialog_172[] = { + "Nice weather.", + CHANGE_CHARACTER, + "Mhmmm...", + END_DIALOG +}; + +const static char* dialog_173[] = { + "Sometimes I feel tired.", + DISPLAY_MESSAGE, + ANIM_WAIT, + "Very tired.", + END_DIALOG +}; + +const static char* dialog_174[] = { + "Hey, Woodstock's over!", + END_DIALOG +}; + +const static char* dialog_175[] = { + "Thanks.", + END_DIALOG +}; + +const static char* dialog_176[] = { + CHANGE_CHARACTER, + "What the...", + END_DIALOG +}; + +const static char* dialog_177[] = { + "Hey, aren't you thirsty?", + DISPLAY_MESSAGE, + "Have you forgotten about your cup?", + END_DIALOG +}; + +const static char* dialog_178[] = { + "Sir, we have been informed that...", + CHANGE_CHARACTER, + "Later!", + DISPLAY_MESSAGE, + "I'm busy right now!", + CHANGE_CHARACTER, + "As you wish, sir.", + END_DIALOG +}; + +const static char* dialog_179[] = { + "Sir, some young boy tried to get inside", + NEW_LINE, + "the mansion.", + END_DIALOG +}; + +const static char* dialog_180[] = { + "Don't worry.", + DISPLAY_MESSAGE, + "Young boys are curious...", + DISPLAY_MESSAGE, + "...and my house plays on their imaginations.", + DISPLAY_MESSAGE, + "But keep an eye on him.", + CHANGE_CHARACTER, + "Yes, sir!", + END_DIALOG +}; + +const static char* dialog_181[] = { + "Sir, that boy tried to get in again.", + END_DIALOG +}; + +const static char* dialog_182[] = { + "Do you think it's serious?", + CHANGE_CHARACTER, + "Hmmm... No...", + DISPLAY_MESSAGE, + "He doesn't look dangerously.", + DISPLAY_MESSAGE, + "But maybe we should...", + CHANGE_CHARACTER, + "Nah.", + DISPLAY_MESSAGE, + "Just keep him out of the mansion.", + DISPLAY_MESSAGE, + "But tell me if he appears again.", + DISPLAY_MESSAGE, + "Now get back to your job.", + END_DIALOG +}; + +const static char* dialog_183[] = { + "Don't tell me it's that boy again...", + CHANGE_CHARACTER, + "I'm afraid so.", + DISPLAY_MESSAGE, + "The guard says the boy's really desperate.", + END_DIALOG +}; + +const static char* dialog_184[] = { + "He's starting to get on my nerves.", + DISPLAY_MESSAGE, + "And what am I paying you for?", + CHANGE_CHARACTER, + "Should I...", + CHANGE_CHARACTER, + "Not yet.", + DISPLAY_MESSAGE, + "Let's give him a last chance.", + END_DIALOG +}; + +const static char* dialog_185[] = { + "Sir...", + CHANGE_CHARACTER, + "Let me guess...", + DISPLAY_MESSAGE, + "THE BOY?!?...", + CHANGE_CHARACTER, + "Bingo.", + CHANGE_CHARACTER, + "Why do I have to do everything?!", + DISPLAY_MESSAGE, + "Can't you do anything yourself?", + CHANGE_CHARACTER, + "Shall I kill him or just beat him to pieces?", + CHANGE_CHARACTER, + "You're stupid.", + DISPLAY_MESSAGE, + "There are ...better ways...", + END_DIALOG +}; + +const static char* dialog_186[] = { + "I'll handle it myself.", + DISPLAY_MESSAGE, + "Now get out!", + DISPLAY_MESSAGE, + "I have to change my clothes.", + END_DIALOG +}; + +const static char* dialog_187[] = { + "Hey, you up there!", + DISPLAY_MESSAGE, + "Get down at once!", + DISPLAY_MESSAGE, + ANIM_WAIT, + "Zero reaction.", + DISPLAY_MESSAGE, + ANIM_WAIT, + "Is it deaf or something?", + END_DIALOG +}; + +const static char* dialog_188[] = { + "Hey, birdy, don't be shy.", + DISPLAY_MESSAGE, + "Come to me...", + DISPLAY_MESSAGE, + ANIM_WAIT, + "(sigh)", + END_DIALOG +}; + +const static char* dialog_189[] = { + "Come here, little bird...", + END_DIALOG +}; + +const static char* dialog_190[] = { + NEW_LINE, + "Hey, keep away from this door!", + CHANGE_CHARACTER, + "Why?", + CHANGE_CHARACTER, + "None of your business.", + DISPLAY_MESSAGE, + "Just keep away.", + END_DIALOG +}; + +const static char* dialog_191[] = { + CHANGE_CHARACTER, + "I told you to keep away, didn't I?", + CHANGE_CHARACTER, + "OK, OK...", + END_DIALOG +}; + +const static char* dialog_192[] = { + "I've got a new delivery of gold.", + CHANGE_CHARACTER, + "Yeah, I know.", + DISPLAY_MESSAGE, + "Password?", + CHANGE_CHARACTER, + "Bimbo.", + CHANGE_CHARACTER, + "All right.", + DISPLAY_MESSAGE, + "You can bring the gold in.", + CHANGE_CHARACTER, + "Okey dokey.", + END_DIALOG +}; + +const static char* dialog_193[] = { + "As I told you, our organisation", + NEW_LINE, + "takes care of unusual problems.", + DISPLAY_MESSAGE, + "Last time we solved the problem of a UFO", + NEW_LINE, + "over the White House.", + CHANGE_CHARACTER, + "Oh, heally?", + DISPLAY_MESSAGE, + "How?", + CHANGE_CHARACTER, + "We shot the thing down.", + CHANGE_CHARACTER, + ANIM_WAIT, + "I undehstand.", + DISPLAY_MESSAGE, + "And who oh ...what was inside?", + CHANGE_CHARACTER, + "You want to know?", + CHANGE_CHARACTER, + "Oh, yes! As a fohtune-telleh", + NEW_LINE, + "I'm a cuhious pehson.", + CHANGE_CHARACTER, + "Do you REALLY want to know?", + CHANGE_CHARACTER, + "YES!", + CHANGE_CHARACTER, + "But...", + DISPLAY_MESSAGE, + "...REALLY REALLY?", + CHANGE_CHARACTER, + "YES!", + CHANGE_CHARACTER, + ANIM_WAIT, + "Sorry, I can't tell you.", + DISPLAY_MESSAGE, + "Anyway, we need your help.", + CHANGE_CHARACTER, + "I'm not a sechet agent, I'm a fohtune-tell...", + CHANGE_CHARACTER, + "I know.", + DISPLAY_MESSAGE, + "We have a very difficult case,", + NEW_LINE, + "which we haven't been able", + NEW_LINE, + "to solve for 6 months.", + DISPLAY_MESSAGE, + "We're in a hopeless situation.", + DISPLAY_MESSAGE, + "I thought to myself, if we handle", + NEW_LINE, + "strange cases...", + DISPLAY_MESSAGE, + "...then why not use", + NEW_LINE, + "strange means?", + CHANGE_CHARACTER, + "And?...", + CHANGE_CHARACTER, + "Let me show you the phone-book.", + DISPLAY_MESSAGE, + "Use your powers and select a name.", + DISPLAY_MESSAGE, + "Maybe a fresh mind will solve the case.", + CHANGE_CHARACTER, + "You don't believe it will wohk, do you?", + CHANGE_CHARACTER, + "No, I don't.", + CHANGE_CHARACTER, + ANIM_WAIT, + "This is stupid.", + CHANGE_CHARACTER, + "I know.", + CHANGE_CHARACTER, + ANIM_WAIT, + "Okay. Show me the book.", + END_DIALOG +}; + +const static char* dialog_194[] = { + ANIM_WAIT, + "The name is...", + DISPLAY_MESSAGE, + "...Hoppeh...", + DISPLAY_MESSAGE, + "...Mahk Hoppeh...", + DISPLAY_MESSAGE, + "Hm...", + END_DIALOG +}; + +const static char* dialog_195[] = { + "He's coming.", + END_DIALOG +}; + +const static char* dialog_196[] = { + "Oh, I'm sorry about my men.", + DISPLAY_MESSAGE, + "Sometimes they get a bit too nervous...", + DISPLAY_MESSAGE, + "But, please...", + DISPLAY_MESSAGE, + "Let's talk...", + END_DIALOG +}; + +const static char* dialog_197[] = { + "Listen, mister...", + CHANGE_CHARACTER, + "I know, I know.", + DISPLAY_MESSAGE, + "Please, give me five minutes and", + NEW_LINE, + "everything will become clear.", + CHANGE_CHARACTER, + "Go on, I always liked that conspiracy stuff.", + CHANGE_CHARACTER, + "Well...", + DISPLAY_MESSAGE, + "I'm head of a secret government organization", + NEW_LINE, + "called the RGB.", + CHANGE_CHARACTER, + "Why RGB?", + CHANGE_CHARACTER, + "Even I don't know.", + DISPLAY_MESSAGE, + "It's so secret.", + CHANGE_CHARACTER, + ANIM_WAIT, + "Cool.", + CHANGE_CHARACTER, + "The aim of my organization is to solve", + NEW_LINE, + "all the extraordinary problems around", + NEW_LINE, + "the world.", + CHANGE_CHARACTER, + "Like taxes?", + CHANGE_CHARACTER, + "No, like UFOs, strange inventions,", + NEW_LINE, + "spirits...", + CHANGE_CHARACTER, + "Wow!", + CHANGE_CHARACTER, + "Yes, when the police, the secret service and so on", + NEW_LINE, + "can't solve the problem...", + DISPLAY_MESSAGE, + "...it's delivered to us. But...", + DISPLAY_MESSAGE, + "You see, six months ago gold and cash", + NEW_LINE, + "deposited in bank safes started to", + NEW_LINE, + "disappear.", + DISPLAY_MESSAGE, + "Literally. Pum! And it's gone!", + CHANGE_CHARACTER, + "And?...", + CHANGE_CHARACTER, + "Gone - without a trace.", + DISPLAY_MESSAGE, + "The whole thing happens in a few seconds.", + DISPLAY_MESSAGE, + "We have video recordings but they don't", + NEW_LINE, + "show anything.", + DISPLAY_MESSAGE, + "That's why we hired the fortune-teller", + NEW_LINE, + "to show us someone who could be our", + NEW_LINE, + "salvation.", + DISPLAY_MESSAGE, + "She has chosen you.", + CHANGE_CHARACTER, + "I beg your pardon...", + DISPLAY_MESSAGE, + "You said you have hired...", + DISPLAY_MESSAGE, + "...A FORTUNE TELLER?!?", + CHANGE_CHARACTER, + "Yes, we're desperate and", + NEW_LINE, + "we'll try everything once.", + CHANGE_CHARACTER, + "This is crazy. Mum, wake me up!", + CHANGE_CHARACTER, + "Relax.", + DISPLAY_MESSAGE, + "We have a proposal for you.", + DISPLAY_MESSAGE, + "Try to help us and you will be rewarded.", + CHANGE_CHARACTER, + "What's in it for me?", + CHANGE_CHARACTER, + "Self-satisfaction?", + END_DIALOG +}; + +const static char* dialog_198[] = { + CHANGE_CHARACTER, + "Our respect?", + END_DIALOG +}; + +const static char* dialog_199[] = { + CHANGE_CHARACTER, + "Patriotism?", + END_DIALOG +}; + +const static char* dialog_200[] = { + CHANGE_CHARACTER, + "Girls?", + CHANGE_CHARACTER, + "What do you mean?", + CHANGE_CHARACTER, + "Every girl loves a secret agent.", + END_DIALOG +}; + +const static char* dialog_201[] = { + "Ok, I agree. What am I supposed to do?", + CHANGE_CHARACTER, + "Before you start, I suggest that first", + NEW_LINE, + "you get some training in our special", + NEW_LINE, + "secret camp.", + DISPLAY_MESSAGE, + "I'll issue a pass for you.", + DISPLAY_MESSAGE, + "Deal?", + CHANGE_CHARACTER, + "Deal!", + END_DIALOG +}; + +const static char* dialog_202[] = { + "...and it was even fun.", + CHANGE_CHARACTER, + "I'm glad you liked our training methods.", + DISPLAY_MESSAGE, + "But let's get to the point.", + DISPLAY_MESSAGE, + "I must admit I didn't believe you could", + NEW_LINE, + "actually help.", + DISPLAY_MESSAGE, + "Nothing personal.", + CHANGE_CHARACTER, + "I hope so.", + CHANGE_CHARACTER, + "But you brought us luck.", + DISPLAY_MESSAGE, + "Our people found out that some", + NEW_LINE, + "businessman is spending his", + NEW_LINE, + "money like crazy...", + DISPLAY_MESSAGE, + "...for some strange materials.", + DISPLAY_MESSAGE, + "There are three very suspicious things", + NEW_LINE, + "about him:", + DISPLAY_MESSAGE, + "...He pays in cash, which is very strange", + NEW_LINE, + "in a plastic card century...", + DISPLAY_MESSAGE, + "...Second, half a year ago he wasn't nearly", + NEW_LINE, + "as rich as he is now...", + DISPLAY_MESSAGE, + "...Third, what the hell does he need", + NEW_LINE, + "100 kg of borax for?", + CHANGE_CHARACTER, + "Maybe he's just a weirdo.", + CHANGE_CHARACTER, + "Maybe.", + DISPLAY_MESSAGE, + "But I want you to fly to the place", + NEW_LINE, + "and try to sneak inside his mansion...", + DISPLAY_MESSAGE, + "...to find out some more information.", + DISPLAY_MESSAGE, + "What do you say?", + CHANGE_CHARACTER, + "Well...", + CHANGE_CHARACTER, + "I promise you'll be an official secret", + NEW_LINE, + "agent after this mission.", + CHANGE_CHARACTER, + ANIM_WAIT, + "Promise?", + CHANGE_CHARACTER, + "My word of honour.", + CHANGE_CHARACTER, + "Okay. I'm ready.", + DISPLAY_MESSAGE, + "Where am I supposed to go?", + CHANGE_CHARACTER, + "It's a little village.", + DISPLAY_MESSAGE, + "You won't find it on many maps...", + DISPLAY_MESSAGE, + "But before you go, let me give you some special", + NEW_LINE, + "agent equipment.", + DISPLAY_MESSAGE, + "I would give you some boots to make you fly,", + NEW_LINE, + "but I've lent them to somebody.", + DISPLAY_MESSAGE, + "The mega-power gauntlets are also", + NEW_LINE, + "out of stock.", + DISPLAY_MESSAGE, + "As a matter of act I only have special", + NEW_LINE, + "super glue.", + DISPLAY_MESSAGE, + "Well, that's better than nothing...", + DISPLAY_MESSAGE, + "Good luck!", + END_DIALOG +}; + +const static char* dialog_203[] = { + CHANGE_CHARACTER, + "Don't worry.", + DISPLAY_MESSAGE, + "He'll be okay.", + DISPLAY_MESSAGE, + "Just... oh, look, here he goes...", + END_DIALOG +}; + +const static char* dialog_204[] = { + CHANGE_CHARACTER, + "He got what he deserved.", + DISPLAY_MESSAGE, + "But we've wasted enough time. Let's go!", + CHANGE_CHARACTER, + "No! Wait!", + DISPLAY_MESSAGE, + "I have to see Anne!", + CHANGE_CHARACTER, + "Ermm... You can't.", + CHANGE_CHARACTER, + "And why is that?!", + CHANGE_CHARACTER, + ANIM_WAIT, + "You see... Anne was our man.", + DISPLAY_MESSAGE, + "She was ordered to keep an eye on you", + NEW_LINE, + "in case the microcamera got out of", + NEW_LINE, + "order.", + DISPLAY_MESSAGE, + "And she was also your motivation...", + CHANGE_CHARACTER, + "I don't believe you!", + CHANGE_CHARACTER, + "She has already gone off on", + NEW_LINE, + "her next mission.", + DISPLAY_MESSAGE, + "Sorry.", + CHANGE_CHARACTER, + "I guess grandma was involved too?", + CHANGE_CHARACTER, + "No, she's from here. We payed her.", + CHANGE_CHARACTER, + "Now that's interesting.", + DISPLAY_MESSAGE, + "Because if so, why did I have so", + NEW_LINE, + "many problems?...", + CHANGE_CHARACTER, + "We were pumping up your determination.", + DISPLAY_MESSAGE, + "We counted on you to have some strong will.", + DISPLAY_MESSAGE, + "We were not wrong, were we?", + CHANGE_CHARACTER, + "You treat people like animals in", + NEW_LINE, + "an experiment.", + DISPLAY_MESSAGE, + "You're ruthless...", + CHANGE_CHARACTER, + "It worked, didn't it?", + CHANGE_CHARACTER, + "This is all so unbelievable.", + DISPLAY_MESSAGE, + "Maybe you're gonna tell me that the dog is", + NEW_LINE, + "a masked agent...", + DISPLAY_MESSAGE, + "...the old man was my guard...", + DISPLAY_MESSAGE, + "...and you are from Mars?!", + END_DIALOG +}; + +const static char* dialog_205[] = { + CHANGE_CHARACTER, + "It's not a soap opera, it's", + NEW_LINE, + "just usual agent work.", + DISPLAY_MESSAGE, + "But hey, join the RGB and you could", + NEW_LINE, + "work with Anne there!", + CHANGE_CHARACTER, + ANIM_WAIT, + "Well...", + DISPLAY_MESSAGE, + "I'll think about it...", + CHANGE_CHARACTER, + "Great.", + DISPLAY_MESSAGE, + "It was a pleasure to see you in action!", + END_DIALOG +}; + +const static char* dialog_206[] = { + CHANGE_CHARACTER, + "You don't have to tell me.", + DISPLAY_MESSAGE, + "I have already read a very detailed report.", + CHANGE_CHARACTER, + "But what happened to the professor?", + CHANGE_CHARACTER, + "Oh, that poor man has forgotten the pill recipe", + NEW_LINE, + "again.", + DISPLAY_MESSAGE, + "We'll give him the best laboratory we can.", + DISPLAY_MESSAGE, + "Right now we have only those few pills", + NEW_LINE, + "you gave us.", + CHANGE_CHARACTER, + "And John Noty?", + CHANGE_CHARACTER, + "Don't worry, his greediness will be punished.", + CHANGE_CHARACTER, + "I hope so...", + DISPLAY_MESSAGE, + "And one more little thing...", + DISPLAY_MESSAGE, + "You have promised me something...", + END_DIALOG +}; + +const static char* dialog_207[] = { + CHANGE_CHARACTER, + "Me?...", + DISPLAY_MESSAGE, + "I don't remember...", + CHANGE_CHARACTER, + "You said you'll make me an official agent...", + DISPLAY_MESSAGE, + "'Girls love secret agents'. Remember?", + END_DIALOG +}; + +const static char* dialog_208[] = { + CHANGE_CHARACTER, + "(sigh)", + DISPLAY_MESSAGE, + "A promise is a promise...", + DISPLAY_MESSAGE, + "Let me think.", + DISPLAY_MESSAGE, + ANIM_WAIT, + "All right. Come here.", + END_DIALOG +}; + +const static char* dialog_209[] = { + "In the name of...", + DISPLAY_MESSAGE, + "...blah...blah...blah...", + DISPLAY_MESSAGE, + "...blah...blah...", + DISPLAY_MESSAGE, + "...for our country.", + END_DIALOG +}; + +const static char* dialog_210[] = { + " Well... ", + DISPLAY_MESSAGE, + "That's all, folks!", + END_DIALOG +}; + +const static char* dialog_211[] = { + "I found the time pill!", + DISPLAY_MESSAGE, + "It must have fallen out of the jar!...", + DISPLAY_MESSAGE, + ANIM_WAIT, + "Cool.", + END_DIALOG +}; + +const static char* dialog_212[] = { + "Wow!", + DISPLAY_MESSAGE, + "This is charming!...", + END_DIALOG +}; + +const static char** dialogs[] = { + dialog_0, + dialog_1, + dialog_2, + dialog_3, + dialog_4, + dialog_5, + dialog_6, + dialog_7, + dialog_8, + dialog_9, + dialog_10, + dialog_11, + dialog_12, + dialog_13, + dialog_14, + dialog_15, + dialog_16, + dialog_17, + dialog_18, + dialog_19, + dialog_20, + dialog_21, + dialog_22, + dialog_23, + dialog_24, + dialog_25, + dialog_26, + dialog_27, + dialog_28, + dialog_29, + dialog_30, + dialog_31, + dialog_32, + dialog_33, + dialog_34, + dialog_35, + dialog_36, + dialog_37, + dialog_38, + dialog_39, + dialog_40, + dialog_41, + dialog_42, + dialog_43, + dialog_44, + dialog_45, + dialog_46, + dialog_47, + dialog_48, + dialog_49, + dialog_50, + dialog_51, + dialog_52, + dialog_53, + dialog_54, + dialog_55, + dialog_56, + dialog_57, + dialog_58, + dialog_59, + dialog_60, + dialog_61, + dialog_62, + dialog_63, + dialog_64, + dialog_65, + dialog_66, + dialog_67, + dialog_68, + dialog_69, + dialog_70, + dialog_71, + dialog_72, + dialog_73, + dialog_74, + dialog_75, + dialog_76, + dialog_77, + dialog_78, + dialog_79, + dialog_80, + dialog_81, + dialog_82, + dialog_83, + dialog_84, + dialog_85, + dialog_86, + dialog_87, + dialog_88, + dialog_89, + dialog_90, + dialog_91, + dialog_92, + dialog_93, + dialog_94, + dialog_95, + dialog_96, + dialog_97, + dialog_98, + dialog_99, + dialog_100, + dialog_101, + dialog_102, + dialog_103, + dialog_104, + dialog_105, + dialog_106, + dialog_107, + dialog_108, + dialog_109, + dialog_110, + dialog_111, + dialog_112, + dialog_113, + dialog_114, + dialog_115, + dialog_116, + dialog_117, + dialog_118, + dialog_119, + dialog_120, + dialog_121, + dialog_122, + dialog_123, + dialog_124, + dialog_125, + dialog_126, + dialog_127, + dialog_128, + dialog_129, + dialog_130, + dialog_131, + dialog_132, + dialog_133, + dialog_134, + dialog_135, + dialog_136, + dialog_137, + dialog_138, + dialog_139, + dialog_140, + dialog_141, + dialog_142, + dialog_143, + dialog_144, + dialog_145, + dialog_146, + dialog_147, + dialog_148, + dialog_149, + dialog_150, + dialog_151, + dialog_152, + dialog_153, + dialog_154, + dialog_155, + dialog_156, + dialog_157, + dialog_158, + dialog_159, + dialog_160, + dialog_161, + dialog_162, + dialog_163, + dialog_164, + dialog_165, + dialog_166, + dialog_167, + dialog_168, + dialog_169, + dialog_170, + dialog_171, + dialog_172, + dialog_173, + dialog_174, + dialog_175, + dialog_176, + dialog_177, + dialog_178, + dialog_179, + dialog_180, + dialog_181, + dialog_182, + dialog_183, + dialog_184, + dialog_185, + dialog_186, + dialog_187, + dialog_188, + dialog_189, + dialog_190, + dialog_191, + dialog_192, + dialog_193, + dialog_194, + dialog_195, + dialog_196, + dialog_197, + dialog_198, + dialog_199, + dialog_200, + dialog_201, + dialog_202, + dialog_203, + dialog_204, + dialog_205, + dialog_206, + dialog_207, + dialog_208, + dialog_209, + dialog_210, + dialog_211, + dialog_212 +}; + +#endif diff --git a/devtools/create_teenagent/util.cpp b/devtools/create_teenagent/util.cpp new file mode 100644 index 0000000000..5ce8237b85 --- /dev/null +++ b/devtools/create_teenagent/util.cpp @@ -0,0 +1,152 @@ +/* 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. + * + */ + +// Disable symbol overrides so that we can use system headers. +#define FORBIDDEN_SYMBOL_ALLOW_ALL + +#include "util.h" +#include <stdarg.h> + +#ifdef _MSC_VER + #define vsnprintf _vsnprintf +#endif + +void error(const char *s, ...) { + char buf[1024]; + va_list va; + + va_start(va, s); + vsnprintf(buf, 1024, s, va); + va_end(va); + + fprintf(stderr, "ERROR: %s!\n", buf); + + exit(1); +} + +void warning(const char *s, ...) { + char buf[1024]; + va_list va; + + va_start(va, s); + vsnprintf(buf, 1024, s, va); + va_end(va); + + fprintf(stderr, "WARNING: %s!\n", buf); +} + +int scumm_stricmp(const char *s1, const char *s2) { + byte l1, l2; + do { + // Don't use ++ inside tolower, in case the macro uses its + // arguments more than once. + l1 = (byte)*s1++; + l1 = tolower(l1); + l2 = (byte)*s2++; + l2 = tolower(l2); + } while (l1 == l2 && l1 != 0); + return l1 - l2; +} + +void debug(int level, const char *s, ...) { + char buf[1024]; + va_list va; + + va_start(va, s); + vsnprintf(buf, 1024, s, va); + va_end(va); + + fprintf(stderr, "DEBUG: %s!\n", buf); +} + +uint8 readByte(FILE *fp) { + return fgetc(fp); +} + +uint16 readUint16BE(FILE *fp) { + uint16 ret = 0; + ret |= fgetc(fp) << 8; + ret |= fgetc(fp); + return ret; +} + +uint16 readUint16LE(FILE *fp) { + uint16 ret = 0; + ret |= fgetc(fp); + ret |= fgetc(fp) << 8; + return ret; +} + +uint32 readUint32BE(FILE *fp) { + uint32 ret = 0; + ret |= fgetc(fp) << 24; + ret |= fgetc(fp) << 16; + ret |= fgetc(fp) << 8; + ret |= fgetc(fp); + return ret; +} + +uint32 readUint32LE(FILE *fp) { + uint32 ret = 0; + ret |= fgetc(fp); + ret |= fgetc(fp) << 8; + ret |= fgetc(fp) << 16; + ret |= fgetc(fp) << 24; + return ret; +} + +void writeByte(FILE *fp, uint8 b) { + fwrite(&b, 1, 1, fp); +} + +void writeUint16BE(FILE *fp, uint16 value) { + writeByte(fp, (uint8)(value >> 8)); + writeByte(fp, (uint8)(value)); +} + +void writeUint16LE(FILE *fp, uint16 value) { + writeByte(fp, (uint8)(value)); + writeByte(fp, (uint8)(value >> 8)); +} + +void writeUint32BE(FILE *fp, uint32 value) { + writeByte(fp, (uint8)(value >> 24)); + writeByte(fp, (uint8)(value >> 16)); + writeByte(fp, (uint8)(value >> 8)); + writeByte(fp, (uint8)(value)); +} + +void writeUint32LE(FILE *fp, uint32 value) { + writeByte(fp, (uint8)(value)); + writeByte(fp, (uint8)(value >> 8)); + writeByte(fp, (uint8)(value >> 16)); + writeByte(fp, (uint8)(value >> 24)); +} + +uint32 fileSize(FILE *fp) { + uint32 sz; + uint32 pos = ftell(fp); + fseek(fp, 0, SEEK_END); + sz = ftell(fp); + fseek(fp, pos, SEEK_SET); + return sz; +} diff --git a/devtools/create_teenagent/util.h b/devtools/create_teenagent/util.h index 0d8e15cc37..a2783cca71 100644 --- a/devtools/create_teenagent/util.h +++ b/devtools/create_teenagent/util.h @@ -50,6 +50,7 @@ uint32 fileSize(FILE *fp); void NORETURN_PRE error(const char *s, ...) NORETURN_POST; void warning(const char *s, ...); void debug(int level, const char *s, ...); +int scumm_stricmp(const char *s1, const char *s2); using namespace Common; diff --git a/devtools/create_tony/create_tony.cpp b/devtools/create_tony/create_tony.cpp new file mode 100644 index 0000000000..f2d086c083 --- /dev/null +++ b/devtools/create_tony/create_tony.cpp @@ -0,0 +1,183 @@ +/* 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. + * + * This is a utility for storing all the hardcoded data of Tony Tough in a separate + * data file, used by the game engine + */ + +// Disable symbol overrides so that we can use system headers. +#define FORBIDDEN_SYMBOL_ALLOW_ALL + +// HACK to allow building with the SDL backend on MinGW +// see bug #1800764 "TOOLS: MinGW tools building broken" +#ifdef main +#undef main +#endif // main + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "common/scummsys.h" +#include "common/events.h" + +#include "create_tony.h" +#include "staticdata.h" + +static void writeByte(FILE *fp, uint8 b) { + fwrite(&b, 1, 1, fp); +} + +static void writeUint16BE(FILE *fp, uint16 value) { + writeByte(fp, (uint8)(value >> 8)); + writeByte(fp, (uint8)(value & 0xFF)); +} + +void writeSint16BE(FILE *fp, int16 value) { + writeUint16BE(fp, (uint16)value); +} + +static void writeUint32BE(FILE *fp, uint32 value) { + writeByte(fp, (uint8)(value >> 24)); + writeByte(fp, (uint8)((value >> 16) & 0xFF)); + writeByte(fp, (uint8)((value >> 8) & 0xFF)); + writeByte(fp, (uint8)(value & 0xFF)); +} + +void writeSint32BE(FILE *fp, int32 value) { + writeUint32BE(fp, (uint16)value); +} + +int main(int argc, char *argv[]) { + FILE *outFile; + + outFile = fopen("tony.dat", "wb"); + + // Write header + fwrite("TONY", 4, 1, outFile); + + writeByte(outFile, TONY_DAT_VER_MAJ); + writeByte(outFile, TONY_DAT_VER_MIN); + + // game versions/variants + writeUint16BE(outFile, NUM_VARIANTS); + + // Italian + for (int i = 0; i < 256; i++) { + writeSint16BE(outFile, _cTableDialogIta[i]); + writeSint16BE(outFile, _lTableDialogIta[i]); + writeSint16BE(outFile, _cTableMaccIta[i]); + writeSint16BE(outFile, _lTableMaccIta[i]); + writeSint16BE(outFile, _cTableCredIta[i]); + writeSint16BE(outFile, _lTableCredIta[i]); + writeSint16BE(outFile, _cTableObjIta[i]); + writeSint16BE(outFile, _lTableObjIta[i]); + } + + // Polish + for (int i = 0; i < 256; i++) { + writeSint16BE(outFile, _cTableDialogPol[i]); + writeSint16BE(outFile, _lTableDialogPol[i]); + writeSint16BE(outFile, _cTableMaccPol[i]); + writeSint16BE(outFile, _lTableMaccPol[i]); + writeSint16BE(outFile, _cTableCredPol[i]); + writeSint16BE(outFile, _lTableCredPol[i]); + writeSint16BE(outFile, _cTableObjPol[i]); + writeSint16BE(outFile, _lTableObjPol[i]); + } + + //Russian + for (int i = 0; i < 256; i++) { + writeSint16BE(outFile, _cTableDialogRus[i]); + writeSint16BE(outFile, _lTableDialogRus[i]); + writeSint16BE(outFile, _cTableMaccRus[i]); + writeSint16BE(outFile, _lTableMaccRus[i]); + writeSint16BE(outFile, _cTableCredRus[i]); + writeSint16BE(outFile, _lTableCredRus[i]); + writeSint16BE(outFile, _cTableObjRus[i]); + writeSint16BE(outFile, _lTableObjRus[i]); + } + + // Czech + for (int i = 0; i < 256; i++) { + writeSint16BE(outFile, _cTableDialogCze[i]); + writeSint16BE(outFile, _lTableDialogCze[i]); + writeSint16BE(outFile, _cTableMaccCze[i]); + writeSint16BE(outFile, _lTableMaccCze[i]); + writeSint16BE(outFile, _cTableCredCze[i]); + writeSint16BE(outFile, _lTableCredCze[i]); + writeSint16BE(outFile, _cTableObjCze[i]); + writeSint16BE(outFile, _lTableObjCze[i]); + } + + // French + for (int i = 0; i < 256; i++) { + writeSint16BE(outFile, _cTableDialogFra[i]); + writeSint16BE(outFile, _lTableDialogFra[i]); + writeSint16BE(outFile, _cTableMaccFra[i]); + writeSint16BE(outFile, _lTableMaccFra[i]); + writeSint16BE(outFile, _cTableCredFra[i]); + writeSint16BE(outFile, _lTableCredFra[i]); + writeSint16BE(outFile, _cTableObjFra[i]); + writeSint16BE(outFile, _lTableObjFra[i]); + } + + // Deutsch + for (int i = 0; i < 256; i++) { + writeSint16BE(outFile, _cTableDialogDeu[i]); + writeSint16BE(outFile, _lTableDialogDeu[i]); + writeSint16BE(outFile, _cTableMaccDeu[i]); + writeSint16BE(outFile, _lTableMaccDeu[i]); + writeSint16BE(outFile, _cTableCredDeu[i]); + writeSint16BE(outFile, _lTableCredDeu[i]); + writeSint16BE(outFile, _cTableObjDeu[i]); + writeSint16BE(outFile, _lTableObjDeu[i]); + } + + fclose(outFile); + return 0; +} + +void writeTextArray(FILE *outFile, const char *textArray[], int nbrText) { + int len, len1, pad; + uint8 padBuf[DATAALIGNMENT]; + + for (int i = 0; i < DATAALIGNMENT; i++) + padBuf[i] = 0; + + writeUint16BE(outFile, nbrText); + len = DATAALIGNMENT - 2; + for (int i = 0; i < nbrText; i++) { + len1 = strlen(textArray[i]) + 1; + pad = DATAALIGNMENT - (len1 + 2) % DATAALIGNMENT; + len += 2 + len1 + pad; + } + writeUint16BE(outFile, len); + + fwrite(padBuf, DATAALIGNMENT - 2, 1, outFile); // padding + for (int i = 0; i < nbrText; i++) { + len = strlen(textArray[i]) + 1; + pad = DATAALIGNMENT - (len + 2) % DATAALIGNMENT; + + writeUint16BE(outFile, len + pad + 2); + fwrite(textArray[i], len, 1, outFile); + fwrite(padBuf, pad, 1, outFile); + } +} diff --git a/devtools/create_tony/create_tony.h b/devtools/create_tony/create_tony.h new file mode 100644 index 0000000000..3075835bd9 --- /dev/null +++ b/devtools/create_tony/create_tony.h @@ -0,0 +1,44 @@ +/* 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 CREATE_TONY_H +#define CREATE_TONY_H + +#define ARRAYSIZE(x) ((int)(sizeof(x) / sizeof(x[0]))) + +#define DATAALIGNMENT 4 + +#define TONY_DAT_VER_MAJ 0 // 1 byte +#define TONY_DAT_VER_MIN 3 // 1 byte + +// Number of variants of the game. For the moment, it's the same +// as the number of languages +#define NUM_VARIANTS 6 + +typedef unsigned char uint8; +typedef unsigned char byte; +typedef unsigned short uint16; +typedef signed short int16; + +void writeTextArray(FILE *outFile, const char *textData[], int nbrText); + +#endif // CREATE_TONY_H diff --git a/devtools/create_tony/module.mk b/devtools/create_tony/module.mk new file mode 100644 index 0000000000..6ff919032b --- /dev/null +++ b/devtools/create_tony/module.mk @@ -0,0 +1,10 @@ +MODULE := devtools/create_tony + +MODULE_OBJS := \ + create_tony.o + +# Set the name of the executable +TOOL_EXECUTABLE := create_tony + +# Include common rules +include $(srcdir)/rules.mk diff --git a/devtools/create_tony/staticdata.h b/devtools/create_tony/staticdata.h new file mode 100644 index 0000000000..fff977d8dc --- /dev/null +++ b/devtools/create_tony/staticdata.h @@ -0,0 +1,1370 @@ +/* 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 STATICDATA_H +#define STATICDATA_H + +const int _cTableDialogIta[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 71, 77, -1, 80, 81, 82, 111, + 75, 76, -1, 68, 63, 66, 64, 78, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 61, 65, 62, + 69, 83, 70, 73, -1, 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, -1, -1, -1, 77, 67, -1, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 79, -1, -1, -1, -1, -1, 87, + -1, 84, -1, -1, 86, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 85, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 108, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 105, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 88, -1, -1, -1, 93, 99, + 107, 106, 89, 89, -1, 94, 90, -1, -1, 95, + -1, 104, 91, -1, -1, -1, 96, -1, 109, 92, + -1, -1, 97, -1, -1, 98}; + +const int _lTableDialogIta[] = { + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 9, 5, 5, 13, 13, 13, 13, 5, + 7, 7, 13, 13, 5, 13, 5, 13, 13, 13, + 13, 13, 10, 13, 13, 13, 13, 13, 5, 5, + 13, 13, 13, 10, 13, 13, 13, 13, 13, 10, + 11, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 5, 13, 13, 14, 15, 12, + 13, 12, 13, 13, 13, 6, 13, 13, 5, 16, + 12, 11, 11, 13, 13, 12, 13, 12, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13}; + +const int _cTableDialogPol[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 71, 77, -1, 80, 81, 82, 111, + 75, 76, -1, 68, 63, 66, 64, 78, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 61, 65, 62, + 69, 83, 70, 73, -1, 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, -1, -1, -1, 77, 67, -1, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 124, -1, -1, 128, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 125, -1, -1, 129, + -1, -1, -1, 118, -1, 112, -1, -1, -1, 87, + -1, 84, -1, -1, 86, 126, -1, -1, -1, 119, + -1, -1, -1, -1, -1, 113, -1, 85, -1, -1, + -1, 127, -1, -1, -1, -1, -1, -1, 114, -1, + -1, -1, 116, -1, -1, -1, -1, -1, -1, 120, + -1, 122, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 88, -1, -1, -1, 93, 99, + 115, 106, 89, 89, 117, 94, 90, -1, -1, 95, + -1, 121, 91, 123, -1, -1, 96, -1, 109, 92, + -1, -1, 97, -1, -1, 98}; + +const int _lTableDialogPol[] = { + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 9, 5, 5, 13, 13, 13, 13, 5, + 7, 7, 13, 13, 5, 13, 5, 13, 13, 13, + 13, 13, 10, 13, 13, 13, 13, 13, 5, 5, + 13, 13, 13, 10, 13, 13, 13, 13, 13, 10, + 11, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 5, 13, 13, 14, 15, 12, + 13, 12, 13, 13, 13, 6, 13, 13, 5, 16, + 12, 11, 11, 13, 13, 12, 13, 12, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 12, 13, 13, 14, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 12, 13, 13, 13, + 13, 13, 13, 14, 13, 14, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 9, + 13, 13, 13, 13, 13, 16, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 12, 13, + 13, 13, 11, 13, 13, 13, 13, 13, 13, 10, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 12, 13, 13, 13, 12, 13, 13, 13, 13, 13, + 13, 11, 13, 11, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13}; + +const int _cTableDialogRus[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 71, 77, -1, 80, 81, 82, 111, + 75, 76, -1, 68, 63, 66, 64, 78, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 61, 65, 62, + 69, 83, 70, 73, -1, 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, -1, -1, -1, 77, 67, -1, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 79, -1, -1, -1, -1, 136, 87, + -1, 84, -1, -1, 86, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 169, -1, -1, 85, -1, -1, + -1, -1, 130, 131, 132, 133, 134, 135, 137, 138, + 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, + 149, 150, 151, 152, 153, 154, 155, 156, 158, 159, + 157, 160, 161, 162, 163, 164, 165, 166, 167, 168, + 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, + 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, + 191, 192, 190, 193, 194, 195}; + +const int _lTableDialogRus[] = { + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 9, 5, 5, 13, 13, 13, 13, 5, + 7, 7, 13, 13, 5, 13, 5, 13, 13, 13, + 13, 13, 10, 13, 13, 13, 13, 13, 5, 5, + 13, 13, 13, 10, 13, 13, 13, 13, 13, 10, + 11, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 5, 13, 13, 14, 15, 12, + 13, 12, 13, 13, 13, 6, 13, 13, 5, 16, + 12, 11, 11, 13, 13, 12, 13, 12, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 11, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 12, 13, 13, 13, 13, 13, + 13, 13, 13, 15, 15, 11, 15, 11, 15, 10, + 13, 13, 12, 13, 14, 14, 13, 11, 12, 12, + 18, 11, 13, 12, 13, 12, 17, 18, 18, 19, + 16, 11, 16, 14, 14, 15, 10, 12, 13, 12, + 12, 10, 10, 10, 11, 12, 12, 12, 12, 10, + 11, 10, 14, 8, 11, 11, 12, 10, 15, 16, + 16, 16, 14, 9, 15, 14}; + +const int _cTableDialogCze[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 71, 77, -1, 80, 81, 82, 111, + 75, 76, -1, 68, 63, 66, 64, 78, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 61, 65, 62, + 69, 83, 70, 73, -1, 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, -1, -1, -1, 77, 67, -1, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 197, -1, + -1, 206, 200, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 212, -1, -1, 221, 215, -1, + -1, -1, -1, 79, -1, -1, -1, -1, -1, 87, + -1, 84, -1, -1, 86, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 85, -1, -1, + -1, -1, -1, 202, -1, -1, -1, -1, 108, -1, + 198, 204, -1, -1, 196, 203, -1, 205, -1, 105, + 207, 208, -1, -1, -1, -1, 199, 209, 210, -1, + -1, 201, -1, -1, 88, 217, -1, -1, 93, 99, + 107, 106, 213, 219, -1, 94, 211, 218, -1, 220, + -1, 104, 222, 223, -1, -1, 96, -1, 214, 224, + 225, -1, 97, 216, -1, 98}; + +const int _lTableDialogCze[] = { + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 9, 5, 5, 13, 13, 13, 13, 5, + 7, 7, 13, 13, 5, 13, 5, 13, 13, 13, + 13, 13, 10, 13, 13, 13, 13, 13, 5, 5, + 13, 13, 13, 10, 13, 13, 13, 13, 13, 10, + 11, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 5, 13, 13, 14, 15, 12, + 13, 12, 13, 13, 13, 6, 13, 13, 5, 16, + 12, 11, 11, 13, 13, 12, 13, 12, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 12, 13, + 13, 19, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 12, 13, 13, 16, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 12, 11, 13, 13, 11, 11, 13, 15, 13, 13, + 10, 13, 13, 13, 13, 13, 14, 13, 13, 13, + 13, 11, 13, 13, 13, 15, 13, 13, 13, 13, + 13, 13, 12, 12, 13, 13, 12, 7, 13, 17, + 13, 13, 11, 11, 13, 13, 13, 13, 12, 13, + 13, 13, 13, 11, 13, 13}; + +const int _cTableDialogFra[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 71, 77, -1, 80, 81, 82, 111, + 75, 76, -1, 68, 63, 66, 64, 78, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 61, 65, 62, + 69, 83, 70, 73, -1, 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, -1, -1, -1, 77, 67, -1, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 79, -1, -1, -1, -1, -1, 87, + -1, 84, -1, -1, 86, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 85, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 108, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 105, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 88, -1, 226, -1, 93, 99, + 107, 106, 89, 227, 228, 94, 90, -1, 229, 95, + -1, 104, 91, -1, 232, -1, 233, -1, 109, 230, + -1, 231, 97, -1, -1, 98}; + +const int _lTableDialogFra[] = { + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 9, 5, 5, 13, 13, 13, 13, 5, + 7, 7, 13, 13, 5, 13, 5, 13, 13, 13, + 13, 13, 10, 13, 13, 13, 13, 13, 5, 5, + 13, 13, 13, 10, 13, 13, 13, 13, 13, 10, + 11, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 5, 13, 13, 14, 15, 12, + 13, 12, 13, 13, 13, 6, 13, 13, 5, 16, + 12, 11, 11, 13, 13, 12, 13, 12, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 15, 13, 13, 13, + 13, 13, 13, 12, 12, 13, 13, 13, 9, 13, + 13, 13, 13, 13, 11, 13, 11, 13, 13, 13, + 13, 13, 13, 13, 13, 13}; + +const int _cTableDialogDeu[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 71, 77, -1, 80, 81, 82, 111, + 75, 76, -1, 68, 63, 66, 64, 78, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 61, 65, 62, + 69, 83, 70, 73, -1, 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, -1, -1, -1, 77, 67, -1, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 79, -1, -1, -1, -1, -1, 87, + -1, 84, -1, -1, 86, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 85, -1, -1, + -1, -1, -1, -1, -1, -1, 236, -1, 108, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 105, + -1, -1, -1, -1, 237, -1, -1, -1, -1, -1, + 238, -1, -1, 234, 88, -1, -1, -1, 93, 99, + 107, 106, 89, 89, -1, 94, 90, -1, -1, 95, + -1, 104, 91, -1, -1, -1, 96, -1, 109, 92, + -1, -1, 97, -1, -1, 98}; + +const int _lTableDialogDeu[] = { + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 9, 5, 5, 13, 13, 13, 13, 5, + 7, 7, 13, 13, 5, 13, 5, 13, 13, 13, + 13, 13, 10, 13, 13, 13, 13, 13, 5, 5, + 13, 13, 13, 10, 13, 13, 13, 13, 13, 10, + 11, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 5, 13, 13, 14, 15, 12, + 13, 12, 13, 13, 13, 6, 13, 13, 5, 16, + 12, 11, 11, 13, 13, 12, 13, 12, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 15, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13}; + +const int _cTableMaccIta[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 62, 64, -1, 65, 66, 67, -1, + 69, 70, 74, 75, 78, 81, 79, 84, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 61, 80, 77, + 82, 71, 83, 72, -1, 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 86, -1, -1, -1, 87, 88, + -1, 101, 89, -1, -1, 90, 92, -1, -1, 93, + -1, 76, 95, -1, -1, -1, 96, -1, -1, 98, + -1, -1, 99, -1, -1, 85}; + +const int _lTableMaccIta[] = { + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10}; + +const int _cTableMaccPol[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 62, 64, -1, 65, 66, 67, -1, + 69, 70, 74, 75, 78, 81, 79, 84, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 61, 80, 77, + 82, 71, 83, 72, -1, 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 114, -1, -1, 118, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 115, -1, -1, 119, + -1, -1, -1, 108, -1, 102, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 116, -1, -1, -1, 109, + -1, -1, -1, -1, -1, 103, -1, -1, -1, -1, + -1, 117, -1, -1, -1, -1, -1, -1, 104, -1, + -1, -1, 106, -1, -1, -1, -1, -1, -1, 110, + -1, 112, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 86, -1, -1, -1, 87, 88, + 105, 101, 89, -1, 107, 90, 92, -1, -1, 93, + -1, 111, 95, 113, -1, -1, 96, -1, -1, 98, + -1, -1, 99, -1, -1, 85}; + +const int _lTableMaccPol[] = { + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 12, 10, 10, 14, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 12, 10, 10, 13, + 10, 10, 10, 14, 10, 14, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 13, 10, 10, 10, 9, + 10, 10, 10, 10, 10, 16, 10, 10, 10, 10, + 10, 13, 10, 10, 10, 10, 10, 10, 12, 10, + 10, 10, 11, 10, 10, 10, 10, 10, 10, 10, + 10, 13, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 12, 10, 10, 10, 12, 10, 10, 10, 10, 10, + 10, 11, 10, 11, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10}; + +const int _cTableMaccRus[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 62, 64, -1, 65, 66, 67, -1, + 69, 70, 74, 75, 78, 81, 79, 84, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 61, 80, 77, + 82, 71, 83, 72, -1, 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 126, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 159, -1, -1, -1, -1, -1, + -1, -1, 120, 121, 122, 123, 124, 125, 127, 128, + 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, + 139, 140, 141, 142, 143, 144, 145, 146, 148, 149, + 147, 150, 151, 152, 153, 154, 155, 156, 157, 158, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, + 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, + 181, 182, 180, 183, 184, 185}; + +const int _lTableMaccRus[] = { + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 9, 10, 10, 10, 10, 10, + 10, 10, 11, 11, 11, 9, 10, 10, 11, 10, + 10, 10, 11, 9, 11, 10, 11, 8, 10, 10, + 11, 11, 11, 11, 10, 10, 10, 10, 11, 11, + 11, 11, 11, 11, 10, 10, 11, 10, 9, 10, + 10, 9, 11, 11, 11, 11, 11, 11, 10, 9, + 11, 10, 9, 11, 10, 11, 10, 10, 11, 11, + 10, 10, 10, 9, 11, 11}; + +const int _cTableMaccCze[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 62, 64, -1, 65, 66, 67, -1, + 69, 70, 74, 75, 78, 81, 79, 84, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 61, 80, 77, + 82, 71, 83, 72, -1, 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, + -1, 196, 190, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 202, -1, -1, 211, 205, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 192, -1, -1, -1, -1, -1, -1, + 188, 194, -1, -1, 186, 193, -1, 195, -1, -1, + 197, 198, -1, -1, -1, -1, 189, 199, 200, -1, + -1, 191, -1, -1, 86, 207, -1, -1, 87, 88, + -1, 101, 203, 209, -1, 90, 201, 208, -1, 210, + -1, 76, 212, 213, -1, -1, 96, -1, 204, 214, + 215, -1, 99, 206, -1, 85}; + +const int _lTableMaccCze[] = { + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 11, 9, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 9, 10, 10, 11, 9, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 11, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 9, 10, 11, 10, 10, + 11, 11, 10, 10, 10, 10, 11, 11, 11, 10, + 10, 11, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 9, 10, 11, + 10, 10, 11, 10, 10, 10, 10, 10, 10, 11, + 11, 10, 10, 11, 10, 10}; + +const int _cTableMaccFra[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 62, 64, -1, 65, 66, 67, -1, + 69, 70, 74, 75, 78, 81, 79, 84, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 61, 80, 77, + 82, 71, 83, 72, -1, 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 86, -1, 226, -1, 87, 88, + -1, 101, 228, 227, -1, 90, 92, -1, 229, 93, + -1, 76, 95, -1, 232, -1, 233, -1, -1, 230, + -1, 231, 99, -1, -1, 85}; + +const int _lTableMaccFra[] = { + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 8, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, + 10, 11, 10, 10, 10, 10}; + +const int _cTableMaccDeu[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 62, 64, -1, 65, 66, 67, -1, + 69, 70, 74, 75, 78, 81, 79, 84, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 61, 80, 77, + 82, 71, 83, 72, -1, 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 236, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 237, -1, -1, -1, -1, -1, + 238, -1, -1, 234, 86, -1, -1, -1, 87, 88, + -1, 101, 89, -1, -1, 90, 92, -1, -1, 93, + -1, 76, 95, -1, -1, -1, 96, -1, -1, 98, + -1, -1, 99, -1, -1, 85}; + +const int _lTableMaccDeu[] = { + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 11, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10}; + +const int _cTableCredIta[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 81, -1, -1, -1, 90, 91, 111, + 84, 85, 99, 93, 95, -1, 100, 92, 101, 102, + 103, 104, 105, 106, 107, 108, 109, 110, 89, 94, + -1, 97, -1, 79, -1, 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, -1, -1, -1, 96, 98, -1, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 77, + -1, 86, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 52, 53, -1, -1, -1, -1, + -1, 74, 56, 57, -1, -1, 60, 61, -1, -1, + -1, 73, 64, 65, -1, -1, -1, -1, -1, 68, + 69, -1, -1, -1, -1, -1}; + +const int _lTableCredIta[] = { + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 11, 10, 10, 10, 10, 10, 10, 5, + 10, 10, 10, 10, 5, 10, 5, 10, 12, 8, + 10, 11, 12, 11, 12, 10, 11, 10, 5, 5, + 10, 10, 10, 10, 10, 19, 15, 14, 13, 14, + 13, 16, 15, 5, 8, 15, 13, 17, 15, 14, + 12, 14, 14, 15, 11, 12, 12, 16, 12, 13, + 14, 10, 10, 10, 9, 10, 10, 11, 9, 9, + 10, 9, 8, 9, 10, 5, 6, 12, 6, 14, + 10, 11, 11, 9, 9, 9, 6, 9, 10, 14, + 9, 10, 9, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 19, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10}; + +const int _cTableCredPol[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 81, -1, -1, -1, 90, 91, 111, + 84, 85, 99, 93, 95, -1, 100, 92, 101, 102, + 103, 104, 105, 106, 107, 108, 109, 110, 89, 94, + -1, 97, -1, 79, -1, 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, -1, -1, -1, 96, 98, -1, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 124, -1, -1, 128, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 125, -1, -1, 129, + -1, -1, -1, 118, -1, 112, -1, -1, -1, 77, + -1, 86, -1, -1, -1, 126, -1, -1, -1, 119, + -1, -1, -1, -1, -1, 113, -1, 87, -1, -1, + -1, 127, -1, -1, -1, -1, -1, -1, 114, -1, + -1, -1, 116, -1, -1, -1, -1, -1, -1, 120, + -1, 122, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 52, 53, -1, -1, -1, -1, + 115, 74, 56, 57, 117, -1, 60, 61, -1, -1, + -1, 121, 64, 123, -1, -1, -1, -1, -1, 68, + 69, -1, -1, -1, -1, -1}; + +const int _lTableCredPol[] = { + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 11, 10, 10, 10, 10, 10, 10, 5, + 10, 10, 10, 10, 5, 10, 5, 10, 12, 8, + 10, 11, 12, 11, 12, 10, 11, 10, 5, 5, + 10, 10, 10, 10, 10, 19, 15, 14, 13, 14, + 13, 16, 15, 5, 8, 15, 13, 17, 15, 14, + 12, 14, 14, 15, 11, 12, 12, 16, 12, 13, + 14, 10, 10, 10, 9, 10, 10, 11, 9, 9, + 10, 9, 8, 9, 10, 5, 6, 12, 6, 14, + 10, 11, 11, 9, 9, 9, 6, 9, 10, 14, + 9, 10, 9, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 15, 10, 10, 15, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 14, 10, 20, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 15, 10, 10, 10, 11, + 10, 10, 10, 10, 10, 12, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 19, 10, 15, 10, + 10, 10, 15, 10, 10, 10, 10, 10, 10, 16, + 10, 15, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 11, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10}; + +const int _cTableCredRus[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 81, -1, -1, -1, 90, 91, 111, + 84, 85, 99, 93, 95, -1, 100, 92, 101, 102, + 103, 104, 105, 106, 107, 108, 109, 110, 89, 94, + -1, 97, -1, 79, -1, 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, -1, -1, -1, 96, 98, -1, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 136, 77, + -1, 86, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 169, -1, -1, 87, -1, -1, + -1, -1, 130, 131, 132, 133, 134, 135, 137, 138, + 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, + 149, 150, 151, 152, 153, 154, 155, 156, 158, 159, + 157, 160, 161, 162, 163, 164, 165, 166, 167, 168, + 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, + 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, + 191, 192, 190, 193, 194, 195}; + +const int _lTableCredRus[] = { + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 11, 10, 10, 10, 10, 10, 10, 5, + 10, 10, 10, 10, 5, 10, 5, 10, 12, 8, + 10, 11, 12, 11, 12, 10, 11, 10, 5, 5, + 10, 10, 10, 10, 10, 19, 15, 14, 13, 14, + 13, 16, 15, 5, 8, 15, 13, 17, 15, 14, + 12, 14, 14, 15, 11, 12, 12, 16, 12, 13, + 14, 10, 10, 10, 9, 10, 10, 11, 9, 9, + 10, 9, 8, 9, 10, 5, 6, 12, 6, 14, + 10, 11, 11, 9, 9, 9, 6, 9, 10, 14, + 9, 10, 9, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 15, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 11, 10, 10, 10, 10, 10, + 10, 10, 20, 16, 16, 14, 22, 15, 20, 12, + 16, 16, 16, 22, 18, 16, 15, 14, 13, 15, + 12, 14, 15, 13, 16, 14, 23, 23, 12, 16, + 10, 12, 20, 15, 12, 10, 10, 11, 16, 10, + 13, 12, 13, 13, 12, 13, 14, 11, 11, 11, + 12, 10, 10, 10, 11, 10, 11, 10, 15, 15, + 12, 16, 10, 11, 13, 11}; + +const int _cTableCredCze[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 81, -1, -1, -1, 90, 91, 111, + 84, 85, 99, 93, 95, -1, 100, 92, 101, 102, + 103, 104, 105, 106, 107, 108, 109, 110, 89, 94, + -1, 97, -1, 79, -1, 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, -1, -1, -1, 96, 98, -1, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 197, -1, + -1, 206, 200, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 212, -1, -1, 221, 215, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 77, + -1, 86, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, + -1, -1, -1, 202, -1, -1, -1, -1, -1, -1, + 198, 204, -1, -1, 196, 203, -1, 205, -1, -1, + 207, 208, -1, -1, -1, -1, 199, 209, 210, -1, + -1, 201, -1, -1, 52, 217, -1, -1, -1, -1, + -1, 74, 213, 219, -1, -1, 211, 218, -1, 220, + -1, 73, 222, 223, -1, -1, -1, -1, 214, 224, + 225, -1, -1, 216, -1, -1}; + +const int _lTableCredCze[] = { + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 11, 10, 10, 10, 10, 10, 10, 5, + 10, 10, 10, 10, 5, 10, 5, 10, 12, 8, + 10, 11, 12, 11, 12, 10, 11, 10, 5, 5, + 10, 10, 10, 10, 10, 19, 15, 14, 13, 14, + 13, 16, 15, 5, 8, 15, 13, 17, 15, 14, + 12, 14, 14, 15, 11, 12, 12, 16, 12, 13, + 14, 10, 10, 10, 9, 10, 10, 11, 9, 9, + 10, 9, 8, 9, 10, 5, 6, 12, 6, 14, + 10, 11, 11, 9, 9, 9, 6, 9, 10, 14, + 9, 10, 9, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 15, 10, + 10, 19, 15, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 11, 10, 10, 12, 11, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 20, 10, 10, 19, 10, 10, 10, + 15, 15, 10, 10, 15, 7, 10, 20, 10, 10, + 16, 15, 10, 10, 10, 10, 15, 13, 13, 10, + 10, 14, 10, 10, 10, 12, 10, 10, 10, 10, + 10, 10, 11, 10, 10, 10, 11, 6, 10, 15, + 10, 10, 11, 11, 10, 10, 10, 10, 11, 10, + 10, 10, 10, 10, 10, 10}; + +const int _cTableCredFra[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 81, -1, -1, -1, 90, 91, 111, + 84, 85, 99, 93, 95, -1, 100, 92, 101, 102, + 103, 104, 105, 106, 107, 108, 109, 110, 89, 94, + -1, 97, -1, 79, -1, 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, -1, -1, -1, 96, 98, -1, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 77, + -1, 86, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 52, 53, 226, -1, -1, -1, + -1, 74, 56, 227, 228, -1, 60, 61, 229, -1, + -1, 73, 64, 65, 232, -1, 233, -1, -1, 230, + 69, 231, -1, -1, -1, -1}; + +const int _lTableCredFra[] = { + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 11, 10, 10, 10, 10, 10, 10, 5, + 10, 10, 10, 10, 5, 10, 5, 10, 12, 8, + 10, 11, 12, 11, 12, 10, 11, 10, 5, 5, + 10, 10, 10, 10, 10, 19, 15, 14, 13, 14, + 13, 16, 15, 5, 8, 15, 13, 17, 15, 14, + 12, 14, 14, 15, 11, 12, 12, 16, 12, 13, + 14, 10, 10, 10, 9, 10, 10, 11, 9, 9, + 10, 9, 8, 9, 10, 5, 6, 12, 6, 14, + 10, 11, 11, 9, 9, 9, 6, 9, 10, 14, + 9, 10, 9, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 19, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 12, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 6, 10, + 10, 10, 10, 10, 11, 10, 11, 10, 10, 10, + 10, 10, 10, 10, 10, 10}; + +const int _cTableCredDeu[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 81, -1, -1, -1, 90, 91, 111, + 84, 85, 99, 93, 95, -1, 100, 92, 101, 102, + 103, 104, 105, 106, 107, 108, 109, 110, 89, 94, + -1, 97, -1, 79, -1, 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, -1, -1, -1, 96, 98, -1, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 77, + -1, 86, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, + -1, -1, -1, -1, -1, -1, 55, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 67, -1, -1, -1, -1, -1, + 71, -1, -1, 234, 52, 53, -1, -1, 55, -1, + -1, 74, 56, 57, -1, -1 , 60, 61, -1, -1, + -1, 73, 64, 65, -1, -1, 67, -1, -1, 68, + 69, -1, 71, -1, -1, -1}; + +const int _lTableCredDeu[] = { + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 11, 10, 10, 10, 10, 10, 10, 5, + 10, 10, 10, 10, 5, 10, 5, 10, 12, 8, + 10, 11, 12, 11, 12, 10, 11, 10, 5, 5, + 10, 10, 10, 10, 10, 19, 15, 14, 13, 14, + 13, 16, 15, 5, 8, 15, 13, 17, 15, 14, + 12, 14, 14, 15, 11, 12, 12, 16, 12, 13, + 14, 10, 10, 10, 9, 10, 10, 11, 9, 9, + 10, 9, 8, 9, 10, 5, 6, 12, 6, 14, + 10, 11, 11, 9, 9, 9, 6, 9, 10, 14, + 9, 10, 9, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 19, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 11, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10}; + +const int _cTableObjIta[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 42, 51, -1, 53, 54, 55, 50, + 47, 48, 57, 41, 36, 40, 38, 46, 26, 27, + 28, 29, 30, 31, 32, 33, 34, 35, 39, 37, + 58, 49, 59, 44, -1, 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, -1, -1, -1, 56, -1, -1, 0, 1, 2, + 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 91, -1, -1, 93, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 91, -1, -1, 93, + -1, -1, -1, 88, -1, 85, -1, -1, -1, 67, + -1, 60, -1, -1, 66, 92, -1, -1, -1, 88, + -1, -1, -1, -1, -1, 85, -1, 61, -1, -1, + -1, 92, -1, 69, -1, -1, 70, 71, 86, -1, + 72, -1, 87, 73, 75, -1, -1, 76, -1, 89, + 78, 90, -1, -1, 79, -1, -1, 81, -1, -1, + 82, -1, -1, -1, -1, -1, -1, -1, 70, -1, + 86, 63, -1, -1, 87, -1, -1, -1, -1, -1, + -1, 89, -1, 90, -1, -1, 79, -1, 62, -1, + -1, -1, 82, -1, -1, -1}; + +const int _lTableObjIta[] = { + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 11, 26, 26, 26, 26, 26, 26, 8, + 26, 26, 26, 26, 26, 12, 8, 26, 20, 20, + 15, 20, 20, 20, 20, 20, 20, 20, 26, 26, + 26, 26, 26, 26, 26, 17, 17, 19, 17, 15, + 17, 19, 17, 16, 26, 17, 14, 19, 17, 19, + 17, 19, 14, 13, 15, 15, 13, 19, 15, 13, + 20, 26, 26, 26, 26, 26, 26, 17, 17, 19, + 17, 15, 17, 19, 17, 16, 26, 17, 14, 19, + 17, 19, 17, 19, 14, 13, 15, 15, 13, 19, + 15, 13, 20, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 15, 26, 26, 21, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 15, 26, 26, 21, + 26, 26, 26, 19, 26, 20, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 21, 26, 26, 26, 19, + 26, 26, 26, 26, 26, 20, 26, 26, 26, 26, + 26, 21, 26, 26, 26, 26, 17, 26, 22, 26, + 26, 26, 17, 26, 26, 26, 26, 26, 26, 17, + 26, 22, 26, 26, 19, 26, 26, 26, 26, 26, + 15, 26, 26, 26, 26, 26, 26, 26, 17, 26, + 22, 26, 26, 26, 17, 26, 26, 26, 26, 26, + 26, 17, 26, 22, 26, 26, 19, 26, 26, 26, + 26, 26, 15, 26, 26, 26}; + +const int _cTableObjPol[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 42, 51, -1, 53, 54, 55, 50, + 47, 48, 57, 41, 36, 40, 38, 46, 26, 27, + 28, 29, 30, 31, 32, 33, 34, 35, 39, 37, + 58, 49, 59, 44, -1, 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, -1, -1, -1, 56, -1, -1, 0, 1, 2, + 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 91, -1, -1, 93, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 91, -1, -1, 93, + -1, -1, -1, 88, -1, 85, -1, -1, -1, 67, + -1, 60, -1, -1, 66, 92, -1, -1, -1, 88, + -1, -1, -1, -1, -1, 85, -1, 61, -1, -1, + -1, 92, -1, 69, -1, -1, 70, 71, 86, -1, + 72, -1, 87, 73, 75, -1, -1, 76, -1, 89, + 78, 90, -1, -1, 79, -1, -1, 81, -1, -1, + 82, -1, -1, -1, -1, -1, -1, -1, 70, -1, + 86, 63, -1, -1, 87, -1, -1, -1, -1, -1, + -1, 89, -1, 90, -1, -1, 79, -1, 62, -1, + -1, -1, 82, -1, -1, -1}; + +const int _lTableObjPol[] = { + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 11, 26, 26, 26, 26, 26, 26, 8, + 26, 26, 26, 26, 26, 12, 8, 26, 20, 20, + 15, 20, 20, 20, 20, 20, 20, 20, 26, 26, + 26, 26, 26, 26, 26, 17, 17, 19, 17, 15, + 17, 19, 17, 16, 26, 17, 14, 19, 17, 19, + 17, 19, 14, 13, 15, 15, 13, 19, 15, 13, + 20, 26, 26, 26, 26, 26, 26, 17, 17, 19, + 17, 15, 17, 19, 17, 16, 26, 17, 14, 19, + 17, 19, 17, 19, 14, 13, 15, 15, 13, 19, + 15, 13, 20, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 15, 26, 26, 21, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 15, 26, 26, 21, + 26, 26, 26, 19, 26, 20, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 21, 26, 26, 26, 19, + 26, 26, 26, 26, 26, 20, 26, 26, 26, 26, + 26, 21, 26, 26, 26, 26, 17, 26, 22, 26, + 26, 26, 17, 26, 26, 26, 26, 26, 26, 17, + 26, 22, 26, 26, 19, 26, 26, 26, 26, 26, + 15, 26, 26, 26, 26, 26, 26, 26, 17, 26, + 22, 26, 26, 26, 17, 26, 26, 26, 26, 26, + 26, 17, 26, 22, 26, 26, 19, 26, 26, 26, + 26, 26, 15, 26, 26, 26}; + +const int _cTableObjRus[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 42, 51, -1, 53, 54, 55, 50, + 47, 48, 57, 41, 36, 40, 38, 46, 26, 27, + 28, 29, 30, 31, 32, 33, 34, 35, 39, 37, + 58, 49, 59, 44, -1, 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, -1, -1, -1, 56, -1, -1, 0, 1, 2, + 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 91, -1, -1, 93, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 91, -1, -1, 93, + -1, -1, -1, 88, -1, 85, -1, -1, 100, 67, + -1, 60, -1, -1, 66, 92, -1, -1, -1, 88, + -1, -1, -1, -1, 100, 85, -1, 61, -1, -1, + -1, 92, 94, 95, 96, 97, 98, 99, 101, 102, + 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, + 113, 114, 115, 116, 117, 118, 119, 120, 122, 123, + 121, 124, 125, 126, 94, 95, 96, 97, 98, 99, + 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, + 122, 123, 121, 124, 125, 126}; + +const int _lTableObjRus[] = { + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 11, 26, 26, 26, 26, 26, 26, 8, + 26, 26, 26, 26, 26, 12, 8, 26, 20, 20, + 15, 20, 20, 20, 20, 20, 20, 20, 26, 26, + 26, 26, 26, 26, 26, 17, 17, 19, 17, 15, + 17, 19, 17, 16, 26, 17, 14, 19, 17, 19, + 17, 19, 14, 13, 15, 15, 13, 19, 15, 13, + 20, 26, 26, 26, 26, 26, 26, 17, 17, 19, + 17, 15, 17, 19, 17, 16, 26, 17, 14, 19, + 17, 19, 17, 19, 14, 13, 15, 15, 13, 19, + 15, 13, 20, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 15, 26, 26, 21, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 15, 26, 26, 21, + 26, 26, 26, 19, 26, 20, 26, 26, 18, 26, + 26, 26, 26, 26, 26, 21, 26, 26, 26, 19, + 26, 26, 26, 26, 18, 20, 26, 26, 26, 26, + 26, 21, 18, 18, 18, 17, 16, 18, 20, 18, + 18, 18, 18, 16, 18, 15, 22, 15, 18, 22, + 19, 16, 21, 20, 16, 16, 19, 22, 19, 19, + 18, 15, 18, 18, 18, 18, 18, 17, 16, 18, + 20, 18, 18, 18, 18, 16, 18, 15, 22, 15, + 18, 22, 19, 16, 21, 20, 16, 16, 19, 22, + 19, 19, 18, 15, 18, 18}; + +const int _cTableObjCze[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 42, 51, -1, 53, 54, 55, 50, + 47, 48, 57, 41, 36, 40, 38, 46, 26, 27, + 28, 29, 30, 31, 32, 33, 34, 35, 39, 37, + 58, 49, 59, 44, -1, 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, -1, -1, -1, 56, -1, -1, 0, 1, 2, + 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 128, -1, + 91, 137, 131, 93, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 128, -1, 91, 137, 131, 93, + -1, -1, -1, 88, -1, 85, -1, -1, -1, 67, + -1, 60, -1, -1, 66, 92, -1, -1, -1, 88, + -1, -1, -1, -1, -1, 85, -1, 61, -1, -1, + -1, 92, -1, 133, -1, -1, 70, 71, 86, -1, + 129, 135, 87, 73, 127, 134, -1, 136, -1, 89, + 138, 139, -1, -1, 79, -1, 130, 140, 141, -1, + 82, 132, -1, -1, -1, 133, -1, -1, 70, -1, + 86, 63, 129, 135, 87, -1, 127, 134, -1, 136, + -1, 89, 138, 139, -1, -1, 79, -1, 130, 140, + 141, -1, 82, 132, -1, -1}; + +const int _lTableObjCze[] = { + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 11, 26, 26, 26, 26, 26, 26, 8, + 26, 26, 26, 26, 26, 12, 8, 26, 20, 20, + 15, 20, 20, 20, 20, 20, 20, 20, 26, 26, + 26, 26, 26, 26, 26, 17, 17, 19, 17, 15, + 17, 19, 17, 16, 26, 17, 14, 19, 17, 19, + 17, 19, 14, 13, 15, 15, 13, 19, 15, 13, + 20, 26, 26, 26, 26, 26, 26, 17, 17, 19, + 17, 15, 17, 19, 17, 16, 26, 17, 14, 19, + 17, 19, 17, 19, 14, 13, 15, 15, 13, 19, + 15, 13, 20, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 15, 26, + 15, 24, 21, 21, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 15, 26, 15, 24, 21, 21, + 26, 26, 26, 19, 26, 20, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 21, 26, 26, 26, 19, + 26, 26, 26, 26, 26, 20, 26, 26, 26, 26, + 26, 21, 26, 18, 26, 26, 17, 26, 22, 26, + 22, 17, 17, 26, 17, 19, 26, 23, 26, 17, + 17, 22, 26, 26, 19, 26, 18, 16, 16, 26, + 15, 16, 26, 26, 26, 18, 26, 26, 17, 26, + 22, 26, 22, 17, 17, 26, 17, 19, 26, 23, + 26, 17, 17, 22, 26, 26, 19, 26, 18, 16, + 16, 26, 15, 16, 26, 26}; + +const int _cTableObjFra[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 42, 51, -1, 53, 54, 55, 50, + 47, 48, 57, 41, 36, 40, 38, 46, 26, 27, + 28, 29, 30, 31, 32, 33, 34, 35, 39, 37, + 58, 49, 59, 44, -1, 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, -1, -1, -1, 56, -1, -1, 0, 1, 2, + 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 91, -1, -1, 93, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 91, -1, -1, 93, + -1, -1, -1, 88, -1, 85, -1, -1, -1, 67, + -1, 60, -1, -1, 66, 92, -1, -1, -1, 88, + -1, -1, -1, -1, -1, 85, -1, 61, -1, -1, + -1, 92, -1, 69, -1, -1, 70, 71, 86, -1, + 72, -1, 87, 73, 75, -1, -1, 76, -1, 89, + 78, 90, -1, -1, 79, -1, -1, 81, -1, -1, + 82, -1, -1, -1, 0, 0, 0, -1, 70, -1, + 86, 63, 4, 4, 87, -1, 8, 8, 8, -1, + -1, 89, 14, 14, 14, -1, 14, -1, 62, 20, + -1, 20, 82, -1, -1, -1}; + +const int _lTableObjFra[] = { + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 11, 26, 26, 26, 26, 26, 26, 8, + 26, 26, 26, 26, 26, 12, 8, 26, 20, 20, + 15, 20, 20, 20, 20, 20, 20, 20, 26, 26, + 26, 26, 26, 26, 26, 17, 17, 19, 17, 15, + 17, 19, 17, 16, 26, 17, 14, 19, 17, 19, + 17, 19, 14, 13, 15, 15, 13, 19, 15, 13, + 20, 26, 26, 26, 26, 26, 26, 17, 17, 19, + 17, 15, 17, 19, 17, 16, 26, 17, 14, 19, + 17, 19, 17, 19, 14, 13, 15, 15, 13, 19, + 15, 13, 20, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 15, 26, 26, 21, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 15, 26, 26, 21, + 26, 26, 26, 19, 26, 20, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 21, 26, 26, 26, 19, + 26, 26, 26, 26, 26, 20, 26, 26, 26, 26, + 26, 21, 26, 26, 26, 26, 17, 26, 22, 26, + 26, 26, 17, 26, 26, 26, 26, 26, 26, 17, + 26, 22, 26, 26, 19, 26, 26, 26, 26, 26, + 15, 26, 26, 26, 17, 17, 17, 26, 17, 26, + 22, 26, 15, 15, 17, 26, 16, 16, 16, 26, + 26, 17, 19, 19, 19, 26, 19, 26, 26, 15, + 26, 15, 15, 26, 26, 26}; + +const int _cTableObjDeu[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 42, 51, -1, 53, 54, 55, 50, + 47, 48, 57, 41, 36, 40, 38, 46, 26, 27, + 28, 29, 30, 31, 32, 33, 34, 35, 39, 37, + 58, 49, 59, 44, -1, 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, -1, -1, -1, 56, -1, -1, 0, 1, 2, + 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 91, -1, -1, 93, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 91, -1, -1, 93, + -1, -1, -1, 88, -1, 85, -1, -1, -1, 67, + -1, 60, -1, -1, 66, 92, -1, -1, -1, 88, + -1, -1, -1, -1, -1, 85, -1, 61, -1, -1, + -1, 92, -1, 69, -1, -1, 70, 71, 86, -1, + 72, -1, 87, 73, 75, -1, -1, 76, -1, 89, + 78, 90, -1, -1, 79, -1, -1, 81, -1, -1, + 82, -1, -1, 142, -1, -1, -1, -1, 70, -1, + 86, 63, -1, -1, 87, -1, -1, -1, -1, -1, + -1, 89, -1, 90, -1, -1, 79, -1, 62, -1, + -1, -1, 82, -1, -1, -1}; + +const int _lTableObjDeu[] = { + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 11, 26, 26, 26, 26, 26, 26, 8, + 26, 26, 26, 26, 26, 12, 8, 26, 20, 20, + 15, 20, 20, 20, 20, 20, 20, 20, 26, 26, + 26, 26, 26, 26, 26, 17, 17, 19, 17, 15, + 17, 19, 17, 16, 26, 17, 14, 19, 17, 19, + 17, 19, 14, 13, 15, 15, 13, 19, 15, 13, + 20, 26, 26, 26, 26, 26, 26, 17, 17, 19, + 17, 15, 17, 19, 17, 16, 26, 17, 14, 19, + 17, 19, 17, 19, 14, 13, 15, 15, 13, 19, + 15, 13, 20, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 15, 26, 26, 21, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 15, 26, 26, 21, + 26, 26, 26, 19, 26, 20, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 21, 26, 26, 26, 19, + 26, 26, 26, 26, 26, 20, 26, 26, 26, 26, + 26, 21, 26, 26, 26, 26, 17, 26, 22, 26, + 26, 26, 17, 26, 26, 26, 26, 26, 26, 17, + 26, 22, 26, 26, 19, 26, 26, 26, 26, 26, + 15, 26, 26, 24, 26, 26, 26, 26, 17, 26, + 22, 26, 26, 26, 17, 26, 26, 26, 26, 26, + 26, 17, 26, 22, 26, 26, 19, 26, 26, 26, + 26, 26, 15, 26, 26, 26}; + +#endif diff --git a/devtools/credits.pl b/devtools/credits.pl index 411ec3c10d..ff3b37a5da 100755 --- a/devtools/credits.pl +++ b/devtools/credits.pl @@ -48,7 +48,7 @@ if ($mode eq "") { $Text::Wrap::unexpand = 0; if ($mode eq "TEXT") { $Text::Wrap::columns = 78; - $max_name_width = 21; # The maximal width of a name. + $max_name_width = 23; # The maximal width of a name. } elsif ($mode eq "CPP") { $Text::Wrap::columns = 48; # Approx. } @@ -69,6 +69,7 @@ sub html_entities_to_ascii { # å -> aa # & -> & # ł -> l + # ś -> s # Š -> S $text =~ s/á/a/g; $text =~ s/é/e/g; @@ -76,6 +77,7 @@ sub html_entities_to_ascii { $text =~ s/ó/o/g; $text =~ s/ø/o/g; $text =~ s/ł/l/g; + $text =~ s/ś/s/g; $text =~ s/Š/S/g; $text =~ s/å/aa/g; @@ -101,6 +103,7 @@ sub html_entities_to_cpp { $text =~ s/ó/\\363/g; $text =~ s/ø/\\370/g; $text =~ s/ł/l/g; + $text =~ s/ś/s/g; $text =~ s/Š/S/g; $text =~ s/å/\\345/g; @@ -356,6 +359,9 @@ sub add_person { my $min_name_width = length $desc > 0 ? $max_name_width : 0; $name = $nick if $name eq ""; $name = html_entities_to_ascii($name); + if (length $name > $max_name_width) { + print STDERR "Warning: max_name_width is too small (" . $max_name_width . " < " . (length $name) . " for \"" . $name. "\")\n"; + } $desc = html_entities_to_ascii($desc); $tab = " " x ($section_level * 2 + 1); @@ -550,7 +556,9 @@ begin_credits("Credits"); begin_section("DreamWeb"); add_person("Torbjörn Andersson", "eriktorbjorn", ""); add_person("Bertrand Augereau", "Tramb", ""); + add_person("Filippos Karapetis", "[md5]", ""); add_person("Vladimir Menshakov", "whoozle", "(retired)"); + add_person("Willem Jan Palenstijn", "wjp", ""); end_section(); begin_section("Gob"); @@ -608,6 +616,10 @@ begin_credits("Credits"); add_person("", "peres", ""); end_section(); + begin_section("Pegasus"); + add_person("Matthew Hoops", "clone2727", ""); + end_section(); + begin_section("Queen"); add_person("David Eriksson", "twogood", "(retired)"); add_person("Gregory Montoir", "cyx", "(retired)"); @@ -675,6 +687,17 @@ begin_credits("Credits"); add_person("Joost Peters", "joostp", ""); end_section(); + begin_section("Toltecs"); + add_person("Benjamin Haisch", "john_doe", ""); + add_person("Filippos Karapetis", "[md5]", ""); + end_section(); + + begin_section("Tony"); + add_person("Arnaud Boutonné", "Strangerke", ""); + add_person("Paul Gilbert", "dreammaster", ""); + add_person("Alyssa Milburn", "fuzzie", ""); + end_section(); + begin_section("Toon"); add_person("Sylvain Dupont", "SylvainTV", ""); end_section(); @@ -692,6 +715,10 @@ begin_credits("Credits"); add_person("Gregory Montoir", "cyx", "(retired)"); end_section(); + begin_section("Wintermute"); + add_person("Einar Johan T. Sømåen", "somaen", ""); + end_section(); + end_section(); @@ -918,6 +945,9 @@ begin_credits("Credits"); begin_section("Basque"); add_person("Mikel Iturbe Urretxa", "", ""); end_section(); + begin_section("Belarusian"); + add_person("Ivan Lukyanov", "", ""); + end_section(); begin_section("Catalan"); add_person("Jordi Vilalta Prat", "jvprat", ""); end_section(); @@ -927,12 +957,18 @@ begin_credits("Credits"); begin_section("Danish"); add_person("Steffen Nyeland", "", ""); end_section(); + begin_section("Finnish"); + add_person("Toni Saarela", "catnose", ""); + end_section(); begin_section("French"); add_person("Thierry Crozat", "criezy", ""); end_section(); + begin_section("Galician"); + add_person("Santiago G. Sanz", "sgsanz", ""); + end_section(); begin_section("German"); add_person("Simon Sawatzki", "SimSaw", ""); - add_person("Lothar Serra Mari", "Lothar93", ""); + add_person("Lothar Serra Mari", "Lothar93", "(retired)"); end_section(); begin_section("Hungarian"); add_person("Alex Bevilacqua", "", ""); @@ -1059,7 +1095,7 @@ begin_credits("Credits"); # HACK! - $max_name_width = 16; + $max_name_width = 17; begin_section("Special thanks to"); begin_persons(); @@ -1072,6 +1108,7 @@ begin_credits("Credits"); add_person("Ivan Dubrov", "", "For contributing the initial version of the Gobliiins engine"); add_person("Henrik Engqvist", "qvist", "For generously providing hosting for our buildbot, SVN repository, planet and doxygen sites as well as tons of HD space"); add_person("DOSBox Team", "", "For their awesome OPL2 and OPL3 emulator"); + add_person("Yusuke Kamiyamane", "", "For contributing some GUI icons "); add_person("Till Kresslein", "Krest", "For design of modern ScummVM GUI"); add_person("", "Jezar", "For his freeverb filter implementation"); add_person("Jim Leiterman", "", "Various info on his FM-TOWNS/Marty SCUMM ports"); @@ -1082,8 +1119,6 @@ begin_credits("Credits"); add_person("James Woodcock", "", "Soundtrack enhancements"); end_persons(); - add_paragraph("Some icons by Yusuke Kamiyamane"); - add_paragraph( "Tony Warriner and everyone at Revolution Software Ltd. for sharing ". "with us the source of some of their brilliant games, allowing us to ". @@ -1127,6 +1162,22 @@ begin_credits("Credits"); "Broken Sword 2.5 team for providing sources of their engine and their great ". "support."); + add_paragraph( + "Neil Dodwell and David Dew from Creative Reality for providing the source ". + "of Dreamweb and for their tremendous support."); + + add_paragraph( + "Janusz Wiśniewski and Miroslaw Liminowicz from Laboratorium Komputerowe Avalon ". + "for providing full source code for Sołtys and letting us redistribute the game."); + + add_paragraph( + "Jan Nedoma for providing the sources to the Wintermute-engine, and for his ". + "support while porting the engine to ScummVM."); + + add_paragraph( + "Bob Bell, Michel Kripalani, Tommy Yune, from Presto Studios for ". + "providing the source code of The Journeyman Project: Pegasus Prime."); + end_section(); end_credits(); diff --git a/devtools/module.mk b/devtools/module.mk index 95eca50d18..1d682cdf05 100644 --- a/devtools/module.mk +++ b/devtools/module.mk @@ -56,7 +56,7 @@ credits: $(srcdir)/devtools/credits.pl --text > $(srcdir)/AUTHORS # $(srcdir)/devtools/credits.pl --rtf > $(srcdir)/Credits.rtf $(srcdir)/devtools/credits.pl --cpp > $(srcdir)/gui/credits.h - $(srcdir)/devtools/credits.pl --xml-website > $(srcdir)/../../web/trunk/data/credits.xml + $(srcdir)/devtools/credits.pl --xml-website > $(srcdir)/../scummvm-web/data/credits.xml # $(srcdir)/devtools/credits.pl --xml-docbook > $(srcdir)/../../docs/trunk/docbook/credits.xml md5scumm: devtools/md5table$(EXEEXT) diff --git a/dists/bada/Res/scummmobile/scummmobile_layout.stx b/dists/bada/Res/scummmobile/scummmobile_layout.stx index efb8ebc6b5..5da293a131 100644 --- a/dists/bada/Res/scummmobile/scummmobile_layout.stx +++ b/dists/bada/Res/scummmobile/scummmobile_layout.stx @@ -371,6 +371,10 @@ height = 'Globals.Line.Height' /> </layout> + <widget name = 'mcFluidSynthSettings' + width = '200' + height = 'Globals.Button.Height' + /> </layout> </dialog> @@ -786,6 +790,157 @@ </layout> </dialog> + <dialog name = 'FluidSynthSettings' overlays = 'GlobalOptions' shading = 'dim'> + <layout type = 'vertical' padding = '0, 0, 0, 0'> + <widget name = 'TabWidget'/> + <layout type = 'horizontal' padding = '16, 16, 16, 16'> + <space/> + <widget name = 'ResetSettings' + type = 'Button' + /> + <widget name = 'Cancel' + type = 'Button' + /> + <widget name = 'Ok' + type = 'Button' + /> + </layout> + </layout> + </dialog> + + <dialog name = 'FluidSynthSettings_Chorus' overlays = 'Dialog.FluidSynthSettings.TabWidget'> + <layout type = 'vertical' padding = '16, 16, 16, 16' spacing = '8'> + <widget name = 'EnableTabCheckbox' + type = 'Checkbox' + /> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'VoiceCountText' + type = 'OptionsLabel' + /> + <widget name = 'VoiceCountSlider' + type = 'Slider' + /> + <widget name = 'VoiceCountLabel' + width = '32' + height = 'Globals.Line.Height' + /> + </layout> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'LevelText' + type = 'OptionsLabel' + /> + <widget name = 'LevelSlider' + type = 'Slider' + /> + <widget name = 'LevelLabel' + width = '32' + height = 'Globals.Line.Height' + /> + </layout> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'SpeedText' + type = 'OptionsLabel' + /> + <widget name = 'SpeedSlider' + type = 'Slider' + /> + <widget name = 'SpeedLabel' + width = '32' + height = 'Globals.Line.Height' + /> + </layout> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'DepthText' + type = 'OptionsLabel' + /> + <widget name = 'DepthSlider' + type = 'Slider' + /> + <widget name = 'DepthLabel' + width = '32' + height = 'Globals.Line.Height' + /> + </layout> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'WaveFormTypeText' + type = 'OptionsLabel' + /> + <widget name = 'WaveFormType' + type = 'PopUp' + /> + </layout> + </layout> + </dialog> + + <dialog name = 'FluidSynthSettings_Reverb' overlays = 'Dialog.FluidSynthSettings.TabWidget'> + <layout type = 'vertical' padding = '16, 16, 16, 16' spacing = '8'> + <widget name = 'EnableTabCheckbox' + type = 'Checkbox' + /> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'RoomSizeText' + type = 'OptionsLabel' + /> + <widget name = 'RoomSizeSlider' + type = 'Slider' + /> + <widget name = 'RoomSizeLabel' + width = '32' + height = 'Globals.Line.Height' + /> + </layout> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'DampingText' + type = 'OptionsLabel' + /> + <widget name = 'DampingSlider' + type = 'Slider' + /> + <widget name = 'DampingLabel' + width = '32' + height = 'Globals.Line.Height' + /> + </layout> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'WidthText' + type = 'OptionsLabel' + /> + <widget name = 'WidthSlider' + type = 'Slider' + /> + <widget name = 'WidthLabel' + width = '32' + height = 'Globals.Line.Height' + /> + </layout> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'LevelText' + type = 'OptionsLabel' + /> + <widget name = 'LevelSlider' + type = 'Slider' + /> + <widget name = 'LevelLabel' + width = '32' + height = 'Globals.Line.Height' + /> + </layout> + </layout> + </dialog> + + <dialog name = 'FluidSynthSettings_Misc' overlays = 'Dialog.FluidSynthSettings.TabWidget'> + <layout type = 'vertical' padding = '16, 16, 16, 16' spacing = '8'> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'InterpolationText' + type = 'OptionsLabel' + /> + <widget name = 'Interpolation' + type = 'PopUp' + /> + </layout> + </layout> + </dialog> + <dialog name = 'SaveLoadChooser' overlays = 'screen' inset = '8' shading = 'dim'> <layout type = 'vertical' padding = '8, 8, 8, 32' center = 'true'> <widget name = 'Title' diff --git a/dists/debian/copyright b/dists/debian/copyright index 318c06f62b..1cccd01a07 100644 --- a/dists/debian/copyright +++ b/dists/debian/copyright @@ -7,7 +7,7 @@ It was downloaded from <http://www.scummvm.org/>. Upstream Authors: see `/usr/share/doc/scummvm/AUTHORS'. -Scummvm is Copyright © 2002-2012 The ScummVM Project +Scummvm is Copyright © 2002-2013 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 diff --git a/dists/engine-data/README b/dists/engine-data/README index ce74d590d9..c9c4bd4817 100644 --- a/dists/engine-data/README +++ b/dists/engine-data/README @@ -1,6 +1,9 @@ engine-data README ------------------------------------------------------------------------------- +drascula.dat +TODO + hugo.dat: This file contains all the hardcoded logic, strings and fonts used by Hugo engine. Those information were stored in the original executables. @@ -10,6 +13,9 @@ The 'kyra.dat' file is created by extracting hardcoded data, like the roomtable, inventory names, various strings, tables for shapes and sequence scripts, from. +lure.dat +TODO + queen.tbl: 'queen.tbl' contains a list of filenames, filesizes and offsets for the individual files saved in QUEEN.1. This data was originally included in the @@ -20,5 +26,11 @@ mp3/ogg/flac encoded need the datafile. sky.cpt: TODO +teenagent.dat +TODO + +tony.dat: +This file contains the font table used by the different versions of the game. + toon.dat: 'toon.dat' contains all the strings hardcoded in the original executables. diff --git a/dists/engine-data/kyra.dat b/dists/engine-data/kyra.dat Binary files differindex c89b21cbca..305c7e665c 100644 --- a/dists/engine-data/kyra.dat +++ b/dists/engine-data/kyra.dat diff --git a/dists/engine-data/teenagent.dat b/dists/engine-data/teenagent.dat Binary files differindex 0dd31dad14..1492326920 100644 --- a/dists/engine-data/teenagent.dat +++ b/dists/engine-data/teenagent.dat diff --git a/dists/engine-data/tony.dat b/dists/engine-data/tony.dat Binary files differnew file mode 100644 index 0000000000..5ea53c6752 --- /dev/null +++ b/dists/engine-data/tony.dat diff --git a/dists/macosx/Info.plist b/dists/macosx/Info.plist index d8c28f6a08..43d7c37bc5 100644 --- a/dists/macosx/Info.plist +++ b/dists/macosx/Info.plist @@ -28,7 +28,7 @@ <key>CFBundleExecutable</key> <string>scummvm</string> <key>CFBundleGetInfoString</key> - <string>1.6.0git, Copyright 2001-2012 The ScummVM team</string> + <string>1.6.0git, Copyright 2001-2013 The ScummVM team</string> <key>CFBundleIconFile</key> <string>scummvm.icns</string> <key>CFBundleIdentifier</key> @@ -46,7 +46,7 @@ <key>NSPrincipalClass</key> <string>NSApplication</string> <key>NSHumanReadableCopyright</key> - <string>Copyright 2001-2012 The ScummVM team</string> + <string>Copyright 2001-2013 The ScummVM team</string> <key>SUFeedURL</key> <string>http://www.scummvm.org/appcasts/macosx/release.xml</string> <key>SUPublicDSAKeyFile</key> diff --git a/dists/macosx/Info.plist.in b/dists/macosx/Info.plist.in index ff010bee07..b810b7ea5a 100644 --- a/dists/macosx/Info.plist.in +++ b/dists/macosx/Info.plist.in @@ -28,7 +28,7 @@ <key>CFBundleExecutable</key> <string>scummvm</string> <key>CFBundleGetInfoString</key> - <string>@VERSION@, Copyright 2001-2012 The ScummVM team</string> + <string>@VERSION@, Copyright 2001-2013 The ScummVM team</string> <key>CFBundleIconFile</key> <string>scummvm.icns</string> <key>CFBundleIdentifier</key> @@ -46,7 +46,7 @@ <key>NSPrincipalClass</key> <string>NSApplication</string> <key>NSHumanReadableCopyright</key> - <string>Copyright 2001-2012 The ScummVM team</string> + <string>Copyright 2001-2013 The ScummVM team</string> <key>SUFeedURL</key> <string>http://www.scummvm.org/appcasts/macosx/release.xml</string> <key>SUPublicDSAKeyFile</key> diff --git a/dists/msvc11/create_msvc11.bat b/dists/msvc11/create_msvc11.bat new file mode 100644 index 0000000000..b6a5413e3b --- /dev/null +++ b/dists/msvc11/create_msvc11.bat @@ -0,0 +1,95 @@ +@echo off + +echo. +echo Automatic creation of the MSVC11 project files +echo. + +if "%~1"=="/stable" goto stable +if "%~1"=="/STABLE" goto stable +if "%~1"=="/all" goto all +if "%~1"=="/ALL" goto all +if "%~1"=="/tools" goto tools +if "%~1"=="/TOOLS" goto tools +if "%~1"=="/clean" goto clean_check +if "%~1"=="/CLEAN" goto clean_check +if "%~1"=="/help" goto command_help +if "%~1"=="/HELP" goto command_help +if "%~1"=="/?" goto command_help + +if "%~1"=="" goto check_tool + +echo Invalid command parameter: %~1 +echo. + +:command_help +echo Valid command parameters are: +echo stable Generated stable engines project files +echo all Generate all engines project files +echo tools Generate project files for the devtools +echo clean Clean generated project files +echo help Show help message +goto done + +:check_tool +if not exist create_project.exe goto no_tool + +:question +echo. +set batchanswer=S +set /p batchanswer="Enable stable engines only, or all engines? (S/a)" +if "%batchanswer%"=="s" goto stable +if "%batchanswer%"=="S" goto stable +if "%batchanswer%"=="a" goto all +if "%batchanswer%"=="A" goto all +goto question + +:no_tool +echo create_project.exe not found in the current folder. +echo You need to build it first and copy it in this +echo folder +goto done + +:all +echo. +echo Creating project files with all engines enabled (stable and unstable) +echo. +create_project ..\.. --enable-all-engines --msvc --msvc-version 11 --build-events +goto done + +:stable +echo. +echo Creating normal project files, with only the stable engines enabled +echo. +create_project ..\.. --msvc --msvc-version 11 +goto done + +:tools +echo. +echo Creating tools project files +echo. +create_project ..\.. --tools --msvc --msvc-version 11 +goto done + +:clean_check +echo. +set cleananswer=N +set /p cleananswer="This will remove all project files. Are you sure you want to continue? (N/y)" +if "%cleananswer%"=="n" goto done +if "%cleananswer%"=="N" goto done +if "%cleananswer%"=="y" goto clean +if "%cleananswer%"=="Y" goto clean +goto clean_check + +:clean +echo. +echo Removing all project files +del /Q *.vcxproj* > NUL 2>&1 +del /Q *.props > NUL 2>&1 +del /Q *.sln* > NUL 2>&1 +del /Q scummvm* > NUL 2>&1 +del /Q devtools* > NUL 2>&1 +goto done + +:done +echo. +pause diff --git a/dists/msvc11/readme.txt b/dists/msvc11/readme.txt new file mode 100644 index 0000000000..fa91a8cc12 --- /dev/null +++ b/dists/msvc11/readme.txt @@ -0,0 +1,6 @@ +The Visual Studio project files can now be created automatically from the GCC +files using the create_project tool inside the /devtools/create_project folder. + +To create the default project files, build create_project.exe, copy it inside +this folder and run the create_msvc11.bat file for a default build. You can run +create_project.exe with no parameters to check the possible command-line options diff --git a/dists/redhat/scummvm.spec b/dists/redhat/scummvm.spec index d80404a3c6..bd17017fbf 100644 --- a/dists/redhat/scummvm.spec +++ b/dists/redhat/scummvm.spec @@ -27,6 +27,7 @@ BuildRequires: flac-devel BuildRequires: zlib-devel BuildRequires: nasm BuildRequires: SDL-devel >= 1.2.2 +BuildRequires: freetype-devel #------------------------------------------------------------------------------ # Description @@ -94,7 +95,7 @@ fi #------------------------------------------------------------------------------ %files %defattr(0644,root,root,0755) -%doc AUTHORS README NEWS COPYING COPYING.LGPL COPYING.BSD COPYRIGHT +%doc AUTHORS README NEWS COPYING COPYING.LGPL COPYING.FREEFONT COPYING.BSD COPYRIGHT %attr(0755,root,root)%{_bindir}/scummvm %{_datadir}/applications/* %{_datadir}/pixmaps/scummvm.xpm diff --git a/dists/redhat/scummvm.spec.in b/dists/redhat/scummvm.spec.in index 3beef2f960..9dbd8add5b 100644 --- a/dists/redhat/scummvm.spec.in +++ b/dists/redhat/scummvm.spec.in @@ -27,6 +27,7 @@ BuildRequires: flac-devel BuildRequires: zlib-devel BuildRequires: nasm BuildRequires: SDL-devel >= 1.2.2 +BuildRequires: freetype-devel #------------------------------------------------------------------------------ # Description @@ -94,7 +95,7 @@ fi #------------------------------------------------------------------------------ %files %defattr(0644,root,root,0755) -%doc AUTHORS README NEWS COPYING COPYING.LGPL COPYING.BSD COPYRIGHT +%doc AUTHORS README NEWS COPYING COPYING.LGPL COPYING.FREEFONT COPYING.BSD COPYRIGHT %attr(0755,root,root)%{_bindir}/scummvm %{_datadir}/applications/* %{_datadir}/pixmaps/scummvm.xpm diff --git a/dists/scummvm.rc b/dists/scummvm.rc index 4a67100f9f..45924f724d 100644 --- a/dists/scummvm.rc +++ b/dists/scummvm.rc @@ -44,6 +44,9 @@ teenagent.dat FILE "dists/engine-data/teenagent.dat" #if ENABLE_TOON == STATIC_PLUGIN toon.dat FILE "dists/engine-data/toon.dat" #endif +#if ENABLE_TONY == STATIC_PLUGIN +tony.dat FILE "dists/engine-data/tony.dat" +#endif #if ENABLE_AGI == STATIC_PLUGIN pred.dic FILE "dists/pred.dic" #endif @@ -69,7 +72,7 @@ BEGIN VALUE "FileDescription", "http://www.scummvm.org/\0" VALUE "FileVersion", "1.6.0git\0" VALUE "InternalName", "scummvm\0" - VALUE "LegalCopyright", "Copyright © 2001-2012 The ScummVM Team\0" + VALUE "LegalCopyright", "Copyright © 2001-2013 The ScummVM Team\0" VALUE "LegalTrademarks", "'SCUMM', and all SCUMM games are a TM of LucasArts. Simon The Sorcerer is a TM of AdventureSoft. Beneath a Steel Sky and Broken Sword are a TM of Revolution. Flight of the Amazon Queen is a TM of John Passfield and Steve Stamatiadis. \0" VALUE "OriginalFilename", "scummvm.exe\0" VALUE "ProductName", "ScummVM\0" diff --git a/dists/scummvm.rc.in b/dists/scummvm.rc.in index a874b98514..3be699f84b 100644 --- a/dists/scummvm.rc.in +++ b/dists/scummvm.rc.in @@ -69,7 +69,7 @@ BEGIN VALUE "FileDescription", "http://www.scummvm.org/\0" VALUE "FileVersion", "@VERSION@\0" VALUE "InternalName", "scummvm\0" - VALUE "LegalCopyright", "Copyright © 2001-2012 The ScummVM Team\0" + VALUE "LegalCopyright", "Copyright © 2001-2013 The ScummVM Team\0" VALUE "LegalTrademarks", "'SCUMM', and all SCUMM games are a TM of LucasArts. Simon The Sorcerer is a TM of AdventureSoft. Beneath a Steel Sky and Broken Sword are a TM of Revolution. Flight of the Amazon Queen is a TM of John Passfield and Steve Stamatiadis. \0" VALUE "OriginalFilename", "scummvm.exe\0" VALUE "ProductName", "ScummVM\0" diff --git a/dists/win32/ScummVM.iss b/dists/win32/ScummVM.iss index c07b0ae64f..fa0f760f59 100644 --- a/dists/win32/ScummVM.iss +++ b/dists/win32/ScummVM.iss @@ -1,5 +1,5 @@ [Setup] -AppCopyright=2012 +AppCopyright=2013 AppName=ScummVM AppVerName=ScummVM Git AppPublisher=The ScummVM Team @@ -108,18 +108,6 @@ Source: doc/se/LasMig.txt; DestDir: {app}; Flags: ignoreversion isreadme; Langua Source: README-SDL.txt; DestDir: {app}; Flags: ignoreversion Source: scummvm.exe; DestDir: {app}; Flags: ignoreversion Source: SDL.dll; DestDir: {app} -Source: scummclassic.zip; DestDir: {app}; Flags: ignoreversion -Source: scummmodern.zip; DestDir: {app}; Flags: ignoreversion -Source: drascula.dat; DestDir: {app}; Flags: ignoreversion -Source: hugo.dat; DestDir: {app}; Flags: ignoreversion -Source: kyra.dat; DestDir: {app}; Flags: ignoreversion -Source: lure.dat; DestDir: {app}; Flags: ignoreversion -Source: pred.dic; DestDir: {app}; Flags: ignoreversion -Source: queen.tbl; DestDir: {app}; Flags: ignoreversion -Source: sky.cpt; DestDir: {app}; Flags: ignoreversion -Source: teenagent.dat; DestDir: {app}; Flags: ignoreversion -Source: toon.dat; DestDir: {app}; Flags: ignoreversion -Source: translations.dat; DestDir: {app}; Flags: ignoreversion ;Mirgration script for saved games in Windows NT4 onwards Source: migration.bat; DestDir: {app}; Flags: ignoreversion; MinVersion: 0, 1 Source: migration.txt; DestDir: {app}; Flags: ignoreversion; MinVersion: 0, 1 diff --git a/dists/win32/migration.bat b/dists/win32/migration.bat index 2bba7baef4..b4b00c02cc 100644 --- a/dists/win32/migration.bat +++ b/dists/win32/migration.bat @@ -4,7 +4,7 @@ :: This script will copy any saved games located in the :: old default location, to the new default location. :: -:: (c) 2012 ScummVM Team +:: (c) 2012-2013 ScummVM Team :: @echo off diff --git a/dists/win32/scummvm.nsi b/dists/win32/scummvm.nsi index 795eb660b6..fb4787a172 100644 --- a/dists/win32/scummvm.nsi +++ b/dists/win32/scummvm.nsi @@ -76,7 +76,7 @@ Name ScummVM !define COMPANY "ScummVM Team" !define URL "http://scummvm.org/" !define DESCRIPTION "ScummVM Installer. Look! A three headed monkey (TM)!" -!define COPYRIGHT "Copyright © 2001-2012 The ScummVM Team" +!define COPYRIGHT "Copyright © 2001-2013 The ScummVM Team" ######################################################################################### # Installer configuration diff --git a/dists/win32/scummvm.nsi.in b/dists/win32/scummvm.nsi.in index 340024e6a1..9f8bd1dad2 100644 --- a/dists/win32/scummvm.nsi.in +++ b/dists/win32/scummvm.nsi.in @@ -76,7 +76,7 @@ Name ScummVM !define COMPANY "ScummVM Team" !define URL "http://scummvm.org/" !define DESCRIPTION "ScummVM Installer. Look! A three headed monkey (TM)!" -!define COPYRIGHT "Copyright © 2001-2012 The ScummVM Team" +!define COPYRIGHT "Copyright © 2001-2013 The ScummVM Team" ######################################################################################### # Installer configuration diff --git a/doc/de/Liesmich b/doc/de/Liesmich index e8ee4ca3f8..88b6ce6de8 100644 --- a/doc/de/Liesmich +++ b/doc/de/Liesmich @@ -2241,7 +2241,7 @@ folgende nicht standardmäßige Schlüsselwörter: erweiterten von ScummVM verwendet. native_fb01 Bool Falls „true“, wird für die MIDI-Ausgabe der Musiktreiber für eine Music-Feature-Karte - von IBM oder für ein Yahama-FB-01-FM- + von IBM oder für ein Yamaha-FB-01-FM- Synthetisierungsmodul verwendet. Baphomets Fluch II verfügt zusätzlich über folgende nicht standardmäßige diff --git a/doc/de/Neues b/doc/de/Neues index 7c6699fcb0..74203148e7 100644 --- a/doc/de/Neues +++ b/doc/de/Neues @@ -2,7 +2,7 @@ Umfangreichere Änderungsaufzeichnungen des neusten experimentellen Codes finden Sie auf Englisch unter: https://github.com/scummvm/scummvm/commits/ -1.5.0 (??.??.????) +1.5.0 (27.07.2012) Neue Spiele: - Unterstützung für Backyard Baseball 2003 hinzugefügt. - Unterstützung für Blue Force hinzugefügt. @@ -10,10 +10,7 @@ Sie auf Englisch unter: - Unterstützung für Dreamweb hinzugefügt. - Unterstützung für Geisha hinzugefügt. - Unterstützung für Gregor und der Heißluftballon hinzugefügt. - - Unterstützung für Magic Tales: Baba Yaga and the Magic Geese hinzugefügt. - - Unterstützung für Magic Tales: Imo and the King hinzugefügt. - Unterstützung für Magic Tales: Liam Finds a Story hinzugefügt. - - Unterstützung für Magic Tales: The Little Samurai hinzugefügt. - Unterstützung für Once Upon A Time: Little Red Riding Hood hinzugefügt. - Unterstützung für Sleeping Cub's Test of Courage hinzugefügt. - Unterstützung für Soltys hinzugefügt. @@ -36,12 +33,10 @@ Allgemein: - Aussehen von vorhersagendem Eingabedialog verbessert. - Verschiedene Verbesserungen der grafischen Benutzeroberfläche. - SDL-Portierungen: - - Unterstützung für OpenGL hinzugefügt. (GSoC-Aufgabe) - Baphomets Fluch 1: - Falsche Soundeffekte in der DOS-/Windows-Demo korrigiert. - Unterstützung für PlayStation-Videos hinzugefügt. + - Fehlende Untertitel zur Demo hinzugefügt. Baphomets Fluch 2: - Unterstützung für PlayStation-Videos hinzugefügt. @@ -49,6 +44,10 @@ Allgemein: Cine: - Roland-MT-32-Ausgabetreiber integriert. + Drascula: + - Spanische Untertitel zur Zwischensequenz mit Von Braun + hinzugefügt (3069981: Keine Untertitel in Szene mit „Von Braun“). + Gob: - Absturz in Lost in Time beseitigt. - AdLib-Abspieler umgeschrieben. Den nun funktionierenden MDY-Abspieler in @@ -69,6 +68,10 @@ Allgemein: - Unterstützung für Seitenverhältniskorrektur hinzugefügt. - Unterstützung für 16 Bits pro Pixel bei Spielen integriert. + Maemo-Portierung: + - Unterstützung für Nokia 770 mit dem Betriebssystem OS2008 HE hinzugefügt. + - Konfigurierbare Tastenzuweisung hinzugefügt. + Windows-Portierung: - Standard-Verzeichnis für Spielstände bei Windows NT4/2000/XP/Vista/7 geändert. diff --git a/engines/advancedDetector.cpp b/engines/advancedDetector.cpp index ac06e74e0a..9beba6ce4a 100644 --- a/engines/advancedDetector.cpp +++ b/engines/advancedDetector.cpp @@ -22,7 +22,6 @@ #include "common/debug.h" #include "common/util.h" -#include "common/hash-str.h" #include "common/file.h" #include "common/macresman.h" #include "common/md5.h" @@ -308,14 +307,7 @@ Common::Error AdvancedMetaEngine::createInstance(OSystem *syst, Engine **engine) return Common::kNoError; } -struct SizeMD5 { - int size; - Common::String md5; -}; - -typedef Common::HashMap<Common::String, SizeMD5, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> SizeMD5Map; - -static void reportUnknown(const Common::FSNode &path, const SizeMD5Map &filesSizeMD5) { +void AdvancedMetaEngine::reportUnknown(const Common::FSNode &path, const ADFilePropertiesMap &filesProps) const { // TODO: This message should be cleaned up / made more specific. // For example, we should specify at least which engine triggered this. // @@ -327,7 +319,7 @@ static void reportUnknown(const Common::FSNode &path, const SizeMD5Map &filesSiz report += _("of the game you tried to add and its version/language/etc.:"); report += "\n"; - for (SizeMD5Map::const_iterator file = filesSizeMD5.begin(); file != filesSizeMD5.end(); ++file) + for (ADFilePropertiesMap::const_iterator file = filesProps.begin(); file != filesProps.end(); ++file) report += Common::String::format(" {\"%s\", 0, \"%s\", %d},\n", file->_key.c_str(), file->_value.md5.c_str(), file->_value.size); report += "\n"; @@ -375,8 +367,36 @@ void AdvancedMetaEngine::composeFileHashMap(FileMap &allFiles, const Common::FSL } } +bool AdvancedMetaEngine::getFileProperties(const Common::FSNode &parent, const FileMap &allFiles, const ADGameDescription &game, const Common::String fname, ADFileProperties &fileProps) const { + // FIXME/TODO: We don't handle the case that a file is listed as a regular + // file and as one with resource fork. + + if (game.flags & ADGF_MACRESFORK) { + Common::MacResManager macResMan; + + if (!macResMan.open(parent, fname)) + return false; + + fileProps.md5 = macResMan.computeResForkMD5AsString(_md5Bytes); + fileProps.size = macResMan.getResForkDataSize(); + return true; + } + + if (!allFiles.contains(fname)) + return false; + + Common::File testFile; + + if (!testFile.open(allFiles[fname])) + return false; + + fileProps.size = (int32)testFile.size(); + fileProps.md5 = Common::computeStreamMD5AsString(testFile, _md5Bytes); + return true; +} + ADGameDescList AdvancedMetaEngine::detectGame(const Common::FSNode &parent, const FileMap &allFiles, Common::Language language, Common::Platform platform, const Common::String &extra) const { - SizeMD5Map filesSizeMD5; + ADFilePropertiesMap filesProps; const ADGameFileDescription *fileDesc; const ADGameDescription *g; @@ -391,39 +411,14 @@ ADGameDescList AdvancedMetaEngine::detectGame(const Common::FSNode &parent, cons for (fileDesc = g->filesDescriptions; fileDesc->fileName; fileDesc++) { Common::String fname = fileDesc->fileName; - SizeMD5 tmp; + ADFileProperties tmp; - if (filesSizeMD5.contains(fname)) + if (filesProps.contains(fname)) continue; - // FIXME/TODO: We don't handle the case that a file is listed as a regular - // file and as one with resource fork. - - if (g->flags & ADGF_MACRESFORK) { - Common::MacResManager macResMan; - - if (macResMan.open(parent, fname)) { - tmp.md5 = macResMan.computeResForkMD5AsString(_md5Bytes); - tmp.size = macResMan.getResForkDataSize(); - debug(3, "> '%s': '%s'", fname.c_str(), tmp.md5.c_str()); - filesSizeMD5[fname] = tmp; - } - } else { - if (allFiles.contains(fname)) { - debug(3, "+ %s", fname.c_str()); - - Common::File testFile; - - if (testFile.open(allFiles[fname])) { - tmp.size = (int32)testFile.size(); - tmp.md5 = Common::computeStreamMD5AsString(testFile, _md5Bytes); - } else { - tmp.size = -1; - } - - debug(3, "> '%s': '%s'", fname.c_str(), tmp.md5.c_str()); - filesSizeMD5[fname] = tmp; - } + if (getFileProperties(parent, allFiles, *g, fname, tmp)) { + debug(3, "> '%s': '%s'", fname.c_str(), tmp.md5.c_str()); + filesProps[fname] = tmp; } } } @@ -456,19 +451,19 @@ ADGameDescList AdvancedMetaEngine::detectGame(const Common::FSNode &parent, cons for (fileDesc = g->filesDescriptions; fileDesc->fileName; fileDesc++) { Common::String tstr = fileDesc->fileName; - if (!filesSizeMD5.contains(tstr)) { + if (!filesProps.contains(tstr)) { fileMissing = true; allFilesPresent = false; break; } - if (fileDesc->md5 != NULL && fileDesc->md5 != filesSizeMD5[tstr].md5) { - debug(3, "MD5 Mismatch. Skipping (%s) (%s)", fileDesc->md5, filesSizeMD5[tstr].md5.c_str()); + if (fileDesc->md5 != NULL && fileDesc->md5 != filesProps[tstr].md5) { + debug(3, "MD5 Mismatch. Skipping (%s) (%s)", fileDesc->md5, filesProps[tstr].md5.c_str()); fileMissing = true; break; } - if (fileDesc->fileSize != -1 && fileDesc->fileSize != filesSizeMD5[tstr].size) { + if (fileDesc->fileSize != -1 && fileDesc->fileSize != filesProps[tstr].size) { debug(3, "Size Mismatch. Skipping"); fileMissing = true; break; @@ -514,8 +509,8 @@ ADGameDescList AdvancedMetaEngine::detectGame(const Common::FSNode &parent, cons // We didn't find a match if (matched.empty()) { - if (!filesSizeMD5.empty() && gotAnyMatchesWithAllFiles) { - reportUnknown(parent, filesSizeMD5); + if (!filesProps.empty() && gotAnyMatchesWithAllFiles) { + reportUnknown(parent, filesProps); } // Filename based fallback @@ -524,7 +519,7 @@ ADGameDescList AdvancedMetaEngine::detectGame(const Common::FSNode &parent, cons return matched; } -const ADGameDescription *AdvancedMetaEngine::detectGameFilebased(const FileMap &allFiles, const ADFileBasedFallback *fileBasedFallback) const { +const ADGameDescription *AdvancedMetaEngine::detectGameFilebased(const FileMap &allFiles, const Common::FSList &fslist, const ADFileBasedFallback *fileBasedFallback, ADFilePropertiesMap *filesProps) const { const ADFileBasedFallback *ptr; const char* const* filenames; @@ -554,6 +549,16 @@ const ADGameDescription *AdvancedMetaEngine::detectGameFilebased(const FileMap & maxNumMatchedFiles = numMatchedFiles; debug(4, "and overridden"); + + if (filesProps) { + for (filenames = ptr->filenames; *filenames; ++filenames) { + ADFileProperties tmp; + + if (getFileProperties(fslist.begin()->getParent(), allFiles, *agdesc, *filenames, tmp)) + (*filesProps)[*filenames] = tmp; + } + } + } } } diff --git a/engines/advancedDetector.h b/engines/advancedDetector.h index 45a9f183e8..3eec33abe5 100644 --- a/engines/advancedDetector.h +++ b/engines/advancedDetector.h @@ -26,6 +26,8 @@ #include "engines/metaengine.h" #include "engines/engine.h" +#include "common/hash-str.h" + #include "common/gui_options.h" // FIXME: Temporary hack? namespace Common { @@ -46,6 +48,20 @@ struct ADGameFileDescription { }; /** + * A record describing the properties of a file. Used on the existing + * files while detecting a game. + */ +struct ADFileProperties { + int32 size; + Common::String md5; +}; + +/** + * A map of all relevant existing files in a game directory while detecting. + */ +typedef Common::HashMap<Common::String, ADFileProperties, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> ADFilePropertiesMap; + +/** * A shortcut to produce an empty ADGameFileDescription record. Used to mark * the end of a list of these. */ @@ -178,7 +194,7 @@ protected: /** * A map containing all the extra game GUI options the engine supports. - */ + */ const ADExtraGuiOptionsMap * const _extraGuiOptions; /** @@ -196,7 +212,7 @@ protected: * * Used to override gameid. * This is a recommended setting to prevent global gameid pollution. - * With this option set, the gameid effectively turns into engineid. + * With this option set, the gameid effectively turns into engineid. * * FIXME: This field actually removes a feature (gameid) in order to * address a more generic problem. We should find a better way to @@ -286,9 +302,17 @@ protected: * In case of a tie, the entry coming first in the list is chosen. * * @param allFiles a map describing all present files + * @param fslist a list of nodes for all present files * @param fileBasedFallback a list of ADFileBasedFallback records, zero-terminated + * @param filesProps if not 0, return a map of properties for all detected files here + */ + const ADGameDescription *detectGameFilebased(const FileMap &allFiles, const Common::FSList &fslist, const ADFileBasedFallback *fileBasedFallback, ADFilePropertiesMap *filesProps = 0) const; + + /** + * Log and print a report that we found an unknown game variant, together with the file + * names, sizes and MD5 sums. */ - const ADGameDescription *detectGameFilebased(const FileMap &allFiles, const ADFileBasedFallback *fileBasedFallback) const; + void reportUnknown(const Common::FSNode &path, const ADFilePropertiesMap &filesProps) const; // TODO void updateGameDescriptor(GameDescriptor &desc, const ADGameDescription *realDesc) const; @@ -298,6 +322,9 @@ protected: * Includes nifty stuff like removing trailing dots and ignoring case. */ void composeFileHashMap(FileMap &allFiles, const Common::FSList &fslist, int depth) const; + + /** Get the properties (size and MD5) of this file. */ + bool getFileProperties(const Common::FSNode &parent, const FileMap &allFiles, const ADGameDescription &game, const Common::String fname, ADFileProperties &fileProps) const; }; #endif diff --git a/engines/agi/agi.cpp b/engines/agi/agi.cpp index 45c00a76ac..98ffca22ed 100644 --- a/engines/agi/agi.cpp +++ b/engines/agi/agi.cpp @@ -219,7 +219,7 @@ void AgiEngine::processEvents() { case Common::KEYCODE_F6: key = 0x4000; break; - case Common::KEYCODE_F7: + case Common::KEYCODE_F7: key = 0x4100; break; case Common::KEYCODE_F8: diff --git a/engines/agi/console.cpp b/engines/agi/console.cpp index dd06736290..b9a64bc572 100644 --- a/engines/agi/console.cpp +++ b/engines/agi/console.cpp @@ -35,7 +35,6 @@ Console::Console(AgiEngine *vm) : GUI::Debugger() { DCmd_Register("debug", WRAP_METHOD(Console, Cmd_Debug)); DCmd_Register("cont", WRAP_METHOD(Console, Cmd_Cont)); DCmd_Register("agiver", WRAP_METHOD(Console, Cmd_Agiver)); - DCmd_Register("crc", WRAP_METHOD(Console, Cmd_Crc)); DCmd_Register("flags", WRAP_METHOD(Console, Cmd_Flags)); DCmd_Register("logic0", WRAP_METHOD(Console, Cmd_Logic0)); DCmd_Register("objs", WRAP_METHOD(Console, Cmd_Objs)); @@ -119,12 +118,6 @@ bool Console::Cmd_RunOpcode(int argc, const char **argv) { return true; } -bool Console::Cmd_Crc(int argc, const char **argv) { - DebugPrintf("command removed from scummvm\n"); - - return true; -} - bool Console::Cmd_Agiver(int argc, const char **argv) { int ver, maj, min; @@ -278,22 +271,17 @@ bool Console::Cmd_BT(int argc, const char **argv) { MickeyConsole::MickeyConsole(MickeyEngine *mickey) : GUI::Debugger() { _mickey = mickey; - DCmd_Register("curRoom", WRAP_METHOD(MickeyConsole, Cmd_CurRoom)); - DCmd_Register("gotoRoom", WRAP_METHOD(MickeyConsole, Cmd_GotoRoom)); + DCmd_Register("room", WRAP_METHOD(MickeyConsole, Cmd_Room)); DCmd_Register("drawPic", WRAP_METHOD(MickeyConsole, Cmd_DrawPic)); DCmd_Register("drawObj", WRAP_METHOD(MickeyConsole, Cmd_DrawObj)); } -bool MickeyConsole::Cmd_CurRoom(int argc, const char **argv) { +bool MickeyConsole::Cmd_Room(int argc, const char **argv) { + if (argc == 2) + _mickey->debugGotoRoom(atoi(argv[1])); + _mickey->debugCurRoom(); - return true; -} -bool MickeyConsole::Cmd_GotoRoom(int argc, const char **argv) { - if (argc != 2) - DebugPrintf("Usage: %s <Room number>\n", argv[0]); - else - _mickey->debugGotoRoom(atoi(argv[1])); return true; } diff --git a/engines/agi/console.h b/engines/agi/console.h index 5f69460907..f8025e0562 100644 --- a/engines/agi/console.h +++ b/engines/agi/console.h @@ -49,7 +49,6 @@ private: bool Cmd_SetFlag(int argc, const char **argv); bool Cmd_SetObj(int argc, const char **argv); bool Cmd_RunOpcode(int argc, const char **argv); - bool Cmd_Crc(int argc, const char **argv); bool Cmd_Agiver(int argc, const char **argv); bool Cmd_Flags(int argc, const char **argv); bool Cmd_Vars(int argc, const char **argv); @@ -75,8 +74,7 @@ public: private: MickeyEngine *_mickey; - bool Cmd_CurRoom(int argc, const char **argv); - bool Cmd_GotoRoom(int argc, const char **argv); + bool Cmd_Room(int argc, const char **argv); bool Cmd_DrawPic(int argc, const char **argv); bool Cmd_DrawObj(int argc, const char **argv); }; diff --git a/engines/agi/detection.cpp b/engines/agi/detection.cpp index 805fe7d366..5f7780bfe3 100644 --- a/engines/agi/detection.cpp +++ b/engines/agi/detection.cpp @@ -20,9 +20,6 @@ * */ -// FIXME: Avoid using printf -#define FORBIDDEN_SYMBOL_EXCEPTION_printf - #include "base/plugins.h" #include "engines/advancedDetector.h" @@ -491,10 +488,14 @@ const ADGameDescription *AgiMetaEngine::fallbackDetect(const FileMap &allFilesXX g_fallbackDesc.desc.gameid = _gameid.c_str(); g_fallbackDesc.desc.extra = _extra.c_str(); - printf("Your game version has been detected using fallback matching as a\n"); - printf("variant of %s (%s).\n", g_fallbackDesc.desc.gameid, g_fallbackDesc.desc.extra); - printf("If this is an original and unmodified version or new made Fanmade game,\n"); - printf("please report any, information previously printed by ScummVM to the team.\n"); + Common::String fallbackWarning; + + fallbackWarning = "Your game version has been detected using fallback matching as a\n"; + fallbackWarning += Common::String::format("variant of %s (%s).\n", g_fallbackDesc.desc.gameid, g_fallbackDesc.desc.extra); + fallbackWarning += "If this is an original and unmodified version or new made Fanmade game,\n"; + fallbackWarning += "please report any, information previously printed by ScummVM to the team.\n"; + + g_system->logMessage(LogMessageType::kWarning, fallbackWarning.c_str()); return (const ADGameDescription *)&g_fallbackDesc; } diff --git a/engines/agi/detection_tables.h b/engines/agi/detection_tables.h index ab0e9a1fe4..9d67b15adb 100644 --- a/engines/agi/detection_tables.h +++ b/engines/agi/detection_tables.h @@ -706,10 +706,10 @@ static const AGIGameDescription gameDescriptions[] = { FANMADE("Go West, Young Hippie", "ff31484ea465441cb5f3a0f8e956b716"), FANMADE("Good Man (demo v3.41)", "3facd8a8f856b7b6e0f6c3200274d88c"), - GAME_LVFPNF("agi-fanmade", "Groza (russian) [AGDS sample]", "logdir", "421da3a18004122a966d64ab6bd86d2e", -1, + GAME_LVFPNF("agi-fanmade", "Groza (russian) [AGDS sample]", "logdir", "421da3a18004122a966d64ab6bd86d2e", -1, Common::RU_RUS, 0x2440, GF_AGDS, GID_FANMADE, Common::kPlatformPC,GType_V2), - GAME_LVFPNF("agi-fanmade", "Get Outta Space Quest", "logdir", "aaea5b4a348acb669d13b0e6f22d4dc9", -1, + GAME_LVFPNF("agi-fanmade", "Get Outta Space Quest", "logdir", "aaea5b4a348acb669d13b0e6f22d4dc9", -1, Common::EN_ANY, 0x2440, GF_FANMADE, GID_GETOUTTASQ, Common::kPlatformPC,GType_V2), FANMADE_F("Half-Death - Terror At White-Mesa", "b62c05d0ace878261392073f57ae788c", GF_AGIMOUSE), diff --git a/engines/agi/loader_v1.cpp b/engines/agi/loader_v1.cpp index c6a3e66705..189c98ee98 100644 --- a/engines/agi/loader_v1.cpp +++ b/engines/agi/loader_v1.cpp @@ -64,7 +64,7 @@ int AgiLoader_v1::detectGame() { int AgiLoader_v1::loadDir_DDP(AgiDir *agid, int offset, int max) { Common::File fp; - + if (!fp.open(_filenameDisk0)) return errBadFileOpen; @@ -73,13 +73,13 @@ int AgiLoader_v1::loadDir_DDP(AgiDir *agid, int offset, int max) { agid[i].volume = 0xFF; agid[i].offset = _EMPTY; } - + fp.seek(offset, SEEK_SET); for (int i = 0; i <= max; i++) { int b0 = fp.readByte(); int b1 = fp.readByte(); int b2 = fp.readByte(); - + if (b0 == 0xFF && b1 == 0xFF && b2 == 0xFF) { agid[i].volume = 0xFF; agid[i].offset = _EMPTY; @@ -98,7 +98,7 @@ int AgiLoader_v1::loadDir_DDP(AgiDir *agid, int offset, int max) { int AgiLoader_v1::loadDir_BC(AgiDir *agid, int offset, int max) { Common::File fp; - + if (!fp.open(_filenameDisk0)) return errBadFileOpen; @@ -107,13 +107,13 @@ int AgiLoader_v1::loadDir_BC(AgiDir *agid, int offset, int max) { agid[i].volume = 0xFF; agid[i].offset = _EMPTY; } - + fp.seek(offset, SEEK_SET); for (int i = 0; i <= max; i++) { int b0 = fp.readByte(); int b1 = fp.readByte(); int b2 = fp.readByte(); - + if (b0 == 0xFF && b1 == 0xFF && b2 == 0xFF) { agid[i].volume = 0xFF; agid[i].offset = _EMPTY; @@ -171,7 +171,7 @@ uint8 *AgiLoader_v1::loadVolRes(struct AgiDir *agid) { if (offset == _EMPTY) return NULL; - + if (offset > IMAGE_SIZE) { fp.open(_filenameDisk1); offset -= IMAGE_SIZE; @@ -191,7 +191,7 @@ uint8 *AgiLoader_v1::loadVolRes(struct AgiDir *agid) { agid->len = fp.readUint16LE(); data = (uint8 *)calloc(1, agid->len + 32); fp.read(data, agid->len); - + fp.close(); return data; diff --git a/engines/agi/menu.cpp b/engines/agi/menu.cpp index cac1701596..d23a5a2e27 100644 --- a/engines/agi/menu.cpp +++ b/engines/agi/menu.cpp @@ -289,7 +289,7 @@ bool Menu::keyhandler(int key) { _vm->_game.clockEnabled = false; drawMenuBar(); } - + // Mouse handling if (_vm->_mouse.button) { int hmenu, vmenu; diff --git a/engines/agi/op_cmd.cpp b/engines/agi/op_cmd.cpp index 7e04328a67..9d899b1855 100644 --- a/engines/agi/op_cmd.cpp +++ b/engines/agi/op_cmd.cpp @@ -1146,7 +1146,7 @@ void cmdFollowEgo(AgiGame *state, uint8 *p) { vt.parm1 = p1 > vt.stepSize ? p1 : vt.stepSize; vt.parm2 = p2; vt.parm3 = 0xff; - + if (getVersion() < 0x2000) { _v[p2] = 0; vt.flags |= fUpdate | fAnimated; @@ -1270,7 +1270,7 @@ void cmdVersion(AgiGame *state, uint8 *p) { // no Sierra as it wraps textbox Common::String verMsg = TITLE " v%s"; - + int ver = getVersion(); int maj = (ver >> 12) & 0xf; int min = ver & 0xfff; @@ -1399,7 +1399,7 @@ void cmdDistance(AgiGame *state, uint8 *p) { // a zombie or the zombie getting turned away by the scarab) we make it appear the // zombie is far away from Rosella if the zombie is not already up and chasing her. enum zombieStates {ZOMBIE_SET_TO_RISE_UP, ZOMBIE_RISING_UP, ZOMBIE_CHASING_EGO}; - uint8 zombieStateVarNumList[] = {155, 156, (_v[vCurRoom] == 16) ? 162 : 158}; + uint8 zombieStateVarNumList[] = {155, 156, (uint8)((_v[vCurRoom] == 16) ? 162 : 158)}; uint8 zombieNum = p2 - 221; // Zombie's number (In range 0-2) uint8 zombieStateVarNum = zombieStateVarNumList[zombieNum]; // Number of the variable containing zombie's state uint8 zombieState = _v[zombieStateVarNum]; // Zombie's state @@ -1839,7 +1839,7 @@ int AgiEngine::runLogic(int n) { // ip = 2; // warning("running logic %d\n", n); // } - + if (_game.exitAllLogics) break; } diff --git a/engines/agi/op_test.cpp b/engines/agi/op_test.cpp index a44c68e0fc..4d5e6fffe1 100644 --- a/engines/agi/op_test.cpp +++ b/engines/agi/op_test.cpp @@ -403,7 +403,7 @@ int AgiEngine::testIfCode(int lognum) { case 0xFF: endTest = true; continue; - + default: // Evaluate the command and skip the rest of the instruction _agiCondCommands[op](state, p); diff --git a/engines/agi/opcodes.cpp b/engines/agi/opcodes.cpp index 29fb860635..807ab2dc2c 100644 --- a/engines/agi/opcodes.cpp +++ b/engines/agi/opcodes.cpp @@ -130,7 +130,7 @@ AgiInstruction insV1[] = { { "...", "", &cmdUnknown }, // 4E # show.obj { "load.logics", "n", &cmdLoadLogic }, // 4F # load.global.logics { "display", "nnns", &cmdDisplay }, // 50 TODO: 4 vs 3 args - { "prevent.input???", "", &cmdUnknown }, // 51 + { "prevent.input???", "", &cmdUnknown }, // 51 { "...", "", &cmdUnknown }, // 52 # nop { "...", "n", &cmdUnknown }, // 53 # text.screen { "...", "", &cmdUnknown }, // 54 ??? @@ -376,6 +376,18 @@ void AgiEngine::setupOpcodes() { logicNamesTest = insV1Test; logicNamesCmd = insV1; } + + // Alter opcode parameters for specific games + // TODO: This could be either turned into a game feature, or a version + // specific check, instead of a game version check + + // The Apple IIGS versions of MH1 and Goldrush both have a parameter for + // show.mouse and hide.mouse. Fixes bugs #3577754 and #3426946. + if ((getGameID() == GID_MH1 || getGameID() == GID_GOLDRUSH) && + getPlatform() == Common::kPlatformApple2GS) { + logicNamesCmd[176].args = "n"; // hide.mouse + logicNamesCmd[178].args = "n"; // show.mouse + } } } diff --git a/engines/agi/saveload.cpp b/engines/agi/saveload.cpp index 3e63da756d..d451a799a0 100644 --- a/engines/agi/saveload.cpp +++ b/engines/agi/saveload.cpp @@ -821,7 +821,7 @@ int AgiEngine::scummVMSaveLoadDialog(bool isSave) { if (slot < 0) return true; - + if (isSave) return doSave(slot, desc); else diff --git a/engines/agi/sound_pcjr.cpp b/engines/agi/sound_pcjr.cpp index d21baa450f..5bffca5765 100644 --- a/engines/agi/sound_pcjr.cpp +++ b/engines/agi/sound_pcjr.cpp @@ -234,7 +234,7 @@ int SoundGenPCJr::getNextNote(int ch) // if tone isn't touched.. it should be inited so it just plays silence // return 0 if it's passing more data // return -1 if it's passing nothing (end of data) -int SoundGenPCJr::getNextNote_v2(int ch) { +int SoundGenPCJr::getNextNote_v2(int ch) { ToneChan *tpcm; SndGenChan *chan; const byte *data; @@ -308,7 +308,7 @@ int SoundGenPCJr::getNextNote_v1(int ch) { _channel[ch].attenuationCopy = 0x0F; return -1; } - + // In the V1 player the default duration for a row is 3 ticks if (duration > 0) { duration--; diff --git a/engines/agi/text.cpp b/engines/agi/text.cpp index 1886a74ab1..4877be2647 100644 --- a/engines/agi/text.cpp +++ b/engines/agi/text.cpp @@ -64,7 +64,7 @@ void AgiEngine::printText2(int l, const char *msg, int foff, int xoff, int yoff, // Note: there were extra checks for *m being a cursor character // here (1, 2 or 3), which have been removed, as the cursor - // character is no longer printed via this function. + // character is no longer printed via this function. if (*m >= 0x20) { int ypos = (y1 * CHAR_LINES) + yoff; @@ -73,7 +73,7 @@ void AgiEngine::printText2(int l, const char *msg, int foff, int xoff, int yoff, if (xpos >= GFX_WIDTH) continue; - + _gfx->putTextCharacter(l, xpos, ypos, *m, fg, bg, checkerboard); if (x1 > maxx) diff --git a/engines/agi/words.cpp b/engines/agi/words.cpp index 4400112247..9c5b3d349a 100644 --- a/engines/agi/words.cpp +++ b/engines/agi/words.cpp @@ -41,7 +41,7 @@ int AgiEngine::loadWords_v1(Common::File &f) { int k; debug(0, "Loading dictionary"); - + // Loop through alphabet, as words in the dictionary file are sorted by // first character f.seek(f.pos() + 26 * 2, SEEK_SET); @@ -131,7 +131,7 @@ int AgiEngine::findWord(const char *word, int *flen) { *flen = 0; Common::Array<AgiWord *> &a = _game.words[c]; - + for (int i = 0; i < (int)a.size(); i++) { int wlen = strlen(a[i]->word); // Keep looking till we find the word itself, or the whole phrase. diff --git a/engines/agos/animation.cpp b/engines/agos/animation.cpp index 10c01741ae..9176412e0e 100644 --- a/engines/agos/animation.cpp +++ b/engines/agos/animation.cpp @@ -260,9 +260,6 @@ bool MoviePlayerDXA::load() { debug(0, "Playing video %s", videoName.c_str()); CursorMan.showMouse(false); - - _firstFrameOffset = _fileStream->pos(); - return true; } @@ -271,6 +268,10 @@ void MoviePlayerDXA::copyFrameToBuffer(byte *dst, uint x, uint y, uint pitch) { uint w = getWidth(); const Graphics::Surface *surface = decodeNextFrame(); + + if (!surface) + return; + byte *src = (byte *)surface->pixels; dst += y * pitch + x; @@ -281,7 +282,7 @@ void MoviePlayerDXA::copyFrameToBuffer(byte *dst, uint x, uint y, uint pitch) { } while (--h); if (hasDirtyPalette()) - setSystemPalette(); + g_system->getPaletteManager()->setPalette(getPalette(), 0, 256); } void MoviePlayerDXA::playVideo() { @@ -302,34 +303,7 @@ void MoviePlayerDXA::stopVideo() { } void MoviePlayerDXA::startSound() { - uint32 offset, size; - - if (getSoundTag() == MKTAG('W','A','V','E')) { - size = _fileStream->readUint32BE(); - - if (_sequenceNum) { - Common::File in; - - _fileStream->seek(size, SEEK_CUR); - - in.open("audio.wav"); - if (!in.isOpen()) { - error("Can't read offset file 'audio.wav'"); - } - - in.seek(_sequenceNum * 8, SEEK_SET); - offset = in.readUint32LE(); - size = in.readUint32LE(); - - in.seek(offset, SEEK_SET); - _bgSoundStream = Audio::makeWAVStream(in.readStream(size), DisposeAfterUse::YES); - in.close(); - } else { - _bgSoundStream = Audio::makeWAVStream(_fileStream->readStream(size), DisposeAfterUse::YES); - } - } else { - _bgSoundStream = Audio::SeekableAudioStream::openStreamFile(baseName); - } + start(); if (_bgSoundStream != NULL) { _vm->_mixer->stopHandle(_bgSound); @@ -344,8 +318,7 @@ void MoviePlayerDXA::nextFrame() { } if (_vm->_interactiveVideo == TYPE_LOOPING && endOfVideo()) { - _fileStream->seek(_firstFrameOffset); - _curFrame = -1; + rewind(); startSound(); } @@ -374,13 +347,15 @@ bool MoviePlayerDXA::processFrame() { copyFrameToBuffer((byte *)screen->pixels, (_vm->_screenWidth - getWidth()) / 2, (_vm->_screenHeight - getHeight()) / 2, screen->pitch); _vm->_system->unlockScreen(); - Common::Rational soundTime(_mixer->getSoundElapsedTime(_bgSound), 1000); - if ((_bgSoundStream == NULL) || ((soundTime * getFrameRate()).toInt() / 1000 < getCurFrame() + 1)) { + uint32 soundTime = _mixer->getSoundElapsedTime(_bgSound); + uint32 nextFrameStartTime = ((Video::VideoDecoder::VideoTrack *)getTrack(0))->getNextFrameStartTime(); + + if ((_bgSoundStream == NULL) || soundTime < nextFrameStartTime) { if (_bgSoundStream && _mixer->isSoundHandleActive(_bgSound)) { - while (_mixer->isSoundHandleActive(_bgSound) && (soundTime * getFrameRate()).toInt() < getCurFrame()) { + while (_mixer->isSoundHandleActive(_bgSound) && soundTime < nextFrameStartTime) { _vm->_system->delayMillis(10); - soundTime = Common::Rational(_mixer->getSoundElapsedTime(_bgSound), 1000); + soundTime = _mixer->getSoundElapsedTime(_bgSound); } // In case the background sound ends prematurely, update // _ticks so that we can still fall back on the no-sound @@ -399,14 +374,35 @@ bool MoviePlayerDXA::processFrame() { return false; } -void MoviePlayerDXA::updateVolume() { - if (g_system->getMixer()->isSoundHandleActive(_bgSound)) - g_system->getMixer()->setChannelVolume(_bgSound, getVolume()); -} +void MoviePlayerDXA::readSoundData(Common::SeekableReadStream *stream) { + uint32 tag = stream->readUint32BE(); + + if (tag == MKTAG('W','A','V','E')) { + uint32 size = stream->readUint32BE(); + + if (_sequenceNum) { + Common::File in; + + stream->skip(size); + + in.open("audio.wav"); + if (!in.isOpen()) { + error("Can't read offset file 'audio.wav'"); + } + + in.seek(_sequenceNum * 8, SEEK_SET); + uint32 offset = in.readUint32LE(); + size = in.readUint32LE(); -void MoviePlayerDXA::updateBalance() { - if (g_system->getMixer()->isSoundHandleActive(_bgSound)) - g_system->getMixer()->setChannelBalance(_bgSound, getBalance()); + in.seek(offset, SEEK_SET); + _bgSoundStream = Audio::makeWAVStream(in.readStream(size), DisposeAfterUse::YES); + in.close(); + } else { + _bgSoundStream = Audio::makeWAVStream(stream->readStream(size), DisposeAfterUse::YES); + } + } else { + _bgSoundStream = Audio::SeekableAudioStream::openStreamFile(baseName); + } } /////////////////////////////////////////////////////////////////////////////// @@ -415,7 +411,7 @@ void MoviePlayerDXA::updateBalance() { MoviePlayerSMK::MoviePlayerSMK(AGOSEngine_Feeble *vm, const char *name) - : MoviePlayer(vm), SmackerDecoder(vm->_mixer) { + : MoviePlayer(vm), SmackerDecoder() { debug(0, "Creating SMK cutscene player"); memset(baseName, 0, sizeof(baseName)); @@ -435,8 +431,6 @@ bool MoviePlayerSMK::load() { CursorMan.showMouse(false); - _firstFrameOffset = _fileStream->pos(); - return true; } @@ -445,6 +439,10 @@ void MoviePlayerSMK::copyFrameToBuffer(byte *dst, uint x, uint y, uint pitch) { uint w = getWidth(); const Graphics::Surface *surface = decodeNextFrame(); + + if (!surface) + return; + byte *src = (byte *)surface->pixels; dst += y * pitch + x; @@ -455,7 +453,7 @@ void MoviePlayerSMK::copyFrameToBuffer(byte *dst, uint x, uint y, uint pitch) { } while (--h); if (hasDirtyPalette()) - setSystemPalette(); + g_system->getPaletteManager()->setPalette(getPalette(), 0, 256); } void MoviePlayerSMK::playVideo() { @@ -468,6 +466,7 @@ void MoviePlayerSMK::stopVideo() { } void MoviePlayerSMK::startSound() { + start(); } void MoviePlayerSMK::handleNextFrame() { @@ -477,10 +476,8 @@ void MoviePlayerSMK::handleNextFrame() { } void MoviePlayerSMK::nextFrame() { - if (_vm->_interactiveVideo == TYPE_LOOPING && endOfVideo()) { - _fileStream->seek(_firstFrameOffset); - _curFrame = -1; - } + if (_vm->_interactiveVideo == TYPE_LOOPING && endOfVideo()) + rewind(); if (!endOfVideo()) { decodeNextFrame(); @@ -503,7 +500,7 @@ bool MoviePlayerSMK::processFrame() { uint32 waitTime = getTimeToNextFrame(); - if (!waitTime) { + if (!waitTime && !endOfVideoTracks()) { warning("dropped frame %i", getCurFrame()); return false; } diff --git a/engines/agos/animation.h b/engines/agos/animation.h index d1ff074b03..9e31fced6d 100644 --- a/engines/agos/animation.h +++ b/engines/agos/animation.h @@ -67,9 +67,6 @@ protected: virtual void handleNextFrame(); virtual bool processFrame() = 0; virtual void startSound() {} - -protected: - uint32 _firstFrameOffset; }; class MoviePlayerDXA : public MoviePlayer, Video::DXADecoder { @@ -84,9 +81,7 @@ public: virtual void stopVideo(); protected: - // VideoDecoder API - void updateVolume(); - void updateBalance(); + void readSoundData(Common::SeekableReadStream *stream); private: void handleNextFrame(); diff --git a/engines/agos/event.cpp b/engines/agos/event.cpp index ed26b96381..cc1c40c207 100644 --- a/engines/agos/event.cpp +++ b/engines/agos/event.cpp @@ -467,11 +467,7 @@ void AGOSEngine::delay(uint amount) { memset(_saveLoadName, 0, sizeof(_saveLoadName)); sprintf(_saveLoadName, "Quick %d", _saveLoadSlot); _saveLoadType = (event.kbd.hasFlags(Common::KBD_ALT)) ? 1 : 2; - - // We should only allow a load or save when it was possible in original - // This stops load/save during copy protection, conversations and cut scenes - if (!_mouseHideCount && !_showPreposition) - quickLoadOrSave(); + quickLoadOrSave(); } else if (event.kbd.hasFlags(Common::KBD_CTRL)) { if (event.kbd.keycode == Common::KEYCODE_a) { GUI::Dialog *_aboutDialog; diff --git a/engines/agos/midiparser_s1d.cpp b/engines/agos/midiparser_s1d.cpp index 9ca87436fc..bef7199a98 100644 --- a/engines/agos/midiparser_s1d.cpp +++ b/engines/agos/midiparser_s1d.cpp @@ -35,7 +35,7 @@ namespace AGOS { class MidiParser_S1D : public MidiParser { private: byte *_data; - bool _no_delta; + bool _noDelta; struct Loop { uint16 timer; @@ -49,7 +49,7 @@ protected: void resetTracking(); public: - MidiParser_S1D() : _data(0), _no_delta(false) {} + MidiParser_S1D() : _data(0), _noDelta(false) {} bool loadMusic(byte *data, uint32 size); }; @@ -75,14 +75,14 @@ void MidiParser_S1D::chainEvent(EventInfo &info) { } void MidiParser_S1D::parseNextEvent(EventInfo &info) { - info.start = _position._play_pos; + info.start = _position._playPos; info.length = 0; - info.delta = _no_delta ? 0 : readVLQ2(_position._play_pos); - _no_delta = false; + info.delta = _noDelta ? 0 : readVLQ2(_position._playPos); + _noDelta = false; - info.event = *_position._play_pos++; + info.event = *_position._playPos++; if (!(info.event & 0x80)) { - _no_delta = true; + _noDelta = true; info.event |= 0x80; } @@ -94,34 +94,43 @@ void MidiParser_S1D::parseNextEvent(EventInfo &info) { } else { switch (info.command()) { case 0x8: // note off - info.basic.param1 = *_position._play_pos++; + info.basic.param1 = *_position._playPos++; info.basic.param2 = 0; break; case 0x9: // note on - info.basic.param1 = *_position._play_pos++; - info.basic.param2 = *_position._play_pos++; + info.basic.param1 = *_position._playPos++; + info.basic.param2 = *_position._playPos++; + // Rewrite note on events with velocity 0 as note off events. + // This is the actual meaning of this, but theoretically this + // should not need to be rewritten, since all MIDI devices should + // interpret it like that. On the other hand all our MidiParser + // implementations do it and there seems to be code in MidiParser + // which relies on this for tracking active notes. + if (info.basic.param2 == 0) { + info.event = info.channel() | 0x80; + } break; case 0xA: { // loop control // In case the stop mode(?) is set to 0x80 this will stop the // track over here. - const int16 loopIterations = int8(*_position._play_pos++); + const int16 loopIterations = int8(*_position._playPos++); if (!loopIterations) { - _loops[info.channel()].start = _position._play_pos; + _loops[info.channel()].start = _position._playPos; } else { if (!_loops[info.channel()].timer) { if (_loops[info.channel()].start) { _loops[info.channel()].timer = uint16(loopIterations); - _loops[info.channel()].end = _position._play_pos; + _loops[info.channel()].end = _position._playPos; // Go to the start of the loop - _position._play_pos = _loops[info.channel()].start; + _position._playPos = _loops[info.channel()].start; } } else { if (_loops[info.channel()].timer) - _position._play_pos = _loops[info.channel()].start; + _position._playPos = _loops[info.channel()].start; --_loops[info.channel()].timer; } } @@ -141,13 +150,13 @@ void MidiParser_S1D::parseNextEvent(EventInfo &info) { break; case 0xC: // program change - info.basic.param1 = *_position._play_pos++; + info.basic.param1 = *_position._playPos++; info.basic.param2 = 0; break; case 0xD: // jump to loop end if (_loops[info.channel()].end) - _position._play_pos = _loops[info.channel()].end; + _position._playPos = _loops[info.channel()].end; // We need to read the next midi event here. Since we can not // safely pass this event to the MIDI event processing. @@ -178,7 +187,7 @@ bool MidiParser_S1D::loadMusic(byte *data, uint32 size) { pos += 1; // And now we're at the actual data. Only one track. - _num_tracks = 1; + _numTracks = 1; _data = pos; _tracks[0] = pos; @@ -194,7 +203,7 @@ bool MidiParser_S1D::loadMusic(byte *data, uint32 size) { void MidiParser_S1D::resetTracking() { MidiParser::resetTracking(); // The first event never contains any delta. - _no_delta = true; + _noDelta = true; memset(_loops, 0, sizeof(_loops)); } diff --git a/engines/agos/module.mk b/engines/agos/module.mk index 7ae5e17bf2..7069d8005b 100644 --- a/engines/agos/module.mk +++ b/engines/agos/module.mk @@ -51,7 +51,6 @@ ifdef ENABLE_AGOS2 MODULE_OBJS += \ animation.o \ feeble.o \ - installshield_cab.o \ oracle.o \ script_dp.o \ script_ff.o \ diff --git a/engines/agos/res.cpp b/engines/agos/res.cpp index 0305879390..2e44a6575c 100644 --- a/engines/agos/res.cpp +++ b/engines/agos/res.cpp @@ -23,6 +23,8 @@ // Resource file routines for Simon1/Simon2 +#include "common/archive.h" +#include "common/installshield_cab.h" #include "common/file.h" #include "common/memstream.h" #include "common/textconsole.h" @@ -31,7 +33,6 @@ #include "agos/agos.h" #include "agos/intern.h" #include "agos/sound.h" -#include "agos/installshield_cab.h" #include "common/zlib.h" @@ -43,7 +44,10 @@ ArchiveMan::ArchiveMan() { #ifdef ENABLE_AGOS2 void ArchiveMan::registerArchive(const Common::String &filename, int priority) { - add(filename, makeInstallShieldArchive(filename), priority); + Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(filename); + + if (stream) + add(filename, makeInstallShieldArchive(stream, DisposeAfterUse::YES), priority); } #endif diff --git a/engines/agos/saveload.cpp b/engines/agos/saveload.cpp index b3ec916b47..e13fa214d1 100644 --- a/engines/agos/saveload.cpp +++ b/engines/agos/saveload.cpp @@ -142,23 +142,41 @@ void AGOSEngine_Feeble::quickLoadOrSave() { } #endif +// The function uses segments of code from the original game scripts +// to allow quick loading and saving, but isn't perfect. +// +// Unfortuntely this allows loading and saving in locations, +// which aren't supported, and will not restore correctly: +// Various locations in Elvira 1/2 and Waxworks where saving +// was disabled void AGOSEngine::quickLoadOrSave() { - // The function uses segments of code from the original game scripts - // to allow quick loading and saving, but isn't perfect. - // - // Unfortuntely this allows loading and saving in locations, - // which aren't supported, and will not restore correctly: - // Any overhead maps in Simon the Sorcerer 2 - // Various locations in Elvira 1/2 and Waxworks where saving - // was disabled - - // The floppy disk demo of Simon the Sorcerer 1 doesn't work. - if (getFeatures() & GF_DEMO) - return; - bool success; Common::String buf; + // Disable loading and saving when it was not possible in the original: + // In overhead maps areas in Simon the Sorcerer 2 + // In the floppy disk demo of Simon the Sorcerer 1 + // In copy protection, conversations and cut scenes + if ((getGameType() == GType_SIMON2 && _boxStarHeight == 200) || + (getGameType() == GType_SIMON1 && (getFeatures() & GF_DEMO)) || + _mouseHideCount || _showPreposition) { + buf = Common::String::format("Quick load or save game isn't supported in this location"); + GUI::MessageDialog dialog(buf, "OK"); + dialog.runModal(); + return; + } + + // Check if Simon is walking, and stop when required + if (getGameType() == GType_SIMON1 && getBitFlag(11)) { + vcStopAnimation(11, 1122); + animate(4, 11, 1122, 0, 0, 2); + waitForSync(1122); + } else if (getGameType() == GType_SIMON2 && getBitFlag(11)) { + vcStopAnimation(11, 232); + animate(4, 11, 232, 0, 0, 2); + waitForSync(1122); + } + char *filename = genSaveName(_saveLoadSlot); if (_saveLoadType == 2) { Subroutine *sub; @@ -1406,7 +1424,7 @@ bool AGOSEngine_Elvira2::loadGame(const char *filename, bool restartMode) { // The floppy disk versions of Simon the Sorcerer 2 block changing // to scrolling rooms, if the copy protection fails. But the copy // protection flags are never set in the CD version. - // Setting this copy protection flag, allows saved games to be shared + // Setting this copy protection flag, allows saved games to be shared // between all versions of Simon the Sorcerer 2. if (getGameType() == GType_SIMON2) { setBitFlag(135, 1); diff --git a/engines/agos/sound.cpp b/engines/agos/sound.cpp index 85c449eafc..bec41bbbd3 100644 --- a/engines/agos/sound.cpp +++ b/engines/agos/sound.cpp @@ -297,7 +297,7 @@ Audio::AudioStream *RawSound::makeAudioStream(uint sound) { warning("RawSound::makeAudioStream: Could not open file \"%s\"", _filename.c_str()); return NULL; } - + file->seek(_offsets[sound], SEEK_SET); uint size = file->readUint32BE(); return Audio::makeRawStream(new Common::SeekableSubReadStream(file, _offsets[sound] + 4, _offsets[sound] + 4 + size, DisposeAfterUse::YES), 22050, _flags, DisposeAfterUse::YES); diff --git a/engines/cge/bitmap.cpp b/engines/cge/bitmap.cpp index 4f85957b3d..7089c8e0d1 100644 --- a/engines/cge/bitmap.cpp +++ b/engines/cge/bitmap.cpp @@ -94,7 +94,7 @@ Bitmap::Bitmap(CGEEngine *vm, uint16 w, uint16 h, uint8 fill) // Replicate across the entire table for (HideDesc *hdP = b + 1; hdP < (b + _h); hdP++) *hdP = *b; - + b->_skip = 0; // fix the first entry _v = v; _b = b; @@ -357,7 +357,7 @@ bool Bitmap::loadVBM(EncryptedStream *f) { // Read in the palette byte palData[kPalSize]; f->read(palData, kPalSize); - + const byte *srcP = palData; for (int idx = 0; idx < kPalCount; idx++, srcP += 3) { _vm->_bitmapPalette[idx]._r = *srcP; diff --git a/engines/cge/cge_main.cpp b/engines/cge/cge_main.cpp index a70e32de7e..f4f1cd3e0b 100644 --- a/engines/cge/cge_main.cpp +++ b/engines/cge/cge_main.cpp @@ -207,7 +207,7 @@ bool CGEEngine::loadGame(int slotNumber, SavegameHeader *header, bool tiny) { readStream = new Common::MemoryReadStream(dataBuffer, size, DisposeAfterUse::YES); } else { - // Open up the savgame file + // Open up the savegame file Common::String slotName = generateSaveName(slotNumber); Common::InSaveFile *saveFile = g_system->getSavefileManager()->openForLoading(slotName); @@ -280,7 +280,7 @@ Common::Error CGEEngine::loadGameState(int slot) { sceneDown(); _hero->park(); resetGame(); - + // If music is playing, kill it. if (_music) _midiPlayer->killMidi(); @@ -305,12 +305,21 @@ Common::Error CGEEngine::saveGameState(int slot, const Common::String &desc) { _hero->park(); _oldLev = _lev; + int x = _hero->_x; + int y = _hero->_y; + int z = _hero->_z; + // Write out the user's progress saveGame(slot, desc); + _commandHandler->addCommand(kCmdLevel, -1, _oldLev, &_sceneLight); // Reload the scene sceneUp(); + // Restore player position + _hero->gotoxy(x, y); + _hero->_z = z; + return Common::kNoError; } @@ -499,7 +508,7 @@ void CGEEngine::loadMapping() { if (!cf.err()) { // Move to the data for the given room cf.seek((_now - 1) * kMapArrSize); - + // Read in the data for (int z = 0; z < kMapZCnt; ++z) { cf.read(&_clusterMap[z][0], kMapXCnt); @@ -763,7 +772,7 @@ void System::touch(uint16 mask, int x, int y, Common::KeyCode keyCode) { if (mask & kEventKeyb) { if (keyCode == Common::KEYCODE_ESCAPE) { - // The original was calling keyClick() + // The original was calling keyClick() // The sound is uselessly annoying and noisy, so it has been removed _vm->killText(); if (_vm->_startupMode == 1) { @@ -1035,7 +1044,7 @@ void CGEEngine::loadSprite(const char *fname, int ref, int scene, int col = 0, i uint16 len; for (line = sprf.readLine(); !sprf.eos(); line = sprf.readLine()) { - len = line.size(); + len = line.size(); lcnt++; strcpy(tmpStr, line.c_str()); if (len == 0 || *tmpStr == '.') diff --git a/engines/cge/detection.cpp b/engines/cge/detection.cpp index d16b682501..3d6c24d68b 100644 --- a/engines/cge/detection.cpp +++ b/engines/cge/detection.cpp @@ -108,7 +108,7 @@ public: } virtual const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const { - return detectGameFilebased(allFiles, CGE::fileBasedFallback); + return detectGameFilebased(allFiles, fslist, CGE::fileBasedFallback); } virtual const char *getName() const { @@ -198,7 +198,7 @@ SaveStateList CGEMetaEngine::listSaves(const char *target) const { SaveStateDescriptor CGEMetaEngine::querySaveMetaInfos(const char *target, int slot) const { Common::String fileName = Common::String::format("%s.%03d", target, slot); Common::InSaveFile *f = g_system->getSavefileManager()->openForLoading(fileName); - + if (f) { CGE::SavegameHeader header; @@ -229,7 +229,7 @@ SaveStateDescriptor CGEMetaEngine::querySaveMetaInfos(const char *target, int sl return desc; } } - + return SaveStateDescriptor(); } diff --git a/engines/cge/events.cpp b/engines/cge/events.cpp index 1530c870ef..89802058f4 100644 --- a/engines/cge/events.cpp +++ b/engines/cge/events.cpp @@ -184,7 +184,7 @@ void Mouse::on() { step(0); if (_busy) _busy->step(0); - } + } } void Mouse::off() { diff --git a/engines/cge/events.h b/engines/cge/events.h index 522aa67905..ab8d87212d 100644 --- a/engines/cge/events.h +++ b/engines/cge/events.h @@ -105,7 +105,7 @@ private: void handleEvents(); public: EventManager(CGEEngine *vm); - void poll(); + void poll(); void clearEvent(Sprite *spr); CGEEvent &getNextEvent(); diff --git a/engines/cge/fileio.cpp b/engines/cge/fileio.cpp index f23105d823..609d5e86aa 100644 --- a/engines/cge/fileio.cpp +++ b/engines/cge/fileio.cpp @@ -98,7 +98,7 @@ uint16 ResourceManager::XCrypt(void *buf, uint16 length) { for (uint16 i = 0; i < length; i++) *b++ ^= kCryptSeed; - + return kCryptSeed; } diff --git a/engines/cge/text.cpp b/engines/cge/text.cpp index fd4120d49d..a8ce8777c5 100644 --- a/engines/cge/text.cpp +++ b/engines/cge/text.cpp @@ -63,7 +63,7 @@ int16 Text::count() { Common::String line; char tmpStr[kLineMax + 1]; - + int counter = 0; for (line = tf.readLine(); !tf.eos(); line = tf.readLine()) { diff --git a/engines/cine/anim.cpp b/engines/cine/anim.cpp index 410fcca1f3..075a59cfb6 100644 --- a/engines/cine/anim.cpp +++ b/engines/cine/anim.cpp @@ -202,13 +202,13 @@ AnimData::AnimData(const AnimData &src) : _width(src._width), if (src._data) { _data = new byte[_size]; assert(_data); - memcpy(_data, src._data, _size*sizeof(byte)); + memcpy(_data, src._data, _size * sizeof(byte)); } if (src._mask) { _mask = new byte[_size]; assert(_mask); - memcpy(_mask, src._mask, _size*sizeof(byte)); + memcpy(_mask, src._mask, _size * sizeof(byte)); } memset(_name, 0, sizeof(_name)); @@ -272,8 +272,7 @@ byte AnimData::getColor(int x, int y) { * @param transparent Transparent color (for ANIM_MASKSPRITE) */ void AnimData::load(byte *d, int type, uint16 w, uint16 h, int16 file, - int16 frame, const char *n, byte transparent) { - + int16 frame, const char *n, byte transparent) { assert(d); if (_data) { @@ -299,7 +298,7 @@ void AnimData::load(byte *d, int type, uint16 w, uint16 h, int16 file, _size = w * h; _data = new byte[_size]; assert(_data); - memcpy(_data, d, _size*sizeof(byte)); + memcpy(_data, d, _size * sizeof(byte)); break; case ANIM_MASK: @@ -536,7 +535,7 @@ int loadSpl(const char *resourceName, int16 idx) { entry = idx < 0 ? emptyAnimSpace() : idx; assert(entry >= 0); - g_cine->_animDataTable[entry].load(dataPtr, ANIM_RAW, g_cine->_partBuffer[foundFileIdx].unpackedSize, 1, foundFileIdx, 0, currentPartName); + g_cine->_animDataTable[entry].load(dataPtr + 0x16, ANIM_RAW, g_cine->_partBuffer[foundFileIdx].unpackedSize - 0x16, 1, foundFileIdx, 0, currentPartName); free(dataPtr); return entry + 1; @@ -546,9 +545,10 @@ int loadSpl(const char *resourceName, int16 idx) { * Load 1bpp mask * @param resourceName Mask filename * @param idx Target index in animDataTable (-1 if any empty space will do) + * @param frameIndex frame of animation to load (-1 for all frames) * @return The number of the animDataTable entry after the loaded mask (-1 if error) */ -int loadMsk(const char *resourceName, int16 idx) { +int loadMsk(const char *resourceName, int16 idx, int16 frameIndex) { int16 foundFileIdx = findFileInBundle(resourceName); if (foundFileIdx < 0) { return -1; @@ -563,9 +563,18 @@ int loadMsk(const char *resourceName, int16 idx) { loadAnimHeader(animHeader, readS); ptr = dataPtr + 0x16; + int16 startFrame = 0; + int16 endFrame = animHeader.numFrames; + + if (frameIndex >= 0) { + startFrame = frameIndex; + endFrame = frameIndex + 1; + ptr += frameIndex * animHeader.frameWidth * animHeader.frameHeight; + } + entry = idx < 0 ? emptyAnimSpace() : idx; assert(entry >= 0); - for (int16 i = 0; i < animHeader.numFrames; i++, entry++) { + for (int16 i = startFrame; i < endFrame; i++, entry++) { g_cine->_animDataTable[entry].load(ptr, ANIM_MASK, animHeader.frameWidth, animHeader.frameHeight, foundFileIdx, i, currentPartName); ptr += animHeader.frameWidth * animHeader.frameHeight; } @@ -578,9 +587,10 @@ int loadMsk(const char *resourceName, int16 idx) { * Load animation * @param resourceName Animation filename * @param idx Target index in animDataTable (-1 if any empty space will do) + * @param frameIndex frame of animation to load (-1 for all frames) * @return The number of the animDataTable entry after the loaded animation (-1 if error) */ -int loadAni(const char *resourceName, int16 idx) { +int loadAni(const char *resourceName, int16 idx, int16 frameIndex) { int16 foundFileIdx = findFileInBundle(resourceName); if (foundFileIdx < 0) { return -1; @@ -596,6 +606,15 @@ int loadAni(const char *resourceName, int16 idx) { loadAnimHeader(animHeader, readS); ptr = dataPtr + 0x16; + int16 startFrame = 0; + int16 endFrame = animHeader.numFrames; + + if (frameIndex >= 0) { + startFrame = frameIndex; + endFrame = frameIndex + 1; + ptr += frameIndex * animHeader.frameWidth * animHeader.frameHeight; + } + transparentColor = getAnimTransparentColor(resourceName); // TODO: Merge this special case hack into getAnimTransparentColor somehow. @@ -609,7 +628,7 @@ int loadAni(const char *resourceName, int16 idx) { entry = idx < 0 ? emptyAnimSpace() : idx; assert(entry >= 0); - for (int16 i = 0; i < animHeader.numFrames; i++, entry++) { + for (int16 i = startFrame; i < endFrame; i++, entry++) { // special case transparency handling if (!strcmp(resourceName, "L2202.ANI")) { transparentColor = i < 2 ? 0 : 7; @@ -669,22 +688,23 @@ void convert8BBP2(byte *dest, byte *source, int16 width, int16 height) { *(source + k) <<= 1; if (k > 0 + m) color <<= 1; - } // end k + } // end k *(dest++) = color; - } // end i - } // end m + } // end i + } // end m source += 0x10; - } // end j + } // end j } /** * Load image set * @param resourceName Image set filename * @param idx Target index in animDataTable (-1 if any empty space will do) + * @param frameIndex frame of animation to load (-1 for all frames) * @return The number of the animDataTable entry after the loaded image set (-1 if error) */ -int loadSet(const char *resourceName, int16 idx) { +int loadSet(const char *resourceName, int16 idx, int16 frameIndex = -1) { AnimHeader2Struct header2; uint16 numSpriteInAnim; int16 foundFileIdx = findFileInBundle(resourceName); @@ -708,7 +728,16 @@ int loadSet(const char *resourceName, int16 idx) { entry = idx < 0 ? emptyAnimSpace() : idx; assert(entry >= 0); - for (int16 i = 0; i < numSpriteInAnim; i++, entry++) { + int16 startFrame = 0; + int16 endFrame = numSpriteInAnim; + + if (frameIndex >= 0) { + startFrame = frameIndex; + endFrame = frameIndex + 1; + ptr += 0x10 * frameIndex; + } + + for (int16 i = startFrame; i < endFrame; i++, entry++) { Common::MemoryReadStream readS(ptr, 0x10); header2.field_0 = readS.readUint32BE(); @@ -755,7 +784,7 @@ int loadSeq(const char *resourceName, int16 idx) { byte *dataPtr = readBundleFile(foundFileIdx); int entry = idx < 0 ? emptyAnimSpace() : idx; - g_cine->_animDataTable[entry].load(dataPtr+0x16, ANIM_RAW, g_cine->_partBuffer[foundFileIdx].unpackedSize-0x16, 1, foundFileIdx, 0, currentPartName); + g_cine->_animDataTable[entry].load(dataPtr + 0x16, ANIM_RAW, g_cine->_partBuffer[foundFileIdx].unpackedSize - 0x16, 1, foundFileIdx, 0, currentPartName); free(dataPtr); return entry + 1; } @@ -767,18 +796,18 @@ int loadSeq(const char *resourceName, int16 idx) { * @return The number of the animDataTable entry after the loaded resource (-1 if error) * @todo Implement loading of all resource types */ -int loadResource(const char *resourceName, int16 idx) { +int loadResource(const char *resourceName, int16 idx, int16 frameIndex) { int result = -1; // Return an error by default if (strstr(resourceName, ".SPL")) { result = loadSpl(resourceName, idx); } else if (strstr(resourceName, ".MSK")) { - result = loadMsk(resourceName, idx); + result = loadMsk(resourceName, idx, frameIndex); } else if (strstr(resourceName, ".ANI")) { - result = loadAni(resourceName, idx); + result = loadAni(resourceName, idx, frameIndex); } else if (strstr(resourceName, ".ANM")) { - result = loadAni(resourceName, idx); + result = loadAni(resourceName, idx, frameIndex); } else if (strstr(resourceName, ".SET")) { - result = loadSet(resourceName, idx); + result = loadSet(resourceName, idx, frameIndex); } else if (strstr(resourceName, ".SEQ")) { result = loadSeq(resourceName, idx); } else if (strstr(resourceName, ".H32")) { diff --git a/engines/cine/anim.h b/engines/cine/anim.h index 9c06c260ce..c5130aab82 100644 --- a/engines/cine/anim.h +++ b/engines/cine/anim.h @@ -98,7 +98,7 @@ public: void freeAnimDataTable(); void freeAnimDataRange(byte startIdx, byte numIdx); -int loadResource(const char *resourceName, int16 idx = -1); +int loadResource(const char *resourceName, int16 idx = -1, int16 frameIndex = -1); void loadResourcesFromSave(Common::SeekableReadStream &fHandle, enum CineSaveGameFormat saveGameFormat); void generateMask(const byte *sprite, byte *mask, uint16 size, byte transparency); diff --git a/engines/cine/bg_list.cpp b/engines/cine/bg_list.cpp index 693fea3294..36ecf53dea 100644 --- a/engines/cine/bg_list.cpp +++ b/engines/cine/bg_list.cpp @@ -39,9 +39,9 @@ uint32 var8; * @param objIdx Sprite description */ void addToBGList(int16 objIdx) { - renderer->incrustSprite(g_cine->_objectTable[objIdx]); - createBgIncrustListElement(objIdx, 0); + + renderer->incrustSprite(g_cine->_bgIncrustList.back()); } /** @@ -49,9 +49,9 @@ void addToBGList(int16 objIdx) { * @param objIdx Sprite description */ void addSpriteFilledToBGList(int16 objIdx) { - renderer->incrustMask(g_cine->_objectTable[objIdx]); - createBgIncrustListElement(objIdx, 1); + + renderer->incrustMask(g_cine->_bgIncrustList.back()); } /** @@ -103,9 +103,9 @@ void loadBgIncrustFromSave(Common::SeekableReadStream &fHandle) { g_cine->_bgIncrustList.push_back(tmp); if (tmp.param == 0) { - renderer->incrustSprite(g_cine->_objectTable[tmp.objIdx]); + renderer->incrustSprite(tmp); } else { - renderer->incrustMask(g_cine->_objectTable[tmp.objIdx]); + renderer->incrustMask(tmp); } } } diff --git a/engines/cine/cine.cpp b/engines/cine/cine.cpp index 6b94c33c31..aa7221f733 100644 --- a/engines/cine/cine.cpp +++ b/engines/cine/cine.cpp @@ -189,6 +189,19 @@ void CineEngine::initialize() { g_cine->_messageTable.clear(); resetObjectTable(); + if (getGameType() == Cine::GType_OS) { + disableSystemMenu = 1; + } else { + // WORKAROUND: We do not save this variable in FW's savegames. + // Initializing this to 1, like we do it in the OS case, will + // cause the menu disabled when loading from the launcher or + // command line. + // A proper fix here would be to save this variable in FW's saves. + // Since it seems these are unversioned so far, there would be need + // to properly add versioning to them first. + disableSystemMenu = 0; + } + var8 = 0; var2 = var3 = var4 = var5 = 0; diff --git a/engines/cine/cine.h b/engines/cine/cine.h index 55376dce29..47edf51c30 100644 --- a/engines/cine/cine.h +++ b/engines/cine/cine.h @@ -159,7 +159,7 @@ private: bool _preLoad; int _timerDelayMultiplier; - public: +public: // TODO: These are pseudo-global vars // They better belong to appropriate classes Common::Array<AnimData> _animDataTable; diff --git a/engines/cine/console.cpp b/engines/cine/console.cpp index 0a24b2408a..4af28592e7 100644 --- a/engines/cine/console.cpp +++ b/engines/cine/console.cpp @@ -28,7 +28,7 @@ namespace Cine { bool labyrinthCheat; CineConsole::CineConsole(CineEngine *vm) : GUI::Debugger(), _vm(vm) { - DCmd_Register("labyrinthCheat", WRAP_METHOD(CineConsole, Cmd_LabyrinthCheat)); + DCmd_Register("labyrinthCheat", WRAP_METHOD(CineConsole, Cmd_LabyrinthCheat)); labyrinthCheat = false; } diff --git a/engines/cine/detection_tables.h b/engines/cine/detection_tables.h index 0ec2768bae..bf02f0519c 100644 --- a/engines/cine/detection_tables.h +++ b/engines/cine/detection_tables.h @@ -251,7 +251,7 @@ static const CINEGameDescription gameDescriptions[] = { AD_ENTRY1("procs00", "d6752e7d25924cb866b61eb7cb0c8b56"), Common::EN_GRB, Common::kPlatformPC, - ADGF_NO_FLAGS, + ADGF_UNSTABLE, GUIO0() }, GType_OS, @@ -267,7 +267,7 @@ static const CINEGameDescription gameDescriptions[] = { AD_ENTRY1("procs1", "9629129b86979fa592c1787385bf3695"), Common::EN_GRB, Common::kPlatformPC, - ADGF_NO_FLAGS, + ADGF_UNSTABLE, GUIO0() }, GType_OS, @@ -281,7 +281,7 @@ static const CINEGameDescription gameDescriptions[] = { AD_ENTRY1("procs1", "d8c3a9d05a63e4cfa801826a7063a126"), Common::EN_USA, Common::kPlatformPC, - ADGF_NO_FLAGS, + ADGF_UNSTABLE, GUIO0() }, GType_OS, @@ -295,7 +295,7 @@ static const CINEGameDescription gameDescriptions[] = { AD_ENTRY1("procs00", "862a75d76fb7fffec30e52be9ad1c474"), Common::EN_USA, Common::kPlatformPC, - ADGF_NO_FLAGS, + ADGF_UNSTABLE, GUIO0() }, GType_OS, @@ -309,7 +309,7 @@ static const CINEGameDescription gameDescriptions[] = { AD_ENTRY1("procs1", "39b91ae35d1297ce0a76a1a803ca1593"), Common::DE_DEU, Common::kPlatformPC, - ADGF_NO_FLAGS, + ADGF_UNSTABLE, GUIO0() }, GType_OS, @@ -323,7 +323,7 @@ static const CINEGameDescription gameDescriptions[] = { AD_ENTRY1("procs1", "74c2dabd9d212525fca8875a5f6d8994"), Common::ES_ESP, Common::kPlatformPC, - ADGF_NO_FLAGS, + ADGF_UNSTABLE, GUIO0() }, GType_OS, @@ -341,7 +341,7 @@ static const CINEGameDescription gameDescriptions[] = { }, Common::ES_ESP, Common::kPlatformPC, - ADGF_NO_FLAGS, + ADGF_UNSTABLE, GUIO0() }, GType_OS, @@ -355,7 +355,7 @@ static const CINEGameDescription gameDescriptions[] = { AD_ENTRY1("procs00", "f143567f08cfd1a9b1c9a41c89eadfef"), Common::FR_FRA, Common::kPlatformPC, - ADGF_NO_FLAGS, + ADGF_UNSTABLE, GUIO0() }, GType_OS, @@ -369,7 +369,7 @@ static const CINEGameDescription gameDescriptions[] = { AD_ENTRY1("procs1", "da066e6b8dd93f2502c2a3755f08dc12"), Common::IT_ITA, Common::kPlatformPC, - ADGF_NO_FLAGS, + ADGF_UNSTABLE, GUIO0() }, GType_OS, @@ -383,7 +383,7 @@ static const CINEGameDescription gameDescriptions[] = { AD_ENTRY1("procs0", "a9da5531ead0ebf9ad387fa588c0cbb0"), Common::EN_GRB, Common::kPlatformAmiga, - ADGF_NO_FLAGS, + ADGF_UNSTABLE, GUIO1(GUIO_NOMIDI) }, GType_OS, @@ -397,7 +397,7 @@ static const CINEGameDescription gameDescriptions[] = { AD_ENTRY1("procs0", "8a429ced2f4acff8a15ae125174042e8"), Common::EN_GRB, Common::kPlatformAmiga, - ADGF_NO_FLAGS, + ADGF_UNSTABLE, GUIO1(GUIO_NOMIDI) }, GType_OS, @@ -411,7 +411,7 @@ static const CINEGameDescription gameDescriptions[] = { AD_ENTRY1("procs0", "d5f27e33fc29c879f36f15b86ccfa58c"), Common::EN_USA, Common::kPlatformAmiga, - ADGF_NO_FLAGS, + ADGF_UNSTABLE, GUIO1(GUIO_NOMIDI) }, GType_OS, @@ -425,7 +425,7 @@ static const CINEGameDescription gameDescriptions[] = { AD_ENTRY1("procs0", "8b7dce249821d3a62b314399c4334347"), Common::DE_DEU, Common::kPlatformAmiga, - ADGF_NO_FLAGS, + ADGF_UNSTABLE, GUIO1(GUIO_NOMIDI) }, GType_OS, @@ -439,7 +439,7 @@ static const CINEGameDescription gameDescriptions[] = { AD_ENTRY1("procs0", "35fc295ddd0af9da932d256ba799a4b0"), Common::ES_ESP, Common::kPlatformAmiga, - ADGF_NO_FLAGS, + ADGF_UNSTABLE, GUIO1(GUIO_NOMIDI) }, GType_OS, @@ -453,7 +453,7 @@ static const CINEGameDescription gameDescriptions[] = { AD_ENTRY1("procs0", "d4ea4a97e01fa67ea066f9e785050ed2"), Common::FR_FRA, Common::kPlatformAmiga, - ADGF_NO_FLAGS, + ADGF_UNSTABLE, GUIO1(GUIO_NOMIDI) }, GType_OS, @@ -467,7 +467,7 @@ static const CINEGameDescription gameDescriptions[] = { AD_ENTRY1("demo", "8d3a750d1c840b1b1071e42f9e6f6aa2"), Common::EN_GRB, Common::kPlatformAmiga, - ADGF_DEMO, + ADGF_DEMO | ADGF_UNSTABLE, GUIO1(GUIO_NOMIDI) }, GType_OS, @@ -481,7 +481,7 @@ static const CINEGameDescription gameDescriptions[] = { AD_ENTRY1("procs0", "1501d5ae364b2814a33ed19347c3fcae"), Common::EN_GRB, Common::kPlatformAtariST, - ADGF_NO_FLAGS, + ADGF_UNSTABLE, GUIO1(GUIO_NOMIDI) }, GType_OS, @@ -495,7 +495,7 @@ static const CINEGameDescription gameDescriptions[] = { AD_ENTRY1("procs0", "2148d25de3219dd4a36580ca735d0afa"), Common::FR_FRA, Common::kPlatformAtariST, - ADGF_NO_FLAGS, + ADGF_UNSTABLE, GUIO1(GUIO_NOMIDI) }, GType_OS, diff --git a/engines/cine/gfx.cpp b/engines/cine/gfx.cpp index 918d522606..636c0cf8d9 100644 --- a/engines/cine/gfx.cpp +++ b/engines/cine/gfx.cpp @@ -113,7 +113,7 @@ FWRenderer::FWRenderer() : _background(NULL), _backupPal(), _cmd(""), assert(_backBuffer); memset(_backBuffer, 0, _screenSize); - memset(_bgName, 0, sizeof (_bgName)); + memset(_bgName, 0, sizeof(_bgName)); } @@ -174,7 +174,8 @@ void FWRenderer::fillSprite(const ObjectStruct &obj, uint8 color) { * @param obj Object info * @param fillColor Sprite color */ -void FWRenderer::incrustMask(const ObjectStruct &obj, uint8 color) { +void FWRenderer::incrustMask(const BGIncrust &incrust, uint8 color) { + const ObjectStruct &obj = g_cine->_objectTable[incrust.objIdx]; const byte *data = g_cine->_animDataTable[obj.frame].data(); int x, y, width, height; @@ -218,7 +219,9 @@ void FWRenderer::drawSprite(const ObjectStruct &obj) { * Draw color sprite on background * @param obj Object info */ -void FWRenderer::incrustSprite(const ObjectStruct &obj) { +void FWRenderer::incrustSprite(const BGIncrust &incrust) { + const ObjectStruct &obj = g_cine->_objectTable[incrust.objIdx]; + const byte *data = g_cine->_animDataTable[obj.frame].data(); const byte *mask = g_cine->_animDataTable[obj.frame].mask(); int x, y, width, height; @@ -246,14 +249,16 @@ void FWRenderer::drawCommand() { unsigned int i; int x = 10, y = _cmdY; - drawPlainBox(x, y, 301, 11, 0); - drawBorder(x - 1, y - 1, 302, 12, 2); + if (disableSystemMenu == 0) { + drawPlainBox(x, y, 301, 11, 0); + drawBorder(x - 1, y - 1, 302, 12, 2); - x += 2; - y += 2; + x += 2; + y += 2; - for (i = 0; i < _cmd.size(); i++) { - x = drawChar(_cmd[i], x, y); + for (i = 0; i < _cmd.size(); i++) { + x = drawChar(_cmd[i], x, y); + } } } @@ -298,10 +303,11 @@ void FWRenderer::drawMessage(const char *str, int x, int y, int width, int color for (i = 0; str[i]; i++, line--) { // Fit line of text into textbox if (!line) { - while (str[i] == ' ') i++; + while (str[i] == ' ') + i++; line = fitLine(str + i, tw, words, cw); - if ( str[i + line] != '\0' && str[i + line] != 0x7C && words) { + if (str[i + line] != '\0' && str[i + line] != 0x7C && words) { space = (tw - cw) / words; extraSpace = (tw - cw) % words; } else { @@ -465,6 +471,41 @@ int FWRenderer::drawChar(char character, int x, int y) { return x; } +/** + * Clears the character glyph to black + * This function is called "undrawChar", because the original only applies + * this drawing after the original glyph has been drawn. + * Possible TODO: Find a better name. + * @param character Character to undraw + * @param x Character coordinate + * @param y Character coordinate + */ +int FWRenderer::undrawChar(char character, int x, int y) { + int width, idx; + + if (character == ' ') { + x += 5; + } else if ((width = g_cine->_textHandler.fontParamTable[(unsigned char)character].characterWidth)) { + idx = g_cine->_textHandler.fontParamTable[(unsigned char)character].characterIdx; + const byte *sprite = g_cine->_textHandler.textTable[idx][FONT_DATA]; + for (uint i = 0; i < FONT_HEIGHT; ++i) { + byte *dst = _backBuffer + (y + i) * 320 + x; + for (uint j = 0; j < FONT_WIDTH; ++j, ++dst) { + // The original does this based on whether bit 1 of the pixel + // is set. Since that's the only bit ever set in (FW) this + // check should be fine. + // TODO: Check how Operation Stealth Amiga works + if (*sprite++) { + *dst = 0; + } + } + } + x += width + 1; + } + + return x; +} + int FWRenderer::getStringWidth(const char *str) { const char *p = str; int width = 0; @@ -839,7 +880,7 @@ void OSRenderer::restorePalette(Common::SeekableReadStream &fHandle, int version fHandle.read(buf, kHighPalNumBytes); - if (colorCount == kHighPalNumBytes) { + if (colorCount == kHighPalNumColors) { // Load the active 256 color palette from file _activePal.load(buf, sizeof(buf), kHighPalFormat, kHighPalNumColors, CINE_LITTLE_ENDIAN); } else { @@ -963,20 +1004,29 @@ void SelectionMenu::drawMenu(FWRenderer &r, bool top) { charX = x + 4; if (i == _selection) { + int color; + if (isAmiga) { - // The original Amiga version is using a different highlight color here, - // but with our current code it is not possible to change the text color, - // thus we can not use the Amiga's color, since otherwise the text - // wouldn't be visible anymore. - r.drawPlainBox(charX, lineY, _width - 8, FONT_HEIGHT, top ? r._messageBg/*2*/ : 18); + if (top) { + color = 2; + } else { + color = 18; + } } else { - r.drawPlainBox(charX, lineY, _width - 8, 9, 0); + color = 0; } + + r.drawPlainBox(x + 2, lineY - 1, _width - 3, 9, color); } const int size = _elements[i].size(); - for (int j = 0; j < size; ++j) - charX = r.drawChar(_elements[i][j], charX, lineY); + for (int j = 0; j < size; ++j) { + if (isAmiga && i == _selection) { + charX = r.undrawChar(_elements[i][j], charX, lineY); + } else { + charX = r.drawChar(_elements[i][j], charX, lineY); + } + } } } @@ -1119,7 +1169,8 @@ void OSRenderer::clear() { * @param obj Object info * @param fillColor Sprite color */ -void OSRenderer::incrustMask(const ObjectStruct &obj, uint8 color) { +void OSRenderer::incrustMask(const BGIncrust &incrust, uint8 color) { + const ObjectStruct &obj = g_cine->_objectTable[incrust.objIdx]; const byte *data = g_cine->_animDataTable[obj.frame].data(); int x, y, width, height; @@ -1154,15 +1205,16 @@ void OSRenderer::drawSprite(const ObjectStruct &obj) { * Draw color sprite * @param obj Object info */ -void OSRenderer::incrustSprite(const ObjectStruct &obj) { - const byte *data = g_cine->_animDataTable[obj.frame].data(); +void OSRenderer::incrustSprite(const BGIncrust &incrust) { + const ObjectStruct &obj = g_cine->_objectTable[incrust.objIdx]; + const byte *data = g_cine->_animDataTable[incrust.frame].data(); int x, y, width, height, transColor; - x = obj.x; - y = obj.y; + x = incrust.x; + y = incrust.y; transColor = obj.part; - width = g_cine->_animDataTable[obj.frame]._realWidth; - height = g_cine->_animDataTable[obj.frame]._height; + width = g_cine->_animDataTable[incrust.frame]._realWidth; + height = g_cine->_animDataTable[incrust.frame]._height; if (_bgTable[_currentBg].bg) { drawSpriteRaw2(data, transColor, width, height, _bgTable[_currentBg].bg, x, y); @@ -1225,7 +1277,6 @@ void OSRenderer::renderOverlay(const Common::List<overlay>::iterator &it) { int len, idx, width, height; ObjectStruct *obj; AnimData *sprite; - byte *mask; byte color; switch (it->type) { @@ -1235,12 +1286,7 @@ void OSRenderer::renderOverlay(const Common::List<overlay>::iterator &it) { break; } sprite = &g_cine->_animDataTable[g_cine->_objectTable[it->objIdx].frame]; - len = sprite->_realWidth * sprite->_height; - mask = new byte[len]; - generateMask(sprite->data(), mask, len, g_cine->_objectTable[it->objIdx].part); - remaskSprite(mask, it); - drawMaskedSprite(g_cine->_objectTable[it->objIdx], mask); - delete[] mask; + drawSprite(&(*it), sprite->data(), sprite->_realWidth, sprite->_height, _backBuffer, g_cine->_objectTable[it->objIdx].x, g_cine->_objectTable[it->objIdx].y, g_cine->_objectTable[it->objIdx].part, sprite->_bpp); break; // game message @@ -1290,14 +1336,6 @@ void OSRenderer::renderOverlay(const Common::List<overlay>::iterator &it) { maskBgOverlay(_bgTable[it->x].bg, sprite->data(), sprite->_realWidth, sprite->_height, _backBuffer, obj->x, obj->y); break; - // FIXME: Implement correct drawing of type 21 overlays. - // Type 21 overlays aren't just filled rectangles, I found their drawing routine - // from Operation Stealth's drawSprite routine. So they're likely some kind of sprites - // and it's just a coincidence that the oxygen meter during the first arcade sequence - // works even somehow currently. I tried the original under DOSBox and the oxygen gauge - // is a long red bar that gets shorter as the air runs out. - case 21: - // A filled rectangle: case 22: // TODO: Check it this implementation really works correctly (Some things might be wrong, needs testing). assert(it->objIdx < NUM_MAX_OBJECT); @@ -1307,7 +1345,7 @@ void OSRenderer::renderOverlay(const Common::List<overlay>::iterator &it) { height = obj->costume; drawPlainBox(obj->x, obj->y, width, height, color); debug(5, "renderOverlay: type=%d, x=%d, y=%d, width=%d, height=%d, color=%d", - it->type, obj->x, obj->y, width, height, color); + it->type, obj->x, obj->y, width, height, color); break; // something else @@ -1431,7 +1469,7 @@ void OSRenderer::selectBg(unsigned int idx) { if (_bgTable[idx].bg) { assert(_bgTable[idx].pal.isValid() && !(_bgTable[idx].pal.empty())); - _currentBg = idx; + _currentBg = idx; } else warning("OSRenderer::selectBg(%d) - attempt to select null background", idx); reloadPalette(); @@ -1752,6 +1790,82 @@ void drawSpriteRaw(const byte *spritePtr, const byte *maskPtr, int16 width, int1 } } +void OSRenderer::drawSprite(overlay *overlayPtr, const byte *spritePtr, int16 width, int16 height, byte *page, int16 x, int16 y, byte transparentColor, byte bpp) { + byte *pMask = NULL; + + // draw the mask based on next objects in the list + Common::List<overlay>::iterator it; + for (it = g_cine->_overlayList.begin(); it != g_cine->_overlayList.end(); ++it) { + if (&(*it) == overlayPtr) { + break; + } + } + + while (it != g_cine->_overlayList.end()) { + overlay *pCurrentOverlay = &(*it); + if ((pCurrentOverlay->type == 5) || ((pCurrentOverlay->type == 21) && (pCurrentOverlay->x == overlayPtr->objIdx))) { + AnimData *sprite = &g_cine->_animDataTable[g_cine->_objectTable[it->objIdx].frame]; + + if (pMask == NULL) { + pMask = new byte[width * height]; + + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { + byte spriteColor = spritePtr[width * i + j]; + pMask[width * i + j] = spriteColor; + } + } + } + + for (int i = 0; i < sprite->_realWidth; i++) { + for (int j = 0; j < sprite->_height; j++) { + int inMaskX = (g_cine->_objectTable[it->objIdx].x + i) - x; + int inMaskY = (g_cine->_objectTable[it->objIdx].y + j) - y; + + if (inMaskX >= 0 && inMaskX < width) { + if (inMaskY >= 0 && inMaskY < height) { + if (sprite->_bpp == 1) { + if (!sprite->getColor(i, j)) { + pMask[inMaskY * width + inMaskX] = page[x + y * 320 + inMaskX + inMaskY * 320]; + } + } + } + } + } + } + } + it++; + } + + // now, draw with the mask we created + if (pMask) { + spritePtr = pMask; + } + + // ignore transparent color in 1bpp + if (bpp == 1) { + transparentColor = 1; + } + + { + for (int i = 0; i < height; i++) { + byte *destPtr = page + x + y * 320; + destPtr += i * 320; + + for (int j = 0; j < width; j++) { + byte color = *(spritePtr++); + if ((transparentColor != color) && x + j >= 0 && x + j < 320 && i + y >= 0 && i + y < 200) { + *(destPtr++) = color; + } else { + destPtr++; + } + } + } + } + + delete[] pMask; +} + void drawSpriteRaw2(const byte *spritePtr, byte transColor, int16 width, int16 height, byte *page, int16 x, int16 y) { int16 i, j; diff --git a/engines/cine/gfx.h b/engines/cine/gfx.h index 737c49cc36..8b8843fd72 100644 --- a/engines/cine/gfx.h +++ b/engines/cine/gfx.h @@ -27,6 +27,7 @@ #include "common/rect.h" #include "common/stack.h" #include "cine/object.h" +#include "cine/bg_list.h" namespace Cine { @@ -151,6 +152,7 @@ protected: void drawBorder(int x, int y, int width, int height, byte color); void drawDoubleBorder(int x, int y, int width, int height, byte color); virtual int drawChar(char character, int x, int y); + virtual int undrawChar(char character, int x, int y); void drawLine(int x, int y, int width, int height, byte color); void remaskSprite(byte *mask, Common::List<overlay>::iterator it); virtual void drawBackground(); @@ -177,8 +179,8 @@ public: void drawFrame(); void setCommand(Common::String cmd); - virtual void incrustMask(const ObjectStruct &obj, uint8 color = 0); - virtual void incrustSprite(const ObjectStruct &obj); + virtual void incrustMask(const BGIncrust &incrust, uint8 color = 0); + virtual void incrustSprite(const BGIncrust &incrust); virtual void loadBg16(const byte *bg, const char *name, unsigned int idx = 0); virtual void loadCt16(const byte *ct, const char *name); @@ -223,6 +225,7 @@ private: protected: void drawSprite(const ObjectStruct &obj); + void drawSprite(overlay *overlayPtr, const byte *spritePtr, int16 width, int16 height, byte *page, int16 x, int16 y, byte transparentColor, byte bpp); int drawChar(char character, int x, int y); void drawBackground(); void renderOverlay(const Common::List<overlay>::iterator &it); @@ -238,8 +241,8 @@ public: void clear(); - void incrustMask(const ObjectStruct &obj, uint8 color = 0); - void incrustSprite(const ObjectStruct &obj); + void incrustMask(const BGIncrust &incrust, uint8 color = 0); + void incrustSprite(const BGIncrust &incrust); void loadBg16(const byte *bg, const char *name, unsigned int idx = 0); void loadCt16(const byte *ct, const char *name); @@ -285,7 +288,7 @@ byte gfxGetColor(int16 x, int16 y, const byte *ptr, int16 width); void gfxResetRawPage(byte *pageRaw); void gfxConvertSpriteToRaw(byte *dst, const byte *src, uint16 w, uint16 h); -void gfxCopyRawPage(byte *source, byte * dest); +void gfxCopyRawPage(byte *source, byte *dest); void gfxFlipRawPage(byte *frontBuffer); void drawSpriteRaw(const byte *spritePtr, const byte *maskPtr, int16 width, int16 height, byte *page, int16 x, int16 y); void gfxDrawPlainBoxRaw(int16 x1, int16 y1, int16 x2, int16 y2, byte color, byte *page); diff --git a/engines/cine/main_loop.cpp b/engines/cine/main_loop.cpp index 971830ce8f..c822f1cabd 100644 --- a/engines/cine/main_loop.cpp +++ b/engines/cine/main_loop.cpp @@ -56,6 +56,12 @@ static void processEvent(Common::Event &event) { case Common::EVENT_RBUTTONDOWN: mouseRight = 1; break; + case Common::EVENT_LBUTTONUP: + mouseLeft = 0; + break; + case Common::EVENT_RBUTTONUP: + mouseRight = 0; + break; case Common::EVENT_MOUSEMOVE: break; case Common::EVENT_KEYDOWN: @@ -115,7 +121,7 @@ static void processEvent(Common::Event &event) { } break; case Common::KEYCODE_F10: - if (!disableSystemMenu && !inMenu) { + if (!inMenu) { g_cine->makeSystemMenu(); } break; @@ -174,19 +180,19 @@ static void processEvent(Common::Event &event) { case Common::KEYCODE_F11: renderer->showCollisionPage(false); break; - case Common::KEYCODE_KP5: // Emulated left mouse button click - case Common::KEYCODE_LEFT: // Left - case Common::KEYCODE_KP4: // Left + case Common::KEYCODE_KP5: // Emulated left mouse button click + case Common::KEYCODE_LEFT: // Left + case Common::KEYCODE_KP4: // Left case Common::KEYCODE_RIGHT: // Right - case Common::KEYCODE_KP6: // Right - case Common::KEYCODE_UP: // Up - case Common::KEYCODE_KP8: // Up - case Common::KEYCODE_DOWN: // Down - case Common::KEYCODE_KP2: // Down - case Common::KEYCODE_KP9: // Up & Right - case Common::KEYCODE_KP7: // Up & Left - case Common::KEYCODE_KP1: // Down & Left - case Common::KEYCODE_KP3: // Down & Right + case Common::KEYCODE_KP6: // Right + case Common::KEYCODE_UP: // Up + case Common::KEYCODE_KP8: // Up + case Common::KEYCODE_DOWN: // Down + case Common::KEYCODE_KP2: // Down + case Common::KEYCODE_KP9: // Up & Right + case Common::KEYCODE_KP7: // Up & Left + case Common::KEYCODE_KP1: // Down & Left + case Common::KEYCODE_KP3: // Down & Right // Stop ego movement made with keyboard when releasing a known key moveUsingKeyboard(0, 0); break; @@ -211,11 +217,8 @@ void manageEvents() { g_system->delayMillis(20); } while (g_system->getMillis() < nextFrame); - g_sound->update(); mouseData.left = mouseLeft; mouseData.right = mouseRight; - mouseLeft = 0; - mouseRight = 0; } void getMouseData(uint16 param, uint16 *pButton, uint16 *pX, uint16 *pY) { @@ -311,6 +314,7 @@ void CineEngine::mainLoop(int bootScriptIdx) { // HACK: Force amount of oxygen left to maximum during Operation Stealth's first arcade sequence. // This makes it possible to pass the arcade sequence for now. // FIXME: Remove the hack and make the first arcade sequence normally playable. + /* if (g_cine->getGameType() == Cine::GType_OS) { Common::String bgName(renderer->getBgName()); // Check if the background is one of the three backgrounds @@ -320,7 +324,7 @@ void CineEngine::mainLoop(int bootScriptIdx) { // Force the amount of oxygen left to the maximum. g_cine->_objectTable[oxygenObjNum].x = maxOxygen; } - } + }*/ // HACK: In Operation Stealth after the first arcade sequence jump player's position to avoid getting stuck. // After the first arcade sequence the player comes up stairs from @@ -379,8 +383,8 @@ void CineEngine::mainLoop(int bootScriptIdx) { playerAction = false; _messageLen <<= 3; - if (_messageLen < 0x800) - _messageLen = 0x800; + if (_messageLen < 800) + _messageLen = 800; do { manageEvents(); @@ -429,9 +433,9 @@ void CineEngine::mainLoop(int bootScriptIdx) { hideMouse(); g_sound->stopMusic(); - // if (g_cine->getGameType() == Cine::GType_OS) { + //if (g_cine->getGameType() == Cine::GType_OS) { // freeUnkList(); - // } + //} closePart(); } diff --git a/engines/cine/object.cpp b/engines/cine/object.cpp index afd95c04b0..a75828abb1 100644 --- a/engines/cine/object.cpp +++ b/engines/cine/object.cpp @@ -59,7 +59,7 @@ void loadObject(char *pObjectName) { assert(numEntry <= NUM_MAX_OBJECT); for (i = 0; i < numEntry; i++) { - if (g_cine->_objectTable[i].costume != -2 && g_cine->_objectTable[i].costume != -3) { // flag is keep ? + if (g_cine->_objectTable[i].costume != -2 && g_cine->_objectTable[i].costume != -3) { // flag is keep? Common::MemoryReadStream readS(ptr, entrySize); g_cine->_objectTable[i].x = readS.readSint16BE(); diff --git a/engines/cine/pal.cpp b/engines/cine/pal.cpp index 779c279ea1..10077ecdc9 100644 --- a/engines/cine/pal.cpp +++ b/engines/cine/pal.cpp @@ -92,7 +92,8 @@ void loadRelatedPalette(const char *fileName) { paletteIndex = findPaletteFromName(localName); if (paletteIndex == -1) { - for (i = 0; i < 16; i++) { // generate default palette + // generate default palette + for (i = 0; i < 16; i++) { paletteBuffer1[i] = paletteBuffer2[i] = (i << 4) + i; } } else { diff --git a/engines/cine/part.cpp b/engines/cine/part.cpp index 03cb743b46..813cbe50af 100644 --- a/engines/cine/part.cpp +++ b/engines/cine/part.cpp @@ -263,7 +263,7 @@ byte *readBundleSoundFile(const char *entryName, uint32 *size) { /** Rotate byte value to the left by n bits */ byte rolByte(byte value, uint n) { n %= 8; - return (byte) ((value << n) | (value >> (8 - n))); + return (byte)((value << n) | (value >> (8 - n))); } byte *readFile(const char *filename, bool crypted) { diff --git a/engines/cine/saveload.cpp b/engines/cine/saveload.cpp index 223099a587..51d2c1f6be 100644 --- a/engines/cine/saveload.cpp +++ b/engines/cine/saveload.cpp @@ -991,7 +991,7 @@ void CineEngine::makeSave(char *saveFileName) { * at a time. */ void loadResourcesFromSave(Common::SeekableReadStream &fHandle, enum CineSaveGameFormat saveGameFormat) { - int16 currentAnim, foundFileIdx; + int16 foundFileIdx; char *animName, part[256], name[10]; strcpy(part, currentPartName); @@ -1001,10 +1001,10 @@ void loadResourcesFromSave(Common::SeekableReadStream &fHandle, enum CineSaveGam const int entrySize = ((saveGameFormat == ANIMSIZE_23) ? 23 : 30); const int fileStartPos = fHandle.pos(); - currentAnim = 0; - while (currentAnim < NUM_MAX_ANIMDATA) { + + for (int resourceIndex = 0; resourceIndex < NUM_MAX_ANIMDATA; resourceIndex++) { // Seek to the start of the current animation's entry - fHandle.seek(fileStartPos + currentAnim * entrySize); + fHandle.seek(fileStartPos + resourceIndex * entrySize); // Read in the current animation entry fHandle.readUint16BE(); // width fHandle.readUint16BE(); @@ -1019,7 +1019,7 @@ void loadResourcesFromSave(Common::SeekableReadStream &fHandle, enum CineSaveGam } foundFileIdx = fHandle.readSint16BE(); - fHandle.readSint16BE(); // frame + int16 frameIndex = fHandle.readSint16BE(); // frame fHandle.read(name, 10); // Handle variables only present in animation entries of size 23 @@ -1029,7 +1029,7 @@ void loadResourcesFromSave(Common::SeekableReadStream &fHandle, enum CineSaveGam // Don't try to load invalid entries. if (foundFileIdx < 0 || !validPtr) { - currentAnim++; // Jump over the invalid entry + //resourceIndex++; // Jump over the invalid entry continue; } @@ -1041,9 +1041,7 @@ void loadResourcesFromSave(Common::SeekableReadStream &fHandle, enum CineSaveGam animName = g_cine->_partBuffer[foundFileIdx].partName; loadRelatedPalette(animName); // Is this for Future Wars only? - const int16 prevAnim = currentAnim; - currentAnim = loadResource(animName, currentAnim); - assert(currentAnim > prevAnim); // Make sure we advance forward + loadResource(animName, resourceIndex, frameIndex); } loadPart(part); diff --git a/engines/cine/saveload.h b/engines/cine/saveload.h index 49c9c0cef7..fd661904af 100644 --- a/engines/cine/saveload.h +++ b/engines/cine/saveload.h @@ -68,7 +68,7 @@ enum CineSaveGameFormat { }; /** Identifier for the temporary Operation Stealth savegame format. */ -static const uint32 TEMP_OS_FORMAT_ID = MKTAG('T','E','M','P'); +static const uint32 TEMP_OS_FORMAT_ID = MKTAG('T', 'E', 'M', 'P'); /** The current version number of Operation Stealth's savegame format. */ static const uint32 CURRENT_OS_SAVE_VER = 1; diff --git a/engines/cine/script.h b/engines/cine/script.h index 3fc86c585b..a07c8d6cfc 100644 --- a/engines/cine/script.h +++ b/engines/cine/script.h @@ -227,6 +227,7 @@ protected: int o1_op72(); int o1_op73(); int o1_playSample(); + int o1_playSampleSwapped(); int o1_disableSystemMenu(); int o1_loadMask5(); int o1_unloadMask5(); diff --git a/engines/cine/script_fw.cpp b/engines/cine/script_fw.cpp index 66150cc5b2..b4fe68c343 100644 --- a/engines/cine/script_fw.cpp +++ b/engines/cine/script_fw.cpp @@ -196,7 +196,7 @@ void FWScript::setupTable() { { 0, 0 }, { &FWScript::o1_playSample, "bbwbww" }, /* 78 */ - { &FWScript::o1_playSample, "bbwbww" }, + { &FWScript::o1_playSampleSwapped, "bbwbww" }, { &FWScript::o1_disableSystemMenu, "b" }, { &FWScript::o1_loadMask5, "b" }, { &FWScript::o1_unloadMask5, "b" } @@ -352,7 +352,7 @@ void ScriptVars::load(Common::SeekableReadStream &fHandle, unsigned int len) { * Reset all values to 0 */ void ScriptVars::reset() { - memset( _vars, 0, _size * sizeof(int16)); + memset(_vars, 0, _size * sizeof(int16)); } /** @@ -380,10 +380,10 @@ RawScript::RawScript(const FWScriptInfo &info, const byte *data, uint16 s) : * Copy constructor */ RawScript::RawScript(const RawScript &src) : _size(src._size), - _data(new byte[_size+1]), _labels(src._labels) { + _data(new byte[_size + 1]), _labels(src._labels) { assert(_data); - memcpy(_data, src._data, _size+1); + memcpy(_data, src._data, _size + 1); } /** @@ -398,7 +398,7 @@ RawScript::~RawScript() { */ RawScript &RawScript::operator=(const RawScript &src) { assert(src._data); - byte *tmp = new byte[src._size+1]; + byte *tmp = new byte[src._size + 1]; assert(tmp); _labels = src._labels; @@ -443,14 +443,14 @@ int RawScript::getNextLabel(const FWScriptInfo &info, int offset) const { pos += 2; break; case 'c': { // byte != 0 ? byte : word - uint8 test = _data[pos]; + uint8 test = _data[pos]; + pos++; + if (test) { pos++; - if (test) { - pos++; - } else { - pos += 2; - } + } else { + pos += 2; } + } break; case 'l': // label return pos; @@ -459,7 +459,7 @@ int RawScript::getNextLabel(const FWScriptInfo &info, int offset) const { ; break; case 'x': // exit script - return -pos-1; + return -pos - 1; } } } @@ -498,9 +498,7 @@ void RawScript::computeLabels(const FWScriptInfo &info) { * * computeScriptStackFromScript replacement */ -uint16 RawScript::getLabel(const FWScriptInfo &info, byte index, uint16 offset) - const { - +uint16 RawScript::getLabel(const FWScriptInfo &info, byte index, uint16 offset) const { assert(_data); int pos = offset; @@ -519,7 +517,7 @@ uint16 RawScript::getLabel(const FWScriptInfo &info, byte index, uint16 offset) */ void RawScript::setData(const FWScriptInfo &info, const byte *data) { assert(!_data); // this function should be called only once per instance - _data = new byte[_size+1]; + _data = new byte[_size + 1]; assert(data && _data); memcpy(_data, data, _size * sizeof(byte)); @@ -533,7 +531,6 @@ void RawScript::setData(const FWScriptInfo &info, const byte *data) { * @return Precalculated script labels */ const ScriptVars &RawScript::labels() const { - assert(_data); return _labels; } @@ -554,7 +551,7 @@ byte RawScript::getByte(unsigned int pos) const { * @return Word of bytecode */ uint16 RawScript::getWord(unsigned int pos) const { - assert(_data && pos+1 < _size); + assert(_data && pos + 1 < _size); return READ_BE_UINT16(_data + pos); } @@ -567,7 +564,7 @@ uint16 RawScript::getWord(unsigned int pos) const { const char *RawScript::getString(unsigned int pos) const { assert(_data && pos < _size); - return (const char*)(_data+pos); + return (const char *)(_data + pos); } /** @@ -581,8 +578,8 @@ const char *RawScript::getString(unsigned int pos) const { * instance can be used. It leaves the instance in partially invalid state. */ RawObjectScript::RawObjectScript(uint16 s, uint16 p1, uint16 p2, uint16 p3) - : RawScript(s), _runCount(0), _param1(p1), _param2(p2), _param3(p3) -{ } + : RawScript(s), _runCount(0), _param1(p1), _param2(p2), _param3(p3) { +} /** * Complete constructor @@ -593,8 +590,9 @@ RawObjectScript::RawObjectScript(uint16 s, uint16 p1, uint16 p2, uint16 p3) * @param p3 Third object script parameter */ RawObjectScript::RawObjectScript(const FWScriptInfo &info, const byte *data, - uint16 s, uint16 p1, uint16 p2, uint16 p3) : RawScript(info, data, s), - _runCount(0), _param1(p1), _param2(p2), _param3(p3) { } + uint16 s, uint16 p1, uint16 p2, uint16 p3) + : RawScript(info, data, s), _runCount(0), _param1(p1), _param2(p2), _param3(p3) { +} /** * Contructor for global scripts @@ -604,7 +602,8 @@ RawObjectScript::RawObjectScript(const FWScriptInfo &info, const byte *data, FWScript::FWScript(const RawScript &script, int16 idx) : _script(script), _pos(0), _line(0), _compare(0), _index(idx), _labels(script.labels()), _localVars(LOCAL_VARS_SIZE), - _globalVars(g_cine->_globalVars), _info(new FWScriptInfo) { } + _globalVars(g_cine->_globalVars), _info(new FWScriptInfo) { +} /** * Copy constructor @@ -612,25 +611,27 @@ FWScript::FWScript(const RawScript &script, int16 idx) : _script(script), FWScript::FWScript(const FWScript &src) : _script(src._script), _pos(src._pos), _line(src._line), _compare(src._compare), _index(src._index), _labels(src._labels), _localVars(src._localVars), - _globalVars(src._globalVars), _info(new FWScriptInfo) { } + _globalVars(src._globalVars), _info(new FWScriptInfo) { +} /** * Contructor for global scripts in derived classes * @param script Script bytecode reference * @param idx Script bytecode index */ -FWScript::FWScript(const RawScript &script, int16 idx, FWScriptInfo *info) : - _script(script), _pos(0), _line(0), _compare(0), _index(idx), +FWScript::FWScript(const RawScript &script, int16 idx, FWScriptInfo *info) + : _script(script), _pos(0), _line(0), _compare(0), _index(idx), _labels(script.labels()), _localVars(LOCAL_VARS_SIZE), - _globalVars(g_cine->_globalVars), _info(info) { } + _globalVars(g_cine->_globalVars), _info(info) { +} /** * Constructor for object scripts in derived classes * @param script Script bytecode reference * @param idx Script bytecode index */ -FWScript::FWScript(RawObjectScript &script, int16 idx, FWScriptInfo *info) : - _script(script), _pos(0), _line(0), _compare(0), _index(idx), +FWScript::FWScript(RawObjectScript &script, int16 idx, FWScriptInfo *info) + : _script(script), _pos(0), _line(0), _compare(0), _index(idx), _labels(script.labels()), _localVars(LOCAL_VARS_SIZE), _globalVars(g_cine->_globalVars), _info(info) { @@ -640,8 +641,8 @@ FWScript::FWScript(RawObjectScript &script, int16 idx, FWScriptInfo *info) : /** * Copy constructor for derived classes */ -FWScript::FWScript(const FWScript &src, FWScriptInfo *info) : - _script(src._script), _pos(src._pos), _line(src._line), +FWScript::FWScript(const FWScript &src, FWScriptInfo *info) + : _script(src._script), _pos(src._pos), _line(src._line), _compare(src._compare), _index(src._index), _labels(src._labels), _localVars(src._localVars), _globalVars(src._globalVars), _info(info) { } @@ -687,7 +688,7 @@ const char *FWScript::getNextString() { * @param pos Restored script position */ void FWScript::load(const ScriptVars &labels, const ScriptVars &local, uint16 compare, uint16 pos) { - assert(pos < _script._size); + assert(pos <= _script._size); _labels = labels; _localVars = local; _compare = compare; @@ -705,13 +706,15 @@ void FWScript::load(const ScriptVars &labels, const ScriptVars &local, uint16 co int FWScript::execute() { int ret = 0; - while (!ret) { - _line = _pos; - byte opcode = getNextByte(); - OpFunc handler = _info->opcodeHandler(opcode); + if (_script._size) { + while (!ret) { + _line = _pos; + byte opcode = getNextByte(); + OpFunc handler = _info->opcodeHandler(opcode); - if (handler) { - ret = (this->*handler)(); + if (handler) { + ret = (this->*handler)(); + } } } @@ -1815,6 +1818,9 @@ int FWScript::o1_playSample() { if (g_cine->getPlatform() == Common::kPlatformAmiga || g_cine->getPlatform() == Common::kPlatformAtariST) { if (size == 0xFFFF) { size = g_cine->_animDataTable[anim]._width * g_cine->_animDataTable[anim]._height; + } else if (size > g_cine->_animDataTable[anim]._width * g_cine->_animDataTable[anim]._height) { + warning("o1_playSample: Got invalid sample size %d for sample %d", size, anim); + size = g_cine->_animDataTable[anim]._width * g_cine->_animDataTable[anim]._height; } if (channel < 10) { // || _currentOpcode == 0x78 int channel1, channel2; @@ -1822,8 +1828,8 @@ int FWScript::o1_playSample() { channel1 = 0; channel2 = 1; } else { - channel1 = 2; - channel2 = 3; + channel1 = 3; + channel2 = 2; } g_sound->playSound(channel1, freq, data, size, -1, volume, 63, repeat); g_sound->playSound(channel2, freq, data, size, 1, volume, 0, repeat); @@ -1857,11 +1863,58 @@ int FWScript::o1_playSample() { return 0; } +int FWScript::o1_playSampleSwapped() { + // TODO: The DOS version probably does not have any stereo support here + // since the only stereo output it supports should be the Roland MT-32. + // So it probably does the same as o1_playSample here. Checking this will + // be a good idea never the less. + if (g_cine->getPlatform() == Common::kPlatformPC) { + return o1_playSample(); + } + + debugC(5, kCineDebugScript, "Line: %d: playSampleInversed()", _line); + + byte anim = getNextByte(); + byte channel = getNextByte(); + + uint16 freq = getNextWord(); + byte repeat = getNextByte(); + + int16 volume = getNextWord(); + uint16 size = getNextWord(); + + const byte *data = g_cine->_animDataTable[anim].data(); + + if (!data) { + return 0; + } + + if (size == 0xFFFF) { + size = g_cine->_animDataTable[anim]._width * g_cine->_animDataTable[anim]._height; + } else if (size > g_cine->_animDataTable[anim]._width * g_cine->_animDataTable[anim]._height) { + warning("o1_playSampleSwapped: Got invalid sample size %d for sample %d", size, anim); + size = g_cine->_animDataTable[anim]._width * g_cine->_animDataTable[anim]._height; + } + + int channel1, channel2; + if (channel == 0) { + channel1 = 1; + channel2 = 0; + } else { + channel1 = 2; + channel2 = 3; + } + + g_sound->playSound(channel1, freq, data, size, -1, volume, 63, repeat); + g_sound->playSound(channel2, freq, data, size, 1, volume, 0, repeat); + return 0; +} + int FWScript::o1_disableSystemMenu() { byte param = getNextByte(); debugC(5, kCineDebugScript, "Line: %d: disableSystemMenu(%d)", _line, param); - disableSystemMenu = (param != 0); + disableSystemMenu = param; return 0; } @@ -2073,1034 +2126,970 @@ void decompileScript(const byte *scriptPtr, uint16 scriptSize, uint16 scriptIdx) strcpy(lineBuffer, ""); switch (opcode - 1) { - case -1: - { - break; - } - case 0x0: - { - byte param1; - byte param2; - int16 param3; + case -1: { + break; + } + case 0x0: { + byte param1; + byte param2; + int16 param3; - param1 = *(localScriptPtr + position); - position++; + param1 = *(localScriptPtr + position); + position++; - param2 = *(localScriptPtr + position); - position++; + param2 = *(localScriptPtr + position); + position++; - param3 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param3 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - sprintf(lineBuffer, "obj[%d]%s = %d\n", param1, getObjPramName(param2), param3); + sprintf(lineBuffer, "obj[%d]%s = %d\n", param1, getObjPramName(param2), param3); - break; - } - case 0x1: - { - byte param1; - byte param2; - byte param3; + break; + } + case 0x1: { + byte param1; + byte param2; + byte param3; - param1 = *(localScriptPtr + position); - position++; + param1 = *(localScriptPtr + position); + position++; - param2 = *(localScriptPtr + position); - position++; + param2 = *(localScriptPtr + position); + position++; - param3 = *(localScriptPtr + position); - position++; + param3 = *(localScriptPtr + position); + position++; - sprintf(lineBuffer, "var[%d]=obj[%d]%s\n", param3, param1, getObjPramName(param2)); - break; - } + sprintf(lineBuffer, "var[%d]=obj[%d]%s\n", param3, param1, getObjPramName(param2)); + break; + } case 0x2: case 0x3: case 0x4: case 0x5: - case 0x6: - { - byte param1; - byte param2; - int16 param3; - - param1 = *(localScriptPtr + position); - position++; - - param2 = *(localScriptPtr + position); - position++; - - param3 = READ_BE_UINT16(localScriptPtr + position); - position += 2; - - if (opcode - 1 == 0x2) { - sprintf(lineBuffer, "obj[%d]%s+=%d\n", param1, getObjPramName(param2), param3); - } else if (opcode - 1 == 0x3) { - sprintf(lineBuffer, "obj[%d]%s-=%d\n", param1, getObjPramName(param2), param3); - } else if (opcode - 1 == 0x4) { - sprintf(lineBuffer, "obj[%d]%s+=obj[%d]%s\n", param1, getObjPramName(param2), param3, getObjPramName(param2)); - } else if (opcode - 1 == 0x5) { - sprintf(lineBuffer, "obj[%d]%s-=obj[%d]%s\n", param1, getObjPramName(param2), param3, getObjPramName(param2)); - } else if (opcode - 1 == 0x6) { - sprintf(compareString1, "obj[%d]%s", param1, getObjPramName(param2)); - sprintf(compareString2, "%d", param3); - } - break; + case 0x6: { + byte param1; + byte param2; + int16 param3; + + param1 = *(localScriptPtr + position); + position++; + + param2 = *(localScriptPtr + position); + position++; + + param3 = READ_BE_UINT16(localScriptPtr + position); + position += 2; + + if (opcode - 1 == 0x2) { + sprintf(lineBuffer, "obj[%d]%s+=%d\n", param1, getObjPramName(param2), param3); + } else if (opcode - 1 == 0x3) { + sprintf(lineBuffer, "obj[%d]%s-=%d\n", param1, getObjPramName(param2), param3); + } else if (opcode - 1 == 0x4) { + sprintf(lineBuffer, "obj[%d]%s+=obj[%d]%s\n", param1, getObjPramName(param2), param3, getObjPramName(param2)); + } else if (opcode - 1 == 0x5) { + sprintf(lineBuffer, "obj[%d]%s-=obj[%d]%s\n", param1, getObjPramName(param2), param3, getObjPramName(param2)); + } else if (opcode - 1 == 0x6) { + sprintf(compareString1, "obj[%d]%s", param1, getObjPramName(param2)); + sprintf(compareString2, "%d", param3); } + break; + } case 0x7: - case 0x8: - { - byte param1; - int16 param2; - int16 param3; - int16 param4; - int16 param5; + case 0x8: { + byte param1; + int16 param2; + int16 param3; + int16 param4; + int16 param5; - param1 = *(localScriptPtr + position); - position++; + param1 = *(localScriptPtr + position); + position++; - param2 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param2 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - param3 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param3 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - param4 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param4 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - param5 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param5 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - if (opcode - 1 == 0x7) { - sprintf(lineBuffer, "setupObject(Idx:%d,X:%d,Y:%d,mask:%d,frame:%d)\n", param1, param2, param3, param4, param5); - } else if (opcode - 1 == 0x8) { - sprintf(lineBuffer, "checkCollision(%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5); - } - break; + if (opcode - 1 == 0x7) { + sprintf(lineBuffer, "setupObject(Idx:%d,X:%d,Y:%d,mask:%d,frame:%d)\n", param1, param2, param3, param4, param5); + } else if (opcode - 1 == 0x8) { + sprintf(lineBuffer, "checkCollision(%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5); } - case 0x9: - { - byte param1; - int16 param2; + break; + } + case 0x9: { + byte param1; + int16 param2; - param1 = *(localScriptPtr + position); - position++; + param1 = *(localScriptPtr + position); + position++; - param2 = *(localScriptPtr + position); - position++; + param2 = *(localScriptPtr + position); + position++; - if (param2) { - byte param3; - - param3 = *(localScriptPtr + position); - position++; - - if (param2 == 1) { - sprintf(lineBuffer, "var[%d]=var[%d]\n", param1, param3); - } else if (param2 == 2) { - sprintf(lineBuffer, "var[%d]=globalVar[%d]\n", param1, param3); - } else if (param2 == 3) { - sprintf(lineBuffer, "var[%d]=mouse.X\n", param1); - } else if (param2 == 4) { - sprintf(lineBuffer, "var[%d]=mouse.Y\n", param1); - } else if (param2 == 5) { - sprintf(lineBuffer, "var[%d]=rand() mod %d\n", param1, param3); - } else if (param2 == 8) { - sprintf(lineBuffer, "var[%d]=file[%d].packedSize\n", param1, param3); - } else if (param2 == 9) { - sprintf(lineBuffer, "var[%d]=file[%d].unpackedSize\n", param1, param3); - } else { - error("decompileScript: 0x09: param2 = %d", param2); - } - } else { - int16 param3; + if (param2) { + byte param3; - param3 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param3 = *(localScriptPtr + position); + position++; - sprintf(lineBuffer, "var[%d]=%d\n", param1, param3); + if (param2 == 1) { + sprintf(lineBuffer, "var[%d]=var[%d]\n", param1, param3); + } else if (param2 == 2) { + sprintf(lineBuffer, "var[%d]=globalVar[%d]\n", param1, param3); + } else if (param2 == 3) { + sprintf(lineBuffer, "var[%d]=mouse.X\n", param1); + } else if (param2 == 4) { + sprintf(lineBuffer, "var[%d]=mouse.Y\n", param1); + } else if (param2 == 5) { + sprintf(lineBuffer, "var[%d]=rand() mod %d\n", param1, param3); + } else if (param2 == 8) { + sprintf(lineBuffer, "var[%d]=file[%d].packedSize\n", param1, param3); + } else if (param2 == 9) { + sprintf(lineBuffer, "var[%d]=file[%d].unpackedSize\n", param1, param3); + } else { + error("decompileScript: 0x09: param2 = %d", param2); } + } else { + int16 param3; - break; + param3 = READ_BE_UINT16(localScriptPtr + position); + position += 2; + + sprintf(lineBuffer, "var[%d]=%d\n", param1, param3); } + + break; + } case 0xA: case 0xB: case 0xC: - case 0xD: - { - byte param1; - byte param2; + case 0xD: { + byte param1; + byte param2; - param1 = *(localScriptPtr + position); - position++; + param1 = *(localScriptPtr + position); + position++; - param2 = *(localScriptPtr + position); + param2 = *(localScriptPtr + position); + position++; + + if (param2) { + byte param3; + + param3 = *(localScriptPtr + position); position++; - if (param2) { - byte param3; + if (opcode - 1 == 0xA) { + sprintf(lineBuffer, "var[%d]+=var[%d]\n", param1, param3); + } else if (opcode - 1 == 0xB) { + sprintf(lineBuffer, "var[%d]-=var[%d]\n", param1, param3); + } else if (opcode - 1 == 0xC) { + sprintf(lineBuffer, "var[%d]*=var[%d]\n", param1, param3); + } else if (opcode - 1 == 0xD) { + sprintf(lineBuffer, "var[%d]/=var[%d]\n", param1, param3); + } + } else { + int16 param3; - param3 = *(localScriptPtr + position); - position++; + param3 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - if (opcode - 1 == 0xA) { - sprintf(lineBuffer, "var[%d]+=var[%d]\n", param1, param3); - } else if (opcode - 1 == 0xB) { - sprintf(lineBuffer, "var[%d]-=var[%d]\n", param1, param3); - } else if (opcode - 1 == 0xC) { - sprintf(lineBuffer, "var[%d]*=var[%d]\n", param1, param3); - } else if (opcode - 1 == 0xD) { - sprintf(lineBuffer, "var[%d]/=var[%d]\n", param1, param3); - } - } else { - int16 param3; - - param3 = READ_BE_UINT16(localScriptPtr + position); - position += 2; - - if (opcode - 1 == 0xA) { - sprintf(lineBuffer, "var[%d]+=%d\n", param1, param3); - } else if (opcode - 1 == 0xB) { - sprintf(lineBuffer, "var[%d]-=%d\n", param1, param3); - } else if (opcode - 1 == 0xC) { - sprintf(lineBuffer, "var[%d]*=%d\n", param1, param3); - } else if (opcode - 1 == 0xD) { - sprintf(lineBuffer, "var[%d]/=%d\n", param1, param3); - } + if (opcode - 1 == 0xA) { + sprintf(lineBuffer, "var[%d]+=%d\n", param1, param3); + } else if (opcode - 1 == 0xB) { + sprintf(lineBuffer, "var[%d]-=%d\n", param1, param3); + } else if (opcode - 1 == 0xC) { + sprintf(lineBuffer, "var[%d]*=%d\n", param1, param3); + } else if (opcode - 1 == 0xD) { + sprintf(lineBuffer, "var[%d]/=%d\n", param1, param3); } - break; } - case 0xE: - { - byte param1; - byte param2; + break; + } + case 0xE: { + byte param1; + byte param2; - param1 = *(localScriptPtr + position); - position++; + param1 = *(localScriptPtr + position); + position++; - param2 = *(localScriptPtr + position); - position++; + param2 = *(localScriptPtr + position); + position++; - if (param2) { - byte param3; + if (param2) { + byte param3; - param3 = *(localScriptPtr + position); - position++; + param3 = *(localScriptPtr + position); + position++; - if (param2 == 1) { - sprintf(compareString1, "var[%d]", param1); - sprintf(compareString2, "var[%d]", param3); + if (param2 == 1) { + sprintf(compareString1, "var[%d]", param1); + sprintf(compareString2, "var[%d]", param3); - } else if (param2 == 2) { - sprintf(compareString1, "var[%d]", param1); - sprintf(compareString2, "globalVar[%d]", param3); - } else { - error("decompileScript: 0x0E: param2 = %d", param2); - } + } else if (param2 == 2) { + sprintf(compareString1, "var[%d]", param1); + sprintf(compareString2, "globalVar[%d]", param3); } else { - int16 param3; + error("decompileScript: 0x0E: param2 = %d", param2); + } + } else { + int16 param3; - param3 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param3 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - sprintf(compareString1, "var[%d]", param1); - sprintf(compareString2, "%d", param3); - } - break; + sprintf(compareString1, "var[%d]", param1); + sprintf(compareString2, "%d", param3); } - case 0xF: - { - byte param1; - byte param2; - byte param3; + break; + } + case 0xF: { + byte param1; + byte param2; + byte param3; - param1 = *(localScriptPtr + position); - position++; + param1 = *(localScriptPtr + position); + position++; - param2 = *(localScriptPtr + position); - position++; + param2 = *(localScriptPtr + position); + position++; - param3 = *(localScriptPtr + position); - position++; + param3 = *(localScriptPtr + position); + position++; - sprintf(lineBuffer, "obj[%d]%s=var[%d]\n", param1, getObjPramName(param2), param3); + sprintf(lineBuffer, "obj[%d]%s=var[%d]\n", param1, getObjPramName(param2), param3); - break; - } + break; + } case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: case 0x18: - case 0x19: - { - byte param; - - param = *(localScriptPtr + position); - position++; - - if (opcode - 1 == 0x13) { - sprintf(lineBuffer, "loadMask0(%d)\n", param); - } else if (opcode - 1 == 0x14) { - sprintf(lineBuffer, "unloadMask0(%d)\n", param); - } else if (opcode - 1 == 0x15) { - sprintf(lineBuffer, "OP_15(%d)\n", param); - } else if (opcode - 1 == 0x16) { - sprintf(lineBuffer, "loadMask1(%d)\n", param); - } else if (opcode - 1 == 0x17) { - sprintf(lineBuffer, "unloadMask0(%d)\n", param); - } else if (opcode - 1 == 0x18) { - sprintf(lineBuffer, "loadMask4(%d)\n", param); - } else if (opcode - 1 == 0x19) { - sprintf(lineBuffer, "unloadMask4(%d)\n", param); - } - break; + case 0x19: { + byte param; + + param = *(localScriptPtr + position); + position++; + + if (opcode - 1 == 0x13) { + sprintf(lineBuffer, "loadMask0(%d)\n", param); + } else if (opcode - 1 == 0x14) { + sprintf(lineBuffer, "unloadMask0(%d)\n", param); + } else if (opcode - 1 == 0x15) { + sprintf(lineBuffer, "OP_15(%d)\n", param); + } else if (opcode - 1 == 0x16) { + sprintf(lineBuffer, "loadMask1(%d)\n", param); + } else if (opcode - 1 == 0x17) { + sprintf(lineBuffer, "unloadMask0(%d)\n", param); + } else if (opcode - 1 == 0x18) { + sprintf(lineBuffer, "loadMask4(%d)\n", param); + } else if (opcode - 1 == 0x19) { + sprintf(lineBuffer, "unloadMask4(%d)\n", param); } - case 0x1A: - { - byte param; + break; + } + case 0x1A: { + byte param; - param = *(localScriptPtr + position); - position++; + param = *(localScriptPtr + position); + position++; - sprintf(lineBuffer, "OP_1A(%d)\n", param); + sprintf(lineBuffer, "OP_1A(%d)\n", param); - break; - } - case 0x1B: - { - sprintf(lineBuffer, "bgIncrustList.clear()\n"); - break; - } - case 0x1D: - { - byte param; + break; + } + case 0x1B: { + sprintf(lineBuffer, "bgIncrustList.clear()\n"); + break; + } + case 0x1D: { + byte param; - param = *(localScriptPtr + position); - position++; + param = *(localScriptPtr + position); + position++; - sprintf(lineBuffer, "label(%d)\n", param); + sprintf(lineBuffer, "label(%d)\n", param); - break; - } - case 0x1E: - { - byte param; + break; + } + case 0x1E: { + byte param; - param = *(localScriptPtr + position); - position++; + param = *(localScriptPtr + position); + position++; - sprintf(lineBuffer, "goto(%d)\n", param); + sprintf(lineBuffer, "goto(%d)\n", param); - break; - } + break; + } // If cases case 0x1F: case 0x20: case 0x21: case 0x22: case 0x23: - case 0x24: - { - byte param; - - param = *(localScriptPtr + position); - position++; - - if (opcode - 1 == 0x1F) { - sprintf(lineBuffer, "if(%s>%s) goto(%d)\n", compareString1, compareString2, param); - } else if (opcode - 1 == 0x20) { - sprintf(lineBuffer, "if(%s>=%s) goto(%d)\n", compareString1, compareString2, param); - } else if (opcode - 1 == 0x21) { - sprintf(lineBuffer, "if(%s<%s) goto(%d)\n", compareString1, compareString2, param); - } else if (opcode - 1 == 0x22) { - sprintf(lineBuffer, "if(%s<=%s) goto(%d)\n", compareString1, compareString2, param); - } else if (opcode - 1 == 0x23) { - sprintf(lineBuffer, "if(%s==%s) goto(%d)\n", compareString1, compareString2, param); - } else if (opcode - 1 == 0x24) { - sprintf(lineBuffer, "if(%s!=%s) goto(%d)\n", compareString1, compareString2, param); - } - break; + case 0x24: { + byte param; + + param = *(localScriptPtr + position); + position++; + + if (opcode - 1 == 0x1F) { + sprintf(lineBuffer, "if(%s>%s) goto(%d)\n", compareString1, compareString2, param); + } else if (opcode - 1 == 0x20) { + sprintf(lineBuffer, "if(%s>=%s) goto(%d)\n", compareString1, compareString2, param); + } else if (opcode - 1 == 0x21) { + sprintf(lineBuffer, "if(%s<%s) goto(%d)\n", compareString1, compareString2, param); + } else if (opcode - 1 == 0x22) { + sprintf(lineBuffer, "if(%s<=%s) goto(%d)\n", compareString1, compareString2, param); + } else if (opcode - 1 == 0x23) { + sprintf(lineBuffer, "if(%s==%s) goto(%d)\n", compareString1, compareString2, param); + } else if (opcode - 1 == 0x24) { + sprintf(lineBuffer, "if(%s!=%s) goto(%d)\n", compareString1, compareString2, param); } - case 0x25: - { - byte param; + break; + } + case 0x25: { + byte param; - param = *(localScriptPtr + position); - position++; + param = *(localScriptPtr + position); + position++; - sprintf(lineBuffer, "removeLabel(%d)\n", param); + sprintf(lineBuffer, "removeLabel(%d)\n", param); - break; - } - case 0x26: - { - byte param1; - byte param2; + break; + } + case 0x26: { + byte param1; + byte param2; - param1 = *(localScriptPtr + position); - position++; - param2 = *(localScriptPtr + position); - position++; + param1 = *(localScriptPtr + position); + position++; + param2 = *(localScriptPtr + position); + position++; - sprintf(lineBuffer, "loop(--var[%d]) -> label(%d)\n", param1, param2); + sprintf(lineBuffer, "loop(--var[%d]) -> label(%d)\n", param1, param2); - break; - } + break; + } case 0x31: - case 0x32: - { - byte param; + case 0x32: { + byte param; - param = *(localScriptPtr + position); - position++; + param = *(localScriptPtr + position); + position++; - if (opcode - 1 == 0x31) { - sprintf(lineBuffer, "startGlobalScript(%d)\n", param); - } else if (opcode - 1 == 0x32) { - sprintf(lineBuffer, "endGlobalScript(%d)\n", param); - } - break; + if (opcode - 1 == 0x31) { + sprintf(lineBuffer, "startGlobalScript(%d)\n", param); + } else if (opcode - 1 == 0x32) { + sprintf(lineBuffer, "endGlobalScript(%d)\n", param); } + break; + } case 0x3B: case 0x3C: case 0x3D: - case OP_loadPart: - { - if (opcode - 1 == 0x3B) { - sprintf(lineBuffer, "loadResource(%s)\n", localScriptPtr + position); - } else if (opcode - 1 == 0x3C) { - sprintf(lineBuffer, "loadBg(%s)\n", localScriptPtr + position); - } else if (opcode - 1 == 0x3D) { - sprintf(lineBuffer, "loadCt(%s)\n", localScriptPtr + position); - } else if (opcode - 1 == OP_loadPart) { - sprintf(lineBuffer, "loadPart(%s)\n", localScriptPtr + position); - } - - position += strlen((const char *)localScriptPtr + position) + 1; - break; - } - case 0x40: - { - sprintf(lineBuffer, "closePart()\n"); - break; - } - case OP_loadNewPrcName: - { - byte param; + case OP_loadPart: { + if (opcode - 1 == 0x3B) { + sprintf(lineBuffer, "loadResource(%s)\n", localScriptPtr + position); + } else if (opcode - 1 == 0x3C) { + sprintf(lineBuffer, "loadBg(%s)\n", localScriptPtr + position); + } else if (opcode - 1 == 0x3D) { + sprintf(lineBuffer, "loadCt(%s)\n", localScriptPtr + position); + } else if (opcode - 1 == OP_loadPart) { + sprintf(lineBuffer, "loadPart(%s)\n", localScriptPtr + position); + } + + position += strlen((const char *)localScriptPtr + position) + 1; + break; + } + case 0x40: { + sprintf(lineBuffer, "closePart()\n"); + break; + } + case OP_loadNewPrcName: { + byte param; - param = *(localScriptPtr + position); - position++; + param = *(localScriptPtr + position); + position++; - sprintf(lineBuffer, "loadPrc(%d,%s)\n", param, localScriptPtr + position); + sprintf(lineBuffer, "loadPrc(%d,%s)\n", param, localScriptPtr + position); - position += strlen((const char *)localScriptPtr + position) + 1; - break; - } - case OP_requestCheckPendingDataLoad: // nop - { - sprintf(lineBuffer, "requestCheckPendingDataLoad()\n"); - break; - } - case 0x45: - { - sprintf(lineBuffer, "blitAndFade()\n"); - break; - } - case 0x46: - { - sprintf(lineBuffer, "fadeToBlack()\n"); - break; - } - case 0x47: - { - byte param1; - byte param2; - int16 param3; - int16 param4; - int16 param5; + position += strlen((const char *)localScriptPtr + position) + 1; + break; + } + case OP_requestCheckPendingDataLoad: { // nop + sprintf(lineBuffer, "requestCheckPendingDataLoad()\n"); + break; + } + case 0x45: { + sprintf(lineBuffer, "blitAndFade()\n"); + break; + } + case 0x46: { + sprintf(lineBuffer, "fadeToBlack()\n"); + break; + } + case 0x47: { + byte param1; + byte param2; + int16 param3; + int16 param4; + int16 param5; - param1 = *(localScriptPtr + position); - position++; + param1 = *(localScriptPtr + position); + position++; - param2 = *(localScriptPtr + position); - position++; + param2 = *(localScriptPtr + position); + position++; - param3 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param3 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - param4 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param4 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - param5 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param5 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - sprintf(lineBuffer, "transformPaletteRange(%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5); + sprintf(lineBuffer, "transformPaletteRange(%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5); - break; - } - case 0x49: - { - byte param; + break; + } + case 0x49: { + byte param; - param = *(localScriptPtr + position); - position++; + param = *(localScriptPtr + position); + position++; - sprintf(lineBuffer, "setDefaultMenuBgColor(%d)\n", param); + sprintf(lineBuffer, "setDefaultMenuBgColor(%d)\n", param); - break; - } - case 0x4F: - { - sprintf(lineBuffer, "break()\n"); - exitScript = 1; - break; - } - case 0x50: - { - sprintf(lineBuffer, "endScript()\n\n"); - break; - } - case 0x51: - { - byte param1; - int16 param2; - int16 param3; - int16 param4; - int16 param5; + break; + } + case 0x4F: { + sprintf(lineBuffer, "break()\n"); + exitScript = 1; + break; + } + case 0x50: { + sprintf(lineBuffer, "endScript()\n\n"); + break; + } + case 0x51: { + byte param1; + int16 param2; + int16 param3; + int16 param4; + int16 param5; - param1 = *(localScriptPtr + position); - position++; + param1 = *(localScriptPtr + position); + position++; - param2 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param2 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - param3 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param3 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - param4 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param4 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - param5 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param5 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - sprintf(lineBuffer, "message(%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5); + sprintf(lineBuffer, "message(%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5); - break; - } + break; + } case 0x52: - case 0x53: - { - byte param1; - byte param2; + case 0x53: { + byte param1; + byte param2; - param1 = *(localScriptPtr + position); - position++; + param1 = *(localScriptPtr + position); + position++; - param2 = *(localScriptPtr + position); - position++; + param2 = *(localScriptPtr + position); + position++; - if (param2) { - byte param3; - - param3 = *(localScriptPtr + position); - position++; - - if (param2 == 1) { - if (opcode - 1 == 0x52) { - sprintf(lineBuffer, "globalVar[%d] = var[%d]\n", param1, param3); - } else if (opcode - 1 == 0x53) { - sprintf(compareString1, "globalVar[%d]", param1); - sprintf(compareString2, "var[%d]", param3); - } - } else if (param2 == 2) { - if (opcode - 1 == 0x52) { - sprintf(lineBuffer, "globalVar[%d] = globalVar[%d]\n", param1, param3); - } else if (opcode - 1 == 0x53) { - sprintf(compareString1, "globalVar[%d]", param1); - sprintf(compareString2, "globalVar[%d]", param3); - } - } else { - if (opcode - 1 == 0x52) { - error("decompileScript: 0x52: param2 = %d", param2); - } else if (opcode - 1 == 0x53) { - error("decompileScript: 0x53: param2 = %d", param2); - } - } - } else { - int16 param3; + if (param2) { + byte param3; - param3 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param3 = *(localScriptPtr + position); + position++; + if (param2 == 1) { if (opcode - 1 == 0x52) { - sprintf(lineBuffer, "globalVar[%d] = %d\n", param1, param3); + sprintf(lineBuffer, "globalVar[%d] = var[%d]\n", param1, param3); } else if (opcode - 1 == 0x53) { sprintf(compareString1, "globalVar[%d]", param1); - sprintf(compareString2, "%d", param3); + sprintf(compareString2, "var[%d]", param3); + } + } else if (param2 == 2) { + if (opcode - 1 == 0x52) { + sprintf(lineBuffer, "globalVar[%d] = globalVar[%d]\n", param1, param3); + } else if (opcode - 1 == 0x53) { + sprintf(compareString1, "globalVar[%d]", param1); + sprintf(compareString2, "globalVar[%d]", param3); + } + } else { + if (opcode - 1 == 0x52) { + error("decompileScript: 0x52: param2 = %d", param2); + } else if (opcode - 1 == 0x53) { + error("decompileScript: 0x53: param2 = %d", param2); } } - break; - } - case 0x59: - { - sprintf(lineBuffer, "comment: %s\n", localScriptPtr + position); + } else { + int16 param3; - position += strlen((const char *)localScriptPtr + position); - break; + param3 = READ_BE_UINT16(localScriptPtr + position); + position += 2; + + if (opcode - 1 == 0x52) { + sprintf(lineBuffer, "globalVar[%d] = %d\n", param1, param3); + } else if (opcode - 1 == 0x53) { + sprintf(compareString1, "globalVar[%d]", param1); + sprintf(compareString2, "%d", param3); + } } - case 0x5A: - { - byte param1; - byte param2; + break; + } + case 0x59: { + sprintf(lineBuffer, "comment: %s\n", localScriptPtr + position); - param1 = *(localScriptPtr + position); - position++; + position += strlen((const char *)localScriptPtr + position); + break; + } + case 0x5A: { + byte param1; + byte param2; - param2 = *(localScriptPtr + position); - position++; + param1 = *(localScriptPtr + position); + position++; - sprintf(lineBuffer, "freePartRang(%d,%d)\n", param1, param2); + param2 = *(localScriptPtr + position); + position++; - break; - } - case 0x5B: - { - sprintf(lineBuffer, "unloadAllMasks()\n"); - break; - } - case 0x65: - { - sprintf(lineBuffer, "setupTableUnk1()\n"); - break; - } - case 0x66: - { - byte param1; - int16 param2; + sprintf(lineBuffer, "freePartRang(%d,%d)\n", param1, param2); - param1 = *(localScriptPtr + position); - position++; + break; + } + case 0x5B: { + sprintf(lineBuffer, "unloadAllMasks()\n"); + break; + } + case 0x65: { + sprintf(lineBuffer, "setupTableUnk1()\n"); + break; + } + case 0x66: { + byte param1; + int16 param2; - param2 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param1 = *(localScriptPtr + position); + position++; - sprintf(lineBuffer, "tableUnk1[%d] = %d\n", param1, param2); + param2 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - break; - } - case 0x68: - { - byte param; + sprintf(lineBuffer, "tableUnk1[%d] = %d\n", param1, param2); - param = *(localScriptPtr + position); - position++; + break; + } + case 0x68: { + byte param; - sprintf(lineBuffer, "setPlayerCommandPosY(%d)\n", param); + param = *(localScriptPtr + position); + position++; - break; - } - case 0x69: - { - sprintf(lineBuffer, "allowPlayerInput()\n"); - break; - } - case 0x6A: - { - sprintf(lineBuffer, "disallowPlayerInput()\n"); - break; - } - case 0x6B: - { - byte newDisk; + sprintf(lineBuffer, "setPlayerCommandPosY(%d)\n", param); - newDisk = *(localScriptPtr + position); - position++; + break; + } + case 0x69: { + sprintf(lineBuffer, "allowPlayerInput()\n"); + break; + } + case 0x6A: { + sprintf(lineBuffer, "disallowPlayerInput()\n"); + break; + } + case 0x6B: { + byte newDisk; - sprintf(lineBuffer, "changeDataDisk(%d)\n", newDisk); + newDisk = *(localScriptPtr + position); + position++; - break; - } - case 0x6D: - { - sprintf(lineBuffer, "loadDat(%s)\n", localScriptPtr + position); + sprintf(lineBuffer, "changeDataDisk(%d)\n", newDisk); - position += strlen((const char *)localScriptPtr + position) + 1; - break; - } - case 0x6E: // nop - { - sprintf(lineBuffer, "updateDat()\n"); - break; - } - case 0x6F: - { - sprintf(lineBuffer, "OP_6F() -> dat related\n"); - break; - } - case 0x70: - { - sprintf(lineBuffer, "stopSample()\n"); - break; - } - case 0x79: - { - byte param; + break; + } + case 0x6D: { + sprintf(lineBuffer, "loadDat(%s)\n", localScriptPtr + position); - param = *(localScriptPtr + position); - position++; + position += strlen((const char *)localScriptPtr + position) + 1; + break; + } + case 0x6E: { // nop + sprintf(lineBuffer, "updateDat()\n"); + break; + } + case 0x6F: { + sprintf(lineBuffer, "OP_6F() -> dat related\n"); + break; + } + case 0x70: { + sprintf(lineBuffer, "stopSample()\n"); + break; + } + case 0x79: { + byte param; - sprintf(lineBuffer, "disableSystemMenu(%d)\n", param); + param = *(localScriptPtr + position); + position++; - break; - } + sprintf(lineBuffer, "disableSystemMenu(%d)\n", param); + + break; + } case 0x77: - case 0x78: - { - byte param1; - byte param2; - int16 param3; - byte param4; - int16 param5; - int16 param6; + case 0x78: { + byte param1; + byte param2; + int16 param3; + byte param4; + int16 param5; + int16 param6; - param1 = *(localScriptPtr + position); - position++; + param1 = *(localScriptPtr + position); + position++; - param2 = *(localScriptPtr + position); - position++; + param2 = *(localScriptPtr + position); + position++; - param3 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param3 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - param4 = *(localScriptPtr + position); - position++; + param4 = *(localScriptPtr + position); + position++; - param5 = READ_BE_UINT16(localScriptPtr + position); - position += 2; - - param6 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param5 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - if (opcode - 1 == 0x77) { - sprintf(lineBuffer, "playSample(%d,%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5, param6); - } else if (opcode - 1 == 0x78) { - sprintf(lineBuffer, "OP_78(%d,%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5, param6); - } + param6 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - break; + if (opcode - 1 == 0x77) { + sprintf(lineBuffer, "playSample(%d,%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5, param6); + } else if (opcode - 1 == 0x78) { + sprintf(lineBuffer, "playSampleSwapped(%d,%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5, param6); } - case 0x7A: - { - byte param; - param = *(localScriptPtr + position); - position++; + break; + } + case 0x7A: { + byte param; - sprintf(lineBuffer, "OP_7A(%d)\n", param); + param = *(localScriptPtr + position); + position++; - break; - } - case 0x7B: // OS only - { - byte param; + sprintf(lineBuffer, "OP_7A(%d)\n", param); - param = *(localScriptPtr + position); - position++; + break; + } + case 0x7B: { // OS only + byte param; - sprintf(lineBuffer, "OP_7B(%d)\n", param); + param = *(localScriptPtr + position); + position++; - break; - } - case 0x7F: // OS only - { - byte param1; - byte param2; - byte param3; - byte param4; - int16 param5; - int16 param6; - int16 param7; + sprintf(lineBuffer, "OP_7B(%d)\n", param); - param1 = *(localScriptPtr + position); - position++; + break; + } + case 0x7F: { // OS only + byte param1; + byte param2; + byte param3; + byte param4; + int16 param5; + int16 param6; + int16 param7; - param2 = *(localScriptPtr + position); - position++; + param1 = *(localScriptPtr + position); + position++; - param3 = *(localScriptPtr + position); - position++; + param2 = *(localScriptPtr + position); + position++; - param4 = *(localScriptPtr + position); - position++; + param3 = *(localScriptPtr + position); + position++; - param5 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param4 = *(localScriptPtr + position); + position++; - param6 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param5 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - param7 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param6 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - sprintf(lineBuffer, "OP_7F(%d,%d,%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5, param6, param7); + param7 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - break; - } - case 0x80: // OS only - { - byte param1; - byte param2; + sprintf(lineBuffer, "OP_7F(%d,%d,%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5, param6, param7); - param1 = *(localScriptPtr + position); - position++; + break; + } + case 0x80: { // OS only + byte param1; + byte param2; - param2 = *(localScriptPtr + position); - position++; + param1 = *(localScriptPtr + position); + position++; - sprintf(lineBuffer, "OP_80(%d,%d)\n", param1, param2); + param2 = *(localScriptPtr + position); + position++; - break; - } - case 0x82: // OS only - { - byte param1; - byte param2; - uint16 param3; - uint16 param4; - byte param5; + sprintf(lineBuffer, "OP_80(%d,%d)\n", param1, param2); - param1 = *(localScriptPtr + position); - position++; + break; + } + case 0x82: { // OS only + byte param1; + byte param2; + uint16 param3; + uint16 param4; + byte param5; - param2 = *(localScriptPtr + position); - position++; + param1 = *(localScriptPtr + position); + position++; - param3 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param2 = *(localScriptPtr + position); + position++; - param4 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param3 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - param5 = *(localScriptPtr + position); - position++; + param4 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - sprintf(lineBuffer, "OP_82(%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5); + param5 = *(localScriptPtr + position); + position++; - break; - } - case 0x83: // OS only - { - byte param1; - byte param2; + sprintf(lineBuffer, "OP_82(%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5); - param1 = *(localScriptPtr + position); - position++; + break; + } + case 0x83: { // OS only + byte param1; + byte param2; - param2 = *(localScriptPtr + position); - position++; + param1 = *(localScriptPtr + position); + position++; - sprintf(lineBuffer, "OP_83(%d,%d)\n", param1, param2); + param2 = *(localScriptPtr + position); + position++; - break; - } - case 0x89: // OS only - { - byte param; + sprintf(lineBuffer, "OP_83(%d,%d)\n", param1, param2); - param = *(localScriptPtr + position); - position++; + break; + } + case 0x89: { // OS only + byte param; - sprintf(lineBuffer, "if(%s!=%s) goto next label(%d)\n", compareString1, compareString2, param); + param = *(localScriptPtr + position); + position++; - break; - } - case 0x8B: // OS only - { - byte param; + sprintf(lineBuffer, "if(%s!=%s) goto next label(%d)\n", compareString1, compareString2, param); - param = *(localScriptPtr + position); - position++; + break; + } + case 0x8B: { // OS only + byte param; - sprintf(lineBuffer, "OP_8B(%d)\n", param); + param = *(localScriptPtr + position); + position++; - break; - } - case 0x8C: // OS only - { - byte param; + sprintf(lineBuffer, "OP_8B(%d)\n", param); - param = *(localScriptPtr + position); - position++; + break; + } + case 0x8C: { // OS only + byte param; - sprintf(lineBuffer, "OP_8C(%d)\n", param); + param = *(localScriptPtr + position); + position++; - break; - } - case 0x8D: // OS only - { - int16 param1; - int16 param2; - int16 param3; - int16 param4; - int16 param5; - int16 param6; - int16 param7; - int16 param8; + sprintf(lineBuffer, "OP_8C(%d)\n", param); - param1 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + break; + } + case 0x8D: { // OS only + int16 param1; + int16 param2; + int16 param3; + int16 param4; + int16 param5; + int16 param6; + int16 param7; + int16 param8; - param2 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param1 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - param3 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param2 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - param4 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param3 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - param5 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param4 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - param6 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param5 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - param7 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param6 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - param8 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param7 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - sprintf(compareString1, "obj[%d]", param1); - sprintf(compareString2, "{%d,%d,%d,%d,%d,%d,%d}", param2, param3, param4, param5, param6, param7, param8); + param8 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - break; - } - case 0x8E: // OS only - { - byte param1; + sprintf(compareString1, "obj[%d]", param1); + sprintf(compareString2, "{%d,%d,%d,%d,%d,%d,%d}", param2, param3, param4, param5, param6, param7, param8); - param1 = *(localScriptPtr + position); - position++; + break; + } + case 0x8E: { // OS only + byte param1; - sprintf(lineBuffer, "ADDBG(%d,%s)\n", param1, localScriptPtr + position); + param1 = *(localScriptPtr + position); + position++; - position += strlen((const char *)localScriptPtr + position); + sprintf(lineBuffer, "ADDBG(%d,%s)\n", param1, localScriptPtr + position); - break; - } - case 0x8F: // OS only - { - byte param; + position += strlen((const char *)localScriptPtr + position); - param = *(localScriptPtr + position); - position++; + break; + } + case 0x8F: { // OS only + byte param; - sprintf(lineBuffer, "OP_8F(%d)\n", param); + param = *(localScriptPtr + position); + position++; - break; - } - case 0x90: // OS only - { - byte param1; + sprintf(lineBuffer, "OP_8F(%d)\n", param); - param1 = *(localScriptPtr + position); - position++; + break; + } + case 0x90: { // OS only + byte param1; - sprintf(lineBuffer, "loadABS(%d,%s)\n", param1, localScriptPtr + position); + param1 = *(localScriptPtr + position); + position++; - position += strlen((const char *)localScriptPtr + position); + sprintf(lineBuffer, "loadABS(%d,%s)\n", param1, localScriptPtr + position); - break; - } - case 0x91: // OS only - { - byte param; + position += strlen((const char *)localScriptPtr + position); - param = *(localScriptPtr + position); - position++; + break; + } + case 0x91: { // OS only + byte param; - sprintf(lineBuffer, "OP_91(%d)\n", param); + param = *(localScriptPtr + position); + position++; - break; - } - case 0x9D: // OS only - { - byte param; + sprintf(lineBuffer, "OP_91(%d)\n", param); - param = *(localScriptPtr + position); - position++; + break; + } + case 0x9D: { // OS only + byte param; - sprintf(lineBuffer, "OP_9D(%d) -> flip img idx\n", param); + param = *(localScriptPtr + position); + position++; - break; - } - case 0x9E: // OS only - { - byte param; + sprintf(lineBuffer, "OP_9D(%d) -> flip img idx\n", param); - param = *(localScriptPtr + position); - position++; + break; + } + case 0x9E: { // OS only + byte param; - if (param) { - byte param2; + param = *(localScriptPtr + position); + position++; - param2 = *(localScriptPtr + position); - position++; + if (param) { + byte param2; - sprintf(lineBuffer, "OP_9E(%d,%d)\n", param, param2); - } else { - int16 param2; + param2 = *(localScriptPtr + position); + position++; - param2 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + sprintf(lineBuffer, "OP_9E(%d,%d)\n", param, param2); + } else { + int16 param2; - sprintf(lineBuffer, "OP_9E(%d,%d)\n", param, param2); - } + param2 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - break; + sprintf(lineBuffer, "OP_9E(%d,%d)\n", param, param2); } - case 0xA0: // OS only - { - byte param1; - byte param2; - param1 = *(localScriptPtr + position); - position++; + break; + } + case 0xA0: { // OS only + byte param1; + byte param2; - param2 = *(localScriptPtr + position); - position++; + param1 = *(localScriptPtr + position); + position++; - sprintf(lineBuffer, "OP_A0(%d,%d)\n", param1, param2); + param2 = *(localScriptPtr + position); + position++; - break; - } - case 0xA1: // OS only - { - byte param1; - byte param2; + sprintf(lineBuffer, "OP_A0(%d,%d)\n", param1, param2); - param1 = *(localScriptPtr + position); - position++; + break; + } + case 0xA1: { // OS only + byte param1; + byte param2; - param2 = *(localScriptPtr + position); - position++; + param1 = *(localScriptPtr + position); + position++; - sprintf(lineBuffer, "OP_A1(%d,%d)\n", param1, param2); + param2 = *(localScriptPtr + position); + position++; - break; - } - default: - { - sprintf(lineBuffer, "Unsupported opcode %X in decompileScript\n\n", opcode - 1); - position = scriptSize; - break; - } + sprintf(lineBuffer, "OP_A1(%d,%d)\n", param1, param2); + + break; + } + default: { + sprintf(lineBuffer, "Unsupported opcode %X in decompileScript\n\n", opcode - 1); + position = scriptSize; + break; + } } - // printf(lineBuffer); + //printf(lineBuffer); strcpy(decompileBuffer[decompileBufferPosition++], lineBuffer); exitScript = 0; diff --git a/engines/cine/sound.cpp b/engines/cine/sound.cpp index b2e992e8f6..10404ae56b 100644 --- a/engines/cine/sound.cpp +++ b/engines/cine/sound.cpp @@ -153,7 +153,7 @@ const int AdLibSoundDriver::_freqTable[] = { const int AdLibSoundDriver::_freqTableCount = ARRAYSIZE(_freqTable); const int AdLibSoundDriver::_operatorsTable[] = { - 0, 1, 2, 3, 4, 5, 8, 9, 10, 11, 12, 13, 16, 17, 18, 19, 20, 21 + 0, 1, 2, 3, 4, 5, 8, 9, 10, 11, 12, 13, 16, 17, 18, 19, 20, 21 }; const int AdLibSoundDriver::_operatorsTableCount = ARRAYSIZE(_operatorsTable); @@ -614,7 +614,7 @@ void AdLibSoundDriverADL::playSample(const byte *data, int size, int channel, in } MidiSoundDriverH32::MidiSoundDriverH32(MidiDriver *output) - : _output(output), _callback(0), _mutex() { + : _output(output), _callback(0), _mutex() { } MidiSoundDriverH32::~MidiSoundDriverH32() { @@ -731,13 +731,13 @@ void MidiSoundDriverH32::selectInstrument(int channel, int timbreGroup, int timb 0x00, 0x00, 0x00, // offset 0x00, // Timbre group _ timbreGroup * 64 + timbreNumber should be the 0x00, // Timbre number / MT-32 instrument in case timbreGroup is 0 or 1. - 0x18, // Key shift (= 0) + 0x18, // Key shift (= 0) 0x32, // Fine tune (= 0) 0x0C, // Bender Range 0x03, // Assign Mode 0x01, // Reverb Switch (= enabled) 0x00, // dummy - 0x00, // Output level + 0x00, // Output level 0x07, // Panpot (= balanced) 0x00, // dummy 0x00, // dummy @@ -785,7 +785,6 @@ PCSoundFxPlayer::~PCSoundFxPlayer() { bool PCSoundFxPlayer::load(const char *song) { debug(9, "PCSoundFxPlayer::load('%s')", song); - Common::StackLock lock(_mutex); /* stop (w/ fade out) the previous song */ while (_fadeOutCounter != 0 && _fadeOutCounter < 100) { @@ -793,6 +792,8 @@ bool PCSoundFxPlayer::load(const char *song) { } _fadeOutCounter = 0; + Common::StackLock lock(_mutex); + stop(); _sfxData = readBundleSoundFile(song); @@ -997,19 +998,55 @@ void PCSound::stopSound(int channel) { } PaulaSound::PaulaSound(Audio::Mixer *mixer, CineEngine *vm) - : Sound(mixer, vm) { + : Sound(mixer, vm), _sfxTimer(0), _musicTimer(0), _musicFadeTimer(0) { _moduleStream = 0; + // The original is using the following timer frequency: + // 0.709379Mhz / 8000 = 88.672375Hz + // 1000000 / 88.672375Hz = 11277.46944863us + g_system->getTimerManager()->installTimerProc(&PaulaSound::sfxTimerProc, 11277, this, "PaulaSound::sfxTimerProc"); + // The original is using the following timer frequency: + // 0.709379Mhz / 14565 = 48.704359Hz + // 1000000 / 48.704359Hz = 20532.04313806us + g_system->getTimerManager()->installTimerProc(&PaulaSound::musicTimerProc, 20532, this, "PaulaSound::musicTimerProc"); } PaulaSound::~PaulaSound() { + Common::StackLock sfxLock(_sfxMutex); + g_system->getTimerManager()->removeTimerProc(&PaulaSound::sfxTimerProc); for (int i = 0; i < NUM_CHANNELS; ++i) { stopSound(i); } + + Common::StackLock musicLock(_musicMutex); + g_system->getTimerManager()->removeTimerProc(&PaulaSound::musicTimerProc); stopMusic(); } void PaulaSound::loadMusic(const char *name) { debugC(5, kCineDebugSound, "PaulaSound::loadMusic('%s')", name); + for (int i = 0; i < NUM_CHANNELS; ++i) { + stopSound(i); + } + + // Fade music out when there is music playing. + _musicMutex.lock(); + if (_mixer->isSoundHandleActive(_moduleHandle)) { + // Only start fade out when it is not in progress. + if (!_musicFadeTimer) { + _musicFadeTimer = 1; + } + + _musicMutex.unlock(); + while (_musicFadeTimer != 64) { + g_system->delayMillis(50); + } + } else { + _musicMutex.unlock(); + } + + Common::StackLock lock(_musicMutex); + assert(!_mixer->isSoundHandleActive(_moduleHandle)); + if (_vm->getGameType() == GType_FW) { // look for separate files Common::File f; @@ -1030,54 +1067,135 @@ void PaulaSound::loadMusic(const char *name) { void PaulaSound::playMusic() { debugC(5, kCineDebugSound, "PaulaSound::playMusic()"); + Common::StackLock lock(_musicMutex); + _mixer->stopHandle(_moduleHandle); if (_moduleStream) { + _musicFadeTimer = 0; _mixer->playStream(Audio::Mixer::kMusicSoundType, &_moduleHandle, _moduleStream); } } void PaulaSound::stopMusic() { debugC(5, kCineDebugSound, "PaulaSound::stopMusic()"); + Common::StackLock lock(_musicMutex); + _mixer->stopHandle(_moduleHandle); } void PaulaSound::fadeOutMusic() { debugC(5, kCineDebugSound, "PaulaSound::fadeOutMusic()"); - // TODO - stopMusic(); + Common::StackLock lock(_musicMutex); + + _musicFadeTimer = 1; } void PaulaSound::playSound(int channel, int frequency, const uint8 *data, int size, int volumeStep, int stepCount, int volume, int repeat) { - // TODO: handle volume slides and repeat debugC(5, kCineDebugSound, "PaulaSound::playSound() channel %d size %d", channel, size); + Common::StackLock lock(_sfxMutex); + assert(frequency > 0); + stopSound(channel); - size = MIN<int>(size - SPL_HDR_SIZE, READ_BE_UINT16(data + 4)); - // TODO: consider skipping the header in loadSpl directly if (size > 0) { byte *sound = (byte *)malloc(size); if (sound) { - memcpy(sound, data + SPL_HDR_SIZE, size); - playSoundChannel(channel, frequency, sound, size, volume); + // Create the audio stream + memcpy(sound, data, size); + + // Clear the first and last 16 bits like in the original. + sound[0] = sound[1] = sound[size - 2] = sound[size - 1] = 0; + + Audio::SeekableAudioStream *stream = Audio::makeRawStream(sound, size, PAULA_FREQ / frequency, 0); + + // Initialize the volume control + _channelsTable[channel].initialize(volume, volumeStep, stepCount); + + // Start the sfx + _mixer->playStream(Audio::Mixer::kSFXSoundType, &_channelsTable[channel].handle, + Audio::makeLoopingAudioStream(stream, repeat ? 0 : 1), + -1, volume * Audio::Mixer::kMaxChannelVolume / 63, + _channelBalance[channel]); } } } void PaulaSound::stopSound(int channel) { debugC(5, kCineDebugSound, "PaulaSound::stopSound() channel %d", channel); - _mixer->stopHandle(_channelsTable[channel]); + Common::StackLock lock(_sfxMutex); + + _mixer->stopHandle(_channelsTable[channel].handle); } -void PaulaSound::update() { - // process volume slides and start sound playback - // TODO +void PaulaSound::sfxTimerProc(void *param) { + PaulaSound *sound = (PaulaSound *)param; + sound->sfxTimerCallback(); } -void PaulaSound::playSoundChannel(int channel, int frequency, uint8 *data, int size, int volume) { - assert(frequency > 0); - frequency = PAULA_FREQ / frequency; - Audio::AudioStream *stream = Audio::makeRawStream(data, size, frequency, 0); - _mixer->playStream(Audio::Mixer::kSFXSoundType, &_channelsTable[channel], stream); - _mixer->setChannelVolume(_channelsTable[channel], volume * Audio::Mixer::kMaxChannelVolume / 63); +void PaulaSound::sfxTimerCallback() { + Common::StackLock lock(_sfxMutex); + + if (_sfxTimer < 6) { + ++_sfxTimer; + + for (int i = 0; i < NUM_CHANNELS; ++i) { + // Only process active channels + if (!_mixer->isSoundHandleActive(_channelsTable[i].handle)) { + continue; + } + + if (_channelsTable[i].curStep) { + --_channelsTable[i].curStep; + } else { + _channelsTable[i].curStep = _channelsTable[i].stepCount; + const int volume = CLIP(_channelsTable[i].volume + _channelsTable[i].volumeStep, 0, 63); + _channelsTable[i].volume = volume; + // Unlike the original we stop silent sounds + if (volume) { + _mixer->setChannelVolume(_channelsTable[i].handle, volume * Audio::Mixer::kMaxChannelVolume / 63); + } else { + _mixer->stopHandle(_channelsTable[i].handle); + } + } + } + } else { + _sfxTimer = 0; + // Possible TODO: The original only ever started sounds here. This + // should not be noticable though. So we do not do it for now. + } +} + +void PaulaSound::musicTimerProc(void *param) { + PaulaSound *sound = (PaulaSound *)param; + sound->musicTimerCallback(); +} + +void PaulaSound::musicTimerCallback() { + Common::StackLock lock(_musicMutex); + + ++_musicTimer; + if (_musicTimer == 6) { + _musicTimer = 0; + if (_musicFadeTimer) { + ++_musicFadeTimer; + if (_musicFadeTimer == 64) { + stopMusic(); + } else { + if (_mixer->isSoundHandleActive(_moduleHandle)) { + _mixer->setChannelVolume(_moduleHandle, (64 - _musicFadeTimer) * Audio::Mixer::kMaxChannelVolume / 64); + } + } + } + } } +const int PaulaSound::_channelBalance[NUM_CHANNELS] = { + // L/R/R/L This is according to the Hardware Reference Manual. + // TODO: It seems the order is swapped for some Amiga models: + // http://www.amiga.org/forums/archive/index.php/t-7862.html + // Maybe we should consider using R/L/L/R to match Amiga 500? + // This also is a bit more drastic to what WineUAE defaults, + // which is only 70% of full panning. + -127, 127, 127, -127 +}; + } // End of namespace Cine diff --git a/engines/cine/sound.h b/engines/cine/sound.h index afc0994a26..fdb183ad34 100644 --- a/engines/cine/sound.h +++ b/engines/cine/sound.h @@ -24,6 +24,7 @@ #define CINE_SOUND_H_ #include "common/util.h" +#include "common/mutex.h" #include "audio/mixer.h" namespace Audio { @@ -47,7 +48,6 @@ public: virtual void playSound(int channel, int frequency, const uint8 *data, int size, int volumeStep, int stepCount, int volume, int repeat) = 0; virtual void stopSound(int channel) = 0; - virtual void update() {} protected: @@ -91,19 +91,39 @@ public: virtual void playSound(int channel, int frequency, const uint8 *data, int size, int volumeStep, int stepCount, int volume, int repeat); virtual void stopSound(int channel); - virtual void update(); enum { - PAULA_FREQ = 7093789, - NUM_CHANNELS = 4, - SPL_HDR_SIZE = 22 + PAULA_FREQ = 3579545, + NUM_CHANNELS = 4 }; protected: - void playSoundChannel(int channel, int frequency, uint8 *data, int size, int volume); - - Audio::SoundHandle _channelsTable[NUM_CHANNELS]; + struct SfxChannel { + Audio::SoundHandle handle; + int volume; + int volumeStep; + int curStep; + int stepCount; + + void initialize(int vol, int volStep, int stepCnt) { + volume = vol; + volumeStep = volStep; + curStep = stepCount = stepCnt; + } + }; + SfxChannel _channelsTable[NUM_CHANNELS]; + static const int _channelBalance[NUM_CHANNELS]; + Common::Mutex _sfxMutex; + int _sfxTimer; + static void sfxTimerProc(void *param); + void sfxTimerCallback(); + + Common::Mutex _musicMutex; + int _musicTimer; + int _musicFadeTimer; + static void musicTimerProc(void *param); + void musicTimerCallback(); Audio::SoundHandle _moduleHandle; Audio::AudioStream *_moduleStream; }; diff --git a/engines/cine/texte.cpp b/engines/cine/texte.cpp index 33ea569df7..998075c6ce 100644 --- a/engines/cine/texte.cpp +++ b/engines/cine/texte.cpp @@ -88,7 +88,7 @@ static const CharacterEntry fontParamTable_standard[NUM_FONT_CHARS] = { {64, 3}, {65, 3}, { 0, 0}, { 0, 0}, {62, 2}, {74, 6}, {66, 1}, {67, 6}, {52, 6}, {53, 6}, {54, 6}, {55, 6}, {56, 6}, {57, 6}, {58, 6}, {59, 6}, {60, 6}, {61, 6}, {76, 3}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, {75, 6}, - { 0, 0}, { 0, 6}, //a + { 0, 0}, { 0, 6}, //a { 1, 6}, { 2, 6}, { 3, 6}, { 4, 6}, { 5, 6}, { 6, 6}, { 7, 6}, { 8, 3}, { 9, 6}, {10, 6}, {11, 6}, {12, 7}, {13, 6}, {14, 6}, {15, 6}, {16, 6}, {17, 6}, {18, 6}, {19, 6}, {20, 6}, {21, 6}, {22, 7}, diff --git a/engines/cine/texte.h b/engines/cine/texte.h index dd4b7e06ee..185dc53bfd 100644 --- a/engines/cine/texte.h +++ b/engines/cine/texte.h @@ -46,7 +46,7 @@ struct CharacterEntry { }; struct TextHandler { - byte textTable[NUM_FONT_CHARS][2][FONT_WIDTH * FONT_HEIGHT]; + byte textTable[NUM_FONT_CHARS][2][FONT_WIDTH *FONT_HEIGHT]; CharacterEntry fontParamTable[NUM_FONT_CHARS]; }; diff --git a/engines/cine/various.cpp b/engines/cine/various.cpp index 9b73ae1101..23f439a7a7 100644 --- a/engines/cine/various.cpp +++ b/engines/cine/various.cpp @@ -36,7 +36,7 @@ namespace Cine { -bool disableSystemMenu = false; +int16 disableSystemMenu = 0; bool inMenu; int16 commandVar3[4]; @@ -99,8 +99,7 @@ byte isInPause = 0; * Bit on = mouse button down * Bit off = mouse button up */ -enum MouseButtonState -{ +enum MouseButtonState { kLeftMouseButton = (1 << 0), kRightMouseButton = (1 << 1) }; @@ -271,7 +270,7 @@ int16 getObjectUnderCursor(uint16 x, uint16 y) { } else if (it->type == 1 && gfxGetBit(xdif, ydif, g_cine->_animDataTable[frame].data(), g_cine->_animDataTable[frame]._width * 4)) { return it->objIdx; } - } else if (it->type == 0) { // use generated mask + } else if (it->type == 0) { // use generated mask if (gfxGetBit(xdif, ydif, g_cine->_animDataTable[frame].mask(), g_cine->_animDataTable[frame]._width)) { return it->objIdx; } @@ -341,7 +340,7 @@ void CineEngine::makeSystemMenu() { int16 mouseX, mouseY, mouseButton; int16 selectedSave; - if (!disableSystemMenu) { + if (disableSystemMenu != 1) { inMenu = true; do { @@ -358,128 +357,122 @@ void CineEngine::makeSystemMenu() { systemCommand = makeMenuChoice(systemMenu, numEntry, mouseX, mouseY, 140); switch (systemCommand) { - case 0: // Pause - { - renderer->drawString(otherMessages[2], 0); - waitPlayerInput(); - break; - } - case 1: // Restart Game - { - getMouseData(mouseUpdateStatus, (uint16 *)&mouseButton, (uint16 *)&mouseX, (uint16 *)&mouseY); - if (!makeMenuChoice(confirmMenu, 2, mouseX, mouseY + 8, 100)) { - _restartRequested = true; - } - break; - } - case 2: // Quit - { - getMouseData(mouseUpdateStatus, (uint16 *)&mouseButton, (uint16 *)&mouseX, (uint16 *)&mouseY); - if (!makeMenuChoice(confirmMenu, 2, mouseX, mouseY + 8, 100)) { - quitGame(); - } - break; + case 0: { // Pause + renderer->drawString(otherMessages[2], 0); + waitPlayerInput(); + break; + } + case 1: { // Restart Game + getMouseData(mouseUpdateStatus, (uint16 *)&mouseButton, (uint16 *)&mouseX, (uint16 *)&mouseY); + if (!makeMenuChoice(confirmMenu, 2, mouseX, mouseY + 8, 100)) { + _restartRequested = true; } - case 3: // Select save drive... change ? - { - break; + break; + } + case 2: { // Quit + getMouseData(mouseUpdateStatus, (uint16 *)&mouseButton, (uint16 *)&mouseX, (uint16 *)&mouseY); + if (!makeMenuChoice(confirmMenu, 2, mouseX, mouseY + 8, 100)) { + quitGame(); } - case 4: // load game - { - if (loadSaveDirectory()) { + break; + } + case 3: { // Select save drive... change ? + break; + } + case 4: { // load game + if (loadSaveDirectory()) { // int16 selectedSave; - getMouseData(mouseUpdateStatus, (uint16 *)&mouseButton, (uint16 *)&mouseX, (uint16 *)&mouseY); - selectedSave = makeMenuChoice(currentSaveName, 10, mouseX, mouseY + 8, 180); + getMouseData(mouseUpdateStatus, (uint16 *)&mouseButton, (uint16 *)&mouseX, (uint16 *)&mouseY); + selectedSave = makeMenuChoice(currentSaveName, 10, mouseX, mouseY + 8, 180); - if (selectedSave >= 0) { - char saveNameBuffer[256]; - sprintf(saveNameBuffer, "%s.%1d", _targetName.c_str(), selectedSave); + if (selectedSave >= 0) { + char saveNameBuffer[256]; + sprintf(saveNameBuffer, "%s.%1d", _targetName.c_str(), selectedSave); - getMouseData(mouseUpdateStatus, (uint16 *)&mouseButton, (uint16 *)&mouseX, (uint16 *)&mouseY); - if (!makeMenuChoice(confirmMenu, 2, mouseX, mouseY + 8, 100)) { - char loadString[256]; + getMouseData(mouseUpdateStatus, (uint16 *)&mouseButton, (uint16 *)&mouseX, (uint16 *)&mouseY); + if (!makeMenuChoice(confirmMenu, 2, mouseX, mouseY + 8, 100)) { + char loadString[256]; - sprintf(loadString, otherMessages[3], currentSaveName[selectedSave]); - renderer->drawString(loadString, 0); + sprintf(loadString, otherMessages[3], currentSaveName[selectedSave]); + renderer->drawString(loadString, 0); - makeLoad(saveNameBuffer); - } else { - renderer->drawString(otherMessages[4], 0); - waitPlayerInput(); - checkDataDisk(-1); - } + makeLoad(saveNameBuffer); } else { renderer->drawString(otherMessages[4], 0); waitPlayerInput(); checkDataDisk(-1); } } else { - renderer->drawString(otherMessages[5], 0); + renderer->drawString(otherMessages[4], 0); waitPlayerInput(); checkDataDisk(-1); } - break; + } else { + renderer->drawString(otherMessages[5], 0); + waitPlayerInput(); + checkDataDisk(-1); } - case 5: // Save game - { - loadSaveDirectory(); - selectedSave = makeMenuChoice(currentSaveName, 10, mouseX, mouseY + 8, 180); + break; + } + case 5: { // Save game + loadSaveDirectory(); + selectedSave = makeMenuChoice(currentSaveName, 10, mouseX, mouseY + 8, 180); - if (selectedSave >= 0) { - char saveFileName[256]; - char saveName[20]; - saveName[0] = 0; + if (selectedSave >= 0) { + char saveFileName[256]; + char saveName[20]; + saveName[0] = 0; - if (!makeTextEntryMenu(otherMessages[6], saveName, 20, 120)) - break; + if (!makeTextEntryMenu(otherMessages[6], saveName, 20, 120)) + break; - strncpy(currentSaveName[selectedSave], saveName, 20); + strncpy(currentSaveName[selectedSave], saveName, 20); - sprintf(saveFileName, "%s.%1d", _targetName.c_str(), selectedSave); + sprintf(saveFileName, "%s.%1d", _targetName.c_str(), selectedSave); - getMouseData(mouseUpdateStatus, (uint16 *)&mouseButton, (uint16 *)&mouseX, (uint16 *)&mouseY); - if (!makeMenuChoice(confirmMenu, 2, mouseX, mouseY + 8, 100)) { - char saveString[256]; - Common::String tmp = Common::String::format("%s.dir", _targetName.c_str()); + getMouseData(mouseUpdateStatus, (uint16 *)&mouseButton, (uint16 *)&mouseX, (uint16 *)&mouseY); + if (!makeMenuChoice(confirmMenu, 2, mouseX, mouseY + 8, 100)) { + char saveString[256]; + Common::String tmp = Common::String::format("%s.dir", _targetName.c_str()); - Common::OutSaveFile *fHandle = _saveFileMan->openForSaving(tmp); - if (!fHandle) { - warning("Unable to open file %s for saving", tmp.c_str()); - break; - } + Common::OutSaveFile *fHandle = _saveFileMan->openForSaving(tmp); + if (!fHandle) { + warning("Unable to open file %s for saving", tmp.c_str()); + break; + } - fHandle->write(currentSaveName, 200); - delete fHandle; + fHandle->write(currentSaveName, 200); + delete fHandle; - sprintf(saveString, otherMessages[3], currentSaveName[selectedSave]); - renderer->drawString(saveString, 0); + sprintf(saveString, otherMessages[3], currentSaveName[selectedSave]); + renderer->drawString(saveString, 0); - makeSave(saveFileName); + makeSave(saveFileName); - checkDataDisk(-1); - } else { - renderer->drawString(otherMessages[4], 0); - waitPlayerInput(); - checkDataDisk(-1); - } + checkDataDisk(-1); + } else { + renderer->drawString(otherMessages[4], 0); + waitPlayerInput(); + checkDataDisk(-1); } - break; } + break; + } } inMenu = false; } } -void drawMessageBox(int16 x, int16 y, int16 width, int16 currentY, int16 offset, int16 color, byte* page) { - gfxDrawLine(x + offset, y + offset, x + width - offset, y + offset, color, page); // top - gfxDrawLine(x + offset, currentY + 4 - offset, x + width - offset, currentY + 4 - offset, color, page); // bottom - gfxDrawLine(x + offset, y + offset, x + offset, currentY + 4 - offset, color, page); // left - gfxDrawLine(x + width - offset, y + offset, x + width - offset, currentY + 4 - offset, color, page); // right +void drawMessageBox(int16 x, int16 y, int16 width, int16 currentY, int16 offset, int16 color, byte *page) { + gfxDrawLine(x + offset, y + offset, x + width - offset, y + offset, color, page); // top + gfxDrawLine(x + offset, currentY + 4 - offset, x + width - offset, currentY + 4 - offset, color, page); // bottom + gfxDrawLine(x + offset, y + offset, x + offset, currentY + 4 - offset, color, page); // left + gfxDrawLine(x + width - offset, y + offset, x + width - offset, currentY + 4 - offset, color, page); // right } -void drawDoubleMessageBox(int16 x, int16 y, int16 width, int16 currentY, int16 color, byte* page) { +void drawDoubleMessageBox(int16 x, int16 y, int16 width, int16 currentY, int16 color, byte *page) { drawMessageBox(x, y, width, currentY, 1, 0, page); drawMessageBox(x, y, width, currentY, 0, color, page); } @@ -544,14 +537,16 @@ int16 buildObjectListCommand(int16 param) { int16 selectSubObject(int16 x, int16 y, int16 param) { int16 listSize = buildObjectListCommand(param); - int16 selectedObject; + int16 selectedObject = -1; bool osExtras = g_cine->getGameType() == Cine::GType_OS; if (!listSize) { return -2; } - selectedObject = makeMenuChoice(objectListCommand, listSize, x, y, 140, osExtras); + if (disableSystemMenu == 0) { + selectedObject = makeMenuChoice(objectListCommand, listSize, x, y, 140, osExtras); + } if (selectedObject == -1) return -1; @@ -579,7 +574,7 @@ void makeCommandLine() { g_cine->_commandBuffer = ""; } - if ((playerCommand != -1) && (choiceResultTable[playerCommand] == 2)) { // need object selection ? + if ((playerCommand != -1) && (choiceResultTable[playerCommand] == 2)) { // need object selection? int16 si; getMouseData(mouseUpdateStatus, &dummyU16, &x, &y); @@ -633,7 +628,7 @@ void makeCommandLine() { } if (g_cine->getGameType() == Cine::GType_OS && playerCommand != 2) { - if (playerCommand != -1 && canUseOnObject != 0) { // call use on sub object + if (playerCommand != -1 && canUseOnObject != 0) { // call use on sub object int16 si; getMouseData(mouseUpdateStatus, &dummyU16, &x, &y); @@ -691,9 +686,6 @@ int16 makeMenuChoice(const CommandeType commandList[], uint16 height, uint16 X, int16 var_4; SelectionMenu *menu; - if (disableSystemMenu) - return -1; - paramY = (height * 9) + 10; if (X + width > 319) { @@ -743,11 +735,11 @@ int16 makeMenuChoice(const CommandeType commandList[], uint16 height, uint16 X, mainLoopSub6(); } - if (menuVar4 && currentSelection > 0) { // go up + if (menuVar4 && currentSelection > 0) { // go up currentSelection--; } - if (menuVar5) { // go down + if (menuVar5) { // go down if (height - 1 > currentSelection) { currentSelection++; } @@ -764,7 +756,7 @@ int16 makeMenuChoice(const CommandeType commandList[], uint16 height, uint16 X, } } - if (currentSelection != oldSelection) { // old != new + if (currentSelection != oldSelection) { // old != new if (needMouseSave) { hideMouse(); } @@ -790,7 +782,7 @@ int16 makeMenuChoice(const CommandeType commandList[], uint16 height, uint16 X, getMouseData(mouseUpdateStatus, &button, &dummyU16, &dummyU16); } while (button && !g_cine->shouldQuit()); - if (var_4 == 2) { // recheck + if (var_4 == 2) { // recheck if (!recheckValue) return -1; else @@ -810,14 +802,18 @@ void makeActionMenu() { getMouseData(mouseUpdateStatus, &mouseButton, &mouseX, &mouseY); if (g_cine->getGameType() == Cine::GType_OS) { - playerCommand = makeMenuChoice(defaultActionCommand, 6, mouseX, mouseY, 70, true); + if (disableSystemMenu == 0) { + playerCommand = makeMenuChoice(defaultActionCommand, 6, mouseX, mouseY, 70, true); + } if (playerCommand >= 8000) { playerCommand -= 8000; canUseOnObject = canUseOnItemTable[playerCommand]; } } else { - playerCommand = makeMenuChoice(defaultActionCommand, 6, mouseX, mouseY, 70); + if (disableSystemMenu == 0) { + playerCommand = makeMenuChoice(defaultActionCommand, 6, mouseX, mouseY, 70); + } } inMenu = false; @@ -1182,7 +1178,7 @@ void removeMessages() { Common::List<overlay>::iterator it; bool remove; - for (it = g_cine->_overlayList.begin(); it != g_cine->_overlayList.end(); ) { + for (it = g_cine->_overlayList.begin(); it != g_cine->_overlayList.end();) { if (g_cine->getGameType() == Cine::GType_OS) { // NOTE: These are really removeOverlay calls that have been deferred. // In Operation Stealth's disassembly elements are removed from the @@ -1345,7 +1341,7 @@ void modifySeqListElement(uint16 objIdx, int16 var4Test, int16 param1, int16 par } void computeMove1(SeqListElement &element, int16 x, int16 y, int16 param1, - int16 param2, int16 x2, int16 y2) { + int16 param2, int16 x2, int16 y2) { element.var16 = 0; element.var14 = 0; @@ -1394,7 +1390,7 @@ uint16 addAni(uint16 param1, uint16 objIdx, const int8 *ptr, SeqListElement &ele int16 di; debug(5, "addAni: param1 = %d, objIdx = %d, ptr = %p, element.var8 = %d, element.var14 = %d param3 = %d", - param1, objIdx, ptr, element.var8, element.var14, param3); + param1, objIdx, ptr, element.var8, element.var14, param3); // In the original an error string is set and 0 is returned if the following doesn't hold assert(ptr); @@ -1410,16 +1406,16 @@ uint16 addAni(uint16 param1, uint16 objIdx, const int8 *ptr, SeqListElement &ele di = (g_cine->_objectTable[objIdx].costume + 1) % (*ptrData); ++ptrData; // Jump over the just read byte // Here ptr2 seems to be indexing a table of structs (8 bytes per struct): - // struct { - // int8 x; // 0 (Used with checkCollision) - // int8 y; // 1 (Used with checkCollision) - // int8 numZones; // 2 (Used with checkCollision) - // int8 var3; // 3 (Not used in this function) - // int8 xAdd; // 4 (Used with an object) - // int8 yAdd; // 5 (Used with an object) - // int8 maskAdd; // 6 (Used with an object) - // int8 frameAdd; // 7 (Used with an object) - // }; + // struct { + // int8 x; // 0 (Used with checkCollision) + // int8 y; // 1 (Used with checkCollision) + // int8 numZones; // 2 (Used with checkCollision) + // int8 var3; // 3 (Not used in this function) + // int8 xAdd; // 4 (Used with an object) + // int8 yAdd; // 5 (Used with an object) + // int8 maskAdd; // 6 (Used with an object) + // int8 frameAdd; // 7 (Used with an object) + // }; ptr2 = ptrData + di * 8; // We might probably safely discard the AND by 1 here because @@ -1569,8 +1565,7 @@ void processSeqListElement(SeqListElement &element) { var_4 = -1; if ((element.var16 == 1 - && !addAni(3, element.objIdx, ptr1, element, 0, &var_4)) || (element.var16 == 2 && !addAni(2, element.objIdx, ptr1, element, 0, - &var_4))) { + && !addAni(3, element.objIdx, ptr1, element, 0, &var_4)) || (element.var16 == 2 && !addAni(2, element.objIdx, ptr1, element, 0, &var_4))) { if (element.varC == 255) { g_cine->_globalVars[VAR_MOUSE_Y_POS] = 0; } @@ -1699,9 +1694,9 @@ bool makeTextEntryMenu(const char *messagePtr, char *inputString, int stringMaxL } break; default: - if (((keycode >= 'a') && (keycode <='z')) || - ((keycode >= '0') && (keycode <='9')) || - ((keycode >= 'A') && (keycode <='Z')) || + if (((keycode >= 'a') && (keycode <= 'z')) || + ((keycode >= '0') && (keycode <= '9')) || + ((keycode >= 'A') && (keycode <= 'Z')) || (keycode == ' ')) { if (inputLength < stringMaxLength - 1) { ch[0] = keycode; diff --git a/engines/cine/various.h b/engines/cine/various.h index 0c1883c323..813619816d 100644 --- a/engines/cine/various.h +++ b/engines/cine/various.h @@ -41,7 +41,7 @@ void makeActionMenu(); void waitPlayerInput(); void setTextWindow(uint16 param1, uint16 param2, uint16 param3, uint16 param4); -extern bool disableSystemMenu; +extern int16 disableSystemMenu; extern bool inMenu; extern CommandeType currentSaveName[10]; diff --git a/engines/composer/composer.cpp b/engines/composer/composer.cpp index 23a9d2ff85..5db778dfda 100644 --- a/engines/composer/composer.cpp +++ b/engines/composer/composer.cpp @@ -20,7 +20,7 @@ * */ #include "common/scummsys.h" - + #include "common/config-manager.h" #include "common/events.h" #include "common/file.h" @@ -102,13 +102,14 @@ Common::Error ComposerEngine::run() { if (_bookIni.hasKey("Height", "Common")) height = atoi(getStringFromConfig("Common", "Height").c_str()); initGraphics(width, height, true); - _surface.create(width, height, Graphics::PixelFormat::createFormatCLUT8()); + _screen.create(width, height, Graphics::PixelFormat::createFormatCLUT8()); _needsUpdate = true; Graphics::Cursor *cursor = Graphics::makeDefaultWinCursor(); CursorMan.replaceCursor(cursor->getSurface(), cursor->getWidth(), cursor->getHeight(), cursor->getHotspotX(), cursor->getHotspotY(), cursor->getKeyColor()); CursorMan.replaceCursorPalette(cursor->getPalette(), cursor->getPaletteStartIndex(), cursor->getPaletteCount()); + delete cursor; loadLibrary(0); @@ -213,6 +214,8 @@ Common::Error ComposerEngine::run() { _system->delayMillis(20); } + _screen.free(); + return Common::kNoError; } diff --git a/engines/composer/composer.h b/engines/composer/composer.h index 0f53258289..33a5356b3a 100644 --- a/engines/composer/composer.h +++ b/engines/composer/composer.h @@ -170,7 +170,7 @@ private: bool _needsUpdate; Common::Array<Common::Rect> _dirtyRects; - Graphics::Surface _surface; + Graphics::Surface _screen; Common::List<Sprite> _sprites; uint _directoriesToStrip; diff --git a/engines/composer/detection.cpp b/engines/composer/detection.cpp index 835f3c5683..8411441c60 100644 --- a/engines/composer/detection.cpp +++ b/engines/composer/detection.cpp @@ -374,7 +374,7 @@ static const ComposerGameDescription gameDescriptions[] = { using namespace Composer; // we match from data too, to stop detection from a non-top-level directory -const static char *directoryGlobs[] = { +static const char *directoryGlobs[] = { "data", "liam", "programs", diff --git a/engines/composer/graphics.cpp b/engines/composer/graphics.cpp index 1314e903ae..2b68fac233 100644 --- a/engines/composer/graphics.cpp +++ b/engines/composer/graphics.cpp @@ -507,7 +507,7 @@ const Sprite *ComposerEngine::getSpriteAtPos(const Common::Point &pos) { void ComposerEngine::dirtySprite(const Sprite &sprite) { Common::Rect rect(sprite._pos.x, sprite._pos.y, sprite._pos.x + sprite._surface.w, sprite._pos.y + sprite._surface.h); - rect.clip(_surface.w, _surface.h); + rect.clip(_screen.w, _screen.h); if (rect.isEmpty()) return; @@ -541,8 +541,8 @@ void ComposerEngine::redraw() { for (uint i = 0; i < _dirtyRects.size(); i++) { const Common::Rect &rect = _dirtyRects[i]; - byte *pixels = (byte *)_surface.pixels + (rect.top * _surface.pitch) + rect.left; - _system->copyRectToScreen(pixels, _surface.pitch, rect.left, rect.top, rect.width(), rect.height()); + byte *pixels = (byte *)_screen.pixels + (rect.top * _screen.pitch) + rect.left; + _system->copyRectToScreen(pixels, _screen.pitch, rect.left, rect.top, rect.width(), rect.height()); } _system->updateScreen(); @@ -814,16 +814,16 @@ void ComposerEngine::drawSprite(const Sprite &sprite) { int y = sprite._pos.y; // incoming data is BMP-style (bottom-up), so flip it - byte *pixels = (byte *)_surface.pixels; + byte *pixels = (byte *)_screen.pixels; for (int j = 0; j < sprite._surface.h; j++) { if (j + y < 0) continue; - if (j + y >= _surface.h) + if (j + y >= _screen.h) break; byte *in = (byte *)sprite._surface.pixels + (sprite._surface.h - j - 1) * sprite._surface.w; - byte *out = pixels + ((j + y) * _surface.w) + x; + byte *out = pixels + ((j + y) * _screen.w) + x; for (int i = 0; i < sprite._surface.w; i++) - if ((x + i >= 0) && (x + i < _surface.w) && in[i]) + if ((x + i >= 0) && (x + i < _screen.w) && in[i]) out[i] = in[i]; } } diff --git a/engines/composer/resource.cpp b/engines/composer/resource.cpp index a4e292747c..83e49971fb 100644 --- a/engines/composer/resource.cpp +++ b/engines/composer/resource.cpp @@ -240,7 +240,7 @@ bool ComposerArchive::openStream(Common::SeekableReadStream *stream) { res.flags = flags; debug(4, "Id %d, offset %d, size %d, flags %08x", id, offset, size, flags); } - + stream->seek(oldPos); } diff --git a/engines/configure.engines b/engines/configure.engines index 8eaee730cc..1f2a31b382 100644 --- a/engines/configure.engines +++ b/engines/configure.engines @@ -1,9 +1,10 @@ # This file is included from the main "configure" script -add_engine scumm "SCUMM" yes "scumm_7_8 he" +# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] +add_engine scumm "SCUMM" yes "scumm_7_8 he" "v0-v6 games" add_engine scumm_7_8 "v7 & v8 games" yes add_engine he "HE71+ games" yes add_engine agi "AGI" yes -add_engine agos "AGOS" yes "agos2" +add_engine agos "AGOS" yes "agos2" "AGOS 1 games" add_engine agos2 "AGOS 2 games" yes add_engine cge "CGE" yes add_engine cine "Cinematique evo 1" yes @@ -13,35 +14,38 @@ add_engine draci "Dragon History" yes add_engine drascula "Drascula: The Vampire Strikes Back" yes add_engine dreamweb "Dreamweb" yes add_engine gob "Gobli*ns" yes -add_engine groovie "Groovie" yes "groovie2" +add_engine groovie "Groovie" yes "groovie2" "7th Guest" add_engine groovie2 "Groovie 2 games" no add_engine hugo "Hugo Trilogy" yes -add_engine kyra "Legend of Kyrandia" yes "lol eob" +add_engine kyra "Kyra" yes "lol eob" "Legend of Kyrandia 1-3" add_engine lol "Lands of Lore" yes -add_engine eob "Eye of the Beholder" no -add_engine lastexpress "The Last Express" no +add_engine eob "Eye of the Beholder" yes +add_engine lastexpress "The Last Express" no "" "" "16bit" add_engine lure "Lure of the Temptress" yes add_engine made "MADE" yes -add_engine mohawk "Mohawk" yes "cstime myst riven" +add_engine mohawk "Mohawk" yes "cstime myst riven" "Living Books" add_engine cstime "Where in Time is Carmen Sandiego?" no -add_engine riven "Riven: The Sequel to Myst" no -add_engine myst "Myst" no +add_engine riven "Riven: The Sequel to Myst" no "" "" "16bit" +add_engine myst "Myst" no "" "" "16bit" add_engine parallaction "Parallaction" yes +add_engine pegasus "The Journeyman Project: Pegasus Prime" no "" "" "16bit" add_engine queen "Flight of the Amazon Queen" yes -add_engine saga "SAGA" yes "ihnm saga2" +add_engine saga "SAGA" yes "ihnm saga2" "ITE" add_engine ihnm "IHNM" yes add_engine saga2 "SAGA 2 games" no -add_engine sci "SCI" yes "sci32" +add_engine sci "SCI" yes "sci32" "SCI 0-1.1 games" add_engine sci32 "SCI32 games" no add_engine sky "Beneath a Steel Sky" yes add_engine sword1 "Broken Sword" yes add_engine sword2 "Broken Sword II" yes -add_engine sword25 "Broken Sword 2.5" no +add_engine sword25 "Broken Sword 2.5" no "" "" "png zlib 16bit" add_engine teenagent "Teen Agent" yes add_engine testbed "TestBed: the Testing framework" no add_engine tinsel "Tinsel" yes -add_engine toltecs "3 Skulls of the Toltecs" no +add_engine toltecs "3 Skulls of the Toltecs" yes add_engine toon "Toonstruck" yes add_engine touche "Touche: The Adventures of the Fifth Musketeer" yes +add_engine tony "Tony Tough and the Night of Roasted Moths" yes "" "" "16bit" add_engine tsage "TsAGE" yes add_engine tucker "Bud Tucker in Double Trouble" yes +add_engine wintermute "Wintermute" no "" "" "png zlib vorbis 16bit" diff --git a/engines/cruise/detection.cpp b/engines/cruise/detection.cpp index cbe17ea4d3..ba79df4822 100644 --- a/engines/cruise/detection.cpp +++ b/engines/cruise/detection.cpp @@ -295,7 +295,7 @@ void CruiseMetaEngine::removeSaveState(const char *target, int slot) const { SaveStateDescriptor CruiseMetaEngine::querySaveMetaInfos(const char *target, int slot) const { Common::InSaveFile *f = g_system->getSavefileManager()->openForLoading( Cruise::CruiseEngine::getSavegameFile(slot)); - + if (f) { Cruise::CruiseSavegameHeader header; Cruise::readSavegameHeader(f, header); diff --git a/engines/cruise/staticres.cpp b/engines/cruise/staticres.cpp index a3fc4f884b..08ff4d7548 100644 --- a/engines/cruise/staticres.cpp +++ b/engines/cruise/staticres.cpp @@ -177,9 +177,9 @@ const int16 spanish_fontCharacterTable[256] = { 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, -1, -1, -1, - 0x72, 0x80 + 0x72, 0x80, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0x7f, 0x79, 0x7b, 0x81, 0x82, 0x83, -1, -1, 0x7d, diff --git a/engines/dialogs.cpp b/engines/dialogs.cpp index cf3dfaa44b..c884075093 100644 --- a/engines/dialogs.cpp +++ b/engines/dialogs.cpp @@ -229,7 +229,7 @@ void MainMenuDialog::save() { "Please consult the README for basic information, and for " "instructions on how to obtain further assistance."), status.getDesc().c_str()); GUI::MessageDialog dialog(failMessage); - dialog.runModal(); + dialog.runModal(); } close(); diff --git a/engines/draci/detection.cpp b/engines/draci/detection.cpp index 61705a1e59..2d78d05933 100644 --- a/engines/draci/detection.cpp +++ b/engines/draci/detection.cpp @@ -153,7 +153,7 @@ void DraciMetaEngine::removeSaveState(const char *target, int slot) const { SaveStateDescriptor DraciMetaEngine::querySaveMetaInfos(const char *target, int slot) const { Common::InSaveFile *f = g_system->getSavefileManager()->openForLoading( Draci::DraciEngine::getSavegameFile(slot)); - + if (f) { Draci::DraciSavegameHeader header; Draci::readSavegameHeader(f, header); diff --git a/engines/drascula/animation.cpp b/engines/drascula/animation.cpp index 43799f7944..7009752365 100644 --- a/engines/drascula/animation.cpp +++ b/engines/drascula/animation.cpp @@ -42,7 +42,6 @@ void DrasculaEngine::updateAnim(int y, int destX, int destY, int width, int heig } } -// This is the game's introduction sequence void DrasculaEngine::animation_1_1() { debug(4, "animation_1_1()"); @@ -92,12 +91,8 @@ void DrasculaEngine::animation_1_1() { playFLI("scrollb.bin", 9); - if ((term_int == 1) || (getScan() == Common::KEYCODE_ESCAPE) || shouldQuit()) - break; clearRoom(); playSound(5); - if ((term_int == 1) || (getScan() == Common::KEYCODE_ESCAPE) || shouldQuit()) - break; if (animate("scr2.bin", 17)) break; stopSound(); @@ -194,8 +189,6 @@ void DrasculaEngine::animation_1_1() { if ((term_int == 1) || (getScan() == Common::KEYCODE_ESCAPE) || shouldQuit()) break; talk_drascula(3); - if ((term_int == 1) || (getScan() == Common::KEYCODE_ESCAPE) || shouldQuit()) - break; if (animate("lib.bin", 16)) break; if (animate("lib2.bin", 16)) @@ -211,8 +204,6 @@ void DrasculaEngine::animation_1_1() { loadPic("plan1.alg", screenSurface, HALF_PAL); updateScreen(); talk_solo(_textd[5], "d5.als"); - if ((term_int == 1) || (getScan() == Common::KEYCODE_ESCAPE) || shouldQuit()) - break; if (animate("lib2.bin", 16)) break; clearRoom(); @@ -220,8 +211,6 @@ void DrasculaEngine::animation_1_1() { updateScreen(); pause(20); talk_solo(_textd[6], "d6.als"); - if ((term_int == 1) || (getScan() == Common::KEYCODE_ESCAPE) || shouldQuit()) - break; if (animate("lib2.bin", 16)) break; clearRoom(); @@ -255,8 +244,6 @@ void DrasculaEngine::animation_1_1() { break; playMusic(11); talk_drascula(10); - if ((term_int == 1) || (getScan() == Common::KEYCODE_ESCAPE) || shouldQuit()) - break; if (animate("rayo1.bin", 16)) break; playSound(5); @@ -372,7 +359,6 @@ void DrasculaEngine::animation_1_1() { loadPic(99, backSurface); } -// John falls in love with BJ, who is then abducted by Drascula void DrasculaEngine::animation_2_1() { debug(4, "animation_2_1()"); @@ -384,9 +370,6 @@ void DrasculaEngine::animation_2_1() { term_int = 0; while (!shouldQuit()) { - if ((term_int == 1) || (getScan() == Common::KEYCODE_ESCAPE) || shouldQuit()) - break; - if (animate("ag.bin", 14)) break; @@ -456,13 +439,8 @@ void DrasculaEngine::animation_2_1() { curY = 95; trackProtagonist = 1; hare_se_ve = 1; - if ((term_int == 1) || (getScan() == Common::KEYCODE_ESCAPE) || shouldQuit()) - break; loadPic("97g.alg", extraSurface); - if ((term_int == 1) || (getScan() == Common::KEYCODE_ESCAPE) || shouldQuit()) - break; - if (animate("lev.bin", 15)) break; @@ -520,8 +498,6 @@ void DrasculaEngine::animation_2_1() { break; talk_bj(12); gotoObject(157, 98 + curHeight); - if ((term_int == 1) || (getScan() == Common::KEYCODE_ESCAPE) || shouldQuit()) - break; if (animate("bes.bin", 16)) break; playMusic(11); @@ -563,7 +539,6 @@ void DrasculaEngine::animation_2_1() { } } -// John Hacker talks with the bartender to book a room void DrasculaEngine::animation_3_1() { debug(4, "animation_3_1()"); @@ -574,7 +549,6 @@ void DrasculaEngine::animation_3_1() { loadPic(97, extraSurface); } -// John Hacker talks with the pianist void DrasculaEngine::animation_4_1() { debug(4, "animation_4_1()"); @@ -676,26 +650,39 @@ void DrasculaEngine::animation_4_2() { pause(5); talk_hacker(57); pause(6); - talk_blind(2); - pause(4); - talk_hacker(58); - talk_blind(3); - delay(14); - talk_hacker(59); - talk_blind(4); - talk_hacker(60); - talk_blind(5); - talk_hacker(61); - talk_blind(6); - talk_hacker(62); - talk_blind(7); - talk_hacker(63); - talk_blind(8); - copyBackground(); - updateScreen(); - _system->delayMillis(1000); - talk_hacker(64); - talk_blind(9); + + if (flags[4] == 0) { // first time + talk_blind(2); + pause(4); + talk_hacker(58); + talk_blind(3); + delay(14); + talk_hacker(59); + talk_blind(4); + talk_hacker(60); + talk_blind(5); + talk_hacker(61); + talk_blind(6); + talk_hacker(62); + talk_blind(7); + talk_hacker(63); + talk_blind(8); + copyBackground(); + updateScreen(); + _system->delayMillis(1000); + talk_hacker(64); + talk_blind(9); + + flags[4] = 1; // talked to the blind man + } else { // second time + _system->delayMillis(1000); + talk_blind(10); + talk_hacker(65); + + flags[33] = 1; + } + + flags[9] = 0; copyBackground(); updateScreen(); @@ -711,9 +698,6 @@ void DrasculaEngine::animation_4_2() { loadPic(97, extraSurface); loadPic(99, backSurface); selectVerb(kVerbNone); - - flags[9] = 0; - flags[4] = 1; } void DrasculaEngine::animation_14_2() { @@ -798,17 +782,22 @@ void DrasculaEngine::animation_16_2() { return; } - delay(3000); + uint32 now = _system->getMillis(); + while (_system->getMillis() - now < 3000 * 2) { + delay(50); + if (getScan() != 0) { + asco(); + return; + } + } if (i < 4) { fadeToBlack(1); - + clearRoom(); if (getScan() != 0) { asco(); return; } - - clearRoom(); } } @@ -818,6 +807,7 @@ void DrasculaEngine::animation_16_2() { for (int l = 1; l < 200; l++) { copyBackground(0, 0, 0, l, 320, 200 - l, drawSurface3, screenSurface); copyBackground(0, 200 - l, 0, 0, 320, l, bgSurface, screenSurface); + delay(10); updateScreen(); if (getScan() != 0) { asco(); @@ -867,6 +857,20 @@ void DrasculaEngine::animation_20_2() { void DrasculaEngine::animation_23_2() { debug(4, "animation_23_2()"); + int p_x, p_y; + int maxN; + int animX, animY; + + // Animation offsets - without earplugs + int johnFrameX[] = {1, 38, 75, 112, 75, 112, 75, 112, 149, 112, 149, 112, 149, 186, 223, 260, + 1, 38, 75, 112, 149, 112, 149, 112, 149, 112, 149, 186, 223, 260, 260, 260, 260, 223}; + int johnFrameY[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 1, 1, 1, 1}; + + // Animation offsets - with earplugs + int john2FrameX[] = {1, 35, 69, 103, 137, 171, 205, 239, 273, 1, 35, 69, 103, 137}; + int john2FrameY[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 73, 73, 73, 73, 73}; + loadPic("an24.alg", frontSurface); flags[21] = 1; @@ -893,10 +897,41 @@ void DrasculaEngine::animation_23_2() { trackVonBraun = 1; talk_vonBraun(18, kVonBraunNormal); - if (flags[29] == 0) - animation_23_joined(); - else - animation_23_joined2(); + if (flags[29] == 0) { + // John isn't wearing earplugs + p_x = curX + 2; + p_y = curY - 3; + maxN = 34; + animX = 36; + animY = 74; + + loadPic("an23.alg", backSurface); + } else { + // John is wearing earplugs + p_x = curX + 4; + p_y = curY; + maxN = 14; + animX = 33; + animY = 71; + + pause(50); + + loadPic("an23_2.alg", backSurface); + } + + for (int n = 0; n < maxN; n++) { + copyRect(p_x, p_y, p_x, p_y, animX, animY, bgSurface, screenSurface); + if (flags[29] == 0) + copyRect(johnFrameX[n], johnFrameY[n], p_x, p_y, animX, animY, backSurface, screenSurface); + else + copyRect(john2FrameX[n], john2FrameY[n], p_x, p_y, animX, animY, backSurface, screenSurface); + updateRefresh(); + updateScreen(p_x, p_y, p_x, p_y, animX, animY, screenSurface); + updateEvents(); + pause(5); + } + + loadPic(99, backSurface); trackVonBraun = 2; animation_25_2(); @@ -921,52 +956,6 @@ void DrasculaEngine::animation_23_2() { breakOut = 1; } -void DrasculaEngine::animation_23_joined() { - debug(4, "animation_23_joined()"); - - int p_x = curX + 2, p_y = curY - 3; - int x[] = {1, 38, 75, 112, 75, 112, 75, 112, 149, 112, 149, 112, 149, 186, 223, 260, - 1, 38, 75, 112, 149, 112, 149, 112, 149, 112, 149, 186, 223, 260, 260, 260, 260, 223}; - int y[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 76, 76, 76, 76, 76, 76, 76, - 76, 76, 76, 76, 76, 76, 76, 1, 1, 1, 1}; - - loadPic("an23.alg", backSurface); - - for (int n = 0; n < 34; n++) { - copyRect(p_x, p_y, p_x, p_y, 36, 74, bgSurface, screenSurface); - copyRect(x[n], y[n], p_x, p_y, 36, 74, backSurface, screenSurface); - updateRefresh(); - updateScreen(p_x, p_y, p_x, p_y, 36, 74, screenSurface); - updateEvents(); - pause(5); - } - - loadPic(99, backSurface); -} - -void DrasculaEngine::animation_23_joined2() { - debug(4, "animation_23_joined2()"); - - int p_x = curX + 4, p_y = curY; - int x[] = {1, 35, 69, 103, 137, 171, 205, 239, 273, 1, 35, 69, 103, 137}; - int y[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 73, 73, 73, 73, 73}; - - pause(50); - - loadPic("an23_2.alg", backSurface); - - for (int n = 0; n < 14; n++) { - copyRect(p_x, p_y, p_x, p_y, 33, 71, bgSurface, screenSurface); - copyRect(x[n], y[n], p_x, p_y, 33, 71, backSurface, screenSurface); - updateRefresh(); - updateScreen(p_x,p_y, p_x,p_y, 33,71, screenSurface); - updateEvents(); - pause(5); - } - - loadPic(99, backSurface); -} - void DrasculaEngine::animation_25_2() { debug(4, "animation_25_2()"); @@ -1091,31 +1080,11 @@ void DrasculaEngine::animation_35_2() { fadeToBlack(2); } -// Use cross on Yoda void DrasculaEngine::animation_2_3() { debug(4, "animation_2_3()"); flags[0] = 1; playMusic(13); - animation_3_3(); - playMusic(13); - animation_4_3(); - flags[1] = 1; - updateRoom(); - updateScreen(120, 0, 120, 0, 200, 200, screenSurface); - animation_5_3(); - flags[0] = 0; - flags[1] = 1; - - loadPic(96, frontSurface); - loadPic(97, extraSurface); - loadPic(99, backSurface); - - gotoObject(332, 127); -} - -void DrasculaEngine::animation_3_3() { - debug(4, "animation_3_3()"); int px = curX - 20, py = curY - 1; @@ -1129,12 +1098,11 @@ void DrasculaEngine::animation_3_3() { updateAnim(75, px, py, 71, 72, 4, extraSurface, 3, true); updateAnim(2, px, py, 71, 72, 4, backSurface, 3, true); updateAnim(75, px, py, 71, 72, 4, backSurface, 3, true); -} -void DrasculaEngine::animation_4_3() { - debug(4, "animation_4_3()"); + playMusic(13); - int px = 120, py = 63; + px = 120; + py = 63; loadPic("any_1.alg", frontSurface); loadPic("any_2.alg", extraSurface); @@ -1146,12 +1114,13 @@ void DrasculaEngine::animation_4_3() { updateAnim(91, px, py, 77, 89, 4, extraSurface, 3, true); updateAnim(1, px, py, 77, 89, 4, backSurface, 3, true); updateAnim(91, px, py, 77, 89, 4, backSurface, 3, true); -} -void DrasculaEngine::animation_5_3() { - debug(4, "animation_5_3()"); + flags[1] = 1; + updateRoom(); + updateScreen(120, 0, 120, 0, 200, 200, screenSurface); - int px = curX - 20, py = curY - 1; + px = curX - 20; + py = curY - 1; loadPic("an3y_1.alg", frontSurface); loadPic("an3y_2.alg", extraSurface); @@ -1163,6 +1132,15 @@ void DrasculaEngine::animation_5_3() { updateAnim(75, px, py, 71, 72, 4, extraSurface, 3, true); updateAnim(2, px, py, 71, 72, 4, backSurface, 3, true); updateAnim(75, px, py, 71, 72, 4, backSurface, 3, true); + + flags[0] = 0; + flags[1] = 1; + + loadPic(96, frontSurface); + loadPic(97, extraSurface); + loadPic(99, backSurface); + + gotoObject(332, 127); } void DrasculaEngine::animation_6_3() { @@ -1197,8 +1175,8 @@ void DrasculaEngine::animation_6_3() { updateScreen(); } -void DrasculaEngine::animation_ray() { - debug(4, "animation_ray()"); +void DrasculaEngine::animation_castle() { + debug(4, "animation_castle()"); loadPic("anr_1.alg", frontSurface, HALF_PAL); loadPic("anr_2.alg", extraSurface); @@ -1365,18 +1343,6 @@ void DrasculaEngine::animation_5_5(){ loadPic(49, bgSurface, HALF_PAL); } -void DrasculaEngine::animation_11_5() { - debug(4, "animation_11_5()"); - - flags[9] = 1; - if (flags[2] == 1 && flags[3] == 1 && flags[4] == 1) - animation_12_5(); - else { - flags[9] = 0; - talk(33); - } -} - void DrasculaEngine::animation_12_5() { debug(4, "animation_12_5()"); @@ -1426,7 +1392,7 @@ void DrasculaEngine::animation_12_5() { loadPic("3an11_1.alg", backSurface); for (frame = 0; frame < 8; frame++) { - if (frame == 2 || frame == 4 || frame == 8 || frame==10) + if (frame == 2 || frame == 4 || frame == 8 || frame == 10) setPalette((byte *)&bgPalette1); else if (frame == 1 || frame == 5 || frame == 7 || frame == 9) setPalette((byte *)&bgPalette2); @@ -1465,7 +1431,8 @@ void DrasculaEngine::animation_12_5() { flags[1] = 1; - animation_13_5(); + animation_12_5_frankenstein(); + playSound(1); hiccup(12); finishSound(); @@ -1487,13 +1454,11 @@ void DrasculaEngine::animation_12_5() { enterRoom(57); } -void DrasculaEngine::animation_13_5() { - debug(4, "animation_13_5()"); - +void DrasculaEngine::animation_12_5_frankenstein() { int frank_x = 199; - int frame = 0; int frus_x[] = {1, 46, 91, 136, 181, 226, 271}; int frus_y[] = {1, 1, 1, 1, 1, 1, 1, 89}; + int frame = 0; loadPic("auxfr.alg", backSurface); @@ -1898,27 +1863,6 @@ void DrasculaEngine::animation_24_2() { loadPic("an24.alg", frontSurface); - animation_32_2(); - - flags[21] = 1; - - talk_vonBraun(22, kVonBraunNormal); - - if (flags[22] == 0) - converse(4); - else - converse(5); - - exitRoom(0); - flags[21] = 0; - flags[24] = 0; - trackVonBraun = 1; - vonBraunX = 120; -} - -void DrasculaEngine::animation_32_2() { - debug(4, "animation_32_2()"); - loadPic("an32_1.alg", drawSurface3); loadPic("an32_2.alg", backSurface); @@ -1939,6 +1883,21 @@ void DrasculaEngine::animation_32_2() { } loadPic("aux18.alg", drawSurface3); + + flags[21] = 1; + + talk_vonBraun(22, kVonBraunNormal); + + if (flags[22] == 0) + converse(4); + else + converse(5); + + exitRoom(0); + flags[21] = 0; + flags[24] = 0; + trackVonBraun = 1; + vonBraunX = 120; } void DrasculaEngine::animation_34_2() { @@ -2126,55 +2085,6 @@ void DrasculaEngine::animation_6_2() { flags[9] = 0; } -void DrasculaEngine::animation_33_2() { - debug(4, "animation_33_2()"); - - stopMusic(); - flags[9] = 1; - - pause(12); - talk(60); - pause(8); - - clearRoom(); - loadPic("ciego1.alg", bgSurface, HALF_PAL); // ciego = blind - loadPic("ciego2.alg", drawSurface3); - loadPic("ciego3.alg", extraSurface); - loadPic("ciego4.alg", backSurface); - loadPic("ciego5.alg", frontSurface); - - copyBackground(); - updateScreen(); - - pause(10); - - talk_blind(1); - pause(5); - talk_hacker(57); - pause(6); - _system->delayMillis(1000); - talk_blind(10); - talk_hacker(65); - - copyBackground(); - updateScreen(); - - pause(14); - - clearRoom(); - - playMusic(roomMusic); - loadPic(9, bgSurface, HALF_PAL); - loadPic("aux9.alg", drawSurface3); - loadPic(96, frontSurface); - loadPic(97, extraSurface); - loadPic(99, backSurface); - selectVerb(kVerbNone); - - flags[33] = 1; - flags[9] = 0; -} - void DrasculaEngine::animation_1_4() { debug(4, "animation_1_4()"); diff --git a/engines/drascula/console.cpp b/engines/drascula/console.cpp index d2fd32f2e5..426b2ade67 100644 --- a/engines/drascula/console.cpp +++ b/engines/drascula/console.cpp @@ -46,7 +46,6 @@ bool Console::Cmd_Room(int argc, const char **argv) { _vm->selectVerb(kVerbNone); _vm->clearRoom(); _vm->loadPic(roomNum, _vm->bgSurface, HALF_PAL); - _vm->selectionMade = 0; return false; } diff --git a/engines/drascula/converse.cpp b/engines/drascula/converse.cpp index 7abbb3214b..d045d683fc 100644 --- a/engines/drascula/converse.cpp +++ b/engines/drascula/converse.cpp @@ -167,7 +167,6 @@ void DrasculaEngine::converse(int index) { // no need to delete the stream, since TextResourceParser takes ownership // delete stream; - if (currentChapter == 2 && !strcmp(fileName, "op_5.cal") && flags[38] == 1 && flags[33] == 1) { strcpy(phrase3, _text[405]); strcpy(sound3, "405.als"); diff --git a/engines/drascula/detection.cpp b/engines/drascula/detection.cpp index 6e38d49b94..e1f69e2158 100644 --- a/engines/drascula/detection.cpp +++ b/engines/drascula/detection.cpp @@ -21,10 +21,13 @@ */ #include "base/plugins.h" +#include "common/file.h" +#include "common/translation.h" #include "engines/advancedDetector.h" #include "engines/savestate.h" -#include "common/file.h" + +#include "graphics/thumbnail.h" #include "drascula/drascula.h" @@ -263,81 +266,123 @@ static const DrasculaGameDescription gameDescriptions[] = { { AD_TABLE_END_MARKER } }; -} // End of namespace Drascula +static const ExtraGuiOption drasculaExtraGuiOption = { + _s("Use original save/load screens"), + _s("Use the original save/load screens, instead of the ScummVM ones"), + "originalsaveload", + false +}; + +SaveStateDescriptor loadMetaData(Common::ReadStream *s, int slot, bool setPlayTime); class DrasculaMetaEngine : public AdvancedMetaEngine { public: DrasculaMetaEngine() : AdvancedMetaEngine(Drascula::gameDescriptions, sizeof(Drascula::DrasculaGameDescription), drasculaGames) { _singleid = "drascula"; - _guioptions = GUIO2(GUIO_NOMIDI, GUIO_NOLAUNCHLOAD); + _guioptions = GUIO1(GUIO_NOMIDI); + } + + virtual const char *getName() const { + return "Drascula"; } - virtual bool hasFeature(MetaEngineFeature f) const { - return (f == kSupportsListSaves); + virtual const char *getOriginalCopyright() const { + return "Drascula Engine (C) 2000 Alcachofa Soft, (C) 1996 Digital Dreams Multimedia, (C) 1994 Emilio de Paz"; } - virtual SaveStateList listSaves(const char *target) const { - Common::SaveFileManager *saveFileMan = g_system->getSavefileManager(); - Common::String pattern = Common::String::format("%s??", target); - - // Get list of savefiles for target game - Common::StringArray filenames = saveFileMan->listSavefiles(pattern); - Common::Array<int> slots; - for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) { - - // Obtain the last 2 digits of the filename, since they correspond to the save slot - int slotNum = atoi(file->c_str() + file->size() - 2); - - // Ensure save slot is within valid range - if (slotNum >= 1 && slotNum <= 10) { - slots.push_back(slotNum); + virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const; + virtual bool hasFeature(MetaEngineFeature f) const; + virtual const ExtraGuiOptions getExtraGuiOptions(const Common::String &target) const; + virtual SaveStateList listSaves(const char *target) const; + virtual int getMaximumSaveSlot() const; + virtual void removeSaveState(const char *target, int slot) const; + SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const; +}; + +bool DrasculaMetaEngine::hasFeature(MetaEngineFeature f) const { + return + (f == kSupportsListSaves) || + (f == kSupportsLoadingDuringStartup) || + (f == kSupportsDeleteSave) || + (f == kSavesSupportMetaInfo) || + (f == kSavesSupportThumbnail) || + (f == kSavesSupportCreationDate) || + (f == kSavesSupportPlayTime); +} + +const ExtraGuiOptions DrasculaMetaEngine::getExtraGuiOptions(const Common::String &target) const { + ExtraGuiOptions options; + options.push_back(drasculaExtraGuiOption); + return options; +} + +SaveStateList DrasculaMetaEngine::listSaves(const char *target) const { + Common::SaveFileManager *saveFileMan = g_system->getSavefileManager(); + Common::StringArray filenames; + Common::String pattern = target; + pattern += ".???"; + + filenames = saveFileMan->listSavefiles(pattern); + sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..) + + SaveStateList saveList; + int slotNum = 0; + for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) { + // Obtain the last 3 digits of the filename, since they correspond to the save slot + slotNum = atoi(file->c_str() + file->size() - 3); + + if (slotNum >= 0 && slotNum <= getMaximumSaveSlot()) { + Common::InSaveFile *in = saveFileMan->openForLoading(*file); + if (in) { + SaveStateDescriptor desc = loadMetaData(in, slotNum, false); + if (desc.getSaveSlot() != slotNum) { + // invalid + delete in; + continue; + } + saveList.push_back(desc); + delete in; } } + } + + return saveList; +} + +SaveStateDescriptor DrasculaMetaEngine::querySaveMetaInfos(const char *target, int slot) const { + char fileName[MAXPATHLEN]; + sprintf(fileName, "%s.%03d", target, slot); + + Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(fileName); - // Sort save slot ids - Common::sort<int>(slots.begin(), slots.end()); - - // Load save index - Common::String fileEpa = Common::String::format("%s.epa", target); - Common::InSaveFile *epa = saveFileMan->openForLoading(fileEpa); - - // Get savegame names from index - Common::String saveDesc; - SaveStateList saveList; - int line = 1; - for (size_t i = 0; i < slots.size(); i++) { - // ignore lines corresponding to unused saveslots - for (; line < slots[i]; line++) - epa->readLine(); - - // copy the name in the line corresponding to the save slot and truncate to 22 characters - saveDesc = Common::String(epa->readLine().c_str(), 22); - - // handle cases where the save directory and save index are detectably out of sync - if (saveDesc == "*") - saveDesc = "No name specified."; - - // increment line number to keep it in sync with slot number - line++; - - // Insert savegame name into list - saveList.push_back(SaveStateDescriptor(slots[i], saveDesc)); + SaveStateDescriptor desc; + // Do not allow save slot 0 (used for auto-saving) to be deleted or + // overwritten. + desc.setDeletableFlag(slot != 0); + desc.setWriteProtectedFlag(slot == 0); + + if (in) { + desc = Drascula::loadMetaData(in, slot, false); + if (desc.getSaveSlot() != slot) { + delete in; + return SaveStateDescriptor(); } - delete epa; - return saveList; - } + Graphics::Surface *const thumbnail = Graphics::loadThumbnail(*in); + desc.setThumbnail(thumbnail); - virtual const char *getName() const { - return "Drascula"; + delete in; } - virtual const char *getOriginalCopyright() const { - return "Drascula Engine (C) 2000 Alcachofa Soft, (C) 1996 Digital Dreams Multimedia, (C) 1994 Emilio de Paz"; - } + return desc; +} - virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const; -}; +int DrasculaMetaEngine::getMaximumSaveSlot() const { return 999; } + +void DrasculaMetaEngine::removeSaveState(const char *target, int slot) const { + Common::String fileName = Common::String::format("%s.%03d", target, slot); + g_system->getSavefileManager()->removeSavefile(fileName); +} bool DrasculaMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const { const Drascula::DrasculaGameDescription *gd = (const Drascula::DrasculaGameDescription *)desc; @@ -347,8 +392,10 @@ bool DrasculaMetaEngine::createInstance(OSystem *syst, Engine **engine, const AD return gd != 0; } +} // End of namespace Drascula + #if PLUGIN_ENABLED_DYNAMIC(DRASCULA) - REGISTER_PLUGIN_DYNAMIC(DRASCULA, PLUGIN_TYPE_ENGINE, DrasculaMetaEngine); + REGISTER_PLUGIN_DYNAMIC(DRASCULA, PLUGIN_TYPE_ENGINE, Drascula::DrasculaMetaEngine); #else - REGISTER_PLUGIN_STATIC(DRASCULA, PLUGIN_TYPE_ENGINE, DrasculaMetaEngine); + REGISTER_PLUGIN_STATIC(DRASCULA, PLUGIN_TYPE_ENGINE, Drascula::DrasculaMetaEngine); #endif diff --git a/engines/drascula/drascula.cpp b/engines/drascula/drascula.cpp index 1b3c4038f0..804881cf9a 100644 --- a/engines/drascula/drascula.cpp +++ b/engines/drascula/drascula.cpp @@ -87,6 +87,7 @@ DrasculaEngine::DrasculaEngine(OSystem *syst, const DrasculaGameDescription *gam _textmisc = 0; _textd1 = 0; _talkSequences = 0; + _currentSaveSlot = 0; _color = 0; blinking = 0; @@ -187,6 +188,8 @@ Common::Error DrasculaEngine::run() { if (!loadDrasculaDat()) return Common::kUnknownError; + checkForOldSaveGames(); + setupRoomsTable(); loadArchives(); @@ -195,6 +198,13 @@ Common::Error DrasculaEngine::run() { currentChapter = 1; // values from 1 to 6 will start each part of game loadedDifferentChapter = 0; + setTotalPlayTime(0); + + // Check if a save is loaded from the launcher + int directSaveSlotLoading = ConfMan.getInt("save_slot"); + if (directSaveSlotLoading >= 0) { + loadGame(directSaveSlotLoading); + } checkCD(); @@ -233,7 +243,6 @@ Common::Error DrasculaEngine::run() { framesWithoutAction = 0; term_int = 0; musicStopped = 0; - selectionMade = 0; globalSpeed = 0; curExcuseLook = 0; curExcuseAction = 0; @@ -246,7 +255,6 @@ Common::Error DrasculaEngine::run() { allocMemory(); _subtitlesDisabled = !ConfMan.getBool("subtitles"); - selectionMade = 0; if (currentChapter != 3) loadPic(96, frontSurface, COMPLETE_PAL); @@ -261,7 +269,7 @@ Common::Error DrasculaEngine::run() { loadPic(96, frontSurface); } else if (currentChapter == 4) { if (loadedDifferentChapter == 0) - animation_ray(); + animation_castle(); loadPic(96, frontSurface); clearRoom(); } else if (currentChapter == 5) { @@ -295,6 +303,7 @@ Common::Error DrasculaEngine::run() { strcpy(iconName[i + 1], _textverbs[i]); assignPalette(defaultPalette); + if (!runCurrentChapter()) { endChapter(); break; @@ -359,7 +368,7 @@ bool DrasculaEngine::runCurrentChapter() { trackProtagonist = 1; objExit = 104; if (loadedDifferentChapter != 0) { - if (!loadGame(saveName)) { + if (!loadGame(_currentSaveSlot)) { return true; } } else { @@ -368,6 +377,8 @@ bool DrasculaEngine::runCurrentChapter() { curY = 56; gotoObject(65, 145); } + + // REMINDER: This is a good place to debug animations } else if (currentChapter == 2) { addObject(kItemPhone); trackProtagonist = 3; @@ -375,7 +386,7 @@ bool DrasculaEngine::runCurrentChapter() { if (loadedDifferentChapter == 0) enterRoom(14); else { - if (!loadGame(saveName)) { + if (!loadGame(_currentSaveSlot)) { return true; } } @@ -393,7 +404,7 @@ bool DrasculaEngine::runCurrentChapter() { if (loadedDifferentChapter == 0) enterRoom(20); else { - if (!loadGame(saveName)) { + if (!loadGame(_currentSaveSlot)) { return true; } } @@ -410,7 +421,7 @@ bool DrasculaEngine::runCurrentChapter() { curX = 235; curY = 164; } else { - if (!loadGame(saveName)) { + if (!loadGame(_currentSaveSlot)) { return true; } } @@ -429,7 +440,7 @@ bool DrasculaEngine::runCurrentChapter() { if (loadedDifferentChapter == 0) { enterRoom(45); } else { - if (!loadGame(saveName)) { + if (!loadGame(_currentSaveSlot)) { return true; } } @@ -443,7 +454,7 @@ bool DrasculaEngine::runCurrentChapter() { enterRoom(58); animation_1_6(); } else { - if (!loadGame(saveName)) { + if (!loadGame(_currentSaveSlot)) { return true; } loadPic("auxdr.alg", drawSurface2); @@ -596,13 +607,23 @@ bool DrasculaEngine::runCurrentChapter() { selectVerb(kVerbTalk); } else if (key == Common::KEYCODE_F6 && !_menuScreen) { selectVerb(kVerbMove); - } else if (key == Common::KEYCODE_F9) { - volumeControls(); - } else if (key == Common::KEYCODE_F10) { - if (!saveLoadScreen()) + } else if (key == Common::KEYCODE_F7) { + // ScummVM load screen + if (!scummVMSaveLoadDialog(false)) return true; } else if (key == Common::KEYCODE_F8) { selectVerb(kVerbNone); + } else if (key == Common::KEYCODE_F9) { + volumeControls(); + } else if (key == Common::KEYCODE_F10) { + if (!ConfMan.getBool("originalsaveload")) { + // ScummVM save screen + scummVMSaveLoadDialog(true); + } else { + // Original save/load screen + if (!saveLoadScreen()) + return true; + } } else if (key == Common::KEYCODE_v) { _subtitlesDisabled = true; ConfMan.setBool("subtitles", !_subtitlesDisabled); @@ -628,17 +649,14 @@ bool DrasculaEngine::runCurrentChapter() { } if (leftMouseButton != 0 || rightMouseButton != 0 || key != 0) - if (currentChapter != 3) - framesWithoutAction = 0; + framesWithoutAction = 0; if (framesWithoutAction == 15000) { screenSaver(); - if (currentChapter != 3) - framesWithoutAction = 0; + framesWithoutAction = 0; } - if (currentChapter != 3) - framesWithoutAction++; + framesWithoutAction++; } return false; @@ -652,8 +670,8 @@ bool DrasculaEngine::verify1() { removeObject(); else { for (l = 0; l < numRoomObjs; l++) { - if (mouseX >= x1[l] && mouseY >= y1[l] - && mouseX <= x2[l] && mouseY <= y2[l] && doBreak == 0) { + if (mouseX >= _objectX1[l] && mouseY >= _objectY1[l] + && mouseX <= _objectX2[l] && mouseY <= _objectY2[l] && doBreak == 0) { if (exitRoom(l)) return true; if (doBreak == 1) @@ -666,8 +684,8 @@ bool DrasculaEngine::verify1() { doBreak = 1; for (l = 0; l < numRoomObjs; l++) { - if (mouseX > x1[l] && mouseY > y1[l] - && mouseX < x2[l] && mouseY < y2[l] && doBreak == 0) { + if (mouseX > _objectX1[l] && mouseY > _objectY1[l] + && mouseX < _objectX2[l] && mouseY < _objectY2[l] && doBreak == 0) { roomX = roomObjX[l]; roomY = roomObjY[l]; trackFinal = trackObj[l]; @@ -700,8 +718,8 @@ bool DrasculaEngine::verify2() { return true; } else { for (l = 0; l < numRoomObjs; l++) { - if (mouseX > x1[l] && mouseY > y1[l] - && mouseX < x2[l] && mouseY < y2[l] && visible[l] == 1) { + if (mouseX > _objectX1[l] && mouseY > _objectY1[l] + && mouseX < _objectX2[l] && mouseY < _objectY2[l] && visible[l] == 1) { trackFinal = trackObj[l]; walkToObject = 1; gotoObject(roomObjX[l], roomObjY[l]); diff --git a/engines/drascula/drascula.h b/engines/drascula/drascula.h index 2d1954e3ca..e547503bee 100644 --- a/engines/drascula/drascula.h +++ b/engines/drascula/drascula.h @@ -36,6 +36,8 @@ #include "common/system.h" #include "common/util.h" +#include "engines/savestate.h" + #include "audio/mixer.h" #include "engines/engine.h" @@ -415,7 +417,7 @@ public: int inventoryObjects[43]; char _targetSurface[40][20]; int _destX[40], _destY[40], trackCharacter_alkeva[40], roomExits[40]; - int x1[40], y1[40], x2[40], y2[40]; + int _objectX1[40], _objectY1[40], _objectX2[40], _objectY2[40]; int takeObject, pickedObject; bool _subtitlesDisabled; bool _menuBar, _menuScreen, _hasName; @@ -453,11 +455,9 @@ public: int term_int; int currentChapter; int loadedDifferentChapter; - char saveName[13]; + int _currentSaveSlot; int _color; int musicStopped; - char select[23]; - int selectionMade; int mouseX; int mouseY; int leftMouseButton; @@ -494,9 +494,16 @@ public: void selectVerb(int); void updateVolume(Audio::Mixer::SoundType soundType, int prevVolume); void volumeControls(); + bool saveLoadScreen(); + bool scummVMSaveLoadDialog(bool isSave); + Common::String enterName(Common::String &selectedName); void loadSaveNames(); - void saveSaveNames(); + void saveGame(int slot, Common::String &desc); + bool loadGame(int slot); + void checkForOldSaveGames(); + void convertSaveGame(int slot, Common::String &desc); + void print_abc(const char *, int, int); void delay(int ms); bool confirmExit(); @@ -550,7 +557,6 @@ public: void updateMusic(); int musicStatus(); void updateRoom(); - bool loadGame(const char *); void updateDoor(int); void setPaletteBase(int darkness); void updateVisible(); @@ -568,7 +574,6 @@ public: void showCursor(); void hideCursor(); bool isCursorVisible(); - void enterName(); bool soundIsActive(); void waitFrameSSN(); void mixVideo(byte *OldScreen, byte *NewScreen, uint16 oldPitch); @@ -589,7 +594,6 @@ public: void quadrant_2(); void quadrant_3(); void quadrant_4(); - void saveGame(const char *gameName); void increaseFrameNum(); int whichObject(); bool checkMenuFlags(); @@ -650,63 +654,56 @@ public: bool room_62(int); bool room_102(int); - void animation_1_1(); - void animation_2_1(); - void animation_3_1(); - void animation_4_1(); - // - void animation_2_2(); - void animation_4_2(); - void animation_5_2(); - void animation_6_2(); - void animation_7_2(); - void animation_11_2(); - void animation_12_2(); - void animation_13_2(); - void animation_14_2(); void asco(); - void animation_16_2(); - void animation_20_2(); - void animation_23_2(); - void animation_23_joined(); - void animation_23_joined2(); - void animation_24_2(); - void animation_25_2(); - void animation_26_2(); - void animation_27_2(); - void animation_29_2(); - void animation_31_2(); - void animation_32_2(); - void animation_33_2(); - void animation_34_2(); - void animation_35_2(); - void animation_36_2(); + + void animation_1_1(); // Game introduction + void animation_2_1(); // John falls in love with BJ, who is then abducted by Drascula + void animation_3_1(); // John talks with the bartender to book a room + void animation_4_1(); // John talks with the pianist + // + void animation_2_2(); // John enters the chapel via the window + void animation_4_2(); // John talks with the blind man (closeup) + void animation_5_2(); // John breaks the chapel window with the pike + void animation_6_2(); // The blind man (closeup) thanks John for giving him money and hands him the sickle + void animation_7_2(); // John uses the sickle + void animation_11_2(); // The drunk man says "they're all dead, thanks *hic*" + void animation_12_2(); // Conversation screen - John talks to the pianist after BJ is abducted by Drascula + void animation_13_2(); // ??? + void animation_14_2(); // The glass box falls from the ceiling + void animation_16_2(); // The drunk tells us about Von Braun + void animation_20_2(); // Von Braun tells John that he needs to have special skills to fight vampires + void animation_23_2(); // Von Braun tests John's reactions to scratching noises + void animation_24_2(); // Conversation screen - John talks with Von Braun + void animation_25_2(); // The glass box is lifted back to the ceiling + void animation_26_2(); // John gives the book to the pianist and gets his earplugs in return + void animation_27_2(); // Von Braun admits that John is ready to fight vampires and gives him his money back + void animation_29_2(); // Von Braun tells John what ingredients he needs for the brew + void animation_31_2(); // Von Braun obtains the items needed for the brew from John and creates it + void animation_34_2(); // John kicks an object + void animation_35_2(); // John jumps into the well + void animation_36_2(); // John asks the bartender about the pianist // - void animation_2_3(); - void animation_3_3(); - void animation_4_3(); - void animation_5_3(); - void animation_6_3(); - void animation_ray(); + void animation_2_3(); // John uses the cross with the Frankenstein-zombie ("yoda") and destroys him + void animation_6_3(); // Frankenstein is blocking John's path // - void animation_1_4(); - void animation_5_4(); - void animation_6_4(); - void animation_7_4(); - void animation_8_4(); + void animation_castle(); // Chapter 4 start - Drascula's castle exterior, lightning strikes + void animation_1_4(); // Conversation screen - John talks with Igor + void animation_5_4(); // John enters Igor's room dressed as Drascula + void animation_6_4(); // Igor says that he's going for supper + void animation_7_4(); // John removes Drascula's disguise + void animation_8_4(); // Secret passage behind bookcase is revealed // - void animation_1_5(); - void animation_5_5(); - void animation_11_5(); - void animation_12_5(); - void animation_13_5(); - void animation_14_5(); + void animation_1_5(); // John finds BJ + void animation_5_5(); // ??? + void animation_12_5(); // Frankenstein comes to life + void animation_12_5_frankenstein(); + void animation_14_5(); // John finds out that an object is empty // - void animation_1_6(); - void animation_5_6(); - void animation_6_6(); - void animation_9_6(); - void animation_19_6(); + void animation_1_6(); // ??? + void animation_5_6(); // John is tied to the table. Drascula and Igor lower the pendulum + void animation_6_6(); // John uses the pendulum to break free + void animation_9_6(); // Game ending - John uses the cross on Drascula and reads BJ's letter + void animation_19_6(); // Someone pops up from behind a door when trying to open it void update_1_pre(); void update_2(); @@ -778,7 +775,7 @@ private: RoomUpdate *_roomPreUpdates, *_roomUpdates; RoomTalkAction *_roomActions; TalkSequenceCommand *_talkSequences; - char _saveNames[10][23]; + Common::String _saveNames[10]; char **loadTexts(Common::File &in); void freeTexts(char **ptr); diff --git a/engines/drascula/graphics.cpp b/engines/drascula/graphics.cpp index 590561f0bd..3bdf724670 100644 --- a/engines/drascula/graphics.cpp +++ b/engines/drascula/graphics.cpp @@ -668,7 +668,7 @@ bool DrasculaEngine::animate(const char *animationFile, int FPS) { } delete stream; - return ((term_int == 1) || (getScan() == Common::KEYCODE_ESCAPE)); + return ((term_int == 1) || (getScan() == Common::KEYCODE_ESCAPE) || shouldQuit()); } } // End of namespace Drascula diff --git a/engines/drascula/interface.cpp b/engines/drascula/interface.cpp index 4b8db63bb7..70212db9e8 100644 --- a/engines/drascula/interface.cpp +++ b/engines/drascula/interface.cpp @@ -153,52 +153,6 @@ void DrasculaEngine::clearMenu() { } } -void DrasculaEngine::enterName() { - Common::KeyCode key; - flushKeyBuffer(); - int v = 0, h = 0; - char select2[23]; - strcpy(select2, " "); - while (!shouldQuit()) { - select2[v] = '-'; - copyBackground(115, 14, 115, 14, 176, 9, bgSurface, screenSurface); - print_abc(select2, 117, 15); - updateScreen(); - - key = getScan(); - - if (key != 0) { - if (key >= 0 && key <= 0xFF && isAlpha(key)) - select2[v] = tolower(key); - else if ((key >= Common::KEYCODE_0 && key <= Common::KEYCODE_9) || key == Common::KEYCODE_SPACE) - select2[v] = key; - else if (key == Common::KEYCODE_ESCAPE) - break; - else if (key == Common::KEYCODE_RETURN) { - select2[v] = '\0'; - h = 1; - break; - } else if (key == Common::KEYCODE_BACKSPACE) - select2[v] = '\0'; - else - v--; - - if (key == Common::KEYCODE_BACKSPACE) - v--; - else - v++; - } - if (v == 22) - v = 21; - else if (v == -1) - v = 0; - } - if (h == 1) { - strcpy(select, select2); - selectionMade = 1; - } -} - bool DrasculaEngine::checkMenuFlags() { int n = whichObject(); if (n != 0) { @@ -213,8 +167,8 @@ void DrasculaEngine::showMap() { _hasName = false; for (int l = 0; l < numRoomObjs; l++) { - if (mouseX > x1[l] && mouseY > y1[l] - && mouseX < x2[l] && mouseY < y2[l] + if (mouseX > _objectX1[l] && mouseY > _objectY1[l] + && mouseX < _objectX2[l] && mouseY < _objectY2[l] && visible[l] == 1) { strcpy(textName, objName[l]); _hasName = true; diff --git a/engines/drascula/objects.cpp b/engines/drascula/objects.cpp index 07b8464de4..f9f68c3317 100644 --- a/engines/drascula/objects.cpp +++ b/engines/drascula/objects.cpp @@ -100,8 +100,8 @@ void DrasculaEngine::checkObjects() { int l, veo = 0; for (l = 0; l < numRoomObjs; l++) { - if (mouseX > x1[l] && mouseY > y1[l] - && mouseX < x2[l] && mouseY < y2[l] + if (mouseX > _objectX1[l] && mouseY > _objectY1[l] + && mouseX < _objectX2[l] && mouseY < _objectY2[l] && visible[l] == 1 && isDoor[l] == 0) { strcpy(textName, objName[l]); _hasName = true; diff --git a/engines/drascula/rooms.cpp b/engines/drascula/rooms.cpp index 9f725b6d76..9f707eaa07 100644 --- a/engines/drascula/rooms.cpp +++ b/engines/drascula/rooms.cpp @@ -374,16 +374,16 @@ bool DrasculaEngine::room_8(int fl) { } bool DrasculaEngine::room_9(int fl) { - if (pickedObject == kVerbTalk && fl == 51 && flags[4] == 0) + // Talking with the blind man + if (pickedObject == kVerbTalk && fl == 51) { animation_4_2(); - else if (pickedObject == kVerbTalk && fl == 51 && flags[4] == 1) - animation_33_2(); - else if (pickedObject == 7 && fl == 51) { + } else if (pickedObject == 7 && fl == 51) { animation_6_2(); removeObject(kItemMoney); - pickObject(14);} - else + pickObject(14); + } else { hasAnswer = 0; + } return true; } @@ -837,9 +837,9 @@ bool DrasculaEngine::room_35(int fl) { } bool DrasculaEngine::room_49(int fl) { - if (pickedObject == kVerbTalk && fl ==51) + if (pickedObject == kVerbTalk && fl == 51) converse(9); - else if ((pickedObject == 8 && fl == 51) || (pickedObject == 8 && fl == 203)) + else if (pickedObject == 8 && (fl == 51 || fl == 203)) animation_5_5(); else hasAnswer = 0; @@ -852,7 +852,13 @@ bool DrasculaEngine::room_53(int fl) { pickObject(16); visible[3] = 0; } else if (pickedObject == kVerbMove && fl == 123) { - animation_11_5(); + flags[9] = 1; + if (flags[2] == 1 && flags[3] == 1 && flags[4] == 1) { + animation_12_5(); + } else { + flags[9] = 0; + talk(33); + } } else if (pickedObject == 12 && fl == 52) { flags[3] = 1; talk(401); @@ -1649,7 +1655,7 @@ bool DrasculaEngine::room(int rN, int fl) { } } - // We did not find any parser, let default one work + // We did not find any parser, let the default one work hasAnswer = 0; } @@ -1706,10 +1712,10 @@ void DrasculaEngine::enterRoom(int roomIndex) { for (l = 0; l < numRoomObjs; l++) { p.parseInt(objectNum[l]); p.parseString(objName[l]); - p.parseInt(x1[l]); - p.parseInt(y1[l]); - p.parseInt(x2[l]); - p.parseInt(y2[l]); + p.parseInt(_objectX1[l]); + p.parseInt(_objectY1[l]); + p.parseInt(_objectX2[l]); + p.parseInt(_objectY2[l]); p.parseInt(roomObjX[l]); p.parseInt(roomObjY[l]); p.parseInt(trackObj[l]); diff --git a/engines/drascula/saveload.cpp b/engines/drascula/saveload.cpp index 35e3821dc4..ba4148fb76 100644 --- a/engines/drascula/saveload.cpp +++ b/engines/drascula/saveload.cpp @@ -21,218 +21,270 @@ */ #include "common/textconsole.h" +#include "common/translation.h" + +#include "engines/savestate.h" +#include "graphics/thumbnail.h" +#include "gui/message.h" +#include "gui/saveload.h" #include "drascula/drascula.h" namespace Drascula { -/** - * Loads the save names from the EPA index file. - * - * TODO: We should move the save names in their respective save files and get - * rid of this completely. A good example is the sword1 engine, which also used - * to have an index file for its saves, that has been removed. - * sword1 contains code that converts the old index-based saves into the new - * format without the index file, so we could apply this idea to drascula as - * well. - */ -void DrasculaEngine::loadSaveNames() { - Common::InSaveFile *sav; - Common::String fileEpa = Common::String::format("%s.epa", _targetName.c_str()); - - // Create and initialize the index file, if it doesn't exist - if (!(sav = _saveFileMan->openForLoading(fileEpa))) { - Common::OutSaveFile *epa; - if (!(epa = _saveFileMan->openForSaving(fileEpa))) - error("Can't open %s file", fileEpa.c_str()); - for (int n = 0; n < NUM_SAVES; n++) - epa->writeString("*\n"); - epa->finalize(); - delete epa; - if (!(sav = _saveFileMan->openForLoading(fileEpa))) { - error("Can't open %s file", fileEpa.c_str()); - } +#define MAGIC_HEADER 0xD6A55A57 // (D)rascula (GA)me (S)cummVM (SA)ve (ST)ate +#define SAVEGAME_VERSION 1 + +void DrasculaEngine::checkForOldSaveGames() { + Common::String indexFileName = Common::String::format("%s.epa", _targetName.c_str()); + Common::InSaveFile *indexFile = _saveFileMan->openForLoading(indexFileName); + + // Check for the existence of an old index file + if (!indexFile) { + delete indexFile; + return; } - // Load the index file - for (int n = 0; n < NUM_SAVES; n++) { - strncpy(_saveNames[n], sav->readLine().c_str(), 23); - _saveNames[n][22] = '\0'; // make sure the savegame name is 0-terminated + GUI::MessageDialog dialog0( + _("ScummVM found that you have old savefiles for Drascula that should be converted.\n" + "The old save game format is no longer supported, so you will not be able to load your games if you don't convert them.\n\n" + "Press OK to convert them now, otherwise you will be asked again the next time you start the game.\n"), _("OK"), _("Cancel")); + + int choice = dialog0.runModal(); + if (choice == GUI::kMessageCancel) + return; + + // Convert every save slot we find in the index file to the new format + Common::SaveFileManager *saveFileMan = g_system->getSavefileManager(); + Common::String pattern = Common::String::format("%s??", _targetName.c_str()); + + // Get list of savefiles for target game + Common::StringArray filenames = saveFileMan->listSavefiles(pattern); + Common::Array<int> slots; + for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) { + // Obtain the last 2 digits of the filename, since they correspond to the save slot + int slotNum = atoi(file->c_str() + file->size() - 2); + + // Ensure save slot is within valid range + if (slotNum >= 1 && slotNum <= 10) { + slots.push_back(slotNum); + } } - delete sav; -} -/** - * Saves the save names into the EPA index file. - * - * TODO: We should move the save names in their respective save files and get - * rid of this completely. A good example is the sword1 engine, which also used - * to have an index file for its saves, that has been removed. - * sword1 contains code that converts the old index-based saves into the new - * format without the index file, so we could apply this idea to drascula as - * well. - */ -void DrasculaEngine::saveSaveNames() { - Common::OutSaveFile *tsav; - Common::String fileEpa = Common::String::format("%s.epa", _targetName.c_str()); + // Sort save slot ids + Common::sort<int>(slots.begin(), slots.end()); - if (!(tsav = _saveFileMan->openForSaving(fileEpa))) { - error("Can't open %s file", fileEpa.c_str()); - } - for (int n = 0; n < NUM_SAVES; n++) { - tsav->writeString(_saveNames[n]); - tsav->writeString("\n"); + // Get savegame names from index + Common::String saveDesc; + + int line = 1; + for (uint i = 0; i < slots.size(); i++) { + // Ignore lines corresponding to unused saveslots + for (; line < slots[i]; line++) + indexFile->readLine(); + + // Copy the name in the line corresponding to the save slot + saveDesc = indexFile->readLine(); + + // Handle cases where the save directory and save index are detectably out of sync + if (saveDesc == "*") + saveDesc = "No name specified."; + + // Increment line number to keep it in sync with slot number + line++; + + // Convert savegame + convertSaveGame(slots[i], saveDesc); } - tsav->finalize(); - delete tsav; -} -bool DrasculaEngine::saveLoadScreen() { - Common::String file; - int n, n2, num_sav = 0, y = 27; + delete indexFile; - clearRoom(); + // Remove index file + _saveFileMan->removeSavefile(indexFileName); +} - loadSaveNames(); +SaveStateDescriptor loadMetaData(Common::ReadStream *s, int slot, bool setPlayTime) { + uint32 sig = s->readUint32BE(); + byte version = s->readByte(); - loadPic("savescr.alg", bgSurface, HALF_PAL); + SaveStateDescriptor desc(-1, ""); // init to an invalid save slot - color_abc(kColorLightGreen); + if (sig != MAGIC_HEADER || version > SAVEGAME_VERSION) + return desc; - select[0] = 0; + // Save is valid, set its slot number + desc.setSaveSlot(slot); - _system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true); - setCursor(kCursorCrosshair); + Common::String name; + byte size = s->readByte(); + for (int i = 0; i < size; ++i) + name += s->readByte(); + desc.setDescription(name); - while (!shouldQuit()) { - y = 27; - copyBackground(); - for (n = 0; n < NUM_SAVES; n++) { - print_abc(_saveNames[n], 116, y); - y = y + 9; - } - print_abc(select, 117, 15); - updateScreen(); - y = 27; + uint32 saveDate = s->readUint32LE(); + int day = (saveDate >> 24) & 0xFF; + int month = (saveDate >> 16) & 0xFF; + int year = saveDate & 0xFFFF; + desc.setSaveDate(year, month, day); - updateEvents(); + uint16 saveTime = s->readUint16LE(); + int hour = (saveTime >> 8) & 0xFF; + int minutes = saveTime & 0xFF; + desc.setSaveTime(hour, minutes); - if (leftMouseButton == 1) { - delay(50); - for (n = 0; n < NUM_SAVES; n++) { - if (mouseX > 115 && mouseY > y + (9 * n) && mouseX < 115 + 175 && mouseY < y + 10 + (9 * n)) { - strcpy(select, _saveNames[n]); - - if (strcmp(select, "*") != 0) - selectionMade = 1; - else { - enterName(); - strcpy(_saveNames[n], select); - if (selectionMade == 1) { - file = Common::String::format("%s%02d", _targetName.c_str(), n + 1); - saveGame(file.c_str()); - saveSaveNames(); - } - } + uint32 playTime = s->readUint32LE(); + desc.setPlayTime(playTime * 1000); + if (setPlayTime) + g_engine->setTotalPlayTime(playTime * 1000); - print_abc(select, 117, 15); - y = 27; - for (n2 = 0; n2 < NUM_SAVES; n2++) { - print_abc(_saveNames[n2], 116, y); - y = y + 9; - } - if (selectionMade == 1) { - file = Common::String::format("%s%02d", _targetName.c_str(), n + 1); - } - num_sav = n; - } - } + return desc; +} - if (mouseX > 117 && mouseY > 15 && mouseX < 295 && mouseY < 24 && selectionMade == 1) { - enterName(); - strcpy(_saveNames[num_sav], select); - print_abc(select, 117, 15); - y = 27; - for (n2 = 0; n2 < NUM_SAVES; n2++) { - print_abc(_saveNames[n2], 116, y); - y = y + 9; - } +void saveMetaData(Common::WriteStream *s, Common::String &desc) { + TimeDate curTime; + g_system->getTimeAndDate(curTime); + + uint32 saveDate = ((curTime.tm_mday & 0xFF) << 24) | (((curTime.tm_mon + 1) & 0xFF) << 16) | ((curTime.tm_year + 1900) & 0xFFFF); + uint16 saveTime = ((curTime.tm_hour & 0xFF) << 8) | ((curTime.tm_min) & 0xFF); + uint32 playTime = g_engine->getTotalPlayTime() / 1000; + + s->writeUint32BE(MAGIC_HEADER); + s->writeByte(SAVEGAME_VERSION); + s->writeByte(desc.size()); + s->writeString(desc); + s->writeUint32LE(saveDate); + s->writeUint16LE(saveTime); + s->writeUint32LE(playTime); +} - if (selectionMade == 1) { - file = Common::String::format("%s%02d", _targetName.c_str(), n + 1); - saveGame(file.c_str()); - saveSaveNames(); - } - } +void DrasculaEngine::convertSaveGame(int slot, Common::String &desc) { + Common::String oldFileName = Common::String::format("%s%02d", _targetName.c_str(), slot); + Common::String newFileName = Common::String::format("%s.%03d", _targetName.c_str(), slot); + Common::InSaveFile *oldFile = _saveFileMan->openForLoading(oldFileName); + if (!oldFile) + error("Can't open %s", oldFileName.c_str()); + Common::OutSaveFile *newFile = _saveFileMan->openForSaving(newFileName); + if (!newFile) + error("Can't open %s", newFileName.c_str()); + + // Read data from old file + int32 dataSize = oldFile->size(); + byte *buffer = new byte[dataSize]; + oldFile->read(buffer, dataSize); + + // First, write the appropriate meta data in the new file + saveMetaData(newFile, desc); + Graphics::saveThumbnail(*newFile); // basically, at this point this will capture a black screen + + // And then attach the actual save data + newFile->write(buffer, dataSize); + newFile->finalize(); + if (newFile->err()) + warning("Can't write file '%s'. (Disk full?)", newFileName.c_str()); + + delete[] buffer; + delete newFile; + delete oldFile; + + // Remove old save file + _saveFileMan->removeSavefile(oldFileName); +} - if (mouseX > 125 && mouseY > 123 && mouseX < 199 && mouseY < 149 && selectionMade == 1) { - if (!loadGame(file.c_str())) { - _system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false); - return false; - } - break; - } else if (mouseX > 208 && mouseY > 123 && mouseX < 282 && mouseY < 149 && selectionMade == 1) { - saveGame(file.c_str()); - saveSaveNames(); - } else if (mouseX > 168 && mouseY > 154 && mouseX < 242 && mouseY < 180) - break; - else if (selectionMade == 0) { - print_abc("Please select a slot", 117, 15); - } - updateScreen(); - delay(200); +/** + * Loads the first 10 save names, to be used in Drascula's save/load screen + */ +void DrasculaEngine::loadSaveNames() { + Common::String saveFileName; + Common::InSaveFile *in; + + for (int n = 0; n < NUM_SAVES; n++) { + saveFileName = Common::String::format("%s.%03d", _targetName.c_str(), n + 1); + if ((in = _saveFileMan->openForLoading(saveFileName))) { + SaveStateDescriptor desc = loadMetaData(in, n + 1, false); + _saveNames[n] = desc.getDescription(); + delete in; } - y = 26; + } +} + +void DrasculaEngine::saveGame(int slot, Common::String &desc) { + Common::OutSaveFile *out; + int l; - delay(5); + Common::String saveFileName = Common::String::format("%s.%03d", _targetName.c_str(), slot); + if (!(out = _saveFileMan->openForSaving(saveFileName))) { + error("Unable to open the file"); } - selectVerb(kVerbNone); + saveMetaData(out, desc); + Graphics::saveThumbnail(*out); - clearRoom(); - loadPic(roomNumber, bgSurface, HALF_PAL); - selectionMade = 0; + // Actual save data follows + out->writeSint32LE(currentChapter); + out->write(currentData, 20); + out->writeSint32LE(curX); + out->writeSint32LE(curY); + out->writeSint32LE(trackProtagonist); - _system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false); + for (l = 1; l < ARRAYSIZE(inventoryObjects); l++) { + out->writeSint32LE(inventoryObjects[l]); + } - return true; + for (l = 0; l < NUM_FLAGS; l++) { + out->writeSint32LE(flags[l]); + } + + out->writeSint32LE(takeObject); + out->writeSint32LE(pickedObject); + + out->finalize(); + if (out->err()) + warning("Can't write file '%s'. (Disk full?)", saveFileName.c_str()); + + delete out; } -bool DrasculaEngine::loadGame(const char *gameName) { +bool DrasculaEngine::loadGame(int slot) { int l, savedChapter, roomNum = 0; - Common::InSaveFile *sav; + Common::InSaveFile *in; previousMusic = roomMusic; _menuScreen = false; if (currentChapter != 1) clearRoom(); - if (!(sav = _saveFileMan->openForLoading(gameName))) { - error("missing savegame file"); + Common::String saveFileName = Common::String::format("%s.%03d", _targetName.c_str(), slot); + if (!(in = _saveFileMan->openForLoading(saveFileName))) { + error("missing savegame file %s", saveFileName.c_str()); } - savedChapter = sav->readSint32LE(); + loadMetaData(in, slot, true); + Graphics::skipThumbnail(*in); + + savedChapter = in->readSint32LE(); if (savedChapter != currentChapter) { - strcpy(saveName, gameName); + _currentSaveSlot = slot; currentChapter = savedChapter - 1; loadedDifferentChapter = 1; + delete in; return false; } - sav->read(currentData, 20); - curX = sav->readSint32LE(); - curY = sav->readSint32LE(); - trackProtagonist = sav->readSint32LE(); + + in->read(currentData, 20); + curX = in->readSint32LE(); + curY = in->readSint32LE(); + trackProtagonist = in->readSint32LE(); for (l = 1; l < ARRAYSIZE(inventoryObjects); l++) { - inventoryObjects[l] = sav->readSint32LE(); + inventoryObjects[l] = in->readSint32LE(); } for (l = 0; l < NUM_FLAGS; l++) { - flags[l] = sav->readSint32LE(); + flags[l] = in->readSint32LE(); } - takeObject = sav->readSint32LE(); - pickedObject = sav->readSint32LE(); + takeObject = in->readSint32LE(); + pickedObject = in->readSint32LE(); loadedDifferentChapter = 0; if (!sscanf(currentData, "%d.ald", &roomNum)) { error("Bad save format"); @@ -243,35 +295,158 @@ bool DrasculaEngine::loadGame(const char *gameName) { return true; } -void DrasculaEngine::saveGame(const char *gameName) { - Common::OutSaveFile *out; - int l; +Common::String DrasculaEngine::enterName(Common::String &selectedName) { + Common::KeyCode key; + Common::String inputLine = selectedName; - if (!(out = _saveFileMan->openForSaving(gameName))) { - error("Unable to open the file"); + flushKeyBuffer(); + _system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true); + + while (!shouldQuit()) { + copyBackground(115, 14, 115, 14, 176, 9, bgSurface, screenSurface); + print_abc((inputLine + "-").c_str(), 117, 15); + updateScreen(); + + key = getScan(); + + if (key != 0) { + if (key >= 0 && key <= 0xFF && isAlpha(key)) { + inputLine += tolower(key); + } else if ((key >= Common::KEYCODE_0 && key <= Common::KEYCODE_9) || key == Common::KEYCODE_SPACE) { + inputLine += key; + } else if (key == Common::KEYCODE_ESCAPE) { + inputLine.clear(); + break; + } else if (key == Common::KEYCODE_RETURN) { + break; + } else if (key == Common::KEYCODE_BACKSPACE) { + inputLine.deleteLastChar(); + } + } } - out->writeSint32LE(currentChapter); - out->write(currentData, 20); - out->writeSint32LE(curX); - out->writeSint32LE(curY); - out->writeSint32LE(trackProtagonist); - for (l = 1; l < ARRAYSIZE(inventoryObjects); l++) { - out->writeSint32LE(inventoryObjects[l]); + _system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false); + return inputLine; +} + +bool DrasculaEngine::scummVMSaveLoadDialog(bool isSave) { + GUI::SaveLoadChooser *dialog; + Common::String desc; + int slot; + + if (isSave) { + dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"), true); + + slot = dialog->runModalWithCurrentTarget(); + desc = dialog->getResultString(); + + if (desc.empty()) { + // create our own description for the saved game, the user didnt enter it + desc = dialog->createDefaultSaveDescription(slot); + } + + if (desc.size() > 28) + desc = Common::String(desc.c_str(), 28); + } else { + dialog = new GUI::SaveLoadChooser(_("Restore game:"), _("Restore"), false); + slot = dialog->runModalWithCurrentTarget(); } - for (l = 0; l < NUM_FLAGS; l++) { - out->writeSint32LE(flags[l]); + delete dialog; + + if (slot < 0) + return true; + + if (isSave) { + saveGame(slot, desc); + return true; + } else { + return loadGame(slot); } +} - out->writeSint32LE(takeObject); - out->writeSint32LE(pickedObject); +bool DrasculaEngine::saveLoadScreen() { + int n, selectedSlot = 0; + Common::String selectedName; - out->finalize(); - if (out->err()) - warning("Can't write file '%s'. (Disk full?)", gameName); + clearRoom(); + loadPic("savescr.alg", bgSurface, HALF_PAL); + color_abc(kColorLightGreen); + setCursor(kCursorCrosshair); + loadSaveNames(); - delete out; + while (!shouldQuit()) { + copyBackground(); + for (n = 0; n < NUM_SAVES; n++) { + print_abc(_saveNames[n].c_str(), 116, 27 + 9 * n); + } + print_abc(selectedName.c_str(), 117, 15); + + updateScreen(); + updateEvents(); + + if (leftMouseButton == 1) { + // Check if the user has clicked on a save slot + for (n = 0; n < NUM_SAVES; n++) { + if (mouseX > 115 && mouseY > 27 + (9 * n) && mouseX < 115 + 175 && mouseY < 27 + 10 + (9 * n)) { + selectedSlot = n; + selectedName = _saveNames[selectedSlot]; + if (selectedName.empty()) { + selectedName = enterName(selectedName); + if (!selectedName.empty()) + _saveNames[selectedSlot] = selectedName; // update save name + } + break; + } + } + + // Check if the user has clicked in the text area above the save slots + if (mouseX > 117 && mouseY > 15 && mouseX < 295 && mouseY < 24 && !selectedName.empty()) { + selectedName = enterName(selectedName); + if (!selectedName.empty()) + _saveNames[selectedSlot] = selectedName; // update save name + } + + // Check if the user has clicked a button + if (mouseX > 208 && mouseY > 123 && mouseX < 282 && mouseY < 149) { + // "Save" button + if (selectedName.empty()) { + print_abc("Please select a slot", 117, 15); + updateScreen(); + delay(200); + } else { + selectVerb(kVerbNone); + clearRoom(); + loadPic(roomNumber, bgSurface, HALF_PAL); + updateRoom(); + updateScreen(); + + saveGame(selectedSlot + 1, _saveNames[selectedSlot]); + return true; + } + } else if (mouseX > 125 && mouseY > 123 && mouseX < 199 && mouseY < 149) { + // "Load" button + if (selectedName.empty()) { + print_abc("Please select a slot", 117, 15); + updateScreen(); + delay(200); + } else { + return loadGame(selectedSlot + 1); + } + } else if (mouseX > 168 && mouseY > 154 && mouseX < 242 && mouseY < 180) { + // "Play" button + break; + } + } // if (leftMouseButton == 1) + + leftMouseButton = 0; + delay(10); + } + + selectVerb(kVerbNone); + clearRoom(); + loadPic(roomNumber, bgSurface, HALF_PAL); + return true; } } // End of namespace Drascula diff --git a/engines/dreamweb/backdrop.cpp b/engines/dreamweb/backdrop.cpp index 1db2663624..5ccc68704a 100644 --- a/engines/dreamweb/backdrop.cpp +++ b/engines/dreamweb/backdrop.cpp @@ -25,35 +25,35 @@ namespace DreamWeb { void DreamWebEngine::doBlocks() { - uint16 dstOffset = _mapAdY * 320 + _mapAdX; + uint16 dstOffset = _mapAdY * kScreenwidth + _mapAdX; uint16 mapOffset = _mapY * kMapWidth + _mapX; const uint8 *mapData = _mapData + mapOffset; uint8 *dstBuffer = workspace() + dstOffset; - for (size_t i = 0; i < 10; ++i) { - for (size_t j = 0; j < 11; ++j) { + for (uint i = 0; i < 10; ++i) { + for (uint j = 0; j < 11; ++j) { uint16 blockType = mapData[j]; if (blockType != 0) { - uint8 *dst = dstBuffer + i * 320 * 16 + j * 16; + uint8 *dst = dstBuffer + i * kScreenwidth * 16 + j * 16; const uint8 *block = _backdropBlocks + blockType * 256; - for (size_t k = 0; k < 4; ++k) { + for (uint k = 0; k < 4; ++k) { memcpy(dst, block, 16); block += 16; - dst += 320; + dst += kScreenwidth; } - for (size_t k = 0; k < 12; ++k) { + for (uint k = 0; k < 12; ++k) { memcpy(dst, block, 16); memset(dst + 16, 0xdf, 4); block += 16; - dst += 320; + dst += kScreenwidth; } dst += 4; memset(dst, 0xdf, 16); - dst += 320; + dst += kScreenwidth; memset(dst, 0xdf, 16); - dst += 320; + dst += kScreenwidth; memset(dst, 0xdf, 16); - dst += 320; + dst += kScreenwidth; memset(dst, 0xdf, 16); } } @@ -129,7 +129,7 @@ void DreamWebEngine::showAllObs() { _setList.clear(); const GraphicsFile &frameBase = _setFrames; - for (size_t i = 0; i < 128; ++i) { + for (uint i = 0; i < 128; ++i) { SetObject *setEntry = &_setDat[i]; uint16 x, y; if (getMapAd(setEntry->mapad, &x, &y) == 0) @@ -154,7 +154,7 @@ void DreamWebEngine::showAllObs() { } static bool addAlong(const MapFlag *mapFlags) { - for (size_t i = 0; i < 11; ++i) { + for (uint i = 0; i < 11; ++i) { if (mapFlags[i]._flag != 0) return true; } @@ -162,7 +162,7 @@ static bool addAlong(const MapFlag *mapFlags) { } static bool addLength(const MapFlag *mapFlags) { - for (size_t i = 0; i < 10; ++i) { + for (uint i = 0; i < 10; ++i) { if (mapFlags[11 * i]._flag != 0) return true; } @@ -205,13 +205,13 @@ void DreamWebEngine::calcMapAd() { } void DreamWebEngine::showAllFree() { - const unsigned int count = 80; + const uint count = 80; _freeList.clear(); const DynObject *freeObjects = _freeDat; const GraphicsFile &frameBase = _freeFrames; - for (size_t i = 0; i < count; ++i) { + for (uint i = 0; i < count; ++i) { uint16 x, y; uint8 mapAd = getMapAd(freeObjects[i].mapad, &x, &y); if (mapAd != 0) { @@ -236,8 +236,8 @@ void DreamWebEngine::drawFlags() { uint16 mapOffset = _mapY * kMapWidth + _mapX; const uint8 *mapData = _mapData + mapOffset; - for (size_t i = 0; i < 10; ++i) { - for (size_t j = 0; j < 11; ++j) { + for (uint i = 0; i < 10; ++i) { + for (uint j = 0; j < 11; ++j) { uint8 tile = mapData[i * kMapWidth + j]; mapFlag->_flag = _backdropFlags[tile]._flag; mapFlag->_flagEx = _backdropFlags[tile]._flagEx; @@ -248,13 +248,13 @@ void DreamWebEngine::drawFlags() { } void DreamWebEngine::showAllEx() { - const unsigned int count = 100; + const uint count = 100; _exList.clear(); DynObject *objects = _exData; const GraphicsFile &frameBase = _exFrames; - for (size_t i = 0; i < count; ++i) { + for (uint i = 0; i < count; ++i) { DynObject *object = objects + i; if (object->mapad[0] == 0xff) continue; diff --git a/engines/dreamweb/detection_tables.h b/engines/dreamweb/detection_tables.h index 063aabbd89..8a2f94f99b 100644 --- a/engines/dreamweb/detection_tables.h +++ b/engines/dreamweb/detection_tables.h @@ -46,7 +46,7 @@ static const DreamWebGameDescription gameDescriptions[] = { }, Common::EN_ANY, Common::kPlatformPC, - ADGF_TESTING, + 0, GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_BRIGHTPALETTE) }, }, @@ -63,7 +63,7 @@ static const DreamWebGameDescription gameDescriptions[] = { }, Common::EN_ANY, Common::kPlatformPC, - ADGF_CD | ADGF_TESTING, + ADGF_CD, GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_BRIGHTPALETTE) }, }, @@ -84,7 +84,7 @@ static const DreamWebGameDescription gameDescriptions[] = { }, Common::EN_GRB, Common::kPlatformPC, - ADGF_CD | ADGF_TESTING, + ADGF_CD, GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_BRIGHTPALETTE) }, }, @@ -101,7 +101,7 @@ static const DreamWebGameDescription gameDescriptions[] = { }, Common::EN_USA, Common::kPlatformPC, - ADGF_CD | ADGF_TESTING, + ADGF_CD, GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_BRIGHTPALETTE) }, }, @@ -118,7 +118,7 @@ static const DreamWebGameDescription gameDescriptions[] = { }, Common::FR_FRA, Common::kPlatformPC, - ADGF_CD | ADGF_TESTING, + ADGF_CD, GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_BRIGHTPALETTE) }, }, @@ -136,7 +136,7 @@ static const DreamWebGameDescription gameDescriptions[] = { }, Common::FR_FRA, Common::kPlatformPC, - ADGF_CD | ADGF_TESTING, + ADGF_CD, GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_BRIGHTPALETTE) }, }, @@ -153,7 +153,7 @@ static const DreamWebGameDescription gameDescriptions[] = { }, Common::DE_DEU, Common::kPlatformPC, - ADGF_TESTING, + 0, GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_BRIGHTPALETTE) }, }, @@ -170,7 +170,7 @@ static const DreamWebGameDescription gameDescriptions[] = { }, Common::DE_DEU, Common::kPlatformPC, - ADGF_CD | ADGF_TESTING, + ADGF_CD, GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_BRIGHTPALETTE) }, }, @@ -187,7 +187,7 @@ static const DreamWebGameDescription gameDescriptions[] = { }, Common::ES_ESP, Common::kPlatformPC, - ADGF_TESTING, + 0, GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_BRIGHTPALETTE) }, }, @@ -204,7 +204,7 @@ static const DreamWebGameDescription gameDescriptions[] = { }, Common::ES_ESP, Common::kPlatformPC, - ADGF_CD | ADGF_TESTING, + ADGF_CD, GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_BRIGHTPALETTE) }, }, @@ -222,7 +222,7 @@ static const DreamWebGameDescription gameDescriptions[] = { }, Common::ES_ESP, Common::kPlatformPC, - ADGF_CD | ADGF_TESTING, + ADGF_CD, GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_BRIGHTPALETTE) }, }, @@ -239,7 +239,7 @@ static const DreamWebGameDescription gameDescriptions[] = { }, Common::IT_ITA, Common::kPlatformPC, - ADGF_TESTING, + 0, GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_BRIGHTPALETTE) }, }, diff --git a/engines/dreamweb/dreamweb.cpp b/engines/dreamweb/dreamweb.cpp index 0ca98d5a7b..c3ede46df2 100644 --- a/engines/dreamweb/dreamweb.cpp +++ b/engines/dreamweb/dreamweb.cpp @@ -208,7 +208,7 @@ DreamWebEngine::DreamWebEngine(OSystem *syst, const DreamWebGameDescription *gam _addToRed = 0; _addToBlue = 0; _lastSoundReel = 0; - _lastHardKey = 0; + _lastHardKey = Common::KEYCODE_INVALID; _bufferIn = 0; _bufferOut = 0; _blinkFrame = 23; @@ -263,7 +263,7 @@ void DreamWebEngine::waitForVSync() { void DreamWebEngine::quit() { _quitRequested = true; - _lastHardKey = 1; + _lastHardKey = Common::KEYCODE_ESCAPE; } void DreamWebEngine::processEvents() { @@ -274,7 +274,7 @@ void DreamWebEngine::processEvents() { _sound->soundHandler(); Common::Event event; - int softKey, hardKey; + int softKey; while (_eventMan->pollEvent(event)) { switch(event.type) { case Common::EVENT_RTL: @@ -309,28 +309,21 @@ void DreamWebEngine::processEvents() { return; //do not pass ctrl + key to the engine } - // Some parts of the ASM code uses the hardware key - // code directly. We don't have that code, so we fake - // it for the keys where it's needed and assume it's - // 0 (which is actually an invalid value, as far as I - // know) otherwise. - - hardKey = 0; + // Some parts of the code uses the hardware key + // code directly. switch (event.kbd.keycode) { case Common::KEYCODE_ESCAPE: - hardKey = 1; + _lastHardKey = Common::KEYCODE_ESCAPE; break; case Common::KEYCODE_SPACE: - hardKey = 57; + _lastHardKey = Common::KEYCODE_SPACE; break; default: - hardKey = 0; + _lastHardKey = Common::KEYCODE_INVALID; break; } - _lastHardKey = hardKey; - // The rest of the keys are converted to ASCII. This // is fairly restrictive, and eventually we may want // to let through more keys. I think this is mostly to @@ -338,11 +331,13 @@ void DreamWebEngine::processEvents() { softKey = 0; - if (event.kbd.keycode >= Common::KEYCODE_a && event.kbd.keycode <= Common::KEYCODE_z) { - softKey = event.kbd.ascii & ~0x20; - } else if (event.kbd.keycode == Common::KEYCODE_MINUS || - event.kbd.keycode == Common::KEYCODE_SPACE || - (event.kbd.keycode >= Common::KEYCODE_0 && event.kbd.keycode <= Common::KEYCODE_9)) { + debug(1, "DreamWebEngine::processEvents() KeyDown keycode:%d ascii:0x%02x", event.kbd.keycode, event.kbd.ascii); + if ((event.kbd.ascii >= 'a' && event.kbd.ascii <= 'z') || + (event.kbd.ascii >= 'A' && event.kbd.ascii <= 'Z')) { + softKey = event.kbd.ascii & ~0x20; // (& ~0x20) forces ascii codes for a-z to map to A-Z + } else if (event.kbd.ascii == '-' || + event.kbd.ascii == ' ' || + (event.kbd.ascii >= '0' && event.kbd.ascii <= '9')) { softKey = event.kbd.ascii; } else if (event.kbd.keycode >= Common::KEYCODE_KP0 && event.kbd.keycode <= Common::KEYCODE_KP9) { softKey = event.kbd.keycode - Common::KEYCODE_KP0 + '0'; @@ -374,6 +369,7 @@ Common::Error DreamWebEngine::run() { ConfMan.registerDefault("bright_palette", true); _hasSpeech = Common::File::exists(_speechDirName + "/r01c0000.raw") && !ConfMan.getBool("speech_mute"); _brightPalette = ConfMan.getBool("bright_palette"); + _copyProtection = ConfMan.getBool("copy_protection"); _timer->installTimerProc(vSyncInterrupt, 1000000 / 70, this, "dreamwebVSync"); dreamweb(); @@ -403,14 +399,14 @@ Common::String DreamWebEngine::getSavegameFilename(int slot) const { void DreamWebEngine::keyPressed(uint16 ascii) { debug(2, "key pressed = %04x", ascii); - uint16 in = (_bufferIn + 1) & 0x0f; + uint16 in = (_bufferIn + 1) % ARRAYSIZE(_keyBuffer); uint16 out = _bufferOut; if (in == out) { warning("keyboard buffer is full"); return; } _bufferIn = in; - DreamWeb::g_keyBuffer[in] = ascii; + _keyBuffer[in] = ascii; } void DreamWebEngine::getPalette(uint8 *data, uint start, uint count) { @@ -421,7 +417,7 @@ void DreamWebEngine::getPalette(uint8 *data, uint start, uint count) { void DreamWebEngine::setPalette(const uint8 *data, uint start, uint count) { assert(start + count <= 256); - uint8 fixed[768]; + uint8 fixed[3*256]; for (uint i = 0; i < count * 3; ++i) { fixed[i] = data[i] << 2; } @@ -429,10 +425,10 @@ void DreamWebEngine::setPalette(const uint8 *data, uint start, uint count) { } void DreamWebEngine::blit(const uint8 *src, int pitch, int x, int y, int w, int h) { - if (y + h > 200) - h = 200 - y; - if (x + w > 320) - w = 320 - x; + if (y + h > (int)kScreenheight) + h = kScreenheight - y; + if (x + w > (int)kScreenwidth) + w = kScreenwidth - x; if (h <= 0 || w <= 0) return; _system->copyRectToScreen(src, pitch, x, y, w, h); @@ -516,7 +512,7 @@ uint8 DreamWebEngine::modifyChar(uint8 c) const { case Common::IT_ITA: switch(c) { case 133: - return 'Z' + 1; + return 'Z' + 1; case 130: return 'Z' + 2; case 138: @@ -548,10 +544,10 @@ uint8 DreamWebEngine::modifyChar(uint8 c) const { return c; } } - + Common::String DreamWebEngine::modifyFileName(const char *name) { Common::String fileName(name); - + // Sanity check if (!fileName.hasPrefix("DREAMWEB.")) return fileName; diff --git a/engines/dreamweb/dreamweb.h b/engines/dreamweb/dreamweb.h index 1f6deb8566..eb35a73f66 100644 --- a/engines/dreamweb/dreamweb.h +++ b/engines/dreamweb/dreamweb.h @@ -25,6 +25,7 @@ #include "common/error.h" #include "common/file.h" +#include "common/keyboard.h" #include "common/random.h" #include "common/rect.h" #include "common/savefile.h" @@ -63,6 +64,7 @@ const unsigned int kMapHeight = 60; const unsigned int kLengthOfMap = kMapWidth * kMapHeight; const unsigned int kNumExObjects = 114; const unsigned int kScreenwidth = 320; +const unsigned int kScreenheight = 200; const unsigned int kDiaryx = (68+24); const unsigned int kDiaryy = (48+12); const unsigned int kInventx = 80; @@ -88,10 +90,6 @@ const unsigned int kNumRoomTexts = 38; const unsigned int kNumFreeTexts = 82; const unsigned int kNumPersonTexts = 1026; -// Keyboard buffer. data.word(kBufferin) and data.word(kBufferout) are indexes -// into this, making it a ring buffer -extern uint8 g_keyBuffer[16]; - // Engine Debug Flags enum { kDebugAnimation = (1 << 0), @@ -156,6 +154,12 @@ public: const Common::String& getSpeechDirName() { return _speechDirName; } private: + // Keyboard buffer. _bufferIn and _bufferOut are indexes + // into this, making it a ring buffer + uint8 _keyBuffer[16]; + uint16 _bufferIn; + uint16 _bufferOut; + void keyPressed(uint16 ascii); void setSpeed(uint speed); @@ -196,7 +200,7 @@ protected: // from monitor.cpp char _inputLine[64]; - char _operand1[14]; + char _operand1[64]; char _currentFile[14]; // from newplace.cpp @@ -316,6 +320,7 @@ public: uint16 _charShift; uint8 _kerning; bool _brightPalette; + bool _copyProtection; uint8 _roomLoaded; uint8 _didZoom; uint16 _lineSpacing; @@ -419,9 +424,7 @@ public: uint8 _addToRed; uint8 _addToBlue; uint16 _lastSoundReel; - uint8 _lastHardKey; - uint16 _bufferIn; - uint16 _bufferOut; + Common::KeyCode _lastHardKey; uint8 _blinkFrame; uint8 _blinkCount; uint8 _reAssesChanges; @@ -750,7 +753,6 @@ public: void showRyanPage(); void switchRyanOn(); void switchRyanOff(); - void middlePanel(); void showDiary(); void readMouse(); uint16 readMouseState(); @@ -882,7 +884,6 @@ public: void obsThatDoThings(); void describeOb(); void putBackObStuff(); - void reExFromOpen(); void showDiaryPage(); void showDiaryKeys(); void dumpDiaryKeys(); diff --git a/engines/dreamweb/keypad.cpp b/engines/dreamweb/keypad.cpp index 3580f8ad52..7bbca2b979 100644 --- a/engines/dreamweb/keypad.cpp +++ b/engines/dreamweb/keypad.cpp @@ -103,7 +103,6 @@ void DreamWebEngine::enterCode(uint8 digit0, uint8 digit1, uint8 digit2, uint8 d // Note: isItRight comes from use.asm, but is only used by enterCode(), // so we place it here. bool DreamWebEngine::isItRight(uint8 digit0, uint8 digit1, uint8 digit2, uint8 digit3) { - return digit0 == _pressList[0] && digit1 == _pressList[1] && digit2 == _pressList[2] && digit3 == _pressList[3]; } @@ -434,7 +433,7 @@ void DreamWebEngine::folderExit() { void DreamWebEngine::showLeftPage() { showFrame(_folderGraphics2, 0, 12, 3, 0); uint16 y = 12+5; - for (size_t i = 0; i < 9; ++i) { + for (uint i = 0; i < 9; ++i) { showFrame(_folderGraphics2, 0, y, 4, 0); y += 16; } @@ -445,7 +444,7 @@ void DreamWebEngine::showLeftPage() { uint8 pageIndex = _folderPage - 2; const uint8 *string = getTextInFile1(pageIndex * 2); y = 48; - for (size_t i = 0; i < 2; ++i) { + for (uint i = 0; i < 2; ++i) { uint8 lastChar; do { lastChar = printDirect(&string, 2, &y, 140, false); @@ -455,19 +454,19 @@ void DreamWebEngine::showLeftPage() { _kerning = 0; _charShift = 0; _lineSpacing = 10; - uint8 *bufferToSwap = workspace() + (48*320)+2; - for (size_t i = 0; i < 120; ++i) { - for (size_t j = 0; j < 65; ++j) { + uint8 *bufferToSwap = workspace() + (48*kScreenwidth)+2; + for (uint i = 0; i < 120; ++i) { + for (uint j = 0; j < 65; ++j) { SWAP(bufferToSwap[j], bufferToSwap[130 - j]); } - bufferToSwap += 320; + bufferToSwap += kScreenwidth; } } void DreamWebEngine::showRightPage() { showFrame(_folderGraphics2, 143, 12, 0, 0); uint16 y = 12+37; - for (size_t i = 0; i < 7; ++i) { + for (uint i = 0; i < 7; ++i) { showFrame(_folderGraphics2, 143, y, 1, 0); y += 16; } @@ -478,7 +477,7 @@ void DreamWebEngine::showRightPage() { uint8 pageIndex = _folderPage - 1; const uint8 *string = getTextInFile1(pageIndex * 2); y = 48; - for (size_t i = 0; i < 2; ++i) { + for (uint i = 0; i < 2; ++i) { uint8 lastChar; do { lastChar = printDirect(&string, 152, &y, 140, false); diff --git a/engines/dreamweb/monitor.cpp b/engines/dreamweb/monitor.cpp index 4e9d8eecc1..b6922cba51 100644 --- a/engines/dreamweb/monitor.cpp +++ b/engines/dreamweb/monitor.cpp @@ -149,6 +149,13 @@ bool DreamWebEngine::execCommand() { return true; case 1: monMessage(6); + // An extra addition in ScummVM: available commands. + // Since the reference to the game manual is a form of copy protection, + // this extra text is wrapped around the common copy protection check, + // to keep it faithful to the original, if requested. + if (!_copyProtection) { + monPrint("VALID COMMANDS ARE EXIT, HELP, LIST, READ, LOGON, KEYS"); + } break; case 2: dirCom(); @@ -194,7 +201,7 @@ void DreamWebEngine::printLogo() { } void DreamWebEngine::input() { - memset(_inputLine, 0, 64); + memset(_inputLine, 0, sizeof(_inputLine)); _curPos = 0; printChar(_monitorCharset, _monAdX, _monAdY, '>', 0, NULL, NULL); multiDump(_monAdX, _monAdY, 6, 8); @@ -366,7 +373,7 @@ void DreamWebEngine::lockLightOff() { } void DreamWebEngine::turnOnPower() { - for (size_t i = 0; i < 3; ++i) { + for (uint i = 0; i < 3; ++i) { powerLightOn(); hangOn(30); powerLightOff(); @@ -665,7 +672,7 @@ void DreamWebEngine::searchForFiles(const char *filesString) { const char *DreamWebEngine::parser() { char *output = _operand1; - memset(output, 0, 14); + memset(output, 0, sizeof(_operand1)); *output++ = '='; diff --git a/engines/dreamweb/object.cpp b/engines/dreamweb/object.cpp index b42591ef91..bee3a6d511 100644 --- a/engines/dreamweb/object.cpp +++ b/engines/dreamweb/object.cpp @@ -31,7 +31,7 @@ void DreamWebEngine::showRyanPage() { void DreamWebEngine::findAllRyan() { memset(_ryanInvList, 0xff, sizeof(_ryanInvList)); - for (size_t i = 0; i < kNumexobjects; ++i) { + for (uint i = 0; i < kNumexobjects; ++i) { const DynObject *extra = getExAd(i); if (extra->mapad[0] != kExObjectType) continue; @@ -47,8 +47,8 @@ void DreamWebEngine::findAllRyan() { void DreamWebEngine::fillRyan() { ObjectRef *inv = &_ryanInvList[_vars._ryanPage * 10]; findAllRyan(); - for (size_t i = 0; i < 2; ++i) { - for (size_t j = 0; j < 5; ++j) { + for (uint i = 0; i < 2; ++i) { + for (uint j = 0; j < 5; ++j) { obToInv(inv->_index, inv->_type, kInventx + j * kItempicsize, kInventy + i * kItempicsize); ++inv; } @@ -435,10 +435,23 @@ void DreamWebEngine::deleteExFrame(uint8 frameNum) { _vars._exFramePos -= frameSize; // Adjust all frame pointers pointing into the shifted data - for (unsigned int i = 0; i < 3*kNumexobjects; ++i) { - frame = &_exFrames._frames[i]; - if (frame->ptr() >= startOff) - frame->setPtr(frame->ptr() - frameSize); + for (unsigned int i = 0; i < kNumexobjects; ++i) { + if (_exData[i].mapad[0] != 0xff) { + frame = &_exFrames._frames[3*i+0]; + if (frame->ptr() >= startOff) { + frame->setPtr(frame->ptr() - frameSize); + assert(frame->ptr() + frame->width*frame->height <= _vars._exFramePos); + } else { + assert(frame->ptr() + frame->width*frame->height <= startOff); + } + frame = &_exFrames._frames[3*i+1]; + if (frame->ptr() >= startOff) { + frame->setPtr(frame->ptr() - frameSize); + assert(frame->ptr() + frame->width*frame->height <= _vars._exFramePos); + } else { + assert(frame->ptr() + frame->width*frame->height <= startOff); + } + } } } @@ -516,7 +529,7 @@ void DreamWebEngine::inToInv() { if (_mouseButton == _oldButton || !(_mouseButton & 1)) return; // notletgo2 - + delPointer(); DynObject *object = getExAd(_itemFrame); object->mapad[0] = 4; @@ -875,7 +888,7 @@ void DreamWebEngine::useOpened() { void DreamWebEngine::outOfOpen() { if (_openedOb == 255) - return; // cannot use opened object + return; // cannot use opened object ObjectRef objectId = findOpenPos(); @@ -892,13 +905,10 @@ void DreamWebEngine::outOfOpen() { } if (_mouseButton == _oldButton) - return; // notletgo4 + return; // notletgo4 - if (_mouseButton != 1) { - if (_mouseButton == 2) - reExFromOpen(); + if (_mouseButton != 1) return; - } delPointer(); _pickUp = 1; @@ -1034,7 +1044,7 @@ void DreamWebEngine::fillOpen() { size = 4; findAllOpen(); for (uint8 i = 0; i < size; ++i) { - uint8 index = _openInvList[i]._index; + uint8 index = _openInvList[i]._index; uint8 type = _openInvList[i]._type; obToInv(index, type, kInventx + i * kItempicsize, kInventy + 96); } @@ -1102,4 +1112,136 @@ void DreamWebEngine::pickupConts(uint8 from, uint8 containerEx) { } } +void DreamWebEngine::incRyanPage() { + commandOnlyCond(31, 222); + + if (_mouseButton == _oldButton || !(_mouseButton & 1)) + return; + + _vars._ryanPage = (_mouseX - (kInventx + 167)) / 18; + + delPointer(); + fillRyan(); + readMouse(); + showPointer(); + workToScreen(); + delPointer(); +} + +void DreamWebEngine::emergencyPurge() { + debug(2, "Ex memory: frames %d/%d, text %d/%d", _vars._exFramePos, kExframeslen, _vars._exTextPos, kExtextlen); + + while (_vars._exFramePos + 4000 >= kExframeslen || + _vars._exTextPos + 400 >= kExtextlen) + { + purgeAnItem(); + debug(2, "Ex memory after purging: frames %d/%d, text %d/%d", _vars._exFramePos, kExframeslen, _vars._exTextPos, kExtextlen); + } +} + +void DreamWebEngine::purgeAnItem() { + const DynObject *extraObjects = _exData; + + + for (uint i = 0; i < kNumexobjects; ++i) { + if (extraObjects[i].mapad[0] == 0 && + (extraObjects[i].objId[0] == 255 || extraObjects[i].objId[0] == 2) && + extraObjects[i].initialLocation != _realLocation) { + debug(1, "Purging ex object %d", i); + deleteExObject(i); + return; + } + } + + for (uint i = 0; i < kNumexobjects; ++i) { + if (extraObjects[i].mapad[0] == 0 && extraObjects[i].objId[0] == 255) { + debug(1, "Purging ex object %d", i); + deleteExObject(i); + return; + } + } + + error("Out of Ex object memory"); +} + +void DreamWebEngine::dropError() { + _commandType = 255; + delPointer(); + printMessage(76, 21, 56, 240, 240 & 1); + workToScreenM(); + hangOnP(50); + showPanel(); + showMan(); + examIcon(); + _commandType = 255; + workToScreenM(); +} + +void DreamWebEngine::cantDrop() { + _commandType = 255; + delPointer(); + printMessage(76, 21, 24, 240, 240 & 1); + workToScreenM(); + hangOnP(50); + showPanel(); + showMan(); + examIcon(); + _commandType = 255; + workToScreenM(); +} + +void DreamWebEngine::examineInventory() { + commandOnlyCond(32, 249); + + if (!(_mouseButton & 1)) + return; + + createPanel(); + showPanel(); + showMan(); + showExit(); + examIcon(); + _pickUp = 0; + _invOpen = 2; + openInv(); + workToScreenM(); +} + +void DreamWebEngine::openInv() { + _invOpen = 1; + printMessage(80, 58 - 10, 61, 240, (240 & 1)); + fillRyan(); + _commandType = 255; +} + +void DreamWebEngine::pickupOb(uint8 command, uint8 pos) { + _lastInvPos = pos; + _objectType = kFreeObjectType; + _itemFrame = command; + _command = command; + //uint8 dummy; + //getAnyAd(&dummy, &dummy); // was in the original source, seems useless here + transferToEx(command); +} + +void DreamWebEngine::initialInv() { + if (_realLocation != 24) + return; + + pickupOb(11, 5); + pickupOb(12, 6); + pickupOb(13, 7); + pickupOb(14, 8); + pickupOb(18, 0); + pickupOb(19, 1); + pickupOb(20, 9); + pickupOb(16, 2); + _vars._watchMode = 1; + _vars._reelToHold = 0; + _vars._endOfHoldReel = 6; + _vars._watchSpeed = 1; + _vars._speedCount = 1; + switchRyanOff(); +} + } // End of namespace DreamWeb diff --git a/engines/dreamweb/pathfind.cpp b/engines/dreamweb/pathfind.cpp index c39070532c..64cffde4de 100644 --- a/engines/dreamweb/pathfind.cpp +++ b/engines/dreamweb/pathfind.cpp @@ -110,7 +110,7 @@ void DreamWebEngine::checkDest(const RoomPaths *roomsPaths) { const PathSegment *segments = roomsPaths->segments; const uint8 tmp = _mansPath << 4; uint8 destination = _destination; - for (size_t i = 0; i < 24; ++i) { + for (uint i = 0; i < 24; ++i) { if ((segments[i].b0 & 0xf0) == tmp && (segments[i].b0 & 0x0f) == _destination) { _destination = segments[i].b1 & 0x0f; diff --git a/engines/dreamweb/people.cpp b/engines/dreamweb/people.cpp index 36a756a49b..53f04d482b 100644 --- a/engines/dreamweb/people.cpp +++ b/engines/dreamweb/people.cpp @@ -194,7 +194,7 @@ void DreamWebEngine::madman(ReelRoutine &routine) { if (newReelPointer == 66) { ++_vars._combatCount; - if (_lastHardKey == 1) // ESC pressed, skip the mad man's speech + if (_lastHardKey == Common::KEYCODE_ESCAPE) // ESC pressed, skip the mad man's speech _vars._combatCount = _speechCount = (hasSpeech() ? 65 : 63); madmanText(); @@ -411,7 +411,7 @@ void DreamWebEngine::interviewer(ReelRoutine &routine) { if (routine.reelPointer() != 250 && routine.reelPointer() != 259 && checkSpeed(routine)) routine.incReelPointer(); - + showGameReel(&routine); } @@ -745,7 +745,7 @@ void DreamWebEngine::introMonks2(ReelRoutine &routine) { if (nextReelPointer == 110) { _introCount++; monks2text(); - + if (_introCount == 35) nextReelPointer = 111; else @@ -895,7 +895,7 @@ void DreamWebEngine::helicopter(ReelRoutine &routine) { nextReelPointer = 9; } } - } + } routine.setReelPointer(nextReelPointer); } @@ -1002,7 +1002,7 @@ void DreamWebEngine::businessMan(ReelRoutine &routine) { nextReelPointer = 92; } } - + routine.setReelPointer(nextReelPointer); } @@ -1037,7 +1037,7 @@ void DreamWebEngine::endGameSeq(ReelRoutine &routine) { showGameReel(&routine); routine.mapY = _mapY; - + if (routine.reelPointer() == 145) { routine.setReelPointer(146); rollEndCreditsGameWon(); @@ -1070,7 +1070,7 @@ void DreamWebEngine::poolGuard(ReelRoutine &routine) { if (checkSpeed(routine)) { uint16 nextReelPointer = routine.reelPointer() + 1; - + if (nextReelPointer != 122) { // Not end guard 1 if (nextReelPointer == 147) { @@ -1103,12 +1103,12 @@ void DreamWebEngine::poolGuard(ReelRoutine &routine) { } } } - + routine.setReelPointer(nextReelPointer); } showGameReel(&routine); - + if (routine.reelPointer() != 121 && routine.reelPointer() != 146) { _pointerMode = 0; _vars._watchingTime = 2; diff --git a/engines/dreamweb/print.cpp b/engines/dreamweb/print.cpp index 3a2c45e07b..bc75b97e71 100644 --- a/engines/dreamweb/print.cpp +++ b/engines/dreamweb/print.cpp @@ -49,7 +49,9 @@ uint8 DreamWebEngine::getNextWord(const GraphicsFile &charSet, const uint8 *stri return 0; } firstChar = modifyChar(firstChar); - if (firstChar != 255) { + // WORKAROUND: Also filter out invalid characters here (refer to the + // workaround in printChar() below for more info). + if (firstChar >= 32 && firstChar != 255) { uint8 secondChar = *string; uint8 width = charSet._frames[firstChar - 32 + _charShift].width; width = kernChars(firstChar, secondChar, width); @@ -59,9 +61,13 @@ uint8 DreamWebEngine::getNextWord(const GraphicsFile &charSet, const uint8 *stri } void DreamWebEngine::printChar(const GraphicsFile &charSet, uint16* x, uint16 y, uint8 c, uint8 nextChar, uint8 *width, uint8 *height) { - if (c == 255) + // WORKAROUND: Some texts contain leftover tab characters, which will cause + // OOB memory access when showing a character, as all the printable ones are + // from 32 onwards. We compensate for that here by ignoring all the invalid + // characters (0 - 31). + if (c < 32 || c == 255) return; - + uint8 dummyWidth, dummyHeight; if (width == NULL) width = &dummyWidth; @@ -212,7 +218,7 @@ const char *DreamWebEngine::monPrint(const char *string) { while (!done) { uint16 count = getNumber(_monitorCharset, (const uint8 *)iterator, 166, false, &x); - do { + do { char c = *iterator++; if (c == ':') break; @@ -315,7 +321,7 @@ void DreamWebEngine::rollEndCreditsGameLost() { waitForVSync(); multiDump(25, 20, 160, 160); - if (_lastHardKey == 1) + if (_lastHardKey == Common::KEYCODE_ESCAPE) return; } @@ -325,7 +331,7 @@ void DreamWebEngine::rollEndCreditsGameLost() { c = *string++; } while (c != ':' && c != 0); - if (_lastHardKey == 1) + if (_lastHardKey == Common::KEYCODE_ESCAPE) return; } diff --git a/engines/dreamweb/rain.cpp b/engines/dreamweb/rain.cpp index 8e42e0c161..b636b7def7 100644 --- a/engines/dreamweb/rain.cpp +++ b/engines/dreamweb/rain.cpp @@ -42,12 +42,12 @@ void DreamWebEngine::showRain() { uint16 offset = (rain.w3 - rain.b5) & 511; rain.w3 = offset; const uint8 *src = frameData + offset; - uint8 *dst = workspace() + y * 320 + x; + uint8 *dst = workspace() + y * kScreenwidth + x; for (uint16 j = 0; j < size; ++j) { uint8 v = src[j]; if (v != 0) *dst = v; - dst += 320-1; // advance diagonally + dst += kScreenwidth-1; // advance diagonally } } diff --git a/engines/dreamweb/saveload.cpp b/engines/dreamweb/saveload.cpp index ea9cdc0249..8a0791d19b 100644 --- a/engines/dreamweb/saveload.cpp +++ b/engines/dreamweb/saveload.cpp @@ -156,8 +156,16 @@ void DreamWebEngine::doLoad(int savegameId) { } else { if (savegameId == -1) { - // Open dialog to get savegameId + // Wait till both mouse buttons are up. We should wait till the user + // releases the mouse button, otherwise the follow-up mouseup event + // will trigger a load of the save slot under the mouse cursor. Fixes + // bug #3582582. + while (_oldMouseState > 0) { + readMouse(); + g_system->delayMillis(10); + } + // Open dialog to get savegameId GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Restore game:"), _("Restore"), false); savegameId = dialog->runModalWithCurrentTarget(); delete dialog; @@ -241,6 +249,15 @@ void DreamWebEngine::saveGame() { } return; } else { + // Wait till both mouse buttons are up. We should wait till the user + // releases the mouse button, otherwise the follow-up mouseup event + // will trigger a save into the save slot under the mouse cursor. Fixes + // bug #3582582. + while (_oldMouseState > 0) { + readMouse(); + g_system->delayMillis(10); + } + GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"), true); int savegameId = dialog->runModalWithCurrentTarget(); Common::String game_description = dialog->getResultString(); @@ -557,6 +574,14 @@ void DreamWebEngine::savePosition(unsigned int slot, const char *descbuf) { delete outSaveFile; } + +// Utility struct for a savegame sanity check in loadPosition +struct FrameExtent { + uint16 start; + uint16 length; + bool operator<(const struct FrameExtent& other) const { return start<other.start; } +}; + void DreamWebEngine::loadPosition(unsigned int slot) { _timeCount = 0; clearChanges(); @@ -636,6 +661,42 @@ void DreamWebEngine::loadPosition(unsigned int slot) { } delete inSaveFile; + + + // Do a sanity check on exFrames data to detect exFrames corruption + // caused by a (now fixed) bug in emergencyPurge. See bug #3591088. + // Gather the location of frame data of all used ex object frames. + Common::List<FrameExtent> flist; + for (unsigned int i = 0; i < kNumexobjects; ++i) { + if (_exData[i].mapad[0] != 0xff) { + FrameExtent fe; + Frame *frame = &_exFrames._frames[3*i+0]; + fe.start = frame->ptr(); + fe.length = frame->width * frame->height; + flist.push_back(fe); + + frame = &_exFrames._frames[3*i+1]; + fe.start = frame->ptr(); + fe.length = frame->width * frame->height; + flist.push_back(fe); + } + } + // ...and check if the frames overlap. + Common::sort(flist.begin(), flist.end(), Common::Less<FrameExtent>()); + Common::List<FrameExtent>::const_iterator iter; + uint16 curEnd = 0; + for (iter = flist.begin(); iter != flist.end(); ++iter) { + if (iter->start < curEnd) + error("exFrames data corruption in savegame"); + curEnd = iter->start + iter->length; + } + if (curEnd > _vars._exFramePos) { + if (curEnd > kExframeslen) + error("exFrames data corruption in savegame"); + warning("Fixing up exFramePos"); + _vars._exFramePos = curEnd; + } + // (end of sanity check) } // Count number of save files, and load their descriptions into _saveNames diff --git a/engines/dreamweb/sprite.cpp b/engines/dreamweb/sprite.cpp index 5b6cf6a6ac..1fa2e7d6a4 100644 --- a/engines/dreamweb/sprite.cpp +++ b/engines/dreamweb/sprite.cpp @@ -26,7 +26,7 @@ namespace DreamWeb { void DreamWebEngine::printSprites() { - for (size_t priority = 0; priority < 7; ++priority) { + for (uint priority = 0; priority < 7; ++priority) { Common::List<Sprite>::const_iterator i; for (i = _spriteTable.begin(); i != _spriteTable.end(); ++i) { const Sprite &sprite = *i; @@ -52,7 +52,7 @@ void DreamWebEngine::printASprite(const Sprite *sprite) { } else { x = sprite->x + _mapAdX; } - + uint8 c; if (sprite->walkFrame != 0) c = 8; @@ -97,7 +97,7 @@ void DreamWebEngine::spriteUpdate() { else { backObject(&sprite); } - + if (_nowInNewRoom == 1) break; } @@ -373,7 +373,7 @@ void DreamWebEngine::lockedDoorway(Sprite *sprite, SetObject *objData) { if (sprite->animFrame != 0) --sprite->animFrame; - + _vars._throughDoor = 0; sprite->frameNumber = objData->index = objData->frames[sprite->animFrame]; @@ -407,7 +407,7 @@ void DreamWebEngine::liftSprite(Sprite *sprite, SetObject *objData) { } sprite->animFrame = 12; sprite->frameNumber = objData->index = objData->frames[sprite->animFrame]; - } + } else if (liftFlag == 3) { //openlift if (sprite->animFrame == 12) { _vars._liftFlag = 1; @@ -672,7 +672,7 @@ static const ReelSound g_roomSound6[] = { { 255,0 } }; static const ReelSound g_roomSound8[] = { - + { 12, 51 }, { 13, 53 }, { 14, 14 }, @@ -691,7 +691,7 @@ static const ReelSound g_roomSound10[] = { { 13, 16 }, { 255,0 } }; - + static const ReelSound g_roomSound11[] = { { 13, 20 }, { 255,0 } @@ -779,7 +779,7 @@ static const ReelSound g_roomSound26[] = { { 15, 102 }, // was 90, should be mine cart { 255,0 } }; - + static const ReelSound g_roomSound27[] = { { 22, 36 }, { 13, 125 }, diff --git a/engines/dreamweb/stubs.cpp b/engines/dreamweb/stubs.cpp index f235f7c2fd..d93c2a951c 100644 --- a/engines/dreamweb/stubs.cpp +++ b/engines/dreamweb/stubs.cpp @@ -26,10 +26,6 @@ namespace DreamWeb { -// Keyboard buffer. _bufferIn and _bufferOut are indexes -// into this, making it a ring buffer -uint8 g_keyBuffer[16]; - const Room g_roomData[] = { // location 0 { "DREAMWEB.R00", // Ryan's apartment @@ -723,7 +719,6 @@ void DreamWebEngine::dreamweb() { showGun(); fadeScreenDown(); hangOn(100); - } } @@ -965,7 +960,6 @@ void DreamWebEngine::useTimedText() { } void DreamWebEngine::setupTimedTemp(uint8 textIndex, uint8 voiceIndex, uint8 x, uint8 y, uint16 countToTimed, uint16 timeCount) { - if (hasSpeech() && voiceIndex != 0) { _speechLoaded = _sound->loadSpeech('T', voiceIndex, 'T', textIndex); if (_speechLoaded) @@ -1056,7 +1050,7 @@ void DreamWebEngine::lockMon() { // key because calling readkey() drains characters from the input // buffer, we we want the user to be able to type ahead while the text // is being printed. - if (_lastHardKey == 57) { + if (_lastHardKey == Common::KEYCODE_SPACE) { // Clear the keyboard buffer. Otherwise the space that caused // the pause will be read immediately unpause the game. do { @@ -1072,7 +1066,7 @@ void DreamWebEngine::lockMon() { } // Forget the last "hard" key, otherwise the space that caused // the unpausing will immediately re-pause the game. - _lastHardKey = 0; + _lastHardKey = Common::KEYCODE_INVALID; lockLightOff(); } } @@ -1148,7 +1142,7 @@ void DreamWebEngine::plotReel(uint16 &reelPointer) { reel += 8; } - for (size_t i = 0; i < 8; ++i) { + for (uint i = 0; i < 8; ++i) { if (reel->frame() != 0xffff) showReelFrame(reel); ++reel; @@ -1247,7 +1241,7 @@ const uint8 *DreamWebEngine::findObName(uint8 type, uint8 index) { void DreamWebEngine::copyName(uint8 type, uint8 index, uint8 *dst) { const uint8 *src = findObName(type, index); - size_t i; + uint i; for (i = 0; i < 28; ++i) { char c = src[i]; if (c == ':') @@ -1377,7 +1371,7 @@ void DreamWebEngine::doChange(uint8 index, uint8 value, uint8 type) { } void DreamWebEngine::deleteTaken() { - for (size_t i = 0; i < kNumexobjects; ++i) { + for (uint i = 0; i < kNumexobjects; ++i) { uint8 location = _exData[i].initialLocation; if (location == _realLocation) { uint8 index = _exData[i].index; @@ -1388,7 +1382,7 @@ void DreamWebEngine::deleteTaken() { uint8 DreamWebEngine::getExPos() { DynObject *objects = _exData; - for (size_t i = 0; i < kNumexobjects; ++i) { + for (uint i = 0; i < kNumexobjects; ++i) { if (objects[i].mapad[0] == 0xff) return i; } @@ -1543,7 +1537,7 @@ void DreamWebEngine::printMessage2(uint16 x, uint16 y, uint8 index, uint8 maxWid bool DreamWebEngine::objectMatches(void *object, const char *id) { const char *objId = (const char *)object + 12; // whether it is a DynObject or a SetObject - for (size_t i = 0; i < 4; ++i) { + for (uint i = 0; i < 4; ++i) { if (id[i] != objId[i] + 'A') return false; } @@ -2138,7 +2132,6 @@ void DreamWebEngine::workToScreenM() { } void DreamWebEngine::atmospheres() { - const Atmosphere *a = &g_atmosphereList[0]; for (; a->_location != 255; ++a) { @@ -2208,8 +2201,8 @@ void DreamWebEngine::readKey() { return; } - bufOut = (bufOut + 1) & 15; // The buffer has size 16 - _currentKey = g_keyBuffer[bufOut]; + bufOut = (bufOut + 1) % ARRAYSIZE(_keyBuffer); + _currentKey = _keyBuffer[bufOut]; _bufferOut = bufOut; } @@ -2220,36 +2213,6 @@ void DreamWebEngine::newGame() { _getBack = 3; } -void DreamWebEngine::pickupOb(uint8 command, uint8 pos) { - _lastInvPos = pos; - _objectType = kFreeObjectType; - _itemFrame = command; - _command = command; - //uint8 dummy; - //getAnyAd(&dummy, &dummy); // was in the original source, seems useless here - transferToEx(command); -} - -void DreamWebEngine::initialInv() { - if (_realLocation != 24) - return; - - pickupOb(11, 5); - pickupOb(12, 6); - pickupOb(13, 7); - pickupOb(14, 8); - pickupOb(18, 0); - pickupOb(19, 1); - pickupOb(20, 9); - pickupOb(16, 2); - _vars._watchMode = 1; - _vars._reelToHold = 0; - _vars._endOfHoldReel = 6; - _vars._watchSpeed = 1; - _vars._speedCount = 1; - switchRyanOff(); -} - void DreamWebEngine::walkIntoRoom() { if (_vars._location == 14 && _mapX == 22) { _destination = 1; @@ -2314,13 +2277,6 @@ void DreamWebEngine::makeMainScreen() { _manIsOffScreen = 0; } -void DreamWebEngine::openInv() { - _invOpen = 1; - printMessage(80, 58 - 10, 61, 240, (240 & 1)); - fillRyan(); - _commandType = 255; -} - void DreamWebEngine::obsThatDoThings() { if (!compare(_command, _objectType, "MEMB")) return; // notlouiscard @@ -2420,10 +2376,6 @@ void DreamWebEngine::errorMessage3() { delPointer(); } -void DreamWebEngine::reExFromOpen() { - -} - void DreamWebEngine::putBackObStuff() { createPanel(); showPanel(); @@ -2444,26 +2396,6 @@ bool DreamWebEngine::isSetObOnMap(uint8 index) { return (getSetAd(index)->mapad[0] == 0); } -void DreamWebEngine::examineInventory() { - commandOnlyCond(32, 249); - - if (!(_mouseButton & 1)) - return; - - createPanel(); - showPanel(); - showMan(); - showExit(); - examIcon(); - _pickUp = 0; - _invOpen = 2; - openInv(); - workToScreenM(); -} - -void DreamWebEngine::middlePanel() { -} - void DreamWebEngine::underTextLine() { if (_foreignRelease) multiGet(_textUnder, _textAddressX, _textAddressY - 3, kUnderTextSizeX_f, kUnderTextSizeY_f); @@ -2575,7 +2507,6 @@ void DreamWebEngine::madmanRun() { _vars._lastWeapon = 8; } - void DreamWebEngine::decide() { setMode(); loadPalFromIFF(); @@ -2659,39 +2590,12 @@ void DreamWebEngine::showGun() { getRidOfTempText(); } -void DreamWebEngine::dropError() { - _commandType = 255; - delPointer(); - printMessage(76, 21, 56, 240, 240 & 1); - workToScreenM(); - hangOnP(50); - showPanel(); - showMan(); - examIcon(); - _commandType = 255; - workToScreenM(); -} - -void DreamWebEngine::cantDrop() { - _commandType = 255; - delPointer(); - printMessage(76, 21, 24, 240, 240 & 1); - workToScreenM(); - hangOnP(50); - showPanel(); - showMan(); - examIcon(); - _commandType = 255; - workToScreenM(); -} - void DreamWebEngine::getBack1() { if (_pickUp != 0) { blank(); return; } - commandOnlyCond(26, 202); if (_mouseButton == _oldButton) @@ -3003,52 +2907,4 @@ void DreamWebEngine::edensFlatReminders() { _vars._progressPoints++; // got card } -void DreamWebEngine::incRyanPage() { - commandOnlyCond(31, 222); - - if (_mouseButton == _oldButton || !(_mouseButton & 1)) - return; - - _vars._ryanPage = (_mouseX - (kInventx + 167)) / 18; - - delPointer(); - fillRyan(); - readMouse(); - showPointer(); - workToScreen(); - delPointer(); - -} - -void DreamWebEngine::emergencyPurge() { - while (true) { - if (_vars._exFramePos + 4000 < kExframeslen) { - // Not near frame end - if (_vars._exTextPos + 400 < kExtextlen) - return; // notneartextend - } - - purgeAnItem(); - } -} - -void DreamWebEngine::purgeAnItem() { - const DynObject *extraObjects = _exData; - - for (size_t i = 0; i < kNumexobjects; ++i) { - if (extraObjects[i].mapad[0] && extraObjects[i].objId[0] == 255 && - extraObjects[i].initialLocation != _realLocation) { - deleteExObject(i); - return; - } - } - - for (size_t i = 0; i < kNumexobjects; ++i) { - if (extraObjects[i].mapad[0] && extraObjects[i].objId[0] == 255) { - deleteExObject(i); - return; - } - } -} - } // End of namespace DreamWeb diff --git a/engines/dreamweb/titles.cpp b/engines/dreamweb/titles.cpp index f005279ba0..4e4faa75a0 100644 --- a/engines/dreamweb/titles.cpp +++ b/engines/dreamweb/titles.cpp @@ -105,34 +105,34 @@ void DreamWebEngine::bibleQuote() { fadeScreenUps(); hangOne(80); - if (_lastHardKey == 1) { - _lastHardKey = 0; + if (_lastHardKey == Common::KEYCODE_ESCAPE) { + _lastHardKey = Common::KEYCODE_INVALID; return; // "biblequotearly" } hangOne(560); - if (_lastHardKey == 1) { - _lastHardKey = 0; + if (_lastHardKey == Common::KEYCODE_ESCAPE) { + _lastHardKey = Common::KEYCODE_INVALID; return; // "biblequotearly" } fadeScreenDowns(); hangOne(200); - if (_lastHardKey == 1) { - _lastHardKey = 0; + if (_lastHardKey == Common::KEYCODE_ESCAPE) { + _lastHardKey = Common::KEYCODE_INVALID; return; // "biblequotearly" } _sound->cancelCh0(); - _lastHardKey = 0; + _lastHardKey = Common::KEYCODE_INVALID; } void DreamWebEngine::hangOne(uint16 delay) { do { waitForVSync(); - if (_lastHardKey == 1) + if (_lastHardKey == Common::KEYCODE_ESCAPE) return; // "hangonearly" } while (--delay); } @@ -150,8 +150,8 @@ void DreamWebEngine::intro() { fadeScreenUps(); runIntroSeq(); - if (_lastHardKey == 1) { - _lastHardKey = 0; + if (_lastHardKey == Common::KEYCODE_ESCAPE) { + _lastHardKey = Common::KEYCODE_INVALID; return; // "introearly" } @@ -160,8 +160,8 @@ void DreamWebEngine::intro() { loadIntroRoom(); runIntroSeq(); - if (_lastHardKey == 1) { - _lastHardKey = 0; + if (_lastHardKey == Common::KEYCODE_ESCAPE) { + _lastHardKey = Common::KEYCODE_INVALID; return; // "introearly" } @@ -170,8 +170,8 @@ void DreamWebEngine::intro() { loadIntroRoom(); runIntroSeq(); - if (_lastHardKey == 1) { - _lastHardKey = 0; + if (_lastHardKey == Common::KEYCODE_ESCAPE) { + _lastHardKey = Common::KEYCODE_INVALID; return; // "introearly" } @@ -181,15 +181,15 @@ void DreamWebEngine::intro() { loadIntroRoom(); runIntroSeq(); - if (_lastHardKey == 1) { - _lastHardKey = 0; + if (_lastHardKey == Common::KEYCODE_ESCAPE) { + _lastHardKey = Common::KEYCODE_INVALID; return; // "introearly" } getRidOfTempText(); clearBeforeLoad(); - _lastHardKey = 0; + _lastHardKey = Common::KEYCODE_INVALID; } void DreamWebEngine::runIntroSeq() { @@ -198,13 +198,13 @@ void DreamWebEngine::runIntroSeq() { do { waitForVSync(); - if (_lastHardKey == 1) + if (_lastHardKey == Common::KEYCODE_ESCAPE) break; spriteUpdate(); waitForVSync(); - if (_lastHardKey == 1) + if (_lastHardKey == Common::KEYCODE_ESCAPE) break; delEverything(); @@ -214,20 +214,20 @@ void DreamWebEngine::runIntroSeq() { useTimedText(); waitForVSync(); - if (_lastHardKey == 1) + if (_lastHardKey == Common::KEYCODE_ESCAPE) break; dumpMap(); dumpTimedText(); waitForVSync(); - if (_lastHardKey == 1) + if (_lastHardKey == Common::KEYCODE_ESCAPE) break; } while (_getBack != 1); - if (_lastHardKey == 1) { + if (_lastHardKey == Common::KEYCODE_ESCAPE) { getRidOfTempText(); clearBeforeLoad(); } @@ -266,7 +266,7 @@ void DreamWebEngine::loadIntroRoom() { _mapOffsetY = 16; clearSprites(); _vars._throughDoor = 0; - _currentKey = '0'; + _currentKey = 0; _mainMode = 0; clearWork(); _vars._newObs = 1; @@ -293,24 +293,24 @@ void DreamWebEngine::realCredits() { hangOne(2); - if (_lastHardKey == 1) { - _lastHardKey = 0; + if (_lastHardKey == Common::KEYCODE_ESCAPE) { + _lastHardKey = Common::KEYCODE_INVALID; return; // "realcreditsearly" } allPalette(); hangOne(80); - if (_lastHardKey == 1) { - _lastHardKey = 0; + if (_lastHardKey == Common::KEYCODE_ESCAPE) { + _lastHardKey = Common::KEYCODE_INVALID; return; // "realcreditsearly" } fadeScreenDowns(); hangOne(256); - if (_lastHardKey == 1) { - _lastHardKey = 0; + if (_lastHardKey == Common::KEYCODE_ESCAPE) { + _lastHardKey = Common::KEYCODE_INVALID; return; // "realcreditsearly" } @@ -318,24 +318,24 @@ void DreamWebEngine::realCredits() { _sound->playChannel0(12, 0); hangOne(2); - if (_lastHardKey == 1) { - _lastHardKey = 0; + if (_lastHardKey == Common::KEYCODE_ESCAPE) { + _lastHardKey = Common::KEYCODE_INVALID; return; // "realcreditsearly" } allPalette(); hangOne(80); - if (_lastHardKey == 1) { - _lastHardKey = 0; + if (_lastHardKey == Common::KEYCODE_ESCAPE) { + _lastHardKey = Common::KEYCODE_INVALID; return; // "realcreditsearly" } fadeScreenDowns(); hangOne(256); - if (_lastHardKey == 1) { - _lastHardKey = 0; + if (_lastHardKey == Common::KEYCODE_ESCAPE) { + _lastHardKey = Common::KEYCODE_INVALID; return; // "realcreditsearly" } @@ -343,24 +343,24 @@ void DreamWebEngine::realCredits() { _sound->playChannel0(12, 0); hangOne(2); - if (_lastHardKey == 1) { - _lastHardKey = 0; + if (_lastHardKey == Common::KEYCODE_ESCAPE) { + _lastHardKey = Common::KEYCODE_INVALID; return; // "realcreditsearly" } allPalette(); hangOne(80); - if (_lastHardKey == 1) { - _lastHardKey = 0; + if (_lastHardKey == Common::KEYCODE_ESCAPE) { + _lastHardKey = Common::KEYCODE_INVALID; return; // "realcreditsearly" } fadeScreenDowns(); hangOne(256); - if (_lastHardKey == 1) { - _lastHardKey = 0; + if (_lastHardKey == Common::KEYCODE_ESCAPE) { + _lastHardKey = Common::KEYCODE_INVALID; return; // "realcreditsearly" } @@ -368,24 +368,24 @@ void DreamWebEngine::realCredits() { _sound->playChannel0(12, 0); hangOne(2); - if (_lastHardKey == 1) { - _lastHardKey = 0; + if (_lastHardKey == Common::KEYCODE_ESCAPE) { + _lastHardKey = Common::KEYCODE_INVALID; return; // "realcreditsearly" } allPalette(); hangOne(80); - if (_lastHardKey == 1) { - _lastHardKey = 0; + if (_lastHardKey == Common::KEYCODE_ESCAPE) { + _lastHardKey = Common::KEYCODE_INVALID; return; // "realcreditsearly" } fadeScreenDowns(); hangOne(256); - if (_lastHardKey == 1) { - _lastHardKey = 0; + if (_lastHardKey == Common::KEYCODE_ESCAPE) { + _lastHardKey = Common::KEYCODE_INVALID; return; // "realcreditsearly" } @@ -393,24 +393,24 @@ void DreamWebEngine::realCredits() { _sound->playChannel0(12, 0); hangOne(2); - if (_lastHardKey == 1) { - _lastHardKey = 0; + if (_lastHardKey == Common::KEYCODE_ESCAPE) { + _lastHardKey = Common::KEYCODE_INVALID; return; // "realcreditsearly" } allPalette(); hangOne(80); - if (_lastHardKey == 1) { - _lastHardKey = 0; + if (_lastHardKey == Common::KEYCODE_ESCAPE) { + _lastHardKey = Common::KEYCODE_INVALID; return; // "realcreditsearly" } fadeScreenDowns(); hangOne(256); - if (_lastHardKey == 1) { - _lastHardKey = 0; + if (_lastHardKey == Common::KEYCODE_ESCAPE) { + _lastHardKey = Common::KEYCODE_INVALID; return; // "realcreditsearly" } @@ -418,23 +418,23 @@ void DreamWebEngine::realCredits() { fadeScreenUps(); hangOne(60); - if (_lastHardKey == 1) { - _lastHardKey = 0; + if (_lastHardKey == Common::KEYCODE_ESCAPE) { + _lastHardKey = Common::KEYCODE_INVALID; return; // "realcreditsearly" } _sound->playChannel0(13, 0); hangOne(350); - if (_lastHardKey == 1) { - _lastHardKey = 0; + if (_lastHardKey == Common::KEYCODE_ESCAPE) { + _lastHardKey = Common::KEYCODE_INVALID; return; // "realcreditsearly" } fadeScreenDowns(); hangOne(256); - _lastHardKey = 0; + _lastHardKey = Common::KEYCODE_INVALID; } } // End of namespace DreamWeb diff --git a/engines/dreamweb/use.cpp b/engines/dreamweb/use.cpp index 995eef04cd..476f847c40 100644 --- a/engines/dreamweb/use.cpp +++ b/engines/dreamweb/use.cpp @@ -131,7 +131,7 @@ void DreamWebEngine::useRoutine() { uint8 dummy; void *obj = getAnyAd(&dummy, &dummy); - for (size_t i = 0; i < sizeof(kUseList)/sizeof(UseListEntry); ++i) { + for (uint i = 0; i < ARRAYSIZE(kUseList); ++i) { const UseListEntry &entry = kUseList[i]; if (objectMatches(obj, entry.id)) { (this->*entry.callback)(); diff --git a/engines/dreamweb/vgafades.cpp b/engines/dreamweb/vgafades.cpp index c8f05641b5..d1e2480f70 100644 --- a/engines/dreamweb/vgafades.cpp +++ b/engines/dreamweb/vgafades.cpp @@ -56,8 +56,8 @@ void DreamWebEngine::fadeDOS() { //processEvents will be called from waitForVSync uint8 *dst = _startPal; getPalette(dst, 0, 64); - for (int fade = 0; fade < 64; ++fade) { - for (int c = 0; c < 768; ++c) { //original sources decrement 768 values -> 256 colors + for (uint fade = 0; fade < 64; ++fade) { + for (uint c = 0; c < 768; ++c) { //original sources decrement 768 values -> 256 colors if (dst[c]) { --dst[c]; } @@ -88,7 +88,7 @@ void DreamWebEngine::fadeCalculation() { uint8 *startPal = _startPal; const uint8 *endPal = _endPal; - for (size_t i = 0; i < 256 * 3; ++i) { + for (uint i = 0; i < 256 * 3; ++i) { uint8 s = startPal[i]; uint8 e = endPal[i]; if (s == e) @@ -212,7 +212,7 @@ void DreamWebEngine::fadeScreenDownHalf() { const uint8 *startPal = _startPal; uint8 *endPal = _endPal; - for (int i = 0; i < 256 * 3; ++i) { + for (uint i = 0; i < 256 * 3; ++i) { *endPal >>= 1; endPal++; } @@ -239,7 +239,7 @@ void DreamWebEngine::greyscaleSum() { byte *src = _mainPal; byte *dst = _endPal; - for (int i = 0; i < 256; ++i) { + for (uint i = 0; i < 256; ++i) { const unsigned int r = 20 * *src++; const unsigned int g = 59 * *src++; const unsigned int b = 11 * *src++; diff --git a/engines/dreamweb/vgagrafx.cpp b/engines/dreamweb/vgagrafx.cpp index ec306c4924..d8984d312b 100644 --- a/engines/dreamweb/vgagrafx.cpp +++ b/engines/dreamweb/vgagrafx.cpp @@ -23,6 +23,7 @@ #include "dreamweb/dreamweb.h" #include "engines/util.h" #include "graphics/surface.h" +#include "graphics/decoders/pcx.h" namespace DreamWeb { @@ -30,14 +31,16 @@ const uint16 kZoomx = 8; const uint16 kZoomy = 132; void DreamWebEngine::multiGet(uint8 *dst, uint16 x, uint16 y, uint8 w, uint8 h) { - assert(x < 320); - assert(y < 200); + assert(x < kScreenwidth); + assert(y < kScreenheight); + const uint8 *src = workspace() + x + y * kScreenwidth; - if (y + h > 200) - h = 200 - y; - if (x + w > 320) - w = 320 - x; - //debug(1, "multiGet %u,%u %ux%u -> segment: %04x->%04x", x, y, w, h, (uint16)ds, (uint16)es); + + if (y + h > kScreenheight) + h = kScreenheight - y; + if (x + w > kScreenwidth) + w = kScreenwidth - x; + for (unsigned l = 0; l < h; ++l) { const uint8 *src_p = src + kScreenwidth * l; uint8 *dst_p = dst + w * l; @@ -46,14 +49,16 @@ void DreamWebEngine::multiGet(uint8 *dst, uint16 x, uint16 y, uint8 w, uint8 h) } void DreamWebEngine::multiPut(const uint8 *src, uint16 x, uint16 y, uint8 w, uint8 h) { - assert(x < 320); - assert(y < 200); + assert(x < kScreenwidth); + assert(y < kScreenheight); + uint8 *dst = workspace() + x + y * kScreenwidth; - if (y + h > 200) - h = 200 - y; - if (x + w > 320) - w = 320 - x; - //debug(1, "multiPut %ux%u -> segment: %04x->%04x", w, h, (uint16)ds, (uint16)es); + + if (y + h > kScreenheight) + h = kScreenheight - y; + if (x + w > kScreenwidth) + w = kScreenwidth - x; + for (unsigned l = 0; l < h; ++l) { const uint8 *src_p = src + w * l; uint8 *dst_p = dst + kScreenwidth * l; @@ -63,12 +68,11 @@ void DreamWebEngine::multiPut(const uint8 *src, uint16 x, uint16 y, uint8 w, uin void DreamWebEngine::multiDump(uint16 x, uint16 y, uint8 width, uint8 height) { unsigned offset = x + y * kScreenwidth; - //debug(1, "multiDump %ux%u(segment: %04x) -> %d,%d(address: %d)", w, h, (uint16)ds, x, y, offset); blit(workspace() + offset, kScreenwidth, x, y, width, height); } void DreamWebEngine::workToScreen() { - blit(workspace(), 320, 0, 0, 320, 200); + blit(workspace(), kScreenwidth, 0, 0, kScreenwidth, kScreenheight); } void DreamWebEngine::frameOutNm(uint8 *dst, const uint8 *src, uint16 pitch, uint16 width, uint16 height, uint16 x, uint16 y) { @@ -146,82 +150,45 @@ void DreamWebEngine::doShake() { void DreamWebEngine::setMode() { waitForVSync(); - initGraphics(320, 200, false); + initGraphics(kScreenwidth, kScreenheight, false); } void DreamWebEngine::showPCX(const Common::String &suffix) { Common::String name = getDatafilePrefix() + suffix; Common::File pcxFile; - if (!pcxFile.open(name)) { warning("showpcx: Could not open '%s'", name.c_str()); return; } - uint8 *mainGamePal; - int i, j; + Graphics::PCXDecoder pcx; + if (!pcx.loadStream(pcxFile)) { + warning("showpcx: Could not process '%s'", name.c_str()); + return; + } // Read the 16-color palette into the 'maingamepal' buffer. Note that // the color components have to be adjusted from 8 to 6 bits. - - pcxFile.seek(16, SEEK_SET); - mainGamePal = _mainPal; - pcxFile.read(mainGamePal, 48); - - memset(mainGamePal + 48, 0xff, 720); - for (i = 0; i < 48; i++) { - mainGamePal[i] >>= 2; + memset(_mainPal, 0xff, 256 * 3); + memcpy(_mainPal, pcx.getPalette(), 48); + for (int i = 0; i < 48; i++) { + _mainPal[i] >>= 2; } - // Decode the image data. - Graphics::Surface *s = g_system->lockScreen(); - Common::Rect rect(640, 480); - - s->fillRect(rect, 0); - pcxFile.seek(128, SEEK_SET); - - for (int y = 0; y < 480; y++) { - byte *dst = (byte *)s->getBasePtr(0, y); - int decoded = 0; - - while (decoded < 320) { - byte col = pcxFile.readByte(); - byte len; - - if ((col & 0xc0) == 0xc0) { - len = col & 0x3f; - col = pcxFile.readByte(); - } else { - len = 1; - } - - // The image uses 16 colors and is stored as four bit - // planes, one for each bit of the color, least - // significant bit plane first. - - for (i = 0; i < len; i++) { - int plane = decoded / 80; - int pos = decoded % 80; - - for (j = 0; j < 8; j++) { - byte bit = (col >> (7 - j)) & 1; - dst[8 * pos + j] |= (bit << plane); - } - - decoded++; - } - } - } - + s->fillRect(Common::Rect(640, 480), 0); + const Graphics::Surface *pcxSurface = pcx.getSurface(); + if (pcxSurface->format.bytesPerPixel != 1) + error("Invalid bytes per pixel in PCX surface (%d)", pcxSurface->format.bytesPerPixel); + for (uint16 y = 0; y < pcxSurface->h; y++) + memcpy((byte *)s->getBasePtr(0, y), pcxSurface->getBasePtr(0, y), pcxSurface->w); g_system->unlockScreen(); - pcxFile.close(); } void DreamWebEngine::frameOutV(uint8 *dst, const uint8 *src, uint16 pitch, uint16 width, uint16 height, int16 x, int16 y) { // NB : These resilience checks were not in the original engine, but did they result in undefined behaviour // or was something broken during porting to C++? - assert(pitch == 320); + assert(pitch == kScreenwidth); if (x < 0) { assert(width >= -x); @@ -235,15 +202,16 @@ void DreamWebEngine::frameOutV(uint8 *dst, const uint8 *src, uint16 pitch, uint1 src += (-y) * width; y = 0; } - if (x >= 320) + + if ((uint16)x >= kScreenwidth) return; - if (y >= 200) + if ((uint16)y >= kScreenheight) return; - if (x + width > 320) { - width = 320 - x; + if ((uint16)x + width > kScreenwidth) { + width = kScreenwidth - x; } - if (y + height > 200) { - height = 200 - y; + if ((uint16)y + height > kScreenheight) { + height = kScreenheight - y; } uint16 stride = pitch - width; @@ -282,20 +250,20 @@ void DreamWebEngine::showFrameInternal(const uint8 *pSrc, uint16 x, uint16 y, ui //addToPrintList(x - _mapAdX, y - _mapAdY); // NB: Commented in the original asm } if (effectsFlag & 4) { // flippedX - frameOutFx(workspace(), pSrc, 320, width, height, x, y); + frameOutFx(workspace(), pSrc, kScreenwidth, width, height, x, y); return; } if (effectsFlag & 2) { // noMask - frameOutNm(workspace(), pSrc, 320, width, height, x, y); + frameOutNm(workspace(), pSrc, kScreenwidth, width, height, x, y); return; } if (effectsFlag & 32) { - frameOutBh(workspace(), pSrc, 320, width, height, x, y); + frameOutBh(workspace(), pSrc, kScreenwidth, width, height, x, y); return; } } // "noEffects" - frameOutV(workspace(), pSrc, 320, width, height, x, y); + frameOutV(workspace(), pSrc, kScreenwidth, width, height, x, y); } void DreamWebEngine::showFrame(const GraphicsFile &frameData, uint16 x, uint16 y, uint16 frameNumber, uint8 effectsFlag, uint8 *width, uint8 *height) { @@ -321,7 +289,7 @@ void DreamWebEngine::showFrame(const GraphicsFile &frameData, uint16 x, uint16 y } void DreamWebEngine::clearWork() { - memset(workspace(), 0, 320*200); + memset(workspace(), 0, kScreenwidth*kScreenheight); } void DreamWebEngine::dumpZoom() { @@ -362,20 +330,20 @@ void DreamWebEngine::zoom() { putUnderZoom(); return; } - uint16 srcOffset = (_oldPointerY - 9) * 320 + (_oldPointerX - 11); - uint16 dstOffset = (kZoomy + 4) * 320 + (kZoomx + 5); + uint16 srcOffset = (_oldPointerY - 9) * kScreenwidth + (_oldPointerX - 11); + uint16 dstOffset = (kZoomy + 4) * kScreenwidth + (kZoomx + 5); const uint8 *src = workspace() + srcOffset; uint8 *dst = workspace() + dstOffset; - for (size_t i = 0; i < 20; ++i) { - for (size_t j = 0; j < 23; ++j) { + for (uint i = 0; i < 20; ++i) { + for (uint j = 0; j < 23; ++j) { uint8 v = src[j]; dst[2*j+0] = v; - dst[2*j+1] = v; - dst[2*j+320] = v; - dst[2*j+321] = v; + dst[2*j+1] = v; + dst[2*j+kScreenwidth] = v; + dst[2*j+kScreenwidth+1] = v; } - src += 320; - dst += 320*2; + src += kScreenwidth; + dst += kScreenwidth*2; } crosshair(); _didZoom = 1; @@ -411,7 +379,7 @@ void DreamWebEngine::loadPalFromIFF() { const uint8 *src = buf + 0x30; uint8 *dst = _mainPal; - for (size_t i = 0; i < 256*3; ++i) { + for (uint i = 0; i < 256*3; ++i) { uint8 c = src[i] / 4; if (_brightPalette) { if (c) { diff --git a/engines/engine.cpp b/engines/engine.cpp index 2ef4ecab60..c84404cc68 100644 --- a/engines/engine.cpp +++ b/engines/engine.cpp @@ -179,7 +179,12 @@ void initCommonGFX(bool defaultTo1XScaler) { } else { // Override global scaler with any game-specific define if (ConfMan.hasKey("gfx_mode")) { - g_system->setGraphicsMode(ConfMan.get("gfx_mode").c_str()); + Common::String gfxMode = ConfMan.get("gfx_mode"); + g_system->setGraphicsMode(gfxMode.c_str()); + + // HACK: For OpenGL modes, we will still honor the graphics scale override + if (defaultTo1XScaler && (gfxMode.equalsIgnoreCase("gl1") || gfxMode.equalsIgnoreCase("gl2") || gfxMode.equalsIgnoreCase("gl4"))) + g_system->resetGraphicsScale(); } } diff --git a/engines/engines.mk b/engines/engines.mk index 9939506b86..61004463fe 100644 --- a/engines/engines.mk +++ b/engines/engines.mk @@ -130,6 +130,11 @@ DEFINES += -DENABLE_PARALLACTION=$(ENABLE_PARALLACTION) MODULES += engines/parallaction endif +ifdef ENABLE_PEGASUS +DEFINES += -DENABLE_PEGASUS=$(ENABLE_PEGASUS) +MODULES += engines/pegasus +endif + ifdef ENABLE_QUEEN DEFINES += -DENABLE_QUEEN=$(ENABLE_QUEEN) MODULES += engines/queen @@ -197,6 +202,11 @@ DEFINES += -DENABLE_TOLTECS=$(ENABLE_TOLTECS) MODULES += engines/toltecs endif +ifdef ENABLE_TONY +DEFINES += -DENABLE_TONY=$(ENABLE_TONY) +MODULES += engines/tony +endif + ifdef ENABLE_TOON DEFINES += -DENABLE_TOON=$(ENABLE_TOON) MODULES += engines/toon @@ -216,3 +226,8 @@ ifdef ENABLE_TUCKER DEFINES += -DENABLE_TUCKER=$(ENABLE_TUCKER) MODULES += engines/tucker endif + +ifdef ENABLE_WINTERMUTE +DEFINES += -DENABLE_WINTERMUTE=$(ENABLE_WINTERMUTE) +MODULES += engines/wintermute +endif diff --git a/engines/gob/anifile.cpp b/engines/gob/anifile.cpp index 2671fe0405..085ac800cd 100644 --- a/engines/gob/anifile.cpp +++ b/engines/gob/anifile.cpp @@ -37,30 +37,38 @@ ANIFile::ANIFile(GobEngine *vm, const Common::String &fileName, uint16 width, uint8 bpp) : _vm(vm), _width(width), _bpp(bpp), _hasPadding(false) { - Common::SeekableReadStream *ani = _vm->_dataIO->getFile(fileName); - if (ani) { - Common::SeekableSubReadStreamEndian sub(ani, 0, ani->size(), false, DisposeAfterUse::YES); + bool bigEndian = false; + Common::String endianFileName = fileName; - load(sub, fileName); - return; - } + if ((_vm->getEndiannessMethod() == kEndiannessMethodAltFile) && + !_vm->_dataIO->hasFile(fileName)) { + // If the game has alternate big-endian files, look if one exist - // File doesn't exist, try to open the big-endian'd alternate file - Common::String alternateFileName = fileName; - alternateFileName.setChar('_', 0); + Common::String alternateFileName = fileName; + alternateFileName.setChar('_', 0); - ani = _vm->_dataIO->getFile(alternateFileName); + if (_vm->_dataIO->hasFile(alternateFileName)) { + bigEndian = true; + endianFileName = alternateFileName; + } + } else if ((_vm->getEndiannessMethod() == kEndiannessMethodBE) || + ((_vm->getEndiannessMethod() == kEndiannessMethodSystem) && + (_vm->getEndianness() == kEndiannessBE))) + // Game always little endian or it follows the system and it is big endian + bigEndian = true; + + Common::SeekableReadStream *ani = _vm->_dataIO->getFile(endianFileName); if (ani) { - Common::SeekableSubReadStreamEndian sub(ani, 0, ani->size(), true, DisposeAfterUse::YES); + Common::SeekableSubReadStreamEndian sub(ani, 0, ani->size(), bigEndian, DisposeAfterUse::YES); // The big endian version pads a few fields to even size - _hasPadding = true; + _hasPadding = bigEndian; load(sub, fileName); return; } - warning("ANIFile::ANIFile(): No such file \"%s\"", fileName.c_str()); + warning("ANIFile::ANIFile(): No such file \"%s\" (\"%s\")", endianFileName.c_str(), fileName.c_str()); } ANIFile::~ANIFile() { @@ -281,4 +289,9 @@ void ANIFile::drawLayer(Surface &dest, uint16 layer, uint16 part, _layers[layer]->draw(dest, part, x, y, transp); } +void ANIFile::recolor(uint8 from, uint8 to) { + for (LayerArray::iterator l = _layers.begin(); l != _layers.end(); ++l) + (*l)->recolor(from, to); +} + } // End of namespace Gob diff --git a/engines/gob/anifile.h b/engines/gob/anifile.h index b6d9c735b5..c930aafc6b 100644 --- a/engines/gob/anifile.h +++ b/engines/gob/anifile.h @@ -92,6 +92,9 @@ public: /** Draw an animation frame. */ void draw(Surface &dest, uint16 animation, uint16 frame, int16 x, int16 y) const; + /** Recolor the animation sprites. */ + void recolor(uint8 from, uint8 to); + private: typedef Common::Array<CMPFile *> LayerArray; typedef Common::Array<Animation> AnimationArray; diff --git a/engines/gob/aniobject.cpp b/engines/gob/aniobject.cpp index 8d739fb3a4..7e3668a0ce 100644 --- a/engines/gob/aniobject.cpp +++ b/engines/gob/aniobject.cpp @@ -28,23 +28,20 @@ namespace Gob { ANIObject::ANIObject(const ANIFile &ani) : _ani(&ani), _cmp(0), - _visible(false), _paused(false), _mode(kModeContinuous), - _x(0), _y(0), _background(0), _drawn(false) { + _visible(false), _paused(false), _mode(kModeContinuous), _x(0), _y(0) { setAnimation(0); setPosition(); } ANIObject::ANIObject(const CMPFile &cmp) : _ani(0), _cmp(&cmp), - _visible(false), _paused(false), _mode(kModeContinuous), - _x(0), _y(0), _background(0), _drawn(false) { + _visible(false), _paused(false), _mode(kModeContinuous), _x(0), _y(0) { setAnimation(0); setPosition(); } ANIObject::~ANIObject() { - delete _background; } void ANIObject::setVisible(bool visible) { @@ -104,7 +101,7 @@ void ANIObject::getPosition(int16 &x, int16 &y) const { y = _y; } -void ANIObject::getFramePosition(int16 &x, int16 &y) const { +void ANIObject::getFramePosition(int16 &x, int16 &y, uint16 n) const { // CMP "animations" have no specific frame positions if (_cmp) { getPosition(x, y); @@ -118,11 +115,24 @@ void ANIObject::getFramePosition(int16 &x, int16 &y) const { if (_frame >= animation.frameCount) return; - x = _x + animation.frameAreas[_frame].left; - y = _y + animation.frameAreas[_frame].top; + // If we're paused, we don't advance any frames + if (_paused) + n = 0; + + // Number of cycles run through after n frames + uint16 cycles = (_frame + n) / animation.frameCount; + // Frame position after n frames + uint16 frame = (_frame + n) % animation.frameCount; + + // Only doing one cycle? + if (_mode == kModeOnce) + cycles = MAX<uint16>(cycles, 1); + + x = _x + animation.frameAreas[frame].left + cycles * animation.deltaX; + y = _y + animation.frameAreas[frame].top + cycles * animation.deltaY; } -void ANIObject::getFrameSize(int16 &width, int16 &height) const { +void ANIObject::getFrameSize(int16 &width, int16 &height, uint16 n) const { if (_cmp) { width = _cmp->getWidth (_animation); height = _cmp->getHeight(_animation); @@ -137,8 +147,15 @@ void ANIObject::getFrameSize(int16 &width, int16 &height) const { if (_frame >= animation.frameCount) return; - width = animation.frameAreas[_frame].right - animation.frameAreas[_frame].left + 1; - height = animation.frameAreas[_frame].bottom - animation.frameAreas[_frame].top + 1; + // If we're paused, we don't advance any frames + if (_paused) + n = 0; + + // Frame position after n frames + uint16 frame = (_frame + n) % animation.frameCount; + + width = animation.frameAreas[frame].right - animation.frameAreas[frame].left + 1; + height = animation.frameAreas[frame].bottom - animation.frameAreas[frame].top + 1; } bool ANIObject::isIn(int16 x, int16 y) const { @@ -188,46 +205,36 @@ bool ANIObject::draw(Surface &dest, int16 &left, int16 &top, bool ANIObject::drawCMP(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom) { - if (!_background) { + if (!hasBuffer()) { uint16 width, height; _cmp->getMaxSize(width, height); - _background = new Surface(width, height, dest.getBPP()); + resizeBuffer(width, height); } - const uint16 cR = _cmp->getWidth (_animation) - 1; - const uint16 cB = _cmp->getHeight(_animation) - 1; - - _backgroundLeft = CLIP<int16>( + _x, 0, dest.getWidth () - 1); - _backgroundTop = CLIP<int16>( + _y, 0, dest.getHeight() - 1); - _backgroundRight = CLIP<int16>(cR + _x, 0, dest.getWidth () - 1); - _backgroundBottom = CLIP<int16>(cB + _y, 0, dest.getHeight() - 1); + left = _x; + top = _y; + right = _x + _cmp->getWidth (_animation) - 1; + bottom = _y + _cmp->getHeight(_animation) - 1; - _background->blit(dest, _backgroundLeft , _backgroundTop, - _backgroundRight, _backgroundBottom, 0, 0); + if (!saveScreen(dest, left, top, right, bottom)) + return false; _cmp->draw(dest, _animation, _x, _y, 0); - _drawn = true; - - left = _backgroundLeft; - top = _backgroundTop; - right = _backgroundRight; - bottom = _backgroundBottom; - return true; } bool ANIObject::drawANI(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom) { - if (!_background) { + if (!hasBuffer()) { uint16 width, height; _ani->getMaxSize(width, height); - _background = new Surface(width, height, dest.getBPP()); + resizeBuffer(width, height); } const ANIFile::Animation &animation = _ani->getAnimationInfo(_animation); @@ -236,45 +243,23 @@ bool ANIObject::drawANI(Surface &dest, int16 &left, int16 &top, const ANIFile::FrameArea &area = animation.frameAreas[_frame]; - _backgroundLeft = CLIP<int16>(area.left + _x, 0, dest.getWidth () - 1); - _backgroundTop = CLIP<int16>(area.top + _y, 0, dest.getHeight() - 1); - _backgroundRight = CLIP<int16>(area.right + _x, 0, dest.getWidth () - 1); - _backgroundBottom = CLIP<int16>(area.bottom + _y, 0, dest.getHeight() - 1); + left = _x + area.left; + top = _y + area.top; + right = _x + area.right; + bottom = _y + area.bottom; - _background->blit(dest, _backgroundLeft , _backgroundTop, - _backgroundRight, _backgroundBottom, 0, 0); + if (!saveScreen(dest, left, top, right, bottom)) + return false; _ani->draw(dest, _animation, _frame, _x, _y); - _drawn = true; - - left = _backgroundLeft; - top = _backgroundTop; - right = _backgroundRight; - bottom = _backgroundBottom; - return true; } bool ANIObject::clear(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom) { - if (!_drawn) - return false; - - const int16 bgRight = _backgroundRight - _backgroundLeft; - const int16 bgBottom = _backgroundBottom - _backgroundTop; - - dest.blit(*_background, 0, 0, bgRight, bgBottom, _backgroundLeft, _backgroundTop); - - _drawn = false; - - left = _backgroundLeft; - top = _backgroundTop; - right = _backgroundRight; - bottom = _backgroundBottom; - - return true; + return restoreScreen(dest, left, top, right, bottom); } void ANIObject::advance() { diff --git a/engines/gob/aniobject.h b/engines/gob/aniobject.h index 00f42b43ce..d8c8edc2b8 100644 --- a/engines/gob/aniobject.h +++ b/engines/gob/aniobject.h @@ -25,6 +25,8 @@ #include "common/system.h" +#include "gob/backbuffer.h" + namespace Gob { class ANIFile; @@ -32,7 +34,7 @@ class CMPFile; class Surface; /** An ANI object, controlling an animation within an ANI file. */ -class ANIObject { +class ANIObject : public BackBuffer { public: enum Mode { kModeContinuous, ///< Play the animation continuously. @@ -68,10 +70,10 @@ public: /** Return the current position. */ void getPosition(int16 &x, int16 &y) const; - /** Return the current frame position. */ - void getFramePosition(int16 &x, int16 &y) const; - /** Return the current frame size. */ - void getFrameSize(int16 &width, int16 &height) const; + /** Return the frame position after another n frames. */ + void getFramePosition(int16 &x, int16 &y, uint16 n = 0) const; + /** Return the current frame size after another n frames. */ + void getFrameSize(int16 &width, int16 &height, uint16 n = 0) const; /** Are there coordinates within the animation sprite? */ bool isIn(int16 x, int16 y) const; @@ -118,13 +120,6 @@ private: int16 _x; ///< The current X position. int16 _y; ///< The current Y position. - Surface *_background; ///< The saved background. - bool _drawn; ///< Was the animation drawn? - - int16 _backgroundLeft; ///< The left position of the saved background. - int16 _backgroundTop; ///< The top of the saved background. - int16 _backgroundRight; ///< The right position of the saved background. - int16 _backgroundBottom; ///< The bottom position of the saved background. bool drawCMP(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom); bool drawANI(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom); diff --git a/engines/gob/backbuffer.cpp b/engines/gob/backbuffer.cpp new file mode 100644 index 0000000000..752042d46e --- /dev/null +++ b/engines/gob/backbuffer.cpp @@ -0,0 +1,100 @@ +/* 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 "common/util.h" + +#include "gob/backbuffer.h" +#include "gob/surface.h" + +namespace Gob { + +BackBuffer::BackBuffer() : _background(0), _saved(false) { +} + +BackBuffer::~BackBuffer() { + delete _background; +} + +bool BackBuffer::hasBuffer() const { + return _background != 0; +} + +bool BackBuffer::hasSavedBackground() const { + return _saved; +} + +void BackBuffer::trashBuffer() { + _saved = false; +} + +void BackBuffer::resizeBuffer(uint16 width, uint16 height) { + trashBuffer(); + + if (_background && (_background->getWidth() == width) && (_background->getHeight() == height)) + return; + + delete _background; + + _background = new Surface(width, height, 1); +} + +bool BackBuffer::saveScreen(const Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom) { + if (!_background) + return false; + + const int16 width = MIN<int16>(right - left + 1, _background->getWidth ()); + const int16 height = MIN<int16>(bottom - top + 1, _background->getHeight()); + if ((width <= 0) || (height <= 0)) + return false; + + right = left + width - 1; + bottom = top + height - 1; + + _saveLeft = left; + _saveTop = top; + _saveRight = right; + _saveBottom = bottom; + + _background->blit(dest, _saveLeft, _saveTop, _saveRight, _saveBottom, 0, 0); + + _saved = true; + + return true; +} + +bool BackBuffer::restoreScreen(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom) { + if (!_saved) + return false; + + left = _saveLeft; + top = _saveTop; + right = _saveRight; + bottom = _saveBottom; + + dest.blit(*_background, 0, 0, right - left, bottom - top, left, top); + + _saved = false; + + return true; +} + +} // End of namespace Gob diff --git a/engines/gob/backbuffer.h b/engines/gob/backbuffer.h new file mode 100644 index 0000000000..c978689e9f --- /dev/null +++ b/engines/gob/backbuffer.h @@ -0,0 +1,59 @@ +/* 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 GOB_BACKBUFFER_H +#define GOB_BACKBUFFER_H + +#include "common/system.h" + +namespace Gob { + +class Surface; + +class BackBuffer { +public: + BackBuffer(); + ~BackBuffer(); + +protected: + void trashBuffer(); + void resizeBuffer(uint16 width, uint16 height); + + bool saveScreen (const Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom); + bool restoreScreen( Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom); + + bool hasBuffer() const; + bool hasSavedBackground() const; + +private: + Surface *_background; ///< The saved background. + bool _saved; ///< Was the background saved? + + int16 _saveLeft; ///< The left position of the saved background. + int16 _saveTop; ///< The top of the saved background. + int16 _saveRight; ///< The right position of the saved background. + int16 _saveBottom; ///< The bottom position of the saved background. +}; + +} // End of namespace Gob + +#endif // GOB_BACKBUFFER_H diff --git a/engines/gob/cmpfile.cpp b/engines/gob/cmpfile.cpp index 7b21c4c835..d304958f76 100644 --- a/engines/gob/cmpfile.cpp +++ b/engines/gob/cmpfile.cpp @@ -21,6 +21,7 @@ */ #include "common/stream.h" +#include "common/substream.h" #include "common/str.h" #include "gob/gob.h" @@ -143,7 +144,13 @@ void CMPFile::loadCMP(Common::SeekableReadStream &cmp) { } void CMPFile::loadRXY(Common::SeekableReadStream &rxy) { - _coordinates = new RXYFile(rxy); + bool bigEndian = (_vm->getEndiannessMethod() == kEndiannessMethodBE) || + ((_vm->getEndiannessMethod() == kEndiannessMethodSystem) && + (_vm->getEndianness() == kEndiannessBE)); + + Common::SeekableSubReadStreamEndian sub(&rxy, 0, rxy.size(), bigEndian, DisposeAfterUse::NO); + + _coordinates = new RXYFile(sub); for (uint i = 0; i < _coordinates->size(); i++) { const RXYFile::Coordinates &c = (*_coordinates)[i]; @@ -243,4 +250,9 @@ uint16 CMPFile::addSprite(uint16 left, uint16 top, uint16 right, uint16 bottom) return _coordinates->add(left, top, right, bottom); } +void CMPFile::recolor(uint8 from, uint8 to) { + if (_surface) + _surface->recolor(from, to); +} + } // End of namespace Gob diff --git a/engines/gob/cmpfile.h b/engines/gob/cmpfile.h index 2b669e4d38..9c858238af 100644 --- a/engines/gob/cmpfile.h +++ b/engines/gob/cmpfile.h @@ -70,6 +70,8 @@ public: uint16 addSprite(uint16 left, uint16 top, uint16 right, uint16 bottom); + void recolor(uint8 from, uint8 to); + private: GobEngine *_vm; diff --git a/engines/gob/decfile.cpp b/engines/gob/decfile.cpp index fb67c52627..85b4c09ca3 100644 --- a/engines/gob/decfile.cpp +++ b/engines/gob/decfile.cpp @@ -38,30 +38,38 @@ DECFile::DECFile(GobEngine *vm, const Common::String &fileName, uint16 width, uint16 height, uint8 bpp) : _vm(vm), _width(width), _height(height), _bpp(bpp), _hasPadding(false), _backdrop(0) { - Common::SeekableReadStream *dec = _vm->_dataIO->getFile(fileName); - if (dec) { - Common::SeekableSubReadStreamEndian sub(dec, 0, dec->size(), false, DisposeAfterUse::YES); - - load(sub, fileName); - return; - } - - // File doesn't exist, try to open the big-endian'd alternate file - Common::String alternateFileName = fileName; - alternateFileName.setChar('_', 0); - - dec = _vm->_dataIO->getFile(alternateFileName); - if (dec) { - Common::SeekableSubReadStreamEndian sub(dec, 0, dec->size(), true, DisposeAfterUse::YES); + bool bigEndian = false; + Common::String endianFileName = fileName; + + if ((_vm->getEndiannessMethod() == kEndiannessMethodAltFile) && + !_vm->_dataIO->hasFile(fileName)) { + // If the game has alternate big-endian files, look if one exist + + Common::String alternateFileName = fileName; + alternateFileName.setChar('_', 0); + + if (_vm->_dataIO->hasFile(alternateFileName)) { + bigEndian = true; + endianFileName = alternateFileName; + } + } else if ((_vm->getEndiannessMethod() == kEndiannessMethodBE) || + ((_vm->getEndiannessMethod() == kEndiannessMethodSystem) && + (_vm->getEndianness() == kEndiannessBE))) + // Game always little endian or it follows the system and it is big endian + bigEndian = true; + + Common::SeekableReadStream *ani = _vm->_dataIO->getFile(endianFileName); + if (ani) { + Common::SeekableSubReadStreamEndian sub(ani, 0, ani->size(), bigEndian, DisposeAfterUse::YES); // The big endian version pads a few fields to even size - _hasPadding = true; + _hasPadding = bigEndian; load(sub, fileName); return; } - warning("DECFile::DECFile(): No such file \"%s\"", fileName.c_str()); + warning("DECFile::DECFile(): No such file \"%s\" (\"%s\")", endianFileName.c_str(), fileName.c_str()); } DECFile::~DECFile() { diff --git a/engines/gob/detection/detection.cpp b/engines/gob/detection/detection.cpp index bcfd5dacfa..8fb0052a5b 100644 --- a/engines/gob/detection/detection.cpp +++ b/engines/gob/detection/detection.cpp @@ -25,40 +25,146 @@ #include "engines/obsolete.h" #include "gob/gob.h" +#include "gob/dataio.h" #include "gob/detection/tables.h" class GobMetaEngine : public AdvancedMetaEngine { public: - GobMetaEngine() : AdvancedMetaEngine(Gob::gameDescriptions, sizeof(Gob::GOBGameDescription), gobGames) { - _singleid = "gob"; - _guioptions = GUIO1(GUIO_NOLAUNCHLOAD); - } + GobMetaEngine(); - virtual GameDescriptor findGame(const char *gameid) const { - return Engines::findGameID(gameid, _gameids, obsoleteGameIDsTable); - } + virtual GameDescriptor findGame(const char *gameid) const; + + virtual const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const; + + virtual const char *getName() const; + virtual const char *getOriginalCopyright() const; + + virtual bool hasFeature(MetaEngineFeature f) const; + + virtual Common::Error createInstance(OSystem *syst, Engine **engine) const; + virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const; + +private: + /** + * Inspect the game archives to detect which Once Upon A Time game this is. + */ + static const Gob::GOBGameDescription *detectOnceUponATime(const Common::FSList &fslist); +}; + +GobMetaEngine::GobMetaEngine() : + AdvancedMetaEngine(Gob::gameDescriptions, sizeof(Gob::GOBGameDescription), gobGames) { + + _singleid = "gob"; + _guioptions = GUIO1(GUIO_NOLAUNCHLOAD); +} + +GameDescriptor GobMetaEngine::findGame(const char *gameid) const { + return Engines::findGameID(gameid, _gameids, obsoleteGameIDsTable); +} + +const ADGameDescription *GobMetaEngine::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const { + ADFilePropertiesMap filesProps; + + const Gob::GOBGameDescription *game; + game = (const Gob::GOBGameDescription *)detectGameFilebased(allFiles, fslist, Gob::fileBased, &filesProps); + if (!game) + return 0; - virtual const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const { - return detectGameFilebased(allFiles, Gob::fileBased); + if (game->gameType == Gob::kGameTypeOnceUponATime) { + game = detectOnceUponATime(fslist); + if (!game) + return 0; } - virtual const char *getName() const { - return "Gob"; + reportUnknown(fslist.begin()->getParent(), filesProps); + return (const ADGameDescription *)game; +} + +const Gob::GOBGameDescription *GobMetaEngine::detectOnceUponATime(const Common::FSList &fslist) { + // Add the game path to the search manager + SearchMan.clear(); + SearchMan.addDirectory(fslist.begin()->getParent().getPath(), fslist.begin()->getParent()); + + // Open the archives + Gob::DataIO dataIO; + if (!dataIO.openArchive("stk1.stk", true) || + !dataIO.openArchive("stk2.stk", true) || + !dataIO.openArchive("stk3.stk", true)) { + + SearchMan.clear(); + return 0; } - virtual const char *getOriginalCopyright() const { - return "Goblins Games (C) Coktel Vision"; + Gob::OnceUponATime gameType = Gob::kOnceUponATimeInvalid; + Gob::OnceUponATimePlatform platform = Gob::kOnceUponATimePlatformInvalid; + + // If these animal files are present, it's Abracadabra + if (dataIO.hasFile("arai.anm") && + dataIO.hasFile("crab.anm") && + dataIO.hasFile("crap.anm") && + dataIO.hasFile("drag.anm") && + dataIO.hasFile("guep.anm") && + dataIO.hasFile("loup.anm") && + dataIO.hasFile("mous.anm") && + dataIO.hasFile("rhin.anm") && + dataIO.hasFile("saut.anm") && + dataIO.hasFile("scor.anm")) + gameType = Gob::kOnceUponATimeAbracadabra; + + // If these animal files are present, it's Baba Yaga + if (dataIO.hasFile("abei.anm") && + dataIO.hasFile("arai.anm") && + dataIO.hasFile("drag.anm") && + dataIO.hasFile("fauc.anm") && + dataIO.hasFile("gren.anm") && + dataIO.hasFile("rena.anm") && + dataIO.hasFile("sang.anm") && + dataIO.hasFile("serp.anm") && + dataIO.hasFile("tort.anm") && + dataIO.hasFile("vaut.anm")) + gameType = Gob::kOnceUponATimeBabaYaga; + + // Detect the platform by endianness and existence of a MOD file + Common::SeekableReadStream *villeDEC = dataIO.getFile("ville.dec"); + if (villeDEC && (villeDEC->size() > 6)) { + byte data[6]; + + if (villeDEC->read(data, 6) == 6) { + if (!memcmp(data, "\000\000\000\001\000\007", 6)) { + // Big endian -> Amiga or Atari ST + + if (dataIO.hasFile("mod.babayaga")) + platform = Gob::kOnceUponATimePlatformAmiga; + else + platform = Gob::kOnceUponATimePlatformAtariST; + + } else if (!memcmp(data, "\000\000\001\000\007\000", 6)) + // Little endian -> DOS + platform = Gob::kOnceUponATimePlatformDOS; + } + + delete villeDEC; } - virtual bool hasFeature(MetaEngineFeature f) const; + SearchMan.clear(); - virtual Common::Error createInstance(OSystem *syst, Engine **engine) const { - Engines::upgradeTargetIfNecessary(obsoleteGameIDsTable); - return AdvancedMetaEngine::createInstance(syst, engine); + if ((gameType == Gob::kOnceUponATimeInvalid) || (platform == Gob::kOnceUponATimePlatformInvalid)) { + warning("GobMetaEngine::detectOnceUponATime(): Detection failed (%d, %d)", + (int) gameType, (int) platform); + return 0; } - virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const; -}; + + return &Gob::fallbackOnceUpon[gameType][platform]; +} + +const char *GobMetaEngine::getName() const { + return "Gob"; +} + +const char *GobMetaEngine::getOriginalCopyright() const { + return "Goblins Games (C) Coktel Vision"; +} bool GobMetaEngine::hasFeature(MetaEngineFeature f) const { return false; @@ -68,6 +174,12 @@ bool Gob::GobEngine::hasFeature(EngineFeature f) const { return (f == kSupportsRTL); } + +Common::Error GobMetaEngine::createInstance(OSystem *syst, Engine **engine) const { + Engines::upgradeTargetIfNecessary(obsoleteGameIDsTable); + return AdvancedMetaEngine::createInstance(syst, engine); +} + bool GobMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const { const Gob::GOBGameDescription *gd = (const Gob::GOBGameDescription *)desc; if (gd) { @@ -77,6 +189,7 @@ bool GobMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameD return gd != 0; } + #if PLUGIN_ENABLED_DYNAMIC(GOB) REGISTER_PLUGIN_DYNAMIC(GOB, PLUGIN_TYPE_ENGINE, GobMetaEngine); #else diff --git a/engines/gob/detection/tables.h b/engines/gob/detection/tables.h index 5d211ac7d8..271f75af79 100644 --- a/engines/gob/detection/tables.h +++ b/engines/gob/detection/tables.h @@ -48,7 +48,10 @@ static const PlainGameDescriptor gobGames[] = { {"gob2cd", "Gobliins 2 CD"}, {"ween", "Ween: The Prophecy"}, {"bargon", "Bargon Attack"}, + {"babayaga", "Once Upon A Time: Baba Yaga"}, + {"abracadabra", "Once Upon A Time: Abracadabra"}, {"littlered", "Once Upon A Time: Little Red Riding Hood"}, + {"onceupon", "Once Upon A Time"}, {"ajworld", "A.J.'s World of Discovery"}, {"gob3", "Goblins Quest 3"}, {"gob3cd", "Goblins Quest 3 CD"}, @@ -94,6 +97,7 @@ static const GOBGameDescription gameDescriptions[] = { #include "gob/detection/tables_ween.h" // Ween: The Prophecy #include "gob/detection/tables_bargon.h" // Bargon Attack #include "gob/detection/tables_littlered.h" // Once Upon A Time: Little Red Riding Hood + #include "gob/detection/tables_onceupon.h" // Once Upon A Time: Baba Yaga and Abracadabra #include "gob/detection/tables_lit.h" // Lost in Time #include "gob/detection/tables_fascin.h" // Fascination #include "gob/detection/tables_geisha.h" // Geisha diff --git a/engines/gob/detection/tables_ajworld.h b/engines/gob/detection/tables_ajworld.h index c78d11a6ad..d86bdb16be 100644 --- a/engines/gob/detection/tables_ajworld.h +++ b/engines/gob/detection/tables_ajworld.h @@ -1,4 +1,3 @@ - /* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names diff --git a/engines/gob/detection/tables_fallback.h b/engines/gob/detection/tables_fallback.h index 2853ee7b4f..05f579c08c 100644 --- a/engines/gob/detection/tables_fallback.h +++ b/engines/gob/detection/tables_fallback.h @@ -23,6 +23,8 @@ #ifndef GOB_DETECTION_TABLES_FALLBACK_H #define GOB_DETECTION_TABLES_FALLBACK_H +// -- Tables for the filename-based fallback -- + static const GOBGameDescription fallbackDescs[] = { { //0 { @@ -362,6 +364,20 @@ static const GOBGameDescription fallbackDescs[] = { }, { //24 { + "onceupon", + "unknown", + AD_ENTRY1(0, 0), + UNK_LANG, + kPlatformUnknown, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeOnceUponATime, + kFeaturesEGA, + 0, 0, 0 + }, + { //25 + { "adi2", "", AD_ENTRY1(0, 0), @@ -374,7 +390,7 @@ static const GOBGameDescription fallbackDescs[] = { kFeatures640x480, "adi2.stk", 0, 0 }, - { //25 + { //26 { "adi4", "", @@ -388,7 +404,7 @@ static const GOBGameDescription fallbackDescs[] = { kFeatures640x480, "adif41.stk", 0, 0 }, - { //26 + { //27 { "coktelplayer", "unknown", @@ -430,10 +446,119 @@ static const ADFileBasedFallback fileBased[] = { { &fallbackDescs[21].desc, { "disk1.stk", "disk2.stk", "disk3.stk", 0 } }, { &fallbackDescs[22].desc, { "intro.stk", "stk2.stk", "stk3.stk", 0 } }, { &fallbackDescs[23].desc, { "intro.stk", "stk2.stk", "stk3.stk", "mod.babayaga", 0 } }, - { &fallbackDescs[24].desc, { "adi2.stk", 0 } }, - { &fallbackDescs[25].desc, { "adif41.stk", "adim41.stk", 0 } }, - { &fallbackDescs[26].desc, { "coktelplayer.scn", 0 } }, + { &fallbackDescs[24].desc, { "stk1.stk", "stk2.stk", "stk3.stk", 0 } }, + { &fallbackDescs[25].desc, { "adi2.stk", 0 } }, + { &fallbackDescs[26].desc, { "adif41.stk", "adim41.stk", 0 } }, + { &fallbackDescs[27].desc, { "coktelplayer.scn", 0 } }, { 0, { 0 } } }; +// -- Tables for detecting the specific Once Upon A Time game -- + +enum OnceUponATime { + kOnceUponATimeInvalid = -1, + kOnceUponATimeAbracadabra = 0, + kOnceUponATimeBabaYaga = 1, + kOnceUponATimeMAX +}; + +enum OnceUponATimePlatform { + kOnceUponATimePlatformInvalid = -1, + kOnceUponATimePlatformDOS = 0, + kOnceUponATimePlatformAmiga = 1, + kOnceUponATimePlatformAtariST = 2, + kOnceUponATimePlatformMAX +}; + +static const GOBGameDescription fallbackOnceUpon[kOnceUponATimeMAX][kOnceUponATimePlatformMAX] = { + { // kOnceUponATimeAbracadabra + { // kOnceUponATimePlatformDOS + { + "abracadabra", + "", + AD_ENTRY1(0, 0), + UNK_LANG, + kPlatformPC, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeAbracadabra, + kFeaturesAdLib | kFeaturesEGA, + 0, 0, 0 + }, + { // kOnceUponATimePlatformAmiga + { + "abracadabra", + "", + AD_ENTRY1(0, 0), + UNK_LANG, + kPlatformAmiga, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeAbracadabra, + kFeaturesEGA, + 0, 0, 0 + }, + { // kOnceUponATimePlatformAtariST + { + "abracadabra", + "", + AD_ENTRY1(0, 0), + UNK_LANG, + kPlatformAtariST, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeAbracadabra, + kFeaturesEGA, + 0, 0, 0 + } + }, + { // kOnceUponATimeBabaYaga + { // kOnceUponATimePlatformDOS + { + "babayaga", + "", + AD_ENTRY1(0, 0), + UNK_LANG, + kPlatformPC, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeBabaYaga, + kFeaturesAdLib | kFeaturesEGA, + 0, 0, 0 + }, + { // kOnceUponATimePlatformAmiga + { + "babayaga", + "", + AD_ENTRY1(0, 0), + UNK_LANG, + kPlatformAmiga, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeBabaYaga, + kFeaturesEGA, + 0, 0, 0 + }, + { // kOnceUponATimePlatformAtariST + { + "babayaga", + "", + AD_ENTRY1(0, 0), + UNK_LANG, + kPlatformAtariST, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeBabaYaga, + kFeaturesEGA, + 0, 0, 0 + } + } +}; + #endif // GOB_DETECTION_TABLES_FALLBACK_H diff --git a/engines/gob/detection/tables_geisha.h b/engines/gob/detection/tables_geisha.h index d05659d9e5..a32d1ebf81 100644 --- a/engines/gob/detection/tables_geisha.h +++ b/engines/gob/detection/tables_geisha.h @@ -69,6 +69,34 @@ kFeaturesEGA | kFeaturesAdLib, "disk1.stk", "intro.tot", 0 }, +{ // Supplied by einstein95 in bug report #3544449 + { + "geisha", + "", + AD_ENTRY1s("disk1.stk", "49107ac897e7c00af6c4ecd78a74a710", 212169), + ES_ESP, + kPlatformPC, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeGeisha, + kFeaturesEGA | kFeaturesAdLib, + "disk1.stk", "intro.tot", 0 +}, +{ // Supplied by einstein95 in bug report #3544449 + { + "geisha", + "", + AD_ENTRY1s("disk1.stk", "49107ac897e7c00af6c4ecd78a74a710", 212169), + IT_ITA, + kPlatformPC, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeGeisha, + kFeaturesEGA | kFeaturesAdLib, + "disk1.stk", "intro.tot", 0 +}, { { "geisha", diff --git a/engines/gob/detection/tables_onceupon.h b/engines/gob/detection/tables_onceupon.h new file mode 100644 index 0000000000..366024d43c --- /dev/null +++ b/engines/gob/detection/tables_onceupon.h @@ -0,0 +1,518 @@ +/* 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. + * + */ + +/* Detection tables for Once Upon A Time: Baba Yaga and Abracadabra. */ + +#ifndef GOB_DETECTION_TABLES_ONCEUPON_H +#define GOB_DETECTION_TABLES_ONCEUPON_H + +// -- Once Upon A Time: Abracadabra, Amiga -- + +{ + { + "abracadabra", + "", + { + {"stk1.stk", 0, "a8e963eea170155548e5bc1d0f07d50d", 209806}, + {"stk2.stk", 0, "e4b21818af03930dc9cab2ad4c93cb5b", 362106}, + {"stk3.stk", 0, "76874ad92782f9b2de57beafc05ec877", 353482}, + {0, 0, 0, 0} + }, + FR_FRA, + kPlatformAmiga, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeAbracadabra, + kFeaturesEGA, + 0, 0, 0 +}, +{ + { + "abracadabra", + "", + { + {"stk1.stk", 0, "a8e963eea170155548e5bc1d0f07d50d", 209806}, + {"stk2.stk", 0, "e4b21818af03930dc9cab2ad4c93cb5b", 362106}, + {"stk3.stk", 0, "76874ad92782f9b2de57beafc05ec877", 353482}, + {0, 0, 0, 0} + }, + DE_DEU, + kPlatformAmiga, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeAbracadabra, + kFeaturesEGA, + 0, 0, 0 +}, +{ + { + "abracadabra", + "", + { + {"stk1.stk", 0, "a8e963eea170155548e5bc1d0f07d50d", 209806}, + {"stk2.stk", 0, "e4b21818af03930dc9cab2ad4c93cb5b", 362106}, + {"stk3.stk", 0, "76874ad92782f9b2de57beafc05ec877", 353482}, + {0, 0, 0, 0} + }, + EN_ANY, + kPlatformAmiga, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeAbracadabra, + kFeaturesEGA, + 0, 0, 0 +}, +{ + { + "abracadabra", + "", + { + {"stk1.stk", 0, "a8e963eea170155548e5bc1d0f07d50d", 209806}, + {"stk2.stk", 0, "e4b21818af03930dc9cab2ad4c93cb5b", 362106}, + {"stk3.stk", 0, "76874ad92782f9b2de57beafc05ec877", 353482}, + {0, 0, 0, 0} + }, + IT_ITA, + kPlatformAmiga, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeAbracadabra, + kFeaturesEGA, + 0, 0, 0 +}, +{ + { + "abracadabra", + "", + { + {"stk1.stk", 0, "a8e963eea170155548e5bc1d0f07d50d", 209806}, + {"stk2.stk", 0, "e4b21818af03930dc9cab2ad4c93cb5b", 362106}, + {"stk3.stk", 0, "76874ad92782f9b2de57beafc05ec877", 353482}, + {0, 0, 0, 0} + }, + ES_ESP, + kPlatformAmiga, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeAbracadabra, + kFeaturesEGA, + 0, 0, 0 +}, + +// -- Once Upon A Time: Abracadabra, Atari ST -- + +{ + { + "abracadabra", + "", + { + {"stk1.stk", 0, "a8e963eea170155548e5bc1d0f07d50d", 209806}, + {"stk2.stk", 0, "c6440aaf068ec3149ae89bc5c41ebf02", 362123}, + {"stk3.stk", 0, "5af3c1202ba6fcf8dad2b2125e1c1383", 353257}, + {0, 0, 0, 0} + }, + FR_FRA, + kPlatformAtariST, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeAbracadabra, + kFeaturesEGA, + 0, 0, 0 +}, +{ + { + "abracadabra", + "", + { + {"stk1.stk", 0, "a8e963eea170155548e5bc1d0f07d50d", 209806}, + {"stk2.stk", 0, "c6440aaf068ec3149ae89bc5c41ebf02", 362123}, + {"stk3.stk", 0, "5af3c1202ba6fcf8dad2b2125e1c1383", 353257}, + {0, 0, 0, 0} + }, + DE_DEU, + kPlatformAtariST, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeAbracadabra, + kFeaturesEGA, + 0, 0, 0 +}, +{ + { + "abracadabra", + "", + { + {"stk1.stk", 0, "a8e963eea170155548e5bc1d0f07d50d", 209806}, + {"stk2.stk", 0, "c6440aaf068ec3149ae89bc5c41ebf02", 362123}, + {"stk3.stk", 0, "5af3c1202ba6fcf8dad2b2125e1c1383", 353257}, + {0, 0, 0, 0} + }, + EN_ANY, + kPlatformAtariST, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeAbracadabra, + kFeaturesEGA, + 0, 0, 0 +}, +{ + { + "abracadabra", + "", + { + {"stk1.stk", 0, "a8e963eea170155548e5bc1d0f07d50d", 209806}, + {"stk2.stk", 0, "c6440aaf068ec3149ae89bc5c41ebf02", 362123}, + {"stk3.stk", 0, "5af3c1202ba6fcf8dad2b2125e1c1383", 353257}, + {0, 0, 0, 0} + }, + IT_ITA, + kPlatformAtariST, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeAbracadabra, + kFeaturesEGA, + 0, 0, 0 +}, +{ + { + "abracadabra", + "", + { + {"stk1.stk", 0, "a8e963eea170155548e5bc1d0f07d50d", 209806}, + {"stk2.stk", 0, "c6440aaf068ec3149ae89bc5c41ebf02", 362123}, + {"stk3.stk", 0, "5af3c1202ba6fcf8dad2b2125e1c1383", 353257}, + {0, 0, 0, 0} + }, + ES_ESP, + kPlatformAtariST, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeAbracadabra, + kFeaturesEGA, + 0, 0, 0 +}, + +// -- Once Upon A Time: Baba Yaga, DOS EGA Floppy -- + +{ + { + "babayaga", + "", + { + {"stk1.stk", 0, "3c777f43e6fb49fde9222543447e135a", 204813}, + {"stk2.stk", 0, "6cf0b009dd185a8f589e91a1f9c33df5", 361582}, + {"stk3.stk", 0, "6473183ca4db1b5b5cea047f9af59a26", 328925}, + {0, 0, 0, 0} + }, + FR_FRA, + kPlatformPC, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeBabaYaga, + kFeaturesAdLib | kFeaturesEGA, + 0, 0, 0 +}, +{ + { + "babayaga", + "", + { + {"stk1.stk", 0, "3c777f43e6fb49fde9222543447e135a", 204813}, + {"stk2.stk", 0, "6cf0b009dd185a8f589e91a1f9c33df5", 361582}, + {"stk3.stk", 0, "6473183ca4db1b5b5cea047f9af59a26", 328925}, + {0, 0, 0, 0} + }, + DE_DEU, + kPlatformPC, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeBabaYaga, + kFeaturesAdLib | kFeaturesEGA, + 0, 0, 0 +}, +{ + { + "babayaga", + "", + { + {"stk1.stk", 0, "3c777f43e6fb49fde9222543447e135a", 204813}, + {"stk2.stk", 0, "6cf0b009dd185a8f589e91a1f9c33df5", 361582}, + {"stk3.stk", 0, "6473183ca4db1b5b5cea047f9af59a26", 328925}, + {0, 0, 0, 0} + }, + EN_ANY, + kPlatformPC, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeBabaYaga, + kFeaturesAdLib | kFeaturesEGA, + 0, 0, 0 +}, +{ + { + "babayaga", + "", + { + {"stk1.stk", 0, "3c777f43e6fb49fde9222543447e135a", 204813}, + {"stk2.stk", 0, "6cf0b009dd185a8f589e91a1f9c33df5", 361582}, + {"stk3.stk", 0, "6473183ca4db1b5b5cea047f9af59a26", 328925}, + {0, 0, 0, 0} + }, + IT_ITA, + kPlatformPC, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeBabaYaga, + kFeaturesAdLib | kFeaturesEGA, + 0, 0, 0 +}, +{ + { + "babayaga", + "", + { + {"stk1.stk", 0, "3c777f43e6fb49fde9222543447e135a", 204813}, + {"stk2.stk", 0, "6cf0b009dd185a8f589e91a1f9c33df5", 361582}, + {"stk3.stk", 0, "6473183ca4db1b5b5cea047f9af59a26", 328925}, + {0, 0, 0, 0} + }, + ES_ESP, + kPlatformPC, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeBabaYaga, + kFeaturesAdLib | kFeaturesEGA, + 0, 0, 0 +}, + +// -- Once Upon A Time: Baba Yaga, Amiga -- + +{ + { + "babayaga", + "", + { + {"stk1.stk", 0, "bcc823d2888057031e54716ed1b3c80e", 205090}, + {"stk2.stk", 0, "f76bf7c2ff60d816d69962d1a593207c", 362122}, + {"stk3.stk", 0, "6227d1aefdf39d88dcf83e38bea2a9af", 328922}, + {0, 0, 0, 0} + }, + FR_FRA, + kPlatformAmiga, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeBabaYaga, + kFeaturesEGA, + 0, 0, 0 +}, +{ + { + "babayaga", + "", + { + {"stk1.stk", 0, "bcc823d2888057031e54716ed1b3c80e", 205090}, + {"stk2.stk", 0, "f76bf7c2ff60d816d69962d1a593207c", 362122}, + {"stk3.stk", 0, "6227d1aefdf39d88dcf83e38bea2a9af", 328922}, + {0, 0, 0, 0} + }, + DE_DEU, + kPlatformAmiga, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeBabaYaga, + kFeaturesEGA, + 0, 0, 0 +}, +{ + { + "babayaga", + "", + { + {"stk1.stk", 0, "bcc823d2888057031e54716ed1b3c80e", 205090}, + {"stk2.stk", 0, "f76bf7c2ff60d816d69962d1a593207c", 362122}, + {"stk3.stk", 0, "6227d1aefdf39d88dcf83e38bea2a9af", 328922}, + {0, 0, 0, 0} + }, + EN_ANY, + kPlatformAmiga, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeBabaYaga, + kFeaturesEGA, + 0, 0, 0 +}, +{ + { + "babayaga", + "", + { + {"stk1.stk", 0, "bcc823d2888057031e54716ed1b3c80e", 205090}, + {"stk2.stk", 0, "f76bf7c2ff60d816d69962d1a593207c", 362122}, + {"stk3.stk", 0, "6227d1aefdf39d88dcf83e38bea2a9af", 328922}, + {0, 0, 0, 0} + }, + IT_ITA, + kPlatformAmiga, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeBabaYaga, + kFeaturesEGA, + 0, 0, 0 +}, +{ + { + "babayaga", + "", + { + {"stk1.stk", 0, "bcc823d2888057031e54716ed1b3c80e", 205090}, + {"stk2.stk", 0, "f76bf7c2ff60d816d69962d1a593207c", 362122}, + {"stk3.stk", 0, "6227d1aefdf39d88dcf83e38bea2a9af", 328922}, + {0, 0, 0, 0} + }, + ES_ESP, + kPlatformAmiga, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeBabaYaga, + kFeaturesEGA, + 0, 0, 0 +}, + +// -- Once Upon A Time: Baba Yaga, Atari ST -- + +{ + { + "babayaga", + "", + { + {"stk1.stk", 0, "17a4e3e7a18cc97231c92d280c7878a1", 205095}, + {"stk2.stk", 0, "bfbc380e5461f63af28e9e6b10f334b5", 362128}, + {"stk3.stk", 0, "6227d1aefdf39d88dcf83e38bea2a9af", 328922}, + {0, 0, 0, 0} + }, + FR_FRA, + kPlatformAtariST, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeBabaYaga, + kFeaturesEGA, + 0, 0, 0 +}, +{ + { + "babayaga", + "", + { + {"stk1.stk", 0, "17a4e3e7a18cc97231c92d280c7878a1", 205095}, + {"stk2.stk", 0, "bfbc380e5461f63af28e9e6b10f334b5", 362128}, + {"stk3.stk", 0, "6227d1aefdf39d88dcf83e38bea2a9af", 328922}, + {0, 0, 0, 0} + }, + DE_DEU, + kPlatformAtariST, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeBabaYaga, + kFeaturesEGA, + 0, 0, 0 +}, +{ + { + "babayaga", + "", + { + {"stk1.stk", 0, "17a4e3e7a18cc97231c92d280c7878a1", 205095}, + {"stk2.stk", 0, "bfbc380e5461f63af28e9e6b10f334b5", 362128}, + {"stk3.stk", 0, "6227d1aefdf39d88dcf83e38bea2a9af", 328922}, + {0, 0, 0, 0} + }, + EN_ANY, + kPlatformAtariST, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeBabaYaga, + kFeaturesEGA, + 0, 0, 0 +}, +{ + { + "babayaga", + "", + { + {"stk1.stk", 0, "17a4e3e7a18cc97231c92d280c7878a1", 205095}, + {"stk2.stk", 0, "bfbc380e5461f63af28e9e6b10f334b5", 362128}, + {"stk3.stk", 0, "6227d1aefdf39d88dcf83e38bea2a9af", 328922}, + {0, 0, 0, 0} + }, + IT_ITA, + kPlatformAtariST, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeBabaYaga, + kFeaturesEGA, + 0, 0, 0 +}, +{ + { + "babayaga", + "", + { + {"stk1.stk", 0, "17a4e3e7a18cc97231c92d280c7878a1", 205095}, + {"stk2.stk", 0, "bfbc380e5461f63af28e9e6b10f334b5", 362128}, + {"stk3.stk", 0, "6227d1aefdf39d88dcf83e38bea2a9af", 328922}, + {0, 0, 0, 0} + }, + ES_ESP, + kPlatformAtariST, + ADGF_NO_FLAGS, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOSPEECH) + }, + kGameTypeBabaYaga, + kFeaturesEGA, + 0, 0, 0 +}, + +#endif // GOB_DETECTION_TABLES_ONCEUPON_H diff --git a/engines/gob/draw.cpp b/engines/gob/draw.cpp index fe59b11f76..8c6919416d 100644 --- a/engines/gob/draw.cpp +++ b/engines/gob/draw.cpp @@ -256,7 +256,7 @@ void Draw::blitInvalidated() { if (_cursorIndex == 4) blitCursor(); - if (_vm->_inter->_terminate) + if (_vm->_inter && _vm->_inter->_terminate) return; if (_noInvalidated && !_applyPal) @@ -271,7 +271,9 @@ void Draw::blitInvalidated() { return; } - _showCursor = (_showCursor & ~2) | ((_showCursor & 1) << 1); + if (_cursorSprites) + _showCursor = (_showCursor & ~2) | ((_showCursor & 1) << 1); + if (_applyPal) { clearPalette(); forceBlit(); @@ -425,28 +427,13 @@ int Draw::stringLength(const char *str, uint16 fontIndex) { return len; } -void Draw::drawString(const char *str, int16 x, int16 y, int16 color1, int16 color2, - int16 transp, Surface &dest, const Font &font) { - - while (*str != '\0') { - const int16 charRight = x + font.getCharWidth(*str); - const int16 charBottom = y + font.getCharHeight(); - - if ((charRight <= dest.getWidth()) && (charBottom <= dest.getHeight())) - font.drawLetter(dest, *str, x, y, color1, color2, transp); - - x += font.getCharWidth(*str); - str++; - } -} - void Draw::printTextCentered(int16 id, int16 left, int16 top, int16 right, int16 bottom, const char *str, int16 fontIndex, int16 color) { adjustCoords(1, &left, &top); adjustCoords(1, &right, &bottom); - uint16 centerOffset = _vm->_game->_script->getFunctionOffset(TOTFile::kFunctionCenter); + uint16 centerOffset = _vm->_game->_script ? _vm->_game->_script->getFunctionOffset(TOTFile::kFunctionCenter) : 0; if (centerOffset != 0) { _vm->_game->_script->call(centerOffset); @@ -505,7 +492,7 @@ void Draw::oPlaytoons_sub_F_1B(uint16 id, int16 left, int16 top, int16 right, in adjustCoords(1, &left, &top); adjustCoords(1, &right, &bottom); - uint16 centerOffset = _vm->_game->_script->getFunctionOffset(TOTFile::kFunctionCenter); + uint16 centerOffset = _vm->_game->_script ? _vm->_game->_script->getFunctionOffset(TOTFile::kFunctionCenter) : 0; if (centerOffset != 0) { _vm->_game->_script->call(centerOffset); diff --git a/engines/gob/draw.h b/engines/gob/draw.h index e7af1f9af3..b51c6466e0 100644 --- a/engines/gob/draw.h +++ b/engines/gob/draw.h @@ -194,8 +194,6 @@ public: adjustCoords(adjust, (int16 *)coord1, (int16 *)coord2); } int stringLength(const char *str, uint16 fontIndex); - void drawString(const char *str, int16 x, int16 y, int16 color1, int16 color2, - int16 transp, Surface &dest, const Font &font); void printTextCentered(int16 id, int16 left, int16 top, int16 right, int16 bottom, const char *str, int16 fontIndex, int16 color); void oPlaytoons_sub_F_1B( uint16 id, int16 left, int16 top, int16 right, int16 bottom, char *paramStr, int16 var3, int16 var4, int16 shortId); diff --git a/engines/gob/draw_fascin.cpp b/engines/gob/draw_fascin.cpp index 54cd52b660..12009d7ee5 100644 --- a/engines/gob/draw_fascin.cpp +++ b/engines/gob/draw_fascin.cpp @@ -222,8 +222,8 @@ void Draw_Fascination::spriteOperation(int16 operation) { _destSpriteX, _destSpriteY, _frontColor, _backColor, _transparency); } } else { - drawString(_textToPrint, _destSpriteX, _destSpriteY, _frontColor, - _backColor, _transparency, *_spritesArray[_destSurface], *font); + font->drawString(_textToPrint, _destSpriteX, _destSpriteY, _frontColor, + _backColor, _transparency, *_spritesArray[_destSurface]); _destSpriteX += len * font->getCharWidth(); } } else { diff --git a/engines/gob/draw_playtoons.cpp b/engines/gob/draw_playtoons.cpp index a443f81ccf..76e2ae591c 100644 --- a/engines/gob/draw_playtoons.cpp +++ b/engines/gob/draw_playtoons.cpp @@ -283,8 +283,8 @@ void Draw_Playtoons::spriteOperation(int16 operation) { _destSpriteX, _destSpriteY, _frontColor, _backColor, _transparency); } } else { - drawString(_textToPrint, _destSpriteX, _destSpriteY, _frontColor, - _backColor, _transparency, *_spritesArray[_destSurface], *font); + font->drawString(_textToPrint, _destSpriteX, _destSpriteY, _frontColor, + _backColor, _transparency, *_spritesArray[_destSurface]); _destSpriteX += len * font->getCharWidth(); } } else { diff --git a/engines/gob/draw_v2.cpp b/engines/gob/draw_v2.cpp index b637ecbd2b..f5475278c4 100644 --- a/engines/gob/draw_v2.cpp +++ b/engines/gob/draw_v2.cpp @@ -74,13 +74,16 @@ void Draw_v2::closeScreen() { } void Draw_v2::blitCursor() { - if (_cursorIndex == -1) + if (!_cursorSprites || (_cursorIndex == -1)) return; _showCursor = (_showCursor & ~2) | ((_showCursor & 1) << 1); } void Draw_v2::animateCursor(int16 cursor) { + if (!_cursorSprites) + return; + int16 cursorIndex = cursor; int16 newX = 0, newY = 0; uint16 hotspotX, hotspotY; @@ -831,8 +834,8 @@ void Draw_v2::spriteOperation(int16 operation) { getColor(_backColor), _transparency); } } else { - drawString(_textToPrint, _destSpriteX, _destSpriteY, getColor(_frontColor), - getColor(_backColor), _transparency, *_spritesArray[_destSurface], *font); + font->drawString(_textToPrint, _destSpriteX, _destSpriteY, getColor(_frontColor), + getColor(_backColor), _transparency, *_spritesArray[_destSurface]); _destSpriteX += len * font->getCharWidth(); } } else { diff --git a/engines/gob/game.cpp b/engines/gob/game.cpp index 0d1953322f..de0c3f2d5c 100644 --- a/engines/gob/game.cpp +++ b/engines/gob/game.cpp @@ -64,7 +64,7 @@ void Environments::clear() { // Deleting unique variables, script and resources for (uint i = 0; i < kEnvironmentCount; i++) { - if (_environments[i].variables == _vm->_inter->_variables) + if (_vm->_inter && (_environments[i].variables == _vm->_inter->_variables)) continue; if (!has(_environments[i].variables, i + 1)) diff --git a/engines/gob/gob.cpp b/engines/gob/gob.cpp index 3d8a18ed38..fcf98f0355 100644 --- a/engines/gob/gob.cpp +++ b/engines/gob/gob.cpp @@ -48,6 +48,10 @@ #include "gob/videoplayer.h" #include "gob/save/saveload.h" +#include "gob/pregob/pregob.h" +#include "gob/pregob/onceupon/abracadabra.h" +#include "gob/pregob/onceupon/babayaga.h" + namespace Gob { #define MAX_TIME_DELTA 100 @@ -115,7 +119,7 @@ GobEngine::GobEngine(OSystem *syst) : Engine(syst), _rnd("gob") { _vidPlayer = 0; _init = 0; _inter = 0; _map = 0; _palAnim = 0; _scenery = 0; _draw = 0; _util = 0; _video = 0; - _saveLoad = 0; + _saveLoad = 0; _preGob = 0; _pauseStart = 0; @@ -180,6 +184,10 @@ void GobEngine::validateVideoMode(int16 videoMode) { error("Video mode 0x%X is not supported", videoMode); } +EndiannessMethod GobEngine::getEndiannessMethod() const { + return _endiannessMethod; +} + Endianness GobEngine::getEndianness() const { if ((getPlatform() == Common::kPlatformAmiga) || (getPlatform() == Common::kPlatformMacintosh) || @@ -274,15 +282,15 @@ void GobEngine::setTrueColor(bool trueColor) { } Common::Error GobEngine::run() { - if (!initGameParts()) { - GUIErrorMessage("GobEngine::init(): Unknown version of game engine"); - return Common::kUnknownError; - } + Common::Error err; - if (!initGraphics()) { - GUIErrorMessage("GobEngine::init(): Failed to set up graphics"); - return Common::kUnknownError; - } + err = initGameParts(); + if (err.getCode() != Common::kNoError) + return err; + + err = initGraphics(); + if (err.getCode() != Common::kNoError) + return err; // On some systems it's not safe to run CD audio games from the CD. if (isCD()) @@ -368,11 +376,12 @@ void GobEngine::pauseEngineIntern(bool pause) { _game->_startTimeKey += duration; _draw->_cursorTimeKey += duration; - if (_inter->_soundEndTimeKey != 0) + if (_inter && (_inter->_soundEndTimeKey != 0)) _inter->_soundEndTimeKey += duration; } - _vidPlayer->pauseAll(pause); + if (_vidPlayer) + _vidPlayer->pauseAll(pause); _mixer->pauseAll(pause); } @@ -392,12 +401,13 @@ void GobEngine::pauseGame() { pauseEngineIntern(false); } -bool GobEngine::initGameParts() { +Common::Error GobEngine::initGameParts() { _resourceSizeWorkaround = false; // just detect some devices some of which will be always there if the music is not disabled _noMusic = MidiDriver::getMusicType(MidiDriver::detectDevice(MDT_PCSPK | MDT_MIDI | MDT_ADLIB)) == MT_NULL ? true : false; - _saveLoad = 0; + + _endiannessMethod = kEndiannessMethodSystem; _global = new Global(this); _util = new Util(this); @@ -429,6 +439,8 @@ bool GobEngine::initGameParts() { _goblin = new Goblin_v1(this); _scenery = new Scenery_v1(this); _saveLoad = new SaveLoad_Geisha(this, _targetName.c_str()); + + _endiannessMethod = kEndiannessMethodAltFile; break; case kGameTypeFascination: @@ -605,20 +617,45 @@ bool GobEngine::initGameParts() { _scenery = new Scenery_v2(this); _saveLoad = new SaveLoad_v2(this, _targetName.c_str()); break; + + case kGameTypeAbracadabra: + _init = new Init_v2(this); + _video = new Video_v2(this); + _mult = new Mult_v2(this); + _draw = new Draw_v2(this); + _map = new Map_v2(this); + _goblin = new Goblin_v2(this); + _scenery = new Scenery_v2(this); + _preGob = new OnceUpon::Abracadabra(this); + break; + + case kGameTypeBabaYaga: + _init = new Init_v2(this); + _video = new Video_v2(this); + _mult = new Mult_v2(this); + _draw = new Draw_v2(this); + _map = new Map_v2(this); + _goblin = new Goblin_v2(this); + _scenery = new Scenery_v2(this); + _preGob = new OnceUpon::BabaYaga(this); + break; + default: deinitGameParts(); - return false; + return Common::kUnsupportedGameidError; } // Setup mixer syncSoundSettings(); - _inter->setupOpcodes(); + if (_inter) + _inter->setupOpcodes(); - return true; + return Common::kNoError; } void GobEngine::deinitGameParts() { + delete _preGob; _preGob = 0; delete _saveLoad; _saveLoad = 0; delete _mult; _mult = 0; delete _vidPlayer; _vidPlayer = 0; @@ -637,10 +674,10 @@ void GobEngine::deinitGameParts() { delete _dataIO; _dataIO = 0; } -bool GobEngine::initGraphics() { +Common::Error GobEngine::initGraphics() { if (is800x600()) { warning("GobEngine::initGraphics(): 800x600 games currently unsupported"); - return false; + return Common::kUnsupportedGameidError; } else if (is640x480()) { _width = 640; _height = 480; @@ -664,7 +701,7 @@ bool GobEngine::initGraphics() { _global->_primarySurfDesc = SurfacePtr(new Surface(_width, _height, _pixelFormat.bytesPerPixel)); - return true; + return Common::kNoError; } } // End of namespace Gob diff --git a/engines/gob/gob.h b/engines/gob/gob.h index 52f3ba8f2d..df73404596 100644 --- a/engines/gob/gob.h +++ b/engines/gob/gob.h @@ -75,6 +75,7 @@ class Scenery; class Util; class SaveLoad; class GobConsole; +class PreGob; #define WRITE_VAR_UINT32(var, val) _vm->_inter->_variables->writeVar32(var, val) #define WRITE_VAR_UINT16(var, val) _vm->_inter->_variables->writeVar16(var, val) @@ -129,7 +130,10 @@ enum GameType { kGameTypeAdi4, kGameTypeAdibou2, kGameTypeAdibou1, + kGameTypeAbracadabra, + kGameTypeBabaYaga, kGameTypeLittleRed, + kGameTypeOnceUponATime, // Need more inspection to see if Baba Yaga or Abracadabra kGameTypeAJWorld }; @@ -145,6 +149,13 @@ enum Features { kFeaturesTrueColor = 1 << 7 }; +enum EndiannessMethod { + kEndiannessMethodLE, ///< Always little endian. + kEndiannessMethodBE, ///< Always big endian. + kEndiannessMethodSystem, ///< Follows system endianness. + kEndiannessMethodAltFile ///< Different endianness in alternate file. +}; + enum { kDebugFuncOp = 1 << 0, kDebugDrawOp = 1 << 1, @@ -168,6 +179,8 @@ private: int32 _features; Common::Platform _platform; + EndiannessMethod _endiannessMethod; + uint32 _pauseStart; // Engine APIs @@ -176,10 +189,10 @@ private: virtual void pauseEngineIntern(bool pause); virtual void syncSoundSettings(); - bool initGameParts(); - void deinitGameParts(); + Common::Error initGameParts(); + Common::Error initGraphics(); - bool initGraphics(); + void deinitGameParts(); public: static const Common::Language _gobToScummVMLang[]; @@ -220,6 +233,7 @@ public: Inter *_inter; SaveLoad *_saveLoad; VideoPlayer *_vidPlayer; + PreGob *_preGob; const char *getLangDesc(int16 language) const; void validateLanguage(); @@ -227,6 +241,7 @@ public: void pauseGame(); + EndiannessMethod getEndiannessMethod() const; Endianness getEndianness() const; Common::Platform getPlatform() const; GameType getGameType() const; diff --git a/engines/gob/init.cpp b/engines/gob/init.cpp index a61261f355..814d4d1821 100644 --- a/engines/gob/init.cpp +++ b/engines/gob/init.cpp @@ -34,9 +34,13 @@ #include "gob/inter.h" #include "gob/video.h" #include "gob/videoplayer.h" + +#include "gob/sound/sound.h" + #include "gob/demos/scnplayer.h" #include "gob/demos/batplayer.h" -#include "gob/sound/sound.h" + +#include "gob/pregob/pregob.h" namespace Gob { @@ -118,6 +122,14 @@ void Init::initGame() { return; } + if (_vm->_preGob) { + _vm->_preGob->run(); + delete _palDesc; + _vm->_video->initPrimary(-1); + cleanup(); + return; + } + Common::SeekableReadStream *infFile = _vm->_dataIO->getFile("intro.inf"); if (!infFile) { diff --git a/engines/gob/inter.h b/engines/gob/inter.h index 63bf3eb1c6..2aa837e777 100644 --- a/engines/gob/inter.h +++ b/engines/gob/inter.h @@ -693,7 +693,7 @@ protected: void o7_zeroVar(); void o7_getINIValue(); void o7_setINIValue(); - void o7_loadLBMPalette(); + void o7_loadIFFPalette(); void o7_opendBase(); void o7_closedBase(); void o7_getDBString(); diff --git a/engines/gob/inter_bargon.cpp b/engines/gob/inter_bargon.cpp index 134203fa9d..029f7c697b 100644 --- a/engines/gob/inter_bargon.cpp +++ b/engines/gob/inter_bargon.cpp @@ -119,7 +119,7 @@ void Inter_Bargon::oBargon_intro2(OpGobParams ¶ms) { MouseButtons buttons; SurfacePtr surface; SoundDesc samples[4]; - int16 comp[5] = { 0, 1, 2, 3, -1 }; + static const int16 comp[5] = { 0, 1, 2, 3, -1 }; static const char *const sndFiles[] = {"1INTROII.snd", "2INTROII.snd", "1INTRO3.snd", "2INTRO3.snd"}; surface = _vm->_video->initSurfDesc(320, 200); @@ -167,8 +167,8 @@ void Inter_Bargon::oBargon_intro3(OpGobParams ¶ms) { MouseButtons buttons; Video::Color *palBak; SoundDesc samples[2]; - int16 comp[3] = { 0, 1, -1 }; byte *palettes[4]; + static const int16 comp[3] = { 0, 1, -1 }; static const char *const sndFiles[] = {"1INTROIV.snd", "2INTROIV.snd"}; static const char *const palFiles[] = {"2ou2.clt", "2ou3.clt", "2ou4.clt", "2ou5.clt"}; diff --git a/engines/gob/inter_v5.cpp b/engines/gob/inter_v5.cpp index c0e8978afd..24905b08d1 100644 --- a/engines/gob/inter_v5.cpp +++ b/engines/gob/inter_v5.cpp @@ -281,7 +281,7 @@ void Inter_v5::o5_getSystemCDSpeed(OpGobParams ¶ms) { Font *font; if ((font = _vm->_draw->loadFont("SPEED.LET"))) { - _vm->_draw->drawString("100 %", 402, 89, 112, 144, 0, *_vm->_draw->_backSurface, *font); + font->drawString("100 %", 402, 89, 112, 144, 0, *_vm->_draw->_backSurface); _vm->_draw->forceBlit(); delete font; @@ -293,7 +293,7 @@ void Inter_v5::o5_getSystemRAM(OpGobParams ¶ms) { Font *font; if ((font = _vm->_draw->loadFont("SPEED.LET"))) { - _vm->_draw->drawString("100 %", 402, 168, 112, 144, 0, *_vm->_draw->_backSurface, *font); + font->drawString("100 %", 402, 168, 112, 144, 0, *_vm->_draw->_backSurface); _vm->_draw->forceBlit(); delete font; @@ -305,7 +305,7 @@ void Inter_v5::o5_getSystemCPUSpeed(OpGobParams ¶ms) { Font *font; if ((font = _vm->_draw->loadFont("SPEED.LET"))) { - _vm->_draw->drawString("100 %", 402, 248, 112, 144, 0, *_vm->_draw->_backSurface, *font); + font->drawString("100 %", 402, 248, 112, 144, 0, *_vm->_draw->_backSurface); _vm->_draw->forceBlit(); delete font; @@ -317,7 +317,7 @@ void Inter_v5::o5_getSystemDrawSpeed(OpGobParams ¶ms) { Font *font; if ((font = _vm->_draw->loadFont("SPEED.LET"))) { - _vm->_draw->drawString("100 %", 402, 326, 112, 144, 0, *_vm->_draw->_backSurface, *font); + font->drawString("100 %", 402, 326, 112, 144, 0, *_vm->_draw->_backSurface); _vm->_draw->forceBlit(); delete font; @@ -329,7 +329,7 @@ void Inter_v5::o5_totalSystemSpecs(OpGobParams ¶ms) { Font *font; if ((font = _vm->_draw->loadFont("SPEED.LET"))) { - _vm->_draw->drawString("100 %", 402, 405, 112, 144, 0, *_vm->_draw->_backSurface, *font); + font->drawString("100 %", 402, 405, 112, 144, 0, *_vm->_draw->_backSurface); _vm->_draw->forceBlit(); delete font; diff --git a/engines/gob/inter_v7.cpp b/engines/gob/inter_v7.cpp index 6cf69ed9df..1238c23e3b 100644 --- a/engines/gob/inter_v7.cpp +++ b/engines/gob/inter_v7.cpp @@ -27,6 +27,7 @@ #include "graphics/cursorman.h" #include "graphics/wincursor.h" +#include "graphics/decoders/iff.h" #include "gob/gob.h" #include "gob/global.h" @@ -72,7 +73,7 @@ void Inter_v7::setupOpcodesDraw() { OPCODEDRAW(0x95, o7_zeroVar); OPCODEDRAW(0xA1, o7_getINIValue); OPCODEDRAW(0xA2, o7_setINIValue); - OPCODEDRAW(0xA4, o7_loadLBMPalette); + OPCODEDRAW(0xA4, o7_loadIFFPalette); OPCODEDRAW(0xC4, o7_opendBase); OPCODEDRAW(0xC5, o7_closedBase); OPCODEDRAW(0xC6, o7_getDBString); @@ -523,7 +524,7 @@ void Inter_v7::o7_setINIValue() { _inis.setValue(file, section, key, value); } -void Inter_v7::o7_loadLBMPalette() { +void Inter_v7::o7_loadIFFPalette() { Common::String file = _vm->_game->_script->evalString(); if (!file.contains('.')) file += ".LBM"; @@ -534,37 +535,46 @@ void Inter_v7::o7_loadLBMPalette() { if (startIndex > stopIndex) SWAP(startIndex, stopIndex); - Common::SeekableReadStream *lbmFile = _vm->_dataIO->getFile(file); - if (!lbmFile) { - warning("o7_loadLBMPalette(): No such file \"%s\"", file.c_str()); + Common::SeekableReadStream *iffFile = _vm->_dataIO->getFile(file); + if (!iffFile) { + warning("o7_loadIFFPalette(): No such file \"%s\"", file.c_str()); return; } - ImageType type = Surface::identifyImage(*lbmFile); - if (type != kImageTypeLBM) { - warning("o7_loadLBMPalette(): \"%s\" is no LBM", file.c_str()); + ImageType type = Surface::identifyImage(*iffFile); + if (type != kImageTypeIFF) { + warning("o7_loadIFFPalette(): \"%s\" is no IFF", file.c_str()); return; } - byte palette[768]; - - LBMLoader lbm(*lbmFile); - if (!lbm.loadPalette(palette)) { - warning("o7_loadLBMPalette(): Failed reading palette from LBM \"%s\"", file.c_str()); + Graphics::IFFDecoder decoder; + decoder.loadStream(*iffFile); + if (!decoder.getPalette() || decoder.getPaletteColorCount() != 256) { + warning("o7_loadIFFPalette(): Failed reading palette from IFF \"%s\"", file.c_str()); return; } - memset(palette , 0x00, 3); - memset(palette + 765, 0xFF, 3); - for (int i = 0; i < 768; i++) - palette[i] >>= 2; - - int16 count = stopIndex - startIndex + 1; + const byte *palette = decoder.getPalette(); startIndex *= 3; - count *= 3; + stopIndex *= 3; + + byte *dst = (byte *)_vm->_draw->_vgaPalette + startIndex; + const byte *src = palette + startIndex; + for (int i = startIndex; i <= stopIndex + 2; ++i) { + *dst++ = *src++ >> 2; + } + + if (startIndex == 0) { + dst = (byte *)_vm->_draw->_vgaPalette; + dst[0] = dst[1] = dst[2] = 0x00 >> 2; + } + + if (stopIndex == 765) { + dst = (byte *)_vm->_draw->_vgaPalette + 765; + dst[0] = dst[1] = dst[2] = 0xFF >> 2; + } - memcpy((char *)_vm->_draw->_vgaPalette + startIndex, palette + startIndex, count); _vm->_video->setFullPalette(_vm->_global->_pPaletteDesc); } diff --git a/engines/gob/minigames/geisha/penetration.cpp b/engines/gob/minigames/geisha/penetration.cpp index 3be9f1f651..c8c4f2bba7 100644 --- a/engines/gob/minigames/geisha/penetration.cpp +++ b/engines/gob/minigames/geisha/penetration.cpp @@ -505,7 +505,7 @@ bool Penetration::play(bool hasAccessPass, bool hasMaxEnergy, bool testMode) { // Draw, fade in if necessary and wait for the end of the frame _vm->_draw->blitInvalidated(); fadeIn(); - _vm->_util->waitEndFrame(); + _vm->_util->waitEndFrame(false); // Handle the input checkInput(); @@ -778,29 +778,24 @@ void Penetration::drawFloorText() { else if (_floor == 2) floorString = strings[kString1stBasement]; + Surface &surface = *_vm->_draw->_backSurface; + if (floorString) - _vm->_draw->drawString(floorString, 10, 15, kColorFloorText, kColorBlack, 1, - *_vm->_draw->_backSurface, *font); + font->drawString(floorString, 10, 15, kColorFloorText, kColorBlack, 1, surface); if (_exits.size() > 0) { int exitCount = kString2Exits; if (_exits.size() == 1) exitCount = kString1Exit; - _vm->_draw->drawString(strings[kStringYouHave] , 10, 38, kColorExitText, kColorBlack, 1, - *_vm->_draw->_backSurface, *font); - _vm->_draw->drawString(strings[exitCount] , 10, 53, kColorExitText, kColorBlack, 1, - *_vm->_draw->_backSurface, *font); - _vm->_draw->drawString(strings[kStringToReach] , 10, 68, kColorExitText, kColorBlack, 1, - *_vm->_draw->_backSurface, *font); - _vm->_draw->drawString(strings[kStringUpperLevel1], 10, 84, kColorExitText, kColorBlack, 1, - *_vm->_draw->_backSurface, *font); - _vm->_draw->drawString(strings[kStringUpperLevel2], 10, 98, kColorExitText, kColorBlack, 1, - *_vm->_draw->_backSurface, *font); + font->drawString(strings[kStringYouHave] , 10, 38, kColorExitText, kColorBlack, 1, surface); + font->drawString(strings[exitCount] , 10, 53, kColorExitText, kColorBlack, 1, surface); + font->drawString(strings[kStringToReach] , 10, 68, kColorExitText, kColorBlack, 1, surface); + font->drawString(strings[kStringUpperLevel1], 10, 84, kColorExitText, kColorBlack, 1, surface); + font->drawString(strings[kStringUpperLevel2], 10, 98, kColorExitText, kColorBlack, 1, surface); } else - _vm->_draw->drawString(strings[kStringNoExit], 10, 53, kColorExitText, kColorBlack, 1, - *_vm->_draw->_backSurface, *font); + font->drawString(strings[kStringNoExit], 10, 53, kColorExitText, kColorBlack, 1, surface); } void Penetration::drawEndText() { @@ -814,21 +809,17 @@ void Penetration::drawEndText() { if (!font) return; + Surface &surface = *_vm->_draw->_backSurface; + const char **strings = kStrings[getLanguage()]; - _vm->_draw->drawString(strings[kStringLevel0] , 11, 21, kColorExitText, kColorBlack, 1, - *_vm->_draw->_backSurface, *font); - _vm->_draw->drawString(strings[kStringPenetration], 11, 42, kColorExitText, kColorBlack, 1, - *_vm->_draw->_backSurface, *font); - _vm->_draw->drawString(strings[kStringSuccessful] , 11, 58, kColorExitText, kColorBlack, 1, - *_vm->_draw->_backSurface, *font); - - _vm->_draw->drawString(strings[kStringDanger] , 11, 82, kColorFloorText, kColorBlack, 1, - *_vm->_draw->_backSurface, *font); - _vm->_draw->drawString(strings[kStringGynoides] , 11, 98, kColorFloorText, kColorBlack, 1, - *_vm->_draw->_backSurface, *font); - _vm->_draw->drawString(strings[kStringActivated], 11, 113, kColorFloorText, kColorBlack, 1, - *_vm->_draw->_backSurface, *font); + font->drawString(strings[kStringLevel0] , 11, 21, kColorExitText, kColorBlack, 1, surface); + font->drawString(strings[kStringPenetration], 11, 42, kColorExitText, kColorBlack, 1, surface); + font->drawString(strings[kStringSuccessful] , 11, 58, kColorExitText, kColorBlack, 1, surface); + + font->drawString(strings[kStringDanger] , 11, 82, kColorFloorText, kColorBlack, 1, surface); + font->drawString(strings[kStringGynoides] , 11, 98, kColorFloorText, kColorBlack, 1, surface); + font->drawString(strings[kStringActivated], 11, 113, kColorFloorText, kColorBlack, 1, surface); _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, kTextAreaLeft, kTextAreaTop, kTextAreaRight, kTextAreaBigBottom); _vm->_draw->blitInvalidated(); diff --git a/engines/gob/module.mk b/engines/gob/module.mk index 3395046b6c..d5ee6478be 100644 --- a/engines/gob/module.mk +++ b/engines/gob/module.mk @@ -3,6 +3,7 @@ MODULE := engines/gob MODULE_OBJS := \ anifile.o \ aniobject.o \ + backbuffer.o \ cheater.o \ cheater_geisha.o \ cmpfile.o \ @@ -77,6 +78,17 @@ MODULE_OBJS := \ demos/scnplayer.o \ demos/batplayer.o \ detection/detection.o \ + pregob/pregob.o \ + pregob/txtfile.o \ + pregob/gctfile.o \ + pregob/seqfile.o \ + pregob/onceupon/onceupon.o \ + pregob/onceupon/abracadabra.o \ + pregob/onceupon/babayaga.o \ + pregob/onceupon/title.o \ + pregob/onceupon/parents.o \ + pregob/onceupon/stork.o \ + pregob/onceupon/chargenchild.o \ minigames/geisha/evilfish.o \ minigames/geisha/oko.o \ minigames/geisha/meter.o \ diff --git a/engines/gob/pregob/gctfile.cpp b/engines/gob/pregob/gctfile.cpp new file mode 100644 index 0000000000..08c32cda76 --- /dev/null +++ b/engines/gob/pregob/gctfile.cpp @@ -0,0 +1,306 @@ +/* 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 "common/random.h" +#include "common/stream.h" + +#include "gob/surface.h" +#include "gob/video.h" + +#include "gob/pregob/gctfile.h" + +namespace Gob { + +GCTFile::Chunk::Chunk() : type(kChunkTypeNone) { +} + + +GCTFile::GCTFile(Common::SeekableReadStream &gct, Common::RandomSource &rnd) : _rnd(&rnd), + _areaLeft(0), _areaTop(0), _areaRight(0), _areaBottom(0), _currentItem(0xFFFF) { + + load(gct); +} + +GCTFile::~GCTFile() { +} + +void GCTFile::load(Common::SeekableReadStream &gct) { + gct.skip(4); // Required buffer size + gct.skip(2); // Unknown + + // Read the selector and line counts for each item + const uint16 itemCount = gct.readUint16LE(); + _items.resize(itemCount); + + for (Items::iterator i = _items.begin(); i != _items.end(); ++i) { + const uint16 selector = gct.readUint16LE(); + const uint16 lineCount = gct.readUint16LE(); + + i->selector = selector; + i->lines.resize(lineCount); + } + + // Read all item lines + for (Items::iterator i = _items.begin(); i != _items.end(); ++i) { + for (Lines::iterator l = i->lines.begin(); l != i->lines.end(); ++l) { + const uint16 lineSize = gct.readUint16LE(); + + readLine(gct, *l, lineSize); + } + } + + if (gct.err()) + error("GCTFile::load(): Failed reading GCT"); +} + +void GCTFile::readLine(Common::SeekableReadStream &gct, Line &line, uint16 lineSize) const { + line.chunks.push_back(Chunk()); + + while (lineSize > 0) { + byte c = gct.readByte(); + lineSize--; + + if (c == 0) { + // Command byte + + if (lineSize == 0) + break; + + byte cmd = gct.readByte(); + lineSize--; + + // Line end command + if (cmd == 0) + break; + + // Item reference command + if (cmd == 1) { + if (lineSize < 2) { + warning("GCTFile::readLine(): Item reference command is missing parameters"); + break; + } + + const uint32 itemRef = gct.readUint16LE(); + lineSize -= 2; + + line.chunks.push_back(Chunk()); + line.chunks.back().type = kChunkTypeItem; + line.chunks.back().item = itemRef; + + line.chunks.push_back(Chunk()); + continue; + } + + warning("GCTFile::readLine(): Invalid command 0x%02X", cmd); + break; + } + + // Text + line.chunks.back().type = kChunkTypeString; + line.chunks.back().text += (char)c; + } + + // Skip bytes we didn't read (because of errors) + gct.skip(lineSize); + + // Remove empty chunks from the end of the list + while (!line.chunks.empty() && (line.chunks.back().type == kChunkTypeNone)) + line.chunks.pop_back(); +} + +uint16 GCTFile::getLineCount(uint item) const { + if (item >= _items.size()) + return 0; + + return _items[item].lines.size(); +} + +void GCTFile::selectLine(uint item, uint16 line) { + if ((item >= _items.size()) && (item != kSelectorAll) && (item != kSelectorRandom)) + return; + + _items[item].selector = line; +} + +void GCTFile::setText(uint item, uint16 line, const Common::String &text) { + if ((item >= _items.size()) || (line >= _items[item].lines.size())) + return; + + _items[item].lines[line].chunks.clear(); + _items[item].lines[line].chunks.push_back(Chunk()); + + _items[item].lines[line].chunks.back().type = kChunkTypeString; + _items[item].lines[line].chunks.back().text = text; +} + +void GCTFile::setText(uint item, const Common::String &text) { + if (item >= _items.size()) + return; + + _items[item].selector = 0; + + _items[item].lines.resize(1); + + setText(item, 0, text); +} + +void GCTFile::reset() { + _currentItem = 0xFFFF; + _currentText.clear(); +} + +Common::String GCTFile::getLineText(const Line &line) const { + Common::String text; + + // Go over all chunks in this line + for (Chunks::const_iterator c = line.chunks.begin(); c != line.chunks.end(); ++c) { + // A chunk is either a direct string, or a reference to another item + + if (c->type == kChunkTypeItem) { + Common::List<Common::String> lines; + + getItemText(c->item, lines); + if (lines.empty()) + continue; + + if (lines.size() > 1) + warning("GCTFile::getLineText(): Referenced item has multiple lines"); + + text += lines.front(); + } else if (c->type == kChunkTypeString) + text += c->text; + } + + return text; +} + +void GCTFile::getItemText(uint item, Common::List<Common::String> &text) const { + text.clear(); + + if ((item >= _items.size()) || _items[item].lines.empty()) + return; + + uint16 line = _items[item].selector; + + // Draw all lines + if (line == kSelectorAll) { + for (Lines::const_iterator l = _items[item].lines.begin(); l != _items[item].lines.end(); ++l) + text.push_back(getLineText(*l)); + + return; + } + + // Draw random line + if (line == kSelectorRandom) + line = _rnd->getRandomNumber(_items[item].lines.size() - 1); + + if (line >= _items[item].lines.size()) + return; + + text.push_back(getLineText(_items[item].lines[line])); +} + +void GCTFile::setArea(int16 left, int16 top, int16 right, int16 bottom) { + trashBuffer(); + + _hasArea = false; + + const int16 width = right - left + 1; + const int16 height = bottom - top + 1; + if ((width <= 0) || (height <= 0)) + return; + + _areaLeft = left; + _areaTop = top; + _areaRight = right; + _areaBottom = bottom; + + _hasArea = true; + + resizeBuffer(width, height); +} + +bool GCTFile::clear(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom) { + return restoreScreen(dest, left, top, right, bottom); +} + +bool GCTFile::fill(Surface &dest, uint8 color, int16 &left, int16 &top, int16 &right, int16 &bottom) { + left = _areaLeft; + top = _areaTop; + right = _areaRight; + bottom = _areaBottom; + + if (!hasSavedBackground()) + saveScreen(dest, left, top, right, bottom); + + dest.fillRect(left, top, right, bottom, color); + + return true; +} + +bool GCTFile::finished() const { + return (_currentItem != 0xFFFF) && _currentText.empty(); +} + +bool GCTFile::draw(Surface &dest, uint16 item, const Font &font, uint8 color, + int16 &left, int16 &top, int16 &right, int16 &bottom) { + + if ((item >= _items.size()) || !_hasArea) + return false; + + left = _areaLeft; + top = _areaTop; + right = _areaRight; + bottom = _areaBottom; + + const int16 width = right - left + 1; + const int16 height = bottom - top + 1; + + const uint lineCount = height / font.getCharHeight(); + if (lineCount == 0) + return false; + + if (!hasSavedBackground()) + saveScreen(dest, left, top, right, bottom); + + if (item != _currentItem) { + _currentItem = item; + + getItemText(_currentItem, _currentText); + } + + if (_currentText.empty()) + return false; + + int16 y = top; + for (uint i = 0; (i < lineCount) && !_currentText.empty(); i++, y += font.getCharHeight()) { + const Common::String &line = _currentText.front(); + const int16 x = left + ((width - (line.size() * font.getCharWidth())) / 2); + + font.drawString(line, x, y, color, 0, true, dest); + _currentText.pop_front(); + } + + return true; +} + +} // End of namespace Gob diff --git a/engines/gob/pregob/gctfile.h b/engines/gob/pregob/gctfile.h new file mode 100644 index 0000000000..ed6351b7a8 --- /dev/null +++ b/engines/gob/pregob/gctfile.h @@ -0,0 +1,149 @@ +/* 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 GOB_PREGOB_GCTFILE_H +#define GOB_PREGOB_GCTFILE_H + +#include "common/str.h" +#include "common/array.h" +#include "common/list.h" + +#include "gob/backbuffer.h" + +namespace Common { + class RandomSource; + class SeekableReadStream; +} + +namespace Gob { + +class Surface; +class Font; + +class GCTFile : public BackBuffer { +public: + static const uint16 kSelectorAll = 0xFFFE; ///< Print all lines. + static const uint16 kSelectorRandom = 0xFFFF; ///< Print a random line. + + + GCTFile(Common::SeekableReadStream &gct, Common::RandomSource &rnd); + ~GCTFile(); + + /** Return the number of lines in an item. */ + uint16 getLineCount(uint item) const; + + /** Set the area the text will be printed in. */ + void setArea(int16 left, int16 top, int16 right, int16 bottom); + + /** Set which line of this item should be printed. */ + void selectLine(uint item, uint16 line); + + /** Change the text of an items' line. */ + void setText(uint item, uint16 line, const Common::String &text); + /** Change the item into one one line and set that line's text. */ + void setText(uint item, const Common::String &text); + + /** Reset the item drawing state. */ + void reset(); + + /** Clear the drawn text, restoring the original content. */ + bool clear(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom); + + /** Fill the text area with a color. */ + bool fill(Surface &dest, uint8 color, int16 &left, int16 &top, int16 &right, int16 &bottom); + + /** Draw an item onto the surface, until all text has been drawn or the area is filled. */ + bool draw(Surface &dest, uint16 item, const Font &font, uint8 color, + int16 &left, int16 &top, int16 &right, int16 &bottom); + + /** Did we draw all text? */ + bool finished() const; + +private: + /** The type of a chunk. */ + enum ChunkType { + kChunkTypeNone = 0, ///< Do nothing. + kChunkTypeString , ///< A direct string. + kChunkTypeItem ///< A reference to an item to print instead. + }; + + /** A chunk in an item text line. */ + struct Chunk { + ChunkType type; ///< The type of the chunk. + + Common::String text; ///< Text to print. + + int item; ///< Item to print instead. + + Chunk(); + }; + + typedef Common::List<Chunk> Chunks; + + /** A line in an item. */ + struct Line { + Chunks chunks; ///< The chunks that make up the line. + }; + + typedef Common::Array<Line> Lines; + + /** A GCT item. */ + struct Item { + Lines lines; ///< The text lines in the item + uint16 selector; ///< Which line to print. + }; + + typedef Common::Array<Item> Items; + + + Common::RandomSource *_rnd; + + Items _items; ///< All GCT items. + + // The area on which to print + bool _hasArea; + int16 _areaLeft; + int16 _areaTop; + int16 _areaRight; + int16 _areaBottom; + + /** Index of the current item we're drawing. */ + uint16 _currentItem; + /** Text left to draw. */ + Common::List<Common::String> _currentText; + + + // -- Loading helpers -- + + void load(Common::SeekableReadStream &gct); + void readLine(Common::SeekableReadStream &gct, Line &line, uint16 lineSize) const; + + + // -- Draw helpers -- + + Common::String getLineText(const Line &line) const; + void getItemText(uint item, Common::List<Common::String> &text) const; +}; + +} // End of namespace Gob + +#endif // GOB_PREGOB_GCTFILE_H diff --git a/engines/gob/pregob/onceupon/abracadabra.cpp b/engines/gob/pregob/onceupon/abracadabra.cpp new file mode 100644 index 0000000000..2cf6855ef8 --- /dev/null +++ b/engines/gob/pregob/onceupon/abracadabra.cpp @@ -0,0 +1,137 @@ +/* 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 "common/textconsole.h" + +#include "gob/gob.h" + +#include "gob/pregob/onceupon/abracadabra.h" + +static const uint8 kCopyProtectionColors[7] = { + 14, 11, 13, 1, 7, 12, 2 +}; + +static const uint8 kCopyProtectionShapes[7 * 20] = { + 3, 4, 3, 0, 1, 2, 0, 2, 2, 0, 2, 4, 0, 3, 4, 1, 1, 4, 1, 3, + 0, 2, 0, 4, 2, 4, 4, 2, 3, 0, 1, 1, 1, 1, 3, 0, 4, 2, 3, 4, + 0, 0, 1, 2, 1, 1, 2, 4, 3, 1, 4, 2, 4, 4, 2, 4, 1, 2, 3, 3, + 1, 0, 2, 3, 4, 2, 3, 2, 2, 0, 0, 0, 4, 2, 3, 4, 4, 0, 4, 1, + 4, 2, 1, 1, 1, 1, 4, 3, 4, 2, 3, 0, 0, 3, 0, 2, 3, 0, 2, 4, + 4, 2, 4, 3, 0, 4, 0, 2, 3, 1, 4, 1, 3, 1, 0, 0, 2, 1, 3, 2, + 3, 1, 0, 3, 1, 3, 4, 2, 4, 4, 3, 2, 0, 2, 0, 1, 2, 0, 1, 4 +}; + +static const uint8 kCopyProtectionObfuscate[4] = { + 1, 0, 2, 3 +}; + +namespace Gob { + +namespace OnceUpon { + +const OnceUpon::MenuButton Abracadabra::kAnimalsButtons = { + true, 131, 127, 183, 164, 193, 0, 243, 35, 132, 128, 0 +}; + +const OnceUpon::MenuButton Abracadabra::kAnimalButtons[] = { + {false, 37, 89, 95, 127, 37, 89, 95, 127, 131, 25, 0}, + {false, 114, 65, 172, 111, 114, 65, 172, 111, 131, 25, 1}, + {false, 186, 72, 227, 96, 186, 72, 227, 96, 139, 25, 2}, + {false, 249, 87, 282, 112, 249, 87, 282, 112, 143, 25, 3}, + {false, 180, 102, 234, 138, 180, 102, 234, 138, 133, 25, 4}, + {false, 197, 145, 242, 173, 197, 145, 242, 173, 137, 25, 5}, + {false, 113, 151, 171, 176, 113, 151, 171, 176, 131, 25, 6}, + {false, 114, 122, 151, 150, 114, 122, 151, 150, 141, 25, 7}, + {false, 36, 136, 94, 176, 36, 136, 94, 176, 131, 25, 8}, + {false, 243, 123, 295, 155, 243, 123, 295, 155, 136, 25, 9} +}; + +const char *Abracadabra::kAnimalNames[] = { + "loup", + "drag", + "arai", + "crap", + "crab", + "mous", + "saut", + "guep", + "rhin", + "scor" +}; + +// The houses where the stork can drop a bundle +const OnceUpon::MenuButton Abracadabra::kStorkHouses[] = { + {false, 16, 80, 87, 125, 0, 0, 0, 0, 0, 0, 0}, // Castle , Lord & Lady + {false, 61, 123, 96, 149, 0, 0, 0, 0, 0, 0, 1}, // Cottage, Farmers + {false, 199, 118, 226, 137, 0, 0, 0, 0, 0, 0, 2}, // Hut , Woodcutters + {false, 229, 91, 304, 188, 0, 0, 0, 0, 0, 0, 3} // Palace , King & Queen +}; + +// The stork bundle drop parameters +const Stork::BundleDrop Abracadabra::kStorkBundleDrops[] = { + { 14, 65, 127, true }, + { 14, 76, 152, true }, + { 14, 204, 137, true }, + { 11, 275, 179, false } +}; + +// Parameters for the stork section. +const OnceUpon::StorkParam Abracadabra::kStorkParam = { + "present.cmp", ARRAYSIZE(kStorkHouses), kStorkHouses, kStorkBundleDrops +}; + + +Abracadabra::Abracadabra(GobEngine *vm) : OnceUpon(vm) { +} + +Abracadabra::~Abracadabra() { +} + +void Abracadabra::run() { + init(); + + // Copy protection + bool correctCP = doCopyProtection(kCopyProtectionColors, kCopyProtectionShapes, kCopyProtectionObfuscate); + if (_vm->shouldQuit() || !correctCP) + return; + + // Show the intro + showIntro(); + if (_vm->shouldQuit()) + return; + + // Handle the start menu + doStartMenu(&kAnimalsButtons, ARRAYSIZE(kAnimalButtons), kAnimalButtons, kAnimalNames); + if (_vm->shouldQuit()) + return; + + // Play the actual game + playGame(); +} + +const OnceUpon::StorkParam &Abracadabra::getStorkParameters() const { + return kStorkParam; +} + +} // End of namespace OnceUpon + +} // End of namespace Gob diff --git a/engines/gob/pregob/onceupon/abracadabra.h b/engines/gob/pregob/onceupon/abracadabra.h new file mode 100644 index 0000000000..8048213f5f --- /dev/null +++ b/engines/gob/pregob/onceupon/abracadabra.h @@ -0,0 +1,61 @@ +/* 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 GOB_PREGOB_ONCEUPON_ABRACADABRA_H +#define GOB_PREGOB_ONCEUPON_ABRACADABRA_H + +#include "gob/pregob/onceupon/onceupon.h" + +namespace Gob { + +namespace OnceUpon { + +class Abracadabra : public OnceUpon { +public: + Abracadabra(GobEngine *vm); + ~Abracadabra(); + + void run(); + +protected: + const StorkParam &getStorkParameters() const; + +private: + /** Definition of the menu button that leads to the animal names screen. */ + static const MenuButton kAnimalsButtons; + + /** Definition of the buttons that make up the animals in the animal names screen. */ + static const MenuButton kAnimalButtons[]; + /** File prefixes for the name of each animal. */ + static const char *kAnimalNames[]; + + // Parameters for the stork section. + static const MenuButton kStorkHouses[]; + static const Stork::BundleDrop kStorkBundleDrops[]; + static const struct StorkParam kStorkParam; +}; + +} // End of namespace OnceUpon + +} // End of namespace Gob + +#endif // GOB_PREGOB_ONCEUPON_ABRACADABRA_H diff --git a/engines/gob/pregob/onceupon/babayaga.cpp b/engines/gob/pregob/onceupon/babayaga.cpp new file mode 100644 index 0000000000..ef56b9dd0b --- /dev/null +++ b/engines/gob/pregob/onceupon/babayaga.cpp @@ -0,0 +1,137 @@ +/* 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 "common/textconsole.h" + +#include "gob/gob.h" + +#include "gob/pregob/onceupon/babayaga.h" + +static const uint8 kCopyProtectionColors[7] = { + 14, 11, 13, 1, 7, 12, 2 +}; + +static const uint8 kCopyProtectionShapes[7 * 20] = { + 0, 0, 1, 2, 1, 1, 2, 4, 3, 1, 4, 2, 4, 4, 2, 4, 1, 2, 3, 3, + 3, 1, 0, 3, 1, 3, 4, 2, 4, 4, 3, 2, 0, 2, 0, 1, 2, 0, 1, 4, + 1, 0, 2, 3, 4, 2, 3, 2, 2, 0, 0, 0, 4, 2, 3, 4, 4, 0, 4, 1, + 0, 2, 0, 4, 2, 4, 4, 2, 3, 0, 1, 1, 1, 1, 3, 0, 4, 2, 3, 4, + 3, 4, 3, 0, 1, 2, 0, 2, 2, 0, 2, 4, 0, 3, 4, 1, 1, 4, 1, 3, + 4, 2, 1, 1, 1, 1, 4, 3, 4, 2, 3, 0, 0, 3, 0, 2, 3, 0, 2, 4, + 4, 2, 4, 3, 0, 4, 0, 2, 3, 1, 4, 1, 3, 1, 0, 0, 2, 1, 3, 2 +}; + +static const uint8 kCopyProtectionObfuscate[4] = { + 0, 1, 2, 3 +}; + +namespace Gob { + +namespace OnceUpon { + +const OnceUpon::MenuButton BabaYaga::kAnimalsButtons = { + true, 131, 127, 183, 164, 193, 0, 245, 37, 131, 127, 0 +}; + +const OnceUpon::MenuButton BabaYaga::kAnimalButtons[] = { + {false, 34, 84, 92, 127, 34, 84, 92, 127, 131, 25, 0}, + {false, 114, 65, 172, 111, 114, 65, 172, 111, 131, 25, 1}, + {false, 186, 72, 227, 96, 186, 72, 227, 96, 139, 25, 2}, + {false, 249, 87, 282, 112, 249, 87, 282, 112, 143, 25, 3}, + {false, 180, 97, 234, 138, 180, 97, 234, 138, 133, 25, 4}, + {false, 197, 145, 242, 173, 197, 145, 242, 173, 137, 25, 5}, + {false, 113, 156, 171, 176, 113, 156, 171, 176, 131, 25, 6}, + {false, 114, 127, 151, 150, 114, 127, 151, 150, 141, 25, 7}, + {false, 36, 136, 94, 176, 36, 136, 94, 176, 131, 25, 8}, + {false, 245, 123, 293, 155, 245, 123, 293, 155, 136, 25, 9} +}; + +const char *BabaYaga::kAnimalNames[] = { + "vaut", + "drag", + "arai", + "gren", + "fauc", + "abei", + "serp", + "tort", + "sang", + "rena" +}; + +// The houses where the stork can drop a bundle +const OnceUpon::MenuButton BabaYaga::kStorkHouses[] = { + {false, 16, 80, 87, 125, 0, 0, 0, 0, 0, 0, 0}, // Castle , Lord & Lady + {false, 61, 123, 96, 149, 0, 0, 0, 0, 0, 0, 1}, // Cottage, Farmers + {false, 199, 118, 226, 137, 0, 0, 0, 0, 0, 0, 2}, // Hut , Woodcutters + {false, 229, 91, 304, 188, 0, 0, 0, 0, 0, 0, 3} // Palace , King & Queen +}; + +// The stork bundle drop parameters +const Stork::BundleDrop BabaYaga::kStorkBundleDrops[] = { + { 14, 35, 129, true }, + { 14, 70, 148, true }, + { 14, 206, 136, true }, + { 11, 260, 225, false } +}; + +// Parameters for the stork section. +const OnceUpon::StorkParam BabaYaga::kStorkParam = { + "present2.cmp", ARRAYSIZE(kStorkHouses), kStorkHouses, kStorkBundleDrops +}; + + +BabaYaga::BabaYaga(GobEngine *vm) : OnceUpon(vm) { +} + +BabaYaga::~BabaYaga() { +} + +void BabaYaga::run() { + init(); + + // Copy protection + bool correctCP = doCopyProtection(kCopyProtectionColors, kCopyProtectionShapes, kCopyProtectionObfuscate); + if (_vm->shouldQuit() || !correctCP) + return; + + // Show the intro + showIntro(); + if (_vm->shouldQuit()) + return; + + // Handle the start menu + doStartMenu(&kAnimalsButtons, ARRAYSIZE(kAnimalButtons), kAnimalButtons, kAnimalNames); + if (_vm->shouldQuit()) + return; + + // Play the actual game + playGame(); +} + +const OnceUpon::StorkParam &BabaYaga::getStorkParameters() const { + return kStorkParam; +} + +} // End of namespace OnceUpon + +} // End of namespace Gob diff --git a/engines/gob/pregob/onceupon/babayaga.h b/engines/gob/pregob/onceupon/babayaga.h new file mode 100644 index 0000000000..0241f78f4e --- /dev/null +++ b/engines/gob/pregob/onceupon/babayaga.h @@ -0,0 +1,61 @@ +/* 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 GOB_PREGOB_ONCEUPON_BABAYAGA_H +#define GOB_PREGOB_ONCEUPON_BABAYAGA_H + +#include "gob/pregob/onceupon/onceupon.h" + +namespace Gob { + +namespace OnceUpon { + +class BabaYaga : public OnceUpon { +public: + BabaYaga(GobEngine *vm); + ~BabaYaga(); + + void run(); + +protected: + const StorkParam &getStorkParameters() const; + +private: + /** Definition of the menu button that leads to the animal names screen. */ + static const MenuButton kAnimalsButtons; + + /** Definition of the buttons that make up the animals in the animal names screen. */ + static const MenuButton kAnimalButtons[]; + /** File prefixes for the name of each animal. */ + static const char *kAnimalNames[]; + + // Parameters for the stork section. + static const MenuButton kStorkHouses[]; + static const Stork::BundleDrop kStorkBundleDrops[]; + static const struct StorkParam kStorkParam; +}; + +} // End of namespace OnceUpon + +} // End of namespace Gob + +#endif // GOB_PREGOB_ONCEUPON_BABAYAGA_H diff --git a/engines/gob/pregob/onceupon/brokenstrings.h b/engines/gob/pregob/onceupon/brokenstrings.h new file mode 100644 index 0000000000..89acb1c6bd --- /dev/null +++ b/engines/gob/pregob/onceupon/brokenstrings.h @@ -0,0 +1,60 @@ +/* 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 GOB_PREGOB_ONCEUPON_BROKENSTRINGS_H +#define GOB_PREGOB_ONCEUPON_BROKENSTRINGS_H + +struct BrokenString { + const char *wrong; + const char *correct; +}; + +struct BrokenStringLanguage { + const BrokenString *strings; + uint count; +}; + +static const BrokenString kBrokenStringsGerman[] = { + { "Zeichungen von Kaki," , "Zeichnungen von Kaki," }, + { "die es in seine Wachtr\204ume", "die es in seine Tagtr\204ume" }, + { " Spielerfahrung" , " Spielerfahren" }, + { " Fortgeschrittene" , " Fortgeschritten" }, + { "die Vespe" , "die Wespe" }, + { "das Rhinoceros" , "das Rhinozeros" }, + { "die Heusschrecke" , "die Heuschrecke" }, + { "Das, von Drachen gebrachte" , "Das vom Drachen gebrachte" }, + { "Am Waldesrand es sieht" , "Am Waldesrand sieht es" }, + { " das Kind den Palast." , "das Kind den Palast." }, + { "Am Waldessaum sieht" , "Am Waldesrand sieht" }, + { "tipp auf ESC!" , "dr\201cke ESC!" }, + { "Wohin fliegt der Drachen?" , "Wohin fliegt der Drache?" } +}; + +static const BrokenStringLanguage kBrokenStrings[kLanguageCount] = { + { 0, 0 }, // French + { kBrokenStringsGerman, ARRAYSIZE(kBrokenStringsGerman) }, // German + { 0, 0 }, // English + { 0, 0 }, // Spanish + { 0, 0 }, // Italian +}; + +#endif // GOB_PREGOB_ONCEUPON_BROKENSTRINGS_H diff --git a/engines/gob/pregob/onceupon/chargenchild.cpp b/engines/gob/pregob/onceupon/chargenchild.cpp new file mode 100644 index 0000000000..ba099e4937 --- /dev/null +++ b/engines/gob/pregob/onceupon/chargenchild.cpp @@ -0,0 +1,117 @@ +/* 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 "gob/surface.h" +#include "gob/anifile.h" + +#include "gob/pregob/onceupon/chargenchild.h" + +enum Animation { + kAnimWalkLeft = 0, + kAnimWalkRight = 1, + kAnimJumpLeft = 2, + kAnimJumpRight = 3, + kAnimTapFoot = 14 +}; + +namespace Gob { + +namespace OnceUpon { + +CharGenChild::CharGenChild(const ANIFile &ani) : ANIObject(ani) { + setPosition(265, 110); + setAnimation(kAnimWalkLeft); + setVisible(true); + setPause(false); +} + +CharGenChild::~CharGenChild() { +} + +void CharGenChild::advance() { + bool wasLastFrame = lastFrame(); + + ANIObject::advance(); + + int16 x, y, left, top, width, height; + getPosition(x, y); + getFramePosition(left, top); + getFrameSize(width, height); + + const int16 right = left + width - 1; + + switch (getAnimation()) { + case kAnimWalkLeft: + if (left <= 147) + setAnimation(kAnimWalkRight); + break; + + case kAnimWalkRight: + if (right >= 290) { + setAnimation(kAnimJumpLeft); + + setPosition(x, y - 14); + } + break; + + case kAnimJumpLeft: + if (wasLastFrame) { + setAnimation(kAnimTapFoot); + + setPosition(x, y - 10); + } + break; + + case kAnimTapFoot: + if (wasLastFrame) { + setAnimation(kAnimJumpRight); + + setPosition(x, y + 10); + } + break; + + case kAnimJumpRight: + if (wasLastFrame) { + setAnimation(kAnimWalkLeft); + + setPosition(x, y + 14); + } + break; + } +} + +CharGenChild::Sound CharGenChild::shouldPlaySound() const { + const uint16 anim = getAnimation(); + const uint16 frame = getFrame(); + + if (((anim == kAnimWalkLeft) || (anim == kAnimWalkRight)) && ((frame == 1) || (frame == 6))) + return kSoundWalk; + + if (((anim == kAnimJumpLeft) || (anim == kAnimJumpRight)) && (frame == 0)) + return kSoundJump; + + return kSoundNone; +} + +} // End of namespace OnceUpon + +} // End of namespace Gob diff --git a/engines/gob/pregob/onceupon/chargenchild.h b/engines/gob/pregob/onceupon/chargenchild.h new file mode 100644 index 0000000000..3b09ef112a --- /dev/null +++ b/engines/gob/pregob/onceupon/chargenchild.h @@ -0,0 +1,60 @@ +/* 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 GOB_PREGOB_ONCEUPON_CHARGENCHILD_H +#define GOB_PREGOB_ONCEUPON_CHARGENCHILD_H + +#include "common/system.h" + +#include "gob/aniobject.h" + +namespace Gob { + +class Surface; +class ANIFile; + +namespace OnceUpon { + +/** The child running around on the character generator screen. */ +class CharGenChild : public ANIObject { +public: + enum Sound { + kSoundNone = 0, + kSoundWalk , + kSoundJump + }; + + CharGenChild(const ANIFile &ani); + ~CharGenChild(); + + /** Advance the animation to the next frame. */ + void advance(); + + /** Should we play a sound right now? */ + Sound shouldPlaySound() const; +}; + +} // End of namespace OnceUpon + +} // End of namespace Gob + +#endif // GOB_PREGOB_ONCEUPON_CHARGENCHILD_H diff --git a/engines/gob/pregob/onceupon/onceupon.cpp b/engines/gob/pregob/onceupon/onceupon.cpp new file mode 100644 index 0000000000..e4c2df34c0 --- /dev/null +++ b/engines/gob/pregob/onceupon/onceupon.cpp @@ -0,0 +1,1904 @@ +/* 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 "gob/gob.h" +#include "gob/global.h" +#include "gob/util.h" +#include "gob/dataio.h" +#include "gob/surface.h" +#include "gob/draw.h" +#include "gob/video.h" +#include "gob/anifile.h" +#include "gob/aniobject.h" + +#include "gob/sound/sound.h" + +#include "gob/pregob/txtfile.h" +#include "gob/pregob/gctfile.h" + +#include "gob/pregob/onceupon/onceupon.h" +#include "gob/pregob/onceupon/palettes.h" +#include "gob/pregob/onceupon/title.h" +#include "gob/pregob/onceupon/parents.h" +#include "gob/pregob/onceupon/chargenchild.h" + +static const uint kLanguageCount = 5; + +static const uint kCopyProtectionHelpStringCount = 3; + +static const char *kCopyProtectionHelpStrings[Gob::OnceUpon::OnceUpon::kLanguageCount][kCopyProtectionHelpStringCount] = { + { // French + "Consulte le livret des animaux, rep\212re la", + "page correspondant \205 la couleur de l\'\202cran", + "et clique le symbole associ\202 \205 l\'animal affich\202.", + }, + { // German + "Suche im Tieralbum die Seite, die der Farbe auf", + "dem Bildschirm entspricht und klicke auf das", + "Tiersymbol.", + }, + { // English + "Consult the book of animals, find the page", + "corresponding to the colour of screen and click", + "the symbol associated with the animal displayed.", + }, + { // Spanish + "Consulta el libro de los animales, localiza la ", + "p\240gina que corresponde al color de la pantalla.", + "Cliquea el s\241mbolo asociado al animal que aparece.", + }, + { // Italian + "Guarda il libretto degli animali, trova la", + "pagina che corrisponde al colore dello schermo,", + "clicca il simbolo associato all\'animale presentato", + } +}; + +static const char *kCopyProtectionWrongStrings[Gob::OnceUpon::OnceUpon::kLanguageCount] = { + "Tu t\'es tromp\202, dommage...", // French + "Schade, du hast dich geirrt." , // German + "You are wrong, what a pity!" , // English + "Te equivocas, l\240stima..." , // Spanish + "Sei Sbagliato, peccato..." // Italian +}; + +static const uint kCopyProtectionShapeCount = 5; + +static const int16 kCopyProtectionShapeCoords[kCopyProtectionShapeCount][6] = { + { 0, 51, 26, 75, 60, 154}, + { 28, 51, 58, 81, 96, 151}, + { 60, 51, 94, 79, 136, 152}, + { 96, 51, 136, 71, 180, 155}, + {140, 51, 170, 77, 228, 153} +}; + +enum ClownAnimation { + kClownAnimationClownCheer = 0, + kClownAnimationClownStand = 1, + kClownAnimationClownCry = 6 +}; + +// 12 seconds delay for one area full of GCT text +static const uint32 kGCTDelay = 12000; + +namespace Gob { + +namespace OnceUpon { + +const OnceUpon::MenuButton OnceUpon::kMainMenuDifficultyButton[] = { + {false, 29, 18, 77, 57, 0, 0, 0, 0, 0, 0, (int)kDifficultyBeginner}, + {false, 133, 18, 181, 57, 0, 0, 0, 0, 0, 0, (int)kDifficultyIntermediate}, + {false, 241, 18, 289, 57, 0, 0, 0, 0, 0, 0, (int)kDifficultyAdvanced}, +}; + +const OnceUpon::MenuButton OnceUpon::kSectionButtons[] = { + {false, 27, 121, 91, 179, 0, 0, 0, 0, 0, 0, 0}, + { true, 95, 121, 159, 179, 4, 1, 56, 49, 100, 126, 2}, + { true, 163, 121, 227, 179, 64, 1, 120, 49, 168, 126, 6}, + { true, 231, 121, 295, 179, 128, 1, 184, 49, 236, 126, 10} +}; + +const OnceUpon::MenuButton OnceUpon::kIngameButtons[] = { + {true, 108, 83, 139, 116, 0, 0, 31, 34, 108, 83, 0}, + {true, 144, 83, 175, 116, 36, 0, 67, 34, 144, 83, 1}, + {true, 180, 83, 211, 116, 72, 0, 103, 34, 180, 83, 2} +}; + +const OnceUpon::MenuButton OnceUpon::kAnimalNamesBack = { + true, 19, 13, 50, 46, 36, 0, 67, 34, 19, 13, 1 +}; + +const OnceUpon::MenuButton OnceUpon::kLanguageButtons[] = { + {true, 43, 80, 93, 115, 0, 55, 50, 90, 43, 80, 0}, + {true, 132, 80, 182, 115, 53, 55, 103, 90, 132, 80, 1}, + {true, 234, 80, 284, 115, 106, 55, 156, 90, 234, 80, 2}, + {true, 43, 138, 93, 173, 159, 55, 209, 90, 43, 138, 3}, + {true, 132, 138, 182, 173, 212, 55, 262, 90, 132, 138, 4}, + {true, 234, 138, 284, 173, 265, 55, 315, 90, 234, 138, 2} +}; + +const char *OnceUpon::kSound[kSoundCount] = { + "diamant.snd", // kSoundClick + "cigogne.snd", // kSoundStork + "saute.snd" // kSoundJump +}; + +const OnceUpon::SectionFunc OnceUpon::kSectionFuncs[kSectionCount] = { + &OnceUpon::sectionStork, + &OnceUpon::sectionChapter1, + &OnceUpon::sectionParents, + &OnceUpon::sectionChapter2, + &OnceUpon::sectionForest0, + &OnceUpon::sectionChapter3, + &OnceUpon::sectionEvilCastle, + &OnceUpon::sectionChapter4, + &OnceUpon::sectionForest1, + &OnceUpon::sectionChapter5, + &OnceUpon::sectionBossFight, + &OnceUpon::sectionChapter6, + &OnceUpon::sectionForest2, + &OnceUpon::sectionChapter7, + &OnceUpon::sectionEnd +}; + + +OnceUpon::ScreenBackup::ScreenBackup() : palette(-1), changedCursor(false), cursorVisible(false) { + screen = new Surface(320, 200, 1); +} + +OnceUpon::ScreenBackup::~ScreenBackup() { + delete screen; +} + + +OnceUpon::OnceUpon(GobEngine *vm) : PreGob(vm), _openedArchives(false), + _jeudak(0), _lettre(0), _plettre(0), _glettre(0) { + +} + +OnceUpon::~OnceUpon() { + deinit(); +} + +void OnceUpon::init() { + deinit(); + + // Open data files + + bool hasSTK1 = _vm->_dataIO->openArchive("stk1.stk", true); + bool hasSTK2 = _vm->_dataIO->openArchive("stk2.stk", true); + bool hasSTK3 = _vm->_dataIO->openArchive("stk3.stk", true); + + if (!hasSTK1 || !hasSTK2 || !hasSTK3) + error("OnceUpon::OnceUpon(): Failed to open archives"); + + _openedArchives = true; + + // Open fonts + + _jeudak = _vm->_draw->loadFont("jeudak.let"); + _lettre = _vm->_draw->loadFont("lettre.let"); + _plettre = _vm->_draw->loadFont("plettre.let"); + _glettre = _vm->_draw->loadFont("glettre.let"); + + if (!_jeudak || !_lettre || !_plettre || !_glettre) + error("OnceUpon::OnceUpon(): Failed to fonts (%d, %d, %d, %d)", + _jeudak != 0, _lettre != 0, _plettre != 0, _glettre != 0); + + // Verify the language + + if (_vm->_global->_language == kLanguageAmerican) + _vm->_global->_language = kLanguageBritish; + + if ((_vm->_global->_language >= kLanguageCount)) + error("We do not support the language \"%s\".\n" + "If you are certain that your game copy includes this language,\n" + "please contact the ScummVM team with details about this version.\n" + "Thanks", _vm->getLangDesc(_vm->_global->_language)); + + // Load all our sounds and init the screen + + loadSounds(kSound, kSoundCount); + initScreen(); + + // We start with an invalid palette + _palette = -1; + + // No quit requested at start + _quit = false; + + // We start with no selected difficulty and at section 0 + _difficulty = kDifficultyCount; + _section = 0; + + // Default name + _name = "Nemo"; + + // Default character properties + _house = 0; + _head = 0; + _colorHair = 0; + _colorJacket = 0; + _colorTrousers = 0; +} + +void OnceUpon::deinit() { + // Free sounds + freeSounds(); + + // Free fonts + + delete _jeudak; + delete _lettre; + delete _plettre; + delete _glettre; + + _jeudak = 0; + _lettre = 0; + _plettre = 0; + _glettre = 0; + + // Close archives + + if (_openedArchives) { + _vm->_dataIO->closeArchive(true); + _vm->_dataIO->closeArchive(true); + _vm->_dataIO->closeArchive(true); + } + + _openedArchives = false; +} + +void OnceUpon::setGamePalette(uint palette) { + if (palette >= kPaletteCount) + return; + + _palette = palette; + + setPalette(kGamePalettes[palette], kPaletteSize); +} + +void OnceUpon::setGameCursor() { + Surface cursor(320, 16, 1); + + // Set the default game cursor + _vm->_video->drawPackedSprite("icon.cmp", cursor); + setCursor(cursor, 105, 0, 120, 15, 0, 0); +} + +void OnceUpon::drawLineByLine(const Surface &src, int16 left, int16 top, int16 right, int16 bottom, + int16 x, int16 y) const { + + // A special way of drawing something: + // Draw every other line "downwards", wait a bit after each line + // Then, draw the remaining lines "upwards" and again wait a bit after each line. + + if (_vm->shouldQuit()) + return; + + const int16 width = right - left + 1; + const int16 height = bottom - top + 1; + + if ((width <= 0) || (height <= 0)) + return; + + // Draw the even lines downwards + for (int16 i = 0; i < height; i += 2) { + if (_vm->shouldQuit()) + return; + + _vm->_draw->_backSurface->blit(src, left, top + i, right, top + i, x, y + i); + + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, x, y + i, x + width - 1, y + 1); + _vm->_draw->blitInvalidated(); + + _vm->_util->longDelay(1); + } + + // Draw the odd lines upwards + for (int16 i = (height & 1) ? height : (height - 1); i >= 0; i -= 2) { + if (_vm->shouldQuit()) + return; + + _vm->_draw->_backSurface->blit(src, left, top + i, right, top + i, x, y + i); + + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, x, y + i, x + width - 1, y + 1); + _vm->_draw->blitInvalidated(); + + _vm->_util->longDelay(1); + } +} + +void OnceUpon::backupScreen(ScreenBackup &backup, bool setDefaultCursor) { + // Backup the screen and palette + backup.screen->blit(*_vm->_draw->_backSurface); + backup.palette = _palette; + + // Backup the cursor + + backup.cursorVisible = isCursorVisible(); + + backup.changedCursor = false; + if (setDefaultCursor) { + backup.changedCursor = true; + + addCursor(); + setGameCursor(); + } +} + +void OnceUpon::restoreScreen(ScreenBackup &backup) { + if (_vm->shouldQuit()) + return; + + // Restore the screen + _vm->_draw->_backSurface->blit(*backup.screen); + _vm->_draw->forceBlit(); + + // Restore the palette + if (backup.palette >= 0) + setGamePalette(backup.palette); + + // Restore the cursor + + if (!backup.cursorVisible) + hideCursor(); + + if (backup.changedCursor) + removeCursor(); + + backup.changedCursor = false; +} + +void OnceUpon::fixTXTStrings(TXTFile &txt) const { + TXTFile::LineArray &lines = txt.getLines(); + for (uint i = 0; i < lines.size(); i++) + lines[i].text = fixString(lines[i].text); +} + +#include "gob/pregob/onceupon/brokenstrings.h" +Common::String OnceUpon::fixString(const Common::String &str) const { + const BrokenStringLanguage &broken = kBrokenStrings[_vm->_global->_language]; + + for (uint i = 0; i < broken.count; i++) { + if (str == broken.strings[i].wrong) + return broken.strings[i].correct; + } + + return str; +} + +enum ClownAnimation { + kClownAnimationStand = 0, + kClownAnimationCheer = 1, + kClownAnimationCry = 2 +}; + +const PreGob::AnimProperties OnceUpon::kClownAnimations[] = { + { 1, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, + { 0, 0, ANIObject::kModeOnce , true, false, false, 0, 0}, + { 6, 0, ANIObject::kModeOnce , true, false, false, 0, 0} +}; + +enum CopyProtectionState { + kCPStateSetup, // Set up the screen + kCPStateWaitUser, // Waiting for the user to pick a shape + kCPStateWaitClown, // Waiting for the clown animation to finish + kCPStateFinish // Finishing +}; + +bool OnceUpon::doCopyProtection(const uint8 colors[7], const uint8 shapes[7 * 20], const uint8 obfuscate[4]) { + fadeOut(); + setPalette(kCopyProtectionPalette, kPaletteSize); + + // Load the copy protection sprites + Surface sprites[2] = {Surface(320, 200, 1), Surface(320, 200, 1)}; + + _vm->_video->drawPackedSprite("grille1.cmp", sprites[0]); + _vm->_video->drawPackedSprite("grille2.cmp", sprites[1]); + + // Load the clown animation + ANIFile ani (_vm, "grille.ani", 320); + ANIList anims; + + loadAnims(anims, ani, 1, &kClownAnimations[kClownAnimationStand]); + + // Set the copy protection cursor + setCursor(sprites[1], 5, 110, 20, 134, 3, 0); + + // We start with 2 tries left, not having a correct answer and the copy protection not set up yet + CopyProtectionState state = kCPStateSetup; + + uint8 triesLeft = 2; + int8 animalShape = -1; + bool hasCorrect = false; + + while (!_vm->shouldQuit() && (state != kCPStateFinish)) { + clearAnim(anims); + + // Set up the screen + if (state == kCPStateSetup) { + animalShape = cpSetup(colors, shapes, obfuscate, sprites); + + setAnim(*anims[0], kClownAnimations[kClownAnimationStand]); + state = kCPStateWaitUser; + } + + drawAnim(anims); + + // If we're waiting for the clown and he finished, evaluate if we're finished + if (!anims[0]->isVisible() && (state == kCPStateWaitClown)) + state = (hasCorrect || (--triesLeft == 0)) ? kCPStateFinish : kCPStateSetup; + + showCursor(); + fadeIn(); + + endFrame(true); + + int16 mouseX, mouseY; + MouseButtons mouseButtons; + + checkInput(mouseX, mouseY, mouseButtons); + + if (state == kCPStateWaitUser) { + // Look if we clicked a shaped and got it right + + int8 guessedShape = -1; + if (mouseButtons == kMouseButtonsLeft) + guessedShape = cpFindShape(mouseX, mouseY); + + if (guessedShape >= 0) { + hasCorrect = guessedShape == animalShape; + animalShape = -1; + + setAnim(*anims[0], kClownAnimations[hasCorrect ? kClownAnimationCheer : kClownAnimationCry]); + state = kCPStateWaitClown; + } + } + } + + freeAnims(anims); + + fadeOut(); + hideCursor(); + clearScreen(); + + // Display the "You are wrong" screen + if (!hasCorrect) + cpWrong(); + + return hasCorrect; +} + +int8 OnceUpon::cpSetup(const uint8 colors[7], const uint8 shapes[7 * 20], const uint8 obfuscate[4], + const Surface sprites[2]) { + + fadeOut(); + hideCursor(); + + // Get a random animal and animal color + int8 animalColor = _vm->_util->getRandom(7); + while ((colors[animalColor] == 1) || (colors[animalColor] == 7) || (colors[animalColor] == 11)) + animalColor = _vm->_util->getRandom(7); + + int8 animal = _vm->_util->getRandom(20); + + int8 animalShape = shapes[animalColor * 20 + animal]; + if (animal < 4) + animal = obfuscate[animal]; + + // Get the position of the animal sprite + int16 animalLeft = (animal % 4) * 80; + int16 animalTop = (animal / 4) * 50; + + uint8 sprite = 0; + if (animalTop >= 200) { + animalTop -= 200; + sprite = 1; + } + + int16 animalRight = animalLeft + 80 - 1; + int16 animalBottom = animalTop + 50 - 1; + + // Fill with the animal color + _vm->_draw->_backSurface->fill(colors[animalColor]); + + // Print the help line strings + for (uint i = 0; i < kCopyProtectionHelpStringCount; i++) { + const char * const helpString = kCopyProtectionHelpStrings[_vm->_global->_language][i]; + + const int x = 160 - (strlen(helpString) * _plettre->getCharWidth()) / 2; + const int y = i * 10 + 5; + + _plettre->drawString(helpString, x, y, 8, 0, true, *_vm->_draw->_backSurface); + } + + // White rectangle with black border + _vm->_draw->_backSurface->fillRect( 93, 43, 226, 134, 15); + _vm->_draw->_backSurface->drawRect( 92, 42, 227, 135, 0); + + // Draw the animal in the animal color + _vm->_draw->_backSurface->fillRect(120, 63, 199, 112, colors[animalColor]); + _vm->_draw->_backSurface->blit(sprites[sprite], animalLeft, animalTop, animalRight, animalBottom, 120, 63, 0); + + // Draw the shapes + for (uint i = 0; i < kCopyProtectionShapeCount; i++) { + const int16 * const coords = kCopyProtectionShapeCoords[i]; + + _vm->_draw->_backSurface->blit(sprites[1], coords[0], coords[1], coords[2], coords[3], coords[4], coords[5], 0); + } + + _vm->_draw->forceBlit(); + + return animalShape; +} + +int8 OnceUpon::cpFindShape(int16 x, int16 y) const { + // Look through all shapes and check if the coordinates are inside one of them + for (uint i = 0; i < kCopyProtectionShapeCount; i++) { + const int16 * const coords = kCopyProtectionShapeCoords[i]; + + const int16 left = coords[4]; + const int16 top = coords[5]; + const int16 right = coords[4] + (coords[2] - coords[0] + 1) - 1; + const int16 bottom = coords[5] + (coords[3] - coords[1] + 1) - 1; + + if ((x >= left) && (x <= right) && (y >= top) && (y <= bottom)) + return i; + } + + return -1; +} + +void OnceUpon::cpWrong() { + // Display the "You are wrong" string, centered + + const char * const wrongString = kCopyProtectionWrongStrings[_vm->_global->_language]; + const int wrongX = 160 - (strlen(wrongString) * _plettre->getCharWidth()) / 2; + + _vm->_draw->_backSurface->clear(); + _plettre->drawString(wrongString, wrongX, 100, 15, 0, true, *_vm->_draw->_backSurface); + + _vm->_draw->forceBlit(); + + fadeIn(); + + waitInput(); + + fadeOut(); + clearScreen(); +} + +void OnceUpon::showIntro() { + // Show all intro parts + + // "Loading" + showWait(10); + if (_vm->shouldQuit()) + return; + + // Quote about fairy tales + showQuote(); + if (_vm->shouldQuit()) + return; + + // Once Upon A Time title + showTitle(); + if (_vm->shouldQuit()) + return; + + // Game title screen + showChapter(0); + if (_vm->shouldQuit()) + return; + + // "Loading" + showWait(17); +} + +void OnceUpon::showWait(uint palette) { + // Show the loading floppy + + fadeOut(); + clearScreen(); + setGamePalette(palette); + + Surface wait(320, 43, 1); + + _vm->_video->drawPackedSprite("wait.cmp", wait); + _vm->_draw->_backSurface->blit(wait, 0, 0, 72, 33, 122, 84); + + _vm->_draw->forceBlit(); + + fadeIn(); +} + +void OnceUpon::showQuote() { + // Show the quote about fairytales + + fadeOut(); + clearScreen(); + setGamePalette(11); + + static const Font *fonts[3] = { _plettre, _glettre, _plettre }; + + TXTFile *quote = loadTXT(getLocFile("gene.tx"), TXTFile::kFormatStringPositionColorFont); + quote->draw(*_vm->_draw->_backSurface, fonts, ARRAYSIZE(fonts)); + delete quote; + + _vm->_draw->forceBlit(); + + fadeIn(); + + waitInput(); + + fadeOut(); +} + +const PreGob::AnimProperties OnceUpon::kTitleAnimation = { + 8, 0, ANIObject::kModeContinuous, true, false, false, 0, 0 +}; + +void OnceUpon::showTitle() { + fadeOut(); + setGamePalette(10); + + Title title(_vm); + title.play(); +} + +void OnceUpon::showChapter(int chapter) { + // Display the intro text to a chapter + + fadeOut(); + clearScreen(); + setGamePalette(11); + + // Parchment background + _vm->_video->drawPackedSprite("parch.cmp", *_vm->_draw->_backSurface); + + static const Font *fonts[3] = { _plettre, _glettre, _plettre }; + + const Common::String chapterFile = getLocFile(Common::String::format("gene%d.tx", chapter)); + + TXTFile *gameTitle = loadTXT(chapterFile, TXTFile::kFormatStringPositionColorFont); + gameTitle->draw(*_vm->_draw->_backSurface, fonts, ARRAYSIZE(fonts)); + delete gameTitle; + + _vm->_draw->forceBlit(); + + fadeIn(); + + waitInput(); + + fadeOut(); +} + +void OnceUpon::showByeBye() { + fadeOut(); + hideCursor(); + clearScreen(); + setGamePalette(1); + + _plettre->drawString("Bye Bye....", 140, 80, 2, 0, true, *_vm->_draw->_backSurface); + _vm->_draw->forceBlit(); + + fadeIn(); + + _vm->_util->longDelay(1000); + + fadeOut(); +} + +void OnceUpon::doStartMenu(const MenuButton *animalsButton, uint animalCount, + const MenuButton *animalButtons, const char * const *animalNames) { + clearScreen(); + + // Wait until we clicked on of the difficulty buttons and are ready to start playing + while (!_vm->shouldQuit()) { + MenuAction action = handleStartMenu(animalsButton); + if (action == kMenuActionPlay) + break; + + // If we pressed the "listen to animal names" button, handle that screen + if (action == kMenuActionAnimals) + handleAnimalNames(animalCount, animalButtons, animalNames); + } +} + +OnceUpon::MenuAction OnceUpon::handleStartMenu(const MenuButton *animalsButton) { + ScreenBackup screenBackup; + backupScreen(screenBackup, true); + + fadeOut(); + setGamePalette(17); + drawStartMenu(animalsButton); + showCursor(); + fadeIn(); + + MenuAction action = kMenuActionNone; + while (!_vm->shouldQuit() && (action == kMenuActionNone)) { + endFrame(true); + + // Check user input + + int16 mouseX, mouseY; + MouseButtons mouseButtons; + + int16 key = checkInput(mouseX, mouseY, mouseButtons); + if (key == kKeyEscape) + // ESC -> Quit + return kMenuActionQuit; + + if (mouseButtons != kMouseButtonsLeft) + continue; + + playSound(kSoundClick); + + // If we clicked on a difficulty button, show the selected difficulty and start the game + int diff = checkButton(kMainMenuDifficultyButton, ARRAYSIZE(kMainMenuDifficultyButton), mouseX, mouseY); + if (diff >= 0) { + _difficulty = (Difficulty)diff; + action = kMenuActionPlay; + + drawStartMenu(animalsButton); + _vm->_util->longDelay(1000); + } + + if (animalsButton && (checkButton(animalsButton, 1, mouseX, mouseY) != -1)) + action = kMenuActionAnimals; + + } + + fadeOut(); + restoreScreen(screenBackup); + + return action; +} + +OnceUpon::MenuAction OnceUpon::handleMainMenu() { + ScreenBackup screenBackup; + backupScreen(screenBackup, true); + + fadeOut(); + setGamePalette(17); + drawMainMenu(); + showCursor(); + fadeIn(); + + MenuAction action = kMenuActionNone; + while (!_vm->shouldQuit() && (action == kMenuActionNone)) { + endFrame(true); + + // Check user input + + int16 mouseX, mouseY; + MouseButtons mouseButtons; + + int16 key = checkInput(mouseX, mouseY, mouseButtons); + if (key == kKeyEscape) + // ESC -> Quit + return kMenuActionQuit; + + if (mouseButtons != kMouseButtonsLeft) + continue; + + playSound(kSoundClick); + + // If we clicked on a difficulty button, change the current difficulty level + int diff = checkButton(kMainMenuDifficultyButton, ARRAYSIZE(kMainMenuDifficultyButton), mouseX, mouseY); + if ((diff >= 0) && (diff != (int)_difficulty)) { + _difficulty = (Difficulty)diff; + + drawMainMenu(); + } + + // If we clicked on a section button, restart the game from this section + int section = checkButton(kSectionButtons, ARRAYSIZE(kSectionButtons), mouseX, mouseY); + if ((section >= 0) && (section <= _section)) { + _section = section; + action = kMenuActionRestart; + } + + } + + fadeOut(); + restoreScreen(screenBackup); + + return action; +} + +OnceUpon::MenuAction OnceUpon::handleIngameMenu() { + ScreenBackup screenBackup; + backupScreen(screenBackup, true); + + drawIngameMenu(); + showCursor(); + + MenuAction action = kMenuActionNone; + while (!_vm->shouldQuit() && (action == kMenuActionNone)) { + endFrame(true); + + // Check user input + + int16 mouseX, mouseY; + MouseButtons mouseButtons; + + int16 key = checkInput(mouseX, mouseY, mouseButtons); + if ((key == kKeyEscape) || (mouseButtons == kMouseButtonsRight)) + // ESC or right mouse button -> Dismiss the menu + action = kMenuActionPlay; + + if (mouseButtons != kMouseButtonsLeft) + continue; + + int button = checkButton(kIngameButtons, ARRAYSIZE(kIngameButtons), mouseX, mouseY); + if (button == 0) + action = kMenuActionQuit; + else if (button == 1) + action = kMenuActionMainMenu; + else if (button == 2) + action = kMenuActionPlay; + + } + + clearIngameMenu(*screenBackup.screen); + restoreScreen(screenBackup); + + return action; +} + +void OnceUpon::drawStartMenu(const MenuButton *animalsButton) { + // Draw the background + _vm->_video->drawPackedSprite("menu2.cmp", *_vm->_draw->_backSurface); + + // Draw the "Listen to animal names" button + if (animalsButton) { + Surface elements(320, 38, 1); + _vm->_video->drawPackedSprite("elemenu.cmp", elements); + _vm->_draw->_backSurface->fillRect(animalsButton->left , animalsButton->top, + animalsButton->right, animalsButton->bottom, 0); + drawButton(*_vm->_draw->_backSurface, elements, *animalsButton); + } + + // Highlight the current difficulty + drawMenuDifficulty(); + + _vm->_draw->forceBlit(); +} + +void OnceUpon::drawMainMenu() { + // Draw the background + _vm->_video->drawPackedSprite("menu.cmp", *_vm->_draw->_backSurface); + + // Highlight the current difficulty + drawMenuDifficulty(); + + // Draw the section buttons + Surface elements(320, 200, 1); + _vm->_video->drawPackedSprite("elemenu.cmp", elements); + + for (uint i = 0; i < ARRAYSIZE(kSectionButtons); i++) { + const MenuButton &button = kSectionButtons[i]; + + if (!button.needDraw) + continue; + + if (_section >= (int)button.id) + drawButton(*_vm->_draw->_backSurface, elements, button); + } + + _vm->_draw->forceBlit(); +} + +void OnceUpon::drawIngameMenu() { + Surface menu(320, 34, 1); + _vm->_video->drawPackedSprite("icon.cmp", menu); + + // Draw the menu in a special way, button by button + for (uint i = 0; i < ARRAYSIZE(kIngameButtons); i++) { + const MenuButton &button = kIngameButtons[i]; + + drawLineByLine(menu, button.srcLeft, button.srcTop, button.srcRight, button.srcBottom, + button.dstX, button.dstY); + } + + _vm->_draw->forceBlit(); + _vm->_video->retrace(); +} + +void OnceUpon::drawMenuDifficulty() { + if (_difficulty == kDifficultyCount) + return; + + TXTFile *difficulties = loadTXT(getLocFile("diffic.tx"), TXTFile::kFormatStringPositionColor); + + // Draw the difficulty name + difficulties->draw((uint) _difficulty, *_vm->_draw->_backSurface, &_plettre, 1); + + // Draw a border around the current difficulty + drawButtonBorder(kMainMenuDifficultyButton[_difficulty], difficulties->getLines()[_difficulty].color); + + delete difficulties; +} + +void OnceUpon::clearIngameMenu(const Surface &background) { + if (_vm->shouldQuit()) + return; + + // Find the area encompassing the whole ingame menu + + int16 left = 0x7FFF; + int16 top = 0x7FFF; + int16 right = 0x0000; + int16 bottom = 0x0000; + + for (uint i = 0; i < ARRAYSIZE(kIngameButtons); i++) { + const MenuButton &button = kIngameButtons[i]; + + if (!button.needDraw) + continue; + + left = MIN<int16>(left , button.dstX); + top = MIN<int16>(top , button.dstY); + right = MAX<int16>(right , button.dstX + (button.srcRight - button.srcLeft + 1) - 1); + bottom = MAX<int16>(bottom, button.dstY + (button.srcBottom - button.srcTop + 1) - 1); + } + + if ((left > right) || (top > bottom)) + return; + + // Clear it line by line + drawLineByLine(background, left, top, right, bottom, left, top); +} + +OnceUpon::MenuAction OnceUpon::doIngameMenu() { + // Show the ingame menu + MenuAction action = handleIngameMenu(); + + if ((action == kMenuActionQuit) || _vm->shouldQuit()) { + + // User pressed the quit button, or quit ScummVM + _quit = true; + action = kMenuActionQuit; + + } else if (action == kMenuActionPlay) { + + // User pressed the return to game button + action = kMenuActionPlay; + + } else if (kMenuActionMainMenu) { + + // User pressed the return to main menu button + action = handleMainMenu(); + } + + return action; +} + +OnceUpon::MenuAction OnceUpon::doIngameMenu(int16 &key, MouseButtons &mouseButtons) { + if ((key != kKeyEscape) && (mouseButtons != kMouseButtonsRight)) + return kMenuActionNone; + + key = 0; + mouseButtons = kMouseButtonsNone; + + MenuAction action = doIngameMenu(); + if (action == kMenuActionPlay) + action = kMenuActionNone; + + return action; +} + +int OnceUpon::checkButton(const MenuButton *buttons, uint count, int16 x, int16 y, int failValue) const { + // Look through all buttons, and return the ID of the button we're in + + for (uint i = 0; i < count; i++) { + const MenuButton &button = buttons[i]; + + if ((x >= button.left) && (x <= button.right) && (y >= button.top) && (y <= button.bottom)) + return (int)button.id; + } + + // We're in none of these buttons, return the fail value + return failValue; +} + +void OnceUpon::drawButton(Surface &dest, const Surface &src, const MenuButton &button, int transp) const { + dest.blit(src, button.srcLeft, button.srcTop, button.srcRight, button.srcBottom, button.dstX, button.dstY, transp); +} + +void OnceUpon::drawButtons(Surface &dest, const Surface &src, const MenuButton *buttons, uint count, int transp) const { + for (uint i = 0; i < count; i++) { + const MenuButton &button = buttons[i]; + + if (!button.needDraw) + continue; + + drawButton(dest, src, button, transp); + } +} + +void OnceUpon::drawButtonBorder(const MenuButton &button, uint8 color) { + _vm->_draw->_backSurface->drawRect(button.left, button.top, button.right, button.bottom, color); + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, button.left, button.top, button.right, button.bottom); +} + +enum AnimalNamesState { + kANStateChoose, // We're in the animal chooser + kANStateNames, // We're in the language chooser + kANStateFinish // We're finished +}; + +void OnceUpon::handleAnimalNames(uint count, const MenuButton *buttons, const char * const *names) { + fadeOut(); + clearScreen(); + setGamePalette(19); + + bool cursorVisible = isCursorVisible(); + + // Set the cursor + addCursor(); + setGameCursor(); + + anSetupChooser(); + + int8 _animal = -1; + + AnimalNamesState state = kANStateChoose; + while (!_vm->shouldQuit() && (state != kANStateFinish)) { + showCursor(); + fadeIn(); + + endFrame(true); + + // Check user input + + int16 mouseX, mouseY; + MouseButtons mouseButtons; + + checkInput(mouseX, mouseY, mouseButtons); + + // If we moused over an animal button, draw a border around it + int animal = checkButton(buttons, count, mouseX, mouseY); + if ((state == kANStateChoose) && (animal != _animal)) { + // Erase the old border + if (_animal >= 0) + drawButtonBorder(buttons[_animal], 15); + + _animal = animal; + + // Draw the new border + if (_animal >= 0) + drawButtonBorder(buttons[_animal], 10); + } + + if (mouseButtons != kMouseButtonsLeft) + continue; + + playSound(kSoundClick); + + // We clicked on a language button, play the animal name + int language = checkButton(kLanguageButtons, ARRAYSIZE(kLanguageButtons), mouseX, mouseY); + if ((state == kANStateNames) && (language >= 0)) + anPlayAnimalName(names[_animal], language); + + // We clicked on an animal + if ((state == kANStateChoose) && (_animal >= 0)) { + anSetupNames(buttons[_animal]); + + state = kANStateNames; + } + + // If we clicked on the back button, go back + if (checkButton(&kAnimalNamesBack, 1, mouseX, mouseY) != -1) { + if (state == kANStateNames) { + anSetupChooser(); + + state = kANStateChoose; + } else if (state == kANStateChoose) + state = kANStateFinish; + } + } + + fadeOut(); + + // Restore the cursor + if (!cursorVisible) + hideCursor(); + removeCursor(); +} + +void OnceUpon::anSetupChooser() { + fadeOut(); + + _vm->_video->drawPackedSprite("dico.cmp", *_vm->_draw->_backSurface); + + // Draw the back button + Surface menu(320, 34, 1); + _vm->_video->drawPackedSprite("icon.cmp", menu); + drawButton(*_vm->_draw->_backSurface, menu, kAnimalNamesBack); + + // "Choose an animal" + TXTFile *choose = loadTXT(getLocFile("choisi.tx"), TXTFile::kFormatStringPosition); + choose->draw(*_vm->_draw->_backSurface, &_plettre, 1, 14); + delete choose; + + _vm->_draw->forceBlit(); +} + +void OnceUpon::anSetupNames(const MenuButton &animal) { + fadeOut(); + + Surface background(320, 200, 1); + + _vm->_video->drawPackedSprite("dico.cmp", background); + + // Draw the background and clear what we don't need + _vm->_draw->_backSurface->blit(background); + _vm->_draw->_backSurface->fillRect(19, 19, 302, 186, 15); + + // Draw the back button + Surface menu(320, 34, 1); + _vm->_video->drawPackedSprite("icon.cmp", menu); + drawButton(*_vm->_draw->_backSurface, menu, kAnimalNamesBack); + + // Draw the animal + drawButton(*_vm->_draw->_backSurface, background, animal); + + // Draw the language buttons + Surface elements(320, 200, 1); + _vm->_video->drawPackedSprite("elemenu.cmp", elements); + drawButtons(*_vm->_draw->_backSurface, elements, kLanguageButtons, ARRAYSIZE(kLanguageButtons)); + + // Draw the language names + _plettre->drawString("Fran\207ais", 43, 70, 10, 15, true, *_vm->_draw->_backSurface); + _plettre->drawString("Deutsch" , 136, 70, 10, 15, true, *_vm->_draw->_backSurface); + _plettre->drawString("English" , 238, 70, 10, 15, true, *_vm->_draw->_backSurface); + _plettre->drawString("Italiano" , 43, 128, 10, 15, true, *_vm->_draw->_backSurface); + _plettre->drawString("Espa\244ol" , 136, 128, 10, 15, true, *_vm->_draw->_backSurface); + _plettre->drawString("English" , 238, 128, 10, 15, true, *_vm->_draw->_backSurface); + + _vm->_draw->forceBlit(); +} + +void OnceUpon::anPlayAnimalName(const Common::String &animal, uint language) { + // Sound file to play + Common::String soundFile = animal + "_" + kLanguageSuffixLong[language] + ".snd"; + + // Get the name of the animal + TXTFile *names = loadTXT(animal + ".anm", TXTFile::kFormatString); + Common::String name = names->getLines()[language].text; + delete names; + + // It should be centered on the screen + const int nameX = 160 - (name.size() * _plettre->getCharWidth()) / 2; + + // Backup the screen surface + Surface backup(162, 23, 1); + backup.blit(*_vm->_draw->_backSurface, 78, 123, 239, 145, 0, 0); + + // Draw the name border + Surface nameBorder(162, 23, 1); + _vm->_video->drawPackedSprite("mot.cmp", nameBorder); + _vm->_draw->_backSurface->blit(nameBorder, 0, 0, 161, 22, 78, 123); + + // Print the animal name + _plettre->drawString(name, nameX, 129, 10, 0, true, *_vm->_draw->_backSurface); + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, 78, 123, 239, 145); + + playSoundFile(soundFile); + + // Restore the screen + _vm->_draw->_backSurface->blit(backup, 0, 0, 161, 22, 78, 123); + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, 78, 123, 239, 145); +} + +void OnceUpon::playGame() { + while (!_vm->shouldQuit() && !_quit) { + // Play a section and advance to the next section if we finished it + if (playSection()) + _section = MIN(_section + 1, kSectionCount - 1); + } + + // If we quit through the game and not through ScummVM, show the "Bye Bye" screen + if (!_vm->shouldQuit()) + showByeBye(); +} + +bool OnceUpon::playSection() { + if ((_section < 0) || (_section >= ARRAYSIZE(kSectionFuncs))) { + _quit = true; + return false; + } + + return (this->*kSectionFuncs[_section])(); +} + +const PreGob::AnimProperties OnceUpon::kSectionStorkAnimations[] = { + { 0, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, + { 1, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, + { 2, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, + { 3, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, + { 4, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, + { 5, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, + { 6, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, + { 7, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, + { 8, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, + {17, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, + {16, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, + {15, 0, ANIObject::kModeContinuous, true, false, false, 0, 0} +}; + +enum StorkState { + kStorkStateWaitUser, + kStorkStateWaitBundle, + kStorkStateFinish +}; + +bool OnceUpon::sectionStork() { + fadeOut(); + hideCursor(); + setGamePalette(0); + setGameCursor(); + + const StorkParam ¶m = getStorkParameters(); + + Surface backdrop(320, 200, 1); + + // Draw the frame + _vm->_video->drawPackedSprite("cadre.cmp", *_vm->_draw->_backSurface); + + // Draw the backdrop + _vm->_video->drawPackedSprite(param.backdrop, backdrop); + _vm->_draw->_backSurface->blit(backdrop, 0, 0, 288, 175, 16, 12); + + // "Where does the stork go?" + TXTFile *whereStork = loadTXT(getLocFile("ouva.tx"), TXTFile::kFormatStringPositionColor); + whereStork->draw(*_vm->_draw->_backSurface, &_plettre, 1); + + // Where the stork actually goes + GCTFile *thereStork = loadGCT(getLocFile("choix.gc")); + thereStork->setArea(17, 18, 303, 41); + + ANIFile ani(_vm, "present.ani", 320); + ANIList anims; + + Stork *stork = new Stork(_vm, ani); + + loadAnims(anims, ani, ARRAYSIZE(kSectionStorkAnimations), kSectionStorkAnimations); + anims.push_back(stork); + + drawAnim(anims); + + _vm->_draw->forceBlit(); + + int8 storkSoundWait = 0; + + StorkState state = kStorkStateWaitUser; + MenuAction action = kMenuActionNone; + while (!_vm->shouldQuit() && (state != kStorkStateFinish)) { + // Play the stork sound + if (--storkSoundWait == 0) + playSound(kSoundStork); + if (storkSoundWait <= 0) + storkSoundWait = 50 - _vm->_util->getRandom(30); + + // Check if the bundle landed + if ((state == kStorkStateWaitBundle) && stork->hasBundleLanded()) + state = kStorkStateFinish; + + // Check user input + + int16 mouseX, mouseY; + MouseButtons mouseButtons; + + int16 key = checkInput(mouseX, mouseY, mouseButtons); + + action = doIngameMenu(key, mouseButtons); + if (action != kMenuActionNone) { + state = kStorkStateFinish; + break; + } + + clearAnim(anims); + + if (mouseButtons == kMouseButtonsLeft) { + stopSound(); + playSound(kSoundClick); + + int house = checkButton(param.houses, param.houseCount, mouseX, mouseY); + if ((state == kStorkStateWaitUser) && (house >= 0)) { + + _house = house; + + stork->dropBundle(param.drops[house]); + state = kStorkStateWaitBundle; + + // Remove the "Where does the stork go?" text + int16 left, top, right, bottom; + if (whereStork->clear(*_vm->_draw->_backSurface, left, top, right, bottom)) + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom); + + // Print the text where the stork actually goes + thereStork->selectLine(3, house); // The house + thereStork->selectLine(4, house); // The house's inhabitants + if (thereStork->draw(*_vm->_draw->_backSurface, 2, *_plettre, 10, left, top, right, bottom)) + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom); + } + } + + drawAnim(anims); + showCursor(); + fadeIn(); + + endFrame(true); + } + + freeAnims(anims); + delete thereStork; + delete whereStork; + + fadeOut(); + hideCursor(); + + // Didn't complete the section + if (action != kMenuActionNone) + return false; + + // Move on to the character generator + + CharGenAction charGenAction = kCharGenRestart; + while (charGenAction == kCharGenRestart) + charGenAction = characterGenerator(); + + // Did we successfully create a character? + return charGenAction == kCharGenDone; +} + +const OnceUpon::MenuButton OnceUpon::kCharGenHeadButtons[] = { + {true, 106, 146, 152, 180, 0, 0, 47, 34, 106, 146, 0}, + {true, 155, 146, 201, 180, 49, 0, 96, 34, 155, 146, 1}, + {true, 204, 146, 250, 180, 98, 0, 145, 34, 204, 146, 2}, + {true, 253, 146, 299, 180, 147, 0, 194, 34, 253, 146, 3} +}; + +const OnceUpon::MenuButton OnceUpon::kCharGenHeads[] = { + {true, 0, 0, 0, 0, 29, 4, 68, 31, 40, 51, 0}, + {true, 0, 0, 0, 0, 83, 4, 113, 31, 45, 51, 1}, + {true, 0, 0, 0, 0, 132, 4, 162, 31, 45, 51, 2}, + {true, 0, 0, 0, 0, 182, 4, 211, 31, 45, 51, 3} +}; + +const OnceUpon::MenuButton OnceUpon::kCharGenHairButtons[] = { + {true, 105, 55, 124, 70, 271, 1, 289, 15, 105, 55, 0x04}, + {true, 105, 74, 124, 89, 271, 20, 289, 34, 105, 74, 0x07} +}; + +const OnceUpon::MenuButton OnceUpon::kCharGenJacketButtons[] = { + {true, 105, 90, 124, 105, 271, 39, 289, 53, 105, 90, 0x06}, + {true, 105, 109, 124, 124, 271, 58, 289, 72, 105, 109, 0x02} +}; + +const OnceUpon::MenuButton OnceUpon::kCharGenTrousersButtons[] = { + {true, 105, 140, 124, 155, 271, 77, 289, 91, 105, 140, 0x01}, + {true, 105, 159, 124, 174, 271, 96, 289, 110, 105, 159, 0x03} +}; + +const OnceUpon::MenuButton OnceUpon::kCharGenNameEntry[] = { + {true, 0, 0, 0, 0, 0, 38, 54, 48, 140, 145, 0}, + {true, 0, 0, 0, 0, 106, 38, 159, 48, 195, 145, 0}, + {true, 0, 0, 0, 0, 0, 105, 54, 121, 140, 156, 0}, + {true, 0, 0, 0, 0, 106, 105, 159, 121, 195, 156, 0} +}; + +enum CharGenState { + kCharGenStateHead = 0, // Choose a head + kCharGenStateHair , // Choose hair color + kCharGenStateJacket , // Choose jacket color + kCharGenStateTrousers , // Choose trousers color + kCharGenStateName , // Choose name + kCharGenStateSure , // "Are you sure?" + kCharGenStateStoryName , // "We're going to tell the story of $NAME" + kCharGenStateFinish // Finished +}; + +void OnceUpon::charGenSetup(uint stage) { + Surface choix(320, 200, 1), elchoix(320, 200, 1), paperDoll(65, 137, 1); + + _vm->_video->drawPackedSprite("choix.cmp" , choix); + _vm->_video->drawPackedSprite("elchoix.cmp", elchoix); + + paperDoll.blit(choix, 200, 0, 264, 136, 0, 0); + + GCTFile *text = loadGCT(getLocFile("choix.gc")); + text->setArea(17, 18, 303, 41); + text->setText(9, _name); + + // Background + _vm->_video->drawPackedSprite("cadre.cmp", *_vm->_draw->_backSurface); + _vm->_draw->_backSurface->fillRect(16, 50, 303, 187, 5); + + // Character sprite frame + _vm->_draw->_backSurface->blit(choix, 0, 38, 159, 121, 140, 54); + + // Recolor the paper doll parts + if (_colorHair != 0xFF) + elchoix.recolor(0x0C, _colorHair); + + if (_colorJacket != 0xFF) + paperDoll.recolor(0x0A, _colorJacket); + + if (_colorTrousers != 0xFF) + paperDoll.recolor(0x09, _colorTrousers); + + _vm->_draw->_backSurface->blit(paperDoll, 32, 51); + + // Paper doll head + if (_head != 0xFF) + drawButton(*_vm->_draw->_backSurface, elchoix, kCharGenHeads[_head], 0); + + if (stage == kCharGenStateHead) { + // Head buttons + drawButtons(*_vm->_draw->_backSurface, choix, kCharGenHeadButtons, ARRAYSIZE(kCharGenHeadButtons)); + + // "Choose a head" + int16 left, top, right, bottom; + text->draw(*_vm->_draw->_backSurface, 5, *_plettre, 10, left, top, right, bottom); + + } else if (stage == kCharGenStateHair) { + // Hair color buttons + drawButtons(*_vm->_draw->_backSurface, choix, kCharGenHairButtons, ARRAYSIZE(kCharGenHairButtons)); + + // "What color is the hair?" + int16 left, top, right, bottom; + text->draw(*_vm->_draw->_backSurface, 6, *_plettre, 10, left, top, right, bottom); + + } else if (stage == kCharGenStateJacket) { + // Jacket color buttons + drawButtons(*_vm->_draw->_backSurface, choix, kCharGenJacketButtons, ARRAYSIZE(kCharGenJacketButtons)); + + // "What color is the jacket?" + int16 left, top, right, bottom; + text->draw(*_vm->_draw->_backSurface, 7, *_plettre, 10, left, top, right, bottom); + + } else if (stage == kCharGenStateTrousers) { + // Trousers color buttons + drawButtons(*_vm->_draw->_backSurface, choix, kCharGenTrousersButtons, ARRAYSIZE(kCharGenTrousersButtons)); + + // "What color are the trousers?" + int16 left, top, right, bottom; + text->draw(*_vm->_draw->_backSurface, 8, *_plettre, 10, left, top, right, bottom); + + } else if (stage == kCharGenStateName) { + // Name entry field + drawButtons(*_vm->_draw->_backSurface, choix, kCharGenNameEntry, ARRAYSIZE(kCharGenNameEntry)); + + // "Enter name" + int16 left, top, right, bottom; + text->draw(*_vm->_draw->_backSurface, 10, *_plettre, 10, left, top, right, bottom); + + charGenDrawName(); + } else if (stage == kCharGenStateSure) { + // Name entry field + drawButtons(*_vm->_draw->_backSurface, choix, kCharGenNameEntry, ARRAYSIZE(kCharGenNameEntry)); + + // "Are you sure?" + TXTFile *sure = loadTXT(getLocFile("estu.tx"), TXTFile::kFormatStringPositionColor); + sure->draw(*_vm->_draw->_backSurface, &_plettre, 1); + delete sure; + + charGenDrawName(); + } else if (stage == kCharGenStateStoryName) { + + // "We're going to tell the story of $NAME" + int16 left, top, right, bottom; + text->draw(*_vm->_draw->_backSurface, 11, *_plettre, 10, left, top, right, bottom); + } + + delete text; +} + +bool OnceUpon::enterString(Common::String &name, int16 key, uint maxLength, const Font &font) { + if (key == 0) + return true; + + if (key == kKeyBackspace) { + name.deleteLastChar(); + return true; + } + + if (key == kKeySpace) + key = ' '; + + if ((key >= ' ') && (key <= 0xFF)) { + if (name.size() >= maxLength) + return false; + + if (!font.hasChar(key)) + return false; + + name += (char) key; + return true; + } + + return false; +} + +void OnceUpon::charGenDrawName() { + _vm->_draw->_backSurface->fillRect(147, 151, 243, 166, 1); + + const int16 nameY = 151 + ((166 - 151 + 1 - _plettre->getCharHeight()) / 2); + const int16 nameX = 147 + ((243 - 147 + 1 - (15 * _plettre->getCharWidth ())) / 2); + + _plettre->drawString(_name, nameX, nameY, 10, 0, true, *_vm->_draw->_backSurface); + + const int16 cursorLeft = nameX + _name.size() * _plettre->getCharWidth(); + const int16 cursorTop = nameY; + const int16 cursorRight = cursorLeft + _plettre->getCharWidth() - 1; + const int16 cursorBottom = cursorTop + _plettre->getCharHeight() - 1; + + _vm->_draw->_backSurface->fillRect(cursorLeft, cursorTop, cursorRight, cursorBottom, 10); + + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, 147, 151, 243, 166); +} + +OnceUpon::CharGenAction OnceUpon::characterGenerator() { + fadeOut(); + hideCursor(); + setGameCursor(); + + showWait(1); + + _name.clear(); + + _head = 0xFF; + _colorHair = 0xFF; + _colorJacket = 0xFF; + _colorTrousers = 0xFF; + + CharGenState state = kCharGenStateHead; + charGenSetup(state); + + ANIFile ani(_vm, "ba.ani", 320); + + ani.recolor(0x0F, 0x0C); + ani.recolor(0x0E, 0x0A); + ani.recolor(0x08, 0x09); + + CharGenChild *child = new CharGenChild(ani); + + ANIList anims; + anims.push_back(child); + + fadeOut(); + _vm->_draw->forceBlit(); + + CharGenAction action = kCharGenRestart; + while (!_vm->shouldQuit() && (state != kCharGenStateFinish)) { + // Check user input + + int16 mouseX, mouseY; + MouseButtons mouseButtons; + + int16 key = checkInput(mouseX, mouseY, mouseButtons); + + MenuAction menuAction = doIngameMenu(key, mouseButtons); + if (menuAction != kMenuActionNone) { + state = kCharGenStateFinish; + action = kCharGenAbort; + break; + } + + clearAnim(anims); + + if (state == kCharGenStateStoryName) { + if ((mouseButtons != kMouseButtonsNone) || (key != 0)) { + state = kCharGenStateFinish; + action = kCharGenDone; + break; + } + } + + if (state == kCharGenStateSure) { + // Not sure => restart + if ((key == 'N') || (key == 'n')) { // No / Nein / Non + state = kCharGenStateFinish; + action = kCharGenRestart; + break; + } + + if ((key == 'Y') || (key == 'y') || // Yes + (key == 'J') || (key == 'j') || // Ja + (key == 'S') || (key == 's') || // Si + (key == 'O') || (key == 'o')) { // Oui + + state = kCharGenStateStoryName; + charGenSetup(state); + _vm->_draw->forceBlit(); + } + } + + if (state == kCharGenStateName) { + if (enterString(_name, key, 14, *_plettre)) { + _vm->_draw->_backSurface->fillRect(147, 151, 243, 166, 1); + + const int16 nameY = 151 + ((166 - 151 + 1 - _plettre->getCharHeight()) / 2); + const int16 nameX = 147 + ((243 - 147 + 1 - (15 * _plettre->getCharWidth ())) / 2); + + _plettre->drawString(_name, nameX, nameY, 10, 0, true, *_vm->_draw->_backSurface); + + const int16 cursorLeft = nameX + _name.size() * _plettre->getCharWidth(); + const int16 cursorTop = nameY; + const int16 cursorRight = cursorLeft + _plettre->getCharWidth() - 1; + const int16 cursorBottom = cursorTop + _plettre->getCharHeight() - 1; + + _vm->_draw->_backSurface->fillRect(cursorLeft, cursorTop, cursorRight, cursorBottom, 10); + + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, 147, 151, 243, 166); + } + + if ((key == kKeyReturn) && !_name.empty()) { + _name.trim(); + _name.setChar(Util::toCP850Upper(_name[0]), 0); + + state = kCharGenStateSure; + charGenSetup(state); + _vm->_draw->forceBlit(); + } + } + + if (mouseButtons == kMouseButtonsLeft) { + stopSound(); + playSound(kSoundClick); + + int trousers = checkButton(kCharGenTrousersButtons, ARRAYSIZE(kCharGenTrousersButtons), mouseX, mouseY); + if ((state == kCharGenStateTrousers) && (trousers >= 0)) { + _colorTrousers = trousers; + + ani.recolor(0x09, _colorTrousers); + + state = kCharGenStateName; + charGenSetup(state); + _vm->_draw->forceBlit(); + } + + int jacket = checkButton(kCharGenJacketButtons, ARRAYSIZE(kCharGenJacketButtons), mouseX, mouseY); + if ((state == kCharGenStateJacket) && (jacket >= 0)) { + _colorJacket = jacket; + + ani.recolor(0x0A, _colorJacket); + + state = kCharGenStateTrousers; + charGenSetup(state); + _vm->_draw->forceBlit(); + } + + int hair = checkButton(kCharGenHairButtons, ARRAYSIZE(kCharGenHairButtons), mouseX, mouseY); + if ((state == kCharGenStateHair) && (hair >= 0)) { + _colorHair = hair; + + ani.recolor(0x0C, _colorHair); + + state = kCharGenStateJacket; + charGenSetup(state); + _vm->_draw->forceBlit(); + } + + int head = checkButton(kCharGenHeadButtons, ARRAYSIZE(kCharGenHeadButtons), mouseX, mouseY); + if ((state == kCharGenStateHead) && (head >= 0)) { + _head = head; + + state = kCharGenStateHair; + charGenSetup(state); + _vm->_draw->forceBlit(); + } + } + + drawAnim(anims); + + // Play the child sounds + CharGenChild::Sound childSound = child->shouldPlaySound(); + if (childSound == CharGenChild::kSoundWalk) { + beep(50, 10); + } else if (childSound == CharGenChild::kSoundJump) { + stopSound(); + playSound(kSoundJump); + } + + showCursor(); + fadeIn(); + + endFrame(true); + } + + fadeOut(); + hideCursor(); + + freeAnims(anims); + + if (_vm->shouldQuit()) + return kCharGenAbort; + + return action; +} + +bool OnceUpon::sectionChapter1() { + showChapter(1); + return true; +} + +bool OnceUpon::sectionParents() { + fadeOut(); + setGamePalette(14); + clearScreen(); + + const Common::String seq = ((_house == 1) || (_house == 2)) ? "parents.seq" : "parents2.seq"; + const Common::String gct = getLocFile("mefait.gc"); + + Parents parents(_vm, seq, gct, _name, _house, *_plettre, kGamePalettes[14], kGamePalettes[13], kPaletteSize); + parents.play(); + + warning("OnceUpon::sectionParents(): TODO: Item search"); + return true; +} + +bool OnceUpon::sectionChapter2() { + showChapter(2); + return true; +} + +bool OnceUpon::sectionForest0() { + warning("OnceUpon::sectionForest0(): TODO"); + return true; +} + +bool OnceUpon::sectionChapter3() { + showChapter(3); + return true; +} + +bool OnceUpon::sectionEvilCastle() { + warning("OnceUpon::sectionEvilCastle(): TODO"); + return true; +} + +bool OnceUpon::sectionChapter4() { + showChapter(4); + return true; +} + +bool OnceUpon::sectionForest1() { + warning("OnceUpon::sectionForest1(): TODO"); + return true; +} + +bool OnceUpon::sectionChapter5() { + showChapter(5); + return true; +} + +bool OnceUpon::sectionBossFight() { + warning("OnceUpon::sectionBossFight(): TODO"); + return true; +} + +bool OnceUpon::sectionChapter6() { + showChapter(6); + return true; +} + +bool OnceUpon::sectionForest2() { + warning("OnceUpon::sectionForest2(): TODO"); + return true; +} + +bool OnceUpon::sectionChapter7() { + showChapter(7); + return true; +} + +const PreGob::AnimProperties OnceUpon::kSectionEndAnimations[] = { + { 0, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, + { 6, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, + { 9, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, + {11, 0, ANIObject::kModeContinuous, true, false, false, 0, 0} +}; + +bool OnceUpon::sectionEnd() { + fadeOut(); + setGamePalette(9); + + _vm->_video->drawPackedSprite("cadre.cmp", *_vm->_draw->_backSurface); + + Surface endBackground(320, 200, 1); + _vm->_video->drawPackedSprite("fin.cmp", endBackground); + + _vm->_draw->_backSurface->blit(endBackground, 0, 0, 288, 137, 16, 50); + + GCTFile *endText = loadGCT(getLocFile("final.gc")); + endText->setArea(17, 18, 303, 41); + endText->setText(1, _name); + + ANIFile ani(_vm, "fin.ani", 320); + ANIList anims; + + loadAnims(anims, ani, ARRAYSIZE(kSectionEndAnimations), kSectionEndAnimations); + drawAnim(anims); + + _vm->_draw->forceBlit(); + + uint32 textStartTime = 0; + + MenuAction action = kMenuActionNone; + while (!_vm->shouldQuit() && (action == kMenuActionNone)) { + // Check user input + + int16 mouseX, mouseY; + MouseButtons mouseButtons; + + int16 key = checkInput(mouseX, mouseY, mouseButtons); + + action = doIngameMenu(key, mouseButtons); + if (action != kMenuActionNone) + break; + + clearAnim(anims); + + // Pressed a key or mouse button => Skip to next area-full of text + if ((mouseButtons == kMouseButtonsLeft) || (key != 0)) + textStartTime = 0; + + // Draw the next area-full of text + uint32 now = _vm->_util->getTimeKey(); + if (!endText->finished() && ((textStartTime == 0) || (now >= (textStartTime + kGCTDelay)))) { + textStartTime = now; + + int16 left, top, right, bottom; + if (endText->clear(*_vm->_draw->_backSurface, left, top, right, bottom)) + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom); + + if (endText->draw(*_vm->_draw->_backSurface, 0, *_plettre, 10, left, top, right, bottom)) + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom); + } + + drawAnim(anims); + fadeIn(); + + endFrame(true); + } + + freeAnims(anims); + delete endText; + + // Restart requested + if (action == kMenuActionRestart) + return false; + + // Last scene. Even if we didn't explicitly request a quit, the game ends here + _quit = true; + return false; +} + +} // End of namespace OnceUpon + +} // End of namespace Gob diff --git a/engines/gob/pregob/onceupon/onceupon.h b/engines/gob/pregob/onceupon/onceupon.h new file mode 100644 index 0000000000..66ef877618 --- /dev/null +++ b/engines/gob/pregob/onceupon/onceupon.h @@ -0,0 +1,344 @@ +/* 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 GOB_PREGOB_ONCEUPON_ONCEUPON_H +#define GOB_PREGOB_ONCEUPON_ONCEUPON_H + +#include "common/system.h" +#include "common/str.h" + +#include "gob/pregob/pregob.h" + +#include "gob/pregob/onceupon/stork.h" + +namespace Gob { + +class Surface; +class Font; + +class ANIObject; + +namespace OnceUpon { + +class OnceUpon : public PreGob { +public: + /** Number of languages we support. */ + static const uint kLanguageCount = 5; + + + OnceUpon(GobEngine *vm); + ~OnceUpon(); + + +protected: + /** A description of a menu button. */ + struct MenuButton { + bool needDraw; ///< Does the button need drawing? + + int16 left; ///< Left coordinate of the button. + int16 top; ///< Top coordinate of the button. + int16 right; ///< Right coordinate of the button. + int16 bottom; ///< Bottom coordinate of the button. + + int16 srcLeft; ///< Left coordinate of the button's sprite. + int16 srcTop; ///< Top coordinate of the button's sprite. + int16 srcRight; ///< Right coordinate of the button's sprite. + int16 srcBottom; ///< Right coordinate of the button's sprite. + + int16 dstX; ///< Destination X coordinate of the button's sprite. + int16 dstY; ///< Destination Y coordinate of the button's sprite. + + uint id; ///< The button's ID. + }; + + /** Parameters for the stork section. */ + struct StorkParam { + const char *backdrop; ///< Backdrop image file. + + uint houseCount; ///< Number of houses. + const MenuButton *houses; ///< House button definitions. + + const Stork::BundleDrop *drops; ///< The bundle drop parameters. + }; + + void init(); + void deinit(); + + /** Handle the copy protection. + * + * @param colors Colors the copy protection animals can be. + * @param shapes The shape that's the correct answer for each animal in each color. + * @param obfuscate Extra obfuscate table. correctShape = shapes[colors][obfuscate[animal]]. + * @return true if the user guessed the correct shape, false otherwise. + */ + bool doCopyProtection(const uint8 colors[7], const uint8 shapes[7 * 20], const uint8 obfuscate[4]); + + /** Show the intro. */ + void showIntro(); + + /** Handle the start menu. + * + * @param animalsButton Definition of the menu button that leads to the animal names screen. Can be 0. + * @param animalCount Number of animals in the animal names screen. + * @param animalButtons Definition of the buttons that make up the animals in the animal names screen. + * @param animalNames File prefixes for the name of each animal. + */ + void doStartMenu(const MenuButton *animalsButton, uint animalCount, + const MenuButton *animalButtons, const char * const *animalNames); + + /** Play the game proper. */ + void playGame(); + + + /** Return the parameters for the stork section. */ + virtual const StorkParam &getStorkParameters() const = 0; + + +private: + /** All actions a user can request in a menu. */ + enum MenuAction { + kMenuActionNone = 0, ///< No action. + kMenuActionAnimals , ///< Do the animal names. + kMenuActionPlay , ///< Play the game. + kMenuActionRestart , ///< Restart the section. + kMenuActionMainMenu, ///< Go to the main menu. + kMenuActionQuit ///< Quit the game. + }; + + /** Difficulty levels. */ + enum Difficulty { + kDifficultyBeginner = 0, + kDifficultyIntermediate = 1, + kDifficultyAdvanced = 2, + kDifficultyCount + }; + + /** The different sounds common in the game. */ + enum Sound { + kSoundClick = 0, + kSoundStork , + kSoundJump , + kSoundCount + }; + + /** Action the character generation wants us to take. */ + enum CharGenAction { + kCharGenDone = 0, ///< Created a character, move on. + kCharGenAbort , ///< Aborted the character generation. + kCharGenRestart ///< Restart the character generation. + }; + + /** A complete screen backup. */ + struct ScreenBackup { + Surface *screen; ///< Screen contents. + int palette; ///< Screen palette. + + bool changedCursor; ///< Did we change the cursor? + bool cursorVisible; ///< Was the cursor visible? + + ScreenBackup(); + ~ScreenBackup(); + }; + + + /** The number of game sections. */ + static const int kSectionCount = 15; + + static const MenuButton kMainMenuDifficultyButton[]; ///< Difficulty buttons. + static const MenuButton kSectionButtons[]; ///< Section buttons. + + static const MenuButton kIngameButtons[]; ///< Ingame menu buttons. + + static const MenuButton kAnimalNamesBack; ///< "Back" button in the animal names screens. + static const MenuButton kLanguageButtons[]; ///< Language buttons in the animal names screen. + + static const MenuButton kSectionStorkHouses[]; + + static const MenuButton kCharGenHeadButtons[]; + static const MenuButton kCharGenHeads[]; + static const MenuButton kCharGenHairButtons[]; + static const MenuButton kCharGenJacketButtons[]; + static const MenuButton kCharGenTrousersButtons[]; + static const MenuButton kCharGenNameEntry[]; + + /** All general game sounds we know about. */ + static const char *kSound[kSoundCount]; + + + static const AnimProperties kClownAnimations[]; + static const AnimProperties kTitleAnimation; + static const AnimProperties kSectionStorkAnimations[]; + static const AnimProperties kSectionEndAnimations[]; + + + /** Function pointer type for a section handler. */ + typedef bool (OnceUpon::*SectionFunc)(); + /** Section handler function. */ + static const SectionFunc kSectionFuncs[kSectionCount]; + + + /** Did we open the game archives? */ + bool _openedArchives; + + // Fonts + Font *_jeudak; + Font *_lettre; + Font *_plettre; + Font *_glettre; + + /** The current palette. */ + int _palette; + + bool _quit; ///< Did the user request a normal game quit? + + Difficulty _difficulty; ///< The current difficulty. + int _section; ///< The current game section. + + Common::String _name; ///< The name of the child. + + uint8 _house; + + uint8 _head; + uint8 _colorHair; + uint8 _colorJacket; + uint8 _colorTrousers; + + + // -- General helpers -- + + void setGamePalette(uint palette); ///< Set a game palette. + void setGameCursor(); ///< Set the default game cursor. + + /** Draw this sprite in a fancy, animated line-by-line way. */ + void drawLineByLine(const Surface &src, int16 left, int16 top, int16 right, int16 bottom, + int16 x, int16 y) const; + + /** Backup the screen contents. */ + void backupScreen(ScreenBackup &backup, bool setDefaultCursor = false); + /** Restore the screen contents with a previously made backup. */ + void restoreScreen(ScreenBackup &backup); + + Common::String fixString(const Common::String &str) const; ///< Fix a string if necessary. + void fixTXTStrings(TXTFile &txt) const; ///< Fix all strings in a TXT. + + + // -- Copy protection helpers -- + + /** Set up the copy protection. */ + int8 cpSetup(const uint8 colors[7], const uint8 shapes[7 * 20], + const uint8 obfuscate[4], const Surface sprites[2]); + /** Find the shape under these coordinates. */ + int8 cpFindShape(int16 x, int16 y) const; + /** Display the "You are wrong" screen. */ + void cpWrong(); + + + // -- Show different game screens -- + + void showWait(uint palette = 0xFFFF); ///< Show the wait / loading screen. + void showQuote(); ///< Show the quote about fairytales. + void showTitle(); ///< Show the Once Upon A Time title. + void showChapter(int chapter); ///< Show a chapter intro text. + void showByeBye(); ///< Show the "bye bye" screen. + + /** Handle the "listen to animal names" part. */ + void handleAnimalNames(uint count, const MenuButton *buttons, const char * const *names); + + + // -- Menu helpers -- + + MenuAction handleStartMenu(const MenuButton *animalsButton); ///< Handle the start menu. + MenuAction handleMainMenu(); ///< Handle the main menu. + MenuAction handleIngameMenu(); ///< Handle the ingame menu. + + void drawStartMenu(const MenuButton *animalsButton); ///< Draw the start menu. + void drawMainMenu(); ///< Draw the main menu. + void drawIngameMenu(); ///< Draw the ingame menu. + + /** Draw the difficulty label. */ + void drawMenuDifficulty(); + + /** Clear the ingame menu in an animated way. */ + void clearIngameMenu(const Surface &background); + + /** Handle the whole ingame menu. */ + MenuAction doIngameMenu(); + /** Handle the whole ingame menu if ESC or right mouse button was pressed. */ + MenuAction doIngameMenu(int16 &key, MouseButtons &mouseButtons); + + + // -- Menu button helpers -- + + /** Find the button under these coordinates. */ + int checkButton(const MenuButton *buttons, uint count, int16 x, int16 y, int failValue = -1) const; + + /** Draw a menu button. */ + void drawButton (Surface &dest, const Surface &src, const MenuButton &button, int transp = -1) const; + /** Draw multiple menu buttons. */ + void drawButtons(Surface &dest, const Surface &src, const MenuButton *buttons, uint count, int transp = -1) const; + + /** Draw a border around a button. */ + void drawButtonBorder(const MenuButton &button, uint8 color); + + + // -- Animal names helpers -- + + /** Set up the animal chooser. */ + void anSetupChooser(); + /** Set up the language chooser for one animal. */ + void anSetupNames(const MenuButton &animal); + /** Play / Display the name of an animal in one language. */ + void anPlayAnimalName(const Common::String &animal, uint language); + + + // -- Game sections -- + + bool playSection(); + + bool sectionStork(); + bool sectionChapter1(); + bool sectionParents(); + bool sectionChapter2(); + bool sectionForest0(); + bool sectionChapter3(); + bool sectionEvilCastle(); + bool sectionChapter4(); + bool sectionForest1(); + bool sectionChapter5(); + bool sectionBossFight(); + bool sectionChapter6(); + bool sectionForest2(); + bool sectionChapter7(); + bool sectionEnd(); + + CharGenAction characterGenerator(); + void charGenSetup(uint stage); + void charGenDrawName(); + + static bool enterString(Common::String &name, int16 key, uint maxLength, const Font &font); +}; + +} // End of namespace OnceUpon + +} // End of namespace Gob + +#endif // GOB_PREGOB_ONCEUPON_ONCEUPON_H diff --git a/engines/gob/pregob/onceupon/palettes.h b/engines/gob/pregob/onceupon/palettes.h new file mode 100644 index 0000000000..952581041c --- /dev/null +++ b/engines/gob/pregob/onceupon/palettes.h @@ -0,0 +1,411 @@ +/* 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 GOB_PREGOB_ONCEUPON_PALETTES_H +#define GOB_PREGOB_ONCEUPON_PALETTES_H + +static const int kPaletteSize = 16; +static const uint kPaletteCount = 20; + +static const byte kCopyProtectionPalette[3 * kPaletteSize] = { + 0x00, 0x00, 0x00, + 0x19, 0x00, 0x19, + 0x00, 0x3F, 0x00, + 0x00, 0x2A, 0x2A, + 0x2A, 0x00, 0x00, + 0x2A, 0x00, 0x2A, + 0x2A, 0x15, 0x00, + 0x00, 0x19, 0x12, + 0x00, 0x00, 0x00, + 0x15, 0x15, 0x3F, + 0x15, 0x3F, 0x15, + 0x00, 0x20, 0x3F, + 0x3F, 0x00, 0x00, + 0x3F, 0x00, 0x20, + 0x3F, 0x3F, 0x00, + 0x3F, 0x3F, 0x3F +}; + +static const byte kGamePalettes[kPaletteCount][3 * kPaletteSize] = { + { + 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, + 0x00, 0x00, 0x18, + 0x00, 0x00, 0x3C, + 0x1C, 0x28, 0x00, + 0x10, 0x18, 0x00, + 0x1C, 0x1C, 0x20, + 0x14, 0x14, 0x14, + 0x14, 0x20, 0x04, + 0x00, 0x00, 0x24, + 0x3C, 0x3C, 0x3C, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x00, 0x00, 0x24, + 0x3C, 0x3C, 0x3C, + 0x14, 0x20, 0x04, + 0x3C, 0x2C, 0x00, + 0x02, 0x00, 0x18, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00, + 0x14, 0x20, 0x04, + 0x00, 0x00, 0x24, + 0x3C, 0x3C, 0x3C, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x38, 0x20, 0x3C, + 0x2C, 0x10, 0x30, + 0x20, 0x08, 0x28, + 0x14, 0x00, 0x1C, + 0x20, 0x20, 0x38, + 0x18, 0x18, 0x2C, + 0x10, 0x10, 0x24, + 0x14, 0x20, 0x04, + 0x00, 0x00, 0x24, + 0x3C, 0x3C, 0x3C, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x3C, 0x20, 0x20, + 0x24, 0x14, 0x14, + 0x1C, 0x10, 0x10, + 0x14, 0x0C, 0x0C, + 0x1C, 0x1C, 0x1C, + 0x18, 0x18, 0x18, + 0x10, 0x10, 0x10, + 0x14, 0x20, 0x04, + 0x00, 0x00, 0x24, + 0x3C, 0x3C, 0x3C, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x10, 0x28, 0x1C, + 0x10, 0x1C, 0x10, + 0x10, 0x14, 0x0C, + 0x1C, 0x1C, 0x3C, + 0x24, 0x24, 0x3C, + 0x18, 0x18, 0x24, + 0x10, 0x10, 0x18, + 0x14, 0x20, 0x04, + 0x00, 0x00, 0x24, + 0x3C, 0x3C, 0x3C, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x3F, 0x26, 0x3F, + 0x36, 0x1C, 0x36, + 0x2C, 0x12, 0x2A, + 0x27, 0x0C, 0x24, + 0x22, 0x07, 0x1E, + 0x1D, 0x03, 0x18, + 0x16, 0x00, 0x10, + 0x14, 0x20, 0x04, + 0x00, 0x00, 0x24, + 0x3C, 0x3C, 0x3A, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x3F, 0x39, 0x26, + 0x38, 0x34, 0x1C, + 0x30, 0x2F, 0x13, + 0x27, 0x29, 0x0C, + 0x1D, 0x22, 0x07, + 0x14, 0x1B, 0x03, + 0x0C, 0x14, 0x00, + 0x14, 0x20, 0x04, + 0x00, 0x00, 0x24, + 0x3C, 0x3C, 0x3A, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x24, 0x3C, 0x3C, + 0x1C, 0x34, 0x38, + 0x14, 0x2C, 0x30, + 0x0C, 0x20, 0x2C, + 0x08, 0x18, 0x28, + 0x04, 0x10, 0x20, + 0x00, 0x08, 0x1C, + 0x14, 0x20, 0x04, + 0x00, 0x00, 0x24, + 0x3C, 0x3C, 0x38, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x24, + 0x38, 0x24, 0x1C, + 0x30, 0x1C, 0x14, + 0x28, 0x18, 0x0C, + 0x20, 0x10, 0x04, + 0x1C, 0x0C, 0x00, + 0x14, 0x08, 0x00, + 0x14, 0x20, 0x04, + 0x00, 0x00, 0x24, + 0x3C, 0x3C, 0x38, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x3C, 0x34, 0x24, + 0x38, 0x2C, 0x1C, + 0x30, 0x24, 0x14, + 0x2C, 0x1C, 0x10, + 0x30, 0x30, 0x3C, + 0x1C, 0x1C, 0x38, + 0x0C, 0x0C, 0x38, + 0x14, 0x20, 0x04, + 0x00, 0x00, 0x24, + 0x3C, 0x3C, 0x3C, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0C, + 0x02, 0x03, 0x14, + 0x07, 0x07, 0x1D, + 0x0E, 0x0E, 0x25, + 0x17, 0x17, 0x2E, + 0x21, 0x22, 0x36, + 0x2F, 0x2F, 0x3F, + 0x3F, 0x3F, 0x3F, + 0x3F, 0x3B, 0x0D, + 0x3A, 0x31, 0x0A, + 0x35, 0x28, 0x07, + 0x30, 0x21, 0x04, + 0x2B, 0x19, 0x02, + 0x26, 0x12, 0x01, + 0x16, 0x0B, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, + 0x21, 0x01, 0x00, + 0x2A, 0x02, 0x00, + 0x33, 0x03, 0x00, + 0x3D, 0x06, 0x00, + 0x2A, 0x19, 0x05, + 0x15, 0x14, 0x14, + 0x22, 0x1F, 0x1E, + 0x2F, 0x2C, 0x28, + 0x3F, 0x3C, 0x29, + 0x3F, 0x38, 0x0B, + 0x3B, 0x30, 0x0A, + 0x37, 0x29, 0x08, + 0x33, 0x23, 0x07, + 0x2F, 0x1D, 0x06 + }, + { + 0x00, 0x00, 0x00, + 0x00, 0x1C, 0x38, + 0x34, 0x30, 0x28, + 0x2C, 0x24, 0x1C, + 0x24, 0x18, 0x10, + 0x1C, 0x10, 0x08, + 0x14, 0x04, 0x04, + 0x10, 0x00, 0x00, + 0x14, 0x20, 0x04, + 0x00, 0x00, 0x24, + 0x3C, 0x3C, 0x38, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x00, 0x1C, 0x38, + 0x34, 0x30, 0x28, + 0x2C, 0x24, 0x1C, + 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, + 0x14, 0x20, 0x04, + 0x00, 0x00, 0x24, + 0x3C, 0x3C, 0x38, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x1A, 0x30, 0x37, + 0x14, 0x28, 0x31, + 0x10, 0x20, 0x2C, + 0x0C, 0x19, 0x27, + 0x08, 0x12, 0x21, + 0x05, 0x0C, 0x1C, + 0x03, 0x07, 0x16, + 0x01, 0x03, 0x11, + 0x00, 0x00, 0x0C, + 0x3C, 0x3C, 0x3C, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x34, 0x30, 0x34, + 0x30, 0x24, 0x30, + 0x28, 0x1C, 0x28, + 0x24, 0x14, 0x24, + 0x1C, 0x0C, 0x1C, + 0x18, 0x08, 0x18, + 0x14, 0x04, 0x14, + 0x0C, 0x04, 0x0C, + 0x08, 0x00, 0x08, + 0x3C, 0x3C, 0x3C, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x2C, 0x24, 0x0C, + 0x34, 0x34, 0x28, + 0x2C, 0x2C, 0x1C, + 0x24, 0x24, 0x10, + 0x1C, 0x18, 0x08, + 0x14, 0x14, 0x08, + 0x10, 0x10, 0x04, + 0x0C, 0x0C, 0x04, + 0x00, 0x00, 0x24, + 0x3C, 0x3C, 0x38, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, + 0x14, 0x28, 0x31, + 0x10, 0x20, 0x2C, + 0x0C, 0x19, 0x27, + 0x08, 0x12, 0x21, + 0x05, 0x0C, 0x1C, + 0x03, 0x07, 0x16, + 0x01, 0x03, 0x11, + 0x00, 0x3C, 0x00, + 0x3C, 0x3C, 0x3C, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x10, 0x28, 0x1C, + 0x10, 0x1C, 0x10, + 0x10, 0x14, 0x0C, + 0x1C, 0x1C, 0x3C, + 0x24, 0x24, 0x3C, + 0x18, 0x18, 0x24, + 0x10, 0x10, 0x18, + 0x14, 0x20, 0x04, + 0x00, 0x00, 0x24, + 0x3C, 0x3C, 0x3C, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x10, 0x28, 0x1C, + 0x10, 0x1C, 0x10, + 0x10, 0x14, 0x0C, + 0x1C, 0x1C, 0x3C, + 0x24, 0x24, 0x3C, + 0x18, 0x18, 0x24, + 0x10, 0x10, 0x18, + 0x14, 0x20, 0x04, + 0x00, 0x00, 0x24, + 0x3C, 0x3C, 0x3C, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + } +}; + +#endif // GOB_PREGOB_ONCEUPON_PALETTES_H diff --git a/engines/gob/pregob/onceupon/parents.cpp b/engines/gob/pregob/onceupon/parents.cpp new file mode 100644 index 0000000000..cdaee6a38d --- /dev/null +++ b/engines/gob/pregob/onceupon/parents.cpp @@ -0,0 +1,217 @@ +/* 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 "gob/gob.h" +#include "gob/global.h" +#include "gob/dataio.h" +#include "gob/palanim.h" +#include "gob/draw.h" +#include "gob/video.h" + +#include "gob/sound/sound.h" + +#include "gob/pregob/gctfile.h" + +#include "gob/pregob/onceupon/palettes.h" +#include "gob/pregob/onceupon/parents.h" + +namespace Gob { + +namespace OnceUpon { + +const char *Parents::kSound[kSoundCount] = { + "rire.snd", // kSoundCackle + "tonn.snd" // kSoundThunder +}; + +// So that every GCT line is displayed for 12 seconds +const uint16 Parents::kLoop[kLoopCount][3] = { + { 72, 77, 33}, + {105, 109, 38}, + {141, 145, 38}, + {446, 454, 23}, + {456, 464, 23}, + {466, 474, 23}, + {476, 484, 23} +}; + + +Parents::Parents(GobEngine *vm, const Common::String &seq, const Common::String &gct, + const Common::String &childName, uint8 house, const Font &font, + const byte *normalPalette, const byte *brightPalette, uint paletteSize) : + SEQFile(vm, seq), + _gct(0), _house(house), _font(&font), + _paletteSize(paletteSize), _normalPalette(normalPalette), _brightPalette(brightPalette) { + + // Load sounds + for (int i = 0; i < kSoundCount; i++) + _vm->_sound->sampleLoad(&_sounds[i], SOUND_SND, kSound[i]); + + // Load GCT + Common::SeekableReadStream *gctStream = _vm->_dataIO->getFile(gct); + if (gctStream) { + _gct = new GCTFile(*gctStream, _vm->_rnd); + + delete gctStream; + } else + error("Parents::Parents(): Failed to open \"%s\"", gct.c_str()); + + _gct->setArea(17, 18, 303, 41); + _gct->setText(1, childName); + + _gct->selectLine(2, _house); + _gct->selectLine(4, _house); + + for (uint i = 0; i < kLoopCount; i++) + _loopID[i] = addLoop(kLoop[i][0], kLoop[i][1], kLoop[i][2]); +} + +Parents::~Parents() { + delete _gct; +} + +void Parents::play() { + _currentLoop = 0; + + SEQFile::play(true, 496, 15); + + // After playback, fade out + if (!_vm->shouldQuit()) + _vm->_palAnim->fade(0, 0, 0); +} + +void Parents::handleFrameEvent() { + switch (getFrame()) { + case 0: + // On fame 0, fade in + _vm->_draw->forceBlit(); + _vm->_palAnim->fade(_vm->_global->_pPaletteDesc, 0, 0); + break; + + case 4: + drawGCT(0); + break; + + case 55: + drawGCT(3, 0); + break; + + case 79: + drawGCT(_house + 5, 1); + break; + + case 110: + drawGCT(_house + 9, 2); + break; + + case 146: + drawGCT(17); + break; + + case 198: + drawGCT(13); + break; + + case 445: + drawGCT(14, 3); + break; + + case 455: + drawGCT(18, 4); + break; + + case 465: + drawGCT(19, 5); + break; + + case 475: + drawGCT(20, 6); + break; + + case 188: + case 228: + case 237: + case 257: + case 275: + case 426: + lightningEffect(); + break; + + case 203: + case 243: + case 252: + case 272: + case 290: + case 441: + playSound(kSoundThunder); + break; + + case 340: + playSound(kSoundCackle); + break; + } +} + +void Parents::handleInput(int16 key, int16 mouseX, int16 mouseY, MouseButtons mouseButtons) { + if ((key == kKeyEscape) || (mouseButtons == kMouseButtonsRight)) + abortPlay(); + + if (((key == kKeySpace) || (mouseButtons == kMouseButtonsLeft)) && (_currentLoop < kLoopCount)) + skipLoop(_loopID[_currentLoop]); +} + +void Parents::playSound(Sound sound) { + _vm->_sound->blasterStop(0); + _vm->_sound->blasterPlay(&_sounds[sound], 0, 0); +} + +void Parents::lightningEffect() { + for (int i = 0; (i < 5) && !_vm->shouldQuit(); i++) { + + setPalette(_brightPalette, _paletteSize); + _vm->_util->delay(5); + + setPalette(_normalPalette, _paletteSize); + _vm->_util->delay(5); + } +} + +void Parents::setPalette(const byte *palette, uint size) { + memcpy(_vm->_draw->_vgaPalette, palette, 3 * size); + + _vm->_video->setFullPalette(_vm->_global->_pPaletteDesc); + _vm->_video->retrace(); +} + +void Parents::drawGCT(uint item, uint loop) { + int16 left, top, right, bottom; + if (_gct->clear(*_vm->_draw->_backSurface, left, top, right, bottom)) + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom); + if (_gct->draw(*_vm->_draw->_backSurface, item, *_font, 10, left, top, right, bottom)) + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom); + + _currentLoop = loop; +} + +} // End of namespace OnceUpon + +} // End of namespace Gob diff --git a/engines/gob/pregob/onceupon/parents.h b/engines/gob/pregob/onceupon/parents.h new file mode 100644 index 0000000000..f5c8307b73 --- /dev/null +++ b/engines/gob/pregob/onceupon/parents.h @@ -0,0 +1,94 @@ +/* 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 GOB_PREGOB_ONCEUPON_PARENTS_H +#define GOB_PREGOB_ONCEUPON_PARENTS_H + +#include "gob/sound/sounddesc.h" + +#include "gob/pregob/seqfile.h" + +namespace Gob { + +class Font; + +class GCTFile; + +namespace OnceUpon { + +/** The home / parents animation sequence. */ +class Parents : public SEQFile { +public: + Parents(GobEngine *vm, const Common::String &seq, const Common::String &gct, + const Common::String &childName, uint8 house, const Font &font, + const byte *normalPalette, const byte *brightPalette, uint paletteSize); + ~Parents(); + + void play(); + +protected: + void handleFrameEvent(); + void handleInput(int16 key, int16 mouseX, int16 mouseY, MouseButtons mouseButtons); + +private: + static const uint kLoopCount = 7; + + static const uint16 kLoop[kLoopCount][3]; + + enum Sound { + kSoundCackle = 0, + kSoundThunder , + kSoundCount + }; + + static const char *kSound[kSoundCount]; + + + uint8 _house; + + const Font *_font; + + uint _paletteSize; + const byte *_normalPalette; + const byte *_brightPalette; + + SoundDesc _sounds[kSoundCount]; + + GCTFile *_gct; + + uint _loopID[kLoopCount]; + uint _currentLoop; + + + void lightningEffect(); + + void playSound(Sound sound); + void setPalette(const byte *palette, uint size); + + void drawGCT(uint item, uint loop = 0xFFFF); +}; + +} // End of namespace OnceUpon + +} // End of namespace Gob + +#endif // GOB_PREGOB_ONCEUPON_PARENTS_H diff --git a/engines/gob/pregob/onceupon/stork.cpp b/engines/gob/pregob/onceupon/stork.cpp new file mode 100644 index 0000000000..3c38037d08 --- /dev/null +++ b/engines/gob/pregob/onceupon/stork.cpp @@ -0,0 +1,234 @@ +/* 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 "common/str.h" + +#include "gob/gob.h" +#include "gob/surface.h" +#include "gob/anifile.h" +#include "gob/video.h" + +#include "gob/pregob/onceupon/stork.h" + +enum Animation { + kAnimFlyNearWithBundle = 9, + kAnimFlyFarWithBundle = 12, + kAnimFlyNearWithoutBundle = 10, + kAnimFlyFarWithoutBundle = 13, + kAnimBundleNear = 11, + kAnimBundleFar = 14 +}; + +namespace Gob { + +namespace OnceUpon { + +Stork::Stork(GobEngine *vm, const ANIFile &ani) : ANIObject(ani), _shouldDrop(false) { + _frame = new Surface(320, 200, 1); + vm->_video->drawPackedSprite("cadre.cmp", *_frame); + + _bundle = new ANIObject(ani); + + _bundle->setVisible(false); + _bundle->setPause(true); + + setState(kStateFlyNearWithBundle, kAnimFlyNearWithBundle, -80); +} + +Stork::~Stork() { + delete _frame; + + delete _bundle; +} + +bool Stork::hasBundleLanded() const { + if (!_shouldDrop || !_bundle->isVisible() || _bundle->isPaused()) + return false; + + int16 x, y, width, height; + _bundle->getFramePosition(x, y); + _bundle->getFrameSize(width, height); + + return (y + height) >= _bundleDrop.landY; +} + +void Stork::dropBundle(const BundleDrop &drop) { + if (_shouldDrop) + return; + + _shouldDrop = true; + _bundleDrop = drop; +} + +bool Stork::draw(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom) { + left = 0x7FFF; + top = 0x7FFF; + right = 0x0000; + bottom = 0x0000; + + bool drawn = ANIObject::draw(dest, left, top, right, bottom); + if (drawn) { + // Left frame edge + if (left <= 15) + dest.blit(*_frame, left, top, MIN<int16>(15, right), bottom, left, top); + + // Right frame edge + if (right >= 304) + dest.blit(*_frame, MAX<int16>(304, left), top, right, bottom, MAX<int16>(304, left), top); + } + + int16 bLeft, bTop, bRight, bBottom; + if (_bundle->draw(dest, bLeft, bTop, bRight, bBottom)) { + // Bottom frame edge + if (bBottom >= 188) + dest.blit(*_frame, bLeft, MAX<int16>(188, bTop), bRight, bBottom, bLeft, MAX<int16>(188, bTop)); + + left = MIN(left , bLeft ); + top = MIN(top , bTop ); + right = MAX(right , bRight ); + bottom = MAX(bottom, bBottom); + + drawn = true; + } + + return drawn; +} + +bool Stork::clear(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom) { + left = 0x7FFF; + top = 0x7FFF; + right = 0x0000; + bottom = 0x0000; + + bool cleared = _bundle->clear(dest, left, top, right, bottom); + + int16 sLeft, sTop, sRight, sBottom; + if (ANIObject::clear(dest, sLeft, sTop, sRight, sBottom)) { + left = MIN(left , sLeft ); + top = MIN(top , sTop ); + right = MAX(right , sRight ); + bottom = MAX(bottom, sBottom); + + cleared = true; + } + + return cleared; +} + +void Stork::advance() { + _bundle->advance(); + + ANIObject::advance(); + + int16 curX, curY, curWidth, curHeight; + getFramePosition(curX, curY, 0); + getFrameSize(curWidth, curHeight, 0); + + const int16 curRight = curX + curWidth - 1; + + int16 nextX, nextY, nextWidth, nextHeight; + getFramePosition(nextX, nextY, 1); + getFrameSize(nextWidth, nextHeight, 1); + + const int16 nextRight = nextX + nextWidth - 1; + + switch (_state) { + case kStateFlyNearWithBundle: + if (curX >= 330) + setState(kStateFlyFarWithBundle, kAnimFlyFarWithBundle, 330); + + if ((curRight <= _bundleDrop.dropX) && + (nextRight >= _bundleDrop.dropX) && _shouldDrop && !_bundleDrop.dropWhileFar) + dropBundle(kStateFlyNearWithoutBundle, kAnimFlyNearWithoutBundle); + + break; + + case kStateFlyFarWithBundle: + if (curX <= -80) + setState(kStateFlyNearWithBundle, kAnimFlyNearWithBundle, -80); + + if ((curX >= _bundleDrop.dropX) && + (nextX <= _bundleDrop.dropX) && _shouldDrop && _bundleDrop.dropWhileFar) + dropBundle(kStateFlyFarWithoutBundle, kAnimFlyFarWithoutBundle); + + break; + + case kStateFlyNearWithoutBundle: + if (curX >= 330) + setState(kStateFlyFarWithoutBundle, kAnimFlyFarWithoutBundle, 330); + break; + + case kStateFlyFarWithoutBundle: + if (curX <= -80) + setState(kStateFlyNearWithoutBundle, kAnimFlyNearWithoutBundle, -80); + break; + + default: + break; + } +} + +void Stork::dropBundle(State state, uint16 anim) { + setState(state, anim); + + int16 x, y, width, height; + getFramePosition(x, y); + getFrameSize(width, height); + + _bundle->setAnimation(_bundleDrop.anim); + _bundle->setPause(false); + _bundle->setVisible(true); + + int16 bWidth, bHeight; + _bundle->getFrameSize(bWidth, bHeight); + + // Drop position + x = _bundleDrop.dropX; + y = y + height - bHeight; + + // If the stork is flying near (from left to right), drop the bundle at the right edge + if (!_bundleDrop.dropWhileFar) + x = x - bWidth; + + _bundle->setPosition(x, y); +} + +void Stork::setState(State state, uint16 anim) { + setAnimation(anim); + setVisible(true); + setPause(false); + + _state = state; +} + +void Stork::setState(State state, uint16 anim, int16 x) { + setState(state, anim); + setPosition(); + + int16 pX, pY; + getPosition(pX, pY); + setPosition( x, pY); +} + +} // End of namespace OnceUpon + +} // End of namespace Gob diff --git a/engines/gob/pregob/onceupon/stork.h b/engines/gob/pregob/onceupon/stork.h new file mode 100644 index 0000000000..756f5258c7 --- /dev/null +++ b/engines/gob/pregob/onceupon/stork.h @@ -0,0 +1,103 @@ +/* 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 GOB_PREGOB_ONCEUPON_STORK_H +#define GOB_PREGOB_ONCEUPON_STORK_H + +#include "common/system.h" + +#include "gob/aniobject.h" + +namespace Common { + class String; +} + +namespace Gob { + +class GobEngine; + +class Surface; +class ANIFile; + +namespace OnceUpon { + +/** The stork in Baba Yaga / dragon in Abracadabra. */ +class Stork : public ANIObject { +public: + /** Information on how to drop the bundle. */ + struct BundleDrop { + int16 anim; ///< Animation of the bundle floating down + + int16 dropX; ///< X position the stork drops the bundle + int16 landY; ///< Y position the bundle lands + + bool dropWhileFar; ///< Does the stork drop the bundle while far instead of near? + }; + + Stork(GobEngine *vm, const ANIFile &ani); + ~Stork(); + + /** Has the bundle landed? */ + bool hasBundleLanded() const; + + /** Drop the bundle. */ + void dropBundle(const BundleDrop &drop); + + /** Draw the current frame onto the surface and return the affected rectangle. */ + bool draw(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom); + /** Draw the current frame from the surface and return the affected rectangle. */ + bool clear(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom); + + /** Advance the animation to the next frame. */ + void advance(); + +private: + enum State { + kStateFlyNearWithBundle = 0, + kStateFlyFarWithBundle , + kStateFlyNearWithoutBundle , + kStateFlyFarWithoutBundle + }; + + + GobEngine *_vm; + + Surface *_frame; + ANIObject *_bundle; + + State _state; + + bool _shouldDrop; + BundleDrop _bundleDrop; + + + void setState(State state, uint16 anim); + void setState(State state, uint16 anim, int16 x); + + void dropBundle(State state, uint16 anim); +}; + +} // End of namespace OnceUpon + +} // End of namespace Gob + +#endif // GOB_PREGOB_ONCEUPON_STORK_H diff --git a/engines/gob/pregob/onceupon/title.cpp b/engines/gob/pregob/onceupon/title.cpp new file mode 100644 index 0000000000..5163ff6822 --- /dev/null +++ b/engines/gob/pregob/onceupon/title.cpp @@ -0,0 +1,117 @@ +/* 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 "gob/gob.h" +#include "gob/global.h" +#include "gob/palanim.h" +#include "gob/draw.h" + +#include "gob/sound/sound.h" + +#include "gob/pregob/onceupon/title.h" + +namespace Gob { + +namespace OnceUpon { + +Title::Title(GobEngine *vm) : SEQFile(vm, "ville.seq") { +} + +Title::~Title() { +} + +void Title::play() { + SEQFile::play(true, 0xFFFF, 15); + + // After playback, fade out and stop the music + if (!_vm->shouldQuit()) + _vm->_palAnim->fade(0, 0, 0); + + stopMusic(); +} + +void Title::handleFrameEvent() { + // On fame 0, start the music and fade in + if (getFrame() == 0) { + playMusic(); + + _vm->_draw->forceBlit(); + _vm->_palAnim->fade(_vm->_global->_pPaletteDesc, 0, 0); + } +} + +void Title::playMusic() { + // Look at what platform this is and play the appropriate music type + + if (_vm->getPlatform() == Common::kPlatformPC) + playMusicDOS(); + else if (_vm->getPlatform() == Common::kPlatformAmiga) + playMusicAmiga(); + else if (_vm->getPlatform() == Common::kPlatformAtariST) + playMusicAtariST(); +} + +void Title::playMusicDOS() { + // Play an AdLib track + + _vm->_sound->adlibLoadTBR("babayaga.tbr"); + _vm->_sound->adlibLoadMDY("babayaga.mdy"); + _vm->_sound->adlibSetRepeating(-1); + _vm->_sound->adlibPlay(); +} + +void Title::playMusicAmiga() { + // Play a Protracker track + + _vm->_sound->protrackerPlay("mod.babayaga"); +} + +void Title::playMusicAtariST() { + // Play a Soundblaster composition + + static const int16 titleMusic[21] = { 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 2, 0, 1, 0, 0, 0, 0, 0, -1}; + static const char * const titleFiles[ 3] = {"baba1.snd", "baba2.snd", "baba3.snd"}; + + for (uint i = 0; i < ARRAYSIZE(titleFiles); i++) + _vm->_sound->sampleLoad(_vm->_sound->sampleGetBySlot(i), SOUND_SND, titleFiles[i]); + + _vm->_sound->blasterPlayComposition(titleMusic, 0); + _vm->_sound->blasterRepeatComposition(-1); +} + +void Title::stopMusic() { + // Just stop everything + + _vm->_sound->adlibSetRepeating(0); + _vm->_sound->blasterRepeatComposition(0); + + _vm->_sound->adlibStop(); + _vm->_sound->blasterStopComposition(); + _vm->_sound->protrackerStop(); + + for (int i = 0; i < ::Gob::Sound::kSoundsCount; i++) + _vm->_sound->sampleFree(_vm->_sound->sampleGetBySlot(i)); +} + +} // End of namespace OnceUpon + +} // End of namespace Gob diff --git a/engines/gob/pregob/onceupon/title.h b/engines/gob/pregob/onceupon/title.h new file mode 100644 index 0000000000..5e7ef76d40 --- /dev/null +++ b/engines/gob/pregob/onceupon/title.h @@ -0,0 +1,55 @@ +/* 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 GOB_PREGOB_ONCEUPON_TITLE_H +#define GOB_PREGOB_ONCEUPON_TITLE_H + +#include "gob/pregob/seqfile.h" + +namespace Gob { + +namespace OnceUpon { + +/** The Once Upon A Time title animation sequence. */ +class Title : public SEQFile { +public: + Title(GobEngine *vm); + ~Title(); + + void play(); + +protected: + void handleFrameEvent(); + +private: + void playMusic(); ///< Play the title music. + void playMusicDOS(); ///< Play the title music of the DOS version. + void playMusicAmiga(); ///< Play the title music of the Amiga version. + void playMusicAtariST(); ///< Play the title music of the Atari ST version. + void stopMusic(); ///< Stop the title music. +}; + +} // End of namespace OnceUpon + +} // End of namespace Gob + +#endif // GOB_PREGOB_ONCEUPON_TITLE_H diff --git a/engines/gob/pregob/pregob.cpp b/engines/gob/pregob/pregob.cpp new file mode 100644 index 0000000000..54eb3c6795 --- /dev/null +++ b/engines/gob/pregob/pregob.cpp @@ -0,0 +1,351 @@ +/* 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 "graphics/cursorman.h" + +#include "gob/gob.h" +#include "gob/global.h" +#include "gob/util.h" +#include "gob/surface.h" +#include "gob/dataio.h" +#include "gob/palanim.h" +#include "gob/draw.h" +#include "gob/video.h" +#include "gob/aniobject.h" + +#include "gob/sound/sound.h" + +#include "gob/pregob/pregob.h" +#include "gob/pregob/gctfile.h" + + +namespace Gob { + +const char PreGob::kLanguageSuffixShort[5] = { 't', 'g', 'a', 'e', 'i'}; +const char *PreGob::kLanguageSuffixLong [5] = {"fr", "al", "an", "it", "es"}; + + +PreGob::PreGob(GobEngine *vm) : _vm(vm), _fadedOut(false) { +} + +PreGob::~PreGob() { +} + +void PreGob::fadeOut() { + if (_fadedOut || _vm->shouldQuit()) + return; + + // Fade to black + _vm->_palAnim->fade(0, 0, 0); + + _fadedOut = true; +} + +void PreGob::fadeIn() { + if (!_fadedOut || _vm->shouldQuit()) + return; + + // Fade to palette + _vm->_draw->blitInvalidated(); + _vm->_palAnim->fade(_vm->_global->_pPaletteDesc, 0, 0); + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, 0, 0, 319, 199); + + _fadedOut = false; +} + +void PreGob::clearScreen() { + _vm->_draw->_backSurface->clear(); + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, 0, 0, 319, 199); + _vm->_draw->blitInvalidated(); + _vm->_video->retrace(); +} + +void PreGob::initScreen() { + _vm->_util->setFrameRate(15); + + _fadedOut = true; + + _vm->_draw->initScreen(); + + _vm->_draw->_backSurface->clear(); + _vm->_util->clearPalette(); + + _vm->_draw->forceBlit(); + _vm->_video->retrace(); + + _vm->_util->processInput(); +} + +void PreGob::setPalette(const byte *palette, uint16 size) { + memcpy(_vm->_draw->_vgaPalette, palette, 3 * size); + + // If we didn't fade out prior, immediately set the palette + if (!_fadedOut) + _vm->_video->setFullPalette(_vm->_global->_pPaletteDesc); +} + +void PreGob::addCursor() { + CursorMan.pushCursor(0, 0, 0, 0, 0, 0); +} + +void PreGob::removeCursor() { + CursorMan.popCursor(); +} + +void PreGob::setCursor(Surface &sprite, int16 hotspotX, int16 hotspotY) { + CursorMan.replaceCursor(sprite.getData(), sprite.getWidth(), sprite.getHeight(), hotspotX, hotspotY, 0); +} + +void PreGob::setCursor(Surface &sprite, int16 left, int16 top, int16 right, int16 bottom, + int16 hotspotX, int16 hotspotY) { + + const int width = right - left + 1; + const int height = bottom - top + 1; + + if ((width <= 0) || (height <= 0)) + return; + + Surface cursor(width, height, 1); + + cursor.blit(sprite, left, top, right, bottom, 0, 0); + + setCursor(cursor, hotspotX, hotspotX); +} + +void PreGob::showCursor() { + CursorMan.showMouse(true); + + _vm->_draw->_showCursor = 4; +} + +void PreGob::hideCursor() { + CursorMan.showMouse(false); + + _vm->_draw->_showCursor = 0; +} + +bool PreGob::isCursorVisible() const { + return CursorMan.isVisible(); +} + +void PreGob::loadSounds(const char * const *sounds, uint soundCount) { + freeSounds(); + + _sounds.resize(soundCount); + + for (uint i = 0; i < soundCount; i++) + loadSound(_sounds[i], sounds[i]); +} + +void PreGob::freeSounds() { + _sounds.clear(); +} + +bool PreGob::loadSound(SoundDesc &sound, const Common::String &file) const { + return _vm->_sound->sampleLoad(&sound, SOUND_SND, file.c_str()); +} + +void PreGob::playSound(uint sound, int16 frequency, int16 repCount) { + if (sound >= _sounds.size()) + return; + + _vm->_sound->blasterPlay(&_sounds[sound], repCount, frequency); +} + +void PreGob::stopSound() { + _vm->_sound->blasterStop(0); +} + +void PreGob::playSoundFile(const Common::String &file, int16 frequency, int16 repCount, bool interruptible) { + stopSound(); + + SoundDesc sound; + if (!loadSound(sound, file)) + return; + + _vm->_sound->blasterPlay(&sound, repCount, frequency); + + _vm->_util->forceMouseUp(); + + bool finished = false; + while (!_vm->shouldQuit() && !finished && _vm->_sound->blasterPlayingSound()) { + endFrame(true); + + finished = hasInput(); + } + + _vm->_util->forceMouseUp(); + + stopSound(); +} + +void PreGob::beep(int16 frequency, int32 length) { + _vm->_sound->speakerOn(frequency, length); +} + +void PreGob::endFrame(bool doInput) { + _vm->_draw->blitInvalidated(); + _vm->_util->waitEndFrame(); + + if (doInput) + _vm->_util->processInput(); +} + +int16 PreGob::checkInput(int16 &mouseX, int16 &mouseY, MouseButtons &mouseButtons) { + _vm->_util->getMouseState(&mouseX, &mouseY, &mouseButtons); + _vm->_util->forceMouseUp(); + + return _vm->_util->checkKey(); +} + +int16 PreGob::waitInput(int16 &mouseX, int16 &mouseY, MouseButtons &mouseButtons) { + bool finished = false; + + int16 key = 0; + while (!_vm->shouldQuit() && !finished) { + endFrame(true); + + key = checkInput(mouseX, mouseY, mouseButtons); + + finished = (mouseButtons != kMouseButtonsNone) || (key != 0); + } + + return key; +} + +int16 PreGob::waitInput() { + int16 mouseX, mouseY; + MouseButtons mouseButtons; + + return waitInput(mouseX, mouseY, mouseButtons); +} + +bool PreGob::hasInput() { + int16 mouseX, mouseY; + MouseButtons mouseButtons; + + return checkInput(mouseX, mouseY, mouseButtons) || (mouseButtons != kMouseButtonsNone); +} + +void PreGob::clearAnim(ANIObject &anim) { + int16 left, top, right, bottom; + + if (anim.clear(*_vm->_draw->_backSurface, left, top, right, bottom)) + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom); +} + +void PreGob::drawAnim(ANIObject &anim) { + int16 left, top, right, bottom; + + if (anim.draw(*_vm->_draw->_backSurface, left, top, right, bottom)) + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom); + anim.advance(); +} + +void PreGob::redrawAnim(ANIObject &anim) { + clearAnim(anim); + drawAnim(anim); +} + +void PreGob::clearAnim(const ANIList &anims) { + for (int i = (anims.size() - 1); i >= 0; i--) + clearAnim(*anims[i]); +} + +void PreGob::drawAnim(const ANIList &anims) { + for (ANIList::const_iterator a = anims.begin(); a != anims.end(); ++a) + drawAnim(**a); +} + +void PreGob::redrawAnim(const ANIList &anims) { + clearAnim(anims); + drawAnim(anims); +} + +void PreGob::loadAnims(ANIList &anims, ANIFile &ani, uint count, const AnimProperties *props) const { + freeAnims(anims); + + anims.resize(count); + for (uint i = 0; i < count; i++) { + anims[i] = new ANIObject(ani); + + setAnim(*anims[i], props[i]); + } +} + +void PreGob::freeAnims(ANIList &anims) const { + for (ANIList::iterator a = anims.begin(); a != anims.end(); ++a) + delete *a; + + anims.clear(); +} + +void PreGob::setAnim(ANIObject &anim, const AnimProperties &props) const { + anim.setAnimation(props.animation); + anim.setFrame(props.frame); + anim.setMode(props.mode); + anim.setPause(props.paused); + anim.setVisible(props.visible); + + if (props.hasPosition) + anim.setPosition(props.x, props.y); + else + anim.setPosition(); +} + +Common::String PreGob::getLocFile(const Common::String &file) const { + if (_vm->_global->_language >= ARRAYSIZE(kLanguageSuffixShort)) + return file; + + return file + kLanguageSuffixShort[_vm->_global->_language]; +} + +TXTFile *PreGob::loadTXT(const Common::String &txtFile, TXTFile::Format format) const { + Common::SeekableReadStream *txtStream = _vm->_dataIO->getFile(txtFile); + if (!txtStream) + error("PreGob::loadTXT(): Failed to open \"%s\"", txtFile.c_str()); + + TXTFile *txt = new TXTFile(*txtStream, format); + + delete txtStream; + + fixTXTStrings(*txt); + + return txt; +} + +void PreGob::fixTXTStrings(TXTFile &txt) const { +} + +GCTFile *PreGob::loadGCT(const Common::String &gctFile) const { + Common::SeekableReadStream *gctStream = _vm->_dataIO->getFile(gctFile); + if (!gctStream) + error("PreGob::loadGCT(): Failed to open \"%s\"", gctFile.c_str()); + + GCTFile *gct = new GCTFile(*gctStream, _vm->_rnd); + + delete gctStream; + + return gct; +} + +} // End of namespace Gob diff --git a/engines/gob/pregob/pregob.h b/engines/gob/pregob/pregob.h new file mode 100644 index 0000000000..632f85b88e --- /dev/null +++ b/engines/gob/pregob/pregob.h @@ -0,0 +1,194 @@ +/* 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 GOB_PREGOB_PREGOB_H +#define GOB_PREGOB_PREGOB_H + +#include "common/str.h" +#include "common/array.h" + +#include "gob/util.h" +#include "gob/aniobject.h" + +#include "gob/sound/sounddesc.h" + +#include "gob/pregob/txtfile.h" + +namespace Gob { + +class GobEngine; +class Surface; + +class GCTFile; + +class PreGob { +public: + PreGob(GobEngine *vm); + virtual ~PreGob(); + + virtual void run() = 0; + + struct AnimProperties { + uint16 animation; + uint16 frame; + + ANIObject::Mode mode; + + bool visible; + bool paused; + + bool hasPosition; + int16 x; + int16 y; + }; + +protected: + typedef Common::Array<ANIObject *> ANIList; + + static const char kLanguageSuffixShort[5]; + static const char *kLanguageSuffixLong [5]; + + + GobEngine *_vm; + + + // -- Graphics -- + + /** Initialize the game screen. */ + void initScreen(); + + void fadeOut(); ///< Fade to black. + void fadeIn(); ///< Fade to the current palette. + + void clearScreen(); + + /** Change the palette. + * + * @param palette The palette to change to. + * @param size Size of the palette in colors. + */ + void setPalette(const byte *palette, uint16 size); ///< Change the palette + + /** Add a new cursor that can be manipulated to the stack. */ + void addCursor(); + /** Remove the top-most cursor from the stack. */ + void removeCursor(); + + /** Set the current cursor. */ + void setCursor(Surface &sprite, int16 hotspotX, int16 hotspotY); + /** Set the current cursor. */ + void setCursor(Surface &sprite, int16 left, int16 top, int16 right, int16 bottom, + int16 hotspotX, int16 hotspotY); + + /** Show the cursor. */ + void showCursor(); + /** Hide the cursor. */ + void hideCursor(); + + /** Is the cursor currently visible? */ + bool isCursorVisible() const; + + /** Remove an animation from the screen. */ + void clearAnim(ANIObject &anim); + /** Draw an animation to the screen, advancing it. */ + void drawAnim(ANIObject &anim); + /** Clear and draw an animation to the screen, advancing it. */ + void redrawAnim(ANIObject &anim); + + /** Remove animations from the screen. */ + void clearAnim(const ANIList &anims); + /** Draw animations to the screen, advancing them. */ + void drawAnim(const ANIList &anims); + /** Clear and draw animations to the screen, advancing them. */ + void redrawAnim(const ANIList &anims); + + void loadAnims(ANIList &anims, ANIFile &ani, uint count, const AnimProperties *props) const; + void freeAnims(ANIList &anims) const; + + void setAnim(ANIObject &anim, const AnimProperties &props) const; + + /** Wait for the frame to end, handling screen updates and optionally update input. */ + void endFrame(bool doInput); + + + // -- Sound -- + + /** Load all sounds that can be played interactively in the game. */ + void loadSounds(const char * const *sounds, uint soundCount); + /** Free all loaded sound. */ + void freeSounds(); + + /** Play a loaded sound. */ + void playSound(uint sound, int16 frequency = 0, int16 repCount = 0); + /** Stop all sound playback. */ + void stopSound(); + + /** Play a sound until it ends or is interrupted by a keypress. */ + void playSoundFile(const Common::String &file, int16 frequency = 0, int16 repCount = 0, bool interruptible = true); + + /** Beep the PC speaker. */ + void beep(int16 frequency, int32 length); + + + // -- Input -- + + /** Check mouse and keyboard input. */ + int16 checkInput(int16 &mouseX, int16 &mouseY, MouseButtons &mouseButtons); + /** Wait for mouse or keyboard input. */ + int16 waitInput (int16 &mouseX, int16 &mouseY, MouseButtons &mouseButtons); + /** Wait for mouse or keyboard input, but don't care about what was done with the mouse. */ + int16 waitInput(); + /** Did we have mouse or keyboard input? */ + bool hasInput(); + + + // -- TXT helpers -- + + /** Get the name of a localized file. */ + Common::String getLocFile(const Common::String &file) const; + /** Open a TXT file. */ + TXTFile *loadTXT(const Common::String &txtFile, TXTFile::Format format) const; + + /** Called by loadTXT() to fix strings within the TXT file. */ + virtual void fixTXTStrings(TXTFile &txt) const; + + + // -- GCT helpers -- + + GCTFile *loadGCT(const Common::String &gctFile) const; + + +private: + /** Did we fade out? */ + bool _fadedOut; + + /** All loaded sounds. */ + Common::Array<SoundDesc> _sounds; + + + /** Load a sound file. */ + bool loadSound(SoundDesc &sound, const Common::String &file) const; +}; + +} // End of namespace Gob + +#endif // GOB_PREGOB_PREGOB_H diff --git a/engines/gob/pregob/seqfile.cpp b/engines/gob/pregob/seqfile.cpp new file mode 100644 index 0000000000..91973bbb85 --- /dev/null +++ b/engines/gob/pregob/seqfile.cpp @@ -0,0 +1,384 @@ +/* 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 "common/str.h" +#include "common/stream.h" + +#include "gob/gob.h" +#include "gob/dataio.h" +#include "gob/draw.h" +#include "gob/decfile.h" +#include "gob/anifile.h" +#include "gob/aniobject.h" + +#include "gob/pregob/seqfile.h" + +namespace Gob { + +SEQFile::SEQFile(GobEngine *vm, const Common::String &fileName) : _vm(vm) { + for (uint i = 0; i < kObjectCount; i++) + _objects[i].object = 0; + + Common::SeekableReadStream *seq = _vm->_dataIO->getFile(Util::setExtension(fileName, ".SEQ")); + if (!seq) { + warning("SEQFile::SEQFile(): No such file \"%s\"", fileName.c_str()); + return; + } + + load(*seq); + + delete seq; +} + +SEQFile::~SEQFile() { + for (uint i = 0; i < kObjectCount; i++) + delete _objects[i].object; + + for (Backgrounds::iterator b = _backgrounds.begin(); b != _backgrounds.end(); ++b) + delete *b; + + for (Animations::iterator a = _animations.begin(); a != _animations.end(); ++a) + delete *a; +} + +void SEQFile::load(Common::SeekableReadStream &seq) { + const uint16 decCount = (uint16)seq.readByte() + 1; + const uint16 aniCount = (uint16)seq.readByte() + 1; + + // Load backgrounds + _backgrounds.reserve(decCount); + for (uint i = 0; i < decCount; i++) { + const Common::String dec = Util::readString(seq, 13); + + if (!_vm->_dataIO->hasFile(dec)) { + warning("SEQFile::load(): No such background \"%s\"", dec.c_str()); + return; + } + + _backgrounds.push_back(new DECFile(_vm, dec, 320, 200)); + } + + // Load animations + _animations.reserve(aniCount); + for (uint i = 0; i < aniCount; i++) { + const Common::String ani = Util::readString(seq, 13); + + if (!_vm->_dataIO->hasFile(ani)) { + warning("SEQFile::load(): No such animation \"%s\"", ani.c_str()); + return; + } + + _animations.push_back(new ANIFile(_vm, ani)); + } + + _frameRate = seq.readUint16LE(); + + // Load background change keys + + const uint16 bgKeyCount = seq.readUint16LE(); + _bgKeys.resize(bgKeyCount); + + for (uint16 i = 0; i < bgKeyCount; i++) { + const uint16 frame = seq.readUint16LE(); + const uint16 index = seq.readUint16LE(); + + _bgKeys[i].frame = frame; + _bgKeys[i].background = index < _backgrounds.size() ? _backgrounds[index] : 0; + } + + // Load animation keys for all 4 objects + + for (uint i = 0; i < kObjectCount; i++) { + const uint16 animKeyCount = seq.readUint16LE(); + _animKeys.reserve(_animKeys.size() + animKeyCount); + + for (uint16 j = 0; j < animKeyCount; j++) { + _animKeys.push_back(AnimationKey()); + + const uint16 frame = seq.readUint16LE(); + const uint16 index = seq.readUint16LE(); + + uint16 animation; + const ANIFile *ani = findANI(index, animation); + + _animKeys.back().object = i; + _animKeys.back().frame = frame; + _animKeys.back().ani = ani; + _animKeys.back().animation = animation; + _animKeys.back().x = seq.readSint16LE(); + _animKeys.back().y = seq.readSint16LE(); + _animKeys.back().order = seq.readSint16LE(); + } + } + +} + +const ANIFile *SEQFile::findANI(uint16 index, uint16 &animation) { + animation = 0xFFFF; + + // 0xFFFF = remove animation + if (index == 0xFFFF) + return 0; + + for (Animations::const_iterator a = _animations.begin(); a != _animations.end(); ++a) { + if (index < (*a)->getAnimationCount()) { + animation = index; + return *a; + } + + index -= (*a)->getAnimationCount(); + } + + return 0; +} + +void SEQFile::play(bool abortable, uint16 endFrame, uint16 frameRate) { + if (_bgKeys.empty() && _animKeys.empty()) + // Nothing to do + return; + + // Init + + _frame = 0; + _abortPlay = false; + + for (uint i = 0; i < kObjectCount; i++) { + delete _objects[i].object; + + _objects[i].object = 0; + _objects[i].order = 0; + } + + for (Loops::iterator l = _loops.begin(); l != _loops.end(); ++l) + l->currentLoop = 0; + + // Set the frame rate + + int16 frameRateBack = _vm->_util->getFrameRate(); + + if (frameRate == 0) + frameRate = _frameRate; + + _vm->_util->setFrameRate(frameRate); + + _abortable = abortable; + + while (!_vm->shouldQuit() && !_abortPlay) { + // Handle the frame contents + playFrame(); + + // Handle extra frame events + handleFrameEvent(); + + // Wait for the frame to end + _vm->_draw->blitInvalidated(); + _vm->_util->waitEndFrame(); + + // Handle input + + _vm->_util->processInput(); + + int16 key = _vm->_util->checkKey(); + + int16 mouseX, mouseY; + MouseButtons mouseButtons; + _vm->_util->getMouseState(&mouseX, &mouseY, &mouseButtons); + _vm->_util->forceMouseUp(); + + handleInput(key, mouseX, mouseY, mouseButtons); + + // Loop + + bool looped = false; + for (Loops::iterator l = _loops.begin(); l != _loops.end(); ++l) { + if ((l->endFrame == _frame) && (l->currentLoop < l->loopCount)) { + _frame = l->startFrame; + + l->currentLoop++; + looped = true; + } + } + + // If we didn't loop, advance the frame and look if we should end here + + if (!looped) { + _frame++; + if (_frame >= endFrame) + break; + } + } + + // Restore the frame rate + _vm->_util->setFrameRate(frameRateBack); +} + +void SEQFile::playFrame() { + // Remove the current animation frames + clearAnims(); + + // Handle background keys, directly updating the background + for (BackgroundKeys::const_iterator b = _bgKeys.begin(); b != _bgKeys.end(); ++b) { + if (!b->background || (b->frame != _frame)) + continue; + + b->background->draw(*_vm->_draw->_backSurface); + + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, 0, 0, 319, 199); + } + + // Handle the animation keys, updating the objects + for (AnimationKeys::const_iterator a = _animKeys.begin(); a != _animKeys.end(); ++a) { + if (a->frame != _frame) + continue; + + Object &object = _objects[a->object]; + + delete object.object; + object.object = 0; + + // No valid animation => remove + if ((a->animation == 0xFFFF) || !a->ani) + continue; + + // Change the animation + + object.object = new ANIObject(*a->ani); + + object.object->setAnimation(a->animation); + object.object->setPosition(a->x, a->y); + object.object->setVisible(true); + object.object->setPause(false); + + object.order = a->order; + } + + // Draw the animations + drawAnims(); +} + +// NOTE: This is really not at all efficient. However, since there's only a +// small number of objects, it should matter. We really do need a stable +// sort, though, so Common::sort() is out. +SEQFile::Objects SEQFile::getOrderedObjects() { + int16 minOrder = (int16)0x7FFF; + int16 maxOrder = (int16)0x8000; + + Objects objects; + + // Find the span of order values + for (uint i = 0; i < kObjectCount; i++) { + if (!_objects[i].object) + continue; + + minOrder = MIN(minOrder, _objects[i].order); + maxOrder = MAX(maxOrder, _objects[i].order); + } + + // Stably sort the objects by order value + for (int16 o = minOrder; o <= maxOrder; o++) + for (uint i = 0; i < kObjectCount; i++) + if (_objects[i].object && (_objects[i].order == o)) + objects.push_back(_objects[i]); + + return objects; +} + +void SEQFile::clearAnims() { + Objects objects = getOrderedObjects(); + + // Remove the animation frames, in reverse drawing order + for (Objects::iterator o = objects.reverse_begin(); o != objects.end(); --o) { + int16 left, top, right, bottom; + + if (o->object->clear(*_vm->_draw->_backSurface, left, top, right, bottom)) + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom); + } +} + +void SEQFile::drawAnims() { + Objects objects = getOrderedObjects(); + + // Draw the animation frames and advance the animation + for (Objects::iterator o = objects.begin(); o != objects.end(); ++o) { + int16 left, top, right, bottom; + + if (o->object->draw(*_vm->_draw->_backSurface, left, top, right, bottom)) + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom); + + o->object->advance(); + } +} + +uint16 SEQFile::getFrame() const { + return _frame; +} + +void SEQFile::seekFrame(uint16 frame) { + _frame = frame; +} + +uint SEQFile::addLoop(uint16 startFrame, uint16 endFrame, uint16 loopCount) { + _loops.resize(_loops.size() + 1); + + _loops.back().startFrame = startFrame; + _loops.back().endFrame = endFrame; + _loops.back().loopCount = loopCount; + _loops.back().currentLoop = 0; + _loops.back().empty = false; + + return _loops.size() - 1; +} + +void SEQFile::skipLoop(uint loopID) { + if (loopID >= _loops.size()) + return; + + _loops[loopID].currentLoop = 0xFFFF; +} + +void SEQFile::delLoop(uint loopID) { + if (loopID >= _loops.size()) + return; + + _loops[loopID].empty = true; + + cleanLoops(); +} + +void SEQFile::cleanLoops() { + while (!_loops.empty() && _loops.back().empty) + _loops.pop_back(); +} + +void SEQFile::abortPlay() { + _abortPlay = true; +} + +void SEQFile::handleFrameEvent() { +} + +void SEQFile::handleInput(int16 key, int16 mouseX, int16 mouseY, MouseButtons mouseButtons) { + if (_abortable && ((key != 0) || (mouseButtons != kMouseButtonsNone))) + abortPlay(); +} + +} // End of namespace Gob diff --git a/engines/gob/pregob/seqfile.h b/engines/gob/pregob/seqfile.h new file mode 100644 index 0000000000..5e12962ef9 --- /dev/null +++ b/engines/gob/pregob/seqfile.h @@ -0,0 +1,193 @@ +/* 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 GOB_PREGOB_SEQFILE_H +#define GOB_PREGOB_SEQFILE_H + +#include "common/system.h" +#include "common/array.h" +#include "common/list.h" + +#include "gob/util.h" + +namespace Common { + class String; + class SeekableReadStream; +} + +namespace Gob { + +class GobEngine; + +class DECFile; +class ANIFile; +class ANIObject; + +/** A SEQ file, describing a complex animation sequence. + * + * Used in early hardcoded gob games. + * The principle is similar to the Mult class (see mult.h), but instead + * of depending on all the externally loaded animations, backgrounds and + * objects, a SEQ file references animation and background directly by + * filename. + */ +class SEQFile { +public: + SEQFile(GobEngine *vm, const Common::String &fileName); + virtual ~SEQFile(); + + /** Play the SEQ. + * + * @param abortable If true, end playback on any user input. + * @param endFrame The frame on where to end, or 0xFFFF for infinite playback. + * @param frameRate The frame rate at which to play the SEQ, or 0 for playing at + * the speed the SEQ itself wants to. + */ + void play(bool abortable = true, uint16 endFrame = 0xFFFF, uint16 frameRate = 0); + + +protected: + GobEngine *_vm; + + + /** Returns the current frame number. */ + uint16 getFrame() const; + + /** Seek to a specific frame. */ + void seekFrame(uint16 frame); + + /** Add a frame loop. */ + uint addLoop(uint16 startFrame, uint16 endFrame, uint16 loopCount); + + /** Skip a frame loop. */ + void skipLoop(uint loopID); + + /** Delete a frame loop. */ + void delLoop(uint loopID); + + /** Ends SEQ playback. */ + void abortPlay(); + + /** Callback for special frame events. */ + virtual void handleFrameEvent(); + /** Callback for special user input handling. */ + virtual void handleInput(int16 key, int16 mouseX, int16 mouseY, MouseButtons mouseButtons); + + +private: + /** Number of animation objects that are visible at the same time. */ + static const uint kObjectCount = 4; + + /** A key for changing the background. */ + struct BackgroundKey { + uint16 frame; ///< Frame the change is to happen. + + const DECFile *background; ///< The background to use. + }; + + /** A key for playing an object animation. */ + struct AnimationKey { + uint object; ///< The object this key belongs to. + + uint16 frame; ///< Frame the change is to happen. + + const ANIFile *ani; ///< The ANI to use. + + uint16 animation; ///< The animation to use. + + int16 x; ///< X position of the animation. + int16 y; ///< Y position of the animation. + + int16 order; ///< Used to determine in which order to draw the objects. + }; + + /** A managed animation object. */ + struct Object { + ANIObject *object; ///< The actual animation object. + + int16 order; ///< The current drawing order. + }; + + /** A frame loop. */ + struct Loop { + uint16 startFrame; + uint16 endFrame; + + uint16 loopCount; + uint16 currentLoop; + + bool empty; + }; + + typedef Common::Array<DECFile *> Backgrounds; + typedef Common::Array<ANIFile *> Animations; + + typedef Common::Array<BackgroundKey> BackgroundKeys; + typedef Common::Array<AnimationKey> AnimationKeys; + + typedef Common::List<Object> Objects; + + typedef Common::Array<Loop> Loops; + + + uint16 _frame; ///< The current frame. + bool _abortPlay; ///< Was the end of the playback requested? + + uint16 _frameRate; + + Backgrounds _backgrounds; ///< All backgrounds in this SEQ. + Animations _animations; ///< All animations in this SEQ. + + BackgroundKeys _bgKeys; ///< The background change keyframes. + AnimationKeys _animKeys; ///< The animation change keyframes. + + Object _objects[kObjectCount]; ///< The managed animation objects. + + Loops _loops; + + /** Whether the playback should be abortable by user input. */ + bool _abortable; + + + // -- Loading helpers -- + + void load(Common::SeekableReadStream &seq); + + const ANIFile *findANI(uint16 index, uint16 &animation); + + // -- Playback helpers -- + + void playFrame(); + + /** Get a list of objects ordered by drawing order. */ + Objects getOrderedObjects(); + + void clearAnims(); ///< Remove all animation frames. + void drawAnims(); ///< Draw the animation frames. + + /** Look if we can compact the loop array. */ + void cleanLoops(); +}; + +} // End of namespace Gob + +#endif // GOB_PREGOB_SEQFILE_H diff --git a/engines/gob/pregob/txtfile.cpp b/engines/gob/pregob/txtfile.cpp new file mode 100644 index 0000000000..3ff0d4b039 --- /dev/null +++ b/engines/gob/pregob/txtfile.cpp @@ -0,0 +1,232 @@ +/* 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 "common/stream.h" + +#include "gob/draw.h" + +#include "gob/pregob/txtfile.h" + +namespace Gob { + +TXTFile::TXTFile(Common::SeekableReadStream &txt, Format format) { + load(txt, format); +} + +TXTFile::~TXTFile() { +} + +TXTFile::LineArray &TXTFile::getLines() { + return _lines; +} + +void TXTFile::load(Common::SeekableReadStream &txt, Format format) { + if (format == kFormatStringPositionColorFont) { + int numLines = getInt(txt); + + _lines.reserve(numLines); + } + + while (!txt.eos()) { + Line line; + + line.text = getStr(txt); + line.x = (format >= kFormatStringPosition) ? getInt(txt) : 0; + line.y = (format >= kFormatStringPosition) ? getInt(txt) : 0; + line.color = (format >= kFormatStringPositionColor) ? getInt(txt) : 0; + line.font = (format >= kFormatStringPositionColorFont) ? getInt(txt) : 0; + + _lines.push_back(line); + } + + while (!_lines.empty() && _lines.back().text.empty()) + _lines.pop_back(); +} + +bool TXTFile::draw(Surface &surface, int16 &left, int16 &top, int16 &right, int16 &bottom, + const Font * const *fonts, uint fontCount, int color) { + + trashBuffer(); + + if (!getArea(left, top, right, bottom, fonts, fontCount)) + return false; + + resizeBuffer(right - left + 1, bottom - top + 1); + saveScreen(surface, left, top, right, bottom); + + for (LineArray::const_iterator l = _lines.begin(); l != _lines.end(); ++l) { + if (l->font >= fontCount) + continue; + + fonts[l->font]->drawString(l->text, l->x, l->y, (color < 0) ? l->color : color, 0, true, surface); + } + + return true; +} + +bool TXTFile::draw(uint line, Surface &surface, int16 &left, int16 &top, int16 &right, int16 &bottom, + const Font * const *fonts, uint fontCount, int color) { + + trashBuffer(); + + if (!getArea(line, left, top, right, bottom, fonts, fontCount)) + return false; + + resizeBuffer(right - left + 1, bottom - top + 1); + saveScreen(surface, left, top, right, bottom); + + const Line &l = _lines[line]; + + fonts[l.font]->drawString(l.text, l.x, l.y, (color < 0) ? l.color : color, 0, true, surface); + + return true; +} + +bool TXTFile::draw(Surface &surface, const Font * const *fonts, uint fontCount, int color) { + int16 left, top, right, bottom; + + return draw(surface, left, top, right, bottom, fonts, fontCount, color); +} + +bool TXTFile::draw(uint line, Surface &surface, const Font * const *fonts, uint fontCount, int color) { + int16 left, top, right, bottom; + + return draw(line, surface, left, top, right, bottom, fonts, fontCount, color); +} + +bool TXTFile::clear(Surface &surface, int16 &left, int16 &top, int16 &right, int16 &bottom) { + return restoreScreen(surface, left, top, right, bottom); +} + +bool TXTFile::getArea(int16 &left, int16 &top, int16 &right, int16 &bottom, + const Font * const *fonts, uint fontCount) const { + + bool hasLine = false; + + left = 0x7FFF; + top = 0x7FFF; + right = 0x0000; + bottom = 0x0000; + + for (uint i = 0; i < _lines.size(); i++) { + int16 lLeft, lTop, lRight, lBottom; + + if (getArea(i, lLeft, lTop, lRight, lBottom, fonts, fontCount)) { + left = MIN(left , lLeft ); + top = MIN(top , lTop ); + right = MAX(right , lRight ); + bottom = MAX(bottom, lBottom); + + hasLine = true; + } + } + + return hasLine; +} + +bool TXTFile::getArea(uint line, int16 &left, int16 &top, int16 &right, int16 &bottom, + const Font * const *fonts, uint fontCount) const { + + + if ((line >= _lines.size()) || (_lines[line].font >= fontCount)) + return false; + + const Line &l = _lines[line]; + + left = l.x; + top = l.y; + right = l.x + l.text.size() * fonts[l.font]->getCharWidth() - 1; + bottom = l.y + fonts[l.font]->getCharHeight() - 1; + + return true; +} + +Common::String TXTFile::getStr(Common::SeekableReadStream &txt) { + // Skip all ' ', '\n' and '\r' + while (!txt.eos()) { + char c = txt.readByte(); + if (txt.eos()) + break; + + if ((c != ' ') && (c != '\n') && (c != '\r')) { + txt.seek(-1, SEEK_CUR); + break; + } + } + + if (txt.eos()) + return ""; + + // Read string until ' ', '\n' or '\r' + Common::String string; + while (!txt.eos()) { + char c = txt.readByte(); + if ((c == ' ') || (c == '\n') || (c == '\r')) + break; + + string += c; + } + + // Replace all '#' with ' ' and throw out non-printables + Common::String cleanString; + + for (uint i = 0; i < string.size(); i++) { + if (string[i] == '#') + cleanString += ' '; + else if ((unsigned char)string[i] >= ' ') + cleanString += string[i]; + } + + return cleanString; +} + +int TXTFile::getInt(Common::SeekableReadStream &txt) { + // Skip all [^-0-9] + while (!txt.eos()) { + char c = txt.readByte(); + if (txt.eos()) + break; + + if ((c == '-') || ((c >= '0') && (c <= '9'))) { + txt.seek(-1, SEEK_CUR); + break; + } + } + + if (txt.eos()) + return 0; + + // Read until [^-0-9] + Common::String string; + while (!txt.eos()) { + char c = txt.readByte(); + if ((c != '-') && ((c < '0') || (c > '9'))) + break; + + string += c; + } + + // Convert to integer + return atoi(string.c_str()); +} + +} // End of namespace Gob diff --git a/engines/gob/pregob/txtfile.h b/engines/gob/pregob/txtfile.h new file mode 100644 index 0000000000..c623b58859 --- /dev/null +++ b/engines/gob/pregob/txtfile.h @@ -0,0 +1,91 @@ +/* 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 GOB_PREGOB_TXTFILE_H +#define GOB_PREGOB_TXTFILE_H + +#include "common/system.h" +#include "common/str.h" +#include "common/array.h" + +#include "gob/backbuffer.h" + +namespace Common { + class SeekableReadStream; +} + +namespace Gob { + +class Surface; +class Font; + +class TXTFile : public BackBuffer { +public: + enum Format { + kFormatString, + kFormatStringPosition, + kFormatStringPositionColor, + kFormatStringPositionColorFont + }; + + struct Line { + Common::String text; + int x, y; + int color; + uint font; + }; + + typedef Common::Array<Line> LineArray; + + TXTFile(Common::SeekableReadStream &txt, Format format); + ~TXTFile(); + + LineArray &getLines(); + + bool draw( Surface &surface, const Font * const *fonts, uint fontCount, int color = -1); + bool draw(uint line, Surface &surface, const Font * const *fonts, uint fontCount, int color = -1); + + bool draw( Surface &surface, int16 &left, int16 &top, int16 &right, int16 &bottom, + const Font * const *fonts, uint fontCount, int color = -1); + bool draw(uint line, Surface &surface, int16 &left, int16 &top, int16 &right, int16 &bottom, + const Font * const *fonts, uint fontCount, int color = -1); + + bool clear(Surface &surface, int16 &left, int16 &top, int16 &right, int16 &bottom); + +private: + LineArray _lines; + + void load(Common::SeekableReadStream &txt, Format format); + + Common::String getStr(Common::SeekableReadStream &txt); + int getInt(Common::SeekableReadStream &txt); + + + bool getArea( int16 &left, int16 &top, int16 &right, int16 &bottom, + const Font * const *fonts, uint fontCount) const; + bool getArea(uint line, int16 &left, int16 &top, int16 &right, int16 &bottom, + const Font * const *fonts, uint fontCount) const; +}; + +} // End of namespace Gob + +#endif // GOB_PREGOB_TXTFILE_H diff --git a/engines/gob/rxyfile.cpp b/engines/gob/rxyfile.cpp index 9702dc8c7f..2ff8c121cd 100644 --- a/engines/gob/rxyfile.cpp +++ b/engines/gob/rxyfile.cpp @@ -21,12 +21,19 @@ */ #include "common/stream.h" +#include "common/substream.h" #include "gob/rxyfile.h" namespace Gob { RXYFile::RXYFile(Common::SeekableReadStream &rxy) : _width(0), _height(0) { + Common::SeekableSubReadStreamEndian sub(&rxy, 0, rxy.size(), false, DisposeAfterUse::NO); + + load(sub); +} + +RXYFile::RXYFile(Common::SeekableSubReadStreamEndian &rxy) : _width(0), _height(0) { load(rxy); } @@ -64,22 +71,22 @@ const RXYFile::Coordinates &RXYFile::operator[](uint i) const { return _coords[i]; } -void RXYFile::load(Common::SeekableReadStream &rxy) { +void RXYFile::load(Common::SeekableSubReadStreamEndian &rxy) { if (rxy.size() < 2) return; rxy.seek(0); - _realCount = rxy.readUint16LE(); + _realCount = rxy.readUint16(); uint16 count = (rxy.size() - 2) / 8; _coords.resize(count); for (CoordArray::iterator c = _coords.begin(); c != _coords.end(); ++c) { - c->left = rxy.readUint16LE(); - c->right = rxy.readUint16LE(); - c->top = rxy.readUint16LE(); - c->bottom = rxy.readUint16LE(); + c->left = rxy.readUint16(); + c->right = rxy.readUint16(); + c->top = rxy.readUint16(); + c->bottom = rxy.readUint16(); if (c->left != 0xFFFF) { _width = MAX<uint16>(_width , c->right + 1); diff --git a/engines/gob/rxyfile.h b/engines/gob/rxyfile.h index bc9600b5b0..4fd46c5e40 100644 --- a/engines/gob/rxyfile.h +++ b/engines/gob/rxyfile.h @@ -28,6 +28,7 @@ namespace Common { class SeekableReadStream; + class SeekableSubReadStreamEndian; } namespace Gob { @@ -46,6 +47,7 @@ public: }; RXYFile(Common::SeekableReadStream &rxy); + RXYFile(Common::SeekableSubReadStreamEndian &rxy); RXYFile(uint16 width, uint16 height); ~RXYFile(); @@ -71,7 +73,7 @@ private: uint16 _height; - void load(Common::SeekableReadStream &rxy); + void load(Common::SeekableSubReadStreamEndian &rxy); }; } // End of namespace Gob diff --git a/engines/gob/sound/sound.cpp b/engines/gob/sound/sound.cpp index 403bd632a1..63af6aeef4 100644 --- a/engines/gob/sound/sound.cpp +++ b/engines/gob/sound/sound.cpp @@ -109,7 +109,7 @@ int Sound::sampleGetNextFreeSlot() const { return -1; } -bool Sound::sampleLoad(SoundDesc *sndDesc, SoundType type, const char *fileName, bool tryExist) { +bool Sound::sampleLoad(SoundDesc *sndDesc, SoundType type, const char *fileName) { if (!sndDesc) return false; @@ -117,12 +117,15 @@ bool Sound::sampleLoad(SoundDesc *sndDesc, SoundType type, const char *fileName, int32 size; byte *data = _vm->_dataIO->getFile(fileName, size); - if (!data) { - warning("Can't open sample file \"%s\"", fileName); + + if (!data || !sndDesc->load(type, data, size)) { + delete data; + + warning("Sound::sampleLoad(): Failed to load sound \"%s\"", fileName); return false; } - return sndDesc->load(type, data, size); + return true; } void Sound::sampleFree(SoundDesc *sndDesc, bool noteAdLib, int index) { @@ -458,7 +461,7 @@ void Sound::blasterStop(int16 fadeLength, SoundDesc *sndDesc) { _blaster->stopSound(fadeLength, sndDesc); } -void Sound::blasterPlayComposition(int16 *composition, int16 freqVal, +void Sound::blasterPlayComposition(const int16 *composition, int16 freqVal, SoundDesc *sndDescs, int8 sndCount) { if (!_blaster) return; diff --git a/engines/gob/sound/sound.h b/engines/gob/sound/sound.h index 6ad0ec5483..bbc182d172 100644 --- a/engines/gob/sound/sound.h +++ b/engines/gob/sound/sound.h @@ -51,7 +51,7 @@ public: const SoundDesc *sampleGetBySlot(int slot) const; int sampleGetNextFreeSlot() const; - bool sampleLoad(SoundDesc *sndDesc, SoundType type, const char *fileName, bool tryExist = true); + bool sampleLoad(SoundDesc *sndDesc, SoundType type, const char *fileName); void sampleFree(SoundDesc *sndDesc, bool noteAdLib = false, int index = -1); @@ -60,7 +60,7 @@ public: int16 frequency, int16 fadeLength = 0); void blasterStop(int16 fadeLength, SoundDesc *sndDesc = 0); - void blasterPlayComposition(int16 *composition, int16 freqVal, + void blasterPlayComposition(const int16 *composition, int16 freqVal, SoundDesc *sndDescs = 0, int8 sndCount = kSoundsCount); void blasterStopComposition(); void blasterRepeatComposition(int32 repCount); diff --git a/engines/gob/sound/soundblaster.cpp b/engines/gob/sound/soundblaster.cpp index 19c2346448..f267eee32d 100644 --- a/engines/gob/sound/soundblaster.cpp +++ b/engines/gob/sound/soundblaster.cpp @@ -88,7 +88,7 @@ void SoundBlaster::nextCompositionPos() { _compositionPos = -1; } -void SoundBlaster::playComposition(int16 *composition, int16 freqVal, +void SoundBlaster::playComposition(const int16 *composition, int16 freqVal, SoundDesc *sndDescs, int8 sndCount) { _compositionSamples = sndDescs; diff --git a/engines/gob/sound/soundblaster.h b/engines/gob/sound/soundblaster.h index c740ba2269..3c4968d611 100644 --- a/engines/gob/sound/soundblaster.h +++ b/engines/gob/sound/soundblaster.h @@ -41,7 +41,7 @@ public: int16 frequency, int16 fadeLength = 0); void stopSound(int16 fadeLength, SoundDesc *sndDesc = 0); - void playComposition(int16 *composition, int16 freqVal, + void playComposition(const int16 *composition, int16 freqVal, SoundDesc *sndDescs = 0, int8 sndCount = 60); void stopComposition(); void endComposition(); diff --git a/engines/gob/surface.cpp b/engines/gob/surface.cpp index 3eaf741be2..6b65eb6ab9 100644 --- a/engines/gob/surface.cpp +++ b/engines/gob/surface.cpp @@ -26,112 +26,15 @@ #include "common/stream.h" #include "common/util.h" #include "common/frac.h" +#include "common/textconsole.h" #include "graphics/primitives.h" #include "graphics/pixelformat.h" #include "graphics/surface.h" +#include "graphics/decoders/iff.h" namespace Gob { -LBMLoader::LBMLoader(Common::SeekableReadStream &stream) : _parser(&stream), - _hasHeader(false), _palette(0), _image(0) { - -} - -bool LBMLoader::loadHeader(Graphics::BMHD &header) { - if (!readHeader()) - return false; - - header = _decoder._header; - return true; -} - -bool LBMLoader::loadPalette(byte *palette) { - assert(!_palette); - assert(palette); - - _palette = palette; - - Common::Functor1Mem<Common::IFFChunk&, bool, LBMLoader> c(this, &LBMLoader::callbackPalette); - _parser.parse(c); - - if (!_palette) - return false; - - _palette = 0; - return true; -} - -bool LBMLoader::loadImage(byte *image) { - assert(!_image); - assert(image); - - if (!readHeader()) - return false; - - _image = image; - - Common::Functor1Mem<Common::IFFChunk&, bool, LBMLoader> c(this, &LBMLoader::callbackImage); - _parser.parse(c); - - if (!_image) - return false; - - _image = 0; - return true; -} - -bool LBMLoader::callbackHeader(Common::IFFChunk &chunk) { - if (chunk._type == ID_BMHD) { - if (chunk._size == sizeof(Graphics::BMHD)) { - _decoder.loadHeader(chunk._stream); - _hasHeader = true; - } - - return true; // Stop the IFF parser - } - - return false; -} - -bool LBMLoader::callbackPalette(Common::IFFChunk &chunk) { - assert(_palette); - - if (chunk._type == ID_CMAP) { - if (chunk._size == 768) { - if (chunk._stream->read(_palette, chunk._size) != chunk._size) - _palette = 0; - } else - _palette = 0; - - return true; // Stop the IFF parser - } - - return false; -} - -bool LBMLoader::callbackImage(Common::IFFChunk &chunk) { - assert(_image); - - if (chunk._type == ID_BODY) { - _decoder.loadBitmap(Graphics::ILBMDecoder::ILBM_UNPACK_PLANES, _image, chunk._stream); - return true; - } - - return false; -} - -bool LBMLoader::readHeader() { - if (_hasHeader) - return true; - - Common::Functor1Mem<Common::IFFChunk&, bool, LBMLoader> c(this, &LBMLoader::callbackHeader); - _parser.parse(c); - - return _hasHeader; -} - - static void plotPixel(int x, int y, int color, void *data) { Surface *dest = (Surface *)data; @@ -684,6 +587,12 @@ void Surface::shadeRect(uint16 left, uint16 top, uint16 right, uint16 bottom, } +void Surface::recolor(uint8 from, uint8 to) { + for (Pixel p = get(); p.isValid(); ++p) + if (p.get() == from) + p.set(to); +} + void Surface::putPixel(uint16 x, uint16 y, uint32 color) { if ((x >= _width) || (y >= _height)) return; @@ -835,8 +744,8 @@ bool Surface::loadImage(Common::SeekableReadStream &stream, ImageType type) { switch (type) { case kImageTypeTGA: return loadTGA(stream); - case kImageTypeLBM: - return loadLBM(stream); + case kImageTypeIFF: + return loadIFF(stream); case kImageTypeBRC: return loadBRC(stream); case kImageTypeBMP: @@ -865,7 +774,7 @@ ImageType Surface::identifyImage(Common::SeekableReadStream &stream) { stream.seek(startPos); if (!strncmp(buffer , "FORM", 4)) - return kImageTypeLBM; + return kImageTypeIFF; if (!strncmp(buffer + 6, "JFIF", 4)) return kImageTypeJPEG; if (!strncmp(buffer , "BRC" , 3)) @@ -898,20 +807,17 @@ bool Surface::loadTGA(Common::SeekableReadStream &stream) { return false; } -bool Surface::loadLBM(Common::SeekableReadStream &stream) { +bool Surface::loadIFF(Common::SeekableReadStream &stream) { + Graphics::IFFDecoder decoder; + decoder.loadStream(stream); - LBMLoader loader(stream); - - Graphics::BMHD header; - loader.loadHeader(header); - - if (header.depth != 8) - // Only 8bpp LBMs supported for now + if (!decoder.getSurface()) return false; - resize(header.width, header.height); + resize(decoder.getSurface()->w, decoder.getSurface()->h); + memcpy(_vidMem, decoder.getSurface()->pixels, decoder.getSurface()->w * decoder.getSurface()->h); - return loader.loadImage(_vidMem); + return true; } bool Surface::loadBRC(Common::SeekableReadStream &stream) { diff --git a/engines/gob/surface.h b/engines/gob/surface.h index 09be8d1a49..8a1b502a95 100644 --- a/engines/gob/surface.h +++ b/engines/gob/surface.h @@ -26,9 +26,6 @@ #include "common/scummsys.h" #include "common/ptr.h" #include "common/rational.h" -#include "common/iff_container.h" - -#include "graphics/iff.h" namespace Common { class SeekableReadStream; @@ -39,37 +36,12 @@ namespace Gob { enum ImageType { kImageTypeNone = -1, kImageTypeTGA = 0, - kImageTypeLBM, + kImageTypeIFF, kImageTypeBRC, kImageTypeBMP, kImageTypeJPEG }; -class LBMLoader { -public: - LBMLoader(Common::SeekableReadStream &stream); - - bool loadHeader (Graphics::BMHD &header); - bool loadPalette(byte *palette); - bool loadImage (byte *image); - -private: - Common::IFFParser _parser; - - bool _hasHeader; - - Graphics::ILBMDecoder _decoder; - - byte *_palette; - byte *_image; - - bool callbackHeader (Common::IFFChunk &chunk); - bool callbackPalette(Common::IFFChunk &chunk); - bool callbackImage (Common::IFFChunk &chunk); - - bool readHeader(); -}; - /** An iterator over a surface's image data, automatically handles different color depths. */ class Pixel { public: @@ -156,6 +128,8 @@ public: void shadeRect(uint16 left, uint16 top, uint16 right, uint16 bottom, uint32 color, uint8 strength); + void recolor(uint8 from, uint8 to); + void putPixel(uint16 x, uint16 y, uint32 color); void drawLine(uint16 x0, uint16 y0, uint16 x1, uint16 y1, uint32 color); void drawRect(uint16 left, uint16 top, uint16 right, uint16 bottom, uint32 color); @@ -180,7 +154,7 @@ private: uint16 dWidth, uint16 dHeight, uint16 sWidth, uint16 sHeight); bool loadTGA (Common::SeekableReadStream &stream); - bool loadLBM (Common::SeekableReadStream &stream); + bool loadIFF (Common::SeekableReadStream &stream); bool loadBRC (Common::SeekableReadStream &stream); bool loadBMP (Common::SeekableReadStream &stream); bool loadJPEG(Common::SeekableReadStream &stream); diff --git a/engines/gob/util.cpp b/engines/gob/util.cpp index 64dfcf9b12..5ac4ef024e 100644 --- a/engines/gob/util.cpp +++ b/engines/gob/util.cpp @@ -189,12 +189,27 @@ bool Util::getKeyFromBuffer(Common::KeyState &key) { return true; } +static const uint16 kLatin1ToCP850[] = { + 0xFF, 0xAD, 0xBD, 0x9C, 0xCF, 0xBE, 0xDD, 0xF5, 0xF9, 0xB8, 0xA6, 0xAE, 0xAA, 0xF0, 0xA9, 0xEE, + 0xF8, 0xF1, 0xFD, 0xFC, 0xEF, 0xE6, 0xF4, 0xFA, 0xF7, 0xFB, 0xA7, 0xAF, 0xAC, 0xAB, 0xF3, 0xA8, + 0xB7, 0xB5, 0xB6, 0xC7, 0x8E, 0x8F, 0x92, 0x80, 0xD4, 0x90, 0xD2, 0xD3, 0xDE, 0xD6, 0xD7, 0xD8, + 0xD1, 0xA5, 0xE3, 0xE0, 0xE2, 0xE5, 0x99, 0x9E, 0x9D, 0xEB, 0xE9, 0xEA, 0x9A, 0xED, 0xE8, 0xE1, + 0x85, 0xA0, 0x83, 0xC6, 0x84, 0x86, 0x91, 0x87, 0x8A, 0x82, 0x88, 0x89, 0x8D, 0xA1, 0x8C, 0x8B, + 0xD0, 0xA4, 0x95, 0xA2, 0x93, 0xE4, 0x94, 0xF6, 0x9B, 0x97, 0xA3, 0x96, 0x81, 0xEC, 0xE7, 0x98 +}; + +int16 Util::toCP850(uint16 latin1) { + if ((latin1 < 0xA0) || ((latin1 - 0xA0) >= ARRAYSIZE(kLatin1ToCP850))) + return 0; + + return kLatin1ToCP850[latin1 - 0xA0]; +} + int16 Util::translateKey(const Common::KeyState &key) { static struct keyS { int16 from; int16 to; } keys[] = { - {Common::KEYCODE_INVALID, kKeyNone }, {Common::KEYCODE_BACKSPACE, kKeyBackspace}, {Common::KEYCODE_SPACE, kKeySpace }, {Common::KEYCODE_RETURN, kKeyReturn }, @@ -216,20 +231,88 @@ int16 Util::translateKey(const Common::KeyState &key) { {Common::KEYCODE_F10, kKeyF10 } }; + // Translate special keys for (int i = 0; i < ARRAYSIZE(keys); i++) if (key.keycode == keys[i].from) return keys[i].to; - if ((key.keycode >= Common::KEYCODE_SPACE) && - (key.keycode <= Common::KEYCODE_DELETE)) { - - // Used as a user input in Gobliins 2 notepad, in the save dialog, ... + // Return the ascii value, for text input + if ((key.ascii >= 32) && (key.ascii <= 127)) return key.ascii; - } + + // Translate international characters into CP850 characters + if ((key.ascii >= 160) && (key.ascii <= 255)) + return toCP850(key.ascii); return 0; } +static const uint8 kLowerToUpper[][2] = { + {0x81, 0x9A}, + {0x82, 0x90}, + {0x83, 0xB6}, + {0x84, 0x8E}, + {0x85, 0xB7}, + {0x86, 0x8F}, + {0x87, 0x80}, + {0x88, 0xD2}, + {0x89, 0xD3}, + {0x8A, 0xD4}, + {0x8B, 0xD8}, + {0x8C, 0xD7}, + {0x8D, 0xDE}, + {0x91, 0x92}, + {0x93, 0xE2}, + {0x94, 0x99}, + {0x95, 0xE3}, + {0x96, 0xEA}, + {0x97, 0xEB}, + {0x95, 0xE3}, + {0x96, 0xEA}, + {0x97, 0xEB}, + {0x9B, 0x9D}, + {0xA0, 0xB5}, + {0xA1, 0xD6}, + {0xA2, 0xE0}, + {0xA3, 0xE9}, + {0xA4, 0xA5}, + {0xC6, 0xC7}, + {0xD0, 0xD1}, + {0xE4, 0xE5}, + {0xE7, 0xE8}, + {0xEC, 0xED} +}; + +char Util::toCP850Lower(char cp850) { + const uint8 cp = (unsigned char)cp850; + if (cp <= 32) + return cp850; + + if (cp <= 127) + return tolower(cp850); + + for (uint i = 0; i < ARRAYSIZE(kLowerToUpper); i++) + if (cp == kLowerToUpper[i][1]) + return (char)kLowerToUpper[i][0]; + + return cp850; +} + +char Util::toCP850Upper(char cp850) { + const uint8 cp = (unsigned char)cp850; + if (cp <= 32) + return cp850; + + if (cp <= 127) + return toupper(cp850); + + for (uint i = 0; i < ARRAYSIZE(kLowerToUpper); i++) + if (cp == kLowerToUpper[i][0]) + return (char)kLowerToUpper[i][1]; + + return cp850; +} + int16 Util::getKey() { Common::KeyState key; @@ -367,21 +450,29 @@ void Util::notifyNewAnim() { _startFrameTime = getTimeKey(); } -void Util::waitEndFrame() { +void Util::waitEndFrame(bool handleInput) { int32 time; - _vm->_video->waitRetrace(); - time = getTimeKey() - _startFrameTime; if ((time > 1000) || (time < 0)) { + _vm->_video->retrace(); _startFrameTime = getTimeKey(); return; } - int32 toWait = _frameWaitTime - time; + int32 toWait = 0; + do { + if (toWait > 0) + delay(MIN<int>(toWait, 10)); + + if (handleInput) + processInput(); + + _vm->_video->retrace(); - if (toWait > 0) - delay(toWait); + time = getTimeKey() - _startFrameTime; + toWait = _frameWaitTime - time; + } while (toWait > 0); _startFrameTime = getTimeKey(); } diff --git a/engines/gob/util.h b/engines/gob/util.h index b26a78ab2c..a4984c6207 100644 --- a/engines/gob/util.h +++ b/engines/gob/util.h @@ -124,7 +124,7 @@ public: int16 getFrameRate(); void setFrameRate(int16 rate); void notifyNewAnim(); - void waitEndFrame(); + void waitEndFrame(bool handleInput = true); void setScrollOffset(int16 x = -1, int16 y = -1); static void insertStr(const char *str1, char *str2, int16 pos); @@ -143,6 +143,11 @@ public: /** Read a constant-length string out of a stream. */ static Common::String readString(Common::SeekableReadStream &stream, int n); + /** Convert a character in CP850 encoding to the equivalent lower case character. */ + static char toCP850Lower(char cp850); + /** Convert a character in CP850 encoding to the equivalent upper case character. */ + static char toCP850Upper(char cp850); + Util(GobEngine *vm); protected: @@ -166,6 +171,7 @@ protected: void addKeyToBuffer(const Common::KeyState &key); bool getKeyFromBuffer(Common::KeyState &key); int16 translateKey(const Common::KeyState &key); + int16 toCP850(uint16 latin1); void checkJoystick(); void keyDown(const Common::Event &event); diff --git a/engines/gob/video.cpp b/engines/gob/video.cpp index 3b1c6423bb..64af34cf62 100644 --- a/engines/gob/video.cpp +++ b/engines/gob/video.cpp @@ -84,6 +84,10 @@ uint16 Font::getCharCount() const { return _endItem - _startItem + 1; } +bool Font::hasChar(uint8 c) const { + return (c >= _startItem) && (c <= _endItem); +} + bool Font::isMonospaced() const { return _charWidths == 0; } @@ -134,6 +138,23 @@ void Font::drawLetter(Surface &surf, uint8 c, uint16 x, uint16 y, } } +void Font::drawString(const Common::String &str, int16 x, int16 y, int16 color1, int16 color2, + bool transp, Surface &dest) const { + + const char *s = str.c_str(); + + while (*s != '\0') { + const int16 charRight = x + getCharWidth(*s); + const int16 charBottom = y + getCharHeight(); + + if ((x >= 0) && (y >= 0) && (charRight <= dest.getWidth()) && (charBottom <= dest.getHeight())) + drawLetter(dest, *s, x, y, color1, color2, transp); + + x += getCharWidth(*s); + s++; + } +} + const byte *Font::getCharData(uint8 c) const { if (_endItem == 0) { warning("Font::getCharData(): _endItem == 0"); @@ -225,7 +246,7 @@ void Video::setSize(bool defaultTo1XScaler) { void Video::retrace(bool mouse) { if (mouse) - CursorMan.showMouse((_vm->_draw->_showCursor & 2) != 0); + CursorMan.showMouse((_vm->_draw->_showCursor & 6) != 0); if (_vm->_global->_primarySurfDesc) { int screenX = _screenDeltaX; @@ -332,6 +353,10 @@ void Video::drawPackedSprite(byte *sprBuf, int16 width, int16 height, void Video::drawPackedSprite(const char *path, Surface &dest, int width) { int32 size; byte *data = _vm->_dataIO->getFile(path, size); + if (!data) { + warning("Video::drawPackedSprite(): Failed to open sprite \"%s\"", path); + return; + } drawPackedSprite(data, width, dest.getHeight(), 0, 0, 0, dest); delete[] data; diff --git a/engines/gob/video.h b/engines/gob/video.h index ecbb579c5f..122c1e47d5 100644 --- a/engines/gob/video.h +++ b/engines/gob/video.h @@ -41,11 +41,16 @@ public: uint8 getCharWidth () const; uint8 getCharHeight() const; + bool hasChar(uint8 c) const; + bool isMonospaced() const; void drawLetter(Surface &surf, uint8 c, uint16 x, uint16 y, uint32 color1, uint32 color2, bool transp) const; + void drawString(const Common::String &str, int16 x, int16 y, int16 color1, int16 color2, + bool transp, Surface &dest) const; + private: const byte *_dataPtr; const byte *_data; diff --git a/engines/groovie/detection.cpp b/engines/groovie/detection.cpp index 1a3b313649..65452f5cf3 100644 --- a/engines/groovie/detection.cpp +++ b/engines/groovie/detection.cpp @@ -25,9 +25,12 @@ #include "groovie/saveload.h" #include "common/system.h" +#include "common/translation.h" namespace Groovie { +#define GAMEOPTION_T7G_FAST_MOVIE_SPEED GUIO_GAMEOPTIONS1 + static const PlainGameDescriptor groovieGames[] = { // Games {"t7g", "The 7th Guest"}, @@ -52,7 +55,7 @@ static const GroovieGameDescription gameDescriptions[] = { "t7g", "", AD_ENTRY1s("script.grv", "d1b8033b40aa67c076039881eccce90d", 16659), Common::EN_ANY, Common::kPlatformPC, ADGF_NO_FLAGS, - GUIO4(GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_NOASPECT) + GUIO5(GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_NOASPECT, GAMEOPTION_T7G_FAST_MOVIE_SPEED) }, kGroovieT7G, 0 }, @@ -63,7 +66,7 @@ static const GroovieGameDescription gameDescriptions[] = { "t7g", "", AD_ENTRY1s("T7GMac", "acdc4a58dd3f007f65e99b99d78e0bce", 1814029), Common::EN_ANY, Common::kPlatformMacintosh, ADGF_MACRESFORK, - GUIO4(GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_NOASPECT) + GUIO5(GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_NOASPECT, GAMEOPTION_T7G_FAST_MOVIE_SPEED) }, kGroovieT7G, 0 }, @@ -79,7 +82,7 @@ static const GroovieGameDescription gameDescriptions[] = { "t7g", "", AD_ENTRY1s("T7GMac", "6bdee8d0f9eef6d58d02fcd7deec3fb2", 1830783), Common::EN_ANY, Common::kPlatformMacintosh, ADGF_MACRESFORK, - GUIO4(GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_NOASPECT) + GUIO5(GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_NOASPECT, GAMEOPTION_T7G_FAST_MOVIE_SPEED) }, kGroovieT7G, 0 }, @@ -90,7 +93,7 @@ static const GroovieGameDescription gameDescriptions[] = { "t7g", "", AD_ENTRY1s("T7GMac", "0d595d4b44ae1814082938d051e5174e", 1830783), Common::EN_ANY, Common::kPlatformMacintosh, ADGF_MACRESFORK, - GUIO4(GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_NOASPECT) + GUIO5(GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_NOASPECT, GAMEOPTION_T7G_FAST_MOVIE_SPEED) }, kGroovieT7G, 0 }, @@ -106,7 +109,7 @@ static const GroovieGameDescription gameDescriptions[] = { { NULL, 0, NULL, 0} }, Common::RU_RUS, Common::kPlatformPC, ADGF_NO_FLAGS, - GUIO4(GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_NOASPECT) + GUIO5(GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_NOASPECT, GAMEOPTION_T7G_FAST_MOVIE_SPEED) }, kGroovieT7G, 0 }, @@ -120,7 +123,7 @@ static const GroovieGameDescription gameDescriptions[] = { { NULL, 0, NULL, 0} }, Common::EN_ANY, Common::kPlatformIOS, ADGF_NO_FLAGS, - GUIO2(GUIO_NOMIDI, GUIO_NOASPECT) + GUIO3(GUIO_NOMIDI, GUIO_NOASPECT, GAMEOPTION_T7G_FAST_MOVIE_SPEED) }, kGroovieT7G, 0 }, @@ -137,12 +140,42 @@ static const GroovieGameDescription gameDescriptions[] = { kGroovieV2, 1 }, + // The 11th Hour Macintosh English + { + { + "11h", "", + { + { "disk.1", 0, "5c0428cd3659fc7bbcd0aa16485ed5da", 227 }, + { "The 11th Hour Installer", 0, "bcdb4040b27f15b18f39fb9e496d384a", 1002987 }, + { 0, 0, 0, 0 } + }, + Common::EN_ANY, Common::kPlatformMacintosh, ADGF_UNSTABLE, + GUIO4(GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_NOASPECT) + }, + kGroovieV2, 1 + }, + + // The 11th Hour Macintosh English (Installed) + { + { + "11h", "Installed", + { + { "disk.1", 0, "5c0428cd3659fc7bbcd0aa16485ed5da", 227 }, + { "el01.mov", 0, "70f42dfc25b1488a08011dc45bb5145d", 6039 }, + { 0, 0, 0, 0 } + }, + Common::EN_ANY, Common::kPlatformMacintosh, ADGF_UNSTABLE, + GUIO4(GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_NOASPECT) + }, + kGroovieV2, 1 + }, + // The 11th Hour DOS Demo English { { "11h", "Demo", AD_ENTRY1s("disk.1", "aacb32ce07e0df2894bd83a3dee40c12", 70), - Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO | ADGF_UNSTABLE, + Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO | ADGF_UNSTABLE, GUIO5(GUIO_NOLAUNCHLOAD, GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_NOASPECT) }, kGroovieV2, 1 @@ -159,6 +192,36 @@ static const GroovieGameDescription gameDescriptions[] = { kGroovieV2, 2 }, + // The Making of The 11th Hour Macintosh English + { + { + "11h", "Making Of", + { + { "disk.1", 0, "5c0428cd3659fc7bbcd0aa16485ed5da", 227 }, + { "The 11th Hour Installer", 0, "bcdb4040b27f15b18f39fb9e496d384a", 1002987 }, + { 0, 0, 0, 0 } + }, + Common::EN_ANY, Common::kPlatformMacintosh, ADGF_UNSTABLE, + GUIO4(GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_NOASPECT) + }, + kGroovieV2, 2 + }, + + // The Making of The 11th Hour Macintosh English (Installed) + { + { + "11h", "Making Of (Installed)", + { + { "disk.1", 0, "5c0428cd3659fc7bbcd0aa16485ed5da", 227 }, + { "el01.mov", 0, "70f42dfc25b1488a08011dc45bb5145d", 6039 }, + { 0, 0, 0, 0 } + }, + Common::EN_ANY, Common::kPlatformMacintosh, ADGF_UNSTABLE, + GUIO4(GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_NOASPECT) + }, + kGroovieV2, 2 + }, + // Clandestiny Trailer DOS English { { @@ -170,6 +233,36 @@ static const GroovieGameDescription gameDescriptions[] = { kGroovieV2, 3 }, + // Clandestiny Trailer Macintosh English + { + { + "clandestiny", "Trailer", + { + { "disk.1", 0, "5c0428cd3659fc7bbcd0aa16485ed5da", 227 }, + { "The 11th Hour Installer", 0, "bcdb4040b27f15b18f39fb9e496d384a", 1002987 }, + { 0, 0, 0, 0 } + }, + Common::EN_ANY, Common::kPlatformMacintosh, ADGF_UNSTABLE, + GUIO4(GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_NOASPECT) + }, + kGroovieV2, 3 + }, + + // Clandestiny Trailer Macintosh English (Installed) + { + { + "clandestiny", "Trailer (Installed)", + { + { "disk.1", 0, "5c0428cd3659fc7bbcd0aa16485ed5da", 227 }, + { "el01.mov", 0, "70f42dfc25b1488a08011dc45bb5145d", 6039 }, + { 0, 0, 0, 0 } + }, + Common::EN_ANY, Common::kPlatformMacintosh, ADGF_UNSTABLE, + GUIO4(GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_NOASPECT) + }, + kGroovieV2, 3 + }, + // Clandestiny DOS English { { @@ -207,9 +300,28 @@ static const GroovieGameDescription gameDescriptions[] = { {AD_TABLE_END_MARKER, kGroovieT7G, 0} }; +static const char *directoryGlobs[] = { + "MIDI", + 0 +}; + +static const ADExtraGuiOptionsMap optionsList[] = { + { + GAMEOPTION_T7G_FAST_MOVIE_SPEED, + { + _s("Fast movie speed"), + _s("Play movies at an increased speed"), + "fast_movie_speed", + false + } + }, + + AD_EXTRA_GUI_OPTIONS_TERMINATOR +}; + class GroovieMetaEngine : public AdvancedMetaEngine { public: - GroovieMetaEngine() : AdvancedMetaEngine(gameDescriptions, sizeof(GroovieGameDescription), groovieGames) { + GroovieMetaEngine() : AdvancedMetaEngine(gameDescriptions, sizeof(GroovieGameDescription), groovieGames, optionsList) { _singleid = "groovie"; // Use kADFlagUseExtraAsHint in order to distinguish the 11th hour from @@ -222,6 +334,10 @@ public: // replaced with an according explanation. _flags = kADFlagUseExtraAsHint; _guioptions = GUIO3(GUIO_NOSUBTITLES, GUIO_NOSFX, GUIO_NOASPECT); + + // Need MIDI directory to detect 11H Mac Installed + _maxScanDepth = 2; + _directoryGlobs = directoryGlobs; } const char *getName() const { diff --git a/engines/groovie/groovie.cpp b/engines/groovie/groovie.cpp index 726e7cbede..5ade442742 100644 --- a/engines/groovie/groovie.cpp +++ b/engines/groovie/groovie.cpp @@ -30,6 +30,7 @@ #include "groovie/music.h" #include "groovie/resource.h" #include "groovie/roq.h" +#include "groovie/stuffit.h" #include "groovie/vdx.h" #include "common/config-manager.h" @@ -56,15 +57,11 @@ GroovieEngine::GroovieEngine(OSystem *syst, const GroovieGameDescription *gd) : SearchMan.addSubDirectoryMatching(gameDataDir, "groovie"); SearchMan.addSubDirectoryMatching(gameDataDir, "media"); SearchMan.addSubDirectoryMatching(gameDataDir, "system"); + SearchMan.addSubDirectoryMatching(gameDataDir, "MIDI"); _modeSpeed = kGroovieSpeedNormal; - if (ConfMan.hasKey("t7g_speed")) { - Common::String speed = ConfMan.get("t7g_speed"); - if (speed.equals("im_an_ios")) - _modeSpeed = kGroovieSpeediOS; - else if (speed.equals("tweaked")) - _modeSpeed = kGroovieSpeedTweaked; - } + if (ConfMan.hasKey("fast_movie_speed") && ConfMan.getBool("fast_movie_speed")) + _modeSpeed = kGroovieSpeedFast; // Initialize the custom debug levels DebugMan.addDebugChannel(kGroovieDebugAll, "All", "Debug everything"); @@ -93,6 +90,15 @@ GroovieEngine::~GroovieEngine() { } Common::Error GroovieEngine::run() { + if (_gameDescription->version == kGroovieV2 && getPlatform() == Common::kPlatformMacintosh) { + // Load the Mac installer with the lowest priority (in case the user has installed + // the game and has the MIDI folder present; faster to just load them) + Common::Archive *archive = createStuffItArchive("The 11th Hour Installer"); + + if (archive) + SearchMan.add("The 11th Hour Installer", archive); + } + _script = new Script(this, _gameDescription->version); // Initialize the graphics @@ -160,10 +166,10 @@ Common::Error GroovieEngine::run() { // Create the music player switch (getPlatform()) { case Common::kPlatformMacintosh: - // TODO: The 11th Hour Mac uses QuickTime MIDI files - // Right now, since the XMIDI are present and it is still detected as - // the DOS version, we don't have to do anything here. - _musicPlayer = new MusicPlayerMac(this); + if (_gameDescription->version == kGroovieT7G) + _musicPlayer = new MusicPlayerMac_t7g(this); + else + _musicPlayer = new MusicPlayerMac_v2(this); break; case Common::kPlatformIOS: _musicPlayer = new MusicPlayerIOS(this); diff --git a/engines/groovie/groovie.h b/engines/groovie/groovie.h index df2f062757..79abc13b1c 100644 --- a/engines/groovie/groovie.h +++ b/engines/groovie/groovie.h @@ -72,10 +72,16 @@ enum DebugLevels { // the current limitation is 32 debug levels (1 << 31 is the last one) }; +/** + * This enum reflects the available movie speed settings: + * - Normal: play videos at a normal speed + * - Fast: play videos with audio at a fast speed. Videos without audio, + * like teeth animations, are played at their regular speed to avoid + * audio sync issues + */ enum GameSpeed { kGroovieSpeedNormal, - kGroovieSpeediOS, - kGroovieSpeedTweaked + kGroovieSpeedFast }; struct GroovieGameDescription; diff --git a/engines/groovie/module.mk b/engines/groovie/module.mk index 1e89ff66f5..b47eed912b 100644 --- a/engines/groovie/module.mk +++ b/engines/groovie/module.mk @@ -15,6 +15,7 @@ MODULE_OBJS := \ roq.o \ saveload.o \ script.o \ + stuffit.o \ vdx.o # This module can be built as a plugin diff --git a/engines/groovie/music.cpp b/engines/groovie/music.cpp index af929d439b..95637fc407 100644 --- a/engines/groovie/music.cpp +++ b/engines/groovie/music.cpp @@ -678,9 +678,9 @@ void MusicPlayerXMI::setTimbreMT(byte channel, const Timbre &timbre) { } -// MusicPlayerMac +// MusicPlayerMac_t7g -MusicPlayerMac::MusicPlayerMac(GroovieEngine *vm) : MusicPlayerMidi(vm) { +MusicPlayerMac_t7g::MusicPlayerMac_t7g(GroovieEngine *vm) : MusicPlayerMidi(vm) { // Create the parser _midiParser = MidiParser::createParser_SMF(); @@ -701,7 +701,7 @@ MusicPlayerMac::MusicPlayerMac(GroovieEngine *vm) : MusicPlayerMidi(vm) { assert(_vm->_macResFork); } -bool MusicPlayerMac::load(uint32 fileref, bool loop) { +bool MusicPlayerMac_t7g::load(uint32 fileref, bool loop) { debugC(1, kGroovieDebugMIDI | kGroovieDebugAll, "Groovie::Music: Starting the playback of song: %04X", fileref); // First try for compressed MIDI @@ -722,7 +722,7 @@ bool MusicPlayerMac::load(uint32 fileref, bool loop) { return loadParser(file, loop); } -Common::SeekableReadStream *MusicPlayerMac::decompressMidi(Common::SeekableReadStream *stream) { +Common::SeekableReadStream *MusicPlayerMac_t7g::decompressMidi(Common::SeekableReadStream *stream) { // Initialize an output buffer of the given size uint32 size = stream->readUint32BE(); byte *output = (byte *)malloc(size); @@ -768,6 +768,52 @@ Common::SeekableReadStream *MusicPlayerMac::decompressMidi(Common::SeekableReadS return new Common::MemoryReadStream(output, size, DisposeAfterUse::YES); } +// MusicPlayerMac_v2 + +MusicPlayerMac_v2::MusicPlayerMac_v2(GroovieEngine *vm) : MusicPlayerMidi(vm) { + // Create the parser + _midiParser = MidiParser::createParser_QT(); + + // Create the driver + MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM); + _driver = MidiDriver::createMidi(dev); + assert(_driver); + + _driver->open(); // TODO: Handle return value != 0 (indicating an error) + + // Set the parser's driver + _midiParser->setMidiDriver(this); + + // Set the timer rate + _midiParser->setTimerRate(_driver->getBaseTempo()); +} + +bool MusicPlayerMac_v2::load(uint32 fileref, bool loop) { + debugC(1, kGroovieDebugMIDI | kGroovieDebugAll, "Groovie::Music: Starting the playback of song: %04X", fileref); + + // Find correct filename + ResInfo info; + _vm->_resMan->getResInfo(fileref, info); + uint len = info.filename.size(); + if (len < 4) + return false; // This shouldn't actually occur + + // Remove the extension and add ".mov" + info.filename.deleteLastChar(); + info.filename.deleteLastChar(); + info.filename.deleteLastChar(); + info.filename += "mov"; + + Common::SeekableReadStream *file = SearchMan.createReadStreamForMember(info.filename); + + if (!file) { + warning("Could not find file '%s'", info.filename.c_str()); + return false; + } + + return loadParser(file, loop); +} + MusicPlayerIOS::MusicPlayerIOS(GroovieEngine *vm) : MusicPlayer(vm) { vm->getTimerManager()->installTimerProc(&onTimer, 50 * 1000, this, "groovieMusic"); } diff --git a/engines/groovie/music.h b/engines/groovie/music.h index cc852aa8dc..92e9c8b487 100644 --- a/engines/groovie/music.h +++ b/engines/groovie/music.h @@ -150,9 +150,9 @@ private: void setTimbreMT(byte channel, const Timbre &timbre); }; -class MusicPlayerMac : public MusicPlayerMidi { +class MusicPlayerMac_t7g : public MusicPlayerMidi { public: - MusicPlayerMac(GroovieEngine *vm); + MusicPlayerMac_t7g(GroovieEngine *vm); protected: bool load(uint32 fileref, bool loop); @@ -161,6 +161,14 @@ private: Common::SeekableReadStream *decompressMidi(Common::SeekableReadStream *stream); }; +class MusicPlayerMac_v2 : public MusicPlayerMidi { +public: + MusicPlayerMac_v2(GroovieEngine *vm); + +protected: + bool load(uint32 fileref, bool loop); +}; + class MusicPlayerIOS : public MusicPlayer { public: MusicPlayerIOS(GroovieEngine *vm); diff --git a/engines/groovie/roq.cpp b/engines/groovie/roq.cpp index 35d7ecf886..72a61fefb2 100644 --- a/engines/groovie/roq.cpp +++ b/engines/groovie/roq.cpp @@ -288,19 +288,18 @@ bool ROQPlayer::processBlockInfo(ROQBlockHeader &blockHeader) { // them it should be just fine. _currBuf->create(width, height, Graphics::PixelFormat(3, 0, 0, 0, 0, 0, 0, 0, 0)); _prevBuf->create(width, height, Graphics::PixelFormat(3, 0, 0, 0, 0, 0, 0, 0, 0)); + } - // Clear the buffers with black YUV values - byte *ptr1 = (byte *)_currBuf->getBasePtr(0, 0); - byte *ptr2 = (byte *)_prevBuf->getBasePtr(0, 0); - for (int i = 0; i < width * height; i++) { - *ptr1++ = 0; - *ptr1++ = 128; - *ptr1++ = 128; - *ptr2++ = 0; - *ptr2++ = 128; - *ptr2++ = 128; - } - + // Clear the buffers with black YUV values + byte *ptr1 = (byte *)_currBuf->getBasePtr(0, 0); + byte *ptr2 = (byte *)_prevBuf->getBasePtr(0, 0); + for (int i = 0; i < width * height; i++) { + *ptr1++ = 0; + *ptr1++ = 128; + *ptr1++ = 128; + *ptr2++ = 0; + *ptr2++ = 128; + *ptr2++ = 128; } return true; @@ -405,7 +404,7 @@ void ROQPlayer::processBlockQuadVectorBlock(int baseX, int baseY, int8 Mx, int8 } void ROQPlayer::processBlockQuadVectorBlockSub(int baseX, int baseY, int8 Mx, int8 My) { - debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Processing quad vector sub block"); + debugC(6, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Processing quad vector sub block"); uint16 codingType = getCodingType(); switch (codingType) { @@ -433,7 +432,7 @@ void ROQPlayer::processBlockQuadVectorBlockSub(int baseX, int baseY, int8 Mx, in bool ROQPlayer::processBlockStill(ROQBlockHeader &blockHeader) { debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Processing still (JPEG) block"); - warning("Groovie::ROQ: JPEG frame (unfinshed)"); + warning("Groovie::ROQ: JPEG frame (unfinished)"); Graphics::JPEGDecoder *jpg = new Graphics::JPEGDecoder(); jpg->loadStream(*_file); diff --git a/engines/groovie/stuffit.cpp b/engines/groovie/stuffit.cpp new file mode 100644 index 0000000000..37f12585e7 --- /dev/null +++ b/engines/groovie/stuffit.cpp @@ -0,0 +1,537 @@ +/* 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. + * + */ + +// Based on the StuffIt code in ResidualVM +// StuffIt parsing based on http://code.google.com/p/theunarchiver/wiki/StuffItFormat +// Compression 14 based on libxad (http://sourceforge.net/projects/libxad/) + +#include "groovie/stuffit.h" + +#include "common/archive.h" +#include "common/bitstream.h" +#include "common/debug.h" +#include "common/hash-str.h" +#include "common/hashmap.h" +#include "common/memstream.h" +#include "common/substream.h" + +namespace Groovie { + +struct SIT14Data; + +class StuffItArchive : public Common::Archive { +public: + StuffItArchive(); + ~StuffItArchive(); + + bool open(const Common::String &filename); + void close(); + bool isOpen() const { return _stream != 0; } + + // Common::Archive API implementation + bool hasFile(const Common::String &name) const; + int listMembers(Common::ArchiveMemberList &list) const; + const Common::ArchiveMemberPtr getMember(const Common::String &name) const; + Common::SeekableReadStream *createReadStreamForMember(const Common::String &name) const; + +private: + struct FileEntry { + byte compression; + uint32 uncompressedSize; + uint32 compressedSize; + uint32 offset; + }; + + Common::SeekableReadStream *_stream; + + typedef Common::HashMap<Common::String, FileEntry, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> FileMap; + FileMap _map; + + // Decompression Functions + Common::SeekableReadStream *decompress14(Common::SeekableReadStream *src, uint32 uncompressedSize) const; + + // Decompression Helpers + void update14(uint16 first, uint16 last, byte *code, uint16 *freq) const; + void readTree14(Common::BitStream *bits, SIT14Data *dat, uint16 codesize, uint16 *result) const; +}; + +StuffItArchive::StuffItArchive() : Common::Archive() { + _stream = 0; +} + +StuffItArchive::~StuffItArchive() { + close(); +} + +// Some known values of StuffIt FourCC's +// 11H Mac in particular uses ST46 +static const uint32 s_magicNumbers[] = { + MKTAG('S', 'I', 'T', '!'), MKTAG('S', 'T', '6', '5'), MKTAG('S', 'T', '5', '0'), + MKTAG('S', 'T', '6', '0'), MKTAG('S', 'T', 'i', 'n'), MKTAG('S', 'T', 'i', '2'), + MKTAG('S', 'T', 'i', '3'), MKTAG('S', 'T', 'i', '4'), MKTAG('S', 'T', '4', '6') +}; + +bool StuffItArchive::open(const Common::String &filename) { + close(); + + _stream = SearchMan.createReadStreamForMember(filename); + + if (!_stream) + return false; + + uint32 tag = _stream->readUint32BE(); + + // Check all the possible FourCC's + bool found = false; + for (int i = 0; i < ARRAYSIZE(s_magicNumbers); i++) { + if (tag == s_magicNumbers[i]) { + found = true; + break; + } + } + + // Didn't find one, let's bail out + if (!found) { + close(); + return false; + } + + /* uint16 fileCount = */ _stream->readUint16BE(); + /* uint32 archiveSize = */ _stream->readUint32BE(); + + // Some sort of second magic number + if (_stream->readUint32BE() != MKTAG('r', 'L', 'a', 'u')) { + close(); + return false; + } + + /* byte version = */ _stream->readByte(); // meaning not clear + + _stream->skip(7); // unknown + + while (_stream->pos() < _stream->size() && !_stream->eos()) { + byte resForkCompression = _stream->readByte(); + byte dataForkCompression = _stream->readByte(); + + byte fileNameLength = _stream->readByte(); + Common::String name; + + for (byte i = 0; i < fileNameLength; i++) + name += (char)_stream->readByte(); + + // Skip remaining bytes + _stream->skip(63 - fileNameLength); + + /* uint32 fileType = */ _stream->readUint32BE(); + /* uint32 fileCreator = */ _stream->readUint32BE(); + /* uint16 finderFlags = */ _stream->readUint16BE(); + /* uint32 creationDate = */ _stream->readUint32BE(); + /* uint32 modificationDate = */ _stream->readUint32BE(); + uint32 resForkUncompressedSize = _stream->readUint32BE(); + uint32 dataForkUncompressedSize = _stream->readUint32BE(); + uint32 resForkCompressedSize = _stream->readUint32BE(); + uint32 dataForkCompressedSize = _stream->readUint32BE(); + /* uint16 resForkCRC = */ _stream->readUint16BE(); + /* uint16 dataForkCRC = */ _stream->readUint16BE(); + _stream->skip(6); // unknown + /* uint16 headerCRC = */ _stream->readUint16BE(); + + // Ignore directories for now + if (dataForkCompression == 32 || dataForkCompression == 33) + continue; + + if (dataForkUncompressedSize != 0) { + // We have a data fork + + FileEntry entry; + entry.compression = dataForkCompression; + entry.uncompressedSize = dataForkUncompressedSize; + entry.compressedSize = dataForkCompressedSize; + entry.offset = _stream->pos() + resForkCompressedSize; + _map[name] = entry; + + debug(0, "StuffIt file '%s', Compression = %d", name.c_str(), entry.compression); + } + + if (resForkUncompressedSize != 0) { + // We have a resource fork + + // Add a .rsrc extension so we know it's the resource fork + name += ".rsrc"; + + FileEntry entry; + entry.compression = resForkCompression; + entry.uncompressedSize = resForkUncompressedSize; + entry.compressedSize = resForkCompressedSize; + entry.offset = _stream->pos(); + _map[name] = entry; + + debug(0, "StuffIt file '%s', Compression = %d", name.c_str(), entry.compression); + } + + // Go to the next entry + _stream->skip(dataForkCompressedSize + resForkCompressedSize); + } + + return true; +} + +void StuffItArchive::close() { + delete _stream; _stream = 0; + _map.clear(); +} + +bool StuffItArchive::hasFile(const Common::String &name) const { + return _map.contains(name); +} + +int StuffItArchive::listMembers(Common::ArchiveMemberList &list) const { + for (FileMap::const_iterator it = _map.begin(); it != _map.end(); it++) + list.push_back(getMember(it->_key)); + + return _map.size(); +} + +const Common::ArchiveMemberPtr StuffItArchive::getMember(const Common::String &name) const { + return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(name, this)); +} + +Common::SeekableReadStream *StuffItArchive::createReadStreamForMember(const Common::String &name) const { + if (!_stream || !_map.contains(name)) + return 0; + + const FileEntry &entry = _map[name]; + + if (entry.compression & 0xF0) + error("Unhandled StuffIt encryption"); + + Common::SeekableSubReadStream subStream(_stream, entry.offset, entry.offset + entry.compressedSize); + + // We currently only support type 14 compression + switch (entry.compression) { + case 0: // Uncompressed + return subStream.readStream(subStream.size()); + case 14: // Installer + return decompress14(&subStream, entry.uncompressedSize); + default: + error("Unhandled StuffIt compression %d", entry.compression); + } + + return 0; +} + +void StuffItArchive::update14(uint16 first, uint16 last, byte *code, uint16 *freq) const { + uint16 i, j; + + while (last - first > 1) { + i = first; + j = last; + + do { + while (++i < last && code[first] > code[i]) + ; + + while (--j > first && code[first] < code[j]) + ; + + if (j > i) { + SWAP(code[i], code[j]); + SWAP(freq[i], freq[j]); + } + } while (j > i); + + if (first != j) { + SWAP(code[first], code[j]); + SWAP(freq[first], freq[j]); + + i = j + 1; + + if (last - i <= j - first) { + update14(i, last, code, freq); + last = j; + } else { + update14(first, j, code, freq); + first = i; + } + } else { + ++first; + } + } +} + +struct SIT14Data { + byte code[308]; + byte codecopy[308]; + uint16 freq[308]; + uint32 buff[308]; + + byte var1[52]; + uint16 var2[52]; + uint16 var3[75 * 2]; + + byte var4[76]; + uint32 var5[75]; + byte var6[1024]; + uint16 var7[308 * 2]; + byte var8[0x4000]; + + byte window[0x40000]; +}; + +// Realign to a byte boundary +#define ALIGN_BITS(b) \ + if (b->pos() & 7) \ + b->skip(8 - (b->pos() & 7)) + +void StuffItArchive::readTree14(Common::BitStream *bits, SIT14Data *dat, uint16 codesize, uint16 *result) const { + uint32 i, l, n; + uint32 k = bits->getBit(); + uint32 j = bits->getBits(2) + 2; + uint32 o = bits->getBits(3) + 1; + uint32 size = 1 << j; + uint32 m = size - 1; + k = k ? (m - 1) : 0xFFFFFFFF; + + if (bits->getBits(2) & 1) { // skip 1 bit! + // requirements for this call: dat->buff[32], dat->code[32], dat->freq[32*2] + readTree14(bits, dat, size, dat->freq); + + for (i = 0; i < codesize; ) { + l = 0; + + do { + l = dat->freq[l + bits->getBit()]; + n = size << 1; + } while (n > l); + + l -= n; + + if (k != l) { + if (l == m) { + l = 0; + + do { + l = dat->freq[l + bits->getBit()]; + n = size << 1; + } while (n > l); + + l += 3 - n; + + while (l--) { + dat->code[i] = dat->code[i - 1]; + ++i; + } + } else { + dat->code[i++] = l + o; + } + } else { + dat->code[i++] = 0; + } + } + } else { + for (i = 0; i < codesize; ) { + l = bits->getBits(j); + + if (k != l) { + if (l == m) { + l = bits->getBits(j) + 3; + + while (l--) { + dat->code[i] = dat->code[i - 1]; + ++i; + } + } else { + dat->code[i++] = l+o; + } + } else { + dat->code[i++] = 0; + } + } + } + + for (i = 0; i < codesize; ++i) { + dat->codecopy[i] = dat->code[i]; + dat->freq[i] = i; + } + + update14(0, codesize, dat->codecopy, dat->freq); + + for (i = 0; i < codesize && !dat->codecopy[i]; ++i) + ; // find first nonempty + + for (j = 0; i < codesize; ++i, ++j) { + if (i) + j <<= (dat->codecopy[i] - dat->codecopy[i - 1]); + + k = dat->codecopy[i]; m = 0; + + for (l = j; k--; l >>= 1) + m = (m << 1) | (l & 1); + + dat->buff[dat->freq[i]] = m; + } + + for (i = 0; i < (uint32)codesize * 2; ++i) + result[i] = 0; + + j = 2; + + for (i = 0; i < codesize; ++i) { + l = 0; + m = dat->buff[i]; + + for (k = 0; k < dat->code[i]; ++k) { + l += (m & 1); + + if (dat->code[i] - 1 <= (int32)k) { + result[l] = codesize * 2 + i; + } else { + if (!result[l]) { + result[l] = j; + j += 2; + } + + l = result[l]; + } + + m >>= 1; + } + } + + ALIGN_BITS(bits); +} + +#define OUTPUT_VAL(x) \ + out.writeByte(x); \ + dat->window[j++] = x; \ + j &= 0x3FFFF + +Common::SeekableReadStream *StuffItArchive::decompress14(Common::SeekableReadStream *src, uint32 uncompressedSize) const { + byte *dst = (byte *)malloc(uncompressedSize); + Common::MemoryWriteStream out(dst, uncompressedSize); + + Common::BitStream *bits = new Common::BitStream8LSB(src); + + uint32 i, j, k, l, m, n; + + SIT14Data *dat = new SIT14Data(); + + // initialization + for (i = k = 0; i < 52; ++i) { + dat->var2[i] = k; + k += (1 << (dat->var1[i] = ((i >= 4) ? ((i - 4) >> 2) : 0))); + } + + for (i = 0; i < 4; ++i) + dat->var8[i] = i; + + for (m = 1, l = 4; i < 0x4000; m <<= 1) // i is 4 + for (n = l+4; l < n; ++l) + for (j = 0; j < m; ++j) + dat->var8[i++] = l; + + for (i = 0, k = 1; i < 75; ++i) { + dat->var5[i] = k; + k += (1 << (dat->var4[i] = (i >= 3 ? ((i - 3) >> 2) : 0))); + } + + for (i = 0; i < 4; ++i) + dat->var6[i] = i - 1; + + for (m = 1, l = 3; i < 0x400; m <<= 1) // i is 4 + for (n = l + 4; l < n; ++l) + for (j = 0; j < m; ++j) + dat->var6[i++] = l; + + m = bits->getBits(16); // number of blocks + j = 0; // window position + + while (m-- && !bits->eos()) { + bits->getBits(16); // skip crunched block size + bits->getBits(16); + n = bits->getBits(16); // number of uncrunched bytes + n |= bits->getBits(16) << 16; + readTree14(bits, dat, 308, dat->var7); + readTree14(bits, dat, 75, dat->var3); + + while (n && !bits->eos()) { + for (i = 0; i < 616;) + i = dat->var7[i + bits->getBit()]; + + i -= 616; + + if (i < 0x100) { + OUTPUT_VAL(i); + --n; + } else { + i -= 0x100; + k = dat->var2[i]+4; + i = dat->var1[i]; + + if (i) + k += bits->getBits(i); + + for (i = 0; i < 150;) + i = dat->var3[i + bits->getBit()]; + + i -= 150; + l = dat->var5[i]; + i = dat->var4[i]; + + if (i) + l += bits->getBits(i); + + n -= k; + l = j + 0x40000 - l; + + while (k--) { + l &= 0x3FFFF; + OUTPUT_VAL(dat->window[l]); + l++; + } + } + } + + ALIGN_BITS(bits); + } + + delete dat; + delete bits; + + return new Common::MemoryReadStream(dst, uncompressedSize, DisposeAfterUse::YES); +} + +#undef OUTPUT_VAL +#undef ALIGN_BITS + +Common::Archive *createStuffItArchive(const Common::String &fileName) { + StuffItArchive *archive = new StuffItArchive(); + + if (!archive->open(fileName)) { + delete archive; + return 0; + } + + return archive; +} + +} // End of namespace Groovie diff --git a/engines/agos/installshield_cab.h b/engines/groovie/stuffit.h index f7e8bed277..44f593dbea 100644 --- a/engines/agos/installshield_cab.h +++ b/engines/groovie/stuffit.h @@ -20,22 +20,24 @@ * */ -#include "common/archive.h" -#include "common/str.h" +#ifndef GROOVIE_STUFFIT_H +#define GROOVIE_STUFFIT_H -#ifndef AGOS_INSTALLSHIELD_CAB_H -#define AGOS_INSTALLSHIELD_CAB_H +namespace Common { +class Archive; +class String; +} -namespace AGOS { +namespace Groovie { /** * This factory method creates an Archive instance corresponding to the content - * of the InstallShield compressed file with the given name. + * of the StuffIt compressed file. * * May return 0 in case of a failure. */ -Common::Archive *makeInstallShieldArchive(const Common::String &name); +Common::Archive *createStuffItArchive(const Common::String &fileName); -} // End of namespace AGOS +} // End of namespace Groovie #endif diff --git a/engines/groovie/vdx.cpp b/engines/groovie/vdx.cpp index b3fcf462b2..8786e75488 100644 --- a/engines/groovie/vdx.cpp +++ b/engines/groovie/vdx.cpp @@ -88,7 +88,7 @@ uint16 VDXPlayer::loadInternal() { // Enable highspeed if we're not obeying fps, and not marked as special // This will be disabled in chunk audio if we're actually an audio vdx - if ( _vm->_modeSpeed == kGroovieSpeediOS || (_vm->_modeSpeed == kGroovieSpeedTweaked && ((_flags & (1 << 15)) == 0))) + if (_vm->_modeSpeed == kGroovieSpeedFast && ((_flags & (1 << 15)) == 0)) setOverrideSpeed(true); if (_flagOnePrev && !_flagOne && !_flagEight) { diff --git a/engines/hugo/file.cpp b/engines/hugo/file.cpp index 15ee06c82a..1758f3f6a5 100644 --- a/engines/hugo/file.cpp +++ b/engines/hugo/file.cpp @@ -32,7 +32,11 @@ #include "common/savefile.h" #include "common/textconsole.h" #include "common/config-manager.h" + +#include "graphics/surface.h" +#include "graphics/decoders/pcx.h" #include "graphics/thumbnail.h" + #include "gui/saveload.h" #include "hugo/hugo.h" @@ -88,66 +92,33 @@ const char *FileManager::getUifFilename() const { } /** - * Convert 4 planes (RGBI) data to 8-bit DIB format - * Return original plane data ptr - */ -byte *FileManager::convertPCC(byte *p, const uint16 y, const uint16 bpl, ImagePtr dataPtr) const { - debugC(2, kDebugFile, "convertPCC(byte *p, %d, %d, ImagePtr dataPtr)", y, bpl); - - dataPtr += y * bpl * 8; // Point to correct DIB line - for (int16 r = 0, g = bpl, b = g + bpl, i = b + bpl; r < bpl; r++, g++, b++, i++) { // Each byte in all planes - for (int8 bit = 7; bit >= 0; bit--) { // Each bit in byte - *dataPtr++ = (((p[r] >> bit & 1) << 0) | - ((p[g] >> bit & 1) << 1) | - ((p[b] >> bit & 1) << 2) | - ((p[i] >> bit & 1) << 3)); - } - } - return p; -} - -/** * Read a pcx file of length len. Use supplied seqPtr and image_p or * allocate space if NULL. Name used for errors. Returns address of seqPtr * Set first TRUE to initialize b_index (i.e. not reading a sequential image in file). */ -Seq *FileManager::readPCX(Common::ReadStream &f, Seq *seqPtr, byte *imagePtr, const bool firstFl, const char *name) { +Seq *FileManager::readPCX(Common::SeekableReadStream &f, Seq *seqPtr, byte *imagePtr, const bool firstFl, const char *name) { debugC(1, kDebugFile, "readPCX(..., %s)", name); - // Read in the PCC header and check consistency - _PCCHeader._mfctr = f.readByte(); - _PCCHeader._vers = f.readByte(); - _PCCHeader._enc = f.readByte(); - _PCCHeader._bpx = f.readByte(); - _PCCHeader._x1 = f.readUint16LE(); - _PCCHeader._y1 = f.readUint16LE(); - _PCCHeader._x2 = f.readUint16LE(); - _PCCHeader._y2 = f.readUint16LE(); - _PCCHeader._xres = f.readUint16LE(); - _PCCHeader._yres = f.readUint16LE(); - f.read(_PCCHeader._palette, sizeof(_PCCHeader._palette)); - _PCCHeader._vmode = f.readByte(); - _PCCHeader._planes = f.readByte(); - _PCCHeader._bytesPerLine = f.readUint16LE(); - f.read(_PCCHeader._fill2, sizeof(_PCCHeader._fill2)); - - if (_PCCHeader._mfctr != 10) - error("Bad data file format: %s", name); - // Allocate memory for Seq if 0 if (seqPtr == 0) { if ((seqPtr = (Seq *)malloc(sizeof(Seq))) == 0) error("Insufficient memory to run game."); } + Graphics::PCXDecoder pcx; + if (!pcx.loadStream(f)) + error("Error while reading PCX image"); + + const Graphics::Surface *pcxSurface = pcx.getSurface(); + if (pcxSurface->format.bytesPerPixel != 1) + error("Invalid bytes per pixel in PCX surface (%d)", pcxSurface->format.bytesPerPixel); + // Find size of image data in 8-bit DIB format // Note save of x2 - marks end of valid data before garbage - uint16 bytesPerLine4 = _PCCHeader._bytesPerLine * 4; // 4-bit bpl - seqPtr->_bytesPerLine8 = bytesPerLine4 * 2; // 8-bit bpl - seqPtr->_lines = _PCCHeader._y2 - _PCCHeader._y1 + 1; - seqPtr->_x2 = _PCCHeader._x2 - _PCCHeader._x1 + 1; + seqPtr->_lines = pcxSurface->h; + seqPtr->_x2 = seqPtr->_bytesPerLine8 = pcxSurface->w; // Size of the image - uint16 size = seqPtr->_lines * seqPtr->_bytesPerLine8; + uint16 size = pcxSurface->w * pcxSurface->h; // Allocate memory for image data if NULL if (imagePtr == 0) @@ -156,26 +127,9 @@ Seq *FileManager::readPCX(Common::ReadStream &f, Seq *seqPtr, byte *imagePtr, co assert(imagePtr); seqPtr->_imagePtr = imagePtr; + for (uint16 y = 0; y < pcxSurface->h; y++) + memcpy(imagePtr + y * pcxSurface->w, pcxSurface->getBasePtr(0, y), pcxSurface->w); - // Process the image data, converting to 8-bit DIB format - uint16 y = 0; // Current line index - byte pline[kXPix]; // Hold 4 planes of data - byte *p = pline; // Ptr to above - while (y < seqPtr->_lines) { - byte c = f.readByte(); - if ((c & kRepeatMask) == kRepeatMask) { - byte d = f.readByte(); // Read data byte - for (int i = 0; i < (c & kLengthMask); i++) { - *p++ = d; - if ((uint16)(p - pline) == bytesPerLine4) - p = convertPCC(pline, y++, _PCCHeader._bytesPerLine, imagePtr); - } - } else { - *p++ = c; - if ((uint16)(p - pline) == bytesPerLine4) - p = convertPCC(pline, y++, _PCCHeader._bytesPerLine, imagePtr); - } - } return seqPtr; } diff --git a/engines/hugo/file.h b/engines/hugo/file.h index 1438bd2054..44f257a2af 100644 --- a/engines/hugo/file.h +++ b/engines/hugo/file.h @@ -112,16 +112,13 @@ protected: Common::File _sceneryArchive1; // Handle for scenery file Common::File _objectsArchive; // Handle for objects file - PCCHeader _PCCHeader; - - Seq *readPCX(Common::ReadStream &f, Seq *seqPtr, byte *imagePtr, const bool firstFl, const char *name); + Seq *readPCX(Common::SeekableReadStream &f, Seq *seqPtr, byte *imagePtr, const bool firstFl, const char *name); // If this is the first call, read the lookup table bool _hasReadHeader; SoundHdr _soundHdr[kMaxSounds]; // Sound lookup table private: - byte *convertPCC(byte *p, const uint16 y, const uint16 bpl, ImagePtr dataPtr) const; UifHdr *getUIFHeader(const Uif id); }; diff --git a/engines/hugo/parser.cpp b/engines/hugo/parser.cpp index 5fdb2026a7..2585c64fd8 100644 --- a/engines/hugo/parser.cpp +++ b/engines/hugo/parser.cpp @@ -235,7 +235,7 @@ void Parser::charHandler() { if (_cmdLineIndex >= kMaxLineSize) { //MessageBeep(MB_ICONASTERISK); warning("STUB: MessageBeep() - Command line too long"); - } else if (isprint(static_cast<unsigned char>(c))) { + } else if (Common::isPrint(c)) { _cmdLine[_cmdLineIndex++] = c; _cmdLine[_cmdLineIndex] = '\0'; } diff --git a/engines/kyra/chargen.cpp b/engines/kyra/chargen.cpp index 54e1abcc2c..80a95da047 100644 --- a/engines/kyra/chargen.cpp +++ b/engines/kyra/chargen.cpp @@ -193,7 +193,7 @@ bool CharacterGenerator::start(EoBCharacter *characters, uint8 ***faceShapes) { } if (inputFlag & 0x8000) { - inputFlag = (inputFlag & 0x0f) - 1; + inputFlag = (inputFlag & 0x0F) - 1; if (inputFlag == 4) { loop = false; } else { @@ -402,7 +402,7 @@ int CharacterGenerator::viewDeleteCharacter() { } if (inputFlag & 0x8000) { - inputFlag = (inputFlag & 0x0f) - 1; + inputFlag = (inputFlag & 0x0F) - 1; if (inputFlag == 4) { res = 1; loop = false; @@ -524,7 +524,7 @@ int CharacterGenerator::classMenu(int raceSex) { while (res == -1 && !_vm->shouldQuit()) { updateMagicShapes(); - int in = getInput(0) & 0xff; + int in = getInput(0) & 0xFF; Common::Point mp = _vm->getMousePos(); if (in == _vm->_keyMap[Common::KEYCODE_ESCAPE] || _vm->_gui->_menuLastInFlags == _vm->_keyMap[Common::KEYCODE_ESCAPE] || _vm->_gui->_menuLastInFlags == _vm->_keyMap[Common::KEYCODE_b]) { @@ -572,7 +572,7 @@ int CharacterGenerator::alignmentMenu(int cClass) { while (res == -1 && !_vm->shouldQuit()) { updateMagicShapes(); - int in = getInput(0) & 0xff; + int in = getInput(0) & 0xFF; Common::Point mp = _vm->getMousePos(); if (in == _vm->_keyMap[Common::KEYCODE_ESCAPE] || _vm->_gui->_menuLastInFlags == _vm->_keyMap[Common::KEYCODE_ESCAPE] || _vm->_gui->_menuLastInFlags == _vm->_keyMap[Common::KEYCODE_b]) { @@ -658,14 +658,14 @@ void CharacterGenerator::generateStats(int index) { sv[i] = _chargenMaxStats[i]; } - c->strengthCur = c->strengthMax = sv[0] & 0xff; + c->strengthCur = c->strengthMax = sv[0] & 0xFF; c->strengthExtCur = c->strengthExtMax = sv[0] >> 8; - c->intelligenceCur = c->intelligenceMax = sv[1] & 0xff; - c->wisdomCur = c->wisdomMax = sv[2] & 0xff; - c->dexterityCur = c->dexterityMax = sv[3] & 0xff; - c->constitutionCur = c->constitutionMax = sv[4] & 0xff; - c->charismaCur = c->charismaMax = sv[5] & 0xff; - c->armorClass = 10 + _vm->getDexterityArmorClassModifier(sv[3] & 0xff); + c->intelligenceCur = c->intelligenceMax = sv[1] & 0xFF; + c->wisdomCur = c->wisdomMax = sv[2] & 0xFF; + c->dexterityCur = c->dexterityMax = sv[3] & 0xFF; + c->constitutionCur = c->constitutionMax = sv[4] & 0xFF; + c->charismaCur = c->charismaMax = sv[5] & 0xFF; + c->armorClass = 10 + _vm->getDexterityArmorClassModifier(sv[3] & 0xFF); c->hitPointsCur = 0; for (int l = 0; l < 3; l++) { @@ -817,7 +817,7 @@ void CharacterGenerator::faceSelectMenu() { } else if (in == _vm->_keyMap[Common::KEYCODE_RETURN] || in == _vm->_keyMap[Common::KEYCODE_KP5]) { in = 3; } else if (in & 0x8000) { - in &= 0xff; + in &= 0xFF; } else { in = 0; } @@ -1017,7 +1017,7 @@ int CharacterGenerator::modifyStat(int index, int8 *stat1, int8 *stat2) { ci = -2; } else if (inputFlag & 0x8000) { - inputFlag = (inputFlag & 0x0f) - 1; + inputFlag = (inputFlag & 0x0F) - 1; if (index != inputFlag) { ci = inputFlag; loop = false; @@ -1037,7 +1037,7 @@ int CharacterGenerator::modifyStat(int index, int8 *stat1, int8 *stat2) { v2--; } - v1 = CLIP<uint8>(v1, _chargenMinStats[index], _chargenMaxStats[index] & 0xff); + v1 = CLIP<uint8>(v1, _chargenMinStats[index], _chargenMaxStats[index] & 0xFF); v2 = (v1 == 18 && _chargenMaxStats[index] >= 19) ? CLIP<uint8>(v2, 0, 100) : 0; if (s2) *s2 = v2; @@ -1206,7 +1206,7 @@ void CharacterGenerator::finish() { static const int8 itemList1[] = { 1, 2, 0, 17, -1, 0, 0 }; static const int8 itemList2[] = { 2, 56, 1, 17, 31, 0, 1, 23, 1, 17, 31, 0, 1 }; static const int8 itemList3[] = { 2, 1, 1, 17, 31, 1, 1, 1, 0, 17, 31, 2, 1 }; - static const int8 *itemList[] = { itemList0, itemList1, itemList2, itemList3 }; + static const int8 *const itemList[] = { itemList0, itemList1, itemList2, itemList3 }; for (int i = 0; i < 4; i++) { EoBCharacter *c = &_characters[i]; @@ -1496,7 +1496,7 @@ TransferPartyWiz::~TransferPartyWiz() { } bool TransferPartyWiz::start() { - _screen->copyPage(0, _vm->_useHiResDithering ? 1 : 12); + _screen->copyPage(0, 12); if (!selectAndLoadTransferFile()) return false; @@ -1536,7 +1536,7 @@ bool TransferPartyWiz::start() { bool TransferPartyWiz::selectAndLoadTransferFile() { do { - _screen->copyPage(_vm->_useHiResDithering ? 1 : 12, 0); + _screen->copyPage(12, 0); if (transferFileDialogue(_vm->_savegameFilename)) break; } while (_vm->_gui->confirmDialogue2(15, 68, 1)); @@ -1566,7 +1566,7 @@ bool TransferPartyWiz::selectAndLoadTransferFile() { return false; Common::String target = _vm->_gui->transferTargetMenu(eobTargets); - _screen->copyPage(_vm->_useHiResDithering ? 1 : 12, 0); + _screen->copyPage(12, 0); if (target.empty()) return true; @@ -1579,10 +1579,10 @@ bool TransferPartyWiz::selectAndLoadTransferFile() { return true; } - _screen->copyPage(_vm->_useHiResDithering ? 1 : 12, 0); + _screen->copyPage(12, 0); bool result = _vm->_gui->transferFileMenu(target, dest); - _screen->copyPage(_vm->_useHiResDithering ? 1 : 12, 0); + _screen->copyPage(12, 0); return result; } @@ -1614,7 +1614,7 @@ int TransferPartyWiz::selectCharactersMenu() { bool update = false; for (bool loop = true; loop && (!_vm->shouldQuit());) { - int inputFlag = _vm->checkInput(0, false, 0) & 0x8ff; + int inputFlag = _vm->checkInput(0, false, 0) & 0x8FF; _vm->removeInputTop(); if (inputFlag) { @@ -1827,7 +1827,7 @@ Item TransferPartyWiz::convertItem(Item eob1Item) { itm2->flags = itm1->flags | 0x40; itm2->icon = itm1->icon; itm2->type = itm1->type; - itm2->level = 0xff; + itm2->level = 0xFF; switch (itm2->type) { case 35: @@ -1850,7 +1850,7 @@ Item TransferPartyWiz::convertItem(Item eob1Item) { return 0; } itm2->value = itm1->value; - itm2->flags = ((itm1->flags & 0x3f) + 3) | 0x40; + itm2->flags = ((itm1->flags & 0x3F) + 3) | 0x40; break; case 18: itm2->icon = 19; @@ -1860,7 +1860,7 @@ Item TransferPartyWiz::convertItem(Item eob1Item) { break; } - switch ((_vm->_itemTypes[itm2->type].extraProperties & 0x7f) - 1) { + switch ((_vm->_itemTypes[itm2->type].extraProperties & 0x7F) - 1) { case 0: case 1: case 2: diff --git a/engines/kyra/darkmoon.cpp b/engines/kyra/darkmoon.cpp index 16bd3dad58..130fb10df3 100644 --- a/engines/kyra/darkmoon.cpp +++ b/engines/kyra/darkmoon.cpp @@ -136,7 +136,7 @@ void DarkMoonEngine::updateUsedCharacterHandItem(int charIndex, int slot) { if (itm->type == 48 || itm->type == 62) { if (itm->value == 5) return; - int charges = itm->flags & 0x3f; + int charges = itm->flags & 0x3F; if (--charges) --itm->flags; else @@ -158,10 +158,7 @@ void DarkMoonEngine::generateMonsterPalettes(const char *file, int16 monsterInde int colx = 302 + 3 * i; for (int ii = 0; ii < 16; ii++) { - // Don't use getPagePixel() here, since in EGA mode it will try to - // undither the pixel (although the shape bitmap is undithered already) - uint8 col = _screen->getCPagePtr(_screen->_curPage | 1)[(184 + ii) * Screen::SCREEN_W + colx]; - + uint8 col = _screen->getPagePixel(_screen->_curPage, colx, 184 + ii); int iii = 0; for (; iii < 16; iii++) { if (tmpPal[iii] == col) { @@ -178,9 +175,7 @@ void DarkMoonEngine::generateMonsterPalettes(const char *file, int16 monsterInde memcpy(tmpPal, _monsterShapes[dci] + 4, 16); for (int iii = 0; iii < 16; iii++) { - // Don't use getPagePixel() here, since in EGA mode it will try to - // undither the pixel (although the shape bitmap is undithered already) - uint8 col = _screen->getCPagePtr(_screen->_curPage | 1)[(184 + iii) * Screen::SCREEN_W + colx + ii]; + uint8 col = _screen->getPagePixel(_screen->_curPage, colx + ii, 184 + iii); if (newPal[iii]) tmpPal[newPal[iii]] = col; } @@ -455,7 +450,7 @@ void DarkMoonEngine::characterLevelGain(int charIndex) { int s = _numLevelsPerClass[c->cClass]; for (int i = 0; i < s; i++) { uint32 er = getRequiredExperience(c->cClass, i, c->level[i] + 1); - if (er == 0xffffffff) + if (er == 0xFFFFFFFF) continue; increaseCharacterExperience(charIndex, er - c->experience[i] + 1); diff --git a/engines/kyra/darkmoon.h b/engines/kyra/darkmoon.h index f6e7b3ed2c..f0057ddd66 100644 --- a/engines/kyra/darkmoon.h +++ b/engines/kyra/darkmoon.h @@ -72,21 +72,21 @@ private: void seq_playFinale(); void seq_playCredits(DarkmoonSequenceHelper *sq, const uint8 *data, int sd, int backupPage, int tempPage, int speed); - const char * const *_introStrings; - const char * const *_cpsFilesIntro; + const char *const *_introStrings; + const char *const *_cpsFilesIntro; const DarkMoonAnimCommand **_animIntro; const DarkMoonShapeDef **_shapesIntro; - const char * const *_finaleStrings; + const char *const *_finaleStrings; const uint8 *_creditsData; - const char * const *_cpsFilesFinale; + const char *const *_cpsFilesFinale; const DarkMoonAnimCommand **_animFinale; const DarkMoonShapeDef **_shapesFinale; - static const char *_palFilesIntroVGA[]; - static const char *_palFilesIntroEGA[]; - static const char *_palFilesFinaleVGA[]; - static const char *_palFilesFinaleEGA[]; + static const char *const _palFilesIntroVGA[]; + static const char *const _palFilesIntroEGA[]; + static const char *const _palFilesFinaleVGA[]; + static const char *const _palFilesFinaleEGA[]; // Ingame sequence void seq_nightmare(); @@ -140,7 +140,7 @@ private: static const uint8 _egaDefaultPalette[]; }; -} // End of namespace Kyra +} // End of namespace Kyra #endif diff --git a/engines/kyra/detection.cpp b/engines/kyra/detection.cpp index e422f3ea19..95c4accd29 100644 --- a/engines/kyra/detection.cpp +++ b/engines/kyra/detection.cpp @@ -298,7 +298,7 @@ SaveStateDescriptor KyraMetaEngine::querySaveMetaInfos(const char *target, int s if (in) { Kyra::KyraEngine_v1::SaveHeader header; - Kyra::KyraEngine_v1::kReadSaveHeaderError error; + Kyra::KyraEngine_v1::ReadSaveHeaderError error; error = Kyra::KyraEngine_v1::readSaveHeader(in, true, header); delete in; diff --git a/engines/kyra/detection_tables.h b/engines/kyra/detection_tables.h index 79ef11e7a9..d948f7d12d 100644 --- a/engines/kyra/detection_tables.h +++ b/engines/kyra/detection_tables.h @@ -53,7 +53,8 @@ namespace { #define LOL_FLOPPY_FLAGS FLAGS(false, false, false, false, false, false, false, false, Kyra::GI_LOL) #define LOL_FLOPPY_FAN_FLAGS(x, y) FLAGS_FAN(x, y, false, false, false, false, false, false, false, false, Kyra::GI_LOL) #define LOL_FLOPPY_CMP_FLAGS FLAGS(false, false, false, false, false, false, false, true, Kyra::GI_LOL) -#define LOL_PC98_SJIS_FLAGS FLAGS(false, false, false, false, true, true, false, false, Kyra::GI_LOL) +#define LOL_PC9801_FLAGS FLAGS(false, false, false, false, true, true, false, false, Kyra::GI_LOL) +#define LOL_FMTOWNS_FLAGS FLAGS(false, false, false, false, true, false, false, false, Kyra::GI_LOL) #define LOL_DEMO_FLAGS FLAGS(true, true, false, false, false, false, false, false, Kyra::GI_LOL) #define LOL_KYRA2_DEMO_FLAGS FLAGS(true, false, false, false, false, false, false, false, Kyra::GI_KYRA2) @@ -1329,6 +1330,22 @@ const KYRAGameDescription adGameDescs[] = { LOL_FLOPPY_CMP_FLAGS }, + { // French floppy version 1.20, bug #3552534 "KYRA: LOL Floppy FR version unknown" + { + "lol", + 0, + { + { "WESTWOOD.1", 0, "43857e24d1fc6731f3b13d9ed6db8c3a", -1 }, + { 0, 0, 0, 0 } + }, + Common::FR_FRA, + Common::kPlatformPC, + ADGF_NO_FLAGS, + GUIO8(GUIO_NOSPEECH, GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_MIDIPCSPK, GUIO_RENDERVGA, GAMEOPTION_LOL_SCROLLING, GAMEOPTION_LOL_CURSORS) + }, + LOL_FLOPPY_CMP_FLAGS + }, + { { "lol", @@ -1397,6 +1414,23 @@ const KYRAGameDescription adGameDescs[] = { LOL_FLOPPY_FLAGS }, + { // French floppy version 1.23, bug #3552534 "KYRA: LOL Floppy FR version unknown" + { + "lol", + "Extracted", + { + { "GENERAL.PAK", 0, "f4fd14f244bd7c7fa08d026fafe44cc5", -1 }, + { "CHAPTER7.PAK", 0, "733e33c8444c93843dac3b683c283eaa", -1 }, + { 0, 0, 0, 0 } + }, + Common::FR_FRA, + Common::kPlatformPC, + ADGF_NO_FLAGS, + GUIO8(GUIO_NOSPEECH, GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_MIDIPCSPK, GUIO_RENDERVGA, GAMEOPTION_LOL_SCROLLING, GAMEOPTION_LOL_CURSORS) + }, + LOL_FLOPPY_FLAGS + }, + // Russian fan translation { { @@ -1429,7 +1463,24 @@ const KYRAGameDescription adGameDescs[] = { ADGF_NO_FLAGS, GUIO5(GUIO_NOSPEECH, GUIO_MIDIPC98, GUIO_RENDERPC9801, GAMEOPTION_LOL_SCROLLING, GAMEOPTION_LOL_CURSORS) }, - LOL_PC98_SJIS_FLAGS + LOL_PC9801_FLAGS + }, + + { + { + "lol", + 0, + { + { "GENERAL.PAK", 0, "2e4d4ce54bac9162e11fcba6907b576e", -1 }, + { "TMUS.PAK", 0, "5543dae575164e51856f5a49cfd6b368", -1 }, + { 0, 0, 0, 0 } + }, + Common::JA_JPN, + Common::kPlatformFMTowns, + ADGF_NO_FLAGS, + GUIO5(GUIO_NOSPEECH, GUIO_MIDITOWNS, GUIO_RENDERFMTOWNS, GAMEOPTION_LOL_SCROLLING, GAMEOPTION_LOL_CURSORS) + }, + LOL_FMTOWNS_FLAGS }, { @@ -1462,7 +1513,7 @@ const KYRAGameDescription adGameDescs[] = { ADGF_DEMO, GUIO5(GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_MIDIPCSPK, GUIO_RENDERVGA) }, - LOL_KYRA2_DEMO_FLAGS + LOL_DEMO_FLAGS }, #endif // ENABLE_LOL #ifdef ENABLE_EOB diff --git a/engines/kyra/eob.cpp b/engines/kyra/eob.cpp index a7bde9f1ee..4e1707b1ac 100644 --- a/engines/kyra/eob.cpp +++ b/engines/kyra/eob.cpp @@ -316,7 +316,7 @@ void EoBEngine::runNpcDialogue(int npcIndex) { void EoBEngine::updateUsedCharacterHandItem(int charIndex, int slot) { EoBItem *itm = &_items[_characters[charIndex].inventory[slot]]; if (itm->type == 48) { - int charges = itm->flags & 0x3f; + int charges = itm->flags & 0x3F; if (--charges) --itm->flags; else @@ -359,7 +359,7 @@ void EoBEngine::loadDoorShapes(int doorType1, int shapeId1, int doorType2, int s _screen->loadShapeSetBitmap("DOOR", 5, 3); _screen->_curPage = 2; - if (doorType1 != 0xff) { + if (doorType1 != 0xFF) { for (int i = 0; i < 3; i++) { const uint8 *enc = &_doorShapeEncodeDefs[(doorType1 * 3 + i) << 2]; _doorShapes[shapeId1 + i] = _screen->encodeShape(enc[0], enc[1], enc[2], enc[3], false, (_flags.gameID == GI_EOB1) ? _cgaMappingLevel[_cgaLevelMappingIndex[_currentLevel - 1]] : 0); @@ -370,7 +370,7 @@ void EoBEngine::loadDoorShapes(int doorType1, int shapeId1, int doorType2, int s } } - if (doorType2 != 0xff) { + if (doorType2 != 0xFF) { for (int i = 0; i < 3; i++) { const uint8 *enc = &_doorShapeEncodeDefs[(doorType2 * 3 + i) << 2]; _doorShapes[shapeId2 + i] = _screen->encodeShape(enc[0], enc[1], enc[2], enc[3], false, (_flags.gameID == GI_EOB1) ? _cgaMappingLevel[_cgaLevelMappingIndex[_currentLevel - 1]] : 0); @@ -460,7 +460,7 @@ void EoBEngine::turnUndeadAuto() { int oc = _openBookChar; for (int i = 0; i < 6; i++) { - if (!testCharacter(i, 0x0d)) + if (!testCharacter(i, 0x0D)) continue; EoBCharacter *c = &_characters[i]; @@ -505,7 +505,7 @@ bool EoBEngine::checkPartyStatusExtra() { _txt->printMessage(_menuStringsDefeat[0]); while (!shouldQuit()) { removeInputTop(); - if (checkInput(0, false, 0) & 0xff) + if (checkInput(0, false, 0) & 0xFF) break; } _screen->copyPage(10, 0); diff --git a/engines/kyra/eob.h b/engines/kyra/eob.h index 37ce483702..bf5440b942 100644 --- a/engines/kyra/eob.h +++ b/engines/kyra/eob.h @@ -99,7 +99,7 @@ private: void turnUndeadAuto(); void turnUndeadAutoHit(); - const char * const *_turnUndeadString; + const char *const *_turnUndeadString; // Misc bool checkPartyStatusExtra(); @@ -113,8 +113,7 @@ private: static const uint8 _egaDefaultPalette[]; }; - -} // End of namespace Kyra +} // End of namespace Kyra #endif diff --git a/engines/kyra/eobcommon.cpp b/engines/kyra/eobcommon.cpp index a63f123258..d477209e5b 100644 --- a/engines/kyra/eobcommon.cpp +++ b/engines/kyra/eobcommon.cpp @@ -57,7 +57,7 @@ EoBCoreEngine::EoBCoreEngine(OSystem *system, const GameFlags &flags) _configMouse = true; _loading = false; - _useHiResDithering = false; + _enableHiResDithering = false; _envAudioTimer = 0; _flashShapeTimer = 0; @@ -364,14 +364,14 @@ void EoBCoreEngine::initKeymap() { } Common::Error EoBCoreEngine::init() { - // In EOB the timer proc is directly invoked via interrupt 0x1c, 18.2 times per second. + // In EOB the timer proc is directly invoked via interrupt 0x1C, 18.2 times per second. // This makes a tick length of 54.94. _tickLength = 55; if (ConfMan.hasKey("render_mode")) _configRenderMode = Common::parseRenderMode(ConfMan.get("render_mode")); - _useHiResDithering = (_configRenderMode == Common::kRenderEGA && _flags.useHiRes); + _enableHiResDithering = (_configRenderMode == Common::kRenderEGA && _flags.useHiRes); _screen = new Screen_EoB(this, _system); assert(_screen); @@ -417,12 +417,6 @@ Common::Error EoBCoreEngine::init() { _screen->loadFont(Screen::FID_6_FNT, "FONT6.FNT"); _screen->loadFont(Screen::FID_8_FNT, "FONT8.FNT"); - if (_useHiResDithering) { - _vcnBlockWidth <<= 1; - _vcnBlockHeight <<= 1; - SWAP(_vcnFlip0, _vcnFlip1); - } - Common::Error err = KyraRpgEngine::init(); if (err.getCode() != Common::kNoError) return err; @@ -494,8 +488,8 @@ Common::Error EoBCoreEngine::init() { _monsterFlashOverlay = new uint8[16]; _monsterStoneOverlay = new uint8[16]; - memset(_monsterFlashOverlay, (_configRenderMode == Common::kRenderCGA) ? 0xff : 0x0f, 16 * sizeof(uint8)); - memset(_monsterStoneOverlay, 0x0d, 16 * sizeof(uint8)); + memset(_monsterFlashOverlay, (_configRenderMode == Common::kRenderCGA) ? 0xFF : 0x0F, 16 * sizeof(uint8)); + memset(_monsterStoneOverlay, 0x0D, 16 * sizeof(uint8)); _monsterFlashOverlay[0] = _monsterStoneOverlay[0] = 0; // Prevent autosave on game startup @@ -770,7 +764,7 @@ void EoBCoreEngine::releaseItemsAndDecorationsShapes() { if (_spellShapes) { for (int i = 0; i < 4; i++) { if (_spellShapes[i]) - delete [] _spellShapes[i]; + delete[] _spellShapes[i]; } delete[] _spellShapes; } @@ -820,7 +814,7 @@ void EoBCoreEngine::releaseItemsAndDecorationsShapes() { if (_firebeamShapes[i]) delete[] _firebeamShapes[i]; } - delete []_firebeamShapes; + delete[] _firebeamShapes; } delete[] _redSplatShape; @@ -991,7 +985,7 @@ void EoBCoreEngine::recalcArmorClass(int index) { int tp = _items[itm].type; - if (!(_itemTypes[tp].allowedClasses & _classModifierFlags[c->cClass]) || (_itemTypes[tp].extraProperties & 0x7f) || (i >= 1 && i <= 2 && tp != 27 && !(_flags.gameID == GI_EOB2 && tp == 57))) + if (!(_itemTypes[tp].allowedClasses & _classModifierFlags[c->cClass]) || (_itemTypes[tp].extraProperties & 0x7F) || (i >= 1 && i <= 2 && tp != 27 && !(_flags.gameID == GI_EOB2 && tp == 57))) continue; c->armorClass += _itemTypes[tp].armorClass; @@ -1003,12 +997,12 @@ void EoBCoreEngine::recalcArmorClass(int index) { int8 m2 = 0; if (c->inventory[25]) { - if (!(_itemTypes[_items[c->inventory[25]].type].extraProperties & 0x7f)) + if (!(_itemTypes[_items[c->inventory[25]].type].extraProperties & 0x7F)) m1 = _items[c->inventory[25]].value; } if (c->inventory[26]) { - if (!(_itemTypes[_items[c->inventory[26]].type].extraProperties & 0x7f)) + if (!(_itemTypes[_items[c->inventory[26]].type].extraProperties & 0x7F)) m2 = _items[c->inventory[26]].value; } @@ -1060,7 +1054,7 @@ int EoBCoreEngine::validateWeaponSlotItem(int index, int slot) { if (!itm2) return 1; - int f = (_itemTypes[tp2].extraProperties & 0x7f); + int f = (_itemTypes[tp2].extraProperties & 0x7F); if (f <= 0 || f > 3) return r; @@ -1275,12 +1269,12 @@ void EoBCoreEngine::removeCharacterFromParty(int charIndex) { if (i == 16 || !c->inventory[i]) continue; - setItemPosition((Item *)&_levelBlockProperties[_currentBlock & 0x3ff].drawObjects, _currentBlock, c->inventory[i], _dropItemDirIndex[(_currentDirection << 2) + rollDice(1, 2, -1)]); + setItemPosition((Item *)&_levelBlockProperties[_currentBlock & 0x3FF].drawObjects, _currentBlock, c->inventory[i], _dropItemDirIndex[(_currentDirection << 2) + rollDice(1, 2, -1)]); c->inventory[i] = 0; } while (c->inventory[16]) - setItemPosition((Item *)&_levelBlockProperties[_currentBlock & 0x3ff].drawObjects, _currentBlock, getQueuedItem(&c->inventory[16], 0, -1), _dropItemDirIndex[(_currentDirection << 2) + rollDice(1, 2, -1)]); + setItemPosition((Item *)&_levelBlockProperties[_currentBlock & 0x3FF].drawObjects, _currentBlock, getQueuedItem(&c->inventory[16], 0, -1), _dropItemDirIndex[(_currentDirection << 2) + rollDice(1, 2, -1)]); c->inventory[16] = 0; @@ -1326,7 +1320,7 @@ void EoBCoreEngine::increaseCharacterExperience(int charIndex, int32 points) { _characters[charIndex].experience[i] += points; uint32 er = getRequiredExperience(cl, i, _characters[charIndex].level[i] + 1); - if (er == 0xffffffff) + if (er == 0xFFFFFFFF) continue; if (_characters[charIndex].experience[i] >= er) @@ -1337,7 +1331,7 @@ void EoBCoreEngine::increaseCharacterExperience(int charIndex, int32 points) { uint32 EoBCoreEngine::getRequiredExperience(int cClass, int levelIndex, int level) { cClass = getCharacterClassType(cClass, levelIndex); if (cClass == -1) - return 0xffffffff; + return 0xFFFFFFFF; const uint32 *tbl = _expRequirementTables[cClass]; return tbl[level - 1]; @@ -1625,7 +1619,7 @@ void EoBCoreEngine::displayParchment(int id) { removeInputTop(); while (!shouldQuit()) { delay(_tickLength); - if (checkInput(0, false, 0) & 0xff) + if (checkInput(0, false, 0) & 0xFF) break; removeInputTop(); } @@ -1658,7 +1652,7 @@ int EoBCoreEngine::countResurrectionCandidates() { if (!inv) continue; - if ((_flags.gameID == GI_EOB1 && ((_itemTypes[_items[inv].type].extraProperties & 0x7f) != 8)) || (_flags.gameID == GI_EOB2 && _items[inv].type != 33)) + if ((_flags.gameID == GI_EOB1 && ((_itemTypes[_items[inv].type].extraProperties & 0x7F) != 8)) || (_flags.gameID == GI_EOB2 && _items[inv].type != 33)) continue; _rrNames[_rrCount] = _npcPreset[_items[inv].value - 1].name; @@ -1667,7 +1661,7 @@ int EoBCoreEngine::countResurrectionCandidates() { } if (_itemInHand > 0) { - if ((_flags.gameID == GI_EOB1 && ((_itemTypes[_items[_itemInHand].type].extraProperties & 0x7f) == 8)) || (_flags.gameID == GI_EOB2 && _items[_itemInHand].type == 33)) { + if ((_flags.gameID == GI_EOB1 && ((_itemTypes[_items[_itemInHand].type].extraProperties & 0x7F) == 8)) || (_flags.gameID == GI_EOB2 && _items[_itemInHand].type == 33)) { _rrNames[_rrCount] = _npcPreset[_items[_itemInHand].value - 1].name; _rrId[_rrCount++] = -_items[_itemInHand].value; } @@ -1748,7 +1742,7 @@ void EoBCoreEngine::seq_portal() { bool EoBCoreEngine::checkPassword() { char answ[20]; Screen::FontId of = _screen->setFont(Screen::FID_8_FNT); - _screen->copyPage(0, _useHiResDithering ? 4 : 10); + _screen->copyPage(0, 10); _screen->setScreenDim(13); gui_drawBox(_screen->_curDim->sx << 3, _screen->_curDim->sy, _screen->_curDim->w << 3, _screen->_curDim->h, guiSettings()->colors.frame1, guiSettings()->colors.frame2, -1); @@ -1775,7 +1769,7 @@ bool EoBCoreEngine::checkPassword() { _screen->modifyScreenDim(13, _screen->_curDim->sx - 1, _screen->_curDim->sy - 2, _screen->_curDim->w + 2, _screen->_curDim->h + 16); _screen->setFont(of); - _screen->copyPage(_useHiResDithering ? 4 : 10, 0); + _screen->copyPage(10, 0); return true; } @@ -1786,7 +1780,7 @@ void EoBCoreEngine::useSlotWeapon(int charIndex, int slotIndex, Item item) { if (c->effectFlags & 0x40) removeCharacterEffect(10, charIndex, 1); // remove invisibility effect - int ep = _itemTypes[tp].extraProperties & 0x7f; + int ep = _itemTypes[tp].extraProperties & 0x7F; int8 inflict = 0; if (ep == 1) { @@ -1829,14 +1823,14 @@ int EoBCoreEngine::closeDistanceAttack(int charIndex, Item item) { if (r == -1) { uint8 w = _specialWallTypes[_levelBlockProperties[d].walls[_sceneDrawVarDown]]; - if (w == 0xff) { + if (w == 0xFF) { if (_flags.gameID == GI_EOB1) { _levelBlockProperties[d].walls[_sceneDrawVarDown]++; _levelBlockProperties[d].walls[_sceneDrawVarDown ^ 2]++; } else { for (int i = 0; i < 4; i++) { - if (_specialWallTypes[_levelBlockProperties[d].walls[i]] == 0xff) + if (_specialWallTypes[_levelBlockProperties[d].walls[i]] == 0xFF) _levelBlockProperties[d].walls[i]++; } } @@ -1927,7 +1921,7 @@ int EoBCoreEngine::projectileWeaponAttack(int charIndex, Item item) { void EoBCoreEngine::inflictMonsterDamage(EoBMonsterInPlay *m, int damage, bool giveExperience) { m->hitPointsCur -= damage; - m->flags = (m->flags & 0xf7) | 1; + m->flags = (m->flags & 0xF7) | 1; if (_monsterProps[m->type].capsFlags & 0x2000) { explodeMonster(m); @@ -2033,7 +2027,7 @@ bool EoBCoreEngine::characterAttackHitTest(int charIndex, int monsterIndex, int if (charIndex < 0) return true; - int p = item ? (_flags.gameID == GI_EOB1 ? _items[item].type : (_itemTypes[_items[item].type].extraProperties & 0x7f)) : 0; + int p = item ? (_flags.gameID == GI_EOB1 ? _items[item].type : (_itemTypes[_items[item].type].extraProperties & 0x7F)) : 0; if (_monsters[monsterIndex].flags & 0x20) return true;// EOB 2 only ? @@ -2212,7 +2206,7 @@ void EoBCoreEngine::statusAttack(int charIndex, int attackStatusFlags, const cha c->flags |= attackStatusFlags; } - if ((attackStatusFlags & 0x0c) && (_openBookChar == charIndex) && _updateFlags) { + if ((attackStatusFlags & 0x0C) && (_openBookChar == charIndex) && _updateFlags) { Button b; clickedSpellbookAbort(&b); } @@ -2318,7 +2312,7 @@ int EoBCoreEngine::getSaveThrowModifier(int hpModifier, int level, int type) { } bool EoBCoreEngine::calcDamageCheckItemType(int itemType) { - itemType = _itemTypes[itemType].extraProperties & 0x7f; + itemType = _itemTypes[itemType].extraProperties & 0x7F; return (itemType == 2 || itemType == 3) ? true : false; } diff --git a/engines/kyra/eobcommon.h b/engines/kyra/eobcommon.h index f60e755dd7..70200d3049 100644 --- a/engines/kyra/eobcommon.h +++ b/engines/kyra/eobcommon.h @@ -722,21 +722,21 @@ protected: void gui_processWeaponSlotClickRight(int charIndex, int slotIndex); void gui_processInventorySlotClick(int slot); - static const int16 _buttonList1[]; + static const uint8 _buttonList1[]; int _buttonList1Size; - static const int16 _buttonList2[]; + static const uint8 _buttonList2[]; int _buttonList2Size; - static const int16 _buttonList3[]; + static const uint8 _buttonList3[]; int _buttonList3Size; - static const int16 _buttonList4[]; + static const uint8 _buttonList4[]; int _buttonList4Size; - static const int16 _buttonList5[]; + static const uint8 _buttonList5[]; int _buttonList5Size; - static const int16 _buttonList6[]; + static const uint8 _buttonList6[]; int _buttonList6Size; - static const int16 _buttonList7[]; + static const uint8 _buttonList7[]; int _buttonList7Size; - static const int16 _buttonList8[]; + static const uint8 _buttonList8[]; int _buttonList8Size; const EoBGuiButtonDef *_buttonDefs; @@ -845,7 +845,7 @@ protected: const uint8 *_cgaMappingLevel[5]; const uint8 *_cgaLevelMappingIndex; - bool _useHiResDithering; + bool _enableHiResDithering; // Default parameters will import all present original save files and push them to the top of the save dialog. bool importOriginalSaveFile(int destSlot, const char *sourceFile = 0); @@ -858,11 +858,11 @@ protected: void restoreWallOfForceTempData(LevelTempData *tmp); void releaseWallOfForceTempData(LevelTempData *tmp); - const char * const *_saveLoadStrings; + const char *const *_saveLoadStrings; const uint8 *_mnDef; - const char * const *_mnWord; - const char * const *_mnPrompt; + const char *const *_mnWord; + const char *const *_mnPrompt; int _mnNumWord; int _rrCount; diff --git a/engines/kyra/gui_eob.cpp b/engines/kyra/gui_eob.cpp index e3c0743e5c..ed3aaefbd9 100644 --- a/engines/kyra/gui_eob.cpp +++ b/engines/kyra/gui_eob.cpp @@ -86,7 +86,7 @@ void EoBCoreEngine::gui_drawCharPortraitWithStats(int index) { int txtCol1 = 12; int txtCol2 = 15; - if ((_flags.gameID == GI_EOB1 && c->flags & 6) || (_flags.gameID == GI_EOB2 && c->flags & 0x0e)) { + if ((_flags.gameID == GI_EOB1 && c->flags & 6) || (_flags.gameID == GI_EOB2 && c->flags & 0x0E)) { txtCol1 = 8; txtCol2 = 6; } @@ -285,7 +285,7 @@ void EoBCoreEngine::gui_drawWeaponSlot(int charIndex, int slot) { else _screen->drawShape(_screen->_curPage, _itemIconShapes[85 + slot], x + 8, y, 0); - if ((_characters[charIndex].disabledSlots & (1 << slot)) || !validateWeaponSlotItem(charIndex, slot) || (_characters[charIndex].hitPointsCur <= 0) || (_characters[charIndex].flags & 0x0c)) + if ((_characters[charIndex].disabledSlots & (1 << slot)) || !validateWeaponSlotItem(charIndex, slot) || (_characters[charIndex].hitPointsCur <= 0) || (_characters[charIndex].flags & 0x0C)) _screen->drawShape(_screen->_curPage, _weaponSlotGrid, x, y, 0); } @@ -321,7 +321,7 @@ void EoBCoreEngine::gui_drawWeaponSlotStatus(int x, int y, int status) { break; } - int textColor= (_configRenderMode == Common::kRenderCGA) ? 2 : 15; + int textColor = (_configRenderMode == Common::kRenderCGA) ? 2 : 15; if (!tmpStr2.empty()) { _screen->printText(tmpStr.c_str(), x + (16 - tmpStr.size() * 3), y + 2, textColor, 0); @@ -485,7 +485,7 @@ void EoBCoreEngine::gui_drawInventoryItem(int slot, int special, int pageNum) { uint8 col1 = guiSettings()->colors.frame1; uint8 col2 = guiSettings()->colors.frame2; - if (_configRenderMode == Common::kRenderCGA ) { + if (_configRenderMode == Common::kRenderCGA) { col1 = 1; col2 = 3; } @@ -777,11 +777,11 @@ int EoBCoreEngine::clickedCamp(Button *button) { } _screen->copyPage(0, 7); - _screen->copyRegion(0, 120, 0, 0, 176, 24, 0, _useHiResDithering ? 1 : 12, Screen::CR_NO_P_CHECK); + _screen->copyRegion(0, 120, 0, 0, 176, 24, 0, 12, Screen::CR_NO_P_CHECK); _gui->runCampMenu(); - _screen->copyRegion(0, 0, 0, 120, 176, 24, _useHiResDithering ? 1 : 12, 2, Screen::CR_NO_P_CHECK); + _screen->copyRegion(0, 0, 0, 120, 176, 24, 12, 2, Screen::CR_NO_P_CHECK); _screen->setScreenDim(cd); drawScene(0); @@ -808,13 +808,13 @@ int EoBCoreEngine::clickedSceneDropPickupItem(Button *button) { if (button->arg > 1) { block = calcNewBlockPosition(_currentBlock, _currentDirection); int f = _wllWallFlags[_levelBlockProperties[block].walls[_sceneDrawVarDown]]; - if (!(f & 0x0b)) + if (!(f & 0x0B)) return 1; } int d = _dropItemDirIndex[(_currentDirection << 2) + button->arg]; if (_itemInHand) { - setItemPosition((Item *)&_levelBlockProperties[block & 0x3ff].drawObjects, block, _itemInHand, d); + setItemPosition((Item *)&_levelBlockProperties[block & 0x3FF].drawObjects, block, _itemInHand, d); setHandItem(0); runLevelScript(block, 4); } else { @@ -856,7 +856,7 @@ int EoBCoreEngine::clickedWeaponSlot(Button *button) { static const uint8 sY[] = { 27, 27, 79, 79, 131, 131 }; int slot = sY[button->arg] > _mouseY ? 0 : 1; - if ((_gui->_flagsMouseLeft & 0x7f) == 1) + if ((_gui->_flagsMouseLeft & 0x7F) == 1) gui_processWeaponSlotClickLeft(button->arg, slot); else gui_processWeaponSlotClickRight(button->arg, slot); @@ -1031,9 +1031,9 @@ int EoBCoreEngine::clickedSpellbookList(Button *button) { } int EoBCoreEngine::clickedCastSpellOnCharacter(Button *button) { - _activeSpellCharId = button->arg & 0xff; + _activeSpellCharId = button->arg & 0xFF; - if (_activeSpellCharId == 0xff) { + if (_activeSpellCharId == 0xFF) { printWarning(_magicStrings3[_flags.gameID == GI_EOB1 ? 2 : 1]); if (_castScrollSlot) { gui_updateSlotAfterScrollUse(); @@ -1170,7 +1170,7 @@ int EoBCoreEngine::clickedSceneSpecial(Button *button) { int EoBCoreEngine::clickedSpellbookAbort(Button *button) { _updateFlags = 0; - _screen->copyRegion(0, 0, 64, 121, 112, 56, _useHiResDithering ? 4 : 10, 0, Screen::CR_NO_P_CHECK); + _screen->copyRegion(0, 0, 64, 121, 112, 56, 10, 0, Screen::CR_NO_P_CHECK); _screen->updateScreen(); gui_drawCompass(true); gui_toggleButtons(); @@ -1215,7 +1215,7 @@ void EoBCoreEngine::gui_processWeaponSlotClickLeft(int charIndex, int slotIndex) int ih = _itemInHand; int t = _items[ih].type; - uint16 v = (ih) ? _itemTypes[t].invFlags : 0xffff; + uint16 v = (ih) ? _itemTypes[t].invFlags : 0xFFFF; if (v & _slotValidationFlags[slotIndex]) { setHandItem(itm); @@ -1227,7 +1227,7 @@ void EoBCoreEngine::gui_processWeaponSlotClickLeft(int charIndex, int slotIndex) } void EoBCoreEngine::gui_processWeaponSlotClickRight(int charIndex, int slotIndex) { - if (!testCharacter(charIndex, 0x0d)) + if (!testCharacter(charIndex, 0x0D)) return; Item itm = _characters[charIndex].inventory[slotIndex]; @@ -1244,7 +1244,7 @@ void EoBCoreEngine::gui_processWeaponSlotClickRight(int charIndex, int slotIndex int8 tp = _items[itm].type; int8 vl = _items[itm].value; - uint8 ep = _itemTypes[tp].extraProperties & 0x7f; + uint8 ep = _itemTypes[tp].extraProperties & 0x7F; switch (ep) { case 0: @@ -1376,7 +1376,7 @@ GUI_EoB::GUI_EoB(EoBCoreEngine *vm) : GUI(vm), _vm(vm), _screen(vm->_screen) { _backupButtonList = 0; _progress = 0; _prcButtonUnk3 = 1; - _cflag = 0xffff; + _cflag = 0xFFFF; _menuLineSpacing = 0; _menuLastInFlags = 0; @@ -1385,8 +1385,8 @@ GUI_EoB::GUI_EoB(EoBCoreEngine *vm) : GUI(vm), _vm(vm), _screen(vm->_screen) { _numPages = (_vm->game() == GI_EOB2) ? 8 : 5; _numVisPages = (_vm->game() == GI_EOB2) ? 6 : 5; - _clericSpellAvltyFlags = (_vm->game() == GI_EOB2) ? 0xf7ffffff : 0x7bffff; - _paladinSpellAvltyFlags = (_vm->game() == GI_EOB2) ? 0xa9bbd1d : 0x800ff2; + _clericSpellAvltyFlags = (_vm->game() == GI_EOB2) ? 0xF7FFFFFF : 0x7BFFFF; + _paladinSpellAvltyFlags = (_vm->game() == GI_EOB2) ? 0xA9BBD1D : 0x800FF2; _numAssignedSpellsOfType = new int8[72]; memset(_numAssignedSpellsOfType, 0, 72); @@ -1524,7 +1524,7 @@ void GUI_EoB::processButton(Button *button) { int GUI_EoB::processButtonList(Kyra::Button *buttonList, uint16 inputFlags, int8 mouseWheel) { _progress = 0; - uint16 in = inputFlags & 0xff; + uint16 in = inputFlags & 0xFF; uint16 buttonReleaseFlag = 0; bool clickEvt = false; //_vm->_processingButtons = true; @@ -1543,13 +1543,13 @@ int GUI_EoB::processButtonList(Kyra::Button *buttonList, uint16 inputFlags, int8 //////////////////////////// if (!buttonList && !(inputFlags & 0x800)) - return inputFlags & 0xff; + return inputFlags & 0xFF; //////////////////////////// inputFlags = 0; clickEvt = true; } else if (inputFlags & 0x8000) { - inputFlags &= 0xff; + inputFlags &= 0xFF; } uint16 result = 0; @@ -1568,8 +1568,8 @@ int GUI_EoB::processButtonList(Kyra::Button *buttonList, uint16 inputFlags, int8 // UNUSED //if (buttonList->flags2 & 0x20) { - //if (_processButtonListExtraCallback) - // this->*_processButtonListExtraCallback(buttonList); + // if (_processButtonListExtraCallback) + // this->*_processButtonListExtraCallback(buttonList); //} if (buttonList->nextButton) @@ -1582,7 +1582,7 @@ int GUI_EoB::processButtonList(Kyra::Button *buttonList, uint16 inputFlags, int8 _specialProcessButton = 0; _prcButtonUnk3 = 1; - _cflag = 0xffff; + _cflag = 0xFFFF; } int sd = 0; @@ -1616,12 +1616,12 @@ int GUI_EoB::processButtonList(Kyra::Button *buttonList, uint16 inputFlags, int8 if (flgs2 & 1) flgs2 |= 8; else - flgs2 &= 0xfff7; + flgs2 &= 0xFFF7; if (flgs2 & 4) flgs2 |= 0x10; else - flgs2 &= 0xffef; + flgs2 &= 0xFFEF; uint16 vL = 0; uint16 vR = 0; @@ -1640,8 +1640,8 @@ int GUI_EoB::processButtonList(Kyra::Button *buttonList, uint16 inputFlags, int8 v6 = 1; } } else if (_flagsModifier || clickEvt) { - vL = flgs & 0xf00; - vR = flgs & 0xf000; + vL = flgs & 0xF00; + vR = flgs & 0xF000; if (_prcButtonUnk3) { if (sd != buttonList->dimTableIndex) { @@ -1675,7 +1675,7 @@ int GUI_EoB::processButtonList(Kyra::Button *buttonList, uint16 inputFlags, int8 flgs2 |= 4; vc = 1; } else { - flgs2 &= 0xfffb; + flgs2 &= 0xFFFB; } if (flgs & 0x100) { @@ -1702,7 +1702,7 @@ int GUI_EoB::processButtonList(Kyra::Button *buttonList, uint16 inputFlags, int8 flgs2 |= 4; vc = 1; } else { - flgs2 &= 0xfffb; + flgs2 &= 0xFFFB; } if (!(flgs & 0x200)) @@ -1730,12 +1730,12 @@ int GUI_EoB::processButtonList(Kyra::Button *buttonList, uint16 inputFlags, int8 } if ((flgs & 2) && (flgs2 & 1)) - flgs2 &= 0xfffe; + flgs2 &= 0xFFFE; break; case 3: if ((flgs & 4) || (!buttonList->data2Val1)) - flgs2 &= 0xfffb; + flgs2 &= 0xFFFB; else flgs2 |= 4; @@ -1745,7 +1745,7 @@ int GUI_EoB::processButtonList(Kyra::Button *buttonList, uint16 inputFlags, int8 } if ((flgs & 2) && (flgs2 & 1)) - flgs2 &= 0xfffe; + flgs2 &= 0xFFFE; break; default: @@ -1761,7 +1761,7 @@ int GUI_EoB::processButtonList(Kyra::Button *buttonList, uint16 inputFlags, int8 if ((flgs & 4) && buttonList->data2Val1) flgs2 |= 4; else - flgs2 &= 0xfffb; + flgs2 &= 0xFFFB; if (flgs & 0x1000) { v6 = 1; @@ -1786,7 +1786,7 @@ int GUI_EoB::processButtonList(Kyra::Button *buttonList, uint16 inputFlags, int8 if ((flgs & 4) && buttonList->data2Val1) flgs2 |= 4; else - flgs2 &= 0xfffb; + flgs2 &= 0xFFFB; if (!(flgs & 0x2000)) break; @@ -1812,12 +1812,12 @@ int GUI_EoB::processButtonList(Kyra::Button *buttonList, uint16 inputFlags, int8 } if ((flgs & 2) && (flgs2 & 1)) - flgs2 &= 0xfffe; + flgs2 &= 0xFFFE; break; case 3: if ((flgs & 4) || (!buttonList->data2Val1)) - flgs2 &= 0xfffb; + flgs2 &= 0xFFFB; else flgs2 |= 4; @@ -1827,7 +1827,7 @@ int GUI_EoB::processButtonList(Kyra::Button *buttonList, uint16 inputFlags, int8 } if ((flgs & 2) && (flgs2 & 1)) - flgs2 &= 0xfffe; + flgs2 &= 0xFFFE; break; default: @@ -1835,7 +1835,7 @@ int GUI_EoB::processButtonList(Kyra::Button *buttonList, uint16 inputFlags, int8 } } } else { // if (_vm->_mouseX >= x2 && _vm->_mouseX <= (x2 + buttonList->width)....) - flgs2 &= 0xfff9; + flgs2 &= 0xFFF9; if ((flgs & 0x40) && (!(flgs & 0x80)) && _specialProcessButton && !v8) { static const uint16 flagsTable[] = { 0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000, 0x8000 }; @@ -1859,7 +1859,7 @@ int GUI_EoB::processButtonList(Kyra::Button *buttonList, uint16 inputFlags, int8 } if ((flgs & 2) && (flgs2 & 1)) - flgs2 &= 0xfffe; + flgs2 &= 0xFFFE; } // end if (_vm->_mouseX >= x2 && _vm->_mouseX <= (x2 + buttonList->width)....) } // end if (_prcButtonUnk3) } // end if (_flagsModifier || clickEvt) @@ -1896,7 +1896,7 @@ int GUI_EoB::processButtonList(Kyra::Button *buttonList, uint16 inputFlags, int8 }; if ((_flagsMouseLeft == 1 || _flagsMouseRight == 1) && !v18) - _cflag = 0xffff; + _cflag = 0xFFFF; if (!result) result = inputFlags; @@ -1938,7 +1938,7 @@ int GUI_EoB::simpleMenu_process(int sd, const char *const *strings, void *b, int int x = (_screen->_curDim->sx + dm->sx) << 3; int y = _screen->_curDim->sy + dm->sy; - int inFlag = _vm->checkInput(0, false, 0) & 0x8ff; + int inFlag = _vm->checkInput(0, false, 0) & 0x8FF; _vm->removeInputTop(); Common::Point mousePos = _vm->getMousePos(); @@ -2051,7 +2051,7 @@ void GUI_EoB::runCampMenu() { newMenu = -1; } - int inputFlag = _vm->checkInput(buttonList, false, 0) & 0x80ff; + int inputFlag = _vm->checkInput(buttonList, false, 0) & 0x80FF; _vm->removeInputTop(); if (inputFlag == _vm->_keyMap[Common::KEYCODE_ESCAPE]) @@ -2060,7 +2060,7 @@ void GUI_EoB::runCampMenu() { inputFlag = 0x8000 + prevHighlightButton->index; } - Button *clickedButton = _vm->gui_getButton(buttonList, inputFlag & 0x7fff); + Button *clickedButton = _vm->gui_getButton(buttonList, inputFlag & 0x7FFF); if (clickedButton) { drawMenuButton(prevHighlightButton, false, false, true); @@ -2140,8 +2140,8 @@ void GUI_EoB::runCampMenu() { displayTextBox(44); // fall through - case 0x800c: - case 0x800f: + case 0x800C: + case 0x800F: if (lastMenu == 1 || lastMenu == 2) newMenu = 0; else if (inputFlag == _vm->_keyMap[Common::KEYCODE_ESCAPE]) @@ -2163,7 +2163,7 @@ void GUI_EoB::runCampMenu() { newMenu = 1; break; - case 0x800a: + case 0x800A: for (; i < 6; i++) { if (_vm->testCharacter(i, 1)) cnt++; @@ -2172,7 +2172,7 @@ void GUI_EoB::runCampMenu() { if (cnt > 4) { _vm->dropCharacter(selectCharacterDialogue(53)); _vm->gui_drawPlayField(false); - _screen->copyRegion(0, 120, 0, 0, 176, 24, 0, _vm->_useHiResDithering ? 1 : 12, Screen::CR_NO_P_CHECK); + _screen->copyRegion(0, 120, 0, 0, 176, 24, 0, 12, Screen::CR_NO_P_CHECK); _screen->setFont(Screen::FID_6_FNT); _vm->gui_drawAllCharPortraitsWithStats(); _screen->setFont(Screen::FID_8_FNT); @@ -2183,19 +2183,19 @@ void GUI_EoB::runCampMenu() { newMenu = 0; break; - case 0x800b: + case 0x800B: if (confirmDialogue(46)) _vm->quitGame(); newMenu = 0; break; - case 0x800d: + case 0x800D: _vm->_configSounds ^= true; _vm->_configMusic = _vm->_configSounds ? 1 : 0; newMenu = 2; break; - case 0x800e: + case 0x800E: _vm->_configHpBarGraphs ^= true; newMenu = 2; redrawPortraits = true; @@ -2290,7 +2290,7 @@ bool GUI_EoB::confirmDialogue2(int dim, int id, int deflt) { else if (_vm->posWithinRect(p.x, p.y, x[1], y, x[1] + 32, y + 14)) newHighlight = 1; - int inputFlag = _vm->checkInput(0, false, 0) & 0x8ff; + int inputFlag = _vm->checkInput(0, false, 0) & 0x8FF; _vm->removeInputTop(); if (inputFlag == _vm->_keyMap[Common::KEYCODE_SPACE] || inputFlag == _vm->_keyMap[Common::KEYCODE_RETURN]) { @@ -2351,7 +2351,7 @@ void GUI_EoB::messageDialogue(int dim, int id, int buttonTextCol) { _screen->updateScreen(); for (bool runLoop = true; runLoop && !_vm->shouldQuit();) { - int inputFlag = _vm->checkInput(0, false, 0) & 0x8ff; + int inputFlag = _vm->checkInput(0, false, 0) & 0x8FF; _vm->removeInputTop(); if (inputFlag == 199 || inputFlag == 201) { @@ -2392,7 +2392,7 @@ void GUI_EoB::messageDialogue2(int dim, int id, int buttonTextCol) { _screen->updateScreen(); for (bool runLoop = true; runLoop && !_vm->shouldQuit();) { - int inputFlag = _vm->checkInput(0, false, 0) & 0x8ff; + int inputFlag = _vm->checkInput(0, false, 0) & 0x8FF; _vm->removeInputTop(); if (inputFlag == 199 || inputFlag == 201) { @@ -2607,7 +2607,7 @@ Common::String GUI_EoB::transferTargetMenu(Common::Array<Common::String> &target break; } while (_saveSlotIdTemp[slot] == -1); - _screen->copyRegion(72, 14, 72, 14, 176, 144, _vm->_useHiResDithering ? 7 : 12, 0, Screen::CR_NO_P_CHECK); + _screen->copyRegion(72, 14, 72, 14, 176, 144, 12, 0, Screen::CR_NO_P_CHECK); _screen->modifyScreenDim(11, xo, yo, dm->w, dm->h); return (slot < 6) ? _savegameList[_savegameOffset + slot] : Common::String(); @@ -2650,7 +2650,7 @@ void GUI_EoB::createScreenThumbnail(Graphics::Surface &dst) { _screen->getRealPalette(0, screenPal); uint16 width = Screen::SCREEN_W; uint16 height = Screen::SCREEN_H; - if (_vm->_useHiResDithering) { + if (_vm->gameFlags().useHiRes) { width <<= 1; height <<= 1; } @@ -2705,11 +2705,20 @@ bool GUI_EoB::runSaveMenu(int x, int y) { for (int in = -1; in == -1 && !_vm->shouldQuit();) { _screen->fillRect(fx - 2, fy, fx + 160, fy + 8, _vm->guiSettings()->colors.fill); in = getTextInput(_saveSlotStringsTemp[slot], x + 1, fy, 19, 2, 0, 8); + if (in == -1) { + useSlot = false; + break; + } + if (!strlen(_saveSlotStringsTemp[slot])) { messageDialogue(11, 54, 6); in = -1; } - }; + } + + if (!useSlot) { + continue; + } _screen->fillRect(fx - 2, fy, fx + 160, fy + 8, _vm->guiSettings()->colors.fill); _screen->printShadedText(_saveSlotStringsTemp[slot], (x + 1) << 3, fy, 15, 0); @@ -2755,7 +2764,7 @@ int GUI_EoB::selectSaveSlotDialogue(int x, int y, int id) { int slot = -1; for (bool runLoop = true; runLoop && !_vm->shouldQuit();) { - int inputFlag = _vm->checkInput(0, false, 0) & 0x8ff; + int inputFlag = _vm->checkInput(0, false, 0) & 0x8FF; _vm->removeInputTop(); if (inputFlag == _vm->_keyMap[Common::KEYCODE_SPACE] || inputFlag == _vm->_keyMap[Common::KEYCODE_RETURN]) { @@ -2999,11 +3008,11 @@ void GUI_EoB::runMemorizePrayMenu(int charIndex, int spellType) { lastHighLightText = newHighLightText; } - int inputFlag = _vm->checkInput(buttonList, false, 0) & 0x80ff; + int inputFlag = _vm->checkInput(buttonList, false, 0) & 0x80FF; _vm->removeInputTop(); if (inputFlag == _vm->_keyMap[Common::KEYCODE_KP6] || inputFlag == _vm->_keyMap[Common::KEYCODE_RIGHT]) { - inputFlag = 0x801a + ((lastHighLightButton + 1) % _numVisPages); + inputFlag = 0x801A + ((lastHighLightButton + 1) % _numVisPages); } else if (inputFlag == _vm->_keyMap[Common::KEYCODE_KP4] || inputFlag == _vm->_keyMap[Common::KEYCODE_LEFT]) { inputFlag = lastHighLightButton ? 0x8019 + lastHighLightButton : 0x8019 + _numVisPages; } else if (inputFlag == _vm->_keyMap[Common::KEYCODE_ESCAPE]) { @@ -3018,7 +3027,7 @@ void GUI_EoB::runMemorizePrayMenu(int charIndex, int spellType) { } if (inputFlag & 0x8000) { - b = _vm->gui_getButton(buttonList, inputFlag & 0x7fff); + b = _vm->gui_getButton(buttonList, inputFlag & 0x7FFF); drawMenuButton(b, true, true, true); _screen->updateScreen(); _vm->_system->delayMillis(80); @@ -3065,9 +3074,9 @@ void GUI_EoB::runMemorizePrayMenu(int charIndex, int spellType) { runLoop = false; } else if (inputFlag & 0x8000) { - newHighLightButton = inputFlag - 0x801a; + newHighLightButton = inputFlag - 0x801A; if (newHighLightButton == lastHighLightButton) - drawMenuButton(_vm->gui_getButton(buttonList, inputFlag & 0x7fff), false, true, true); + drawMenuButton(_vm->gui_getButton(buttonList, inputFlag & 0x7FFF), false, true, true); } } @@ -3544,7 +3553,7 @@ bool GUI_EoB::confirmDialogue(int id) { lastHighlight = newHighlight; } - int inputFlag = _vm->checkInput(buttonList, false, 0) & 0x80ff; + int inputFlag = _vm->checkInput(buttonList, false, 0) & 0x80FF; _vm->removeInputTop(); if (inputFlag == _vm->_keyMap[Common::KEYCODE_KP5] || inputFlag == _vm->_keyMap[Common::KEYCODE_SPACE] || inputFlag == _vm->_keyMap[Common::KEYCODE_RETURN]) { diff --git a/engines/kyra/gui_eob.h b/engines/kyra/gui_eob.h index f6be18ffbb..1b7bdf3482 100644 --- a/engines/kyra/gui_eob.h +++ b/engines/kyra/gui_eob.h @@ -156,7 +156,7 @@ private: static const uint8 _highlightColorTableEGA[]; }; -} // End of namespace Kyra +} // End of namespace Kyra #endif // ENABLE_EOB diff --git a/engines/kyra/gui_hof.cpp b/engines/kyra/gui_hof.cpp index 36fbb0b40a..3a2c07beff 100644 --- a/engines/kyra/gui_hof.cpp +++ b/engines/kyra/gui_hof.cpp @@ -521,7 +521,7 @@ void KyraEngine_HoF::bookPrintText(int dstPage, const uint8 *str, int x, int y, Screen::FontId oldFont = _screen->setFont(_flags.lang == Common::JA_JPN ? Screen::FID_SJIS_FNT : Screen::FID_BOOKFONT_FNT); _screen->_charWidth = -2; - _screen->printText((const char *)str, x, y, color, (_flags.lang == Common::JA_JPN) ? 0xf6 : 0); + _screen->printText((const char *)str, x, y, color, (_flags.lang == Common::JA_JPN) ? 0xF6 : 0); _screen->_charWidth = 0; _screen->setFont(oldFont); diff --git a/engines/kyra/gui_lol.cpp b/engines/kyra/gui_lol.cpp index a79da0681e..f7a5386574 100644 --- a/engines/kyra/gui_lol.cpp +++ b/engines/kyra/gui_lol.cpp @@ -120,7 +120,7 @@ void LoLEngine::gui_drawScroll() { if (h) { _screen->copyRegion(201, 1, 17, 15, 6, h, 2, 2, Screen::CR_NO_P_CHECK); _screen->copyRegion(208, 1, 89, 15, 6, h, 2, 2, Screen::CR_NO_P_CHECK); - _screen->fillRect(21, 15, 89, h + 15, _flags.use16ColorMode ? 0xbb : 206); + _screen->fillRect(21, 15, 89, h + 15, _flags.use16ColorMode ? 0xBB : 206); } _screen->copyRegion(112, 16, 12, h + 15, 87, 14, 2, 2, Screen::CR_NO_P_CHECK); @@ -172,7 +172,7 @@ void LoLEngine::gui_displayCharInventory(int charNum) { gui_drawAllCharPortraitsWithStats(); if (_flags.use16ColorMode) - _screen->fprintString("%s", 156, 8, 0xe1, 0, 1, l->name); + _screen->fprintString("%s", 156, 8, 0xE1, 0, 1, l->name); else _screen->fprintString("%s", 157, 9, 254, 0, 5, l->name); @@ -182,7 +182,7 @@ void LoLEngine::gui_displayCharInventory(int charNum) { gui_drawCharInventoryItem(i); Screen::FontId of = _screen->setFont(Screen::FID_9_FNT); - _screen->fprintString("%s", 182, 103, _flags.use16ColorMode ? 0xbb : 172, 0, 5, getLangString(0x4033)); + _screen->fprintString("%s", 182, 103, _flags.use16ColorMode ? 0xBB : 172, 0, 5, getLangString(0x4033)); _screen->setFont(of); static const uint16 statusFlags[] = { 0x0080, 0x0000, 0x1000, 0x0002, 0x100, 0x0001, 0x0000, 0x0000 }; @@ -206,7 +206,7 @@ void LoLEngine::gui_displayCharInventory(int charNum) { int32 b = l->experiencePts[i] - _expRequirements[l->skillLevels[i] - 1]; int32 e = _expRequirements[l->skillLevels[i]] - _expRequirements[l->skillLevels[i] - 1]; - while (e & 0xffff8000) { + while (e & 0xFFFF8000) { e >>= 1; c = b; b >>= 1; @@ -245,7 +245,7 @@ void LoLEngine::gui_printCharacterStats(int index, int redraw, int value) { // protection if (_flags.use16ColorMode) { y = (index + 2) << 3; - col = 0xa1; + col = 0xA1; if (redraw) _screen->fprintString("%s", offs + 108, y, col, 0, 0, getLangString(0x4014 + index)); } else { @@ -260,7 +260,7 @@ void LoLEngine::gui_printCharacterStats(int index, int redraw, int value) { y = s * 10 + 62; if (_flags.use16ColorMode) { y = (s + 8) << 3; - col = _characters[_selectedCharacter].flags & (0x200 << s) ? 0xe1 : 0x81; + col = _characters[_selectedCharacter].flags & (0x200 << s) ? 0xE1 : 0x81; if (redraw) _screen->fprintString("%s", offs + 108, y, col, 0, 0, getLangString(0x4014 + index)); } else { @@ -274,7 +274,7 @@ void LoLEngine::gui_printCharacterStats(int index, int redraw, int value) { if (offs) _screen->copyRegion(294, y, 182 + offs, y, 18, 8, 6, _screen->_curPage, Screen::CR_NO_P_CHECK); - Screen::FontId of = _flags.use16ColorMode ? _screen->setFont(Screen::FID_SJIS_FNT) : _screen->_currentFont; + Screen::FontId of = (_flags.lang == Common::JA_JPN && _flags.use16ColorMode) ? _screen->setFont(Screen::FID_SJIS_FNT) : _screen->_currentFont; _screen->fprintString("%d", 200 + offs, y, col, 0, _flags.use16ColorMode ? 2 : 6, value); _screen->setFont(of); } @@ -322,12 +322,12 @@ void LoLEngine::gui_changeCharacterStats(int charNum) { void LoLEngine::gui_drawCharInventoryItem(int itemIndex) { static const uint8 slotShapes[] = { 0x30, 0x34, 0x30, 0x34, 0x2E, 0x2F, 0x32, 0x33, 0x31, 0x35, 0x35 }; - + //2Eh, 32h, 2Eh, 32h, 2Ch, 2Dh, 30h, 31h, 2Fh, 33h, 33h const uint8 *coords = &_charInvDefs[_charInvIndex[_characters[_selectedCharacter].raceClassSex] * 22 + itemIndex * 2]; uint8 x = *coords++; uint8 y = *coords; - if (y == 0xff) + if (y == 0xFF) return; if (!_screen->_curPage) @@ -366,7 +366,7 @@ void LoLEngine::gui_drawCharPortraitWithStats(int charNum) { gui_drawCharFaceShape(charNum, 0, 1, _screen->_curPage); if (_flags.use16ColorMode) { - gui_drawLiveMagicBar(33, 32, _characters[charNum].magicPointsCur, 0, _characters[charNum].magicPointsMax, 5, 32, 0xaa, 0x44, 0); + gui_drawLiveMagicBar(33, 32, _characters[charNum].magicPointsCur, 0, _characters[charNum].magicPointsMax, 5, 32, 0xAA, 0x44, 0); gui_drawLiveMagicBar(39, 32, _characters[charNum].hitPointsCur, 0, _characters[charNum].hitPointsMax, 5, 32, 0x66, 0x44, 1); _screen->printText(getLangString(0x4253), 33, 1, 0x99, 0); _screen->printText(getLangString(0x4254), 39, 1, 0x55, 0); @@ -493,7 +493,7 @@ void LoLEngine::gui_drawLiveMagicBar(int x, int y, int curPoints, int unk, int m if (flag) { t = maxPoints >> 1; if (t > curPoints) - col1 = _flags.use16ColorMode ? 0xbb : 144; + col1 = _flags.use16ColorMode ? 0xBB : 144; t = maxPoints >> 2; if (t > curPoints) col1 = _flags.use16ColorMode ? 0x88 : 132; @@ -525,9 +525,9 @@ void LoLEngine::calcCharPortraitXpos() { } void LoLEngine::gui_drawMoneyBox(int pageNum) { - static const uint16 moneyX256[] = { 0x128, 0x134, 0x12b, 0x131, 0x12e}; + static const uint16 moneyX256[] = { 0x128, 0x134, 0x12B, 0x131, 0x12E}; static const uint16 moneyY256[] = { 0x73, 0x73, 0x74, 0x74, 0x75}; - static const uint16 moneyX16[] = { 0x127, 0x133, 0x12a, 0x130, 0x12d}; + static const uint16 moneyX16[] = { 0x127, 0x133, 0x12A, 0x130, 0x12D}; static const uint16 moneyY16[] = { 0x74, 0x74, 0x75, 0x75, 0x76}; int backupPage = _screen->_curPage; @@ -551,11 +551,11 @@ void LoLEngine::gui_drawMoneyBox(int pageNum) { continue; uint8 h = _moneyColumnHeight[i] - 1; - _screen->drawClippedLine(moneyX[i], moneyY[i], moneyX[i], moneyY[i] - h, _flags.use16ColorMode ? 1 : 0xd2); - _screen->drawClippedLine(moneyX[i] + 1, moneyY[i], moneyX[i] + 1, moneyY[i] - h, _flags.use16ColorMode ? 2 : 0xd1); - _screen->drawClippedLine(moneyX[i] + 2, moneyY[i], moneyX[i] + 2, moneyY[i] - h, _flags.use16ColorMode ? 3 : 0xd0); - _screen->drawClippedLine(moneyX[i] + 3, moneyY[i], moneyX[i] + 3, moneyY[i] - h, _flags.use16ColorMode ? 2 : 0xd1); - _screen->drawClippedLine(moneyX[i] + 4, moneyY[i], moneyX[i] + 4, moneyY[i] - h, _flags.use16ColorMode ? 1 : 0xd2); + _screen->drawClippedLine(moneyX[i], moneyY[i], moneyX[i], moneyY[i] - h, _flags.use16ColorMode ? 1 : 0xD2); + _screen->drawClippedLine(moneyX[i] + 1, moneyY[i], moneyX[i] + 1, moneyY[i] - h, _flags.use16ColorMode ? 2 : 0xD1); + _screen->drawClippedLine(moneyX[i] + 2, moneyY[i], moneyX[i] + 2, moneyY[i] - h, _flags.use16ColorMode ? 3 : 0xD0); + _screen->drawClippedLine(moneyX[i] + 3, moneyY[i], moneyX[i] + 3, moneyY[i] - h, _flags.use16ColorMode ? 2 : 0xD1); + _screen->drawClippedLine(moneyX[i] + 4, moneyY[i], moneyX[i] + 4, moneyY[i] - h, _flags.use16ColorMode ? 1 : 0xD2); } Screen::FontId backupFont = _screen->setFont(Screen::FID_6_FNT); @@ -584,7 +584,7 @@ void LoLEngine::gui_drawCompass() { _compassDirection = _currentDirection << 6; } - int t = ((_compassDirection + 4) >> 3) & 0x1f; + int t = ((_compassDirection + 4) >> 3) & 0x1F; if (t == _compassDirectionIndex) return; @@ -744,13 +744,13 @@ void LoLEngine::gui_toggleFightButtons(bool disable) { if (disable) _characters[i].flags |= 0x2000; else - _characters[i].flags &= 0xdfff; + _characters[i].flags &= 0xDFFF; if (disable && !textEnabled()) { int u = _selectedCharacter; _selectedCharacter = 99; int f = _updateFlags; - _updateFlags &= 0xfffd; + _updateFlags &= 0xFFFD; gui_drawCharPortraitWithStats(i); @@ -774,7 +774,7 @@ void LoLEngine::gui_updateInput() { if (inputFlag && _activeMagicMenu != -1 && !(inputFlag & 0x8800)) { gui_enableDefaultPlayfieldButtons(); - _characters[_activeMagicMenu].flags &= 0xffef; + _characters[_activeMagicMenu].flags &= 0xFFEF; gui_drawCharPortraitWithStats(_activeMagicMenu); gui_triggerEvent(inputFlag); _preserveEvents = false; @@ -875,7 +875,7 @@ void LoLEngine::gui_initCharInventorySpecialButtons(int charNum) { const uint8 *s = &_charInvDefs[_charInvIndex[_characters[charNum].raceClassSex] * 22]; for (int i = 0; i < 11; i++) { - if (*s != 0xff) + if (*s != 0xFF) gui_initButton(33 + i, s[0], s[1], i); s += 2; } @@ -916,7 +916,7 @@ void LoLEngine::gui_initButton(int index, int x, int y, int val) { *b = Button(); b->nextButton = 0; - b->data0Val2 = b->data1Val2 = b->data2Val2 = 0xfe; + b->data0Val2 = b->data1Val2 = b->data2Val2 = 0xFE; b->data0Val3 = b->data1Val3 = b->data2Val3 = 0x01; b->index = cnt; @@ -925,7 +925,7 @@ void LoLEngine::gui_initButton(int index, int x, int y, int val) { b->dimTableIndex = _buttonData[index].screenDim; b->flags = _buttonData[index].buttonflags; - b->arg = (val != -1) ? (uint8)(val & 0xff) : _buttonData[index].index; + b->arg = (val != -1) ? (uint8)(val & 0xFF) : _buttonData[index].index; if (index == 15) { // magic sub menu @@ -1100,17 +1100,17 @@ int LoLEngine::clickedMagicSubmenu(Button *button) { gui_enableDefaultPlayfieldButtons(); if (checkMagic(c, _availableSpells[_selectedSpell], spellLevel)) { - _characters[c].flags &= 0xffef; + _characters[c].flags &= 0xFFEF; gui_drawCharPortraitWithStats(c); } else { _characters[c].flags |= 4; - _characters[c].flags &= 0xffef; + _characters[c].flags &= 0xFFEF; if (castSpell(c, _availableSpells[_selectedSpell], spellLevel)) { setCharacterUpdateEvent(c, 1, 8, 1); increaseExperience(c, 2, spellLevel * spellLevel); } else { - _characters[c].flags &= 0xfffb; + _characters[c].flags &= 0xFFFB; gui_drawCharPortraitWithStats(c); } } @@ -1120,7 +1120,7 @@ int LoLEngine::clickedMagicSubmenu(Button *button) { } int LoLEngine::clickedScreen(Button *button) { - _characters[_activeMagicMenu].flags &= 0xffef; + _characters[_activeMagicMenu].flags &= 0xFFEF; gui_drawCharPortraitWithStats(_activeMagicMenu); _activeMagicMenu = -1; @@ -1178,12 +1178,12 @@ int LoLEngine::clickedPortraitEtcRight(Button *button) { runItemScript(c, _itemInHand, 0x400, 0, 0); runLevelScriptCustom(_currentBlock, 0x400, c, _itemInHand, 0, 0); } else { - _txt->printMessage(2, getLangString(0x402c), _characters[c].name); + _txt->printMessage(2, getLangString(0x402C), _characters[c].name); } return 1; } - _txt->printMessage(2, "%s", getLangString((flg & 8) ? 0x4029 : ((flg & 0x10) ? 0x402a : 0x402b))); + _txt->printMessage(2, "%s", getLangString((flg & 8) ? 0x4029 : ((flg & 0x10) ? 0x402A : 0x402B))); return 1; } @@ -1235,13 +1235,13 @@ int LoLEngine::clickedCharInventorySlot(Button *button) { } int LoLEngine::clickedExitCharInventory(Button *button) { - _updateFlags &= 0xfff3; + _updateFlags &= 0xFFF3; gui_enableDefaultPlayfieldButtons(); _weaponsDisabled = false; for (int i = 0; i < 4; i++) { if (_charInventoryUnk & (1 << i)) - _characters[i].flags &= 0xf1ff; + _characters[i].flags &= 0xF1FF; } _screen->copyBlockToPage(2, 0, 0, 320, 200, _pageBuffer1); @@ -1315,7 +1315,7 @@ int LoLEngine::clickedScenePickupItem(Button *button) { uint16 block = (p <= 128) ? calcNewBlockPosition(_currentBlock, _currentDirection) : _currentBlock; - int found = checkSceneForItems(&_levelBlockProperties[block].drawObjects, p & 0x7f); + int found = checkSceneForItems(&_levelBlockProperties[block].drawObjects, p & 0x7F); if (found != -1) { removeLevelItem(found, block); @@ -1465,7 +1465,7 @@ int LoLEngine::clickedSpellTargetCharacter(Button *button) { int t = button->arg; _txt->printMessage(0, "%s.\r", _characters[t].name); - if ((_spellProperties[_activeSpell.spell].flags & 0xff) == 1) { + if ((_spellProperties[_activeSpell.spell].flags & 0xFF) == 1) { _activeSpell.target = t; castHealOnSingleCharacter(&_activeSpell); } @@ -1504,7 +1504,7 @@ int LoLEngine::clickedSceneThrowItem(Button *button) { uint16 y = 0; calcCoordinates(x, y, _currentBlock, 0x80, 0x80); - if (launchObject(0, _itemInHand, x, y, 12, _currentDirection << 1, 6, _selectedCharacter, 0x3f)) { + if (launchObject(0, _itemInHand, x, y, 12, _currentDirection << 1, 6, _selectedCharacter, 0x3F)) { snd_playSoundEffect(18, -1); setHandItem(0); } @@ -1520,7 +1520,7 @@ int LoLEngine::clickedOptions(Button *button) { _updateFlags |= 4; Button b; - b.data0Val2 = b.data1Val2 = b.data2Val2 = 0xfe; + b.data0Val2 = b.data1Val2 = b.data2Val2 = 0xFE; b.data0Val3 = b.data1Val3 = b.data2Val3 = 0x01; if (_weaponsDisabled) @@ -1540,7 +1540,7 @@ int LoLEngine::clickedOptions(Button *button) { _gui->runMenu(_gui->_mainMenu); - _updateFlags &= 0xfffb; + _updateFlags &= 0xFFFB; setMouseCursorToItemInHand(); resetLampStatus(); gui_enableDefaultPlayfieldButtons(); @@ -1564,7 +1564,7 @@ int LoLEngine::clickedRestParty(Button *button) { gui_toggleButtonDisplayMode(_flags.isTalkie ? 77 : 75, 1); Button b; - b.data0Val2 = b.data1Val2 = b.data2Val2 = 0xfe; + b.data0Val2 = b.data1Val2 = b.data2Val2 = 0xFE; b.data0Val3 = b.data1Val3 = b.data2Val3 = 0x01; if (_weaponsDisabled) @@ -1710,7 +1710,7 @@ int LoLEngine::clickedRestParty(Button *button) { bool setframe = true; if (_characters[i].flags & 0x1000) { - _characters[i].flags &= 0xefff; + _characters[i].flags &= 0xEFFF; if (_partyAwake) { if (_characters[i].damageSuffered) { @@ -1733,7 +1733,7 @@ int LoLEngine::clickedRestParty(Button *button) { setTemporaryFaceFrame(i, frm, upd, 1); } - _updateFlags &= 0xfffe; + _updateFlags &= 0xFFFE; _partyAwake = true; updateDrawPage2(); gui_drawScene(0); @@ -1742,7 +1742,7 @@ int LoLEngine::clickedRestParty(Button *button) { } else { for (int i = 0; i < 4; i++) - _characters[i].flags &= 0xefff; + _characters[i].flags &= 0xEFFF; if (needPoisoningFlags) { setTemporaryFaceFrameForAllCharacters(0, 0, 0); @@ -1750,7 +1750,7 @@ int LoLEngine::clickedRestParty(Button *button) { if (needPoisoningFlags & (1 << i)) setTemporaryFaceFrame(i, 3, 8, 0); } - _txt->printMessage(0x8000, "%s", getLangString(0x405a)); + _txt->printMessage(0x8000, "%s", getLangString(0x405A)); gui_drawAllCharPortraitsWithStats(); } else { @@ -1773,10 +1773,10 @@ int LoLEngine::clickedCompass(Button *button) { return 0; if (_compassBroken) { - if (characterSays(0x425b, -1, true)) - _txt->printMessage(4, "%s", getLangString(0x425b)); + if (characterSays(0x425B, -1, true)) + _txt->printMessage(4, "%s", getLangString(0x425B)); } else { - _txt->printMessage(0, "%s", getLangString(0x402f + _currentDirection)); + _txt->printMessage(0, "%s", getLangString(0x402F + _currentDirection)); } return 1; @@ -1813,8 +1813,8 @@ int LoLEngine::clickedLamp(Button *button) { _lampOilStatus += 100; } else { - uint16 s = (_lampOilStatus >= 100) ? 0x4060 : ((!_lampOilStatus) ? 0x405c : (_lampOilStatus / 33) + 0x405d); - _txt->printMessage(0, getLangString(0x405b), getLangString(s)); + uint16 s = (_lampOilStatus >= 100) ? 0x4060 : ((!_lampOilStatus) ? 0x405C : (_lampOilStatus / 33) + 0x405D); + _txt->printMessage(0, getLangString(0x405B), getLangString(s)); } if (_brightness) @@ -1836,7 +1836,7 @@ int LoLEngine::clickedStatusIcon(Button *button) { if (str == 0 || str > 3) return 1; - _txt->printMessage(0x8002, "%s", getLangString(str == 1 ? 0x424c : (str == 2 ? 0x424e : 0x424d))); + _txt->printMessage(0x8002, "%s", getLangString(str == 1 ? 0x424C : (str == 2 ? 0x424E : 0x424D))); return 1; } @@ -2239,7 +2239,7 @@ int GUI_LoL::runMenu(Menu &menu) { if (_currentMenu == &_gameOptions) { char *s = (char *)_vm->_tempBuffer5120; - Common::strlcpy(s, _vm->getLangString(0x406f + _vm->_monsterDifficulty), 30); + Common::strlcpy(s, _vm->getLangString(0x406F + _vm->_monsterDifficulty), 30); _currentMenu->item[_vm->gameFlags().isTalkie ? 0 : 2].itemString = s; s += (strlen(s) + 1); @@ -2252,7 +2252,7 @@ int GUI_LoL::runMenu(Menu &menu) { s += (strlen(s) + 1); if (_vm->gameFlags().isTalkie) { - Common::strlcpy(s, _vm->getLangString(0x42d6 + _vm->_lang), 30); + Common::strlcpy(s, _vm->getLangString(0x42D6 + _vm->_lang), 30); _currentMenu->item[3].itemString = s; s += (strlen(s) + 1); @@ -2315,7 +2315,7 @@ int GUI_LoL::runMenu(Menu &menu) { for (int ii = 0; ii < 3; ++ii) { Button *b = getButtonListData() + 1 + (i - 1) * 3 + ii; b->nextButton = 0; - b->data0Val2 = b->data1Val2 = b->data2Val2 = 0xfe; + b->data0Val2 = b->data1Val2 = b->data2Val2 = 0xFE; b->data0Val3 = b->data1Val3 = b->data2Val3 = 0x01; b->index = ii; @@ -2367,9 +2367,9 @@ int GUI_LoL::runMenu(Menu &menu) { int mw = (d->w << 3) + 1; int mh = d->h + 1; if (_vm->gameFlags().use16ColorMode) { - _screen->drawShadedBox(mx, my, mx + mw + 1, my + mh + 1, 0xdd, 0xff); - _screen->drawLine(true, mx + mw + 1, my, mh + 1, 0xcc); - _screen->drawLine(false, mx, my + mh + 1, mw + 2, 0xcc); + _screen->drawShadedBox(mx, my, mx + mw + 1, my + mh + 1, 0xDD, 0xFF); + _screen->drawLine(true, mx + mw + 1, my, mh + 1, 0xCC); + _screen->drawLine(false, mx, my + mh + 1, mw + 2, 0xCC); } else { _screen->drawShadedBox(mx, my, mx + mw, my + mh, 227, 223); } @@ -2622,7 +2622,7 @@ int GUI_LoL::clickedMainMenu(Button *button) { _newMenu = &_audioOptions; break; case 0x4006: - _choiceMenu.menuNameId = 0x400a; + _choiceMenu.menuNameId = 0x400A; _newMenu = &_choiceMenu; break; case 0x4005: @@ -2677,7 +2677,7 @@ int GUI_LoL::clickedDeleteMenu(Button *button) { return 1; } - _choiceMenu.menuNameId = 0x400b; + _choiceMenu.menuNameId = 0x400B; _newMenu = &_choiceMenu; int16 s = (int16)button->arg; _menuResult = _deleteMenu.item[-s - 2].saveSlot + 1; @@ -2689,7 +2689,7 @@ int GUI_LoL::clickedOptionsMenu(Button *button) { updateMenuButton(button); switch (button->arg) { - case 0xfff9: + case 0xFFF9: _vm->_configMusic ^= 1; _vm->sound()->enableMusic(_vm->_configMusic); @@ -2698,23 +2698,23 @@ int GUI_LoL::clickedOptionsMenu(Button *button) { else _vm->_sound->beginFadeOut(); break; - case 0xfff8: + case 0xFFF8: _vm->_configSounds ^= true; _vm->sound()->enableSFX(_vm->_configSounds); break; - case 0xfff7: + case 0xFFF7: _vm->_monsterDifficulty = (_vm->_monsterDifficulty + 1) % 3; break; - case 0xfff6: + case 0xFFF6: _vm->_smoothScrollingEnabled ^= true; break; - case 0xfff5: + case 0xFFF5: _vm->_floatingCursorsEnabled ^= true; break; - case 0xfff4: + case 0xFFF4: _vm->_lang = (_vm->_lang + 1) % 3; break; - case 0xfff3: + case 0xFFF3: _vm->_configVoice ^= 3; break; case 0x4072: { @@ -2793,7 +2793,7 @@ int GUI_LoL::clickedAudioMenu(Button *button) { } while (1); } else if (button->arg == 5) { _vm->_lastSpeechId = -1; - _vm->snd_playCharacterSpeech(0x42e0, 0, 0); + _vm->snd_playCharacterSpeech(0x42E0, 0, 0); } } diff --git a/engines/kyra/gui_lol.h b/engines/kyra/gui_lol.h index dbf54e41f0..79d4b6b4c9 100644 --- a/engines/kyra/gui_lol.h +++ b/engines/kyra/gui_lol.h @@ -36,9 +36,9 @@ namespace Kyra { menu.width = (dim->w << 3); \ menu.height = (dim->h); \ if (_vm->gameFlags().use16ColorMode) { \ - menu.bkgdColor = 0xcc; \ - menu.color1 = 0xff; \ - menu.color2 = 0xdd; \ + menu.bkgdColor = 0xCC; \ + menu.color1 = 0xFF; \ + menu.color2 = 0xDD; \ } else { \ menu.bkgdColor = 225; \ menu.color1 = 223; \ @@ -49,7 +49,7 @@ namespace Kyra { menu.numberOfItems = d; \ menu.titleX = (dim->sx << 3) + (dim->w << 2); \ menu.titleY = 6; \ - menu.textColor = _vm->gameFlags().use16ColorMode ? 0xe1 : 254; \ + menu.textColor = _vm->gameFlags().use16ColorMode ? 0xE1 : 254; \ menu.scrollUpButtonX = e; \ menu.scrollUpButtonY = f; \ menu.scrollDownButtonX = g; \ @@ -65,13 +65,13 @@ namespace Kyra { item.y = c; \ item.width = d; \ item.height = e; \ - item.textColor = _vm->gameFlags().use16ColorMode ? 0xc1 : 204; \ - item.highlightColor = _vm->gameFlags().use16ColorMode ? 0xe1 : 254; \ + item.textColor = _vm->gameFlags().use16ColorMode ? 0xC1 : 204; \ + item.highlightColor = _vm->gameFlags().use16ColorMode ? 0xE1 : 254; \ item.titleX = -1; \ if (_vm->gameFlags().use16ColorMode) { \ - item.bkgdColor = 0xcc; \ - item.color1 = 0xff; \ - item.color2 = 0xdd; \ + item.bkgdColor = 0xCC; \ + item.color1 = 0xFF; \ + item.color2 = 0xDD; \ } else { \ item.bkgdColor = 225; \ item.color1 = 223; \ diff --git a/engines/kyra/gui_mr.cpp b/engines/kyra/gui_mr.cpp index 37526f9a5f..bcbfe27b69 100644 --- a/engines/kyra/gui_mr.cpp +++ b/engines/kyra/gui_mr.cpp @@ -25,7 +25,7 @@ #include "kyra/text_mr.h" #include "kyra/resource.h" #include "kyra/timer.h" -#include "kyra/sound.h" +#include "kyra/sound_digital.h" #include "common/system.h" diff --git a/engines/kyra/gui_rpg.cpp b/engines/kyra/gui_rpg.cpp index be40050bb1..ab25f95df8 100644 --- a/engines/kyra/gui_rpg.cpp +++ b/engines/kyra/gui_rpg.cpp @@ -76,8 +76,8 @@ void KyraRpgEngine::gui_drawHorizontalBarGraph(int x, int y, int w, int h, int32 screen()->fillRect(x + t, y, x + w - 1, y + h, col2); } -void KyraRpgEngine::gui_initButtonsFromList(const int16 *list) { - while (*list != -1) +void KyraRpgEngine::gui_initButtonsFromList(const uint8 *list) { + while (*list != 0xFF) gui_initButton(*list++); } @@ -107,7 +107,7 @@ bool KyraRpgEngine::clickedShape(int shapeIndex) { uint16 s = _levelDecorationProperties[shapeIndex].shapeIndex[1]; - if (s == 0xffff) + if (s == 0xFFFF) continue; int w = _flags.gameID == GI_LOL ? _levelDecorationShapes[s][3] : (_levelDecorationShapes[s][2] << 3); diff --git a/engines/kyra/items_eob.cpp b/engines/kyra/items_eob.cpp index 0994e12e4f..6c7ccf14dc 100644 --- a/engines/kyra/items_eob.cpp +++ b/engines/kyra/items_eob.cpp @@ -118,7 +118,7 @@ void EoBCoreEngine::setItemPosition(Item *itemQueue, int block, Item item, int p EoBItem *itm = &_items[item]; itm->pos = pos; itm->block = block; - itm->level = block < 0 ? 0xff : _currentLevel; + itm->level = block < 0 ? 0xFF : _currentLevel; if (!*itemQueue) { *itemQueue = itm->next = itm->prev = item; @@ -141,7 +141,7 @@ void EoBCoreEngine::createInventoryItem(EoBCharacter *c, Item itemIndex, int16 i if (itemValue != -1) _items[itemIndex].value = itemValue; - if (itemValue && ((_itemTypes[_items[itemIndex].type].extraProperties & 0x7f) < 4)) + if (itemValue && ((_itemTypes[_items[itemIndex].type].extraProperties & 0x7F) < 4)) _items[itemIndex].flags |= 0x80; if (c->inventory[preferedInventorySlot]) { @@ -208,7 +208,7 @@ int EoBCoreEngine::validateInventorySlotForItem(Item item, int charIndex, int sl } int itm = _characters[charIndex].inventory[slot]; - int ex = _itemTypes[_items[itm].type].extraProperties & 0x7f; + int ex = _itemTypes[_items[itm].type].extraProperties & 0x7F; if (_items[itm].flags & 0x20 && (_flags.gameID == GI_EOB1 || slot < 2)) { if (_flags.gameID == GI_EOB2 && ex > 0 && ex < 4) @@ -216,7 +216,7 @@ int EoBCoreEngine::validateInventorySlotForItem(Item item, int charIndex, int sl return 0; } - uint16 v = item ? _itemTypes[_items[item].type].invFlags : 0xffff; + uint16 v = item ? _itemTypes[_items[item].type].invFlags : 0xFFFF; if (v & _slotValidationFlags[slot]) return 1; @@ -359,7 +359,7 @@ void EoBCoreEngine::printFullItemName(Item item) { EoBItem *itm = &_items[item]; const char *nameUnid = _itemNames[itm->nameUnid]; const char *nameId = _itemNames[itm->nameId]; - uint8 f = _itemTypes[itm->type].extraProperties & 0x7f; + uint8 f = _itemTypes[itm->type].extraProperties & 0x7F; int8 v = itm->value; const char *tstr2 = 0; diff --git a/engines/kyra/items_hof.cpp b/engines/kyra/items_hof.cpp index ef2c50c0c5..e1d3c659de 100644 --- a/engines/kyra/items_hof.cpp +++ b/engines/kyra/items_hof.cpp @@ -84,7 +84,7 @@ bool KyraEngine_HoF::dropItem(int unk1, Item item, int x, int y, int unk2) { bool success = processItemDrop(_mainCharacter.sceneId, item, x, y, unk1, unk2); if (!success) { - snd_playSoundEffect(0x0d); + snd_playSoundEffect(0x0D); if (countAllItems() >= 30) showMessageFromCCode(5, 0x84, 0); } @@ -205,7 +205,7 @@ void KyraEngine_HoF::itemDropDown(int startX, int startY, int dstX, int dstY, in if (startX == dstX && startY == dstY) { if (_layerFlagTable[_screen->getLayer(dstX, dstY)] && item != 13) { updateCharFacing(); - snd_playSoundEffect(0x2d); + snd_playSoundEffect(0x2D); removeHandItem(); objectChat(getTableString(0xFF, _cCodeBuffer, 1), 0, 0x83, 0xFF); } else { @@ -213,7 +213,7 @@ void KyraEngine_HoF::itemDropDown(int startX, int startY, int dstX, int dstY, in _itemList[itemSlot].y = dstY; _itemList[itemSlot].id = item; _itemList[itemSlot].sceneId = _mainCharacter.sceneId; - snd_playSoundEffect(0x0c); + snd_playSoundEffect(0x0C); addItemToAnimList(itemSlot); } } else { @@ -283,7 +283,7 @@ void KyraEngine_HoF::itemDropDown(int startX, int startY, int dstX, int dstY, in if (_layerFlagTable[_screen->getLayer(dstX, dstY)] && item != 13) { updateCharFacing(); - snd_playSoundEffect(0x2d); + snd_playSoundEffect(0x2D); removeHandItem(); _screen->showMouse(); objectChat(getTableString(0xFF, _cCodeBuffer, 1), 0, 0x83, 0xFF); @@ -292,7 +292,7 @@ void KyraEngine_HoF::itemDropDown(int startX, int startY, int dstX, int dstY, in _itemList[itemSlot].y = dstY; _itemList[itemSlot].id = item; _itemList[itemSlot].sceneId = _mainCharacter.sceneId; - snd_playSoundEffect(0x0c); + snd_playSoundEffect(0x0C); addItemToAnimList(itemSlot); _screen->showMouse(); } @@ -307,7 +307,7 @@ void KyraEngine_HoF::exchangeMouseItem(int itemPos) { _itemInHand = itemId; addItemToAnimList(itemPos); - snd_playSoundEffect(0x0b); + snd_playSoundEffect(0x0B); setMouseCursor(_itemInHand); int str2 = 7; @@ -331,7 +331,7 @@ bool KyraEngine_HoF::pickUpItem(int x, int y) { deleteItemAnimEntry(itemPos); int itemId = _itemList[itemPos].id; _itemList[itemPos].id = kItemNone; - snd_playSoundEffect(0x0b); + snd_playSoundEffect(0x0B); setMouseCursor(itemId); int str2 = 7; diff --git a/engines/kyra/items_lok.cpp b/engines/kyra/items_lok.cpp index b92cd616c1..8ee07e8271 100644 --- a/engines/kyra/items_lok.cpp +++ b/engines/kyra/items_lok.cpp @@ -29,9 +29,9 @@ namespace Kyra { int KyraEngine_LoK::findDuplicateItemShape(int shape) { static const uint8 dupTable[] = { - 0x48, 0x46, 0x49, 0x47, 0x4a, 0x46, 0x4b, 0x47, - 0x4c, 0x46, 0x4d, 0x47, 0x5b, 0x5a, 0x5c, 0x5a, - 0x5d, 0x5a, 0x5e, 0x5a, 0xFF, 0xFF + 0x48, 0x46, 0x49, 0x47, 0x4A, 0x46, 0x4B, 0x47, + 0x4C, 0x46, 0x4D, 0x47, 0x5B, 0x5A, 0x5C, 0x5A, + 0x5D, 0x5A, 0x5E, 0x5A, 0xFF, 0xFF }; int i = 0; diff --git a/engines/kyra/items_lol.cpp b/engines/kyra/items_lol.cpp index 409b53f6f0..f7fd04bab8 100644 --- a/engines/kyra/items_lol.cpp +++ b/engines/kyra/items_lol.cpp @@ -29,7 +29,7 @@ namespace Kyra { LoLObject *LoLEngine::findObject(uint16 index) { if (index & 0x8000) - return &_monsters[index & 0x7fff]; + return &_monsters[index & 0x7FFF]; else return &_itemsInPlay[index]; } @@ -221,7 +221,7 @@ Item LoLEngine::makeItem(int itemType, int curFrame, int flags) { memset(&_itemsInPlay[slot], 0, sizeof(LoLItem)); _itemsInPlay[slot].itemPropertyIndex = itemType; - _itemsInPlay[slot].shpCurFrame_flg = (curFrame & 0x1fff) | flags; + _itemsInPlay[slot].shpCurFrame_flg = (curFrame & 0x1FFF) | flags; _itemsInPlay[slot].level = -1; return slot; @@ -292,7 +292,7 @@ void LoLEngine::runItemScript(int charNum, Item item, int flags, int next, int r memset(&scriptState, 0, sizeof(EMCState)); uint8 func = item ? _itemProperties[_itemsInPlay[item].itemPropertyIndex].itemScriptFunc : 3; - if (func == 0xff) + if (func == 0xFF) return; _emc->init(&scriptState, &_itemScript); @@ -351,8 +351,8 @@ bool LoLEngine::itemEquipped(int charNum, uint16 itemType) { void LoLEngine::setItemPosition(Item item, uint16 x, uint16 y, int flyingHeight, int moveable) { if (!flyingHeight) { - x = (x & 0xffc0) | 0x40; - y = (y & 0xffc0) | 0x40; + x = (x & 0xFFC0) | 0x40; + y = (y & 0xFFC0) | 0x40; } uint16 block = calcBlockIndex(x, y); @@ -364,7 +364,7 @@ void LoLEngine::setItemPosition(Item item, uint16 x, uint16 y, int flyingHeight, if (moveable) _itemsInPlay[item].shpCurFrame_flg |= 0x4000; else - _itemsInPlay[item].shpCurFrame_flg &= 0xbfff; + _itemsInPlay[item].shpCurFrame_flg &= 0xBFFF; assignItemToBlock(&_levelBlockProperties[block].assignedObjects, item); @@ -429,9 +429,9 @@ bool LoLEngine::launchObject(int objectType, Item item, int startX, int startY, if (attackerId != -1) { if (attackerId & 0x8000) { - t->flags &= 0xfd; + t->flags &= 0xFD; } else { - t->flags &= 0xfb; + t->flags &= 0xFB; increaseExperience(attackerId, 1, 2); } } @@ -455,8 +455,8 @@ void LoLEngine::endObjectFlight(FlyingObject *t, int x, int y, int collisionType if (t->objectType == 0 || t->objectType == 1) { objectFlightProcessHits(t, cx, cy, collisionType); - t->x = (cx & 0xffc0) | 0x40; - t->y = (cy & 0xffc0) | 0x40; + t->x = (cx & 0xFFC0) | 0x40; + t->y = (cy & 0xFFC0) | 0x40; t->flyingHeight = 0; updateObjectFlightPosition(t); } diff --git a/engines/kyra/kyra_hof.cpp b/engines/kyra/kyra_hof.cpp index 7fbecb7f53..b180285ffc 100644 --- a/engines/kyra/kyra_hof.cpp +++ b/engines/kyra/kyra_hof.cpp @@ -52,13 +52,6 @@ KyraEngine_HoF::KyraEngine_HoF(OSystem *system, const GameFlags &flags) : KyraEn _screen = 0; _text = 0; - _seqProcessedString = 0; - _activeWSA = 0; - _activeText = 0; - _seqWsa = 0; - _sequences = 0; - _sequenceSoundList = 0; - _gamePlayBuffer = 0; _cCodeBuffer = _optionsBuffer = _chapterBuffer = 0; @@ -89,7 +82,6 @@ KyraEngine_HoF::KyraEngine_HoF(OSystem *system, const GameFlags &flags) : KyraEn memset(&_invWsa, 0, sizeof(_invWsa)); _itemAnimDefinition = 0; - _demoAnimData = 0; _nextAnimItem = 0; for (int i = 0; i < 15; i++) @@ -136,7 +128,6 @@ KyraEngine_HoF::KyraEngine_HoF(OSystem *system, const GameFlags &flags) : KyraEn memset(_cauldronStateTables, 0, sizeof(_cauldronStateTables)); _menuDirectlyToLoad = false; - _menu = 0; _chatIsNote = false; memset(&_npcScriptData, 0, sizeof(_npcScriptData)); @@ -148,7 +139,6 @@ KyraEngine_HoF::KyraEngine_HoF(OSystem *system, const GameFlags &flags) : KyraEn KyraEngine_HoF::~KyraEngine_HoF() { cleanup(); - seq_uninit(); delete _screen; delete _text; @@ -157,15 +147,6 @@ KyraEngine_HoF::~KyraEngine_HoF() { _text = 0; delete _invWsa.wsa; - if (_sequenceSoundList) { - for (int i = 0; i < _sequenceSoundListSize; i++) { - if (_sequenceSoundList[i]) - delete[] _sequenceSoundList[i]; - } - delete[] _sequenceSoundList; - _sequenceSoundList = NULL; - } - delete[] _dlgBuffer; for (int i = 0; i < 19; i++) delete[] _conversationState[i]; @@ -179,41 +160,13 @@ KyraEngine_HoF::~KyraEngine_HoF() { void KyraEngine_HoF::pauseEngineIntern(bool pause) { KyraEngine_v2::pauseEngineIntern(pause); + seq_pausePlayer(pause); + if (!pause) { uint32 pausedTime = _system->getMillis() - _pauseStart; _pauseStart = 0; - // sequence player - // - // Timers in KyraEngine_HoF::seq_cmpFadeFrame() and KyraEngine_HoF::seq_animatedSubFrame() - // have been left out for now. I think we don't need them here. - - _seqStartTime += pausedTime; - _seqSubFrameStartTime += pausedTime; - _seqEndTime += pausedTime; - _seqSubFrameEndTimeInternal += pausedTime; - _seqWsaChatTimeout += pausedTime; - _seqWsaChatFrameTimeout += pausedTime; - - if (_activeText) { - for (int x = 0; x < 10; x++) { - if (_activeText[x].duration != -1) - _activeText[x].startTime += pausedTime; - } - } - - if (_activeWSA) { - for (int x = 0; x < 8; x++) { - if (_activeWSA[x].flags != -1) - _activeWSA[x].nextFrame += pausedTime; - } - } - _nextIdleAnim += pausedTime; - - for (int x = 0; x < _itemAnimDefinitionSize; x++) - _activeItemAnim[x].nextFrameTime += pausedTime; - _tim->refreshTimersAfterPause(pausedTime); } } @@ -244,8 +197,6 @@ Common::Error KyraEngine_HoF::init() { _screen->loadFont(_screen->FID_8_FNT, "8FAT.FNT"); _screen->loadFont(_screen->FID_BOOKFONT_FNT, "BOOKFONT.FNT"); } - _screen->loadFont(_screen->FID_GOLDFONT_FNT, "GOLDFONT.FNT"); - _screen->setFont(_flags.lang == Common::JA_JPN ? Screen::FID_SJIS_FNT : _screen->FID_8_FNT); _screen->setAnimBlockPtr(3504); @@ -254,13 +205,6 @@ Common::Error KyraEngine_HoF::init() { if (!_sound->init()) error("Couldn't init sound"); - _abortIntroFlag = false; - - if (_sequenceStrings) { - for (int i = 0; i < MIN(33, _sequenceStringsSize); i++) - _sequenceStringsDuration[i] = (int) strlen(_sequenceStrings[i]) * 8; - } - // No mouse display in demo if (_flags.isDemo && !_flags.isTalkie) return Common::kNoError; @@ -279,28 +223,24 @@ Common::Error KyraEngine_HoF::init() { } Common::Error KyraEngine_HoF::go() { + int menuChoice = 0; + if (_gameToLoad == -1) { if (_flags.platform == Common::kPlatformFMTowns || _flags.platform == Common::kPlatformPC98) seq_showStarcraftLogo(); if (_flags.isDemo && !_flags.isTalkie) { -#ifdef ENABLE_LOL - if (_flags.gameID == GI_LOL) - seq_playSequences(kSequenceLoLDemoScene1, kSequenceLoLDemoScene6); - else -#endif // ENABLE_LOL - seq_playSequences(kSequenceDemoVirgin, kSequenceDemoFisher); - _menuChoice = 4; + menuChoice = seq_playDemo(); } else { - seq_playSequences(kSequenceVirgin, kSequenceZanfaun); + menuChoice = seq_playIntro(); } } else { - _menuChoice = 1; + menuChoice = 1; } _res->unloadAllPakFiles(); - if (_menuChoice != 4) { + if (menuChoice != 4) { // load just the pak files needed for ingame _staticres->loadStaticResourceFile(); @@ -317,24 +257,24 @@ Common::Error KyraEngine_HoF::go() { } } - _menuDirectlyToLoad = (_menuChoice == 3) ? true : false; + _menuDirectlyToLoad = (menuChoice == 3) ? true : false; _menuDirectlyToLoad &= saveFileLoadable(0); - if (_menuChoice & 1) { + if (menuChoice & 1) { startup(); if (!shouldQuit()) runLoop(); cleanup(); if (_showOutro) - seq_playSequences(kSequenceFunters, kSequenceFrash); + seq_playOutro(); } return Common::kNoError; } void KyraEngine_HoF::startup() { - _sound->setSoundList(&_soundData[kMusicIngame]); + _sound->selectAudioResourceSet(kMusicIngame); // The track map is exactly the same // for FM-TOWNS and DOS _trackMap = _dosTrackMap; @@ -1508,7 +1448,7 @@ void KyraEngine_HoF::snd_playSoundEffect(int track, int volume) { int16 vocIndex = (int16)READ_LE_UINT16(&_ingameSoundIndex[track * 2]); if (vocIndex != -1) { - _sound->voicePlay(_ingameSoundList[vocIndex], 0, 255, true); + _sound->voicePlay(_ingameSoundList[vocIndex], 0, 255, 255, true); } else if (_flags.platform == Common::kPlatformPC) { if (_sound->getSfxType() == Sound::kMidiMT32) track = track < _mt32SfxMapSize ? _mt32SfxMap[track] - 1 : -1; diff --git a/engines/kyra/kyra_hof.h b/engines/kyra/kyra_hof.h index 182854cdf1..1b84e5b56f 100644 --- a/engines/kyra/kyra_hof.h +++ b/engines/kyra/kyra_hof.h @@ -35,158 +35,13 @@ namespace Kyra { -enum Sequences { - kSequenceVirgin = 0, - kSequenceWestwood, - kSequenceTitle, - kSequenceOverview, - kSequenceLibrary, - kSequenceHand, - kSequencePoint, - kSequenceZanfaun, - - kSequenceFunters, - kSequenceFerb, - kSequenceFish, - kSequenceFheep, - kSequenceFarmer, - kSequenceFuards, - kSequenceFirates, - kSequenceFrash, - - kSequenceArraySize -}; - -enum NestedSequences { - kSequenceFiggle = 0, - kSequenceOver1, - kSequenceOver2, - kSequenceForest, - kSequenceDragon, - kSequenceDarm, - kSequenceLibrary2, - kSequenceLibrary3, - kSequenceMarco, - kSequenceHand1a, - kSequenceHand1b, - kSequenceHand1c, - kSequenceHand2, - kSequenceHand3, - kSequenceHand4 -}; - -enum SequencesDemo { - kSequenceDemoVirgin = 0, - kSequenceDemoWestwood, - kSequenceDemoTitle, - kSequenceDemoHill, - kSequenceDemoOuthome, - kSequenceDemoWharf, - kSequenceDemoDinob, - kSequenceDemoFisher -}; - -enum NestedSequencesDemo { - kSequenceDemoWharf2 = 0, - kSequenceDemoDinob2, - kSequenceDemoWater, - kSequenceDemoBail, - kSequenceDemoDig -}; - -#ifdef ENABLE_LOL -enum SequencesLoLDemo { - kSequenceLoLDemoScene1 = 0, - kSequenceLoLDemoText1, - kSequenceLoLDemoScene2, - kSequenceLoLDemoText2, - kSequenceLoLDemoScene3, - kSequenceLoLDemoText3, - kSequenceLoLDemoScene4, - kSequenceLoLDemoText4, - kSequenceLoLDemoScene5, - kSequenceLoLDemoText5, - kSequenceLoLDemoScene6 -}; -#endif // ENABLE_LOL - -class WSAMovie_v2; -class KyraEngine_HoF; +//class WSAMovie_v2; +//class KyraEngine_HoF; class TextDisplayer_HoF; +class SeqPlayer_HOF; struct TIM; -typedef int (KyraEngine_HoF::*SeqProc)(WSAMovie_v2 *, int, int, int); - -struct ActiveWSA { - SeqProc callback; - WSAMovie_v2 *movie; - const FrameControl *control; - int16 flags; - uint16 startFrame; - uint16 endFrame; - uint16 frameDelay; - uint32 nextFrame; - uint16 currentFrame; - uint16 lastFrame; - uint16 x; - uint16 y; - uint16 startupCommand; - uint16 finalCommand; -}; - -struct ActiveText { - uint16 strIndex; - uint16 x; - uint16 y; - uint16 width; - int32 duration; - uint32 startTime; - int16 textcolor; -}; - -struct Sequence { - const char *wsaFile; - const char *cpsFile; - uint16 flags; - uint8 startupCommand; - uint8 finalCommand; - int16 stringIndex1; - int16 stringIndex2; - uint16 startFrame; - uint16 numFrames; - uint16 frameDelay; - uint16 xPos; - uint16 yPos; - uint16 duration; -}; - -struct NestedSequence { - const char *wsaFile; - const FrameControl *wsaControl; - uint16 flags; - uint16 startframe; - uint16 endFrame; - uint16 frameDelay; - uint16 x; - uint16 y; - uint16 startupCommand; - uint16 finalCommand; -}; - -struct HofSeqData { - const Sequence *seq; - int numSeq; - const NestedSequence *seqn; - int numSeqn; -}; - -struct ItemAnimData_v1 { - int16 itemIndex; - uint16 y; - const uint16 *frames; -}; - class KyraEngine_HoF : public KyraEngine_v2 { friend class Debugger_HoF; friend class TextDisplayer_HoF; @@ -202,98 +57,18 @@ public: GUI *gui() const { return _gui; } virtual TextDisplayer *text() { return _text; } int language() const { return _lang; } + protected: static const EngineDesc _hofEngineDesc; // intro/outro - void seq_playSequences(int startSeq, int endSeq = -1); - - int seq_introWestwood(WSAMovie_v2 *wsaObj, int x, int y, int frm); - int seq_introTitle(WSAMovie_v2 *wsaObj, int x, int y, int frm); - int seq_introOverview(WSAMovie_v2 *wsaObj, int x, int y, int frm); - int seq_introLibrary(WSAMovie_v2 *wsaObj, int x, int y, int frm); - int seq_introHand(WSAMovie_v2 *wsaObj, int x, int y, int frm); - int seq_introPoint(WSAMovie_v2 *wsaObj, int x, int y, int frm); - int seq_introZanfaun(WSAMovie_v2 *wsaObj, int x, int y, int frm); - - int seq_introOver1(WSAMovie_v2 *wsaObj, int x, int y, int frm); - int seq_introOver2(WSAMovie_v2 *wsaObj, int x, int y, int frm); - int seq_introForest(WSAMovie_v2 *wsaObj, int x, int y, int frm); - int seq_introDragon(WSAMovie_v2 *wsaObj, int x, int y, int frm); - int seq_introDarm(WSAMovie_v2 *wsaObj, int x, int y, int frm); - int seq_introLibrary2(WSAMovie_v2 *wsaObj, int x, int y, int frm); - int seq_introMarco(WSAMovie_v2 *wsaObj, int x, int y, int frm); - int seq_introHand1a(WSAMovie_v2 *wsaObj, int x, int y, int frm); - int seq_introHand1b(WSAMovie_v2 *wsaObj, int x, int y, int frm); - int seq_introHand1c(WSAMovie_v2 *wsaObj, int x, int y, int frm); - int seq_introHand2(WSAMovie_v2 *wsaObj, int x, int y, int frm); - int seq_introHand3(WSAMovie_v2 *wsaObj, int x, int y, int frm); - - int seq_finaleFunters(WSAMovie_v2 *wsaObj, int x, int y, int frm); - int seq_finaleFerb(WSAMovie_v2 *wsaObj, int x, int y, int frm); - int seq_finaleFish(WSAMovie_v2 *wsaObj, int x, int y, int frm); - int seq_finaleFheep(WSAMovie_v2 *wsaObj, int x, int y, int frm); - int seq_finaleFarmer(WSAMovie_v2 *wsaObj, int x, int y, int frm); - int seq_finaleFuards(WSAMovie_v2 *wsaObj, int x, int y, int frm); - int seq_finaleFirates(WSAMovie_v2 *wsaObj, int x, int y, int frm); - int seq_finaleFrash(WSAMovie_v2 *wsaObj, int x, int y, int frm); - - int seq_finaleFiggle(WSAMovie_v2 *wsaObj, int x, int y, int frm); - - int seq_demoVirgin(WSAMovie_v2 *wsaObj, int x, int y, int frm); - int seq_demoWestwood(WSAMovie_v2 *wsaObj, int x, int y, int frm); - int seq_demoTitle(WSAMovie_v2 *wsaObj, int x, int y, int frm); - int seq_demoHill(WSAMovie_v2 *wsaObj, int x, int y, int frm); - int seq_demoOuthome(WSAMovie_v2 *wsaObj, int x, int y, int frm); - int seq_demoWharf(WSAMovie_v2 *wsaObj, int x, int y, int frm); - int seq_demoDinob(WSAMovie_v2 *wsaObj, int x, int y, int frm); - int seq_demoFisher(WSAMovie_v2 *wsaObj, int x, int y, int frm); - - int seq_demoWharf2(WSAMovie_v2 *wsaObj, int x, int y, int frm); - int seq_demoDinob2(WSAMovie_v2 *wsaObj, int x, int y, int frm); - int seq_demoWater(WSAMovie_v2 *wsaObj, int x, int y, int frm); - int seq_demoBail(WSAMovie_v2 *wsaObj, int x, int y, int frm); - int seq_demoDig(WSAMovie_v2 *wsaObj, int x, int y, int frm); - -#ifdef ENABLE_LOL - int seq_lolDemoScene1(WSAMovie_v2 *wsaObj, int x, int y, int frm); - int seq_lolDemoScene2(WSAMovie_v2 *wsaObj, int x, int y, int frm); - int seq_lolDemoScene3(WSAMovie_v2 *wsaObj, int x, int y, int frm); - int seq_lolDemoScene4(WSAMovie_v2 *wsaObj, int x, int y, int frm); - int seq_lolDemoScene5(WSAMovie_v2 *wsaObj, int x, int y, int frm); - int seq_lolDemoText5(WSAMovie_v2 *wsaObj, int x, int y, int frm); - int seq_lolDemoScene6(WSAMovie_v2 *wsaObj, int x, int y, int frm); -#endif // ENABLE_LOL - - void seq_sequenceCommand(int command); - void seq_loadNestedSequence(int wsaNum, int seqNum); - void seq_nestedSequenceFrame(int command, int wsaNum); - void seq_animatedSubFrame(int srcPage, int dstPage, int delaytime, - int steps, int x, int y, int w, int h, int openClose, int directionFlags); - bool seq_processNextSubFrame(int wsaNum); - void seq_resetActiveWSA(int wsaNum); - void seq_unloadWSA(int wsaNum); - void seq_processWSAs(); - void seq_cmpFadeFrame(const char *cmpFile); - void seq_playTalkText(uint8 chatNum); - void seq_resetAllTextEntries(); - uint32 seq_activeTextsTimeLeft(); - void seq_waitForTextsTimeout(); - int seq_setTextEntry(uint16 strIndex, uint16 posX, uint16 posY, int duration, uint16 width); - void seq_processText(); - char *seq_preprocessString(const char *str, int width); - void seq_printCreditsString(uint16 strIndex, int x, int y, const uint8 *colorMap, uint8 textcolor); - void seq_playWsaSyncDialogue(uint16 strIndex, uint16 vocIndex, int textColor, int x, int y, int width, - WSAMovie_v2 * wsa, int firstframe, int lastframe, int wsaXpos, int wsaYpos); - void seq_finaleActorScreen(); - void seq_displayScrollText(uint8 *data, const ScreenDim *d, int tempPage1, int tempPage2, int speed, int step, Screen::FontId fid1, Screen::FontId fid2, const uint8 *shapeData = 0, const char *const *specialData = 0); - void seq_scrollPage(int bottom, int top); void seq_showStarcraftLogo(); - MainMenu *_menu; + int seq_playIntro(); + int seq_playOutro(); + int seq_playDemo(); - void seq_init(); - void seq_uninit(); + void seq_pausePlayer(bool toggle); Common::Error init(); Common::Error go(); @@ -311,7 +86,6 @@ protected: static const int8 _pcSpkSfxMap[]; static const int _pcSpkSfxMapSize; - AudioDataStruct _soundData[3]; protected: // game initialization void startup(); @@ -841,19 +615,19 @@ protected: bool _chatAltFlag; // sequence player - ActiveWSA *_activeWSA; +/* ActiveWSA *_activeWSA; ActiveText *_activeText; - - const char * const *_sequencePakList; - int _sequencePakListSize; - const char * const *_ingamePakList; + */ + /*const char *const *_sequencePakList; + int _sequencePakListSize;*/ + const char *const *_ingamePakList; int _ingamePakListSize; - const char * const *_musicFileListIntro; + const char *const *_musicFileListIntro; int _musicFileListIntroSize; - const char * const *_musicFileListFinale; + const char *const *_musicFileListFinale; int _musicFileListFinaleSize; - const char * const *_musicFileListIngame; + const char *const *_musicFileListIngame; int _musicFileListIngameSize; const uint8 *_cdaTrackTableIntro; int _cdaTrackTableIntroSize; @@ -861,34 +635,33 @@ protected: int _cdaTrackTableIngameSize; const uint8 *_cdaTrackTableFinale; int _cdaTrackTableFinaleSize; - const char * const *_sequenceSoundList; - int _sequenceSoundListSize; - const char * const *_ingameSoundList; + const char *const *_ingameSoundList; int _ingameSoundListSize; const uint16 *_ingameSoundIndex; int _ingameSoundIndexSize; - const char * const *_sequenceStrings; - int _sequenceStringsSize; const uint16 *_ingameTalkObjIndex; int _ingameTalkObjIndexSize; - const char * const *_ingameTimJpStr; + const char *const *_ingameTimJpStr; int _ingameTimJpStrSize; - const HofSeqData *_sequences; + const ItemAnimDefinition *_itemAnimDefinition; int _itemAnimDefinitionSize; + + /*const HofSeqData *_sequences; + const ItemAnimData_v1 *_demoAnimData; int _demoAnimSize; - int _sequenceStringsDuration[33]; + int _sequenceStringsDuration[33];*/ - static const uint8 _seqTextColorPresets[]; +/* static const uint8 _seqTextColorPresets[]; char *_seqProcessedString; WSAMovie_v2 *_seqWsa; bool _abortIntroFlag; - int _menuChoice; + int _menuChoice;*/ - uint32 _seqFrameDelay; + /*uint32 _seqFrameDelay; uint32 _seqStartTime; uint32 _seqSubFrameStartTime; uint32 _seqEndTime; @@ -902,10 +675,7 @@ protected: bool _seqSpecialFlag; bool _seqSubframePlaying; uint8 _seqTextColor[2]; - uint8 _seqTextColorMap[16]; - - const SeqProc *_callbackS; - const SeqProc *_callbackN; + uint8 _seqTextColorMap[16];*/ static const uint8 _rainbowRoomData[]; diff --git a/engines/kyra/kyra_lok.cpp b/engines/kyra/kyra_lok.cpp index 27bc2ad22a..7d4e35092f 100644 --- a/engines/kyra/kyra_lok.cpp +++ b/engines/kyra/kyra_lok.cpp @@ -98,8 +98,6 @@ KyraEngine_LoK::KyraEngine_LoK(OSystem *system, const GameFlags &flags) _malcolmFrame = 0; _malcolmTimer1 = _malcolmTimer2 = 0; - - _soundFiles = 0; } KyraEngine_LoK::~KyraEngine_LoK() { @@ -123,8 +121,6 @@ KyraEngine_LoK::~KyraEngine_LoK() { delete _animator; delete _seq; - delete[] _soundFiles; - delete[] _characterList; delete[] _roomTable; @@ -194,7 +190,7 @@ Common::Error KyraEngine_LoK::init() { initStaticResource(); - _sound->setSoundList(&_soundData[kMusicIntro]); + _sound->selectAudioResourceSet(kMusicIntro); if (_flags.platform == Common::kPlatformAmiga) { _trackMap = _amigaTrackMap; @@ -349,7 +345,7 @@ void KyraEngine_LoK::startup() { static const uint8 colorMap[] = { 0, 0, 0, 0, 12, 12, 12, 0, 0, 0, 0, 0 }; _screen->setTextColorMap(colorMap); - _sound->setSoundList(&_soundData[kMusicIngame]); + _sound->selectAudioResourceSet(kMusicIngame); if (_flags.platform == Common::kPlatformPC98) _sound->loadSoundFile("SE.DAT"); else diff --git a/engines/kyra/kyra_lok.h b/engines/kyra/kyra_lok.h index e5fb3cddca..def5cbcf6f 100644 --- a/engines/kyra/kyra_lok.h +++ b/engines/kyra/kyra_lok.h @@ -126,13 +126,13 @@ public: typedef bool (KyraEngine_LoK::*IntroProc)(); // static data access - const char * const *seqWSATable() { return _seq_WSATable; } - const char * const *seqCPSTable() { return _seq_CPSTable; } - const char * const *seqCOLTable() { return _seq_COLTable; } - const char * const *seqTextsTable() { return _seq_textsTable; } + const char *const *seqWSATable() { return _seq_WSATable; } + const char *const *seqCPSTable() { return _seq_CPSTable; } + const char *const *seqCOLTable() { return _seq_COLTable; } + const char *const *seqTextsTable() { return _seq_textsTable; } - const uint8 * const *palTable1() { return &_specialPalettes[0]; } - const uint8 * const *palTable2() { return &_specialPalettes[29]; } + const uint8 *const *palTable1() { return &_specialPalettes[0]; } + const uint8 *const *palTable2() { return &_specialPalettes[29]; } protected: virtual Common::Error go(); @@ -538,12 +538,12 @@ protected: const uint8 *_seq_Demo4; const uint8 *_seq_Reunion; - const char * const *_seq_WSATable; - const char * const *_seq_CPSTable; - const char * const *_seq_COLTable; - const char * const *_seq_textsTable; + const char *const *_seq_WSATable; + const char *const *_seq_CPSTable; + const char *const *_seq_COLTable; + const char *const *_seq_textsTable; - const char * const *_storyStrings; + const char *const *_storyStrings; int _seq_WSATable_Size; int _seq_CPSTable_Size; @@ -552,25 +552,25 @@ protected: int _storyStringsSize; - const char * const *_itemList; - const char * const *_takenList; - const char * const *_placedList; - const char * const *_droppedList; - const char * const *_noDropList; - const char * const *_putDownFirst; - const char * const *_waitForAmulet; - const char * const *_blackJewel; - const char * const *_poisonGone; - const char * const *_healingTip; - const char * const *_thePoison; - const char * const *_fluteString; - const char * const *_wispJewelStrings; - const char * const *_magicJewelString; - const char * const *_flaskFull; - const char * const *_fullFlask; - const char * const *_veryClever; - const char * const *_homeString; - const char * const *_newGameString; + const char *const *_itemList; + const char *const *_takenList; + const char *const *_placedList; + const char *const *_droppedList; + const char *const *_noDropList; + const char *const *_putDownFirst; + const char *const *_waitForAmulet; + const char *const *_blackJewel; + const char *const *_poisonGone; + const char *const *_healingTip; + const char *const *_thePoison; + const char *const *_fluteString; + const char *const *_wispJewelStrings; + const char *const *_magicJewelString; + const char *const *_flaskFull; + const char *const *_fullFlask; + const char *const *_veryClever; + const char *const *_homeString; + const char *const *_newGameString; int _itemList_Size; int _takenList_Size; @@ -592,13 +592,13 @@ protected: int _homeString_Size; int _newGameString_Size; - const char * const *_characterImageTable; + const char *const *_characterImageTable; int _characterImageTableSize; - const char * const *_guiStrings; + const char *const *_guiStrings; int _guiStringsSize; - const char * const *_configStrings; + const char *const *_configStrings; int _configStringsSize; Shape *_defaultShapeTable; @@ -636,20 +636,12 @@ protected: Room *_roomTable; int _roomTableSize; - const char * const *_roomFilenameTable; + const char *const *_roomFilenameTable; int _roomFilenameTableSize; const uint8 *_amuleteAnim; - const uint8 * const *_specialPalettes; - - const char * const *_soundFiles; - int _soundFilesSize; - const char * const *_soundFilesIntro; - int _soundFilesIntroSize; - const int32 *_cdaTrackTable; - int _cdaTrackTableSize; - AudioDataStruct _soundData[3]; + const uint8 *const *_specialPalettes; // positions of the inventory static const uint16 _itemPosX[]; diff --git a/engines/kyra/kyra_mr.cpp b/engines/kyra/kyra_mr.cpp index 448e4ef70d..48ba96ec8b 100644 --- a/engines/kyra/kyra_mr.cpp +++ b/engines/kyra/kyra_mr.cpp @@ -28,7 +28,7 @@ #include "kyra/debugger.h" #include "kyra/gui_mr.h" #include "kyra/resource.h" -#include "kyra/sound.h" +#include "kyra/sound_digital.h" #include "common/system.h" #include "common/config-manager.h" @@ -213,8 +213,6 @@ Common::Error KyraEngine_MR::init() { _soundDigital = new SoundDigital(this, _mixer); assert(_soundDigital); - if (!_soundDigital->init()) - error("_soundDigital->init() failed"); KyraEngine_v1::_text = _text = new TextDisplayer_MR(this, _screen); assert(_text); _gui = new GUI_MR(this); @@ -724,7 +722,7 @@ void KyraEngine_MR::loadCharacterShapes(int newShapes) { static const uint8 numberOffset[] = { 3, 3, 4, 4, 3, 3 }; static const uint8 startShape[] = { 0x32, 0x58, 0x78, 0x98, 0xB8, 0xD8 }; static const uint8 endShape[] = { 0x57, 0x77, 0x97, 0xB7, 0xD7, 0xF7 }; - static const char * const filenames[] = { + static const char *const filenames[] = { "MSW##.SHP", "MTA##.SHP", "MTFL##.SHP", diff --git a/engines/kyra/kyra_mr.h b/engines/kyra/kyra_mr.h index 004236ca04..d194fedd4d 100644 --- a/engines/kyra/kyra_mr.h +++ b/engines/kyra/kyra_mr.h @@ -182,11 +182,11 @@ private: private: // main menu - const char * const *_mainMenuStrings; + const char *const *_mainMenuStrings; int _mainMenuStringsSize; - static const char * const _mainMenuSpanishFan[]; - static const char * const _mainMenuItalianFan[]; + static const char *const _mainMenuSpanishFan[]; + static const char *const _mainMenuItalianFan[]; // animator uint8 *_gamePlayBuffer; diff --git a/engines/kyra/kyra_rpg.cpp b/engines/kyra/kyra_rpg.cpp index f1d9550e8f..f8eb7d00cd 100644 --- a/engines/kyra/kyra_rpg.cpp +++ b/engines/kyra/kyra_rpg.cpp @@ -46,12 +46,7 @@ KyraRpgEngine::KyraRpgEngine(OSystem *system, const GameFlags &flags) : KyraEngi _vcnTransitionMask = 0; _vcnShift = 0; _vcnColTable = 0; - _vcnBlockWidth = 4; - _vcnBlockHeight = 8; - _vcnFlip0 = 0; - _vcnFlip1 = 1; _vmpPtr = 0; - _vmpSize = 0; _blockBrightness = _wllVcnOffset = 0; _blockDrawingBuffer = 0; _sceneWindowBuffer = 0; @@ -173,9 +168,8 @@ Common::Error KyraRpgEngine::init() { _blockDrawingBuffer = new uint16[1320]; memset(_blockDrawingBuffer, 0, 1320 * sizeof(uint16)); - uint32 swbSize = 22 * _vcnBlockWidth * 2 * 15 * _vcnBlockHeight; - _sceneWindowBuffer = new uint8[swbSize]; - memset(_sceneWindowBuffer, 0, swbSize); + _sceneWindowBuffer = new uint8[21120]; + memset(_sceneWindowBuffer, 0, 21120); _lvlShapeTop = new int16[18]; memset(_lvlShapeTop, 0, 18 * sizeof(int16)); @@ -186,7 +180,7 @@ Common::Error KyraRpgEngine::init() { _vcnColTable = new uint8[128]; for (int i = 0; i < 128; i++) - _vcnColTable[i] = i & 0x0f; + _vcnColTable[i] = i & 0x0F; _doorShapes = new uint8*[6]; memset(_doorShapes, 0, 6 * sizeof(uint8 *)); @@ -210,18 +204,19 @@ bool KyraRpgEngine::posWithinRect(int posX, int posY, int x1, int y1, int x2, in void KyraRpgEngine::drawDialogueButtons() { int cp = screen()->setCurPage(0); - Screen::FontId of = screen()->setFont(gameFlags().use16ColorMode ? Screen::FID_SJIS_FNT : Screen::FID_6_FNT); + Screen::FontId of = screen()->setFont(_flags.lang == Common::JA_JPN && _flags.use16ColorMode ? Screen::FID_SJIS_FNT : Screen::FID_6_FNT); for (int i = 0; i < _dialogueNumButtons; i++) { int x = _dialogueButtonPosX[i]; - if (gameFlags().use16ColorMode) { - gui_drawBox(x, ((_dialogueButtonYoffs + _dialogueButtonPosY[i]) & ~7) - 1, 74, 10, 0xee, 0xcc, -1); + if (_flags.lang == Common::JA_JPN && _flags.use16ColorMode) { + gui_drawBox(x, ((_dialogueButtonYoffs + _dialogueButtonPosY[i]) & ~7) - 1, 74, 10, 0xEE, 0xCC, -1); screen()->printText(_dialogueButtonString[i], (x + 37 - (screen()->getTextWidth(_dialogueButtonString[i])) / 2) & ~3, - ((_dialogueButtonYoffs + _dialogueButtonPosY[i]) + 2) & ~7, _dialogueHighlightedButton == i ? 0xc1 : 0xe1, 0); + ((_dialogueButtonYoffs + _dialogueButtonPosY[i]) + 2) & ~7, _dialogueHighlightedButton == i ? 0xC1 : 0xE1, 0); } else { + int sjisYOffset = (_flags.lang == Common::JA_JPN && _dialogueButtonString[i][0] < 0) ? 2 : 0; gui_drawBox(x, (_dialogueButtonYoffs + _dialogueButtonPosY[i]), _dialogueButtonWidth, guiSettings()->buttons.height, guiSettings()->colors.frame1, guiSettings()->colors.frame2, guiSettings()->colors.fill); screen()->printText(_dialogueButtonString[i], x + (_dialogueButtonWidth >> 1) - (screen()->getTextWidth(_dialogueButtonString[i])) / 2, - (_dialogueButtonYoffs + _dialogueButtonPosY[i]) + 2, _dialogueHighlightedButton == i ? _dialogueButtonLabelColor1 : _dialogueButtonLabelColor2, 0); + (_dialogueButtonYoffs + _dialogueButtonPosY[i]) + 2 - sjisYOffset, _dialogueHighlightedButton == i ? _dialogueButtonLabelColor1 : _dialogueButtonLabelColor2, 0); } } screen()->setFont(of); @@ -234,7 +229,7 @@ uint16 KyraRpgEngine::processDialogue() { for (int i = 0; i < _dialogueNumButtons; i++) { int x = _dialogueButtonPosX[i]; - int y = (gameFlags().use16ColorMode ? ((_dialogueButtonYoffs + _dialogueButtonPosY[i]) & ~7) - 1 : (_dialogueButtonYoffs + _dialogueButtonPosY[i])); + int y = ((_flags.lang == Common::JA_JPN && _flags.use16ColorMode) ? ((_dialogueButtonYoffs + _dialogueButtonPosY[i]) & ~7) - 1 : (_dialogueButtonYoffs + _dialogueButtonPosY[i])); Common::Point p = getMousePos(); if (posWithinRect(p.x, p.y, x, y, x + _dialogueButtonWidth, y + guiSettings()->buttons.height)) { _dialogueHighlightedButton = i; diff --git a/engines/kyra/kyra_rpg.h b/engines/kyra/kyra_rpg.h index 50a4c9bdc1..cd36d2a5cd 100644 --- a/engines/kyra/kyra_rpg.h +++ b/engines/kyra/kyra_rpg.h @@ -222,7 +222,6 @@ protected: uint16 _decorationCount; int16 _mappedDecorationsCount; uint16 *_vmpPtr; - uint16 _vmpSize; uint8 *_vcnBlocks; uint8 *_vcfBlocks; uint8 *_vcnTransitionMask; @@ -232,10 +231,6 @@ protected: uint8 *_sceneWindowBuffer; uint8 _blockBrightness; uint8 _wllVcnOffset; - uint8 _vcnBlockWidth; - uint8 _vcnBlockHeight; - uint8 _vcnFlip0; - uint8 _vcnFlip1; uint8 **_doorShapes; @@ -288,7 +283,7 @@ protected: void removeInputTop(); void gui_drawBox(int x, int y, int w, int h, int frameColor1, int frameColor2, int fillColor); virtual void gui_drawHorizontalBarGraph(int x, int y, int w, int h, int32 curVal, int32 maxVal, int col1, int col2); - void gui_initButtonsFromList(const int16 *list); + void gui_initButtonsFromList(const uint8 *list); virtual void gui_initButton(int index, int x = -1, int y = -1, int val = -1) = 0; void gui_resetButtonList(); void gui_notifyButtonListChanged(); @@ -385,7 +380,7 @@ protected: bool lineIsPassable(int, int) { return false; } }; -} // End of namespace Kyra +} // End of namespace Kyra #endif // ENABLE_EOB || ENABLE_LOL diff --git a/engines/kyra/kyra_v1.cpp b/engines/kyra/kyra_v1.cpp index 2672618c67..9194b64155 100644 --- a/engines/kyra/kyra_v1.cpp +++ b/engines/kyra/kyra_v1.cpp @@ -172,19 +172,6 @@ Common::Error KyraEngine_v1::init() { assert(_res); _res->reset(); - if (_flags.isDemo) { - // HACK: check whether this is the HOF demo or the LOL demo. - // The LOL demo needs to be detected and run as KyraEngine_HoF, - // but the static resource loader and the sequence player will - // need correct IDs. - if (_res->exists("scene1.cps")) -#ifdef ENABLE_LOL - _flags.gameID = GI_LOL; -#else - error("Lands of Lore demo is not supported in this build"); -#endif // !ENABLE_LOL - } - _staticres = new StaticResource(this); assert(_staticres); if (!_staticres->init()) @@ -366,84 +353,88 @@ void KyraEngine_v1::setupKeyMap() { Common::KeyCode kcScummVM; int16 kcDOS; int16 kcPC98; + int16 kcFMTowns; }; +#define UNKNOWN_KEYCODE -1 #define KC(x) Common::KEYCODE_##x static const KeyCodeMapEntry keys[] = { - { KC(SPACE), 61, 53 }, - { KC(RETURN), 43, 29 }, - { KC(UP), 96, 68 }, - { KC(KP8), 96, 68 }, - { KC(RIGHT), 102, 73 }, - { KC(KP6), 102, 73 }, - { KC(DOWN), 98, 76 }, - { KC(KP2), 98, 76 }, - { KC(KP5), 97, 72 }, - { KC(LEFT), 92, 71 }, - { KC(KP4), 92, 71 }, - { KC(HOME), 91, 67 }, - { KC(KP7), 91, 67 }, - { KC(PAGEUP), 101, 69 }, - { KC(KP9), 101, 69 }, - { KC(END), 93, 0/*unknown*/ }, - { KC(KP1), 93, 0/*unknown*/ }, - { KC(PAGEDOWN), 103, 0/*unknown*/ }, - { KC(KP3), 103, 0/*unknown*/ }, - { KC(F1), 112, 99 }, - { KC(F2), 113, 100 }, - { KC(F3), 114, 101 }, - { KC(F4), 115, 102 }, - { KC(F5), 116, 103 }, - { KC(F6), 117, 104 }, - { KC(a), 31, 31 }, - { KC(b), 50, 50 }, - { KC(c), 48, 48 }, - { KC(d), 33, 33 }, - { KC(e), 19, 19 }, - { KC(f), 34, 34 }, - { KC(i), 24, 24 }, - { KC(k), 38, 38 }, - { KC(m), 52, 52 }, - { KC(n), 51, 51 }, - { KC(o), 25, 25 }, - { KC(p), 26, 26 }, - { KC(r), 20, 20 }, - { KC(s), 32, 32 }, - { KC(w), 18, 18 }, - { KC(y), 22, 22 }, - { KC(z), 46, 46 }, - { KC(1), 2, 0/*unknown*/ }, - { KC(2), 3, 0/*unknown*/ }, - { KC(3), 4, 0/*unknown*/ }, - { KC(4), 5, 0/*unknown*/ }, - { KC(5), 6, 0/*unknown*/ }, - { KC(6), 7, 0/*unknown*/ }, - { KC(7), 8, 0/*unknown*/ }, - { KC(SLASH), 55, 55 }, - { KC(ESCAPE), 110, 1 }, - { KC(MINUS), 12, 0/*unknown*/ }, - { KC(KP_MINUS), 105, 0/*unknown*/ }, - { KC(PLUS), 13, 0/*unknown*/ }, - { KC(KP_PLUS), 106, 0/*unknown*/ }, + { KC(SPACE), 61, 53, 32 }, + { KC(RETURN), 43, 29, 13 }, + { KC(UP), 96, 68, 30 }, + { KC(KP8), 96, 68, 30 }, + { KC(RIGHT), 102, 73, 28 }, + { KC(KP6), 102, 73, 28 }, + { KC(DOWN), 98, 76, 31 }, + { KC(KP2), 98, 76, 31 }, + { KC(KP5), 97, 72, UNKNOWN_KEYCODE }, + { KC(LEFT), 92, 71, 29 }, + { KC(KP4), 92, 71, 29 }, + { KC(HOME), 91, 67, 127 }, + { KC(KP7), 91, 67, 127 }, + { KC(PAGEUP), 101, 69, 18 }, + { KC(KP9), 101, 69, 18 }, + { KC(END), 93, UNKNOWN_KEYCODE, UNKNOWN_KEYCODE }, + { KC(KP1), 93, UNKNOWN_KEYCODE, UNKNOWN_KEYCODE }, + { KC(PAGEDOWN), 103, UNKNOWN_KEYCODE, UNKNOWN_KEYCODE }, + { KC(KP3), 103, UNKNOWN_KEYCODE, UNKNOWN_KEYCODE }, + { KC(F1), 112, 99, UNKNOWN_KEYCODE }, + { KC(F2), 113, 100, UNKNOWN_KEYCODE }, + { KC(F3), 114, 101, UNKNOWN_KEYCODE }, + { KC(F4), 115, 102, UNKNOWN_KEYCODE }, + { KC(F5), 116, 103, UNKNOWN_KEYCODE }, + { KC(F6), 117, 104, UNKNOWN_KEYCODE }, + { KC(a), 31, 31, UNKNOWN_KEYCODE }, + { KC(b), 50, 50, UNKNOWN_KEYCODE }, + { KC(c), 48, 48, 67 }, + { KC(d), 33, 33, UNKNOWN_KEYCODE }, + { KC(e), 19, 19, UNKNOWN_KEYCODE }, + { KC(f), 34, 34, UNKNOWN_KEYCODE }, + { KC(i), 24, 24, UNKNOWN_KEYCODE }, + { KC(k), 38, 38, UNKNOWN_KEYCODE }, + { KC(m), 52, 52, UNKNOWN_KEYCODE }, + { KC(n), 51, 51, UNKNOWN_KEYCODE }, + { KC(o), 25, 25, 79 }, + { KC(p), 26, 26, 80 }, + { KC(r), 20, 20, 82 }, + { KC(s), 32, 32, UNKNOWN_KEYCODE }, + { KC(w), 18, 18, UNKNOWN_KEYCODE }, + { KC(y), 22, 22, UNKNOWN_KEYCODE }, + { KC(z), 46, 46, UNKNOWN_KEYCODE }, + { KC(0), UNKNOWN_KEYCODE, UNKNOWN_KEYCODE, 48 }, + { KC(1), 2, UNKNOWN_KEYCODE, 49 }, + { KC(2), 3, UNKNOWN_KEYCODE, 50 }, + { KC(3), 4, UNKNOWN_KEYCODE, 51 }, + { KC(4), 5, UNKNOWN_KEYCODE, 52 }, + { KC(5), 6, UNKNOWN_KEYCODE, 53 }, + { KC(6), 7, UNKNOWN_KEYCODE, 54 }, + { KC(7), 8, UNKNOWN_KEYCODE, 55 }, + { KC(SLASH), 55, 55, 47 }, + { KC(ESCAPE), 110, 1, 27 }, + { KC(MINUS), 12, UNKNOWN_KEYCODE, UNKNOWN_KEYCODE }, + { KC(KP_MINUS), 105, UNKNOWN_KEYCODE, UNKNOWN_KEYCODE }, + { KC(PLUS), 13, UNKNOWN_KEYCODE, UNKNOWN_KEYCODE }, + { KC(KP_PLUS), 106, UNKNOWN_KEYCODE, UNKNOWN_KEYCODE }, // Multiple mappings for the keys to the right of the 'M' key, // since these are different for QWERTZ, QWERTY and AZERTY keyboards. // QWERTZ - { KC(COMMA), 53, 0/*unknown*/ }, - { KC(PERIOD), 54, 0/*unknown*/ }, + { KC(COMMA), 53, UNKNOWN_KEYCODE, UNKNOWN_KEYCODE }, + { KC(PERIOD), 54, UNKNOWN_KEYCODE, UNKNOWN_KEYCODE }, // AZERTY - { KC(SEMICOLON), 53, 0/*unknown*/ }, - { KC(COLON), 54, 0/*unknown*/ }, + { KC(SEMICOLON), 53, UNKNOWN_KEYCODE, UNKNOWN_KEYCODE }, + { KC(COLON), 54, UNKNOWN_KEYCODE, UNKNOWN_KEYCODE }, // QWERTY - { KC(LESS), 53, 0/*unknown*/ }, - { KC(GREATER), 54, 0/*unknown*/ } + { KC(LESS), 53, UNKNOWN_KEYCODE, UNKNOWN_KEYCODE }, + { KC(GREATER), 54, UNKNOWN_KEYCODE, UNKNOWN_KEYCODE } }; #undef KC +#undef UNKNOWN_KEYCODE _keyMap.clear(); for (int i = 0; i < ARRAYSIZE(keys); i++) - _keyMap[keys[i].kcScummVM] = (_flags.platform == Common::kPlatformPC98) ? keys[i].kcPC98 : keys[i].kcDOS; + _keyMap[keys[i].kcScummVM] = (_flags.platform == Common::kPlatformPC98) ? keys[i].kcPC98 : ((_flags.platform == Common::kPlatformFMTowns) ? keys[i].kcFMTowns : keys[i].kcDOS); } void KyraEngine_v1::updateInput() { diff --git a/engines/kyra/kyra_v1.h b/engines/kyra/kyra_v1.h index 0033969047..cd048563ca 100644 --- a/engines/kyra/kyra_v1.h +++ b/engines/kyra/kyra_v1.h @@ -140,14 +140,6 @@ enum { GI_EOB2 = 6 }; -struct AudioDataStruct { - const char *const *fileList; - int fileListLen; - const void *cdaTracks; - int cdaNumTracks; - int extraOffset; -}; - // TODO: this is just the start of makeing the debug output of the kyra engine a bit more useable // in the future we maybe merge some flags and/or create new ones enum DebugLevels { @@ -164,7 +156,7 @@ enum DebugLevels { kDebugLevelTimer = 1 << 10 ///< debug level for "TimerManager" functions }; -enum MusicDataID { +enum AudioResourceSet { kMusicIntro = 0, kMusicIngame, kMusicFinale @@ -188,6 +180,7 @@ friend class GUI; friend class GUI_v1; friend class GUI_EoB; friend class SoundMidiPC; // For _eventMan +friend class SeqPlayer_HOF; // For skipFlag() friend class TransferPartyWiz; // For save state API public: KyraEngine_v1(OSystem *system, const GameFlags &flags); @@ -415,14 +408,14 @@ protected: Graphics::Surface *thumbnail; }; - enum kReadSaveHeaderError { + enum ReadSaveHeaderError { kRSHENoError = 0, kRSHEInvalidType = 1, kRSHEInvalidVersion = 2, kRSHEIoError = 3 }; - static kReadSaveHeaderError readSaveHeader(Common::SeekableReadStream *file, bool loadThumbnail, SaveHeader &header); + static ReadSaveHeaderError readSaveHeader(Common::SeekableReadStream *file, bool loadThumbnail, SaveHeader &header); void loadGameStateCheck(int slot); virtual Common::Error loadGameState(int slot) = 0; diff --git a/engines/kyra/lol.cpp b/engines/kyra/lol.cpp index d3028c5e2d..f7696d45b5 100644 --- a/engines/kyra/lol.cpp +++ b/engines/kyra/lol.cpp @@ -114,7 +114,7 @@ LoLEngine::LoLEngine(OSystem *system, const GameFlags &flags) : KyraRpgEngine(sy _selectedSpell = 0; _updateCharNum = _portraitSpeechAnimMode = _textColorFlag = 0; _palUpdateTimer = _updatePortraitNext = 0; - _lampStatusTimer = 0xffffffff; + _lampStatusTimer = 0xFFFFFFFF; _weaponsDisabled = false; _charInventoryUnk = 0; @@ -381,10 +381,10 @@ Common::Error LoLEngine::init() { _screen->setAnimBlockPtr(10000); _screen->setScreenDim(0); - _pageBuffer1 = new uint8[0xfa00]; - memset(_pageBuffer1, 0, 0xfa00); - _pageBuffer2 = new uint8[0xfa00]; - memset(_pageBuffer2, 0, 0xfa00); + _pageBuffer1 = new uint8[0xFA00]; + memset(_pageBuffer1, 0, 0xFA00); + _pageBuffer2 = new uint8[0xFA00]; + memset(_pageBuffer2, 0, 0xFA00); _itemsInPlay = new LoLItem[400]; memset(_itemsInPlay, 0, sizeof(LoLItem) * 400); @@ -499,6 +499,11 @@ void LoLEngine::initKeymap() { #endif } +void LoLEngine::pauseEngineIntern(bool pause) { + KyraEngine_v1::pauseEngineIntern(pause); + pauseDemoPlayer(pause); +} + Common::Error LoLEngine::go() { int action = -1; @@ -519,7 +524,7 @@ Common::Error LoLEngine::go() { // the prologue code we need to setup them manually here. if (_gameToLoad != -1 && action != 3) { preInit(); - _screen->setFont(_flags.use16ColorMode ? Screen::FID_SJIS_FNT : Screen::FID_9_FNT); + _screen->setFont((_flags.lang == Common::JA_JPN && _flags.use16ColorMode) ? Screen::FID_SJIS_FNT : Screen::FID_9_FNT); } // We have three sound.dat files, one for the intro, one for the @@ -529,7 +534,7 @@ Common::Error LoLEngine::go() { if (_flags.platform == Common::kPlatformPC98) _sound->loadSoundFile("sound.dat"); - _sound->setSoundList(&_soundData[kMusicIngame]); + _sound->selectAudioResourceSet(kMusicIngame); if (_flags.platform != Common::kPlatformPC) _sound->loadSoundFile(0); @@ -669,7 +674,7 @@ void LoLEngine::checkFloatingPointerRegions() { uint8 *LoLEngine::getItemIconShapePtr(int index) { int ix = _itemProperties[_itemsInPlay[index].itemPropertyIndex].shpIndex; if (_itemProperties[_itemsInPlay[index].itemPropertyIndex].flags & 0x200) - ix += (_itemsInPlay[index].shpCurFrame_flg & 0x1fff) - 1; + ix += (_itemsInPlay[index].shpCurFrame_flg & 0x1FFF) - 1; return _itemIconShapes[ix]; } @@ -678,14 +683,14 @@ int LoLEngine::mainMenu() { bool hasSave = saveFileLoadable(0); MainMenu::StaticData data[] = { - // 256 color mode + // 256 color ASCII mode { { 0, 0, 0, 0, 0 }, { 0x01, 0x04, 0x0C, 0x04, 0x00, 0x3D, 0x9F }, { 0x2C, 0x19, 0x48, 0x2C }, Screen::FID_9_FNT, 1 }, - // 16 color mode + // 16 color SJIS mode { { 0, 0, 0, 0, 0 }, { 0x01, 0x04, 0x0C, 0x04, 0x00, 0xC1, 0xE1 }, @@ -928,7 +933,7 @@ void LoLEngine::writeSettings() { case 0: default: - if (_flags.platform == Common::kPlatformPC98) + if (_flags.platform == Common::kPlatformPC98 || _flags.platform == Common::kPlatformFMTowns) _flags.lang = Common::JA_JPN; else _flags.lang = Common::EN_ANY; @@ -1024,7 +1029,7 @@ void LoLEngine::decodeSjis(const char *src, char *dst) { uint8 cmd = 0; while ((cmd = *src++) != 0) { if (cmd == 27) { - cmd = *src++ & 0x7f; + cmd = *src++ & 0x7F; memcpy(dst, src, cmd * 2); dst += cmd * 2; src += cmd * 2; @@ -1336,7 +1341,7 @@ int LoLEngine::calculateProtection(int index) { int c = 0; if (index & 0x8000) { // Monster - index &= 0x7fff; + index &= 0x7FFF; c = (_monsters[index].properties->itemProtection * _monsters[index].properties->fightingStats[2]) >> 8; } else { // Character @@ -1485,7 +1490,7 @@ void LoLEngine::increaseCharacterHitpoints(int charNum, int points, bool ignoreD points = 1; _characters[charNum].hitPointsCur = CLIP<int16>(_characters[charNum].hitPointsCur + points, 1, _characters[charNum].hitPointsMax); - _characters[charNum].flags &= 0xfff7; + _characters[charNum].flags &= 0xFFF7; } void LoLEngine::setupScreenDims() { @@ -1556,10 +1561,10 @@ void LoLEngine::gui_specialSceneSuspendControls(int controlMode) { void LoLEngine::gui_specialSceneRestoreControls(int restoreLamp) { if (restoreLamp) { - _updateFlags &= 0xfffa; + _updateFlags &= 0xFFFA; resetLampStatus(); } - _updateFlags &= 0xfffe; + _updateFlags &= 0xFFFE; _specialSceneFlag = 0; checkFloatingPointerRegions(); } @@ -1567,7 +1572,7 @@ void LoLEngine::gui_specialSceneRestoreControls(int restoreLamp) { void LoLEngine::restoreAfterSceneWindowDialogue(int redraw) { gui_enableControls(); _txt->setupField(false); - _updateFlags &= 0xffdf; + _updateFlags &= 0xFFDF; setDefaultButtonState(); @@ -1596,8 +1601,8 @@ void LoLEngine::initDialogueSequence(int controlMode, int pageNum) { if (_flags.use16ColorMode) { _screen->fillRect(0, 128, 319, 199, 0x44); - gui_drawBox(0, 129, 320, 71, 0xee, 0xcc, -1); - gui_drawBox(1, 130, 318, 69, 0xee, 0xcc, 0x11); + gui_drawBox(0, 129, 320, 71, 0xEE, 0xCC, -1); + gui_drawBox(1, 130, 318, 69, 0xEE, 0xCC, 0x11); } else { _screen->fillRect(0, 128, 319, 199, 1); gui_drawBox(0, 129, 320, 71, 136, 251, -1); @@ -1646,7 +1651,7 @@ void LoLEngine::restoreAfterDialogueSequence(int controlMode) { if (_currentControlMode) { _screen->modifyScreenDim(4, 11, 124, 28, 45); _screen->modifyScreenDim(5, 85, 123, 233, 54); - _updateFlags &= 0xfffd; + _updateFlags &= 0xFFFD; } else { const ScreenDim *d = _screen->getScreenDim(5); _screen->fillRect(d->sx, d->sy, d->sx + d->w - (_flags.use16ColorMode ? 3 : 2), d->sy + d->h - 2, d->unkA); @@ -1709,14 +1714,14 @@ void LoLEngine::generateBrightnessPalette(const Palette &src, Palette &dst, int brightness = (8 - brightness) << 5; if (modifier >= 0 && modifier < 8 && (_flagsTable[31] & 0x08)) { - brightness = 256 - ((((modifier & 0xfffe) << 5) * (256 - brightness)) >> 8); + brightness = 256 - ((((modifier & 0xFFFE) << 5) * (256 - brightness)) >> 8); if (brightness < 0) brightness = 0; } for (int i = 0; i < 384; i++) { uint16 c = (dst[i] * brightness) >> 8; - dst[i] = c & 0xff; + dst[i] = c & 0xFF; } } } @@ -1726,9 +1731,9 @@ void LoLEngine::generateFlashPalette(const Palette &src, Palette &dst, int color for (int i = 2; i < 128; i++) { for (int ii = 0; ii < 3; ii++) { - uint8 t = src[i * 3 + ii] & 0x3f; + uint8 t = src[i * 3 + ii] & 0x3F; if (colorFlags & (1 << ii)) - t += ((0x3f - t) >> 1); + t += ((0x3F - t) >> 1); else t -= (t >> 1); dst[i * 3 + ii] = t; @@ -1750,7 +1755,7 @@ void LoLEngine::createTransparencyTables() { 0x88, 0x00, 0x99, 0x00, 0xAA, 0x00, 0xBB, 0x00, 0xCC, 0x00, 0xDD, 0x00, 0xEE, 0x00, 0xFF, 0x00 }; - memset(tpal, 0xff, 768); + memset(tpal, 0xFF, 768); _res->loadFileToBuf("LOL.NOL", tpal, 48); for (int i = 15; i > -1; i--) { @@ -1758,7 +1763,7 @@ void LoLEngine::createTransparencyTables() { tpal[s] = tpal[i * 3]; tpal[s + 1] = tpal[i * 3 + 1]; tpal[s + 2] = tpal[i * 3 + 2]; - tpal[i * 3 + 2] = tpal[i * 3 + 1] = tpal[i * 3] = 0xff; + tpal[i * 3 + 2] = tpal[i * 3 + 1] = tpal[i * 3] = 0xFF; } _screen->createTransparencyTablesIntern(colTbl, 16, tpal, tpal, _transparencyTable1, _transparencyTable2, 80); @@ -1948,15 +1953,15 @@ void LoLEngine::giveItemToMonster(LoLMonster *monster, Item item) { } const uint16 *LoLEngine::getCharacterOrMonsterStats(int id) { - return (id & 0x8000) ? (const uint16 *)_monsters[id & 0x7fff].properties->fightingStats : _characters[id].defaultModifiers; + return (id & 0x8000) ? (const uint16 *)_monsters[id & 0x7FFF].properties->fightingStats : _characters[id].defaultModifiers; } uint16 *LoLEngine::getCharacterOrMonsterItemsMight(int id) { - return (id & 0x8000) ? _monsters[id & 0x7fff].properties->itemsMight : _characters[id].itemsMight; + return (id & 0x8000) ? _monsters[id & 0x7FFF].properties->itemsMight : _characters[id].itemsMight; } uint16 *LoLEngine::getCharacterOrMonsterProtectionAgainstItems(int id) { - return (id & 0x8000) ? _monsters[id & 0x7fff].properties->protectionAgainstItems : _characters[id].protectionAgainstItems; + return (id & 0x8000) ? _monsters[id & 0x7FFF].properties->protectionAgainstItems : _characters[id].protectionAgainstItems; } void LoLEngine::delay(uint32 millis, bool doUpdate, bool) { @@ -2093,7 +2098,7 @@ int LoLEngine::processMagicSpark(int charNum, int spellLevel) { uint16 target = getNearestMonsterFromCharacterForBlock(targetBlock, charNum); static const uint8 dmg[] = { 7, 15, 25, 60 }; - if (target != 0xffff) { + if (target != 0xFFFF) { inflictMagicalDamage(target, charNum, dmg[spellLevel], 5, 0); updateDrawPage2(); gui_drawScene(0); @@ -2111,8 +2116,8 @@ int LoLEngine::processMagicSpark(int charNum, int spellLevel) { const uint16 height = mov->height(); for (int i = 0; i < 6; i++) { - wX[i] = (_rnd.getRandomNumber(0x7fff) % 64) + ((176 - width) >> 1) + 80; - wY[i] = (_rnd.getRandomNumber(0x7fff) % 32) + ((120 - height) >> 1) - 16; + wX[i] = (_rnd.getRandomNumber(0x7FFF) % 64) + ((176 - width) >> 1) + 80; + wY[i] = (_rnd.getRandomNumber(0x7FFF) % 32) + ((120 - height) >> 1) - 16; wFrames[i] = i << 1; } @@ -2162,7 +2167,7 @@ int LoLEngine::processMagicHeal(int charNum, int spellLevel) { tpal.copy(_screen->getPalette(1)); if (_flags.use16ColorMode) { - tpal.fill(16, 240, 0xff); + tpal.fill(16, 240, 0xFF); uint8 *dst = tpal.getData(); for (int i = 1; i < 16; i++) { int s = ((i << 4) | i) * 3; @@ -2245,7 +2250,7 @@ int LoLEngine::processMagicHeal(int charNum, int spellLevel) { _screen->copyRegion(charNum * 77, 32, pX[charNum], pY, 77, 44, 2, 2, Screen::CR_NO_P_CHECK); - pts[charNum] &= 0xff; + pts[charNum] &= 0xFF; pts[charNum] += ((diff[charNum] << 8) / 16); increaseCharacterHitpoints(charNum, pts[charNum] / 256, true); gui_drawCharPortraitWithStats(charNum); @@ -2328,8 +2333,8 @@ int LoLEngine::processMagicIce(int charNum, int spellLevel) { tpal[i * 3 + 1] = v; tpal[i * 3 + 2] = v << 1; - if (tpal[i * 3 + 2] > 0x3f) - tpal[i * 3 + 2] = 0x3f; + if (tpal[i * 3 + 2] > 0x3F) + tpal[i * 3 + 2] = 0x3F; } } @@ -2361,7 +2366,7 @@ int LoLEngine::processMagicIce(int charNum, int spellLevel) { playSpellAnimation(0, 0, 0, 2, 0, 0, 0, s.getData(), tpal.getData(), 40, false); - _screen->fadePaletteStep(s.getData(), tpal.getData(), _system->getMillis(), _tickLength); + _screen->timedPaletteFadeStep(s.getData(), tpal.getData(), _system->getMillis(), _tickLength); if (mov->opened()) { int r = true; if (spellLevel > 2) { @@ -2390,7 +2395,7 @@ int LoLEngine::processMagicIce(int charNum, int spellLevel) { int might = rollDice(iceDamageMin[spellLevel], iceDamageMax[spellLevel]) + iceDamageAdd[spellLevel]; int dmg = calcInflictableDamagePerItem(charNum, 0, might, 3, 2); - LoLMonster *m = &_monsters[o & 0x7fff]; + LoLMonster *m = &_monsters[o & 0x7FFF]; if (m->hitPoints <= dmg) { increaseExperience(charNum, 2, m->hitPoints); o = m->nextAssignedObject; @@ -2430,7 +2435,7 @@ int LoLEngine::processMagicIce(int charNum, int spellLevel) { playSpellAnimation(0, 0, 0, 2, 0, 0, 0, tpal.getData(), swampCol.getData(), 40, 0); - _screen->fadePaletteStep(tpal.getData(), swampCol.getData(), _system->getMillis(), _tickLength); + _screen->timedPaletteFadeStep(tpal.getData(), swampCol.getData(), _system->getMillis(), _tickLength); if (breakWall) breakIceWall(tpal.getData(), swampCol.getData()); @@ -2469,7 +2474,7 @@ int LoLEngine::processMagicFireball(int charNum, int spellLevel) { while (o & 0x8000) { static const uint8 fireballDamage[] = { 20, 40, 80, 100 }; int dmg = calcInflictableDamagePerItem(charNum, o, fireballDamage[spellLevel], 4, 1); - LoLMonster *m = &_monsters[o & 0x7fff]; + LoLMonster *m = &_monsters[o & 0x7FFF]; o = m->nextAssignedObject; _envSfxUseQueue = true; inflictDamage(m->id | 0x8000, dmg, charNum, 2, 4); @@ -2519,8 +2524,8 @@ int LoLEngine::processMagicFireball(int charNum, int spellLevel) { static const int8 finShpIndex2[] = { -1, 1, 2, 3, 4, -1 }; uint8 *shp = fb->finalize ? _fireballShapes[finShpIndex1[fb->finProgress]] : _fireballShapes[0]; - int fX = (((fb->progress * _fireBallCoords[fb->tblIndex & 0xff]) >> 16) + fb->destX) - ((fb->progress / 8 + shp[3] + fireBallWH) >> 1); - int fY = (((fb->progress * _fireBallCoords[(fb->tblIndex + 64) & 0xff]) >> 16) + fb->destY) - ((fb->progress / 8 + shp[2] + fireBallWH) >> 1); + int fX = (((fb->progress * _fireBallCoords[fb->tblIndex & 0xFF]) >> 16) + fb->destX) - ((fb->progress / 8 + shp[3] + fireBallWH) >> 1); + int fY = (((fb->progress * _fireBallCoords[(fb->tblIndex + 64) & 0xFF]) >> 16) + fb->destY) - ((fb->progress / 8 + shp[2] + fireBallWH) >> 1); int sW = ((fb->progress / 8 + shp[3] + fireBallWH) << 8) / shp[3]; int sH = ((fb->progress / 8 + shp[2] + fireBallWH) << 8) / shp[2]; @@ -2532,8 +2537,8 @@ int LoLEngine::processMagicFireball(int charNum, int spellLevel) { if (finShpIndex2[fb->finProgress] != -1) { shp = _fireballShapes[finShpIndex2[fb->finProgress]]; - fX = (((fb->progress * _fireBallCoords[fb->tblIndex & 0xff]) >> 16) + fb->destX) - ((fb->progress / 8 + shp[3] + fireBallWH) >> 1); - fY = (((fb->progress * _fireBallCoords[(fb->tblIndex + 64) & 0xff]) >> 16) + fb->destY) - ((fb->progress / 8 + shp[2] + fireBallWH) >> 1); + fX = (((fb->progress * _fireBallCoords[fb->tblIndex & 0xFF]) >> 16) + fb->destX) - ((fb->progress / 8 + shp[3] + fireBallWH) >> 1); + fY = (((fb->progress * _fireBallCoords[(fb->tblIndex + 64) & 0xFF]) >> 16) + fb->destY) - ((fb->progress / 8 + shp[2] + fireBallWH) >> 1); sW = ((fb->progress / 8 + shp[3] + fireBallWH) << 8) / shp[3]; sH = ((fb->progress / 8 + shp[2] + fireBallWH) << 8) / shp[2]; _screen->drawShape(_screen->_curPage, shp, fX, fY, 0, 4, sW, sH); @@ -2634,7 +2639,7 @@ int LoLEngine::processMagicHandOfFate(int spellLevel) { uint16 o = _levelBlockProperties[b1].assignedObjects; while (o & 0x8000) { uint16 o2 = o; - LoLMonster *m = &_monsters[o & 0x7fff]; + LoLMonster *m = &_monsters[o & 0x7FFF]; o = findObject(o)->nextAssignedObject; int nX = 0; int nY = 0; @@ -2662,7 +2667,7 @@ int LoLEngine::processMagicHandOfFate(int spellLevel) { // This might be a bug in the original code, but using // the hand of fate spell won't give any experience points int dmg = calcInflictableDamagePerItem(-1, t, damage[spellLevel - 2], 0x80, 1); - inflictDamage(t, dmg, 0xffff, 3, 0x80); + inflictDamage(t, dmg, 0xFFFF, 3, 0x80); } } @@ -2778,7 +2783,7 @@ int LoLEngine::processMagicFog() { uint16 o = _levelBlockProperties[calcNewBlockPosition(_currentBlock, _currentDirection)].assignedObjects; while (o & 0x8000) { inflictMagicalDamage(o, -1, 15, 6, 0); - o = _monsters[o & 0x7fff].nextAssignedObject; + o = _monsters[o & 0x7FFF].nextAssignedObject; } gui_drawScene(0); @@ -2803,7 +2808,7 @@ int LoLEngine::processMagicSwarm(int charNum, int damage) { int t = 0; uint16 o = _levelBlockProperties[calcNewBlockPosition(_currentBlock, _currentDirection)].assignedObjects; while (o & 0x8000) { - o &= 0x7fff; + o &= 0x7FFF; if (_monsters[o].mode != 13) { destIds[t++] = o; @@ -2811,7 +2816,7 @@ int LoLEngine::processMagicSwarm(int charNum, int damage) { _envSfxUseQueue = true; inflictMagicalDamage(o | 0x8000, charNum, damage, 0, 0); _envSfxUseQueue = false; - _monsters[o].flags &= 0xffef; + _monsters[o].flags &= 0xFFEF; } } o = _monsters[o].nextAssignedObject; @@ -2886,7 +2891,7 @@ int LoLEngine::processMagicVaelansCube() { uint32 endTime = _system->getMillis() + 70 * _tickLength; while (_system->getMillis() < endTime) { - _screen->fadePaletteStep(tmpPal1, tmpPal2, _system->getMillis() - ctime, 70 * _tickLength); + _screen->timedPaletteFadeStep(tmpPal1, tmpPal2, _system->getMillis() - ctime, 70 * _tickLength); updateInput(); } @@ -2903,9 +2908,9 @@ int LoLEngine::processMagicVaelansCube() { uint16 o = _levelBlockProperties[bl].assignedObjects; while (o & 0x8000) { - LoLMonster *m = &_monsters[o & 0x7fff]; + LoLMonster *m = &_monsters[o & 0x7FFF]; if (m->properties->flags & 0x1000) { - inflictDamage(o, 100, 0xffff, 0, 0x80); + inflictDamage(o, 100, 0xFFFF, 0, 0x80); res = 1; } o = m->nextAssignedObject; @@ -2915,7 +2920,7 @@ int LoLEngine::processMagicVaelansCube() { endTime = _system->getMillis() + 70 * _tickLength; while (_system->getMillis() < endTime) { - _screen->fadePaletteStep(tmpPal2, tmpPal1, _system->getMillis() - ctime, 70 * _tickLength); + _screen->timedPaletteFadeStep(tmpPal2, tmpPal1, _system->getMillis() - ctime, 70 * _tickLength); updateInput(); } @@ -3031,7 +3036,7 @@ void LoLEngine::drinkBezelCup(int numUses, int charNum) { uint16 step = 0; do { - step = (step & 0xff) + (hpDiff * 256) / (bezelAnimData[numUses * 3 + 1]); + step = (step & 0xFF) + (hpDiff * 256) / (bezelAnimData[numUses * 3 + 1]); increaseCharacterHitpoints(charNum, step / 256, true); gui_drawCharPortraitWithStats(charNum); @@ -3068,7 +3073,7 @@ void LoLEngine::addSpellToScroll(int spell, int charNum) { } if (_availableSpells[i] == spell) { - _txt->printMessage(2, "%s", getLangString(0x42d0)); + _txt->printMessage(2, "%s", getLangString(0x42D0)); return; } } @@ -3096,7 +3101,7 @@ void LoLEngine::transferSpellToScollAnimation(int charNum, int spell, int slot) _screen->copyRegion(201, 1, 17, 15, 6, h, 2, 2, Screen::CR_NO_P_CHECK); _screen->copyRegion(208, 1, 89, 15, 6, h, 2, 2, Screen::CR_NO_P_CHECK); int cp = _screen->setCurPage(2); - _screen->fillRect(21, 15, 89, h + 15, _flags.use16ColorMode ? 0xbb : 206); + _screen->fillRect(21, 15, 89, h + 15, _flags.use16ColorMode ? 0xBB : 206); _screen->copyRegion(112, 16, 12, h + 15, 87, 14, 2, 2, Screen::CR_NO_P_CHECK); int y = 15; @@ -3244,7 +3249,7 @@ void LoLEngine::playSpellAnimation(WSAMovie_v2 *mov, int firstFrame, int lastFra continue; } - if (!_screen->fadePaletteStep(pal1, pal2, _system->getMillis() - startTime, _tickLength * fadeDelay) && !mov) + if (!_screen->timedPaletteFadeStep(pal1, pal2, _system->getMillis() - startTime, _tickLength * fadeDelay) && !mov) return; if (del) { @@ -3284,7 +3289,7 @@ int LoLEngine::checkMagic(int charNum, int spellNum, int spellLevel) { } int LoLEngine::getSpellTargetBlock(int currentBlock, int direction, int maxDistance, uint16 &targetBlock) { - targetBlock = 0xffff; + targetBlock = 0xFFFF; uint16 c = calcNewBlockPosition(currentBlock, direction); int i = 0; @@ -3316,9 +3321,9 @@ void LoLEngine::inflictMagicalDamageForBlock(int block, int attacker, int damage uint16 o = _levelBlockProperties[block].assignedObjects; while (o & 0x8000) { inflictDamage(o, calcInflictableDamagePerItem(attacker, o, damage, index, 2), attacker, 2, index); - if ((_monsters[o & 0x7fff].flags & 0x20) && (_currentLevel != 22)) + if ((_monsters[o & 0x7FFF].flags & 0x20) && (_currentLevel != 22)) break; - o = _monsters[o & 0x7fff].nextAssignedObject; + o = _monsters[o & 0x7FFF].nextAssignedObject; } } @@ -3331,7 +3336,7 @@ int LoLEngine::battleHitSkillTest(int16 attacker, int16 target, int skill) { return 1; if (target & 0x8000) { - if (_monsters[target & 0x7fff].mode >= 13) + if (_monsters[target & 0x7FFF].mode >= 13) return 0; } @@ -3340,8 +3345,8 @@ int LoLEngine::battleHitSkillTest(int16 attacker, int16 target, int skill) { int sk = 0; if (attacker & 0x8000) { - hitChanceModifier = _monsters[target & 0x7fff].properties->fightingStats[0]; - sk = 100 - _monsters[target & 0x7fff].properties->skillLevel; + hitChanceModifier = _monsters[target & 0x7FFF].properties->fightingStats[0]; + sk = 100 - _monsters[target & 0x7FFF].properties->skillLevel; } else { hitChanceModifier = _characters[attacker].defaultModifiers[0]; int8 m = _characters[attacker].skillModifiers[skill]; @@ -3351,8 +3356,10 @@ int LoLEngine::battleHitSkillTest(int16 attacker, int16 target, int skill) { } if (target & 0x8000) { - evadeChanceModifier = (_monsterModifiers[9 + _monsterDifficulty] * _monsters[target & 0x7fff].properties->fightingStats[3]) >> 8; - _monsters[target & 0x7fff].flags |= 0x10; + evadeChanceModifier = _monsters[target & 0x7FFF].properties->fightingStats[3]; + if (_monsterModifiers4) + evadeChanceModifier = (evadeChanceModifier * _monsterModifiers4[_monsterDifficulty]) >> 8; + _monsters[target & 0x7FFF].flags |= 0x10; } else { evadeChanceModifier = _characters[target].defaultModifiers[3]; } @@ -3387,7 +3394,7 @@ int LoLEngine::inflictDamage(uint16 target, int damage, uint16 attacker, int ski LoLCharacter *c = 0; if (target & 0x8000) { - m = &_monsters[target & 0x7fff]; + m = &_monsters[target & 0x7FFF]; if (m->mode >= 13) return 0; @@ -3477,7 +3484,7 @@ void LoLEngine::removeCharacterEffects(LoLCharacter *c, int first, int last) { for (int i = first; i <= last; i++) { switch (i - 1) { case 0: - c->flags &= 0xfffb; + c->flags &= 0xFFFB; c->weaponHit = 0; break; @@ -3486,19 +3493,19 @@ void LoLEngine::removeCharacterEffects(LoLCharacter *c, int first, int last) { break; case 2: - c->flags &= 0xffbf; + c->flags &= 0xFFBF; break; case 3: - c->flags &= 0xff7f; + c->flags &= 0xFF7F; break; case 4: - c->flags &= 0xfeff; + c->flags &= 0xFEFF; break; case 6: - c->flags &= 0xefff; + c->flags &= 0xEFFF; break; default: @@ -3552,7 +3559,7 @@ int LoLEngine::calcInflictableDamagePerItem(int16 attacker, int16 target, uint16 void LoLEngine::checkForPartyDeath() { Button b; - b.data0Val2 = b.data1Val2 = b.data2Val2 = 0xfe; + b.data0Val2 = b.data1Val2 = b.data2Val2 = 0xFE; b.data0Val3 = b.data1Val3 = b.data2Val3 = 0x01; for (int i = 0; i < 4; i++) { @@ -3591,7 +3598,7 @@ void LoLEngine::checkForPartyDeath() { _gui->runMenu(_gui->_deathMenu); setMouseCursorToItemInHand(); - _updateFlags &= 0xfffb; + _updateFlags &= 0xFFFB; resetLampStatus(); gui_enableDefaultPlayfieldButtons(); @@ -3608,7 +3615,7 @@ void LoLEngine::applyMonsterAttackSkill(LoLMonster *monster, int16 target, int16 switch (monster->properties->attackSkillType - 1) { case 0: - t = removeCharacterItem(target, 0x7ff); + t = removeCharacterItem(target, 0x7FF); if (t) { giveItemToMonster(monster, t); if (characterSays(0x4019, _characters[target].id, true)) @@ -3625,16 +3632,16 @@ void LoLEngine::applyMonsterAttackSkill(LoLMonster *monster, int16 target, int16 t = removeCharacterItem(target, 0x20); if (t) { deleteItem(t); - if (characterSays(0x401b, _characters[target].id, true)) - _txt->printMessage(6, "%s", getLangString(0x401b)); + if (characterSays(0x401B, _characters[target].id, true)) + _txt->printMessage(6, "%s", getLangString(0x401B)); } break; case 3: - t = removeCharacterItem(target, 0x0f); + t = removeCharacterItem(target, 0x0F); if (t) { - if (characterSays(0x401e, _characters[target].id, true)) - _txt->printMessage(6, getLangString(0x401e), _characters[target].name); + if (characterSays(0x401E, _characters[target].id, true)) + _txt->printMessage(6, getLangString(0x401E), _characters[target].name); setItemPosition(t, monster->x, monster->y, 0, 1); } break; @@ -3679,27 +3686,27 @@ void LoLEngine::applyMonsterDefenseSkill(LoLMonster *monster, int16 attacker, in switch (monster->properties->defenseSkillType - 1) { case 0: case 1: - if ((flags & 0x3f) == 2 || skill) + if ((flags & 0x3F) == 2 || skill) return; for (int i = 0; i < 3; i++) { itm = _characters[attacker].items[i]; if (!itm) continue; - if ((_itemProperties[_itemsInPlay[itm].itemPropertyIndex].protection & 0x3f) != flags) + if ((_itemProperties[_itemsInPlay[itm].itemPropertyIndex].protection & 0x3F) != flags) continue; - removeCharacterItem(attacker, 0x7fff); + removeCharacterItem(attacker, 0x7FFF); if (monster->properties->defenseSkillType == 1) { giveItemToMonster(monster, itm); - if (characterSays(0x401c, _characters[attacker].id, true)) - _txt->printMessage(6, "%s", getLangString(0x401c)); + if (characterSays(0x401C, _characters[attacker].id, true)) + _txt->printMessage(6, "%s", getLangString(0x401C)); } else { deleteItem(itm); - if (characterSays(0x401d, _characters[attacker].id, true)) - _txt->printMessage(6, "%s", getLangString(0x401d)); + if (characterSays(0x401D, _characters[attacker].id, true)) + _txt->printMessage(6, "%s", getLangString(0x401D)); } } break; @@ -3890,7 +3897,7 @@ void LoLEngine::launchMagicViper() { void LoLEngine::breakIceWall(uint8 *pal1, uint8 *pal2) { _screen->hideMouse(); uint16 bl = calcNewBlockPosition(_currentBlock, _currentDirection); - _levelBlockProperties[bl].flags &= 0xef; + _levelBlockProperties[bl].flags &= 0xEF; _screen->copyPage(0, 2); gui_drawScene(2); _screen->copyPage(2, 10); @@ -3918,10 +3925,10 @@ uint16 LoLEngine::getNearestMonsterFromCharacterForBlock(uint16 block, int charN uint16 cX = 0; uint16 cY = 0; - uint16 id = 0xffff; - int minDist = 0x7fff; + uint16 id = 0xFFFF; + int minDist = 0x7FFF; - if (block == 0xffff) + if (block == 0xFFFF) return id; calcCoordinatesForSingleCharacter(charNum, cX, cY); @@ -3929,7 +3936,7 @@ uint16 LoLEngine::getNearestMonsterFromCharacterForBlock(uint16 block, int charN int o = _levelBlockProperties[block].assignedObjects; while (o & 0x8000) { - LoLMonster *m = &_monsters[o & 0x7fff]; + LoLMonster *m = &_monsters[o & 0x7FFF]; if (m->mode >= 13) { o = m->nextAssignedObject; continue; @@ -3948,8 +3955,8 @@ uint16 LoLEngine::getNearestMonsterFromCharacterForBlock(uint16 block, int charN } uint16 LoLEngine::getNearestMonsterFromPos(int x, int y) { - uint16 id = 0xffff; - int minDist = 0x7fff; + uint16 id = 0xFFFF; + int minDist = 0x7FFF; for (int i = 0; i < 30; i++) { if (_monsters[i].mode > 13) @@ -3966,8 +3973,8 @@ uint16 LoLEngine::getNearestMonsterFromPos(int x, int y) { } uint16 LoLEngine::getNearestPartyMemberFromPos(int x, int y) { - uint16 id = 0xffff; - int minDist = 0x7fff; + uint16 id = 0xFFFF; + int minDist = 0x7FFF; for (int i = 0; i < 4; i++) { if (!(_characters[i].flags & 1) || _characters[i].hitPointsCur <= 0) @@ -4049,7 +4056,7 @@ void LoLEngine::displayAutomap() { delayTimer = _system->getMillis() + 8 * _tickLength; } - int f = checkInput(0) & 0xff; + int f = checkInput(0) & 0xFF; removeInputTop(); if (f) { @@ -4057,7 +4064,7 @@ void LoLEngine::displayAutomap() { gui_notifyButtonListChanged(); } - if (f == 0x30) { + if (f == _keyMap[Common::KEYCODE_c]) { for (int i = 0; i < 1024; i++) _levelBlockProperties[i].flags |= 7; _mapUpdateNeeded = true; @@ -4089,7 +4096,7 @@ void LoLEngine::updateAutoMap(uint16 block) { return; _levelBlockProperties[block].flags |= 7; - uint16 x = block & 0x1f; + uint16 x = block & 0x1F; uint16 y = block >> 5; updateAutoMapIntern(block, x, y, -1, -1); @@ -4107,7 +4114,7 @@ bool LoLEngine::updateAutoMapIntern(uint16 block, uint16 x, uint16 y, int16 xOff x += xOffs; y += yOffs; - if ((x & 0xffe0) || (y & 0xffe0)) + if ((x & 0xFFE0) || (y & 0xFFE0)) return false; xOffs++; @@ -4117,7 +4124,7 @@ bool LoLEngine::updateAutoMapIntern(uint16 block, uint16 x, uint16 y, int16 xOff uint16 b = block + blockPosTable[6 + xOffs]; if (fx != -1) { - if (_wllAutomapData[_levelBlockProperties[b].walls[fx]] & 0xc0) + if (_wllAutomapData[_levelBlockProperties[b].walls[fx]] & 0xC0) return false; } @@ -4125,13 +4132,13 @@ bool LoLEngine::updateAutoMapIntern(uint16 block, uint16 x, uint16 y, int16 xOff b = block + blockPosTable[9 + yOffs]; if (fy != -1) { - if (_wllAutomapData[_levelBlockProperties[b].walls[fy]] & 0xc0) + if (_wllAutomapData[_levelBlockProperties[b].walls[fy]] & 0xC0) return false; } b = block + blockPosTable[6 + xOffs] + blockPosTable[9 + yOffs]; - if ((fx != -1) && (fy != -1) && (_wllAutomapData[_levelBlockProperties[b].walls[fx]] & 0xc0) && (_wllAutomapData[_levelBlockProperties[b].walls[fy]] & 0xc0)) + if ((fx != -1) && (fy != -1) && (_wllAutomapData[_levelBlockProperties[b].walls[fx]] & 0xC0) && (_wllAutomapData[_levelBlockProperties[b].walls[fy]] & 0xC0)) return false; _levelBlockProperties[b].flags |= 7; @@ -4142,8 +4149,8 @@ bool LoLEngine::updateAutoMapIntern(uint16 block, uint16 x, uint16 y, int16 xOff void LoLEngine::loadMapLegendData(int level) { uint16 *legendData = (uint16 *)_tempBuffer5120; for (int i = 0; i < 32; i++) { - legendData[i * 6] = 0xffff; - legendData[i * 6 + 5] = 0xffff; + legendData[i * 6] = 0xFFFF; + legendData[i * 6 + 5] = 0xFFFF; } Common::String file = Common::String::format("level%d.xxx", level); @@ -4184,7 +4191,7 @@ void LoLEngine::drawMapPage(int pageNum) { _screen->copyRegion(236, 16, 236 + xOffset, 16, -xOffset, 1, pageNum, pageNum, Screen::CR_NO_P_CHECK); int cp = _screen->setCurPage(pageNum); - Screen::FontId of = _screen->setFont(_flags.use16ColorMode ? Screen::FID_SJIS_FNT : Screen::FID_9_FNT); + Screen::FontId of = _screen->setFont((_flags.lang == Common::JA_JPN && _flags.use16ColorMode) ? Screen::FID_SJIS_FNT : Screen::FID_9_FNT); _screen->printText(getLangString(_autoMapStrings[_currentMapLevel]), 236 + xOffset, 8, 1, 0); uint16 blX = mapGetStartPosX(); uint16 bl = (mapGetStartPosY() << 5) + blX; @@ -4194,7 +4201,7 @@ void LoLEngine::drawMapPage(int pageNum) { for (; bl < 1024; bl++) { uint8 *w = _levelBlockProperties[bl].walls; - if ((_levelBlockProperties[bl].flags & 7) == 7 && (!(_wllAutomapData[w[0]] & 0xc0)) && (!(_wllAutomapData[w[2]] & 0xc0)) && (!(_wllAutomapData[w[1]] & 0xc0)) && (!(_wllAutomapData[w[3]] & 0xc0))) { + if ((_levelBlockProperties[bl].flags & 7) == 7 && (!(_wllAutomapData[w[0]] & 0xC0)) && (!(_wllAutomapData[w[2]] & 0xC0)) && (!(_wllAutomapData[w[1]] & 0xC0)) && (!(_wllAutomapData[w[3]] & 0xC0))) { uint16 b0 = calcNewBlockPosition(bl, 0); uint16 b2 = calcNewBlockPosition(bl, 2); uint16 b1 = calcNewBlockPosition(bl, 1); @@ -4211,25 +4218,25 @@ void LoLEngine::drawMapPage(int pageNum) { // draw north wall drawMapBlockWall(b3, w31, sx, sy, 3); drawMapShape(w31, sx, sy, 3); - if (_wllAutomapData[w31] & 0xc0) + if (_wllAutomapData[w31] & 0xC0) _screen->copyBlockAndApplyOverlay(_screen->_curPage, sx, sy, _screen->_curPage, sx, sy, 1, 6, 0, _mapOverlay); // draw west wall drawMapBlockWall(b1, w13, sx, sy, 1); drawMapShape(w13, sx, sy, 1); - if (_wllAutomapData[w13] & 0xc0) + if (_wllAutomapData[w13] & 0xC0) _screen->copyBlockAndApplyOverlay(_screen->_curPage, sx + 6, sy, _screen->_curPage, sx + 6, sy, 1, 6, 0, _mapOverlay); // draw east wall drawMapBlockWall(b0, w02, sx, sy, 0); drawMapShape(w02, sx, sy, 0); - if (_wllAutomapData[w02] & 0xc0) + if (_wllAutomapData[w02] & 0xC0) _screen->copyBlockAndApplyOverlay(_screen->_curPage, sx, sy, _screen->_curPage, sx, sy, 7, 1, 0, _mapOverlay); //draw south wall drawMapBlockWall(b2, w20, sx, sy, 2); drawMapShape(w20, sx, sy, 2); - if (_wllAutomapData[w20] & 0xc0) + if (_wllAutomapData[w20] & 0xC0) _screen->copyBlockAndApplyOverlay(_screen->_curPage, sx, sy + 5, _screen->_curPage, sx, sy + 5, 7, 1, 0, _mapOverlay); } @@ -4244,7 +4251,7 @@ void LoLEngine::drawMapPage(int pageNum) { _screen->setFont(of); _screen->setCurPage(cp); - of = _screen->setFont(_flags.use16ColorMode ? Screen::FID_SJIS_FNT : Screen::FID_6_FNT); + of = _screen->setFont((_flags.lang == Common::JA_JPN && _flags.use16ColorMode) ? Screen::FID_SJIS_FNT : Screen::FID_6_FNT); int tY = 0; sx = mapGetStartPosX(); @@ -4255,19 +4262,19 @@ void LoLEngine::drawMapPage(int pageNum) { for (int ii = 0; ii < 32; ii++) { uint16 *l = &legendData[ii * 6]; - if (l[0] == 0xffff) + if (l[0] == 0xFFFF) break; uint16 cbl = l[0] + (l[1] << 5); if ((_levelBlockProperties[cbl].flags & 7) != 7) continue; - if (l[2] == 0xffff) + if (l[2] == 0xFFFF) continue; printMapText(l[2], 244 + xOffset, (tY << 3) + 22 + yOffset); - if (l[5] == 0xffff) { + if (l[5] == 0xFFFF) { tY++; continue; } @@ -4338,7 +4345,7 @@ bool LoLEngine::automapProcessButtons(int inputFlag) { void LoLEngine::automapForwardButton() { int i = _currentMapLevel + 1; while (!(_hasTempDataFlags & (1 << (i - 1)))) - i = (i + 1) & 0x1f; + i = (i + 1) & 0x1F; if (i == _currentMapLevel) return; @@ -4355,7 +4362,7 @@ void LoLEngine::automapForwardButton() { void LoLEngine::automapBackButton() { int i = _currentMapLevel - 1; while (!(_hasTempDataFlags & (1 << (i - 1)))) - i = (i - 1) & 0x1f; + i = (i - 1) & 0x1F; if (i == _currentMapLevel) return; @@ -4396,7 +4403,7 @@ void LoLEngine::redrawMapCursor() { } void LoLEngine::drawMapBlockWall(uint16 block, uint8 wall, int x, int y, int direction) { - if (((1 << direction) & _levelBlockProperties[block].flags) || ((_wllAutomapData[wall] & 0x1f) != 13)) + if (((1 << direction) & _levelBlockProperties[block].flags) || ((_wllAutomapData[wall] & 0x1F) != 13)) return; int cp = _screen->_curPage; @@ -4406,8 +4413,8 @@ void LoLEngine::drawMapBlockWall(uint16 block, uint8 wall, int x, int y, int dir } void LoLEngine::drawMapShape(uint8 wall, int x, int y, int direction) { - int l = _wllAutomapData[wall] & 0x1f; - if (l == 0x1f) + int l = _wllAutomapData[wall] & 0x1F; + if (l == 0x1F) return; _screen->drawShape(_screen->_curPage, _automapShapes[(l << 2) + direction], x + _mapCoords[10][direction] - 2, y + _mapCoords[11][direction] - 2, 0, 0); @@ -4473,7 +4480,7 @@ int LoLEngine::mapGetStartPosY() { } void LoLEngine::mapIncludeLegendData(int type) { - type &= 0x7f; + type &= 0x7F; for (int i = 0; i < 11; i++) { if (_defaultLegendData[i].shapeIndex != type) continue; @@ -4494,7 +4501,7 @@ void LoLEngine::printMapText(uint16 stringId, int x, int y) { void LoLEngine::printMapExitButtonText() { int cp = _screen->setCurPage(2); Screen::FontId of = _screen->setFont(Screen::FID_9_FNT); - _screen->fprintString("%s", 295, 182, _flags.use16ColorMode ? 0xbb : 172, 0, 5, getLangString(0x4033)); + _screen->fprintString("%s", 295, 182, _flags.use16ColorMode ? 0xBB : 172, 0, 5, getLangString(0x4033)); _screen->setFont(of); _screen->setCurPage(cp); } diff --git a/engines/kyra/lol.h b/engines/kyra/lol.h index dcd13804b3..4002346d31 100644 --- a/engines/kyra/lol.h +++ b/engines/kyra/lol.h @@ -270,6 +270,8 @@ public: virtual void initKeymap(); + void pauseEngineIntern(bool pause); + Screen *screen(); GUI *gui() const; @@ -334,9 +336,9 @@ private: static const char *const _charPreviewNamesDefault[]; static const char *const _charPreviewNamesRussianFloppy[]; - // PC98 specific data + // PC98/FM-TOWNS specific data static const uint16 _charPosXPC98[]; - static const uint8 _charNamesPC98[][11]; + static const char *const _charNamesJapanese[]; WSAMovie_v2 *_chargenWSA; static const uint8 _chargenFrameTableTalkie[]; @@ -389,7 +391,7 @@ private: uint8 _outroShapeTable[256]; // TODO: Consider moving these tables to kyra.dat - static const char * const _outroShapeFileTable[]; + static const char *const _outroShapeFileTable[]; static const uint8 _outroFrameTable[]; static const int16 _outroRightMonsterPos[]; @@ -400,6 +402,10 @@ private: static const int _outroMonsterScaleTableX[]; static const int _outroMonsterScaleTableY[]; + // Non-interactive demo + int playDemo(); + void pauseDemoPlayer(bool toggle); + // timers void setupTimers(); @@ -464,8 +470,6 @@ private: const uint8 *_ingamePCSpeakerSoundIndex; int _ingamePCSpeakerSoundIndexSize; - AudioDataStruct _soundData[3]; - // gui void gui_drawPlayField(); void gui_drawScene(int pageNum); @@ -554,14 +558,14 @@ private: int clickedStatusIcon(Button *button); const LoLButtonDef *_buttonData; - const int16 *_buttonList1; - const int16 *_buttonList2; - const int16 *_buttonList3; - const int16 *_buttonList4; - const int16 *_buttonList5; - const int16 *_buttonList6; - const int16 *_buttonList7; - const int16 *_buttonList8; + const uint8 *_buttonList1; + const uint8 *_buttonList2; + const uint8 *_buttonList3; + const uint8 *_buttonList4; + const uint8 *_buttonList5; + const uint8 *_buttonList6; + const uint8 *_buttonList7; + const uint8 *_buttonList8; // text int characterSays(int track, int charId, bool redraw); @@ -810,7 +814,7 @@ private: void decodeSjis(const char *src, char *dst); int decodeCyrillic(const char *src, char *dst); - static const char * const _languageExt[]; + static const char *const _languageExt[]; // graphics void setupScreenDims(); @@ -1009,8 +1013,8 @@ private: uint8 *_tempBuffer5120; - const char * const *_levelDatList; - const char * const *_levelShpList; + const char *const *_levelDatList; + const char *const *_levelShpList; const int8 *_dscWalls; @@ -1133,7 +1137,11 @@ private: uint16 _monsterCurBlock; int _objectLastDirection; - const uint16 *_monsterModifiers; + const uint16 *_monsterModifiers1; + const uint16 *_monsterModifiers2; + const uint16 *_monsterModifiers3; + const uint16 *_monsterModifiers4; + const int8 *_monsterShiftOffs; const uint8 *_monsterDirFlags; const uint8 *_monsterScaleX; diff --git a/engines/kyra/magic_eob.cpp b/engines/kyra/magic_eob.cpp index 985286854b..2180c5359d 100644 --- a/engines/kyra/magic_eob.cpp +++ b/engines/kyra/magic_eob.cpp @@ -60,7 +60,7 @@ void EoBCoreEngine::useMagicBookOrSymbol(int charIndex, int type) { } if (!_updateFlags) - _screen->copyRegion(64, 121, 0, 0, 112, 56, 0, _useHiResDithering ? 4 : 10, Screen::CR_NO_P_CHECK); + _screen->copyRegion(64, 121, 0, 0, 112, 56, 0, 10, Screen::CR_NO_P_CHECK); _updateFlags = 1; gui_setPlayFieldButtons(); gui_drawSpellbook(); @@ -179,7 +179,7 @@ void EoBCoreEngine::castSpell(int spell, int weaponSlot) { if ((s->flags & 0x100) && (c->effectFlags & 0x40)) // remove invisibility effect - removeCharacterEffect(10, _openBookChar, 1); + removeCharacterEffect(_flags.gameID == GI_EOB1 ? 8 : 10, _openBookChar, 1); int ci = _openBookChar; if (ci > 3) @@ -308,7 +308,7 @@ void EoBCoreEngine::startSpell(int spell) { EoBCharacter *c = &_characters[_activeSpellCharId]; snd_playSoundEffect(s->sound); - if (s->flags & 0xa0) + if (s->flags & 0xA0) sparkEffectDefensive(_activeSpellCharId); else if (s->flags & 0x40) sparkEffectDefensive(-1); @@ -623,7 +623,7 @@ bool EoBCoreEngine::turnUndeadHit(EoBMonsterInPlay *m, int hitChance, int caster assert(_monsterProps[m->type].tuResist > 0); uint8 e = _turnUndeadEffect[_monsterProps[m->type].tuResist * 14 + MIN(casterLevel, 14)]; - if (e == 0xff) { + if (e == 0xFF) { calcAndInflictMonsterDamage(m, 0, 0, 500, 0x200, 5, 3); } else if (hitChance < e) { return false; @@ -711,7 +711,7 @@ Item EoBCoreEngine::createMagicWeaponItem(int flags, int icon, int value, int ty void EoBCoreEngine::removeMagicWeaponItem(Item item) { _itemTypes[_items[item].type].armorClass = -30; _items[item].block = -2; - _items[item].level = 0xff; + _items[item].level = 0xFF; } void EoBCoreEngine::updateWallOfForceTimers() { @@ -841,7 +841,7 @@ bool EoBCoreEngine::spellCallback_end_magicMissile(void *obj) { } void EoBCoreEngine::spellCallback_start_shockingGrasp() { - int t = createMagicWeaponType(0, 0, 0, 0x0f, 1, 8, getMageLevel(_openBookChar), 1); + int t = createMagicWeaponType(0, 0, 0, 0x0F, 1, 8, getMageLevel(_openBookChar), 1); Item i = (t != -1) ? createMagicWeaponItem(0x10, 82, 0, t) : -1; if (t == -1 || i == -1) { if (_flags.gameID == GI_EOB2) @@ -948,7 +948,7 @@ bool EoBCoreEngine::spellCallback_end_lightningBolt(void *obj) { } void EoBCoreEngine::spellCallback_start_vampiricTouch() { - int t = createMagicWeaponType(0, 0, 0, 0x0f, getMageLevel(_openBookChar) >> 1, 6, 0, 1); + int t = createMagicWeaponType(0, 0, 0, 0x0F, getMageLevel(_openBookChar) >> 1, 6, 0, 1); Item i = (t != -1) ? createMagicWeaponItem(0x18, 83, 0, t) : -1; if (t == -1 || i == -1) { if (_flags.gameID == GI_EOB2) @@ -989,7 +989,7 @@ bool EoBCoreEngine::spellCallback_end_iceStorm(void *obj) { if (res) { for (int i = 0; i < 4; i++) { uint16 bl = fo->curBlock; - fo->curBlock = (fo->curBlock + blockAdv[i]) & 0x3ff; + fo->curBlock = (fo->curBlock + blockAdv[i]) & 0x3FF; magicObjectDamageHit(fo, 1, 6, 0, getMageLevel(fo->attackerId)); fo->curBlock = bl; } @@ -1027,7 +1027,7 @@ void EoBCoreEngine::spellCallback_start_coneOfCold() { _preventMonsterFlash = true; for (int i = 0; i < 7; i++) { - for (const int16 *m = findBlockMonsters((_currentBlock + tbl[i]) & 0x3ff, 4, _currentDirection, 1, 1); *m != -1; m++) + for (const int16 *m = findBlockMonsters((_currentBlock + tbl[i]) & 0x3FF, 4, _currentDirection, 1, 1); *m != -1; m++) calcAndInflictMonsterDamage(&_monsters[*m], cl, 4, cl, 0x41, 5, 0); } @@ -1054,7 +1054,7 @@ void EoBCoreEngine::spellCallback_start_wallOfForce() { return; } - uint32 dur = 0xffffffff; + uint32 dur = 0xFFFFFFFF; int s = 0; int i = 0; @@ -1158,7 +1158,7 @@ bool EoBCoreEngine::spellCallback_end_aid(void *obj) { } void EoBCoreEngine::spellCallback_start_flameBlade() { - int t = createMagicWeaponType(0, 0, 0, 0x0f, 1, 4, 4, 1); + int t = createMagicWeaponType(0, 0, 0, 0x0F, 1, 4, 4, 1); Item i = (t != -1) ? createMagicWeaponItem(0, 84, 0, t) : -1; if (t == -1 || i == -1) { if (_flags.gameID == GI_EOB2) diff --git a/engines/kyra/resource.h b/engines/kyra/resource.h index f2bc4e8146..5c179a7864 100644 --- a/engines/kyra/resource.h +++ b/engines/kyra/resource.h @@ -62,7 +62,7 @@ public: bool isInCacheList(Common::String name); bool loadFileList(const Common::String &filedata); - bool loadFileList(const char * const *filelist, uint32 numFiles); + bool loadFileList(const char *const *filelist, uint32 numFiles); // This unloads *all* pakfiles, even kyra.dat and protected ones. // It does not remove files from cache though! @@ -87,7 +87,7 @@ protected: Common::Archive *loadArchive(const Common::String &name, Common::ArchiveMemberPtr member); Common::Archive *loadInstallerArchive(const Common::String &file, const Common::String &ext, const uint8 offset); - bool loadProtectedFiles(const char * const * list); + bool loadProtectedFiles(const char *const * list); void initializeLoaders(); @@ -679,7 +679,10 @@ enum KyraResources { kLoLCharDefsKieran, kLoLCharDefsAkshel, kLoLExpRequirements, - kLoLMonsterModifiers, + kLoLMonsterModifiers1, + kLoLMonsterModifiers2, + kLoLMonsterModifiers3, + kLoLMonsterModifiers4, kLoLMonsterShiftOffsets, kLoLMonsterDirFlags, kLoLMonsterScaleY, @@ -742,6 +745,8 @@ enum KyraResources { struct Shape; struct Room; struct AmigaSfxTable; +struct HoFSeqData; +struct HoFSeqItemAnimData; class StaticResource { public: @@ -755,13 +760,13 @@ public: bool init(); void deinit(); - const char * const *loadStrings(int id, int &strings); + const char *const *loadStrings(int id, int &strings); const uint8 *loadRawData(int id, int &size); const Shape *loadShapeTable(int id, int &entries); const AmigaSfxTable *loadAmigaSfxTable(int id, int &entries); const Room *loadRoomTable(int id, int &entries); - const HofSeqData *loadHofSequenceData(int id, int &entries); - const ItemAnimData_v1 *loadShapeAnimData_v1(int id, int &entries); + const HoFSeqData *loadHoFSequenceData(int id, int &entries); + const HoFSeqItemAnimData *loadHoFSeqItemAnimData(int id, int &entries); const ItemAnimDefinition *loadItemAnimDefinition(int id, int &entries); #if defined(ENABLE_EOB) || defined(ENABLE_LOL) const uint16 *loadRawDataBe16(int id, int &entries); @@ -803,8 +808,8 @@ private: bool loadShapeTable(Common::SeekableReadStream &stream, void *&ptr, int &size); bool loadAmigaSfxTable(Common::SeekableReadStream &stream, void *&ptr, int &size); bool loadRoomTable(Common::SeekableReadStream &stream, void *&ptr, int &size); - bool loadHofSequenceData(Common::SeekableReadStream &stream, void *&ptr, int &size); - bool loadShapeAnimData_v1(Common::SeekableReadStream &stream, void *&ptr, int &size); + bool loadHoFSequenceData(Common::SeekableReadStream &stream, void *&ptr, int &size); + bool loadHoFSeqItemAnimData(Common::SeekableReadStream &stream, void *&ptr, int &size); bool loadItemAnimDefinition(Common::SeekableReadStream &stream, void *&ptr, int &size); #if defined(ENABLE_EOB) || defined(ENABLE_LOL) bool loadRawDataBe16(Common::SeekableReadStream &stream, void *&ptr, int &size); @@ -829,8 +834,8 @@ private: void freeShapeTable(void *&ptr, int &size); void freeAmigaSfxTable(void *&ptr, int &size); void freeRoomTable(void *&ptr, int &size); - void freeHofSequenceData(void *&ptr, int &size); - void freeHofShapeAnimDataV1(void *&ptr, int &size); + void freeHoFSequenceData(void *&ptr, int &size); + void freeHoFSeqItemAnimData(void *&ptr, int &size); void freeItemAnimDefinition(void *&ptr, int &size); #if defined(ENABLE_EOB) || defined(ENABLE_LOL) void freeRawDataBe16(void *&ptr, int &size); @@ -857,7 +862,7 @@ private: kAmigaSfxTable = 4, k2SeqData = 5, - k2ShpAnimDataV1 = 6, + k2SeqItemAnimData = 6, k2ItemAnimDefinition = 7, kLoLCharData = 8, diff --git a/engines/kyra/resource_intern.cpp b/engines/kyra/resource_intern.cpp index 6f7591ccf1..4c413487ff 100644 --- a/engines/kyra/resource_intern.cpp +++ b/engines/kyra/resource_intern.cpp @@ -496,7 +496,7 @@ public: void advSrcBitsBy1(); void advSrcBitsByIndex(uint8 newIndex); - uint8 getKeyLower() const { return _key & 0xff; } + uint8 getKeyLower() const { return _key & 0xFF; } void setIndex(uint8 index) { _index = index; } uint16 getKeyMasked(uint8 newIndex); uint16 keyMaskedAlign(uint16 val); @@ -515,7 +515,7 @@ void FileExpanderSource::advSrcBitsBy1() { _key >>= 1; if (!--_bitsLeft) { if (_dataPtr < _endofBuffer) - _key = ((*_dataPtr++) << 8) | (_key & 0xff); + _key = ((*_dataPtr++) << 8) | (_key & 0xFF); _bitsLeft = 8; } } @@ -528,7 +528,7 @@ void FileExpanderSource::advSrcBitsByIndex(uint8 newIndex) { _index = -_bitsLeft; _bitsLeft = 8 - _index; if (_dataPtr < _endofBuffer) - _key = (*_dataPtr++ << 8) | (_key & 0xff); + _key = (*_dataPtr++ << 8) | (_key & 0xFF); } _key >>= _index; } @@ -540,13 +540,13 @@ uint16 FileExpanderSource::getKeyMasked(uint8 newIndex) { if (_index > 8) { newIndex = _index - 8; - res = (_key & 0xff) & mskTable[8]; + res = (_key & 0xFF) & mskTable[8]; advSrcBitsByIndex(8); _index = newIndex; - res |= (((_key & 0xff) & mskTable[_index]) << 8); + res |= (((_key & 0xFF) & mskTable[_index]) << 8); advSrcBitsByIndex(_index); } else { - res = (_key & 0xff) & mskTable[_index]; + res = (_key & 0xFF) & mskTable[_index]; advSrcBitsByIndex(_index); } @@ -568,10 +568,10 @@ void FileExpanderSource::copyBytes(uint8 *& dst) { uint16 FileExpanderSource::keyMaskedAlign(uint16 val) { val -= 0x101; - _index = (val & 0xff) >> 2; + _index = (val & 0xFF) >> 2; int16 b = ((_bitsLeft << 8) | _index) - 1; _bitsLeft = b >> 8; - _index = b & 0xff; + _index = b & 0xFF; uint16 res = (((val & 3) + 4) << _index) + 0x101; return res + getKeyMasked(_index); } @@ -727,20 +727,20 @@ bool FileExpander::process(uint8 *dst, const uint8 *src, uint32 outsize, uint32 cmd = ((int16 *)_tables[2])[_src->getKeyLower()]; _src->advSrcBitsByIndex(cmd < 0 ? calcCmdAndIndex(_tables[3], cmd) : _tables[0][cmd]); - if (cmd == 0x11d) { + if (cmd == 0x11D) { cmd = 0x200; } else if (cmd > 0x108) { cmd = _src->keyMaskedAlign(cmd); } if (!(cmd >> 8)) { - *d++ = cmd & 0xff; + *d++ = cmd & 0xFF; } else if (cmd != 0x100) { - cmd -= 0xfe; + cmd -= 0xFE; int16 offset = ((int16 *)_tables[4])[_src->getKeyLower()]; _src->advSrcBitsByIndex(offset < 0 ? calcCmdAndIndex(_tables[5], offset) : _tables[1][offset]); - if ((offset & 0xff) >= 4) { - uint8 newIndex = ((offset & 0xff) >> 1) - 1; + if ((offset & 0xFF) >= 4) { + uint8 newIndex = ((offset & 0xFF) >> 1) - 1; offset = (((offset & 1) + 2) << newIndex); offset += _src->getKeyMasked(newIndex); } @@ -775,7 +775,7 @@ bool FileExpander::process(uint8 *dst, const uint8 *src, uint32 outsize, uint32 void FileExpander::generateTables(uint8 srcIndex, uint8 dstIndex, uint8 dstIndex2, int cnt) { uint8 *tbl1 = _tables[srcIndex]; uint8 *tbl2 = _tables[dstIndex]; - uint8 *tbl3 = dstIndex2 == 0xff ? 0 : _tables[dstIndex2]; + uint8 *tbl3 = dstIndex2 == 0xFF ? 0 : _tables[dstIndex2]; if (!cnt) return; @@ -859,7 +859,7 @@ void FileExpander::generateTables(uint8 srcIndex, uint8 dstIndex, uint8 dstIndex do { s2[o] = cnt; o += inc; - } while (!(o & 0xf00)); + } while (!(o & 0xF00)); } else if (t > 8) { if (!bt) @@ -868,7 +868,7 @@ void FileExpander::generateTables(uint8 srcIndex, uint8 dstIndex, uint8 dstIndex t -= 8; uint8 shiftCnt = 1; uint8 v = (*d) >> 8; - s2 = &((uint16 *)tbl2)[*d & 0xff]; + s2 = &((uint16 *)tbl2)[*d & 0xFF]; do { if (!*s2) { @@ -897,7 +897,7 @@ uint8 FileExpander::calcCmdAndIndex(const uint8 *tbl, int16 ¶) { do { newIndex++; - para = t[((~para) & 0xfffe) | (v & 1)]; + para = t[((~para) & 0xFFFE) | (v & 1)]; v >>= 1; } while (para < 0); @@ -1020,7 +1020,7 @@ Common::Archive *InstallerLoader::load(Resource *owner, const Common::String &fi pos = 0; - const uint32 kExecSize = 0x0bba; + const uint32 kExecSize = 0x0BBA; const uint32 kHeaderSize = 30; const uint32 kHeaderSize2 = 46; diff --git a/engines/kyra/resource_intern.h b/engines/kyra/resource_intern.h index 9d9574d823..e63eab7d6a 100644 --- a/engines/kyra/resource_intern.h +++ b/engines/kyra/resource_intern.h @@ -75,7 +75,7 @@ private: const uint32 *findFile(const Common::String &name) const; const uint16 _entryCount; - const uint32 * const _fileEntries; + const uint32 *const _fileEntries; }; class CachedArchive : public Common::Archive { diff --git a/engines/kyra/saveload.cpp b/engines/kyra/saveload.cpp index 41ba1e5e50..bacfb62c16 100644 --- a/engines/kyra/saveload.cpp +++ b/engines/kyra/saveload.cpp @@ -37,7 +37,7 @@ namespace Kyra { -KyraEngine_v1::kReadSaveHeaderError KyraEngine_v1::readSaveHeader(Common::SeekableReadStream *in, bool loadThumbnail, SaveHeader &header) { +KyraEngine_v1::ReadSaveHeaderError KyraEngine_v1::readSaveHeader(Common::SeekableReadStream *in, bool loadThumbnail, SaveHeader &header) { uint32 type = in->readUint32BE(); header.originalSave = false; header.oldHeader = false; @@ -140,7 +140,7 @@ Common::SeekableReadStream *KyraEngine_v1::openSaveForReading(const char *filena if (!(in = _saveFileMan->openForLoading(filename))) return 0; - kReadSaveHeaderError errorCode = KyraEngine_v1::readSaveHeader(in, false, header); + ReadSaveHeaderError errorCode = KyraEngine_v1::readSaveHeader(in, false, header); if (errorCode != kRSHENoError) { if (errorCode == kRSHEInvalidType) warning("No ScummVM Kyra engine savefile header"); diff --git a/engines/kyra/saveload_eob.cpp b/engines/kyra/saveload_eob.cpp index f7d7d95b57..e4f53a852d 100644 --- a/engines/kyra/saveload_eob.cpp +++ b/engines/kyra/saveload_eob.cpp @@ -298,7 +298,7 @@ Common::Error EoBCoreEngine::loadGameState(int slot) { useMagicBookOrSymbol(_openBookChar, _openBookType); } - _screen->copyRegion(0, 120, 0, 0, 176, 24, 0, _useHiResDithering ? 1 : 12, Screen::CR_NO_P_CHECK); + _screen->copyRegion(0, 120, 0, 0, 176, 24, 0, 12, Screen::CR_NO_P_CHECK); gui_toggleButtons(); setHandItem(_itemInHand); @@ -453,7 +453,7 @@ Common::Error EoBCoreEngine::saveGameStateIntern(int slot, const char *saveName, out->write(l->wallsXorData, 4096); for (int ii = 0; ii < 1024; ii++) - out->writeByte(l->flags[ii] & 0xff); + out->writeByte(l->flags[ii] & 0xFF); EoBMonsterInPlay *lm = (EoBMonsterInPlay *)_lvlTempData[i]->monsters; EoBFlyingObject *lf = (EoBFlyingObject *)_lvlTempData[i]->flyingObjects; @@ -680,7 +680,7 @@ Common::String EoBCoreEngine::readOriginalSaveFile(Common::String &file) { in.skip(4); delete[] c->faceShape; c->faceShape = 0; - in.read(c->mageSpells, (_flags.gameID == GI_EOB1) ? 30 :80); + in.read(c->mageSpells, (_flags.gameID == GI_EOB1) ? 30 : 80); in.read(c->clericSpells, (_flags.gameID == GI_EOB1) ? 30 : 80); c->mageSpellsAvailableFlags = in.readUint32(); for (int ii = 0; ii < 27; ii++) diff --git a/engines/kyra/saveload_lok.cpp b/engines/kyra/saveload_lok.cpp index b76d1da52a..f8cca1ab7b 100644 --- a/engines/kyra/saveload_lok.cpp +++ b/engines/kyra/saveload_lok.cpp @@ -150,7 +150,7 @@ Common::Error KyraEngine_LoK::loadGameState(int slot) { // it wasn't made sure that _curSfxFile was initialized // so if it's out of bounds we just set it to 0. if (_flags.platform == Common::kPlatformFMTowns) { - if (_curSfxFile >= _soundData->fileListLen || _curSfxFile < 0) + if (!_sound->hasSoundFile(_curSfxFile)) _curSfxFile = 0; _sound->loadSoundFile(_curSfxFile); } diff --git a/engines/kyra/saveload_lol.cpp b/engines/kyra/saveload_lol.cpp index b6840663e9..6c83ebd51b 100644 --- a/engines/kyra/saveload_lol.cpp +++ b/engines/kyra/saveload_lol.cpp @@ -41,7 +41,7 @@ Common::Error LoLEngine::loadGameState(int slot) { SaveHeader header; Common::InSaveFile *saveFile = openSaveForReading(fileName, header); if (!saveFile) { - _txt->printMessage(2, "%s", getLangString(0x425d)); + _txt->printMessage(2, "%s", getLangString(0x425D)); return Common::kNoError; } @@ -437,7 +437,7 @@ Common::Error LoLEngine::saveGameStateIntern(int slot, const char *saveName, con out->write(l->wallsXorData, 4096); for (int ii = 0; ii < 1024; ii++) - out->writeByte(l->flags[ii] & 0xff); + out->writeByte(l->flags[ii] & 0xFF); LoLMonster *lm = (LoLMonster *)_lvlTempData[i]->monsters; FlyingObject *lf = (FlyingObject *)_lvlTempData[i]->flyingObjects; @@ -547,7 +547,7 @@ void LoLEngine::restoreTempDataAdjustMonsterStrength(int index) { if (_lvlTempData[index]->monsterDifficulty == _monsterDifficulty) return; - uint16 d = (_monsterModifiers[_lvlTempData[index]->monsterDifficulty] << 8) / _monsterModifiers[_monsterDifficulty]; + uint16 d = (_monsterModifiers1[_lvlTempData[index]->monsterDifficulty] << 8) / _monsterModifiers1[_monsterDifficulty]; for (int i = 0; i < 30; i++) { if (_monsters[i].mode >= 14 || _monsters[i].block == 0 || _monsters[i].hitPoints <= 0) diff --git a/engines/kyra/scene_eob.cpp b/engines/kyra/scene_eob.cpp index 3db055db90..84cf4a3809 100644 --- a/engines/kyra/scene_eob.cpp +++ b/engines/kyra/scene_eob.cpp @@ -100,7 +100,7 @@ void EoBCoreEngine::loadLevel(int level, int sub) { void EoBCoreEngine::readLevelFileData(int level) { Common::String file; Common::SeekableReadStream *s = 0; - static const char *suffix[] = { "INF", "DRO", "ELO", 0 }; + static const char *const suffix[] = { "INF", "DRO", "ELO", 0 }; for (const char *const *sf = suffix; *sf && !s; sf++) { file = Common::String::format("LEVEL%d.%s", level, *sf); @@ -144,10 +144,10 @@ Common::String EoBCoreEngine::initLevelData(int sub) { const char *vmpPattern = (_flags.gameID == GI_EOB1 && (_configRenderMode == Common::kRenderEGA || _configRenderMode == Common::kRenderCGA)) ? "%s.EMP" : "%s.VMP"; Common::SeekableReadStream *s = _res->createReadStream(Common::String::format(vmpPattern, (const char *)pos)); - _vmpSize = s->readUint16LE(); + uint16 size = s->readUint16LE(); delete[] _vmpPtr; - _vmpPtr = new uint16[_vmpSize]; - for (int i = 0; i < _vmpSize; i++) + _vmpPtr = new uint16[size]; + for (int i = 0; i < size; i++) _vmpPtr[i] = s->readUint16LE(); delete s; @@ -157,7 +157,7 @@ Common::String EoBCoreEngine::initLevelData(int sub) { _curGfxFile = (const char *)pos; pos += slen; - if (*pos++ != 0xff && _flags.gameID == GI_EOB2) { + if (*pos++ != 0xFF && _flags.gameID == GI_EOB2) { tmpStr = Common::String::format(paletteFilePattern, (const char *)pos); pos += 13; } @@ -174,7 +174,7 @@ Common::String EoBCoreEngine::initLevelData(int sub) { if (_configRenderMode != Common::kRenderCGA) { Palette backupPal(256); backupPal.copy(_screen->getPalette(0), 224, 32, 224); - _screen->getPalette(0).fill(224, 32, 0x3f); + _screen->getPalette(0).fill(224, 32, 0x3F); uint8 *src = _screen->getPalette(0).getData(); _screen->createFadeTable(src, _screen->getFadeTable(0), 4, 75); // green @@ -271,7 +271,7 @@ void EoBCoreEngine::addLevelItems() { for (int i = 0; i < 600; i++) { if (_items[i].level != _currentLevel || _items[i].block <= 0) continue; - setItemPosition((Item *)&_levelBlockProperties[_items[i].block & 0x3ff].drawObjects, _items[i].block, i, _items[i].pos); + setItemPosition((Item *)&_levelBlockProperties[_items[i].block & 0x3FF].drawObjects, _items[i].block, i, _items[i].pos); } } @@ -283,7 +283,7 @@ void EoBCoreEngine::loadVcnData(const char *file, const uint8 *cgaMapping) { _screen->loadBitmap(Common::String::format(filePattern, _lastBlockDataFile).c_str(), 3, 3, 0); const uint8 *pos = _screen->getCPagePtr(3); - uint32 vcnSize = READ_LE_UINT16(pos) * _vcnBlockWidth * _vcnBlockHeight; + uint32 vcnSize = READ_LE_UINT16(pos) << 5; pos += 2; const uint8 *colMap = pos; @@ -292,78 +292,28 @@ void EoBCoreEngine::loadVcnData(const char *file, const uint8 *cgaMapping) { delete[] _vcnBlocks; _vcnBlocks = new uint8[vcnSize]; - if (_flags.gameID == GI_EOB2 && _configRenderMode == Common::kRenderEGA) { - const uint8 *egaTable = _screen->getEGADitheringTable(); - assert(_vmpPtr); - assert(egaTable); - - delete[] _vcnTransitionMask; - _vcnTransitionMask = new uint8[vcnSize]; - - for (int i = 0; i < _vmpSize; i++) { - uint16 vcnOffs = _vmpPtr[i] & 0x3FFF; - const uint8 *src = &pos[vcnOffs << 5]; - uint8 *dst1 = &_vcnBlocks[vcnOffs << 7]; - uint8 *dst3 = &_vcnTransitionMask[vcnOffs << 7]; - int palOffset = (i < 330) ? 0 : _wllVcnOffset; - - for (int y = 0; y < 8; y++) { - uint8 *dst2 = dst1 + 8; - uint8 *dst4 = dst3 + 8; - - for (int x = 0; x < 4; x++) { - uint8 in = *src++; - - dst1[0] = dst2[0] = egaTable[colMap[(in >> 4) + palOffset]]; - dst1[1] = dst2[1] = egaTable[colMap[(in & 0x0f) + palOffset]]; - dst3[0] = dst4[0] = (in & 0xf0) ? 0 : 0xff; - dst3[1] = dst4[1] = (in & 0x0f) ? 0 : 0xff; - - dst1 += 2; - dst2 += 2; - dst3 += 2; - dst4 += 2; - } - - dst1 += 8; - dst3 += 8; - } - } - } else if (_configRenderMode == Common::kRenderCGA) { + if (_configRenderMode == Common::kRenderCGA) { uint8 *tmp = _screen->encodeShape(0, 0, 1, 8, false, cgaMapping); delete[] tmp; delete[] _vcnTransitionMask; _vcnTransitionMask = new uint8[vcnSize]; - uint8 tblSwitch = 0; + uint8 tblSwitch = 1; uint8 *dst = _vcnBlocks; uint8 *dst2 = _vcnTransitionMask; while (dst < _vcnBlocks + vcnSize) { const uint16 *table = _screen->getCGADitheringTable((tblSwitch++) & 1); for (int ii = 0; ii < 2; ii++) { - *dst++ = ((table[pos[0]] & 0x000f) << 4) | ((table[pos[0]] & 0x0f00) >> 8); - *dst++= ((table[pos[1]] & 0x000f) << 4) | ((table[pos[1]] & 0x0f00) >> 8); - - uint8 msk = 0; - if (pos[0] & 0xf0) - msk |= 0x30; - if (pos[0] & 0x0f) - msk |= 0x03; - *dst2++ = msk ^ 0x33; - - msk = 0; - if (pos[1] & 0xf0) - msk |= 0x30; - if (pos[1] & 0x0f) - msk |= 0x03; - *dst2++ = msk ^ 0x33; - + *dst++ = (table[pos[0]] & 0x000F) | ((table[pos[0]] & 0x0F00) >> 4); + *dst++ = (table[pos[1]] & 0x000F) | ((table[pos[1]] & 0x0F00) >> 4); + *dst2++ = ((pos[0] & 0xF0 ? 0x30 : 0) | (pos[0] & 0x0F ? 0x03 : 0)) ^ 0x33; + *dst2++ = ((pos[1] & 0xF0 ? 0x30 : 0) | (pos[1] & 0x0F ? 0x03 : 0)) ^ 0x33; pos += 2; } } } else { - if (_configRenderMode != Common::kRenderEGA) + if (!(_flags.gameID == GI_EOB1 && _configRenderMode == Common::kRenderEGA)) memcpy(_vcnColTable, colMap, 32); memcpy(_vcnBlocks, pos, vcnSize); } @@ -429,8 +379,8 @@ void EoBCoreEngine::loadDecorations(const char *cpsFile, const char *decFile) { LevelDecorationProperty *l = &_levelDecorationData[i]; for (int ii = 0; ii < 10; ii++) { l->shapeIndex[ii] = s->readByte(); - if (l->shapeIndex[ii] == 0xff) - l->shapeIndex[ii] = 0xffff; + if (l->shapeIndex[ii] == 0xFF) + l->shapeIndex[ii] = 0xFFFF; } l->next = s->readByte(); l->flags = s->readByte(); @@ -477,7 +427,7 @@ void EoBCoreEngine::assignWallsAndDecorations(int wallIndex, int vmpIndex, int d for (int i = 0; i < 10; i++) { uint16 t = _levelDecorationProperties[_mappedDecorationsCount].shapeIndex[i]; - if (t == 0xffff) + if (t == 0xFFFF) continue; if (_levelDecorationShapes[t]) @@ -529,7 +479,7 @@ void EoBCoreEngine::toggleWallState(int wall, int toggle) { if (toggle) _wllWallFlags[wall + i] |= 2; else - _wllWallFlags[wall + i] &= 0xfd; + _wllWallFlags[wall + i] &= 0xFD; } } @@ -643,7 +593,7 @@ void EoBCoreEngine::drawDecorations(int index) { if ((i == 0) && (flg & 1 || ((flg & 2) && _wllProcessFlag))) ix = -ix; - if (_levelDecorationProperties[l].shapeIndex[shpIx] == 0xffff) { + if (_levelDecorationProperties[l].shapeIndex[shpIx] == 0xFFFF) { l = _levelDecorationProperties[l].next; continue; } @@ -758,7 +708,7 @@ int EoBCoreEngine::clickedNiche(uint16 block, uint16 direction) { if (_dscItemShapeMap[_items[_itemInHand].icon] <= 14) { _txt->printMessage(_pryDoorStrings[5]); } else { - setItemPosition((Item *)&_levelBlockProperties[block & 0x3ff].drawObjects, block, _itemInHand, 8); + setItemPosition((Item *)&_levelBlockProperties[block & 0x3FF].drawObjects, block, _itemInHand, 8); runLevelScript(block, 4); setHandItem(0); _sceneUpdateRequired = true; @@ -781,7 +731,7 @@ int EoBCoreEngine::clickedDoorPry(uint16 block, uint16 direction) { int d = -1; for (int i = 0; i < 6; i++) { - if (!testCharacter(i, 0x0d)) + if (!testCharacter(i, 0x0D)) continue; if (d >= 0) { int s1 = _characters[i].strengthCur + _characters[i].strengthExtCur; @@ -826,7 +776,7 @@ int EoBCoreEngine::clickedDoorNoPry(uint16 block, uint16 direction) { int EoBCoreEngine::specialWallAction(int block, int direction) { direction ^= 2; uint8 type = _specialWallTypes[_levelBlockProperties[block].walls[direction]]; - if (!type || !(_clickedSpecialFlag & (((_levelBlockProperties[block].flags & 0xf8) >> 3) | 0xe0))) + if (!type || !(_clickedSpecialFlag & (((_levelBlockProperties[block].flags & 0xF8) >> 3) | 0xE0))) return 0; int res = 0; diff --git a/engines/kyra/scene_lol.cpp b/engines/kyra/scene_lol.cpp index 628654f127..f1045d2ddf 100644 --- a/engines/kyra/scene_lol.cpp +++ b/engines/kyra/scene_lol.cpp @@ -193,7 +193,7 @@ int LoLEngine::assignLevelDecorationShapes(int index) { for (int i = 0; i < 10; i++) { uint16 t = _levelDecorationProperties[o].shapeIndex[i]; - if (t == 0xffff) + if (t == 0xFFFF) continue; uint16 pv = p1[t]; @@ -254,7 +254,7 @@ void LoLEngine::loadBlockProperties(const char *cmzFile) { _levelBlockProperties[i].direction = 5; if (_wllAutomapData[_levelBlockProperties[i].walls[0]] == 17) { - _levelBlockProperties[i].flags &= 0xef; + _levelBlockProperties[i].flags &= 0xEF; _levelBlockProperties[i].flags |= 0x20; } } @@ -315,9 +315,9 @@ void LoLEngine::loadLevelGraphics(const char *file, int specialColor, int weight if (_lastSpecialColor == 1) _lastSpecialColor = 0x44; else if (_lastSpecialColor == 0x66) - _lastSpecialColor = scumm_stricmp(file, "YVEL2") ? 0xcc : 0x44; - else if (_lastSpecialColor == 0x6b) - _lastSpecialColor = 0xcc; + _lastSpecialColor = scumm_stricmp(file, "YVEL2") ? 0xCC : 0x44; + else if (_lastSpecialColor == 0x6B) + _lastSpecialColor = 0xCC; else _lastSpecialColor = 0x44; } @@ -431,17 +431,17 @@ void LoLEngine::loadLevelGraphics(const char *file, int specialColor, int weight } for (int ii = l; ii < 256; ii++) - levelOverlay[ii] = ii & 0xff; + levelOverlay[ii] = ii & 0xFF; } uint8 *levelOverlay = _screen->getLevelOverlay(7); for (int i = 0; i < 256; i++) - levelOverlay[i] = i & 0xff; + levelOverlay[i] = i & 0xFF; if (_flags.use16ColorMode) { - _screen->getLevelOverlay(6)[0xee] = 0xee; + _screen->getLevelOverlay(6)[0xEE] = 0xEE; if (_lastSpecialColor == 0x44) - _screen->getLevelOverlay(5)[0xee] = 0xee; + _screen->getLevelOverlay(5)[0xEE] = 0xEE; for (int i = 0; i < 7; i++) memcpy(_screen->getLevelOverlay(i), _screen->getLevelOverlay(i + 1), 256); @@ -505,14 +505,14 @@ void LoLEngine::resetBlockProperties() { for (int i = 0; i < 1024; i++) { LevelBlockProperty *l = &_levelBlockProperties[i]; if (l->flags & 0x10) { - l->flags &= 0xef; + l->flags &= 0xEF; if (testWallInvisibility(i, 0) && testWallInvisibility(i, 1)) l->flags |= 0x40; } else { if (l->flags & 0x40) - l->flags &= 0xbf; + l->flags &= 0xBF; else if (l->flags & 0x80) - l->flags &= 0x7f; + l->flags &= 0x7F; } } } @@ -574,7 +574,7 @@ void LoLEngine::updateLampStatus() { setPaletteBrightness(_screen->getPalette(0), _brightness, newLampEffect); _lampStatusTimer = _system->getMillis() + (10 + rollDice(1, 30)) * _tickLength; } else { - if ((_lampEffect & 0xfe) == (newLampEffect & 0xfe)) { + if ((_lampEffect & 0xFE) == (newLampEffect & 0xFE)) { if (_system->getMillis() <= _lampStatusTimer) { newLampEffect = _lampEffect; } else { @@ -702,7 +702,7 @@ void LoLEngine::moveParty(uint16 direction, int unk1, int unk2, int buttonShape) runLevelScript(opos, 8); runLevelScript(npos, 2); - if (_levelBlockProperties[npos].walls[0] == 0x1a) + if (_levelBlockProperties[npos].walls[0] == 0x1A) memset(_levelBlockProperties[npos].walls, 0, 4); } } @@ -711,12 +711,12 @@ void LoLEngine::moveParty(uint16 direction, int unk1, int unk2, int buttonShape) } uint16 LoLEngine::calcBlockIndex(uint16 x, uint16 y) { - return (((y & 0xff00) >> 3) | (x >> 8)) & 0x3ff; + return (((y & 0xFF00) >> 3) | (x >> 8)) & 0x3FF; } void LoLEngine::calcCoordinates(uint16 &x, uint16 &y, int block, uint16 xOffs, uint16 yOffs) { - x = (block & 0x1f) << 8 | xOffs; - y = ((block & 0xffe0) << 3) | yOffs; + x = (block & 0x1F) << 8 | xOffs; + y = ((block & 0xFFE0) << 3) | yOffs; } void LoLEngine::calcCoordinatesForSingleCharacter(int charNum, uint16 &x, uint16 &y) { @@ -732,8 +732,8 @@ void LoLEngine::calcCoordinatesForSingleCharacter(int charNum, uint16 &x, uint16 calcCoordinatesAddDirectionOffset(x, y, _currentDirection); - x |= (_partyPosX & 0xff00); - y |= (_partyPosY & 0xff00); + x |= (_partyPosX & 0xFF00); + y |= (_partyPosY & 0xFF00); } void LoLEngine::calcCoordinatesAddDirectionOffset(uint16 &x, uint16 &y, int direction) { @@ -777,7 +777,7 @@ void LoLEngine::notifyBlockNotPassable(int scrollFlag) { movePartySmoothScrollBlocked(2); snd_stopSpeech(true); - _txt->printMessage(0x8002, "%s", getLangString(0x403f)); + _txt->printMessage(0x8002, "%s", getLangString(0x403F)); snd_playSoundEffect(19, -1); } @@ -804,7 +804,7 @@ int LoLEngine::clickedNiche(uint16 block, uint16 direction) { return 0; uint16 x = 0x80; - uint16 y = 0xff; + uint16 y = 0xFF; calcCoordinatesAddDirectionOffset(x, y, _currentDirection); calcCoordinates(x, y, block, x, y); setItemPosition(_itemInHand, x, y, 8, 1); @@ -1180,14 +1180,14 @@ void LoLEngine::processGasExplosion(int soundId) { memcpy(p2, p1, 768); for (int i = 1; i < 128; i++) - p2[i * 3] = 0x3f; + p2[i * 3] = 0x3F; uint32 ctime = _system->getMillis(); - while (_screen->fadePaletteStep(_screen->getPalette(0).getData(), p2, _system->getMillis() - ctime, 10)) + while (_screen->timedPaletteFadeStep(_screen->getPalette(0).getData(), p2, _system->getMillis() - ctime, 10)) updateInput(); ctime = _system->getMillis(); - while (_screen->fadePaletteStep(p2, _screen->getPalette(0).getData(), _system->getMillis() - ctime, 50)) + while (_screen->timedPaletteFadeStep(p2, _screen->getPalette(0).getData(), _system->getMillis() - ctime, 50)) updateInput(); } @@ -1244,10 +1244,10 @@ void LoLEngine::setWallType(int block, int wall, int val) { for (int i = 0; i < 4; i++) _levelBlockProperties[block].walls[i] = val; if (_wllAutomapData[val] == 17) { - _levelBlockProperties[block].flags &= 0xef; + _levelBlockProperties[block].flags &= 0xEF; _levelBlockProperties[block].flags |= 0x20; } else { - _levelBlockProperties[block].flags &= 0xdf; + _levelBlockProperties[block].flags &= 0xDF; } } else { _levelBlockProperties[block].walls[wall] = val; @@ -1491,7 +1491,7 @@ void LoLEngine::drawDecorations(int index) { ov = 0; } ovl = _screen->getLevelOverlay(ov); - } else if (_levelDecorationProperties[l].shapeIndex[shpIx] != 0xffff) { + } else if (_levelDecorationProperties[l].shapeIndex[shpIx] != 0xFFFF) { scaleW = scaleH = 0x100; int ov = 7; if (_flags.use16ColorMode) { @@ -1504,7 +1504,7 @@ void LoLEngine::drawDecorations(int index) { ovl = _screen->getLevelOverlay(ov); } - if (_levelDecorationProperties[l].shapeIndex[shpIx] != 0xffff) { + if (_levelDecorationProperties[l].shapeIndex[shpIx] != 0xFFFF) { shapeData = _levelDecorationShapes[_levelDecorationProperties[l].shapeIndex[shpIx]]; if (shapeData) { if (ix < 0) { @@ -1538,10 +1538,10 @@ void LoLEngine::drawDecorations(int index) { } void LoLEngine::drawBlockEffects(int index, int type) { - static const uint16 yOffs[] = { 0xff, 0xff, 0x80, 0x80 }; + static const uint16 yOffs[] = { 0xFF, 0xFF, 0x80, 0x80 }; uint8 flg = _visibleBlocks[index]->flags; // flags: 0x10 = ice wall, 0x20 = teleporter, 0x40 = blue slime spot, 0x80 = blood spot - if (!(flg & 0xf0)) + if (!(flg & 0xF0)) return; type = (type == 0) ? 2 : 0; @@ -1562,8 +1562,8 @@ void LoLEngine::drawBlockEffects(int index, int type) { calcCoordinatesAddDirectionOffset(x, y, _currentDirection); - x |= ((_visibleBlockIndex[index] & 0x1f) << 8); - y |= ((_visibleBlockIndex[index] & 0xffe0) << 3); + x |= ((_visibleBlockIndex[index] & 0x1F) << 8); + y |= ((_visibleBlockIndex[index] & 0xFFE0) << 3); drawItemOrMonster(_effectShapes[type], ovl, x, y, 0, (type == 1) ? -20 : 0, drawFlag, -1, false); } diff --git a/engines/kyra/scene_mr.cpp b/engines/kyra/scene_mr.cpp index d6df523f82..c9486d9c45 100644 --- a/engines/kyra/scene_mr.cpp +++ b/engines/kyra/scene_mr.cpp @@ -22,7 +22,7 @@ #include "kyra/kyra_mr.h" #include "kyra/screen_mr.h" -#include "kyra/sound.h" +#include "kyra/sound_digital.h" #include "kyra/resource.h" #include "common/system.h" diff --git a/engines/kyra/scene_rpg.cpp b/engines/kyra/scene_rpg.cpp index 3a694e05fe..6d724efed0 100644 --- a/engines/kyra/scene_rpg.cpp +++ b/engines/kyra/scene_rpg.cpp @@ -151,7 +151,7 @@ void KyraRpgEngine::generateBlockDrawingBuffer() { memset(_blockDrawingBuffer, 0, 660 * sizeof(uint16)); - _wllProcessFlag = ((_currentBlock >> 5) + (_currentBlock & 0x1f) + _currentDirection) & 1; + _wllProcessFlag = ((_currentBlock >> 5) + (_currentBlock & 0x1F) + _currentDirection) & 1; if (_wllProcessFlag) // floor and ceiling generateVmpTileDataFlipped(0, 15, 1, -330, 22, 15); @@ -318,7 +318,7 @@ bool KyraRpgEngine::hasWall(int index) { void KyraRpgEngine::assignVisibleBlocks(int block, int direction) { for (int i = 0; i < 18; i++) { - uint16 t = (block + _dscBlockIndex[direction * 18 + i]) & 0x3ff; + uint16 t = (block + _dscBlockIndex[direction * 18 + i]) & 0x3FF; _visibleBlockIndex[i] = t; _visibleBlocks[i] = &_levelBlockProperties[t]; @@ -348,9 +348,6 @@ bool KyraRpgEngine::checkSceneUpdateNeed(int block) { void KyraRpgEngine::drawVcnBlocks() { uint8 *d = _sceneWindowBuffer; uint16 *bdb = _blockDrawingBuffer; - uint16 pitch = 22 * _vcnBlockWidth * 2; - uint8 pxl[2]; - pxl[0] = pxl[1] = 0; for (int y = 0; y < 15; y++) { for (int x = 0; x < 22; x++) { @@ -369,96 +366,95 @@ void KyraRpgEngine::drawVcnBlocks() { if (vcnOffset & 0x4000) { horizontalFlip = true; - vcnOffset &= 0x3fff; + vcnOffset &= 0x3FFF; } uint8 *src = 0; if (vcnOffset) { - src = &_vcnBlocks[vcnOffset * _vcnBlockWidth * _vcnBlockHeight]; + src = &_vcnBlocks[vcnOffset << 5]; wllVcnOffset = _wllVcnOffset; } else { // floor/ceiling blocks vcnOffset = bdb[329]; if (vcnOffset & 0x4000) { horizontalFlip = true; - vcnOffset &= 0x3fff; + vcnOffset &= 0x3FFF; } - src = (_vcfBlocks ? _vcfBlocks : _vcnBlocks) + (vcnOffset * _vcnBlockWidth * _vcnBlockHeight); + src = (_vcfBlocks ? _vcfBlocks : _vcnBlocks) + (vcnOffset << 5); } uint8 shift = _vcnShift ? _vcnShift[vcnOffset] : _blockBrightness; if (horizontalFlip) { - for (int blockY = 0; blockY < _vcnBlockHeight; blockY++) { - src += (_vcnBlockWidth - 1); - for (int blockX = 0; blockX < _vcnBlockWidth; blockX++) { + for (int blockY = 0; blockY < 8; blockY++) { + src += 3; + for (int blockX = 0; blockX < 4; blockX++) { uint8 bl = *src--; - d[_vcnFlip0] = _vcnColTable[((bl & 0x0f) + wllVcnOffset) | shift]; - d[_vcnFlip1] = _vcnColTable[((bl >> 4) + wllVcnOffset) | shift]; - d += 2; + *d++ = _vcnColTable[((bl & 0x0F) + wllVcnOffset) | shift]; + *d++ = _vcnColTable[((bl >> 4) + wllVcnOffset) | shift]; } - src += (_vcnBlockWidth + 1); - d += (pitch - 2 * _vcnBlockWidth); + src += 5; + d += 168; } } else { - for (int blockY = 0; blockY < _vcnBlockHeight; blockY++) { - for (int blockX = 0; blockX < _vcnBlockWidth; blockX++) { + for (int blockY = 0; blockY < 8; blockY++) { + for (int blockX = 0; blockX < 4; blockX++) { uint8 bl = *src++; *d++ = _vcnColTable[((bl >> 4) + wllVcnOffset) | shift]; - *d++ = _vcnColTable[((bl & 0x0f) + wllVcnOffset) | shift]; + *d++ = _vcnColTable[((bl & 0x0F) + wllVcnOffset) | shift]; } - d += (pitch - 2 * _vcnBlockWidth); + d += 168; } } - d -= (pitch * _vcnBlockHeight - 2 * _vcnBlockWidth); + d -= 1400; if (vcnExtraOffsetWll) { - d -= (2 * _vcnBlockWidth); + d -= 8; horizontalFlip = false; if (vcnExtraOffsetWll & 0x4000) { - vcnExtraOffsetWll &= 0x3fff; + vcnExtraOffsetWll &= 0x3FFF; horizontalFlip = true; } shift = _vcnShift ? _vcnShift[vcnExtraOffsetWll] : _blockBrightness; - src = &_vcnBlocks[vcnExtraOffsetWll * _vcnBlockWidth * _vcnBlockHeight]; - uint8 *maskTable = _vcnTransitionMask ? &_vcnTransitionMask[vcnExtraOffsetWll * _vcnBlockWidth * _vcnBlockHeight] : 0; + src = &_vcnBlocks[vcnExtraOffsetWll << 5]; + uint8 *maskTable = _vcnTransitionMask ? &_vcnTransitionMask[vcnExtraOffsetWll << 5] : 0; if (horizontalFlip) { - for (int blockY = 0; blockY < _vcnBlockHeight; blockY++) { - src += (_vcnBlockWidth - 1); - maskTable += (_vcnBlockWidth - 1); - for (int blockX = 0; blockX < _vcnBlockWidth; blockX++) { + for (int blockY = 0; blockY < 8; blockY++) { + src += 3; + maskTable += 3; + for (int blockX = 0; blockX < 4; blockX++) { uint8 bl = *src--; uint8 mask = _vcnTransitionMask ? *maskTable-- : 0; - pxl[_vcnFlip0] = _vcnColTable[((bl & 0x0f) + wllVcnRmdOffset) | shift]; - pxl[_vcnFlip1] = _vcnColTable[((bl >> 4) + wllVcnRmdOffset) | shift]; + uint8 h = _vcnColTable[((bl & 0x0F) + wllVcnRmdOffset) | shift]; + uint8 l = _vcnColTable[((bl >> 4) + wllVcnRmdOffset) | shift]; if (_vcnTransitionMask) - *d = (*d & (mask & 0x0f)) | pxl[0]; - else if (pxl[0]) - *d = pxl[0]; + *d = (*d & (mask & 0x0F)) | h; + else if (h) + *d = h; d++; if (_vcnTransitionMask) - *d = (*d & (mask >> 4)) | pxl[1]; - else if (pxl[1]) - *d = pxl[1]; + *d = (*d & (mask >> 4)) | l; + else if (l) + *d = l; d++; } - src += (_vcnBlockWidth + 1); - maskTable += (_vcnBlockWidth + 1); - d += (pitch - 2 * _vcnBlockWidth); + src += 5; + maskTable += 5; + d += 168; } } else { - for (int blockY = 0; blockY < _vcnBlockHeight; blockY++) { - for (int blockX = 0; blockX < _vcnBlockWidth; blockX++) { + for (int blockY = 0; blockY < 8; blockY++) { + for (int blockX = 0; blockX < 4; blockX++) { uint8 bl = *src++; uint8 mask = _vcnTransitionMask ? *maskTable++ : 0; uint8 h = _vcnColTable[((bl >> 4) + wllVcnRmdOffset) | shift]; - uint8 l = _vcnColTable[((bl & 0x0f) + wllVcnRmdOffset) | shift]; + uint8 l = _vcnColTable[((bl & 0x0F) + wllVcnRmdOffset) | shift]; if (_vcnTransitionMask) *d = (*d & (mask >> 4)) | h; @@ -467,18 +463,18 @@ void KyraRpgEngine::drawVcnBlocks() { d++; if (_vcnTransitionMask) - *d = (*d & (mask & 0x0f)) | l; + *d = (*d & (mask & 0x0F)) | l; else if (l) *d = l; d++; } - d += (pitch - 2 * _vcnBlockWidth); + d += 168; } } - d -= (pitch * _vcnBlockHeight - 2 * _vcnBlockWidth); + d -= 1400; } } - d += (pitch * (_vcnBlockHeight - 1)); + d += 1232; } screen()->copyBlockToPage(_sceneDrawPage1, _sceneXoffset, 0, 176, 120, _sceneWindowBuffer); @@ -486,7 +482,7 @@ void KyraRpgEngine::drawVcnBlocks() { uint16 KyraRpgEngine::calcNewBlockPosition(uint16 curBlock, uint16 direction) { static const int16 blockPosTable[] = { -32, 1, 32, -1 }; - return (curBlock + blockPosTable[direction]) & 0x3ff; + return (curBlock + blockPosTable[direction]) & 0x3FF; } int KyraRpgEngine::clickedWallShape(uint16 block, uint16 direction) { diff --git a/engines/kyra/screen.cpp b/engines/kyra/screen.cpp index 04d805737f..419b630714 100644 --- a/engines/kyra/screen.cpp +++ b/engines/kyra/screen.cpp @@ -51,13 +51,12 @@ Screen::Screen(KyraEngine_v1 *vm, OSystem *system, const ScreenDim *dimTable, co memset(_fonts, 0, sizeof(_fonts)); memset(_pagePtrs, 0, sizeof(_pagePtrs)); - // Set scale factor to 1 (no scaling) for all pages - memset(_pageScaleFactor, 1, sizeof(_pageScaleFactor)); // In VGA mode the odd and even page pointers point to the same buffers. for (int i = 0; i < SCREEN_PAGE_NUM; i++) _pageMapping[i] = i & ~1; _renderMode = Common::kRenderDefault; + _sjisMixedFontMode = false; _currentFont = FID_8_FNT; _paletteChanged = true; @@ -114,7 +113,7 @@ bool Screen::init() { } // CGA and EGA modes use additional pages to do the CGA/EGA specific graphics conversions. - if (_renderMode == Common::kRenderCGA || _renderMode == Common::kRenderEGA) { + if (_vm->game() == GI_EOB1 && (_renderMode == Common::kRenderCGA || _renderMode == Common::kRenderEGA)) { for (int i = 0; i < 8; i++) _pageMapping[i] = i; } @@ -126,6 +125,7 @@ bool Screen::init() { if (_useOverlays) { _useSJIS = (_vm->gameFlags().lang == Common::JA_JPN); _sjisInvisibleColor = (_vm->game() == GI_KYRA1) ? 0x80 : 0xF6; + _sjisMixedFontMode = !_use16ColorMode; for (int i = 0; i < SCREEN_OVLS_NUM; ++i) { if (!_sjisOverlayPtrs[i]) { @@ -141,7 +141,7 @@ bool Screen::init() { if (!font) error("Could not load any SJIS font, neither the original nor ScummVM's 'SJIS.FNT'"); - _fonts[FID_SJIS_FNT] = new SJISFont(font, _sjisInvisibleColor, _use16ColorMode, !_use16ColorMode); + _fonts[FID_SJIS_FNT] = new SJISFont(font, _sjisInvisibleColor, _use16ColorMode, !_use16ColorMode && _vm->game() != GI_LOL, _vm->game() == GI_LOL ? 1 : 0); } } @@ -154,9 +154,7 @@ bool Screen::init() { } int numPages = realPages.size(); - uint32 bufferSize = 0; - for (int i = 0; i < numPages; i++) - bufferSize += (SCREEN_PAGE_SIZE * _pageScaleFactor[realPages[i]] * _pageScaleFactor[realPages[i]]); + uint32 bufferSize = numPages * SCREEN_PAGE_SIZE; uint8 *pagePtr = new uint8[bufferSize]; memset(pagePtr, 0, bufferSize); @@ -167,7 +165,7 @@ bool Screen::init() { _pagePtrs[i] = _pagePtrs[_pageMapping[i]]; } else { _pagePtrs[i] = pagePtr; - pagePtr += (SCREEN_PAGE_SIZE * _pageScaleFactor[i] * _pageScaleFactor[i]); + pagePtr += SCREEN_PAGE_SIZE; } } @@ -292,7 +290,7 @@ void Screen::updateScreen() { needRealUpdate = true; if (!_useOverlays) - _system->copyRectToScreen(getPagePtr(2), SCREEN_W, 320, 0, SCREEN_W * _pageScaleFactor[2], SCREEN_H * _pageScaleFactor[2]); + _system->copyRectToScreen(getPagePtr(2), SCREEN_W, 320, 0, SCREEN_W, SCREEN_H); else _system->copyRectToScreen(getPagePtr(2), SCREEN_W, 640, 0, SCREEN_W, SCREEN_H); } @@ -303,12 +301,12 @@ void Screen::updateScreen() { void Screen::updateDirtyRects() { if (_forceFullUpdate) { - _system->copyRectToScreen(getCPagePtr(0), SCREEN_W * _pageScaleFactor[0], 0, 0, SCREEN_W * _pageScaleFactor[0], SCREEN_H * _pageScaleFactor[0]); + _system->copyRectToScreen(getCPagePtr(0), SCREEN_W, 0, 0, SCREEN_W, SCREEN_H); } else { const byte *page0 = getCPagePtr(0); Common::List<Common::Rect>::iterator it; for (it = _dirtyRects.begin(); it != _dirtyRects.end(); ++it) { - _system->copyRectToScreen(page0 + it->top * SCREEN_W * _pageScaleFactor[0] + it->left, SCREEN_W * _pageScaleFactor[0], it->left, it->top, it->width(), it->height()); + _system->copyRectToScreen(page0 + it->top * SCREEN_W + it->left, SCREEN_W, it->left, it->top, it->width(), it->height()); } } _forceFullUpdate = false; @@ -493,7 +491,7 @@ void Screen::clearPage(int pageNum) { assert(pageNum < SCREEN_PAGE_NUM); if (pageNum == 0 || pageNum == 1) _forceFullUpdate = true; - memset(getPagePtr(pageNum), 0, SCREEN_PAGE_SIZE * _pageScaleFactor[_curPage] * _pageScaleFactor[_curPage]); + memset(getPagePtr(pageNum), 0, SCREEN_PAGE_SIZE); clearOverlayPage(pageNum); } @@ -507,7 +505,7 @@ int Screen::setCurPage(int pageNum) { void Screen::clearCurPage() { if (_curPage == 0 || _curPage == 1) _forceFullUpdate = true; - memset(getPagePtr(_curPage), 0, SCREEN_PAGE_SIZE * _pageScaleFactor[_curPage] * _pageScaleFactor[_curPage]); + memset(getPagePtr(_curPage), 0, SCREEN_PAGE_SIZE); clearOverlayPage(_curPage); } @@ -672,7 +670,7 @@ void Screen::setPagePixel(int pageNum, int x, int y, uint8 color) { color |= (color << 4); } else if (_renderMode == Common::kRenderCGA) { color &= 0x03; - } else if (_renderMode == Common::kRenderEGA) { + } else if (_renderMode == Common::kRenderEGA && !_useHiResEGADithering) { color &= 0x0F; } @@ -881,26 +879,16 @@ void Screen::copyToPage0(int y, int h, uint8 page, uint8 *seqBuf) { } void Screen::copyRegion(int x1, int y1, int x2, int y2, int w, int h, int srcPage, int dstPage, int flags) { - // Since we don't (need to) do any actual scaling, we check for compatible pages here - assert(_pageScaleFactor[srcPage] == _pageScaleFactor[dstPage]); - - x1 *= _pageScaleFactor[srcPage]; - y1 *= _pageScaleFactor[srcPage]; - x2 *= _pageScaleFactor[dstPage]; - y2 *= _pageScaleFactor[dstPage]; - w *= _pageScaleFactor[srcPage]; - h *= _pageScaleFactor[srcPage]; - if (x2 < 0) { if (x2 <= -w) return; w += x2; x1 -= x2; x2 = 0; - } else if (x2 + w >= SCREEN_W * _pageScaleFactor[dstPage]) { - if (x2 > SCREEN_W * _pageScaleFactor[dstPage]) + } else if (x2 + w >= SCREEN_W) { + if (x2 > SCREEN_W) return; - w = SCREEN_W * _pageScaleFactor[srcPage] - x2; + w = SCREEN_W - x2; } if (y2 < 0) { @@ -909,14 +897,14 @@ void Screen::copyRegion(int x1, int y1, int x2, int y2, int w, int h, int srcPag h += y2; y1 -= y2; y2 = 0; - } else if (y2 + h >= SCREEN_H * _pageScaleFactor[dstPage]) { - if (y2 > SCREEN_H * _pageScaleFactor[dstPage]) + } else if (y2 + h >= SCREEN_H) { + if (y2 > SCREEN_H) return; - h = SCREEN_H * _pageScaleFactor[srcPage] - y2; + h = SCREEN_H - y2; } - const uint8 *src = getPagePtr(srcPage) + y1 * SCREEN_W * _pageScaleFactor[srcPage] + x1; - uint8 *dst = getPagePtr(dstPage) + y2 * SCREEN_W * _pageScaleFactor[dstPage] + x2; + const uint8 *src = getPagePtr(srcPage) + y1 * SCREEN_W + x1; + uint8 *dst = getPagePtr(dstPage) + y2 * SCREEN_W + x2; if (src == dst) return; @@ -929,8 +917,8 @@ void Screen::copyRegion(int x1, int y1, int x2, int y2, int w, int h, int srcPag if (flags & CR_NO_P_CHECK) { while (h--) { memmove(dst, src, w); - src += SCREEN_W * _pageScaleFactor[srcPage]; - dst += SCREEN_W * _pageScaleFactor[dstPage]; + src += SCREEN_W; + dst += SCREEN_W; } } else { while (h--) { @@ -938,24 +926,19 @@ void Screen::copyRegion(int x1, int y1, int x2, int y2, int w, int h, int srcPag if (src[i]) dst[i] = src[i]; } - src += SCREEN_W * _pageScaleFactor[srcPage]; - dst += SCREEN_W * _pageScaleFactor[dstPage]; + src += SCREEN_W; + dst += SCREEN_W; } } } void Screen::copyRegionToBuffer(int pageNum, int x, int y, int w, int h, uint8 *dest) { - x *= _pageScaleFactor[pageNum]; - y *= _pageScaleFactor[pageNum]; - w *= _pageScaleFactor[pageNum]; - h *= _pageScaleFactor[pageNum]; - if (y < 0) { dest += (-y) * w; h += y; y = 0; } else if (y + h > SCREEN_H) { - h = SCREEN_H * _pageScaleFactor[pageNum] - y; + h = SCREEN_H - y; } if (x < 0) { @@ -963,7 +946,7 @@ void Screen::copyRegionToBuffer(int pageNum, int x, int y, int w, int h, uint8 * w += x; x = 0; } else if (x + w > SCREEN_W) { - w = SCREEN_W * _pageScaleFactor[pageNum] - x; + w = SCREEN_W - x; } if (w < 0 || h < 0) @@ -972,17 +955,14 @@ void Screen::copyRegionToBuffer(int pageNum, int x, int y, int w, int h, uint8 * uint8 *pagePtr = getPagePtr(pageNum); for (int i = y; i < y + h; ++i) - memcpy(dest + (i - y) * w, pagePtr + i * SCREEN_W * _pageScaleFactor[pageNum] + x, w); + memcpy(dest + (i - y) * w, pagePtr + i * SCREEN_W + x, w); } void Screen::copyPage(uint8 srcPage, uint8 dstPage) { - // Since we don't (need to) do any actual scaling, we check for compatible pages here - assert(_pageScaleFactor[srcPage] == _pageScaleFactor[dstPage]); - uint8 *src = getPagePtr(srcPage); uint8 *dst = getPagePtr(dstPage); if (src != dst) - memcpy(dst, src, SCREEN_W * _pageScaleFactor[srcPage] * SCREEN_H * _pageScaleFactor[srcPage]); + memcpy(dst, src, SCREEN_W * SCREEN_H); copyOverlayRegion(0, 0, 0, 0, SCREEN_W, SCREEN_H, srcPage, dstPage); if (dstPage == 0 || dstPage == 1) @@ -1009,12 +989,7 @@ void Screen::copyBlockToPage(int pageNum, int x, int y, int w, int h, const uint if (w < 0 || h < 0) return; - x *= _pageScaleFactor[pageNum]; - y *= _pageScaleFactor[pageNum]; - w *= _pageScaleFactor[pageNum]; - h *= _pageScaleFactor[pageNum]; - - uint8 *dst = getPagePtr(pageNum) + y * SCREEN_W * _pageScaleFactor[pageNum] + x; + uint8 *dst = getPagePtr(pageNum) + y * SCREEN_W + x; if (pageNum == 0 || pageNum == 1) addDirtyRect(x, y, w, h); @@ -1023,7 +998,7 @@ void Screen::copyBlockToPage(int pageNum, int x, int y, int w, int h, const uint while (h--) { memcpy(dst, src, w); - dst += SCREEN_W * _pageScaleFactor[pageNum]; + dst += SCREEN_W; src += w; } } @@ -1101,7 +1076,7 @@ void Screen::fillRect(int x1, int y1, int x2, int y2, uint8 color, int pageNum, color |= (color << 4); } else if (_renderMode == Common::kRenderCGA) { color &= 0x03; - } else if (_renderMode == Common::kRenderEGA) { + } else if (_renderMode == Common::kRenderEGA && !_useHiResEGADithering) { color &= 0x0F; } @@ -1178,7 +1153,7 @@ void Screen::drawLine(bool vertical, int x, int y, int length, int color) { color |= (color << 4); } else if (_renderMode == Common::kRenderCGA) { color &= 0x03; - } else if (_renderMode == Common::kRenderEGA) { + } else if (_renderMode == Common::kRenderEGA && !_useHiResEGADithering) { color &= 0x0F; } @@ -1187,7 +1162,7 @@ void Screen::drawLine(bool vertical, int x, int y, int length, int color) { int currLine = 0; while (currLine < length) { *ptr = color; - ptr += SCREEN_W * _pageScaleFactor[_curPage]; + ptr += SCREEN_W; currLine++; } } else { @@ -1233,7 +1208,8 @@ bool Screen::loadFont(FontId fontId, const char *filename) { fnt = new AMIGAFont(); #ifdef ENABLE_EOB else if (_vm->game() == GI_EOB1 || _vm->game() == GI_EOB2) - fnt = new OldDOSFont(_renderMode, _vm->gameFlags().useHiRes); + // We use normal VGA rendering in EOB II, since we do the complete EGA dithering in updateScreen(). + fnt = new OldDOSFont(_useHiResEGADithering ? Common::kRenderVGA : _renderMode); #endif // ENABLE_EOB else fnt = new DOSFont(); @@ -1272,11 +1248,16 @@ int Screen::getCharWidth(uint16 c) const { return width + ((_currentFont != FID_SJIS_FNT) ? _charWidth : 0); } -int Screen::getTextWidth(const char *str) const { +int Screen::getTextWidth(const char *str) { int curLineLen = 0; int maxLineLen = 0; + FontId curFont = _currentFont; + while (1) { + if (_sjisMixedFontMode) + setFont(*str < 0 ? FID_SJIS_FNT : curFont); + uint c = fetchChar(str); if (c == 0) { @@ -1300,7 +1281,7 @@ void Screen::printText(const char *str, int x, int y, uint8 color1, uint8 color2 cmap[1] = color1; setTextColor(cmap, 0, 1); - const uint8 charHeightFnt = getFontHeight(); + FontId curFont = _currentFont; if (x < 0) x = 0; @@ -1314,6 +1295,11 @@ void Screen::printText(const char *str, int x, int y, uint8 color1, uint8 color2 return; while (1) { + if (_sjisMixedFontMode) + setFont(*str < 0 ? FID_SJIS_FNT : curFont); + + uint8 charHeightFnt = getFontHeight(); + uint c = fetchChar(str); if (c == 0) { @@ -1362,9 +1348,6 @@ void Screen::drawChar(uint16 c, int x, int y) { if (x + charWidth > SCREEN_W || y + charHeight > SCREEN_H) return; - x *= _pageScaleFactor[_curPage]; - y *= _pageScaleFactor[_curPage]; - if (useOverlay) { uint8 *destPage = getOverlayPtr(_curPage); if (!destPage) { @@ -1376,11 +1359,11 @@ void Screen::drawChar(uint16 c, int x, int y) { fnt->drawChar(c, destPage, 640); } else { - fnt->drawChar(c, getPagePtr(_curPage) + y * SCREEN_W * _pageScaleFactor[_curPage] + x, SCREEN_W * _pageScaleFactor[_curPage]); + fnt->drawChar(c, getPagePtr(_curPage) + y * SCREEN_W + x, SCREEN_W); } if (_curPage == 0 || _curPage == 1) - addDirtyRect(x, y, charWidth * _pageScaleFactor[_curPage], charHeight * _pageScaleFactor[_curPage]); + addDirtyRect(x, y, charWidth, charHeight); } void Screen::drawShape(uint8 pageNum, const uint8 *shapeData, int x, int y, int sd, int flags, ...) { @@ -1519,7 +1502,7 @@ void Screen::drawShape(uint8 pageNum, const uint8 *shapeData, int x, int y, int int scaleCounterV = 0; - const int drawFunc = flags & 0x0f; + const int drawFunc = flags & 0x0F; _dsProcessMargin = dsMarginFunc[drawFunc]; _dsScaleSkip = dsSkipFunc[drawFunc]; _dsProcessLine = dsLineFunc[drawFunc]; @@ -1776,7 +1759,7 @@ int Screen::drawShapeMarginScaleUpwind(uint8 *&dst, const uint8 *&src, int &cnt) _dsTmpWidth += cnt; int i = (_dsOffscreenLeft - cnt) * _dsScaleW; - int res = i & 0xff; + int res = i & 0xFF; i >>= 8; i -= _dsOffscreenScaleVal2; dst += i; @@ -1802,7 +1785,7 @@ int Screen::drawShapeMarginScaleDownwind(uint8 *&dst, const uint8 *&src, int &cn _dsTmpWidth += cnt; int i = (_dsOffscreenLeft - cnt) * _dsScaleW; - int res = i & 0xff; + int res = i & 0xFF; i >>= 8; i -= _dsOffscreenScaleVal2; dst -= i; @@ -1891,7 +1874,7 @@ void Screen::drawShapeProcessLineScaleUpwind(uint8 *&dst, const uint8 *&src, int int r = c * _dsScaleW + scaleState; dst += (r >> 8); cnt -= (r >> 8); - scaleState = r & 0xff; + scaleState = r & 0xFF; } } else if (scaleState) { (this->*_dsPlot)(dst++, c); @@ -1919,7 +1902,7 @@ void Screen::drawShapeProcessLineScaleDownwind(uint8 *&dst, const uint8 *&src, i int r = c * _dsScaleW + scaleState; dst -= (r >> 8); cnt -= (r >> 8); - scaleState = r & 0xff; + scaleState = r & 0xFF; } } else { (this->*_dsPlot)(dst--, c); @@ -1967,9 +1950,9 @@ void Screen::drawShapePlotType5(uint8 *dst, uint8 cmd) { void Screen::drawShapePlotType6(uint8 *dst, uint8 cmd) { int t = _drawShapeVar4 + _drawShapeVar5; - if (t & 0xff00) { + if (t & 0xFF00) { cmd = dst[_drawShapeVar3]; - t &= 0xff; + t &= 0xFF; } else { cmd = _dsTable2[cmd]; } @@ -1980,7 +1963,7 @@ void Screen::drawShapePlotType6(uint8 *dst, uint8 cmd) { void Screen::drawShapePlotType8(uint8 *dst, uint8 cmd) { uint32 relOffs = dst - _dsDstPage; - int t = (_shapePages[0][relOffs] & 0x7f) & 0x87; + int t = (_shapePages[0][relOffs] & 0x7F) & 0x87; if (_dsDrawLayer < t) cmd = _shapePages[1][relOffs]; @@ -1989,7 +1972,7 @@ void Screen::drawShapePlotType8(uint8 *dst, uint8 cmd) { void Screen::drawShapePlotType9(uint8 *dst, uint8 cmd) { uint32 relOffs = dst - _dsDstPage; - int t = (_shapePages[0][relOffs] & 0x7f) & 0x87; + int t = (_shapePages[0][relOffs] & 0x7F) & 0x87; if (_dsDrawLayer < t) { cmd = _shapePages[1][relOffs]; } else { @@ -2003,7 +1986,7 @@ void Screen::drawShapePlotType9(uint8 *dst, uint8 cmd) { void Screen::drawShapePlotType11_15(uint8 *dst, uint8 cmd) { uint32 relOffs = dst - _dsDstPage; - int t = (_shapePages[0][relOffs] & 0x7f) & 0x87; + int t = (_shapePages[0][relOffs] & 0x7F) & 0x87; if (_dsDrawLayer < t) { cmd = _shapePages[1][relOffs]; @@ -2019,7 +2002,7 @@ void Screen::drawShapePlotType11_15(uint8 *dst, uint8 cmd) { void Screen::drawShapePlotType12(uint8 *dst, uint8 cmd) { uint32 relOffs = dst - _dsDstPage; - int t = (_shapePages[0][relOffs] & 0x7f) & 0x87; + int t = (_shapePages[0][relOffs] & 0x7F) & 0x87; if (_dsDrawLayer < t) { cmd = _shapePages[1][relOffs]; } else { @@ -2031,7 +2014,7 @@ void Screen::drawShapePlotType12(uint8 *dst, uint8 cmd) { void Screen::drawShapePlotType13(uint8 *dst, uint8 cmd) { uint32 relOffs = dst - _dsDstPage; - int t = (_shapePages[0][relOffs] & 0x7f) & 0x87; + int t = (_shapePages[0][relOffs] & 0x7F) & 0x87; if (_dsDrawLayer < t) { cmd = _shapePages[1][relOffs]; } else { @@ -2046,14 +2029,14 @@ void Screen::drawShapePlotType13(uint8 *dst, uint8 cmd) { void Screen::drawShapePlotType14(uint8 *dst, uint8 cmd) { uint32 relOffs = dst - _dsDstPage; - int t = (_shapePages[0][relOffs] & 0x7f) & 0x87; + int t = (_shapePages[0][relOffs] & 0x7F) & 0x87; if (_dsDrawLayer < t) { cmd = _shapePages[1][relOffs]; } else { t = _drawShapeVar4 + _drawShapeVar5; - if (t & 0xff00) { + if (t & 0xFF00) { cmd = dst[_drawShapeVar3]; - t &= 0xff; + t &= 0xFF; } else { cmd = _dsTable2[cmd]; } @@ -2147,7 +2130,7 @@ void Screen::decodeFrame1(const uint8 *src, uint8 *dst, uint32 size) { uint8 nib = 0; uint16 code = decodeEGAGetCode(src, nib); - uint8 last = code & 0xff; + uint8 last = code & 0xFF; uint8 *dstPrev = dst; uint16 count = 1; @@ -2160,7 +2143,7 @@ void Screen::decodeFrame1(const uint8 *src, uint8 *dst, uint32 size) { uint8 cmd = code >> 8; if (cmd--) { - code = (cmd << 8) | (code & 0xff); + code = (cmd << 8) | (code & 0xFF); uint8 *tmpDst = dst; if (code < numPatterns) { @@ -2188,7 +2171,7 @@ void Screen::decodeFrame1(const uint8 *src, uint8 *dst, uint32 size) { count = countPrev; } else { - *dst++ = last = (code & 0xff); + *dst++ = last = (code & 0xFF); if (numPatterns < 3840) { patterns[numPatterns].pos = dstPrev; @@ -2209,7 +2192,7 @@ uint16 Screen::decodeEGAGetCode(const uint8 *&pos, uint8 &nib) { res >>= 4; } else { pos++; - res &= 0xfff; + res &= 0xFFF; } return res; } @@ -2891,8 +2874,6 @@ void Screen::setShapePages(int page1, int page2, int minY, int maxY) { void Screen::setMouseCursor(int x, int y, const byte *shape) { if (!shape) return; - // if mouseDisabled - // return _mouseShape if (_vm->gameFlags().useAltShapeHeader) shape += 2; @@ -3215,7 +3196,7 @@ void Screen::addDirtyRect(int x, int y, int w, int h) { Common::Rect r(x, y, x + w, y + h); // Clip rectangle - r.clip(SCREEN_W * _pageScaleFactor[0], SCREEN_H * _pageScaleFactor[0]); + r.clip(SCREEN_W, SCREEN_H); // If it is empty after clipping, we are done if (r.isEmpty()) @@ -3322,8 +3303,6 @@ void Screen::crossFadeRegion(int x1, int y1, int x2, int y2, int w, int h, int s if (srcPage > 13 || dstPage > 13) error("Screen::crossFadeRegion(): attempting to use temp page as source or dest page."); - assert(_pageScaleFactor[srcPage] == _pageScaleFactor[dstPage]); - hideMouse(); uint16 *wB = (uint16 *)_pagePtrs[14]; @@ -3341,6 +3320,9 @@ void Screen::crossFadeRegion(int x1, int y1, int x2, int y2, int w, int h, int s for (int i = 0; i < h; i++) SWAP(hB[_vm->_rnd.getRandomNumberRng(0, h - 1)], hB[i]); + uint8 *s = _pagePtrs[srcPage]; + uint8 *d = _pagePtrs[dstPage]; + for (int i = 0; i < h; i++) { int iH = i; uint32 end = _system->getMillis() + 3; @@ -3353,7 +3335,8 @@ void Screen::crossFadeRegion(int x1, int y1, int x2, int y2, int w, int h, int s if (++iH >= h) iH = 0; - setPagePixel(dstPage, dX, dY, getPagePixel(srcPage, sX, sY)); + d[dY * 320 + dX] = s[sY * 320 + sX]; + addDirtyRect(dX, dY, 1, 1); } // This tries to speed things up, to get similiar speeds as in DOSBox etc. @@ -3595,11 +3578,11 @@ void AMIGAFont::unload() { memset(_chars, 0, sizeof(_chars)); } -SJISFont::SJISFont(Graphics::FontSJIS *font, const uint8 invisColor, bool is16Color, bool outlineSize) - : _colorMap(0), _font(font), _invisColor(invisColor), _is16Color(is16Color) { +SJISFont::SJISFont(Graphics::FontSJIS *font, const uint8 invisColor, bool is16Color, bool drawOutline, int extraSpacing) + : _colorMap(0), _font(font), _invisColor(invisColor), _is16Color(is16Color), _drawOutline(drawOutline), _sjisWidthOffset(extraSpacing) { assert(_font); - _font->setDrawingMode(outlineSize ? Graphics::FontSJIS::kOutlineMode : Graphics::FontSJIS::kDefaultMode); + _font->setDrawingMode(_drawOutline ? Graphics::FontSJIS::kOutlineMode : Graphics::FontSJIS::kDefaultMode); _sjisWidth = _font->getMaxFontWidth() >> 1; _fontHeight = _font->getFontHeight() >> 1; @@ -3616,14 +3599,14 @@ int SJISFont::getHeight() const { } int SJISFont::getWidth() const { - return _sjisWidth; + return _sjisWidth + _sjisWidthOffset; } int SJISFont::getCharWidth(uint16 c) const { if (c <= 0x7F || (c >= 0xA1 && c <= 0xDF)) return _asciiWidth; else - return _sjisWidth; + return _sjisWidth + _sjisWidthOffset; } void SJISFont::setColorMap(const uint8 *src) { @@ -3633,7 +3616,7 @@ void SJISFont::setColorMap(const uint8 *src) { if (_colorMap[0] == _invisColor) _font->setDrawingMode(Graphics::FontSJIS::kDefaultMode); else - _font->setDrawingMode(Graphics::FontSJIS::kOutlineMode); + _font->setDrawingMode(_drawOutline ? Graphics::FontSJIS::kOutlineMode : Graphics::FontSJIS::kDefaultMode); } } @@ -3672,7 +3655,7 @@ void Palette::loadVGAPalette(Common::ReadStream &stream, int startIndex, int col uint8 *pos = _palData + startIndex * 3; for (int i = 0 ; i < colors * 3; i++) - *pos++ = stream.readByte() & 0x3f; + *pos++ = stream.readByte() & 0x3F; } void Palette::loadEGAPalette(Common::ReadStream &stream, int startIndex, int colors) { diff --git a/engines/kyra/screen.h b/engines/kyra/screen.h index 60bfeb3241..156b5b9a7c 100644 --- a/engines/kyra/screen.h +++ b/engines/kyra/screen.h @@ -146,7 +146,7 @@ private: */ class OldDOSFont : public Font { public: - OldDOSFont(Common::RenderMode mode, bool useHiResEGADithering); + OldDOSFont(Common::RenderMode mode); ~OldDOSFont(); bool load(Common::SeekableReadStream &file); @@ -168,8 +168,6 @@ private: int _numGlyphs; Common::RenderMode _renderMode; - bool _useHiResEGADithering; - bool _useLoResEGA; static uint16 *_cgaDitheringTable; static int _numRef; @@ -213,7 +211,7 @@ private: */ class SJISFont : public Font { public: - SJISFont(Graphics::FontSJIS *font, const uint8 invisColor, bool is16Color, bool outlineSize); + SJISFont(Graphics::FontSJIS *font, const uint8 invisColor, bool is16Color, bool drawOutline, int extraSpacing); ~SJISFont() { unload(); } bool usesOverlay() const { return true; } @@ -232,6 +230,12 @@ private: Graphics::FontSJIS *_font; const uint8 _invisColor; const bool _is16Color; + const bool _drawOutline; + // We use this for cases where the font width returned by getWidth() or getCharWidth() does not match the original. + // The original Japanese game versions use hard coded sjis font widths of 8 or 9. However, this does not necessarily + // depend on whether an outline is used or not (neither LOL/PC-9801 nor LOL/FM-TOWNS use an outline, but the first + // version uses a font width of 8 where the latter uses a font width of 9). + const int _sjisWidthOffset; int _sjisWidth, _asciiWidth; int _fontHeight; @@ -426,12 +430,12 @@ public: void copyBlockToPage(int pageNum, int x, int y, int w, int h, const uint8 *src); void shuffleScreen(int sx, int sy, int w, int h, int srcPage, int dstPage, int ticks, bool transparent); - virtual void fillRect(int x1, int y1, int x2, int y2, uint8 color, int pageNum = -1, bool xored = false); + void fillRect(int x1, int y1, int x2, int y2, uint8 color, int pageNum = -1, bool xored = false); void clearPage(int pageNum); - virtual uint8 getPagePixel(int pageNum, int x, int y); - virtual void setPagePixel(int pageNum, int x, int y, uint8 color); + uint8 getPagePixel(int pageNum, int x, int y); + void setPagePixel(int pageNum, int x, int y, uint8 color); const uint8 *getCPagePtr(int pageNum) const; uint8 *getPageRect(int pageNum, int x, int y, int w, int h); @@ -457,7 +461,7 @@ public: void copyPalette(const int dst, const int src); // gui specific (processing on _curPage) - virtual void drawLine(bool vertical, int x, int y, int length, int color); + void drawLine(bool vertical, int x, int y, int length, int color); void drawClippedLine(int x1, int y1, int x2, int y2, int color); virtual void drawShadedBox(int x1, int y1, int x2, int y2, int color1, int color2); void drawBox(int x1, int y1, int x2, int y2, int color); @@ -470,9 +474,9 @@ public: int getFontWidth() const; int getCharWidth(uint16 c) const; - int getTextWidth(const char *str) const; + int getTextWidth(const char *str); - virtual void printText(const char *str, int x, int y, uint8 color1, uint8 color2); + void printText(const char *str, int x, int y, uint8 color1, uint8 color2); virtual void setTextColorMap(const uint8 *cmap) = 0; void setTextColor(const uint8 *cmap, int a, int b); @@ -549,7 +553,7 @@ public: protected: uint8 *getPagePtr(int pageNum); - void updateDirtyRects(); + virtual void updateDirtyRects(); void updateDirtyRectsAmiga(); void updateDirtyRectsOvl(); @@ -573,18 +577,17 @@ protected: uint8 *_pagePtrs[16]; uint8 *_sjisOverlayPtrs[SCREEN_OVLS_NUM]; - uint8 _pageScaleFactor[SCREEN_PAGE_NUM]; uint8 _pageMapping[SCREEN_PAGE_NUM]; bool _useOverlays; bool _useSJIS; bool _use16ColorMode; bool _useHiResEGADithering; - bool _useLoResEGA; bool _isAmiga; Common::RenderMode _renderMode; uint8 _sjisInvisibleColor; + bool _sjisMixedFontMode; Palette *_screenPalette; Common::Array<Palette *> _palettes; @@ -600,7 +603,7 @@ protected: int _animBlockSize; // dimension handling - const ScreenDim * const _dimTable; + const ScreenDim *const _dimTable; ScreenDim **_customDimTable; const int _dimTableCount; int _curDimIndex; diff --git a/engines/kyra/screen_eob.cpp b/engines/kyra/screen_eob.cpp index e06ca42c40..b1b26357f8 100644 --- a/engines/kyra/screen_eob.cpp +++ b/engines/kyra/screen_eob.cpp @@ -52,10 +52,10 @@ Screen_EoB::Screen_EoB(EoBCoreEngine *vm, OSystem *system) : Screen(vm, system, _cgaScaleTable = 0; _gfxMaxY = 0; _egaDitheringTable = 0; - _egaPixelValueTable = 0; + _egaDitheringTempPage = 0; _cgaMappingDefault = 0; _cgaDitheringTables[0] = _cgaDitheringTables[1] = 0; - _useLoResEGA = _useHiResEGADithering = false; + _useHiResEGADithering = false; } Screen_EoB::~Screen_EoB() { @@ -63,18 +63,12 @@ Screen_EoB::~Screen_EoB() { delete[] _dsTempPage; delete[] _cgaScaleTable; delete[] _egaDitheringTable; - delete[] _egaPixelValueTable; + delete[] _egaDitheringTempPage; delete[] _cgaDitheringTables[0]; delete[] _cgaDitheringTables[1]; } bool Screen_EoB::init() { - // Define hi-res pages for EGA mode in EOB II - if (_vm->gameFlags().useHiRes) { - for (int i = 0; i < 8; i++) - _pageScaleFactor[i] = 2; - } - if (Screen::init()) { int temp; _gfxMaxY = _vm->staticres()->loadRawData(kEoBBaseExpObjectY, temp); @@ -98,13 +92,9 @@ bool Screen_EoB::init() { if (_vm->gameFlags().useHiRes && _renderMode == Common::kRenderEGA) { _useHiResEGADithering = true; _egaDitheringTable = new uint8[256]; - _egaPixelValueTable = new uint8[256]; - for (int i = 0; i < 256; i++) { - _egaDitheringTable[i] = i & 0x0f; - _egaPixelValueTable[i] = i & 0x0f; - } - } else if (_renderMode == Common::kRenderEGA) { - _useLoResEGA = true; + _egaDitheringTempPage = new uint8[SCREEN_W * 2 * SCREEN_H * 2]; + for (int i = 0; i < 256; i++) + _egaDitheringTable[i] = i & 0x0F; } else if (_renderMode == Common::kRenderCGA) { _cgaMappingDefault = _vm->staticres()->loadRawData(kEoB1CgaMappingDefault, temp); _cgaDitheringTables[0] = new uint16[256]; @@ -115,7 +105,7 @@ bool Screen_EoB::init() { _cgaScaleTable = new uint8[256]; memset(_cgaScaleTable, 0, 256 * sizeof(uint8)); for (int i = 0; i < 256; i++) - _cgaScaleTable[i] = ((i & 0xf0) >> 2) | (i & 0x03); + _cgaScaleTable[i] = ((i & 0xF0) >> 2) | (i & 0x03); } return true; @@ -144,14 +134,20 @@ void Screen_EoB::setMouseCursor(int x, int y, const byte *shape, const uint8 *ov int mouseH = (shape[3]); int colorKey = (_renderMode == Common::kRenderCGA) ? 0 : _cursorColorKey; - uint8 *cursor = new uint8[mouseW * _pageScaleFactor[6] * mouseH * _pageScaleFactor[6]]; + int scaleFactor = _useHiResEGADithering ? 2 : 1; + + uint8 *cursor = new uint8[mouseW * scaleFactor * mouseH * scaleFactor]; // We use memset and copyBlockToPage instead of fillRect to make sure that the // color key 0xFF doesn't get converted into EGA color - memset(cursor, colorKey, mouseW * _pageScaleFactor[6] * mouseH * _pageScaleFactor[6]); - copyBlockToPage(6, 0, 0, mouseW, mouseH, cursor); + memset(cursor, colorKey, mouseW * scaleFactor * mouseH * scaleFactor); + copyBlockToPage(6, 0, 0, mouseW * scaleFactor, mouseH * scaleFactor, cursor); drawShape(6, shape, 0, 0, 0, 2, ovl); CursorMan.showMouse(false); - copyRegionToBuffer(6, 0, 0, mouseW, mouseH, cursor); + + if (_useHiResEGADithering) + ditherRect(getCPagePtr(6), cursor, mouseW * scaleFactor, mouseW, mouseH, colorKey); + else + copyRegionToBuffer(6, 0, 0, mouseW, mouseH, cursor); // Mouse cursor post processing for CGA mode. Unlike the original (which uses drawShape for the mouse cursor) // the cursor manager cannot know whether a pixel value of 0 is supposed to be black or transparent. Thus, we @@ -176,7 +172,7 @@ void Screen_EoB::setMouseCursor(int x, int y, const byte *shape, const uint8 *ov } } - CursorMan.replaceCursor(cursor, mouseW * _pageScaleFactor[6], mouseH * _pageScaleFactor[6], x, y, colorKey); + CursorMan.replaceCursor(cursor, mouseW * scaleFactor, mouseH * scaleFactor, x, y, colorKey); if (isMouseVisible()) CursorMan.showMouse(true); delete[] cursor; @@ -192,19 +188,6 @@ void Screen_EoB::loadFileDataToPage(Common::SeekableReadStream *s, int pageNum, s->read(_pagePtrs[pageNum], size); } -void Screen_EoB::printText(const char *str, int x, int y, uint8 color1, uint8 color2) { - if (_useHiResEGADithering) { - // This is sort of an abuse of the text color map. But since EOB doesn't use it anyway - // and the font drawing code needs access to both the original color values and the - // EGA dithering colors we pass them on like this. - uint8 cmap[2]; - cmap[0] = _egaDitheringTable[color2]; - cmap[1] = _egaDitheringTable[color1]; - setTextColor(cmap, 2, 3); - } - Screen::printText(str, x, y, color1, color2); -} - void Screen_EoB::printShadedText(const char *string, int x, int y, int col1, int col2) { printText(string, x - 1, y, 12, col2); printText(string, x, y + 1, 12, 0); @@ -272,32 +255,19 @@ void Screen_EoB::convertPage(int srcPage, int dstPage, const uint8 *cgaMapping) if (cgaMapping) generateCGADitheringTables(cgaMapping); - uint16 *d = (uint16*)dst; + uint16 *d = (uint16 *)dst; uint8 tblSwitch = 0; for (int height = SCREEN_H; height; height--) { const uint16 *table = _cgaDitheringTables[(tblSwitch++) & 1]; for (int width = SCREEN_W / 2; width; width--) { - WRITE_LE_UINT16(d++, table[((src[1] & 0x0f) << 4) | (src[0] & 0x0f)]); + WRITE_LE_UINT16(d++, table[((src[1] & 0x0F) << 4) | (src[0] & 0x0F)]); src += 2; } } - - } else if (_useHiResEGADithering) { - for (int height = SCREEN_H; height; height--) { - uint8 *dst2 = dst + SCREEN_W * 2; - for (int width = SCREEN_W; width; width--) { - uint8 in = _egaDitheringTable[*src++]; - *dst++ = *dst2++ = in >> 4; - *dst++ = *dst2++ = in & 0x0f; - } - dst += (SCREEN_W * 2); - } - - } else if (_renderMode == Common::kRenderEGA) { + } else if (_renderMode == Common::kRenderEGA && !_useHiResEGADithering) { uint32 len = SCREEN_W * SCREEN_H; while (len--) - *dst++ = *src++ & 0x0f; - + *dst++ = *src++ & 0x0F; } else { copyPage(srcPage, dstPage); } @@ -306,111 +276,6 @@ void Screen_EoB::convertPage(int srcPage, int dstPage, const uint8 *cgaMapping) _forceFullUpdate = true; } -void Screen_EoB::fillRect(int x1, int y1, int x2, int y2, uint8 color, int pageNum, bool xored) { - if (!_useHiResEGADithering) { - Screen::fillRect(x1, y1, x2, y2, color, pageNum, xored); - return; - } - - assert(x2 < SCREEN_W && y2 < SCREEN_H); - if (pageNum == -1) - pageNum = _curPage; - - uint16 pitch = (SCREEN_W - (x2 - x1 + 1)) * _pageScaleFactor[pageNum]; - uint8 col1 = (_egaDitheringTable[color] >> 4); - uint8 col2 = (_egaDitheringTable[color] & 0x0f); - - x1 *= _pageScaleFactor[pageNum]; - y1 *= _pageScaleFactor[pageNum]; - x2 *= _pageScaleFactor[pageNum]; - y2 *= _pageScaleFactor[pageNum]; - uint16 w = x2 - x1 + _pageScaleFactor[pageNum]; - uint16 h = y2 - y1 + _pageScaleFactor[pageNum]; - - uint8 *dst = getPagePtr(pageNum) + y1 * SCREEN_W * _pageScaleFactor[pageNum] + x1; - if (pageNum == 0 || pageNum == 1) - addDirtyRect(x1, y1, w, h); - - while (h--) { - for (uint16 w1 = w; w1; w1 -= 2) { - *dst++ = col1; - *dst++ = col2; - } - dst += pitch; - } -} - -void Screen_EoB::drawLine(bool vertical, int x, int y, int length, int color) { - if (!_useHiResEGADithering) { - Screen::drawLine(vertical, x, y, length, color); - return; - } - - uint16 pitch = (SCREEN_W - 1) * _pageScaleFactor[_curPage]; - uint8 col1 = (_egaDitheringTable[color] >> 4); - uint8 col2 = (_egaDitheringTable[color] & 0x0f); - - x *= _pageScaleFactor[_curPage]; - y *= _pageScaleFactor[_curPage]; - length *= _pageScaleFactor[_curPage]; - uint8 *ptr = getPagePtr(_curPage) + y * SCREEN_W * _pageScaleFactor[_curPage] + x; - uint8 *ptr2 = ptr + SCREEN_W * _pageScaleFactor[_curPage]; - - if (vertical) { - assert((y + length) <= SCREEN_H * _pageScaleFactor[_curPage]); - int currLine = 0; - while (currLine < length) { - *ptr++ = col1; - *ptr++ = col2; - ptr += pitch; - currLine++; - } - } else { - assert((x + length) <= SCREEN_W * _pageScaleFactor[_curPage]); - int currLine = 0; - while (currLine < length) { - *ptr++ = *ptr2++ = col1; - *ptr++ = *ptr2++ = col2; - currLine += 2; - } - } - - if (_curPage == 0 || _curPage == 1) - addDirtyRect(x, y, (vertical) ? _pageScaleFactor[_curPage] : length, (vertical) ? length : _pageScaleFactor[_curPage]); -} - -uint8 Screen_EoB::getPagePixel(int pageNum, int x, int y) { - if (!_useHiResEGADithering) - return Screen::getPagePixel(pageNum, x, y); - - x *= _pageScaleFactor[_curPage]; - y *= _pageScaleFactor[_curPage]; - uint8 *pos = &_pagePtrs[pageNum][y * SCREEN_W * _pageScaleFactor[_curPage] + x]; - - return _egaPixelValueTable[(pos[0] << 4) | (pos[1] & 0x0f)]; -} - -void Screen_EoB::setPagePixel(int pageNum, int x, int y, uint8 color) { - if (!_useHiResEGADithering) { - Screen::setPagePixel(pageNum, x, y, color); - return; - } - - assert(pageNum < SCREEN_PAGE_NUM); - assert(x >= 0 && x < SCREEN_W && y >= 0 && y < SCREEN_H); - - x *= _pageScaleFactor[_curPage]; - y *= _pageScaleFactor[_curPage]; - - if (pageNum == 0 || pageNum == 1) - addDirtyRect(x, y, _pageScaleFactor[pageNum], _pageScaleFactor[pageNum]); - - uint8 *pos = &_pagePtrs[pageNum][y * SCREEN_W * _pageScaleFactor[_curPage] + x]; - uint8 *pos2 = pos + SCREEN_W * _pageScaleFactor[_curPage]; - pos[0] = pos2[0] = _egaDitheringTable[color] >> 4; - pos[1] = pos2[1] = _egaDitheringTable[color] & 0x0f; -} - void Screen_EoB::setScreenPalette(const Palette &pal) { if (_useHiResEGADithering && pal.getNumColors() != 16) { generateEGADitheringTable(pal); @@ -444,7 +309,7 @@ uint8 *Screen_EoB::encodeShape(uint16 x, uint16 y, uint16 w, uint16 h, bool enco uint8 *srcLineStart = getPagePtr(_curPage | 1) + y * 320 + (x << 3); uint8 *src = srcLineStart; - if (_useLoResEGA) + if (_renderMode == Common::kRenderEGA && !_useHiResEGADithering) encode8bit = false; if (_renderMode == Common::kRenderCGA) { @@ -456,9 +321,9 @@ uint8 *Screen_EoB::encodeShape(uint16 x, uint16 y, uint16 w, uint16 h, bool enco uint8 *dst = shp; *dst++ = 4; - *dst++ = (h & 0xff); - *dst++ = (w & 0xff); - *dst++ = (h & 0xff); + *dst++ = (h & 0xFF); + *dst++ = (w & 0xFF); + *dst++ = (h & 0xFF); uint8 *dst2 = dst + (h * (w << 1)); @@ -469,8 +334,8 @@ uint8 *Screen_EoB::encodeShape(uint16 x, uint16 y, uint16 w, uint16 h, bool enco const uint16 *table = _cgaDitheringTables[(tblSwitch++) & 1]; while (w1--) { - uint16 p0 = table[((src[1] & 0x0f) << 4) | (src[0] & 0x0f)]; - uint16 p1 = table[((src[3] & 0x0f) << 4) | (src[2] & 0x0f)]; + uint16 p0 = table[((src[1] & 0x0F) << 4) | (src[0] & 0x0F)]; + uint16 p1 = table[((src[3] & 0x0F) << 4) | (src[2] & 0x0F)]; *dst++ = ((p0 & 0x0003) << 6) | ((p0 & 0x0300) >> 4) | ((p1 & 0x0003) << 2) | ((p1 & 0x0300) >> 8); @@ -515,9 +380,9 @@ uint8 *Screen_EoB::encodeShape(uint16 x, uint16 y, uint16 w, uint16 h, bool enco uint8 *dst = shp; *dst++ = 8; - *dst++ = (h & 0xff); - *dst++ = (w & 0xff); - *dst++ = (h & 0xff); + *dst++ = (h & 0xFF); + *dst++ = (w & 0xFF); + *dst++ = (h & 0xFF); srcLineStart = getPagePtr(_curPage | 1) + y * 320 + (x << 3); src = srcLineStart; @@ -540,7 +405,7 @@ uint8 *Screen_EoB::encodeShape(uint16 x, uint16 y, uint16 w, uint16 h, bool enco *dst++ = 0; numZero -= 255; } - val = numZero & 0xff; + val = numZero & 0xFF; } *dst++ = val; } while (src != lineEnd); @@ -555,7 +420,7 @@ uint8 *Screen_EoB::encodeShape(uint16 x, uint16 y, uint16 w, uint16 h, bool enco if (_renderMode != Common::kRenderEGA || _useHiResEGADithering) { colorMap = new uint8[0x100]; - memset(colorMap, 0xff, 0x100); + memset(colorMap, 0xFF, 0x100); } shapesize = h * (w << 2) + 20; @@ -564,15 +429,15 @@ uint8 *Screen_EoB::encodeShape(uint16 x, uint16 y, uint16 w, uint16 h, bool enco uint8 *dst = shp; *dst++ = 2; - *dst++ = (h & 0xff); - *dst++ = (w & 0xff); - *dst++ = (h & 0xff); + *dst++ = (h & 0xFF); + *dst++ = (w & 0xFF); + *dst++ = (h & 0xFF); - if (_useLoResEGA) { + if (_renderMode != Common::kRenderEGA || _useHiResEGADithering) { + memset(dst, 0xFF, 0x10); + } else { for (int i = 0; i < 16; i++) dst[i] = i; - } else { - memset(dst, 0xff, 0x10); } uint8 *pal = dst; @@ -584,10 +449,10 @@ uint8 *Screen_EoB::encodeShape(uint16 x, uint16 y, uint16 w, uint16 h, bool enco uint16 w1 = w << 3; while (w1--) { uint8 s = *src++; - uint8 c = s & 0x0f; + uint8 c = s & 0x0F; if (colorMap) { c = colorMap[s]; - if (c == 0xff) { + if (c == 0xFF) { if (col < 0x10) { *pal++ = s; c = colorMap[s] = col++; @@ -607,7 +472,7 @@ uint8 *Screen_EoB::encodeShape(uint16 x, uint16 y, uint16 w, uint16 h, bool enco srcLineStart += SCREEN_W; src = srcLineStart; } - delete [] colorMap; + delete[] colorMap; } return shp; @@ -632,7 +497,7 @@ void Screen_EoB::drawShape(uint8 pageNum, const uint8 *shapeData, int x, int y, y += _dsY1; } - dst += (_dsX1 << 3) * _pageScaleFactor[pageNum]; + dst += (_dsX1 << 3); int16 dX = x - (_dsX1 << 3); int16 dY = y; int16 dW = _dsX2 - _dsX1; @@ -704,11 +569,11 @@ void Screen_EoB::drawShape(uint8 pageNum, const uint8 *shapeData, int x, int y, marginRight = w2 - marginLeft - width; } - dst += (dY * SCREEN_W * _pageScaleFactor[pageNum] * _pageScaleFactor[pageNum] + dX * _pageScaleFactor[pageNum]); + dst += (dY * SCREEN_W + dX); uint8 *dstL = dst; if (pageNum == 0 || pageNum == 1) - addDirtyRect(rX * _pageScaleFactor[pageNum], rY * _pageScaleFactor[pageNum], rW * _pageScaleFactor[pageNum], rH * _pageScaleFactor[pageNum]); + addDirtyRect(rX, rY, rW, rH); while (dH--) { int16 xpos = (int16) marginLeft; @@ -743,7 +608,7 @@ void Screen_EoB::drawShape(uint8 pageNum, const uint8 *shapeData, int x, int y, } while (xpos > 0); } - dst -= (xpos * _pageScaleFactor[pageNum]); + dst -= xpos; xpos += width; while (xpos > 0) { @@ -752,12 +617,12 @@ void Screen_EoB::drawShape(uint8 pageNum, const uint8 *shapeData, int x, int y, src += pixelStep; if (m) { - drawShapeSetPixel(dst, c, SCREEN_W * _pageScaleFactor[pageNum]); - dst += _pageScaleFactor[pageNum]; + drawShapeSetPixel(dst, c); + dst++; xpos--; } else { uint8 len = (flags & 1) ? src[1] : src[0]; - dst += (len * _pageScaleFactor[pageNum]); + dst += len; xpos -= len; src += pixelStep; } @@ -783,7 +648,7 @@ void Screen_EoB::drawShape(uint8 pageNum, const uint8 *shapeData, int x, int y, } while (xpos > 0); } - dstL += SCREEN_W * _pageScaleFactor[pageNum] * _pageScaleFactor[pageNum]; + dstL += SCREEN_W; dst = dstL; if (flags & 1) src = src2 + 1; @@ -797,7 +662,7 @@ void Screen_EoB::drawShape(uint8 pageNum, const uint8 *shapeData, int x, int y, pal = ovl ? ovl : src; src += 16; } else { - static const uint8 cgaDefOvl[] = { 0x00, 0x55, 0xaa, 0xff }; + static const uint8 cgaDefOvl[] = { 0x00, 0x55, 0xAA, 0xFF }; pal = ovl ? ovl : cgaDefOvl; for (int i = 0; i < 4; i++) cgaPal[i] = pal[i] & 3; @@ -851,12 +716,12 @@ void Screen_EoB::drawShape(uint8 pageNum, const uint8 *shapeData, int x, int y, if (d < width) width = d; - dst += (dY * _pageScaleFactor[pageNum] * SCREEN_W * _pageScaleFactor[pageNum] + dX * _pageScaleFactor[pageNum]); + dst += (dY * SCREEN_W + dX); if (pageNum == 0 || pageNum == 1) - addDirtyRect(rX * _pageScaleFactor[pageNum], rY * _pageScaleFactor[pageNum], rW * _pageScaleFactor[pageNum], rH * _pageScaleFactor[pageNum]); + addDirtyRect(rX, rY, rW, rH); - int pitch = SCREEN_W * _pageScaleFactor[pageNum] * _pageScaleFactor[pageNum] - width * _pageScaleFactor[pageNum]; + int pitch = SCREEN_W - width; int16 lineSrcStep = (w2 - width) / pixelsPerByte; uint8 lineSrcStepRemainder = (w2 - width) % pixelsPerByte; @@ -899,8 +764,8 @@ void Screen_EoB::drawShape(uint8 pageNum, const uint8 *shapeData, int x, int y, } uint8 col = (pixelsPerByte == 2) ? pal[(in >> shift) & pixelPackingMask] : (*dst & ((trans >> shift) & (pixelPackingMask))) | pal[(in >> shift) & pixelPackingMask]; if (col || pixelsPerByte == 4) - drawShapeSetPixel(dst, col, SCREEN_W * _pageScaleFactor[pageNum]); - dst += _pageScaleFactor[pageNum]; + drawShapeSetPixel(dst, col); + dst++; shift = ((shift - (pixelStep * pixelPacking)) & 7); } src += lineSrcStep; @@ -922,7 +787,7 @@ const uint8 *Screen_EoB::scaleShapeStep(const uint8 *shp) { uint8 *dst = (shp != _dsTempPage) ? _dsTempPage : _dsTempPage + 6000; uint8 *d = dst; uint8 pixelsPerByte = *d++ = *shp++; - assert (pixelsPerByte > 1); + assert(pixelsPerByte > 1); uint16 h = shp[0] + 1; d[0] = d[2] = (h << 1) / 3; @@ -952,7 +817,7 @@ const uint8 *Screen_EoB::scaleShapeStep(const uint8 *shp) { else i = -i; - _dsScaleTrans = (i << 4) | (i & 0x0f); + _dsScaleTrans = (i << 4) | (i & 0x0F); for (int ii = 0; ii < 16; ii++) *d++ = *shp++; } @@ -976,7 +841,7 @@ const uint8 *Screen_EoB::scaleShapeStep(const uint8 *shp) { shp += w2; } - return (const uint8*)dst; + return (const uint8 *)dst; } const uint8 *Screen_EoB::generateShapeOverlay(const uint8 *shp, int paletteOverlayIndex) { @@ -1328,14 +1193,14 @@ void Screen_EoB::createFadeTable(uint8 *palData, uint8 *dst, uint8 rootColor, ui for (uint8 i = 1; i; i++) { uint16 tmp = (uint16)((*src - r) * weight) << 1; - tr = *src++ - ((tmp >> 8) & 0xff); + tr = *src++ - ((tmp >> 8) & 0xFF); tmp = (uint16)((*src - g) * weight) << 1; - tg = *src++ - ((tmp >> 8) & 0xff); + tg = *src++ - ((tmp >> 8) & 0xFF); tmp = (uint16)((*src - b) * weight) << 1; - tb = *src++ - ((tmp >> 8) & 0xff); + tb = *src++ - ((tmp >> 8) & 0xFF); uint8 *d = palData + 3; - uint16 v = 0xffff; + uint16 v = 0xFFFF; uint8 col = rootColor; for (uint8 ii = 1; ii; ii++) { @@ -1367,15 +1232,55 @@ const uint8 *Screen_EoB::getEGADitheringTable() { return _egaDitheringTable; } -void Screen_EoB::drawShapeSetPixel(uint8 *dst, uint8 col, uint16 pitch) { +void Screen_EoB::updateDirtyRects() { + if (!_useHiResEGADithering) { + Screen::updateDirtyRects(); + return; + } + + if (_forceFullUpdate) { + ditherRect(getCPagePtr(0), _egaDitheringTempPage, SCREEN_W * 2, SCREEN_W, SCREEN_H); + _system->copyRectToScreen(_egaDitheringTempPage, SCREEN_W * 2, 0, 0, SCREEN_W * 2, SCREEN_H * 2); + } else { + const byte *page0 = getCPagePtr(0); + Common::List<Common::Rect>::iterator it; + for (it = _dirtyRects.begin(); it != _dirtyRects.end(); ++it) { + ditherRect(page0 + it->top * SCREEN_W + it->left, _egaDitheringTempPage, SCREEN_W * 2, it->width(), it->height()); + _system->copyRectToScreen(_egaDitheringTempPage, SCREEN_W * 2, it->left * 2, it->top * 2, it->width() * 2, it->height() * 2); + } + } + _forceFullUpdate = false; + _dirtyRects.clear(); +} + +void Screen_EoB::ditherRect(const uint8 *src, uint8 *dst, int dstPitch, int srcW, int srcH, int colorKey) { + while (srcH--) { + uint8 *dst2 = dst + dstPitch; + for (int i = 0; i < srcW; i++) { + int in = *src++; + if (in != colorKey) { + in = _egaDitheringTable[in]; + *dst++ = *dst2++ = in >> 4; + *dst++ = *dst2++ = in & 0x0F; + } else { + dst[0] = dst[1] = dst2[0] = dst2[1] = colorKey; + dst += 2; + dst2 += 2; + } + } + src += (SCREEN_W - srcW); + dst += ((dstPitch - srcW) * 2); + } +} + +void Screen_EoB::drawShapeSetPixel(uint8 *dst, uint8 col) { if ((_renderMode != Common::kRenderCGA && _renderMode != Common::kRenderEGA) || _useHiResEGADithering) { if (_shapeFadeMode[0]) { if (_shapeFadeMode[1]) { - col = _useHiResEGADithering ? _egaPixelValueTable[(dst[0] << 4) | (dst[1] & 0x0f)] : *dst; + col = *dst; } else { _shapeFadeInternal &= 7; - col = _useHiResEGADithering ? _egaPixelValueTable[(dst[_shapeFadeInternal] << 4) | (dst[_shapeFadeInternal + 1] & 0x0f)] : dst[_shapeFadeInternal]; - _shapeFadeInternal++; + col = *(dst + _shapeFadeInternal++); } } @@ -1386,21 +1291,15 @@ void Screen_EoB::drawShapeSetPixel(uint8 *dst, uint8 col, uint16 pitch) { } } - if (_useHiResEGADithering) { - col = _egaDitheringTable[col]; - dst[0] = dst[pitch] = col >> 4; - dst[1] = dst[pitch + 1] = col & 0x0f; - } else { - *dst = col; - } + *dst = col; } void Screen_EoB::scaleShapeProcessLine2Bit(uint8 *&shpDst, const uint8 *&shpSrc, uint32 transOffsetDst, uint32 transOffsetSrc) { for (int i = 0; i < _dsDiv; i++) { shpDst[0] = (_cgaScaleTable[shpSrc[0]] << 2) | (shpSrc[1] >> 6); - shpDst[1] = ((shpSrc[1] & 0x0f) << 4) | ((shpSrc[2] >> 2) & 0x0f); + shpDst[1] = ((shpSrc[1] & 0x0F) << 4) | ((shpSrc[2] >> 2) & 0x0F); shpDst[transOffsetDst] = (_cgaScaleTable[shpSrc[transOffsetSrc]] << 2) | (shpSrc[transOffsetSrc + 1] >> 6); - shpDst[transOffsetDst + 1] = ((shpSrc[transOffsetSrc + 1] & 0x0f) << 4) | ((shpSrc[transOffsetSrc + 2] >> 2) & 0x0f); + shpDst[transOffsetDst + 1] = ((shpSrc[transOffsetSrc + 1] & 0x0F) << 4) | ((shpSrc[transOffsetSrc + 2] >> 2) & 0x0F); shpSrc += 3; shpDst += 2; } @@ -1409,15 +1308,15 @@ void Screen_EoB::scaleShapeProcessLine2Bit(uint8 *&shpDst, const uint8 *&shpSrc, shpDst[0] = _cgaScaleTable[shpSrc[0]] << 2; shpDst[1] = 0; shpDst[transOffsetDst] = (_cgaScaleTable[shpSrc[transOffsetSrc]] << 2) | 3; - shpDst[transOffsetDst + 1] = 0xff; + shpDst[transOffsetDst + 1] = 0xFF; shpSrc++; shpDst += 2; } else if (_dsRem == 2) { shpDst[0] = (_cgaScaleTable[shpSrc[0]] << 2) | (shpSrc[1] >> 6); - shpDst[1] = (shpSrc[1] & 0x3f) << 2; + shpDst[1] = (shpSrc[1] & 0x3F) << 2; shpDst[transOffsetDst] = (_cgaScaleTable[shpSrc[transOffsetSrc]] << 2) | (shpSrc[transOffsetSrc + 1] >> 6); - shpDst[transOffsetDst + 1] = ((shpSrc[transOffsetSrc + 1] & 0x3f) << 2) | 3; + shpDst[transOffsetDst + 1] = ((shpSrc[transOffsetSrc + 1] & 0x3F) << 2) | 3; shpSrc += 2; shpDst += 2; } @@ -1426,7 +1325,7 @@ void Screen_EoB::scaleShapeProcessLine2Bit(uint8 *&shpDst, const uint8 *&shpSrc, void Screen_EoB::scaleShapeProcessLine4Bit(uint8 *&dst, const uint8 *&src) { for (int i = 0; i < _dsDiv; i++) { *dst++ = *src++; - *dst++ = (READ_BE_UINT16(src) >> 4) & 0xff; + *dst++ = (READ_BE_UINT16(src) >> 4) & 0xFF; src += 2; } @@ -1434,7 +1333,7 @@ void Screen_EoB::scaleShapeProcessLine4Bit(uint8 *&dst, const uint8 *&src) { *dst++ = *src++; *dst++ = _dsScaleTrans; } else if (_dsRem == 2) { - *dst++ = (src[0] & 0xf0) | (src[1] >> 4); + *dst++ = (src[0] & 0xF0) | (src[1] >> 4); src += 2; *dst++ = _dsScaleTrans; *dst++ = _dsScaleTrans; @@ -1463,7 +1362,7 @@ void Screen_EoB::generateEGADitheringTable(const Palette &pal) { for (int ii = 256; ii; ii--) { const uint8 *palEntry = _egaMatchTable + (ii - 1) * 3; - if (*palEntry == 0xff) + if (*palEntry == 0xFF) continue; int e_r = palEntry[0] - r; @@ -1479,16 +1378,12 @@ void Screen_EoB::generateEGADitheringTable(const Palette &pal) { } *dst++ = col; } - - memset(_egaPixelValueTable, 0, 256); - for (int i = 0; i < 256; i++) - _egaPixelValueTable[_egaDitheringTable[i]] = i; } void Screen_EoB::generateCGADitheringTables(const uint8 *mappingData) { for (int i = 0; i < 256; i++) { - _cgaDitheringTables[0][i] = (mappingData[(i >> 4) + 16] << 8) | mappingData[i & 0x0f]; - _cgaDitheringTables[1][i] = (mappingData[i >> 4] << 8) | mappingData[(i & 0x0f) + 16]; + _cgaDitheringTables[0][i] = (mappingData[(i >> 4) + 16] << 8) | mappingData[i & 0x0F]; + _cgaDitheringTables[1][i] = (mappingData[i >> 4] << 8) | mappingData[(i & 0x0F) + 16]; } } @@ -1546,11 +1441,10 @@ const uint8 Screen_EoB::_egaMatchTable[] = { uint16 *OldDOSFont::_cgaDitheringTable = 0; int OldDOSFont::_numRef = 0; -OldDOSFont::OldDOSFont(Common::RenderMode mode, bool useHiResEGADithering) : _renderMode(mode), _useHiResEGADithering(useHiResEGADithering) { +OldDOSFont::OldDOSFont(Common::RenderMode mode) : _renderMode(mode) { _data = 0; _width = _height = _numGlyphs = 0; _bitmapOffsets = 0; - _useLoResEGA = (_renderMode == Common::kRenderEGA && !_useHiResEGADithering); _numRef++; if (!_cgaDitheringTable && _numRef == 1) { @@ -1615,42 +1509,42 @@ void OldDOSFont::drawChar(uint16 c, byte *dst, int pitch) const { if (_width == 6) { switch (c) { case 0x81: - case 0x9a: - c = 0x5d; + case 0x9A: + c = 0x5D; break; case 0x84: - case 0x8e: - c = 0x5b; + case 0x8E: + c = 0x5B; break; case 0x94: case 0x99: c = 0x40; - case 0xe1: + case 0xE1: // TODO: recheck this: no conversion for 'ß' ? break; } } else if (_width == 8) { switch (c) { case 0x81: - case 0x9a: - case 0x5d: - c = 0x1d; + case 0x9A: + case 0x5D: + c = 0x1D; break; case 0x84: - case 0x5b: - c = 0x1e; + case 0x5B: + c = 0x1E; break; case 0x94: case 0x40: - c = 0x1f; + c = 0x1F; break; - case 0x8e: - c = 0x1b; + case 0x8E: + c = 0x1B; break; case 0x99: - c = 0x1c; + c = 0x1C; break; - case 0xe1: + case 0xE1: c = 0x19; break; } @@ -1662,24 +1556,16 @@ void OldDOSFont::drawChar(uint16 c, byte *dst, int pitch) const { int w = (_width - 1) >> 3; pitch -= _width; - if (_useHiResEGADithering) - pitch *= 2; - uint8 color1 = _colorMap[1]; uint8 color2 = _colorMap[0]; - uint8 colEGA11 = _colorMap[3] >> 4; - uint8 colEGA12 = _colorMap[3] & 0x0f; - uint8 colEGA21 = _colorMap[2] >> 4; - uint8 colEGA22 = _colorMap[2] & 0x0f; - static const uint16 cgaColorMask[] = { 0, 0x5555, 0xAAAA, 0xFFFF }; uint16 cgaMask1 = cgaColorMask[color1 & 3]; uint16 cgaMask2 = cgaColorMask[color2 & 3]; - if (_renderMode == Common::kRenderCGA || _useLoResEGA) { - color1 &= 0x0f; - color2 &= 0x0f; + if (_renderMode == Common::kRenderCGA || _renderMode == Common::kRenderEGA) { + color1 &= 0x0F; + color2 &= 0x0F; } int cH = _height; @@ -1710,7 +1596,7 @@ void OldDOSFont::drawChar(uint16 c, byte *dst, int pitch) const { uint8 sh = 6; for (int i = 0; i < _width; i++) { cDst |= ((dst[i] & 3) << sh); - sh = (sh - 2) & 0x0f; + sh = (sh - 2) & 0x0F; } uint16 out = (~(cmp1 | cmp2) & cDst) | (cmp1 & cgaMask1) | (cmp2 & cgaMask2); @@ -1718,7 +1604,7 @@ void OldDOSFont::drawChar(uint16 c, byte *dst, int pitch) const { sh = 6; for (int i = 0; i < _width; i++) { *dst++ = (out >> sh) & 3; - sh = (sh - 2) & 0x0f; + sh = (sh - 2) & 0x0F; } last = s; @@ -1734,27 +1620,13 @@ void OldDOSFont::drawChar(uint16 c, byte *dst, int pitch) const { break; } - if (_useHiResEGADithering) { - if (s & i) { - if (color1) { - dst[0] = dst2[0] = colEGA11; - dst[1] = dst2[1] = colEGA12; - } - } else if (color2) { - dst[0] = dst2[0] = colEGA21; - dst[1] = dst2[1] = colEGA22; - } - dst += 2; - dst2 += 2; - } else { - if (s & i) { - if (color1) - *dst = color1; - } else if (color2) { - *dst = color2; - } - dst++; + if (s & i) { + if (color1) + *dst = color1; + } else if (color2) { + *dst = color2; } + dst++; } if (cW) diff --git a/engines/kyra/screen_eob.h b/engines/kyra/screen_eob.h index fc40cfe903..934483d146 100644 --- a/engines/kyra/screen_eob.h +++ b/engines/kyra/screen_eob.h @@ -45,7 +45,6 @@ public: void loadFileDataToPage(Common::SeekableReadStream *s, int pageNum, uint32 size); - void printText(const char *str, int x, int y, uint8 color1, uint8 color2); void printShadedText(const char *string, int x, int y, int col1, int col2); void loadEoBBitmap(const char *file, const uint8 *cgaMapping, int tempPage, int destPage, int convertToPage); @@ -53,11 +52,6 @@ public: void convertPage(int srcPage, int dstPage, const uint8 *cgaMapping); - void fillRect(int x1, int y1, int x2, int y2, uint8 color, int pageNum = -1, bool xored = false); - void drawLine(bool vertical, int x, int y, int length, int color); - uint8 getPagePixel(int pageNum, int x, int y); - void setPagePixel(int pageNum, int x, int y, uint8 color); - void setScreenPalette(const Palette &pal); void getRealPalette(int num, uint8 *dst); @@ -68,7 +62,7 @@ public: const uint8 *generateShapeOverlay(const uint8 *shp, int paletteOverlayIndex); void setShapeFrame(int x1, int y1, int x2, int y2); - void setShapeFadeMode (uint8 i, bool b); + void setShapeFadeMode(uint8 i, bool b); void setGfxParameters(int x, int y, int col); void drawExplosion(int scale, int radius, int numElements, int stepSize, int aspectRatio, const uint8 *colorTable, int colorTableSize); @@ -88,7 +82,10 @@ public: const uint8 *getEGADitheringTable(); private: - void drawShapeSetPixel(uint8 *dst, uint8 col, uint16 pitch); + void updateDirtyRects(); + void ditherRect(const uint8 *src, uint8 *dst, int dstPitch, int srcW, int srcH, int colorKey = -1); + + void drawShapeSetPixel(uint8 *dst, uint8 col); void scaleShapeProcessLine2Bit(uint8 *&shpDst, const uint8 *&shpSrc, uint32 transOffsetDst, uint32 transOffsetSrc); void scaleShapeProcessLine4Bit(uint8 *&dst, const uint8 *&src); bool posWithinRect(int posX, int posY, int x1, int y1, int x2, int y2); @@ -115,14 +112,14 @@ private: const uint8 *_cgaMappingDefault; uint8 *_egaDitheringTable; - uint8 *_egaPixelValueTable; + uint8 *_egaDitheringTempPage; static const uint8 _egaMatchTable[]; static const ScreenDim _screenDimTable[]; static const int _screenDimTableCount; }; -} // End of namespace Kyra +} // End of namespace Kyra #endif // ENABLE_EOB diff --git a/engines/kyra/screen_hof.cpp b/engines/kyra/screen_hof.cpp index ac6ee5eb77..b7de7988b8 100644 --- a/engines/kyra/screen_hof.cpp +++ b/engines/kyra/screen_hof.cpp @@ -34,16 +34,16 @@ void Screen_HoF::generateGrayOverlay(const Palette &srcPal, uint8 *grayOverlay, for (int i = 0; i != lastColor; i++) { if (flag) { - int v = ((((srcPal[3 * i] & 0x3f) + (srcPal[3 * i + 1] & 0x3f) - + (srcPal[3 * i + 2] & 0x3f)) / 3) * factor) / 0x40; - tmpPal[3 * i] = tmpPal[3 * i + 1] = tmpPal[3 * i + 2] = v & 0xff; + int v = ((((srcPal[3 * i] & 0x3F) + (srcPal[3 * i + 1] & 0x3F) + + (srcPal[3 * i + 2] & 0x3F)) / 3) * factor) / 0x40; + tmpPal[3 * i] = tmpPal[3 * i + 1] = tmpPal[3 * i + 2] = v & 0xFF; } else { - int v = (((srcPal[3 * i] & 0x3f) * factor) / 0x40) + addR; - tmpPal[3 * i] = (v > 0x3f) ? 0x3f : v & 0xff; - v = (((srcPal[3 * i + 1] & 0x3f) * factor) / 0x40) + addG; - tmpPal[3 * i + 1] = (v > 0x3f) ? 0x3f : v & 0xff; - v = (((srcPal[3 * i + 2] & 0x3f) * factor) / 0x40) + addB; - tmpPal[3 * i + 2] = (v > 0x3f) ? 0x3f : v & 0xff; + int v = (((srcPal[3 * i] & 0x3F) * factor) / 0x40) + addR; + tmpPal[3 * i] = (v > 0x3F) ? 0x3F : v & 0xFF; + v = (((srcPal[3 * i + 1] & 0x3F) * factor) / 0x40) + addG; + tmpPal[3 * i + 1] = (v > 0x3F) ? 0x3F : v & 0xFF; + v = (((srcPal[3 * i + 2] & 0x3F) * factor) / 0x40) + addB; + tmpPal[3 * i + 2] = (v > 0x3F) ? 0x3F : v & 0xFF; } } @@ -93,46 +93,4 @@ void Screen_HoF::cmpFadeFrameStep(int srcPage, int srcW, int srcH, int srcX, int } } -void Screen_HoF::copyPageMemory(int srcPage, int srcPos, int dstPage, int dstPos, int numBytes) { - const uint8 *src = getPagePtr(srcPage) + srcPos; - uint8 *dst = getPagePtr(dstPage) + dstPos; - memcpy(dst, src, numBytes); -} - -void Screen_HoF::copyRegionEx(int srcPage, int srcW, int srcH, int dstPage, int dstX, int dstY, int dstW, int dstH, const ScreenDim *dim, bool flag) { - int x0 = dim->sx << 3; - int y0 = dim->sy; - int w0 = dim->w << 3; - int h0 = dim->h; - - int x1 = dstX; - int y1 = dstY; - int w1 = dstW; - int h1 = dstH; - - int x2, y2, w2; - - calcBounds(w0, h0, x1, y1, w1, h1, x2, y2, w2); - - const uint8 *src = getPagePtr(srcPage) + (320 * srcH) + srcW; - uint8 *dst = getPagePtr(dstPage) + 320 * (y0 + y1); - - for (int y = 0; y < h1; y++) { - const uint8 *s = src + x2; - uint8 *d = dst + x0 + x1; - - if (flag) - d += (h1 >> 1); - - for (int x = 0; x < w1; x++) { - if (*s) - *d = *s; - s++; - d++; - } - dst += 320; - src += 320; - } -} - } // End of namespace Kyra diff --git a/engines/kyra/screen_hof.h b/engines/kyra/screen_hof.h index 51c6a001fa..95f461677d 100644 --- a/engines/kyra/screen_hof.h +++ b/engines/kyra/screen_hof.h @@ -37,8 +37,7 @@ public: // sequence player void generateGrayOverlay(const Palette &pal, uint8 *grayOverlay, int factor, int addR, int addG, int addB, int lastColor, bool flag); void cmpFadeFrameStep(int srcPage, int srcW, int srcH, int srcX, int srcY, int dstPage, int dstW, int dstH, int dstX, int dstY, int cmpW, int cmpH, int cmpPage); - void copyPageMemory(int srcPage, int srcPos, int dstPage, int dstPos, int numBytes); - void copyRegionEx(int srcPage, int srcW, int srcH, int dstPage, int dstX,int dstY, int dstW, int dstH, const ScreenDim *d, bool flag = false); + private: KyraEngine_HoF *_vm; diff --git a/engines/kyra/screen_lol.cpp b/engines/kyra/screen_lol.cpp index 3726b1f4b9..16a77c8fcb 100644 --- a/engines/kyra/screen_lol.cpp +++ b/engines/kyra/screen_lol.cpp @@ -133,12 +133,12 @@ void Screen_LoL::generateGrayOverlay(const Palette &srcPal, uint8 *grayOverlay, Palette tmpPal(lastColor); for (int i = 0; i != lastColor; i++) { - int v = (((srcPal[3 * i] & 0x3f) * factor) / 0x40) + addR; - tmpPal[3 * i] = (v > 0x3f) ? 0x3f : v & 0xff; - v = (((srcPal[3 * i + 1] & 0x3f) * factor) / 0x40) + addG; - tmpPal[3 * i + 1] = (v > 0x3f) ? 0x3f : v & 0xff; - v = (((srcPal[3 * i + 2] & 0x3f) * factor) / 0x40) + addB; - tmpPal[3 * i + 2] = (v > 0x3f) ? 0x3f : v & 0xff; + int v = (((srcPal[3 * i] & 0x3F) * factor) / 0x40) + addR; + tmpPal[3 * i] = (v > 0x3F) ? 0x3F : v & 0xFF; + v = (((srcPal[3 * i + 1] & 0x3F) * factor) / 0x40) + addG; + tmpPal[3 * i + 1] = (v > 0x3F) ? 0x3F : v & 0xFF; + v = (((srcPal[3 * i + 2] & 0x3F) * factor) / 0x40) + addB; + tmpPal[3 * i + 2] = (v > 0x3F) ? 0x3F : v & 0xFF; } for (int i = 0; i < lastColor; i++) @@ -149,7 +149,7 @@ void Screen_LoL::createTransparencyTablesIntern(const uint8 *ovl, int a, const u Palette screenPal(256); screenPal.copy(fxPal2, 0, 256); - memset(outTable1, 0xff, 256); + memset(outTable1, 0xFF, 256); for (int i = 0; i < a; i++) outTable1[ovl[i]] = i; @@ -233,8 +233,6 @@ void Screen_LoL::drawGridBox(int x, int y, int w, int h, int col) { *(p + tmp) = col; p += 2; } - } else { - w = 1; } if (s == 1) { @@ -270,7 +268,7 @@ void Screen_LoL::fadeClearSceneWindow(int delay) { void Screen_LoL::backupSceneWindow(int srcPageNum, int dstPageNum) { uint8 *src = getPagePtr(srcPageNum) + 112; - uint8 *dst = getPagePtr(dstPageNum) + 0xa500; + uint8 *dst = getPagePtr(dstPageNum) + 0xA500; for (int h = 0; h < 120; h++) { for (int w = 0; w < 176; w++) @@ -280,7 +278,7 @@ void Screen_LoL::backupSceneWindow(int srcPageNum, int dstPageNum) { } void Screen_LoL::restoreSceneWindow(int srcPageNum, int dstPageNum) { - uint8 *src = getPagePtr(srcPageNum) + 0xa500; + uint8 *src = getPagePtr(srcPageNum) + 0xA500; uint8 *dst = getPagePtr(dstPageNum) + 112; for (int h = 0; h < 120; h++) { @@ -294,7 +292,7 @@ void Screen_LoL::restoreSceneWindow(int srcPageNum, int dstPageNum) { } void Screen_LoL::clearGuiShapeMemory(int pageNum) { - uint8 *dst = getPagePtr(pageNum) + 0x79b0; + uint8 *dst = getPagePtr(pageNum) + 0x79B0; for (int i = 0; i < 23; i++) { memset(dst, 0, 176); dst += 320; @@ -302,7 +300,7 @@ void Screen_LoL::clearGuiShapeMemory(int pageNum) { } void Screen_LoL::copyGuiShapeFromSceneBackupBuffer(int srcPageNum, int dstPageNum) { - uint8 *src = getPagePtr(srcPageNum) + 0x79c3; + uint8 *src = getPagePtr(srcPageNum) + 0x79C3; uint8 *dst = getPagePtr(dstPageNum); for (int i = 0; i < 23; i++) { @@ -325,7 +323,7 @@ void Screen_LoL::copyGuiShapeFromSceneBackupBuffer(int srcPageNum, int dstPageN void Screen_LoL::copyGuiShapeToSurface(int srcPageNum, int dstPageNum) { uint8 *src = getPagePtr(srcPageNum); - uint8 *dst = getPagePtr(dstPageNum) + 0xe7c3; + uint8 *dst = getPagePtr(dstPageNum) + 0xE7C3; for (int i = 0; i < 23; i++) { uint8 v = *src++; @@ -344,8 +342,8 @@ void Screen_LoL::copyGuiShapeToSurface(int srcPageNum, int dstPageNum) { } void Screen_LoL::smoothScrollZoomStepTop(int srcPageNum, int dstPageNum, int x, int y) { - uint8 *src = getPagePtr(srcPageNum) + 0xa500 + y * 176 + x; - uint8 *dst = getPagePtr(dstPageNum) + 0xa500; + uint8 *src = getPagePtr(srcPageNum) + 0xA500 + y * 176 + x; + uint8 *dst = getPagePtr(dstPageNum) + 0xA500; x <<= 1; uint16 width = 176 - x; @@ -365,7 +363,7 @@ void Screen_LoL::smoothScrollZoomStepTop(int srcPageNum, int dstPageNum, int x, do { scaleXc += scaleX; int numbytes = cntW + (scaleXc >> 16); - scaleXc &= 0xffff; + scaleXc &= 0xFFFF; memset(dst, *src++, numbytes); dst += numbytes; } while (--widthCnt); @@ -387,8 +385,8 @@ void Screen_LoL::smoothScrollZoomStepTop(int srcPageNum, int dstPageNum, int x, } void Screen_LoL::smoothScrollZoomStepBottom(int srcPageNum, int dstPageNum, int x, int y) { - uint8 *src = getPagePtr(srcPageNum) + 0xc4a0 + x; - uint8 *dst = getPagePtr(dstPageNum) + 0xc4a0; + uint8 *src = getPagePtr(srcPageNum) + 0xC4A0 + x; + uint8 *dst = getPagePtr(dstPageNum) + 0xC4A0; x <<= 1; uint16 width = 176 - x; @@ -408,7 +406,7 @@ void Screen_LoL::smoothScrollZoomStepBottom(int srcPageNum, int dstPageNum, int do { scaleXc += scaleX; int numbytes = cntW + (scaleXc >> 16); - scaleXc &= 0xffff; + scaleXc &= 0xFFFF; memset(dst, *src++, numbytes); dst += numbytes; } while (--widthCnt); @@ -455,7 +453,7 @@ void Screen_LoL::smoothScrollHorizontalStep(int pageNum, int srcX, int dstX, int void Screen_LoL::smoothScrollTurnStep1(int srcPage1Num, int srcPage2Num, int dstPageNum) { uint8 *s = getPagePtr(srcPage1Num) + 273; - uint8 *d = getPagePtr(dstPageNum) + 0xa500; + uint8 *d = getPagePtr(dstPageNum) + 0xA500; for (int i = 0; i < 120; i++) { uint8 a = *s++; @@ -474,7 +472,7 @@ void Screen_LoL::smoothScrollTurnStep1(int srcPage1Num, int srcPage2Num, int dst } s = getPagePtr(srcPage2Num) + 112; - d = getPagePtr(dstPageNum) + 0xa52c; + d = getPagePtr(dstPageNum) + 0xA52C; for (int i = 0; i < 120; i++) { for (int ii = 0; ii < 33; ii++) { @@ -492,7 +490,7 @@ void Screen_LoL::smoothScrollTurnStep1(int srcPage1Num, int srcPage2Num, int dst void Screen_LoL::smoothScrollTurnStep2(int srcPage1Num, int srcPage2Num, int dstPageNum) { uint8 *s = getPagePtr(srcPage1Num) + 244; - uint8 *d = getPagePtr(dstPageNum) + 0xa500; + uint8 *d = getPagePtr(dstPageNum) + 0xA500; for (int k = 0; k < 2; k++) { for (int i = 0; i < 120; i++) { @@ -507,13 +505,13 @@ void Screen_LoL::smoothScrollTurnStep2(int srcPage1Num, int srcPage2Num, int dst } s = getPagePtr(srcPage2Num) + 112; - d = getPagePtr(dstPageNum) + 0xa558; + d = getPagePtr(dstPageNum) + 0xA558; } } void Screen_LoL::smoothScrollTurnStep3(int srcPage1Num, int srcPage2Num, int dstPageNum) { uint8 *s = getPagePtr(srcPage1Num) + 189; - uint8 *d = getPagePtr(dstPageNum) + 0xa500; + uint8 *d = getPagePtr(dstPageNum) + 0xA500; for (int i = 0; i < 120; i++) { for (int ii = 0; ii < 33; ii++) { @@ -529,7 +527,7 @@ void Screen_LoL::smoothScrollTurnStep3(int srcPage1Num, int srcPage2Num, int dst } s = getPagePtr(srcPage2Num) + 112; - d = getPagePtr(dstPageNum) + 0xa584; + d = getPagePtr(dstPageNum) + 0xA584; for (int i = 0; i < 120; i++) { for (int ii = 0; ii < 14; ii++) { @@ -783,8 +781,8 @@ bool Screen_LoL::fadeColor(int dstColorIndex, int srcColorIndex, uint32 elapsedT for (int i = 0; i < 3; i++) { if (elapsedTicks < totalTicks) { - srcV = *src & 0x3f; - dstV = *dst & 0x3f; + srcV = *src & 0x3F; + dstV = *dst & 0x3F; outV = srcV - dstV; if (outV) @@ -796,7 +794,7 @@ bool Screen_LoL::fadeColor(int dstColorIndex, int srcColorIndex, uint32 elapsedT res = false; } - tmpPalEntry[i] = outV & 0xff; + tmpPalEntry[i] = outV & 0xFF; src++; dst++; p++; @@ -810,34 +808,6 @@ bool Screen_LoL::fadeColor(int dstColorIndex, int srcColorIndex, uint32 elapsedT return res; } -bool Screen_LoL::fadePaletteStep(uint8 *pal1, uint8 *pal2, uint32 elapsedTime, uint32 targetTime) { - Palette &p1 = getPalette(1); - - bool res = false; - for (int i = 0; i < p1.getNumColors() * 3; i++) { - uint8 out = 0; - - if (elapsedTime < targetTime) { - int32 d = ((pal2[i] & 0x3f) - (pal1[i] & 0x3f)); - if (d) - res = true; - - int32 val = ((((d << 8) / (int32)targetTime) * (int32)elapsedTime) >> 8); - out = ((pal1[i] & 0x3f) + (int8)val); - } else { - out = p1[i] = (pal2[i] & 0x3f); - res = false; - } - - (*_internFadePalette)[i] = out; - } - - setScreenPalette(*_internFadePalette); - updateScreen(); - - return res; -} - Palette **Screen_LoL::generateFadeTable(Palette **dst, Palette *src1, Palette *src2, int numTabs) { int len = _use16ColorMode ? 48 : 768; if (!src1) diff --git a/engines/kyra/screen_lol.h b/engines/kyra/screen_lol.h index 09496705bb..8ceb8431bc 100644 --- a/engines/kyra/screen_lol.h +++ b/engines/kyra/screen_lol.h @@ -65,7 +65,6 @@ public: void loadSpecialColors(Palette &dst); void copyColor(int dstColorIndex, int srcColorIndex); bool fadeColor(int dstColorIndex, int srcColorIndex, uint32 elapsedTicks, uint32 totalTicks); - bool fadePaletteStep(uint8 *pal1, uint8 *pal2, uint32 elapsedTime, uint32 targetTime); Palette **generateFadeTable(Palette **dst, Palette *src1, Palette *src2, int numTabs); void generateGrayOverlay(const Palette &Pal, uint8 *grayOverlay, int factor, int addR, int addG, int addB, int lastColor, bool skipSpecialColors); diff --git a/engines/kyra/screen_v2.cpp b/engines/kyra/screen_v2.cpp index 7d4b064e2a..cc7d526ffe 100644 --- a/engines/kyra/screen_v2.cpp +++ b/engines/kyra/screen_v2.cpp @@ -117,11 +117,11 @@ void Screen_v2::applyOverlay(int x, int y, int w, int h, int pageNum, const uint } int Screen_v2::findLeastDifferentColor(const uint8 *paletteEntry, const Palette &pal, uint8 firstColor, uint16 numColors, bool skipSpecialColors) { - int m = 0x7fff; + int m = 0x7FFF; int r = 0x101; for (int i = 0; i < numColors; i++) { - if (skipSpecialColors && i >= 0xc0 && i <= 0xc3) + if (skipSpecialColors && i >= 0xC0 && i <= 0xC3) continue; int v = paletteEntry[0] - pal[(i + firstColor) * 3 + 0]; @@ -162,6 +162,34 @@ void Screen_v2::getFadeParams(const Palette &pal, int delay, int &delayInc, int } } +bool Screen_v2::timedPaletteFadeStep(uint8 *pal1, uint8 *pal2, uint32 elapsedTime, uint32 totalTime) { + Palette &p1 = getPalette(1); + + bool res = false; + for (int i = 0; i < p1.getNumColors() * 3; i++) { + uint8 out = 0; + + if (elapsedTime < totalTime) { + int32 d = ((pal2[i] & 0x3F) - (pal1[i] & 0x3F)); + if (d) + res = true; + + int32 val = ((((d << 8) / (int32)totalTime) * (int32)elapsedTime) >> 8); + out = ((pal1[i] & 0x3F) + (int8)val); + } else { + out = p1[i] = (pal2[i] & 0x3F); + res = false; + } + + (*_internFadePalette)[i] = out; + } + + setScreenPalette(*_internFadePalette); + updateScreen(); + + return res; +} + const uint8 *Screen_v2::getPtrToShape(const uint8 *shpFile, int shape) { uint16 shapes = READ_LE_UINT16(shpFile); @@ -283,13 +311,13 @@ void Screen_v2::wsaFrameAnimationStep(int x1, int y1, int x2, int y2, if (w1 == 1) { memset(dt, *s, w2); } else { - t = ((((((w2 - w1 + 1) & 0xffff) << 8) / w1) + 0x100) & 0xffff) << 8; + t = ((((((w2 - w1 + 1) & 0xFFFF) << 8) / w1) + 0x100) & 0xFFFF) << 8; int bp = 0; for (int i = 0; i < w1; i++) { int cnt = (t >> 16); - bp += (t & 0xffff); - if (bp > 0xffff) { - bp -= 0xffff; + bp += (t & 0xFFFF); + if (bp > 0xFFFF) { + bp -= 0xFFFF; cnt++; } memset(dt, *s++, cnt); @@ -300,13 +328,13 @@ void Screen_v2::wsaFrameAnimationStep(int x1, int y1, int x2, int y2, if (w2 == 1) { *dt = *s; } else { - t = (((((w1 - w2) & 0xffff) << 8) / w2) & 0xffff) << 8; + t = (((((w1 - w2) & 0xFFFF) << 8) / w2) & 0xFFFF) << 8; int bp = 0; for (int i = 0; i < w2; i++) { *dt++ = *s++; - bp += (t & 0xffff); - if (bp > 0xffff) { - bp -= 0xffff; + bp += (t & 0xFFFF); + if (bp > 0xFFFF) { + bp -= 0xFFFF; s++; } s += (t >> 16); @@ -322,6 +350,48 @@ void Screen_v2::wsaFrameAnimationStep(int x1, int y1, int x2, int y2, addDirtyRect(x2, y2, w2, h2); } +void Screen_v2::copyPageMemory(int srcPage, int srcPos, int dstPage, int dstPos, int numBytes) { + const uint8 *src = getPagePtr(srcPage) + srcPos; + uint8 *dst = getPagePtr(dstPage) + dstPos; + memcpy(dst, src, numBytes); +} + +void Screen_v2::copyRegionEx(int srcPage, int srcW, int srcH, int dstPage, int dstX, int dstY, int dstW, int dstH, const ScreenDim *dim, bool flag) { + int x0 = dim->sx << 3; + int y0 = dim->sy; + int w0 = dim->w << 3; + int h0 = dim->h; + + int x1 = dstX; + int y1 = dstY; + int w1 = dstW; + int h1 = dstH; + + int x2, y2, w2; + + calcBounds(w0, h0, x1, y1, w1, h1, x2, y2, w2); + + const uint8 *src = getPagePtr(srcPage) + (320 * srcH) + srcW; + uint8 *dst = getPagePtr(dstPage) + 320 * (y0 + y1); + + for (int y = 0; y < h1; y++) { + const uint8 *s = src + x2; + uint8 *d = dst + x0 + x1; + + if (flag) + d += (h1 >> 1); + + for (int x = 0; x < w1; x++) { + if (*s) + *d = *s; + s++; + d++; + } + dst += 320; + src += 320; + } +} + bool Screen_v2::calcBounds(int w0, int h0, int &x1, int &y1, int &w1, int &h1, int &x2, int &y2, int &w2) { x2 = 0; y2 = 0; diff --git a/engines/kyra/screen_v2.h b/engines/kyra/screen_v2.h index f84c923128..6f4d67136a 100644 --- a/engines/kyra/screen_v2.h +++ b/engines/kyra/screen_v2.h @@ -43,6 +43,8 @@ public: virtual void getFadeParams(const Palette &pal, int delay, int &delayInc, int &diff); + bool timedPaletteFadeStep(uint8 *pal1, uint8 *pal2, uint32 elapsedTime, uint32 totalTime); + // shape handling uint8 *getPtrToShape(uint8 *shpFile, int shape); const uint8 *getPtrToShape(const uint8 *shpFile, int shape); @@ -66,6 +68,10 @@ public: // special WSA handling void wsaFrameAnimationStep(int x1, int y1, int x2, int y2, int w1, int h1, int w2, int h2, int srcPage, int dstPage, int dim); + + // used in non-interactive HoF/LoL demos + void copyPageMemory(int srcPage, int srcPos, int dstPage, int dstPos, int numBytes); + void copyRegionEx(int srcPage, int srcW, int srcH, int dstPage, int dstX,int dstY, int dstW, int dstH, const ScreenDim *d, bool flag = false); protected: uint8 *_wsaFrameAnimBuffer; }; diff --git a/engines/kyra/script_eob.cpp b/engines/kyra/script_eob.cpp index c07c41f706..e5ccbf2c2e 100644 --- a/engines/kyra/script_eob.cpp +++ b/engines/kyra/script_eob.cpp @@ -175,7 +175,7 @@ void EoBInfProcessor::run(int func, int flags) { uint16 f = _vm->_levelBlockProperties[func].flags; - uint16 subFlags = ((f & 0xfff8) >> 3) | 0xe0; + uint16 subFlags = ((f & 0xFFF8) >> 3) | 0xE0; if (!(flags & subFlags)) return; @@ -233,7 +233,7 @@ void EoBInfProcessor::reset() { } const char *EoBInfProcessor::getString(uint16 index) { - if (index == 0xffff) + if (index == 0xFFFF) return 0; int8 *res = _scriptData + READ_LE_UINT16(_scriptData); @@ -348,7 +348,7 @@ int EoBInfProcessor::oeob_movePartyOrObject(int8 *data) { int8 *pos = data; int8 a = *pos++; - uint16 b = 0xffff; + uint16 b = 0xFFFF; uint16 c = 0; uint16 d = 0; @@ -972,7 +972,7 @@ int EoBInfProcessor::oeob_eval_v2(int8 *data) { case 9: switch (*pos++) { case -36: - _stack[_stackIndex++] = _vm->_itemTypes[_vm->_items[_vm->_lastUsedItem].type].extraProperties & 0x7f; + _stack[_stackIndex++] = _vm->_itemTypes[_vm->_items[_vm->_lastUsedItem].type].extraProperties & 0x7F; break; case -31: _stack[_stackIndex++] = _vm->_items[_vm->_lastUsedItem].type; @@ -1097,7 +1097,7 @@ int EoBInfProcessor::oeob_eval_v2(int8 *data) { case 26: a = 0; for (i = 0; i < 6; i++) { - if (_vm->testCharacter(i, 0x0f)) + if (_vm->testCharacter(i, 0x0F)) a++; } _stack[_stackIndex++] = a; @@ -1260,7 +1260,7 @@ int EoBInfProcessor::oeob_loadNewLevelOrMonsters(int8 *data) { pos += 2; uint8 dir = (uint8)*pos++; - if (dir != 0xff) + if (dir != 0xFF) _vm->_currentDirection = dir; for (int i = 0; i < 30; i++) @@ -1328,11 +1328,11 @@ int EoBInfProcessor::oeob_createItem_v1(int8 *data) { uint8 itmPos = *pos++; if (itm) { - if (block == 0xffff && !_vm->_itemInHand) { + if (block == 0xFFFF && !_vm->_itemInHand) { _vm->setHandItem(itm); debugC(5, kDebugLevelScript, " - create hand item '%d'", itm); - } else if (block != 0xffff) { - _vm->setItemPosition((Item *)&_vm->_levelBlockProperties[block & 0x3ff].drawObjects, block, itm, itmPos); + } else if (block != 0xFFFF) { + _vm->setItemPosition((Item *)&_vm->_levelBlockProperties[block & 0x3FF].drawObjects, block, itm, itmPos); debugC(5, kDebugLevelScript, " - create item '%d' on block '0x%.04X', position '%d'", itm, block, itmPos); } } @@ -1363,19 +1363,19 @@ int EoBInfProcessor::oeob_createItem_v2(int8 *data) { if (!itm) return pos - data; - if (block == 0xffff) { + if (block == 0xFFFF) { if (!_vm->_itemInHand) { _vm->setHandItem(itm); debugC(5, kDebugLevelScript, " - create hand item '%d' (value '%d', flags '0x%X', icon number '%d')", itm, _vm->_items[itm].value, _vm->_items[itm].flags, _vm->_items[itm].icon); } else { - _vm->setItemPosition((Item *)&_vm->_levelBlockProperties[_vm->_currentBlock & 0x3ff].drawObjects, _vm->_currentBlock, itm, _itemPos[_vm->rollDice(1, 2, -1)]); + _vm->setItemPosition((Item *)&_vm->_levelBlockProperties[_vm->_currentBlock & 0x3FF].drawObjects, _vm->_currentBlock, itm, _itemPos[_vm->rollDice(1, 2, -1)]); debugC(5, kDebugLevelScript, " - create item '%d' (value '%d', flags '0x%X', icon number '%d') on current block", itm, _vm->_items[itm].value, _vm->_items[itm].flags, _vm->_items[itm].icon); } - } else if (block == 0xfffe) { - _vm->setItemPosition((Item *)&_vm->_levelBlockProperties[_vm->_currentBlock & 0x3ff].drawObjects, _vm->_currentBlock, itm, _itemPos[(_vm->_currentDirection << 2) + _vm->rollDice(1, 2, -1)]); + } else if (block == 0xFFFE) { + _vm->setItemPosition((Item *)&_vm->_levelBlockProperties[_vm->_currentBlock & 0x3FF].drawObjects, _vm->_currentBlock, itm, _itemPos[(_vm->_currentDirection << 2) + _vm->rollDice(1, 2, -1)]); debugC(5, kDebugLevelScript, " - create item '%d' (value '%d', flags '0x%X', icon number '%d') on current block", itm, _vm->_items[itm].value, _vm->_items[itm].flags, _vm->_items[itm].icon); } else { - _vm->setItemPosition((Item *)&_vm->_levelBlockProperties[block & 0x3ff].drawObjects, block, itm, itmPos); + _vm->setItemPosition((Item *)&_vm->_levelBlockProperties[block & 0x3FF].drawObjects, block, itm, itmPos); debugC(5, kDebugLevelScript, " - create item '%d' (value '%d', flags '0x%X', icon number '%d') on block '0x%.04X', position '%d'", itm, _vm->_items[itm].value, _vm->_items[itm].flags, _vm->_items[itm].icon, block, itmPos); } @@ -1530,7 +1530,7 @@ int EoBInfProcessor::oeob_dialogue(int8 *data) { break; case -40: - _dlgResult = _vm->runDialogue(READ_LE_UINT16(pos), READ_LE_UINT16(pos + 6) == 0xffff ? 2 : 3, getString(READ_LE_UINT16(pos + 2)), getString(READ_LE_UINT16(pos + 4)), getString(READ_LE_UINT16(pos + 6))); + _dlgResult = _vm->runDialogue(READ_LE_UINT16(pos), READ_LE_UINT16(pos + 6) == 0xFFFF ? 2 : 3, getString(READ_LE_UINT16(pos + 2)), getString(READ_LE_UINT16(pos + 4)), getString(READ_LE_UINT16(pos + 6))); pos += 8; break; diff --git a/engines/kyra/script_eob.h b/engines/kyra/script_eob.h index fc8b4cfc31..ff3a5e0fac 100644 --- a/engines/kyra/script_eob.h +++ b/engines/kyra/script_eob.h @@ -91,7 +91,7 @@ private: EoBCoreEngine *_vm; Screen_EoB *_screen; - typedef Common::Functor1Mem<int8*, int, EoBInfProcessor> InfProc; + typedef Common::Functor1Mem<int8 *, int, EoBInfProcessor> InfProc; struct InfOpcode : private Common::NonCopyable { InfOpcode(InfProc *p, const char *d) : proc(p), desc(d) {} ~InfOpcode() { delete proc; } diff --git a/engines/kyra/script_hof.cpp b/engines/kyra/script_hof.cpp index fca83ae632..5bf8f6e78d 100644 --- a/engines/kyra/script_hof.cpp +++ b/engines/kyra/script_hof.cpp @@ -253,7 +253,7 @@ int KyraEngine_HoF::o2_displayWsaSequentialFrames(EMCState *script) { uint16 currentFrame = stackPos(3); uint16 lastFrame = stackPos(4); uint16 index = stackPos(5); - uint16 copyParam = stackPos(6) | 0xc000; + uint16 copyParam = stackPos(6) | 0xC000; _screen->hideMouse(); @@ -278,7 +278,7 @@ int KyraEngine_HoF::o2_displayWsaSequence(EMCState *script) { const int frameDelay = stackPos(2) * _tickLength; const int index = stackPos(3); const bool doUpdate = (stackPos(4) != 0); - const uint16 copyParam = stackPos(5) | 0xc000; + const uint16 copyParam = stackPos(5) | 0xC000; _screen->hideMouse(); @@ -319,8 +319,8 @@ int KyraEngine_HoF::o2_drawShape(EMCState *script) { uint8 *shp = getShapePtr(stackPos(0) + 64); int x = stackPos(1); int y = stackPos(2); - uint8 dsFlag = stackPos(3) & 0xff; - uint8 modeFlag = stackPos(4) & 0xff; + uint8 dsFlag = stackPos(3) & 0xFF; + uint8 modeFlag = stackPos(4) & 0xFF; if (modeFlag) { _screen->drawShape(2, shp, x, y, 2, dsFlag ? 1 : 0); @@ -918,7 +918,7 @@ int KyraEngine_HoF::o2_useItemOnMainChar(EMCState *script) { tmpScript.regs[0] = _mainCharacter.sceneId; int oldVocH = _vocHigh; - _vocHigh = 0x5a; + _vocHigh = 0x5A; while (_emc->isValid(&tmpScript)) _emc->run(&tmpScript); @@ -955,7 +955,7 @@ int KyraEngine_HoF::o2_pressColorKey(EMCState *script) { debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_pressColorKey(%p) (%d)", (const void *)script, stackPos(0)); for (int i = 6; i; i--) _inputColorCode[i] = _inputColorCode[i - 1]; - _inputColorCode[0] = stackPos(0) & 0xff; + _inputColorCode[0] = stackPos(0) & 0xFF; for (int i = 0; i < 7; i++) { if (_presetColorCode[i] != _inputColorCode[6 - i]) return _dbgPass; @@ -1023,8 +1023,8 @@ int KyraEngine_HoF::o2_getColorCodeValue(EMCState *script) { int KyraEngine_HoF::o2_setColorCodeValue(EMCState *script) { debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_setColorCodeValue(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); - _presetColorCode[stackPos(0)] = stackPos(1) & 0xff; - return stackPos(1) & 0xff; + _presetColorCode[stackPos(0)] = stackPos(1) & 0xFF; + return stackPos(1) & 0xFF; } int KyraEngine_HoF::o2_countItemInstances(EMCState *script) { @@ -1495,7 +1495,7 @@ void KyraEngine_HoF::setupOpcodeTable() { OpcodeUnImpl(); OpcodeUnImpl(); Opcode(o2_setCharacterAnimFrame); - // 0x0c + // 0x0C Opcode(o2_setCharacterFacingOverwrite); Opcode(o2_trySceneChange); Opcode(o2_moveCharacter); @@ -1515,7 +1515,7 @@ void KyraEngine_HoF::setupOpcodeTable() { Opcode(o2_wsaOpen); Opcode(o2_displayWsaSequentialFrames); Opcode(o2_displayWsaSequence); - // 0x1c + // 0x1C Opcode(o2_addItemToInventory); Opcode(o2_drawShape); Opcode(o2_addItemToCurScene); @@ -1536,7 +1536,7 @@ void KyraEngine_HoF::setupOpcodeTable() { Opcode(o1_setGameFlag); Opcode(o1_setHandItem); Opcode(o1_removeHandItem); - // 0x2c + // 0x2C Opcode(o1_getMouseState); Opcode(o1_hideMouse); Opcode(o2_addSpecialExit); @@ -1556,7 +1556,7 @@ void KyraEngine_HoF::setupOpcodeTable() { Opcode(o2_setTimerDelay); Opcode(o2_setScaleTableItem); Opcode(o2_setDrawLayerTableItem); - // 0x3c + // 0x3C Opcode(o2_setCharPalEntry); Opcode(o2_loadZShapes); Opcode(o2_drawSceneShape); @@ -1576,7 +1576,7 @@ void KyraEngine_HoF::setupOpcodeTable() { Opcode(o2_restoreInventoryGfx); Opcode(o2_setSceneAnimPos2); Opcode(o2_update); - // 0x4c + // 0x4C OpcodeUnImpl(); Opcode(o2_fadeScenePal); Opcode(o2_dummy); @@ -1596,7 +1596,7 @@ void KyraEngine_HoF::setupOpcodeTable() { Opcode(o1_playSoundEffect); Opcode(o2_setSceneAnimPos); Opcode(o1_blockInWalkableRegion); - // 0x5c + // 0x5C Opcode(o1_blockOutWalkableRegion); OpcodeUnImpl(); Opcode(o2_setCauldronState); @@ -1616,7 +1616,7 @@ void KyraEngine_HoF::setupOpcodeTable() { OpcodeUnImpl(); Opcode(o2_playFireflyScore); Opcode(o2_waitForConfirmationClick); - // 0x6c + // 0x6C Opcode(o2_encodeShape); Opcode(o2_defineRoomEntrance); Opcode(o2_runAnimationScript); @@ -1636,7 +1636,7 @@ void KyraEngine_HoF::setupOpcodeTable() { Opcode(o2_defineScene); Opcode(o2_addCauldronStateTableEntry); Opcode(o2_setCountDown); - // 0x7c + // 0x7C Opcode(o2_getCountDown); Opcode(o2_dummy); Opcode(o2_dummy); @@ -1656,7 +1656,7 @@ void KyraEngine_HoF::setupOpcodeTable() { Opcode(o2_removeItemFromScene); Opcode(o2_initObject); Opcode(o2_npcChat); - // 0x8c + // 0x8C Opcode(o2_deinitObject); Opcode(o2_playTimSequence); Opcode(o2_makeBookOrCauldronAppear); @@ -1676,27 +1676,27 @@ void KyraEngine_HoF::setupOpcodeTable() { Opcode(o2_customChatFinish); Opcode(o2_setupSceneAnimation); Opcode(o2_stopSceneAnimation); - // 0x9c + // 0x9C Opcode(o2_disableTimer); Opcode(o2_enableTimer); Opcode(o2_setTimerCountdown); Opcode(o2_processPaletteIndex); - // 0xa0 + // 0xA0 Opcode(o2_updateTwoSceneAnims); Opcode(o2_getRainbowRoomData); Opcode(o2_drawSceneShapeEx); Opcode(o2_midiSoundFadeout); - // 0xa4 + // 0xA4 Opcode(o2_getSfxDriver); Opcode(o2_getVocSupport); Opcode(o2_getMusicDriver); Opcode(o2_setVocHigh); - // 0xa8 + // 0xA8 Opcode(o2_getVocHigh); Opcode(o2_zanthiaChat); Opcode(o2_isVoiceEnabled); Opcode(o2_isVoicePlaying); - // 0xac + // 0xAC Opcode(o2_stopVoicePlaying); Opcode(o2_getGameLanguage); Opcode(o2_demoFinale); diff --git a/engines/kyra/script_lok.cpp b/engines/kyra/script_lok.cpp index db9e01cabb..22d5e9fd7c 100644 --- a/engines/kyra/script_lok.cpp +++ b/engines/kyra/script_lok.cpp @@ -1774,7 +1774,7 @@ void KyraEngine_LoK::setupOpcodeTable() { Opcode(o1_blockInWalkableRegion); Opcode(o1_blockOutWalkableRegion); Opcode(o1_walkPlayerToPoint); - // 0x0c + // 0x0C Opcode(o1_dropItemInScene); Opcode(o1_drawAnimShapeIntoScene); Opcode(o1_setHandItem); @@ -1794,7 +1794,7 @@ void KyraEngine_LoK::setupOpcodeTable() { Opcode(o1_phaseInSameScene); Opcode(o1_setScenePhasingFlag); Opcode(o1_resetScenePhasingFlag); - // 0x1c + // 0x1C Opcode(o1_queryScenePhasingFlag); Opcode(o1_sceneToDirection); Opcode(o1_setBirthstoneGem); @@ -1814,7 +1814,7 @@ void KyraEngine_LoK::setupOpcodeTable() { Opcode(o1_openWSAFile); Opcode(o1_closeWSAFile); Opcode(o1_runWSAFromBeginningToEnd); - // 0x2c + // 0x2C Opcode(o1_displayWSAFrame); Opcode(o1_enterNewScene); Opcode(o1_setSpecialEnterXAndY); @@ -1834,7 +1834,7 @@ void KyraEngine_LoK::setupOpcodeTable() { Opcode(o1_getCharacterY); Opcode(o1_setCharacterFacing); Opcode(o1_copyWSARegion); - // 0x3c + // 0x3C Opcode(o1_printText); Opcode(o1_getRand); Opcode(o1_loadSoundFile); @@ -1854,7 +1854,7 @@ void KyraEngine_LoK::setupOpcodeTable() { Opcode(o1_placeCharacterInOtherScene); Opcode(o1_getKey); Opcode(o1_specificItemInInventory); - // 0x4c + // 0x4C Opcode(o1_popMobileNPCIntoScene); Opcode(o1_mobileCharacterInScene); Opcode(o1_hideMobileCharacter); @@ -1874,7 +1874,7 @@ void KyraEngine_LoK::setupOpcodeTable() { Opcode(o1_setCharacterMovementDelay); Opcode(o1_getCharacterFacing); Opcode(o1_bkgdScrollSceneAndMasksRight); - // 0x5c + // 0x5C Opcode(o1_dispelMagicAnimation); Opcode(o1_findBrightestFireberry); Opcode(o1_setFireberryGlowPalette); @@ -1894,7 +1894,7 @@ void KyraEngine_LoK::setupOpcodeTable() { Opcode(o1_addItemToInventory); Opcode(o1_intPrint); Opcode(o1_shakeScreen); - // 0x6c + // 0x6C Opcode(o1_createAmuletJewel); Opcode(o1_setSceneAnimCurrXY); Opcode(o1_poisonBrandonAndRemaps); @@ -1914,7 +1914,7 @@ void KyraEngine_LoK::setupOpcodeTable() { Opcode(o1_setMousePos); Opcode(o1_getMouseState); Opcode(o1_setEntranceMouseCursorTrack); - // 0x7c + // 0x7C Opcode(o1_itemAppearsOnGround); Opcode(o1_setNoDrawShapesFlag); Opcode(o1_fadeEntirePalette); @@ -1934,7 +1934,7 @@ void KyraEngine_LoK::setupOpcodeTable() { Opcode(o1_getScaleDepthTableValue); Opcode(o1_setScaleDepthTableValue); Opcode(o1_message); - // 0x8c + // 0x8C Opcode(o1_checkClickOnNPC); Opcode(o1_getFoyerItem); Opcode(o1_setFoyerItem); @@ -1954,7 +1954,7 @@ void KyraEngine_LoK::setupOpcodeTable() { Opcode(o1_fillRect); Opcode(o1_vocUnload); Opcode(o1_vocLoad); - // 0x9c + // 0x9C Opcode(o1_dummy); } #undef Opcode diff --git a/engines/kyra/script_lol.cpp b/engines/kyra/script_lol.cpp index 9c0fe21ad4..0bbe66f530 100644 --- a/engines/kyra/script_lol.cpp +++ b/engines/kyra/script_lol.cpp @@ -126,7 +126,7 @@ int LoLEngine::olol_moveParty(EMCState *script) { mode = (mode - 6 - _currentDirection) & 3; Button b; - b.data0Val2 = b.data1Val2 = b.data2Val2 = 0xfe; + b.data0Val2 = b.data1Val2 = b.data2Val2 = 0xFE; b.data0Val3 = b.data1Val3 = b.data2Val3 = 0x01; switch (mode) { @@ -268,7 +268,7 @@ int LoLEngine::olol_makeItem(EMCState *script) { int LoLEngine::olol_placeMoveLevelItem(EMCState *script) { debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_placeMoveLevelItem(%p) (%d, %d, %d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5)); - placeMoveLevelItem(stackPos(0), stackPos(1), stackPos(2), stackPos(3) & 0xff, stackPos(4) & 0xff, stackPos(5)); + placeMoveLevelItem(stackPos(0), stackPos(1), stackPos(2), stackPos(3) & 0xFF, stackPos(4) & 0xFF, stackPos(5)); return 1; } @@ -321,7 +321,7 @@ int LoLEngine::olol_getItemPara(EMCState *script) { case 14: return p->unkB; case 15: - return i->shpCurFrame_flg & 0x1fff; + return i->shpCurFrame_flg & 0x1FFF; case 16: return p->flags; case 17: @@ -397,7 +397,7 @@ int LoLEngine::olol_setCharacterStat(EMCState *script) { break; case 1: - c->raceClassSex = e & 0x0f; + c->raceClassSex = e & 0x0F; break; case 5: @@ -478,19 +478,19 @@ int LoLEngine::olol_loadDoorShapes(EMCState *script) { _wllWallFlags[i + 3] |= 7; int t = i % 5; if (t == 4) - _wllWallFlags[i + 3] &= 0xf8; + _wllWallFlags[i + 3] &= 0xF8; if (t == 3) - _wllWallFlags[i + 3] &= 0xfd; + _wllWallFlags[i + 3] &= 0xFD; } if (stackPos(3)) { for (int i = 3; i < 13; i++) - _wllWallFlags[i] &= 0xfd; + _wllWallFlags[i] &= 0xFD; } if (stackPos(4)) { for (int i = 13; i < 23; i++) - _wllWallFlags[i] &= 0xfd; + _wllWallFlags[i] &= 0xFD; } return 1; @@ -683,7 +683,7 @@ int LoLEngine::olol_setGlobalVar(EMCState *script) { break; case 2: - _currentLevel = b & 0xff; + _currentLevel = b & 0xFF; break; case 3: @@ -691,7 +691,7 @@ int LoLEngine::olol_setGlobalVar(EMCState *script) { break; case 4: - _brightness = b & 0xff; + _brightness = b & 0xFF; break; case 5: @@ -717,20 +717,20 @@ int LoLEngine::olol_setGlobalVar(EMCState *script) { break; case 9: - _lampOilStatus = b & 0xff; + _lampOilStatus = b & 0xFF; break; case 10: - _sceneDefaultUpdate = b & 0xff; + _sceneDefaultUpdate = b & 0xFF; gui_toggleButtonDisplayMode(0, 0); break; case 11: - _compassBroken = a & 0xff; + _compassBroken = a & 0xFF; break; case 12: - _drainMagic = a & 0xff; + _drainMagic = a & 0xFF; break; default: @@ -763,9 +763,9 @@ int LoLEngine::olol_checkEquippedItemScriptFlags(EMCState *script) { int LoLEngine::olol_setDoorState(EMCState *script) { debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_setDoorState(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); if (stackPos(1)) - _levelBlockProperties[stackPos(0)].flags = (_levelBlockProperties[stackPos(0)].flags & 0xef) | 0x20; + _levelBlockProperties[stackPos(0)].flags = (_levelBlockProperties[stackPos(0)].flags & 0xEF) | 0x20; else - _levelBlockProperties[stackPos(0)].flags &= 0xdf; + _levelBlockProperties[stackPos(0)].flags &= 0xDF; return 1; } @@ -784,7 +784,7 @@ int LoLEngine::olol_assignLevelDecorationShape(EMCState *script) { int LoLEngine::olol_resetBlockShapeAssignment(EMCState *script) { debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_resetBlockShapeAssignment(%p) (%d)", (const void *)script, stackPos(0)); - uint8 v = stackPos(0) & 0xff; + uint8 v = stackPos(0) & 0xFF; memset(_wllShapeMap + 3, v, 5); memset(_wllShapeMap + 13, v, 5); return 1; @@ -823,7 +823,7 @@ int LoLEngine::olol_initMonster(EMCState *script) { l->type = stackPos(4); l->properties = &_monsterProperties[l->type]; l->direction = l->facing << 1; - l->hitPoints = (l->properties->hitPoints * _monsterModifiers[_monsterDifficulty]) >> 8; + l->hitPoints = (l->properties->hitPoints * _monsterModifiers1[_monsterDifficulty]) >> 8; if (_currentLevel != 12 || l->type != 2) l->hitPoints = (l->hitPoints * (rollDice(1, 128) + 192)) >> 8; @@ -842,7 +842,7 @@ int LoLEngine::olol_initMonster(EMCState *script) { l->destDirection = l->direction; for (int ii = 0; ii < 4; ii++) - l->equipmentShapes[ii] = stackPos(7 + ii) & 0xff; + l->equipmentShapes[ii] = stackPos(7 + ii) & 0xFF; checkSceneUpdateNeed(l->block); return i; @@ -927,7 +927,7 @@ int LoLEngine::olol_loadMonsterProperties(EMCState *script) { stackPos(35), stackPos(36), stackPos(37), stackPos(38), stackPos(39), stackPos(40), stackPos(41)); LoLMonsterProperty *l = &_monsterProperties[stackPos(0)]; - l->shapeIndex = stackPos(1) & 0xff; + l->shapeIndex = stackPos(1) & 0xFF; int shpWidthMax = 0; @@ -1079,7 +1079,7 @@ int LoLEngine::olol_addRemoveCharacter(EMCState *script) { if (!(_characters[i].flags & 1) || _characters[i].id != id) continue; - _characters[i].flags &= 0xfffe; + _characters[i].flags &= 0xFFFE; calcCharPortraitXpos(); if (_selectedCharacter == i) @@ -1192,7 +1192,7 @@ int LoLEngine::olol_changeMonsterStat(EMCState *script) { if (stackPos(0) == -1) return 1; - LoLMonster *m = &_monsters[stackPos(0) & 0x7fff]; + LoLMonster *m = &_monsters[stackPos(0) & 0x7FFF]; int16 d = stackPos(2); uint16 x = 0; @@ -1208,7 +1208,7 @@ int LoLEngine::olol_changeMonsterStat(EMCState *script) { break; case 2: - calcCoordinates(x, y, d, m->x & 0xff, m->y & 0xff); + calcCoordinates(x, y, d, m->x & 0xFF, m->y & 0xFF); if (!walkMonsterCheckDest(x, y, m, 7)) placeMonster(m, x, y); break; @@ -1233,7 +1233,7 @@ int LoLEngine::olol_getMonsterStat(EMCState *script) { if (stackPos(0) == -1) return 0; - LoLMonster *m = &_monsters[stackPos(0) & 0x7fff]; + LoLMonster *m = &_monsters[stackPos(0) & 0x7FFF]; int d = stackPos(1); switch (d) { @@ -1318,8 +1318,8 @@ int LoLEngine::olol_drawExitButton(EMCState *script) { int w = _screen->getTextWidth(str); if (_flags.use16ColorMode) { - gui_drawBox(x - offs - w, y - 9, w + offs, 9, 0xee, 0xcc, 0x11); - _screen->printText(str, x - (offs >> 1) - w, y - 7, 0xbb, 0); + gui_drawBox(x - offs - w, y - 9, w + offs, 9, 0xEE, 0xCC, 0x11); + _screen->printText(str, x - (offs >> 1) - w, y - 7, 0xBB, 0); } else { gui_drawBox(x - offs - w, y - 9, w + offs, 9, 136, 251, 252); _screen->printText(str, x - (offs >> 1) - w, y - 7, 144, 0); @@ -1497,7 +1497,7 @@ int LoLEngine::olol_distanceAttack(EMCState *script) { uint16 y = 0; calcCoordinates(x, y, stackPos(2), fX, fY); - if (launchObject(stackPos(0), stackPos(1), x, y, stackPos(5), stackPos(6) << 1, stackPos(7), stackPos(8), 0x3f)) + if (launchObject(stackPos(0), stackPos(1), x, y, stackPos(5), stackPos(6) << 1, stackPos(7), stackPos(8), 0x3F)) return 1; deleteItem(stackPos(1)); @@ -1532,7 +1532,7 @@ int LoLEngine::olol_moveBlockObjects(EMCState *script) { // WORKAROUND for script bug // Items would vanish when thrown towards the stairs // in white tower level 3. - if (_currentLevel == 21 && level == 21 && destBlock == 0x3e0) { + if (_currentLevel == 21 && level == 21 && destBlock == 0x3E0) { level = 20; destBlock = 0x0247; } @@ -1544,7 +1544,7 @@ int LoLEngine::olol_moveBlockObjects(EMCState *script) { if (!includeMonsters) continue; - l &= 0x7fff; + l &= 0x7FFF; LoLMonster *m = &_monsters[l]; @@ -1558,7 +1558,7 @@ int LoLEngine::olol_moveBlockObjects(EMCState *script) { if (!(_itemsInPlay[l].shpCurFrame_flg & 0x4000) || !includeItems) continue; - placeMoveLevelItem(l, level, destBlock, _itemsInPlay[l].x & 0xff, _itemsInPlay[l].y & 0xff, _itemsInPlay[l].flyingHeight); + placeMoveLevelItem(l, level, destBlock, _itemsInPlay[l].x & 0xFF, _itemsInPlay[l].y & 0xFF, _itemsInPlay[l].flyingHeight); res = 1; if (!runScript || level != _currentLevel) @@ -1617,7 +1617,7 @@ int LoLEngine::olol_dummy1(EMCState *script) { int LoLEngine::olol_suspendMonster(EMCState *script) { debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_suspendMonster(%p) (%d)", (const void *)script, stackPos(0)); - LoLMonster *m = &_monsters[stackPos(0) & 0x7fff]; + LoLMonster *m = &_monsters[stackPos(0) & 0x7FFF]; setMonsterMode(m, 14); checkSceneUpdateNeed(m->block); placeMonster(m, 0, 0); @@ -1732,11 +1732,11 @@ int LoLEngine::olol_getItemOnPos(EMCState *script) { debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_getItemOnPos(%p) (%d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3)); int pX = stackPos(1); if (pX != -1) - pX &= 0xff; + pX &= 0xFF; int pY = stackPos(2); if (pY != -1) - pY &= 0xff; + pY &= 0xFF; int o = (stackPos(3) || _emcLastItem == -1) ? stackPos(0) : _emcLastItem; @@ -1744,19 +1744,19 @@ int LoLEngine::olol_getItemOnPos(EMCState *script) { while (_emcLastItem) { if (_emcLastItem & 0x8000) { - o = _emcLastItem & 0x7fff; + o = _emcLastItem & 0x7FFF; _emcLastItem = _levelBlockProperties[o].assignedObjects; continue; } - if (pX != -1 && (_itemsInPlay[_emcLastItem].x & 0xff) != pX) { - o = _emcLastItem & 0x7fff; + if (pX != -1 && (_itemsInPlay[_emcLastItem].x & 0xFF) != pX) { + o = _emcLastItem & 0x7FFF; _emcLastItem = _levelBlockProperties[o].assignedObjects; continue; } - if (pY != -1 && (_itemsInPlay[_emcLastItem].y & 0xff) != pY) { - o = _emcLastItem & 0x7fff; + if (pY != -1 && (_itemsInPlay[_emcLastItem].y & 0xFF) != pY) { + o = _emcLastItem & 0x7FFF; _emcLastItem = _levelBlockProperties[o].assignedObjects; continue; } @@ -1835,7 +1835,7 @@ int LoLEngine::olol_assignCustomSfx(EMCState *script) { return 0; uint16 t = READ_LE_UINT16(&_ingameSoundIndex[i << 1]); - if (t == 0xffff) + if (t == 0xFFFF) return 0; strcpy(_ingameSoundList[t], c); @@ -1848,7 +1848,7 @@ int LoLEngine::olol_findAssignedMonster(EMCState *script) { uint16 o = stackPos(1) == -1 ? _levelBlockProperties[stackPos(0)].assignedObjects : findObject(stackPos(1))->nextAssignedObject; while (o) { if (o & 0x8000) - return o & 0x7fff; + return o & 0x7FFF; o = findObject(o)->nextAssignedObject; } return -1; @@ -1861,8 +1861,8 @@ int LoLEngine::olol_checkBlockForMonster(EMCState *script) { uint16 o = _levelBlockProperties[block].assignedObjects; while (o & 0x8000) { - if (id == 0xffff || id == o) - return o & 0x7fff; + if (id == 0xFFFF || id == o) + return o & 0x7FFF; o = findObject(o)->nextAssignedObject; } return -1; @@ -2040,7 +2040,7 @@ int LoLEngine::olol_changeItemTypeOrFlag(EMCState *script) { if (stackPos(1) == 4) i->itemPropertyIndex = val; else if (stackPos(1) == 15) - i->shpCurFrame_flg = (i->shpCurFrame_flg & 0xe000) | (val & 0x1fff); + i->shpCurFrame_flg = (i->shpCurFrame_flg & 0xE000) | (val & 0x1FFF); else val = -1; @@ -2129,7 +2129,7 @@ int LoLEngine::olol_paletteFlash(EMCState *script) { uint8 *d = p2.getData(); for (int i = 0; i < 16; i++) - d[i * 3] = 0x3f; + d[i * 3] = 0x3F; _screen->setScreenPalette(p2); _screen->updateScreen(); diff --git a/engines/kyra/script_mr.cpp b/engines/kyra/script_mr.cpp index 22d0bc4e95..f656b162fd 100644 --- a/engines/kyra/script_mr.cpp +++ b/engines/kyra/script_mr.cpp @@ -1144,7 +1144,7 @@ void KyraEngine_MR::setupOpcodeTable() { Opcode(o3_dummy); Opcode(o3_dummy); Opcode(o3_getCharacterFrameFromFacing); - // 0x0c + // 0x0C Opcode(o2_setCharacterFacingOverwrite); Opcode(o2_trySceneChange); Opcode(o2_moveCharacter); @@ -1164,7 +1164,7 @@ void KyraEngine_MR::setupOpcodeTable() { Opcode(o3_showAlbum); Opcode(o3_setInventorySlot); Opcode(o3_getInventorySlot); - // 0x1c + // 0x1C Opcode(o3_addItemToInventory); OpcodeUnImpl(); Opcode(o3_addItemToCurScene); @@ -1184,7 +1184,7 @@ void KyraEngine_MR::setupOpcodeTable() { Opcode(o1_setGameFlag); Opcode(o1_setHandItem); Opcode(o1_removeHandItem); - // 0x2c + // 0x2C Opcode(o1_getMouseState); Opcode(o1_hideMouse); Opcode(o2_addSpecialExit); @@ -1204,7 +1204,7 @@ void KyraEngine_MR::setupOpcodeTable() { Opcode(o3_makeSecondChanceSave); Opcode(o3_setSceneFilename); OpcodeUnImpl(); - // 0x3c + // 0x3C Opcode(o3_removeItemsFromScene); Opcode(o3_disguiseMalcolm); Opcode(o3_drawSceneShape); @@ -1224,7 +1224,7 @@ void KyraEngine_MR::setupOpcodeTable() { Opcode(o3_dummy); Opcode(o3_setSceneAnimPosAndFrame); Opcode(o2_update); - // 0x4c + // 0x4C Opcode(o3_removeItemInstances); Opcode(o3_dummy); Opcode(o3_disableInventory); @@ -1244,7 +1244,7 @@ void KyraEngine_MR::setupOpcodeTable() { Opcode(o3_playSoundEffect); Opcode(o3_getScore); Opcode(o3_daggerWarning); - // 0x5c + // 0x5C Opcode(o3_blockOutWalkableRegion); Opcode(o3_dummy); Opcode(o3_showSceneStringsMessage); @@ -1264,7 +1264,7 @@ void KyraEngine_MR::setupOpcodeTable() { Opcode(o3_dummy); Opcode(o3_dummy); Opcode(o2_waitForConfirmationClick); - // 0x6c + // 0x6C Opcode(o3_dummy); Opcode(o2_defineRoomEntrance); Opcode(o2_runAnimationScript); @@ -1284,7 +1284,7 @@ void KyraEngine_MR::setupOpcodeTable() { Opcode(o2_defineScene); Opcode(o3_setConversationState); OpcodeUnImpl(); - // 0x7c + // 0x7C OpcodeUnImpl(); Opcode(o3_getConversationState); Opcode(o3_dummy); @@ -1304,7 +1304,7 @@ void KyraEngine_MR::setupOpcodeTable() { Opcode(o3_dummy); Opcode(o3_dialogStartScript); Opcode(o3_dummy); - // 0x8c + // 0x8C Opcode(o3_dialogEndScript); Opcode(o3_dummy); Opcode(o3_dummy); @@ -1324,27 +1324,27 @@ void KyraEngine_MR::setupOpcodeTable() { Opcode(o3_customChatFinish); Opcode(o3_setupSceneAnimObject); Opcode(o3_removeSceneAnimObject); - // 0x9c + // 0x9C Opcode(o2_disableTimer); Opcode(o2_enableTimer); Opcode(o2_setTimerCountdown); OpcodeUnImpl(); - // 0xa0 + // 0xA0 Opcode(o3_dummy); Opcode(o3_dummy); Opcode(o3_dummy); Opcode(o3_dummy); - // 0xa4 + // 0xA4 OpcodeUnImpl(); OpcodeUnImpl(); OpcodeUnImpl(); Opcode(o2_setVocHigh); - // 0xa8 + // 0xA8 Opcode(o2_getVocHigh); OpcodeUnImpl(); OpcodeUnImpl(); OpcodeUnImpl(); - // 0xac + // 0xAC OpcodeUnImpl(); Opcode(o3_dummy); OpcodeUnImpl(); diff --git a/engines/kyra/script_tim.cpp b/engines/kyra/script_tim.cpp index 177d7993a0..ba0f62a2b4 100644 --- a/engines/kyra/script_tim.cpp +++ b/engines/kyra/script_tim.cpp @@ -297,20 +297,20 @@ void TIMInterpreter::displayText(uint16 textId, int16 flags) { memcpy(filename, text+1, end-1-text); } - const bool isPC98 = (_vm->gameFlags().platform == Common::kPlatformPC98); + const bool sjisMode = (_vm->gameFlags().lang == Common::JA_JPN && _vm->gameFlags().use16ColorMode); if (filename[0] && (_vm->speechEnabled() || !_vm->gameFlags().isTalkie)) - _vm->sound()->voicePlay(filename); + _vm->sound()->voicePlay(filename, 0, 255, 255, !_vm->gameFlags().isTalkie); if (text[0] == '$') text = strchr(text + 1, '$') + 1; - if (!isPC98) + if (!_vm->gameFlags().use16ColorMode) setupTextPalette((flags < 0) ? 1 : flags, 0); if (flags < 0) { static const uint8 colorMap[] = { 0x00, 0xF0, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - _screen->setFont(isPC98 ? Screen::FID_SJIS_FNT : Screen::FID_8_FNT); + _screen->setFont(sjisMode ? Screen::FID_SJIS_FNT : Screen::FID_8_FNT); _screen->setTextColorMap(colorMap); _screen->_charWidth = -2; } @@ -335,7 +335,7 @@ void TIMInterpreter::displayText(uint16 textId, int16 flags) { int width = _screen->getTextWidth(str); if (flags >= 0) { - if (isPC98) { + if (_vm->gameFlags().use16ColorMode) { static const uint8 colorMap[] = { 0xE1, 0xE1, 0xC1, 0xA1, 0x81, 0x61 }; _screen->printText(str, (320 - width) >> 1, 160 + heightAdd, colorMap[flags], 0x00); } else { @@ -359,7 +359,7 @@ void TIMInterpreter::displayText(uint16 textId, int16 flags) { if (flags < 0) { static const uint8 colorMap[] = { 0x00, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0x00, 0x00, 0x00, 0x00 }; - _screen->setFont(isPC98 ? Screen::FID_SJIS_FNT : Screen::FID_INTRO_FNT); + _screen->setFont(sjisMode ? Screen::FID_SJIS_FNT : Screen::FID_INTRO_FNT); _screen->setTextColorMap(colorMap); _screen->_charWidth = 0; } @@ -377,7 +377,7 @@ void TIMInterpreter::displayText(uint16 textId, int16 flags, uint8 color) { if (flags == 255) return; - _screen->setFont(_vm->gameFlags().use16ColorMode ? Screen::FID_SJIS_FNT : Screen::FID_INTRO_FNT); + _screen->setFont((_vm->gameFlags().lang == Common::JA_JPN && _vm->gameFlags().use16ColorMode) ? Screen::FID_SJIS_FNT : Screen::FID_INTRO_FNT); static const uint8 colorMap[] = { 0x00, 0xA0, 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; _screen->setTextColorMap(colorMap); @@ -393,14 +393,14 @@ void TIMInterpreter::displayText(uint16 textId, int16 flags, uint8 color) { int y = 0; if (_vm->gameFlags().use16ColorMode) { - if (color == 0xda) - color = 0xa1; - else if (color == 0xf2) - color = 0xe1; + if (color == 0xDA) + color = 0xA1; + else if (color == 0xF2) + color = 0xE1; else if (flags < 0) - color = 0xe1; + color = 0xE1; else - color = 0xc1; + color = 0xC1; } while (str[0]) { @@ -736,7 +736,7 @@ int TIMInterpreter::cmd_playVocFile(const uint16 *param) { const int volume = (param[1] * 255) / 100; if (index < ARRAYSIZE(_vocFiles) && !_vocFiles[index].empty()) - _vm->sound()->voicePlay(_vocFiles[index].c_str(), 0, volume, true); + _vm->sound()->voicePlay(_vocFiles[index].c_str(), 0, volume, 255, true); else if (index == 7 && !_vm->gameFlags().isTalkie) _vm->sound()->playTrack(index); else @@ -1083,7 +1083,7 @@ int TIMInterpreter_LoL::cmd_dialogueBox(const uint16 *param) { int cnt = 0; for (int i = 1; i < 4; i++) { - if (param[i] != 0xffff) { + if (param[i] != 0xFFFF) { tmpStr[i-1] = getTableString(param[i]); cnt++; } else { diff --git a/engines/kyra/sequences_darkmoon.cpp b/engines/kyra/sequences_darkmoon.cpp index d4f5c847fd..d2e6e85218 100644 --- a/engines/kyra/sequences_darkmoon.cpp +++ b/engines/kyra/sequences_darkmoon.cpp @@ -867,7 +867,7 @@ void DarkMoonEngine::seq_playCredits(DarkmoonSequenceHelper *sq, const uint8 *da int16 nextY = i ? items[i].y + items[i].size + (items[i].size >> 2) : dm->h; const char *posOld = pos; - pos = strchr(pos, 0x0d); + pos = strchr(pos, 0x0D); if (!pos) pos = strchr(posOld, 0x00); @@ -893,7 +893,7 @@ void DarkMoonEngine::seq_playCredits(DarkmoonSequenceHelper *sq, const uint8 *da items[i + 1].dataType = 0; int l = pos - posOld; - if (items[i + 1].crlf != 0x0d) + if (items[i + 1].crlf != 0x0D) l++; delete[] items[i + 1].str; @@ -1071,7 +1071,7 @@ void DarkmoonSequenceHelper::animCommand(int index, int del) { uint32 end = 0; - for (const DarkMoonAnimCommand *s = _config->animData[index]; s->command != 0xff && !_vm->skipFlag() && !_vm->shouldQuit(); s++) { + for (const DarkMoonAnimCommand *s = _config->animData[index]; s->command != 0xFF && !_vm->skipFlag() && !_vm->shouldQuit(); s++) { int palIndex = _config->mode == kFinale ? (s->pal + 1) : s->pal; int x = s->x1; int y = s->y1; @@ -1187,7 +1187,7 @@ void DarkmoonSequenceHelper::animCommand(int index, int del) { case 6: // play sound effect - if (s->obj != 0xff) + if (s->obj != 0xFF) _vm->snd_playSoundEffect(s->obj); break; diff --git a/engines/kyra/sequences_eob.cpp b/engines/kyra/sequences_eob.cpp index 4a9f7d8a65..0fec386485 100644 --- a/engines/kyra/sequences_eob.cpp +++ b/engines/kyra/sequences_eob.cpp @@ -706,7 +706,7 @@ void EoBIntroPlayer::waterdeepExit() { _vm->delay(60 * _vm->_tickLength); for (int i = 0; i < 56 && !_vm->shouldQuit() && !_vm->skipFlag(); i++) { - uint32 end = _vm->_system->getMillis() +_vm->_tickLength; + uint32 end = _vm->_system->getMillis() + _vm->_tickLength; _screen->copyRegion(0, 136 + i, 80, 16, 160, 56 - i, 2, 0, Screen::CR_NO_P_CHECK); _screen->copyRegion(160, 0, 80, 72 - i, 160, 96 + i, 2, 0, Screen::CR_NO_P_CHECK); _screen->updateScreen(); @@ -1061,7 +1061,7 @@ void EoBEngine::seq_playFinale() { gui_drawBox(0, 0, 176, 175, guiSettings()->colors.frame1, guiSettings()->colors.frame2, guiSettings()->colors.fill); _txt->printDialogueText(51, _moreStrings[0]); - if (!checkScriptFlags(0x1ffe)) { + if (!checkScriptFlags(0x1FFE)) { _screen->fadeToBlack(); return; } diff --git a/engines/kyra/sequences_hof.cpp b/engines/kyra/sequences_hof.cpp index f2abfb81dc..231337e6d4 100644 --- a/engines/kyra/sequences_hof.cpp +++ b/engines/kyra/sequences_hof.cpp @@ -21,94 +21,659 @@ */ #include "kyra/kyra_hof.h" -#include "kyra/timer.h" +#include "kyra/screen_hof.h" +#include "kyra/screen_lol.h" #include "kyra/resource.h" #include "kyra/sound.h" +#include "kyra/sequences_hof.h" +#include "kyra/timer.h" #include "common/system.h" namespace Kyra { -void KyraEngine_HoF::seq_playSequences(int startSeq, int endSeq) { - seq_init(); +enum SequenceID { + kSequenceNoLooping = -1, + kSequenceVirgin = 0, + kSequenceWestwood, + kSequenceTitle, + kSequenceOverview, + kSequenceLibrary, + kSequenceHand, + kSequencePoint, + kSequenceZanfaun, + + kSequenceFunters, + kSequenceFerb, + kSequenceFish, + kSequenceFheep, + kSequenceFarmer, + kSequenceFuards, + kSequenceFirates, + kSequenceFrash, + + kSequenceHoFDemoVirgin, + kSequenceHoFDemoWestwood, + kSequenceHoFDemoTitle, + kSequenceHoFDemoHill, + kSequenceHoFDemoOuthome, + kSequenceHoFDemoWharf, + kSequenceHoFDemoDinob, + kSequenceHoFDemoFisher, + +// The following enums remain active even if LoL is disabled + kSequenceLoLDemoScene1, + kSequenceLoLDemoText1, + kSequenceLoLDemoScene2, + kSequenceLoLDemoText2, + kSequenceLoLDemoScene3, + kSequenceLoLDemoText3, + kSequenceLoLDemoScene4, + kSequenceLoLDemoText4, + kSequenceLoLDemoScene5, + kSequenceLoLDemoText5, + kSequenceLoLDemoScene6, + + kSequenceArraySize +}; + +enum NestedSequenceID { + kNestedSequenceFiggle = 0, + + kNestedSequenceOver1, + kNestedSequenceOver2, + kNestedSequenceForest, + kNestedSequenceDragon, + kNestedSequenceDarm, + kNestedSequenceLibrary2, + kNestedSequenceLibrary3, + kNestedSequenceMarco, + kNestedSequenceHand1a, + kNestedSequenceHand1b, + kNestedSequenceHand1c, + kNestedSequenceHand2, + kNestedSequenceHand3, + kNestedSequenceHand4, + + kNestedSequenceHoFDemoWharf2, + kNestedSequenceHoFDemoDinob2, + kNestedSequenceHoFDemoWater, + kNestedSequenceHoFDemoBail, + kNestedSequenceHoFDemoDig, + + kNestedSequenceArraySize +}; + +typedef int (SeqPlayer_HOF::*SeqProc)(WSAMovie_v2 *, int, int, int); + +struct SeqPlayerConfig { + SeqPlayerConfig(const HoFSeqData *data, const SeqProc *callbacks, const SeqProc *nestedCallbacks) : seq(data->seq), seqProc(callbacks), numSeq(data->numSeq), nestedSeq(data->nestedSeq), nestedSeqProc(nestedCallbacks), numNestedSeq(data->numNestedSeq) {} + const HoFSequence *seq; + const SeqProc *seqProc; + int numSeq; + const HoFNestedSequence *nestedSeq; + const SeqProc *nestedSeqProc; + int numNestedSeq; +}; + +class SeqPlayer_HOF { +public: + SeqPlayer_HOF(KyraEngine_v1 *vm, Screen_v2 *screen, OSystem *system, bool startupSaveLoadable = false); + ~SeqPlayer_HOF(); + + int play(SequenceID firstScene, SequenceID loopStartScene); + void pause(bool toggle); + + static SeqPlayer_HOF *instance() { return _instance; } + +private: + // Init + void setupCallbacks(); + + // Playback loop + void runLoop(); + void playScenes(); + + bool checkAbortPlayback(); + bool checkPlaybackStatus(); + + bool _abortRequested; + uint32 _pauseStart; + + // Sequence transitions + void doTransition(int type); + void nestedFrameAnimTransition(int srcPage, int dstPage, int delaytime, int steps, int x, int y, int w, int h, int openClose, int directionFlags); + void nestedFrameFadeTransition(const char *cmpFile); + + // Animations + void playAnimation(WSAMovie_v2 *wsaObj, int startFrame, int numFrames, int frameRate, int x, int y, const SeqProc callback, Palette *fadePal1, Palette *fadePal2, int fadeRate, bool restoreScreen); + void playDialogueAnimation(uint16 strID, uint16 soundID, int textColor, int textPosX, int textPosY, int textWidth, WSAMovie_v2 *wsaObj, int animStartFrame, int animLastFrame, int animPosX, int animPosY); + + void startNestedAnimation(int animSlot, int sequenceID); + void closeNestedAnimation(int animSlot); + void unloadNestedAnimation(int animSlot); + void doNestedFrameTransition(int transitionType, int animSlot); + void updateAllNestedAnimations(); + bool updateNestedAnimation(int animSlot); + + struct AnimSlot { + SeqProc callback; + WSAMovie_v2 *movie; + const FrameControl *control; + int16 flags; + uint16 startFrame; + uint16 endFrame; + uint16 frameDelay; + uint32 nextFrame; + uint16 currentFrame; + uint16 lastFrame; + uint16 x; + uint16 y; + uint16 fadeInTransitionType; + uint16 fadeOutTransitionType; + }; + + AnimSlot _animSlots[8]; + + bool _updateAnimations; + uint32 _animDuration; + int _animCurrentFrame; + int _callbackCurrentFrame; + + // The only reason to declare these here (instead of just locally) is being able to increase them after pausing the Engine + uint32 _specialAnimTimeOutTotal; + uint32 _specialAnimFrameTimeOut; + + // Subtitles/Dialogue/Sound + void playSoundEffect(uint16 id, int16 vol); + void playSoundAndDisplaySubTitle(uint16 id); + void printFadingText(uint16 strID, int x, int y, const uint8 *colorMap, uint8 textcolor); + + int displaySubTitle(uint16 strID, uint16 posX, uint16 posY, int duration, uint16 width); + void updateSubTitles(); + char *preprocessString(const char *str, int width); + void waitForSubTitlesTimeout(); + uint32 ticksTillSubTitlesTimeout(); + void resetAllTextSlots(); + + void fadeOutMusic(); + + struct TextSlot { + uint16 strIndex; + uint16 x; + uint16 y; + uint16 width; + int32 duration; + uint32 startTime; + int16 textcolor; + }; - bool allowSkip = (_flags.isDemo && !_flags.isTalkie) || startSeq != kSequenceTitle; + TextSlot _textSlots[10]; - if (endSeq == -1) - endSeq = startSeq; + char *_tempString; - assert(startSeq >= 0 && endSeq < kSequenceArraySize && startSeq <= endSeq); + uint8 _textColor[2]; + uint8 _textColorMap[16]; + int _textDuration[33]; - _sound->setSoundList(&_soundData[(startSeq > kSequenceZanfaun) ? kMusicFinale : kMusicIntro]); - _sound->loadSoundFile(0); + const char *const *_sequenceStrings; + const char *const *_sequenceSoundList; + int _sequenceSoundListSize; - _screen->_charWidth = (_flags.gameID == GI_LOL) ? 0 : -2; + static const uint8 _textColorPresets[]; - memset(_activeWSA, 0, sizeof(ActiveWSA) * 8); - for (int i = 0; i < 8; ++i) - _activeWSA[i].flags = -1; + // HOF credits + void playHoFTalkieCredits(); + void displayHoFTalkieScrollText(uint8 *data, const ScreenDim *d, int tempPage1, int tempPage2, int speed, int step, Screen::FontId fid1, Screen::FontId fid2, const uint8 *shapeData = 0, const char *const *specialData = 0); + + bool _talkieFinaleExtraFlag; + + // HOF+LOL demo specific + void updateDemoAdText(int bottom, int top); + + ActiveItemAnim _hofDemoActiveItemAnim[5]; + const HoFSeqItemAnimData *_hofDemoAnimData; + + uint32 _fisherAnimCurTime; + int _scrollProgressCounter; + + uint8 *_hofDemoShapeData; + uint8 *_hofDemoItemShapes[20]; + + // Misc + void delayTicks(uint32 ticks); + void delayUntil(uint32 dest); + void setCountDown(uint32 ticks); + bool countDownRunning(); + + uint32 _countDownRemainder; + uint32 _countDownLastUpdate; + + enum SeqPlayerTargetInfo { + kHoF = 0, + kHoFDemo, + kLoLDemo + }; + + SeqPlayerTargetInfo _target; + int _firstScene, _loopStartScene, _curScene, _preventSkipBeforeScene, _lastScene; + bool _startupSaveLoadable, _isFinale, _preventLooping; + + SeqPlayerConfig *_config; + + MainMenu *_menu; + int _result; + + bool _abortPlayback; + + KyraEngine_v1 *_vm; + Screen_v2 *_screen; + // We might consider getting rid of Screen_HoF, since there are only 2 methods left in that class anyway + Screen_HoF *_screenHoF; + OSystem *_system; + + static SeqPlayer_HOF *_instance; + +private: + // Sequence specific callback functions + int cbHOF_westwood(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOF_title(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOF_overview(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOF_library(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOF_hand(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOF_point(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOF_zanfaun(WSAMovie_v2 *wsaObj, int x, int y, int frm); + + int cbHOF_over1(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOF_over2(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOF_forest(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOF_dragon(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOF_darm(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOF_library2(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOF_marco(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOF_hand1a(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOF_hand1b(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOF_hand1c(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOF_hand2(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOF_hand3(WSAMovie_v2 *wsaObj, int x, int y, int frm); + + int cbHOF_funters(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOF_ferb(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOF_fish(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOF_fheep(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOF_farmer(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOF_fuards(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOF_firates(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOF_frash(WSAMovie_v2 *wsaObj, int x, int y, int frm); + + int cbHOF_figgle(WSAMovie_v2 *wsaObj, int x, int y, int frm); + + int cbHOFDEMO_virgin(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOFDEMO_westwood(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOFDEMO_title(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOFDEMO_hill(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOFDEMO_outhome(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOFDEMO_wharf(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOFDEMO_dinob(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOFDEMO_fisher(WSAMovie_v2 *wsaObj, int x, int y, int frm); + + int cbHOFDEMO_wharf2(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOFDEMO_dinob2(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOFDEMO_water(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOFDEMO_bail(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOFDEMO_dig(WSAMovie_v2 *wsaObj, int x, int y, int frm); + +#ifdef ENABLE_LOL + int cbLOLDEMO_scene1(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbLOLDEMO_scene2(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbLOLDEMO_scene3(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbLOLDEMO_scene4(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbLOLDEMO_scene5(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbLOLDEMO_text5(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbLOLDEMO_scene6(WSAMovie_v2 *wsaObj, int x, int y, int frm); +#endif // ENABLE_LOL +}; + +SeqPlayer_HOF *SeqPlayer_HOF::_instance = 0; + +SeqPlayer_HOF::SeqPlayer_HOF(KyraEngine_v1 *vm, Screen_v2 *screen, OSystem *system, bool startupSaveLoadable) : _vm(vm), _screen(screen), _system(system), _startupSaveLoadable(startupSaveLoadable) { + // We use a static pointer for pauseEngine functionality. Since we don't + // ever need more than one SeqPlayer_HOF object at the same time we keep + // this simple and just add an assert to detect typos, regressions, etc. + assert(_instance == 0); + + memset(_animSlots, 0, sizeof(_animSlots)); + memset(_textSlots, 0, sizeof(_textSlots)); + memset(_hofDemoActiveItemAnim, 0, sizeof(_hofDemoActiveItemAnim)); + + _screenHoF = _vm->game() == GI_KYRA2 ? (Screen_HoF*)screen : 0; + _config = 0; + _result = 0; + _sequenceSoundList = 0; + _hofDemoAnimData = 0; + _hofDemoShapeData = 0; + _isFinale = false; + _preventLooping = false; + _menu = 0; + _abortRequested = false; + _pauseStart = 0; + + _updateAnimations = false; + _animDuration = 0; + _animCurrentFrame = 0; + _callbackCurrentFrame = 0; + + _abortPlayback = false; + _curScene = 0; + _preventSkipBeforeScene = -1; + _lastScene = 0; + + _scrollProgressCounter = 0; + _fisherAnimCurTime = 0; + + _tempString = new char[200]; + + _countDownRemainder = 0; + _countDownLastUpdate = 0; + + int tempSize = 0; + _vm->resource()->unloadAllPakFiles(); + _vm->resource()->loadPakFile(StaticResource::staticDataFilename()); + const char *const *files = _vm->staticres()->loadStrings(k2SeqplayPakFiles, tempSize); + _vm->resource()->loadFileList(files, tempSize); + + _sequenceStrings = _vm->staticres()->loadStrings(k2SeqplayStrings, tempSize); + uint8 multiplier = (_vm->gameFlags().platform == Common::kPlatformFMTowns || _vm->gameFlags().platform == Common::kPlatformPC98) ? 12 : 8; + for (int i = 0; i < MIN(33, tempSize); i++) + _textDuration[i] = (int) strlen(_sequenceStrings[i]) * multiplier; + + if (_sequenceSoundList) { + for (int i = 0; i < _sequenceSoundListSize; i++) { + if (_sequenceSoundList[i]) + delete[] _sequenceSoundList[i]; + } + delete[] _sequenceSoundList; + _sequenceSoundList = 0; + } - memset(_activeText, 0, sizeof(ActiveText) * 10); - seq_resetAllTextEntries(); + const char *const *seqSoundList = _vm->staticres()->loadStrings(k2SeqplaySfxFiles, _sequenceSoundListSize); + // replace sequence talkie files with localized versions + const char *const *tlkfiles = _vm->staticres()->loadStrings(k2SeqplayTlkFiles, tempSize); + char **tmpSndLst = new char *[_sequenceSoundListSize]; + + for (int i = 0; i < _sequenceSoundListSize; i++) { + const int len = strlen(seqSoundList[i]); + + tmpSndLst[i] = new char[len + 1]; + tmpSndLst[i][0] = 0; + + if (tlkfiles && len > 1) { + for (int ii = 0; ii < tempSize; ii++) { + if (strlen(tlkfiles[ii]) > 1 && !scumm_stricmp(&seqSoundList[i][1], &tlkfiles[ii][1])) + strcpy(tmpSndLst[i], tlkfiles[ii]); + } + } + + if (tmpSndLst[i][0] == 0) + strcpy(tmpSndLst[i], seqSoundList[i]); + } + + tlkfiles = seqSoundList = 0; + _vm->staticres()->unloadId(k2SeqplayTlkFiles); + _vm->staticres()->unloadId(k2SeqplaySfxFiles); + _sequenceSoundList = tmpSndLst; + + if (_vm->gameFlags().platform == Common::kPlatformPC98) + _vm->sound()->loadSoundFile("SOUND.DAT"); + + _screen->loadFont(_screen->FID_GOLDFONT_FNT, "GOLDFONT.FNT"); + _screen->setFont(_vm->gameFlags().lang == Common::JA_JPN ? Screen::FID_SJIS_FNT : Screen::FID_GOLDFONT_FNT); + + if (_vm->gameFlags().isDemo && !_vm->gameFlags().isTalkie) { + if (_vm->game() == GI_KYRA2) { + _hofDemoAnimData = _vm->staticres()->loadHoFSeqItemAnimData(k2SeqplayShapeAnimData, tempSize); + uint8 *shp = _vm->resource()->fileData("ICONS.SHP", 0); + uint32 outsize = READ_LE_UINT16(shp + 4); + _hofDemoShapeData = new uint8[outsize]; + Screen::decodeFrame4(shp + 10, _hofDemoShapeData, outsize); + for (int i = 0; i < 20; i++) + _hofDemoItemShapes[i] = _screen->getPtrToShape(_hofDemoShapeData, i); + delete[] shp; + } + } else { + const MainMenu::StaticData data = { + { _sequenceStrings[97], _sequenceStrings[96], _sequenceStrings[95], _sequenceStrings[98], 0 }, + { 0x01, 0x04, 0x0C, 0x04, 0x00, 0xD7, 0xD6 }, + { 0xD8, 0xDA, 0xD9, 0xD8 }, + (_vm->gameFlags().lang == Common::JA_JPN) ? Screen::FID_SJIS_FNT : Screen::FID_8_FNT, 240 + }; + + _menu = new MainMenu(_vm); + _menu->init(data, MainMenu::Animation()); + } + + _instance = this; +} + +SeqPlayer_HOF::~SeqPlayer_HOF() { + _instance = 0; + + if (_sequenceSoundList) { + for (int i = 0; i < _sequenceSoundListSize; i++) { + if (_sequenceSoundList[i]) + delete[] _sequenceSoundList[i]; + } + delete[] _sequenceSoundList; + _sequenceSoundList = NULL; + } + + delete[] _tempString; + delete[] _hofDemoShapeData; + delete _menu; + + if (_vm->game() != GI_LOL) + _screen->setFont(_vm->gameFlags().lang == Common::JA_JPN ? Screen::FID_SJIS_FNT : Screen::FID_8_FNT); +} + +int SeqPlayer_HOF::play(SequenceID firstScene, SequenceID loopStartScene) { + bool incompatibleData = false; + AudioResourceSet soundSet = kMusicIntro; + _firstScene = firstScene; + _loopStartScene = loopStartScene; + _preventLooping = false; + _result = 0; + + if (firstScene >= kSequenceArraySize || firstScene < kSequenceVirgin || loopStartScene >= kSequenceArraySize || loopStartScene < kSequenceNoLooping) { + return 0; + } else if (firstScene >= kSequenceLoLDemoScene1) { +#ifndef ENABLE_LOL + error("SeqPlayer_HOF::play(): The Lands of Lore sub engine (including this non-interactive demo) has been disabled in this build"); +#endif + incompatibleData = (_vm->game() != GI_LOL); + _firstScene -= kSequenceLoLDemoScene1; + if (loopStartScene != kSequenceNoLooping) + _loopStartScene -= kSequenceLoLDemoScene1; + _lastScene = kSequenceLoLDemoScene6 - kSequenceLoLDemoScene1; + _target = kLoLDemo; + _screen->_charWidth = 0; + } else if (firstScene >= kSequenceHoFDemoVirgin) { + incompatibleData = (_vm->game() != GI_KYRA2 || !_vm->gameFlags().isDemo || _vm->gameFlags().isTalkie); + _firstScene -= kSequenceHoFDemoVirgin; + if (loopStartScene != kSequenceNoLooping) + _loopStartScene -= kSequenceHoFDemoVirgin; + _lastScene = kSequenceHoFDemoFisher - kSequenceHoFDemoVirgin; + _target = kHoFDemo; + _screen->_charWidth = -2; + } else { + _isFinale = _preventLooping = firstScene > kSequenceZanfaun; + incompatibleData = (_vm->game() != GI_KYRA2 || (_vm->gameFlags().isDemo && (!_vm->gameFlags().isTalkie || _isFinale))); + _target = kHoF; + _screen->_charWidth = -2; + if (_isFinale) { + soundSet = kMusicFinale; + _lastScene = kSequenceFrash; + } else { + _lastScene = kSequenceZanfaun; + } + } + + if (incompatibleData) + error("SeqPlayer_HOF::play(): Specified sequences do not match the available sequence data for this target"); + + _vm->sound()->selectAudioResourceSet(soundSet); + _vm->sound()->loadSoundFile(0); + + setupCallbacks(); + runLoop(); + + return _result; +} + +void SeqPlayer_HOF::pause(bool toggle) { + if (toggle) { + _pauseStart = _system->getMillis(); + } else { + uint32 pausedTime = _system->getMillis() - _pauseStart; + _pauseStart = 0; + + _countDownLastUpdate += pausedTime; + _fisherAnimCurTime += pausedTime; + _specialAnimTimeOutTotal += pausedTime; + _specialAnimFrameTimeOut += pausedTime; + + for (int i = 0; i < 10; i++) { + if (_textSlots[i].duration != -1) + _textSlots[i].startTime += pausedTime; + } + + for (int i = 0; i < 8; i++) { + if (_animSlots[i].flags != -1) + _animSlots[i].nextFrame += pausedTime; + } + } +} + +void SeqPlayer_HOF::setupCallbacks() { +#define SCB(x) &SeqPlayer_HOF::cbHOF_##x + static const SeqProc seqCallbacksHoF[] = { 0, SCB(westwood), SCB(title), SCB(overview), SCB(library), SCB(hand), SCB(point), SCB(zanfaun), SCB(funters), SCB(ferb), SCB(fish), SCB(fheep), SCB(farmer), SCB(fuards), SCB(firates), SCB(frash) }; + static const SeqProc nestedSeqCallbacksHoF[] = { SCB(figgle), SCB(over1), SCB(over2), SCB(forest), SCB(dragon), SCB(darm), SCB(library2), SCB(library2), SCB(marco), SCB(hand1a), SCB(hand1b), SCB(hand1c), SCB(hand2), SCB(hand3), 0 }; +#undef SCB +#define SCB(x) &SeqPlayer_HOF::cbHOFDEMO_##x + static const SeqProc seqCallbacksHoFDemo[] = { SCB(virgin), SCB(westwood), SCB(title), SCB(hill), SCB(outhome), SCB(wharf), SCB(dinob), SCB(fisher) }; + static const SeqProc nestedSeqCallbacksHoFDemo[] = { SCB(wharf2), SCB(dinob2), SCB(water), SCB(bail), SCB(dig), 0 }; +#undef SCB +#ifdef ENABLE_LOL +#define SCB(x) &SeqPlayer_HOF::cbLOLDEMO_##x + static const SeqProc seqCallbacksLoLDemo[] = { SCB(scene1), 0, SCB(scene2), 0, SCB(scene3), 0, SCB(scene4), 0, SCB(scene5), SCB(text5), SCB(scene6), 0 }; +#undef SCB +#else + static const SeqProc seqCallbacksLoLDemo[] = { 0 }; +#endif + static const SeqProc nestedSeqCallbacksLoLDemo[] = { 0 }; + + static const SeqProc *const seqCallbacks[] = { seqCallbacksHoF, seqCallbacksHoFDemo, seqCallbacksLoLDemo}; + static const SeqProc *const nestedSeqCallbacks[] = { nestedSeqCallbacksHoF, nestedSeqCallbacksHoFDemo, nestedSeqCallbacksLoLDemo}; + + int tmpSize = 0; + delete _config; + _config = new SeqPlayerConfig(_vm->staticres()->loadHoFSequenceData(k2SeqplaySeqData, tmpSize), seqCallbacks[_target], nestedSeqCallbacks[_target]); +} + +void SeqPlayer_HOF::runLoop() { + memset(_animSlots, 0, sizeof(_animSlots)); + memset(_textSlots, 0, sizeof(_textSlots)); + memset(_hofDemoActiveItemAnim, 0, sizeof(_hofDemoActiveItemAnim)); + for (int i = 0; i < 8; ++i) + _animSlots[i].flags = -1; + + _screen->clearPage(10); + _screen->clearPage(12); _screen->hideMouse(); int oldPage = _screen->setCurPage(2); for (int i = 0; i < 4; ++i) _screen->getPalette(i).clear(); - _screen->clearPage(10); - _screen->clearPage(12); + _updateAnimations = false; + _animCurrentFrame = 0; + _textColor[0] = _textColor[1] = 0; + _curScene = _firstScene; - _seqSubframePlaying = false; + do { + playScenes(); + doTransition(0); + resetAllTextSlots(); + fadeOutMusic(); + _firstScene = ((!_startupSaveLoadable || _preventLooping) && _curScene >= _loopStartScene) ? kSequenceNoLooping : _loopStartScene; + } while (!_vm->shouldQuit() && _firstScene != kSequenceNoLooping); - _seqWsaCurrentFrame = 0; - _seqTextColor[0] = _seqTextColor[1] = 0; - _seqEndTime = 0; - _menuChoice = 0; + checkPlaybackStatus(); - for (int seqNum = startSeq; seqNum <= endSeq && !((skipFlag() && allowSkip) || shouldQuit() || (_abortIntroFlag && allowSkip) || _menuChoice); seqNum++) { - _screen->clearPage(0); - _screen->clearPage(8); - _screen->copyPalette(1, 0); - _seqFrameCounter = 0; - _seqStartTime = _system->getMillis(); + for (int i = 0; i < 8; i++) + unloadNestedAnimation(i); + + if (_vm->gameFlags().isDemo && !_vm->gameFlags().isTalkie) + _screen->fadeToBlack(); - allowSkip = (_flags.isDemo && !_flags.isTalkie) || seqNum != kSequenceTitle; + if (!_result) + delayTicks(75); - Sequence cseq = _sequences->seq[seqNum]; - SeqProc cb = _callbackS[seqNum]; + _screen->setCurPage(oldPage); + _screen->_charWidth = 0; + _screen->showMouse(); +} - if (cseq.flags & 2) { - _screen->loadBitmap(cseq.cpsFile, 2, 2, &_screen->getPalette(0)); +void SeqPlayer_HOF::playScenes() { + _vm->sound()->stopAllSoundEffects(); + _curScene = _firstScene; + + _screen->copyPalette(1, 0); + WSAMovie_v2 anim(_vm); + _abortRequested = false; + + _scrollProgressCounter = 0; + + while (!_vm->shouldQuit()) { + if (checkAbortPlayback()) + if (checkPlaybackStatus()) + break; + + _callbackCurrentFrame = 0; + + if (_curScene > _lastScene) + break; + + const Kyra::HoFSequence &sq = _config->seq[_curScene]; + + if (sq.flags & 2) { + _screen->loadBitmap(sq.cpsFile, 2, 2, &_screen->getPalette(0)); _screen->setScreenPalette(_screen->getPalette(0)); } else { _screen->setCurPage(2); _screen->clearPage(2); - _screen->loadPalette("goldfont.col", _screen->getPalette(0)); + _screen->loadPalette("GOLDFONT.COL", _screen->getPalette(0)); } - if (cb && !(_flags.isDemo && !_flags.isTalkie)) - (this->*cb)(0, 0, 0, -1); + if (_config->seqProc[_curScene] && !(_vm->gameFlags().isDemo && !_vm->gameFlags().isTalkie)) + (this->*_config->seqProc[_curScene])(0, 0, 0, -1); - if (cseq.flags & 1) { - _seqWsa->close(); - _seqWsa->open(cseq.wsaFile, 0, &_screen->getPalette(0)); - _screen->setScreenPalette(_screen->getPalette(0)); - _seqWsa->displayFrame(0, 2, cseq.xPos, cseq.yPos, 0, 0, 0); + if (sq.flags & 1) { + anim.open(sq.wsaFile, 0, &_screen->getPalette(0)); + if (!(sq.flags & 2)) + anim.displayFrame(0, 2, sq.xPos, sq.yPos, 0x4000, 0, 0); } - if (cseq.flags & 4) { + if (sq.flags & 4) { int cp = _screen->setCurPage(2); - Screen::FontId cf = _screen->setFont(_flags.lang == Common::JA_JPN ? Screen::FID_SJIS_FNT : Screen::FID_GOLDFONT_FNT); - if (cseq.stringIndex1 != -1) { - int sX = (320 - _screen->getTextWidth(_sequenceStrings[cseq.stringIndex1])) / 2; - _screen->printText(_sequenceStrings[cseq.stringIndex1], sX, 100 - _screen->getFontHeight(), 1, 0); - } - if (cseq.stringIndex2 != -1) { - int sX = (320 - _screen->getTextWidth(_sequenceStrings[cseq.stringIndex2])) / 2; - _screen->printText(_sequenceStrings[cseq.stringIndex2], sX, 100, 1, 0); - } + Screen::FontId cf = _screen->setFont(_vm->gameFlags().lang == Common::JA_JPN ? Screen::FID_SJIS_FNT : Screen::FID_GOLDFONT_FNT); + + if (sq.stringIndex1 != -1) + _screen->printText(_sequenceStrings[sq.stringIndex1], (320 - _screen->getTextWidth(_sequenceStrings[sq.stringIndex1])) / 2, 100 - _screen->getFontHeight(), 1, 0); + + if (sq.stringIndex2 != -1) + _screen->printText(_sequenceStrings[sq.stringIndex2], (320 - _screen->getTextWidth(_sequenceStrings[sq.stringIndex2])) / 2, 100, 1, 0); + _screen->setFont(cf); _screen->setCurPage(cp); } @@ -118,235 +683,1107 @@ void KyraEngine_HoF::seq_playSequences(int startSeq, int endSeq) { _screen->copyPage(2, 10); _screen->copyPage(12, 2); - seq_sequenceCommand(cseq.startupCommand); + doTransition(sq.fadeInTransitionType); - if (!((skipFlag() && allowSkip) || shouldQuit() || (_abortIntroFlag && allowSkip) || _menuChoice)) { + if (!(checkAbortPlayback() || _vm->shouldQuit() || _result)) { _screen->copyPage(2, 0); _screen->updateScreen(); } - if (cseq.flags & 1) { - int x = cseq.xPos; - int y = cseq.yPos; + //_screen->copyPage(2, 6); - _seqFrameDelay = cseq.frameDelay; + if (sq.flags & 1) { + playAnimation(&anim, sq.startFrame, sq.numFrames, sq.duration, sq.xPos, sq.yPos, _config->seqProc[_curScene], &_screen->getPalette(1), &_screen->getPalette(0), 30, 0); + anim.close(); + } else { + _animDuration = sq.duration; + setCountDown(_animDuration); - if (_seqWsa) { - if (x < 0) { - _seqWsa->setWidth(_seqWsa->width() + x); - x = 0; - } + while (!checkAbortPlayback() && !_vm->shouldQuit() && (countDownRunning() || _updateAnimations)) { + uint32 endFrame = (_system->getMillis() + _vm->tickLength()) & ~(_vm->tickLength() - 1); + updateAllNestedAnimations(); - if (y < 0) { - _seqWsa->setHeight(_seqWsa->height() + y); - y = 0; - } + if (_config->seqProc[_curScene]) + (this->*_config->seqProc[_curScene])(0, 0, 0, 0); - if (cseq.xPos + _seqWsa->width() > 319) - _seqWsa->setWidth(320 - cseq.xPos); + updateSubTitles(); - if (cseq.yPos + _seqWsa->height() > 199) - _seqWsa->setHeight(199 - cseq.yPos); - } - uint8 dir = (cseq.startFrame > cseq.numFrames) ? 0 : 1; - _seqWsaCurrentFrame = cseq.startFrame; + _screen->copyPage(2, 0); + _screen->updateScreen(); + _screen->copyPage(12, 2); - bool loop = true; - while (loop && !((skipFlag() && allowSkip) || shouldQuit() || (_abortIntroFlag && allowSkip) || _menuChoice)) { - _seqEndTime = _system->getMillis() + _seqFrameDelay * _tickLength; + do { + if (checkAbortPlayback()) + if (checkPlaybackStatus()) + break; + } while (_system->getMillis() < endFrame); + } + } - if (_seqWsa || !cb) - _screen->copyPage(12, 2); + if (_config->seqProc[_curScene] && !(_vm->gameFlags().isDemo && !_vm->gameFlags().isTalkie)) + (this->*_config->seqProc[_curScene])(0, 0, 0, -2); - if (cb) { - int f = _seqWsaCurrentFrame % _seqWsa->frames(); - (this->*cb)(_seqWsa, cseq.xPos, cseq.yPos, f); - } + uint32 textTimeOut = ticksTillSubTitlesTimeout(); + setCountDown(sq.timeout < textTimeOut ? textTimeOut : sq.timeout); - if (_seqWsa) { - int f = _seqWsaCurrentFrame % _seqWsa->frames(); - _seqWsa->displayFrame(f, 2, cseq.xPos, cseq.yPos, 0, 0, 0); - } + while (!checkAbortPlayback() && !_vm->shouldQuit() && (countDownRunning() || _updateAnimations)) { + updateAllNestedAnimations(); + _screen->copyPage(2, 0); + _screen->updateScreen(); + _screen->copyPage(12, 2); + } - _screen->copyPage(2, 12); + doTransition(sq.fadeOutTransitionType); + _curScene++; + } - seq_processWSAs(); - seq_processText(); + resetAllTextSlots(); + _vm->sound()->haltTrack(); + _vm->sound()->voiceStop(); - if ((_seqWsa || !cb) && !((skipFlag() && allowSkip) || shouldQuit() || (_abortIntroFlag && allowSkip) || _menuChoice)) { - _screen->copyPage(2, 0); - _screen->updateScreen(); - } + if ((!checkAbortPlayback() || _vm->shouldQuit()) && _vm->gameFlags().isDemo) + _curScene = -1; +} - bool loop2 = true; - while (loop2 && !((skipFlag() && allowSkip) || shouldQuit() || (_abortIntroFlag && allowSkip) || _menuChoice)) { - if (_seqWsa) { - seq_processText(); - if (!((skipFlag() && allowSkip) || shouldQuit() || (_abortIntroFlag && allowSkip) || _menuChoice)) { - _screen->copyPage(2, 0); - _screen->updateScreen(); - } +bool SeqPlayer_HOF::checkAbortPlayback() { + Common::Event event; - uint32 now = _system->getMillis(); - if (now >= _seqEndTime) { - loop2 = false; - } else { - uint32 tdiff = _seqEndTime - now; - uint32 dly = tdiff < _tickLength ? tdiff : _tickLength; - delay(dly); - } - } else { - loop = loop2 = false; - } - } + if (_vm->skipFlag()) { + _abortRequested = true; + _vm->resetSkipFlag(); + } - if (loop) { - if (dir == 1) { - if (++_seqWsaCurrentFrame >= cseq.numFrames) - loop = false; - } else { - if (--_seqWsaCurrentFrame < cseq.numFrames) - loop = false; - } - } + if (_abortRequested) + return true; + + while (_system->getEventManager()->pollEvent(event)) { + switch (event.type) { + case Common::EVENT_KEYDOWN: + if (event.kbd.keycode == Common::KEYCODE_q && event.kbd.hasFlags(Common::KBD_CTRL)) { + _abortRequested = true; + _vm->quitGame(); + return true; + } else if (event.kbd.keycode != Common::KEYCODE_ESCAPE && event.kbd.keycode != Common::KEYCODE_RETURN && event.kbd.keycode != Common::KEYCODE_SPACE) { + continue; } - _seqWsa->close(); - } else { - _seqFrameDelay = cseq.frameDelay; - _seqEndTime = _system->getMillis() + _seqFrameDelay * _tickLength; - while (!((skipFlag() && allowSkip) || shouldQuit() || (_abortIntroFlag && allowSkip) || _menuChoice)) { - _seqSubFrameStartTime = _system->getMillis(); - seq_processWSAs(); - if (cb) - (this->*cb)(0, 0, 0, 0); + // fall through + case Common::EVENT_LBUTTONDOWN: + case Common::EVENT_RBUTTONDOWN: + case Common::EVENT_LBUTTONUP: + case Common::EVENT_RBUTTONUP: + _abortRequested = true; + return true; + default: + break; + } + } - seq_processText(); + return false; +} - _screen->copyPage(2, 0); - _screen->updateScreen(); - _screen->copyPage(12, 2); +bool SeqPlayer_HOF::checkPlaybackStatus() { + _updateAnimations = false; - uint32 now = _system->getMillis(); - if (now >= _seqEndTime && !_seqSubframePlaying) - break; + if (_curScene <= _preventSkipBeforeScene || (_curScene == _loopStartScene && !_isFinale)) { + _abortRequested = false; + return false; + } - uint32 tdiff = _seqEndTime - _seqSubFrameStartTime; - int32 dly = _tickLength - (now - _seqSubFrameStartTime); - if (dly > 0) - delay(MIN<uint32>(dly, tdiff)); - else - updateInput(); - } + if (_loopStartScene == kSequenceNoLooping) { + doTransition(0); + fadeOutMusic(); + _abortPlayback = true; + } else { + return true; + } + + return false; +} + +void SeqPlayer_HOF::doTransition(int type) { + for (int i = 0; i < 8; i++) + closeNestedAnimation(i); + + switch (type) { + case 0: + _screen->fadeToBlack(36); + _screen->getPalette(0).clear(); + _screen->getPalette(1).clear(); + break; + + case 1: + playSoundAndDisplaySubTitle(_vm->_rnd.getRandomBit()); + + _screen->getPalette(0).fill(0, 256, 0x3F); + _screen->fadePalette(_screen->getPalette(0), 16); + + _screen->copyPalette(1, 0); + break; + + case 3: + _screen->copyPage(2, 0); + _screen->fadePalette(_screen->getPalette(0), 16); + _screen->copyPalette(1, 0); + break; + + case 4: + _screen->copyPage(2, 0); + _screen->fadePalette(_screen->getPalette(0), 36); + _screen->copyPalette(1, 0); + break; + + case 5: + _screen->copyPage(2, 0); + break; + + case 6: + // UNUSED + // seq_loadBLD("library.bld"); + break; + + case 7: + // UNUSED + // seq_loadBLD("marco.bld"); + break; + + case 8: + _screen->fadeToBlack(16); + _screen->getPalette(0).clear(); + _screen->getPalette(1).clear(); + + delayTicks(120); + break; + + case 9: { + Palette &pal = _screen->getPalette(0); + for (int i = 0; i < 255; i++) + pal.fill(i, 1, (pal[3 * i] + pal[3 * i + 1] + pal[3 * i + 2]) / 3); + pal.fill(255, 1, 0x3F); + + _screen->fadePalette(pal, 64); + _screen->copyPalette(1, 0); + } break; + + default: + break; + } +} + +void SeqPlayer_HOF::nestedFrameAnimTransition(int srcPage, int dstPage, int delaytime, int steps, int x, int y, int w, int h, int openClose, int directionFlags) { + if (openClose) { + for (int i = 1; i < steps; i++) { + uint32 endtime = _system->getMillis() + delaytime * _vm->tickLength(); + + int w2 = (((w * 256) / steps) * i) / 256; + int h2 = (((h * 256) / steps) * i) / 256; + + int ym = (directionFlags & 2) ? (h - h2) : 0; + int xm = (directionFlags & 1) ? (w - w2) : 0; + + _screen->wsaFrameAnimationStep(0, 0, x + xm, y + ym, w, h, w2, h2, srcPage, dstPage, 0); + + _screen->copyPage(dstPage, 6); + _screen->copyPage(dstPage, 0); + _screen->updateScreen(); + + _screen->copyPage(12, dstPage); + delayUntil(endtime); } - if (cb && !(_flags.isDemo && !_flags.isTalkie)) - (this->*cb)(0, 0, 0, -2); + _screen->wsaFrameAnimationStep(0, 0, x, y, w, h, w, h, srcPage, dstPage, 0); + _screen->copyPage(dstPage, 6); + _screen->copyPage(dstPage, 0); + _screen->updateScreen(); + } else { + _screen->copyPage(12, dstPage); + for (int i = steps; i; i--) { + uint32 endtime = _system->getMillis() + delaytime * _vm->tickLength(); + + int w2 = (((w * 256) / steps) * i) / 256; + int h2 = (((h * 256) / steps) * i) / 256; + + int ym = (directionFlags & 2) ? (h - h2) : 0; + int xm = (directionFlags & 1) ? (w - w2) : 0; - uint32 ct = seq_activeTextsTimeLeft(); - uint32 dl = cseq.duration * _tickLength; - if (dl < ct) - dl = ct; - _seqEndTime = _system->getMillis() + dl; + _screen->wsaFrameAnimationStep(0, 0, x + xm, y + ym, w, h, w2, h2, srcPage, dstPage, 0); - while (!((skipFlag() && allowSkip) || shouldQuit() || (_abortIntroFlag && allowSkip) || _menuChoice)) { - _seqSubFrameStartTime = _system->getMillis(); - seq_processWSAs(); + _screen->copyPage(dstPage, 6); + _screen->copyPage(dstPage, 0); + _screen->updateScreen(); + _screen->copyPage(12, dstPage); + delayUntil(endtime); + } + } +} + +void SeqPlayer_HOF::nestedFrameFadeTransition(const char *cmpFile) { + _screen->copyPage(10, 2); + _screen->copyPage(4, 10); + _screen->clearPage(6); + _screen->loadBitmap(cmpFile, 6, 6, 0); + _screen->copyPage(12, 4); + + for (int i = 0; i < 3; i++) { + uint32 endtime = _system->getMillis() + 4 * _vm->tickLength(); + assert(_screenHoF); + _screenHoF->cmpFadeFrameStep(4, 320, 200, 0, 0, 2, 320, 200, 0, 0, 320, 200, 6); + _screen->copyRegion(0, 0, 0, 0, 320, 200, 2, 0); + _screen->updateScreen(); + delayUntil(endtime); + } + + _screen->copyPage(4, 0); + _screen->updateScreen(); + _screen->copyPage(4, 2); + _screen->copyPage(4, 6); + _screen->copyPage(10, 4); +} + +void SeqPlayer_HOF::playAnimation(WSAMovie_v2 *wsaObj, int startFrame, int lastFrame, int frameRate, int x, int y, const SeqProc callback, Palette *fadePal1, Palette *fadePal2, int fadeRate, bool restoreScreen) { + bool finished = false; + uint32 startTime = _system->getMillis(); + + int origW = wsaObj->width(); + int origH = wsaObj->width(); + int drwX = x; + int drwY = y; + int drwW = origW; + int drwH = origH; + + _animDuration = frameRate; + + if (wsaObj) { + if (x < 0) { + drwW += x; + drwX = 0; + } + + if (y < 0) { + drwH += y; + drwY = 0; + } + + if (x + origW > 319) + origW = 320 - x; + + if (y + origH > 199) + origW = 200 - y; + } + + int8 frameStep = (startFrame > lastFrame) ? -1 : 1; + _animCurrentFrame = startFrame; + + while (!_vm->shouldQuit() && !finished) { + if (checkAbortPlayback()) + if (checkPlaybackStatus()) + break; + + setCountDown(_animDuration); + + if (wsaObj || callback) + _screen->copyPage(12, 2); + + int frameIndex = _animCurrentFrame; + if (wsaObj) + frameIndex %= wsaObj->frames(); + + if (callback) + (this->*callback)(wsaObj, x, y, frameIndex); + + if (wsaObj) + wsaObj->displayFrame(frameIndex, 2, x, y, 0, 0, 0); + + _screen->copyPage(2, 12); + + updateAllNestedAnimations(); + updateSubTitles(); + + if ((wsaObj || callback) && (!(checkAbortPlayback() || _vm->shouldQuit() || _result))) { _screen->copyPage(2, 0); _screen->updateScreen(); + } + + while (!_vm->shouldQuit()) { + if (checkAbortPlayback()) + if (checkPlaybackStatus()) + break; + + if (fadePal1 && fadePal2) { + if (!_screen->timedPaletteFadeStep(fadePal1->getData(), fadePal2->getData(), _system->getMillis() - startTime, fadeRate * _vm->tickLength()) && !wsaObj) + break; + } + + if ((wsaObj || callback) && (!(checkAbortPlayback() || _vm->shouldQuit() || _result))) { + _screen->copyPage(2, 0); + _screen->updateScreen(); + } + + updateSubTitles(); + + if (!countDownRunning()) + break; + } + + if (wsaObj) { + _animCurrentFrame += frameStep; + if ((frameStep > 0 && _animCurrentFrame >= lastFrame) || (frameStep < 0 && _animCurrentFrame < lastFrame)) + finished = true; + } + + if (restoreScreen && (wsaObj || callback)) { _screen->copyPage(12, 2); + _screen->copyRegion(drwX, drwY, drwX, drwY, drwW, drwH, 2, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + } + } +} + +void SeqPlayer_HOF::playDialogueAnimation(uint16 strID, uint16 soundID, int textColor, int textPosX, int textPosY, int textWidth, WSAMovie_v2 *wsaObj, int animStartFrame, int animLastFrame, int animPosX, int animPosY) { + int dur = int(strlen(_sequenceStrings[strID])) * (_vm->gameFlags().isTalkie ? 7 : 15); + if (_vm->textEnabled()) { + int slot = displaySubTitle(strID, textPosX, textPosY, dur, textWidth); + _textSlots[slot].textcolor = textColor; + } + _specialAnimTimeOutTotal = _system->getMillis() + dur * _vm->tickLength(); + int curframe = animStartFrame; + + if (soundID && _vm->speechEnabled()) { + while (_vm->sound()->voiceIsPlaying() && !_abortPlayback) + delayTicks(1); + playSoundAndDisplaySubTitle(soundID); + } + + while (_system->getMillis() < _specialAnimTimeOutTotal && !_abortPlayback) { + if (animLastFrame < 0) { + int t = ABS(animLastFrame); + if (t < curframe) + curframe = t; + } + + if (ABS(animLastFrame) < curframe) + curframe = animStartFrame; - uint32 now = _system->getMillis(); - if (now >= _seqEndTime && !_seqSubframePlaying) { + _specialAnimFrameTimeOut = _system->getMillis() + _animDuration * _vm->tickLength(); + setCountDown(_animDuration); + + if (wsaObj) + wsaObj->displayFrame(curframe % wsaObj->frames(), 2, animPosX, animPosY, 0, 0, 0); + + _screen->copyPage(2, 12); + updateSubTitles(); + delayUntil(MIN(_specialAnimFrameTimeOut, _specialAnimTimeOutTotal)); + + if (_vm->speechEnabled() && !_vm->textEnabled() && !_vm->snd_voiceIsPlaying()) + break; + + if (checkAbortPlayback()) + if (checkPlaybackStatus()) break; - } - uint32 tdiff = _seqEndTime - _seqSubFrameStartTime; - int32 dly = _tickLength - (now - _seqSubFrameStartTime); - if (dly > 0) - delay(MIN<uint32>(dly, tdiff)); + _screen->copyPage(2, 0); + _screen->updateScreen(); + curframe++; + } + + if (_abortPlayback) + _vm->sound()->voiceStop(); + + if (ABS(animLastFrame) < curframe) + curframe = ABS(animLastFrame); + + if (curframe == animStartFrame) + curframe++; + + _animCurrentFrame = curframe; +} + +void SeqPlayer_HOF::startNestedAnimation(int animSlot, int sequenceID) { + if (_animSlots[animSlot].flags != -1) + return; + + if (_target == kLoLDemo) { + return; + } else if (_target == kHoFDemo) { + assert(sequenceID >= kNestedSequenceHoFDemoWharf2); + sequenceID -= kNestedSequenceHoFDemoWharf2; + } + + HoFNestedSequence s = _config->nestedSeq[sequenceID]; + + if (!_animSlots[animSlot].movie) { + _animSlots[animSlot].movie = new WSAMovie_v2(_vm); + assert(_animSlots[animSlot].movie); + } + + _animSlots[animSlot].movie->close(); + + _animSlots[animSlot].movie->open(s.wsaFile, 0, 0); + + if (!_animSlots[animSlot].movie->opened()) { + delete _animSlots[animSlot].movie; + _animSlots[animSlot].movie = 0; + return; + } + + _animSlots[animSlot].endFrame = s.endFrame; + _animSlots[animSlot].startFrame = _animSlots[animSlot].currentFrame = s.startframe; + _animSlots[animSlot].frameDelay = s.frameDelay; + _animSlots[animSlot].callback = _config->nestedSeqProc[sequenceID]; + _animSlots[animSlot].control = s.wsaControl; + + _animSlots[animSlot].flags = s.flags | 1; + _animSlots[animSlot].x = s.x; + _animSlots[animSlot].y = s.y; + _animSlots[animSlot].fadeInTransitionType = s.fadeInTransitionType; + _animSlots[animSlot].fadeOutTransitionType = s.fadeOutTransitionType; + _animSlots[animSlot].lastFrame = 0xFFFF; + + doNestedFrameTransition(s.fadeInTransitionType, animSlot); + + if (!s.fadeInTransitionType) + updateNestedAnimation(animSlot); + + _animSlots[animSlot].nextFrame = _system->getMillis() & ~(_vm->tickLength() - 1); +} + +void SeqPlayer_HOF::closeNestedAnimation(int animSlot) { + if (_animSlots[animSlot].flags == -1) + return; + + _animSlots[animSlot].flags = -1; + doNestedFrameTransition(_animSlots[animSlot].fadeOutTransitionType, animSlot); + _animSlots[animSlot].movie->close(); +} + +void SeqPlayer_HOF::unloadNestedAnimation(int animSlot) { + if (_animSlots[animSlot].movie) { + _animSlots[animSlot].movie->close(); + delete _animSlots[animSlot].movie; + _animSlots[animSlot].movie = 0; + } +} + +void SeqPlayer_HOF::doNestedFrameTransition(int transitionType, int animSlot) { + int xa = 0, ya = 0; + transitionType--; + if (!_animSlots[animSlot].movie || _abortPlayback || _vm->shouldQuit()) + return; + + switch (transitionType) { + case 0: + xa = -_animSlots[animSlot].movie->xAdd(); + ya = -_animSlots[animSlot].movie->yAdd(); + _animSlots[animSlot].movie->displayFrame(0, 8, xa, ya, 0, 0, 0); + nestedFrameAnimTransition(8, 2, 7, 8, _animSlots[animSlot].movie->xAdd(), _animSlots[animSlot].movie->yAdd(), + _animSlots[animSlot].movie->width(), _animSlots[animSlot].movie->height(), 1, 2); + break; + + case 1: + xa = -_animSlots[animSlot].movie->xAdd(); + ya = -_animSlots[animSlot].movie->yAdd(); + _animSlots[animSlot].movie->displayFrame(0, 8, xa, ya, 0, 0, 0); + nestedFrameAnimTransition(8, 2, 7, 8, _animSlots[animSlot].movie->xAdd(), _animSlots[animSlot].movie->yAdd(), + _animSlots[animSlot].movie->width(), _animSlots[animSlot].movie->height(), 1, 1); + break; + + case 2: + waitForSubTitlesTimeout(); + xa = -_animSlots[animSlot].movie->xAdd(); + ya = -_animSlots[animSlot].movie->yAdd(); + _animSlots[animSlot].movie->displayFrame(21, 8, xa, ya, 0, 0, 0); + nestedFrameAnimTransition(8, 2, 7, 8, _animSlots[animSlot].movie->xAdd(), _animSlots[animSlot].movie->yAdd(), + _animSlots[animSlot].movie->width(), _animSlots[animSlot].movie->height(), 0, 2); + break; + + case 3: + _screen->copyPage(2, 10); + _animSlots[animSlot].movie->displayFrame(0, 2, 0, 0, 0, 0, 0); + _screen->copyPage(2, 12); + nestedFrameFadeTransition("scene2.cmp"); + break; + + case 4: + _screen->copyPage(2, 10); + _animSlots[animSlot].movie->displayFrame(0, 2, 0, 0, 0, 0, 0); + _screen->copyPage(2, 12); + nestedFrameFadeTransition("scene3.cmp"); + break; + + default: + break; + } +} + +void SeqPlayer_HOF::updateAllNestedAnimations() { + for (int i = 0; i < 8; i++) { + if (_animSlots[i].flags != -1) { + if (updateNestedAnimation(i)) + closeNestedAnimation(i); + } + } +} + +bool SeqPlayer_HOF::updateNestedAnimation(int animSlot) { + uint16 currentFrame = _animSlots[animSlot].currentFrame; + uint32 curTick = _system->getMillis() & ~(_vm->tickLength() - 1); + + if (_animSlots[animSlot].callback && currentFrame != _animSlots[animSlot].lastFrame) { + _animSlots[animSlot].lastFrame = currentFrame; + currentFrame = (this->*_animSlots[animSlot].callback)(_animSlots[animSlot].movie, _animSlots[animSlot].x, _animSlots[animSlot].y, currentFrame); + } + + if (_animSlots[animSlot].movie) { + if (_animSlots[animSlot].flags & 0x20) { + _animSlots[animSlot].movie->displayFrame(_animSlots[animSlot].control[currentFrame].index, 2, _animSlots[animSlot].x, _animSlots[animSlot].y, 0x4000, 0, 0); + _animSlots[animSlot].frameDelay = _animSlots[animSlot].control[currentFrame].delay; + } else { + _animSlots[animSlot].movie->displayFrame(currentFrame % _animSlots[animSlot].movie->frames(), 2, _animSlots[animSlot].x, _animSlots[animSlot].y, 0x4000, 0, 0); + } + } + + if (_animSlots[animSlot].flags & 0x10) { + currentFrame = (curTick - _animSlots[animSlot].nextFrame) / (_animSlots[animSlot].frameDelay * _vm->tickLength()); + } else { + int diff = (curTick - _animSlots[animSlot].nextFrame) / (_animSlots[animSlot].frameDelay * _vm->tickLength()); + if (diff > 0) { + currentFrame++; + if (_vm->gameFlags().platform == Common::kPlatformFMTowns || _vm->gameFlags().platform == Common::kPlatformPC98) + _animSlots[animSlot].nextFrame += ((curTick - _animSlots[animSlot].nextFrame) * 2 / 3); else - updateInput(); + _animSlots[animSlot].nextFrame = curTick; } + } - seq_sequenceCommand(cseq.finalCommand); - seq_resetAllTextEntries(); + bool res = false; + + if (currentFrame >= _animSlots[animSlot].endFrame) { + int sw = ((_animSlots[animSlot].flags & 0x1E) - 2); + switch (sw) { + case 0: + res = true; + currentFrame = _animSlots[animSlot].endFrame; + _screen->copyPage(2, 12); + break; + + case 6: + case 8: + currentFrame = _animSlots[animSlot].endFrame - 1; + break; - if (_abortIntroFlag || skipFlag()) { - _sound->haltTrack(); - _sound->voiceStop(); + case 2: + case 10: + currentFrame = _animSlots[animSlot].startFrame; + break; + + default: + currentFrame = _animSlots[animSlot].endFrame - 1; + res = true; } + } + + _animSlots[animSlot].currentFrame = currentFrame; + return res; +} - if (!_flags.isDemo || _flags.isTalkie) { - if ((seqNum != kSequenceTitle && seqNum < kSequenceZanfaun && - (_abortIntroFlag || skipFlag())) || seqNum == kSequenceZanfaun) { - _abortIntroFlag = false; - _eventList.clear(); - seqNum = kSequenceWestwood; - } else if (seqNum < kSequenceFrash && (_abortIntroFlag || skipFlag())) { - _abortIntroFlag = false; - _eventList.clear(); - seqNum = kSequenceFirates; +void SeqPlayer_HOF::playSoundEffect(uint16 id, int16 vol) { + assert(id < _sequenceSoundListSize); + _vm->sound()->voicePlay(_sequenceSoundList[id], 0, vol); +} + +void SeqPlayer_HOF::playSoundAndDisplaySubTitle(uint16 id) { + assert(id < _sequenceSoundListSize); + + if (id < 12 && !_vm->gameFlags().isDemo && _vm->textEnabled()) + displaySubTitle(id, 160, 168, _textDuration[id], 160); + + _vm->sound()->voicePlay(_sequenceSoundList[id], 0); +} + +void SeqPlayer_HOF::printFadingText(uint16 strID, int x, int y, const uint8 *colorMap, uint8 textcolor) { + uint8 cmap[16]; + + if (checkAbortPlayback()) + checkPlaybackStatus(); + + if (_abortPlayback || _abortRequested || _vm->shouldQuit() || _result) + return; + + Screen::FontId of = _screen->setFont(Screen::FID_8_FNT); + _screen->getPalette(0).fill(254, 2, 63); + _screen->setPaletteIndex(252, 63, 32, 48); + cmap[0] = colorMap[0]; + cmap[1] = 253; + memcpy(&cmap[2], &colorMap[2], 14); + uint8 col0 = _textColor[0]; + + _textColor[0] = 253; + _screen->setTextColorMap(cmap); + resetAllTextSlots(); + displaySubTitle(strID, x, y, 128, 120); + updateSubTitles(); + _screen->copyPage(2, 0); + _screen->updateScreen(); + _screen->getPalette(0).copy(_screen->getPalette(0), textcolor, 1, 253); + _screen->fadePalette(_screen->getPalette(0), 24); + + _textColor[0] = textcolor; + _screen->setTextColorMap(colorMap); + resetAllTextSlots(); + displaySubTitle(strID, x, y, 128, 120); + updateSubTitles(); + _screen->copyPage(2, 0); + _screen->updateScreen(); + _screen->getPalette(0).fill(253, 1, 0); + _screen->fadePalette(_screen->getPalette(0), 1); + + _screen->copyPage(2, 12); + resetAllTextSlots(); + + _textColor[0] = col0; + + _screen->setFont(of); +} + +int SeqPlayer_HOF::displaySubTitle(uint16 strIndex, uint16 posX, uint16 posY, int duration, uint16 width) { + for (int i = 0; i < 10; i++) { + if (_textSlots[i].duration != -1) { + if (i < 9) + continue; + else + return -1; + } + + _textSlots[i].strIndex = strIndex; + _textSlots[i].x = posX; + _textSlots[i].y = posY; + _textSlots[i].duration = duration * _vm->tickLength(); + _textSlots[i].width = width; + _textSlots[i].startTime = _system->getMillis(); + _textSlots[i].textcolor = -1; + + return i; + } + return -1; +} + +void SeqPlayer_HOF::updateSubTitles() { + int curPage = _screen->setCurPage(2); + char outputStr[70]; + + for (int i = 0; i < 10; i++) { + if (_textSlots[i].startTime + _textSlots[i].duration > _system->getMillis() && _textSlots[i].duration != -1) { + + char *srcStr = preprocessString(_sequenceStrings[_textSlots[i].strIndex], _textSlots[i].width); + int yPos = _textSlots[i].y; + + while (*srcStr) { + uint32 linePos = 0; + for (; *srcStr; linePos++) { + if (*srcStr == '\r') + break; + outputStr[linePos] = *srcStr; + srcStr++; + } + outputStr[linePos] = 0; + if (*srcStr == '\r') + srcStr++; + + uint8 textColor = (_textSlots[i].textcolor >= 0) ? _textSlots[i].textcolor : _textColor[0]; + _screen->printText(outputStr, _textSlots[i].x - (_screen->getTextWidth(outputStr) / 2), yPos, textColor, 0); + yPos += 10; } - } else if (seqNum == endSeq && !(_abortIntroFlag || skipFlag())) { - seqNum = 0; + } else { + _textSlots[i].duration = -1; + } + } + + _screen->setCurPage(curPage); +} + +char *SeqPlayer_HOF::preprocessString(const char *srcStr, int width) { + char *dstStr = _tempString; + int lineStart = 0; + int linePos = 0; + + while (*srcStr) { + while (*srcStr && *srcStr != ' ') + dstStr[lineStart + linePos++] = *srcStr++; + dstStr[lineStart + linePos] = 0; + + int len = _screen->getTextWidth(&dstStr[lineStart]); + if (width >= len && *srcStr) { + dstStr[lineStart + linePos++] = *srcStr++; + } else { + dstStr[lineStart + linePos] = '\r'; + lineStart += linePos + 1; + linePos = 0; + if (*srcStr) + srcStr++; } + } + dstStr[lineStart + linePos] = 0; - if (_menuChoice) { - _abortIntroFlag = false; - _eventList.clear(); + return strlen(_tempString) ? dstStr : 0; +} + +void SeqPlayer_HOF::waitForSubTitlesTimeout() { + uint32 timeOut = _system->getMillis() + ticksTillSubTitlesTimeout() * _vm->tickLength(); - if (_menuChoice == 2) { - seqNum = kSequenceTitle; - _menuChoice = 0; + if (_vm->textEnabled()) { + delayUntil(timeOut); + } else if (_vm->speechEnabled()) { + while (_vm->snd_voiceIsPlaying()) + delayTicks(1); + } + + resetAllTextSlots(); +} + +uint32 SeqPlayer_HOF::ticksTillSubTitlesTimeout() { + uint32 longest = 0; + + for (int i = 0; i < 10; i++) { + uint32 timeOut = (_textSlots[i].duration + _textSlots[i].startTime); + uint32 curtime = _system->getMillis(); + if (_textSlots[i].duration != -1 && timeOut > curtime) { + timeOut -= curtime; + if (longest < timeOut) + longest = timeOut; + } + } + + uint32 tl = _vm->tickLength(); + return (longest + (tl - 1)) / tl; +} + +void SeqPlayer_HOF::resetAllTextSlots() { + for (int i = 0; i < 10; i++) + _textSlots[i].duration = -1; +} + +void SeqPlayer_HOF::fadeOutMusic() { + _vm->sound()->beginFadeOut(); + delayTicks(80); +} + +void SeqPlayer_HOF::playHoFTalkieCredits() { + static const uint8 colormap[] = {0, 0, 102, 102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + static const ScreenDim d = { 0x00, 0x0C, 0x28, 0xB4, 0xFF, 0x00, 0x00, 0x00 }; + + _screen->loadBitmap("finale.cps", 3, 3, &_screen->getPalette(0)); + _screen->setFont(Screen::FID_GOLDFONT_FNT); + + int talkieCreditsSize, talkieCreditsSpecialSize; + const uint8 *talkieCredits = _vm->staticres()->loadRawData(k2SeqplayCredits, talkieCreditsSize); + const char *const *talkieCreditsSpecial = _vm->staticres()->loadStrings(k2SeqplayCreditsSpecial, talkieCreditsSpecialSize); + + _vm->sound()->selectAudioResourceSet(kMusicIngame); + _vm->sound()->loadSoundFile(3); + _vm->sound()->playTrack(3); + + _screen->setTextColorMap(colormap); + _screen->copyRegion(0, 0, 0, 0, 320, 200, 2, 0); + _screen->updateScreen(); + _screen->fadeFromBlack(); + + _screen->_charWidth = -2; + uint8 *dataPtr = new uint8[0xAFD]; + memcpy(dataPtr, talkieCredits, talkieCreditsSize); + _vm->staticres()->unloadId(k2SeqplayCredits); + + displayHoFTalkieScrollText(dataPtr, &d, 2, 6, 5, 1, Screen::FID_GOLDFONT_FNT, Screen::FID_GOLDFONT_FNT, 0, talkieCreditsSpecial); + delayTicks(8); + + delete[] dataPtr; + _vm->staticres()->unloadId(k2SeqplayCreditsSpecial); + _vm->sound()->selectAudioResourceSet(kMusicFinale); + _vm->sound()->loadSoundFile(0); +} + +void SeqPlayer_HOF::displayHoFTalkieScrollText(uint8 *data, const ScreenDim *d, int tempPage1, int tempPage2, int speed, + int step, Screen::FontId fid1, Screen::FontId fid2, const uint8 *shapeData, const char *const *specialData) { + if (!data) + return; + + static const char mark[] = { 5, 13, 0 }; + + _screen->clearPage(tempPage1); + _screen->clearPage(tempPage2); + _screen->copyRegion(d->sx << 3, d->sy, d->sx << 3, d->sy, d->w << 3, d->h, 0, tempPage1); + + struct ScrollTextData { + int16 x; + int16 y; + uint8 *text; + byte unk1; + byte height; + byte adjust; + + ScrollTextData() { + x = 0; // 0 11 + y = 0; // 2 13 + text = 0; // 4 15 + unk1 = 0; // 8 19 + height = 0; // 9 20 + adjust = 0; // 10 21 + } + }; + + ScrollTextData *textData = new ScrollTextData[36]; + uint8 *ptr = data; + + bool loop = true; + int cnt = 0; + + while (loop) { + uint32 loopEnd = _system->getMillis() + speed * _vm->tickLength(); + + while (cnt < 35 && *ptr) { + uint16 cH; + + if (cnt) + cH = textData[cnt].y + textData[cnt].height + (textData[cnt].height >> 3); + else + cH = d->h; + + char *str = (char *)ptr; + + ptr = (uint8 *)strpbrk(str, mark); + if (!ptr) + ptr = (uint8 *)strchr(str, 0); + + textData[cnt + 1].unk1 = *ptr; + *ptr = 0; + if (textData[cnt + 1].unk1) + ptr++; + + if (*str == 3 || *str == 4) + textData[cnt + 1].adjust = *str++; + else + textData[cnt + 1].adjust = 0; + + _screen->setFont(fid1); + + if (*str == 1) { + _screen->setFont(fid2); + str++; + } else if (*str == 2) { + str++; + } + + textData[cnt + 1].height = _screen->getFontHeight(); + + switch (textData[cnt + 1].adjust) { + case 3: + textData[cnt + 1].x = 157 - _screen->getTextWidth(str); + break; + case 4: + textData[cnt + 1].x = 161; + break; + default: + textData[cnt + 1].x = (((d->w << 3) - _screen->getTextWidth(str)) >> 1) + 1; } + + if (textData[cnt].unk1 == 5) + cH -= (textData[cnt].height + (textData[cnt].height >> 3)); + + textData[cnt + 1].y = cH; + textData[cnt + 1].text = (uint8 *)str; + cnt++; } + + _screen->copyRegion(d->sx << 3, d->sy, d->sx << 3, d->sy, d->w << 3, d->h, tempPage1, tempPage2); + + int cnt2 = 0; + bool palCycle = 0; + + while (cnt2 < cnt) { + const char *str = (const char *)textData[cnt2 + 1].text; + const char *str2 = str; + int16 cW = textData[cnt2 + 1].x - 10; + int16 cH = textData[cnt2 + 1].y; + int x = (d->sx << 3) + cW; + int y = d->sy + cH; + int col1 = 255; + + if (cH < d->h) { + _screen->setCurPage(tempPage2); + _screen->setFont(fid1); + if (textData[cnt2 + 1].height != _screen->getFontHeight()) + _screen->setFont(fid2); + + if (specialData) { + if (!strcmp(str, specialData[0])) { + col1 = 112; + char cChar[2] = " "; + while (*str2) { + cChar[0] = *str2; + _screen->printText(cChar, x, y, col1++, 0); + x += _screen->getCharWidth((uint8)*str2++); + } + palCycle = true; + } else if (!strcmp(str, specialData[1])) { + col1 = 133; + char cChar[2] = " "; + while (*str2) { + cChar[0] = *str2; + _screen->printText(cChar, x, y, col1--, 0); + x += _screen->getCharWidth((uint8)*str2++); + } + palCycle = true; + } else { + _screen->printText(str, x, y, col1, 0); + } + } else { + _screen->printText(str, x, y, col1, 0); + } + _screen->setCurPage(0); + } + + textData[cnt2 + 1].y -= step; + cnt2++; + } + + _screen->copyRegion(d->sx << 3, d->sy, d->sx << 3, d->sy, d->w << 3, d->h, tempPage2, 0); + _screen->updateScreen(); + + if (textData[1].y < -10) { + textData[1].text += strlen((char *)textData[1].text); + textData[1].text[0] = textData[1].unk1; + cnt--; + memcpy(&textData[1], &textData[2], cnt * sizeof(ScrollTextData)); + } + + if (palCycle) { + for (int col = 133; col > 112; col--) + _screen->getPalette(0).copy(_screen->getPalette(0), col - 1, 1, col); + _screen->getPalette(0).copy(_screen->getPalette(0), 133, 1, 112); + _screen->setScreenPalette(_screen->getPalette(0)); + } + + delayUntil(loopEnd); + + if ((cnt < 36) && ((d->sy + d->h) > (textData[cnt].y + textData[cnt].height)) && !_abortPlayback) { + delayTicks(500); + cnt = 0; + } + + if (checkAbortPlayback()) + if (checkPlaybackStatus()) + loop = false; + + if (!cnt || _abortPlayback) + loop = false; } - if (_flags.isDemo && !_flags.isTalkie) { - _eventList.clear(); - _screen->fadeToBlack(); + _vm->sound()->beginFadeOut(); + _screen->fadeToBlack(); + + _abortPlayback = _abortRequested = false; + + delete[] textData; +} + +void SeqPlayer_HOF::updateDemoAdText(int bottom, int top) { + int dstY, dstH, srcH; + + static const ScreenDim d = { 0x00, 0x00, 0x28, 0x320, 0xFF, 0xFE, 0x00, 0x00 }; + + if (_scrollProgressCounter - (top - 1) < 0) { + dstY = top - _scrollProgressCounter; + dstH = _scrollProgressCounter; + srcH = 0; + } else { + dstY = 0; + srcH = _scrollProgressCounter - top; + dstH = (400 - srcH <= top) ? 400 - srcH : top; } - if (!_menuChoice) - delay(1200); + if (dstH > 0) { + if (_hofDemoAnimData) { + for (int i = 0; i < 4; i++) { + const HoFSeqItemAnimData *def = &_hofDemoAnimData[i]; + ActiveItemAnim *a = &_hofDemoActiveItemAnim[i]; - _screen->setCurPage(oldPage); - _screen->showMouse(); + _screen->fillRect(12, def->y - 8, 28, def->y + 8, 0, 4); + _screen->drawShape(4, _hofDemoItemShapes[def->itemIndex + def->frames[a->currentFrame]], 12, def->y - 8, 0, 0); + if (_callbackCurrentFrame % 2 == 0) + a->currentFrame = (a->currentFrame + 1) % 20; + } + } + _screen->copyRegionEx(4, 0, srcH, 2, 2, dstY + bottom, 320, dstH, &d); + } +} - for (int i = 0; i < 8; i++) - seq_unloadWSA(i); +void SeqPlayer_HOF::delayTicks(uint32 ticks) { + uint32 len = ticks * _vm->tickLength(); + while (len && !_vm->shouldQuit() && !checkAbortPlayback()) { + uint32 step = (len >= 10) ? 10 : len; + _system->delayMillis(step); + len -= step; + } +} - _seqWsa->close(); +void SeqPlayer_HOF::delayUntil(uint32 dest) { + for (uint32 ct = _system->getMillis(); ct < dest && !_vm->shouldQuit() && !checkAbortPlayback(); ) { + uint32 step = (dest - ct >= 10) ? 10 : (dest - ct); + _system->delayMillis(step); + ct = _system->getMillis(); + } +} - _screen->_charWidth = 0; +void SeqPlayer_HOF::setCountDown(uint32 ticks) { + _countDownRemainder = ticks * _vm->tickLength(); + if (_vm->gameFlags().platform == Common::kPlatformFMTowns || _vm->gameFlags().platform == Common::kPlatformPC98) + _countDownRemainder = _countDownRemainder * 2 / 3; + _countDownLastUpdate = _system->getMillis() & ~(_vm->tickLength() - 1); +} - seq_uninit(); +bool SeqPlayer_HOF::countDownRunning() { + uint32 cur = _system->getMillis(); + uint32 step = cur - _countDownLastUpdate; + _countDownLastUpdate = cur; + _countDownRemainder = (step <= _countDownRemainder) ? _countDownRemainder - step : 0; + return _countDownRemainder; } -int KyraEngine_HoF::seq_introWestwood(WSAMovie_v2 *wsaObj, int x, int y, int frm) { +#define CASE_ALT(dosCase, towns98Case)\ + case dosCase:\ + case towns98Case:\ + if (!((_callbackCurrentFrame == towns98Case && (_vm->gameFlags().platform == Common::kPlatformFMTowns || _vm->gameFlags().platform == Common::kPlatformPC98)) || (_callbackCurrentFrame == dosCase && _vm->gameFlags().platform == Common::kPlatformPC)))\ + break; + +int SeqPlayer_HOF::cbHOF_westwood(WSAMovie_v2 *wsaObj, int x, int y, int frm) { if (frm == -2) { - if (_flags.platform == Common::kPlatformFMTowns || _flags.platform == Common::kPlatformPC98) - delay(300 * _tickLength); + if (_vm->gameFlags().platform == Common::kPlatformFMTowns || _vm->gameFlags().platform == Common::kPlatformPC98) + delayTicks(300); } else if (!frm) { - _sound->playTrack(2); + _vm->sound()->playTrack(2); } return 0; } -int KyraEngine_HoF::seq_introTitle(WSAMovie_v2 *wsaObj, int x, int y, int frm) { +int SeqPlayer_HOF::cbHOF_title(WSAMovie_v2 *wsaObj, int x, int y, int frm) { if (frm == 1) { - _sound->playTrack(3); - } else if (frm == 25) { + _vm->sound()->playTrack(3); + } else if (frm == 25 && _startupSaveLoadable) { int cp = _screen->setCurPage(0); _screen->showMouse(); _system->updateScreen(); - _menuChoice = _menu->handle(11) + 1; - _seqEndTime = 0; - _seqSubframePlaying = false; - if (_menuChoice == 4) - quitGame(); + _result = _menu->handle(11) + 1; + _updateAnimations = false; + + if (_result == 1) { + _curScene = _lastScene; + _preventLooping = true; + } else { + setCountDown(200); + } + + if (_result == 4) + _vm->quitGame(); _screen->hideMouse(); _screen->setCurPage(cp); @@ -355,31 +1792,29 @@ int KyraEngine_HoF::seq_introTitle(WSAMovie_v2 *wsaObj, int x, int y, int frm) { return 0; } -int KyraEngine_HoF::seq_introOverview(WSAMovie_v2 *wsaObj, int x, int y, int frm) { +int SeqPlayer_HOF::cbHOF_overview(WSAMovie_v2 *wsaObj, int x, int y, int frm) { uint8 *tmpPal = _screen->getPalette(3).getData() + 0x101; memset(tmpPal, 0, 256); - _seqSubFrameEndTimeInternal = 0; - uint32 now = 0; + uint32 frameEnd = 0; - switch (_seqFrameCounter) { + switch (_callbackCurrentFrame) { case 0: - _seqSubframePlaying = true; - _sound->playTrack(4); - _seqSubFrameEndTimeInternal = _system->getMillis() + 60 * _tickLength; + _updateAnimations = true; + fadeOutMusic(); + _vm->sound()->playTrack(4); + frameEnd = _system->getMillis() + 60 * _vm->tickLength(); - _seqTextColor[1] = _screen->findLeastDifferentColor(_seqTextColorPresets, _screen->getPalette(0), 1, 255) & 0xff; - memset(_seqTextColorMap, _seqTextColor[1], 16); - _seqTextColorMap[1] = _seqTextColor[0] = _screen->findLeastDifferentColor(_seqTextColorPresets + 3, _screen->getPalette(0), 1, 255) & 0xff; + _textColor[1] = _screen->findLeastDifferentColor(_textColorPresets, _screen->getPalette(0), 1, 255) & 0xFF; + memset(_textColorMap, _textColor[1], 16); + _textColorMap[1] = _textColor[0] = _screen->findLeastDifferentColor(_textColorPresets + 3, _screen->getPalette(0), 1, 255) & 0xFF; + _screen->setTextColorMap(_textColorMap); - _screen->setTextColorMap(_seqTextColorMap); - - now = _system->getMillis(); - if (_seqSubFrameEndTimeInternal > now) - delay(_seqSubFrameEndTimeInternal - now); + delayUntil(frameEnd); break; case 1: - _screen->generateGrayOverlay(_screen->getPalette(0), _screen->getPalette(3).getData(), 0x40, 0, 0, 0, 0x100, true); + assert(_screenHoF); + _screenHoF->generateGrayOverlay(_screen->getPalette(0), _screen->getPalette(3).getData(), 0x40, 0, 0, 0, 0x100, true); for (int i = 0; i < 256; i++) tmpPal[_screen->getPalette(3)[i]] = 1; @@ -393,19 +1828,19 @@ int KyraEngine_HoF::seq_introOverview(WSAMovie_v2 *wsaObj, int x, int y, int frm break; case 40: - seq_loadNestedSequence(0, kSequenceOver1); + startNestedAnimation(0, kNestedSequenceOver1); break; case 60: - seq_loadNestedSequence(1, kSequenceOver2); + startNestedAnimation(1, kNestedSequenceOver2); break; case 120: - seq_playTalkText(0); + playSoundAndDisplaySubTitle(0); break; case 200: - seq_waitForTextsTimeout(); + waitForSubTitlesTimeout(); _screen->fadePalette(_screen->getPalette(2), 64); break; @@ -417,64 +1852,57 @@ int KyraEngine_HoF::seq_introOverview(WSAMovie_v2 *wsaObj, int x, int y, int frm _screen->copyRegion(0, 0, 0, 0, 320, 200, 2, 0); _screen->setScreenPalette(_screen->getPalette(0)); _screen->updateScreen(); - seq_resetActiveWSA(0); - seq_resetActiveWSA(1); + closeNestedAnimation(0); + closeNestedAnimation(1); break; case 282: - seq_loadNestedSequence(0, kSequenceForest); - seq_playTalkText(1); + startNestedAnimation(0, kNestedSequenceForest); + playSoundAndDisplaySubTitle(1); break; - case 354: - case 434: - if (!((_seqFrameCounter == 354 && (_flags.platform == Common::kPlatformFMTowns || _flags.platform == Common::kPlatformPC98)) || (_seqFrameCounter == 434 && _flags.platform == Common::kPlatformPC))) - break; - - seq_resetActiveWSA(0); - seq_loadNestedSequence(0, kSequenceDragon); + CASE_ALT(434, 354) + closeNestedAnimation(0); + startNestedAnimation(0, kNestedSequenceDragon); break; - case 400: - case 540: - if (!((_seqFrameCounter == 400 && (_flags.platform == Common::kPlatformFMTowns || _flags.platform == Common::kPlatformPC98)) || (_seqFrameCounter == 540 && _flags.platform == Common::kPlatformPC))) - break; - - seq_waitForTextsTimeout(); - seq_resetActiveWSA(0); - _seqEndTime = 0; - _seqSubframePlaying = false; + CASE_ALT(540, 400) + waitForSubTitlesTimeout(); + closeNestedAnimation(0); + setCountDown(0); + _updateAnimations = false; break; default: break; } - _seqFrameCounter++; + _callbackCurrentFrame++; return 0; } -int KyraEngine_HoF::seq_introLibrary(WSAMovie_v2 *wsaObj, int x, int y, int frm) { - switch (_seqFrameCounter) { +int SeqPlayer_HOF::cbHOF_library(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + switch (_callbackCurrentFrame) { case 0: - _seqSubframePlaying = true; - _sound->playTrack(5); + _updateAnimations = true; + _vm->sound()->playTrack(5); - _screen->generateGrayOverlay(_screen->getPalette(0), _screen->getPalette(3).getData(), 0x24, 0, 0, 0, 0x100, false); - _seqTextColor[1] = _screen->findLeastDifferentColor(_seqTextColorPresets, _screen->getPalette(0), 1, 255) & 0xff; - memset(_seqTextColorMap, _seqTextColor[1], 16); - _seqTextColorMap[1] = _seqTextColor[0] = _screen->findLeastDifferentColor(_seqTextColorPresets + 3, _screen->getPalette(0), 1, 255) & 0xff; + assert(_screenHoF); + _screenHoF->generateGrayOverlay(_screen->getPalette(0), _screen->getPalette(3).getData(), 0x24, 0, 0, 0, 0x100, false); + _textColor[1] = _screen->findLeastDifferentColor(_textColorPresets, _screen->getPalette(0), 1, 255) & 0xFF; + memset(_textColorMap, _textColor[1], 16); + _textColorMap[1] = _textColor[0] = _screen->findLeastDifferentColor(_textColorPresets + 3, _screen->getPalette(0), 1, 255) & 0xFF; - _screen->setTextColorMap(_seqTextColorMap); + _screen->setTextColorMap(_textColorMap); break; case 1: - seq_loadNestedSequence(0, kSequenceLibrary3); - seq_playTalkText(4); + startNestedAnimation(0, kNestedSequenceLibrary3); + playSoundAndDisplaySubTitle(4); break; case 100: - seq_waitForTextsTimeout(); + waitForSubTitlesTimeout(); _screen->copyPage(12, 2); _screen->applyOverlay(0, 0, 320, 200, 2, _screen->getPalette(3).getData()); @@ -482,585 +1910,560 @@ int KyraEngine_HoF::seq_introLibrary(WSAMovie_v2 *wsaObj, int x, int y, int frm) _screen->updateScreen(); _screen->copyPage(2, 12); - seq_resetActiveWSA(0); - seq_loadNestedSequence(0, kSequenceDarm); + closeNestedAnimation(0); + startNestedAnimation(0, kNestedSequenceDarm); break; case 104: - seq_playTalkText(5); + playSoundAndDisplaySubTitle(5); break; case 240: - seq_waitForTextsTimeout(); - seq_resetActiveWSA(0); - seq_loadNestedSequence(0, kSequenceLibrary2); + waitForSubTitlesTimeout(); + closeNestedAnimation(0); + startNestedAnimation(0, kNestedSequenceLibrary2); break; case 340: - seq_resetActiveWSA(0); + closeNestedAnimation(0); _screen->applyOverlay(0, 0, 320, 200, 2, _screen->getPalette(3).getData()); _screen->copyPage(2, 12); _screen->copyRegion(0, 0, 0, 0, 320, 200, 2, 0); _screen->updateScreen(); - seq_loadNestedSequence(0, kSequenceMarco); - seq_playTalkText(6); + startNestedAnimation(0, kNestedSequenceMarco); + playSoundAndDisplaySubTitle(6); break; - case 480: - case 660: - if (!((_seqFrameCounter == 480 && (_flags.platform == Common::kPlatformFMTowns || _flags.platform == Common::kPlatformPC98)) || (_seqFrameCounter == 660 && _flags.platform == Common::kPlatformPC))) - break; - + CASE_ALT(660, 480) _screen->copyPage(2, 12); - seq_waitForTextsTimeout(); - seq_resetActiveWSA(0); - _seqEndTime = 0; - _seqSubframePlaying = false; + waitForSubTitlesTimeout(); + closeNestedAnimation(0); + setCountDown(0); + _updateAnimations = false; break; default: break; } - _seqFrameCounter++; + _callbackCurrentFrame++; return 0; } - -int KyraEngine_HoF::seq_introHand(WSAMovie_v2 *wsaObj, int x, int y, int frm) { - switch (_seqFrameCounter) { +int SeqPlayer_HOF::cbHOF_hand(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + switch (_callbackCurrentFrame) { case 0: - _seqSubframePlaying = true; - _sound->playTrack(6); + _updateAnimations = true; + _vm->sound()->playTrack(6); - _screen->generateGrayOverlay(_screen->getPalette(0), _screen->getPalette(3).getData(), 0x24, 0, 0, 0, 0x100, false); - _seqTextColor[1] = _screen->findLeastDifferentColor(_seqTextColorPresets, _screen->getPalette(0), 1, 255) & 0xff; - memset(_seqTextColorMap, _seqTextColor[1], 16); - _seqTextColorMap[1] = _seqTextColor[0] = _screen->findLeastDifferentColor(_seqTextColorPresets + 3, _screen->getPalette(0), 1, 255) & 0xff; + assert(_screenHoF); + _screenHoF->generateGrayOverlay(_screen->getPalette(0), _screen->getPalette(3).getData(), 0x24, 0, 0, 0, 0x100, false); + _textColor[1] = _screen->findLeastDifferentColor(_textColorPresets, _screen->getPalette(0), 1, 255) & 0xFF; + memset(_textColorMap, _textColor[1], 16); + _textColorMap[1] = _textColor[0] = _screen->findLeastDifferentColor(_textColorPresets + 3, _screen->getPalette(0), 1, 255) & 0xFF; - _screen->setTextColorMap(_seqTextColorMap); + _screen->setTextColorMap(_textColorMap); break; case 1: - seq_loadNestedSequence(0, kSequenceHand1a); - seq_loadNestedSequence(1, kSequenceHand1b); - seq_loadNestedSequence(2, kSequenceHand1c); - seq_playTalkText(7); + startNestedAnimation(0, kNestedSequenceHand1a); + startNestedAnimation(1, kNestedSequenceHand1b); + startNestedAnimation(2, kNestedSequenceHand1c); + playSoundAndDisplaySubTitle(7); break; case 201: - seq_waitForTextsTimeout(); + waitForSubTitlesTimeout(); _screen->applyOverlay(0, 0, 320, 200, 2, _screen->getPalette(3).getData()); _screen->copyPage(2, 12); _screen->copyRegion(0, 0, 0, 0, 320, 200, 2, 0); _screen->updateScreen(); - seq_resetActiveWSA(0); - seq_resetActiveWSA(1); - seq_resetActiveWSA(2); - seq_loadNestedSequence(0, kSequenceHand2); - seq_playTalkText(8); + closeNestedAnimation(0); + closeNestedAnimation(1); + closeNestedAnimation(2); + startNestedAnimation(0, kNestedSequenceHand2); + playSoundAndDisplaySubTitle(8); break; - case 260: - case 395: - if (!((_seqFrameCounter == 260 && (_flags.platform == Common::kPlatformFMTowns || _flags.platform == Common::kPlatformPC98)) || (_seqFrameCounter == 395 && _flags.platform == Common::kPlatformPC))) - break; - - seq_waitForTextsTimeout(); - seq_resetActiveWSA(0); - seq_loadNestedSequence(1, kSequenceHand3); - seq_playTalkText(9); + CASE_ALT(395, 260) + waitForSubTitlesTimeout(); + closeNestedAnimation(0); + startNestedAnimation(1, kNestedSequenceHand3); + playSoundAndDisplaySubTitle(9); break; - case 365: - case 500: - if (!((_seqFrameCounter == 365 && (_flags.platform == Common::kPlatformFMTowns || _flags.platform == Common::kPlatformPC98)) || (_seqFrameCounter == 500 && _flags.platform == Common::kPlatformPC))) - break; - - seq_waitForTextsTimeout(); - seq_resetActiveWSA(1); - seq_loadNestedSequence(0, kSequenceHand4); + CASE_ALT(500, 365) + waitForSubTitlesTimeout(); + closeNestedAnimation(1); + startNestedAnimation(0, kNestedSequenceHand4); break; - case 405: - case 540: - if (!((_seqFrameCounter == 405 && (_flags.platform == Common::kPlatformFMTowns || _flags.platform == Common::kPlatformPC98)) || (_seqFrameCounter == 540 && _flags.platform == Common::kPlatformPC))) - break; - - seq_playTalkText(10); + CASE_ALT(540, 405) + playSoundAndDisplaySubTitle(10); break; - case 484: - case 630: - if (!((_seqFrameCounter == 484 && (_flags.platform == Common::kPlatformFMTowns || _flags.platform == Common::kPlatformPC98)) || (_seqFrameCounter == 630 && _flags.platform == Common::kPlatformPC))) - break; - - seq_waitForTextsTimeout(); - seq_resetActiveWSA(0); - _seqEndTime = 0; - _seqSubframePlaying = false; + CASE_ALT(630, 484) + waitForSubTitlesTimeout(); + closeNestedAnimation(0); + setCountDown(0); + _updateAnimations = false; break; default: break; } - _seqFrameCounter++; + _callbackCurrentFrame++; return 0; } -int KyraEngine_HoF::seq_introPoint(WSAMovie_v2 *wsaObj, int x, int y, int frm) { +int SeqPlayer_HOF::cbHOF_point(WSAMovie_v2 *wsaObj, int x, int y, int frm) { if (frm == -2) { - seq_waitForTextsTimeout(); - _seqEndTime = 0; + waitForSubTitlesTimeout(); + setCountDown(0); } - switch (_seqFrameCounter) { + switch (_callbackCurrentFrame) { case -2: - seq_waitForTextsTimeout(); + waitForSubTitlesTimeout(); break; case 0: - _sound->playTrack(7); + _vm->sound()->playTrack(7); - _seqTextColor[1] = 0xf7; - memset(_seqTextColorMap, _seqTextColor[1], 16); - _seqTextColorMap[1] = _seqTextColor[0] = _screen->findLeastDifferentColor(_seqTextColorPresets + 3, _screen->getPalette(0), 1, 255) & 0xff; - _screen->setTextColorMap(_seqTextColorMap); - _screen->generateGrayOverlay(_screen->getPalette(0), _screen->getPalette(3).getData(), 0x24, 0, 0, 0, 0x100, false); + _textColor[1] = 0xF7; + memset(_textColorMap, _textColor[1], 16); + _textColorMap[1] = _textColor[0] = _screen->findLeastDifferentColor(_textColorPresets + 3, _screen->getPalette(0), 1, 255) & 0xFF; + _screen->setTextColorMap(_textColorMap); + assert(_screenHoF); + _screenHoF->generateGrayOverlay(_screen->getPalette(0), _screen->getPalette(3).getData(), 0x24, 0, 0, 0, 0x100, false); break; case 1: - seq_playTalkText(11); + playSoundAndDisplaySubTitle(11); break; default: break; } - _seqFrameCounter++; + _callbackCurrentFrame++; return 0; } -int KyraEngine_HoF::seq_introZanfaun(WSAMovie_v2 *wsaObj, int x, int y, int frm) { +int SeqPlayer_HOF::cbHOF_zanfaun(WSAMovie_v2 *wsaObj, int x, int y, int frm) { if (frm == -2) { - seq_waitForTextsTimeout(); - _seqEndTime = 0; + waitForSubTitlesTimeout(); + setCountDown(0); return 0; } - switch (_seqFrameCounter) { + switch (_callbackCurrentFrame) { case 0: - _sound->playTrack(8); + _vm->sound()->playTrack(8); - _seqTextColor[1] = 0xfd; - memset(_seqTextColorMap, _seqTextColor[1], 16); - _seqTextColorMap[1] = _seqTextColor[0] = _screen->findLeastDifferentColor(_seqTextColorPresets + 3, _screen->getPalette(0), 1, 255) & 0xff; - _screen->setTextColorMap(_seqTextColorMap); + _textColor[1] = 0xFD; + memset(_textColorMap, _textColor[1], 16); + _textColorMap[1] = _textColor[0] = _screen->findLeastDifferentColor(_textColorPresets + 3, _screen->getPalette(0), 1, 255) & 0xFF; + _screen->setTextColorMap(_textColorMap); break; case 1: - if (_flags.isTalkie) { - seq_playWsaSyncDialogue(21, 13, -1, 140, 70, 160, wsaObj, 0, 8, x, y); + if (_vm->gameFlags().isTalkie) { + playDialogueAnimation(21, 13, -1, 140, 70, 160, wsaObj, 0, 8, x, y); } else { - seq_setTextEntry(21, 140, 70, 200, 160); - _seqFrameDelay = 200; + displaySubTitle(21, 140, 70, 200, 160); + _animDuration = 200; } break; case 2: case 11: case 21: - if (!_flags.isTalkie) - _seqFrameDelay = 12; + if (!_vm->gameFlags().isTalkie) + _animDuration = 12; break; case 9: - if (_flags.isTalkie) - seq_playWsaSyncDialogue(13, 14, -1, 140, (_flags.lang == Common::FR_FRA - || _flags.lang == Common::DE_DEU) ? 50 : 70, 160, wsaObj, 9, 15, x, y); + if (_vm->gameFlags().isTalkie) + playDialogueAnimation(13, 14, -1, 140, (_vm->gameFlags().lang == Common::FR_FRA + || _vm->gameFlags().lang == Common::DE_DEU) ? 50 : 70, 160, wsaObj, 9, 15, x, y); break; case 10: - if (!_flags.isTalkie) { - seq_waitForTextsTimeout(); - seq_setTextEntry(13, 140, 50, _sequenceStringsDuration[13], 160); - _seqFrameDelay = 300; + if (!_vm->gameFlags().isTalkie) { + waitForSubTitlesTimeout(); + displaySubTitle(13, 140, 50, _textDuration[13], 160); + _animDuration = 300; } break; case 16: - if (_flags.isTalkie) - seq_playWsaSyncDialogue(18, 15, -1, 140, (_flags.lang == Common::FR_FRA) ? 50 : - (_flags.lang == Common::DE_DEU ? 40 : 70), 160, wsaObj, 10, 16, x, y); + if (_vm->gameFlags().isTalkie) + playDialogueAnimation(18, 15, -1, 140, (_vm->gameFlags().lang == Common::FR_FRA) ? 50 : + (_vm->gameFlags().lang == Common::DE_DEU ? 40 : 70), 160, wsaObj, 10, 16, x, y); break; case 17: - if (_flags.isTalkie) - _seqFrameDelay = 12; + if (_vm->gameFlags().isTalkie) + _animDuration = 12; break; case 20: - if (!_flags.isTalkie) { - seq_waitForTextsTimeout(); - seq_setTextEntry(18, 160, 50, _sequenceStringsDuration[18], 160); - _seqFrameDelay = 200; + if (!_vm->gameFlags().isTalkie) { + waitForSubTitlesTimeout(); + displaySubTitle(18, 160, 50, _textDuration[18], 160); + _animDuration = 200; } break; case 26: - seq_waitForTextsTimeout(); + waitForSubTitlesTimeout(); break; case 46: - if (_flags.isTalkie) { - seq_playWsaSyncDialogue(16, 16, -1, 200, 50, 120, wsaObj, 46, 46, x, y); + if (_vm->gameFlags().isTalkie) { + playDialogueAnimation(16, 16, -1, 200, 50, 120, wsaObj, 46, 46, x, y); } else { - seq_waitForTextsTimeout(); - seq_setTextEntry(16, 200, 50, _sequenceStringsDuration[16], 120); + waitForSubTitlesTimeout(); + displaySubTitle(16, 200, 50, _textDuration[16], 120); } - _seqEndTime = _system->getMillis() + 120 * _tickLength; + setCountDown(120); break; default: break; } - _seqFrameCounter++; + _callbackCurrentFrame++; return 0; } -int KyraEngine_HoF::seq_introOver1(WSAMovie_v2 *wsaObj, int x, int y, int frm) { +int SeqPlayer_HOF::cbHOF_over1(WSAMovie_v2 *wsaObj, int x, int y, int frm) { if (frm == 2) - seq_waitForTextsTimeout(); + waitForSubTitlesTimeout(); else if (frm == 3) - seq_playTalkText(12); + playSoundAndDisplaySubTitle(12); return frm; } - -int KyraEngine_HoF::seq_introOver2(WSAMovie_v2 *wsaObj, int x, int y, int frm) { +int SeqPlayer_HOF::cbHOF_over2(WSAMovie_v2 *wsaObj, int x, int y, int frm) { if (frm == 1) - seq_playTalkText(12); + playSoundAndDisplaySubTitle(12); return frm; } -int KyraEngine_HoF::seq_introForest(WSAMovie_v2 *wsaObj, int x, int y, int frm) { +int SeqPlayer_HOF::cbHOF_forest(WSAMovie_v2 *wsaObj, int x, int y, int frm) { if (frm == 11) - seq_waitForTextsTimeout(); + waitForSubTitlesTimeout(); else if (frm == 12) - seq_playTalkText(2); + playSoundAndDisplaySubTitle(2); return frm; } -int KyraEngine_HoF::seq_introDragon(WSAMovie_v2 *wsaObj, int x, int y, int frm) { +int SeqPlayer_HOF::cbHOF_dragon(WSAMovie_v2 *wsaObj, int x, int y, int frm) { if (frm == 11) - seq_waitForTextsTimeout(); + waitForSubTitlesTimeout(); else if (frm == 3) - seq_playTalkText(3); + playSoundAndDisplaySubTitle(3); return frm; } -int KyraEngine_HoF::seq_introDarm(WSAMovie_v2 *wsaObj, int x, int y, int frm) { - //NULLSUB (at least in FM-TOWNS version) +int SeqPlayer_HOF::cbHOF_darm(WSAMovie_v2 *wsaObj, int x, int y, int frm) { return frm; } -int KyraEngine_HoF::seq_introLibrary2(WSAMovie_v2 *wsaObj, int x, int y, int frm) { - //NULLSUB (at least in FM-TOWNS version) +int SeqPlayer_HOF::cbHOF_library2(WSAMovie_v2 *wsaObj, int x, int y, int frm) { return frm; } -int KyraEngine_HoF::seq_introMarco(WSAMovie_v2 *wsaObj, int x, int y, int frm) { +int SeqPlayer_HOF::cbHOF_marco(WSAMovie_v2 *wsaObj, int x, int y, int frm) { if (frm == 36) { - seq_waitForTextsTimeout(); - _seqEndTime = 0; + waitForSubTitlesTimeout(); + setCountDown(0); } return frm; } -int KyraEngine_HoF::seq_introHand1a(WSAMovie_v2 *wsaObj, int x, int y, int frm) { - //NULLSUB (at least in FM-TOWNS version) +int SeqPlayer_HOF::cbHOF_hand1a(WSAMovie_v2 *wsaObj, int x, int y, int frm) { return frm; } -int KyraEngine_HoF::seq_introHand1b(WSAMovie_v2 *wsaObj, int x, int y, int frm) { +int SeqPlayer_HOF::cbHOF_hand1b(WSAMovie_v2 *wsaObj, int x, int y, int frm) { if (frm == 15) frm = 12; return frm; } -int KyraEngine_HoF::seq_introHand1c(WSAMovie_v2 *wsaObj, int x, int y, int frm) { +int SeqPlayer_HOF::cbHOF_hand1c(WSAMovie_v2 *wsaObj, int x, int y, int frm) { if (frm == 8) frm = 4; return frm; } -int KyraEngine_HoF::seq_introHand2(WSAMovie_v2 *wsaObj, int x, int y, int frm) { - //NULLSUB (at least in FM-TOWNS version) +int SeqPlayer_HOF::cbHOF_hand2(WSAMovie_v2 *wsaObj, int x, int y, int frm) { return frm; } -int KyraEngine_HoF::seq_introHand3(WSAMovie_v2 *wsaObj, int x, int y, int frm) { - //NULLSUB (at least in FM-TOWNS version) +int SeqPlayer_HOF::cbHOF_hand3(WSAMovie_v2 *wsaObj, int x, int y, int frm) { return frm; } -int KyraEngine_HoF::seq_finaleFunters(WSAMovie_v2 *wsaObj, int x, int y, int frm) { - _seqSubFrameEndTimeInternal = 0; - int chatX = 0; - int chatY = 0; - int chatW = 0; - int chatFirstFrame = 0; - int chatLastFrame = 0; +int SeqPlayer_HOF::cbHOF_funters(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + uint32 frameEnd = 0; + int subTitleX = 0; + int subTitleY = 0; + int subTitleW = 0; + int subTitleFirstFrame = 0; + int subTitleLastFrame = 0; uint16 voiceIndex = 0; switch (frm) { case -2: - seq_sequenceCommand(9); + doTransition(9); break; case 0: - _sound->playTrack(3); + _vm->sound()->playTrack(3); - _seqTextColor[1] = _screen->findLeastDifferentColor(_seqTextColorPresets, _screen->getPalette(0), 1, 255) & 0xff; - memset(_seqTextColorMap, _seqTextColor[1], 16); - _seqTextColor[0] = _seqTextColorMap[1] = 0xff; - _screen->setTextColorMap(_seqTextColorMap); + _textColor[1] = _screen->findLeastDifferentColor(_textColorPresets, _screen->getPalette(0), 1, 255) & 0xFF; + memset(_textColorMap, _textColor[1], 16); + _textColor[0] = _textColorMap[1] = 0xFF; + _screen->setTextColorMap(_textColorMap); - _seqSubFrameEndTimeInternal = _system->getMillis() + 480 * _tickLength; - seq_printCreditsString(81, 240, 70, _seqTextColorMap, 252); - seq_printCreditsString(82, 240, 90, _seqTextColorMap, _seqTextColor[0]); + frameEnd = _system->getMillis() + 480 * _vm->tickLength(); + printFadingText(81, 240, 70, _textColorMap, 252); + printFadingText(82, 240, 90, _textColorMap, _textColor[0]); _screen->copyPage(2, 12); - seq_playTalkText(_flags.isTalkie ? 28 : 24); - delay(_seqSubFrameEndTimeInternal - _system->getMillis()); - _seqTextColor[0] = 1; - - if (_flags.isTalkie) { - chatY = (_flags.lang == Common::FR_FRA) ? 70 : 78; - chatFirstFrame = 9; - chatLastFrame = 15; + playSoundAndDisplaySubTitle(_vm->gameFlags().isTalkie ? 28 : 24); + delayUntil(frameEnd); + _textColor[0] = 1; + + if (_vm->gameFlags().isTalkie) { + subTitleY = (_vm->gameFlags().lang == Common::FR_FRA) ? 70 : 78; + subTitleFirstFrame = 9; + subTitleLastFrame = 15; voiceIndex = 34; } else { - chatY = (_flags.lang == Common::FR_FRA) ? 78 : 70; - chatFirstFrame = 0; - chatLastFrame = 8; + subTitleY = (_vm->gameFlags().lang == Common::FR_FRA) ? 78 : 70; + subTitleFirstFrame = 0; + subTitleLastFrame = 8; } - chatX = (_flags.lang == Common::FR_FRA) ? 84 : 88; - chatW = 100; + subTitleX = (_vm->gameFlags().lang == Common::FR_FRA) ? 84 : 88; + subTitleW = 100; - seq_playWsaSyncDialogue(22, voiceIndex, 187, chatX, chatY, chatW, wsaObj, chatFirstFrame, chatLastFrame, x, y); + playDialogueAnimation(22, voiceIndex, 187, subTitleX, subTitleY, subTitleW, wsaObj, subTitleFirstFrame, subTitleLastFrame, x, y); break; case 9: case 16: - if (!((frm == 9 && !_flags.isTalkie) || (frm == 16 && _flags.isTalkie))) + if (!((frm == 9 && !_vm->gameFlags().isTalkie) || (frm == 16 && _vm->gameFlags().isTalkie))) break; - _seqFrameDelay = 12; + _animDuration = 12; - if (_flags.lang == Common::FR_FRA) { - chatX = 80; - chatW = 112; + if (_vm->gameFlags().lang == Common::FR_FRA) { + subTitleX = 80; + subTitleW = 112; } else { - chatX = (_flags.lang == Common::DE_DEU) ? 84 : 96; - chatW = 100; + subTitleX = (_vm->gameFlags().lang == Common::DE_DEU) ? 84 : 96; + subTitleW = 100; } - if (_flags.isTalkie) { - chatFirstFrame = 0; - chatLastFrame = 8; + if (_vm->gameFlags().isTalkie) { + subTitleFirstFrame = 0; + subTitleLastFrame = 8; voiceIndex = 35; } else { - chatFirstFrame = 9; - chatLastFrame = 15; + subTitleFirstFrame = 9; + subTitleLastFrame = 15; } - chatY = 70; + subTitleY = 70; - seq_playWsaSyncDialogue(23, voiceIndex, 137, chatX, chatY, chatW, wsaObj, chatFirstFrame, chatLastFrame, x, y); - if (_flags.isTalkie) - _seqWsaCurrentFrame = 17; + playDialogueAnimation(23, voiceIndex, 137, subTitleX, subTitleY, subTitleW, wsaObj, subTitleFirstFrame, subTitleLastFrame, x, y); + if (_vm->gameFlags().isTalkie) + _animCurrentFrame = 17; break; default: break; } - _seqFrameCounter++; + _callbackCurrentFrame++; return 0; } -int KyraEngine_HoF::seq_finaleFerb(WSAMovie_v2 *wsaObj, int x, int y, int frm) { - _seqSubFrameEndTimeInternal = 0; - int chatX = 0; - int chatY = 0; - int chatW = 0; - int chatFirstFrame = 0; - int chatLastFrame = 0; +int SeqPlayer_HOF::cbHOF_ferb(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + uint32 frameEnd = 0; + int subTitleX = 0; + int subTitleY = 0; + int subTitleW = 0; + int subTitleFirstFrame = 0; + int subTitleLastFrame = 0; uint16 voiceIndex = 0; switch (frm) { case -2: - seq_sequenceCommand(9); - _seqSubFrameEndTimeInternal = _system->getMillis() + 480 * _tickLength; - seq_printCreditsString(34, 240, _flags.isTalkie ? 60 : 40, _seqTextColorMap, 252); - seq_printCreditsString(35, 240, _flags.isTalkie ? 70 : 50, _seqTextColorMap, _seqTextColor[0]); - seq_printCreditsString(36, 240, _flags.isTalkie ? 90 : 70, _seqTextColorMap, 252); - seq_printCreditsString(37, 240, _flags.isTalkie ? 100 : 90, _seqTextColorMap, _seqTextColor[0]); - seq_printCreditsString(38, 240, _flags.isTalkie ? 120 : 110, _seqTextColorMap, 252); - seq_printCreditsString(39, 240, _flags.isTalkie ? 130 : 120, _seqTextColorMap, _seqTextColor[0]); - if (_flags.platform == Common::kPlatformFMTowns || _flags.platform == Common::kPlatformPC98) - seq_printCreditsString(103, 240, 130, _seqTextColorMap, _seqTextColor[0]); - delay(_seqSubFrameEndTimeInternal - _system->getMillis()); - _seqEndTime = 0; + doTransition(9); + frameEnd = _system->getMillis() + 480 * _vm->tickLength(); + printFadingText(34, 240, _vm->gameFlags().isTalkie ? 60 : 40, _textColorMap, 252); + printFadingText(35, 240, _vm->gameFlags().isTalkie ? 70 : 50, _textColorMap, _textColor[0]); + printFadingText(36, 240, _vm->gameFlags().isTalkie ? 90 : 70, _textColorMap, 252); + printFadingText(37, 240, _vm->gameFlags().isTalkie ? 100 : 90, _textColorMap, _textColor[0]); + printFadingText(38, 240, _vm->gameFlags().isTalkie ? 120 : 110, _textColorMap, 252); + printFadingText(39, 240, _vm->gameFlags().isTalkie ? 130 : 120, _textColorMap, _textColor[0]); + if (_vm->gameFlags().platform == Common::kPlatformFMTowns || _vm->gameFlags().platform == Common::kPlatformPC98) + printFadingText(103, 240, 130, _textColorMap, _textColor[0]); + delayUntil(frameEnd); + setCountDown(0); break; case 0: - _seqTextColor[1] = _screen->findLeastDifferentColor(_seqTextColorPresets, _screen->getPalette(0), 1, 255) & 0xff; - memset(_seqTextColorMap, _seqTextColor[1], 16); - _seqTextColor[0] = _seqTextColorMap[1] = 255; - _screen->setTextColorMap(_seqTextColorMap); + _textColor[1] = _screen->findLeastDifferentColor(_textColorPresets, _screen->getPalette(0), 1, 255) & 0xFF; + memset(_textColorMap, _textColor[1], 16); + _textColor[0] = _textColorMap[1] = 255; + _screen->setTextColorMap(_textColorMap); break; case 5: - if (!_flags.isTalkie) - seq_playTalkText(18); - _seqFrameDelay = 16; + if (!_vm->gameFlags().isTalkie) + playSoundAndDisplaySubTitle(18); + _animDuration = 16; - if (_flags.isTalkie) { - chatFirstFrame = 5; - chatLastFrame = 8; + if (_vm->gameFlags().isTalkie) { + subTitleFirstFrame = 5; + subTitleLastFrame = 8; voiceIndex = 22; } else { - chatLastFrame = 14; + subTitleLastFrame = 14; } - chatX = 116; - chatY = 90; - chatW = 60; + subTitleX = 116; + subTitleY = 90; + subTitleW = 60; - seq_playWsaSyncDialogue(24, voiceIndex, 149, chatX, chatY, chatW, wsaObj, chatFirstFrame, chatLastFrame, x, y); + playDialogueAnimation(24, voiceIndex, 149, subTitleX, subTitleY, subTitleW, wsaObj, subTitleFirstFrame, subTitleLastFrame, x, y); break; case 11: - if (_flags.isTalkie) - seq_playWsaSyncDialogue(24, 22, 149, 116, 90, 60, wsaObj, 11, 14, x, y); + if (_vm->gameFlags().isTalkie) + playDialogueAnimation(24, 22, 149, 116, 90, 60, wsaObj, 11, 14, x, y); break; case 16: - seq_playTalkText(_flags.isTalkie ? 23 : 19); - _seqFrameDelay = _flags.isTalkie ? 20 : 16; + playSoundAndDisplaySubTitle(_vm->gameFlags().isTalkie ? 23 : 19); + _animDuration = _vm->gameFlags().isTalkie ? 20 : 16; - if (_flags.lang == Common::FR_FRA) { - chatY = 48; - chatW = 88; + if (_vm->gameFlags().lang == Common::FR_FRA) { + subTitleY = 48; + subTitleW = 88; } else { - chatY = 60; - chatW = 100; + subTitleY = 60; + subTitleW = 100; } - chatX = 60; + subTitleX = 60; - if (_flags.isTalkie) + if (_vm->gameFlags().isTalkie) voiceIndex = 36; - seq_playWsaSyncDialogue(25, voiceIndex, 143, chatX, chatY, chatW, wsaObj, 16, 25, x, y); - _seqFrameDelay = 16; + playDialogueAnimation(25, voiceIndex, 143, subTitleX, subTitleY, subTitleW, wsaObj, 16, 25, x, y); + _animDuration = 16; break; default: break; } - _seqFrameCounter++; + _callbackCurrentFrame++; return 0; } -int KyraEngine_HoF::seq_finaleFish(WSAMovie_v2 *wsaObj, int x, int y, int frm) { - _seqSubFrameEndTimeInternal = 0; - int chatX = 0; - int chatY = 0; - int chatW = 0; +int SeqPlayer_HOF::cbHOF_fish(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + uint32 frameEnd = 0; + int subTitleX = 0; + int subTitleY = 0; + int subTitleW = 0; uint16 voiceIndex = 0; switch (frm) { case -2: - seq_sequenceCommand(9); - _seqSubFrameEndTimeInternal = _system->getMillis() + 480 * _tickLength; + doTransition(9); + frameEnd = _system->getMillis() + 480 * _vm->tickLength(); - seq_printCreditsString(40, 240, _flags.isTalkie ? 55 : 40, _seqTextColorMap, 252); - seq_printCreditsString(41, 240, _flags.isTalkie ? 65 : 50, _seqTextColorMap, _seqTextColor[0]); - seq_printCreditsString(42, 240, _flags.isTalkie ? 75 : 60, _seqTextColorMap, _seqTextColor[0]); - seq_printCreditsString(43, 240, _flags.isTalkie ? 95 : 80, _seqTextColorMap, 252); - seq_printCreditsString(44, 240, _flags.isTalkie ? 105 : 90, _seqTextColorMap, _seqTextColor[0]); - seq_printCreditsString(93, 240, _flags.isTalkie ? 125 : 110, _seqTextColorMap, 252); - seq_printCreditsString(94, 240, _flags.isTalkie ? 135 : 120, _seqTextColorMap, _seqTextColor[0]); - delay(_seqSubFrameEndTimeInternal - _system->getMillis()); - _seqEndTime = 0; + printFadingText(40, 240, _vm->gameFlags().isTalkie ? 55 : 40, _textColorMap, 252); + printFadingText(41, 240, _vm->gameFlags().isTalkie ? 65 : 50, _textColorMap, _textColor[0]); + printFadingText(42, 240, _vm->gameFlags().isTalkie ? 75 : 60, _textColorMap, _textColor[0]); + printFadingText(43, 240, _vm->gameFlags().isTalkie ? 95 : 80, _textColorMap, 252); + printFadingText(44, 240, _vm->gameFlags().isTalkie ? 105 : 90, _textColorMap, _textColor[0]); + printFadingText(93, 240, _vm->gameFlags().isTalkie ? 125 : 110, _textColorMap, 252); + printFadingText(94, 240, _vm->gameFlags().isTalkie ? 135 : 120, _textColorMap, _textColor[0]); + delayUntil(frameEnd); + setCountDown(0); break; case 0: - _seqTextColor[1] = _screen->findLeastDifferentColor(_seqTextColorPresets, _screen->getPalette(0), 1, 255) & 0xff; - memset(_seqTextColorMap, _seqTextColor[1], 16); - _seqTextColor[0] = _seqTextColorMap[1] = 0xff; - _screen->setTextColorMap(_seqTextColorMap); + _textColor[1] = _screen->findLeastDifferentColor(_textColorPresets, _screen->getPalette(0), 1, 255) & 0xFF; + memset(_textColorMap, _textColor[1], 16); + _textColor[0] = _textColorMap[1] = 0xFF; + _screen->setTextColorMap(_textColorMap); break; case 4: - chatX = 94; - chatY = 42; - chatW = 100; - if (_flags.isTalkie) + subTitleX = 94; + subTitleY = 42; + subTitleW = 100; + if (_vm->gameFlags().isTalkie) voiceIndex = 37; - seq_playWsaSyncDialogue(26, voiceIndex, 149, chatX, chatY, chatW, wsaObj, 3, 12, x, y); + playDialogueAnimation(26, voiceIndex, 149, subTitleX, subTitleY, subTitleW, wsaObj, 3, 12, x, y); break; case 14: - seq_playTalkText(_flags.isTalkie ? 19 : 15); + playSoundAndDisplaySubTitle(_vm->gameFlags().isTalkie ? 19 : 15); break; case 23: - seq_playTalkText(_flags.isTalkie ? 20 : 16); + playSoundAndDisplaySubTitle(_vm->gameFlags().isTalkie ? 20 : 16); break; case 29: - chatX = (_flags.lang == Common::DE_DEU) ? 82 : ((_flags.lang == Common::FR_FRA) ? 92 : 88); - chatY = 40; - chatW = 100; + subTitleX = (_vm->gameFlags().lang == Common::DE_DEU) ? 82 : ((_vm->gameFlags().lang == Common::FR_FRA) ? 92 : 88); + subTitleY = 40; + subTitleW = 100; - if (_flags.isTalkie) { - if (_flags.lang == Common::DE_DEU) - chatY = 35; + if (_vm->gameFlags().isTalkie) { + if (_vm->gameFlags().lang == Common::DE_DEU) + subTitleY = 35; voiceIndex = 38; } - seq_playWsaSyncDialogue(27, voiceIndex, 187, chatX, chatY, chatW, wsaObj, 28, 34, x, y); + playDialogueAnimation(27, voiceIndex, 187, subTitleX, subTitleY, subTitleW, wsaObj, 28, 34, x, y); break; case 45: - seq_playTalkText(_flags.isTalkie ? 21 : 17); + playSoundAndDisplaySubTitle(_vm->gameFlags().isTalkie ? 21 : 17); break; case 50: - seq_playTalkText(_flags.isTalkie ? 29 : 25); + playSoundAndDisplaySubTitle(_vm->gameFlags().isTalkie ? 29 : 25); break; default: break; } - _seqFrameCounter++; + _callbackCurrentFrame++; return 0; } -int KyraEngine_HoF::seq_finaleFheep(WSAMovie_v2 *wsaObj, int x, int y, int frm) { - _seqSubFrameEndTimeInternal = 0; - int chatX = 0; - int chatY = 0; - int chatW = 0; - int chatFirstFrame = 0; - int chatLastFrame = 0; +int SeqPlayer_HOF::cbHOF_fheep(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + uint32 frameEnd = 0; + int subTitleX = 0; + int subTitleY = 0; + int subTitleW = 0; + int subTitleFirstFrame = 0; + int subTitleLastFrame = 0; uint16 voiceIndex = 0; switch (frm) { @@ -1068,79 +2471,79 @@ int KyraEngine_HoF::seq_finaleFheep(WSAMovie_v2 *wsaObj, int x, int y, int frm) _screen->copyPage(12, 2); _screen->copyPage(2, 0); _screen->updateScreen(); - seq_sequenceCommand(9); - _seqSubFrameEndTimeInternal = _system->getMillis() + 480 * _tickLength; - seq_printCreditsString(49, 240, 20, _seqTextColorMap, 252); - seq_printCreditsString(50, 240, 30, _seqTextColorMap, _seqTextColor[0]); - seq_printCreditsString(51, 240, 40, _seqTextColorMap, _seqTextColor[0]); - seq_printCreditsString(52, 240, 50, _seqTextColorMap, _seqTextColor[0]); - seq_printCreditsString(53, 240, 60, _seqTextColorMap, _seqTextColor[0]); - seq_printCreditsString(54, 240, 70, _seqTextColorMap, _seqTextColor[0]); - seq_printCreditsString(55, 240, 80, _seqTextColorMap, _seqTextColor[0]); - seq_printCreditsString(56, 240, 90, _seqTextColorMap, _seqTextColor[0]); - seq_printCreditsString(57, 240, 100, _seqTextColorMap, _seqTextColor[0]); - seq_printCreditsString(58, 240, 110, _seqTextColorMap, _seqTextColor[0]); - seq_printCreditsString(60, 240, 120, _seqTextColorMap, _seqTextColor[0]); - seq_printCreditsString(61, 240, 130, _seqTextColorMap, _seqTextColor[0]); - seq_printCreditsString(62, 240, 140, _seqTextColorMap, _seqTextColor[0]); - seq_printCreditsString(63, 240, 150, _seqTextColorMap, _seqTextColor[0]); - seq_printCreditsString(64, 240, 160, _seqTextColorMap, _seqTextColor[0]); - - delay(_seqSubFrameEndTimeInternal - _system->getMillis()); - _seqEndTime = 0; + doTransition(9); + frameEnd = _system->getMillis() + 480 * _vm->tickLength(); + printFadingText(49, 240, 20, _textColorMap, 252); + printFadingText(50, 240, 30, _textColorMap, _textColor[0]); + printFadingText(51, 240, 40, _textColorMap, _textColor[0]); + printFadingText(52, 240, 50, _textColorMap, _textColor[0]); + printFadingText(53, 240, 60, _textColorMap, _textColor[0]); + printFadingText(54, 240, 70, _textColorMap, _textColor[0]); + printFadingText(55, 240, 80, _textColorMap, _textColor[0]); + printFadingText(56, 240, 90, _textColorMap, _textColor[0]); + printFadingText(57, 240, 100, _textColorMap, _textColor[0]); + printFadingText(58, 240, 110, _textColorMap, _textColor[0]); + printFadingText(60, 240, 120, _textColorMap, _textColor[0]); + printFadingText(61, 240, 130, _textColorMap, _textColor[0]); + printFadingText(62, 240, 140, _textColorMap, _textColor[0]); + printFadingText(63, 240, 150, _textColorMap, _textColor[0]); + printFadingText(64, 240, 160, _textColorMap, _textColor[0]); + + delayUntil(frameEnd); + setCountDown(0); break; case 0: - _seqTextColor[1] = _screen->findLeastDifferentColor(_seqTextColorPresets, _screen->getPalette(0), 1, 255) & 0xff; - memset(_seqTextColorMap, _seqTextColor[1], 16); - _seqTextColor[0] = _seqTextColorMap[1] = 0xff; - _screen->setTextColorMap(_seqTextColorMap); + _textColor[1] = _screen->findLeastDifferentColor(_textColorPresets, _screen->getPalette(0), 1, 255) & 0xFF; + memset(_textColorMap, _textColor[1], 16); + _textColor[0] = _textColorMap[1] = 0xFF; + _screen->setTextColorMap(_textColorMap); break; case 2: - seq_playTalkText(_flags.isTalkie ? 25 : 21); + playSoundAndDisplaySubTitle(_vm->gameFlags().isTalkie ? 25 : 21); - if (_flags.lang == Common::FR_FRA) { - chatX = 92; - chatY = 72; + if (_vm->gameFlags().lang == Common::FR_FRA) { + subTitleX = 92; + subTitleY = 72; } else { - chatX = (_flags.lang == Common::DE_DEU) ? 90 : 98; - chatY = 84; + subTitleX = (_vm->gameFlags().lang == Common::DE_DEU) ? 90 : 98; + subTitleY = 84; } - if (_flags.isTalkie) { - chatFirstFrame = 8; - chatLastFrame = 9; + if (_vm->gameFlags().isTalkie) { + subTitleFirstFrame = 8; + subTitleLastFrame = 9; voiceIndex = 39; } else { - chatFirstFrame = 2; - chatLastFrame = -8; + subTitleFirstFrame = 2; + subTitleLastFrame = -8; } - chatW = 100; + subTitleW = 100; - seq_playWsaSyncDialogue(28, voiceIndex, -1, chatX, chatY, chatW, wsaObj, chatFirstFrame, chatLastFrame, x, y); - if (_flags.isTalkie) - _seqWsaCurrentFrame = 4; + playDialogueAnimation(28, voiceIndex, -1, subTitleX, subTitleY, subTitleW, wsaObj, subTitleFirstFrame, subTitleLastFrame, x, y); + if (_vm->gameFlags().isTalkie) + _animCurrentFrame = 4; break; case 9: - seq_playTalkText(_flags.isTalkie ? 24 : 20); - _seqFrameDelay = 100; + playSoundAndDisplaySubTitle(_vm->gameFlags().isTalkie ? 24 : 20); + _animDuration = 100; break; default: break; } - _seqFrameCounter++; + _callbackCurrentFrame++; return 0; } -int KyraEngine_HoF::seq_finaleFarmer(WSAMovie_v2 *wsaObj, int x, int y, int frm) { - _seqSubFrameEndTimeInternal = 0; - int chatX = 0; - int chatY = 0; - int chatW = 0; +int SeqPlayer_HOF::cbHOF_farmer(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + uint32 frameEnd = 0; + int subTitleX = 0; + int subTitleY = 0; + int subTitleW = 0; uint16 voiceIndex = 0; switch (frm) { @@ -1148,169 +2551,164 @@ int KyraEngine_HoF::seq_finaleFarmer(WSAMovie_v2 *wsaObj, int x, int y, int frm) _screen->copyPage(12, 2); _screen->copyPage(2, 0); _screen->updateScreen(); - seq_sequenceCommand(9); - _seqSubFrameEndTimeInternal = _system->getMillis() + 480 * _tickLength; - seq_printCreditsString(45, 240, 40, _seqTextColorMap, 252); - seq_printCreditsString(46, 240, 50, _seqTextColorMap, _seqTextColor[0]); - seq_printCreditsString(47, 240, 60, _seqTextColorMap, _seqTextColor[0]); - seq_printCreditsString(83, 240, 80, _seqTextColorMap, 252); - seq_printCreditsString(48, 240, 90, _seqTextColorMap, _seqTextColor[0]); - seq_printCreditsString(65, 240, 110, _seqTextColorMap, 252); - seq_printCreditsString(66, 240, 120, _seqTextColorMap, _seqTextColor[0]); - seq_printCreditsString(67, 240, 130, _seqTextColorMap, _seqTextColor[0]); - seq_printCreditsString(68, 240, 140, _seqTextColorMap, _seqTextColor[0]); - seq_printCreditsString(69, 240, 150, _seqTextColorMap, _seqTextColor[0]); - if (_flags.platform == Common::kPlatformFMTowns || _flags.platform == Common::kPlatformPC98) - seq_printCreditsString(104, 240, 160, _seqTextColorMap, _seqTextColor[0]); - delay(_seqSubFrameEndTimeInternal - _system->getMillis()); - _seqEndTime = 0; + doTransition(9); + frameEnd = _system->getMillis() + 480 * _vm->tickLength(); + printFadingText(45, 240, 40, _textColorMap, 252); + printFadingText(46, 240, 50, _textColorMap, _textColor[0]); + printFadingText(47, 240, 60, _textColorMap, _textColor[0]); + printFadingText(83, 240, 80, _textColorMap, 252); + printFadingText(48, 240, 90, _textColorMap, _textColor[0]); + printFadingText(65, 240, 110, _textColorMap, 252); + printFadingText(66, 240, 120, _textColorMap, _textColor[0]); + printFadingText(67, 240, 130, _textColorMap, _textColor[0]); + printFadingText(68, 240, 140, _textColorMap, _textColor[0]); + printFadingText(69, 240, 150, _textColorMap, _textColor[0]); + if (_vm->gameFlags().platform == Common::kPlatformFMTowns || _vm->gameFlags().platform == Common::kPlatformPC98) + printFadingText(104, 240, 160, _textColorMap, _textColor[0]); + delayUntil(frameEnd); + setCountDown(0); break; case 0: - _seqTextColor[1] = 1 + (_screen->findLeastDifferentColor(_seqTextColorPresets, _screen->getPalette(0), 1, 254) & 0xff); - memset(_seqTextColorMap, _seqTextColor[1], 16); - _seqTextColorMap[1] = _seqTextColor[0] = 1 + (_screen->findLeastDifferentColor(_seqTextColorPresets + 3, _screen->getPalette(0), 1, 254) & 0xff); - _screen->setTextColorMap(_seqTextColorMap); - seq_playTalkText(_flags.isTalkie ? 30 : 26); + _textColor[1] = 1 + (_screen->findLeastDifferentColor(_textColorPresets, _screen->getPalette(0), 1, 254) & 0xFF); + memset(_textColorMap, _textColor[1], 16); + _textColorMap[1] = _textColor[0] = 1 + (_screen->findLeastDifferentColor(_textColorPresets + 3, _screen->getPalette(0), 1, 254) & 0xFF); + _screen->setTextColorMap(_textColorMap); + playSoundAndDisplaySubTitle(_vm->gameFlags().isTalkie ? 30 : 26); break; case 6: - if (_flags.isTalkie) - seq_playTalkText(18); + if (_vm->gameFlags().isTalkie) + playSoundAndDisplaySubTitle(18); break; case 12: - if (!_flags.isTalkie) - seq_playTalkText(14); + if (!_vm->gameFlags().isTalkie) + playSoundAndDisplaySubTitle(14); - chatX = 90; - chatY = 30; - chatW = 100; + subTitleX = 90; + subTitleY = 30; + subTitleW = 100; - if (_flags.isTalkie) { - if (_flags.lang == Common::FR_FRA || _flags.lang == Common::DE_DEU) { - chatX = 75; - chatY = 25; + if (_vm->gameFlags().isTalkie) { + if (_vm->gameFlags().lang == Common::FR_FRA || _vm->gameFlags().lang == Common::DE_DEU) { + subTitleX = 75; + subTitleY = 25; } voiceIndex = 40; } - seq_playWsaSyncDialogue(29, voiceIndex, 150, chatX, chatY, chatW, wsaObj, 12, -21, x, y); + playDialogueAnimation(29, voiceIndex, 150, subTitleX, subTitleY, subTitleW, wsaObj, 12, -21, x, y); break; default: break; } - _seqFrameCounter++; + _callbackCurrentFrame++; return 0; } -int KyraEngine_HoF::seq_finaleFuards(WSAMovie_v2 *wsaObj, int x, int y, int frm) { - _seqSubFrameEndTimeInternal = 0; - int chatX = 0; - int chatY = 0; - int chatW = 0; - int chatFirstFrame = 0; - int chatLastFrame = 0; - //int textCol = 0; +int SeqPlayer_HOF::cbHOF_fuards(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + uint32 frameEnd = 0; + int subTitleX = 0; + int subTitleY = 0; + int subTitleW = 0; + int subTitleFirstFrame = 0; + int subTitleLastFrame = 0; uint16 voiceIndex = 0; switch (frm) { case -2: - seq_sequenceCommand(9); - _seqSubFrameEndTimeInternal = _system->getMillis() + 480 * _tickLength; - seq_printCreditsString(70, 240, 20, _seqTextColorMap, 252); - seq_printCreditsString(71, 240, 30, _seqTextColorMap, _seqTextColor[0]); - seq_printCreditsString(72, 240, 40, _seqTextColorMap, _seqTextColor[0]); - seq_printCreditsString(73, 240, 50, _seqTextColorMap, _seqTextColor[0]); - seq_printCreditsString(74, 240, 60, _seqTextColorMap, _seqTextColor[0]); - seq_printCreditsString(75, 240, 70, _seqTextColorMap, _seqTextColor[0]); - seq_printCreditsString(101, 240, 80, _seqTextColorMap, _seqTextColor[0]); - seq_printCreditsString(102, 240, 90, _seqTextColorMap, _seqTextColor[0]); - seq_printCreditsString(87, 240, 100, _seqTextColorMap, _seqTextColor[0]); - seq_printCreditsString(88, 240, 110, _seqTextColorMap, _seqTextColor[0]); - seq_printCreditsString(89, 240, 120, _seqTextColorMap, _seqTextColor[0]); - seq_printCreditsString(90, 240, 130, _seqTextColorMap, _seqTextColor[0]); - seq_printCreditsString(91, 240, 140, _seqTextColorMap, _seqTextColor[0]); - seq_printCreditsString(92, 240, 150, _seqTextColorMap, _seqTextColor[0]); - delay(_seqSubFrameEndTimeInternal - _system->getMillis()); - _seqEndTime = 0; + doTransition(9); + frameEnd = _system->getMillis() + 480 * _vm->tickLength(); + printFadingText(70, 240, 20, _textColorMap, 252); + printFadingText(71, 240, 30, _textColorMap, _textColor[0]); + printFadingText(72, 240, 40, _textColorMap, _textColor[0]); + printFadingText(73, 240, 50, _textColorMap, _textColor[0]); + printFadingText(74, 240, 60, _textColorMap, _textColor[0]); + printFadingText(75, 240, 70, _textColorMap, _textColor[0]); + printFadingText(101, 240, 80, _textColorMap, _textColor[0]); + printFadingText(102, 240, 90, _textColorMap, _textColor[0]); + printFadingText(87, 240, 100, _textColorMap, _textColor[0]); + printFadingText(88, 240, 110, _textColorMap, _textColor[0]); + printFadingText(89, 240, 120, _textColorMap, _textColor[0]); + printFadingText(90, 240, 130, _textColorMap, _textColor[0]); + printFadingText(91, 240, 140, _textColorMap, _textColor[0]); + printFadingText(92, 240, 150, _textColorMap, _textColor[0]); + delayUntil(frameEnd); + setCountDown(0); break; case 0: for (int i = 0; i < 0x300; i++) - _screen->getPalette(0)[i] &= 0x3f; - _seqTextColor[1] = 0xCf; - memset(_seqTextColorMap, _seqTextColor[1], 16); - _seqTextColor[0] = _seqTextColorMap[1] = 0xfe; + _screen->getPalette(0)[i] &= 0x3F; + _textColor[1] = 0xCf; + memset(_textColorMap, _textColor[1], 16); + _textColor[0] = _textColorMap[1] = 0xFE; - _screen->setTextColorMap(_seqTextColorMap); + _screen->setTextColorMap(_textColorMap); break; case 6: - _seqFrameDelay = 20; + _animDuration = 20; - if (_flags.isTalkie) { - chatX = 82; - //textCol = 143; - chatFirstFrame = 16; - chatLastFrame = 21; + if (_vm->gameFlags().isTalkie) { + subTitleX = 82; + subTitleFirstFrame = 16; + subTitleLastFrame = 21; voiceIndex = 41; } else { - chatX = 62; - //textCol = 137; - chatFirstFrame = 9; - chatLastFrame = 13; + subTitleX = 62; + subTitleFirstFrame = 9; + subTitleLastFrame = 13; } - chatY = (_flags.lang == Common::FR_FRA || _flags.lang == Common::DE_DEU) ? 88 :100; - chatW = 80; + subTitleY = (_vm->gameFlags().lang == Common::FR_FRA || _vm->gameFlags().lang == Common::DE_DEU) ? 88 :100; + subTitleW = 80; - seq_playWsaSyncDialogue(30, voiceIndex, 137, chatX, chatY, chatW, wsaObj, chatFirstFrame, chatLastFrame, x, y); - if (_flags.isTalkie) - _seqWsaCurrentFrame = 8; + playDialogueAnimation(30, voiceIndex, 137, subTitleX, subTitleY, subTitleW, wsaObj, subTitleFirstFrame, subTitleLastFrame, x, y); + if (_vm->gameFlags().isTalkie) + _animCurrentFrame = 8; break; case 9: case 16: - if (_flags.isTalkie) { + if (_vm->gameFlags().isTalkie) { if (frm == 16) break; - chatX = 64; - //textCol = 137; - chatFirstFrame = 9; - chatLastFrame = 13; + subTitleX = 64; + subTitleFirstFrame = 9; + subTitleLastFrame = 13; voiceIndex = 42; } else { if (frm == 9) break; - chatX = 80; - //textCol = 143; - chatFirstFrame = 16; - chatLastFrame = 21; + subTitleX = 80; + subTitleFirstFrame = 16; + subTitleLastFrame = 21; } - chatY = 100; - chatW = 100; + subTitleY = 100; + subTitleW = 100; - seq_playWsaSyncDialogue(31, voiceIndex, 143, chatX, chatY, chatW, wsaObj, chatFirstFrame, chatLastFrame, x, y); - if (_flags.isTalkie) - _seqWsaCurrentFrame = 21; + playDialogueAnimation(31, voiceIndex, 143, subTitleX, subTitleY, subTitleW, wsaObj, subTitleFirstFrame, subTitleLastFrame, x, y); + if (_vm->gameFlags().isTalkie) + _animCurrentFrame = 21; break; default: break; } - _seqFrameCounter++; + _callbackCurrentFrame++; return 0; } -int KyraEngine_HoF::seq_finaleFirates(WSAMovie_v2 *wsaObj, int x, int y, int frm) { - _seqSubFrameEndTimeInternal = 0; - int chatX = 0; - int chatY = 0; - int chatW = 0; +int SeqPlayer_HOF::cbHOF_firates(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + uint32 frameEnd = 0; + int subTitleX = 0; + int subTitleY = 0; + int subTitleW = 0; uint16 voiceIndex = 0; switch (frm) { @@ -1318,87 +2716,87 @@ int KyraEngine_HoF::seq_finaleFirates(WSAMovie_v2 *wsaObj, int x, int y, int frm _screen->copyPage(12, 2); _screen->copyPage(2, 0); _screen->updateScreen(); - seq_sequenceCommand(9); - _seqSubFrameEndTimeInternal = _system->getMillis() + 480 * _tickLength; - seq_printCreditsString(76, 240, 40, _seqTextColorMap, 252); - seq_printCreditsString(77, 240, 50, _seqTextColorMap, 252); - seq_printCreditsString(78, 240, 60, _seqTextColorMap, _seqTextColor[0]); - seq_printCreditsString(79, 240, 70, _seqTextColorMap, _seqTextColor[0]); - seq_printCreditsString(80, 240, 80, _seqTextColorMap, _seqTextColor[0]); - seq_printCreditsString(84, 240, 100, _seqTextColorMap, 252); - seq_printCreditsString(85, 240, 110, _seqTextColorMap, _seqTextColor[0]); - seq_printCreditsString(99, 240, 130, _seqTextColorMap, 252); - seq_printCreditsString(100, 240, 140, _seqTextColorMap, _seqTextColor[0]); - delay(_seqSubFrameEndTimeInternal - _system->getMillis()); - _seqEndTime = 0; + doTransition(9); + frameEnd = _system->getMillis() + 480 * _vm->tickLength(); + printFadingText(76, 240, 40, _textColorMap, 252); + printFadingText(77, 240, 50, _textColorMap, 252); + printFadingText(78, 240, 60, _textColorMap, _textColor[0]); + printFadingText(79, 240, 70, _textColorMap, _textColor[0]); + printFadingText(80, 240, 80, _textColorMap, _textColor[0]); + printFadingText(84, 240, 100, _textColorMap, 252); + printFadingText(85, 240, 110, _textColorMap, _textColor[0]); + printFadingText(99, 240, 130, _textColorMap, 252); + printFadingText(100, 240, 140, _textColorMap, _textColor[0]); + delayUntil(frameEnd); + setCountDown(0); break; case 0: - _seqTextColor[1] = _screen->findLeastDifferentColor(_seqTextColorPresets, _screen->getPalette(0), 1, 255) & 0xff; - memset(_seqTextColorMap, _seqTextColor[1], 16); - _seqTextColor[0] = _seqTextColorMap[1] = 0xff; - _screen->setTextColorMap(_seqTextColorMap); + _textColor[1] = _screen->findLeastDifferentColor(_textColorPresets, _screen->getPalette(0), 1, 255) & 0xFF; + memset(_textColorMap, _textColor[1], 16); + _textColor[0] = _textColorMap[1] = 0xFF; + _screen->setTextColorMap(_textColorMap); break; case 6: - seq_playTalkText(_flags.isTalkie ? 31 : 27); + playSoundAndDisplaySubTitle(_vm->gameFlags().isTalkie ? 31 : 27); break; case 14: case 15: - if (!((frm == 15 && !_flags.isTalkie) || (frm == 14 && _flags.isTalkie))) + if (!((frm == 15 && !_vm->gameFlags().isTalkie) || (frm == 14 && _vm->gameFlags().isTalkie))) break; - seq_playTalkText(_flags.isTalkie ? 31 : 27); + playSoundAndDisplaySubTitle(_vm->gameFlags().isTalkie ? 31 : 27); - if (_flags.lang == Common::DE_DEU) { - chatX = 82; - chatY = 84; - chatW = 140; + if (_vm->gameFlags().lang == Common::DE_DEU) { + subTitleX = 82; + subTitleY = 84; + subTitleW = 140; } else { - chatX = 74; - chatY = (_flags.lang == Common::FR_FRA) ? 96: 108; - chatW = 80; + subTitleX = 74; + subTitleY = (_vm->gameFlags().lang == Common::FR_FRA) ? 96: 108; + subTitleW = 80; } - if (_flags.isTalkie) + if (_vm->gameFlags().isTalkie) voiceIndex = 43; - seq_playWsaSyncDialogue(32, voiceIndex, 137, chatX, chatY, chatW, wsaObj, 14, 16, x, y); + playDialogueAnimation(32, voiceIndex, 137, subTitleX, subTitleY, subTitleW, wsaObj, 14, 16, x, y); break; case 28: - seq_playTalkText(_flags.isTalkie ? 32 : 28); + playSoundAndDisplaySubTitle(_vm->gameFlags().isTalkie ? 32 : 28); break; case 29: - seq_playTalkText(_flags.isTalkie ? 33 : 29); + playSoundAndDisplaySubTitle(_vm->gameFlags().isTalkie ? 33 : 29); break; case 31: - if (_flags.isTalkie) + if (_vm->gameFlags().isTalkie) voiceIndex = 44; - chatX = 90; - chatY = (_flags.lang == Common::DE_DEU) ? 60 : 76; - chatW = 80; + subTitleX = 90; + subTitleY = (_vm->gameFlags().lang == Common::DE_DEU) ? 60 : 76; + subTitleW = 80; - seq_playWsaSyncDialogue(33, voiceIndex, 143, chatX, chatY, chatW, wsaObj, 31, 34, x, y); + playDialogueAnimation(33, voiceIndex, 143, subTitleX, subTitleY, subTitleW, wsaObj, 31, 34, x, y); break; case 35: - _seqFrameDelay = 300; + _animDuration = 300; break; default: break; } - _seqFrameCounter++; + _callbackCurrentFrame++; return 0; } -int KyraEngine_HoF::seq_finaleFrash(WSAMovie_v2 *wsaObj, int x, int y, int frm) { +int SeqPlayer_HOF::cbHOF_frash(WSAMovie_v2 *wsaObj, int x, int y, int frm) { int tmp = 0; switch (frm) { @@ -1408,473 +2806,440 @@ int KyraEngine_HoF::seq_finaleFrash(WSAMovie_v2 *wsaObj, int x, int y, int frm) _screen->copyPage(2, 12); _screen->copyPage(2, 0); _screen->updateScreen(); - _seqFrameCounter = 0; - seq_loadNestedSequence(0, kSequenceFiggle); + _callbackCurrentFrame = 0; + startNestedAnimation(0, kNestedSequenceFiggle); break; case -1: - if (_flags.isTalkie) - seq_finaleActorScreen(); - _seqSpecialFlag = _flags.isTalkie; + if (_vm->gameFlags().isTalkie) + playHoFTalkieCredits(); + _talkieFinaleExtraFlag = _vm->gameFlags().isTalkie; break; case 0: - if (_seqFrameCounter == 1) { - _sound->playTrack(4); - _seqTextColor[1] = _screen->findLeastDifferentColor(_seqTextColorPresets, _screen->getPalette(0), 1, 255) & 0xff; - memset(_seqTextColorMap, _seqTextColor[1], 16); - _seqTextColor[0] = _seqTextColorMap[1] = 0xff; - _screen->setTextColorMap(_seqTextColorMap); + if (_callbackCurrentFrame == 1) { + _vm->sound()->playTrack(4); + _textColor[1] = _screen->findLeastDifferentColor(_textColorPresets, _screen->getPalette(0), 1, 255) & 0xFF; + memset(_textColorMap, _textColor[1], 16); + _textColor[0] = _textColorMap[1] = 0xFF; + _screen->setTextColorMap(_textColorMap); } - _seqFrameDelay = 10; + _animDuration = 10; break; case 1: - if (_seqFrameCounter < 20 && _seqSpecialFlag) { - _seqWsaCurrentFrame = 0; + if (_callbackCurrentFrame < 20 && _talkieFinaleExtraFlag) { + _animCurrentFrame = 0; } else { - _seqFrameDelay = _flags.isTalkie ? 500 : (300 + _rnd.getRandomNumberRng(1, 300)); - seq_playTalkText(_flags.isTalkie ? 26 : 22); - if (_seqSpecialFlag) { - _seqFrameCounter = 3; - _seqSpecialFlag = false; + _animDuration = _vm->gameFlags().isTalkie ? 500 : (300 + _vm->_rnd.getRandomNumberRng(1, 300)); + playSoundAndDisplaySubTitle(_vm->gameFlags().isTalkie ? 26 : 22); + if (_talkieFinaleExtraFlag) { + _callbackCurrentFrame = 3; + _talkieFinaleExtraFlag = false; } } break; case 2: - _seqFrameDelay = 20; + _animDuration = 20; break; case 3: - seq_playTalkText(_flags.isTalkie ? 27 : 23); - _seqFrameDelay = _flags.isTalkie ? 500 : (300 + _rnd.getRandomNumberRng(1, 300)); + playSoundAndDisplaySubTitle(_vm->gameFlags().isTalkie ? 27 : 23); + _animDuration = _vm->gameFlags().isTalkie ? 500 : (300 + _vm->_rnd.getRandomNumberRng(1, 300)); break; case 4: - _seqFrameDelay = 10; + _animDuration = 10; break; case 5: - seq_playTalkText(_flags.isTalkie ? 27 : 23); - tmp = _seqFrameCounter / 6; + playSoundAndDisplaySubTitle(_vm->gameFlags().isTalkie ? 27 : 23); + tmp = _callbackCurrentFrame / 6; if (tmp == 2) - _seqFrameDelay = _flags.isTalkie ? 7 : (1 + _rnd.getRandomNumberRng(1, 10)); + _animDuration = _vm->gameFlags().isTalkie ? 7 : (1 + _vm->_rnd.getRandomNumberRng(1, 10)); else if (tmp < 2) - _seqFrameDelay = _flags.isTalkie ? 500 : (300 + _rnd.getRandomNumberRng(1, 300)); + _animDuration = _vm->gameFlags().isTalkie ? 500 : (300 + _vm->_rnd.getRandomNumberRng(1, 300)); break; case 6: - _seqFrameDelay = 10; - tmp = _seqFrameCounter / 6; + _animDuration = 10; + tmp = _callbackCurrentFrame / 6; if (tmp == 2) - _seqWsaCurrentFrame = 4; + _animCurrentFrame = 4; else if (tmp < 2) - _seqWsaCurrentFrame = 0; + _animCurrentFrame = 0; break; case 7: - _seqFrameCounter = 0; - _seqFrameDelay = 5; - seq_playTalkText(_flags.isTalkie ? 26 : 22); + _callbackCurrentFrame = 0; + _animDuration = 5; + playSoundAndDisplaySubTitle(_vm->gameFlags().isTalkie ? 26 : 22); break; case 11: - if (_seqFrameCounter < 8) - _seqWsaCurrentFrame = 8; + if (_callbackCurrentFrame < 8) + _animCurrentFrame = 8; break; default: break; } - _seqFrameCounter++; + _callbackCurrentFrame++; return 0; } -void KyraEngine_HoF::seq_finaleActorScreen() { - static const uint8 colormap[] = {0, 0, 102, 102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - static const ScreenDim d = { 0x00, 0x0C, 0x28, 0xB4, 0xFF, 0x00, 0x00, 0x00 }; - - _screen->loadBitmap("finale.cps", 3, 3, &_screen->getPalette(0)); - _screen->setFont(Screen::FID_GOLDFONT_FNT); - - int talkieCreditsSize, talkieCreditsSpecialSize; - const uint8 *talkieCredits = _staticres->loadRawData(k2SeqplayCredits, talkieCreditsSize); - const char * const *talkieCreditsSpecial = _staticres->loadStrings(k2SeqplayCreditsSpecial, talkieCreditsSpecialSize); - - _sound->setSoundList(&_soundData[kMusicIngame]); - _sound->loadSoundFile(3); - _sound->playTrack(3); - - _screen->setTextColorMap(colormap); - _screen->copyRegion(0, 0, 0, 0, 320, 200, 2, 0); - _screen->updateScreen(); - _screen->fadeFromBlack(); - - _screen->_charWidth = -2; - uint8 *dataPtr = new uint8[0xafd]; - memcpy(dataPtr, talkieCredits, talkieCreditsSize); - _staticres->unloadId(k2SeqplayCredits); - - seq_displayScrollText(dataPtr, &d, 2, 6, 5, 1, Screen::FID_GOLDFONT_FNT, Screen::FID_GOLDFONT_FNT, 0, talkieCreditsSpecial); - delay(120); - - delete[] dataPtr; - _staticres->unloadId(k2SeqplayCreditsSpecial); - _sound->setSoundList(&_soundData[kMusicFinale]); - _sound->loadSoundFile(0); -} - -int KyraEngine_HoF::seq_finaleFiggle(WSAMovie_v2 *wsaObj, int x, int y, int frm) { - if (_seqFrameCounter == 10) - _seqEndTime = 0; - if (_seqFrameCounter == 10 || _seqFrameCounter == 5 || _seqFrameCounter == 7) - seq_playTalkText(_flags.isTalkie ? 45 : 30); +int SeqPlayer_HOF::cbHOF_figgle(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + if (_callbackCurrentFrame == 10) + setCountDown(0); + if (_callbackCurrentFrame == 10 || _callbackCurrentFrame == 5 || _callbackCurrentFrame == 7) + playSoundAndDisplaySubTitle(_vm->gameFlags().isTalkie ? 45 : 30); - _seqFrameCounter++; + _callbackCurrentFrame++; return frm; } -int KyraEngine_HoF::seq_demoVirgin(WSAMovie_v2 *wsaObj, int x, int y, int frm) { +int SeqPlayer_HOF::cbHOFDEMO_virgin(WSAMovie_v2 *wsaObj, int x, int y, int frm) { if (!frm) - delay(50 * _tickLength); + delayTicks(50); return 0; } -int KyraEngine_HoF::seq_demoWestwood(WSAMovie_v2 *wsaObj, int x, int y, int frm) { +int SeqPlayer_HOF::cbHOFDEMO_westwood(WSAMovie_v2 *wsaObj, int x, int y, int frm) { if (!frm) - _sound->playTrack(2); + _vm->sound()->playTrack(2); return 0; } -int KyraEngine_HoF::seq_demoTitle(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + +int SeqPlayer_HOF::cbHOFDEMO_title(WSAMovie_v2 *wsaObj, int x, int y, int frm) { if (!frm) { - _sound->playTrack(3); + _vm->sound()->playTrack(3); } else if (frm == 25) { - delay(60 * _tickLength); - _seqEndTime = 0; - seq_sequenceCommand(0); + delayTicks(60); + setCountDown(0); + doTransition(0); } return 0; } -int KyraEngine_HoF::seq_demoHill(WSAMovie_v2 *wsaObj, int x, int y, int frm) { +int SeqPlayer_HOF::cbHOFDEMO_hill(WSAMovie_v2 *wsaObj, int x, int y, int frm) { if (!frm) { - _sound->playTrack(4); + _vm->sound()->playTrack(4); } else if (frm == 25) { - seq_loadNestedSequence(0, kSequenceDemoWater); - _seqFrameDelay--; + startNestedAnimation(0, kNestedSequenceHoFDemoWater); + _animDuration--; } else if (frm > 25 && frm < 50) { - if (_seqFrameDelay > 3) - _seqFrameDelay--; + if (_animDuration > 3) + _animDuration--; } else if (frm == 95) { - _seqFrameDelay = 70; + _animDuration = 70; } else if (frm == 96) { - _seqFrameDelay = 7; + _animDuration = 7; } else if (frm == 129) { - seq_resetActiveWSA(0); + closeNestedAnimation(0); } return 0; } -int KyraEngine_HoF::seq_demoOuthome(WSAMovie_v2 *wsaObj, int x, int y, int frm) { +int SeqPlayer_HOF::cbHOFDEMO_outhome(WSAMovie_v2 *wsaObj, int x, int y, int frm) { switch (frm) { case 12: - seq_playTalkText(4); + playSoundAndDisplaySubTitle(4); break; case 32: - seq_playTalkText(7); + playSoundAndDisplaySubTitle(7); break; case 36: - seq_playTalkText(10); + playSoundAndDisplaySubTitle(10); break; case 57: - seq_playTalkText(9); + playSoundAndDisplaySubTitle(9); break; case 80: case 96: case 149: - _seqFrameDelay = 70; + _animDuration = 70; break; case 81: case 97: - _seqFrameDelay = 5; + _animDuration = 5; break; case 110: - seq_playTalkText(5); + playSoundAndDisplaySubTitle(5); break; case 137: - seq_playTalkText(6); + playSoundAndDisplaySubTitle(6); break; } return 0; } -int KyraEngine_HoF::seq_demoWharf(WSAMovie_v2 *wsaObj, int x, int y, int frm) { - if (!_seqFrameCounter) - seq_loadNestedSequence(0, kSequenceDemoWharf2); +int SeqPlayer_HOF::cbHOFDEMO_wharf(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + if (!_callbackCurrentFrame) + startNestedAnimation(0, kNestedSequenceHoFDemoWharf2); switch (frm) { case 0: - seq_playTalkText(11); + playSoundAndDisplaySubTitle(11); break; case 5: - if ((_seqFrameCounter / 8) <= 2 || _activeWSA[0].flags != -1) - _seqWsaCurrentFrame = 0; + if ((_callbackCurrentFrame / 8) <= 2 || _animSlots[0].flags != -1) + _animCurrentFrame = 0; else - seq_resetActiveWSA(0); + closeNestedAnimation(0); break; case 6: - seq_resetActiveWSA(0); + closeNestedAnimation(0); break; case 8: case 10: - seq_playTalkText(2); + playSoundAndDisplaySubTitle(2); break; case 13: - seq_playTalkText(7); + playSoundAndDisplaySubTitle(7); break; case 16: - seq_playTalkText(12); + playSoundAndDisplaySubTitle(12); break; default: break; } - _seqFrameCounter++; + _callbackCurrentFrame++; return 0; } -int KyraEngine_HoF::seq_demoDinob(WSAMovie_v2 *wsaObj, int x, int y, int frm) { +int SeqPlayer_HOF::cbHOFDEMO_dinob(WSAMovie_v2 *wsaObj, int x, int y, int frm) { if (frm == 0) { - if (!(_seqFrameCounter/8)) { - seq_loadNestedSequence(0, kSequenceDemoDinob2); - _seqWsaCurrentFrame = 0; + if (!(_callbackCurrentFrame/8)) { + startNestedAnimation(0, kNestedSequenceHoFDemoDinob2); + _animCurrentFrame = 0; } } else if (frm == 3) { - if (_activeWSA[0].flags != -1) { - _seqWsaCurrentFrame = 0; + if (_animSlots[0].flags != -1) { + _animCurrentFrame = 0; } else { - seq_resetActiveWSA(0); + closeNestedAnimation(0); _screen->copyPage(2, 12); } } else if (frm == 4) { - seq_resetActiveWSA(0); + closeNestedAnimation(0); } - _seqFrameCounter++; + _callbackCurrentFrame++; return 0; } -int KyraEngine_HoF::seq_demoFisher(WSAMovie_v2 *wsaObj, int x, int y, int frm) { - if (((_system->getMillis() - _seqStartTime) / (5 * _tickLength)) > 0) { - _seqStartTime = _system->getMillis(); - if (!_seqFrameCounter) { - seq_loadNestedSequence(0, kSequenceDemoBail); - seq_loadNestedSequence(1, kSequenceDemoDig); +int SeqPlayer_HOF::cbHOFDEMO_fisher(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + if (((_system->getMillis() - _fisherAnimCurTime) / (5 * _vm->tickLength())) > 0) { + _fisherAnimCurTime = _system->getMillis(); + if (!_callbackCurrentFrame) { + startNestedAnimation(0, kNestedSequenceHoFDemoBail); + startNestedAnimation(1, kNestedSequenceHoFDemoDig); } - if (_seqScrollTextCounter >= 0x18f && !_seqFrameCounter) + if (_scrollProgressCounter >= 0x18F && !_callbackCurrentFrame) return 0; - if (!_seqFrameCounter) { + if (!_callbackCurrentFrame) { _screen->loadBitmap("adtext.cps", 4, 4, 0); _screen->loadBitmap("adtext2.cps", 6, 6, 0); _screen->copyPageMemory(6, 0, 4, 64000, 1024); _screen->copyPageMemory(6, 1023, 6, 0, 64000); - _seqScrollTextCounter = 0; + _scrollProgressCounter = 0; } - seq_scrollPage(24, 144); - _seqFrameCounter++; - if (_seqFrameCounter < 0x256 || _seqFrameCounter > 0x31c) { - if (_seqFrameCounter < 0x174 || _seqFrameCounter > 0x1d7) { - if (_seqFrameCounter < 0x84 || _seqFrameCounter > 0xe7) { - _seqScrollTextCounter++; + updateDemoAdText(24, 144); + _callbackCurrentFrame++; + if (_callbackCurrentFrame < 0x256 || _callbackCurrentFrame > 0x31C) { + if (_callbackCurrentFrame < 0x174 || _callbackCurrentFrame > 0x1D7) { + if (_callbackCurrentFrame < 0x84 || _callbackCurrentFrame > 0xE7) { + _scrollProgressCounter++; } } } - if (_seqFrameCounter > 0x31e) { - seq_resetActiveWSA(0); - seq_resetActiveWSA(1); - _seqEndTime = 0; + if (_callbackCurrentFrame > 0x31E) { + closeNestedAnimation(0); + closeNestedAnimation(1); + setCountDown(0); _screen->copyPage(2, 12); } } else { - seq_scrollPage(24, 144); + updateDemoAdText(24, 144); } return 0; } -int KyraEngine_HoF::seq_demoWharf2(WSAMovie_v2 *wsaObj, int x, int y, int frm) { +int SeqPlayer_HOF::cbHOFDEMO_wharf2(WSAMovie_v2 *wsaObj, int x, int y, int frm) { if (frm == 69) - _seqWsaCurrentFrame = 8; + _animCurrentFrame = 8; return frm; } -int KyraEngine_HoF::seq_demoDinob2(WSAMovie_v2 *wsaObj, int x, int y, int frm) { +int SeqPlayer_HOF::cbHOFDEMO_dinob2(WSAMovie_v2 *wsaObj, int x, int y, int frm) { switch (frm) { case 19: - seq_playTalkText(13); + playSoundAndDisplaySubTitle(13); break; case 54: - seq_playTalkText(15); + playSoundAndDisplaySubTitle(15); break; case 61: - seq_playTalkText(16); + playSoundAndDisplaySubTitle(16); break; case 69: - seq_playTalkText(14); + playSoundAndDisplaySubTitle(14); break; case 77: - seq_playTalkText(13); + playSoundAndDisplaySubTitle(13); break; case 79: - _seqWsaCurrentFrame = 4; + _animCurrentFrame = 4; break; } return frm; } -int KyraEngine_HoF::seq_demoWater(WSAMovie_v2 *wsaObj, int x, int y, int frm) { +int SeqPlayer_HOF::cbHOFDEMO_water(WSAMovie_v2 *wsaObj, int x, int y, int frm) { if (frm == 1) - seq_playTalkText(11); + playSoundAndDisplaySubTitle(11); return frm; } -int KyraEngine_HoF::seq_demoBail(WSAMovie_v2 *wsaObj, int x, int y, int frm) { +int SeqPlayer_HOF::cbHOFDEMO_bail(WSAMovie_v2 *wsaObj, int x, int y, int frm) { return frm; } -int KyraEngine_HoF::seq_demoDig(WSAMovie_v2 *wsaObj, int x, int y, int frm) { +int SeqPlayer_HOF::cbHOFDEMO_dig(WSAMovie_v2 *wsaObj, int x, int y, int frm) { return frm; } #ifdef ENABLE_LOL -int KyraEngine_HoF::seq_lolDemoScene1(WSAMovie_v2 *wsaObj, int x, int y, int frm) { +int SeqPlayer_HOF::cbLOLDEMO_scene1(WSAMovie_v2 *wsaObj, int x, int y, int frm) { Palette &tmpPal = _screen->getPalette(2); - if (!(_seqFrameCounter % 100)) { - if (_seqFrameCounter == 0) { - _sound->haltTrack(); - _sound->playTrack(6); + if (!(_callbackCurrentFrame % 100)) { + if (_callbackCurrentFrame == 0) { + _vm->sound()->haltTrack(); + _vm->sound()->playTrack(6); } tmpPal.copy(_screen->getPalette(0)); - for (int i = 3; i < 0x300; i++) { + for (int i = 3; i < 768; i++) { tmpPal[i] = ((int)tmpPal[i] * 120) / 64; - if (tmpPal[i] > 0x3f) - tmpPal[i] = 0x3f; + if (tmpPal[i] > 0x3F) + tmpPal[i] = 0x3F; } - seq_playTalkText(_rnd.getRandomBit()); + playSoundAndDisplaySubTitle(_vm->_rnd.getRandomBit()); _screen->setScreenPalette(tmpPal); _screen->updateScreen(); - delay(8); + _vm->delay(8); } else { _screen->setScreenPalette(_screen->getPalette(0)); _screen->updateScreen(); - if (_seqFrameCounter == 40) - seq_playTalkText(3); + if (_callbackCurrentFrame == 40) + playSoundAndDisplaySubTitle(3); } - _seqFrameCounter++; + _callbackCurrentFrame++; return frm; } -int KyraEngine_HoF::seq_lolDemoScene2(WSAMovie_v2 *wsaObj, int x, int y, int frm) { - switch (_seqFrameCounter - 17) { +int SeqPlayer_HOF::cbLOLDEMO_scene2(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + switch (frm - 17) { case 0: - _seqFrameDelay = 8; + _animDuration = 8; break; case 3: case 6: case 9: - seq_playTalkText(8); + playSoundEffect(8, 255 - ((26 - frm) << 3)); break; case 15: - seq_playTalkText(9); + playSoundAndDisplaySubTitle(9); break; case 18: - seq_playTalkText(2); + playSoundAndDisplaySubTitle(2); break; default: break; } - _seqFrameCounter++; + _callbackCurrentFrame++; return frm; } -int KyraEngine_HoF::seq_lolDemoScene3(WSAMovie_v2 *wsaObj, int x, int y, int frm) { - if (_seqFrameCounter == 1) - seq_playTalkText(6); - else if (frm == 26) - seq_playTalkText(7); +int SeqPlayer_HOF::cbLOLDEMO_scene3(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + if (frm == 1) + playSoundAndDisplaySubTitle(6); + else if (frm == 24) + playSoundAndDisplaySubTitle(7); - _seqFrameCounter++; + _callbackCurrentFrame++; return frm; } -int KyraEngine_HoF::seq_lolDemoScene4(WSAMovie_v2 *wsaObj, int x, int y, int frm) { - switch (_seqFrameCounter) { +int SeqPlayer_HOF::cbLOLDEMO_scene4(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + switch (frm) { case 11: case 14: case 17: case 20: - seq_playTalkText(8); + playSoundEffect(8, 255 - ((22 - frm) << 3)); break; case 22: - seq_playTalkText(11); + playSoundAndDisplaySubTitle(11); break; case 24: - seq_playTalkText(8); + playSoundAndDisplaySubTitle(8); break; case 30: - seq_playTalkText(15); + playSoundAndDisplaySubTitle(15); break; case 34: - seq_playTalkText(14); + playSoundAndDisplaySubTitle(14); break; case 38: - seq_playTalkText(13); + playSoundAndDisplaySubTitle(13); break; case 42: - seq_playTalkText(12); + playSoundAndDisplaySubTitle(12); break; default: break; } - _seqFrameCounter++; + _callbackCurrentFrame++; return frm; } -int KyraEngine_HoF::seq_lolDemoScene5(WSAMovie_v2 *wsaObj, int x, int y, int frm) { - switch (_seqFrameCounter++) { +int SeqPlayer_HOF::cbLOLDEMO_scene5(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + switch (_callbackCurrentFrame++) { case 0: case 4: case 6: @@ -1889,13 +3254,13 @@ int KyraEngine_HoF::seq_lolDemoScene5(WSAMovie_v2 *wsaObj, int x, int y, int frm case 26: case 28: case 30: - seq_playTalkText(15); + playSoundEffect(15, 255 - ((31 - frm) << 3)); break; case 32: - seq_playTalkText(16); + playSoundAndDisplaySubTitle(16); break; case 42: - seq_playTalkText(6); + playSoundAndDisplaySubTitle(6); break; default: break; @@ -1903,24 +3268,24 @@ int KyraEngine_HoF::seq_lolDemoScene5(WSAMovie_v2 *wsaObj, int x, int y, int frm return frm; } -int KyraEngine_HoF::seq_lolDemoText5(WSAMovie_v2 *wsaObj, int x, int y, int frm) { - if (_seqFrameCounter++ == 100) - seq_playTalkText(5); +int SeqPlayer_HOF::cbLOLDEMO_text5(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + if (_callbackCurrentFrame++ == 100) + playSoundAndDisplaySubTitle(5); return frm; } -int KyraEngine_HoF::seq_lolDemoScene6(WSAMovie_v2 *wsaObj, int x, int y, int frm) { - while (_seqScrollTextCounter < 0x122) { - _seqEndTime = _system->getMillis() + 6 * _tickLength; - if (!_seqFrameCounter) { +int SeqPlayer_HOF::cbLOLDEMO_scene6(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + while (_scrollProgressCounter < 290) { + setCountDown(6); + if (!_callbackCurrentFrame) { _screen->loadBitmap("adtext.cps", 4, 4, 0); _screen->loadBitmap("adtext2.cps", 6, 6, 0); _screen->copyPageMemory(6, 0, 4, 64000, 1024); _screen->copyPageMemory(6, 1023, 6, 0, 64000); - _seqScrollTextCounter = 0; + _scrollProgressCounter = 0; } - if (_seqFrameCounter % 175) { + if (_callbackCurrentFrame % 175) { _screen->setScreenPalette(_screen->getPalette(0)); } else { Palette &tmpPal = _screen->getPalette(2); @@ -1928,27 +3293,29 @@ int KyraEngine_HoF::seq_lolDemoScene6(WSAMovie_v2 *wsaObj, int x, int y, int frm for (int i = 3; i < 0x300; i++) { tmpPal[i] = ((int)tmpPal[i] * 120) / 64; - if (tmpPal[i] > 0x3f) - tmpPal[i] = 0x3f; + if (tmpPal[i] > 0x3F) + tmpPal[i] = 0x3F; } - seq_playTalkText(_rnd.getRandomBit()); + playSoundAndDisplaySubTitle(_vm->_rnd.getRandomBit()); _screen->setScreenPalette(tmpPal); _screen->updateScreen(); - delay(8); + _vm->delay(8); } - if (_seqFrameCounter == 40 || _seqFrameCounter == 80 || _seqFrameCounter == 150 || _seqFrameCounter == 300) - seq_playTalkText(3); + if (_callbackCurrentFrame == 40 || _callbackCurrentFrame == 80 || _callbackCurrentFrame == 150 || _callbackCurrentFrame == 300) + playSoundAndDisplaySubTitle(3); _screen->copyPage(12, 2); - seq_scrollPage(70, 130); + updateDemoAdText(70, 130); _screen->copyPage(2, 0); _screen->updateScreen(); - _seqFrameCounter++; - if (_seqFrameCounter < 128 || _seqFrameCounter > 207) - _seqScrollTextCounter++; - delayUntil(_seqEndTime); + _callbackCurrentFrame++; + if (_callbackCurrentFrame < 128 || _callbackCurrentFrame > 207) + _scrollProgressCounter++; + + while (countDownRunning()) + delayTicks(1); } _screen->copyPage(2, 12); @@ -1956,776 +3323,16 @@ int KyraEngine_HoF::seq_lolDemoScene6(WSAMovie_v2 *wsaObj, int x, int y, int frm } #endif // ENABLE_LOL -uint32 KyraEngine_HoF::seq_activeTextsTimeLeft() { - uint32 res = 0; - - for (int i = 0; i < 10; i++) { - uint32 chatend = (_activeText[i].duration + _activeText[i].startTime); - uint32 curtime = _system->getMillis(); - if (_activeText[i].duration != -1 && chatend > curtime) { - chatend -= curtime; - if (res < chatend) - res = chatend; - } - } - - return res; -} - -void KyraEngine_HoF::seq_processWSAs() { - for (int i = 0; i < 8; i++) { - if (_activeWSA[i].flags != -1) { - if (seq_processNextSubFrame(i)) - seq_resetActiveWSA(i); - } - } -} - -void KyraEngine_HoF::seq_processText() { - int curPage = _screen->setCurPage(2); - char outputStr[70]; - - for (int i = 0; i < 10; i++) { - if (_activeText[i].startTime + _activeText[i].duration > _system->getMillis() && _activeText[i].duration != -1) { - - char *srcStr = seq_preprocessString(_sequenceStrings[_activeText[i].strIndex], _activeText[i].width); - int yPos = _activeText[i].y; - - while (*srcStr) { - uint32 linePos = 0; - for (; *srcStr; linePos++) { - if (*srcStr == 0x0d) // Carriage return - break; - outputStr[linePos] = *srcStr; - srcStr++; - } - outputStr[linePos] = 0; - if (*srcStr == 0x0d) - srcStr++; - - uint8 textColor = (_activeText[i].textcolor >= 0) ? _activeText[i].textcolor : _seqTextColor[0]; - _screen->printText(outputStr, _activeText[i].x - (_screen->getTextWidth(outputStr) / 2), yPos, textColor, 0); - yPos += 10; - } - } else { - _activeText[i].duration = -1; - } - } - - _screen->setCurPage(curPage); -} - -char *KyraEngine_HoF::seq_preprocessString(const char *srcStr, int width) { - char *dstStr = _seqProcessedString; - int lineStart = 0; - int linePos = 0; - - while (*srcStr) { - while (*srcStr && *srcStr != 0x20) // Space - dstStr[lineStart + linePos++] = *srcStr++; - dstStr[lineStart + linePos] = 0; - - int len = _screen->getTextWidth(&dstStr[lineStart]); - if (width >= len && *srcStr) { - dstStr[lineStart + linePos++] = *srcStr++; - } else { - dstStr[lineStart + linePos] = 0x0d; // Carriage return - lineStart += linePos + 1; - linePos = 0; - if (*srcStr) - srcStr++; - } - } - dstStr[lineStart + linePos] = 0; - - return strlen(_seqProcessedString) ? dstStr : 0; -} - -void KyraEngine_HoF::seq_sequenceCommand(int command) { - for (int i = 0; i < 8; i++) - seq_resetActiveWSA(i); - - switch (command) { - case 0: - _screen->fadeToBlack(36); - _screen->getPalette(0).clear(); - _screen->getPalette(1).clear(); - break; - - case 1: - seq_playTalkText(_rnd.getRandomBit()); - - _screen->getPalette(0).fill(0, 256, 0x3F); - _screen->fadePalette(_screen->getPalette(0), 16); - - _screen->copyPalette(1, 0); - break; - - case 3: - _screen->copyPage(2, 0); - _screen->fadePalette(_screen->getPalette(0), 16); - _screen->copyPalette(1, 0); - break; - - case 4: - _screen->copyPage(2, 0); - _screen->fadePalette(_screen->getPalette(0), 36); - _screen->copyPalette(1, 0); - break; - - case 5: - _screen->copyPage(2, 0); - break; - - case 6: - // UNUSED - // seq_loadBLD("library.bld"); - break; - - case 7: - // UNUSED - // seq_loadBLD("marco.bld"); - break; - - case 8: - _screen->fadeToBlack(16); - _screen->getPalette(0).clear(); - _screen->getPalette(1).clear(); - - delay(120 * _tickLength); - break; - - case 9: { - Palette &pal = _screen->getPalette(0); - for (int i = 0; i < 256; i++) { - int pv = (pal[3 * i] + pal[3 * i + 1] + pal[3 * i + 2]) / 3; - pal[3 * i] = pal[3 * i + 1] = pal[3 * i + 2] = pv & 0xff; - } - - //int a = 0x100; - //int d = (0x800 << 5) - 0x100; - //pal[3 * i] = pal[3 * i + 1] = pal[3 * i + 2] = 0x3f; - - _screen->fadePalette(pal, 64); - _screen->copyPalette(1, 0); - } break; - - default: - break; - } -} - -void KyraEngine_HoF::seq_cmpFadeFrame(const char *cmpFile) { - _screen->copyPage(10, 2); - _screen->copyPage(4, 10); - _screen->clearPage(6); - _screen->loadBitmap(cmpFile, 6, 6, 0); - _screen->copyPage(12, 4); - - for (int i = 0; i < 3; i++) { - uint32 endtime = _system->getMillis() + 4 * _tickLength; - _screen->cmpFadeFrameStep(4, 320, 200, 0, 0, 2, 320, 200, 0, 0, 320, 200, 6); - _screen->copyRegion(0, 0, 0, 0, 320, 200, 2, 0); - _screen->updateScreen(); - delayUntil(endtime); - } - - _screen->copyPage(4, 0); - _screen->updateScreen(); - _screen->copyPage(4, 2); - _screen->copyPage(4, 6); - _screen->copyPage(10, 4); -} - -void KyraEngine_HoF::seq_playTalkText(uint8 chatNum) { - assert(chatNum < _sequenceSoundListSize); - - if (chatNum < 12 && !_flags.isDemo && textEnabled()) - seq_setTextEntry(chatNum, 160, 168, _sequenceStringsDuration[chatNum], 160); - - _sound->voicePlay(_sequenceSoundList[chatNum], &_speechHandle); -} - -void KyraEngine_HoF::seq_waitForTextsTimeout() { - uint32 longest = seq_activeTextsTimeLeft() + _system->getMillis(); - uint32 now = _system->getMillis(); - - if (textEnabled()) { - if (longest > now) - delay(longest - now); - } else if (speechEnabled()) { - while (snd_voiceIsPlaying()) - delay(_tickLength); - } - - seq_resetAllTextEntries(); -} - -void KyraEngine_HoF::seq_resetAllTextEntries() { - for (int i = 0; i < 10; i++) - _activeText[i].duration = -1; -} - -int KyraEngine_HoF::seq_setTextEntry(uint16 strIndex, uint16 posX, uint16 posY, int duration, uint16 width) { - for (int i = 0; i < 10; i++) { - if (_activeText[i].duration != -1) { - if (i < 9) - continue; - else - return -1; - } - - _activeText[i].strIndex = strIndex; - _activeText[i].x = posX; - _activeText[i].y = posY; - _activeText[i].duration = duration * _tickLength; - _activeText[i].width = width; - _activeText[i].startTime = _system->getMillis(); - _activeText[i].textcolor = -1; - - return i; - } - return -1; -} - -void KyraEngine_HoF::seq_loadNestedSequence(int wsaNum, int seqNum) { - if (_activeWSA[wsaNum].flags != -1) - return; - - NestedSequence s = _sequences->seqn[seqNum]; - - if (!_activeWSA[wsaNum].movie) { - _activeWSA[wsaNum].movie = new WSAMovie_v2(this); - assert(_activeWSA[wsaNum].movie); - } - - _activeWSA[wsaNum].movie->close(); - - _activeWSA[wsaNum].movie->open(s.wsaFile, 0, 0); - - if (!_activeWSA[wsaNum].movie->opened()) { - delete _activeWSA[wsaNum].movie; - _activeWSA[wsaNum].movie = 0; - return; - } - - _activeWSA[wsaNum].endFrame = s.endFrame; - _activeWSA[wsaNum].startFrame = _activeWSA[wsaNum].currentFrame = s.startframe; - _activeWSA[wsaNum].frameDelay = s.frameDelay; - _activeWSA[wsaNum].callback = _callbackN[seqNum]; - _activeWSA[wsaNum].control = s.wsaControl; - - _activeWSA[wsaNum].flags = s.flags | 1; - _activeWSA[wsaNum].x = s.x; - _activeWSA[wsaNum].y = s.y; - _activeWSA[wsaNum].startupCommand = s.startupCommand; - _activeWSA[wsaNum].finalCommand = s.finalCommand; - _activeWSA[wsaNum].lastFrame = 0xffff; - - seq_nestedSequenceFrame(s.startupCommand, wsaNum); - - if (!s.startupCommand) - seq_processNextSubFrame(wsaNum); - - _activeWSA[wsaNum].nextFrame = _system->getMillis(); -} - -void KyraEngine_HoF::seq_nestedSequenceFrame(int command, int wsaNum) { - int xa = 0, ya = 0; - command--; - if (!_activeWSA[wsaNum].movie || skipFlag() || shouldQuit() || _abortIntroFlag) - return; - - switch (command) { - case 0: - xa = -_activeWSA[wsaNum].movie->xAdd(); - ya = -_activeWSA[wsaNum].movie->yAdd(); - _activeWSA[wsaNum].movie->displayFrame(0, 8, xa, ya, 0, 0, 0); - seq_animatedSubFrame(8, 2, 7, 8, _activeWSA[wsaNum].movie->xAdd(), _activeWSA[wsaNum].movie->yAdd(), - _activeWSA[wsaNum].movie->width(), _activeWSA[wsaNum].movie->height(), 1, 2); - break; - - case 1: - xa = -_activeWSA[wsaNum].movie->xAdd(); - ya = -_activeWSA[wsaNum].movie->yAdd(); - _activeWSA[wsaNum].movie->displayFrame(0, 8, xa, ya, 0, 0, 0); - seq_animatedSubFrame(8, 2, 7, 8, _activeWSA[wsaNum].movie->xAdd(), _activeWSA[wsaNum].movie->yAdd(), - _activeWSA[wsaNum].movie->width(), _activeWSA[wsaNum].movie->height(), 1, 1); - break; - - case 2: - seq_waitForTextsTimeout(); - xa = -_activeWSA[wsaNum].movie->xAdd(); - ya = -_activeWSA[wsaNum].movie->yAdd(); - _activeWSA[wsaNum].movie->displayFrame(0x15, 8, xa, ya, 0, 0, 0); - seq_animatedSubFrame(8, 2, 7, 8, _activeWSA[wsaNum].movie->xAdd(), _activeWSA[wsaNum].movie->yAdd(), - _activeWSA[wsaNum].movie->width(), _activeWSA[wsaNum].movie->height(), 0, 2); - break; - - case 3: - _screen->copyPage(2, 10); - _activeWSA[wsaNum].movie->displayFrame(0, 2, 0, 0, 0, 0, 0); - _screen->copyPage(2, 12); - seq_cmpFadeFrame("scene2.cmp"); - break; - - case 4: - _screen->copyPage(2, 10); - _activeWSA[wsaNum].movie->displayFrame(0, 2, 0, 0, 0, 0, 0); - _screen->copyPage(2, 12); - seq_cmpFadeFrame("scene3.cmp"); - break; - - default: - break; - } -} - -void KyraEngine_HoF::seq_animatedSubFrame(int srcPage, int dstPage, int delaytime, int steps, - int x, int y, int w, int h, int openClose, int directionFlags) { - if (openClose) { - for (int i = 1; i < steps; i++) { - uint32 endtime = _system->getMillis() + delaytime * _tickLength; - - int w2 = (((w * 256) / steps) * i) / 256; - int h2 = (((h * 256) / steps) * i) / 256; - - int ym = (directionFlags & 2) ? (h - h2) : 0; - int xm = (directionFlags & 1) ? (w - w2) : 0; - - _screen->wsaFrameAnimationStep(0, 0, x + xm, y + ym, w, h, w2, h2, srcPage, dstPage, 0); - - _screen->copyPage(dstPage, 6); - _screen->copyPage(dstPage, 0); - _screen->updateScreen(); - - _screen->copyPage(12, dstPage); - delayUntil(endtime); - } - - _screen->wsaFrameAnimationStep(0, 0, x, y, w, h, w, h, srcPage, dstPage, 0); - _screen->copyPage(dstPage, 6); - _screen->copyPage(dstPage, 0); - _screen->updateScreen(); - } else { - _screen->copyPage(12, dstPage); - for (int i = steps; i; i--) { - uint32 endtime = _system->getMillis() + delaytime * _tickLength; - - int w2 = (((w * 256) / steps) * i) / 256; - int h2 = (((h * 256) / steps) * i) / 256; +#undef CASE_ALT - int ym = (directionFlags & 2) ? (h - h2) : 0; - int xm = (directionFlags & 1) ? (w - w2) : 0; - - _screen->wsaFrameAnimationStep(0, 0, x + xm, y + ym, w, h, w2, h2, srcPage, dstPage, 0); - - _screen->copyPage(dstPage, 6); - _screen->copyPage(dstPage, 0); - _screen->updateScreen(); - - _screen->copyPage(12, dstPage); - delayUntil(endtime); - } - } -} - -void KyraEngine_HoF::seq_resetActiveWSA(int wsaNum) { - if (_activeWSA[wsaNum].flags == -1) - return; - - _activeWSA[wsaNum].flags = -1; - seq_nestedSequenceFrame(_activeWSA[wsaNum].finalCommand, wsaNum); - _activeWSA[wsaNum].movie->close(); -} - -void KyraEngine_HoF::seq_unloadWSA(int wsaNum) { - if (_activeWSA[wsaNum].movie) { - _activeWSA[wsaNum].movie->close(); - delete _activeWSA[wsaNum].movie; - _activeWSA[wsaNum].movie = 0; - } -} - -bool KyraEngine_HoF::seq_processNextSubFrame(int wsaNum) { - uint32 currentFrame = _activeWSA[wsaNum].currentFrame; - uint32 currentTime = _system->getMillis(); - - if (_activeWSA[wsaNum].callback && currentFrame != _activeWSA[wsaNum].lastFrame) { - _activeWSA[wsaNum].lastFrame = currentFrame; - currentFrame = (this->*_activeWSA[wsaNum].callback)(_activeWSA[wsaNum].movie, _activeWSA[wsaNum].x, _activeWSA[wsaNum].y, currentFrame); - } - - if (_activeWSA[wsaNum].movie) { - if (_activeWSA[wsaNum].flags & 0x20) { - _activeWSA[wsaNum].movie->displayFrame(_activeWSA[wsaNum].control[currentFrame].index, 2, _activeWSA[wsaNum].x, _activeWSA[wsaNum].y, 0x4000, 0, 0); - _activeWSA[wsaNum].frameDelay = _activeWSA[wsaNum].control[currentFrame].delay; - } else { - _activeWSA[wsaNum].movie->displayFrame(currentFrame % _activeWSA[wsaNum].movie->frames(), 2, _activeWSA[wsaNum].x, _activeWSA[wsaNum].y, 0x4000, 0, 0); - } - } - - if (_activeWSA[wsaNum].flags & 0x10) { - currentFrame = (currentTime - _activeWSA[wsaNum].nextFrame) / (_activeWSA[wsaNum].frameDelay * _tickLength); - } else { - if (((int32)(currentTime - _activeWSA[wsaNum].nextFrame) / (int32)(_activeWSA[wsaNum].frameDelay * _tickLength)) > 0) { - currentFrame++; - _activeWSA[wsaNum].nextFrame = currentTime; - } - } - - bool res = false; - - if (currentFrame >= _activeWSA[wsaNum].endFrame) { - int sw = ((_activeWSA[wsaNum].flags & 0x1e) - 2); - switch (sw) { - case 0: - res = true; - currentFrame = _activeWSA[wsaNum].endFrame; - _screen->copyPage(2, 12); - break; - - case 6: - case 8: - currentFrame = _activeWSA[wsaNum].endFrame - 1; - break; - - case 2: - case 10: - currentFrame = _activeWSA[wsaNum].startFrame; - break; - - default: - currentFrame = _activeWSA[wsaNum].endFrame - 1; - res = true; - } - } - - _activeWSA[wsaNum].currentFrame = currentFrame & 0xffff; - return res; -} - -void KyraEngine_HoF::seq_printCreditsString(uint16 strIndex, int x, int y, const uint8 *colorMap, uint8 textcolor) { - uint8 colormap[16]; - if (skipFlag() || shouldQuit() || _abortIntroFlag || _menuChoice) - return; - - Screen::FontId of = _screen->setFont(Screen::FID_8_FNT); - - memset(&_screen->getPalette(0)[0x2fa], 0x3f, 6); - _screen->getPalette(0)[0x2f6] = 0x3f; - _screen->getPalette(0)[0x2f5] = 0x20; - _screen->getPalette(0)[0x2f4] = 0x30; - colormap[0] = colorMap[0]; - colormap[1] = 0xfd; - memcpy(&colormap[2], &colorMap[2], 14); - uint8 seqTextColor0 = _seqTextColor[0]; - - _seqTextColor[0] = 0xfd; - _screen->setTextColorMap(colormap); - seq_resetAllTextEntries(); - seq_setTextEntry(strIndex, x, y, 0x80, 0x78); - seq_processText(); - _screen->copyPage(2, 0); - _screen->updateScreen(); - _screen->getPalette(0)[0x2f7] = _screen->getPalette(0)[textcolor * 3]; - _screen->getPalette(0)[0x2f8] = _screen->getPalette(0)[textcolor * 3 + 1]; - _screen->getPalette(0)[0x2f9] = _screen->getPalette(0)[textcolor * 3 + 2]; - _screen->fadePalette(_screen->getPalette(0), 0x18); - - _seqTextColor[0] = textcolor; - _screen->setTextColorMap(colorMap); - seq_resetAllTextEntries(); - seq_setTextEntry(strIndex, x, y, 0x80, 0x78); - seq_processText(); - _screen->copyPage(2, 0); - _screen->updateScreen(); - _screen->getPalette(0)[0x2f7] = _screen->getPalette(0)[0x2f8] = _screen->getPalette(0)[0x2f9] = 0; - _screen->fadePalette(_screen->getPalette(0), 1); - _screen->copyPage(2, 12); - seq_resetAllTextEntries(); - - _seqTextColor[0] = seqTextColor0; - - _screen->setFont(of); -} - -void KyraEngine_HoF::seq_playWsaSyncDialogue(uint16 strIndex, uint16 vocIndex, int textColor, int x, int y, int width, WSAMovie_v2 *wsa, int firstframe, int lastframe, int wsaXpos, int wsaYpos) { - int dur = int(strlen(_sequenceStrings[strIndex])) * (_flags.isTalkie ? 7 : 15); - if (textEnabled()) { - int entry = seq_setTextEntry(strIndex, x, y, dur, width); - _activeText[entry].textcolor = textColor; - } - _seqWsaChatTimeout = _system->getMillis() + dur * _tickLength; - int curframe = firstframe; - - if (vocIndex && speechEnabled()) { - while (_sound->voiceIsPlaying() && !skipFlag()) - delay(4); - seq_playTalkText(vocIndex); - } - - while (_system->getMillis() < _seqWsaChatTimeout && !(_abortIntroFlag || skipFlag())) { - if (lastframe < 0) { - int t = ABS(lastframe); - if (t < curframe) - curframe = t; - } - - if (ABS(lastframe) < curframe) - curframe = firstframe; - - _seqWsaChatFrameTimeout = _seqEndTime = _system->getMillis() + _seqFrameDelay * _tickLength; - if (wsa) - wsa->displayFrame(curframe % wsa->frames(), 2, wsaXpos, wsaYpos, 0, 0, 0); - - _screen->copyPage(2, 12); - - seq_processText(); - - uint32 tm = _system->getMillis(); - if (_seqWsaChatFrameTimeout > tm && _seqWsaChatTimeout > tm) - delay(MIN(_seqWsaChatFrameTimeout - tm, _seqWsaChatTimeout - tm)); - - if (speechEnabled() && !textEnabled() && !snd_voiceIsPlaying()) - break; - - _screen->copyPage(2, 0); - _screen->updateScreen(); - curframe++; - } - - if (_abortIntroFlag || skipFlag()) - _sound->voiceStop(); - - if (ABS(lastframe) < curframe) - curframe = ABS(lastframe); - - if (curframe == firstframe) - curframe++; - - _seqWsaCurrentFrame = curframe; -} - -void KyraEngine_HoF::seq_displayScrollText(uint8 *data, const ScreenDim *d, int tempPage1, int tempPage2, int speed, - int step, Screen::FontId fid1, Screen::FontId fid2, const uint8 *shapeData, const char *const *specialData) { - if (!data) - return; - - static const char mark[] = { 5, 13, 0 }; - - _screen->clearPage(tempPage1); - _screen->clearPage(tempPage2); - _screen->copyRegion(d->sx << 3, d->sy, d->sx << 3, d->sy, d->w << 3, d->h, 0, tempPage1); - - struct ScrollTextData { - int16 x; - int16 y; - uint8 *text; - byte unk1; - byte height; - byte adjust; - - ScrollTextData() { - x = 0; // 0 11 - y = 0; // 2 13 - text = 0; // 4 15 - unk1 = 0; // 8 19 - height = 0; // 9 20 - adjust = 0; // 10 21 - } - }; - - ScrollTextData *textData = new ScrollTextData[36]; - uint8 *ptr = data; - - bool loop = true; - int cnt = 0; - - while (loop) { - _seqSubFrameEndTimeInternal = _system->getMillis() + speed * _tickLength; - - while (cnt < 35 && *ptr) { - uint16 cH; - - if (cnt) - cH = textData[cnt].y + textData[cnt].height + (textData[cnt].height >> 3); - else - cH = d->h; - - char *str = (char *)ptr; - - ptr = (uint8 *)strpbrk(str, mark); - if (!ptr) - ptr = (uint8 *)strchr(str, 0); - - textData[cnt + 1].unk1 = *ptr; - *ptr = 0; - if (textData[cnt + 1].unk1) - ptr++; - - if (*str == 3 || *str == 4) - textData[cnt + 1].adjust = *str++; - else - textData[cnt + 1].adjust = 0; - - _screen->setFont(fid1); - - if (*str == 1) { - _screen->setFont(fid2); - str++; - } else if (*str == 2) { - str++; - } - - textData[cnt + 1].height = _screen->getFontHeight(); - - switch (textData[cnt + 1].adjust) { - case 3: - textData[cnt + 1].x = 157 - _screen->getTextWidth(str); - break; - case 4: - textData[cnt + 1].x = 161; - break; - default: - textData[cnt + 1].x = (((d->w << 3) - _screen->getTextWidth(str)) >> 1) + 1; - } - - if (textData[cnt].unk1 == 5) - cH -= (textData[cnt].height + (textData[cnt].height >> 3)); - - textData[cnt + 1].y = cH; - textData[cnt + 1].text = (uint8 *)str; - cnt++; - } - - _screen->copyRegion(d->sx << 3, d->sy, d->sx << 3, d->sy, d->w << 3, d->h, tempPage1, tempPage2); - - int cnt2 = 0; - bool palCycle = 0; - - while (cnt2 < cnt) { - const char *str = (const char *)textData[cnt2 + 1].text; - const char *str2 = str; - int16 cW = textData[cnt2 + 1].x - 10; - int16 cH = textData[cnt2 + 1].y; - int x = (d->sx << 3) + cW; - int y = d->sy + cH; - int col1 = 255; - - if (cH < d->h) { - _screen->setCurPage(tempPage2); - _screen->setFont(fid1); - if (textData[cnt2 + 1].height != _screen->getFontHeight()) - _screen->setFont(fid2); - - if (specialData) { - if (!strcmp(str, specialData[0])) { - col1 = 112; - char cChar[2] = " "; - while (*str2) { - cChar[0] = *str2; - _screen->printText(cChar, x, y, col1++, 0); - x += _screen->getCharWidth((uint8)*str2++); - } - palCycle = true; - } else if (!strcmp(str, specialData[1])) { - col1 = 133; - char cChar[2] = " "; - while (*str2) { - cChar[0] = *str2; - _screen->printText(cChar, x, y, col1--, 0); - x += _screen->getCharWidth((uint8)*str2++); - } - palCycle = true; - } else { - _screen->printText(str, x, y, col1, 0); - } - } else { - _screen->printText(str, x, y, col1, 0); - } - _screen->setCurPage(0); - } - - textData[cnt2 + 1].y -= step; - cnt2++; - } - - _screen->copyRegion(d->sx << 3, d->sy, d->sx << 3, d->sy, d->w << 3, d->h, tempPage2, 0); - _screen->updateScreen(); - - if (textData[1].y < -10) { - textData[1].text += strlen((char *)textData[1].text); - textData[1].text[0] = textData[1].unk1; - cnt--; - memcpy(&textData[1], &textData[2], cnt * sizeof(ScrollTextData)); - } - - if (palCycle) { - for (int col = 133; col > 112; col--) - _screen->getPalette(0).copy(_screen->getPalette(0), col - 1, 1, col); - _screen->getPalette(0).copy(_screen->getPalette(0), 133, 1, 112); - _screen->setScreenPalette(_screen->getPalette(0)); - } - - delayUntil(_seqSubFrameEndTimeInternal); - - if ((cnt < 36) && ((d->sy + d->h) > (textData[cnt].y + textData[cnt].height)) && !skipFlag()) { - resetSkipFlag(); - delay(_tickLength * 500); - cnt = 0; - } - - if (!cnt || skipFlag()) - loop = false; - } - - _sound->beginFadeOut(); - _screen->fadeToBlack(); - - _abortIntroFlag= false; - resetSkipFlag(); - - delete[] textData; -} - -void KyraEngine_HoF::seq_scrollPage(int bottom, int top) { - int dstY, dstH, srcH; - - static const ScreenDim d = { 0x00, 0x00, 0x28, 0x320, 0xFF, 0xFE, 0x00, 0x00 }; - - if (_seqScrollTextCounter - (top - 1) < 0) { - dstY = top - _seqScrollTextCounter; - dstH = _seqScrollTextCounter; - srcH = 0; - } else { - dstY = 0; - srcH = _seqScrollTextCounter - top; - dstH = (400 - srcH <= top) ? 400 - srcH : top; - } - - if (dstH > 0) { - if (_demoAnimData) { - for (int i = 0; i < 4; i++) { - const ItemAnimData_v1 *def = &_demoAnimData[i]; - ActiveItemAnim *a = &_activeItemAnim[i]; - - _screen->fillRect(12, def->y - 8, 28, def->y + 8, 0, 4); - _screen->drawShape(4, getShapePtr(def->itemIndex + def->frames[a->currentFrame]), 12, def->y - 8, 0, 0); - if (_seqFrameCounter % 2 == 0) - a->currentFrame = (a->currentFrame + 1) % 20; - } - } - _screen->copyRegionEx(4, 0, srcH, 2, 2, dstY + bottom, 320, dstH, &d); - } -} +const uint8 SeqPlayer_HOF::_textColorPresets[] = { 0x01, 0x01, 0x00, 0x3F, 0x3F, 0x3F }; void KyraEngine_HoF::seq_showStarcraftLogo() { WSAMovie_v2 *ci = new WSAMovie_v2(this); assert(ci); _screen->clearPage(2); _res->loadPakFile("INTROGEN.PAK"); - int endframe = ci->open("ci.wsa", 0, &_screen->getPalette(0)); + int endframe = ci->open("CI.WSA", 0, &_screen->getPalette(0)); _res->unloadPakFile("INTROGEN.PAK"); if (!ci->opened()) { delete ci; @@ -2736,20 +3343,28 @@ void KyraEngine_HoF::seq_showStarcraftLogo() { _screen->copyPage(2, 0); _screen->fadeFromBlack(); for (int i = 1; i < endframe; i++) { - _seqEndTime = _system->getMillis() + 50; + uint32 end = _system->getMillis() + 50; if (skipFlag()) break; ci->displayFrame(i, 2, 0, 0, 0, 0, 0); _screen->copyPage(2, 0); _screen->updateScreen(); - delay(_seqEndTime - _system->getMillis()); + uint32 cur = _system->getMillis(); + if (end > cur) + delay(end - cur); + else + updateInput(); } if (!skipFlag()) { - _seqEndTime = _system->getMillis() + 50; + uint32 end = _system->getMillis() + 50; ci->displayFrame(0, 2, 0, 0, 0, 0, 0); _screen->copyPage(2, 0); _screen->updateScreen(); - delay(_seqEndTime - _system->getMillis()); + uint32 cur = _system->getMillis(); + if (end > cur) + delay(end - cur); + else + updateInput(); } _screen->fadeToBlack(); _screen->showMouse(); @@ -2758,67 +3373,38 @@ void KyraEngine_HoF::seq_showStarcraftLogo() { delete ci; } -void KyraEngine_HoF::seq_init() { - _seqProcessedString = new char[200]; - _seqWsa = new WSAMovie_v2(this); - _activeWSA = new ActiveWSA[8]; - _activeText = new ActiveText[10]; - - _res->unloadAllPakFiles(); - _res->loadPakFile(StaticResource::staticDataFilename()); - _res->loadFileList(_sequencePakList, _sequencePakListSize); - - if (_flags.platform == Common::kPlatformPC98) - _sound->loadSoundFile("SOUND.DAT"); - - _screen->setFont(_flags.lang == Common::JA_JPN ? Screen::FID_SJIS_FNT : Screen::FID_GOLDFONT_FNT); - - if (_flags.gameID == GI_LOL) - return; - - if (_flags.isDemo && !_flags.isTalkie) { - _demoAnimData = _staticres->loadShapeAnimData_v1(k2SeqplayShapeAnimData, _itemAnimDefinitionSize); - uint8 *shp = _res->fileData("icons.shp", 0); - uint32 outsize = READ_LE_UINT16(shp + 4); - _animShapeFiledata = new uint8[outsize]; - Screen::decodeFrame4(shp + 10, _animShapeFiledata, outsize); - delete[] shp; - - for (int i = 0; i < 20; i++) - addShapeToPool(_screen->getPtrToShape(_animShapeFiledata, i), i); - } else { - const MainMenu::StaticData data = { - { _sequenceStrings[97], _sequenceStrings[96], _sequenceStrings[95], _sequenceStrings[98], 0 }, - { 0x01, 0x04, 0x0C, 0x04, 0x00, 0xd7, 0xd6 }, - { 0xd8, 0xda, 0xd9, 0xd8 }, - (_flags.lang == Common::JA_JPN) ? Screen::FID_SJIS_FNT : Screen::FID_8_FNT, 240 - }; - - _menu = new MainMenu(this); - _menu->init(data, MainMenu::Animation()); - } +int KyraEngine_HoF::seq_playIntro() { + bool startupSaveLoadable = saveFileLoadable(0); + return SeqPlayer_HOF(this, _screen, _system, startupSaveLoadable).play(kSequenceVirgin, startupSaveLoadable? kSequenceTitle : kSequenceNoLooping); } -void KyraEngine_HoF::seq_uninit() { - delete[] _seqProcessedString; - _seqProcessedString = NULL; - - delete[] _activeWSA; - _activeWSA = NULL; +int KyraEngine_HoF::seq_playOutro() { + return SeqPlayer_HOF(this, _screen, _system).play(kSequenceFunters, kSequenceFrash); +} - delete[] _activeText; - _activeText = NULL; +int KyraEngine_HoF::seq_playDemo() { + SeqPlayer_HOF(this, _screen, _system).play(kSequenceHoFDemoVirgin, kSequenceHoFDemoVirgin); + return 4; +} - delete _seqWsa; - _seqWsa = NULL; +void KyraEngine_HoF::seq_pausePlayer(bool toggle) { + SeqPlayer_HOF *activePlayer = SeqPlayer_HOF::instance(); + if (activePlayer) + activePlayer->pause(toggle); +} - delete[] _animShapeFiledata; - _animShapeFiledata = 0; +#ifdef ENABLE_LOL +int LoLEngine::playDemo() { + SeqPlayer_HOF(this, _screen, _system).play(kSequenceLoLDemoScene1, kSequenceLoLDemoScene1); + return -1; +} - delete _menu; - _menu = 0; - _screen->setFont(_flags.lang == Common::JA_JPN ? Screen::FID_SJIS_FNT : Screen::FID_8_FNT); +void LoLEngine::pauseDemoPlayer(bool toggle) { + SeqPlayer_HOF *activePlayer = SeqPlayer_HOF::instance(); + if (activePlayer) + activePlayer->pause(toggle); } +#endif // ENABLE_LOL #pragma mark - #pragma mark - Ingame sequences diff --git a/engines/kyra/sequences_hof.h b/engines/kyra/sequences_hof.h new file mode 100644 index 0000000000..2558a68a6a --- /dev/null +++ b/engines/kyra/sequences_hof.h @@ -0,0 +1,74 @@ +/* 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 KYRA_SEQUENCES_HOF_H +#define KYRA_SEQUENCES_HOF_H + +#include "kyra/kyra_v2.h" + +namespace Kyra { + +struct HoFSequence { + const char *wsaFile; + const char *cpsFile; + uint16 flags; + uint8 fadeInTransitionType; + uint8 fadeOutTransitionType; + int16 stringIndex1; + int16 stringIndex2; + uint16 startFrame; + uint16 numFrames; + uint16 duration; + uint16 xPos; + uint16 yPos; + uint16 timeout; +}; + +struct HoFNestedSequence { + const char *wsaFile; + const FrameControl *wsaControl; + uint16 flags; + uint16 startframe; + uint16 endFrame; + uint16 frameDelay; + uint16 x; + uint16 y; + uint16 fadeInTransitionType; + uint16 fadeOutTransitionType; +}; + +struct HoFSeqData { + const HoFSequence *seq; + int numSeq; + const HoFNestedSequence *nestedSeq; + int numNestedSeq; +}; + +struct HoFSeqItemAnimData { + int16 itemIndex; + uint16 y; + const uint16 *frames; +}; + +} // End of namespace Kyra + +#endif diff --git a/engines/kyra/sequences_lok.cpp b/engines/kyra/sequences_lok.cpp index e63d0a7d8f..994bd2ba9b 100644 --- a/engines/kyra/sequences_lok.cpp +++ b/engines/kyra/sequences_lok.cpp @@ -1207,7 +1207,7 @@ struct CreditsLine { void KyraEngine_LoK::seq_playCredits() { static const uint8 colorMap[] = { 0, 0, 0xC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - static const char stringTerms[] = { 0x5, 0xd, 0x0}; + static const char stringTerms[] = { 0x5, 0xD, 0x0}; typedef Common::List<CreditsLine> CreditsLineList; CreditsLineList lines; diff --git a/engines/kyra/sequences_lol.cpp b/engines/kyra/sequences_lol.cpp index a06f2077ba..c8f97eb770 100644 --- a/engines/kyra/sequences_lol.cpp +++ b/engines/kyra/sequences_lol.cpp @@ -36,10 +36,16 @@ namespace Kyra { #pragma mark - Intro int LoLEngine::processPrologue() { - setupPrologueData(true); - - if (!saveFileLoadable(0) || _flags.isDemo) - showIntro(); + // There are two non-interactive demos (one which plays the intro and another one) which plays a number of specific scenes. + // We try to identify the latter one by looking for a specific file. + _res->loadPakFile("GENERAL.PAK"); + if (_flags.isDemo && _res->exists("scene1.cps")) { + return playDemo(); + } else { + setupPrologueData(true); + if (!saveFileLoadable(0) || _flags.isDemo) + showIntro(); + } if (_flags.isDemo) { _screen->fadePalette(_screen->getPalette(1), 30, 0); @@ -66,7 +72,7 @@ int LoLEngine::processPrologue() { // Original version: (260|193) "V CD1.02 D" const int width = _screen->getTextWidth(versionString.c_str()); _screen->fprintString("%s", 320 - width, 193, 0x67, 0x00, 0x04, versionString.c_str()); - _screen->setFont(_flags.lang == Common::JA_JPN ? Screen::FID_SJIS_FNT : Screen::FID_9_FNT); + _screen->setFont((_flags.lang == Common::JA_JPN && _flags.use16ColorMode) ? Screen::FID_SJIS_FNT : Screen::FID_9_FNT); _screen->fadePalette(_screen->getPalette(0), 0x1E); _screen->updateScreen(); @@ -139,7 +145,12 @@ void LoLEngine::setupPrologueData(bool load) { static const char *const fileListFloppy[] = { "INTRO.PAK", "INTROVOC.PAK", 0 }; - const char *const *fileList = _flags.isTalkie ? fileListCD : fileListFloppy; + + static const char *const fileListTowns[] = { + "INTRO.PAK", "TINTROVO.PAK", 0 + }; + + const char *const *fileList = _flags.isTalkie ? fileListCD : (_flags.platform == Common::kPlatformFMTowns ? fileListTowns : fileListFloppy); char filename[32]; for (uint i = 0; fileList[i]; ++i) { @@ -176,7 +187,7 @@ void LoLEngine::setupPrologueData(bool load) { memset(_selectionAnimTimers, 0, sizeof(_selectionAnimTimers)); _screen->getPalette(1).clear(); - _sound->setSoundList(&_soundData[kMusicIntro]); + _sound->selectAudioResourceSet(kMusicIntro); // We have three sound.dat files, one for the intro, one for the // end sequence and one for ingame, each contained in a different @@ -197,7 +208,7 @@ void LoLEngine::setupPrologueData(bool load) { return; _eventList.clear(); - _sound->setSoundList(0); + _sound->selectAudioResourceSet(kMusicIntro); } } @@ -219,7 +230,7 @@ void LoLEngine::showIntro() { _screen->loadFont(Screen::FID_8_FNT, "NEW8P.FNT"); _screen->loadFont(Screen::FID_INTRO_FNT, "INTRO.FNT"); - _screen->setFont(_flags.lang == Common::JA_JPN ? Screen::FID_SJIS_FNT : Screen::FID_8_FNT); + _screen->setFont((_flags.lang == Common::JA_JPN && _flags.use16ColorMode) ? Screen::FID_SJIS_FNT : Screen::FID_8_FNT); _tim->resetFinishedFlag(); _tim->setLangData("LOLINTRO.DIP"); @@ -289,10 +300,10 @@ int LoLEngine::chooseCharacter() { _chargenWSA->displayFrame(0, 2, 113, 0, 0, 0, 0); - _screen->setFont(_flags.lang == Common::JA_JPN ? Screen::FID_SJIS_FNT : Screen::FID_9_FNT); + _screen->setFont((_flags.lang == Common::JA_JPN && _flags.use16ColorMode) ? Screen::FID_SJIS_FNT : Screen::FID_9_FNT); _screen->_curPage = 2; - if (_flags.platform == Common::kPlatformPC98) { + if (_flags.platform == Common::kPlatformPC98 && _flags.use16ColorMode) { _screen->fillRect(17, 29, 94, 97, 17); _screen->fillRect(68, 167, 310, 199, 17); _screen->drawClippedLine(68, 166, 311, 166, 238); @@ -304,7 +315,7 @@ int LoLEngine::chooseCharacter() { _screen->_curPage = 2; for (int i = 0; i < 4; ++i) { - _screen->printText((const char *)_charNamesPC98[i], _charPosXPC98[i], 168, 0xC1, 0x00); + _screen->printText(_charNamesJapanese[i], _charPosXPC98[i], 168, 0xC1, 0x00); Screen::FontId old = _screen->setFont(Screen::FID_SJIS_FNT); for (int j = 0; j < 3; ++j) { @@ -318,7 +329,7 @@ int LoLEngine::chooseCharacter() { _screen->printText(_tim->getCTableEntry(53), 72, 184, 0x81, 0x00); _screen->printText(_tim->getCTableEntry(55), 72, 192, 0x81, 0x00); } else { - const char *const *previewNames = (_flags.lang == Common::RU_RUS && !_flags.isTalkie) ? _charPreviewNamesRussianFloppy : _charPreviewNamesDefault; + const char *const *previewNames = (_flags.lang == Common::RU_RUS && !_flags.isTalkie) ? _charPreviewNamesRussianFloppy : (_flags.lang == Common::JA_JPN ? _charNamesJapanese : _charPreviewNamesDefault); for (int i = 0; i < 4; ++i) { _screen->fprintStringIntro("%s", _charPreviews[i].x + 16, _charPreviews[i].y + 36, 0xC0, 0x00, 0x9C, 0x120, previewNames[i]); _screen->fprintStringIntro("%d", _charPreviews[i].x + 21, _charPreviews[i].y + 48, 0x98, 0x00, 0x9C, 0x220, _charPreviews[i].attrib[0]); @@ -710,7 +721,7 @@ void LoLEngine::showStarcraftLogo() { _screen->fadeFromBlack(); int inputFlag = 0; for (int i = 0; i < endframe; i++) { - inputFlag = checkInput(0) & 0xff; + inputFlag = checkInput(0) & 0xFF; if (shouldQuit() || inputFlag) break; ci->displayFrame(i, 2, 32, 80, 0, 0, 0); @@ -722,7 +733,7 @@ void LoLEngine::showStarcraftLogo() { if (!(shouldQuit() || inputFlag)) { _sound->voicePlay("star2", &_speechHandle); while (_sound->voiceIsPlaying(&_speechHandle) && !(shouldQuit() || inputFlag)) { - inputFlag = checkInput(0) & 0xff; + inputFlag = checkInput(0) & 0xFF; delay(_tickLength); } } @@ -1009,7 +1020,11 @@ void LoLEngine::setupEpilogueData(bool load) { "GENERAL.PAK", "INTRO.PAK", "FINALE1.PAK", "FINALE2.PAK", 0 }; - const char *const *fileList = _flags.isTalkie ? fileListCD : fileListFloppy; + static const char *const fileListTowns[] = { + "GENERAL.PAK", "INTRO.PAK", "FINALE1.PAK", "TFINALE2.PAK", 0 + }; + + const char *const *fileList = _flags.isTalkie ? fileListCD : (_flags.platform == Common::kPlatformFMTowns ? fileListTowns : fileListFloppy); assert(fileList); char filename[32]; @@ -1035,7 +1050,7 @@ void LoLEngine::setupEpilogueData(bool load) { _screen->clearPage(3); if (load) { - _sound->setSoundList(&_soundData[kMusicFinale]); + _sound->selectAudioResourceSet(kMusicFinale); // We have three sound.dat files, one for the intro, one for the // end sequence and one for ingame, each contained in a different @@ -1051,7 +1066,7 @@ void LoLEngine::setupEpilogueData(bool load) { return; _eventList.clear(); - _sound->setSoundList(0); + _sound->selectAudioResourceSet(kMusicIntro); } } diff --git a/engines/kyra/sound.cpp b/engines/kyra/sound.cpp index 73c20ee6df..32d175bdb0 100644 --- a/engines/kyra/sound.cpp +++ b/engines/kyra/sound.cpp @@ -37,7 +37,7 @@ namespace Kyra { Sound::Sound(KyraEngine_v1 *vm, Audio::Mixer *mixer) : _vm(vm), _mixer(mixer), _soundChannels(), _musicEnabled(1), - _sfxEnabled(true), _soundDataList(0) { + _sfxEnabled(true) { } Sound::~Sound() { @@ -47,14 +47,6 @@ Sound::kType Sound::getSfxType() const { return getMusicType(); } -void Sound::setSoundList(const AudioDataStruct *list) { - _soundDataList = list; -} - -bool Sound::hasSoundFile(uint file) const { - return (fileListEntry(file) != 0); -} - bool Sound::isPlaying() const { return false; } @@ -73,7 +65,7 @@ bool Sound::isVoicePresent(const char *file) const { return false; } -int32 Sound::voicePlay(const char *file, Audio::SoundHandle *handle, uint8 volume, bool isSfx) { +int32 Sound::voicePlay(const char *file, Audio::SoundHandle *handle, uint8 volume, uint8 priority, bool isSfx) { Audio::SeekableAudioStream *audioStream = getVoiceStream(file); if (!audioStream) { @@ -81,7 +73,7 @@ int32 Sound::voicePlay(const char *file, Audio::SoundHandle *handle, uint8 volum } int playTime = audioStream->getLength().msecs(); - playVoiceStream(audioStream, handle, volume, isSfx); + playVoiceStream(audioStream, handle, volume, priority, isSfx); return playTime; } @@ -109,12 +101,20 @@ Audio::SeekableAudioStream *Sound::getVoiceStream(const char *file) const { } } -bool Sound::playVoiceStream(Audio::AudioStream *stream, Audio::SoundHandle *handle, uint8 volume, bool isSfx) { +bool Sound::playVoiceStream(Audio::AudioStream *stream, Audio::SoundHandle *handle, uint8 volume, uint8 priority, bool isSfx) { int h = 0; - while (h < kNumChannelHandles && _mixer->isSoundHandleActive(_soundChannels[h])) + while (h < kNumChannelHandles && _mixer->isSoundHandleActive(_soundChannels[h].handle)) ++h; if (h >= kNumChannelHandles) { + h = 0; + while (h < kNumChannelHandles && _soundChannels[h].priority > priority) + ++h; + if (h < kNumChannelHandles) + voiceStop(&_soundChannels[h].handle); + } + + if (h >= kNumChannelHandles) { // When we run out of handles we need to destroy the stream object, // this is to avoid memory leaks in some scenes where too many sfx // are started. @@ -123,9 +123,10 @@ bool Sound::playVoiceStream(Audio::AudioStream *stream, Audio::SoundHandle *hand return false; } - _mixer->playStream(isSfx ? Audio::Mixer::kSFXSoundType : Audio::Mixer::kSpeechSoundType, &_soundChannels[h], stream, -1, volume); + _mixer->playStream(isSfx ? Audio::Mixer::kSFXSoundType : Audio::Mixer::kSpeechSoundType, &_soundChannels[h].handle, stream, -1, volume); + _soundChannels[h].priority = priority; if (handle) - *handle = _soundChannels[h]; + *handle = _soundChannels[h].handle; return true; } @@ -133,8 +134,8 @@ bool Sound::playVoiceStream(Audio::AudioStream *stream, Audio::SoundHandle *hand void Sound::voiceStop(const Audio::SoundHandle *handle) { if (!handle) { for (int h = 0; h < kNumChannelHandles; ++h) { - if (_mixer->isSoundHandleActive(_soundChannels[h])) - _mixer->stopHandle(_soundChannels[h]); + if (_mixer->isSoundHandleActive(_soundChannels[h].handle)) + _mixer->stopHandle(_soundChannels[h].handle); } } else { _mixer->stopHandle(*handle); @@ -144,7 +145,7 @@ void Sound::voiceStop(const Audio::SoundHandle *handle) { bool Sound::voiceIsPlaying(const Audio::SoundHandle *handle) const { if (!handle) { for (int h = 0; h < kNumChannelHandles; ++h) { - if (_mixer->isSoundHandleActive(_soundChannels[h])) + if (_mixer->isSoundHandleActive(_soundChannels[h].handle)) return true; } } else { @@ -156,7 +157,7 @@ bool Sound::voiceIsPlaying(const Audio::SoundHandle *handle) const { bool Sound::allVoiceChannelsPlaying() const { for (int i = 0; i < kNumChannelHandles; ++i) - if (!_mixer->isSoundHandleActive(_soundChannels[i])) + if (!_mixer->isSoundHandleActive(_soundChannels[i].handle)) return false; return true; } @@ -194,9 +195,14 @@ void MixedSoundDriver::updateVolumeSettings() { _sfx->updateVolumeSettings(); } -void MixedSoundDriver::setSoundList(const AudioDataStruct *list) { - _music->setSoundList(list); - _sfx->setSoundList(list); +void MixedSoundDriver::initAudioResourceInfo(int set, void *info) { + _music->initAudioResourceInfo(set, info); + _sfx->initAudioResourceInfo(set, info); +} + +void MixedSoundDriver::selectAudioResourceSet(int set) { + _music->selectAudioResourceSet(set); + _sfx->selectAudioResourceSet(set); } bool MixedSoundDriver::hasSoundFile(uint file) const { diff --git a/engines/kyra/sound.h b/engines/kyra/sound.h index 63cec48d00..2f5a0b6121 100644 --- a/engines/kyra/sound.h +++ b/engines/kyra/sound.h @@ -37,6 +37,35 @@ class SeekableAudioStream; namespace Kyra { +// Helper structs to format the data passed to the various initAudioResourceInfo() implementations +struct SoundResourceInfo_PC { + SoundResourceInfo_PC(const char *const *files, int numFiles) : fileList(files), fileListSize(numFiles) {} + const char *const *fileList; + uint fileListSize; +}; + +struct SoundResourceInfo_Towns { + SoundResourceInfo_Towns(const char *const *files, int numFiles, const int32 *cdaTbl, int cdaTblSize) : fileList(files), fileListSize(numFiles), cdaTable(cdaTbl), cdaTableSize(cdaTblSize) {} + const char *const *fileList; + uint fileListSize; + const int32 *cdaTable; + uint cdaTableSize; +}; + +struct SoundResourceInfo_PC98 { + SoundResourceInfo_PC98(const char *fileNamePattern) : pattern(fileNamePattern) {} + const char *pattern; +}; + +struct SoundResourceInfo_TownsPC98V2 { + SoundResourceInfo_TownsPC98V2(const char *const *files, int numFiles, const char *fileNamePattern, const uint16 *cdaTbl, int cdaTblSize) : fileList(files), fileListSize(numFiles), pattern(fileNamePattern), cdaTable(cdaTbl), cdaTableSize(cdaTblSize) {} + const char *const *fileList; + uint fileListSize; + const char *pattern; + const uint16 *cdaTable; + uint cdaTableSize; +}; + /** * Analog audio output device API for Kyrandia games. * It contains functionality to play music tracks, @@ -78,12 +107,20 @@ public: virtual void updateVolumeSettings() {} /** - * Sets the soundfiles the output device will use - * when playing a track and/or sound effect. + * Assigns static resource data with information on how to load + * audio resources to * - * @param list soundfile list + * @param set value defined in AudioResourceSet enum + * info various types of resource info data (file list, file name pattern, struct, etc. - depending on the inheriting driver type) */ - virtual void setSoundList(const AudioDataStruct *list); + virtual void initAudioResourceInfo(int set, void *info) = 0; + + /** + * Select audio resource set. + * + * @param set value defined in AudioResourceSet enum + */ + virtual void selectAudioResourceSet(int set) = 0; /** * Checks if a given sound file is present. @@ -91,7 +128,7 @@ public: * @param track track number * @return true if available, false otherwise */ - virtual bool hasSoundFile(uint file) const; + virtual bool hasSoundFile(uint file) const = 0; /** * Load a specifc sound file for use of @@ -128,7 +165,7 @@ public: * * @param track sound effect id */ - virtual void playSoundEffect(uint8 track, uint8 volume = 0xff) = 0; + virtual void playSoundEffect(uint8 track, uint8 volume = 0xFF) = 0; /** * Stop playback of all sfx tracks. @@ -184,11 +221,11 @@ public: * @param handle store a copy of the sound handle * @return playtime of the voice file (-1 marks unknown playtime) */ - virtual int32 voicePlay(const char *file, Audio::SoundHandle *handle = 0, uint8 volume = 255, bool isSfx = false); + virtual int32 voicePlay(const char *file, Audio::SoundHandle *handle = 0, uint8 volume = 255, uint8 priority = 255, bool isSfx = false); Audio::SeekableAudioStream *getVoiceStream(const char *file) const; - bool playVoiceStream(Audio::AudioStream *stream, Audio::SoundHandle *handle = 0, uint8 volume = 255, bool isSfx = false); + bool playVoiceStream(Audio::AudioStream *stream, Audio::SoundHandle *handle = 0, uint8 volume = 255, uint8 priority = 255, bool isSfx = false); /** * Checks if a voice is being played. @@ -228,17 +265,17 @@ public: */ virtual void resetTrigger() {} protected: - const char *fileListEntry(int file) const { return (_soundDataList != 0 && file >= 0 && file < _soundDataList->fileListLen) ? _soundDataList->fileList[file] : ""; } - int fileListLen() const { return _soundDataList->fileListLen; } - const void *cdaData() const { return _soundDataList != 0 ? _soundDataList->cdaTracks : 0; } - int cdaTrackNum() const { return _soundDataList != 0 ? _soundDataList->cdaNumTracks : 0; } - int extraOffset() const { return _soundDataList != 0 ? _soundDataList->extraOffset : 0; } - enum { kNumChannelHandles = 4 }; - Audio::SoundHandle _soundChannels[kNumChannelHandles]; + struct SoundChannel { + SoundChannel() : handle(), priority(0) {} + Audio::SoundHandle handle; + int priority; + }; + + SoundChannel _soundChannels[kNumChannelHandles]; int _musicEnabled; bool _sfxEnabled; @@ -247,8 +284,6 @@ protected: Audio::Mixer *_mixer; private: - const AudioDataStruct *_soundDataList; - struct SpeechCodecs { const char *fileext; Audio::SeekableAudioStream *(*streamFunc)( @@ -272,7 +307,8 @@ public: virtual void updateVolumeSettings(); - virtual void setSoundList(const AudioDataStruct *list); + virtual void initAudioResourceInfo(int set, void *info); + virtual void selectAudioResourceSet(int set); virtual bool hasSoundFile(uint file) const; virtual void loadSoundFile(uint file); virtual void loadSoundFile(Common::String file); @@ -283,7 +319,7 @@ public: virtual void haltTrack(); virtual bool isPlaying() const; - virtual void playSoundEffect(uint8 track, uint8 volume = 0xff); + virtual void playSoundEffect(uint8 track, uint8 volume = 0xFF); virtual void stopAllSoundEffects(); @@ -293,88 +329,6 @@ private: Sound *_music, *_sfx; }; -// Digital Audio -class AUDStream; -class KyraAudioStream; -class KyraEngine_MR; - -/** - * Digital audio output device. - * - * This is just used for Kyrandia 3. - */ -class SoundDigital { -public: - SoundDigital(KyraEngine_MR *vm, Audio::Mixer *mixer); - ~SoundDigital(); - - bool init() { return true; } - - /** - * Plays a sound. - * - * @param filename file to be played - * @param priority priority of the sound - * @param type type - * @param volume channel volume - * @param loop true if the sound should loop (endlessly) - * @param channel tell the sound player to use a specific channel for playback - * - * @return channel playing the sound - */ - int playSound(const char *filename, uint8 priority, Audio::Mixer::SoundType type, int volume = 255, bool loop = false, int channel = -1); - - /** - * Checks if a given channel is playing a sound. - * - * @param channel channel number to check - * @return true if playing, else false - */ - bool isPlaying(int channel); - - /** - * Stop the playback of a sound in the given - * channel. - * - * @param channel channel number - */ - void stopSound(int channel); - - /** - * Stops playback of all sounds. - */ - void stopAllSounds(); - - /** - * Makes the sound in a given channel - * fading out. - * - * @param channel channel number - * @param ticks fadeout time - */ - void beginFadeOut(int channel, int ticks); -private: - KyraEngine_MR *_vm; - Audio::Mixer *_mixer; - - struct Sound { - Audio::SoundHandle handle; - - char filename[16]; - uint8 priority; - KyraAudioStream *stream; - } _sounds[4]; - - struct AudioCodecs { - const char *fileext; - Audio::SeekableAudioStream *(*streamFunc)( - Common::SeekableReadStream *stream, - DisposeAfterUse::Flag disposeAfterUse); - }; - - static const AudioCodecs _supportedCodecs[]; -}; - } // End of namespace Kyra #endif diff --git a/engines/kyra/sound_adlib.cpp b/engines/kyra/sound_adlib.cpp index 668e662413..1d665709e5 100644 --- a/engines/kyra/sound_adlib.cpp +++ b/engines/kyra/sound_adlib.cpp @@ -683,14 +683,14 @@ void AdLibDriver::adjustSfxData(uint8 *ptr, int volume) { _sfxVelocity = ptr[3]; // Adjust the values. - if (volume != 0xff) { + if (volume != 0xFF) { if (_version >= 3) { int newVal = ((((ptr[3]) + 63) * volume) >> 8) & 0xFF; ptr[3] = -newVal + 63; ptr[1] = ((ptr[1] * volume) >> 8) & 0xFF; } else { - int newVal = ((_sfxVelocity << 2) ^ 0xff) * volume; - ptr[3] = (newVal >> 10) ^ 0x3f; + int newVal = ((_sfxVelocity << 2) ^ 0xFF) * volume; + ptr[3] = (newVal >> 10) ^ 0x3F; ptr[1] = newVal >> 11; } } @@ -2292,6 +2292,8 @@ SoundAdLibPC::SoundAdLibPC(KyraEngine_v1 *vm, Audio::Mixer *mixer) _numSoundTriggers = 0; _sfxPlayingSound = -1; _soundFileLoaded.clear(); + _currentResourceSet = 0; + memset(&_resInfo, 0, sizeof(_resInfo)); switch (vm->game()) { case GI_LOL: @@ -2322,6 +2324,8 @@ SoundAdLibPC::SoundAdLibPC(KyraEngine_v1 *vm, Audio::Mixer *mixer) SoundAdLibPC::~SoundAdLibPC() { delete _driver; delete[] _soundDataPtr; + for (int i = 0; i < 3; i++) + initAudioResourceInfo(i, 0); } bool SoundAdLibPC::init() { @@ -2371,7 +2375,7 @@ void SoundAdLibPC::playTrack(uint8 track) { _driver->setSyncJumpMask(0x000F); else _driver->setSyncJumpMask(0); - play(track, 0xff); + play(track, 0xFF); } } @@ -2405,7 +2409,7 @@ void SoundAdLibPC::play(uint8 track, uint8 volume) { } void SoundAdLibPC::beginFadeOut() { - play(_version > 2 ? 1 : 15, 0xff); + play(_version > 2 ? 1 : 15, 0xFF); } int SoundAdLibPC::checkTrigger() { @@ -2416,8 +2420,29 @@ void SoundAdLibPC::resetTrigger() { _driver->resetSoundTrigger(); } +void SoundAdLibPC::initAudioResourceInfo(int set, void *info) { + if (set >= kMusicIntro && set <= kMusicFinale) { + delete _resInfo[set]; + _resInfo[set] = info ? new SoundResourceInfo_PC(*(SoundResourceInfo_PC*)info) : 0; + } +} + +void SoundAdLibPC::selectAudioResourceSet(int set) { + if (set >= kMusicIntro && set <= kMusicFinale) { + if (_resInfo[set]) + _currentResourceSet = set; + } +} + +bool SoundAdLibPC::hasSoundFile(uint file) const { + if (file < res()->fileListSize) + return (res()->fileList[file] != 0); + return false; +} + void SoundAdLibPC::loadSoundFile(uint file) { - internalLoadFile(fileListEntry(file)); + if (file < res()->fileListSize) + internalLoadFile(res()->fileList[file]); } void SoundAdLibPC::loadSoundFile(Common::String file) { diff --git a/engines/kyra/sound_adlib.h b/engines/kyra/sound_adlib.h index 8492f3b99a..f8486499ab 100644 --- a/engines/kyra/sound_adlib.h +++ b/engines/kyra/sound_adlib.h @@ -69,6 +69,9 @@ public: virtual void updateVolumeSettings(); + virtual void initAudioResourceInfo(int set, void *info); + virtual void selectAudioResourceSet(int set); + virtual bool hasSoundFile(uint file) const; virtual void loadSoundFile(uint file); virtual void loadSoundFile(Common::String file); @@ -76,7 +79,7 @@ public: virtual void haltTrack(); virtual bool isPlaying() const; - virtual void playSoundEffect(uint8 track, uint8 volume = 0xff); + virtual void playSoundEffect(uint8 track, uint8 volume = 0xFF); virtual void beginFadeOut(); @@ -87,6 +90,10 @@ private: void play(uint8 track, uint8 volume); + const SoundResourceInfo_PC *res() const {return _resInfo[_currentResourceSet]; } + SoundResourceInfo_PC *_resInfo[3]; + int _currentResourceSet; + AdLibDriver *_driver; int _version; diff --git a/engines/kyra/sound_amiga.cpp b/engines/kyra/sound_amiga.cpp index ec2748dd38..7292541594 100644 --- a/engines/kyra/sound_amiga.cpp +++ b/engines/kyra/sound_amiga.cpp @@ -53,10 +53,26 @@ bool SoundAmiga::init() { return _driver != 0 && _tableSfxIntro && _tableSfxGame; } +void SoundAmiga::initAudioResourceInfo(int set, void *info) { + // See comment below +} + +void SoundAmiga::selectAudioResourceSet(int set) { + // It seems that loadSoundFile() is doing what would normally be done in here. + // As long as this driver is only required for one single target (Kyra 1 Amiga) + // this doesn't matter much. +} + +bool SoundAmiga::hasSoundFile(uint file) const { + if (file < 3) + return true; + return false; +} + void SoundAmiga::loadSoundFile(uint file) { debugC(5, kDebugLevelSound, "SoundAmiga::loadSoundFile(%d)", file); - static const char * const tableFilenames[3][2] = { + static const char *const tableFilenames[3][2] = { { "introscr.mx", "introinst.mx" }, { "kyramusic.mx", 0 }, { "finalescr.mx", "introinst.mx" } diff --git a/engines/kyra/sound_digital.cpp b/engines/kyra/sound_digital.cpp index fe0f1fb9bc..518805c43e 100644 --- a/engines/kyra/sound_digital.cpp +++ b/engines/kyra/sound_digital.cpp @@ -20,7 +20,7 @@ * */ -#include "kyra/sound.h" +#include "kyra/sound_digital.h" #include "kyra/resource.h" #include "kyra/kyra_mr.h" @@ -271,8 +271,8 @@ int AUDStream::readChunk(int16 *buffer, const int maxSamples) { while (outSize > 0) { input = _inBuffer[i++] << 2; - code = (input >> 8) & 0xff; - count = (input & 0xff) >> 2; + code = (input >> 8) & 0xFF; + count = (input & 0xFF) >> 2; switch (code) { case 2: @@ -294,7 +294,7 @@ int AUDStream::readChunk(int16 *buffer, const int maxSamples) { for (; count >= 0; count--) { code = _inBuffer[i++]; - curSample += WSTable4Bit[code & 0x0f]; + curSample += WSTable4Bit[code & 0x0F]; curSample = clip8BitSample(curSample); _outBuffer[j++] = curSample; diff --git a/engines/kyra/sound_digital.h b/engines/kyra/sound_digital.h new file mode 100644 index 0000000000..271dde6a21 --- /dev/null +++ b/engines/kyra/sound_digital.h @@ -0,0 +1,119 @@ +/* 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 KYRA_SOUND_DIGITAL_H +#define KYRA_SOUND_DIGITAL_H + +#include "audio/mixer.h" + +namespace Common { +class SeekableReadStream; +} // End of namespace Common + +namespace Audio { +class SeekableAudioStream; +} // End of namespace Audio + +namespace Kyra { + +// Digital Audio +class KyraAudioStream; +class KyraEngine_MR; + +/** + * Digital audio output device. + * + * This is just used for Kyrandia 3. + */ +class SoundDigital { +public: + SoundDigital(KyraEngine_MR *vm, Audio::Mixer *mixer); + ~SoundDigital(); + + /** + * Plays a sound. + * + * @param filename file to be played + * @param priority priority of the sound + * @param type type + * @param volume channel volume + * @param loop true if the sound should loop (endlessly) + * @param channel tell the sound player to use a specific channel for playback + * + * @return channel playing the sound + */ + int playSound(const char *filename, uint8 priority, Audio::Mixer::SoundType type, int volume = 255, bool loop = false, int channel = -1); + + /** + * Checks if a given channel is playing a sound. + * + * @param channel channel number to check + * @return true if playing, else false + */ + bool isPlaying(int channel); + + /** + * Stop the playback of a sound in the given + * channel. + * + * @param channel channel number + */ + void stopSound(int channel); + + /** + * Stops playback of all sounds. + */ + void stopAllSounds(); + + /** + * Makes the sound in a given channel + * fading out. + * + * @param channel channel number + * @param ticks fadeout time + */ + void beginFadeOut(int channel, int ticks); +private: + KyraEngine_MR *_vm; + Audio::Mixer *_mixer; + + struct Sound { + Audio::SoundHandle handle; + + char filename[16]; + uint8 priority; + KyraAudioStream *stream; + } _sounds[4]; + + struct AudioCodecs { + const char *fileext; + Audio::SeekableAudioStream *(*streamFunc)( + Common::SeekableReadStream *stream, + DisposeAfterUse::Flag disposeAfterUse); + }; + + static const AudioCodecs _supportedCodecs[]; +}; + +} // End of namespace Kyra + +#endif diff --git a/engines/kyra/sound_intern.h b/engines/kyra/sound_intern.h index 827a487685..007ca3d3f5 100644 --- a/engines/kyra/sound_intern.h +++ b/engines/kyra/sound_intern.h @@ -23,6 +23,8 @@ #ifndef KYRA_SOUND_INTERN_H #define KYRA_SOUND_INTERN_H + + #include "kyra/sound.h" #include "kyra/sound_adlib.h" @@ -40,6 +42,7 @@ class MaxTrax; } // End of namespace Audio namespace Kyra { + class MidiOutput; /** @@ -51,28 +54,31 @@ class MidiOutput; class SoundMidiPC : public Sound { public: SoundMidiPC(KyraEngine_v1 *vm, Audio::Mixer *mixer, MidiDriver *driver, kType type); - ~SoundMidiPC(); + virtual ~SoundMidiPC(); - kType getMusicType() const { return _type; } + virtual kType getMusicType() const { return _type; } - bool init(); + virtual bool init(); - void updateVolumeSettings(); + virtual void updateVolumeSettings(); - void loadSoundFile(uint file); - void loadSoundFile(Common::String file); - void loadSfxFile(Common::String file); + virtual void initAudioResourceInfo(int set, void *info); + virtual void selectAudioResourceSet(int set); + virtual bool hasSoundFile(uint file) const; + virtual void loadSoundFile(uint file); + virtual void loadSoundFile(Common::String file); + virtual void loadSfxFile(Common::String file); - void playTrack(uint8 track); - void haltTrack(); - bool isPlaying() const; + virtual void playTrack(uint8 track); + virtual void haltTrack(); + virtual bool isPlaying() const; - void playSoundEffect(uint8 track, uint8 volume = 0xff); - void stopAllSoundEffects(); + virtual void playSoundEffect(uint8 track, uint8 volume = 0xFF); + virtual void stopAllSoundEffects(); - void beginFadeOut(); + virtual void beginFadeOut(); - void pause(bool paused); + virtual void pause(bool paused); private: static void onTimer(void *data); @@ -89,6 +95,10 @@ private: MidiParser *_music; MidiParser *_sfx[3]; + const SoundResourceInfo_PC *res() const {return _resInfo[_currentResourceSet]; } + SoundResourceInfo_PC *_resInfo[3]; + int _currentResourceSet; + // misc kType _type; Common::String getFileName(const Common::String &str); @@ -103,25 +113,28 @@ private: class SoundTowns : public Sound { public: SoundTowns(KyraEngine_v1 *vm, Audio::Mixer *mixer); - ~SoundTowns(); + virtual ~SoundTowns(); - kType getMusicType() const { return kTowns; } + virtual kType getMusicType() const { return kTowns; } - bool init(); - void process(); + virtual bool init(); + virtual void process(); - void loadSoundFile(uint file); - void loadSoundFile(Common::String) {} + virtual void initAudioResourceInfo(int set, void *info); + virtual void selectAudioResourceSet(int set); + virtual bool hasSoundFile(uint file) const; + virtual void loadSoundFile(uint file); + virtual void loadSoundFile(Common::String) {} - void playTrack(uint8 track); - void haltTrack(); + virtual void playTrack(uint8 track); + virtual void haltTrack(); - void playSoundEffect(uint8 track, uint8 volume = 0xff); - void stopAllSoundEffects(); + virtual void playSoundEffect(uint8 track, uint8 volume = 0xFF); + virtual void stopAllSoundEffects(); - void beginFadeOut(); + virtual void beginFadeOut(); - void updateVolumeSettings(); + virtual void updateVolumeSettings(); private: bool loadInstruments(); @@ -142,6 +155,10 @@ private: bool _cdaPlaying; + const SoundResourceInfo_Towns *res() const {return _resInfo[_currentResourceSet]; } + SoundResourceInfo_Towns *_resInfo[3]; + int _currentResourceSet; + const uint8 *_musicFadeTable; const uint8 *_sfxBTTable; const uint8 *_sfxWDTable; @@ -150,55 +167,64 @@ private: class SoundPC98 : public Sound { public: SoundPC98(KyraEngine_v1 *vm, Audio::Mixer *mixer); - ~SoundPC98(); + virtual ~SoundPC98(); virtual kType getMusicType() const { return kPC98; } - bool init(); + virtual bool init(); - void process() {} - void loadSoundFile(uint file); - void loadSoundFile(Common::String file); + virtual void initAudioResourceInfo(int set, void *info); + virtual void selectAudioResourceSet(int set); + virtual bool hasSoundFile(uint file) const; + virtual void loadSoundFile(uint file); + virtual void loadSoundFile(Common::String file); - void playTrack(uint8 track); - void haltTrack(); - void beginFadeOut(); + virtual void playTrack(uint8 track); + virtual void haltTrack(); + virtual void beginFadeOut(); - int32 voicePlay(const char *file, Audio::SoundHandle *handle, uint8 volume, bool isSfx) { return -1; } - void playSoundEffect(uint8 track, uint8 volume = 0xff); + virtual int32 voicePlay(const char *file, Audio::SoundHandle *handle, uint8 volume, uint8 priority, bool isSfx) override { return -1; } + virtual void playSoundEffect(uint8 track, uint8 volume = 0xFF); - void updateVolumeSettings(); + virtual void updateVolumeSettings(); -protected: +private: int _lastTrack; uint8 *_musicTrackData; uint8 *_sfxTrackData; TownsPC98_AudioDriver *_driver; + + const char *resPattern() {return _resInfo[_currentResourceSet]->c_str(); } + Common::String *_resInfo[3]; + int _currentResourceSet; }; class SoundTownsPC98_v2 : public Sound { public: SoundTownsPC98_v2(KyraEngine_v1 *vm, Audio::Mixer *mixer); - ~SoundTownsPC98_v2(); + virtual ~SoundTownsPC98_v2(); - kType getMusicType() const { return _vm->gameFlags().platform == Common::kPlatformFMTowns ? kTowns : kPC98; } + virtual kType getMusicType() const { return _vm->gameFlags().platform == Common::kPlatformFMTowns ? kTowns : kPC98; } - bool init(); - void process(); + virtual bool init(); + virtual void process(); - void loadSoundFile(uint file) {} - void loadSoundFile(Common::String file); + virtual void initAudioResourceInfo(int set, void *info); + virtual void selectAudioResourceSet(int set); + virtual bool hasSoundFile(uint file) const; + virtual void loadSoundFile(uint file) {} + virtual void loadSoundFile(Common::String file); - void playTrack(uint8 track); - void haltTrack(); - void beginFadeOut(); + virtual void playTrack(uint8 track); + virtual void haltTrack(); + virtual void beginFadeOut(); - int32 voicePlay(const char *file, Audio::SoundHandle *handle, uint8 volume, bool isSfx); - void playSoundEffect(uint8 track, uint8 volume = 0xff); + virtual int32 voicePlay(const char *file, Audio::SoundHandle *handle, uint8 volume = 255, uint8 priority = 255, bool isSfx = true) override; + virtual void playSoundEffect(uint8 track, uint8 volume = 0xFF); - void updateVolumeSettings(); + virtual void updateVolumeSettings(); -protected: +private: Audio::AudioStream *_currentSFX; int _lastTrack; bool _useFmSfx; @@ -206,6 +232,10 @@ protected: uint8 *_musicTrackData; uint8 *_sfxTrackData; TownsPC98_AudioDriver *_driver; + + const SoundResourceInfo_TownsPC98V2 *res() const {return _resInfo[_currentResourceSet]; } + SoundResourceInfo_TownsPC98V2 *_resInfo[3]; + int _currentResourceSet; }; // PC Speaker MIDI driver @@ -288,22 +318,24 @@ struct AmigaSfxTable { class SoundAmiga : public Sound { public: SoundAmiga(KyraEngine_v1 *vm, Audio::Mixer *mixer); - ~SoundAmiga(); + virtual ~SoundAmiga(); virtual kType getMusicType() const { return kAmiga; } //FIXME - bool init(); + virtual bool init(); - void process() {} - void loadSoundFile(uint file); - void loadSoundFile(Common::String) {} + virtual void initAudioResourceInfo(int set, void *info); + virtual void selectAudioResourceSet(int set); + virtual bool hasSoundFile(uint file) const; + virtual void loadSoundFile(uint file); + virtual void loadSoundFile(Common::String) {} - void playTrack(uint8 track); - void haltTrack(); - void beginFadeOut(); + virtual void playTrack(uint8 track); + virtual void haltTrack(); + virtual void beginFadeOut(); - int32 voicePlay(const char *file, Audio::SoundHandle *handle, uint8 volume, bool isSfx) { return -1; } - void playSoundEffect(uint8 track, uint8 volume = 0xff); + virtual int32 voicePlay(const char *file, Audio::SoundHandle *handle, uint8 volume, uint8 priority, bool isSfx) override { return -1; } + virtual void playSoundEffect(uint8 track, uint8 volume = 0xFF); protected: Audio::MaxTrax *_driver; diff --git a/engines/kyra/sound_lol.cpp b/engines/kyra/sound_lol.cpp index cb9be43b07..48230d025c 100644 --- a/engines/kyra/sound_lol.cpp +++ b/engines/kyra/sound_lol.cpp @@ -60,7 +60,7 @@ bool LoLEngine::snd_playCharacterSpeech(int id, int8 speaker, int) { Common::String pattern2 = Common::String::format("%02d", id & 0x4000 ? 0 : _curTlkFile); if (id & 0x4000) { - pattern1 = Common::String::format("%03X", id & 0x3fff); + pattern1 = Common::String::format("%03X", id & 0x3FFF); } else if (id < 1000) { pattern1 = Common::String::format("%03d", id); } else { @@ -164,11 +164,19 @@ void LoLEngine::snd_playSoundEffect(int track, int volume) { if (track == -1 || track >= _ingameSoundListSize) return; - volume &= 0xff; - int16 volIndex = (int16)READ_LE_UINT16(&_ingameSoundIndex[track * 2 + 1]); + volume &= 0xFF; + int16 prIndex = (int16)READ_LE_UINT16(&_ingameSoundIndex[track * 2 + 1]); + uint16 priority = (prIndex > 0) ? (prIndex * volume) >> 8 : -prIndex; - uint16 vocLevel = (volIndex > 0) ? (volIndex * volume) >> 8 : -volIndex; - vocLevel = CLIP(volume >> 4, 2, 13) * 7 + 164; + static const uint8 volTable1[] = { 223, 159, 95, 47, 15, 0 }; + static const uint8 volTable2[] = { 255, 191, 127, 63, 30, 0 }; + + for (int i = 0; i < 6; i++) { + if (volTable1[i] < volume) { + volume = volTable2[i]; + break; + } + } int16 vocIndex = (int16)READ_LE_UINT16(&_ingameSoundIndex[track * 2]); @@ -180,7 +188,7 @@ void LoLEngine::snd_playSoundEffect(int track, int volume) { if (hasVocFile) { if (_sound->isVoicePresent(_ingameSoundList[vocIndex])) - _sound->voicePlay(_ingameSoundList[vocIndex], 0, vocLevel & 0xff, true); + _sound->voicePlay(_ingameSoundList[vocIndex], 0, volume, priority, true); } else if (_flags.platform == Common::kPlatformPC) { if (_sound->getSfxType() == Sound::kMidiMT32) track = (track < _ingameMT32SoundIndexSize) ? (_ingameMT32SoundIndex[track] - 1) : -1; @@ -206,8 +214,8 @@ bool LoLEngine::snd_processEnvironmentalSoundEffect(int soundId, int block) { uint16 cbl = _currentBlock; for (int i = 3; i > 0; i--) { - int dir = calcMonsterDirection(cbl & 0x1f, cbl >> 5, block & 0x1f, block >> 5); - cbl = (cbl + blockShiftTable[dir]) & 0x3ff; + int dir = calcMonsterDirection(cbl & 0x1F, cbl >> 5, block & 0x1F, block >> 5); + cbl = (cbl + blockShiftTable[dir]) & 0x3FF; if (cbl != block) { if (testWallFlag(cbl, 0, 1)) _environmentSfxVol >>= 1; @@ -239,7 +247,7 @@ void LoLEngine::snd_playQueuedEffects() { void LoLEngine::snd_loadSoundFile(int track) { if (_sound->musicEnabled()) { - if (_flags.platform != Common::kPlatformPC98) { + if (_flags.platform == Common::kPlatformPC) { int t = (track - 250) * 3; if (_curMusicFileIndex != _musicTrackMap[t] || _curMusicFileExt != (char)_musicTrackMap[t + 1]) { snd_stopMusic(); @@ -261,12 +269,12 @@ int LoLEngine::snd_playTrack(int track) { _lastMusicTrack = track; if (_sound->musicEnabled()) { - if (_flags.platform == Common::kPlatformPC98) { - _sound->playTrack(track - 249); - } else { + if (_flags.platform == Common::kPlatformPC) { snd_loadSoundFile(track); int t = (track - 250) * 3; _sound->playTrack(_musicTrackMap[t + 2]); + } else { + _sound->playTrack(track - 249); } } diff --git a/engines/kyra/sound_midi.cpp b/engines/kyra/sound_midi.cpp index 70cc304192..a54c52612b 100644 --- a/engines/kyra/sound_midi.cpp +++ b/engines/kyra/sound_midi.cpp @@ -442,6 +442,8 @@ SoundMidiPC::SoundMidiPC(KyraEngine_v1 *vm, Audio::Mixer *mixer, MidiDriver *dri _output = 0; _musicFile = _sfxFile = 0; + _currentResourceSet = 0; + memset(&_resInfo, 0, sizeof(_resInfo)); _music = MidiParser::createParser_XMIDI(); assert(_music); @@ -495,6 +497,9 @@ SoundMidiPC::~SoundMidiPC() { delete[] _sfxFile; delete[] _musicFile; + + for (int i = 0; i < 3; i++) + initAudioResourceInfo(i, 0); } bool SoundMidiPC::init() { @@ -586,8 +591,29 @@ void SoundMidiPC::updateVolumeSettings() { _output->setSourceVolume(i, _sfxVolume, false); } +void SoundMidiPC::initAudioResourceInfo(int set, void *info) { + if (set >= kMusicIntro && set <= kMusicFinale) { + delete _resInfo[set]; + _resInfo[set] = info ? new SoundResourceInfo_PC(*(SoundResourceInfo_PC*)info) : 0; + } +} + +void SoundMidiPC::selectAudioResourceSet(int set) { + if (set >= kMusicIntro && set <= kMusicFinale) { + if (_resInfo[set]) + _currentResourceSet = set; + } +} + +bool SoundMidiPC::hasSoundFile(uint file) const { + if (file < res()->fileListSize) + return (res()->fileList[file] != 0); + return false; +} + void SoundMidiPC::loadSoundFile(uint file) { - loadSoundFile(fileListEntry(file)); + if (file < res()->fileListSize) + loadSoundFile(res()->fileList[file]); } void SoundMidiPC::loadSoundFile(Common::String file) { diff --git a/engines/kyra/sound_towns.cpp b/engines/kyra/sound_towns.cpp index 4b25db33f2..af741a1ebe 100644 --- a/engines/kyra/sound_towns.cpp +++ b/engines/kyra/sound_towns.cpp @@ -35,8 +35,8 @@ namespace Kyra { SoundTowns::SoundTowns(KyraEngine_v1 *vm, Audio::Mixer *mixer) : Sound(vm, mixer), _lastTrack(-1), _musicTrackData(0), _sfxFileData(0), _cdaPlaying(0), - _sfxFileIndex((uint)-1), _musicFadeTable(0), _sfxWDTable(0), _sfxBTTable(0), _sfxChannel(0x46) { - + _sfxFileIndex((uint)-1), _musicFadeTable(0), _sfxWDTable(0), _sfxBTTable(0), _sfxChannel(0x46), _currentResourceSet(0) { + memset(&_resInfo, 0, sizeof(_resInfo)); _driver = new TownsEuphonyDriver(_mixer); } @@ -46,6 +46,8 @@ SoundTowns::~SoundTowns() { delete _driver; delete[] _musicTrackData; delete[] _sfxFileData; + for (int i = 0; i < 3; i++) + initAudioResourceInfo(i, 0); } bool SoundTowns::init() { @@ -78,11 +80,12 @@ void SoundTowns::playTrack(uint8 track) { return; track -= 2; - const int32 *const tTable = (const int32 *)cdaData(); - int tTableIndex = 3 * track; + uint tTableIndex = 3 * track; + + assert(tTableIndex + 2 < res()->cdaTableSize); - int trackNum = (int)READ_LE_UINT32(&tTable[tTableIndex + 2]); - int32 loop = (int32)READ_LE_UINT32(&tTable[tTableIndex + 1]); + int trackNum = (int)READ_LE_UINT32(&res()->cdaTable[tTableIndex + 2]); + int32 loop = (int32)READ_LE_UINT32(&res()->cdaTable[tTableIndex + 1]); if (track == _lastTrack && _musicEnabled) return; @@ -95,7 +98,7 @@ void SoundTowns::playTrack(uint8 track) { g_system->getAudioCDManager()->updateCD(); _cdaPlaying = true; } else if (_musicEnabled) { - playEuphonyTrack(READ_LE_UINT32(&tTable[tTableIndex]), loop); + playEuphonyTrack(READ_LE_UINT32(&res()->cdaTable[tTableIndex]), loop); _cdaPlaying = false; } @@ -117,12 +120,32 @@ void SoundTowns::haltTrack() { _driver->stopParser(); } +void SoundTowns::initAudioResourceInfo(int set, void *info) { + if (set >= kMusicIntro && set <= kMusicFinale) { + delete _resInfo[set]; + _resInfo[set] = info ? new SoundResourceInfo_Towns(*(SoundResourceInfo_Towns*)info) : 0; + } +} + +void SoundTowns::selectAudioResourceSet(int set) { + if (set >= kMusicIntro && set <= kMusicFinale) { + if (_resInfo[set]) + _currentResourceSet = set; + } +} + +bool SoundTowns::hasSoundFile(uint file) const { + if (file < res()->fileListSize) + return (res()->fileList[file] != 0); + return false; +} + void SoundTowns::loadSoundFile(uint file) { - if (_sfxFileIndex == file) + if (_sfxFileIndex == file || file >= res()->fileListSize) return; _sfxFileIndex = file; delete[] _sfxFileData; - _sfxFileData = _vm->resource()->fileData(fileListEntry(file), 0); + _sfxFileData = _vm->resource()->fileData(res()->fileList[file], 0); } void SoundTowns::playSoundEffect(uint8 track, uint8) { @@ -151,8 +174,8 @@ void SoundTowns::playSoundEffect(uint8 track, uint8) { } } - uint8 *fileBody = _sfxFileData + 0x01b8; - int32 offset = (int32)READ_LE_UINT32(_sfxFileData + (track - 0x0b) * 4); + uint8 *fileBody = _sfxFileData + 0x01B8; + int32 offset = (int32)READ_LE_UINT32(_sfxFileData + (track - 0x0B) * 4); if (offset == -1) return; @@ -191,10 +214,10 @@ void SoundTowns::playSoundEffect(uint8 track, uint8) { sfx_WdTable_Number = READ_LE_UINT16(_sfxWDTable + sfx_WdTable_Offset); sfx_BtTable_Offset += (int16)READ_LE_UINT16(_sfxWDTable + sfx_WdTable_Offset + 2); - *tgt++ = _sfxBTTable[((sfx_BtTable_Offset >> 2) & 0xff)]; + *tgt++ = _sfxBTTable[((sfx_BtTable_Offset >> 2) & 0xFF)]; sfx_BtTable_Offset += (int16)READ_LE_UINT16(_sfxWDTable + sfx_WdTable_Offset + 4); - *tgt++ = _sfxBTTable[((sfx_BtTable_Offset >> 2) & 0xff)]; + *tgt++ = _sfxBTTable[((sfx_BtTable_Offset >> 2) & 0xFF)]; } } @@ -270,7 +293,7 @@ void SoundTowns::beginFadeOut() { for (int ii = 0; ii < 6; ii++) _driver->chanVolume(ii, fadeVolCur[ii]); for (int ii = 0x40; ii < 0x46; ii++) - _driver->chanVolume(ii, fadeVolCur[ii - 0x3a]); + _driver->chanVolume(ii, fadeVolCur[ii - 0x3A]); for (int ii = 0; ii < 6; ii++) { fadeVolCur[ii] -= fadeVolStep[ii]; @@ -367,13 +390,16 @@ void SoundTowns::fadeOutSoundEffects() { } SoundPC98::SoundPC98(KyraEngine_v1 *vm, Audio::Mixer *mixer) : - Sound(vm, mixer), _musicTrackData(0), _sfxTrackData(0), _lastTrack(-1), _driver(0) { + Sound(vm, mixer), _musicTrackData(0), _sfxTrackData(0), _lastTrack(-1), _driver(0), _currentResourceSet(0) { + memset(&_resInfo, 0, sizeof(_resInfo)); } SoundPC98::~SoundPC98() { delete[] _musicTrackData; delete[] _sfxTrackData; delete _driver; + for (int i = 0; i < 3; i++) + initAudioResourceInfo(i, 0); } bool SoundPC98::init() { @@ -383,8 +409,26 @@ bool SoundPC98::init() { return reslt; } -void SoundPC98::loadSoundFile(uint file) { - if (!scumm_strnicmp(fileListEntry(0), "INTRO", 5)) { +void SoundPC98::initAudioResourceInfo(int set, void *info) { + if (set >= kMusicIntro && set <= kMusicFinale) { + delete _resInfo[set]; + _resInfo[set] = info ? new Common::String(((SoundResourceInfo_PC98*)info)->pattern) : 0; + } +} + +void SoundPC98::selectAudioResourceSet(int set) { + if (set >= kMusicIntro && set <= kMusicFinale) { + if (_resInfo[set]) + _currentResourceSet = set; + } +} + +bool SoundPC98::hasSoundFile(uint file) const { + return true; +} + +void SoundPC98::loadSoundFile(uint) { + if (_currentResourceSet == kMusicIntro) { delete[] _sfxTrackData; _sfxTrackData = 0; @@ -407,14 +451,14 @@ void SoundPC98::loadSoundFile(Common::String file) { } void SoundPC98::playTrack(uint8 track) { - track += extraOffset(); + track -= 1; if (track == _lastTrack && _musicEnabled) return; beginFadeOut(); - Common::String musicFile = fileListLen() == 1 ? Common::String::format(fileListEntry(0), track) : fileListEntry(track); + Common::String musicFile = Common::String::format(resPattern(), track); delete[] _musicTrackData; _musicTrackData = _vm->resource()->fileData(musicFile.c_str(), 0); if (_musicEnabled) @@ -464,13 +508,16 @@ void SoundPC98::updateVolumeSettings() { // KYRA 2 SoundTownsPC98_v2::SoundTownsPC98_v2(KyraEngine_v1 *vm, Audio::Mixer *mixer) : - Sound(vm, mixer), _currentSFX(0), _musicTrackData(0), _sfxTrackData(0), _lastTrack(-1), _driver(0), _useFmSfx(false) { + Sound(vm, mixer), _currentSFX(0), _musicTrackData(0), _sfxTrackData(0), _lastTrack(-1), _driver(0), _useFmSfx(false), _currentResourceSet(0) { + memset(&_resInfo, 0, sizeof(_resInfo)); } SoundTownsPC98_v2::~SoundTownsPC98_v2() { delete[] _musicTrackData; delete[] _sfxTrackData; delete _driver; + for (int i = 0; i < 3; i++) + initAudioResourceInfo(i, 0); } bool SoundTownsPC98_v2::init() { @@ -478,7 +525,9 @@ bool SoundTownsPC98_v2::init() { TownsPC98_AudioDriver::kType86 : TownsPC98_AudioDriver::kTypeTowns); if (_vm->gameFlags().platform == Common::kPlatformFMTowns) { - _vm->checkCD(); + if (_resInfo[_currentResourceSet]) + if (_resInfo[_currentResourceSet]->cdaTableSize) + _vm->checkCD(); // FIXME: While checking for 'track1.XXX(X)' looks like // a good idea, we should definitely not be doing this // here. Basically our filenaming scheme could change @@ -486,9 +535,9 @@ bool SoundTownsPC98_v2::init() { // this misses the possibility that we play the tracks // right off CD. So we should find another way to // check if we have access to CD audio. - Resource *res = _vm->resource(); + Resource *r = _vm->resource(); if (_musicEnabled && - (res->exists("track1.mp3") || res->exists("track1.ogg") || res->exists("track1.flac") || res->exists("track1.fla"))) + (r->exists("track1.mp3") || r->exists("track1.ogg") || r->exists("track1.flac") || r->exists("track1.fla"))) _musicEnabled = 2; else _musicEnabled = 1; @@ -503,6 +552,26 @@ bool SoundTownsPC98_v2::init() { return reslt; } +void SoundTownsPC98_v2::initAudioResourceInfo(int set, void *info) { + if (set >= kMusicIntro && set <= kMusicFinale) { + delete _resInfo[set]; + _resInfo[set] = info ? new SoundResourceInfo_TownsPC98V2(*(SoundResourceInfo_TownsPC98V2*)info) : 0; + } +} + +void SoundTownsPC98_v2::selectAudioResourceSet(int set) { + if (set >= kMusicIntro && set <= kMusicFinale) { + if (_resInfo[set]) + _currentResourceSet = set; + } +} + +bool SoundTownsPC98_v2::hasSoundFile(uint file) const { + if (file < res()->fileListSize) + return (res()->fileList[file] != 0); + return false; +} + void SoundTownsPC98_v2::loadSoundFile(Common::String file) { delete[] _sfxTrackData; _sfxTrackData = _vm->resource()->fileData(file.c_str(), 0); @@ -513,18 +582,14 @@ void SoundTownsPC98_v2::process() { } void SoundTownsPC98_v2::playTrack(uint8 track) { - track += extraOffset(); - if (track == _lastTrack && _musicEnabled) return; - const uint16 *const cdaTracks = (const uint16 *)cdaData(); - int trackNum = -1; if (_vm->gameFlags().platform == Common::kPlatformFMTowns) { - for (int i = 0; i < cdaTrackNum(); i++) { - if (track == (uint8) READ_LE_UINT16(&cdaTracks[i * 2])) { - trackNum = (int) READ_LE_UINT16(&cdaTracks[i * 2 + 1]) - 1; + for (uint i = 0; i < res()->cdaTableSize; i++) { + if (track == (uint8) READ_LE_UINT16(&res()->cdaTable[i * 2])) { + trackNum = (int) READ_LE_UINT16(&res()->cdaTable[i * 2 + 1]) - 1; break; } } @@ -532,9 +597,10 @@ void SoundTownsPC98_v2::playTrack(uint8 track) { beginFadeOut(); - Common::String musicFile = fileListLen() == 1 ? Common::String::format(fileListEntry(0), track) : fileListEntry(track); + Common::String musicFile = res()->pattern ? Common::String::format(res()->pattern, track) : (res()->fileList ? res()->fileList[track] : 0); if (musicFile.empty()) return; + delete[] _musicTrackData; _musicTrackData = _vm->resource()->fileData(musicFile.c_str(), 0); @@ -569,15 +635,24 @@ void SoundTownsPC98_v2::beginFadeOut() { haltTrack(); } -int32 SoundTownsPC98_v2::voicePlay(const char *file, Audio::SoundHandle *handle, uint8, bool) { +int32 SoundTownsPC98_v2::voicePlay(const char *file, Audio::SoundHandle *handle, uint8 volume, uint8 priority, bool) { static const uint16 rates[] = { 0x10E1, 0x0CA9, 0x0870, 0x0654, 0x0438, 0x032A, 0x021C, 0x0194 }; static const char patternHOF[] = "%s.PCM"; static const char patternLOL[] = "%s.VOC"; int h = 0; if (_currentSFX) { - while (h < kNumChannelHandles && _mixer->isSoundHandleActive(_soundChannels[h])) + while (h < kNumChannelHandles && _mixer->isSoundHandleActive(_soundChannels[h].handle)) h++; + + if (h >= kNumChannelHandles) { + h = 0; + while (h < kNumChannelHandles && _soundChannels[h].priority > priority) + ++h; + if (h < kNumChannelHandles) + voiceStop(&_soundChannels[h].handle); + } + if (h >= kNumChannelHandles) return 0; } @@ -621,7 +696,7 @@ int32 SoundTownsPC98_v2::voicePlay(const char *file, Audio::SoundHandle *handle, cmd = ~cmd; } else { cmd |= 0x80; - if (cmd == 0xff) + if (cmd == 0xFF) cmd--; } if (cmd < 0x80) @@ -630,9 +705,10 @@ int32 SoundTownsPC98_v2::voicePlay(const char *file, Audio::SoundHandle *handle, } _currentSFX = Audio::makeRawStream(sfx, outsize, sfxRate * 10, Audio::FLAG_UNSIGNED | Audio::FLAG_LITTLE_ENDIAN); - _mixer->playStream(Audio::Mixer::kSFXSoundType, &_soundChannels[h], _currentSFX); + _mixer->playStream(Audio::Mixer::kSFXSoundType, &_soundChannels[h].handle, _currentSFX, -1, volume); + _soundChannels[h].priority = priority; if (handle) - *handle = _soundChannels[h]; + *handle = _soundChannels[h].handle; delete[] data; return 1; diff --git a/engines/kyra/sprites_eob.cpp b/engines/kyra/sprites_eob.cpp index 5c679f5cb4..962efe6a4e 100644 --- a/engines/kyra/sprites_eob.cpp +++ b/engines/kyra/sprites_eob.cpp @@ -58,7 +58,7 @@ void EoBCoreEngine::releaseMonsterShapes(int first, int num) { const uint8 *EoBCoreEngine::loadMonsterProperties(const uint8 *data) { uint8 cmd = *data++; - while (cmd != 0xff) { + while (cmd != 0xFF) { EoBMonsterProperty *d = &_monsterProps[cmd]; d->armorClass = (int8)*data++; d->hitChance = (int8)*data++; @@ -90,7 +90,7 @@ const uint8 *EoBCoreEngine::loadMonsterProperties(const uint8 *data) { d->sound2 = (int8)*data++; d->numRemoteAttacks = *data++; - if (*data++ != 0xff) { + if (*data++ != 0xFF) { d->remoteWeaponChangeMode = *data++; d->numRemoteWeapons = *data++; @@ -113,7 +113,7 @@ const uint8 *EoBCoreEngine::loadMonsterProperties(const uint8 *data) { } const uint8 *EoBCoreEngine::loadActiveMonsterData(const uint8 *data, int level) { - for (uint8 p = *data++; p != 0xff; p = *data++) { + for (uint8 p = *data++; p != 0xFF; p = *data++) { uint8 v = *data++; _timer->setCountdown(0x20 + (p << 1), v); _timer->setCountdown(0x21 + (p << 1), v); @@ -132,7 +132,7 @@ const uint8 *EoBCoreEngine::loadActiveMonsterData(const uint8 *data, int level) memset(_monsters, 0, 30 * sizeof(EoBMonsterInPlay)); for (int i = 0; i < 30; i++, data += 14) { - if (*data == 0xff) + if (*data == 0xFF) continue; initMonster(data[0], data[1], READ_LE_UINT16(&data[2]), data[4], (int8)data[5], data[6], data[7], data[8], data[9], READ_LE_UINT16(&data[10]), READ_LE_UINT16(&data[12])); @@ -174,7 +174,7 @@ void EoBCoreEngine::initMonster(int index, int unit, uint16 block, int pos, int } void EoBCoreEngine::placeMonster(EoBMonsterInPlay *m, uint16 block, int dir) { - if (block != 0xffff) { + if (block != 0xFFFF) { checkSceneUpdateNeed(m->block); if (_levelBlockProperties[m->block].flags & 7) { _levelBlockProperties[m->block].flags--; @@ -201,11 +201,11 @@ void EoBCoreEngine::killMonster(EoBMonsterInPlay *m, bool giveExperience) { if (m->randItem) { if (rollDice(1, 10, 0) == 1) - setItemPosition((Item *)&_levelBlockProperties[m->block & 0x3ff].drawObjects, m->block, duplicateItem(m->randItem), pos); + setItemPosition((Item *)&_levelBlockProperties[m->block & 0x3FF].drawObjects, m->block, duplicateItem(m->randItem), pos); } if (m->fixedItem) - setItemPosition((Item *)&_levelBlockProperties[m->block & 0x3ff].drawObjects, m->block, duplicateItem(m->fixedItem), pos); + setItemPosition((Item *)&_levelBlockProperties[m->block & 0x3FF].drawObjects, m->block, duplicateItem(m->fixedItem), pos); if (giveExperience) increasePartyExperience(_monsterProps[m->type].experience); @@ -357,7 +357,7 @@ void EoBCoreEngine::flashMonsterShape(EoBMonsterInPlay *m) { disableSysTimer(2); _flashShapeTimer = 0; drawScene(1); - m->flags &= 0xfd; + m->flags &= 0xFD; _flashShapeTimer = _system->getMillis() + _tickLength; enableSysTimer(2); @@ -536,7 +536,7 @@ void EoBCoreEngine::drawMonsters(int index) { int h = shp[1]; x = x - (w >> 1) + (d->idleAnimState >> 4); - y = y - h + (d->idleAnimState & 0x0f); + y = y - h + (d->idleAnimState & 0x0F); drawMonsterShape(shp, x, y, f >= 0 ? 0 : 1, d->flags, palIndex); diff --git a/engines/kyra/sprites_lol.cpp b/engines/kyra/sprites_lol.cpp index f4bae113c5..88b24e1367 100644 --- a/engines/kyra/sprites_lol.cpp +++ b/engines/kyra/sprites_lol.cpp @@ -66,7 +66,7 @@ void LoLEngine::loadMonsterShapes(const char *file, int monsterIndex, int animTy of[2] = _screen->makeShapeCopy(p, s + 2); } } - _monsterAnimType[monsterIndex] = animType & 0xff; + _monsterAnimType[monsterIndex] = animType & 0xFF; uint8 *palShape = _screen->makeShapeCopy(p, 16); @@ -90,7 +90,7 @@ void LoLEngine::loadMonsterShapes(const char *file, int monsterIndex, int animTy uint16 sz = MIN(_screen->getShapeSize(_monsterShapes[pos]) - 10, 256); memset(tmpPal2, 0, 256); memcpy(tmpPal2, _monsterShapes[pos] + 10, sz); - memset(tmpPal3, 0xff, 256 * sizeof(uint16)); + memset(tmpPal3, 0xFF, 256 * sizeof(uint16)); uint8 numCol = *tmpPal2; for (int ii = 0; ii < numCol; ii++) { @@ -104,7 +104,7 @@ void LoLEngine::loadMonsterShapes(const char *file, int monsterIndex, int animTy memset(tmpPal2, 0, 256); memcpy(tmpPal2, _monsterShapes[pos] + 10, sz); for (int iii = 0; iii < numCol; iii++) { - if (tmpPal3[iii] == 0xffff) + if (tmpPal3[iii] == 0xFFFF) continue; if (p[tmpPal3[iii] * 320 + ii + 1]) tmpPal2[1 + iii] = p[tmpPal3[iii] * 320 + ii + 1]; @@ -159,7 +159,7 @@ int LoLEngine::deleteMonstersFromBlock(int block) { continue; } - LoLMonster *m = &_monsters[i & 0x7fff]; + LoLMonster *m = &_monsters[i & 0x7FFF]; cnt++; setMonsterMode(m, 14); @@ -215,8 +215,8 @@ bool LoLEngine::updateMonsterAdjustBlocks(LoLMonster *monster) { if (monster->properties->flags & 8) return true; - uint16 x1 = (monster->x & 0xff00) | 0x80; - uint16 y1 = (monster->y & 0xff00) | 0x80; + uint16 x1 = (monster->x & 0xFF00) | 0x80; + uint16 y1 = (monster->y & 0xFF00) | 0x80; int x2 = _partyPosX; int y2 = _partyPosY; @@ -242,7 +242,7 @@ bool LoLEngine::updateMonsterAdjustBlocks(LoLMonster *monster) { return false; for (int i = 0; i < 18; i++) - _visibleBlocks[i] = &_levelBlockProperties[(monster->block + _dscBlockIndex[dir + i]) & 0x3ff]; + _visibleBlocks[i] = &_levelBlockProperties[(monster->block + _dscBlockIndex[dir + i]) & 0x3FF]; int16 fx1 = 0; int16 fx2 = 0; @@ -277,8 +277,8 @@ void LoLEngine::placeMonster(LoLMonster *monster, uint16 x, uint16 y) { _levelBlockProperties[monster->block].direction = 5; checkSceneUpdateNeed(monster->block); - // WORKAROUND: Some monsters in the white tower have sound id's of 0xff. This is definitely a bug, since the - // last valid track number is 249 and there is no specific handling for 0xff. Nonetheless this wouldn't + // WORKAROUND: Some monsters in the white tower have sound id's of 0xFF. This is definitely a bug, since the + // last valid track number is 249 and there is no specific handling for 0xFF. Nonetheless this wouldn't // cause problems in the original code, because it just so happens that the invalid memory address points // to an entry in _ingameGMSoundIndex which just so happens to have a value of -1 if (monster->properties->sounds[0] == 0 || monster->properties->sounds[0] == 255 || cont == false) @@ -364,7 +364,7 @@ int LoLEngine::checkBlockBeforeObjectPlacement(uint16 x, uint16 y, uint16 object return 4; if (x & 0x80) { - if (((x & 0xff) + objectWidth) & 0xff00) { + if (((x & 0xFF) + objectWidth) & 0xFF00) { xOffs = 1; _objectLastDirection = 2; x2 = x + objectWidth; @@ -380,7 +380,7 @@ int LoLEngine::checkBlockBeforeObjectPlacement(uint16 x, uint16 y, uint16 object flag = 1; } } else { - if (((x & 0xff) - objectWidth) & 0xff00) { + if (((x & 0xFF) - objectWidth) & 0xFF00) { xOffs = -1; _objectLastDirection = 6; x2 = x - objectWidth; @@ -398,7 +398,7 @@ int LoLEngine::checkBlockBeforeObjectPlacement(uint16 x, uint16 y, uint16 object } if (y & 0x80) { - if (((y & 0xff) + objectWidth) & 0xff00) { + if (((y & 0xFF) + objectWidth) & 0xFF00) { yOffs = 1; _objectLastDirection = 4; y2 = y + objectWidth; @@ -415,7 +415,7 @@ int LoLEngine::checkBlockBeforeObjectPlacement(uint16 x, uint16 y, uint16 object flag = 0; } } else { - if (((y & 0xff) - objectWidth) & 0xff00) { + if (((y & 0xFF) - objectWidth) & 0xFF00) { yOffs = -1; _objectLastDirection = 0; y2 = y - objectWidth; @@ -449,7 +449,7 @@ int LoLEngine::checkBlockBeforeObjectPlacement(uint16 x, uint16 y, uint16 object int LoLEngine::testBlockPassability(int block, int x, int y, int objectWidth, int testFlag, int wallFlag) { if (block == _currentBlock) - testFlag &= 0xfffe; + testFlag &= 0xFFFE; if (testFlag & 1) { _monsterCurBlock = block; @@ -463,7 +463,7 @@ int LoLEngine::testBlockPassability(int block, int x, int y, int objectWidth, in uint16 obj = _levelBlockProperties[block].assignedObjects; while (obj & 0x8000) { - LoLMonster *monster = &_monsters[obj & 0x7fff]; + LoLMonster *monster = &_monsters[obj & 0x7FFF]; if (monster->mode < 13) { int r = checkDrawObjectSpace(x, y, monster->x, monster->y); @@ -481,19 +481,8 @@ int LoLEngine::calcMonsterSkillLevel(int id, int a) { const uint16 *c = getCharacterOrMonsterStats(id); int r = (a << 8) / c[4]; - /* - if (!(id & 0x8000)) - r = (r * _monsterModifiers[3 + _monsterDifficulty]) >> 8; - - id &= 0x7fff; - - if (_characters[id].skillLevels[1] <= 3) - return r; - else if (_characters[id].skillLevels[1] <= 7) - return (r- (r >> 2));*/ - if (id & 0x8000) { - r = (r * _monsterModifiers[3 + _monsterDifficulty]) >> 8; + r = (r * _monsterModifiers2[3 + _monsterDifficulty]) >> 8; } else { if (_characters[id].skillLevels[1] > 7) r = (r - (r >> 1)); @@ -530,7 +519,7 @@ void LoLEngine::drawBlockObjects(int blockArrayIndex) { s = l->drawObjects; while (s) { if (s & 0x8000) { - s &= 0x7fff; + s &= 0x7FFF; if (blockArrayIndex < 15) drawMonster(s); s = _monsters[s].nextDrawObject; @@ -607,7 +596,7 @@ void LoLEngine::drawBlockObjects(int blockArrayIndex) { void LoLEngine::drawMonster(uint16 id) { LoLMonster *m = &_monsters[id]; int16 flg = _monsterDirFlags[(_currentDirection << 2) + m->facing]; - int curFrm = getMonsterCurFrame(m, flg & 0xffef); + int curFrm = getMonsterCurFrame(m, flg & 0xFFEF); uint8 *shp = 0; if (curFrm == -1) { @@ -622,7 +611,7 @@ void LoLEngine::drawMonster(uint16 id) { if (m->properties->flags & 0x800) flg |= 0x20; - uint8 *monsterPalette = d ? _monsterPalettes[(m->properties->shapeIndex << 4) + (curFrm & 0x0f)] + (shp[10] * (d - 1)) : 0; + uint8 *monsterPalette = d ? _monsterPalettes[(m->properties->shapeIndex << 4) + (curFrm & 0x0F)] + (shp[10] * (d - 1)) : 0; uint8 *brightnessOverlay = drawItemOrMonster(shp, monsterPalette, m->x + _monsterShiftOffs[m->shiftStep << 1], m->y + _monsterShiftOffs[(m->shiftStep << 1) + 1], 0, 0, flg | 1, -1, flip); for (int i = 0; i < 4; i++) { @@ -644,16 +633,16 @@ void LoLEngine::drawMonster(uint16 id) { int dW = _screen->getShapeScaledWidth(shp, _dmScaleW) >> 1; int dH = _screen->getShapeScaledHeight(shp, _dmScaleH) >> 1; - int bloodAmount = (m->mode == 13) ? (m->fightCurTick << 1) : (m->properties->hitPoints / (m->damageReceived & 0x7fff)); + int bloodAmount = (m->mode == 13) ? (m->fightCurTick << 1) : (m->properties->hitPoints / (m->damageReceived & 0x7FFF)); shp = _gameShapes[6]; - int bloodType = m->properties->flags & 0xc000; + int bloodType = m->properties->flags & 0xC000; if (bloodType == 0x4000) - bloodType = _flags.use16ColorMode ? 0xbb : 63; + bloodType = _flags.use16ColorMode ? 0xBB : 63; else if (bloodType == 0x8000) bloodType = _flags.use16ColorMode ? 0x55 : 15; - else if (bloodType == 0xc000) + else if (bloodType == 0xC000) bloodType = _flags.use16ColorMode ? 0x33 : 74; else bloodType = 0; @@ -708,7 +697,9 @@ int LoLEngine::getMonsterCurFrame(LoLMonster *m, uint16 dirFlags) { break; case 1: // monsters whose outward appearance reflects the damage they have taken - tmp = (m->properties->hitPoints * _monsterModifiers[_monsterDifficulty]) >> 8; + tmp = m->properties->hitPoints; + if (_flags.isTalkie) + tmp = (tmp * _monsterModifiers1[_monsterDifficulty]) >> 8; if (m->hitPoints > (tmp >> 1)) tmp = 0; else if (m->hitPoints > (tmp >> 2)) @@ -796,7 +787,7 @@ void LoLEngine::redrawSceneItem() { int t = (i << 7) + 1; while (s) { if (s & 0x8000) { - s = _monsters[s & 0x7fff].nextDrawObject; + s = _monsters[s & 0x7FFF].nextDrawObject; } else { LoLItem *item = &_itemsInPlay[s]; @@ -934,7 +925,7 @@ uint8 *LoLEngine::drawItemOrMonster(uint8 *shape, uint8 *monsterPalette, int x, uint8 tmpOvl[16]; if (flags & 0x80) { - flags &= 0xff7f; + flags &= 0xFF7F; ovl2 = monsterPalette; monsterPalette = 0; } else { @@ -969,7 +960,7 @@ uint8 *LoLEngine::drawItemOrMonster(uint8 *shape, uint8 *monsterPalette, int x, if (_flags.use16ColorMode) { if (_currentLevel != 22) - flg &= 0xdfff; + flg &= 0xDFFF; } else { if (_currentLevel == 22) { @@ -1059,7 +1050,7 @@ void LoLEngine::updateMonster(LoLMonster *monster) { if ((monster->mode != 11) && (monster->mode != 14)) { if (!(_rnd.getRandomNumber(255) & 3)) { - monster->shiftStep = (monster->shiftStep + 1) & 0x0f; + monster->shiftStep = (monster->shiftStep + 1) & 0x0F; checkSceneUpdateNeed(monster->block); } } @@ -1127,7 +1118,7 @@ void LoLEngine::updateMonster(LoLMonster *monster) { // first recovery phase after delivering an attack if (++monster->fightCurTick > 2) { setMonsterMode(monster, 5); - monster->fightCurTick = (int8)((((8 << 8) / monster->properties->fightingStats[4]) * _monsterModifiers[6 + _monsterDifficulty]) >> 8); + monster->fightCurTick = (int8)((((8 << 8) / monster->properties->fightingStats[4]) * _monsterModifiers3[_monsterDifficulty]) >> 8); } checkSceneUpdateNeed(monster->block); break; @@ -1137,7 +1128,7 @@ void LoLEngine::updateMonster(LoLMonster *monster) { chasePartyWithCloseAttacks(monster); } else { setMonsterMode(monster, 7); - monster->flags &= 0xfff7; + monster->flags &= 0xFFF7; } break; @@ -1164,13 +1155,13 @@ void LoLEngine::updateMonster(LoLMonster *monster) { if (monster->damageReceived) { if (monster->damageReceived & 0x8000) - monster->damageReceived &= 0x7fff; + monster->damageReceived &= 0x7FFF; else monster->damageReceived = 0; checkSceneUpdateNeed(monster->block); } - monster->flags &= 0xffef; + monster->flags &= 0xFFEF; } void LoLEngine::moveMonster(LoLMonster *monster) { @@ -1239,13 +1230,13 @@ bool LoLEngine::chasePartyWithDistanceAttacks(LoLMonster *monster) { int flyingObject = monster->properties->distWeapons[s]; - if (flyingObject & 0xc000) { + if (flyingObject & 0xC000) { if (getBlockDistance(monster->block, _currentBlock) > 1) { int type = flyingObject & 0x4000 ? 0 : 1; - flyingObject = makeItem(flyingObject & 0x3fff, 0, 0); + flyingObject = makeItem(flyingObject & 0x3FFF, 0, 0); if (flyingObject) { - if (!launchObject(type, flyingObject, monster->x, monster->y, 12, dir << 1, -1, monster->id | 0x8000, 0x3f)) + if (!launchObject(type, flyingObject, monster->x, monster->y, 12, dir << 1, -1, monster->id | 0x8000, 0x3F)) deleteItem(flyingObject); } } @@ -1274,7 +1265,7 @@ bool LoLEngine::chasePartyWithDistanceAttacks(LoLMonster *monster) { if (getBlockDistance(monster->block, _monsters[i].block) < 7) setMonsterMode(monster, 7); } - _txt->printMessage(2, "%s", getLangString(0x401a)); + _txt->printMessage(2, "%s", getLangString(0x401A)); } else if (flyingObject == 4) { launchMagicViper(); @@ -1294,7 +1285,7 @@ bool LoLEngine::chasePartyWithDistanceAttacks(LoLMonster *monster) { void LoLEngine::chasePartyWithCloseAttacks(LoLMonster *monster) { if (!(monster->flags & 8)) { - int dir = calcMonsterDirection(monster->x & 0xff00, monster->y & 0xff00, _partyPosX & 0xff00, _partyPosY & 0xff00); + int dir = calcMonsterDirection(monster->x & 0xFF00, monster->y & 0xFF00, _partyPosX & 0xFF00, _partyPosY & 0xFF00); int x1 = _partyPosX; int y1 = _partyPosY; @@ -1396,11 +1387,11 @@ int LoLEngine::checkForPossibleDistanceAttack(uint16 monsterBlock, int direction if (mdist > distance) return 5; - int dir = calcMonsterDirection(monsterBlock & 0x1f, monsterBlock >> 5, curBlock & 0x1f, curBlock >> 5); + int dir = calcMonsterDirection(monsterBlock & 0x1F, monsterBlock >> 5, curBlock & 0x1F, curBlock >> 5); if ((dir & 1) || (dir != (direction << 1))) return 5; - if (((monsterBlock & 0x1f) != (curBlock & 0x1f)) && ((monsterBlock & 0xffe0) != (curBlock & 0xffe0))) + if (((monsterBlock & 0x1F) != (curBlock & 0x1F)) && ((monsterBlock & 0xFFE0) != (curBlock & 0xFFE0))) return 5; if (distance < 0) @@ -1438,8 +1429,8 @@ void LoLEngine::getNextStepCoords(int16 srcX, int16 srcY, int &newX, int &newY, static const int8 stepAdjustX[] = { 0, 32, 32, 32, 0, -32, -32, -32 }; static const int8 stepAdjustY[] = { -32, -32, 0, 32, 32, 32, 0, -32 }; - newX = (srcX + stepAdjustX[direction]) & 0x1fff; - newY = (srcY + stepAdjustY[direction]) & 0x1fff; + newX = (srcX + stepAdjustX[direction]) & 0x1FFF; + newY = (srcY + stepAdjustY[direction]) & 0x1FFF; } void LoLEngine::alignMonsterToParty(LoLMonster *monster) { @@ -1447,7 +1438,7 @@ void LoLEngine::alignMonsterToParty(LoLMonster *monster) { uint16 mx = monster->x; uint16 my = monster->y; uint16 *pos = (mdir & 1) ? &my : &mx; - bool centered = (*pos & 0x7f) == 0; + bool centered = (*pos & 0x7F) == 0; bool posFlag = true; if (monster->properties->maxWidth <= 63) { @@ -1458,7 +1449,7 @@ void LoLEngine::alignMonsterToParty(LoLMonster *monster) { r = true; } else { uint16 id = _levelBlockProperties[monster->block].assignedObjects; - id = (id & 0x8000) ? (id & 0x7fff) : 0xffff; + id = (id & 0x8000) ? (id & 0x7FFF) : 0xFFFF; if (id != monster->id) { r = true; @@ -1466,8 +1457,8 @@ void LoLEngine::alignMonsterToParty(LoLMonster *monster) { for (int i = 0; i < 3; i++) { mdir = (mdir + 1) & 3; id = _levelBlockProperties[calcNewBlockPosition(monster->block, mdir)].assignedObjects; - id = (id & 0x8000) ? (id & 0x7fff) : 0xffff; - if (id != 0xffff) { + id = (id & 0x8000) ? (id & 0x7FFF) : 0xFFFF; + if (id != 0xFFFF) { r = true; break; } diff --git a/engines/kyra/sprites_rpg.cpp b/engines/kyra/sprites_rpg.cpp index 0c4fcb09ab..9c08bc8dd6 100644 --- a/engines/kyra/sprites_rpg.cpp +++ b/engines/kyra/sprites_rpg.cpp @@ -27,9 +27,9 @@ namespace Kyra { int KyraRpgEngine::getBlockDistance(uint16 block1, uint16 block2) { - int b1x = block1 & 0x1f; + int b1x = block1 & 0x1F; int b1y = block1 >> 5; - int b2x = block2 & 0x1f; + int b2x = block2 & 0x1F; int b2y = block2 >> 5; uint8 dy = ABS(b2y - b1y); diff --git a/engines/kyra/staticres.cpp b/engines/kyra/staticres.cpp index 423b827092..bac31f0a3e 100644 --- a/engines/kyra/staticres.cpp +++ b/engines/kyra/staticres.cpp @@ -31,6 +31,7 @@ #include "kyra/gui_lok.h" #include "kyra/gui_hof.h" #include "kyra/gui_mr.h" +#include "kyra/sequences_hof.h" #include "kyra/sound_intern.h" #include "common/endian.h" @@ -38,7 +39,7 @@ namespace Kyra { -#define RESFILE_VERSION 82 +#define RESFILE_VERSION 84 namespace { bool checkKyraDat(Common::SeekableReadStream *file) { @@ -245,8 +246,8 @@ bool StaticResource::init() { { kAmigaSfxTable, proc(loadAmigaSfxTable), proc(freeAmigaSfxTable) }, { kRawData, proc(loadRawData), proc(freeRawData) }, - { k2SeqData, proc(loadHofSequenceData), proc(freeHofSequenceData) }, - { k2ShpAnimDataV1, proc(loadShapeAnimData_v1), proc(freeHofShapeAnimDataV1) }, + { k2SeqData, proc(loadHoFSequenceData), proc(freeHoFSequenceData) }, + { k2SeqItemAnimData, proc(loadHoFSeqItemAnimData), proc(freeHoFSeqItemAnimData) }, { k2ItemAnimDefinition, proc(loadItemAnimDefinition), proc(freeItemAnimDefinition) }, #ifdef ENABLE_LOL @@ -289,7 +290,7 @@ void StaticResource::deinit() { } const char *const *StaticResource::loadStrings(int id, int &strings) { - return (const char * const *)getData(id, kStringList, strings); + return (const char *const *)getData(id, kStringList, strings); } const uint8 *StaticResource::loadRawData(int id, int &size) { @@ -308,12 +309,12 @@ const Room *StaticResource::loadRoomTable(int id, int &entries) { return (const Room *)getData(id, StaticResource::kRoomList, entries); } -const HofSeqData *StaticResource::loadHofSequenceData(int id, int &entries) { - return (const HofSeqData *)getData(id, k2SeqData, entries); +const HoFSeqData *StaticResource::loadHoFSequenceData(int id, int &entries) { + return (const HoFSeqData *)getData(id, k2SeqData, entries); } -const ItemAnimData_v1 *StaticResource::loadShapeAnimData_v1(int id, int &entries) { - return (const ItemAnimData_v1 *)getData(id, k2ShpAnimDataV1, entries); +const HoFSeqItemAnimData *StaticResource::loadHoFSeqItemAnimData(int id, int &entries) { + return (const HoFSeqItemAnimData *)getData(id, k2SeqItemAnimData, entries); } const ItemAnimDefinition *StaticResource::loadItemAnimDefinition(int id, int &entries) { @@ -513,12 +514,12 @@ bool StaticResource::loadRoomTable(Common::SeekableReadStream &stream, void *&pt return true; } -bool StaticResource::loadHofSequenceData(Common::SeekableReadStream &stream, void *&ptr, int &size) { +bool StaticResource::loadHoFSequenceData(Common::SeekableReadStream &stream, void *&ptr, int &size) { int numSeq = stream.readUint16BE(); uint32 offset = 2; - Sequence *tmp_s = new Sequence[numSeq]; + HoFSequence *tmp_s = new HoFSequence[numSeq]; - size = sizeof(HofSeqData) + numSeq * (sizeof(Sequence) + 28); + size = sizeof(HoFSeqData) + numSeq * (sizeof(HoFSequence) + 28); for (int i = 0; i < numSeq; i++) { stream.seek(offset, SEEK_SET); offset += 2; @@ -529,22 +530,22 @@ bool StaticResource::loadHofSequenceData(Common::SeekableReadStream &stream, voi stream.read(const_cast<char *>(tmp_s[i].wsaFile), 14); tmp_s[i].cpsFile = new char[14]; stream.read(const_cast<char *>(tmp_s[i].cpsFile), 14); - tmp_s[i].startupCommand = stream.readByte(); - tmp_s[i].finalCommand = stream.readByte(); + tmp_s[i].fadeInTransitionType = stream.readByte(); + tmp_s[i].fadeOutTransitionType = stream.readByte(); tmp_s[i].stringIndex1 = stream.readUint16BE(); tmp_s[i].stringIndex2 = stream.readUint16BE(); tmp_s[i].startFrame = stream.readUint16BE(); tmp_s[i].numFrames = stream.readUint16BE(); - tmp_s[i].frameDelay = stream.readUint16BE(); + tmp_s[i].duration = stream.readUint16BE(); tmp_s[i].xPos = stream.readUint16BE(); tmp_s[i].yPos = stream.readUint16BE(); - tmp_s[i].duration = stream.readUint16BE(); + tmp_s[i].timeout = stream.readUint16BE(); } stream.seek(offset, SEEK_SET); offset += 2; int numSeqN = stream.readUint16BE(); - NestedSequence *tmp_n = new NestedSequence[numSeqN]; - size += (numSeqN * (sizeof(NestedSequence) + 14)); + HoFNestedSequence *tmp_n = new HoFNestedSequence[numSeqN]; + size += (numSeqN * (sizeof(HoFNestedSequence) + 14)); for (int i = 0; i < numSeqN; i++) { stream.seek(offset, SEEK_SET); offset += 2; @@ -559,8 +560,8 @@ bool StaticResource::loadHofSequenceData(Common::SeekableReadStream &stream, voi tmp_n[i].x = stream.readUint16BE(); tmp_n[i].y = stream.readUint16BE(); uint16 ctrlOffs = stream.readUint16BE(); - tmp_n[i].startupCommand = stream.readUint16BE(); - tmp_n[i].finalCommand = stream.readUint16BE(); + tmp_n[i].fadeInTransitionType = stream.readUint16BE(); + tmp_n[i].fadeOutTransitionType = stream.readUint16BE(); if (ctrlOffs) { stream.seek(ctrlOffs, SEEK_SET); @@ -580,21 +581,21 @@ bool StaticResource::loadHofSequenceData(Common::SeekableReadStream &stream, voi } } - HofSeqData *loadTo = new HofSeqData; + HoFSeqData *loadTo = new HoFSeqData; assert(loadTo); loadTo->seq = tmp_s; - loadTo->seqn = tmp_n; + loadTo->nestedSeq = tmp_n; loadTo->numSeq = numSeq; - loadTo->numSeqn = numSeqN; + loadTo->numNestedSeq = numSeqN; ptr = loadTo; return true; } -bool StaticResource::loadShapeAnimData_v1(Common::SeekableReadStream &stream, void *&ptr, int &size) { +bool StaticResource::loadHoFSeqItemAnimData(Common::SeekableReadStream &stream, void *&ptr, int &size) { size = stream.readByte(); - ItemAnimData_v1 *loadTo = new ItemAnimData_v1[size]; + HoFSeqItemAnimData *loadTo = new HoFSeqItemAnimData[size]; assert(loadTo); for (int i = 0; i < size; i++) { @@ -670,8 +671,8 @@ void StaticResource::freeRoomTable(void *&ptr, int &size) { size = 0; } -void StaticResource::freeHofSequenceData(void *&ptr, int &size) { - HofSeqData *h = (HofSeqData *)ptr; +void StaticResource::freeHoFSequenceData(void *&ptr, int &size) { + HoFSeqData *h = (HoFSeqData *)ptr; for (int i = 0; i < h->numSeq; i++) { delete[] h->seq[i].wsaFile; @@ -679,19 +680,19 @@ void StaticResource::freeHofSequenceData(void *&ptr, int &size) { } delete[] h->seq; - for (int i = 0; i < h->numSeqn; i++) { - delete[] h->seqn[i].wsaFile; - delete[] h->seqn[i].wsaControl; + for (int i = 0; i < h->numNestedSeq; i++) { + delete[] h->nestedSeq[i].wsaFile; + delete[] h->nestedSeq[i].wsaControl; } - delete[] h->seqn; + delete[] h->nestedSeq; delete h; ptr = 0; size = 0; } -void StaticResource::freeHofShapeAnimDataV1(void *&ptr, int &size) { - ItemAnimData_v1 *d = (ItemAnimData_v1 *)ptr; +void StaticResource::freeHoFSeqItemAnimData(void *&ptr, int &size) { + HoFSeqItemAnimData *d = (HoFSeqItemAnimData *)ptr; for (int i = 0; i < size; i++) delete[] d[i].frames; delete[] d; @@ -777,22 +778,6 @@ void KyraEngine_LoK::initStaticResource() { _storyStrings = _staticres->loadStrings(k1PC98StoryStrings, _storyStringsSize); - int size1, size2; - const char *const *soundfiles1 = _staticres->loadStrings(k1AudioTracks, size1); - const char *const *soundfiles2 = _staticres->loadStrings(k1AudioTracks2, size2); - _soundFilesSize = size1 + size2; - if (_soundFilesSize) { - delete[] _soundFiles; - const char **soundfiles = new const char*[_soundFilesSize]; - for (int i = 0; i < _soundFilesSize; i++) - soundfiles[i] = (i < size1) ? soundfiles1[i] : soundfiles2[i - size1]; - _soundFiles = soundfiles; - } - _soundFilesIntro = _staticres->loadStrings(k1AudioTracksIntro, _soundFilesIntroSize); - _cdaTrackTable = (const int32 *)_staticres->loadRawData(k1TownsCDATable, _cdaTrackTableSize); - - // copied static res - // room list const Room *tempRoomList = _staticres->loadRoomTable(k1RoomList, _roomTableSize); @@ -819,34 +804,40 @@ void KyraEngine_LoK::initStaticResource() { _staticres->unloadId(k1DefaultShapes); } - // audio data tables - static const char *const tIntro98[] = { "INTRO%d.DAT" }; - static const char *const tIngame98[] = { "KYRAM%d.DAT" }; + // audio resource assignment + int size1, size2; + const char *const *soundfiles1 = _staticres->loadStrings(k1AudioTracks, size1); + const char *const *soundfiles2 = _staticres->loadStrings(k1AudioTracks2, size2); + int soundFilesSize = size1 + size2; + int soundFilesIntroSize = 0; + int cdaTableSize = 0; + const char **soundFiles = 0; + + if (soundFilesSize) { + soundFiles = new const char*[soundFilesSize]; + for (int i = 0; i < soundFilesSize; i++) + soundFiles[i] = (i < size1) ? soundfiles1[i] : soundfiles2[i - size1]; + } + const char *const *soundFilesIntro = _staticres->loadStrings(k1AudioTracksIntro, temp); + const int32 *cdaTable = (const int32 *)_staticres->loadRawData(k1TownsCDATable, cdaTableSize); // FIXME: It seems Kyra1 MAC CD includes AdLib and MIDI music and sfx, thus we enable // support for those for now. (Based on patch #2767489 "Support for Mac Kyrandia 1 CD" by satz). - memset(_soundData, 0, sizeof(_soundData)); if (_flags.platform == Common::kPlatformPC || _flags.platform == Common::kPlatformMacintosh) { - _soundData[0].fileList = _soundFilesIntro; - _soundData[0].fileListLen = _soundFilesIntroSize; - _soundData[1].fileList = _soundFiles; - _soundData[1].fileListLen = _soundFilesSize; + SoundResourceInfo_PC resInfoIntro(soundFilesIntro, soundFilesIntroSize); + SoundResourceInfo_PC resInfoIngame(soundFiles, soundFilesSize); + _sound->initAudioResourceInfo(kMusicIntro, &resInfoIntro); + _sound->initAudioResourceInfo(kMusicIngame, &resInfoIngame); } else if (_flags.platform == Common::kPlatformFMTowns) { - _soundData[0].fileList = _soundFiles; - _soundData[0].fileListLen = _soundFilesSize; - _soundData[0].cdaTracks = _cdaTrackTable; - _soundData[0].cdaNumTracks = _cdaTrackTableSize; - _soundData[1].fileList = _soundFiles; - _soundData[1].fileListLen = _soundFilesSize; - _soundData[1].cdaTracks = _cdaTrackTable; - _soundData[1].cdaNumTracks = _cdaTrackTableSize; + SoundResourceInfo_Towns resInfoIntro(soundFiles, soundFilesSize, cdaTable, cdaTableSize); + SoundResourceInfo_Towns resInfoIngame(soundFiles, soundFilesSize, cdaTable, cdaTableSize); + _sound->initAudioResourceInfo(kMusicIntro, &resInfoIntro); + _sound->initAudioResourceInfo(kMusicIngame, &resInfoIngame); } else if (_flags.platform == Common::kPlatformPC98) { - _soundData[0].fileList = tIntro98; - _soundData[0].fileListLen = 1; - _soundData[0].extraOffset = -1; - _soundData[1].fileList = tIngame98; - _soundData[1].fileListLen = 1; - _soundData[1].extraOffset = -1; + SoundResourceInfo_PC98 resInfoIntro("INTRO%d.DAT"); + SoundResourceInfo_PC98 resInfoIngame("KYRAM%d.DAT"); + _sound->initAudioResourceInfo(kMusicIntro, &resInfoIntro); + _sound->initAudioResourceInfo(kMusicIngame, &resInfoIngame); } } @@ -992,11 +983,7 @@ void KyraEngine_LoK::loadMainScreen(int page) { } void KyraEngine_HoF::initStaticResource() { - int tmpSize = 0; - - _sequencePakList = _staticres->loadStrings(k2SeqplayPakFiles, _sequencePakListSize); _ingamePakList = _staticres->loadStrings(k2IngamePakFiles, _ingamePakListSize); - _sequenceStrings = _staticres->loadStrings(k2SeqplayStrings, _sequenceStringsSize); _ingameSoundList = _staticres->loadStrings(k2IngameSfxFiles, _ingameSoundListSize); _ingameSoundIndex = (const uint16 *)_staticres->loadRawData(k2IngameSfxIndex, _ingameSoundIndexSize); _musicFileListIntro = _staticres->loadStrings(k2SeqplayIntroTracks, _musicFileListIntroSize); @@ -1009,131 +996,29 @@ void KyraEngine_HoF::initStaticResource() { _ingameTimJpStr = _staticres->loadStrings(k2IngameTimJpStrings, _ingameTimJpStrSize); _itemAnimDefinition = _staticres->loadItemAnimDefinition(k2IngameShapeAnimData, _itemAnimDefinitionSize); - // replace sequence talkie files with localized versions - const char *const *seqSoundList = _staticres->loadStrings(k2SeqplaySfxFiles, _sequenceSoundListSize); - const char *const *tlkfiles = _staticres->loadStrings(k2SeqplayTlkFiles, tmpSize); - char **tmpSndLst = new char *[_sequenceSoundListSize]; - - for (int i = 0; i < _sequenceSoundListSize; i++) { - const int len = strlen(seqSoundList[i]); - - tmpSndLst[i] = new char[len + 1]; - tmpSndLst[i][0] = 0; - - if (tlkfiles && len > 1) { - for (int ii = 0; ii < tmpSize; ii++) { - if (strlen(tlkfiles[ii]) > 1 && !scumm_stricmp(&seqSoundList[i][1], &tlkfiles[ii][1])) - strcpy(tmpSndLst[i], tlkfiles[ii]); - } - } - - if (tmpSndLst[i][0] == 0) - strcpy(tmpSndLst[i], seqSoundList[i]); - } - - tlkfiles = seqSoundList = 0; - _staticres->unloadId(k2SeqplayTlkFiles); - _staticres->unloadId(k2SeqplaySfxFiles); - _sequenceSoundList = tmpSndLst; - // assign music data - static const char *const fmtMusicFileListIntro[] = { "intro%d.twn" }; - static const char *const fmtMusicFileListFinale[] = { "finale%d.twn" }; - static const char *const fmtMusicFileListIngame[] = { "km%02d.twn" }; - - static const char *const pc98MusicFileListIntro[] = { "intro%d.86" }; - static const char *const pc98MusicFileListFinale[] = { "finale%d.86" }; - static const char *const pc98MusicFileListIngame[] = { "km%02d.86" }; - - memset(_soundData, 0, sizeof(_soundData)); if (_flags.platform == Common::kPlatformPC) { - _soundData[0].fileList = _musicFileListIntro; - _soundData[0].fileListLen = _musicFileListIntroSize; - _soundData[1].fileList = _musicFileListIngame; - _soundData[1].fileListLen = _musicFileListIngameSize; - _soundData[2].fileList = _musicFileListFinale; - _soundData[2].fileListLen = _musicFileListIntroSize; + SoundResourceInfo_PC resInfoIntro(_musicFileListIntro, _musicFileListIntroSize); + SoundResourceInfo_PC resInfoIngame(_musicFileListIngame, _musicFileListIngameSize); + SoundResourceInfo_PC resInfoFinale(_musicFileListFinale, _musicFileListFinaleSize); + _sound->initAudioResourceInfo(kMusicIntro, &resInfoIntro); + _sound->initAudioResourceInfo(kMusicIngame, &resInfoIngame); + _sound->initAudioResourceInfo(kMusicFinale, &resInfoFinale); } else if (_flags.platform == Common::kPlatformFMTowns) { - _soundData[0].fileList = fmtMusicFileListIntro; - _soundData[0].fileListLen = 1; - _soundData[0].cdaTracks = _cdaTrackTableIntro; - _soundData[0].cdaNumTracks = _cdaTrackTableIntroSize >> 1; - _soundData[1].fileList = fmtMusicFileListIngame; - _soundData[1].fileListLen = 1; - _soundData[1].cdaTracks = _cdaTrackTableIngame; - _soundData[1].cdaNumTracks = _cdaTrackTableIngameSize >> 1; - _soundData[2].fileList = fmtMusicFileListFinale; - _soundData[2].fileListLen = 1; - _soundData[2].cdaTracks = _cdaTrackTableFinale; - _soundData[2].cdaNumTracks = _cdaTrackTableFinaleSize >> 1; + SoundResourceInfo_TownsPC98V2 resInfoIntro(0, 0, "intro%d.twn", (const uint16*)_cdaTrackTableIntro, _cdaTrackTableIntroSize >> 1); + SoundResourceInfo_TownsPC98V2 resInfoIngame(0, 0, "km%02d.twn", (const uint16*)_cdaTrackTableIngame, _cdaTrackTableIngameSize >> 1); + SoundResourceInfo_TownsPC98V2 resInfoFinale(0, 0, "finale%d.twn", (const uint16*)_cdaTrackTableFinale, _cdaTrackTableFinaleSize >> 1); + _sound->initAudioResourceInfo(kMusicIntro, &resInfoIntro); + _sound->initAudioResourceInfo(kMusicIngame, &resInfoIngame); + _sound->initAudioResourceInfo(kMusicFinale, &resInfoFinale); } else if (_flags.platform == Common::kPlatformPC98) { - _soundData[0].fileList = pc98MusicFileListIntro; - _soundData[0].fileListLen = 1; - _soundData[1].fileList = pc98MusicFileListIngame; - _soundData[1].fileListLen = 1; - _soundData[2].fileList = pc98MusicFileListFinale; - _soundData[2].fileListLen = 1; + SoundResourceInfo_TownsPC98V2 resInfoIntro(0, 0, "intro%d.86", 0, 0); + SoundResourceInfo_TownsPC98V2 resInfoIngame(0, 0, "km%02d.86", 0, 0); + SoundResourceInfo_TownsPC98V2 resInfoFinale(0, 0, "finale%d.86", 0, 0); + _sound->initAudioResourceInfo(kMusicIntro, &resInfoIntro); + _sound->initAudioResourceInfo(kMusicIngame, &resInfoIngame); + _sound->initAudioResourceInfo(kMusicFinale, &resInfoFinale); } - - // setup sequence data - _sequences = _staticres->loadHofSequenceData(k2SeqplaySeqData, tmpSize); - - static const SeqProc hofSequenceCallbacks[] = { - 0, &KyraEngine_HoF::seq_introWestwood, - &KyraEngine_HoF::seq_introTitle, &KyraEngine_HoF::seq_introOverview, - &KyraEngine_HoF::seq_introLibrary, &KyraEngine_HoF::seq_introHand, - &KyraEngine_HoF::seq_introPoint, &KyraEngine_HoF::seq_introZanfaun, - &KyraEngine_HoF::seq_finaleFunters, &KyraEngine_HoF::seq_finaleFerb, - &KyraEngine_HoF::seq_finaleFish, &KyraEngine_HoF::seq_finaleFheep, - &KyraEngine_HoF::seq_finaleFarmer, &KyraEngine_HoF::seq_finaleFuards, - &KyraEngine_HoF::seq_finaleFirates, &KyraEngine_HoF::seq_finaleFrash - }; - - static const SeqProc hofNestedSequenceCallbacks[] = { - &KyraEngine_HoF::seq_finaleFiggle, &KyraEngine_HoF::seq_introOver1, - &KyraEngine_HoF::seq_introOver2, &KyraEngine_HoF::seq_introForest, - &KyraEngine_HoF::seq_introDragon, &KyraEngine_HoF::seq_introDarm, - &KyraEngine_HoF::seq_introLibrary2, &KyraEngine_HoF::seq_introLibrary2, - &KyraEngine_HoF::seq_introMarco, &KyraEngine_HoF::seq_introHand1a, - &KyraEngine_HoF::seq_introHand1b, &KyraEngine_HoF::seq_introHand1c, - &KyraEngine_HoF::seq_introHand2, &KyraEngine_HoF::seq_introHand3, 0 - }; - - static const SeqProc hofDemoSequenceCallbacks[] = { - &KyraEngine_HoF::seq_demoVirgin, &KyraEngine_HoF::seq_demoWestwood, - &KyraEngine_HoF::seq_demoTitle, &KyraEngine_HoF::seq_demoHill, - &KyraEngine_HoF::seq_demoOuthome, &KyraEngine_HoF::seq_demoWharf, - &KyraEngine_HoF::seq_demoDinob, &KyraEngine_HoF::seq_demoFisher, 0 - }; - - static const SeqProc hofDemoNestedSequenceCallbacks[] = { - &KyraEngine_HoF::seq_demoWharf2, &KyraEngine_HoF::seq_demoDinob2, - &KyraEngine_HoF::seq_demoWater, &KyraEngine_HoF::seq_demoBail, - &KyraEngine_HoF::seq_demoDig, 0 - }; - -#ifdef ENABLE_LOL - static const SeqProc kLoLDemoSequenceCallbacks[] = { - &KyraEngine_HoF::seq_lolDemoScene1, 0, &KyraEngine_HoF::seq_lolDemoScene2, 0, - &KyraEngine_HoF::seq_lolDemoScene3, 0, &KyraEngine_HoF::seq_lolDemoScene4, 0, - &KyraEngine_HoF::seq_lolDemoScene5, &KyraEngine_HoF::seq_lolDemoText5, - &KyraEngine_HoF::seq_lolDemoScene6, 0 - }; - - static const SeqProc kLoLDemoNestedSequenceCallbacks[] = { 0 }; -#endif // ENABLE_LOL - - _callbackS = -#ifdef ENABLE_LOL - _flags.gameID == GI_LOL ? kLoLDemoSequenceCallbacks : -#endif // ENABLE_LOL - ((_flags.isDemo && !_flags.isTalkie) ? hofDemoSequenceCallbacks : hofSequenceCallbacks); - - _callbackN = -#ifdef ENABLE_LOL - _flags.gameID == GI_LOL ? kLoLDemoNestedSequenceCallbacks : -#endif // ENABLE_LOL - ((_flags.isDemo && !_flags.isTalkie) ? hofDemoNestedSequenceCallbacks : hofNestedSequenceCallbacks); } void KyraEngine_MR::initStaticResource() { @@ -1224,13 +1109,13 @@ const uint8 KyraEngine_LoK::_itemPosY[] = { }; void GUI_LoK::initStaticResource() { - GUI_V1_BUTTON(_scrollUpButton, 0x12, 1, 1, 1, 0x483, 0, 0, 0, 0x18, 0x0f, 0); - GUI_V1_BUTTON(_scrollDownButton, 0x13, 1, 1, 1, 0x483, 0, 0, 0, 0x18, 0x0f, 0); + GUI_V1_BUTTON(_scrollUpButton, 0x12, 1, 1, 1, 0x483, 0, 0, 0, 0x18, 0x0F, 0); + GUI_V1_BUTTON(_scrollDownButton, 0x13, 1, 1, 1, 0x483, 0, 0, 0, 0x18, 0x0F, 0); - GUI_V1_BUTTON(_menuButtonData[0], 0x0c, 1, 1, 1, 0x487, 0, 0, 0, 0, 0, 0); - GUI_V1_BUTTON(_menuButtonData[1], 0x0d, 1, 1, 1, 0x487, 0, 0, 0, 0, 0, 0); - GUI_V1_BUTTON(_menuButtonData[2], 0x0e, 1, 1, 1, 0x487, 0, 0, 0, 0, 0, 0); - GUI_V1_BUTTON(_menuButtonData[3], 0x0f, 1, 1, 1, 0x487, 0, 0, 0, 0, 0, 0); + GUI_V1_BUTTON(_menuButtonData[0], 0x0C, 1, 1, 1, 0x487, 0, 0, 0, 0, 0, 0); + GUI_V1_BUTTON(_menuButtonData[1], 0x0D, 1, 1, 1, 0x487, 0, 0, 0, 0, 0, 0); + GUI_V1_BUTTON(_menuButtonData[2], 0x0E, 1, 1, 1, 0x487, 0, 0, 0, 0, 0, 0); + GUI_V1_BUTTON(_menuButtonData[3], 0x0F, 1, 1, 1, 0x487, 0, 0, 0, 0, 0, 0); GUI_V1_BUTTON(_menuButtonData[4], 0x10, 1, 1, 1, 0x487, 0, 0, 0, 0, 0, 0); GUI_V1_BUTTON(_menuButtonData[5], 0x11, 1, 1, 1, 0x487, 0, 0, 0, 0, 0, 0); @@ -1412,8 +1297,6 @@ const int GUI_v2::_sliderBarsPosition[] = { // kyra 2 static res -const uint8 KyraEngine_HoF::_seqTextColorPresets[] = { 0x01, 0x01, 0x00, 0x3f, 0x3f, 0x3f }; - const char *const KyraEngine_HoF::_languageExtension[] = { "ENG", "FRE", @@ -1631,7 +1514,7 @@ void KyraEngine_HoF::initInventoryButtonList() { _inventoryButtons[i].buttonCallback = inventoryCallback; _buttonList = &_inventoryButtons[0]; - for (size_t i = 1; i < 15; ++i) + for (int i = 1; i < 15; ++i) _buttonList = _gui->addButtonToList(_buttonList, &_inventoryButtons[i]); } diff --git a/engines/kyra/staticres_eob.cpp b/engines/kyra/staticres_eob.cpp index 7a5012f117..e0a2862dea 100644 --- a/engines/kyra/staticres_eob.cpp +++ b/engines/kyra/staticres_eob.cpp @@ -220,44 +220,44 @@ const uint8 EoBCoreEngine::_wallOfForceShapeDefs[] = { 0x0C, 0x00, 0x05, 0x10 }; -const int16 EoBCoreEngine::_buttonList1[] = { +const uint8 EoBCoreEngine::_buttonList1[] = { 58, 0, 1, 2, 3, 90, 91, 4, 5, 6, 7, 8, 9, 10, 11, 12, 78, 79, 13, 14, 15, 16, - 80, 81, 17, 18, 19, 20, 82, 83, 49, 50, 51, 52, 53, 54, 56, 57, -1 + 80, 81, 17, 18, 19, 20, 82, 83, 49, 50, 51, 52, 53, 54, 56, 57, 255 }; -const int16 EoBCoreEngine::_buttonList2[] = { +const uint8 EoBCoreEngine::_buttonList2[] = { 58, 61, 62, 63, 64, 65, 93, 94, 66, 67, 68, 69, 70, 71, 76, 77, 88, 0, 1, 2, 3, 90, 91, 4, 5, 6, 7, 8, 9, 10, 11, 12, 78, 79, 13, 14, 15, 16, 80, 81, 17, 18, - 19, 20, 82, 83, 49, 50, 51, 52, 53, 54, 56, 57, -1 + 19, 20, 82, 83, 49, 50, 51, 52, 53, 54, 56, 57, 255 }; -const int16 EoBCoreEngine::_buttonList3[] = { +const uint8 EoBCoreEngine::_buttonList3[] = { 58, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 84, 85, 46, 47, 48, 60, 59, 92, 4, 5, 6, 7, 8, 49, 50, - 51, 52, 53, 54, 56, 57, -1 + 51, 52, 53, 54, 56, 57, 255 }; -const int16 EoBCoreEngine::_buttonList4[] = { - 58, 47, 48, 60, 59, 92, 4, 5, 6, 7, 8, 49, 50, 51, 52, 53, 54, 56, 57, -1 +const uint8 EoBCoreEngine::_buttonList4[] = { + 58, 47, 48, 60, 59, 92, 4, 5, 6, 7, 8, 49, 50, 51, 52, 53, 54, 56, 57, 255 }; -const int16 EoBCoreEngine::_buttonList5[] = { +const uint8 EoBCoreEngine::_buttonList5[] = { 58, 61, 62, 63, 64, 65, 93, 66, 67, 68, 69, 70, 71, 88, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 84, - 85, 46, 47, 48, 60, 59, 92, 4, 5, 6, 7, 8, 49, 50, 51, 52, 53, 54, 56, 57, -1 + 85, 46, 47, 48, 60, 59, 92, 4, 5, 6, 7, 8, 49, 50, 51, 52, 53, 54, 56, 57, 255 }; -const int16 EoBCoreEngine::_buttonList6[] = { +const uint8 EoBCoreEngine::_buttonList6[] = { 58, 61, 62, 63, 64, 65, 93, 66, 67, 68, 69, 70, 71, 88, 46, 47, 48, 60, 59, 92, - 4, 5, 6, 7, 8, 49, 50, 51, 52, 53, 54, 56, 57, -1 + 4, 5, 6, 7, 8, 49, 50, 51, 52, 53, 54, 56, 57, 255 }; -const int16 EoBCoreEngine::_buttonList7[] = { - 17, 18, 19, 20, 82, 83, 55, -1 +const uint8 EoBCoreEngine::_buttonList7[] = { + 17, 18, 19, 20, 82, 83, 55, 255 }; -const int16 EoBCoreEngine::_buttonList8[] = { - 72, 73, 74, 75, 86, 87, 89, -1 +const uint8 EoBCoreEngine::_buttonList8[] = { + 72, 73, 74, 75, 86, 87, 89, 255 }; const uint8 EoBCoreEngine::_clock2Timers[] = { @@ -465,13 +465,13 @@ void EoBCoreEngine::initStaticResource() { // EOB I doesn't have load and save menus, because there is only one single // save slot. Instead of emulating this we provide a menu similiar to EOB II. - static const char *saveLoadStrings[3][4] = { + static const char *const saveLoadStrings[3][4] = { { "Cancel", "Empty Slot", "Save Game", "Load Game" }, { "Abbr.", "Leerer Slot", "Speichern", " Laden" }, { 0, 0, 0, 0 } }; - static const char *errorSlotEmptyString[3] = { + static const char *const errorSlotEmptyString[3] = { "There is no game\rsaved in that slot!", "Hier ist noch kein\rSpiel gespeichert!", 0 @@ -1096,7 +1096,7 @@ void EoBEngine::initStaticResource() { p->dmgModifierEvade = *ps++; } - static const char *errorSlotNoNameString[3] = { + static const char *const errorSlotNoNameString[3] = { " You must specify\r a name for your\r save game!", " Spielstaende mues-\r sen einen Namen\r haben!", 0 @@ -1118,10 +1118,10 @@ void EoBEngine::initSpells() { { 0x0000, 0x000000, 0x00 }, // dummy { 0x0033, 0x000001, 0x00 }, // armor { 0x0100, 0x000000, 0x21 }, // burning hands - { 0x004c, 0x000002, 0x00 }, // detect magic + { 0x004C, 0x000002, 0x00 }, // detect magic { 0x0100, 0x000000, 0x01 }, // magic missile { 0x0000, 0x000000, 0x00 }, // dummy - { 0x008b, 0x000008, 0x00 }, // shield + { 0x008B, 0x000008, 0x00 }, // shield { 0x0488, 0x000000, 0x03 }, // shocking grasp { 0x0021, 0x000040, 0x00 }, // invisibility { 0x0000, 0x000000, 0x00 }, // dummy @@ -1141,10 +1141,10 @@ void EoBEngine::initSpells() { { 0x0000, 0x000000, 0x00 }, // CLOUD KILL { 0x0100, 0x000000, 0x41 }, // cone of cold { 0x0100, 0x000000, 0x00 }, // hold monster - { 0x005c, 0x000400, 0x00 }, // bless + { 0x005C, 0x000400, 0x00 }, // bless { 0x0020, 0x000000, 0x00 }, // cure light wounds { 0x0100, 0x000000, 0x01 }, // cause light wounds - { 0x004c, 0x000002, 0x00 }, // detect magic + { 0x004C, 0x000002, 0x00 }, // detect magic { 0x0029, 0x000800, 0x00 }, // prot from evil { 0x0039, 0x000000, 0x00 }, // aid { 0x2408, 0x000000, 0x21 }, // flame blade @@ -1153,7 +1153,7 @@ void EoBEngine::initSpells() { { 0x0040, 0x000000, 0x00 }, // create food { 0x1000, 0x000000, 0x00 }, // dispel magic { 0x0099, 0x004000, 0x00 }, // magical vestment - { 0x004c, 0x008000, 0x00 }, // prayer + { 0x004C, 0x008000, 0x00 }, // prayer { 0x0040, 0x000000, 0x00 }, // remove paralysis { 0x0020, 0x000000, 0x00 }, // cure serious { 0x0100, 0x000000, 0x01 }, // cause serious @@ -1187,7 +1187,7 @@ void EoBEngine::initSpells() { const KyraRpgGUISettings EoBEngine::_guiSettingsVGA = { { 9, 15, 95, 9, 7, { 285, 139 }, { 189, 162 }, { 31, 31 } }, - { 135, 130, 132, 133, 133, 17, 23, 20, 184, 177, 180, 184, 177, 180 } + { 135, 130, 132, 133, 133, 17, 23, 20, 184, 177, 180, 184, 177, 180 } }; const KyraRpgGUISettings EoBEngine::_guiSettingsEGA = { @@ -1210,7 +1210,7 @@ void DarkMoonEngine::initStaticResource() { _animIntro[i] = _staticres->loadEoB2SeqData(kEob2IntroAnimData00 + i, temp); _shapesIntro = new const DarkMoonShapeDef*[13]; - memset(_shapesIntro, 0, sizeof(DarkMoonShapeDef*) * 13); + memset(_shapesIntro, 0, sizeof(DarkMoonShapeDef *) * 13); _shapesIntro[0] = _staticres->loadEoB2ShapeData(kEoB2IntroShapes00, temp); _shapesIntro[1] = _staticres->loadEoB2ShapeData(kEoB2IntroShapes01, temp); _shapesIntro[4] = _staticres->loadEoB2ShapeData(kEoB2IntroShapes04, temp); @@ -1225,7 +1225,7 @@ void DarkMoonEngine::initStaticResource() { _animFinale[i] = _staticres->loadEoB2SeqData(kEob2FinaleAnimData00 + i, temp); _shapesFinale = new const DarkMoonShapeDef*[13]; - memset(_shapesFinale, 0, sizeof(DarkMoonShapeDef*) * 13); + memset(_shapesFinale, 0, sizeof(DarkMoonShapeDef *) * 13); _shapesFinale[0] = _staticres->loadEoB2ShapeData(kEoB2FinaleShapes00, temp); _shapesFinale[3] = _staticres->loadEoB2ShapeData(kEoB2FinaleShapes03, temp); _shapesFinale[7] = _staticres->loadEoB2ShapeData(kEoB2FinaleShapes07, temp); @@ -1249,7 +1249,7 @@ void DarkMoonEngine::initStaticResource() { _wallOfForceDsNumH = _staticres->loadRawData(kEoB2WallOfForceNumH, temp); _wallOfForceShpId = _staticres->loadRawData(kEoB2WallOfForceShpId, temp); - static const char *errorSlotNoNameString[3] = { + static const char *const errorSlotNoNameString[3] = { " You must specify\r a name for your\r save game!", " Spielst[nde m]ssen\r einen Namen haben!", 0 @@ -1258,7 +1258,7 @@ void DarkMoonEngine::initStaticResource() { _errorSlotNoNameString = errorSlotNoNameString[(_flags.lang == Common::EN_ANY) ? 0 : ((_flags.lang == Common::DE_DEU) ? 1 : 2)]; // ScummVM specific - static const char *transferStringsScummVM[3][5] = { + static const char *const transferStringsScummVM[3][5] = { { "\r We cannot find any EOB save game\r file. Please make sure that the\r save game file with the party\r you wish to transfer is located\r in your ScummVM save game\r directory. If you have set up\r multiple save directories you\r have to copy the EOB save file\r into your EOB II save directory.\r Do you wish to try again?", "Game ID", @@ -1300,7 +1300,7 @@ void DarkMoonEngine::initSpells() { } } -const char *DarkMoonEngine::_palFilesIntroVGA[] = { +const char *const DarkMoonEngine::_palFilesIntroVGA[] = { "PALETTE1.PAL", "PALETTE3.PAL", "PALETTE2.PAL", @@ -1308,7 +1308,7 @@ const char *DarkMoonEngine::_palFilesIntroVGA[] = { 0 }; -const char *DarkMoonEngine::_palFilesIntroEGA[] = { +const char *const DarkMoonEngine::_palFilesIntroEGA[] = { "PALETTE0.PAL", "PALETTE3.PAL", "PALETTE2.PAL", @@ -1316,7 +1316,7 @@ const char *DarkMoonEngine::_palFilesIntroEGA[] = { 0 }; -const char *DarkMoonEngine::_palFilesFinaleVGA[] = { +const char *const DarkMoonEngine::_palFilesFinaleVGA[] = { "FINALE_0.PAL", "FINALE_0.PAL", "FINALE_1.PAL", @@ -1329,7 +1329,7 @@ const char *DarkMoonEngine::_palFilesFinaleVGA[] = { 0 }; -const char *DarkMoonEngine::_palFilesFinaleEGA[] = { +const char *const DarkMoonEngine::_palFilesFinaleEGA[] = { "FINALE_0.PAL", "FINALE_0.PAL", "FINALE_1.PAL", diff --git a/engines/kyra/staticres_lol.cpp b/engines/kyra/staticres_lol.cpp index 63bc7fa99b..a1c5ff340c 100644 --- a/engines/kyra/staticres_lol.cpp +++ b/engines/kyra/staticres_lol.cpp @@ -24,6 +24,7 @@ #include "kyra/lol.h" #include "kyra/screen_lol.h" #include "kyra/gui_lol.h" +#include "kyra/sound_intern.h" #ifdef ENABLE_LOL @@ -213,30 +214,39 @@ void StaticResource::freeButtonDefs(void *&ptr, int &size) { } void LoLEngine::initStaticResource() { - // assign music data - static const char *const pcMusicFileListIntro[] = { "LOREINTR" }; - static const char *const pcMusicFileListFinale[] = { "LOREFINL" }; - static const char *const pcMusicFileListIngame[] = { "LORE%02d%c" }; - - static const char *const pc98MusicFileListIntro[] = { 0, "lore84.86", "lore82.86", 0, 0, 0, "lore83.86", "lore81.86" }; - static const char *const pc98MusicFileListFinale[] = { 0, 0, "lore85.86", "lore86.86", "lore87.86" }; - static const char *const pc98MusicFileListIngame[] = { "lore%02d.86" }; - - memset(_soundData, 0, sizeof(_soundData)); + // assign music resource data. if (_flags.platform == Common::kPlatformPC) { - _soundData[0].fileList = pcMusicFileListIntro; - _soundData[0].fileListLen = ARRAYSIZE(pcMusicFileListIntro); - _soundData[1].fileList = pcMusicFileListIngame; - _soundData[1].fileListLen = ARRAYSIZE(pcMusicFileListIngame); - _soundData[2].fileList = pcMusicFileListFinale; - _soundData[2].fileListLen = ARRAYSIZE(pcMusicFileListFinale); + if (_flags.isDemo) { + static const char *const file[] = { "LOREDEMO" }; + SoundResourceInfo_PC resInfoDemo(file, ARRAYSIZE(file)); + _sound->initAudioResourceInfo(kMusicIntro, &resInfoDemo); + } else { + static const char *const intro[] = { "LOREINTR" }; + static const char *const finale[] = { "LOREFINL" }; + SoundResourceInfo_PC resInfoIntro(intro, ARRAYSIZE(intro)); + SoundResourceInfo_PC resInfoFinale(finale, ARRAYSIZE(finale)); + _sound->initAudioResourceInfo(kMusicIntro, &resInfoIntro); + // In game music file handling is different, thus does not need a file list. + _sound->initAudioResourceInfo(kMusicFinale, &resInfoFinale); + } } else if (_flags.platform == Common::kPlatformPC98) { - _soundData[0].fileList = pc98MusicFileListIntro; - _soundData[0].fileListLen = ARRAYSIZE(pc98MusicFileListIntro); - _soundData[1].fileList = pc98MusicFileListIngame; - _soundData[1].fileListLen = ARRAYSIZE(pc98MusicFileListIngame); - _soundData[2].fileList = pc98MusicFileListFinale; - _soundData[2].fileListLen = ARRAYSIZE(pc98MusicFileListFinale); + static const char *const fileListIntro[] = { 0, "lore84.86", "lore82.86", 0, 0, 0, "lore83.86", "lore81.86" }; + static const char *const fileListFinale[] = { 0, 0, "lore85.86", "lore86.86", "lore87.86" }; + SoundResourceInfo_TownsPC98V2 resInfoIntro(fileListIntro, ARRAYSIZE(fileListIntro), 0, 0, 0); + SoundResourceInfo_TownsPC98V2 resInfoIngame(0, 0, "lore%02d.86", 0, 0); + SoundResourceInfo_TownsPC98V2 resInfoFinale(fileListFinale, ARRAYSIZE(fileListFinale), 0, 0, 0); + _sound->initAudioResourceInfo(kMusicIntro, &resInfoIntro); + _sound->initAudioResourceInfo(kMusicIngame, &resInfoIngame); + _sound->initAudioResourceInfo(kMusicFinale, &resInfoFinale); + } else if (_flags.platform == Common::kPlatformFMTowns) { + static const char *const fileListIntro[] = { 0, "lore84.twn", "lore82.twn", 0, 0, 0, "lore83.twn", "lore81.twn" }; + static const char *const fileListFinale[] = { 0, 0, "lore85.twn", "lore86.twn", "lore87.twn" }; + SoundResourceInfo_TownsPC98V2 resInfoIntro(fileListIntro, ARRAYSIZE(fileListIntro), 0, 0, 0); + SoundResourceInfo_TownsPC98V2 resInfoIngame(0, 0, "lore%02d.twn", 0, 0); + SoundResourceInfo_TownsPC98V2 resInfoFinale(fileListFinale, ARRAYSIZE(fileListFinale), 0, 0, 0); + _sound->initAudioResourceInfo(kMusicIntro, &resInfoIntro); + _sound->initAudioResourceInfo(kMusicIngame, &resInfoIngame); + _sound->initAudioResourceInfo(kMusicFinale, &resInfoFinale); } if (_flags.isDemo) @@ -260,7 +270,10 @@ void LoLEngine::initStaticResource() { _charDefsKieran = _staticres->loadRawDataBe16(kLoLCharDefsKieran, tempSize); _charDefsAkshel = _staticres->loadRawDataBe16(kLoLCharDefsAkshel, tempSize); _expRequirements = (const int32 *)_staticres->loadRawDataBe32(kLoLExpRequirements, tempSize); - _monsterModifiers = _staticres->loadRawDataBe16(kLoLMonsterModifiers, tempSize); + _monsterModifiers1 = _staticres->loadRawDataBe16(kLoLMonsterModifiers1, tempSize); + _monsterModifiers2 = _staticres->loadRawDataBe16(kLoLMonsterModifiers2, tempSize); + _monsterModifiers3 = _staticres->loadRawDataBe16(kLoLMonsterModifiers3, tempSize); + _monsterModifiers4 = _staticres->loadRawDataBe16(kLoLMonsterModifiers4, tempSize); _monsterShiftOffs = (const int8 *)_staticres->loadRawData(kLoLMonsterShiftOffsets, tempSize); _monsterDirFlags = _staticres->loadRawData(kLoLMonsterDirFlags, tempSize); _monsterScaleX = _staticres->loadRawData(kLoLMonsterScaleX, tempSize); @@ -303,14 +316,14 @@ void LoLEngine::initStaticResource() { } _buttonData = _staticres->loadButtonDefs(kLoLButtonDefs, tempSize); - _buttonList1 = (const int16 *)_staticres->loadRawDataBe16(kLoLButtonList1, tempSize); - _buttonList2 = (const int16 *)_staticres->loadRawDataBe16(kLoLButtonList2, tempSize); - _buttonList3 = (const int16 *)_staticres->loadRawDataBe16(kLoLButtonList3, tempSize); - _buttonList4 = (const int16 *)_staticres->loadRawDataBe16(kLoLButtonList4, tempSize); - _buttonList5 = (const int16 *)_staticres->loadRawDataBe16(kLoLButtonList5, tempSize); - _buttonList6 = (const int16 *)_staticres->loadRawDataBe16(kLoLButtonList6, tempSize); - _buttonList7 = (const int16 *)_staticres->loadRawDataBe16(kLoLButtonList7, tempSize); - _buttonList8 = (const int16 *)_staticres->loadRawDataBe16(kLoLButtonList8, tempSize); + _buttonList1 = _staticres->loadRawData(kLoLButtonList1, tempSize); + _buttonList2 = _staticres->loadRawData(kLoLButtonList2, tempSize); + _buttonList3 = _staticres->loadRawData(kLoLButtonList3, tempSize); + _buttonList4 = _staticres->loadRawData(kLoLButtonList4, tempSize); + _buttonList5 = _staticres->loadRawData(kLoLButtonList5, tempSize); + _buttonList6 = _staticres->loadRawData(kLoLButtonList6, tempSize); + _buttonList7 = _staticres->loadRawData(kLoLButtonList7, tempSize); + _buttonList8 = _staticres->loadRawData(kLoLButtonList8, tempSize); _autoMapStrings = _staticres->loadRawDataBe16(kLoLMapStringId, tempSize); @@ -457,11 +470,11 @@ void LoLEngine::initStaticResource() { } void GUI_LoL::initStaticData() { - GUI_V2_BUTTON(_scrollUpButton, 20, 96, 0, 1, 1, 1, 0x4487, 0, 0, 0, 25, 16, 0xfe, 0x01, 0xfe, 0x01, 0xfe, 0x01, 0); - GUI_V2_BUTTON(_scrollDownButton, 21, 98, 0, 1, 1, 1, 0x4487, 0, 0, 0, 25, 16, 0xfe, 0x01, 0xfe, 0x01, 0xfe, 0x01, 0); + GUI_V2_BUTTON(_scrollUpButton, 20, 96, 0, 1, 1, 1, 0x4487, 0, 0, 0, 25, 16, 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01, 0); + GUI_V2_BUTTON(_scrollDownButton, 21, 98, 0, 1, 1, 1, 0x4487, 0, 0, 0, 25, 16, 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01, 0); for (uint i = 0; i < ARRAYSIZE(_menuButtons); ++i) - GUI_V2_BUTTON(_menuButtons[i], i, 0, 0, 0, 0, 0, 0x4487, 0, 0, 0, 0, 0, 0xfe, 0x01, 0xfe, 0x01, 0xfe, 0x01, 0); + GUI_V2_BUTTON(_menuButtons[i], i, 0, 0, 0, 0, 0, 0x4487, 0, 0, 0, 0, 0, 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01, 0); if (_vm->gameFlags().isTalkie) GUI_LOL_MENU(_mainMenu, 9, 0x4000, 0, 7, -1, -1, -1, -1); @@ -486,61 +499,61 @@ void GUI_LoL::initStaticData() { for (int i = 0; i < _mainMenu.numberOfItems; ++i) _mainMenu.item[i].callback = mainMenuFunctor; - GUI_LOL_MENU(_loadMenu, 10, 0x400e, 1, 5, 128, 20, 128, 118); - GUI_LOL_MENU_ITEM(_loadMenu.item[0], 0xfffe, 8, 39, 256, 15, 0, 0); - GUI_LOL_MENU_ITEM(_loadMenu.item[1], 0xfffd, 8, 56, 256, 15, 0, 0); - GUI_LOL_MENU_ITEM(_loadMenu.item[2], 0xfffc, 8, 73, 256, 15, 0, 0); - GUI_LOL_MENU_ITEM(_loadMenu.item[3], 0xfffb, 8, 90, 256, 15, 0, 0); + GUI_LOL_MENU(_loadMenu, 10, 0x400E, 1, 5, 128, 20, 128, 118); + GUI_LOL_MENU_ITEM(_loadMenu.item[0], 0xFFFE, 8, 39, 256, 15, 0, 0); + GUI_LOL_MENU_ITEM(_loadMenu.item[1], 0xFFFD, 8, 56, 256, 15, 0, 0); + GUI_LOL_MENU_ITEM(_loadMenu.item[2], 0xFFFC, 8, 73, 256, 15, 0, 0); + GUI_LOL_MENU_ITEM(_loadMenu.item[3], 0xFFFB, 8, 90, 256, 15, 0, 0); GUI_LOL_MENU_ITEM(_loadMenu.item[4], 0x4011, 168, 118, 96, 15, 0, _vm->_keyMap[Common::KEYCODE_ESCAPE]); Button::Callback loadMenuFunctor = BUTTON_FUNCTOR(GUI_LoL, this, &GUI_LoL::clickedLoadMenu); for (int i = 0; i < 5; ++i) _loadMenu.item[i].callback = loadMenuFunctor; - GUI_LOL_MENU(_saveMenu, 10, 0x400d, 1, 5, 128, 20, 128, 118); - GUI_LOL_MENU_ITEM(_saveMenu.item[0], 0xfffe, 8, 39, 256, 15, 0, 0); - GUI_LOL_MENU_ITEM(_saveMenu.item[1], 0xfffd, 8, 56, 256, 15, 0, 0); - GUI_LOL_MENU_ITEM(_saveMenu.item[2], 0xfffc, 8, 73, 256, 15, 0, 0); - GUI_LOL_MENU_ITEM(_saveMenu.item[3], 0xfffb, 8, 90, 256, 15, 0, 0); + GUI_LOL_MENU(_saveMenu, 10, 0x400D, 1, 5, 128, 20, 128, 118); + GUI_LOL_MENU_ITEM(_saveMenu.item[0], 0xFFFE, 8, 39, 256, 15, 0, 0); + GUI_LOL_MENU_ITEM(_saveMenu.item[1], 0xFFFD, 8, 56, 256, 15, 0, 0); + GUI_LOL_MENU_ITEM(_saveMenu.item[2], 0xFFFC, 8, 73, 256, 15, 0, 0); + GUI_LOL_MENU_ITEM(_saveMenu.item[3], 0xFFFB, 8, 90, 256, 15, 0, 0); GUI_LOL_MENU_ITEM(_saveMenu.item[4], 0x4011, 168, 118, 96, 15, 0, _vm->_keyMap[Common::KEYCODE_ESCAPE]); Button::Callback saveMenuFunctor = BUTTON_FUNCTOR(GUI_LoL, this, &GUI_LoL::clickedSaveMenu); for (int i = 0; i < 5; ++i) _saveMenu.item[i].callback = saveMenuFunctor; - GUI_LOL_MENU(_deleteMenu, 10, 0x400f, 1, 5, 128, 20, 128, 118); - GUI_LOL_MENU_ITEM(_deleteMenu.item[0], 0xfffe, 8, 39, 256, 15, 0, 0); - GUI_LOL_MENU_ITEM(_deleteMenu.item[1], 0xfffd, 8, 56, 256, 15, 0, 0); - GUI_LOL_MENU_ITEM(_deleteMenu.item[2], 0xfffc, 8, 73, 256, 15, 0, 0); - GUI_LOL_MENU_ITEM(_deleteMenu.item[3], 0xfffb, 8, 90, 256, 15, 0, 0); + GUI_LOL_MENU(_deleteMenu, 10, 0x400F, 1, 5, 128, 20, 128, 118); + GUI_LOL_MENU_ITEM(_deleteMenu.item[0], 0xFFFE, 8, 39, 256, 15, 0, 0); + GUI_LOL_MENU_ITEM(_deleteMenu.item[1], 0xFFFD, 8, 56, 256, 15, 0, 0); + GUI_LOL_MENU_ITEM(_deleteMenu.item[2], 0xFFFC, 8, 73, 256, 15, 0, 0); + GUI_LOL_MENU_ITEM(_deleteMenu.item[3], 0xFFFB, 8, 90, 256, 15, 0, 0); GUI_LOL_MENU_ITEM(_deleteMenu.item[4], 0x4011, 168, 118, 96, 15, 0, _vm->_keyMap[Common::KEYCODE_ESCAPE]); Button::Callback deleteMenuFunctor = BUTTON_FUNCTOR(GUI_LoL, this, &GUI_LoL::clickedDeleteMenu); for (int i = 0; i < 5; ++i) _deleteMenu.item[i].callback = deleteMenuFunctor; - GUI_LOL_MENU(_gameOptions, 17, 0x400c, 0, 6, -1, -1, -1, -1); + GUI_LOL_MENU(_gameOptions, 17, 0x400C, 0, 6, -1, -1, -1, -1); if (_vm->gameFlags().isTalkie) { - GUI_LOL_MENU_ITEM(_gameOptions.item[0], 0xfff7, 120, 22, 80, 15, 0x406e, 0); - GUI_LOL_MENU_ITEM(_gameOptions.item[1], 0xfff6, 120, 39, 80, 15, 0x406c, 0); - GUI_LOL_MENU_ITEM(_gameOptions.item[2], 0xfff5, 120, 56, 80, 15, 0x406d, 0); - GUI_LOL_MENU_ITEM(_gameOptions.item[3], 0xfff4, 120, 73, 80, 15, 0x42d5, 0); - GUI_LOL_MENU_ITEM(_gameOptions.item[4], 0xfff3, 120, 90, 80, 15, 0x42d2, 0); + GUI_LOL_MENU_ITEM(_gameOptions.item[0], 0xFFF7, 120, 22, 80, 15, 0x406E, 0); + GUI_LOL_MENU_ITEM(_gameOptions.item[1], 0xFFF6, 120, 39, 80, 15, 0x406C, 0); + GUI_LOL_MENU_ITEM(_gameOptions.item[2], 0xFFF5, 120, 56, 80, 15, 0x406D, 0); + GUI_LOL_MENU_ITEM(_gameOptions.item[3], 0xFFF4, 120, 73, 80, 15, 0x42D5, 0); + GUI_LOL_MENU_ITEM(_gameOptions.item[4], 0xFFF3, 120, 90, 80, 15, 0x42D2, 0); GUI_LOL_MENU_ITEM(_gameOptions.item[5], 0x4072, 104, 110, 96, 15, 0, _vm->_keyMap[Common::KEYCODE_ESCAPE]); } else { - GUI_LOL_MENU_ITEM(_gameOptions.item[0], 0xfff9, 120, 22, 80, 15, 0x406a, 0); - GUI_LOL_MENU_ITEM(_gameOptions.item[1], 0xfff8, 120, 39, 80, 15, 0x406b, 0); - GUI_LOL_MENU_ITEM(_gameOptions.item[2], 0xfff7, 120, 56, 80, 15, 0x406e, 0); - GUI_LOL_MENU_ITEM(_gameOptions.item[3], 0xfff6, 120, 73, 80, 15, 0x406c, 0); - GUI_LOL_MENU_ITEM(_gameOptions.item[4], 0xfff5, 120, 90, 80, 15, 0x406d, 0); + GUI_LOL_MENU_ITEM(_gameOptions.item[0], 0xFFF9, 120, 22, 80, 15, 0x406A, 0); + GUI_LOL_MENU_ITEM(_gameOptions.item[1], 0xFFF8, 120, 39, 80, 15, 0x406B, 0); + GUI_LOL_MENU_ITEM(_gameOptions.item[2], 0xFFF7, 120, 56, 80, 15, 0x406E, 0); + GUI_LOL_MENU_ITEM(_gameOptions.item[3], 0xFFF6, 120, 73, 80, 15, 0x406C, 0); + GUI_LOL_MENU_ITEM(_gameOptions.item[4], 0xFFF5, 120, 90, 80, 15, 0x406D, 0); GUI_LOL_MENU_ITEM(_gameOptions.item[5], 0x4072, 104, 110, 96, 15, 0, _vm->_keyMap[Common::KEYCODE_ESCAPE]); } Button::Callback optionsMenuFunctor = BUTTON_FUNCTOR(GUI_LoL, this, &GUI_LoL::clickedOptionsMenu); for (int i = 0; i < _gameOptions.numberOfItems; ++i) _gameOptions.item[i].callback = optionsMenuFunctor; - GUI_LOL_MENU(_audioOptions, 18, 0x42d9, 2, 1, -1, -1, -1, -1); + GUI_LOL_MENU(_audioOptions, 18, 0x42D9, 2, 1, -1, -1, -1, -1); GUI_LOL_MENU_ITEM(_audioOptions.item[0], 0x4072, 152, 76, 96, 15, 0, _vm->_keyMap[Common::KEYCODE_ESCAPE]); - GUI_LOL_MENU_ITEM(_audioOptions.item[1], 3, 128, 22, 114, 14, 0x42db, 0); - GUI_LOL_MENU_ITEM(_audioOptions.item[2], 4, 128, 39, 114, 14, 0x42da, 0); - GUI_LOL_MENU_ITEM(_audioOptions.item[3], 5, 128, 56, 114, 14, 0x42dc, 0); + GUI_LOL_MENU_ITEM(_audioOptions.item[1], 3, 128, 22, 114, 14, 0x42DB, 0); + GUI_LOL_MENU_ITEM(_audioOptions.item[2], 4, 128, 39, 114, 14, 0x42DA, 0); + GUI_LOL_MENU_ITEM(_audioOptions.item[3], 5, 128, 56, 114, 14, 0x42DC, 0); Button::Callback audioMenuFunctor = BUTTON_FUNCTOR(GUI_LoL, this, &GUI_LoL::clickedAudioMenu); for (int i = 0; i < 4; ++i) _audioOptions.item[i].callback = audioMenuFunctor; @@ -658,11 +671,11 @@ const uint16 LoLEngine::_charPosXPC98[] = { 92, 152, 212, 268 }; -const uint8 LoLEngine::_charNamesPC98[][11] = { - { 0x83, 0x41, 0x83, 0x4E, 0x83, 0x56, 0x83, 0x46, 0x83, 0x8B, 0x00 }, - { 0x83, 0x7D, 0x83, 0x43, 0x83, 0x50, 0x83, 0x8B, 0x00, 0x00, 0x00 }, - { 0x83, 0x4C, 0x81, 0x5B, 0x83, 0x89, 0x83, 0x93, 0x00, 0x00, 0x00 }, - { 0x83, 0x52, 0x83, 0x93, 0x83, 0x89, 0x83, 0x62, 0x83, 0x68, 0x00 } +const char *const LoLEngine::_charNamesJapanese[] = { + "\x83\x41\x83\x4E\x83\x56\x83\x46\x83\x8B\0", + "\x83\x7D\x83\x43\x83\x50\x83\x8B\x00\x00\0", + "\x83\x4C\x81\x5B\x83\x89\x83\x93\x00\x00\0", + "\x83\x52\x83\x93\x83\x89\x83\x62\x83\x68\0" }; const uint8 LoLEngine::_chargenFrameTableTalkie[] = { diff --git a/engines/kyra/text_hof.cpp b/engines/kyra/text_hof.cpp index 06067d6693..7fa823da0f 100644 --- a/engines/kyra/text_hof.cpp +++ b/engines/kyra/text_hof.cpp @@ -464,9 +464,9 @@ void KyraEngine_HoF::processDialogue(int dlgOffset, int vocH, int csEntry) { nextTimSequence = READ_LE_UINT16(&_ingameTalkObjIndex[cmd]); if (nextTimSequence == 10) { - if (queryGameFlag(0x3e)) + if (queryGameFlag(0x3E)) nextTimSequence = 14; - if (queryGameFlag(0x3f)) + if (queryGameFlag(0x3F)) nextTimSequence = 15; if (queryGameFlag(0x40)) nextTimSequence = 16; diff --git a/engines/kyra/text_lol.cpp b/engines/kyra/text_lol.cpp index ee42d6db92..6e77db1f8a 100644 --- a/engines/kyra/text_lol.cpp +++ b/engines/kyra/text_lol.cpp @@ -85,7 +85,7 @@ void TextDisplayer_LoL::setupField(bool mode) { _screen->copyBlockToPage(3, 0, 0, 320, 200, _vm->_pageBuffer1); _screen->setCurPage(cp); - _vm->_updateFlags &= 0xfffd; + _vm->_updateFlags &= 0xFFFD; } } else { if (!mode) @@ -136,18 +136,16 @@ void TextDisplayer_LoL::expandField() { void TextDisplayer_LoL::printDialogueText(int dim, char *str, EMCState *script, const uint16 *paramList, int16 paramIndex) { int oldDim = 0; - const bool isPc98 = (_vm->gameFlags().platform == Common::kPlatformPC98); - if (dim == 3) { if (_vm->_updateFlags & 2) { oldDim = clearDim(4); - _textDimData[4].color1 = isPc98 ? 0x33 : 254; + _textDimData[4].color1 = _vm->gameFlags().use16ColorMode ? 0x33 : 254; _textDimData[4].color2 = _screen->_curDim->unkA; } else { oldDim = clearDim(3); - _textDimData[3].color1 = isPc98 ? 0x33 : 192; + _textDimData[3].color1 = _vm->gameFlags().use16ColorMode ? 0x33 : 192; _textDimData[3].color2 = _screen->_curDim->unkA; - if (!isPc98) + if (!_vm->gameFlags().use16ColorMode) _screen->copyColor(192, 254); _vm->enableTimer(11); _vm->_textColorFlag = 0; @@ -157,12 +155,12 @@ void TextDisplayer_LoL::printDialogueText(int dim, char *str, EMCState *script, oldDim = _screen->curDimIndex(); _screen->setScreenDim(dim); _lineCount = 0; - _textDimData[dim].color1 = isPc98 ? 0x33 : 254; + _textDimData[dim].color1 = _vm->gameFlags().use16ColorMode ? 0x33 : 254; _textDimData[dim].color2 = _screen->_curDim->unkA; } int cp = _screen->setCurPage(0); - Screen::FontId of = _screen->setFont(_vm->gameFlags().use16ColorMode ? Screen::FID_SJIS_FNT : Screen::FID_9_FNT); + Screen::FontId of = _screen->setFont((_vm->gameFlags().lang == Common::JA_JPN && _vm->gameFlags().use16ColorMode) ? Screen::FID_SJIS_FNT : Screen::FID_9_FNT); preprocessString(str, script, paramList, paramIndex); _numCharsTotal = strlen(_dialogueBuffer); @@ -176,8 +174,8 @@ void TextDisplayer_LoL::printDialogueText(int dim, char *str, EMCState *script, } void TextDisplayer_LoL::printMessage(uint16 type, const char *str, ...) { - static const uint8 textColors256[] = { 0xfe, 0xa2, 0x84, 0x97, 0x9F }; - static const uint8 textColors16[] = { 0x33, 0xaa, 0x88, 0x55, 0x99 }; + static const uint8 textColors256[] = { 0xFE, 0xA2, 0x84, 0x97, 0x9F }; + static const uint8 textColors16[] = { 0x33, 0xAA, 0x88, 0x55, 0x99 }; static const uint8 soundEffect[] = { 0x0B, 0x00, 0x2B, 0x1B, 0x00 }; const uint8 *textColors = _vm->gameFlags().use16ColorMode ? textColors16 : textColors256; @@ -187,7 +185,7 @@ void TextDisplayer_LoL::printMessage(uint16 type, const char *str, ...) { else _vm->stopPortraitSpeechAnim(); - uint16 col = textColors[type & 0x7fff]; + uint16 col = textColors[type & 0x7FFF]; int od = _screen->curDimIndex(); @@ -222,16 +220,15 @@ void TextDisplayer_LoL::printMessage(uint16 type, const char *str, ...) { _vm->sound()->playSoundEffect(soundEffect[type]); } - _vm->_textColorFlag = type & 0x7fff; + _vm->_textColorFlag = type & 0x7FFF; _vm->_fadeText = false; } void TextDisplayer_LoL::preprocessString(char *str, EMCState *script, const uint16 *paramList, int16 paramIndex) { char *dst = _dialogueBuffer; - const bool isPc98 = (_vm->gameFlags().platform == Common::kPlatformPC98); for (char *s = str; *s;) { - if (isPc98) { + if (_vm->gameFlags().lang == Common::JA_JPN) { uint8 c = *s; if (c >= 0xE0 || (c > 0x80 && c < 0xA0)) { *dst++ = *s++; diff --git a/engines/kyra/text_rpg.cpp b/engines/kyra/text_rpg.cpp index 52c14c7223..a19d678e35 100644 --- a/engines/kyra/text_rpg.cpp +++ b/engines/kyra/text_rpg.cpp @@ -36,7 +36,7 @@ enum { TextDisplayer_rpg::TextDisplayer_rpg(KyraRpgEngine *engine, Screen *scr) : _vm(engine), _screen(scr), _lineCount(0), _printFlag(false), _lineWidth(0), _numCharsTotal(0), _allowPageBreak(true), - _numCharsLeft(0), _numCharsPrinted(0), _sjisLineBreakFlag(false), _waitButtonMode(1) { + _numCharsLeft(0), _numCharsPrinted(0), _sjisTextModeLineBreak(false), _waitButtonMode(1) { _dialogueBuffer = new char[kEoBTextBufferSize]; memset(_dialogueBuffer, 0, kEoBTextBufferSize); @@ -88,8 +88,8 @@ void TextDisplayer_rpg::resetDimTextPositions(int dim) { } void TextDisplayer_rpg::resetPageBreakString() { - if (vm()->_moreStrings) - strcpy(_pageBreakString, vm()->_moreStrings[0]); + if (_vm->_moreStrings) + strcpy(_pageBreakString, _vm->_moreStrings[0]); } void TextDisplayer_rpg::setPageBreakFlag() { @@ -102,8 +102,6 @@ void TextDisplayer_rpg::removePageBreakFlag() { } void TextDisplayer_rpg::displayText(char *str, ...) { - const bool isPc98 = (_vm->gameFlags().platform == Common::kPlatformPC98); - _printFlag = false; _lineWidth = 0; @@ -125,7 +123,9 @@ void TextDisplayer_rpg::displayText(char *str, ...) { const ScreenDim *sd = _screen->_curDim; int sdx = _screen->curDimIndex(); - bool pc98PrintFlag = (isPc98 && (sdx == 3 || sdx == 4 || sdx == 5 || sdx == 15)) ? true : false; + bool sjisTextMode = (_vm->gameFlags().lang == Common::JA_JPN && _vm->gameFlags().use16ColorMode && (sdx == 3 || sdx == 4 || sdx == 5 || sdx == 15)) ? true : false; + int sjisOffs = sjisTextMode ? 8 : 9; + uint16 charsPerLine = (sd->w << 3) / (_screen->getFontWidth() + _screen->_charWidth); while (c) { @@ -146,15 +146,25 @@ void TextDisplayer_rpg::displayText(char *str, ...) { c = parseCommand(); } - if (isPc98) { + if (_vm->gameFlags().lang == Common::JA_JPN) { uint8 cu = (uint8) c; if (cu >= 0xE0 || (cu > 0x80 && cu < 0xA0)) { - _currentLine[_numCharsLeft++] = c; - _currentLine[_numCharsLeft++] = parseCommand(); - _currentLine[_numCharsLeft] = '\0'; - _lineWidth += 8; - if ((_textDimData[sdx].column + _lineWidth) > (sd->w << 3)) + if (sjisTextMode) { + _currentLine[_numCharsLeft++] = c; + _currentLine[_numCharsLeft++] = parseCommand(); + _currentLine[_numCharsLeft] = '\0'; + } + + if ((_textDimData[sdx].column + _lineWidth + sjisOffs) > (sd->w << 3)) printLine(_currentLine); + + if (!sjisTextMode) { + _currentLine[_numCharsLeft++] = c; + _currentLine[_numCharsLeft++] = parseCommand(); + _currentLine[_numCharsLeft] = '\0'; + } + + _lineWidth += sjisOffs; c = parseCommand(); continue; } @@ -182,17 +192,17 @@ void TextDisplayer_rpg::displayText(char *str, ...) { case 8: printLine(_currentLine); dv = _textDimData[sdx].column / (_screen->getFontWidth() + _screen->_charWidth); - dv = ((dv + 8) & 0xfff8) - 1; + dv = ((dv + 8) & 0xFFF8) - 1; if (dv >= charsPerLine) dv = 0; _textDimData[sdx].column = (_screen->getFontWidth() + _screen->_charWidth) * dv; break; case 12: - if (isPc98) - _sjisLineBreakFlag = true; + if (sjisTextMode) + _sjisTextModeLineBreak = true; printLine(_currentLine); - _sjisLineBreakFlag = false; + _sjisTextModeLineBreak = false; _lineCount++; _textDimData[sdx].column = 0; _textDimData[sdx].line++; @@ -208,7 +218,7 @@ void TextDisplayer_rpg::displayText(char *str, ...) { default: if (_vm->game() == GI_LOL || (unsigned char)c > 30) { - _lineWidth += (pc98PrintFlag ? 4 : _screen->getCharWidth((uint8)c)); + _lineWidth += (sjisTextMode ? 4 : (_screen->_currentFont == Screen::FID_SJIS_FNT ? 9 : _screen->getCharWidth((uint8)c))); _currentLine[_numCharsLeft++] = c; _currentLine[_numCharsLeft] = 0; @@ -266,7 +276,7 @@ void TextDisplayer_rpg::readNextPara() { // versions depend on this code we'll have to look at this again. #if 0 if ((_vm->game() != GI_LOL) && (d & 0x80)) { - d &= 0x7f; + d &= 0x7F; c = d & 7; d = (d & 0x78) >> 3; uint8 l = d; @@ -280,10 +290,9 @@ void TextDisplayer_rpg::readNextPara() { } void TextDisplayer_rpg::printLine(char *str) { - const bool isPc98 = (_vm->gameFlags().platform == Common::kPlatformPC98); const ScreenDim *sd = _screen->_curDim; int sdx = _screen->curDimIndex(); - bool pc98PrintFlag = (isPc98 && (sdx == 3 || sdx == 4 || sdx == 5 || sdx == 15)) ? true : false; + bool sjisTextMode = (_vm->gameFlags().lang == Common::JA_JPN && _vm->gameFlags().use16ColorMode && (sdx == 3 || sdx == 4 || sdx == 5 || sdx == 15)) ? true : false; int fh = (_screen->_currentFont == Screen::FID_SJIS_FNT) ? 9 : (_screen->getFontHeight() + _screen->_charOffset); int lines = (sd->h - _screen->_charOffset) / fh; @@ -307,25 +316,27 @@ void TextDisplayer_rpg::printLine(char *str) { } int x1 = (sd->sx << 3) + _textDimData[sdx].column; - int y = sd->sy + (pc98PrintFlag ? (_textDimData[sdx].line << 3) : (fh * _textDimData[sdx].line)); + int y = sd->sy + (sjisTextMode ? (_textDimData[sdx].line << 3) : (fh * _textDimData[sdx].line)); int w = sd->w << 3; int lw = _lineWidth; int s = _numCharsLeft; char c = 0; + uint8 twoByteCharOffs = 0; - if (pc98PrintFlag) { + + if (sjisTextMode) { bool ct = true; if ((lw + _textDimData[sdx].column) > w) { if ((lines - 1 - (_waitButtonSpace << 1)) <= _lineCount) // cut off line to leave space for "MORE" button - w -= vm()->guiSettings()->buttons.waitReserve; + w -= _vm->guiSettings()->buttons.waitReserve; } else { - if (!_sjisLineBreakFlag || (_lineCount + 1 < lines - 1)) + if (!_sjisTextModeLineBreak || (_lineCount + 1 < lines - 1)) ct = false; else // cut off line to leave space for "MORE" button - w -= vm()->guiSettings()->buttons.waitReserve; + w -= _vm->guiSettings()->buttons.waitReserve; } if (ct) { @@ -344,41 +355,88 @@ void TextDisplayer_rpg::printLine(char *str) { s = n2; } } else { - if ((lw + _textDimData[sdx].column) > w) { + if (_vm->gameFlags().lang == Common::JA_JPN) { + for (int i = 0; i < s; ++i) { + uint8 cu = (uint8) str[i]; + if (cu >= 0xE0 || (cu > 0x80 && cu < 0xA0)) + twoByteCharOffs = 8; + } + } + + if ((lw + _textDimData[sdx].column) >= w) { if ((lines - 1) <= _lineCount && _allowPageBreak) // cut off line to leave space for "MORE" button - w -= vm()->guiSettings()->buttons.waitReserve; + w -= _vm->guiSettings()->buttons.waitReserve; w -= _textDimData[sdx].column; - int n2 = 0; - int n1 = s - 1; + int lineLastCharPos = 0; + int strPos = s - 1; + + if (twoByteCharOffs) { + lw = 0; + int prevStrPos = 0; + c = str[0]; + + for (strPos = 0; strPos < s; ++strPos) { + uint8 cu = (uint8) str[strPos]; + if (cu >= 0xE0 || (cu > 0x80 && cu < 0xA0)) { + lw += 9; + strPos++; + } else { + lw += _screen->getCharWidth((uint8)c); + } + + if (!lineLastCharPos && w < lw + twoByteCharOffs) + lineLastCharPos = prevStrPos; + + if (lineLastCharPos && c == ' ') { + s = strPos; + _printFlag = false; + break; + } + prevStrPos = strPos; + c = (char) cu; + } + + if (!lineLastCharPos) { + lineLastCharPos = s - 1; + if (lineLastCharPos && str[lineLastCharPos] == ' ') { + s = strPos; + _printFlag = false; + } + } - while (n1 > 0) { - //cut off line after last space - c = str[n1]; + lw = _lineWidth; - lw -= _screen->getCharWidth((uint8)c); + } else { + while (strPos > 0) { + //cut off line after last space + c = str[strPos]; - if (!n2 && lw <= w) - n2 = n1; + lw -= _screen->getCharWidth((uint8)c); - if (n2 && c == ' ') { - s = n1; - _printFlag = false; - break; + if (!lineLastCharPos && lw <= w) + lineLastCharPos = strPos; + + if (lineLastCharPos && c == ' ') { + s = strPos; + _printFlag = false; + break; + } + strPos--; } - n1--; } - if (!n1) { + if (!strPos) { if (_textDimData[sdx].column && !_printFlag) { s = lw = 0; _printFlag = true; } else { - s = n2; + s = lineLastCharPos; } } + } } @@ -386,7 +444,7 @@ void TextDisplayer_rpg::printLine(char *str) { str[s] = 0; uint8 col = _textDimData[sdx].color1; - if (isPc98 && (sdx == 2 || sdx == 3 || sdx == 4 || sdx == 5 || sdx == 15)) { + if (sjisTextMode && (sdx == 2 || sdx == 3 || sdx == 4 || sdx == 5 || sdx == 15)) { switch (_textDimData[sdx].color1) { case 0x88: col = 0x41; @@ -394,14 +452,14 @@ void TextDisplayer_rpg::printLine(char *str) { case 0x55: col = 0x81; break; - case 0xaa: + case 0xAA: col = 0x21; break; case 0x99: - col = 0xa1; + col = 0xA1; break; case 0x33: - col = 0xe1; + col = 0xE1; break; case 0x18: col = 0x61; @@ -413,6 +471,7 @@ void TextDisplayer_rpg::printLine(char *str) { _screen->printText(str, x1 & ~3, (y + 8) & ~7, col, 0); } else { _screen->printText(str, x1, y, col, _textDimData[sdx].color2); + _screen->updateScreen(); } _textDimData[sdx].column += lw; @@ -432,9 +491,9 @@ void TextDisplayer_rpg::printLine(char *str) { str[len] = 0; _numCharsLeft = strlen(str); - _lineWidth = pc98PrintFlag ? (_numCharsLeft << 2) : _screen->getTextWidth(str); + _lineWidth = sjisTextMode ? (_numCharsLeft << 2) : (_screen->_currentFont == Screen::FID_SJIS_FNT ? _numCharsLeft * 9: _screen->getTextWidth(str)); - if (!_numCharsLeft && _textDimData[sdx].column < (sd->w << 3)) + if (!_numCharsLeft && (_textDimData[sdx].column + twoByteCharOffs) <= (sd->w << 3)) return; _textDimData[sdx].column = 0; @@ -483,7 +542,7 @@ void TextDisplayer_rpg::printMessage(const char *str, int textColor, ...) { displayText(_dialogueBuffer); - if (vm()->game() != GI_EOB1) + if (_vm->game() != GI_EOB1) _textDimData[_screen->curDimIndex()].color1 = tc; if (!_screen->_curPage) @@ -494,7 +553,7 @@ int TextDisplayer_rpg::clearDim(int dim) { int res = _screen->curDimIndex(); _screen->setScreenDim(dim); _textDimData[dim].color1 = _screen->_curDim->unk8; - _textDimData[dim].color2 = vm()->game() == GI_LOL ? _screen->_curDim->unkA : vm()->guiSettings()->colors.fill; + _textDimData[dim].color2 = _vm->game() == GI_LOL ? _screen->_curDim->unkA : _vm->guiSettings()->colors.fill; clearCurDim(); return res; } @@ -502,7 +561,7 @@ int TextDisplayer_rpg::clearDim(int dim) { void TextDisplayer_rpg::clearCurDim() { int d = _screen->curDimIndex(); const ScreenDim *tmp = _screen->getScreenDim(d); - if (vm()->gameFlags().use16ColorMode) { + if (_vm->gameFlags().use16ColorMode) { _screen->fillRect(tmp->sx << 3, tmp->sy, ((tmp->sx + tmp->w) << 3) - 2, (tmp->sy + tmp->h) - 2, _textDimData[d].color2); } else _screen->fillRect(tmp->sx << 3, tmp->sy, ((tmp->sx + tmp->w) << 3) - 1, (tmp->sy + tmp->h) - 1, _textDimData[d].color2); @@ -512,40 +571,40 @@ void TextDisplayer_rpg::clearCurDim() { } void TextDisplayer_rpg::textPageBreak() { - if (vm()->game() != GI_LOL) - SWAP(vm()->_dialogueButtonLabelColor1, vm()->_dialogueButtonLabelColor2); + if (_vm->game() != GI_LOL) + SWAP(_vm->_dialogueButtonLabelColor1, _vm->_dialogueButtonLabelColor2); int cp = _screen->setCurPage(0); - Screen::FontId cf = _screen->setFont(vm()->gameFlags().use16ColorMode ? Screen::FID_SJIS_FNT : Screen::FID_6_FNT); + Screen::FontId cf = _screen->setFont((_vm->gameFlags().lang == Common::JA_JPN && _vm->gameFlags().use16ColorMode) ? Screen::FID_SJIS_FNT : Screen::FID_6_FNT); - if (vm()->game() == GI_LOL) - vm()->_timer->pauseSingleTimer(11, true); + if (_vm->game() == GI_LOL) + _vm->_timer->pauseSingleTimer(11, true); - vm()->_fadeText = false; + _vm->_fadeText = false; int resetPortraitAfterSpeechAnim = 0; int updatePortraitSpeechAnimDuration = 0; - if (vm()->_updateCharNum != -1) { - resetPortraitAfterSpeechAnim = vm()->_resetPortraitAfterSpeechAnim; - vm()->_resetPortraitAfterSpeechAnim = 0; - updatePortraitSpeechAnimDuration = vm()->_updatePortraitSpeechAnimDuration; - if (vm()->_updatePortraitSpeechAnimDuration > 36) - vm()->_updatePortraitSpeechAnimDuration = 36; + if (_vm->_updateCharNum != -1) { + resetPortraitAfterSpeechAnim = _vm->_resetPortraitAfterSpeechAnim; + _vm->_resetPortraitAfterSpeechAnim = 0; + updatePortraitSpeechAnimDuration = _vm->_updatePortraitSpeechAnimDuration; + if (_vm->_updatePortraitSpeechAnimDuration > 36) + _vm->_updatePortraitSpeechAnimDuration = 36; } uint32 speechPartTime = 0; - if (vm()->speechEnabled() && vm()->_activeVoiceFileTotalTime && _numCharsTotal) - speechPartTime = vm()->_system->getMillis() + ((_numCharsPrinted * vm()->_activeVoiceFileTotalTime) / _numCharsTotal); + if (_vm->speechEnabled() && _vm->_activeVoiceFileTotalTime && _numCharsTotal) + speechPartTime = _vm->_system->getMillis() + ((_numCharsPrinted * _vm->_activeVoiceFileTotalTime) / _numCharsTotal); const ScreenDim *dim = _screen->getScreenDim(_screen->curDimIndex()); int x = ((dim->sx + dim->w) << 3) - (_vm->_dialogueButtonWidth + 3); int y = 0; - int w = vm()->_dialogueButtonWidth; + int w = _vm->_dialogueButtonWidth; - if (vm()->game() == GI_LOL) { - if (vm()->_needSceneRestore && (vm()->_updateFlags & 2)) { - if (vm()->_currentControlMode || !(vm()->_updateFlags & 2)) { + if (_vm->game() == GI_LOL) { + if (_vm->_needSceneRestore && (_vm->_updateFlags & 2)) { + if (_vm->_currentControlMode || !(_vm->_updateFlags & 2)) { y = dim->sy + dim->h - 5; } else { x += 6; @@ -555,49 +614,49 @@ void TextDisplayer_rpg::textPageBreak() { y = dim->sy + dim->h - 10; } } else { - y = vm()->guiSettings()->buttons.waitY[_waitButtonMode]; - x = vm()->guiSettings()->buttons.waitX[_waitButtonMode]; - w = vm()->guiSettings()->buttons.waitWidth[_waitButtonMode]; + y = _vm->guiSettings()->buttons.waitY[_waitButtonMode]; + x = _vm->guiSettings()->buttons.waitX[_waitButtonMode]; + w = _vm->guiSettings()->buttons.waitWidth[_waitButtonMode]; } - if (vm()->gameFlags().use16ColorMode) { - vm()->gui_drawBox(x + 8, (y & ~7) - 1, 66, 10, 0xee, 0xcc, -1); - _screen->printText(_pageBreakString, (x + 37 - (strlen(_pageBreakString) << 1) + 4) & ~3, (y + 2) & ~7, 0xc1, 0); + if (_vm->gameFlags().use16ColorMode) { + _vm->gui_drawBox(x + 8, (y & ~7) - 1, 66, 10, 0xEE, 0xCC, -1); + _screen->printText(_pageBreakString, (x + 37 - (strlen(_pageBreakString) << 1) + 4) & ~3, (y + 2) & ~7, 0xC1, 0); } else { - vm()->gui_drawBox(x, y, w, vm()->guiSettings()->buttons.height, vm()->guiSettings()->colors.frame1, vm()->guiSettings()->colors.frame2, vm()->guiSettings()->colors.fill); - _screen->printText(_pageBreakString, x + (w >> 1) - (vm()->screen()->getTextWidth(_pageBreakString) >> 1), y + 2, vm()->_dialogueButtonLabelColor1, 0); + _vm->gui_drawBox(x, y, w, _vm->guiSettings()->buttons.height, _vm->guiSettings()->colors.frame1, _vm->guiSettings()->colors.frame2, _vm->guiSettings()->colors.fill); + _screen->printText(_pageBreakString, x + (w >> 1) - (_vm->screen()->getTextWidth(_pageBreakString) >> 1), y + 2, _vm->_dialogueButtonLabelColor1, 0); } - vm()->removeInputTop(); + _vm->removeInputTop(); bool loop = true; bool target = false; do { - int inputFlag = vm()->checkInput(0, false) & 0xFF; - vm()->removeInputTop(); + int inputFlag = _vm->checkInput(0, false) & 0xFF; + _vm->removeInputTop(); while (!inputFlag && !_vm->shouldQuit()) { - vm()->update(); + _vm->update(); - if (vm()->speechEnabled()) { - if (((vm()->_system->getMillis() > speechPartTime) || (vm()->snd_updateCharacterSpeech() != 2)) && speechPartTime) { + if (_vm->speechEnabled()) { + if (((_vm->_system->getMillis() > speechPartTime) || (_vm->snd_updateCharacterSpeech() != 2)) && speechPartTime) { loop = false; - inputFlag = vm()->_keyMap[Common::KEYCODE_RETURN]; + inputFlag = _vm->_keyMap[Common::KEYCODE_RETURN]; break; } } - inputFlag = vm()->checkInput(0, false) & 0xFF; - vm()->removeInputTop(); + inputFlag = _vm->checkInput(0, false) & 0xFF; + _vm->removeInputTop(); } - vm()->gui_notifyButtonListChanged(); + _vm->gui_notifyButtonListChanged(); - if (inputFlag == vm()->_keyMap[Common::KEYCODE_SPACE] || inputFlag == vm()->_keyMap[Common::KEYCODE_RETURN]) { + if (inputFlag == _vm->_keyMap[Common::KEYCODE_SPACE] || inputFlag == _vm->_keyMap[Common::KEYCODE_RETURN]) { loop = false; } else if (inputFlag == 199 || inputFlag == 201) { - if (vm()->posWithinRect(vm()->_mouseX, vm()->_mouseY, x, y, x + w, y + 9)) { + if (_vm->posWithinRect(_vm->_mouseX, _vm->_mouseY, x, y, x + w, y + 9)) { if (_vm->game() == GI_LOL) target = true; else @@ -609,7 +668,7 @@ void TextDisplayer_rpg::textPageBreak() { } } while (loop && !_vm->shouldQuit()); - if (vm()->gameFlags().use16ColorMode) + if (_vm->gameFlags().use16ColorMode) _screen->fillRect(x + 8, y, x + 57, y + 9, _textDimData[_screen->curDimIndex()].color2); else _screen->fillRect(x, y, x + w - 1, y + 8, _textDimData[_screen->curDimIndex()].color2); @@ -617,52 +676,52 @@ void TextDisplayer_rpg::textPageBreak() { clearCurDim(); _screen->updateScreen(); - if (vm()->game() == GI_LOL) - vm()->_timer->pauseSingleTimer(11, false); + if (_vm->game() == GI_LOL) + _vm->_timer->pauseSingleTimer(11, false); - if (vm()->_updateCharNum != -1) { - vm()->_resetPortraitAfterSpeechAnim = resetPortraitAfterSpeechAnim; + if (_vm->_updateCharNum != -1) { + _vm->_resetPortraitAfterSpeechAnim = resetPortraitAfterSpeechAnim; if (updatePortraitSpeechAnimDuration > 36) updatePortraitSpeechAnimDuration -= 36; else updatePortraitSpeechAnimDuration >>= 1; - vm()->_updatePortraitSpeechAnimDuration = updatePortraitSpeechAnimDuration; + _vm->_updatePortraitSpeechAnimDuration = updatePortraitSpeechAnimDuration; } _screen->setFont(cf); _screen->setCurPage(cp); - if (vm()->game() != GI_LOL) - SWAP(vm()->_dialogueButtonLabelColor1, vm()->_dialogueButtonLabelColor2); + if (_vm->game() != GI_LOL) + SWAP(_vm->_dialogueButtonLabelColor1, _vm->_dialogueButtonLabelColor2); - vm()->removeInputTop(); + _vm->removeInputTop(); } void TextDisplayer_rpg::displayWaitButton() { - vm()->_dialogueNumButtons = 1; - vm()->_dialogueButtonString[0] = _pageBreakString; - vm()->_dialogueButtonString[1] = 0; - vm()->_dialogueButtonString[2] = 0; - vm()->_dialogueHighlightedButton = 0; + _vm->_dialogueNumButtons = 1; + _vm->_dialogueButtonString[0] = _pageBreakString; + _vm->_dialogueButtonString[1] = 0; + _vm->_dialogueButtonString[2] = 0; + _vm->_dialogueHighlightedButton = 0; - vm()->_dialogueButtonPosX = &vm()->guiSettings()->buttons.waitX[_waitButtonMode]; - vm()->_dialogueButtonPosY = &vm()->guiSettings()->buttons.waitY[_waitButtonMode]; - vm()->_dialogueButtonWidth = vm()->guiSettings()->buttons.waitWidth[_waitButtonMode]; - vm()->_dialogueButtonYoffs = 0; + _vm->_dialogueButtonPosX = &_vm->guiSettings()->buttons.waitX[_waitButtonMode]; + _vm->_dialogueButtonPosY = &_vm->guiSettings()->buttons.waitY[_waitButtonMode]; + _vm->_dialogueButtonWidth = _vm->guiSettings()->buttons.waitWidth[_waitButtonMode]; + _vm->_dialogueButtonYoffs = 0; - SWAP(vm()->_dialogueButtonLabelColor1, vm()->_dialogueButtonLabelColor2); - vm()->drawDialogueButtons(); + SWAP(_vm->_dialogueButtonLabelColor1, _vm->_dialogueButtonLabelColor2); + _vm->drawDialogueButtons(); - if (!vm()->shouldQuit()) - vm()->removeInputTop(); + if (!_vm->shouldQuit()) + _vm->removeInputTop(); - while (!vm()->processDialogue() && !vm()->shouldQuit()) {} + while (!_vm->processDialogue() && !_vm->shouldQuit()) {} - _screen->fillRect(vm()->_dialogueButtonPosX[0], vm()->_dialogueButtonPosY[0], vm()->_dialogueButtonPosX[0] + vm()->_dialogueButtonWidth - 1, vm()->_dialogueButtonPosY[0] + vm()->guiSettings()->buttons.height - 1, vm()->guiSettings()->colors.fill); + _screen->fillRect(_vm->_dialogueButtonPosX[0], _vm->_dialogueButtonPosY[0], _vm->_dialogueButtonPosX[0] + _vm->_dialogueButtonWidth - 1, _vm->_dialogueButtonPosY[0] + _vm->guiSettings()->buttons.height - 1, _vm->guiSettings()->colors.fill); _screen->updateScreen(); - vm()->_dialogueButtonWidth = 95; - SWAP(vm()->_dialogueButtonLabelColor1, vm()->_dialogueButtonLabelColor2); + _vm->_dialogueButtonWidth = 95; + SWAP(_vm->_dialogueButtonLabelColor1, _vm->_dialogueButtonLabelColor2); clearCurDim(); } diff --git a/engines/kyra/text_rpg.h b/engines/kyra/text_rpg.h index 5ad8899484..eb8617a371 100644 --- a/engines/kyra/text_rpg.h +++ b/engines/kyra/text_rpg.h @@ -79,7 +79,7 @@ protected: uint32 _numCharsPrinted; bool _printFlag; - bool _sjisLineBreakFlag; + bool _sjisTextModeLineBreak; char _pageBreakString[20]; char _scriptParaString[11]; diff --git a/engines/kyra/timer_eob.cpp b/engines/kyra/timer_eob.cpp index 766fe453ab..effda22ac9 100644 --- a/engines/kyra/timer_eob.cpp +++ b/engines/kyra/timer_eob.cpp @@ -57,7 +57,7 @@ void EoBCoreEngine::setupTimers() { void EoBCoreEngine::setCharEventTimer(int charIndex, uint32 countdown, int evnt, int updateExistingTimer) { uint32 ntime = _system->getMillis() + countdown * _tickLength; - uint8 timerId = 0x30 | (charIndex & 0x0f); + uint8 timerId = 0x30 | (charIndex & 0x0F); EoBCharacter *c = &_characters[charIndex]; if (!_timer->isEnabled(timerId)) { @@ -118,7 +118,7 @@ void EoBCoreEngine::setupCharacterTimers() { if (!testCharacter(i, 1)) continue; - uint32 nextTimer = 0xffffffff; + uint32 nextTimer = 0xFFFFFFFF; for (int ii = 0; ii < 10; ii++) { if (c->timers[ii] && c->timers[ii] < nextTimer) @@ -126,7 +126,7 @@ void EoBCoreEngine::setupCharacterTimers() { } uint32 ctime = _system->getMillis(); - if (nextTimer == 0xffffffff) + if (nextTimer == 0xFFFFFFFF) _timer->disable(0x30 | i); else { enableTimer(0x30 | i); @@ -219,11 +219,11 @@ void EoBCoreEngine::timerProcessFlyingObjects(int timerNum) { } void EoBCoreEngine::timerProcessMonsters(int timerNum) { - updateMonsters(timerNum & 0x0f); + updateMonsters(timerNum & 0x0F); } void EoBCoreEngine::timerSpecialCharacterUpdate(int timerNum) { - int charIndex = timerNum & 0x0f; + int charIndex = timerNum & 0x0F; EoBCharacter *c = &_characters[charIndex]; uint32 ctime = _system->getMillis(); @@ -309,13 +309,13 @@ void EoBCoreEngine::timerSpecialCharacterUpdate(int timerNum) { _screen->setFont(of); } - uint32 nextTimer = 0xffffffff; + uint32 nextTimer = 0xFFFFFFFF; for (int i = 0; i < 10; i++) { if (c->timers[i] && c->timers[i] < nextTimer) nextTimer = c->timers[i]; } - if (nextTimer == 0xffffffff) + if (nextTimer == 0xFFFFFFFF) _timer->disable(timerNum); else _timer->setCountdown(timerNum, (nextTimer - ctime) / _tickLength); diff --git a/engines/kyra/timer_lol.cpp b/engines/kyra/timer_lol.cpp index a3df8dbe00..9d0cc0dd72 100644 --- a/engines/kyra/timer_lol.cpp +++ b/engines/kyra/timer_lol.cpp @@ -49,7 +49,7 @@ void LoLEngine::setupTimers() { } void LoLEngine::timerProcessMonsters(int timerNum) { - for (int i = timerNum & 0x0f; i < 30; i += 2) + for (int i = timerNum & 0x0F; i < 30; i += 2) updateMonster(&_monsters[i]); } @@ -77,7 +77,7 @@ void LoLEngine::timerSpecialCharacterUpdate(int timerNum) { if (_characters[i].characterUpdateDelay[ii] > eventsLeft) eventsLeft = _characters[i].characterUpdateDelay[ii]; } else { - _characters[i].flags &= 0xfffb; + _characters[i].flags &= 0xFFFB; } gui_drawCharPortraitWithStats(i); @@ -89,7 +89,7 @@ void LoLEngine::timerSpecialCharacterUpdate(int timerNum) { break; case 2: - _characters[i].flags &= 0xffbf; + _characters[i].flags &= 0xFFBF; gui_drawCharPortraitWithStats(i); break; @@ -104,7 +104,7 @@ void LoLEngine::timerSpecialCharacterUpdate(int timerNum) { break; case 4: - _characters[i].flags &= 0xfeff; + _characters[i].flags &= 0xFEFF; _txt->printMessage(0, getLangString(0x4027), _characters[i].name); gui_drawCharPortraitWithStats(i); break; @@ -114,7 +114,7 @@ void LoLEngine::timerSpecialCharacterUpdate(int timerNum) { break; case 6: - _characters[i].flags &= 0xefff; + _characters[i].flags &= 0xEFFF; gui_drawCharPortraitWithStats(i); break; @@ -146,7 +146,7 @@ void LoLEngine::timerProcessFlyingObjects(int timerNum) { } void LoLEngine::timerRunSceneAnimScript(int timerNum) { - runLevelScript(0x401 + (timerNum & 0x0f), -1); + runLevelScript(0x401 + (timerNum & 0x0F), -1); } void LoLEngine::timerRegeneratePoints(int timerNum) { diff --git a/engines/kyra/vqa.cpp b/engines/kyra/vqa.cpp index 471e83c9ed..081d94a050 100644 --- a/engines/kyra/vqa.cpp +++ b/engines/kyra/vqa.cpp @@ -126,8 +126,8 @@ void VQAMovie::decodeSND1(byte *inbuf, uint32 insize, byte *outbuf, uint32 outsi while (outsize > 0) { input = *inbuf++ << 2; - code = (input >> 8) & 0xff; - count = (input & 0xff) >> 2; + code = (input >> 8) & 0xFF; + count = (input & 0xFF) >> 2; switch (code) { case 2: @@ -149,7 +149,7 @@ void VQAMovie::decodeSND1(byte *inbuf, uint32 insize, byte *outbuf, uint32 outsi for (; count >= 0; count--) { code = *inbuf++; - curSample += WSTable4Bit[code & 0x0f]; + curSample += WSTable4Bit[code & 0x0F]; curSample = CLIP<int16>(curSample, 0, 255); *outbuf++ = curSample; @@ -264,7 +264,7 @@ bool VQAMovie::open(const char *filename) { _frameInfo = new uint32[_header.numFrames]; _frame = new byte[_header.width * _header.height]; - _codeBookSize = 0xf00 * _header.blockW * _header.blockH; + _codeBookSize = 0xF00 * _header.blockW * _header.blockH; _codeBook = new byte[_codeBookSize]; _partialCodeBook = new byte[_codeBookSize]; memset(_codeBook, 0, _codeBookSize); diff --git a/engines/lastexpress/data/animation.cpp b/engines/lastexpress/data/animation.cpp index 9d0ed532f2..7618259e69 100644 --- a/engines/lastexpress/data/animation.cpp +++ b/engines/lastexpress/data/animation.cpp @@ -32,10 +32,8 @@ #include "common/events.h" #include "common/rational.h" -#include "common/rect.h" #include "common/stream.h" #include "common/system.h" -#include "common/textconsole.h" #include "engines/engine.h" @@ -232,7 +230,7 @@ AnimFrame *Animation::processChunkFrame(Common::SeekableReadStream *in, const Ch i.read(str, false); // Decode the frame - AnimFrame *f = new AnimFrame(str, i); + AnimFrame *f = new AnimFrame(str, i, true); // Delete the temporary chunk buffer delete str; @@ -250,7 +248,7 @@ void Animation::processChunkAudio(Common::SeekableReadStream *in, const Chunk &c // Read Snd header uint32 header1 = in->readUint32LE(); uint16 header2 = in->readUint16LE(); - warning("Start ADPCM: %d, %d", header1, header2); + debugC(4, kLastExpressDebugSound, "Start ADPCM: %d, %d", header1, header2); size -= 6; } diff --git a/engines/lastexpress/data/background.cpp b/engines/lastexpress/data/background.cpp index 3d866c26f9..60379251a3 100644 --- a/engines/lastexpress/data/background.cpp +++ b/engines/lastexpress/data/background.cpp @@ -107,6 +107,7 @@ byte *Background::decodeComponent(Common::SeekableReadStream *in, uint32 inSize, return NULL; // Initialize the decoding + memset(out, 0, outSize * sizeof(byte)); uint32 inPos = 0; uint32 outPos = 0; diff --git a/engines/lastexpress/data/cursor.cpp b/engines/lastexpress/data/cursor.cpp index 205c46f667..d176d963d1 100644 --- a/engines/lastexpress/data/cursor.cpp +++ b/engines/lastexpress/data/cursor.cpp @@ -128,7 +128,7 @@ Common::Rect Icon::draw(Graphics::Surface *surface) { for (int i = 0; i < 32; i++) { // Adjust brightness - if (_brightnessIndex == -1) + if (_brightnessIndex == -1 || _brightnessIndex >= ARRAYSIZE(brigthnessData)) *s = *image; else *s = (*image & brigthnessData[_brightnessIndex]) >> _brightnessIndex; diff --git a/engines/lastexpress/data/font.cpp b/engines/lastexpress/data/font.cpp index 79cf64e617..8ac1afce9a 100644 --- a/engines/lastexpress/data/font.cpp +++ b/engines/lastexpress/data/font.cpp @@ -149,7 +149,7 @@ uint8 Font::getCharWidth(uint16 c) const{ uint16 Font::getStringWidth(Common::String str) const { uint16 width = 0; for (uint i = 0; i < str.size(); i++) - width += getCharWidth((unsigned) (int)str[i]); + width += getCharWidth((unsigned char)str[i]); return width; } @@ -185,8 +185,8 @@ void Font::drawChar(Graphics::Surface *surface, int16 x, int16 y, uint16 c) { Common::Rect Font::drawString(Graphics::Surface *surface, int16 x, int16 y, Common::String str) { int16 currentX = x; for (uint i = 0; i < str.size(); i++) { - drawChar(surface, currentX, y, (unsigned) (int)str[i]); - currentX += getCharWidth((unsigned) (int)str[i]); + drawChar(surface, currentX, y, (unsigned char)str[i]); + currentX += getCharWidth((unsigned char)str[i]); } return Common::Rect(x, y, x + currentX, y + (int16)_charHeight); diff --git a/engines/lastexpress/data/scene.cpp b/engines/lastexpress/data/scene.cpp index 8f279ffbb3..fdb1ac6d46 100644 --- a/engines/lastexpress/data/scene.cpp +++ b/engines/lastexpress/data/scene.cpp @@ -28,7 +28,6 @@ #include "lastexpress/lastexpress.h" #include "lastexpress/resource.h" -#include "common/textconsole.h" #include "common/stream.h" namespace LastExpress { @@ -122,7 +121,7 @@ bool SceneHotspot::isInside(const Common::Point &point) { // Scene Scene::~Scene() { // Free the hotspots - for (int i = 0; i < (int)_hotspots.size(); i++) + for (uint i = 0; i < _hotspots.size(); i++) delete _hotspots[i]; } @@ -172,7 +171,7 @@ bool Scene::checkHotSpot(const Common::Point &coord, SceneHotspot **hotspot) { bool found = false; int _location = 0; - for (int i = 0; i < (int)_hotspots.size(); i++) { + for (uint i = 0; i < _hotspots.size(); i++) { if (_hotspots[i]->isInside(coord)) { if (_location <= _hotspots[i]->location) { _location = _hotspots[i]->location; @@ -224,7 +223,7 @@ Common::String Scene::toString() { // Hotspots if (_hotspots.size() != 0) { output += "\nHotspots:\n"; - for (int i = 0; i < (int)_hotspots.size(); i++) + for (uint i = 0; i < _hotspots.size(); i++) output += _hotspots[i]->toString() + "\n"; } @@ -241,7 +240,7 @@ SceneLoader::~SceneLoader() { void SceneLoader::clear() { // Remove all scenes - for (int i = 0; i < (int)_scenes.size(); i++) + for (uint i = 0; i < _scenes.size(); i++) delete _scenes[i]; _scenes.clear(); @@ -292,9 +291,9 @@ Scene *SceneLoader::get(SceneIndex index) { return NULL; // Load the hotspots if needed - _scenes[(int)index]->loadHotspots(_stream); + _scenes[(uint)index]->loadHotspots(_stream); - return _scenes[(int)index]; + return _scenes[(uint)index]; } } // End of namespace LastExpress diff --git a/engines/lastexpress/data/sequence.cpp b/engines/lastexpress/data/sequence.cpp index a62348f6c0..a5bcba84cd 100644 --- a/engines/lastexpress/data/sequence.cpp +++ b/engines/lastexpress/data/sequence.cpp @@ -27,7 +27,6 @@ #include "lastexpress/debug.h" #include "common/stream.h" -#include "common/textconsole.h" namespace LastExpress { @@ -77,7 +76,7 @@ void FrameInfo::read(Common::SeekableReadStream *in, bool isSequence) { // AnimFrame -AnimFrame::AnimFrame(Common::SeekableReadStream *in, const FrameInfo &f) : _palette(NULL) { +AnimFrame::AnimFrame(Common::SeekableReadStream *in, const FrameInfo &f, bool ignoreSubtype) : _palette(NULL), _ignoreSubtype(ignoreSubtype) { _palSize = 1; // TODO: use just the needed rectangle _image.create(640, 480, Graphics::PixelFormat::createFormatCLUT8()); diff --git a/engines/lastexpress/data/sequence.h b/engines/lastexpress/data/sequence.h index 9987eae48e..610a55cebf 100644 --- a/engines/lastexpress/data/sequence.h +++ b/engines/lastexpress/data/sequence.h @@ -49,8 +49,9 @@ byte {1} - Compression type byte {1} - Subtype (determines which set of decompression functions will be called) => 0, 1, 2, 3 byte {1} - Unknown + byte {1} - Keep previous frame while drawing + byte {1} - Unknown byte {1} - Unknown - uint16 {2} - Unknown byte {1} - Sound action byte {1} - Unknown uint32 {4} - positionId @@ -129,7 +130,7 @@ struct FrameInfo { class AnimFrame : public Drawable { public: - AnimFrame(Common::SeekableReadStream *in, const FrameInfo &f); + AnimFrame(Common::SeekableReadStream *in, const FrameInfo &f, bool ignoreSubtype = false); ~AnimFrame(); Common::Rect draw(Graphics::Surface *s); @@ -146,6 +147,7 @@ private: uint16 _palSize; uint16 *_palette; Common::Rect _rect; + bool _ignoreSubtype; }; class Sequence { diff --git a/engines/lastexpress/data/snd.cpp b/engines/lastexpress/data/snd.cpp index a9bee6155d..a77e4a06c6 100644 --- a/engines/lastexpress/data/snd.cpp +++ b/engines/lastexpress/data/snd.cpp @@ -28,11 +28,9 @@ #include "lastexpress/debug.h" #include "audio/decoders/adpcm_intern.h" -#include "audio/audiostream.h" #include "common/debug.h" #include "common/memstream.h" #include "common/system.h" -#include "common/textconsole.h" namespace LastExpress { @@ -358,6 +356,8 @@ public: Audio::ADPCMStream(stream, disposeAfterUse, size, 44100, 1, blockSize) { _currentFilterId = -1; _nextFilterId = filterId; + _stepAdjust1 = 0; + _stepAdjust2 = 0; } int readBuffer(int16 *buffer, const int numSamples) { @@ -378,7 +378,7 @@ public: // Get current filter _currentFilterId = _nextFilterId; - _nextFilterId = -1; + //_nextFilterId = -1; // FIXME: the filter id should be recomputed based on the sound entry status for each block // No filter: skip decoding if (_currentFilterId == -1) @@ -455,7 +455,9 @@ void SimpleSound::play(Audio::AudioStream *as) { ////////////////////////////////////////////////////////////////////////// StreamedSound::StreamedSound() : _as(NULL), _loaded(false) {} -StreamedSound::~StreamedSound() {} +StreamedSound::~StreamedSound() { + _as = NULL; +} bool StreamedSound::load(Common::SeekableReadStream *stream, int32 filterId) { if (!stream) @@ -484,6 +486,9 @@ bool StreamedSound::isFinished() { } void StreamedSound::setFilterId(int32 filterId) { + if (!_as) + return; + ((LastExpress_ADPCMStream *)_as)->setFilterId(filterId); } @@ -521,6 +526,7 @@ void AppendableSound::queueBuffer(Common::SeekableReadStream *bufferIn) { // Setup the ADPCM decoder uint32 sizeIn = (uint32)bufferIn->size(); Audio::AudioStream *adpcm = makeDecoder(bufferIn, sizeIn); + ((LastExpress_ADPCMStream *)adpcm)->setFilterId(16); // Queue the stream _as->queueAudioStream(adpcm); diff --git a/engines/lastexpress/data/subtitle.cpp b/engines/lastexpress/data/subtitle.cpp index 0be832cbdd..4d19c02aa7 100644 --- a/engines/lastexpress/data/subtitle.cpp +++ b/engines/lastexpress/data/subtitle.cpp @@ -32,7 +32,6 @@ #include "common/debug.h" #include "common/rect.h" #include "common/stream.h" -#include "common/textconsole.h" namespace LastExpress { @@ -151,7 +150,7 @@ SubtitleManager::~SubtitleManager() { } void SubtitleManager::reset() { - for (int i = 0; i < (int)_subtitles.size(); i++) + for (uint i = 0; i < _subtitles.size(); i++) delete _subtitles[i]; _subtitles.clear(); @@ -211,10 +210,10 @@ void SubtitleManager::setTime(uint16 time) { _currentIndex = -1; // Find the appropriate line to show - for (int16 i = 0; i < (int16)_subtitles.size(); i++) { + for (uint i = 0; i < _subtitles.size(); i++) { if ((time >= _subtitles[i]->getTimeStart()) && (time <= _subtitles[i]->getTimeStop())) { // Keep the index of the line to show - _currentIndex = i; + _currentIndex = (int16)i; return; } } @@ -238,7 +237,7 @@ Common::Rect SubtitleManager::draw(Graphics::Surface *surface) { // Draw the current line assert(_currentIndex >= 0 && _currentIndex < (int16)_subtitles.size()); - return _subtitles[_currentIndex]->draw(surface, _font); + return _subtitles[(uint16)_currentIndex]->draw(surface, _font); } } // End of namespace LastExpress diff --git a/engines/lastexpress/debug.cpp b/engines/lastexpress/debug.cpp index dc2807db63..db3a3e3962 100644 --- a/engines/lastexpress/debug.cpp +++ b/engines/lastexpress/debug.cpp @@ -28,13 +28,13 @@ #include "lastexpress/data/cursor.h" #include "lastexpress/data/scene.h" #include "lastexpress/data/sequence.h" -#include "lastexpress/data/snd.h" #include "lastexpress/data/subtitle.h" #include "lastexpress/fight/fight.h" #include "lastexpress/game/action.h" #include "lastexpress/game/beetle.h" +#include "lastexpress/game/entities.h" #include "lastexpress/game/inventory.h" #include "lastexpress/game/logic.h" #include "lastexpress/game/object.h" @@ -44,15 +44,12 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/graphics.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" #include "lastexpress/resource.h" #include "common/debug-channels.h" -#include "common/events.h" #include "common/md5.h" namespace LastExpress { @@ -88,7 +85,6 @@ Debugger::Debugger(LastExpressEngine *engine) : _engine(engine), _command(NULL), DCmd_Register("entity", WRAP_METHOD(Debugger, cmdEntity)); // Misc - DCmd_Register("loadgame", WRAP_METHOD(Debugger, cmdLoadGame)); DCmd_Register("chapter", WRAP_METHOD(Debugger, cmdSwitchChapter)); DCmd_Register("clear", WRAP_METHOD(Debugger, cmdClear)); @@ -142,6 +138,9 @@ void Debugger::copyCommand(int argc, const char **argv) { for (int i = 0; i < _numParams; i++) { _commandParams[i] = (char *)malloc(strlen(argv[i]) + 1); + if (_commandParams[i] == NULL) + error("[Debugger::copyCommand] Cannot allocate memory for command parameters"); + memset(_commandParams[i], 0, strlen(argv[i]) + 1); strcpy(_commandParams[i], argv[i]); } @@ -155,9 +154,18 @@ void Debugger::callCommand() { (*_command)(_numParams, const_cast<const char **>(_commandParams)); } -void Debugger::loadArchive(ArchiveIndex index) const { - _engine->getResourceManager()->loadArchive(index); - getScenes()->loadSceneDataFile(index); +bool Debugger::loadArchive(int index) { + if (index < 1 || index > 3) { + DebugPrintf("Invalid cd number (was: %d, valid: [1-3])\n", index); + return false; + } + + if (!_engine->getResourceManager()->loadArchive((ArchiveIndex)index)) + return false; + + getScenes()->loadSceneDataFile((ArchiveIndex)index); + + return true; } // Restore loaded archive @@ -236,8 +244,10 @@ bool Debugger::cmdListFiles(int argc, const char **argv) { Common::String filter(const_cast<char *>(argv[1])); // Load the proper archive - if (argc == 3) - loadArchive((ArchiveIndex)getNumber(argv[2])); + if (argc == 3) { + if (!loadArchive(getNumber(argv[2]))) + return true; + } Common::ArchiveMemberList list; int count = _engine->getResourceManager()->listMatchingMembers(list, filter); @@ -320,8 +330,10 @@ bool Debugger::cmdShowFrame(int argc, const char **argv) { Common::String filename(const_cast<char *>(argv[1])); filename += ".seq"; - if (argc == 4) - loadArchive((ArchiveIndex)getNumber(argv[3])); + if (argc == 4) { + if (!loadArchive(getNumber(argv[3]))) + return true; + } if (!_engine->getResourceManager()->hasFile(filename)) { DebugPrintf("Cannot find file: %s\n", filename.c_str()); @@ -380,8 +392,10 @@ bool Debugger::cmdShowBg(int argc, const char **argv) { if (argc == 2 || argc == 3) { Common::String filename(const_cast<char *>(argv[1])); - if (argc == 3) - loadArchive((ArchiveIndex)getNumber(argv[2])); + if (argc == 3) { + if (!loadArchive(getNumber(argv[2]))) + return true; + } if (!_engine->getResourceManager()->hasFile(filename + ".BG")) { DebugPrintf("Cannot find file: %s\n", (filename + ".BG").c_str()); @@ -433,8 +447,10 @@ bool Debugger::cmdPlaySeq(int argc, const char **argv) { Common::String filename(const_cast<char *>(argv[1])); filename += ".seq"; - if (argc == 3) - loadArchive((ArchiveIndex)getNumber(argv[2])); + if (argc == 3) { + if (!loadArchive(getNumber(argv[2]))) + return true; + } if (!_engine->getResourceManager()->hasFile(filename)) { DebugPrintf("Cannot find file: %s\n", filename.c_str()); @@ -508,8 +524,10 @@ bool Debugger::cmdPlaySeq(int argc, const char **argv) { bool Debugger::cmdPlaySnd(int argc, const char **argv) { if (argc == 2 || argc == 3) { - if (argc == 3) - loadArchive((ArchiveIndex)getNumber(argv[2])); + if (argc == 3) { + if (!loadArchive(getNumber(argv[2]))) + return true; + } // Add .SND at the end of the filename if needed Common::String name(const_cast<char *>(argv[1])); @@ -523,7 +541,7 @@ bool Debugger::cmdPlaySnd(int argc, const char **argv) { _engine->_system->getMixer()->stopAll(); - _soundStream->load(getArchive(name)); + _soundStream->load(getArchive(name), 16); if (argc == 3) restoreArchive(); @@ -545,8 +563,10 @@ bool Debugger::cmdPlaySbe(int argc, const char **argv) { if (argc == 2 || argc == 3) { Common::String filename(const_cast<char *>(argv[1])); - if (argc == 3) - loadArchive((ArchiveIndex)getNumber(argv[2])); + if (argc == 3) { + if (!loadArchive(getNumber(argv[2]))) + return true; + } filename += ".sbe"; @@ -608,11 +628,13 @@ bool Debugger::cmdPlayNis(int argc, const char **argv) { if (argc == 2 || argc == 3) { Common::String name(const_cast<char *>(argv[1])); - if (argc == 3) - loadArchive((ArchiveIndex)getNumber(argv[2])); + if (argc == 3) { + if (!loadArchive(getNumber(argv[2]))) + return true; + } // If we got a nis filename, check that the file exists - if (name.contains('.') && _engine->getResourceManager()->hasFile(name)) { + if (name.contains('.') && !_engine->getResourceManager()->hasFile(name)) { DebugPrintf("Cannot find file: %s\n", name.c_str()); return true; } @@ -665,8 +687,10 @@ bool Debugger::cmdLoadScene(int argc, const char **argv) { SceneIndex index = (SceneIndex)getNumber(argv[1]); // Check args - if (argc == 3) - loadArchive((ArchiveIndex)getNumber(argv[2])); + if (argc == 3) { + if (!loadArchive(getNumber(argv[2]))) + return true; + } if (index > 2500) { DebugPrintf("Error: invalid index value (0-2500)"); @@ -1094,30 +1118,6 @@ label_error: } /** - * Command: loads a game - * - * @param argc The argument count. - * @param argv The values. - * - * @return true if it was handled, false otherwise - */ -bool Debugger::cmdLoadGame(int argc, const char **argv) { - if (argc == 2) { - int id = getNumber(argv[1]); - - if (id == 0 || id > 6) - goto error; - - getSaveLoad()->loadGame((GameId)(id - 1)); - } else { -error: - DebugPrintf("Syntax: loadgame <id> (id=1-6)\n"); - } - - return true; -} - -/** * Command: switch to a specific chapter * * @param argc The argument count. diff --git a/engines/lastexpress/debug.h b/engines/lastexpress/debug.h index d9ba6f47a1..532cb83717 100644 --- a/engines/lastexpress/debug.h +++ b/engines/lastexpress/debug.h @@ -79,7 +79,6 @@ private: bool cmdShow(int argc, const char **argv); bool cmdEntity(int argc, const char **argv); - bool cmdLoadGame(int argc, const char **argv); bool cmdSwitchChapter(int argc, const char **argv); bool cmdClear(int argc, const char **argv); @@ -87,7 +86,7 @@ private: void copyCommand(int argc, const char **argv); int getNumber(const char *arg) const; - void loadArchive(ArchiveIndex index) const; + bool loadArchive(int index); void restoreArchive() const; Debuglet *_command; diff --git a/engines/lastexpress/detection.cpp b/engines/lastexpress/detection.cpp index 82a6520522..2fdeef910a 100644 --- a/engines/lastexpress/detection.cpp +++ b/engines/lastexpress/detection.cpp @@ -173,7 +173,7 @@ static const ADGameDescription gameDescriptions[] = { ADGF_UNSTABLE, GUIO1(GUIO_NOASPECT) }, - + // The Last Express (Russian) // expressw.exe 1999-04-05 15:33:56 // express.exe ??? @@ -211,6 +211,7 @@ public: return "LastExpress Engine (C) 1997 Smoking Car Productions"; } +protected: bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const; }; diff --git a/engines/lastexpress/entities/abbot.cpp b/engines/lastexpress/entities/abbot.cpp index 301c52e142..406b017d3a 100644 --- a/engines/lastexpress/entities/abbot.cpp +++ b/engines/lastexpress/entities/abbot.cpp @@ -34,9 +34,7 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" namespace LastExpress { @@ -60,10 +58,10 @@ Abbot::Abbot(LastExpressEngine *engine) : Entity(engine, kEntityAbbot) { ADD_CALLBACK_FUNCTION(Abbot, chapter2); ADD_CALLBACK_FUNCTION(Abbot, chapter3); ADD_CALLBACK_FUNCTION(Abbot, chapter3Handler); - ADD_CALLBACK_FUNCTION(Abbot, function19); - ADD_CALLBACK_FUNCTION(Abbot, function20); - ADD_CALLBACK_FUNCTION(Abbot, function21); - ADD_CALLBACK_FUNCTION(Abbot, function22); + ADD_CALLBACK_FUNCTION(Abbot, conversationWithBoutarel); + ADD_CALLBACK_FUNCTION(Abbot, readPaper); + ADD_CALLBACK_FUNCTION(Abbot, goToLunch); + ADD_CALLBACK_FUNCTION(Abbot, haveLunch); ADD_CALLBACK_FUNCTION(Abbot, function23); ADD_CALLBACK_FUNCTION(Abbot, function24); ADD_CALLBACK_FUNCTION(Abbot, function25); @@ -261,7 +259,7 @@ IMPLEMENT_FUNCTION(18, Abbot, chapter3Handler) getData()->entityPosition = kPosition_6470; getData()->location = kLocationInsideCompartment; - setup_function19(); + setup_conversationWithBoutarel(); break; } break; @@ -274,7 +272,7 @@ IMPLEMENT_FUNCTION(18, Abbot, chapter3Handler) IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// -IMPLEMENT_FUNCTION(19, Abbot, function19) +IMPLEMENT_FUNCTION(19, Abbot, conversationWithBoutarel) switch (savepoint.action) { default: break; @@ -313,21 +311,21 @@ IMPLEMENT_FUNCTION(19, Abbot, function19) case 3: getSavePoints()->push(kEntityAbbot, kEntityBoutarel, kAction122288808); - setup_function20(); + setup_readPaper(); break; } } IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// -IMPLEMENT_FUNCTION(20, Abbot, function20) +IMPLEMENT_FUNCTION(20, Abbot, readPaper) switch (savepoint.action) { default: break; case kActionNone: if (getState()->time > kTime1966500 && getEntities()->isInRestaurant(kEntityBoutarel)) - setup_function21(); + setup_goToLunch(); break; case kActionDefault: @@ -337,7 +335,7 @@ IMPLEMENT_FUNCTION(20, Abbot, function20) IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// -IMPLEMENT_FUNCTION(21, Abbot, function21) +IMPLEMENT_FUNCTION(21, Abbot, goToLunch) switch (savepoint.action) { default: break; @@ -395,7 +393,7 @@ IMPLEMENT_FUNCTION(21, Abbot, function21) break; case 7: - setup_function22(); + setup_haveLunch(); break; } break; @@ -411,13 +409,13 @@ IMPLEMENT_FUNCTION(21, Abbot, function21) IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// -IMPLEMENT_FUNCTION(22, Abbot, function22) +IMPLEMENT_FUNCTION(22, Abbot, haveLunch) switch (savepoint.action) { default: break; case kActionNone: - TIME_CHECK_SAVEPOINT(kTime1971000, params->param1, kEntityAbbot, kEntityServers0, kAction218586752); + Entity::timeCheckSavepoint(kTime1971000, params->param1, kEntityAbbot, kEntityServers0, kAction218586752); if (getState()->time > kTime1989000 && getEntities()->isSomebodyInsideRestaurantOrSalon()) { getData()->inventoryItem = kItemNone; @@ -516,7 +514,8 @@ IMPLEMENT_FUNCTION(24, Abbot, function24) break; case kActionNone: - UPDATE_PARAM(params->param1, getState()->time, 900); + if (!Entity::updateParameter(params->param1, getState()->time, 900)) + break; setup_function25(); break; @@ -617,7 +616,8 @@ IMPLEMENT_FUNCTION(26, Abbot, function26) break; case kActionNone: - UPDATE_PARAM(params->param2, getState()->time, 4500); + if (!Entity::updateParameter(params->param2, getState()->time, 4500)) + break; if (getEntities()->isSomebodyInsideRestaurantOrSalon()) setup_function27(); @@ -691,7 +691,7 @@ IMPLEMENT_FUNCTION(28, Abbot, function28) break; case kActionNone: - TIME_CHECK_CALLBACK(kTime2052000, params->param1, 1, setup_function29); + Entity::timeCheckCallback(kTime2052000, params->param1, 1, WRAP_SETUP_FUNCTION(Abbot, setup_function29)); break; case kActionDefault: @@ -770,7 +770,7 @@ IMPLEMENT_FUNCTION(29, Abbot, function29) getSavePoints()->push(kEntityAbbot, kEntityBoutarel, kAction122358304); getEntities()->drawSequenceLeft(kEntityAbbot, "508B"); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -864,7 +864,8 @@ IMPLEMENT_FUNCTION(31, Abbot, function31) if (!params->param1) break; - UPDATE_PARAM(params->param5, getState()->time, 450); + if (!Entity::updateParameter(params->param5, getState()->time, 450)) + break; setCallback(6); setup_callbackActionRestaurantOrSalon(); @@ -920,7 +921,8 @@ IMPLEMENT_FUNCTION(31, Abbot, function31) getSavePoints()->push(kEntityAbbot, kEntityAlexei, kAction122288808); params->param1 = 1; - UPDATE_PARAM(params->param5, getState()->time, 450); + if (!Entity::updateParameter(params->param5, getState()->time, 450)) + break; setCallback(6); setup_callbackActionRestaurantOrSalon(); @@ -1163,7 +1165,8 @@ IMPLEMENT_FUNCTION(36, Abbot, function36) break; case 2: - UPDATE_PARAM(params->param4, getState()->time, 900); + if (!Entity::updateParameter(params->param4, getState()->time, 900)) + break; getSound()->playSound(kEntityAbbot, "Abb3042"); break; @@ -1287,7 +1290,7 @@ IMPLEMENT_FUNCTION_II(40, Abbot, function40, CarIndex, EntityPosition) case kActionNone: if (getEntities()->updateEntity(kEntityAbbot, (CarIndex)params->param1, (EntityPosition)params->param2)) { - CALLBACK_ACTION(); + callbackAction(); } else if (!getEvent(kEventAbbotInvitationDrink) && getEntities()->isDistanceBetweenEntities(kEntityAbbot, kEntityPlayer, 1000) && !getEntities()->isInsideCompartments(kEntityPlayer) @@ -1302,7 +1305,7 @@ IMPLEMENT_FUNCTION_II(40, Abbot, function40, CarIndex, EntityPosition) case kActionDefault: if (getEntities()->updateEntity(kEntityAbbot, (CarIndex)params->param1, (EntityPosition)params->param2)) - CALLBACK_ACTION(); + callbackAction(); break; case kActionCallback: @@ -1321,7 +1324,7 @@ IMPLEMENT_FUNCTION(41, Abbot, chapter4Handler) break; case kActionNone: - TIME_CHECK_SAVEPOINT(kTime2358000, params->param1, kEntityAbbot, kEntityServers0, kAction218128129); + Entity::timeCheckSavepoint(kTime2358000, params->param1, kEntityAbbot, kEntityServers0, kAction218128129); if (getState()->time > kTime2389500 && getEntities()->isSomebodyInsideRestaurantOrSalon()) setup_function42(); @@ -1425,10 +1428,12 @@ IMPLEMENT_FUNCTION(43, Abbot, function43) } label_callback_1: - TIME_CHECK(kTime2466000, params->param5, setup_function44); + if (Entity::timeCheck(kTime2466000, params->param5, WRAP_SETUP_FUNCTION(Abbot, setup_function44))) + break; if (params->param3) { - UPDATE_PARAM(params->param6, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param6, getState()->timeTicks, 75)) + break; params->param2 = 1; params->param3 = 0; @@ -1542,7 +1547,7 @@ IMPLEMENT_FUNCTION(45, Abbot, function45) getData()->car = kCarRedSleeping; getData()->location = kLocationOutsideCompartment; - RESET_ENTITY_STATE(kEntityVerges, Verges, setup_function38); + RESET_ENTITY_STATE(kEntityVerges, Verges, setup_resetState); getEntities()->drawSequenceLeft(kEntityAbbot, "617Ec"); getEntities()->enterCompartment(kEntityAbbot, kObjectCompartmentC, true); @@ -1646,14 +1651,14 @@ IMPLEMENT_FUNCTION(48, Abbot, function48) if (ENTITY_PARAM(0, 1)) getData()->inventoryItem = kItemInvalid; - UPDATE_PARAM_PROC(params->param1, getState()->time, 1800) + if (Entity::updateParameter(params->param1, getState()->time, 1800)) { getData()->inventoryItem = kItemNone; setCallback(4); setup_updatePosition("126C", kCarRedSleeping, 52); - UPDATE_PARAM_PROC_END + } - TIME_CHECK_CALLBACK_INVENTORY(kTime2533500, params->param2, 5, setup_callbackActionRestaurantOrSalon); + Entity::timeCheckCallbackInventory(kTime2533500, params->param2, 5, WRAP_SETUP_FUNCTION(Abbot, setup_callbackActionRestaurantOrSalon)); break; case kAction1: @@ -1705,7 +1710,7 @@ IMPLEMENT_FUNCTION(48, Abbot, function48) getEntities()->drawSequenceLeft(kEntityAbbot, "126B"); params->param1 = 0; - TIME_CHECK_CALLBACK_INVENTORY(kTime2533500, params->param2, 5, setup_callbackActionRestaurantOrSalon); + Entity::timeCheckCallbackInventory(kTime2533500, params->param2, 5, WRAP_SETUP_FUNCTION(Abbot, setup_callbackActionRestaurantOrSalon)); break; case 5: @@ -1750,7 +1755,8 @@ IMPLEMENT_FUNCTION(49, Abbot, pickBomb) break; case kActionNone: - UPDATE_PARAM(params->param1, getState()->timeTicks, 150); + if (!Entity::updateParameter(params->param1, getState()->timeTicks, 150)) + break; getSavePoints()->push(kEntityAbbot, kEntityAbbot, kAction157489665); break; diff --git a/engines/lastexpress/entities/abbot.h b/engines/lastexpress/entities/abbot.h index 462f5a491e..dc3e86db54 100644 --- a/engines/lastexpress/entities/abbot.h +++ b/engines/lastexpress/entities/abbot.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_ABBOT_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { @@ -157,10 +156,10 @@ public: * Handle Chapter 3 events */ DECLARE_FUNCTION(chapter3Handler) - DECLARE_FUNCTION(function19) - DECLARE_FUNCTION(function20) - DECLARE_FUNCTION(function21) - DECLARE_FUNCTION(function22) + DECLARE_FUNCTION(conversationWithBoutarel) + DECLARE_FUNCTION(readPaper) + DECLARE_FUNCTION(goToLunch) + DECLARE_FUNCTION(haveLunch) DECLARE_FUNCTION(function23) DECLARE_FUNCTION(function24) DECLARE_FUNCTION(function25) diff --git a/engines/lastexpress/entities/alexei.cpp b/engines/lastexpress/entities/alexei.cpp index 54c2d87b89..115c890f6f 100644 --- a/engines/lastexpress/entities/alexei.cpp +++ b/engines/lastexpress/entities/alexei.cpp @@ -31,9 +31,6 @@ #include "lastexpress/game/scenes.h" #include "lastexpress/game/state.h" -#include "lastexpress/sound/sound.h" - -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" namespace LastExpress { @@ -207,7 +204,7 @@ IMPLEMENT_FUNCTION(13, Alexei, function13) getData()->entityPosition = kPosition_7500; getEntities()->clearSequences(kEntityAlexei); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -242,7 +239,7 @@ IMPLEMENT_FUNCTION(14, Alexei, function14) getObjects()->update(kObjectCompartment2, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand); getEntities()->exitCompartment(kEntityAlexei, kObjectCompartment2, true); - CALLBACK_ACTION(); + callbackAction(); break; } IMPLEMENT_FUNCTION_END @@ -254,7 +251,7 @@ IMPLEMENT_FUNCTION(15, Alexei, function15) break; case kActionNone: - UPDATE_PARAM_CHECK(params->param2, getState()->time, params->param1) + if (Entity::updateParameterCheck(params->param2, getState()->time, params->param1)) { if (getEntities()->isSomebodyInsideRestaurantOrSalon()) { getData()->location = kLocationOutsideCompartment; @@ -292,7 +289,7 @@ IMPLEMENT_FUNCTION(15, Alexei, function15) getData()->location = kLocationInsideCompartment; getEntities()->drawSequenceLeft(kEntityAlexei, "103B"); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -312,12 +309,13 @@ IMPLEMENT_FUNCTION_IS(16, Alexei, function16, TimeValue) getObjects()->update(kObjectCompartment2, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand); getObjects()->update(kObjectHandleInsideBathroom, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); break; } if (params->param5) { - UPDATE_PARAM(CURRENT_PARAM(1, 1), getState()->timeTicks, 75); + if (!Entity::updateParameter(CURRENT_PARAM(1, 1), getState()->timeTicks, 75)) + break; params->param5 = 0; params->param6 = 1; @@ -448,7 +446,7 @@ IMPLEMENT_FUNCTION(17, Alexei, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler) + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(Alexei, setup_chapter1Handler)); break; case kActionDefault: @@ -485,7 +483,9 @@ IMPLEMENT_FUNCTION(18, Alexei, chapter1Handler) } if (params->param1) { - UPDATE_PARAM(params->param3, getState()->timeTicks, 90); + if (!Entity::updateParameter(params->param3, getState()->timeTicks, 90)) + break; + getScenes()->loadSceneFromPosition(kCarRestaurant, 61); } else { params->param3 = 0; @@ -689,7 +689,7 @@ IMPLEMENT_FUNCTION(21, Alexei, function21) break; case kActionNone: - UPDATE_PARAM_CHECK(params->param2, getState()->time, params->param1) + if (Entity::updateParameterCheck(params->param2, getState()->time, params->param1)) { getData()->location = kLocationOutsideCompartment; getData()->inventoryItem = kItemNone; @@ -751,7 +751,7 @@ IMPLEMENT_FUNCTION(22, Alexei, function22) break; case kActionNone: - UPDATE_PARAM_PROC(params->param2, getState()->time, params->param2) + if (Entity::updateParameter(params->param2, getState()->time, params->param2)) { if (getEntities()->isSomebodyInsideRestaurantOrSalon()) { getData()->location = kLocationOutsideCompartment; getData()->inventoryItem = kItemNone; @@ -760,7 +760,7 @@ IMPLEMENT_FUNCTION(22, Alexei, function22) setup_updatePosition("103D", kCarRestaurant, 52); break; } - UPDATE_PARAM_PROC_END + } if (params->param3 == kTimeInvalid || getState()->time <= kTime1111500) break; @@ -978,7 +978,7 @@ IMPLEMENT_FUNCTION(26, Alexei, function26) break; case kActionNone: - TIME_CHECK(kTime1512000, params->param1, setup_function27) + Entity::timeCheck(kTime1512000, params->param1, WRAP_SETUP_FUNCTION(Alexei, setup_function27)); break; case kActionDefault: @@ -1333,25 +1333,26 @@ IMPLEMENT_FUNCTION(35, Alexei, function35) case kActionNone: if (getEntities()->isInSalon(kEntityPlayer)) { - UPDATE_PARAM_PROC(params->param2, getState()->time, 2700) + if (Entity::updateParameter(params->param2, getState()->time, 2700)) { setCallback(1); setup_callbackActionRestaurantOrSalon(); break; - UPDATE_PARAM_PROC_END + } } else { params->param2 = 0; } - UPDATE_PARAM_PROC(params->param3, getState()->time, params->param1) + if (Entity::updateParameter(params->param3, getState()->time, params->param1)) { if (getEntities()->isSomebodyInsideRestaurantOrSalon()) { setCallback(3); setup_function15(); break; } - UPDATE_PARAM_PROC_END + } label_callback_3: - UPDATE_PARAM(params->param4, getState()->time, 9000); + if (!Entity::updateParameter(params->param4, getState()->time, 9000)) + break; setCallback(4); setup_callbackActionRestaurantOrSalon(); @@ -1378,7 +1379,7 @@ label_callback_3: case 2: case 5: - CALLBACK_ACTION(); + callbackAction(); break; case 3: @@ -1400,7 +1401,8 @@ IMPLEMENT_FUNCTION(36, Alexei, function36) if (params->param3 || params->param2) break; - UPDATE_PARAM(params->param4, getState()->timeTicks, params->param1); + if (!Entity::updateParameter(params->param4, getState()->timeTicks, params->param1)) + break; getEntities()->drawSequenceRight(kEntityAlexei, "124B"); @@ -1448,7 +1450,7 @@ IMPLEMENT_FUNCTION(36, Alexei, function36) break; case 4: - CALLBACK_ACTION(); + callbackAction(); break; } break; diff --git a/engines/lastexpress/entities/alexei.h b/engines/lastexpress/entities/alexei.h index 262826ae42..9792385863 100644 --- a/engines/lastexpress/entities/alexei.h +++ b/engines/lastexpress/entities/alexei.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_ALEXEI_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/alouan.cpp b/engines/lastexpress/entities/alouan.cpp index cd79870559..e834e1f7cb 100644 --- a/engines/lastexpress/entities/alouan.cpp +++ b/engines/lastexpress/entities/alouan.cpp @@ -28,9 +28,6 @@ #include "lastexpress/game/savepoint.h" #include "lastexpress/game/state.h" -#include "lastexpress/sound/sound.h" - -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" namespace LastExpress { @@ -89,22 +86,22 @@ IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// IMPLEMENT_FUNCTION(6, Alouan, compartment6) - COMPARTMENT_TO(Alouan, kObjectCompartment6, kPosition_4070, "621Cf", "621Df"); + Entity::goToCompartment(savepoint, kObjectCompartment6, kPosition_4070, "621Cf", "621Df"); IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// IMPLEMENT_FUNCTION(7, Alouan, compartment8) - COMPARTMENT_TO(Alouan, kObjectCompartment8, kPosition_2740, "621Ch", "621Dh"); + Entity::goToCompartment(savepoint, kObjectCompartment8, kPosition_2740, "621Ch", "621Dh"); IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// IMPLEMENT_FUNCTION(8, Alouan, compartment6to8) - COMPARTMENT_FROM_TO(Alouan, kObjectCompartment6, kPosition_4070, "621Bf", kObjectCompartment8, kPosition_2740, "621Ah"); + Entity::goToCompartmentFromCompartment(savepoint, kObjectCompartment6, kPosition_4070, "621Bf", kObjectCompartment8, kPosition_2740, "621Ah"); IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// IMPLEMENT_FUNCTION(9, Alouan, compartment8to6) - COMPARTMENT_FROM_TO(Alouan, kObjectCompartment8, kPosition_2740, "621Bh", kObjectCompartment6, kPosition_4070, "621Af"); + Entity::goToCompartmentFromCompartment(savepoint, kObjectCompartment8, kPosition_2740, "621Bh", kObjectCompartment6, kPosition_4070, "621Af"); IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// @@ -114,7 +111,7 @@ IMPLEMENT_FUNCTION(10, Alouan, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler); + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(Alouan, setup_chapter1Handler)); break; case kActionDefault: @@ -134,7 +131,8 @@ IMPLEMENT_FUNCTION(11, Alouan, chapter1Handler) case kActionNone: - TIME_CHECK_CALLBACK(kTime1096200, params->param1, 1, setup_compartment8to6); + if (Entity::timeCheckCallback(kTime1096200, params->param1, 1, WRAP_SETUP_FUNCTION(Alouan, setup_compartment8to6))) + break; label_callback1: if (getState()->time > kTime1162800 && !params->param2) { @@ -284,21 +282,28 @@ IMPLEMENT_FUNCTION(16, Alouan, chapter3Handler) break; case kActionNone: - TIME_CHECK_CALLBACK(kTimeCitySalzbourg, params->param1, 1, setup_compartment8to6); + if (Entity::timeCheckCallback(kTimeCitySalzbourg, params->param1, 1, WRAP_SETUP_FUNCTION(Alouan, setup_compartment8to6))) + break; label_callback1: - if (params->param2 != kTimeInvalid && getState()->time > kTime1989000) - TIME_CHECK_CAR(kTime2119500, params->param5, 5, setup_compartment8); + if (params->param2 != kTimeInvalid && getState()->time > kTime1989000) { + if (Entity::timeCheckCar(kTime2119500, params->param5, 5, WRAP_SETUP_FUNCTION(Alouan, setup_compartment8))) + break; + } label_callback2: - TIME_CHECK_CALLBACK_1(kTime2052000, params->param3, 3, setup_playSound, "Har1005"); + if (Entity::timeCheckCallback(kTime2052000, params->param3, 3, "Har1005", WRAP_SETUP_FUNCTION_S(Alouan, setup_playSound))) + break; label_callback3: - TIME_CHECK_CALLBACK(kTime2133000, params->param4, 4, setup_compartment6to8); + if (Entity::timeCheckCallback(kTime2133000, params->param4, 4, WRAP_SETUP_FUNCTION(Alouan, setup_compartment6to8))) + break; label_callback4: - if (params->param5 != kTimeInvalid && getState()->time > kTime2151000) - TIME_CHECK_CAR(kTime2241000, params->param5, 5, setup_compartment8); + if (params->param5 != kTimeInvalid && getState()->time > kTime2151000) { + if (Entity::timeCheckCar(kTime2241000, params->param5, 5, WRAP_SETUP_FUNCTION(Alouan, setup_compartment8))) + break; + } break; case kActionDefault: @@ -355,11 +360,14 @@ IMPLEMENT_FUNCTION(18, Alouan, chapter4Handler) break; case kActionNone: - if (params->param1 != kTimeInvalid) - TIME_CHECK_CAR(kTime2443500, params->param1, 1, setup_compartment8); + if (params->param1 != kTimeInvalid) { + if (Entity::timeCheckCar(kTime2443500, params->param1, 1, WRAP_SETUP_FUNCTION(Alouan, setup_compartment8))) + break; + } label_callback1: - TIME_CHECK_CALLBACK(kTime2455200, params->param2, 2, setup_compartment8to6); + if (Entity::timeCheckCallback(kTime2455200, params->param2, 2, WRAP_SETUP_FUNCTION(Alouan, setup_compartment8to6))) + break; label_callback2: if (getState()->time > kTime2475000 && !params->param3) { @@ -441,7 +449,9 @@ IMPLEMENT_FUNCTION(22, Alouan, function22) break; case kActionNone: - UPDATE_PARAM(params->param1, getState()->time, 2700); + if (!Entity::updateParameter(params->param1, getState()->time, 2700)) + break; + setup_function23(); break; diff --git a/engines/lastexpress/entities/alouan.h b/engines/lastexpress/entities/alouan.h index c6a6beddd9..91254a449a 100644 --- a/engines/lastexpress/entities/alouan.h +++ b/engines/lastexpress/entities/alouan.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_ALOUAN_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/anna.cpp b/engines/lastexpress/entities/anna.cpp index b13aa21f6d..f8768032b5 100644 --- a/engines/lastexpress/entities/anna.cpp +++ b/engines/lastexpress/entities/anna.cpp @@ -34,9 +34,7 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" namespace LastExpress { @@ -200,16 +198,17 @@ IMPLEMENT_FUNCTION(12, Anna, function12) params->param2 = 1; if (params->param6) { - UPDATE_PARAM_PROC(params->param7, getState()->timeTicks, 75) + if (Entity::updateParameter(params->param7, getState()->timeTicks, 75)) { getSavePoints()->push(kEntityAnna, kEntityAnna, kActionEndSound); params->param6 = 0; params->param7 = 0; - UPDATE_PARAM_PROC_END + } } if (params->param4) { - UPDATE_PARAM(params->param8, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param8, getState()->timeTicks, 75)) + break; params->param4 = 0; params->param5 = 1; @@ -227,7 +226,7 @@ IMPLEMENT_FUNCTION(12, Anna, function12) case kActionEndSound: if (params->param2) { - CALLBACK_ACTION(); + callbackAction(); break; } @@ -289,7 +288,7 @@ IMPLEMENT_FUNCTION(12, Anna, function12) getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand); getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -424,12 +423,13 @@ IMPLEMENT_FUNCTION_IS(15, Anna, function15, TimeValue) getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand); getObjects()->update(kObject53, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); break; } if (params->param5) { - UPDATE_PARAM(params->param8, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param8, getState()->timeTicks, 75)) + break; params->param5 = 0; params->param6 = 1; @@ -547,7 +547,7 @@ IMPLEMENT_FUNCTION(16, Anna, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler); + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(Anna, setup_chapter1Handler)); break; case kActionDefault: @@ -577,7 +577,7 @@ IMPLEMENT_FUNCTION_II(17, Anna, function17, uint32, uint32) if (getEntities()->updateEntity(kEntityAnna, (CarIndex)params->param1, (EntityPosition)params->param2)) { getData()->inventoryItem = kItemNone; - CALLBACK_ACTION(); + callbackAction(); } break; @@ -615,7 +615,7 @@ IMPLEMENT_FUNCTION_II(17, Anna, function17, uint32, uint32) } if (getEntities()->updateEntity(kEntityAnna, (CarIndex)params->param1, (EntityPosition)params->param2)) - CALLBACK_ACTION(); + callbackAction(); break; case kActionCallback: @@ -664,20 +664,21 @@ IMPLEMENT_FUNCTION_I(18, Anna, function18, TimeValue) case kActionNone: if (params->param1 && params->param1 < getState()->time && getEntities()->isSomebodyInsideRestaurantOrSalon()) { getData()->inventoryItem = kItemNone; - CALLBACK_ACTION(); + callbackAction(); break; } if (params->param5 && !params->param4) { - UPDATE_PARAM_PROC(params->param6, getState()->time, 900) + if (Entity::updateParameter(params->param6, getState()->time, 900)) { params->param2 |= kItemScarf; params->param5 = 0; params->param6 = 0; - UPDATE_PARAM_PROC_END + } } if (params->param3) { - UPDATE_PARAM(params->param7, getState()->timeTicks, 90); + if (!Entity::updateParameter(params->param7, getState()->timeTicks, 90)) + break; getScenes()->loadSceneFromPosition(kCarRestaurant, 61); } else { @@ -757,7 +758,7 @@ IMPLEMENT_FUNCTION_I(18, Anna, function18, TimeValue) case kAction259136835: case kAction268773672: getData()->inventoryItem = kItemNone; - CALLBACK_ACTION(); + callbackAction(); break; } IMPLEMENT_FUNCTION_END @@ -1151,15 +1152,16 @@ IMPLEMENT_FUNCTION(29, Anna, function29) case kActionNone: if (params->param2) { - UPDATE_PARAM_PROC(params->param3, getState()->time, 900) + if (Entity::updateParameter(params->param3, getState()->time, 900)) { getData()->inventoryItem = (InventoryItem)(getData()->inventoryItem | kItemScarf); params->param2 = 0; params->param3 = 0; - UPDATE_PARAM_PROC_END + } } if (params->param1) { - UPDATE_PARAM(params->param4, getState()->timeTicks, 90); + if (!Entity::updateParameter(params->param4, getState()->timeTicks, 90)) + break; getScenes()->loadSceneFromPosition(kCarRestaurant, 55); } else { @@ -1281,7 +1283,9 @@ IMPLEMENT_FUNCTION(30, Anna, function30) } if (params->param1) { - UPDATE_PARAM(params->param5, getState()->timeTicks, 90); + if (!Entity::updateParameter(params->param5, getState()->timeTicks, 90)) + break; + getScenes()->loadSceneFromPosition(kCarRestaurant, 55); } else { params->param5 = 0; @@ -1411,15 +1415,15 @@ IMPLEMENT_FUNCTION(34, Anna, function34) case kActionNone: if (!params->param1 && getEntities()->isPlayerPosition(kCarRedSleeping, 60)) { - UPDATE_PARAM_PROC(params->param2, getState()->time, 150) + if (Entity::updateParameter(params->param2, getState()->time, 150)) { setCallback(1); setup_draw("419B"); break; - UPDATE_PARAM_PROC_END + } } label_callback_1: - TIME_CHECK(kTime1489500, params->param3, setup_function35); + Entity::timeCheck(kTime1489500, params->param3, WRAP_SETUP_FUNCTION(Anna, setup_function35)); break; case kActionKnock: @@ -1485,7 +1489,8 @@ IMPLEMENT_FUNCTION(35, Anna, function35) if (!params->param1) break; - UPDATE_PARAM(params->param3, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param3, getState()->timeTicks, 75)) + break; switch (params->param2) { default: @@ -1666,7 +1671,7 @@ IMPLEMENT_FUNCTION_II(39, Anna, function39, CarIndex, EntityPosition) if (getEntities()->updateEntity(kEntityAnna, (CarIndex)params->param1, (EntityPosition)params->param2)) { getData()->inventoryItem = kItemNone; - CALLBACK_ACTION(); + callbackAction(); } break; @@ -1687,7 +1692,7 @@ IMPLEMENT_FUNCTION_II(39, Anna, function39, CarIndex, EntityPosition) if (getEntities()->updateEntity(kEntityAnna, (CarIndex)params->param1, (EntityPosition)params->param2)) { getData()->inventoryItem = kItemNone; - CALLBACK_ACTION(); + callbackAction(); } break; @@ -1799,7 +1804,8 @@ IMPLEMENT_FUNCTION(41, Anna, function41) break; case kActionNone: - UPDATE_PARAM(params->param2, getState()->time, 2700); + if (!Entity::updateParameter(params->param2, getState()->time, 2700)) + break; params->param5++; switch (params->param5) { @@ -1985,7 +1991,7 @@ IMPLEMENT_FUNCTION_I(45, Anna, function45, bool) case 2: getEntities()->exitCompartment(kEntityAnna, kObjectCompartmentF, true); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -2092,11 +2098,11 @@ IMPLEMENT_FUNCTION(48, Anna, function48) break; if (params->param3 != kTimeInvalid && getState()->time > kTime1969200) { - UPDATE_PARAM_PROC_TIME(kTime1983600, (!getEntities()->isInRestaurant(kEntityPlayer) || getSoundQueue()->isBuffered(kEntityBoutarel)), params->param3, 150) + if (Entity::updateParameterTime(kTime1983600, (!getEntities()->isInRestaurant(kEntityPlayer) || getSoundQueue()->isBuffered(kEntityBoutarel)), params->param3, 150)) { setCallback(3); setup_playSound("Aug3007A"); break; - UPDATE_PARAM_PROC_END + } } label_callback_4: @@ -2195,7 +2201,7 @@ IMPLEMENT_FUNCTION(49, Anna, leaveTableWithAugust) getSavePoints()->push(kEntityAnna, kEntityTables3, kActionDrawTablesWithChairs, "010M"); getEntities()->clearSequences(kEntityAugust); - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: @@ -2386,22 +2392,23 @@ IMPLEMENT_FUNCTION(53, Anna, function53) case kActionNone: if (getProgress().field_48 && params->param5 != kTimeInvalid) { - UPDATE_PARAM_PROC_TIME(kTime2065500, !getEntities()->isPlayerInCar(kCarRedSleeping), params->param5, 150) + if (Entity::updateParameterTime(kTime2065500, !getEntities()->isPlayerInCar(kCarRedSleeping), params->param5, 150)) { setup_function54(); break; - UPDATE_PARAM_PROC_END + } } if (params->param3) { - UPDATE_PARAM_PROC(params->param6, getState()->time, 9000) + if (Entity::updateParameter(params->param6, getState()->time, 9000)) { params->param4 = !params->param4; getEntities()->drawSequenceLeft(kEntityAnna, params->param4 ? "417B" : "417A"); params->param6 = 0; - UPDATE_PARAM_PROC_END + } } if (params->param1) { - UPDATE_PARAM(params->param7, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param7, getState()->timeTicks, 75)) + break; CursorStyle cursor = getEntities()->isInsideCompartment(kEntityMax, kCarRedSleeping, kPosition_4070) ? kCursorHand : kCursorNormal; @@ -2537,17 +2544,19 @@ IMPLEMENT_FUNCTION(54, Anna, function54) case kActionNone: if (params->param3) { - TIME_CHECK(kTime2079000, params->param5, setup_function55); + if (Entity::timeCheck(kTime2079000, params->param5, WRAP_SETUP_FUNCTION(Anna, setup_function55))) + break; - UPDATE_PARAM_PROC(params->param6, getState()->time, 9000) + if (Entity::updateParameter(params->param6, getState()->time, 9000)) { params->param4 = !params->param4; getEntities()->drawSequenceLeft(kEntityAnna, params->param4 ? "417B" : "417A"); params->param6 = 0; - UPDATE_PARAM_PROC_END + } } if (params->param1) { - UPDATE_PARAM(params->param7, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param7, getState()->timeTicks, 75)) + break; CursorStyle cursor = getEntities()->isInsideCompartment(kEntityMax, kCarRedSleeping, kPosition_4070) ? kCursorHand : kCursorNormal; @@ -2894,7 +2903,8 @@ IMPLEMENT_FUNCTION(59, Anna, function59) } if (params->param1) { - UPDATE_PARAM(params->param5, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param5, getState()->timeTicks, 75)) + break; CursorStyle style = getEntities()->isInsideCompartment(kEntityMax, kCarRedSleeping, kPosition_4070) ? kCursorHand : kCursorNormal; getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, style); @@ -3040,7 +3050,7 @@ IMPLEMENT_FUNCTION(60, Anna, function60) getData()->location = kLocationInsideCompartment; getEntities()->clearSequences(kEntityAnna); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -3268,7 +3278,8 @@ IMPLEMENT_FUNCTION(67, Anna, chapter4Handler) case kActionNone: if (getEntities()->isPlayerPosition(kCarRedSleeping, 46)) { - UPDATE_PARAM_GOTO(params->param4, getState()->timeTicks, 30, label_next); + if (!Entity::updateParameter(params->param4, getState()->timeTicks, 30)) + goto label_next; getScenes()->loadSceneFromPosition(kCarRedSleeping, 8); } @@ -3277,7 +3288,8 @@ IMPLEMENT_FUNCTION(67, Anna, chapter4Handler) label_next: if (params->param1) { - UPDATE_PARAM(params->param5, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param5, getState()->timeTicks, 75)) + break; params->param1 = 0; params->param2 = 1; @@ -3407,7 +3419,8 @@ IMPLEMENT_FUNCTION(69, Anna, function69) case kActionNone: if (params->param1) { - UPDATE_PARAM(params->param2, getState()->time, 4500); + if (!Entity::updateParameter(params->param2, getState()->time, 4500)) + break; getData()->car = kCarRedSleeping; getData()->entityPosition = kPosition_9270; @@ -3417,7 +3430,7 @@ IMPLEMENT_FUNCTION(69, Anna, function69) break; } - TIME_CHECK_CALLBACK(kTime2535300, params->param3, 4, setup_callbackActionRestaurantOrSalon); + Entity::timeCheckCallback(kTime2535300, params->param3, 4, WRAP_SETUP_FUNCTION(Anna, setup_callbackActionRestaurantOrSalon)); break; case kActionDefault: @@ -3533,7 +3546,7 @@ IMPLEMENT_FUNCTION(71, Anna, function71) getEntities()->exitCompartment(kEntityAnna, kObjectCompartmentF); getData()->entityPosition = kPosition_4070; - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: @@ -3589,7 +3602,7 @@ IMPLEMENT_FUNCTION_II(72, Anna, function72, CarIndex, EntityPosition) if (getEntities()->updateEntity(kEntityAnna, (CarIndex)params->param1, (EntityPosition)params->param2)) { getData()->inventoryItem = kItemNone; - CALLBACK_ACTION(); + callbackAction(); } break; @@ -3602,7 +3615,7 @@ IMPLEMENT_FUNCTION_II(72, Anna, function72, CarIndex, EntityPosition) case kActionDefault: if (getEntities()->updateEntity(kEntityAnna, (CarIndex)params->param1, (EntityPosition)params->param2)) { - CALLBACK_ACTION(); + callbackAction(); } else if (!getEvent(kEventAnnaTired)) getData()->inventoryItem = kItemInvalid; break; @@ -3911,7 +3924,8 @@ IMPLEMENT_FUNCTION(80, Anna, function80) break; case kActionNone: - UPDATE_PARAM(params->param1, getState()->timeTicks, 450); + if (!Entity::updateParameter(params->param1, getState()->timeTicks, 450)) + break; getSound()->playSound(kEntityPlayer, "Kro5001", kFlagDefault); break; @@ -3980,7 +3994,8 @@ IMPLEMENT_FUNCTION(81, Anna, finalSequence) break; case kActionNone: - UPDATE_PARAM(params->param1, getState()->timeTicks, 180); + if (!Entity::updateParameter(params->param1, getState()->timeTicks, 180)) + break; getSound()->playSound(kEntityTrain, "LIB069"); getLogic()->gameOver(kSavegameTypeIndex, 2, kSceneNone, true); diff --git a/engines/lastexpress/entities/anna.h b/engines/lastexpress/entities/anna.h index 72c6db4bd9..205ff9d42c 100644 --- a/engines/lastexpress/entities/anna.h +++ b/engines/lastexpress/entities/anna.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_ANNA_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/august.cpp b/engines/lastexpress/entities/august.cpp index cfde8a2d6f..dbae7bad20 100644 --- a/engines/lastexpress/entities/august.cpp +++ b/engines/lastexpress/entities/august.cpp @@ -36,9 +36,7 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" namespace LastExpress { @@ -150,7 +148,7 @@ IMPLEMENT_FUNCTION_END IMPLEMENT_FUNCTION_SI(7, August, enterExitCompartment3, ObjectIndex) if (savepoint.action == kAction4) { getEntities()->exitCompartment(kEntityAugust, (ObjectIndex)params->param4); - CALLBACK_ACTION(); + callbackAction(); return; } @@ -177,7 +175,7 @@ IMPLEMENT_FUNCTION_IIS(10, August, callSavepointNoDrawing, EntityIndex, ActionIn if (!params->param6) getSavePoints()->call(kEntityAugust, (EntityIndex)params->param1, (ActionIndex)params->param2, (char *)¶ms->seq); - CALLBACK_ACTION(); + callbackAction(); break; case kAction10: @@ -233,7 +231,7 @@ IMPLEMENT_FUNCTION_I(17, August, function17, TimeValue) case kActionNone: if (params->param1 < getState()->time && !params->param2) { params->param2 = 1; - CALLBACK_ACTION(); + callbackAction(); break; } @@ -262,7 +260,7 @@ IMPLEMENT_FUNCTION_I(17, August, function17, TimeValue) case 1: if (ENTITY_PARAM(0, 1)) { - CALLBACK_ACTION(); + callbackAction(); break; } @@ -272,7 +270,7 @@ IMPLEMENT_FUNCTION_I(17, August, function17, TimeValue) case 2: case 3: if (ENTITY_PARAM(0, 1)) { - CALLBACK_ACTION(); + callbackAction(); break; } @@ -289,7 +287,7 @@ IMPLEMENT_FUNCTION_I(17, August, function17, TimeValue) case 5: if (ENTITY_PARAM(0, 1)) { - CALLBACK_ACTION(); + callbackAction(); break; } @@ -308,7 +306,7 @@ IMPLEMENT_FUNCTION_II(18, August, updateEntity2, CarIndex, EntityPosition) case kActionNone: if (getEntities()->updateEntity(_entityIndex, (CarIndex)params->param1, (EntityPosition)params->param2)) { - CALLBACK_ACTION(); + callbackAction(); } else if (getEntities()->isDistanceBetweenEntities(kEntityAugust, kEntityPlayer, 1000) && !getEntities()->isInGreenCarEntrance(kEntityPlayer) && !getEntities()->isInsideCompartments(kEntityPlayer) @@ -316,14 +314,14 @@ IMPLEMENT_FUNCTION_II(18, August, updateEntity2, CarIndex, EntityPosition) if (getData()->car == kCarGreenSleeping || getData()->car == kCarRedSleeping) { ENTITY_PARAM(0, 1) = 1; - CALLBACK_ACTION(); + callbackAction(); } } break; case kActionDefault: if (getEntities()->updateEntity(_entityIndex, (CarIndex)params->param1, (EntityPosition)params->param2)) - CALLBACK_ACTION(); + callbackAction(); break; } IMPLEMENT_FUNCTION_END @@ -407,7 +405,7 @@ IMPLEMENT_FUNCTION_II(19, August, function19, bool, bool) getData()->location = kLocationInsideCompartment; getEntities()->clearSequences(kEntityAugust); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -503,7 +501,7 @@ IMPLEMENT_FUNCTION_I(20, August, function20, bool) getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand); getEntities()->exitCompartment(kEntityAugust, kObjectCompartment3, true); - CALLBACK_ACTION(); + callbackAction(); break; } IMPLEMENT_FUNCTION_END @@ -520,12 +518,13 @@ IMPLEMENT_FUNCTION_I(21, August, function21, TimeValue) getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); break; } if (params->param2) { - UPDATE_PARAM_GOTO(params->param8, getState()->timeTicks, 75, label_continue); + if (!Entity::updateParameter(params->param8, getState()->timeTicks, 75)) + goto label_continue; params->param2 = 0; params->param3 = 1; @@ -540,10 +539,10 @@ label_continue: break; if (params->param6) { - UPDATE_PARAM_PROC(CURRENT_PARAM(1, 1), getState()->time, 6300) + if (Entity::updateParameter(CURRENT_PARAM(1, 1), getState()->time, 6300)) { params->param6 = 0; CURRENT_PARAM(1, 1) = 0; - UPDATE_PARAM_PROC_END + } } if (!params->param4 @@ -753,7 +752,7 @@ IMPLEMENT_FUNCTION(22, August, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler); + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(August, setup_chapter1Handler)); break; case kActionDefault: @@ -786,7 +785,7 @@ IMPLEMENT_FUNCTION_I(23, August, function23, TimeValue) } else { getEntities()->exitCompartment(kEntityAugust, kObjectCompartment1, true); getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); } break; } @@ -806,7 +805,7 @@ IMPLEMENT_FUNCTION_I(23, August, function23, TimeValue) } label_callback_8: - UPDATE_PARAM_PROC(CURRENT_PARAM(1, 4), getState()->timeTicks, 75) + if (Entity::updateParameter(CURRENT_PARAM(1, 4), getState()->timeTicks, 75)) { getEntities()->exitCompartment(kEntityAugust, kObjectCompartment1, true); if (getProgress().eventCorpseMovedFromFloor) { @@ -822,7 +821,7 @@ label_callback_8: setup_savegame(kSavegameTypeEvent, kEventAugustFindCorpse); } break; - UPDATE_PARAM_PROC_END + } label_callback_9: if (params->param3 && params->param1 < getState()->time && !CURRENT_PARAM(1, 5)) { @@ -842,7 +841,8 @@ label_callback_9: break; if (getObjects()->get(kObjectCompartment1).location == kObjectLocation1) { - UPDATE_PARAM(CURRENT_PARAM(1, 2), getState()->timeTicks, 75); + if (!Entity::updateParameter(CURRENT_PARAM(1, 2), getState()->timeTicks, 75)) + break; getObjects()->update(kObjectCompartment1, kEntityAugust, getObjects()->get(kObjectCompartment1).location, kCursorNormal, kCursorNormal); @@ -867,7 +867,7 @@ label_callback_9: if (params->param8 >= 3) { getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); break; } @@ -959,7 +959,7 @@ label_callback_9: case 2: getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); break; case 3: @@ -986,7 +986,7 @@ label_callback_9: getScenes()->loadScene(kScene41); - CALLBACK_ACTION(); + callbackAction(); break; case 5: @@ -1028,7 +1028,7 @@ label_callback_9: case 12: getData()->location = kLocationOutsideCompartment; - CALLBACK_ACTION(); + callbackAction(); break; case 13: @@ -1056,7 +1056,7 @@ label_callback_9: getScenes()->loadScene(kScene41); - CALLBACK_ACTION(); + callbackAction(); break; case 15: @@ -1096,7 +1096,7 @@ IMPLEMENT_FUNCTION(24, August, dinner) getScenes()->loadSceneFromPosition(kCarRestaurant, 61); - CALLBACK_ACTION(); + callbackAction(); } break; } @@ -1485,7 +1485,8 @@ IMPLEMENT_FUNCTION(30, August, restaurant) break; case kActionNone: - UPDATE_PARAM(params->param3, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param3, getState()->timeTicks, 75)) + break; getData()->inventoryItem = kItemInvalid; break; @@ -1634,9 +1635,9 @@ IMPLEMENT_FUNCTION(32, August, function32) break; case kActionNone: - UPDATE_PARAM_PROC_TIME(kTime1179000, (!getEntities()->isInSalon(kEntityAnna) || getEntities()->isInSalon(kEntityPlayer)), params->param6, 0); + if (Entity::updateParameterTime(kTime1179000, (!getEntities()->isInSalon(kEntityAnna) || getEntities()->isInSalon(kEntityPlayer)), params->param6, 0)) { getSavePoints()->push(kEntityAugust, kEntityAnna, kAction123712592); - UPDATE_PARAM_PROC_END + } if (params->param1 && getEntities()->isSomebodyInsideRestaurantOrSalon()) { if (!params->param4) { @@ -1645,18 +1646,19 @@ IMPLEMENT_FUNCTION(32, August, function32) } if (params->param7 != kTimeInvalid && params->param4 < getState()->time) { - UPDATE_PARAM_PROC_TIME(params->param5, getEntities()->isInSalon(kEntityPlayer), params->param7, 0); + if (Entity::updateParameterTime((TimeValue)params->param5, getEntities()->isInSalon(kEntityPlayer), params->param7, 0)) { getData()->location = kLocationOutsideCompartment; setCallback(5); setup_updatePosition("109D", kCarRestaurant, 56); break; - UPDATE_PARAM_PROC_END + } } } if (params->param3) { - UPDATE_PARAM(params->param8, getState()->timeTicks, 90); + if (!Entity::updateParameter(params->param8, getState()->timeTicks, 90)) + break; getScenes()->loadSceneFromPosition(kCarRestaurant, 55); } else { @@ -1813,7 +1815,7 @@ IMPLEMENT_FUNCTION(36, August, chapter2Handler) break; case kActionNone: - TIME_CHECK_SAVEPOINT(kTime1755000, params->param2, kEntityAugust, kEntityServers0, kAction252568704); + Entity::timeCheckSavepoint(kTime1755000, params->param2, kEntityAugust, kEntityServers0, kAction252568704); if (getState()->time > kTime1773000 && params->param1 && getEntities()->isSomebodyInsideRestaurantOrSalon()) { getData()->inventoryItem = kItemNone; @@ -1904,7 +1906,7 @@ IMPLEMENT_FUNCTION(37, August, function37) break; case kActionNone: - TIME_CHECK_CALLBACK_1(kTime1791000, params->param2, 5, setup_function20, true); + Entity::timeCheckCallback(kTime1791000, params->param2, 5, true, WRAP_SETUP_FUNCTION_B(August, setup_function20)); break; case kActionDefault: @@ -1962,9 +1964,9 @@ IMPLEMENT_FUNCTION(38, August, function38) break; case kActionNone: - TIME_CHECK_SAVEPOINT(kTime1801800, params->param1, kEntityAugust, kEntityRebecca, kAction155980128); + Entity::timeCheckSavepoint(kTime1801800, params->param1, kEntityAugust, kEntityRebecca, kAction155980128); - TIME_CHECK_CALLBACK(kTime1820700, params->param2, 3, setup_callbackActionRestaurantOrSalon); + Entity::timeCheckCallback(kTime1820700, params->param2, 3, WRAP_SETUP_FUNCTION(August, setup_callbackActionRestaurantOrSalon)); break; case kActionDefault: @@ -2110,7 +2112,7 @@ IMPLEMENT_FUNCTION_II(41, August, function41, CarIndex, EntityPosition) getData()->inventoryItem = kItemNone; if (getEntities()->updateEntity(kEntityAugust, (CarIndex)params->param1, (EntityPosition)params->param2)) { - CALLBACK_ACTION(); + callbackAction(); break; } @@ -2147,7 +2149,7 @@ IMPLEMENT_FUNCTION_II(41, August, function41, CarIndex, EntityPosition) case kActionDefault: if (getEntities()->updateEntity(kEntityAugust, (CarIndex)params->param1, (EntityPosition)params->param2)) { - CALLBACK_ACTION(); + callbackAction(); break; } @@ -2172,7 +2174,7 @@ IMPLEMENT_FUNCTION_III(42, August, function42, CarIndex, EntityPosition, bool) if (getEntities()->updateEntity(kEntityAugust, (CarIndex)params->param1, (EntityPosition)params->param2)) { getData()->inventoryItem = kItemNone; - CALLBACK_ACTION(); + callbackAction(); } break; @@ -2191,7 +2193,7 @@ IMPLEMENT_FUNCTION_III(42, August, function42, CarIndex, EntityPosition, bool) case kActionDefault: if (getEntities()->updateEntity(kEntityAugust, (CarIndex)params->param1, (EntityPosition)params->param2)) { - CALLBACK_ACTION(); + callbackAction(); break; } @@ -2212,7 +2214,7 @@ IMPLEMENT_FUNCTION(43, August, chapter3Handler) break; case kActionNone: - TIME_CHECK_SAVEPOINT(kTime1953000, params->param2, kEntityAugust, kEntityAnna, kAction291662081); + Entity::timeCheckSavepoint(kTime1953000, params->param2, kEntityAugust, kEntityAnna, kAction291662081); // Set as same position as Anna if (params->param1) { @@ -2402,7 +2404,7 @@ IMPLEMENT_FUNCTION_END IMPLEMENT_FUNCTION(46, August, function46) switch (savepoint.action) { default: - TIME_CHECK_CALLBACK(kTime2088000, params->param1, 1, setup_function47); + Entity::timeCheckCallback(kTime2088000, params->param1, 1, WRAP_SETUP_FUNCTION(August, setup_function47)); break; case kActionNone: @@ -2473,7 +2475,7 @@ IMPLEMENT_FUNCTION(47, August, function47) break; case 5: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -2487,7 +2489,7 @@ IMPLEMENT_FUNCTION(48, August, function48) break; case kActionNone: - TIME_CHECK(kTimeCityLinz, params->param1, setup_function49); + Entity::timeCheck(kTimeCityLinz, params->param1, WRAP_SETUP_FUNCTION(August, setup_function49)); break; case kActionKnock: @@ -2770,7 +2772,8 @@ IMPLEMENT_FUNCTION(54, August, function54) getData()->inventoryItem = kItemInvalid; if (!params->param2 && params->param1) { - UPDATE_PARAM(params->param5, getState()->time, params->param1); + if (!Entity::updateParameter(params->param5, getState()->time, params->param1)) + break; getData()->inventoryItem = kItemNone; setup_function55(); @@ -3046,7 +3049,8 @@ IMPLEMENT_FUNCTION(60, August, function60) if (!params->param1) break; - UPDATE_PARAM(params->param3, getState()->time, 9000); + if (!Entity::updateParameter(params->param3, getState()->time, 9000)) + break; setCallback(1); setup_callbackActionRestaurantOrSalon(); @@ -3142,7 +3146,8 @@ IMPLEMENT_FUNCTION(62, August, function62) break; case kActionNone: - UPDATE_PARAM(params->param1, getState()->time, 900); + if (!Entity::updateParameter(params->param1, getState()->time, 900)) + break; getSound()->playSound(kEntityAugust, "Aug4003A"); @@ -3220,9 +3225,9 @@ IMPLEMENT_FUNCTION(63, August, function63) break; case kActionNone: - UPDATE_PARAM_PROC(params->param3, getState()->time, 1800) + if (Entity::updateParameter(params->param3, getState()->time, 1800)) { getData()->inventoryItem = kItemInvalid; - UPDATE_PARAM_PROC_END + } if (getState()->time > kTime2488500 && !params->param4) { params->param4 = 1; @@ -3231,7 +3236,8 @@ IMPLEMENT_FUNCTION(63, August, function63) break; } - UPDATE_PARAM(params->param5, getState()->timeTicks, params->param1); + if (!Entity::updateParameter(params->param5, getState()->timeTicks, params->param1)) + break; params->param2 = (params->param6 < 1 ? 1 : 0); @@ -3388,7 +3394,8 @@ IMPLEMENT_FUNCTION(68, August, function68) case kActionNone: if (params->param1) { - UPDATE_PARAM(params->param4, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param4, getState()->timeTicks, 75)) + break; params->param1 = 0; params->param2 = 1; @@ -3523,7 +3530,7 @@ IMPLEMENT_FUNCTION(69, August, unhookCars) getScenes()->loadSceneFromPosition(kCarRestaurant, 85, 1); getSavePoints()->pushAll(kEntityAugust, kActionProceedChapter5); - RESET_ENTITY_STATE(kEntityVerges, Verges, setup_function42) + RESET_ENTITY_STATE(kEntityVerges, Verges, setup_end) } break; } diff --git a/engines/lastexpress/entities/august.h b/engines/lastexpress/entities/august.h index 2cbb31b968..606321955b 100644 --- a/engines/lastexpress/entities/august.h +++ b/engines/lastexpress/entities/august.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_AUGUST_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/boutarel.cpp b/engines/lastexpress/entities/boutarel.cpp index 315b12a69e..219ddf901b 100644 --- a/engines/lastexpress/entities/boutarel.cpp +++ b/engines/lastexpress/entities/boutarel.cpp @@ -32,9 +32,7 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" namespace LastExpress { @@ -231,7 +229,7 @@ IMPLEMENT_FUNCTION_I(11, Boutarel, function11, bool) case 7: getData()->location = kLocationInsideCompartment; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -249,7 +247,7 @@ IMPLEMENT_FUNCTION(12, Boutarel, enterTableWithMmeBoutarel) getSavePoints()->push(kEntityBoutarel, kEntityTables2, kAction136455232); getData()->location = kLocationInsideCompartment; - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: @@ -276,7 +274,7 @@ IMPLEMENT_FUNCTION(13, Boutarel, leaveTableWithMmeBoutarel) getSavePoints()->push(kEntityBoutarel, kEntityTables2, kActionDrawTablesWithChairs, "008F"); getEntities()->clearSequences(kEntityMmeBoutarel); - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: @@ -358,7 +356,7 @@ IMPLEMENT_FUNCTION_I(14, Boutarel, function14, bool) getEntities()->clearSequences(kEntityBoutarel); getData()->location = kLocationInsideCompartment; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -416,7 +414,7 @@ IMPLEMENT_FUNCTION_IS(15, Boutarel, function15, bool) case 5: getData()->location = kLocationInsideCompartment; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -464,7 +462,7 @@ IMPLEMENT_FUNCTION_IS(16, Boutarel, function16, bool) getData()->location = kLocationInsideCompartment; getEntities()->clearSequences(kEntityBoutarel); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -478,10 +476,13 @@ IMPLEMENT_FUNCTION_IS(17, Boutarel, function17, TimeValue) break; case kActionNone: - TIME_CHECK_CALLBACK_ACTION(params->param1, params->param6); + if (Entity::timeCheckCallbackAction((TimeValue)params->param1, params->param6)) + break; if (params->param5) { - UPDATE_PARAM(params->param7, getState()->timeTicks, 90) + if (!Entity::updateParameter(params->param7, getState()->timeTicks, 90)) + break; + getScenes()->loadSceneFromPosition(kCarRestaurant, 51); } else { params->param7 = 0; @@ -511,12 +512,13 @@ IMPLEMENT_FUNCTION_I(18, Boutarel, function18, TimeValue) getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand); getObjects()->update(kObject50, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); break; } if (params->param2) { - UPDATE_PARAM(params->param5, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param5, getState()->timeTicks, 75)) + break; params->param2 = 0; params->param3 = 1; @@ -615,7 +617,7 @@ IMPLEMENT_FUNCTION(19, Boutarel, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler); + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(Boutarel, setup_chapter1Handler)); break; case kActionDefault: @@ -644,14 +646,14 @@ IMPLEMENT_FUNCTION(20, Boutarel, function20) break; if (!params->param2) { - UPDATE_PARAM_PROC(params->param3, getState()->time, 4500) + if (Entity::updateParameter(params->param3, getState()->time, 4500)) { setCallback(3); setup_playSound("MRB1078A"); break; - UPDATE_PARAM_PROC_END + } } - TIME_CHECK_CALLBACK_1(kTime1138500, params->param4, 4, setup_function14, false); + Entity::timeCheckCallback(kTime1138500, params->param4, 4, false, WRAP_SETUP_FUNCTION_B(Boutarel, setup_function14)); break; case kActionDefault: @@ -676,13 +678,13 @@ IMPLEMENT_FUNCTION(20, Boutarel, function20) break; case 3: - TIME_CHECK_CALLBACK_1(kTime1138500, params->param4, 4, setup_function14, false); + Entity::timeCheckCallback(kTime1138500, params->param4, 4, false, WRAP_SETUP_FUNCTION_B(Boutarel, setup_function14)); break; case 4: getSavePoints()->push(kEntityBoutarel, kEntityCooks, kAction224849280); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -834,7 +836,7 @@ IMPLEMENT_FUNCTION(24, Boutarel, chapter2Handler) break; case kActionNone: - TIME_CHECK_CALLBACK_1(kTime1759500, params->param2, 1, setup_function14, false); + Entity::timeCheckCallback(kTime1759500, params->param2, 1, false, WRAP_SETUP_FUNCTION_B(Boutarel, setup_function14)); break; case kActionDefault: @@ -934,9 +936,9 @@ IMPLEMENT_FUNCTION(29, Boutarel, function29) break; case kActionNone: - UPDATE_PARAM_PROC(params->param2, getState()->time, 450) + if (Entity::updateParameter(params->param2, getState()->time, 450)) { getSavePoints()->push(kEntityBoutarel, kEntityServers1, kAction256200848); - UPDATE_PARAM_PROC_END + } if (!params->param1) break; @@ -959,7 +961,7 @@ IMPLEMENT_FUNCTION(29, Boutarel, function29) } } - TIME_CHECK_CALLBACK_1(kTime2002500, params->param4, 1, setup_function14, true); + Entity::timeCheckCallback(kTime2002500, params->param4, 1, true, WRAP_SETUP_FUNCTION_B(Boutarel, setup_function14)); break; case kActionDefault: @@ -972,7 +974,7 @@ IMPLEMENT_FUNCTION(29, Boutarel, function29) break; case 1: - TIME_CHECK_CALLBACK_1(kTime2002500, params->param4, 1, setup_function14, true); + Entity::timeCheckCallback(kTime2002500, params->param4, 1, true, WRAP_SETUP_FUNCTION_B(Boutarel, setup_function14)); break; case 2: @@ -1045,7 +1047,7 @@ IMPLEMENT_FUNCTION(32, Boutarel, chapter4Handler) break; case kActionNone: - TIME_CHECK(kTime2367000, params->param1, setup_function33); + Entity::timeCheck(kTime2367000, params->param1, WRAP_SETUP_FUNCTION(Boutarel, setup_function33)); break; case kActionDefault: @@ -1063,7 +1065,7 @@ IMPLEMENT_FUNCTION(33, Boutarel, function33) case kActionNone: if (params->param1) - TIME_CHECK_CALLBACK_1(kTime2389500, params->param2, 3, setup_function14, false); + Entity::timeCheckCallback(kTime2389500, params->param2, 3, false, WRAP_SETUP_FUNCTION_B(Boutarel, setup_function14)); break; case kActionDefault: @@ -1111,7 +1113,8 @@ IMPLEMENT_FUNCTION(34, Boutarel, function34) break; case kActionNone: - TIME_CHECK(kTime2470500, params->param1, setup_function35); + if (Entity::timeCheck(kTime2470500, params->param1, WRAP_SETUP_FUNCTION(Boutarel, setup_function35))) + break; if (getState()->time > kTime2457000 && getEvent(kEventAugustDrink)) { getSavePoints()->push(kEntityBoutarel, kEntityAbbot, kAction159003408); diff --git a/engines/lastexpress/entities/boutarel.h b/engines/lastexpress/entities/boutarel.h index 5eb264631c..04838f6527 100644 --- a/engines/lastexpress/entities/boutarel.h +++ b/engines/lastexpress/entities/boutarel.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_BOUTAREL_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/chapters.cpp b/engines/lastexpress/entities/chapters.cpp index 4ef2dc50e8..d373432710 100644 --- a/engines/lastexpress/entities/chapters.cpp +++ b/engines/lastexpress/entities/chapters.cpp @@ -63,11 +63,9 @@ #include "lastexpress/game/state.h" #include "lastexpress/menu/menu.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" #include "lastexpress/resource.h" @@ -152,7 +150,7 @@ IMPLEMENT_FUNCTION(5, Chapters, resetMainEntities) RESET_ENTITY_STATE(kEntityVesna, Vesna, setup_reset); RESET_ENTITY_STATE(kEntityYasmin, Yasmin, setup_reset); - CALLBACK_ACTION(); + callbackAction(); IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// @@ -191,7 +189,7 @@ IMPLEMENT_FUNCTION(6, Chapters, chapter1End) getScenes()->loadScene(kScene41); - CALLBACK_ACTION(); + callbackAction(); } else { getSound()->playSound(kEntityPlayer, "LIB014"); getSound()->playSound(kEntityPlayer, "LIB015", kFlagDefault, 15); @@ -386,12 +384,6 @@ IMPLEMENT_FUNCTION(7, Chapters, chapter1Init) IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// -#define PLAY_STEAM() { \ - getSoundQueue()->resetState(); \ - getSound()->playSteam((CityIndex)ENTITY_PARAM(0, 4)); \ - ENTITY_PARAM(0, 2) = 0; \ - } - IMPLEMENT_FUNCTION(8, Chapters, chapter1Handler) switch (savepoint.action) { default: @@ -401,21 +393,28 @@ IMPLEMENT_FUNCTION(8, Chapters, chapter1Handler) if (!getProgress().isTrainRunning || getState()->time >= kTime1458000) goto label_processStations; - UPDATE_PARAM_PROC(params->param6, getState()->timeTicks, params->param2) + if (Entity::updateParameter(params->param6, getState()->timeTicks, params->param2)) { // Play sound FX getSound()->playLocomotiveSound(); params->param2 = 225 * (4 * rnd(5) + 20); params->param6 = 0; - UPDATE_PARAM_PROC_END + } label_processStations: // Process stations - TIME_CHECK_CALLBACK_2(kTime1039500, params->param7, 1, setup_savegame, kSavegameTypeTime, kTimeNone); + if (getState()->time > kTime1039500 && !params->param7) { + params->param7 = 1; + setCallback(1); + setup_savegame(kSavegameTypeTime, kTimeNone); + + break; + } label_enter_epernay: // Entering Epernay station - TIME_CHECK_CALLBACK_2(kTimeEnterEpernay, params->param8, 1, setup_enterStation, "Epernay", kCityEpernay); + if (timeCheckEnterStation(kTimeEnterEpernay, params->param8, 1, "Epernay", kCityEpernay)) + break; label_exit_epernay: // Exiting Epernay station @@ -445,19 +444,23 @@ label_enter_chalons: goto label_exit_strasbourg; // Entering Chalons station - TIME_CHECK_CALLBACK_2(kTimeEnterChalons, CURRENT_PARAM(1, 3), 5, setup_enterStation, "Chalons", kCityChalons); + if (timeCheckEnterStation(kTimeEnterChalons, CURRENT_PARAM(1, 3), 5, "Chalons", kCityChalons)) + break; label_exit_chalons: // Exiting Chalons station - TIME_CHECK_CALLBACK_1(kTimeExitChalons, CURRENT_PARAM(1, 4), 6, setup_exitStation, "Chalons"); + if (timeCheckExitStation(kTimeExitChalons, CURRENT_PARAM(1, 4), 6, "Chalons")) + break; label_enter_barleduc: // Entering Bar-Le-Duc station - TIME_CHECK_CALLBACK_2(kTimeCityBarLeDuc, CURRENT_PARAM(1, 5), 7, setup_enterStation, "BarLeDuc", kCityBarleduc); + if (timeCheckEnterStation(kTimeCityBarLeDuc, CURRENT_PARAM(1, 5), 7, "BarLeDuc", kCityBarleduc)) + break; label_exit_barleduc: // Exiting Bar-Le-Duc station - TIME_CHECK_CALLBACK_1(kTimeExitBarLeDuc, CURRENT_PARAM(1, 6), 8, setup_exitStation, "BarLeDuc"); + if (timeCheckExitStation(kTimeExitBarLeDuc, CURRENT_PARAM(1, 6), 8, "BarLeDuc")) + break; label_enter_nancy: if (getState()->time > kTime1260000 && !CURRENT_PARAM(1, 7)) { @@ -466,50 +469,67 @@ label_enter_nancy: } // Entering Nancy station - TIME_CHECK_CALLBACK_2(kTimeCityNancy, CURRENT_PARAM(1, 8), 9, setup_enterStation, "Nancy", kCityNancy); + if (timeCheckEnterStation(kTimeCityNancy, CURRENT_PARAM(1, 8), 9, "Nancy", kCityNancy)) + break; label_exit_nancy: // Exiting Nancy station - TIME_CHECK_CALLBACK_1(kTimeExitNancy, CURRENT_PARAM(2, 1), 10, setup_exitStation, "Nancy"); + if (timeCheckExitStation(kTimeExitNancy, CURRENT_PARAM(2, 1), 10, "Nancy")) + break; label_enter_luneville: // Entering Luneville station - TIME_CHECK_CALLBACK_2(kTimeCityLuneville, CURRENT_PARAM(2, 2), 11, setup_enterStation, "Luneville", kCityLuneville); + if (timeCheckEnterStation(kTimeCityLuneville, CURRENT_PARAM(2, 2), 11, "Luneville", kCityLuneville)) + break; label_exit_luneville: // Exiting Luneville station - TIME_CHECK_CALLBACK_1(kTimeExitLuneville, CURRENT_PARAM(2, 3), 12, setup_exitStation, "Luneville"); + if (timeCheckExitStation(kTimeExitLuneville, CURRENT_PARAM(2, 3), 12, "Luneville")) + break; label_enter_avricourt: // Entering Avricourt station - TIME_CHECK_CALLBACK_2(kTimeCityAvricourt, CURRENT_PARAM(2, 4), 13, setup_enterStation, "Avricourt", kCityAvricourt); + if (timeCheckEnterStation(kTimeCityAvricourt, CURRENT_PARAM(2, 4), 13, "Avricourt", kCityAvricourt)) + break; label_exit_avricourt: // Exiting Avricourt station - TIME_CHECK_CALLBACK_1(kTimeExitAvricourt, CURRENT_PARAM(2, 5), 14, setup_exitStation, "Avricourt"); + if (timeCheckExitStation(kTimeExitAvricourt, CURRENT_PARAM(2, 5), 14, "Avricourt")) + break; label_enter_deutschavricourt: // Entering Deutsch-Avricourt station - TIME_CHECK_CALLBACK_2(kTimeCityDeutschAvricourt, CURRENT_PARAM(2, 6), 15, setup_enterStation, "DeutschA", kCityDeutschAvricourt); + if (timeCheckEnterStation(kTimeCityDeutschAvricourt, CURRENT_PARAM(2, 6), 15, "DeutschA", kCityDeutschAvricourt)) + break; label_exit_deutschavricourt: // Exiting Avricourt station - TIME_CHECK_CALLBACK_1(kTimeExitDeutschAvricourt, CURRENT_PARAM(2, 7), 16, setup_exitStation, "DeutschA"); + if (timeCheckExitStation(kTimeExitDeutschAvricourt, CURRENT_PARAM(2, 7), 16, "DeutschA")) + break; label_enter_strasbourg: - TIME_CHECK_CALLBACK_2(kTimeCityStrasbourg, CURRENT_PARAM(2, 8), 17, setup_savegame, kSavegameTypeTime, kTimeNone); + if (getState()->time > kTimeCityStrasbourg && !CURRENT_PARAM(2, 8)) { + CURRENT_PARAM(2, 8) = 1; + setCallback(17); + setup_savegame(kSavegameTypeTime, kTimeNone); + + break; + } label_exit_strasbourg: // Exiting Strasbourg station - TIME_CHECK_CALLBACK_1(kTimeExitStrasbourg, CURRENT_PARAM(3, 1), 19, setup_exitStation, "Strasbou"); + if (timeCheckExitStation(kTimeExitStrasbourg, CURRENT_PARAM(3, 1), 19, "Strasbou")) + break; label_enter_badenoos: // Entering Baden Oos station - TIME_CHECK_CALLBACK_2(kTimeCityBadenOos, CURRENT_PARAM(3, 2), 20, setup_enterStation, "BadenOos", kCityBadenOos); + if (timeCheckEnterStation(kTimeCityBadenOos, CURRENT_PARAM(3, 2), 20, "BadenOos", kCityBadenOos)) + break; label_exit_badenoos: // Exiting Baden Oos station - TIME_CHECK_CALLBACK_1(kTimeExitBadenOos, CURRENT_PARAM(3, 3), 21, setup_exitStation, "BadenOos"); + if (timeCheckExitStation(kTimeExitBadenOos, CURRENT_PARAM(3, 3), 21, "BadenOos")) + break; label_chapter1_next: if (getState()->time > kTimeChapter1End3 && ! CURRENT_PARAM(3, 4)) { @@ -524,43 +544,43 @@ label_chapter1_next: getSavePoints()->push(kEntityChapters, kEntityTrain, kActionTrainStopRunning); if (getEntityData(kEntityPlayer)->location != kLocationOutsideTrain) { - PLAY_STEAM(); + playSteam(); break; } if (getEntities()->isOutsideAlexeiWindow()) { getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49); - PLAY_STEAM(); + playSteam(); break; } if (getEntities()->isOutsideAnnaWindow()) { getScenes()->loadSceneFromPosition(kCarRedSleeping, 49); - PLAY_STEAM(); + playSteam(); break; } CarIndex car = getEntityData(kEntityPlayer)->car; if (car < kCarRedSleeping || car > kCarCoalTender) { if (car < kCarBaggageRear || car > kCarGreenSleeping) { - PLAY_STEAM(); + playSteam(); break; } if (getEntities()->isPlayerPosition(kCarGreenSleeping, 98)) { getSound()->playSound(kEntityPlayer, "LIB015"); getScenes()->loadSceneFromPosition(kCarGreenSleeping, 71); - PLAY_STEAM(); + playSteam(); break; } getScenes()->loadSceneFromPosition(kCarGreenSleeping, 82); - PLAY_STEAM(); + playSteam(); break; } getScenes()->loadSceneFromPosition(kCarRestaurant, 82); - PLAY_STEAM(); + playSteam(); break; } @@ -817,7 +837,8 @@ IMPLEMENT_FUNCTION(12, Chapters, chapter2Handler) if (!getProgress().isTrainRunning) break; - UPDATE_PARAM(params->param2, getState()->timeTicks, params->param1); + if (!Entity::updateParameter(params->param2, getState()->timeTicks, params->param1)) + break; getSound()->playLocomotiveSound(); @@ -903,15 +924,15 @@ IMPLEMENT_FUNCTION(15, Chapters, chapter3Handler) case kActionNone: if (getProgress().isTrainRunning) { - UPDATE_PARAM_PROC(params->param4, getState()->timeTicks, params->param1) + if (Entity::updateParameter(params->param4, getState()->timeTicks, params->param1)) { getSound()->playLocomotiveSound(); params->param1 = 225 * (4 * rnd(5) + 20); params->param4 = 0; - UPDATE_PARAM_PROC_END + } } - UPDATE_PARAM_PROC(params->param5, getState()->timeTicks, params->param2) + if (Entity::updateParameter(params->param5, getState()->timeTicks, params->param2)) { switch (rnd(2)) { default: break; @@ -927,30 +948,38 @@ IMPLEMENT_FUNCTION(15, Chapters, chapter3Handler) params->param2 = 225 * (4 * rnd(6) + 8); params->param5 = 0; - UPDATE_PARAM_PROC_END + } - TIME_CHECK_CALLBACK_2(kTimeEnterSalzbourg, params->param6, 1, setup_enterStation, "Salzburg", kCitySalzbourg); + if (timeCheckEnterStation(kTimeEnterSalzbourg, params->param6, 1, "Salzburg", kCitySalzbourg)) + break; label_callback_1: - TIME_CHECK_CALLBACK_1(kTimeExitSalzbourg, params->param7, 2, setup_exitStation, "Salzburg"); + if (timeCheckExitStation(kTimeExitSalzbourg, params->param7, 2, "Salzburg")) + break; label_callback_2: - TIME_CHECK_CALLBACK_2(kTimeEnterAttnangPuchheim, params->param8, 3, setup_enterStation, "Attnang", kCityAttnangPuchheim); + if (timeCheckEnterStation(kTimeEnterAttnangPuchheim, params->param8, 3, "Attnang", kCityAttnangPuchheim)) + break; label_callback_3: - TIME_CHECK_CALLBACK_1(kTimeExitAttnangPuchheim, CURRENT_PARAM(1, 1), 4, setup_exitStation, "Attnang"); + if (timeCheckExitStation(kTimeExitAttnangPuchheim, CURRENT_PARAM(1, 1), 4, "Attnang")) + break; label_callback_4: - TIME_CHECK_CALLBACK_2(kTimeEnterWels, CURRENT_PARAM(1, 2), 5, setup_enterStation, "Wels", kCityWels); + if (timeCheckEnterStation(kTimeEnterWels, CURRENT_PARAM(1, 2), 5, "Wels", kCityWels)) + break; label_callback_5: - TIME_CHECK_CALLBACK_1(kTimeEnterWels, CURRENT_PARAM(1, 3), 6, setup_exitStation, "Wels"); + if (timeCheckExitStation(kTimeEnterWels, CURRENT_PARAM(1, 3), 6, "Wels")) + break; label_callback_6: - TIME_CHECK_CALLBACK_2(kTimeEnterLinz, CURRENT_PARAM(1, 4), 7, setup_enterStation, "Linz", kCityLinz); + if (timeCheckEnterStation(kTimeEnterLinz, CURRENT_PARAM(1, 4), 7, "Linz", kCityLinz)) + break; label_callback_7: - TIME_CHECK_CALLBACK_1(kTimeCityLinz, CURRENT_PARAM(1, 5), 8, setup_exitStation, "Linz"); + if (timeCheckExitStation(kTimeCityLinz, CURRENT_PARAM(1, 5), 8, "Linz")) + break; label_callback_8: if (getState()->time > kTime2187000 && !CURRENT_PARAM(1, 6)) { @@ -958,7 +987,7 @@ label_callback_8: getState()->timeDelta = 5; } - TIME_CHECK_CALLBACK_2(kTimeCityVienna, CURRENT_PARAM(1, 7), 9, setup_enterStation, "Vienna", kCityVienna); + timeCheckEnterStation(kTimeCityVienna, CURRENT_PARAM(1, 7), 9, "Vienna", kCityVienna); break; case kActionEndSound: @@ -1202,15 +1231,15 @@ IMPLEMENT_FUNCTION(19, Chapters, chapter4Handler) case kActionNone: if (getProgress().isTrainRunning) { - UPDATE_PARAM_PROC(params->param6, getState()->timeTicks, params->param4); + if (Entity::updateParameter(params->param6, getState()->timeTicks, params->param4)) { getSound()->playLocomotiveSound(); params->param4 = 225 * (4 * rnd(5) + 20); params->param6 = 0; - UPDATE_PARAM_PROC_END + } } - UPDATE_PARAM_PROC(params->param7, getState()->timeTicks, params->param5) + if (Entity::updateParameter(params->param7, getState()->timeTicks, params->param5)) { switch (rnd(2)) { default: break; @@ -1226,12 +1255,14 @@ IMPLEMENT_FUNCTION(19, Chapters, chapter4Handler) params->param5 = 225 * (4 * rnd(6) + 8); params->param7 = 0; - UPDATE_PARAM_PROC_END + } - TIME_CHECK_CALLBACK_2(kTimeEnterPoszony, params->param8, 1, setup_enterStation, "Pozsony", kCityPoszony); + if (timeCheckEnterStation(kTimeEnterPoszony, params->param8, 1, "Pozsony", kCityPoszony)) + break; label_exitPozsony: - TIME_CHECK_CALLBACK_1(kTimeExitPoszony, CURRENT_PARAM(1, 1), 2, setup_exitStation, "Pozsony"); + if (timeCheckExitStation(kTimeExitPoszony, CURRENT_PARAM(1, 1), 2, "Pozsony")) + break; label_enterGalanta: if (getObjects()->get(kObjectCompartment1).location2 == kObjectLocation1) { @@ -1244,10 +1275,12 @@ label_enterGalanta: if (params->param1) goto label_callback_4; - TIME_CHECK_CALLBACK_2(kTimeEnterGalanta, CURRENT_PARAM(1, 3), 3, setup_enterStation, "Galanta", kCityGalanta); + if (timeCheckEnterStation(kTimeEnterGalanta, CURRENT_PARAM(1, 3), 3, "Galanta", kCityGalanta)) + break; label_exitGalanta: - TIME_CHECK_CALLBACK_1(kTimeExitGalanta, CURRENT_PARAM(1, 4), 4, setup_exitStation, "Galanta"); + if (timeCheckExitStation(kTimeExitGalanta, CURRENT_PARAM(1, 4), 4, "Galanta")) + break; label_callback_4: if (getState()->time > kTime2470500 && !CURRENT_PARAM(1, 5)) { @@ -1280,43 +1313,43 @@ label_callback_4: getSavePoints()->push(kEntityChapters, kEntityTrain, kActionTrainStopRunning); if (getEntityData(kEntityPlayer)->location != kLocationOutsideTrain) { - PLAY_STEAM(); + playSteam(); break; } if (getEntities()->isOutsideAlexeiWindow()) { getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49); - PLAY_STEAM(); + playSteam(); break; } if (getEntities()->isOutsideAnnaWindow()) { getScenes()->loadSceneFromPosition(kCarRedSleeping, 49); - PLAY_STEAM(); + playSteam(); break; } CarIndex car = getEntityData(kEntityPlayer)->car; if (car < kCarRedSleeping || car > kCarCoalTender) { if (car < kCarBaggageRear || car > kCarGreenSleeping) { - PLAY_STEAM(); + playSteam(); break; } if (getEntities()->isPlayerPosition(kCarGreenSleeping, 98)) { getSound()->playSound(kEntityPlayer, "LIB015"); getScenes()->loadSceneFromPosition(kCarGreenSleeping, 71); - PLAY_STEAM(); + playSteam(); break; } getScenes()->loadSceneFromPosition(kCarGreenSleeping, 82); - PLAY_STEAM(); + playSteam(); break; } getScenes()->loadSceneFromPosition(kCarRestaurant, 82); - PLAY_STEAM(); + playSteam(); break; } @@ -1815,7 +1848,37 @@ void Chapters::enterExitHelper(bool isEnteringStation) { ENTITY_PARAM(0, 3) = 1; } - CALLBACK_ACTION(); + callbackAction(); +} + +void Chapters::playSteam() const { + getSoundQueue()->resetState(); + getSound()->playSteam((CityIndex)ENTITY_PARAM(0, 4)); + ENTITY_PARAM(0, 2) = 0; +} + +bool Chapters::timeCheckEnterStation(TimeValue timeValue, uint ¶meter, byte callback, const char *sequence, CityIndex cityIndex) { + if (getState()->time > timeValue && !parameter) { + parameter = 1; + setCallback(callback); + setup_enterStation(sequence, cityIndex); + + return true; + } + + return false; +} + +bool Chapters::timeCheckExitStation(TimeValue timeValue, uint ¶meter, byte callback, const char *sequence) { + if (getState()->time > timeValue && !parameter) { + parameter = 1; + setCallback(callback); + setup_exitStation(sequence); + + return true; + } + + return false; } } // End of namespace LastExpress diff --git a/engines/lastexpress/entities/chapters.h b/engines/lastexpress/entities/chapters.h index 353d3a6b5b..fb52ea3ee4 100644 --- a/engines/lastexpress/entities/chapters.h +++ b/engines/lastexpress/entities/chapters.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_CHAPTERS_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { @@ -154,8 +153,11 @@ public: DECLARE_FUNCTION(chapter5Handler) private: + bool timeCheckEnterStation(TimeValue timeValue, uint ¶meter, byte callback, const char *sequence, CityIndex cityIndex); + bool timeCheckExitStation(TimeValue timeValue, uint ¶meter, byte callback, const char *sequence); void enterExitStation(const SavePoint &savepoint, bool isEnteringStation); void enterExitHelper(bool isEnteringStation); + void playSteam() const; }; } // End of namespace LastExpress diff --git a/engines/lastexpress/entities/cooks.cpp b/engines/lastexpress/entities/cooks.cpp index 42e888cc7c..5e8a2df8cb 100644 --- a/engines/lastexpress/entities/cooks.cpp +++ b/engines/lastexpress/entities/cooks.cpp @@ -29,9 +29,7 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" namespace LastExpress { @@ -96,7 +94,7 @@ IMPLEMENT_FUNCTION(3, Cooks, function3) case kActionDrawScene: if (!getEntities()->isInKitchen(kEntityPlayer)) { getEntities()->clearSequences(kEntityCooks); - CALLBACK_ACTION(); + callbackAction(); break; } @@ -108,7 +106,7 @@ IMPLEMENT_FUNCTION(3, Cooks, function3) if (!getEntities()->hasValidFrame(kEntityCooks)) { getSound()->playSound(kEntityCooks, "LIB015"); getEntities()->clearSequences(kEntityCooks); - CALLBACK_ACTION(); + callbackAction(); } break; } @@ -122,7 +120,7 @@ IMPLEMENT_FUNCTION(3, Cooks, function3) if (params->param1 && !getEntities()->hasValidFrame(kEntityCooks)) { getSound()->playSound(kEntityCooks, "LIB015"); getEntities()->clearSequences(kEntityCooks); - CALLBACK_ACTION(); + callbackAction(); } break; @@ -182,7 +180,7 @@ IMPLEMENT_FUNCTION(4, Cooks, function4) case kActionDrawScene: if (!getEntities()->isInKitchen(kEntityPlayer)) { getEntities()->clearSequences(kEntityCooks); - CALLBACK_ACTION(); + callbackAction(); break; } @@ -194,7 +192,7 @@ IMPLEMENT_FUNCTION(4, Cooks, function4) if (!getEntities()->hasValidFrame(kEntityCooks)) { getSound()->playSound(kEntityCooks, "LIB015"); getEntities()->clearSequences(kEntityCooks); - CALLBACK_ACTION(); + callbackAction(); } break; } @@ -208,7 +206,7 @@ IMPLEMENT_FUNCTION(4, Cooks, function4) if (params->param1 && !getEntities()->hasValidFrame(kEntityCooks)) { getSound()->playSound(kEntityCooks, "LIB015"); getEntities()->clearSequences(kEntityCooks); - CALLBACK_ACTION(); + callbackAction(); } break; @@ -241,7 +239,7 @@ IMPLEMENT_FUNCTION(5, Cooks, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler); + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(Cooks, setup_chapter1Handler)); break; case kActionDefault: @@ -262,7 +260,8 @@ IMPLEMENT_FUNCTION(6, Cooks, chapter1Handler) break; case kActionNone: - UPDATE_PARAM(params->param4, getState()->time, params->param2); + if (!Entity::updateParameter(params->param4, getState()->time, params->param2)) + break; // Broken plate sound getSound()->playSound(kEntityPlayer, "LIB122", getSound()->getSoundFlag(kEntityCooks)); @@ -375,7 +374,8 @@ IMPLEMENT_FUNCTION(9, Cooks, chapter2Handler) break; case kActionNone: - UPDATE_PARAM(params->param3, getState()->time, params->param1); + if (!Entity::updateParameter(params->param3, getState()->time, params->param1)) + break; // Broken plate sound getSound()->playSound(kEntityPlayer, "LIB122", getSound()->getSoundFlag(kEntityCooks)); @@ -434,12 +434,12 @@ IMPLEMENT_FUNCTION(11, Cooks, chapter3Handler) break; case kActionNone: - UPDATE_PARAM_PROC(params->param4, getState()->time, params->param2) + if (Entity::updateParameter(params->param4, getState()->time, params->param2)) { // Broken plate sound getSound()->playSound(kEntityPlayer, "LIB122", getSound()->getSoundFlag(kEntityCooks)); params->param2 = 225 * (4 * rnd(30) + 120); params->param4 = 0; - UPDATE_PARAM_PROC_END + } if (getState()->time > kTime2079000 && !params->param5) { params->param1 = 0; @@ -526,7 +526,8 @@ IMPLEMENT_FUNCTION(13, Cooks, chapter4Handler) break; case kActionNone: - UPDATE_PARAM(params->param3, getState()->time, params->param1) + if (!Entity::updateParameter(params->param3, getState()->time, params->param1)) + break; // Broken plate sound getSound()->playSound(kEntityPlayer, "LIB122", getSound()->getSoundFlag(kEntityCooks)); diff --git a/engines/lastexpress/entities/cooks.h b/engines/lastexpress/entities/cooks.h index 3ab7d35161..f01d0b2ca0 100644 --- a/engines/lastexpress/entities/cooks.h +++ b/engines/lastexpress/entities/cooks.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_COOKS_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/coudert.cpp b/engines/lastexpress/entities/coudert.cpp index c3e7e37b88..b604277903 100644 --- a/engines/lastexpress/entities/coudert.cpp +++ b/engines/lastexpress/entities/coudert.cpp @@ -32,22 +32,11 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" namespace LastExpress { -#define SAVEGAME_BLOOD_JACKET() \ - if (getProgress().jacket == kJacketBlood \ - && getEntities()->isDistanceBetweenEntities(kEntityCoudert, kEntityPlayer, 1000) \ - && !getEntities()->isInsideCompartments(kEntityPlayer) \ - && !getEntities()->checkFields10(kEntityPlayer)) { \ - setCallback(1); \ - setup_savegame(kSavegameTypeEvent, kEventMertensBloodJacket); \ - } - Coudert::Coudert(LastExpressEngine *engine) : Entity(engine, kEntityCoudert) { ADD_CALLBACK_FUNCTION(Coudert, reset); ADD_CALLBACK_FUNCTION(Coudert, bloodJacket); @@ -126,11 +115,11 @@ IMPLEMENT_FUNCTION_S(2, Coudert, bloodJacket) break; case kActionNone: - SAVEGAME_BLOOD_JACKET(); + Entity::savegameBloodJacket(); break; case kActionExitCompartment: - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: @@ -153,7 +142,7 @@ IMPLEMENT_FUNCTION_SI(3, Coudert, enterExitCompartment, ObjectIndex) break; case kActionNone: - SAVEGAME_BLOOD_JACKET(); + Entity::savegameBloodJacket(); return; case kActionCallback: @@ -175,15 +164,15 @@ IMPLEMENT_FUNCTION(4, Coudert, callbackActionOnDirection) case kActionNone: if (getData()->direction != kDirectionRight) { - CALLBACK_ACTION(); + callbackAction(); break; } - SAVEGAME_BLOOD_JACKET(); + Entity::savegameBloodJacket(); break; case kActionExitCompartment: - CALLBACK_ACTION(); + callbackAction(); break; case kActionCallback: @@ -202,7 +191,7 @@ IMPLEMENT_FUNCTION_SIII(5, Coudert, enterExitCompartment2, ObjectIndex, EntityPo break; case kActionNone: - SAVEGAME_BLOOD_JACKET(); + Entity::savegameBloodJacket(); return; case kActionCallback: @@ -223,11 +212,11 @@ IMPLEMENT_FUNCTION_S(6, Coudert, playSound) break; case kActionNone: - SAVEGAME_BLOOD_JACKET(); + Entity::savegameBloodJacket(); break; case kActionEndSound: - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: @@ -252,11 +241,11 @@ IMPLEMENT_FUNCTION_NOSETUP(7, Coudert, playSound16) break; case kActionNone: - SAVEGAME_BLOOD_JACKET(); + Entity::savegameBloodJacket(); break; case kActionEndSound: - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: @@ -296,7 +285,7 @@ IMPLEMENT_FUNCTION_II(9, Coudert, updateEntity, CarIndex, EntityPosition) if (getEntities()->updateEntity(kEntityCoudert, (CarIndex)params->param1, (EntityPosition)params->param2)) { getData()->inventoryItem = kItemNone; - CALLBACK_ACTION(); + callbackAction(); } break; } @@ -332,7 +321,7 @@ IMPLEMENT_FUNCTION_II(9, Coudert, updateEntity, CarIndex, EntityPosition) params->param3 = kItemInvalid; if (getEntities()->updateEntity(kEntityCoudert, (CarIndex)params->param1, (EntityPosition)params->param2)) - CALLBACK_ACTION(); + callbackAction(); break; case kActionCallback: @@ -365,11 +354,12 @@ IMPLEMENT_FUNCTION_I(10, Coudert, updateFromTime, uint32) break; case kActionNone: - SAVEGAME_BLOOD_JACKET(); + Entity::savegameBloodJacket(); - UPDATE_PARAM(params->param2, getState()->time, params->param1); + if (!Entity::updateParameter(params->param2, getState()->time, params->param1)) + break; - CALLBACK_ACTION(); + callbackAction(); break; case kActionCallback: @@ -388,11 +378,12 @@ IMPLEMENT_FUNCTION_I(11, Coudert, updateFromTicks, uint32) break; case kActionNone: - SAVEGAME_BLOOD_JACKET(); + Entity::savegameBloodJacket(); - UPDATE_PARAM(params->param2, getState()->timeTicks, params->param1); + if (!Entity::updateParameter(params->param2, getState()->timeTicks, params->param1)) + break; - CALLBACK_ACTION(); + callbackAction(); break; case kActionCallback: @@ -412,7 +403,7 @@ IMPLEMENT_FUNCTION_I(12, Coudert, excuseMe, EntityIndex) return; if (getSoundQueue()->isBuffered(kEntityCoudert)) { - CALLBACK_ACTION(); + callbackAction(); return; } @@ -452,7 +443,7 @@ IMPLEMENT_FUNCTION_I(12, Coudert, excuseMe, EntityIndex) getSound()->playSound(kEntityCoudert, "JAC1112E"); } - CALLBACK_ACTION(); + callbackAction(); IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// @@ -462,7 +453,7 @@ IMPLEMENT_FUNCTION_II(13, Coudert, function13, bool, EntityIndex) break; case kActionNone: - SAVEGAME_BLOOD_JACKET(); + Entity::savegameBloodJacket(); if (!params->param2 && !params->param3) { @@ -487,7 +478,8 @@ IMPLEMENT_FUNCTION_II(13, Coudert, function13, bool, EntityIndex) } } - UPDATE_PARAM(params->param5, getState()->timeTicks, 225); + if (!Entity::updateParameter(params->param5, getState()->timeTicks, 225)) + break; getData()->inventoryItem = kItemNone; setCallback(5); @@ -561,7 +553,7 @@ IMPLEMENT_FUNCTION_II(13, Coudert, function13, bool, EntityIndex) case 5: case 6: case 7: - CALLBACK_ACTION(); + callbackAction(); break; case 9: @@ -584,7 +576,7 @@ IMPLEMENT_FUNCTION_I(14, Coudert, function14, EntityIndex) break; case kActionNone: - SAVEGAME_BLOOD_JACKET(); + Entity::savegameBloodJacket(); break; case kActionDefault: @@ -629,7 +621,7 @@ IMPLEMENT_FUNCTION_I(14, Coudert, function14, EntityIndex) break; case 5: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -706,7 +698,7 @@ IMPLEMENT_FUNCTION_I(15, Coudert, function15, bool) break; case 5: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -724,7 +716,7 @@ IMPLEMENT_FUNCTION(16, Coudert, function16) ENTITY_PARAM(2, 1) = 0; getInventory()->setLocationAndProcess(kItem5, kObjectLocation1); - CALLBACK_ACTION(); + callbackAction(); break; } @@ -743,7 +735,7 @@ IMPLEMENT_FUNCTION(16, Coudert, function16) if (!getEntities()->isPlayerPosition(kCarRedSleeping, 2)) getData()->entityPosition = kPosition_2088; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -761,7 +753,7 @@ IMPLEMENT_FUNCTION_I(17, Coudert, function17, bool) if (ENTITY_PARAM(2, 1)) { ENTITY_PARAM(2, 1) = 0; - CALLBACK_ACTION(); + callbackAction(); break; } @@ -789,7 +781,7 @@ IMPLEMENT_FUNCTION_I(17, Coudert, function17, bool) case 1: case 2: case 3: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -817,7 +809,7 @@ IMPLEMENT_FUNCTION(18, Coudert, function18) getEntities()->drawSequenceLeft(kEntityCoudert, "627K"); getScenes()->loadSceneFromItemPosition(kItem5); - CALLBACK_ACTION(); + callbackAction(); break; } @@ -849,7 +841,7 @@ IMPLEMENT_FUNCTION(18, Coudert, function18) break; case 2: - CALLBACK_ACTION(); + callbackAction(); break; case 3: @@ -857,7 +849,7 @@ IMPLEMENT_FUNCTION(18, Coudert, function18) ENTITY_PARAM(0, 1) = 0; getSavePoints()->push(kEntityCoudert, kEntityCoudert, kActionDrawScene); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -876,14 +868,14 @@ IMPLEMENT_FUNCTION_I(19, Coudert, function19, bool) || ENTITY_PARAM(2, 4) || ENTITY_PARAM(2, 6)) { getInventory()->setLocationAndProcess(kItem5, kObjectLocation1); ENTITY_PARAM(2, 1) = 1; - CALLBACK_ACTION(); + callbackAction(); break; } if (ENTITY_PARAM(0, 3) || ENTITY_PARAM(0, 5) || ENTITY_PARAM(0, 4)) { getScenes()->loadSceneFromItemPosition(kItem5); ENTITY_PARAM(2, 1) = 1; - CALLBACK_ACTION(); + callbackAction(); break; } @@ -903,7 +895,7 @@ IMPLEMENT_FUNCTION_I(19, Coudert, function19, bool) getEntities()->drawSequenceLeft(kEntityCoudert, ENTITY_PARAM(0, 2) ? "627B" : "627E"); ENTITY_PARAM(0, 1) = 0; - CALLBACK_ACTION(); + callbackAction(); } break; } @@ -916,11 +908,12 @@ IMPLEMENT_FUNCTION_II(20, Coudert, function20, ObjectIndex, ObjectIndex) break; case kActionNone: - UPDATE_PARAM_PROC(CURRENT_PARAM(1, 3), getState()->time, 300) + if (Entity::updateParameter(CURRENT_PARAM(1, 3), getState()->time, 300)) { getSound()->playSound(kEntityPlayer, "ZFX1004", getSound()->getSoundFlag(kEntityCoudert)); - UPDATE_PARAM_PROC_END + } - UPDATE_PARAM(CURRENT_PARAM(1, 4), getState()->time, 900); + if (!Entity::updateParameter(CURRENT_PARAM(1, 4), getState()->time, 900)) + break; getObjects()->updateLocation2((ObjectIndex)params->param1, kObjectLocation1); @@ -930,7 +923,7 @@ IMPLEMENT_FUNCTION_II(20, Coudert, function20, ObjectIndex, ObjectIndex) if (params->param2) getObjects()->update((ObjectIndex)params->param2, (EntityIndex)params->param7, (ObjectLocation)params->param8, (CursorStyle)CURRENT_PARAM(1, 1), (CursorStyle)CURRENT_PARAM(1, 2)); - CALLBACK_ACTION(); + callbackAction(); break; case kActionKnock: @@ -999,7 +992,8 @@ IMPLEMENT_FUNCTION(21, Coudert, function21) case kActionNone: if (!params->param1) { - UPDATE_PARAM(params->param2, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param2, getState()->timeTicks, 75)) + break; setCallback(3); setup_enterExitCompartment("627Zh", kObjectCompartmentH); @@ -1044,7 +1038,7 @@ IMPLEMENT_FUNCTION(21, Coudert, function21) case 5: getData()->location = kLocationOutsideCompartment; - CALLBACK_ACTION(); + callbackAction(); break; case 6: @@ -1073,7 +1067,7 @@ IMPLEMENT_FUNCTION(21, Coudert, function21) getData()->location = kLocationOutsideCompartment; getSavePoints()->push(kEntityCoudert, kEntityIvo, kAction123852928); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1100,7 +1094,8 @@ IMPLEMENT_FUNCTION(22, Coudert, function22) case kActionNone: if (!params->param1) { - UPDATE_PARAM(params->param2, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param2, getState()->timeTicks, 75)) + break; setCallback(3); setup_enterExitCompartment("627Rg", kObjectCompartmentG); @@ -1145,7 +1140,7 @@ IMPLEMENT_FUNCTION(22, Coudert, function22) case 5: getData()->location = kLocationOutsideCompartment; - CALLBACK_ACTION(); + callbackAction(); break; case 6: @@ -1174,7 +1169,7 @@ IMPLEMENT_FUNCTION(22, Coudert, function22) getData()->location = kLocationOutsideCompartment; getSavePoints()->push(kEntityCoudert, kEntityMilos, kAction123852928); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1225,7 +1220,7 @@ IMPLEMENT_FUNCTION(23, Coudert, function23) case 3: getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentF, true); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1296,7 +1291,7 @@ IMPLEMENT_FUNCTION(25, Coudert, function25) getData()->location = kLocationOutsideCompartment; getSavePoints()->push(kEntityCoudert, kEntityRebecca, kAction123852928); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1311,7 +1306,8 @@ IMPLEMENT_FUNCTION(26, Coudert, function26) case kActionNone: if (params->param1) { - UPDATE_PARAM(params->param2, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param2, getState()->timeTicks, 75)) + break; setCallback(3); setup_enterExitCompartment2("627Zd", kObjectCompartmentD, kPosition_5790, kPosition_6130); @@ -1357,7 +1353,7 @@ IMPLEMENT_FUNCTION(26, Coudert, function26) case 5: getData()->location = kLocationOutsideCompartment; - CALLBACK_ACTION(); + callbackAction(); break; case 6: @@ -1375,7 +1371,7 @@ IMPLEMENT_FUNCTION(26, Coudert, function26) getData()->location = kLocationOutsideCompartment; getSavePoints()->push(kEntityCoudert, kEntityMmeBoutarel, kAction123852928); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1402,7 +1398,8 @@ IMPLEMENT_FUNCTION(27, Coudert, function27) case kActionNone: if (!params->param1) { - UPDATE_PARAM(params->param2, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param2, getState()->timeTicks, 75)) + break; setCallback(3); setup_enterExitCompartment2("627Rc", kObjectCompartmentC, kPosition_6470, kPosition_6130); @@ -1446,7 +1443,7 @@ IMPLEMENT_FUNCTION(27, Coudert, function27) case 5: getData()->location = kLocationOutsideCompartment; - CALLBACK_ACTION(); + callbackAction(); break; case 6: @@ -1473,7 +1470,7 @@ IMPLEMENT_FUNCTION(27, Coudert, function27) getData()->location = kLocationOutsideCompartment; getSavePoints()->push(kEntityCoudert, kEntityBoutarel, kAction123852928); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1515,7 +1512,7 @@ IMPLEMENT_FUNCTION_I(30, Coudert, function30, ObjectIndex) case kActionDefault: switch (parameters->param1) { default: - CALLBACK_ACTION(); + callbackAction(); // Stop processing here return; @@ -1615,7 +1612,7 @@ IMPLEMENT_FUNCTION_I(30, Coudert, function30, ObjectIndex) break; case 6: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1654,7 +1651,7 @@ IMPLEMENT_FUNCTION_I(31, Coudert, function31, uint32) case 2: case 3: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1699,7 +1696,7 @@ IMPLEMENT_FUNCTION(32, Coudert, function32) break; case 5: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1726,7 +1723,7 @@ IMPLEMENT_FUNCTION(33, Coudert, function33) setup_updateEntity(kCarRedSleeping, kPosition_540); } } else { - CALLBACK_ACTION(); + callbackAction(); } break; @@ -1763,7 +1760,7 @@ IMPLEMENT_FUNCTION(33, Coudert, function33) case 4: ENTITY_PARAM(2, 6) = 0; - CALLBACK_ACTION(); + callbackAction(); break; case 5: @@ -1808,7 +1805,7 @@ IMPLEMENT_FUNCTION(33, Coudert, function33) case 10: ENTITY_PARAM(2, 6) = 0; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1882,7 +1879,7 @@ IMPLEMENT_FUNCTION_I(34, Coudert, function34, bool) break; case 7: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1902,7 +1899,8 @@ IMPLEMENT_FUNCTION_I(35, Coudert, function35, bool) getScenes()->loadSceneFromPosition(kCarRestaurant, 65); } - UPDATE_PARAM(params->param2, getState()->time, 2700); + if (!Entity::updateParameter(params->param2, getState()->time, 2700)) + break; getSavePoints()->push(kEntityCoudert, kEntityMax, kActionMaxFreeFromCage); @@ -1951,7 +1949,7 @@ IMPLEMENT_FUNCTION_I(35, Coudert, function35, bool) break; case 4: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1965,7 +1963,7 @@ IMPLEMENT_FUNCTION(36, Coudert, chapter1) break; case kActionNone: - TIME_CHECK_CALLBACK(kTimeChapter1, params->param1, 1, setup_chapter1Handler) + Entity::timeCheckCallback(kTimeChapter1, params->param1, 1, WRAP_SETUP_FUNCTION(Coudert, setup_chapter1Handler)); break; case kActionDefault: @@ -2174,7 +2172,7 @@ IMPLEMENT_FUNCTION(39, Coudert, function39) getSavePoints()->push(kEntityCoudert, kEntityVerges, kAction167854368); ENTITY_PARAM(2, 2) = 0; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -2283,22 +2281,22 @@ label_callback_9: label_callback_10: if (getState()->time > kTime1189800 && !ENTITY_PARAM(0, 1) && !ENTITY_PARAM(2, 1)) { - UPDATE_PARAM_PROC(params->param3, getState()->time, 2700); + if (Entity::updateParameter(params->param3, getState()->time, 2700)) { ENTITY_PARAM(0, 2) = 1; ENTITY_PARAM(0, 1) = 1; getEntities()->drawSequenceLeft(kEntityCoudert, "697F"); params->param3 = 0; - UPDATE_PARAM_PROC_END + } } if (!ENTITY_PARAM(0, 2)) break; - TIME_CHECK_OBJECT(kTime1107000, params->param4, kObject111, kObjectLocation2); - TIME_CHECK_OBJECT(kTime1161000, params->param5, kObject111, kObjectLocation3); - TIME_CHECK_OBJECT(kTime1206000, params->param6, kObject111, kObjectLocation4); + timeCheckObject(kTime1107000, params->param4, kObject111, kObjectLocation2); + timeCheckObject(kTime1161000, params->param5, kObject111, kObjectLocation3); + timeCheckObject(kTime1206000, params->param6, kObject111, kObjectLocation4); break; case kAction1: @@ -2536,7 +2534,7 @@ IMPLEMENT_FUNCTION(41, Coudert, function41) case 18: getSavePoints()->push(kEntityCoudert, kEntityMilos, kAction208228224); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -2827,28 +2825,34 @@ label_callback_12: } label_callback_13: - TIME_CHECK_CALLBACK(kTime2088900, params->param1, 14, setup_function32); + if (Entity::timeCheckCallback(kTime2088900, params->param1, 14, WRAP_SETUP_FUNCTION(Coudert, setup_function32))) + break; label_callback_14: - TIME_CHECK_CALLBACK(kTime2119500, params->param2, 15, setup_function32); + if (Entity::timeCheckCallback(kTime2119500, params->param2, 15, WRAP_SETUP_FUNCTION(Coudert, setup_function32))) + break; label_callback_15: - TIME_CHECK_CALLBACK(kTime2138400, params->param3, 16, setup_function32); + if (Entity::timeCheckCallback(kTime2138400, params->param3, 16, WRAP_SETUP_FUNCTION(Coudert, setup_function32))) + break; label_callback_16: - TIME_CHECK_CALLBACK(kTime2147400, params->param4, 17, setup_function32); + if (Entity::timeCheckCallback(kTime2147400, params->param4, 17, WRAP_SETUP_FUNCTION(Coudert, setup_function32))) + break; label_callback_17: - TIME_CHECK_CALLBACK(kTime2160000, params->param5, 18, setup_function32); + if (Entity::timeCheckCallback(kTime2160000, params->param5, 18, WRAP_SETUP_FUNCTION(Coudert, setup_function32))) + break; label_callback_18: - TIME_CHECK_CALLBACK(kTime2205000, params->param6, 19, setup_function32); + if (Entity::timeCheckCallback(kTime2205000, params->param6, 19, WRAP_SETUP_FUNCTION(Coudert, setup_function32))) + break; label_callback_19: if (ENTITY_PARAM(0, 2)) { - TIME_CHECK_OBJECT(kTime2025000, params->param7, kObject111, kObjectLocation7); - TIME_CHECK_OBJECT(kTime2133000, params->param8, kObject111, kObjectLocation8); - TIME_CHECK_OBJECT(kTime2173500, CURRENT_PARAM(1, 1), kObject111, kObjectLocation9); + timeCheckObject(kTime2025000, params->param7, kObject111, kObjectLocation7); + timeCheckObject(kTime2133000, params->param8, kObject111, kObjectLocation8); + timeCheckObject(kTime2173500, CURRENT_PARAM(1, 1), kObject111, kObjectLocation9); } break; @@ -3051,7 +3055,7 @@ IMPLEMENT_FUNCTION(46, Coudert, function46) break; case 11: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -3116,7 +3120,7 @@ IMPLEMENT_FUNCTION_I(47, Coudert, function47, bool) break; case 7: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -3164,7 +3168,7 @@ IMPLEMENT_FUNCTION(48, Coudert, function48) break; case 5: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -3261,7 +3265,7 @@ IMPLEMENT_FUNCTION(49, Coudert, function49) break; case 11: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -3347,7 +3351,7 @@ IMPLEMENT_FUNCTION(50, Coudert, function50) break; case 9: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -3541,11 +3545,11 @@ label_callback_1: params->param2 = (uint)(getState()->time + 4500); if (params->param3 != kTimeInvalid) { - UPDATE_PARAM_PROC_TIME(params->param2, !getEntities()->isPlayerInCar(kCarRedSleeping), params->param3, 0) + if (Entity::updateParameterTime((TimeValue)params->param2, !getEntities()->isPlayerInCar(kCarRedSleeping), params->param3, 0)) { setCallback(2); setup_function55(); break; - UPDATE_PARAM_PROC_END + } } } @@ -3558,18 +3562,22 @@ label_callback_2: label_callback_3: if (!params->param1) { - TIME_CHECK_CALLBACK(kTime2394000, params->param4, 4, setup_function56); + if (Entity::timeCheckCallback(kTime2394000, params->param4, 4, WRAP_SETUP_FUNCTION(Coudert, setup_function56))) + break; label_callback_4: - TIME_CHECK_CALLBACK(kTime2434500, params->param5, 5, setup_function32); + if (Entity::timeCheckCallback(kTime2434500, params->param5, 5, WRAP_SETUP_FUNCTION(Coudert, setup_function32))) + break; label_callback_5: - TIME_CHECK_CALLBACK(kTime2448000, params->param6, 6, setup_function32); + if (Entity::timeCheckCallback(kTime2448000, params->param6, 6, WRAP_SETUP_FUNCTION(Coudert, setup_function32))) + break; } label_callback_6: if (getState()->time > kTime2538000 && !ENTITY_PARAM(0, 1) && !ENTITY_PARAM(2, 1)) { - UPDATE_PARAM(params->param7, getState()->time, 2700); + if (!Entity::updateParameter(params->param7, getState()->time, 2700)) + break; ENTITY_PARAM(0, 2) = 0; ENTITY_PARAM(0, 1) = 1; @@ -3696,7 +3704,7 @@ IMPLEMENT_FUNCTION(54, Coudert, function54) break; case 3: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -3764,7 +3772,7 @@ IMPLEMENT_FUNCTION(55, Coudert, function55) break; case 7: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -3868,7 +3876,7 @@ IMPLEMENT_FUNCTION(56, Coudert, function56) break; case 16: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -4034,7 +4042,8 @@ IMPLEMENT_FUNCTION(62, Coudert, function62) case kActionNone: if (params->param1) { - UPDATE_PARAM(params->param4, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param4, getState()->timeTicks, 75)) + break; params->param1 = 0; params->param2 = 1; @@ -4168,7 +4177,7 @@ void Coudert::visitCompartment(const SavePoint &savepoint, EntityPosition positi case 6: getData()->location = kLocationOutsideCompartment; - CALLBACK_ACTION(); + callbackAction(); break; } break; diff --git a/engines/lastexpress/entities/coudert.h b/engines/lastexpress/entities/coudert.h index 45d13ce9bb..8303c80a32 100644 --- a/engines/lastexpress/entities/coudert.h +++ b/engines/lastexpress/entities/coudert.h @@ -24,8 +24,6 @@ #define LASTEXPRESS_COUDERT_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" - namespace LastExpress { class LastExpressEngine; diff --git a/engines/lastexpress/entities/entity.cpp b/engines/lastexpress/entities/entity.cpp index e136ca4776..dad5e67392 100644 --- a/engines/lastexpress/entities/entity.cpp +++ b/engines/lastexpress/entities/entity.cpp @@ -22,22 +22,17 @@ #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" - #include "lastexpress/data/sequence.h" #include "lastexpress/game/action.h" #include "lastexpress/game/entities.h" #include "lastexpress/game/logic.h" -#include "lastexpress/game/scenes.h" -#include "lastexpress/game/state.h" +#include "lastexpress/game/object.h" #include "lastexpress/game/savegame.h" #include "lastexpress/game/savepoint.h" #include "lastexpress/game/state.h" +#include "lastexpress/game/scenes.h" -#include "lastexpress/sound/sound.h" - -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" namespace LastExpress { @@ -55,6 +50,19 @@ EntityData::EntityCallData::~EntityCallData() { SAFE_DELETE(sequence3); } +void EntityData::EntityCallData::syncString(Common::Serializer &s, Common::String &string, uint length) const { + char seqName[13]; + memset(&seqName, 0, length); + + if (s.isSaving()) + strcpy((char *)&seqName, string.c_str()); + + s.syncBytes((byte *)&seqName, length); + + if (s.isLoading()) + string = seqName; +} + void EntityData::EntityCallData::saveLoadWithSerializer(Common::Serializer &s) { for (uint i = 0; i < ARRAYSIZE(callbacks); i++) s.syncAsByte(callbacks[i]); @@ -81,23 +89,19 @@ void EntityData::EntityCallData::saveLoadWithSerializer(Common::Serializer &s) { s.syncAsByte(directionSwitch); // Sync strings -#define SYNC_STRING(varName, count) { \ - char seqName[13]; \ - memset(&seqName, 0, count); \ - if (s.isSaving()) strcpy((char *)&seqName, varName.c_str()); \ - s.syncBytes((byte *)&seqName, count); \ - if (s.isLoading()) varName = seqName; \ -} - - SYNC_STRING(sequenceName, 13); - SYNC_STRING(sequenceName2, 13); - SYNC_STRING(sequenceNamePrefix, 7); - SYNC_STRING(sequenceNameCopy, 13); - -#undef SYNC_STRING + syncString(s, sequenceName, 13); + syncString(s, sequenceName2, 13); + syncString(s, sequenceNamePrefix, 7); + syncString(s, sequenceNameCopy, 13); // Skip pointers to frame & sequences - s.skip(5 * 4); + // (we are using a compressed stream, so we cannot seek on load) + if (s.isLoading()) { + byte empty[5 * 4]; + s.syncBytes(empty, 5 * 4); + } else { + s.skip(5 * 4); + } } ////////////////////////////////////////////////////////////////////////// @@ -113,7 +117,7 @@ EntityData::EntityParameters *EntityData::getParameters(uint callback, byte inde return _parameters[callback].parameters[index]; } -int EntityData::getCallback(uint callback) const { +byte EntityData::getCallback(uint callback) const { if (callback >= 16) error("[EntityData::getCallback] Invalid callback value (was: %d, max: 16)", callback); @@ -246,16 +250,38 @@ void Entity::savegame(const SavePoint &savepoint) { break; case kActionNone: - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: getSaveLoad()->saveGame((SavegameType)params->param1, _entityIndex, (EventIndex)params->param2); - CALLBACK_ACTION(); + callbackAction(); break; } } +void Entity::savegameBloodJacket() { + if (getProgress().jacket == kJacketBlood + && getEntities()->isDistanceBetweenEntities(_entityIndex, kEntityPlayer, 1000) + && !getEntities()->isInsideCompartments(kEntityPlayer) + && !getEntities()->checkFields10(kEntityPlayer)) { + setCallback(1); + + switch (_entityIndex) { + default: + break; + + case kEntityCoudert: + setup_savegame(kSavegameTypeEvent, kEventCoudertBloodJacket); + break; + + case kEntityMertens: + setup_savegame(kSavegameTypeEvent, kEventCoudertBloodJacket); + break; + } + } +} + void Entity::playSound(const SavePoint &savepoint, bool resetItem, SoundFlag flag) { EXPOSE_PARAMS(EntityData::EntityParametersSIIS) @@ -264,7 +290,7 @@ void Entity::playSound(const SavePoint &savepoint, bool resetItem, SoundFlag fla break; case kActionEndSound: - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: @@ -284,7 +310,7 @@ void Entity::draw(const SavePoint &savepoint, bool handleExcuseMe) { break; case kActionExitCompartment: - CALLBACK_ACTION(); + callbackAction(); break; case kActionExcuseMeCath: @@ -308,7 +334,7 @@ void Entity::draw2(const SavePoint &savepoint) { break; case kActionExitCompartment: - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: @@ -326,8 +352,10 @@ void Entity::updateFromTicks(const SavePoint &savepoint) { break; case kActionNone: - UPDATE_PARAM(params->param2, getState()->timeTicks, params->param1) - CALLBACK_ACTION(); + if (Entity::updateParameter(params->param2, getState()->timeTicks, params->param1)) + break; + + callbackAction(); break; } } @@ -340,8 +368,10 @@ void Entity::updateFromTime(const SavePoint &savepoint) { break; case kActionNone: - UPDATE_PARAM(params->param2, getState()->time, params->param1) - CALLBACK_ACTION(); + if (Entity::updateParameter(params->param2, getState()->time, params->param1)) + break; + + callbackAction(); break; } } @@ -352,12 +382,12 @@ void Entity::callbackActionOnDirection(const SavePoint &savepoint) { break; case kActionExitCompartment: - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: if (getData()->direction != kDirectionRight) - CALLBACK_ACTION(); + callbackAction(); break; } } @@ -370,7 +400,7 @@ void Entity::callbackActionRestaurantOrSalon(const SavePoint &savepoint) { case kActionNone: case kActionDefault: if (getEntities()->isSomebodyInsideRestaurantOrSalon()) - CALLBACK_ACTION(); + callbackAction(); break; } } @@ -395,7 +425,7 @@ void Entity::updateEntity(const SavePoint &savepoint, bool handleExcuseMe) { case kActionNone: case kActionDefault: if (getEntities()->updateEntity(_entityIndex, (CarIndex)params->param1, (EntityPosition)params->param2)) - CALLBACK_ACTION(); + callbackAction(); break; } } @@ -410,7 +440,7 @@ void Entity::callSavepoint(const SavePoint &savepoint, bool handleExcuseMe) { case kActionExitCompartment: if (!CURRENT_PARAM(1, 1)) getSavePoints()->call(_entityIndex, (EntityIndex)params->param4, (ActionIndex)params->param5, (char *)¶ms->seq2); - CALLBACK_ACTION(); + callbackAction(); break; case kActionExcuseMeCath: @@ -448,7 +478,7 @@ void Entity::enterExitCompartment(const SavePoint &savepoint, EntityPosition pos if (updateLocation) getData()->location = kLocationInsideCompartment; - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: @@ -468,6 +498,74 @@ void Entity::enterExitCompartment(const SavePoint &savepoint, EntityPosition pos } } +void Entity::goToCompartment(const SavePoint &savepoint, ObjectIndex compartmentFrom, EntityPosition positionFrom, Common::String sequenceFrom, Common::String sequenceTo) { + switch (savepoint.action) { + default: + break; + + case kActionDefault: + getData()->entityPosition = positionFrom; + setCallback(1); + setup_enterExitCompartment(sequenceFrom.c_str(), compartmentFrom); + break; + + case kActionCallback: + switch (getCallback()) { + default: + break; + + case 1: + setCallback(2); + setup_enterExitCompartment(sequenceTo.c_str(), compartmentFrom); + break; + + case 2: + getData()->entityPosition = positionFrom; + getEntities()->clearSequences(_entityIndex); + callbackAction(); + break; + } + break; + } +} + +void Entity::goToCompartmentFromCompartment(const SavePoint &savepoint, ObjectIndex compartmentFrom, EntityPosition positionFrom, Common::String sequenceFrom, ObjectIndex compartmentTo, EntityPosition positionTo, Common::String sequenceTo) { + switch (savepoint.action) { + default: + break; + + case kActionDefault: + getData()->entityPosition = positionFrom; + getData()->location = kLocationOutsideCompartment; + setCallback(1); + setup_enterExitCompartment(sequenceFrom.c_str(), compartmentFrom); + break; + + case kActionCallback: + switch (getCallback()) { + default: + break; + + case 1: + setCallback(2); + setup_updateEntity(kCarGreenSleeping, positionTo); + break; + + case 2: + setCallback(3); + setup_enterExitCompartment(sequenceTo.c_str(), compartmentTo); + break; + + case 3: + getData()->location = kLocationInsideCompartment; + getEntities()->clearSequences(_entityIndex); + callbackAction(); + break; + } + break; + } +} + void Entity::updatePosition(const SavePoint &savepoint, bool handleExcuseMe) { EXPOSE_PARAMS(EntityData::EntityParametersSIII) @@ -477,7 +575,7 @@ void Entity::updatePosition(const SavePoint &savepoint, bool handleExcuseMe) { case kActionExitCompartment: getEntities()->updatePositionExit(_entityIndex, (CarIndex)params->param4, (Position)params->param5); - CALLBACK_ACTION(); + callbackAction(); break; case kActionExcuseMeCath: @@ -494,4 +592,385 @@ void Entity::updatePosition(const SavePoint &savepoint, bool handleExcuseMe) { } } +void Entity::callbackAction() { + if (getData()->currentCall == 0) + error("[Entity::callbackAction] currentCall is already 0, cannot proceed"); + + getData()->currentCall--; + + getSavePoints()->setCallback(_entityIndex, _callbacks[_data->getCurrentCallback()]); + + getSavePoints()->call(_entityIndex, _entityIndex, kActionCallback); +} + +////////////////////////////////////////////////////////////////////////// +// Setup functions +////////////////////////////////////////////////////////////////////////// +void Entity::setup(const char *name, uint index) { + debugC(6, kLastExpressDebugLogic, "Entity: %s()", name); + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->setCallback(_entityIndex, _callbacks[index]); + _data->setCurrentCallback(index); + _data->resetCurrentParameters<EntityData::EntityParametersIIII>(); + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->call(_entityIndex, _entityIndex, kActionDefault); +} + +void Entity::setupI(const char *name, uint index, uint param1) { + debugC(6, kLastExpressDebugLogic, "Entity: %s(%u)", name, param1); + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->setCallback(_entityIndex, _callbacks[index]); + _data->setCurrentCallback(index); + _data->resetCurrentParameters<EntityData::EntityParametersIIII>(); + + EntityData::EntityParametersIIII *params = (EntityData::EntityParametersIIII *)_data->getCurrentParameters(); + params->param1 = (unsigned int)param1; + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->call(_entityIndex, _entityIndex, kActionDefault); +} + +void Entity::setupII(const char *name, uint index, uint param1, uint param2) { + debugC(6, kLastExpressDebugLogic, "Entity: %s(%u, %u)", name, param1, param2); + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->setCallback(_entityIndex, _callbacks[index]); + _data->setCurrentCallback(index); + _data->resetCurrentParameters<EntityData::EntityParametersIIII>(); + + EntityData::EntityParametersIIII *params = (EntityData::EntityParametersIIII *)_data->getCurrentParameters(); + params->param1 = param1; + params->param2 = param2; + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->call(_entityIndex, _entityIndex, kActionDefault); +} + +void Entity::setupIII(const char *name, uint index, uint param1, uint param2, uint param3) { + debugC(6, kLastExpressDebugLogic, "Entity: %s(%u, %u, %u)", name, param1, param2, param3); + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->setCallback(_entityIndex, _callbacks[index]); + _data->setCurrentCallback(index); + _data->resetCurrentParameters<EntityData::EntityParametersIIII>(); + + EntityData::EntityParametersIIII *params = (EntityData::EntityParametersIIII *)_data->getCurrentParameters(); + params->param1 = param1; + params->param2 = param2; + params->param3 = param3; + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->call(_entityIndex, _entityIndex, kActionDefault); +} + +void Entity::setupS(const char *name, uint index, const char *seq1) { + debugC(6, kLastExpressDebugLogic, "Entity: %s(%s)", name, seq1); + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->setCallback(_entityIndex, _callbacks[index]); + _data->setCurrentCallback(index); + _data->resetCurrentParameters<EntityData::EntityParametersSIIS>(); + + EntityData::EntityParametersSIIS *params = (EntityData::EntityParametersSIIS*)_data->getCurrentParameters(); + strncpy(params->seq1, seq1, 12); + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->call(_entityIndex, _entityIndex, kActionDefault); +} + +void Entity::setupSS(const char *name, uint index, const char *seq1, const char *seq2) { + debugC(6, kLastExpressDebugLogic, "Entity: %s(%s, %s)", name, seq1, seq2); + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->setCallback(_entityIndex, _callbacks[index]); + _data->setCurrentCallback(index); + _data->resetCurrentParameters<EntityData::EntityParametersSSII>(); + + EntityData::EntityParametersSSII *params = (EntityData::EntityParametersSSII*)_data->getCurrentParameters(); + strncpy(params->seq1, seq1, 12); + strncpy(params->seq2, seq2, 12); + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->call(_entityIndex, _entityIndex, kActionDefault); +} + +void Entity::setupSI(const char *name, uint index, const char *seq1, uint param4) { + debugC(6, kLastExpressDebugLogic, "Entity: %s(%s, %u)", name, seq1, param4); + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->setCallback(_entityIndex, _callbacks[index]); + _data->setCurrentCallback(index); + _data->resetCurrentParameters<EntityData::EntityParametersSIIS>(); + + EntityData::EntityParametersSIIS *params = (EntityData::EntityParametersSIIS *)_data->getCurrentParameters(); + strncpy(params->seq1, seq1, 12); + params->param4 = param4; + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->call(_entityIndex, _entityIndex, kActionDefault); +} + +void Entity::setupSII(const char *name, uint index, const char *seq1, uint param4, uint param5) { + debugC(6, kLastExpressDebugLogic, "Entity: %s(%s, %u, %u)", name, seq1, param4, param5); + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->setCallback(_entityIndex, _callbacks[index]); + _data->setCurrentCallback(index); + _data->resetCurrentParameters<EntityData::EntityParametersSIIS>(); + + EntityData::EntityParametersSIIS *params = (EntityData::EntityParametersSIIS *)_data->getCurrentParameters(); + strncpy(params->seq1, seq1, 12); + params->param4 = param4; + params->param5 = param5; + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->call(_entityIndex, _entityIndex, kActionDefault); +} + +void Entity::setupSIII(const char *name, uint index, const char *seq, uint param4, uint param5, uint param6) { + debugC(6, kLastExpressDebugLogic, "Entity: %s(%s, %u, %u, %u)", name, seq, param4, param5, param6); + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->setCallback(_entityIndex, _callbacks[index]); + _data->setCurrentCallback(index); + _data->resetCurrentParameters<EntityData::EntityParametersSIII>(); + + EntityData::EntityParametersSIII *params = (EntityData::EntityParametersSIII *)_data->getCurrentParameters(); + strncpy(params->seq, seq, 12); + params->param4 = param4; + params->param5 = param5; + params->param6 = param6; + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->call(_entityIndex, _entityIndex, kActionDefault); +} + +void Entity::setupSIIS(const char *name, uint index, const char *seq1, uint param4, uint param5, const char *seq2) { + debugC(6, kLastExpressDebugLogic, "Entity: %s(%s, %u, %u, %s)", name, seq1, param4, param5, seq2); + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->setCallback(_entityIndex, _callbacks[index]); + _data->setCurrentCallback(index); + _data->resetCurrentParameters<EntityData::EntityParametersSIIS>(); + + EntityData::EntityParametersSIIS *params = (EntityData::EntityParametersSIIS *)_data->getCurrentParameters(); + strncpy(params->seq1, seq1, 12); + params->param4 = param4; + params->param5 = param5; + strncpy(params->seq2, seq2, 12); + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->call(_entityIndex, _entityIndex, kActionDefault); +} + +void Entity::setupSSI(const char *name, uint index, const char *seq1, const char *seq2, uint param7) { + debugC(6, kLastExpressDebugLogic, "Entity: %s(%s, %s, %u)", name, seq1, seq2, param7); + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->setCallback(_entityIndex, _callbacks[index]); + _data->setCurrentCallback(index); + _data->resetCurrentParameters<EntityData::EntityParametersSSII>(); + + EntityData::EntityParametersSSII *params = (EntityData::EntityParametersSSII *)_data->getCurrentParameters(); + strncpy(params->seq1, seq1, 12); + strncpy(params->seq2, seq2, 12); + params->param7 = param7; + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->call(_entityIndex, _entityIndex, kActionDefault); +} + +void Entity::setupIS(const char *name, uint index, uint param1, const char *seq) { + debugC(6, kLastExpressDebugLogic, "Entity: %s(%u, %s)", name, param1, seq); + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->setCallback(_entityIndex, _callbacks[index]); + _data->setCurrentCallback(index); + _data->resetCurrentParameters<EntityData::EntityParametersISII>(); + + EntityData::EntityParametersISII *params = (EntityData::EntityParametersISII *)_data->getCurrentParameters(); + params->param1 = (unsigned int)param1; + strncpy(params->seq, seq, 12); + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->call(_entityIndex, _entityIndex, kActionDefault); +} + +void Entity::setupISS(const char *name, uint index, uint param1, const char *seq1, const char *seq2) { + debugC(6, kLastExpressDebugLogic, "Entity: %s(%u, %s, %s)", name, param1, seq1, seq2); + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->setCallback(_entityIndex, _callbacks[index]); + _data->setCurrentCallback(index); + _data->resetCurrentParameters<EntityData::EntityParametersISSI>(); + + EntityData::EntityParametersISSI *params = (EntityData::EntityParametersISSI *)_data->getCurrentParameters(); + params->param1 = param1; + strncpy(params->seq1, seq1, 12); + strncpy(params->seq2, seq2, 12); + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->call(_entityIndex, _entityIndex, kActionDefault); +} + +void Entity::setupIIS(const char *name, uint index, uint param1, uint param2, const char *seq) { + debugC(6, kLastExpressDebugLogic, "Entity: %s(%u, %u, %s)", name, param1, param2, seq); + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->setCallback(_entityIndex, _callbacks[index]); + _data->setCurrentCallback(index); + _data->resetCurrentParameters<EntityData::EntityParametersIISI>(); + + EntityData::EntityParametersIISI *params = (EntityData::EntityParametersIISI *)_data->getCurrentParameters(); + params->param1 = param1; + params->param2 = param2; + strncpy(params->seq, seq, 12); + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->call(_entityIndex, _entityIndex, kActionDefault); +} + +void Entity::setupIISS(const char *name, uint index, uint param1, uint param2, const char *seq1, const char *seq2) { + debugC(6, kLastExpressDebugLogic, "Entity: %s(%u, %u, %s, %s)", name, param1, param2, seq1, seq2); + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->setCallback(_entityIndex, _callbacks[index]); + _data->setCurrentCallback(index); + _data->resetCurrentParameters<EntityData::EntityParametersIISS>(); + + EntityData::EntityParametersIISS *params = (EntityData::EntityParametersIISS *)_data->getCurrentParameters(); + params->param1 = param1; + params->param2 = param2; + strncpy(params->seq1, seq1, 12); + strncpy(params->seq2, seq2, 12); + + _engine->getGameLogic()->getGameState()->getGameSavePoints()->call(_entityIndex, _entityIndex, kActionDefault); +} + +////////////////////////////////////////////////////////////////////////// +// Helper functions +////////////////////////////////////////////////////////////////////////// + +bool Entity::updateParameter(uint ¶meter, uint timeType, uint delta) const { + if (!parameter) + parameter = (uint)(timeType + delta); + + if (parameter >= timeType) + return false; + + parameter = kTimeInvalid; + + return true; +} + +bool Entity::updateParameterTime(TimeValue timeValue, bool check, uint ¶meter, uint delta) const { + if (getState()->time <= timeValue) { + if (check || !parameter) + parameter = (uint)(getState()->time + delta); + } + + if (parameter >= getState()->time && getState()->time <= timeValue) + return false; + + parameter = kTimeInvalid; + + return true; +} + +bool Entity::updateParameterCheck(uint ¶meter, uint timeType, uint delta) const { + if (parameter && parameter >= timeType) + return false; + + if (!parameter) + parameter = (uint)(timeType + delta); + + return true; +} + +bool Entity::timeCheck(TimeValue timeValue, uint ¶meter, Common::Functor0<void> *function) const { + if (getState()->time > timeValue && !parameter) { + parameter = 1; + (*function)(); + + return true; + } + + return false; +} + +bool Entity::timeCheckCallback(TimeValue timeValue, uint ¶meter, byte callback, Common::Functor0<void> *function) { + if (getState()->time > timeValue && !parameter) { + parameter = 1; + setCallback(callback); + (*function)(); + + return true; + } + + return false; +} + +bool Entity::timeCheckCallback(TimeValue timeValue, uint ¶meter, byte callback, const char *str, Common::Functor1<const char *, void> *function) { + if (getState()->time > timeValue && !parameter) { + parameter = 1; + setCallback(callback); + (*function)(str); + + return true; + } + + return false; +} + +bool Entity::timeCheckCallback(TimeValue timeValue, uint ¶meter, byte callback, bool check, Common::Functor1<bool, void> *function) { + if (getState()->time > timeValue && !parameter) { + parameter = 1; + setCallback(callback); + (*function)(check); + + return true; + } + + return false; +} + +bool Entity::timeCheckCallbackInventory(TimeValue timeValue, uint ¶meter, byte callback, Common::Functor0<void> *function) { + if (getState()->time > timeValue && !parameter) { + parameter = 1; + getData()->inventoryItem = kItemNone; + setCallback(callback); + (*function)(); + + return true; + } + + return false; +} + +bool Entity::timeCheckCar(TimeValue timeValue, uint ¶meter, byte callback, Common::Functor0<void> *function) { + if ((getState()->time <= timeValue && !getEntities()->isPlayerInCar(kCarGreenSleeping)) || !parameter) + parameter = (uint)getState()->time + 75; + + if (getState()->time > timeValue || parameter < getState()->time) { + parameter = kTimeInvalid; + setCallback(callback); + (*function)(); + + return true; + } + + return false; +} + +void Entity::timeCheckSavepoint(TimeValue timeValue, uint ¶meter, EntityIndex entity1, EntityIndex entity2, ActionIndex action) const { + if (getState()->time > timeValue && !parameter) { + parameter = 1; + getSavePoints()->push(entity1, entity2, action); + } +} + +void Entity::timeCheckObject(TimeValue timeValue, uint ¶meter, ObjectIndex object, ObjectLocation location) const { + if (getState()->time > timeValue && !parameter) { + parameter = 1; + getObjects()->updateLocation2(object, location); + } +} + +bool Entity::timeCheckCallbackAction(TimeValue timeValue, uint ¶meter) { + if (getState()->time > timeValue && !parameter) { + parameter = 1; + callbackAction(); + return true; + } + + return false; +} + +bool Entity::timeCheckPlaySoundUpdatePosition(TimeValue timeValue, uint ¶meter, byte callback, const char* sound, EntityPosition position) { + if (getState()->time > timeValue && !parameter) { + parameter = 1; + getData()->entityPosition = position; + setCallback(callback); + setup_playSound(sound); + return true; + } + + return false; +} + + } // End of namespace LastExpress diff --git a/engines/lastexpress/entities/entity.h b/engines/lastexpress/entities/entity.h index 039f461c7b..c67d13db9e 100644 --- a/engines/lastexpress/entities/entity.h +++ b/engines/lastexpress/entities/entity.h @@ -41,10 +41,229 @@ class Sequence; class SequenceFrame; struct SavePoint; +////////////////////////////////////////////////////////////////////////// +// Declaration +////////////////////////////////////////////////////////////////////////// +#define DECLARE_FUNCTION(name) \ + void setup_##name(); \ + void name(const SavePoint &savepoint); + +#define DECLARE_FUNCTION_1(name, param1) \ + void setup_##name(param1); \ + void name(const SavePoint &savepoint); + +#define DECLARE_FUNCTION_2(name, param1, param2) \ + void setup_##name(param1, param2); \ + void name(const SavePoint &savepoint); + +#define DECLARE_FUNCTION_3(name, param1, param2, param3) \ + void setup_##name(param1, param2, param3); \ + void name(const SavePoint &savepoint); + +#define DECLARE_FUNCTION_4(name, param1, param2, param3, param4) \ + void setup_##name(param1, param2, param3, param4); \ + void name(const SavePoint &savepoint); + +#define DECLARE_FUNCTION_NOSETUP(name) \ + void name(const SavePoint &savepoint); + +#define DECLARE_NULL_FUNCTION() \ + void setup_nullfunction(); + +////////////////////////////////////////////////////////////////////////// +// Callbacks +////////////////////////////////////////////////////////////////////////// +#define ENTITY_CALLBACK(class, name, pointer) \ + Common::Functor1Mem<const SavePoint&, void, class>(pointer, &class::name) + +#define ADD_CALLBACK_FUNCTION(class, name) \ + _callbacks.push_back(new ENTITY_CALLBACK(class, name, this)); + +#define ADD_NULL_FUNCTION() \ + _callbacks.push_back(new ENTITY_CALLBACK(Entity, nullfunction, this)); + +#define WRAP_SETUP_FUNCTION(className, method) \ + new Common::Functor0Mem<void, className>(this, &className::method) + +#define WRAP_SETUP_FUNCTION_S(className, method) \ + new Common::Functor1Mem<const char *, void, className>(this, &className::method) + +#define WRAP_SETUP_FUNCTION_B(className, method) \ + new Common::Functor1Mem<bool, void, className>(this, &className::method) + +////////////////////////////////////////////////////////////////////////// +// Parameters macros +////////////////////////////////////////////////////////////////////////// +#define CURRENT_PARAM(index, id) \ + ((EntityData::EntityParametersIIII*)_data->getCurrentParameters(index))->param##id + +#define ENTITY_PARAM(index, id) \ + ((EntityData::EntityParametersIIII*)_data->getParameters(8, index))->param##id + +////////////////////////////////////////////////////////////////////////// +// Misc +////////////////////////////////////////////////////////////////////////// +#define RESET_ENTITY_STATE(entity, class, function) \ + getEntities()->resetState(entity); \ + ((class *)getEntities()->get(entity))->function(); + +////////////////////////////////////////////////////////////////////////// +// Implementation +////////////////////////////////////////////////////////////////////////// + +// Expose parameters and check validity +#define EXPOSE_PARAMS(type) \ + type *params = (type *)_data->getCurrentParameters(); \ + if (!params) \ + error("[EXPOSE_PARAMS] Trying to call an entity function with invalid parameters"); \ + +// function signature without setup (we keep the index for consistency but never use it) +#define IMPLEMENT_FUNCTION_NOSETUP(index, class, name) \ + void class::name(const SavePoint &savepoint) { \ + debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(index=" #index ")"); + +// simple setup with no parameters +#define IMPLEMENT_FUNCTION(index, class, name) \ + void class::setup_##name() { \ + Entity::setup(#class "::setup_" #name, index); \ + } \ + void class::name(const SavePoint &savepoint) { \ + EXPOSE_PARAMS(EntityData::EntityParametersIIII) \ + debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "() - action: %s", ACTION_NAME(savepoint.action)); + +#define IMPLEMENT_FUNCTION_END } + +// nullfunction call +#define IMPLEMENT_NULL_FUNCTION(index, class) \ + void class::setup_nullfunction() { \ + Entity::setup(#class "::setup_nullfunction", index); \ + } + +// setup with one uint parameter +#define IMPLEMENT_FUNCTION_I(index, class, name, paramType) \ + void class::setup_##name(paramType param1) { \ + Entity::setupI(#class "::setup_" #name, index, param1); \ + } \ + void class::name(const SavePoint &savepoint) { \ + EXPOSE_PARAMS(EntityData::EntityParametersIIII) \ + debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d) - action: %s", params->param1, ACTION_NAME(savepoint.action)); + +// setup with two uint parameters +#define IMPLEMENT_FUNCTION_II(index, class, name, paramType1, paramType2) \ + void class::setup_##name(paramType1 param1, paramType2 param2) { \ + Entity::setupII(#class "::setup_" #name, index, param1, param2); \ + } \ + void class::name(const SavePoint &savepoint) { \ + EXPOSE_PARAMS(EntityData::EntityParametersIIII) \ + debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d, %d) - action: %s", params->param1, params->param2, ACTION_NAME(savepoint.action)); + +// setup with three uint parameters +#define IMPLEMENT_FUNCTION_III(index, class, name, paramType1, paramType2, paramType3) \ + void class::setup_##name(paramType1 param1, paramType2 param2, paramType3 param3) { \ + Entity::setupIII(#class "::setup_" #name, index, param1, param2, param3); \ + } \ + void class::name(const SavePoint &savepoint) { \ + EXPOSE_PARAMS(EntityData::EntityParametersIIII) \ + debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d, %d, %d) - action: %s", params->param1, params->param2, params->param3, ACTION_NAME(savepoint.action)); + +// setup with one char *parameter +#define IMPLEMENT_FUNCTION_S(index, class, name) \ + void class::setup_##name(const char *seq1) { \ + Entity::setupS(#class "::setup_" #name, index, seq1); \ + } \ + void class::name(const SavePoint &savepoint) { \ + EXPOSE_PARAMS(EntityData::EntityParametersSIIS) \ + debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s) - action: %s", (char *)¶ms->seq1, ACTION_NAME(savepoint.action)); + +// setup with one char *parameter and one uint +#define IMPLEMENT_FUNCTION_SI(index, class, name, paramType2) \ + void class::setup_##name(const char *seq1, paramType2 param4) { \ + Entity::setupSI(#class "::setup_" #name, index, seq1, param4); \ + } \ + void class::name(const SavePoint &savepoint) { \ + EXPOSE_PARAMS(EntityData::EntityParametersSIIS) \ + debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s, %d) - action: %s", (char *)¶ms->seq1, params->param4, ACTION_NAME(savepoint.action)); + +// setup with one char *parameter and two uints +#define IMPLEMENT_FUNCTION_SII(index, class, name, paramType2, paramType3) \ + void class::setup_##name(const char *seq1, paramType2 param4, paramType3 param5) { \ + Entity::setupSII(#class "::setup_" #name, index, seq1, param4, param5); \ + } \ + void class::name(const SavePoint &savepoint) { \ + EXPOSE_PARAMS(EntityData::EntityParametersSIIS) \ + debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s, %d, %d) - action: %s", (char *)¶ms->seq1, params->param4, params->param5, ACTION_NAME(savepoint.action)); + +// setup with one char *parameter and three uints +#define IMPLEMENT_FUNCTION_SIII(index, class, name, paramType2, paramType3, paramType4) \ + void class::setup_##name(const char *seq, paramType2 param4, paramType3 param5, paramType4 param6) { \ + Entity::setupSIII(#class "::setup_" #name, index, seq, param4, param5, param6); \ + } \ + void class::name(const SavePoint &savepoint) { \ + EXPOSE_PARAMS(EntityData::EntityParametersSIII) \ + debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s, %d, %d, %d) - action: %s", (char *)¶ms->seq, params->param4, params->param5, params->param6, ACTION_NAME(savepoint.action)); + +#define IMPLEMENT_FUNCTION_SIIS(index, class, name, paramType2, paramType3) \ + void class::setup_##name(const char *seq1, paramType2 param4, paramType3 param5, const char *seq2) { \ + Entity::setupSIIS(#class "::setup_" #name, index, seq1, param4, param5, seq2); \ + } \ + void class::name(const SavePoint &savepoint) { \ + EXPOSE_PARAMS(EntityData::EntityParametersSIIS) \ + debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s, %d, %d, %s) - action: %s", (char *)¶ms->seq1, params->param4, params->param5, (char *)¶ms->seq2, ACTION_NAME(savepoint.action)); + +#define IMPLEMENT_FUNCTION_SS(index, class, name) \ + void class::setup_##name(const char *seq1, const char *seq2) { \ + Entity::setupSS(#class "::setup_" #name, index, seq1, seq2); \ + } \ + void class::name(const SavePoint &savepoint) { \ + EXPOSE_PARAMS(EntityData::EntityParametersSSII) \ + debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s, %s) - action: %s", (char *)¶ms->seq1, (char *)¶ms->seq2, ACTION_NAME(savepoint.action)); + +#define IMPLEMENT_FUNCTION_SSI(index, class, name, paramType3) \ + void class::setup_##name(const char *seq1, const char *seq2, paramType3 param7) { \ + Entity::setupSSI(#class "::setup_" #name, index, seq1, seq2, param7); \ + } \ + void class::name(const SavePoint &savepoint) { \ + EXPOSE_PARAMS(EntityData::EntityParametersSSII) \ + debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s, %s, %d) - action: %s", (char *)¶ms->seq1, (char *)¶ms->seq2, params->param7, ACTION_NAME(savepoint.action)); + +#define IMPLEMENT_FUNCTION_IS(index, class, name, paramType) \ + void class::setup_##name(paramType param1, const char *seq) { \ + Entity::setupIS(#class "::setup_" #name, index, param1, seq); \ + } \ + void class::name(const SavePoint &savepoint) { \ + EXPOSE_PARAMS(EntityData::EntityParametersISII) \ + debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d, %s) - action: %s", params->param1, (char *)¶ms->seq, ACTION_NAME(savepoint.action)); + +#define IMPLEMENT_FUNCTION_ISS(index, class, name, paramType) \ + void class::setup_##name(paramType param1, const char *seq1, const char *seq2) { \ + Entity::setupISS(#class "::setup_" #name, index, param1, seq1, seq2); \ + } \ + void class::name(const SavePoint &savepoint) { \ + EXPOSE_PARAMS(EntityData::EntityParametersISSI) \ + debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d, %s, %s) - action: %s", params->param1, (char *)¶ms->seq1, (char *)¶ms->seq2, ACTION_NAME(savepoint.action)); + +#define IMPLEMENT_FUNCTION_IIS(index, class, name, paramType1, paramType2) \ + void class::setup_##name(paramType1 param1, paramType2 param2, const char *seq) { \ + Entity::setupIIS(#class "::setup_" #name, index, param1, param2, seq); \ + } \ + void class::name(const SavePoint &savepoint) { \ + EXPOSE_PARAMS(EntityData::EntityParametersIISI) \ + debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d, %d, %s) - action: %s", params->param1, params->param2, (char *)¶ms->seq, ACTION_NAME(savepoint.action)); + +#define IMPLEMENT_FUNCTION_IISS(index, class, name, paramType1, paramType2) \ + void class::setup_##name(paramType1 param1, paramType2 param2, const char *seq1, const char *seq2) { \ + Entity::setupIISS(#class "::setup_" #name, index, param1, param2, seq1, seq2); \ + } \ + void class::name(const SavePoint &savepoint) { \ + EXPOSE_PARAMS(EntityData::EntityParametersIISS) \ + debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d, %d, %s, %s) - action: %s", params->param1, params->param2, (char *)¶ms->seq1, (char *)¶ms->seq2, ACTION_NAME(savepoint.action)); + + +////////////////////////////////////////////////////////////////////////// class EntityData : Common::Serializable { public: - struct EntityParameters : Common::Serializable{ + struct EntityParameters : Common::Serializable { virtual ~EntityParameters() {} virtual Common::String toString() = 0; @@ -596,6 +815,15 @@ public: return str; } + /** + * Synchronizes a string. + * + * @param s The Common::Serializer to use. + * @param string The string. + * @param length Length of the string. + */ + void syncString(Common::Serializer &s, Common::String &string, uint length) const; + // Serializable void saveLoadWithSerializer(Common::Serializer &s); }; @@ -611,32 +839,30 @@ public: params->parameters[i] = new T(); } - EntityCallData *getCallData() { return &_data; } + EntityCallData *getCallData() { return &_data; } - EntityParameters *getParameters(uint callback, byte index) const; - EntityParameters *getCurrentParameters(byte index = 0) { return getParameters(_data.currentCall, index); } + EntityParameters *getParameters(uint callback, byte index) const; + EntityParameters *getCurrentParameters(byte index = 0) { return getParameters(_data.currentCall, index); } + EntityCallParameters *getCurrentCallParameters() { return &_parameters[_data.currentCall]; } - int getCallback(uint callback) const; - int getCurrentCallback() { return getCallback(_data.currentCall); } - void setCallback(uint callback, byte index); - void setCurrentCallback(uint index) { setCallback(_data.currentCall, index); } + byte getCallback(uint callback) const; + byte getCurrentCallback() { return getCallback(_data.currentCall); } + void setCallback(uint callback, byte index); + void setCurrentCallback(uint index) { setCallback(_data.currentCall, index); } - void updateParameters(uint32 index) const; + void updateParameters(uint32 index) const; // Serializable - void saveLoadWithSerializer(Common::Serializer &ser); + void saveLoadWithSerializer(Common::Serializer &ser); private: - EntityCallData _data; + EntityCallData _data; EntityCallParameters _parameters[9]; }; class Entity : Common::Serializable { public: - - typedef Common::Functor1<const SavePoint&, void> Callback; - Entity(LastExpressEngine *engine, EntityIndex index); virtual ~Entity(); @@ -657,6 +883,12 @@ public: virtual void setup_chapter4() = 0; virtual void setup_chapter5() = 0; + // Shared functions + virtual void setup_savegame(SavegameType, uint32) { error("[Entity::setup_savegame] Trying to call the parent setup function. Use the specific entity function directly"); } + virtual void setup_enterExitCompartment(const char *, ObjectIndex) { error("[Entity::setup_enterExitCompartment] Trying to call the parent setup function. Use the specific entity function directly"); } + virtual void setup_updateEntity(CarIndex, EntityPosition) { error("[Entity::setup_updateEntity] Trying to call the parent setup function. Use the specific entity function directly"); } + virtual void setup_playSound(const char*) { error("[Entity::setup_playSound] Trying to call the parent setup function. Use the specific entity function directly"); } + // Serializable void saveLoadWithSerializer(Common::Serializer &ser) { _data->saveLoadWithSerializer(ser); } @@ -664,10 +896,11 @@ public: protected: LastExpressEngine *_engine; + typedef Common::Functor1<const SavePoint&, void> Callback; - EntityIndex _entityIndex; - EntityData *_data; - Common::Array<Callback *> _callbacks; + EntityIndex _entityIndex; + EntityData *_data; + Common::Array<Callback *> _callbacks; /** * Saves the game @@ -679,6 +912,13 @@ protected: void savegame(const SavePoint &savepoint); /** + * Saves the game before being found out with a blood covered jacket. + * + * @param saveFunction The setup function to call to save the game + */ + void savegameBloodJacket(); + + /** * Play sound * * @param savepoint The savepoint @@ -782,15 +1022,83 @@ protected: void enterExitCompartment(const SavePoint &savepoint, EntityPosition position1 = kPositionNone, EntityPosition position2 = kPositionNone, CarIndex car = kCarNone, ObjectIndex compartment = kObjectNone, bool alternate = false, bool updateLocation = false); /** + * Go to compartment. + * + * @param savepoint The savepoint. + * @param compartmentFrom The compartment from. + * @param positionFrom The position from. + * @param sequenceFrom The sequence from. + * @param sequenceTo The sequence to. + */ + void goToCompartment(const SavePoint &savepoint, ObjectIndex compartmentFrom, EntityPosition positionFrom, Common::String sequenceFrom, Common::String sequenceTo); + + /** + * Go to compartment from compartment. + * + * @param savepoint The savepoint. + * @param compartmentFrom The compartment from. + * @param positionFrom The position from. + * @param sequenceFrom The sequence from. + * @param compartmentTo The compartment to. + * @param positionTo The position to. + * @param sequenceTo The sequence to. + */ + void goToCompartmentFromCompartment(const SavePoint &savepoint, ObjectIndex compartmentFrom, EntityPosition positionFrom, Common::String sequenceFrom, ObjectIndex compartmentTo, EntityPosition positionTo, Common::String sequenceTo); + + /** * Updates the position * - * @param savepoint The savepoint + * @param savepoint The savepoint * - Sequence name * - CarIndex * - Position * @param handleExcuseMe true to handle excuseMe actions */ void updatePosition(const SavePoint &savepoint, bool handleExcuseMe = false); + + /** + * Store the current callback information and perform the callback action + */ + void callbackAction(); + + ////////////////////////////////////////////////////////////////////////// + // Setup functions + ////////////////////////////////////////////////////////////////////////// + void setup(const char *name, uint index); + void setupI(const char *name, uint index, uint param1); + void setupII(const char *name, uint index, uint param1, uint param2); + void setupIII(const char *name, uint index, uint param1, uint param2, uint param3); + void setupS(const char *name, uint index, const char *seq1); + void setupSS(const char *name, uint index, const char *seq1, const char *seq2); + void setupSI(const char *name, uint index, const char *seq1, uint param4); + void setupSII(const char *name, uint index, const char *seq1, uint param4, uint param5); + void setupSIII(const char *name, uint index, const char *seq, uint param4, uint param5, uint param6); + void setupSIIS(const char *name, uint index, const char *seq1, uint param4, uint param5, const char *seq2); + void setupSSI(const char *name, uint index, const char *seq1, const char *seq2, uint param7); + void setupIS(const char *name, uint index, uint param1, const char *seq); + void setupISS(const char *name, uint index, uint param1, const char *seq1, const char *seq2); + void setupIIS(const char *name, uint index, uint param1, uint param2, const char *seq); + void setupIISS(const char *name, uint index, uint param1, uint param2, const char *seq1, const char *seq2); + + ////////////////////////////////////////////////////////////////////////// + // Helper functions + ////////////////////////////////////////////////////////////////////////// + + bool updateParameter(uint ¶meter, uint timeType, uint delta) const; + bool updateParameterCheck(uint ¶meter, uint timeType, uint delta) const; + bool updateParameterTime(TimeValue timeValue, bool check, uint ¶meter, uint delta) const; + + bool timeCheck(TimeValue timeValue, uint ¶meter, Common::Functor0<void> *function) const; + bool timeCheckCallback(TimeValue timeValue, uint ¶meter, byte callback, Common::Functor0<void> *function); + bool timeCheckCallback(TimeValue timeValue, uint ¶meter, byte callback, const char *str, Common::Functor1<const char *, void> *function); + bool timeCheckCallback(TimeValue timeValue, uint ¶meter, byte callback, bool check, Common::Functor1<bool, void> *function); + bool timeCheckCallbackInventory(TimeValue timeValue, uint ¶meter, byte callback, Common::Functor0<void> *function); + bool timeCheckCar(TimeValue timeValue, uint ¶meter, byte callback, Common::Functor0<void> *function); + void timeCheckSavepoint(TimeValue timeValue, uint ¶meter, EntityIndex entity1, EntityIndex entity2, ActionIndex action) const; + void timeCheckObject(TimeValue timeValue, uint ¶meter, ObjectIndex index, ObjectLocation location) const; + bool timeCheckCallbackAction(TimeValue timeValue, uint ¶meter); + bool timeCheckPlaySoundUpdatePosition(TimeValue timeValue, uint ¶meter, byte callback, const char* sound, EntityPosition position); + }; diff --git a/engines/lastexpress/entities/entity39.cpp b/engines/lastexpress/entities/entity39.cpp index e786d153a0..1786cd2201 100644 --- a/engines/lastexpress/entities/entity39.cpp +++ b/engines/lastexpress/entities/entity39.cpp @@ -28,7 +28,6 @@ #include "lastexpress/game/savepoint.h" #include "lastexpress/game/state.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/entity39.h b/engines/lastexpress/entities/entity39.h index 4335a9566e..54b65408c7 100644 --- a/engines/lastexpress/entities/entity39.h +++ b/engines/lastexpress/entities/entity39.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_ENTITY39_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { @@ -72,4 +71,4 @@ private: } // End of namespace LastExpress -#endif // LASTEXPRESS_##define##_H +#endif // LASTEXPRESS_ENTITY39_H diff --git a/engines/lastexpress/entities/entity_intern.h b/engines/lastexpress/entities/entity_intern.h index 2da0da15b3..fd803676a9 100644 --- a/engines/lastexpress/entities/entity_intern.h +++ b/engines/lastexpress/entities/entity_intern.h @@ -25,502 +25,6 @@ namespace LastExpress { -#define LOW_BYTE(w) ((unsigned char)(((unsigned long)(w)) & 0xff)) - -////////////////////////////////////////////////////////////////////////// -// Callbacks -#define ENTITY_CALLBACK(class, name, pointer) \ - Common::Functor1Mem<const SavePoint&, void, class>(pointer, &class::name) - -#define ADD_CALLBACK_FUNCTION(class, name) \ - _callbacks.push_back(new ENTITY_CALLBACK(class, name, this)); - -#define ADD_NULL_FUNCTION() \ - _callbacks.push_back(new ENTITY_CALLBACK(Entity, nullfunction, this)); - -////////////////////////////////////////////////////////////////////////// -// Declaration -////////////////////////////////////////////////////////////////////////// - -#define DECLARE_FUNCTION(name) \ - void setup_##name(); \ - void name(const SavePoint &savepoint); - -#define DECLARE_FUNCTION_1(name, param1) \ - void setup_##name(param1); \ - void name(const SavePoint &savepoint); - -#define DECLARE_FUNCTION_2(name, param1, param2) \ - void setup_##name(param1, param2); \ - void name(const SavePoint &savepoint); - -#define DECLARE_FUNCTION_3(name, param1, param2, param3) \ - void setup_##name(param1, param2, param3); \ - void name(const SavePoint &savepoint); - -#define DECLARE_FUNCTION_4(name, param1, param2, param3, param4) \ - void setup_##name(param1, param2, param3, param4); \ - void name(const SavePoint &savepoint); - -#define DECLARE_FUNCTION_NOSETUP(name) \ - void name(const SavePoint &savepoint); - -#define DECLARE_NULL_FUNCTION() \ - void setup_nullfunction(); - -////////////////////////////////////////////////////////////////////////// -// Setup -////////////////////////////////////////////////////////////////////////// - -#define IMPLEMENT_SETUP(class, callback_class, name, index) \ -void class::setup_##name() { \ - BEGIN_SETUP(callback_class, name, index, EntityData::EntityParametersIIII) \ - debugC(6, kLastExpressDebugLogic, "Entity: " #class "::setup_" #name "()"); \ - END_SETUP() \ -} - -#define BEGIN_SETUP(class, name, index, type) \ - _engine->getGameLogic()->getGameState()->getGameSavePoints()->setCallback(_entityIndex, _callbacks[index]); \ - _data->setCurrentCallback(index); \ - _data->resetCurrentParameters<type>(); - -#define END_SETUP() \ - _engine->getGameLogic()->getGameState()->getGameSavePoints()->call(_entityIndex, _entityIndex, kActionDefault); - - -////////////////////////////////////////////////////////////////////////// -// Implementation -////////////////////////////////////////////////////////////////////////// - -// Expose parameters and check validity -#define EXPOSE_PARAMS(type) \ - type *params = (type *)_data->getCurrentParameters(); \ - if (!params) \ - error("[EXPOSE_PARAMS] Trying to call an entity function with invalid parameters"); \ - - -// function signature without setup (we keep the index for consistency but never use it) -#define IMPLEMENT_FUNCTION_NOSETUP(index, class, name) \ - void class::name(const SavePoint &savepoint) { \ - debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(index=" #index ")"); - -// simple setup with no parameters -#define IMPLEMENT_FUNCTION(index, class, name) \ - IMPLEMENT_SETUP(class, class, name, index) \ - void class::name(const SavePoint &savepoint) { \ - EXPOSE_PARAMS(EntityData::EntityParametersIIII) \ - debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "() - action: %s", ACTION_NAME(savepoint.action)); - -#define IMPLEMENT_FUNCTION_END } - -// nullfunction call -#define IMPLEMENT_NULL_FUNCTION(index, class) \ - IMPLEMENT_SETUP(class, Entity, nullfunction, index) - -// setup with one uint parameter -#define IMPLEMENT_FUNCTION_I(index, class, name, paramType) \ - void class::setup_##name(paramType param1) { \ - BEGIN_SETUP(class, name, index, EntityData::EntityParametersIIII) \ - EntityData::EntityParametersIIII *params = (EntityData::EntityParametersIIII*)_data->getCurrentParameters(); \ - params->param1 = (unsigned int)param1; \ - END_SETUP() \ - } \ - void class::name(const SavePoint &savepoint) { \ - EXPOSE_PARAMS(EntityData::EntityParametersIIII) \ - debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d) - action: %s", params->param1, ACTION_NAME(savepoint.action)); - -// setup with two uint parameters -#define IMPLEMENT_FUNCTION_II(index, class, name, paramType1, paramType2) \ - void class::setup_##name(paramType1 param1, paramType2 param2) { \ - BEGIN_SETUP(class, name, index, EntityData::EntityParametersIIII) \ - EntityData::EntityParametersIIII *params = (EntityData::EntityParametersIIII*)_data->getCurrentParameters(); \ - params->param1 = param1; \ - params->param2 = param2; \ - END_SETUP() \ - } \ - void class::name(const SavePoint &savepoint) { \ - EXPOSE_PARAMS(EntityData::EntityParametersIIII) \ - debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d, %d) - action: %s", params->param1, params->param2, ACTION_NAME(savepoint.action)); - -// setup with three uint parameters -#define IMPLEMENT_FUNCTION_III(index, class, name, paramType1, paramType2, paramType3) \ - void class::setup_##name(paramType1 param1, paramType2 param2, paramType3 param3) { \ - BEGIN_SETUP(class, name, index, EntityData::EntityParametersIIII) \ - EntityData::EntityParametersIIII *params = (EntityData::EntityParametersIIII*)_data->getCurrentParameters(); \ - params->param1 = param1; \ - params->param2 = param2; \ - params->param3 = param3; \ - END_SETUP() \ - } \ - void class::name(const SavePoint &savepoint) { \ - EXPOSE_PARAMS(EntityData::EntityParametersIIII) \ - debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d, %d, %d) - action: %s", params->param1, params->param2, params->param3, ACTION_NAME(savepoint.action)); - -// setup with one char *parameter -#define IMPLEMENT_FUNCTION_S(index, class, name) \ - void class::setup_##name(const char *seq1) { \ - BEGIN_SETUP(class, name, index, EntityData::EntityParametersSIIS) \ - EntityData::EntityParametersSIIS *params = (EntityData::EntityParametersSIIS*)_data->getCurrentParameters(); \ - strncpy((char *)¶ms->seq1, seq1, 12); \ - END_SETUP() \ - } \ - void class::name(const SavePoint &savepoint) { \ - EXPOSE_PARAMS(EntityData::EntityParametersSIIS) \ - debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s) - action: %s", (char *)¶ms->seq1, ACTION_NAME(savepoint.action)); - -// setup with one char *parameter and one uint -#define IMPLEMENT_FUNCTION_SI(index, class, name, paramType2) \ - void class::setup_##name(const char *seq1, paramType2 param4) { \ - BEGIN_SETUP(class, name, index, EntityData::EntityParametersSIIS) \ - EntityData::EntityParametersSIIS *params = (EntityData::EntityParametersSIIS*)_data->getCurrentParameters(); \ - strncpy((char *)¶ms->seq1, seq1, 12); \ - params->param4 = param4; \ - END_SETUP() \ - } \ - void class::name(const SavePoint &savepoint) { \ - EXPOSE_PARAMS(EntityData::EntityParametersSIIS) \ - debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s, %d) - action: %s", (char *)¶ms->seq1, params->param4, ACTION_NAME(savepoint.action)); - -// setup with one char *parameter and two uints -#define IMPLEMENT_FUNCTION_SII(index, class, name, paramType2, paramType3) \ - void class::setup_##name(const char *seq1, paramType2 param4, paramType3 param5) { \ - BEGIN_SETUP(class, name, index, EntityData::EntityParametersSIIS) \ - EntityData::EntityParametersSIIS *params = (EntityData::EntityParametersSIIS*)_data->getCurrentParameters(); \ - strncpy((char *)¶ms->seq1, seq1, 12); \ - params->param4 = param4; \ - params->param5 = param5; \ - END_SETUP() \ - } \ - void class::name(const SavePoint &savepoint) { \ - EXPOSE_PARAMS(EntityData::EntityParametersSIIS) \ - debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s, %d, %d) - action: %s", (char *)¶ms->seq1, params->param4, params->param5, ACTION_NAME(savepoint.action)); - -// setup with one char *parameter and three uints -#define IMPLEMENT_FUNCTION_SIII(index, class, name, paramType2, paramType3, paramType4) \ - void class::setup_##name(const char *seq, paramType2 param4, paramType3 param5, paramType4 param6) { \ - BEGIN_SETUP(class, name, index, EntityData::EntityParametersSIII) \ - EntityData::EntityParametersSIII *params = (EntityData::EntityParametersSIII*)_data->getCurrentParameters(); \ - strncpy((char *)¶ms->seq, seq, 12); \ - params->param4 = param4; \ - params->param5 = param5; \ - params->param6 = param6; \ - END_SETUP() \ - } \ - void class::name(const SavePoint &savepoint) { \ - EXPOSE_PARAMS(EntityData::EntityParametersSIII) \ - debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s, %d, %d, %d) - action: %s", (char *)¶ms->seq, params->param4, params->param5, params->param6, ACTION_NAME(savepoint.action)); - -#define IMPLEMENT_FUNCTION_SIIS(index, class, name, paramType2, paramType3) \ - void class::setup_##name(const char *seq1, paramType2 param4, paramType3 param5, const char *seq2) { \ - BEGIN_SETUP(class, name, index, EntityData::EntityParametersSIIS) \ - EntityData::EntityParametersSIIS *params = (EntityData::EntityParametersSIIS*)_data->getCurrentParameters(); \ - strncpy((char *)¶ms->seq1, seq1, 12); \ - params->param4 = param4; \ - params->param5 = param5; \ - strncpy((char *)¶ms->seq2, seq2, 12); \ - END_SETUP() \ - } \ - void class::name(const SavePoint &savepoint) { \ - EXPOSE_PARAMS(EntityData::EntityParametersSIIS) \ - debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s, %d, %d, %s) - action: %s", (char *)¶ms->seq1, params->param4, params->param5, (char *)¶ms->seq2, ACTION_NAME(savepoint.action)); - -#define IMPLEMENT_FUNCTION_SS(index, class, name) \ - void class::setup_##name(const char *seq1, const char *seq2) { \ - BEGIN_SETUP(class, name, index, EntityData::EntityParametersSSII) \ - EntityData::EntityParametersSSII *params = (EntityData::EntityParametersSSII*)_data->getCurrentParameters(); \ - strncpy((char *)¶ms->seq1, seq1, 12); \ - strncpy((char *)¶ms->seq2, seq2, 12); \ - END_SETUP() \ - } \ - void class::name(const SavePoint &savepoint) { \ - EXPOSE_PARAMS(EntityData::EntityParametersSSII) \ - debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s, %s) - action: %s", (char *)¶ms->seq1, (char *)¶ms->seq2, ACTION_NAME(savepoint.action)); - -#define IMPLEMENT_FUNCTION_SSI(index, class, name, paramType3) \ - void class::setup_##name(const char *seq1, const char *seq2, paramType3 param7) { \ - BEGIN_SETUP(class, name, index, EntityData::EntityParametersSSII) \ - EntityData::EntityParametersSSII *params = (EntityData::EntityParametersSSII*)_data->getCurrentParameters(); \ - strncpy((char *)¶ms->seq1, seq1, 12); \ - strncpy((char *)¶ms->seq2, seq2, 12); \ - params->param7 = param7; \ - END_SETUP() \ - } \ - void class::name(const SavePoint &savepoint) { \ - EXPOSE_PARAMS(EntityData::EntityParametersSSII) \ - debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s, %s, %d) - action: %s", (char *)¶ms->seq1, (char *)¶ms->seq2, params->param7, ACTION_NAME(savepoint.action)); - -#define IMPLEMENT_FUNCTION_IS(index, class, name, paramType) \ - void class::setup_##name(paramType param1, const char *seq) { \ - BEGIN_SETUP(class, name, index, EntityData::EntityParametersISII) \ - EntityData::EntityParametersISII *params = (EntityData::EntityParametersISII*)_data->getCurrentParameters(); \ - params->param1 = (unsigned int)param1; \ - strncpy((char *)¶ms->seq, seq, 12); \ - END_SETUP() \ - } \ - void class::name(const SavePoint &savepoint) { \ - EXPOSE_PARAMS(EntityData::EntityParametersISII) \ - debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d, %s) - action: %s", params->param1, (char *)¶ms->seq, ACTION_NAME(savepoint.action)); - -#define IMPLEMENT_FUNCTION_ISS(index, class, name, paramType) \ - void class::setup_##name(paramType param1, const char *seq1, const char *seq2) { \ - BEGIN_SETUP(class, name, index, EntityData::EntityParametersISSI) \ - EntityData::EntityParametersISSI *params = (EntityData::EntityParametersISSI*)_data->getCurrentParameters(); \ - params->param1 = param1; \ - strncpy((char *)¶ms->seq1, seq1, 12); \ - strncpy((char *)¶ms->seq2, seq2, 12); \ - END_SETUP() \ - } \ - void class::name(const SavePoint &savepoint) { \ - EXPOSE_PARAMS(EntityData::EntityParametersISSI) \ - debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d, %s, %s) - action: %s", params->param1, (char *)¶ms->seq1, (char *)¶ms->seq2, ACTION_NAME(savepoint.action)); - -#define IMPLEMENT_FUNCTION_IIS(index, class, name, paramType1, paramType2) \ - void class::setup_##name(paramType1 param1, paramType2 param2, const char *seq) { \ - BEGIN_SETUP(class, name, index, EntityData::EntityParametersIISI) \ - EntityData::EntityParametersIISI *params = (EntityData::EntityParametersIISI*)_data->getCurrentParameters(); \ - params->param1 = param1; \ - params->param2 = param2; \ - strncpy((char *)¶ms->seq, seq, 12); \ - END_SETUP() \ - } \ - void class::name(const SavePoint &savepoint) { \ - EXPOSE_PARAMS(EntityData::EntityParametersIISI) \ - debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d, %d, %s) - action: %s", params->param1, params->param2, (char *)¶ms->seq, ACTION_NAME(savepoint.action)); - -#define IMPLEMENT_FUNCTION_IISS(index, class, name, paramType1, paramType2) \ - void class::setup_##name(paramType1 param1, paramType2 param2, const char *seq1, const char *seq2) { \ - BEGIN_SETUP(class, name, index, EntityData::EntityParametersIISS) \ - EntityData::EntityParametersIISS *params = (EntityData::EntityParametersIISS*)_data->getCurrentParameters(); \ - params->param1 = param1; \ - params->param2 = param2; \ - strncpy((char *)¶ms->seq1, seq1, 12); \ - strncpy((char *)¶ms->seq2, seq2, 12); \ - END_SETUP() \ - } \ - void class::name(const SavePoint &savepoint) { \ - EXPOSE_PARAMS(EntityData::EntityParametersIISS) \ - debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d, %d, %s, %s) - action: %s", params->param1, params->param2, (char *)¶ms->seq1, (char *)¶ms->seq2, ACTION_NAME(savepoint.action)); - - -////////////////////////////////////////////////////////////////////////// -// Misc -////////////////////////////////////////////////////////////////////////// -#define RESET_ENTITY_STATE(entity, class, function) \ - getEntities()->resetState(entity); \ - ((class *)getEntities()->get(entity))->function(); - -////////////////////////////////////////////////////////////////////////// -// Parameters macros (for default IIII parameters) -////////////////////////////////////////////////////////////////////////// -#define CURRENT_PARAM(index, id) \ - ((EntityData::EntityParametersIIII*)_data->getCurrentParameters(index))->param##id - -#define ENTITY_PARAM(index, id) \ - ((EntityData::EntityParametersIIII*)_data->getParameters(8, index))->param##id - -////////////////////////////////////////////////////////////////////////// -// Time check macros -////////////////////////////////////////////////////////////////////////// -#define TIME_CHECK(timeValue, parameter, function) \ - if (getState()->time > timeValue && !parameter) { \ - parameter = 1; \ - function(); \ - break; \ - } - -#define TIME_CHECK_SAVEPOINT(timeValue, parameter, entity1, entity2, action) \ - if (getState()->time > timeValue && !parameter) { \ - parameter = 1; \ - getSavePoints()->push(entity1, entity2, action); \ - } - -#define TIME_CHECK_CALLBACK(timeValue, parameter, callback, function) \ - if (getState()->time > timeValue && !parameter) { \ - parameter = 1; \ - setCallback(callback); \ - function(); \ - break; \ - } - -#define TIME_CHECK_CALLBACK_1(timeValue, parameter, callback, function, param1) \ - if (getState()->time > timeValue && !parameter) { \ - parameter = 1; \ - setCallback(callback); \ - function(param1); \ - break; \ - } - -#define TIME_CHECK_CALLBACK_2(timeValue, parameter, callback, function, param1, param2) \ - if (getState()->time > timeValue && !parameter) { \ - parameter = 1; \ - setCallback(callback); \ - function(param1, param2); \ - break; \ - } - -#define TIME_CHECK_CALLBACK_3(timeValue, parameter, callback, function, param1, param2, param3) \ - if (getState()->time > timeValue && !parameter) { \ - parameter = 1; \ - setCallback(callback); \ - function(param1, param2, param3); \ - break; \ - } - -#define TIME_CHECK_CALLBACK_INVENTORY(timeValue, parameter, callback, function) \ - if (getState()->time > timeValue && !parameter) { \ - parameter = 1; \ - getData()->inventoryItem = kItemNone; \ - setCallback(callback); \ - function(); \ - break; \ - } - -#define TIME_CHECK_CALLBACK_ACTION(timeValue, parameter) \ - if (getState()->time > timeValue && !parameter) { \ - parameter = 1; \ - CALLBACK_ACTION(); \ - break; \ - } - -#define TIME_CHECK_PLAYSOUND_UPDATEPOSITION(timeValue, parameter, callback, sound, position) \ - if (getState()->time > timeValue && !parameter) { \ - parameter = 1; \ - getData()->entityPosition = position; \ - setCallback(callback); \ - setup_playSound(sound); \ - break; \ - } - -#define TIME_CHECK_OBJECT(timeValue, parameter, object, location) \ - if (getState()->time > timeValue && !parameter) { \ - parameter = 1; \ - getObjects()->updateLocation2(object, location); \ - } - -#define TIME_CHECK_CAR(timeValue, parameter, callback, function) {\ - if ((getState()->time <= timeValue && !getEntities()->isPlayerInCar(kCarGreenSleeping)) || !parameter) \ - parameter = (uint)getState()->time + 75; \ - if (getState()->time > timeValue || parameter < getState()->time) { \ - parameter = kTimeInvalid; \ - setCallback(callback); \ - function(); \ - break; \ - } \ -} - -////////////////////////////////////////////////////////////////////////// -// Callback action -////////////////////////////////////////////////////////////////////////// -#define CALLBACK_ACTION() { \ - if (getData()->currentCall == 0) \ - error("[CALLBACK_ACTION] currentCall is already 0, cannot proceed"); \ - getData()->currentCall--; \ - getSavePoints()->setCallback(_entityIndex, _callbacks[_data->getCurrentCallback()]); \ - getSavePoints()->call(_entityIndex, _entityIndex, kActionCallback); \ - } - -////////////////////////////////////////////////////////////////////////// -// Param update -////////////////////////////////////////////////////////////////////////// -#define UPDATE_PARAM(parameter, type, value) { \ - if (!parameter) \ - parameter = (uint)(type + value); \ - if (parameter >= type) \ - break; \ - parameter = kTimeInvalid; \ -} - -// Todo: replace with UPDATE_PARAM_PROC as appropriate -#define UPDATE_PARAM_GOTO(parameter, type, value, label) { \ - if (!parameter) \ - parameter = (uint)(type + value); \ - if (parameter >= type) \ - goto label; \ - parameter = kTimeInvalid; \ -} - -// Updating parameter with code inside the check -#define UPDATE_PARAM_PROC(parameter, type, value) \ - if (!parameter) \ - parameter = (uint)(type + value); \ - if (parameter < type) { \ - parameter = kTimeInvalid; - -#define UPDATE_PARAM_PROC_TIME(timeValue, test, parameter, value) \ - if (getState()->time <= timeValue) { \ - if (test || !parameter) \ - parameter = (uint)(getState()->time + value); \ - } \ - if (parameter < getState()->time || getState()->time > timeValue) { \ - parameter = kTimeInvalid; - -#define UPDATE_PARAM_PROC_END } - -// Updating parameter with an added check (and code inside the check) -#define UPDATE_PARAM_CHECK(parameter, type, value) \ - if (!parameter || parameter < type) { \ - if (!parameter) \ - parameter = (uint)(type + value); - -////////////////////////////////////////////////////////////////////////// -// Compartments -////////////////////////////////////////////////////////////////////////// -// Go from one compartment to another (or the same one if no optional args are passed -#define COMPARTMENT_TO(class, compartmentFrom, positionFrom, sequenceFrom, sequenceTo) \ - switch (savepoint.action) { \ - default: \ - break; \ - case kActionDefault: \ - getData()->entityPosition = positionFrom; \ - setCallback(1); \ - setup_enterExitCompartment(sequenceFrom, compartmentFrom); \ - break; \ - case kActionCallback: \ - switch (getCallback()) { \ - default: \ - break; \ - case 1: \ - setCallback(2); \ - setup_enterExitCompartment(sequenceTo, compartmentFrom); \ - break; \ - case 2: \ - getData()->entityPosition = positionFrom; \ - getEntities()->clearSequences(_entityIndex); \ - CALLBACK_ACTION(); \ - } \ - break; \ - } - -#define COMPARTMENT_FROM_TO(class, compartmentFrom, positionFrom, sequenceFrom, compartmentTo, positionTo, sequenceTo) \ - switch (savepoint.action) { \ - default: \ - break; \ - case kActionDefault: \ - getData()->entityPosition = positionFrom; \ - getData()->location = kLocationOutsideCompartment; \ - setCallback(1); \ - setup_enterExitCompartment(sequenceFrom, compartmentFrom); \ - break; \ - case kActionCallback: \ - switch (getCallback()) { \ - default: \ - break; \ - case 1: \ - setCallback(2); \ - setup_updateEntity(kCarGreenSleeping, positionTo); \ - break; \ - case 2: \ - setCallback(3); \ - setup_enterExitCompartment(sequenceTo, compartmentTo); \ - break; \ - case 3: \ - getData()->location = kLocationInsideCompartment; \ - getEntities()->clearSequences(_entityIndex); \ - CALLBACK_ACTION(); \ - break; \ - } \ - break; \ - } } // End of namespace LastExpress diff --git a/engines/lastexpress/entities/francois.cpp b/engines/lastexpress/entities/francois.cpp index 46cd790ffb..d2bbc9854c 100644 --- a/engines/lastexpress/entities/francois.cpp +++ b/engines/lastexpress/entities/francois.cpp @@ -32,7 +32,6 @@ #include "lastexpress/sound/queue.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" namespace LastExpress { @@ -114,7 +113,7 @@ IMPLEMENT_FUNCTION_II(8, Francois, updateEntity, CarIndex, EntityPosition) case kActionNone: if (getEntities()->updateEntity(_entityIndex, (CarIndex)params->param1, (EntityPosition)params->param2)) { - CALLBACK_ACTION(); + callbackAction(); } else { if (!getEntities()->isDistanceBetweenEntities(kEntityFrancois, kEntityPlayer, 2000) || !getInventory()->hasItem(kItemFirebird) @@ -169,7 +168,7 @@ IMPLEMENT_FUNCTION_II(8, Francois, updateEntity, CarIndex, EntityPosition) case kActionDefault: if (getEntities()->updateEntity(_entityIndex, (CarIndex)params->param1, (EntityPosition)params->param2)) - CALLBACK_ACTION(); + callbackAction(); break; case kActionCallback: @@ -225,7 +224,7 @@ IMPLEMENT_FUNCTION(9, Francois, function9) case 2: getData()->location = kLocationOutsideCompartment; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -263,7 +262,7 @@ IMPLEMENT_FUNCTION(10, Francois, function10) getData()->location = kLocationInsideCompartment; getEntities()->clearSequences(kEntityFrancois); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -279,7 +278,7 @@ IMPLEMENT_FUNCTION_I(11, Francois, function11, TimeValue) case kActionNone: if (!getSoundQueue()->isBuffered(kEntityFrancois)) { - UPDATE_PARAM_PROC(CURRENT_PARAM(1, 1), getState()->timeTicks, params->param6) + if (Entity::updateParameter(CURRENT_PARAM(1, 1), getState()->timeTicks, params->param6)) { switch (rnd(7)) { default: break; @@ -312,7 +311,7 @@ IMPLEMENT_FUNCTION_I(11, Francois, function11, TimeValue) params->param6 = 15 * rnd(7); CURRENT_PARAM(1, 1) = 0; - UPDATE_PARAM_PROC_END + } } if (!getEntities()->hasValidFrame(kEntityFrancois) || !getEntities()->isWalkingOppositeToPlayer(kEntityFrancois)) @@ -442,7 +441,7 @@ label_callback: break; case 5: - CALLBACK_ACTION(); + callbackAction(); break; case 6: @@ -465,7 +464,7 @@ label_callback: getData()->field_4A3 = 30; getData()->inventoryItem = kItemNone; - CALLBACK_ACTION(); + callbackAction(); break; case kAction205346192: @@ -524,7 +523,7 @@ IMPLEMENT_FUNCTION(12, Francois, function12) break; case 7: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -609,7 +608,7 @@ IMPLEMENT_FUNCTION(13, Francois, function13) break; case 11: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -708,7 +707,7 @@ IMPLEMENT_FUNCTION_IIS(14, Francois, function14, ObjectIndex, EntityPosition) break; case 13: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -764,7 +763,7 @@ IMPLEMENT_FUNCTION(15, Francois, function15) case 7: if (!getEntities()->isInsideCompartment(kEntityMmeBoutarel, kCarRedSleeping, kPosition_5790)) { - CALLBACK_ACTION(); + callbackAction(); break; } @@ -773,7 +772,7 @@ IMPLEMENT_FUNCTION(15, Francois, function15) break; case 8: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -830,7 +829,7 @@ IMPLEMENT_FUNCTION(16, Francois, function16) getData()->entityPosition = kPosition_5790; getData()->location = kLocationInsideCompartment; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -849,7 +848,7 @@ IMPLEMENT_FUNCTION(17, Francois, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler); + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(Francois, setup_chapter1Handler)); break; case kActionDefault: @@ -867,7 +866,7 @@ IMPLEMENT_FUNCTION(18, Francois, chapter1Handler) break; case kActionNone: - TIME_CHECK_CALLBACK_1(kTimeParisEpernay, params->param1, 1, setup_function11, kTime1093500); + timeCheckCallback(kTimeParisEpernay, params->param1, 1, kTime1093500); break; case kActionCallback: @@ -884,7 +883,7 @@ IMPLEMENT_FUNCTION(19, Francois, function19) break; case kActionNone: - TIME_CHECK_CALLBACK(kTime1161000, params->param1, 2, setup_function12); + Entity::timeCheckCallback(kTime1161000, params->param1, 2, WRAP_SETUP_FUNCTION(Francois, setup_function12)); break; case kAction101107728: @@ -979,17 +978,21 @@ IMPLEMENT_FUNCTION(23, Francois, function23) } label_callback_1: - TIME_CHECK_CALLBACK_1(kTime1764000, params->param1, 2, setup_playSound, "Fra2011"); + if (Entity::timeCheckCallback(kTime1764000, params->param1, 2, "Fra2011", WRAP_SETUP_FUNCTION_S(Francois, setup_playSound))) + break; label_callback_2: - TIME_CHECK_CALLBACK(kTime1800000, params->param2, 3, setup_function13); + if (Entity::timeCheckCallback(kTime1800000, params->param2, 3, WRAP_SETUP_FUNCTION(Francois, setup_function13))) + break; label_callback_3: if (!getInventory()->hasItem(kItemWhistle) && getInventory()->get(kItemWhistle)->location != kObjectLocation3) { - TIME_CHECK_CALLBACK_1(kTime1768500, params->param3, 4, setup_function11, kTime1773000); + if (timeCheckCallback(kTime1768500, params->param3, 4, kTime1773000)) + break; label_callback_4: - TIME_CHECK_CALLBACK_1(kTime1827000, params->param4, 5, setup_function11, kTime1831500); + if (timeCheckCallback(kTime1827000, params->param4, 5, kTime1831500)) + break; } label_callback_5: @@ -999,18 +1002,19 @@ label_callback_5: } if (params->param5 != kTimeInvalid) { - UPDATE_PARAM_PROC_TIME(kTimeEnd, !getEntities()->isDistanceBetweenEntities(kEntityFrancois, kEntityPlayer, 2000), params->param5, 75); + if (Entity::updateParameterTime(kTimeEnd, !getEntities()->isDistanceBetweenEntities(kEntityFrancois, kEntityPlayer, 2000), params->param5, 75)) { setCallback(6); setup_playSound("Fra2010"); break; - UPDATE_PARAM_PROC_END + } } label_callback_6: - TIME_CHECK_CALLBACK_3(kTime1782000, params->param6, 7, setup_function14, kObjectCompartmentC, kPosition_6470, "c"); + if (timeCheckCallbackCompartment(kTime1782000, params->param6, 7, kObjectCompartmentC, kPosition_6470, "c")) + break; label_callback_7: - TIME_CHECK_CALLBACK_3(kTime1813500, params->param7, 8, setup_function14, kObjectCompartmentF, kPosition_4070, "f"); + timeCheckCallbackCompartment(kTime1813500, params->param7, 8, kObjectCompartmentF, kPosition_4070, "f"); break; case kActionCallback: @@ -1086,32 +1090,41 @@ IMPLEMENT_FUNCTION(25, Francois, chapter3Handler) } label_callback_2: - TIME_CHECK_CALLBACK(kTime2025000, params->param3, 3, setup_function12); + if (Entity::timeCheckCallback(kTime2025000, params->param3, 3, WRAP_SETUP_FUNCTION(Francois, setup_function12))) + break; label_callback_3: - TIME_CHECK_CALLBACK(kTime2052000, params->param4, 4, setup_function12); + if (Entity::timeCheckCallback(kTime2052000, params->param4, 4, WRAP_SETUP_FUNCTION(Francois, setup_function12))) + break; label_callback_4: - TIME_CHECK_CALLBACK(kTime2079000, params->param5, 5, setup_function12); + if (Entity::timeCheckCallback(kTime2079000, params->param5, 5, WRAP_SETUP_FUNCTION(Francois, setup_function12))) + break; label_callback_5: - TIME_CHECK_CALLBACK(kTime2092500, params->param6, 6, setup_function12); + if (Entity::timeCheckCallback(kTime2092500, params->param6, 6, WRAP_SETUP_FUNCTION(Francois, setup_function12))) + break; label_callback_6: - TIME_CHECK_CALLBACK(kTime2173500, params->param7, 7, setup_function12); + if (Entity::timeCheckCallback(kTime2173500, params->param7, 7, WRAP_SETUP_FUNCTION(Francois, setup_function12))) + break; label_callback_7: - TIME_CHECK_CALLBACK(kTime2182500, params->param8, 8, setup_function12); + if (Entity::timeCheckCallback(kTime2182500, params->param8, 8, WRAP_SETUP_FUNCTION(Francois, setup_function12))) + break; label_callback_8: - TIME_CHECK_CALLBACK(kTime2241000, CURRENT_PARAM(1, 1), 9, setup_function12); + if (Entity::timeCheckCallback(kTime2241000, CURRENT_PARAM(1, 1), 9, WRAP_SETUP_FUNCTION(Francois, setup_function12))) + break; label_callback_9: if (!getInventory()->hasItem(kItemWhistle) && getInventory()->get(kItemWhistle)->location != kObjectLocation3) { - TIME_CHECK_CALLBACK_1(kTime2011500, CURRENT_PARAM(1, 2), 10, setup_function11, kTime2016000); + if (timeCheckCallback(kTime2011500, CURRENT_PARAM(1, 2), 10, kTime2016000)) + break; label_callback_10: - TIME_CHECK_CALLBACK_1(kTime2115000, CURRENT_PARAM(1, 3), 11, setup_function11, kTime2119500); + if (timeCheckCallback(kTime2115000, CURRENT_PARAM(1, 3), 11, kTime2119500)) + break; } label_callback_11: @@ -1129,13 +1142,15 @@ label_callback_11: } label_callback_12: - TIME_CHECK_CALLBACK_3(kTime2040300, CURRENT_PARAM(1, 5), 13, setup_function14, kObjectCompartmentE, kPosition_4840, "e"); + if (timeCheckCallbackCompartment(kTime2040300, CURRENT_PARAM(1, 5), 13, kObjectCompartmentE, kPosition_4840, "e")) + break; label_callback_13: - TIME_CHECK_CALLBACK_3(kTime2040300, CURRENT_PARAM(1, 6), 14, setup_function14, kObjectCompartmentF, kPosition_4070, "f"); + if (timeCheckCallbackCompartment(kTime2040300, CURRENT_PARAM(1, 6), 14, kObjectCompartmentF, kPosition_4070, "f")) + break; label_callback_14: - TIME_CHECK_CALLBACK_3(kTime2040300, CURRENT_PARAM(1, 7), 15, setup_function14, kObjectCompartmentB, kPosition_7500, "b"); + timeCheckCallbackCompartment(kTime2040300, CURRENT_PARAM(1, 7), 15, kObjectCompartmentB, kPosition_7500, "b"); } } break; @@ -1291,4 +1306,33 @@ IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// IMPLEMENT_NULL_FUNCTION(31, Francois) + +////////////////////////////////////////////////////////////////////////// +// Helper functions +////////////////////////////////////////////////////////////////////////// +bool Francois::timeCheckCallbackCompartment(TimeValue timeValue, uint ¶meter, byte callback, ObjectIndex compartment, EntityPosition position, const char* sequenceSuffix) { + if (getState()->time > timeValue && !parameter) { + parameter = 1; + setCallback(callback); + setup_function14(compartment, position, sequenceSuffix); + + return true; + } + + return false; +} + +bool Francois::timeCheckCallback(TimeValue timeValue, uint ¶meter, byte callback, TimeValue timeValue2) { + if (getState()->time > timeValue && !parameter) { + parameter = 1; + setCallback(callback); + setup_function11(timeValue2); + + return true; + } + + return false; +} + + } // End of namespace LastExpress diff --git a/engines/lastexpress/entities/francois.h b/engines/lastexpress/entities/francois.h index 19eca6fb50..51270fa4b6 100644 --- a/engines/lastexpress/entities/francois.h +++ b/engines/lastexpress/entities/francois.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_FRANCOIS_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { @@ -160,6 +159,10 @@ public: DECLARE_FUNCTION(function30) DECLARE_NULL_FUNCTION() + +private: + bool timeCheckCallbackCompartment(TimeValue timeValue, uint ¶meter, byte callback, ObjectIndex compartment, EntityPosition position, const char* sequenceSuffix); + bool timeCheckCallback(TimeValue timeValue, uint ¶meter, byte callback, TimeValue timeValue2); }; } // End of namespace LastExpress diff --git a/engines/lastexpress/entities/gendarmes.cpp b/engines/lastexpress/entities/gendarmes.cpp index daa50956d3..a912fa4ecb 100644 --- a/engines/lastexpress/entities/gendarmes.cpp +++ b/engines/lastexpress/entities/gendarmes.cpp @@ -31,7 +31,6 @@ #include "lastexpress/game/state.h" #include "lastexpress/lastexpress.h" -#include "lastexpress/helpers.h" namespace LastExpress { @@ -67,7 +66,7 @@ IMPLEMENT_FUNCTION(2, Gendarmes, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler); + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(Gendarmes, setup_chapter1Handler)); break; case kActionDefault: @@ -195,7 +194,7 @@ IMPLEMENT_FUNCTION_IISS(9, Gendarmes, function9, CarIndex, EntityPosition) break; case 1: - CALLBACK_ACTION(); + callbackAction(); break; case 2: @@ -233,7 +232,7 @@ IMPLEMENT_FUNCTION_IISS(9, Gendarmes, function9, CarIndex, EntityPosition) case 6: getData()->location = kLocationOutsideCompartment; getEntities()->exitCompartment(kEntityGendarmes, (ObjectIndex)parameters2->param5); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -267,11 +266,12 @@ IMPLEMENT_FUNCTION_III(10, Gendarmes, function10, CarIndex, EntityPosition, Obje getSound()->playSound(kEntityGendarmes, "POL1046A", kFlagDefault); } - UPDATE_PARAM(params->param7, getState()->timeTicks, 300); + if (!Entity::updateParameter(params->param7, getState()->timeTicks, 300)) + break; if (!params->param4 && getEntities()->isOutsideAlexeiWindow()) { getObjects()->update((ObjectIndex)params->param3, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); } else { if (getEntities()->isOutsideAlexeiWindow()) getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49); @@ -322,7 +322,7 @@ IMPLEMENT_FUNCTION_III(10, Gendarmes, function10, CarIndex, EntityPosition, Obje getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true); getObjects()->update((ObjectIndex)params->param3, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); break; case 4: @@ -330,7 +330,7 @@ IMPLEMENT_FUNCTION_III(10, Gendarmes, function10, CarIndex, EntityPosition, Obje getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverPolice1, true); getObjects()->update((ObjectIndex)params->param3, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); break; case 5: @@ -552,13 +552,14 @@ void Gendarmes::arrest(const SavePoint &savepoint, bool shouldPlaySound, SoundFl case kActionNone: if (checkCallback) { EXPOSE_PARAMS(EntityData::EntityParametersIIII); - TIME_CHECK_CALLBACK_ACTION(params->param1, params->param2); + if (Entity::timeCheckCallbackAction((TimeValue)params->param1, params->param2)) + break; } if (shouldUpdateEntity) { EXPOSE_PARAMS(EntityData::EntityParametersIIII); if (getEntities()->updateEntity(kEntityGendarmes, (CarIndex)params->param1, (EntityPosition)params->param2)) { - CALLBACK_ACTION(); + callbackAction(); break; } } @@ -582,7 +583,7 @@ void Gendarmes::arrest(const SavePoint &savepoint, bool shouldPlaySound, SoundFl break; case kActionExitCompartment: - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: @@ -599,7 +600,7 @@ void Gendarmes::arrest(const SavePoint &savepoint, bool shouldPlaySound, SoundFl if (shouldUpdateEntity) { EXPOSE_PARAMS(EntityData::EntityParametersIIII); if (getEntities()->updateEntity(kEntityGendarmes, (CarIndex)params->param1, (EntityPosition)params->param2)) { - CALLBACK_ACTION(); + callbackAction(); break; } } diff --git a/engines/lastexpress/entities/gendarmes.h b/engines/lastexpress/entities/gendarmes.h index d999cfc1fd..a761643531 100644 --- a/engines/lastexpress/entities/gendarmes.h +++ b/engines/lastexpress/entities/gendarmes.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_GENDARMES_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" #include "lastexpress/sound/sound.h" diff --git a/engines/lastexpress/entities/hadija.cpp b/engines/lastexpress/entities/hadija.cpp index 8ec972b939..e9abcd888a 100644 --- a/engines/lastexpress/entities/hadija.cpp +++ b/engines/lastexpress/entities/hadija.cpp @@ -28,10 +28,7 @@ #include "lastexpress/game/savepoint.h" #include "lastexpress/game/state.h" -#include "lastexpress/sound/sound.h" - #include "lastexpress/lastexpress.h" -#include "lastexpress/helpers.h" namespace LastExpress { @@ -89,22 +86,22 @@ IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// IMPLEMENT_FUNCTION(6, Hadija, compartment6) - COMPARTMENT_TO(Hadija, kObjectCompartment6, kPosition_4070, "619Cf", "619Df"); + Entity::goToCompartment(savepoint, kObjectCompartment6, kPosition_4070, "619Cf", "619Df"); IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// IMPLEMENT_FUNCTION(7, Hadija, compartment8) - COMPARTMENT_TO(Hadija, kObjectCompartment8, kPosition_2740, "619Ch", "619Dh"); + Entity::goToCompartment(savepoint, kObjectCompartment8, kPosition_2740, "619Ch", "619Dh"); IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// IMPLEMENT_FUNCTION(8, Hadija, compartment6to8) - COMPARTMENT_FROM_TO(Hadija, kObjectCompartment6, kPosition_4070, "619Bf", kObjectCompartment8, kPosition_2740, "619Ah"); + Entity::goToCompartmentFromCompartment(savepoint, kObjectCompartment6, kPosition_4070, "619Bf", kObjectCompartment8, kPosition_2740, "619Ah"); IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// IMPLEMENT_FUNCTION(9, Hadija, compartment8to6) - COMPARTMENT_FROM_TO(Hadija, kObjectCompartment8, kPosition_2740, "619Bh", kObjectCompartment6, kPosition_4070, "619Af"); + Entity::goToCompartmentFromCompartment(savepoint, kObjectCompartment8, kPosition_2740, "619Bh", kObjectCompartment6, kPosition_4070, "619Af"); IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// @@ -114,7 +111,7 @@ IMPLEMENT_FUNCTION(10, Hadija, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler); + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(Hadija, setup_chapter1Handler)); break; case kActionDefault: @@ -133,10 +130,12 @@ IMPLEMENT_FUNCTION(11, Hadija, chapter1Handler) break; case kActionNone: - TIME_CHECK_PLAYSOUND_UPDATEPOSITION(kTimeParisEpernay, params->param1, 1, "Har1100", kPosition_4840); + if (Entity::timeCheckPlaySoundUpdatePosition(kTimeParisEpernay, params->param1, 1, "Har1100", kPosition_4840)) + break; label_callback1: - TIME_CHECK_CALLBACK(kTime1084500, params->param2, 2, setup_compartment6to8); + if (Entity::timeCheckCallback(kTime1084500, params->param2, 2, WRAP_SETUP_FUNCTION(Hadija, setup_compartment6to8))) + break; label_callback2: if (params->param3 != kTimeInvalid && getState()->time > kTime1093500) { @@ -164,7 +163,8 @@ label_callback2: } label_callback3: - TIME_CHECK_CALLBACK(kTime1156500, params->param4, 4, setup_compartment8to6); + if (Entity::timeCheckCallback(kTime1156500, params->param4, 4, WRAP_SETUP_FUNCTION(Hadija, setup_compartment8to6))) + break; label_callback4: if (params->param5 != kTimeInvalid && getState()->time > kTime1165500) { @@ -254,7 +254,7 @@ IMPLEMENT_FUNCTION(14, Hadija, chapter2Handler) } if (params->param2 == kTimeInvalid || getState()->time <= kTime1786500) { - TIME_CHECK_CALLBACK(kTime1822500, params->param3, 2, setup_compartment8to6); + Entity::timeCheckCallback(kTime1822500, params->param3, 2, WRAP_SETUP_FUNCTION(Hadija, setup_compartment8to6)); break; } @@ -264,7 +264,7 @@ IMPLEMENT_FUNCTION(14, Hadija, chapter2Handler) params->param2 = (uint)getState()->time + 75; if (params->param2 >= getState()->time) { - TIME_CHECK_CALLBACK(kTime1822500, params->param3, 2, setup_compartment8to6); + Entity::timeCheckCallback(kTime1822500, params->param3, 2, WRAP_SETUP_FUNCTION(Hadija, setup_compartment8to6)); break; } } @@ -281,7 +281,7 @@ IMPLEMENT_FUNCTION(14, Hadija, chapter2Handler) break; case 1: - TIME_CHECK_CALLBACK(kTime1822500, params->param3, 2, setup_compartment8to6); + Entity::timeCheckCallback(kTime1822500, params->param3, 2, WRAP_SETUP_FUNCTION(Hadija, setup_compartment8to6)); break; case 2: @@ -321,20 +321,26 @@ IMPLEMENT_FUNCTION(16, Hadija, chapter3Handler) break; case kActionNone: - TIME_CHECK_CALLBACK(kTime1998000, params->param1, 1, setup_compartment6to8); + if (Entity::timeCheckCallback(kTime1998000, params->param1, 1, WRAP_SETUP_FUNCTION(Hadija, setup_compartment6to8))) + break; label_callback1: - TIME_CHECK_CALLBACK(kTime2020500, params->param2, 2, setup_compartment8to6); + if (Entity::timeCheckCallback(kTime2020500, params->param2, 2, WRAP_SETUP_FUNCTION(Hadija, setup_compartment8to6))) + break; label_callback2: - TIME_CHECK_CALLBACK(kTime2079000, params->param3, 3, setup_compartment6to8); + if (Entity::timeCheckCallback(kTime2079000, params->param3, 3, WRAP_SETUP_FUNCTION(Hadija, setup_compartment6to8))) + break; label_callback3: - TIME_CHECK_CALLBACK(kTime2187000, params->param4, 4, setup_compartment8to6); + if (Entity::timeCheckCallback(kTime2187000, params->param4, 4, WRAP_SETUP_FUNCTION(Hadija, setup_compartment8to6))) + break; label_callback4: - if (params->param5 != kTimeInvalid && getState()->time > kTime2196000) - TIME_CHECK_CAR(kTime2254500, params->param5, 5, setup_compartment6); + if (params->param5 != kTimeInvalid && getState()->time > kTime2196000) { + if (Entity::timeCheckCar(kTime2254500, params->param5, 5, WRAP_SETUP_FUNCTION(Hadija, setup_compartment6))) + break; + } break; case kActionDefault: @@ -387,18 +393,24 @@ IMPLEMENT_FUNCTION(18, Hadija, chapter4Handler) break; case kActionNone: - if (params->param1 != kTimeInvalid) - TIME_CHECK_CAR(kTime1714500, params->param1, 1, setup_compartment6); + if (params->param1 != kTimeInvalid) { + if (Entity::timeCheckCar(kTime1714500, params->param1, 1, WRAP_SETUP_FUNCTION(Hadija, setup_compartment6))) + break; + } label_callback1: - TIME_CHECK_CALLBACK(kTime2367000, params->param2, 2, setup_compartment6to8); + if (Entity::timeCheckCallback(kTime2367000, params->param2, 2, WRAP_SETUP_FUNCTION(Hadija, setup_compartment6to8))) + break; label_callback2: - TIME_CHECK_CALLBACK(kTime2421000, params->param3, 3, setup_compartment8to6); + if (Entity::timeCheckCallback(kTime2421000, params->param3, 3, WRAP_SETUP_FUNCTION(Hadija, setup_compartment8to6))) + break; label_callback3: - if (params->param4 != kTimeInvalid && getState()->time > kTime2425500) - TIME_CHECK_CAR(kTime2484000, params->param4, 4, setup_compartment6); + if (params->param4 != kTimeInvalid && getState()->time > kTime2425500) { + if (Entity::timeCheckCar(kTime2484000, params->param4, 4, WRAP_SETUP_FUNCTION(Hadija, setup_compartment6))) + break; + } break; case kActionCallback: @@ -468,7 +480,9 @@ IMPLEMENT_FUNCTION(22, Hadija, function22) break; case kActionNone: - UPDATE_PARAM(params->param1, getState()->time, 2700); + if (!Entity::updateParameter(params->param1, getState()->time, 2700)) + break; + setup_function23(); break; diff --git a/engines/lastexpress/entities/hadija.h b/engines/lastexpress/entities/hadija.h index d2495955e0..545f21ee03 100644 --- a/engines/lastexpress/entities/hadija.h +++ b/engines/lastexpress/entities/hadija.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_HADIJA_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/ivo.cpp b/engines/lastexpress/entities/ivo.cpp index f2261b438c..c53f4689fb 100644 --- a/engines/lastexpress/entities/ivo.cpp +++ b/engines/lastexpress/entities/ivo.cpp @@ -32,10 +32,7 @@ #include "lastexpress/game/scenes.h" #include "lastexpress/game/state.h" -#include "lastexpress/sound/sound.h" - #include "lastexpress/lastexpress.h" -#include "lastexpress/helpers.h" namespace LastExpress { @@ -184,7 +181,7 @@ IMPLEMENT_FUNCTION(11, Ivo, function11) getData()->location = kLocationInsideCompartment; getEntities()->clearSequences(kEntityIvo); - CALLBACK_ACTION(); + callbackAction(); break; case 4: @@ -193,7 +190,7 @@ IMPLEMENT_FUNCTION(11, Ivo, function11) getData()->location = kLocationInsideCompartment; getEntities()->clearSequences(kEntityIvo); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -210,7 +207,7 @@ IMPLEMENT_FUNCTION(12, Ivo, sitAtTableWithSalko) getEntities()->clearSequences(kEntitySalko); getSavePoints()->push(kEntityIvo, kEntityTables2, kAction136455232); - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: @@ -231,7 +228,7 @@ IMPLEMENT_FUNCTION(13, Ivo, leaveTableWithSalko) getSavePoints()->push(kEntityIvo, kEntityTables2, kActionDrawTablesWithChairs, "009E"); getEntities()->clearSequences(kEntitySalko); - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: @@ -249,7 +246,7 @@ IMPLEMENT_FUNCTION(14, Ivo, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler); + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(Ivo, setup_chapter1Handler)); break; case kActionDefault: @@ -374,7 +371,7 @@ IMPLEMENT_FUNCTION(18, Ivo, chapter2) break; case kActionNone: - TIME_CHECK(kTime1777500, params->param1, setup_function19); + Entity::timeCheck(kTime1777500, params->param1, WRAP_SETUP_FUNCTION(Ivo, setup_function19)); break; case kActionDefault: diff --git a/engines/lastexpress/entities/ivo.h b/engines/lastexpress/entities/ivo.h index cd5cb7b6be..75814336e0 100644 --- a/engines/lastexpress/entities/ivo.h +++ b/engines/lastexpress/entities/ivo.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_IVO_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/kahina.cpp b/engines/lastexpress/entities/kahina.cpp index 2918b1e8bd..7860836a26 100644 --- a/engines/lastexpress/entities/kahina.cpp +++ b/engines/lastexpress/entities/kahina.cpp @@ -32,10 +32,8 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/lastexpress.h" -#include "lastexpress/helpers.h" namespace LastExpress { @@ -90,7 +88,7 @@ IMPLEMENT_FUNCTION_END IMPLEMENT_FUNCTION_I(4, Kahina, updateFromTime, uint32) if (savepoint.action == kAction137503360) { ENTITY_PARAM(0, 2) = 1; - CALLBACK_ACTION(); + callbackAction(); } Entity::updateFromTime(savepoint); @@ -111,7 +109,7 @@ IMPLEMENT_FUNCTION_I(6, Kahina, function6, TimeValue) if (params->param1 < getState()->time && !params->param2) { params->param2 = 1; - CALLBACK_ACTION(); + callbackAction(); break; } @@ -141,7 +139,7 @@ IMPLEMENT_FUNCTION_I(6, Kahina, function6, TimeValue) case 1: if (ENTITY_PARAM(0, 1) || ENTITY_PARAM(0, 2)) { - CALLBACK_ACTION(); + callbackAction(); break; } @@ -151,7 +149,7 @@ IMPLEMENT_FUNCTION_I(6, Kahina, function6, TimeValue) case 2: case 3: if (ENTITY_PARAM(0, 1) || ENTITY_PARAM(0, 2)) { - CALLBACK_ACTION(); + callbackAction(); break; } @@ -163,7 +161,7 @@ IMPLEMENT_FUNCTION_I(6, Kahina, function6, TimeValue) case 4: if (ENTITY_PARAM(0, 2)) { - CALLBACK_ACTION(); + callbackAction(); break; } @@ -173,7 +171,7 @@ IMPLEMENT_FUNCTION_I(6, Kahina, function6, TimeValue) case 5: if (ENTITY_PARAM(0, 1) || ENTITY_PARAM(0, 2)) { - CALLBACK_ACTION(); + callbackAction(); break; } @@ -185,7 +183,7 @@ IMPLEMENT_FUNCTION_I(6, Kahina, function6, TimeValue) case kAction137503360: ENTITY_PARAM(0, 2) = 1; - CALLBACK_ACTION(); + callbackAction(); break; } IMPLEMENT_FUNCTION_END @@ -198,12 +196,12 @@ IMPLEMENT_FUNCTION_II(7, Kahina, updateEntity2, CarIndex, EntityPosition) case kActionNone: if (getEntities()->updateEntity(_entityIndex, (CarIndex)params->param1, (EntityPosition)params->param2)) - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: if (getEntities()->updateEntity(_entityIndex, (CarIndex)params->param1, (EntityPosition)params->param2)) { - CALLBACK_ACTION(); + callbackAction(); } else if (getEntities()->isDistanceBetweenEntities(kEntityKahina, kEntityPlayer, 1000) && !getEntities()->isInGreenCarEntrance(kEntityPlayer) && !getEntities()->isInsideCompartments(kEntityPlayer) @@ -211,14 +209,14 @@ IMPLEMENT_FUNCTION_II(7, Kahina, updateEntity2, CarIndex, EntityPosition) if (getData()->car == kCarGreenSleeping || getData()->car == kCarRedSleeping) { ENTITY_PARAM(0, 1) = 1; - CALLBACK_ACTION(); + callbackAction(); } } break; case kAction137503360: ENTITY_PARAM(0, 2) = 1; - CALLBACK_ACTION(); + callbackAction(); break; } IMPLEMENT_FUNCTION_END @@ -249,7 +247,7 @@ IMPLEMENT_FUNCTION(10, Kahina, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler); + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(Kahina, setup_chapter1Handler)); break; case kActionDefault: @@ -269,7 +267,7 @@ IMPLEMENT_FUNCTION(11, Kahina, chapter1Handler) return; if (getProgress().jacket != kJacketOriginal) - TIME_CHECK_SAVEPOINT(kTime1107000, params->param1, kEntityKahina, kEntityMertens, kAction238732837); + Entity::timeCheckSavepoint(kTime1107000, params->param1, kEntityKahina, kEntityMertens, kAction238732837); if (getProgress().eventMertensKronosInvitation) setup_function12(); @@ -282,7 +280,7 @@ IMPLEMENT_FUNCTION(12, Kahina, function12) break; case kActionNone: - TIME_CHECK(kTime1485000, params->param2, setup_function13); + Entity::timeCheck(kTime1485000, params->param2, WRAP_SETUP_FUNCTION(Kahina, setup_function13)); break; case kActionKnock: @@ -372,12 +370,12 @@ IMPLEMENT_FUNCTION(14, Kahina, function14) case kActionExitCompartment: getEntities()->exitCompartment(kEntityKahina, kObjectCompartmentF); - CALLBACK_ACTION(); + callbackAction(); break; case kAction4: getEntities()->exitCompartment(kEntityKahina, kObjectCompartmentF); - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: @@ -396,10 +394,10 @@ IMPLEMENT_FUNCTION(15, Kahina, function15) case kActionNone: if (params->param2 != kTimeInvalid) { - UPDATE_PARAM_PROC_TIME(params->param1, !getEntities()->isPlayerInCar(kCarRedSleeping), params->param2, 0) + if (Entity::updateParameterTime((TimeValue)params->param1, !getEntities()->isPlayerInCar(kCarRedSleeping), params->param2, 0)) { setCallback(9); setup_updateEntity(kCarRedSleeping, kPosition_4070); - UPDATE_PARAM_PROC_END + } } break; @@ -542,7 +540,7 @@ IMPLEMENT_FUNCTION(15, Kahina, function15) case 17: getEntities()->clearSequences(kEntityKahina); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -582,18 +580,18 @@ IMPLEMENT_FUNCTION(17, Kahina, chapter2Handler) case kActionNone: if (params->param1) { - UPDATE_PARAM_PROC(params->param2, getState()->time, 9000) + if (Entity::updateParameter(params->param2, getState()->time, 9000)) { params->param1 = 1; params->param2 = 0; - UPDATE_PARAM_PROC_END + } } if (getEvent(kEventKahinaAskSpeakFirebird) && getEvent(kEventKronosConversationFirebird) && getEntities()->isInsideTrainCar(kEntityPlayer, kCarKronos)) { - UPDATE_PARAM_PROC(params->param3, getState()->time, 900) + if (Entity::updateParameter(params->param3, getState()->time, 900)) { setCallback(1); setup_savegame(kSavegameTypeEvent, kEventKronosConversationFirebird); break; - UPDATE_PARAM_PROC_END + } } label_callback_3: @@ -729,7 +727,7 @@ IMPLEMENT_FUNCTION_II(19, Kahina, function19, CarIndex, EntityPosition) RESET_ENTITY_STATE(kEntityKahina, Kahina, setup_function22); if (getEntities()->updateEntity(kEntityKahina, (CarIndex)params->param1, (EntityPosition)params->param2)) - CALLBACK_ACTION(); + callbackAction(); break; case kActionExcuseMeCath: @@ -745,7 +743,7 @@ IMPLEMENT_FUNCTION_II(19, Kahina, function19, CarIndex, EntityPosition) case kActionDefault: if (getEntities()->updateEntity(kEntityKahina, (CarIndex)params->param1, (EntityPosition)params->param2)) - CALLBACK_ACTION(); + callbackAction(); break; } IMPLEMENT_FUNCTION_END @@ -787,16 +785,17 @@ label_callback_2: } if (!params->param1) { - UPDATE_PARAM_PROC(params->param3, getState()->time, 9000) + if (Entity::updateParameter(params->param3, getState()->time, 9000)) { params->param1 = 1; params->param3 = 0; - UPDATE_PARAM_PROC_END + } } if (getEvent(kEventKahinaAskSpeakFirebird) && !getEvent(kEventKronosConversationFirebird) && getEntities()->isInsideTrainCar(kEntityPlayer, kCarKronos)) { - UPDATE_PARAM(params->param4, getState()->time, 900); + if (!Entity::updateParameter(params->param4, getState()->time, 900)) + break; setCallback(3); setup_savegame(kSavegameTypeEvent, kEventKronosConversationFirebird); @@ -928,11 +927,11 @@ IMPLEMENT_FUNCTION(21, Kahina, function21) params->param3 = (uint)getState()->time + 4500; if (params->param6 != kTimeInvalid) { - UPDATE_PARAM_PROC_TIME(params->param3, (getEntities()->isPlayerPosition(kCarKronos, 80) || getEntities()->isPlayerPosition(kCarKronos, 88)), params->param5, 0) + if (Entity::updateParameterTime((TimeValue)params->param3, (getEntities()->isPlayerPosition(kCarKronos, 80) || getEntities()->isPlayerPosition(kCarKronos, 88)), params->param5, 0)) { setCallback(2); setup_function23(); break; - UPDATE_PARAM_PROC_END + } } } @@ -943,14 +942,14 @@ label_callback_2: params->param4 = (uint)getState()->time + 4500; if (params->param6 != kTimeInvalid) { - UPDATE_PARAM_PROC_TIME(params->param3, (getEntities()->isPlayerPosition(kCarKronos, 80) || getEntities()->isPlayerPosition(kCarKronos, 88)), params->param6, 0) + if (Entity::updateParameterTime((TimeValue)params->param3, (getEntities()->isPlayerPosition(kCarKronos, 80) || getEntities()->isPlayerPosition(kCarKronos, 88)), params->param6, 0)) { getSound()->playSound(kEntityPlayer, "LIB014", getSound()->getSoundFlag(kEntityKahina)); getSound()->playSound(kEntityPlayer, "LIB015", getSound()->getSoundFlag(kEntityKahina)); getEntities()->drawSequenceLeft(kEntityKahina, "202a"); params->param2 = 0; - UPDATE_PARAM_PROC_END + } } } @@ -1125,7 +1124,7 @@ IMPLEMENT_FUNCTION(23, Kahina, function23) case 7: getEntities()->clearSequences(kEntityKahina); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1262,7 +1261,7 @@ IMPLEMENT_FUNCTION(25, Kahina, function25) getProgress().field_78 = 1; ENTITY_PARAM(0, 3) = 0; - CALLBACK_ACTION(); + callbackAction(); break; case kActionCallback: @@ -1303,7 +1302,7 @@ IMPLEMENT_FUNCTION(25, Kahina, function25) case 13: getEntities()->clearSequences(kEntityKahina); - CALLBACK_ACTION(); + callbackAction(); break; case 6: @@ -1387,7 +1386,7 @@ IMPLEMENT_FUNCTION(26, Kahina, function26) getInventory()->setLocationAndProcess(kItemBriefcase, kObjectLocation2); getProgress().field_78 = 1; - CALLBACK_ACTION(); + callbackAction(); break; case kActionCallback: @@ -1429,7 +1428,7 @@ IMPLEMENT_FUNCTION(26, Kahina, function26) case 9: getEntities()->clearSequences(kEntityKahina); - CALLBACK_ACTION(); + callbackAction(); break; case 6: diff --git a/engines/lastexpress/entities/kahina.h b/engines/lastexpress/entities/kahina.h index b25053e339..7479cf76f9 100644 --- a/engines/lastexpress/entities/kahina.h +++ b/engines/lastexpress/entities/kahina.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_KAHINA_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/kronos.cpp b/engines/lastexpress/entities/kronos.cpp index 134dce9c81..26ce3ca424 100644 --- a/engines/lastexpress/entities/kronos.cpp +++ b/engines/lastexpress/entities/kronos.cpp @@ -39,10 +39,8 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/lastexpress.h" -#include "lastexpress/helpers.h" namespace LastExpress { @@ -128,7 +126,7 @@ IMPLEMENT_FUNCTION(7, Kronos, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler); + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(Kronos, setup_chapter1Handler)); break; case kActionDefault: @@ -149,7 +147,7 @@ IMPLEMENT_FUNCTION(8, Kronos, chapter1Handler) break; case kActionNone: - TIME_CHECK(kTime1489500, params->param2, setup_function11); + Entity::timeCheck(kTime1489500, params->param2, WRAP_SETUP_FUNCTION(Kronos, setup_function11)); break; case kAction171849314: @@ -191,7 +189,7 @@ IMPLEMENT_FUNCTION(10, Kronos, function10) break; case kActionNone: - TIME_CHECK(kTime1489500, params->param1, setup_function11); + Entity::timeCheck(kTime1489500, params->param1, WRAP_SETUP_FUNCTION(Kronos, setup_function11)); break; case kActionDefault: @@ -295,10 +293,10 @@ IMPLEMENT_FUNCTION(15, Kronos, function15) case kActionNone: if (params->param1 && !getEntities()->isInSalon(kEntityBoutarel)) { - UPDATE_PARAM_PROC(params->param2, getState()->timeTicks, 75) + if (Entity::updateParameter(params->param2, getState()->timeTicks, 75)) { setup_function16(); break; - UPDATE_PARAM_PROC_END + } } if (params->param3 != kTimeInvalid && getState()->time > kTime2002500) { @@ -407,8 +405,7 @@ IMPLEMENT_FUNCTION(18, Kronos, function18) params->param2 = 1; } - TIME_CHECK(kTime2106000, params->param3, setup_function19) - else { + if (!Entity::timeCheck(kTime2106000, params->param3, WRAP_SETUP_FUNCTION(Kronos, setup_function19))) { if (params->param1 && getEntities()->isInKronosSanctum(kEntityPlayer)) { setCallback(1); setup_savegame(kSavegameTypeEvent, kEventKahinaPunchSuite4); @@ -528,9 +525,9 @@ IMPLEMENT_FUNCTION(20, Kronos, function20) } if (CURRENT_PARAM(1, 2) != kTimeInvalid && params->param7 < getState()->time) { - UPDATE_PARAM_PROC_TIME(params->param8, !params->param1, CURRENT_PARAM(1, 2), 450) + if (Entity::updateParameterTime((TimeValue)params->param8, !params->param1, CURRENT_PARAM(1, 2), 450)) { getSavePoints()->push(kEntityKronos, kEntityKahina, kAction237555748); - UPDATE_PARAM_PROC_END + } } if (!params->param1) diff --git a/engines/lastexpress/entities/kronos.h b/engines/lastexpress/entities/kronos.h index 4c61b98620..a7693fcd46 100644 --- a/engines/lastexpress/entities/kronos.h +++ b/engines/lastexpress/entities/kronos.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_KRONOS_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/mahmud.cpp b/engines/lastexpress/entities/mahmud.cpp index 0e67b45cd2..af86ef8cdd 100644 --- a/engines/lastexpress/entities/mahmud.cpp +++ b/engines/lastexpress/entities/mahmud.cpp @@ -34,10 +34,8 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/lastexpress.h" -#include "lastexpress/helpers.h" namespace LastExpress { @@ -86,7 +84,8 @@ IMPLEMENT_FUNCTION_SIII(4, Mahmud, enterExitCompartment2, ObjectIndex, uint32, O break; case kActionNone: - UPDATE_PARAM(params->param7, getState()->timeTicks, params->param5); + if (!Entity::updateParameter(params->param7, getState()->timeTicks, params->param5)) + break; if (!getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingUp)) getScenes()->loadSceneFromObject((ObjectIndex)params->param6, true); @@ -95,7 +94,7 @@ IMPLEMENT_FUNCTION_SIII(4, Mahmud, enterExitCompartment2, ObjectIndex, uint32, O case kActionExitCompartment: getEntities()->exitCompartment(kEntityMahmud, (ObjectIndex)params->param4); - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: @@ -146,7 +145,8 @@ IMPLEMENT_FUNCTION_II(10, Mahmud, function10, ObjectIndex, bool) break; case kActionNone: - UPDATE_PARAM(params->param6, getState()->time, 13500); + if (!Entity::updateParameter(params->param6, getState()->time, 13500)) + break; getObjects()->update(kObjectCompartment5, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand); getObjects()->update(kObjectCompartment6, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand); @@ -267,7 +267,7 @@ IMPLEMENT_FUNCTION_II(10, Mahmud, function10, ObjectIndex, bool) getData()->location = kLocationInsideCompartment; getEntities()->clearSequences(kEntityMahmud); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -392,7 +392,7 @@ IMPLEMENT_FUNCTION(11, Mahmud, function11) getEntities()->clearSequences(kEntityMahmud); getObjects()->update(kObjectCompartment4, kEntityMahmud, kObjectLocation3, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -471,7 +471,7 @@ IMPLEMENT_FUNCTION(12, Mahmud, function12) getData()->location = kLocationInsideCompartment; getEntities()->clearSequences(kEntityMahmud); - CALLBACK_ACTION(); + callbackAction(); break; } @@ -537,7 +537,7 @@ IMPLEMENT_FUNCTION(13, Mahmud, function13) getData()->location = kLocationInsideCompartment; getEntities()->clearSequences(kEntityMahmud); - CALLBACK_ACTION(); + callbackAction(); break; } @@ -560,7 +560,8 @@ IMPLEMENT_FUNCTION(14, Mahmud, chaptersHandler) if (!params->param2 && getProgress().chapter == kChapter1) { - TIME_CHECK_CALLBACK(kTime1098000, params->param6, 1, setup_function13); + if (Entity::timeCheckCallback(kTime1098000, params->param6, 1, WRAP_SETUP_FUNCTION(Mahmud, setup_function13))) + break; if (!getSoundQueue()->isBuffered("HAR1104") && getState()->time > kTime1167300 && !params->param7) { params->param7 = 1; @@ -572,7 +573,8 @@ IMPLEMENT_FUNCTION(14, Mahmud, chaptersHandler) } if (params->param5) { - UPDATE_PARAM(params->param8, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param8, getState()->timeTicks, 75)) + break; params->param4 = 1; params->param5 = 0; @@ -732,7 +734,7 @@ IMPLEMENT_FUNCTION(15, Mahmud, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chaptersHandler); + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(Mahmud, setup_chaptersHandler)); break; case kActionDefault: diff --git a/engines/lastexpress/entities/mahmud.h b/engines/lastexpress/entities/mahmud.h index 5feb95cba5..685100f078 100644 --- a/engines/lastexpress/entities/mahmud.h +++ b/engines/lastexpress/entities/mahmud.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_MAHMUD_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/max.cpp b/engines/lastexpress/entities/max.cpp index eacc38bf60..abd2aae9e4 100644 --- a/engines/lastexpress/entities/max.cpp +++ b/engines/lastexpress/entities/max.cpp @@ -31,10 +31,8 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/lastexpress.h" -#include "lastexpress/helpers.h" namespace LastExpress { @@ -91,7 +89,8 @@ IMPLEMENT_FUNCTION(6, Max, chapter12_handler) break; case kActionNone: - UPDATE_PARAM(params->param2, getState()->time, params->param1); + if (!Entity::updateParameter(params->param2, getState()->time, params->param1)) + break; if (!getSoundQueue()->isBuffered(kEntityMax)) getSound()->playSound(kEntityMax, "Max1122"); @@ -125,7 +124,8 @@ IMPLEMENT_FUNCTION(7, Max, function7) break; case kActionNone: - UPDATE_PARAM(params->param2, getState()->time, params->param1) + if (!Entity::updateParameter(params->param2, getState()->time, params->param1)) + break; if (!getSoundQueue()->isBuffered(kEntityMax)) getSound()->playSound(kEntityMax, "Max1122"); @@ -186,7 +186,7 @@ IMPLEMENT_FUNCTION(7, Max, function7) case kAction101687594: getEntities()->clearSequences(kEntityMax); - CALLBACK_ACTION(); + callbackAction(); break; case kAction122358304: @@ -195,7 +195,7 @@ IMPLEMENT_FUNCTION(7, Max, function7) getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand); getObjects()->update(kObject53, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); break; case kAction158007856: @@ -214,7 +214,8 @@ IMPLEMENT_FUNCTION(8, Max, chapter4Handler) break; case kActionNone: - UPDATE_PARAM(params->param3, getState()->time, params->param2); + if (!Entity::updateParameter(params->param3, getState()->time, params->param2)) + break; if (!getSoundQueue()->isBuffered(kEntityMax)) getSound()->playSound(kEntityMax, "Max3101"); @@ -323,7 +324,7 @@ IMPLEMENT_FUNCTION(10, Max, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chapter12_handler); + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(Max, setup_chapter12_handler)); break; case kActionDefault: @@ -392,7 +393,8 @@ IMPLEMENT_FUNCTION(13, Max, chapter3Handler) break; } - UPDATE_PARAM(params->param3, getState()->time, params->param1); + if (!Entity::updateParameter(params->param3, getState()->time, params->param1)) + break; if (!getSoundQueue()->isBuffered(kEntityMax)) getSound()->playSound(kEntityMax, "Max1122"); @@ -514,7 +516,8 @@ IMPLEMENT_FUNCTION(15, Max, function15) } if (!params->param1) { - UPDATE_PARAM(params->param3, getState()->time, 900); + if (!Entity::updateParameter(params->param3, getState()->time, 900)) + break; getSavePoints()->push(kEntityMax, kEntityCoudert, kAction157026693); } diff --git a/engines/lastexpress/entities/max.h b/engines/lastexpress/entities/max.h index 17b58475aa..acd8235e89 100644 --- a/engines/lastexpress/entities/max.h +++ b/engines/lastexpress/entities/max.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_MAX_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/mertens.cpp b/engines/lastexpress/entities/mertens.cpp index d88962fce2..97dd293793 100644 --- a/engines/lastexpress/entities/mertens.cpp +++ b/engines/lastexpress/entities/mertens.cpp @@ -32,21 +32,11 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/lastexpress.h" -#include "lastexpress/helpers.h" namespace LastExpress { -#define SAVEGAME_BLOOD_JACKET() \ - if (getProgress().jacket == kJacketBlood \ - && getEntities()->isDistanceBetweenEntities(kEntityMertens, kEntityPlayer, 1000) \ - && !getEntities()->isInsideCompartments(kEntityPlayer) \ - && !getEntities()->checkFields10(kEntityPlayer)) { \ - setCallback(1); \ - setup_savegame(kSavegameTypeEvent, kEventMertensBloodJacket); \ - } Mertens::Mertens(LastExpressEngine *engine) : Entity(engine, kEntityMertens) { ADD_CALLBACK_FUNCTION(Mertens, reset); @@ -117,11 +107,11 @@ IMPLEMENT_FUNCTION_S(2, Mertens, bloodJacket) break; case kActionNone: - SAVEGAME_BLOOD_JACKET(); + Entity::savegameBloodJacket(); break; case kActionExitCompartment: - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: @@ -144,7 +134,7 @@ IMPLEMENT_FUNCTION_SI(3, Mertens, enterExitCompartment, ObjectIndex) break; case kActionNone: - SAVEGAME_BLOOD_JACKET(); + Entity::savegameBloodJacket(); return; case kActionCallback: @@ -165,12 +155,12 @@ IMPLEMENT_FUNCTION_SI(4, Mertens, enterExitCompartment2, ObjectIndex) break; case kActionNone: - SAVEGAME_BLOOD_JACKET(); + Entity::savegameBloodJacket(); return; case kAction4: getEntities()->exitCompartment(kEntityMertens, (ObjectIndex)params->param4); - CALLBACK_ACTION(); + callbackAction(); return; case kActionCallback: @@ -191,13 +181,13 @@ IMPLEMENT_FUNCTION_SIII(5, Mertens, enterExitCompartment3, ObjectIndex, EntityPo break; case kActionNone: - SAVEGAME_BLOOD_JACKET(); + Entity::savegameBloodJacket(); break; case kActionExitCompartment: getEntities()->exitCompartment(_entityIndex, (ObjectIndex)params->param4); getData()->entityPosition = (EntityPosition)params->param5; - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: @@ -229,15 +219,15 @@ IMPLEMENT_FUNCTION(6, Mertens, callbackActionOnDirection) case kActionNone: if (getData()->direction != kDirectionRight) { - CALLBACK_ACTION(); + callbackAction(); break; } - SAVEGAME_BLOOD_JACKET(); + Entity::savegameBloodJacket(); break; case kActionExitCompartment: - CALLBACK_ACTION(); + callbackAction(); break; case kActionCallback: @@ -256,11 +246,11 @@ IMPLEMENT_FUNCTION_S(7, Mertens, playSound) break; case kActionNone: - SAVEGAME_BLOOD_JACKET(); + Entity::savegameBloodJacket(); break; case kActionEndSound: - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: @@ -283,11 +273,11 @@ IMPLEMENT_FUNCTION_S(8, Mertens, playSound16) break; case kActionNone: - SAVEGAME_BLOOD_JACKET(); + Entity::savegameBloodJacket(); break; case kActionEndSound: - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: @@ -310,14 +300,6 @@ IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// IMPLEMENT_FUNCTION_II(10, Mertens, updateEntity, CarIndex, EntityPosition) - -#define LOADSCENE_FROM_POSITION() \ - if (getData()->direction != kDirectionUp) { \ - getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + 750)); \ - } else { \ - getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition - 750), true); \ - } - switch (savepoint.action) { default: break; @@ -333,7 +315,7 @@ IMPLEMENT_FUNCTION_II(10, Mertens, updateEntity, CarIndex, EntityPosition) || getEntities()->checkFields10(kEntityPlayer)) { if (getEntities()->updateEntity(kEntityMertens, (CarIndex)params->param1, (EntityPosition)params->param2)) { getData()->inventoryItem = kItemNone; - CALLBACK_ACTION(); + callbackAction(); } break; } @@ -364,7 +346,7 @@ IMPLEMENT_FUNCTION_II(10, Mertens, updateEntity, CarIndex, EntityPosition) if (getEntities()->updateEntity(kEntityMertens, (CarIndex)params->param1, (EntityPosition)params->param2)) { getData()->inventoryItem = kItemNone; - CALLBACK_ACTION(); + callbackAction(); } break; @@ -395,7 +377,7 @@ IMPLEMENT_FUNCTION_II(10, Mertens, updateEntity, CarIndex, EntityPosition) params->param3 = 1; if (getEntities()->updateEntity(kEntityMertens, (CarIndex)params->param1, (EntityPosition)params->param2)) - CALLBACK_ACTION(); + callbackAction(); break; case kActionCallback: @@ -416,7 +398,7 @@ IMPLEMENT_FUNCTION_II(10, Mertens, updateEntity, CarIndex, EntityPosition) ENTITY_PARAM(0, 7) = 0; if (params->param1 != 3 || (params->param2 != kPosition_8200 && params->param2 != kPosition_9510)) { - LOADSCENE_FROM_POSITION(); + loadSceneFromPosition(); break; } @@ -428,7 +410,7 @@ IMPLEMENT_FUNCTION_II(10, Mertens, updateEntity, CarIndex, EntityPosition) getEntities()->updateEntity(kEntityMertens, kCarGreenSleeping, kPosition_2000); getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + 750)); - CALLBACK_ACTION(); + callbackAction(); break; case 3: @@ -444,35 +426,33 @@ IMPLEMENT_FUNCTION_II(10, Mertens, updateEntity, CarIndex, EntityPosition) getEntities()->updateEntity(kEntityMertens, kCarGreenSleeping, kPosition_2000); getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + 750)); - CALLBACK_ACTION(); + callbackAction(); break; } - LOADSCENE_FROM_POSITION(); + loadSceneFromPosition(); break; case 4: getAction()->playAnimation(kEventMertensKronosConcertInvitation); ENTITY_PARAM(2, 4) = 0; - LOADSCENE_FROM_POSITION(); + loadSceneFromPosition(); break; case 5: getAction()->playAnimation(getData()->entityPosition < getEntityData(kEntityPlayer)->entityPosition ? kEventMertensAskTylerCompartmentD : kEventMertensAskTylerCompartment); - LOADSCENE_FROM_POSITION(); + loadSceneFromPosition(); break; case 6: getAction()->playAnimation(kEventMertensDontMakeBed); - LOADSCENE_FROM_POSITION(); + loadSceneFromPosition(); ENTITY_PARAM(0, 4) = 0; break; } break; } - -#undef LOADSCENE_FROM_POSITION IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// @@ -482,11 +462,12 @@ IMPLEMENT_FUNCTION_I(11, Mertens, function11, uint32) break; case kActionNone: - SAVEGAME_BLOOD_JACKET(); + Entity::savegameBloodJacket(); - UPDATE_PARAM(params->param2, getState()->time, params->param1) + if (!Entity::updateParameter(params->param2, getState()->time, params->param1)) + break; - CALLBACK_ACTION(); + callbackAction(); break; case kActionCallback: @@ -506,7 +487,7 @@ IMPLEMENT_FUNCTION_I(12, Mertens, bonsoir, EntityIndex) return; if (getSoundQueue()->isBuffered(kEntityMertens)) { - CALLBACK_ACTION(); + callbackAction(); return; } @@ -540,7 +521,7 @@ IMPLEMENT_FUNCTION_I(12, Mertens, bonsoir, EntityIndex) getSound()->playSound(kEntityMertens, "CON1112G"); } - CALLBACK_ACTION(); + callbackAction(); IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// @@ -550,23 +531,23 @@ IMPLEMENT_FUNCTION_II(13, Mertens, function13, bool, bool) break; case kActionNone: - SAVEGAME_BLOOD_JACKET(); + Entity::savegameBloodJacket(); if (!params->param2 && !params->param3) { - UPDATE_PARAM_PROC(params->param4, getState()->timeTicks, 75) + if (Entity::updateParameter(params->param4, getState()->timeTicks, 75)) { getData()->inventoryItem = kItemNone; setCallback(5); setup_function18(); break; - UPDATE_PARAM_PROC_END + } } - UPDATE_PARAM_PROC(params->param5, getState()->timeTicks, 225) + if (Entity::updateParameter(params->param5, getState()->timeTicks, 225)) { getData()->inventoryItem = kItemNone; setCallback(6); setup_function18(); break; - UPDATE_PARAM_PROC_END + } getData()->inventoryItem = (getProgress().chapter == kChapter1 && !ENTITY_PARAM(2, 1) @@ -640,7 +621,7 @@ IMPLEMENT_FUNCTION_II(13, Mertens, function13, bool, bool) case 6: case 9: case 10: - CALLBACK_ACTION(); + callbackAction(); break; case 7: @@ -672,7 +653,7 @@ IMPLEMENT_FUNCTION_I(14, Mertens, function14, EntityIndex) break; case kActionNone: - SAVEGAME_BLOOD_JACKET(); + Entity::savegameBloodJacket(); break; case kActionDefault: @@ -719,7 +700,7 @@ IMPLEMENT_FUNCTION_I(14, Mertens, function14, EntityIndex) break; case 5: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -783,7 +764,7 @@ IMPLEMENT_FUNCTION_I(15, Mertens, function15, bool) break; case 6: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -860,7 +841,7 @@ IMPLEMENT_FUNCTION_I(16, Mertens, function16, bool) break; case 6: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -888,7 +869,7 @@ IMPLEMENT_FUNCTION(17, Mertens, function17) getScenes()->loadSceneFromItemPosition(kItem7); ENTITY_PARAM(2, 1) = 1; - CALLBACK_ACTION(); + callbackAction(); break; } @@ -926,7 +907,7 @@ IMPLEMENT_FUNCTION(17, Mertens, function17) break; case 2: - CALLBACK_ACTION(); + callbackAction(); break; case 3: @@ -944,7 +925,7 @@ IMPLEMENT_FUNCTION(17, Mertens, function17) getSavePoints()->push(kEntityMertens, kEntityMertens, kActionDrawScene); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -970,7 +951,7 @@ IMPLEMENT_FUNCTION(18, Mertens, function18) getInventory()->setLocationAndProcess(kItem7, kObjectLocation1); ENTITY_PARAM(2, 1) = 1; - CALLBACK_ACTION(); + callbackAction(); break; } @@ -978,7 +959,7 @@ IMPLEMENT_FUNCTION(18, Mertens, function18) getScenes()->loadSceneFromItemPosition(kItem7); ENTITY_PARAM(2, 1) = 1; - CALLBACK_ACTION(); + callbackAction(); break; } @@ -1009,7 +990,7 @@ IMPLEMENT_FUNCTION(18, Mertens, function18) ENTITY_PARAM(0, 1) = 0; getData()->inventoryItem = kItemNone; - CALLBACK_ACTION(); + callbackAction(); } break; } @@ -1025,7 +1006,7 @@ IMPLEMENT_FUNCTION(19, Mertens, function19) if (ENTITY_PARAM(2, 1)) { getInventory()->setLocationAndProcess(kItem7, kObjectLocation1); ENTITY_PARAM(2, 1) = 0; - CALLBACK_ACTION(); + callbackAction(); } else { setCallback(1); setup_bloodJacket("601C"); @@ -1039,7 +1020,7 @@ IMPLEMENT_FUNCTION(19, Mertens, function19) if (!getEntities()->isPlayerPosition(kCarGreenSleeping, 2)) getData()->entityPosition = kPosition_2088; - CALLBACK_ACTION(); + callbackAction(); } break; } @@ -1057,7 +1038,7 @@ IMPLEMENT_FUNCTION(20, Mertens, function20) if (ENTITY_PARAM(2, 1)) { ENTITY_PARAM(2, 1) = 0; - CALLBACK_ACTION(); + callbackAction(); } else { setCallback(1); setup_bloodJacket("601C"); @@ -1066,7 +1047,7 @@ IMPLEMENT_FUNCTION(20, Mertens, function20) case kActionCallback: if (getCallback() == 1) - CALLBACK_ACTION(); + callbackAction(); break; } IMPLEMENT_FUNCTION_END @@ -1078,11 +1059,12 @@ IMPLEMENT_FUNCTION_II(21, Mertens, function21, ObjectIndex, ObjectIndex) break; case kActionNone: - UPDATE_PARAM_PROC(CURRENT_PARAM(1, 4), getState()->time, 300) + if (Entity::updateParameter(CURRENT_PARAM(1, 4), getState()->time, 300)) { getSound()->playSound(kEntityPlayer, "ZFX1004", getSound()->getSoundFlag(kEntityMertens)); - UPDATE_PARAM_PROC_END + } - UPDATE_PARAM(CURRENT_PARAM(1, 5), getState()->time, 900); + if (!Entity::updateParameter(CURRENT_PARAM(1, 5), getState()->time, 900)) + break; // Update objects getObjects()->updateLocation2((ObjectIndex)params->param1, kObjectLocation1); @@ -1092,7 +1074,7 @@ IMPLEMENT_FUNCTION_II(21, Mertens, function21, ObjectIndex, ObjectIndex) if (params->param2) getObjects()->update((ObjectIndex)params->param2, (EntityIndex)params->param8, (ObjectLocation)CURRENT_PARAM(1, 1), (CursorStyle)CURRENT_PARAM(1, 2), (CursorStyle)CURRENT_PARAM(1, 3)); - CALLBACK_ACTION(); + callbackAction(); break; case kActionKnock: @@ -1222,7 +1204,7 @@ IMPLEMENT_FUNCTION(22, Mertens, function22) break; case 9: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1291,7 +1273,7 @@ IMPLEMENT_FUNCTION(23, Mertens, function23) case 6: getData()->location = kLocationOutsideCompartment; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1306,7 +1288,8 @@ IMPLEMENT_FUNCTION(24, Mertens, function24) case kActionNone: if (!params->param1) { - UPDATE_PARAM(params->param2, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param2, getState()->timeTicks, 75)) + break; setCallback(3); setup_enterExitCompartment3("601Rc", kObjectCompartment3, kPosition_6470, kPosition_6130); @@ -1351,7 +1334,7 @@ IMPLEMENT_FUNCTION(24, Mertens, function24) case 5: getData()->location = kLocationOutsideCompartment; - CALLBACK_ACTION(); + callbackAction(); break; case 6: @@ -1380,7 +1363,7 @@ IMPLEMENT_FUNCTION(24, Mertens, function24) break; case 9: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1409,7 +1392,8 @@ IMPLEMENT_FUNCTION(25, Mertens, function25) case kActionNone: if (!params->param1) { - UPDATE_PARAM(params->param2, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param2, getState()->timeTicks, 75)) + break; setCallback(3); setup_enterExitCompartment3("601Zb", kObjectCompartment2, kPosition_7500, kPositionNone); @@ -1458,7 +1442,7 @@ IMPLEMENT_FUNCTION(25, Mertens, function25) case 5: getData()->location = kLocationOutsideCompartment; - CALLBACK_ACTION(); + callbackAction(); break; case 6: @@ -1492,7 +1476,7 @@ IMPLEMENT_FUNCTION(25, Mertens, function25) break; case 9: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1547,7 +1531,7 @@ IMPLEMENT_FUNCTION_I(26, Mertens, function26, bool) case 2: getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); break; case 3: @@ -1613,7 +1597,7 @@ IMPLEMENT_FUNCTION_I(26, Mertens, function26, bool) getData()->location = kLocationOutsideCompartment; getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1628,17 +1612,17 @@ IMPLEMENT_FUNCTION_I(27, Mertens, tylerCompartment, MertensActionType) case kActionNone: if (getProgress().field_14 == 29) { - CALLBACK_ACTION(); + callbackAction(); break; } - UPDATE_PARAM_PROC(params->param2, getState()->timeTicks, 150) + if (Entity::updateParameter(params->param2, getState()->timeTicks, 150)) { getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorNormal, kCursorNormal); setCallback(10); setup_playSound16("CON1018A"); break; - UPDATE_PARAM_PROC_END + } label_callback10: if (!params->param3) @@ -1646,7 +1630,8 @@ label_callback10: if (params->param3 >= getState()->timeTicks) { label_callback11: - UPDATE_PARAM(params->param4, getState()->timeTicks, 375); + if (!Entity::updateParameter(params->param4, getState()->timeTicks, 375)) + break; getSound()->playSound(kEntityPlayer, "LIB033"); @@ -1680,7 +1665,7 @@ label_callback11: getSound()->playSound(kEntityPlayer, "LIB015"); getScenes()->loadScene(kScene41); - CALLBACK_ACTION(); + callbackAction(); break; } } else { @@ -1738,7 +1723,7 @@ label_callback11: getSound()->playSound(kEntityPlayer, "LIB015"); getScenes()->loadScene(kScene41); - CALLBACK_ACTION(); + callbackAction(); break; } } else { @@ -1763,7 +1748,7 @@ label_callback11: default: getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); break; case 1: @@ -1821,7 +1806,7 @@ label_callback11: getSound()->playSound(kEntityPlayer, "LIB015"); getScenes()->loadScene(kScene41); - CALLBACK_ACTION(); + callbackAction(); break; } } else { @@ -1930,7 +1915,7 @@ label_callback11: getData()->location = kLocationOutsideCompartment; getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); break; case 8: @@ -1957,7 +1942,7 @@ label_callback11: case 19: case 22: case 28: - CALLBACK_ACTION(); + callbackAction(); break; case 15: @@ -1969,7 +1954,7 @@ label_callback11: getSound()->playSound(kEntityPlayer, "LIB015"); getScenes()->loadScene(kScene41); - CALLBACK_ACTION(); + callbackAction(); break; case 16: @@ -1981,27 +1966,27 @@ label_callback11: getSound()->playSound(kEntityPlayer, "LIB015"); getScenes()->loadScene(kScene41); - CALLBACK_ACTION(); + callbackAction(); break; case 23: getProgress().eventMertensAugustWaiting = true; getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); break; case 24: getProgress().eventMertensKronosInvitation = true; getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); break; case 25: getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -2053,7 +2038,7 @@ IMPLEMENT_FUNCTION_S(28, Mertens, function28) break; case 4: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -2115,7 +2100,7 @@ IMPLEMENT_FUNCTION_SS(29, Mertens, function29) break; case 4: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -2140,14 +2125,14 @@ IMPLEMENT_FUNCTION_I(30, Mertens, function30, MertensActionType) case kActionDefault: switch (params->param1) { default: - CALLBACK_ACTION(); + callbackAction(); return; case 1: params->param2 = kPosition_8200; if (getProgress().field_14) { - CALLBACK_ACTION(); + callbackAction(); return; } @@ -2273,7 +2258,7 @@ IMPLEMENT_FUNCTION_I(30, Mertens, function30, MertensActionType) break; case 9: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -2312,7 +2297,7 @@ IMPLEMENT_FUNCTION_I(31, Mertens, function31, MertensActionType) case 2: case 3: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -2360,7 +2345,7 @@ IMPLEMENT_FUNCTION(32, Mertens, function32) break; case 5: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -2382,7 +2367,7 @@ IMPLEMENT_FUNCTION(33, Mertens, function33) setCallback(ENTITY_PARAM(0, 8) ? 1 : 3); setup_updateEntity(kCarGreenSleeping, ENTITY_PARAM(0, 8) ? kPosition_1500 : kPosition_540); } else { - CALLBACK_ACTION(); + callbackAction(); } break; @@ -2401,7 +2386,7 @@ IMPLEMENT_FUNCTION(33, Mertens, function33) case 2: ENTITY_PARAM(1, 8) = 0; - CALLBACK_ACTION(); + callbackAction(); break; case 3: @@ -2480,7 +2465,7 @@ IMPLEMENT_FUNCTION(33, Mertens, function33) break; } - CALLBACK_ACTION(); + callbackAction(); break; case 12: @@ -2493,13 +2478,13 @@ IMPLEMENT_FUNCTION(33, Mertens, function33) break; } - CALLBACK_ACTION(); + callbackAction(); break; case 13: ENTITY_PARAM(2, 2) = 0; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -2513,7 +2498,7 @@ IMPLEMENT_FUNCTION(34, Mertens, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler); + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(Mertens, setup_chapter1Handler)); break; case kActionDefault: @@ -2548,7 +2533,7 @@ IMPLEMENT_FUNCTION(35, Mertens, function35) case kActionDefault: if (getProgress().field_14 == 29) { - CALLBACK_ACTION(); + callbackAction(); break; } else { getProgress().field_14 = 3; @@ -2589,7 +2574,7 @@ IMPLEMENT_FUNCTION(35, Mertens, function35) break; case 4: - CALLBACK_ACTION(); + callbackAction(); break; case 5: @@ -2611,7 +2596,7 @@ IMPLEMENT_FUNCTION(35, Mertens, function35) break; case 7: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -2626,7 +2611,7 @@ IMPLEMENT_FUNCTION(36, Mertens, function36) case kActionDefault: if (getProgress().field_14 == 29) { - CALLBACK_ACTION(); + callbackAction(); } else { getProgress().field_14 = 3; @@ -2712,7 +2697,7 @@ IMPLEMENT_FUNCTION(36, Mertens, function36) case 6: case 9: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -2767,7 +2752,7 @@ IMPLEMENT_FUNCTION(37, Mertens, function37) break; case 4: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -2791,12 +2776,12 @@ IMPLEMENT_FUNCTION(38, Mertens, function38) case kActionDefault: if (!ENTITY_PARAM(0, 4)) { - CALLBACK_ACTION(); + callbackAction(); break; } if (getProgress().field_14 == 29) { - CALLBACK_ACTION(); + callbackAction(); } else { setCallback(1); setup_updateEntity(kCarGreenSleeping, kPosition_8200); @@ -2810,7 +2795,7 @@ IMPLEMENT_FUNCTION(38, Mertens, function38) case 1: if (!ENTITY_PARAM(0, 4)) { - CALLBACK_ACTION(); + callbackAction(); break; } @@ -2820,7 +2805,7 @@ IMPLEMENT_FUNCTION(38, Mertens, function38) case 2: ENTITY_PARAM(0, 4) = 0; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -2894,7 +2879,7 @@ IMPLEMENT_FUNCTION(39, Mertens, function39) break; case 10: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -2940,7 +2925,7 @@ IMPLEMENT_FUNCTION(40, Mertens, function40) case 5: ENTITY_PARAM(0, 6) = 1; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -3013,7 +2998,7 @@ IMPLEMENT_FUNCTION(42, Mertens, function42) getData()->inventoryItem = kItemInvalid; if (!params->param2) { - TIME_CHECK_SAVEPOINT(kTime1125000, params->param3, kEntityMertens, kEntityMahmud, kAction170483072); + Entity::timeCheckSavepoint(kTime1125000, params->param3, kEntityMertens, kEntityMahmud, kAction170483072); if (params->param4 != kTimeInvalid && getState()->time > kTimeCityChalons) { @@ -3040,11 +3025,11 @@ IMPLEMENT_FUNCTION(42, Mertens, function42) label_callback_8: if (getState()->time > kTime1215000 && !ENTITY_PARAM(0, 1) && !ENTITY_PARAM(2, 1)) { - UPDATE_PARAM_PROC(params->param5, getState()->time, 2700) + if (Entity::updateParameter(params->param5, getState()->time, 2700)) { getEntities()->drawSequenceLeft(kEntityMertens, "601E"); ENTITY_PARAM(0, 1) = 1; params->param5 = 0; - UPDATE_PARAM_PROC_END + } } if (ENTITY_PARAM(0, 8)) { @@ -3547,19 +3532,23 @@ label_callback_5: } label_callback_6: - TIME_CHECK_CALLBACK_1(kTime1971000, params->param1, 7, setup_function28, "CON3012"); + if (Entity::timeCheckCallback(kTime1971000, params->param1, 7, "CON3012", WRAP_SETUP_FUNCTION_S(Mertens, setup_function28))) + break; label_callback_7: - TIME_CHECK_CALLBACK(kTime2117700, params->param2, 8, setup_function32); + if (Entity::timeCheckCallback(kTime2117700, params->param2, 8, WRAP_SETUP_FUNCTION(Mertens, setup_function32))) + break; label_callback_8: - TIME_CHECK_CALLBACK_1(kTime2124000, params->param3, 9, setup_function28, "CON2010"); + if (Entity::timeCheckCallback(kTime2124000, params->param3, 9, "CON2010", WRAP_SETUP_FUNCTION_S(Mertens, setup_function28))) + break; label_callback_9: - TIME_CHECK_CALLBACK(kTime2146500, params->param4, 10, setup_function32); + if (Entity::timeCheckCallback(kTime2146500, params->param4, 10, WRAP_SETUP_FUNCTION(Mertens, setup_function32))) + break; label_callback_10: - TIME_CHECK_CALLBACK(kTime2169000, params->param5, 11, setup_function32); + Entity::timeCheckCallback(kTime2169000, params->param5, 11, WRAP_SETUP_FUNCTION(Mertens, setup_function32)); break; case kAction11: @@ -3742,21 +3731,26 @@ label_callback_3: label_callback_4: if (!params->param1) { - TIME_CHECK_CALLBACK(kTime2403000, params->param2, 5, setup_function49); + if (Entity::timeCheckCallback(kTime2403000, params->param2, 5, WRAP_SETUP_FUNCTION(Mertens, setup_function49))) + break; label_callback_5: - TIME_CHECK_CALLBACK(kTime2430000, params->param3, 6, setup_function32); + if (Entity::timeCheckCallback(kTime2430000, params->param3, 6, WRAP_SETUP_FUNCTION(Mertens, setup_function32))) + break; label_callback_6: - TIME_CHECK_CALLBACK(kTime2439000, params->param4, 7, setup_function32); + if (Entity::timeCheckCallback(kTime2439000, params->param4, 7, WRAP_SETUP_FUNCTION(Mertens, setup_function32))) + break; label_callback_7: - TIME_CHECK_CALLBACK(kTime2448000, params->param5, 8, setup_function32); + if (Entity::timeCheckCallback(kTime2448000, params->param5, 8, WRAP_SETUP_FUNCTION(Mertens, setup_function32))) + break; } label_callback_8: if (getState()->time > kTime2538000 && !ENTITY_PARAM(0, 1) && !ENTITY_PARAM(2, 1)) { - UPDATE_PARAM(params->param6, getState()->time, 2700); + if (!Entity::updateParameter(params->param6, getState()->time, 2700)) + break; getEntities()->drawSequenceLeft(kEntityMertens, "601E"); @@ -3913,7 +3907,7 @@ IMPLEMENT_FUNCTION(49, Mertens, function49) break; case 11: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -4010,7 +4004,8 @@ IMPLEMENT_FUNCTION(53, Mertens, function53) case kActionNone: if (params->param1) { - UPDATE_PARAM(params->param4, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param4, getState()->timeTicks, 75)) + break; params->param1 = 0; params->param2 = 0; @@ -4108,4 +4103,15 @@ IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// IMPLEMENT_NULL_FUNCTION(54, Mertens) +////////////////////////////////////////////////////////////////////////// +// Helper functions +////////////////////////////////////////////////////////////////////////// + +void Mertens::loadSceneFromPosition() { + if (getData()->direction != kDirectionUp) + getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + 750)); + else + getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition - 750), true); +} + } // End of namespace LastExpress diff --git a/engines/lastexpress/entities/mertens.h b/engines/lastexpress/entities/mertens.h index 31b7a97dcd..4cc58fd4ba 100644 --- a/engines/lastexpress/entities/mertens.h +++ b/engines/lastexpress/entities/mertens.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_MERTENS_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { @@ -210,6 +209,9 @@ public: DECLARE_FUNCTION(function53) DECLARE_NULL_FUNCTION() + +private: + void loadSceneFromPosition(); }; } // End of namespace LastExpress diff --git a/engines/lastexpress/entities/milos.cpp b/engines/lastexpress/entities/milos.cpp index ff3d2b6744..519a613497 100644 --- a/engines/lastexpress/entities/milos.cpp +++ b/engines/lastexpress/entities/milos.cpp @@ -36,10 +36,8 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/lastexpress.h" -#include "lastexpress/helpers.h" namespace LastExpress { @@ -135,7 +133,7 @@ IMPLEMENT_FUNCTION_II(10, Milos, enterCompartmentDialog, CarIndex, EntityPositio case kActionNone: case kActionDefault: if (getEntities()->updateEntity(kEntityMilos, (CarIndex)params->param1, (EntityPosition)params->param2)) - CALLBACK_ACTION(); + callbackAction(); break; case kActionExcuseMeCath: @@ -173,16 +171,16 @@ IMPLEMENT_FUNCTION_I(11, Milos, function11, TimeValue) if (!params->param5 && params->param1 < getState()->time && !params->param7) { params->param7 = 1; - CALLBACK_ACTION(); + callbackAction(); break; } if (params->param2) { - UPDATE_PARAM_PROC(params->param8, getState()->timeTicks, 75) + if (Entity::updateParameter(params->param8, getState()->timeTicks, 75)) { params->param2 = 0; params->param3 = 1; getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation1, kCursorNormal, kCursorNormal); - UPDATE_PARAM_PROC_END + } } params->param8 = 0; @@ -191,10 +189,10 @@ IMPLEMENT_FUNCTION_I(11, Milos, function11, TimeValue) break; if (params->param6) { - UPDATE_PARAM_PROC(CURRENT_PARAM(1, 1), getState()->time, 4500) + if (Entity::updateParameter(CURRENT_PARAM(1, 1), getState()->time, 4500)) { params->param6 = 0; CURRENT_PARAM(1, 1) = 0; - UPDATE_PARAM_PROC_END + } } if (!getProgress().field_CC) { @@ -364,7 +362,7 @@ IMPLEMENT_FUNCTION(12, Milos, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler); + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(Milos, setup_chapter1Handler)); break; case kActionDefault: @@ -394,7 +392,7 @@ IMPLEMENT_FUNCTION(13, Milos, function13) getEntities()->clearSequences(kEntityIvo); getEntities()->clearSequences(kEntitySalko); - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: @@ -422,7 +420,7 @@ IMPLEMENT_FUNCTION(14, Milos, function14) getEntities()->exitCompartment(kEntityMilos, kObjectCompartment1, true); getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); } break; } @@ -436,7 +434,8 @@ IMPLEMENT_FUNCTION(14, Milos, function14) if (CURRENT_PARAM(1, 1) < getState()->timeTicks) { if (getObjects()->get(kObjectCompartment1).location == kObjectLocation1) { - UPDATE_PARAM(CURRENT_PARAM(1, 2), getState()->timeTicks, 75); + if (!Entity::updateParameter(CURRENT_PARAM(1, 2), getState()->timeTicks, 75)) + break; getObjects()->update(kObjectCompartment1, kEntityMilos, getObjects()->get(kObjectCompartment1).location, kCursorNormal, kCursorNormal); @@ -474,7 +473,7 @@ IMPLEMENT_FUNCTION(14, Milos, function14) getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); break; } } else { @@ -507,7 +506,8 @@ IMPLEMENT_FUNCTION(14, Milos, function14) } label_callback_12: - UPDATE_PARAM(CURRENT_PARAM(1, 4), getState()->timeTicks, 75); + if (!Entity::updateParameter(CURRENT_PARAM(1, 4), getState()->timeTicks, 75)) + break; getEntities()->exitCompartment(kEntityMilos, kObjectCompartment1, true); @@ -593,7 +593,7 @@ label_callback_12: getData()->location = kLocationOutsideCompartment; getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); break; case 2: @@ -633,7 +633,7 @@ label_callback_12: getScenes()->loadScene(kScene41); getData()->location = kLocationOutsideCompartment; - CALLBACK_ACTION(); + callbackAction(); break; case 6: @@ -722,7 +722,7 @@ IMPLEMENT_FUNCTION(15, Milos, chapter1Handler) break; case kActionNone: - TIME_CHECK_SAVEPOINT(kTime1071000, params->param3, kEntityMilos, kEntityServers1, kAction223002560); + Entity::timeCheckSavepoint(kTime1071000, params->param3, kEntityMilos, kEntityServers1, kAction223002560); if (getState()->time > kTime1089000 && getEntities()->isSomebodyInsideRestaurantOrSalon()) { setup_function16(); @@ -730,15 +730,16 @@ IMPLEMENT_FUNCTION(15, Milos, chapter1Handler) } if (getEntities()->isPlayerPosition(kCarRestaurant, 61) && !params->param1) { - UPDATE_PARAM_PROC(params->param4, getState()->timeTicks, 45) + if (Entity::updateParameter(params->param4, getState()->timeTicks, 45)) { setCallback(1); setup_draw("009C"); break; - UPDATE_PARAM_PROC_END + } } if (getEntities()->isPlayerPosition(kCarRestaurant, 70) && !params->param2) { - UPDATE_PARAM(params->param5, getState()->timeTicks, 45); + if (!Entity::updateParameter(params->param5, getState()->timeTicks, 45)) + break; setCallback(2); setup_draw("009C"); @@ -953,7 +954,8 @@ IMPLEMENT_FUNCTION(21, Milos, function21) break; case kActionNone: - UPDATE_PARAM(params->param2, getState()->time, 4500); + if (!Entity::updateParameter(params->param2, getState()->time, 4500)) + break; params->param1 = 1; break; @@ -1118,7 +1120,8 @@ IMPLEMENT_FUNCTION(24, Milos, function24) } if (params->param1) { - UPDATE_PARAM(params->param5, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param5, getState()->timeTicks, 75)) + break; params->param1 = 0; params->param2 = 1; @@ -1284,14 +1287,15 @@ IMPLEMENT_FUNCTION(25, Milos, function25) case kActionNone: if (!getEvent(kEventMilosCompartmentVisitTyler) && !getProgress().field_54 && !ENTITY_PARAM(0, 4)) { - UPDATE_PARAM_PROC(params->param3, getState()->time, 13500) + if (Entity::updateParameter(params->param3, getState()->time, 13500)) { getSavePoints()->push(kEntityMilos, kEntityVesna, kAction155913424); params->param3 = 0; - UPDATE_PARAM_PROC_END + } } if (params->param1) { - UPDATE_PARAM(params->param4, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param4, getState()->timeTicks, 75)) + break; params->param1 = 0; params->param2 = 1; @@ -1386,7 +1390,7 @@ IMPLEMENT_FUNCTION_I(26, Milos, function26, TimeValue) case kActionNone: if (params->param1 < getState()->time && !params->param2) { - CALLBACK_ACTION(); + callbackAction(); break; } @@ -1415,7 +1419,7 @@ IMPLEMENT_FUNCTION_I(26, Milos, function26, TimeValue) case 1: if (ENTITY_PARAM(0, 2)) { - CALLBACK_ACTION(); + callbackAction(); break; } @@ -1425,7 +1429,7 @@ IMPLEMENT_FUNCTION_I(26, Milos, function26, TimeValue) case 2: case 3: if (ENTITY_PARAM(0, 2)) { - CALLBACK_ACTION(); + callbackAction(); break; } @@ -1442,7 +1446,7 @@ IMPLEMENT_FUNCTION_I(26, Milos, function26, TimeValue) case 5: if (ENTITY_PARAM(0, 2)) { - CALLBACK_ACTION(); + callbackAction(); break; } @@ -1461,7 +1465,7 @@ IMPLEMENT_FUNCTION_II(27, Milos, function27, CarIndex, EntityPosition) case kActionNone: if (getEntities()->updateEntity(kEntityMilos, (CarIndex)params->param1, (EntityPosition)params->param2)) { - CALLBACK_ACTION(); + callbackAction(); break; } @@ -1472,14 +1476,14 @@ IMPLEMENT_FUNCTION_II(27, Milos, function27, CarIndex, EntityPosition) if (getData()->car == kCarRedSleeping || getData()->car == kCarGreenSleeping) { ENTITY_PARAM(0, 2) = 1; - CALLBACK_ACTION(); + callbackAction(); } } break; case kActionDefault: if (getEntities()->updateEntity(kEntityMilos, (CarIndex)params->param1, (EntityPosition)params->param2)) - CALLBACK_ACTION(); + callbackAction(); break; } IMPLEMENT_FUNCTION_END @@ -1536,7 +1540,7 @@ IMPLEMENT_FUNCTION(29, Milos, chapter4Handler) TIME_CHECK_PLAYSOUND_MILOS(kTime2370600, params->param5, "Mil4015"); - TIME_CHECK_SAVEPOINT(kTime2407500, params->param6, kEntityMilos, kEntityVesna, kAction55996766); + Entity::timeCheckSavepoint(kTime2407500, params->param6, kEntityMilos, kEntityVesna, kAction55996766); break; case kActionCallback: diff --git a/engines/lastexpress/entities/milos.h b/engines/lastexpress/entities/milos.h index d58d717f8a..e8f95dc5ff 100644 --- a/engines/lastexpress/entities/milos.h +++ b/engines/lastexpress/entities/milos.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_MILOS_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/mmeboutarel.cpp b/engines/lastexpress/entities/mmeboutarel.cpp index 9ca10ca374..950644cb8f 100644 --- a/engines/lastexpress/entities/mmeboutarel.cpp +++ b/engines/lastexpress/entities/mmeboutarel.cpp @@ -31,10 +31,8 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/lastexpress.h" -#include "lastexpress/helpers.h" namespace LastExpress { @@ -124,7 +122,7 @@ IMPLEMENT_FUNCTION_S(8, MmeBoutarel, function8) if (!getEntities()->isPlayerPosition(kCarRedSleeping, 2)) getData()->entityPosition = kPosition_2088; - CALLBACK_ACTION(); + callbackAction(); } break; @@ -211,7 +209,7 @@ IMPLEMENT_FUNCTION(9, MmeBoutarel, function9) getData()->location = kLocationInsideCompartment; getEntities()->clearSequences(kEntityMmeBoutarel); - CALLBACK_ACTION(); + callbackAction(); break; case 5: @@ -220,7 +218,7 @@ IMPLEMENT_FUNCTION(9, MmeBoutarel, function9) getData()->location = kLocationInsideCompartment; getEntities()->clearSequences(kEntityMmeBoutarel); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -246,7 +244,7 @@ IMPLEMENT_FUNCTION(10, MmeBoutarel, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler); + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(MmeBoutarel, setup_chapter1Handler)); break; case kActionDefault: @@ -312,7 +310,7 @@ IMPLEMENT_FUNCTION(11, MmeBoutarel, function11) break; case 4: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -402,7 +400,7 @@ IMPLEMENT_FUNCTION(13, MmeBoutarel, function13) case kActionNone: if (!getSoundQueue()->isBuffered(kEntityMmeBoutarel) && params->param6 != kTimeInvalid) { - UPDATE_PARAM_PROC_TIME(params->param1, !getEntities()->isDistanceBetweenEntities(kEntityMmeBoutarel, kEntityPlayer, 2000), params->param6, 0) + if (Entity::updateParameterTime((TimeValue)params->param1, !getEntities()->isDistanceBetweenEntities(kEntityMmeBoutarel, kEntityPlayer, 2000), params->param6, 0)) { getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal); getObjects()->update(kObject51, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal); @@ -414,22 +412,24 @@ IMPLEMENT_FUNCTION(13, MmeBoutarel, function13) setCallback(1); setup_playSound("MME1037"); break; - UPDATE_PARAM_PROC_END + } } label_callback_1: if (getProgress().field_24 && params->param7 != kTimeInvalid) { - UPDATE_PARAM_PROC_TIME(kTime1093500, (!params->param5 || !getEntities()->isPlayerInCar(kCarRedSleeping)), params->param7, 0) + if (Entity::updateParameterTime(kTime1093500, (!params->param5 || !getEntities()->isPlayerInCar(kCarRedSleeping)), params->param7, 0)) { setCallback(2); setup_function11(); break; - UPDATE_PARAM_PROC_END + } } - TIME_CHECK(kTime1094400, params->param8, setup_function14); + if (Entity::timeCheck(kTime1094400, params->param8, WRAP_SETUP_FUNCTION(MmeBoutarel, setup_function14))) + break; if (params->param4) { - UPDATE_PARAM(CURRENT_PARAM(1, 1), getState()->timeTicks, 75); + if (!Entity::updateParameter(CURRENT_PARAM(1, 1), getState()->timeTicks, 75)) + break; params->param3 = 1; params->param4 = 0; @@ -589,7 +589,8 @@ IMPLEMENT_FUNCTION(15, MmeBoutarel, function15) label_callback_5: if (params->param2) { - UPDATE_PARAM(params->param5, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param5, getState()->timeTicks, 75)) + break; params->param1 = 1; params->param2 = 0; @@ -1018,7 +1019,8 @@ IMPLEMENT_FUNCTION(23, MmeBoutarel, chapter4Handler) case kActionNone: if (params->param1) { - UPDATE_PARAM(params->param2, getState()->time, 900); + if (!Entity::updateParameter(params->param2, getState()->time, 900)) + break; getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue); @@ -1064,10 +1066,12 @@ IMPLEMENT_FUNCTION(24, MmeBoutarel, function24) break; case kActionNone: - TIME_CHECK(kTime2470500, params->param4, setup_function25); + if (Entity::timeCheck(kTime2470500, params->param4, WRAP_SETUP_FUNCTION(MmeBoutarel, setup_function25))) + break; if (params->param2) { - UPDATE_PARAM(params->param5, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param5, getState()->timeTicks, 75)) + break; params->param1 = 1; params->param2 = 0; @@ -1210,7 +1214,8 @@ IMPLEMENT_FUNCTION(28, MmeBoutarel, function28) case kActionNone: if (params->param1) { - UPDATE_PARAM(params->param3, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param3, getState()->timeTicks, 75)) + break; params->param1 = 0; params->param2 = 1; diff --git a/engines/lastexpress/entities/mmeboutarel.h b/engines/lastexpress/entities/mmeboutarel.h index 772451ba15..0f6e6349fe 100644 --- a/engines/lastexpress/entities/mmeboutarel.h +++ b/engines/lastexpress/entities/mmeboutarel.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_MMEBOUTAREL_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/pascale.cpp b/engines/lastexpress/entities/pascale.cpp index a191273702..6e9f992390 100644 --- a/engines/lastexpress/entities/pascale.cpp +++ b/engines/lastexpress/entities/pascale.cpp @@ -30,10 +30,8 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/lastexpress.h" -#include "lastexpress/helpers.h" namespace LastExpress { @@ -171,7 +169,7 @@ IMPLEMENT_FUNCTION(8, Pascale, welcomeSophieAndRebecca) getData()->entityPosition = kPosition_5900; ENTITY_PARAM(0, 4) = 0; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -185,8 +183,8 @@ IMPLEMENT_FUNCTION(9, Pascale, sitSophieAndRebecca) break; case kActionExitCompartment: - CALLBACK_ACTION(); - break; + callbackAction(); + break; case kActionDefault: getEntities()->drawSequenceLeft(kEntityPascale, "012C1"); @@ -217,7 +215,7 @@ IMPLEMENT_FUNCTION(10, Pascale, welcomeCath) getScenes()->loadSceneFromPosition(kCarRestaurant, 69); } - CALLBACK_ACTION(); + callbackAction(); break; case kAction4: @@ -239,7 +237,7 @@ IMPLEMENT_FUNCTION(10, Pascale, welcomeCath) getScenes()->loadSceneFromPosition(kCarRestaurant, 69); - CALLBACK_ACTION(); + callbackAction(); } break; } @@ -283,7 +281,7 @@ IMPLEMENT_FUNCTION(11, Pascale, function11) getEntities()->clearSequences(kEntityPascale); getData()->entityPosition = kPosition_5900; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -298,7 +296,7 @@ IMPLEMENT_FUNCTION(12, Pascale, chapter1) case kActionNone: setup_chapter1Handler(); - break; + break; case kActionDefault: getSavePoints()->addData(kEntityPascale, kAction239072064, 0); @@ -366,7 +364,7 @@ IMPLEMENT_FUNCTION(13, Pascale, getMessageFromAugustToTyler) getSavePoints()->push(kEntityPascale, kEntityVerges, kActionDeliverMessageToTyler); ENTITY_PARAM(0, 1) = 0; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -382,7 +380,7 @@ IMPLEMENT_FUNCTION(14, Pascale, sitAnna) case kActionExitCompartment: getEntities()->updatePositionExit(kEntityPascale, kCarRestaurant, 62); - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: @@ -433,7 +431,7 @@ IMPLEMENT_FUNCTION(15, Pascale, welcomeAnna) getData()->entityPosition = kPosition_5900; ENTITY_PARAM(0, 2) = 0; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -486,7 +484,7 @@ IMPLEMENT_FUNCTION(16, Pascale, serveTatianaVassili) getData()->entityPosition = kPosition_5900; ENTITY_PARAM(0, 3) = 0; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -648,7 +646,7 @@ IMPLEMENT_FUNCTION(21, Pascale, chapter3) case kActionNone: setup_chapter3Handler(); - break; + break; case kActionDefault: getEntities()->clearSequences(kEntityPascale); @@ -685,7 +683,7 @@ label_callback: setCallback(2); setup_welcomeSophieAndRebecca(); } - break; + break; case kActionCallback: if (getCallback() == 1) @@ -727,7 +725,7 @@ IMPLEMENT_FUNCTION(23, Pascale, function23) ENTITY_PARAM(0, 7) = 0; getEntities()->clearSequences(kEntityPascale); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -748,7 +746,7 @@ IMPLEMENT_FUNCTION(24, Pascale, welcomeAbbot) break; case kActionExitCompartment: - CALLBACK_ACTION(); + callbackAction(); break; case kAction10: @@ -771,7 +769,7 @@ IMPLEMENT_FUNCTION(25, Pascale, chapter4) case kActionNone: setup_chapter4Handler(); - break; + break; case kActionDefault: getEntities()->clearSequences(kEntityPascale); @@ -953,7 +951,7 @@ IMPLEMENT_FUNCTION(27, Pascale, function27) ENTITY_PARAM(1, 1) = 0; ENTITY_PARAM(1, 2) = 1; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -999,7 +997,7 @@ IMPLEMENT_FUNCTION(28, Pascale, messageFromAnna) getData()->entityPosition = kPosition_5900; ENTITY_PARAM(1, 2) = 0; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1037,7 +1035,7 @@ IMPLEMENT_FUNCTION(29, Pascale, function29) case 2: getData()->entityPosition = kPosition_850; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1075,7 +1073,7 @@ IMPLEMENT_FUNCTION(30, Pascale, function30) case 2: getData()->entityPosition = kPosition_5900; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1090,7 +1088,7 @@ IMPLEMENT_FUNCTION(31, Pascale, chapter5) case kActionNone: setup_chapter5Handler(); - break; + break; case kActionDefault: getEntities()->clearSequences(kEntityPascale); @@ -1117,18 +1115,19 @@ IMPLEMENT_FUNCTION(33, Pascale, function33) case kActionNone: if (params->param4) { - UPDATE_PARAM_PROC(params->param5, getState()->time, 4500) + if (Entity::updateParameter(params->param5, getState()->time, 4500)) { getObjects()->update(kObjectCompartmentG, kEntityPascale, kObjectLocation1, kCursorNormal, kCursorNormal); setCallback(1); setup_playSound("Wat5010"); break; - UPDATE_PARAM_PROC_END + } } label_callback1: if (params->param1) { - UPDATE_PARAM(params->param6, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param6, getState()->timeTicks, 75)) + break; params->param1 = 0; params->param2 = 2; diff --git a/engines/lastexpress/entities/pascale.h b/engines/lastexpress/entities/pascale.h index 333ebcae3e..eaf0f3ba0c 100644 --- a/engines/lastexpress/entities/pascale.h +++ b/engines/lastexpress/entities/pascale.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_PASCALE_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/rebecca.cpp b/engines/lastexpress/entities/rebecca.cpp index b1a176b47e..5bcb6aef85 100644 --- a/engines/lastexpress/entities/rebecca.cpp +++ b/engines/lastexpress/entities/rebecca.cpp @@ -30,10 +30,8 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/lastexpress.h" -#include "lastexpress/helpers.h" namespace LastExpress { @@ -179,7 +177,7 @@ IMPLEMENT_FUNCTION(15, Rebecca, function15) getData()->location = kLocationInsideCompartment; getEntities()->clearSequences(kEntityRebecca); - CALLBACK_ACTION(); + callbackAction(); } break; } @@ -261,7 +259,7 @@ IMPLEMENT_FUNCTION_I(16, Rebecca, function16, bool) getSavePoints()->push(kEntityRebecca, kEntityTables3, kAction136455232); getData()->location = kLocationInsideCompartment; - CALLBACK_ACTION(); + callbackAction(); break; } IMPLEMENT_FUNCTION_END @@ -334,7 +332,7 @@ IMPLEMENT_FUNCTION_I(17, Rebecca, function17, bool) case 5: getData()->location = kLocationInsideCompartment; - CALLBACK_ACTION(); + callbackAction(); break; case 6: @@ -343,7 +341,7 @@ IMPLEMENT_FUNCTION_I(17, Rebecca, function17, bool) getData()->location = kLocationInsideCompartment; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -397,7 +395,7 @@ IMPLEMENT_FUNCTION(18, Rebecca, function18) case 2: case 3: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -473,7 +471,7 @@ IMPLEMENT_FUNCTION(19, Rebecca, function19) case 5: case 6: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -493,21 +491,21 @@ IMPLEMENT_FUNCTION_I(20, Rebecca, function20, TimeValue) getObjects()->update(kObjectCompartmentE, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand); getObjects()->update(kObject52, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); break; } if (!params->param2) { params->param6 = 0; } else { - UPDATE_PARAM_PROC(params->param6, getState()->timeTicks, 75) + if (Entity::updateParameter(params->param6, getState()->timeTicks, 75)) { params->param2 = 0; params->param3 = 1; getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorNormal, kCursorNormal); getObjects()->update(kObject52, kEntityRebecca, kObjectLocation1, kCursorNormal, kCursorNormal); params->param6 = 0; - UPDATE_PARAM_PROC_END + } } if (getProgress().chapter == kChapter1 && !ENTITY_PARAM(0, 3)) { @@ -657,7 +655,7 @@ IMPLEMENT_FUNCTION(21, Rebecca, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler); + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(Rebecca, setup_chapter1Handler)); break; case kActionDefault: @@ -685,7 +683,8 @@ IMPLEMENT_FUNCTION(22, Rebecca, chapter1Handler) break; case kActionNone: - TIME_CHECK_CALLBACK_1(kTime1084500, params->param3, 1, setup_playSound, "REB1015"); + if (Entity::timeCheckCallback(kTime1084500, params->param3, 1, "REB1015", WRAP_SETUP_FUNCTION_S(Rebecca, setup_playSound))) + break; if (params->param4 == kTimeInvalid) goto label_callback_4; @@ -699,7 +698,7 @@ IMPLEMENT_FUNCTION(22, Rebecca, chapter1Handler) if (params->param4 >= getState()->time) { label_callback_4: if (params->param1) { - UPDATE_PARAM_CHECK(params->param5, getState()->time, 900) + if (Entity::updateParameterCheck(params->param5, getState()->time, 900)) { if (getEntities()->isInSalon(kEntityPlayer)) { setCallback(5); setup_playSound("REB1013"); @@ -710,7 +709,9 @@ label_callback_4: label_callback_5: if (params->param2) { - UPDATE_PARAM(params->param6, getState()->timeTicks, 90); + if (!Entity::updateParameter(params->param6, getState()->timeTicks, 90)) + break; + getScenes()->loadSceneFromPosition(kCarRestaurant, 55); } else { params->param6 = 0; @@ -774,7 +775,13 @@ IMPLEMENT_FUNCTION(23, Rebecca, function23) break; case kActionNone: - TIME_CHECK_CALLBACK_2(kTime1111500, params->param2, 3, setup_enterExitCompartment, "623De", kObjectCompartmentE); + if (getState()->time > kTime1111500 && !params->param2) { + params->param2 = 1; + setCallback(3); + setup_enterExitCompartment("623De", kObjectCompartmentE); + + break; + } break; case kActionDefault: @@ -843,12 +850,13 @@ IMPLEMENT_FUNCTION(24, Rebecca, function24) break; case kActionNone: - TIME_CHECK_SAVEPOINT(kTime1134000, params->param2, kEntityRebecca, kEntityServers0, kAction223712416); + Entity::timeCheckSavepoint(kTime1134000, params->param2, kEntityRebecca, kEntityServers0, kAction223712416); if (!params->param1) break; - TIME_CHECK_CALLBACK(kTime1165500, params->param3, 6, setup_function19); + if (Entity::timeCheckCallback(kTime1165500, params->param3, 6, WRAP_SETUP_FUNCTION(Rebecca, setup_function19))) + break; if (params->param4 != kTimeInvalid) { if (getState()->time <= kTime1161000) { @@ -965,10 +973,16 @@ IMPLEMENT_FUNCTION(26, Rebecca, function26) break; case kActionNone: - TIME_CHECK_CALLBACK_3(kTime1224000, params->param2, 1, setup_updatePosition, "118H", kCarRestaurant, 52); + if (getState()->time > kTime1224000 && !params->param2) { + params->param2 = 1; + setCallback(1); + setup_updatePosition("118H", kCarRestaurant, 52); + break; + } if (params->param1) { - UPDATE_PARAM(params->param3, getState()->timeTicks, 90); + if (!Entity::updateParameter(params->param3, getState()->timeTicks, 90)) + break; getScenes()->loadSceneFromPosition(kCarRestaurant, 51); } @@ -1223,7 +1237,7 @@ IMPLEMENT_FUNCTION(34, Rebecca, function34) params->param2 = (uint)getState()->time; if (params->param2 >= getState()->time) { - TIME_CHECK_CALLBACK(kTime2052000, params->param3, 1, setup_function19); + Entity::timeCheckCallback(kTime2052000, params->param3, 1, WRAP_SETUP_FUNCTION(Rebecca, setup_function19)); break; } } @@ -1233,7 +1247,7 @@ IMPLEMENT_FUNCTION(34, Rebecca, function34) getSavePoints()->push(kEntityRebecca, kEntityServers0, kAction223712416); } - TIME_CHECK_CALLBACK(kTime2052000, params->param3, 1, setup_function19); + Entity::timeCheckCallback(kTime2052000, params->param3, 1, WRAP_SETUP_FUNCTION(Rebecca, setup_function19)); break; case kActionEndSound: @@ -1632,7 +1646,7 @@ label_next: label_callback_2: if (params->param2) - TIME_CHECK_CALLBACK(kTime2443500, params->param5, 3, setup_function19); + Entity::timeCheckCallback(kTime2443500, params->param5, 3, WRAP_SETUP_FUNCTION(Rebecca, setup_function19)); break; case kActionEndSound: @@ -1755,7 +1769,8 @@ IMPLEMENT_FUNCTION(48, Rebecca, function48) case kActionNone: if (params->param1) { - UPDATE_PARAM(params->param3, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param3, getState()->timeTicks, 75)) + break; params->param1 = 0; params->param2 = 1; diff --git a/engines/lastexpress/entities/rebecca.h b/engines/lastexpress/entities/rebecca.h index e91ee30d4d..885632f884 100644 --- a/engines/lastexpress/entities/rebecca.h +++ b/engines/lastexpress/entities/rebecca.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_REBECCA_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/salko.cpp b/engines/lastexpress/entities/salko.cpp index 63d995dc42..e8b4b4247b 100644 --- a/engines/lastexpress/entities/salko.cpp +++ b/engines/lastexpress/entities/salko.cpp @@ -33,10 +33,8 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/lastexpress.h" -#include "lastexpress/helpers.h" namespace LastExpress { @@ -137,7 +135,7 @@ IMPLEMENT_FUNCTION_II(7, Salko, function7, CarIndex, EntityPosition) break; case kAction123668192: - CALLBACK_ACTION(); + callbackAction(); break; } IMPLEMENT_FUNCTION_END @@ -158,7 +156,7 @@ IMPLEMENT_FUNCTION(9, Salko, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler); + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(Salko, setup_chapter1Handler)); break; case kActionDefault: @@ -301,7 +299,8 @@ IMPLEMENT_FUNCTION(15, Salko, chapter3Handler) case kActionNone: if (getState()->time < kTime2200500) { - UPDATE_PARAM(params->param1, getState()->time, 81000); + if (!Entity::updateParameter(params->param1, getState()->time, 81000)) + break; setCallback(1); setup_function16(); @@ -331,7 +330,8 @@ IMPLEMENT_FUNCTION(16, Salko, function16) } label_callback3: - UPDATE_PARAM(params->param1, getState()->time, 4500); + if (!Entity::updateParameter(params->param1, getState()->time, 4500)) + break; getSavePoints()->push(kEntitySalko, kEntitySalko, kAction101169464); break; @@ -390,7 +390,7 @@ label_callback3: getData()->entityPosition = kPosition_2740; getEntities()->clearSequences(kEntitySalko); - CALLBACK_ACTION(); + callbackAction(); break; } break; diff --git a/engines/lastexpress/entities/salko.h b/engines/lastexpress/entities/salko.h index 6308211053..6ca73e39f6 100644 --- a/engines/lastexpress/entities/salko.h +++ b/engines/lastexpress/entities/salko.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_SALKO_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/servers0.cpp b/engines/lastexpress/entities/servers0.cpp index 989bddd662..73e0d34722 100644 --- a/engines/lastexpress/entities/servers0.cpp +++ b/engines/lastexpress/entities/servers0.cpp @@ -28,10 +28,7 @@ #include "lastexpress/game/savepoint.h" #include "lastexpress/game/state.h" -#include "lastexpress/sound/sound.h" - #include "lastexpress/lastexpress.h" -#include "lastexpress/helpers.h" namespace LastExpress { @@ -113,11 +110,11 @@ IMPLEMENT_FUNCTION_NOSETUP(5, Servers0, callbackActionOnDirection) case kActionNone: if (getData()->direction != kDirectionRight) - CALLBACK_ACTION(); + callbackAction(); break; case kActionExitCompartment: - CALLBACK_ACTION(); + callbackAction(); break; case kActionExcuseMeCath: @@ -163,7 +160,7 @@ IMPLEMENT_FUNCTION(7, Servers0, function7) case 2: getEntities()->clearSequences(kEntityServers0); getData()->entityPosition = kPosition_5900; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -220,7 +217,7 @@ IMPLEMENT_FUNCTION(9, Servers0, function9) ENTITY_PARAM(2, 2) = 0; ENTITY_PARAM(1, 6) = 0; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -315,17 +312,17 @@ IMPLEMENT_FUNCTION(20, Servers0, chapter1Handler) case kActionNone: if (params->param2) { - UPDATE_PARAM_PROC(params->param3, getState()->time, 2700); + if (Entity::updateParameter(params->param3, getState()->time, 2700)) { ENTITY_PARAM(0, 4) = 1; params->param2 = 0; - UPDATE_PARAM_PROC_END + } } if (params->param1) { - UPDATE_PARAM_PROC(params->param4, getState()->time, 4500) + if (Entity::updateParameter(params->param4, getState()->time, 4500)) { ENTITY_PARAM(0, 5) = 1; params->param1 = 0; - UPDATE_PARAM_PROC_END + } } if (!getEntities()->isInKitchen(kEntityServers0) && !getEntities()->isSomebodyInsideRestaurantOrSalon()) @@ -486,7 +483,7 @@ IMPLEMENT_FUNCTION(25, Servers0, function25) getEntities()->clearSequences(kEntityServers0); ENTITY_PARAM(1, 3) = 0; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -647,7 +644,7 @@ IMPLEMENT_FUNCTION(29, Servers0, augustAnnaDateOrder) getEntities()->clearSequences(kEntityServers0); ENTITY_PARAM(1, 5) = 0; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -693,7 +690,7 @@ IMPLEMENT_FUNCTION(30, Servers0, function30) getEntities()->clearSequences(kEntityServers0); ENTITY_PARAM(2, 4) = 0; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -736,10 +733,10 @@ IMPLEMENT_FUNCTION(32, Servers0, chapter4Handler) break; case kActionNone: - UPDATE_PARAM_PROC(params->param2, getState()->time, 3600) + if (Entity::updateParameter(params->param2, getState()->time, 3600)) { ENTITY_PARAM(1, 8) = 1; params->param1 = 0; - UPDATE_PARAM_PROC_END + } if (!getEntities()->isInKitchen(kEntityServers1) || !getEntities()->isSomebodyInsideRestaurantOrSalon()) break; @@ -859,7 +856,7 @@ IMPLEMENT_FUNCTION(33, Servers0, augustOrderSteak) getEntities()->clearSequences(kEntityServers0); ENTITY_PARAM(1, 7) = 0; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -902,7 +899,7 @@ IMPLEMENT_FUNCTION(34, Servers0, augustServeDuck) getEntities()->clearSequences(kEntityServers0); ENTITY_PARAM(1, 8) = 0; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -972,7 +969,7 @@ void Servers0::handleServer(const SavePoint &savepoint, const char *name, Entity getSavePoints()->push(kEntityServers0, entity, action); *parameter = 0; - CALLBACK_ACTION(); + callbackAction(); } break; } @@ -1027,7 +1024,7 @@ void Servers0::serveTable(const SavePoint &savepoint, const char *seq1, EntityIn getEntities()->clearSequences(kEntityServers0); *parameter = 0; - CALLBACK_ACTION(); + callbackAction(); break; } break; diff --git a/engines/lastexpress/entities/servers0.h b/engines/lastexpress/entities/servers0.h index 2e9165a410..4c35637d65 100644 --- a/engines/lastexpress/entities/servers0.h +++ b/engines/lastexpress/entities/servers0.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_SERVERS0_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/servers1.cpp b/engines/lastexpress/entities/servers1.cpp index 995fbbc01b..a8f4c0233c 100644 --- a/engines/lastexpress/entities/servers1.cpp +++ b/engines/lastexpress/entities/servers1.cpp @@ -28,10 +28,7 @@ #include "lastexpress/game/savepoint.h" #include "lastexpress/game/state.h" -#include "lastexpress/sound/sound.h" - #include "lastexpress/lastexpress.h" -#include "lastexpress/helpers.h" namespace LastExpress { @@ -143,7 +140,7 @@ IMPLEMENT_FUNCTION(7, Servers1, function7) getData()->entityPosition = kPosition_5900; ENTITY_PARAM(1, 2) = 0; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -219,7 +216,7 @@ IMPLEMENT_FUNCTION(9, Servers1, function9) getData()->entityPosition = kPosition_5900; ENTITY_PARAM(0, 1) = 0; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -265,7 +262,7 @@ IMPLEMENT_FUNCTION(10, Servers1, function10) getData()->entityPosition = kPosition_5900; ENTITY_PARAM(0, 2) = 0; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -469,7 +466,7 @@ IMPLEMENT_FUNCTION(20, Servers1, function20) getEntities()->drawSequenceLeft(kEntityServers1, "BLANK"); ENTITY_PARAM(0, 7) = 0; - CALLBACK_ACTION(); + callbackAction(); } break; } @@ -566,10 +563,10 @@ IMPLEMENT_FUNCTION(26, Servers1, chapter4Handler) case kActionNone: if (params->param2) { - UPDATE_PARAM_PROC(params->param2, getState()->time, 900) + if (Entity::updateParameter(params->param2, getState()->time, 900)) { ENTITY_PARAM(1, 5) = 1; params->param1 = 0; - UPDATE_PARAM_PROC_END + } } if (!getEntities()->isInKitchen(kEntityServers1) || !getEntities()->isSomebodyInsideRestaurantOrSalon()) @@ -705,7 +702,7 @@ void Servers1::serveTable(const SavePoint &savepoint, const char *seq1, EntityIn if (parameter2 != NULL) *parameter2 = 0; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -775,7 +772,7 @@ void Servers1::serveSalon(const SavePoint &savepoint, const char *seq1, const ch getData()->entityPosition = kPosition_5900; *parameter = 0; - CALLBACK_ACTION(); + callbackAction(); break; } break; diff --git a/engines/lastexpress/entities/servers1.h b/engines/lastexpress/entities/servers1.h index 13b30696f0..c8f667ec5c 100644 --- a/engines/lastexpress/entities/servers1.h +++ b/engines/lastexpress/entities/servers1.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_SERVERS1_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/sophie.cpp b/engines/lastexpress/entities/sophie.cpp index 57bd491949..170090005f 100644 --- a/engines/lastexpress/entities/sophie.cpp +++ b/engines/lastexpress/entities/sophie.cpp @@ -27,38 +27,10 @@ #include "lastexpress/game/savepoint.h" #include "lastexpress/game/state.h" -#include "lastexpress/sound/sound.h" - -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" namespace LastExpress { -#define CHAPTER_IMPLEMENTATION() \ - switch (savepoint.action) { \ - default: \ - break; \ - case kActionNone: \ - setup_chaptersHandler(); \ - break; \ - case kActionDefault: \ - getEntities()->clearSequences(kEntitySophie); \ - getData()->entityPosition = kPosition_4840; \ - getData()->location = kLocationInsideCompartment; \ - getData()->car = kCarRedSleeping; \ - getData()->clothes = kClothesDefault; \ - getData()->inventoryItem = kItemNone; \ - break; \ - } - -#define DEFAULT_ACTION_IMPLEMENTATION() \ - if (savepoint.action == kActionDefault) { \ - getData()->entityPosition = kPosition_4840; \ - getData()->location = kLocationInsideCompartment; \ - getData()->car = kCarRedSleeping; \ - getEntities()->clearSequences(kEntitySophie); \ - } - Sophie::Sophie(LastExpressEngine *engine) : Entity(engine, kEntitySophie) { ADD_CALLBACK_FUNCTION(Sophie, reset); ADD_CALLBACK_FUNCTION(Sophie, updateEntity); @@ -123,7 +95,7 @@ IMPLEMENT_FUNCTION_II(2, Sophie, updateEntity, CarIndex, EntityPosition) break; case kAction123668192: - CALLBACK_ACTION(); + callbackAction(); break; } IMPLEMENT_FUNCTION_END @@ -207,7 +179,7 @@ IMPLEMENT_FUNCTION(4, Sophie, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chaptersHandler); + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(Sophie, setup_chaptersHandler)); break; case kActionDefault: @@ -220,27 +192,27 @@ IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// IMPLEMENT_FUNCTION(5, Sophie, function5) - DEFAULT_ACTION_IMPLEMENTATION() + handleAction(savepoint); IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// IMPLEMENT_FUNCTION(6, Sophie, chapter2) - CHAPTER_IMPLEMENTATION() + handleChapter(savepoint); IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// IMPLEMENT_FUNCTION(7, Sophie, chapter3) - CHAPTER_IMPLEMENTATION() + handleChapter(savepoint); IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// IMPLEMENT_FUNCTION(8, Sophie, chapter4) - CHAPTER_IMPLEMENTATION() + handleChapter(savepoint); IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// IMPLEMENT_FUNCTION(9, Sophie, function9) - DEFAULT_ACTION_IMPLEMENTATION() + handleAction(savepoint); IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// @@ -274,4 +246,37 @@ IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// IMPLEMENT_NULL_FUNCTION(12, Sophie) +////////////////////////////////////////////////////////////////////////// +// Helpers functions +////////////////////////////////////////////////////////////////////////// + +void Sophie::handleAction(const SavePoint &savepoint) { + if (savepoint.action == kActionDefault) { + getData()->entityPosition = kPosition_4840; + getData()->location = kLocationInsideCompartment; + getData()->car = kCarRedSleeping; + getEntities()->clearSequences(kEntitySophie); + } +} + +void Sophie::handleChapter(const SavePoint &savepoint) { + switch (savepoint.action) { + default: + break; + + case kActionNone: + setup_chaptersHandler(); + break; + + case kActionDefault: + getEntities()->clearSequences(kEntitySophie); + getData()->entityPosition = kPosition_4840; + getData()->location = kLocationInsideCompartment; + getData()->car = kCarRedSleeping; + getData()->clothes = kClothesDefault; + getData()->inventoryItem = kItemNone; + break; + } +} + } // End of namespace LastExpress diff --git a/engines/lastexpress/entities/sophie.h b/engines/lastexpress/entities/sophie.h index c2ca348027..188788bb9b 100644 --- a/engines/lastexpress/entities/sophie.h +++ b/engines/lastexpress/entities/sophie.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_SOPHIE_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { @@ -88,6 +87,10 @@ public: DECLARE_FUNCTION(chapter5Handler) DECLARE_NULL_FUNCTION() + +private: + void handleAction(const SavePoint &savepoint); + void handleChapter(const SavePoint &savepoint); }; } // End of namespace LastExpress diff --git a/engines/lastexpress/entities/tables.cpp b/engines/lastexpress/entities/tables.cpp index 06ea4c597c..4f8d2b954d 100644 --- a/engines/lastexpress/entities/tables.cpp +++ b/engines/lastexpress/entities/tables.cpp @@ -29,10 +29,8 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/lastexpress.h" -#include "lastexpress/helpers.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/tables.h b/engines/lastexpress/entities/tables.h index 7fcfc0499e..c213aac978 100644 --- a/engines/lastexpress/entities/tables.h +++ b/engines/lastexpress/entities/tables.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_TABLES_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/tatiana.cpp b/engines/lastexpress/entities/tatiana.cpp index c8901b3e30..432def6253 100644 --- a/engines/lastexpress/entities/tatiana.cpp +++ b/engines/lastexpress/entities/tatiana.cpp @@ -35,10 +35,8 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/lastexpress.h" -#include "lastexpress/helpers.h" namespace LastExpress { @@ -192,7 +190,7 @@ IMPLEMENT_FUNCTION(14, Tatiana, function14) getData()->location = kLocationInsideCompartment; getEntities()->clearSequences(kEntityTatiana); - CALLBACK_ACTION(); + callbackAction(); } break; @@ -228,7 +226,7 @@ IMPLEMENT_FUNCTION(15, Tatiana, function15) getEntities()->exitCompartment(kEntityTatiana, kObjectCompartmentB, true); getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); break; } IMPLEMENT_FUNCTION_END @@ -246,12 +244,13 @@ IMPLEMENT_FUNCTION_I(16, Tatiana, function16, uint32) getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand); getObjects()->update(kObject49, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); break; } if (params->param2) { - UPDATE_PARAM(params->param5, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param5, getState()->timeTicks, 75)) + break; params->param2 = 0; params->param3 = 1; @@ -342,7 +341,7 @@ IMPLEMENT_FUNCTION(17, Tatiana, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler); + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(Tatiana, setup_chapter1Handler)); break; case kActionDefault: @@ -375,10 +374,10 @@ IMPLEMENT_FUNCTION(18, Tatiana, function18) } if (!params->param1) { - UPDATE_PARAM_PROC(params->param3, getState()->time, 4500) + if (Entity::updateParameter(params->param3, getState()->time, 4500)) { getEntities()->drawSequenceRight(kEntityTatiana, "806DS"); params->param1 = 1; - UPDATE_PARAM_PROC_END + } } } @@ -386,14 +385,14 @@ IMPLEMENT_FUNCTION(18, Tatiana, function18) getSavePoints()->push(kEntityTatiana, kEntityAlexei, kAction157159392); getEntities()->clearSequences(kEntityTatiana); - CALLBACK_ACTION(); + callbackAction(); } break; case kActionExitCompartment: getSavePoints()->push(kEntityTatiana, kEntityAlexei, kAction188784532); - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: @@ -425,27 +424,29 @@ IMPLEMENT_FUNCTION(19, Tatiana, chapter1Handler) if (getSoundQueue()->isBuffered(kEntityTatiana) || !params->param4 || params->param3 == 2 || getSoundQueue()->isBuffered("TAT1066")) goto label_tatiana_chapter1_2; - UPDATE_PARAM_PROC(params->param5, getState()->timeTicks, 450) + if (Entity::updateParameter(params->param5, getState()->timeTicks, 450)) { getSound()->playSound(kEntityTatiana, params->param3 ? "TAT1069B" : "TAT1069A"); getProgress().field_64 = 1; params->param3++; params->param5 = 0; - UPDATE_PARAM_PROC_END + } if (getEntities()->isPlayerPosition(kCarRestaurant, 71)) { - UPDATE_PARAM_PROC(params->param6, getState()->timeTicks, 75) + if (Entity::updateParameter(params->param6, getState()->timeTicks, 75)) { getSound()->playSound(kEntityTatiana, params->param3 ? "TAT1069B" : "TAT1069A"); getProgress().field_64 = 1; params->param3++; params->param6 = 0; - UPDATE_PARAM_PROC_END + } } label_tatiana_chapter1_2: - TIME_CHECK_SAVEPOINT(kTime1084500, params->param7, kEntityTatiana, kEntityPascale, kAction257489762); + Entity::timeCheckSavepoint(kTime1084500, params->param7, kEntityTatiana, kEntityPascale, kAction257489762); if (params->param1) { - UPDATE_PARAM(params->param8, getState()->timeTicks, 90); + if (!Entity::updateParameter(params->param8, getState()->timeTicks, 90)) + break; + getScenes()->loadSceneFromPosition(kCarRestaurant, 65); } else { params->param8 = 0; @@ -614,7 +615,7 @@ IMPLEMENT_FUNCTION(22, Tatiana, function22) if (params->param1 == kTimeInvalid || getState()->time <= kTime1179000) goto label_update; - UPDATE_PARAM_PROC_TIME(kTime1233000, ((!getEvent(kEventTatianaAskMatchSpeakRussian) && !getEvent(kEventTatianaAskMatch)) || getEntities()->isInGreenCarEntrance(kEntityPlayer)), params->param1, 0) + if (Entity::updateParameterTime(kTime1233000, ((!getEvent(kEventTatianaAskMatchSpeakRussian) && !getEvent(kEventTatianaAskMatch)) || getEntities()->isInGreenCarEntrance(kEntityPlayer)), params->param1, 0)) { label_update: if (!getEvent(kEventTatianaAskMatchSpeakRussian) && !getEvent(kEventTatianaAskMatch) @@ -623,7 +624,7 @@ label_update: getObjects()->update(kObject25, kEntityTatiana, kObjectLocation1, kCursorNormal, kCursorForward); getObjects()->update(kObjectTrainTimeTable, kEntityTatiana, kObjectLocation1, kCursorNormal, kCursorForward); } - UPDATE_PARAM_PROC_END + } params->param1 = kTimeInvalid; @@ -1022,7 +1023,7 @@ IMPLEMENT_FUNCTION(32, Tatiana, chapter3Handler) } if (parameters->param4 && parameters->param5) { - UPDATE_PARAM_CHECK(parameters->param4, getState()->time, 6300) + if (Entity::updateParameterCheck(parameters->param4, getState()->time, 6300)) { if (getEntities()->isSomebodyInsideRestaurantOrSalon()) { getData()->location = kLocationOutsideCompartment; @@ -1283,18 +1284,19 @@ IMPLEMENT_FUNCTION(37, Tatiana, function37) params->param3 = (uint)getState()->time + 900; if (params->param4 != kTimeInvalid && params->param3 < getState()->time) { - UPDATE_PARAM_PROC_TIME(kTime2227500, !getEntities()->isPlayerInCar(kCarRedSleeping), params->param4, 450) + if (Entity::updateParameterTime(kTime2227500, !getEntities()->isPlayerInCar(kCarRedSleeping), params->param4, 450)) { getProgress().field_5C = 1; if (getEntities()->isInsideCompartment(kEntityAnna, kCarRedSleeping, kPosition_4070)) { setup_function38(); break; } - UPDATE_PARAM_PROC_END + } } } if (params->param1) { - UPDATE_PARAM(params->param5, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param5, getState()->timeTicks, 75)) + break; getObjects()->update(kObjectCompartmentB, kEntityTatiana, kObjectLocation1, kCursorNormal, kCursorNormal); getObjects()->update(kObject49, kEntityTatiana, kObjectLocation1, kCursorNormal, kCursorNormal); @@ -1403,7 +1405,8 @@ IMPLEMENT_FUNCTION(38, Tatiana, function38) break; case kActionNone: - UPDATE_PARAM(params->param1, getState()->time, 450); + if (!Entity::updateParameter(params->param1, getState()->time, 450)) + break; getEntities()->exitCompartment(kEntityTatiana, kObjectCompartmentF, true); @@ -1535,7 +1538,7 @@ IMPLEMENT_FUNCTION(40, Tatiana, function40) if (getEntities()->isInsideTrainCar(kEntityPlayer, kCarKronos) || getData()->car != getEntityData(kEntityPlayer)->car || getEntities()->updateEntity(kEntityTatiana, kCarKronos, kPosition_9270)) - CALLBACK_ACTION(); + callbackAction(); break; case kActionExcuseMe: @@ -1547,7 +1550,7 @@ IMPLEMENT_FUNCTION(40, Tatiana, function40) case kActionDefault: if (getEntities()->updateEntity(kEntityTatiana, kCarKronos, kPosition_9270)) - CALLBACK_ACTION(); + callbackAction(); break; } IMPLEMENT_FUNCTION_END @@ -1595,7 +1598,7 @@ IMPLEMENT_FUNCTION(41, Tatiana, function41) } getEntities()->clearSequences(kEntityTatiana); - CALLBACK_ACTION(); + callbackAction(); } break; @@ -1629,7 +1632,7 @@ IMPLEMENT_FUNCTION(41, Tatiana, function41) case 6: getEntities()->clearSequences(kEntityTatiana); - CALLBACK_ACTION(); + callbackAction(); break; case 4: @@ -1952,7 +1955,8 @@ IMPLEMENT_FUNCTION(48, Tatiana, function48) if (!params->param1 || getSoundQueue()->isBuffered(kEntityTatiana)) goto label_end; - UPDATE_PARAM_GOTO(params->param2, getState()->timeTicks, 5 * (3 * rnd(5) + 30), label_end); + if (!Entity::updateParameter(params->param2, getState()->timeTicks, 5 * (3 * rnd(5) + 30))) + goto label_end; getSound()->playSound(kEntityTatiana, "LIB012", kFlagDefault); params->param2 = 0; @@ -2199,7 +2203,8 @@ IMPLEMENT_FUNCTION(54, Tatiana, function54) } if (params->param1 > 3) { - UPDATE_PARAM(params->param3, getState()->timeTicks, 225); + if (!Entity::updateParameter(params->param3, getState()->timeTicks, 225)) + break; params->param1 = 0; params->param3 = 0; diff --git a/engines/lastexpress/entities/tatiana.h b/engines/lastexpress/entities/tatiana.h index c457d49b1e..8ec69db5e9 100644 --- a/engines/lastexpress/entities/tatiana.h +++ b/engines/lastexpress/entities/tatiana.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_TATIANA_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/train.cpp b/engines/lastexpress/entities/train.cpp index bced1da62b..e3f530ef25 100644 --- a/engines/lastexpress/entities/train.cpp +++ b/engines/lastexpress/entities/train.cpp @@ -32,10 +32,8 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/lastexpress.h" -#include "lastexpress/helpers.h" namespace LastExpress { @@ -269,18 +267,20 @@ IMPLEMENT_FUNCTION(8, Train, process) if ((getEntities()->isPlayerInCar(kCarGreenSleeping) || getEntities()->isPlayerInCar(kCarRedSleeping)) && params->param4 && !params->param5) { - params->param4 -= 1; + params->param4 -= 1; - if (!params->param4 && getProgress().jacket == kJacketGreen) { + if (!params->param4 && getProgress().jacket == kJacketGreen) { - getAction()->playAnimation(isNight() ? kEventCathSmokeNight : kEventCathSmokeDay); - params->param5 = 1; - getScenes()->processScene(); - } + getAction()->playAnimation(isNight() ? kEventCathSmokeNight : kEventCathSmokeDay); + params->param5 = 1; + getScenes()->processScene(); + } } if (params->param6) { - UPDATE_PARAM_GOTO(params1->param7, getState()->time, 900, label_process); + if (!Entity::updateParameter(params1->param7, getState()->time, 900)) + goto label_process; + getScenes()->loadSceneFromPosition(kCarRestaurant, 58); } @@ -552,7 +552,7 @@ void Train::handleCompartmentAction() { ENTITY_PARAM(0, 8) = params->param1; - CALLBACK_ACTION(); + callbackAction(); } ////////////////////////////////////////////////////////////////////////// diff --git a/engines/lastexpress/entities/train.h b/engines/lastexpress/entities/train.h index ea9e1d7a07..4b8bc10c1a 100644 --- a/engines/lastexpress/entities/train.h +++ b/engines/lastexpress/entities/train.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_TRAIN_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/vassili.cpp b/engines/lastexpress/entities/vassili.cpp index 22f41afa92..4695f8831f 100644 --- a/engines/lastexpress/entities/vassili.cpp +++ b/engines/lastexpress/entities/vassili.cpp @@ -35,10 +35,8 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/lastexpress.h" -#include "lastexpress/helpers.h" namespace LastExpress { @@ -85,7 +83,7 @@ IMPLEMENT_FUNCTION(4, Vassili, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler); + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(Vassili, setup_chapter1Handler)); break; case kActionDefault: @@ -146,7 +144,8 @@ IMPLEMENT_FUNCTION(6, Vassili, function6) case kActionNone: if (getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_8200)) { - UPDATE_PARAM_GOTO(params->param3, getState()->timeTicks, params->param1, label_function7); + if (!Entity::updateParameter(params->param3, getState()->timeTicks, params->param1)) + goto label_function7; setCallback(1); setup_draw("303B"); @@ -402,7 +401,8 @@ IMPLEMENT_FUNCTION(13, Vassili, sleeping) case kActionNone: if (getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_8200)) { - UPDATE_PARAM(params->param3, getState()->timeTicks, params->param1); + if (!Entity::updateParameter(params->param3, getState()->timeTicks, params->param1)) + break; setCallback(1); setup_draw("303B"); @@ -461,7 +461,8 @@ IMPLEMENT_FUNCTION(15, Vassili, stealEgg) case kActionNone: if (getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_8200)) { - UPDATE_PARAM(params->param3, getState()->timeTicks, params->param1); + if (!Entity::updateParameter(params->param3, getState()->timeTicks, params->param1)) + break; setCallback(1); setup_draw("303B"); @@ -545,7 +546,8 @@ IMPLEMENT_FUNCTION(17, Vassili, chapter4Handler) case kActionNone: if (getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_8200)) { - UPDATE_PARAM(params->param3, getState()->timeTicks, params->param1); + if (!Entity::updateParameter(params->param3, getState()->timeTicks, params->param1)) + break; setCallback(1); setup_draw("303B"); diff --git a/engines/lastexpress/entities/vassili.h b/engines/lastexpress/entities/vassili.h index 843a065174..d006770f7b 100644 --- a/engines/lastexpress/entities/vassili.h +++ b/engines/lastexpress/entities/vassili.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_VASSILI_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/verges.cpp b/engines/lastexpress/entities/verges.cpp index 8246f85145..d9ddb0a4d1 100644 --- a/engines/lastexpress/entities/verges.cpp +++ b/engines/lastexpress/entities/verges.cpp @@ -32,10 +32,8 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/lastexpress.h" -#include "lastexpress/helpers.h" namespace LastExpress { @@ -48,40 +46,40 @@ Verges::Verges(LastExpressEngine *engine) : Entity(engine, kEntityVerges) { ADD_CALLBACK_FUNCTION(Verges, callbackActionRestaurantOrSalon); ADD_CALLBACK_FUNCTION(Verges, savegame); ADD_CALLBACK_FUNCTION(Verges, updateEntity); - ADD_CALLBACK_FUNCTION(Verges, function9); - ADD_CALLBACK_FUNCTION(Verges, function10); + ADD_CALLBACK_FUNCTION(Verges, walkBetweenCars); + ADD_CALLBACK_FUNCTION(Verges, makeAnnouncement); ADD_CALLBACK_FUNCTION(Verges, function11); ADD_CALLBACK_FUNCTION(Verges, function12); - ADD_CALLBACK_FUNCTION(Verges, function13); + ADD_CALLBACK_FUNCTION(Verges, baggageCar); ADD_CALLBACK_FUNCTION(Verges, updateFromTime); - ADD_CALLBACK_FUNCTION(Verges, function15); - ADD_CALLBACK_FUNCTION(Verges, function16); - ADD_CALLBACK_FUNCTION(Verges, function17); + ADD_CALLBACK_FUNCTION(Verges, dialog); + ADD_CALLBACK_FUNCTION(Verges, dialog2); + ADD_CALLBACK_FUNCTION(Verges, talkAboutPassengerList); ADD_CALLBACK_FUNCTION(Verges, chapter1); ADD_CALLBACK_FUNCTION(Verges, talkHarem); ADD_CALLBACK_FUNCTION(Verges, talkPassengerList); ADD_CALLBACK_FUNCTION(Verges, talkGendarmes); - ADD_CALLBACK_FUNCTION(Verges, function22); + ADD_CALLBACK_FUNCTION(Verges, askMertensToRelayAugustInvitation); ADD_CALLBACK_FUNCTION(Verges, function23); ADD_CALLBACK_FUNCTION(Verges, policeGettingOffTrain); - ADD_CALLBACK_FUNCTION(Verges, function25); + ADD_CALLBACK_FUNCTION(Verges, policeSearch); ADD_CALLBACK_FUNCTION(Verges, chapter1Handler); ADD_CALLBACK_FUNCTION(Verges, chapter2); ADD_CALLBACK_FUNCTION(Verges, chapter2Handler); ADD_CALLBACK_FUNCTION(Verges, chapter3); ADD_CALLBACK_FUNCTION(Verges, function30); - ADD_CALLBACK_FUNCTION(Verges, function31); + ADD_CALLBACK_FUNCTION(Verges, talkAboutMax); ADD_CALLBACK_FUNCTION(Verges, function32); ADD_CALLBACK_FUNCTION(Verges, function33); ADD_CALLBACK_FUNCTION(Verges, function34); - ADD_CALLBACK_FUNCTION(Verges, function35); + ADD_CALLBACK_FUNCTION(Verges, organizeConcertInvitations); ADD_CALLBACK_FUNCTION(Verges, chapter4); ADD_CALLBACK_FUNCTION(Verges, chapter4Handler); - ADD_CALLBACK_FUNCTION(Verges, function38); + ADD_CALLBACK_FUNCTION(Verges, resetState); ADD_CALLBACK_FUNCTION(Verges, chapter5); ADD_CALLBACK_FUNCTION(Verges, chapter5Handler); - ADD_CALLBACK_FUNCTION(Verges, function41); - ADD_CALLBACK_FUNCTION(Verges, function42); + ADD_CALLBACK_FUNCTION(Verges, askPassengersToStayInCompartments); + ADD_CALLBACK_FUNCTION(Verges, end); } ////////////////////////////////////////////////////////////////////////// @@ -102,11 +100,11 @@ IMPLEMENT_FUNCTION(3, Verges, callbackActionOnDirection) case kActionNone: if (getData()->direction != kDirectionRight) - CALLBACK_ACTION(); + callbackAction(); break; case kActionExitCompartment: - CALLBACK_ACTION(); + callbackAction(); break; case kActionExcuseMeCath: @@ -151,7 +149,7 @@ IMPLEMENT_FUNCTION_II(8, Verges, updateEntity, CarIndex, EntityPosition) IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// -IMPLEMENT_FUNCTION_S(9, Verges, function9) +IMPLEMENT_FUNCTION_S(9, Verges, walkBetweenCars) switch (savepoint.action) { default: break; @@ -203,7 +201,7 @@ switch (savepoint.action) { case 3: setCallback(4); - setup_function10(kCarGreenSleeping, kPosition_540, (char *)¶ms->seq1); + setup_makeAnnouncement(kCarGreenSleeping, kPosition_540, (char *)¶ms->seq1); break; case 4: @@ -219,7 +217,7 @@ switch (savepoint.action) { break; case 6: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -227,7 +225,7 @@ switch (savepoint.action) { IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// -IMPLEMENT_FUNCTION_IIS(10, Verges, function10, CarIndex, EntityPosition) +IMPLEMENT_FUNCTION_IIS(10, Verges, makeAnnouncement, CarIndex, EntityPosition) switch (savepoint.action) { default: break; @@ -241,12 +239,13 @@ IMPLEMENT_FUNCTION_IIS(10, Verges, function10, CarIndex, EntityPosition) } if (getEntities()->updateEntity(kEntityVerges, (CarIndex)params->param1, (EntityPosition)params->param2)) { - CALLBACK_ACTION(); + callbackAction(); break; } if (params->param6) { - UPDATE_PARAM(params->param8, getState()->timeTicks, 75); + if (!Entity::updateParameter(params->param8, getState()->timeTicks, 75)) + break; getSound()->playSound(kEntityVerges, (char *)¶ms->seq); @@ -266,7 +265,7 @@ IMPLEMENT_FUNCTION_IIS(10, Verges, function10, CarIndex, EntityPosition) } if (getEntities()->updateEntity(kEntityVerges, (CarIndex)params->param1, (EntityPosition)params->param2)) - CALLBACK_ACTION(); + callbackAction(); break; } IMPLEMENT_FUNCTION_END @@ -337,7 +336,7 @@ IMPLEMENT_FUNCTION(11, Verges, function11) getObjects()->update(kObject104, kEntityVerges, kObjectLocationNone, kCursorNormal, kCursorHand); getObjects()->update(kObject105, kEntityVerges, kObjectLocationNone, kCursorNormal, kCursorHand); - CALLBACK_ACTION(); + callbackAction(); break; } } @@ -397,7 +396,7 @@ IMPLEMENT_FUNCTION(12, Verges, function12) getData()->entityPosition = kPosition_850; getEntities()->clearSequences(kEntityVerges); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -405,7 +404,7 @@ IMPLEMENT_FUNCTION(12, Verges, function12) IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// -IMPLEMENT_FUNCTION_I(13, Verges, function13, bool) +IMPLEMENT_FUNCTION_I(13, Verges, baggageCar, bool) switch (savepoint.action) { default: break; @@ -438,7 +437,7 @@ IMPLEMENT_FUNCTION_I(13, Verges, function13, bool) getEntities()->clearSequences(kEntityVerges); getScenes()->loadSceneFromPosition(kCarBaggage, 91); - CALLBACK_ACTION(); + callbackAction(); } break; } @@ -450,7 +449,7 @@ IMPLEMENT_FUNCTION_I(14, Verges, updateFromTime, uint32) IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// -IMPLEMENT_FUNCTION_IS(15, Verges, function15, EntityIndex) +IMPLEMENT_FUNCTION_IS(15, Verges, dialog, EntityIndex) switch (savepoint.action) { default: break; @@ -462,7 +461,7 @@ IMPLEMENT_FUNCTION_IS(15, Verges, function15, EntityIndex) if (!getEntities()->isPlayerPosition(kCarGreenSleeping, 2) && !getEntities()->isPlayerPosition(kCarRedSleeping, 2)) getData()->entityPosition = kPosition_2088; - CALLBACK_ACTION(); + callbackAction(); } break; @@ -487,7 +486,7 @@ IMPLEMENT_FUNCTION_IS(15, Verges, function15, EntityIndex) IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// -IMPLEMENT_FUNCTION_ISS(16, Verges, function16, EntityIndex) +IMPLEMENT_FUNCTION_ISS(16, Verges, dialog2, EntityIndex) switch (savepoint.action) { default: break; @@ -499,7 +498,7 @@ IMPLEMENT_FUNCTION_ISS(16, Verges, function16, EntityIndex) if (!getEntities()->isPlayerPosition(kCarGreenSleeping, 2) && !getEntities()->isPlayerPosition(kCarRedSleeping, 2)) getData()->entityPosition = kPosition_2088; - CALLBACK_ACTION(); + callbackAction(); } break; @@ -527,7 +526,7 @@ IMPLEMENT_FUNCTION_ISS(16, Verges, function16, EntityIndex) IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// -IMPLEMENT_FUNCTION(17, Verges, function17) +IMPLEMENT_FUNCTION(17, Verges, talkAboutPassengerList) switch (savepoint.action) { default: break; @@ -549,7 +548,7 @@ IMPLEMENT_FUNCTION(17, Verges, function17) case 2: setCallback(3); - setup_function15(kEntityMertens, "TRA1291"); + setup_dialog(kEntityMertens, "TRA1291"); break; case 3: @@ -559,7 +558,7 @@ IMPLEMENT_FUNCTION(17, Verges, function17) case 4: ENTITY_PARAM(0, 3) = 0; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -573,7 +572,7 @@ IMPLEMENT_FUNCTION(18, Verges, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler); + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(Verges, setup_chapter1Handler)); break; case kActionDefault: @@ -612,7 +611,7 @@ IMPLEMENT_FUNCTION(21, Verges, talkGendarmes) IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// -IMPLEMENT_FUNCTION(22, Verges, function22) +IMPLEMENT_FUNCTION(22, Verges, askMertensToRelayAugustInvitation) switch (savepoint.action) { default: break; @@ -635,10 +634,10 @@ IMPLEMENT_FUNCTION(22, Verges, function22) case 2: if (getEvent(kEventMertensAskTylerCompartment) || getEvent(kEventMertensAskTylerCompartmentD) || getEvent(kEventMertensAugustWaiting)) { setCallback(3); - setup_function16(kEntityMertens, "TRA1200", "TRA1201"); + setup_dialog2(kEntityMertens, "TRA1200", "TRA1201"); } else { setCallback(4); - setup_function16(kEntityMertens, "TRA1200A", "TRA1201"); + setup_dialog2(kEntityMertens, "TRA1200A", "TRA1201"); } break; @@ -651,7 +650,7 @@ IMPLEMENT_FUNCTION(22, Verges, function22) break; case 5: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -697,7 +696,7 @@ IMPLEMENT_FUNCTION(24, Verges, policeGettingOffTrain) break; case kActionEndSound: - CALLBACK_ACTION(); + callbackAction(); break; case kActionDefault: @@ -715,7 +714,7 @@ IMPLEMENT_FUNCTION(24, Verges, policeGettingOffTrain) IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// -IMPLEMENT_FUNCTION(25, Verges, function25) +IMPLEMENT_FUNCTION(25, Verges, policeSearch) switch (savepoint.action) { default: break; @@ -775,10 +774,10 @@ IMPLEMENT_FUNCTION(25, Verges, function25) if (getData()->car == kCarRedSleeping) { setCallback(6); - setup_function10(kCarGreenSleeping, kPosition_540, "TRA1005"); + setup_makeAnnouncement(kCarGreenSleeping, kPosition_540, "TRA1005"); } else { setCallback(7); - setup_function10(kCarRedSleeping, kPosition_9460, "TRA1006"); + setup_makeAnnouncement(kCarRedSleeping, kPosition_9460, "TRA1006"); } break; } @@ -806,7 +805,7 @@ IMPLEMENT_FUNCTION(25, Verges, function25) getSavePoints()->push(kEntityVerges, kEntityCoudert, kAction168254872); setCallback(4); - setup_function10(kCarRedSleeping, kPosition_9460, "TRA1006"); + setup_makeAnnouncement(kCarRedSleeping, kPosition_9460, "TRA1006"); break; case 4: @@ -818,7 +817,7 @@ IMPLEMENT_FUNCTION(25, Verges, function25) case 11: ENTITY_PARAM(0, 7) = 0; - CALLBACK_ACTION(); + callbackAction(); break; case 6: @@ -839,7 +838,7 @@ IMPLEMENT_FUNCTION(25, Verges, function25) getSavePoints()->push(kEntityVerges, kEntityCoudert, kAction168254872); setCallback(10); - setup_function10(kCarGreenSleeping, kPosition_540, "TRA1006"); + setup_makeAnnouncement(kCarGreenSleeping, kPosition_540, "TRA1006"); break; case 10: @@ -893,14 +892,14 @@ IMPLEMENT_FUNCTION(26, Verges, chapter1Handler) label_callback1: if (getEntities()->isInBaggageCarEntrance(kEntityPlayer)) { setCallback(2); - setup_function13(false); + setup_baggageCar(false); break; } label_callback2: if (ENTITY_PARAM(0, 7)) { setCallback(3); - setup_function25(); + setup_policeSearch(); break; } @@ -908,10 +907,12 @@ label_callback3: if (params->param6) goto label_callback12; - TIME_CHECK_CALLBACK_1(kTimeChapter1, params->param7, 4, setup_function9, "TRA1001"); + if (Entity::timeCheckCallback(kTimeChapter1, params->param7, 4, "TRA1001", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars))) + break; label_callback4: - TIME_CHECK_CALLBACK(kTime1089000, params->param8, 5, setup_function12); + if (Entity::timeCheckCallback(kTime1089000, params->param8, 5, WRAP_SETUP_FUNCTION(Verges, setup_function12))) + break; params->param8 = 1; @@ -922,16 +923,20 @@ label_callback4: } label_callback8: - TIME_CHECK_CALLBACK_1(kTime1107000, CURRENT_PARAM(1, 1), 9, setup_function9, "TRA1001A"); + if (Entity::timeCheckCallback(kTime1107000, CURRENT_PARAM(1, 1), 9, "TRA1001A", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars))) + break; label_callback9: - TIME_CHECK_CALLBACK_1(kTime1134000, CURRENT_PARAM(1, 2), 10, setup_function9, "TRA1002"); + if (Entity::timeCheckCallback(kTime1134000, CURRENT_PARAM(1, 2), 10, "TRA1002", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars))) + break; label_callback10: - TIME_CHECK_CALLBACK_1(kTime1165500, CURRENT_PARAM(1, 3), 11, setup_function9, "TRA1003"); + if (Entity::timeCheckCallback(kTime1165500, CURRENT_PARAM(1, 3), 11, "TRA1003", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars))) + break; label_callback11: - TIME_CHECK_CALLBACK_1(kTime1225800, CURRENT_PARAM(1, 4), 12, setup_function9, "TRA1004"); + if (Entity::timeCheckCallback(kTime1225800, CURRENT_PARAM(1, 4), 12, "TRA1004", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars))) + break; label_callback12: if (ENTITY_PARAM(0, 5) && !params->param2) { @@ -950,7 +955,7 @@ label_callback13: label_callback14: if (ENTITY_PARAM(0, 3) && !params->param4 && (getState()->time < kTime1134000 || getState()->time > kTime1156500)) { setCallback(15); - setup_function17(); + setup_talkAboutPassengerList(); break; } @@ -958,14 +963,14 @@ label_callback15: if (ENTITY_PARAM(0, 1) && !params->param5) { if (getState()->time < kTime1134000 || getState()->time > kTime1156500) { setCallback(16); - setup_function22(); + setup_askMertensToRelayAugustInvitation(); } } break; case kActionOpenDoor: setCallback(17); - setup_function13(savepoint.param.intValue < 106 ? true : false); + setup_baggageCar(savepoint.param.intValue < 106 ? true : false); break; case kActionDefault: @@ -1001,7 +1006,7 @@ label_callback15: case 6: setCallback(7); - setup_function15(kEntityMertens, "TRA1202"); + setup_dialog(kEntityMertens, "TRA1202"); break; case 7: @@ -1079,11 +1084,12 @@ IMPLEMENT_FUNCTION(28, Verges, chapter2Handler) case kActionNone: if (getEntities()->isInBaggageCarEntrance(kEntityPlayer)) { setCallback(1); - setup_function13(false); + setup_baggageCar(false); } label_callback_1: - TIME_CHECK_CALLBACK_1(kTime1818900, params->param1, 2, setup_function9, "Tra2177"); + if (Entity::timeCheckCallback(kTime1818900, params->param1, 2, "Tra2177", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars))) + break; label_callback_2: if (params->param2 == kTimeInvalid || !getState()->time) @@ -1111,7 +1117,7 @@ label_callback_6: if (ENTITY_PARAM(0, 3)) { setCallback(7); - setup_function17(); + setup_talkAboutPassengerList(); } break; @@ -1124,7 +1130,7 @@ label_callback_6: case kActionOpenDoor: setCallback(8); - setup_function13(savepoint.param.intValue < 106); + setup_baggageCar(savepoint.param.intValue < 106); break; case kActionDefault: @@ -1149,7 +1155,7 @@ label_callback_6: case 4: setCallback(5); - setup_function15(kEntityCoudert, "TRA2100"); + setup_dialog(kEntityCoudert, "TRA2100"); break; case 5: @@ -1171,7 +1177,7 @@ IMPLEMENT_FUNCTION(29, Verges, chapter3) break; case kActionNone: - setup_function23(); + setup_function33(); break; case kActionDefault: @@ -1215,7 +1221,7 @@ IMPLEMENT_FUNCTION_S(30, Verges, function30) case 2: setCallback(3); - setup_function15(kEntityCoudert, (char *)¶ms->seq1); + setup_dialog(kEntityCoudert, (char *)¶ms->seq1); break; case 3: @@ -1224,7 +1230,7 @@ IMPLEMENT_FUNCTION_S(30, Verges, function30) break; case 4: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1232,7 +1238,7 @@ IMPLEMENT_FUNCTION_S(30, Verges, function30) IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// -IMPLEMENT_FUNCTION(31, Verges, function31) +IMPLEMENT_FUNCTION(31, Verges, talkAboutMax) switch (savepoint.action) { default: break; @@ -1254,7 +1260,7 @@ IMPLEMENT_FUNCTION(31, Verges, function31) case 2: setCallback(3); - setup_function15(kEntityCoudert, "TRA3015"); + setup_dialog(kEntityCoudert, "TRA3015"); break; case 3: @@ -1266,7 +1272,7 @@ IMPLEMENT_FUNCTION(31, Verges, function31) getProgress().field_48 = 1; ENTITY_PARAM(0, 4) = 0; - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1280,7 +1286,12 @@ IMPLEMENT_FUNCTION(32, Verges, function32) break; case kActionNone: - TIME_CHECK_CALLBACK_3(kTime2263500, params->param1, 5, setup_function10, kCarRedSleeping, kPosition_9460, "TRA3006"); + if (getState()->time > kTime2263500 && !params->param1) { + params->param1 = 1; + setCallback(5); + setup_makeAnnouncement(kCarRedSleeping, kPosition_9460, "TRA3006"); + break; + } break; case kActionDefault: @@ -1330,7 +1341,7 @@ IMPLEMENT_FUNCTION(32, Verges, function32) case 3: setCallback(4); - setup_function10(kCarGreenSleeping, kPosition_540, "TRA3004"); + setup_makeAnnouncement(kCarGreenSleeping, kPosition_540, "TRA3004"); break; case 4: @@ -1343,7 +1354,7 @@ IMPLEMENT_FUNCTION(32, Verges, function32) break; case 6: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1401,7 +1412,7 @@ IMPLEMENT_FUNCTION(33, Verges, function33) getSavePoints()->push(kEntityVerges, kEntityAbbot, kAction192054567); setCallback(6); - setup_function9("Tra3010"); + setup_walkBetweenCars("Tra3010"); break; case 6: @@ -1421,49 +1432,55 @@ IMPLEMENT_FUNCTION(34, Verges, function34) case kActionNone: if (getEntities()->isInBaggageCarEntrance(kEntityPlayer)) { setCallback(1); - setup_function13(false); + setup_baggageCar(false); break; } label_callback_1: if (ENTITY_PARAM(0, 4)) { setCallback(2); - setup_function31(); + setup_talkAboutMax(); break; } label_callback_2: if (ENTITY_PARAM(0, 3)) { setCallback(3); - setup_function17(); + setup_talkAboutPassengerList(); break; } label_callback_3: - TIME_CHECK_CALLBACK_1(kTime1971000, params->param1, 4, setup_function9, "Tra3001"); + if (Entity::timeCheckCallback(kTime1971000, params->param1, 4, "Tra3001", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars))) + break; label_callback_4: - TIME_CHECK_CALLBACK_1(kTime1998000, params->param2, 5, setup_function9, "Tra3010a"); + if (Entity::timeCheckCallback(kTime1998000, params->param2, 5, "Tra3010a", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars))) + break; label_callback_5: - TIME_CHECK_CALLBACK(kTime2016000, params->param3, 6, setup_function35); + if (Entity::timeCheckCallback(kTime2016000, params->param3, 6, WRAP_SETUP_FUNCTION(Verges, setup_organizeConcertInvitations))) + break; label_callback_6: - TIME_CHECK_CALLBACK_1(kTime2070000, params->param4, 7, setup_function9, "Tra3002"); + if (Entity::timeCheckCallback(kTime2070000, params->param4, 7, "Tra3002", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars))) + break; label_callback_7: - TIME_CHECK_CALLBACK_1(kTime2142000, params->param5, 8, setup_function9, "Tra3003"); + if (Entity::timeCheckCallback(kTime2142000, params->param5, 8, "Tra3003", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars))) + break; label_callback_8: - TIME_CHECK_CALLBACK_1(kTime2173500, params->param6, 9, setup_function30, "Tra3012"); + if (Entity::timeCheckCallback(kTime2173500, params->param6, 9, "Tra3012", WRAP_SETUP_FUNCTION_S(Verges, setup_function30))) + break; label_callback_9: - TIME_CHECK_CALLBACK(kTime2218500, params->param7, 10, setup_function32); + Entity::timeCheckCallback(kTime2218500, params->param7, 10, WRAP_SETUP_FUNCTION(Verges, setup_function32)); break; case kActionOpenDoor: setCallback(11); - setup_function13(savepoint.param.intValue < 106); + setup_baggageCar(savepoint.param.intValue < 106); break; case kActionCallback: @@ -1503,7 +1520,7 @@ label_callback_9: IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// -IMPLEMENT_FUNCTION(35, Verges, function35) +IMPLEMENT_FUNCTION(35, Verges, organizeConcertInvitations) switch (savepoint.action) { default: break; @@ -1525,7 +1542,7 @@ IMPLEMENT_FUNCTION(35, Verges, function35) case 2: setCallback(3); - setup_function15(kEntityMertens, "Tra3011A"); + setup_dialog(kEntityMertens, "Tra3011A"); break; case 3: @@ -1537,7 +1554,7 @@ IMPLEMENT_FUNCTION(35, Verges, function35) case 4: setCallback(5); - setup_function15(kEntityMertens, "Tra3011"); + setup_dialog(kEntityMertens, "Tra3011"); break; case 5: @@ -1548,7 +1565,7 @@ IMPLEMENT_FUNCTION(35, Verges, function35) break; case 6: - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1592,7 +1609,7 @@ IMPLEMENT_FUNCTION(37, Verges, chapter4Handler) case kActionNone: if (getEntities()->isInBaggageCarEntrance(kEntityPlayer)) { setCallback(1); - setup_function13(false); + setup_baggageCar(false); break; } @@ -1600,37 +1617,42 @@ label_callback_1: if (ENTITY_PARAM(0, 6)) { if (ENTITY_PARAM(0, 3)) { setCallback(2); - setup_function17(); + setup_talkAboutPassengerList(); break; } label_callback_2: - TIME_CHECK_CALLBACK_1(kTime2349000, params->param1, 3, setup_function9, "Tra1001"); + if (Entity::timeCheckCallback(kTime2349000, params->param1, 3, "Tra1001", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars))) + break; label_callback_3: - TIME_CHECK_CALLBACK_1(kTime2378700, params->param2, 4, setup_function9, "Tra4001"); + if (Entity::timeCheckCallback(kTime2378700, params->param2, 4, "Tra4001", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars))) + break; label_callback_4: - TIME_CHECK_CALLBACK_1(kTime2403000, params->param3, 5, setup_function9, "Tra1001A"); + if (Entity::timeCheckCallback(kTime2403000, params->param3, 5, "Tra1001A", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars))) + break; label_callback_5: - TIME_CHECK_CALLBACK_1(kTime2414700, params->param4, 6, setup_function9, "Tra4002"); + if (Entity::timeCheckCallback(kTime2414700, params->param4, 6, "Tra4002", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars))) + break; label_callback_6: - TIME_CHECK_CALLBACK_1(kTime2484000, params->param5, 7, setup_function9, "Tra4003"); + if (Entity::timeCheckCallback(kTime2484000, params->param5, 7, "Tra4003", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars))) + break; label_callback_7: - TIME_CHECK_CALLBACK_1(kTime2511000, params->param6, 8, setup_function9, "Tra4004"); + if (Entity::timeCheckCallback(kTime2511000, params->param6, 8, "Tra4004", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars))) + break; } label_callback_8: - TIME_CHECK_CALLBACK_1(kTime2538000, params->param7, 9, setup_function9, "Tra4005"); - + Entity::timeCheckCallback(kTime2538000, params->param7, 9, "Tra4005", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars)); break; case kActionOpenDoor: setCallback(10); - setup_function13(savepoint.param.intValue < 106); + setup_baggageCar(savepoint.param.intValue < 106); break; case kActionDefault: @@ -1675,7 +1697,7 @@ label_callback_8: IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// -IMPLEMENT_FUNCTION(38, Verges, function38) +IMPLEMENT_FUNCTION(38, Verges, resetState) switch (savepoint.action) { default: break; @@ -1781,14 +1803,14 @@ IMPLEMENT_FUNCTION(40, Verges, chapter5Handler) getAction()->playAnimation(kEventCathFreePassengers); getSavePoints()->pushAll(kEntityVerges, kActionProceedChapter5); getScenes()->loadSceneFromPosition(kCarRedSleeping, 40); - setup_function41(); + setup_askPassengersToStayInCompartments(); } break; } IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// -IMPLEMENT_FUNCTION(41, Verges, function41) +IMPLEMENT_FUNCTION(41, Verges, askPassengersToStayInCompartments) switch (savepoint.action) { default: break; @@ -1800,7 +1822,7 @@ IMPLEMENT_FUNCTION(41, Verges, function41) getData()->location = kLocationInsideCompartment; setCallback(1); - setup_function10(kCarRedSleeping, kPosition_2000, "Tra5001"); + setup_makeAnnouncement(kCarRedSleeping, kPosition_2000, "Tra5001"); break; case kActionCallback: @@ -1830,7 +1852,7 @@ IMPLEMENT_FUNCTION(41, Verges, function41) break; case 4: - setup_function42(); + setup_end(); break; } break; @@ -1838,7 +1860,7 @@ IMPLEMENT_FUNCTION(41, Verges, function41) IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// -IMPLEMENT_FUNCTION(42, Verges, function42) +IMPLEMENT_FUNCTION(42, Verges, end) if (savepoint.action == kActionDefault) getEntities()->clearSequences(kEntityVerges); IMPLEMENT_FUNCTION_END @@ -1869,7 +1891,7 @@ void Verges::talk(const SavePoint &savepoint, const char *sound1, const char *so case 2: setCallback(3); - setup_function15(kEntityCoudert, sound1); + setup_dialog(kEntityCoudert, sound1); break; case 3: @@ -1879,7 +1901,7 @@ void Verges::talk(const SavePoint &savepoint, const char *sound1, const char *so case 4: setCallback(5); - setup_function15(kEntityMertens, sound2); + setup_dialog(kEntityMertens, sound2); break; case 5: @@ -1887,7 +1909,7 @@ void Verges::talk(const SavePoint &savepoint, const char *sound1, const char *so break; case 6: - CALLBACK_ACTION(); + callbackAction(); break; } break; diff --git a/engines/lastexpress/entities/verges.h b/engines/lastexpress/entities/verges.h index 17a3c8ac40..93cc190d1e 100644 --- a/engines/lastexpress/entities/verges.h +++ b/engines/lastexpress/entities/verges.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_VERGES_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { @@ -88,11 +87,11 @@ public: */ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition) - DECLARE_FUNCTION_1(function9, const char *soundName) - DECLARE_FUNCTION_3(function10, CarIndex car, EntityPosition entityPosition, const char *soundName) + DECLARE_FUNCTION_1(walkBetweenCars, const char *soundName) + DECLARE_FUNCTION_3(makeAnnouncement, CarIndex car, EntityPosition entityPosition, const char *soundName) DECLARE_FUNCTION(function11) DECLARE_FUNCTION(function12) - DECLARE_FUNCTION_1(function13, bool) + DECLARE_FUNCTION_1(baggageCar, bool) /** * Updates parameter 2 using time value @@ -101,9 +100,9 @@ public: */ DECLARE_FUNCTION_1(updateFromTime, uint32 time) - DECLARE_FUNCTION_2(function15, EntityIndex entity, const char *soundName) - DECLARE_FUNCTION_3(function16, EntityIndex entityIndex, const char *soundName1, const char *soundName2) - DECLARE_FUNCTION(function17) + DECLARE_FUNCTION_2(dialog, EntityIndex entity, const char *soundName) + DECLARE_FUNCTION_3(dialog2, EntityIndex entityIndex, const char *soundName1, const char *soundName2) + DECLARE_FUNCTION(talkAboutPassengerList) /** * Setup Chapter 1 @@ -113,10 +112,10 @@ public: DECLARE_FUNCTION_NOSETUP(talkHarem) DECLARE_FUNCTION(talkPassengerList) DECLARE_FUNCTION(talkGendarmes) - DECLARE_FUNCTION(function22) + DECLARE_FUNCTION(askMertensToRelayAugustInvitation) DECLARE_FUNCTION(function23) DECLARE_FUNCTION(policeGettingOffTrain) - DECLARE_FUNCTION(function25) + DECLARE_FUNCTION(policeSearch) /** * Handle Chapter 1 events @@ -139,11 +138,11 @@ public: DECLARE_FUNCTION(chapter3) DECLARE_FUNCTION_1(function30, const char *soundName) - DECLARE_FUNCTION(function31) + DECLARE_FUNCTION(talkAboutMax) DECLARE_FUNCTION(function32) DECLARE_FUNCTION(function33) DECLARE_FUNCTION(function34) - DECLARE_FUNCTION(function35) + DECLARE_FUNCTION(organizeConcertInvitations) /** * Setup Chapter 4 @@ -155,7 +154,7 @@ public: */ DECLARE_FUNCTION(chapter4Handler) - DECLARE_FUNCTION(function38) + DECLARE_FUNCTION(resetState) /** * Setup Chapter 5 @@ -167,8 +166,8 @@ public: */ DECLARE_FUNCTION(chapter5Handler) - DECLARE_FUNCTION(function41) - DECLARE_FUNCTION(function42) + DECLARE_FUNCTION(askPassengersToStayInCompartments) + DECLARE_FUNCTION(end) private: void talk(const SavePoint &savepoint, const char *sound1, const char *sound2); diff --git a/engines/lastexpress/entities/vesna.cpp b/engines/lastexpress/entities/vesna.cpp index 7a1f1d3195..f29bce8b2b 100644 --- a/engines/lastexpress/entities/vesna.cpp +++ b/engines/lastexpress/entities/vesna.cpp @@ -32,10 +32,7 @@ #include "lastexpress/game/scenes.h" #include "lastexpress/game/state.h" -#include "lastexpress/sound/sound.h" - #include "lastexpress/lastexpress.h" -#include "lastexpress/helpers.h" namespace LastExpress { @@ -134,7 +131,7 @@ IMPLEMENT_FUNCTION_II(7, Vesna, updateEntity2, CarIndex, EntityPosition) break; case kAction123668192: - CALLBACK_ACTION(); + callbackAction(); break; } IMPLEMENT_FUNCTION_END @@ -165,7 +162,8 @@ IMPLEMENT_FUNCTION(11, Vesna, function11) case kActionNone: if (parameters->param3) { - UPDATE_PARAM(parameters->param7, getState()->timeTicks, 75); + if (!Entity::updateParameter(parameters->param7, getState()->timeTicks, 75)) + break; parameters->param2 = 1; parameters->param3 = 0; @@ -245,7 +243,7 @@ IMPLEMENT_FUNCTION(11, Vesna, function11) case kAction55996766: case kAction101687594: - CALLBACK_ACTION(); + callbackAction(); break; } IMPLEMENT_FUNCTION_END @@ -257,7 +255,7 @@ IMPLEMENT_FUNCTION(12, Vesna, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler); + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(Vesna, setup_chapter1Handler)); break; case kActionDefault: @@ -458,7 +456,7 @@ IMPLEMENT_FUNCTION(18, Vesna, function18) getData()->location = kLocationInsideCompartment; getEntities()->clearSequences(kEntityVesna); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -514,7 +512,8 @@ IMPLEMENT_FUNCTION(20, Vesna, chapter3Handler) } if (parameters->param2) { - UPDATE_PARAM(parameters->param8, getState()->timeTicks, 75); + if (!Entity::updateParameter(parameters->param8, getState()->timeTicks, 75)) + break; parameters->param1 = 1; parameters->param2 = 0; @@ -711,7 +710,7 @@ IMPLEMENT_FUNCTION(21, Vesna, function21) getData()->location = kLocationInsideCompartment; getEntities()->clearSequences(kEntityVesna); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -1083,13 +1082,14 @@ IMPLEMENT_FUNCTION(30, Vesna, function30) case kActionNone: if (!params->param1) { - UPDATE_PARAM_PROC(params->param3, getState()->timeTicks, 120) + if (Entity::updateParameter(params->param3, getState()->timeTicks, 120)) { getSound()->playSound(kEntityVesna, "Ves50001", kFlagDefault); params->param1 = 1; - UPDATE_PARAM_PROC_END + } } - UPDATE_PARAM(params->param4, getState()->timeTicks, 180); + if (!Entity::updateParameter(params->param4, getState()->timeTicks, 180)) + break; setCallback(1); setup_savegame(kSavegameTypeEvent, kEventCathVesnaTrainTopKilled); diff --git a/engines/lastexpress/entities/vesna.h b/engines/lastexpress/entities/vesna.h index a09664096d..025d45882e 100644 --- a/engines/lastexpress/entities/vesna.h +++ b/engines/lastexpress/entities/vesna.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_VESNA_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/entities/yasmin.cpp b/engines/lastexpress/entities/yasmin.cpp index 45e5e11568..1d280f51e0 100644 --- a/engines/lastexpress/entities/yasmin.cpp +++ b/engines/lastexpress/entities/yasmin.cpp @@ -28,10 +28,8 @@ #include "lastexpress/game/savepoint.h" #include "lastexpress/game/state.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/lastexpress.h" -#include "lastexpress/helpers.h" namespace LastExpress { @@ -132,7 +130,7 @@ IMPLEMENT_FUNCTION(6, Yasmin, function6) getData()->location = kLocationInsideCompartment; getEntities()->clearSequences(kEntityYasmin); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -172,7 +170,7 @@ IMPLEMENT_FUNCTION(7, Yasmin, function7) getData()->location = kLocationInsideCompartment; getEntities()->clearSequences(kEntityYasmin); - CALLBACK_ACTION(); + callbackAction(); break; } break; @@ -186,7 +184,7 @@ IMPLEMENT_FUNCTION(8, Yasmin, chapter1) break; case kActionNone: - TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler); + Entity::timeCheck(kTimeChapter1, params->param1, WRAP_SETUP_FUNCTION(Yasmin, setup_chapter1Handler)); break; case kActionDefault: @@ -204,12 +202,22 @@ IMPLEMENT_FUNCTION(9, Yasmin, chapter1Handler) break; case kActionNone: - TIME_CHECK_CALLBACK(kTime1093500, params->param1, 1, setup_function6); - TIME_CHECK_CALLBACK(kTime1161000, params->param2, 3, setup_function7); - TIME_CHECK_PLAYSOUND_UPDATEPOSITION(kTime1162800, params->param3, 4, "Har1102", kPosition_4070); - TIME_CHECK_CALLBACK_1(kTime1165500, params->param4, 5, setup_playSound, "Har1104"); - TIME_CHECK_CALLBACK_1(kTime1174500, params->param5, 6, setup_playSound, "Har1106"); - TIME_CHECK_CALLBACK(kTime1183500, params->param6, 7, setup_function6); + if (Entity::timeCheckCallback(kTime1093500, params->param1, 1, WRAP_SETUP_FUNCTION(Yasmin, setup_function6))) + break; + + if (Entity::timeCheckCallback(kTime1161000, params->param2, 3, WRAP_SETUP_FUNCTION(Yasmin, setup_function7))) + break; + + if (Entity::timeCheckPlaySoundUpdatePosition(kTime1162800, params->param3, 4, "Har1102", kPosition_4070)) + break; + + if (Entity::timeCheckCallback(kTime1165500, params->param4, 5, "Har1104", WRAP_SETUP_FUNCTION_S(Yasmin, setup_playSound))) + break; + + if (Entity::timeCheckCallback(kTime1174500, params->param5, 6, "Har1106", WRAP_SETUP_FUNCTION_S(Yasmin, setup_playSound))) + break; + + Entity::timeCheckCallback(kTime1183500, params->param6, 7, WRAP_SETUP_FUNCTION(Yasmin, setup_function6)); break; case kActionCallback: @@ -224,23 +232,27 @@ IMPLEMENT_FUNCTION(9, Yasmin, chapter1Handler) break; case 2: - TIME_CHECK_CALLBACK(kTime1161000, params->param2, 3, setup_function7); + if (Entity::timeCheckCallback(kTime1161000, params->param2, 3, WRAP_SETUP_FUNCTION(Yasmin, setup_function7))) + break; // Fallback to case 3 case 3: - TIME_CHECK_PLAYSOUND_UPDATEPOSITION(kTime1162800, params->param3, 4, "Har1102", kPosition_4070); + if (Entity::timeCheckPlaySoundUpdatePosition(kTime1162800, params->param3, 4, "Har1102", kPosition_4070)) + break; // Fallback to case 4 case 4: - TIME_CHECK_CALLBACK_1(kTime1165500, params->param4, 5, setup_playSound, "Har1104"); + if (Entity::timeCheckCallback(kTime1165500, params->param4, 5, "Har1104", WRAP_SETUP_FUNCTION_S(Yasmin, setup_playSound))) + break; // Fallback to case 5 case 5: - TIME_CHECK_CALLBACK_1(kTime1174500, params->param5, 6, setup_playSound, "Har1106"); + if (Entity::timeCheckCallback(kTime1174500, params->param5, 6, "Har1106", WRAP_SETUP_FUNCTION_S(Yasmin, setup_playSound))) + break; // Fallback to case 6 case 6: - TIME_CHECK_CALLBACK(kTime1183500, params->param6, 7, setup_function6); + Entity::timeCheckCallback(kTime1183500, params->param6, 7, WRAP_SETUP_FUNCTION(Yasmin, setup_function6)); break; } break; @@ -281,7 +293,8 @@ IMPLEMENT_FUNCTION(12, Yasmin, chapter2Handler) break; case kActionNone: - TIME_CHECK_CALLBACK(kTime1759500, params->param1, 1, setup_function7); + if (Entity::timeCheckCallback(kTime1759500, params->param1, 1, WRAP_SETUP_FUNCTION(Yasmin, setup_function7))) + break; if (getState()->time > kTime1800000 && !params->param2) { params->param2 = 1; @@ -334,9 +347,13 @@ IMPLEMENT_FUNCTION(14, Yasmin, chapter3Handler) break; case kActionNone: - TIME_CHECK_CALLBACK(kTime2062800, params->param1, 1, setup_function6); - TIME_CHECK_CALLBACK(kTime2106000, params->param2, 2, setup_function7); - TIME_CHECK_CALLBACK(kTime2160000, params->param3, 3, setup_function6); + if (Entity::timeCheckCallback(kTime2062800, params->param1, 1, WRAP_SETUP_FUNCTION(Yasmin, setup_function6))) + break; + + if (Entity::timeCheckCallback(kTime2106000, params->param2, 2, WRAP_SETUP_FUNCTION(Yasmin, setup_function7))) + break; + + Entity::timeCheckCallback(kTime2160000, params->param3, 3, WRAP_SETUP_FUNCTION(Yasmin, setup_function6)); break; case kActionCallback: @@ -345,11 +362,12 @@ IMPLEMENT_FUNCTION(14, Yasmin, chapter3Handler) break; case 1: - TIME_CHECK_CALLBACK(kTime2106000, params->param2, 2, setup_function7); + if (Entity::timeCheckCallback(kTime2106000, params->param2, 2, WRAP_SETUP_FUNCTION(Yasmin, setup_function7))) + break; // Fallback to case 2 case 2: - TIME_CHECK_CALLBACK(kTime2160000, params->param3, 3, setup_function6); + Entity::timeCheckCallback(kTime2160000, params->param3, 3, WRAP_SETUP_FUNCTION(Yasmin, setup_function6)); break; } break; @@ -381,8 +399,10 @@ IMPLEMENT_FUNCTION(16, Yasmin, chapter4Handler) break; case kActionNone: - TIME_CHECK_CALLBACK(kTime2457000, params->param1, 1, setup_function7); - TIME_CHECK_CALLBACK(kTime2479500, params->param2, 3, setup_function6); + if (Entity::timeCheckCallback(kTime2457000, params->param1, 1, WRAP_SETUP_FUNCTION(Yasmin, setup_function7))) + break; + + Entity::timeCheckCallback(kTime2479500, params->param2, 3, WRAP_SETUP_FUNCTION(Yasmin, setup_function6)); break; case kActionCallback: @@ -397,7 +417,7 @@ IMPLEMENT_FUNCTION(16, Yasmin, chapter4Handler) break; case 2: - TIME_CHECK_CALLBACK(kTime2479500, params->param2, 3, setup_function6); + Entity::timeCheckCallback(kTime2479500, params->param2, 3, WRAP_SETUP_FUNCTION(Yasmin, setup_function6)); break; } break; @@ -445,7 +465,9 @@ IMPLEMENT_FUNCTION(20, Yasmin, function20) break; case kActionNone: - UPDATE_PARAM(params->param1, getState()->time, 2700); + if (!Entity::updateParameter(params->param1, getState()->time, 2700)) + break; + setup_function21(); break; @@ -472,7 +494,7 @@ IMPLEMENT_FUNCTION(21, Yasmin, function21) case kActionNone: case kActionDefault: if (getEntities()->updateEntity(kEntityYasmin, (CarIndex)params->param1, (EntityPosition)params->param2)) - CALLBACK_ACTION(); + callbackAction(); break; case kActionExcuseMeCath: diff --git a/engines/lastexpress/entities/yasmin.h b/engines/lastexpress/entities/yasmin.h index e943a8b158..b1413f5c2f 100644 --- a/engines/lastexpress/entities/yasmin.h +++ b/engines/lastexpress/entities/yasmin.h @@ -24,7 +24,6 @@ #define LASTEXPRESS_YASMIN_H #include "lastexpress/entities/entity.h" -#include "lastexpress/entities/entity_intern.h" namespace LastExpress { diff --git a/engines/lastexpress/fight/fight.cpp b/engines/lastexpress/fight/fight.cpp index b832d46a60..49a9b85657 100644 --- a/engines/lastexpress/fight/fight.cpp +++ b/engines/lastexpress/fight/fight.cpp @@ -31,6 +31,7 @@ #include "lastexpress/data/cursor.h" #include "lastexpress/data/sequence.h" +#include "lastexpress/game/entities.h" #include "lastexpress/game/inventory.h" #include "lastexpress/game/logic.h" #include "lastexpress/game/object.h" @@ -40,7 +41,6 @@ #include "lastexpress/sound/queue.h" #include "lastexpress/graphics.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" #include "lastexpress/resource.h" @@ -53,6 +53,8 @@ Fight::FightData::FightData() { index = 0; isFightRunning = false; + + memset(&sequences, 0, sizeof(sequences)); } Fight::FightData::~FightData() { @@ -399,6 +401,9 @@ end_load: } void Fight::setOpponents() { + if (!_data) + error("[Fight::setOpponents] Data not initialized"); + _data->player->setOpponent(_data->opponent); _data->opponent->setOpponent(_data->player); diff --git a/engines/lastexpress/fight/fighter.cpp b/engines/lastexpress/fight/fighter.cpp index bae7728a2b..4b1cddabd4 100644 --- a/engines/lastexpress/fight/fighter.cpp +++ b/engines/lastexpress/fight/fighter.cpp @@ -53,20 +53,20 @@ Fighter::Fighter(LastExpressEngine *engine) : _engine(engine) { } Fighter::~Fighter() { - clearSequences(); -} - -////////////////////////////////////////////////////////////////////////// -// Cleanup -////////////////////////////////////////////////////////////////////////// -void Fighter::clearSequences() { // The original game resets the function pointers to default values, just before deleting the struct getScenes()->removeAndRedraw(&_frame, false); // Free sequences - for (int i = 0; i < (int)_sequences.size(); i++) + for (uint i = 0; i < _sequences.size(); i++) SAFE_DELETE(_sequences[i]); + + // Zero-out passed pointers + _sequence = NULL; + _opponent = NULL; + _fight = NULL; + + _engine = NULL; } ////////////////////////////////////////////////////////////////////////// @@ -113,6 +113,9 @@ void Fighter::draw() { // Processing ////////////////////////////////////////////////////////////////////////// void Fighter::process() { + if (!_fight) + error("[Fighter::handleAction] Fighter not initialized properly"); + if (!_sequence) { if (_frame) { getScenes()->removeFromQueue(_frame); @@ -188,6 +191,9 @@ void Fighter::process() { // Default actions ////////////////////////////////////////////////////////////////////////// void Fighter::handleAction(FightAction action) { + if (!_opponent || !_fight) + error("[Fighter::handleAction] Fighter not initialized properly"); + switch (action) { default: return; @@ -243,7 +249,10 @@ void Opponent::update() { // Helpers ////////////////////////////////////////////////////////////////////////// bool Fighter::checkFrame(uint32 val) { - return (_frame->getInfo()->field_33 & val); + if (!_frame) + error("[Fighter::checkFrame] Invalid current frame"); + + return (bool)(_frame->getInfo()->field_33 & val); } } // End of namespace LastExpress diff --git a/engines/lastexpress/fight/fighter.h b/engines/lastexpress/fight/fighter.h index e37fe49d86..dad95af186 100644 --- a/engines/lastexpress/fight/fighter.h +++ b/engines/lastexpress/fight/fighter.h @@ -99,9 +99,6 @@ protected: void draw(); void process(); - // Cleanup - void clearSequences(); - // Helpers bool checkFrame(uint32 val); }; diff --git a/engines/lastexpress/game/action.cpp b/engines/lastexpress/game/action.cpp index 98d74dd1a7..796abf2ce7 100644 --- a/engines/lastexpress/game/action.cpp +++ b/engines/lastexpress/game/action.cpp @@ -29,7 +29,6 @@ #include "lastexpress/entities/abbot.h" #include "lastexpress/entities/anna.h" -#include "lastexpress/entities/entity.h" #include "lastexpress/game/beetle.h" #include "lastexpress/game/entities.h" @@ -42,9 +41,7 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" #include "lastexpress/resource.h" @@ -332,6 +329,22 @@ static const struct { {"8042A", 600} }; +template<class Arg, class Res, class T> +class Functor1MemConst : public Common::Functor1<Arg, Res> { +public: + typedef Res (T::*FuncType)(Arg) const; + + Functor1MemConst(T *t, const FuncType &func) : _t(t), _func(func) {} + + bool isValid() const { return _func != 0 && _t != 0; } + Res operator()(Arg v1) const { + return (_t->*_func)(v1); + } +private: + mutable T *_t; + const FuncType _func; +}; + Action::Action(LastExpressEngine *engine) : _engine(engine) { ADD_ACTION(dummy); ADD_ACTION(inventory); @@ -381,7 +394,7 @@ Action::Action(LastExpressEngine *engine) : _engine(engine) { } Action::~Action() { - for (int i = 0; i < (int)_actions.size(); i++) + for (uint i = 0; i < _actions.size(); i++) SAFE_DELETE(_actions[i]); _actions.clear(); @@ -407,9 +420,7 @@ SceneIndex Action::processHotspot(const SceneHotspot &hotspot) { ////////////////////////////////////////////////////////////////////////// // Action 0 IMPLEMENT_ACTION(dummy) - warning("[Action::action_dummy] Dummy action function called (hotspot action: %d)", hotspot.action); - - return kSceneInvalid; + error("[Action::action_dummy] Dummy action function called (hotspot action: %d)", hotspot.action); } ////////////////////////////////////////////////////////////////////////// @@ -1742,14 +1753,14 @@ CursorStyle Action::getCursor(const SceneHotspot &hotspot) const { return kCursorBackward; case SceneHotspot::kActionKnockOnDoor: - warning("================================= DOOR %03d =================================", object); + debugC(2, kLastExpressDebugScenes, "================================= DOOR %03d =================================", object); if (object >= kObjectMax) return kCursorNormal; else return (CursorStyle)getObjects()->get(object).cursor; case SceneHotspot::kAction12: - warning("================================= OBJECT %03d =================================", object); + debugC(2, kLastExpressDebugScenes, "================================= OBJECT %03d =================================", object); if (object >= kObjectMax) return kCursorNormal; @@ -1759,7 +1770,7 @@ CursorStyle Action::getCursor(const SceneHotspot &hotspot) const { return kCursorNormal; case SceneHotspot::kActionPickItem: - warning("================================= ITEM %03d =================================", object); + debugC(2, kLastExpressDebugScenes, "================================= ITEM %03d =================================", object); if (object >= kObjectCompartmentA) return kCursorNormal; @@ -1770,7 +1781,7 @@ CursorStyle Action::getCursor(const SceneHotspot &hotspot) const { return kCursorNormal; case SceneHotspot::kActionDropItem: - warning("================================= ITEM %03d =================================", object); + debugC(2, kLastExpressDebugScenes, "================================= ITEM %03d =================================", object); if (object >= kObjectCompartmentA) return kCursorNormal; @@ -1887,7 +1898,7 @@ LABEL_KEY: // Handle compartment action case SceneHotspot::kActionCompartment: case SceneHotspot::kActionExitCompartment: - warning("================================= DOOR %03d =================================", object); + debugC(2, kLastExpressDebugScenes, "================================= DOOR %03d =================================", object); if (object >= kObjectMax) return kCursorNormal; diff --git a/engines/lastexpress/game/beetle.cpp b/engines/lastexpress/game/beetle.cpp index ab707ddae9..d7a369ba40 100644 --- a/engines/lastexpress/game/beetle.cpp +++ b/engines/lastexpress/game/beetle.cpp @@ -27,7 +27,6 @@ #include "lastexpress/game/scenes.h" #include "lastexpress/game/state.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" #include "lastexpress/resource.h" @@ -95,7 +94,7 @@ void Beetle::load() { // Check that all sequences are loaded properly _data->isLoaded = true; - for (int i = 0; i < (int)_data->sequences.size(); i++) { + for (uint i = 0; i < _data->sequences.size(); i++) { if (!_data->sequences[i]->isLoaded()) { _data->isLoaded = false; break; @@ -337,26 +336,13 @@ void Beetle::drawUpdate() { } } -#define INVERT_Y() \ - switch (_data->indexes[_data->offset]) { \ - default: \ - break; \ - case 24: \ - case 25: \ - case 26: \ - case 27: \ - case 28: \ - _data->coordY = -_data->coordY; \ - break; \ - } - // Invert direction - INVERT_Y(); + invertDirection(); SequenceFrame *frame = new SequenceFrame(_data->currentSequence, (uint16)_data->currentFrame); updateFrame(frame); - INVERT_Y(); + invertDirection(); getScenes()->addToQueue(frame); @@ -364,6 +350,24 @@ void Beetle::drawUpdate() { _data->frame = frame; } +void Beetle::invertDirection() { + if (!_data) + error("[Beetle::invertDirection] Sequences have not been loaded"); + + switch (_data->indexes[_data->offset]) { + default: + break; + + case 24: + case 25: + case 26: + case 27: + case 28: + _data->coordY = -_data->coordY; + break; + } +} + void Beetle::move() { if (!_data) error("[Beetle::move] Sequences have not been loaded"); diff --git a/engines/lastexpress/game/beetle.h b/engines/lastexpress/game/beetle.h index d3c47f39e5..034ebbd557 100644 --- a/engines/lastexpress/game/beetle.h +++ b/engines/lastexpress/game/beetle.h @@ -111,6 +111,7 @@ private: void updateFrame(SequenceFrame *frame) const; void updateData(uint32 index); void drawUpdate(); + void invertDirection(); }; } // End of namespace LastExpress diff --git a/engines/lastexpress/game/entities.cpp b/engines/lastexpress/game/entities.cpp index f27087a609..fafbd7cb64 100644 --- a/engines/lastexpress/game/entities.cpp +++ b/engines/lastexpress/game/entities.cpp @@ -27,8 +27,6 @@ #include "lastexpress/data/sequence.h" // Entities -#include "lastexpress/entities/entity.h" - #include "lastexpress/entities/abbot.h" #include "lastexpress/entities/alexei.h" #include "lastexpress/entities/alouan.h" @@ -71,10 +69,8 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/graphics.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" #include "lastexpress/resource.h" @@ -185,7 +181,7 @@ Entities::Entities(LastExpressEngine *engine) : _engine(engine) { Entities::~Entities() { SAFE_DELETE(_header); - for (int i = 0; i < (int)_entities.size(); i++) + for (uint i = 0; i < _entities.size(); i++) SAFE_DELETE(_entities[i]); _entities.clear(); @@ -673,11 +669,12 @@ void Entities::executeCallbacks() { ////////////////////////////////////////////////////////////////////////// // Processing ////////////////////////////////////////////////////////////////////////// -#define INCREMENT_DIRECTION_COUNTER() { \ - data->doProcessEntity = false; \ - if (data->direction == kDirectionRight || (data->direction == kDirectionSwitch && data->directionSwitch == kDirectionRight)) \ - ++data->field_4A1; \ - } +void Entities::incrementDirectionCounter(EntityData::EntityCallData *data) const { + data->doProcessEntity = false; + + if (data->direction == kDirectionRight || (data->direction == kDirectionSwitch && data->directionSwitch == kDirectionRight)) + ++data->field_4A1; +} void Entities::processEntity(EntityIndex entityIndex) { EntityData::EntityCallData *data = getData(entityIndex); @@ -696,7 +693,7 @@ void Entities::processEntity(EntityIndex entityIndex) { getScenes()->removeAndRedraw(&data->frame, false); getScenes()->removeAndRedraw(&data->frame1, false); - INCREMENT_DIRECTION_COUNTER(); + incrementDirectionCounter(data); return; } @@ -726,7 +723,7 @@ label_nosequence: processFrame(entityIndex, false, true); if (!getFlags()->flag_entities_0 && !data->doProcessEntity) { - INCREMENT_DIRECTION_COUNTER(); + incrementDirectionCounter(data); return; } } else { @@ -744,7 +741,7 @@ label_nosequence: data->position = 0; } - INCREMENT_DIRECTION_COUNTER(); + incrementDirectionCounter(data); } return; } @@ -754,46 +751,44 @@ label_nosequence: if (data->frame->getInfo()->field_30 > (data->field_49B + 1) || (data->direction == kDirectionLeft && data->sequence->count() == 1)) { ++data->field_49B; - } else { - if (data->frame->getInfo()->field_30 > data->field_49B && !data->frame->getInfo()->keepPreviousFrame) { - ++data->field_49B; - } else { - if (data->frame->getInfo()->keepPreviousFrame == 1) - keepPreviousFrame = true; - - // Increment current frame - ++data->currentFrame; + } else if (data->frame->getInfo()->field_30 <= data->field_49B || data->frame->getInfo()->keepPreviousFrame) { + if (data->frame->getInfo()->keepPreviousFrame == 1) + keepPreviousFrame = true; - if (data->currentFrame > (int16)(data->sequence->count() - 1) || (data->field_4A9 && checkSequenceFromPosition(entityIndex))) { + // Increment current frame + ++data->currentFrame; - if (data->direction == kDirectionLeft) { - data->currentFrame = 0; - } else { - keepPreviousFrame = true; - drawNextSequence(entityIndex); + if (data->currentFrame > (int16)(data->sequence->count() - 1) || (data->field_4A9 && checkSequenceFromPosition(entityIndex))) { - if (getFlags()->flag_entities_0 || data->doProcessEntity) - return; + if (data->direction == kDirectionLeft) { + data->currentFrame = 0; + } else { + keepPreviousFrame = true; + drawNextSequence(entityIndex); - if (!data->sequence2) { - updateEntityPosition(entityIndex); - data->doProcessEntity = false; - return; - } + if (getFlags()->flag_entities_0 || data->doProcessEntity) + return; - copySequenceData(entityIndex); + if (!data->sequence2) { + updateEntityPosition(entityIndex); + data->doProcessEntity = false; + return; } + copySequenceData(entityIndex); } - processFrame(entityIndex, keepPreviousFrame, false); - - if (getFlags()->flag_entities_0 || data->doProcessEntity) - return; } + + processFrame(entityIndex, keepPreviousFrame, false); + + if (getFlags()->flag_entities_0 || data->doProcessEntity) + return; + } else { + ++data->field_49B; } - INCREMENT_DIRECTION_COUNTER(); + incrementDirectionCounter(data); } void Entities::computeCurrentFrame(EntityIndex entityIndex) const { @@ -2283,7 +2278,7 @@ label_process_entity: if (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingUp)) { getSavePoints()->push(kEntityPlayer, entity, kActionExcuseMeCath); - } else if (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingDown) || getScenes()->checkCurrentPosition(false)){ + } else if (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingDown) || getScenes()->checkCurrentPosition(false)) { getSavePoints()->push(kEntityPlayer, entity, kActionExcuseMe); if (getScenes()->checkCurrentPosition(false)) diff --git a/engines/lastexpress/game/entities.h b/engines/lastexpress/game/entities.h index eb5eae461f..81aed627aa 100644 --- a/engines/lastexpress/game/entities.h +++ b/engines/lastexpress/game/entities.h @@ -344,6 +344,7 @@ private: uint _positions[_positionsCount]; void executeCallbacks(); + void incrementDirectionCounter(EntityData::EntityCallData *data) const; void processEntity(EntityIndex entity); void drawSequence(EntityIndex entity, const char *sequence, EntityDirection direction) const; diff --git a/engines/lastexpress/game/inventory.cpp b/engines/lastexpress/game/inventory.cpp index e417b1ec0d..11e7369ee1 100644 --- a/engines/lastexpress/game/inventory.cpp +++ b/engines/lastexpress/game/inventory.cpp @@ -26,17 +26,17 @@ #include "lastexpress/data/scene.h" #include "lastexpress/data/snd.h" +#include "lastexpress/game/entities.h" #include "lastexpress/game/logic.h" +#include "lastexpress/game/savegame.h" #include "lastexpress/game/scenes.h" #include "lastexpress/game/state.h" #include "lastexpress/menu/menu.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/graphics.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" #include "lastexpress/resource.h" @@ -44,7 +44,7 @@ namespace LastExpress { Inventory::Inventory(LastExpressEngine *engine) : _engine(engine), _selectedItem(kItemNone), _highlightedItemIndex(0), _itemsShown(0), - _showingHourGlass(false), _blinkingEgg(false), _blinkingTime(0), _blinkingInterval(_defaultBlinkingInterval), _blinkingBrightness(1), + _showingHourGlass(false), _blinkingDirection(1), _blinkingBrightness(0), _useMagnifier(false), _portraitHighlighted(false), _isOpened(false), _eggHightlighted(false), _itemScene(NULL) { //_inventoryRect = Common::Rect(0, 0, 32, 32); @@ -162,13 +162,11 @@ void Inventory::handleMouseEvent(const Common::Event &ev) { getMenu()->show(true, kSavegameTypeIndex, 0); - } else if (ev.type == Common::EVENT_RBUTTONDOWN) { - if (getGlobalTimer()) { - if (getSoundQueue()->isBuffered("TIMER")) - getSoundQueue()->removeFromQueue("TIMER"); + } else if (ev.type == Common::EVENT_RBUTTONDOWN && getGlobalTimer()) { + if (getSoundQueue()->isBuffered("TIMER")) + getSoundQueue()->removeFromQueue("TIMER"); - setGlobalTimer(900); - } + setGlobalTimer(900); } } @@ -180,7 +178,7 @@ void Inventory::handleMouseEvent(const Common::Event &ev) { if (_highlightedItemIndex) drawHighlight(_highlightedItemIndex, true); } else { - // The user released the mouse button, we need to update the inventory state (clear hightlight and items) + // The user released the mouse button, we need to update the inventory state (clear highlight and items) drawItem((CursorStyle)getProgress().portrait, 0, 0, 1); _engine->getGraphicsManager()->clear(GraphicsManager::kBackgroundInventory, Common::Rect(0, 44, 32, (int16)(40 * _itemsShown + 40))); _isOpened = false; @@ -226,12 +224,11 @@ void Inventory::handleMouseEvent(const Common::Event &ev) { if (getFlags()->mouseLeftPressed) { if (getState()->sceneUseBackup) { - if (getState()->sceneBackup2 - && getFirstExaminableItem() == _selectedItem) { - SceneIndex sceneIndex = getState()->sceneBackup2; - getState()->sceneBackup2 = kSceneNone; + if (getState()->sceneBackup2 && getFirstExaminableItem() == _selectedItem) { + SceneIndex sceneIndex = getState()->sceneBackup2; + getState()->sceneBackup2 = kSceneNone; - getScenes()->loadScene(sceneIndex); + getScenes()->loadScene(sceneIndex); } } else { getState()->sceneBackup = getState()->scene; @@ -261,7 +258,7 @@ void Inventory::handleMouseEvent(const Common::Event &ev) { // Change item highlight on list if (getFlags()->mouseLeftPressed) { - uint32 index = ev.mouse.y / 40; + uint32 index = (uint16)ev.mouse.y / 40; if (_highlightedItemIndex && _highlightedItemIndex != index) drawHighlight(_highlightedItemIndex, true); @@ -418,12 +415,12 @@ void Inventory::show() { drawEgg(); } -void Inventory::setPortrait(InventoryItem item) { +void Inventory::setPortrait(InventoryItem item) const { getProgress().portrait = item; drawItem((CursorStyle)getProgress().portrait, 0, 0); } -void Inventory::showHourGlass(){ +void Inventory::showHourGlass() const { if (!getMenu()->isShown()) drawItem(kCursorHourGlass, 608, 448); @@ -613,7 +610,7 @@ void Inventory::examine(InventoryItem item) { } } -void Inventory::drawEgg() { +void Inventory::drawEgg() const { if (!getMenu()->isShown()) drawItem((CursorStyle)(getMenu()->getGameId() + 39), 608, 448, _eggHightlighted ? 0 : 1); @@ -621,40 +618,51 @@ void Inventory::drawEgg() { } // Blinking egg: we need to blink the egg for delta time, with the blinking getting faster until it's always lit. -void Inventory::drawBlinkingEgg() { +void Inventory::drawBlinkingEgg(uint ticks) { + uint globalTimer = (uint)getGlobalTimer(); + uint timerValue = (getProgress().jacket == kJacketGreen) ? 450 : 225; - warning("[Inventory::drawBlinkingEgg] Blinking not implemented"); + if (globalTimer == timerValue || globalTimer == 900) { + _blinkingBrightness = 0; + _blinkingDirection = 1; + } - //// TODO show egg (with or without mouseover) + globalTimer = globalTimer <= ticks ? 0 : globalTimer - ticks; + setGlobalTimer(globalTimer); - //// Play timer sound - //if (getGlobalTimer() < 90) { - // if (getGlobalTimer() + ticks >= 90) - // getSound()->playSoundWithSubtitles("TIMER.SND", 50331664, kEntityPlayer); + if (getFlags()->flag_0 + || (globalTimer % 5) == 0 + || (globalTimer <= 500 && (globalTimer % ((globalTimer + 100) / 100)) == 0)) + blinkEgg(); - // if (getSoundQueue()->isBuffered("TIMER")) - // setGlobalTimer(0); - //} + if (globalTimer < 90) { + if ((globalTimer + ticks) >= 90) + getSound()->playSoundWithSubtitles("TIMER", (SoundFlag)(kFlagType13|kFlagDefault), kEntityPlayer); - //// Restore egg to standard brightness - //if (!getGlobalTimer()) { - // - //} + if (!getSoundQueue()->isBuffered("TIMER")) + setGlobalTimer(0); + } + if (globalTimer == 0) { + drawItem((CursorStyle)(getMenu()->getGameId() + 39), 608, 448, _menuEggRect.contains(getCoords()) ? 1 : -1); - //drawItem(608, 448, getMenu()->getGameId() + 39, _blinkingBrightness) + askForRedraw(); - //// TODO if delta time > _blinkingInterval, update egg & ask for redraw then adjust blinking time and remaining time - // + getSaveLoad()->saveGame(kSavegameTypeAuto, kEntityChapters, 0); + } +} - //// Reset values and stop blinking - //if (_blinkingTime == 0) - // blinkEgg(false); +void Inventory::blinkEgg() { + drawItem((CursorStyle)(getMenu()->getGameId() + 39), 608, 448, (_blinkingBrightness == 0) ? -1 : (int16)_blinkingBrightness); askForRedraw(); + + _blinkingBrightness += _blinkingDirection; + if (_blinkingBrightness == 0 || _blinkingBrightness == 3) + _blinkingDirection = -_blinkingDirection; } -void Inventory::drawItem(CursorStyle id, uint16 x, uint16 y, int16 brightnessIndex) { +void Inventory::drawItem(CursorStyle id, uint16 x, uint16 y, int16 brightnessIndex) const { Icon icon(id); icon.setPosition(x, y); @@ -678,7 +686,7 @@ void Inventory::drawSelectedItem() { } } -void Inventory::clearSelectedItem() { +void Inventory::clearSelectedItem() const { _engine->getGraphicsManager()->clear(GraphicsManager::kBackgroundInventory, Common::Rect(44, 0, 44 + 32, 32)); } @@ -733,7 +741,7 @@ void Inventory::drawHighlight(uint32 currentIndex, bool reset) { } } -uint32 Inventory::getItemIndex(uint32 currentIndex) { +uint32 Inventory::getItemIndex(uint32 currentIndex) const { uint32 count = 0; for (uint32 i = 1; i < ARRAYSIZE(_entries); i++) { diff --git a/engines/lastexpress/game/inventory.h b/engines/lastexpress/game/inventory.h index b1995adce3..b1019a43c6 100644 --- a/engines/lastexpress/game/inventory.h +++ b/engines/lastexpress/game/inventory.h @@ -106,11 +106,10 @@ public: // UI Control void show(); - void blinkEgg(bool enabled); - void showHourGlass(); - void setPortrait(InventoryItem item); - void drawEgg(); - void drawBlinkingEgg(); + void showHourGlass() const; + void setPortrait(InventoryItem item) const; + void drawEgg() const; + void drawBlinkingEgg(uint ticks = 1); // Handle inventory UI events. void handleMouseEvent(const Common::Event &ev); @@ -133,8 +132,6 @@ public: Common::String toString(); private: - static const uint32 _defaultBlinkingInterval = 250; ///< Default blinking interval in ms - LastExpressEngine *_engine; InventoryEntry _entries[32]; @@ -144,9 +141,7 @@ private: uint32 _itemsShown; bool _showingHourGlass; - bool _blinkingEgg; - uint32 _blinkingTime; - uint32 _blinkingInterval; + int16 _blinkingDirection; uint16 _blinkingBrightness; // Flags @@ -157,8 +152,6 @@ private: Scene *_itemScene; - // Important rects - //Common::Rect _inventoryRect; Common::Rect _menuEggRect; Common::Rect _selectedItemRect; @@ -168,14 +161,15 @@ private: void close(); void examine(InventoryItem item); void drawHighlight(uint32 currentIndex, bool reset); - uint32 getItemIndex(uint32 currentIndex); + uint32 getItemIndex(uint32 currentIndex) const; bool isItemSceneParameter(InventoryItem item) const; - void drawItem(CursorStyle id, uint16 x, uint16 y, int16 brighnessIndex = -1); + void drawItem(CursorStyle id, uint16 x, uint16 y, int16 brighnessIndex = -1) const; + void blinkEgg(); void drawSelectedItem(); - void clearSelectedItem(); + void clearSelectedItem() const; }; } // End of namespace LastExpress diff --git a/engines/lastexpress/game/logic.cpp b/engines/lastexpress/game/logic.cpp index aeac8cff98..09104d1bf9 100644 --- a/engines/lastexpress/game/logic.cpp +++ b/engines/lastexpress/game/logic.cpp @@ -47,10 +47,7 @@ #include "lastexpress/menu/menu.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" -#include "lastexpress/graphics.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" #include "lastexpress/resource.h" @@ -88,16 +85,6 @@ Logic::~Logic() { ////////////////////////////////////////////////////////////////////////// // Event Handling ////////////////////////////////////////////////////////////////////////// -#define REDRAW_CURSOR() { \ - if (getInventory()->isMagnifierInUse()) \ - _engine->getCursor()->setStyle(kCursorMagnifier); \ - if (getInventory()->isPortraitHighlighted() \ - || getInventory()->isOpened() \ - || getInventory()->isEggHighlighted()) \ - _engine->getCursor()->setStyle(kCursorNormal); \ - return; \ -} - void Logic::eventMouse(const Common::Event &ev) { bool hotspotHandled = false; @@ -168,7 +155,9 @@ void Logic::eventMouse(const Common::Event &ev) { getInventory()->unselectItem(); } - REDRAW_CURSOR() + redrawCursor(); + + return; } // Handle match case @@ -194,7 +183,9 @@ void Logic::eventMouse(const Common::Event &ev) { getScenes()->processScene(); } - REDRAW_CURSOR() + redrawCursor(); + + return; } // Handle entity item case @@ -315,7 +306,7 @@ void Logic::eventTick(const Common::Event &) { ////////////////////////////////////////////////////////////////////////// // Draw the blinking egg if needed if (getGlobalTimer() && !getFlags()->shouldDrawEggOrHourGlass) - getInventory()->drawBlinkingEgg(); + getInventory()->drawBlinkingEgg(ticks); ////////////////////////////////////////////////////////////////////////// // Adjust time and save game if needed @@ -411,9 +402,12 @@ void Logic::eventTick(const Common::Event &) { * Resets the game state. */ void Logic::resetState() { - getState()->scene = kSceneDefault; + getScenes()->setCoordinates(Common::Rect(80, 0, 559, 479)); + + SAFE_DELETE(_entities); + _entities = new Entities(_engine); - warning("[Logic::resetState] Not implemented! You need to restart the engine until this is implemented."); + _state->reset(); } /** @@ -595,4 +589,14 @@ void Logic::updateCursor(bool) const { /* the cursor is always updated, even whe _engine->getCursor()->setStyle(style); } +void Logic::redrawCursor() const { + if (getInventory()->isMagnifierInUse()) + _engine->getCursor()->setStyle(kCursorMagnifier); + + if (getInventory()->isPortraitHighlighted() + || getInventory()->isOpened() + || getInventory()->isEggHighlighted()) + _engine->getCursor()->setStyle(kCursorNormal); +} + } // End of namespace LastExpress diff --git a/engines/lastexpress/game/logic.h b/engines/lastexpress/game/logic.h index 8b7dcef942..efb8f1e1a3 100644 --- a/engines/lastexpress/game/logic.h +++ b/engines/lastexpress/game/logic.h @@ -25,8 +25,6 @@ #include "lastexpress/shared.h" -#include "lastexpress/game/entities.h" - #include "lastexpress/eventhandler.h" #include "common/events.h" @@ -75,6 +73,7 @@ private: void switchChapter() const; void showCredits() const; + void redrawCursor() const; // Flags & Members bool _flagActionPerformed; diff --git a/engines/lastexpress/game/object.cpp b/engines/lastexpress/game/object.cpp index d9e9e4279a..48df91ea6d 100644 --- a/engines/lastexpress/game/object.cpp +++ b/engines/lastexpress/game/object.cpp @@ -22,11 +22,11 @@ #include "lastexpress/game/object.h" +#include "lastexpress/game/entities.h" #include "lastexpress/game/logic.h" #include "lastexpress/game/scenes.h" #include "lastexpress/game/state.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" namespace LastExpress { diff --git a/engines/lastexpress/game/savegame.cpp b/engines/lastexpress/game/savegame.cpp index 9c464feb6e..021dc40bb9 100644 --- a/engines/lastexpress/game/savegame.cpp +++ b/engines/lastexpress/game/savegame.cpp @@ -22,6 +22,7 @@ #include "lastexpress/game/savegame.h" +#include "lastexpress/game/entities.h" #include "lastexpress/game/inventory.h" #include "lastexpress/game/logic.h" #include "lastexpress/game/object.h" @@ -34,13 +35,13 @@ #include "lastexpress/debug.h" #include "lastexpress/lastexpress.h" -#include "lastexpress/helpers.h" #include "common/file.h" -#include "common/system.h" namespace LastExpress { +#define DISABLE_COMPRESSION 1 + // Names of savegames static const struct { const char *saveFile; @@ -54,14 +55,305 @@ static const struct { }; ////////////////////////////////////////////////////////////////////////// +// SavegameStream +////////////////////////////////////////////////////////////////////////// + +uint32 SavegameStream::write(const void *dataPtr, uint32 dataSize) { +#if !DISABLE_COMPRESSION + if (_enableCompression) + return writeCompressed(dataPtr, dataSize); +#endif + + return Common::MemoryWriteStreamDynamic::write(dataPtr, dataSize); +} + +uint32 SavegameStream::read(void *dataPtr, uint32 dataSize) { +#if !DISABLE_COMPRESSION + if (_enableCompression) + return readCompressed(dataPtr, dataSize); +#endif + + return readUncompressed(dataPtr, dataSize); +} + +uint32 SavegameStream::readUncompressed(void *dataPtr, uint32 dataSize) { + if ((int32)dataSize > size() - pos()) { + dataSize = (uint32)(size() - pos()); + _eos = true; + } + memcpy(dataPtr, getData() + pos(), dataSize); + + seek(dataSize, SEEK_CUR); + + return dataSize; +} + +void SavegameStream::writeBuffer(uint8 value, bool onlyValue) { + if (_bufferOffset == -1) + _bufferOffset = 0; + + if (_bufferOffset == 256) { + _bufferOffset = 0; + Common::MemoryWriteStreamDynamic::write(_buffer, 256); + } + + if (onlyValue || value < 0xFB) + _buffer[_bufferOffset] = value; + else + _buffer[_bufferOffset] = 0xFE; + + _offset++; + _bufferOffset++; + + if (!onlyValue && value >= 0xFB) + { + if (_bufferOffset == 256) { + _bufferOffset = 0; + Common::MemoryWriteStreamDynamic::write(_buffer, 256); + } + + _buffer[_bufferOffset] = value; + + _bufferOffset++; + _offset++; + } +} + +uint8 SavegameStream::readBuffer() { + if (_bufferOffset == -1 || _bufferOffset >= 256) { + readUncompressed(_buffer, 256); + _bufferOffset = 0; + } + + byte val = _buffer[_bufferOffset]; + _bufferOffset++; + + return val; +} + +uint32 SavegameStream::process() { + _enableCompression = !_enableCompression; + +#if DISABLE_COMPRESSION + return 0; +#else + switch (_status) { + default: + break; + + case kStatusReading: + _status = kStatusReady; + if (_bufferOffset != -1 && _bufferOffset != 256) { + seek(_bufferOffset - 256, SEEK_CUR); + _bufferOffset = -1; + } + break; + + case kStatusWriting: + switch (_valueCount) { + default: + break; + + case 1: + writeBuffer(_previousValue, false); + break; + + case 2: + if (_previousValue) { + writeBuffer(0xFF); + writeBuffer(_repeatCount); + writeBuffer(_previousValue); + break; + } + + if (_repeatCount == 3) { + writeBuffer(0xFB); + break; + } + + if (_repeatCount == 255) { + writeBuffer(0xFC); + break; + } + + writeBuffer(0xFD); + writeBuffer(_repeatCount); + break; + } + + if (_bufferOffset != -1 && _bufferOffset != 0) { + Common::MemoryWriteStreamDynamic::write(_buffer, _bufferOffset); + _bufferOffset = -1; + } + break; + } + + _status = kStatusReady; + _valueCount = 0; + uint32 offset = _offset; + _offset = 0; + + return offset; +#endif +} + +uint32 SavegameStream::writeCompressed(const void *dataPtr, uint32 dataSize) { + if (_status == kStatusReading) + error("[SavegameStream::writeCompressed] Error: Compression buffer is in read mode."); + + _status = kStatusWriting; + const byte *data = (const byte *)dataPtr; + + while (dataSize) { + switch (_valueCount) { + default: + error("[SavegameStream::writeCompressed] Invalid value count (%d)", _valueCount); + + case 0: + _previousValue = *data++; + _valueCount = 1; + break; + + case 1: + if (*data != _previousValue) { + writeBuffer(_previousValue, false); + _previousValue = *data; + } else { + _valueCount = 2; + _repeatCount = 2; + } + + ++data; + break; + + case 2: + if (*data != _previousValue || _repeatCount >= 255) { + if (_previousValue) { + writeBuffer(0xFF, true); + writeBuffer((uint8)_repeatCount, true); + writeBuffer(_previousValue, true); + + _previousValue = *data++; + _valueCount = 1; + break; + } + + if (_repeatCount == 3) { + writeBuffer(0xFB, true); + + _previousValue = *data++; + _valueCount = 1; + break; + } + + if (_repeatCount == -1) { + writeBuffer(0xFC, true); + + _previousValue = *data++; + _valueCount = 1; + break; + } + + writeBuffer(0xFD, true); + writeBuffer((uint8)_repeatCount, true); + + _previousValue = *data++; + _valueCount = 1; + } + + ++data; + ++_repeatCount; + break; + } + + --dataSize; + } + + return _offset; +} + +uint32 SavegameStream::readCompressed(void *dataPtr, uint32 dataSize) { + if (_status == kStatusWriting) + error("[SavegameStream::writeCompressed] Error: Compression buffer is in write mode."); + + _status = kStatusReady; + byte *data = (byte *)dataPtr; + + while (dataSize) { + switch (_valueCount) { + default: + error("[SavegameStream::readCompressed] Invalid value count (%d)", _valueCount); + + case 0: + case 1: { + // Read control code + byte control = readBuffer(); + + switch (control) { + default: + // Data value + *data++ = control; + break; + + case 0xFB: + _repeatCount = 2; + _previousValue = 0; + *data++ = 0; + _valueCount = 2; + break; + + case 0xFC: + _repeatCount = 254; + _previousValue = 0; + *data++ = 0; + _valueCount = 2; + break; + + case 0xFD: + _repeatCount = readBuffer() - 1; + _previousValue = 0; + *data++ = 0; + _valueCount = 2; + break; + + case 0xFE: + *data++ = readBuffer(); + break; + + case 0xFF: + _repeatCount = readBuffer() - 1; + _previousValue = readBuffer(); + *data++ = _previousValue; + _valueCount = 2; + break; + } + } + break; + + case 2: + *data++ = _previousValue; + _repeatCount--; + if (!_repeatCount) + _valueCount = 1; + break; + } + + --dataSize; + } + + return _offset; +} + +////////////////////////////////////////////////////////////////////////// // Constructors ////////////////////////////////////////////////////////////////////////// -SaveLoad::SaveLoad(LastExpressEngine *engine) : _engine(engine), _savegame(NULL), _gameTicksLastSavegame(0) { +SaveLoad::SaveLoad(LastExpressEngine *engine) : _engine(engine), _savegame(NULL), _gameTicksLastSavegame(0), _entity(kEntityPlayer) { } SaveLoad::~SaveLoad() { clear(true); + _savegame = NULL; // Zero passed pointers _engine = NULL; @@ -81,6 +373,7 @@ void SaveLoad::flushStream(GameId id) { error("[SaveLoad::flushStream] Savegame stream is invalid"); save->write(_savegame->getData(), (uint32)_savegame->size()); + save->finalize(); delete save; } @@ -189,10 +482,10 @@ void SaveLoad::clear(bool clearStream) { // Save & Load ////////////////////////////////////////////////////////////////////////// -// Load game -void SaveLoad::loadGame(GameId id) { +// Load last saved game +void SaveLoad::loadLastGame() { if (!_savegame) - error("[SaveLoad::loadGame] No savegame stream present"); + error("[SaveLoad::loadLastGame] No savegame stream present"); // Rewind current savegame _savegame->seek(0); @@ -229,7 +522,29 @@ void SaveLoad::loadGame(GameId id) { } // Load a specific game entry -void SaveLoad::loadGame(GameId id, uint32 index) { +void SaveLoad::loadGame(uint32 index) { + if (!_savegame) + error("[SaveLoad::loadLastGame] No savegame stream present"); + + // Rewind current savegame + _savegame->seek(0); + + // Write main header (with selected index) + SavegameMainHeader header; + header.count = index; + header.brightness = getState()->brightness; + header.volume = getState()->volume; + + Common::Serializer ser(NULL, _savegame); + header.saveLoadWithSerializer(ser); + + // TODO + // Go to the entry + // Load the entry + // Get offset (main and entry) + // Write main header again with correct entry offset + // Setup game and start + error("[SaveLoad::loadGame] Not implemented! (only loading the last entry is working for now)"); } @@ -341,16 +656,52 @@ bool SaveLoad::loadMainHeader(Common::InSaveFile *stream, SavegameMainHeader *he ////////////////////////////////////////////////////////////////////////// // Entries ////////////////////////////////////////////////////////////////////////// -void SaveLoad::writeEntry(SavegameType type, EntityIndex entity, uint32 value) { -#define WRITE_ENTRY(name, func, val) { \ - uint32 _prevPosition = (uint32)_savegame->pos(); \ - func; \ - uint32 _count = (uint32)_savegame->pos() - _prevPosition; \ - debugC(kLastExpressDebugSavegame, "Savegame: Writing " #name ": %d bytes", _count); \ - if (_count != val)\ - error("[SaveLoad::writeEntry] Number of bytes written (%d) differ from expected count (%d)", _count, val); \ +uint32 SaveLoad::writeValue(Common::Serializer &ser, const char *name, Common::Functor1<Common::Serializer &, void> *function, uint size) { + if (!_savegame) + error("[SaveLoad::writeValue] Stream not initialized properly"); + + debugC(kLastExpressDebugSavegame, "Savegame: Writing %s: %u bytes", name, size); + + uint32 prevPosition = (uint32)_savegame->pos(); + + // Serialize data into our buffer + (*function)(ser); + + uint32 count = (uint32)_savegame->pos() - prevPosition; + +#if DISABLE_COMPRESSION + if (count != size) + error("[SaveLoad::writeValue] %s - Number of bytes written (%d) differ from expected count (%d)", name, count, size); +#endif + + return count; } +uint32 SaveLoad::readValue(Common::Serializer &ser, const char *name, Common::Functor1<Common::Serializer &, void> *function, uint size) { + if (!_savegame) + error("[SaveLoad::readValue] Stream not initialized properly"); + + debugC(kLastExpressDebugSavegame, "Savegame: Reading %s: %u bytes", name, size); + + uint32 prevPosition = (uint32)_savegame->pos(); + + (*function)(ser); + + uint32 count = (uint32)_savegame->pos() - prevPosition; + +#if DISABLE_COMPRESSION + if (size != 0 && count != size) + error("[SaveLoad::readValue] %s - Number of bytes read (%d) differ from expected count (%d)", name, count, size); +#endif + + return count; +} + +void SaveLoad::syncEntity(Common::Serializer &ser) { + ser.syncAsUint32LE(_entity); +} + +void SaveLoad::writeEntry(SavegameType type, EntityIndex entity, uint32 value) { if (!_savegame) error("[SaveLoad::writeEntry] Savegame stream is invalid"); @@ -369,18 +720,22 @@ void SaveLoad::writeEntry(SavegameType type, EntityIndex entity, uint32 value) { header.saveLoadWithSerializer(ser); // Write game data - WRITE_ENTRY("entity index", ser.syncAsUint32LE(entity), 4); - WRITE_ENTRY("state", getState()->saveLoadWithSerializer(ser), 4 + 4 + 4 + 4 + 1 + 4 + 4); - WRITE_ENTRY("selected item", getInventory()->saveSelectedItem(ser), 4); - WRITE_ENTRY("positions", getEntities()->savePositions(ser), 4 * 1000); - WRITE_ENTRY("compartments", getEntities()->saveCompartments(ser), 4 * 16 * 2); - WRITE_ENTRY("progress", getProgress().saveLoadWithSerializer(ser), 4 * 128); - WRITE_ENTRY("events", getState()->syncEvents(ser), 512); - WRITE_ENTRY("inventory", getInventory()->saveLoadWithSerializer(ser), 7 * 32); - WRITE_ENTRY("objects", getObjects()->saveLoadWithSerializer(ser), 5 * 128); - WRITE_ENTRY("entities", getEntities()->saveLoadWithSerializer(ser), 1262 * 40); - WRITE_ENTRY("sound", getSoundQueue()->saveLoadWithSerializer(ser), 3 * 4 + getSoundQueue()->count() * 64); - WRITE_ENTRY("savepoints", getSavePoints()->saveLoadWithSerializer(ser), 128 * 16 + 4 + getSavePoints()->count() * 16); + _entity = entity; + + _savegame->process(); + writeValue(ser, "entity index", WRAP_SYNC_FUNCTION(this, SaveLoad, syncEntity), 4); + writeValue(ser, "state", WRAP_SYNC_FUNCTION(getState(), State::GameState, saveLoadWithSerializer), 4 + 4 + 4 + 4 + 1 + 4 + 4); + writeValue(ser, "selected item", WRAP_SYNC_FUNCTION(getInventory(), Inventory, saveSelectedItem), 4); + writeValue(ser, "positions", WRAP_SYNC_FUNCTION(getEntities(), Entities, savePositions), 4 * 1000); + writeValue(ser, "compartments", WRAP_SYNC_FUNCTION(getEntities(), Entities, saveCompartments), 4 * 16 * 2); + writeValue(ser, "progress", WRAP_SYNC_FUNCTION(&getProgress(), State::GameProgress, saveLoadWithSerializer), 4 * 128); + writeValue(ser, "events", WRAP_SYNC_FUNCTION(getState(), State::GameState, syncEvents), 512); + writeValue(ser, "inventory", WRAP_SYNC_FUNCTION(getInventory(), Inventory, saveLoadWithSerializer), 7 * 32); + writeValue(ser, "objects", WRAP_SYNC_FUNCTION(getObjects(), Objects, saveLoadWithSerializer), 5 * 128); + writeValue(ser, "entities", WRAP_SYNC_FUNCTION(getEntities(), Entities, saveLoadWithSerializer), 1262 * 40); + writeValue(ser, "sound", WRAP_SYNC_FUNCTION(getSoundQueue(), SoundQueue, saveLoadWithSerializer), 3 * 4 + getSoundQueue()->count() * 64); + writeValue(ser, "savepoints", WRAP_SYNC_FUNCTION(getSavePoints(), SavePoints, saveLoadWithSerializer), 128 * 16 + 4 + getSavePoints()->count() * 16); + _savegame->process(); header.offset = (uint32)_savegame->pos() - (originalPosition + 32); @@ -406,22 +761,6 @@ void SaveLoad::writeEntry(SavegameType type, EntityIndex entity, uint32 value) { } void SaveLoad::readEntry(SavegameType *type, EntityIndex *entity, uint32 *val, bool keepIndex) { -#define LOAD_ENTRY(name, func, val) { \ - uint32 _prevPosition = (uint32)_savegame->pos(); \ - func; \ - uint32 _count = (uint32)_savegame->pos() - _prevPosition; \ - debugC(kLastExpressDebugSavegame, "Savegame: Reading " #name ": %d bytes", _count); \ - if (_count != val) \ - error("[SaveLoad::readEntry] Number of bytes read (%d) differ from expected count (%d)", _count, val); \ -} - -#define LOAD_ENTRY_ONLY(name, func) { \ - uint32 _prevPosition = (uint32)_savegame->pos(); \ - func; \ - uint32 _count = (uint32)_savegame->pos() - _prevPosition; \ - debugC(kLastExpressDebugSavegame, "Savegame: Reading " #name ": %d bytes", _count); \ -} - if (!type || !entity || !val) error("[SaveLoad::readEntry] Invalid parameters passed"); @@ -444,20 +783,23 @@ void SaveLoad::readEntry(SavegameType *type, EntityIndex *entity, uint32 *val, b uint32 originalPosition = (uint32)_savegame->pos(); // Load game data - LOAD_ENTRY("entity index", ser.syncAsUint32LE(*entity), 4); - LOAD_ENTRY("state", getState()->saveLoadWithSerializer(ser), 4 + 4 + 4 + 4 + 1 + 4 + 4); - LOAD_ENTRY("selected item", getInventory()->saveSelectedItem(ser), 4); - LOAD_ENTRY("positions", getEntities()->savePositions(ser), 4 * 1000); - LOAD_ENTRY("compartments", getEntities()->saveCompartments(ser), 4 * 16 * 2); - LOAD_ENTRY("progress", getProgress().saveLoadWithSerializer(ser), 4 * 128); - LOAD_ENTRY("events", getState()->syncEvents(ser), 512); - LOAD_ENTRY("inventory", getInventory()->saveLoadWithSerializer(ser), 7 * 32); - LOAD_ENTRY("objects", getObjects()->saveLoadWithSerializer(ser), 5 * 128); - LOAD_ENTRY("entities", getEntities()->saveLoadWithSerializer(ser), 1262 * 40); - LOAD_ENTRY_ONLY("sound", getSoundQueue()->saveLoadWithSerializer(ser)); - LOAD_ENTRY_ONLY("savepoints", getSavePoints()->saveLoadWithSerializer(ser)); + _savegame->process(); + readValue(ser, "entity index", WRAP_SYNC_FUNCTION(this, SaveLoad, syncEntity), 4); + readValue(ser, "state", WRAP_SYNC_FUNCTION(getState(), State::GameState, saveLoadWithSerializer), 4 + 4 + 4 + 4 + 1 + 4 + 4); + readValue(ser, "selected item", WRAP_SYNC_FUNCTION(getInventory(), Inventory, saveSelectedItem), 4); + readValue(ser, "positions", WRAP_SYNC_FUNCTION(getEntities(), Entities, savePositions), 4 * 1000); + readValue(ser, "compartments", WRAP_SYNC_FUNCTION(getEntities(), Entities, saveCompartments), 4 * 16 * 2); + readValue(ser, "progress", WRAP_SYNC_FUNCTION(&getProgress(), State::GameProgress, saveLoadWithSerializer), 4 * 128); + readValue(ser, "events", WRAP_SYNC_FUNCTION(getState(), State::GameState, syncEvents), 512); + readValue(ser, "inventory", WRAP_SYNC_FUNCTION(getInventory(), Inventory, saveLoadWithSerializer), 7 * 32); + readValue(ser, "objects", WRAP_SYNC_FUNCTION(getObjects(), Objects, saveLoadWithSerializer), 5 * 128); + readValue(ser, "entities", WRAP_SYNC_FUNCTION(getEntities(), Entities, saveLoadWithSerializer), 1262 * 40); + readValue(ser, "sound", WRAP_SYNC_FUNCTION(getSoundQueue(), SoundQueue, saveLoadWithSerializer)); + readValue(ser, "savepoints", WRAP_SYNC_FUNCTION(getSavePoints(), SavePoints, saveLoadWithSerializer)); + _savegame->process(); // Update chapter + *entity = _entity; getProgress().chapter = entry.chapter; // Skip padding @@ -567,7 +909,7 @@ Common::InSaveFile *SaveLoad::openForLoading(GameId id) { } Common::OutSaveFile *SaveLoad::openForSaving(GameId id) { - Common::OutSaveFile *save = g_system->getSavefileManager()->openForSaving(getFilename(id)); + Common::OutSaveFile *save = g_system->getSavefileManager()->openForSaving(getFilename(id), false); // TODO Enable compression again if (!save) debugC(2, kLastExpressDebugSavegame, "Cannot open savegame for writing: %s", getFilename(id).c_str()); diff --git a/engines/lastexpress/game/savegame.h b/engines/lastexpress/game/savegame.h index 6f0408487f..361957227e 100644 --- a/engines/lastexpress/game/savegame.h +++ b/engines/lastexpress/game/savegame.h @@ -80,11 +80,68 @@ namespace LastExpress { // Savegame signatures -#define SAVEGAME_SIGNATURE 0x12001200 -#define SAVEGAME_ENTRY_SIGNATURE 0xE660E660 +#define SAVEGAME_SIGNATURE 0x12001200 // 301994496 +#define SAVEGAME_ENTRY_SIGNATURE 0xE660E660 // 3865110112 + +#define WRAP_SYNC_FUNCTION(instance, className, method) \ + new Common::Functor1Mem<Common::Serializer &, void, className>(instance, &className::method) class LastExpressEngine; +class SavegameStream : public Common::MemoryWriteStreamDynamic, public Common::SeekableReadStream { +public: + SavegameStream() : MemoryWriteStreamDynamic(DisposeAfterUse::YES), _eos(false) { + _enableCompression = false; + _bufferOffset = -1; + _valueCount = 0; + _previousValue = 0; + _repeatCount = 0; + _offset = 0; + _status = kStatusReady; + + memset(_buffer, 0, 256); + } + + int32 pos() const { return MemoryWriteStreamDynamic::pos(); } + int32 size() const { return MemoryWriteStreamDynamic::size(); } + bool seek(int32 offset, int whence = SEEK_SET) { return MemoryWriteStreamDynamic::seek(offset, whence); } + bool eos() const { return _eos; } + uint32 read(void *dataPtr, uint32 dataSize); + uint32 write(const void *dataPtr, uint32 dataSize); + + uint32 process(); + +private: + enum CompressedStreamStatus { + kStatusReady, + kStatusReading, + kStatusWriting + }; + + uint32 readUncompressed(void *dataPtr, uint32 dataSize); + + // Compressed data + uint32 writeCompressed(const void *dataPtr, uint32 dataSize); + uint32 readCompressed(void *dataPtr, uint32 dataSize); + + void writeBuffer(uint8 value, bool onlyValue = true); + uint8 readBuffer(); + +private: + bool _eos; + + // Compression handling + bool _enableCompression; + int16 _bufferOffset; + byte _valueCount; + byte _previousValue; + int16 _repeatCount; + uint32 _offset; + CompressedStreamStatus _status; + + byte _buffer[256]; +}; + class SaveLoad { public: SaveLoad(LastExpressEngine *engine); @@ -96,8 +153,8 @@ public: uint32 init(GameId id, bool resetHeaders); // Save & Load - void loadGame(GameId id); - void loadGame(GameId id, uint32 index); + void loadLastGame(); + void loadGame(uint32 index); void saveGame(SavegameType type, EntityIndex entity, uint32 value); void loadVolumeBrightness(); @@ -116,30 +173,6 @@ public: uint32 getLastSavegameTicks() const { return _gameTicksLastSavegame; } private: - class SavegameStream : public Common::MemoryWriteStreamDynamic, public Common::SeekableReadStream { - public: - SavegameStream() : MemoryWriteStreamDynamic(DisposeAfterUse::YES), - _eos(false) {} - - int32 pos() const { return MemoryWriteStreamDynamic::pos(); } - int32 size() const { return MemoryWriteStreamDynamic::size(); } - bool seek(int32 offset, int whence = SEEK_SET) { return MemoryWriteStreamDynamic::seek(offset, whence); } - bool eos() const { return _eos; } - uint32 read(void *dataPtr, uint32 dataSize) { - if ((int32)dataSize > size() - pos()) { - dataSize = size() - pos(); - _eos = true; - } - memcpy(dataPtr, getData() + pos(), dataSize); - - seek(dataSize, SEEK_CUR); - - return dataSize; - } - private: - bool _eos; - }; - LastExpressEngine *_engine; struct SavegameMainHeader : Common::Serializable { @@ -268,6 +301,9 @@ private: void writeEntry(SavegameType type, EntityIndex entity, uint32 val); void readEntry(SavegameType *type, EntityIndex *entity, uint32 *val, bool keepIndex); + uint32 writeValue(Common::Serializer &ser, const char *name, Common::Functor1<Common::Serializer &, void> *function, uint size); + uint32 readValue(Common::Serializer &ser, const char *name, Common::Functor1<Common::Serializer &, void> *function, uint size = 0); + SavegameEntryHeader *getEntry(uint32 index); // Opening save files @@ -279,6 +315,10 @@ private: void initStream(); void loadStream(GameId id); void flushStream(GameId id); + + // Misc + EntityIndex _entity; + void syncEntity(Common::Serializer &ser); }; } // End of namespace LastExpress diff --git a/engines/lastexpress/game/savepoint.cpp b/engines/lastexpress/game/savepoint.cpp index 64ae26c2be..8d14ec386b 100644 --- a/engines/lastexpress/game/savepoint.cpp +++ b/engines/lastexpress/game/savepoint.cpp @@ -26,7 +26,6 @@ #include "lastexpress/game/logic.h" #include "lastexpress/game/state.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" @@ -95,7 +94,7 @@ void SavePoints::process() { if (!updateEntityFromData(savepoint)) { // Call requested callback - Entity::Callback *callback = getCallback(savepoint.entity1); + Callback *callback = getCallback(savepoint.entity1); if (callback && callback->isValid()) { debugC(8, kLastExpressDebugLogic, "Savepoint: entity1=%s, action=%s, entity2=%s", ENTITY_NAME(savepoint.entity1), ACTION_NAME(savepoint.action), ENTITY_NAME(savepoint.entity2)); (*callback)(savepoint); @@ -126,7 +125,7 @@ void SavePoints::addData(EntityIndex entity, ActionIndex action, uint32 param) { ////////////////////////////////////////////////////////////////////////// // Callbacks ////////////////////////////////////////////////////////////////////////// -void SavePoints::setCallback(EntityIndex index, Entity::Callback *callback) { +void SavePoints::setCallback(EntityIndex index, Callback *callback) { if (index >= 40) error("[SavePoints::setCallback] Attempting to use an invalid entity index. Valid values 0-39, was %d", index); @@ -136,7 +135,7 @@ void SavePoints::setCallback(EntityIndex index, Entity::Callback *callback) { _callbacks[index] = callback; } -Entity::Callback *SavePoints::getCallback(EntityIndex index) const { +Callback *SavePoints::getCallback(EntityIndex index) const { if (index >= 40) error("[SavePoints::getCallback] Attempting to use an invalid entity index. Valid values 0-39, was %d", index); @@ -150,7 +149,7 @@ void SavePoints::call(EntityIndex entity2, EntityIndex entity1, ActionIndex acti point.entity2 = entity2; point.param.intValue = param; - Entity::Callback *callback = getCallback(entity1); + Callback *callback = getCallback(entity1); if (callback != NULL && callback->isValid()) { debugC(8, kLastExpressDebugLogic, "Savepoint: entity1=%s, action=%s, entity2=%s, param=%d", ENTITY_NAME(entity1), ACTION_NAME(action), ENTITY_NAME(entity2), param); (*callback)(point); @@ -164,7 +163,7 @@ void SavePoints::call(EntityIndex entity2, EntityIndex entity1, ActionIndex acti point.entity2 = entity2; strcpy((char *)&point.param.charValue, param); - Entity::Callback *callback = getCallback(entity1); + Callback *callback = getCallback(entity1); if (callback != NULL && callback->isValid()) { debugC(8, kLastExpressDebugLogic, "Savepoint: entity1=%s, action=%s, entity2=%s, param=%s", ENTITY_NAME(entity1), ACTION_NAME(action), ENTITY_NAME(entity2), param); (*callback)(point); @@ -181,7 +180,7 @@ void SavePoints::callAndProcess() { bool isRunning = getFlags()->isGameRunning; while (isRunning) { - Entity::Callback *callback = getCallback(index); + Callback *callback = getCallback(index); if (callback != NULL && callback->isValid()) { (*callback)(savepoint); isRunning = getFlags()->isGameRunning; @@ -203,7 +202,7 @@ void SavePoints::callAndProcess() { // Misc ////////////////////////////////////////////////////////////////////////// bool SavePoints::updateEntityFromData(const SavePoint &savepoint) { - for (int i = 0; i < (int)_data.size(); i++) { + for (uint i = 0; i < _data.size(); i++) { // Not a data savepoint! if (!_data[i].entity1) @@ -211,7 +210,7 @@ bool SavePoints::updateEntityFromData(const SavePoint &savepoint) { // Found our data! if (_data[i].entity1 == savepoint.entity1 && _data[i].action == savepoint.action) { - debugC(8, kLastExpressDebugLogic, "Update entity from data: entity1=%s, action=%s, param=%d", ENTITY_NAME(_data[i].entity1), ACTION_NAME(_data[i].action), _data[i].param); + debugC(8, kLastExpressDebugLogic, "Update entity from data: entity1=%s, action=%s, param=%u", ENTITY_NAME(_data[i].entity1), ACTION_NAME(_data[i].action), _data[i].param); // the SavePoint param value is the index of the entity call parameter to update getEntities()->get(_data[i].entity1)->getParamData()->updateParameters(_data[i].param); @@ -243,7 +242,15 @@ void SavePoints::saveLoadWithSerializer(Common::Serializer &s) { } // Skip uninitialized data if any - s.skip((_savePointsMaxSize - dataSize) * 16); + // (we are using a compressed stream, so we cannot seek on load) + uint32 unusedDataSize = (_savePointsMaxSize - dataSize) * 16; + if (s.isLoading()) { + byte *empty = (byte *)malloc(unusedDataSize); + s.syncBytes(empty, unusedDataSize); + free(empty); + } else { + s.skip(unusedDataSize); + } // Number of savepoints uint32 numSavepoints = _savepoints.size(); diff --git a/engines/lastexpress/game/savepoint.h b/engines/lastexpress/game/savepoint.h index a3303b4b8a..005133891a 100644 --- a/engines/lastexpress/game/savepoint.h +++ b/engines/lastexpress/game/savepoint.h @@ -23,9 +23,8 @@ #ifndef LASTEXPRESS_SAVEPOINT_H #define LASTEXPRESS_SAVEPOINT_H -#include "lastexpress/entities/entity.h" - #include "lastexpress/helpers.h" +#include "lastexpress/shared.h" #include "common/array.h" #include "common/list.h" @@ -74,10 +73,9 @@ struct SavePoint { } }; -class SavePoints : Common::Serializable { -private: - typedef Common::Functor1<const SavePoint&, void> Callback; +typedef Common::Functor1<const SavePoint&, void> Callback; +class SavePoints : Common::Serializable { public: struct SavePointData { @@ -112,7 +110,7 @@ public: void addData(EntityIndex entity, ActionIndex action, uint32 param); // Callbacks - void setCallback(EntityIndex index, Entity::Callback *callback); + void setCallback(EntityIndex index, Callback *callback); Callback *getCallback(EntityIndex entity) const; void call(EntityIndex entity2, EntityIndex entity1, ActionIndex action, uint32 param = 0) const; void call(EntityIndex entity2, EntityIndex entity1, ActionIndex action, const char *param) const; diff --git a/engines/lastexpress/game/scenes.cpp b/engines/lastexpress/game/scenes.cpp index b886951e0b..a2c7226b93 100644 --- a/engines/lastexpress/game/scenes.cpp +++ b/engines/lastexpress/game/scenes.cpp @@ -22,8 +22,6 @@ #include "lastexpress/game/scenes.h" -#include "lastexpress/data/scene.h" - #include "lastexpress/game/action.h" #include "lastexpress/game/beetle.h" #include "lastexpress/game/entities.h" @@ -34,10 +32,8 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/graphics.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" #include "lastexpress/resource.h" @@ -493,7 +489,7 @@ bool SceneManager::checkCurrentPosition(bool doCheckOtherCars) const { if (position == 99) return true; - switch (car){ + switch (car) { default: break; @@ -743,24 +739,31 @@ void SceneManager::resetQueue() { _queue.clear(); } -void SceneManager::setCoordinates(SequenceFrame *frame) { +void SceneManager::setCoordinates(const Common::Rect &rect) { + _flagCoordinates = true; - if (!frame || frame->getInfo()->subType == 3) - return; + if (_coords.right > rect.right) + _coords.right = rect.right; - _flagCoordinates = true; + if (_coords.bottom > rect.bottom) + _coords.bottom = rect.bottom; + + if (_coords.left < rect.left) + _coords.left = rect.left; - if (_coords.right > (int)frame->getInfo()->xPos1) - _coords.right = (int16)frame->getInfo()->xPos1; + if (_coords.top < rect.top) + _coords.top = rect.top; +} - if (_coords.bottom > (int)frame->getInfo()->yPos1) - _coords.bottom = (int16)frame->getInfo()->yPos1; +void SceneManager::setCoordinates(SequenceFrame *frame) { - if (_coords.left < (int)frame->getInfo()->xPos2) - _coords.left = (int16)frame->getInfo()->xPos2; + if (!frame || frame->getInfo()->subType == 3) + return; - if (_coords.top < (int)frame->getInfo()->yPos2) - _coords.top = (int16)frame->getInfo()->yPos2; + setCoordinates(Common::Rect((int16)frame->getInfo()->xPos1, + (int16)frame->getInfo()->yPos1, + (int16)frame->getInfo()->xPos2, + (int16)frame->getInfo()->yPos2)); } void SceneManager::resetCoordinates() { diff --git a/engines/lastexpress/game/scenes.h b/engines/lastexpress/game/scenes.h index 172dde2683..1c7ae85f98 100644 --- a/engines/lastexpress/game/scenes.h +++ b/engines/lastexpress/game/scenes.h @@ -79,6 +79,7 @@ public: void removeAndRedraw(SequenceFrame **frame, bool doRedraw); void resetQueue(); void setCoordinates(SequenceFrame *frame); + void setCoordinates(const Common::Rect &rect); // Helpers SceneIndex getSceneIndexFromPosition(CarIndex car, Position position, int param3 = -1); diff --git a/engines/lastexpress/game/state.cpp b/engines/lastexpress/game/state.cpp index f3fd9720b1..02ede25595 100644 --- a/engines/lastexpress/game/state.cpp +++ b/engines/lastexpress/game/state.cpp @@ -49,6 +49,18 @@ State::~State() { _engine = NULL; } +void State::reset() { + SAFE_DELETE(_inventory); + SAFE_DELETE(_objects); + SAFE_DELETE(_savepoints); + SAFE_DELETE(_state); + + _inventory = new Inventory(_engine); + _objects = new Objects(_engine); + _savepoints = new SavePoints(_engine); + _state = new GameState(); +} + bool State::isNightTime() const { return (_state->progress.chapter == kChapter1 || _state->progress.chapter == kChapter4 diff --git a/engines/lastexpress/game/state.h b/engines/lastexpress/game/state.h index c937fdce9f..2c484f6976 100644 --- a/engines/lastexpress/game/state.h +++ b/engines/lastexpress/game/state.h @@ -621,6 +621,8 @@ public: State(LastExpressEngine *engine); ~State(); + void reset(); + // Accessors Inventory *getGameInventory() { return _inventory; } Objects *getGameObjects() { return _objects; } diff --git a/engines/lastexpress/helpers.h b/engines/lastexpress/helpers.h index 7f3f1e246c..02454be13d 100644 --- a/engines/lastexpress/helpers.h +++ b/engines/lastexpress/helpers.h @@ -27,6 +27,8 @@ // Misc helpers ////////////////////////////////////////////////////////////////////////// +#define LOW_BYTE(w) ((unsigned char)(((unsigned long)(w)) & 0xff)) + // Misc #define getArchive(name) _engine->getResourceManager()->getFileStream(name) #define rnd(value) _engine->getRandom().getRandomNumber(value - 1) diff --git a/engines/lastexpress/lastexpress.cpp b/engines/lastexpress/lastexpress.cpp index 250fa0f2d0..01d2634dec 100644 --- a/engines/lastexpress/lastexpress.cpp +++ b/engines/lastexpress/lastexpress.cpp @@ -54,18 +54,17 @@ const char *g_entityNames[] = { "Player", "Anna", "August", "Mertens", "Coudert" namespace LastExpress { LastExpressEngine::LastExpressEngine(OSystem *syst, const ADGameDescription *gd) : - Engine(syst), _gameDescription(gd), - _debugger(NULL), _cursor(NULL), - _font(NULL), _logic(NULL), _menu(NULL), - _frameCounter(0), _lastFrameCount(0), + Engine(syst), _gameDescription(gd), + _debugger(NULL), _random("lastexpress"), _cursor(NULL), + _font(NULL), _logic(NULL), _menu(NULL), + _frameCounter(0), _lastFrameCount(0), _graphicsMan(NULL), _resMan(NULL), _sceneMan(NULL), _soundMan(NULL), _eventMouse(NULL), _eventTick(NULL), - _eventMouseBackup(NULL), _eventTickBackup(NULL), - _random("lastexpress") + _eventMouseBackup(NULL), _eventTickBackup(NULL) { // Setup mixer - syncSoundSettings(); + Engine::syncSoundSettings(); // Adding the default directories const Common::FSNode gameDataDir(ConfMan.get("path")); diff --git a/engines/lastexpress/menu/menu.cpp b/engines/lastexpress/menu/menu.cpp index f1a8bebe94..c48e55bb55 100644 --- a/engines/lastexpress/menu/menu.cpp +++ b/engines/lastexpress/menu/menu.cpp @@ -30,6 +30,7 @@ #include "lastexpress/fight/fight.h" +#include "lastexpress/game/entities.h" #include "lastexpress/game/inventory.h" #include "lastexpress/game/logic.h" #include "lastexpress/game/savegame.h" @@ -41,10 +42,8 @@ #include "lastexpress/menu/trainline.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/graphics.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" #include "lastexpress/resource.h" @@ -865,7 +864,7 @@ void Menu::init(bool doSavegame, SavegameType type, uint32 value) { doSavegame = false; } else { - // TODO rename saves? + warning("[Menu::initGame] Renaming saves not implemented"); } // Create a new savegame if needed @@ -876,7 +875,7 @@ void Menu::init(bool doSavegame, SavegameType type, uint32 value) { getSaveLoad()->saveGame(kSavegameTypeEvent2, kEntityPlayer, kEventNone); if (!getGlobalTimer()) { - // TODO: remove existing savegame temp file + warning("[Menu::initGame] Removing temporary saves not implemented"); } // Init savegame & menu values @@ -917,13 +916,13 @@ void Menu::startGame() { if (_lastIndex == _index) { setGlobalTimer(0); if (_index) { - getSaveLoad()->loadGame(_gameId); + getSaveLoad()->loadLastGame(); } else { getLogic()->resetState(); getEntities()->setup(true, kEntityPlayer); } } else { - getSaveLoad()->loadGame(_gameId, _index); + getSaveLoad()->loadGame(_index); } } diff --git a/engines/lastexpress/resource.cpp b/engines/lastexpress/resource.cpp index ee4885e34e..1d010355ac 100644 --- a/engines/lastexpress/resource.cpp +++ b/engines/lastexpress/resource.cpp @@ -31,7 +31,6 @@ #include "common/debug.h" #include "common/file.h" -#include "common/textconsole.h" namespace LastExpress { @@ -129,13 +128,10 @@ bool ResourceManager::loadArchive(const Common::String &name) { // Get a stream to file in the archive // - same as createReadStreamForMember except it checks if the file exists and will assert / output a debug message if not -Common::SeekableReadStream *ResourceManager::getFileStream(const Common::String &name) { +Common::SeekableReadStream *ResourceManager::getFileStream(const Common::String &name) const { // Check if the file exits in the archive if (!hasFile(name)) { -//#ifdef _DEBUG -// error("[ResourceManager::getFileStream] Cannot open file: %s", name.c_str()); -//#endif debugC(2, kLastExpressDebugResource, "Error opening file: %s", name.c_str()); return NULL; } diff --git a/engines/lastexpress/resource.h b/engines/lastexpress/resource.h index f2f5d63bce..90ac9b87ad 100644 --- a/engines/lastexpress/resource.h +++ b/engines/lastexpress/resource.h @@ -42,7 +42,7 @@ public: // Loading bool loadArchive(ArchiveIndex type); static bool isArchivePresent(ArchiveIndex type); - Common::SeekableReadStream *getFileStream(const Common::String &name); + Common::SeekableReadStream *getFileStream(const Common::String &name) const; // Archive functions bool hasFile(const Common::String &name) const; diff --git a/engines/lastexpress/shared.h b/engines/lastexpress/shared.h index d60a498447..56cf730e24 100644 --- a/engines/lastexpress/shared.h +++ b/engines/lastexpress/shared.h @@ -80,7 +80,8 @@ enum SoundFlag { kFlagMusic = 0x5000010, kFlagType3 = 0x6000000, kFlagLoop = 0x6001008, - kFlagType9 = 0x7000000 + kFlagType9 = 0x7000000, + kFlagNIS = 0x7002010 }; enum SoundState { @@ -1732,62 +1733,6 @@ enum ActionIndex { kActionEnd }; -////////////////////////////////////////////////////////////////////////// -// Functors classes used by the engine -////////////////////////////////////////////////////////////////////////// - -// FIXME is this achievable with the existing Functor1Mem function -template<class Arg, class Res, class T> -class Functor1MemConst : public Common::Functor1<Arg, Res> { -public: - typedef Res (T::*FuncType)(Arg) const; - - Functor1MemConst(T *t, const FuncType &func) : _t(t), _func(func) {} - - bool isValid() const { return _func != 0 && _t != 0; } - Res operator()(Arg v1) const { - return (_t->*_func)(v1); - } -private: - mutable T *_t; - const FuncType _func; -}; - -// FIXME move this to existing func.h file -template<class Arg1, class Arg2, class Arg3, class Arg4, class Result> -struct QuaternaryFunction { - typedef Arg1 FirstArgumentType; - typedef Arg2 SecondArgumentType; - typedef Arg3 ThirdArgumentType; - typedef Arg4 FourthArgumentType; - typedef Result ResultType; -}; - -template<class Arg1, class Arg2, class Arg3, class Arg4, class Res> -struct Functor4 : public QuaternaryFunction<Arg1, Arg2, Arg3, Arg4, Res> { - virtual ~Functor4() {} - - virtual bool isValid() const = 0; - virtual Res operator()(Arg1, Arg2, Arg3, Arg4) const = 0; -}; - -template<class Arg1, class Arg2, class Arg3, class Arg4, class Res, class T> -class Functor4Mem : public Functor4<Arg1, Arg2, Arg3, Arg4, Res> { -public: - typedef Res (T::*FuncType)(Arg1, Arg2, Arg3, Arg4); - - Functor4Mem(T *t, const FuncType &func) : _t(t), _func(func) {} - - bool isValid() const { return _func != 0 && _t != 0; } - Res operator()(Arg1 v1, Arg2 v2, Arg3 v3, Arg4 v4) const { - return (_t->*_func)(v1, v2, v3, v4); - } -private: - mutable T *_t; - const FuncType _func; -}; - - } // End of namespace LastExpress #endif // LASTEXPRESS_SHARED_H diff --git a/engines/lastexpress/sound/entry.cpp b/engines/lastexpress/sound/entry.cpp index 44cc68a57b..3d22657124 100644 --- a/engines/lastexpress/sound/entry.cpp +++ b/engines/lastexpress/sound/entry.cpp @@ -30,11 +30,9 @@ #include "lastexpress/sound/sound.h" #include "lastexpress/graphics.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" #include "lastexpress/resource.h" -#include "common/stream.h" namespace LastExpress { @@ -47,6 +45,8 @@ namespace LastExpress { SoundEntry::SoundEntry(LastExpressEngine *engine) : _engine(engine) { _type = kSoundTypeNone; + _currentDataPtr = NULL; + _blockCount = 0; _time = 0; @@ -71,7 +71,13 @@ SoundEntry::~SoundEntry() { // Entries that have been queued will have their streamed disposed automatically if (!_soundStream) SAFE_DELETE(_stream); - delete _soundStream; + + SAFE_DELETE(_soundStream); + + free(_currentDataPtr); + + _subtitle = NULL; + _stream = NULL; // Zero passed pointers _engine = NULL; @@ -110,10 +116,8 @@ void SoundEntry::close() { } void SoundEntry::play() { - if (!_stream) { - warning("[SoundEntry::play] stream has been disposed"); - return; - } + if (!_stream) + error("[SoundEntry::play] stream has been disposed"); // Prepare sound stream if (!_soundStream) @@ -277,7 +281,7 @@ bool SoundEntry::updateSound() { int l = strlen(sub) + 1; if (l - 1 > 4) - sub[l - 1 - 4] = 0; + sub[l - (1 + 4)] = 0; showSubtitle(sub); } } else { @@ -393,6 +397,10 @@ SubtitleEntry::SubtitleEntry(LastExpressEngine *engine) : _engine(engine) { SubtitleEntry::~SubtitleEntry() { SAFE_DELETE(_data); + + // Zero-out passed pointers + _sound = NULL; + _engine = NULL; } void SubtitleEntry::load(Common::String filename, SoundEntry *soundEntry) { @@ -423,6 +431,9 @@ void SubtitleEntry::loadData() { } void SubtitleEntry::setupAndDraw() { + if (!_sound) + error("[SubtitleEntry::setupAndDraw] Sound entry not initialized"); + if (!_data) { _data = new SubtitleManager(_engine->getFont()); _data->load(getArchive(_filename)); diff --git a/engines/lastexpress/sound/queue.cpp b/engines/lastexpress/sound/queue.cpp index 33b4c06793..8904b48930 100644 --- a/engines/lastexpress/sound/queue.cpp +++ b/engines/lastexpress/sound/queue.cpp @@ -26,6 +26,7 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/entry.h" +#include "lastexpress/sound/sound.h" #include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" @@ -39,6 +40,7 @@ SoundQueue::SoundQueue(LastExpressEngine *engine) : _engine(engine) { _subtitlesFlag = 0; _currentSubtitle = NULL; + //_soundCacheData = NULL; } SoundQueue::~SoundQueue() { @@ -51,6 +53,7 @@ SoundQueue::~SoundQueue() { _subtitles.clear(); _currentSubtitle = NULL; + //SAFE_DELETE(_soundCacheData); // Zero passed pointers _engine = NULL; @@ -64,6 +67,8 @@ void SoundQueue::handleTimer() { for (Common::List<SoundEntry *>::iterator i = _soundList.begin(); i != _soundList.end(); ++i) { SoundEntry *entry = (*i); + if (entry == NULL) + error("[SoundQueue::handleTimer] Invalid entry found in sound queue"); // When the entry has stopped playing, we remove his buffer if (entry->isFinished()) { @@ -120,6 +125,8 @@ void SoundQueue::updateQueue() { for (Common::List<SoundEntry *>::iterator it = _soundList.begin(); it != _soundList.end(); ++it) { SoundEntry *entry = *it; + if (entry == NULL) + error("[SoundQueue::updateQueue] Invalid entry found in sound queue"); // Original removes the entry data from the cache and sets the archive as not loaded // and if the sound data buffer is not full, loads a new entry to be played based on @@ -134,7 +141,7 @@ void SoundQueue::updateQueue() { // Original update the current entry, loading another set of samples to be decoded - getFlags()->flag_3 = 0; + getFlags()->flag_3 = false; --_flag; } @@ -176,6 +183,8 @@ void SoundQueue::clearQueue() { for (Common::List<SoundEntry *>::iterator i = _soundList.begin(); i != _soundList.end(); ++i) { SoundEntry *entry = (*i); + if (entry == NULL) + error("[SoundQueue::clearQueue] Invalid entry found in sound queue"); // Delete entry entry->close(); @@ -340,13 +349,14 @@ void SoundQueue::updateSubtitles() { return; } + if (!subtitle) + return; + if (_subtitlesFlag & 1) subtitle->drawOnScreen(); - if (subtitle) { - subtitle->loadData(); - subtitle->setupAndDraw(); - } + subtitle->loadData(); + subtitle->setupAndDraw(); } ////////////////////////////////////////////////////////////////////////// @@ -368,7 +378,15 @@ void SoundQueue::saveLoadWithSerializer(Common::Serializer &s) { (*i)->saveLoadWithSerializer(s); } else { warning("[Sound::saveLoadWithSerializer] Loading not implemented"); - s.skip(numEntries * 64); + + uint32 unusedDataSize = numEntries * 64; + if (s.isLoading()) { + byte *empty = (byte *)malloc(unusedDataSize); + s.syncBytes(empty, unusedDataSize); + free(empty); + } else { + s.skip(unusedDataSize); + } } } diff --git a/engines/lastexpress/sound/queue.h b/engines/lastexpress/sound/queue.h index 75fe06883a..e1f9be1cf7 100644 --- a/engines/lastexpress/sound/queue.h +++ b/engines/lastexpress/sound/queue.h @@ -106,7 +106,7 @@ private: // Entries Common::List<SoundEntry *> _soundList; ///< List of all sound entries - void *_soundCacheData; + //void *_soundCacheData; // Subtitles int _subtitlesFlag; diff --git a/engines/lastexpress/sound/sound.cpp b/engines/lastexpress/sound/sound.cpp index 2f7bb4a601..319f7cd4f4 100644 --- a/engines/lastexpress/sound/sound.cpp +++ b/engines/lastexpress/sound/sound.cpp @@ -33,7 +33,6 @@ #include "lastexpress/sound/entry.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/helpers.h" #include "lastexpress/graphics.h" #include "lastexpress/lastexpress.h" #include "lastexpress/resource.h" @@ -675,7 +674,7 @@ const char *SoundManager::getDialogName(EntityIndex entity) const { ////////////////////////////////////////////////////////////////////////// // Letters & Messages ////////////////////////////////////////////////////////////////////////// -void SoundManager::readText(int id){ +void SoundManager::readText(int id) { if (!_queue->isBuffered(kEntityTables4)) return; @@ -1330,23 +1329,23 @@ void SoundManager::playLoopingSound(int param) { } } else { switch (getEntityData(kEntityPlayer)->car) { - case 1: - case 6: + case kCarBaggageRear: + case kCarBaggage: partNumber = 4; break; - case 2: - case 3: - case 4: - case 5: + case kCarKronos: + case kCarGreenSleeping: + case kCarRedSleeping: + case kCarRestaurant: partNumber = 1; break; - case 7: + case kCarCoalTender: partNumber = 5; break; - case 8: + case kCarLocomotive: partNumber = 99; break; - case 9: + case kCar9: partNumber = 3; break; default: @@ -1357,13 +1356,13 @@ void SoundManager::playLoopingSound(int param) { } if (partNumber != 99) - sprintf(tmp, "LOOP%d%c.SND", partNumber, _engine->getRandom().getRandomNumber(numLoops[partNumber] - 1) + 'A'); + sprintf(tmp, "LOOP%d%c.SND", partNumber, (char)(_engine->getRandom().getRandomNumber(numLoops[partNumber] - 1) + 'A')); } if (getFlags()->flag_3) fnameLen = 5; - if (!entry || scumm_strnicmp(entry->getName2().c_str(), tmp, fnameLen)) { + if (!entry || scumm_strnicmp(entry->getName2().c_str(), tmp, (uint)fnameLen)) { _loopingSoundDuration = _engine->getRandom().getRandomNumber(319) + 260; if (partNumber != 99) { diff --git a/engines/lure/decode.cpp b/engines/lure/decode.cpp index 1338559534..484126c43f 100644 --- a/engines/lure/decode.cpp +++ b/engines/lure/decode.cpp @@ -255,7 +255,7 @@ MemoryBlock *PictureDecoder::vgaDecode(MemoryBlock *src, uint32 maxOutputSize) { decrCtr(); if (shlCarry()) break; - + AL = dataIn->data()[BP + 3]; } else { decrCtr(); @@ -375,7 +375,7 @@ uint32 AnimationDecoder::decode_data(MemoryBlock *src, MemoryBlock *dest, uint32 // Main loop bool loopFlag = true; while (loopFlag) { - for (;;) { + for (;;) { carry = false; rcl(currData, carry); if (--bitCtr == 0) { diff --git a/engines/made/screenfx.cpp b/engines/made/screenfx.cpp index de9231a158..ad71f1fb49 100644 --- a/engines/made/screenfx.cpp +++ b/engines/made/screenfx.cpp @@ -200,10 +200,10 @@ void ScreenEffects::stepBlendedPalette() { setBlendedPalette(_blendedPaletteStatus._palette, _blendedPaletteStatus._newPalette, _blendedPaletteStatus._colorCount, _blendedPaletteStatus._value, _blendedPaletteStatus._maxValue); if (_blendedPaletteStatus._value == _blendedPaletteStatus._maxValue) - _blendedPaletteStatus._value++; - else - _blendedPaletteStatus._value = MIN<int16>(_blendedPaletteStatus._value + _blendedPaletteStatus._incr, _blendedPaletteStatus._maxValue); - } + _blendedPaletteStatus._value++; + else + _blendedPaletteStatus._value = MIN<int16>(_blendedPaletteStatus._value + _blendedPaletteStatus._incr, _blendedPaletteStatus._maxValue); + } } void ScreenEffects::copyFxRect(Graphics::Surface *surface, int16 x1, int16 y1, int16 x2, int16 y2) { diff --git a/engines/mohawk/bitmap.cpp b/engines/mohawk/bitmap.cpp index 952b6daec2..bc19fe2d3e 100644 --- a/engines/mohawk/bitmap.cpp +++ b/engines/mohawk/bitmap.cpp @@ -630,7 +630,7 @@ void MohawkBitmap::drawRLE8(Graphics::Surface *surface, bool isLE) { // Myst Bitmap Decoder ////////////////////////////////////////// -MohawkSurface *MystBitmap::decodeImage(Common::SeekableReadStream* stream) { +MohawkSurface *MystBitmap::decodeImage(Common::SeekableReadStream *stream) { uint32 uncompressedSize = stream->readUint32LE(); Common::SeekableReadStream *bmpStream = decompressLZ(stream, uncompressedSize); delete stream; @@ -652,10 +652,10 @@ MohawkSurface *MystBitmap::decodeImage(Common::SeekableReadStream* stream) { } // Copy the palette to one of our own - const byte *palette = bitmapDecoder.getPalette(); byte *newPal = 0; - if (palette) { + if (bitmapDecoder.hasPalette()) { + const byte *palette = bitmapDecoder.getPalette(); newPal = (byte *)malloc(256 * 3); memcpy(newPal, palette, 256 * 3); } diff --git a/engines/mohawk/console.cpp b/engines/mohawk/console.cpp index a7a650d8ed..fc957e895e 100644 --- a/engines/mohawk/console.cpp +++ b/engines/mohawk/console.cpp @@ -75,7 +75,7 @@ bool MystConsole::Cmd_ChangeCard(int argc, const char **argv) { } _vm->_sound->stopSound(); - _vm->changeToCard((uint16)atoi(argv[1]), true); + _vm->changeToCard((uint16)atoi(argv[1]), kTransitionCopy); return false; } diff --git a/engines/mohawk/cursors.cpp b/engines/mohawk/cursors.cpp index e73d4ed6a3..cdb4e1482f 100644 --- a/engines/mohawk/cursors.cpp +++ b/engines/mohawk/cursors.cpp @@ -106,8 +106,8 @@ void MystCursorManager::hideCursor() { void MystCursorManager::setCursor(uint16 id) { // Zero means empty cursor if (id == 0) { - static const byte emptyCursor = 0; - CursorMan.replaceCursor(&emptyCursor, 1, 1, 0, 0, 0); + static const byte emptyCursor[4] = { 0, 0, 0, 0 }; + CursorMan.replaceCursor(&emptyCursor, 2, 2, 0, 0, 0); return; } diff --git a/engines/mohawk/detection.cpp b/engines/mohawk/detection.cpp index f0c657897d..ef07de0180 100644 --- a/engines/mohawk/detection.cpp +++ b/engines/mohawk/detection.cpp @@ -143,6 +143,7 @@ static const PlainGameDescriptor mohawkGames[] = { {"harryhh","Harry and the Haunted House"}, {"stellaluna", "Stellaluna"}, {"sheila", "Sheila Rae, the Brave"}, + {"rugratsps", "Rugrats Print Shop" }, {0, 0} }; @@ -167,7 +168,7 @@ public: } virtual const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const { - return detectGameFilebased(allFiles, Mohawk::fileBased); + return detectGameFilebased(allFiles, fslist, Mohawk::fileBased); } virtual const char *getName() const { diff --git a/engines/mohawk/detection_tables.h b/engines/mohawk/detection_tables.h index 5acc1bb179..fc7dfce9bb 100644 --- a/engines/mohawk/detection_tables.h +++ b/engines/mohawk/detection_tables.h @@ -204,24 +204,6 @@ static const MohawkGameDescription gameDescriptions[] = { }, // Myst Masterpiece Edition - // English Windows - // From clone2727 - { - { - "myst", - "Masterpiece Edition", - AD_ENTRY1("MYST.DAT", "c4cae9f143b5947262e6cb2397e1617e"), - Common::EN_ANY, - Common::kPlatformMacintosh, - ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) - }, - GType_MYST, - GF_ME, - 0, - }, - - // Myst Masterpiece Edition // German Windows // From DrMcCoy (Included in "Myst: Die Trilogie") { @@ -2114,6 +2096,24 @@ static const MohawkGameDescription gameDescriptions[] = { 0 }, + // Rugrats Adventure Game + // English Windows Demo + // From GeorgeQGreg (Rugrats Movie Soundtrack) + { + { + "rugrats", + "Demo", + AD_ENTRY1("outline", "adbd7ff6c5e1bdb7062c89879a4e39e6"), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_DEMO | ADGF_UNSTABLE, + GUIO1(GUIO_NOASPECT) + }, + GType_LIVINGBOOKSV4, + 0, + 0 + }, + { { "lbsampler", @@ -2525,6 +2525,24 @@ static const MohawkGameDescription gameDescriptions[] = { 0 }, + // Rugrats Print Shop + // English Windows Demo + // From GeorgeQGreg (Rugrats Movie Soundtrack) + { + { + "rugratsps", + "Demo", + AD_ENTRY1("outline", "808d5ee8427180ddebdd5dd4199b47cb"), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_DEMO | ADGF_UNSTABLE, + GUIO1(GUIO_NOASPECT) + }, + GType_LIVINGBOOKSV4, + 0, + 0 + }, + { AD_TABLE_END_MARKER, 0, 0, 0 } }; diff --git a/engines/mohawk/dialogs.cpp b/engines/mohawk/dialogs.cpp index 4461a30ad4..5f5a3b3800 100644 --- a/engines/mohawk/dialogs.cpp +++ b/engines/mohawk/dialogs.cpp @@ -137,12 +137,6 @@ void MystOptionsDialog::open() { void MystOptionsDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) { switch (cmd) { - case kZipCmd: - _vm->_gameState->_globals.zipMode = _zipModeCheckbox->getState(); - break; - case kTransCmd: - _vm->_gameState->_globals.transitions = _transitionsCheckbox->getState(); - break; case kDropCmd: _vm->_needsPageDrop = true; close(); @@ -155,8 +149,10 @@ void MystOptionsDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, ui _vm->_needsShowDemoMenu = true; close(); break; - case GUI::kCloseCmd: - close(); + case GUI::kOKCmd: + _vm->_gameState->_globals.zipMode = _zipModeCheckbox->getState(); + _vm->_gameState->_globals.transitions = _transitionsCheckbox->getState(); + GUI::OptionsDialog::handleCommand(sender, cmd, data); break; default: GUI::OptionsDialog::handleCommand(sender, cmd, data); diff --git a/engines/mohawk/myst.cpp b/engines/mohawk/myst.cpp index 0efd412bd0..8140817eb3 100644 --- a/engines/mohawk/myst.cpp +++ b/engines/mohawk/myst.cpp @@ -98,11 +98,6 @@ MohawkEngine_Myst::MohawkEngine_Myst(OSystem *syst, const MohawkGameDescription _view.soundListVolume = NULL; _view.scriptResCount = 0; _view.scriptResources = NULL; - - if ((getFeatures() & GF_ME) && getPlatform() == Common::kPlatformMacintosh) { - const Common::FSNode gameDataDir(ConfMan.get("path")); - SearchMan.addSubDirectoryMatching(gameDataDir, "CD Data"); - } } MohawkEngine_Myst::~MohawkEngine_Myst() { @@ -205,11 +200,6 @@ static const char *mystFiles[] = { // qtw/myst/libelev.mov: libup.mov is basically the same with sound Common::String MohawkEngine_Myst::wrapMovieFilename(const Common::String &movieName, uint16 stack) { - // The Macintosh release of Myst ME stores its videos in a different folder - // WORKAROUND: The gear rotation videos are not in the CD Data folder. See above comments. - if ((getFeatures() & GF_ME) && getPlatform() == Common::kPlatformMacintosh && !movieName.matchString("cl1wg?")) - return Common::String("CD Data/m/") + movieName + ".mov"; - Common::String prefix; switch (stack) { @@ -498,52 +488,32 @@ void MohawkEngine_Myst::changeToStack(uint16 stack, uint16 card, uint16 linkSrcS if (!_mhk[0]->openFile(mystFiles[_curStack])) error("Could not open %s", mystFiles[_curStack]); - if (getPlatform() == Common::kPlatformMacintosh) - _gfx->loadExternalPictureFile(_curStack); - _runExitScript = false; // Clear the resource cache and the image cache _cache.clear(); _gfx->clearCache(); - // Play Flyby Entry Movie on Masterpiece Edition. The Macintosh version is currently hooked - // up to the Cinepak versions of the video (the 'c' suffix) until the SVQ1 decoder is completed. + // Play Flyby Entry Movie on Masterpiece Edition. const char *flyby = 0; if (getFeatures() & GF_ME) { switch (_curStack) { case kSeleniticStack: - if (getPlatform() == Common::kPlatformMacintosh) - flyby = "FLY_SEc"; - else - flyby = "selenitic flyby"; + flyby = "selenitic flyby"; break; case kStoneshipStack: - if (getPlatform() == Common::kPlatformMacintosh) - flyby = "FLY_STc"; - else - flyby = "stoneship flyby"; + flyby = "stoneship flyby"; break; // Myst Flyby Movie not used in Original Masterpiece Edition Engine case kMystStack: - if (_tweaksEnabled) { - if (getPlatform() == Common::kPlatformMacintosh) - flyby = "FLY_MYc"; - else - flyby = "myst flyby"; - } + if (_tweaksEnabled) + flyby = "myst flyby"; break; case kMechanicalStack: - if (getPlatform() == Common::kPlatformMacintosh) - flyby = "FLY_MEc"; - else - flyby = "mech age flyby"; + flyby = "mech age flyby"; break; case kChannelwoodStack: - if (getPlatform() == Common::kPlatformMacintosh) - flyby = "FLY_CHc"; - else - flyby = "channelwood flyby"; + flyby = "channelwood flyby"; break; default: break; @@ -553,7 +523,7 @@ void MohawkEngine_Myst::changeToStack(uint16 stack, uint16 card, uint16 linkSrcS _video->playMovieBlockingCentered(wrapMovieFilename(flyby, kMasterpieceOnly)); } - changeToCard(card, true); + changeToCard(card, kTransitionCopy); if (linkDstSound) _sound->playSoundBlocking(linkDstSound); @@ -579,7 +549,7 @@ void MohawkEngine_Myst::drawCardBackground() { _gfx->copyImageToBackBuffer(getCardBackgroundId(), Common::Rect(0, 0, 544, 332)); } -void MohawkEngine_Myst::changeToCard(uint16 card, bool updateScreen) { +void MohawkEngine_Myst::changeToCard(uint16 card, TransitionType transition) { debug(2, "changeToCard(%d)", card); _scriptParser->disablePersistentScripts(); @@ -659,9 +629,11 @@ void MohawkEngine_Myst::changeToCard(uint16 card, bool updateScreen) { } // Make sure the screen is updated - if (updateScreen) { - _gfx->copyBackBufferToScreen(Common::Rect(544, 333)); - _system->updateScreen(); + if (transition != kNoTransition) { + if (!_gameState->_globals.transitions) + transition = kTransitionCopy; + + _gfx->runTransition(transition, Common::Rect(544, 333), 10, 0); } // Make sure we have the right cursor showing @@ -1209,41 +1181,41 @@ bool MohawkEngine_Myst::canSaveGameStateCurrently() { } void MohawkEngine_Myst::dropPage() { - uint16 page = _gameState->_globals.heldPage; + uint16 page = _gameState->_globals.heldPage; bool whitePage = page == 13; bool bluePage = page - 1 < 6; - bool redPage = page - 7 < 6; - - // Play drop page sound - _sound->replaceSoundMyst(800); - - // Drop page - _gameState->_globals.heldPage = 0; - - // Redraw page area - if (whitePage && _gameState->_globals.currentAge == 2) { - redrawArea(41); - } else if (bluePage) { - if (page == 6) { - if (_gameState->_globals.currentAge == 2) - redrawArea(24); - } else { - redrawArea(103); - } - } else if (redPage) { - if (page == 12) { - if (_gameState->_globals.currentAge == 2) - redrawArea(25); - } else if (page == 10) { - if (_gameState->_globals.currentAge == 1) - redrawArea(35); - } else { - redrawArea(102); - } - } - - setMainCursor(kDefaultMystCursor); - checkCursorHints(); + bool redPage = page - 7 < 6; + + // Play drop page sound + _sound->replaceSoundMyst(800); + + // Drop page + _gameState->_globals.heldPage = 0; + + // Redraw page area + if (whitePage && _gameState->_globals.currentAge == 2) { + redrawArea(41); + } else if (bluePage) { + if (page == 6) { + if (_gameState->_globals.currentAge == 2) + redrawArea(24); + } else { + redrawArea(103); + } + } else if (redPage) { + if (page == 12) { + if (_gameState->_globals.currentAge == 2) + redrawArea(25); + } else if (page == 10) { + if (_gameState->_globals.currentAge == 1) + redrawArea(35); + } else { + redrawArea(102); + } + } + + setMainCursor(kDefaultMystCursor); + checkCursorHints(); } } // End of namespace Mohawk diff --git a/engines/mohawk/myst.h b/engines/mohawk/myst.h index 30770f7ec9..a268c19737 100644 --- a/engines/mohawk/myst.h +++ b/engines/mohawk/myst.h @@ -75,6 +75,23 @@ enum { kStoneshipStack // Stoneship Age }; +// Transitions +enum TransitionType { + kTransitionLeftToRight = 0, + kTransitionRightToLeft = 1, + kTransitionSlideToLeft = 2, + kTransitionSlideToRight = 3, + kTransitionDissolve = 4, + kTransitionTopToBottom = 5, + kTransitionBottomToTop = 6, + kTransitionSlideToTop = 7, + kTransitionSlideToBottom= 8, + kTransitionPartToRight = 9, + kTransitionPartToLeft = 10, + kTransitionCopy = 11, + kNoTransition = 999 +}; + const uint16 kMasterpieceOnly = 0xFFFF; struct MystCondition { @@ -154,7 +171,7 @@ public: void reloadSaveList(); void changeToStack(uint16 stack, uint16 card, uint16 linkSrcSound, uint16 linkDstSound); - void changeToCard(uint16 card, bool updateScreen); + void changeToCard(uint16 card, TransitionType transition); uint16 getCurCard() { return _curCard; } uint16 getCurStack() { return _curStack; } void setMainCursor(uint16 cursor); diff --git a/engines/mohawk/myst_areas.cpp b/engines/mohawk/myst_areas.cpp index a54b67bef4..12a2c7f44c 100644 --- a/engines/mohawk/myst_areas.cpp +++ b/engines/mohawk/myst_areas.cpp @@ -70,10 +70,30 @@ MystResource::~MystResource() { } void MystResource::handleMouseUp() { - if (_dest != 0) - _vm->changeToCard(_dest, true); - else + if (_dest == 0) { warning("Movement type resource with null destination at position (%d, %d), (%d, %d)", _rect.left, _rect.top, _rect.right, _rect.bottom); + return; + } + + uint16 opcode; + + switch (type) { + case kMystForwardArea: + opcode = 6; + break; + case kMystLeftArea: + opcode = 8; + break; + case kMystRightArea: + opcode = 7; + break; + default: + opcode = 48; + break; + } + + _vm->_scriptParser->setInvokingResource(this); + _vm->_scriptParser->runOpcode(opcode, 0); } bool MystResource::canBecomeActive() { @@ -202,22 +222,23 @@ VideoHandle MystResourceType6::playMovie() { // Check if the video is already running VideoHandle handle = _vm->_video->findVideoHandle(_videoFile); - if (_direction != 1) - warning("Playing QT movies backwards is not implemented"); - // If the video is not running, play it if (handle == NULL_VID_HANDLE || _vm->_video->endOfVideo(handle)) { - if (_playBlocking) { - _vm->_video->playMovieBlocking(_videoFile, _left, _top); - handle = NULL_VID_HANDLE; - } else { - handle = _vm->_video->playMovie(_videoFile, _left, _top, _loop); + handle = _vm->_video->playMovie(_videoFile, _left, _top, _loop); + if (_direction == -1) { + _vm->_video->seekToTime(handle, _vm->_video->getDuration(handle)); + _vm->_video->setVideoRate(handle, -1); } } else { // Resume the video _vm->_video->pauseMovie(handle, false); } + if (_playBlocking) { + _vm->_video->waitUntilMovieEnds(handle); + handle = NULL_VID_HANDLE; + } + return handle; } diff --git a/engines/mohawk/myst_graphics.cpp b/engines/mohawk/myst_graphics.cpp index ae80dd5538..6a292c66e2 100644 --- a/engines/mohawk/myst_graphics.cpp +++ b/engines/mohawk/myst_graphics.cpp @@ -49,8 +49,6 @@ MystGraphics::MystGraphics(MohawkEngine_Myst* vm) : GraphicsManager(), _vm(vm) { if (_pixelFormat.bytesPerPixel == 1) error("Myst requires greater than 256 colors to run"); - _pictureFile.entries = NULL; - // Initialize our buffer _backBuffer = new Graphics::Surface(); _backBuffer->create(_vm->_system->getWidth(), _vm->_system->getHeight(), _pixelFormat); @@ -61,122 +59,50 @@ MystGraphics::MystGraphics(MohawkEngine_Myst* vm) : GraphicsManager(), _vm(vm) { MystGraphics::~MystGraphics() { delete _bmpDecoder; - delete[] _pictureFile.entries; _backBuffer->free(); delete _backBuffer; } -static const char *s_picFileNames[] = { - "CHpics", - "", - "", - "DUpics", - "INpics", - "", - "MEpics", - "MYpics", - "SEpics", - "", - "", - "STpics" -}; - -void MystGraphics::loadExternalPictureFile(uint16 stack) { - if (_vm->getPlatform() != Common::kPlatformMacintosh) - return; - - if (_pictureFile.picFile.isOpen()) - _pictureFile.picFile.close(); - delete[] _pictureFile.entries; - - if (!scumm_stricmp(s_picFileNames[stack], "")) - return; - - if (!_pictureFile.picFile.open(s_picFileNames[stack])) - error ("Could not open external picture file \'%s\'", s_picFileNames[stack]); - - _pictureFile.pictureCount = _pictureFile.picFile.readUint32BE(); - _pictureFile.entries = new PictureFile::PictureEntry[_pictureFile.pictureCount]; - - for (uint32 i = 0; i < _pictureFile.pictureCount; i++) { - _pictureFile.entries[i].offset = _pictureFile.picFile.readUint32BE(); - _pictureFile.entries[i].size = _pictureFile.picFile.readUint32BE(); - _pictureFile.entries[i].id = _pictureFile.picFile.readUint16BE(); - _pictureFile.entries[i].type = _pictureFile.picFile.readUint16BE(); - _pictureFile.entries[i].width = _pictureFile.picFile.readUint16BE(); - _pictureFile.entries[i].height = _pictureFile.picFile.readUint16BE(); +MohawkSurface *MystGraphics::decodeImage(uint16 id) { + // We need to grab the image from the current stack archive, however, we don't know + // if it's a PICT or WDIB resource. If it's Myst ME it's most likely a PICT, and if it's + // original it's definitely a WDIB. However, Myst ME throws us another curve ball in + // that PICT resources can contain WDIB's instead of PICT's. + Common::SeekableReadStream *dataStream = NULL; + + if (_vm->getFeatures() & GF_ME && _vm->hasResource(ID_PICT, id)) { + // The PICT resource exists. However, it could still contain a MystBitmap + // instead of a PICT image... + dataStream = _vm->getResource(ID_PICT, id); + } else { + // No PICT, so the WDIB must exist. Let's go grab it. + dataStream = _vm->getResource(ID_WDIB, id); } -} -MohawkSurface *MystGraphics::decodeImage(uint16 id) { - MohawkSurface *mhkSurface = 0; + bool isPict = false; - // Myst ME uses JPEG/PICT images instead of compressed Windows Bitmaps for room images, - // though there are a few weird ones that use that format. For further nonsense with images, - // the Macintosh version stores images in external "picture files." We check them before - // going to check for a PICT resource. - if (_vm->getFeatures() & GF_ME && _vm->getPlatform() == Common::kPlatformMacintosh && _pictureFile.picFile.isOpen()) { - for (uint32 i = 0; i < _pictureFile.pictureCount; i++) - if (_pictureFile.entries[i].id == id) { - if (_pictureFile.entries[i].type == 0) { - Graphics::JPEGDecoder jpeg; - Common::SeekableSubReadStream subStream(&_pictureFile.picFile, _pictureFile.entries[i].offset, _pictureFile.entries[i].offset + _pictureFile.entries[i].size); - - if (!jpeg.loadStream(subStream)) - error("Could not decode Myst ME Mac JPEG"); - - mhkSurface = new MohawkSurface(jpeg.getSurface()->convertTo(_pixelFormat)); - } else if (_pictureFile.entries[i].type == 1) { - Graphics::PICTDecoder pict; - Common::SeekableSubReadStream subStream(&_pictureFile.picFile, _pictureFile.entries[i].offset, _pictureFile.entries[i].offset + _pictureFile.entries[i].size); - - if (!pict.loadStream(subStream)) - error("Could not decode Myst ME Mac PICT"); - - mhkSurface = new MohawkSurface(pict.getSurface()->convertTo(_pixelFormat)); - } else - error ("Unknown Picture File type %d", _pictureFile.entries[i].type); - break; - } + if (_vm->getFeatures() & GF_ME) { + // Here we detect whether it's really a PICT or a WDIB. Since a MystBitmap + // would be compressed, there's no way to detect for the BM without a hack. + // So, we search for the PICT version opcode for detection. + dataStream->seek(512 + 10); // 512 byte pict header + isPict = (dataStream->readUint32BE() == 0x001102FF); + dataStream->seek(0); } - // We're not using the external Mac files, so it's time to delve into the main Mohawk - // archives. However, we still don't know if it's a PICT or WDIB resource. If it's Myst - // ME it's most likely a PICT, and if it's original it's definitely a WDIB. However, - // Myst ME throws us another curve ball in that PICT resources can contain WDIB's instead - // of PICT's. - if (!mhkSurface) { - bool isPict = false; - Common::SeekableReadStream *dataStream = NULL; - - if (_vm->getFeatures() & GF_ME && _vm->hasResource(ID_PICT, id)) { - // The PICT resource exists. However, it could still contain a MystBitmap - // instead of a PICT image... - dataStream = _vm->getResource(ID_PICT, id); - } else // No PICT, so the WDIB must exist. Let's go grab it. - dataStream = _vm->getResource(ID_WDIB, id); - - if (_vm->getFeatures() & GF_ME) { - // Here we detect whether it's really a PICT or a WDIB. Since a MystBitmap - // would be compressed, there's no way to detect for the BM without a hack. - // So, we search for the PICT version opcode for detection. - dataStream->seek(512 + 10); // 512 byte pict header - isPict = (dataStream->readUint32BE() == 0x001102FF); - dataStream->seek(0); - } + MohawkSurface *mhkSurface = 0; - if (isPict) { - Graphics::PICTDecoder pict; + if (isPict) { + Graphics::PICTDecoder pict; - if (!pict.loadStream(*dataStream)) - error("Could not decode Myst ME PICT"); + if (!pict.loadStream(*dataStream)) + error("Could not decode Myst ME PICT"); - mhkSurface = new MohawkSurface(pict.getSurface()->convertTo(_pixelFormat)); - } else { - mhkSurface = _bmpDecoder->decodeImage(dataStream); - mhkSurface->convertToTrueColor(); - } + mhkSurface = new MohawkSurface(pict.getSurface()->convertTo(_pixelFormat)); + } else { + mhkSurface = _bmpDecoder->decodeImage(dataStream); + mhkSurface->convertToTrueColor(); } assert(mhkSurface); @@ -283,15 +209,15 @@ void MystGraphics::copyBackBufferToScreen(Common::Rect r) { _vm->_system->copyRectToScreen(_backBuffer->getBasePtr(r.left, r.top), _backBuffer->pitch, r.left, r.top, r.width(), r.height()); } -void MystGraphics::runTransition(uint16 type, Common::Rect rect, uint16 steps, uint16 delay) { +void MystGraphics::runTransition(TransitionType type, Common::Rect rect, uint16 steps, uint16 delay) { // Do not artificially delay during transitions int oldEnableDrawingTimeSimulation = _enableDrawingTimeSimulation; _enableDrawingTimeSimulation = 0; switch (type) { - case 0: { - debugC(kDebugScript, "Left to Right"); + case kTransitionLeftToRight: { + debugC(kDebugView, "Left to Right"); uint16 step = (rect.right - rect.left) / steps; Common::Rect area = rect; @@ -313,8 +239,8 @@ void MystGraphics::runTransition(uint16 type, Common::Rect rect, uint16 steps, u } } break; - case 1: { - debugC(kDebugScript, "Right to Left"); + case kTransitionRightToLeft: { + debugC(kDebugView, "Right to Left"); uint16 step = (rect.right - rect.left) / steps; Common::Rect area = rect; @@ -336,8 +262,25 @@ void MystGraphics::runTransition(uint16 type, Common::Rect rect, uint16 steps, u } } break; - case 5: { - debugC(kDebugScript, "Top to Bottom"); + case kTransitionSlideToLeft: + debugC(kDebugView, "Slide to left"); + transitionSlideToLeft(rect, steps, delay); + break; + case kTransitionSlideToRight: + debugC(kDebugView, "Slide to right"); + transitionSlideToRight(rect, steps, delay); + break; + case kTransitionDissolve: { + debugC(kDebugView, "Dissolve"); + + for (int16 step = 0; step < 8; step++) { + simulatePreviousDrawDelay(rect); + transitionDissolve(rect, step); + } + } + break; + case kTransitionTopToBottom: { + debugC(kDebugView, "Top to Bottom"); uint16 step = (rect.bottom - rect.top) / steps; Common::Rect area = rect; @@ -359,8 +302,8 @@ void MystGraphics::runTransition(uint16 type, Common::Rect rect, uint16 steps, u } } break; - case 6: { - debugC(kDebugScript, "Bottom to Top"); + case kTransitionBottomToTop: { + debugC(kDebugView, "Bottom to Top"); uint16 step = (rect.bottom - rect.top) / steps; Common::Rect area = rect; @@ -382,18 +325,260 @@ void MystGraphics::runTransition(uint16 type, Common::Rect rect, uint16 steps, u } } break; - default: - warning("Unknown Update Direction"); + case kTransitionSlideToTop: + debugC(kDebugView, "Slide to top"); + transitionSlideToTop(rect, steps, delay); + break; + case kTransitionSlideToBottom: + debugC(kDebugView, "Slide to bottom"); + transitionSlideToBottom(rect, steps, delay); + break; + case kTransitionPartToRight: { + debugC(kDebugView, "Partial left to right"); - //TODO: Replace minimal implementation + transitionPartialToRight(rect, 75, 3); + } + break; + case kTransitionPartToLeft: { + debugC(kDebugView, "Partial right to left"); + + transitionPartialToLeft(rect, 75, 3); + } + break; + case kTransitionCopy: copyBackBufferToScreen(rect); _vm->_system->updateScreen(); break; + default: + error("Unknown transition %d", type); } _enableDrawingTimeSimulation = oldEnableDrawingTimeSimulation; } +void MystGraphics::transitionDissolve(Common::Rect rect, uint step) { + static const bool pattern[][4][4] = { + { + { true, false, false, false }, + { false, false, false, false }, + { false, false, true, false }, + { false, false, false, false } + }, + { + { false, false, true, false }, + { false, false, false, false }, + { true, false, false, false }, + { false, false, false, false } + }, + { + { false, false, false, false }, + { false, true, false, false }, + { false, false, false, false }, + { false, false, false, true } + }, + { + { false, false, false, false }, + { false, false, false, true }, + { false, false, false, false }, + { false, true, false, false } + }, + { + { false, false, false, false }, + { false, false, true, false }, + { false, true, false, false }, + { false, false, false, false } + }, + { + { false, true, false, false }, + { false, false, false, false }, + { false, false, false, false }, + { false, false, true, false } + }, + { + { false, false, false, false }, + { true, false, false, false }, + { false, false, false, true }, + { false, false, false, false } + }, + { + { false, false, false, true }, + { false, false, false, false }, + { false, false, false, false }, + { true, false, false, false } + } + }; + + rect.clip(_viewport); + + Graphics::Surface *screen = _vm->_system->lockScreen(); + + for (uint16 y = rect.top; y < rect.bottom; y++) { + const bool *linePattern = pattern[step][y % 4]; + + if (!linePattern[0] && !linePattern[1] && !linePattern[2] && !linePattern[3]) + continue; + + for (uint16 x = rect.left; x < rect.right; x++) { + if (linePattern[x % 4]) { + if (_pixelFormat.bytesPerPixel == 2) { + uint16 *dst = (uint16 *)screen->getBasePtr(x, y); + *dst = *(const uint16 *)_backBuffer->getBasePtr(x, y); + } else { + uint32 *dst = (uint32 *)screen->getBasePtr(x, y); + *dst = *(const uint32 *)_backBuffer->getBasePtr(x, y); + } + } + } + } + + _vm->_system->unlockScreen(); + _vm->_system->updateScreen(); +} + +void MystGraphics::transitionSlideToLeft(Common::Rect rect, uint16 steps, uint16 delay) { + rect.clip(_viewport); + + uint32 stepWidth = (rect.right - rect.left) / steps; + Common::Rect srcRect = Common::Rect(rect.right, rect.top, rect.right, rect.bottom); + Common::Rect dstRect = Common::Rect(rect.left, rect.top, rect.left, rect.bottom); + + for (uint step = 1; step <= steps; step++) { + dstRect.right = dstRect.left + step * stepWidth; + srcRect.left = srcRect.right - step * stepWidth; + + _vm->_system->delayMillis(delay); + + simulatePreviousDrawDelay(dstRect); + _vm->_system->copyRectToScreen(_backBuffer->getBasePtr(dstRect.left, dstRect.top), + _backBuffer->pitch, srcRect.left, srcRect.top, srcRect.width(), srcRect.height()); + _vm->_system->updateScreen(); + } + + if (dstRect.right != rect.right) { + copyBackBufferToScreen(rect); + _vm->_system->updateScreen(); + } +} + +void MystGraphics::transitionSlideToRight(Common::Rect rect, uint16 steps, uint16 delay) { + rect.clip(_viewport); + + uint32 stepWidth = (rect.right - rect.left) / steps; + Common::Rect srcRect = Common::Rect(rect.left, rect.top, rect.left, rect.bottom); + Common::Rect dstRect = Common::Rect(rect.right, rect.top, rect.right, rect.bottom); + + for (uint step = 1; step <= steps; step++) { + dstRect.left = dstRect.right - step * stepWidth; + srcRect.right = srcRect.left + step * stepWidth; + + _vm->_system->delayMillis(delay); + + simulatePreviousDrawDelay(dstRect); + _vm->_system->copyRectToScreen(_backBuffer->getBasePtr(dstRect.left, dstRect.top), + _backBuffer->pitch, srcRect.left, srcRect.top, srcRect.width(), srcRect.height()); + _vm->_system->updateScreen(); + } + + if (dstRect.left != rect.left) { + copyBackBufferToScreen(rect); + _vm->_system->updateScreen(); + } +} + +void MystGraphics::transitionSlideToTop(Common::Rect rect, uint16 steps, uint16 delay) { + rect.clip(_viewport); + + uint32 stepWidth = (rect.bottom - rect.top) / steps; + Common::Rect srcRect = Common::Rect(rect.left, rect.bottom, rect.right, rect.bottom); + Common::Rect dstRect = Common::Rect(rect.left, rect.top, rect.right, rect.top); + + for (uint step = 1; step <= steps; step++) { + dstRect.bottom = dstRect.top + step * stepWidth; + srcRect.top = srcRect.bottom - step * stepWidth; + + _vm->_system->delayMillis(delay); + + simulatePreviousDrawDelay(dstRect); + _vm->_system->copyRectToScreen(_backBuffer->getBasePtr(dstRect.left, dstRect.top), + _backBuffer->pitch, srcRect.left, srcRect.top, srcRect.width(), srcRect.height()); + _vm->_system->updateScreen(); + } + + + if (dstRect.bottom < rect.bottom) { + copyBackBufferToScreen(rect); + _vm->_system->updateScreen(); + } +} + +void MystGraphics::transitionSlideToBottom(Common::Rect rect, uint16 steps, uint16 delay) { + rect.clip(_viewport); + + uint32 stepWidth = (rect.bottom - rect.top) / steps; + Common::Rect srcRect = Common::Rect(rect.left, rect.top, rect.right, rect.top); + Common::Rect dstRect = Common::Rect(rect.left, rect.bottom, rect.right, rect.bottom); + + for (uint step = 1; step <= steps; step++) { + dstRect.top = dstRect.bottom - step * stepWidth; + srcRect.bottom = srcRect.top + step * stepWidth; + + _vm->_system->delayMillis(delay); + + simulatePreviousDrawDelay(dstRect); + _vm->_system->copyRectToScreen(_backBuffer->getBasePtr(dstRect.left, dstRect.top), + _backBuffer->pitch, srcRect.left, srcRect.top, srcRect.width(), srcRect.height()); + _vm->_system->updateScreen(); + } + + + if (dstRect.top > rect.top) { + copyBackBufferToScreen(rect); + _vm->_system->updateScreen(); + } +} + +void MystGraphics::transitionPartialToRight(Common::Rect rect, uint32 width, uint32 steps) { + rect.clip(_viewport); + + uint32 stepWidth = width / steps; + Common::Rect srcRect = Common::Rect(rect.right, rect.top, rect.right, rect.bottom); + Common::Rect dstRect = Common::Rect(rect.left, rect.top, rect.left, rect.bottom); + + for (uint step = 1; step <= steps; step++) { + dstRect.right = dstRect.left + step * stepWidth; + srcRect.left = srcRect.right - step * stepWidth; + + simulatePreviousDrawDelay(dstRect); + _vm->_system->copyRectToScreen(_backBuffer->getBasePtr(dstRect.left, dstRect.top), + _backBuffer->pitch, srcRect.left, srcRect.top, srcRect.width(), srcRect.height()); + _vm->_system->updateScreen(); + } + + copyBackBufferToScreen(rect); + _vm->_system->updateScreen(); +} + +void MystGraphics::transitionPartialToLeft(Common::Rect rect, uint32 width, uint32 steps) { + rect.clip(_viewport); + + uint32 stepWidth = width / steps; + Common::Rect srcRect = Common::Rect(rect.left, rect.top, rect.left, rect.bottom); + Common::Rect dstRect = Common::Rect(rect.right, rect.top, rect.right, rect.bottom); + + for (uint step = 1; step <= steps; step++) { + dstRect.left = dstRect.right - step * stepWidth; + srcRect.right = srcRect.left + step * stepWidth; + + simulatePreviousDrawDelay(dstRect); + _vm->_system->copyRectToScreen(_backBuffer->getBasePtr(dstRect.left, dstRect.top), + _backBuffer->pitch, srcRect.left, srcRect.top, srcRect.width(), srcRect.height()); + _vm->_system->updateScreen(); + } + + copyBackBufferToScreen(rect); + _vm->_system->updateScreen(); +} + void MystGraphics::drawRect(Common::Rect rect, RectState state) { rect.clip(_viewport); diff --git a/engines/mohawk/myst_graphics.h b/engines/mohawk/myst_graphics.h index 20fd46c5b9..4bbc8d5b8c 100644 --- a/engines/mohawk/myst_graphics.h +++ b/engines/mohawk/myst_graphics.h @@ -43,13 +43,12 @@ public: MystGraphics(MohawkEngine_Myst*); ~MystGraphics(); - void loadExternalPictureFile(uint16 stack); void copyImageSectionToScreen(uint16 image, Common::Rect src, Common::Rect dest); void copyImageSectionToBackBuffer(uint16 image, Common::Rect src, Common::Rect dest); void copyImageToScreen(uint16 image, Common::Rect dest); void copyImageToBackBuffer(uint16 image, Common::Rect dest); void copyBackBufferToScreen(Common::Rect r); - void runTransition(uint16 type, Common::Rect rect, uint16 steps, uint16 delay); + void runTransition(TransitionType type, Common::Rect rect, uint16 steps, uint16 delay); void drawRect(Common::Rect rect, RectState state); void drawLine(const Common::Point &p1, const Common::Point &p2, uint32 color); void enableDrawingTimeSimulation(bool enable); @@ -61,25 +60,17 @@ protected: MohawkEngine *getVM() { return (MohawkEngine *)_vm; } void simulatePreviousDrawDelay(const Common::Rect &dest); void copyBackBufferToScreenWithSaturation(int16 saturation); - + void transitionDissolve(Common::Rect rect, uint step); + void transitionSlideToLeft(Common::Rect rect, uint16 steps, uint16 delay); + void transitionSlideToRight(Common::Rect rect, uint16 steps, uint16 delay); + void transitionSlideToTop(Common::Rect rect, uint16 steps, uint16 delay); + void transitionSlideToBottom(Common::Rect rect, uint16 steps, uint16 delay); + void transitionPartialToRight(Common::Rect rect, uint32 width, uint32 steps); + void transitionPartialToLeft(Common::Rect rect, uint32 width, uint32 steps); private: MohawkEngine_Myst *_vm; MystBitmap *_bmpDecoder; - struct PictureFile { - uint32 pictureCount; - struct PictureEntry { - uint32 offset; - uint32 size; - uint16 id; - uint16 type; - uint16 width; - uint16 height; - } *entries; - - Common::File picFile; - } _pictureFile; - Graphics::Surface *_backBuffer; Graphics::PixelFormat _pixelFormat; Common::Rect _viewport; diff --git a/engines/mohawk/myst_scripts.cpp b/engines/mohawk/myst_scripts.cpp index 107a8b03e9..c1b75df4cf 100644 --- a/engines/mohawk/myst_scripts.cpp +++ b/engines/mohawk/myst_scripts.cpp @@ -100,18 +100,18 @@ void MystScriptParser::setupCommonOpcodes() { // "Standard" Opcodes OPCODE(0, o_toggleVar); OPCODE(1, o_setVar); - OPCODE(2, o_changeCardSwitch); + OPCODE(2, o_changeCardSwitch4); OPCODE(3, o_takePage); OPCODE(4, o_redrawCard); // Opcode 5 Not Present - OPCODE(6, o_goToDest); - OPCODE(7, o_goToDest); - OPCODE(8, o_goToDest); + OPCODE(6, o_goToDestForward); + OPCODE(7, o_goToDestLeft); + OPCODE(8, o_goToDestRight); OPCODE(9, o_triggerMovie); OPCODE(10, o_toggleVarNoRedraw); // Opcode 11 Not Present - OPCODE(12, o_changeCardSwitch); - OPCODE(13, o_changeCardSwitch); + OPCODE(12, o_changeCardSwitchLtR); + OPCODE(13, o_changeCardSwitchRtL); OPCODE(14, o_drawAreaState); OPCODE(15, o_redrawAreaForVar); OPCODE(16, o_changeCardDirectional); @@ -120,7 +120,7 @@ void MystScriptParser::setupCommonOpcodes() { OPCODE(19, o_enableAreas); OPCODE(20, o_disableAreas); OPCODE(21, o_directionalUpdate); - OPCODE(22, o_goToDest); + OPCODE(22, o_goToDestUp); OPCODE(23, o_toggleAreasActivation); OPCODE(24, o_playSound); // Opcode 25 is unused; original calls replaceSoundMyst @@ -145,6 +145,7 @@ void MystScriptParser::setupCommonOpcodes() { OPCODE(44, o_restoreMainCursor); // Opcode 45 Not Present OPCODE(46, o_soundWaitStop); + OPCODE(48, o_goToDest); OPCODE(51, o_exitMap); // Opcodes 47 to 99 Not Present @@ -273,7 +274,7 @@ void MystScriptParser::animatedUpdate(uint16 argc, uint16 *argv, uint16 delay) { while (argsRead < argc) { Common::Rect rect = Common::Rect(argv[argsRead], argv[argsRead + 1], argv[argsRead + 2], argv[argsRead + 3]); - uint16 kind = argv[argsRead + 4]; + TransitionType kind = static_cast<TransitionType>(argv[argsRead + 4]); uint16 steps = argv[argsRead + 5]; debugC(kDebugScript, "\trect.left: %d", rect.left); @@ -323,16 +324,41 @@ void MystScriptParser::o_setVar(uint16 op, uint16 var, uint16 argc, uint16 *argv _vm->redrawArea(var); } -void MystScriptParser::o_changeCardSwitch(uint16 op, uint16 var, uint16 argc, uint16 *argv) { - // Opcodes 2, 12, and 13 are the same +void MystScriptParser::o_changeCardSwitch4(uint16 op, uint16 var, uint16 argc, uint16 *argv) { uint16 value = getVar(var); debugC(kDebugScript, "Opcode %d: changeCardSwitch var %d: %d", op, var, value); if (value) - _vm->changeToCard(argv[value -1 ], true); + _vm->changeToCard(argv[value -1 ], kTransitionDissolve); else if (_invokingResource != NULL) - _vm->changeToCard(_invokingResource->getDest(), true); + _vm->changeToCard(_invokingResource->getDest(), kTransitionDissolve); + else + warning("Missing invokingResource in altDest call"); +} + +void MystScriptParser::o_changeCardSwitchLtR(uint16 op, uint16 var, uint16 argc, uint16 *argv) { + uint16 value = getVar(var); + + debugC(kDebugScript, "Opcode %d: changeCardSwitch var %d: %d", op, var, value); + + if (value) + _vm->changeToCard(argv[value -1 ], kTransitionLeftToRight); + else if (_invokingResource != NULL) + _vm->changeToCard(_invokingResource->getDest(), kTransitionLeftToRight); + else + warning("Missing invokingResource in altDest call"); +} + +void MystScriptParser::o_changeCardSwitchRtL(uint16 op, uint16 var, uint16 argc, uint16 *argv) { + uint16 value = getVar(var); + + debugC(kDebugScript, "Opcode %d: changeCardSwitch var %d: %d", op, var, value); + + if (value) + _vm->changeToCard(argv[value -1 ], kTransitionRightToLeft); + else if (_invokingResource != NULL) + _vm->changeToCard(_invokingResource->getDest(), kTransitionRightToLeft); else warning("Missing invokingResource in altDest call"); } @@ -373,10 +399,47 @@ void MystScriptParser::o_goToDest(uint16 op, uint16 var, uint16 argc, uint16 *ar debugC(kDebugScript, "Opcode %d: Change To Dest of Invoking Resource", op); if (_invokingResource != NULL) - _vm->changeToCard(_invokingResource->getDest(), true); + _vm->changeToCard(_invokingResource->getDest(), kTransitionCopy); + else + warning("Opcode %d: Missing invokingResource", op); +} + +void MystScriptParser::o_goToDestForward(uint16 op, uint16 var, uint16 argc, uint16 *argv) { + debugC(kDebugScript, "Opcode %d: Change To Dest of Invoking Resource", op); + + if (_invokingResource != NULL) + _vm->changeToCard(_invokingResource->getDest(), kTransitionDissolve); + else + warning("Opcode %d: Missing invokingResource", op); +} + +void MystScriptParser::o_goToDestLeft(uint16 op, uint16 var, uint16 argc, uint16 *argv) { + debugC(kDebugScript, "Opcode %d: Change To Dest of Invoking Resource", op); + + if (_invokingResource != NULL) + _vm->changeToCard(_invokingResource->getDest(), kTransitionPartToRight); + else + warning("Opcode %d: Missing invokingResource", op); +} + +void MystScriptParser::o_goToDestRight(uint16 op, uint16 var, uint16 argc, uint16 *argv) { + debugC(kDebugScript, "Opcode %d: Change To Dest of Invoking Resource", op); + + if (_invokingResource != NULL) + _vm->changeToCard(_invokingResource->getDest(), kTransitionPartToLeft); else warning("Opcode %d: Missing invokingResource", op); } + +void MystScriptParser::o_goToDestUp(uint16 op, uint16 var, uint16 argc, uint16 *argv) { + debugC(kDebugScript, "Opcode %d: Change To Dest of Invoking Resource", op); + + if (_invokingResource != NULL) + _vm->changeToCard(_invokingResource->getDest(), kTransitionTopToBottom); + else + warning("Opcode %d: Missing invokingResource", op); +} + void MystScriptParser::o_triggerMovie(uint16 op, uint16 var, uint16 argc, uint16 *argv) { debugC(kDebugScript, "Opcode %d: Trigger Type 6 Resource Movie..", op); // TODO: If movie has sound, pause background music @@ -427,7 +490,7 @@ void MystScriptParser::o_changeCardDirectional(uint16 op, uint16 var, uint16 arg debugC(kDebugScript, "\tcardId: %d", cardId); debugC(kDebugScript, "\tdirectonal update data size: %d", directionalUpdateDataSize); - _vm->changeToCard(cardId, false); + _vm->changeToCard(cardId, kNoTransition); animatedUpdate(directionalUpdateDataSize, &argv[2], 0); } @@ -440,23 +503,23 @@ void MystScriptParser::o_changeCardPush(uint16 op, uint16 var, uint16 argc, uint debugC(kDebugScript, "Opcode %d: Jump to Card Id, Storing Current Card Id", op); _savedCardId = _vm->getCurCard(); - uint16 cardId = argv[0]; - // argv[1] is not used in the original engine + uint16 cardId = argv[0]; + TransitionType transition = static_cast<TransitionType>(argv[1]); debugC(kDebugScript, "\tCurrent CardId: %d", _savedCardId); debugC(kDebugScript, "\tJump to CardId: %d", cardId); - _vm->changeToCard(cardId, true); + _vm->changeToCard(cardId, transition); } void MystScriptParser::o_changeCardPop(uint16 op, uint16 var, uint16 argc, uint16 *argv) { debugC(kDebugScript, "Opcode %d: Return To Stored Card Id", op); debugC(kDebugScript, "\tCardId: %d", _savedCardId); - // argv[0] is not used in the original engine + TransitionType transition = static_cast<TransitionType>(argv[0]); - _vm->changeToCard(_savedCardId, true); + _vm->changeToCard(_savedCardId, transition); } void MystScriptParser::o_enableAreas(uint16 op, uint16 var, uint16 argc, uint16 *argv) { @@ -752,14 +815,11 @@ void MystScriptParser::o_changeCard(uint16 op, uint16 var, uint16 argc, uint16 * debugC(kDebugScript, "Opcode %d: Change Card", op); uint16 cardId = argv[0]; - - // Argument 1 if present is not used - // uint16 u0 = argv[1]; + TransitionType transition = static_cast<TransitionType>(argv[1]); debugC(kDebugScript, "\tTarget Card: %d", cardId); - //debugC(kDebugScript, "\tu0: %d", u0); // Unused data - _vm->changeToCard(cardId, true); + _vm->changeToCard(cardId, transition); } void MystScriptParser::o_drawImageChangeCard(uint16 op, uint16 var, uint16 argc, uint16 *argv) { @@ -767,7 +827,7 @@ void MystScriptParser::o_drawImageChangeCard(uint16 op, uint16 var, uint16 argc, uint16 imageId = argv[0]; uint16 cardId = argv[1]; - // argv[2] is not used in the original engine + TransitionType transition = static_cast<TransitionType>(argv[2]); debugC(kDebugScript, "\timageId: %d", imageId); debugC(kDebugScript, "\tcardId: %d", cardId); @@ -775,7 +835,7 @@ void MystScriptParser::o_drawImageChangeCard(uint16 op, uint16 var, uint16 argc, _vm->_gfx->copyImageToScreen(imageId, Common::Rect(0, 0, 544, 333)); _vm->_system->updateScreen(); - _vm->changeToCard(cardId, true); + _vm->changeToCard(cardId, transition); } void MystScriptParser::o_changeMainCursor(uint16 op, uint16 var, uint16 argc, uint16 *argv) { @@ -850,7 +910,7 @@ void MystScriptParser::o_changeCardPlaySoundDirectional(uint16 op, uint16 var, u if (soundId) _vm->_sound->replaceSoundMyst(soundId); - _vm->changeToCard(cardId, false); + _vm->changeToCard(cardId, kNoTransition); animatedUpdate(dataSize, &argv[4], delayBetweenSteps); } @@ -901,12 +961,12 @@ void MystScriptParser::o_quit(uint16 op, uint16 var, uint16 argc, uint16 *argv) void MystScriptParser::showMap() { if (_vm->getCurCard() != getMap()) { _savedMapCardId = _vm->getCurCard(); - _vm->changeToCard(getMap(), true); + _vm->changeToCard(getMap(), kTransitionCopy); } } void MystScriptParser::o_exitMap(uint16 op, uint16 var, uint16 argc, uint16 *argv) { - _vm->changeToCard(_savedMapCardId, true); + _vm->changeToCard(_savedMapCardId, kTransitionCopy); } } // End of namespace Mohawk diff --git a/engines/mohawk/myst_scripts.h b/engines/mohawk/myst_scripts.h index ccb76e0dc8..b75da0801a 100644 --- a/engines/mohawk/myst_scripts.h +++ b/engines/mohawk/myst_scripts.h @@ -86,10 +86,16 @@ public: // Common opcodes DECLARE_OPCODE(o_toggleVar); DECLARE_OPCODE(o_setVar); - DECLARE_OPCODE(o_changeCardSwitch); + DECLARE_OPCODE(o_changeCardSwitch4); + DECLARE_OPCODE(o_changeCardSwitchLtR); + DECLARE_OPCODE(o_changeCardSwitchRtL); DECLARE_OPCODE(o_takePage); DECLARE_OPCODE(o_redrawCard); DECLARE_OPCODE(o_goToDest); + DECLARE_OPCODE(o_goToDestForward); + DECLARE_OPCODE(o_goToDestLeft); + DECLARE_OPCODE(o_goToDestRight); + DECLARE_OPCODE(o_goToDestUp); DECLARE_OPCODE(o_triggerMovie); DECLARE_OPCODE(o_toggleVarNoRedraw); DECLARE_OPCODE(o_drawAreaState); diff --git a/engines/mohawk/myst_stacks/channelwood.cpp b/engines/mohawk/myst_stacks/channelwood.cpp index 069281f5dc..63ba5f7c85 100644 --- a/engines/mohawk/myst_stacks/channelwood.cpp +++ b/engines/mohawk/myst_stacks/channelwood.cpp @@ -341,7 +341,7 @@ void Channelwood::o_drawImageChangeCardAndVolume(uint16 op, uint16 var, uint16 a _vm->_gfx->copyImageToScreen(imageId, Common::Rect(0, 0, 544, 333)); _vm->_system->updateScreen(); - _vm->changeToCard(cardId, true); + _vm->changeToCard(cardId, kTransitionPartToLeft); if (argc == 3) { uint16 volume = argv[2]; @@ -476,9 +476,12 @@ void Channelwood::o_stairsDoorToggle(uint16 op, uint16 var, uint16 argc, uint16 MystResourceType6 *movie = static_cast<MystResourceType6 *>(_invokingResource); if (_state.stairsUpperDoorState) { - // TODO: Play backwards + // Close door, play the open movie backwards + movie->setDirection(-1); movie->playMovie(); } else { + // Open door + movie->setDirection(1); movie->playMovie(); } } diff --git a/engines/mohawk/myst_stacks/demo.cpp b/engines/mohawk/myst_stacks/demo.cpp index 29a12571fd..9f393ea401 100644 --- a/engines/mohawk/myst_stacks/demo.cpp +++ b/engines/mohawk/myst_stacks/demo.cpp @@ -104,14 +104,14 @@ void Demo::returnToMenu_run() { switch (_returnToMenuStep){ case 0: _vm->_gfx->fadeToBlack(); - _vm->changeToCard(2003, false); + _vm->changeToCard(2003, kNoTransition); _vm->_gfx->fadeFromBlack(); _returnToMenuStep++; break; case 1: _vm->_gfx->fadeToBlack(); - _vm->changeToCard(2001, false); + _vm->changeToCard(2001, kNoTransition); _vm->_gfx->fadeFromBlack(); _vm->_cursor->showCursor(); diff --git a/engines/mohawk/myst_stacks/dni.cpp b/engines/mohawk/myst_stacks/dni.cpp index cae165ccf0..d103105c2d 100644 --- a/engines/mohawk/myst_stacks/dni.cpp +++ b/engines/mohawk/myst_stacks/dni.cpp @@ -109,7 +109,7 @@ void Dni::o_handPage(uint16 op, uint16 var, uint16 argc, uint16 *argv) { _vm->setMainCursor(kDefaultMystCursor); // Play movie end (atrus leaving) - _vm->_video->setVideoBounds(atrus, Audio::Timestamp(0, 14813, 600), Audio::Timestamp(0xFFFFFFFF)); + _vm->_video->setVideoBounds(atrus, Audio::Timestamp(0, 14813, 600), _vm->_video->getDuration(atrus)); _vm->_video->setVideoLooping(atrus, false); _atrusLeft = true; diff --git a/engines/mohawk/myst_stacks/intro.cpp b/engines/mohawk/myst_stacks/intro.cpp index a5f608dbbf..71733227ac 100644 --- a/engines/mohawk/myst_stacks/intro.cpp +++ b/engines/mohawk/myst_stacks/intro.cpp @@ -100,12 +100,8 @@ void Intro::introMovies_run() { switch (_introStep) { case 0: - // Play the Mattel (or UbiSoft) logo in the Myst ME Mac version - if ((_vm->getFeatures() & GF_ME) && _vm->getPlatform() == Common::kPlatformMacintosh) { - _vm->_video->playMovie(_vm->wrapMovieFilename("mattel", kIntroStack)); - _introStep = 1; - } else - _introStep = 2; + _introStep = 1; + _vm->_video->playMovie(_vm->wrapMovieFilename("broder", kIntroStack)); break; case 1: if (!_vm->_video->isVideoPlaying()) @@ -113,10 +109,7 @@ void Intro::introMovies_run() { break; case 2: _introStep = 3; - if ((_vm->getFeatures() & GF_ME) && _vm->getPlatform() == Common::kPlatformMacintosh) - _vm->_video->playMovie(_vm->wrapMovieFilename("presto", kIntroStack)); - else - _vm->_video->playMovie(_vm->wrapMovieFilename("broder", kIntroStack)); + _vm->_video->playMovie(_vm->wrapMovieFilename("cyanlogo", kIntroStack)); break; case 3: if (!_vm->_video->isVideoPlaying()) @@ -124,27 +117,19 @@ void Intro::introMovies_run() { break; case 4: _introStep = 5; - _vm->_video->playMovie(_vm->wrapMovieFilename("cyanlogo", kIntroStack)); - break; - case 5: - if (!_vm->_video->isVideoPlaying()) - _introStep = 6; - break; - case 6: - _introStep = 7; if (!(_vm->getFeatures() & GF_DEMO)) // The demo doesn't have the intro video _vm->_video->playMovie(_vm->wrapMovieFilename("intro", kIntroStack)); break; - case 7: + case 5: if (!_vm->_video->isVideoPlaying()) - _introStep = 8; + _introStep = 6; break; default: if (_vm->getFeatures() & GF_DEMO) - _vm->changeToCard(2001, true); + _vm->changeToCard(2001, kTransitionRightToLeft); else - _vm->changeToCard(2, true); + _vm->changeToCard(2, kTransitionRightToLeft); } } @@ -163,7 +148,7 @@ void Intro::mystLinkBook_run() { _vm->_gfx->copyBackBufferToScreen(Common::Rect(544, 333)); } } else if (!_linkBookMovie->isPlaying()) { - _vm->changeToCard(5, true); + _vm->changeToCard(5, kTransitionRightToLeft); } } diff --git a/engines/mohawk/myst_stacks/mechanical.cpp b/engines/mohawk/myst_stacks/mechanical.cpp index 79de03308c..43e9bcfed5 100644 --- a/engines/mohawk/myst_stacks/mechanical.cpp +++ b/engines/mohawk/myst_stacks/mechanical.cpp @@ -39,8 +39,17 @@ Mechanical::Mechanical(MohawkEngine_Myst *vm) : MystScriptParser(vm), _state(vm->_gameState->_mechanical) { setupOpcodes(); + _elevatorGoingMiddle = false; + _mystStaircaseState = false; _fortressPosition = 0; + _fortressRotationSpeed = 0; + _fortressSimulationSpeed = 0; + _gearsWereRunning = false; + + _fortressRotationShortMovieWorkaround = false; + _fortressRotationShortMovieCount = 0; + _fortressRotationShortMovieLast = 0; } Mechanical::~Mechanical() { @@ -74,9 +83,9 @@ void Mechanical::setupOpcodes() { OPCODE(121, o_elevatorWindowMovie); OPCODE(122, o_elevatorGoMiddle); OPCODE(123, o_elevatorTopMovie); - OPCODE(124, opcode_124); + OPCODE(124, o_fortressRotationSetPosition); OPCODE(125, o_mystStaircaseMovie); - OPCODE(126, opcode_126); + OPCODE(126, o_elevatorWaitTimeout); OPCODE(127, o_crystalEnterYellow); OPCODE(128, o_crystalLeaveYellow); OPCODE(129, o_crystalEnterGreen); @@ -103,7 +112,6 @@ void Mechanical::setupOpcodes() { void Mechanical::disablePersistentScripts() { _fortressSimulationRunning = false; _elevatorRotationLeverMoving = false; - _elevatorGoingMiddle = false; _birdSinging = false; _fortressRotationRunning = false; } @@ -599,30 +607,33 @@ void Mechanical::elevatorGoMiddle_run() { _vm->_gfx->copyBackBufferToScreen(Common::Rect(10, 137, 61, 165)); _vm->_system->updateScreen(); } - } else if (_elevatorInCabin) { + } else { _elevatorTooLate = true; - - // Elevator going to middle animation - _vm->_cursor->hideCursor(); - _vm->_sound->playSoundBlocking(11120); - _vm->_gfx->copyImageToBackBuffer(6118, Common::Rect(544, 333)); - _vm->_sound->replaceSoundMyst(12120); - _vm->_gfx->runTransition(2, Common::Rect(177, 0, 370, 333), 25, 0); - _vm->_sound->playSoundBlocking(13120); - _vm->_sound->replaceSoundMyst(8120); - _vm->_gfx->copyImageToBackBuffer(6327, Common::Rect(544, 333)); - _vm->_system->delayMillis(500); - _vm->_sound->replaceSoundMyst(9120); - static uint16 moviePos[2] = { 3540, 5380 }; - o_elevatorWindowMovie(121, 0, 2, moviePos); - _vm->_gfx->copyBackBufferToScreen(Common::Rect(544, 333)); - _vm->_sound->replaceSoundMyst(10120); - _vm->_cursor->showCursor(); - _elevatorGoingMiddle = false; - _elevatorPosition = 1; - _vm->changeToCard(6327, true); + if (_elevatorInCabin) { + + // Elevator going to middle animation + _vm->_cursor->hideCursor(); + _vm->_sound->playSoundBlocking(11120); + _vm->_gfx->copyImageToBackBuffer(6118, Common::Rect(544, 333)); + _vm->_sound->replaceSoundMyst(12120); + _vm->_gfx->runTransition(kTransitionSlideToLeft, Common::Rect(177, 0, 370, 333), 25, 0); + _vm->_sound->playSoundBlocking(13120); + _vm->_sound->replaceSoundMyst(8120); + _vm->_gfx->copyImageToBackBuffer(6327, Common::Rect(544, 333)); + _vm->_system->delayMillis(500); + _vm->_sound->replaceSoundMyst(9120); + static uint16 moviePos[2] = { 3540, 5380 }; + o_elevatorWindowMovie(121, 0, 2, moviePos); + _vm->_gfx->copyBackBufferToScreen(Common::Rect(544, 333)); + _vm->_sound->replaceSoundMyst(10120); + _vm->_cursor->showCursor(); + + _elevatorPosition = 1; + + _vm->changeToCard(6327, kTransitionRightToLeft); + } } } } @@ -638,16 +649,18 @@ void Mechanical::o_elevatorTopMovie(uint16 op, uint16 var, uint16 argc, uint16 * _vm->_video->waitUntilMovieEnds(window); } -void Mechanical::opcode_124(uint16 op, uint16 var, uint16 argc, uint16 *argv) { - varUnusedCheck(op, var); +void Mechanical::o_fortressRotationSetPosition(uint16 op, uint16 var, uint16 argc, uint16 *argv) { + debugC(kDebugScript, "Opcode %d: Set fortress position", op); - if (argc == 0) { - // Used by Card 6156 (Fortress Rotation Controls) - // Called when Red Exit Button Pressed to raise Elevator + VideoHandle gears = _fortressRotationGears->playMovie(); + uint32 moviePosition = Audio::Timestamp(_vm->_video->getTime(gears), 600).totalNumberOfFrames(); + + // Myst ME short movie workaround, explained in o_fortressRotation_init + if (_fortressRotationShortMovieWorkaround) { + moviePosition += 3600 * _fortressRotationShortMovieCount; + } - // TODO: Fill in Code... - } else - unknown(op, var, argc, argv); + _fortressPosition = (moviePosition + 900) / 1800 % 4; } void Mechanical::o_mystStaircaseMovie(uint16 op, uint16 var, uint16 argc, uint16 *argv) { @@ -656,17 +669,14 @@ void Mechanical::o_mystStaircaseMovie(uint16 op, uint16 var, uint16 argc, uint16 _vm->_video->playMovieBlocking(_vm->wrapMovieFilename("sstairs", kMechanicalStack), 199, 108); } -void Mechanical::opcode_126(uint16 op, uint16 var, uint16 argc, uint16 *argv) { - varUnusedCheck(op, var); +void Mechanical::o_elevatorWaitTimeout(uint16 op, uint16 var, uint16 argc, uint16 *argv) { + debugC(kDebugScript, "Opcode %d: Wait for the elevator to go middle", op); - if (argc == 0) { - // Used by Card 6120 (Fortress Elevator) - // Called when Red Exit Button Pressed to raise Elevator and - // exit is clicked... - - // TODO: Fill in Code... - } else - unknown(op, var, argc, argv); + // Wait while the elevator times out + while (_elevatorGoingMiddle) { + runPersistentScripts(); + _vm->skippableWait(10); + } } void Mechanical::o_crystalEnterYellow(uint16 op, uint16 var, uint16 argc, uint16 *argv) { @@ -776,8 +786,76 @@ void Mechanical::o_elevatorRotation_init(uint16 op, uint16 var, uint16 argc, uin } void Mechanical::fortressRotation_run() { - // Used for Card 6156 (Fortress Rotation Controls) - // TODO: Fill in function... + VideoHandle gears = _fortressRotationGears->playMovie(); + + double oldRate = _vm->_video->getVideoRate(gears).toDouble(); + + uint32 moviePosition = Audio::Timestamp(_vm->_video->getTime(gears), 600).totalNumberOfFrames(); + + // Myst ME short movie workaround, explained in o_fortressRotation_init + if (_fortressRotationShortMovieWorkaround) { + // Detect if we just looped + if (ABS<int32>(_fortressRotationShortMovieLast - 3680) < 50 + && ABS<int32>(moviePosition) < 50) { + _fortressRotationShortMovieCount++; + } + + _fortressRotationShortMovieLast = moviePosition; + + // Simulate longer movie + moviePosition += 3600 * _fortressRotationShortMovieCount; + } + + int32 positionInQuarter = 900 - (moviePosition + 900) % 1800; + + // Are the gears moving? + if (oldRate >= 0.1 || ABS<int32>(positionInQuarter) >= 30 || _fortressRotationBrake) { + + double newRate = oldRate; + if (_fortressRotationBrake && (double)_fortressRotationBrake * 0.2 > oldRate) { + newRate += 0.1; + } + + // Don't let the gears get stuck between two fortress positions + if (ABS<double>(oldRate) <= 0.05) { + if (oldRate <= 0.0) { + newRate += oldRate; + } else { + newRate -= oldRate; + } + } else { + if (oldRate <= 0.0) { + newRate += 0.05; + } else { + newRate -= 0.05; + } + } + + // Adjust speed accordingly to acceleration lever + newRate += (double) (positionInQuarter / 1500.0) + * (double) (9 - _fortressRotationSpeed) / 9.0; + + newRate = CLIP<double>(newRate, -2.5, 2.5); + + _vm->_video->setVideoRate(gears, Common::Rational((int)(newRate * 1000.0), 1000)); + + _gearsWereRunning = true; + } else if (_gearsWereRunning) { + // The fortress has stopped. Set its new position + _fortressPosition = (moviePosition + 900) / 1800 % 4; + + _vm->_video->setVideoRate(gears, 0); + + if (!_fortressRotationShortMovieWorkaround) { + _vm->_video->seekToTime(gears, Audio::Timestamp(0, 1800 * _fortressPosition, 600)); + } else { + _vm->_video->seekToTime(gears, Audio::Timestamp(0, 1800 * (_fortressPosition % 2), 600)); + } + + _vm->_sound->playSoundBlocking(_fortressRotationSounds[_fortressPosition]); + + _gearsWereRunning = false; + } } void Mechanical::o_fortressRotation_init(uint16 op, uint16 var, uint16 argc, uint16 *argv) { @@ -785,6 +863,11 @@ void Mechanical::o_fortressRotation_init(uint16 op, uint16 var, uint16 argc, uin _fortressRotationGears = static_cast<MystResourceType6 *>(_invokingResource); + VideoHandle gears = _fortressRotationGears->playMovie(); + _vm->_video->setVideoLooping(gears, true); + _vm->_video->seekToTime(gears, Audio::Timestamp(0, 1800 * _fortressPosition, 600)); + _vm->_video->setVideoRate(gears, 0); + _fortressRotationSounds[0] = argv[0]; _fortressRotationSounds[1] = argv[1]; _fortressRotationSounds[2] = argv[2]; @@ -792,12 +875,113 @@ void Mechanical::o_fortressRotation_init(uint16 op, uint16 var, uint16 argc, uin _fortressRotationBrake = 0; + // WORKAROUND for the tower rotation bug in Myst ME. + // The original engine only allowed to visit two out of the three small islands, + // preventing the game from being fully completable. + // The fortress rotation is computed from the current position in the movie + // hcgears.mov. The version of this movie that shipped with the ME edition is + // too short to allow to visit all the islands. + // ScummVM simulates a longer movie by counting the number of times the movie + // looped and adding that time to the current movie position. + // Hence allowing the fortress position to be properly computed. + uint32 movieDuration = _vm->_video->getDuration(gears).convertToFramerate(600).totalNumberOfFrames(); + if (movieDuration == 3680) { + _fortressRotationShortMovieWorkaround = true; + _fortressRotationShortMovieCount = 0; + _fortressRotationShortMovieLast = 0; + } + _fortressRotationRunning = true; + _gearsWereRunning = false; } void Mechanical::fortressSimulation_run() { - // Used for Card 6044 (Fortress Rotation Simulator) - // TODO: Fill in function... + if (_fortressSimulationInit) { + // Init sequence + _vm->_sound->replaceBackgroundMyst(_fortressSimulationStartSound1, 65535); + _vm->skippableWait(5000); + _vm->_sound->replaceSoundMyst(_fortressSimulationStartSound2); + + // Update movie while the sound is playing + VideoHandle startup = _fortressSimulationStartup->playMovie(); + while (_vm->_sound->isPlaying(_fortressSimulationStartSound2)) { + if (_vm->_video->updateMovies()) + _vm->_system->updateScreen(); + + _vm->_system->delayMillis(10); + } + _vm->_sound->replaceBackgroundMyst(_fortressSimulationStartSound1, 65535); + _vm->_video->waitUntilMovieEnds(startup); + _vm->_sound->stopBackgroundMyst(); + _vm->_sound->replaceSoundMyst(_fortressSimulationStartSound2); + + + Common::Rect src = Common::Rect(0, 0, 176, 176); + Common::Rect dst = Common::Rect(187, 3, 363, 179); + _vm->_gfx->copyImageSectionToBackBuffer(6046, src, dst); + _vm->_gfx->copyBackBufferToScreen(dst); + _vm->_system->updateScreen(); + + _fortressSimulationStartup->pauseMovie(true); + VideoHandle holo = _fortressSimulationHolo->playMovie(); + _vm->_video->setVideoLooping(holo, true); + _vm->_video->setVideoRate(holo, 0); + + _vm->_cursor->showCursor(); + + _fortressSimulationInit = false; + } else { + VideoHandle holo = _fortressSimulationHolo->playMovie(); + + double oldRate = _vm->_video->getVideoRate(holo).toDouble(); + + uint32 moviePosition = Audio::Timestamp(_vm->_video->getTime(holo), 600).totalNumberOfFrames(); + + int32 positionInQuarter = 900 - (moviePosition + 900) % 1800; + + // Are the gears moving? + if (oldRate >= 0.1 || ABS<int32>(positionInQuarter) >= 30 || _fortressSimulationBrake) { + + double newRate = oldRate; + if (_fortressSimulationBrake && (double)_fortressSimulationBrake * 0.2 > oldRate) { + newRate += 0.1; + } + + // Don't let the gears get stuck between two fortress positions + if (ABS<double>(oldRate) <= 0.05) { + if (oldRate <= 0.0) { + newRate += oldRate; + } else { + newRate -= oldRate; + } + } else { + if (oldRate <= 0.0) { + newRate += 0.05; + } else { + newRate -= 0.05; + } + } + + // Adjust speed accordingly to acceleration lever + newRate += (double) (positionInQuarter / 1500.0) + * (double) (9 - _fortressSimulationSpeed) / 9.0; + + newRate = CLIP<double>(newRate, -2.5, 2.5); + + _vm->_video->setVideoRate(holo, Common::Rational((int)(newRate * 1000.0), 1000)); + + _gearsWereRunning = true; + } else if (_gearsWereRunning) { + // The fortress has stopped. Set its new position + uint16 simulationPosition = (moviePosition + 900) / 1800 % 4; + + _vm->_video->setVideoRate(holo, 0); + _vm->_video->seekToTime(holo, Audio::Timestamp(0, 1800 * simulationPosition, 600)); + _vm->_sound->playSoundBlocking( _fortressRotationSounds[simulationPosition]); + + _gearsWereRunning = false; + } + } } void Mechanical::o_fortressSimulation_init(uint16 op, uint16 var, uint16 argc, uint16 *argv) { @@ -816,6 +1000,10 @@ void Mechanical::o_fortressSimulation_init(uint16 op, uint16 var, uint16 argc, u _fortressSimulationBrake = 0; _fortressSimulationRunning = true; + _gearsWereRunning = false; + _fortressSimulationInit = true; + + _vm->_cursor->hideCursor(); } void Mechanical::o_fortressSimulationStartup_init(uint16 op, uint16 var, uint16 argc, uint16 *argv) { diff --git a/engines/mohawk/myst_stacks/mechanical.h b/engines/mohawk/myst_stacks/mechanical.h index 3bd7f2d71b..7f3d5143e4 100644 --- a/engines/mohawk/myst_stacks/mechanical.h +++ b/engines/mohawk/myst_stacks/mechanical.h @@ -80,9 +80,9 @@ private: DECLARE_OPCODE(o_elevatorWindowMovie); DECLARE_OPCODE(o_elevatorGoMiddle); DECLARE_OPCODE(o_elevatorTopMovie); - DECLARE_OPCODE(opcode_124); + DECLARE_OPCODE(o_fortressRotationSetPosition); DECLARE_OPCODE(o_mystStaircaseMovie); - DECLARE_OPCODE(opcode_126); + DECLARE_OPCODE(o_elevatorWaitTimeout); DECLARE_OPCODE(o_crystalEnterYellow); DECLARE_OPCODE(o_crystalEnterGreen); DECLARE_OPCODE(o_crystalEnterRed); @@ -104,13 +104,19 @@ private: bool _mystStaircaseState; // 76 bool _fortressRotationRunning; + bool _gearsWereRunning; uint16 _fortressRotationSpeed; // 78 uint16 _fortressRotationBrake; // 80 uint16 _fortressPosition; // 82 uint16 _fortressRotationSounds[4]; // 86 to 92 MystResourceType6 *_fortressRotationGears; // 172 + bool _fortressRotationShortMovieWorkaround; + uint32 _fortressRotationShortMovieCount; + uint32 _fortressRotationShortMovieLast; + bool _fortressSimulationRunning; + bool _fortressSimulationInit; // 94 uint16 _fortressSimulationSpeed; // 96 uint16 _fortressSimulationBrake; // 98 uint16 _fortressSimulationStartSound1; // 102 diff --git a/engines/mohawk/myst_stacks/myst.cpp b/engines/mohawk/myst_stacks/myst.cpp index c1ddc74c82..f17d765c99 100644 --- a/engines/mohawk/myst_stacks/myst.cpp +++ b/engines/mohawk/myst_stacks/myst.cpp @@ -51,6 +51,8 @@ Myst::Myst(MohawkEngine_Myst *vm) : _dockVaultState = 0; _cabinDoorOpened = 0; _cabinMatchState = 2; + _cabinGaugeMovie = NULL_VID_HANDLE; + _cabinFireMovie = NULL_VID_HANDLE; _matchBurning = false; _tree = 0; _treeAlcove = 0; @@ -189,7 +191,7 @@ void Myst::setupOpcodes() { OPCODE(215, o_gulls2_init); OPCODE(216, o_treeCard_init); OPCODE(217, o_treeEntry_init); - OPCODE(218, opcode_218); + OPCODE(218, o_boilerMovies_init); OPCODE(219, o_rocketSliders_init); OPCODE(220, o_rocketLinkVideo_init); OPCODE(221, o_greenBook_init); @@ -202,7 +204,7 @@ void Myst::setupOpcodes() { OPCODE(303, NOP); OPCODE(304, o_treeCard_exit); OPCODE(305, o_treeEntry_exit); - OPCODE(306, NOP); + OPCODE(306, o_boiler_exit); OPCODE(307, o_generatorControlRoom_exit); OPCODE(308, NOP); OPCODE(309, NOP); @@ -608,6 +610,8 @@ uint16 Myst::getVar(uint16 var) { return 1; case 302: // Green Book Opened Before Flag return _state.greenBookOpenedBefore; + case 303: // Library Bookcase status changed + return _libraryBookcaseChanged; case 304: // Tower Rotation Map Initialized return _towerRotationMapInitialized; case 305: // Cabin Boiler Lit @@ -1041,7 +1045,7 @@ void Myst::o_bookGivePage(uint16 op, uint16 var, uint16 argc, uint16 *argv) { // No page or white page if (!_globals.heldPage || _globals.heldPage == 13) { - _vm->changeToCard(cardIdBookCover, true); + _vm->changeToCard(cardIdBookCover, kTransitionDissolve); return; } @@ -1083,7 +1087,7 @@ void Myst::o_bookGivePage(uint16 op, uint16 var, uint16 argc, uint16 *argv) { // Wrong book if (bookVar != var) { - _vm->changeToCard(cardIdBookCover, true); + _vm->changeToCard(cardIdBookCover, kTransitionDissolve); return; } @@ -1109,9 +1113,9 @@ void Myst::o_bookGivePage(uint16 op, uint16 var, uint16 argc, uint16 *argv) { else _globals.currentAge = 10; - _vm->changeToCard(cardIdLose, true); + _vm->changeToCard(cardIdLose, kTransitionDissolve); } else { - _vm->changeToCard(cardIdBookCover, true); + _vm->changeToCard(cardIdBookCover, kTransitionDissolve); } } @@ -1298,7 +1302,7 @@ void Myst::imagerValidation_run() { if (_imagerValidationStep == 11) { _imagerValidationStep = 0; - _vm->changeToCard(_imagerValidationCard, true); + _vm->changeToCard(_imagerValidationCard, kTransitionBottomToTop); } else { _startTime = time + 100; } @@ -1473,10 +1477,10 @@ void Myst::o_cabinSafeHandleMove(uint16 op, uint16 var, uint16 argc, uint16 *arg if (soundId) _vm->_sound->replaceSoundMyst(soundId); - _vm->changeToCard(4103, false); + _vm->changeToCard(4103, kNoTransition); Common::Rect screenRect = Common::Rect(544, 333); - _vm->_gfx->runTransition(0, screenRect, 2, 5); + _vm->_gfx->runTransition(kTransitionLeftToRight, screenRect, 2, 5); } _tempVar = 1; } else { @@ -1869,17 +1873,50 @@ void Myst::o_boilerLightPilot(uint16 op, uint16 var, uint16 argc, uint16 *argv) _state.cabinPilotLightLit = 1; _vm->redrawArea(98); + boilerFireUpdate(false); + // Put out match _matchGoOutTime = _vm->_system->getMillis(); if (_state.cabinValvePosition > 0) _vm->_sound->replaceBackgroundMyst(8098, 49152); - if (_state.cabinValvePosition > 12) + if (_state.cabinValvePosition > 12) { + // Compute the speed of the gauge to synchronize it with the next tree move + uint32 delay = treeNextMoveDelay(_state.cabinValvePosition); + Common::Rational rate = boilerComputeGaugeRate(_state.cabinValvePosition, delay); + boilerResetGauge(rate); + _state.treeLastMoveTime = _vm->_system->getMillis(); + } + } +} - // TODO: Complete. Play movies +Common::Rational Myst::boilerComputeGaugeRate(uint16 pressure, uint32 delay) { + Common::Rational rate = Common::Rational(2088, delay); + if (pressure < 12) + return -rate; + else + return rate; +} + +void Myst::boilerResetGauge(const Common::Rational &rate) { + if (_vm->_video->endOfVideo(_cabinGaugeMovie)) { + if (_vm->getCurCard() == 4098) { + _cabinGaugeMovie = _vm->_video->playMovie(_vm->wrapMovieFilename("cabingau", kMystStack), 243, 96); + } else { + _cabinGaugeMovie = _vm->_video->playMovie(_vm->wrapMovieFilename("cabcgfar", kMystStack), 254, 136); + } } + + Audio::Timestamp goTo; + if (rate > 0) + goTo = Audio::Timestamp(0, 0, 600); + else + goTo = _vm->_video->getDuration(_cabinGaugeMovie); + + _vm->_video->seekToTime(_cabinGaugeMovie, goTo); + _vm->_video->setVideoRate(_cabinGaugeMovie, rate); } void Myst::o_boilerIncreasePressureStop(uint16 op, uint16 var, uint16 argc, uint16 *argv) { @@ -1893,7 +1930,12 @@ void Myst::o_boilerIncreasePressureStop(uint16 op, uint16 var, uint16 argc, uint if (_state.cabinValvePosition > 0) _vm->_sound->replaceBackgroundMyst(8098, 49152); - // TODO: Play movies + if (!_vm->_video->endOfVideo(_cabinGaugeMovie)) { + uint16 delay = treeNextMoveDelay(_state.cabinValvePosition); + Common::Rational rate = boilerComputeGaugeRate(_state.cabinValvePosition, delay); + _vm->_video->setVideoRate(_cabinGaugeMovie, rate); + } + } else if (_state.cabinValvePosition > 0) _vm->_sound->replaceBackgroundMyst(4098, _state.cabinValvePosition << 10); } @@ -1903,7 +1945,8 @@ void Myst::boilerPressureIncrease_run() { if (!_vm->_sound->isPlaying(5098) && _state.cabinValvePosition < 25) { _state.cabinValvePosition++; if (_state.cabinValvePosition == 1) { - // TODO: Play fire movie + // Set fire to high + boilerFireUpdate(false); // Draw fire _vm->redrawArea(305); @@ -1927,7 +1970,8 @@ void Myst::boilerPressureDecrease_run() { if (!_vm->_sound->isPlaying(5098) && _state.cabinValvePosition > 0) { _state.cabinValvePosition--; if (_state.cabinValvePosition == 0) { - // TODO: Play fire movie + // Set fire to low + boilerFireUpdate(false); // Draw fire _vm->redrawArea(305); @@ -1961,7 +2005,12 @@ void Myst::o_boilerDecreasePressureStop(uint16 op, uint16 var, uint16 argc, uint if (_state.cabinValvePosition > 0) _vm->_sound->replaceBackgroundMyst(8098, 49152); - // TODO: Play movies + if (!_vm->_video->endOfVideo(_cabinGaugeMovie)) { + uint16 delay = treeNextMoveDelay(_state.cabinValvePosition); + Common::Rational rate = boilerComputeGaugeRate(_state.cabinValvePosition, delay); + _vm->_video->setVideoRate(_cabinGaugeMovie, rate); + } + } else { if (_state.cabinValvePosition > 0) _vm->_sound->replaceBackgroundMyst(4098, _state.cabinValvePosition << 10); @@ -2067,6 +2116,11 @@ void Myst::tree_run() { // Check if alcove is accessible treeSetAlcoveAccessible(); + if (_cabinGaugeMovie != NULL_VID_HANDLE) { + Common::Rational rate = boilerComputeGaugeRate(pressure, delay); + boilerResetGauge(rate); + } + _state.treeLastMoveTime = time; } } @@ -2968,7 +3022,12 @@ void Myst::clockReset() { _vm->_system->delayMillis(1000); _vm->_sound->replaceSoundMyst(7113); - // TODO: Play cl1wggat backwards + // Gear closing movie + VideoHandle handle = _vm->_video->playMovie(_vm->wrapMovieFilename("cl1wggat", kMystStack) , 195, 225); + _vm->_video->seekToTime(handle, _vm->_video->getDuration(handle)); + _vm->_video->setVideoRate(handle, -1); + _vm->_video->waitUntilMovieEnds(handle); + // Redraw gear _state.gearsOpen = 0; _vm->redrawArea(40); @@ -2980,16 +3039,9 @@ void Myst::clockReset() { void Myst::clockResetWeight() { _clockWeightVideo = _vm->_video->playMovie(_vm->wrapMovieFilename("cl1wlfch", kMystStack) , 124, 0); - if (!(_vm->getFeatures() & GF_ME)) { - // Set video bounds, weight going up - _vm->_video->setVideoBounds(_clockWeightVideo, - Audio::Timestamp(0, 2214 * 2 - _clockWeightPosition, 600), - Audio::Timestamp(0, 2214 * 2, 600)); - } else { - //FIXME: Needs QT backwards playing, for now just display the weight up - warning("Weight going back up not implemented"); - _vm->_video->drawVideoFrame(_clockWeightVideo, Audio::Timestamp(0, 0, 600)); - } + // Play the movie backwards, weight going up + _vm->_video->seekToTime(_clockWeightVideo, Audio::Timestamp(0, _clockWeightPosition, 600)); + _vm->_video->setVideoRate(_clockWeightVideo, -1); // Reset position _clockWeightPosition = 0; @@ -3246,7 +3298,7 @@ void Myst::libraryBookcaseTransform_run(void) { if (_state.libraryBookcaseDoor) { _vm->_gfx->copyImageSectionToBackBuffer(11179, Common::Rect(0, 0, 106, 81), Common::Rect(0, 72, 106, 153)); - _vm->_gfx->runTransition(6, Common::Rect(0, 72, 106, 153), 5, 10); + _vm->_gfx->runTransition(kTransitionBottomToTop, Common::Rect(0, 72, 106, 153), 5, 10); _vm->_sound->playSoundBlocking(7348); _vm->_sound->replaceBackgroundMyst(4348, 16384); } else { @@ -3510,22 +3562,60 @@ void Myst::o_treeEntry_init(uint16 op, uint16 var, uint16 argc, uint16 *argv) { treeSetAlcoveAccessible(); } -void Myst::opcode_218(uint16 op, uint16 var, uint16 argc, uint16 *argv) { - varUnusedCheck(op, var); +void Myst::o_boilerMovies_init(uint16 op, uint16 var, uint16 argc, uint16 *argv) { + debugC(kDebugScript, "Opcode %d: Boiler movies init", op); - // Used for Card 4097 (Cabin Boiler) - // TODO: Fill in logic - if (false) { - _vm->_video->playMovieBlocking(_vm->wrapMovieFilename("cabfirfr", kMystStack), 254, 244); - _vm->_video->playMovieBlocking(_vm->wrapMovieFilename("cabcgfar", kMystStack), 254, 138); + boilerFireInit(); + boilerGaugeInit(); +} + +void Myst::boilerFireInit() { + if (_vm->getCurCard() == 4098) { + _cabinFireMovie = _vm->_video->playMovie(_vm->wrapMovieFilename("cabfire", kMystStack), 240, 279, true); + _vm->_video->pauseMovie(_cabinFireMovie, true); + + _vm->redrawArea(305); + boilerFireUpdate(true); + } else { + if (_state.cabinPilotLightLit == 1 && _state.cabinValvePosition >= 1) { + _cabinFireMovie = _vm->_video->playMovie(_vm->wrapMovieFilename("cabfirfr", kMystStack), 254, 244, true); + } + } +} + +void Myst::boilerFireUpdate(bool init) { + uint position = _vm->_video->getTime(_cabinFireMovie); + + if (_state.cabinPilotLightLit == 1) { + if (_state.cabinValvePosition == 0) { + if (position > (uint)Audio::Timestamp(0, 200, 600).msecs() || init) { + _vm->_video->setVideoBounds(_cabinFireMovie, Audio::Timestamp(0, 0, 600), Audio::Timestamp(0, 100, 600)); + _vm->_video->pauseMovie(_cabinFireMovie, false); + } + } else { + if (position < (uint)Audio::Timestamp(0, 200, 600).msecs() || init) { + _vm->_video->setVideoBounds(_cabinFireMovie, Audio::Timestamp(0, 201, 600), Audio::Timestamp(0, 1900, 600)); + _vm->_video->pauseMovie(_cabinFireMovie, false); + } + } } +} - // Used for Card 4098 (Cabin Boiler) - // TODO: Fill in logic - if (false) { - _vm->_video->playMovieBlocking(_vm->wrapMovieFilename("cabfire", kMystStack), 240, 279); - _vm->_video->playMovieBlocking(_vm->wrapMovieFilename("cabingau", kMystStack), 243, 97); +void Myst::boilerGaugeInit() { + if (_vm->getCurCard() == 4098) { + _cabinGaugeMovie = _vm->_video->playMovie(_vm->wrapMovieFilename("cabingau", kMystStack), 243, 96); + } else { + _cabinGaugeMovie = _vm->_video->playMovie(_vm->wrapMovieFilename("cabcgfar", kMystStack), 254, 136); } + + Audio::Timestamp frame; + + if (_state.cabinPilotLightLit == 1 && _state.cabinValvePosition > 12) + frame = _vm->_video->getDuration(_cabinGaugeMovie); + else + frame = Audio::Timestamp(0, 0, 600); + + _vm->_video->drawVideoFrame(_cabinGaugeMovie, frame); } void Myst::o_rocketSliders_init(uint16 op, uint16 var, uint16 argc, uint16 *argv) { @@ -3648,6 +3738,13 @@ void Myst::o_treeEntry_exit(uint16 op, uint16 var, uint16 argc, uint16 *argv) { _treeAlcove = 0; } +void Myst::o_boiler_exit(uint16 op, uint16 var, uint16 argc, uint16 *argv) { + debugC(kDebugScript, "Opcode %d: Exit boiler card", op); + + _cabinGaugeMovie = NULL_VID_HANDLE; + _cabinFireMovie = NULL_VID_HANDLE; +} + void Myst::o_generatorControlRoom_exit(uint16 op, uint16 var, uint16 argc, uint16 *argv) { debugC(kDebugScript, "Opcode %d: Generator room exit", op); diff --git a/engines/mohawk/myst_stacks/myst.h b/engines/mohawk/myst_stacks/myst.h index e9bff08cb4..de88843d59 100644 --- a/engines/mohawk/myst_stacks/myst.h +++ b/engines/mohawk/myst_stacks/myst.h @@ -43,7 +43,7 @@ public: virtual void disablePersistentScripts(); virtual void runPersistentScripts(); -private: +protected: void setupOpcodes(); uint16 getVar(uint16 var); void toggleVar(uint16 var); @@ -52,7 +52,7 @@ private: virtual uint16 getMap() { return 9934; } void towerRotationMap_run(); - void libraryBookcaseTransform_run(); + virtual void libraryBookcaseTransform_run(); void generatorControlRoom_run(); void opcode_212_run(); void libraryCombinationBook_run(); @@ -174,7 +174,7 @@ private: DECLARE_OPCODE(o_gulls2_init); DECLARE_OPCODE(o_treeCard_init); DECLARE_OPCODE(o_treeEntry_init); - DECLARE_OPCODE(opcode_218); + DECLARE_OPCODE(o_boilerMovies_init); DECLARE_OPCODE(o_rocketSliders_init); DECLARE_OPCODE(o_rocketLinkVideo_init); DECLARE_OPCODE(o_greenBook_init); @@ -183,6 +183,7 @@ private: DECLARE_OPCODE(o_bookAddSpecialPage_exit); DECLARE_OPCODE(o_treeCard_exit); DECLARE_OPCODE(o_treeEntry_exit); + DECLARE_OPCODE(o_boiler_exit); DECLARE_OPCODE(o_generatorControlRoom_exit); @@ -259,6 +260,9 @@ private: uint16 _cabinMatchState; // 60 uint32 _matchGoOutTime; // 144 + VideoHandle _cabinFireMovie; // 240 + VideoHandle _cabinGaugeMovie; // 244 + bool _boilerPressureIncreasing; bool _boilerPressureDecreasing; bool _basementPressureIncreasing; @@ -317,6 +321,12 @@ private: Common::Point towerRotationMapComputeCoords(const Common::Point ¢er, uint16 angle); void towerRotationMapDrawLine(const Common::Point ¢er, const Common::Point &end); + void boilerFireInit(); + void boilerFireUpdate(bool init); + void boilerGaugeInit(); + Common::Rational boilerComputeGaugeRate(uint16 pressure, uint32 delay); + void boilerResetGauge(const Common::Rational &rate); + void treeSetAlcoveAccessible(); uint32 treeNextMoveDelay(uint16 pressure); diff --git a/engines/mohawk/myst_stacks/preview.cpp b/engines/mohawk/myst_stacks/preview.cpp index 0b8dcf897a..75e870281e 100644 --- a/engines/mohawk/myst_stacks/preview.cpp +++ b/engines/mohawk/myst_stacks/preview.cpp @@ -60,6 +60,7 @@ void Preview::setupOpcodes() { OVERRIDE_OPCODE(199, o_speechStop); // "Init" Opcodes + OVERRIDE_OPCODE(209, o_libraryBookcaseTransformDemo_init); OPCODE(298, o_speech_init); OPCODE(299, o_library_init); } @@ -139,7 +140,7 @@ void Preview::speech_run() { break; case 1: // Open book if (_currentCue >= 1) { - _vm->changeToCard(3001, true); + _vm->changeToCard(3001, kTransitionDissolve); _speechStep++; } @@ -147,7 +148,7 @@ void Preview::speech_run() { case 2: // Go to Myst if (_currentCue >= 2) { _vm->_gfx->fadeToBlack(); - _vm->changeToCard(3002, false); + _vm->changeToCard(3002, kNoTransition); _vm->_gfx->fadeFromBlack(); _speechStep++; @@ -164,7 +165,7 @@ void Preview::speech_run() { if (_currentCue >= 4) { _library->drawConditionalDataToScreen(0); - _vm->changeToCard(3003, true); + _vm->changeToCard(3003, kTransitionDissolve); _speechNextTime = time + 2000; _speechStep++; @@ -181,7 +182,7 @@ void Preview::speech_run() { if (time < _speechNextTime) break; - _vm->changeToCard(3004, true); + _vm->changeToCard(3004, kTransitionDissolve); _speechNextTime = time + 2000; _speechStep++; break; @@ -190,7 +191,7 @@ void Preview::speech_run() { break; _vm->_gfx->fadeToBlack(); - _vm->changeToCard(3005, false); + _vm->changeToCard(3005, kNoTransition); _vm->_gfx->fadeFromBlack(); _speechNextTime = time + 1000; _speechStep++; @@ -205,7 +206,7 @@ void Preview::speech_run() { if (time < _speechNextTime) break; - _vm->changeToCard(3006 + _speechStep - 7, true); + _vm->changeToCard(3006 + _speechStep - 7, kTransitionDissolve); _speechNextTime = time + 2000; _speechStep++; break; @@ -213,7 +214,7 @@ void Preview::speech_run() { if (time < _speechNextTime) break; - _vm->changeToCard(4329, true); + _vm->changeToCard(4329, kTransitionDissolve); _speechRunning = false; _globals.currentAge = 2; @@ -241,5 +242,22 @@ void Preview::o_library_init(uint16 op, uint16 var, uint16 argc, uint16 *argv) { _library = static_cast<MystResourceType8 *>(_invokingResource); } +void Preview::o_libraryBookcaseTransformDemo_init(uint16 op, uint16 var, uint16 argc, uint16 *argv) { + if (_libraryBookcaseChanged) { + MystResourceType7 *resource = static_cast<MystResourceType7 *>(_invokingResource); + _libraryBookcaseMovie = static_cast<MystResourceType6 *>(resource->getSubResource(getVar(303))); + _libraryBookcaseSoundId = argv[0]; + _libraryBookcaseMoving = true; + } +} + +void Preview::libraryBookcaseTransform_run() { + if (_libraryBookcaseChanged) + _state.libraryBookcaseDoor = !_state.libraryBookcaseDoor; + + Myst::libraryBookcaseTransform_run(); +} + + } // End of namespace MystStacks } // End of namespace Mohawk diff --git a/engines/mohawk/myst_stacks/preview.h b/engines/mohawk/myst_stacks/preview.h index 1e4ff3efb4..706220e8ed 100644 --- a/engines/mohawk/myst_stacks/preview.h +++ b/engines/mohawk/myst_stacks/preview.h @@ -51,6 +51,7 @@ private: DECLARE_OPCODE(o_stayHere); DECLARE_OPCODE(o_speechStop); + DECLARE_OPCODE(o_libraryBookcaseTransformDemo_init); DECLARE_OPCODE(o_speech_init); DECLARE_OPCODE(o_library_init); @@ -65,6 +66,8 @@ private: void speech_run(); void speechUpdateCue(); + + void libraryBookcaseTransform_run(); }; } // End of namespace MystStacks diff --git a/engines/mohawk/myst_stacks/selenitic.cpp b/engines/mohawk/myst_stacks/selenitic.cpp index 1473742259..a941b14eaa 100644 --- a/engines/mohawk/myst_stacks/selenitic.cpp +++ b/engines/mohawk/myst_stacks/selenitic.cpp @@ -729,11 +729,11 @@ void Selenitic::o_mazeRunnerDoorButton(uint16 op, uint16 var, uint16 argc, uint1 uint16 cardIdEntry = argv[1]; if (_mazeRunnerPosition == 288) { - _vm->changeToCard(cardIdEntry, false); + _vm->changeToCard(cardIdEntry, kNoTransition); _vm->_sound->replaceSoundMyst(cardIdEntry); animatedUpdate(argv[2], &argv[3], 10); } else if (_mazeRunnerPosition == 289) { - _vm->changeToCard(cardIdExit, false); + _vm->changeToCard(cardIdExit, kNoTransition); _vm->_sound->replaceSoundMyst(cardIdExit); animatedUpdate(argv[2], &argv[3], 10); } @@ -895,9 +895,9 @@ void Selenitic::o_soundLockButton(uint16 op, uint16 var, uint16 argc, uint16 *ar uint16 cardIdClosed = argv[0]; uint16 cardIdOpen = argv[1]; - _vm->changeToCard(cardIdClosed, true); + _vm->changeToCard(cardIdClosed, kTransitionDissolve); - _vm->changeToCard(cardIdOpen, false); + _vm->changeToCard(cardIdOpen, kNoTransition); _vm->_sound->replaceSoundMyst(argv[2]); animatedUpdate(argv[4], &argv[5], argv[3]); diff --git a/engines/mohawk/myst_stacks/slides.cpp b/engines/mohawk/myst_stacks/slides.cpp index c0bb400db1..720926904a 100644 --- a/engines/mohawk/myst_stacks/slides.cpp +++ b/engines/mohawk/myst_stacks/slides.cpp @@ -63,7 +63,7 @@ void Slides::runPersistentScripts() { // Used on Cards... if (_vm->_system->getMillis() > _nextCardTime) { _vm->_gfx->fadeToBlack(); - _vm->changeToCard(_nextCardID, false); + _vm->changeToCard(_nextCardID, kNoTransition); _vm->_gfx->fadeFromBlack(); } } diff --git a/engines/mohawk/myst_stacks/stoneship.cpp b/engines/mohawk/myst_stacks/stoneship.cpp index ef228e62f3..1359685302 100644 --- a/engines/mohawk/myst_stacks/stoneship.cpp +++ b/engines/mohawk/myst_stacks/stoneship.cpp @@ -440,9 +440,9 @@ void Stoneship::o_drawerOpenSirius(uint16 op, uint16 var, uint16 argc, uint16 *a drawer->drawConditionalDataToScreen(0, 0); } - uint16 transition = 5; + TransitionType transition = kTransitionTopToBottom; if (argc == 2 && argv[1]) - transition = 11; + transition = kTransitionCopy; _vm->_gfx->runTransition(transition, drawer->getRect(), 25, 5); } @@ -579,7 +579,7 @@ void Stoneship::o_drawerOpenAchenar(uint16 op, uint16 var, uint16 argc, uint16 * MystResourceType8 *drawer = static_cast<MystResourceType8 *>(_vm->_resources[argv[0]]); drawer->drawConditionalDataToScreen(0, 0); - _vm->_gfx->runTransition(5, drawer->getRect(), 25, 5); + _vm->_gfx->runTransition(kTransitionTopToBottom, drawer->getRect(), 25, 5); } void Stoneship::o_hologramPlayback(uint16 op, uint16 var, uint16 argc, uint16 *argv) { @@ -692,9 +692,9 @@ void Stoneship::o_chestValveVideos(uint16 op, uint16 var, uint16 argc, uint16 *a _vm->_sound->resumeBackgroundMyst(); } else { // Valve opening - // TODO: Play backwards VideoHandle valve = _vm->_video->playMovie(movie, 97, 267); - _vm->_video->setVideoBounds(valve, Audio::Timestamp(0, 0, 600), Audio::Timestamp(0, 350, 600)); + _vm->_video->seekToTime(valve, Audio::Timestamp(0, 350, 600)); + _vm->_video->setVideoRate(valve, -1); _vm->_video->waitUntilMovieEnds(valve); } } @@ -777,7 +777,7 @@ void Stoneship::o_cloudOrbLeave(uint16 op, uint16 var, uint16 argc, uint16 *argv _cloudOrbMovie->pauseMovie(true); _vm->_sound->replaceSoundMyst(_cloudOrbStopSound); - _vm->_gfx->runTransition(5, _invokingResource->getRect(), 4, 0); + _vm->_gfx->runTransition(kTransitionTopToBottom, _invokingResource->getRect(), 4, 0); } void Stoneship::o_drawerCloseOpened(uint16 op, uint16 var, uint16 argc, uint16 *argv) { @@ -794,7 +794,7 @@ void Stoneship::drawerClose(uint16 drawer) { _vm->drawResourceImages(); MystResource *res = _vm->_resources[drawer]; - _vm->_gfx->runTransition(6, res->getRect(), 25, 5); + _vm->_gfx->runTransition(kTransitionBottomToTop, res->getRect(), 25, 5); } void Stoneship::o_hologramDisplay_init(uint16 op, uint16 var, uint16 argc, uint16 *argv) { diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp index e54d6fefa2..71aa371073 100644 --- a/engines/mohawk/riven.cpp +++ b/engines/mohawk/riven.cpp @@ -177,7 +177,7 @@ Common::Error MohawkEngine_Riven::run() { } } else { // Otherwise, start us off at aspit's card 1 (the main menu) - changeToStack(aspit); + changeToStack(aspit); changeToCard(1); } @@ -646,7 +646,7 @@ Common::String MohawkEngine_Riven::getName(uint16 nameResource, uint16 nameID) { } delete nameStream; - delete [] stringOffsets; + delete[] stringOffsets; return name; } @@ -830,7 +830,7 @@ static void sunnersTopStairsTimer(MohawkEngine_Riven *vm) { } else if (sunnerTime < vm->getTotalPlayTime()) { VideoHandle handle = vm->_video->playMovieRiven(vm->_rnd->getRandomNumberRng(1, 3)); - timerTime = vm->_video->getDuration(handle) + vm->_rnd->getRandomNumberRng(2, 15) * 1000; + timerTime = vm->_video->getDuration(handle).msecs() + vm->_rnd->getRandomNumberRng(2, 15) * 1000; } sunnerTime = timerTime + vm->getTotalPlayTime(); @@ -868,7 +868,7 @@ static void sunnersMidStairsTimer(MohawkEngine_Riven *vm) { VideoHandle handle = vm->_video->playMovieRiven(movie); - timerTime = vm->_video->getDuration(handle) + vm->_rnd->getRandomNumberRng(1, 10) * 1000; + timerTime = vm->_video->getDuration(handle).msecs() + vm->_rnd->getRandomNumberRng(1, 10) * 1000; } sunnerTime = timerTime + vm->getTotalPlayTime(); @@ -898,7 +898,7 @@ static void sunnersLowerStairsTimer(MohawkEngine_Riven *vm) { } else if (sunnerTime < vm->getTotalPlayTime()) { VideoHandle handle = vm->_video->playMovieRiven(vm->_rnd->getRandomNumberRng(3, 5)); - timerTime = vm->_video->getDuration(handle) + vm->_rnd->getRandomNumberRng(1, 30) * 1000; + timerTime = vm->_video->getDuration(handle).msecs() + vm->_rnd->getRandomNumberRng(1, 30) * 1000; } sunnerTime = timerTime + vm->getTotalPlayTime(); @@ -932,7 +932,7 @@ static void sunnersBeachTimer(MohawkEngine_Riven *vm) { vm->_video->activateMLST(mlstID, vm->getCurCard()); VideoHandle handle = vm->_video->playMovieRiven(mlstID); - timerTime = vm->_video->getDuration(handle) + vm->_rnd->getRandomNumberRng(1, 30) * 1000; + timerTime = vm->_video->getDuration(handle).msecs() + vm->_rnd->getRandomNumberRng(1, 30) * 1000; } sunnerTime = timerTime + vm->getTotalPlayTime(); diff --git a/engines/mohawk/riven_external.cpp b/engines/mohawk/riven_external.cpp index 337a57e3e1..384e89a4cf 100644 --- a/engines/mohawk/riven_external.cpp +++ b/engines/mohawk/riven_external.cpp @@ -1467,7 +1467,7 @@ static void catherineViewerIdleTimer(MohawkEngine_Riven *vm) { VideoHandle videoHandle = vm->_video->playMovieRiven(30); // Reset the timer - vm->installTimer(&catherineViewerIdleTimer, vm->_video->getDuration(videoHandle) + vm->_rnd->getRandomNumber(60) * 1000); + vm->installTimer(&catherineViewerIdleTimer, vm->_video->getDuration(videoHandle).msecs() + vm->_rnd->getRandomNumber(60) * 1000); } void RivenExternal::xglview_prisonon(uint16 argc, uint16 *argv) { @@ -1507,7 +1507,7 @@ void RivenExternal::xglview_prisonon(uint16 argc, uint16 *argv) { _vm->_video->activateMLST(cathMovie, _vm->getCurCard()); VideoHandle videoHandle = _vm->_video->playMovieRiven(30); - timeUntilNextMovie = _vm->_video->getDuration(videoHandle) + _vm->_rnd->getRandomNumber(60) * 1000; + timeUntilNextMovie = _vm->_video->getDuration(videoHandle).msecs() + _vm->_rnd->getRandomNumber(60) * 1000; } else { // Otherwise, just redraw the imager timeUntilNextMovie = _vm->_rnd->getRandomNumberRng(10, 20) * 1000; @@ -2335,7 +2335,7 @@ static void rebelPrisonWindowTimer(MohawkEngine_Riven *vm) { VideoHandle handle = vm->_video->playMovieRiven(movie); // Ensure the next video starts after this one ends - uint32 timeUntilNextVideo = vm->_video->getDuration(handle) + vm->_rnd->getRandomNumberRng(38, 58) * 1000; + uint32 timeUntilNextVideo = vm->_video->getDuration(handle).msecs() + vm->_rnd->getRandomNumberRng(38, 58) * 1000; // Save the time in case we leave the card and return vm->_vars["rvillagetime"] = timeUntilNextVideo + vm->getTotalPlayTime(); diff --git a/engines/mohawk/video.cpp b/engines/mohawk/video.cpp index 18d609c513..8b0130d711 100644 --- a/engines/mohawk/video.cpp +++ b/engines/mohawk/video.cpp @@ -29,6 +29,7 @@ #include "common/textconsole.h" #include "common/system.h" +#include "graphics/palette.h" #include "graphics/surface.h" #include "video/qt_decoder.h" @@ -43,13 +44,12 @@ void VideoEntry::clear() { loop = false; enabled = false; start = Audio::Timestamp(0, 1); - end = Audio::Timestamp(0xFFFFFFFF, 1); // Largest possible, there is an endOfVideo() check anyway filename.clear(); id = -1; } bool VideoEntry::endOfVideo() { - return !video || video->endOfVideo() || video->getTime() >= (uint)end.msecs(); + return !video || video->endOfVideo(); } VideoManager::VideoManager(MohawkEngine* vm) : _vm(vm) { @@ -207,7 +207,7 @@ bool VideoManager::updateMovies() { // Remove any videos that are over if (_videoStreams[i].endOfVideo()) { if (_videoStreams[i].loop) { - _videoStreams[i]->seekToTime(_videoStreams[i].start); + _videoStreams[i]->seek(_videoStreams[i].start); } else { // Check the video time one last time before deleting it _vm->doVideoTimer(i, true); @@ -239,7 +239,7 @@ bool VideoManager::updateMovies() { frame = convertedFrame; } else if (pixelFormat.bytesPerPixel == 1 && _videoStreams[i]->hasDirtyPalette()) { // Set the palette when running in 8bpp mode only - _videoStreams[i]->setSystemPalette(); + _vm->_system->getPaletteManager()->setPalette(_videoStreams[i]->getPalette(), 0, 256); } // Clip the width/height to make sure we stay on the screen (Myst does this a few times) @@ -394,6 +394,8 @@ VideoHandle VideoManager::createVideoHandle(uint16 id, uint16 x, uint16 y, bool entry.loop = loop; entry.enabled = true; + entry->start(); + // Search for any deleted videos so we can take a formerly used slot for (uint32 i = 0; i < _videoStreams.size(); i++) if (!_videoStreams[i].video) { @@ -430,6 +432,7 @@ VideoHandle VideoManager::createVideoHandle(const Common::String &filename, uint entry->loadStream(file); entry->setVolume(volume); + entry->start(); // Search for any deleted videos so we can take a formerly used slot for (uint32 i = 0; i < _videoStreams.size(); i++) @@ -475,7 +478,7 @@ VideoHandle VideoManager::findVideoHandle(const Common::String &filename) { return NULL_VID_HANDLE; } -int32 VideoManager::getCurFrame(VideoHandle handle) { +int VideoManager::getCurFrame(VideoHandle handle) { assert(handle != NULL_VID_HANDLE); return _videoStreams[handle]->getCurFrame(); } @@ -490,7 +493,7 @@ uint32 VideoManager::getTime(VideoHandle handle) { return _videoStreams[handle]->getTime(); } -uint32 VideoManager::getDuration(VideoHandle handle) { +Audio::Timestamp VideoManager::getDuration(VideoHandle handle) { assert(handle != NULL_VID_HANDLE); return _videoStreams[handle]->getDuration(); } @@ -511,14 +514,13 @@ bool VideoManager::isVideoPlaying() { void VideoManager::setVideoBounds(VideoHandle handle, Audio::Timestamp start, Audio::Timestamp end) { assert(handle != NULL_VID_HANDLE); _videoStreams[handle].start = start; - _videoStreams[handle].end = end; - _videoStreams[handle]->seekToTime(start); + _videoStreams[handle]->setEndTime(end); + _videoStreams[handle]->seek(start); } void VideoManager::drawVideoFrame(VideoHandle handle, Audio::Timestamp time) { assert(handle != NULL_VID_HANDLE); - _videoStreams[handle].end = Audio::Timestamp(0xffffffff, 1); - _videoStreams[handle]->seekToTime(time); + _videoStreams[handle]->seek(time); updateMovies(); delete _videoStreams[handle].video; _videoStreams[handle].clear(); @@ -526,7 +528,7 @@ void VideoManager::drawVideoFrame(VideoHandle handle, Audio::Timestamp time) { void VideoManager::seekToTime(VideoHandle handle, Audio::Timestamp time) { assert(handle != NULL_VID_HANDLE); - _videoStreams[handle]->seekToTime(time); + _videoStreams[handle]->seek(time); } void VideoManager::setVideoLooping(VideoHandle handle, bool loop) { @@ -534,6 +536,16 @@ void VideoManager::setVideoLooping(VideoHandle handle, bool loop) { _videoStreams[handle].loop = loop; } +Common::Rational VideoManager::getVideoRate(VideoHandle handle) const { + assert(handle != NULL_VID_HANDLE); + return _videoStreams[handle]->getRate(); +} + +void VideoManager::setVideoRate(VideoHandle handle, const Common::Rational &rate) { + assert(handle != NULL_VID_HANDLE); + _videoStreams[handle]->setRate(rate); +} + void VideoManager::pauseMovie(VideoHandle handle, bool pause) { assert(handle != NULL_VID_HANDLE); _videoStreams[handle]->pauseVideo(pause); diff --git a/engines/mohawk/video.h b/engines/mohawk/video.h index 98bcadfb53..2c4c827aa8 100644 --- a/engines/mohawk/video.h +++ b/engines/mohawk/video.h @@ -45,19 +45,19 @@ struct MLSTRecord { struct VideoEntry { // Playback variables - Video::SeekableVideoDecoder *video; + Video::VideoDecoder *video; uint16 x; uint16 y; bool loop; bool enabled; - Audio::Timestamp start, end; + Audio::Timestamp start; // Identification Common::String filename; // External video files int id; // Internal Mohawk files // Helper functions - Video::SeekableVideoDecoder *operator->() const { assert(video); return video; } // TODO: Remove this eventually + Video::VideoDecoder *operator->() const { assert(video); return video; } // TODO: Remove this eventually void clear(); bool endOfVideo(); }; @@ -98,15 +98,17 @@ public: // Handle functions VideoHandle findVideoHandle(uint16 id); VideoHandle findVideoHandle(const Common::String &filename); - int32 getCurFrame(VideoHandle handle); + int getCurFrame(VideoHandle handle); uint32 getFrameCount(VideoHandle handle); uint32 getTime(VideoHandle handle); - uint32 getDuration(VideoHandle videoHandle); + Audio::Timestamp getDuration(VideoHandle videoHandle); bool endOfVideo(VideoHandle handle); void setVideoBounds(VideoHandle handle, Audio::Timestamp start, Audio::Timestamp end); void drawVideoFrame(VideoHandle handle, Audio::Timestamp time); void seekToTime(VideoHandle handle, Audio::Timestamp time); void setVideoLooping(VideoHandle handle, bool loop); + Common::Rational getVideoRate(VideoHandle handle) const; + void setVideoRate(VideoHandle handle, const Common::Rational &rate); void waitUntilMovieEnds(VideoHandle videoHandle); void delayUntilMovieEnds(VideoHandle videoHandle); void pauseMovie(VideoHandle videoHandle, bool pause); diff --git a/engines/parallaction/adlib.cpp b/engines/parallaction/adlib.cpp new file mode 100644 index 0000000000..134e5cfbf3 --- /dev/null +++ b/engines/parallaction/adlib.cpp @@ -0,0 +1,808 @@ +/* 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 "common/debug.h" +#include "common/system.h" + +#include "audio/fmopl.h" +#include "audio/mpu401.h" +#include "audio/softsynth/emumidi.h" + +namespace Parallaction { + +const uint kNumVoices = 9; +// adlib FM voices 0-5 +const uint kNumMelodic = 6; +// adlib FM voice 6 and 7-8 +const uint kNumPercussion = 5; + +// mask for maximum volume level +#define LEVEL_MASK 0x7f + +struct OPLOperator { + uint8 characteristic; // amplitude modulation, vibrato, envelope, keyboard scaling, modulator frequency + uint8 levels; + uint8 attackDecay; + uint8 sustainRelease; + uint8 waveform; +}; + +struct MelodicProgram { + OPLOperator op[2]; + uint8 feedbackAlgo; +}; + +struct PercussionNote { + OPLOperator op[2]; + uint8 feedbackAlgo; + uint8 percussion; + uint8 valid; + uint16 frequency; + uint8 octave; +}; + +static const MelodicProgram melodicPrograms[128] = { + {{{ 0x1, 0x51, 0xf2, 0xb2, 0x0 }, { 0x11, 0x0, 0xf2, 0xa2, 0x0 }}, 0x0 }, + {{{ 0xc2, 0x4b, 0xf1, 0x53, 0x0 }, { 0xd2, 0x0, 0xf2, 0x74, 0x0 }}, 0x4 }, + {{{ 0x81, 0x9d, 0xf2, 0x74, 0x0 }, { 0x13, 0x0, 0xf2, 0xf1, 0x0 }}, 0x6 }, + {{{ 0x3, 0x4f, 0xf1, 0x53, 0x0 }, { 0x17, 0x3, 0xf2, 0x74, 0x0 }}, 0x6 }, + {{{ 0xd1, 0x81, 0x81, 0x73, 0x2 }, { 0xd4, 0x0, 0xe1, 0x34, 0x0 }}, 0x3 }, + {{{ 0x1, 0x0, 0x94, 0xa6, 0x0 }, { 0x2, 0x0, 0x83, 0x26, 0x0 }}, 0x1 }, + {{{ 0xf3, 0x84, 0x81, 0x2, 0x1 }, { 0x55, 0x80, 0xdd, 0x3, 0x0 }}, 0x4 }, + {{{ 0x5, 0x8a, 0xf2, 0x26, 0x0 }, { 0x1, 0x80, 0xf3, 0x48, 0x0 }}, 0x0 }, + {{{ 0x32, 0x0, 0xb1, 0x14, 0x0 }, { 0x12, 0x0, 0xfd, 0x36, 0x0 }}, 0x3 }, + {{{ 0x1, 0x0, 0x82, 0xa, 0x2 }, { 0x2, 0x0, 0x85, 0x15, 0x0 }}, 0x3 }, + {{{ 0xd1, 0x1, 0x97, 0xaa, 0x0 }, { 0x4, 0xd, 0xf3, 0xa5, 0x1 }}, 0x9 }, + {{{ 0x17, 0x0, 0xf2, 0x62, 0x0 }, { 0x12, 0x0, 0xf2, 0x72, 0x0 }}, 0x8 }, + {{{ 0x6, 0x0, 0xff, 0xf4, 0x0 }, { 0xc4, 0x0, 0xf8, 0xb5, 0x0 }}, 0xe }, + {{{ 0xc0, 0x81, 0xf2, 0x13, 0x2 }, { 0xc0, 0xc1, 0xf3, 0x14, 0x2 }}, 0xb }, + {{{ 0x44, 0x53, 0xf5, 0x31, 0x0 }, { 0x60, 0x80, 0xfd, 0x22, 0x0 }}, 0x6 }, + {{{ 0xe0, 0x80, 0xf4, 0xf2, 0x0 }, { 0x61, 0x0, 0xf2, 0x6, 0x0 }}, 0x8 }, + {{{ 0xc1, 0x6, 0x83, 0x23, 0x0 }, { 0xc1, 0x4, 0xf0, 0x26, 0x0 }}, 0x1 }, + {{{ 0x26, 0x0, 0xf4, 0xb6, 0x0 }, { 0x21, 0x0, 0x81, 0x4b, 0x0 }}, 0x1 }, + {{{ 0x24, 0x80, 0xff, 0xf, 0x0 }, { 0x21, 0x80, 0xff, 0xf, 0x0 }}, 0x1 }, + {{{ 0x24, 0x4f, 0xf2, 0xb, 0x0 }, { 0x31, 0x0, 0x52, 0xb, 0x0 }}, 0xb }, + {{{ 0x31, 0x8, 0x81, 0xb, 0x0 }, { 0xa1, 0x80, 0x92, 0x3b, 0x0 }}, 0x0 }, + {{{ 0x70, 0xc5, 0x52, 0x11, 0x1 }, { 0x71, 0x80, 0x31, 0xfe, 0x1 }}, 0x0 }, + {{{ 0x51, 0x88, 0x10, 0xf0, 0x0 }, { 0x42, 0x83, 0x40, 0xfc, 0x0 }}, 0x8 }, + {{{ 0xf0, 0xd9, 0x81, 0x3, 0x0 }, { 0xb1, 0x80, 0xf1, 0x5, 0x0 }}, 0xa }, + {{{ 0x21, 0x4f, 0xf1, 0x31, 0x0 }, { 0x2, 0x80, 0xc3, 0x45, 0x0 }}, 0x0 }, + {{{ 0x7, 0x8f, 0x9c, 0x33, 0x1 }, { 0x1, 0x80, 0x8a, 0x13, 0x0 }}, 0x0 }, + {{{ 0x21, 0x40, 0xf1, 0x31, 0x0 }, { 0x6, 0x80, 0xf4, 0x44, 0x0 }}, 0x0 }, + {{{ 0x21, 0x40, 0xf1, 0x31, 0x3 }, { 0x81, 0x0, 0xf4, 0x44, 0x2 }}, 0x2 }, + {{{ 0x11, 0x8d, 0xfd, 0x11, 0x0 }, { 0x11, 0x80, 0xfd, 0x11, 0x0 }}, 0x8 }, + {{{ 0xf0, 0x1, 0x97, 0x17, 0x0 }, { 0x21, 0xd, 0xf1, 0x18, 0x0 }}, 0x8 }, + {{{ 0xf1, 0x1, 0x97, 0x17, 0x0 }, { 0x21, 0xd, 0xf1, 0x18, 0x0 }}, 0x8 }, + {{{ 0xcd, 0x9e, 0x55, 0xd1, 0x0 }, { 0xd1, 0x0, 0xf2, 0x71, 0x0 }}, 0xe }, + {{{ 0x1, 0x0, 0xf2, 0x88, 0x0 }, { 0x1, 0x0, 0xf5, 0x88, 0x0 }}, 0x1 }, + {{{ 0x30, 0xd, 0xf2, 0xef, 0x0 }, { 0x21, 0x0, 0xf5, 0x78, 0x0 }}, 0x6 }, + {{{ 0x0, 0x10, 0xf4, 0xd9, 0x0 }, { 0x0, 0x0, 0xf5, 0xd7, 0x0 }}, 0x4 }, + {{{ 0x1, 0x4c, 0xf2, 0x50, 0x0 }, { 0x1, 0x40, 0xd2, 0x59, 0x0 }}, 0x8 }, + {{{ 0x20, 0x11, 0xe2, 0x8a, 0x0 }, { 0x20, 0x0, 0xe4, 0xa8, 0x0 }}, 0xa }, + {{{ 0x21, 0x40, 0x7b, 0x4, 0x1 }, { 0x21, 0x0, 0x75, 0x72, 0x0 }}, 0x2 }, + {{{ 0x31, 0xd, 0xf2, 0xef, 0x0 }, { 0x21, 0x0, 0xf5, 0x78, 0x0 }}, 0xa }, + {{{ 0x1, 0xc, 0xf5, 0x2f, 0x1 }, { 0x0, 0x80, 0xf5, 0x5c, 0x0 }}, 0x0 }, + {{{ 0xb0, 0x1c, 0x81, 0x3, 0x2 }, { 0x20, 0x0, 0x54, 0x67, 0x2 }}, 0xe }, + {{{ 0x1, 0x0, 0xf1, 0x65, 0x0 }, { 0x1, 0x80, 0xa3, 0xa8, 0x2 }}, 0x1 }, + {{{ 0xe1, 0x4f, 0xc1, 0xd3, 0x2 }, { 0x21, 0x0, 0x32, 0x74, 0x1 }}, 0x0 }, + {{{ 0x2, 0x0, 0xf6, 0x16, 0x0 }, { 0x12, 0x0, 0xf2, 0xf8, 0x0 }}, 0x1 }, + {{{ 0xe0, 0x63, 0xf8, 0xf3, 0x0 }, { 0x70, 0x80, 0xf7, 0xf3, 0x0 }}, 0x4 }, + {{{ 0x1, 0x6, 0xf3, 0xff, 0x0 }, { 0x8, 0x0, 0xf7, 0xff, 0x0 }}, 0x4 }, + {{{ 0x21, 0x16, 0xb0, 0x81, 0x1 }, { 0x22, 0x0, 0xb3, 0x13, 0x1 }}, 0xc }, + {{{ 0x1, 0x4f, 0xf0, 0xff, 0x0 }, { 0x30, 0x0, 0x90, 0xf, 0x0 }}, 0x6 }, + {{{ 0x0, 0x10, 0xf1, 0xf2, 0x2 }, { 0x1, 0x0, 0xf1, 0xf2, 0x3 }}, 0x0 }, + {{{ 0x1, 0x4f, 0xf1, 0x50, 0x0 }, { 0x21, 0x80, 0xa3, 0x5, 0x3 }}, 0x6 }, + {{{ 0xb1, 0x3, 0x55, 0x3, 0x0 }, { 0xb1, 0x3, 0x8, 0xa, 0x0 }}, 0x9 }, + {{{ 0x22, 0x0, 0xa9, 0x34, 0x1 }, { 0x1, 0x0, 0xa2, 0x42, 0x2 }}, 0x2 }, + {{{ 0xa0, 0xdc, 0x81, 0x31, 0x3 }, { 0xb1, 0x80, 0xf1, 0x1, 0x3 }}, 0x0 }, + {{{ 0x1, 0x4f, 0xf1, 0x50, 0x0 }, { 0x21, 0x80, 0xa3, 0x5, 0x3 }}, 0x6 }, + {{{ 0xf1, 0x80, 0xa0, 0x72, 0x0 }, { 0x74, 0x0, 0x90, 0x22, 0x0 }}, 0x9 }, + {{{ 0xe1, 0x13, 0x71, 0xae, 0x0 }, { 0xe1, 0x0, 0xf0, 0xfc, 0x1 }}, 0xa }, + {{{ 0x31, 0x1c, 0x41, 0xb, 0x0 }, { 0xa1, 0x80, 0x92, 0x3b, 0x0 }}, 0xe }, + {{{ 0x71, 0x1c, 0x41, 0x1f, 0x0 }, { 0xa1, 0x80, 0x92, 0x3b, 0x0 }}, 0xe }, + {{{ 0x21, 0x1c, 0x53, 0x1d, 0x0 }, { 0xa1, 0x80, 0x52, 0x3b, 0x0 }}, 0xc }, + {{{ 0x21, 0x1d, 0xa4, 0xae, 0x1 }, { 0x21, 0x0, 0xb1, 0x9e, 0x0 }}, 0xc }, + {{{ 0xe1, 0x16, 0x71, 0xae, 0x0 }, { 0xe1, 0x0, 0x81, 0x9e, 0x0 }}, 0xa }, + {{{ 0xe1, 0x15, 0x71, 0xae, 0x0 }, { 0xe2, 0x0, 0x81, 0x9e, 0x0 }}, 0xe }, + {{{ 0x21, 0x16, 0x71, 0xae, 0x0 }, { 0x21, 0x0, 0x81, 0x9e, 0x0 }}, 0xe }, + {{{ 0x71, 0x1c, 0x41, 0x1f, 0x0 }, { 0xa1, 0x80, 0x92, 0x3b, 0x0 }}, 0xe }, + {{{ 0x21, 0x4f, 0x81, 0x53, 0x0 }, { 0x32, 0x0, 0x22, 0x2c, 0x0 }}, 0xa }, + {{{ 0x22, 0x4f, 0x81, 0x53, 0x0 }, { 0x32, 0x0, 0x22, 0x2c, 0x0 }}, 0xa }, + {{{ 0x23, 0x4f, 0x81, 0x53, 0x0 }, { 0x34, 0x0, 0x22, 0x2c, 0x0 }}, 0xa }, + {{{ 0xe1, 0x16, 0x71, 0xae, 0x0 }, { 0xe1, 0x0, 0x81, 0x9e, 0x0 }}, 0xa }, + {{{ 0x71, 0xc5, 0x6e, 0x17, 0x0 }, { 0x22, 0x5, 0x8b, 0xe, 0x0 }}, 0x2 }, + {{{ 0xe6, 0x27, 0x70, 0xf, 0x1 }, { 0xe3, 0x0, 0x60, 0x9f, 0x0 }}, 0xa }, + {{{ 0x30, 0xc8, 0xd5, 0x19, 0x0 }, { 0xb1, 0x80, 0x61, 0x1b, 0x0 }}, 0xc }, + {{{ 0x32, 0x9a, 0x51, 0x1b, 0x0 }, { 0xa1, 0x82, 0xa2, 0x3b, 0x0 }}, 0xc }, + {{{ 0xad, 0x3, 0x74, 0x29, 0x0 }, { 0xa2, 0x82, 0x73, 0x29, 0x0 }}, 0x7 }, + {{{ 0x21, 0x83, 0x74, 0x17, 0x0 }, { 0x62, 0x8d, 0x65, 0x17, 0x0 }}, 0x7 }, + {{{ 0x94, 0xb, 0x85, 0xff, 0x1 }, { 0x13, 0x0, 0x74, 0xff, 0x0 }}, 0xc }, + {{{ 0x74, 0x87, 0xa4, 0x2, 0x0 }, { 0xd6, 0x80, 0x45, 0x42, 0x0 }}, 0x2 }, + {{{ 0xb3, 0x85, 0x76, 0x21, 0x1 }, { 0x20, 0x0, 0x3d, 0xc1, 0x0 }}, 0x6 }, + {{{ 0x17, 0x4f, 0xf2, 0x61, 0x0 }, { 0x12, 0x8, 0xf1, 0xb4, 0x0 }}, 0x8 }, + {{{ 0x4f, 0x86, 0x65, 0x1, 0x0 }, { 0x1f, 0x0, 0x32, 0x74, 0x0 }}, 0x4 }, + {{{ 0xe1, 0x23, 0x71, 0xae, 0x0 }, { 0xe4, 0x0, 0x82, 0x9e, 0x0 }}, 0xa }, + {{{ 0x11, 0x86, 0xf2, 0xbd, 0x0 }, { 0x4, 0x80, 0xa0, 0x9b, 0x1 }}, 0x8 }, + {{{ 0x20, 0x90, 0xf5, 0x9e, 0x2 }, { 0x11, 0x0, 0xf4, 0x5b, 0x3 }}, 0xc }, + {{{ 0xf0, 0x80, 0x34, 0xe4, 0x0 }, { 0x7e, 0x0, 0xa2, 0x6, 0x0 }}, 0x8 }, + {{{ 0x90, 0xf, 0xff, 0x1, 0x3 }, { 0x0, 0x0, 0x1f, 0x1, 0x0 }}, 0xe }, + {{{ 0x1, 0x4f, 0xf0, 0xff, 0x0 }, { 0x33, 0x0, 0x90, 0xf, 0x0 }}, 0x6 }, + {{{ 0x1e, 0x0, 0x1f, 0xf, 0x0 }, { 0x10, 0x0, 0x1f, 0x7f, 0x0 }}, 0x0 }, + {{{ 0xbe, 0x0, 0xf1, 0x1, 0x3 }, { 0x31, 0x0, 0xf1, 0x1, 0x0 }}, 0x4 }, + {{{ 0xbe, 0x0, 0xf1, 0x1, 0x3 }, { 0x31, 0x0, 0xf1, 0x1, 0x0 }}, 0x4 }, + {{{ 0x93, 0x6, 0xc1, 0x4, 0x1 }, { 0x82, 0x0, 0x51, 0x9, 0x0 }}, 0x6 }, + {{{ 0xa0, 0x0, 0x96, 0x33, 0x0 }, { 0x20, 0x0, 0x55, 0x2b, 0x0 }}, 0x6 }, + {{{ 0x0, 0xc0, 0xff, 0x5, 0x0 }, { 0x0, 0x0, 0xff, 0x5, 0x3 }}, 0x0 }, + {{{ 0x4, 0x8, 0xf8, 0x7, 0x0 }, { 0x1, 0x0, 0x82, 0x74, 0x0 }}, 0x8 }, + {{{ 0x0, 0x0, 0x2f, 0x5, 0x0 }, { 0x20, 0x0, 0xff, 0x5, 0x3 }}, 0xa }, + {{{ 0x93, 0x0, 0xf7, 0x7, 0x2 }, { 0x0, 0x0, 0xf7, 0x7, 0x0 }}, 0xa }, + {{{ 0x0, 0x40, 0x80, 0x7a, 0x0 }, { 0xc4, 0x0, 0xc0, 0x7e, 0x0 }}, 0x8 }, + {{{ 0x90, 0x80, 0x55, 0xf5, 0x0 }, { 0x0, 0x0, 0x55, 0xf5, 0x0 }}, 0x8 }, + {{{ 0xe1, 0x80, 0x34, 0xe4, 0x0 }, { 0x69, 0x0, 0xf2, 0x6, 0x0 }}, 0x8 }, + {{{ 0x3, 0x2, 0xf0, 0xff, 0x3 }, { 0x11, 0x80, 0xf0, 0xff, 0x2 }}, 0x2 }, + {{{ 0x1e, 0x0, 0x1f, 0xf, 0x0 }, { 0x10, 0x0, 0x1f, 0x7f, 0x0 }}, 0x0 }, + {{{ 0x0, 0x0, 0x2f, 0x1, 0x0 }, { 0x0, 0x0, 0xff, 0x1, 0x0 }}, 0x4 }, + {{{ 0xbe, 0x0, 0xf1, 0x1, 0x3 }, { 0x31, 0x0, 0xf1, 0x1, 0x0 }}, 0x4 }, + {{{ 0x93, 0x85, 0x3f, 0x6, 0x1 }, { 0x0, 0x0, 0x5f, 0x7, 0x0 }}, 0x6 }, + {{{ 0x6, 0x0, 0xa0, 0xf0, 0x0 }, { 0x44, 0x0, 0xc5, 0x75, 0x0 }}, 0xe }, + {{{ 0x60, 0x0, 0x10, 0x81, 0x0 }, { 0x20, 0x8c, 0x12, 0x91, 0x0 }}, 0xe }, + {{{ 0x1, 0x40, 0xf1, 0x53, 0x0 }, { 0x8, 0x40, 0xf1, 0x53, 0x0 }}, 0x0 }, + {{{ 0x31, 0x0, 0x56, 0x31, 0x0 }, { 0x16, 0x0, 0x7d, 0x41, 0x0 }}, 0x0 }, + {{{ 0x0, 0x10, 0xf2, 0x72, 0x0 }, { 0x13, 0x0, 0xf2, 0x72, 0x0 }}, 0xc }, + {{{ 0x10, 0x0, 0x75, 0x93, 0x1 }, { 0x1, 0x0, 0xf5, 0x82, 0x1 }}, 0x0 }, + {{{ 0x0, 0x0, 0xf6, 0xff, 0x2 }, { 0x0, 0x0, 0xf6, 0xff, 0x0 }}, 0x8 }, + {{{ 0x30, 0x0, 0xff, 0xa0, 0x3 }, { 0x63, 0x0, 0x65, 0xb, 0x2 }}, 0x0 }, + {{{ 0x2a, 0x0, 0xf6, 0x87, 0x0 }, { 0x2b, 0x0, 0x76, 0x25, 0x0 }}, 0x0 }, + {{{ 0x85, 0x0, 0xb8, 0x84, 0x0 }, { 0x43, 0x0, 0xe5, 0x8f, 0x0 }}, 0x6 }, + {{{ 0x7, 0x4f, 0xf2, 0x60, 0x0 }, { 0x12, 0x0, 0xf2, 0x72, 0x0 }}, 0x8 }, + {{{ 0x5, 0x40, 0xb3, 0xd3, 0x0 }, { 0x86, 0x80, 0xf2, 0x24, 0x0 }}, 0x2 }, + {{{ 0xd0, 0x0, 0x11, 0xcf, 0x0 }, { 0xd1, 0x0, 0xf4, 0xe8, 0x3 }}, 0x0 }, + {{{ 0x5, 0x4e, 0xda, 0x25, 0x2 }, { 0x1, 0x0, 0xf9, 0x15, 0x0 }}, 0xa }, + {{{ 0x3, 0x0, 0x8f, 0x7, 0x2 }, { 0x2, 0x0, 0xff, 0x6, 0x0 }}, 0x0 }, + {{{ 0x13, 0x0, 0x8f, 0x7, 0x2 }, { 0x2, 0x0, 0xf9, 0x5, 0x0 }}, 0x0 }, + {{{ 0xf0, 0x1, 0x97, 0x17, 0x0 }, { 0x21, 0xd, 0xf1, 0x18, 0x0 }}, 0x8 }, + {{{ 0xf1, 0x41, 0x11, 0x11, 0x0 }, { 0xf1, 0x41, 0x11, 0x11, 0x0 }}, 0x2 }, + {{{ 0x13, 0x0, 0x8f, 0x7, 0x2 }, { 0x2, 0x0, 0xff, 0x6, 0x0 }}, 0x0 }, + {{{ 0x1, 0x0, 0x2f, 0x1, 0x0 }, { 0x1, 0x0, 0xaf, 0x1, 0x3 }}, 0xf }, + {{{ 0x1, 0x6, 0xf3, 0xff, 0x0 }, { 0x8, 0x0, 0xf7, 0xff, 0x0 }}, 0x4 }, + {{{ 0xc0, 0x4f, 0xf1, 0x3, 0x0 }, { 0xbe, 0xc, 0x10, 0x1, 0x0 }}, 0x2 }, + {{{ 0x0, 0x2, 0xf0, 0xff, 0x0 }, { 0x11, 0x80, 0xf0, 0xff, 0x0 }}, 0x6 }, + {{{ 0x81, 0x47, 0xf1, 0x83, 0x0 }, { 0xa2, 0x4, 0x91, 0x86, 0x0 }}, 0x6 }, + {{{ 0xf0, 0xc0, 0xff, 0xff, 0x3 }, { 0xe5, 0x0, 0xfb, 0xf0, 0x0 }}, 0xe }, + {{{ 0x0, 0x2, 0xf0, 0xff, 0x0 }, { 0x11, 0x80, 0xf0, 0xff, 0x0 }}, 0x6 } +}; + +static const PercussionNote percussionNotes[47] = { + {{{ 0x0, 0xb, 0xa8, 0x38, 0x0 }, { 0x0, 0x0, 0xd6, 0x49, 0x0 }}, 0x0, 0x4, 0x1, 0x97, 0x4 }, + {{{ 0xc0, 0xc0, 0xf8, 0x3f, 0x2 }, { 0xc0, 0x0, 0xf6, 0x8e, 0x0 }}, 0x0, 0x4, 0x1, 0xf7, 0x4 }, + {{{ 0xc0, 0x80, 0xc9, 0xab, 0x0 }, { 0xeb, 0x40, 0xb5, 0xf6, 0x0 }}, 0x1, 0x3, 0x1, 0x6a, 0x6 }, + {{{ 0xc, 0x0, 0xd8, 0xa6, 0x0 }, { 0x0, 0x0, 0xd6, 0x4f, 0x0 }}, 0x1, 0x3, 0x1, 0x6c, 0x5 }, + {{{ 0x1, 0x0, 0xe2, 0xd2, 0x0 }, { 0x3, 0x41, 0x8f, 0x48, 0x49 }}, 0xc, 0x4, 0x1, 0x2f, 0x5 }, + {{{ 0x0, 0x0, 0xc8, 0x58, 0x3 }, { 0x0, 0x0, 0xf6, 0x4f, 0x0 }}, 0x9, 0x3, 0x1, 0x108, 0x4 }, + {{{ 0x1, 0x0, 0xff, 0x5, 0x0 }, { 0xf2, 0xff, 0xe0, 0x50, 0x52 }}, 0x5d, 0x2, 0x1, 0x9f, 0x5 }, + {{{ 0xe, 0x9, 0xb9, 0x47, 0x0 }, { 0xeb, 0x40, 0xf5, 0xe6, 0x0 }}, 0x0, 0x0, 0x1, 0x82, 0x6 }, + {{{ 0x0, 0x0, 0xd6, 0x83, 0x0 }, { 0xd6, 0xd7, 0xe0, 0x41, 0x5e }}, 0x4a, 0x2, 0x1, 0xc7, 0x5 }, + {{{ 0x1, 0x9, 0x89, 0x67, 0x0 }, { 0xd6, 0xd7, 0xe0, 0x41, 0x5e }}, 0x4a, 0x0, 0x1, 0x80, 0x6 }, + {{{ 0x1, 0x0, 0xd6, 0x96, 0x0 }, { 0xd6, 0xd7, 0xe0, 0x41, 0x5e }}, 0x4a, 0x2, 0x1, 0xed, 0x5 }, + {{{ 0x0, 0x9, 0xa9, 0x55, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x1, 0x82, 0x6 }, + {{{ 0x2, 0x0, 0xc6, 0x96, 0x0 }, { 0xe0, 0x0, 0xe0, 0x40, 0x0 }}, 0x1, 0x2, 0x1, 0x123, 0x5 }, + {{{ 0x5, 0x0, 0xf6, 0x56, 0x0 }, { 0xf7, 0xff, 0xb3, 0x90, 0x4f }}, 0x1, 0x2, 0x1, 0x15b, 0x5 }, + {{{ 0x1, 0x0, 0xf7, 0x14, 0x0 }, { 0xf7, 0xff, 0x36, 0x90, 0x79 }}, 0xe7, 0x1, 0x1, 0x1ac, 0x5 }, + {{{ 0x0, 0x0, 0xf6, 0x56, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x1, 0x2, 0x1, 0x18b, 0x5 }, + {{{ 0x0, 0x83, 0xfb, 0x5, 0x0 }, { 0xf7, 0x41, 0x39, 0x90, 0x79 }}, 0x1, 0x1, 0x1, 0xc8, 0x5 }, + {{{ 0x0, 0x0, 0xff, 0x5, 0x0 }, { 0xf7, 0xff, 0x36, 0x90, 0x79 }}, 0xe7, 0x1, 0x1, 0xf9, 0x5 }, + {{{ 0x1, 0x0, 0xa0, 0x5, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x2, 0x1, 0x27a, 0x6 }, + {{{ 0x0, 0x5, 0xf3, 0x6, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x2, 0x1, 0x108, 0x7 }, + {{{ 0x1, 0x0, 0xf9, 0x34, 0x0 }, { 0xf7, 0xff, 0x36, 0x90, 0x79 }}, 0xe7, 0x1, 0x1, 0x147, 0x4 }, + {{{ 0x0, 0x0, 0xf7, 0x16, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x2, 0x1, 0x120, 0x6 }, + {{{ 0x1, 0x0, 0xff, 0x5, 0x0 }, { 0xf7, 0xff, 0x36, 0x90, 0x79 }}, 0xe7, 0x1, 0x1, 0x42, 0x6 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x1, 0x0, 0xff, 0x5, 0x0 }, { 0xf7, 0xff, 0x36, 0x90, 0x79 }}, 0xe7, 0x1, 0x1, 0x6d, 0x5 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 } +}; + +const uint16 melodicFrequencies[36] = { + 0x55, 0x5a, 0x60, 0x66, 0x6c, 0x72, 0x79, 0x80, 0x88, + 0x90, 0x99, 0xa1, 0xab, 0xb5, 0xc0, 0xcc, 0xd8, 0xe5, + 0xf2, 0x101, 0x110, 0x120, 0x132, 0x143, 0x156, 0x16b, 0x181, + 0x198, 0x1b0, 0x1ca, 0x1e5, 0x202, 0x220, 0x241, 0x263, 0x286 +}; + +class AdLibDriver; + +class AdLibChannel : public MidiChannel_MPU401 { +public: + void reset(); + + uint8 _program; + uint8 _volume; + uint8 _pedal; +}; + +struct MelodicVoice { + bool _used; + uint8 _channel; + uint8 _program; + + uint8 _key; + uint32 _timestamp; + uint16 _frequency; + int8 _octave; +}; + +class AdLibDriver : public MidiDriver_Emulated { +public: + AdLibDriver(Audio::Mixer *mixer) : MidiDriver_Emulated(mixer) { + for (uint i = 0; i < 16; ++i) + _channels[i].init(this, i); + } + + int open(); + void close(); + void send(uint32 b); + MidiChannel *allocateChannel(); + MidiChannel *getPercussionChannel() { return &_channels[9]; } + + bool isStereo() const { return false; } + int getRate() const { return _mixer->getOutputRate(); } + + void generateSamples(int16 *buf, int len); + +protected: + OPL::OPL *_opl; + AdLibChannel _channels[16]; + MelodicVoice _voices[kNumMelodic]; + uint8 _notesPerPercussion[kNumPercussion]; + + uint _lastVoice; + + uint8 _percussionMask; + + void noteOff(uint8 channel, uint8 note); + void noteOn(uint8 channel, uint8 note, uint8 velocity); + void allNotesOff(); + void setModulationWheel(uint8 channel, uint8 value); + void setFootController(uint8 channel, uint8 value); + void setVolume(uint8 channel, uint8 value); + void setPitchBend(uint8 channel, int16 value); + + void playNote(uint8 voice, uint8 octave, uint16 frequency); + + void programOperatorSimple(uint8 offset, const OPLOperator &op); + void programOperator(uint8 offset, const OPLOperator &op); + void setOperatorLevel(uint8 offset, const OPLOperator &op, uint8 velocity, uint8 channel, bool forceVolume); + + void setupPercussion(const PercussionNote ¬e); + void playPercussion(uint8 channel, const PercussionNote ¬e, uint8 velocity); + + void programMelodicVoice(uint8 voice, uint8 program); + void playMelodicNote(uint8 voice, uint8 channel, uint8 note, uint8 velocity); + void muteMelodicVoice(uint8 voice); + + void initVoices(); +}; + +MidiDriver *createAdLibDriver() { + return new AdLibDriver(g_system->getMixer()); +} + +void AdLibChannel::reset() { + _program = 0; + _volume = 127; + _pedal = 0; +} + +/* + bit 7 - Clear: AM depth is 1 dB + bit 6 - Clear: Vibrato depth is 7 cent + bit 5 - Set: Rhythm enabled (6 melodic voices) + bit 4 - Bass drum off + bit 3 - Snare drum off + bit 2 - Tom tom off + bit 1 - Cymbal off + bit 0 - Hi Hat off +*/ +const uint8 kDefaultPercussionMask = 0x20; + +int AdLibDriver::open() { + if (_isOpen) + return MERR_ALREADY_OPEN; + + MidiDriver_Emulated::open(); + + _opl = OPL::Config::create(); + _opl->init(getRate()); + _opl->writeReg(0x1, 0x20); // set bit 5 (enable all waveforms) + + // Reset the OPL registers. + for (uint i = 0; i < kNumVoices; ++i) { + _opl->writeReg(0xA0 + i, 0); // frequency + _opl->writeReg(0xB0 + i, 0); // key on + _opl->writeReg(0xC0 + i, 0); // feedback + } + _opl->writeReg(0xBD, kDefaultPercussionMask); + + initVoices(); + + _mixer->playStream(Audio::Mixer::kMusicSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); + return 0; +} + +void AdLibDriver::close() { + if (!_isOpen) + return; + + _isOpen = false; + _mixer->stopHandle(_mixerSoundHandle); + + delete _opl; +} + +void AdLibDriver::send(uint32 b) { + uint channel = b & 0xf; + uint cmd = (b >> 4) & 0xf; + uint param1 = (b >> 8) & 0xff; + uint param2 = (b >> 16) & 0xff; + + switch (cmd) { + case 8: + noteOff(channel, param1); + break; + case 9: + // TODO: map volume? + noteOn(channel, param1, param2); + break; + case 11: + // controller change + switch (param1) { + case 1: + setModulationWheel(channel, param2); + break; + case 4: + setFootController(channel, param2); + break; + case 7: + setVolume(channel, param2); + break; + case 123: + // all notes off + allNotesOff(); + break; + } + break; + case 12: + // program change + _channels[channel]._program = param1; + break; + case 14: + setPitchBend(channel, (param1 | (param2 << 7)) - 0x2000); + break; + } +} + +void AdLibDriver::noteOff(uint8 channel, uint8 note) { + if (channel == 9) { + if (note < 35 || note > 81) + return; + + _percussionMask &= ~(1 << percussionNotes[note - 35].percussion); + _opl->writeReg(0xBD, _percussionMask); + return; + } + + for (int i = kNumMelodic - 1; i >= 0; --i) { + if (_voices[i]._channel != channel) + continue; + if (_voices[i]._key != note) + continue; + muteMelodicVoice(i); + _voices[i]._used = false; + return; + } + + //debug(1, "failed to find voice off for channel %d, note %d", channel, note); +} + +void AdLibDriver::noteOn(uint8 channel, uint8 note, uint8 velocity) { + if (channel == 9) { + if (note < 35 || note > 81) + return; + + const PercussionNote &info = percussionNotes[note - 35]; + if (!info.valid) + return; + + if (note != _notesPerPercussion[info.percussion]) { + setupPercussion(info); + _notesPerPercussion[info.percussion] = note; + } + + playPercussion(channel, info, velocity); + return; + } + + if (velocity == 0) { + noteOff(channel, note); + return; + } + + // We want to play a note on a melodic (voice) channel. + + // First, look for a voice playing the same note with the same program. + for (uint i = 0; i < kNumMelodic; ++i) { + if (_voices[i]._channel != channel || _voices[i]._key != note) + continue; + if (_voices[i]._program != _channels[channel]._program) + continue; + muteMelodicVoice(i); + playMelodicNote(i, channel, note, velocity); + return; + } + + // The loops below try to start at _lastVoice and find a voice to use. + // They ignore _lastVoice itself, and update _lastVoice if they succeed. + + // Then, try finding a melodic voice with the same program. + for (uint i = (_lastVoice + 1) % kNumMelodic; i != _lastVoice; i = (i + 1) % kNumMelodic) { + if (_voices[i]._used) + continue; + if (_voices[i]._program != _channels[channel]._program) + continue; + playMelodicNote(i, channel, note, velocity); + _lastVoice = i; + return; + } + + // Then, try finding a free melodic voice of any kind. + for (uint i = (_lastVoice + 1) % kNumMelodic; i != _lastVoice; i = (i + 1) % kNumMelodic) { + if (_voices[i]._used) + continue; + programMelodicVoice(i, _channels[channel]._program); + playMelodicNote(i, channel, note, velocity); + _lastVoice = i; + return; + } + + // Then just try finding a melodic voice with the same program, + // and steal it. + for (uint i = (_lastVoice + 1) % kNumMelodic; i != _lastVoice; i = (i + 1) % kNumMelodic) { + if (_voices[i]._program != _channels[channel]._program) + continue; + muteMelodicVoice(i); + playMelodicNote(i, channel, note, velocity); + _lastVoice = i; + return; + } + + // Finally, just take control of the channel used least recently. + uint voiceId = 0; + uint32 bestTimestamp = 0xffffffff; + for (uint i = 0; i < kNumMelodic; ++i) + if (bestTimestamp > _voices[i]._timestamp) { + voiceId = i; + bestTimestamp = _voices[i]._timestamp; + } + + //debug(1, "ran out of voices for channel %d, note %d, program %d: reused voice %d", channel, note, _channels[channel]._program, voiceId); + programMelodicVoice(voiceId, _channels[channel]._program); + playMelodicNote(voiceId, channel, note, velocity); + _lastVoice = voiceId; +} + +// TODO: this doesn't match original +void AdLibDriver::allNotesOff() { + for (uint i = 0; i < kNumMelodic; ++i) { + muteMelodicVoice(i); + _voices[i]._used = false; + } + + _percussionMask = kDefaultPercussionMask; + _opl->writeReg(0xBD, kDefaultPercussionMask); +} + +void AdLibDriver::setModulationWheel(uint8 channel, uint8 value) { + if (value >= 64) + _percussionMask |= 0x80; + else + _percussionMask &= 0x7f; + + _opl->writeReg(0xBD, _percussionMask); +} + +void AdLibDriver::setFootController(uint8 channel, uint8 value) { + _channels[channel]._pedal = (value >= 64); +} + +void AdLibDriver::setVolume(uint8 channel, uint8 value) { + _channels[channel]._volume = value; +} + +void AdLibDriver::setPitchBend(uint8 channel, int16 value) { + for (uint i = 0; i < kNumMelodic; ++i) { + if (_voices[i]._channel != channel || !_voices[i]._used) + continue; + + // index into frequency table + uint f = 12 + (_voices[i]._key % 12); + + int16 bendAmount = value; + if (bendAmount > 0) { + // bend up two semitones + bendAmount *= (melodicFrequencies[f + 2] - melodicFrequencies[f]); + } else { + // bend down two semitones + bendAmount *= (melodicFrequencies[f] - melodicFrequencies[f - 2]); + } + bendAmount /= 0x2000; + bendAmount += melodicFrequencies[f]; // add the base frequency + playNote(i, _voices[i]._octave, bendAmount); + _voices[i]._timestamp = g_system->getMillis(); + } +} + +void AdLibDriver::playNote(uint8 voice, uint8 octave, uint16 frequency) { + /* Percussions are always fed keyOn = 0 even to set the note, as they are activated using the + BD register instead. I wonder if they can just be fed the same value as melodic voice and + be done with it. */ + uint8 keyOn = (voice < kNumMelodic) ? 0x20 : 0; + + // key on, octave, high 2 bits of frequency + _opl->writeReg(0xB0 + voice, keyOn | ((octave & 7) << 2) | ((frequency >> 8) & 3)); + // low 8 bits of frequency + _opl->writeReg(0xA0 + voice, frequency & 0xff); +} + +void AdLibDriver::programOperatorSimple(uint8 offset, const OPLOperator &op) { + _opl->writeReg(0x40 + offset, op.levels & LEVEL_MASK); + _opl->writeReg(0x60 + offset, op.attackDecay); + _opl->writeReg(0x80 + offset, op.sustainRelease); +} + +void AdLibDriver::programOperator(uint8 offset, const OPLOperator &op) { + _opl->writeReg(0x20 + offset, op.characteristic); + _opl->writeReg(0x60 + offset, op.attackDecay); + _opl->writeReg(0x80 + offset, op.sustainRelease); + _opl->writeReg(0xE0 + offset, op.waveform); + _opl->writeReg(0x40 + offset, op.levels); +} + +const uint16 adlibLogVolume[] = { + 0, 37, 58, 73, 85, 95, 103, 110, 116, 121, 127, 131, 135, 139, 143, 146, + 149, 153, 155, 158, 161, 163, 165, 168, 170, 172, 174, 176, 178, 179, 181, 183, + 184, 186, 188, 189, 191, 192, 193, 195, 196, 197, 198, 200, 201, 202, 203, 204, + 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 219, + 220, 221, 222, 223, 223, 224, 225, 226, 226, 227, 228, 228, 229, 230, 231, 231, + 232, 233, 233, 234, 234, 235, 236, 236, 237, 237, 238, 239, 239, 240, 240, 241, + 241, 242, 242, 243, 244, 244, 245, 245, 246, 246, 247, 247, 248, 248, 248, 249, + 249, 250, 250, 251, 251, 252, 252, 253, 253, 253, 254, 254, 255, 255, 256, 256, + 256 +}; + +void AdLibDriver::setOperatorLevel(uint8 offset, const OPLOperator &op, uint8 velocity, uint8 channel, bool forceVolume) { + uint8 programLevel = LEVEL_MASK; + if (!forceVolume) + programLevel -= (op.levels & LEVEL_MASK); + + uint32 noteLevel = adlibLogVolume[velocity]; + uint32 channelLevel = adlibLogVolume[_channels[channel]._volume]; + // programLevel comes from the static data and is probably already in the correct logarithmic scale + uint32 finalLevel = LEVEL_MASK - ((noteLevel * channelLevel * programLevel) >> 16); + + // high 2 bits are scaling level, the rest is (inversed) volume + _opl->writeReg(0x40 + offset, (op.levels & 0xc0) | (finalLevel & 0x3f)); +} + +const uint8 operatorOffsetsForPercussion[] = { + 0x11, // hi-hat + 0x15, // cymbal + 0x12, // tom tom + 0x14 // snare drum +}; + +void AdLibDriver::setupPercussion(const PercussionNote ¬e) { + if (note.percussion < 4) { + // simple percussion (1 operator) + + // turn off relevant percussion + _percussionMask &= ~(1 << note.percussion); + _opl->writeReg(0xBD, _percussionMask); + + programOperatorSimple(operatorOffsetsForPercussion[note.percussion], note.op[0]); + return; + } + + // bass drum (2 operators) + + // turn off bass drum + _percussionMask &= ~(0x10); + _opl->writeReg(0xBD, _percussionMask); + + programOperator(0x10, note.op[0]); + programOperator(0x13, note.op[1]); + + _opl->writeReg(0xC0 + 6, note.feedbackAlgo); +} + +void AdLibDriver::playPercussion(uint8 channel, const PercussionNote ¬e, uint8 velocity) { + if (note.percussion < 4) { + // simple percussion (1 operator) + + // turn off relevant percussion + _percussionMask &= ~(1 << note.percussion); + _opl->writeReg(0xBD, _percussionMask); + + setOperatorLevel(operatorOffsetsForPercussion[note.percussion], note.op[0], velocity, channel, true); + + if (note.percussion == 2) { + // tom tom + playNote(8, note.octave, note.frequency); + } else if (note.percussion == 3) { + // snare drum + playNote(7, note.octave, note.frequency); + } + + // turn on relevant percussion + _percussionMask |= (1 << note.percussion); + _opl->writeReg(0xBD, _percussionMask); + return; + } + + // turn off bass drum + _percussionMask &= ~(0x10); + _opl->writeReg(0xBD, _percussionMask); + + if (note.feedbackAlgo & 1) { + // operators 1 and 2 in additive synthesis + setOperatorLevel(0x10, note.op[0], velocity, channel, true); + setOperatorLevel(0x13, note.op[1], velocity, channel, true); + } else { + // operator 2 is modulating operator 1 + setOperatorLevel(0x13, note.op[1], velocity, channel, true); + } + + playNote(6, note.octave, note.frequency); + + // turn on bass drum + _percussionMask |= 0x10; + _opl->writeReg(0xBD, _percussionMask); +} + +const uint8 offset1ForMelodic[kNumVoices] = { 0x0, 0x1, 0x2, 0x8, 0x9, 0xa, 0x10, 0x11, 0x12 }; +const uint8 offset2ForMelodic[kNumVoices] = { 0x3, 0x4, 0x5, 0xb, 0xc, 0xd, 0x13, 0x14, 0x15 }; + +void AdLibDriver::programMelodicVoice(uint8 voice, uint8 program) { + assert(program < 128); + assert(voice < kNumMelodic); + + const MelodicProgram &info = melodicPrograms[program]; + uint8 offset1 = offset1ForMelodic[voice]; + uint8 offset2 = offset2ForMelodic[voice]; + + // Start at lowest volume. + _opl->writeReg(0x40 + offset1, LEVEL_MASK); + _opl->writeReg(0x40 + offset2, LEVEL_MASK); + + muteMelodicVoice(voice); + + programOperator(offset1, info.op[0]); + programOperator(offset2, info.op[1]); + + _opl->writeReg(0xC0 + voice, info.feedbackAlgo); +} + +void AdLibDriver::playMelodicNote(uint8 voice, uint8 channel, uint8 note, uint8 velocity) { + assert(voice < kNumMelodic); + + uint8 octave = note / 12; + uint8 f = 12 + (note % 12); + + if (octave > 7) + octave = 7; + + const MelodicProgram &info = melodicPrograms[_channels[channel]._program]; + uint8 offset1 = offset1ForMelodic[voice]; + uint8 offset2 = offset2ForMelodic[voice]; + + if (info.feedbackAlgo & 1) { + setOperatorLevel(offset1, info.op[0], velocity, channel, false); + setOperatorLevel(offset2, info.op[1], velocity, channel, false); + } else { + setOperatorLevel(offset2, info.op[1], velocity, channel, true); + } + + playNote(voice, octave, melodicFrequencies[f]); + + _voices[voice]._program = _channels[channel]._program; + _voices[voice]._key = note; + _voices[voice]._channel = channel; + _voices[voice]._timestamp = g_system->getMillis(); + _voices[voice]._frequency = melodicFrequencies[f]; + _voices[voice]._octave = octave; + _voices[voice]._used = true; +} + +void AdLibDriver::muteMelodicVoice(uint8 voice) { + _opl->writeReg(0xB0 + voice, 0 | ((_voices[voice]._octave & 7) << 2) | ((_voices[voice]._frequency >> 8) & 3)); +} + +MidiChannel *AdLibDriver::allocateChannel() { + for (uint i = 0; i < 16; ++i) { + if (i == 9) + continue; + + if (_channels[i].allocate()) + return &_channels[i]; + } + + return NULL; +} + +void AdLibDriver::generateSamples(int16 *buf, int len) { + memset(buf, 0, sizeof(int16) * len); + _opl->readBuffer(buf, len); +} + +void AdLibDriver::initVoices() { + _percussionMask = kDefaultPercussionMask; + _opl->writeReg(0xBD, _percussionMask); + + for (uint i = 0; i < 16; ++i) + _channels[i].reset(); + + for (uint i = 0; i < kNumMelodic; ++i) { + _voices[i]._key = 0xff; + _voices[i]._program = 0xff; + _voices[i]._channel = 0xff; + _voices[i]._timestamp = 0; + _voices[i]._frequency = 0; + _voices[i]._octave = 0; + _voices[i]._used = false; + } + + for (uint i = 0; i < kNumPercussion; ++i) + _notesPerPercussion[i] = 0xff; + + _lastVoice = 0; +} + +} // namespace Parallaction diff --git a/engines/parallaction/callables_ns.cpp b/engines/parallaction/callables_ns.cpp index 64885c7ff3..c1720a1a8e 100644 --- a/engines/parallaction/callables_ns.cpp +++ b/engines/parallaction/callables_ns.cpp @@ -275,7 +275,7 @@ void Parallaction_ns::_c_contaFoglie(void *parm) { if (num_foglie != 6) return; - _globalFlags |= 0x1000; + g_globalFlags |= 0x1000; return; } @@ -286,7 +286,7 @@ void Parallaction_ns::_c_zeroFoglie(void *parm) { } void Parallaction_ns::_c_trasformata(void *parm) { - _engineFlags ^= kEngineTransformedDonna; + g_engineFlags ^= kEngineTransformedDonna; // No need to invoke changeCharacter here, as // transformation happens on a location switch // and character change is automatically triggered. @@ -295,11 +295,11 @@ void Parallaction_ns::_c_trasformata(void *parm) { void Parallaction_ns::_c_offMouse(void *parm) { _input->setMouseState(MOUSE_DISABLED); - _engineFlags |= kEngineBlockInput; + g_engineFlags |= kEngineBlockInput; } void Parallaction_ns::_c_onMouse(void *parm) { - _engineFlags &= ~kEngineBlockInput; + g_engineFlags &= ~kEngineBlockInput; _input->setMouseState(MOUSE_ENABLED_SHOW); } @@ -389,7 +389,7 @@ void Parallaction_ns::_c_finito(void *parm) { } void Parallaction_ns::_c_ridux(void *parm) { - changeCharacter(_minidinoName); + changeCharacter(g_minidinoName); return; } @@ -444,7 +444,7 @@ void Parallaction_ns::_c_startIntro(void *parm) { _soundManI->playMusic(); } - _engineFlags |= kEngineBlockInput; + g_engineFlags |= kEngineBlockInput; _input->setMouseState(MOUSE_DISABLED); _intro = true; } diff --git a/engines/parallaction/debug.cpp b/engines/parallaction/debug.cpp index 0cb329e0f0..25acac9b06 100644 --- a/engines/parallaction/debug.cpp +++ b/engines/parallaction/debug.cpp @@ -103,7 +103,7 @@ bool Debugger::Cmd_Locations(int argc, const char **argv) { bool Debugger::Cmd_GlobalFlags(int argc, const char **argv) { - uint32 flags = _globalFlags; + uint32 flags = g_globalFlags; DebugPrintf("+------------------------------+---------+\n" "| flag name | value |\n" @@ -128,10 +128,10 @@ bool Debugger::Cmd_ToggleGlobalFlag(int argc, const char **argv) { DebugPrintf("invalid flag '%s'\n", argv[1]); } else { i--; - if ((_globalFlags & (1 << i)) == 0) - _globalFlags |= (1 << i); + if ((g_globalFlags & (1 << i)) == 0) + g_globalFlags |= (1 << i); else - _globalFlags &= ~(1 << i); + g_globalFlags &= ~(1 << i); } break; diff --git a/engines/parallaction/dialogue.cpp b/engines/parallaction/dialogue.cpp index e0bd6a6677..78cc23311f 100644 --- a/engines/parallaction/dialogue.cpp +++ b/engines/parallaction/dialogue.cpp @@ -192,7 +192,7 @@ void DialogueManager::transitionToState(DialogueState newState) { bool DialogueManager::testAnswerFlags(Answer *a) { uint32 flags = _vm->getLocationFlags(); if (a->_yesFlags & kFlagsGlobal) - flags = _globalFlags | kFlagsGlobal; + flags = g_globalFlags | kFlagsGlobal; return ((a->_yesFlags & flags) == a->_yesFlags) && ((a->_noFlags & ~flags) == a->_noFlags); } @@ -370,9 +370,9 @@ protected: bool _askPassword; bool checkPassword() { - return ((!scumm_stricmp(_vm->_char.getBaseName(), _doughName) && _vm->_password.hasPrefix("1732461")) || - (!scumm_stricmp(_vm->_char.getBaseName(), _donnaName) && _vm->_password.hasPrefix("1622")) || - (!scumm_stricmp(_vm->_char.getBaseName(), _dinoName) && _vm->_password.hasPrefix("179"))); + return ((!scumm_stricmp(_vm->_char.getBaseName(), g_doughName) && _vm->_password.hasPrefix("1732461")) || + (!scumm_stricmp(_vm->_char.getBaseName(), g_donnaName) && _vm->_password.hasPrefix("1622")) || + (!scumm_stricmp(_vm->_char.getBaseName(), g_dinoName) && _vm->_password.hasPrefix("179"))); } void resetPassword() { diff --git a/engines/parallaction/disk.cpp b/engines/parallaction/disk.cpp deleted file mode 100644 index f20e05771a..0000000000 --- a/engines/parallaction/disk.cpp +++ /dev/null @@ -1,145 +0,0 @@ -/* 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 "common/iff_container.h" -#include "common/textconsole.h" - -#include "parallaction/disk.h" -#include "parallaction/graphics.h" - -namespace Parallaction { - -void ILBMLoader::setupBuffer(uint32 w, uint32 h) { - _intBuffer = 0; - switch (_bodyMode) { - case BODYMODE_SURFACE: - if (!_surf) { - _surf = new Graphics::Surface; - assert(_surf); - } - _surf->create(w, h, Graphics::PixelFormat::createFormatCLUT8()); - _mode = Graphics::ILBMDecoder::ILBM_UNPACK_PLANES; - _intBuffer = (byte *)_surf->pixels; - break; - - case BODYMODE_MASKBUFFER: - if (!_maskBuffer) { - _maskBuffer = new MaskBuffer; - assert(_maskBuffer); - } - _maskBuffer->create(w, h); - _mode = Graphics::ILBMDecoder::ILBM_2_PACK_PLANES; - _intBuffer = _maskBuffer->data; - break; - - case BODYMODE_PATHBUFFER: - if (!_pathBuffer) { - _pathBuffer = new PathBuffer; - assert(_pathBuffer); - } - _pathBuffer->create(w, h); - _mode = Graphics::ILBMDecoder::ILBM_1_PACK_PLANES; - _intBuffer = _pathBuffer->data; - break; - - default: - error("Invalid bodyMode '%i' for ILBMLoader", _bodyMode); - break; - } -} - -bool ILBMLoader::callback(Common::IFFChunk &chunk) { - switch (chunk._type) { - case ID_BMHD: - _decoder.loadHeader(chunk._stream); - break; - - case ID_CMAP: - if (_palette) { - chunk._stream->read(_palette, chunk._size); - } - break; - - case ID_CRNG: - if (_crng) { - PaletteFxRange *ptr = &_crng[_numCRNG]; - chunk._stream->read((byte *)ptr, chunk._size); - ptr->_timer = FROM_BE_16(ptr->_timer); - ptr->_step = FROM_BE_16(ptr->_step); - ptr->_flags = FROM_BE_16(ptr->_flags); - ++_numCRNG; - } - break; - - case ID_BODY: - setupBuffer(_decoder._header.width, _decoder._header.height); - assert(_intBuffer); - _decoder.loadBitmap(_mode, _intBuffer, chunk._stream); - return true; // stop the parser - } - - return false; -} - -void ILBMLoader::load(Common::ReadStream *in, bool disposeStream) { - Common::IFFParser parser(in, disposeStream); - Common::Functor1Mem< Common::IFFChunk&, bool, ILBMLoader > c(this, &ILBMLoader::callback); - parser.parse(c); -} - -ILBMLoader::ILBMLoader(uint32 bodyMode, byte *palette, PaletteFxRange *crng) { - _bodyMode = bodyMode; - _surf = 0; - _maskBuffer = 0; - _pathBuffer = 0; - _palette = palette; - _crng = crng; - _numCRNG = 0; -} - -ILBMLoader::ILBMLoader(Graphics::Surface *surf, byte *palette, PaletteFxRange *crng) { - _bodyMode = ILBMLoader::BODYMODE_SURFACE; - _surf = surf; - _palette = palette; - _crng = crng; - _numCRNG = 0; -} - -ILBMLoader::ILBMLoader(MaskBuffer *buffer) { - _bodyMode = ILBMLoader::BODYMODE_MASKBUFFER; - _maskBuffer = buffer; - _palette = 0; - _crng = 0; - _numCRNG = 0; -} - -ILBMLoader::ILBMLoader(PathBuffer *buffer) { - _bodyMode = ILBMLoader::BODYMODE_PATHBUFFER; - _pathBuffer = buffer; - _palette = 0; - _crng = 0; - _numCRNG = 0; -} - - - -} diff --git a/engines/parallaction/disk.h b/engines/parallaction/disk.h index d1171c3179..63e33dcfbd 100644 --- a/engines/parallaction/disk.h +++ b/engines/parallaction/disk.h @@ -28,13 +28,10 @@ #include "common/archive.h" #include "common/str.h" -#include "graphics/iff.h" - namespace Common { class FSDirectory; class ReadStream; class SeekableReadStream; -struct IFFChunk; } namespace Graphics { @@ -86,36 +83,6 @@ public: virtual PathBuffer *loadPath(const char *name, uint32 w, uint32 h) { return 0; } }; -struct PaletteFxRange; - -struct ILBMLoader { - enum { - BODYMODE_SURFACE, - BODYMODE_MASKBUFFER, - BODYMODE_PATHBUFFER - }; - uint32 _bodyMode; - Graphics::Surface *_surf; - MaskBuffer *_maskBuffer; - PathBuffer *_pathBuffer; - byte *_palette; - PaletteFxRange *_crng; - uint32 _mode; - byte* _intBuffer; - uint32 _numCRNG; - Graphics::ILBMDecoder _decoder; - - ILBMLoader(uint32 bodyMode, byte *palette = 0, PaletteFxRange *crng = 0); - ILBMLoader(Graphics::Surface *surf, byte *palette = 0, PaletteFxRange *crng = 0); - ILBMLoader(MaskBuffer *buffer); - ILBMLoader(PathBuffer *buffer); - - bool callback(Common::IFFChunk &chunk); - void setupBuffer(uint32 w, uint32 h); - void load(Common::ReadStream *in, bool disposeStream = false); -}; - - class Disk_ns : public Disk { protected: diff --git a/engines/parallaction/disk_br.cpp b/engines/parallaction/disk_br.cpp index 5e39c893db..3135c3e8c5 100644 --- a/engines/parallaction/disk_br.cpp +++ b/engines/parallaction/disk_br.cpp @@ -20,18 +20,17 @@ * */ -#include "graphics/iff.h" - #include "common/config-manager.h" #include "common/fs.h" #include "common/textconsole.h" +#include "graphics/decoders/iff.h" #include "parallaction/parallaction.h" #include "parallaction/parser.h" namespace Parallaction { -extern byte _braAmigaFramesDefaultPalette[]; +extern byte braAmigaFramesDefaultPalette[]; struct Sprite { uint16 size; @@ -459,8 +458,9 @@ void AmigaDisk_br::adjustForPalette(Graphics::Surface &surf, int transparentColo void AmigaDisk_br::loadBackground(BackgroundInfo& info, const char *filename) { byte r,g,b; - byte *p; + const byte *p; Common::SeekableReadStream *stream; + Graphics::IFFDecoder decoder; uint i; stream = tryOpenFile("backs/" + Common::String(filename), ".ap"); @@ -475,7 +475,7 @@ void AmigaDisk_br::loadBackground(BackgroundInfo& info, const char *filename) { } delete stream; } else { - p = _braAmigaFramesDefaultPalette; + p = braAmigaFramesDefaultPalette; for (i = 0; i < 16; i++) { r = *p >> 2; p++; @@ -488,15 +488,16 @@ void AmigaDisk_br::loadBackground(BackgroundInfo& info, const char *filename) { } stream = openFile("backs/" + Common::String(filename), ".bkg"); + decoder.loadStream(*stream); - byte pal[768]; - ILBMLoader loader(&info.bg, pal); - loader.load(stream, true); - + info.bg.copyFrom(*decoder.getSurface()); info.width = info.bg.w; info.height = info.bg.h; - p = pal; + // Overwrite the first color (transparent key) in the palette + p = decoder.getPalette(); + info.palette.setEntry(0, p[0] >> 2, p[1] >> 2, p[2] >> 0); + for (i = 16; i < 32; i++) { r = *p >> 2; p++; @@ -507,9 +508,6 @@ void AmigaDisk_br::loadBackground(BackgroundInfo& info, const char *filename) { info.palette.setEntry(i, r, g, b); } - // Overwrite the first color (transparent key) in the palette - info.palette.setEntry(0, pal[0] >> 2, pal[1] >> 2, pal[2] >> 0); - // background data is drawn used the upper portion of the palette adjustForPalette(info.bg); } @@ -546,10 +544,15 @@ MaskBuffer *AmigaDisk_br::loadMask(const char *name, uint32 w, uint32 h) { return 0; } - ILBMLoader loader(ILBMLoader::BODYMODE_MASKBUFFER); - loader.load(stream, true); + Graphics::IFFDecoder decoder; + decoder.setNumRelevantPlanes(2); // use only 2 first bits from each pixels + decoder.setPixelPacking(true); // pack 4 2bit pixels into 1 byte + decoder.loadStream(*stream); - MaskBuffer *buffer = loader._maskBuffer; + MaskBuffer *buffer = new MaskBuffer; + // surface width was shrunk to 1/4th of the bitmap width due to the pixel packing + buffer->create(decoder.getSurface()->w * 4, decoder.getSurface()->h); + memcpy(buffer->data, decoder.getSurface()->pixels, buffer->size); buffer->bigEndian = true; finalpass(buffer->data, buffer->size); return buffer; @@ -580,12 +583,12 @@ GfxObj* AmigaDisk_br::loadStatic(const char* name) { Common::String sName = name; Common::SeekableReadStream *stream = openFile("ras/" + sName, ".ras"); + Graphics::IFFDecoder decoder; + decoder.loadStream(*stream); - ILBMLoader loader(ILBMLoader::BODYMODE_SURFACE); - loader.load(stream, true); - - Graphics::Surface* surf = loader._surf; + Graphics::Surface *surf = new Graphics::Surface; assert(surf); + surf->copyFrom(*decoder.getSurface()); // Static pictures are drawn used the upper half of the palette: this must be // done before shadow mask is applied. This way, only really transparent pixels @@ -617,7 +620,7 @@ GfxObj* AmigaDisk_br::loadStatic(const char* name) { } } - delete []shadow; + delete[] shadow; delete stream; } @@ -717,23 +720,23 @@ GfxObj* AmigaDisk_br::loadObjects(const char *name, uint8 part) { debugC(5, kDebugDisk, "AmigaDisk_br::loadObjects"); Common::SeekableReadStream *stream = openFile(name); - ILBMLoader loader(ILBMLoader::BODYMODE_SURFACE); - loader.load(stream, true); + Graphics::IFFDecoder decoder; + decoder.loadStream(*stream); uint16 max = objectsMax[part]; if (_vm->getFeatures() & GF_DEMO) max = 72; byte *data = new byte[max * 2601]; - byte *srcPtr = (byte *)loader._surf->getBasePtr(0,0); - int w = loader._surf->w; + const byte *srcPtr = (const byte *)decoder.getSurface()->getBasePtr(0,0); + int w = decoder.getSurface()->w; // Convert to the expected display format for (int i = 0; i < max; i++) { uint16 x = (i % 8) * 51; uint16 y = (i / 8) * 51; - byte *src = srcPtr + y * w + x; + const byte *src = srcPtr + y * w + x; byte *dst = data + i * 2601; for (int h = 0; h < 51; h++) { memcpy(dst, src, 51); @@ -741,7 +744,6 @@ GfxObj* AmigaDisk_br::loadObjects(const char *name, uint8 part) { dst += 51; } } - delete loader._surf; return new GfxObj(0, new Cnv(max, 51, 51, data, true)); } diff --git a/engines/parallaction/disk_ns.cpp b/engines/parallaction/disk_ns.cpp index 839b2c6834..f03f16ca37 100644 --- a/engines/parallaction/disk_ns.cpp +++ b/engines/parallaction/disk_ns.cpp @@ -22,9 +22,11 @@ #include "common/config-manager.h" #include "common/fs.h" +#include "common/iff_container.h" #include "common/memstream.h" #include "common/substream.h" #include "common/textconsole.h" +#include "graphics/decoders/iff.h" #include "parallaction/parser.h" #include "parallaction/parallaction.h" @@ -262,8 +264,15 @@ Common::SeekableReadStream *DosDisk_ns::tryOpenFile(const char* name) { Script* Disk_ns::loadLocation(const char *name) { char path[PATH_LEN]; + const char *charName = _vm->_char.getBaseName(); - sprintf(path, "%s%s/%s.loc", _vm->_char.getBaseName(), _language.c_str(), name); + // WORKAROUND: Special case for the Multilingual DOS version: during the ending + // sequence, it tries to load a non-existing file using "Dinor" as a character + // name. In this case, the character name should be just "dino". + if (!strcmp(charName, "Dinor")) + charName = "dino"; + + sprintf(path, "%s%s/%s.loc", charName, _language.c_str(), name); debugC(3, kDebugDisk, "Disk_ns::loadLocation(%s): trying '%s'", name, path); Common::SeekableReadStream *stream = tryOpenFile(path); @@ -305,7 +314,7 @@ void DosDisk_ns::decodeCnv(byte *data, uint16 numFrames, uint16 width, uint16 he int32 decsize = numFrames * width * height; bool packed = (stream->size() - stream->pos()) != decsize; if (packed) { - Graphics::PackBitsReadStream decoder(*stream); + Common::PackBitsReadStream decoder(*stream); decoder.read(data, decsize); } else { stream->read(data, decsize); @@ -328,7 +337,7 @@ GfxObj* DosDisk_ns::loadTalk(const char *name) { } char v20[30]; - if (_engineFlags & kEngineTransformedDonna) { + if (g_engineFlags & kEngineTransformedDonna) { sprintf(v20, "%stta.cnv", name); } else { sprintf(v20, "%stal.cnv", name); @@ -832,7 +841,7 @@ void AmigaDisk_ns::decodeCnv(byte *data, uint16 numFrames, uint16 width, uint16 assert(buf); stream->read(buf, rawsize); unpackBitmap(data, buf, numFrames, bytesPerPlane, height); - delete []buf; + delete[] buf; } #undef NUM_PLANES @@ -907,17 +916,15 @@ void AmigaDisk_ns::buildMask(byte* buf) { void AmigaDisk_ns::loadBackground(BackgroundInfo& info, const char *name) { - PaletteFxRange ranges[6]; - byte pal[768]; - Common::SeekableReadStream *s = openFile(name); - ILBMLoader loader(&info.bg, pal, ranges); - loader.load(s, true); + Graphics::IFFDecoder decoder; + decoder.loadStream(*s); + info.bg.copyFrom(*decoder.getSurface()); info.width = info.bg.w; info.height = info.bg.h; - byte *p = pal; + const byte *p = decoder.getPalette(); for (uint i = 0; i < 32; i++) { byte r = *p >> 2; p++; @@ -928,8 +935,15 @@ void AmigaDisk_ns::loadBackground(BackgroundInfo& info, const char *name) { info.palette.setEntry(i, r, g, b); } - for (uint j = 0; j < 6; j++) { - info.setPaletteRange(j, ranges[j]); + const Common::Array<Graphics::IFFDecoder::PaletteRange> &paletteRanges = decoder.getPaletteRanges(); + for (uint j = 0; j < 6 && j < paletteRanges.size(); j++) { + PaletteFxRange range; + range._timer = paletteRanges[j].timer; + range._step = paletteRanges[j].step; + range._flags = paletteRanges[j].flags; + range._first = paletteRanges[j].first; + range._last = paletteRanges[j].last; + info.setPaletteRange(j, range); } } @@ -945,19 +959,25 @@ void AmigaDisk_ns::loadMask_internal(BackgroundInfo& info, const char *name) { return; // no errors if missing mask files: not every location has one } - byte pal[768]; - ILBMLoader loader(ILBMLoader::BODYMODE_MASKBUFFER, pal); - loader.load(s, true); + Graphics::IFFDecoder decoder; + decoder.setNumRelevantPlanes(2); // use only 2 first bits from each pixel + decoder.setPixelPacking(true); // pack 4 2bit pixels into 1 byte + decoder.loadStream(*s); + const byte *p = decoder.getPalette(); byte r, g, b; for (uint i = 0; i < 4; i++) { - r = pal[i*3]; - g = pal[i*3+1]; - b = pal[i*3+2]; + r = p[i*3]; + g = p[i*3+1]; + b = p[i*3+2]; info.layers[i] = (((r << 4) & 0xF00) | (g & 0xF0) | (b >> 4)) & 0xFF; } - info._mask = loader._maskBuffer; + info._mask = new MaskBuffer; + // surface width was shrunk to 1/4th of the bitmap width due to the pixel packing + info._mask->create(decoder.getSurface()->w * 4, decoder.getSurface()->h); + memcpy(info._mask->data, decoder.getSurface()->pixels, info._mask->size); + info._mask->bigEndian = true; } void AmigaDisk_ns::loadPath_internal(BackgroundInfo& info, const char *name) { @@ -970,9 +990,15 @@ void AmigaDisk_ns::loadPath_internal(BackgroundInfo& info, const char *name) { return; // no errors if missing path files: not every location has one } - ILBMLoader loader(ILBMLoader::BODYMODE_PATHBUFFER); - loader.load(s, true); - info._path = loader._pathBuffer; + Graphics::IFFDecoder decoder; + decoder.setNumRelevantPlanes(1); // use only first bit from each pixel + decoder.setPixelPacking(true); // pack 8 1bit pixels into 1 byte + decoder.loadStream(*s); + + info._path = new PathBuffer; + // surface width was shrunk to 1/8th of the bitmap width due to the pixel packing + info._path->create(decoder.getSurface()->w * 8, decoder.getSurface()->h); + memcpy(info._path->data, decoder.getSurface()->pixels, info._path->size); info._path->bigEndian = true; } diff --git a/engines/parallaction/exec.cpp b/engines/parallaction/exec.cpp index 8594d02641..122abf9e0e 100644 --- a/engines/parallaction/exec.cpp +++ b/engines/parallaction/exec.cpp @@ -56,7 +56,7 @@ void ProgramExec::runScript(ProgramPtr script, AnimationPtr a) { } void ProgramExec::runScripts(ProgramList::iterator first, ProgramList::iterator last) { - if (_engineFlags & kEnginePauseJobs) { + if (g_engineFlags & kEnginePauseJobs) { return; } @@ -110,7 +110,7 @@ void CommandExec::runList(CommandList::iterator first, CommandList::iterator las } if (cmd->_flagsOn & kFlagsGlobal) { - useFlags = _globalFlags | kFlagsGlobal; + useFlags = g_globalFlags | kFlagsGlobal; useLocalFlags = false; } else { useFlags = _vm->getLocationFlags(); @@ -182,7 +182,7 @@ void CommandExec::suspend() { } void CommandExec::runSuspended() { - if (_engineFlags & kEngineWalking) { + if (g_engineFlags & kEngineWalking) { return; } diff --git a/engines/parallaction/exec_br.cpp b/engines/parallaction/exec_br.cpp index 658ef5af82..985ea29311 100644 --- a/engines/parallaction/exec_br.cpp +++ b/engines/parallaction/exec_br.cpp @@ -307,7 +307,7 @@ DECLARE_COMMAND_OPCODE(testsfx) { DECLARE_COMMAND_OPCODE(ret) { - _engineFlags |= kEngineReturn; + g_engineFlags |= kEngineReturn; } @@ -327,7 +327,7 @@ DECLARE_INSTRUCTION_OPCODE(invalid) { DECLARE_COMMAND_OPCODE(clear) { if (ctxt._cmd->_flags & kFlagsGlobal) { ctxt._cmd->_flags &= ~kFlagsGlobal; - _globalFlags &= ~ctxt._cmd->_flags; + g_globalFlags &= ~ctxt._cmd->_flags; } else { _vm->clearLocationFlags(ctxt._cmd->_flags); } @@ -356,7 +356,7 @@ DECLARE_COMMAND_OPCODE(get) { DECLARE_COMMAND_OPCODE(toggle) { if (ctxt._cmd->_flags & kFlagsGlobal) { ctxt._cmd->_flags &= ~kFlagsGlobal; - _globalFlags ^= ctxt._cmd->_flags; + g_globalFlags ^= ctxt._cmd->_flags; } else { _vm->toggleLocationFlags(ctxt._cmd->_flags); } @@ -373,7 +373,7 @@ DECLARE_COMMAND_OPCODE(invalid) { DECLARE_COMMAND_OPCODE(set) { if (ctxt._cmd->_flags & kFlagsGlobal) { ctxt._cmd->_flags &= ~kFlagsGlobal; - _globalFlags |= ctxt._cmd->_flags; + g_globalFlags |= ctxt._cmd->_flags; } else { _vm->setLocationFlags(ctxt._cmd->_flags); } diff --git a/engines/parallaction/exec_ns.cpp b/engines/parallaction/exec_ns.cpp index d8fbdea971..3ea4332e50 100644 --- a/engines/parallaction/exec_ns.cpp +++ b/engines/parallaction/exec_ns.cpp @@ -151,7 +151,7 @@ DECLARE_INSTRUCTION_OPCODE(call) { DECLARE_INSTRUCTION_OPCODE(wait) { - if (_engineFlags & kEngineWalking) { + if (g_engineFlags & kEngineWalking) { ctxt._ip--; ctxt._suspend = true; } @@ -198,7 +198,7 @@ DECLARE_COMMAND_OPCODE(invalid) { DECLARE_COMMAND_OPCODE(set) { if (ctxt._cmd->_flags & kFlagsGlobal) { ctxt._cmd->_flags &= ~kFlagsGlobal; - _globalFlags |= ctxt._cmd->_flags; + g_globalFlags |= ctxt._cmd->_flags; } else { _vm->setLocationFlags(ctxt._cmd->_flags); } @@ -208,7 +208,7 @@ DECLARE_COMMAND_OPCODE(set) { DECLARE_COMMAND_OPCODE(clear) { if (ctxt._cmd->_flags & kFlagsGlobal) { ctxt._cmd->_flags &= ~kFlagsGlobal; - _globalFlags &= ~ctxt._cmd->_flags; + g_globalFlags &= ~ctxt._cmd->_flags; } else { _vm->clearLocationFlags(ctxt._cmd->_flags); } @@ -267,7 +267,7 @@ DECLARE_COMMAND_OPCODE(call) { DECLARE_COMMAND_OPCODE(toggle) { if (ctxt._cmd->_flags & kFlagsGlobal) { ctxt._cmd->_flags &= ~kFlagsGlobal; - _globalFlags ^= ctxt._cmd->_flags; + g_globalFlags ^= ctxt._cmd->_flags; } else { _vm->toggleLocationFlags(ctxt._cmd->_flags); } diff --git a/engines/parallaction/font.cpp b/engines/parallaction/font.cpp index d4c9aefd32..3b40960381 100644 --- a/engines/parallaction/font.cpp +++ b/engines/parallaction/font.cpp @@ -29,7 +29,7 @@ namespace Parallaction { -extern byte _amigaTopazFont[]; +extern byte amigaTopazFont[]; class BraFont : public Font { @@ -675,7 +675,7 @@ void Parallaction_ns::initFonts() { _introFont = _disk->loadFont("slide"); } else { _dialogueFont = _disk->loadFont("comic"); - Common::MemoryReadStream stream(_amigaTopazFont, 2600, DisposeAfterUse::NO); + Common::MemoryReadStream stream(amigaTopazFont, 2600, DisposeAfterUse::NO); _labelFont = new AmigaFont(stream); _menuFont = _disk->loadFont("slide"); _introFont = _disk->loadFont("intro"); diff --git a/engines/parallaction/gfxbase.cpp b/engines/parallaction/gfxbase.cpp index 852235ce34..a9889cc7af 100644 --- a/engines/parallaction/gfxbase.cpp +++ b/engines/parallaction/gfxbase.cpp @@ -152,22 +152,22 @@ void Gfx::freeCharacterObjects() { freeDialogueObjects(); } -void BackgroundInfo::loadGfxObjMask(const char *name, GfxObj *obj) { +void BackgroundInfo::loadGfxObjMask(Parallaction *vm, const char *name, GfxObj *obj) { debugC(1, kDebugGraphics, "BackgroundInfo::loadGfxObjMask(\"%s\")", name); Common::Rect rect; obj->getRect(0, rect); - MaskBuffer *buf = _vm->_disk->loadMask(name, rect.width(), rect.height()); + MaskBuffer *buf = vm->_disk->loadMask(name, rect.width(), rect.height()); obj->_maskId = addMaskPatch(buf); obj->_hasMask = true; } -void BackgroundInfo::loadGfxObjPath(const char *name, GfxObj *obj) { +void BackgroundInfo::loadGfxObjPath(Parallaction *vm, const char *name, GfxObj *obj) { Common::Rect rect; obj->getRect(0, rect); - PathBuffer *buf = _vm->_disk->loadPath(name, rect.width(), rect.height()); + PathBuffer *buf = vm->_disk->loadPath(name, rect.width(), rect.height()); obj->_pathId = addPathPatch(buf); obj->_hasPath = true; @@ -226,6 +226,11 @@ void Gfx::drawGfxObject(GfxObj *obj, Graphics::Surface &surf) { rect.translate(x, y); data = obj->getData(obj->frame); + // WORKAROUND: During the end credits, game scripts try to show a + // non-existing frame. We change it to an existing one here. + if (obj->frame == 14 && obj->getNum() == 9 && !strcmp(obj->getName(), "Dinor")) + obj->frame = 8; + if (obj->getSize(obj->frame) == obj->getRawSize(obj->frame)) { blt(rect, data, &surf, obj->layer, obj->scale, obj->transparentKey); } else { diff --git a/engines/parallaction/graphics.cpp b/engines/parallaction/graphics.cpp index 6868505c52..59cd02e6ef 100644 --- a/engines/parallaction/graphics.cpp +++ b/engines/parallaction/graphics.cpp @@ -80,11 +80,11 @@ void drawCircle(int xCenter, int yCenter, int radius, int color, void (*plotProc Palette::Palette() { - int gameType = _vm->getGameType(); + int gameType = g_vm->getGameType(); if (gameType == GType_Nippon) { _colors = 32; - _hb = (_vm->getPlatform() == Common::kPlatformAmiga); + _hb = (g_vm->getPlatform() == Common::kPlatformAmiga); } else if (gameType == GType_BRA) { _colors = 256; @@ -766,7 +766,7 @@ Gfx::~Gfx() { freeLabels(); - delete []_unpackedBitmap; + delete[] _unpackedBitmap; return; } diff --git a/engines/parallaction/graphics.h b/engines/parallaction/graphics.h index b43dd193b5..e9daabb194 100644 --- a/engines/parallaction/graphics.h +++ b/engines/parallaction/graphics.h @@ -144,7 +144,7 @@ public: ~Cnv() { if (_freeData) - delete []_data; + delete[] _data; } byte* getFramePtr(uint16 index) { @@ -377,14 +377,14 @@ public: void toggleMaskPatch(uint id, int x, int y, bool apply); uint16 getMaskLayer(uint16 z) const; void finalizeMask(); - void loadGfxObjMask(const char *name, GfxObj *obj); + void loadGfxObjMask(Parallaction *vm, const char *name, GfxObj *obj); // path management bool hasPath(); uint addPathPatch(PathBuffer *patch); void togglePathPatch(uint id, int x, int y, bool apply); void finalizePath(); - void loadGfxObjPath(const char *name, GfxObj *obj); + void loadGfxObjPath(Parallaction *vm, const char *name, GfxObj *obj); }; diff --git a/engines/parallaction/gui_ns.cpp b/engines/parallaction/gui_ns.cpp index 3794aeae29..082c37f666 100644 --- a/engines/parallaction/gui_ns.cpp +++ b/engines/parallaction/gui_ns.cpp @@ -787,7 +787,7 @@ public: } destroyLabels(); - _engineFlags &= ~kEngineBlockInput; + g_engineFlags &= ~kEngineBlockInput; return _helper->getState("selectcharacter"); } diff --git a/engines/parallaction/input.cpp b/engines/parallaction/input.cpp index 453bf9849d..4fbd9b99cc 100644 --- a/engines/parallaction/input.cpp +++ b/engines/parallaction/input.cpp @@ -188,15 +188,15 @@ int Input::updateGameInput() { int event = kEvNone; if (!isMouseEnabled() || - (_engineFlags & kEngineBlockInput) || - (_engineFlags & kEngineWalking) || - (_engineFlags & kEngineChangeLocation)) { + (g_engineFlags & kEngineBlockInput) || + (g_engineFlags & kEngineWalking) || + (g_engineFlags & kEngineChangeLocation)) { debugC(3, kDebugInput, "updateGameInput: input flags (mouse: %i, block: %i, walking: %i, changeloc: %i)", isMouseEnabled(), - (_engineFlags & kEngineBlockInput) == 0, - (_engineFlags & kEngineWalking) == 0, - (_engineFlags & kEngineChangeLocation) == 0 + (g_engineFlags & kEngineBlockInput) == 0, + (g_engineFlags & kEngineWalking) == 0, + (g_engineFlags & kEngineChangeLocation) == 0 ); return event; @@ -289,7 +289,7 @@ void Input::walkTo(const Common::Point &dest) { bool Input::translateGameInput() { - if (_engineFlags & kEnginePauseJobs) { + if (g_engineFlags & kEnginePauseJobs) { return false; } @@ -312,7 +312,7 @@ bool Input::translateGameInput() { // test if mouse is hovering on an interactive zone for the currently selected inventory item ZonePtr z = _vm->hitZone(_activeItem._id, mousePos.x, mousePos.y); - if (((_mouseButtons == kMouseLeftUp) && (_activeItem._id == 0) && ((_engineFlags & kEngineWalking) == 0)) && ((!z) || (ACTIONTYPE(z) != kZoneCommand))) { + if (((_mouseButtons == kMouseLeftUp) && (_activeItem._id == 0) && ((g_engineFlags & kEngineWalking) == 0)) && ((!z) || (ACTIONTYPE(z) != kZoneCommand))) { walkTo(mousePos); return true; } @@ -361,7 +361,7 @@ void Input::enterInventoryMode() { if (hitCharacter) { if (_activeItem._id != 0) { _activeItem._index = (_activeItem._id >> 16) & 0xFFFF; - _engineFlags |= kEngineDragging; + g_engineFlags |= kEngineDragging; } else { setArrowCursor(); } @@ -384,9 +384,9 @@ void Input::exitInventoryMode() { int pos = _vm->getHoverInventoryItem(mousePos.x, mousePos.y); _vm->highlightInventoryItem(-1); // disable - if ((_engineFlags & kEngineDragging)) { + if ((g_engineFlags & kEngineDragging)) { - _engineFlags &= ~kEngineDragging; + g_engineFlags &= ~kEngineDragging; ZonePtr z = _vm->hitZone(kZoneMerge, _activeItem._index, _vm->getInventoryItemIndex(pos)); if (z) { diff --git a/engines/parallaction/module.mk b/engines/parallaction/module.mk index d65653cd92..f8a4e0b9a3 100644 --- a/engines/parallaction/module.mk +++ b/engines/parallaction/module.mk @@ -1,13 +1,13 @@ MODULE := engines/parallaction MODULE_OBJS := \ + adlib.o \ balloons.o \ callables_br.o \ callables_ns.o \ debug.o \ detection.o \ dialogue.o \ - disk.o \ disk_br.o \ disk_ns.o \ exec.o \ diff --git a/engines/parallaction/objects.cpp b/engines/parallaction/objects.cpp index d3529c5dd9..50556c3ec4 100644 --- a/engines/parallaction/objects.cpp +++ b/engines/parallaction/objects.cpp @@ -203,7 +203,7 @@ Zone::Zone() { } Zone::~Zone() { - _vm->_gfx->unregisterLabel(_label); + g_vm->_gfx->unregisterLabel(_label); delete _label; } @@ -325,7 +325,7 @@ int16 ScriptVar::getValue() { } if (_flags & kParaRandom) { - return (_vm->_rnd.getRandomNumber(65536) * _value) >> 16; + return (g_vm->_rnd.getRandomNumber(65536) * _value) >> 16; } error("Parameter is not an r-value"); diff --git a/engines/parallaction/parallaction.cpp b/engines/parallaction/parallaction.cpp index 3b1b7d54a0..e6ef53aa78 100644 --- a/engines/parallaction/parallaction.cpp +++ b/engines/parallaction/parallaction.cpp @@ -33,12 +33,12 @@ #include "parallaction/walk.h" namespace Parallaction { -Parallaction *_vm = NULL; +Parallaction *g_vm = NULL; // public stuff -char _saveData1[30] = { '\0' }; -uint32 _engineFlags = 0; -uint32 _globalFlags = 0; +char g_saveData1[30] = { '\0' }; +uint32 g_engineFlags = 0; +uint32 g_globalFlags = 0; // private stuff @@ -48,7 +48,7 @@ Parallaction::Parallaction(OSystem *syst, const PARALLACTIONGameDescription *gam // Setup mixer syncSoundSettings(); - _vm = this; + g_vm = this; DebugMan.addDebugChannel(kDebugDialogue, "dialogue", "Dialogues debug level"); DebugMan.addDebugChannel(kDebugParser, "parser", "Parser debug level"); DebugMan.addDebugChannel(kDebugDisk, "disk", "Disk debug level"); @@ -87,7 +87,7 @@ Parallaction::~Parallaction() { Common::Error Parallaction::init() { _gameType = getGameType(); - _engineFlags = 0; + g_engineFlags = 0; _objectsNames = NULL; _globalFlagsNames = NULL; _location._hasSound = false; @@ -129,13 +129,9 @@ GUI::Debugger *Parallaction::getDebugger() { return _debugger; } -bool canScroll() { - return (_vm->_gfx->_backgroundInfo->width > _vm->_screenWidth); -} - void Parallaction::updateView() { - if ((_engineFlags & kEnginePauseJobs) && (_input->_inputMode != Input::kInputModeInventory)) { + if ((g_engineFlags & kEnginePauseJobs) && (_input->_inputMode != Input::kInputModeInventory)) { return; } @@ -147,14 +143,14 @@ void Parallaction::updateView() { void Parallaction::pauseJobs() { debugC(9, kDebugExec, "pausing jobs execution"); - _engineFlags |= kEnginePauseJobs; + g_engineFlags |= kEnginePauseJobs; return; } void Parallaction::resumeJobs() { debugC(9, kDebugExec, "resuming jobs execution"); - _engineFlags &= ~kEnginePauseJobs; + g_engineFlags &= ~kEnginePauseJobs; return; } @@ -265,7 +261,7 @@ void Parallaction::runGameFrame(int event) { if (shouldQuit()) return; - if (_engineFlags & kEngineChangeLocation) { + if (g_engineFlags & kEngineChangeLocation) { changeLocation(); } @@ -900,14 +896,14 @@ void CharacterName::bind(const char *name) { if (!_dummy) { if (!strcmp(name, "donna")) { - _engineFlags &= ~kEngineTransformedDonna; + g_engineFlags &= ~kEngineTransformedDonna; } else { - if (_engineFlags & kEngineTransformedDonna) { + if (g_engineFlags & kEngineTransformedDonna) { _suffix = _suffixTras; } else { const char *s = strstr(name, "tras"); if (s) { - _engineFlags |= kEngineTransformedDonna; + g_engineFlags |= kEngineTransformedDonna; _suffix = _suffixTras; end = s; } @@ -953,7 +949,7 @@ void Parallaction::beep() { void Parallaction::scheduleLocationSwitch(const char *location) { debugC(9, kDebugExec, "scheduleLocationSwitch(%s)\n", location); _newLocationName = location; - _engineFlags |= kEngineChangeLocation; + g_engineFlags |= kEngineChangeLocation; } } // End of namespace Parallaction diff --git a/engines/parallaction/parallaction.h b/engines/parallaction/parallaction.h index 0d56b62e2f..2dbb0227d6 100644 --- a/engines/parallaction/parallaction.h +++ b/engines/parallaction/parallaction.h @@ -104,17 +104,17 @@ struct PARALLACTIONGameDescription; -extern uint32 _engineFlags; -extern char _saveData1[]; -extern uint32 _globalFlags; -extern const char *_dinoName; -extern const char *_donnaName; -extern const char *_doughName; -extern const char *_drkiName; -extern const char *_minidinoName; -extern const char *_minidonnaName; -extern const char *_minidoughName; -extern const char *_minidrkiName; +extern uint32 g_engineFlags; +extern char g_saveData1[]; +extern uint32 g_globalFlags; +extern const char *g_dinoName; +extern const char *g_donnaName; +extern const char *g_doughName; +extern const char *g_drkiName; +extern const char *g_minidinoName; +extern const char *g_minidonnaName; +extern const char *g_minidoughName; +extern const char *g_minidrkiName; @@ -601,7 +601,7 @@ private: void _c_password(void *); }; -extern Parallaction *_vm; +extern Parallaction *g_vm; } // End of namespace Parallaction diff --git a/engines/parallaction/parallaction_br.cpp b/engines/parallaction/parallaction_br.cpp index 658a8e8795..07755fac5f 100644 --- a/engines/parallaction/parallaction_br.cpp +++ b/engines/parallaction/parallaction_br.cpp @@ -79,7 +79,7 @@ Common::Error Parallaction_br::init() { _cmdExec = new CommandExec_br(this); _programExec = new ProgramExec_br(this); - _walker = new PathWalker_BR; + _walker = new PathWalker_BR(this); _part = -1; _nextPart = -1; @@ -161,10 +161,10 @@ Common::Error Parallaction_br::go() { // initCharacter(); - while (((_engineFlags & kEngineReturn) == 0) && (!shouldQuit())) { + while (((g_engineFlags & kEngineReturn) == 0) && (!shouldQuit())) { runGame(); } - _engineFlags &= ~kEngineReturn; + g_engineFlags &= ~kEngineReturn; cleanupGame(); } @@ -259,7 +259,7 @@ void Parallaction_br::cleanupGame() { _countersNames = 0; _numLocations = 0; - _globalFlags = 0; + g_globalFlags = 0; memset(_localFlags, 0, sizeof(_localFlags)); memset(_locationNames, 0, sizeof(_locationNames)); memset(_zoneFlags, 0, sizeof(_zoneFlags)); @@ -275,7 +275,7 @@ void Parallaction_br::changeLocation() { cleanupGame(); // more cleanup needed for part changes (see also saveload) - _globalFlags = 0; + g_globalFlags = 0; cleanInventory(true); strcpy(_characterName1, "null"); @@ -358,7 +358,7 @@ void Parallaction_br::changeLocation() { // TODO: implement the music commands which control music execution _soundMan->execute(SC_PLAYMUSIC); - _engineFlags &= ~kEngineChangeLocation; + g_engineFlags &= ~kEngineChangeLocation; _newLocationName.clear(); _nextPart = -1; } @@ -548,7 +548,7 @@ void Parallaction_br::scheduleWalk(int16 x, int16 y, bool fromUser) { } } - _engineFlags |= kEngineWalking; + g_engineFlags |= kEngineWalking; } void Parallaction_br::setFollower(const Common::String &name) { diff --git a/engines/parallaction/parallaction_ns.cpp b/engines/parallaction/parallaction_ns.cpp index 0b92db1f0a..d33be0aa47 100644 --- a/engines/parallaction/parallaction_ns.cpp +++ b/engines/parallaction/parallaction_ns.cpp @@ -182,7 +182,7 @@ Common::Error Parallaction_ns::init() { _cmdExec = new CommandExec_ns(this); _programExec = new ProgramExec_ns(this); - _walker = new PathWalker_NS; + _walker = new PathWalker_NS(this); _sarcophagusDeltaX = 0; _movingSarcophagus = false; @@ -310,6 +310,7 @@ void Parallaction_ns::changeBackground(const char* background, const char* mask, _system->delayMillis(20); _gfx->setPalette(pal); _gfx->updateScreen(); + return; } if (path == 0) { @@ -382,8 +383,8 @@ void Parallaction_ns::changeLocation() { changeCharacter(locname.character()); } - strcpy(_saveData1, locname.location()); - parseLocation(_saveData1); + strcpy(g_saveData1, locname.location()); + parseLocation(g_saveData1); if (_location._startPosition.x != -1000) { _char._ani->setX(_location._startPosition.x); @@ -399,7 +400,7 @@ void Parallaction_ns::changeLocation() { // BUG #1837503: kEngineChangeLocation flag must be cleared *before* commands // and acommands are executed, so that it can be set again if needed. - _engineFlags &= ~kEngineChangeLocation; + g_engineFlags &= ~kEngineChangeLocation; _cmdExec->run(_location._commands); @@ -412,6 +413,11 @@ void Parallaction_ns::changeLocation() { if (!_intro) { _input->setMouseState(oldMouseState); + // WORKAROUND: Fix a script bug in the Multilingual DOS version of + // Nippon Safes: the mouse cursor is incorrectly hidden outside the + // cave at the end of the game. Fix it here. + if (!strcmp(_location._name, "ingressocav")) + _input->setMouseState(MOUSE_ENABLED_SHOW); } debugC(1, kDebugExec, "changeLocation() done"); @@ -526,10 +532,10 @@ void Parallaction_ns::cleanupGame() { _soundManI->stopMusic(); _inTestResult = false; - _engineFlags &= ~kEngineTransformedDonna; + g_engineFlags &= ~kEngineTransformedDonna; _numLocations = 0; - _globalFlags = 0; + g_globalFlags = 0; memset(_localFlags, 0, sizeof(_localFlags)); memset(_locationNames, 0, sizeof(_locationNames)); @@ -553,7 +559,7 @@ void Parallaction_ns::scheduleWalk(int16 x, int16 y, bool fromUser) { } _walker->buildPath(a, x, y); - _engineFlags |= kEngineWalking; + g_engineFlags |= kEngineWalking; } }// namespace Parallaction diff --git a/engines/parallaction/parser_br.cpp b/engines/parallaction/parser_br.cpp index 0904dbf655..e60349ffa8 100644 --- a/engines/parallaction/parser_br.cpp +++ b/engines/parallaction/parser_br.cpp @@ -325,6 +325,7 @@ DECLARE_LOCATION_PARSER(location) { nextToken = 2; } + debugC(7, kDebugParser, "flip: %d", flip); // TODO: handle background horizontal flip (via a context parameter) if (_tokens[nextToken][0] != '\0') { @@ -767,10 +768,10 @@ void LocationParser_br::parseGetData(ZonePtr z) { data->_gfxobj = obj; } else if (!scumm_stricmp(_tokens[0], "mask")) { - _out->_info->loadGfxObjMask(_tokens[1], data->_gfxobj); + _out->_info->loadGfxObjMask(_vm, _tokens[1], data->_gfxobj); } else if (!scumm_stricmp(_tokens[0], "path")) { - _out->_info->loadGfxObjPath(_tokens[1], data->_gfxobj); + _out->_info->loadGfxObjPath(_vm, _tokens[1], data->_gfxobj); } else if (!scumm_stricmp(_tokens[0], "icon")) { data->_getIcon = 4 + _vm->_objectsNames->lookup(_tokens[1]); diff --git a/engines/parallaction/parser_ns.cpp b/engines/parallaction/parser_ns.cpp index f1d1db53e9..41ff74f0b4 100644 --- a/engines/parallaction/parser_ns.cpp +++ b/engines/parallaction/parser_ns.cpp @@ -246,7 +246,7 @@ DECLARE_ANIM_PARSER(file) { char vC8[200]; strcpy(vC8, _tokens[1]); - if (_engineFlags & kEngineTransformedDonna) { + if (g_engineFlags & kEngineTransformedDonna) { if (!scumm_stricmp(_tokens[1], "donnap") || !scumm_stricmp(_tokens[1], "donnapa")) { strcat(vC8, "tras"); } diff --git a/engines/parallaction/saveload.cpp b/engines/parallaction/saveload.cpp index 8de2d89b18..2ecc5377a4 100644 --- a/engines/parallaction/saveload.cpp +++ b/engines/parallaction/saveload.cpp @@ -88,7 +88,7 @@ void SaveLoad_ns::doLoadGame(uint16 slot) { _vm->_score = atoi(s.c_str()); s = f->readLine(); - _globalFlags = atoi(s.c_str()); + g_globalFlags = atoi(s.c_str()); s = f->readLine(); _vm->_numLocations = atoi(s.c_str()); @@ -151,7 +151,7 @@ void SaveLoad_ns::doSaveGame(uint16 slot, const char* name) { sprintf(s, "%s\n", _vm->_char.getFullName()); f->writeString(s); - sprintf(s, "%s\n", _saveData1); + sprintf(s, "%s\n", g_saveData1); f->writeString(s); sprintf(s, "%d\n", _vm->_char._ani->getX()); f->writeString(s); @@ -159,7 +159,7 @@ void SaveLoad_ns::doSaveGame(uint16 slot, const char* name) { f->writeString(s); sprintf(s, "%d\n", _vm->_score); f->writeString(s); - sprintf(s, "%u\n", _globalFlags); + sprintf(s, "%u\n", g_globalFlags); f->writeString(s); sprintf(s, "%d\n", _vm->_numLocations); diff --git a/engines/parallaction/sound.h b/engines/parallaction/sound.h index e875e69334..e12e50e278 100644 --- a/engines/parallaction/sound.h +++ b/engines/parallaction/sound.h @@ -33,6 +33,7 @@ #define PATH_LEN 200 class MidiParser; +class MidiDriver; namespace Parallaction { @@ -41,6 +42,7 @@ class MidiPlayer; class Parallaction_br; class MidiPlayer_MSC; +MidiDriver *createAdLibDriver(); class SoundManImpl { public: diff --git a/engines/parallaction/sound_br.cpp b/engines/parallaction/sound_br.cpp index 0925e55309..ad510eb1f1 100644 --- a/engines/parallaction/sound_br.cpp +++ b/engines/parallaction/sound_br.cpp @@ -91,20 +91,20 @@ public: }; void MidiParser_MSC::parseMetaEvent(EventInfo &info) { - uint8 type = read1(_position._play_pos); - uint8 len = read1(_position._play_pos); + uint8 type = read1(_position._playPos); + uint8 len = read1(_position._playPos); info.ext.type = type; info.length = len; info.ext.data = 0; if (type == 0x51) { - info.ext.data = _position._play_pos; + info.ext.data = _position._playPos; } else { warning("unknown meta event 0x%02X", type); info.ext.type = 0; } - _position._play_pos += len; + _position._playPos += len; } void MidiParser_MSC::parseMidiEvent(EventInfo &info) { @@ -116,13 +116,13 @@ void MidiParser_MSC::parseMidiEvent(EventInfo &info) { case 0xA: case 0xB: case 0xE: - info.basic.param1 = read1(_position._play_pos); - info.basic.param2 = read1(_position._play_pos); + info.basic.param1 = read1(_position._playPos); + info.basic.param2 = read1(_position._playPos); break; case 0xC: case 0xD: - info.basic.param1 = read1(_position._play_pos); + info.basic.param1 = read1(_position._playPos); info.basic.param2 = 0; break; @@ -135,9 +135,9 @@ void MidiParser_MSC::parseMidiEvent(EventInfo &info) { } void MidiParser_MSC::parseNextEvent(EventInfo &info) { - info.start = _position._play_pos; + info.start = _position._playPos; - if (_position._play_pos >= _trackEnd) { + if (_position._playPos >= _trackEnd) { // fake an end-of-track meta event info.delta = 0; info.event = 0xFF; @@ -146,8 +146,9 @@ void MidiParser_MSC::parseNextEvent(EventInfo &info) { return; } - info.delta = readVLQ(_position._play_pos); - info.event = read1(_position._play_pos); + info.length = 0; + info.delta = readVLQ(_position._playPos); + info.event = read1(_position._playPos); if (info.event == 0xFF) { parseMetaEvent(info); @@ -155,7 +156,7 @@ void MidiParser_MSC::parseNextEvent(EventInfo &info) { } if (info.event < 0x80) { - _position._play_pos--; + _position._playPos--; info.event = _lastEvent; } @@ -185,7 +186,7 @@ bool MidiParser_MSC::loadMusic(byte *data, uint32 size) { _lastEvent = 0; _trackEnd = data + size; - _num_tracks = 1; + _numTracks = 1; _tracks[0] = pos; setTempo(500000); @@ -224,7 +225,12 @@ MidiPlayer_MSC::MidiPlayer_MSC() : _paused(false) { MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM); - _driver = MidiDriver::createMidi(dev); + const MusicType musicType = MidiDriver::getMusicType(dev); + if (musicType == MT_ADLIB) { + _driver = createAdLibDriver(); + } else { + _driver = MidiDriver::createMidi(dev); + } assert(_driver); int ret = _driver->open(); diff --git a/engines/parallaction/sound_ns.cpp b/engines/parallaction/sound_ns.cpp index 3cc25b36b0..0ee3d73556 100644 --- a/engines/parallaction/sound_ns.cpp +++ b/engines/parallaction/sound_ns.cpp @@ -168,13 +168,13 @@ void DosSoundMan_ns::playCharacterMusic(const char *character) { char *name = const_cast<char *>(character); const char *newMusicFile = 0; - if (!scumm_stricmp(name, _dinoName)) { + if (!scumm_stricmp(name, g_dinoName)) { newMusicFile = "dino"; } else - if (!scumm_stricmp(name, _donnaName)) { + if (!scumm_stricmp(name, g_donnaName)) { newMusicFile = "donna"; } else - if (!scumm_stricmp(name, _doughName)) { + if (!scumm_stricmp(name, g_doughName)) { newMusicFile = "nuts"; } else { warning("unknown character '%s' in DosSoundMan_ns_ns::playCharacterMusic", character); @@ -237,7 +237,7 @@ AmigaSoundMan_ns::~AmigaSoundMan_ns() { stopSfx(2); stopSfx(3); - delete []beepSoundBuffer; + delete[] beepSoundBuffer; } Audio::AudioStream *AmigaSoundMan_ns::loadChannelData(const char *filename, Channel *ch, bool looping) { diff --git a/engines/parallaction/staticres.cpp b/engines/parallaction/staticres.cpp index 73e78cae3c..f09b1241bc 100644 --- a/engines/parallaction/staticres.cpp +++ b/engines/parallaction/staticres.cpp @@ -89,14 +89,14 @@ byte Input::_resMouseArrow_BR_Amiga[512] = { /* This palette snippet is used for animations in Big Red Adventure. */ -byte _braAmigaFramesDefaultPalette[48] = { +byte braAmigaFramesDefaultPalette[48] = { 0x00, 0x00, 0x00, 0x14, 0x14, 0x14, 0xFF, 0xE0, 0xCF, 0x7F, 0x7F, 0x7F, 0xD9, 0x9C, 0x84, 0x00, 0x9E, 0xF0, 0x91, 0xCC, 0x36, 0xFF, 0x6A, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xDC, 0x11, 0xB0, 0xEE, 0xF0, 0xFF, 0x17, 0x3D, 0x18, 0xAC, 0x3A, 0xB0, 0x00, 0x00, 0x7D, 0x00, 0x00, 0xFF, 0xA8, 0xFF, }; -byte _amigaTopazFont[2600] = { +byte amigaTopazFont[2600] = { 0x00, 0x00, 0x03, 0xf3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x79, 0x00, 0x00, 0x03, 0xe9, 0x00, 0x00, 0x02, 0x79, 0x70, 0xff, 0x4e, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, @@ -263,7 +263,7 @@ byte _amigaTopazFont[2600] = { }; -const char *_callableNamesRes_ns[] = { +const char *callableNamesRes_ns[] = { "Projector", "HBOff", "StartIntro", @@ -292,7 +292,7 @@ const char *_callableNamesRes_ns[] = { }; -const char *_callableNamesRes_br[] = { +const char *callableNamesRes_br[] = { "blufade", "resetpalette", "ferrcycle", @@ -301,15 +301,15 @@ const char *_callableNamesRes_br[] = { "password" }; -const char *_dinoName = "dino"; -const char *_donnaName = "donna"; -const char *_doughName = "dough"; -const char *_drkiName = "drki"; +const char *g_dinoName = "dino"; +const char *g_donnaName = "donna"; +const char *g_doughName = "dough"; +const char *g_drkiName = "drki"; -const char *_minidinoName = "minidino"; -const char *_minidonnaName = "minidonna"; -const char *_minidoughName = "minidough"; -const char *_minidrkiName = "minidrki"; +const char *g_minidinoName = "minidino"; +const char *g_minidonnaName = "minidonna"; +const char *g_minidoughName = "minidough"; +const char *g_minidrkiName = "minidrki"; #define CALLABLE_NS(x) &Parallaction_ns::x @@ -391,7 +391,7 @@ const Parallaction_br::Callable Parallaction_br::_amigaCallables[] = { void Parallaction_ns::initResources() { - _callableNames = new Table(ARRAYSIZE(_callableNamesRes_ns), _callableNamesRes_ns); + _callableNames = new Table(ARRAYSIZE(callableNamesRes_ns), callableNamesRes_ns); _localFlagNames = new FixedTable(NUM_LOCATIONS, 1); _localFlagNames->addData("visited"); @@ -406,7 +406,7 @@ void Parallaction_ns::initResources() { void Parallaction_br::initResources() { - _callableNames = new Table(ARRAYSIZE(_callableNamesRes_br), _callableNamesRes_br); + _callableNames = new Table(ARRAYSIZE(callableNamesRes_br), callableNamesRes_br); _localFlagNames = new FixedTable(NUM_LOCATIONS, 2); _localFlagNames->addData("visited"); diff --git a/engines/parallaction/walk.cpp b/engines/parallaction/walk.cpp index 53237db4ef..19162cd7db 100644 --- a/engines/parallaction/walk.cpp +++ b/engines/parallaction/walk.cpp @@ -55,27 +55,27 @@ WalkFrames _char24WalkFrames_NS = { }; static int getPathWidth() { - if (!_vm->_gfx->_backgroundInfo->_path) { + if (!g_vm->_gfx->_backgroundInfo->_path) { warning("getPathWidth() _path is NULL!"); return 0; } else - return _vm->_gfx->_backgroundInfo->_path->w; + return g_vm->_gfx->_backgroundInfo->_path->w; } static int getPathHeight() { - if (!_vm->_gfx->_backgroundInfo->_path) { + if (!g_vm->_gfx->_backgroundInfo->_path) { warning("getPathHeight() _path is NULL!"); return 0; } else - return _vm->_gfx->_backgroundInfo->_path->h; + return g_vm->_gfx->_backgroundInfo->_path->h; } static bool isPathClear(uint16 x, uint16 y) { - if (!_vm->_gfx->_backgroundInfo->_path) { + if (!g_vm->_gfx->_backgroundInfo->_path) { warning("isPathClear() _path is NULL!"); return false; } else - return (_vm->_gfx->_backgroundInfo->_path->getValue(x, y) ? true : false); + return (g_vm->_gfx->_backgroundInfo->_path->getValue(x, y) ? true : false); } // adjusts position towards nearest walkable point @@ -306,7 +306,7 @@ void PathWalker_NS::checkDoor(const Common::Point &foot) { } void PathWalker_NS::finalizeWalk() { - _engineFlags &= ~kEngineWalking; + g_engineFlags &= ~kEngineWalking; Common::Point foot; _a->getFoot(foot); @@ -316,7 +316,7 @@ void PathWalker_NS::finalizeWalk() { } void PathWalker_NS::walk() { - if ((_engineFlags & kEngineWalking) == 0) { + if ((g_engineFlags & kEngineWalking) == 0) { return; } @@ -382,7 +382,7 @@ void PathWalker_NS::updateDirection(const Common::Point& pos, const Common::Poin _a->setF(frames->firstWalkFrame[_direction] + (_step / frames->frameRepeat[_direction]) % frames->numWalkFrames[_direction]); } -PathWalker_NS::PathWalker_NS() : _direction(WALK_DOWN), _step(0) { +PathWalker_NS::PathWalker_NS(Parallaction *vm) : _direction(WALK_DOWN), _step(0), _vm(vm) { } bool PathWalker_BR::directPathExists(const Common::Point &from, const Common::Point &to) { @@ -481,7 +481,7 @@ void PathWalker_BR::buildPath(State &s, uint16 x, uint16 y) { } void PathWalker_BR::finalizeWalk(State &s) { - _engineFlags &= ~kEngineWalking; + g_engineFlags &= ~kEngineWalking; Common::Point foot; _character._a->getFoot(foot); @@ -508,8 +508,8 @@ void PathWalker_BR::finalizeWalk(State &s) { #if 0 // TODO: Input::walkTo must be extended to support destination frame in addition to coordinates - if (_engineFlags & FINAL_WALK_FRAME) { // this flag is set in readInput() - _engineFlags &= ~FINAL_WALK_FRAME; + if (g_engineFlags & FINAL_WALK_FRAME) { // this flag is set in readInput() + g_engineFlags &= ~FINAL_WALK_FRAME; _ch._a->_frame = _moveToF; // from readInput()... } else { _ch._a->_frame = _dirFrame; // from walk() @@ -523,7 +523,7 @@ void PathWalker_BR::finalizeWalk(State &s) { } void PathWalker_BR::walk() { - if ((_engineFlags & kEngineWalking) == 0) { + if ((g_engineFlags & kEngineWalking) == 0) { return; } @@ -714,7 +714,7 @@ void PathWalker_BR::doWalk(State &s) { } } -PathWalker_BR::PathWalker_BR() { +PathWalker_BR::PathWalker_BR(Parallaction *vm) : _vm(vm) { _character._active = false; _character._step = 0; _follower._active = false; diff --git a/engines/parallaction/walk.h b/engines/parallaction/walk.h index 6796991f9d..ae6db6eaf1 100644 --- a/engines/parallaction/walk.h +++ b/engines/parallaction/walk.h @@ -49,8 +49,10 @@ class PathWalker_NS { void checkDoor(const Common::Point &foot); void updateDirection(const Common::Point& pos, const Common::Point& to); + Parallaction *_vm; + public: - PathWalker_NS(); + PathWalker_NS(Parallaction *vm); void buildPath(AnimationPtr a, uint16 x, uint16 y); void walk(); @@ -79,8 +81,10 @@ class PathWalker_BR { void doWalk(State &s); void checkTrap(const Common::Point &p); + Parallaction *_vm; + public: - PathWalker_BR(); + PathWalker_BR(Parallaction *vm); ~PathWalker_BR() { } void setCharacterPath(AnimationPtr a, uint16 x, uint16 y); diff --git a/engines/pegasus/ai/ai_action.cpp b/engines/pegasus/ai/ai_action.cpp new file mode 100644 index 0000000000..38d639038f --- /dev/null +++ b/engines/pegasus/ai/ai_action.cpp @@ -0,0 +1,78 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/ai/ai_action.h" +#include "pegasus/ai/ai_area.h" + +namespace Pegasus { + +AICompoundAction::~AICompoundAction() { + for (AIActionList::iterator it = _compoundActions.begin(); it != _compoundActions.end(); it++) + delete *it; +} + +void AICompoundAction::performAIAction(AIRule *rule) { + for (AIActionList::iterator it = _compoundActions.begin(); it != _compoundActions.end(); it++) + (*it)->performAIAction(rule); +} + +AIPlayMessageAction::AIPlayMessageAction(const Common::String &movieName, bool keepLastFrame, const InputBits interruptionFilter) { + _movieName = movieName; + _keepLastFrame = keepLastFrame; + _interruptionFilter = interruptionFilter; +} + +void AIPlayMessageAction::performAIAction(AIRule *) { + if (g_AIArea) { + g_AIArea->checkMiddleArea(); + g_AIArea->playAIMovie(kRightAreaSignature, _movieName, _keepLastFrame, _interruptionFilter); + } +} + +AIStartTimerAction::AIStartTimerAction(AITimerCondition *timerCondition) { + _timerCondition = timerCondition; +} + +void AIStartTimerAction::performAIAction(AIRule *) { + _timerCondition->startTimer(); +} + +AIActivateRuleAction::AIActivateRuleAction(AIRule *rule) { + _rule = rule; +} + +void AIActivateRuleAction::performAIAction(AIRule *) { + _rule->activateRule(); +} + +AIDeactivateRuleAction::AIDeactivateRuleAction(AIRule *rule) { + _rule = rule; +} + +void AIDeactivateRuleAction::performAIAction(AIRule *) { + _rule->deactivateRule(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/ai/ai_action.h b/engines/pegasus/ai/ai_action.h new file mode 100644 index 0000000000..6eac976f9c --- /dev/null +++ b/engines/pegasus/ai/ai_action.h @@ -0,0 +1,136 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_AI_AIACTION_H +#define PEGASUS_AI_AIACTION_H + +#include "common/list.h" + +#include "pegasus/input.h" +#include "pegasus/types.h" + +namespace Pegasus { + +class AIRule; +class AITimerCondition; + +///////////////////////////////////////////// +// +// AIAction + +class AIAction { +friend class AIRule; +public: + AIAction() { _actionCount = 1; } + virtual ~AIAction() {} + + virtual void performAIAction(AIRule *) = 0; + + void setActionCount(const uint32 count) { _actionCount = count; } + +protected: + uint32 _actionCount; +}; + +typedef Common::List<AIAction *> AIActionList; + +///////////////////////////////////////////// +// +// AICompoundAction + +class AICompoundAction : public AIAction { +public: + AICompoundAction() {} + virtual ~AICompoundAction(); + + void addAction(AIAction *action) { _compoundActions.push_back(action); } + + virtual void performAIAction(AIRule *); + +protected: + AIActionList _compoundActions; +}; + +///////////////////////////////////////////// +// +// AIPlayMessageAction + +class AIPlayMessageAction : public AIAction { +public: + AIPlayMessageAction(const Common::String &movieName, bool keepLastFrame, const InputBits = kWarningInterruption); + + virtual void performAIAction(AIRule *); + +protected: + Common::String _movieName; + InputBits _interruptionFilter; + bool _keepLastFrame; +}; + +///////////////////////////////////////////// +// +// AIStartTimerAction + +class AIStartTimerAction : public AIAction { +public: + AIStartTimerAction(AITimerCondition *); + + virtual void performAIAction(AIRule *); + +protected: + AITimerCondition *_timerCondition; +}; + +///////////////////////////////////////////// +// +// AIActivateRuleAction + +class AIActivateRuleAction : public AIAction { +public: + AIActivateRuleAction(AIRule *); + + virtual void performAIAction(AIRule *); + +protected: + AIRule *_rule; +}; + +///////////////////////////////////////////// +// +// AIDeactivateRuleAction + +class AIDeactivateRuleAction : public AIAction { +public: + AIDeactivateRuleAction(AIRule *rule); + + virtual void performAIAction(AIRule *); + +protected: + AIRule *_rule; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/ai/ai_area.cpp b/engines/pegasus/ai/ai_area.cpp new file mode 100644 index 0000000000..5ac8af8812 --- /dev/null +++ b/engines/pegasus/ai/ai_area.cpp @@ -0,0 +1,613 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "common/memstream.h" + +#include "pegasus/cursor.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/biochips/aichip.h" +#include "pegasus/items/biochips/biochipitem.h" +#include "pegasus/items/biochips/opticalchip.h" +#include "pegasus/items/biochips/pegasuschip.h" +#include "pegasus/items/inventory/airmask.h" +#include "pegasus/items/inventory/inventoryitem.h" + +namespace Pegasus { + +AIArea *g_AIArea = 0; + +AIArea::AIArea(InputHandler *nextHandler) : InputHandler(nextHandler), _leftAreaMovie(kAILeftAreaID), + _middleAreaMovie(kAIMiddleAreaID), _rightAreaMovie(kAIRightAreaID), _AIMovie(kAIMovieID) { + g_AIArea = this; + _leftAreaOwner = kNoClientSignature; + _middleAreaOwner = kNoClientSignature; + _rightAreaOwner = kNoClientSignature; + _leftInventoryTime = 0xffffffff; + _middleInventoryTime = 0xffffffff; + _middleBiochipTime = 0xffffffff; + _rightBiochipTime = 0xffffffff; + _lockCount = 0; + startIdling(); +} + +AIArea::~AIArea() { + if (_middleAreaOwner == kBiochipSignature) { + BiochipItem *currentBiochip = ((PegasusEngine *)g_engine)->getCurrentBiochip(); + if (currentBiochip && currentBiochip->isSelected()) + currentBiochip->giveUpSharedArea(); + } else if (_middleAreaOwner == kInventorySignature) { + InventoryItem *currentItem = ((PegasusEngine *)g_engine)->getCurrentInventoryItem(); + if (currentItem && currentItem->isSelected()) + currentItem->giveUpSharedArea(); + } + + stopIdling(); + + for (AIRuleList::iterator it = _AIRules.begin(); it != _AIRules.end(); it++) + delete *it; + + g_AIArea = 0; +} + +// Save last state of AI rules... +void AIArea::saveAIState() { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + delete vm->_aiSaveStream; + + Common::MemoryWriteStreamDynamic out; + writeAIRules(&out); + + vm->_aiSaveStream = new Common::MemoryReadStream(out.getData(), out.size(), DisposeAfterUse::YES); +} + +void AIArea::restoreAIState() { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + if (vm->_aiSaveStream) + readAIRules(vm->_aiSaveStream); +} + +void AIArea::writeAIRules(Common::WriteStream *stream) { + _AIRules.writeAIRules(stream); +} + +void AIArea::readAIRules(Common::ReadStream *stream) { + _AIRules.readAIRules(stream); +} + +void AIArea::initAIArea() { + allocateSurface(Common::Rect(0, 0, 384, 96)); + + _leftAreaMovie.shareSurface(this); + _leftAreaMovie.initFromMovieFile("Images/Items/Left Area Movie"); + _leftAreaMovie.moveElementTo(kAILeftAreaLeft, kAILeftAreaTop); + _leftAreaMovie.setDisplayOrder(kAILeftAreaOrder); + _leftAreaMovie.startDisplaying(); + _leftAreaMovie.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel()); + + _middleAreaMovie.shareSurface(this); + _middleAreaMovie.initFromMovieFile("Images/Items/Middle Area Movie"); + _middleAreaMovie.moveElementTo(kAIMiddleAreaLeft, kAIMiddleAreaTop); + _middleAreaMovie.moveMovieBoxTo(kAIMiddleAreaLeft - kAILeftAreaLeft, 0); + _middleAreaMovie.setDisplayOrder(kAIMiddleAreaOrder); + _middleAreaMovie.startDisplaying(); + _middleAreaMovie.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel()); + + _rightAreaMovie.shareSurface(this); + _rightAreaMovie.initFromMovieFile("Images/Items/Right Area Movie"); + _rightAreaMovie.moveElementTo(kAIRightAreaLeft, kAIRightAreaTop); + _rightAreaMovie.moveMovieBoxTo(kAIRightAreaLeft - kAILeftAreaLeft, 0); + _rightAreaMovie.setDisplayOrder(kAIRightAreaOrder); + _rightAreaMovie.startDisplaying(); + _rightAreaMovie.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel()); + + _AIMovie.setDisplayOrder(kAIMovieOrder); +} + +void AIArea::setAIVolume(const uint16 volume) { + _leftAreaMovie.setVolume(volume); + _middleAreaMovie.setVolume(volume); + _rightAreaMovie.setVolume(volume); +} + +// There are only so many legal combinations of client/area. +// Here is the list of supported pairs: +// kInventorySignature kLeftAreaSignature +// kInventorySignature kMiddleAreaSignature +// kBiochipSignature kMiddleAreaSignature +// kBiochipSignature kRightAreaSignature +// kAISignature kLeftAreaSignature +// Further, the kAISignature never sets a static frame time in the left area, +// but only plays a sequence. + +// If this function is called while a sequence is playing, it will just "remember" +// the time value, so that when the sequence finishes, the new time is asserted. + +void AIArea::setAIAreaToTime(const LowerClientSignature client, const LowerAreaSignature area, const TimeValue time) { + switch (area) { + case kLeftAreaSignature: + // Only support kInventorySignature client, since AI never calls SetAIAreaToTime. + _leftAreaMovie.setSegment(0, _leftAreaMovie.getDuration()); + + if (time == 0xffffffff) { + _leftAreaMovie.hide(); + _leftAreaOwner = kNoClientSignature; + } else { + setLeftMovieTime(time); + } + break; + case kMiddleAreaSignature: + // Only support kInventorySignature and kBiochipSignature clients. + _middleAreaMovie.stop(); + _middleAreaMovie.setFlags(0); + _middleAreaMovie.setSegment(0, _middleAreaMovie.getDuration()); + + if (time == 0xffffffff) { + if (client == kInventorySignature) { + if (_middleBiochipTime != 0xffffffff) { + setMiddleMovieTime(kBiochipSignature, _middleBiochipTime); + } else { + _middleAreaMovie.hide(); + _middleAreaOwner = kNoClientSignature; + } + } else { // client == kBiochipSignature + if (_middleInventoryTime != 0xffffffff) { + setMiddleMovieTime(kInventorySignature, _middleInventoryTime); + } else { + _middleAreaMovie.hide(); + _middleAreaOwner = kNoClientSignature; + } + } + } else { + setMiddleMovieTime(client, time); + } + break; + case kRightAreaSignature: + // Only support kBiochipSignature client. + _rightAreaMovie.setSegment(0, _rightAreaMovie.getDuration()); + + if (time == 0xffffffff) { + _rightAreaMovie.hide(); + _rightAreaOwner = kNoClientSignature; + } else { + setRightMovieTime(time); + } + break; + } +} + +// Plays a sequence on an area. When the sequence ends, the previous image +// is restored. +// Also, is input disabled or not? +// Easy answer: yes. + +// There are only so many legal combinations of client/area. +// Here is the list of supported pairs: +// kBiochipSignature kMiddleAreaSignature +// kBiochipSignature kRightAreaSignature +// kInventorySignature kMiddleAreaSignature + +void AIArea::playAIAreaSequence(const LowerClientSignature, const LowerAreaSignature area, const TimeValue start, const TimeValue stop) { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + lockAIOut(); + + switch (area) { + case kLeftAreaSignature: + break; + case kMiddleAreaSignature: + if (_middleAreaOwner == kInventorySignature) + _middleInventoryTime = _middleAreaMovie.getTime(); + else if (_middleAreaOwner == kBiochipSignature) + _middleBiochipTime = _middleAreaMovie.getTime(); + + _middleAreaMovie.stop(); + _middleAreaMovie.setFlags(0); + _middleAreaMovie.setSegment(start, stop); + _middleAreaMovie.setTime(start); + _middleAreaMovie.show(); + _middleAreaMovie.start(); + vm->_cursor->hide(); + + while (_middleAreaMovie.isRunning()) { + vm->checkCallBacks(); + vm->refreshDisplay(); + g_system->delayMillis(10); + } + + _middleAreaMovie.stop(); + vm->_cursor->hideUntilMoved(); + + if (_middleAreaOwner == kInventorySignature) + setAIAreaToTime(_middleAreaOwner, kMiddleAreaSignature, _middleInventoryTime); + else if (_middleAreaOwner == kBiochipSignature) + setAIAreaToTime(_middleAreaOwner, kMiddleAreaSignature, _middleBiochipTime); + else + setAIAreaToTime(_middleAreaOwner, kMiddleAreaSignature, 0xffffffff); + break; + case kRightAreaSignature: + _rightBiochipTime = _rightAreaMovie.getTime(); + _rightAreaMovie.setSegment(start, stop); + _rightAreaMovie.setTime(start); + _rightAreaMovie.show(); + _rightAreaMovie.start(); + vm->_cursor->hide(); + + while (_rightAreaMovie.isRunning()) { + vm->checkCallBacks(); + vm->refreshDisplay(); + g_system->delayMillis(10); + } + + _rightAreaMovie.stop(); + vm->_cursor->hideUntilMoved(); + setAIAreaToTime(_rightAreaOwner, kRightAreaSignature, _rightBiochipTime); + break; + } + + unlockAI(); +} + +bool AIArea::playAIMovie(const LowerAreaSignature area, const Common::String &movieName, bool keepLastFrame, const InputBits interruptFilter) { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + lockAIOut(); + + InputDevice.waitInput(interruptFilter); + if (_AIMovie.isMovieValid()) + _AIMovie.releaseMovie(); + + _AIMovie.shareSurface(this); + _AIMovie.initFromMovieFile(movieName); + + if (area == kLeftAreaSignature) { + _AIMovie.moveElementTo(kAILeftAreaLeft, kAILeftAreaTop); + _leftAreaMovie.hide(); + } else { + _AIMovie.moveElementTo(kAIRightAreaLeft, kAIRightAreaTop); + _AIMovie.moveMovieBoxTo(kAIRightAreaLeft - kAILeftAreaLeft, 0); + _rightAreaMovie.hide(); + } + + _AIMovie.setTime(0); + _AIMovie.startDisplaying(); + _AIMovie.show(); + _AIMovie.redrawMovieWorld(); + _AIMovie.setVolume(vm->getSoundFXLevel()); + _AIMovie.start(); + vm->_cursor->hide(); + + bool result = true; + bool saveAllowed = vm->swapSaveAllowed(false); + bool openAllowed = vm->swapLoadAllowed(false); + + while (_AIMovie.isRunning()) { + Input input; + InputDevice.getInput(input, interruptFilter); + + if (input.anyInput() || vm->shouldQuit() || vm->saveRequested() || vm->loadRequested()) { + result = false; + break; + } + + vm->checkCallBacks(); + vm->refreshDisplay(); + g_system->delayMillis(10); + } + + _AIMovie.stop(); + + vm->swapSaveAllowed(saveAllowed); + vm->swapLoadAllowed(openAllowed); + + // This used to keep the last frame up even if the movie was interrupted. + // However, this only occurs in the recalibration, where interruption means skip the + // whole thing, so skipping causes the AI to go away even when keepLastFrame is true. + + if (!(result && keepLastFrame)) { + _AIMovie.stopDisplaying(); + _AIMovie.releaseMovie(); + + if (area == kLeftAreaSignature) { + _leftAreaMovie.setTime(_leftInventoryTime); + _leftAreaMovie.show(); + _leftAreaMovie.redrawMovieWorld(); + } else { + _rightAreaMovie.setTime(_rightBiochipTime); + _rightAreaMovie.show(); + _rightAreaMovie.redrawMovieWorld(); + } + } + + vm->_cursor->hideUntilMoved(); + unlockAI(); + return result; +} + +// Only implemented for kMiddleAreaSignature, kInventorySignature +void AIArea::loopAIAreaSequence(const LowerClientSignature owner, const LowerAreaSignature area, const TimeValue start, const TimeValue stop) { + if (area == kMiddleAreaSignature && owner == kInventorySignature && owner == _middleAreaOwner) { + _middleAreaMovie.stop(); + _middleAreaMovie.setFlags(0); + _middleAreaMovie.setSegment(start, stop); + _middleAreaMovie.setFlags(kLoopTimeBase); + _middleAreaMovie.setTime(start); + _middleAreaMovie.show(); + _middleAreaMovie.start(); + } +} + +// Only called by kInventorySignature. +void AIArea::setLeftMovieTime(const TimeValue time) { + if (!_AIMovie.isSurfaceValid()) { + _leftAreaMovie.setTime(time); + _leftAreaMovie.show(); + _leftAreaMovie.redrawMovieWorld(); + } + + _leftAreaOwner = kInventorySignature; + _leftInventoryTime = time; +} + +void AIArea::setMiddleMovieTime(const LowerClientSignature client, const TimeValue time) { + if (client == kInventorySignature) { + _middleInventoryTime = time; + if (_middleAreaOwner == kBiochipSignature) { + BiochipItem *currentBiochip = ((PegasusEngine *)g_engine)->getCurrentBiochip(); + if (currentBiochip && currentBiochip->isSelected()) + currentBiochip->giveUpSharedArea(); + } + } else { + _middleBiochipTime = time; + if (_middleAreaOwner == kInventorySignature) { + InventoryItem *currentItem = ((PegasusEngine *)g_engine)->getCurrentInventoryItem(); + if (currentItem && currentItem->isSelected()) + currentItem->giveUpSharedArea(); + } + } + + _middleAreaMovie.setSegment(0, _middleAreaMovie.getDuration()); + _middleAreaMovie.stop(); + _middleAreaMovie.setFlags(0); + _middleAreaMovie.setTime(time); + _middleAreaMovie.show(); + _middleAreaMovie.redrawMovieWorld(); + _middleAreaOwner = client; +} + +// Only called by kBiochipSignature. +void AIArea::setRightMovieTime(const TimeValue time) { + if (!_AIMovie.isSurfaceValid()) { + // Can't do it when the AI movie is up... + _rightAreaMovie.setTime(time); + _rightAreaMovie.show(); + _rightAreaMovie.redrawMovieWorld(); + } + + _rightAreaOwner = kBiochipSignature; + _rightBiochipTime = time; +} + +void AIArea::handleInput(const Input &input, const Hotspot *cursorSpot) { + if (JMPPPInput::isToggleAIMiddleInput(input)) + toggleMiddleAreaOwner(); + else + InputHandler::handleInput(input, cursorSpot); +} + +void AIArea::toggleMiddleAreaOwner() { + if (_middleAreaOwner == kInventorySignature) { + BiochipItem *currentBiochip = ((PegasusEngine *)g_engine)->getCurrentBiochip(); + if (currentBiochip) { + setMiddleMovieTime(kBiochipSignature, currentBiochip->getSharedAreaTime()); + currentBiochip->takeSharedArea(); + } + } else if (_middleAreaOwner == kBiochipSignature) { + InventoryItem *currentItem = ((PegasusEngine *)g_engine)->getCurrentInventoryItem(); + if (currentItem) { + setMiddleMovieTime(kInventorySignature, currentItem->getSharedAreaTime()); + currentItem->takeSharedArea(); + } + } +} + +void AIArea::activateHotspots() { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + if (_middleAreaOwner == kBiochipSignature) { + BiochipItem *currentBiochip = ((PegasusEngine *)g_engine)->getCurrentBiochip(); + if (currentBiochip) + switch (currentBiochip->getObjectID()) { + case kAIBiochip: + ((AIChip *)currentBiochip)->activateAIHotspots(); + break; + case kPegasusBiochip: + if (!vm->isDemo()) + ((PegasusChip *)currentBiochip)->activatePegasusHotspots(); + break; + case kOpticalBiochip: + ((OpticalChip *)currentBiochip)->activateOpticalHotspots(); + break; + } + } else if (_middleAreaOwner == kInventorySignature) { + InventoryItem *currentItem = ((PegasusEngine *)g_engine)->getCurrentInventoryItem(); + if (currentItem && currentItem->getObjectID() == kAirMask) + ((AirMask *)currentItem)->activateAirMaskHotspots(); + } + + InputHandler::activateHotspots(); +} + +void AIArea::clickInHotspot(const Input &input, const Hotspot *hotspot) { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + bool handled = false; + + if (_middleAreaOwner == kBiochipSignature) { + BiochipItem *currentBiochip = ((PegasusEngine *)g_engine)->getCurrentBiochip(); + + if (currentBiochip) { + switch (currentBiochip->getObjectID()) { + case kAIBiochip: + if ((hotspot->getHotspotFlags() & kAIBiochipSpotFlag) != 0) { + ((AIChip *)currentBiochip)->clickInAIHotspot(hotspot->getObjectID()); + handled = true; + } + break; + case kPegasusBiochip: + if (!vm->isDemo() && ((hotspot->getHotspotFlags() & kPegasusBiochipSpotFlag) != 0)) { + ((PegasusChip *)currentBiochip)->clickInPegasusHotspot(); + handled = true; + } + break; + case kOpticalBiochip: + if ((hotspot->getHotspotFlags() & kOpticalBiochipSpotFlag) != 0) { + ((OpticalChip *)currentBiochip)->clickInOpticalHotspot(hotspot->getObjectID()); + handled = true; + } + break; + } + } + } else if (_middleAreaOwner == kInventorySignature) { + InventoryItem *currentItem = ((PegasusEngine *)g_engine)->getCurrentInventoryItem(); + + if (currentItem) { + switch (currentItem->getObjectID()) { + case kAirMask: + if ((hotspot->getHotspotFlags() & kAirMaskSpotFlag) != 0) { + ((AirMask *)currentItem)->clickInAirMaskHotspot(); + handled = true; + } + break; + } + } + } + + if (!handled) + InputHandler::clickInHotspot(input, hotspot); +} + +void AIArea::lockAIOut() { + if (_lockCount == 0) + stopIdling(); + + _lockCount++; +} + +void AIArea::unlockAI() { + if (_lockCount > 0) { + _lockCount--; + if (_lockCount == 0) + startIdling(); + } +} + +void AIArea::forceAIUnlocked() { + if (_lockCount > 0) { + _lockCount = 1; + unlockAI(); + } +} + +void AIArea::checkRules() { + if (_lockCount == 0 && ((PegasusEngine *)g_engine)->playerAlive()) + for (AIRuleList::iterator it = _AIRules.begin(); it != _AIRules.end(); it++) + if ((*it)->fireRule()) + break; +} + +void AIArea::useIdleTime() { + checkRules(); +} + +void AIArea::addAIRule(AIRule *rule) { + _AIRules.push_back(rule); +} + +void AIArea::removeAllRules() { + for (AIRuleList::iterator it = _AIRules.begin(); it != _AIRules.end(); it++) + delete *it; + + _AIRules.clear(); +} + +void AIArea::checkMiddleArea() { + BiochipItem *currentBiochip = ((PegasusEngine *)g_engine)->getCurrentBiochip(); + + if (currentBiochip) { + if (_middleAreaOwner == kBiochipSignature) { + switch (currentBiochip->getObjectID()) { + case kAIBiochip: + ((AIChip *)currentBiochip)->setUpAIChip(); + break; + case kPegasusBiochip: + ((PegasusChip *)currentBiochip)->setUpPegasusChip(); + break; + } + } else { + switch (currentBiochip->getObjectID()) { + case kAIBiochip: + ((AIChip *)currentBiochip)->setUpAIChipRude(); + break; + case kPegasusBiochip: + ((PegasusChip *)currentBiochip)->setUpPegasusChipRude(); + break; + } + } + } +} + +TimeValue AIArea::getBigInfoTime() { + if (_middleAreaOwner == kInventorySignature) { + InventoryItem *currentItem = ((PegasusEngine *)g_engine)->getCurrentInventoryItem(); + return currentItem->getInfoLeftTime(); + } else if (_middleAreaOwner == kBiochipSignature) { + BiochipItem *currentBiochip = ((PegasusEngine *)g_engine)->getCurrentBiochip(); + return currentBiochip->getInfoLeftTime(); + } + + return 0xffffffff; +} + +void AIArea::getSmallInfoSegment(TimeValue &start, TimeValue &stop) { + if (_middleAreaOwner == kInventorySignature) { + InventoryItem *currentItem = ((PegasusEngine *)g_engine)->getCurrentInventoryItem(); + currentItem->getInfoRightTimes(start, stop); + } else if (_middleAreaOwner == kBiochipSignature) { + BiochipItem *currentBiochip = ((PegasusEngine *)g_engine)->getCurrentBiochip(); + currentBiochip->getInfoRightTimes(start, stop); + } else { + start = 0xffffffff; + stop = 0xffffffff; + } +} + +LowerClientSignature AIArea::getMiddleAreaOwner() { + return _middleAreaOwner; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/ai/ai_area.h b/engines/pegasus/ai/ai_area.h new file mode 100644 index 0000000000..806e6ef6bb --- /dev/null +++ b/engines/pegasus/ai/ai_area.h @@ -0,0 +1,172 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_AI_AIAREA_H +#define PEGASUS_AI_AIAREA_H + +#include "pegasus/input.h" +#include "pegasus/movie.h" +#include "pegasus/timers.h" +#include "pegasus/ai/ai_rule.h" + +namespace Common { + class ReadStream; + class WriteStream; +} + +/* + + The AI area is the area at the bottom of the screen. There are three areas within + the AI area: + 1) the inventory/AI help area + 2) the middle area + 3) the biochip display area + + Area 1 is used for displaying the current inventory item. When the player changes the + current item, either by selecting a new one in the inventory list or by picking + up a new item, area 1 updates to show the new item. + + If the AI decides to give a message, the AI's head temporarily takes over area 1 + for the duration of the message, then goes away, returning the area to the current + inventory item. + + Area 2 is used to display the current inventory item's state, the current biochip's + state, and any extra information from the AI. The contention for this area is + resolved as follows: + -- If the AI needs to use the area while giving a message in area 1, it takes over + area 2 for the duration of its message. +*** This is not true. + -- If the player selects a new inventory item, the inventory item's state gets + displayed immediately. + -- If the player selects a new biochip, the biochip's state info gets displayed + immediately. + -- If any auto drawing is to occur, it seizes the area as soon as the drawing is + to occur. For example, the mapping biochip does auto drawing every time the + player takes a step. The only exception to this rule is if the AI is presenting + a warning. When the AI seizes areas 1 and 2, it preempts all other uses. + Some inventory items and biochips can cause arbitrary drawing to occur in this area + at arbitrary times. The map biochip is one example which causes drawing when the + player takes a step. Another example is the poison gas canister, which flashes in + this area to indicate a dangerous compound. + + Area 3 is used to display the current biochip. When the player changes the current + biochip, either by selecting a new one from the biochip list or by picking up a + new one, area 3 updates to show the new item. In addition, some biochips can play + animation in this area. + +*/ + +namespace Pegasus { + +class AIArea : public Surface, public Idler, public InputHandler { +public: + AIArea(InputHandler *); + virtual ~AIArea(); + + void writeAIRules(Common::WriteStream *stream); + void readAIRules(Common::ReadStream *stream); + + void initAIArea(); + + void saveAIState(); + void restoreAIState(); + + void handleInput(const Input &, const Hotspot *); + void activateHotspots(); + void clickInHotspot(const Input &, const Hotspot *); + + void setAIVolume(const uint16); + + // There are only so many legal combinations of client/area. + // Here is the list of supported pairs: + // kInventorySignature kLeftAreaSignature + // kInventorySignature kMiddleAreaSignature + // kBiochipSignature kMiddleAreaSignature + // kBiochipSignature kRightAreaSignature + // kAISignature kLeftAreaSignature + // Further, the kAISignature never sets a static frame time in the left area, + // but only plays a sequence from the AI movie. + void setAIAreaToTime(const LowerClientSignature, const LowerAreaSignature, const TimeValue); + + // The "Play" functions play the requested sequence synchronously. + void playAIAreaSequence(const LowerClientSignature, const LowerAreaSignature, const TimeValue, const TimeValue); + + // For PlayAIMovie, it is assumed that the client is the AI itself. + // This is used to play AI messages as well as Optical Memory video. + // Returns true if the movie played all the way through, false if it was interrupted. + bool playAIMovie(const LowerAreaSignature, const Common::String &movieName, bool keepLastFrame, const InputBits); + + // Loop the requested sequence indefinitely. + void loopAIAreaSequence(const LowerClientSignature, const LowerAreaSignature, const TimeValue, const TimeValue); + + void addAIRule(AIRule *); + + // Remove and delete all rules. + void removeAllRules(); + + void lockAIOut(); + void unlockAI(); + void forceAIUnlocked(); + + void checkMiddleArea(); + void checkRules(); + + LowerClientSignature getMiddleAreaOwner(); + void toggleMiddleAreaOwner(); + + TimeValue getBigInfoTime(); + void getSmallInfoSegment(TimeValue &, TimeValue &); + +protected: + void useIdleTime(); + + void setLeftMovieTime(const TimeValue); + void setMiddleMovieTime(const LowerClientSignature, const TimeValue); + void setRightMovieTime(const TimeValue); + + Movie _leftAreaMovie; + Movie _middleAreaMovie; + Movie _rightAreaMovie; + Movie _AIMovie; + + LowerClientSignature _leftAreaOwner; + LowerClientSignature _middleAreaOwner; + LowerClientSignature _rightAreaOwner; + + TimeValue _leftInventoryTime; + TimeValue _middleInventoryTime; + TimeValue _middleBiochipTime; + TimeValue _rightBiochipTime; + + AIRuleList _AIRules; + + uint _lockCount; +}; + +extern AIArea *g_AIArea; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/ai/ai_condition.cpp b/engines/pegasus/ai/ai_condition.cpp new file mode 100644 index 0000000000..9fc9272566 --- /dev/null +++ b/engines/pegasus/ai/ai_condition.cpp @@ -0,0 +1,290 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "common/stream.h" + +#include "pegasus/energymonitor.h" +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_condition.h" +#include "pegasus/items/itemlist.h" +#include "pegasus/items/biochips/biochipitem.h" +#include "pegasus/items/inventory/inventoryitem.h" + +namespace Pegasus { + +AIOneChildCondition::AIOneChildCondition(AICondition *child) { + _child = child; +} + +AIOneChildCondition::~AIOneChildCondition() { + delete _child; +} + +void AIOneChildCondition::writeAICondition(Common::WriteStream *stream) { + if (_child) + _child->writeAICondition(stream); +} + +void AIOneChildCondition::readAICondition(Common::ReadStream *stream) { + if (_child) + _child->readAICondition(stream); +} + +AITwoChildrenCondition::AITwoChildrenCondition(AICondition *leftChild, AICondition *rightChild) { + _leftChild = leftChild; + _rightChild = rightChild; +} + +AITwoChildrenCondition::~AITwoChildrenCondition() { + delete _leftChild; + delete _rightChild; +} + +void AITwoChildrenCondition::writeAICondition(Common::WriteStream *stream) { + if (_leftChild) + _leftChild->writeAICondition(stream); + + if (_rightChild) + _rightChild->writeAICondition(stream); +} + +void AITwoChildrenCondition::readAICondition(Common::ReadStream *stream) { + if (_leftChild) + _leftChild->readAICondition(stream); + + if (_rightChild) + _rightChild->readAICondition(stream); +} + +AINotCondition::AINotCondition(AICondition* child) : AIOneChildCondition(child) { +} + +bool AINotCondition::fireCondition() { + return _child && !_child->fireCondition(); +} + +AIAndCondition::AIAndCondition(AICondition *leftChild, AICondition *rightChild) : AITwoChildrenCondition(leftChild, rightChild) { +} + +bool AIAndCondition::fireCondition() { + return _leftChild && _leftChild->fireCondition() && _rightChild && _rightChild->fireCondition(); +} + +AIOrCondition::AIOrCondition(AICondition *leftChild, AICondition *rightChild) : AITwoChildrenCondition(leftChild, rightChild) { +} + +bool AIOrCondition::fireCondition() { + return (_leftChild && _leftChild->fireCondition()) || (_rightChild && _rightChild->fireCondition()); +} + +AITimerCondition::AITimerCondition(const TimeValue time, const TimeScale scale, const bool shouldStartTimer) { + _timerFuse.primeFuse(time, scale); + _timerFuse.setFunctor(new Common::Functor0Mem<void, AITimerCondition>(this, &AITimerCondition::fire)); + _fired = false; + + if (shouldStartTimer) + startTimer(); +} + +void AITimerCondition::startTimer() { + _fired = false; + _timerFuse.lightFuse(); +} + +void AITimerCondition::stopTimer() { + _timerFuse.stopFuse(); +} + +void AITimerCondition::writeAICondition(Common::WriteStream *stream) { + stream->writeByte(_timerFuse.isFuseLit()); + stream->writeByte(_fired); + stream->writeUint32BE(_timerFuse.getTimeRemaining()); + stream->writeUint32BE(_timerFuse.getFuseScale()); +} + +void AITimerCondition::readAICondition(Common::ReadStream *stream) { + bool running = stream->readByte(); + _fired = stream->readByte(); + TimeValue time = stream->readUint32BE(); + TimeScale scale = stream->readUint32BE(); + + _timerFuse.stopFuse(); + _timerFuse.primeFuse(time, scale); + + if (running) + _timerFuse.lightFuse(); +} + +bool AITimerCondition::fireCondition() { + return _fired; +} + +void AITimerCondition::fire() { + _fired = true; +} + +AILocationCondition::AILocationCondition(uint32 maxLocations) { + _numLocations = 0; + _maxLocations = maxLocations; + _locations = new RoomViewID[maxLocations]; +} + +AILocationCondition::~AILocationCondition() { + delete[] _locations; +} + +void AILocationCondition::addLocation(const RoomViewID location) { + if (_numLocations < _maxLocations) + _locations[_numLocations++] = location; +} + +bool AILocationCondition::fireCondition() { + RoomViewID test = GameState.getCurrentRoomAndView(), *p; + uint32 i; + + for (i = 0, p = _locations; i < _numLocations; i++, p++) { + if (test == *p) { + *p = MakeRoomView(kNoRoomID, kNoDirection); + return true; + } + } + + return false; +} + +void AILocationCondition::writeAICondition(Common::WriteStream *stream) { + stream->writeUint32BE(_maxLocations); + stream->writeUint32BE(_numLocations); + + uint32 i; + RoomViewID *p; + for (i = 0, p = _locations; i < _numLocations; i++, p++) + stream->writeUint32BE(*p); +} + +void AILocationCondition::readAICondition(Common::ReadStream *stream) { + uint32 maxLocations = stream->readUint32BE(); + + if (_maxLocations != maxLocations) { + delete[] _locations; + _locations = new RoomViewID[maxLocations]; + _maxLocations = maxLocations; + } + + _numLocations = stream->readUint32BE(); + + uint32 i; + RoomViewID *p; + for (i = 0, p = _locations; i < _numLocations; i++, p++) + *p = stream->readUint32BE(); +} + +AIDoorOpenedCondition::AIDoorOpenedCondition(RoomViewID doorLocation) { + _doorLocation = doorLocation; +} + +bool AIDoorOpenedCondition::fireCondition() { + return GameState.getCurrentRoomAndView() == _doorLocation && GameState.isCurrentDoorOpen(); +} + +AIHasItemCondition::AIHasItemCondition(const ItemID item) { + _item = item; +} + +bool AIHasItemCondition::fireCondition() { + return _item == kNoItemID || GameState.isTakenItemID(_item); +} + +AIDoesntHaveItemCondition::AIDoesntHaveItemCondition(const ItemID item) { + _item = item; +} + +bool AIDoesntHaveItemCondition::fireCondition() { + return _item == kNoItemID || !GameState.isTakenItemID(_item); +} + +AICurrentItemCondition::AICurrentItemCondition(const ItemID item) { + _item = item; +} + +bool AICurrentItemCondition::fireCondition() { + InventoryItem *item = ((PegasusEngine *)g_engine)->getCurrentInventoryItem(); + + if (_item == kNoItemID) + return item == 0; + + return item != 0 && item->getObjectID() == _item; +} + +AICurrentBiochipCondition::AICurrentBiochipCondition(const ItemID biochip) { + _biochip = biochip; +} + +bool AICurrentBiochipCondition::fireCondition() { + BiochipItem *biochip = ((PegasusEngine *)g_engine)->getCurrentBiochip(); + + if (_biochip == kNoItemID) + return biochip == 0; + + return biochip != 0 && biochip->getObjectID() == _biochip; +} + +AIItemStateCondition::AIItemStateCondition(const ItemID item, const ItemState state) { + _item = item; + _state = state; +} + +bool AIItemStateCondition::fireCondition() { + Item *item = g_allItems.findItemByID(_item); + return item != 0 && item->getItemState() == _state; +} + +AIEnergyMonitorCondition::AIEnergyMonitorCondition(const int32 energyThreshold) { + _energyThreshold = energyThreshold; +} + +bool AIEnergyMonitorCondition::fireCondition() { + return g_energyMonitor != 0 && g_energyMonitor->getCurrentEnergy() < _energyThreshold; +} + +AILastExtraCondition::AILastExtraCondition(const ExtraID lastExtra) { + _lastExtra = lastExtra; +} + +bool AILastExtraCondition::fireCondition() { + return g_neighborhood && (ExtraID)g_neighborhood->getLastExtra() == _lastExtra; +} + +AICondition *makeLocationAndDoesntHaveItemCondition(const RoomID room, const DirectionConstant direction, const ItemID item) { + AILocationCondition *location = new AILocationCondition(1); + location->addLocation(MakeRoomView(room, direction)); + + AIDoesntHaveItemCondition *doesntHaveItem = new AIDoesntHaveItemCondition(item); + + return new AIAndCondition(location, doesntHaveItem); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/ai/ai_condition.h b/engines/pegasus/ai/ai_condition.h new file mode 100644 index 0000000000..ae85168a36 --- /dev/null +++ b/engines/pegasus/ai/ai_condition.h @@ -0,0 +1,287 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_AI_AICONDITION_H +#define PEGASUS_AI_AICONDITION_H + +#include "pegasus/timers.h" + +namespace Common { + class ReadStream; + class WriteStream; +} + +namespace Pegasus { + +///////////////////////////////////////////// +// +// AICondition + +class AICondition { +public: + AICondition() {} + virtual ~AICondition() {} + + virtual bool fireCondition() = 0; + + // Only need these for conditions that are dynamic, like timer conditions... + // other conditions, like item related conditions, which don't change during + // the run of an environment, are completely initted when the environment + // is created. + virtual void writeAICondition(Common::WriteStream *) {} + virtual void readAICondition(Common::ReadStream *) {} +}; + +///////////////////////////////////////////// +// +// AIOneChildCondition + +class AIOneChildCondition : public AICondition { +public: + AIOneChildCondition(AICondition *); + virtual ~AIOneChildCondition(); + + virtual void writeAICondition(Common::WriteStream *); + virtual void readAICondition(Common::ReadStream *); + +protected: + AICondition *_child; +}; + +///////////////////////////////////////////// +// +// AITwoChildrenCondition + +class AITwoChildrenCondition : public AICondition { +public: + AITwoChildrenCondition(AICondition *, AICondition *); + virtual ~AITwoChildrenCondition(); + + virtual void writeAICondition(Common::WriteStream *); + virtual void readAICondition(Common::ReadStream *); + +protected: + AICondition *_leftChild, *_rightChild; +}; + +///////////////////////////////////////////// +// +// AINotCondition + +class AINotCondition : public AIOneChildCondition { +public: + AINotCondition(AICondition *); + + virtual bool fireCondition(); +}; + +///////////////////////////////////////////// +// +// AIAndCondition + +class AIAndCondition : public AITwoChildrenCondition { +public: + AIAndCondition(AICondition *, AICondition *); + + virtual bool fireCondition(); +}; + +///////////////////////////////////////////// +// +// AIOrCondition + +class AIOrCondition : public AITwoChildrenCondition { +public: + AIOrCondition(AICondition *, AICondition *); + + virtual bool fireCondition(); +}; + +///////////////////////////////////////////// +// +// AITimerCondition + +class AITimerCondition : public AICondition { +public: + AITimerCondition(const TimeValue, const TimeScale, const bool); + + void startTimer(); + void stopTimer(); + + virtual bool fireCondition(); + + virtual void writeAICondition(Common::WriteStream *); + virtual void readAICondition(Common::ReadStream *); + +protected: + void fire(); + + FuseFunction _timerFuse; + bool _fired; +}; + +///////////////////////////////////////////// +// +// AILocationCondition + +class AILocationCondition : public AICondition { +public: + AILocationCondition(uint32); + virtual ~AILocationCondition(); + + void addLocation(RoomViewID); + virtual bool fireCondition(); + + virtual void writeAICondition(Common::WriteStream *); + virtual void readAICondition(Common::ReadStream *); + +protected: + uint32 _numLocations, _maxLocations; + RoomViewID *_locations; +}; + +///////////////////////////////////////////// +// +// AIDoorOpenedCondition + +class AIDoorOpenedCondition : public AICondition { +public: + AIDoorOpenedCondition(RoomViewID); + virtual ~AIDoorOpenedCondition() {} + + virtual bool fireCondition(); + +protected: + RoomViewID _doorLocation; +}; + +///////////////////////////////////////////// +// +// AIHasItemCondition + +class AIHasItemCondition : public AICondition { +public: + AIHasItemCondition(const ItemID); + + virtual bool fireCondition(); + +protected: + ItemID _item; +}; + +///////////////////////////////////////////// +// +// AIDoesntHaveItemCondition + +class AIDoesntHaveItemCondition : public AICondition { +public: + AIDoesntHaveItemCondition(const ItemID); + + virtual bool fireCondition(); + +protected: + ItemID _item; +}; + +///////////////////////////////////////////// +// +// AICurrentItemCondition + +class AICurrentItemCondition : public AICondition { +public: + AICurrentItemCondition(const ItemID); + + virtual bool fireCondition(); + +protected: + ItemID _item; +}; + +///////////////////////////////////////////// +// +// AICurrentBiochipCondition + +class AICurrentBiochipCondition : public AICondition { +public: + AICurrentBiochipCondition(const ItemID); + + virtual bool fireCondition(); + +protected: + ItemID _biochip; +}; + +///////////////////////////////////////////// +// +// AIItemStateCondition + +class AIItemStateCondition : public AICondition { +public: + AIItemStateCondition(const ItemID, const ItemState); + + virtual bool fireCondition(); + +protected: + ItemID _item; + ItemState _state; +}; + +///////////////////////////////////////////// +// +// AIEnergyMonitorCondition + +class AIEnergyMonitorCondition : public AICondition { +public: + AIEnergyMonitorCondition(const int32); + + virtual bool fireCondition(); + +protected: + int32 _energyThreshold; +}; + +///////////////////////////////////////////// +// +// AILastExtraCondition + +class AILastExtraCondition : public AICondition { +public: + AILastExtraCondition(const ExtraID); + + virtual bool fireCondition(); + +protected: + ExtraID _lastExtra; +}; + +///////////////////////////////////////////// +// +// Helper functions + +AICondition *makeLocationAndDoesntHaveItemCondition(const RoomID room, const DirectionConstant direction, const ItemID item); + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/ai/ai_rule.cpp b/engines/pegasus/ai/ai_rule.cpp new file mode 100644 index 0000000000..3aaa530a4a --- /dev/null +++ b/engines/pegasus/ai/ai_rule.cpp @@ -0,0 +1,78 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "common/stream.h" + +#include "pegasus/ai/ai_action.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/ai/ai_condition.h" +#include "pegasus/ai/ai_rule.h" + +namespace Pegasus { + +bool AIRule::fireRule() { + if (_ruleActive && _ruleCondition && _ruleAction && _ruleCondition->fireCondition()) { + if (g_AIArea) + g_AIArea->lockAIOut(); + + _ruleAction->performAIAction(this); + + if (--_ruleAction->_actionCount <= 0) + deactivateRule(); + + if (g_AIArea) + g_AIArea->unlockAI(); + + return true; + } + + return false; +} + +void AIRule::writeAIRule(Common::WriteStream *stream) { + stream->writeByte(_ruleActive); + + if (_ruleCondition) + _ruleCondition->writeAICondition(stream); +} + +void AIRule::readAIRule(Common::ReadStream *stream) { + _ruleActive = stream->readByte(); + + if (_ruleCondition) + _ruleCondition->readAICondition(stream); +} + +void AIRuleList::writeAIRules(Common::WriteStream *stream) { + for (AIRuleList::iterator it = begin(); it != end(); it++) + (*it)->writeAIRule(stream); +} + +void AIRuleList::readAIRules(Common::ReadStream *stream) { + for (AIRuleList::iterator it = begin(); it != end(); it++) + (*it)->readAIRule(stream); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/ai/ai_rule.h b/engines/pegasus/ai/ai_rule.h new file mode 100644 index 0000000000..aa2ca07332 --- /dev/null +++ b/engines/pegasus/ai/ai_rule.h @@ -0,0 +1,86 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_AI_AIRULE_H +#define PEGASUS_AI_AIRULE_H + +#include "common/list.h" + +#include "pegasus/ai/ai_action.h" +#include "pegasus/ai/ai_condition.h" + +namespace Common { + class ReadStream; + class WriteStream; +} + +namespace Pegasus { + +class AICondition; +class AIAction; + +class AIRule { +public: + AIRule(AICondition *condition, AIAction *rule) { + _ruleCondition = condition; + _ruleAction = rule; + _ruleActive = true; + } + + ~AIRule() { + if (_ruleCondition) + delete _ruleCondition; + + if (_ruleAction) + delete _ruleAction; + } + + bool fireRule(); + + void activateRule() { _ruleActive = true; } + void deactivateRule() { _ruleActive = false; } + bool isRuleActive() { return _ruleActive; } + + void writeAIRule(Common::WriteStream *); + void readAIRule(Common::ReadStream *); + +protected: + AICondition *_ruleCondition; + AIAction *_ruleAction; + bool _ruleActive; +}; + +class AIRuleList : public Common::List<AIRule *> { +public: + AIRuleList() {} + ~AIRuleList() {} + + void writeAIRules(Common::WriteStream *); + void readAIRules(Common::ReadStream *); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/compass.cpp b/engines/pegasus/compass.cpp new file mode 100644 index 0000000000..5de8b91b11 --- /dev/null +++ b/engines/pegasus/compass.cpp @@ -0,0 +1,82 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/compass.h" + +namespace Pegasus { + +Compass *g_compass = 0; + +Compass::Compass() : FaderAnimation(kCompassID) { + // Initialize it to east... + setFaderValue(90); + g_compass = this; +} + +Compass::~Compass() { + g_compass = 0; +} + +void Compass::initCompass() { + if (!isCompassValid()) { + Common::Rect r; + _compassImage.initFromPICTFile("Images/Compass/Compass"); + _compassImage.getSurfaceBounds(r); + r.right = kCompassWidth; + setBounds(r); + } +} + +void Compass::deallocateCompass() { + _compassImage.deallocateSurface(); +} + +void Compass::setFaderValue(const int32 angle) { + int16 a = angle % 360; + + if (a < 0) + a += 360; + + FaderAnimation::setFaderValue(a); +} + +void Compass::draw(const Common::Rect &r1) { + if (_compassImage.isSurfaceValid()) { + Common::Rect bounds; + getBounds(bounds); + + Common::Rect r2; + _compassImage.getSurfaceBounds(r2); + + CoordType width = r2.width(); + CoordType offsetH = width / 10 - bounds.width() / 2 + (getFaderValue() * width) / 450 - bounds.left; + CoordType offsetV = -bounds.top; + r2 = r1; + r2.translate(offsetH, offsetV); + _compassImage.drawImage(r2, r1); + } +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/compass.h b/engines/pegasus/compass.h new file mode 100644 index 0000000000..67a8e06294 --- /dev/null +++ b/engines/pegasus/compass.h @@ -0,0 +1,58 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_COMPASS_H +#define PEGASUS_COMPASS_H + +#include "pegasus/fader.h" +#include "pegasus/surface.h" + +namespace Pegasus { + +// Compass is defined with 0 as north, 90 east, 180 south, 270 west. +// Clockwise rotation increases the angle, counterclockwise rotation decreases the angle. + +class Compass : public FaderAnimation { +public: + Compass(); + virtual ~Compass(); + + void initCompass(); + void deallocateCompass(); + bool isCompassValid() const { return _compassImage.isSurfaceValid(); } + + void setFaderValue(const int32); + +protected: + void draw(const Common::Rect &); + + Frame _compassImage; +}; + +extern Compass *g_compass; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/console.cpp b/engines/pegasus/console.cpp new file mode 100644 index 0000000000..64bd0ba5f2 --- /dev/null +++ b/engines/pegasus/console.cpp @@ -0,0 +1,102 @@ +/* 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 "pegasus/console.h" +#include "pegasus/interface.h" +#include "pegasus/pegasus.h" +#include "pegasus/neighborhood/neighborhood.h" + +namespace Pegasus { + +PegasusConsole::PegasusConsole(PegasusEngine *vm) : GUI::Debugger(), _vm(vm) { + DCmd_Register("die", WRAP_METHOD(PegasusConsole, Cmd_Die)); + + // These functions are non-demo specific + if (!_vm->isDemo()) + DCmd_Register("jump", WRAP_METHOD(PegasusConsole, Cmd_Jump)); +} + +PegasusConsole::~PegasusConsole() { +} + +bool PegasusConsole::Cmd_Die(int argc, const char **argv) { + if (argc == 1) { + DebugPrintf("Usage: die <death reason>\n"); + return true; + } + + int reason = atoi(argv[1]); + + bool invalidReason = (reason == 0 || reason > kPlayerWonGame); + + if (!invalidReason && _vm->isDemo()) + invalidReason = (reason != kDeathFallOffCliff) && (reason != kDeathEatenByDinosaur) && + (reason != kDeathStranded) && (reason != kPlayerWonGame); + + + if (invalidReason) { + DebugPrintf("Invalid death reason %d\n", reason); + return true; + } + + _vm->die(atoi(argv[1])); + return false; +} + +bool PegasusConsole::Cmd_Jump(int argc, const char **argv) { + if (!g_interface) { + // TODO + DebugPrintf("Cannot jump without interface set up\n"); + return true; + } + + // TODO: Default room/direction for each neighborhood + + if (argc < 4) { + DebugPrintf("Usage: jump <neighborhood> <room> <direction>\n"); + return true; + } + + NeighborhoodID neighborhood = (NeighborhoodID)atoi(argv[1]); + RoomID room = (RoomID)atoi(argv[2]); + DirectionConstant direction = (DirectionConstant)atoi(argv[3]); + + if ((neighborhood < kCaldoriaID || neighborhood > kNoradDeltaID || neighborhood == kFinalTSAID) && + neighborhood != kNoradSubChaseID) { + DebugPrintf("Invalid neighborhood %d", neighborhood); + return true; + } + + // No real way to check room validity at this point + + if (direction > kWest) { + DebugPrintf("Invalid direction %d", direction); + return true; + } + + // Here we go! + // TODO: Can't clear menu since the engine is paused + _vm->jumpToNewEnvironment(neighborhood, room, direction); + return false; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/console.h b/engines/pegasus/console.h new file mode 100644 index 0000000000..f00ee25294 --- /dev/null +++ b/engines/pegasus/console.h @@ -0,0 +1,46 @@ +/* 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 PEGASUS_CONSOLE_H +#define PEGASUS_CONSOLE_H + +#include "gui/debugger.h" + +namespace Pegasus { + +class PegasusEngine; + +class PegasusConsole : public GUI::Debugger { +public: + PegasusConsole(PegasusEngine *vm); + virtual ~PegasusConsole(); + +private: + bool Cmd_Die(int argc, const char **argv); + bool Cmd_Jump(int argc, const char **argv); + + PegasusEngine *_vm; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/constants.h b/engines/pegasus/constants.h new file mode 100644 index 0000000000..f81d2197ac --- /dev/null +++ b/engines/pegasus/constants.h @@ -0,0 +1,729 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_CONSTANTS_H +#define PEGASUS_CONSTANTS_H + +#include "common/endian.h" +#include "common/rect.h" + +#include "pegasus/types.h" + +namespace Pegasus { + +// TODO: Organize these + +static const GameID kGameIDNothing = -1; + +static const ActorID kNoActorID = kGameIDNothing; +static const ActorID kPlayerID = 0; +static const ItemID kNoItemID = kGameIDNothing; +static const RoomID kNoRoomID = kGameIDNothing; +static const ExtraID kNoExtraID = 0xFFFFFFFF; +static const NeighborhoodID kNoNeighborhoodID = kGameIDNothing; +static const AlternateID kNoAlternateID = 0; +static const GameMenuCommand kMenuCmdNoCommand = 0; + +static const HotSpotActivationID kActivateHotSpotAlways = 0; +static const HotSpotActivationID kActivateHotSpotNever = -1; + +static const ItemState kNoItemState = -1; + +static const DirectionConstant kNoDirection = 0xFF; +static const DirectionConstant kNorth = 0; +static const DirectionConstant kSouth = 1; +static const DirectionConstant kEast = 2; +static const DirectionConstant kWest = 3; + +static const TurnDirection kNoTurn = 0xFF; +static const TurnDirection kTurnLeft = 0; +static const TurnDirection kTurnRight = 1; +static const TurnDirection kTurnUp = 2; +static const TurnDirection kTurnDown = 3; +static const TurnDirection kMaxTurns = 4; + +static const GameMode kNoMode = -1; +static const GameMode kModeNavigation = 0; +static const GameMode kLastGameShellMode = kModeNavigation; + +static const CanMoveForwardReason kCanMoveForward = 0; +static const CanMoveForwardReason kCantMoveBlocked = kCanMoveForward + 1; +static const CanMoveForwardReason kCantMoveDoorClosed = kCantMoveBlocked + 1; +static const CanMoveForwardReason kCantMoveDoorLocked = kCantMoveDoorClosed + 1; +static const CanMoveForwardReason kCantMoveLastReason = kCantMoveDoorLocked; + +static const CanTurnReason kCanTurn = 0; +static const CanTurnReason kCantTurnNoTurn = kCanTurn + 1; +static const CanTurnReason kCantTurnLastReason = kCantTurnNoTurn; + +static const CanOpenDoorReason kCanOpenDoor = 0; +static const CanOpenDoorReason kCantOpenNoDoor = kCanOpenDoor + 1; +static const CanOpenDoorReason kCantOpenLocked = kCantOpenNoDoor + 1; +static const CanOpenDoorReason kCantOpenAlreadyOpen = kCantOpenLocked + 1; +static const CanOpenDoorReason kCantOpenLastReason = kCantOpenAlreadyOpen; + +static const DisplayElementID kNoDisplayElement = -1; +static const DisplayElementID kHighestReservedElementID = -2; + +static const DisplayElementID kCursorID = kHighestReservedElementID; +static const DisplayElementID kLoadScreenID = kCursorID - 1; + +static const DisplayOrder kMinAvailableOrder = 0; +static const DisplayOrder kMaxAvailableOrder = 999998; +static const DisplayOrder kLoadScreenOrder = 900000; +static const DisplayOrder kCursorOrder = 1000000; + +static const HotSpotID kNoHotSpotID = -1; +static const HotSpotFlags kNoHotSpotFlags = 0; +static const HotSpotFlags kAllHotSpotFlags = ~kNoHotSpotFlags; + +static const NotificationFlags kNoNotificationFlags = 0; + +static const DisplayElementID kCurrentDragSpriteID = 1000; + +static const TimeScale kDefaultTimeScale = 600; + +// Ticks per second. + +static const TimeScale kOneTickPerSecond = 1; +static const TimeScale kTwoTicksPerSecond = 2; +static const TimeScale kFifteenTicksPerSecond = 15; +static const TimeScale kThirtyTicksPerSecond = 30; +static const TimeScale kSixtyTicksPerSecond = 60; +static const TimeScale kMovieTicksPerSecond = 600; + +// These times are in seconds. + +static const TimeValue kOneSecond = 1; +static const TimeValue kTwoSeconds = 2; +static const TimeValue kThreeSeconds = 3; +static const TimeValue kFourSeconds = 4; +static const TimeValue kFiveSeconds = 5; +static const TimeValue kSixSeconds = 6; +static const TimeValue kSevenSeconds = 7; +static const TimeValue kEightSeconds = 8; +static const TimeValue kNineSeconds = 9; +static const TimeValue kTenSeconds = 10; +static const TimeValue kElevenSeconds = 11; +static const TimeValue kTwelveSeconds = 12; +static const TimeValue kThirteenSeconds = 13; +static const TimeValue kFourteenSeconds = 14; +static const TimeValue kFifteenSeconds = 15; +static const TimeValue kSixteenSeconds = 16; +static const TimeValue kSeventeenSeconds = 17; +static const TimeValue kEighteenSeconds = 18; +static const TimeValue kNineteenSeconds = 19; +static const TimeValue kTwentySeconds = 20; +static const TimeValue kThirtySeconds = 30; +static const TimeValue kFortySeconds = 40; +static const TimeValue kFiftySeconds = 50; +static const TimeValue kSixtySeconds = 60; +static const TimeValue kOneMinute = 60; +static const TimeValue kTwoMinutes = kOneMinute * 2; +static const TimeValue kThreeMinutes = kOneMinute * 3; +static const TimeValue kFourMinutes = kOneMinute * 4; +static const TimeValue kFiveMinutes = kOneMinute * 5; +static const TimeValue kSixMinutes = kOneMinute * 6; +static const TimeValue kSevenMinutes = kOneMinute * 7; +static const TimeValue kEightMinutes = kOneMinute * 8; +static const TimeValue kNineMinutes = kOneMinute * 9; +static const TimeValue kTenMinutes = kOneMinute * 10; +static const TimeValue kElevenMinutes = kOneMinute * 11; +static const TimeValue kTwelveMinutes = kOneMinute * 12; +static const TimeValue kThirteenMinutes = kOneMinute * 13; +static const TimeValue kFourteenMinutes = kOneMinute * 14; +static const TimeValue kFifteenMinutes = kOneMinute * 15; +static const TimeValue kSixteenMinutes = kOneMinute * 16; +static const TimeValue kSeventeenMinutes = kOneMinute * 17; +static const TimeValue kEighteenMinutes = kOneMinute * 18; +static const TimeValue kNineteenMinutes = kOneMinute * 19; +static const TimeValue kTwentyMinutes = kOneMinute * 20; +static const TimeValue kThirtyMinutes = kOneMinute * 30; +static const TimeValue kFortyMinutes = kOneMinute * 40; +static const TimeValue kFiftyMinutes = kOneMinute * 50; +static const TimeValue kOneHour = kOneMinute * 60; +static const TimeValue kTwoHours = kOneHour * 2; + +// Common times. + +static const TimeValue kHalfSecondPerTwoTicks = kTwoTicksPerSecond / 2; +static const TimeValue kHalfSecondPerThirtyTicks = kThirtyTicksPerSecond / 2; +static const TimeValue kHalfSecondPerSixtyTicks = kSixtyTicksPerSecond / 2; + +static const TimeValue kOneSecondPerTwoTicks = kTwoTicksPerSecond; +static const TimeValue kOneSecondPerThirtyTicks = kThirtyTicksPerSecond; +static const TimeValue kOneSecondPerSixtyTicks = kSixtyTicksPerSecond; + +static const TimeValue kOneMinutePerFifteenTicks = kOneMinute * kFifteenTicksPerSecond; +static const TimeValue kFiveMinutesPerFifteenTicks = kFiveMinutes * kFifteenTicksPerSecond; +static const TimeValue kTenMinutesPerFifteenTicks = kTenMinutes * kFifteenTicksPerSecond; + +static const TimeValue kOneMinutePerThirtyTicks = kOneMinute * kThirtyTicksPerSecond; +static const TimeValue kFiveMinutesPerThirtyTicks = kFiveMinutes * kThirtyTicksPerSecond; +static const TimeValue kTenMinutesPerThirtyTicks = kTenMinutes * kThirtyTicksPerSecond; + +static const TimeValue kOneMinutePerSixtyTicks = kOneMinute * kSixtyTicksPerSecond; +static const TimeValue kFiveMinutesPerSixtyTicks = kFiveMinutes * kSixtyTicksPerSecond; +static const TimeValue kTenMinutesPerSixtyTicks = kTenMinutes * kSixtyTicksPerSecond; + +// Time in seconds you can hang around Caldoria without going to work... +static const TimeValue kLateWarning2TimeLimit = kFiveMinutes; +static const TimeValue kLateWarning3TimeLimit = kTenMinutes; + +static const TimeValue kSinclairShootsTimeLimit = kThreeMinutes; +static const TimeValue kCardBombCountDownTime = kTwelveSeconds; + +static const TimeValue kOxyMaskFullTime = kThirtyMinutes; + +static const TimeValue kTSAUncreatedTimeLimit = kFiveMinutes; +static const TimeValue kRipTimeLimit = kTenMinutesPerFifteenTicks; +static const TimeScale kRipTimeScale = kFifteenTicksPerSecond; + +static const TimeValue kIntroTimeOut = kThirtySeconds; + +static const TimeValue kMarsRobotPatienceLimit = kFifteenSeconds; +static const TimeValue kLockFreezeTimeLmit = kFifteenSeconds; +static const TimeValue kSpaceChaseTimeLimit = kTenMinutes; +static const TimeValue kVacuumSurvivalTimeLimit = kThirtySeconds; +static const TimeValue kColorMatchingTimeLimit = kFourMinutes; +static const TimeScale kJunkTimeScale = kFifteenTicksPerSecond; +static const TimeValue kJunkDropBaseTime = kFiveSeconds; +static const TimeValue kJunkDropSlopTime = kThreeSeconds; +static const TimeValue kJunkTravelTime = kTenSeconds * kJunkTimeScale; +static const TimeValue kCollisionReboundTime = kOneSecond * kJunkTimeScale; +static const TimeValue kWeaponReboundTime = kTwoSeconds * kJunkTimeScale; + +static const TimeValue kGawkAtRobotTime = kTenSeconds; +static const TimeValue kGawkAtRobotTime2 = kThirteenSeconds; +static const TimeValue kPlasmaImpactTime = kTwoSeconds; + +static const TimeValue kNoradAirMaskTimeLimit = kOneMinute + kFifteenSeconds; + +static const NotificationID kNeighborhoodNotificationID = 1; +static const NotificationID kLastNeighborhoodNotificationID = kNeighborhoodNotificationID; + +static const NotificationFlags kNeighborhoodMovieCompletedFlag = 1; +static const NotificationFlags kMoveForwardCompletedFlag = kNeighborhoodMovieCompletedFlag << 1; +static const NotificationFlags kStrideCompletedFlag = kMoveForwardCompletedFlag << 1; +static const NotificationFlags kTurnCompletedFlag = kStrideCompletedFlag << 1; +static const NotificationFlags kSpotCompletedFlag = kTurnCompletedFlag << 1; +static const NotificationFlags kDoorOpenCompletedFlag = kSpotCompletedFlag << 1; +static const NotificationFlags kExtraCompletedFlag = kDoorOpenCompletedFlag << 1; +static const NotificationFlags kSpotSoundCompletedFlag = kExtraCompletedFlag << 1; +static const NotificationFlags kDelayCompletedFlag = kSpotSoundCompletedFlag << 1; +static const NotificationFlags kActionRequestCompletedFlag = kDelayCompletedFlag << 1; +static const NotificationFlags kDeathExtraCompletedFlag = kActionRequestCompletedFlag << 1; +static const NotificationFlags kLastNeighborhoodNotificationFlag = kDeathExtraCompletedFlag; + +static const NotificationFlags kNeighborhoodFlags = kNeighborhoodMovieCompletedFlag | + kMoveForwardCompletedFlag | + kStrideCompletedFlag | + kTurnCompletedFlag | + kSpotCompletedFlag | + kDoorOpenCompletedFlag | + kExtraCompletedFlag | + kSpotSoundCompletedFlag | + kDelayCompletedFlag | + kActionRequestCompletedFlag | + kDeathExtraCompletedFlag; + +static const uint32 kPegasusPrimeCreator = MKTAG('J', 'P', 'P', 'P'); +static const uint32 kPegasusPrimeContinueType = MKTAG('P', 'P', 'C', 'T'); + +static const uint32 kPegasusPrimeDisk1GameType = MKTAG('P', 'P', 'G', '1'); +static const uint32 kPegasusPrimeDisk2GameType = MKTAG('P', 'P', 'G', '2'); +static const uint32 kPegasusPrimeDisk3GameType = MKTAG('P', 'P', 'G', '3'); +static const uint32 kPegasusPrimeDisk4GameType = MKTAG('P', 'P', 'G', '4'); + +// We only support one of the save versions; the rest are from betas +// and we are not supporting them. +static const uint32 kPegasusPrimeVersion = 0x00009019; + +static const char kNormalSave = 0; +static const char kContinueSave = 1; + +// Display IDs. + +static const DisplayElementID kNavMovieID = 1; +static const DisplayElementID kTurnPushID = 2; + +static const DisplayElementID kMaxGameShellDisplayID = kTurnPushID; + +// Display ordering. + +static const DisplayOrder kNavLayer = 10000; +static const DisplayOrder kNavMovieOrder = kNavLayer; +static const DisplayOrder kTurnPushOrder = kNavMovieOrder + 1; + +///////////////////////////////////////////// +// +// Display IDs. + +static const DisplayElementID kScreenDimmerID = kMaxGameShellDisplayID + 1; +static const DisplayElementID kInterface1ID = kScreenDimmerID + 1; +static const DisplayElementID kInterface2ID = kInterface1ID + 1; +static const DisplayElementID kInterface3ID = kInterface2ID + 1; +static const DisplayElementID kInterface4ID = kInterface3ID + 1; +static const DisplayElementID kDateID = kInterface4ID + 1; +static const DisplayElementID kCompassID = kDateID + 1; +static const DisplayElementID kInventoryPushID = kCompassID + 1; +static const DisplayElementID kInventoryLidID = kInventoryPushID + 1; +static const DisplayElementID kBiochipPushID = kInventoryLidID + 1; +static const DisplayElementID kBiochipLidID = kBiochipPushID + 1; +static const DisplayElementID kEnergyBarID = kBiochipLidID + 1; +static const DisplayElementID kWarningLightID = kEnergyBarID + 1; +static const DisplayElementID kAILeftAreaID = kWarningLightID + 1; +static const DisplayElementID kAIMiddleAreaID = kAILeftAreaID + 1; +static const DisplayElementID kAIRightAreaID = kAIMiddleAreaID + 1; +static const DisplayElementID kAIMovieID = kAIRightAreaID + 1; +static const DisplayElementID kInventoryDropHighlightID = kAIMovieID + 1; +static const DisplayElementID kBiochipDropHighlightID = kInventoryDropHighlightID + 1; + +static const DisplayElementID kDraggingSpriteID = 1000; + +static const DisplayElementID kCroppedMovieID = 2000; + +static const DisplayElementID kNeighborhoodDisplayID = 3000; + +static const DisplayElementID kItemPictureBaseID = 5000; + +static const CoordType kNavAreaLeft = 64; +static const CoordType kNavAreaTop = 64; + +static const CoordType kBackground1Left = 0; +static const CoordType kBackground1Top = 64; + +static const CoordType kBackground2Left = 0; +static const CoordType kBackground2Top = 0; + +static const CoordType kBackground3Left = 576; +static const CoordType kBackground3Top = 64; + +static const CoordType kBackground4Left = 0; +static const CoordType kBackground4Top = 320; + +static const CoordType kOverviewControllerLeft = 540; +static const CoordType kOverviewControllerTop = 348; + +static const CoordType kSwapLeft = 194; +static const CoordType kSwapTop = 116; + +static const CoordType kSwapHiliteLeft = 200; +static const CoordType kSwapHiliteTop = 206; + +static const CoordType kDateLeft = 136; +static const CoordType kDateTop = 44; + +static const CoordType kCompassLeft = 222; +static const CoordType kCompassTop = 42; +static const CoordType kCompassWidth = 92; + +static const CoordType kInventoryPushLeft = 74; +static const CoordType kInventoryPushTop = 92; + +static const CoordType kInventoryLidLeft = 74; +static const CoordType kInventoryLidTop = 316; + +static const CoordType kBiochipPushLeft = 362; +static const CoordType kBiochipPushTop = 192; + +static const CoordType kBiochipLidLeft = 362; +static const CoordType kBiochipLidTop = 316; + +static const CoordType kInventoryDropLeft = 0; +static const CoordType kInventoryDropTop = 320; +static const CoordType kInventoryDropRight = 232; +static const CoordType kInventoryDropBottom = 480; + +static const CoordType kBiochipDropLeft = 302; +static const CoordType kBiochipDropTop = 320; +static const CoordType kBiochipDropRight = 640; +static const CoordType kBiochipDropBottom = 480; + +static const CoordType kFinalMessageLeft = kInventoryPushLeft + 1; +static const CoordType kFinalMessageTop = kInventoryPushTop + 24; + +///////////////////////////////////////////// +// +// Notifications. + +static const NotificationID kJMPDCShellNotificationID = kLastNeighborhoodNotificationID + 1; +static const NotificationID kInterfaceNotificationID = kJMPDCShellNotificationID + 1; +static const NotificationID kAINotificationID = kInterfaceNotificationID + 1; +static const NotificationID kNoradNotificationID = kAINotificationID + 1; +static const NotificationID kNoradECRNotificationID = kNoradNotificationID + 1; +static const NotificationID kNoradFillingStationNotificationID = kNoradECRNotificationID + 1; +static const NotificationID kNoradPressureNotificationID = kNoradFillingStationNotificationID + 1; +static const NotificationID kNoradUtilityNotificationID = kNoradPressureNotificationID + 1; +static const NotificationID kNoradElevatorNotificationID = kNoradUtilityNotificationID + 1; +static const NotificationID kNoradSubPlatformNotificationID = kNoradElevatorNotificationID + 1; +static const NotificationID kSubControlNotificationID = kNoradSubPlatformNotificationID + 1; +static const NotificationID kNoradGreenBallNotificationID = kSubControlNotificationID + 1; +static const NotificationID kNoradGlobeNotificationID = kNoradGreenBallNotificationID + 1; +static const NotificationID kCaldoriaVidPhoneNotificationID = kNoradGlobeNotificationID + 1; +static const NotificationID kCaldoriaMessagesNotificationID = kCaldoriaVidPhoneNotificationID + 1; +static const NotificationID kCaldoriaBombTimerNotificationID = kCaldoriaMessagesNotificationID + 1; + +// Sent to the shell by fShellNotification. +static const NotificationFlags kGameStartingFlag = 1; +static const NotificationFlags kNeedNewJumpFlag = kGameStartingFlag << 1; +static const NotificationFlags kPlayerDiedFlag = kNeedNewJumpFlag << 1; + +static const NotificationFlags kJMPShellNotificationFlags = kGameStartingFlag | + kNeedNewJumpFlag | + kPlayerDiedFlag; + +// Sent to the interface. +static const NotificationFlags kInventoryLidOpenFlag = 1; +static const NotificationFlags kInventoryLidClosedFlag = kInventoryLidOpenFlag << 1; +static const NotificationFlags kInventoryDrawerUpFlag = kInventoryLidClosedFlag << 1; +static const NotificationFlags kInventoryDrawerDownFlag = kInventoryDrawerUpFlag << 1; +static const NotificationFlags kBiochipLidOpenFlag = kInventoryDrawerDownFlag << 1; +static const NotificationFlags kBiochipLidClosedFlag = kBiochipLidOpenFlag << 1; +static const NotificationFlags kBiochipDrawerUpFlag = kBiochipLidClosedFlag << 1; +static const NotificationFlags kBiochipDrawerDownFlag = kBiochipDrawerUpFlag << 1; + +static const NotificationFlags kInterfaceNotificationFlags = kInventoryLidOpenFlag | + kInventoryLidClosedFlag | + kInventoryDrawerUpFlag | + kInventoryDrawerDownFlag | + kBiochipLidOpenFlag | + kBiochipLidClosedFlag | + kBiochipDrawerUpFlag | + kBiochipDrawerDownFlag; + +// Hot spots. + +// Neighborhood hot spots. + +static const HotSpotID kFirstNeighborhoodSpotID = 5000; + +// kShellSpotFlag is a flag which marks all hot spots which belong to the shell, like +// the current item and current biochip spots. +static const HotSpotFlags kShellSpotFlag = 1; +// kNeighborhoodSpotFlag is a flag which marks all hot spots which belong to a +// neighborhood, like buttons on walls and so on. +static const HotSpotFlags kNeighborhoodSpotFlag = kShellSpotFlag << 1; +// kZoomInSpotFlag is a flag which marks all hot spots which indicate a zoom. +static const HotSpotFlags kZoomInSpotFlag = kNeighborhoodSpotFlag << 1; +// kZoomOutSpotFlag is a flag which marks all hot spots which indicate a zoom. +static const HotSpotFlags kZoomOutSpotFlag = kZoomInSpotFlag << 1; + +static const HotSpotFlags kClickSpotFlag = kZoomOutSpotFlag << 1; +static const HotSpotFlags kPlayExtraSpotFlag = kClickSpotFlag << 1; +static const HotSpotFlags kPickUpItemSpotFlag = kPlayExtraSpotFlag << 1; +static const HotSpotFlags kDropItemSpotFlag = kPickUpItemSpotFlag << 1; +static const HotSpotFlags kOpenDoorSpotFlag = kDropItemSpotFlag << 1; + +static const HotSpotFlags kZoomSpotFlags = kZoomInSpotFlag | kZoomOutSpotFlag; + +static const HotSpotFlags kHighestGameShellSpotFlag = kOpenDoorSpotFlag; + +///////////////////////////////////////////// +// +// Hot spots. + +// Shell hot spots. +// The shell reserves all hot spot IDs from 0 to 999 + +static const HotSpotID kCurrentItemSpotID = 0; +static const HotSpotID kCurrentBiochipSpotID = kCurrentItemSpotID + 1; + +static const HotSpotID kInventoryDropSpotID = kCurrentBiochipSpotID + 1; +static const HotSpotID kBiochipDropSpotID = kInventoryDropSpotID + 1; + +static const HotSpotID kInfoReturnSpotID = kBiochipDropSpotID + 1; + +static const HotSpotID kAIHint1SpotID = kInfoReturnSpotID + 1; +static const HotSpotID kAIHint2SpotID = kAIHint1SpotID + 1; +static const HotSpotID kAIHint3SpotID = kAIHint2SpotID + 1; +static const HotSpotID kAISolveSpotID = kAIHint3SpotID + 1; +static const HotSpotID kAIBriefingSpotID = kAISolveSpotID + 1; +static const HotSpotID kAIScanSpotID = kAIBriefingSpotID + 1; + +static const HotSpotID kPegasusRecallSpotID = kAIScanSpotID + 1; + +static const HotSpotID kAriesSpotID = kPegasusRecallSpotID + 1; +static const HotSpotID kMercurySpotID = kAriesSpotID + 1; +static const HotSpotID kPoseidonSpotID = kMercurySpotID + 1; + +static const HotSpotID kAirMaskToggleSpotID = kPoseidonSpotID + 1; + +static const HotSpotID kShuttleEnergySpotID = kAirMaskToggleSpotID + 1; +static const HotSpotID kShuttleGravitonSpotID = kShuttleEnergySpotID + 1; +static const HotSpotID kShuttleTractorSpotID = kShuttleGravitonSpotID + 1; +static const HotSpotID kShuttleViewSpotID = kShuttleTractorSpotID + 1; +static const HotSpotID kShuttleTransportSpotID = kShuttleViewSpotID + 1; + +// Most of these are obsolete: + +// kInventoryDropSpotFlag is a flag which marks hot spots which are valid drop spots +// for inventory items. +// static const HotSpotFlags kInventoryDropSpotFlag = kHighestGameShellSpotFlag << 1; + +// kBiochipDropSpotFlag is a flag which marks hot spots which are valid drop spots +// for biochips. +// static const HotSpotFlags kBiochipDropSpotFlag = kInventoryDropSpotFlag << 1; + +// kInventorySpotFlag is a flag which marks hot spots which indicate inventory items +// in the environment. +// static const HotSpotFlags kInventorySpotFlag = kBiochipDropSpotFlag << 1; + +// kBiochipSpotFlag is a flag which marks hot spots which indicate biochips +// in the environment. +static const HotSpotFlags kPickUpBiochipSpotFlag = kHighestGameShellSpotFlag << 1; +static const HotSpotFlags kDropBiochipSpotFlag = kPickUpBiochipSpotFlag << 1; + +static const HotSpotFlags kInfoReturnSpotFlag = kDropBiochipSpotFlag << 1; + +// Biochip and inventory hot spot flags... + +static const HotSpotFlags kAIBiochipSpotFlag = kInfoReturnSpotFlag << 1; +static const HotSpotFlags kPegasusBiochipSpotFlag = kAIBiochipSpotFlag << 1; +static const HotSpotFlags kOpticalBiochipSpotFlag = kPegasusBiochipSpotFlag << 1; +static const HotSpotFlags kAirMaskSpotFlag = kOpticalBiochipSpotFlag << 1; + +static const HotSpotFlags kJMPClickingSpotFlags = kClickSpotFlag | + kPlayExtraSpotFlag | + kOpenDoorSpotFlag | + kInfoReturnSpotFlag | + kAIBiochipSpotFlag | + kPegasusBiochipSpotFlag | + kOpticalBiochipSpotFlag | + kAirMaskSpotFlag; + +static const int32 kMainMenuID = 1; +static const int32 kPauseMenuID = 2; +static const int32 kCreditsMenuID = 3; +static const int32 kDeathMenuID = 4; + +///////////////////////////////////////////// +// +// Menu commands. + +static const GameMenuCommand kMenuCmdOverview = kMenuCmdNoCommand + 1; +static const GameMenuCommand kMenuCmdStartAdventure = kMenuCmdOverview + 1; +static const GameMenuCommand kMenuCmdStartWalkthrough = kMenuCmdStartAdventure + 1; +static const GameMenuCommand kMenuCmdRestore = kMenuCmdStartWalkthrough + 1; +static const GameMenuCommand kMenuCmdCredits = kMenuCmdRestore + 1; +static const GameMenuCommand kMenuCmdQuit = kMenuCmdCredits + 1; + +static const GameMenuCommand kMenuCmdDeathContinue = kMenuCmdQuit + 1; + +static const GameMenuCommand kMenuCmdDeathQuitDemo = kMenuCmdDeathContinue + 1; +static const GameMenuCommand kMenuCmdDeathMainMenuDemo = kMenuCmdDeathQuitDemo + 1; + +static const GameMenuCommand kMenuCmdDeathRestore = kMenuCmdDeathMainMenuDemo + 1; +static const GameMenuCommand kMenuCmdDeathMainMenu = kMenuCmdDeathRestore + 1; + +static const GameMenuCommand kMenuCmdPauseSave = kMenuCmdDeathMainMenu + 1; +static const GameMenuCommand kMenuCmdPauseContinue = kMenuCmdPauseSave + 1; +static const GameMenuCommand kMenuCmdPauseRestore = kMenuCmdPauseContinue + 1; +static const GameMenuCommand kMenuCmdPauseQuit = kMenuCmdPauseRestore + 1; + +static const GameMenuCommand kMenuCmdCreditsMainMenu = kMenuCmdPauseQuit + 1; + +static const GameMenuCommand kMenuCmdCancelRestart = kMenuCmdCreditsMainMenu + 1; +static const GameMenuCommand kMenuCmdEjectRestart = kMenuCmdCancelRestart + 1; + +static const TimeValue kMenuButtonHiliteTime = 20; +static const TimeScale kMenuButtonHiliteScale = kSixtyTicksPerSecond; + +// PICT resources: + +// Warning light PICTs: + +static const ResIDType kLightOffID = 128; +static const ResIDType kLightYellowID = 129; +static const ResIDType kLightOrangeID = 130; +static const ResIDType kLightRedID = 131; + +// Date PICTs: + +static const ResIDType kDatePrehistoricID = 138; +static const ResIDType kDate2112ID = 139; +static const ResIDType kDate2185ID = 140; +static const ResIDType kDate2310ID = 141; +static const ResIDType kDate2318ID = 142; + +///////////////////////////////////////////// +// +// Display Order + +static const DisplayOrder kCroppedMovieLayer = 11000; + +static const DisplayOrder kMonitorLayer = 12000; + +static const DisplayOrder kDragSpriteLayer = 15000; +static const DisplayOrder kDragSpriteOrder = kDragSpriteLayer; + +static const DisplayOrder kInterfaceLayer = 20000; +static const DisplayOrder kBackground1Order = kInterfaceLayer; +static const DisplayOrder kBackground2Order = kBackground1Order + 1; +static const DisplayOrder kBackground3Order = kBackground2Order + 1; +static const DisplayOrder kBackground4Order = kBackground3Order + 1; +static const DisplayOrder kDateOrder = kBackground4Order + 1; +static const DisplayOrder kCompassOrder = kDateOrder + 1; +static const DisplayOrder kEnergyBarOrder = kCompassOrder + 1; +static const DisplayOrder kEnergyLightOrder = kEnergyBarOrder + 1; + +static const DisplayOrder kAILayer = 22000; +static const DisplayOrder kAILeftAreaOrder = kAILayer; +static const DisplayOrder kAIMiddleAreaOrder = kAILeftAreaOrder + 1; +static const DisplayOrder kAIRightAreaOrder = kAIMiddleAreaOrder + 1; +static const DisplayOrder kAIMovieOrder = kAIRightAreaOrder + 1; + +static const DisplayOrder kHilitesLayer = 23000; +static const DisplayOrder kInventoryHiliteOrder = kHilitesLayer; +static const DisplayOrder kBiochipHiliteOrder = kInventoryHiliteOrder + 1; + +static const DisplayOrder kPanelsLayer = 25000; +static const DisplayOrder kInventoryPushOrder = kPanelsLayer; +static const DisplayOrder kInventoryLidOrder = kInventoryPushOrder + 1; +static const DisplayOrder kBiochipPushOrder = kInventoryLidOrder + 1; +static const DisplayOrder kBiochipLidOrder = kBiochipPushOrder + 1; +static const DisplayOrder kFinalMessageOrder = kBiochipLidOrder + 1; + +static const DisplayOrder kInfoLayer = 26000; +static const DisplayOrder kInfoBackgroundOrder = kInfoLayer; +static const DisplayOrder kInfoSpinOrder = kInfoBackgroundOrder + 1; + +static const DisplayOrder kScreenDimmerOrder = 30000; + +static const DisplayOrder kPauseScreenLayer = 31000; +static const DisplayOrder kPauseMenuOrder = kPauseScreenLayer; +static const DisplayOrder kSaveGameOrder = kPauseMenuOrder + 1; +static const DisplayOrder kContinueOrder = kSaveGameOrder + 1; +static const DisplayOrder kRestoreOrder = kContinueOrder + 1; +static const DisplayOrder kSoundFXOrder = kRestoreOrder + 1; +static const DisplayOrder kAmbienceOrder = kSoundFXOrder + 1; +static const DisplayOrder kWalkthruOrder = kAmbienceOrder + 1; +static const DisplayOrder kQuitToMainMenuOrder = kWalkthruOrder + 1; +static const DisplayOrder kPauseLargeHiliteOrder = kQuitToMainMenuOrder + 1; +static const DisplayOrder kPauseSmallHiliteOrder = kPauseLargeHiliteOrder + 1; + +///////////////////////////////////////////// +// +// Death reasons. +enum { + // Caldoria + kDeathUncreatedInCaldoria = 1, + kDeathCardBomb, + kDeathShotBySinclair, + kDeathSinclairShotDelegate, + kDeathNuclearExplosion, + + // TSA + kDeathUncreatedInTSA, + kDeathShotByTSARobots, + + // Prehistoric + kDeathFallOffCliff, + kDeathEatenByDinosaur, + kDeathStranded, + + // Norad + kDeathGassedInNorad, + kDeathArrestedInNorad, + kDeathWokeUpNorad, + kDeathSubDestroyed, // Unused + kDeathRobotThroughNoradDoor, + kDeathRobotSubControlRoom, + + // Mars + kDeathWrongShuttleLock, + kDeathArrestedInMars, + kDeathRunOverByPod, + kDeathDidntGetOutOfWay, + kDeathReactorBurn, + kDeathDidntFindMarsBomb, + kDeathDidntDisarmMarsBomb, + kDeathNoMaskInMaze, + kDeathNoAirInMaze, + kDeathGroundByMazebot, + kDeathMissedOreBucket, + kDeathDidntLeaveBucket, + kDeathRanIntoCanyonWall, // Unused + kDeathRanIntoSpaceJunk, + + // WSC + kDeathDidntStopPoison, + kDeathArrestedInWSC, + kDeathHitByPlasma, + kDeathShotOnCatwalk, + + // Winning + kPlayerWonGame +}; + +static const CoordType kAILeftAreaLeft = 76; +static const CoordType kAILeftAreaTop = 334; + +static const CoordType kAILeftAreaWidth = 96; +static const CoordType kAILeftAreaHeight = 96; + +static const CoordType kAIMiddleAreaLeft = 172; +static const CoordType kAIMiddleAreaTop = 334; + +static const CoordType kAIMiddleAreaWidth = 192; +static const CoordType kAIMiddleAreaHeight = 96; + +static const CoordType kAIRightAreaLeft = 364; +static const CoordType kAIRightAreaTop = 334; + +static const CoordType kAIRightAreaWidth = 96; +static const CoordType kAIRightAreaHeight = 96; + +enum { + kTSAPlayerNotArrived, // initial state, must be zero + kTSAPlayerForcedReview, // Player must watch TBP before rip occurs. + kTSAPlayerDetectedRip, // Player finished TBP, rip alarm just went off. + kTSAPlayerNeedsHistoricalLog, // Player is instructed to get historical log + kTSAPlayerGotHistoricalLog, + kTSAPlayerInstalledHistoricalLog, + kTSABossSawHistoricalLog, + kRobotsAtCommandCenter, + kRobotsAtFrontDoor, + kRobotsAtReadyRoom, + kPlayerLockedInPegasus, + kPlayerOnWayToPrehistoric, + kPlayerWentToPrehistoric, + kPlayerOnWayToNorad, + kPlayerOnWayToMars, + kPlayerOnWayToWSC, + kPlayerFinishedWithTSA +}; + +///////////////////////////////////////////// +// +// Mode static constants. + +static const GameMode kModeInventoryPick = kLastGameShellMode + 1; +static const GameMode kModeBiochipPick = kModeInventoryPick + 1; +static const GameMode kModeInfoScreen = kModeBiochipPick + 1; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/cursor.cpp b/engines/pegasus/cursor.cpp new file mode 100644 index 0000000000..5babdf34af --- /dev/null +++ b/engines/pegasus/cursor.cpp @@ -0,0 +1,213 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "common/events.h" +#include "common/stream.h" +#include "common/system.h" +#include "graphics/cursorman.h" +#include "graphics/surface.h" +#include "graphics/decoders/pict.h" + +#include "pegasus/cursor.h" +#include "pegasus/graphics.h" +#include "pegasus/pegasus.h" + +namespace Pegasus { + +Cursor::Cursor() { + _cursorObscured = false; + _index = -1; + startIdling(); +} + +Cursor::~Cursor() { + for (uint32 i = 0; i < _info.size(); i++) { + if (_info[i].surface) { + _info[i].surface->free(); + delete _info[i].surface; + } + delete[] _info[i].palette; + } + + stopIdling(); +} + +void Cursor::addCursorFrames(uint16 id) { + PegasusEngine *vm = (PegasusEngine *)g_engine; + Common::SeekableReadStream *cursStream = vm->_resFork->getResource(MKTAG('C', 'u', 'r', 's'), id); + if (!cursStream) + error("Could not load cursor frames set %d", id); + + uint16 frameCount = cursStream->readUint16BE(); + for (uint16 i = 0; i < frameCount; i++) { + CursorInfo info; + info.tag = cursStream->readUint16BE(); + info.hotspot.x = cursStream->readUint16BE(); + info.hotspot.y = cursStream->readUint16BE(); + info.surface = 0; + info.palette = 0; + info.colorCount = 0; + _info.push_back(info); + } + + delete cursStream; + + setCurrentFrameIndex(0); +} + +void Cursor::setCurrentFrameIndex(int32 index) { + if (_index != index) { + _index = index; + if (index != -1) { + loadCursorImage(_info[index]); + CursorMan.replaceCursorPalette(_info[index].palette, 0, _info[index].colorCount); + CursorMan.replaceCursor((byte *)_info[index].surface->pixels, _info[index].surface->w, _info[index].surface->h, _info[index].hotspot.x, _info[index].hotspot.y, 0); + ((PegasusEngine *)g_engine)->_gfx->markCursorAsDirty(); + } + } +} + +int32 Cursor::getCurrentFrameIndex() const { + return _index; +} + +void Cursor::show() { + if (!isVisible()) + CursorMan.showMouse(true); + + _cursorObscured = false; + ((PegasusEngine *)g_engine)->_gfx->markCursorAsDirty(); +} + +void Cursor::hide() { + CursorMan.showMouse(false); + setCurrentFrameIndex(0); + ((PegasusEngine *)g_engine)->_gfx->markCursorAsDirty(); +} + +void Cursor::hideUntilMoved() { + if (!_cursorObscured) { + hide(); + _cursorObscured = true; + } +} + +void Cursor::useIdleTime() { + if (g_system->getEventManager()->getMousePos() != _cursorLocation) { + _cursorLocation = g_system->getEventManager()->getMousePos(); + if (_index != -1 && _cursorObscured) + show(); + ((PegasusEngine *)g_engine)->_gfx->markCursorAsDirty(); + } +} + +void Cursor::getCursorLocation(Common::Point &pt) const { + pt = _cursorLocation; +} + +bool Cursor::isVisible() { + return CursorMan.isVisible(); +} + +void Cursor::loadCursorImage(CursorInfo &cursorInfo) { + if (cursorInfo.surface) + return; + + cursorInfo.surface = new Graphics::Surface(); + + PegasusEngine *vm = (PegasusEngine *)g_engine; + Common::SeekableReadStream *cicnStream = vm->_resFork->getResource(MKTAG('c', 'i', 'c', 'n'), cursorInfo.tag); + + if (!cicnStream) + error("Failed to find color icon %d", cursorInfo.tag); + + // PixMap section + Graphics::PICTDecoder::PixMap pixMap = Graphics::PICTDecoder::readPixMap(*cicnStream); + + // Mask section + cicnStream->readUint32BE(); // mask baseAddr + uint16 maskRowBytes = cicnStream->readUint16BE(); // mask rowBytes + cicnStream->skip(3 * 2); // mask rect + /* uint16 maskHeight = */ cicnStream->readUint16BE(); + + // Bitmap section + cicnStream->readUint32BE(); // baseAddr + uint16 rowBytes = cicnStream->readUint16BE(); + cicnStream->readUint16BE(); // top + cicnStream->readUint16BE(); // left + uint16 height = cicnStream->readUint16BE(); // bottom + cicnStream->readUint16BE(); // right + + // Data section + cicnStream->readUint32BE(); // icon handle + cicnStream->skip(maskRowBytes * height); // FIXME: maskHeight doesn't work here, though the specs say it should + cicnStream->skip(rowBytes * height); + + // Palette section + cicnStream->readUint32BE(); // always 0 + cicnStream->readUint16BE(); // always 0 + cursorInfo.colorCount = cicnStream->readUint16BE() + 1; + + cursorInfo.palette = new byte[cursorInfo.colorCount * 3]; + for (uint16 i = 0; i < cursorInfo.colorCount; i++) { + cicnStream->readUint16BE(); + cursorInfo.palette[i * 3] = cicnStream->readUint16BE() >> 8; + cursorInfo.palette[i * 3 + 1] = cicnStream->readUint16BE() >> 8; + cursorInfo.palette[i * 3 + 2] = cicnStream->readUint16BE() >> 8; + } + + // PixMap data + if (pixMap.pixelSize == 8) { + cursorInfo.surface->create(pixMap.rowBytes, pixMap.bounds.height(), Graphics::PixelFormat::createFormatCLUT8()); + cicnStream->read(cursorInfo.surface->pixels, pixMap.rowBytes * pixMap.bounds.height()); + + // While this looks sensible, it actually doesn't work for some cursors + // (ie. the 'can grab' hand) + //cursorInfo.surface->w = pixMap.bounds.width(); + } else if (pixMap.pixelSize == 1) { + cursorInfo.surface->create(pixMap.bounds.width(), pixMap.bounds.height(), Graphics::PixelFormat::createFormatCLUT8()); + + for (int y = 0; y < pixMap.bounds.height(); y++) { + byte *line = (byte *)cursorInfo.surface->getBasePtr(0, y); + + for (int x = 0; x < pixMap.bounds.width();) { + byte b = cicnStream->readByte(); + + for (int i = 0; i < 8; i++) { + *line++ = ((b & (1 << (7 - i))) != 0) ? 1 : 0; + + if (++x == pixMap.bounds.width()) + break; + } + } + } + } else { + error("Unhandled %dbpp cicn images", pixMap.pixelSize); + } + + delete cicnStream; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/cursor.h b/engines/pegasus/cursor.h new file mode 100644 index 0000000000..ada82e3967 --- /dev/null +++ b/engines/pegasus/cursor.h @@ -0,0 +1,84 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_CURSOR_H +#define PEGASUS_CURSOR_H + +#include "common/array.h" +#include "common/rect.h" + +#include "pegasus/timers.h" + +namespace Graphics { + struct Surface; +} + +namespace Pegasus { + +// The original cursor code was in the graphics code directly, +// unlike ScummVM where we have the cursor code separate. We're +// going to go with CursorManager here and therefore not inherit +// from the Sprite class. + +class Cursor : private Idler { +public: + Cursor(); + virtual ~Cursor(); + + void addCursorFrames(uint16 id); + + void setCurrentFrameIndex(int32 index); + int32 getCurrentFrameIndex() const; + + void show(); + void hide(); + void hideUntilMoved(); + bool isVisible(); + + void getCursorLocation(Common::Point &) const; + +protected: + virtual void useIdleTime(); + +private: + struct CursorInfo { + uint16 tag; + Common::Point hotspot; + Graphics::Surface *surface; + byte *palette; + uint16 colorCount; + }; + + Common::Point _cursorLocation; + Common::Array<CursorInfo> _info; + bool _cursorObscured; + int _index; + + void loadCursorImage(CursorInfo &cursorInfo); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/detection.cpp b/engines/pegasus/detection.cpp new file mode 100644 index 0000000000..908005b665 --- /dev/null +++ b/engines/pegasus/detection.cpp @@ -0,0 +1,157 @@ +/* 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 "base/plugins.h" + +#include "engines/advancedDetector.h" +#include "common/config-manager.h" +#include "common/file.h" +#include "common/savefile.h" + +#include "pegasus/pegasus.h" + +namespace Pegasus { + +struct PegasusGameDescription { + ADGameDescription desc; +}; + +bool PegasusEngine::hasFeature(EngineFeature f) const { + return + (f == kSupportsRTL) + || (f == kSupportsLoadingDuringRuntime) + || (f == kSupportsSavingDuringRuntime); +} + +bool PegasusEngine::isDemo() const { + return (_gameDescription->desc.flags & ADGF_DEMO) != 0; +} + +} // End of namespace Pegasus + +static const PlainGameDescriptor pegasusGames[] = { + {"pegasus", "The Journeyman Project: Pegasus Prime"}, + {0, 0} +}; + + +namespace Pegasus { + +static const PegasusGameDescription gameDescriptions[] = { + { + { + "pegasus", + "", + AD_ENTRY1s("JMP PP Resources", "d13a602d2498010d720a6534f097f88b", 2009943), + Common::EN_ANY, + Common::kPlatformMacintosh, + ADGF_MACRESFORK, + GUIO0() + }, + }, + + { + { + "pegasus", + "Demo", + AD_ENTRY1s("JMP PP Resources", "d13a602d2498010d720a6534f097f88b", 360129), + Common::EN_ANY, + Common::kPlatformMacintosh, + ADGF_MACRESFORK|ADGF_DEMO, + GUIO1(GUIO_NOLAUNCHLOAD) + }, + }, + + { AD_TABLE_END_MARKER } +}; + +} // End of namespace Pegasus + + +class PegasusMetaEngine : public AdvancedMetaEngine { +public: + PegasusMetaEngine() : AdvancedMetaEngine(Pegasus::gameDescriptions, sizeof(Pegasus::PegasusGameDescription), pegasusGames) { + _singleid = "pegasus"; + } + + virtual const char *getName() const { + return "The Journeyman Project: Pegasus Prime"; + } + + virtual const char *getOriginalCopyright() const { + return "The Journeyman Project: Pegasus Prime (C) Presto Studios"; + } + + virtual bool hasFeature(MetaEngineFeature f) const; + virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const; + virtual SaveStateList listSaves(const char *target) const; + virtual int getMaximumSaveSlot() const { return 999; } + virtual void removeSaveState(const char *target, int slot) const; +}; + +bool PegasusMetaEngine::hasFeature(MetaEngineFeature f) const { + return + (f == kSupportsListSaves) + || (f == kSupportsLoadingDuringStartup) + || (f == kSupportsDeleteSave); +} + +SaveStateList PegasusMetaEngine::listSaves(const char *target) const { + // The original had no pattern, so the user must rename theirs + // Note that we ignore the target because saves are compatible between + // all versions + Common::StringArray filenames = g_system->getSavefileManager()->listSavefiles("pegasus-*.sav"); + + SaveStateList saveList; + for (uint32 i = 0; i < filenames.size(); i++) { + // Isolate the description from the file name + Common::String desc = filenames[i].c_str() + 8; + for (int j = 0; j < 4; j++) + desc.deleteLastChar(); + + saveList.push_back(SaveStateDescriptor(i, desc)); + } + + return saveList; +} + +void PegasusMetaEngine::removeSaveState(const char *target, int slot) const { + // See listSaves() for info on the pattern + Common::StringArray filenames = g_system->getSavefileManager()->listSavefiles("pegasus-*.sav"); + g_system->getSavefileManager()->removeSavefile(filenames[slot].c_str()); +} + +bool PegasusMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const { + const Pegasus::PegasusGameDescription *gd = (const Pegasus::PegasusGameDescription *)desc; + + if (gd) + *engine = new Pegasus::PegasusEngine(syst, gd); + + return (gd != 0); +} + +#if PLUGIN_ENABLED_DYNAMIC(PEGASUS) + REGISTER_PLUGIN_DYNAMIC(PEGASUS, PLUGIN_TYPE_ENGINE, PegasusMetaEngine); +#else + REGISTER_PLUGIN_STATIC(PEGASUS, PLUGIN_TYPE_ENGINE, PegasusMetaEngine); +#endif + diff --git a/engines/pegasus/elements.cpp b/engines/pegasus/elements.cpp new file mode 100644 index 0000000000..c84d555444 --- /dev/null +++ b/engines/pegasus/elements.cpp @@ -0,0 +1,568 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "common/macresman.h" +#include "common/stream.h" + +#include "pegasus/elements.h" +#include "pegasus/graphics.h" +#include "pegasus/surface.h" + +namespace Pegasus { + +DisplayElement::DisplayElement(const DisplayElementID id) : IDObject(id) { + _elementIsDisplaying = false; + _elementIsVisible = false; + _elementOrder = 0; + _triggeredElement = this; + _nextElement = 0; +} + +DisplayElement::~DisplayElement() { + if (isDisplaying()) + ((PegasusEngine *)g_engine)->_gfx->removeDisplayElement(this); +} + +void DisplayElement::setDisplayOrder(const DisplayOrder order) { + if (_elementOrder != order) { + _elementOrder = order; + if (isDisplaying()) { + ((PegasusEngine *)g_engine)->_gfx->removeDisplayElement(this); + ((PegasusEngine *)g_engine)->_gfx->addDisplayElement(this); + triggerRedraw(); + } + } +} + +void DisplayElement::startDisplaying() { + if (!isDisplaying()) { + ((PegasusEngine *)g_engine)->_gfx->addDisplayElement(this); + triggerRedraw(); + } +} + +void DisplayElement::stopDisplaying() { + if (isDisplaying()) { + triggerRedraw(); + ((PegasusEngine *)g_engine)->_gfx->removeDisplayElement(this); + } +} + +void DisplayElement::setBounds(const CoordType left, const CoordType top, const CoordType right, const CoordType bottom) { + setBounds(Common::Rect(left, top, right, bottom)); +} + +void DisplayElement::getBounds(Common::Rect &r) const { + r = _bounds; +} + +void DisplayElement::sizeElement(const CoordType h, const CoordType v) { + Common::Rect newBounds = _bounds; + newBounds.right = _bounds.left + h; + newBounds.bottom = _bounds.top + v; + setBounds(newBounds); +} + +void DisplayElement::moveElementTo(const CoordType h, const CoordType v) { + Common::Rect newBounds = _bounds; + newBounds.moveTo(h, v); + setBounds(newBounds); +} + +void DisplayElement::moveElement(const CoordType dh, const CoordType dv) { + Common::Rect newBounds = _bounds; + newBounds.translate(dh, dv); + setBounds(newBounds); +} + +void DisplayElement::getLocation(CoordType &h, CoordType &v) const { + h = _bounds.left; + v = _bounds.top; +} + +void DisplayElement::centerElementAt(const CoordType h, const CoordType v) { + Common::Rect newBounds = _bounds; + newBounds.moveTo(h - (_bounds.width() / 2), v - (_bounds.height() / 2)); + setBounds(newBounds); +} + +void DisplayElement::getCenter(CoordType &h, CoordType &v) const { + h = (_bounds.left + _bounds.right) / 2; + v = (_bounds.top + _bounds.bottom) / 2; +} + +void DisplayElement::setBounds(const Common::Rect &r) { + if (r != _bounds) { + triggerRedraw(); + _bounds = r; + triggerRedraw(); + } +} + +void DisplayElement::hide() { + if (_elementIsVisible) { + triggerRedraw(); + _elementIsVisible = false; + } +} + +void DisplayElement::show() { + if (!_elementIsVisible) { + _elementIsVisible = true; + triggerRedraw(); + } +} + +// Only invalidates this element's bounding rectangle if all these conditions are true: +// -- The triggered element is this element. +// -- The element is displaying on the display list. +// -- The element is visible. +// -- The element is part of the active layer OR is one of the reserved items. +void DisplayElement::triggerRedraw() { + GraphicsManager *gfx = ((PegasusEngine *)g_engine)->_gfx; + + if (_triggeredElement == this) { + if (validToDraw(gfx->getBackOfActiveLayer(), gfx->getFrontOfActiveLayer())) + gfx->invalRect(_bounds); + } else { + _triggeredElement->triggerRedraw(); + } +} + +void DisplayElement::setTriggeredElement(DisplayElement *element) { + if (element) + _triggeredElement = element; + else + _triggeredElement = this; +} + +bool DisplayElement::validToDraw(DisplayOrder backLayer, DisplayOrder frontLayer) { + return isDisplaying() && _elementIsVisible && + (getObjectID() <= kHighestReservedElementID || + (getDisplayOrder() >= backLayer && + getDisplayOrder() <= frontLayer)); +} + +DropHighlight::DropHighlight(const DisplayElementID id) : DisplayElement(id) { + _highlightColor = 0; + _thickness = 2; + _cornerDiameter = 0; +} + +void DropHighlight::draw(const Common::Rect &) { + Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getWorkArea(); + + // Since this is only used in two different ways, I'm only + // going to implement it in those two ways. Deal with it. + + Common::Rect rect = _bounds; + rect.grow(-_thickness); + screen->frameRect(rect, _highlightColor); + rect.grow(1); + screen->frameRect(rect, _highlightColor); + + if (_cornerDiameter == 8 && _thickness == 4) { + rect.grow(1); + screen->frameRect(rect, _highlightColor); + screen->hLine(rect.left + 1, rect.top - 1, rect.right - 2, _highlightColor); + screen->hLine(rect.left + 1, rect.bottom, rect.right - 2, _highlightColor); + screen->vLine(rect.left - 1, rect.top + 1, rect.bottom - 2, _highlightColor); + screen->vLine(rect.right, rect.top + 1, rect.bottom - 2, _highlightColor); + } +} + +IdlerAnimation::IdlerAnimation(const DisplayElementID id) : Animation(id) { + _lastTime = 0xffffffff; +} + +void IdlerAnimation::startDisplaying() { + if (!isDisplaying()) { + Animation::startDisplaying(); + startIdling(); + } +} + +void IdlerAnimation::stopDisplaying() { + if (isDisplaying()) { + Animation::stopDisplaying(); + stopIdling(); + } +} + +void IdlerAnimation::useIdleTime() { + uint32 currentTime = getTime(); + + if (currentTime != _lastTime) { + _lastTime = currentTime; + timeChanged(_lastTime); + } +} + +void IdlerAnimation::timeChanged(const TimeValue) { + triggerRedraw(); +} + +FrameSequence::FrameSequence(const DisplayElementID id) : IdlerAnimation(id) { + _duration = 0; + _currentFrameNum = 0; + _resFork = new Common::MacResManager(); + _numFrames = 0; +} + +FrameSequence::~FrameSequence() { + delete _resFork; +} + +void FrameSequence::useFileName(const Common::String &fileName) { + _resFork->open(fileName); +} + +void FrameSequence::openFrameSequence() { + if (!_resFork->hasResFork()) + return; + + Common::SeekableReadStream *res = _resFork->getResource(MKTAG('P', 'F', 'r', 'm'), 0x80); + + if (!res) + return; + + uint32 scale = res->readUint32BE(); + _bounds.top = res->readUint16BE(); + _bounds.left = res->readUint16BE(); + _bounds.bottom = res->readUint16BE(); + _bounds.right = res->readUint16BE(); + _numFrames = res->readUint16BE(); + _duration = 0; + + _frameTimes.clear(); + for (uint32 i = 0; i < _numFrames; i++) { + TimeValue time = res->readUint32BE(); + _duration += time; + _frameTimes.push_back(_duration); + } + + setScale(scale); + setSegment(0, _duration); + setTime(0); + _currentFrameNum = 0; + newFrame(_currentFrameNum); + triggerRedraw(); + + delete res; +} + +void FrameSequence::closeFrameSequence() { + stop(); + _resFork->close(); + _duration = 0; + _numFrames = 0; + _frameTimes.clear(); +} + +void FrameSequence::timeChanged(const TimeValue time) { + int16 frameNum = 0; + for (int16 i = _numFrames - 1; i >= 0; i--) { + if (_frameTimes[i] < time) { + frameNum = i; + break; + } + } + + if (frameNum != _currentFrameNum) { + _currentFrameNum = frameNum; + newFrame(_currentFrameNum); + triggerRedraw(); + } +} + +void FrameSequence::setFrameNum(const int16 frameNum) { + int16 f = CLIP<int>(frameNum, 0, _numFrames); + + if (_currentFrameNum != f) { + _currentFrameNum = f; + setTime(_frameTimes[f]); + newFrame(f); + triggerRedraw(); + } +} + +bool FrameSequence::isSequenceOpen() const { + return _numFrames != 0; +} + +Sprite::Sprite(const DisplayElementID id) : DisplayElement(id) { + _numFrames = 0; + _currentFrameNum = 0xffffffff; + _currentFrame = 0; +} + +Sprite::~Sprite() { + discardFrames(); +} + +void Sprite::discardFrames() { + if (!_frameArray.empty()) { + for (uint32 i = 0; i < _numFrames; i++) { + SpriteFrame *frame = _frameArray[i].frame; + frame->_referenceCount--; + if (frame->_referenceCount == 0) + delete frame; + } + + _frameArray.clear(); + _numFrames = 0; + _currentFrame = 0; + _currentFrameNum = 0xffffffff; + setBounds(0, 0, 0, 0); + } +} + +void Sprite::addPICTResourceFrame(const ResIDType pictID, bool transparent, const CoordType left, const CoordType top) { + SpriteFrame *frame = new SpriteFrame(); + frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, pictID, transparent); + addFrame(frame, left, top); +} + +uint32 Sprite::addFrame(SpriteFrame *frame, const CoordType left, const CoordType top) { + SpriteFrameRec frameRecord; + frameRecord.frame = frame; + frameRecord.frameLeft = left; + frameRecord.frameTop = top; + _frameArray.push_back(frameRecord); + _numFrames++; + frame->_referenceCount++; + + Common::Rect frameBounds; + frame->getSurfaceBounds(frameBounds); + + // 9/3/96 + // BB Should this be + left or - left? + frameBounds.moveTo(_bounds.left + left, _bounds.top + top); + + frameBounds.extend(_bounds); + + if (_bounds != frameBounds) + setBounds(frameBounds); + + return _numFrames - 1; +} + +void Sprite::removeFrame(const uint32 frameNum) { + _frameArray[frameNum].frame->_referenceCount--; + if (_frameArray[frameNum].frame->_referenceCount == 0) + delete _frameArray[frameNum].frame; + + // Calculate the new bounds + Common::Rect frameBounds; + for (uint32 i = 0; i < _numFrames; i++) { + if (i == frameNum) + continue; + + Common::Rect r; + _frameArray[i].frame->getSurfaceBounds(r); + r.translate(_frameArray[i].frameLeft, _frameArray[i].frameTop); + frameBounds.extend(r); + } + + _frameArray.remove_at(frameNum); + + frameBounds.moveTo(_bounds.left, _bounds.top); + setBounds(frameBounds); + + if (_currentFrameNum == frameNum) + triggerRedraw(); + else if (_currentFrameNum != 0xffffffff && _currentFrameNum > frameNum) + --_currentFrameNum; +} + +void Sprite::setCurrentFrameIndex(const int32 frameNum) { + if (frameNum < 0) { + if (_currentFrameNum != 0xffffffff) { + _currentFrameNum = 0xffffffff; + _currentFrame = 0; + triggerRedraw(); + } + } else if (_numFrames > 0) { + uint32 f = frameNum % _numFrames; + if (f != _currentFrameNum) { + _currentFrameNum = f; + _currentFrame = &_frameArray[f]; + triggerRedraw(); + } + } +} + +SpriteFrame *Sprite::getFrame(const int32 index) { + if (index < 0 || (uint32)index >= _numFrames) + return 0; + + return _frameArray[index].frame; +} + +void Sprite::draw(const Common::Rect &r) { + if (_currentFrame) { + Common::Rect frameBounds; + _currentFrame->frame->getSurfaceBounds(frameBounds); + + frameBounds.translate(_bounds.left + _currentFrame->frameLeft, _bounds.top + _currentFrame->frameTop); + Common::Rect r1 = frameBounds.findIntersectingRect(r); + + Common::Rect r2 = frameBounds; + r2.translate(-_bounds.left - _currentFrame->frameLeft, -_bounds.top - _currentFrame->frameTop); + + _currentFrame->frame->drawImage(r2, r1); + } +} + +SpriteSequence::SpriteSequence(const DisplayElementID id, const DisplayElementID spriteID) : + FrameSequence(id), _sprite(spriteID), _transparent(false) { +} + +void SpriteSequence::openFrameSequence() { + if (!isSequenceOpen()) { + FrameSequence::openFrameSequence(); + + if (isSequenceOpen()) { + uint32 numFrames = getNumFrames(); + + for (uint32 i = 0; i < numFrames; ++i) { + SpriteFrame *frame = new SpriteFrame(); + frame->initFromPICTResource(_resFork, i + 0x80, _transparent); + _sprite.addFrame(frame, 0, 0); + } + + _sprite.setBounds(_bounds); + } + } +} + +void SpriteSequence::closeFrameSequence() { + if (isSequenceOpen()) { + FrameSequence::closeFrameSequence(); + _sprite.discardFrames(); + } +} + +void SpriteSequence::setBounds(const Common::Rect &bounds) { + FrameSequence::setBounds(bounds); + _sprite.setBounds(_bounds); +} + +void SpriteSequence::draw(const Common::Rect &r) { + _sprite.draw(r); +} + +void SpriteSequence::newFrame(const uint16 frame) { + _sprite.setCurrentFrameIndex(frame); +} + +#define DRAW_PIXEL() \ + if (bytesPerPixel == 2) \ + *((uint16 *)dst) = black; \ + else \ + *((uint32 *)dst) = black; \ + dst += bytesPerPixel + +#define SKIP_PIXEL() \ + dst += bytesPerPixel + +void ScreenDimmer::draw(const Common::Rect &r) { + // We're going to emulate QuickDraw's srcOr+gray mode here + // In this mode, every other y column is all black (odd-columns). + // Basically, every row does three black and then one transparent + // repeatedly. + + // The output is identical to the original + + uint32 black = g_system->getScreenFormat().RGBToColor(0, 0, 0); + Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getWorkArea(); + byte bytesPerPixel = g_system->getScreenFormat().bytesPerPixel; + + // We're currently doing it to the whole screen to simplify the code + + for (int y = 0; y < 480; y++) { + byte *dst = (byte *)screen->getBasePtr(0, y); + + for (int x = 0; x < 640; x += 4) { + if (y & 1) { + DRAW_PIXEL(); + DRAW_PIXEL(); + SKIP_PIXEL(); + DRAW_PIXEL(); + } else { + SKIP_PIXEL(); + DRAW_PIXEL(); + DRAW_PIXEL(); + DRAW_PIXEL(); + } + } + } +} + +#undef DRAW_PIXEL +#undef SKIP_PIXEL + +SoundLevel::SoundLevel(const DisplayElementID id) : DisplayElement(id) { + _soundLevel = 0; +} + +void SoundLevel::incrementLevel() { + if (_soundLevel < 12) { + _soundLevel++; + triggerRedraw(); + } +} + +void SoundLevel::decrementLevel() { + if (_soundLevel > 0) { + _soundLevel--; + triggerRedraw(); + } +} + +uint16 SoundLevel::getSoundLevel() { + return CLIP<int>(_soundLevel * 22, 0, 256); +} + +void SoundLevel::setSoundLevel(uint16 level) { + uint16 newLevel = (level + 21) / 22; + + if (newLevel != _soundLevel) { + _soundLevel = newLevel; + triggerRedraw(); + } +} + +void SoundLevel::draw(const Common::Rect &r) { + Common::Rect levelRect(_bounds.right + (8 * (_soundLevel - 12)), _bounds.top, _bounds.right, _bounds.bottom); + levelRect = r.findIntersectingRect(levelRect); + + if (!levelRect.isEmpty()) { + Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getWorkArea(); + screen->fillRect(levelRect, g_system->getScreenFormat().RGBToColor(0, 0, 0)); + } +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/elements.h b/engines/pegasus/elements.h new file mode 100644 index 0000000000..140553f675 --- /dev/null +++ b/engines/pegasus/elements.h @@ -0,0 +1,254 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_ELEMENTS_H +#define PEGASUS_ELEMENTS_H + +#include "common/array.h" +#include "common/rect.h" +#include "common/str.h" +#include "common/system.h" +#include "graphics/surface.h" + +#include "pegasus/timers.h" +#include "pegasus/util.h" + +namespace Common { + class MacResManager; +} + +namespace Pegasus { + +class DisplayElement : public IDObject { +friend class GraphicsManager; +public: + DisplayElement(const DisplayElementID); + virtual ~DisplayElement(); + + void setDisplayOrder(const DisplayOrder); + DisplayOrder getDisplayOrder() const { return _elementOrder; } + + bool validToDraw(DisplayOrder, DisplayOrder); + + virtual void draw(const Common::Rect&) {} + bool isDisplaying() { return _elementIsDisplaying; } + virtual void startDisplaying(); + virtual void stopDisplaying(); + + virtual void show(); + virtual void hide(); + bool isVisible() { return _elementIsVisible; } + + // triggerRedraw only triggers a draw if the element is displaying and visible. + void triggerRedraw(); + void setTriggeredElement(DisplayElement *); + + virtual void setBounds(const CoordType, const CoordType, const CoordType, const CoordType); + virtual void setBounds(const Common::Rect &); + virtual void getBounds(Common::Rect &) const; + virtual void sizeElement(const CoordType, const CoordType); + virtual void moveElementTo(const CoordType, const CoordType); + virtual void moveElement(const CoordType, const CoordType); + virtual void getLocation(CoordType &, CoordType &) const; + virtual void getCenter(CoordType &, CoordType &) const; + virtual void centerElementAt(const CoordType, const CoordType); + +protected: + Common::Rect _bounds; + bool _elementIsVisible; + DisplayElement *_triggeredElement; + + // Used only by PegasusEngine + bool _elementIsDisplaying; + DisplayOrder _elementOrder; + DisplayElement *_nextElement; +}; + +// I'm using the proper "highlight" instead of the evil +// QuickDraw "hilite" :P (deal with it!) +class DropHighlight : public DisplayElement { +public: + DropHighlight(const DisplayElementID); + virtual ~DropHighlight() {} + + void setHighlightColor(const uint32 &highlight) { _highlightColor = highlight; } + void getHighlightColor(uint32 &highlight) const { highlight = _highlightColor; } + + void setHighlightThickness(const uint16 thickness) { _thickness = thickness; } + uint16 getHighlightThickness() const { return _thickness; } + + void setHighlightCornerDiameter(const uint16 diameter) { _cornerDiameter = diameter; } + uint16 getHighlightCornerDiameter() const { return _cornerDiameter; } + + virtual void draw(const Common::Rect&); + +protected: + uint32 _highlightColor; + uint16 _thickness; + uint16 _cornerDiameter; +}; + +class Animation : public DisplayElement, public DynamicElement { +public: + Animation(const DisplayElementID id) : DisplayElement(id) {} +}; + +class IdlerAnimation : public Animation, public Idler { +public: + IdlerAnimation(const DisplayElementID); + + virtual void startDisplaying(); + virtual void stopDisplaying(); + + TimeValue getLastTime() const { return _lastTime; } + +protected: + virtual void useIdleTime(); + virtual void timeChanged(const TimeValue); + + TimeValue _lastTime; +}; + +// This class reads PICT resources and plays them like a movie. +// Assumes there is a resource of type 'PFrm' describing the time values for each +// PICT frame, as well as the total time in the movie. +// Assumes that PICT frames begin at PICT 128 + +class FrameSequence : public IdlerAnimation { +public: + FrameSequence(const DisplayElementID); + virtual ~FrameSequence(); + + void useFileName(const Common::String &fileName); + + virtual void openFrameSequence(); + virtual void closeFrameSequence(); + bool isSequenceOpen() const; + + uint16 getNumFrames() const { return _numFrames; } + virtual uint16 getFrameNum() const { return _currentFrameNum; } + virtual void setFrameNum(const int16); + +protected: + virtual void timeChanged(const TimeValue); + virtual void newFrame(const uint16) {} + + Common::MacResManager *_resFork; + TimeValue _duration; + + uint16 _numFrames; + Common::Array<TimeValue> _frameTimes; + + uint16 _currentFrameNum; +}; + +class SpriteFrame; + +class Sprite : public DisplayElement { +friend class SpriteFrame; +public: + Sprite(const DisplayElementID); + virtual ~Sprite(); + + virtual void addPICTResourceFrame(const ResIDType, const bool, const CoordType, const CoordType); + virtual uint32 addFrame(SpriteFrame *, const CoordType, const CoordType); + virtual void removeFrame(const uint32); + virtual void discardFrames(); + + // Setting the current frame. + // If the index is negative, sets the current frame to NULL and hides the sprite. + // If the index is larger than the number of frames in the sprite, the number + // is treated modulo the number of frames. + virtual void setCurrentFrameIndex(const int32); + virtual uint32 getCurrentFrameIndex() const { return _currentFrameNum; } + + virtual SpriteFrame *getFrame(const int32); + + virtual void draw(const Common::Rect &); + + uint32 getNumFrames() const { return _numFrames; } + +protected: + struct SpriteFrameRec { + SpriteFrame *frame; + CoordType frameLeft; + CoordType frameTop; + }; + + uint32 _numFrames; + uint32 _currentFrameNum; + SpriteFrameRec *_currentFrame; + Common::Array<SpriteFrameRec> _frameArray; +}; + +class SpriteSequence : public FrameSequence { +public: + SpriteSequence(const DisplayElementID id, const DisplayElementID spriteID); + virtual ~SpriteSequence() {} + + void useTransparent(bool transparent) { _transparent = transparent; } + + virtual void openFrameSequence(); + virtual void closeFrameSequence(); + + virtual void draw(const Common::Rect &); + + virtual void setBounds(const Common::Rect &); + +protected: + virtual void newFrame(const uint16); + + bool _transparent; + Sprite _sprite; +}; + +class ScreenDimmer : public DisplayElement { +public: + ScreenDimmer() : DisplayElement(kScreenDimmerID) {} + virtual ~ScreenDimmer() {} + + virtual void draw(const Common::Rect &); +}; + +class SoundLevel : public DisplayElement { +public: + SoundLevel(const DisplayElementID); + virtual ~SoundLevel() {} + + void incrementLevel(); + void decrementLevel(); + + uint16 getSoundLevel(); + void setSoundLevel(uint16); + + void draw(const Common::Rect &); + +protected: + uint16 _soundLevel; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/energymonitor.cpp b/engines/pegasus/energymonitor.cpp new file mode 100644 index 0000000000..8aa77eb341 --- /dev/null +++ b/engines/pegasus/energymonitor.cpp @@ -0,0 +1,296 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/energymonitor.h" +#include "pegasus/pegasus.h" +#include "pegasus/surface.h" + +namespace Pegasus { + +Blinker::Blinker() { + _sprite = 0; + _frame1 = -1; + _frame2 = -1; + _blinkDuration = 0; +} + +void Blinker::startBlinking(Sprite *sprite, int32 frame1, int32 frame2, uint32 numBlinks, TimeValue blinkDuration, TimeScale blinkScale) { + stopBlinking(); + _sprite = sprite; + _frame1 = frame1; + _frame2 = frame2; + _blinkDuration = blinkDuration; + setScale(blinkScale); + setSegment(0, blinkDuration * numBlinks * 2, blinkScale); + setTime(0); + start(); +} + +void Blinker::stopBlinking() { + if (_sprite) { + _sprite->setCurrentFrameIndex(_frame2); + _sprite = 0; + stop(); + } +} + +void Blinker::timeChanged(const TimeValue time) { + if (_sprite && _blinkDuration != 0) { + if (((time / _blinkDuration) & 1) != 0 || time == getDuration()) { + _sprite->setCurrentFrameIndex(_frame2); + if (!isRunning()) + stopBlinking(); + } else { + _sprite->setCurrentFrameIndex(_frame1); + } + } +} + +static const NotificationFlags kEnergyExpiredFlag = 1; + +EnergyMonitor *g_energyMonitor = 0; + +EnergyMonitor::EnergyMonitor() : IdlerAnimation(kEnergyBarID), _energyLight(kWarningLightID) { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + _stage = kStageNoStage; + + _calibrating = false; + _dontFlash = false; + + setBounds(338, 48, 434, 54); + + setDisplayOrder(kEnergyBarOrder); + startDisplaying(); + + SpriteFrame *frame = new SpriteFrame(); + frame->initFromPICTResource(vm->_resFork, kLightOffID); + _energyLight.addFrame(frame, 0, 0); + + frame = new SpriteFrame(); + frame->initFromPICTResource(vm->_resFork, kLightYellowID); + _energyLight.addFrame(frame, 0, 0); + + frame = new SpriteFrame(); + frame->initFromPICTResource(vm->_resFork, kLightOrangeID); + _energyLight.addFrame(frame, 0, 0); + + frame = new SpriteFrame(); + frame->initFromPICTResource(vm->_resFork, kLightRedID); + _energyLight.addFrame(frame, 0, 0); + + _energyLight.setBounds(540, 35, 600, 59); + _energyLight.setDisplayOrder(kEnergyLightOrder); + _energyLight.startDisplaying(); + + setScale(1); + setSegment(0, kMaxJMPEnergy); + + setEnergyValue(kCasualEnergy); + + g_energyMonitor = this; +} + +EnergyMonitor::~EnergyMonitor() { + g_energyMonitor = 0; +} + +void EnergyMonitor::setEnergyValue(const uint32 value) { + if (isRunning()) { + stop(); + setTime(getStop() - value); + start(); + } else { + setTime(getStop() - value); + } +} + +void EnergyMonitor::startEnergyDraining() { + if (!isRunning()) { + _energyLight.show(); + start(); + show(); + } +} + +void EnergyMonitor::setEnergyDrainRate(Common::Rational rate) { + setRate(rate); +} + +Common::Rational EnergyMonitor::getEnergyDrainRate() { + return getRate(); +} + +void EnergyMonitor::stopEnergyDraining() { + if (isRunning()) { + stop(); + _energyLight.hide(); + hide(); + } +} + +void EnergyMonitor::drainEnergy(const int32 delta) { + setTime(getTime() + delta); +} + +int32 EnergyMonitor::getCurrentEnergy() { + return kMaxJMPEnergy - getTime(); +} + +void EnergyMonitor::timeChanged(const TimeValue currentTime) { + if (currentTime == getStop()) { + PegasusEngine *vm = (PegasusEngine *)g_engine; + if (vm->getEnergyDeathReason() != -1) + vm->die(vm->getEnergyDeathReason()); + } else { + uint32 currentEnergy = kMaxJMPEnergy - currentTime; + + EnergyStage newStage; + if (currentEnergy > kWorriedEnergy) + newStage = kStageCasual; + else if (currentEnergy > kNervousEnergy) + newStage = kStageWorried; + else if (currentEnergy > kPanicStrickenEnergy) + newStage = kStageNervous; + else + newStage = kStagePanicStricken; + + if (_stage != newStage) { + uint32 newFrame; + + switch (newStage) { + case kStageCasual: + _barColor = g_system->getScreenFormat().RGBToColor(0x48, 0xB0, 0xD8); + newFrame = kFrameLightOff; + break; + case kStageWorried: + _barColor = g_system->getScreenFormat().RGBToColor(0xD8, 0xC0, 0x30); + newFrame = kFrameLightYellow; + break; + case kStageNervous: + _barColor = g_system->getScreenFormat().RGBToColor(0xD8, 0x78, 0x38); + newFrame = kFrameLightOrange; + break; + case kStagePanicStricken: + _barColor = g_system->getScreenFormat().RGBToColor(0xD8, 0x40, 0x38); + newFrame = kFrameLightRed; + break; + default: + error("no stage in energy monitor?"); + break; + } + + _stage = newStage; + uint32 oldFrame = _energyLight.getCurrentFrameIndex(); + + if (!_calibrating) { + if (oldFrame > newFrame || oldFrame == 0xffffffff || _dontFlash) { + _energyLight.setCurrentFrameIndex(newFrame); + _dontFlash = false; + } else { + _lightBlinker.startBlinking(&_energyLight, oldFrame, newFrame, 4, 1, 3); + triggerRedraw(); + } + } + } + + Common::Rect r; + calcLevelRect(r); + if (r != _levelRect) { + _levelRect = r; + triggerRedraw(); + } + } +} + +void EnergyMonitor::calcLevelRect(Common::Rect &r) { + if (getStop() == 0) { + r = Common::Rect(); + } else { + getBounds(r); + r.left = r.right - r.width() * (kMaxJMPEnergy - getTime()) / getStop(); + } +} + +void EnergyMonitor::draw(const Common::Rect &r) { + Common::Rect r2 = r.findIntersectingRect(_levelRect); + + if (!r2.isEmpty()) { + Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getWorkArea(); + screen->fillRect(r2, _barColor); + } +} + +void EnergyMonitor::calibrateEnergyBar() { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + _calibrating = true; + + vm->setEnergyDeathReason(-1); + + uint32 numFrames = _energyLight.getNumFrames(); + for (uint32 i = 1; i < numFrames; i++) { + _energyLight.setCurrentFrameIndex(i); + _energyLight.show(); + vm->delayShell(1, 3); + _energyLight.hide(); + vm->delayShell(1, 3); + } + + _energyLight.setCurrentFrameIndex(0); + _energyLight.hide(); + + show(); + setEnergyValue(0); + setEnergyDrainRate(-(int32)kMaxJMPEnergy / 2); + + // Make sure warning light is hidden... + _energyLight.hide(); + while (getCurrentEnergy() != (int32)kMaxJMPEnergy) { + vm->checkCallBacks(); + vm->refreshDisplay(); + g_system->delayMillis(10); + } + + vm->refreshDisplay(); + setEnergyDrainRate(0); + hide(); + + _calibrating = false; +} + +void EnergyMonitor::restoreLastEnergyValue() { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + _dontFlash = true; + setEnergyValue(vm->getSavedEnergyValue()); + vm->resetEnergyDeathReason(); +} + +void EnergyMonitor::saveCurrentEnergyValue() { + ((PegasusEngine *)g_engine)->setLastEnergyValue(getCurrentEnergy()); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/energymonitor.h b/engines/pegasus/energymonitor.h new file mode 100644 index 0000000000..02377d515a --- /dev/null +++ b/engines/pegasus/energymonitor.h @@ -0,0 +1,111 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_ENERGYMONITOR_H +#define PEGASUS_ENERGYMONITOR_H + +#include "pegasus/elements.h" + +namespace Pegasus { + +class Sprite; + +class Blinker : private IdlerTimeBase { +public: + Blinker(); + virtual ~Blinker() {} + + void startBlinking(Sprite *sprite, int32 frame1, int32 frame2, uint32 numBlinks, TimeValue blinkDuration, TimeScale blinkScale); + void stopBlinking(); + +protected: + virtual void timeChanged(const TimeValue); + + Sprite *_sprite; + int32 _frame1; + int32 _frame2; + TimeValue _blinkDuration; +}; + +// Energy monitor constants. + +// These are in seconds. +// Max is two hours +static const uint32 kMaxJMPEnergy = 7200; + +static const uint32 kCasualEnergy = kMaxJMPEnergy * 100 / 100; // 100% +static const uint32 kWorriedEnergy = kMaxJMPEnergy * 50 / 100; // 50% +static const uint32 kNervousEnergy = kMaxJMPEnergy * 25 / 100; // 25% +static const uint32 kPanicStrickenEnergy = kMaxJMPEnergy * 5 / 100; // 5% + +static const uint32 kFullEnergy = kCasualEnergy; + +static const uint32 kFrameLightOff = 0; +static const uint32 kFrameLightYellow = 1; +static const uint32 kFrameLightOrange = 2; +static const uint32 kFrameLightRed = 3; + +static const int kEnergyDrainNormal = 1; +static const int kMarsReactorEnergyDrainNoShield = 6; +static const int kMarsReactorEnergyDrainWithShield = 3; +static const int kWSCPoisonEnergyDrainWithDart = 20; +static const int kWSCPoisonEnergyDrainNoDart = 10; + +class EnergyMonitor : private IdlerAnimation { +public: + EnergyMonitor(); + virtual ~EnergyMonitor(); + + void setEnergyValue(const uint32); + void startEnergyDraining(); + void setEnergyDrainRate(Common::Rational); + Common::Rational getEnergyDrainRate(); + void stopEnergyDraining(); + void drainEnergy(const int32); + int32 getCurrentEnergy(); + + void restoreLastEnergyValue(); + void saveCurrentEnergyValue(); + + void calibrateEnergyBar(); + +protected: + void timeChanged(const TimeValue); + void calcLevelRect(Common::Rect &); + void draw(const Common::Rect &); + + uint32 _barColor; + Common::Rect _levelRect; + EnergyStage _stage; + Sprite _energyLight; + Blinker _lightBlinker; + bool _calibrating, _dontFlash; +}; + +extern EnergyMonitor *g_energyMonitor; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/fader.cpp b/engines/pegasus/fader.cpp new file mode 100644 index 0000000000..a2bbf22944 --- /dev/null +++ b/engines/pegasus/fader.cpp @@ -0,0 +1,218 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/fader.h" +#include "pegasus/pegasus.h" +#include "pegasus/sound.h" +#include "pegasus/util.h" + +namespace Pegasus { + +Fader::Fader() { + _currentValue = 0; + _currentFaderMove._numKnots = 0; +} + +void Fader::setFaderValue(const int32 newValue) { + _currentValue = newValue; +} + +bool Fader::initFaderMove(const FaderMoveSpec &spec) { + bool faderMoves = false; + int32 value = 0; + + if (spec._numKnots > 0) { + stopFader(); + value = spec._knots[0].knotValue; + TimeValue startTime = spec._knots[0].knotTime; + + if (startTime != 0xffffffff) { + if (spec._numKnots > 1) { + TimeValue stopTime = spec._knots[spec._numKnots - 1].knotTime; + + if (spec._faderScale > 0) { + if (stopTime > startTime) { + for (uint32 i = 1; i < spec._numKnots; ++i) { + if (spec._knots[i - 1].knotValue != spec._knots[i].knotValue) { + faderMoves = true; + break; + } + } + + if (faderMoves) + _currentFaderMove = spec; + } else if (spec._knots[spec._numKnots - 1].knotValue != value) { + value = spec._knots[spec._numKnots - 1].knotValue; + } + } + } + } + } + + setFaderValue(value); + return faderMoves; +} + +void Fader::startFader(const FaderMoveSpec &spec) { + if (initFaderMove(spec)) { + setFlags(0); + setScale(spec._faderScale); + setSegment(spec._knots[0].knotTime, spec._knots[spec._numKnots - 1].knotTime); + setTime(spec._knots[0].knotTime); + start(); + } +} + +void Fader::startFaderSync(const FaderMoveSpec &spec) { + if (initFaderMove(spec)) { + setFlags(0); + setScale(spec._faderScale); + setSegment(spec._knots[0].knotTime, spec._knots[spec._numKnots - 1].knotTime); + setTime(spec._knots[0].knotTime); + start(); + + while (isFading()) { + ((PegasusEngine *)g_engine)->checkCallBacks(); + useIdleTime(); + } + + // Once more, for good measure, to make sure that there are no boundary + // condition problems. + useIdleTime(); + stopFader(); + } +} + +void Fader::loopFader(const FaderMoveSpec &spec) { + if (initFaderMove(spec)) { + setFlags(kLoopTimeBase); + setScale(spec._faderScale); + setSegment(spec._knots[0].knotTime, spec._knots[spec._numKnots - 1].knotTime); + setTime(spec._knots[0].knotTime); + start(); + } +} + +void Fader::stopFader() { + stop(); +} + +void Fader::pauseFader() { + stopFader(); +} + +void Fader::continueFader() { + if (getTime() < getStop()) + start(); +} + +void Fader::timeChanged(const TimeValue newTime) { + if (_currentFaderMove._numKnots != 0) { + uint32 i; + for (i = 0; i < _currentFaderMove._numKnots; i++) + if (_currentFaderMove._knots[i].knotTime > newTime) + break; + + int32 newValue; + if (i == 0) + newValue = _currentFaderMove._knots[0].knotValue; + else if (i == _currentFaderMove._numKnots) + newValue = _currentFaderMove._knots[i - 1].knotValue; + else + newValue = linearInterp(_currentFaderMove._knots[i - 1].knotTime, _currentFaderMove._knots[i].knotTime, newTime, _currentFaderMove._knots[i - 1].knotValue, _currentFaderMove._knots[i].knotValue); + + if (newValue != _currentValue) + setFaderValue(newValue); + } +} + +void FaderMoveSpec::makeOneKnotFaderSpec(const int32 knotValue) { + _numKnots = 1; + _knots[0].knotTime = 0; + _knots[0].knotValue = knotValue; +} + +void FaderMoveSpec::makeTwoKnotFaderSpec(const TimeScale faderScale, const TimeValue time1, const int32 value1, const TimeValue time2, const int32 value2) { + _numKnots = 2; + _faderScale = faderScale; + _knots[0].knotTime = time1; + _knots[0].knotValue = value1; + _knots[1].knotTime = time2; + _knots[1].knotValue = value2; +} + +void FaderMoveSpec::insertFaderKnot(const TimeValue knotTime, const int32 knotValue) { + if (_numKnots != kMaxFaderKnots) { + uint32 index; + for (index = 0; index < _numKnots; index++) { + if (knotTime == _knots[index].knotTime) { + _knots[index].knotValue = knotValue; + return; + } else if (knotTime < _knots[index].knotTime) { + break; + } + } + + for (uint32 i = _numKnots; i > index; i--) + _knots[i] = _knots[i - 1]; + + _knots[index].knotTime = knotTime; + _knots[index].knotValue = knotValue; + _numKnots++; + } +} + +void FaderAnimation::setFaderValue(const int32 newValue) { + if (getFaderValue() != newValue) { + Fader::setFaderValue(newValue); + triggerRedraw(); + } +} + +SoundFader::SoundFader() { + _sound = 0; + _masterVolume = 0xff; +} + +void SoundFader::attachSound(Sound *sound) { + if (!sound && isFading()) + stopFader(); + + _sound = sound; +} + +void SoundFader::setFaderValue(const int32 newVolume) { + if (_sound) + _sound->setVolume((newVolume * _masterVolume) >> 8); + + _currentValue = newVolume; +} + +void SoundFader::setMasterVolume(const uint16 masterVolume) { + _masterVolume = masterVolume; + setFaderValue(getFaderValue()); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/fader.h b/engines/pegasus/fader.h new file mode 100644 index 0000000000..0a8cd549e6 --- /dev/null +++ b/engines/pegasus/fader.h @@ -0,0 +1,130 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_FADER_H +#define PEGASUS_FADER_H + +#include "pegasus/elements.h" +#include "pegasus/timers.h" + +namespace Pegasus { + +class Fader; + +class FaderMoveSpec { +friend class Fader; +public: + FaderMoveSpec() { + _faderScale = kDefaultTimeScale; + _numKnots = 0; + } + + FaderMoveSpec(const TimeScale scale) { + _faderScale = scale; + _numKnots = 0; + } + + void setFaderScale(const TimeScale scale) { _faderScale = scale; } + TimeScale getFaderScale() const { return _faderScale; } + + void makeOneKnotFaderSpec(const int32); + void makeTwoKnotFaderSpec(const TimeScale, const TimeValue, const int32, const TimeValue, const int32); + + void insertFaderKnot(const TimeValue, const int32); + + uint32 getNumKnots() const { return _numKnots; } + TimeValue getNthKnotTime(const uint32 index) const { return _knots[index].knotTime; } + int32 getNthKnotValue(const uint32 index) const { return _knots[index].knotValue; } + +protected: + struct FaderKnot { + TimeValue knotTime; + int32 knotValue; + }; + + TimeScale _faderScale; + uint32 _numKnots; + + static const uint32 kMaxFaderKnots = 20; + FaderKnot _knots[kMaxFaderKnots]; +}; + +class Fader : public IdlerTimeBase { +public: + Fader(); + virtual ~Fader() {} + + virtual void setFaderValue(const int32); + int32 getFaderValue() const { return _currentValue; } + virtual void startFader(const FaderMoveSpec &); + virtual void startFaderSync(const FaderMoveSpec &); + virtual void loopFader(const FaderMoveSpec &); + virtual void stopFader(); + virtual bool isFading() { return isRunning(); } + + void pauseFader(); + void continueFader(); + + void getCurrentFaderMove(FaderMoveSpec &spec) { spec = _currentFaderMove; } + +protected: + bool initFaderMove(const FaderMoveSpec &); + virtual void timeChanged(const TimeValue); + + int32 _currentValue; + FaderMoveSpec _currentFaderMove; +}; + +class FaderAnimation : public DisplayElement, public Fader { +public: + FaderAnimation(const DisplayElementID id) : DisplayElement(id) {} + virtual ~FaderAnimation() {} + + void setFaderValue(const int32); +}; + +class Sound; + +class SoundFader : public Fader { +friend class Sound; +public: + SoundFader(); + virtual ~SoundFader() {} + + void setFaderValue(const int32); + + void setMasterVolume(const uint16); + uint16 getMasterVolume() const { return _masterVolume; } + +protected: + void attachSound(Sound *); + + Sound *_sound; + uint16 _masterVolume; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/gamestate.cpp b/engines/pegasus/gamestate.cpp new file mode 100644 index 0000000000..7a4e657e02 --- /dev/null +++ b/engines/pegasus/gamestate.cpp @@ -0,0 +1,2359 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "common/error.h" +#include "common/stream.h" + +#include "pegasus/constants.h" +#include "pegasus/gamestate.h" +#include "pegasus/scoring.h" + +namespace Common { +DECLARE_SINGLETON(Pegasus::GameStateManager); +} + +namespace Pegasus { + +Common::Error GameStateManager::writeGameState(Common::WriteStream *stream) { + stream->writeUint16BE(_currentNeighborhood); + stream->writeUint16BE(_currentRoom); + stream->writeByte(_currentDirection); + stream->writeUint16BE(_nexNeighborhoodID); + stream->writeUint16BE(_nextRoomID); + stream->writeByte(_nextDirection); + stream->writeUint16BE(_lastNeighborhood); + stream->writeUint16BE(_lastRoom); + stream->writeByte(_lastDirection); + stream->writeUint16BE(_openDoorRoom); + stream->writeByte(_openDoorDirection); + + _globalFlags.writeToStream(stream); + _scoringFlags.writeToStream(stream); + _itemTakenFlags.writeToStream(stream); + + writeCaldoriaState(stream); + writeTSAState(stream); + writePrehistoricState(stream); + writeNoradState(stream); + writeMarsState(stream); + writeWSCState(stream); + + if (stream->err()) + return Common::kWritingFailed; + + return Common::kNoError; +} + +Common::Error GameStateManager::readGameState(Common::ReadStream *stream) { + _currentNeighborhood = stream->readUint16BE(); + _currentRoom = stream->readUint16BE(); + _currentDirection = stream->readByte(); + _nexNeighborhoodID = stream->readUint16BE(); + _nextRoomID = stream->readUint16BE(); + _nextDirection = stream->readByte(); + _lastNeighborhood = stream->readUint16BE(); + _lastRoom = stream->readUint16BE(); + _lastDirection = stream->readByte(); + _openDoorRoom = stream->readUint16BE(); + _openDoorDirection = stream->readByte(); + + _globalFlags.readFromStream(stream); + _scoringFlags.readFromStream(stream); + _itemTakenFlags.readFromStream(stream); + + readCaldoriaState(stream); + readTSAState(stream); + readPrehistoricState(stream); + readNoradState(stream); + readMarsState(stream); + readWSCState(stream); + + if (stream->err()) + return Common::kReadingFailed; + + return Common::kNoError; +} + +void GameStateManager::resetGameState() { + _currentNeighborhood = kNoNeighborhoodID; + _currentRoom = kNoRoomID; + _currentDirection = kNoDirection; + _nexNeighborhoodID = kNoNeighborhoodID; + _nextRoomID = kNoRoomID; + _nextDirection = kNoDirection; + _lastNeighborhood = kNoNeighborhoodID; + _lastRoom = kNoRoomID; + _lastDirection = kNoDirection; + _openDoorRoom = kNoRoomID; + _openDoorDirection = kNoDirection; + + _globalFlags.clearAllFlags(); + _scoringFlags.clearAllFlags(); + _itemTakenFlags.clearAllFlags(); + + resetCaldoriaState(); + resetTSAState(); + resetPrehistoricState(); + resetNoradState(); + resetMarsState(); + resetWSCState(); +} + +void GameStateManager::getCurrentLocation(NeighborhoodID &neighborhood, RoomID &room, DirectionConstant &direction) { + neighborhood = _currentNeighborhood; + room = _currentRoom; + direction = _currentDirection; +} + +void GameStateManager::setCurrentLocation(const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) { + _lastNeighborhood = _currentNeighborhood; + _lastRoom = _currentRoom; + _lastDirection = _currentDirection; + _currentNeighborhood = neighborhood; + _currentRoom = room; + _currentDirection = direction; +} + +NeighborhoodID GameStateManager::getCurrentNeighborhood() { + return _currentNeighborhood; +} + +void GameStateManager::setCurrentNeighborhood(const NeighborhoodID neighborhood) { + _lastNeighborhood = _currentNeighborhood; + _currentNeighborhood = neighborhood; +} + +RoomID GameStateManager::getCurrentRoom() { + return _currentRoom; +} + +void GameStateManager::setCurrentRoom(const RoomID room) { + _lastRoom = _currentRoom; + _currentRoom = room; +} + +DirectionConstant GameStateManager::getCurrentDirection() { + return _currentDirection; +} + +void GameStateManager::setCurrentDirection(const DirectionConstant direction) { + _lastDirection = _currentDirection; + _currentDirection = direction; +} + +RoomViewID GameStateManager::getCurrentRoomAndView() { + return MakeRoomView(_currentRoom, _currentDirection); +} + +void GameStateManager::getNextLocation(NeighborhoodID &neighborhood, RoomID &room, DirectionConstant &direction) { + neighborhood = _nexNeighborhoodID; + room = _nextRoomID; + direction = _nextDirection; +} + +void GameStateManager::setNextLocation(const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) { + _nexNeighborhoodID = neighborhood; + _nextRoomID = room; + _nextDirection = direction; +} + +NeighborhoodID GameStateManager::getNextNeighborhood() { + return _nexNeighborhoodID; +} + +void GameStateManager::setNextNeighborhood(const NeighborhoodID neighborhood) { + _nexNeighborhoodID = neighborhood; +} + +RoomID GameStateManager::getNextRoom() { + return _nextRoomID; +} + +void GameStateManager::setNextRoom(const RoomID room) { + _nextRoomID = room; +} + +DirectionConstant GameStateManager::getNextDirection() { + return _nextDirection; +} + +void GameStateManager::setNextDirection(const DirectionConstant direction) { + _nextDirection = direction; +} + +void GameStateManager::getLastLocation(NeighborhoodID &neighborhood, RoomID &room, DirectionConstant &direction) { + neighborhood = _currentNeighborhood; + room = _currentRoom; + direction = _currentDirection; +} + +void GameStateManager::setLastLocation(const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) { + _currentNeighborhood = neighborhood; + _currentRoom = room; + _currentDirection = direction; +} + +NeighborhoodID GameStateManager::getLastNeighborhood() { + return _lastNeighborhood; +} + +void GameStateManager::setLastNeighborhood(const NeighborhoodID neighborhood) { + _lastNeighborhood = neighborhood; +} + +RoomID GameStateManager::getLastRoom() { + return _lastRoom; +} + +void GameStateManager::setLastRoom(const RoomID room) { + _lastRoom = room; +} + +DirectionConstant GameStateManager::getLastDirection() { + return _lastDirection; +} + +void GameStateManager::setLastDirection(const DirectionConstant direction) { + _lastDirection = direction; +} + +RoomViewID GameStateManager::getLastRoomAndView() { + return MakeRoomView(_lastRoom, _lastDirection); +} + +void GameStateManager::getOpenDoorLocation(RoomID &room, DirectionConstant &direction) { + room = _openDoorRoom; + direction = _openDoorDirection; +} + +void GameStateManager::setOpenDoorLocation(const RoomID room, const DirectionConstant direction) { + _openDoorRoom = room; + _openDoorDirection = direction; +} + +RoomID GameStateManager::getOpenDoorRoom() { + return _openDoorRoom; +} + +void GameStateManager::setOpenDoorRoom(const RoomID room) { + _openDoorRoom = room; +} + +DirectionConstant GameStateManager::getOpenDoorDirection() { + return _openDoorDirection; +} + +void GameStateManager::setOpenDoorDirection(const DirectionConstant direction) { + _openDoorDirection = direction; +} + +RoomViewID GameStateManager::getDoorOpenRoomAndView() { + return MakeRoomView(_openDoorRoom, _openDoorDirection); +} + +bool GameStateManager::isCurrentDoorOpen() { + return _openDoorRoom == _currentRoom && _openDoorDirection == _currentDirection; +} + +GameScoreType GameStateManager::getCaldoriaTSAScore() { + GameScoreType result = 0; + + if (_scoringFlags.getFlag(kScoringSawINNFlag)) + result += kSawINNScore; + if (_scoringFlags.getFlag(kScoringTookShowerFlag)) + result += kTookShowerScore; + if (_scoringFlags.getFlag(kScoringFixedHairFlag)) + result += kFixedHairScore; + if (_scoringFlags.getFlag(kScoringGotKeyCardFlag)) + result += kGotKeyCardScore; + if (_scoringFlags.getFlag(kScoringReadPaperFlag)) + result += kReadPaperScore; + if (_scoringFlags.getFlag(kScoringLookThroughTelescopeFlag)) + result += kLookThroughTelescopeScore; + if (_scoringFlags.getFlag(kScoringSawCaldoriaKioskFlag)) + result += kSawCaldoriaKioskScore; + if (_scoringFlags.getFlag(kScoringGoToTSAFlag)) + result += kGoToTSAScore; + if (_scoringFlags.getFlag(kScoringEnterTSAFlag)) + result += kEnterTSAScore; + if (_scoringFlags.getFlag(kScoringSawBust1Flag)) + result += kSawBust1Score; + if (_scoringFlags.getFlag(kScoringSawBust2Flag)) + result += kSawBust2Score; + if (_scoringFlags.getFlag(kScoringSawBust3Flag)) + result += kSawBust3Score; + if (_scoringFlags.getFlag(kScoringSawBust4Flag)) + result += kSawBust4Score; + if (_scoringFlags.getFlag(kScoringSawBust5Flag)) + result += kSawBust5Score; + if (_scoringFlags.getFlag(kScoringSawBust6Flag)) + result += kSawBust6Score; + if (_scoringFlags.getFlag(kScoringSawTheoryFlag)) + result += kSawTheoryScore; + if (_scoringFlags.getFlag(kScoringSawBackgroundFlag)) + result += kSawBackgroundScore; + if (_scoringFlags.getFlag(kScoringSawProcedureFlag)) + result += kSawProcedureScore; + if (_scoringFlags.getFlag(kScoringGotJourneymanKeyFlag)) + result += kGotJourneymanKeyScore; + if (_scoringFlags.getFlag(kScoringGotPegasusBiochipFlag)) + result += kGotPegasusBiochipScore; + if (_scoringFlags.getFlag(kScoringGotBiosuitFlag)) + result += kGotBiosuitScore; + if (_scoringFlags.getFlag(kScoringGoToPrehistoricFlag)) + result += kGoToPrehistoricScore; + if (_scoringFlags.getFlag(kScoringPutLogInReaderFlag)) + result += kPutLogInReaderScore; + if (_scoringFlags.getFlag(kScoringSawCaldoriaNormalFlag)) + result += kSawCaldoriaNormalScore; + if (_scoringFlags.getFlag(kScoringSawCaldoriaAlteredFlag)) + result += kSawCaldoriaAlteredScore; + if (_scoringFlags.getFlag(kScoringSawNoradNormalFlag)) + result += kSawNoradNormalScore; + if (_scoringFlags.getFlag(kScoringSawNoradAlteredFlag)) + result += kSawNoradAlteredScore; + if (_scoringFlags.getFlag(kScoringSawMarsNormalFlag)) + result += kSawMarsNormalScore; + if (_scoringFlags.getFlag(kScoringSawMarsAlteredFlag)) + result += kSawMarsAlteredScore; + if (_scoringFlags.getFlag(kScoringSawWSCNormalFlag)) + result += kSawWSCNormalScore; + if (_scoringFlags.getFlag(kScoringSawWSCAlteredFlag)) + result += kSawWSCAlteredScore; + if (_scoringFlags.getFlag(kScoringWentToReadyRoom2Flag)) + result += kWentToReadyRoom2Score; + if (_scoringFlags.getFlag(kScoringWentAfterSinclairFlag)) + result += kWentAfterSinclairScore; + if (_scoringFlags.getFlag(kScoringUsedCardBombFlag)) + result += kUsedCardBombScore; + if (_scoringFlags.getFlag(kScoringShieldedCardBombFlag)) + result += kShieldedCardBombScore; + if (_scoringFlags.getFlag(kScoringStunnedSinclairFlag)) + result += kStunnedSinclairScore; + if (_scoringFlags.getFlag(kScoringDisarmedNukeFlag)) + result += kDisarmedNukeScore; + + return result; +} + +GameScoreType GameStateManager::getPrehistoricScore() { + GameScoreType result = 0; + + if (_scoringFlags.getFlag(kScoringThrewBreakerFlag)) + result += kThrewBreakerScore; + if (_scoringFlags.getFlag(kScoringExtendedBridgeFlag)) + result += kExtendedBridgeScore; + if (_scoringFlags.getFlag(kScoringGotHistoricalLogFlag)) + result += kGotHistoricalLogScore; + if (_scoringFlags.getFlag(kScoringFinishedPrehistoricFlag)) + result += kFinishedPrehistoricScore; + + return result; +} + +GameScoreType GameStateManager::getMarsScore() { + GameScoreType result = 0; + + if (_scoringFlags.getFlag(kScoringThrownByRobotFlag)) + result += kThrownByRobotScore; + if (_scoringFlags.getFlag(kScoringGotMarsCardFlag)) + result += kGotMarsCardScore; + if (_scoringFlags.getFlag(kScoringSawMarsKioskFlag)) + result += kSawMarsKioskScore; + if (_scoringFlags.getFlag(kScoringSawTransportMapFlag)) + result += kSawTransportMapScore; + if (_scoringFlags.getFlag(kScoringGotCrowBarFlag)) + result += kGotCrowBarScore; + if (_scoringFlags.getFlag(kScoringTurnedOnTransportFlag)) + result += kTurnedOnTransportScore; + if (_scoringFlags.getFlag(kScoringGotOxygenMaskFlag)) + result += kGotOxygenMaskScore; + if (_scoringFlags.getFlag(kScoringAvoidedRobotFlag)) + result += kAvoidedRobotScore; + if (_scoringFlags.getFlag(kScoringActivatedPlatformFlag)) + result += kActivatedPlatformScore; + if (_scoringFlags.getFlag(kScoringUsedLiquidNitrogenFlag)) + result += kUsedLiquidNitrogenScore; + if (_scoringFlags.getFlag(kScoringUsedCrowBarFlag)) + result += kUsedCrowBarScore; + if (_scoringFlags.getFlag(kScoringFoundCardBombFlag)) + result += kFoundCardBombScore; + if (_scoringFlags.getFlag(kScoringDisarmedCardBombFlag)) + result += kDisarmedCardBombScore; + if (_scoringFlags.getFlag(kScoringGotCardBombFlag)) + result += kGotCardBombScore; + if (_scoringFlags.getFlag(kScoringThreadedMazeFlag)) + result += kThreadedMazeScore; + if (_scoringFlags.getFlag(kScoringThreadedGearRoomFlag)) + result += kThreadedGearRoomScore; + if (_scoringFlags.getFlag(kScoringEnteredShuttleFlag)) + result += kEnteredShuttleScore; + if (_scoringFlags.getFlag(kScoringEnteredLaunchTubeFlag)) + result += kEnteredLaunchTubeScore; + if (_scoringFlags.getFlag(kScoringStoppedRobotsShuttleFlag)) + result += kStoppedRobotsShuttleScore; + if (_scoringFlags.getFlag(kScoringGotMarsOpMemChipFlag)) + result += kGotMarsOpMemChipScore; + if (_scoringFlags.getFlag(kScoringFinishedMarsFlag)) + result += kFinishedMarsScore; + + return result; +} + +GameScoreType GameStateManager::getNoradScore() { + GameScoreType result = 0; + + if (_scoringFlags.getFlag(kScoringSawSecurityMonitorFlag)) + result += kSawSecurityMonitorScore; + if (_scoringFlags.getFlag(kScoringFilledOxygenCanisterFlag)) + result += kFilledOxygenCanisterScore; + if (_scoringFlags.getFlag(kScoringFilledArgonCanisterFlag)) + result += kFilledArgonCanisterScore; + if (_scoringFlags.getFlag(kScoringSawUnconsciousOperatorFlag)) + result += kSawUnconsciousOperatorScore; + if (_scoringFlags.getFlag(kScoringWentThroughPressureDoorFlag)) + result += kWentThroughPressureDoorScore; + if (_scoringFlags.getFlag(kScoringPreppedSubFlag)) + result += kPreppedSubScore; + if (_scoringFlags.getFlag(kScoringEnteredSubFlag)) + result += kEnteredSubScore; + if (_scoringFlags.getFlag(kScoringExitedSubFlag)) + result += kExitedSubScore; + if (_scoringFlags.getFlag(kScoringSawRobotAt54NorthFlag)) + result += kSawRobotAt54NorthScore; + if (_scoringFlags.getFlag(kScoringPlayedWithClawFlag)) + result += kPlayedWithClawScore; + if (_scoringFlags.getFlag(kScoringUsedRetinalChipFlag)) + result += kUsedRetinalChipScore; + if (_scoringFlags.getFlag(kScoringFinishedGlobeGameFlag)) + result += kFinishedGlobeGameScore; + if (_scoringFlags.getFlag(kScoringStoppedNoradRobotFlag)) + result += kStoppedNoradRobotScore; + if (_scoringFlags.getFlag(kScoringGotNoradOpMemChipFlag)) + result += kGotNoradOpMemChipScore; + if (_scoringFlags.getFlag(kScoringFinishedNoradFlag)) + result += kFinishedNoradScore; + + return result; +} + +GameScoreType GameStateManager::getWSCScore() { + GameScoreType result = 0; + + if (_scoringFlags.getFlag(kScoringRemovedDartFlag)) + result += kRemovedDartScore; + if (_scoringFlags.getFlag(kScoringAnalyzedDartFlag)) + result += kAnalyzedDartScore; + if (_scoringFlags.getFlag(kScoringBuiltAntidoteFlag)) + result += kBuiltAntidoteScore; + if (_scoringFlags.getFlag(kScoringGotSinclairKeyFlag)) + result += kGotSinclairKeyScore; + if (_scoringFlags.getFlag(kScoringGotArgonCanisterFlag)) + result += kGotArgonCanisterScore; + if (_scoringFlags.getFlag(kScoringGotNitrogenCanisterFlag)) + result += kGotNitrogenCanisterScore; + if (_scoringFlags.getFlag(kScoringPlayedWithMessagesFlag)) + result += kPlayedWithMessagesScore; + if (_scoringFlags.getFlag(kScoringSawMorphExperimentFlag)) + result += kSawMorphExperimentScore; + if (_scoringFlags.getFlag(kScoringEnteredSinclairOfficeFlag)) + result += kEnteredSinclairOfficeScore; + if (_scoringFlags.getFlag(kScoringSawBrochureFlag)) + result += kSawBrochureScore; + if (_scoringFlags.getFlag(kScoringSawSinclairEntry1Flag)) + result += kSawSinclairEntry1Score; + if (_scoringFlags.getFlag(kScoringSawSinclairEntry2Flag)) + result += kSawSinclairEntry2Score; + if (_scoringFlags.getFlag(kScoringSawSinclairEntry3Flag)) + result += kSawSinclairEntry3Score; + if (_scoringFlags.getFlag(kScoringSawWSCDirectoryFlag)) + result += kSawWSCDirectoryScore; + if (_scoringFlags.getFlag(kScoringUsedCrowBarInWSCFlag)) + result += kUsedCrowBarInWSCScore; + if (_scoringFlags.getFlag(kScoringFinishedPlasmaDodgeFlag)) + result += kFinishedPlasmaDodgeScore; + if (_scoringFlags.getFlag(kScoringOpenedCatwalkFlag)) + result += kOpenedCatwalkScore; + if (_scoringFlags.getFlag(kScoringStoppedWSCRobotFlag)) + result += kStoppedWSCRobotScore; + if (_scoringFlags.getFlag(kScoringGotWSCOpMemChipFlag)) + result += kGotWSCOpMemChipScore; + if (_scoringFlags.getFlag(kScoringFinishedWSCFlag)) + result += kFinishedWSCScore; + + return result; +} + +GameScoreType GameStateManager::getGandhiScore() { + GameScoreType result = 0; + + if (_scoringFlags.getFlag(kScoringMarsGandhiFlag)) + result += kMarsGandhiScore; + if (_scoringFlags.getFlag(kScoringNoradGandhiFlag)) + result += kNoradGandhiScore; + if (_scoringFlags.getFlag(kScoringWSCGandhiFlag)) + result += kWSCGandhiScore; + + return result; +} + +GameScoreType GameStateManager::getTotalScore() { + return getCaldoriaTSAScore() + + getPrehistoricScore() + + getMarsScore() + + getNoradScore() + + getWSCScore() + + getGandhiScore(); +} + +///////////////////////////////////////////// +// +// Caldoria data + +void GameStateManager::writeCaldoriaState(Common::WriteStream *stream) { + _caldoriaFlags.writeToStream(stream); + stream->writeUint32BE(_caldoriaFuseTimeLimit); +} + +void GameStateManager::readCaldoriaState(Common::ReadStream *stream) { + _caldoriaFlags.readFromStream(stream); + _caldoriaFuseTimeLimit = stream->readUint32BE(); +} + +void GameStateManager::resetCaldoriaState() { + _caldoriaFlags.clearAllFlags(); + _caldoriaFuseTimeLimit = 0; +} + +///////////////////////////////////////////// +// +// TSA data + +void GameStateManager::writeTSAState(Common::WriteStream *stream) { + _TSAFlags.writeToStream(stream); + stream->writeUint32BE(_TSARipTimerTime); + stream->writeUint32BE(_TSAFuseTimeLimit); + stream->writeByte(_TSAState); + stream->writeByte(_T0BMonitorMode); + stream->writeUint32BE(_T0BMonitorStart); +} + +void GameStateManager::readTSAState(Common::ReadStream *stream) { + _TSAFlags.readFromStream(stream); + _TSARipTimerTime = stream->readUint32BE(); + _TSAFuseTimeLimit = stream->readUint32BE(); + _TSAState = stream->readByte(); + _T0BMonitorMode = stream->readByte(); + _T0BMonitorStart = stream->readUint32BE(); +} + +void GameStateManager::resetTSAState() { + _TSAFlags.clearAllFlags(); + _TSAState = 0; + _T0BMonitorMode = 0; + _T0BMonitorStart = 0; + _TSARipTimerTime = 0; + _TSAFuseTimeLimit = kTSAUncreatedTimeLimit; +} + +///////////////////////////////////////////// +// +// Prehistoric data + +void GameStateManager::writePrehistoricState(Common::WriteStream *stream) { + _prehistoricFlags.writeToStream(stream); +} + +void GameStateManager::readPrehistoricState(Common::ReadStream *stream) { + _prehistoricFlags.readFromStream(stream); +} + +void GameStateManager::resetPrehistoricState() { + _prehistoricFlags.clearAllFlags(); +} + +///////////////////////////////////////////// +// +// Norad data + +void GameStateManager::writeNoradState(Common::WriteStream *stream) { + _noradFlags.writeToStream(stream); + stream->writeUint16BE(_noradSubRoomPressure); + stream->writeByte(_noradSubPrepState); +} + +void GameStateManager::readNoradState(Common::ReadStream *stream) { + _noradFlags.readFromStream(stream); + _noradSubRoomPressure = stream->readUint16BE(); + _noradSubPrepState = (NoradSubPrepState)stream->readByte(); +} + +void GameStateManager::resetNoradState() { + _noradFlags.clearAllFlags(); + _noradSubRoomPressure = 9; + _noradSubPrepState = kSubNotPrepped; +} + +///////////////////////////////////////////// +// +// Mars data + +void GameStateManager::writeMarsState(Common::WriteStream *stream) { + _marsFlags.writeToStream(stream); +} + +void GameStateManager::readMarsState(Common::ReadStream *stream) { + _marsFlags.readFromStream(stream); +} + +void GameStateManager::resetMarsState() { + _marsFlags.clearAllFlags(); +} + +///////////////////////////////////////////// +// +// WSC data + +void GameStateManager::writeWSCState(Common::WriteStream *stream) { + _WSCFlags.writeToStream(stream); +} + +void GameStateManager::readWSCState(Common::ReadStream *stream) { + _WSCFlags.readFromStream(stream); +} + +void GameStateManager::resetWSCState() { + _WSCFlags.clearAllFlags(); +} + +void GameStateManager::setScoringSawINN(const bool flag) { + _scoringFlags.setFlag(kScoringSawINNFlag, flag); +} + +void GameStateManager::setScoringTookShower(const bool flag) { + _scoringFlags.setFlag(kScoringTookShowerFlag, flag); +} + +void GameStateManager::setScoringFixedHair(const bool flag) { + _scoringFlags.setFlag(kScoringFixedHairFlag, flag); +} + +void GameStateManager::setScoringGotKeyCard(const bool flag) { + _scoringFlags.setFlag(kScoringGotKeyCardFlag, flag); +} + +void GameStateManager::setScoringReadPaper(const bool flag) { + _scoringFlags.setFlag(kScoringReadPaperFlag, flag); +} + +void GameStateManager::setScoringLookThroughTelescope(const bool flag) { + _scoringFlags.setFlag(kScoringLookThroughTelescopeFlag, flag); +} + +void GameStateManager::setScoringSawCaldoriaKiosk(const bool flag) { + _scoringFlags.setFlag(kScoringSawCaldoriaKioskFlag, flag); +} + +void GameStateManager::setScoringGoToTSA(const bool flag) { + _scoringFlags.setFlag(kScoringGoToTSAFlag, flag); +} + +void GameStateManager::setScoringEnterTSA(const bool flag) { + _scoringFlags.setFlag(kScoringEnterTSAFlag, flag); +} + +void GameStateManager::setScoringSawBust1(const bool flag) { + _scoringFlags.setFlag(kScoringSawBust1Flag, flag); +} + +void GameStateManager::setScoringSawBust2(const bool flag) { + _scoringFlags.setFlag(kScoringSawBust2Flag, flag); +} + +void GameStateManager::setScoringSawBust3(const bool flag) { + _scoringFlags.setFlag(kScoringSawBust3Flag, flag); +} + +void GameStateManager::setScoringSawBust4(const bool flag) { + _scoringFlags.setFlag(kScoringSawBust4Flag, flag); +} + +void GameStateManager::setScoringSawBust5(const bool flag) { + _scoringFlags.setFlag(kScoringSawBust5Flag, flag); +} + +void GameStateManager::setScoringSawBust6(const bool flag) { + _scoringFlags.setFlag(kScoringSawBust6Flag, flag); +} + +void GameStateManager::setScoringSawTheory(const bool flag) { + _scoringFlags.setFlag(kScoringSawTheoryFlag, flag); +} + +void GameStateManager::setScoringSawBackground(const bool flag) { + _scoringFlags.setFlag(kScoringSawBackgroundFlag, flag); +} + +void GameStateManager::setScoringSawProcedure(const bool flag) { + _scoringFlags.setFlag(kScoringSawProcedureFlag, flag); +} + +void GameStateManager::setScoringGotJourneymanKey(const bool flag) { + _scoringFlags.setFlag(kScoringGotJourneymanKeyFlag, flag); +} + +void GameStateManager::setScoringGotPegasusBiochip(const bool flag) { + _scoringFlags.setFlag(kScoringGotPegasusBiochipFlag, flag); +} + +void GameStateManager::setScoringGotBiosuit(const bool flag) { + _scoringFlags.setFlag(kScoringGotBiosuitFlag, flag); +} + +void GameStateManager::setScoringGoToPrehistoric(const bool flag) { + _scoringFlags.setFlag(kScoringGoToPrehistoricFlag, flag); +} + +void GameStateManager::setScoringPutLogInReader(const bool flag) { + _scoringFlags.setFlag(kScoringPutLogInReaderFlag, flag); +} + +void GameStateManager::setScoringSawCaldoriaNormal(const bool flag) { + _scoringFlags.setFlag(kScoringSawCaldoriaNormalFlag, flag); +} + +void GameStateManager::setScoringSawCaldoriaAltered(const bool flag) { + _scoringFlags.setFlag(kScoringSawCaldoriaAlteredFlag, flag); +} + +void GameStateManager::setScoringSawNoradNormal(const bool flag) { + _scoringFlags.setFlag(kScoringSawNoradNormalFlag, flag); +} + +void GameStateManager::setScoringSawNoradAltered(const bool flag) { + _scoringFlags.setFlag(kScoringSawNoradAlteredFlag, flag); +} + +void GameStateManager::setScoringSawMarsNormal(const bool flag) { + _scoringFlags.setFlag(kScoringSawMarsNormalFlag, flag); +} + +void GameStateManager::setScoringSawMarsAltered(const bool flag) { + _scoringFlags.setFlag(kScoringSawMarsAlteredFlag, flag); +} + +void GameStateManager::setScoringSawWSCNormal(const bool flag) { + _scoringFlags.setFlag(kScoringSawWSCNormalFlag, flag); +} + +void GameStateManager::setScoringSawWSCAltered(const bool flag) { + _scoringFlags.setFlag(kScoringSawWSCAlteredFlag, flag); +} + +void GameStateManager::setScoringWentToReadyRoom2(const bool flag) { + _scoringFlags.setFlag(kScoringWentToReadyRoom2Flag, flag); +} + +void GameStateManager::setScoringWentAfterSinclair(const bool flag) { + _scoringFlags.setFlag(kScoringWentAfterSinclairFlag, flag); +} + +void GameStateManager::setScoringUsedCardBomb(const bool flag) { + _scoringFlags.setFlag(kScoringUsedCardBombFlag, flag); +} + +void GameStateManager::setScoringShieldedCardBomb(const bool flag) { + _scoringFlags.setFlag(kScoringShieldedCardBombFlag, flag); +} + +void GameStateManager::setScoringStunnedSinclair(const bool flag) { + _scoringFlags.setFlag(kScoringStunnedSinclairFlag, flag); +} + +void GameStateManager::setScoringDisarmedNuke(const bool flag) { + _scoringFlags.setFlag(kScoringDisarmedNukeFlag, flag); +} + +void GameStateManager::setScoringThrewBreaker(const bool flag) { + _scoringFlags.setFlag(kScoringThrewBreakerFlag, flag); +} + +void GameStateManager::setScoringExtendedBridge(const bool flag) { + _scoringFlags.setFlag(kScoringExtendedBridgeFlag, flag); +} + +void GameStateManager::setScoringGotHistoricalLog(const bool flag) { + _scoringFlags.setFlag(kScoringGotHistoricalLogFlag, flag); +} + +void GameStateManager::setScoringFinishedPrehistoric(const bool flag) { + _scoringFlags.setFlag(kScoringFinishedPrehistoricFlag, flag); +} + +void GameStateManager::setScoringThrownByRobot(const bool flag) { + _scoringFlags.setFlag(kScoringThrownByRobotFlag, flag); +} + +void GameStateManager::setScoringGotMarsCard(const bool flag) { + _scoringFlags.setFlag(kScoringGotMarsCardFlag, flag); +} + +void GameStateManager::setScoringSawMarsKiosk(const bool flag) { + _scoringFlags.setFlag(kScoringSawMarsKioskFlag, flag); +} + +void GameStateManager::setScoringSawTransportMap(const bool flag) { + _scoringFlags.setFlag(kScoringSawTransportMapFlag, flag); +} + +void GameStateManager::setScoringGotCrowBar(const bool flag) { + _scoringFlags.setFlag(kScoringGotCrowBarFlag, flag); +} + +void GameStateManager::setScoringTurnedOnTransport(const bool flag) { + _scoringFlags.setFlag(kScoringTurnedOnTransportFlag, flag); +} + +void GameStateManager::setScoringGotOxygenMask(const bool flag) { + _scoringFlags.setFlag(kScoringGotOxygenMaskFlag, flag); +} + +void GameStateManager::setScoringAvoidedRobot(const bool flag) { + _scoringFlags.setFlag(kScoringAvoidedRobotFlag, flag); +} + +void GameStateManager::setScoringActivatedPlatform(const bool flag) { + _scoringFlags.setFlag(kScoringActivatedPlatformFlag, flag); +} + +void GameStateManager::setScoringUsedLiquidNitrogen(const bool flag) { + _scoringFlags.setFlag(kScoringUsedLiquidNitrogenFlag, flag); +} + +void GameStateManager::setScoringUsedCrowBar(const bool flag) { + _scoringFlags.setFlag(kScoringUsedCrowBarFlag, flag); +} + +void GameStateManager::setScoringFoundCardBomb(const bool flag) { + _scoringFlags.setFlag(kScoringFoundCardBombFlag, flag); +} + +void GameStateManager::setScoringDisarmedCardBomb(const bool flag) { + _scoringFlags.setFlag(kScoringDisarmedCardBombFlag, flag); +} + +void GameStateManager::setScoringGotCardBomb(const bool flag) { + _scoringFlags.setFlag(kScoringGotCardBombFlag, flag); +} + +void GameStateManager::setScoringThreadedMaze(const bool flag) { + _scoringFlags.setFlag(kScoringThreadedMazeFlag, flag); +} + +void GameStateManager::setScoringThreadedGearRoom(const bool flag) { + _scoringFlags.setFlag(kScoringThreadedGearRoomFlag, flag); +} + +void GameStateManager::setScoringEnteredShuttle(const bool flag) { + _scoringFlags.setFlag(kScoringEnteredShuttleFlag, flag); +} + +void GameStateManager::setScoringEnteredLaunchTube(const bool flag) { + _scoringFlags.setFlag(kScoringEnteredLaunchTubeFlag, flag); +} + +void GameStateManager::setScoringStoppedRobotsShuttle(const bool flag) { + _scoringFlags.setFlag(kScoringStoppedRobotsShuttleFlag, flag); +} + +void GameStateManager::setScoringGotMarsOpMemChip(const bool flag) { + _scoringFlags.setFlag(kScoringGotMarsOpMemChipFlag, flag); +} + +void GameStateManager::setScoringFinishedMars(const bool flag) { + _scoringFlags.setFlag(kScoringFinishedMarsFlag, flag); +} + +void GameStateManager::setScoringSawSecurityMonitor(const bool flag) { + _scoringFlags.setFlag(kScoringSawSecurityMonitorFlag, flag); +} + +void GameStateManager::setScoringFilledOxygenCanister(const bool flag) { + _scoringFlags.setFlag(kScoringFilledOxygenCanisterFlag, flag); +} + +void GameStateManager::setScoringFilledArgonCanister(const bool flag) { + _scoringFlags.setFlag(kScoringFilledArgonCanisterFlag, flag); +} + +void GameStateManager::setScoringSawUnconsciousOperator(const bool flag) { + _scoringFlags.setFlag(kScoringSawUnconsciousOperatorFlag, flag); +} + +void GameStateManager::setScoringWentThroughPressureDoor(const bool flag) { + _scoringFlags.setFlag(kScoringWentThroughPressureDoorFlag, flag); +} + +void GameStateManager::setScoringPreppedSub(const bool flag) { + _scoringFlags.setFlag(kScoringPreppedSubFlag, flag); +} + +void GameStateManager::setScoringEnteredSub(const bool flag) { + _scoringFlags.setFlag(kScoringEnteredSubFlag, flag); +} + +void GameStateManager::setScoringExitedSub(const bool flag) { + _scoringFlags.setFlag(kScoringExitedSubFlag, flag); +} + +void GameStateManager::setScoringSawRobotAt54North(const bool flag) { + _scoringFlags.setFlag(kScoringSawRobotAt54NorthFlag, flag); +} + +void GameStateManager::setScoringPlayedWithClaw(const bool flag) { + _scoringFlags.setFlag(kScoringPlayedWithClawFlag, flag); +} + +void GameStateManager::setScoringUsedRetinalChip(const bool flag) { + _scoringFlags.setFlag(kScoringUsedRetinalChipFlag, flag); +} + +void GameStateManager::setScoringFinishedGlobeGame(const bool flag) { + _scoringFlags.setFlag(kScoringFinishedGlobeGameFlag, flag); +} + +void GameStateManager::setScoringStoppedNoradRobot(const bool flag) { + _scoringFlags.setFlag(kScoringStoppedNoradRobotFlag, flag); +} + +void GameStateManager::setScoringGotNoradOpMemChip(const bool flag) { + _scoringFlags.setFlag(kScoringGotNoradOpMemChipFlag, flag); +} + +void GameStateManager::setScoringFinishedNorad(const bool flag) { + _scoringFlags.setFlag(kScoringFinishedNoradFlag, flag); +} + +void GameStateManager::setScoringRemovedDart(const bool flag) { + _scoringFlags.setFlag(kScoringRemovedDartFlag, flag); +} + +void GameStateManager::setScoringAnalyzedDart(const bool flag) { + _scoringFlags.setFlag(kScoringAnalyzedDartFlag, flag); +} + +void GameStateManager::setScoringBuiltAntidote(const bool flag) { + _scoringFlags.setFlag(kScoringBuiltAntidoteFlag, flag); +} + +void GameStateManager::setScoringGotSinclairKey(const bool flag) { + _scoringFlags.setFlag(kScoringGotSinclairKeyFlag, flag); +} + +void GameStateManager::setScoringGotArgonCanister(const bool flag) { + _scoringFlags.setFlag(kScoringGotArgonCanisterFlag, flag); +} + +void GameStateManager::setScoringGotNitrogenCanister(const bool flag) { + _scoringFlags.setFlag(kScoringGotNitrogenCanisterFlag, flag); +} + +void GameStateManager::setScoringPlayedWithMessages(const bool flag) { + _scoringFlags.setFlag(kScoringPlayedWithMessagesFlag, flag); +} + +void GameStateManager::setScoringSawMorphExperiment(const bool flag) { + _scoringFlags.setFlag(kScoringSawMorphExperimentFlag, flag); +} + +void GameStateManager::setScoringEnteredSinclairOffice(const bool flag) { + _scoringFlags.setFlag(kScoringEnteredSinclairOfficeFlag, flag); +} + +void GameStateManager::setScoringSawBrochure(const bool flag) { + _scoringFlags.setFlag(kScoringSawBrochureFlag, flag); +} + +void GameStateManager::setScoringSawSinclairEntry1(const bool flag) { + _scoringFlags.setFlag(kScoringSawSinclairEntry1Flag, flag); +} + +void GameStateManager::setScoringSawSinclairEntry2(const bool flag) { + _scoringFlags.setFlag(kScoringSawSinclairEntry2Flag, flag); +} + +void GameStateManager::setScoringSawSinclairEntry3(const bool flag) { + _scoringFlags.setFlag(kScoringSawSinclairEntry3Flag, flag); +} + +void GameStateManager::setScoringSawWSCDirectory(const bool flag) { + _scoringFlags.setFlag(kScoringSawWSCDirectoryFlag, flag); +} + +void GameStateManager::setScoringUsedCrowBarInWSC(const bool flag) { + _scoringFlags.setFlag(kScoringUsedCrowBarInWSCFlag, flag); +} + +void GameStateManager::setScoringFinishedPlasmaDodge(const bool flag) { + _scoringFlags.setFlag(kScoringFinishedPlasmaDodgeFlag, flag); +} + +void GameStateManager::setScoringOpenedCatwalk(const bool flag) { + _scoringFlags.setFlag(kScoringOpenedCatwalkFlag, flag); +} + +void GameStateManager::setScoringStoppedWSCRobot(const bool flag) { + _scoringFlags.setFlag(kScoringStoppedWSCRobotFlag, flag); +} + +void GameStateManager::setScoringGotWSCOpMemChip(const bool flag) { + _scoringFlags.setFlag(kScoringGotWSCOpMemChipFlag, flag); +} + +void GameStateManager::setScoringFinishedWSC(const bool flag) { + _scoringFlags.setFlag(kScoringFinishedWSCFlag, flag); +} + +void GameStateManager::setScoringMarsGandhi(const bool flag) { + _scoringFlags.setFlag(kScoringMarsGandhiFlag, flag); +} + +void GameStateManager::setScoringNoradGandhi(const bool flag) { + _scoringFlags.setFlag(kScoringNoradGandhiFlag, flag); +} + +void GameStateManager::setScoringWSCGandhi(const bool flag) { + _scoringFlags.setFlag(kScoringWSCGandhiFlag, flag); +} + +bool GameStateManager::getScoringSawINN() { + return _scoringFlags.getFlag(kScoringSawINNFlag); +} + +bool GameStateManager::getScoringTookShower() { + return _scoringFlags.getFlag(kScoringTookShowerFlag); +} + +bool GameStateManager::getScoringFixedHair() { + return _scoringFlags.getFlag(kScoringFixedHairFlag); +} + +bool GameStateManager::getScoringGotKeyCard() { + return _scoringFlags.getFlag(kScoringGotKeyCardFlag); +} + +bool GameStateManager::getScoringReadPaper() { + return _scoringFlags.getFlag(kScoringReadPaperFlag); +} + +bool GameStateManager::getScoringLookThroughTelescope() { + return _scoringFlags.getFlag(kScoringLookThroughTelescopeFlag); +} + +bool GameStateManager::getScoringSawCaldoriaKiosk() { + return _scoringFlags.getFlag(kScoringSawCaldoriaKioskFlag); +} + +bool GameStateManager::getScoringGoToTSA() { + return _scoringFlags.getFlag(kScoringGoToTSAFlag); +} + +bool GameStateManager::getScoringEnterTSA() { + return _scoringFlags.getFlag(kScoringEnterTSAFlag); +} + +bool GameStateManager::getScoringSawBust1() { + return _scoringFlags.getFlag(kScoringSawBust1Flag); +} + +bool GameStateManager::getScoringSawBust2() { + return _scoringFlags.getFlag(kScoringSawBust2Flag); +} + +bool GameStateManager::getScoringSawBust3() { + return _scoringFlags.getFlag(kScoringSawBust3Flag); +} + +bool GameStateManager::getScoringSawBust4() { + return _scoringFlags.getFlag(kScoringSawBust4Flag); +} + +bool GameStateManager::getScoringSawBust5() { + return _scoringFlags.getFlag(kScoringSawBust5Flag); +} + +bool GameStateManager::getScoringSawBust6() { + return _scoringFlags.getFlag(kScoringSawBust6Flag); +} + +bool GameStateManager::getScoringSawTheory() { + return _scoringFlags.getFlag(kScoringSawTheoryFlag); +} + +bool GameStateManager::getScoringSawBackground() { + return _scoringFlags.getFlag(kScoringSawBackgroundFlag); +} + +bool GameStateManager::getScoringSawProcedure() { + return _scoringFlags.getFlag(kScoringSawProcedureFlag); +} + +bool GameStateManager::getScoringGotJourneymanKey() { + return _scoringFlags.getFlag(kScoringGotJourneymanKeyFlag); +} + +bool GameStateManager::getScoringGotPegasusBiochip() { + return _scoringFlags.getFlag(kScoringGotPegasusBiochipFlag); +} + +bool GameStateManager::getScoringGotBiosuit() { + return _scoringFlags.getFlag(kScoringGotBiosuitFlag); +} + +bool GameStateManager::getScoringGoToPrehistoric() { + return _scoringFlags.getFlag(kScoringGoToPrehistoricFlag); +} + +bool GameStateManager::getScoringPutLogInReader() { + return _scoringFlags.getFlag(kScoringPutLogInReaderFlag); +} + +bool GameStateManager::getScoringSawCaldoriaNormal() { + return _scoringFlags.getFlag(kScoringSawCaldoriaNormalFlag); +} + +bool GameStateManager::getScoringSawCaldoriaAltered() { + return _scoringFlags.getFlag(kScoringSawCaldoriaAlteredFlag); +} + +bool GameStateManager::getScoringSawNoradNormal() { + return _scoringFlags.getFlag(kScoringSawNoradNormalFlag); +} + +bool GameStateManager::getScoringSawNoradAltered() { + return _scoringFlags.getFlag(kScoringSawNoradAlteredFlag); +} + +bool GameStateManager::getScoringSawMarsNormal() { + return _scoringFlags.getFlag(kScoringSawMarsNormalFlag); +} + +bool GameStateManager::getScoringSawMarsAltered() { + return _scoringFlags.getFlag(kScoringSawMarsAlteredFlag); +} + +bool GameStateManager::getScoringSawWSCNormal() { + return _scoringFlags.getFlag(kScoringSawWSCNormalFlag); +} + +bool GameStateManager::getScoringSawWSCAltered() { + return _scoringFlags.getFlag(kScoringSawWSCAlteredFlag); +} + +bool GameStateManager::getScoringWentToReadyRoom2() { + return _scoringFlags.getFlag(kScoringWentToReadyRoom2Flag); +} + +bool GameStateManager::getScoringWentAfterSinclair() { + return _scoringFlags.getFlag(kScoringWentAfterSinclairFlag); +} + +bool GameStateManager::getScoringUsedCardBomb() { + return _scoringFlags.getFlag(kScoringUsedCardBombFlag); +} + +bool GameStateManager::getScoringShieldedCardBomb() { + return _scoringFlags.getFlag(kScoringShieldedCardBombFlag); +} + +bool GameStateManager::getScoringStunnedSinclair() { + return _scoringFlags.getFlag(kScoringStunnedSinclairFlag); +} + +bool GameStateManager::getScoringDisarmedNuke() { + return _scoringFlags.getFlag(kScoringDisarmedNukeFlag); +} + +bool GameStateManager::getScoringThrewBreaker() { + return _scoringFlags.getFlag(kScoringThrewBreakerFlag); +} + +bool GameStateManager::getScoringExtendedBridge() { + return _scoringFlags.getFlag(kScoringExtendedBridgeFlag); +} + +bool GameStateManager::getScoringGotHistoricalLog() { + return _scoringFlags.getFlag(kScoringGotHistoricalLogFlag); +} + +bool GameStateManager::getScoringFinishedPrehistoric() { + return _scoringFlags.getFlag(kScoringFinishedPrehistoricFlag); +} + +bool GameStateManager::getScoringThrownByRobot() { + return _scoringFlags.getFlag(kScoringThrownByRobotFlag); +} + +bool GameStateManager::getScoringGotMarsCard() { + return _scoringFlags.getFlag(kScoringGotMarsCardFlag); +} + +bool GameStateManager::getScoringSawMarsKiosk() { + return _scoringFlags.getFlag(kScoringSawMarsKioskFlag); +} + +bool GameStateManager::getScoringSawTransportMap() { + return _scoringFlags.getFlag(kScoringSawTransportMapFlag); +} + +bool GameStateManager::getScoringGotCrowBar() { + return _scoringFlags.getFlag(kScoringGotCrowBarFlag); +} + +bool GameStateManager::getScoringTurnedOnTransport() { + return _scoringFlags.getFlag(kScoringTurnedOnTransportFlag); +} + +bool GameStateManager::getScoringGotOxygenMask() { + return _scoringFlags.getFlag(kScoringGotOxygenMaskFlag); +} + +bool GameStateManager::getScoringAvoidedRobot() { + return _scoringFlags.getFlag(kScoringAvoidedRobotFlag); +} + +bool GameStateManager::getScoringActivatedPlatform() { + return _scoringFlags.getFlag(kScoringActivatedPlatformFlag); +} + +bool GameStateManager::getScoringUsedLiquidNitrogen() { + return _scoringFlags.getFlag(kScoringUsedLiquidNitrogenFlag); +} + +bool GameStateManager::getScoringUsedCrowBar() { + return _scoringFlags.getFlag(kScoringUsedCrowBarFlag); +} + +bool GameStateManager::getScoringFoundCardBomb() { + return _scoringFlags.getFlag(kScoringFoundCardBombFlag); +} + +bool GameStateManager::getScoringDisarmedCardBomb() { + return _scoringFlags.getFlag(kScoringDisarmedCardBombFlag); +} + +bool GameStateManager::getScoringGotCardBomb() { + return _scoringFlags.getFlag(kScoringGotCardBombFlag); +} + +bool GameStateManager::getScoringThreadedMaze() { + return _scoringFlags.getFlag(kScoringThreadedMazeFlag); +} + +bool GameStateManager::getScoringThreadedGearRoom() { + return _scoringFlags.getFlag(kScoringThreadedGearRoomFlag); +} + +bool GameStateManager::getScoringEnteredShuttle() { + return _scoringFlags.getFlag(kScoringEnteredShuttleFlag); +} + +bool GameStateManager::getScoringEnteredLaunchTube() { + return _scoringFlags.getFlag(kScoringEnteredLaunchTubeFlag); +} + +bool GameStateManager::getScoringStoppedRobotsShuttle() { + return _scoringFlags.getFlag(kScoringStoppedRobotsShuttleFlag); +} + +bool GameStateManager::getScoringGotMarsOpMemChip() { + return _scoringFlags.getFlag(kScoringGotMarsOpMemChipFlag); +} + +bool GameStateManager::getScoringFinishedMars() { + return _scoringFlags.getFlag(kScoringFinishedMarsFlag); +} + +bool GameStateManager::getScoringSawSecurityMonitor() { + return _scoringFlags.getFlag(kScoringSawSecurityMonitorFlag); +} + +bool GameStateManager::getScoringFilledOxygenCanister() { + return _scoringFlags.getFlag(kScoringFilledOxygenCanisterFlag); +} + +bool GameStateManager::getScoringFilledArgonCanister() { + return _scoringFlags.getFlag(kScoringFilledArgonCanisterFlag); +} + +bool GameStateManager::getScoringSawUnconsciousOperator() { + return _scoringFlags.getFlag(kScoringSawUnconsciousOperatorFlag); +} + +bool GameStateManager::getScoringWentThroughPressureDoor() { + return _scoringFlags.getFlag(kScoringWentThroughPressureDoorFlag); +} + +bool GameStateManager::getScoringPreppedSub() { + return _scoringFlags.getFlag(kScoringPreppedSubFlag); +} + +bool GameStateManager::getScoringEnteredSub() { + return _scoringFlags.getFlag(kScoringEnteredSubFlag); +} + +bool GameStateManager::getScoringExitedSub() { + return _scoringFlags.getFlag(kScoringExitedSubFlag); +} + +bool GameStateManager::getScoringSawRobotAt54North() { + return _scoringFlags.getFlag(kScoringSawRobotAt54NorthFlag); +} + +bool GameStateManager::getScoringPlayedWithClaw() { + return _scoringFlags.getFlag(kScoringPlayedWithClawFlag); +} + +bool GameStateManager::getScoringUsedRetinalChip() { + return _scoringFlags.getFlag(kScoringUsedRetinalChipFlag); +} + +bool GameStateManager::getScoringFinishedGlobeGame() { + return _scoringFlags.getFlag(kScoringFinishedGlobeGameFlag); +} + +bool GameStateManager::getScoringStoppedNoradRobot() { + return _scoringFlags.getFlag(kScoringStoppedNoradRobotFlag); +} + +bool GameStateManager::getScoringGotNoradOpMemChip() { + return _scoringFlags.getFlag(kScoringGotNoradOpMemChipFlag); +} + +bool GameStateManager::getScoringFinishedNorad() { + return _scoringFlags.getFlag(kScoringFinishedNoradFlag); +} + +bool GameStateManager::getScoringRemovedDart() { + return _scoringFlags.getFlag(kScoringRemovedDartFlag); +} + +bool GameStateManager::getScoringAnalyzedDart() { + return _scoringFlags.getFlag(kScoringAnalyzedDartFlag); +} + +bool GameStateManager::getScoringBuiltAntidote() { + return _scoringFlags.getFlag(kScoringBuiltAntidoteFlag); +} + +bool GameStateManager::getScoringGotSinclairKey() { + return _scoringFlags.getFlag(kScoringGotSinclairKeyFlag); +} + +bool GameStateManager::getScoringGotArgonCanister() { + return _scoringFlags.getFlag(kScoringGotArgonCanisterFlag); +} + +bool GameStateManager::getScoringGotNitrogenCanister() { + return _scoringFlags.getFlag(kScoringGotNitrogenCanisterFlag); +} + +bool GameStateManager::getScoringPlayedWithMessages() { + return _scoringFlags.getFlag(kScoringPlayedWithMessagesFlag); +} + +bool GameStateManager::getScoringSawMorphExperiment() { + return _scoringFlags.getFlag(kScoringSawMorphExperimentFlag); +} + +bool GameStateManager::getScoringEnteredSinclairOffice() { + return _scoringFlags.getFlag(kScoringEnteredSinclairOfficeFlag); +} + +bool GameStateManager::getScoringSawBrochure() { + return _scoringFlags.getFlag(kScoringSawBrochureFlag); +} + +bool GameStateManager::getScoringSawSinclairEntry1() { + return _scoringFlags.getFlag(kScoringSawSinclairEntry1Flag); +} + +bool GameStateManager::getScoringSawSinclairEntry2() { + return _scoringFlags.getFlag(kScoringSawSinclairEntry2Flag); +} + +bool GameStateManager::getScoringSawSinclairEntry3() { + return _scoringFlags.getFlag(kScoringSawSinclairEntry3Flag); +} + +bool GameStateManager::getScoringSawWSCDirectory() { + return _scoringFlags.getFlag(kScoringSawWSCDirectoryFlag); +} + +bool GameStateManager::getScoringUsedCrowBarInWSC() { + return _scoringFlags.getFlag(kScoringUsedCrowBarInWSCFlag); +} + +bool GameStateManager::getScoringFinishedPlasmaDodge() { + return _scoringFlags.getFlag(kScoringFinishedPlasmaDodgeFlag); +} + +bool GameStateManager::getScoringOpenedCatwalk() { + return _scoringFlags.getFlag(kScoringOpenedCatwalkFlag); +} + +bool GameStateManager::getScoringStoppedWSCRobot() { + return _scoringFlags.getFlag(kScoringStoppedWSCRobotFlag); +} + +bool GameStateManager::getScoringGotWSCOpMemChip() { + return _scoringFlags.getFlag(kScoringGotWSCOpMemChipFlag); +} + +bool GameStateManager::getScoringFinishedWSC() { + return _scoringFlags.getFlag(kScoringFinishedWSCFlag); +} + +bool GameStateManager::getScoringMarsGandhi() { + return _scoringFlags.getFlag(kScoringMarsGandhiFlag); +} + +bool GameStateManager::getScoringNoradGandhi() { + return _scoringFlags.getFlag(kScoringNoradGandhiFlag); +} + +bool GameStateManager::getScoringWSCGandhi() { + return _scoringFlags.getFlag(kScoringWSCGandhiFlag); +} + +void GameStateManager::setWalkthroughMode(bool value) { + _globalFlags.setFlag(kGlobalWalkthroughFlag, value); +} + +bool GameStateManager::getWalkthroughMode() { + return _globalFlags.getFlag(kGlobalWalkthroughFlag); +} + +void GameStateManager::setShieldOn(bool value) { + _globalFlags.setFlag(kGlobalShieldOnFlag, value); +} + +bool GameStateManager::getShieldOn() { + return _globalFlags.getFlag(kGlobalShieldOnFlag); +} + +void GameStateManager::setEasterEgg(bool value) { + _globalFlags.setFlag(kGlobalEasterEggFlag, value); +} + +bool GameStateManager::getEasterEgg() { + return _globalFlags.getFlag(kGlobalEasterEggFlag); +} + +void GameStateManager::setBeenToWSC(bool value) { + _globalFlags.setFlag(kGlobalBeenToWSCFlag, value); +} + +bool GameStateManager::getBeenToWSC() { + return _globalFlags.getFlag(kGlobalBeenToWSCFlag); +} + +void GameStateManager::setBeenToMars(bool value) { + _globalFlags.setFlag(kGlobalBeenToMarsFlag, value); +} + +bool GameStateManager::getBeenToMars() { + return _globalFlags.getFlag(kGlobalBeenToMarsFlag); +} + +void GameStateManager::setBeenToNorad(bool value) { + _globalFlags.setFlag(kGlobalBeenToNoradFlag, value); +} + +bool GameStateManager::getBeenToNorad() { + return _globalFlags.getFlag(kGlobalBeenToNoradFlag); +} + +void GameStateManager::setWSCFinished(bool value) { + _globalFlags.setFlag(kGlobalWSCFinishedFlag, value); +} + +bool GameStateManager::getWSCFinished() { + return _globalFlags.getFlag(kGlobalWSCFinishedFlag); +} + +void GameStateManager::setMarsFinished(bool value) { + _globalFlags.setFlag(kGlobalMarsFinishedFlag, value); +} + +bool GameStateManager::getMarsFinished() { + return _globalFlags.getFlag(kGlobalMarsFinishedFlag); +} + +void GameStateManager::setNoradFinished(bool value) { + _globalFlags.setFlag(kGlobalNoradFinishedFlag, value); +} + +bool GameStateManager::getNoradFinished() { + return _globalFlags.getFlag(kGlobalNoradFinishedFlag); +} + +bool GameStateManager::allTimeZonesFinished() { + return getWSCFinished() && getMarsFinished() && getNoradFinished(); +} + +void GameStateManager::setTakenItemID(ItemID id, bool value) { + _itemTakenFlags.setFlag(id, value); +} + +bool GameStateManager::isTakenItemID(ItemID id) { + return _itemTakenFlags.getFlag(id); +} + +void GameStateManager::setTakenItem(Item *item, bool value) { + setTakenItemID(item->getObjectID(), value); +} + +bool GameStateManager::isTakenItem(Item *item) { + return isTakenItemID(item->getObjectID()); +} + +void GameStateManager::setCaldoriaFuseTimeLimit(const TimeValue timeLimit) { + _caldoriaFuseTimeLimit = timeLimit; +} + +TimeValue GameStateManager::getCaldoriaFuseTimeLimit() { + return _caldoriaFuseTimeLimit; +} + +void GameStateManager::setCaldoriaSeenPullback(bool value) { + _caldoriaFlags.setFlag(kCaldoriaSeenPullbackFlag, value); +} + +bool GameStateManager::getCaldoriaSeenPullback() { + return _caldoriaFlags.getFlag(kCaldoriaSeenPullbackFlag); +} + +void GameStateManager::setCaldoriaMadeOJ(bool value) { + _caldoriaFlags.setFlag(kCaldoriaMadeOJFlag, value); +} + +bool GameStateManager::getCaldoriaMadeOJ() { + return _caldoriaFlags.getFlag(kCaldoriaMadeOJFlag); +} + +void GameStateManager::setCaldoriaWokenUp(bool value) { + _caldoriaFlags.setFlag(kCaldoriaWokenUpFlag, value); +} + +bool GameStateManager::getCaldoriaWokenUp() { + return _caldoriaFlags.getFlag(kCaldoriaWokenUpFlag); +} + +void GameStateManager::setCaldoriaDidRecalibration(bool value) { + _caldoriaFlags.setFlag(kCaldoriaDidRecalibrationFlag, value); +} + +bool GameStateManager::getCaldoriaDidRecalibration() { + return _caldoriaFlags.getFlag(kCaldoriaDidRecalibrationFlag); +} + +void GameStateManager::setCaldoriaSeenSinclairInElevator(bool value) { + _caldoriaFlags.setFlag(kCaldoriaSeenSinclairInElevatorFlag, value); +} + +bool GameStateManager::getCaldoriaSeenSinclairInElevator() { + return _caldoriaFlags.getFlag(kCaldoriaSeenSinclairInElevatorFlag); +} + +void GameStateManager::setCaldoriaINNAnnouncing(bool value) { + _caldoriaFlags.setFlag(kCaldoriaINNAnnouncingFlag, value); +} + +bool GameStateManager::getCaldoriaINNAnnouncing() { + return _caldoriaFlags.getFlag(kCaldoriaINNAnnouncingFlag); +} + +void GameStateManager::setCaldoriaSeenINN(bool value) { + _caldoriaFlags.setFlag(kCaldoriaSeenINNFlag, value); +} + +bool GameStateManager::getCaldoriaSeenINN() { + return _caldoriaFlags.getFlag(kCaldoriaSeenINNFlag); +} + +void GameStateManager::setCaldoriaSeenMessages(bool value) { + _caldoriaFlags.setFlag(kCaldoriaSeenMessagesFlag, value); +} + +bool GameStateManager::getCaldoriaSeenMessages() { + return _caldoriaFlags.getFlag(kCaldoriaSeenMessagesFlag); +} + +void GameStateManager::setCaldoriaSinclairShot(bool value) { + _caldoriaFlags.setFlag(kCaldoriaSinclairShotFlag, value); +} + +bool GameStateManager::getCaldoriaSinclairShot() { + return _caldoriaFlags.getFlag(kCaldoriaSinclairShotFlag); +} + +void GameStateManager::setCaldoriaBombDisarmed(bool value) { + _caldoriaFlags.setFlag(kCaldoriaBombDisarmedFlag, value); +} + +bool GameStateManager::getCaldoriaBombDisarmed() { + return _caldoriaFlags.getFlag(kCaldoriaBombDisarmedFlag); +} + +void GameStateManager::setCaldoriaRoofDoorOpen(bool value) { + _caldoriaFlags.setFlag(kCaldoriaRoofDoorOpenFlag, value); +} + +bool GameStateManager::getCaldoriaRoofDoorOpen() { + return _caldoriaFlags.getFlag(kCaldoriaRoofDoorOpenFlag); +} + +void GameStateManager::setCaldoriaDoneHygiene(bool value) { + _caldoriaFlags.setFlag(kCaldoriaDoneHygieneFlag, value); +} + +bool GameStateManager::getCaldoriaDoneHygiene() { + return _caldoriaFlags.getFlag(kCaldoriaDoneHygieneFlag); +} + +void GameStateManager::setCaldoriaSawVoiceAnalysis(bool value) { + _caldoriaFlags.setFlag(kCaldoriaSawVoiceAnalysisFlag, value); +} + +bool GameStateManager::getCaldoriaSawVoiceAnalysis() { + return _caldoriaFlags.getFlag(kCaldoriaSawVoiceAnalysisFlag); +} + +void GameStateManager::setCaldoriaDoorBombed(bool value) { + _caldoriaFlags.setFlag(kCaldoriaDoorBombedFlag, value); +} + +bool GameStateManager::getCaldoriaDoorBombed() { + return _caldoriaFlags.getFlag(kCaldoriaDoorBombedFlag); +} + +void GameStateManager::setCaldoriaGunAimed(bool value) { + _caldoriaFlags.setFlag(kCaldoriaGunAimedFlag, value); +} + +bool GameStateManager::getCaldoriaGunAimed() { + return _caldoriaFlags.getFlag(kCaldoriaGunAimedFlag); +} + +void GameStateManager::setRipTimerTime(TimeValue limit) { + _TSARipTimerTime = limit; +} + +TimeValue GameStateManager::getRipTimerTime() { + return _TSARipTimerTime; +} + +void GameStateManager::setTSAFuseTimeLimit(TimeValue limit) { + _TSAFuseTimeLimit = limit; +} + +TimeValue GameStateManager::getTSAFuseTimeLimit() { + return _TSAFuseTimeLimit; +} + +void GameStateManager::setTSAState(byte state) { + _TSAState = state; +} + +byte GameStateManager::getTSAState() { + return _TSAState; +} + +void GameStateManager::setT0BMonitorMode(byte mode) { + _T0BMonitorMode = mode; +} + +byte GameStateManager::getT0BMonitorMode() { + return _T0BMonitorMode; +} + +void GameStateManager::setT0BMonitorStart(TimeValue start) { + _T0BMonitorStart = start; +} + +TimeValue GameStateManager::getT0BMonitorStart() { + return _T0BMonitorStart; +} + +void GameStateManager::setTSAIDedAtDoor(bool value) { + _TSAFlags.setFlag(kTSAIDedAtDoorFlag, value); +} + +bool GameStateManager::getTSAIDedAtDoor() { + return _TSAFlags.getFlag(kTSAIDedAtDoorFlag); +} + +void GameStateManager::setTSA0BZoomedIn(bool value) { + _TSAFlags.setFlag(kTSA0BZoomedInFlag, value); +} + +bool GameStateManager::getTSA0BZoomedIn() { + return _TSAFlags.getFlag(kTSA0BZoomedInFlag); +} + +void GameStateManager::setTSAFrontDoorUnlockedOutside(bool value) { + _TSAFlags.setFlag(kTSAFrontDoorUnlockedOutsideFlag, value); +} + +bool GameStateManager::getTSAFrontDoorUnlockedOutside() { + return _TSAFlags.getFlag(kTSAFrontDoorUnlockedOutsideFlag); +} + +void GameStateManager::setTSAFrontDoorUnlockedInside(bool value) { + _TSAFlags.setFlag(kTSAFrontDoorUnlockedInsideFlag, value); +} + +bool GameStateManager::getTSAFrontDoorUnlockedInside() { + return _TSAFlags.getFlag(kTSAFrontDoorUnlockedInsideFlag); +} + +void GameStateManager::setTSASeenRobotGreeting(bool value) { + _TSAFlags.setFlag(kTSASeenRobotGreetingFlag, value); +} + +bool GameStateManager::getTSASeenRobotGreeting() { + return _TSAFlags.getFlag(kTSASeenRobotGreetingFlag); +} + +void GameStateManager::setTSASeenTheory(bool value) { + _TSAFlags.setFlag(kTSASeenTheoryFlag, value); +} + +bool GameStateManager::getTSASeenTheory() { + return _TSAFlags.getFlag(kTSASeenTheoryFlag); +} + +void GameStateManager::setTSASeenBackground(bool value) { + _TSAFlags.setFlag(kTSASeenBackgroundFlag, value); +} + +bool GameStateManager::getTSASeenBackground() { + return _TSAFlags.getFlag(kTSASeenBackgroundFlag); +} + +void GameStateManager::setTSASeenProcedure(bool value) { + _TSAFlags.setFlag(kTSASeenProcedureFlag, value); +} + +bool GameStateManager::getTSASeenProcedure() { + return _TSAFlags.getFlag(kTSASeenProcedureFlag); +} + +void GameStateManager::setTSASeenAgent3AtDoor(bool value) { + _TSAFlags.setFlag(kTSASeenAgent3AtDoorFlag, value); +} + +bool GameStateManager::getTSASeenAgent3AtDoor() { + return _TSAFlags.getFlag(kTSASeenAgent3AtDoorFlag); +} + +void GameStateManager::setTSACommandCenterLocked(bool value) { + _TSAFlags.setFlag(kTSACommandCenterLockedFlag, value); +} + +bool GameStateManager::getTSACommandCenterLocked() { + return _TSAFlags.getFlag(kTSACommandCenterLockedFlag); +} + +void GameStateManager::setTSASeenCaldoriaNormal(bool value) { + _TSAFlags.setFlag(kTSASeenCaldoriaNormalFlag, value); +} + +bool GameStateManager::getTSASeenCaldoriaNormal() { + return _TSAFlags.getFlag(kTSASeenCaldoriaNormalFlag); +} + +void GameStateManager::setTSASeenCaldoriaAltered(bool value) { + _TSAFlags.setFlag(kTSASeenCaldoriaAlteredFlag, value); +} + +bool GameStateManager::getTSASeenCaldoriaAltered() { + return _TSAFlags.getFlag(kTSASeenCaldoriaAlteredFlag); +} + +void GameStateManager::setTSASeenNoradNormal(bool value) { + _TSAFlags.setFlag(kTSASeenNoradNormalFlag, value); +} + +bool GameStateManager::getTSASeenNoradNormal() { + return _TSAFlags.getFlag(kTSASeenNoradNormalFlag); +} + +void GameStateManager::setTSASeenNoradAltered(bool value) { + _TSAFlags.setFlag(kTSASeenNoradAlteredFlag, value); +} + +bool GameStateManager::getTSASeenNoradAltered() { + return _TSAFlags.getFlag(kTSASeenNoradAlteredFlag); +} + +void GameStateManager::setTSASeenMarsNormal(bool value) { + _TSAFlags.setFlag(kTSASeenMarsNormalFlag, value); +} + +bool GameStateManager::getTSASeenMarsNormal() { + return _TSAFlags.getFlag(kTSASeenMarsNormalFlag); +} + +void GameStateManager::setTSASeenMarsAltered(bool value) { + _TSAFlags.setFlag(kTSASeenMarsAlteredFlag, value); +} + +bool GameStateManager::getTSASeenMarsAltered() { + return _TSAFlags.getFlag(kTSASeenMarsAlteredFlag); +} + +void GameStateManager::setTSASeenWSCNormal(bool value) { + _TSAFlags.setFlag(kTSASeenWSCNormalFlag, value); +} + +bool GameStateManager::getTSASeenWSCNormal() { + return _TSAFlags.getFlag(kTSASeenWSCNormalFlag); +} + +void GameStateManager::setTSASeenWSCAltered(bool value) { + _TSAFlags.setFlag(kTSASeenWSCAlteredFlag, value); +} + +bool GameStateManager::getTSASeenWSCAltered() { + return _TSAFlags.getFlag(kTSASeenWSCAlteredFlag); +} + +void GameStateManager::setTSABiosuitOn(bool value) { + _TSAFlags.setFlag(kTSABiosuitOnFlag, value); +} + +bool GameStateManager::getTSABiosuitOn() { + return _TSAFlags.getFlag(kTSABiosuitOnFlag); +} + +void GameStateManager::setPrehistoricTriedToExtendBridge(bool value) { + _prehistoricFlags.setFlag(kPrehistoricTriedToExtendBridgeFlag, value); +} + +bool GameStateManager::getPrehistoricTriedToExtendBridge() { + return _prehistoricFlags.getFlag(kPrehistoricTriedToExtendBridgeFlag); +} + +void GameStateManager::setPrehistoricSeenTimeStream(bool value) { + _prehistoricFlags.setFlag(kPrehistoricSeenTimeStreamFlag, value); +} + +bool GameStateManager::getPrehistoricSeenTimeStream() { + return _prehistoricFlags.getFlag(kPrehistoricSeenTimeStreamFlag); +} + +void GameStateManager::setPrehistoricSeenFlyer1(bool value) { + _prehistoricFlags.setFlag(kPrehistoricSeenFlyer1Flag, value); +} + +bool GameStateManager::getPrehistoricSeenFlyer1() { + return _prehistoricFlags.getFlag(kPrehistoricSeenFlyer1Flag); +} + +void GameStateManager::setPrehistoricSeenFlyer2(bool value) { + _prehistoricFlags.setFlag(kPrehistoricSeenFlyer2Flag, value); +} + +bool GameStateManager::getPrehistoricSeenFlyer2() { + return _prehistoricFlags.getFlag(kPrehistoricSeenFlyer2Flag); +} + +void GameStateManager::setPrehistoricSeenBridgeZoom(bool value) { + _prehistoricFlags.setFlag(kPrehistoricSeenBridgeZoomFlag, value); +} + +bool GameStateManager::getPrehistoricSeenBridgeZoom() { + return _prehistoricFlags.getFlag(kPrehistoricSeenBridgeZoomFlag); +} + +void GameStateManager::setPrehistoricBreakerThrown(bool value) { + _prehistoricFlags.setFlag(kPrehistoricBreakerThrownFlag, value); +} + +bool GameStateManager::getPrehistoricBreakerThrown() { + return _prehistoricFlags.getFlag(kPrehistoricBreakerThrownFlag); +} + +void GameStateManager::setNoradSeenTimeStream(bool value) { + _noradFlags.setFlag(kNoradSeenTimeStreamFlag, value); +} + +bool GameStateManager::getNoradSeenTimeStream() { + return _noradFlags.getFlag(kNoradSeenTimeStreamFlag); +} + +void GameStateManager::setNoradGassed(bool value) { + _noradFlags.setFlag(kNoradGassedFlag, value); +} + +bool GameStateManager::getNoradGassed() { + return _noradFlags.getFlag(kNoradGassedFlag); +} + +void GameStateManager::setNoradFillingStationOn(bool value) { + _noradFlags.setFlag(kNoradFillingStationOnFlag, value); +} + +bool GameStateManager::getNoradFillingStationOn() { + return _noradFlags.getFlag(kNoradFillingStationOnFlag); +} + +void GameStateManager::setNoradN22MessagePlayed(bool value) { + _noradFlags.setFlag(kNoradN22MessagePlayedFlag, value); +} + +bool GameStateManager::getNoradN22MessagePlayed() { + return _noradFlags.getFlag(kNoradN22MessagePlayedFlag); +} + +void GameStateManager::setNoradPlayedGlobeGame(bool value) { + _noradFlags.setFlag(kNoradPlayedGlobeGameFlag, value); +} + +bool GameStateManager::getNoradPlayedGlobeGame() { + return _noradFlags.getFlag(kNoradPlayedGlobeGameFlag); +} + +void GameStateManager::setNoradBeatRobotWithClaw(bool value) { + _noradFlags.setFlag(kNoradBeatRobotWithClawFlag, value); +} + +bool GameStateManager::getNoradBeatRobotWithClaw() { + return _noradFlags.getFlag(kNoradBeatRobotWithClawFlag); +} + +void GameStateManager::setNoradBeatRobotWithDoor(bool value) { + _noradFlags.setFlag(kNoradBeatRobotWithDoorFlag, value); +} + +bool GameStateManager::getNoradBeatRobotWithDoor() { + return _noradFlags.getFlag(kNoradBeatRobotWithDoorFlag); +} + +void GameStateManager::setNoradRetScanGood(bool value) { + _noradFlags.setFlag(kNoradRetScanGoodFlag, value); +} + +bool GameStateManager::getNoradRetScanGood() { + return _noradFlags.getFlag(kNoradRetScanGoodFlag); +} + +void GameStateManager::setNoradWaitingForLaser(bool value) { + _noradFlags.setFlag(kNoradWaitingForLaserFlag, value); +} + +bool GameStateManager::getNoradWaitingForLaser() { + return _noradFlags.getFlag(kNoradWaitingForLaserFlag); +} + +void GameStateManager::setNoradSubRoomPressure(uint16 pressure) { + _noradSubRoomPressure = pressure; +} + +uint16 GameStateManager::getNoradSubRoomPressure() { + return _noradSubRoomPressure; +} + +void GameStateManager::setNoradSubPrepState(NoradSubPrepState state) { + _noradSubPrepState = state; +} + +NoradSubPrepState GameStateManager::getNoradSubPrepState() { + return _noradSubPrepState; +} + +void GameStateManager::setNoradArrivedFromSub(bool value) { + _noradFlags.setFlag(kNoradArrivedFromSubFlag, value); +} + +bool GameStateManager::getNoradArrivedFromSub() { + return _noradFlags.getFlag(kNoradArrivedFromSubFlag); +} + +void GameStateManager::setMarsSeenTimeStream(bool value) { + _marsFlags.setFlag(kMarsSeenTimeStreamFlag, value); +} + +bool GameStateManager::getMarsSeenTimeStream() { + return _marsFlags.getFlag(kMarsSeenTimeStreamFlag); +} + +void GameStateManager::setMarsHeardUpperPodMessage(bool value) { + _marsFlags.setFlag(kMarsHeardUpperPodMessageFlag, value); +} + +bool GameStateManager::getMarsHeardUpperPodMessage() { + return _marsFlags.getFlag(kMarsHeardUpperPodMessageFlag); +} + +void GameStateManager::setMarsRobotThrownPlayer(bool value) { + _marsFlags.setFlag(kMarsRobotThrownPlayerFlag, value); +} + +bool GameStateManager::getMarsRobotThrownPlayer() { + return _marsFlags.getFlag(kMarsRobotThrownPlayerFlag); +} + +void GameStateManager::setMarsHeardCheckInMessage(bool value) { + _marsFlags.setFlag(kMarsHeardCheckInMessageFlag, value); +} + +bool GameStateManager::getMarsHeardCheckInMessage() { + return _marsFlags.getFlag(kMarsHeardCheckInMessageFlag); +} + +void GameStateManager::setMarsPodAtUpperPlatform(bool value) { + _marsFlags.setFlag(kMarsPodAtUpperPlatformFlag, value); +} + +bool GameStateManager::getMarsPodAtUpperPlatform() { + return _marsFlags.getFlag(kMarsPodAtUpperPlatformFlag); +} + +void GameStateManager::setMarsSeenThermalScan(bool value) { + _marsFlags.setFlag(kMarsSeenThermalScanFlag, value); +} + +bool GameStateManager::getMarsSeenThermalScan() { + return _marsFlags.getFlag(kMarsSeenThermalScanFlag); +} + +void GameStateManager::setMarsArrivedBelow(bool value) { + _marsFlags.setFlag(kMarsArrivedBelowFlag, value); +} + +bool GameStateManager::getMarsArrivedBelow() { + return _marsFlags.getFlag(kMarsArrivedBelowFlag); +} + +void GameStateManager::setMarsSeenRobotAtReactor(bool value) { + _marsFlags.setFlag(kMarsSeenRobotAtReactorFlag, value); +} + +bool GameStateManager::getMarsSeenRobotAtReactor() { + return _marsFlags.getFlag(kMarsSeenRobotAtReactorFlag); +} + +void GameStateManager::setMarsAvoidedReactorRobot(bool value) { + _marsFlags.setFlag(kMarsAvoidedReactorRobotFlag, value); +} + +bool GameStateManager::getMarsAvoidedReactorRobot() { + return _marsFlags.getFlag(kMarsAvoidedReactorRobotFlag); +} + +void GameStateManager::setMarsSecurityDown(bool value) { + _marsFlags.setFlag(kMarsSecurityDownFlag, value); +} + +bool GameStateManager::getMarsSecurityDown() { + return _marsFlags.getFlag(kMarsSecurityDownFlag); +} + +void GameStateManager::setMarsInAirlock(bool value) { + _marsFlags.setFlag(kMarsInAirlockFlag, value); +} + +bool GameStateManager::getMarsInAirlock() { + return _marsFlags.getFlag(kMarsInAirlockFlag); +} + +void GameStateManager::setMarsAirlockOpen(bool value) { + _marsFlags.setFlag(kMarsAirlockOpenFlag, value); +} + +bool GameStateManager::getMarsAirlockOpen() { + return _marsFlags.getFlag(kMarsAirlockOpenFlag); +} + +void GameStateManager::setMarsMaskOnFiller(bool value) { + _marsFlags.setFlag(kMarsMaskOnFillerFlag, value); +} + +bool GameStateManager::getMarsMaskOnFiller() { + return _marsFlags.getFlag(kMarsMaskOnFillerFlag); +} + +void GameStateManager::setMarsLockFrozen(bool value) { + _marsFlags.setFlag(kMarsLockFrozenFlag, value); +} + +bool GameStateManager::getMarsLockFrozen() { + return _marsFlags.getFlag(kMarsLockFrozenFlag); +} + +void GameStateManager::setMarsLockBroken(bool value) { + _marsFlags.setFlag(kMarsLockBrokenFlag, value); +} + +bool GameStateManager::getMarsLockBroken() { + return _marsFlags.getFlag(kMarsLockBrokenFlag); +} + +void GameStateManager::setMarsMazeDoorPair1(bool value) { + _marsFlags.setFlag(kMarsMazeDoorPair1Flag, value); +} + +bool GameStateManager::getMarsMazeDoorPair1() { + return _marsFlags.getFlag(kMarsMazeDoorPair1Flag); +} + +void GameStateManager::setMarsMazeDoorPair2(bool value) { + _marsFlags.setFlag(kMarsMazeDoorPair2Flag, value); +} + +bool GameStateManager::getMarsMazeDoorPair2() { + return _marsFlags.getFlag(kMarsMazeDoorPair2Flag); +} + +void GameStateManager::setMarsMazeDoorPair3(bool value) { + _marsFlags.setFlag(kMarsMazeDoorPair3Flag, value); +} + +bool GameStateManager::getMarsMazeDoorPair3() { + return _marsFlags.getFlag(kMarsMazeDoorPair3Flag); +} + +void GameStateManager::setMarsSawRobotLeave(bool value) { + _marsFlags.setFlag(kMarsSawRobotLeaveFlag, value); +} + +bool GameStateManager::getMarsSawRobotLeave() { + return _marsFlags.getFlag(kMarsSawRobotLeaveFlag); +} + +void GameStateManager::setMarsHitRobotWithCannon(bool flag) { + _marsFlags.setFlag(kMarsHitRobotWithCannonFlag, flag); +} + +bool GameStateManager::getMarsHitRobotWithCannon() { + return _marsFlags.getFlag(kMarsHitRobotWithCannonFlag); +} + +void GameStateManager::setMarsReadyForShuttleTransport(bool value) { + _marsFlags.setFlag(kMarsReadyForShuttleTransportFlag, value); +} + +bool GameStateManager::getMarsReadyForShuttleTransport() { + return _marsFlags.getFlag(kMarsReadyForShuttleTransportFlag); +} + +void GameStateManager::setMarsFinishedCanyonChase(bool flag) { + _marsFlags.setFlag(kMarsFinishedCanyonChaseFlag, flag); +} + +bool GameStateManager::getMarsFinishedCanyonChase() { + return _marsFlags.getFlag(kMarsFinishedCanyonChaseFlag); +} + +void GameStateManager::setMarsThreadedMaze(bool flag) { + _marsFlags.setFlag(kMarsThreadedMazeFlag, flag); +} + +bool GameStateManager::getMarsThreadedMaze() { + return _marsFlags.getFlag(kMarsThreadedMazeFlag); +} + +void GameStateManager::setWSCSeenTimeStream(bool value) { + _WSCFlags.setFlag(kWSCSeenTimeStreamFlag, value); +} + +bool GameStateManager::getWSCSeenTimeStream() { + return _WSCFlags.getFlag(kWSCSeenTimeStreamFlag); +} + +void GameStateManager::setWSCPoisoned(bool value) { + _WSCFlags.setFlag(kWSCPoisonedFlag, value); +} + +bool GameStateManager::getWSCPoisoned() { + return _WSCFlags.getFlag(kWSCPoisonedFlag); +} + +void GameStateManager::setWSCAnsweredAboutDart(bool value) { + _WSCFlags.setFlag(kWSCAnsweredAboutDartFlag, value); +} + +bool GameStateManager::getWSCAnsweredAboutDart() { + return _WSCFlags.getFlag(kWSCAnsweredAboutDartFlag); +} + +void GameStateManager::setWSCRemovedDart(bool value) { + _WSCFlags.setFlag(kWSCRemovedDartFlag, value); +} + +bool GameStateManager::getWSCRemovedDart() { + return _WSCFlags.getFlag(kWSCRemovedDartFlag); +} + +void GameStateManager::setWSCAnalyzerOn(bool value) { + _WSCFlags.setFlag(kWSCAnalyzerOnFlag, value); +} + +bool GameStateManager::getWSCAnalyzerOn() { + return _WSCFlags.getFlag(kWSCAnalyzerOnFlag); +} + +void GameStateManager::setWSCDartInAnalyzer(bool value) { + _WSCFlags.setFlag(kWSCDartInAnalyzerFlag, value); +} + +bool GameStateManager::getWSCDartInAnalyzer() { + return _WSCFlags.getFlag(kWSCDartInAnalyzerFlag); +} + +void GameStateManager::setWSCAnalyzedDart(bool value) { + _WSCFlags.setFlag(kWSCAnalyzedDartFlag, value); +} + +bool GameStateManager::getWSCAnalyzedDart() { + return _WSCFlags.getFlag(kWSCAnalyzedDartFlag); +} + +void GameStateManager::setWSCSawMorph(bool value) { + _WSCFlags.setFlag(kWSCSawMorphFlag, value); +} + +bool GameStateManager::getWSCSawMorph() { + return _WSCFlags.getFlag(kWSCSawMorphFlag); +} + +void GameStateManager::setWSCDesignedAntidote(bool value) { + _WSCFlags.setFlag(kWSCDesignedAntidoteFlag, value); +} + +bool GameStateManager::getWSCDesignedAntidote() { + return _WSCFlags.getFlag(kWSCDesignedAntidoteFlag); +} + +void GameStateManager::setWSCPickedUpAntidote(bool value) { + _WSCFlags.setFlag(kWSCPickedUpAntidoteFlag, value); +} + +bool GameStateManager::getWSCPickedUpAntidote() { + return _WSCFlags.getFlag(kWSCPickedUpAntidoteFlag); +} + +void GameStateManager::setWSCOfficeMessagesOpen(bool value) { + _WSCFlags.setFlag(kWSCOfficeMessagesOpenFlag, value); +} + +bool GameStateManager::getWSCOfficeMessagesOpen() { + return _WSCFlags.getFlag(kWSCOfficeMessagesOpenFlag); +} + +void GameStateManager::setWSCSeenNerd(bool value) { + _WSCFlags.setFlag(kWSCSeenNerdFlag, value); +} + +bool GameStateManager::getWSCSeenNerd() { + return _WSCFlags.getFlag(kWSCSeenNerdFlag); +} + +void GameStateManager::setWSCHeardPage1(bool value) { + _WSCFlags.setFlag(kWSCHeardPage1Flag, value); +} + +bool GameStateManager::getWSCHeardPage1() { + return _WSCFlags.getFlag(kWSCHeardPage1Flag); +} + +void GameStateManager::setWSCHeardPage2(bool value) { + _WSCFlags.setFlag(kWSCHeardPage2Flag, value); +} + +bool GameStateManager::getWSCHeardPage2() { + return _WSCFlags.getFlag(kWSCHeardPage2Flag); +} + +void GameStateManager::setWSCHeardCheckIn(bool value) { + _WSCFlags.setFlag(kWSCHeardCheckInFlag, value); +} + +bool GameStateManager::getWSCHeardCheckIn() { + return _WSCFlags.getFlag(kWSCHeardCheckInFlag); +} + +void GameStateManager::setWSCDidPlasmaDodge(bool value) { + _WSCFlags.setFlag(kWSCDidPlasmaDodgeFlag, value); +} + +bool GameStateManager::getWSCDidPlasmaDodge() { + return _WSCFlags.getFlag(kWSCDidPlasmaDodgeFlag); +} + +void GameStateManager::setWSCSeenSinclairLecture(bool value) { + _WSCFlags.setFlag(kWSCSeenSinclairLectureFlag, value); +} + +bool GameStateManager::getWSCSeenSinclairLecture() { + return _WSCFlags.getFlag(kWSCSeenSinclairLectureFlag); +} + +void GameStateManager::setWSCBeenAtWSC93(bool value) { + _WSCFlags.setFlag(kWSCBeenAtWSC93Flag, value); +} + +bool GameStateManager::getWSCBeenAtWSC93() { + return _WSCFlags.getFlag(kWSCBeenAtWSC93Flag); +} + +void GameStateManager::setWSCCatwalkDark(bool value) { + _WSCFlags.setFlag(kWSCCatwalkDarkFlag, value); +} + +bool GameStateManager::getWSCCatwalkDark() { + return _WSCFlags.getFlag(kWSCCatwalkDarkFlag); +} + +void GameStateManager::setWSCRobotDead(bool value) { + _WSCFlags.setFlag(kWSCRobotDeadFlag, value); +} + +bool GameStateManager::getWSCRobotDead() { + return _WSCFlags.getFlag(kWSCRobotDeadFlag); +} + +void GameStateManager::setWSCRobotGone(bool value) { + _WSCFlags.setFlag(kWSCRobotGoneFlag, value); +} + +bool GameStateManager::getWSCRobotGone() { + return _WSCFlags.getFlag(kWSCRobotGoneFlag); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/gamestate.h b/engines/pegasus/gamestate.h new file mode 100644 index 0000000000..dd47bd6e51 --- /dev/null +++ b/engines/pegasus/gamestate.h @@ -0,0 +1,886 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_GAMESTATE_H +#define PEGASUS_GAMESTATE_H + +#include "common/singleton.h" +#include "common/util.h" + +#include "pegasus/types.h" +#include "pegasus/util.h" +#include "pegasus/items/item.h" + +namespace Common { + class Error; + class ReadStream; + class WriteStream; +} + +namespace Pegasus { + +// The only things saved in here are things which get written out to a saved game file... + +enum { + kGlobalWalkthroughFlag, + kGlobalShieldOnFlag, + kGlobalEasterEggFlag, + kGlobalBeenToWSCFlag, + kGlobalBeenToMarsFlag, + kGlobalBeenToNoradFlag, + kGlobalWSCFinishedFlag, + kGlobalMarsFinishedFlag, + kGlobalNoradFinishedFlag, + kNumGlobalFlags +}; + +enum { + kScoringSawINNFlag, + kScoringTookShowerFlag, + kScoringFixedHairFlag, + kScoringGotKeyCardFlag, + kScoringReadPaperFlag, + kScoringLookThroughTelescopeFlag, + kScoringSawCaldoriaKioskFlag, + kScoringGoToTSAFlag, + kScoringEnterTSAFlag, + kScoringSawBust1Flag, + kScoringSawBust2Flag, + kScoringSawBust3Flag, + kScoringSawBust4Flag, + kScoringSawBust5Flag, + kScoringSawBust6Flag, + kScoringSawTheoryFlag, + kScoringSawBackgroundFlag, + kScoringSawProcedureFlag, + kScoringGotJourneymanKeyFlag, + kScoringGotPegasusBiochipFlag, + kScoringGotBiosuitFlag, + kScoringGoToPrehistoricFlag, + kScoringPutLogInReaderFlag, + kScoringSawCaldoriaNormalFlag, + kScoringSawCaldoriaAlteredFlag, + kScoringSawNoradNormalFlag, + kScoringSawNoradAlteredFlag, + kScoringSawMarsNormalFlag, + kScoringSawMarsAlteredFlag, + kScoringSawWSCNormalFlag, + kScoringSawWSCAlteredFlag, + kScoringWentToReadyRoom2Flag, + kScoringWentAfterSinclairFlag, + kScoringUsedCardBombFlag, + kScoringShieldedCardBombFlag, + kScoringStunnedSinclairFlag, + kScoringDisarmedNukeFlag, + + kScoringThrewBreakerFlag, + kScoringExtendedBridgeFlag, + kScoringGotHistoricalLogFlag, + kScoringFinishedPrehistoricFlag, + + kScoringThrownByRobotFlag, + kScoringGotMarsCardFlag, + kScoringSawMarsKioskFlag, + kScoringSawTransportMapFlag, + kScoringGotCrowBarFlag, + kScoringTurnedOnTransportFlag, + kScoringGotOxygenMaskFlag, + kScoringAvoidedRobotFlag, + kScoringActivatedPlatformFlag, + kScoringUsedLiquidNitrogenFlag, + kScoringUsedCrowBarFlag, + kScoringFoundCardBombFlag, + kScoringDisarmedCardBombFlag, + kScoringGotCardBombFlag, + kScoringThreadedMazeFlag, + kScoringThreadedGearRoomFlag, + kScoringEnteredShuttleFlag, + kScoringEnteredLaunchTubeFlag, + kScoringStoppedRobotsShuttleFlag, + kScoringGotMarsOpMemChipFlag, + kScoringFinishedMarsFlag, + + kScoringSawSecurityMonitorFlag, + kScoringFilledOxygenCanisterFlag, + kScoringFilledArgonCanisterFlag, + kScoringSawUnconsciousOperatorFlag, + kScoringWentThroughPressureDoorFlag, + kScoringPreppedSubFlag, + kScoringEnteredSubFlag, + kScoringExitedSubFlag, + kScoringSawRobotAt54NorthFlag, + kScoringPlayedWithClawFlag, + kScoringUsedRetinalChipFlag, + kScoringFinishedGlobeGameFlag, + kScoringStoppedNoradRobotFlag, + kScoringGotNoradOpMemChipFlag, + kScoringFinishedNoradFlag, + + kScoringRemovedDartFlag, + kScoringAnalyzedDartFlag, + kScoringBuiltAntidoteFlag, + kScoringGotSinclairKeyFlag, + kScoringGotArgonCanisterFlag, + kScoringGotNitrogenCanisterFlag, + kScoringPlayedWithMessagesFlag, + kScoringSawMorphExperimentFlag, + kScoringEnteredSinclairOfficeFlag, + kScoringSawBrochureFlag, + kScoringSawSinclairEntry1Flag, + kScoringSawSinclairEntry2Flag, + kScoringSawSinclairEntry3Flag, + kScoringSawWSCDirectoryFlag, + kScoringUsedCrowBarInWSCFlag, + kScoringFinishedPlasmaDodgeFlag, + kScoringOpenedCatwalkFlag, + kScoringStoppedWSCRobotFlag, + kScoringGotWSCOpMemChipFlag, + kScoringFinishedWSCFlag, + + kScoringMarsGandhiFlag, + kScoringNoradGandhiFlag, + kScoringWSCGandhiFlag, + + kNumScoringFlags +}; + +enum { + kCaldoriaSeenPullbackFlag, + kCaldoriaMadeOJFlag, + kCaldoriaWokenUpFlag, + kCaldoriaDidRecalibrationFlag, + kCaldoriaSeenSinclairInElevatorFlag, + kCaldoriaINNAnnouncingFlag, + kCaldoriaSeenINNFlag, + kCaldoriaSeenMessagesFlag, + kCaldoriaSinclairShotFlag, + kCaldoriaBombDisarmedFlag, + kCaldoriaRoofDoorOpenFlag, + kCaldoriaDoneHygieneFlag, + kCaldoriaSawVoiceAnalysisFlag, + kCaldoriaDoorBombedFlag, + kCaldoriaGunAimedFlag, + kNumCaldoriaFlags +}; + +enum { + kCaldoriaNoFuseRunning, + kCaldoriaDoorBombFuseRunning, + kCaldoriaSinclairFuseRunning +}; + +enum { + kTSAIDedAtDoorFlag, + kTSA0BZoomedInFlag, + kTSAFrontDoorUnlockedOutsideFlag, + kTSAFrontDoorUnlockedInsideFlag, + kTSASeenRobotGreetingFlag, + kTSASeenTheoryFlag, + kTSASeenBackgroundFlag, + kTSASeenProcedureFlag, + kTSASeenAgent3AtDoorFlag, + kTSACommandCenterLockedFlag, + kTSASeenCaldoriaNormalFlag, + kTSASeenCaldoriaAlteredFlag, + kTSASeenNoradNormalFlag, + kTSASeenNoradAlteredFlag, + kTSASeenMarsNormalFlag, + kTSASeenMarsAlteredFlag, + kTSASeenWSCNormalFlag, + kTSASeenWSCAlteredFlag, + kTSABiosuitOnFlag, + kNumTSAFlags +}; + +enum { + kPrehistoricTriedToExtendBridgeFlag, + kPrehistoricSeenTimeStreamFlag, + kPrehistoricSeenFlyer1Flag, + kPrehistoricSeenFlyer2Flag, + kPrehistoricSeenBridgeZoomFlag, + kPrehistoricBreakerThrownFlag, + kNumPrehistoricFlags +}; + +enum { + kNoradSeenTimeStreamFlag, + kNoradGassedFlag, + kNoradFillingStationOnFlag, + kNoradN22MessagePlayedFlag, + kNoradArrivedFromSubFlag, + kNoradWaitingForLaserFlag, + kNoradRetScanGoodFlag, + kNoradPlayedGlobeGameFlag, + kNoradBeatRobotWithClawFlag, + kNoradBeatRobotWithDoorFlag, + kNumNoradFlags +}; + +enum { + kMarsSeenTimeStreamFlag, + kMarsHeardUpperPodMessageFlag, + kMarsRobotThrownPlayerFlag, + kMarsHeardCheckInMessageFlag, + kMarsPodAtUpperPlatformFlag, + kMarsSeenThermalScanFlag, + kMarsArrivedBelowFlag, + kMarsSeenRobotAtReactorFlag, + kMarsAvoidedReactorRobotFlag, + kMarsInAirlockFlag, + kMarsAirlockOpenFlag, + kMarsMaskOnFillerFlag, + kMarsLockFrozenFlag, + kMarsLockBrokenFlag, + kMarsMazeDoorPair1Flag, + kMarsMazeDoorPair2Flag, + kMarsMazeDoorPair3Flag, + kMarsSawRobotLeaveFlag, + kMarsSecurityDownFlag, + kMarsHitRobotWithCannonFlag, + kMarsReadyForShuttleTransportFlag, + kMarsFinishedCanyonChaseFlag, + kMarsThreadedMazeFlag, + kNumMarsFlags +}; + +enum { + kWSCSeenTimeStreamFlag, + kWSCPoisonedFlag, + kWSCAnsweredAboutDartFlag, + kWSCRemovedDartFlag, + kWSCAnalyzerOnFlag, + kWSCDartInAnalyzerFlag, + kWSCAnalyzedDartFlag, + kWSCSawMorphFlag, + kWSCDesignedAntidoteFlag, + kWSCPickedUpAntidoteFlag, + kWSCOfficeMessagesOpenFlag, + kWSCSeenNerdFlag, + kWSCHeardPage1Flag, + kWSCHeardPage2Flag, + kWSCHeardCheckInFlag, + kWSCDidPlasmaDodgeFlag, + kWSCSeenSinclairLectureFlag, + kWSCBeenAtWSC93Flag, + kWSCCatwalkDarkFlag, + kWSCRobotDeadFlag, + kWSCRobotGoneFlag, + kNumWSCFlags +}; + +class GameStateManager : public Common::Singleton<GameStateManager> { +public: + GameStateManager() { resetGameState(); } + + // Base game state + Common::Error writeGameState(Common::WriteStream *stream); + Common::Error readGameState(Common::ReadStream *stream); + + void resetGameState(); + + void getCurrentLocation(NeighborhoodID &neighborhood, RoomID &room, DirectionConstant &direction); + void setCurrentLocation(const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction); + + NeighborhoodID getCurrentNeighborhood(); + void setCurrentNeighborhood(const NeighborhoodID neighborhood); + RoomID getCurrentRoom(); + void setCurrentRoom(const RoomID room); + DirectionConstant getCurrentDirection(); + void setCurrentDirection(const DirectionConstant direction); + + RoomViewID getCurrentRoomAndView(); + + void getNextLocation(NeighborhoodID &neighborhood, RoomID &room, DirectionConstant &direction); + void setNextLocation(const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction); + + NeighborhoodID getNextNeighborhood(); + void setNextNeighborhood(const NeighborhoodID neighborhood); + RoomID getNextRoom(); + void setNextRoom(const RoomID room); + DirectionConstant getNextDirection(); + void setNextDirection(const DirectionConstant direction); + + void getLastLocation(NeighborhoodID &neighborhood, RoomID &room, DirectionConstant &direction); + void setLastLocation(const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction); + + NeighborhoodID getLastNeighborhood(); + void setLastNeighborhood(const NeighborhoodID neighborhood); + RoomID getLastRoom(); + void setLastRoom(const RoomID room); + DirectionConstant getLastDirection(); + void setLastDirection(const DirectionConstant direction); + + RoomViewID getLastRoomAndView(); + + void getOpenDoorLocation(RoomID &room, DirectionConstant &direction); + void setOpenDoorLocation(const RoomID room, const DirectionConstant direction); + RoomID getOpenDoorRoom(); + void setOpenDoorRoom(const RoomID room); + DirectionConstant getOpenDoorDirection(); + void setOpenDoorDirection(const DirectionConstant direction); + + RoomViewID getDoorOpenRoomAndView(); + + bool isCurrentDoorOpen(); + + // Pegasus Prime + + // Scoring... + // Scoring "Set" functions. + // Caldoria/TSA scoring + void setScoringSawINN(const bool = true); + void setScoringTookShower(const bool = true); + void setScoringFixedHair(const bool = true); + void setScoringGotKeyCard(const bool = true); + void setScoringReadPaper(const bool = true); + void setScoringLookThroughTelescope(const bool = true); + void setScoringSawCaldoriaKiosk(const bool = true); + void setScoringGoToTSA(const bool = true); + void setScoringEnterTSA(const bool = true); + void setScoringSawBust1(const bool = true); + void setScoringSawBust2(const bool = true); + void setScoringSawBust3(const bool = true); + void setScoringSawBust4(const bool = true); + void setScoringSawBust5(const bool = true); + void setScoringSawBust6(const bool = true); + void setScoringSawTheory(const bool = true); + void setScoringSawBackground(const bool = true); + void setScoringSawProcedure(const bool = true); + void setScoringGotJourneymanKey(const bool = true); + void setScoringGotPegasusBiochip(const bool = true); + void setScoringGotBiosuit(const bool = true); + void setScoringGoToPrehistoric(const bool = true); + void setScoringPutLogInReader(const bool = true); + void setScoringSawCaldoriaNormal(const bool = true); + void setScoringSawCaldoriaAltered(const bool = true); + void setScoringSawNoradNormal(const bool = true); + void setScoringSawNoradAltered(const bool = true); + void setScoringSawMarsNormal(const bool = true); + void setScoringSawMarsAltered(const bool = true); + void setScoringSawWSCNormal(const bool = true); + void setScoringSawWSCAltered(const bool = true); + void setScoringWentToReadyRoom2(const bool = true); + void setScoringWentAfterSinclair(const bool = true); + void setScoringUsedCardBomb(const bool = true); + void setScoringShieldedCardBomb(const bool = true); + void setScoringStunnedSinclair(const bool = true); + void setScoringDisarmedNuke(const bool = true); + + // Prehistoric scoring + void setScoringThrewBreaker(const bool = true); + void setScoringExtendedBridge(const bool = true); + void setScoringGotHistoricalLog(const bool = true); + void setScoringFinishedPrehistoric(const bool = true); + + // Mars scoring + void setScoringThrownByRobot(const bool = true); + void setScoringGotMarsCard(const bool = true); + void setScoringSawMarsKiosk(const bool = true); + void setScoringSawTransportMap(const bool = true); + void setScoringGotCrowBar(const bool = true); + void setScoringTurnedOnTransport(const bool = true); + void setScoringGotOxygenMask(const bool = true); + void setScoringAvoidedRobot(const bool = true); + void setScoringActivatedPlatform(const bool = true); + void setScoringUsedLiquidNitrogen(const bool = true); + void setScoringUsedCrowBar(const bool = true); + void setScoringFoundCardBomb(const bool = true); + void setScoringDisarmedCardBomb(const bool = true); + void setScoringGotCardBomb(const bool = true); + void setScoringThreadedMaze(const bool = true); + void setScoringThreadedGearRoom(const bool = true); + void setScoringEnteredShuttle(const bool = true); + void setScoringEnteredLaunchTube(const bool = true); + void setScoringStoppedRobotsShuttle(const bool = true); + void setScoringGotMarsOpMemChip(const bool = true); + void setScoringFinishedMars(const bool = true); + + // Norad scoring + void setScoringSawSecurityMonitor(const bool = true); + void setScoringFilledOxygenCanister(const bool = true); + void setScoringFilledArgonCanister(const bool = true); + void setScoringSawUnconsciousOperator(const bool = true); + void setScoringWentThroughPressureDoor(const bool = true); + void setScoringPreppedSub(const bool = true); + void setScoringEnteredSub(const bool = true); + void setScoringExitedSub(const bool = true); + void setScoringSawRobotAt54North(const bool = true); + void setScoringPlayedWithClaw(const bool = true); + void setScoringUsedRetinalChip(const bool = true); + void setScoringFinishedGlobeGame(const bool = true); + void setScoringStoppedNoradRobot(const bool = true); + void setScoringGotNoradOpMemChip(const bool = true); + void setScoringFinishedNorad(const bool = true); + + // WSC scoring + void setScoringRemovedDart(const bool = true); + void setScoringAnalyzedDart(const bool = true); + void setScoringBuiltAntidote(const bool = true); + void setScoringGotSinclairKey(const bool = true); + void setScoringGotArgonCanister(const bool = true); + void setScoringGotNitrogenCanister(const bool = true); + void setScoringPlayedWithMessages(const bool = true); + void setScoringSawMorphExperiment(const bool = true); + void setScoringEnteredSinclairOffice(const bool = true); + void setScoringSawBrochure(const bool = true); + void setScoringSawSinclairEntry1(const bool = true); + void setScoringSawSinclairEntry2(const bool = true); + void setScoringSawSinclairEntry3(const bool = true); + void setScoringSawWSCDirectory(const bool = true); + void setScoringUsedCrowBarInWSC(const bool = true); + void setScoringFinishedPlasmaDodge(const bool = true); + void setScoringOpenedCatwalk(const bool = true); + void setScoringStoppedWSCRobot(const bool = true); + void setScoringGotWSCOpMemChip(const bool = true); + void setScoringFinishedWSC(const bool = true); + + // Gandhi scoring + void setScoringMarsGandhi(const bool = true); + void setScoringNoradGandhi(const bool = true); + void setScoringWSCGandhi(const bool = true); + + // Scoring "Get" functions. + bool getScoringSawINN(); + bool getScoringTookShower(); + bool getScoringFixedHair(); + bool getScoringGotKeyCard(); + bool getScoringReadPaper(); + bool getScoringLookThroughTelescope(); + bool getScoringSawCaldoriaKiosk(); + bool getScoringGoToTSA(); + bool getScoringEnterTSA(); + bool getScoringSawBust1(); + bool getScoringSawBust2(); + bool getScoringSawBust3(); + bool getScoringSawBust4(); + bool getScoringSawBust5(); + bool getScoringSawBust6(); + bool getScoringSawTheory(); + bool getScoringSawBackground(); + bool getScoringSawProcedure(); + bool getScoringGotJourneymanKey(); + bool getScoringGotPegasusBiochip(); + bool getScoringGotBiosuit(); + bool getScoringGoToPrehistoric(); + bool getScoringPutLogInReader(); + bool getScoringSawCaldoriaNormal(); + bool getScoringSawCaldoriaAltered(); + bool getScoringSawNoradNormal(); + bool getScoringSawNoradAltered(); + bool getScoringSawMarsNormal(); + bool getScoringSawMarsAltered(); + bool getScoringSawWSCNormal(); + bool getScoringSawWSCAltered(); + bool getScoringWentToReadyRoom2(); + bool getScoringWentAfterSinclair(); + bool getScoringUsedCardBomb(); + bool getScoringShieldedCardBomb(); + bool getScoringStunnedSinclair(); + bool getScoringDisarmedNuke(); + bool getScoringThrewBreaker(); + bool getScoringExtendedBridge(); + bool getScoringGotHistoricalLog(); + bool getScoringFinishedPrehistoric(); + bool getScoringThrownByRobot(); + bool getScoringGotMarsCard(); + bool getScoringSawMarsKiosk(); + bool getScoringSawTransportMap(); + bool getScoringGotCrowBar(); + bool getScoringTurnedOnTransport(); + bool getScoringGotOxygenMask(); + bool getScoringAvoidedRobot(); + bool getScoringActivatedPlatform(); + bool getScoringUsedLiquidNitrogen(); + bool getScoringUsedCrowBar(); + bool getScoringFoundCardBomb(); + bool getScoringDisarmedCardBomb(); + bool getScoringGotCardBomb(); + bool getScoringThreadedMaze(); + bool getScoringThreadedGearRoom(); + bool getScoringEnteredShuttle(); + bool getScoringEnteredLaunchTube(); + bool getScoringStoppedRobotsShuttle(); + bool getScoringGotMarsOpMemChip(); + bool getScoringFinishedMars(); + bool getScoringSawSecurityMonitor(); + bool getScoringFilledOxygenCanister(); + bool getScoringFilledArgonCanister(); + bool getScoringSawUnconsciousOperator(); + bool getScoringWentThroughPressureDoor(); + bool getScoringPreppedSub(); + bool getScoringEnteredSub(); + bool getScoringExitedSub(); + bool getScoringSawRobotAt54North(); + bool getScoringPlayedWithClaw(); + bool getScoringUsedRetinalChip(); + bool getScoringFinishedGlobeGame(); + bool getScoringStoppedNoradRobot(); + bool getScoringGotNoradOpMemChip(); + bool getScoringFinishedNorad(); + bool getScoringRemovedDart(); + bool getScoringAnalyzedDart(); + bool getScoringBuiltAntidote(); + bool getScoringGotSinclairKey(); + bool getScoringGotArgonCanister(); + bool getScoringGotNitrogenCanister(); + bool getScoringPlayedWithMessages(); + bool getScoringSawMorphExperiment(); + bool getScoringEnteredSinclairOffice(); + bool getScoringSawBrochure(); + bool getScoringSawSinclairEntry1(); + bool getScoringSawSinclairEntry2(); + bool getScoringSawSinclairEntry3(); + bool getScoringSawWSCDirectory(); + bool getScoringUsedCrowBarInWSC(); + bool getScoringFinishedPlasmaDodge(); + bool getScoringOpenedCatwalk(); + bool getScoringStoppedWSCRobot(); + bool getScoringGotWSCOpMemChip(); + bool getScoringFinishedWSC(); + bool getScoringMarsGandhi(); + bool getScoringNoradGandhi(); + bool getScoringWSCGandhi(); + + GameScoreType getCaldoriaTSAScore(); + GameScoreType getPrehistoricScore(); + GameScoreType getMarsScore(); + GameScoreType getNoradScore(); + GameScoreType getWSCScore(); + GameScoreType getGandhiScore(); + GameScoreType getTotalScore(); + + void writeCaldoriaState(Common::WriteStream *stream); + void readCaldoriaState(Common::ReadStream *stream); + void resetCaldoriaState(); + + void writeTSAState(Common::WriteStream *stream); + void readTSAState(Common::ReadStream *stream); + void resetTSAState(); + + void writePrehistoricState(Common::WriteStream *stream); + void readPrehistoricState(Common::ReadStream *stream); + void resetPrehistoricState(); + + void writeNoradState(Common::WriteStream *stream); + void readNoradState(Common::ReadStream *stream); + void resetNoradState(); + + void writeMarsState(Common::WriteStream *stream); + void readMarsState(Common::ReadStream *stream); + void resetMarsState(); + + void writeWSCState(Common::WriteStream *stream); + void readWSCState(Common::ReadStream *stream); + void resetWSCState(); + + // Globals. + void setWalkthroughMode(bool); + bool getWalkthroughMode(); + void setShieldOn(bool); + bool getShieldOn(); + void setEasterEgg(bool); + bool getEasterEgg(); + void setBeenToWSC(bool value); + bool getBeenToWSC(); + void setBeenToMars(bool value); + bool getBeenToMars(); + void setBeenToNorad(bool value); + bool getBeenToNorad(); + void setWSCFinished(bool); + bool getWSCFinished(); + void setMarsFinished(bool); + bool getMarsFinished(); + void setNoradFinished(bool); + bool getNoradFinished(); + bool allTimeZonesFinished(); + void setTakenItemID(ItemID, bool); + bool isTakenItemID(ItemID); + void setTakenItem(Item *, bool); + bool isTakenItem(Item *); + + // Caldoria + void setCaldoriaFuseTimeLimit(const TimeValue); + TimeValue getCaldoriaFuseTimeLimit(); + void setCaldoriaSeenPullback(bool); + bool getCaldoriaSeenPullback(); + void setCaldoriaMadeOJ(bool); + bool getCaldoriaMadeOJ(); + void setCaldoriaWokenUp(bool); + bool getCaldoriaWokenUp(); + void setCaldoriaDidRecalibration(bool); + bool getCaldoriaDidRecalibration(); + void setCaldoriaSeenSinclairInElevator(bool); + bool getCaldoriaSeenSinclairInElevator(); + void setCaldoriaINNAnnouncing(bool); + bool getCaldoriaINNAnnouncing(); + void setCaldoriaSeenINN(bool); + bool getCaldoriaSeenINN(); + void setCaldoriaSeenMessages(bool); + bool getCaldoriaSeenMessages(); + void setCaldoriaSinclairShot(bool); + bool getCaldoriaSinclairShot(); + void setCaldoriaBombDisarmed(bool); + bool getCaldoriaBombDisarmed(); + void setCaldoriaRoofDoorOpen(bool); + bool getCaldoriaRoofDoorOpen(); + void setCaldoriaDoneHygiene(bool); + bool getCaldoriaDoneHygiene(); + void setCaldoriaSawVoiceAnalysis(bool); + bool getCaldoriaSawVoiceAnalysis(); + void setCaldoriaDoorBombed(bool); + bool getCaldoriaDoorBombed(); + void setCaldoriaGunAimed(bool); + bool getCaldoriaGunAimed(); + + // TSA + void setRipTimerTime(TimeValue); + TimeValue getRipTimerTime(); + void setTSAFuseTimeLimit(TimeValue); + TimeValue getTSAFuseTimeLimit(); + void setT0BMonitorMode(byte); + byte getT0BMonitorMode(); + void setTSAState(byte); + byte getTSAState(); + void setT0BMonitorStart(TimeValue); + TimeValue getT0BMonitorStart(); + void setTSAIDedAtDoor(bool); + bool getTSAIDedAtDoor(); + void setTSA0BZoomedIn(bool); + bool getTSA0BZoomedIn(); + void setTSAFrontDoorUnlockedOutside(bool); + bool getTSAFrontDoorUnlockedOutside(); + void setTSAFrontDoorUnlockedInside(bool); + bool getTSAFrontDoorUnlockedInside(); + void setTSASeenRobotGreeting(bool); + bool getTSASeenRobotGreeting(); + void setTSASeenTheory(bool); + bool getTSASeenTheory(); + void setTSASeenBackground(bool); + bool getTSASeenBackground(); + void setTSASeenProcedure(bool); + bool getTSASeenProcedure(); + void setTSASeenAgent3AtDoor(bool); + bool getTSASeenAgent3AtDoor(); + void setTSACommandCenterLocked(bool); + bool getTSACommandCenterLocked(); + void setTSASeenCaldoriaNormal(bool); + bool getTSASeenCaldoriaNormal(); + void setTSASeenCaldoriaAltered(bool); + bool getTSASeenCaldoriaAltered(); + void setTSASeenNoradNormal(bool); + bool getTSASeenNoradNormal(); + void setTSASeenNoradAltered(bool); + bool getTSASeenNoradAltered(); + void setTSASeenMarsNormal(bool); + bool getTSASeenMarsNormal(); + void setTSASeenMarsAltered(bool); + bool getTSASeenMarsAltered(); + void setTSASeenWSCNormal(bool); + bool getTSASeenWSCNormal(); + void setTSASeenWSCAltered(bool); + bool getTSASeenWSCAltered(); + void setTSABiosuitOn(bool); + bool getTSABiosuitOn(); + + // Prehistoric + void setPrehistoricTriedToExtendBridge(bool); + bool getPrehistoricTriedToExtendBridge(); + void setPrehistoricSeenTimeStream(bool); + bool getPrehistoricSeenTimeStream(); + void setPrehistoricSeenFlyer1(bool); + bool getPrehistoricSeenFlyer1(); + void setPrehistoricSeenFlyer2(bool); + bool getPrehistoricSeenFlyer2(); + void setPrehistoricSeenBridgeZoom(bool); + bool getPrehistoricSeenBridgeZoom(); + void setPrehistoricBreakerThrown(bool); + bool getPrehistoricBreakerThrown(); + + // Norad + void setNoradSeenTimeStream(bool); + bool getNoradSeenTimeStream(); + void setNoradGassed(bool); + bool getNoradGassed(); + void setNoradFillingStationOn(bool); + bool getNoradFillingStationOn(); + void setNoradN22MessagePlayed(bool); + bool getNoradN22MessagePlayed(); + void setNoradPlayedGlobeGame(bool); + bool getNoradPlayedGlobeGame(); + void setNoradBeatRobotWithClaw(bool); + bool getNoradBeatRobotWithClaw(); + void setNoradBeatRobotWithDoor(bool); + bool getNoradBeatRobotWithDoor(); + void setNoradRetScanGood(bool); + bool getNoradRetScanGood(); + void setNoradWaitingForLaser(bool); + bool getNoradWaitingForLaser(); + void setNoradSubRoomPressure(uint16); + uint16 getNoradSubRoomPressure(); + void setNoradSubPrepState(NoradSubPrepState); + NoradSubPrepState getNoradSubPrepState(); + void setNoradArrivedFromSub(bool); + bool getNoradArrivedFromSub(); + + // Mars + void setMarsSeenTimeStream(bool); + bool getMarsSeenTimeStream(); + void setMarsHeardUpperPodMessage(bool); + bool getMarsHeardUpperPodMessage(); + void setMarsRobotThrownPlayer(bool); + bool getMarsRobotThrownPlayer(); + void setMarsHeardCheckInMessage(bool); + bool getMarsHeardCheckInMessage(); + void setMarsPodAtUpperPlatform(bool); + bool getMarsPodAtUpperPlatform(); + void setMarsSeenThermalScan(bool); + bool getMarsSeenThermalScan(); + void setMarsArrivedBelow(bool); + bool getMarsArrivedBelow(); + void setMarsSeenRobotAtReactor(bool); + bool getMarsSeenRobotAtReactor(); + void setMarsAvoidedReactorRobot(bool); + bool getMarsAvoidedReactorRobot(); + void setMarsInAirlock(bool); + bool getMarsInAirlock(); + void setMarsAirlockOpen(bool); + bool getMarsAirlockOpen(); + void setMarsMaskOnFiller(bool); + bool getMarsMaskOnFiller(); + void setMarsLockFrozen(bool); + bool getMarsLockFrozen(); + void setMarsLockBroken(bool); + bool getMarsLockBroken(); + void setMarsMazeDoorPair1(bool); + bool getMarsMazeDoorPair1(); + void setMarsMazeDoorPair2(bool); + bool getMarsMazeDoorPair2(); + void setMarsMazeDoorPair3(bool); + bool getMarsMazeDoorPair3(); + void setMarsSawRobotLeave(bool); + bool getMarsSawRobotLeave(); + void setMarsSecurityDown(bool); + bool getMarsSecurityDown(); + void setMarsFinishedCanyonChase(bool); + bool getMarsFinishedCanyonChase(); + void setMarsThreadedMaze(bool); + bool getMarsThreadedMaze(); + void setMarsHitRobotWithCannon(bool); + bool getMarsHitRobotWithCannon(); + void setMarsReadyForShuttleTransport(bool); + bool getMarsReadyForShuttleTransport(); + + // WSC + void setWSCSeenTimeStream(bool); + bool getWSCSeenTimeStream(); + void setWSCPoisoned(bool); + bool getWSCPoisoned(); + void setWSCAnsweredAboutDart(bool); + bool getWSCAnsweredAboutDart(); + void setWSCRemovedDart(bool); + bool getWSCRemovedDart(); + void setWSCAnalyzerOn(bool); + bool getWSCAnalyzerOn(); + void setWSCDartInAnalyzer(bool); + bool getWSCDartInAnalyzer(); + void setWSCAnalyzedDart(bool); + bool getWSCAnalyzedDart(); + void setWSCSawMorph(bool); + bool getWSCSawMorph(); + void setWSCDesignedAntidote(bool); + bool getWSCDesignedAntidote(); + void setWSCPickedUpAntidote(bool); + bool getWSCPickedUpAntidote(); + void setWSCOfficeMessagesOpen(bool); + bool getWSCOfficeMessagesOpen(); + void setWSCSeenNerd(bool); + bool getWSCSeenNerd(); + void setWSCHeardPage1(bool); + bool getWSCHeardPage1(); + void setWSCHeardPage2(bool); + bool getWSCHeardPage2(); + void setWSCHeardCheckIn(bool); + bool getWSCHeardCheckIn(); + void setWSCDidPlasmaDodge(bool); + bool getWSCDidPlasmaDodge(); + void setWSCSeenSinclairLecture(bool); + bool getWSCSeenSinclairLecture(); + void setWSCBeenAtWSC93(bool); + bool getWSCBeenAtWSC93(); + void setWSCCatwalkDark(bool); + bool getWSCCatwalkDark(); + void setWSCRobotDead(bool); + bool getWSCRobotDead(); + void setWSCRobotGone(bool); + bool getWSCRobotGone(); + +protected: + friend class Common::Singleton<SingletonBaseType>; + +private: + // Base + NeighborhoodID _currentNeighborhood; + RoomID _currentRoom; + DirectionConstant _currentDirection; + NeighborhoodID _nexNeighborhoodID; + RoomID _nextRoomID; + DirectionConstant _nextDirection; + NeighborhoodID _lastNeighborhood; + RoomID _lastRoom; + DirectionConstant _lastDirection; + RoomID _openDoorRoom; + DirectionConstant _openDoorDirection; + + // Pegasus Prime + FlagsArray<byte, kNumGlobalFlags> _globalFlags; + FlagsArray<byte, kNumScoringFlags> _scoringFlags; + FlagsArray<uint32, kNumItems> _itemTakenFlags; + + FlagsArray<byte, kNumCaldoriaFlags> _caldoriaFlags; + TimeValue _caldoriaFuseTimeLimit; + + TimeValue _TSARipTimerTime; + TimeValue _TSAFuseTimeLimit; + byte _TSAState; + byte _T0BMonitorMode; + TimeValue _T0BMonitorStart; + FlagsArray<byte, kNumTSAFlags> _TSAFlags; + + FlagsArray<byte, kNumPrehistoricFlags> _prehistoricFlags; + + FlagsArray<byte, kNumNoradFlags> _noradFlags; + uint16 _noradSubRoomPressure; + NoradSubPrepState _noradSubPrepState; + + FlagsArray<byte, kNumMarsFlags> _marsFlags; + + FlagsArray<byte, kNumWSCFlags> _WSCFlags; +}; + +} // End of namespace Pegasus + +#define GameState (::Pegasus::GameStateManager::instance()) + +#endif diff --git a/engines/pegasus/graphics.cpp b/engines/pegasus/graphics.cpp new file mode 100644 index 0000000000..8dbd678809 --- /dev/null +++ b/engines/pegasus/graphics.cpp @@ -0,0 +1,346 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "common/events.h" +#include "common/file.h" +#include "common/textconsole.h" +#include "engines/util.h" + +#include "pegasus/elements.h" +#include "pegasus/graphics.h" +#include "pegasus/transition.h" + +namespace Pegasus { + +GraphicsManager::GraphicsManager(PegasusEngine *vm) : _vm(vm) { + initGraphics(640, 480, true, NULL); + + if (_vm->_system->getScreenFormat().bytesPerPixel == 1) + error("No true color mode available"); + + _backLayer = kMinAvailableOrder; + _frontLayer = kMaxAvailableOrder; + _firstDisplayElement = _lastDisplayElement = 0; + _workArea.create(640, 480, _vm->_system->getScreenFormat()); + _modifiedScreen = false; + _curSurface = &_workArea; + _erase = false; + _updatesEnabled = true; + _screenFader = new ScreenFader(); +} + +GraphicsManager::~GraphicsManager() { + _workArea.free(); + delete _screenFader; +} + +void GraphicsManager::invalRect(const Common::Rect &rect) { + // We're using a simpler algorithm for dirty rect handling than the original + // The original was way too overcomplicated for what we need here now. + + if (_dirtyRect.width() == 0 || _dirtyRect.height() == 0) { + // We have no dirty rect, so this is now our dirty rect + _dirtyRect = rect; + } else { + // Expand our dirty rect to include rect + _dirtyRect.extend(rect); + } + + // Sanity check: clip our rect to the screen + _dirtyRect.right = MIN<int>(640, _dirtyRect.right); + _dirtyRect.bottom = MIN<int>(480, _dirtyRect.bottom); +} + +void GraphicsManager::addDisplayElement(DisplayElement *newElement) { + newElement->_elementOrder = CLIP<int>(newElement->_elementOrder, kMinAvailableOrder, kMaxAvailableOrder); + + if (_firstDisplayElement) { + DisplayElement *runner = _firstDisplayElement; + DisplayElement *lastRunner = 0; + + // Search for first element whose display order is greater than + // the new element's and add the new element just before it. + while (runner) { + if (newElement->_elementOrder < runner->_elementOrder) { + if (lastRunner) { + lastRunner->_nextElement = newElement; + newElement->_nextElement = runner; + } else { + newElement->_nextElement = _firstDisplayElement; + _firstDisplayElement = newElement; + } + break; + } + lastRunner = runner; + runner = runner->_nextElement; + } + + // If got here and runner == NULL, we ran through the whole list without + // inserting, so add at the end. + if (!runner) { + _lastDisplayElement->_nextElement = newElement; + _lastDisplayElement = newElement; + } + } else { + _firstDisplayElement = newElement; + _lastDisplayElement = newElement; + } + + newElement->_elementIsDisplaying = true; +} + +void GraphicsManager::removeDisplayElement(DisplayElement *oldElement) { + if (!_firstDisplayElement) + return; + + if (oldElement == _firstDisplayElement) { + if (oldElement == _lastDisplayElement) { + _firstDisplayElement = 0; + _lastDisplayElement = 0; + } else { + _firstDisplayElement = oldElement->_nextElement; + } + + invalRect(oldElement->_bounds); + } else { + // Scan list for element. + // If we get here, we know that the list has at least one item, and it + // is not the first item, so we can skip it. + DisplayElement *runner = _firstDisplayElement->_nextElement; + DisplayElement *lastRunner = _firstDisplayElement; + + while (runner) { + if (runner == oldElement) { + lastRunner->_nextElement = runner->_nextElement; + + if (oldElement == _lastDisplayElement) + _lastDisplayElement = lastRunner; + + invalRect(oldElement->_bounds); + break; + } + + lastRunner = runner; + runner = runner->_nextElement; + } + } + + oldElement->_nextElement = 0; + oldElement->_elementIsDisplaying = false; +} + +void GraphicsManager::updateDisplay() { + bool screenDirty = false; + + if (!_dirtyRect.isEmpty()) { + // Fill the dirty area with black if erase mode is enabled + if (_erase) + _workArea.fillRect(_dirtyRect, _workArea.format.RGBToColor(0, 0, 0)); + + for (DisplayElement *runner = _firstDisplayElement; runner != 0; runner = runner->_nextElement) { + Common::Rect bounds; + runner->getBounds(bounds); + + // TODO: Better logic; it does a bit more work than it probably needs to + // but it should work fine for now. + if (bounds.intersects(_dirtyRect) && runner->validToDraw(_backLayer, _frontLayer)) { + runner->draw(bounds); + screenDirty = true; + } + } + + // Copy only the dirty rect to the screen + if (screenDirty) + g_system->copyRectToScreen((byte *)_workArea.getBasePtr(_dirtyRect.left, _dirtyRect.top), _workArea.pitch, _dirtyRect.left, _dirtyRect.top, _dirtyRect.width(), _dirtyRect.height()); + + // Clear the dirty rect + _dirtyRect = Common::Rect(); + } + + if (_updatesEnabled && (screenDirty || _modifiedScreen)) + g_system->updateScreen(); + + _modifiedScreen = false; +} + +void GraphicsManager::clearScreen() { + Graphics::Surface *screen = g_system->lockScreen(); + screen->fillRect(Common::Rect(0, 0, 640, 480), g_system->getScreenFormat().RGBToColor(0, 0, 0)); + g_system->unlockScreen(); + _modifiedScreen = true; +} + +DisplayElement *GraphicsManager::findDisplayElement(const DisplayElementID id) { + DisplayElement *runner = _firstDisplayElement; + + while (runner) { + if (runner->getObjectID() == id) + return runner; + runner = runner->_nextElement; + } + + return 0; +} + +void GraphicsManager::doFadeOutSync(const TimeValue time, const TimeScale scale, bool isBlack) { + _updatesEnabled = false; + _screenFader->doFadeOutSync(time, scale, isBlack); +} + +void GraphicsManager::doFadeInSync(const TimeValue time, const TimeScale scale, bool isBlack) { + _screenFader->doFadeInSync(time, scale, isBlack); + _updatesEnabled = true; +} + +void GraphicsManager::markCursorAsDirty() { + _modifiedScreen = true; +} + +void GraphicsManager::newShakePoint(int32 index1, int32 index2, int32 maxRadius) { + int32 index3 = (index1 + index2) >> 1; + + if (maxRadius == 0) { + _shakeOffsets[index3].x = ((_shakeOffsets[index1].x + _shakeOffsets[index2].x) >> 1); + _shakeOffsets[index3].y = ((_shakeOffsets[index1].y + _shakeOffsets[index2].y) >> 1); + } else { + double angle = (int32)(_vm->getRandomNumber(360 - 1) * 3.1415926535 / 180); + int32 radius = maxRadius; + _shakeOffsets[index3].x = (int32)(((_shakeOffsets[index1].x + _shakeOffsets[index2].x) >> 1) + + cos(angle) / 2 * radius); + _shakeOffsets[index3].y = (int32)(((_shakeOffsets[index1].y + _shakeOffsets[index2].y) >> 1) + + sin(angle) * radius); + } + + if (index1 < index3 - 1) + newShakePoint(index1, index3, maxRadius * 2 / 3); + + if (index3 < index2 - 1) + newShakePoint(index3, index2, maxRadius * 2 / 3); +} + +void GraphicsManager::shakeTheWorld(TimeValue duration, TimeScale scale) { + if (duration == 0 || scale == 0) + return; + + _shakeOffsets[0].x = 0; + _shakeOffsets[0].y = 0; + _shakeOffsets[(kMaxShakeOffsets - 1) / 4].x = 0; + _shakeOffsets[(kMaxShakeOffsets - 1) / 4].y = 0; + _shakeOffsets[(kMaxShakeOffsets - 1) / 2].x = 0; + _shakeOffsets[(kMaxShakeOffsets - 1) / 2].y = 0; + _shakeOffsets[(kMaxShakeOffsets - 1) * 3 / 4].x = 0; + _shakeOffsets[(kMaxShakeOffsets - 1) * 3 / 4].y = 0; + _shakeOffsets[kMaxShakeOffsets - 1].x = 0; + _shakeOffsets[kMaxShakeOffsets - 1].y = 0; + + newShakePoint(0, (kMaxShakeOffsets - 1) / 4, 8); + newShakePoint((kMaxShakeOffsets - 1) / 4, (kMaxShakeOffsets - 1) / 2, 6); + newShakePoint((kMaxShakeOffsets - 1) / 2, (kMaxShakeOffsets - 1) * 3 / 4, 4); + newShakePoint((kMaxShakeOffsets - 1) * 3 / 4, kMaxShakeOffsets - 1, 3); + + Common::Point lastOffset(0, 0); + + // Store the current screen for later use + Graphics::Surface oldScreen; + Graphics::Surface *curScreen = g_system->lockScreen(); + oldScreen.copyFrom(*curScreen); + g_system->unlockScreen(); + + // Convert to millis + duration = duration * 1000 / scale; + + uint32 startTime = g_system->getMillis(); + + while (g_system->getMillis() < startTime + duration) { + Common::Point thisOffset = _shakeOffsets[(g_system->getMillis() - startTime) * (kMaxShakeOffsets - 1) / duration]; + if (thisOffset != lastOffset) { + // Fill the screen with black + Graphics::Surface *screen = g_system->lockScreen(); + screen->fillRect(Common::Rect(0, 0, 640, 480), g_system->getScreenFormat().RGBToColor(0, 0, 0)); + g_system->unlockScreen(); + + // Calculate the src/dst offsets and the width/height + int32 srcOffsetX, dstOffsetX, width; + + if (thisOffset.x > 0) { + srcOffsetX = 0; + dstOffsetX = thisOffset.x; + width = 640 - dstOffsetX; + } else { + srcOffsetX = -thisOffset.x; + dstOffsetX = 0; + width = 640 - srcOffsetX; + } + + int32 srcOffsetY, dstOffsetY, height; + + if (thisOffset.y > 0) { + srcOffsetY = 0; + dstOffsetY = thisOffset.y; + height = 480 - dstOffsetY; + } else { + srcOffsetY = -thisOffset.y; + dstOffsetY = 0; + height = 480 - srcOffsetY; + } + + // Now copy to the screen + g_system->copyRectToScreen((byte *)oldScreen.getBasePtr(srcOffsetX, srcOffsetY), oldScreen.pitch, + dstOffsetX, dstOffsetY, width, height); + g_system->updateScreen(); + + lastOffset = thisOffset; + } + + g_system->delayMillis(10); + } + + if (lastOffset.x != 0 || lastOffset.y != 0) { + g_system->copyRectToScreen((byte *)oldScreen.pixels, oldScreen.pitch, 0, 0, 640, 480); + g_system->updateScreen(); + } + + oldScreen.free(); +} + +void GraphicsManager::enableErase() { + _erase = true; +} + +void GraphicsManager::disableErase() { + _erase = false; +} + +void GraphicsManager::enableUpdates() { + _updatesEnabled = true; + _screenFader->setFaderValue(100); +} + +void GraphicsManager::disableUpdates() { + _updatesEnabled = false; + _screenFader->setFaderValue(0); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/graphics.h b/engines/pegasus/graphics.h new file mode 100644 index 0000000000..fcdf1c9e78 --- /dev/null +++ b/engines/pegasus/graphics.h @@ -0,0 +1,95 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_GRAPHICS_H +#define PEGASUS_GRAPHICS_H + +#include "common/rect.h" +#include "common/str.h" +#include "common/system.h" +#include "graphics/surface.h" + +#include "pegasus/constants.h" +#include "pegasus/pegasus.h" +#include "pegasus/util.h" + +namespace Pegasus { + +class Cursor; +class DisplayElement; +class PegasusEngine; +class ScreenFader; + +class GraphicsManager { +friend class Cursor; +public: + GraphicsManager(PegasusEngine *vm); + ~GraphicsManager(); + + void addDisplayElement(DisplayElement *element); + void removeDisplayElement(DisplayElement *element); + void invalRect(const Common::Rect &rect); + DisplayOrder getBackOfActiveLayer() const { return _backLayer; } + DisplayOrder getFrontOfActiveLayer() const { return _frontLayer; } + void updateDisplay(); + Graphics::Surface *getCurSurface() { return _curSurface; } + void setCurSurface(Graphics::Surface *surface) { _curSurface = surface; } + Graphics::Surface *getWorkArea() { return &_workArea; } + void clearScreen(); + DisplayElement *findDisplayElement(const DisplayElementID id); + void shakeTheWorld(TimeValue time, TimeScale scale); + void enableErase(); + void disableErase(); + void enableUpdates(); + void disableUpdates(); + + // These default to black + void doFadeOutSync(const TimeValue = kOneSecondPerThirtyTicks, const TimeScale = kThirtyTicksPerSecond, bool isBlack = true); + void doFadeInSync(const TimeValue = kOneSecondPerThirtyTicks, const TimeScale = kThirtyTicksPerSecond, bool isBlack = true); + +protected: + void markCursorAsDirty(); + +private: + PegasusEngine *_vm; + + bool _modifiedScreen, _erase; + Common::Rect _dirtyRect; + DisplayOrder _backLayer, _frontLayer; + DisplayElement *_firstDisplayElement, *_lastDisplayElement; + Graphics::Surface _workArea, *_curSurface; + + // Shake Shake Shake! + static const int kMaxShakeOffsets = 17; + Common::Point _shakeOffsets[kMaxShakeOffsets]; + void newShakePoint(int32 index1, int32 index2, int32 maxRadius); + + bool _updatesEnabled; + ScreenFader *_screenFader; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/hotspot.cpp b/engines/pegasus/hotspot.cpp new file mode 100644 index 0000000000..d8b07a94f4 --- /dev/null +++ b/engines/pegasus/hotspot.cpp @@ -0,0 +1,325 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "common/stream.h" + +#include "pegasus/hotspot.h" + +namespace Pegasus { + +Region::Region(Common::ReadStream *stream) { + uint16 length = stream->readUint16BE(); + + assert(length >= 10); + + _bounds.top = stream->readUint16BE(); + _bounds.left = stream->readUint16BE(); + _bounds.bottom = stream->readUint16BE(); + _bounds.right = stream->readUint16BE(); + + _bounds.debugPrint(0, "Bounds:"); + + if (length == 10) + return; + + length -= 10; + + while (length > 0) { + Vector v; + v.y = stream->readUint16BE(); + length -= 2; + + if (v.y == 0x7fff) + break; + + debug(0, "y: %d", v.y); + + // Normalize y to _bounds + v.y -= _bounds.top; + + while (length > 0) { + Run run; + run.start = stream->readUint16BE(); + length -= 2; + + if (run.start == 0x7fff) + break; + + run.end = stream->readUint16BE(); + length -= 2; + + debug(0, "\t[%d, %d)", run.start, run.end); + + // Normalize to _bounds + run.start -= _bounds.left; + run.end -= _bounds.left; + + v.push_back(run); + } + + _vectors.push_back(v); + } +} + +Region::Region(const Common::Rect &rect) { + _bounds = rect; +} + +bool Region::pointInRegion(const Common::Point &point) const { + if (!_bounds.contains(point)) + return false; + + bool pixelActive = false; + + // Normalize the points to _bounds + uint16 x = point.x - _bounds.left; + uint16 y = point.y - _bounds.top; + + for (Common::List<Vector>::const_iterator v = _vectors.begin(); v != _vectors.end(); v++) { + if (v->y > y) + return pixelActive; + + for (Vector::const_iterator run = v->begin(); run != v->end(); run++) { + if (x >= run->start && x < run->end) { + pixelActive = !pixelActive; + break; + } + } + } + + // the case if the region is just a rect + return true; +} + +void Region::moveTo(CoordType h, CoordType v) { + _bounds.moveTo(h, v); +} + +void Region::moveTo(const Common::Point &point) { + _bounds.moveTo(point); +} + +void Region::translate(CoordType h, CoordType v) { + _bounds.translate(h, v); +} + +void Region::translate(const Common::Point &point) { + _bounds.translate(point.x, point.y); +} + +void Region::getCenter(CoordType &h, CoordType &v) const { + h = (_bounds.left + _bounds.right) / 2; + v = (_bounds.top + _bounds.bottom) / 2; +} + +void Region::getCenter(Common::Point &point) const { + getCenter(point.x, point.y); +} + +Hotspot::Hotspot(const HotSpotID id) : IDObject(id) { + _spotFlags = kNoHotSpotFlags; + _spotActive = false; +} + +Hotspot::~Hotspot() { +} + +void Hotspot::setArea(const Common::Rect &area) { + _spotArea = Region(area); +} + +void Hotspot::setArea(const CoordType left, const CoordType top, const CoordType right, const CoordType bottom) { + _spotArea = Region(Common::Rect(left, top, right, bottom)); +} + +void Hotspot::getBoundingBox(Common::Rect &r) const { + r = _spotArea.getBoundingBox(); +} + +void Hotspot::getCenter(Common::Point &pt) const { + _spotArea.getCenter(pt); +} + +void Hotspot::getCenter(CoordType &h, CoordType &v) const { + _spotArea.getCenter(h, v); +} + +void Hotspot::setActive() { + _spotActive = true; +} + +void Hotspot::setInactive() { + _spotActive = false; +} + +void Hotspot::setHotspotFlags(const HotSpotFlags flags) { + _spotFlags = flags; +} + +void Hotspot::setMaskedHotspotFlags(const HotSpotFlags flags, const HotSpotFlags mask) { + _spotFlags = (_spotFlags & ~mask) | flags; +} + +bool Hotspot::isSpotActive() const { + return _spotActive; +} + +void Hotspot::moveSpotTo(const CoordType h, const CoordType v) { + _spotArea.moveTo(h, v); +} + +void Hotspot::moveSpotTo(const Common::Point pt) { + _spotArea.moveTo(pt); +} + +void Hotspot::moveSpot(const CoordType h, const CoordType v) { + _spotArea.translate(h, v); +} + +void Hotspot::moveSpot(const Common::Point pt) { + _spotArea.translate(pt.x, pt.y); +} + +bool Hotspot::pointInSpot(const Common::Point where) const { + return _spotActive && _spotArea.pointInRegion(where); +} + +HotSpotFlags Hotspot::getHotspotFlags() const { + return _spotFlags; +} + +HotspotList::HotspotList() { +} + +HotspotList::~HotspotList() { + // TODO: Should this call deleteHotspots()? +} + +void HotspotList::deleteHotspots() { + for (HotspotIterator it = begin(); it != end(); it++) + delete *it; + + clear(); +} + +Hotspot *HotspotList::findHotspot(const Common::Point where) { + for (HotspotIterator it = begin(); it != end(); it++) + if ((*it)->pointInSpot(where)) + return *it; + + return 0; +} + +HotSpotID HotspotList::findHotspotID(const Common::Point where) { + Hotspot *hotspot = findHotspot(where); + return hotspot ? hotspot->getObjectID() : kNoHotSpotID; +} + +Hotspot *HotspotList::findHotspotByID(const HotSpotID id) { + for (HotspotIterator it = begin(); it != end(); it++) + if ((*it)->getObjectID() == id) + return *it; + + return 0; +} + +Hotspot *HotspotList::findHotspotByMask(const HotSpotFlags flags) { + for (HotspotIterator it = begin(); it != end(); it++) + if (((*it)->getHotspotFlags() & flags) == flags) + return *it; + + return 0; +} + +void HotspotList::activateMaskedHotspots(const HotSpotFlags flags) { + for (HotspotIterator it = begin(); it != end(); it++) + if (flags == kNoHotSpotFlags || ((*it)->getHotspotFlags() & flags) != 0) + (*it)->setActive(); +} + +void HotspotList::deactivateAllHotspots() { + for (HotspotIterator it = begin(); it != end(); it++) + (*it)->setInactive(); +} + +void HotspotList::deactivateMaskedHotspots(const HotSpotFlags flags) { + for (HotspotIterator it = begin(); it != end(); it++) + if (((*it)->getHotspotFlags() & flags) != 0) + (*it)->setInactive(); +} + +void HotspotList::activateOneHotspot(const HotSpotID id) { + for (HotspotIterator it = begin(); it != end(); it++) { + if ((*it)->getObjectID() == id) { + (*it)->setActive(); + return; + } + } +} + +void HotspotList::deactivateOneHotspot(const HotSpotID id) { + for (HotspotIterator it = begin(); it != end(); it++) { + if ((*it)->getObjectID() == id) { + (*it)->setInactive(); + return; + } + } +} + +void HotspotList::removeOneHotspot(const HotSpotID id) { + for (HotspotIterator it = begin(); it != end(); it++) { + if ((*it)->getObjectID() == id) { + erase(it); + return; + } + } +} + +void HotspotList::removeMaskedHotspots(const HotSpotFlags flags) { + if (flags != kNoHotSpotFlags) { + for (HotspotIterator it = begin(); it != end(); ) { + if (((*it)->getHotspotFlags() & flags) != 0) + it = erase(it); + else + it++; + } + } else { + clear(); + } +} + +void HotspotList::setHotspotRect(const HotSpotID id, const Common::Rect &r) { + Hotspot *hotspot = findHotspotByID(id); + if (hotspot) + hotspot->setArea(r); +} + +void HotspotList::getHotspotRect(const HotSpotID id, Common::Rect &r) { + Hotspot *hotspot = findHotspotByID(id); + if (hotspot) + hotspot->getBoundingBox(r); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/hotspot.h b/engines/pegasus/hotspot.h new file mode 100644 index 0000000000..64d3fb19f9 --- /dev/null +++ b/engines/pegasus/hotspot.h @@ -0,0 +1,152 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_HOTSPOT_H +#define PEGASUS_HOTSPOT_H + +#include "common/list.h" +#include "common/rect.h" + +#include "pegasus/constants.h" +#include "pegasus/types.h" +#include "pegasus/util.h" + +/* + + Hot spots combine a pixel area, an ID value and an active flag. + + A point is considered in a hot spot if the point is in the hot spot's pixel area and + the active flag is set. + + In addition, hot spots have a 32 bit word of bit flags for filtering use. + +*/ + +namespace Common { + class ReadStream; +} + +namespace Pegasus { + +// Our implementation of QuickDraw regions +class Region { +public: + Region() {} + Region(Common::ReadStream *stream); + Region(const Common::Rect &rect); + + Common::Rect getBoundingBox() const { return _bounds; } + + bool pointInRegion(const Common::Point &point) const; + + void moveTo(CoordType h, CoordType v); + void moveTo(const Common::Point &point); + void translate(CoordType h, CoordType v); + void translate(const Common::Point &point); + void getCenter(CoordType &h, CoordType &v) const; + void getCenter(Common::Point &point) const; + +private: + Common::Rect _bounds; + + struct Run { + uint16 start, end; + }; + + class Vector : public Common::List<Run> { + public: + uint16 y; + }; + + Common::List<Vector> _vectors; +}; + +class Hotspot : public IDObject { +public: + Hotspot(const HotSpotID); + virtual ~Hotspot(); + + void setArea(const Region ®ion) { _spotArea = region; } + void setArea(const Common::Rect &); + void setArea(const CoordType, const CoordType, const CoordType, const CoordType); + void getBoundingBox(Common::Rect &) const; + void getArea(Region &) const; + void getCenter(Common::Point&) const; + void getCenter(CoordType&, CoordType&) const; + + void moveSpotTo(const CoordType, const CoordType); + void moveSpotTo(const Common::Point); + void moveSpot(const CoordType, const CoordType); + void moveSpot(const Common::Point); + + bool pointInSpot(const Common::Point) const; + + void setActive(); + void setInactive(); + bool isSpotActive() const; + + HotSpotFlags getHotspotFlags() const; + void setHotspotFlags(const HotSpotFlags); + void setMaskedHotspotFlags(const HotSpotFlags flags, const HotSpotFlags mask); + +protected: + Region _spotArea; + HotSpotFlags _spotFlags; + bool _spotActive; +}; + +class HotspotList : public Common::List<Hotspot *> { +public: + HotspotList(); + virtual ~HotspotList(); + + void deleteHotspots(); + + Hotspot *findHotspot(const Common::Point); + HotSpotID findHotspotID(const Common::Point); + Hotspot *findHotspotByID(const HotSpotID); + Hotspot *findHotspotByMask(const HotSpotFlags); + + void activateMaskedHotspots(const HotSpotFlags = kNoHotSpotFlags); + void deactivateAllHotspots(); + void deactivateMaskedHotspots(const HotSpotFlags); + + void activateOneHotspot(const HotSpotID); + void deactivateOneHotspot(const HotSpotID); + + void removeOneHotspot(const HotSpotID); + void removeMaskedHotspots(const HotSpotFlags = kNoHotSpotFlags); + + void setHotspotRect(const HotSpotID, const Common::Rect&); + void getHotspotRect(const HotSpotID, Common::Rect&); +}; + +typedef HotspotList::iterator HotspotIterator; + +#define g_allHotspots (((PegasusEngine *)g_engine)->getAllHotspots()) + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/input.cpp b/engines/pegasus/input.cpp new file mode 100644 index 0000000000..b74e4a4c45 --- /dev/null +++ b/engines/pegasus/input.cpp @@ -0,0 +1,373 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "common/events.h" +#include "common/system.h" + +#include "pegasus/cursor.h" +#include "pegasus/input.h" +#include "pegasus/pegasus.h" + +namespace Common { +DECLARE_SINGLETON(Pegasus::InputDeviceManager); +} + +namespace Pegasus { + +InputDeviceManager::InputDeviceManager() { + // Set all keys to "not down" + _keyMap[Common::KEYCODE_UP] = false; + _keyMap[Common::KEYCODE_KP8] = false; + _keyMap[Common::KEYCODE_LEFT] = false; + _keyMap[Common::KEYCODE_KP4] = false; + _keyMap[Common::KEYCODE_DOWN] = false; + _keyMap[Common::KEYCODE_KP5] = false; + _keyMap[Common::KEYCODE_RIGHT] = false; + _keyMap[Common::KEYCODE_KP6] = false; + _keyMap[Common::KEYCODE_RETURN] = false; + _keyMap[Common::KEYCODE_SPACE] = false; + _keyMap[Common::KEYCODE_t] = false; + _keyMap[Common::KEYCODE_KP_EQUALS] = false; + _keyMap[Common::KEYCODE_i] = false; + _keyMap[Common::KEYCODE_KP_DIVIDE] = false; + _keyMap[Common::KEYCODE_q] = false; + _keyMap[Common::KEYCODE_ESCAPE] = false; + _keyMap[Common::KEYCODE_p] = false; + _keyMap[Common::KEYCODE_TILDE] = false; + _keyMap[Common::KEYCODE_BACKQUOTE] = false; + _keyMap[Common::KEYCODE_NUMLOCK] = false; + _keyMap[Common::KEYCODE_BACKSPACE] = false; + _keyMap[Common::KEYCODE_KP_MULTIPLY] = false; + _keyMap[Common::KEYCODE_LALT] = false; + _keyMap[Common::KEYCODE_RALT] = false; + _keyMap[Common::KEYCODE_e] = false; + _keyMap[Common::KEYCODE_KP_ENTER] = false; + + g_system->getEventManager()->getEventDispatcher()->registerObserver(this, 2, false); + _lastRawBits = kAllUpBits; + _consoleRequested = false; +} + +InputDeviceManager::~InputDeviceManager() { + g_system->getEventManager()->getEventDispatcher()->unregisterObserver(this); +} + +void InputDeviceManager::getInput(Input &input, const InputBits filter) { + // Poll for events, but ignore them! + // We'll pick them up in notifyEvent() + // We do that so that any pollEvent() call can update the variables + // (ie. if one uses enter to access the restore menu, we never receive + // the key up event, which leads to bad things) + // This is to closely emulate what the GetKeys() function did on Mac OS + Common::Event event; + while (g_system->getEventManager()->pollEvent(event)) + ; + + // Now create the bitfield + InputBits currentBits = 0; + + if (_keyMap[Common::KEYCODE_UP] || _keyMap[Common::KEYCODE_KP8]) + currentBits |= (kRawButtonDown << kUpButtonShift); + + if (_keyMap[Common::KEYCODE_DOWN] || _keyMap[Common::KEYCODE_KP5]) + currentBits |= (kRawButtonDown << kDownButtonShift); + + if (_keyMap[Common::KEYCODE_LEFT] || _keyMap[Common::KEYCODE_KP4]) + currentBits |= (kRawButtonDown << kLeftButtonShift); + + if (_keyMap[Common::KEYCODE_RIGHT] || _keyMap[Common::KEYCODE_KP6]) + currentBits |= (kRawButtonDown << kRightButtonShift); + + if (_keyMap[Common::KEYCODE_SPACE] || _keyMap[Common::KEYCODE_RETURN] || _keyMap[Common::KEYCODE_KP_ENTER]) + currentBits |= (kRawButtonDown << kTwoButtonShift); + + if (_keyMap[Common::KEYCODE_t] || _keyMap[Common::KEYCODE_KP_EQUALS]) + currentBits |= (kRawButtonDown << kThreeButtonShift); + + if (_keyMap[Common::KEYCODE_i] || _keyMap[Common::KEYCODE_KP_DIVIDE]) + currentBits |= (kRawButtonDown << kFourButtonShift); + + if (_keyMap[Common::KEYCODE_q]) + currentBits |= (kRawButtonDown << kMod1ButtonShift); + + if (_keyMap[Common::KEYCODE_ESCAPE] || _keyMap[Common::KEYCODE_p]) + currentBits |= (kRawButtonDown << kMod3ButtonShift); + + if (_keyMap[Common::KEYCODE_TILDE] || _keyMap[Common::KEYCODE_BACKQUOTE] || _keyMap[Common::KEYCODE_NUMLOCK]) + currentBits |= (kRawButtonDown << kLeftFireButtonShift); + + if (_keyMap[Common::KEYCODE_BACKSPACE] || _keyMap[Common::KEYCODE_KP_MULTIPLY]) + currentBits |= (kRawButtonDown << kRightFireButtonShift); + + // Update mouse button state + // Note that we don't use EVENT_LBUTTONUP/EVENT_LBUTTONDOWN because + // they do not show if the button is being held down. We're treating + // both mouse buttons as the same for ease of use. + if (g_system->getEventManager()->getButtonState() != 0) + currentBits |= (kRawButtonDown << kTwoButtonShift); + + // Update the mouse position too + input.setInputLocation(g_system->getEventManager()->getMousePos()); + + // Set the outgoing bits + InputBits filteredBits = currentBits & filter; + input.setInputBits((filteredBits & kAllButtonDownBits) | (filteredBits & _lastRawBits & kAllAutoBits)); + + // Update the last bits + _lastRawBits = currentBits; + + // Set the console to be requested or not + input.setConsoleRequested(_consoleRequested); + + // WORKAROUND: The original had this in currentBits, but then + // pressing alt would count as an event (and mess up someone + // trying to do alt+enter or something). Since it's only used + // as an easter egg, I'm just going to handle it as a separate + // bool value. + // WORKAROUND x2: I'm also accepting 'e' here since an + // alt+click is often intercepted by the OS. 'e' is used as the + // easter egg key in Buried in Time and Legacy of Time. + input.setAltDown(_keyMap[Common::KEYCODE_LALT] || _keyMap[Common::KEYCODE_RALT] || _keyMap[Common::KEYCODE_e]); +} + +// Wait until the input device stops returning input allowed by filter... +void InputDeviceManager::waitInput(const InputBits filter) { + if (filter != 0) { + for (;;) { + Input input; + getInput(input, filter); + if (!input.anyInput()) + break; + } + } +} + +bool InputDeviceManager::notifyEvent(const Common::Event &event) { + // We're mapping from ScummVM events to pegasus events, which + // are based on pippin events. + _consoleRequested = false; + + switch (event.type) { + case Common::EVENT_KEYDOWN: + switch (event.kbd.keycode) { + case Common::KEYCODE_d: + if (event.kbd.flags & Common::KBD_CTRL) // Console! + _consoleRequested = true; + break; + case Common::KEYCODE_s: + // We support meta where available and control elsewhere + if (event.kbd.flags & (Common::KBD_CTRL|Common::KBD_META)) + ((PegasusEngine *)g_engine)->requestSave(); + break; + case Common::KEYCODE_o: // o for open (original) + case Common::KEYCODE_l: // l for load (ScummVM terminology) + // We support meta where available and control elsewhere + if (event.kbd.flags & (Common::KBD_CTRL|Common::KBD_META)) + ((PegasusEngine *)g_engine)->requestLoad(); + break; + default: + // Otherwise, set the key to down if we have it + if (_keyMap.contains(event.kbd.keycode)) + _keyMap[event.kbd.keycode] = true; + break; + } + break; + case Common::EVENT_KEYUP: + // Set the key to up if we have it + if (_keyMap.contains(event.kbd.keycode)) + _keyMap[event.kbd.keycode] = false; + break; + default: + break; + } + + return false; +} + +int operator==(const Input &arg1, const Input &arg2) { + return arg1._inputState == arg2._inputState; +} + +int operator!=(const Input &arg1, const Input &arg2) { + return !operator==(arg1, arg2); +} + +InputHandler *InputHandler::_inputHandler = 0; +bool InputHandler::_invalHotspots = false; +InputBits InputHandler::_lastFilter = kFilterNoInput; + +InputHandler *InputHandler::setInputHandler(InputHandler *currentHandler) { + InputHandler *result = 0; + + if (_inputHandler != currentHandler && (!_inputHandler || _inputHandler->releaseInputFocus())) { + result = _inputHandler; + _inputHandler = currentHandler; + if (_inputHandler) + _inputHandler->grabInputFocus(); + } + + return result; +} + +void InputHandler::pollForInput() { + if (_inputHandler) { + Input input; + Hotspot *cursorSpot = 0; + + InputHandler::getInput(input, cursorSpot); + if (_inputHandler->isClickInput(input, cursorSpot)) + _inputHandler->clickInHotspot(input, cursorSpot); + else + _inputHandler->handleInput(input, cursorSpot); + } +} + +void InputHandler::getInput(Input &input, Hotspot *&cursorSpot) { + Cursor *cursor = ((PegasusEngine *)g_engine)->_cursor; + + if (_inputHandler) + _lastFilter = _inputHandler->getInputFilter(); + else + _lastFilter = kFilterAllInput; + + InputDevice.getInput(input, _lastFilter); + + if (_inputHandler && _inputHandler->wantsCursor() && (_lastFilter & _inputHandler->getClickFilter()) != 0) { + if (cursor->isVisible()) { + g_allHotspots.deactivateAllHotspots(); + _inputHandler->activateHotspots(); + + Common::Point cursorLocation; + cursor->getCursorLocation(cursorLocation); + cursorSpot = g_allHotspots.findHotspot(cursorLocation); + + if (_inputHandler) + _inputHandler->updateCursor(cursorLocation, cursorSpot); + } else { + cursor->hideUntilMoved(); + } + } else { + cursor->hide(); + } +} + +void InputHandler::readInputDevice(Input &input) { + InputDevice.getInput(input, kFilterAllInput); +} + +InputHandler::InputHandler(InputHandler *nextHandler) { + _nextHandler = nextHandler; + allowInput(true); +} + +InputHandler::~InputHandler() { + if (_inputHandler == this) + setInputHandler(_nextHandler); +} + +void InputHandler::handleInput(const Input &input, const Hotspot *cursorSpot) { + if (_nextHandler) + _nextHandler->handleInput(input, cursorSpot); +} + +void InputHandler::clickInHotspot(const Input &input, const Hotspot *cursorSpot) { + if (_nextHandler) + _nextHandler->clickInHotspot(input, cursorSpot); +} + +bool InputHandler::isClickInput(const Input &input, const Hotspot *cursorSpot) { + if (_nextHandler) + return _nextHandler->isClickInput(input, cursorSpot); + + return false; +} + +void InputHandler::activateHotspots() { + if (_nextHandler) + _nextHandler->activateHotspots(); +} + +InputBits InputHandler::getInputFilter() { + if (_allowInput) { + if (_nextHandler) + return _nextHandler->getInputFilter(); + else + return kFilterAllInput; + } + + return kFilterNoInput; +} + +InputBits InputHandler::getClickFilter() { + if (_allowInput && _nextHandler) + return _nextHandler->getClickFilter(); + + return kFilterNoInput; +} + +void InputHandler::updateCursor(const Common::Point cursorLocation, const Hotspot *cursorSpot) { + if (_nextHandler) + _nextHandler->updateCursor(cursorLocation, cursorSpot); +} + +bool InputHandler::wantsCursor() { + if (_allowInput) { + if (_nextHandler) + return _nextHandler->wantsCursor(); + else + return true; + } + + return false; +} + +Tracker *Tracker::_currentTracker = 0; + +void Tracker::handleInput(const Input &input, const Hotspot *) { + if (stopTrackingInput(input)) + stopTracking(input); + else if (isTracking()) + continueTracking(input); +} + +void Tracker::startTracking(const Input &) { + if (!isTracking()) { + _savedHandler = InputHandler::setInputHandler(this); + _currentTracker = this; + } +} + +void Tracker::stopTracking(const Input &) { + if (isTracking()) { + _currentTracker = NULL; + InputHandler::setInputHandler(_savedHandler); + } +} + +bool Tracker::isClickInput(const Input &input, const Hotspot *hotspot) { + return !isTracking() && InputHandler::isClickInput(input, hotspot); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/input.h b/engines/pegasus/input.h new file mode 100644 index 0000000000..3e938fa42a --- /dev/null +++ b/engines/pegasus/input.h @@ -0,0 +1,500 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_INPUT_H +#define PEGASUS_INPUT_H + +#include "common/events.h" +#include "common/hashmap.h" +#include "common/rect.h" +#include "common/singleton.h" + +#include "pegasus/constants.h" +#include "pegasus/types.h" + +namespace Pegasus { + +class Hotspot; +class Input; + +class InputDeviceManager : public Common::Singleton<InputDeviceManager>, public Common::EventObserver { +public: + InputDeviceManager(); + ~InputDeviceManager(); + + bool notifyEvent(const Common::Event &event); + + void getInput(Input &, const InputBits); + + void waitInput(const InputBits); + +protected: + friend class Common::Singleton<SingletonBaseType>; + + // Keep track of which keys are down (= true) or not + Common::HashMap<uint, bool> _keyMap; + InputBits _lastRawBits; + bool _consoleRequested; +}; + +enum { + kButtonDownBit = 0, + kAutoButtonBit = 1, + kBitsPerButton = 2, + + kButtonDownMask = 1 << kButtonDownBit, + kAutoButtonMask = 1 << kAutoButtonBit, + + kButtonStateBits = kButtonDownMask | kAutoButtonMask, + + kRawButtonUp = 0, + kRawButtonDown = kButtonDownMask | kAutoButtonMask, + + kButtonUp = 0, + kButtonDown = kButtonDownMask, + kButtonAutoUp = kAutoButtonMask, + kButtonAutoDown = kButtonDownMask | kAutoButtonMask +}; + +enum { + kUpButtonNum = 0, + kLeftButtonNum = 1, + kDownButtonNum = 2, + kRightButtonNum = 3, + kLeftFireButtonNum = 4, + kRightFireButtonNum = 5, + kOneButtonNum = 6, + kTwoButtonNum = 7, + kThreeButtonNum = 8, + kFourButtonNum = 9, + kMod1ButtonNum = 10, + kMod2ButtonNum = 11, + kMod3ButtonNum = 12 +}; + +enum { + kUpButtonShift = kUpButtonNum * kBitsPerButton, + kLeftButtonShift = kLeftButtonNum * kBitsPerButton, + kDownButtonShift = kDownButtonNum * kBitsPerButton, + kRightButtonShift = kRightButtonNum * kBitsPerButton, + kLeftFireButtonShift = kLeftFireButtonNum * kBitsPerButton, + kRightFireButtonShift = kRightFireButtonNum * kBitsPerButton, + kOneButtonShift = kOneButtonNum * kBitsPerButton, + kTwoButtonShift = kTwoButtonNum * kBitsPerButton, + kThreeButtonShift = kThreeButtonNum * kBitsPerButton, + kFourButtonShift = kFourButtonNum * kBitsPerButton, + kMod1ButtonShift = kMod1ButtonNum * kBitsPerButton, + kMod2ButtonShift = kMod2ButtonNum * kBitsPerButton, + kMod3ButtonShift = kMod3ButtonNum * kBitsPerButton +}; + +enum { + kAllUpBits = (kButtonUp << kUpButtonShift) | + (kButtonUp << kLeftButtonShift) | + (kButtonUp << kDownButtonShift) | + (kButtonUp << kRightButtonShift) | + (kButtonUp << kLeftFireButtonShift) | + (kButtonUp << kRightFireButtonShift) | + (kButtonUp << kOneButtonShift) | + (kButtonUp << kTwoButtonShift) | + (kButtonUp << kThreeButtonShift) | + (kButtonUp << kFourButtonShift) | + (kButtonUp << kMod1ButtonShift) | + (kButtonUp << kMod2ButtonShift) | + (kButtonUp << kMod3ButtonShift), + kDirectionBits = (kButtonDownMask << kUpButtonShift) | + (kButtonDownMask << kLeftButtonShift) | + (kButtonDownMask << kDownButtonShift) | + (kButtonDownMask << kRightButtonShift), + kButtonBits = (kButtonDownMask << kLeftFireButtonShift) | + (kButtonDownMask << kRightFireButtonShift) | + (kButtonDownMask << kOneButtonShift) | + (kButtonDownMask << kTwoButtonShift) | + (kButtonDownMask << kThreeButtonShift) | + (kButtonDownMask << kFourButtonShift) | + (kButtonDownMask << kMod1ButtonShift) | + (kButtonDownMask << kMod2ButtonShift) | + (kButtonDownMask << kMod3ButtonShift), + kAllButtonDownBits = kDirectionBits | kButtonBits, + kAllAutoBits = (kAutoButtonMask << kUpButtonShift) | + (kAutoButtonMask << kLeftButtonShift) | + (kAutoButtonMask << kDownButtonShift) | + (kAutoButtonMask << kRightButtonShift) | + (kAutoButtonMask << kLeftFireButtonShift) | + (kAutoButtonMask << kRightFireButtonShift) | + (kAutoButtonMask << kOneButtonShift) | + (kAutoButtonMask << kTwoButtonShift) | + (kAutoButtonMask << kThreeButtonShift) | + (kAutoButtonMask << kFourButtonShift) | + (kAutoButtonMask << kMod1ButtonShift) | + (kAutoButtonMask << kMod2ButtonShift) | + (kAutoButtonMask << kMod3ButtonShift), + + kFilterUpButton = kButtonDownMask << kUpButtonShift, + kFilterUpAuto = kAutoButtonMask << kUpButtonShift, + kFilterUpButtonAny = kFilterUpButton | kFilterUpAuto, + kFilterLeftButton = kButtonDownMask << kLeftButtonShift, + kFilterLeftAuto = kAutoButtonMask << kLeftButtonShift, + kFilterLeftButtonAny = kFilterLeftButton | kFilterLeftAuto, + kFilterDownButton = kButtonDownMask << kDownButtonShift, + kFilterDownAuto = kAutoButtonMask << kDownButtonShift, + kFilterDownButtonAny = kFilterDownButton | kFilterDownAuto, + kFilterRightButton = kButtonDownMask << kRightButtonShift, + kFilterRightAuto = kAutoButtonMask << kRightButtonShift, + kFilterRightButtonAny = kFilterRightButton | kFilterRightAuto, + kFilterLeftFireButton = kButtonDownMask << kLeftFireButtonShift, + kFilterLeftFireAuto = kAutoButtonMask << kLeftFireButtonShift, + kFilterLeftFireButtonAny = kFilterLeftFireButton | kFilterLeftFireAuto, + kFilterRightFireButton = kButtonDownMask << kRightFireButtonShift, + kFilterRightFireAuto = kAutoButtonMask << kRightFireButtonShift, + kFilterRightFireButtonAny = kFilterRightFireButton | kFilterRightFireAuto, + kFilterOneButton = kButtonDownMask << kOneButtonShift, + kFilterOneAuto = kAutoButtonMask << kOneButtonShift, + kFilterOneButtonAny = kFilterOneButton | kFilterOneAuto, + kFilterTwoButton = kButtonDownMask << kTwoButtonShift, + kFilterTwoAuto = kAutoButtonMask << kTwoButtonShift, + kFilterTwoButtonAny = kFilterTwoButton | kFilterTwoAuto, + kFilterThreeButton = kButtonDownMask << kThreeButtonShift, + kFilterThreeAuto = kAutoButtonMask << kThreeButtonShift, + kFilterThreeButtonAny = kFilterThreeButton | kFilterThreeAuto, + kFilterFourButton = kButtonDownMask << kFourButtonShift, + kFilterFourAuto = kAutoButtonMask << kFourButtonShift, + kFilterFourButtonAny = kFilterFourButton | kFilterFourAuto, + kFilterMod1Button = kButtonDownMask << kMod1ButtonShift, + kFilterMod1Auto = kAutoButtonMask << kMod1ButtonShift, + kFilterMod1ButtonAny = kFilterMod1Button | kFilterMod1Auto, + kFilterMod2Button = kButtonDownMask << kMod2ButtonShift, + kFilterMod2Auto = kAutoButtonMask << kMod2ButtonShift, + kFilterMod2ButtonAny = kFilterMod2Button | kFilterMod2Auto, + kFilterMod3Button = kButtonDownMask << kMod3ButtonShift, + kFilterMod3Auto = kAutoButtonMask << kMod3ButtonShift, + kFilterMod3ButtonAny = kFilterMod3Button | kFilterMod3Auto, + + kFilterNoInput = 0, + kFilterAllInput = kFilterUpButton | + kFilterUpAuto | + kFilterLeftButton | + kFilterLeftAuto | + kFilterDownButton | + kFilterDownAuto | + kFilterRightButton | + kFilterRightAuto | + kFilterLeftFireButton | + kFilterLeftFireAuto | + kFilterRightFireButton | + kFilterRightFireAuto | + kFilterOneButton | + kFilterOneAuto | + kFilterTwoButton | + kFilterTwoAuto | + kFilterThreeButton | + kFilterThreeAuto | + kFilterFourButton | + kFilterFourAuto | + kFilterMod1Button | + kFilterMod1Auto | + kFilterMod2Button | + kFilterMod2Auto | + kFilterMod3Button | + kFilterMod3Auto, + + kFilterAllDirections = kFilterUpButton | + kFilterUpAuto | + kFilterLeftButton | + kFilterLeftAuto | + kFilterDownButton | + kFilterDownAuto | + kFilterRightButton | + kFilterRightAuto, + + kFilterButtons = kFilterOneButton | + kFilterOneAuto | + kFilterTwoButton | + kFilterTwoAuto | + kFilterThreeButton | + kFilterThreeAuto | + kFilterFourButton | + kFilterFourAuto, + + kFilterFireButtons = kFilterLeftFireButton | + kFilterLeftFireAuto | + kFilterRightFireButton | + kFilterRightFireAuto, + + kFilterAllButtons = kFilterLeftFireButton | + kFilterLeftFireAuto | + kFilterRightFireButton | + kFilterRightFireAuto | + kFilterOneButton | + kFilterOneAuto | + kFilterTwoButton | + kFilterTwoAuto | + kFilterThreeButton | + kFilterThreeAuto | + kFilterFourButton | + kFilterFourAuto | + kFilterMod1Button | + kFilterMod1Auto | + kFilterMod2Button | + kFilterMod2Auto | + kFilterMod3Button | + kFilterMod3Auto, + + kFilterAllInputNoAuto = kFilterUpButton | + kFilterLeftButton | + kFilterDownButton | + kFilterRightButton | + kFilterLeftFireButton | + kFilterRightFireButton | + kFilterOneButton | + kFilterTwoButton | + kFilterThreeButton | + kFilterFourButton | + kFilterMod1Button | + kFilterMod2Button | + kFilterMod3Button +}; + +static const InputBits kHintInterruption = kFilterAllInputNoAuto; +static const InputBits kWarningInterruption = kFilterNoInput; +static const InputBits kOpticalInterruption = kFilterAllInputNoAuto; + +/* + + Buttons are defined as: + up, left, down, right direction buttons. + fireLeft, fireRight: fire buttons. + mod1, mod2, mod3: modifier buttons, similar to shift, control, etc. + a, b, c, d: general purpose buttons. + + button state is held as bits in a long word, two bits per button. + + Filter bits: + for each button, two bits are assigned for filtering. If bit 0 is set, the + corresponding button is available for "button down" input. If bit 1 is set, + the corresponding button is available for "auto down" input. Note that bit + 1 is meaningful only if bit 0 is set. + +*/ + +class Input { +friend int operator==(const Input &, const Input &); +friend int operator!=(const Input &, const Input &); +friend class InputDeviceManager; + +public: + Input() { clearInput(); } + + bool upButtonDown() const { return (_inputState & (kButtonStateBits << kUpButtonShift)) == (kButtonDown << kUpButtonShift); } + bool upButtonAutoDown() const { return (_inputState & (kButtonStateBits << kUpButtonShift)) == (kButtonAutoDown << kUpButtonShift); } + bool upButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kUpButtonShift)) != 0; } + + bool leftButtonDown() const { return (_inputState & (kButtonStateBits << kLeftButtonShift)) == (kButtonDown << kLeftButtonShift); } + bool leftButtonAutoDown() const { return (_inputState & (kButtonStateBits << kLeftButtonShift)) == (kButtonAutoDown << kLeftButtonShift); } + bool leftButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kLeftButtonShift)) != 0; } + + bool downButtonDown() const { return (_inputState & (kButtonStateBits << kDownButtonShift)) == (kButtonDown << kDownButtonShift); } + bool downButtonAutoDown() const { return (_inputState & (kButtonStateBits << kDownButtonShift)) == (kButtonAutoDown << kDownButtonShift); } + bool downButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kDownButtonShift)) != 0; } + + bool rightButtonDown() const { return (_inputState & (kButtonStateBits << kRightButtonShift)) == (kButtonDown << kRightButtonShift); } + bool rightButtonAutoDown() const { return (_inputState & (kButtonStateBits << kRightButtonShift)) == (kButtonAutoDown << kRightButtonShift); } + bool rightButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kRightButtonShift)) != 0; } + + bool leftFireButtonDown() const { return (_inputState & (kButtonStateBits << kLeftFireButtonShift)) == (kButtonDown << kLeftFireButtonShift); } + bool leftFireButtonAutoDown() const { return (_inputState & (kButtonStateBits << kLeftFireButtonShift)) == (kButtonAutoDown << kLeftFireButtonShift); } + bool leftFireButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kLeftFireButtonShift)) != 0; } + + bool rightFireButtonDown() const { return (_inputState & (kButtonStateBits << kRightFireButtonShift)) == (kButtonDown << kRightFireButtonShift); } + bool rightFireButtonAutoDown() const { return (_inputState & (kButtonStateBits << kRightFireButtonShift)) == (kButtonAutoDown << kRightFireButtonShift); } + bool rightFireButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kRightFireButtonShift)) != 0; } + + bool oneButtonDown() const { return (_inputState & (kButtonStateBits << kOneButtonShift)) == (kButtonDown << kOneButtonShift); } + bool oneButtonAutoDown() const { return (_inputState & (kButtonStateBits << kOneButtonShift)) == (kButtonAutoDown << kOneButtonShift); } + bool oneButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kOneButtonShift)) != 0; } + + bool twoButtonDown() const { return (_inputState & (kButtonStateBits << kTwoButtonShift)) == (kButtonDown << kTwoButtonShift); } + bool twoButtonAutoDown() const { return (_inputState & (kButtonStateBits << kTwoButtonShift)) == (kButtonAutoDown << kTwoButtonShift); } + bool twoButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kTwoButtonShift)) != 0; } + + bool threeButtonDown() const { return (_inputState & (kButtonStateBits << kThreeButtonShift)) == (kButtonDown << kThreeButtonShift); } + bool threeButtonAutoDown() const { return (_inputState & (kButtonStateBits << kThreeButtonShift)) == (kButtonAutoDown << kThreeButtonShift); } + bool threeButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kThreeButtonShift)) != 0; } + + bool fourButtonDown() const { return (_inputState & (kButtonStateBits << kFourButtonShift)) == (kButtonDown << kFourButtonShift); } + bool fourButtonAutoDown() const { return (_inputState & (kButtonStateBits << kFourButtonShift)) == (kButtonAutoDown << kFourButtonShift); } + bool fourButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kFourButtonShift)) != 0; } + + bool mod1ButtonDown() const { return (_inputState & (kButtonStateBits << kMod1ButtonShift)) == (kButtonDown << kMod1ButtonShift); } + bool mod1ButtonAutoDown() const { return (_inputState & (kButtonStateBits << kMod1ButtonShift)) == (kButtonAutoDown << kMod1ButtonShift); } + bool mod1ButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kMod1ButtonShift)) != 0; } + + bool mod2ButtonDown() const { return (_inputState & (kButtonStateBits << kMod2ButtonShift)) == (kButtonDown << kMod2ButtonShift); } + bool mod2ButtonAutoDown() const { return (_inputState & (kButtonStateBits << kMod2ButtonShift)) == (kButtonAutoDown << kMod2ButtonShift); } + bool mod2ButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kMod2ButtonShift)) != 0; } + + bool mod3ButtonDown() const { return (_inputState & (kButtonStateBits << kMod3ButtonShift)) == (kButtonDown << kMod3ButtonShift); } + bool mod3ButtonAutoDown() const { return (_inputState & (kButtonStateBits << kMod3ButtonShift)) == (kButtonAutoDown << kMod3ButtonShift); } + bool mod3ButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kMod3ButtonShift)) != 0; } + + bool allAutoInput() const { return (_inputState & kAllAutoBits) != 0; } + bool anyDirectionInput() const { return (_inputState & kDirectionBits) != 0; } + bool anyButtonInput() const { return (_inputState & kButtonBits) != 0; } + bool anyInput() const { return _inputState != 0; } + + void getInputLocation(Common::Point &where) const { where = _inputLocation; } + + bool anyInputBitSet(const InputBits bits) const { return (_inputState & bits) != 0; } + + bool isAltDown() const { return _altDown; } + bool isConsoleRequested() const { return _consoleRequested; } + + void clearInput() { + _inputState = kAllUpBits; + _inputLocation.x = 0; + _inputLocation.y = 0; + _consoleRequested = false; + _altDown = false; + } + +protected: + void setInputBits(const InputBits state) { _inputState = state; } + void setInputLocation(const Common::Point &where) { _inputLocation = where; } + void setConsoleRequested(bool consoleRequested) { _consoleRequested = consoleRequested; } + void setAltDown(bool altDown) { _altDown = altDown; } + + InputBits _inputState; + Common::Point _inputLocation; + bool _consoleRequested; + bool _altDown; +}; + +class InputHandler { +public: + static InputHandler *setInputHandler(InputHandler*); + static InputHandler *getCurrentHandler() { return _inputHandler; } + static void pollForInput(); + static void getInput(Input&, Hotspot*&); + static void readInputDevice(Input&); + static void invalHotspots() { _invalHotspots = true; } + static InputBits getCurrentFilter() { return _lastFilter; } + + InputHandler(InputHandler*); + virtual ~InputHandler(); + + virtual void setNextHandler(InputHandler *nextHandler) { _nextHandler = nextHandler; } + virtual InputHandler *getNextHandler() { return _nextHandler; } + + virtual void handleInput(const Input &, const Hotspot *); + virtual void clickInHotspot(const Input &, const Hotspot *); + + virtual void activateHotspots(); + virtual void updateCursor(const Common::Point, const Hotspot *); + virtual bool isClickInput(const Input &, const Hotspot *); + virtual bool wantsCursor(); + + virtual bool releaseInputFocus() { return true; } + virtual void grabInputFocus() {} + + // This returns bits set for what kinds of input to accept. + virtual InputBits getInputFilter(); + + // This returns bits defining what input constitutes a "click." + virtual InputBits getClickFilter(); + + virtual void allowInput(const bool allow) { _allowInput = allow; } + +protected: + static InputHandler *_inputHandler; + static bool _invalHotspots; + static InputBits _lastFilter; + + InputHandler *_nextHandler; + bool _allowInput; +}; + + +/* + + Tracker implements "dragging". A Tracker can receive a startTracking message, + which causes it to be the current tracker, as well as setting it up as the current + input handler. In addition, only one tracker can be tracking at a time, and no + other handler can be set up as the current handler until the track finishes. By + default, there is no next input handler for a Tracker, but this behavior can be + overridden if desired. + +*/ + +class Tracker : public InputHandler { +public: + Tracker() : InputHandler(0) {} + virtual ~Tracker() {} + + virtual void handleInput(const Input &, const Hotspot *); + virtual bool stopTrackingInput(const Input &) { return false; } + + virtual void startTracking(const Input &); + virtual void stopTracking(const Input &); + virtual void continueTracking(const Input &) {} + + bool isTracking() { return this == _currentTracker; } + bool isClickInput(const Input &, const Hotspot *); + + bool releaseInputFocus() { return !isTracking(); } + +protected: + static Tracker *_currentTracker; + + InputHandler *_savedHandler; +}; + +class JMPPPInput { +public: + static bool isMenuButtonPressInput(const Input &input) { return input.twoButtonDown(); } + + static InputBits getClickInputFilter() { return kFilterTwoButton; } + static bool isClickInput(const Input &input) { return input.twoButtonDown(); } + static bool isDraggingInput(const Input &input) { return input.twoButtonAnyDown(); } + static bool isPressingInput(const Input &input) { return input.twoButtonAnyDown(); } + + static bool isRaiseInventoryInput(const Input &input) { return input.leftFireButtonDown(); } + static bool isRaiseBiochipsInput(const Input &input) { return input.rightFireButtonDown(); } + static InputBits getItemPanelsInputFilter() { return kFilterLeftFireButton | kFilterRightFireButton; } + + static bool isToggleAIMiddleInput(const Input &input) { return input.threeButtonDown(); } + + static bool isToggleInfoInput(const Input &input) { return input.fourButtonDown(); } + + // Hmmmmm.... + static bool isEasterEggModifierInput(const Input &input) { return input.isAltDown(); } + + static bool isTogglePauseInput(const Input &input) { return input.mod3ButtonDown(); } +}; + +} // End of namespace Pegasus + +#define InputDevice (::Pegasus::InputDeviceManager::instance()) + +#endif diff --git a/engines/pegasus/interaction.h b/engines/pegasus/interaction.h new file mode 100644 index 0000000000..293ee6be83 --- /dev/null +++ b/engines/pegasus/interaction.h @@ -0,0 +1,110 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_INTERACTION_H +#define PEGASUS_INTERACTION_H + +#include "pegasus/input.h" +#include "pegasus/util.h" + +namespace Pegasus { + +static const InteractionID kNoInteractionID = -1; + +class Neighborhood; + +class GameInteraction : public IDObject, public InputHandler { +public: + GameInteraction(const InteractionID id, Neighborhood *nextHandler) : IDObject(id), InputHandler((InputHandler *)nextHandler) { + _isInteracting = false; + _savedHandler = 0; + _owner = nextHandler; + } + + // If the interaction is open (_isInteracting == true), it's too late to do anything + // about it here. + virtual ~GameInteraction() {} + + // startInteraction and stopInteraction are called by the outside world to + // start and stop the interaction sequence. + // isInteracting returns a bool indicating whether or not the interaction + // is going. + void startInteraction() { + if (!isInteracting()) { + openInteraction(); + initInteraction(); + _isInteracting = true; + _savedHandler = InputHandler::setInputHandler(this); + } + } + void stopInteraction() { + if (isInteracting()) { + closeInteraction(); + _isInteracting = false; + if (InputHandler::_inputHandler == this) + InputHandler::setInputHandler(_savedHandler); + } + } + void startOverInteraction() { + if (isInteracting()) + resetInteraction(); + } + bool isInteracting() const { return _isInteracting; } + Neighborhood *getOwner() const { return _owner; } + + virtual Common::String getBriefingMovie() { return ""; } + virtual Common::String getEnvScanMovie() { return ""; } + virtual long getNumHints() { return 0; } + virtual Common::String getHintMovie(uint) { return ""; } + virtual bool canSolve() { return false; } + + virtual void setSoundFXLevel(const uint16) {} + virtual void setAmbienceLevel(const uint16) {} + + virtual void doSolve() {} + +protected: + // Subclasses override openInteraction and closeInteraction to perform + // specific initialization and cleanup. Override resetInteraction to + // "start the interaction over." resetInteraction is called only when + // the interaction is already open. + // These functions are only called in pairs, never two opens or closes + // in a row. + virtual void openInteraction() {} + virtual void initInteraction() {} + virtual void closeInteraction() {} + virtual void resetInteraction() {} + + InputHandler *_savedHandler; + Neighborhood *_owner; + +private: + // Private so that only StartInteraction and StopInteraction can touch it. + bool _isInteracting; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/interface.cpp b/engines/pegasus/interface.cpp new file mode 100644 index 0000000000..d9d3865192 --- /dev/null +++ b/engines/pegasus/interface.cpp @@ -0,0 +1,667 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/compass.h" +#include "pegasus/energymonitor.h" +#include "pegasus/interface.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/biochips/biochipitem.h" +#include "pegasus/items/inventory/inventoryitem.h" + +namespace Pegasus { + +Interface *g_interface = 0; + +Interface::Interface() : InputHandler(0), _interfaceNotification(kInterfaceNotificationID, (NotificationManager *)((PegasusEngine *)g_engine)), + _currentItemSpot(kCurrentItemSpotID), _currentBiochipSpot(kCurrentBiochipSpotID), + _background1(kInterface1ID), _background2(kInterface2ID), _background3(kInterface3ID), + _background4(kInterface4ID), _datePicture(kDateID), _inventoryPush(kInventoryPushID), + _inventoryLid(kInventoryLidID, kNoDisplayElement), + _inventoryPanel(kNoDisplayElement, (InputHandler *)((PegasusEngine *)g_engine), ((PegasusEngine *)g_engine)->getItemsInventory()), + _biochipPush(kBiochipPushID), _biochipLid(kBiochipLidID, kNoDisplayElement), + _biochipPanel(kNoDisplayElement, (InputHandler *)((PegasusEngine *)g_engine), ((PegasusEngine *)g_engine)->getBiochipsInventory()) { + g_energyMonitor = 0; + _previousHandler = 0; + _inventoryRaised = false; + _biochipRaised = false; + _playingEndMessage = false; + g_interface = this; +} + +Interface::~Interface() { + throwAwayInterface(); + g_interface = 0; +} + +void Interface::throwAwayInterface() { + g_allHotspots.removeOneHotspot(kCurrentItemSpotID); + g_allHotspots.removeOneHotspot(kCurrentBiochipSpotID); + + throwAwayBackground(); + throwAwayDateMonitor(); + throwAwayEnergyMonitor(); + throwAwayAIArea(); + throwAwayCompass(); + throwAwayNotifications(); + throwAwayInventoryPanel(); + throwAwayBiochipPanel(); +} + +void Interface::validateBackground() { + if (!_background1.isSurfaceValid()) { + _background1.initFromPICTFile("Images/Interface/3DInterface Left"); + _background2.initFromPICTFile("Images/Interface/3DInterface Top"); + _background3.initFromPICTFile("Images/Interface/3DInterface Right"); + _background4.initFromPICTFile("Images/Interface/3DInterface Bottom"); + + _background1.setDisplayOrder(kBackground1Order); + _background1.startDisplaying(); + _background1.moveElementTo(kBackground1Left, kBackground1Top); + + _background2.setDisplayOrder(kBackground2Order); + _background2.startDisplaying(); + _background2.moveElementTo(kBackground2Left, kBackground2Top); + + _background3.setDisplayOrder(kBackground2Order); + _background3.startDisplaying(); + _background3.moveElementTo(kBackground3Left, kBackground3Top); + + _background4.setDisplayOrder(kBackground4Order); + _background4.startDisplaying(); + _background4.moveElementTo(kBackground4Left, kBackground4Top); + + _background1.show(); + _background2.show(); + _background3.show(); + _background4.show(); + } +} + +void Interface::throwAwayBackground() { + _background1.stopDisplaying(); + _background1.deallocateSurface(); + _background2.stopDisplaying(); + _background2.deallocateSurface(); + _background3.stopDisplaying(); + _background3.deallocateSurface(); + _background4.stopDisplaying(); + _background4.deallocateSurface(); +} + +void Interface::validateDateMonitor() { + if (!_datePicture.isSurfaceValid()) { + _datePicture.setDisplayOrder(kDateOrder); + _datePicture.startDisplaying(); + _datePicture.moveElementTo(kDateLeft, kDateTop); + _datePicture.show(); + } +} + +void Interface::throwAwayDateMonitor() { + _datePicture.stopDisplaying(); + _datePicture.deallocateSurface(); +} + +void Interface::setDate(const uint16 dateResID) { + validateDateMonitor(); + _datePicture.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, dateResID); + _datePicture.triggerRedraw(); +} + +void Interface::validateCompass() { + if (!g_compass) { + new Compass(); + g_compass->initCompass(); + g_compass->setDisplayOrder(kCompassOrder); + g_compass->startDisplaying(); + g_compass->moveElementTo(kCompassLeft, kCompassTop); + g_compass->show(); + } +} + +void Interface::throwAwayCompass() { + delete g_compass; +} + +void Interface::validateNotifications() { + _interfaceNotification.notifyMe(this, kInterfaceNotificationFlags, kInterfaceNotificationFlags); + _inventoryLidCallBack.setNotification(&_interfaceNotification); + _inventoryPushCallBack.setNotification(&_interfaceNotification); + _biochipLidCallBack.setNotification(&_interfaceNotification); + _biochipPushCallBack.setNotification(&_interfaceNotification); +} + +void Interface::throwAwayNotifications() { + _interfaceNotification.cancelNotification(this); +} + +void Interface::validateAIArea() { + if (!g_AIArea) { + new AIArea((InputHandler *)((PegasusEngine *)g_engine)); + if (g_AIArea) + g_AIArea->initAIArea(); + } +} + +void Interface::throwAwayAIArea() { + delete g_AIArea; +} + +void Interface::validateInventoryPanel() { + if (!_inventoryPanel.isSurfaceValid()) { + _inventoryPanel.initInventoryImage(&_inventoryPush); + _inventoryPanel.moveElementTo(kInventoryPushLeft, kInventoryPushTop); + _inventoryPush.setSlideDirection(kSlideUpMask); + _inventoryPush.setInAndOutElements(&_inventoryPanel, 0); + _inventoryPush.setDisplayOrder(kInventoryPushOrder); + _inventoryPush.startDisplaying(); + + _inventoryLid.useFileName("Images/Lids/Inventory Lid Sequence"); + _inventoryLid.useTransparent(true); + _inventoryLid.openFrameSequence(); + _inventoryLid.moveElementTo(kInventoryLidLeft, kInventoryLidTop); + _inventoryLid.setDisplayOrder(kInventoryLidOrder); + _inventoryLid.startDisplaying(); + + _inventoryPushCallBack.initCallBack(&_inventoryPush, kCallBackAtExtremes); + _inventoryLidCallBack.initCallBack(&_inventoryLid, kCallBackAtExtremes); + + _inventoryUp = false; + _inventoryRaised = false; + + Item *item = getCurrentInventoryItem(); + if (item) + item->select(); + } +} + +void Interface::throwAwayInventoryPanel() { + _inventoryPanel.stopDisplaying(); + _inventoryPanel.throwAwayInventoryImage(); + _inventoryPush.stopDisplaying(); + _inventoryLid.stopDisplaying(); + _inventoryLid.closeFrameSequence(); + _inventoryPushCallBack.releaseCallBack(); + _inventoryLidCallBack.releaseCallBack(); + + Item *item = getCurrentInventoryItem(); + if (item) + item->deselect(); + + _inventoryUp = false; + _inventoryRaised = false; +} + +void Interface::validateBiochipPanel() { + if (!_biochipPanel.isSurfaceValid()) { + _biochipPanel.initInventoryImage(&_biochipPush); + _biochipPanel.moveElementTo(kBiochipPushLeft, kBiochipPushTop); + _biochipPush.setSlideDirection(kSlideUpMask); + _biochipPush.setInAndOutElements(&_biochipPanel, 0); + _biochipPush.setDisplayOrder(kBiochipPushOrder); + _biochipPush.startDisplaying(); + + _biochipLid.useFileName("Images/Lids/Biochip Lid Sequence"); + _biochipLid.useTransparent(true); + _biochipLid.openFrameSequence(); + _biochipLid.moveElementTo(kBiochipLidLeft, kBiochipLidTop); + _biochipLid.setDisplayOrder(kBiochipLidOrder); + _biochipLid.startDisplaying(); + + _biochipPushCallBack.initCallBack(&_biochipPush, kCallBackAtExtremes); + _biochipLidCallBack.initCallBack(&_biochipLid, kCallBackAtExtremes); + + _biochipUp = false; + _biochipRaised = false; + + Item *item = getCurrentBiochip(); + if (item) + item->select(); + } +} + +void Interface::throwAwayBiochipPanel() { + _biochipPanel.stopDisplaying(); + _biochipPanel.throwAwayInventoryImage(); + _biochipPush.stopDisplaying(); + _biochipLid.stopDisplaying(); + _biochipLid.closeFrameSequence(); + _biochipPushCallBack.releaseCallBack(); + _biochipLidCallBack.releaseCallBack(); + + Item *item = getCurrentBiochip(); + if (item) + item->deselect(); + + _biochipUp = false; + _biochipRaised = false; +} + +void Interface::validateEnergyMonitor() { + if (!g_energyMonitor) + new EnergyMonitor(); +} + +void Interface::throwAwayEnergyMonitor() { + delete g_energyMonitor; +} + +void Interface::createInterface() { + validateBackground(); + validateDateMonitor(); + validateCompass(); + validateNotifications(); + validateAIArea(); + validateBiochipPanel(); + validateInventoryPanel(); + validateEnergyMonitor(); + + if (!g_allHotspots.findHotspotByID(kCurrentItemSpotID)) { + _currentItemSpot.setArea(Common::Rect(76, 334, 172, 430)); + _currentItemSpot.setHotspotFlags(kShellSpotFlag); + _currentItemSpot.setActive(); + g_allHotspots.push_back(&_currentItemSpot); + } + + if (!g_allHotspots.findHotspotByID(kCurrentBiochipSpotID)) { + _currentBiochipSpot.setArea(Common::Rect(364, 334, 460, 430)); + _currentBiochipSpot.setHotspotFlags(kShellSpotFlag); + _currentBiochipSpot.setActive(); + g_allHotspots.push_back(&_currentBiochipSpot); + } +} + +InventoryResult Interface::addInventoryItem(InventoryItem *item) { + return _inventoryPanel.addInventoryItem(item); +} + +InventoryResult Interface::removeInventoryItem(InventoryItem *item) { + return _inventoryPanel.removeInventoryItem(item); +} + +void Interface::removeAllItemsFromInventory() { + _inventoryPanel.removeAllItems(); +} + +InventoryItem *Interface::getCurrentInventoryItem() { + return (InventoryItem *)_inventoryPanel.getCurrentItem(); +} + +void Interface::setCurrentInventoryItem(InventoryItem *item) { + setCurrentInventoryItemID(item->getObjectID()); +} + +void Interface::setCurrentInventoryItemID(ItemID id) { + _inventoryPanel.setCurrentItemID(id); +} + +InventoryResult Interface::addBiochip(BiochipItem *item) { + return _biochipPanel.addInventoryItem(item); +} + +void Interface::removeAllItemsFromBiochips() { + _biochipPanel.removeAllItems(); +} + +BiochipItem *Interface::getCurrentBiochip() { + return (BiochipItem *)_biochipPanel.getCurrentItem(); +} + +void Interface::setCurrentBiochip(BiochipItem *item) { + setCurrentBiochipID(item->getObjectID()); +} + +void Interface::setCurrentBiochipID(ItemID id) { + _biochipPanel.setCurrentItemID(id); +} + +void Interface::receiveNotification(Notification *notification, const NotificationFlags flags) { + if (notification == &_interfaceNotification) { + switch (flags) { + case kInventoryLidOpenFlag: + inventoryLidOpen(true); + break; + case kInventoryLidClosedFlag: + inventoryLidClosed(); + break; + case kInventoryDrawerUpFlag: + inventoryDrawerUp(); + break; + case kInventoryDrawerDownFlag: + inventoryDrawerDown(true); + break; + case kBiochipLidOpenFlag: + biochipLidOpen(true); + break; + case kBiochipLidClosedFlag: + biochipLidClosed(); + break; + case kBiochipDrawerUpFlag: + biochipDrawerUp(); + break; + case kBiochipDrawerDownFlag: + biochipDrawerDown(true); + break; + } + } +} + +void Interface::raiseInventoryDrawer(const bool doCallBacks) { + if (!_biochipUp) + _previousHandler = InputHandler::getCurrentHandler(); + + InputHandler::setInputHandler(&_inventoryPanel); + _inventoryUp = true; + _inventoryPanel.activateInventoryPicture(); + + if (doCallBacks) { + _inventoryLidCallBack.setCallBackFlag(kInventoryLidOpenFlag); + _inventoryLidCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + } + + _inventoryLid.show(); + _inventoryPush.show(); + _inventoryLid.start(); +} + +void Interface::playEndMessage() { + raiseInventoryDrawerForMessage(); + _playingEndMessage = true; + _inventoryPanel.playEndMessage(&_inventoryPush); + lowerInventoryDrawerForMessage(); + _playingEndMessage = false; +} + +void Interface::raiseInventoryDrawerForMessage() { + _inventoryPanel.disableLooping(); + raiseInventoryDrawerSync(); +} + +void Interface::lowerInventoryDrawerForMessage() { + lowerInventoryDrawerSync(); +} + +void Interface::inventoryLidOpen(const bool doCallBacks) { + _inventoryLid.stop(); + + if (doCallBacks) { + _inventoryPushCallBack.setCallBackFlag(kInventoryDrawerUpFlag); + _inventoryPushCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + } + + FaderMoveSpec moveSpec; + moveSpec.makeTwoKnotFaderSpec(60, 0, 0, 15, 1000); + _inventoryPush.startFader(moveSpec); +} + +void Interface::inventoryDrawerUp() { + _inventoryPush.stopFader(); + _inventoryPanel.panelUp(); + _inventoryRaised = true; +} + +bool Interface::isInventoryUp() { + return _inventoryRaised; +} + +bool Interface::isInventoryDown() { + return !_inventoryUp; +} + +void Interface::lowerInventoryDrawer(const bool doCallBacks) { + if (_inventoryRaised) { + _inventoryRaised = false; + + if (!_playingEndMessage) + _inventoryPanel.deactivateInventoryPicture(); + + if (doCallBacks) { + _inventoryPushCallBack.setCallBackFlag(kInventoryDrawerDownFlag); + _inventoryPushCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + } + + FaderMoveSpec moveSpec; + moveSpec.makeTwoKnotFaderSpec(60, 0, 1000, 15, 0); + _inventoryPush.startFader(moveSpec); + } +} + +void Interface::inventoryDrawerDown(const bool doCallBacks) { + _inventoryPush.stopFader(); + + if (doCallBacks) { + _inventoryLidCallBack.setCallBackFlag(kInventoryLidClosedFlag); + _inventoryLidCallBack.scheduleCallBack(kTriggerAtStart, 0, 0); + } + + _inventoryLid.setRate(-1); +} + +void Interface::inventoryLidClosed() { + _inventoryLid.stop(); + + if (!_biochipUp) + InputHandler::setInputHandler(_previousHandler); + + _inventoryLid.hide(); + _inventoryPush.hide(); + _inventoryUp = false; +} + +void Interface::raiseBiochipDrawer(const bool doCallBacks) { + if (!_inventoryUp) + _previousHandler = InputHandler::getCurrentHandler(); + + InputHandler::setInputHandler(&_biochipPanel); + _biochipUp = true; + _biochipPanel.activateInventoryPicture(); + + if (doCallBacks) { + _biochipLidCallBack.setCallBackFlag(kBiochipLidOpenFlag); + _biochipLidCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + } + + _biochipLid.show(); + _biochipPush.show(); + _biochipLid.start(); +} + +void Interface::biochipLidOpen(const bool doCallBacks) { + _biochipLid.stop(); + + if (doCallBacks) { + _biochipPushCallBack.setCallBackFlag(kBiochipDrawerUpFlag); + _biochipPushCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + } + + FaderMoveSpec moveSpec; + moveSpec.makeTwoKnotFaderSpec(60, 0, 0, 9, 1000); + _biochipPush.startFader(moveSpec); +} + +void Interface::biochipDrawerUp() { + _biochipPush.stopFader(); + _biochipPanel.panelUp(); + _biochipRaised = true; +} + +void Interface::lowerBiochipDrawer(const bool doCallBacks) { + if (_biochipRaised) { + _biochipRaised = false; + _biochipPanel.deactivateInventoryPicture(); + + if (doCallBacks) { + _biochipPushCallBack.setCallBackFlag(kBiochipDrawerDownFlag); + _biochipPushCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + } + + FaderMoveSpec moveSpec; + moveSpec.makeTwoKnotFaderSpec(60, 0, 1000, 9, 0); + _biochipPush.startFader(moveSpec); + } +} + +void Interface::biochipDrawerDown(const bool doCallBacks) { + _biochipPush.stopFader(); + + if (doCallBacks) { + _biochipLidCallBack.setCallBackFlag(kBiochipLidClosedFlag); + _biochipLidCallBack.scheduleCallBack(kTriggerAtStart, 0, 0); + } + + _biochipLid.setRate(-1); +} + +void Interface::biochipLidClosed() { + _biochipLid.stop(); + + if (!_inventoryUp) + InputHandler::setInputHandler(_previousHandler); + + _biochipLid.hide(); + _biochipPush.hide(); + _biochipUp = false; +} + +void Interface::calibrateCompass() { + uint32 currentValue = g_compass->getFaderValue(); + FaderMoveSpec compassMove; + compassMove.makeTwoKnotFaderSpec(15, 0, currentValue, 30, currentValue + 360); + + g_compass->startFader(compassMove); + + PegasusEngine *vm = (PegasusEngine *)g_engine; + + while (g_compass->isFading()) { + vm->refreshDisplay(); + g_system->delayMillis(10); + } + + vm->refreshDisplay(); + g_compass->setFaderValue(currentValue); +} + +void Interface::calibrateEnergyBar() { + g_energyMonitor->calibrateEnergyBar(); +} + +void Interface::raiseInventoryDrawerSync() { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + raiseInventoryDrawer(false); + + while (_inventoryLid.isRunning()) { + vm->checkCallBacks(); + vm->refreshDisplay(); + g_system->delayMillis(10); + } + + vm->refreshDisplay(); + inventoryLidOpen(false); + + while (_inventoryPush.isFading()) { + vm->checkCallBacks(); + vm->refreshDisplay(); + g_system->delayMillis(10); + } + + vm->refreshDisplay(); + inventoryDrawerUp(); +} + +void Interface::lowerInventoryDrawerSync() { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + lowerInventoryDrawer(false); + + while (_inventoryPush.isFading()) { + vm->checkCallBacks(); + vm->refreshDisplay(); + g_system->delayMillis(10); + } + + vm->refreshDisplay(); + inventoryDrawerDown(false); + + while (_inventoryLid.isRunning()) { + vm->checkCallBacks(); + vm->refreshDisplay(); + g_system->delayMillis(10); + } + + vm->refreshDisplay(); + inventoryLidClosed(); +} + +void Interface::raiseBiochipDrawerSync() { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + raiseBiochipDrawer(false); + + while (_biochipLid.isRunning()) { + vm->checkCallBacks(); + vm->refreshDisplay(); + g_system->delayMillis(10); + } + + vm->refreshDisplay(); + biochipLidOpen(false); + + while (_biochipPush.isFading()) { + vm->checkCallBacks(); + vm->refreshDisplay(); + g_system->delayMillis(10); + } + + vm->refreshDisplay(); + biochipDrawerUp(); +} + +void Interface::lowerBiochipDrawerSync() { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + lowerBiochipDrawer(false); + + while (_biochipPush.isFading()) { + vm->checkCallBacks(); + vm->refreshDisplay(); + g_system->delayMillis(10); + } + + vm->refreshDisplay(); + biochipDrawerDown(false); + + while (_biochipLid.isRunning()) { + vm->checkCallBacks(); + vm->refreshDisplay(); + g_system->delayMillis(10); + } + + vm->refreshDisplay(); + biochipLidClosed(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/interface.h b/engines/pegasus/interface.h new file mode 100644 index 0000000000..a65d9a595a --- /dev/null +++ b/engines/pegasus/interface.h @@ -0,0 +1,148 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_INTERFACE_H +#define PEGASUS_INTERFACE_H + +#include "pegasus/hotspot.h" +#include "pegasus/input.h" +#include "pegasus/notification.h" +#include "pegasus/surface.h" +#include "pegasus/transition.h" +#include "pegasus/items/inventorypicture.h" + +namespace Pegasus { + +class BiochipItem; +class InventoryItem; + +class Interface : public InputHandler, public NotificationReceiver { +public: + Interface(); + virtual ~Interface(); + + void createInterface(); + + // Recalibration functions... + void calibrateCompass(); + void calibrateEnergyBar(); + void raiseInventoryDrawerSync(); + void lowerInventoryDrawerSync(); + void raiseBiochipDrawerSync(); + void lowerBiochipDrawerSync(); + + void raiseInventoryDrawer(const bool doCallBacks = true); + void raiseBiochipDrawer(const bool doCallBacks = true); + void lowerInventoryDrawer(const bool doCallBacks = true); + void lowerBiochipDrawer(const bool doCallBacks = true); + + void raiseInventoryDrawerForMessage(); + void lowerInventoryDrawerForMessage(); + bool isInventoryUp(); + bool isInventoryDown(); + + InventoryResult addInventoryItem(InventoryItem *); + InventoryResult removeInventoryItem(InventoryItem *); + void removeAllItemsFromInventory(); + InventoryItem *getCurrentInventoryItem(); + void setCurrentInventoryItem(InventoryItem *); + void setCurrentInventoryItemID(ItemID); + InventoryResult addBiochip(BiochipItem *); + void removeAllItemsFromBiochips(); + BiochipItem *getCurrentBiochip(); + void setCurrentBiochip(BiochipItem *); + void setCurrentBiochipID(ItemID); + + void setDate(const uint16); + + void playEndMessage(); + + void throwAwayInterface(); + +protected: + void validateBackground(); + void validateDateMonitor(); + void validateCompass(); + void validateNotifications(); + void validateAIArea(); + void validateInventoryPanel(); + void validateBiochipPanel(); + void validateEnergyMonitor(); + + void throwAwayBackground(); + void throwAwayDateMonitor(); + void throwAwayCompass(); + void throwAwayNotifications(); + void throwAwayAIArea(); + void throwAwayInventoryPanel(); + void throwAwayBiochipPanel(); + void throwAwayEnergyMonitor(); + + void receiveNotification(Notification *, const NotificationFlags); + void inventoryLidOpen(const bool doCallBacks); + void inventoryLidClosed(); + void inventoryDrawerUp(); + void inventoryDrawerDown(const bool doCallBacks); + void biochipLidOpen(const bool doCallBacks); + void biochipLidClosed(); + void biochipDrawerUp(); + void biochipDrawerDown(const bool doCallBacks); + + Picture _background1; + Picture _background2; + Picture _background3; + Picture _background4; + + Picture _datePicture; + + InputHandler *_previousHandler; + + Push _inventoryPush; + SpriteSequence _inventoryLid; + NotificationCallBack _inventoryPushCallBack; + NotificationCallBack _inventoryLidCallBack; + InventoryItemsPicture _inventoryPanel; + bool _inventoryUp, _inventoryRaised; + + Push _biochipPush; + SpriteSequence _biochipLid; + NotificationCallBack _biochipPushCallBack; + NotificationCallBack _biochipLidCallBack; + BiochipPicture _biochipPanel; + bool _biochipUp, _biochipRaised; + + Hotspot _currentItemSpot; + Hotspot _currentBiochipSpot; + + Notification _interfaceNotification; + + bool _playingEndMessage; +}; + +extern Interface *g_interface; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/autodragger.cpp b/engines/pegasus/items/autodragger.cpp new file mode 100644 index 0000000000..40bad14a89 --- /dev/null +++ b/engines/pegasus/items/autodragger.cpp @@ -0,0 +1,91 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/elements.h" +#include "pegasus/items/autodragger.h" + +namespace Pegasus { + +AutoDragger::AutoDragger() { + _draggingElement = NULL; + _lastTime = 0; + initCallBack(this, kCallBackAtExtremes); +} + +void AutoDragger::autoDrag(DisplayElement *dragElement, const Common::Point &startPoint, const Common::Point &stopPoint, + TimeValue dragTime, TimeScale dragScale) { + _draggingElement = dragElement; + + if (_draggingElement) { + _startLocation = startPoint; + _stopLocation = stopPoint; + _lastTime = 0; + _done = false; + _draggingElement->moveElementTo(_startLocation.x, _startLocation.y); + setScale(dragScale); + setSegment(0, dragTime); + setTime(0); + scheduleCallBack(kTriggerAtStop, 0, 0); + startIdling(); + start(); + } else { + stopDragging(); + } +} + +void AutoDragger::stopDragging() { + cancelCallBack(); + stopIdling(); + _draggingElement = 0; + _startLocation = Common::Point(); + _stopLocation = Common::Point(); + _lastTime = 0; + _done = true; +} + +bool AutoDragger::isDragging() { + return isIdling(); +} + +void AutoDragger::useIdleTime() { + TimeValue thisTime = getTime(); + + if (thisTime != _lastTime) { + int32 offsetX = (_stopLocation.x - _startLocation.x) * (int32)thisTime / (int32)getDuration(); + int32 offsetY = (_stopLocation.y - _startLocation.y) * (int32)thisTime / (int32)getDuration(); + _draggingElement->moveElementTo(_startLocation.x + offsetX, _startLocation.y + offsetY); + _lastTime = thisTime; + } + + if (_done) + stopDragging(); +} + +void AutoDragger::callBack() { + if (isIdling()) + _done = true; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/autodragger.h b/engines/pegasus/items/autodragger.h new file mode 100644 index 0000000000..6783fdf9a3 --- /dev/null +++ b/engines/pegasus/items/autodragger.h @@ -0,0 +1,57 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_ITEMS_AUTODRAGGER_H +#define PEGASUS_ITEMS_AUTODRAGGER_H + +#include "pegasus/timers.h" + +namespace Pegasus { + +class DisplayElement; + +class AutoDragger : private Idler, private TimeBase, private TimeBaseCallBack { +public: + AutoDragger(); + virtual ~AutoDragger() {} + + void autoDrag(DisplayElement *, const Common::Point &, const Common::Point &, TimeValue, TimeScale); + bool isDragging(); + void stopDragging(); + +protected: + void useIdleTime(); + void callBack(); + + DisplayElement *_draggingElement; + Common::Point _startLocation, _stopLocation; + TimeValue _lastTime; + bool _done; +}; + +} // End of namespace Pegasus + +#endif + diff --git a/engines/pegasus/items/biochips/aichip.cpp b/engines/pegasus/items/biochips/aichip.cpp new file mode 100644 index 0000000000..cbcfc363e8 --- /dev/null +++ b/engines/pegasus/items/biochips/aichip.cpp @@ -0,0 +1,279 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/energymonitor.h" +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/biochips/aichip.h" +#include "pegasus/neighborhood/neighborhood.h" + +namespace Pegasus { + +// indexed by [number of hints][number of solves (0, 1, or 2)][which button to highlight] +static const ItemState s_highlightState[4][3][7] = { + { + {kAI000, -1, -1, -1, -1, kAI005, kAI006}, + {kAI010, -1, -1, -1, -1, kAI015, kAI016}, + {kAI020, -1, -1, -1, kAI024, -1, -1} + }, + { + {kAI100, kAI101, -1, -1, -1, kAI105, kAI106}, + {kAI110, kAI111, -1, -1, -1, kAI115, kAI116}, + {kAI120, kAI121, -1, -1, kAI124, kAI125, kAI126} + }, + { + {kAI200, kAI201, kAI202, -1, -1, kAI205, kAI206}, + {kAI210, kAI211, kAI212, -1, -1, kAI215, kAI216}, + {kAI220, kAI221, kAI222, -1, kAI224, kAI225, kAI226} + }, + { + {kAI300, kAI301, kAI302, kAI303, -1, kAI305, kAI306}, + {kAI310, kAI311, kAI312, kAI313, -1, kAI315, kAI316}, + {kAI320, kAI321, kAI322, kAI323, kAI324, kAI325, kAI326} + } +}; + +AIChip *g_AIChip = 0; + +AIChip::AIChip(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) : + BiochipItem(id, neighborhood, room, direction), _briefingSpot(kAIBriefingSpotID), _scanSpot(kAIScanSpotID), + _hint1Spot(kAIHint1SpotID), _hint2Spot(kAIHint2SpotID), _hint3Spot(kAIHint3SpotID), _solveSpot(kAISolveSpotID) { + _briefingSpot.setArea(Common::Rect(kAIMiddleAreaLeft + 10, kAIMiddleAreaTop + 27, kAIMiddleAreaLeft + 10 + 81, kAIMiddleAreaTop + 27 + 31)); + _briefingSpot.setHotspotFlags(kAIBiochipSpotFlag); + g_allHotspots.push_back(&_briefingSpot); + + _scanSpot.setArea(Common::Rect(kAIMiddleAreaLeft + 100, kAIMiddleAreaTop + 27, kAIMiddleAreaLeft + 100 + 81, kAIMiddleAreaTop + 27 + 31)); + _scanSpot.setHotspotFlags(kAIBiochipSpotFlag); + g_allHotspots.push_back(&_scanSpot); + + _hint1Spot.setArea(Common::Rect(kAIMiddleAreaLeft + 70, kAIMiddleAreaTop + 67, kAIMiddleAreaLeft + 70 + 21, kAIMiddleAreaTop + 67 + 21)); + _hint1Spot.setHotspotFlags(kAIBiochipSpotFlag); + g_allHotspots.push_back(&_hint1Spot); + + _hint2Spot.setArea(Common::Rect(kAIMiddleAreaLeft + 91, kAIMiddleAreaTop + 67, kAIMiddleAreaLeft + 91 + 20, kAIMiddleAreaTop + 67 + 21)); + _hint2Spot.setHotspotFlags(kAIBiochipSpotFlag); + g_allHotspots.push_back(&_hint2Spot); + + _hint3Spot.setArea(Common::Rect(kAIMiddleAreaLeft + 111, kAIMiddleAreaTop + 67, kAIMiddleAreaLeft + 111 + 20, kAIMiddleAreaTop + 67 + 21)); + _hint3Spot.setHotspotFlags(kAIBiochipSpotFlag); + g_allHotspots.push_back(&_hint3Spot); + + _solveSpot.setArea(Common::Rect(kAIMiddleAreaLeft + 131, kAIMiddleAreaTop + 67, kAIMiddleAreaLeft + 131 + 50, kAIMiddleAreaTop + 67 + 21)); + _solveSpot.setHotspotFlags(kAIBiochipSpotFlag); + g_allHotspots.push_back(&_solveSpot); + + _playingMovie = false; + setItemState(kAI000); + + g_AIChip = this; +} + +AIChip::~AIChip() { + g_AIChip = NULL; + + g_allHotspots.removeOneHotspot(kAIBriefingSpotID); + g_allHotspots.removeOneHotspot(kAIScanSpotID); + g_allHotspots.removeOneHotspot(kAIHint1SpotID); + g_allHotspots.removeOneHotspot(kAIHint2SpotID); + g_allHotspots.removeOneHotspot(kAIHint3SpotID); + g_allHotspots.removeOneHotspot(kAISolveSpotID); +} + +void AIChip::select() { + BiochipItem::select(); + setUpAIChip(); +} + +void AIChip::takeSharedArea() { + setUpAIChip(); +} + +void AIChip::setUpAIChip() { + if (!_playingMovie) { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + uint numSolves; + if (GameState.getWalkthroughMode()) { + if (vm->canSolve()) + numSolves = 2; + else + numSolves = 1; + } else { + numSolves = 0; + } + + setItemState(s_highlightState[vm->getNumHints()][numSolves][0]); + } +} + +// Only does something when there are hints or solves available. +void AIChip::setUpAIChipRude() { + if (!_playingMovie) { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + uint numSolves; + if (GameState.getWalkthroughMode()) { + if (vm->canSolve()) + numSolves = 2; + else + numSolves = 1; + } else { + numSolves = 0; + } + + uint numHints = vm->getNumHints(); + if (numSolves == 2 || numHints != 0) + setItemState(s_highlightState[numHints][numSolves][0]); + } +} + +void AIChip::activateAIHotspots() { + PegasusEngine *vm = (PegasusEngine *)g_engine; + _briefingSpot.setActive(); + _scanSpot.setActive(); + + switch (vm->getNumHints()) { + case 3: + _hint3Spot.setActive(); + // fall through + case 2: + _hint2Spot.setActive(); + // fall through + case 1: + _hint1Spot.setActive(); + break; + } + + if (GameState.getWalkthroughMode() && vm->canSolve()) + _solveSpot.setActive(); +} + +void AIChip::showBriefingClicked() { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + _playingMovie = true; + + uint numSolves; + if (GameState.getWalkthroughMode()) { + if (vm->canSolve()) + numSolves = 2; + else + numSolves = 1; + } else { + numSolves = 0; + } + + ItemState newState = s_highlightState[vm->getNumHints()][numSolves][kAIBriefingSpotID - kAIHint1SpotID + 1]; + if (newState != -1) + setItemState(newState); +} + +void AIChip::showEnvScanClicked() { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + _playingMovie = true; + + uint numSolves; + if (GameState.getWalkthroughMode()) { + if (vm->canSolve()) + numSolves = 2; + else + numSolves = 1; + } else { + numSolves = 0; + } + + ItemState newState = s_highlightState[vm->getNumHints()][numSolves][kAIScanSpotID - kAIHint1SpotID + 1]; + + if (newState != -1) + setItemState(newState); +} + +void AIChip::clearClicked() { + _playingMovie = false; + setUpAIChip(); +} + +void AIChip::clickInAIHotspot(HotSpotID id) { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + Common::String movieName; + + switch (id) { + case kAIBriefingSpotID: + movieName = vm->getBriefingMovie(); + break; + case kAIScanSpotID: + movieName = vm->getEnvScanMovie(); + break; + case kAIHint1SpotID: + movieName = vm->getHintMovie(1); + break; + case kAIHint2SpotID: + movieName = vm->getHintMovie(2); + break; + case kAIHint3SpotID: + movieName = vm->getHintMovie(3); + break; + case kAISolveSpotID: + g_neighborhood->doSolve(); + break; + } + + ItemState state = getItemState(); + + if (!movieName.empty()) { + _playingMovie = true; + + uint numSolves; + if (GameState.getWalkthroughMode()) { + if (vm->canSolve()) + numSolves = 2; + else + numSolves = 1; + } else { + numSolves = 0; + } + + ItemState newState = s_highlightState[vm->getNumHints()][numSolves][id - kAIHint1SpotID + 1]; + + if (newState != -1) + setItemState(newState); + + if (g_AIArea) { + vm->prepareForAIHint(movieName); + g_AIArea->playAIMovie(kRightAreaSignature, movieName, false, kHintInterruption); + vm->cleanUpAfterAIHint(movieName); + } + + if (newState != -1) + setItemState(state); + + _playingMovie = false; + } +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/biochips/aichip.h b/engines/pegasus/items/biochips/aichip.h new file mode 100644 index 0000000000..7a33953612 --- /dev/null +++ b/engines/pegasus/items/biochips/aichip.h @@ -0,0 +1,69 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_ITEMS_BIOCHIPS_AICHIP_H +#define PEGASUS_ITEMS_BIOCHIPS_AICHIP_H + +#include "pegasus/hotspot.h" +#include "pegasus/items/biochips/biochipitem.h" + +namespace Pegasus { + +class AIChip : public BiochipItem { +public: + AIChip(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant); + virtual ~AIChip(); + + void select(); + + void setUpAIChip(); + + // Called to set up the AI chip when the AI chip is the current chip but does not + // own the center area. + void setUpAIChipRude(); + void activateAIHotspots(); + void clickInAIHotspot(HotSpotID); + + void takeSharedArea(); + + void showBriefingClicked(); + void showEnvScanClicked(); + void clearClicked(); + +protected: + Hotspot _briefingSpot; + Hotspot _scanSpot; + Hotspot _hint1Spot; + Hotspot _hint2Spot; + Hotspot _hint3Spot; + Hotspot _solveSpot; + bool _playingMovie; +}; + +extern AIChip *g_AIChip; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/biochips/biochipitem.cpp b/engines/pegasus/items/biochips/biochipitem.cpp new file mode 100644 index 0000000000..5686948937 --- /dev/null +++ b/engines/pegasus/items/biochips/biochipitem.cpp @@ -0,0 +1,95 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "common/stream.h" + +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/biochips/biochipitem.h" + +namespace Pegasus { + +BiochipItem::BiochipItem(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) : + Item(id, neighborhood, room, direction) { + + PegasusEngine *vm = (PegasusEngine *)g_engine; + + Common::SeekableReadStream *biochipInfo = vm->_resFork->getResource(MKTAG('B', 'i', 'o', 'I'), kItemBaseResID + id); + if (biochipInfo) { + _biochipInfoPanelTime = biochipInfo->readUint32BE(); + delete biochipInfo; + } else { + _biochipInfoPanelTime = 0; + } + + Common::SeekableReadStream *rightInfo = vm->_resFork->getResource(MKTAG('R', 'g', 'h', 't'), kItemBaseResID + id); + if (!rightInfo) + error("Could not find right info for biochip %d", id); + + _rightAreaInfo = readItemState(rightInfo); + delete rightInfo; + + setItemState(kNormalItem); +} + +BiochipItem::~BiochipItem() { + delete[] _rightAreaInfo.entries; +} + +ItemType BiochipItem::getItemType() { + return kBiochipItemType; +} + +TimeValue BiochipItem::getRightAreaTime() const { + if (!_rightAreaInfo.entries) + return 0xffffffff; + + TimeValue time; + ItemState state; + + findItemStateEntryByState(_rightAreaInfo, _itemState, time); + if (time == 0xffffffff) + getItemStateEntry(_rightAreaInfo, 0, state, time); + + return time; +} + +// Must affect images in right area. +void BiochipItem::select() { + Item::select(); + + if (g_AIArea) + g_AIArea->setAIAreaToTime(kBiochipSignature, kRightAreaSignature, getRightAreaTime()); +} + +void BiochipItem::deselect() { + Item::deselect(); + + if (g_AIArea) + g_AIArea->setAIAreaToTime(kBiochipSignature, kRightAreaSignature, 0xffffffff); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/biochips/biochipitem.h b/engines/pegasus/items/biochips/biochipitem.h new file mode 100644 index 0000000000..2039e80c6f --- /dev/null +++ b/engines/pegasus/items/biochips/biochipitem.h @@ -0,0 +1,54 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_ITEMS_BIOCHIPS_BIOCHIPITEM_H +#define PEGASUS_ITEMS_BIOCHIPS_BIOCHIPITEM_H + +#include "pegasus/items/item.h" + +namespace Pegasus { + +class BiochipItem : public Item { +public: + BiochipItem(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant); + virtual ~BiochipItem(); + + virtual ItemType getItemType(); + + TimeValue getPanelTime() const { return _biochipInfoPanelTime; } + TimeValue getRightAreaTime() const; + + // Must affect images in right area. + virtual void select(); + virtual void deselect(); + +protected: + TimeValue _biochipInfoPanelTime; + ItemStateInfo _rightAreaInfo; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/biochips/mapchip.cpp b/engines/pegasus/items/biochips/mapchip.cpp new file mode 100644 index 0000000000..69050d5193 --- /dev/null +++ b/engines/pegasus/items/biochips/mapchip.cpp @@ -0,0 +1,106 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/gamestate.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/biochips/mapchip.h" +#include "pegasus/neighborhood/neighborhood.h" + +namespace Pegasus { + +MapChip *g_map = 0; + +MapChip::MapChip(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) : + BiochipItem(id, neighborhood, room, direction) { + g_map = this; + setItemState(kMapUnavailable); +} + +MapChip::~MapChip() { + g_map = 0; +} + +void MapChip::writeToStream(Common::WriteStream *stream) { + return _image.writeToStream(stream); +} + +void MapChip::readFromStream(Common::ReadStream *stream) { + return _image.readFromStream(stream); +} + +void MapChip::select() { + BiochipItem::select(); + moveToMapLocation(GameState.getCurrentNeighborhood(), GameState.getCurrentRoom(), GameState.getCurrentDirection()); + _image.show(); +} + +void MapChip::takeSharedArea() { + _image.show(); +} + +void MapChip::giveUpSharedArea() { + _image.hide(); +} + +void MapChip::deselect() { + BiochipItem::deselect(); + _image.unloadImage(); +} + +void MapChip::moveToMapLocation(const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant dir) { + AirQuality airQuality; + + if (g_neighborhood) + airQuality = g_neighborhood->getAirQuality(room); + else + airQuality = kAirQualityGood; + + switch (neighborhood) { + case kMarsID: + if (airQuality == kAirQualityVacuum) { + if (room >= kMars35 && room <= kMars39) { + setItemState(kMapEngaged); + if (isSelected() && g_AIArea && g_AIArea->getMiddleAreaOwner() == kBiochipSignature) + _image.loadGearRoomIfNecessary(); + } else { + setItemState(kMapEngaged); + if (isSelected() && g_AIArea && g_AIArea->getMiddleAreaOwner() == kBiochipSignature) + _image.loadMazeIfNecessary(); + } + + _image.moveToMapLocation(neighborhood, room, dir); + } else { + _image.unloadImage(); + setItemState(kMapUnavailable); + } + break; + default: + _image.unloadImage(); + setItemState(kMapUnavailable); + break; + } +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/biochips/mapchip.h b/engines/pegasus/items/biochips/mapchip.h new file mode 100644 index 0000000000..6690090aa4 --- /dev/null +++ b/engines/pegasus/items/biochips/mapchip.h @@ -0,0 +1,64 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_ITEMS_BIOCHIPS_MAPCHIP_H +#define PEGASUS_ITEMS_BIOCHIPS_MAPCHIP_H + +#include "pegasus/items/biochips/biochipitem.h" +#include "pegasus/items/biochips/mapimage.h" + +namespace Common { + class ReadStream; + class WriteStream; +} + +namespace Pegasus { + +class MapChip : public BiochipItem { +public: + MapChip(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant); + virtual ~MapChip(); + + void select(); + void deselect(); + void takeSharedArea(); + void giveUpSharedArea(); + + void moveToMapLocation(const NeighborhoodID, const RoomID, const DirectionConstant); + + void writeToStream(Common::WriteStream *); + void readFromStream(Common::ReadStream *); + + bool beenToMaze() { return _image.anyFlagSet(); } + +protected: + MapImage _image; +}; + +extern MapChip *g_map; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/biochips/mapimage.cpp b/engines/pegasus/items/biochips/mapimage.cpp new file mode 100644 index 0000000000..9f4170d063 --- /dev/null +++ b/engines/pegasus/items/biochips/mapimage.cpp @@ -0,0 +1,443 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/items/biochips/mapimage.h" + +namespace Pegasus { + +#define FLAG_TO_INDEX(flag) ((flag) >> 2) +#define INDEX_TO_FLAG(index) ((index) << 2) + +#define ROOM_TO_INDEX(room) \ + (((room) >= kMars35 && (room) <= kMars39) ? ((room) - kMars35) : \ + (((room) == kMars60) ? (kMars39 - kMars35 + 1) : \ + ((room) - kMarsMaze004 + kMars39 - kMars35 + 2))) + +#define INDEX_TO_ROOM(index) \ + (((index) <= ROOM_TO_INDEX(kMars39)) ? \ + (((index) - ROOM_TO_INDEX(kMars35)) + kMars35) : \ + ((index) <= ROOM_TO_INDEX(kMars60,)) ? kMars60 : \ + ((((index) - ROOM_TO_INDEX(kMarsMaze004))) + kMarsMaze004)) + +#define ROOM_TO_FLAG(room, dir) (INDEX_TO_FLAG(ROOM_TO_INDEX(room)) | (dir)) + +#define FLAG_TO_ROOM(flag) (INDEX_TO_ROOM(FLAG_TO_INDEX(flag))) + +#define FLAG_TO_DIRECTION(flag) ((flag) & 3) + +static const int kGearRoomFlagLow = ROOM_TO_FLAG(kMars35, kNorth); +static const int kGearRoomFlagHigh = ROOM_TO_FLAG(kMars39, kWest); + +static const int kMazeFlagLow = ROOM_TO_FLAG(kMars60, kNorth); +static const int kMazeFlagHigh = ROOM_TO_FLAG(kMarsMaze200, kWest); + +static const CoordType kGearRoomScreenOffsetX = 49; +static const CoordType kGearRoomScreenOffsetY = 47; + +static const CoordType kGearRoomGridOriginX = 1; +static const CoordType kGearRoomGridOriginY = 4; + +static const CoordType kMazeScreenOffsetX = 16; +static const CoordType kMazeScreenOffsetY = 20; + +static const CoordType kMazeGridOriginX = 6; +static const CoordType kMazeGridOriginY = 1; + +static const CoordType kGridWidth = 4; +static const CoordType kGridHeight = 4; + +static const uint16 kMapOfMazePICTID = 906; +static const uint16 kMapOfGearRoomPICTID = 907; + +static const int s_mapCoords[MapImage::kNumMappingRooms][2] = { + /* kMars35 */ { 0, 0 }, + /* kMars36 */ { 1, 0 }, + /* kMars37 */ { 2, 0 }, + /* kMars38 */ { 3, 0 }, + /* kMars39 */ { 4, 0 }, + /* kMars60 */ { 19, 9 }, + /* kMarsMaze004 */ { 18, 9 }, + /* kMarsMaze005 */ { 18, 10 }, + /* kMarsMaze006 */ { 17, 10 }, + /* kMarsMaze007 */ { 16, 10 }, + /* kMarsMaze008 */ { 15, 10 }, + /* kMarsMaze009 */ { 14, 10 }, + /* kMarsMaze010 */ { 14, 9 }, + /* kMarsMaze011 */ { 14, 8 }, + /* kMarsMaze012 */ { 14, 7 }, + /* kMarsMaze015 */ { 16, 7 }, + /* kMarsMaze016 */ { 14, 11 }, + /* kMarsMaze017 */ { 14, 12 }, + /* kMarsMaze018 */ { 15, 12 }, + /* kMarsMaze019 */ { 16, 12 }, + /* kMarsMaze020 */ { 16, 13 }, + /* kMarsMaze021 */ { 16, 14 }, + /* kMarsMaze022 */ { 16, 15 }, + /* kMarsMaze023 */ { 17, 15 }, + /* kMarsMaze024 */ { 18, 15 }, + /* kMarsMaze025 */ { 18, 14 }, + /* kMarsMaze026 */ { 18, 13 }, + /* kMarsMaze027 */ { 18, 12 }, + /* kMarsMaze028 */ { 18, 11 }, + /* kMarsMaze031 */ { 19, 14 }, + /* kMarsMaze032 */ { 20, 14 }, + /* kMarsMaze033 */ { 20, 13 }, + /* kMarsMaze034 */ { 20, 12 }, + /* kMarsMaze035 */ { 20, 11 }, + /* kMarsMaze036 */ { 21, 11 }, + /* kMarsMaze037 */ { 15, 15 }, + /* kMarsMaze038 */ { 14, 15 }, + /* kMarsMaze039 */ { 13, 15 }, + /* kMarsMaze042 */ { 10, 15 }, + /* kMarsMaze043 */ { 9, 15 }, + /* kMarsMaze044 */ { 8, 15 }, + /* kMarsMaze045 */ { 7, 15 }, + /* kMarsMaze046 */ { 6, 15 }, + /* kMarsMaze047 */ { 5, 15 }, + /* kMarsMaze049 */ { 13, 14 }, + /* kMarsMaze050 */ { 12, 14 }, + /* kMarsMaze051 */ { 11, 14 }, + /* kMarsMaze052 */ { 10, 14 }, + /* kMarsMaze053 */ { 10, 13 }, + /* kMarsMaze054 */ { 9, 13 }, + /* kMarsMaze055 */ { 8, 13 }, + /* kMarsMaze056 */ { 8, 12 }, + /* kMarsMaze057 */ { 7, 12 }, + /* kMarsMaze058 */ { 12, 13 }, + /* kMarsMaze059 */ { 12, 12 }, + /* kMarsMaze060 */ { 12, 11 }, + /* kMarsMaze061 */ { 12, 10 }, + /* kMarsMaze063 */ { 12, 9 }, + /* kMarsMaze064 */ { 12, 8 }, + /* kMarsMaze065 */ { 12, 7 }, + /* kMarsMaze066 */ { 13, 7 }, + /* kMarsMaze067 */ { 15, 7 }, + /* kMarsMaze068 */ { 17, 7 }, + /* kMarsMaze069 */ { 18, 7 }, + /* kMarsMaze070 */ { 19, 7 }, + /* kMarsMaze071 */ { 20, 7 }, + /* kMarsMaze072 */ { 20, 6 }, + /* kMarsMaze074 */ { 20, 5 }, + /* kMarsMaze076 */ { 20, 4 }, + /* kMarsMaze078 */ { 20, 3 }, + /* kMarsMaze079 */ { 20, 2 }, + /* kMarsMaze081 */ { 20, 2 }, + /* kMarsMaze083 */ { 20, 0 }, + /* kMarsMaze084 */ { 19, 0 }, + /* kMarsMaze085 */ { 18, 0 }, + /* kMarsMaze086 */ { 17, 0 }, + /* kMarsMaze087 */ { 16, 0 }, + /* kMarsMaze088 */ { 15, 0 }, + /* kMarsMaze089 */ { 14, 0 }, + /* kMarsMaze090 */ { 13, 0 }, + /* kMarsMaze091 */ { 12, 0 }, + /* kMarsMaze092 */ { 11, 0 }, + /* kMarsMaze093 */ { 10, 0 }, + /* kMarsMaze098 */ { 10, 1 }, + /* kMarsMaze099 */ { 8, 2 }, + /* kMarsMaze100 */ { 9, 2 }, + /* kMarsMaze101 */ { 10, 2 }, + /* kMarsMaze104 */ { 13, 2 }, + /* kMarsMaze105 */ { 13, 3 }, + /* kMarsMaze106 */ { 13, 4 }, + /* kMarsMaze107 */ { 13, 5 }, + /* kMarsMaze108 */ { 14, 5 }, + /* kMarsMaze111 */ { 15, 5 }, + /* kMarsMaze113 */ { 16, 5 }, + /* kMarsMaze114 */ { 17, 5 }, + /* kMarsMaze115 */ { 18, 5 }, + /* kMarsMaze116 */ { 18, 4 }, + /* kMarsMaze117 */ { 18, 3 }, + /* kMarsMaze118 */ { 19, 3 }, + /* kMarsMaze119 */ { 18, 2 }, + /* kMarsMaze120 */ { 17, 2 }, + /* kMarsMaze121 */ { 16, 2 }, + /* kMarsMaze122 */ { 15, 2 }, + /* kMarsMaze123 */ { 15, 1 }, + /* kMarsMaze124 */ { 12, 4 }, + /* kMarsMaze125 */ { 11, 4 }, + /* kMarsMaze126 */ { 10, 4 }, + /* kMarsMaze127 */ { 10, 5 }, + /* kMarsMaze128 */ { 10, 6 }, + /* kMarsMaze129 */ { 9, 6 }, + /* kMarsMaze130 */ { 8, 6 }, + /* kMarsMaze131 */ { 7, 6 }, + /* kMarsMaze132 */ { 7, 7 }, + /* kMarsMaze133 */ { 7, 8 }, + /* kMarsMaze136 */ { 7, 11 }, + /* kMarsMaze137 */ { 6, 11 }, + /* kMarsMaze138 */ { 5, 11 }, + /* kMarsMaze139 */ { 5, 12 }, + /* kMarsMaze140 */ { 4, 12 }, + /* kMarsMaze141 */ { 5, 13 }, + /* kMarsMaze142 */ { 5, 14 }, + /* kMarsMaze143 */ { 4, 14 }, + /* kMarsMaze144 */ { 3, 14 }, + /* kMarsMaze145 */ { 3, 13 }, + /* kMarsMaze146 */ { 2, 13 }, + /* kMarsMaze147 */ { 1, 13 }, + /* kMarsMaze148 */ { 1, 14 }, + /* kMarsMaze149 */ { 1, 15 }, + /* kMarsMaze152 */ { 1, 12 }, + /* kMarsMaze153 */ { 1, 11 }, + /* kMarsMaze154 */ { 1, 10 }, + /* kMarsMaze155 */ { 1, 9 }, + /* kMarsMaze156 */ { 1, 8 }, + /* kMarsMaze157 */ { 2, 10 }, + /* kMarsMaze159 */ { 2, 8 }, + /* kMarsMaze160 */ { 2, 7 }, + /* kMarsMaze161 */ { 2, 6 }, + /* kMarsMaze162 */ { 3, 10 }, + /* kMarsMaze163 */ { 3, 9 }, + /* kMarsMaze164 */ { 3, 8 }, + /* kMarsMaze165 */ { 4, 8 }, + /* kMarsMaze166 */ { 5, 8 }, + /* kMarsMaze167 */ { 6, 8 }, + /* kMarsMaze168 */ { 3, 6 }, + /* kMarsMaze169 */ { 4, 6 }, + /* kMarsMaze170 */ { 5, 6 }, + /* kMarsMaze171 */ { 5, 5 }, + /* kMarsMaze172 */ { 5, 4 }, + /* kMarsMaze173 */ { 4, 4 }, + /* kMarsMaze174 */ { 3, 4 }, + /* kMarsMaze175 */ { 3, 5 }, + /* kMarsMaze177 */ { 8, 4 }, + /* kMarsMaze178 */ { 8, 3 }, + /* kMarsMaze179 */ { 7, 4 }, + /* kMarsMaze180 */ { 6, 4 }, + /* kMarsMaze181 */ { 6, 3 }, + /* kMarsMaze182 */ { 6, 2 }, + /* kMarsMaze183 */ { 6, 1 }, + /* kMarsMaze184 */ { 6, 0 }, + /* kMarsMaze187 */ { 3, 0 }, + /* kMarsMaze188 */ { 2, 0 }, + /* kMarsMaze189 */ { 1, 0 }, + /* kMarsMaze190 */ { 1, 1 }, + /* kMarsMaze191 */ { 1, 2 }, + /* kMarsMaze192 */ { 5, 2 }, + /* kMarsMaze193 */ { 4, 2 }, + /* kMarsMaze194 */ { 3, 2 }, + /* kMarsMaze195 */ { 3, 1 }, + /* kMarsMaze198 */ { 1, 3 }, + /* kMarsMaze199 */ { 1, 4 }, + /* kMarsMaze200 */ { 0, 4 } +}; + +MapImage::MapImage() : DisplayElement(kNoDisplayElement) { + _whichArea = kMapNoArea; + setBounds(kAIMiddleAreaLeft, kAIMiddleAreaTop, kAIMiddleAreaLeft + kAIMiddleAreaWidth, kAIMiddleAreaTop + kAIMiddleAreaHeight); + setDisplayOrder(kAIMiddleAreaOrder + 10); + startDisplaying(); + + _darkGreen = g_system->getScreenFormat().RGBToColor(64, 150, 10); + _lightGreen = g_system->getScreenFormat().RGBToColor(102, 239, 0); +} + +void MapImage::writeToStream(Common::WriteStream *stream) { + _mappedRooms.writeToStream(stream); +} + +void MapImage::readFromStream(Common::ReadStream *stream) { + _mappedRooms.readFromStream(stream); +} + +void MapImage::loadGearRoomIfNecessary() { + if (_whichArea != kMapGearRoom) { + _mapImage.getImageFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kMapOfGearRoomPICTID); + + Common::Rect bounds; + _mapImage.getSurfaceBounds(bounds); + _mapMask.allocateSurface(bounds); + _whichArea = kMapGearRoom; + + GraphicsManager *gfx = ((PegasusEngine *)g_engine)->_gfx; + gfx->setCurSurface(_mapMask.getSurface()); + + gfx->getCurSurface()->fillRect(bounds, g_system->getScreenFormat().RGBToColor(0xff, 0xff, 0xff)); + + for (int i = kGearRoomFlagLow; i <= kGearRoomFlagHigh; i++) + if (_mappedRooms.getFlag(i)) + addFlagToMask(i); + + gfx->setCurSurface(gfx->getWorkArea()); + show(); + } +} + +void MapImage::loadMazeIfNecessary() { + if (_whichArea != kMapMaze) { + _mapImage.getImageFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kMapOfMazePICTID); + + Common::Rect bounds; + _mapImage.getSurfaceBounds(bounds); + _mapMask.allocateSurface(bounds); + _whichArea = kMapMaze; + + GraphicsManager *gfx = ((PegasusEngine *)g_engine)->_gfx; + gfx->setCurSurface(_mapMask.getSurface()); + + gfx->getCurSurface()->fillRect(bounds, g_system->getScreenFormat().RGBToColor(0xff, 0xff, 0xff)); + + for (int i = kMazeFlagLow; i <= kMazeFlagHigh; i++) + if (_mappedRooms.getFlag(i)) + addFlagToMask(i); + + gfx->setCurSurface(gfx->getWorkArea()); + show(); + } +} + +void MapImage::unloadImage() { + _mapImage.deallocateSurface(); + _mapMask.deallocateSurface(); + hide(); + _whichArea = kMapNoArea; +} + +void MapImage::moveToMapLocation(const NeighborhoodID, const RoomID room, const DirectionConstant dir) { + GraphicsManager *gfx = ((PegasusEngine *)g_engine)->_gfx; + + int flag = ROOM_TO_FLAG(room, dir); + + if (!_mappedRooms.getFlag(flag)) { + _mappedRooms.setFlag(flag, true); + + if (_mapMask.isSurfaceValid()) { + gfx->setCurSurface(_mapMask.getSurface()); + addFlagToMask(flag); + gfx->setCurSurface(gfx->getWorkArea()); + } + } + + if (isDisplaying()) + triggerRedraw(); +} + +void MapImage::addFlagToMask(const int flag) { + Common::Rect r1; + getRevealedRects(flag, r1); + ((PegasusEngine *)g_engine)->_gfx->getCurSurface()->fillRect(r1, g_system->getScreenFormat().RGBToColor(0, 0, 0)); +} + +// This function can even be sensitive to open doors. +// clone2727 notices that it's not, though +void MapImage::getRevealedRects(const uint32 flag, Common::Rect &r1) { + CoordType gridX, gridY; + + switch (_whichArea) { + case kMapMaze: + gridX = kMazeGridOriginX; + gridY = kMazeGridOriginY; + break; + case kMapGearRoom: + gridX = kGearRoomGridOriginX; + gridY = kGearRoomGridOriginY; + break; + default: + return; + } + + int index = FLAG_TO_INDEX(flag); + gridX += s_mapCoords[index][0] * kGridWidth; + gridY += s_mapCoords[index][1] * kGridHeight; + + r1 = Common::Rect(gridX - 1, gridY - 1, gridX + kGridWidth + 1, gridY + kGridHeight + 1); +} + +void MapImage::drawPlayer() { + Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getCurSurface(); + + CoordType gridX, gridY; + + switch (_whichArea) { + case kMapMaze: + gridX = _bounds.left + kMazeScreenOffsetX + kMazeGridOriginX; + gridY = _bounds.top + kMazeScreenOffsetY + kMazeGridOriginY; + break; + case kMapGearRoom: + gridX = _bounds.left + kGearRoomScreenOffsetX + kGearRoomGridOriginX; + gridY = _bounds.top + kGearRoomScreenOffsetY + kGearRoomGridOriginY; + break; + default: + return; + } + + int index = ROOM_TO_INDEX(GameState.getCurrentRoom()); + gridX += s_mapCoords[index][0] * kGridWidth; + gridY += s_mapCoords[index][1] * kGridHeight; + + // This was intended to make little arrows + switch (GameState.getCurrentDirection()) { + case kNorth: + screen->drawLine(gridX + 1, gridY, gridX + 2, gridY, _darkGreen); + screen->drawLine(gridX, gridY + 1, gridX + 3, gridY + 1, _darkGreen); + screen->drawLine(gridX + 1, gridY + 1, gridX + 2, gridY + 1, _lightGreen); + screen->drawLine(gridX, gridY + 2, gridX + 3, gridY + 2, _lightGreen); + break; + case kSouth: + screen->drawLine(gridX + 1, gridY + 3, gridX + 2, gridY + 3, _darkGreen); + screen->drawLine(gridX, gridY + 2, gridX + 3, gridY + 2, _darkGreen); + screen->drawLine(gridX + 1, gridY + 2, gridX + 2, gridY + 2, _lightGreen); + screen->drawLine(gridX, gridY + 1, gridX + 3, gridY + 1, _lightGreen); + break; + case kEast: + screen->drawLine(gridX + 3, gridY + 1, gridX + 3, gridY + 2, _darkGreen); + screen->drawLine(gridX + 2, gridY, gridX + 2, gridY + 3, _darkGreen); + screen->drawLine(gridX + 2, gridY + 1, gridX + 2, gridY + 2, _lightGreen); + screen->drawLine(gridX + 1, gridY, gridX + 1, gridY + 3, _lightGreen); + break; + case kWest: + screen->drawLine(gridX, gridY + 1, gridX, gridY + 2, _darkGreen); + screen->drawLine(gridX + 1, gridY, gridX + 1, gridY + 3, _darkGreen); + screen->drawLine(gridX + 1, gridY + 1, gridX + 1, gridY + 2, _lightGreen); + screen->drawLine(gridX + 2, gridY, gridX + 2, gridY + 3, _lightGreen); + break; + } +} + +void MapImage::draw(const Common::Rect &) { + Common::Rect r1; + _mapImage.getSurfaceBounds(r1); + + Common::Rect r2 = r1; + switch (_whichArea) { + case kMapMaze: + r2.moveTo(_bounds.left + kMazeScreenOffsetX, _bounds.top + kMazeScreenOffsetY); + break; + case kMapGearRoom: + r2.moveTo(_bounds.left + kGearRoomScreenOffsetX, _bounds.top + kGearRoomScreenOffsetY); + break; + default: + return; + } + + _mapImage.copyToCurrentPortMasked(r1, r2, &_mapMask); + + drawPlayer(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/biochips/mapimage.h b/engines/pegasus/items/biochips/mapimage.h new file mode 100644 index 0000000000..49ad9945ee --- /dev/null +++ b/engines/pegasus/items/biochips/mapimage.h @@ -0,0 +1,84 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_ITEMS_BIOCHIPS_MAPIMAGE_H +#define PEGASUS_ITEMS_BIOCHIPS_MAPIMAGE_H + +#include "pegasus/elements.h" +#include "pegasus/surface.h" +#include "pegasus/util.h" +#include "pegasus/neighborhood/mars/constants.h" + +namespace Common { + class ReadStream; + class WriteStream; +} + +namespace Pegasus { + +class MapImage : public DisplayElement { +public: + MapImage(); + virtual ~MapImage() {} + + void writeToStream(Common::WriteStream *); + void readFromStream(Common::ReadStream *); + + void loadGearRoomIfNecessary(); + void loadMazeIfNecessary(); + void unloadImage(); + void moveToMapLocation(const NeighborhoodID, const RoomID, const DirectionConstant); + + void draw(const Common::Rect &); + + bool anyFlagSet() { return _mappedRooms.anyFlagSet(); } + + static const uint32 kNumMappingRooms = (kMars39 - kMars35 + 1) + (kMars60 - kMars60 + 1) + + (kMarsMaze200 - kMarsMaze004 + 1); + static const uint32 kNumMappingFlags = kNumMappingRooms * 4; + +protected: + enum MapArea { + kMapNoArea, + kMapMaze, + kMapGearRoom + }; + + void addFlagToMask(const int flag); + void getRevealedRects(const uint32, Common::Rect &); + void drawPlayer(); + + MapArea _whichArea; + + FlagsArray<byte, kNumMappingFlags> _mappedRooms; + + uint32 _darkGreen, _lightGreen; + + Surface _mapImage, _mapMask; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/biochips/opticalchip.cpp b/engines/pegasus/items/biochips/opticalchip.cpp new file mode 100644 index 0000000000..7b8858edae --- /dev/null +++ b/engines/pegasus/items/biochips/opticalchip.cpp @@ -0,0 +1,190 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/biochips/opticalchip.h" + +namespace Pegasus { + +OpticalChip *g_opticalChip = 0; + +OpticalChip::OpticalChip(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) : + BiochipItem(id, neighborhood, room, direction), _ariesHotspot(kAriesSpotID), _mercuryHotspot(kMercurySpotID), + _poseidonHotspot(kPoseidonSpotID) { + _ariesHotspot.setArea(Common::Rect(kAIMiddleAreaLeft + 60, kAIMiddleAreaTop + 27, kAIMiddleAreaLeft + 60 + 121, kAIMiddleAreaTop + 27 + 20)); + _ariesHotspot.setHotspotFlags(kOpticalBiochipSpotFlag); + g_allHotspots.push_back(&_ariesHotspot); + + _mercuryHotspot.setArea(Common::Rect(kAIMiddleAreaLeft + 60, kAIMiddleAreaTop + 47, kAIMiddleAreaLeft + 60 + 121, kAIMiddleAreaTop + 47 + 20)); + _mercuryHotspot.setHotspotFlags(kOpticalBiochipSpotFlag); + g_allHotspots.push_back(&_mercuryHotspot); + + _poseidonHotspot.setArea(Common::Rect(kAIMiddleAreaLeft + 60, kAIMiddleAreaTop + 67, kAIMiddleAreaLeft + 60 + 121, kAIMiddleAreaTop + 67 + 20)); + _poseidonHotspot.setHotspotFlags(kOpticalBiochipSpotFlag); + g_allHotspots.push_back(&_poseidonHotspot); + + setItemState(kOptical000); + + g_opticalChip = this; +} + +OpticalChip::~OpticalChip() { + g_allHotspots.removeOneHotspot(kAriesSpotID); + g_allHotspots.removeOneHotspot(kMercurySpotID); + g_allHotspots.removeOneHotspot(kPoseidonSpotID); +} + +void OpticalChip::writeToStream(Common::WriteStream *stream) { + BiochipItem::writeToStream(stream); + _opticalFlags.writeToStream(stream); +} + +void OpticalChip::readFromStream(Common::ReadStream *stream) { + BiochipItem::readFromStream(stream); + _opticalFlags.readFromStream(stream); +} + +void OpticalChip::addAries() { + _opticalFlags.setFlag(kOpticalAriesExposed, true); + setUpOpticalChip(); +} + +void OpticalChip::addMercury() { + _opticalFlags.setFlag(kOpticalMercuryExposed, true); + setUpOpticalChip(); +} + +void OpticalChip::addPoseidon() { + _opticalFlags.setFlag(kOpticalPoseidonExposed, true); + setUpOpticalChip(); +} + +void OpticalChip::setUpOpticalChip() { + if (_opticalFlags.getFlag(kOpticalAriesExposed)) { + if (_opticalFlags.getFlag(kOpticalMercuryExposed)) { + if (_opticalFlags.getFlag(kOpticalPoseidonExposed)) + setItemState(kOptical111); + else + setItemState(kOptical011); + } else { + if (_opticalFlags.getFlag(kOpticalPoseidonExposed)) + setItemState(kOptical101); + else + setItemState(kOptical001); + } + } else { + if (_opticalFlags.getFlag(kOpticalMercuryExposed)) { + if (_opticalFlags.getFlag(kOpticalPoseidonExposed)) + setItemState(kOptical110); + else + setItemState(kOptical010); + } else { + if (_opticalFlags.getFlag(kOpticalPoseidonExposed)) + setItemState(kOptical100); + else + setItemState(kOptical000); + } + } +} + +void OpticalChip::activateOpticalHotspots() { + if (_opticalFlags.getFlag(kOpticalAriesExposed)) + _ariesHotspot.setActive(); + if (_opticalFlags.getFlag(kOpticalMercuryExposed)) + _mercuryHotspot.setActive(); + if (_opticalFlags.getFlag(kOpticalPoseidonExposed)) + _poseidonHotspot.setActive(); +} + +void OpticalChip::clickInOpticalHotspot(HotSpotID id) { + playOpMemMovie(id); +} + +void OpticalChip::playOpMemMovie(HotSpotID id) { + Common::String movieName; + switch (id) { + case kAriesSpotID: + movieName = "Images/AI/Globals/OMAI"; + break; + case kMercurySpotID: + movieName = "Images/AI/Globals/OMMI"; + break; + case kPoseidonSpotID: + movieName = "Images/AI/Globals/OMPI"; + break; + } + + ItemState state = getItemState(), newState; + switch (state) { + case kOptical001: + newState = kOptical002; + break; + case kOptical010: + newState = kOptical020; + break; + case kOptical011: + if (id == kAriesSpotID) + newState = kOptical012; + else + newState = kOptical021; + break; + case kOptical100: + newState = kOptical200; + break; + case kOptical101: + if (id == kAriesSpotID) + newState = kOptical102; + else + newState = kOptical201; + break; + case kOptical110: + if (id == kMercurySpotID) + newState = kOptical120; + else + newState = kOptical210; + break; + case kOptical111: + if (id == kAriesSpotID) + newState = kOptical112; + else if (id == kMercurySpotID) + newState = kOptical121; + else + newState = kOptical211; + break; + case kOptical000: // Can never happen. + default: + error("Invalid optical chip state"); + } + + setItemState(newState); + + if (g_AIArea) + g_AIArea->playAIMovie(kRightAreaSignature, movieName, false, kOpticalInterruption); + + setItemState(state); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/biochips/opticalchip.h b/engines/pegasus/items/biochips/opticalchip.h new file mode 100644 index 0000000000..2f66f73d3a --- /dev/null +++ b/engines/pegasus/items/biochips/opticalchip.h @@ -0,0 +1,71 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_ITEMS_BIOCHIPS_OPTICALCHIP_H +#define PEGASUS_ITEMS_BIOCHIPS_OPTICALCHIP_H + +#include "pegasus/hotspot.h" +#include "pegasus/util.h" +#include "pegasus/items/biochips/biochipitem.h" + +namespace Pegasus { + +class OpticalChip : public BiochipItem { +public: + OpticalChip(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant); + virtual ~OpticalChip(); + + virtual void writeToStream(Common::WriteStream *); + virtual void readFromStream(Common::ReadStream *); + + void addAries(); + void addMercury(); + void addPoseidon(); + + void activateOpticalHotspots(); + void clickInOpticalHotspot(HotSpotID); + void playOpMemMovie(HotSpotID); + +protected: + enum { + kOpticalAriesExposed, + kOpticalMercuryExposed, + kOpticalPoseidonExposed, + kNumOpticalChipFlags + }; + + void setUpOpticalChip(); + + FlagsArray<byte, kNumOpticalChipFlags> _opticalFlags; + Hotspot _ariesHotspot; + Hotspot _mercuryHotspot; + Hotspot _poseidonHotspot; +}; + +extern OpticalChip *g_opticalChip; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/biochips/pegasuschip.cpp b/engines/pegasus/items/biochips/pegasuschip.cpp new file mode 100644 index 0000000000..fa551fce30 --- /dev/null +++ b/engines/pegasus/items/biochips/pegasuschip.cpp @@ -0,0 +1,198 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/energymonitor.h" +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/items/biochips/pegasuschip.h" +#include "pegasus/neighborhood/tsa/fulltsa.h" +#include "pegasus/neighborhood/tsa/tinytsa.h" + +namespace Pegasus { + +PegasusChip::PegasusChip(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) : + BiochipItem(id, neighborhood, room, direction), _recallSpot(kPegasusRecallSpotID) { + _recallSpot.setArea(Common::Rect(kAIMiddleAreaLeft + 116, kAIMiddleAreaTop + 63, kAIMiddleAreaLeft + 184, kAIMiddleAreaTop + 91)); + _recallSpot.setHotspotFlags(kPegasusBiochipSpotFlag); + g_allHotspots.push_back(&_recallSpot); + setItemState(kPegasusTSA00); +} + +PegasusChip::~PegasusChip() { + g_allHotspots.removeOneHotspot(kPegasusRecallSpotID); +} + +void PegasusChip::select() { + BiochipItem::select(); + setUpPegasusChip(); +} + +void PegasusChip::setUpPegasusChip() { + switch (GameState.getCurrentNeighborhood()) { + case kCaldoriaID: + setItemState(kPegasusCaldoria); + break; + case kFullTSAID: + case kFinalTSAID: + case kTinyTSAID: + setItemState(kPegasusTSA10); + break; + case kPrehistoricID: + if (((PegasusEngine *)g_engine)->playerHasItemID(kHistoricalLog)) + setItemState(kPegasusPrehistoric00); + else + setItemState(kPegasusPrehistoric10); + break; + case kMarsID: + if (GameState.getMarsFinished()) + setItemState(kPegasusMars00); + else + setItemState(kPegasusMars10); + break; + case kWSCID: + if (GameState.getWSCFinished()) + setItemState(kPegasusWSC00); + else + setItemState(kPegasusWSC10); + break; + case kNoradAlphaID: + case kNoradDeltaID: + if (GameState.getNoradFinished()) + setItemState(kPegasusNorad00); + else + setItemState(kPegasusNorad10); + break; + } +} + +// Only does something if the chip should be announcing that the time zone is finished... +void PegasusChip::setUpPegasusChipRude() { + switch (GameState.getCurrentNeighborhood()) { + case kPrehistoricID: + if (((PegasusEngine *)g_engine)->playerHasItemID(kHistoricalLog)) + setItemState(kPegasusPrehistoric00); + break; + case kMarsID: + if (GameState.getMarsFinished()) + setItemState(kPegasusMars00); + break; + case kWSCID: + if (GameState.getWSCFinished()) + setItemState(kPegasusWSC00); + break; + case kNoradAlphaID: + case kNoradDeltaID: + if (GameState.getNoradFinished()) + setItemState(kPegasusNorad00); + break; + } +} + +void PegasusChip::activatePegasusHotspots() { + switch (GameState.getCurrentNeighborhood()) { + case kPrehistoricID: + // WORKAROUND: Don't allow the player to recall if they don't have + // the historical log. Otherwise, gameplay is broken when returning + // to the TSA. + if (!((PegasusEngine *)g_engine)->playerHasItemID(kHistoricalLog)) + return; + // fall through + case kMarsID: + case kWSCID: + case kNoradAlphaID: + case kNoradDeltaID: + _recallSpot.setActive(); + break; + } +} + +void PegasusChip::clickInPegasusHotspot() { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + ItemState thisState = getItemState(); + ItemState hiliteState; + + switch (thisState) { + case kPegasusPrehistoric00: + hiliteState = kPegasusPrehistoric01; + break; + case kPegasusPrehistoric10: + hiliteState = kPegasusPrehistoric11; + break; + case kPegasusMars00: + hiliteState = kPegasusMars01; + break; + case kPegasusMars10: + hiliteState = kPegasusMars11; + break; + case kPegasusNorad00: + hiliteState = kPegasusNorad01; + break; + case kPegasusNorad10: + hiliteState = kPegasusNorad11; + break; + case kPegasusWSC00: + hiliteState = kPegasusWSC01; + break; + case kPegasusWSC10: + hiliteState = kPegasusWSC11; + break; + default: + error("Invalid pegasus chip state"); + } + + // WORKAROUND: The original called setItemState() here. However, + // since we're overriding select() to call setUpPegasusChip(), + // the highlighted frame is never displayed! So, we're manually + // setting the state and selecting the item. Also of note is that + // setItemState() for this class is effectively useless since it + // always gets overriden in the select() function. The only reason + // that this doesn't end in infinite recursion is because setItemState() + // has a check against the current state to make sure you don't call + // select() again. </rant> + _itemState = hiliteState; + BiochipItem::select(); + + uint32 time = g_system->getMillis(); + while (g_system->getMillis() < time + 500) { + vm->refreshDisplay(); + g_system->delayMillis(10); + } + + setItemState(thisState); + + if (!((Neighborhood *)g_neighborhood)->okayToJump()) + return; + + if (g_energyMonitor) + g_energyMonitor->stopEnergyDraining(); + + if (GameState.getTSAState() == kPlayerWentToPrehistoric || GameState.allTimeZonesFinished()) + vm->jumpToNewEnvironment(kFullTSAID, kTSA37, kNorth); + else + vm->jumpToNewEnvironment(kTinyTSAID, kTinyTSA37, kNorth); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/biochips/pegasuschip.h b/engines/pegasus/items/biochips/pegasuschip.h new file mode 100644 index 0000000000..7597424821 --- /dev/null +++ b/engines/pegasus/items/biochips/pegasuschip.h @@ -0,0 +1,55 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_ITEMS_BIOCHIPS_PEGASUSCHIP_H +#define PEGASUS_ITEMS_BIOCHIPS_PEGASUSCHIP_H + +#include "pegasus/hotspot.h" +#include "pegasus/items/biochips/biochipitem.h" + +namespace Pegasus { + +class PegasusChip : public BiochipItem { +public: + PegasusChip(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant); + virtual ~PegasusChip(); + + void select(); + + void setUpPegasusChip(); + + // Called to set up the Pegasus chip when the Pegasus chip is the current chip but does not + // own the center area. + void setUpPegasusChipRude(); + void activatePegasusHotspots(); + void clickInPegasusHotspot(); + +protected: + Hotspot _recallSpot; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/biochips/retscanchip.cpp b/engines/pegasus/items/biochips/retscanchip.cpp new file mode 100644 index 0000000000..84b74a63d2 --- /dev/null +++ b/engines/pegasus/items/biochips/retscanchip.cpp @@ -0,0 +1,49 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/ai/ai_area.h" +#include "pegasus/items/biochips/retscanchip.h" + +namespace Pegasus { + +RetScanChip::RetScanChip(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) : + BiochipItem(id, neighborhood, room, direction) { +} + +void RetScanChip::searchForLaser() { + ItemExtraEntry entry; + findItemExtra(kRetinalScanSearching, entry); + + if (g_AIArea) + g_AIArea->playAIAreaSequence(kBiochipSignature, kMiddleAreaSignature, entry.extraStart, entry.extraStop); + + findItemExtra(kRetinalScanActivated, entry); + if (g_AIArea) + g_AIArea->playAIAreaSequence(kBiochipSignature, kRightAreaSignature, entry.extraStart, entry.extraStop); + + setItemState(kRetinalSimulating); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/biochips/retscanchip.h b/engines/pegasus/items/biochips/retscanchip.h new file mode 100644 index 0000000000..153e6cd071 --- /dev/null +++ b/engines/pegasus/items/biochips/retscanchip.h @@ -0,0 +1,43 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_ITEMS_BIOCHIPS_RETSCANCHIP_H +#define PEGASUS_ITEMS_BIOCHIPS_RETSCANCHIP_H + +#include "pegasus/items/biochips/biochipitem.h" + +namespace Pegasus { + +class RetScanChip : public BiochipItem { +public: + RetScanChip(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant); + virtual ~RetScanChip() {} + + void searchForLaser(); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/biochips/shieldchip.cpp b/engines/pegasus/items/biochips/shieldchip.cpp new file mode 100644 index 0000000000..58cbfcc4ec --- /dev/null +++ b/engines/pegasus/items/biochips/shieldchip.cpp @@ -0,0 +1,53 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/gamestate.h" +#include "pegasus/items/biochips/shieldchip.h" +#include "pegasus/neighborhood/neighborhood.h" + +namespace Pegasus { + +ShieldChip *g_shield = 0; + +ShieldChip::ShieldChip(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) : + BiochipItem(id, neighborhood, room, direction) { + g_shield = this; +} + +void ShieldChip::select() { + BiochipItem::select(); + GameState.setShieldOn(true); + if (g_neighborhood) + g_neighborhood->shieldOn(); +} + +void ShieldChip::deselect() { + BiochipItem::deselect(); + GameState.setShieldOn(false); + if (g_neighborhood) + g_neighborhood->shieldOff(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/biochips/shieldchip.h b/engines/pegasus/items/biochips/shieldchip.h new file mode 100644 index 0000000000..69c6369236 --- /dev/null +++ b/engines/pegasus/items/biochips/shieldchip.h @@ -0,0 +1,46 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_ITEMS_BIOCHIPS_SHIELDCHIP_H +#define PEGASUS_ITEMS_BIOCHIPS_SHIELDCHIP_H + +#include "pegasus/items/biochips/biochipitem.h" + +namespace Pegasus { + +class ShieldChip : public BiochipItem { +public: + ShieldChip(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant); + virtual ~ShieldChip() {} + + void select(); + void deselect(); +}; + +extern ShieldChip *g_shield; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/inventory.cpp b/engines/pegasus/items/inventory.cpp new file mode 100644 index 0000000000..57923b105d --- /dev/null +++ b/engines/pegasus/items/inventory.cpp @@ -0,0 +1,175 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/constants.h" +#include "pegasus/items/item.h" +#include "pegasus/items/inventory.h" + +namespace Pegasus { + +Inventory::Inventory() { + _weightLimit = 100; + _ownerID = kNoActorID; + _referenceCount = 0; +} + +Inventory::~Inventory() { +} + +void Inventory::setWeightLimit(WeightType limit) { + _weightLimit = limit; + // *** What to do if the new weight limit is greater than the current weight? +} + +WeightType Inventory::getWeight() { + WeightType result = 0; + + for (ItemIterator it = _inventoryList.begin(); it != _inventoryList.end(); it++) + result += (*it)->getItemWeight(); + + return result; +} + +// If the item already belongs, just return kInventoryOK. +InventoryResult Inventory::addItem(Item *item) { + if (itemInInventory(item)) + return kInventoryOK; + + if (getWeight() + item->getItemWeight() > _weightLimit) + return kTooMuchWeight; + + _inventoryList.push_back(item); + item->setItemOwner(_ownerID); + + ++_referenceCount; + return kInventoryOK; +} + +InventoryResult Inventory::removeItem(Item *item) { + for (ItemIterator it = _inventoryList.begin(); it != _inventoryList.end(); it++) { + if (*it == item) { + _inventoryList.erase(it); + item->setItemOwner(kNoActorID); + + ++_referenceCount; + return kInventoryOK; + } + } + + return kItemNotInInventory; +} + +InventoryResult Inventory::removeItem(ItemID id) { + Item *item = findItemByID(id); + + if (item) { + _inventoryList.remove(item); + item->setItemOwner(kNoActorID); + + ++_referenceCount; + return kInventoryOK; + } + + return kItemNotInInventory; +} + +void Inventory::removeAllItems() { + _inventoryList.clear(); + ++_referenceCount; +} + +bool Inventory::itemInInventory(Item *item) { + for (ItemIterator it = _inventoryList.begin(); it != _inventoryList.end(); it++) + if (*it == item) + return true; + + return false; +} + +bool Inventory::itemInInventory(ItemID id) { + return findItemByID(id) != NULL; +} + +Item *Inventory::getItemAt(int32 index) { + int32 i = 0; + for (ItemIterator it = _inventoryList.begin(); it != _inventoryList.end(); it++, i++) + if (i == index) + return *it; + + return 0; +} + +ItemID Inventory::getItemIDAt(int32 index) { + Item *item = getItemAt(index); + + if (item) + return item->getObjectID(); + + return kNoItemID; +} + +Item *Inventory::findItemByID(ItemID id) { + return _inventoryList.findItemByID(id); +} + +// Return -1 if not found. + +int32 Inventory::findIndexOf(Item *item) { + uint32 i = 0; + for (ItemIterator it = _inventoryList.begin(); it != _inventoryList.end(); it++, i++) + if (*it == item) + return i; + + return -1; +} + +// Return -1 if not found. + +int32 Inventory::findIndexOf(ItemID id) { + uint32 i = 0; + for (ItemIterator it = _inventoryList.begin(); it != _inventoryList.end(); it++, i++) + if ((*it)->getObjectID() == id) + return i; + + return -1; +} + +WeightType Inventory::getWeightLimit() { + return _weightLimit; +} + +int32 Inventory::getNumItems() { + return _inventoryList.size(); +} + +void Inventory::setOwnerID(const ActorID id) { + _ownerID = id; +} + +ActorID Inventory::getOwnerID() const { + return _ownerID; +} + +} // End of namespae Pegasus diff --git a/engines/pegasus/items/inventory.h b/engines/pegasus/items/inventory.h new file mode 100644 index 0000000000..796ec49556 --- /dev/null +++ b/engines/pegasus/items/inventory.h @@ -0,0 +1,80 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_ITEMS_INVENTORY_H +#define PEGASUS_ITEMS_INVENTORY_H + +#include "pegasus/types.h" +#include "pegasus/items/itemlist.h" + +namespace Pegasus { + +class Item; + +// Inventories have a "current item". This item is the default item the player can +// use. In a text adventure system, the current item would be "it", as in +// "Hit the troll with it," where "it" would refer to some weapon which is the current +// item. In a graphic adventure, the current item would be the item the user selects +// to use with the mouse or other pointing device. + +class Inventory { +public: + Inventory(); + virtual ~Inventory(); + + WeightType getWeightLimit(); + void setWeightLimit(WeightType limit); + WeightType getWeight(); + + virtual InventoryResult addItem(Item *item); + virtual InventoryResult removeItem(Item *item); + virtual InventoryResult removeItem(ItemID id); + virtual bool itemInInventory(Item *item); + virtual bool itemInInventory(ItemID id); + virtual Item *getItemAt(int32 index); + virtual ItemID getItemIDAt(int32 index); + virtual Item *findItemByID(ItemID id); + virtual int32 findIndexOf(Item *item); + virtual int32 findIndexOf(ItemID id); + int32 getNumItems(); + virtual void removeAllItems(); + + void setOwnerID(const ActorID id); + ActorID getOwnerID() const; + + uint32 getReferenceCount() { return _referenceCount; } + +protected: + WeightType _weightLimit; + ActorID _ownerID; + ItemList _inventoryList; + +private: + uint32 _referenceCount; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/inventory/airmask.cpp b/engines/pegasus/items/inventory/airmask.cpp new file mode 100644 index 0000000000..c65dd36102 --- /dev/null +++ b/engines/pegasus/items/inventory/airmask.cpp @@ -0,0 +1,249 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/inventory/airmask.h" +#include "pegasus/neighborhood/neighborhood.h" + +namespace Pegasus { + +AirMask *g_airMask = 0; + +// Based on full == 100, which is scale used by GetAirLeft(). +static const TimeValue kOxygenLowThreshold = 25; + +void AirMask::airMaskTimerExpired() { + if (g_neighborhood) + g_neighborhood->checkAirMask(); +} + +AirMask::AirMask(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) : + InventoryItem(id, neighborhood, room, direction), _toggleSpot(kAirMaskToggleSpotID) { + g_airMask = this; + _toggleSpot.setArea(Common::Rect(kAIMiddleAreaLeft + 10, kAIMiddleAreaTop + 17, kAIMiddleAreaLeft + 110, kAIMiddleAreaTop + 57)); + _toggleSpot.setHotspotFlags(kAirMaskSpotFlag); + g_allHotspots.push_back(&_toggleSpot); + setItemState(kAirMaskEmptyOff); + _oxygenTimer.primeFuse(0); + _oxygenTimer.setFunctor(new Common::Functor0Mem<void, AirMask>(this, &AirMask::airMaskTimerExpired)); +} + +AirMask::~AirMask() { + g_allHotspots.removeOneHotspot(kAirMaskToggleSpotID); + g_airMask = 0; +} + +void AirMask::writeToStream(Common::WriteStream *stream) { + InventoryItem::writeToStream(stream); + stream->writeUint32BE(_oxygenTimer.getTimeRemaining()); +} + +void AirMask::readFromStream(Common::ReadStream *stream) { + _oxygenTimer.stopFuse(); + InventoryItem::readFromStream(stream); + _oxygenTimer.primeFuse(stream->readUint32BE()); +} + +void AirMask::putMaskOn() { + AirQuality airQuality; + + if (g_neighborhood) + airQuality = g_neighborhood->getAirQuality(GameState.getCurrentRoom()); + else + airQuality = kAirQualityGood; + + uint airLevel = getAirLeft(); + ItemState newState = getItemState(); + ItemState oldState = newState; + + if (airLevel == 0) { + newState = kAirMaskEmptyFilter; + } else if (airLevel <= kOxygenLowThreshold) { + if (airQuality == kAirQualityVacuum) + newState = kAirMaskLowOn; + else + newState = kAirMaskLowFilter; + } else { + if (airQuality == kAirQualityVacuum) + newState = kAirMaskFullOn; + else + newState = kAirMaskFullFilter; + } + + if (newState != oldState) + setItemState(newState); +} + +void AirMask::takeMaskOff() { + uint airLevel = getAirLeft(); + ItemState newState = getItemState(); + ItemState oldState = newState; + + if (airLevel == 0) + newState = kAirMaskEmptyOff; + else if (airLevel <= kOxygenLowThreshold) + newState = kAirMaskLowOff; + else + newState = kAirMaskFullOff; + + if (newState != oldState) + setItemState(newState); +} + +void AirMask::toggleItemState() { + if (isAirMaskInUse()) + takeMaskOff(); + else + putMaskOn(); +} + +void AirMask::airQualityChanged() { + if (isAirMaskInUse()) + putMaskOn(); + else + takeMaskOff(); +} + +void AirMask::setItemState(const ItemState newState) { + if (newState != getItemState()) { + InventoryItem::setItemState(newState); + + switch (newState) { + case kAirMaskFullOn: + case kAirMaskLowOn: + if (!_oxygenTimer.isFuseLit()) { + _oxygenTimer.lightFuse(); + startIdling(); + } + break; + default: + if (_oxygenTimer.isFuseLit()) { + _oxygenTimer.stopFuse(); + stopIdling(); + } + break; + } + + if (g_neighborhood) + g_neighborhood->checkAirMask(); + + g_AIArea->checkMiddleArea(); + } +} + +void AirMask::useIdleTime() { + if (getAirLeft() == 0) + setItemState(kAirMaskEmptyOff); + else if (getAirLeft() <= kOxygenLowThreshold) + setItemState(kAirMaskLowOn); +} + +void AirMask::refillAirMask() { + switch (getItemState()) { + case kAirMaskEmptyOff: + case kAirMaskLowOff: + setItemState(kAirMaskFullOff); + break; + case kAirMaskEmptyFilter: + case kAirMaskLowFilter: + setItemState(kAirMaskFullFilter); + break; + case kAirMaskLowOn: + setItemState(kAirMaskFullOn); + break; + } + + if (_oxygenTimer.isFuseLit()) { + _oxygenTimer.stopFuse(); + _oxygenTimer.primeFuse(kOxyMaskFullTime); + _oxygenTimer.lightFuse(); + } else { + _oxygenTimer.primeFuse(kOxyMaskFullTime); + } +} + +// Doesn't return 0 until the timer is actually at 0. +uint AirMask::getAirLeft() { + return CLIP<int>(((_oxygenTimer.getTimeRemaining() * 100) + kOxyMaskFullTime - 1) / kOxyMaskFullTime, 0, 100); +} + +bool AirMask::isAirMaskInUse() { + switch (getItemState()) { + case kAirMaskEmptyOff: + case kAirMaskLowOff: + case kAirMaskFullOff: + return false; + break; + default: + return true; + break; + } +} + +bool AirMask::isAirMaskOn() { + switch (getItemState()) { + case kAirMaskLowOn: + case kAirMaskFullOn: + return true; + break; + default: + return false; + break; + } +} + +bool AirMask::isAirFilterOn() { + switch (getItemState()) { + case kAirMaskEmptyFilter: + case kAirMaskLowFilter: + case kAirMaskFullFilter: + return true; + break; + default: + return false; + break; + } +} + +void AirMask::addedToInventory() { + GameState.setMarsMaskOnFiller(false); +} + +void AirMask::removedFromInventory() { + if (isAirMaskInUse()) + toggleItemState(); +} + +void AirMask::activateAirMaskHotspots() { + _toggleSpot.setActive(); +} + +void AirMask::clickInAirMaskHotspot() { + toggleItemState(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/inventory/airmask.h b/engines/pegasus/items/inventory/airmask.h new file mode 100644 index 0000000000..6a2d708a6c --- /dev/null +++ b/engines/pegasus/items/inventory/airmask.h @@ -0,0 +1,76 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_ITEMS_INVENTORY_AIRMASK_H +#define PEGASUS_ITEMS_INVENTORY_AIRMASK_H + +#include "pegasus/hotspot.h" +#include "pegasus/timers.h" +#include "pegasus/items/inventory/inventoryitem.h" + +namespace Pegasus { + +class AirMask : public InventoryItem, private Idler { +public: + AirMask(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant); + virtual ~AirMask(); + + virtual void writeToStream(Common::WriteStream *); + virtual void readFromStream(Common::ReadStream *); + + virtual void setItemState(const ItemState); + void putMaskOn(); + void takeMaskOff(); + void toggleItemState(); + void airQualityChanged(); + + bool isAirMaskInUse(); + bool isAirMaskOn(); + bool isAirFilterOn(); + + void refillAirMask(); + + // Returns a percentage + uint getAirLeft(); + + void activateAirMaskHotspots(); + void clickInAirMaskHotspot(); + +protected: + void airMaskTimerExpired(); + + virtual void removedFromInventory(); + virtual void addedToInventory(); + void useIdleTime(); + + Hotspot _toggleSpot; + FuseFunction _oxygenTimer; +}; + +extern AirMask *g_airMask; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/inventory/gascanister.cpp b/engines/pegasus/items/inventory/gascanister.cpp new file mode 100644 index 0000000000..bf63cc6542 --- /dev/null +++ b/engines/pegasus/items/inventory/gascanister.cpp @@ -0,0 +1,46 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/ai/ai_area.h" +#include "pegasus/items/inventory/gascanister.h" + +namespace Pegasus { + +GasCanister::GasCanister(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) : + InventoryItem(id, neighborhood, room, direction) { +} + +void GasCanister::select() { + InventoryItem::select(); + takeSharedArea(); +} + +void GasCanister::takeSharedArea() { + ItemExtraEntry entry; + findItemExtra(kGasCanLoop, entry); + g_AIArea->loopAIAreaSequence(kInventorySignature, kMiddleAreaSignature, entry.extraStart, entry.extraStop); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/inventory/gascanister.h b/engines/pegasus/items/inventory/gascanister.h new file mode 100644 index 0000000000..7d4d8193f5 --- /dev/null +++ b/engines/pegasus/items/inventory/gascanister.h @@ -0,0 +1,44 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_ITEMS_INVENTORY_GASCANISTER_H +#define PEGASUS_ITEMS_INVENTORY_GASCANISTER_H + +#include "pegasus/items/inventory/inventoryitem.h" + +namespace Pegasus { + +class GasCanister : public InventoryItem { +public: + GasCanister(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant); + virtual ~GasCanister() {} + + void select(); + void takeSharedArea(); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/inventory/inventoryitem.cpp b/engines/pegasus/items/inventory/inventoryitem.cpp new file mode 100644 index 0000000000..4399708879 --- /dev/null +++ b/engines/pegasus/items/inventory/inventoryitem.cpp @@ -0,0 +1,110 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "common/stream.h" + +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/inventory/inventoryitem.h" + +namespace Pegasus { + +InventoryItem::InventoryItem(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) : + Item(id, neighborhood, room, direction) { + + PegasusEngine *vm = (PegasusEngine *)g_engine; + + Common::SeekableReadStream *leftInfo = vm->_resFork->getResource(MKTAG('L', 'e', 'f', 't'), kItemBaseResID + id); + if (leftInfo) { + _leftAreaInfo = readItemState(leftInfo); + delete leftInfo; + } else { + _leftAreaInfo.numEntries = 0; + _leftAreaInfo.entries = 0; + } + + Common::SeekableReadStream *inventoryInfo = vm->_resFork->getResource(MKTAG('I', 'n', 'v', 'I'), kItemBaseResID + id); + if (inventoryInfo) { + _inventoryInfo.panelStart = inventoryInfo->readUint32BE(); + _inventoryInfo.panelStop = inventoryInfo->readUint32BE(); + delete inventoryInfo; + } else { + _inventoryInfo.panelStart = _inventoryInfo.panelStop = 0; + } + + _itemAnimationTime = 0; +} + +InventoryItem::~InventoryItem() { + delete[] _leftAreaInfo.entries; +} + +ItemType InventoryItem::getItemType() { + return kInventoryItemType; +} + +TimeValue InventoryItem::getLeftAreaTime() const { + if (!_leftAreaInfo.entries) + return 0xffffffff; + + TimeValue time; + ItemState state; + + findItemStateEntryByState(_leftAreaInfo, _itemState, time); + if (time == 0xffffffff) + getItemStateEntry(_leftAreaInfo, 0, state, time); + + return time; +} + +void InventoryItem::setAnimationTime(const TimeValue time) { + _itemAnimationTime = time; +} + +TimeValue InventoryItem::getAnimationTime() const { + return _itemAnimationTime; +} + +// Must affect images in left area. +void InventoryItem::select() { + Item::select(); + + if (g_AIArea) + g_AIArea->setAIAreaToTime(kInventorySignature, kLeftAreaSignature, getLeftAreaTime()); +} + +void InventoryItem::deselect() { + Item::deselect(); + + if (g_AIArea) + g_AIArea->setAIAreaToTime(kInventorySignature, kLeftAreaSignature, 0xffffffff); +} + +void InventoryItem::getPanelTimes(TimeValue &start, TimeValue &stop) const { + start = _inventoryInfo.panelStart; + stop = _inventoryInfo.panelStop; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/inventory/inventoryitem.h b/engines/pegasus/items/inventory/inventoryitem.h new file mode 100644 index 0000000000..9d78113014 --- /dev/null +++ b/engines/pegasus/items/inventory/inventoryitem.h @@ -0,0 +1,67 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_ITEMS_INVENTORY_INVENTORYITEM_H +#define PEGASUS_ITEMS_INVENTORY_INVENTORYITEM_H + +#include "pegasus/items/item.h" + +namespace Pegasus { + +// JMPInventoryInfo contains the resource data used by InventoryItems. + +struct JMPInventoryInfo { + TimeValue panelStart; + TimeValue panelStop; +}; + +class InventoryItem : public Item { +public: + InventoryItem(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant); + virtual ~InventoryItem(); + + virtual ItemType getItemType(); + + void getPanelTimes(TimeValue &, TimeValue &) const; + TimeValue getLeftAreaTime() const; + + void setAnimationTime(const TimeValue); + TimeValue getAnimationTime() const; + + virtual void toggleItemState() {} + + // Must affect images in left area. + virtual void select(); + virtual void deselect(); + +protected: + JMPInventoryInfo _inventoryInfo; + ItemStateInfo _leftAreaInfo; + TimeValue _itemAnimationTime; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/inventory/keycard.cpp b/engines/pegasus/items/inventory/keycard.cpp new file mode 100644 index 0000000000..c818b6675b --- /dev/null +++ b/engines/pegasus/items/inventory/keycard.cpp @@ -0,0 +1,59 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/pegasus.h" +#include "pegasus/items/inventory/keycard.h" + +namespace Pegasus { + +KeyCard::KeyCard(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) : + InventoryItem(id, neighborhood, room, direction) { + setItemState(kFlashlightOff); +} + +void KeyCard::toggleItemState() { + if (getItemState() == kFlashlightOff) + setItemState(kFlashlightOn); + else + setItemState(kFlashlightOff); +} + +void KeyCard::setItemState(const ItemState newState) { + if (newState != getItemState()) { + InventoryItem::setItemState(newState); + ((PegasusEngine *)g_engine)->checkFlashlight(); + } +} + +bool KeyCard::isFlashlightOn() { + return getItemState() == kFlashlightOn; +} + +void KeyCard::removedFromInventory() { + if (isFlashlightOn()) + setItemState(kFlashlightOff); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/inventory/keycard.h b/engines/pegasus/items/inventory/keycard.h new file mode 100644 index 0000000000..846f40e6e5 --- /dev/null +++ b/engines/pegasus/items/inventory/keycard.h @@ -0,0 +1,48 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_ITEMS_INVENTORY_KEYCARD_H +#define PEGASUS_ITEMS_INVENTORY_KEYCARD_H + +#include "pegasus/items/inventory/inventoryitem.h" + +namespace Pegasus { + +class KeyCard : public InventoryItem { +public: + KeyCard(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant); + virtual ~KeyCard() {} + + virtual void toggleItemState(); + virtual void setItemState(const ItemState); + bool isFlashlightOn(); + +protected: + virtual void removedFromInventory(); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/inventorypicture.cpp b/engines/pegasus/items/inventorypicture.cpp new file mode 100644 index 0000000000..fc812faae2 --- /dev/null +++ b/engines/pegasus/items/inventorypicture.cpp @@ -0,0 +1,370 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/pegasus.h" +#include "pegasus/transition.h" +#include "pegasus/items/inventorypicture.h" +#include "pegasus/items/biochips/biochipitem.h" +#include "pegasus/items/inventory/inventoryitem.h" + +namespace Pegasus { + +InventoryPicture::InventoryPicture(const DisplayElementID id, InputHandler *nextHandler, Inventory *inventory) : + InputHandler(nextHandler), Picture(id), _panelMovie(kNoDisplayElement){ + _inventory = inventory; + _lastReferenceCount = 0xffffffff; + + if (_inventory->getNumItems() > 0) { + _currentItemIndex = 0; + _currentItem = (Item *)_inventory->getItemAt(0); + } else { + _currentItemIndex = -1; + _currentItem = 0; + } + + _active = false; + _shouldDrawHighlight = true; + _itemsPerRow = 1; + _numberOfRows = 1; + _itemWidth = 0; + _itemHeight = 0; + _itemX = 0; + _itemY = 0; +} + +void InventoryPicture::initInventoryImage(Transition *transition) { + initFromPICTFile(_pictName, true); + _panelMovie.shareSurface(this); + _panelMovie.initFromMovieFile(_movieName); + _panelMovie.getBounds(_highlightBounds); + _panelMovie.setTriggeredElement(transition); + _highlightImage.initFromPICTFile(_highlightName, true); +} + +void InventoryPicture::throwAwayInventoryImage() { + if (isSurfaceValid()) { + _panelMovie.releaseMovie(); + _highlightImage.deallocateSurface(); + deallocateSurface(); + } +} + +void InventoryPicture::getItemXY(uint32 index, CoordType &x, CoordType &y) { + x = (index % _itemsPerRow) * _itemWidth + _itemX; + y = (index / _itemsPerRow) * _itemHeight + _itemY; +} + +void InventoryPicture::drawItemHighlight(const Common::Rect &r) { + if (_highlightImage.isSurfaceValid()) { + Common::Rect r2 = _highlightBounds; + Common::Rect bounds; + getBounds(bounds); + + r2.translate(bounds.left, bounds.top); + r2 = r2.findIntersectingRect(r); + if (!r2.isEmpty()) { + Common::Rect r1 = r2; + r1.translate(-bounds.left - _highlightBounds.left, -bounds.top - _highlightBounds.top); + _highlightImage.drawImage(r1, r2); + } + } +} + +void InventoryPicture::draw(const Common::Rect &r) { + Picture::draw(r); + if (_inventory->getNumItems() != 0 && _shouldDrawHighlight) + drawItemHighlight(r); +} + +// Assumes index >= 0. +void InventoryPicture::setCurrentItemIndex(int32 index) { + if (index >= _inventory->getNumItems()) + index = _inventory->getNumItems() - 1; + + Item *currentItem = 0; + if (index >= 0) + currentItem = (Item *)_inventory->getItemAt(index); + + if (currentItem != _currentItem) { + if (_currentItem) { + if (_currentItem->isSelected()) + _currentItem->deselect(); + + if (_active) + unhighlightCurrentItem(); + } + + _currentItemIndex = index; + _currentItem = currentItem; + if (_currentItem) { + _currentItem->select(); + + if (_active) + highlightCurrentItem(); + } + + if (_active) + triggerRedraw(); + } +} + +void InventoryPicture::setCurrentItemID(ItemID id) { + int32 index = _inventory->findIndexOf(id); + if (index >= 0) + setCurrentItemIndex(index); +} + +InventoryResult InventoryPicture::addInventoryItem(Item *item) { + InventoryResult result = _inventory->addItem(item); + + if (result == kInventoryOK) + setCurrentItemIndex(_inventory->findIndexOf(item)); + + return result; +} + +InventoryResult InventoryPicture::removeInventoryItem(Item *item) { + InventoryResult result = _inventory->removeItem(item); + + if (result == kInventoryOK) + setCurrentItemIndex(getCurrentItemIndex()); + + return result; +} + +void InventoryPicture::removeAllItems() { + _inventory->removeAllItems(); + setCurrentItemIndex(0); +} + +bool InventoryPicture::itemInInventory(Item *item) { + return _inventory->itemInInventory(item); +} + +bool InventoryPicture::itemInInventory(const ItemID id) { + return _inventory->itemInInventory(id); +} + +void InventoryPicture::panelUp() { + allowInput(true); +} + +// Must ensure that the picture matches the _inventory member variable. +void InventoryPicture::activateInventoryPicture() { + if (_active) + return; + + allowInput(false); + + if (_lastReferenceCount != _inventory->getReferenceCount()) { + uint32 numItems = _inventory->getNumItems(); + + CoordType x, y; + getItemXY(0, x, y); + _panelMovie.moveMovieBoxTo(x, y); + _panelMovie.show(); + + for (uint32 i = 0; i < numItems; i++) { + Item *item = (Item *)_inventory->getItemAt(i); + if (item == _currentItem) + item->select(); + + getItemXY(i, x, y); + _panelMovie.moveMovieBoxTo(x, y); + _panelMovie.setTime(getItemPanelTime(item)); + _panelMovie.redrawMovieWorld(); + } + + uint32 numSlots = _itemsPerRow * _numberOfRows; + + for (uint32 i = numItems; i < numSlots; i++) { + getItemXY(i, x, y); + _panelMovie.moveMovieBoxTo(x, y); + _panelMovie.setTime(0); + _panelMovie.redrawMovieWorld(); + } + + _lastReferenceCount = _inventory->getReferenceCount(); + } + + show(); // *** Do we really need this? + if (_currentItem) + highlightCurrentItem(); + + _active = true; +} + +void InventoryPicture::deactivateInventoryPicture() { + if (!_active) + return; + + _active = false; + allowInput(false); + _panelMovie.hide(); + hide(); + + if (_inventory->getNumItems() != 0) + if (!_currentItem->isActive()) + _currentItem->activate(); +} + +void InventoryPicture::handleInput(const Input &input, const Hotspot *cursorSpot) { + if (_active) { + if (input.upButtonDown()) { + if (_currentItemIndex - _itemsPerRow >= 0) + setCurrentItemIndex(_currentItemIndex - _itemsPerRow); + } else if (input.downButtonDown()) { + if (_currentItemIndex + _itemsPerRow < _inventory->getNumItems()) + setCurrentItemIndex(_currentItemIndex + _itemsPerRow); + } else if (input.leftButtonDown()) { + if ((_currentItemIndex % _itemsPerRow) != 0) + setCurrentItemIndex(_currentItemIndex - 1); + } else if (input.rightButtonDown()) { + if (((_currentItemIndex + 1) % _itemsPerRow) != 0 && _currentItemIndex + 1 < _inventory->getNumItems()) + setCurrentItemIndex(_currentItemIndex + 1); + } + } + + InputHandler::handleInput(input, cursorSpot); +} + +void InventoryPicture::highlightCurrentItem() { + CoordType x, y; + getItemXY(_currentItemIndex, x, y); + _highlightBounds.moveTo(x, y); +} + +InventoryItemsPicture::InventoryItemsPicture(const DisplayElementID id, InputHandler *nextHandler, Inventory *inventory) : + InventoryPicture(id, nextHandler, inventory) { + _pictName = "Images/Items/Inventory/Inventory Panel"; + _movieName = "Images/Items/Inventory/Inventory Panel Movie"; + _highlightName = "Images/Items/Inventory/Inventory Hilite"; + + _itemsPerRow = 3; + _numberOfRows = 3; + _itemWidth = 88; + _itemHeight = 64; + _itemX = 8; + _itemY = 26; + _isLooping = true; +} + +void InventoryItemsPicture::loopCurrentItem() { + if (_isLooping) { + CoordType x, y; + getItemXY(_currentItemIndex, x, y); + _panelMovie.moveMovieBoxTo(x, y); + _highlightBounds.moveTo(x, y); + + TimeValue start, stop; + ((InventoryItem *)_currentItem)->getPanelTimes(start, stop); + _panelMovie.stop(); + _panelMovie.setFlags(0); + _panelMovie.setSegment(start, stop); + _panelMovie.setFlags(kLoopTimeBase); + _panelMovie.setTime(((InventoryItem *)_currentItem)->getAnimationTime()); + _panelMovie.start(); + } +} + +void InventoryItemsPicture::highlightCurrentItem() { + InventoryPicture::highlightCurrentItem(); + loopCurrentItem(); +} + +void InventoryItemsPicture::unhighlightCurrentItem() { + InventoryPicture::unhighlightCurrentItem(); + _panelMovie.stop(); + _panelMovie.setFlags(0); + ((InventoryItem *)_currentItem)->setAnimationTime(_panelMovie.getTime()); +} + +TimeValue InventoryItemsPicture::getItemPanelTime(Item *item) { + TimeValue start, stop; + ((InventoryItem *)item)->getPanelTimes(start, stop); + ((InventoryItem *)item)->setAnimationTime(start); + return start; +} + +void InventoryItemsPicture::deactivateInventoryPicture() { + if (_active) { + InventoryPicture::deactivateInventoryPicture(); + _panelMovie.stop(); + _panelMovie.setFlags(0); + _panelMovie.setSegment(0, _panelMovie.getDuration()); + _isLooping = true; + } +} + +void InventoryItemsPicture::playEndMessage(DisplayElement *pushElement) { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + Movie endMessage(0); + + _shouldDrawHighlight = false; + endMessage.shareSurface(this); + endMessage.initFromMovieFile("Images/Caldoria/A56 Congrats"); + endMessage.moveMovieBoxTo(kFinalMessageLeft - kInventoryPushLeft, kFinalMessageTop - kInventoryPushTop); + endMessage.setTriggeredElement(pushElement); + endMessage.start(); + + while (endMessage.isRunning()) { + vm->checkCallBacks(); + vm->refreshDisplay(); + g_system->delayMillis(10); + } + + endMessage.stop(); +} + +BiochipPicture::BiochipPicture(const DisplayElementID id, InputHandler *nextHandler, Inventory *inventory) : + InventoryPicture(id, nextHandler, inventory) { + _pictName = "Images/Items/Biochips/Biochip Panel"; + _movieName = "Images/Items/Biochips/Biochip Panel Movie"; + _highlightName = "Images/Items/Biochips/BioChip Hilite"; + + _itemsPerRow = 4; + _numberOfRows = 2; + _itemWidth = 46; + _itemHeight = 46; + _itemX = 4; + _itemY = 24; +} + +void BiochipPicture::unhighlightCurrentItem() { + InventoryPicture::unhighlightCurrentItem(); + CoordType x, y; + getItemXY(_currentItemIndex, x, y); + _panelMovie.show(); + _panelMovie.moveMovieBoxTo(x, y); + _panelMovie.setTime(getItemPanelTime(_currentItem)); + _panelMovie.redrawMovieWorld(); +} + +TimeValue BiochipPicture::getItemPanelTime(Item *item) { + return ((BiochipItem *)item)->getPanelTime(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/inventorypicture.h b/engines/pegasus/items/inventorypicture.h new file mode 100644 index 0000000000..88a4a4ba75 --- /dev/null +++ b/engines/pegasus/items/inventorypicture.h @@ -0,0 +1,125 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_ITEMS_INVENTORYPICTURE_H +#define PEGASUS_ITEMS_INVENTORYPICTURE_H + +#include "pegasus/input.h" +#include "pegasus/movie.h" +#include "pegasus/surface.h" + +namespace Pegasus { + +class Inventory; +class Item; +class Input; +class Transition; + +class InventoryPicture : public InputHandler, public Picture { +public: + InventoryPicture(const DisplayElementID, InputHandler *, Inventory *); + virtual ~InventoryPicture() {} + + void initInventoryImage(Transition *); + void throwAwayInventoryImage(); + + void panelUp(); + void activateInventoryPicture(); + void deactivateInventoryPicture(); + void handleInput(const Input &, const Hotspot *); + bool wantsCursor() { return false; } + + InventoryResult addInventoryItem(Item *); + InventoryResult removeInventoryItem(Item *); + void removeAllItems(); + Item *getCurrentItem() { return _currentItem; } + void setCurrentItemIndex(int32); + void setCurrentItemID(ItemID); + int32 getCurrentItemIndex() { return _currentItemIndex; } + bool itemInInventory(Item *); + bool itemInInventory(const ItemID); + +protected: + void getItemXY(uint32, CoordType &, CoordType &); + void draw(const Common::Rect &); + void drawItemHighlight(const Common::Rect &); + virtual void highlightCurrentItem(); + virtual void unhighlightCurrentItem() {} + virtual TimeValue getItemPanelTime(Item *) = 0; + + Inventory *_inventory; + uint32 _lastReferenceCount; + Frame _highlightImage; + Movie _panelMovie; + int32 _currentItemIndex; + Item *_currentItem; + Common::Rect _highlightBounds; + bool _active, _shouldDrawHighlight; + + Common::String _pictName; + Common::String _movieName; + Common::String _highlightName; + uint16 _itemsPerRow; + uint16 _numberOfRows; + uint16 _itemWidth; + uint16 _itemHeight; + uint16 _itemX; + uint16 _itemY; +}; + +class InventoryItemsPicture : public InventoryPicture { +public: + InventoryItemsPicture(const DisplayElementID, InputHandler *, Inventory *); + virtual ~InventoryItemsPicture() {} + + void deactivateInventoryPicture(); + + void disableLooping() { _isLooping = false; } + + void playEndMessage(DisplayElement *); + +protected: + virtual void highlightCurrentItem(); + virtual void unhighlightCurrentItem(); + virtual TimeValue getItemPanelTime(Item *); + void loopCurrentItem(); + + InputHandler *_previousHandler; + bool _isLooping; +}; + +class BiochipPicture : public InventoryPicture { +public: + BiochipPicture(const DisplayElementID, InputHandler *, Inventory *); + virtual ~BiochipPicture() {} + +protected: + virtual void unhighlightCurrentItem(); + virtual TimeValue getItemPanelTime(Item *); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/item.cpp b/engines/pegasus/items/item.cpp new file mode 100644 index 0000000000..8089f2b93d --- /dev/null +++ b/engines/pegasus/items/item.cpp @@ -0,0 +1,314 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "common/error.h" +#include "common/stream.h" + +#include "pegasus/constants.h" +#include "pegasus/elements.h" +#include "pegasus/pegasus.h" +#include "pegasus/surface.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/item.h" +#include "pegasus/items/itemlist.h" +#include "pegasus/items/biochips/biochipitem.h" +#include "pegasus/items/inventory/inventoryitem.h" + +namespace Pegasus { + +Item::Item(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) : IDObject(id) { + _itemNeighborhood = neighborhood; + _itemRoom = room; + _itemDirection = direction; + _itemWeight = 1; + _itemOwnerID = kNoActorID; + _itemState = 0; + + PegasusEngine *vm = (PegasusEngine *)g_engine; + + Common::SeekableReadStream *info = vm->_resFork->getResource(kItemInfoResType, kItemBaseResID + id); + if (info) { + _itemInfo.infoLeftTime = info->readUint32BE(); + _itemInfo.infoRightStart = info->readUint32BE(); + _itemInfo.infoRightStop = info->readUint32BE(); + _itemInfo.dragSpriteNormalID = info->readUint16BE(); + _itemInfo.dragSpriteUsedID = info->readUint16BE(); + + if (vm->isDemo()) { + // Adjust info right times to account for the stuff that was chopped out of the + // info right movies. + // Assumes time scale of 600. + + // Gap times in seconds + static const TimeValue kGap1 = 24; + static const TimeValue kGap2 = 34; + static const TimeValue kGap3 = 4; + static const TimeValue kGap4 = 4; + + static const TimeValue kGapForGroup1 = kGap1; + static const TimeValue kGapForGroup2 = kGapForGroup1 + kGap2; + static const TimeValue kGapForGroup3 = kGapForGroup2 + kGap3; + static const TimeValue kGapForGroup4 = kGapForGroup3 + kGap4; + + switch (id) { + case kHistoricalLog: + case kJourneymanKey: + case kKeyCard: + _itemInfo.infoRightStart -= 600 * kGapForGroup1; + _itemInfo.infoRightStop -= 600 * kGapForGroup1; + break; + case kAIBiochip: + _itemInfo.infoRightStart -= 600 * kGapForGroup2; + _itemInfo.infoRightStop -= 600 * kGapForGroup2; + break; + case kMapBiochip: + _itemInfo.infoRightStart -= 600 * kGapForGroup3; + _itemInfo.infoRightStop -= 600 * kGapForGroup3; + break; + case kPegasusBiochip: + _itemInfo.infoRightStart -= 600 * kGapForGroup4; + _itemInfo.infoRightStop -= 600 * kGapForGroup4; + break; + } + } + + delete info; + } else { + memset(&_itemInfo, 0, sizeof(_itemInfo)); + } + + Common::SeekableReadStream *middleAreaInfo = vm->_resFork->getResource(kMiddleAreaInfoResType, kItemBaseResID + id); + if (middleAreaInfo) { + _sharedAreaInfo = readItemState(middleAreaInfo); + delete middleAreaInfo; + } else { + // Only kArgonPickup does not have this + memset(&_sharedAreaInfo, 0, sizeof(_sharedAreaInfo)); + } + + Common::SeekableReadStream *extraInfo = vm->_resFork->getResource(kItemExtraInfoResType, kItemBaseResID + id); + if (!extraInfo) + error("Extra info not found for item %d", id); + + _itemExtras.numEntries = extraInfo->readUint16BE(); + _itemExtras.entries = new ItemExtraEntry[_itemExtras.numEntries]; + for (uint16 i = 0; i < _itemExtras.numEntries; i++) { + _itemExtras.entries[i].extraID = extraInfo->readUint32BE(); + _itemExtras.entries[i].extraArea = extraInfo->readUint16BE(); + _itemExtras.entries[i].extraStart = extraInfo->readUint32BE(); + _itemExtras.entries[i].extraStop = extraInfo->readUint32BE(); + } + + delete extraInfo; + + g_allItems.push_back(this); +} + +Item::~Item() { + delete[] _sharedAreaInfo.entries; + delete[] _itemExtras.entries; +} + +void Item::writeToStream(Common::WriteStream *stream) { + stream->writeUint16BE(_itemNeighborhood); + stream->writeUint16BE(_itemRoom); + stream->writeByte(_itemDirection); + stream->writeUint16BE(_itemOwnerID); + stream->writeUint16BE(_itemState); +} + +void Item::readFromStream(Common::ReadStream *stream) { + _itemNeighborhood = stream->readUint16BE(); + _itemRoom = stream->readUint16BE(); + _itemDirection = stream->readByte(); + _itemOwnerID = stream->readUint16BE(); + _itemState = stream->readUint16BE(); +} + +ActorID Item::getItemOwner() const { + return _itemOwnerID; +} + +void Item::setItemOwner(const ActorID owner) { + _itemOwnerID = owner; + + if (owner == kNoActorID) { + if (isSelected()) + deselect(); + removedFromInventory(); + } else { + addedToInventory(); + } +} + +WeightType Item::getItemWeight() { + return _itemWeight; +} + +ItemState Item::getItemState() const { + return _itemState; +} + +void Item::setItemState(const ItemState state) { + if (state != _itemState) { + _itemState = state; + + if (getItemType() == kInventoryItemType && ((PegasusEngine *)g_engine)->getCurrentInventoryItem() == (InventoryItem *)this) + select(); + else if (getItemType() == kBiochipItemType && ((PegasusEngine *)g_engine)->getCurrentBiochip() == (BiochipItem *)this) + select(); + } +} + +void Item::getItemRoom(NeighborhoodID &neighborhood, RoomID &room, DirectionConstant &direction) const { + neighborhood = _itemNeighborhood; + room = _itemRoom; + direction = _itemDirection; +} + +void Item::setItemRoom(const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) { + _itemNeighborhood = neighborhood; + _itemRoom = room; + _itemDirection = direction; + + if (neighborhood == kNoNeighborhoodID) + pickedUp(); + else + dropped(); +} + +NeighborhoodID Item::getItemNeighborhood() const { + return _itemNeighborhood; +} + +TimeValue Item::getSharedAreaTime() const { + if (!_sharedAreaInfo.entries) + return 0xffffffff; + + TimeValue time; + ItemState state; + + findItemStateEntryByState(_sharedAreaInfo, _itemState, time); + if (time == 0xffffffff) + getItemStateEntry(_sharedAreaInfo, 0, state, time); + + return time; +} + +// Must affect images in shared area. +void Item::select() { + _isSelected = true; + + if (g_AIArea) { + if (getItemType() == kInventoryItemType) + g_AIArea->setAIAreaToTime(kInventorySignature, kMiddleAreaSignature, getSharedAreaTime()); + else + g_AIArea->setAIAreaToTime(kBiochipSignature, kMiddleAreaSignature, getSharedAreaTime()); + } +} + +void Item::deselect() { + _isActive = false; + _isSelected = false; + + if (g_AIArea) { + if (getItemType() == kInventoryItemType) + g_AIArea->setAIAreaToTime(kInventorySignature, kMiddleAreaSignature, 0xffffffff); + else + g_AIArea->setAIAreaToTime(kBiochipSignature, kMiddleAreaSignature, 0xffffffff); + } +} + +void Item::getItemStateEntry(ItemStateInfo info, uint32 index, ItemState &state, TimeValue &time) { + if (index < info.numEntries) { + state = info.entries[index].itemState; + time = info.entries[index].itemTime; + } else { + state = kNoItemState; + time = 0xffffffff; + } +} + +void Item::findItemStateEntryByState(ItemStateInfo info, ItemState state, TimeValue &time) { + for (uint16 i = 0; i < info.numEntries; i++) { + if (info.entries[i].itemState == state) { + time = info.entries[i].itemTime; + return; + } + } + + time = 0xffffffff; +} + +ItemStateInfo Item::readItemState(Common::SeekableReadStream *stream) { + ItemStateInfo info; + + info.numEntries = stream->readUint16BE(); + info.entries = new ItemStateEntry[info.numEntries]; + for (uint16 i = 0; i < info.numEntries; i++) { + info.entries[i].itemState = stream->readSint16BE(); + info.entries[i].itemTime = stream->readUint32BE(); + } + + return info; +} + +Sprite *Item::getDragSprite(const DisplayElementID id) const { + PegasusEngine *vm = (PegasusEngine *)g_engine; + Sprite *result = new Sprite(id); + SpriteFrame *frame = new SpriteFrame(); + + frame->initFromPICTResource(vm->_resFork, _itemInfo.dragSpriteNormalID, true); + result->addFrame(frame, 0, 0); + + if (_itemInfo.dragSpriteNormalID != _itemInfo.dragSpriteUsedID) { + frame = new SpriteFrame(); + frame->initFromPICTResource(vm->_resFork, _itemInfo.dragSpriteUsedID, true); + } + + result->addFrame(frame, 0, 0); + result->setCurrentFrameIndex(0); + return result; +} + +TimeValue Item::getInfoLeftTime() const { + return _itemInfo.infoLeftTime; +} + +void Item::getInfoRightTimes(TimeValue &start, TimeValue &stop) const { + start = _itemInfo.infoRightStart; + stop = _itemInfo.infoRightStop; +} + +void Item::findItemExtra(const uint32 extraID, ItemExtraEntry &entry) { + for (uint32 i = 0; i < _itemExtras.numEntries; i++) { + if (_itemExtras.entries[i].extraID == extraID) { + entry = _itemExtras.entries[i]; + return; + } + } +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/item.h b/engines/pegasus/items/item.h new file mode 100644 index 0000000000..a1451b2a58 --- /dev/null +++ b/engines/pegasus/items/item.h @@ -0,0 +1,363 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_ITEMS_ITEM_H +#define PEGASUS_ITEMS_ITEM_H + +#include "common/endian.h" + +#include "pegasus/types.h" +#include "pegasus/util.h" + +namespace Common { + class Error; + class ReadStream; + class WriteStream; + class SeekableReadStream; +} + +namespace Pegasus { + +// JMPItemInfo contains resource data used by all Items. + +struct JMPItemInfo { + TimeValue infoLeftTime; + TimeValue infoRightStart; + TimeValue infoRightStop; + uint32 dragSpriteNormalID; + uint32 dragSpriteUsedID; +}; + +// ItemStateEntry contains a single state/TimeValue pair. The TimeValue is +// the time value to set the shared area movie that corresponds with the given +// state of an inventory item. + +struct ItemStateEntry { + ItemState itemState; + TimeValue itemTime; +}; + +struct ItemStateInfo { + uint16 numEntries; // For easy ResEdit access + ItemStateEntry *entries; +}; + +// ItemExtraEntry + +static const short kLeftAreaExtra = 0; +static const short kMiddleAreaExtra = 1; +static const short kRightAreaExtra = 2; + +struct ItemExtraEntry { + uint32 extraID; + uint16 extraArea; + TimeValue extraStart; + TimeValue extraStop; +}; + +struct ItemExtraInfo { + uint16 numEntries; // For easy ResEdit access + ItemExtraEntry *entries; +}; + +// Inventory info resource type and ID: +// Individual inventory items are stored in these resource types. +// Resource ID is item ID + kItemBaseResID. + +static const uint32 kItemInfoResType = MKTAG('I', 't', 'e', 'm'); // JMPItemInfoHandle +static const uint32 kLeftAreaInfoResType = MKTAG('L', 'e', 'f', 't'); // ItemStateInfoHandle +static const uint32 kMiddleAreaInfoResType = MKTAG('M', 'i', 'd', 'l'); // ItemStateInfoHandle +static const uint32 kRightAreaInfoResType = MKTAG('R', 'g', 'h', 't'); // ItemStateInfoHandle +static const uint32 kItemExtraInfoResType = MKTAG('I', 'X', 't', 'r'); // ItemExtraInfoHandle + +static const uint16 kItemBaseResID = 128; + +// Item IDs. + +static const ItemID kAirMask = 7; +static const ItemID kAntidote = 8; +static const ItemID kArgonCanister = 9; +static const ItemID kCardBomb = 10; +static const ItemID kCrowbar = 11; +static const ItemID kGasCanister = 12; +static const ItemID kHistoricalLog = 13; +static const ItemID kJourneymanKey = 14; +static const ItemID kKeyCard = 15; +static const ItemID kMachineGun = 16; +static const ItemID kMarsCard = 17; +static const ItemID kNitrogenCanister = 18; +static const ItemID kOrangeJuiceGlassFull = 19; +static const ItemID kOrangeJuiceGlassEmpty = 20; +static const ItemID kPoisonDart = 21; +static const ItemID kSinclairKey = 22; +static const ItemID kStunGun = 23; +static const ItemID kArgonPickup = 24; + +// Biochips. + +static const ItemID kAIBiochip = 0; +static const ItemID kInterfaceBiochip = 1; +static const ItemID kMapBiochip = 2; +static const ItemID kOpticalBiochip = 3; +static const ItemID kPegasusBiochip = 4; +static const ItemID kRetinalScanBiochip = 5; +static const ItemID kShieldBiochip = 6; + +static const ItemID kNumItems = 25; + +// Item States. + +static const ItemState kAI000 = 0; +static const ItemState kAI005 = 1; +static const ItemState kAI006 = 2; +static const ItemState kAI010 = 3; +static const ItemState kAI015 = 4; +static const ItemState kAI016 = 5; +static const ItemState kAI020 = 6; +static const ItemState kAI024 = 7; +static const ItemState kAI100 = 8; +static const ItemState kAI101 = 9; +static const ItemState kAI105 = 10; +static const ItemState kAI106 = 11; +static const ItemState kAI110 = 12; +static const ItemState kAI111 = 13; +static const ItemState kAI115 = 14; +static const ItemState kAI116 = 15; +static const ItemState kAI120 = 16; +static const ItemState kAI121 = 17; +static const ItemState kAI124 = 18; +static const ItemState kAI125 = 19; +static const ItemState kAI126 = 20; +static const ItemState kAI200 = 21; +static const ItemState kAI201 = 22; +static const ItemState kAI202 = 23; +static const ItemState kAI205 = 24; +static const ItemState kAI206 = 25; +static const ItemState kAI210 = 26; +static const ItemState kAI211 = 27; +static const ItemState kAI212 = 28; +static const ItemState kAI215 = 29; +static const ItemState kAI216 = 30; +static const ItemState kAI220 = 31; +static const ItemState kAI221 = 32; +static const ItemState kAI222 = 33; +static const ItemState kAI224 = 34; +static const ItemState kAI225 = 35; +static const ItemState kAI226 = 36; +static const ItemState kAI300 = 37; +static const ItemState kAI301 = 38; +static const ItemState kAI302 = 39; +static const ItemState kAI303 = 40; +static const ItemState kAI305 = 41; +static const ItemState kAI306 = 42; +static const ItemState kAI310 = 43; +static const ItemState kAI311 = 44; +static const ItemState kAI312 = 45; +static const ItemState kAI313 = 46; +static const ItemState kAI315 = 47; +static const ItemState kAI316 = 48; +static const ItemState kAI320 = 49; +static const ItemState kAI321 = 50; +static const ItemState kAI322 = 51; +static const ItemState kAI323 = 52; +static const ItemState kAI324 = 53; +static const ItemState kAI325 = 54; +static const ItemState kAI326 = 55; +static const ItemState kNormalItem = 56; +static const ItemState kMapUnavailable = 57; +static const ItemState kMapEngaged = 58; +static const ItemState kOptical000 = 59; +static const ItemState kOptical001 = 60; +static const ItemState kOptical002 = 61; +static const ItemState kOptical010 = 62; +static const ItemState kOptical011 = 63; +static const ItemState kOptical012 = 64; +static const ItemState kOptical020 = 65; +static const ItemState kOptical021 = 66; +static const ItemState kOptical100 = 67; +static const ItemState kOptical101 = 68; +static const ItemState kOptical102 = 69; +static const ItemState kOptical110 = 70; +static const ItemState kOptical111 = 71; +static const ItemState kOptical112 = 72; +static const ItemState kOptical120 = 73; +static const ItemState kOptical121 = 74; +static const ItemState kOptical200 = 75; +static const ItemState kOptical201 = 76; +static const ItemState kOptical210 = 77; +static const ItemState kOptical211 = 78; +static const ItemState kPegasusTSA00 = 79; +static const ItemState kPegasusTSA10 = 80; +static const ItemState kPegasusPrehistoric00 = 81; +static const ItemState kPegasusPrehistoric01 = 82; +static const ItemState kPegasusPrehistoric10 = 83; +static const ItemState kPegasusPrehistoric11 = 84; +static const ItemState kPegasusMars00 = 85; +static const ItemState kPegasusMars01 = 86; +static const ItemState kPegasusMars10 = 87; +static const ItemState kPegasusMars11 = 88; +static const ItemState kPegasusNorad00 = 89; +static const ItemState kPegasusNorad01 = 90; +static const ItemState kPegasusNorad10 = 91; +static const ItemState kPegasusNorad11 = 92; +static const ItemState kPegasusWSC00 = 93; +static const ItemState kPegasusWSC01 = 94; +static const ItemState kPegasusWSC10 = 95; +static const ItemState kPegasusWSC11 = 96; +static const ItemState kPegasusCaldoria = 97; +static const ItemState kRetinalSimulating = 98; +static const ItemState kShieldNormal = 99; +static const ItemState kShieldRadiation = 100; +static const ItemState kShieldPlasma = 101; +static const ItemState kShieldCardBomb = 102; +static const ItemState kShieldDraining = 103; +static const ItemState kAirMaskEmptyOff = 104; +static const ItemState kAirMaskEmptyFilter = 105; +static const ItemState kAirMaskLowOff = 106; +static const ItemState kAirMaskLowFilter = 107; +static const ItemState kAirMaskLowOn = 108; +static const ItemState kAirMaskFullOff = 109; +static const ItemState kAirMaskFullFilter = 110; +static const ItemState kAirMaskFullOn = 111; +static const ItemState kArgonEmpty = 112; +static const ItemState kArgonFull = 113; +static const ItemState kFlashlightOff = 114; +static const ItemState kFlashlightOn = 115; +static const ItemState kNitrogenEmpty = 116; +static const ItemState kNitrogenFull = 117; +static const ItemState kFullGlass = 118; + +// Extra IDs. + +static const uint32 kRetinalScanSearching = 0; +static const uint32 kRetinalScanActivated = 1; +static const uint32 kShieldIntro = 2; +static const uint32 kRemoveAirMask = 3; +static const uint32 kRemoveArgon = 4; +static const uint32 kRemoveCrowbar = 5; +static const uint32 kGasCanLoop = 6; +static const uint32 kRemoveJourneymanKey = 7; +static const uint32 kRemoveMarsCard = 8; +static const uint32 kRemoveNitrogen = 9; +static const uint32 kRemoveGlass = 10; +static const uint32 kRemoveDart = 11; +static const uint32 kRemoveSinclairKey = 12; + +enum ItemType { + kInventoryItemType, + kBiochipItemType +}; + +class Sprite; + +/* + + Item is an object which can be picked up and carried around. + Items have + a location + an ID. + weight + an owner (kNoActorID if no one is carrying the Item) + +*/ + +class Item : public IDObject { +public: + Item(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction); + virtual ~Item(); + + // WriteToStream writes everything EXCEPT the item's ID. + // It is assumed that the calling function will write and read the ID. + virtual void writeToStream(Common::WriteStream *stream); + virtual void readFromStream(Common::ReadStream *stream); + + virtual ActorID getItemOwner() const; + virtual void setItemOwner(const ActorID owner); + + void getItemRoom(NeighborhoodID &neighborhood, RoomID &room, DirectionConstant &direction) const; + void setItemRoom(const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction); + NeighborhoodID getItemNeighborhood() const; + + virtual WeightType getItemWeight(); + + virtual void setItemState(const ItemState state); + virtual ItemState getItemState() const; + + virtual ItemType getItemType() = 0; + + TimeValue getInfoLeftTime() const; + void getInfoRightTimes(TimeValue &, TimeValue &) const; + TimeValue getSharedAreaTime() const; + + Sprite *getDragSprite(const DisplayElementID) const; + + /* + select -- called when this item becomes current. Also called when the inventory + panel holding this item is raised and this is the current item. + deselect -- called when this item is no longer current. + activate -- called on the current item when the panel is closed. + */ + // In an override of these three member functions, you must call the inherited + // member functions. + virtual void select(); + virtual void deselect(); + virtual bool isSelected() { return _isSelected; } + + virtual void activate() { _isActive = true; } + virtual bool isActive() { return _isActive; } + virtual void pickedUp() {} + virtual void addedToInventory() {} + virtual void removedFromInventory() {} + virtual void dropped() {} + + // Called when the shared area is taken by another item, but this item is still + // selected. + virtual void giveUpSharedArea() {} + virtual void takeSharedArea() {} + + void findItemExtra(const uint32 extraID, ItemExtraEntry &entry); + +protected: + NeighborhoodID _itemNeighborhood; + RoomID _itemRoom; + DirectionConstant _itemDirection; + ActorID _itemOwnerID; + WeightType _itemWeight; + ItemState _itemState; + + JMPItemInfo _itemInfo; + ItemStateInfo _sharedAreaInfo; + ItemExtraInfo _itemExtras; + bool _isActive; + bool _isSelected; + + static void getItemStateEntry(ItemStateInfo, uint32, ItemState &, TimeValue &); + static void findItemStateEntryByState(ItemStateInfo, ItemState, TimeValue &); + static ItemStateInfo readItemState(Common::SeekableReadStream *stream); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/itemdragger.cpp b/engines/pegasus/items/itemdragger.cpp new file mode 100644 index 0000000000..97fc5a97ac --- /dev/null +++ b/engines/pegasus/items/itemdragger.cpp @@ -0,0 +1,193 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/elements.h" +#include "pegasus/hotspot.h" +#include "pegasus/pegasus.h" +#include "pegasus/items/itemdragger.h" + +namespace Pegasus { + +SpriteDragger::SpriteDragger() { + _draggingSprite = 0; + _limitRect = Common::Rect(-30000, -30000, 30000, 30000); + _slopRect = Common::Rect(-30000, -30000, 30000, 30000); + _dragOffset.x = 0; + _dragOffset.y = 0; + _lastHotspot = 0; +} + +void SpriteDragger::setDragSprite(Sprite *newSprite) { + if (!isTracking()) + _draggingSprite = newSprite; +} + +void SpriteDragger::setDragConstraints(const Common::Rect &limitRect, const Common::Rect &slopRect) { + if (!isTracking()) { + _rawLimitRect = limitRect; + _slopRect = slopRect; + } +} + +void SpriteDragger::getDragConstraints(Common::Rect &limitRect, Common::Rect &slopRect) const { + limitRect = _rawLimitRect; + slopRect = _slopRect; +} + +void SpriteDragger::startTracking(const Input &input) { + if (_draggingSprite) { + Tracker::startTracking(input); + + if (isTracking()) { + input.getInputLocation(_startPoint); + _lastRawPoint = _startRawPoint = _startPoint; + + Common::Rect r; + _draggingSprite->getBounds(r); + _dragOffset.x = _startPoint.x - r.left; + _dragOffset.y = _startPoint.y - r.top; + + _limitRect = _rawLimitRect; + _limitRect.left += _dragOffset.x; + _limitRect.top += _dragOffset.y; + _limitRect.right -= r.width() - _dragOffset.x; + _limitRect.bottom -= r.height() - _dragOffset.y; + pinPointInRect(_limitRect, _startPoint); + + _lastPoint = _startPoint; + if (_startPoint != _startRawPoint) { + Common::Point pt = _startPoint - _dragOffset; + _draggingSprite->moveElementTo(pt.x, pt.y); + } + + _lastHotspot = g_allHotspots.findHotspot(_lastRawPoint); + if (_lastHotspot) + enterHotspot(_lastHotspot); + } + } +} + +void SpriteDragger::continueTracking(const Input &input) { + if (_draggingSprite) { + Common::Point rawPoint; + input.getInputLocation(rawPoint); + + if (!_slopRect.contains(rawPoint)) + rawPoint = _startRawPoint; + + if (rawPoint != _lastRawPoint) { + Common::Point newPoint = rawPoint; + pinPointInRect(_limitRect, newPoint); + newPoint -= _dragOffset; + + if (newPoint != _lastPoint) { + _draggingSprite->moveElementTo(newPoint.x, newPoint.y); + _lastPoint = newPoint; + } + + Hotspot *newHotspot = g_allHotspots.findHotspot(rawPoint); + if (newHotspot != _lastHotspot) { + if (_lastHotspot) + exitHotspot(_lastHotspot); + if (newHotspot) + enterHotspot(newHotspot); + _lastHotspot = newHotspot; + } + + _lastRawPoint = rawPoint; + } + } +} + +void SpriteDragger::pinPointInRect(const Common::Rect &r, Common::Point &pt) { + pt.x = CLIP<int>(pt.x, r.left, r.right - 1); + pt.y = CLIP<int>(pt.y, r.top, r.bottom - 1); +} + +ItemDragger::ItemDragger(PegasusEngine *owner) : _inventoryDropSpot(kInventoryDropSpotID), _biochipDropSpot(kBiochipDropSpotID), + _inventoryHighlight(kInventoryDropHighlightID), _biochipHighlight(kBiochipDropHighlightID) { + _owner = owner; + + Common::Rect r(kInventoryDropLeft, kInventoryDropTop, kInventoryDropRight, kInventoryDropBottom); + _inventoryDropSpot.setArea(r); + _inventoryDropSpot.setHotspotFlags(kDropItemSpotFlag); + g_allHotspots.push_back(&_inventoryDropSpot); + + r = Common::Rect(kBiochipDropLeft, kBiochipDropTop, kBiochipDropRight, kBiochipDropBottom); + _biochipDropSpot.setArea(r); + _biochipDropSpot.setHotspotFlags(kDropBiochipSpotFlag); + g_allHotspots.push_back(&_biochipDropSpot); +} + +void ItemDragger::startTracking(const Input &input) { + _inventoryHighlight.setDisplayOrder(kInventoryHiliteOrder); + _inventoryHighlight.startDisplaying(); + + _biochipHighlight.setDisplayOrder(kBiochipHiliteOrder); + _biochipHighlight.startDisplaying(); + + SpriteDragger::startTracking(input); +} + +void ItemDragger::stopTracking(const Input &input) { + SpriteDragger::stopTracking(input); + _inventoryHighlight.hide(); + _biochipHighlight.hide(); + _inventoryHighlight.stopDisplaying(); + _biochipHighlight.stopDisplaying(); + _owner->dragTerminated(input); +} + +bool ItemDragger::stopTrackingInput(const Input &input) { + return !JMPPPInput::isDraggingInput(input); +} + +void ItemDragger::enterHotspot(Hotspot *spot) { + if (spot->getObjectID() == kInventoryDropSpotID) + _inventoryHighlight.show(); + else if (spot->getObjectID() == kBiochipDropSpotID) + _biochipHighlight.show(); + else if ((spot->getHotspotFlags() & kDropItemSpotFlag) != 0) + _draggingSprite->setCurrentFrameIndex(1); +} + +void ItemDragger::exitHotspot(Hotspot *spot) { + if (spot->getObjectID() == kInventoryDropSpotID) + _inventoryHighlight.hide(); + else if (spot->getObjectID() == kBiochipDropSpotID) + _biochipHighlight.hide(); + else if ((spot->getHotspotFlags() & kDropItemSpotFlag) != 0) + _draggingSprite->setCurrentFrameIndex(0); +} + +void ItemDragger::setHighlightBounds() { + uint32 color = g_system->getScreenFormat().RGBToColor(0x48, 0x80, 0xD8); + _inventoryHighlight.setBounds(Common::Rect(76, 334, 172, 430)); + _inventoryHighlight.setHighlightColor(color); + _biochipHighlight.setBounds(Common::Rect(364, 334, 460, 430)); + _biochipHighlight.setHighlightColor(color); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/itemdragger.h b/engines/pegasus/items/itemdragger.h new file mode 100644 index 0000000000..fce953d695 --- /dev/null +++ b/engines/pegasus/items/itemdragger.h @@ -0,0 +1,96 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_ITEMS_ITEMDRAGGER_H +#define PEGASUS_ITEMS_ITEMDRAGGER_H + +#include "pegasus/elements.h" +#include "pegasus/input.h" + +namespace Pegasus { + +// TODO: Merge SpriteDragger and ItemDragger + +class Hotspot; +class Sprite; + +class SpriteDragger : public Tracker { +public: + SpriteDragger(); + virtual ~SpriteDragger() {} + + void setDragSprite(Sprite *); + Sprite *getDragSprite() { return _draggingSprite; } + + void setDragConstraints(const Common::Rect &, const Common::Rect &); + void getDragConstraints(Common::Rect &, Common::Rect &) const; + + void startTracking(const Input &); + void continueTracking(const Input&); + + Hotspot *getLastHotspot() const { return _lastHotspot; } + +protected: + virtual void enterHotspot(Hotspot *) {} + virtual void exitHotspot(Hotspot *) {} + + Sprite *_draggingSprite; + Common::Point _startPoint, _lastPoint, _dragOffset; + Common::Point _startRawPoint, _lastRawPoint; + Common::Rect _rawLimitRect; + Common::Rect _limitRect; + Common::Rect _slopRect; + Hotspot *_lastHotspot; + + // This is a replica of QuickDraw's PinPointInRect function + void pinPointInRect(const Common::Rect &, Common::Point &); +}; + +class PegasusEngine; + +class ItemDragger : public SpriteDragger { +public: + ItemDragger(PegasusEngine *); + virtual ~ItemDragger() {} + + void setHighlightBounds(); + void startTracking(const Input &); + void stopTracking(const Input &); + bool stopTrackingInput(const Input &); + +protected: + virtual void enterHotspot(Hotspot *); + virtual void exitHotspot(Hotspot *); + + PegasusEngine *_owner; + DropHighlight _inventoryHighlight; + Hotspot _inventoryDropSpot; + DropHighlight _biochipHighlight; + Hotspot _biochipDropSpot; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/itemlist.cpp b/engines/pegasus/items/itemlist.cpp new file mode 100644 index 0000000000..ff8cae546b --- /dev/null +++ b/engines/pegasus/items/itemlist.cpp @@ -0,0 +1,67 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "common/error.h" +#include "common/stream.h" + +#include "pegasus/pegasus.h" +#include "pegasus/items/item.h" +#include "pegasus/items/itemlist.h" + +namespace Pegasus { + +ItemList::ItemList() { +} + +ItemList::~ItemList() { +} + +void ItemList::writeToStream(Common::WriteStream *stream) { + stream->writeUint32BE(size()); + + for (ItemIterator it = begin(); it != end(); it++) { + stream->writeUint16BE((*it)->getObjectID()); + (*it)->writeToStream(stream); + } +} + +void ItemList::readFromStream(Common::ReadStream *stream) { + uint32 itemCount = stream->readUint32BE(); + + for (uint32 i = 0; i < itemCount; i++) { + ItemID itemID = stream->readUint16BE(); + g_allItems.findItemByID(itemID)->readFromStream(stream); + } +} + +Item *ItemList::findItemByID(const ItemID id) { + for (ItemIterator it = begin(); it != end(); it++) + if ((*it)->getObjectID() == id) + return *it; + + return 0; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/itemlist.h b/engines/pegasus/items/itemlist.h new file mode 100644 index 0000000000..9b59206ab3 --- /dev/null +++ b/engines/pegasus/items/itemlist.h @@ -0,0 +1,59 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_ITEMS_ITEMLIST_H +#define PEGASUS_ITEMS_ITEMLIST_H + +#include "common/list.h" + +#include "pegasus/types.h" + +namespace Common { + class ReadStream; + class WriteStream; +} + +namespace Pegasus { + +class Item; + +class ItemList : public Common::List<Item *> { +public: + ItemList(); + virtual ~ItemList(); + + virtual void writeToStream(Common::WriteStream *stream); + virtual void readFromStream(Common::ReadStream *stream); + + Item *findItemByID(const ItemID id); +}; + +typedef ItemList::iterator ItemIterator; + +#define g_allItems (((PegasusEngine *)g_engine)->getAllItems()) + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/menu.cpp b/engines/pegasus/menu.cpp new file mode 100644 index 0000000000..deaa460188 --- /dev/null +++ b/engines/pegasus/menu.cpp @@ -0,0 +1,1219 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/gamestate.h" +#include "pegasus/menu.h" +#include "pegasus/pegasus.h" +#include "pegasus/scoring.h" + +namespace Pegasus { + +GameMenu::GameMenu(const uint32 id) : IDObject(id), InputHandler((InputHandler *)((PegasusEngine *)g_engine)) { + _previousHandler = 0; + _lastCommand = kMenuCmdNoCommand; +} + +void GameMenu::becomeCurrentHandler() { + _previousHandler = InputHandler::setInputHandler(this); +} + +void GameMenu::restorePreviousHandler() { + InputHandler::setInputHandler(_previousHandler); +} + +void GameMenu::drawScore(GameScoreType score, GameScoreType total, const Common::Rect &scoreBounds, Surface *numbers) { + CoordType x = scoreBounds.right; + drawNumber(total, x, scoreBounds.top, numbers); + + x -= 12; + Common::Rect r1(120, 0, 132, 12); // The slash. + Common::Rect r2 = r1; + r2.moveTo(x, scoreBounds.top); + numbers->copyToCurrentPort(r1, r2); + drawNumber(score, x, scoreBounds.top, numbers); +} + +void GameMenu::drawNumber(GameScoreType number, CoordType &x, CoordType y, Surface *numbers) { + Common::Rect r1(0, 0, 12, 12); // Width, height of one digit + Common::Rect r2 = r1; + r2.moveTo(x - 12, y); + + do { + uint16 digit = number % 10; + number /= 10; + r1.moveTo(digit * 12, 0); + numbers->copyToCurrentPort(r1, r2); + r2.translate(-12, 0); + } while (number != 0); + + x = r2.right; +} + +enum { + kMainMenuStartDemo = 0, + kMainMenuCreditsDemo, + kMainMenuQuitDemo, + kFirstSelectionDemo = kMainMenuStartDemo, + kLastSelectionDemo = kMainMenuQuitDemo, + + kMainMenuOverview = 0, + kMainMenuStart, + kMainMenuRestore, + kMainMenuDifficulty, + kMainMenuCredits, + kMainMenuQuit, + kFirstSelection = kMainMenuOverview, + kLastSelection = kMainMenuQuit +}; + +static const CoordType kStartLeftDemo = 44; +static const CoordType kStartTopDemo = 336; + +static const CoordType kStartSelectLeftDemo = 40; +static const CoordType kStartSelectTopDemo = 331; + +static const CoordType kCreditsLeftDemo = 44; +static const CoordType kCreditsTopDemo = 372; + +static const CoordType kCreditsSelectLeftDemo = 40; +static const CoordType kCreditsSelectTopDemo = 367; + +static const CoordType kMainMenuQuitLeftDemo = 32; +static const CoordType kMainMenuQuitTopDemo = 412; + +static const CoordType kMainMenuQuitSelectLeftDemo = 28; +static const CoordType kMainMenuQuitSelectTopDemo = 408; + +static const CoordType kOverviewLeft = 200; +static const CoordType kOverviewTop = 208; + +static const CoordType kOverviewSelectLeft = 152; +static const CoordType kOverviewSelectTop = 204; + +static const CoordType kStartLeft = 212; +static const CoordType kStartTop = 256; + +static const CoordType kStartSelectLeft = 152; +static const CoordType kStartSelectTop = 252; + +static const CoordType kRestoreLeft = 212; +static const CoordType kRestoreTop = 296; + +static const CoordType kRestoreSelectLeft = 152; +static const CoordType kRestoreSelectTop = 292; + +static const CoordType kDifficultyLeft = 320; +static const CoordType kDifficultyTop = 340; + +static const CoordType kDifficultySelectLeft = 152; +static const CoordType kDifficultySelectTop = 336; + +static const CoordType kCreditsLeft = 212; +static const CoordType kCreditsTop = 388; + +static const CoordType kCreditsSelectLeft = 152; +static const CoordType kCreditsSelectTop = 384; + +static const CoordType kMainMenuQuitLeft = 212; +static const CoordType kMainMenuQuitTop = 428; + +static const CoordType kMainMenuQuitSelectLeft = 152; +static const CoordType kMainMenuQuitSelectTop = 424; + +// Never set the current input handler to the MainMenu. +MainMenu::MainMenu() : GameMenu(kMainMenuID), _menuBackground(0), _overviewButton(0), + _restoreButton(0), _adventureButton(0), _walkthroughButton(0), _startButton(0), + _creditsButton(0), _quitButton(0), _largeSelect(0), _smallSelect(0) { + + bool isDemo = ((PegasusEngine *)g_engine)->isDemo(); + + if (isDemo) + _menuBackground.initFromPICTFile("Images/Demo/DemoMenu.pict"); + else + _menuBackground.initFromPICTFile("Images/Main Menu/MainMenu.mac"); + _menuBackground.setDisplayOrder(0); + _menuBackground.startDisplaying(); + _menuBackground.show(); + + if (!isDemo) { + _overviewButton.initFromPICTFile("Images/Main Menu/pbOvervi.pict"); + _overviewButton.setDisplayOrder(1); + _overviewButton.moveElementTo(kOverviewLeft, kOverviewTop); + _overviewButton.startDisplaying(); + + _restoreButton.initFromPICTFile("Images/Main Menu/pbRestor.pict"); + _restoreButton.setDisplayOrder(1); + _restoreButton.moveElementTo(kRestoreLeft, kRestoreTop); + _restoreButton.startDisplaying(); + + _adventureButton.initFromPICTFile("Images/Main Menu/BtnAdv.pict"); + _adventureButton.setDisplayOrder(1); + _adventureButton.moveElementTo(kDifficultyLeft, kDifficultyTop); + _adventureButton.startDisplaying(); + + _walkthroughButton.initFromPICTFile("Images/Main Menu/BtnWlk.pict"); + _walkthroughButton.setDisplayOrder(1); + _walkthroughButton.moveElementTo(kDifficultyLeft, kDifficultyTop); + _walkthroughButton.startDisplaying(); + } + + if (isDemo) + _startButton.initFromPICTFile("Images/Demo/Start.pict"); + else + _startButton.initFromPICTFile("Images/Main Menu/pbStart.pict"); + _startButton.setDisplayOrder(1); + _startButton.moveElementTo(isDemo ? kStartLeftDemo : kStartLeft, isDemo ? kStartTopDemo : kStartTop); + _startButton.startDisplaying(); + + if (isDemo) + _creditsButton.initFromPICTFile("Images/Demo/Credits.pict"); + else + _creditsButton.initFromPICTFile("Images/Main Menu/pbCredit.pict"); + _creditsButton.setDisplayOrder(1); + _creditsButton.moveElementTo(isDemo ? kCreditsLeftDemo : kCreditsLeft, isDemo ? kCreditsTopDemo : kCreditsTop); + _creditsButton.startDisplaying(); + + if (isDemo) + _quitButton.initFromPICTFile("Images/Demo/Quit.pict"); + else + _quitButton.initFromPICTFile("Images/Main Menu/pbQuit.pict"); + _quitButton.setDisplayOrder(1); + _quitButton.moveElementTo(isDemo ? kMainMenuQuitLeftDemo : kMainMenuQuitLeft, isDemo ? kMainMenuQuitTopDemo : kMainMenuQuitTop); + _quitButton.startDisplaying(); + + if (isDemo) + _largeSelect.initFromPICTFile("Images/Demo/SelectL.pict", true); + else + _largeSelect.initFromPICTFile("Images/Main Menu/SelectL.pict", true); + _largeSelect.setDisplayOrder(1); + _largeSelect.startDisplaying(); + + if (isDemo) + _smallSelect.initFromPICTFile("Images/Demo/SelectS.pict", true); + else + _smallSelect.initFromPICTFile("Images/Main Menu/SelectS.pict", true); + _smallSelect.setDisplayOrder(1); + _smallSelect.startDisplaying(); + + _menuSelection = isDemo ? kFirstSelectionDemo : kFirstSelection; + + _adventureMode = true; + + _menuLoop.attachFader(&_menuFader); + _menuLoop.initFromAIFFFile("Sounds/Main Menu.aiff"); + + updateDisplay(); +} + +MainMenu::~MainMenu() { + if (_menuLoop.isPlaying()) + stopMainMenuLoop(); +} + +void MainMenu::startMainMenuLoop() { + FaderMoveSpec spec; + + _menuLoop.loopSound(); + spec.makeTwoKnotFaderSpec(30, 0, 0, 30, 255); + _menuFader.startFaderSync(spec); +} + +void MainMenu::stopMainMenuLoop() { + FaderMoveSpec spec; + + spec.makeTwoKnotFaderSpec(30, 0, 255, 30, 0); + _menuFader.startFaderSync(spec); + _menuLoop.stopSound(); +} + +void MainMenu::handleInput(const Input &input, const Hotspot *cursorSpot) { + PegasusEngine *vm = (PegasusEngine *)g_engine; + bool isDemo = vm->isDemo(); + + if (input.upButtonDown()) { + if (_menuSelection > (isDemo ? kFirstSelectionDemo : kFirstSelection)) { + _menuSelection--; + updateDisplay(); + } + } else if (input.downButtonDown()) { + if (_menuSelection < (isDemo ? kLastSelectionDemo : kLastSelection)) { + _menuSelection++; + updateDisplay(); + } + } else if (!isDemo && (input.leftButtonDown() || input.rightButtonDown())) { + if (_menuSelection == kMainMenuDifficulty) { + _adventureMode = !_adventureMode; + updateDisplay(); + } + } else if (JMPPPInput::isMenuButtonPressInput(input)) { + if (isDemo) { + switch (_menuSelection) { + case kMainMenuCreditsDemo: + _creditsButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _creditsButton.hide(); + setLastCommand(kMenuCmdCredits); + break; + case kMainMenuStartDemo: + _startButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _startButton.hide(); + setLastCommand(kMenuCmdStartAdventure); + break; + case kMainMenuQuitDemo: + _quitButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _quitButton.hide(); + setLastCommand(kMenuCmdQuit); + break; + } + } else { + switch (_menuSelection) { + case kMainMenuOverview: + _overviewButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _overviewButton.hide(); + setLastCommand(kMenuCmdOverview); + break; + case kMainMenuRestore: + _restoreButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _restoreButton.hide(); + setLastCommand(kMenuCmdRestore); + break; + case kMainMenuCredits: + _creditsButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _creditsButton.hide(); + setLastCommand(kMenuCmdCredits); + break; + case kMainMenuStart: + _startButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _startButton.hide(); + if (_adventureMode) + setLastCommand(kMenuCmdStartAdventure); + else + setLastCommand(kMenuCmdStartWalkthrough); + break; + case kMainMenuDifficulty: + _adventureMode = !_adventureMode; + updateDisplay(); + break; + case kMainMenuQuit: + _quitButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _quitButton.hide(); + setLastCommand(kMenuCmdQuit); + break; + } + } + } + + InputHandler::handleInput(input, cursorSpot); +} + +void MainMenu::updateDisplay() { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + if (vm->isDemo()) { + switch (_menuSelection) { + case kMainMenuStartDemo: + _smallSelect.moveElementTo(kStartSelectLeftDemo, kStartSelectTopDemo); + _smallSelect.show(); + _largeSelect.hide(); + break; + case kMainMenuCreditsDemo: + _smallSelect.moveElementTo(kCreditsSelectLeftDemo, kCreditsSelectTopDemo); + _smallSelect.show(); + _largeSelect.hide(); + break; + case kMainMenuQuitDemo: + _largeSelect.moveElementTo(kMainMenuQuitSelectLeftDemo, kMainMenuQuitSelectTopDemo); + _largeSelect.show(); + _smallSelect.hide(); + break; + } + } else { + switch (_menuSelection) { + case kMainMenuOverview: + _largeSelect.moveElementTo(kOverviewSelectLeft, kOverviewSelectTop); + _largeSelect.show(); + _smallSelect.hide(); + break; + case kMainMenuRestore: + _smallSelect.moveElementTo(kRestoreSelectLeft, kRestoreSelectTop); + _smallSelect.show(); + _largeSelect.hide(); + break; + case kMainMenuDifficulty: + if (_adventureMode) { + _adventureButton.show(); + _walkthroughButton.hide(); + } else { + _walkthroughButton.show(); + _adventureButton.hide(); + } + + _largeSelect.moveElementTo(kDifficultySelectLeft, kDifficultySelectTop); + _largeSelect.show(); + _smallSelect.hide(); + break; + case kMainMenuStart: + _smallSelect.moveElementTo(kStartSelectLeft, kStartSelectTop); + _smallSelect.show(); + _largeSelect.hide(); + break; + case kMainMenuCredits: + _smallSelect.moveElementTo(kCreditsSelectLeft, kCreditsSelectTop); + _smallSelect.show(); + _largeSelect.hide(); + break; + case kMainMenuQuit: + _smallSelect.moveElementTo(kMainMenuQuitSelectLeft, kMainMenuQuitSelectTop); + _smallSelect.show(); + _largeSelect.hide(); + break; + } + + vm->resetIntroTimer(); + } +} + +enum { + kCreditsMenuCoreTeam, + kCreditsMenuSupportTeam, + kCreditsMenuOriginalTeam, + kCreditsMenuTalent, + kCreditsMenuOtherTitles, + kCreditsMenuMainMenu, + + kCreditsFirstSelection = kCreditsMenuCoreTeam, + kCreditsLastSelection = kCreditsMenuMainMenu +}; + +static const CoordType kCreditsMovieLeft = 288; +static const CoordType kCreditsMovieTop = 0; + +static const CoordType kCoreTeamSelectLeft = 40; +static const CoordType kCoreTeamSelectTop = 223; + +static const CoordType kSupportTeamSelectLeft = 40; +static const CoordType kSupportTeamSelectTop = 259; + +static const CoordType kOriginalTeamSelectLeft = 40; +static const CoordType kOriginalTeamSelectTop = 295; + +static const CoordType kTalentSelectLeft = 40; +static const CoordType kTalentSelectTop = 331; + +static const CoordType kOtherTitlesSelectLeft = 40; +static const CoordType kOtherTitlesSelectTop = 367; + +static const CoordType kCreditsMainMenuLeft = 32; +static const CoordType kCreditsMainMenuTop = 412; + +static const CoordType kCreditsMainMenuSelectLeft = 30; +static const CoordType kCreditsMainMenuSelectTop = 408; + +static const TimeValue kCoreTeamTime = 0; +static const TimeValue kSupportTeamTime = 1920; +static const TimeValue kOriginalTeamTime = 3000; +static const TimeValue kTalentTime = 4440; +static const TimeValue kOtherTitlesTime = 4680; + +static const TimeValue kFrameIncrement = 120; // Three frames... + +// Never set the current input handler to the CreditsMenu. +CreditsMenu::CreditsMenu() : GameMenu(kCreditsMenuID), _menuBackground(0), _creditsMovie(0), + _mainMenuButton(0), _largeSelect(0), _smallSelect(0) { + + _menuBackground.initFromPICTFile("Images/Credits/CredScrn.pict"); + _menuBackground.setDisplayOrder(0); + _menuBackground.startDisplaying(); + _menuBackground.show(); + + _creditsMovie.initFromMovieFile("Images/Credits/Credits.movie"); + _creditsMovie.setDisplayOrder(1); + _creditsMovie.moveElementTo(kCreditsMovieLeft, kCreditsMovieTop); + _creditsMovie.startDisplaying(); + _creditsMovie.show(); + _creditsMovie.redrawMovieWorld(); + + _mainMenuButton.initFromPICTFile("Images/Credits/MainMenu.pict"); + _mainMenuButton.setDisplayOrder(1); + _mainMenuButton.moveElementTo(kCreditsMainMenuLeft, kCreditsMainMenuTop); + _mainMenuButton.startDisplaying(); + + _largeSelect.initFromPICTFile("Images/Credits/SelectL.pict", true); + _largeSelect.setDisplayOrder(2); + _largeSelect.moveElementTo(kCreditsMainMenuSelectLeft, kCreditsMainMenuSelectTop); + _largeSelect.startDisplaying(); + + _smallSelect.initFromPICTFile("Images/Credits/SelectS.pict", true); + _smallSelect.setDisplayOrder(2); + _smallSelect.show(); + _smallSelect.startDisplaying(); + + _menuSelection = -1; + + newMenuSelection(kCreditsMenuCoreTeam); +} + +// Assumes the new selection is never more than one away from the old... +void CreditsMenu::newMenuSelection(const int newSelection) { + if (newSelection != _menuSelection) { + switch (newSelection) { + case kCreditsMenuCoreTeam: + _smallSelect.moveElementTo(kCoreTeamSelectLeft, kCoreTeamSelectTop); + _creditsMovie.setTime(kCoreTeamTime); + _creditsMovie.redrawMovieWorld(); + break; + case kCreditsMenuSupportTeam: + _smallSelect.moveElementTo(kSupportTeamSelectLeft, kSupportTeamSelectTop); + _creditsMovie.setTime(kSupportTeamTime); + _creditsMovie.redrawMovieWorld(); + break; + case kCreditsMenuOriginalTeam: + _smallSelect.moveElementTo(kOriginalTeamSelectLeft, kOriginalTeamSelectTop); + _creditsMovie.setTime(kOriginalTeamTime); + _creditsMovie.redrawMovieWorld(); + break; + case kCreditsMenuTalent: + _smallSelect.moveElementTo(kTalentSelectLeft, kTalentSelectTop); + _creditsMovie.setTime(kTalentTime); + _creditsMovie.redrawMovieWorld(); + break; + case kCreditsMenuOtherTitles: + _smallSelect.moveElementTo(kOtherTitlesSelectLeft, kOtherTitlesSelectTop); + _smallSelect.show(); + _largeSelect.hide(); + _creditsMovie.setTime(kOtherTitlesTime); + _creditsMovie.redrawMovieWorld(); + break; + case kCreditsMenuMainMenu: + _smallSelect.hide(); + _largeSelect.show(); + break; + } + + _menuSelection = newSelection; + } +} + +void CreditsMenu::newMovieTime(const TimeValue newTime) { + if (newTime < kSupportTeamTime) { + _smallSelect.moveElementTo(kCoreTeamSelectLeft, kCoreTeamSelectTop); + _menuSelection = kCreditsMenuCoreTeam; + } else if (newTime < kOriginalTeamTime) { + _smallSelect.moveElementTo(kSupportTeamSelectLeft, kSupportTeamSelectTop); + _menuSelection = kCreditsMenuSupportTeam; + } else if (newTime < kTalentTime) { + _smallSelect.moveElementTo(kOriginalTeamSelectLeft, kOriginalTeamSelectTop); + _menuSelection = kCreditsMenuOriginalTeam; + } else if (newTime < kOtherTitlesTime) { + _smallSelect.moveElementTo(kTalentSelectLeft, kTalentSelectTop); + _smallSelect.show(); + _largeSelect.hide(); + _menuSelection = kCreditsMenuTalent; + } else if ((int)newTime == -120) { + // HACK: Avoid getting sent to the bottom button in the default case + return; + } else { + _smallSelect.moveElementTo(kOtherTitlesSelectLeft, kOtherTitlesSelectTop); + _smallSelect.show(); + _largeSelect.hide(); + _menuSelection = kCreditsMenuOtherTitles; + } + + _creditsMovie.setTime(newTime); + _creditsMovie.redrawMovieWorld(); +} + +void CreditsMenu::handleInput(const Input &input, const Hotspot *cursorSpot) { + if (input.upButtonDown()) { + if (_menuSelection > kCreditsFirstSelection) + newMenuSelection(_menuSelection - 1); + } else if (input.downButtonDown()) { + if (_menuSelection < kCreditsLastSelection) + newMenuSelection(_menuSelection + 1); + } else if (input.leftButtonDown()) { + newMovieTime(_creditsMovie.getTime() - kFrameIncrement); + } else if (input.rightButtonDown()) { + newMovieTime(_creditsMovie.getTime() + kFrameIncrement); + } else if (JMPPPInput::isMenuButtonPressInput(input)) { + if (_menuSelection == kCreditsMenuMainMenu) { + _mainMenuButton.show(); + ((PegasusEngine *)g_engine)->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _mainMenuButton.hide(); + setLastCommand(kMenuCmdCreditsMainMenu); + } + } + + InputHandler::handleInput(input, cursorSpot); +} + +static const CoordType kContinueLeft = 44; +static const CoordType kContinueTop = 336; + +static const CoordType kContinueSelectLeft = 40; +static const CoordType kContinueSelectTopDemo = 331; +static const CoordType kContinueSelectTop = 332; + +static const CoordType kMainMenuLeftDemo = 44; +static const CoordType kMainMenuTopDemo = 372; + +static const CoordType kMainMenuSelectLeftDemo = 40; +static const CoordType kMainMenuSelectTopDemo = 367; + +static const CoordType kQuitLeftDemo = 32; +static const CoordType kQuitTopDemo = 412; + +static const CoordType kQuitSelectLeftDemo = 28; +static const CoordType kQuitSelectTopDemo = 408; + +static const CoordType kRestoreLeftDeath = 44; +static const CoordType kRestoreTopDeath = 372; + +static const CoordType kRestoreSelectLeftDeath = 40; +static const CoordType kRestoreSelectTopDeath = 368; + +static const CoordType kMainMenuLeft = 32; +static const CoordType kMainMenuTop = 412; + +static const CoordType kMainMenuSelectLeft = 28; +static const CoordType kMainMenuSelectTop = 408; + +enum { + kDeathScreenContinueDemo = 0, + kDeathScreenMainMenuDemo, + kDeathScreenQuitDemo, + + kFirstDeathSelectionDemo = kDeathScreenContinueDemo, + kLastDeathSelectionDemo = kDeathScreenQuitDemo, + + kDeathScreenContinue = 0, + kDeathScreenRestore, + kDeathScreenMainMenu, + + kFirstDeathSelection = kDeathScreenContinue, + kLastDeathSelection = kDeathScreenMainMenu +}; + +// Never set the current input handler to the DeathMenu. +DeathMenu::DeathMenu(const DeathReason deathReason) : GameMenu(kDeathMenuID), _deathBackground(0), _continueButton(0), + _mainMenuButton(0), _quitButton(0), _restoreButton(0), _largeSelect(0), _smallSelect(0) { + PegasusEngine *vm = (PegasusEngine *)g_engine; + bool isDemo = vm->isDemo(); + + _playerWon = (deathReason == kPlayerWonGame); + + Common::String prefix = "Images/"; + Common::String imageName; + if (isDemo) { + prefix += "Demo/"; + imageName = prefix; + + switch (deathReason) { + case kDeathFallOffCliff: + imageName += "dPfall"; + break; + case kDeathEatenByDinosaur: + imageName += "dPdino"; + break; + case kDeathStranded: + imageName += "dPstuck"; + break; + default: + imageName += "dPdemowin"; + break; + } + + imageName += ".pict"; + } else { + prefix += "Death Screens/"; + imageName = prefix; + + static const char *fileNames[] = { + "dAunmade", "dAbombed", "dAshot", "dAassass", "dAnuked", + "dTunmade", "dTshot", "dPfall", "dPdino", "dPstuck", + "dNchoke", "dNcaught", "dNcaught", "dNsub", "dNrobot1", + "dNrobot2", "dMfall", "dMcaught", "dMtracks", "dMrobot", + "dMtoast", "dMexplo1", "dMexplo2", "dMchoke1", "dMchoke2", + "dMdroid", "dMfall", "dMgears", "dMshutt1", "dMshutt2", + "dWpoison", "dWcaught", "dWplasma", "dWshot", "dAfinale" + }; + + imageName += fileNames[deathReason - 1]; + imageName += ".pict"; + } + + _deathBackground.initFromPICTFile(imageName); + _deathReason = deathReason; + + if (!isDemo) { + vm->_gfx->setCurSurface(_deathBackground.getSurface()); + drawAllScores(); + vm->_gfx->setCurSurface(vm->_gfx->getWorkArea()); + } + + _deathBackground.setDisplayOrder(0); + _deathBackground.startDisplaying(); + _deathBackground.show(); + + if (isDemo) { + if (_playerWon) // Make credits button... + _continueButton.initFromPICTFile(prefix + "Credits.pict"); + else // Make continue button... + _continueButton.initFromPICTFile(prefix + "Continue.pict"); + + _mainMenuButton.initFromPICTFile(prefix + "MainMenu.pict"); + _mainMenuButton.setDisplayOrder(1); + _mainMenuButton.moveElementTo(kMainMenuLeftDemo, kMainMenuTopDemo); + _mainMenuButton.startDisplaying(); + + _quitButton.initFromPICTFile(prefix + "Quit.pict"); + _quitButton.setDisplayOrder(1); + _quitButton.moveElementTo(kQuitLeftDemo, kQuitTopDemo); + _quitButton.startDisplaying(); + + _menuSelection = kDeathScreenContinueDemo; + } else { + if (!_playerWon) { + _mainMenuButton.initFromPICTFile(prefix + "MainMenu.pict"); + _mainMenuButton.setDisplayOrder(1); + _mainMenuButton.moveElementTo(kMainMenuLeft, kMainMenuTop); + _mainMenuButton.startDisplaying(); + + _restoreButton.initFromPICTFile(prefix + "Restore.pict"); + _restoreButton.setDisplayOrder(1); + _restoreButton.moveElementTo(kRestoreLeftDeath, kRestoreTopDeath); + _restoreButton.startDisplaying(); + } + + _continueButton.initFromPICTFile(prefix + "Continue.pict"); + + _menuSelection = kDeathScreenContinue; + } + + _smallSelect.initFromPICTFile(prefix + "SelectS.pict", true); + _smallSelect.setDisplayOrder(2); + _smallSelect.startDisplaying(); + + _continueButton.setDisplayOrder(1); + _continueButton.moveElementTo(kContinueLeft, kContinueTop); + _continueButton.startDisplaying(); + + if (isDemo || !_playerWon) { + _largeSelect.initFromPICTFile(prefix + "SelectL.pict", true); + _largeSelect.setDisplayOrder(2); + _largeSelect.startDisplaying(); + } else { + _triumphSound.initFromQuickTime("Sounds/Caldoria/Galactic Triumph"); + _triumphSound.playSound(); + } + + updateDisplay(); +} + +void DeathMenu::handleInput(const Input &input, const Hotspot *cursorSpot) { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + if (input.upButtonDown()) { + if (_menuSelection > (vm->isDemo() ? kFirstDeathSelectionDemo : kFirstDeathSelection)) { + _menuSelection--; + updateDisplay(); + } + } else if (input.downButtonDown() && (vm->isDemo() || !_playerWon)) { + if (_menuSelection < (vm->isDemo() ? kLastDeathSelectionDemo : kLastDeathSelection)) { + _menuSelection++; + updateDisplay(); + } + } else if (JMPPPInput::isMenuButtonPressInput(input)) { + if (vm->isDemo()) { + switch (_menuSelection) { + case kDeathScreenContinueDemo: + _continueButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _continueButton.hide(); + setLastCommand(kMenuCmdDeathContinue); + break; + case kDeathScreenQuitDemo: + _quitButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _quitButton.hide(); + setLastCommand(kMenuCmdDeathQuitDemo); + break; + case kDeathScreenMainMenuDemo: + _mainMenuButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _mainMenuButton.hide(); + setLastCommand(kMenuCmdDeathMainMenuDemo); + break; + } + } else { + switch (_menuSelection) { + case kDeathScreenContinue: + _continueButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _continueButton.hide(); + setLastCommand(kMenuCmdDeathContinue); + break; + case kDeathScreenRestore: + _restoreButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _restoreButton.hide(); + setLastCommand(kMenuCmdDeathRestore); + break; + case kDeathScreenMainMenu: + _mainMenuButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _mainMenuButton.hide(); + setLastCommand(kMenuCmdDeathMainMenu); + break; + } + } + } + + InputHandler::handleInput(input, cursorSpot); +} + +void DeathMenu::updateDisplay() { + if (((PegasusEngine *)g_engine)->isDemo()) { + switch (_menuSelection) { + case kDeathScreenContinueDemo: + _smallSelect.moveElementTo(kContinueSelectLeft, kContinueSelectTopDemo); + _smallSelect.show(); + _largeSelect.hide(); + break; + case kDeathScreenQuitDemo: + _largeSelect.moveElementTo(kQuitSelectLeftDemo, kQuitSelectTopDemo); + _largeSelect.show(); + _smallSelect.hide(); + break; + case kDeathScreenMainMenuDemo: + _smallSelect.moveElementTo(kMainMenuSelectLeftDemo, kMainMenuSelectTopDemo); + _smallSelect.show(); + _largeSelect.hide(); + break; + } + } else { + switch (_menuSelection) { + case kDeathScreenContinue: + _smallSelect.moveElementTo(kContinueSelectLeft, kContinueSelectTop); + _smallSelect.show(); + _largeSelect.hide(); + break; + case kDeathScreenRestore: + _smallSelect.moveElementTo(kRestoreSelectLeftDeath, kRestoreSelectTopDeath); + _smallSelect.show(); + _largeSelect.hide(); + break; + case kDeathScreenMainMenu: + _largeSelect.moveElementTo(kMainMenuSelectLeft, kMainMenuSelectTop); + _largeSelect.show(); + _smallSelect.hide(); + break; + } + } +} + +void DeathMenu::drawAllScores() { + Surface numbers; + numbers.getImageFromPICTFile("Images/Death Screens/Numbers.pict"); + + Common::Rect scoreBounds; + GameScoreType caldoriaTotal = 0; + + switch (_deathReason) { + case kDeathCardBomb: + case kDeathShotBySinclair: + case kDeathSinclairShotDelegate: + case kDeathNuclearExplosion: + case kDeathGassedInNorad: + case kDeathWokeUpNorad: + case kDeathArrestedInNorad: + case kDeathSubDestroyed: + case kDeathRobotThroughNoradDoor: + case kDeathRobotSubControlRoom: + case kDeathWrongShuttleLock: + case kDeathArrestedInMars: + case kDeathRunOverByPod: + case kDeathDidntGetOutOfWay: + case kDeathReactorBurn: + case kDeathDidntFindMarsBomb: + case kDeathDidntDisarmMarsBomb: + case kDeathNoMaskInMaze: + case kDeathNoAirInMaze: + case kDeathGroundByMazebot: + case kDeathMissedOreBucket: + case kDeathDidntLeaveBucket: + case kDeathRanIntoCanyonWall: + case kDeathRanIntoSpaceJunk: + case kDeathDidntStopPoison: + case kDeathArrestedInWSC: + case kDeathHitByPlasma: + case kDeathShotOnCatwalk: + case kPlayerWonGame: + caldoriaTotal += kMaxCaldoriaTSAScoreAfter; + scoreBounds = Common::Rect(kDeathScreenScoreLeft, kDeathScreenScoreTop - kDeathScreenScoreSkipVert * 5, + kDeathScreenScoreLeft + kDeathScreenScoreWidth, kDeathScreenScoreTop - kDeathScreenScoreSkipVert * 5 + kDeathScreenScoreHeight); + drawScore(GameState.getGandhiScore(), kMaxGandhiScore, scoreBounds, &numbers); + + scoreBounds.translate(0, kDeathScreenScoreSkipVert); + drawScore(GameState.getWSCScore(), kMaxWSCScore, scoreBounds, &numbers); + + scoreBounds.translate(0, kDeathScreenScoreSkipVert); + drawScore(GameState.getNoradScore(), kMaxNoradScore, scoreBounds, &numbers); + + scoreBounds.translate(0, kDeathScreenScoreSkipVert); + drawScore(GameState.getMarsScore(), kMaxMarsScore, scoreBounds, &numbers); + // fall through + case kDeathFallOffCliff: + case kDeathEatenByDinosaur: + case kDeathStranded: + case kDeathShotByTSARobots: + scoreBounds = Common::Rect(kDeathScreenScoreLeft, kDeathScreenScoreTop - kDeathScreenScoreSkipVert, + kDeathScreenScoreLeft + kDeathScreenScoreWidth, kDeathScreenScoreTop - kDeathScreenScoreSkipVert + kDeathScreenScoreHeight); + drawScore(GameState.getPrehistoricScore(), kMaxPrehistoricScore, scoreBounds, &numbers); + // fall through + case kDeathUncreatedInCaldoria: + case kDeathUncreatedInTSA: + scoreBounds = Common::Rect(kDeathScreenScoreLeft, kDeathScreenScoreTop, kDeathScreenScoreLeft + kDeathScreenScoreWidth, + kDeathScreenScoreTop + kDeathScreenScoreHeight); + caldoriaTotal += kMaxCaldoriaTSAScoreBefore; + drawScore(GameState.getCaldoriaTSAScore(), caldoriaTotal, scoreBounds, &numbers); + + scoreBounds = Common::Rect(kDeathScreenScoreLeft, kDeathScreenScoreTop - kDeathScreenScoreSkipVert * 6, + kDeathScreenScoreLeft + kDeathScreenScoreWidth, kDeathScreenScoreTop - kDeathScreenScoreSkipVert * 6 + kDeathScreenScoreHeight); + + drawScore(GameState.getTotalScore(), kMaxTotalScore, scoreBounds, &numbers); + break; + } +} + +enum { + kPauseMenuSave, + kPauseMenuContinue, + kPauseMenuRestore, + kPauseMenuSoundFX, + kPauseMenuAmbience, + kPauseMenuWalkthru, + kPauseMenuQuitToMainMenu, + + kFirstPauseSelection = kPauseMenuSave, + kLastPauseSelection = kPauseMenuQuitToMainMenu +}; + +static const CoordType kPauseLeft = 194; +static const CoordType kPauseTop = 68; + +static const CoordType kSaveGameLeft = kPauseLeft + 6; +static const CoordType kSaveGameTop = kPauseTop + 56; + +static const CoordType kSaveGameSelectLeft = kPauseLeft - 44; +static const CoordType kSaveGameSelectTop = kPauseTop + 52; + +static const CoordType kPauseContinueLeft = kPauseLeft + 18; +static const CoordType kPauseContinueTop = kPauseTop + 100; + +static const CoordType kPauseContinueSelectLeft = kPauseLeft - 44; +static const CoordType kPauseContinueSelectTop = kPauseTop + 95; + +static const CoordType kPauseRestoreLeft = kPauseLeft + 18; +static const CoordType kPauseRestoreTop = kPauseTop + 136; + +static const CoordType kPauseRestoreSelectLeft = kPauseLeft - 44; +static const CoordType kPauseRestoreSelectTop = kPauseTop + 131; + +static const CoordType kSoundFXLeft = kPauseLeft + 128; +static const CoordType kSoundFXTop = kPauseTop + 187; +static const CoordType kSoundFXRight = kSoundFXLeft + 96; +static const CoordType kSoundFXBottom = kSoundFXTop + 14; + +static const CoordType kSoundFXSelectLeft = kPauseLeft - 44; +static const CoordType kSoundFXSelectTop = kPauseTop + 172; + +static const CoordType kAmbienceLeft = kPauseLeft + 128; +static const CoordType kAmbienceTop = kPauseTop + 227; +static const CoordType kAmbienceRight = kAmbienceLeft + 96; +static const CoordType kAmbienceBottom = kAmbienceTop + 14; + +static const CoordType kAmbienceSelectLeft = kPauseLeft - 44; +static const CoordType kAmbienceSelectTop = kPauseTop + 212; + +static const CoordType kWalkthruLeft = kPauseLeft + 128; +static const CoordType kWalkthruTop = kPauseTop + 264; + +static const CoordType kWalkthruSelectLeft = kPauseLeft - 44; +static const CoordType kWalkthruSelectTop = kPauseTop + 255; + +static const CoordType kQuitLeft = kPauseLeft + 18; +static const CoordType kQuitTop = kPauseTop + 302; + +static const CoordType kQuitSelectLeft = kPauseLeft - 44; +static const CoordType kQuitSelectTop = kPauseTop + 297; + +// These are relative to the pause background. +static const CoordType kPauseScoreLeft = 130; +static const CoordType kPauseScoreTop = 34; +static const CoordType kPauseScoreRight = kPauseScoreLeft + 108; +static const CoordType kPauseScoreBottom = kPauseScoreTop + 12; + +// Never set the current input handler to the CPauseMenu. +PauseMenu::PauseMenu() : GameMenu(kPauseMenuID), _pauseBackground(0), _saveButton(0), _restoreButton(0), + _walkthroughButton(0), _continueButton(0), _soundFXLevel(0), _ambienceLevel(0), _quitButton(0), + _largeSelect(0), _smallSelect(0) { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + _pauseBackground.initFromPICTFile("Images/Pause Screen/PausScrn.pict", true); + + if (!vm->isDemo()) { + Surface numbers; + numbers.getImageFromPICTFile("Images/Pause Screen/Numbers.pict"); + vm->_gfx->setCurSurface(_pauseBackground.getSurface()); + drawScore(GameState.getTotalScore(), kMaxTotalScore, + Common::Rect(kPauseScoreLeft, kPauseScoreTop, kPauseScoreRight, kPauseScoreBottom), &numbers); + vm->_gfx->setCurSurface(vm->_gfx->getWorkArea()); + } + + _pauseBackground.setDisplayOrder(kPauseMenuOrder); + _pauseBackground.moveElementTo(kPauseLeft, kPauseTop); + _pauseBackground.startDisplaying(); + _pauseBackground.show(); + + if (!vm->isDemo()) { + _saveButton.initFromPICTFile("Images/Pause Screen/SaveGame.pict"); + _saveButton.setDisplayOrder(kSaveGameOrder); + _saveButton.moveElementTo(kSaveGameLeft, kSaveGameTop); + _saveButton.startDisplaying(); + + _restoreButton.initFromPICTFile("Images/Pause Screen/Restore.pict"); + _restoreButton.setDisplayOrder(kRestoreOrder); + _restoreButton.moveElementTo(kPauseRestoreLeft, kPauseRestoreTop); + _restoreButton.startDisplaying(); + + _walkthroughButton.initFromPICTFile("Images/Pause Screen/Walkthru.pict"); + _walkthroughButton.setDisplayOrder(kWalkthruOrder); + _walkthroughButton.moveElementTo(kWalkthruLeft, kWalkthruTop); + _walkthroughButton.startDisplaying(); + + if (GameState.getWalkthroughMode()) + _walkthroughButton.show(); + } + + _continueButton.initFromPICTFile("Images/Pause Screen/Continue.pict"); + _continueButton.setDisplayOrder(kContinueOrder); + _continueButton.moveElementTo(kPauseContinueLeft, kPauseContinueTop); + _continueButton.startDisplaying(); + + _soundFXLevel.setDisplayOrder(kSoundFXOrder); + _soundFXLevel.setBounds(Common::Rect(kSoundFXLeft, kSoundFXTop, kSoundFXRight, kSoundFXBottom)); + _soundFXLevel.startDisplaying(); + _soundFXLevel.show(); + _soundFXLevel.setSoundLevel(vm->getSoundFXLevel()); + + _ambienceLevel.setDisplayOrder(kAmbienceOrder); + _ambienceLevel.setBounds(Common::Rect(kAmbienceLeft, kAmbienceTop, kAmbienceRight, kAmbienceBottom)); + _ambienceLevel.startDisplaying(); + _ambienceLevel.show(); + _ambienceLevel.setSoundLevel(vm->getAmbienceLevel()); + + _quitButton.initFromPICTFile("Images/Pause Screen/Quit2MM.pict"); + _quitButton.setDisplayOrder(kQuitToMainMenuOrder); + _quitButton.moveElementTo(kQuitLeft, kQuitTop); + _quitButton.startDisplaying(); + + _largeSelect.initFromPICTFile("Images/Pause Screen/SelectL.pict", true); + _largeSelect.setDisplayOrder(kPauseLargeHiliteOrder); + _largeSelect.startDisplaying(); + + _smallSelect.initFromPICTFile("Images/Pause Screen/SelectS.pict", true); + _smallSelect.setDisplayOrder(kPauseSmallHiliteOrder); + _smallSelect.startDisplaying(); + + _menuSelection = (vm->isDemo()) ? kPauseMenuContinue : kPauseMenuSave; + + updateDisplay(); +} + +void PauseMenu::handleInput(const Input &input, const Hotspot *cursorSpot) { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + if (input.upButtonDown()) { + if (vm->isDemo()) { + if (_menuSelection > kPauseMenuContinue) { + switch (_menuSelection) { + case kPauseMenuSoundFX: + _menuSelection = kPauseMenuContinue; + break; + case kPauseMenuAmbience: + _menuSelection = kPauseMenuSoundFX; + break; + case kPauseMenuQuitToMainMenu: + _menuSelection = kPauseMenuAmbience; + break; + } + updateDisplay(); + } + } else { + if (_menuSelection > kFirstPauseSelection) { + _menuSelection--; + updateDisplay(); + } + } + } else if (input.downButtonDown()) { + if (vm->isDemo()) { + if (_menuSelection < kPauseMenuQuitToMainMenu) { + switch (_menuSelection) { + case kPauseMenuContinue: + _menuSelection = kPauseMenuSoundFX; + break; + case kPauseMenuSoundFX: + _menuSelection = kPauseMenuAmbience; + break; + case kPauseMenuAmbience: + _menuSelection = kPauseMenuQuitToMainMenu; + break; + } + updateDisplay(); + } + } else { + if (_menuSelection < kLastPauseSelection) { + _menuSelection++; + updateDisplay(); + } + } + } else if (input.leftButtonDown()) { + if (_menuSelection == kPauseMenuSoundFX) { + _soundFXLevel.decrementLevel(); + vm->setSoundFXLevel(_soundFXLevel.getSoundLevel()); + } else if (_menuSelection == kPauseMenuAmbience) { + _ambienceLevel.decrementLevel(); + vm->setAmbienceLevel(_ambienceLevel.getSoundLevel()); + } else if (!vm->isDemo() && _menuSelection == kPauseMenuWalkthru) { + GameState.setWalkthroughMode(!GameState.getWalkthroughMode()); + if (GameState.getWalkthroughMode()) + _walkthroughButton.show(); + else + _walkthroughButton.hide(); + } + } else if (input.rightButtonDown()) { + if (_menuSelection == kPauseMenuSoundFX) { + _soundFXLevel.incrementLevel(); + vm->setSoundFXLevel(_soundFXLevel.getSoundLevel()); + } else if (_menuSelection == kPauseMenuAmbience) { + _ambienceLevel.incrementLevel(); + vm->setAmbienceLevel(_ambienceLevel.getSoundLevel()); + } else if (!vm->isDemo() && _menuSelection == kPauseMenuWalkthru) { + GameState.setWalkthroughMode(!GameState.getWalkthroughMode()); + if (GameState.getWalkthroughMode()) + _walkthroughButton.show(); + else + _walkthroughButton.hide(); + } + } else if (JMPPPInput::isMenuButtonPressInput(input)) { + switch (_menuSelection) { + case kPauseMenuSave: + _saveButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _saveButton.hide(); + setLastCommand(kMenuCmdPauseSave); + break; + case kPauseMenuRestore: + _restoreButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _restoreButton.hide(); + setLastCommand(kMenuCmdPauseRestore); + break; + case kPauseMenuContinue: + _continueButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _continueButton.hide(); + setLastCommand(kMenuCmdPauseContinue); + break; + case kPauseMenuWalkthru: + GameState.setWalkthroughMode(!GameState.getWalkthroughMode()); + if (GameState.getWalkthroughMode()) + _walkthroughButton.show(); + else + _walkthroughButton.hide(); + break; + case kPauseMenuQuitToMainMenu: + _quitButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _quitButton.hide(); + setLastCommand(kMenuCmdPauseQuit); + break; + } + } + + InputHandler::handleInput(input, cursorSpot); +} + +void PauseMenu::updateDisplay() { + switch (_menuSelection) { + case kPauseMenuSave: + _largeSelect.moveElementTo(kSaveGameSelectLeft, kSaveGameSelectTop); + _largeSelect.show(); + _smallSelect.hide(); + break; + case kPauseMenuContinue: + _smallSelect.moveElementTo(kPauseContinueSelectLeft, kPauseContinueSelectTop); + _smallSelect.show(); + _largeSelect.hide(); + break; + case kPauseMenuRestore: + _smallSelect.moveElementTo(kPauseRestoreSelectLeft, kPauseRestoreSelectTop); + _smallSelect.show(); + _largeSelect.hide(); + break; + case kPauseMenuSoundFX: + _largeSelect.moveElementTo(kSoundFXSelectLeft, kSoundFXSelectTop); + _largeSelect.show(); + _smallSelect.hide(); + break; + case kPauseMenuAmbience: + _largeSelect.moveElementTo(kAmbienceSelectLeft, kAmbienceSelectTop); + _largeSelect.show(); + _smallSelect.hide(); + break; + case kPauseMenuWalkthru: + _largeSelect.moveElementTo(kWalkthruSelectLeft, kWalkthruSelectTop); + _largeSelect.show(); + _smallSelect.hide(); + break; + case kPauseMenuQuitToMainMenu: + _smallSelect.moveElementTo(kQuitSelectLeft, kQuitSelectTop); + _smallSelect.show(); + _largeSelect.hide(); + break; + } + + ((PegasusEngine *)g_engine)->resetIntroTimer(); +} + + +} // End of namespace Pegasus diff --git a/engines/pegasus/menu.h b/engines/pegasus/menu.h new file mode 100644 index 0000000000..288b846093 --- /dev/null +++ b/engines/pegasus/menu.h @@ -0,0 +1,171 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_MENU_H +#define PEGASUS_MENU_H + +#include "pegasus/constants.h" +#include "pegasus/fader.h" +#include "pegasus/input.h" +#include "pegasus/movie.h" +#include "pegasus/sound.h" +#include "pegasus/surface.h" +#include "pegasus/util.h" + +namespace Pegasus { + +class GameMenu : public IDObject, public InputHandler { +public: + GameMenu(const uint32); + virtual ~GameMenu() {} + + virtual void becomeCurrentHandler(); + virtual void restorePreviousHandler(); + + GameMenuCommand getLastCommand() { return _lastCommand; } + void clearLastCommand() { _lastCommand = kMenuCmdNoCommand; } + +protected: + void setLastCommand(const GameMenuCommand command) { _lastCommand = command; } + + InputHandler *_previousHandler; + GameMenuCommand _lastCommand; + + void drawScore(GameScoreType, GameScoreType, const Common::Rect &, Surface *); + +private: + void drawNumber(GameScoreType, CoordType &, CoordType, Surface *); +}; + +class Hotspot; + +class MainMenu : public GameMenu { +public: + MainMenu(); + virtual ~MainMenu(); + + virtual void handleInput(const Input &input, const Hotspot *); + void startMainMenuLoop(); + void stopMainMenuLoop(); + +protected: + void updateDisplay(); + + uint32 _menuSelection; + + // Full and Demo + Picture _menuBackground; + Picture _startButton; + Picture _creditsButton; + Picture _quitButton; + Picture _largeSelect; + Picture _smallSelect; + + // Full only + bool _adventureMode; + Picture _overviewButton; + Picture _restoreButton; + Picture _adventureButton; + Picture _walkthroughButton; + + Sound _menuLoop; + SoundFader _menuFader; +}; + +class CreditsMenu : public GameMenu { +public: + CreditsMenu(); + virtual ~CreditsMenu() {} + + virtual void handleInput(const Input &input, const Hotspot *); + +protected: + void newMenuSelection(const int); + void newMovieTime(const TimeValue); + + int _menuSelection; + Picture _menuBackground; + Movie _creditsMovie; + Picture _mainMenuButton; + Picture _largeSelect; + Picture _smallSelect; +}; + +class DeathMenu : public GameMenu { +public: + DeathMenu(const DeathReason); + virtual ~DeathMenu() {} + + virtual void handleInput(const Input &input, const Hotspot *); + + bool playerWon() { return _playerWon; } + +protected: + void drawAllScores(); + + void updateDisplay(); + + bool _playerWon; + int _menuSelection; + DeathReason _deathReason; + + Picture _deathBackground; + Picture _continueButton; + Picture _restoreButton; + Picture _mainMenuButton; + Picture _quitButton; + + Picture _largeSelect; + Picture _smallSelect; + + Sound _triumphSound; +}; + +class PauseMenu : public GameMenu { +public: + PauseMenu(); + virtual ~PauseMenu() {} + + virtual void handleInput(const Input &input, const Hotspot *); + +protected: + void updateDisplay(); + + uint32 _menuSelection; + Picture _pauseBackground; + Picture _saveButton; + Picture _restoreButton; + Picture _walkthroughButton; + Picture _continueButton; + SoundLevel _soundFXLevel; + SoundLevel _ambienceLevel; + Picture _quitButton; + Picture _largeSelect; + Picture _smallSelect; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/module.mk b/engines/pegasus/module.mk new file mode 100644 index 0000000000..cb44a04171 --- /dev/null +++ b/engines/pegasus/module.mk @@ -0,0 +1,100 @@ +MODULE := engines/pegasus + +MODULE_OBJS = \ + compass.o \ + console.o \ + cursor.o \ + detection.o \ + elements.o \ + energymonitor.o \ + fader.o \ + gamestate.o \ + graphics.o \ + hotspot.o \ + input.o \ + interface.o \ + menu.o \ + movie.o \ + notification.o \ + pegasus.o \ + sound.o \ + surface.o \ + timers.o \ + transition.o \ + util.o \ + ai/ai_action.o \ + ai/ai_area.o \ + ai/ai_condition.o \ + ai/ai_rule.o \ + items/autodragger.o \ + items/inventory.o \ + items/inventorypicture.o \ + items/item.o \ + items/itemdragger.o \ + items/itemlist.o \ + items/biochips/aichip.o \ + items/biochips/biochipitem.o \ + items/biochips/mapchip.o \ + items/biochips/mapimage.o \ + items/biochips/opticalchip.o \ + items/biochips/pegasuschip.o \ + items/biochips/retscanchip.o \ + items/biochips/shieldchip.o \ + items/inventory/airmask.o \ + items/inventory/gascanister.o \ + items/inventory/inventoryitem.o \ + items/inventory/keycard.o \ + neighborhood/door.o \ + neighborhood/exit.o \ + neighborhood/extra.o \ + neighborhood/hotspotinfo.o \ + neighborhood/neighborhood.o \ + neighborhood/spot.o \ + neighborhood/turn.o \ + neighborhood/view.o \ + neighborhood/zoom.o \ + neighborhood/caldoria/caldoria.o \ + neighborhood/caldoria/caldoria4dsystem.o \ + neighborhood/caldoria/caldoriabomb.o \ + neighborhood/caldoria/caldoriamessages.o \ + neighborhood/caldoria/caldoriamirror.o \ + neighborhood/mars/energybeam.o \ + neighborhood/mars/gravitoncannon.o \ + neighborhood/mars/hermite.o \ + neighborhood/mars/mars.o \ + neighborhood/mars/planetmover.o \ + neighborhood/mars/reactor.o \ + neighborhood/mars/robotship.o \ + neighborhood/mars/shuttleenergymeter.o \ + neighborhood/mars/shuttlehud.o \ + neighborhood/mars/shuttleweapon.o \ + neighborhood/mars/spacechase3d.o \ + neighborhood/mars/spacejunk.o \ + neighborhood/mars/tractorbeam.o \ + neighborhood/norad/norad.o \ + neighborhood/norad/noradelevator.o \ + neighborhood/norad/pressuredoor.o \ + neighborhood/norad/pressuretracker.o \ + neighborhood/norad/subcontrolroom.o \ + neighborhood/norad/subplatform.o \ + neighborhood/norad/alpha/ecrmonitor.o \ + neighborhood/norad/alpha/fillingstation.o \ + neighborhood/norad/alpha/noradalpha.o \ + neighborhood/norad/alpha/panorama.o \ + neighborhood/norad/alpha/panoramascroll.o \ + neighborhood/norad/delta/globegame.o \ + neighborhood/norad/delta/noraddelta.o \ + neighborhood/prehistoric/prehistoric.o \ + neighborhood/tsa/fulltsa.o \ + neighborhood/tsa/tinytsa.o \ + neighborhood/wsc/moleculebin.o \ + neighborhood/wsc/wsc.o + + +# This module can be built as a plugin +ifeq ($(ENABLE_PEGASUS), DYNAMIC_PLUGIN) +PLUGIN := 1 +endif + +# Include common rules +include $(srcdir)/rules.mk diff --git a/engines/pegasus/movie.cpp b/engines/pegasus/movie.cpp new file mode 100644 index 0000000000..59814a753d --- /dev/null +++ b/engines/pegasus/movie.cpp @@ -0,0 +1,281 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "common/system.h" +#include "graphics/surface.h" +#include "video/qt_decoder.h" +#include "video/video_decoder.h" + +#include "pegasus/movie.h" + +namespace Pegasus { + +Movie::Movie(const DisplayElementID id) : Animation(id) { + _video = 0; + setScale(600); +} + +Movie::~Movie() { + releaseMovie(); +} + +// *** Make sure this will stop displaying the movie. + +void Movie::releaseMovie() { + if (_video) { + delete _video; + _video = 0; + disposeAllCallBacks(); + deallocateSurface(); + } + + setBounds(Common::Rect(0, 0, 0, 0)); +} + +void Movie::initFromMovieFile(const Common::String &fileName, bool transparent) { + _transparent = transparent; + + releaseMovie(); + _video = new Video::QuickTimeDecoder(); + if (!_video->loadFile(fileName)) { + // Replace any colon with an underscore, since only Mac OS X + // supports that. See PegasusEngine::detectOpeningClosingDirectory() + // for more info. + Common::String newName(fileName); + if (newName.contains(':')) + for (uint i = 0; i < newName.size(); i++) + if (newName[i] == ':') + newName.setChar('_', i); + + if (!_video->loadFile(newName)) + error("Could not load video '%s'", fileName.c_str()); + } + + Common::Rect bounds(0, 0, _video->getWidth(), _video->getHeight()); + sizeElement(_video->getWidth(), _video->getHeight()); + _movieBox = bounds; + + if (!isSurfaceValid()) + allocateSurface(bounds); + + setStart(0, getScale()); + TimeBase::setStop(_video->getDuration().convertToFramerate(getScale()).totalNumberOfFrames(), getScale()); +} + +void Movie::redrawMovieWorld() { + if (_video && _video->needsUpdate()) { + const Graphics::Surface *frame = _video->decodeNextFrame(); + + if (!frame) + return; + + // Make sure we have a surface in the current pixel format + Graphics::Surface *convertedFrame = 0; + + if (frame->format != g_system->getScreenFormat()) { + convertedFrame = frame->convertTo(g_system->getScreenFormat()); + frame = convertedFrame; + } + + // Copy to the surface using _movieBox + uint16 width = MIN<int>(frame->w, _movieBox.width()); + uint16 height = MIN<int>(frame->h, _movieBox.height()); + + for (uint16 y = 0; y < height; y++) + memcpy((byte *)_surface->getBasePtr(_movieBox.left, _movieBox.top + y), (const byte *)frame->getBasePtr(0, y), width * frame->format.bytesPerPixel); + + if (convertedFrame) { + convertedFrame->free(); + delete convertedFrame; + } + + triggerRedraw(); + } +} + +void Movie::draw(const Common::Rect &r) { + Common::Rect worldBounds = _movieBox; + Common::Rect elementBounds; + getBounds(elementBounds); + + worldBounds.moveTo(elementBounds.left, elementBounds.top); + Common::Rect r1 = r.findIntersectingRect(worldBounds); + + Common::Rect r2 = r1; + r2.translate(_movieBox.left - elementBounds.left, _movieBox.top - elementBounds.top); + drawImage(r2, r1); +} + +void Movie::moveMovieBoxTo(const CoordType h, const CoordType v) { + _movieBox.moveTo(h, v); +} + +void Movie::setStop(const TimeValue stopTime, const TimeScale scale) { + TimeBase::setStop(stopTime, scale); + + if (_video) + _video->setEndTime(Audio::Timestamp(0, _stopTime, _stopScale)); +} + +void Movie::setVolume(uint16 volume) { + if (_video) + _video->setVolume(MIN<uint>(volume, 0xFF)); +} + +void Movie::setTime(const TimeValue time, const TimeScale scale) { + if (_video) { + // Don't go past the ends of the movie + Common::Rational timeFrac = Common::Rational(time, ((scale == 0) ? getScale() : scale)); + + if (timeFrac < Common::Rational(_startTime, _startScale)) + timeFrac = Common::Rational(_startTime, _startScale); + else if (timeFrac >= Common::Rational(_stopTime, _stopScale)) + return; + + _video->seek(Audio::Timestamp(0, timeFrac.getNumerator(), timeFrac.getDenominator())); + _time = timeFrac; + _lastMillis = 0; + } +} + +void Movie::setRate(const Common::Rational rate) { + if (_video) { + _video->setRate(rate); + + TimeBase::setRate(_video->getRate()); + return; + } + + TimeBase::setRate(rate); +} + +void Movie::start() { + if (_video) + _video->start(); + + TimeBase::start(); +} + +void Movie::stop() { + if (_video) + _video->stop(); + + TimeBase::stop(); +} + +void Movie::resume() { + if (_paused) { + if (_video) + _video->pauseVideo(false); + + _paused = false; + } +} + +void Movie::pause() { + if (isRunning() && !_paused) { + if (_video) + _video->pauseVideo(true); + + _paused = true; + _pauseStart = g_system->getMillis(); + } +} + +TimeValue Movie::getDuration(const TimeScale scale) const { + // Unlike TimeBase::getDuration(), this returns the whole duration of the movie + // The original source has a TODO to make this behave like TimeBase::getDuration(), + // but the problem is that too much code requires this function to behave this way... + + if (_video) + return _video->getDuration().convertToFramerate(((scale == 0) ? getScale() : scale)).totalNumberOfFrames(); + + return 0; +} + +void Movie::updateTime() { + // The reason why we overrode TimeBase's updateTime(): + // Again, avoiding timers and handling it here + if (_video && _video->isPlaying() && !_video->isPaused()) { + redrawMovieWorld(); + + uint32 startTime = _startTime * getScale() / _startScale; + uint32 stopTime = _stopTime * getScale() / _stopScale; + uint32 actualTime = CLIP<int>(_video->getTime() * getScale() / 1000, startTime, stopTime); + + // HACK: Due to the inaccuracy of the time, we won't actually allow us to hit + // the stop time unless we've actually reached the end of the segment. + if (actualTime == stopTime && !_video->endOfVideo()) + actualTime--; + + _time = Common::Rational(actualTime, getScale()); + } +} + +GlowingMovie::GlowingMovie(const DisplayElementID id) : Movie(id) { + _glowing = false; +} + +void GlowingMovie::draw(const Common::Rect &r) { + // Make sure the rectangles are clipped properly, OR guarantee that _bounds will + // never fall off the screen. + if (_glowing) { + Common::Rect bounds; + getBounds(bounds); + + copyToCurrentPortTransparentGlow(_movieBox, bounds); + } else { + Movie::draw(r); + } +} + +void GlowingMovie::setBounds(const Common::Rect &r) { + Common::Rect bounds; + getBounds(bounds); + + if (r != bounds) { + // Avoid Movie::setBounds. + // clone2727 asks why, but goes along with it + Animation::setBounds(r); + } +} + +ScalingMovie::ScalingMovie(const DisplayElementID id) : GlowingMovie(id) { +} + +void ScalingMovie::draw(const Common::Rect &) { + // Make sure the rectangles are clipped properly, OR guarantee that _bounds will + // never fall off the screen. + + Common::Rect bounds; + getBounds(bounds); + + if (_glowing) + scaleTransparentCopyGlow(_movieBox, bounds); + else + scaleTransparentCopy(_movieBox, bounds); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/movie.h b/engines/pegasus/movie.h new file mode 100644 index 0000000000..9b9cc2bdbe --- /dev/null +++ b/engines/pegasus/movie.h @@ -0,0 +1,105 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_MOVIE_H +#define PEGASUS_MOVIE_H + +#include "common/str.h" + +#include "pegasus/elements.h" +#include "pegasus/surface.h" + +namespace Video { +class VideoDecoder; +} + +namespace Pegasus { + +class Movie : public Animation, public PixelImage { +public: + Movie(const DisplayElementID); + virtual ~Movie(); + + virtual void initFromMovieFile(const Common::String &fileName, bool transparent = false); + + bool isMovieValid() { return _video != 0; } + + virtual void releaseMovie(); + + virtual void draw(const Common::Rect &); + virtual void redrawMovieWorld(); + + virtual void setTime(const TimeValue, const TimeScale = 0); + + virtual void setRate(const Common::Rational); + + virtual void start(); + virtual void stop(); + virtual void resume(); + virtual void pause(); + + virtual void moveMovieBoxTo(const CoordType, const CoordType); + + virtual void setStop(const TimeValue, const TimeScale = 0); + + virtual TimeValue getDuration(const TimeScale = 0) const; + + // *** HACK ALERT + Video::VideoDecoder *getMovie() { return _video; } + void setVolume(uint16); + +protected: + void updateTime(); + + Video::VideoDecoder *_video; + Common::Rect _movieBox; +}; + +class GlowingMovie : public Movie { +public: + GlowingMovie(DisplayElementID); + virtual ~GlowingMovie() {} + + virtual void draw(const Common::Rect &); + + void setBounds(const Common::Rect &); + + void setGlowing(const bool glowing) { _glowing = glowing; } + +protected: + bool _glowing; +}; + +class ScalingMovie : public GlowingMovie { +public: + ScalingMovie(DisplayElementID); + virtual ~ScalingMovie() {} + + virtual void draw(const Common::Rect &); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/caldoria/caldoria.cpp b/engines/pegasus/neighborhood/caldoria/caldoria.cpp new file mode 100644 index 0000000000..140e6e8093 --- /dev/null +++ b/engines/pegasus/neighborhood/caldoria/caldoria.cpp @@ -0,0 +1,1962 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "common/system.h" +#include "video/qt_decoder.h" + +#include "pegasus/cursor.h" +#include "pegasus/energymonitor.h" +#include "pegasus/gamestate.h" +#include "pegasus/interface.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/biochips/biochipitem.h" +#include "pegasus/neighborhood/caldoria/caldoria.h" +#include "pegasus/neighborhood/caldoria/caldoria4dsystem.h" +#include "pegasus/neighborhood/caldoria/caldoriabomb.h" +#include "pegasus/neighborhood/caldoria/caldoriamessages.h" +#include "pegasus/neighborhood/caldoria/caldoriamirror.h" +#include "pegasus/neighborhood/tsa/fulltsa.h" + +namespace Pegasus { + +static const int16 kVidPhoneAngle = 30; +static const int16 kReplicatorAngle = 50; +static const int16 kDrawersAngle = -30; +static const int16 kCaldoria53Angle = 45; +static const int16 kCaldoria55Angle = -45; + +static const TimeValue kSinclairInterruptionTime1 = 2955; +static const TimeValue kSinclairInterruptionTime2 = 6835; +static const TimeValue kSinclairInterruptionTime3 = 9835; +static const TimeValue kSinclairInterruptionTime4 = 12555; + +static const InputBits kPullbackInterruptFilter = kFilterAllInput; +static const InputBits kRecalibrationInterruptFilter = kFilterAllInput; + +static const TimeValue kCaldoriaReplicatorIntroIn = 4933; +static const TimeValue kCaldoriaReplicatorIntroOut = 6557; + +static const TimeValue kCaldoriaReplicatorWrongChoiceIn = 6557; +static const TimeValue kCaldoriaReplicatorWrongChoiceOut = 8586; + +static const TimeValue kCaldoriaReplicatorOJChoiceIn = 8586; +static const TimeValue kCaldoriaReplicatorOJChoiceOut = 11687; + +static const TimeValue kCaldoriaMessagesIntroIn = 11687; +static const TimeValue kCaldoriaMessagesIntroOut = 13641; + +static const TimeValue kCaldoriaFirstMessageIn = 13641; +static const TimeValue kCaldoriaFirstMessageOut = 14203; + +static const TimeValue kCaldoriaSecondMessageIn = 14203; +static const TimeValue kCaldoriaSecondMessageOut = 14750; + +static const TimeValue kCaldoriaDoorCloseIn = 14750; +static const TimeValue kCaldoriaDoorCloseOut = 15472; + +static const TimeValue kCaldoriaElevatorCloseIn = 15472; +static const TimeValue kCaldoriaElevatorCloseOut = 16336; + +static const TimeValue kCaldoriaShowerCloseIn = 16336; +static const TimeValue kCaldoriaShowerCloseOut = 17101; + +static const TimeValue kCaldoriaGTDoorCloseIn = 17101; +static const TimeValue kCaldoriaGTDoorCloseOut = 18523; + +static const TimeValue kCaldoriaNobodyHomeIn = 18523; +static const TimeValue kCaldoriaNobodyHomeOut = 21469; + +static const TimeValue kCaldoriaNoOtherFloorIn = 21469; +static const TimeValue kCaldoriaNoOtherFloorOut = 28013; + +static const TimeValue kCaldoria4DInstructionsIn = 28013; +static const TimeValue kCaldoria4DInstructionsOut = 29730; + +static const TimeValue kCaldoriaDrinkOJIn = 33910; +static const TimeValue kCaldoriaDrinkOJOut = 35846; + +static const TimeValue kCaldoriaNoOtherDestinationIn = 35846; +static const TimeValue kCaldoriaNoOtherDestinationOut = 37877; + +static const TimeValue kCaldoriaUhghIn = 37877; +static const TimeValue kCaldoriaUhghOut = 38025; + +static const TimeValue kCaldoriaSinclairShootsOSIn = 38025; +static const TimeValue kCaldoriaSinclairShootsOSOut = 40649; + +static const TimeValue kCaldoriaScreamingAfterIn = 40649; +static const TimeValue kCaldoriaScreamingAfterOut = 47661; + +static const TimeValue k4FloorTime = 0; + +static const TimeValue k4To1Start = 40; +static const TimeValue k4To1Stop = 7720; + +static const TimeValue k4To5Start = 7720; +static const TimeValue k4To5Stop = 10280; + +static const TimeValue k4To2Time = 10280; + +static const TimeValue k4To3Time = 10320; + +static const TimeValue k1FloorTime = 10360; + +static const TimeValue k1To4Start = 10400; +static const TimeValue k1To4Stop = 18080; + +static const TimeValue k1To5Start = 18080; +static const TimeValue k1To5Stop = 28320; + +static const TimeValue k1To2Time = 28320; + +static const TimeValue k1To3Time = 28360; + +static const TimeValue k5FloorTime = 28400; + +static const TimeValue k5To1Start = 28440; +static const TimeValue k5To1Stop = 38680; + +static const TimeValue k5To4Start = 38680; +static const TimeValue k5To4Stop = 41240; + +static const TimeValue k5To2Time = 41240; + +static const TimeValue k5To3Time = 41280; + +// FuseFunction functions... + +const NotificationFlags kSinclairLoopDoneFlag = kLastNeighborhoodNotificationFlag << 1; + +SinclairCallBack::SinclairCallBack(Caldoria *caldoria) { + _caldoria = caldoria; +} + +void SinclairCallBack::callBack() { + _caldoria->checkInterruptSinclair(); +} + +Caldoria::Caldoria(InputHandler* nextHandler, PegasusEngine *owner) + : Neighborhood(nextHandler, owner, "Caldoria", kCaldoriaID), _sinclairInterrupt(this) { + setIsItemTaken(kKeyCard); + setIsItemTaken(kOrangeJuiceGlassEmpty); + GameState.setTakenItemID(kOrangeJuiceGlassFull, GameState.isTakenItemID(kOrangeJuiceGlassEmpty)); + _zoomOutSpot = 0; + _gunSprite = 0; +} + +Caldoria::~Caldoria() { + _sinclairInterrupt.releaseCallBack(); +} + +void Caldoria::init() { + Neighborhood::init(); + + // We need this notification flag as well. + _neighborhoodNotification.notifyMe(this, kSinclairLoopDoneFlag, kSinclairLoopDoneFlag); + + _sinclairInterrupt.initCallBack(&_navMovie, kCallBackAtTime); + + forceStridingStop(kCaldoria55, kSouth, kAltCaldoriaSinclairDown); + forceStridingStop(kCaldoria50, kNorth, kAltCaldoriaSinclairDown); +} + +void Caldoria::start() { + g_energyMonitor->stopEnergyDraining(); + + if (!GameState.getCaldoriaSeenPullback()) { + _vm->_gfx->doFadeOutSync(kOneSecond * kFifteenTicksPerSecond, kFifteenTicksPerSecond); + + g_system->delayMillis(2 * 1000); + + Video::VideoDecoder *pullbackMovie = new Video::QuickTimeDecoder(); + + if (!pullbackMovie->loadFile("Images/Caldoria/Pullback.movie")) + error("Could not load pullback movie"); + + // Draw the first frame so we can fade to it + const Graphics::Surface *frame = pullbackMovie->decodeNextFrame(); + assert(frame); + assert(frame->format == g_system->getScreenFormat()); + g_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, 64, 112, frame->w, frame->h); + _vm->_gfx->doFadeInSync(kTwoSeconds * kFifteenTicksPerSecond, kFifteenTicksPerSecond); + + bool saveAllowed = _vm->swapSaveAllowed(false); + bool openAllowed = _vm->swapLoadAllowed(false); + + bool skipped = false; + Input input; + + pullbackMovie->start(); + + while (!_vm->shouldQuit() && !pullbackMovie->endOfVideo()) { + if (pullbackMovie->needsUpdate()) { + frame = pullbackMovie->decodeNextFrame(); + + if (frame) { + g_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, 64, 112, frame->w, frame->h); + g_system->updateScreen(); + } + } + + InputDevice.getInput(input, kPullbackInterruptFilter); + if (input.anyInput() || _vm->saveRequested() || _vm->loadRequested()) { + skipped = true; + break; + } + + g_system->delayMillis(10); + } + + delete pullbackMovie; + + if (_vm->shouldQuit()) + return; + + _vm->swapSaveAllowed(saveAllowed); + _vm->swapLoadAllowed(openAllowed); + + ExtraTable::Entry entry; + + if (!skipped) { + _vm->_gfx->doFadeOutSync(kThreeSeconds * kFifteenTicksPerSecond, kFifteenTicksPerSecond, false); + g_system->delayMillis(3 * 1000 / 2); + getExtraEntry(kCaldoria00WakeUp1, entry); + _navMovie.setTime(entry.movieStart); + _navMovie.redrawMovieWorld(); + _navMovie.show(); + _vm->refreshDisplay(); + _vm->_gfx->doFadeInSync(kOneSecond * kFifteenTicksPerSecond, kFifteenTicksPerSecond, false); + } else { + getExtraEntry(kCaldoria00WakeUp1, entry); + _navMovie.setTime(entry.movieStart); + _navMovie.redrawMovieWorld(); + _navMovie.show(); + } + + GameState.setCaldoriaSeenPullback(true); + } + + Neighborhood::start(); +} + +void Caldoria::flushGameState() { + GameState.setCaldoriaFuseTimeLimit(_utilityFuse.getTimeRemaining()); +} + +class AIBombActiveCondition : public AICondition { +public: + AIBombActiveCondition() {} + + bool fireCondition(); +}; + +// Return true if player is on 53 east and Sinclair is shot. +bool AIBombActiveCondition::fireCondition() { + return GameState.getCurrentRoom() == kCaldoria53 && GameState.getCurrentDirection() == kEast && + GameState.getCaldoriaSinclairShot(); +} + +void Caldoria::setUpAIRules() { + Neighborhood::setUpAIRules(); + + if (g_AIArea) { + if (GameState.allTimeZonesFinished()) { + AIPlayMessageAction *messageAction = new AIPlayMessageAction("Images/AI/Caldoria/X49NB1", false); + AILocationCondition *locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kCaldoria49, kNorth)); + AIRule *rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Caldoria/X56EH1", false); + AIBombActiveCondition *activeCondition = new AIBombActiveCondition(); + rule = new AIRule(activeCondition, messageAction); + g_AIArea->addAIRule(rule); + } else { + AIPlayMessageAction *messageAction = new AIPlayMessageAction("Images/AI/Caldoria/XAB2", false); + AITimerCondition *timerCondition = new AITimerCondition(kLateWarning3TimeLimit, 1, true); + AILocationCondition *locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kCaldoria44, kEast)); + AINotCondition *notCondition = new AINotCondition(locCondition); + AIAndCondition *andCondition = new AIAndCondition(timerCondition, notCondition); + AIRule *rule = new AIRule(andCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Caldoria/XAB1", false); + timerCondition = new AITimerCondition(kLateWarning2TimeLimit, 1, true); + locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kCaldoria44, kEast)); + notCondition = new AINotCondition(locCondition); + andCondition = new AIAndCondition(timerCondition, notCondition); + rule = new AIRule(andCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Caldoria/XA44EB", false); + locCondition = new AILocationCondition(3); + locCondition->addLocation(MakeRoomView(kCaldoria01, kNorth)); + locCondition->addLocation(MakeRoomView(kCaldoria01, kEast)); + locCondition->addLocation(MakeRoomView(kCaldoria01, kSouth)); + rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Caldoria/X42WH1", false); + AICondition *condition = makeLocationAndDoesntHaveItemCondition(kCaldoria44, kEast, kKeyCard); + rule = new AIRule(condition, messageAction); + g_AIArea->addAIRule(rule); + + AIActivateRuleAction *ruleAction = new AIActivateRuleAction(rule); + locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kCaldoria42, kEast)); + rule = new AIRule(locCondition, ruleAction); + g_AIArea->addAIRule(rule); + } + } +} + +uint16 Caldoria::getDateResID() const { + return kDate2318ID; +} + +TimeValue Caldoria::getViewTime(const RoomID room, const DirectionConstant direction) { + ExtraTable::Entry extra; + uint32 extraID = 0xffffffff; + + switch (room) { + case kCaldoria00: + if (direction == kEast && _privateFlags.getFlag(kCaldoriaPrivate4DSystemOpenFlag)) + extraID = k4DEnvironOpenView; + break; + case kCaldoriaDrawers: + if (direction == kNorth && _privateFlags.getFlag(kCaldoriaPrivateRightDrawerOpenFlag)) { + if (GameState.isTakenItemID(kKeyCard)) + extraID = kRightDrawerOpenViewNoKeys; + else + extraID = kRightDrawerOpenViewWithKeys; + } + break; + case kCaldoria16: + if (direction == kSouth && GameState.getCaldoriaSeenSinclairInElevator()) + extraID = kCaldoria16SouthViewWithElevator; + break; + case kCaldoriaReplicator: + if (GameState.getCaldoriaMadeOJ() && !(GameState.isTakenItemID(kOrangeJuiceGlassEmpty) || GameState.isTakenItemID(kOrangeJuiceGlassFull))) + extraID = kReplicatorNorthViewWithOJ; + break; + case kCaldoriaKiosk: + case kCaldoriaBinoculars: + return 0xffffffff; + case kCaldoria48: + if (direction == kNorth && GameState.getCaldoriaRoofDoorOpen()) + extraID = kCa48NorthExplosion; + break; + } + + if (extraID == 0xffffffff) + return Neighborhood::getViewTime(room, direction); + + getExtraEntry(extraID, extra); + return extra.movieEnd - 1; +} + +void Caldoria::startSpotOnceOnly(TimeValue startTime, TimeValue stopTime) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kCaldoria13, kEast): + if (!_privateFlags.getFlag(kCaldoriaPrivateSeen13CarFlag) && _vm->getRandomBit() == 0) { + _privateFlags.setFlag(kCaldoriaPrivateSeen13CarFlag, true); + Neighborhood::startSpotOnceOnly(startTime, stopTime); + } + break; + case MakeRoomView(kCaldoria14, kEast): + if (!_privateFlags.getFlag(kCaldoriaPrivateSeen14CarFlag) && _vm->getRandomBit() == 0) { + _privateFlags.setFlag(kCaldoriaPrivateSeen14CarFlag, true); + Neighborhood::startSpotOnceOnly(startTime, stopTime); + } + break; + case MakeRoomView(kCaldoria18, kWest): + if (!_privateFlags.getFlag(kCaldoriaPrivateSeen18CarFlag) && _vm->getRandomBit() == 0) { + _privateFlags.setFlag(kCaldoriaPrivateSeen18CarFlag, true); + Neighborhood::startSpotOnceOnly(startTime, stopTime); + } + break; + case MakeRoomView(kCaldoria23, kSouth): + if (!_privateFlags.getFlag(kCaldoriaPrivateSeen23CarFlag) && _vm->getRandomBit() == 0) { + _privateFlags.setFlag(kCaldoriaPrivateSeen23CarFlag, true); + Neighborhood::startSpotOnceOnly(startTime, stopTime); + } + break; + case MakeRoomView(kCaldoria33, kSouth): + if (!_privateFlags.getFlag(kCaldoriaPrivateSeen33CarFlag) && _vm->getRandomBit() == 0) { + _privateFlags.setFlag(kCaldoriaPrivateSeen33CarFlag, true); + Neighborhood::startSpotOnceOnly(startTime, stopTime); + } + break; + case MakeRoomView(kCaldoria36, kNorth): + if (!_privateFlags.getFlag(kCaldoriaPrivateSeen36CarFlag) && _vm->getRandomBit() == 0) { + _privateFlags.setFlag(kCaldoriaPrivateSeen36CarFlag, true); + Neighborhood::startSpotOnceOnly(startTime, stopTime); + } + break; + case MakeRoomView(kCaldoria41, kNorth): + if (!_privateFlags.getFlag(kCaldoriaPrivateSeen41NorthCarFlag) && _vm->getRandomBit() == 0) { + _privateFlags.setFlag(kCaldoriaPrivateSeen41NorthCarFlag, true); + Neighborhood::startSpotOnceOnly(startTime, stopTime); + } + break; + case MakeRoomView(kCaldoria41, kEast): + if (!_privateFlags.getFlag(kCaldoriaPrivateSeen41EastCarFlag) && _vm->getRandomBit() == 0) { + _privateFlags.setFlag(kCaldoriaPrivateSeen41EastCarFlag, true); + Neighborhood::startSpotOnceOnly(startTime, stopTime); + } + break; + case MakeRoomView(kCaldoria41, kWest): + if (!_privateFlags.getFlag(kCaldoriaPrivateSeen41WestCarFlag) && _vm->getRandomBit() == 0) { + _privateFlags.setFlag(kCaldoriaPrivateSeen41WestCarFlag, true); + Neighborhood::startSpotOnceOnly(startTime, stopTime); + } + break; + default: + Neighborhood::startSpotOnceOnly(startTime, stopTime); + break; + } +} + +void Caldoria::findSpotEntry(const RoomID room, const DirectionConstant direction, SpotFlags flags, SpotTable::Entry &entry) { + Neighborhood::findSpotEntry(room, direction, flags, entry); + + switch (room) { + case kCaldoria00: + if (direction == kEast && (!GameState.getCaldoriaINNAnnouncing() || GameState.getCaldoriaSeenINN())) + entry.clear(); + break; + case kCaldoriaVidPhone: + if (direction == kNorth && GameState.getCaldoriaSeenMessages()) + entry.clear(); + break; + case kCaldoria44: + if (direction == kEast && GameState.getLastRoom() != kCaldoria42) + entry.clear(); + break; + } +} + +void Caldoria::startExitMovie(const ExitTable::Entry &exitEntry) { + switch (GameState.getCurrentRoom()) { + case kCaldoria05: + case kCaldoria07: + if (GameState.getCurrentDirection() == kWest) + closeCroppedMovie(); + // fall through + case kCaldoria11: + if (GameState.getCurrentDirection() == kEast) + closeCroppedMovie(); + break; + case kCaldoria13: + case kCaldoria14: + if (GameState.getCurrentDirection() == kNorth) + closeCroppedMovie(); + break; + } + + Neighborhood::startExitMovie(exitEntry); +} + +void Caldoria::startZoomMovie(const ZoomTable::Entry &zoomEntry) { + switch (GameState.getCurrentRoom()) { + case kCaldoria12: + if (GameState.getCurrentDirection() == kNorth) + closeCroppedMovie(); + break; + } + + Neighborhood::startZoomMovie(zoomEntry); +} + +void Caldoria::startDoorOpenMovie(const TimeValue startTime, const TimeValue stopTime) { + if (GameState.getCurrentRoom() == kCaldoria27 || GameState.getCurrentRoom() == kCaldoria28 || GameState.getCurrentRoom() == kCaldoria45) + // Must be opening elevator door. + closeCroppedMovie(); + + if (GameState.getCurrentRoom() == kCaldoria44 && GameState.getLastRoom() != kCaldoria42) + startExtraSequence(kArriveAtCaldoriaFromTSA, kDoorOpenCompletedFlag, false); + else + Neighborhood::startDoorOpenMovie(startTime, stopTime); +} + +void Caldoria::startTurnPush(const TurnDirection turnDirection, const TimeValue newViewTime, const DirectionConstant destDirection) { + switch (GameState.getCurrentRoom()) { + case kCaldoria05: + case kCaldoria07: + if (GameState.getCurrentDirection() == kWest) + closeCroppedMovie(); + break; + case kCaldoria11: + if (GameState.getCurrentDirection() == kEast) + closeCroppedMovie(); + break; + case kCaldoria12: + case kCaldoria13: + case kCaldoria14: + case kCaldoria27: + case kCaldoria28: + case kCaldoria45: + if (GameState.getCurrentDirection() == kNorth) + closeCroppedMovie(); + break; + case kCaldoria48: + if (_croppedMovie.isSurfaceValid()) + closeCroppedMovie(); + break; + } + + Neighborhood::startTurnPush(turnDirection, newViewTime, destDirection); +} + +void Caldoria::bumpIntoWall() { + requestSpotSound(kCaldoriaUhghIn, kCaldoriaUhghOut, kFilterNoInput, 0); + Neighborhood::bumpIntoWall(); +} + +void Caldoria::closeDoorOffScreen(const RoomID room, const DirectionConstant direction) { + switch (room) { + case kCaldoria08: + if (direction == kNorth) + playSpotSoundSync(kCaldoriaShowerCloseIn, kCaldoriaShowerCloseOut); + else + playSpotSoundSync(kCaldoriaDoorCloseIn, kCaldoriaDoorCloseOut); + break; + case kCaldoria09: + playSpotSoundSync(kCaldoriaShowerCloseIn, kCaldoriaShowerCloseOut); + break; + case kCaldoria16: + case kCaldoria38: + case kCaldoria46: + case kCaldoria27: + case kCaldoria28: + case kCaldoria45: + playSpotSoundSync(kCaldoriaElevatorCloseIn, kCaldoriaElevatorCloseOut); + break; + case kCaldoria44: + case kCaldoria42: + if (GameState.getCurrentRoom() == kCaldoria42) + playSpotSoundSync(kCaldoriaGTDoorCloseIn, kCaldoriaGTDoorCloseOut); + break; + default: + playSpotSoundSync(kCaldoriaDoorCloseIn, kCaldoriaDoorCloseOut); + break; + } +} + +int16 Caldoria::getStaticCompassAngle(const RoomID room, const DirectionConstant dir) { + int16 result = Neighborhood::getStaticCompassAngle(room, dir); + + switch (room) { + case kCaldoriaVidPhone: + result += kVidPhoneAngle; + break; + case kCaldoriaReplicator: + result += kReplicatorAngle; + break; + case kCaldoriaDrawers: + result += kDrawersAngle; + break; + case kCaldoria53: + result += kCaldoria53Angle; + break; + case kCaldoria55: + result += kCaldoria55Angle; + break; + } + + return result; +} + +void Caldoria::getExitCompassMove(const ExitTable::Entry &exitEntry, FaderMoveSpec &compassMove) { + Neighborhood::getExitCompassMove(exitEntry, compassMove); + + switch (MakeRoomView(exitEntry.room, exitEntry.direction)) { + case MakeRoomView(kCaldoria08, kNorth): + case MakeRoomView(kCaldoria09, kSouth): + compassMove.insertFaderKnot((exitEntry.movieStart + exitEntry.movieEnd) >> 1, compassMove.getNthKnotValue(0) + 30); + break; + case MakeRoomView(kCaldoria10, kEast): + compassMove.insertFaderKnot(exitEntry.movieStart + 4 * kCaldoriaFrameDuration, 90); + compassMove.insertFaderKnot(exitEntry.movieStart + 19 * kCaldoriaFrameDuration, -90); + break; + case MakeRoomView(kCaldoria42, kWest): + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), exitEntry.movieStart, -90, exitEntry.movieEnd, 90); + compassMove.insertFaderKnot(exitEntry.movieStart + 3 * kCaldoriaFrameDuration, -90); + compassMove.insertFaderKnot(exitEntry.movieStart + 33 * kCaldoriaFrameDuration, 90); + break; + case MakeRoomView(kCaldoria54, kEast): + if (getCurrentAlternate() != kAltCaldoriaSinclairDown) { + compassMove.insertFaderKnot(exitEntry.movieStart + 16 * kCaldoriaFrameDuration, 135); + compassMove.insertFaderKnot(exitEntry.movieEnd, 135); + } + break; + case MakeRoomView(kCaldoria55, kNorth): + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), exitEntry.movieStart, 315, exitEntry.movieEnd, 270); + break; + } +} + +void Caldoria::getZoomCompassMove(const ZoomTable::Entry &zoomEntry, FaderMoveSpec &compassMove) { + Neighborhood::getZoomCompassMove(zoomEntry, compassMove); + + switch (zoomEntry.hotspot) { + case kCaBathroomToiletSpotID: + compassMove.insertFaderKnot(zoomEntry.movieStart + 4 * kCaldoriaFrameDuration, 90); + compassMove.insertFaderKnot(zoomEntry.movieStart + 19 * kCaldoriaFrameDuration, -90); + compassMove.insertFaderKnot(zoomEntry.movieEnd, -90); + break; + } +} + +void Caldoria::getExtraCompassMove(const ExtraTable::Entry &entry, FaderMoveSpec &compassMove) { + switch (entry.extra) { + case kCaldoria00WakeUp1: + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, 90, entry.movieEnd, 180); + compassMove.insertFaderKnot(entry.movieStart + 1000, 90); + compassMove.insertFaderKnot(entry.movieStart + 1640, 120); + compassMove.insertFaderKnot(entry.movieStart + 2240, 135); + compassMove.insertFaderKnot(entry.movieStart + 2640, 180); + break; + case kCaldoria00WakeUp2: + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, 180, entry.movieEnd, 90); + compassMove.insertFaderKnot(entry.movieStart + 560, 90); + break; + case kCaldoria56BombStage1: + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, 90, entry.movieEnd, 10); + compassMove.insertFaderKnot(entry.movieStart + 31 * kCaldoriaFrameDuration, 60); + compassMove.insertFaderKnot(entry.movieStart + 49 * kCaldoriaFrameDuration, 60); + compassMove.insertFaderKnot(entry.movieStart + 66 * kCaldoriaFrameDuration, 10); + break; + case kCaldoria56BombStage7: + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, 10, entry.movieEnd, 90); + compassMove.insertFaderKnot(entry.movieStart + 131 * kCaldoriaFrameDuration, 10); + compassMove.insertFaderKnot(entry.movieStart + 148 * kCaldoriaFrameDuration, 60); + compassMove.insertFaderKnot(entry.movieStart + 165 * kCaldoriaFrameDuration, 60); + compassMove.insertFaderKnot(entry.movieEnd - 5 * kCaldoriaFrameDuration, 90); + break; + default: + Neighborhood::getExtraCompassMove(entry, compassMove); + break; + } +} + +void Caldoria::loadAmbientLoops() { + RoomID room = GameState.getCurrentRoom(); + + if (room == kCaldoria00 && GameState.getCaldoriaWokenUp()) + loadLoopSound1("Sounds/Caldoria/Apartment Music.AIFF", 0x100 / 4); + else if (room >= kCaldoria01 && room <= kCaldoria14) + loadLoopSound1("Sounds/Caldoria/Apartment Music.AIFF", 0x100 / 4); + else if (room == kCaldoria27 || room == kCaldoria28 || room == kCaldoria45) + loadLoopSound1("Sounds/Caldoria/Elevator Loop.AIFF", 0x100 / 5); + else if (room == kCaldoria44) + loadLoopSound1("Sounds/Caldoria/TSA Hum Loop.AIFF"); + else if (room >= kCaldoria15 && room <= kCaldoria48) + loadLoopSound1("Sounds/Caldoria/Industrial Nuage.aiff", 2 * 0x100 / 3); + else if (room >= kCaldoria49 && room <= kCaldoria56) + loadLoopSound1("Sounds/Caldoria/A50NLB00.22K.AIFF", 0x100 / 4); +} + +void Caldoria::checkContinuePoint(const RoomID room, const DirectionConstant direction) { + switch (MakeRoomView(room, direction)) { + case MakeRoomView(kCaldoria06, kSouth): + case MakeRoomView(kCaldoria13, kNorth): + case MakeRoomView(kCaldoria16, kSouth): + case MakeRoomView(kCaldoria38, kEast): + case MakeRoomView(kCaldoria38, kWest): + case MakeRoomView(kCaldoria40, kNorth): + case MakeRoomView(kCaldoria44, kEast): + case MakeRoomView(kCaldoria48, kNorth): + case MakeRoomView(kCaldoria49, kNorth): + makeContinuePoint(); + break; + } +} + +void Caldoria::spotCompleted() { + Neighborhood::spotCompleted(); + if (GameState.getCurrentRoom() == kCaldoriaBinoculars) + startExtraSequence(kBinocularsZoomInOnShip, kExtraCompletedFlag, kFilterNoInput); +} + +void Caldoria::arriveAt(const RoomID room, const DirectionConstant direction) { + switch (room) { + case kCaldoria56: + if (!GameState.getCaldoriaGunAimed()) + // Fall through... + case kCaldoria49: + case kCaldoria50: + case kCaldoria51: + case kCaldoria52: + case kCaldoria53: + case kCaldoria54: + case kCaldoria55: + if (GameState.getCaldoriaSinclairShot()) + setCurrentAlternate(kAltCaldoriaSinclairDown); + break; + } + + Neighborhood::arriveAt(room, direction); + Input dummy; + + switch (room) { + case kCaldoria00: + arriveAtCaldoria00(); + break; + case kCaldoria05: + if (direction == kWest && GameState.getCaldoriaINNAnnouncing()) + loopCroppedMovie("Images/Caldoria/A05 Light Loop", kCaldoriaA05LightLoopLeft, kCaldoriaA05LightLoopTop); + break; + case kCaldoria07: + if (direction == kWest && GameState.getCaldoriaINNAnnouncing()) + loopCroppedMovie("Images/Caldoria/A07 Light Loop", kCaldoriaA07LightLoopLeft, kCaldoriaA07LightLoopTop); + break; + case kCaldoria09: + _lastExtra = 0xffffffff; + break; + case kCaldoriaToilet: + GameState.setScoringReadPaper(true); + break; + case kCaldoriaReplicator: + setCurrentActivation(kActivateReplicatorReady); + requestSpotSound(kCaldoriaReplicatorIntroIn, kCaldoriaReplicatorIntroOut, kFilterNoInput, 0); + break; + case kCaldoria11: + setCurrentAlternate(kAltCaldoriaNormal); + if (direction == kEast && !GameState.getCaldoriaSeenMessages()) + loopCroppedMovie("Images/Caldoria/A11 Message Machine Loop", kCaldoria11MessageLoopLeft, kCaldoria11MessageLoopTop); + break; + case kCaldoria12: + if (direction == kNorth && !GameState.getCaldoriaSeenMessages()) + loopCroppedMovie("Images/Caldoria/A12 Message Machine Loop", kCaldoria12MessageLoopLeft, kCaldoria12MessageLoopTop); + break; + case kCaldoriaDrawers: + setCurrentActivation(kActivateDrawersClosed); + break; + case kCaldoria13: + GameState.setCaldoriaINNAnnouncing(true); + if (direction == kNorth && !GameState.getCaldoriaSeenMessages()) + loopCroppedMovie("Images/Caldoria/A13 Message Machine Loop", kCaldoria13MessageLoopLeft, kCaldoria13MessageLoopTop); + break; + case kCaldoria14: + if (direction == kNorth && !GameState.getCaldoriaSeenMessages()) + loopCroppedMovie("Images/Caldoria/A14 Message Machine Loop", kCaldoria14MessageLoopLeft, kCaldoria14MessageLoopTop); + break; + case kCaldoria08: + if (direction == kWest) + setCurrentActivation(kActivateMirrorReady); + // Fall through... + case kCaldoria15: + GameState.setCaldoriaINNAnnouncing(true); + break; + case kCaldoria27: + case kCaldoria28: + case kCaldoria45: + if (GameState.getCurrentDirection() == kNorth) + openDoor(); + break; + case kCaldoriaBinoculars: + GameState.setScoringLookThroughTelescope(true); + break; + case kCaldoriaKiosk: + GameState.setScoringSawCaldoriaKiosk(true); + startExtraSequenceSync(kCaldoriaKioskVideo, kFilterAllInput); + downButton(dummy); + break; + case kCaldoria44: + arriveAtCaldoria44(); + break; + case kCaldoria49: + arriveAtCaldoria49(); + break; + case kCaldoria53: + if (direction == kEast && !GameState.getCaldoriaSinclairShot()) + zoomToSinclair(); + break; + case kCaldoria50: + if (direction == kNorth && !GameState.getCaldoriaSinclairShot()) + setUpSinclairLoops(); + break; + case kCaldoria54: + if (direction == kSouth && !GameState.getCaldoriaSinclairShot()) + setUpSinclairLoops(); + break; + case kCaldoria56: + arriveAtCaldoria56(); + break; + case kCaldoriaDeathRoom: + arriveAtCaldoriaDeath(); + break; + } + + checkSinclairShootsOS(); + setUpRoofTop(); +} + +void Caldoria::doAIRecalibration() { + GameState.setCaldoriaDidRecalibration(true); + + if (!g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Caldoria/XA01EB1", true, kRecalibrationInterruptFilter)) + return; + + g_interface->calibrateEnergyBar(); + if (!g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Caldoria/XA01EB4", true, kRecalibrationInterruptFilter)) + return; + + g_interface->raiseInventoryDrawerSync(); + if (!g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Caldoria/XA01EB6", true, kRecalibrationInterruptFilter)) { + g_interface->lowerInventoryDrawerSync(); + return; + } + + g_interface->lowerInventoryDrawerSync(); + g_interface->raiseBiochipDrawerSync(); + + if (!g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Caldoria/XA01EB5", true, kRecalibrationInterruptFilter)) { + g_interface->lowerBiochipDrawerSync(); + return; + } + + g_interface->lowerBiochipDrawerSync(); + + g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Caldoria/XA01EB8", false, kRecalibrationInterruptFilter); +} + +void Caldoria::arriveAtCaldoria00() { + if (GameState.getCurrentDirection() == kEast) { + if (GameState.getCaldoriaWokenUp()) { + if (!GameState.getCaldoriaDidRecalibration()) + doAIRecalibration(); + setCurrentActivation(kActivate4DClosed); + } else { + // Good morning, sleeping beauty + ExtraTable::Entry extra; + getExtraEntry(kCaldoria00WakeUp1, extra); + + if (_navMovie.getTime() != extra.movieStart) { + _navMovie.setTime(extra.movieStart); + _navMovie.redrawMovieWorld(); + } + + startExtraSequenceSync(kCaldoria00WakeUp1, kFilterNoInput); + GameState.setCaldoriaWokenUp(true); + playCroppedMovieOnce("Images/Caldoria/VidPhone.movie", kCaldoriaVidPhoneLeft, kCaldoriaVidPhoneTop, kFilterAllInput); + startExtraSequence(kCaldoria00WakeUp2, kExtraCompletedFlag, kFilterNoInput); + } + } +} + +bool Caldoria::wantsCursor() { + return GameState.getCaldoriaDidRecalibration(); +} + +void Caldoria::arriveAtCaldoria44() { + if (GameState.getLastNeighborhood() != kCaldoriaID) { + openDoor(); + } else { + setCurrentActivation(kActivateReadyForCard); + loopExtraSequence(kCaldoriaTransporterArrowLoop, 0); + } +} + +void Caldoria::arriveAtCaldoria49() { + if (GameState.getLastRoom() == kCaldoria48) + setCurrentAlternate(kAltCaldoriaNormal); + + // Need to force the loop to play. + if (GameState.getCurrentDirection() == kNorth) { + GameState.setCaldoriaFuseTimeLimit(kSinclairShootsTimeLimit); + startExtraSequence(kCa49NorthVoiceAnalysis, kExtraCompletedFlag, kFilterNoInput); + } +} + +void Caldoria::arriveAtCaldoria56() { + if (!GameState.getCaldoriaBombDisarmed()) { + _privateFlags.setFlag(kCaldoriaPrivateZoomingToBombFlag, true); + + if (GameState.getCurrentDirection() == kNorth) { + turnRight(); + } else if (GameState.getCurrentDirection() == kSouth) { + turnLeft(); + } else if (GameState.getCurrentDirection() == kEast) { + _privateFlags.setFlag(kCaldoriaPrivateZoomingToBombFlag, false); + newInteraction(kCaldoriaBombInteractionID); + } + } +} + +void Caldoria::arriveAtCaldoriaDeath() { + if (GameState.getLastRoom() == kCaldoria49) { + if (GameState.getCaldoriaSinclairShot()) { + die(kDeathNuclearExplosion); + } else { + playSpotSoundSync(kCaldoriaSinclairShootsOSIn, kCaldoriaSinclairShootsOSOut); + playSpotSoundSync(kCaldoriaScreamingAfterIn, kCaldoriaScreamingAfterOut); + die(kDeathSinclairShotDelegate); + } + } else { + die(kDeathShotBySinclair); + } +} + +void Caldoria::setUpRoofTop() { + switch (GameState.getCurrentRoom()) { + case kCaldoria48: + if (GameState.getCurrentDirection() == kNorth) { + if (GameState.getCaldoriaRoofDoorOpen()) { + setCurrentAlternate(kAltCaldoriaRoofDoorBlown); + } else if (GameState.getCaldoriaDoorBombed()) { + // Long enough for AI hints...? + _utilityFuse.primeFuse(kCardBombCountDownTime); + _utilityFuse.setFunctor(new Common::Functor0Mem<void, Caldoria>(this, &Caldoria::doorBombTimerExpired)); + _utilityFuse.lightFuse(); + + loopCroppedMovie("Images/Caldoria/A48 Bomb Loop", kCaldoria48CardBombLoopLeft, kCaldoria48CardBombLoopTop); + } else { + setCurrentActivation(kActivateRoofSlotEmpty); + } + } + break; + case kCaldoria56: + if (GameState.getCurrentDirection() == kEast && GameState.getCaldoriaGunAimed()) + startExtraSequence(kCa53EastShootSinclair, kExtraCompletedFlag, false); + else + // Fall through... + case kCaldoria49: + case kCaldoria50: + case kCaldoria51: + case kCaldoria52: + case kCaldoria53: + case kCaldoria54: + case kCaldoria55: + if (!GameState.getCaldoriaSinclairShot()) { + if (GameState.getCaldoriaSawVoiceAnalysis() && !_utilityFuse.isFuseLit()) { + _utilityFuse.primeFuse(GameState.getCaldoriaFuseTimeLimit()); + _utilityFuse.setFunctor(new Common::Functor0Mem<void, Caldoria>(this, &Caldoria::sinclairTimerExpired)); + _utilityFuse.lightFuse(); + } + } else { + setCurrentAlternate(kAltCaldoriaSinclairDown); + } + break; + } +} + +void Caldoria::downButton(const Input &input) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kCaldoria01, kEast): + GameState.setCaldoriaWokenUp(true); + startExtraSequence(kCaldoria00SitDown, kExtraCompletedFlag, kFilterNoInput); + break; + default: + Neighborhood::downButton(input); + break; + } +} + +void Caldoria::turnTo(const DirectionConstant direction) { + Neighborhood::turnTo(direction); + + switch (GameState.getCurrentRoom()) { + case kCaldoria00: + if (direction == kEast) + setCurrentActivation(kActivate4DClosed); + break; + case kCaldoria01: + if (direction == kEast) { + GameState.setCaldoriaWokenUp(true); + startExtraSequence(kCaldoria00SitDown, kExtraCompletedFlag, kFilterNoInput); + } + break; + case kCaldoria05: + if (direction == kWest && GameState.getCaldoriaINNAnnouncing()) + loopCroppedMovie("Images/Caldoria/A05 Light Loop", kCaldoriaA05LightLoopLeft, kCaldoriaA05LightLoopTop); + break; + case kCaldoria07: + if (direction == kWest && GameState.getCaldoriaINNAnnouncing()) + loopCroppedMovie("Images/Caldoria/A07 Light Loop", kCaldoriaA07LightLoopLeft, kCaldoriaA07LightLoopTop); + break; + case kCaldoria08: + if (direction == kWest) + setCurrentActivation(kActivateMirrorReady); + break; + case kCaldoria09: + _lastExtra = 0xffffffff; + break; + case kCaldoria11: + if (direction == kEast && !GameState.getCaldoriaSeenMessages()) + loopCroppedMovie("Images/Caldoria/A11 Message Machine Loop", kCaldoria11MessageLoopLeft, kCaldoria11MessageLoopTop); + break; + case kCaldoria12: + if (direction == kNorth && !GameState.getCaldoriaSeenMessages()) + loopCroppedMovie("Images/Caldoria/A12 Message Machine Loop", kCaldoria12MessageLoopLeft, kCaldoria12MessageLoopTop); + break; + case kCaldoria13: + if (direction == kNorth && !GameState.getCaldoriaSeenMessages()) + loopCroppedMovie("Images/Caldoria/A13 Message Machine Loop", kCaldoria13MessageLoopLeft, kCaldoria13MessageLoopTop); + break; + case kCaldoria14: + if (direction == kNorth && !GameState.getCaldoriaSeenMessages()) + loopCroppedMovie("Images/Caldoria/A14 Message Machine Loop", kCaldoria14MessageLoopLeft, kCaldoria14MessageLoopTop); + break; + case kCaldoria27: + case kCaldoria28: + case kCaldoria45: + if (direction == kNorth) + openElevatorMovie(); + else + closeCroppedMovie(); + break; + case kCaldoria48: + if (direction == kNorth && !GameState.getCaldoriaDoorBombed()) + setCurrentActivation(kActivateRoofSlotEmpty); + break; + case kCaldoria53: + if (GameState.getCurrentDirection() == kEast && !GameState.getCaldoriaSinclairShot()) + zoomToSinclair(); + break; + case kCaldoria50: + if (direction == kNorth && !GameState.getCaldoriaSinclairShot()) + setUpSinclairLoops(); + break; + case kCaldoria54: + if (direction == kSouth && !GameState.getCaldoriaSinclairShot()) + setUpSinclairLoops(); + break; + case kCaldoria56: + if (_privateFlags.getFlag(kCaldoriaPrivateZoomingToBombFlag)) { + _privateFlags.setFlag(kCaldoriaPrivateZoomingToBombFlag, false); + newInteraction(kCaldoriaBombInteractionID); + } else if (GameState.getCaldoriaBombDisarmed()) { + _vm->playEndMessage(); + } + break; + } + + checkSinclairShootsOS(); +} + +void Caldoria::zoomTo(const Hotspot *zoomOutSpot) { + // Need to set _zoomOutSpot here because we may come through + // this function another way, say by pressing the down arrow, + // that doesn't involve the ClickInHotSpot function. + _zoomOutSpot = zoomOutSpot; + + if (zoomOutSpot->getObjectID() == kCaldoriaDrawersOutSpotID) { + if (_privateFlags.getFlag(kCaloriaPrivateLeftDrawerOpenFlag)) { + _privateFlags.setFlag(kCaloriaPrivateLeftDrawerOpenFlag, false); + startExtraSequence(kLeftDrawerClose, kExtraCompletedFlag, kFilterNoInput); + } else if (_privateFlags.getFlag(kCaldoriaPrivateRightDrawerOpenFlag)) { + _privateFlags.setFlag(kCaldoriaPrivateRightDrawerOpenFlag, false); + if (GameState.isTakenItemID(kKeyCard)) + startExtraSequence(kRightDrawerCloseNoKeys, kExtraCompletedFlag, false); + else + startExtraSequence(kRightDrawerCloseWithKeys, kExtraCompletedFlag, false); + } else { + Neighborhood::zoomTo(zoomOutSpot); + } + } else { + Neighborhood::zoomTo(zoomOutSpot); + } +} + +void Caldoria::setUpSinclairLoops() { + _navMovie.stop(); + scheduleNavCallBack(kSinclairLoopDoneFlag); + _sinclairLoopCount = 0; + _numSinclairLoops = 2; + _navMovie.start(); +} + +void Caldoria::zoomToSinclair() { + _utilityFuse.stopFuse(); + _privateFlags.setFlag(kCaldoriaPrivateReadyToShootFlag, true); + setCurrentActivation(kActivateZoomedOnSinclair); + + ExtraTable::Entry entry; + getExtraEntry(kCa53EastZoomToSinclair, entry); + _sinclairInterrupt.scheduleCallBack(kTriggerTimeFwd, entry.movieStart + kSinclairInterruptionTime1, _navMovie.getScale()); + startExtraSequence(kCa53EastZoomToSinclair, kExtraCompletedFlag, kFilterAllInput); +} + +void Caldoria::receiveNotification(Notification *notification, const NotificationFlags flags) { + Neighborhood::receiveNotification(notification, flags); + + if ((flags & kExtraCompletedFlag) != 0) { + InventoryItem *item; + _interruptionFilter = kFilterAllInput; + + switch (_lastExtra) { + case kCaldoria00WakeUp2: + makeContinuePoint(); + // Force ArriveAt to do its thing... + GameState.setCurrentRoom(kNoRoomID); + arriveAt(kCaldoria00, kEast); + break; + case k4DEnvironOpenToINN: + GameState.setCaldoriaSeenINN(true); + GameState.setScoringSawINN(true); + // Fall through to k4DEnvironOpen... + case k4DEnvironOpen: + _privateFlags.setFlag(kCaldoriaPrivate4DSystemOpenFlag, true); + setCurrentActivation(kActivate4DOpen); + newInteraction(kCaldoria4DInteractionID); + break; + case kCaldoriaShowerUp: + GameState.setScoringTookShower(true); + GameState.setCaldoriaDoneHygiene(true); + break; + case kLeftDrawerClose: + case kRightDrawerCloseNoKeys: + case kRightDrawerCloseWithKeys: + if (_zoomOutSpot && _zoomOutSpot->getObjectID() == kCaldoriaDrawersOutSpotID) { + Input input; + clickInHotspot(input, _zoomOutSpot); + } + break; + case kCreateOrangeJuice: + setCurrentActivation(kActivateOJOnThePad); + requestSpotSound(kCaldoriaReplicatorOJChoiceIn, kCaldoriaReplicatorOJChoiceOut, kFilterNoInput, 0); + break; + case kCaldoria00SitDown: + arriveAt(kCaldoria00, kEast); + break; + case kCaldoria16ElevatorUp: + startExtraSequence(kCaldoria16ElevatorDown, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaldoria16ElevatorDown: + GameState.setCaldoriaSeenSinclairInElevator(true); + _privateFlags.setFlag(kCaldoriaPrivateCanOpenElevatorDoorFlag, true); + openDoor(); + break; + case kCaldoriaFourthToGround: + case kCaldoriaRoofToGround: + arriveAt(kCaldoria28, GameState.getCurrentDirection()); + break; + case kCaldoriaFourthToRoof: + case kCaldoriaGroundToRoof: + arriveAt(kCaldoria45, GameState.getCurrentDirection()); + break; + case kCaldoriaGroundToFourth: + case kCaldoriaRoofToFourth: + arriveAt(kCaldoria27, GameState.getCurrentDirection()); + break; + case kCaGTCardSwipe: + item = (InventoryItem *)_vm->getAllItems().findItemByID(kKeyCard); + _vm->addItemToInventory(item); + setCurrentActivation(kActivateReadyToTransport); + break; + case kCaGTFryTheFly: + case kCaGTGoToTSA: + _vm->jumpToNewEnvironment(kFullTSAID, kTSA00, kNorth); + break; + case kCaGTGoToTokyo: + playDeathExtra(kCaGTArriveAtTokyo, kDeathUncreatedInCaldoria); + break; + case kCaGTGoToBeach: + playDeathExtra(kCaGTArriveAtBeach, kDeathUncreatedInCaldoria); + break; + case kCa48NorthExplosion: + // Current biochip must be the shield if we got here. + _vm->getCurrentBiochip()->setItemState(kShieldNormal); + break; + case kBinocularsZoomInOnShip: + setCurrentActivation(kActivateFocusedOnShip); + break; + case kCa49NorthVoiceAnalysis: + _utilityFuse.primeFuse(kSinclairShootsTimeLimit); + _utilityFuse.setFunctor(new Common::Functor0Mem<void, Caldoria>(this, &Caldoria::sinclairTimerExpired)); + _utilityFuse.lightFuse(); + GameState.setCaldoriaSawVoiceAnalysis(true); + break; + case kCa53EastZoomToSinclair: + if (GameState.getCaldoriaSinclairShot()) { + delete _gunSprite; + _gunSprite = 0; + startExtraSequence(kCa53EastShootSinclair, kExtraCompletedFlag, false); + } else { + playDeathExtra(kCa53EastDeath2, kDeathSinclairShotDelegate); + } + break; + case kCa53EastShootSinclair: + _vm->addItemToInventory((InventoryItem *)_vm->getAllItems().findItemByID(kStunGun)); + startExtraSequence(kCa53EastZoomOutFromSinclair, kExtraCompletedFlag, false); + GameState.setScoringStunnedSinclair(true); + break; + case kCa53EastZoomOutFromSinclair: + setCurrentAlternate(kAltCaldoriaSinclairDown); + updateViewFrame(); + makeContinuePoint(); + break; + } + } else if ((flags & kSpotSoundCompletedFlag) != 0) { + switch (GameState.getCurrentRoom()) { + case kCaldoria20: + case kCaldoria21: + case kCaldoria26: + case kCaldoria29: + case kCaldoria34: + case kCaldoria35: + updateViewFrame(); + break; + case kCaldoria27: + case kCaldoria28: + case kCaldoria45: + updateElevatorMovie(); + break; + case kCaldoriaReplicator: + emptyOJGlass(); + break; + } + } else if ((flags & kSinclairLoopDoneFlag) != 0) { + if (++_sinclairLoopCount == _numSinclairLoops) { + switch (GameState.getCurrentRoom()) { + case kCaldoria50: + playDeathExtra(kCa50SinclairShoots, kDeathShotBySinclair); + break; + case kCaldoria54: + playDeathExtra(kCa54SouthDeath, kDeathShotBySinclair); + break; + } + } else { + _navMovie.stop(); + scheduleNavCallBack(kSinclairLoopDoneFlag); + _navMovie.start(); + } + } + + g_AIArea->checkMiddleArea(); +} + +InputBits Caldoria::getInputFilter() { + InputBits result = Neighborhood::getInputFilter(); + + switch (GameState.getCurrentRoom()) { + case kCaldoria00: + if (_privateFlags.getFlag(kCaldoriaPrivate4DSystemOpenFlag)) + result &= ~kFilterAllDirections; + break; + case kCaldoriaBinoculars: + if (getCurrentActivation() == kActivateNotFocusedOnShip) + result &= ~(kFilterDownButton | kFilterDownAuto); + break; + case kCaldoria53: + if (_privateFlags.getFlag(kCaldoriaPrivateReadyToShootFlag) && !GameState.getCaldoriaSinclairShot()) + result &= ~kFilterAllDirections; + break; + case kCaldoria48: + if (GameState.getCaldoriaDoorBombed()) + result &= ~kFilterAllDirections; + } + + return result; +} + +void Caldoria::activateHotspots() { + Neighborhood::activateHotspots(); + + switch (GameState.getCurrentRoom()) { + case kCaldoriaDrawers: + if (getCurrentActivation() == kActivateRightOpen) { + if (GameState.isTakenItemID(kKeyCard)) { + _vm->getAllHotspots().activateOneHotspot(kCaldoriaRightDrawerNoKeysCloseSpotID); + _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaRightDrawerWithKeysCloseSpotID); + } else { + _vm->getAllHotspots().activateOneHotspot(kCaldoriaRightDrawerWithKeysCloseSpotID); + _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaRightDrawerNoKeysCloseSpotID); + } + } + case kCaldoriaReplicator: + if (GameState.getCaldoriaMadeOJ()) + _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaMakeOJSpotID); + break; + case kCaldoria27: + if (GameState.isCurrentDoorOpen()) { + _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaFourthFloorElevator1); + _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaFourthFloorElevator2); + _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaFourthFloorElevator3); + _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaFourthFloorElevator4); + _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaFourthFloorElevator5); + } + break; + case kCaldoria28: + if (GameState.isCurrentDoorOpen()) { + _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaGroundElevator1); + _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaGroundElevator2); + _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaGroundElevator3); + _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaGroundElevator4); + _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaGroundElevator5); + } + break; + case kCaldoria45: + if (GameState.isCurrentDoorOpen()) { + _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaRoofElevator1); + _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaRoofElevator2); + _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaRoofElevator3); + _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaRoofElevator4); + _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaRoofElevator5); + } + break; + } +} + +void Caldoria::clickInHotspot(const Input &input, const Hotspot *spot) { + switch (spot->getObjectID()) { + case kCa4DEnvironOpenSpotID: + if (!GameState.getCaldoriaINNAnnouncing() || GameState.getCaldoriaSeenINN()) { + startExtraSequence(k4DEnvironOpen, kExtraCompletedFlag, kFilterNoInput); + } else { + // This trick depends on the following sequences being in order in the + // world movie: + // k4DEnvironOpenToINN + // k4DINNInterruption + // k4DINNIntro + // k4DINNMarkJohnson + // k4DINNMeganLove + // k4DINNFadeOut + // k4DEnvironOpenFromINN + loadLoopSound1(""); + loadLoopSound2(""); + startExtraLongSequence(k4DEnvironOpenToINN, k4DEnvironOpenFromINN, kExtraCompletedFlag, kFilterNoInput); + } + break; + case kCa4DEnvironCloseSpotID: + ((Caldoria4DSystem *)_currentInteraction)->shutDown4DSystem(); + break; + case kCaBathroomMirrorSpotID: + newInteraction(kCaldoriaMirrorInteractionID); + break; + case kCaShowerSpotID: + requestExtraSequence(kCaldoriaShowerTitle, 0, kFilterNoInput); + requestExtraSequence(kCaldoriaShowerButton, 0, kFilterNoInput); + requestExtraSequence(kCaldoriaShowerDown, 0, kFilterNoInput); + requestExtraSequence(kCaldoriaShowerUp, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaldoriaLeftDrawerOpenSpotID: + _privateFlags.setFlag(kCaloriaPrivateLeftDrawerOpenFlag, true); + setCurrentActivation(kActivateLeftOpen); + startExtraSequence(kLeftDrawerOpen, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaldoriaLeftDrawerCloseSpotID: + _privateFlags.setFlag(kCaloriaPrivateLeftDrawerOpenFlag, false); + setCurrentActivation(kActivateDrawersClosed); + startExtraSequence(kLeftDrawerClose, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaldoriaRightDrawerOpenSpotID: + _privateFlags.setFlag(kCaldoriaPrivateRightDrawerOpenFlag, true); + setCurrentActivation(kActivateRightOpen); + if (GameState.isTakenItemID(kKeyCard)) + startExtraSequence(kRightDrawerOpenNoKeys, kExtraCompletedFlag, kFilterNoInput); + else + startExtraSequence(kRightDrawerOpenWithKeys, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaldoriaRightDrawerWithKeysCloseSpotID: + _privateFlags.setFlag(kCaldoriaPrivateRightDrawerOpenFlag, false); + setCurrentActivation(kActivateDrawersClosed); + startExtraSequence(kRightDrawerCloseWithKeys, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaldoriaRightDrawerNoKeysCloseSpotID: + _privateFlags.setFlag(kCaldoriaPrivateRightDrawerOpenFlag, false); + setCurrentActivation(kActivateDrawersClosed); + startExtraSequence(kRightDrawerCloseNoKeys, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaldoriaMakeStickyBunsSpotID: + requestSpotSound(kCaldoriaReplicatorWrongChoiceIn, kCaldoriaReplicatorWrongChoiceOut, kFilterNoInput, 0); + break; + case kCaldoriaMakeOJSpotID: + GameState.setCaldoriaMadeOJ(true); + startExtraSequence(kCreateOrangeJuice, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaBedroomVidPhoneActivationSpotID: + newInteraction(kCaldoriaMessagesInteractionID); + break; + case kCaldoriaFourthFloorElevatorSpotID: + if (!GameState.getCaldoriaSeenSinclairInElevator()) { + startExtraSequence(kCaldoria16ElevatorUp, kExtraCompletedFlag, kFilterNoInput); + } else { + _privateFlags.setFlag(kCaldoriaPrivateCanOpenElevatorDoorFlag, true); + openDoor(); + } + break; + case kCaldoriaGroundElevatorSpotID: + _privateFlags.setFlag(kCaldoriaPrivateCanOpenElevatorDoorFlag, true); + openDoor(); + break; + case kCaldoriaRoofElevatorSpotID: + _privateFlags.setFlag(kCaldoriaPrivateCanOpenElevatorDoorFlag, true); + openDoor(); + break; + case kCaldoriaFourthFloorElevator1: + case kCaldoriaFourthFloorElevator2: + case kCaldoriaFourthFloorElevator3: + case kCaldoriaFourthFloorElevator4: + case kCaldoriaFourthFloorElevator5: + // Assumes that elevator hot spots are consecutive. + takeElevator(4, spot->getObjectID() - kCaldoriaFourthFloorElevator1 + 1); + break; + case kCaldoriaGroundElevator1: + case kCaldoriaGroundElevator2: + case kCaldoriaGroundElevator3: + case kCaldoriaGroundElevator4: + case kCaldoriaGroundElevator5: + // Assumes that elevator hot spots are consecutive. + takeElevator(1, spot->getObjectID() - kCaldoriaGroundElevator1 + 1); + break; + case kCaldoriaRoofElevator1: + case kCaldoriaRoofElevator2: + case kCaldoriaRoofElevator3: + case kCaldoriaRoofElevator4: + case kCaldoriaRoofElevator5: + // Assumes that elevator hot spots are consecutive. + takeElevator(5, spot->getObjectID() - kCaldoriaRoofElevator1 + 1); + break; + case kCaldoriaGTTokyoSpotID: + startExtraSequence(kCaGTGoToTokyo, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaldoriaGTTSASpotID: + GameState.setScoringGoToTSA(true); + startExtraLongSequence(kCaGTFryTheFly, kCaGTGoToTSA, kExtraCompletedFlag, false); + break; + case kCaldoriaGTBeachSpotID: + startExtraSequence(kCaGTGoToBeach, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaldoriaGTOtherSpotID: + showExtraView(kCaGTOtherChoice); + playSpotSoundSync(kCaldoriaNoOtherDestinationIn, kCaldoriaNoOtherDestinationOut); + showExtraView(kCaGTCardSwipe); + break; + case kCaldoriaZoomInOnShipSpotID: + startExtraSequence(kBinocularsZoomInOnShip, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaldoriaRoofDoorSpotID: + startExtraSequence(kCa48NorthRooftopClosed, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaldoria20DoorbellSpotID: + case kCaldoria21DoorbellSpotID: + case kCaldoria26DoorbellSpotID: + case kCaldoria29DoorbellSpotID: + case kCaldoria34DoorbellSpotID: + case kCaldoria35DoorbellSpotID: + clickOnDoorbell(spot->getObjectID()); + break; + default: + Neighborhood::clickInHotspot(input, spot); + break; + } +} + +void Caldoria::clickOnDoorbell(const HotSpotID doorBellSpotID) { + uint32 extra; + ExtraTable::Entry entry; + + switch (doorBellSpotID) { + case kCaldoria20DoorbellSpotID: + extra = kCaldoria20Doorbell; + break; + case kCaldoria21DoorbellSpotID: + extra = kCaldoria21Doorbell; + break; + case kCaldoria26DoorbellSpotID: + extra = kCaldoria26Doorbell; + break; + case kCaldoria29DoorbellSpotID: + extra = kCaldoria29Doorbell; + break; + case kCaldoria34DoorbellSpotID: + extra = kCaldoria34Doorbell; + break; + case kCaldoria35DoorbellSpotID: + extra = kCaldoria35Doorbell; + break; + default: + error("Invalid doorbell hotspot"); + } + + getExtraEntry(extra, entry); + showViewFrame(entry.movieStart); + requestSpotSound(kCaldoriaNobodyHomeIn, kCaldoriaNobodyHomeOut, kFilterNoInput, kSpotSoundCompletedFlag); +} + +CanOpenDoorReason Caldoria::canOpenDoor(DoorTable::Entry &entry) { + switch (GameState.getCurrentRoom()) { + case kCaldoria16: + case kCaldoria38: + case kCaldoria46: + if (GameState.getCurrentDirection() == kSouth && !_privateFlags.getFlag(kCaldoriaPrivateCanOpenElevatorDoorFlag)) + return kCantOpenLocked; + break; + } + + return Neighborhood::canOpenDoor(entry); +} + +void Caldoria::doorOpened() { + Neighborhood::doorOpened(); + _privateFlags.setFlag(kCaldoriaPrivateCanOpenElevatorDoorFlag, false); +} + +GameInteraction *Caldoria::makeInteraction(const InteractionID interactionID) { + switch (interactionID) { + case kCaldoria4DInteractionID: + return new Caldoria4DSystem(this); + case kCaldoriaBombInteractionID: + return new CaldoriaBomb(this, _vm); + case kCaldoriaMessagesInteractionID: + return new CaldoriaMessages(this, kCaldoriaMessagesNotificationID, _vm); + case kCaldoriaMirrorInteractionID: + return new CaldoriaMirror(this); + } + + return 0; +} + +void Caldoria::newInteraction(const InteractionID interactionID) { + Neighborhood::newInteraction(interactionID); + + if (!_currentInteraction) { + if (_privateFlags.getFlag(kCaldoriaPrivate4DSystemOpenFlag)) { + _privateFlags.setFlag(kCaldoriaPrivate4DSystemOpenFlag, false); + setCurrentActivation(kActivate4DClosed); + startExtraSequence(k4DEnvironClose, kExtraCompletedFlag, kFilterNoInput); + } else if (GameState.getCaldoriaBombDisarmed()) { + turnLeft(); + } + } +} + +// Only called when trying to pick up an item and the player can't (because +// the inventory is too full or because the player lets go of the item before +// dropping it into the inventory). +Hotspot *Caldoria::getItemScreenSpot(Item *item, DisplayElement *element) { + HotSpotID destSpotID = kNoHotSpotID; + + switch (item->getObjectID()) { + case kKeyCard: + destSpotID = kCaldoriaKeyCardSpotID; + break; + case kOrangeJuiceGlassEmpty: + case kOrangeJuiceGlassFull: + destSpotID = kCaldoriaOrangeJuiceSpotID; + break; + } + + if (destSpotID == kNoHotSpotID) + return Neighborhood::getItemScreenSpot(item, element); + + return _vm->getAllHotspots().findHotspotByID(destSpotID); +} + +void Caldoria::pickedUpItem(Item *item) { + switch (item->getObjectID()) { + case kKeyCard: + GameState.setScoringGotKeyCard(true); + break; + case kOrangeJuiceGlassFull: + setCurrentActivation(kActivateReplicatorReady); + requestSpotSound(kCaldoriaDrinkOJIn, kCaldoriaDrinkOJOut, kFilterNoInput, kSpotSoundCompletedFlag); + break; + case kStunGun: + GameState.setCaldoriaGunAimed(false); + break; + } +} + +void Caldoria::dropItemIntoRoom(Item *item, Hotspot *dropSpot) { + switch (item->getObjectID()) { + case kKeyCard: + Neighborhood::dropItemIntoRoom(item, dropSpot); + if (dropSpot->getObjectID() == kCaldoriaGTCardDropSpotID) + startExtraSequence(kCaGTCardSwipe, kExtraCompletedFlag, kFilterNoInput); + break; + case kOrangeJuiceGlassEmpty: + Neighborhood::dropItemIntoRoom(item, dropSpot); + if (dropSpot->getObjectID() == kCaldoriaOrangeJuiceDropSpotID) { + GameState.setCaldoriaMadeOJ(false); + startExtraSequence(kDisposeOrangeJuice, kExtraCompletedFlag, kFilterNoInput); + } + break; + case kCardBomb: + GameState.setCaldoriaDoorBombed(true); + setCurrentActivation(kActivateHotSpotAlways); + Neighborhood::dropItemIntoRoom(item, dropSpot); + // Long enough for AI hints...? + _utilityFuse.primeFuse(kCardBombCountDownTime); + _utilityFuse.setFunctor(new Common::Functor0Mem<void, Caldoria>(this, &Caldoria::doorBombTimerExpired)); + _utilityFuse.lightFuse(); + GameState.setCaldoriaFuseTimeLimit(kCardBombCountDownTime); + loopCroppedMovie("Images/Caldoria/A48 Bomb Loop", kCaldoria48CardBombLoopLeft, kCaldoria48CardBombLoopTop); + GameState.setScoringUsedCardBomb(true); + break; + case kStunGun: + GameState.setCaldoriaGunAimed(true); + GameState.setCaldoriaSinclairShot(true); + _gunSprite = item->getDragSprite(0); + _gunSprite->setCurrentFrameIndex(1); + _gunSprite->setDisplayOrder(kDragSpriteOrder); + _gunSprite->moveElementTo(kCaldoriaGunSpriteLeft, kCaldoriaGunSpriteTop); + _gunSprite->startDisplaying(); + _gunSprite->show(); + break; + default: + Neighborhood::dropItemIntoRoom(item, dropSpot); + break; + } +} + +void Caldoria::takeElevator(uint startFloor, uint endFloor) { + _croppedMovie.stop(); + _croppedMovie.setSegment(0, _croppedMovie.getDuration()); + + switch (startFloor) { + case 1: + switch (endFloor) { + case 1: + // Do nothing. + break; + case 2: + _croppedMovie.setTime(k1To2Time); + requestSpotSound(kCaldoriaNoOtherDestinationIn, kCaldoriaNoOtherDestinationOut, kFilterNoInput, kSpotSoundCompletedFlag); + break; + case 3: + _croppedMovie.setTime(k1To3Time); + requestSpotSound(kCaldoriaNoOtherDestinationIn, kCaldoriaNoOtherDestinationOut, kFilterNoInput, kSpotSoundCompletedFlag); + break; + case 4: + _croppedMovie.setSegment(k1To4Start, k1To4Stop); + _croppedMovie.setTime(k1To4Start); + startExtraSequence(kCaldoriaGroundToFourth, kExtraCompletedFlag, false); + _croppedMovie.start(); + break; + case 5: + _croppedMovie.setSegment(k1To5Start, k1To5Stop); + _croppedMovie.setTime(k1To5Start); + startExtraSequence(kCaldoriaGroundToRoof, kExtraCompletedFlag, false); + _croppedMovie.start(); + break; + } + break; + case 4: + switch (endFloor) { + case 1: + _croppedMovie.setSegment(k4To1Start, k4To1Stop); + _croppedMovie.setTime(k4To1Start); + startExtraSequence(kCaldoriaFourthToGround, kExtraCompletedFlag, false); + _croppedMovie.start(); + break; + case 2: + _croppedMovie.setTime(k4To2Time); + requestSpotSound(kCaldoriaNoOtherDestinationIn, kCaldoriaNoOtherDestinationOut, kFilterNoInput, kSpotSoundCompletedFlag); + break; + case 3: + _croppedMovie.setTime(k4To3Time); + requestSpotSound(kCaldoriaNoOtherDestinationIn, kCaldoriaNoOtherDestinationOut, kFilterNoInput, kSpotSoundCompletedFlag); + break; + case 4: + // Do nothing. + break; + case 5: + _croppedMovie.setSegment(k4To5Start, k4To5Stop); + _croppedMovie.setTime(k4To5Start); + startExtraSequence(kCaldoriaFourthToRoof, kExtraCompletedFlag, false); + _croppedMovie.start(); + break; + } + break; + case 5: + switch (endFloor) { + case 1: + _croppedMovie.setSegment(k5To1Start, k5To1Stop); + _croppedMovie.setTime(k5To1Start); + startExtraSequence(kCaldoriaRoofToGround, kExtraCompletedFlag, false); + _croppedMovie.start(); + break; + case 2: + _croppedMovie.setTime(k5To2Time); + requestSpotSound(kCaldoriaNoOtherDestinationIn, kCaldoriaNoOtherDestinationOut, kFilterNoInput, kSpotSoundCompletedFlag); + break; + case 3: + _croppedMovie.setTime(k5To3Time); + requestSpotSound(kCaldoriaNoOtherDestinationIn, kCaldoriaNoOtherDestinationOut, kFilterNoInput, kSpotSoundCompletedFlag); + break; + case 4: + _croppedMovie.setSegment(k5To4Start, k5To4Stop); + _croppedMovie.setTime(k5To4Start); + startExtraSequence(kCaldoriaRoofToFourth, kExtraCompletedFlag, false); + _croppedMovie.start(); + break; + case 5: + // Do nothing. + break; + } + break; + }; +} + +void Caldoria::updateElevatorMovie() { + TimeValue time = 0xffffffff; + + if (GameState.getCurrentDirection() == kNorth) { + switch (GameState.getCurrentRoom()) { + case kCaldoria27: + time = k4FloorTime; + break; + case kCaldoria28: + time = k1FloorTime; + break; + case kCaldoria45: + time = k5FloorTime; + break; + } + } + + _croppedMovie.stop(); + + if (time == 0xffffffff) { + _croppedMovie.hide(); + } else { + _croppedMovie.stop(); + _croppedMovie.setSegment(0, _croppedMovie.getDuration()); + _croppedMovie.setTime(time); + _croppedMovie.redrawMovieWorld(); + _croppedMovie.show(); + + // *** Why do I need this? + // clone2727: "don't ask me!" + _navMovie.redrawMovieWorld(); + } +} + +void Caldoria::openElevatorMovie() { + if (!_croppedMovie.isSurfaceValid()) + openCroppedMovie("Images/Caldoria/Caldoria Elevator.movie", kCaldoriaElevatorLeft, kCaldoriaElevatorTop); + + updateElevatorMovie(); +} + +void Caldoria::emptyOJGlass() { + GameState.setTakenItemID(kOrangeJuiceGlassFull, false); + GameState.setTakenItemID(kOrangeJuiceGlassEmpty, true); + _vm->removeItemFromInventory((InventoryItem *)_vm->getAllItems().findItemByID(kOrangeJuiceGlassFull)); + _vm->addItemToInventory((InventoryItem *)_vm->getAllItems().findItemByID(kOrangeJuiceGlassEmpty)); +} + +void Caldoria::doorBombTimerExpired() { + closeCroppedMovie(); + + if (GameState.getShieldOn()) { + _vm->getCurrentBiochip()->setItemState(kShieldCardBomb); + setCurrentAlternate(kAltCaldoriaRoofDoorBlown); + startExtraSequence(kCa48NorthExplosion, kExtraCompletedFlag, kFilterNoInput); + GameState.setScoringShieldedCardBomb(true); + GameState.setCaldoriaDoorBombed(false); + GameState.setCaldoriaRoofDoorOpen(true); + } else { + playDeathExtra(kCa48NorthExplosionDeath, kDeathCardBomb); + } +} + +void Caldoria::sinclairTimerExpired() { + _privateFlags.setFlag(kCaldoriaPrivateSinclairTimerExpiredFlag, true); + checkSinclairShootsOS(); +} + +void Caldoria::checkSinclairShootsOS() { + if (_privateFlags.getFlag(kCaldoriaPrivateSinclairTimerExpiredFlag)) + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kCaldoria49, kNorth): + case MakeRoomView(kCaldoria49, kSouth): + case MakeRoomView(kCaldoria49, kEast): + case MakeRoomView(kCaldoria49, kWest): + case MakeRoomView(kCaldoria50, kSouth): + case MakeRoomView(kCaldoria50, kEast): + case MakeRoomView(kCaldoria50, kWest): + case MakeRoomView(kCaldoria51, kNorth): + case MakeRoomView(kCaldoria51, kSouth): + case MakeRoomView(kCaldoria51, kWest): + case MakeRoomView(kCaldoria52, kNorth): + case MakeRoomView(kCaldoria52, kSouth): + case MakeRoomView(kCaldoria52, kWest): + case MakeRoomView(kCaldoria53, kNorth): + case MakeRoomView(kCaldoria53, kSouth): + case MakeRoomView(kCaldoria53, kWest): + case MakeRoomView(kCaldoria54, kNorth): + case MakeRoomView(kCaldoria54, kEast): + case MakeRoomView(kCaldoria54, kWest): + playSpotSoundSync(kCaldoriaSinclairShootsOSIn, kCaldoriaSinclairShootsOSOut); + playSpotSoundSync(kCaldoriaScreamingAfterIn, kCaldoriaScreamingAfterOut); + die(kDeathSinclairShotDelegate); + break; + } +} + +void Caldoria::checkInterruptSinclair() { + if (GameState.getCaldoriaSinclairShot()) { + _navMovie.stop(); + _neighborhoodNotification.setNotificationFlags(kExtraCompletedFlag, kExtraCompletedFlag); + g_AIArea->unlockAI(); + } else { + uint32 currentTime = _navMovie.getTime(); + + ExtraTable::Entry entry; + getExtraEntry(kCa53EastZoomToSinclair, entry); + + if (currentTime < entry.movieStart + kSinclairInterruptionTime2) + _sinclairInterrupt.scheduleCallBack(kTriggerTimeFwd, entry.movieStart + kSinclairInterruptionTime2, + _navMovie.getScale()); + else if (currentTime < entry.movieStart + kSinclairInterruptionTime3) + _sinclairInterrupt.scheduleCallBack(kTriggerTimeFwd, entry.movieStart + kSinclairInterruptionTime3, + _navMovie.getScale()); + else if (currentTime < entry.movieStart + kSinclairInterruptionTime4) + _sinclairInterrupt.scheduleCallBack(kTriggerTimeFwd, entry.movieStart + kSinclairInterruptionTime4, + _navMovie.getScale()); + } +} + +Common::String Caldoria::getBriefingMovie() { + Common::String movieName = Neighborhood::getBriefingMovie(); + + if (movieName.empty()) { + if (GameState.allTimeZonesFinished()) + return "Images/AI/Caldoria/XA02"; + + return "Images/AI/Caldoria/XA01"; + } + + return movieName; +} + +Common::String Caldoria::getEnvScanMovie() { + Common::String movieName = Neighborhood::getEnvScanMovie(); + + if (movieName.empty()) { + RoomID room = GameState.getCurrentRoom(); + + if (room >= kCaldoria00 && room <= kCaldoria14) { + // Inside apartment. + if (GameState.getCaldoriaDoneHygiene()) + return "Images/AI/Caldoria/XAE2"; + + return "Images/AI/Caldoria/XAE1"; + } else if (room >= kCaldoria15 && room <= kCaldoria48) { + // Wandering the halls... + return "Images/AI/Caldoria/XAE3"; + } else { + // Must be the roof. + return "Images/AI/Caldoria/XAEH2"; + } + } + + return movieName; +} + +uint Caldoria::getNumHints() { + uint numHints = Neighborhood::getNumHints(); + + if (numHints == 0) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kCaldoria44, kEast): + if (!GameState.isTakenItemID(kKeyCard) && GameState.getOpenDoorRoom() == kNoRoomID) + numHints = 1; + break; + case MakeRoomView(kCaldoria48, kNorth): + if (!GameState.getCaldoriaRoofDoorOpen()) { + if (_croppedMovie.isRunning()) // Bomb must be looping. + numHints = 3; + else if (GameState.isTakenItemID(kCardBomb)) + numHints = 1; + } + break; + case MakeRoomView(kCaldoria49, kEast): + case MakeRoomView(kCaldoria54, kEast): + numHints = 1; + break; + case MakeRoomView(kCaldoria49, kNorth): + numHints = 1; + break; + } + } + + return numHints; +} + +Common::String Caldoria::getHintMovie(uint hintNum) { + Common::String movieName = Neighborhood::getHintMovie(hintNum); + + if (movieName.empty()) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kCaldoria44, kEast): + return "Images/AI/Caldoria/X42WH2"; + case MakeRoomView(kCaldoria48, kNorth): + if (_croppedMovie.isRunning()) { // Bomb must be looping. + if (hintNum == 1) + return "Images/AI/Caldoria/X48ND1"; + else if (hintNum == 2) + return "Images/AI/Caldoria/X48ND2"; + else if (GameState.isTakenItemID(kShieldBiochip)) + return "Images/AI/Caldoria/X48ND3"; + + // *** Doesn't work yet, need global movies. + break; + } + + return "Images/AI/Globals/XGLOB1A"; + case MakeRoomView(kCaldoria49, kEast): + case MakeRoomView(kCaldoria54, kEast): + return "Images/AI/Caldoria/X49E"; + case MakeRoomView(kCaldoria49, kNorth): + return "Images/AI/Caldoria/X49NB2"; + } + } + + return movieName; +} + +void Caldoria::updateCursor(const Common::Point where, const Hotspot *cursorSpot) { + if (cursorSpot) { + switch (cursorSpot->getObjectID()) { + case kCa4DEnvironCloseSpotID: + _vm->_cursor->setCurrentFrameIndex(2); + return; + case kCaldoriaKioskSpotID: + _vm->_cursor->setCurrentFrameIndex(3); + return; + } + } + + Neighborhood::updateCursor(where, cursorSpot); +} + +Common::String Caldoria::getNavMovieName() { + return "Images/Caldoria/Caldoria.movie"; +} + +Common::String Caldoria::getSoundSpotsName() { + return "Sounds/Caldoria/Caldoria Spots"; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/caldoria/caldoria.h b/engines/pegasus/neighborhood/caldoria/caldoria.h new file mode 100644 index 0000000000..3d6a155170 --- /dev/null +++ b/engines/pegasus/neighborhood/caldoria/caldoria.h @@ -0,0 +1,523 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_NEIGHBORHOOD_CALDORIA_CALDORIA_H +#define PEGASUS_NEIGHBORHOOD_CALDORIA_CALDORIA_H + +#include "pegasus/neighborhood/neighborhood.h" + +namespace Pegasus { + +static const TimeScale kCaldoriaMovieScale = 600; +static const TimeScale kCaldoriaFramesPerSecond = 15; +static const TimeScale kCaldoriaFrameDuration = 40; + +// Alternate IDs. + +static const AlternateID kAltCaldoriaNormal = 0; +static const AlternateID kAltCaldoriaRoofDoorBlown = 2; +static const AlternateID kAltCaldoriaSinclairDown = 3; + +// Room IDs. + +static const RoomID kCaldoria00 = 1; +static const RoomID kCaldoria01 = 2; +static const RoomID kCaldoria02 = 3; +static const RoomID kCaldoria03 = 4; +static const RoomID kCaldoria04 = 5; +static const RoomID kCaldoria05 = 6; +static const RoomID kCaldoria06 = 7; +static const RoomID kCaldoria07 = 8; +static const RoomID kCaldoria08 = 9; +static const RoomID kCaldoria09 = 10; +static const RoomID kCaldoria10 = 11; +static const RoomID kCaldoriaToilet = 12; +static const RoomID kCaldoria11 = 13; +static const RoomID kCaldoria12 = 14; +static const RoomID kCaldoriaVidPhone = 15; +static const RoomID kCaldoriaReplicator = 16; +static const RoomID kCaldoriaDrawers = 17; +static const RoomID kCaldoria13 = 18; +static const RoomID kCaldoria14 = 19; +static const RoomID kCaldoria15 = 20; +static const RoomID kCaldoria16 = 21; +static const RoomID kCaldoria17 = 22; +static const RoomID kCaldoria18 = 23; +static const RoomID kCaldoria19 = 24; +static const RoomID kCaldoria20 = 25; +static const RoomID kCaldoria21 = 26; +static const RoomID kCaldoria22 = 27; +static const RoomID kCaldoria23 = 28; +static const RoomID kCaldoria24 = 29; +static const RoomID kCaldoria25 = 30; +static const RoomID kCaldoria26 = 31; +static const RoomID kCaldoria27 = 32; +static const RoomID kCaldoria28 = 33; +static const RoomID kCaldoria29 = 34; +static const RoomID kCaldoria30 = 35; +static const RoomID kCaldoria31 = 36; +static const RoomID kCaldoria32 = 37; +static const RoomID kCaldoria33 = 38; +static const RoomID kCaldoria34 = 39; +static const RoomID kCaldoria35 = 40; +static const RoomID kCaldoria36 = 41; +static const RoomID kCaldoria37 = 42; +static const RoomID kCaldoria38 = 43; +static const RoomID kCaldoria39 = 44; +static const RoomID kCaldoria40 = 45; +static const RoomID kCaldoria41 = 46; +static const RoomID kCaldoriaBinoculars = 47; +static const RoomID kCaldoria42 = 48; +static const RoomID kCaldoriaKiosk = 49; +static const RoomID kCaldoria44 = 50; +static const RoomID kCaldoria45 = 51; +static const RoomID kCaldoria46 = 52; +static const RoomID kCaldoria47 = 53; +static const RoomID kCaldoria48 = 54; +static const RoomID kCaldoria49 = 55; +static const RoomID kCaldoria50 = 56; +static const RoomID kCaldoria51 = 57; +static const RoomID kCaldoria52 = 58; +static const RoomID kCaldoria53 = 59; +static const RoomID kCaldoria54 = 60; +static const RoomID kCaldoria55 = 61; +static const RoomID kCaldoria56 = 62; +static const RoomID kCaldoriaDeathRoom = 0; + +// Hot Spot Activation IDs. + +static const HotSpotActivationID kActivate4DClosed = 1; +static const HotSpotActivationID kActivate4DOpen = 2; +static const HotSpotActivationID kActivateMirrorReady = 3; +static const HotSpotActivationID kActivateStylistReady = 4; +static const HotSpotActivationID kActivateReplicatorReady = 5; +static const HotSpotActivationID kActivateOJOnThePad = 6; +static const HotSpotActivationID kActivateDrawersClosed = 7; +static const HotSpotActivationID kActivateRightOpen = 8; +static const HotSpotActivationID kActivateLeftOpen = 9; +static const HotSpotActivationID kActivateFocusedOnShip = 10; +static const HotSpotActivationID kActivateNotFocusedOnShip = 11; +static const HotSpotActivationID kActivateReadyForCard = 12; +static const HotSpotActivationID kActivateReadyToTransport = 13; +static const HotSpotActivationID kActivateRoofSlotEmpty = 14; +static const HotSpotActivationID kActivateZoomedOnSinclair = 15; + +// Hot Spot IDs. + +static const HotSpotID kCa4DEnvironOpenSpotID = 5000; +static const HotSpotID kCa4DEnvironCloseSpotID = 5001; +static const HotSpotID kCa4DVisualSpotID = 5002; +static const HotSpotID kCa4DAudioSpotID = 5003; +static const HotSpotID kCa4DChoice1SpotID = 5004; +static const HotSpotID kCa4DChoice2SpotID = 5005; +static const HotSpotID kCa4DChoice3SpotID = 5006; +static const HotSpotID kCa4DChoice4SpotID = 5007; +static const HotSpotID kCaBathroomMirrorSpotID = 5008; +static const HotSpotID kCaHairStyle1SpotID = 5009; +static const HotSpotID kCaHairStyle2SpotID = 5010; +static const HotSpotID kCaHairStyle3SpotID = 5011; +static const HotSpotID kCaShowerSpotID = 5012; +static const HotSpotID kCaBathroomToiletSpotID = 5013; +static const HotSpotID kCaldoriaVidPhoneSpotID = 5014; +static const HotSpotID kCaldoriaReplicatorSpotID = 5015; +static const HotSpotID kCaldoriaDrawersSpotID = 5016; +static const HotSpotID kCaldoriaVidPhoneOutSpotID = 5017; +static const HotSpotID kCaBedroomVidPhoneActivationSpotID = 5018; +static const HotSpotID kCaldoriaReplicatorOutSpotID = 5019; +static const HotSpotID kCaldoriaMakeOJSpotID = 5020; +static const HotSpotID kCaldoriaMakeStickyBunsSpotID = 5021; +static const HotSpotID kCaldoriaOrangeJuiceSpotID = 5022; +static const HotSpotID kCaldoriaOrangeJuiceDropSpotID = 5023; +static const HotSpotID kCaldoriaDrawersOutSpotID = 5024; +static const HotSpotID kCaldoriaLeftDrawerOpenSpotID = 5025; +static const HotSpotID kCaldoriaRightDrawerOpenSpotID = 5026; +static const HotSpotID kCaldoriaKeyCardSpotID = 5027; +static const HotSpotID kCaldoriaLeftDrawerCloseSpotID = 5028; +static const HotSpotID kCaldoriaRightDrawerWithKeysCloseSpotID = 5029; +static const HotSpotID kCaldoriaRightDrawerNoKeysCloseSpotID = 5030; +static const HotSpotID kCaldoriaFourthFloorElevatorSpotID = 5031; +static const HotSpotID kCaldoria20DoorbellSpotID = 5032; +static const HotSpotID kCaldoria21DoorbellSpotID = 5033; +static const HotSpotID kCaldoria26DoorbellSpotID = 5034; +static const HotSpotID kCaldoriaFourthFloorElevator1 = 5035; +static const HotSpotID kCaldoriaFourthFloorElevator2 = 5036; +static const HotSpotID kCaldoriaFourthFloorElevator3 = 5037; +static const HotSpotID kCaldoriaFourthFloorElevator4 = 5038; +static const HotSpotID kCaldoriaFourthFloorElevator5 = 5039; +static const HotSpotID kCaldoriaGroundElevator1 = 5040; +static const HotSpotID kCaldoriaGroundElevator2 = 5041; +static const HotSpotID kCaldoriaGroundElevator3 = 5042; +static const HotSpotID kCaldoriaGroundElevator4 = 5043; +static const HotSpotID kCaldoriaGroundElevator5 = 5044; +static const HotSpotID kCaldoria29DoorbellSpotID = 5045; +static const HotSpotID kCaldoria34DoorbellSpotID = 5046; +static const HotSpotID kCaldoria35DoorbellSpotID = 5047; +static const HotSpotID kCaldoriaGroundElevatorSpotID = 5048; +static const HotSpotID kCaldoriaBinocularZoomInSpotID = 5049; +static const HotSpotID kCaldoriaBinocularsOutSpotID = 5050; +static const HotSpotID kCaldoriaZoomInOnShipSpotID = 5051; +static const HotSpotID kCaldoriaKioskSpotID = 5052; +static const HotSpotID kCaldoriaKioskOutSpotID = 5053; +static const HotSpotID kCaldoriaKioskInfoSpotID = 5054; +static const HotSpotID kCaldoriaGTCardDropSpotID = 5055; +static const HotSpotID kCaldoriaGTTokyoSpotID = 5056; +static const HotSpotID kCaldoriaGTTSASpotID = 5057; +static const HotSpotID kCaldoriaGTBeachSpotID = 5058; +static const HotSpotID kCaldoriaGTOtherSpotID = 5059; +static const HotSpotID kCaldoriaRoofElevator1 = 5060; +static const HotSpotID kCaldoriaRoofElevator2 = 5061; +static const HotSpotID kCaldoriaRoofElevator3 = 5062; +static const HotSpotID kCaldoriaRoofElevator4 = 5063; +static const HotSpotID kCaldoriaRoofElevator5 = 5064; +static const HotSpotID kCaldoriaRoofElevatorSpotID = 5065; +static const HotSpotID kCaldoriaRoofDoorSpotID = 5066; +static const HotSpotID kCaldoriaRoofCardDropSpotID = 5067; +static const HotSpotID kCaldoria53EastSinclairTargetSpotID = 5068; + +// Extra sequence IDs. + +static const ExtraID kCaldoriaWakeUpView1 = 0; +static const ExtraID kCaldoria00WakeUp1 = 1; +static const ExtraID kCaldoria00WakeUp2 = 2; +static const ExtraID kCaldoria00SitDown = 3; +static const ExtraID k4DEnvironOpenToINN = 4; +static const ExtraID k4DINNInterruption = 5; +static const ExtraID k4DINNIntro = 6; +static const ExtraID k4DINNMarkJohnson = 7; +static const ExtraID k4DINNMeganLove = 8; +static const ExtraID k4DINNFadeOut = 9; +static const ExtraID k4DEnvironOpenFromINN = 10; +static const ExtraID k4DEnvironOpen = 11; +static const ExtraID k4DEnvironOpenView = 12; +static const ExtraID k4DEnvironClose = 13; +static const ExtraID k4DIslandLoop = 14; +static const ExtraID k4DDesertLoop = 15; +static const ExtraID k4DMountainLoop = 16; +static const ExtraID k4DIsland1ToIsland0 = 17; +static const ExtraID k4DIsland2ToIsland0 = 18; +static const ExtraID k4DIsland0ToDesert0 = 19; +static const ExtraID k4DIsland1ToDesert0 = 20; +static const ExtraID k4DIsland2ToDesert0 = 21; +static const ExtraID k4DIsland0ToMountain0 = 22; +static const ExtraID k4DIsland1ToMountain0 = 23; +static const ExtraID k4DIsland2ToMountain0 = 24; +static const ExtraID k4DDesert0ToIsland0 = 25; +static const ExtraID k4DDesert1ToIsland0 = 26; +static const ExtraID k4DDesert2ToIsland0 = 27; +static const ExtraID k4DDesert0ToMountain0 = 28; +static const ExtraID k4DDesert1ToMountain0 = 29; +static const ExtraID k4DDesert2ToMountain0 = 30; +static const ExtraID k4DMountain0ToIsland0 = 31; +static const ExtraID k4DMountain1ToIsland0 = 32; +static const ExtraID k4DMountain2ToIsland0 = 33; +static const ExtraID k4DMountain0ToDesert0 = 34; +static const ExtraID k4DMountain1ToDesert0 = 35; +static const ExtraID k4DMountain2ToDesert0 = 36; +static const ExtraID kCaBathroomGreeting = 37; +static const ExtraID kCaBathroomBodyFat = 38; +static const ExtraID kCaBathroomStylistIntro = 39; +static const ExtraID kCaBathroomRetrothrash = 40; +static const ExtraID kCaBathroomRetrothrashReturn = 41; +static const ExtraID kCaBathroomGeoWave = 42; +static const ExtraID kCaBathroomGeoWaveReturn = 43; +static const ExtraID kCaBathroomAgencyStandard = 44; +static const ExtraID kCaldoriaShowerTitle = 45; +static const ExtraID kCaldoriaShowerButton = 46; +static const ExtraID kCaldoriaShowerDown = 47; +static const ExtraID kCaldoriaShowerUp = 48; +static const ExtraID kCaBedroomVidPhone = 49; +static const ExtraID kCaBedroomMessage1 = 50; +static const ExtraID kCaBedroomMessage2 = 51; +static const ExtraID kCreateOrangeJuice = 52; +static const ExtraID kDisposeOrangeJuice = 53; +static const ExtraID kReplicatorNorthViewWithOJ = 54; +static const ExtraID kLeftDrawerOpen = 55; +static const ExtraID kLeftDrawerClose = 56; +static const ExtraID kRightDrawerOpenWithKeys = 57; +static const ExtraID kRightDrawerCloseWithKeys = 58; +static const ExtraID kRightDrawerOpenNoKeys = 59; +static const ExtraID kRightDrawerCloseNoKeys = 60; +static const ExtraID kRightDrawerOpenViewWithKeys = 61; +static const ExtraID kRightDrawerOpenViewNoKeys = 62; +static const ExtraID kCaldoria16ElevatorUp = 63; +static const ExtraID kCaldoria16ElevatorDown = 64; +static const ExtraID kCaldoria16SouthViewWithElevator = 65; +static const ExtraID kCaldoria20Doorbell = 66; +static const ExtraID kCaldoria21Doorbell = 67; +static const ExtraID kCaldoria26Doorbell = 68; +static const ExtraID kCaldoriaFourthToGround = 69; +static const ExtraID kCaldoriaRoofToFourth = 70; +static const ExtraID kCaldoriaRoofToGround = 71; +static const ExtraID kCaldoriaGroundToFourth = 72; +static const ExtraID kCaldoriaGroundToRoof = 73; +static const ExtraID kCaldoriaFourthToRoof = 74; +static const ExtraID kCaldoria29Doorbell = 75; +static const ExtraID kCaldoria34Doorbell = 76; +static const ExtraID kCaldoria35Doorbell = 77; +static const ExtraID kBinocularsZoomInOnShip = 78; +static const ExtraID kCaldoriaKioskVideo = 79; +static const ExtraID kCaldoriaTransporterArrowLoop = 80; +static const ExtraID kArriveAtCaldoriaFromTSA = 81; +static const ExtraID kCaGTOtherChoice = 82; +static const ExtraID kCaGTCardSwipe = 83; +static const ExtraID kCaGTSelectTSA = 84; +static const ExtraID kCaGTFryTheFly = 85; +static const ExtraID kCaGTGoToTSA = 86; +static const ExtraID kCaGTSelectBeach = 87; +static const ExtraID kCaGTGoToBeach = 88; +static const ExtraID kCaGTArriveAtBeach = 89; +static const ExtraID kCaGTSelectTokyo = 90; +static const ExtraID kCaGTGoToTokyo = 91; +static const ExtraID kCaGTArriveAtTokyo = 92; +static const ExtraID kCa48NorthRooftopClosed = 93; +static const ExtraID kCa48NorthExplosion = 94; +static const ExtraID kCa48NorthExplosionDeath = 95; +static const ExtraID kCa49NorthVoiceAnalysis = 96; +static const ExtraID kCa50SinclairShoots = 97; +static const ExtraID kCa53EastZoomToSinclair = 98; +static const ExtraID kCa53EastDeath2 = 99; +static const ExtraID kCa53EastShootSinclair = 100; +static const ExtraID kCa53EastZoomOutFromSinclair = 101; +static const ExtraID kCa54SouthDeath = 102; +static const ExtraID kCaldoria56BombStage1 = 103; +static const ExtraID kCaldoria56BombStage2 = 104; +static const ExtraID kCaldoria56BombStage3 = 105; +static const ExtraID kCaldoria56BombStage4 = 106; +static const ExtraID kCaldoria56BombStage5 = 107; +static const ExtraID kCaldoria56BombStage6 = 108; +static const ExtraID kCaldoria56BombStage7 = 109; +static const ExtraID kCaldoria56BombExplodes = 110; + +// Caldoria interactions. + +static const InteractionID kCaldoria4DInteractionID = 0; +static const InteractionID kCaldoriaBombInteractionID = 1; +static const InteractionID kCaldoriaMessagesInteractionID = 2; +static const InteractionID kCaldoriaMirrorInteractionID = 3; + +// Caldoria: + +static const DisplayOrder kVidPhoneOrder = kMonitorLayer; +static const DisplayOrder k4DSpritesOrder = kMonitorLayer; +static const DisplayOrder kCaldoriaMessagesOrder = kMonitorLayer; +static const DisplayOrder kCaldoriaElevatorOrder = kMonitorLayer; +static const DisplayOrder kCaldoriaA05LightLoopOrder = kMonitorLayer; +static const DisplayOrder kCaldoriaA07LightLoopOrder = kMonitorLayer; +static const DisplayOrder kCaldoriaBombGridOrder = kMonitorLayer; +static const DisplayOrder kCaldoriaBombTimerOrder = kCaldoriaBombGridOrder + 1; + +///////////////////////////////////////////// +// +// Caldoria + +static const CoordType kCaldoriaVidPhoneLeft = kNavAreaLeft + 105; +static const CoordType kCaldoriaVidPhoneTop = kNavAreaTop + 28; + +static const CoordType kCaldoria4DSpritesLeft = kNavAreaLeft + 10; +static const CoordType kCaldoria4DSpritesTop = kNavAreaTop + 142; + +static const CoordType kCaldoriaMessageLeft = kNavAreaLeft + 202; +static const CoordType kCaldoriaMessageTop = kNavAreaTop + 26; + +static const CoordType kCaldoriaElevatorLeft = kNavAreaLeft + 407; +static const CoordType kCaldoriaElevatorTop = kNavAreaTop + 138; + +static const CoordType kCaldoriaA05LightLoopLeft = kNavAreaLeft + 213; +static const CoordType kCaldoriaA05LightLoopTop = kNavAreaTop + 215; + +static const CoordType kCaldoriaA07LightLoopLeft = kNavAreaLeft + 414; +static const CoordType kCaldoriaA07LightLoopTop = kNavAreaTop + 215; + +static const CoordType kCaldoriaGunSpriteLeft = kNavAreaLeft + 276; +static const CoordType kCaldoriaGunSpriteTop = kNavAreaTop + 115; + +static const CoordType kCaldoria11MessageLoopLeft = kNavAreaLeft + 135; +static const CoordType kCaldoria11MessageLoopTop = kNavAreaTop + 214; + +static const CoordType kCaldoria12MessageLoopLeft = kNavAreaLeft + 209; +static const CoordType kCaldoria12MessageLoopTop = kNavAreaTop + 170; + +static const CoordType kCaldoria13MessageLoopLeft = kNavAreaLeft + 480; +static const CoordType kCaldoria13MessageLoopTop = kNavAreaTop + 191; + +static const CoordType kCaldoria14MessageLoopLeft = kNavAreaLeft + 248; +static const CoordType kCaldoria14MessageLoopTop = kNavAreaTop + 191; + +static const CoordType kCaldoria48CardBombLoopLeft = kNavAreaLeft + 337; +static const CoordType kCaldoria48CardBombLoopTop = kNavAreaTop + 205; + +static const CoordType kCaldoriaBombGridLeft = kNavAreaLeft + 290; +static const CoordType kCaldoriaBombGridTop = kNavAreaTop + 58; + +static const CoordType kCaldoriaBombTimerLeft = kNavAreaLeft + 58; +static const CoordType kCaldoriaBombTimerTop = kNavAreaTop + 204; + +// Caldoria display IDs. + +static const DisplayElementID kCaldoriaVidPhoneID = kNeighborhoodDisplayID; +static const DisplayElementID kCaldoria4DSpritesID = kCaldoriaVidPhoneID + 1; +static const DisplayElementID kCaldoriaMessagesID = kCaldoria4DSpritesID + 1; +static const DisplayElementID kCaldoriaUtilityID = kCaldoriaMessagesID + 1; +static const DisplayElementID kCaldoriaBombGridID = kCaldoriaUtilityID + 1; +static const DisplayElementID kCaldoriaBombTimerID = kCaldoriaBombGridID + 1; + +static const TimeValue kCaldoria4DBlankChoiceIn = 29730; +static const TimeValue kCaldoria4DBlankChoiceOut = 33910; + +class Caldoria; + +class SinclairCallBack : public TimeBaseCallBack { +public: + SinclairCallBack(Caldoria *); + ~SinclairCallBack() {} + +protected: + virtual void callBack(); + + Caldoria *_caldoria; +}; + +class Caldoria : public Neighborhood { +friend class SinclairCallBack; + +public: + Caldoria(InputHandler *, PegasusEngine *); + virtual ~Caldoria(); + + virtual uint16 getDateResID() const; + + void pickedUpItem(Item *); + + virtual GameInteraction *makeInteraction(const InteractionID); + + virtual Common::String getBriefingMovie(); + virtual Common::String getEnvScanMovie(); + virtual uint getNumHints(); + virtual Common::String getHintMovie(uint); + void loadAmbientLoops(); + bool wantsCursor(); + void flushGameState(); + + void checkContinuePoint(const RoomID, const DirectionConstant); + +protected: + enum { + kCaldoriaPrivate4DSystemOpenFlag, + kCaloriaPrivateLeftDrawerOpenFlag, + kCaldoriaPrivateRightDrawerOpenFlag, + kCaldoriaPrivateReadyToShootFlag, + kCaldoriaPrivateZoomingToBombFlag, + kCaldoriaPrivateCanOpenElevatorDoorFlag, + kCaldoriaPrivateSinclairTimerExpiredFlag, + kCaldoriaPrivateSeen13CarFlag, + kCaldoriaPrivateSeen14CarFlag, + kCaldoriaPrivateSeen18CarFlag, + kCaldoriaPrivateSeen23CarFlag, + kCaldoriaPrivateSeen33CarFlag, + kCaldoriaPrivateSeen36CarFlag, + kCaldoriaPrivateSeen41NorthCarFlag, + kCaldoriaPrivateSeen41EastCarFlag, + kCaldoriaPrivateSeen41WestCarFlag, + kNumCaldoriaPrivateFlags + }; + + void init(); + void start(); + + void setUpRoofTop(); + + void setUpAIRules(); + void doAIRecalibration(); + TimeValue getViewTime(const RoomID, const DirectionConstant); + void findSpotEntry(const RoomID, const DirectionConstant, SpotFlags, SpotTable::Entry &); + void startSpotOnceOnly(TimeValue, TimeValue); + void startExitMovie(const ExitTable::Entry &); + void startZoomMovie(const ZoomTable::Entry &); + void startDoorOpenMovie(const TimeValue, const TimeValue); + void startTurnPush(const TurnDirection, const TimeValue, const DirectionConstant); + void bumpIntoWall(); + int16 getStaticCompassAngle(const RoomID, const DirectionConstant); + void getExitCompassMove(const ExitTable::Entry &, FaderMoveSpec &); + void getZoomCompassMove(const ZoomTable::Entry &, FaderMoveSpec &); + void getExtraCompassMove(const ExtraTable::Entry &, FaderMoveSpec &); + void spotCompleted(); + void arriveAt(const RoomID, const DirectionConstant); + void arriveAtCaldoria00(); + void arriveAtCaldoriaToilet(); + void arriveAtCaldoria44(); + void arriveAtCaldoria49(); + void arriveAtCaldoria56(); + void arriveAtCaldoriaDeath(); + void turnTo(const DirectionConstant); + void zoomTo(const Hotspot *); + void downButton(const Input &); + void receiveNotification(Notification *, const NotificationFlags); + InputBits getInputFilter(); + void activateHotspots(); + void clickInHotspot(const Input &, const Hotspot *); + void newInteraction(const InteractionID); + + void clickOnDoorbell(const HotSpotID); + + Hotspot *getItemScreenSpot(Item *, DisplayElement *); + void dropItemIntoRoom(Item *, Hotspot *); + void takeElevator(uint, uint); + void updateElevatorMovie(); + void openElevatorMovie(); + void emptyOJGlass(); + void closeDoorOffScreen(const RoomID, const DirectionConstant); + void doorBombTimerExpired(); + void sinclairTimerExpired(); + void checkSinclairShootsOS(); + void setUpSinclairLoops(); + void zoomToSinclair(); + void playEndMessage(); + void checkInterruptSinclair(); + + CanOpenDoorReason canOpenDoor(DoorTable::Entry &); + void doorOpened(); + + void updateCursor(const Common::Point, const Hotspot *); + + FlagsArray<uint16, kNumCaldoriaPrivateFlags> _privateFlags; + + const Hotspot *_zoomOutSpot; + + FuseFunction _utilityFuse; + + long _sinclairLoopCount; + long _numSinclairLoops; + + Sprite *_gunSprite; + + SinclairCallBack _sinclairInterrupt; + + Common::String getSoundSpotsName(); + Common::String getNavMovieName(); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/caldoria/caldoria4dsystem.cpp b/engines/pegasus/neighborhood/caldoria/caldoria4dsystem.cpp new file mode 100644 index 0000000000..0494753661 --- /dev/null +++ b/engines/pegasus/neighborhood/caldoria/caldoria4dsystem.cpp @@ -0,0 +1,370 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/neighborhood/caldoria/caldoria.h" +#include "pegasus/neighborhood/caldoria/caldoria4dsystem.h" + +namespace Pegasus { + +static const TimeValue kSwitchableSlop = 3 * kCaldoriaFrameDuration; +// Two seconds - some slop +static const TimeValue kSwitchableDuration = kCaldoriaMovieScale * 2 - kSwitchableSlop; +// Twelve frames + some slop +static const TimeValue kNonswitchableDuration = kCaldoriaFrameDuration * 12 + kSwitchableSlop; + +static const TimeValue kSwitchable1Start = 0; +static const TimeValue kSwitchable1Stop = kSwitchable1Start + kSwitchableDuration; + +static const TimeValue kSwitchable2Start = kSwitchable1Stop + kNonswitchableDuration; +static const TimeValue kSwitchable2Stop = kSwitchable2Start + kSwitchableDuration; + +static const TimeValue kSwitchable3Start = kSwitchable2Stop + kNonswitchableDuration; +static const TimeValue kSwitchable3Stop = kSwitchable3Start + kSwitchableDuration; + +static const NotificationFlags kVidPhoneDoneFlag = 1; + +static const TimeValue kRockMusicLoopIn = 0; +static const TimeValue kRockMusicLoopOut = 2088; + +static const TimeValue kOrchestralMusicLoopIn = 2088; +static const TimeValue kOrchestralMusicLoopOut = 4985; + +static const TimeValue kRhythmsMusicLoopIn = 4985; +static const TimeValue kRhythmsMusicLoopOut = 6824; + +static const TimeValue kAcousticMusicLoopIn = 6824; +static const TimeValue kAcousticMusicLoopOut = 9387; + +enum { + k4DVideoMenu, + k4DAudioMenu, + k4DShuttingDown, + + // These constants are the exact frame numbers of the sprite movie. + k4DRockChoice = 0, + k4DOrchestralChoice, + k4DRhythmsChoice, + k4DAcousticChoice, + k4DIslandChoice, + k4DDesertChoice, + k4DMountainChoice, + + k4DFirstVideoChoice = k4DIslandChoice +}; + +static const ExtraID s_transitionExtras0[3][3] = { + { 0xffffffff, k4DIsland0ToDesert0, k4DIsland0ToMountain0 }, + { k4DDesert0ToIsland0, 0xffffffff, k4DDesert0ToMountain0 }, + { k4DMountain0ToIsland0, k4DMountain0ToDesert0, 0xffffffff } +}; + +static const ExtraID s_transitionExtras1[3][3] = { + { 0xffffffff, k4DIsland1ToDesert0, k4DIsland1ToMountain0 }, + { k4DDesert1ToIsland0, 0xffffffff, k4DDesert1ToMountain0 }, + { k4DMountain1ToIsland0, k4DMountain1ToDesert0, 0xffffffff } +}; + +static const ExtraID s_transitionExtras2[3][3] = { + { 0xffffffff, k4DIsland2ToDesert0, k4DIsland2ToMountain0 }, + { k4DDesert2ToIsland0, 0xffffffff, k4DDesert2ToMountain0 }, + { k4DMountain2ToIsland0, k4DMountain2ToDesert0, 0xffffffff } +}; + +static const ExtraID s_shutDownExtras[3][3] = { + { 0xffffffff, k4DIsland1ToIsland0, k4DIsland2ToIsland0 }, + { k4DDesert0ToIsland0, k4DDesert1ToIsland0, k4DDesert2ToIsland0 }, + { k4DMountain0ToIsland0, k4DMountain1ToIsland0, k4DMountain2ToIsland0 } +}; + +Caldoria4DSystem::Caldoria4DSystem(Neighborhood *owner) : GameInteraction(kCaldoria4DInteractionID, owner), + _4DSpritesMovie(kCaldoria4DSpritesID) { + g_AIArea->lockAIOut(); +} + +Caldoria4DSystem::~Caldoria4DSystem() { + g_AIArea->unlockAI(); +} + +void Caldoria4DSystem::openInteraction() { + _whichMenu = k4DVideoMenu; + _videoChoice = k4DIslandChoice; + _audioChoice = k4DRockChoice; + _clickedHotspotID = kNoHotSpotID; + + _4DSpritesMovie.initFromMovieFile("Images/Caldoria/4D Sprites", true); + _4DSpritesMovie.moveElementTo(kCaldoria4DSpritesLeft, kCaldoria4DSpritesTop); + _4DSpritesMovie.setDisplayOrder(k4DSpritesOrder); + _4DSpritesMovie.startDisplaying(); + _4DSpritesMovie.show(); + + _4DSpritesScale = _4DSpritesMovie.getScale(); + + _neighborhoodNotification = _owner->getNeighborhoodNotification(); + _neighborhoodNotification->notifyMe(this, kExtraCompletedFlag, kExtraCompletedFlag); + + startIdling(); +} + +void Caldoria4DSystem::loopExtra(const ExtraID extraID) { + ExtraTable::Entry extraEntry; + + _owner->getExtraEntry(extraID, extraEntry); + _loopStart = extraEntry.movieStart; + _owner->loopExtraSequence(extraID); +} + +void Caldoria4DSystem::useIdleTime() { + if (_whichMenu == k4DShuttingDown) { + TimeValue movieTime = _owner->getNavMovie()->getTime() - _loopStart; + ExtraID extraID; + + if (movieTime < kSwitchable1Stop) + extraID = s_shutDownExtras[_videoChoice - k4DFirstVideoChoice][0]; + else if (movieTime >= kSwitchable2Start && movieTime < kSwitchable2Stop) + extraID = s_shutDownExtras[_videoChoice - k4DFirstVideoChoice][1]; + else if (movieTime >= kSwitchable3Start && movieTime < kSwitchable3Stop) + extraID = s_shutDownExtras[_videoChoice - k4DFirstVideoChoice][2]; + else + extraID = 0xffffffff; + + if (extraID != 0xffffffff) { + setSpritesMovie(); + _loopStart = 0; + _owner->startExtraSequence(extraID, kExtraCompletedFlag, kFilterNoInput); + } + } else if (_clickedHotspotID != kNoHotSpotID) { + TimeValue movieTime = _owner->getNavMovie()->getTime() - _loopStart; + ExtraID extraID; + + if (movieTime < kSwitchable1Stop) { + extraID = s_transitionExtras0[_videoChoice - k4DFirstVideoChoice][_clickedHotspotID - kCa4DChoice1SpotID]; + _clickedHotspotID = kNoHotSpotID; + } else if (movieTime >= kSwitchable2Start && movieTime < kSwitchable2Stop) { + extraID = s_transitionExtras1[_videoChoice - k4DFirstVideoChoice][_clickedHotspotID - kCa4DChoice1SpotID]; + _clickedHotspotID = kNoHotSpotID; + } else if (movieTime >= kSwitchable3Start && movieTime < kSwitchable3Stop) { + extraID = s_transitionExtras2[_videoChoice - k4DFirstVideoChoice][_clickedHotspotID - kCa4DChoice1SpotID]; + _clickedHotspotID = kNoHotSpotID; + } else + extraID = 0xffffffff; + + if (extraID != 0xffffffff) { + switch (extraID) { + case k4DDesert0ToIsland0: + case k4DMountain0ToIsland0: + case k4DDesert1ToIsland0: + case k4DMountain1ToIsland0: + case k4DDesert2ToIsland0: + case k4DMountain2ToIsland0: + _videoChoice = k4DIslandChoice; + break; + case k4DIsland0ToDesert0: + case k4DMountain0ToDesert0: + case k4DIsland1ToDesert0: + case k4DMountain1ToDesert0: + case k4DIsland2ToDesert0: + case k4DMountain2ToDesert0: + _videoChoice = k4DDesertChoice; + break; + case k4DDesert0ToMountain0: + case k4DIsland0ToMountain0: + case k4DIsland1ToMountain0: + case k4DDesert1ToMountain0: + case k4DIsland2ToMountain0: + case k4DDesert2ToMountain0: + _videoChoice = k4DMountainChoice; + break; + } + + setSpritesMovie(); + _loopStart = 0; + _owner->startExtraSequence(extraID, kExtraCompletedFlag, kFilterNoInput); + } + } +} + +void Caldoria4DSystem::initInteraction() { + setSpritesMovie(); + + _owner->loadLoopSound1("Sounds/Caldoria/Rock.aiff"); + loopExtra(k4DIslandLoop); +} + +void Caldoria4DSystem::closeInteraction() { + stopIdling(); + _neighborhoodNotification->cancelNotification(this); + _4DSpritesMovie.releaseMovie(); + _owner->loadAmbientLoops(); +} + +void Caldoria4DSystem::setSpritesMovie() { + if (_whichMenu == k4DShuttingDown) + _4DSpritesMovie.setTime(_4DSpritesScale * k4DIslandChoice); + else if (_whichMenu == k4DVideoMenu) + _4DSpritesMovie.setTime(_4DSpritesScale * _videoChoice); + else if (_whichMenu == k4DAudioMenu) + _4DSpritesMovie.setTime(_4DSpritesScale * _audioChoice); + + _4DSpritesMovie.redrawMovieWorld(); +} + +void Caldoria4DSystem::handleInput(const Input &input, const Hotspot *cursorSpot) { + if (input.downButtonAnyDown()) + return; + if (input.anyDirectionInput()) + shutDown4DSystem(); + else + GameInteraction::handleInput(input, cursorSpot); +} + +void Caldoria4DSystem::activateHotspots() { + GameInteraction::activateHotspots(); + if (_whichMenu == k4DAudioMenu) + g_allHotspots.activateOneHotspot(kCa4DChoice4SpotID); +} + +void Caldoria4DSystem::clickInHotspot(const Input &input, const Hotspot *spot) { + switch (spot->getObjectID()) { + case kCa4DVisualSpotID: + if (_whichMenu == k4DAudioMenu) { + _whichMenu = k4DVideoMenu; + setSpritesMovie(); + } + break; + case kCa4DAudioSpotID: + if (_whichMenu == k4DVideoMenu) { + _whichMenu = k4DAudioMenu; + setSpritesMovie(); + } + break; + case kCa4DChoice1SpotID: + if (_whichMenu == k4DVideoMenu) + makeIslandChoice(); + else if (_whichMenu == k4DAudioMenu) + makeRockChoice(); + break; + case kCa4DChoice2SpotID: + if (_whichMenu == k4DVideoMenu) + makeDesertChoice(); + else if (_whichMenu == k4DAudioMenu) + makeOrchestralChoice(); + break; + case kCa4DChoice3SpotID: + if (_whichMenu == k4DVideoMenu) + makeMountainChoice(); + else if (_whichMenu == k4DAudioMenu) + makeRhythmsChoice(); + break; + case kCa4DChoice4SpotID: + if (_whichMenu == k4DAudioMenu) + makeAcousticChoice(); + else + _owner->playSpotSoundSync(kCaldoria4DBlankChoiceIn, kCaldoria4DBlankChoiceOut); + break; + default: + GameInteraction::clickInHotspot(input, spot); + } +} + +void Caldoria4DSystem::receiveNotification(Notification *, const NotificationFlags) { + if (_whichMenu == k4DShuttingDown) { + _owner->requestDeleteCurrentInteraction(); + } else { + uint32 extraID; + + switch (_videoChoice) { + case k4DIslandChoice: + extraID = k4DIslandLoop; + break; + case k4DDesertChoice: + extraID = k4DDesertLoop; + break; + case k4DMountainChoice: + extraID = k4DMountainLoop; + break; + default: + extraID = 0xffffffff; + break; + } + + if (extraID != 0xffffffff) + loopExtra(extraID); + } +} + +void Caldoria4DSystem::makeIslandChoice() { + if (_videoChoice != k4DIslandChoice && _clickedHotspotID == kNoHotSpotID) + _clickedHotspotID = kCa4DChoice1SpotID; +} + +void Caldoria4DSystem::makeDesertChoice() { + if (_videoChoice != k4DDesertChoice && _clickedHotspotID == kNoHotSpotID) + _clickedHotspotID = kCa4DChoice2SpotID; +} + +void Caldoria4DSystem::makeMountainChoice() { + if (_videoChoice != k4DMountainChoice && _clickedHotspotID == kNoHotSpotID) + _clickedHotspotID = kCa4DChoice3SpotID; +} + +void Caldoria4DSystem::makeRockChoice() { + if (_audioChoice != k4DRockChoice) { + _audioChoice = k4DRockChoice; + setSpritesMovie(); + _owner->loadLoopSound1("Sounds/Caldoria/Rock.aiff"); + } +} + +void Caldoria4DSystem::makeOrchestralChoice() { + if (_audioChoice != k4DOrchestralChoice) { + _audioChoice = k4DOrchestralChoice; + setSpritesMovie(); + _owner->loadLoopSound1("Sounds/Caldoria/Orchestral.aiff"); + } +} + +void Caldoria4DSystem::makeRhythmsChoice() { + if (_audioChoice != k4DRhythmsChoice) { + _audioChoice = k4DRhythmsChoice; + setSpritesMovie(); + _owner->loadLoopSound1("Sounds/Caldoria/Rhythms.aiff"); + } +} + +void Caldoria4DSystem::makeAcousticChoice() { + if (_audioChoice != k4DAcousticChoice) { + _audioChoice = k4DAcousticChoice; + setSpritesMovie(); + _owner->loadLoopSound1("Sounds/Caldoria/Acoustic.aiff"); + } +} + +void Caldoria4DSystem::shutDown4DSystem() { + _whichMenu = k4DShuttingDown; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/caldoria/caldoria4dsystem.h b/engines/pegasus/neighborhood/caldoria/caldoria4dsystem.h new file mode 100644 index 0000000000..1c5fa44b90 --- /dev/null +++ b/engines/pegasus/neighborhood/caldoria/caldoria4dsystem.h @@ -0,0 +1,78 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_NEIGHBORHOOD_CALDORIA_CALDORIA4DSYSTEM_H +#define PEGASUS_NEIGHBORHOOD_CALDORIA_CALDORIA4DSYSTEM_H + +#include "pegasus/interaction.h" +#include "pegasus/movie.h" +#include "pegasus/notification.h" +#include "pegasus/timers.h" + +namespace Pegasus { + +class Neighborhood; + +class Caldoria4DSystem : public GameInteraction, private Idler, public NotificationReceiver { +public: + Caldoria4DSystem(Neighborhood *); + virtual ~Caldoria4DSystem(); + + void shutDown4DSystem(); + +protected: + void openInteraction(); + void initInteraction(); + void closeInteraction(); + + void handleInput(const Input &, const Hotspot *); + void activateHotspots(); + void clickInHotspot(const Input &, const Hotspot *); + void receiveNotification(Notification *, const NotificationFlags); + void setSpritesMovie(); + void makeIslandChoice(); + void makeRockChoice(); + void makeMountainChoice(); + void makeOrchestralChoice(); + void makeDesertChoice(); + void makeRhythmsChoice(); + void makeAcousticChoice(); + + void useIdleTime(); + void loopExtra(const ExtraID); + + Movie _4DSpritesMovie; + TimeScale _4DSpritesScale; + uint _whichMenu; + uint _videoChoice; + uint _audioChoice; + Notification *_neighborhoodNotification; + TimeValue _loopStart; + HotSpotID _clickedHotspotID; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/caldoria/caldoriabomb.cpp b/engines/pegasus/neighborhood/caldoria/caldoriabomb.cpp new file mode 100644 index 0000000000..abf34d3863 --- /dev/null +++ b/engines/pegasus/neighborhood/caldoria/caldoriabomb.cpp @@ -0,0 +1,1442 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/neighborhood/caldoria/caldoria.h" +#include "pegasus/neighborhood/caldoria/caldoriabomb.h" + +namespace Pegasus { + +// Bomb game PICTs: + +static const uint16 kYellowBombPICTBaseID = 700; +static const uint16 kRedBombPICTBaseID = 709; +static const uint16 kTimerLeftPICTID = 718; +static const uint16 kTimerRightPICTID = 719; + +static const uint32 kFlashOnTime = 20; +static const uint32 kFlashOffTime = 10; + +static const uint32 kOnTime1 = kFlashOnTime; +static const uint32 kOffTime1 = kOnTime1 + kFlashOffTime; +static const uint32 kOnTime2 = kOffTime1 + kFlashOnTime; +static const uint32 kOffTime2 = kOnTime2 + kFlashOffTime; +static const uint32 kOnTime3 = kOffTime2 + kFlashOnTime; +static const uint32 kOffTime3 = kOnTime3 + kFlashOffTime; +static const uint32 kOnTime4 = kOffTime3 + kFlashOnTime; + +static const HotSpotID kVertextHotSpotBaseID = 10000; + +static const CoordType kVertextHotSpotWidth = 24; +static const CoordType kVertextHotSpotHeight = 24; + +static const NotificationFlags kBombTimerExpiredFlag = 1; + +static const VertexType kBombLevelOne[] = { + 0, 1, 0, 1, 0, // hot vertices first. + 1, 1, 0, 1, 1, + 1, 1, 0, 1, 0, + 1, 1, 0, 1, 1, + 0, 1, 0, 1, 0, + + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + + 9, // 9 edges in this level + + kEdgeOneFourth, + 3, + 1, 2, 3, + 0, 0, + + kEdgeOneFourth, + 5, + 5, 6, 7, 8, 9, + 0, 0, 0, 0, + + kEdgeOneFourth, + 4, + 10, 11, 12, 13, + 0, 0, 0, + + kEdgeOneFourth, + 5, + 15, 16, 17, 18, 19, + 0, 0, 0, 0, + + kEdgeOneFourth, + 3, + 21, 22, 23, + 0, 0, + + kEdgeOneHalf, + 3, + 5, 10, 15, + 0, 0, + + kEdgeOneHalf, + 5, + 1, 6, 11, 16, 21, + 0, 0, 0, 0, + + kEdgeOneHalf, + 5, + 3, 8, 13, 18, 23, + 0, 0, 0, 0, + + kEdgeOneHalf, + 3, + 9, 14, 19, + 0, 0 +}; + +static const VertexType kBombLevelTwo[] = { + 0, 1, 0, 1, 0, + 1, 1, 1, 0, 1, + 0, 0, 0, 1, 0, + 1, 1, 1, 0, 1, + 0, 1, 0, 1, 0, + + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + + 15, + + kEdgeOneEighth, + 2, + 5, 1, + 0, + + kEdgeOneEighth, + 3, + 17, 13, 9, + 0, 0, + + kEdgeOneEighth, + 2, + 23, 19, + 0, + + kEdgeThreeEighths, + 2, + 3, 9, + 0, + + kEdgeThreeEighths, + 3, + 7, 13, 19, + 0, 0, + + kEdgeThreeEighths, + 2, + 15, 21, + 0, + + kEdgeOneFourth, + 3, + 1, 2, 3, + 0, 0, + + kEdgeOneFourth, + 4, + 6, 7, 8, 9, + 0, 0, 0, + + kEdgeOneFourth, + 4, + 16, 17, 18, 19, + 0, 0, 0, + + kEdgeOneFourth, + 3, + 21, 22, 23, + 0, 0, + + kEdgeOneHalf, + 3, + 5, 10, 15, + 0, 0, + + kEdgeOneHalf, + 2, + 1, 6, + 0, + + kEdgeOneHalf, + 3, + 7, 12, 17, + 0, 0, + + kEdgeOneHalf, + 3, + 9, 14, 19, + 0, 0, + + kEdgeOneHalf, + 2, + 16, 21, + 0 +}; + +static const VertexType kBombLevelThree[] = { + 0, 1, 0, 1, 0, + 1, 1, 1, 1, 1, + 0, 1, 1, 0, 0, + 1, 1, 1, 1, 1, + 0, 1, 0, 1, 0, + + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + + 22, + + kEdgeThreeSixteenths, + 3, + 15, 12, 9, + 0, 0, + + kEdgeFiveSixteenths, + 3, + 5, 12, 19, + 0, 0, + + kEdgeOneEighth, + 2, + 5, 1, + 0, + + kEdgeOneEighth, + 2, + 7, 3, + 0, + + kEdgeOneEighth, + 2, + 15, 11, + 0, + + kEdgeOneEighth, + 2, + 21, 17, + 0, + + kEdgeOneEighth, + 2, + 23, 19, + 0, + + kEdgeThreeEighths, + 2, + 1, 7, + 0, + + kEdgeThreeEighths, + 2, + 3, 9, + 0, + + kEdgeThreeEighths, + 2, + 5, 11, + 0, + + kEdgeThreeEighths, + 2, + 15, 21, + 0, + + kEdgeThreeEighths, + 2, + 17, 23, + 0, + + kEdgeOneFourth, + 3, + 1, 2, 3, + 0, 0, + + kEdgeOneFourth, + 2, + 5, 6, + 0, + + kEdgeOneFourth, + 2, + 8, 9, + 0, + + kEdgeOneFourth, + 2, + 15, 16, + 0, + + kEdgeOneFourth, + 2, + 18, 19, + 0, + + kEdgeOneFourth, + 3, + 21, 22, 23, + 0, 0, + + kEdgeOneHalf, + 2, + 1, 6, + 0, + + kEdgeOneHalf, + 2, + 3, 8, + 0, + + kEdgeOneHalf, + 2, + 16, 21, + 0, + + kEdgeOneHalf, + 2, + 18, 23, + 0 +}; + +static const VertexType kBombLevelFour[] = { + 1, 1, 1, 1, 0, + 1, 1, 0, 1, 1, + 1, 0, 1, 0, 1, + 1, 1, 0, 1, 1, + 0, 1, 1, 1, 1, + + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + + 19, + + kEdgeOneEighth, + 2, + 5, 1, + 0, + + kEdgeOneEighth, + 3, + 10, 6, 2, + 0, 0, + + kEdgeOneEighth, + 3, + 16, 12, 8, + 0, 0, + + kEdgeOneEighth, + 3, + 22, 18, 14, + 0, 0, + + kEdgeOneEighth, + 2, + 23, 19, + 0, + + kEdgeThreeEighths, + 3, + 2, 8, 14, + 0, 0, + + kEdgeThreeEighths, + 3, + 10, 16, 22, + 0, 0, + + kEdgeOneFourth, + 4, + 0, 1, 2, 3, + 0, 0, 0, + + kEdgeOneFourth, + 2, + 5, 6, + 0, + + kEdgeOneFourth, + 2, + 8, 9, + 0, + + kEdgeOneFourth, + 2, + 15, 16, + 0, + + kEdgeOneFourth, + 2, + 18, 19, + 0, + + kEdgeOneFourth, + 4, + 21, 22, 23, 24, + 0, 0, 0, + + kEdgeOneHalf, + 4, + 0, 5, 10, 15, + 0, 0, 0, + + kEdgeOneHalf, + 2, + 1, 6, + 0, + + kEdgeOneHalf, + 2, + 3, 8, + 0, + + kEdgeOneHalf, + 4, + 9, 14, 19, 24, + 0, 0, 0, + + kEdgeOneHalf, + 2, + 16, 21, + 0, + + kEdgeOneHalf, + 2, + 18, 23, + 0 +}; + +static const VertexType kBombLevelFive[] = { + 0, 1, 0, 1, 0, + 1, 1, 1, 1, 1, + 0, 1, 1, 1, 0, + 1, 1, 1, 1, 1, + 0, 1, 0, 1, 0, + + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + + 19, + + kEdgeOneEighth, + 2, + 5, 1, + 0, + + kEdgeOneEighth, + 2, + 7, 3, + 0, + + kEdgeOneEighth, + 2, + 13, 9, + 0, + + kEdgeOneEighth, + 2, + 15, 11, + 0, + + kEdgeOneEighth, + 2, + 21, 17, + 0, + + kEdgeOneEighth, + 2, + 23, 19, + 0, + + kEdgeThreeEighths, + 2, + 1, 7, + 0, + + kEdgeThreeEighths, + 4, + 5, 11, 17, 23, + 0, 0, 0, + + kEdgeThreeEighths, + 3, + 6, 12, 18, + 0, 0, + + kEdgeThreeEighths, + 2, + 13, 19, + 0, + + kEdgeThreeEighths, + 2, + 15, 21, + 0, + + kEdgeOneFourth, + 5, + 5, 6, 7, 8, 9, + 0, 0, 0, 0, + + kEdgeOneFourth, + 3, + 15, 16, 17, + 0, 0, + + kEdgeOneFourth, + 2, + 18, 19, + 0, + + kEdgeOneFourth, + 3, + 21, 22, 23, + 0, 0, + + kEdgeOneHalf, + 3, + 5, 10, 15, + 0, 0, + + kEdgeOneHalf, + 2, + 1, 6, + 0, + + kEdgeOneHalf, + 3, + 11, 16, 21, + 0, 0, + + kEdgeOneHalf, + 5, + 3, 8, 13, 18, 23, + 0, 0, 0, 0 +}; + +static const VertexType kBombLevelSix[] = { + 0, 1, 1, 1, 0, + 1, 1, 1, 1, 1, + 1, 0, 0, 0, 1, + 1, 1, 1, 1, 1, + 0, 1, 1, 1, 0, + + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + + 25, + + kEdgeOneSixteenth, + 2, + 10, 1, + 0, + + kEdgeOneSixteenth, + 2, + 23, 14, + 0, + + kEdgeSevenSixteenths, + 2, + 3, 14, + 0, + + kEdgeSevenSixteenths, + 2, + 10, 21, + 0, + + kEdgeOneEighth, + 2, + 5, 1, + 0, + + kEdgeOneEighth, + 3, + 10, 6, 2, + 0, 0, + + kEdgeOneEighth, + 2, + 7, 3, + 0, + + kEdgeOneEighth, + 2, + 21, 17, + 0, + + kEdgeOneEighth, + 3, + 22, 18, 14, + 0, 0, + + kEdgeOneEighth, + 2, + 23, 19, + 0, + + kEdgeThreeEighths, + 2, + 1, 7, + 0, + + kEdgeThreeEighths, + 3, + 2, 8, 14, + 0, 0, + + kEdgeThreeEighths, + 2, + 3, 9, + 0, + + kEdgeThreeEighths, + 3, + 10, 16, 22, + 0, 0, + + kEdgeThreeEighths, + 2, + 15, 21, + 0, + + kEdgeThreeEighths, + 2, + 17, 23, + 0, + + kEdgeOneFourth, + 3, + 1, 2, 3, + 0, 0, + + kEdgeOneFourth, + 3, + 6, 7, 8, + 0, 0, + + kEdgeOneFourth, + 3, + 16, 17, 18, + 0, 0, + + kEdgeOneFourth, + 3, + 21, 22, 23, + 0, 0, + + kEdgeOneHalf, + 3, + 5, 10, 15, + 0, 0, + + kEdgeOneHalf, + 3, + 6, 11, 16, + 0, 0, + + kEdgeOneHalf, + 5, + 2, 7, 12, 17, 22, + 0, 0, 0, 0, + + kEdgeOneHalf, + 3, + 8, 13, 18, + 0, 0, + + kEdgeOneHalf, + 3, + 9, 14, 19, + 0, 0 +}; + +static const CoordType kBombGridWidth = 140; +static const CoordType kBombGridHeight = 140; + +static const CoordType kDotOriginX = 0; +static const CoordType kDotOriginY = 0; + +static const CoordType kVertOriginX = 2; +static const CoordType kVertOriginY = 6; + +static const CoordType kHorizOriginX = 6; +static const CoordType kHorizOriginY = 2; + +static const CoordType kDiagOriginX = 6; +static const CoordType kDiagOriginY = 6; + +static const int g_originsX[] = { + kDiagOriginX, + kDiagOriginX, + kDiagOriginX, + kHorizOriginX, + kDiagOriginX, + kDiagOriginX, + kDiagOriginX, + kVertOriginX +}; + +static const int g_originsY[] = { + kDiagOriginY - 64, + kDiagOriginY - 32, + kDiagOriginY - 32, + kHorizOriginY, + kDiagOriginY, + kDiagOriginY, + kDiagOriginY, + kVertOriginY +}; + +struct HotVerticesList { + int numHotVerts; + VertexType hotVerts[25]; +}; + +CoordType vertToX(VertexType vertex) { + return (vertex % 5) * 32; +} + +CoordType vertToY(VertexType vertex) { + return (vertex / 5) * 32; +} + +// This function returns the number of edges in the bomb edge list. +VertexType getNumEdges(BombEdgeList edges) { + return edges[50]; +} + +// These four functions return pointers into the given edge list. + +// getFirstEdge and getNextEdge can be used to iterate across all edges +// in an edge list. These functions can be used to walk all the edges +// in a bomb edge list for drawing. +VertexType *getFirstEdge(BombEdgeList edges) { + return &edges[51]; +} + +VertexType *getNextEdge(VertexType *anEdge) { + return anEdge + *(anEdge + 1) * 2 + 1; +} + +// getVertices returns a pointer to all of the vertices that should are +// hot. These vertices indicate all the vertices that should be drawn in +// the game. +VertexType *getVertices(BombEdgeList edges) { + return &edges[0]; +} + +// getUsedVertices returns a pointer to the "used" vertices area: the +// area that keeps track of which vertices have been set by the +// setVertexUsed used function. +VertexType *getUsedVertices(BombEdgeList edges) { + return &edges[25]; +} + +// Useful for saving. Saving the state of the bomb game is as simple as writing +// out the edge list. +int getEdgeListSize(BombEdgeList edges) { + VertexType numEdges = getNumEdges(edges); + VertexType *anEdge = getFirstEdge(edges); + + while (numEdges--) + anEdge = getNextEdge(anEdge); + + return anEdge - edges + 4; +} + +// Returns true if the given vertex lies on the given edge. +bool vertexOnEdge(VertexType *anEdge, VertexType whichVertex) { + VertexType numVerts = *++anEdge; + + while (numVerts--) + if (*++anEdge == whichVertex) + return true; + + return false; +} + +// Given an edge list and a from vertex, this function constructs a list +// of all vertices that may be clicked on. +// if fromVertex == -1, all vertices are eligible. +// otherwise, only vertices on a line from fromVertex are eligible. +void makeHotVertexList(BombEdgeList edges, VertexType fromVertex, HotVerticesList &hotVertices) { + hotVertices.numHotVerts = 0; + + if (fromVertex == -1) { + for (VertexType i = 0; i < 25; i++) + if (edges[i]) + hotVertices.hotVerts[hotVertices.numHotVerts++] = i; + } else { + VertexType numEdges = getNumEdges(edges); + VertexType *anEdge = getFirstEdge(edges); + hotVertices.hotVerts[hotVertices.numHotVerts++] = fromVertex; + + while (numEdges--) { + if (vertexOnEdge(anEdge, fromVertex)) { + VertexType *p = anEdge + 1; + VertexType numVerts = *p; + + while (numVerts--) + if (*++p != fromVertex) + hotVertices.hotVerts[hotVertices.numHotVerts++] = *p; + } + + anEdge = getNextEdge(anEdge); + } + } +} + +// Set all edges in the edge list to the value passed in "edgeVal". +// For drawing purposes, 0 can mean don't draw, and 1 and higher can +// represent different colors. +void setAllEdgesUsed(BombEdgeList edges, VertexType used) { + VertexType numEdges = getNumEdges(edges); + VertexType *anEdge = getFirstEdge(edges); + + while (numEdges--) { + VertexType *p1 = anEdge + 1; + VertexType numVerts = *p1; + p1 += numVerts + 1; + + while (--numVerts) + *p1++ = used; + + anEdge = getNextEdge(anEdge); + } + + VertexType *p1 = edges; + VertexType *p2 = getUsedVertices(edges); + + for (VertexType i = 0; i < 25; i++, p1++, p2++) + if (*p1) + *p2 = used; +} + +// Same as setAllEdgesUsed, but only affects edges that are already set +// to a non-zero value. +void setAllUsedEdgesUsed(BombEdgeList edges, VertexType used) { + VertexType numEdges = getNumEdges(edges); + VertexType *anEdge = getFirstEdge(edges); + + while (numEdges--) { + VertexType *p = anEdge + 1; + VertexType numVerts = *p; + p += numVerts + 1; + + while (--numVerts) { + if (*p) + *p = used; + ++p; + } + + anEdge = getNextEdge(anEdge); + } + + VertexType *p = getUsedVertices(edges); + for (VertexType i = 0; i < 25; i++, p++) + if (*p) + *p = used; +} + +// Replace all edges with value "value" with the new value "used". +void replaceUsedEdges(BombEdgeList edges, VertexType value, VertexType used) { + VertexType numEdges = getNumEdges(edges); + VertexType *anEdge = getFirstEdge(edges); + + while (numEdges--) { + VertexType *p = anEdge + 1; + VertexType numVerts = *p; + p += numVerts + 1; + + while (--numVerts) { + if (*p == value) + *p = used; + + p++; + } + + anEdge = getNextEdge(anEdge); + } + + VertexType *p = getUsedVertices(edges); + for (VertexType i = 0; i < 25; i++, p++) + if (*p == value) + *p = used; +} + +// Set a vertex's value to "used". +void setVertexUsed(BombEdgeList edges, VertexType whichVertex, VertexType value) { + *(getUsedVertices(edges) + whichVertex) = value; +} + +// Mark an edge in the given list between the two vertices as "used". This marks +// all inbetween vertices as well, even if the vertex is not marked as a "hot" +// vertex in the hot vertex section. Returns true if doing this operation +// crosses an already marked edge. +bool setEdgeUsed(BombEdgeList edges, VertexType fromVertex, VertexType toVertex) { + VertexType numEdges = getNumEdges(edges); + VertexType *anEdge = getFirstEdge(edges); + bool crossed = false; + + while (numEdges--) { + VertexType *p = anEdge; + VertexType numVerts = *++p; + VertexType *fromPtr = 0; + VertexType *toPtr = 0; + VertexType i = numVerts; + p++; + + while (i--) { + if (*p == fromVertex) + fromPtr = p; + else if (*p == toVertex) + toPtr = p; + + if (fromPtr && toPtr) { + // Found the edge... + if (fromPtr > toPtr) { + p = fromPtr; + fromPtr = toPtr; + toPtr = p; + } + + p = fromPtr + numVerts; + + for (i = toPtr - fromPtr; i > 0; i--, p++) { + ++(*p); + + if (*p == 2) + crossed = true; + } + + VertexType *verts = getVertices(edges); + VertexType *usedVerts = getUsedVertices(edges); + *(usedVerts + *fromPtr) = 1; + + for (p = fromPtr + 1; p != toPtr; p++) + if (*(verts + *p)) + *(usedVerts + *p) = 1; + + *(usedVerts + *toPtr) = 1; + return crossed; + } + + p++; + } + + anEdge = getNextEdge(anEdge); + } + + return false; +} + +// Return true if all edges are used. Can be used to determine when the bomb +// game is over. +bool allEdgesUsed(BombEdgeList edges) { + VertexType numEdges = getNumEdges(edges); + VertexType *anEdge = getFirstEdge(edges); + + while (numEdges--) { + VertexType *p = anEdge + 1; + VertexType numVerts = *p; + p += numVerts + 1; + + while (--numVerts) { + if (!*p) + return false; + + ++p; + } + + anEdge = getNextEdge(anEdge); + } + + return true; +} + +BombGrid::BombGrid(const DisplayElementID id) : Picture(id) { + Common::Rect bounds(0, 0, kBombGridWidth, kBombGridHeight); + + allocateSurface(bounds); + setBounds(bounds); + _surface->fillRect(bounds, g_system->getScreenFormat().RGBToColor(0xff, 0xff, 0xff)); + + _transparent = true; + + _yellowDot.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kYellowBombPICTBaseID, true); + _yellowOneSixteenth.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kYellowBombPICTBaseID + 1, true); + _yellowOneEighth.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kYellowBombPICTBaseID + 2, true); + _yellowThreeSixteenths.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kYellowBombPICTBaseID + 3, true); + _yellowOneFourth.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kYellowBombPICTBaseID + 4, true); + _yellowFiveSixteenths.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kYellowBombPICTBaseID + 5, true); + _yellowThreeEighths.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kYellowBombPICTBaseID + 6, true); + _yellowSevenSixteenths.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kYellowBombPICTBaseID + 7, true); + _yellowOneHalf.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kYellowBombPICTBaseID + 8, true); + + _redDot.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kRedBombPICTBaseID, true); + _redOneSixteenth.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kRedBombPICTBaseID + 1, true); + _redOneEighth.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kRedBombPICTBaseID + 2, true); + _redThreeSixteenths.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kRedBombPICTBaseID + 3, true); + _redOneFourth.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kRedBombPICTBaseID + 4, true); + _redFiveSixteenths.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kRedBombPICTBaseID + 5, true); + _redThreeEighths.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kRedBombPICTBaseID + 6, true); + _redSevenSixteenths.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kRedBombPICTBaseID + 7, true); + _redOneHalf.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kRedBombPICTBaseID + 8, true); +} + +void BombGrid::drawEdges(BombEdgeList edges) { + GraphicsManager *gfx = ((PegasusEngine *)g_engine)->_gfx; + gfx->setCurSurface(_surface); + + _surface->fillRect(Common::Rect(0, 0, kBombGridWidth, kBombGridHeight), g_system->getScreenFormat().RGBToColor(0xff, 0xff, 0xff)); + + Frame *yellowStuff = &_yellowDot; + Frame *redStuff = &_redDot; + VertexType numEdges = getNumEdges(edges); + VertexType *anEdge = getFirstEdge(edges); + VertexType i, *p; + + Common::Rect bounds; + getSurfaceBounds(bounds); + + while (numEdges--) { + p = anEdge; + VertexType edgeDirection = *p++; + VertexType numVerts = *p++; + VertexType numSegs = numVerts - 1; + + for (i = 0; i < numSegs; i++, p++) { + if (*(p + numVerts) > 0 && *(p + numVerts) < 4) { + Frame *drawStuff; + + if (*(p + numVerts) == 2) + drawStuff = redStuff; + else + drawStuff = yellowStuff; + + int x = vertToX(*p) + g_originsX[edgeDirection]; + int y = vertToY(*p) + g_originsY[edgeDirection]; + + Common::Rect r1; + drawStuff[edgeDirection + 1].getSurfaceBounds(r1); + Common::Rect r2 = r1; + r2.moveTo(x, y); + drawStuff[edgeDirection + 1].drawImage(r1, r2); + } + } + + anEdge = getNextEdge(anEdge); + } + + for (i = 0, p = getUsedVertices(edges); i < 25; i++, p++) { + if (*p > 0 && *p < 4) { + Frame *drawStuff; + + if (*p == 2) + drawStuff = redStuff; + else + drawStuff = yellowStuff; + + int x = vertToX(i) + kDotOriginX; + int y = vertToY(i) + kDotOriginY; + + Common::Rect r1; + drawStuff->getSurfaceBounds(r1); + Common::Rect r2 = r1; + r2.moveTo(x, y); + drawStuff->drawImage(r1, r2); + } + } + + triggerRedraw(); + gfx->setCurSurface(gfx->getWorkArea()); +} + +BombTimer::BombTimer(const DisplayElementID id) : IdlerAnimation(id) { + _middle = -1; + _leftImage.getImageFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kTimerLeftPICTID); + _rightImage.getImageFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kTimerRightPICTID); + + Common::Rect r; + _leftImage.getSurfaceBounds(r); + setBounds(r); +} + +void BombTimer::draw(const Common::Rect &updateRect) { + Common::Rect bounds; + getBounds(bounds); + + Common::Rect r1 = bounds; + r1.right = _middle; + r1 = r1.findIntersectingRect(updateRect); + + if (!r1.isEmpty()) { + Common::Rect r2 = r1; + r2.moveTo(r1.left - bounds.left, r1.top - bounds.top); + _leftImage.copyToCurrentPort(r2, r1); + } + + r1 = bounds; + r1.left = _middle; + r1 = r1.findIntersectingRect(updateRect); + + if (!r1.isEmpty()) { + Common::Rect r2 = r1; + r2.moveTo(r1.left - bounds.left, r1.top - bounds.top); + _rightImage.copyToCurrentPort(r2, r1); + } +} + +void BombTimer::timeChanged(const TimeValue newTime) { + Common::Rect bounds; + getBounds(bounds); + + int newMiddle = bounds.right - bounds.width() * newTime / getDuration(); + if (newMiddle != _middle) { + _middle = newMiddle; + triggerRedraw(); + } +} + +#define CREATE_BOMB_LEVEL(num, data) \ + _bombLevel[num] = new VertexType[sizeof(data)]; \ + memcpy(_bombLevel[num], data, sizeof(data)) + +CaldoriaBomb::CaldoriaBomb(Neighborhood *owner, NotificationManager *manager) : + GameInteraction(kCaldoriaBombInteractionID, owner), _grid(kCaldoriaBombGridID), + _timer(kCaldoriaBombTimerID), _timerNotification(kCaldoriaBombTimerNotificationID, manager) { + CREATE_BOMB_LEVEL(0, kBombLevelOne); + CREATE_BOMB_LEVEL(1, kBombLevelTwo); + CREATE_BOMB_LEVEL(2, kBombLevelThree); + CREATE_BOMB_LEVEL(3, kBombLevelFour); + CREATE_BOMB_LEVEL(4, kBombLevelFive); + CREATE_BOMB_LEVEL(5, kBombLevelSix); + _currentLevel = 0; +} + +#undef CREATE_BOMB_LEVEL + +CaldoriaBomb::~CaldoriaBomb() { + for (int i = 0; i < 6; i++) + delete[] _bombLevel[i]; +} + +void CaldoriaBomb::openInteraction() { + _grid.moveElementTo(kCaldoriaBombGridLeft, kCaldoriaBombGridTop); + _grid.setDisplayOrder(kCaldoriaBombGridOrder); + _grid.startDisplaying(); + + _timer.moveElementTo(kCaldoriaBombTimerLeft, kCaldoriaBombTimerTop); + _timer.setDisplayOrder(kCaldoriaBombTimerOrder); + _timer.startDisplaying(); + _timer.setSegment(0, kTenMinutesPerFifteenTicks, kFifteenTicksPerSecond); + _timer.setTime(0); + + _timerNotification.notifyMe(this, kBombTimerExpiredFlag, kBombTimerExpiredFlag); + _timerCallBack.setNotification(&_timerNotification); + _timerCallBack.initCallBack(&_timer, kCallBackAtExtremes); + _timerCallBack.setCallBackFlag(kBombTimerExpiredFlag); + + Common::Rect r(0, 0, kVertextHotSpotWidth, kVertextHotSpotHeight); + + for (VertexType i = 0; i < 25; i++) { + _vertexHotspot[i] = new Hotspot(i + kVertextHotSpotBaseID); + r.moveTo(vertToX(i) + kCaldoriaBombGridLeft - kVertextHotSpotWidth / 2 + 6, + vertToY(i) + kCaldoriaBombGridTop - kVertextHotSpotHeight / 2 + 6); + _vertexHotspot[i]->setArea(r); + _vertexHotspot[i]->setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag); + g_allHotspots.push_back(_vertexHotspot[i]); + } + + _neighborhoodNotification = _owner->getNeighborhoodNotification(); + _neighborhoodNotification->notifyMe(this, kExtraCompletedFlag, kExtraCompletedFlag); +} + +void CaldoriaBomb::initInteraction() { + _owner->loadLoopSound1(""); + _owner->startExtraSequence(kCaldoria56BombStage1, kExtraCompletedFlag, kFilterNoInput); +} + +void CaldoriaBomb::closeInteraction() { + _timer.stop(); + _timer.hide(); + _timer.stopDisplaying(); + _grid.hide(); + _grid.stopDisplaying(); + + // The original did not do this, but we need it here + // Not sure why the original worked without this; probably + // related to the way the List code worked in CodeWarrior. + // If this is not here, the notifications will later attempt + // to remove itself from this receiver causing a very nasty + // crash. + _timerNotification.cancelNotification(this); + _neighborhoodNotification->cancelNotification(this); +} + +void CaldoriaBomb::startBombAmbient(Common::String ambient) { + _owner->loadLoopSound1(ambient); +} + +void CaldoriaBomb::receiveNotification(Notification *notification, const NotificationFlags) { + if (notification == _neighborhoodNotification) { + switch (_owner->getLastExtra()) { + case kCaldoria56BombStage1: + _grid.show(); + _timer.show(); + _timerCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + _timer.start(); + _currentLevel = 0; + _lastVertex = -1; + startBombAmbient("Sounds/Caldoria/BmbLoop1.22K.AIFF"); + break; + case kCaldoria56BombStage2: + case kCaldoria56BombStage3: + case kCaldoria56BombStage4: + case kCaldoria56BombStage5: + case kCaldoria56BombStage6: + _grid.show(); + _currentLevel++; + _grid.drawEdges(_bombLevel[_currentLevel]); + _lastVertex = -1; + startBombAmbient(Common::String::format("Sounds/Caldoria/BmbLoop%d.22K.AIFF", _owner->getLastExtra() - kCaldoria56BombStage1 + 1)); + break; + case kCaldoria56BombStage7: + _owner->requestDeleteCurrentInteraction(); + GameState.setCaldoriaBombDisarmed(true); + GameState.setScoringDisarmedNuke(true); + _owner->loadAmbientLoops(); + break; + } + } else if (notification == &_timerNotification) { + _grid.hide(); + _timer.stop(); + _timer.hide(); + _owner->loadLoopSound1(""); + _owner->playDeathExtra(kCaldoria56BombExplodes, kDeathNuclearExplosion); + } +} + +void CaldoriaBomb::activateHotspots() { + GameInteraction::activateHotspots(); + + if (_currentLevel != -1 && _lastVertex >= -1) { + HotVerticesList hotVertices; + makeHotVertexList(_bombLevel[_currentLevel], _lastVertex, hotVertices); + + for (VertexType i = 0; i < hotVertices.numHotVerts; i++) + g_allHotspots.activateOneHotspot(hotVertices.hotVerts[i] + kVertextHotSpotBaseID); + } +} + +void CaldoriaBomb::clickInHotspot(const Input &input, const Hotspot *hotspot) { + int clickedVertex = (int)hotspot->getObjectID() - (int)kVertextHotSpotBaseID; + + if (clickedVertex >= 0 && clickedVertex < 25) { + if (_lastVertex != -1 && setEdgeUsed(_bombLevel[_currentLevel], _lastVertex, clickedVertex)) { + clickedVertex = -2; + _flashTime = tickCount(); + } else if (allEdgesUsed(_bombLevel[_currentLevel])) { + setVertexUsed(_bombLevel[_currentLevel], clickedVertex, 1); + clickedVertex = -20; + _flashTime = tickCount(); + } else { + setVertexUsed(_bombLevel[_currentLevel], clickedVertex, 2); + } + + _grid.drawEdges(_bombLevel[_currentLevel]); + _lastVertex = clickedVertex; + } else { + GameInteraction::clickInHotspot(input, hotspot); + } +} + +InputBits CaldoriaBomb::getInputFilter() { + // Disallow arrow buttons. + return GameInteraction::getInputFilter() & kFilterAllButtons; +} + +void CaldoriaBomb::handleInput(const Input &input, const Hotspot *hotspot) { + GameInteraction::handleInput(input, hotspot); + + switch (_lastVertex) { + case -2: // Flash back to yellow. + if (tickCount() > _flashTime + kOnTime1) { + replaceUsedEdges(_bombLevel[_currentLevel], 2, 3); + _grid.drawEdges(_bombLevel[_currentLevel]); + _lastVertex = -3; + } + break; + case -3: // Flash back to red. + if (tickCount() > _flashTime + kOffTime1) { + replaceUsedEdges(_bombLevel[_currentLevel], 3, 2); + _grid.drawEdges(_bombLevel[_currentLevel]); + _lastVertex = -4; + } + break; + case -4: // Flash all to yellow. + if (tickCount() > _flashTime + kOnTime2) { + setAllUsedEdgesUsed(_bombLevel[_currentLevel], 1); + _grid.drawEdges(_bombLevel[_currentLevel]); + _lastVertex = -5; + } + break; + case -5: // Flash all to red. + if (tickCount() > _flashTime + kOffTime2) { + setAllUsedEdgesUsed(_bombLevel[_currentLevel], 2); + _grid.drawEdges(_bombLevel[_currentLevel]); + _lastVertex = -6; + } + break; + case -6: // Flash all to yellow. + if (tickCount() > _flashTime + kOnTime3) { + setAllUsedEdgesUsed(_bombLevel[_currentLevel], 1); + _grid.drawEdges(_bombLevel[_currentLevel]); + _lastVertex = -7; + } + break; + case -7: // Flash all to red. + if (tickCount() > _flashTime + kOffTime3) { + setAllUsedEdgesUsed(_bombLevel[_currentLevel], 2); + _grid.drawEdges(_bombLevel[_currentLevel]); + _lastVertex = -8; + } + break; + case -8: // Restore to normal. + if (tickCount() > _flashTime + kOnTime4) { + setAllEdgesUsed(_bombLevel[_currentLevel], 0); + _grid.drawEdges(_bombLevel[_currentLevel]); + _lastVertex = -1; + } + break; + + // Flash grid after success. + case -20: // Flash off. + if (tickCount() > _flashTime + kOnTime1) { + setAllEdgesUsed(_bombLevel[_currentLevel], 4); + _grid.drawEdges(_bombLevel[_currentLevel]); + _lastVertex = -21; + } + break; + case -21: // Flash on. + if (tickCount() > _flashTime + kOffTime1) { + setAllEdgesUsed(_bombLevel[_currentLevel], 1); + _grid.drawEdges(_bombLevel[_currentLevel]); + _lastVertex = -22; + } + break; + case -22: // Flash off. + if (tickCount() > _flashTime + kOnTime2) { + setAllEdgesUsed(_bombLevel[_currentLevel], 4); + _grid.drawEdges(_bombLevel[_currentLevel]); + _lastVertex = -23; + } + break; + case -23: // Flash on. + if (tickCount() > _flashTime + kOffTime2) { + setAllEdgesUsed(_bombLevel[_currentLevel], 1); + _grid.drawEdges(_bombLevel[_currentLevel]); + _lastVertex = -24; + } + break; + case -24: + if (tickCount() > _flashTime + kOnTime3) { + _grid.hide(); + _lastVertex = -1; + _owner->loadLoopSound1(""); + + switch (_currentLevel) { + case 0: + _owner->startExtraSequence(kCaldoria56BombStage2, kExtraCompletedFlag, kFilterNoInput); + break; + case 1: + _owner->startExtraSequence(kCaldoria56BombStage3, kExtraCompletedFlag, kFilterNoInput); + break; + case 2: + _owner->startExtraSequence(kCaldoria56BombStage4, kExtraCompletedFlag, kFilterNoInput); + break; + case 3: + _owner->startExtraSequence(kCaldoria56BombStage5, kExtraCompletedFlag, kFilterNoInput); + break; + case 4: + _owner->startExtraSequence(kCaldoria56BombStage6, kExtraCompletedFlag, kFilterNoInput); + break; + case 5: + _timer.stop(); + _grid.hide(); + _timer.hide(); + _owner->startExtraSequence(kCaldoria56BombStage7, kExtraCompletedFlag, kFilterNoInput); + break; + } + } + break; + } +} + +long CaldoriaBomb::getNumHints() { + return 2; +} + +Common::String CaldoriaBomb::getHintMovie(uint hintNum) { + return (hintNum == 1) ? "Images/AI/Caldoria/X56EH2" : "Images/AI/Caldoria/X56EH3"; +} + +bool CaldoriaBomb::canSolve() { + return true; +} + +void CaldoriaBomb::doSolve() { + _timer.stop(); + _grid.hide(); + _timer.hide(); + _owner->loadLoopSound1(""); + _owner->startExtraSequence(kCaldoria56BombStage7, kExtraCompletedFlag, kFilterNoInput); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/caldoria/caldoriabomb.h b/engines/pegasus/neighborhood/caldoria/caldoriabomb.h new file mode 100644 index 0000000000..5bb39b4122 --- /dev/null +++ b/engines/pegasus/neighborhood/caldoria/caldoriabomb.h @@ -0,0 +1,156 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_NEIGHBORHOOD_CALDORIA_CALDORIABOMB_H +#define PEGASUS_NEIGHBORHOOD_CALDORIA_CALDORIABOMB_H + +#include "pegasus/interaction.h" +#include "pegasus/notification.h" +#include "pegasus/surface.h" + +namespace Pegasus { + +/* + Edge list is arranged as follows: + + all values in the edge list are bytes. + + all vertices are numbers between 0 and 24. x coordinate of vertex is vertex % 5, + and y coordinate is vertex / 5. + + an edge is + a direction code + a number of vertices in the edge + an array of vertices -- all vertices along the edge, whether or not they're + clickable. + an array of bools (bytes) indicating that a portion of the edge is + traversed (and should be drawn). the number of bools is one less than + the number of vertices. + + an edge list is + an array of 25 bools indicating which vertex is clickable. + an array of 25 bools indicating which vertex is used (drawn). + a number of edges + an array of edges. + + a hot vertex list is + a number of vertices + an array of 25 vertices + +*/ + +typedef int8 VertexType; +typedef VertexType *BombEdgeList; + +static const VertexType kEdgeOneSixteenth = 0; +static const VertexType kEdgeOneEighth = 1; +static const VertexType kEdgeThreeSixteenths = 2; +static const VertexType kEdgeOneFourth = 3; +static const VertexType kEdgeFiveSixteenths = 4; +static const VertexType kEdgeThreeEighths = 5; +static const VertexType kEdgeSevenSixteenths = 6; +static const VertexType kEdgeOneHalf = 7; + +class BombTimer : public IdlerAnimation { +public: + BombTimer(const DisplayElementID); + virtual ~BombTimer() {} + + void draw(const Common::Rect &); + +protected: + void timeChanged(const TimeValue); + + int _middle; + Surface _leftImage, _rightImage; +}; + +class BombGrid : public Picture { +public: + BombGrid(const DisplayElementID); + virtual ~BombGrid() {} + + void drawEdges(BombEdgeList); + +protected: + Frame _yellowDot; + Frame _yellowOneSixteenth; + Frame _yellowOneEighth; + Frame _yellowThreeSixteenths; + Frame _yellowOneFourth; + Frame _yellowFiveSixteenths; + Frame _yellowThreeEighths; + Frame _yellowSevenSixteenths; + Frame _yellowOneHalf; + Frame _redDot; + Frame _redOneSixteenth; + Frame _redOneEighth; + Frame _redThreeSixteenths; + Frame _redOneFourth; + Frame _redFiveSixteenths; + Frame _redThreeEighths; + Frame _redSevenSixteenths; + Frame _redOneHalf; +}; + +class Hotspot; + +class CaldoriaBomb : public GameInteraction, public NotificationReceiver { +public: + CaldoriaBomb(Neighborhood *, NotificationManager *); + virtual ~CaldoriaBomb(); + + long getNumHints(); + Common::String getHintMovie(uint); + void doSolve(); + bool canSolve(); + +protected: + void openInteraction(); + void initInteraction(); + void closeInteraction(); + void receiveNotification(Notification *, const NotificationFlags); + void activateHotspots(); + void clickInHotspot(const Input &, const Hotspot *); + void handleInput(const Input &, const Hotspot *); + InputBits getInputFilter(); + void startBombAmbient(Common::String); + + Notification *_neighborhoodNotification; + BombGrid _grid; + BombTimer _timer; + BombEdgeList _bombLevel[6]; + int _currentLevel, _flashTime; + Hotspot *_vertexHotspot[25]; + VertexType _lastVertex; + Notification _timerNotification; + NotificationCallBack _timerCallBack; + + TimeValue _readTime; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/caldoria/caldoriamessages.cpp b/engines/pegasus/neighborhood/caldoria/caldoriamessages.cpp new file mode 100644 index 0000000000..a3ce97d438 --- /dev/null +++ b/engines/pegasus/neighborhood/caldoria/caldoriamessages.cpp @@ -0,0 +1,115 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/gamestate.h" +#include "pegasus/neighborhood/neighborhood.h" +#include "pegasus/neighborhood/caldoria/caldoria.h" +#include "pegasus/neighborhood/caldoria/caldoriamessages.h" + +namespace Pegasus { + +static const NotificationFlags kMessageDoneFlag = 1; + +CaldoriaMessages::CaldoriaMessages(Neighborhood *owner, const NotificationID id, NotificationManager *manager) : + GameInteraction(kCaldoriaMessagesInteractionID, owner), Notification(id, manager), _messageMovie(kCaldoriaMessagesID) { +} + +void CaldoriaMessages::openInteraction() { + _neighborhoodNotification = GameInteraction::_owner->getNeighborhoodNotification(); + _neighborhoodNotification->notifyMe(this, kExtraCompletedFlag, kExtraCompletedFlag); + _messageCallBack.setNotification(this); + notifyMe(this, kMessageDoneFlag, kMessageDoneFlag); + _messageCallBack.setCallBackFlag(kMessageDoneFlag); + _messageNumber = 1; +} + +void CaldoriaMessages::initInteraction() { + GameInteraction::_owner->startExtraSequence(kCaBedroomVidPhone, kExtraCompletedFlag, kFilterNoInput); +} + +void CaldoriaMessages::closeInteraction() { + cancelNotification(this); + _neighborhoodNotification->cancelNotification(this); +} + +void CaldoriaMessages::receiveNotification(Notification *notification, const NotificationFlags) { + if (notification == _neighborhoodNotification) { + switch (GameInteraction::_owner->getLastExtra()) { + case kCaBedroomVidPhone: + GameInteraction::_owner->showExtraView(kCaBedroomMessage1); + break; + case kCaBedroomMessage1: + play1Message(1); + break; + case kCaBedroomMessage2: + play1Message(2); + break; + } + } else { + _messageCallBack.releaseCallBack(); + _messageMovie.releaseMovie(); + + uint32 extraID = (_messageNumber == 1) ? kCaBedroomMessage1 : kCaBedroomMessage2; + GameInteraction::_owner->showExtraView(extraID); + allowInput(true); + } +} + +void CaldoriaMessages::clickInHotspot(const Input &input, const Hotspot *spot) { + uint32 extraID; + + switch (spot->getObjectID()) { + case kCaBedroomVidPhoneActivationSpotID: + extraID = (_messageNumber == 1) ? kCaBedroomMessage1 : kCaBedroomMessage2; + GameInteraction::_owner->startExtraSequence(extraID, kExtraCompletedFlag, kFilterNoInput); + break; + default: + GameInteraction::clickInHotspot(input, spot); + break; + } +} + +void CaldoriaMessages::play1Message(uint messageNumber) { + if (messageNumber == 1) { + _messageMovie.initFromMovieFile("Images/Caldoria/A12NVA.movie"); + _messageNumber = 2; + } else { + _messageMovie.initFromMovieFile("Images/Caldoria/A12NVB.movie"); + _messageNumber = 1; + GameState.setCaldoriaSeenMessages(true); + } + + _messageMovie.moveElementTo(kCaldoriaMessageLeft, kCaldoriaMessageTop); + _messageMovie.setDisplayOrder(kCaldoriaMessagesOrder); + _messageMovie.startDisplaying(); + _messageCallBack.initCallBack(&_messageMovie, kCallBackAtExtremes); + _messageCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + allowInput(false); + _messageMovie.show(); + _messageMovie.redrawMovieWorld(); + _messageMovie.start(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/caldoria/caldoriamessages.h b/engines/pegasus/neighborhood/caldoria/caldoriamessages.h new file mode 100644 index 0000000000..955fe10ce9 --- /dev/null +++ b/engines/pegasus/neighborhood/caldoria/caldoriamessages.h @@ -0,0 +1,60 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_NEIGHBORHOOD_CALDORIA_CALDORIAMESSAGES_H +#define PEGASUS_NEIGHBORHOOD_CALDORIA_CALDORIAMESSAGES_H + +#include "pegasus/input.h" +#include "pegasus/interaction.h" +#include "pegasus/movie.h" +#include "pegasus/notification.h" +#include "pegasus/timers.h" + +namespace Pegasus { + +class Neighborhood; + +class CaldoriaMessages : public GameInteraction, public Notification, public NotificationReceiver { +public: + CaldoriaMessages(Neighborhood *, const NotificationID, NotificationManager *); + virtual ~CaldoriaMessages() {} + +protected: + void openInteraction(); + void initInteraction(); + void closeInteraction(); + void receiveNotification(Notification *, const NotificationFlags); + void clickInHotspot(const Input &, const Hotspot *); + void play1Message(uint); + + Movie _messageMovie; + NotificationCallBack _messageCallBack; + Notification *_neighborhoodNotification; + uint _messageNumber; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/caldoria/caldoriamirror.cpp b/engines/pegasus/neighborhood/caldoria/caldoriamirror.cpp new file mode 100644 index 0000000000..ff4d1811d0 --- /dev/null +++ b/engines/pegasus/neighborhood/caldoria/caldoriamirror.cpp @@ -0,0 +1,135 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/neighborhood/neighborhood.h" +#include "pegasus/neighborhood/caldoria/caldoria.h" +#include "pegasus/neighborhood/caldoria/caldoriamirror.h" + +namespace Pegasus { + +CaldoriaMirror::CaldoriaMirror(Neighborhood *owner) : GameInteraction(kCaldoriaMirrorInteractionID, owner) { +} + +void CaldoriaMirror::openInteraction() { + _neighborhoodNotification = _owner->getNeighborhoodNotification(); + _neighborhoodNotification->notifyMe(this, kExtraCompletedFlag, kExtraCompletedFlag); +} + +void CaldoriaMirror::initInteraction() { + _owner->setCurrentActivation(kActivateMirrorReady); + _owner->startExtraSequence(kCaBathroomGreeting, kExtraCompletedFlag, kFilterNoInput); +} + +void CaldoriaMirror::closeInteraction() { + _neighborhoodNotification->cancelNotification(this); +} + +void CaldoriaMirror::handleInput(const Input &input, const Hotspot *cursorSpot) { + if (_owner->getLastExtra() == (uint32)kCaBathroomAgencyStandard || !input.anyDirectionInput()) + GameInteraction::handleInput(input, cursorSpot); +} + +void CaldoriaMirror::activateHotspots() { + GameInteraction::activateHotspots(); + + switch (_owner->getLastExtra()) { + case kCaBathroomGreeting: + case kCaBathroomBodyFat: + case kCaBathroomRetrothrash: + case kCaBathroomGeoWave: + g_allHotspots.activateOneHotspot(kCaBathroomMirrorSpotID); + g_allHotspots.deactivateOneHotspot(kCaHairStyle1SpotID); + g_allHotspots.deactivateOneHotspot(kCaHairStyle2SpotID); + g_allHotspots.deactivateOneHotspot(kCaHairStyle3SpotID); + break; + case kCaBathroomStylistIntro: + case kCaBathroomRetrothrashReturn: + case kCaBathroomGeoWaveReturn: + g_allHotspots.activateOneHotspot(kCaHairStyle1SpotID); + g_allHotspots.activateOneHotspot(kCaHairStyle2SpotID); + g_allHotspots.activateOneHotspot(kCaHairStyle3SpotID); + g_allHotspots.deactivateOneHotspot(kCaBathroomMirrorSpotID); + break; + } +} + +void CaldoriaMirror::clickInHotspot(const Input &input, const Hotspot *spot) { + switch (spot->getObjectID()) { + case kCaBathroomMirrorSpotID: + switch (_owner->getLastExtra()) { + case kCaBathroomGreeting: + _owner->startExtraSequence(kCaBathroomBodyFat, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaBathroomBodyFat: + _owner->startExtraSequence(kCaBathroomStylistIntro, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaBathroomRetrothrash: + _owner->startExtraSequence(kCaBathroomRetrothrashReturn, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaBathroomGeoWave: + _owner->startExtraSequence(kCaBathroomGeoWaveReturn, kExtraCompletedFlag, kFilterNoInput); + break; + } + break; + case kCaHairStyle1SpotID: + _owner->startExtraSequence(kCaBathroomRetrothrash, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaHairStyle2SpotID: + _owner->startExtraSequence(kCaBathroomAgencyStandard, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaHairStyle3SpotID: + _owner->startExtraSequence(kCaBathroomGeoWave, kExtraCompletedFlag, kFilterNoInput); + break; + default: + GameInteraction::clickInHotspot(input, spot); + break; + } +} + +void CaldoriaMirror::receiveNotification(Notification *, const NotificationFlags) { + switch (_owner->getLastExtra()) { + case kCaBathroomRetrothrash: + case kCaBathroomGeoWave: + _owner->setCurrentActivation(kActivateMirrorReady); + break; + case kCaBathroomStylistIntro: + case kCaBathroomRetrothrashReturn: + case kCaBathroomGeoWaveReturn: + _owner->setCurrentActivation(kActivateStylistReady); + break; + case kCaBathroomAgencyStandard: + _owner->setCurrentActivation(kActivateHotSpotAlways); + _owner->requestDeleteCurrentInteraction(); + GameState.setScoringFixedHair(true); + GameState.setCaldoriaDoneHygiene(true); + break; + } + + allowInput(true); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/caldoria/caldoriamirror.h b/engines/pegasus/neighborhood/caldoria/caldoriamirror.h new file mode 100644 index 0000000000..1ca47ec774 --- /dev/null +++ b/engines/pegasus/neighborhood/caldoria/caldoriamirror.h @@ -0,0 +1,54 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_NEIGHBORHOOD_CALDORIA_CALDORIAMIRROR_H +#define PEGASUS_NEIGHBORHOOD_CALDORIA_CALDORIAMIRROR_H + +#include "pegasus/interaction.h" +#include "pegasus/notification.h" + +namespace Pegasus { + +class CaldoriaMirror : public GameInteraction, public NotificationReceiver { +public: + CaldoriaMirror(Neighborhood *); + virtual ~CaldoriaMirror() {} + +protected: + void openInteraction(); + void initInteraction(); + void closeInteraction(); + + void handleInput(const Input &, const Hotspot *); + void activateHotspots(); + void clickInHotspot(const Input &, const Hotspot *); + void receiveNotification(Notification *, const NotificationFlags); + + Notification *_neighborhoodNotification; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/door.cpp b/engines/pegasus/neighborhood/door.cpp new file mode 100644 index 0000000000..f7ec7559fc --- /dev/null +++ b/engines/pegasus/neighborhood/door.cpp @@ -0,0 +1,64 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "common/debug.h" +#include "common/stream.h" +#include "common/textconsole.h" + +#include "pegasus/neighborhood/door.h" + +namespace Pegasus { + +void DoorTable::loadFromStream(Common::SeekableReadStream *stream) { + uint32 count = stream->readUint32BE(); + _entries.resize(count); + + for (uint32 i = 0; i < count; i++) { + _entries[i].room = stream->readUint16BE(); + _entries[i].direction = stream->readByte(); + _entries[i].altCode = stream->readByte(); + _entries[i].movieStart = stream->readUint32BE(); + _entries[i].movieEnd = stream->readUint32BE(); + _entries[i].flags = stream->readByte(); + stream->readByte(); // alignment + debug(0, "Door[%d]: %d %d %d %d %d %d", i, _entries[i].room, _entries[i].direction, + _entries[i].altCode, _entries[i].movieStart, _entries[i].movieEnd, + _entries[i].flags); + } +} + +void DoorTable::clear() { + _entries.clear(); +} + +DoorTable::Entry DoorTable::findEntry(RoomID room, DirectionConstant direction, AlternateID altCode) { + for (uint32 i = 0; i < _entries.size(); i++) + if (_entries[i].room == room && _entries[i].direction == direction && _entries[i].altCode == altCode) + return _entries[i]; + + return Entry(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/door.h b/engines/pegasus/neighborhood/door.h new file mode 100644 index 0000000000..8ea757559a --- /dev/null +++ b/engines/pegasus/neighborhood/door.h @@ -0,0 +1,90 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_NEIGHBORHOOD_DOOR_H +#define PEGASUS_NEIGHBORHOOD_DOOR_H + +#include "common/array.h" +#include "common/endian.h" + +#include "pegasus/constants.h" + +namespace Common { + class SeekableReadStream; +} + +namespace Pegasus { + +typedef byte DoorFlags; + +enum { + kDoorPresentBit, // Bit set if there is a door here. + kDoorLockedBit // Bit set if door is locked, clear if unlocked. +}; + +static const DoorFlags kNoDoorFlags = 0; +static const DoorFlags kDoorPresentMask = 1 << kDoorPresentBit; +static const DoorFlags kDoorLockedMask = 1 << kDoorLockedBit; + +class DoorTable { +public: + DoorTable() {} + ~DoorTable() {} + + static uint32 getResTag() { return MKTAG('D', 'o', 'o', 'r'); } + + void loadFromStream(Common::SeekableReadStream *stream); + void clear(); + + struct Entry { + Entry() { clear(); } + bool isEmpty() { return movieStart == 0xffffffff; } + void clear() { + room = kNoRoomID; + direction = kNoDirection; + altCode = kNoAlternateID; + movieStart = 0xffffffff; + movieEnd = 0xffffffff; + flags = kNoDoorFlags; + } + + RoomID room; + DirectionConstant direction; + AlternateID altCode; + TimeValue movieStart; + TimeValue movieEnd; + DoorFlags flags; + }; + + Entry findEntry(RoomID room, DirectionConstant direction, AlternateID altCode); + +private: + Common::Array<Entry> _entries; +}; + +} // End of namespace Pegasus + +#endif + diff --git a/engines/pegasus/neighborhood/exit.cpp b/engines/pegasus/neighborhood/exit.cpp new file mode 100644 index 0000000000..f0dfff12d3 --- /dev/null +++ b/engines/pegasus/neighborhood/exit.cpp @@ -0,0 +1,70 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "common/debug.h" +#include "common/stream.h" +#include "common/textconsole.h" + +#include "pegasus/neighborhood/exit.h" + +namespace Pegasus { + +void ExitTable::loadFromStream(Common::SeekableReadStream *stream) { + uint32 count = stream->readUint32BE(); + _entries.resize(count); + + for (uint32 i = 0; i < count; i++) { + _entries[i].room = stream->readUint16BE(); + _entries[i].direction = stream->readByte(); + _entries[i].altCode = stream->readByte(); + _entries[i].movieStart = stream->readUint32BE(); + _entries[i].movieEnd = stream->readUint32BE(); + _entries[i].exitEnd = stream->readUint32BE(); + _entries[i].exitLoop = stream->readUint32BE(); + _entries[i].exitRoom = stream->readUint16BE(); + _entries[i].exitDirection = stream->readByte(); + stream->readByte(); // alignment + + _entries[i].originalEnd = _entries[i].exitEnd; + + debug(0, "Exit[%d]: %d %d %d %d %d %d %d %d %d", i, _entries[i].room, _entries[i].direction, + _entries[i].altCode, _entries[i].movieStart, _entries[i].movieEnd, _entries[i].exitEnd, + _entries[i].exitLoop, _entries[i].exitRoom, _entries[i].exitDirection); + } +} + +void ExitTable::clear() { + _entries.clear(); +} + +ExitTable::Entry ExitTable::findEntry(RoomID room, DirectionConstant direction, AlternateID altCode) { + for (uint32 i = 0; i < _entries.size(); i++) + if (_entries[i].room == room && _entries[i].direction == direction && _entries[i].altCode == altCode) + return _entries[i]; + + return Entry(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/exit.h b/engines/pegasus/neighborhood/exit.h new file mode 100644 index 0000000000..17150892f9 --- /dev/null +++ b/engines/pegasus/neighborhood/exit.h @@ -0,0 +1,93 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_NEIGHBORHOOD_EXIT_H +#define PEGASUS_NEIGHBORHOOD_EXIT_H + +#include "common/array.h" +#include "common/endian.h" + +#include "pegasus/constants.h" + +namespace Common { + class SeekableReadStream; +} + +namespace Pegasus { + +class ExitTable { +public: + ExitTable() {} + ~ExitTable() {} + + static uint32 getResTag() { return MKTAG('E', 'x', 'i', 't'); } + + void loadFromStream(Common::SeekableReadStream *stream); + void clear(); + + struct Entry { + Entry() { clear(); } + bool isEmpty() { return movieStart == 0xffffffff; } + void clear() { + room = kNoRoomID; + direction = kNoDirection; + altCode = kNoAlternateID; + movieStart = 0xffffffff; + movieEnd = 0xffffffff; + exitEnd = 0xffffffff; + originalEnd = 0xffffffff; + exitLoop = 0xffffffff; + exitRoom = kNoRoomID; + exitDirection = kNoDirection; + } + + RoomID room; + DirectionConstant direction; + AlternateID altCode; + TimeValue movieStart; + TimeValue movieEnd; + // exitEnd is the end of the optimized run of walks. + TimeValue exitEnd; + TimeValue originalEnd; + // exitLoop is the loop start time of the optimized run of walks if the run + // loops back on itself (so far, only in TSA). + TimeValue exitLoop; + RoomID exitRoom; + DirectionConstant exitDirection; + }; + + Entry findEntry(RoomID room, DirectionConstant direction, AlternateID altCode); + + typedef Common::Array<Entry>::iterator iterator; + iterator begin() { return _entries.begin(); } + iterator end() { return _entries.end(); } + +private: + Common::Array<Entry> _entries; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/extra.cpp b/engines/pegasus/neighborhood/extra.cpp new file mode 100644 index 0000000000..b8c4e5b510 --- /dev/null +++ b/engines/pegasus/neighborhood/extra.cpp @@ -0,0 +1,58 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "common/debug.h" +#include "common/stream.h" +#include "common/textconsole.h" + +#include "pegasus/neighborhood/extra.h" + +namespace Pegasus { + +void ExtraTable::loadFromStream(Common::SeekableReadStream *stream) { + uint32 count = stream->readUint32BE(); + _entries.resize(count); + + for (uint32 i = 0; i < count; i++) { + _entries[i].extra = stream->readUint32BE(); + _entries[i].movieStart = stream->readUint32BE(); + _entries[i].movieEnd = stream->readUint32BE(); + debug(0, "Extra[%d]: %d %d %d", i, _entries[i].extra, _entries[i].movieStart, _entries[i].movieEnd); + } +} + +void ExtraTable::clear() { + _entries.clear(); +} + +ExtraTable::Entry ExtraTable::findEntry(ExtraID extra) { + for (uint32 i = 0; i < _entries.size(); i++) + if (_entries[i].extra == extra) + return _entries[i]; + + return Entry(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/extra.h b/engines/pegasus/neighborhood/extra.h new file mode 100644 index 0000000000..14fcff1009 --- /dev/null +++ b/engines/pegasus/neighborhood/extra.h @@ -0,0 +1,67 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_NEIGHBORHOOD_EXTRA_H +#define PEGASUS_NEIGHBORHOOD_EXTRA_H + +#include "common/array.h" +#include "common/endian.h" + +#include "pegasus/constants.h" + +namespace Common { + class SeekableReadStream; +} + +namespace Pegasus { + +class ExtraTable { +public: + ExtraTable() {} + ~ExtraTable() {} + + static uint32 getResTag() { return MKTAG('X', 't', 'r', 'a'); } + + void loadFromStream(Common::SeekableReadStream *stream); + void clear(); + + struct Entry { + Entry() { movieStart = 0xffffffff; } + bool isEmpty() { return movieStart == 0xffffffff; } + + ExtraID extra; + TimeValue movieStart; + TimeValue movieEnd; + }; + + Entry findEntry(ExtraID extra); + +private: + Common::Array<Entry> _entries; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/hotspotinfo.cpp b/engines/pegasus/neighborhood/hotspotinfo.cpp new file mode 100644 index 0000000000..c7524f3a0f --- /dev/null +++ b/engines/pegasus/neighborhood/hotspotinfo.cpp @@ -0,0 +1,65 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "common/debug.h" +#include "common/stream.h" +#include "common/textconsole.h" + +#include "pegasus/neighborhood/hotspotinfo.h" + +namespace Pegasus { + +void HotspotInfoTable::loadFromStream(Common::SeekableReadStream *stream) { + uint32 count = stream->readUint32BE(); + _entries.resize(count); + + for (uint32 i = 0; i < count; i++) { + _entries[i].hotspot = stream->readUint16BE(); + _entries[i].hotspotActivation = stream->readSByte(); + stream->readByte(); // alignment + _entries[i].hotspotRoom = stream->readUint16BE(); + _entries[i].hotspotDirection = stream->readByte(); + stream->readByte(); // alignment + _entries[i].hotspotExtra = stream->readUint32BE(); + _entries[i].hotspotItem = stream->readUint16BE(); + debug(0, "Hotspot[%d]: %d %d %d %d %d %d", i, _entries[i].hotspot, _entries[i].hotspotActivation, + _entries[i].hotspotRoom, _entries[i].hotspotDirection, _entries[i].hotspotExtra, + _entries[i].hotspotItem); + } +} + +void HotspotInfoTable::clear() { + _entries.clear(); +} + +HotspotInfoTable::Entry HotspotInfoTable::findEntry(HotSpotID hotspot) { + for (uint32 i = 0; i < _entries.size(); i++) + if (_entries[i].hotspot == hotspot) + return _entries[i]; + + return Entry(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/hotspotinfo.h b/engines/pegasus/neighborhood/hotspotinfo.h new file mode 100644 index 0000000000..965f445ba8 --- /dev/null +++ b/engines/pegasus/neighborhood/hotspotinfo.h @@ -0,0 +1,77 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_NEIGHBORHOOD_HOTSPOTINFO_H +#define PEGASUS_NEIGHBORHOOD_HOTSPOTINFO_H + +#include "common/array.h" +#include "common/endian.h" + +#include "pegasus/constants.h" + +namespace Common { + class SeekableReadStream; +} + +namespace Pegasus { + +class HotspotInfoTable { +public: + HotspotInfoTable() {} + ~HotspotInfoTable() {} + + static uint32 getResTag() { return MKTAG('H', 'S', 'I', 'n'); } + + void loadFromStream(Common::SeekableReadStream *stream); + void clear(); + + struct Entry { + Entry() { hotspotRoom = kNoRoomID; } + bool isEmpty() { return hotspotRoom == kNoRoomID; } + + HotSpotID hotspot; + HotSpotActivationID hotspotActivation; + // Location hot spot lives in: + RoomID hotspotRoom; + DirectionConstant hotspotDirection; + // Extra to play if this is a "play extra" hot spot. + ExtraID hotspotExtra; + // Item corresponding to this hot spot if it is an item-related hot spot. + ItemID hotspotItem; + }; + + Entry findEntry(HotSpotID hotspot); + + typedef Common::Array<Entry>::iterator iterator; + iterator begin() { return _entries.begin(); } + iterator end() { return _entries.end(); } + +private: + Common::Array<Entry> _entries; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/mars/constants.h b/engines/pegasus/neighborhood/mars/constants.h new file mode 100644 index 0000000000..82a7f03b68 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/constants.h @@ -0,0 +1,941 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_NEIGHBORHOOD_MARS_CONSTANTS_H +#define PEGASUS_NEIGHBORHOOD_MARS_CONSTANTS_H + +#include "pegasus/constants.h" + +namespace Pegasus { + +// Element Coordinates + +static const CoordType kUndoHiliteLeft = kNavAreaLeft + 140; +static const CoordType kUndoHiliteTop = kNavAreaTop + 36; + +static const CoordType kCurrentGuessLeft = kNavAreaLeft + 146; +static const CoordType kCurrentGuessTop = kNavAreaTop + 90; + +static const CoordType kReactorChoiceHiliteLeft = kNavAreaLeft + 116; +static const CoordType kReactorChoiceHiliteTop = kNavAreaTop + 158; + +static const CoordType kReactorHistoryLeft = kNavAreaLeft + 302; +static const CoordType kReactorHistoryTop = kNavAreaTop + 39; + +static const CoordType kAnswerLeft = kNavAreaLeft + 304; +static const CoordType kAnswerTop = kNavAreaTop + 180; + +static const CoordType kShuttle1Left = 0; +static const CoordType kShuttle1Top = 0; + +static const CoordType kShuttle2Left = 0; +static const CoordType kShuttle2Top = 96; + +static const CoordType kShuttle3Left = 500; +static const CoordType kShuttle3Top = 96; + +static const CoordType kShuttle4Left = 0; +static const CoordType kShuttle4Top = 320; + +static const CoordType kShuttleWindowLeft = 140; +static const CoordType kShuttleWindowTop = 96; +static const CoordType kShuttleWindowWidth = 360; +static const CoordType kShuttleWindowHeight = 224; + +static const CoordType kShuttleWindowMidH = (kShuttleWindowLeft * 2 + kShuttleWindowWidth) / 2; +static const CoordType kShuttleWindowMidV = (kShuttleWindowTop * 2 + kShuttleWindowHeight) / 2; + +static const CoordType kShuttleLeftLeft = 0; +static const CoordType kShuttleLeftTop = 128; + +static const CoordType kShuttleRightLeft = 506; +static const CoordType kShuttleRightTop = 128; + +static const CoordType kShuttleLowerLeftLeft = 74; +static const CoordType kShuttleLowerLeftTop = 358; + +static const CoordType kShuttleLowerRightLeft = 486; +static const CoordType kShuttleLowerRightTop = 354; + +static const CoordType kShuttleCenterLeft = 260; +static const CoordType kShuttleCenterTop = 336; + +static const CoordType kShuttleUpperLeftLeft = 30; +static const CoordType kShuttleUpperLeftTop = 32; + +static const CoordType kShuttleUpperRightLeft = 506; +static const CoordType kShuttleUpperRightTop = 52; + +static const CoordType kShuttleLeftEnergyLeft = 110; +static const CoordType kShuttleLeftEnergyTop = 186; + +static const CoordType kShuttleRightEnergyLeft = 510; +static const CoordType kShuttleRightEnergyTop = 186; + +static const CoordType kShuttleEnergyLeft = 186; +static const CoordType kShuttleEnergyTop = 60; +static const CoordType kShuttleEnergyWidth = 252; +static const CoordType kShuttleEnergyHeight = 22; + +static const CoordType kPlanetStartLeft = kShuttleWindowLeft; +static const CoordType kPlanetStartTop = kShuttleWindowTop + kShuttleWindowHeight; + +static const CoordType kPlanetStopLeft = kShuttleWindowLeft; +static const CoordType kPlanetStopTop = kShuttleWindowTop + kShuttleWindowHeight - 100; + +static const CoordType kShuttleTractorLeft = kShuttleWindowLeft + 6; +static const CoordType kShuttleTractorTop = kShuttleWindowTop + 56; +static const CoordType kShuttleTractorWidth = 348; +static const CoordType kShuttleTractorHeight = 112; + +static const CoordType kShuttleJunkLeft = kShuttleWindowLeft + 6; +static const CoordType kShuttleJunkTop = kShuttleWindowTop + 6; + +static const DisplayOrder kShuttlePlanetOrder = kInterfaceLayer; +static const DisplayOrder kShuttleAlienShipOrder = kShuttlePlanetOrder + 1; +static const DisplayOrder kShuttleRobotShipOrder = kShuttleAlienShipOrder + 1; +static const DisplayOrder kShuttleTractorBeamMovieOrder = kShuttleRobotShipOrder + 1; +static const DisplayOrder kShuttleWeaponBackOrder = kShuttleTractorBeamMovieOrder + 1; +static const DisplayOrder kShuttleJunkOrder = kShuttleWeaponBackOrder + 1; +static const DisplayOrder kShuttleWeaponFrontOrder = kShuttleJunkOrder + 1; +static const DisplayOrder kShuttleTractorBeamOrder = kShuttleWeaponFrontOrder + 1; +static const DisplayOrder kShuttleHUDOrder = kShuttleTractorBeamOrder + 1; +static const DisplayOrder kShuttleBackgroundOrder = kShuttleHUDOrder + 1; +static const DisplayOrder kShuttleMonitorOrder = kShuttleBackgroundOrder + 1; +static const DisplayOrder kShuttleStatusOrder = kShuttleMonitorOrder + 1; + +static const TimeValue kShuttleSwingStart = 0; +static const TimeValue kShuttleSwingStop = 5 * 600; + +static const TimeValue kCanyonChaseStart = kShuttleSwingStop; +static const TimeValue kCanyonChaseStop = 60 * 600 + 43 * 600 + 14 * 40; + +static const TimeValue kLaunchTubeReachedTime = 60 * 600 + 38 * 600 - kCanyonChaseStart; +static const TimeValue kCanyonChaseFinishedTime = kCanyonChaseStop - kCanyonChaseStart - + kLaunchTubeReachedTime; + +// Left shuttle. + +static const TimeValue kShuttleLeftIntroStart = 0; +static const TimeValue kShuttleLeftIntroStop = 400; + +static const TimeValue kShuttleLeftBlankTime = 400; + +static const TimeValue kShuttleLeftNormalTime = 440; + +static const TimeValue kShuttleLeftAutoTestTime = 480; + +static const TimeValue kShuttleLeftDamagedTime = 520; + +static const TimeValue kShuttleLeftDampingTime = 560; + +static const TimeValue kShuttleLeftGravitonTime = 600; + +static const TimeValue kShuttleLeftTractorTime = 640; + +// Right shuttle. + +static const TimeValue kShuttleRightIntroStart = 0; +static const TimeValue kShuttleRightIntroStop = 400; + +static const TimeValue kShuttleRightDestroyedStart = 400; +static const TimeValue kShuttleRightDestroyedStop = 840; + +static const TimeValue kShuttleRightBlankTime = 840; + +static const TimeValue kShuttleRightNormalTime = 880; + +static const TimeValue kShuttleRightDamagedTime = 920; + +static const TimeValue kShuttleRightTargetLockTime = 960; + +static const TimeValue kShuttleRightGravitonTime = 1000; + +static const TimeValue kShuttleRightOverloadTime = 1040; + +// Lower Left shuttle. + +static const TimeValue kShuttleLowerLeftCollisionTime = 0; + +static const TimeValue kShuttleLowerLeftTubeTime = 40; + +static const TimeValue kShuttleLowerLeftAutopilotTime = 80; + +// Lower Right shuttle. + +static const TimeValue kShuttleLowerRightOffTime = 0; + +static const TimeValue kShuttleLowerRightTrackingTime = 40; + +static const TimeValue kShuttleLowerRightTransportTime = 80; + +static const TimeValue kShuttleLowerRightTransportHiliteTime = 120; + +// Center shuttle. + +static const TimeValue kShuttleCenterBoardingTime = 0; + +static const TimeValue kShuttleCenterCheckTime = 40; + +static const TimeValue kShuttleCenterNavCompTime = 80; + +static const TimeValue kShuttleCenterCommTime = 120; + +static const TimeValue kShuttleCenterWeaponsTime = 160; + +static const TimeValue kShuttleCenterAllSystemsTime = 200; + +static const TimeValue kShuttleCenterSecureLooseTime = 240; + +static const TimeValue kShuttleCenterAutoTestTime = 280; + +static const TimeValue kShuttleCenterLaunchTime = 320; + +static const TimeValue kShuttleCenterEnterTubeTime = 360; + +static const TimeValue kShuttleCenterTargetSightedTime = 400; + +static const TimeValue kShuttleCenterVerifyingTime = 440; + +static const TimeValue kShuttleCenterScanningTime = 480; + +static const TimeValue kShuttleCenterSafeTime = 520; + +// Upper Left shuttle. + +static const TimeValue kShuttleUpperLeftDimTime = 0; + +static const TimeValue kShuttleUpperLeftDampingTime = 40; + +static const TimeValue kShuttleUpperLeftGravitonTime = 80; + +static const TimeValue kShuttleUpperLeftTractorTime = 120; + +// Upper Right shuttle. + +static const TimeValue kShuttleUpperRightLockedTime = 0; + +static const TimeValue kShuttleUpperRightArmedTime = 40; + +static const TimeValue kShuttleUpperRightAlienDestroyedTime = 80; + +static const TimeValue kShuttleUpperRightOverloadTime = 120; + +static const TimeValue kShuttleUpperRightTargetDestroyedTime = 160; + +// Shuttle distance + +static const int kShuttleDistance = 500; + +static const int kJunkMaxDistance = kShuttleDistance; +static const int kJunkMinDistance = 40; + +static const int kEnergyBeamMaxDistance = kShuttleDistance; +static const int kEnergyBeamMinDistance = 40; + +static const int kGravitonMaxDistance = kShuttleDistance; +static const int kGravitonMinDistance = 40; + +static const TimeValue kMarsOxyMaskOnIn = 0; +static const TimeValue kMarsOxyMaskOnOut = 1560; + +static const TimeValue kMarsAirlockButtonBeepIn = 1560; +static const TimeValue kMarsAirlockButtonBeepOut = 1620; + +static const TimeValue kMarsColorMatchingButtonBeepIn = 1620; +static const TimeValue kMarsColorMatchingButtonBeepOut = 1680; + +static const TimeValue kMarsKioskBeepIn = 1680; +static const TimeValue kMarsKioskBeepOut = 1740; + +static const TimeValue kMarsBumpIntoWallIn = 1740; +static const TimeValue kMarsBumpIntoWallOut = 1888; + +static const TimeValue kMarsGantryDoorCloseIn = 1888; +static const TimeValue kMarsGantryDoorCloseOut = 2866; + +static const TimeValue kMarsTransportDoorCloseIn = 2866; +static const TimeValue kMarsTransportDoorCloseOut = 3593; + +static const TimeValue kMarsAirlockPressurizeIn = 3593; +static const TimeValue kMarsAirlockPressurizeOut = 4766; + +static const TimeValue kMarsBigAirlockDoorCloseIn = 4766; +static const TimeValue kMarsBigAirlockDoorCloseOut = 7872; + +static const TimeValue kMarsSmallAirlockDoorCloseIn = 7872; +static const TimeValue kMarsSmallAirlockDoorCloseOut = 10000; + +static const TimeValue kMarsMazeDoorCloseIn = 10000; +static const TimeValue kMarsMazeDoorCloseOut = 10969; + +static const TimeValue kMarsRobotTakesTransportIn = 10969; +static const TimeValue kMarsRobotTakesTransportOut = 12802; + +static const TimeValue kMarsPodDepartedUpperPlatformIn = 12802; +static const TimeValue kMarsPodDepartedUpperPlatformOut = 15783; + +static const TimeValue kMarsPodDepartedLowerPlatformIn = 15783; +static const TimeValue kMarsPodDepartedLowerPlatformOut = 18736; + +static const TimeValue kMarsPodArrivedUpperPlatformIn = 18736; +static const TimeValue kMarsPodArrivedUpperPlatformOut = 21605; + +static const TimeValue kMarsCheckInRequiredIn = 21605; +static const TimeValue kMarsCheckInRequiredOut = 27463; + +static const TimeValue kMarsCantOpenShuttleIn = 27463; +static const TimeValue kMarsCantOpenShuttleOut = 29214; + +static const TimeValue kMarsShuttleLockOverrideIn = 29214; +static const TimeValue kMarsShuttleLockOverrideOut = 30330; + +static const TimeValue kMarsNoShuttleIn = 30330; +static const TimeValue kMarsNoShuttleOut = 31502; + +static const TimeValue kMustBeUnlockedIn = 31502; +static const TimeValue kMustBeUnlockedOut = 33960; + +static const TimeValue kColorMatchBlueIn = 33960; +static const TimeValue kColorMatchBlueOut = 34240; + +static const TimeValue kColorMatchRedIn = 34240; +static const TimeValue kColorMatchRedOut = 34538; + +static const TimeValue kColorMatchGreenIn = 34538; +static const TimeValue kColorMatchGreenOut = 34827; + +static const TimeValue kColorMatchYellowIn = 34827; +static const TimeValue kColorMatchYellowOut = 35162; + +static const TimeValue kColorMatchPurpleIn = 35162; +static const TimeValue kColorMatchPurpleOut = 35426; + +static const TimeValue kColorMatchZeroNodesIn = 35426; +static const TimeValue kColorMatchZeroNodesOut = 36376; + +static const TimeValue kColorMatchOneNodeIn = 36376; +static const TimeValue kColorMatchOneNodeOut = 37209; + +static const TimeValue kColorMatchTwoNodesIn = 37209; +static const TimeValue kColorMatchTwoNodesOut = 37983; + +static const TimeValue kColorMatchThreeNodesIn = 37983; +static const TimeValue kColorMatchThreeNodesOut = 38784; + +static const TimeValue kMarsShuttle1DepartedIn = 38784; +static const TimeValue kMarsShuttle1DepartedOut = 40323; + +static const TimeValue kMarsShuttle2DepartedIn = 40323; +static const TimeValue kMarsShuttle2DepartedOut = 41824; + +static const TimeValue kShuttleCockpitIn = 41824; +static const TimeValue kShuttleCockpitOut = 43126; + +static const TimeValue kShuttleOnboardIn = 43126; +static const TimeValue kShuttleOnboardOut = 44284; + +static const TimeValue kShuttleNavigationIn = 44284; +static const TimeValue kShuttleNavigationOut = 46049; + +static const TimeValue kShuttleCommunicationIn = 46049; +static const TimeValue kShuttleCommunicationOut = 47288; + +static const TimeValue kShuttleAutoTestingIn = 47288; +static const TimeValue kShuttleAutoTestingOut = 48179; + +static const TimeValue kMarsThrusterAutoTestIn = 48179; +static const TimeValue kMarsThrusterAutoTestOut = 49979; + +static const TimeValue kShuttleAllSystemsIn = 49979; +static const TimeValue kShuttleAllSystemsOut = 51065; + +static const TimeValue kShuttleSecureLooseIn = 51065; +static const TimeValue kShuttleSecureLooseOut = 52346; + +static const TimeValue kShuttlePrepareForDropIn = 52346; +static const TimeValue kShuttlePrepareForDropOut = 53216; + +static const TimeValue kShuttleAllClearIn = 53216; +static const TimeValue kShuttleAllClearOut = 54031; + +static const TimeValue kShuttleConfiguringIn = 54031; +static const TimeValue kShuttleConfiguringOut = 54994; + +static const TimeValue kShuttleGeneratingIn = 54994; +static const TimeValue kShuttleGeneratingOut = 56033; + +static const TimeValue kShuttleBreakawayIn = 56033; +static const TimeValue kShuttleBreakawayOut = 57346; + +static const TimeValue kMarsAtmosphericBreakawayIn = 57346; +static const TimeValue kMarsAtmosphericBreakawayOut = 59237; + +static const TimeValue kMarsCockpitChatterIn = 59237; +static const TimeValue kMarsCockpitChatterOut = 70344; + +static const TimeValue kShuttleDamperDescIn = 70344; +static const TimeValue kShuttleDamperDescOut = 73262; + +static const TimeValue kShuttleGravitonDescIn = 73262; +static const TimeValue kShuttleGravitonDescOut = 75296; + +static const TimeValue kShuttleTractorDescIn = 75296; +static const TimeValue kShuttleTractorDescOut = 78381; + +static const TimeValue kShuttleTargetSightedIn = 78381; +static const TimeValue kShuttleTargetSightedOut = 79074; + +static const TimeValue kShuttleAutopilotEngagedIn = 79074; +static const TimeValue kShuttleAutopilotEngagedOut = 80414; + +static const TimeValue kMarsEDBBlastIn = 80414; +static const TimeValue kMarsEDBBlastOut = 80705; + +static const TimeValue kMarsGravitonBlastIn = 80705; +static const TimeValue kMarsGravitonBlastOut = 81199; + +static const TimeValue kMarsJunkCollisionIn = 81199; +static const TimeValue kMarsJunkCollisionOut = 81961; + +static const TimeValue kShuttleGravitonIn = 81961; +static const TimeValue kShuttleGravitonOut = 82587; + +static const TimeValue kShuttleDampingBeamIn = 82587; +static const TimeValue kShuttleDampingBeamOut = 83331; + +static const TimeValue kShuttleTractorBeamIn = 83331; +static const TimeValue kShuttleTractorBeamOut = 83802; + +static const TimeValue kShuttleHullBreachIn = 83802; +static const TimeValue kShuttleHullBreachOut = 84721; + +static const TimeValue kShuttleWingDamageIn = 84721; +static const TimeValue kShuttleWingDamageOut = 85640; + +static const TimeValue kShuttleHullDamageIn = 85640; +static const TimeValue kShuttleHullDamageOut = 86513; + +static const TimeValue kShuttleEnergyTooLowIn = 86513; +static const TimeValue kShuttleEnergyTooLowOut = 87578; + +static const TimeValue kShuttleTractorLimitedIn = 87578; +static const TimeValue kShuttleTractorLimitedOut = 89164; + +static const TimeValue kShuttleCantHoldIn = 89164; +static const TimeValue kShuttleCantHoldOut = 90945; + +static const TimeValue kShuttleBrokeFreeIn = 90945; +static const TimeValue kShuttleBrokeFreeOut = 92322; + +static const TimeValue kShuttleDestroyedIn = 92322; +static const TimeValue kShuttleDestroyedOut = 93189; + +static const TimeValue kShuttleCoordinatesIn = 93189; +static const TimeValue kShuttleCoordinatesOut = 94018; + +static const TimeValue kShuttleScanningIn = 94018; +static const TimeValue kShuttleScanningOut = 94975; + +static const TimeValue kShuttleSafeIn = 94975; +static const TimeValue kShuttleSafeOut = 96176; + +static const TimeValue kShuttleOverloadedIn = 96176; +static const TimeValue kShuttleOverloadedOut = 101308; + +static const TimeScale kMarsMovieScale = 600; +static const TimeScale kMarsFramesPerSecond = 15; +static const TimeScale kMarsFrameDuration = 40; + +// Alternate IDs. + +static const AlternateID kAltMarsNormal = 0; +static const AlternateID kAltMarsPodAtMars34 = 1; +static const AlternateID kAltMarsTookCard = 2; +static const AlternateID kAltMars35AirlockEast = 3; +static const AlternateID kAltMars35AirlockWest = 4; +static const AlternateID kAltMarsPodAtMars45 = 5; +static const AlternateID kAltMarsTookMask = 6; +static const AlternateID kAltMarsMaskOnFiller = 7; +static const AlternateID kAltMars60AirlockEast = 8; +static const AlternateID kAltMars60AirlockWest = 9; + +// Room IDs. + +static const RoomID kMars0A = 0; +static const RoomID kMars00 = 1; +static const RoomID kMars01 = 2; +static const RoomID kMars02 = 3; +static const RoomID kMars03 = 4; +static const RoomID kMars04 = 5; +static const RoomID kMars05 = 6; +static const RoomID kMars06 = 7; +static const RoomID kMars07 = 8; +static const RoomID kMars08 = 9; +static const RoomID kMars09 = 10; +static const RoomID kMars10 = 11; +static const RoomID kMars11 = 12; +static const RoomID kMars12 = 13; +static const RoomID kMars13 = 14; +static const RoomID kMars14 = 15; +static const RoomID kMars15 = 16; +static const RoomID kMars16 = 17; +static const RoomID kMars17 = 18; +static const RoomID kMars18 = 19; +static const RoomID kMars19 = 20; +static const RoomID kMars20 = 21; +static const RoomID kMars21 = 22; +static const RoomID kMars22 = 23; +static const RoomID kMars23 = 24; +static const RoomID kMars24 = 25; +static const RoomID kMars25 = 26; +static const RoomID kMars26 = 27; +static const RoomID kMars27 = 28; +static const RoomID kMars28 = 29; +static const RoomID kMars29 = 30; +static const RoomID kMars30 = 31; +static const RoomID kMars31 = 32; +static const RoomID kMars31South = 33; +static const RoomID kMars32 = 34; +static const RoomID kMars33 = 35; +static const RoomID kMars33North = 36; +static const RoomID kMars34 = 37; +static const RoomID kMars35 = 38; +static const RoomID kMars36 = 39; +static const RoomID kMars37 = 40; +static const RoomID kMars38 = 41; +static const RoomID kMars39 = 42; +static const RoomID kMars41 = 43; +static const RoomID kMars42 = 44; +static const RoomID kMars43 = 45; +static const RoomID kMars44 = 46; +static const RoomID kMars45 = 47; +static const RoomID kMars46 = 48; +static const RoomID kMars47 = 49; +static const RoomID kMars48 = 50; +static const RoomID kMars49 = 51; +static const RoomID kMars50 = 52; +static const RoomID kMars51 = 53; +static const RoomID kMars52 = 54; +static const RoomID kMars54 = 55; +static const RoomID kMars56 = 56; +static const RoomID kMars58 = 57; +static const RoomID kMars60 = 58; +static const RoomID kMarsRobotShuttle = 59; +static const RoomID kMarsMaze004 = 60; +static const RoomID kMarsMaze005 = 61; +static const RoomID kMarsMaze006 = 62; +static const RoomID kMarsMaze007 = 63; +static const RoomID kMarsMaze008 = 64; +static const RoomID kMarsMaze009 = 65; +static const RoomID kMarsMaze010 = 66; +static const RoomID kMarsMaze011 = 67; +static const RoomID kMarsMaze012 = 68; +static const RoomID kMarsMaze015 = 69; +static const RoomID kMarsMaze016 = 70; +static const RoomID kMarsMaze017 = 71; +static const RoomID kMarsMaze018 = 72; +static const RoomID kMarsMaze019 = 73; +static const RoomID kMarsMaze020 = 74; +static const RoomID kMarsMaze021 = 75; +static const RoomID kMarsMaze022 = 76; +static const RoomID kMarsMaze023 = 77; +static const RoomID kMarsMaze024 = 78; +static const RoomID kMarsMaze025 = 79; +static const RoomID kMarsMaze026 = 80; +static const RoomID kMarsMaze027 = 81; +static const RoomID kMarsMaze028 = 82; +static const RoomID kMarsMaze031 = 83; +static const RoomID kMarsMaze032 = 84; +static const RoomID kMarsMaze033 = 85; +static const RoomID kMarsMaze034 = 86; +static const RoomID kMarsMaze035 = 87; +static const RoomID kMarsMaze036 = 88; +static const RoomID kMarsMaze037 = 89; +static const RoomID kMarsMaze038 = 90; +static const RoomID kMarsMaze039 = 91; +static const RoomID kMarsMaze042 = 92; +static const RoomID kMarsMaze043 = 93; +static const RoomID kMarsMaze044 = 94; +static const RoomID kMarsMaze045 = 95; +static const RoomID kMarsMaze046 = 96; +static const RoomID kMarsMaze047 = 97; +static const RoomID kMarsMaze049 = 98; +static const RoomID kMarsMaze050 = 99; +static const RoomID kMarsMaze051 = 100; +static const RoomID kMarsMaze052 = 101; +static const RoomID kMarsMaze053 = 102; +static const RoomID kMarsMaze054 = 103; +static const RoomID kMarsMaze055 = 104; +static const RoomID kMarsMaze056 = 105; +static const RoomID kMarsMaze057 = 106; +static const RoomID kMarsMaze058 = 107; +static const RoomID kMarsMaze059 = 108; +static const RoomID kMarsMaze060 = 109; +static const RoomID kMarsMaze061 = 110; +static const RoomID kMarsMaze063 = 111; +static const RoomID kMarsMaze064 = 112; +static const RoomID kMarsMaze065 = 113; +static const RoomID kMarsMaze066 = 114; +static const RoomID kMarsMaze067 = 115; +static const RoomID kMarsMaze068 = 116; +static const RoomID kMarsMaze069 = 117; +static const RoomID kMarsMaze070 = 118; +static const RoomID kMarsMaze071 = 119; +static const RoomID kMarsMaze072 = 120; +static const RoomID kMarsMaze074 = 121; +static const RoomID kMarsMaze076 = 122; +static const RoomID kMarsMaze078 = 123; +static const RoomID kMarsMaze079 = 124; +static const RoomID kMarsMaze081 = 125; +static const RoomID kMarsMaze083 = 126; +static const RoomID kMarsMaze084 = 127; +static const RoomID kMarsMaze085 = 128; +static const RoomID kMarsMaze086 = 129; +static const RoomID kMarsMaze087 = 130; +static const RoomID kMarsMaze088 = 131; +static const RoomID kMarsMaze089 = 132; +static const RoomID kMarsMaze090 = 133; +static const RoomID kMarsMaze091 = 134; +static const RoomID kMarsMaze092 = 135; +static const RoomID kMarsMaze093 = 136; +static const RoomID kMarsMaze098 = 137; +static const RoomID kMarsMaze099 = 138; +static const RoomID kMarsMaze100 = 139; +static const RoomID kMarsMaze101 = 140; +static const RoomID kMarsMaze104 = 141; +static const RoomID kMarsMaze105 = 142; +static const RoomID kMarsMaze106 = 143; +static const RoomID kMarsMaze107 = 144; +static const RoomID kMarsMaze108 = 145; +static const RoomID kMarsMaze111 = 146; +static const RoomID kMarsMaze113 = 147; +static const RoomID kMarsMaze114 = 148; +static const RoomID kMarsMaze115 = 149; +static const RoomID kMarsMaze116 = 150; +static const RoomID kMarsMaze117 = 151; +static const RoomID kMarsMaze118 = 152; +static const RoomID kMarsMaze119 = 153; +static const RoomID kMarsMaze120 = 154; +static const RoomID kMarsMaze121 = 155; +static const RoomID kMarsMaze122 = 156; +static const RoomID kMarsMaze123 = 157; +static const RoomID kMarsMaze124 = 158; +static const RoomID kMarsMaze125 = 159; +static const RoomID kMarsMaze126 = 160; +static const RoomID kMarsMaze127 = 161; +static const RoomID kMarsMaze128 = 162; +static const RoomID kMarsMaze129 = 163; +static const RoomID kMarsMaze130 = 164; +static const RoomID kMarsMaze131 = 165; +static const RoomID kMarsMaze132 = 166; +static const RoomID kMarsMaze133 = 167; +static const RoomID kMarsMaze136 = 168; +static const RoomID kMarsMaze137 = 169; +static const RoomID kMarsMaze138 = 170; +static const RoomID kMarsMaze139 = 171; +static const RoomID kMarsMaze140 = 172; +static const RoomID kMarsMaze141 = 173; +static const RoomID kMarsMaze142 = 174; +static const RoomID kMarsMaze143 = 175; +static const RoomID kMarsMaze144 = 176; +static const RoomID kMarsMaze145 = 177; +static const RoomID kMarsMaze146 = 178; +static const RoomID kMarsMaze147 = 179; +static const RoomID kMarsMaze148 = 180; +static const RoomID kMarsMaze149 = 181; +static const RoomID kMarsMaze152 = 182; +static const RoomID kMarsMaze153 = 183; +static const RoomID kMarsMaze154 = 184; +static const RoomID kMarsMaze155 = 185; +static const RoomID kMarsMaze156 = 186; +static const RoomID kMarsMaze157 = 187; +static const RoomID kMarsMaze159 = 188; +static const RoomID kMarsMaze160 = 189; +static const RoomID kMarsMaze161 = 190; +static const RoomID kMarsMaze162 = 191; +static const RoomID kMarsMaze163 = 192; +static const RoomID kMarsMaze164 = 193; +static const RoomID kMarsMaze165 = 194; +static const RoomID kMarsMaze166 = 195; +static const RoomID kMarsMaze167 = 196; +static const RoomID kMarsMaze168 = 197; +static const RoomID kMarsMaze169 = 198; +static const RoomID kMarsMaze170 = 199; +static const RoomID kMarsMaze171 = 200; +static const RoomID kMarsMaze172 = 201; +static const RoomID kMarsMaze173 = 202; +static const RoomID kMarsMaze174 = 203; +static const RoomID kMarsMaze175 = 204; +static const RoomID kMarsMaze177 = 205; +static const RoomID kMarsMaze178 = 206; +static const RoomID kMarsMaze179 = 207; +static const RoomID kMarsMaze180 = 208; +static const RoomID kMarsMaze181 = 209; +static const RoomID kMarsMaze182 = 210; +static const RoomID kMarsMaze183 = 211; +static const RoomID kMarsMaze184 = 212; +static const RoomID kMarsMaze187 = 213; +static const RoomID kMarsMaze188 = 214; +static const RoomID kMarsMaze189 = 215; +static const RoomID kMarsMaze190 = 216; +static const RoomID kMarsMaze191 = 217; +static const RoomID kMarsMaze192 = 218; +static const RoomID kMarsMaze193 = 219; +static const RoomID kMarsMaze194 = 220; +static const RoomID kMarsMaze195 = 221; +static const RoomID kMarsMaze198 = 222; +static const RoomID kMarsMaze199 = 223; +static const RoomID kMarsMaze200 = 224; +static const RoomID kMarsDeathRoom = 225; + +// Hot Spot Activation IDs. + +static const HotSpotActivationID kActivationReadyForKiosk = 1; +static const HotSpotActivationID kActivationKioskChoice = 2; +static const HotSpotActivationID kActivationTunnelMapReady = 3; +static const HotSpotActivationID kActivateMarsPodClosed = 4; +static const HotSpotActivationID kActivateMarsPodOpen = 5; +static const HotSpotActivationID kActivateReadyToPressurizeAirlock = 6; +static const HotSpotActivationID kActivateAirlockPressurized = 7; +static const HotSpotActivationID kActivateMaskOnHolder = 8; +static const HotSpotActivationID kActivateMaskOnFiller = 9; +static const HotSpotActivationID kActivateReactorPlatformOut = 10; +static const HotSpotActivationID kActivateReactorPlatformIn = 11; +static const HotSpotActivationID kActivateReactorAskLowerScreen = 12; +static const HotSpotActivationID kActivateReactorReadyForNitrogen = 13; +static const HotSpotActivationID kActivateReactorReadyForCrowBar = 14; +static const HotSpotActivationID kActivateReactorAskOperation = 15; +static const HotSpotActivationID kActivateReactorRanEvaluation = 16; +static const HotSpotActivationID kActivateReactorRanDiagnostics = 17; +static const HotSpotActivationID kActivateReactorAnalyzed = 18; +static const HotSpotActivationID kActivateReactorInstructions = 19; +static const HotSpotActivationID kActivateReactorInGame = 20; +static const HotSpotActivationID kActivateReactorBombSafe = 21; +static const HotSpotActivationID kActivateReactorBombExposed = 22; +static const HotSpotActivationID kActivationRobotHeadClosed = 23; +static const HotSpotActivationID kActivationRobotHeadOpen = 24; + +// Hot Spot IDs. + +static const HotSpotID kMars11NorthKioskSpotID = 5000; +static const HotSpotID kMars11NorthKioskSightsSpotID = 5001; +static const HotSpotID kMars11NorthKioskColonySpotID = 5002; +static const HotSpotID kMars12NorthKioskSpotID = 5003; +static const HotSpotID kMars12NorthKioskSightsSpotID = 5004; +static const HotSpotID kMars12NorthKioskColonySpotID = 5005; +static const HotSpotID kMars31SouthSpotID = 5006; +static const HotSpotID kMars31SouthOutSpotID = 5007; +static const HotSpotID kMars31SouthCardSpotID = 5008; +static const HotSpotID kMars33NorthSpotID = 5009; +static const HotSpotID kMars33NorthOutSpotID = 5010; +static const HotSpotID kMars33NorthMonitorSpotID = 5011; +static const HotSpotID kMars34NorthCardDropSpotID = 5012; +static const HotSpotID kMars34SouthOpenStorageSpotID = 5013; +static const HotSpotID kMars34SouthCloseStorageSpotID = 5014; +static const HotSpotID kMars34SouthCrowbarSpotID = 5015; +static const HotSpotID kMars35EastPressurizeSpotID = 5016; +static const HotSpotID kMars35EastSpinSpotID = 5017; +static const HotSpotID kMars35WestPressurizeSpotID = 5018; +static const HotSpotID kMars35WestSpinSpotID = 5019; +static const HotSpotID kMars45NorthOpenStorageSpotID = 5020; +static const HotSpotID kMars45NorthCloseStorageSpotID = 5021; +static const HotSpotID kMars45NorthCrowbarSpotID = 5022; +static const HotSpotID kAttackRobotHotSpotID = 5023; +static const HotSpotID kMars49AirMaskSpotID = 5024; +static const HotSpotID kMars49AirMaskFilledSpotID = 5025; +static const HotSpotID kMars49AirFillingDropSpotID = 5026; +static const HotSpotID kMars52MoveLeftSpotID = 5027; +static const HotSpotID kMars52MoveRightSpotID = 5028; +static const HotSpotID kMars52ExtractSpotID = 5029; +static const HotSpotID kMars53RetractSpotID = 5030; +static const HotSpotID kMars54MoveLeftSpotID = 5031; +static const HotSpotID kMars54MoveRightSpotID = 5032; +static const HotSpotID kMars54ExtractSpotID = 5033; +static const HotSpotID kMars55RetractSpotID = 5034; +static const HotSpotID kMars56MoveLeftSpotID = 5035; +static const HotSpotID kMars56MoveRightSpotID = 5036; +static const HotSpotID kMars56ExtractSpotID = 5037; +static const HotSpotID kMars57RetractSpotID = 5038; +static const HotSpotID kMars57LowerScreenSpotID = 5039; +static const HotSpotID kMars57Retract2SpotID = 5040; +static const HotSpotID kMars57DropNitrogenSpotID = 5041; +static const HotSpotID kMars57DropCrowBarSpotID = 5042; +static const HotSpotID kMars57CantOpenPanelSpotID = 5043; +static const HotSpotID kMars57ShieldEvaluationSpotID = 5044; +static const HotSpotID kMars57MeasureOutputSpotID = 5045; +static const HotSpotID kMars57RunDiagnosticsSpotID = 5046; +static const HotSpotID kMars57BackToOperationMenuSpotID = 5047; +static const HotSpotID kMars57AnalyzeObjectSpotID = 5048; +static const HotSpotID kMars57RemoveObjectMenuSpotID = 5049; +static const HotSpotID kMars57CircuitLinkSpotID = 5050; +static const HotSpotID kMars57CancelCircuitLinkSpotID = 5051; +static const HotSpotID kMars57GameInstructionsSpotID = 5052; +static const HotSpotID kMars57UndoMoveSpotID = 5053; +static const HotSpotID kMars57RedMoveSpotID = 5054; +static const HotSpotID kMars57YellowMoveSpotID = 5055; +static const HotSpotID kMars57GreenMoveSpotID = 5056; +static const HotSpotID kMars57BlueMoveSpotID = 5057; +static const HotSpotID kMars57PurpleMoveSpotID = 5058; +static const HotSpotID kMars57LowerScreenSafelySpotID = 5059; +static const HotSpotID kMars57GrabBombSpotID = 5060; +static const HotSpotID kMars58MoveLeftSpotID = 5061; +static const HotSpotID kMars58MoveRightSpotID = 5062; +static const HotSpotID kMars58ExtractSpotID = 5063; +static const HotSpotID kMars59RetractSpotID = 5064; +static const HotSpotID kMars60EastPressurizeSpotID = 5065; +static const HotSpotID kMars60EastSpinSpotID = 5066; +static const HotSpotID kMars60WestPressurizeSpotID = 5067; +static const HotSpotID kMars60WestSpinSpotID = 5068; +static const HotSpotID kRobotShuttleOpenHeadSpotID = 5069; +static const HotSpotID kRobotShuttleMapChipSpotID = 5070; +static const HotSpotID kRobotShuttleOpticalChipSpotID = 5071; +static const HotSpotID kRobotShuttleShieldChipSpotID = 5072; + +// Extra sequence IDs. + +static const ExtraID kMarsArrivalFromTSA = 0; +static const ExtraID kMars0AWatchShuttleDepart = 1; +static const ExtraID kRobotThrowsPlayer = 2; +static const ExtraID kMarsInfoKioskIntro = 3; +static const ExtraID kMarsColonyInfo = 4; +static const ExtraID kMarsSightsInfo = 5; +static const ExtraID kRobotOnWayToShuttle = 6; +static const ExtraID kMars31SouthZoomInNoCard = 7; +static const ExtraID kMars31SouthViewNoCard = 8; +static const ExtraID kMars31SouthZoomOutNoCard = 9; +static const ExtraID kMars31SouthZoomViewNoCard = 10; +static const ExtraID kMars33SlideShow1 = 11; +static const ExtraID kMars33SlideShow2 = 12; +static const ExtraID kMars33SlideShow3 = 13; +static const ExtraID kMars33SlideShow4 = 14; +static const ExtraID kMars34SpotOpenWithBar = 15; +static const ExtraID kMars34SpotCloseWithBar = 16; +static const ExtraID kMars34SpotOpenNoBar = 17; +static const ExtraID kMars34SpotCloseNoBar = 18; +static const ExtraID kMars34ViewOpenWithBar = 19; +static const ExtraID kMars34ViewOpenNoBar = 20; +static const ExtraID kMars34NorthPodGreeting = 21; +static const ExtraID kMarsTurnOnPod = 22; +static const ExtraID kMarsTakePodToMars45 = 23; +static const ExtraID kMars35WestSpinAirlockToEast = 24; +static const ExtraID kMars35EastSpinAirlockToWest = 25; +static const ExtraID kMars45SpotOpenWithBar = 26; +static const ExtraID kMars45SpotCloseWithBar = 27; +static const ExtraID kMars45SpotOpenNoBar = 28; +static const ExtraID kMars45SpotCloseNoBar = 29; +static const ExtraID kMars45ViewOpenWithBar = 30; +static const ExtraID kMars45ViewOpenNoBar = 31; +static const ExtraID kMars48RobotApproaches = 32; +static const ExtraID kMars48RobotKillsPlayer = 33; +static const ExtraID kMars48RobotLoops = 34; +static const ExtraID kMars48RobotView = 35; +static const ExtraID kMars48RobotDefends = 36; +static const ExtraID kMars49SouthViewMaskFilling = 37; +static const ExtraID kMars52SpinLeft = 38; +static const ExtraID kMars52SpinRight = 39; +static const ExtraID kMars52Extend = 40; +static const ExtraID kMars53Retract = 41; +static const ExtraID kMars54SpinLeft = 42; +static const ExtraID kMars54SpinRight = 43; +static const ExtraID kMars54Extend = 44; +static const ExtraID kMars55Retract = 45; +static const ExtraID kMars56SpinLeft = 46; +static const ExtraID kMars56SpinRight = 47; +static const ExtraID kMars56ExtendWithBomb = 48; +static const ExtraID kMars56ExtendNoBomb = 49; +static const ExtraID kMars57RetractWithBomb = 50; +static const ExtraID kMars57RetractNoBomb = 51; +static const ExtraID kMars57LowerScreenClosed = 52; +static const ExtraID kMars57CantOpenPanel = 53; +static const ExtraID kMars57FreezeLock = 54; +static const ExtraID kMars57BreakLock = 55; +static const ExtraID kMars57LockFrozenView = 56; +static const ExtraID kMars57ThawLock = 57; +static const ExtraID kMars57OpenPanel = 58; +static const ExtraID kMars57OpenPanelChoices = 59; +static const ExtraID kMars57ShieldEvaluation = 60; +static const ExtraID kMars57MeasureOutput = 61; +static const ExtraID kMars57ShieldOkayLoop = 62; +static const ExtraID kMars57RunDiagnostics = 63; +static const ExtraID kMars57BombExplodes = 64; +static const ExtraID kMars57BombAnalysis = 65; +static const ExtraID kMars57DontLink = 66; +static const ExtraID kMars57CircuitLink = 67; +static const ExtraID kMars57GameLevel1 = 68; +static const ExtraID kMars57GameLevel2 = 69; +static const ExtraID kMars57GameLevel3 = 70; +static const ExtraID kMars57BombExplodesInGame = 71; +static const ExtraID kMars57GameSolved = 72; +static const ExtraID kMars57ExposeBomb = 73; +static const ExtraID kMars57BackToNormal = 74; +static const ExtraID kMars57ViewOpenNoBomb = 75; +static const ExtraID kMars58SpinLeft = 76; +static const ExtraID kMars58SpinRight = 77; +static const ExtraID kMars58Extend = 78; +static const ExtraID kMars59Retract = 79; +static const ExtraID kMars60WestSpinAirlockToEast = 80; +static const ExtraID kMars60EastSpinAirlockToWest = 81; +static const ExtraID kMarsRobotHeadOpen = 82; +static const ExtraID kMarsRobotHeadClose = 83; +static const ExtraID kMarsRobotHead000 = 84; +static const ExtraID kMarsRobotHead001 = 85; +static const ExtraID kMarsRobotHead010 = 86; +static const ExtraID kMarsRobotHead011 = 87; +static const ExtraID kMarsRobotHead100 = 88; +static const ExtraID kMarsRobotHead101 = 89; +static const ExtraID kMarsRobotHead110 = 90; +static const ExtraID kMarsRobotHead111 = 91; +static const ExtraID kMarsMaze007RobotApproach = 92; +static const ExtraID kMarsMaze007RobotLoop = 93; +static const ExtraID kMarsMaze007RobotDeath = 94; +static const ExtraID kMarsMaze015SouthRobotApproach = 95; +static const ExtraID kMarsMaze015SouthRobotLoop = 96; +static const ExtraID kMarsMaze015SouthRobotDeath = 97; +static const ExtraID kMarsMaze101EastRobotApproach = 98; +static const ExtraID kMarsMaze101EastRobotLoop = 99; +static const ExtraID kMarsMaze101EastRobotDeath = 100; +static const ExtraID kMarsMaze104WestLoop = 101; +static const ExtraID kMarsMaze104WestDeath = 102; +static const ExtraID kMarsMaze133SouthApproach = 103; +static const ExtraID kMarsMaze133SouthLoop = 104; +static const ExtraID kMarsMaze133SouthDeath = 105; +static const ExtraID kMarsMaze136NorthApproach = 106; +static const ExtraID kMarsMaze136NorthLoop = 107; +static const ExtraID kMarsMaze136NorthDeath = 108; +static const ExtraID kMarsMaze184WestLoop = 109; +static const ExtraID kMarsMaze184WestDeath = 110; +static const ExtraID kMars200DeathInBucket = 111; + +static const ResIDType kReactorUndoHilitePICTID = 900; + +static const int16 kMars52Compass = 90; +static const int16 kMars54Compass = 180; +static const int16 kMars56Compass = 270; +static const int16 kMars58Compass = 0; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/mars/energybeam.cpp b/engines/pegasus/neighborhood/mars/energybeam.cpp new file mode 100644 index 0000000000..964c8ba381 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/energybeam.cpp @@ -0,0 +1,70 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/pegasus.h" +#include "pegasus/neighborhood/mars/energybeam.h" + +namespace Pegasus { + +static const TimeValue kEnergyBeamTime = kOneSecond * kShuttleWeaponScale / 2; + +static const CoordType kEnergyBeamOriginH = kShuttleWindowMidH; +static const CoordType kEnergyBeamOriginV = kShuttleWindowTop + kShuttleWindowHeight; + +static const float kBeamXOrigin = convertScreenHToSpaceX(kEnergyBeamOriginH, kEnergyBeamMinDistance); +static const float kBeamYOrigin = convertScreenVToSpaceY(kEnergyBeamOriginV, kEnergyBeamMinDistance); +static const float kBeamZOrigin = kEnergyBeamMinDistance; + +EnergyBeam::EnergyBeam() { + _weaponDuration = kEnergyBeamTime; + setSegment(0, kEnergyBeamTime); + _weaponOrigin = Point3D(kBeamXOrigin, kBeamYOrigin, kBeamZOrigin); +} + +void EnergyBeam::draw(const Common::Rect &) { + static const int kBeamColorRed1 = 224; + static const int kBeamColorRed2 = 64; + + Graphics::Surface *surface = ((PegasusEngine *)g_engine)->_gfx->getWorkArea(); + + byte red = linearInterp(0, kEnergyBeamTime, _lastTime, kBeamColorRed1, kBeamColorRed2); + uint32 color = surface->format.RGBToColor(red, 0, 0); + + Point3D startPoint; + if (_weaponTime < 0.1) + startPoint = _weaponOrigin; + else + linearInterp(_weaponOrigin, _weaponTarget, _weaponTime - 0.1, startPoint); + + Common::Point lineStart; + project3DTo2D(startPoint, lineStart); + + Common::Point lineEnd; + project3DTo2D(_weaponLocation, lineEnd); + + surface->drawThickLine(lineStart.x, lineStart.y, lineEnd.x, lineEnd.y, 2, 1, color); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/mars/energybeam.h b/engines/pegasus/neighborhood/mars/energybeam.h new file mode 100644 index 0000000000..715ed4b01d --- /dev/null +++ b/engines/pegasus/neighborhood/mars/energybeam.h @@ -0,0 +1,43 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_NEIGHBORHOOD_MARS_ENERGYBEAM_H +#define PEGASUS_NEIGHBORHOOD_MARS_ENERGYBEAM_H + +#include "pegasus/neighborhood/mars/shuttleweapon.h" + +namespace Pegasus { + +class EnergyBeam : public ShuttleWeapon { +public: + EnergyBeam(); + virtual ~EnergyBeam() {} + + void draw(const Common::Rect &); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/mars/gravitoncannon.cpp b/engines/pegasus/neighborhood/mars/gravitoncannon.cpp new file mode 100644 index 0000000000..d04b3d08b2 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/gravitoncannon.cpp @@ -0,0 +1,134 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/neighborhood/mars/gravitoncannon.h" +#include "pegasus/neighborhood/mars/robotship.h" +#include "pegasus/neighborhood/mars/spacejunk.h" + +namespace Pegasus { + +static const TimeValue kGravitonTime = kOneSecond * kShuttleWeaponScale; + +static const CoordType kGravitonOriginH = kShuttleWindowLeft - 1; +static const CoordType kGravitonOriginV = kShuttleWindowMidV; + +static const float kGravitonXOrigin = convertScreenHToSpaceX(kGravitonOriginH, kGravitonMinDistance); +static const float kGravitonYOrigin = convertScreenVToSpaceY(kGravitonOriginV, kGravitonMinDistance); +static const float kGravitonZOrigin = kGravitonMinDistance; + +// Width of graviton sprite... +static const CoordType kGravitonMaxScreenWidth = 78; +static const CoordType kGravitonMaxScreenHeight = 46; + +static const float kGravitonWidth = convertScreenHToSpaceX(kShuttleWindowMidH + kGravitonMaxScreenWidth / 2, kGravitonMinDistance) + - convertScreenHToSpaceX(kShuttleWindowMidH - kGravitonMaxScreenWidth / 2, kGravitonMinDistance); +static const float kGravitonHeight = convertScreenVToSpaceY(kShuttleWindowMidV - kGravitonMaxScreenHeight / 2, kGravitonMinDistance) + - convertScreenVToSpaceY(kShuttleWindowMidV + kGravitonMaxScreenHeight / 2, kGravitonMinDistance); + +GravitonCannon::GravitonCannon() { + _weaponDuration = kGravitonTime; + setSegment(0, kGravitonTime); + _weaponOrigin = Point3D(kGravitonXOrigin, kGravitonYOrigin, kGravitonZOrigin); + _rightOrigin = Point3D(-kGravitonXOrigin, kGravitonYOrigin, kGravitonZOrigin); +} + +void GravitonCannon::initShuttleWeapon() { + ShuttleWeapon::initShuttleWeapon(); + _gravitonImage.getImageFromPICTFile("Images/Mars/Graviton Cannon"); + _gravitonImage.getSurfaceBounds(_gravitonBounds); +} + +void GravitonCannon::cleanUpShuttleWeapon() { + _gravitonImage.deallocateSurface(); + ShuttleWeapon::cleanUpShuttleWeapon(); +} + +void GravitonCannon::draw(const Common::Rect &) { + // Left graviton... + Point3D pt3D = _weaponLocation; + pt3D.translate(-kGravitonWidth / 2, kGravitonHeight / 2, 0); + Common::Point pt2D; + project3DTo2D(pt3D, pt2D); + Common::Rect gravitonRect; + gravitonRect.left = pt2D.x; + gravitonRect.top = pt2D.y; + + pt3D.translate(kGravitonWidth, -kGravitonHeight, 0); + project3DTo2D(pt3D, pt2D); + gravitonRect.right = pt2D.x; + gravitonRect.bottom = pt2D.y; + + _gravitonImage.scaleTransparentCopy(_gravitonBounds, gravitonRect); + + // Right graviton... + pt3D = _rightLocation; + pt3D.translate(-kGravitonWidth / 2, kGravitonHeight / 2, 0); + project3DTo2D(pt3D, pt2D); + gravitonRect.left = pt2D.x; + gravitonRect.top = pt2D.y; + + pt3D.translate(kGravitonWidth, -kGravitonHeight, 0); + project3DTo2D(pt3D, pt2D); + gravitonRect.right = pt2D.x; + gravitonRect.bottom = pt2D.y; + + _gravitonImage.scaleTransparentCopy(_gravitonBounds, gravitonRect); +} + +void GravitonCannon::updateWeaponPosition() { + ShuttleWeapon::updateWeaponPosition(); + if (_weaponTime != 1.0) + linearInterp(_rightOrigin, _weaponTarget, _weaponTime, _rightLocation); +} + +bool GravitonCannon::collisionWithJunk(Common::Point &impactPoint) { + if (getDisplayOrder() == kShuttleWeaponFrontOrder) { + Point3D junkPosition; + g_spaceJunk->getJunkPosition(junkPosition); + + if (junkPosition.z < _weaponLocation.z) { + setDisplayOrder(kShuttleWeaponBackOrder); + project3DTo2D(_weaponLocation, impactPoint); + + if (g_spaceJunk->pointInJunk(impactPoint)) + return true; + + project3DTo2D(_rightLocation, impactPoint); + return g_spaceJunk->pointInJunk(impactPoint); + } + } + + return false; +} + +void GravitonCannon::hitJunk(Common::Point impactPoint) { + g_spaceJunk->hitByGravitonCannon(impactPoint); +} + +void GravitonCannon::hitShuttle(Common::Point impactPoint) { + g_robotShip->hitByGravitonCannon(impactPoint); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/mars/gravitoncannon.h b/engines/pegasus/neighborhood/mars/gravitoncannon.h new file mode 100644 index 0000000000..b94fd55e5b --- /dev/null +++ b/engines/pegasus/neighborhood/mars/gravitoncannon.h @@ -0,0 +1,57 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_NEIGHBORHOOD_MARS_GRAVITONCANNON_H +#define PEGASUS_NEIGHBORHOOD_MARS_GRAVITONCANNON_H + +#include "pegasus/surface.h" +#include "pegasus/neighborhood/mars/shuttleweapon.h" + +namespace Pegasus { + +class GravitonCannon : public ShuttleWeapon { +public: + GravitonCannon(); + virtual ~GravitonCannon() {} + + void initShuttleWeapon(); + void cleanUpShuttleWeapon(); + + void draw(const Common::Rect &); + +protected: + virtual void updateWeaponPosition(); + virtual bool collisionWithJunk(Common::Point &impactPoint); + virtual void hitJunk(Common::Point impactPoint); + virtual void hitShuttle(Common::Point impactPoint); + + Surface _gravitonImage; + Common::Rect _gravitonBounds; + Point3D _rightOrigin, _rightLocation; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/mars/hermite.cpp b/engines/pegasus/neighborhood/mars/hermite.cpp new file mode 100644 index 0000000000..7f631b369d --- /dev/null +++ b/engines/pegasus/neighborhood/mars/hermite.cpp @@ -0,0 +1,76 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/neighborhood/mars/hermite.h" + +namespace Pegasus { + +CoordType hermite(CoordType p1, CoordType p4, CoordType r1, CoordType r4, int32 time, int32 duration) { + float t = (float)time / duration; + float tsq = t * t; + float tcu = t * tsq; + float tcu2 = tcu + tcu; + float tsq2 = tsq + tsq; + float tsq3 = tsq2 + tsq; + return (CoordType)((tcu2 - tsq3 + 1) * p1 + (tsq3 - tcu2) * p4 + (tcu - tsq2 + t) * r1 + (tcu - tsq) * r4); +} + +CoordType dHermite(CoordType p1, CoordType p4, CoordType r1, CoordType r4, int32 time, int32 duration) { + float t = (float)time / duration; + float t2 = t + t; + float t4 = t2 + t2; + float t6 = t4 + t2; + float tsq = t * t; + float tsq3 = tsq + tsq + tsq; + float tsq6 = tsq3 + tsq3; + return (CoordType)((tsq6 - t6) * p1 + (t6 - tsq6) * p4 + (tsq3 - t4 + 1) * r1 + (tsq3 - t2) * r4); +} + +void hermite(Common::Point p1, Common::Point p4, Common::Point r1, Common::Point r4, int32 time, int32 duration, Common::Point &result) { + float t = (float)time / duration; + float tsq = t * t; + float tcu = t * tsq; + float tcu2 = tcu + tcu; + float tsq2 = tsq + tsq; + float tsq3 = tsq2 + tsq; + + result.x = (int16)((tcu2 - tsq3 + 1) * p1.x + (tsq3 - tcu2) * p4.x + (tcu - tsq2 + t) * r1.x + (tcu - tsq) * r4.x); + result.y = (int16)((tcu2 - tsq3 + 1) * p1.y + (tsq3 - tcu2) * p4.y + (tcu - tsq2 + t) * r1.y + (tcu - tsq) * r4.y); +} + +void dHermite(Common::Point p1, Common::Point p4, Common::Point r1, Common::Point r4, int32 time, int32 duration, Common::Point &result) { + float t = (float)time / duration; + float t2 = t + t; + float t4 = t2 + t2; + float t6 = t4 + t2; + float tsq = t * t; + float tsq3 = tsq + tsq + tsq; + float tsq6 = tsq3 + tsq3; + + result.x = (int16)((tsq6 - t6) * p1.x + (t6 - tsq6) * p4.x + (tsq3 - t4 + 1) * r1.x + (tsq3 - t2) * r4.x); + result.y = (int16)((tsq6 - t6) * p1.y + (t6 - tsq6) * p4.y + (tsq3 - t4 + 1) * r1.y + (tsq3 - t2) * r4.y); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/mars/hermite.h b/engines/pegasus/neighborhood/mars/hermite.h new file mode 100644 index 0000000000..44cb3a5a11 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/hermite.h @@ -0,0 +1,41 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_NEIGHBORHOOD_MARS_HERMITE_H +#define PEGASUS_NEIGHBORHOOD_MARS_HERMITE_H + +#include "common/rect.h" +#include "pegasus/types.h" + +namespace Pegasus { + +CoordType hermite(CoordType p1, CoordType p4, CoordType r1, CoordType r4, int32 t, int32 duration); +CoordType dHermite(CoordType p1, CoordType p4, CoordType r1, CoordType r4, int32 t, int32 duration); +void hermite(Common::Point p1, Common::Point p4, Common::Point r1, Common::Point r4, int32 t, int32 duration, Common::Point &result); +void dHermite(Common::Point p1, Common::Point p4, Common::Point r1, Common::Point r4, int32 t, int32 duration, Common::Point &result); + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/mars/mars.cpp b/engines/pegasus/neighborhood/mars/mars.cpp new file mode 100644 index 0000000000..34c9e3d0f8 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/mars.cpp @@ -0,0 +1,3735 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "common/events.h" +#include "video/qt_decoder.h" + +#include "pegasus/cursor.h" +#include "pegasus/energymonitor.h" +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/biochips/opticalchip.h" +#include "pegasus/items/biochips/shieldchip.h" +#include "pegasus/items/inventory/airmask.h" +#include "pegasus/neighborhood/mars/mars.h" + +namespace Pegasus { + +// This should really be 22.5. +// Probably no one will know the difference. +static const int16 kMarsShieldPanelOffsetAngle = 22; + +static const CanMoveForwardReason kCantMoveRobotBlocking = kCantMoveLastReason + 1; + +static const NotificationFlags kTimeForCanyonChaseFlag = kLastNeighborhoodNotificationFlag << 1; +static const NotificationFlags kExplosionFinishedFlag = kTimeForCanyonChaseFlag << 1; +static const NotificationFlags kTimeToTransportFlag = kExplosionFinishedFlag << 1; + +static const NotificationFlags kMarsNotificationFlags = kTimeForCanyonChaseFlag | + kExplosionFinishedFlag | + kTimeToTransportFlag; + +static const TimeValue kLittleExplosionStart = 0 * 40; +static const TimeValue kLittleExplosionStop = 24 * 40; + +static const TimeValue kBigExplosionStart = 24 * 40; +static const TimeValue kBigExplosionStop = 62 * 40; + +enum { + kMaze007RobotLoopingEvent, + kMaze015RobotLoopingEvent, + kMaze101RobotLoopingEvent, + kMaze104RobotLoopingEvent, + kMaze133RobotLoopingEvent, + kMaze136RobotLoopingEvent, + kMaze184RobotLoopingEvent +}; + +enum { + kMaze007RobotLoopingTime = (64 + 96) * kMarsFrameDuration, + kMaze015RobotLoopingTime = (64 + 93) * kMarsFrameDuration, + kMaze101RobotLoopingTime = (64 + 45) * kMarsFrameDuration, + kMaze104RobotLoopingTime = 96 * kMarsFrameDuration, + kMaze133RobotLoopingTime = (64 + 96) * kMarsFrameDuration, + kMaze136RobotLoopingTime = (64 + 96) * kMarsFrameDuration, + kMaze184RobotLoopingTime = 96 * kMarsFrameDuration +}; + +// I've made a couple macros for these rects so we don't +// have to globally construct them or whatnot +#define kShuttleEnergyBeamBounds Common::Rect(24, 27, 24 + 112, 27 + 46) +#define kShuttleGravitonBounds Common::Rect(24, 73, 24 + 112, 73 + 30) +#define kShuttleTractorBounds Common::Rect(24, 103, 24 + 112, 103 + 30) +#define kShuttleTransportBounds Common::Rect(484, 353, 89 + 484, 79 + 353) + +void MarsTimerEvent::fire() { + mars->marsTimerExpired(*this); +} + +Mars::Mars(InputHandler *nextHandler, PegasusEngine *owner) : Neighborhood(nextHandler, owner, "Mars", kMarsID), + _guessObject(kNoDisplayElement), _undoPict(kNoDisplayElement), _guessHistory(kNoDisplayElement), + _choiceHighlight(kNoDisplayElement), _shuttleInterface1(kNoDisplayElement), _shuttleInterface2(kNoDisplayElement), + _shuttleInterface3(kNoDisplayElement), _shuttleInterface4(kNoDisplayElement), _canyonChaseMovie(kNoDisplayElement), + _leftShuttleMovie(kNoDisplayElement), _rightShuttleMovie(kNoDisplayElement), _lowerLeftShuttleMovie(kNoDisplayElement), + _lowerRightShuttleMovie(kNoDisplayElement), _centerShuttleMovie(kNoDisplayElement), + _upperLeftShuttleMovie(kNoDisplayElement), _upperRightShuttleMovie(kNoDisplayElement), + _leftDamageShuttleMovie(kNoDisplayElement), _rightDamageShuttleMovie(kNoDisplayElement), _explosions(kNoDisplayElement), + _planetMovie(kNoDisplayElement), _junk(kNoDisplayElement), _energyChoiceSpot(kShuttleEnergySpotID), + _gravitonChoiceSpot(kShuttleGravitonSpotID), _tractorChoiceSpot(kShuttleTractorSpotID), + _shuttleViewSpot(kShuttleViewSpotID), _shuttleTransportSpot(kShuttleTransportSpotID) { + _noAirFuse.setFunctor(new Common::Functor0Mem<void, Mars>(this, &Mars::airStageExpired)); + setIsItemTaken(kMarsCard); + setIsItemTaken(kAirMask); + setIsItemTaken(kCrowbar); + setIsItemTaken(kCardBomb); +} + +Mars::~Mars() { + _vm->getAllHotspots().remove(&_energyChoiceSpot); + _vm->getAllHotspots().remove(&_gravitonChoiceSpot); + _vm->getAllHotspots().remove(&_tractorChoiceSpot); + _vm->getAllHotspots().remove(&_shuttleViewSpot); + _vm->getAllHotspots().remove(&_shuttleTransportSpot); +} + +void Mars::init() { + Neighborhood::init(); + + Hotspot *attackSpot = _vm->getAllHotspots().findHotspotByID(kAttackRobotHotSpotID); + attackSpot->setMaskedHotspotFlags(kDropItemSpotFlag, kDropItemSpotFlag); + _attackingItem = NULL; + + forceStridingStop(kMars08, kNorth, kAltMarsNormal); + + _neighborhoodNotification.notifyMe(this, kMarsNotificationFlags, kMarsNotificationFlags); + + _explosionCallBack.setNotification(&_neighborhoodNotification); + _explosionCallBack.setCallBackFlag(kExplosionFinishedFlag); + + _weaponSelection = kNoWeapon; +} + +void Mars::flushGameState() { + g_energyMonitor->saveCurrentEnergyValue(); +} + +void Mars::start() { + g_energyMonitor->stopEnergyDraining(); + g_energyMonitor->restoreLastEnergyValue(); + _vm->resetEnergyDeathReason(); + g_energyMonitor->startEnergyDraining(); + Neighborhood::start(); +} + +class AirMaskCondition : public AICondition { +public: + AirMaskCondition(const uint32); + + virtual bool fireCondition(); + +protected: + uint32 _airThreshold; + uint32 _lastAirLevel; +}; + +AirMaskCondition::AirMaskCondition(const uint32 airThreshold) { + _airThreshold = airThreshold; + _lastAirLevel = g_airMask->getAirLeft(); +} + +bool AirMaskCondition::fireCondition() { + bool result = g_airMask && g_airMask->isAirMaskOn() && + g_airMask->getAirLeft() <= _airThreshold && _lastAirLevel > _airThreshold; + + _lastAirLevel = g_airMask->getAirLeft(); + return result; +} + +void Mars::setUpAIRules() { + Neighborhood::setUpAIRules(); + + // Don't add these rules if we're going to the robot's shuttle... + if (g_AIArea && !GameState.getMarsReadyForShuttleTransport()) { + AIPlayMessageAction *messageAction = new AIPlayMessageAction("Images/AI/Globals/XGLOB1E", false); + AILocationCondition *locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kMars47, kSouth)); + AIRule *rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Mars/XM27NB", false); + locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kMars27, kNorth)); + rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Mars/XM27NB", false); + locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kMars28, kNorth)); + rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Mars/XM41ED", false); + locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kMars19, kEast)); + rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + + AIDeactivateRuleAction *deactivate = new AIDeactivateRuleAction(rule); + locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kMars35, kWest)); + rule = new AIRule(locCondition, deactivate); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Mars/XM41ED", false); + locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kMars48, kWest)); + rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + + AirMaskCondition *airMask50Condition = new AirMaskCondition(50); + messageAction = new AIPlayMessageAction("Images/AI/Mars/XMMAZB1", false); + AIRule *rule50 = new AIRule(airMask50Condition, messageAction); + + AirMaskCondition *airMask25Condition = new AirMaskCondition(25); + AICompoundAction *compound = new AICompoundAction(); + messageAction = new AIPlayMessageAction("Images/AI/Mars/XMMAZB2", false); + compound->addAction(messageAction); + deactivate = new AIDeactivateRuleAction(rule50); + compound->addAction(deactivate); + AIRule *rule25 = new AIRule(airMask25Condition, compound); + + AirMaskCondition *airMask5Condition = new AirMaskCondition(5); + compound = new AICompoundAction; + messageAction = new AIPlayMessageAction("Images/AI/Mars/XMMAZB3", false); + compound->addAction(messageAction); + deactivate = new AIDeactivateRuleAction(rule50); + compound->addAction(deactivate); + deactivate = new AIDeactivateRuleAction(rule25); + compound->addAction(deactivate); + AIRule *rule5 = new AIRule(airMask5Condition, compound); + + g_AIArea->addAIRule(rule5); + g_AIArea->addAIRule(rule25); + g_AIArea->addAIRule(rule50); + + messageAction = new AIPlayMessageAction("Images/AI/Mars/XM51ND", false); + AIDoorOpenedCondition *doorOpen = new AIDoorOpenedCondition(MakeRoomView(kMars51, kEast)); + rule = new AIRule(doorOpen, messageAction); + g_AIArea->addAIRule(rule); + } +} + +uint16 Mars::getDateResID() const { + return kDate2185ID; +} + +TimeValue Mars::getViewTime(const RoomID room, const DirectionConstant direction) { + ExtraTable::Entry extra; + SpotTable::Entry spotEntry; + uint32 extraID = 0xffffffff; + + switch (MakeRoomView(room, direction)) { + case MakeRoomView(kMars0A, kNorth): + if (!GameState.getMarsSeenTimeStream()) { + getExtraEntry(kMarsArrivalFromTSA, extra); + return extra.movieStart; + } + break; + case MakeRoomView(kMars31South, kSouth): + if (GameState.isTakenItemID(kMarsCard)) + extraID = kMars31SouthZoomViewNoCard; + break; + case MakeRoomView(kMars31, kSouth): + if (GameState.isTakenItemID(kMarsCard)) + extraID = kMars31SouthViewNoCard; + break; + case MakeRoomView(kMars34, kSouth): + if (_privateFlags.getFlag(kMarsPrivatePodStorageOpenFlag)) { + if (GameState.isTakenItemID(kCrowbar)) + extraID = kMars34ViewOpenNoBar; + else + extraID = kMars34ViewOpenWithBar; + } + break; + case MakeRoomView(kMars36, kSouth): + case MakeRoomView(kMars37, kSouth): + case MakeRoomView(kMars38, kSouth): + findSpotEntry(room, direction, kSpotOnTurnMask | kSpotLoopsMask, spotEntry); + return spotEntry.movieStart; + case MakeRoomView(kMars45, kNorth): + if (_privateFlags.getFlag(kMarsPrivatePodStorageOpenFlag)) { + if (GameState.isTakenItemID(kCrowbar)) + extraID = kMars45ViewOpenNoBar; + else + extraID = kMars45ViewOpenWithBar; + } + break; + case MakeRoomView(kMars48, kEast): + if (GameState.getMarsSeenRobotAtReactor() && !GameState.getMarsAvoidedReactorRobot()) + extraID = kMars48RobotView; + break; + case MakeRoomView(kMars56, kEast): + if (_privateFlags.getFlag(kMarsPrivateBombExposedFlag)) { + if (_privateFlags.getFlag(kMarsPrivateDraggingBombFlag)) + extraID = kMars57ViewOpenNoBomb; + else + extraID = kMars57ExposeBomb; + } else if (GameState.getMarsLockBroken()) { + extraID = kMars57OpenPanelChoices; + } else if (GameState.getMarsLockFrozen()) { + extraID = kMars57LockFrozenView; + } + break; + case MakeRoomView(kMarsRobotShuttle, kEast): + if (getCurrentActivation() == kActivationRobotHeadOpen) { + extraID = kMarsRobotHead111; + + if (_privateFlags.getFlag(kMarsPrivateGotMapChipFlag)) + extraID -= 1; + if (_privateFlags.getFlag(kMarsPrivateGotOpticalChipFlag)) + extraID -= 2; + if (_privateFlags.getFlag(kMarsPrivateGotShieldChipFlag)) + extraID -= 4; + } + break; + } + + if (extraID == 0xffffffff) + return Neighborhood::getViewTime(room, direction); + + getExtraEntry(extraID, extra); + return extra.movieEnd - 1; +} + +void Mars::getZoomEntry(const HotSpotID spotID, ZoomTable::Entry &entry) { + Neighborhood::getZoomEntry(spotID, entry); + + uint32 extraID = 0xffffffff; + + switch (spotID) { + case kMars31SouthSpotID: + if (GameState.getCurrentDirection() == kSouth && GameState.isTakenItemID(kMarsCard)) + extraID = kMars31SouthZoomInNoCard; + break; + case kMars31SouthOutSpotID: + if (GameState.getCurrentDirection() == kSouth && GameState.isTakenItemID(kMarsCard)) + extraID = kMars31SouthZoomOutNoCard; + break; + } + + if (extraID != 0xffffffff) { + ExtraTable::Entry extra; + getExtraEntry(extraID, extra); + entry.movieStart = extra.movieStart; + entry.movieEnd = extra.movieEnd; + } +} + +void Mars::findSpotEntry(const RoomID room, const DirectionConstant direction, SpotFlags flags, SpotTable::Entry &entry) { + Neighborhood::findSpotEntry(room, direction, flags, entry); + + if ((flags & (kSpotOnArrivalMask | kSpotOnTurnMask)) != 0) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kMars27, kNorth): + if (GameState.getMarsSeenThermalScan()) + entry.clear(); + else + GameState.setMarsSeenThermalScan(true); + break; + case MakeRoomView(kMars28, kNorth): + if (GameState.getMarsSeenThermalScan()) + entry.clear(); + else + GameState.setMarsSeenThermalScan(true); + break; + } + } +} + +CanMoveForwardReason Mars::canMoveForward(ExitTable::Entry &entry) { + CanMoveForwardReason reason = Neighborhood::canMoveForward(entry); + + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kMars48, kEast): + if (GameState.getMarsSeenRobotAtReactor() && !GameState.getMarsAvoidedReactorRobot()) + reason = kCantMoveRobotBlocking; + break; + case MakeRoomView(kMars48, kSouth): + if (GameState.getMarsSeenRobotAtReactor() && !GameState.getMarsAvoidedReactorRobot()) + _utilityFuse.stopFuse(); + break; + } + + return reason; +} + +void Mars::cantMoveThatWay(CanMoveForwardReason reason) { + if (reason == kCantMoveRobotBlocking) { + startExtraSequence(kMars48RobotKillsPlayer, kExtraCompletedFlag, kFilterNoInput); + loadLoopSound2(""); + } else { + Neighborhood::cantMoveThatWay(reason); + } +} + +void Mars::moveForward() { + if (GameState.getCurrentRoom() == kMars02 || (GameState.getCurrentRoom() >= kMars05 && GameState.getCurrentRoom() <= kMars08)) + loadLoopSound2(""); + + Neighborhood::moveForward(); +} + +void Mars::bumpIntoWall() { + requestSpotSound(kMarsBumpIntoWallIn, kMarsBumpIntoWallOut, kFilterNoInput, 0); + Neighborhood::bumpIntoWall(); +} + +CanOpenDoorReason Mars::canOpenDoor(DoorTable::Entry &entry) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kMars05, kEast): + case MakeRoomView(kMars06, kEast): + case MakeRoomView(kMars07, kEast): + if (!GameState.getMarsSecurityDown()) + return kCantOpenLocked; + break; + case MakeRoomView(kMarsMaze037, kWest): + case MakeRoomView(kMarsMaze038, kEast): + if (GameState.getMarsMazeDoorPair1()) + return kCantOpenLocked; + break; + case MakeRoomView(kMarsMaze050, kNorth): + case MakeRoomView(kMarsMaze058, kSouth): + if (!GameState.getMarsMazeDoorPair1()) + return kCantOpenLocked; + break; + case MakeRoomView(kMarsMaze047, kNorth): + case MakeRoomView(kMarsMaze142, kSouth): + if (GameState.getMarsMazeDoorPair2()) + return kCantOpenLocked; + break; + case MakeRoomView(kMarsMaze057, kNorth): + case MakeRoomView(kMarsMaze136, kSouth): + if (!GameState.getMarsMazeDoorPair2()) + return kCantOpenLocked; + break; + case MakeRoomView(kMarsMaze120, kWest): + case MakeRoomView(kMarsMaze121, kEast): + if (GameState.getMarsMazeDoorPair3()) + return kCantOpenLocked; + break; + case MakeRoomView(kMarsMaze081, kNorth): + case MakeRoomView(kMarsMaze083, kSouth): + if (!GameState.getMarsMazeDoorPair3()) + return kCantOpenLocked; + break; + } + + return Neighborhood::canOpenDoor(entry); +} + +void Mars::cantOpenDoor(CanOpenDoorReason reason) { + switch (GameState.getCurrentRoom()) { + case kMars05: + case kMars06: + case kMars07: + playSpotSoundSync(kMarsCantOpenShuttleIn, kMarsCantOpenShuttleOut); + break; + default: + Neighborhood::cantOpenDoor(reason); + break; + } +} + +void Mars::openDoor() { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kMars06, kEast): + case MakeRoomView(kMars07, kEast): + if (GameState.getMarsSecurityDown()) + playSpotSoundSync(kMarsNoShuttleIn, kMarsNoShuttleOut); + break; + case MakeRoomView(kMars47, kSouth): + if (GameState.isTakenItemID(kAirMask)) + setCurrentAlternate(kAltMarsTookMask); + else + setCurrentAlternate(kAltMarsNormal); + break; + case MakeRoomView(kMars48, kNorth): + if (GameState.getMarsPodAtUpperPlatform()) + setCurrentAlternate(kAltMarsNormal); + else + setCurrentAlternate(kAltMarsPodAtMars45); + break; + case MakeRoomView(kMars48, kEast): + if (GameState.getMarsSeenRobotAtReactor() && !GameState.getMarsAvoidedReactorRobot()) { + die(kDeathDidntGetOutOfWay); + return; + } + break; + } + + Neighborhood::openDoor(); +} + +void Mars::doorOpened() { + switch (GameState.getCurrentRoom()) { + case kMars27: + case kMars28: + if (GameState.getCurrentDirection() == kNorth) + _vm->die(kDeathArrestedInMars); + else + Neighborhood::doorOpened(); + break; + case kMars41: + case kMars42: + if (GameState.getCurrentDirection() == kEast) + _vm->die(kDeathWrongShuttleLock); + else + Neighborhood::doorOpened(); + break; + case kMars51: + Neighborhood::doorOpened(); + setUpReactorEnergyDrain(); + + if (g_AIArea) + g_AIArea->checkRules(); + break; + case kMars19: + if (GameState.getCurrentDirection() == kEast) + GameState.setMarsAirlockOpen(true); + + Neighborhood::doorOpened(); + break; + case kMars48: + if (GameState.getCurrentDirection() == kWest) + GameState.setMarsAirlockOpen(true); + + Neighborhood::doorOpened(); + break; + default: + Neighborhood::doorOpened(); + break; + } +} + +void Mars::setUpReactorEnergyDrain() { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kMars51, kEast): + if (GameState.isCurrentDoorOpen()) { + if (g_energyMonitor->getEnergyDrainRate() == kEnergyDrainNormal) { + if (GameState.getShieldOn()) { + g_shield->setItemState(kShieldRadiation); + g_energyMonitor->setEnergyDrainRate(kMarsReactorEnergyDrainWithShield); + } else { + g_energyMonitor->setEnergyDrainRate(kMarsReactorEnergyDrainNoShield); + } + _vm->setEnergyDeathReason(kDeathReactorBurn); + } + } else { + if (g_energyMonitor->getEnergyDrainRate() != kEnergyDrainNormal) { + if (GameState.getShieldOn()) + g_shield->setItemState(kShieldNormal); + g_energyMonitor->setEnergyDrainRate(kEnergyDrainNormal); + _vm->resetEnergyDeathReason(); + } + } + break; + case MakeRoomView(kMars52, kNorth): + case MakeRoomView(kMars52, kSouth): + case MakeRoomView(kMars52, kEast): + case MakeRoomView(kMars52, kWest): + case MakeRoomView(kMars54, kNorth): + case MakeRoomView(kMars54, kSouth): + case MakeRoomView(kMars54, kEast): + case MakeRoomView(kMars54, kWest): + case MakeRoomView(kMars56, kNorth): + case MakeRoomView(kMars56, kSouth): + case MakeRoomView(kMars56, kEast): + case MakeRoomView(kMars56, kWest): + case MakeRoomView(kMars58, kNorth): + case MakeRoomView(kMars58, kSouth): + case MakeRoomView(kMars58, kEast): + case MakeRoomView(kMars58, kWest): + if (g_energyMonitor->getEnergyDrainRate() == kEnergyDrainNormal) { + if (GameState.getShieldOn()) { + g_shield->setItemState(kShieldRadiation); + g_energyMonitor->setEnergyDrainRate(kMarsReactorEnergyDrainWithShield); + } else { + g_energyMonitor->setEnergyDrainRate(kMarsReactorEnergyDrainNoShield); + } + _vm->setEnergyDeathReason(kDeathReactorBurn); + } + break; + default: + if (g_energyMonitor->getEnergyDrainRate() != kEnergyDrainNormal) { + if (GameState.getShieldOn()) + g_shield->setItemState(kShieldNormal); + g_energyMonitor->setEnergyDrainRate(kEnergyDrainNormal); + _vm->resetEnergyDeathReason(); + } + break; + } +} + +void Mars::closeDoorOffScreen(const RoomID room, const DirectionConstant direction) { + switch (room) { + case kMars51: + playSpotSoundSync(kMarsGantryDoorCloseIn, kMarsGantryDoorCloseOut); + if (GameState.getShieldOn()) + g_shield->setItemState(kShieldNormal); + g_energyMonitor->setEnergyDrainRate(kEnergyDrainNormal); + _vm->resetEnergyDeathReason(); + break; + case kMars05: + case kMars06: + case kMars07: + case kMars13: + case kMars22: + case kMars47: + case kMars52: + playSpotSoundSync(kMarsGantryDoorCloseIn, kMarsGantryDoorCloseOut); + break; + case kMars18: + case kMars32: + playSpotSoundSync(kMarsTransportDoorCloseIn, kMarsTransportDoorCloseOut); + break; + case kMars19: + if (GameState.getCurrentRoom() != kMars35) { + playSpotSoundSync(kMarsBigAirlockDoorCloseIn, kMarsBigAirlockDoorCloseOut); + GameState.setMarsAirlockOpen(false); + } + break; + case kMars36: + if (GameState.getCurrentRoom() != kMars35) + playSpotSoundSync(kMarsSmallAirlockDoorCloseIn, kMarsSmallAirlockDoorCloseOut); + break; + case kMars48: + if (direction == kWest) { + if (GameState.getCurrentRoom() != kMars60) { + playSpotSoundSync(kMarsSmallAirlockDoorCloseIn, kMarsSmallAirlockDoorCloseOut); + GameState.setMarsAirlockOpen(false); + } + } else { + playSpotSoundSync(kMarsGantryDoorCloseIn, kMarsGantryDoorCloseOut); + } + break; + case kMars41: + case kMars42: + case kMars43: + if (direction == kWest) + playSpotSoundSync(kMarsGantryDoorCloseIn, kMarsGantryDoorCloseOut); + break; + case kMarsMaze037: + case kMarsMaze038: + case kMarsMaze012: + case kMarsMaze066: + case kMarsMaze050: + case kMarsMaze058: + case kMarsMaze057: + case kMarsMaze136: + case kMarsMaze047: + case kMarsMaze142: + case kMarsMaze133: + case kMarsMaze132: + case kMarsMaze113: + case kMarsMaze114: + case kMarsMaze120: + case kMarsMaze121: + case kMarsMaze081: + case kMarsMaze083: + case kMarsMaze088: + case kMarsMaze089: + case kMarsMaze179: + case kMarsMaze180: + playSpotSoundSync(kMarsMazeDoorCloseIn, kMarsMazeDoorCloseOut); + break; + } +} + +void Mars::checkAirlockDoors() { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kMars19, kWest): + case MakeRoomView(kMars18, kWest): + case MakeRoomView(kMars17, kWest): + case MakeRoomView(kMars16, kWest): + case MakeRoomView(kMars15, kWest): + case MakeRoomView(kMars14, kWest): + case MakeRoomView(kMars12, kWest): + case MakeRoomView(kMars11, kWest): + case MakeRoomView(kMars10, kWest): + if (GameState.getMarsInAirlock()) { + playSpotSoundSync(kMarsBigAirlockDoorCloseIn, kMarsBigAirlockDoorCloseOut); + GameState.setMarsInAirlock(false); + } + break; + case MakeRoomView(kMars36, kEast): + case MakeRoomView(kMars37, kEast): + case MakeRoomView(kMars38, kEast): + case MakeRoomView(kMars39, kEast): + case MakeRoomView(kMars48, kEast): + case MakeRoomView(kMars50, kEast): + case MakeRoomView(kMars51, kEast): + case MakeRoomView(kMars52, kEast): + if (GameState.getMarsInAirlock()) { + playSpotSoundSync(kMarsSmallAirlockDoorCloseIn, kMarsSmallAirlockDoorCloseOut); + GameState.setMarsInAirlock(false); + } + break; + case MakeRoomView(kMars35, kWest): + case MakeRoomView(kMars35, kEast): + case MakeRoomView(kMars60, kWest): + case MakeRoomView(kMars60, kEast): + GameState.setMarsInAirlock(true); + break; + default: + GameState.setMarsInAirlock(false); + break; + } +} + +int16 Mars::getStaticCompassAngle(const RoomID room, const DirectionConstant dir) { + int16 angle = Neighborhood::getStaticCompassAngle(room, dir); + + switch (MakeRoomView(room, dir)) { + case MakeRoomView(kMars0A, kNorth): + angle -= 20; + break; + case MakeRoomView(kMars23, kNorth): + case MakeRoomView(kMars23, kSouth): + case MakeRoomView(kMars23, kEast): + case MakeRoomView(kMars23, kWest): + case MakeRoomView(kMars26, kNorth): + case MakeRoomView(kMars26, kSouth): + case MakeRoomView(kMars26, kEast): + case MakeRoomView(kMars26, kWest): + angle += 30; + break; + case MakeRoomView(kMars24, kNorth): + case MakeRoomView(kMars24, kSouth): + case MakeRoomView(kMars24, kEast): + case MakeRoomView(kMars24, kWest): + case MakeRoomView(kMars25, kNorth): + case MakeRoomView(kMars25, kSouth): + case MakeRoomView(kMars25, kEast): + case MakeRoomView(kMars25, kWest): + angle -= 30; + break; + case MakeRoomView(kMars54, kNorth): + case MakeRoomView(kMars54, kSouth): + case MakeRoomView(kMars54, kEast): + case MakeRoomView(kMars54, kWest): + angle += 90; + break; + case MakeRoomView(kMars56, kNorth): + case MakeRoomView(kMars56, kSouth): + case MakeRoomView(kMars56, kEast): + case MakeRoomView(kMars56, kWest): + angle += 180; + break; + case MakeRoomView(kMars58, kNorth): + case MakeRoomView(kMars58, kSouth): + case MakeRoomView(kMars58, kEast): + case MakeRoomView(kMars58, kWest): + angle -= 90; + break; + } + + return angle; +} + +void Mars::getExitCompassMove(const ExitTable::Entry &exitEntry, FaderMoveSpec &compassMove) { + Neighborhood::getExitCompassMove(exitEntry, compassMove); + + if (exitEntry.room == kMars43 && exitEntry.direction == kEast) { + compassMove.insertFaderKnot(exitEntry.movieStart + 16 * kMarsFrameDuration, 90); + compassMove.insertFaderKnot(exitEntry.movieStart + 32 * kMarsFrameDuration, 270); + } else if (exitEntry.room == kMars46 && exitEntry.direction == kWest && exitEntry.altCode != kAltMarsPodAtMars45) { + compassMove.makeTwoKnotFaderSpec(kMarsMovieScale, exitEntry.movieStart, 270, exitEntry.movieEnd, 360); + compassMove.insertFaderKnot(exitEntry.movieStart + 43 * kMarsFrameDuration, 270); + compassMove.insertFaderKnot(exitEntry.movieStart + 58 * kMarsFrameDuration, 360); + } +} + +void Mars::getExtraCompassMove(const ExtraTable::Entry &entry, FaderMoveSpec &compassMove) { + switch (entry.extra) { + case kMarsTakePodToMars45: + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, 0, entry.movieEnd, 180); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 3), 30); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 11), 10); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 14), 40); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 16), 30); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 23), 100); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 31), 70); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 34), 100); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 37), 85); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 42), 135); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 44), 125); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 46), 145); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 49), 160); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 51), 180); + break; + case kMars35WestSpinAirlockToEast: + case kMars60WestSpinAirlockToEast: + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, 90, entry.movieEnd, 270); + compassMove.insertFaderKnot(entry.movieStart + kMarsMovieScale, 90); + compassMove.insertFaderKnot(entry.movieStart + kMarsMovieScale * 3, 270); + break; + case kMars35EastSpinAirlockToWest: + case kMars60EastSpinAirlockToWest: + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, 270, entry.movieEnd, 90); + compassMove.insertFaderKnot(entry.movieStart + kMarsMovieScale, 270); + compassMove.insertFaderKnot(entry.movieStart + kMarsMovieScale * 3, 90); + break; + case kMars52SpinLeft: + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, kMars52Compass, entry.movieEnd, kMars54Compass); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 10, kMars52Compass); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 110, kMars54Compass); + break; + case kMars52SpinRight: + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, kMars52Compass, entry.movieEnd, kMars58Compass); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 10, kMars52Compass); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 110, kMars58Compass); + break; + case kMars52Extend: + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, kMars52Compass, + entry.movieEnd, kMars52Compass + kMarsShieldPanelOffsetAngle); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 10, kMars52Compass); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 60, kMars52Compass + kMarsShieldPanelOffsetAngle); + break; + case kMars53Retract: + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, + kMars52Compass + kMarsShieldPanelOffsetAngle, entry.movieEnd, kMars52Compass); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 10, kMars52Compass + kMarsShieldPanelOffsetAngle); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 60, kMars52Compass); + break; + case kMars56ExtendWithBomb: + case kMars56ExtendNoBomb: + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, kMars56Compass, + entry.movieEnd, kMars56Compass - kMarsShieldPanelOffsetAngle); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 10, kMars56Compass); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 60, kMars56Compass - kMarsShieldPanelOffsetAngle); + break; + case kMars57RetractWithBomb: + case kMars57RetractNoBomb: + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, + kMars56Compass - kMarsShieldPanelOffsetAngle, entry.movieEnd, kMars56Compass); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 10, kMars56Compass - kMarsShieldPanelOffsetAngle); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 60, kMars56Compass); + break; + case kMars54SpinLeft: + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, kMars54Compass, entry.movieEnd, kMars56Compass); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 10, kMars54Compass); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 110, kMars56Compass); + break; + case kMars54SpinRight: + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, kMars54Compass, entry.movieEnd, kMars52Compass); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 10, kMars54Compass); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 110, kMars52Compass); + break; + case kMars56SpinLeft: + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, kMars56Compass, + entry.movieEnd, kMars58Compass + 360); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 10, kMars56Compass); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 110, kMars58Compass + 360); + break; + case kMars56SpinRight: + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, kMars56Compass, entry.movieEnd, kMars54Compass); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 10, kMars56Compass); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 110, kMars54Compass); + break; + case kMars58SpinLeft: + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, kMars58Compass, + entry.movieEnd, kMars52Compass); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 10, kMars58Compass); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 110, kMars52Compass); + break; + case kMars58SpinRight: + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, + kMars58Compass + 360, entry.movieEnd, kMars56Compass); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 10, kMars58Compass + 360); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 110, kMars56Compass); + break; + default: + Neighborhood::getExtraCompassMove(entry, compassMove); + } +} + +void Mars::loadAmbientLoops() { + RoomID room = GameState.getCurrentRoom(); + + if ((room >= kMars0A && room <= kMars21) || (room >= kMars41 && room <= kMars43)) { + if (GameState.getMarsSeenTimeStream()) + loadLoopSound1("Sounds/Mars/Gantry Ambient.22K.8.AIFF"); + } else if (room >= kMars22 && room <= kMars31South) { + loadLoopSound1("Sounds/Mars/Reception.02.22K.8.AIFF", 0x100 / 4); + } else if (room >= kMars32 && room <= kMars34) { + loadLoopSound1("Sounds/Mars/Pod Room Ambient.22K.8.AIFF"); + } else if (room == kMars35) { + if (getAirQuality(room) == kAirQualityVacuum) + loadLoopSound1("Sounds/Mars/Gear Room Ambient.22K.8.AIFF"); + else + loadLoopSound1("Sounds/Mars/Gantry Ambient.22K.8.AIFF", 0x100 / 2); + } else if (room >= kMars36 && room <= kMars39) { + loadLoopSound1("Sounds/Mars/Gear Room Ambient.22K.8.AIFF"); + } else if (room >= kMars45 && room <= kMars51) { + loadLoopSound1("Sounds/Mars/Lower Mars Ambient.22K.8.AIFF"); + } else if (room >= kMars52 && room <= kMars58) { + loadLoopSound1("Sounds/Mars/ReactorLoop.22K.8.AIFF"); + } else if (room == kMars60) { + if (getAirQuality(room) == kAirQualityVacuum) + loadLoopSound1("Sounds/Mars/Mars Maze Ambient.22K.8.AIFF"); + else + loadLoopSound1("Sounds/Mars/Lower Mars Ambient.22K.8.AIFF", 0x100 / 2); + } else if (room >= kMarsMaze004 && room <= kMarsMaze200) { + loadLoopSound1("Sounds/Mars/Mars Maze Ambient.22K.8.AIFF"); + } else if (room == kMarsRobotShuttle) { + loadLoopSound1("Sounds/Mars/Robot Shuttle.22K.8.AIFF"); + } + + if (!_noAirFuse.isFuseLit()) { + switch (room) { + case kMars02: + case kMars05: + case kMars06: + case kMars07: + case kMars08: + loadLoopSound2("Sounds/Mars/Gantry Loop.aiff", 0x100, 0, 0); + break; + // Robot at maze 48 + case kMarsMaze037: + if (GameState.isCurrentDoorOpen()) + loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 2); + else + loadLoopSound2(""); + break; + case kMarsMaze038: + case kMarsMaze039: + case kMarsMaze049: + loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100); + break; + case kMarsMaze050: + loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 * 3 / 4); + break; + case kMarsMaze051: + loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 2); + break; + case kMarsMaze052: + loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 4); + break; + case kMarsMaze042: + case kMarsMaze053: + loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 8); + break; + case kMarsMaze058: + if (GameState.isCurrentDoorOpen()) + loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 4); + else + loadLoopSound2(""); + break; + // Robot at 151 + case kMarsMaze148: + loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100); + break; + case kMarsMaze147: + case kMarsMaze149: + loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 * 3 / 4); + break; + case kMarsMaze146: + case kMarsMaze152: + loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 2); + break; + case kMarsMaze145: + case kMarsMaze153: + loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 4); + break; + // Robots at 80 and 82. + case kMarsMaze079: + case kMarsMaze081: + loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100); + break; + case kMarsMaze078: + loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 * 3 / 4); + break; + case kMarsMaze083: + if (GameState.isCurrentDoorOpen()) + loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 * 3 / 4); + else + loadLoopSound2(""); + break; + case kMarsMaze118: + case kMarsMaze076: + loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 2); + break; + case kMarsMaze074: + case kMarsMaze117: + loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 4); + break; + // Robot at 94 + case kMarsMaze093: + loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100); + break; + case kMarsMaze091: + case kMarsMaze092: + case kMarsMaze098: + case kMarsMaze101: + case kMarsMaze100: + loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 * 3 / 4); + break; + case kMarsMaze090: + case kMarsMaze099: + loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 2); + break; + case kMarsMaze089: + if (GameState.isCurrentDoorOpen()) + loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 2); + break; + case kMarsMaze178: + loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 4); + break; + // Robot at 197 + case kMarsMaze191: + loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100); + break; + case kMarsMaze190: + loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 * 3 / 4); + break; + case kMarsMaze198: + case kMarsMaze189: + loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 2); + break; + default: + loadLoopSound2(""); + break; + } + } +} + +void Mars::checkContinuePoint(const RoomID room, const DirectionConstant direction) { + switch (MakeRoomView(room, direction)) { + case MakeRoomView(kMars02, kSouth): + case MakeRoomView(kMars19, kEast): + case MakeRoomView(kMars22, kNorth): + case MakeRoomView(kMars43, kEast): + case MakeRoomView(kMars51, kEast): + case MakeRoomView(kMars56, kEast): + case MakeRoomView(kMars60, kWest): + case MakeRoomView(kMarsMaze004, kWest): + case MakeRoomView(kMarsMaze009, kWest): + case MakeRoomView(kMarsMaze012, kWest): + case MakeRoomView(kMarsMaze037, kWest): + case MakeRoomView(kMarsMaze047, kNorth): + case MakeRoomView(kMarsMaze052, kWest): + case MakeRoomView(kMarsMaze057, kNorth): + case MakeRoomView(kMarsMaze071, kWest): + case MakeRoomView(kMarsMaze081, kNorth): + case MakeRoomView(kMarsMaze088, kWest): + case MakeRoomView(kMarsMaze093, kWest): + case MakeRoomView(kMarsMaze115, kNorth): + case MakeRoomView(kMarsMaze120, kWest): + case MakeRoomView(kMarsMaze126, kEast): + case MakeRoomView(kMarsMaze133, kNorth): + case MakeRoomView(kMarsMaze144, kNorth): + case MakeRoomView(kMarsMaze156, kEast): + case MakeRoomView(kMarsMaze162, kNorth): + case MakeRoomView(kMarsMaze177, kWest): + case MakeRoomView(kMarsMaze180, kNorth): + case MakeRoomView(kMarsMaze187, kWest): + case MakeRoomView(kMarsMaze199, kWest): + makeContinuePoint(); + break; + case MakeRoomView(kMars05, kEast): + case MakeRoomView(kMars06, kEast): + case MakeRoomView(kMars07, kEast): + if (GameState.getMarsSecurityDown()) + makeContinuePoint(); + break; + case MakeRoomView(kMars46, kSouth): + if (!GameState.getMarsSeenRobotAtReactor()) + makeContinuePoint(); + break; + case MakeRoomView(kMars46, kWest): + if (GameState.getMarsAvoidedReactorRobot()) + makeContinuePoint(); + break; + } +} + +void Mars::launchMaze007Robot() { + startExtraLongSequence(kMarsMaze007RobotApproach, kMarsMaze007RobotDeath, kExtraCompletedFlag, kFilterAllInput); + scheduleEvent(kMaze007RobotLoopingTime, kMarsMovieScale, kMaze007RobotLoopingEvent); +} + +void Mars::launchMaze015Robot() { + startExtraLongSequence(kMarsMaze015SouthRobotApproach, kMarsMaze015SouthRobotDeath, kExtraCompletedFlag, kFilterAllInput); + scheduleEvent(kMaze015RobotLoopingTime, kMarsMovieScale, kMaze015RobotLoopingEvent); +} + +void Mars::launchMaze101Robot() { + startExtraLongSequence(kMarsMaze101EastRobotApproach, kMarsMaze101EastRobotDeath, kExtraCompletedFlag, kFilterAllInput); + scheduleEvent(kMaze101RobotLoopingTime, kMarsMovieScale, kMaze101RobotLoopingEvent); +} + +void Mars::launchMaze104Robot() { + startExtraLongSequence(kMarsMaze104WestLoop, kMarsMaze104WestDeath, kExtraCompletedFlag, kFilterAllInput); + scheduleEvent(kMaze104RobotLoopingTime, kMarsMovieScale, kMaze104RobotLoopingEvent); +} + +void Mars::launchMaze133Robot() { + startExtraLongSequence(kMarsMaze133SouthApproach, kMarsMaze133SouthDeath, kExtraCompletedFlag, kFilterAllInput); + scheduleEvent(kMaze133RobotLoopingTime, kMarsMovieScale, kMaze133RobotLoopingEvent); +} + +void Mars::launchMaze136Robot() { + startExtraLongSequence(kMarsMaze136NorthApproach, kMarsMaze136NorthDeath, kExtraCompletedFlag, kFilterAllInput); + scheduleEvent(kMaze136RobotLoopingTime, kMarsMovieScale, kMaze136RobotLoopingEvent); +} + +void Mars::launchMaze184Robot() { + startExtraLongSequence(kMarsMaze184WestLoop, kMarsMaze184WestDeath, kExtraCompletedFlag, kFilterAllInput); + scheduleEvent(kMaze184RobotLoopingTime, kMarsMovieScale, kMaze184RobotLoopingEvent); +} + +void Mars::timerExpired(const uint32 eventType) { + switch (eventType) { + case kMaze007RobotLoopingEvent: + case kMaze015RobotLoopingEvent: + case kMaze101RobotLoopingEvent: + case kMaze104RobotLoopingEvent: + case kMaze133RobotLoopingEvent: + case kMaze136RobotLoopingEvent: + case kMaze184RobotLoopingEvent: + _interruptionFilter = kFilterNoInput; + break; + } +} + +void Mars::arriveAt(const RoomID room, const DirectionConstant direction) { + switch (MakeRoomView(room, direction)) { + case MakeRoomView(kMars18, kNorth): + if (GameState.getMarsPodAtUpperPlatform()) + setCurrentAlternate(kAltMarsPodAtMars34); + break; + case MakeRoomView(kMars27, kEast): + case MakeRoomView(kMars29, kEast): + if (GameState.isTakenItemID(kMarsCard)) + setCurrentAlternate(kAltMarsTookCard); + else + setCurrentAlternate(kAltMarsNormal); + break; + case MakeRoomView(kMars35, kEast): + case MakeRoomView(kMars35, kWest): + if (GameState.getMarsAirlockOpen()) + setCurrentAlternate(kAltMars35AirlockWest); + else + setCurrentAlternate(kAltMars35AirlockEast); + break; + case MakeRoomView(kMars60, kEast): + case MakeRoomView(kMars60, kWest): + if (GameState.getMarsAirlockOpen()) + setCurrentAlternate(kAltMars60AirlockEast); + else + setCurrentAlternate(kAltMars60AirlockWest); + break; + case MakeRoomView(kMars45, kNorth): + case MakeRoomView(kMars45, kSouth): + case MakeRoomView(kMars45, kEast): + case MakeRoomView(kMars45, kWest): + GameState.setMarsPodAtUpperPlatform(false); + setCurrentAlternate(kAltMarsPodAtMars45); + break; + case MakeRoomView(kMars46, kNorth): + case MakeRoomView(kMars46, kSouth): + case MakeRoomView(kMars46, kEast): + case MakeRoomView(kMars46, kWest): + case MakeRoomView(kMars47, kNorth): + case MakeRoomView(kMars47, kSouth): + case MakeRoomView(kMars47, kEast): + case MakeRoomView(kMars47, kWest): + if (GameState.getMarsPodAtUpperPlatform()) + setCurrentAlternate(kAltMarsNormal); + else + setCurrentAlternate(kAltMarsPodAtMars45); + break; + case MakeRoomView(kMars48, kNorth): + case MakeRoomView(kMars48, kSouth): + case MakeRoomView(kMars48, kEast): + case MakeRoomView(kMars48, kWest): + case MakeRoomView(kMars49, kNorth): + case MakeRoomView(kMars49, kEast): + case MakeRoomView(kMars49, kWest): + if (GameState.isTakenItemID(kAirMask)) + setCurrentAlternate(kAltMarsTookMask); + else + setCurrentAlternate(kAltMarsNormal); + break; + case MakeRoomView(kMars49, kSouth): + if (GameState.getMarsMaskOnFiller()) + setCurrentAlternate(kAltMarsMaskOnFiller); + else if (GameState.isTakenItemID(kAirMask)) + setCurrentAlternate(kAltMarsTookMask); + else + setCurrentAlternate(kAltMarsNormal); + break; + } + + Neighborhood::arriveAt(room, direction); + checkAirlockDoors(); + setUpReactorEnergyDrain(); + + switch (MakeRoomView(room, direction)) { + case MakeRoomView(kMars0A, kNorth): + if (!GameState.getMarsSeenTimeStream()) + startExtraLongSequence(kMarsArrivalFromTSA, kMars0AWatchShuttleDepart, kExtraCompletedFlag, kFilterNoInput); + break; + case MakeRoomView(kMars07, kSouth): + case MakeRoomView(kMars13, kNorth): + if (!GameState.getMarsHeardCheckInMessage()) { + playSpotSoundSync(kMarsCheckInRequiredIn, kMarsCheckInRequiredOut); + GameState.setMarsHeardCheckInMessage(true); + } + break; + case MakeRoomView(kMars44, kWest): + if (GameState.getMarsReadyForShuttleTransport()) + startUpFromFinishedSpaceChase(); + else if (GameState.getMarsFinishedCanyonChase()) + startUpFromSpaceChase(); + else + _neighborhoodNotification.setNotificationFlags(kTimeForCanyonChaseFlag, kTimeForCanyonChaseFlag); + break; + case MakeRoomView(kMars10, kNorth): + if (!GameState.getMarsRobotThrownPlayer()) + startExtraSequence(kRobotThrowsPlayer, kExtraCompletedFlag, kFilterNoInput); + break; + case MakeRoomView(kMars11, kSouth): + case MakeRoomView(kMars12, kSouth): + setCurrentActivation(kActivationReadyForKiosk); + break; + case MakeRoomView(kMars15, kWest): + if (GameState.getMarsThreadedMaze() && !GameState.getMarsSecurityDown()) { + playSpotSoundSync(kMarsShuttle2DepartedIn, kMarsShuttle2DepartedOut); + restoreStriding(kMars17, kWest, kAltMarsNormal); + GameState.setMarsSecurityDown(true); + } + break; + case MakeRoomView(kMars17, kNorth): + case MakeRoomView(kMars17, kSouth): + case MakeRoomView(kMars17, kEast): + case MakeRoomView(kMars17, kWest): + if (GameState.getMarsThreadedMaze() && !GameState.getMarsSecurityDown()) + forceStridingStop(kMars17, kWest, kAltMarsNormal); + + if (GameState.getMarsThreadedMaze() && !GameState.getMarsSawRobotLeave()) { + startExtraSequence(kRobotOnWayToShuttle, kExtraCompletedFlag, kFilterNoInput); + restoreStriding(kMars19, kWest, kAltMarsNormal); + GameState.setMarsSawRobotLeave(true); + } + break; + case MakeRoomView(kMars19, kNorth): + case MakeRoomView(kMars19, kSouth): + case MakeRoomView(kMars19, kWest): + if (GameState.getMarsThreadedMaze() && !GameState.getMarsSawRobotLeave()) + forceStridingStop(kMars19, kWest, kAltMarsNormal); + + if (GameState.getMarsThreadedMaze() && !GameState.getMarsSecurityDown()) + forceStridingStop(kMars17, kWest, kAltMarsNormal); + break; + case MakeRoomView(kMars19, kEast): + if (GameState.getMarsThreadedMaze() && !GameState.getMarsSawRobotLeave()) + forceStridingStop(kMars19, kWest, kAltMarsNormal); + + if (GameState.getMarsThreadedMaze() && !GameState.getMarsSecurityDown()) + forceStridingStop(kMars17, kWest, kAltMarsNormal); + break; + case MakeRoomView(kMars32, kNorth): + if (!GameState.getMarsPodAtUpperPlatform()) { + playSpotSoundSync(kMarsPodArrivedUpperPlatformIn, kMarsPodArrivedUpperPlatformOut); + GameState.setMarsPodAtUpperPlatform(true); + } + break; + case MakeRoomView(kMars33North, kNorth): + setCurrentActivation(kActivationTunnelMapReady); + // Fall through... + case MakeRoomView(kMars33, kSouth): + case MakeRoomView(kMars33, kEast): + case MakeRoomView(kMars33, kWest): + case MakeRoomView(kMars32, kSouth): + case MakeRoomView(kMars32, kEast): + case MakeRoomView(kMars32, kWest): + if (!GameState.getMarsPodAtUpperPlatform()) + GameState.setMarsPodAtUpperPlatform(true); + break; + case MakeRoomView(kMars34, kNorth): + startExtraSequence(kMars34NorthPodGreeting, kExtraCompletedFlag, kFilterNoInput); + break; + case MakeRoomView(kMars34, kSouth): + case MakeRoomView(kMars45, kNorth): + setCurrentActivation(kActivateMarsPodClosed); + break; + case MakeRoomView(kMars35, kWest): + if (GameState.getMarsThreadedMaze() && !GameState.getMarsSecurityDown()) + forceStridingStop(kMars19, kWest, kAltMarsNormal); + // Fall through... + case MakeRoomView(kMars60, kEast): + if (!GameState.getMarsAirlockOpen()) + setCurrentActivation(kActivateReadyToPressurizeAirlock); + break; + case MakeRoomView(kMars35, kEast): + case MakeRoomView(kMars60, kWest): + if (GameState.getMarsAirlockOpen()) + setCurrentActivation(kActivateReadyToPressurizeAirlock); + break; + case MakeRoomView(kMars39, kWest): + if (GameState.getLastRoom() == kMarsMaze200) + GameState.setMarsPodAtUpperPlatform(false); + break; + case MakeRoomView(kMars45, kSouth): + // Set up maze doors here. + // Doing it here makes sure that it will be the same if the player comes + // back out of the maze and goes back in, but will vary if + // the player comes back down to the maze a second time. + GameState.setMarsMazeDoorPair1(_vm->getRandomBit()); + GameState.setMarsMazeDoorPair2(_vm->getRandomBit()); + GameState.setMarsMazeDoorPair3(_vm->getRandomBit()); + GameState.setMarsArrivedBelow(true); + break; + case MakeRoomView(kMars48, kEast): + if (!GameState.getMarsSeenRobotAtReactor()) { + // Preload the looping sound... + loadLoopSound2("Sounds/Mars/Robot Loop.aiff", 0, 0, 0); + startExtraSequence(kMars48RobotApproaches, kExtraCompletedFlag, kFilterNoInput); + } else if (!GameState.getMarsAvoidedReactorRobot()) { + loadLoopSound2("Sounds/Mars/Robot Loop.aiff", 0x100, 0, 0); + loopExtraSequence(kMars48RobotLoops); + _utilityFuse.primeFuse(kMarsRobotPatienceLimit); + _utilityFuse.setFunctor(new Common::Functor0Mem<void, Mars>(this, &Mars::robotTiredOfWaiting)); + _utilityFuse.lightFuse(); + } + break; + case MakeRoomView(kMars48, kSouth): + if (GameState.getMarsSeenRobotAtReactor() && !GameState.getMarsAvoidedReactorRobot()) { + loadLoopSound2("Sounds/Mars/Robot Loop.aiff", 0x100, 0, 0); + _utilityFuse.primeFuse(kMarsRobotPatienceLimit); + _utilityFuse.setFunctor(new Common::Functor0Mem<void, Mars>(this, &Mars::robotTiredOfWaiting)); + _utilityFuse.lightFuse(); + } + break; + case MakeRoomView(kMars49, kSouth): + if (GameState.getMarsSeenRobotAtReactor() && !GameState.getMarsAvoidedReactorRobot()) { + playSpotSoundSync(kMarsRobotTakesTransportIn, kMarsRobotTakesTransportOut); + playSpotSoundSync(kMarsPodDepartedLowerPlatformIn, kMarsPodDepartedLowerPlatformOut); + GameState.setMarsAvoidedReactorRobot(true); + GameState.setMarsPodAtUpperPlatform(true); + GameState.setScoringAvoidedRobot(); + } + + if (GameState.isTakenItemID(kAirMask)) + setCurrentActivation(kActivateHotSpotAlways); + else if (GameState.getMarsMaskOnFiller()) + setCurrentActivation(kActivateMaskOnFiller); + else + setCurrentActivation(kActivateMaskOnHolder); + break; + case MakeRoomView(kMars51, kWest): + case MakeRoomView(kMars50, kWest): + case MakeRoomView(kMars48, kWest): + if (GameState.getShieldOn()) + g_shield->setItemState(kShieldNormal); + g_energyMonitor->setEnergyDrainRate(kEnergyDrainNormal); + _vm->resetEnergyDeathReason(); + break; + case MakeRoomView(kMars52, kNorth): + case MakeRoomView(kMars52, kSouth): + case MakeRoomView(kMars52, kEast): + case MakeRoomView(kMars52, kWest): + case MakeRoomView(kMars54, kNorth): + case MakeRoomView(kMars54, kSouth): + case MakeRoomView(kMars54, kEast): + case MakeRoomView(kMars54, kWest): + case MakeRoomView(kMars56, kNorth): + case MakeRoomView(kMars56, kSouth): + case MakeRoomView(kMars56, kWest): + case MakeRoomView(kMars58, kNorth): + case MakeRoomView(kMars58, kSouth): + case MakeRoomView(kMars58, kEast): + case MakeRoomView(kMars58, kWest): + setCurrentActivation(kActivateReactorPlatformOut); + break; + case MakeRoomView(kMars56, kEast): + if (GameState.getMarsLockBroken()) { + setCurrentActivation(kActivateReactorAskOperation); + _privateFlags.setFlag(kMarsPrivatePlatformZoomedInFlag, true); + } else if (GameState.getMarsLockFrozen()) { + setCurrentActivation(kActivateReactorReadyForCrowBar); + _privateFlags.setFlag(kMarsPrivatePlatformZoomedInFlag, true); + _utilityFuse.primeFuse(kLockFreezeTimeLmit); + _utilityFuse.setFunctor(new Common::Functor0Mem<void, Mars>(this, &Mars::lockThawed)); + _utilityFuse.lightFuse(); + } else { + setCurrentActivation(kActivateReactorPlatformOut); + } + break; + case MakeRoomView(kMarsRobotShuttle, kEast): + setCurrentActivation(kActivationRobotHeadClosed); + break; + case MakeRoomView(kMarsMaze007, kNorth): + launchMaze007Robot(); + break; + case MakeRoomView(kMarsMaze015, kSouth): + launchMaze015Robot(); + break; + case MakeRoomView(kMarsMaze101, kEast): + launchMaze101Robot(); + break; + case MakeRoomView(kMarsMaze104, kWest): + launchMaze104Robot(); + break; + case MakeRoomView(kMarsMaze133, kSouth): + launchMaze133Robot(); + break; + case MakeRoomView(kMarsMaze136, kNorth): + launchMaze136Robot(); + break; + case MakeRoomView(kMarsMaze184, kWest): + launchMaze184Robot(); + break; + case MakeRoomView(kMarsMaze199, kSouth): + GameState.setScoringThreadedMaze(); + GameState.setMarsThreadedMaze(true); + break; + case MakeRoomView(kMarsDeathRoom, kNorth): + case MakeRoomView(kMarsDeathRoom, kSouth): + case MakeRoomView(kMarsDeathRoom, kEast): + case MakeRoomView(kMarsDeathRoom, kWest): + switch (GameState.getLastRoom()) { + case kMars39: + die(kDeathDidntLeaveBucket); + break; + case kMars46: + die(kDeathRunOverByPod); + break; + } + break; + } + + checkAirMask(); +} + +void Mars::shieldOn() { + setUpReactorEnergyDrain(); +} + +void Mars::shieldOff() { + setUpReactorEnergyDrain(); +} + +void Mars::turnTo(const DirectionConstant direction) { + switch (MakeRoomView(GameState.getCurrentRoom(), direction)) { + case MakeRoomView(kMars27, kNorth): + case MakeRoomView(kMars27, kSouth): + case MakeRoomView(kMars27, kEast): + case MakeRoomView(kMars29, kNorth): + case MakeRoomView(kMars29, kSouth): + case MakeRoomView(kMars29, kEast): + if (GameState.isTakenItemID(kMarsCard)) + setCurrentAlternate(kAltMarsTookCard); + break; + case MakeRoomView(kMars35, kNorth): + case MakeRoomView(kMars35, kSouth): + case MakeRoomView(kMars60, kNorth): + case MakeRoomView(kMars60, kSouth): + if (getCurrentActivation() == kActivateAirlockPressurized) + playSpotSoundSync(kMarsAirlockPressurizeIn, kMarsAirlockPressurizeOut); + break; + } + + Neighborhood::turnTo(direction); + + switch (MakeRoomView(GameState.getCurrentRoom(), direction)) { + case MakeRoomView(kMars11, kSouth): + case MakeRoomView(kMars12, kSouth): + setCurrentActivation(kActivationReadyForKiosk); + break; + case MakeRoomView(kMars18, kNorth): + if (GameState.getMarsPodAtUpperPlatform()) + setCurrentAlternate(kAltMarsPodAtMars34); + break; + case MakeRoomView(kMars22, kSouth): + if (!GameState.getMarsHeardCheckInMessage()) { + playSpotSoundSync(kMarsCheckInRequiredIn, kMarsCheckInRequiredOut); + GameState.setMarsHeardCheckInMessage(true); + } + break; + case MakeRoomView(kMars34, kSouth): + case MakeRoomView(kMars45, kNorth): + setCurrentActivation(kActivateMarsPodClosed); + break; + case MakeRoomView(kMars34, kNorth): + startExtraSequence(kMars34NorthPodGreeting, kExtraCompletedFlag, kFilterNoInput); + break; + case MakeRoomView(kMars35, kEast): + case MakeRoomView(kMars60, kWest): + if (GameState.getMarsAirlockOpen()) + setCurrentActivation(kActivateReadyToPressurizeAirlock); + break; + case MakeRoomView(kMars60, kEast): + if (!GameState.getMarsAirlockOpen()) + setCurrentActivation(kActivateReadyToPressurizeAirlock); + break; + case MakeRoomView(kMars35, kWest): + if (!GameState.getMarsAirlockOpen()) + setCurrentActivation(kActivateReadyToPressurizeAirlock); + + // Do this here because this will be called after spinning the airlock after + // going through the gear room. + if (GameState.getMarsThreadedMaze()) + GameState.setScoringThreadedGearRoom(); + break; + case MakeRoomView(kMars48, kNorth): + if (GameState.getMarsSeenRobotAtReactor() && !GameState.getMarsAvoidedReactorRobot()) + die(kDeathDidntGetOutOfWay); + break; + case MakeRoomView(kMars48, kEast): + if (!GameState.getMarsSeenRobotAtReactor()) { + // Preload the looping sound... + loadLoopSound2("Sounds/Mars/Robot Loop.aiff", 0, 0, 0); + startExtraSequence(kMars48RobotApproaches, kExtraCompletedFlag, kFilterNoInput); + } else if (!GameState.getMarsAvoidedReactorRobot()) { + loopExtraSequence(kMars48RobotLoops); + } else if (GameState.isTakenItemID(kAirMask)) { + setCurrentAlternate(kAltMarsTookMask); + } else { + setCurrentAlternate(kAltMarsNormal); + } + break; + case MakeRoomView(kMars48, kWest): + if (GameState.getMarsSeenRobotAtReactor() && !GameState.getMarsAvoidedReactorRobot()) + die(kDeathDidntGetOutOfWay); + else if (GameState.isTakenItemID(kAirMask)) + setCurrentAlternate(kAltMarsTookMask); + else + setCurrentAlternate(kAltMarsNormal); + break; + case MakeRoomView(kMars49, kSouth): + if (GameState.isTakenItemID(kAirMask)) + setCurrentActivation(kActivateHotSpotAlways); + else + setCurrentActivation(kActivateMaskOnHolder); + break; + case MakeRoomView(kMars52, kNorth): + case MakeRoomView(kMars52, kSouth): + case MakeRoomView(kMars52, kEast): + case MakeRoomView(kMars52, kWest): + case MakeRoomView(kMars54, kNorth): + case MakeRoomView(kMars54, kSouth): + case MakeRoomView(kMars54, kEast): + case MakeRoomView(kMars54, kWest): + case MakeRoomView(kMars56, kNorth): + case MakeRoomView(kMars56, kSouth): + case MakeRoomView(kMars56, kEast): + case MakeRoomView(kMars56, kWest): + case MakeRoomView(kMars58, kNorth): + case MakeRoomView(kMars58, kSouth): + case MakeRoomView(kMars58, kEast): + case MakeRoomView(kMars58, kWest): + setCurrentActivation(kActivateReactorPlatformOut); + break; + case MakeRoomView(kMarsMaze007, kNorth): + launchMaze007Robot(); + break; + case MakeRoomView(kMarsMaze015, kSouth): + launchMaze015Robot(); + break; + case MakeRoomView(kMarsMaze101, kEast): + launchMaze101Robot(); + break; + case MakeRoomView(kMarsMaze104, kWest): + launchMaze104Robot(); + break; + case MakeRoomView(kMarsMaze133, kSouth): + launchMaze133Robot(); + break; + case MakeRoomView(kMarsMaze136, kNorth): + launchMaze136Robot(); + break; + case MakeRoomView(kMarsMaze184, kWest): + launchMaze184Robot(); + break; + } +} + +void Mars::activateOneHotspot(HotspotInfoTable::Entry &entry, Hotspot *hotspot) { + switch (hotspot->getObjectID()) { + case kMars57RedMoveSpotID: + case kMars57YellowMoveSpotID: + case kMars57GreenMoveSpotID: + if (!_choiceHighlight.choiceHighlighted(hotspot->getObjectID() - kMars57RedMoveSpotID)) + hotspot->setActive(); + break; + case kMars57BlueMoveSpotID: + if (_reactorStage >= 2 && !_choiceHighlight.choiceHighlighted(3)) + hotspot->setActive(); + break; + case kMars57PurpleMoveSpotID: + if (_reactorStage == 3 && !_choiceHighlight.choiceHighlighted(4)) + hotspot->setActive(); + break; + default: + Neighborhood::activateOneHotspot(entry, hotspot); + break; + } +} + +void Mars::activateHotspots() { + InventoryItem *item; + + Neighborhood::activateHotspots(); + + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kMars48, kEast): + if ((_navMovie.getFlags() & kLoopTimeBase) != 0 && _vm->getDragType() == kDragInventoryUse) + _vm->getAllHotspots().activateOneHotspot(kAttackRobotHotSpotID); + break; + case MakeRoomView(kMars56, kEast): + switch (getCurrentActivation()) { + case kActivateReactorReadyForNitrogen: + item = (InventoryItem *)_vm->getAllItems().findItemByID(kNitrogenCanister); + if (item->getItemState() != kNitrogenFull) + _vm->getAllHotspots().deactivateOneHotspot(kMars57DropNitrogenSpotID); + // Fall through... + case kActivateReactorReadyForCrowBar: + _vm->getAllHotspots().activateOneHotspot(kMars57CantOpenPanelSpotID); + break; + } + break; + case MakeRoomView(kMarsRobotShuttle, kEast): + if (_privateFlags.getFlag(kMarsPrivateGotMapChipFlag)) + _vm->getAllHotspots().deactivateOneHotspot(kRobotShuttleMapChipSpotID); + else + _vm->getAllHotspots().activateOneHotspot(kRobotShuttleMapChipSpotID); + + if (_privateFlags.getFlag(kMarsPrivateGotOpticalChipFlag)) + _vm->getAllHotspots().deactivateOneHotspot(kRobotShuttleOpticalChipSpotID); + else + _vm->getAllHotspots().activateOneHotspot(kRobotShuttleOpticalChipSpotID); + + if (_privateFlags.getFlag(kMarsPrivateGotShieldChipFlag)) + _vm->getAllHotspots().deactivateOneHotspot(kRobotShuttleShieldChipSpotID); + else + _vm->getAllHotspots().activateOneHotspot(kRobotShuttleShieldChipSpotID); + break; + default: + if (_privateFlags.getFlag(kMarsPrivateInSpaceChaseFlag)) { + if (GameState.getMarsReadyForShuttleTransport()) { + _shuttleTransportSpot.setActive(); + } else { + _energyChoiceSpot.setActive(); + _gravitonChoiceSpot.setActive(); + _tractorChoiceSpot.setActive(); + if (_weaponSelection != kNoWeapon) + _shuttleViewSpot.setActive(); + } + } + break; + } +} + +void Mars::clickInHotspot(const Input &input, const Hotspot *clickedSpot) { + switch (clickedSpot->getObjectID()) { + case kMars11NorthKioskSpotID: + case kMars12NorthKioskSpotID: + playSpotSoundSync(kMarsKioskBeepIn, kMarsKioskBeepOut); + Neighborhood::clickInHotspot(input, clickedSpot); + break; + case kMars11NorthKioskSightsSpotID: + case kMars12NorthKioskSightsSpotID: + playSpotSoundSync(kMarsKioskBeepIn, kMarsKioskBeepOut); + if (!startExtraSequenceSync(kMarsSightsInfo, kFilterAllInput)) + showExtraView(kMarsInfoKioskIntro); + break; + case kMars11NorthKioskColonySpotID: + case kMars12NorthKioskColonySpotID: + playSpotSoundSync(kMarsKioskBeepIn, kMarsKioskBeepOut); + if (!startExtraSequenceSync(kMarsColonyInfo, kFilterAllInput)) + showExtraView(kMarsInfoKioskIntro); + break; + case kMars33NorthMonitorSpotID: + switch (_lastExtra) { + case kMars33SlideShow1: + startExtraSequence(kMars33SlideShow2, kExtraCompletedFlag, kFilterNoInput); + break; + case kMars33SlideShow2: + startExtraSequence(kMars33SlideShow3, kExtraCompletedFlag, kFilterNoInput); + break; + case kMars33SlideShow3: + startExtraSequence(kMars33SlideShow4, kExtraCompletedFlag, kFilterNoInput); + break; + case kMars33SlideShow4: + // Should never happen... + default: + startExtraSequence(kMars33SlideShow1, kExtraCompletedFlag, kFilterNoInput); + break; + } + break; + case kMars34SouthOpenStorageSpotID: + if (GameState.isTakenItemID(kCrowbar)) + startExtraSequence(kMars34SpotOpenNoBar, kExtraCompletedFlag, kFilterNoInput); + else + startExtraSequence(kMars34SpotOpenWithBar, kExtraCompletedFlag, kFilterNoInput); + break; + case kMars34SouthCloseStorageSpotID: + if (GameState.isTakenItemID(kCrowbar)) + startExtraSequence(kMars34SpotCloseNoBar, kExtraCompletedFlag, kFilterNoInput); + else + startExtraSequence(kMars34SpotCloseWithBar, kExtraCompletedFlag, kFilterNoInput); + break; + case kMars35WestPressurizeSpotID: + case kMars35EastPressurizeSpotID: + case kMars60WestPressurizeSpotID: + case kMars60EastPressurizeSpotID: + playSpotSoundSync(kMarsAirlockButtonBeepIn, kMarsAirlockButtonBeepOut); + playSpotSoundSync(kMarsAirlockPressurizeIn, kMarsAirlockPressurizeOut); + setCurrentActivation(kActivateAirlockPressurized); + break; + case kMars45NorthOpenStorageSpotID: + if (GameState.isTakenItemID(kCrowbar)) + startExtraSequence(kMars45SpotOpenNoBar, kExtraCompletedFlag, kFilterNoInput); + else + startExtraSequence(kMars45SpotOpenWithBar, kExtraCompletedFlag, kFilterNoInput); + break; + case kMars45NorthCloseStorageSpotID: + if (GameState.isTakenItemID(kCrowbar)) + startExtraSequence(kMars45SpotCloseNoBar, kExtraCompletedFlag, kFilterNoInput); + else + startExtraSequence(kMars45SpotCloseWithBar, kExtraCompletedFlag, kFilterNoInput); + break; + case kMars56ExtractSpotID: + if (GameState.isTakenItemID(kCardBomb)) { + startExtraSequence(kMars56ExtendNoBomb, kExtraCompletedFlag, kFilterNoInput); + setCurrentActivation(kActivateReactorPlatformIn); + } else { + startExtraSequence(kMars56ExtendWithBomb, kExtraCompletedFlag, kFilterNoInput); + setCurrentActivation(kActivateReactorAskLowerScreen); + } + break; + case kMars57UndoMoveSpotID: + playSpotSoundSync(kMarsColorMatchingButtonBeepIn, kMarsColorMatchingButtonBeepOut); + doUndoOneGuess(); + break; + case kMars57RedMoveSpotID: + playSpotSoundSync(kMarsColorMatchingButtonBeepIn, kMarsColorMatchingButtonBeepOut); + doReactorGuess(0); + break; + case kMars57YellowMoveSpotID: + playSpotSoundSync(kMarsColorMatchingButtonBeepIn, kMarsColorMatchingButtonBeepOut); + doReactorGuess(1); + break; + case kMars57GreenMoveSpotID: + playSpotSoundSync(kMarsColorMatchingButtonBeepIn, kMarsColorMatchingButtonBeepOut); + doReactorGuess(2); + break; + case kMars57BlueMoveSpotID: + playSpotSoundSync(kMarsColorMatchingButtonBeepIn, kMarsColorMatchingButtonBeepOut); + doReactorGuess(3); + break; + case kMars57PurpleMoveSpotID: + playSpotSoundSync(kMarsColorMatchingButtonBeepIn, kMarsColorMatchingButtonBeepOut); + doReactorGuess(4); + break; + case kShuttleEnergySpotID: + case kShuttleGravitonSpotID: + case kShuttleTractorSpotID: + case kShuttleViewSpotID: + case kShuttleTransportSpotID: + spaceChaseClick(input, clickedSpot->getObjectID()); + break; + default: + Neighborhood::clickInHotspot(input, clickedSpot); + break; + } +} + +InputBits Mars::getInputFilter() { + InputBits result = Neighborhood::getInputFilter(); + + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kMars49, kSouth): + if (GameState.getMarsMaskOnFiller()) + // Can't move when mask is on filler. + result &= ~kFilterAllDirections; + break; + case MakeRoomView(kMars52, kNorth): + case MakeRoomView(kMars52, kSouth): + case MakeRoomView(kMars52, kEast): + case MakeRoomView(kMars52, kWest): + case MakeRoomView(kMars54, kNorth): + case MakeRoomView(kMars54, kSouth): + case MakeRoomView(kMars54, kEast): + case MakeRoomView(kMars54, kWest): + case MakeRoomView(kMars56, kNorth): + case MakeRoomView(kMars56, kSouth): + case MakeRoomView(kMars56, kEast): + case MakeRoomView(kMars56, kWest): + case MakeRoomView(kMars58, kNorth): + case MakeRoomView(kMars58, kSouth): + case MakeRoomView(kMars58, kEast): + case MakeRoomView(kMars58, kWest): + if (_privateFlags.getFlag(kMarsPrivatePlatformZoomedInFlag)) + // Can't move when platform is extended. + result &= ~kFilterAllDirections; + break; + case MakeRoomView(kMars44, kWest): + if (_canyonChaseMovie.isMovieValid() && _canyonChaseMovie.isRunning()) + result &= ~kFilterAllDirections; + break; + } + + return result; +} + +// Only called when trying to pick up an item and the player can't (because +// the inventory is too full or because the player lets go of the item before +// dropping it into the inventory). +Hotspot *Mars::getItemScreenSpot(Item *item, DisplayElement *element) { + HotSpotID destSpotID; + + switch (item->getObjectID()) { + case kCardBomb: + destSpotID = kMars57GrabBombSpotID; + break; + case kMarsCard: + destSpotID = kMars31SouthCardSpotID; + break; + case kAirMask: + if (GameState.getMarsMaskOnFiller()) + destSpotID = kMars49AirFillingDropSpotID; + else + destSpotID = kMars49AirMaskSpotID; + break; + case kCrowbar: + if (GameState.getCurrentRoom() == kMars34) + destSpotID = kMars34SouthCrowbarSpotID; + else + destSpotID = kMars45NorthCrowbarSpotID; + break; + case kMapBiochip: + destSpotID = kRobotShuttleMapChipSpotID; + break; + case kOpticalBiochip: + destSpotID = kRobotShuttleOpticalChipSpotID; + break; + case kShieldBiochip: + destSpotID = kRobotShuttleShieldChipSpotID; + break; + default: + destSpotID = kNoHotSpotID; + break; + } + + if (destSpotID == kNoHotSpotID) + return Neighborhood::getItemScreenSpot(item, element); + + return _vm->getAllHotspots().findHotspotByID(destSpotID); +} + +void Mars::takeItemFromRoom(Item *item) { + switch (item->getObjectID()) { + case kAirMask: + setCurrentAlternate(kAltMarsTookMask); + break; + case kCardBomb: + _privateFlags.setFlag(kMarsPrivateDraggingBombFlag, true); + break; + case kMapBiochip: + _privateFlags.setFlag(kMarsPrivateGotMapChipFlag, true); + break; + case kShieldBiochip: + _privateFlags.setFlag(kMarsPrivateGotShieldChipFlag, true); + break; + case kOpticalBiochip: + _privateFlags.setFlag(kMarsPrivateGotOpticalChipFlag, true); + break; + } + + Neighborhood::takeItemFromRoom(item); +} + +void Mars::pickedUpItem(Item *item) { + switch (item->getObjectID()) { + case kAirMask: + setCurrentActivation(kActivateHotSpotAlways); + if (!GameState.getScoringGotOxygenMask()) { + g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Mars/XM48SB", false, kWarningInterruption); + GameState.setScoringGotOxygenMask(); + } + break; + case kCrowbar: + GameState.setScoringGotCrowBar(); + g_AIArea->checkMiddleArea(); + break; + case kMarsCard: + GameState.setScoringGotMarsCard(); + g_AIArea->checkMiddleArea(); + break; + case kCardBomb: + GameState.setScoringGotCardBomb(); + if (GameState.getMarsLockBroken()) { + startExtraSequence(kMars57BackToNormal, kExtraCompletedFlag, kFilterNoInput); + GameState.setMarsLockBroken(false); + } + + _privateFlags.setFlag(kMarsPrivateDraggingBombFlag, false); + break; + case kMapBiochip: + if (_privateFlags.getFlag(kMarsPrivateGotMapChipFlag) && + _privateFlags.getFlag(kMarsPrivateGotShieldChipFlag) && + _privateFlags.getFlag(kMarsPrivateGotOpticalChipFlag)) { + GameState.setMarsFinished(true); + GameState.setScoringMarsGandhi(); + startExtraSequence(kMarsRobotHeadClose, kExtraCompletedFlag, kFilterNoInput); + } + break; + case kShieldBiochip: + if (_privateFlags.getFlag(kMarsPrivateGotMapChipFlag) && + _privateFlags.getFlag(kMarsPrivateGotShieldChipFlag) && + _privateFlags.getFlag(kMarsPrivateGotOpticalChipFlag)) { + GameState.setMarsFinished(true); + GameState.setScoringMarsGandhi(); + startExtraSequence(kMarsRobotHeadClose, kExtraCompletedFlag, kFilterNoInput); + } + break; + case kOpticalBiochip: + g_opticalChip->addAries(); + GameState.setScoringGotMarsOpMemChip(); + + if (_privateFlags.getFlag(kMarsPrivateGotMapChipFlag) && + _privateFlags.getFlag(kMarsPrivateGotShieldChipFlag) && + _privateFlags.getFlag(kMarsPrivateGotOpticalChipFlag)) { + GameState.setMarsFinished(true); + GameState.setScoringMarsGandhi(); + startExtraSequence(kMarsRobotHeadClose, kExtraCompletedFlag, kFilterNoInput); + } + break; + } +} + +void Mars::dropItemIntoRoom(Item *item, Hotspot *dropSpot) { + if (dropSpot->getObjectID() == kAttackRobotHotSpotID) { + _attackingItem = (InventoryItem *)item; + startExtraSequence(kMars48RobotDefends, kExtraCompletedFlag, kFilterNoInput); + loadLoopSound2(""); + } else { + switch (item->getObjectID()) { + case kMarsCard: + Neighborhood::dropItemIntoRoom(item, dropSpot); + if (dropSpot && dropSpot->getObjectID() == kMars34NorthCardDropSpotID) + startExtraSequence(kMarsTurnOnPod, kExtraCompletedFlag, kFilterNoInput); + break; + case kNitrogenCanister: + Neighborhood::dropItemIntoRoom(item, dropSpot); + if (dropSpot && dropSpot->getObjectID() == kMars57DropNitrogenSpotID) + startExtraSequence(kMars57FreezeLock, kExtraCompletedFlag, kFilterNoInput); + break; + case kCrowbar: + _utilityFuse.stopFuse(); + Neighborhood::dropItemIntoRoom(item, dropSpot); + if (dropSpot && dropSpot->getObjectID() == kMars57DropCrowBarSpotID) + startExtraSequence(kMars57BreakLock, kExtraCompletedFlag, kFilterNoInput); + break; + case kAirMask: + if (dropSpot) { + if (dropSpot->getObjectID() == kMars49AirFillingDropSpotID) { + if (!GameState.getMarsMaskOnFiller()) { + Neighborhood::dropItemIntoRoom(item, dropSpot); + startExtraSequence(kMars49SouthViewMaskFilling, kExtraCompletedFlag, kFilterNoInput); + } else { + setCurrentActivation(kActivateMaskOnFiller); + setCurrentAlternate(kAltMarsMaskOnFiller); + Neighborhood::dropItemIntoRoom(item, dropSpot); + } + } else if (dropSpot->getObjectID() == kMars49AirMaskSpotID) { + setCurrentAlternate(kAltMarsNormal); + setCurrentActivation(kActivateMaskOnHolder); + Neighborhood::dropItemIntoRoom(item, dropSpot); + } + } + break; + case kCardBomb: + _privateFlags.setFlag(kMarsPrivateDraggingBombFlag, false); + Neighborhood::dropItemIntoRoom(item, dropSpot); + break; + case kMapBiochip: + _privateFlags.setFlag(kMarsPrivateGotMapChipFlag, false); + Neighborhood::dropItemIntoRoom(item, dropSpot); + break; + case kShieldBiochip: + _privateFlags.setFlag(kMarsPrivateGotShieldChipFlag, false); + Neighborhood::dropItemIntoRoom(item, dropSpot); + break; + case kOpticalBiochip: + _privateFlags.setFlag(kMarsPrivateGotOpticalChipFlag, false); + Neighborhood::dropItemIntoRoom(item, dropSpot); + break; + default: + Neighborhood::dropItemIntoRoom(item, dropSpot); + break; + } + } +} + +void Mars::robotTiredOfWaiting() { + if (GameState.getCurrentRoomAndView() == MakeRoomView(kMars48, kEast)) { + if (_attackingItem) { + startExtraSequence(kMars48RobotKillsPlayer, kExtraCompletedFlag, kFilterNoInput); + loadLoopSound2(""); + } else { + _privateFlags.setFlag(kMarsPrivateRobotTiredOfWaitingFlag, true); + } + } else { + die(kDeathDidntGetOutOfWay); + } +} + +void Mars::turnLeft() { + if (isEventTimerRunning()) + cancelEvent(); + + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kMars34, kSouth): + if (_privateFlags.getFlag(kMarsPrivatePodStorageOpenFlag)) { + _privateFlags.setFlag(kMarsPrivatePodTurnLeftFlag, true); + if (GameState.isTakenItemID(kCrowbar)) + startExtraSequence(kMars34SpotCloseNoBar, kExtraCompletedFlag, kFilterNoInput); + else + startExtraSequence(kMars34SpotCloseWithBar, kExtraCompletedFlag, kFilterNoInput); + } else { + Neighborhood::turnLeft(); + } + break; + case MakeRoomView(kMars45, kNorth): + if (_privateFlags.getFlag(kMarsPrivatePodStorageOpenFlag)) { + _privateFlags.setFlag(kMarsPrivatePodTurnLeftFlag, true); + if (GameState.isTakenItemID(kCrowbar)) + startExtraSequence(kMars45SpotCloseNoBar, kExtraCompletedFlag, kFilterNoInput); + else + startExtraSequence(kMars45SpotCloseWithBar, kExtraCompletedFlag, kFilterNoInput); + } else { + Neighborhood::turnLeft(); + } + break; + default: + Neighborhood::turnLeft(); + break; + } +} + +void Mars::turnRight() { + if (isEventTimerRunning()) + cancelEvent(); + + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kMars34, kSouth): + if (_privateFlags.getFlag(kMarsPrivatePodStorageOpenFlag)) { + _privateFlags.setFlag(kMarsPrivatePodTurnRightFlag, true); + if (GameState.isTakenItemID(kCrowbar)) + startExtraSequence(kMars34SpotCloseNoBar, kExtraCompletedFlag, kFilterNoInput); + else + startExtraSequence(kMars34SpotCloseWithBar, kExtraCompletedFlag, kFilterNoInput); + } else { + Neighborhood::turnRight(); + } + break; + case MakeRoomView(kMars45, kNorth): + if (_privateFlags.getFlag(kMarsPrivatePodStorageOpenFlag)) { + _privateFlags.setFlag(kMarsPrivatePodTurnRightFlag, true); + if (GameState.isTakenItemID(kCrowbar)) + startExtraSequence(kMars45SpotCloseNoBar, kExtraCompletedFlag, kFilterNoInput); + else + startExtraSequence(kMars45SpotCloseWithBar, kExtraCompletedFlag, kFilterNoInput); + } else { + Neighborhood::turnRight(); + } + break; + default: + Neighborhood::turnRight(); + break; + } +} + +void Mars::receiveNotification(Notification *notification, const NotificationFlags flag) { + InventoryItem *item; + + Neighborhood::receiveNotification(notification, flag); + + if ((flag & kExtraCompletedFlag) != 0) { + _interruptionFilter = kFilterAllInput; + + switch (_lastExtra) { + case kMarsArrivalFromTSA: + GameState.setMarsSeenTimeStream(true); + loadAmbientLoops(); + playSpotSoundSync(kMarsShuttle1DepartedIn, kMarsShuttle1DepartedOut); + makeContinuePoint(); + break; + case kRobotThrowsPlayer: + GameState.setMarsRobotThrownPlayer(true); + GameState.setScoringThrownByRobot(); + restoreStriding(kMars08, kNorth, kAltMarsNormal); + arriveAt(kMars08, kNorth); + if (!GameState.getMarsHeardUpperPodMessage()) { + playSpotSoundSync(kMarsPodDepartedUpperPlatformIn, + kMarsPodDepartedUpperPlatformOut); + GameState.setMarsHeardUpperPodMessage(true); + } + break; + case kMarsInfoKioskIntro: + GameState.setScoringSawMarsKiosk(); + setCurrentActivation(kActivationKioskChoice); + break; + case kMars33SlideShow4: + GameState.setScoringSawTransportMap(); + setCurrentActivation(kActivateHotSpotAlways); + break; + case kMars34SpotOpenNoBar: + case kMars34SpotOpenWithBar: + case kMars45SpotOpenNoBar: + case kMars45SpotOpenWithBar: + _privateFlags.setFlag(kMarsPrivatePodStorageOpenFlag, true); + setCurrentActivation(kActivateMarsPodOpen); + break; + case kMars34SpotCloseNoBar: + case kMars34SpotCloseWithBar: + case kMars45SpotCloseNoBar: + case kMars45SpotCloseWithBar: + _privateFlags.setFlag(kMarsPrivatePodStorageOpenFlag, false); + setCurrentActivation(kActivateMarsPodClosed); + if (_privateFlags.getFlag(kMarsPrivatePodTurnLeftFlag)) { + _privateFlags.setFlag(kMarsPrivatePodTurnLeftFlag, false); + turnLeft(); + } else if (_privateFlags.getFlag(kMarsPrivatePodTurnRightFlag)) { + _privateFlags.setFlag(kMarsPrivatePodTurnRightFlag, false); + turnRight(); + } + break; + case kMarsTurnOnPod: + item = (InventoryItem *)_vm->getAllItems().findItemByID(kMarsCard); + _vm->addItemToInventory(item); + GameState.setScoringTurnedOnTransport(); + loadLoopSound1(""); + loadLoopSound2(""); + startExtraSequence(kMarsTakePodToMars45, kExtraCompletedFlag, kFilterNoInput); + break; + case kMarsTakePodToMars45: + arriveAt(kMars45, kSouth); + break; + case kMars35WestSpinAirlockToEast: + GameState.setMarsAirlockOpen(false); + setCurrentAlternate(kAltMars35AirlockEast); + turnTo(kWest); + setCurrentActivation(kActivateReadyToPressurizeAirlock); + g_airMask->airQualityChanged(); + checkAirMask(); + loadAmbientLoops(); + break; + case kMars35EastSpinAirlockToWest: + GameState.setMarsAirlockOpen(true); + setCurrentAlternate(kAltMars35AirlockWest); + turnTo(kEast); + setCurrentActivation(kActivateReadyToPressurizeAirlock); + g_airMask->airQualityChanged(); + checkAirMask(); + loadAmbientLoops(); + break; + case kMars48RobotApproaches: + loadLoopSound2("Sounds/Mars/Robot Loop.aiff", 0x100, 0, 0); + GameState.setMarsSeenRobotAtReactor(true); + loopExtraSequence(kMars48RobotLoops); + _utilityFuse.primeFuse(kMarsRobotPatienceLimit); + _utilityFuse.setFunctor(new Common::Functor0Mem<void, Mars>(this, &Mars::robotTiredOfWaiting)); + _utilityFuse.lightFuse(); + break; + case kMars48RobotDefends: + _vm->addItemToInventory(_attackingItem); + _attackingItem = 0; + if (_privateFlags.getFlag(kMarsPrivateRobotTiredOfWaitingFlag)) { + startExtraSequence(kMars48RobotKillsPlayer, kExtraCompletedFlag, kFilterNoInput); + loadLoopSound2("", 0x100, 0, 0); + } else { + loadLoopSound2("Sounds/Mars/Robot Loop.aiff", 0x100, 0, 0); + loopExtraSequence(kMars48RobotLoops, kExtraCompletedFlag); + } + break; + case kMars48RobotKillsPlayer: + loadLoopSound2(""); + die(kDeathDidntGetOutOfWay); + break; + case kMars49SouthViewMaskFilling: + setCurrentActivation(kActivateMaskOnFiller); + setCurrentAlternate(kAltMarsMaskOnFiller); + GameState.setMarsMaskOnFiller(true); + break; + case kMars58SpinLeft: + case kMars54SpinRight: + GameState.setScoringActivatedPlatform(); + arriveAt(kMars52, kEast); + break; + case kMars52SpinLeft: + case kMars56SpinRight: + GameState.setScoringActivatedPlatform(); + arriveAt(kMars54, kEast); + break; + case kMars54SpinLeft: + case kMars58SpinRight: + GameState.setScoringActivatedPlatform(); + arriveAt(kMars56, kEast); + break; + case kMars56SpinLeft: + case kMars52SpinRight: + GameState.setScoringActivatedPlatform(); + arriveAt(kMars58, kEast); + break; + case kMars52Extend: + case kMars54Extend: + case kMars56ExtendNoBomb: + case kMars58Extend: + GameState.setScoringActivatedPlatform(); + setCurrentActivation(kActivateReactorPlatformIn); + _privateFlags.setFlag(kMarsPrivatePlatformZoomedInFlag, true); + break; + case kMars53Retract: + case kMars55Retract: + case kMars57RetractWithBomb: + case kMars57RetractNoBomb: + case kMars59Retract: + GameState.setScoringActivatedPlatform(); + setCurrentActivation(kActivateReactorPlatformOut); + _privateFlags.setFlag(kMarsPrivatePlatformZoomedInFlag, false); + break; + case kMars56ExtendWithBomb: + playSpotSoundSync(kMustBeUnlockedIn, kMustBeUnlockedOut); + GameState.setScoringActivatedPlatform(); + _privateFlags.setFlag(kMarsPrivatePlatformZoomedInFlag, true); + break; + case kMars57CantOpenPanel: + GameState.setScoringActivatedPlatform(); + setCurrentActivation(kActivateReactorAskLowerScreen); + break; + case kMars57LowerScreenClosed: + case kMars57ThawLock: + setCurrentActivation(kActivateReactorReadyForNitrogen); + GameState.setMarsLockFrozen(false); + break; + case kMars57FreezeLock: + item = (InventoryItem *)_vm->getAllItems().findItemByID(kNitrogenCanister); + item->setItemState(kNitrogenEmpty); + _vm->addItemToInventory(item); + setCurrentActivation(kActivateReactorReadyForCrowBar); + GameState.setScoringUsedLiquidNitrogen(); + GameState.setMarsLockFrozen(true); + showExtraView(kMars57LockFrozenView); + _utilityFuse.primeFuse(kLockFreezeTimeLmit); + _utilityFuse.setFunctor(new Common::Functor0Mem<void, Mars>(this, &Mars::lockThawed)); + _utilityFuse.lightFuse(); + break; + case kMars57BreakLock: + item = (InventoryItem *)_vm->getAllItems().findItemByID(kCrowbar); + _vm->addItemToInventory(item); + GameState.setScoringUsedCrowBar(); + GameState.setMarsLockBroken(true); + GameState.setMarsLockFrozen(false); + startExtraLongSequence(kMars57OpenPanel, kMars57OpenPanelChoices, kExtraCompletedFlag, kFilterNoInput); + break; + case kMars57OpenPanel: + case kMars57OpenPanelChoices: + setCurrentActivation(kActivateReactorAskOperation); + break; + case kMars57ShieldEvaluation: + case kMars57MeasureOutput: + setCurrentActivation(kActivateReactorRanEvaluation); + loopExtraSequence(kMars57ShieldOkayLoop); + break; + case kMars57RunDiagnostics: + setCurrentActivation(kActivateReactorRanDiagnostics); + GameState.setScoringFoundCardBomb(); + break; + case kMars57BombExplodes: + case kMars57BombExplodesInGame: + die(kDeathDidntDisarmMarsBomb); + break; + case kMars57BombAnalysis: + setCurrentActivation(kActivateReactorAnalyzed); + break; + case kMars57DontLink: + startExtraSequence(kMars57OpenPanelChoices, kExtraCompletedFlag, kFilterNoInput); + break; + case kMars57CircuitLink: + setCurrentActivation(kActivateReactorInstructions); + break; + case kMars57GameLevel1: + setUpReactorLevel1(); + break; + case kMars57GameLevel2: + case kMars57GameLevel3: + setUpNextReactorLevel(); + break; + case kMars57GameSolved: + setCurrentActivation(kActivateReactorBombSafe); + break; + case kMars57ExposeBomb: + setCurrentActivation(kActivateReactorBombExposed); + _privateFlags.setFlag(kMarsPrivateBombExposedFlag, true); + break; + case kMars57BackToNormal: + setCurrentActivation(kActivateReactorPlatformIn); + _privateFlags.setFlag(kMarsPrivateBombExposedFlag, false); + g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Mars/XM51SW", false, kWarningInterruption); + break; + case kMars60WestSpinAirlockToEast: + GameState.setMarsAirlockOpen(true); + setCurrentAlternate(kAltMars60AirlockEast); + turnTo(kWest); + setCurrentActivation(kActivateReadyToPressurizeAirlock); + g_airMask->airQualityChanged(); + checkAirMask(); + loadAmbientLoops(); + break; + case kMars60EastSpinAirlockToWest: + GameState.setMarsAirlockOpen(false); + setCurrentAlternate(kAltMars60AirlockWest); + turnTo(kEast); + setCurrentActivation(kActivateReadyToPressurizeAirlock); + g_airMask->airQualityChanged(); + checkAirMask(); + loadAmbientLoops(); + break; + case kMarsRobotHeadOpen: + setCurrentActivation(kActivationRobotHeadOpen); + break; + case kMarsRobotHeadClose: + recallToTSASuccess(); + break; + case kMarsMaze007RobotApproach: + case kMarsMaze015SouthRobotApproach: + case kMarsMaze101EastRobotApproach: + case kMarsMaze104WestLoop: + case kMarsMaze133SouthApproach: + case kMarsMaze136NorthApproach: + case kMarsMaze184WestLoop: + die(kDeathGroundByMazebot); + break; + } + } else if ((flag & kTimeForCanyonChaseFlag) != 0) { + doCanyonChase(); + } else if ((flag & kExplosionFinishedFlag) != 0) { + _explosions.stop(); + _explosions.hide(); + if (g_robotShip->isDead()) { + GameState.setMarsFinished(true); + _centerShuttleMovie.hide(); + _upperRightShuttleMovie.show(); + _upperRightShuttleMovie.setTime(kShuttleUpperRightTargetDestroyedTime); + _upperRightShuttleMovie.redrawMovieWorld(); + _rightDamageShuttleMovie.hide(); + playMovieSegment(&_rightShuttleMovie, kShuttleRightDestroyedStart, kShuttleRightDestroyedStop); + playSpotSoundSync(kShuttleDestroyedIn, kShuttleDestroyedOut); + throwAwayMarsShuttle(); + reinstateMonocleInterface(); + recallToTSASuccess(); + } + } else if ((flag & kTimeToTransportFlag) != 0) { + transportToRobotShip(); + } + + if (g_AIArea) + g_AIArea->checkMiddleArea(); +} + +void Mars::spotCompleted() { + Neighborhood::spotCompleted(); + + if (GameState.getCurrentRoom() == kMarsRobotShuttle) + g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Mars/XN59WD", false, kWarningInterruption); +} + +void Mars::doCanyonChase() { + GameState.setScoringEnteredShuttle(); + setNextHandler(_vm); + throwAwayInterface(); + + _vm->_cursor->hide(); + + // Open the spot sounds movie again... + _spotSounds.initFromQuickTime(getSoundSpotsName()); + _spotSounds.setVolume(_vm->getSoundFXLevel()); + + Video::VideoDecoder *video = new Video::QuickTimeDecoder(); + if (!video->loadFile("Images/Mars/M44ESA.movie")) + error("Could not load interface->shuttle transition video"); + + video->start(); + + while (!_vm->shouldQuit() && !video->endOfVideo()) { + if (video->needsUpdate()) { + const Graphics::Surface *frame = video->decodeNextFrame(); + + if (frame) + _vm->drawScaledFrame(frame, 0, 0); + } + + Common::Event event; + while (g_system->getEventManager()->pollEvent(event)) + ; + + g_system->delayMillis(10); + } + + delete video; + + if (_vm->shouldQuit()) + return; + + initOnePicture(&_shuttleInterface1, "Images/Mars/MCmain1.pict", kShuttleBackgroundOrder, kShuttle1Left, + kShuttle1Top, true); + initOnePicture(&_shuttleInterface2, "Images/Mars/MCmain2.pict", kShuttleBackgroundOrder, kShuttle2Left, + kShuttle2Top, true); + initOnePicture(&_shuttleInterface3, "Images/Mars/MCmain3.pict", kShuttleBackgroundOrder, kShuttle3Left, + kShuttle3Top, true); + initOnePicture(&_shuttleInterface4, "Images/Mars/MCmain4.pict", kShuttleBackgroundOrder, kShuttle4Left, + kShuttle4Top, true); + + initOneMovie(&_canyonChaseMovie, "Images/Mars/Canyon.movie", + kShuttleMonitorOrder, kShuttleWindowLeft, kShuttleWindowTop, true); + _canyonChaseMovie.setVolume(_vm->getSoundFXLevel()); + + loadLoopSound1("Sounds/Mars/Inside Cockpit.22K.8.AIFF"); + + // Swing shuttle around... + playMovieSegment(&_canyonChaseMovie, kShuttleSwingStart, kShuttleSwingStop); + + initOneMovie(&_leftShuttleMovie, "Images/Mars/Left Shuttle.movie", + kShuttleMonitorOrder, kShuttleLeftLeft, kShuttleLeftTop, false); + + initOneMovie(&_rightShuttleMovie, "Images/Mars/Right Shuttle.movie", + kShuttleMonitorOrder, kShuttleRightLeft, kShuttleRightTop, false); + + initOneMovie(&_lowerLeftShuttleMovie, "Images/Mars/Lower Left Shuttle.movie", kShuttleMonitorOrder, + kShuttleLowerLeftLeft, kShuttleLowerLeftTop, false); + + initOneMovie(&_lowerRightShuttleMovie, "Images/Mars/Lower Right Shuttle.movie", kShuttleMonitorOrder, + kShuttleLowerRightLeft, kShuttleLowerRightTop, false); + + initOneMovie(&_centerShuttleMovie, "Images/Mars/Center Shuttle.movie", + kShuttleMonitorOrder, kShuttleCenterLeft, kShuttleCenterTop, false); + + initOneMovie(&_upperLeftShuttleMovie, "Images/Mars/Upper Left Shuttle.movie", kShuttleMonitorOrder, + kShuttleUpperLeftLeft, kShuttleUpperLeftTop, false); + + initOneMovie(&_upperRightShuttleMovie, "Images/Mars/Upper Right Shuttle.movie", kShuttleMonitorOrder, + kShuttleUpperRightLeft, kShuttleUpperRightTop, false); + + initOneMovie(&_leftDamageShuttleMovie, "Images/Mars/Left Damage Shuttle.movie", + kShuttleStatusOrder, kShuttleLeftEnergyLeft, kShuttleLeftEnergyTop, false); + + initOneMovie(&_rightDamageShuttleMovie, "Images/Mars/Right Damage Shuttle.movie", + kShuttleStatusOrder, kShuttleRightEnergyLeft, kShuttleRightEnergyTop, false); + + _centerShuttleMovie.show(); + _centerShuttleMovie.setTime(kShuttleCenterBoardingTime); + playSpotSoundSync(kShuttleCockpitIn, kShuttleCockpitOut); + + _centerShuttleMovie.setTime(kShuttleCenterCheckTime); + playSpotSoundSync(kShuttleOnboardIn, kShuttleOnboardOut); + + _shuttleEnergyMeter.initShuttleEnergyMeter(); + _shuttleEnergyMeter.powerUpMeter(); + while (_shuttleEnergyMeter.isFading()) { + _vm->checkCallBacks(); + _vm->refreshDisplay(); + g_system->updateScreen(); + } + + _leftShuttleMovie.show(); + playMovieSegment(&_leftShuttleMovie, kShuttleLeftIntroStart, kShuttleLeftIntroStop); + + _leftShuttleMovie.setTime(kShuttleLeftNormalTime); + _leftShuttleMovie.redrawMovieWorld(); + + _leftDamageShuttleMovie.show(); + playMovieSegment(&_leftDamageShuttleMovie); + + // Take it down a tick initially. This sets the time to the time of the last tick, + // so that subsequence drops will drop it down a tick. + _leftDamageShuttleMovie.setTime(_leftDamageShuttleMovie.getTime() - 40); + _leftDamageShuttleMovie.redrawMovieWorld(); + + _lowerRightShuttleMovie.show(); + _lowerRightShuttleMovie.setTime(kShuttleLowerRightOffTime); + _lowerRightShuttleMovie.redrawMovieWorld(); + _centerShuttleMovie.setTime(kShuttleCenterNavCompTime); + _centerShuttleMovie.redrawMovieWorld(); + playSpotSoundSync(kShuttleNavigationIn, kShuttleNavigationOut); + + _centerShuttleMovie.setTime(kShuttleCenterCommTime); + _centerShuttleMovie.redrawMovieWorld(); + playSpotSoundSync(kShuttleCommunicationIn, kShuttleCommunicationOut); + + _centerShuttleMovie.setTime(kShuttleCenterAllSystemsTime); + _centerShuttleMovie.redrawMovieWorld(); + playSpotSoundSync(kShuttleAllSystemsIn, kShuttleAllSystemsOut); + + _centerShuttleMovie.setTime(kShuttleCenterSecureLooseTime); + _centerShuttleMovie.redrawMovieWorld(); + playSpotSoundSync(kShuttleSecureLooseIn, kShuttleSecureLooseOut); + + _centerShuttleMovie.setTime(kShuttleCenterAutoTestTime); + _centerShuttleMovie.redrawMovieWorld(); + playSpotSoundSync(kShuttleAutoTestingIn, kShuttleAutoTestingOut); + + _leftShuttleMovie.setTime(kShuttleLeftAutoTestTime); + _leftShuttleMovie.redrawMovieWorld(); + playSpotSoundSync(kMarsThrusterAutoTestIn, kMarsThrusterAutoTestOut); + _leftShuttleMovie.setTime(kShuttleLeftNormalTime); + _leftShuttleMovie.redrawMovieWorld(); + + _centerShuttleMovie.setTime(kShuttleCenterLaunchTime); + _centerShuttleMovie.redrawMovieWorld(); + playSpotSoundSync(kShuttlePrepareForDropIn, kShuttlePrepareForDropOut); + + playSpotSoundSync(kShuttleAllClearIn, kShuttleAllClearOut); + + _centerShuttleMovie.setTime(kShuttleCenterEnterTubeTime); + _centerShuttleMovie.redrawMovieWorld(); + + _lowerLeftShuttleMovie.show(); + _lowerLeftShuttleMovie.setTime(kShuttleLowerLeftCollisionTime); + + loadLoopSound1(""); + + _canyonChaseMovie.setSegment(kCanyonChaseStart, kCanyonChaseStop); + _canyonChaseMovie.start(); + + startMarsTimer(kLaunchTubeReachedTime, kMovieTicksPerSecond, kMarsLaunchTubeReached); +} + +void Mars::startUpFromFinishedSpaceChase() { + setNextHandler(_vm); + throwAwayInterface(); + + initOnePicture(&_shuttleInterface1, "Images/Mars/MCmain1.pict", kShuttleBackgroundOrder, kShuttle1Left, + kShuttle1Top, true); + initOnePicture(&_shuttleInterface2, "Images/Mars/MCmain2.pict", kShuttleBackgroundOrder, kShuttle2Left, + kShuttle2Top, true); + initOnePicture(&_shuttleInterface3, "Images/Mars/MCmain3.pict", kShuttleBackgroundOrder, kShuttle3Left, + kShuttle3Top, true); + initOnePicture(&_shuttleInterface4, "Images/Mars/MCmain4.pict", kShuttleBackgroundOrder, kShuttle4Left, + kShuttle4Top, true); + + initOneMovie(&_leftShuttleMovie, "Images/Mars/Left Shuttle.movie", + kShuttleMonitorOrder, kShuttleLeftLeft, kShuttleLeftTop, false); + + initOneMovie(&_rightShuttleMovie, "Images/Mars/Right Shuttle.movie", + kShuttleMonitorOrder, kShuttleRightLeft, kShuttleRightTop, false); + + initOneMovie(&_lowerLeftShuttleMovie, "Images/Mars/Lower Left Shuttle.movie", kShuttleMonitorOrder, + kShuttleLowerLeftLeft, kShuttleLowerLeftTop, false); + + initOneMovie(&_lowerRightShuttleMovie, "Images/Mars/Lower Right Shuttle.movie", kShuttleMonitorOrder, + kShuttleLowerRightLeft, kShuttleLowerRightTop, false); + + initOneMovie(&_centerShuttleMovie, "Images/Mars/Center Shuttle.movie", + kShuttleMonitorOrder, kShuttleCenterLeft, kShuttleCenterTop, false); + + initOneMovie(&_upperLeftShuttleMovie, "Images/Mars/Upper Left Shuttle.movie", kShuttleMonitorOrder, + kShuttleUpperLeftLeft, kShuttleUpperLeftTop, false); + + initOneMovie(&_upperRightShuttleMovie, "Images/Mars/Upper Right Shuttle.movie", kShuttleMonitorOrder, + kShuttleUpperRightLeft, kShuttleUpperRightTop, false); + + initOneMovie(&_leftDamageShuttleMovie, "Images/Mars/Left Damage Shuttle.movie", + kShuttleStatusOrder, kShuttleLeftEnergyLeft, kShuttleLeftEnergyTop, false); + + initOneMovie(&_rightDamageShuttleMovie, "Images/Mars/Right Damage Shuttle.movie", + kShuttleStatusOrder, kShuttleRightEnergyLeft, kShuttleRightEnergyTop, false); + + _centerShuttleMovie.show(); + + _shuttleEnergyMeter.initShuttleEnergyMeter(); + _shuttleEnergyMeter.setEnergyValue(kFullShuttleEnergy); + + _leftShuttleMovie.show(); + _leftShuttleMovie.setTime(kShuttleLeftNormalTime); + _leftShuttleMovie.redrawMovieWorld(); + + _leftDamageShuttleMovie.show(); + _leftDamageShuttleMovie.setTime(_leftDamageShuttleMovie.getDuration() - 40); + _leftDamageShuttleMovie.redrawMovieWorld(); + + _lowerRightShuttleMovie.show(); + + _lowerLeftShuttleMovie.show(); + + loadLoopSound1("Sounds/Mars/Space Ambient.22K.8.AIFF"); + + initOneMovie(&_junk, "Images/Mars/Junk.movie", kShuttleJunkOrder, kShuttleJunkLeft, + kShuttleJunkTop, false); + + initOneMovie(&_explosions, "Images/Mars/Explosions.movie", kShuttleWeaponFrontOrder, 0, 0, false); + _explosionCallBack.initCallBack(&_explosions, kCallBackAtExtremes); + + _energyBeam.initShuttleWeapon(); + _gravitonCannon.initShuttleWeapon(); + + _upperLeftShuttleMovie.show(); + _upperLeftShuttleMovie.setTime(kShuttleUpperLeftDimTime); + _upperLeftShuttleMovie.redrawMovieWorld(); + + _rightShuttleMovie.show(); + _rightShuttleMovie.setTime(kShuttleRightIntroStop - 1); + _rightShuttleMovie.redrawMovieWorld(); + + _rightDamageShuttleMovie.show(); + _rightDamageShuttleMovie.setTime(40); + _rightDamageShuttleMovie.redrawMovieWorld(); + + _lowerLeftShuttleMovie.setTime(kShuttleLowerLeftAutopilotTime); + _lowerLeftShuttleMovie.redrawMovieWorld(); + + _shuttleTransportSpot.setArea(kShuttleTransportBounds); + _shuttleTransportSpot.setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag); + _vm->getAllHotspots().push_back(&_shuttleTransportSpot); + + _privateFlags.setFlag(kMarsPrivateInSpaceChaseFlag, true); + + _upperRightShuttleMovie.show(); + _upperRightShuttleMovie.setTime(kShuttleUpperRightOverloadTime); + _upperRightShuttleMovie.redrawMovieWorld(); + + _centerShuttleMovie.setTime(kShuttleCenterSafeTime); + _centerShuttleMovie.redrawMovieWorld(); + + _lowerRightShuttleMovie.setTime(kShuttleLowerRightTransportTime); + _lowerRightShuttleMovie.redrawMovieWorld(); + + initOneMovie(&_canyonChaseMovie, "Images/Mars/M98EAS.movie", kShuttleTractorBeamMovieOrder, + kShuttleWindowLeft, kShuttleWindowTop, true); + _canyonChaseMovie.setTime(_canyonChaseMovie.getDuration()); + _canyonChaseMovie.redrawMovieWorld(); +} + +void Mars::startUpFromSpaceChase() { + setNextHandler(_vm); + throwAwayInterface(); + + // Open the spot sounds movie again... + _spotSounds.initFromQuickTime(getSoundSpotsName()); + _spotSounds.setVolume(_vm->getSoundFXLevel());; + + initOnePicture(&_shuttleInterface1, "Images/Mars/MCmain1.pict", kShuttleBackgroundOrder, kShuttle1Left, + kShuttle1Top, true); + initOnePicture(&_shuttleInterface2, "Images/Mars/MCmain2.pict", kShuttleBackgroundOrder, kShuttle2Left, + kShuttle2Top, true); + initOnePicture(&_shuttleInterface3, "Images/Mars/MCmain3.pict", kShuttleBackgroundOrder, kShuttle3Left, + kShuttle3Top, true); + initOnePicture(&_shuttleInterface4, "Images/Mars/MCmain4.pict", kShuttleBackgroundOrder, kShuttle4Left, + kShuttle4Top, true); + + initOneMovie(&_leftShuttleMovie, "Images/Mars/Left Shuttle.movie", + kShuttleMonitorOrder, kShuttleLeftLeft, kShuttleLeftTop, false); + + initOneMovie(&_rightShuttleMovie, "Images/Mars/Right Shuttle.movie", + kShuttleMonitorOrder, kShuttleRightLeft, kShuttleRightTop, false); + + initOneMovie(&_lowerLeftShuttleMovie, "Images/Mars/Lower Left Shuttle.movie", kShuttleMonitorOrder, + kShuttleLowerLeftLeft, kShuttleLowerLeftTop, false); + + initOneMovie(&_lowerRightShuttleMovie, "Images/Mars/Lower Right Shuttle.movie", kShuttleMonitorOrder, + kShuttleLowerRightLeft, kShuttleLowerRightTop, false); + + initOneMovie(&_centerShuttleMovie, "Images/Mars/Center Shuttle.movie", + kShuttleMonitorOrder, kShuttleCenterLeft, kShuttleCenterTop, false); + + initOneMovie(&_upperLeftShuttleMovie, "Images/Mars/Upper Left Shuttle.movie", kShuttleMonitorOrder, + kShuttleUpperLeftLeft, kShuttleUpperLeftTop, false); + + initOneMovie(&_upperRightShuttleMovie, "Images/Mars/Upper Right Shuttle.movie", kShuttleMonitorOrder, + kShuttleUpperRightLeft, kShuttleUpperRightTop, false); + + initOneMovie(&_leftDamageShuttleMovie, "Images/Mars/Left Damage Shuttle.movie", + kShuttleStatusOrder, kShuttleLeftEnergyLeft, kShuttleLeftEnergyTop, false); + + initOneMovie(&_rightDamageShuttleMovie, "Images/Mars/Right Damage Shuttle.movie", + kShuttleStatusOrder, kShuttleRightEnergyLeft, kShuttleRightEnergyTop, false); + + _centerShuttleMovie.show(); + + _shuttleEnergyMeter.initShuttleEnergyMeter(); + _shuttleEnergyMeter.setEnergyValue(kFullShuttleEnergy); + + _leftShuttleMovie.show(); + _leftShuttleMovie.setTime(kShuttleLeftNormalTime); + _leftShuttleMovie.redrawMovieWorld(); + + _leftDamageShuttleMovie.show(); + _leftDamageShuttleMovie.setTime(_leftDamageShuttleMovie.getDuration() - 40); + _leftDamageShuttleMovie.redrawMovieWorld(); + + _lowerRightShuttleMovie.show(); + + _lowerLeftShuttleMovie.show(); + + loadLoopSound1("Sounds/Mars/Space Ambient.22K.8.AIFF"); + + initOneMovie(&_planetMovie, "Images/Mars/Planet.movie", kShuttlePlanetOrder, + kPlanetStartLeft, kPlanetStartTop, true); + _planetMovie.setFlags(kLoopTimeBase); + + initOneMovie(&_junk, "Images/Mars/Junk.movie", kShuttleJunkOrder, kShuttleJunkLeft, + kShuttleJunkTop, false); + + initOneMovie(&_explosions, "Images/Mars/Explosions.movie", kShuttleWeaponFrontOrder, 0, 0, false); + _explosionCallBack.initCallBack(&_explosions, kCallBackAtExtremes); + + _energyBeam.initShuttleWeapon(); + _gravitonCannon.initShuttleWeapon(); + + _upperLeftShuttleMovie.show(); + + _robotShip.initRobotShip(); + + _planetMovie.start(); + _planetMover.startMoving(&_planetMovie); + + _upperLeftShuttleMovie.setTime(kShuttleUpperLeftDimTime); + _upperLeftShuttleMovie.redrawMovieWorld(); + + _centerShuttleMovie.setTime(kShuttleCenterTargetSightedTime); + _centerShuttleMovie.redrawMovieWorld(); + + _lowerRightShuttleMovie.setTime(kShuttleLowerRightTrackingTime); + _lowerRightShuttleMovie.redrawMovieWorld(); + + _rightShuttleMovie.show(); + _rightShuttleMovie.setTime(kShuttleRightIntroStop - 1); + _rightShuttleMovie.redrawMovieWorld(); + + _rightDamageShuttleMovie.show(); + _rightDamageShuttleMovie.setTime(_rightDamageShuttleMovie.getDuration() - 40); + _rightDamageShuttleMovie.redrawMovieWorld(); + + _lowerLeftShuttleMovie.setTime(kShuttleLowerLeftAutopilotTime); + _lowerLeftShuttleMovie.redrawMovieWorld(); + + _robotShip.startMoving(); + + _shuttleHUD.initShuttleHUD(); + + _tractorBeam.startDisplaying(); + + _energyChoiceSpot.setArea(kShuttleEnergyBeamBounds); + _energyChoiceSpot.setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag); + _vm->getAllHotspots().push_back(&_energyChoiceSpot); + _gravitonChoiceSpot.setArea(kShuttleGravitonBounds); + _gravitonChoiceSpot.setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag); + _vm->getAllHotspots().push_back(&_gravitonChoiceSpot); + _tractorChoiceSpot.setArea(kShuttleTractorBounds); + _tractorChoiceSpot.setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag); + _vm->getAllHotspots().push_back(&_tractorChoiceSpot); + _shuttleViewSpot.setArea(kShuttleWindowLeft, kShuttleWindowTop, + kShuttleWindowLeft + kShuttleWindowWidth, kShuttleWindowTop + kShuttleWindowHeight); + _shuttleViewSpot.setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag); + _vm->getAllHotspots().push_back(&_shuttleViewSpot); + _shuttleTransportSpot.setArea(kShuttleTransportBounds); + _shuttleTransportSpot.setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag); + _vm->getAllHotspots().push_back(&_shuttleTransportSpot); + + _privateFlags.setFlag(kMarsPrivateInSpaceChaseFlag, true); + + startMarsTimer(kSpaceChaseTimeLimit, kOneTickPerSecond, kMarsSpaceChaseFinished); +} + +void Mars::setSoundFXLevel(const uint16 level) { + Neighborhood::setSoundFXLevel(level); + + if (_canyonChaseMovie.isMovieValid()) + _canyonChaseMovie.setVolume(level); + + if (_explosions.isMovieValid()) + _explosions.setVolume(level); +} + +void Mars::startMarsTimer(TimeValue time, TimeScale scale, MarsTimerCode code) { + _utilityFuse.primeFuse(time, scale); + _marsEvent.mars = this; + _marsEvent.event = code; + _utilityFuse.setFunctor(new Common::Functor0Mem<void, MarsTimerEvent>(&_marsEvent, &MarsTimerEvent::fire)); + _utilityFuse.lightFuse(); +} + +void Mars::marsTimerExpired(MarsTimerEvent &event) { + Common::Rect r; + uint16 x, y; + + switch (event.event) { + case kMarsLaunchTubeReached: + _lowerLeftShuttleMovie.setTime(kShuttleLowerLeftTubeTime); + _lowerLeftShuttleMovie.redrawMovieWorld(); + startMarsTimer(kCanyonChaseFinishedTime, kMovieTicksPerSecond, kMarsCanyonChaseFinished); + break; + case kMarsCanyonChaseFinished: + GameState.setScoringEnteredLaunchTube(); + + while (_canyonChaseMovie.isRunning()) { + _vm->checkCallBacks(); + _vm->refreshDisplay(); + _vm->_system->delayMillis(10); + } + + _canyonChaseMovie.stop(); + _canyonChaseMovie.stopDisplaying(); + _canyonChaseMovie.releaseMovie(); + + _vm->_gfx->enableErase(); + + loadLoopSound1("Sounds/Mars/Space Ambient.22K.8.AIFF"); + + playSpotSoundSync(kShuttleConfiguringIn, kShuttleConfiguringOut); + playSpotSoundSync(kShuttleGeneratingIn, kShuttleGeneratingOut); + playSpotSoundSync(kShuttleBreakawayIn, kShuttleBreakawayOut); + playSpotSoundSync(kMarsAtmosphericBreakawayIn, kMarsAtmosphericBreakawayOut); + + initOneMovie(&_planetMovie, "Images/Mars/Planet.movie", kShuttlePlanetOrder, kPlanetStartLeft, kPlanetStartTop, true); + _planetMovie.setFlags(kLoopTimeBase); + + initOneMovie(&_junk, "Images/Mars/Junk.movie", kShuttleJunkOrder, kShuttleJunkLeft, kShuttleJunkTop, false); + + initOneMovie(&_explosions, "Images/Mars/Explosions.movie", kShuttleWeaponFrontOrder, 0, 0, false); + _explosionCallBack.initCallBack(&_explosions, kCallBackAtExtremes); + + _energyBeam.initShuttleWeapon(); + _gravitonCannon.initShuttleWeapon(); + + _centerShuttleMovie.setTime(kShuttleCenterWeaponsTime); + _centerShuttleMovie.redrawMovieWorld(); + + _upperLeftShuttleMovie.show(); + _upperLeftShuttleMovie.setTime(kShuttleUpperLeftDampingTime); + _upperLeftShuttleMovie.redrawMovieWorld(); + + _robotShip.initRobotShip(); + + _planetMovie.start(); + _planetMover.startMoving(&_planetMovie); + + playSpotSoundSync(kShuttleDamperDescIn, kShuttleDamperDescOut); + _upperLeftShuttleMovie.setTime(kShuttleUpperLeftGravitonTime); + _upperLeftShuttleMovie.redrawMovieWorld(); + + playSpotSoundSync(kShuttleGravitonDescIn, kShuttleGravitonDescOut); + _upperLeftShuttleMovie.setTime(kShuttleUpperLeftTractorTime); + _upperLeftShuttleMovie.redrawMovieWorld(); + + playSpotSoundSync(kShuttleTractorDescIn, kShuttleTractorDescOut); + _upperLeftShuttleMovie.setTime(kShuttleUpperLeftDimTime); + _upperLeftShuttleMovie.redrawMovieWorld(); + + _centerShuttleMovie.setTime(kShuttleCenterTargetSightedTime); + _centerShuttleMovie.redrawMovieWorld(); + playSpotSoundSync(kShuttleTargetSightedIn, kShuttleTargetSightedOut); + + _lowerRightShuttleMovie.setTime(kShuttleLowerRightTrackingTime); + _lowerRightShuttleMovie.redrawMovieWorld(); + _rightShuttleMovie.show(); + playMovieSegment(&_rightShuttleMovie, kShuttleRightIntroStart, kShuttleRightIntroStop); + + _rightDamageShuttleMovie.show(); + playMovieSegment(&_rightDamageShuttleMovie); + + // Take it down a tick initially. This sets the time to the time of the last tick, + // so that subsequence drops will drop it down a tick. + _rightDamageShuttleMovie.setTime(_rightDamageShuttleMovie.getTime() - 40); + _rightDamageShuttleMovie.redrawMovieWorld(); + + _lowerLeftShuttleMovie.setTime(kShuttleLowerLeftAutopilotTime); + _lowerLeftShuttleMovie.redrawMovieWorld(); + playSpotSoundSync(kShuttleAutopilotEngagedIn, kShuttleAutopilotEngagedOut); + + _robotShip.startMoving(); + + _shuttleHUD.initShuttleHUD(); + + _tractorBeam.startDisplaying(); + + _energyChoiceSpot.setArea(kShuttleEnergyBeamBounds); + _energyChoiceSpot.setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag); + _vm->getAllHotspots().push_back(&_energyChoiceSpot); + _gravitonChoiceSpot.setArea(kShuttleGravitonBounds); + _gravitonChoiceSpot.setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag); + _vm->getAllHotspots().push_back(&_gravitonChoiceSpot); + _tractorChoiceSpot.setArea(kShuttleTractorBounds); + _tractorChoiceSpot.setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag); + _vm->getAllHotspots().push_back(&_tractorChoiceSpot); + _shuttleViewSpot.setArea(kShuttleWindowLeft, kShuttleWindowTop, + kShuttleWindowLeft + kShuttleWindowWidth, kShuttleWindowTop + kShuttleWindowHeight); + _shuttleViewSpot.setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag); + _vm->getAllHotspots().push_back(&_shuttleViewSpot); + _shuttleTransportSpot.setArea(kShuttleTransportBounds); + _shuttleTransportSpot.setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag); + _vm->getAllHotspots().push_back(&_shuttleTransportSpot); + + _privateFlags.setFlag(kMarsPrivateInSpaceChaseFlag, true); + + playSpotSoundSync(kMarsCockpitChatterIn, kMarsCockpitChatterOut); + + GameState.setMarsFinishedCanyonChase(true); + + startMarsTimer(kSpaceChaseTimeLimit, kOneTickPerSecond, kMarsSpaceChaseFinished); + + _vm->_cursor->hideUntilMoved(); + break; + case kMarsSpaceChaseFinished: + // Player failed to stop the robot in time... + _interruptionFilter = kFilterNoInput; + + _rightShuttleMovie.setTime(kShuttleRightTargetLockTime); + _rightShuttleMovie.redrawMovieWorld(); + + _upperRightShuttleMovie.show(); + _upperRightShuttleMovie.setTime(kShuttleUpperRightLockedTime); + _upperRightShuttleMovie.redrawMovieWorld(); + + _rightShuttleMovie.setTime(kShuttleRightGravitonTime); + _rightShuttleMovie.redrawMovieWorld(); + _upperRightShuttleMovie.setTime(kShuttleUpperRightArmedTime); + _upperRightShuttleMovie.redrawMovieWorld(); + + _vm->delayShell(3, 1); + + x = _vm->getRandomNumber(19); + y = _vm->getRandomNumber(19); + + r = Common::Rect(kShuttleWindowMidH - x, kShuttleWindowMidV - y, + kShuttleWindowMidH - x + 20, kShuttleWindowMidV - y + 20); + showBigExplosion(r, kShuttleAlienShipOrder); + + while (_explosions.isRunning()) { + _vm->checkCallBacks(); + _vm->refreshDisplay(); + g_system->delayMillis(10); + } + + throwAwayMarsShuttle(); + reinstateMonocleInterface(); + recallToTSAFailure(); + break; + default: + break; + } + + _interruptionFilter = kFilterAllInput; +} + +void Mars::throwAwayMarsShuttle() { + _shuttleInterface1.deallocateSurface(); + _shuttleInterface1.stopDisplaying(); + _shuttleInterface2.deallocateSurface(); + _shuttleInterface2.stopDisplaying(); + _shuttleInterface3.deallocateSurface(); + _shuttleInterface3.stopDisplaying(); + _shuttleInterface4.deallocateSurface(); + _shuttleInterface4.stopDisplaying(); + + _spotSounds.disposeSound(); + + _canyonChaseMovie.releaseMovie(); + _canyonChaseMovie.stopDisplaying(); + _leftShuttleMovie.releaseMovie(); + _leftShuttleMovie.stopDisplaying(); + _rightShuttleMovie.releaseMovie(); + _rightShuttleMovie.stopDisplaying(); + _lowerLeftShuttleMovie.releaseMovie(); + _lowerLeftShuttleMovie.stopDisplaying(); + _lowerRightShuttleMovie.releaseMovie(); + _lowerRightShuttleMovie.stopDisplaying(); + _centerShuttleMovie.releaseMovie(); + _centerShuttleMovie.stopDisplaying(); + _upperLeftShuttleMovie.releaseMovie(); + _upperLeftShuttleMovie.stopDisplaying(); + _upperRightShuttleMovie.releaseMovie(); + _upperRightShuttleMovie.stopDisplaying(); + _leftDamageShuttleMovie.releaseMovie(); + _leftDamageShuttleMovie.stopDisplaying(); + _rightDamageShuttleMovie.releaseMovie(); + _rightDamageShuttleMovie.stopDisplaying(); + + _shuttleEnergyMeter.disposeShuttleEnergyMeter(); + _robotShip.cleanUpRobotShip(); + _shuttleHUD.cleanUpShuttleHUD(); + _tractorBeam.stopDisplaying(); + _junk.releaseMovie(); + _junk.stopDisplaying(); + _energyBeam.cleanUpShuttleWeapon(); + _gravitonCannon.cleanUpShuttleWeapon(); + _vm->getAllHotspots().remove(&_energyChoiceSpot); + _vm->getAllHotspots().remove(&_gravitonChoiceSpot); + _vm->getAllHotspots().remove(&_tractorChoiceSpot); + _vm->getAllHotspots().remove(&_shuttleViewSpot); + _vm->getAllHotspots().remove(&_shuttleTransportSpot); + _explosions.releaseMovie(); + _explosions.stopDisplaying(); + + loadLoopSound1(""); +} + +void Mars::transportToRobotShip() { + throwAwayMarsShuttle(); + + Video::VideoDecoder *video = new Video::QuickTimeDecoder(); + if (!video->loadFile("Images/Mars/M98EAE.movie")) + error("Could not load shuttle->interface transition video"); + + video->start(); + + while (!_vm->shouldQuit() && !video->endOfVideo()) { + if (video->needsUpdate()) { + const Graphics::Surface *frame = video->decodeNextFrame(); + + if (frame) + _vm->drawScaledFrame(frame, 0, 0); + } + + Common::Event event; + while (g_system->getEventManager()->pollEvent(event)) + ; + + g_system->delayMillis(10); + } + + delete video; + + if (_vm->shouldQuit()) + return; + + reinstateMonocleInterface(); + + g_energyMonitor->stopEnergyDraining(); + g_energyMonitor->restoreLastEnergyValue(); + _vm->resetEnergyDeathReason(); + g_energyMonitor->startEnergyDraining(); + + arriveAt(kMarsRobotShuttle, kEast); + + _navMovie.stop(); + _navMovie.setTime(_navMovie.getStart()); + _navMovie.start(); +} + +const int kRobotTooStrong = 1; +const int kTractorTooWeak = 2; +const int kCapturedRobotShip = 3; + +void Mars::spaceChaseClick(const Input &input, const HotSpotID id) { + Common::Point pt; + + switch (id) { + case kShuttleEnergySpotID: + _upperLeftShuttleMovie.setTime(kShuttleUpperLeftDampingTime); + _upperLeftShuttleMovie.redrawMovieWorld(); + _leftShuttleMovie.setTime(kShuttleLeftDampingTime); + _leftShuttleMovie.redrawMovieWorld(); + _shuttleHUD.hide(); + _weaponSelection = kEnergyBeam; + playSpotSoundSync(kShuttleDampingBeamIn, kShuttleDampingBeamOut); + break; + case kShuttleGravitonSpotID: + _upperLeftShuttleMovie.setTime(kShuttleUpperLeftGravitonTime); + _upperLeftShuttleMovie.redrawMovieWorld(); + _leftShuttleMovie.setTime(kShuttleLeftGravitonTime); + _leftShuttleMovie.redrawMovieWorld(); + _shuttleHUD.hide(); + _weaponSelection = kGravitonCannon; + playSpotSoundSync(kShuttleGravitonIn, kShuttleGravitonOut); + break; + case kShuttleTractorSpotID: + _upperLeftShuttleMovie.setTime(kShuttleUpperLeftTractorTime); + _upperLeftShuttleMovie.redrawMovieWorld(); + _leftShuttleMovie.setTime(kShuttleLeftTractorTime); + _leftShuttleMovie.redrawMovieWorld(); + _shuttleHUD.show(); + _weaponSelection = kTractorBeam; + playSpotSoundSync(kShuttleTractorBeamIn, kShuttleTractorBeamOut); + break; + case kShuttleViewSpotID: + switch (_weaponSelection) { + case kEnergyBeam: + if (_shuttleEnergyMeter.getEnergyValue() < kMinDampingEnergy) { + playSpotSoundSync(kShuttleEnergyTooLowIn, kShuttleEnergyTooLowOut); + } else { + if (_energyBeam.canFireWeapon()) { + _shuttleEnergyMeter.dropEnergyValue(kMinDampingEnergy); + input.getInputLocation(pt); + _energyBeam.fireWeapon(pt.x, pt.y); + playSpotSoundSync(kMarsEDBBlastIn, kMarsEDBBlastOut); + } + } + break; + case kGravitonCannon: + if (_shuttleEnergyMeter.getEnergyValue() < kMinGravitonEnergy) { + playSpotSoundSync(kShuttleEnergyTooLowIn, kShuttleEnergyTooLowOut); + } else { + if (_gravitonCannon.canFireWeapon()) { + _shuttleEnergyMeter.dropEnergyValue(kMinGravitonEnergy); + input.getInputLocation(pt); + _gravitonCannon.fireWeapon(pt.x, pt.y); + playSpotSoundSync(kMarsGravitonBlastIn, kMarsGravitonBlastOut); + } + } + break; + case kTractorBeam: + if (_shuttleHUD.isTargetLocked()) { + // play tractor beam sound? + _utilityFuse.stopFuse(); + + _tractorBeam.show(); + + int capture; + if (_rightDamageShuttleMovie.getTime() > 40) { + capture = kRobotTooStrong; + } else if (!_shuttleEnergyMeter.enoughEnergyForTractorBeam()) { + capture = kTractorTooWeak; + } else { + _robotShip.snareByTractorBeam(); + capture = kCapturedRobotShip; + _planetMover.dropPlanetOutOfSight(); + } + + _shuttleEnergyMeter.drainForTractorBeam(); + + while (_shuttleEnergyMeter.isFading()) { + _vm->checkCallBacks(); + _vm->refreshDisplay(); + _vm->_system->delayMillis(10); + } + + _shuttleEnergyMeter.setEnergyValue(_shuttleEnergyMeter.getEnergyValue()); + + switch (capture) { + case kRobotTooStrong: + _tractorBeam.hide(); + playSpotSoundSync(kShuttleBrokeFreeIn, kShuttleBrokeFreeOut); + _utilityFuse.lightFuse(); + break; + case kTractorTooWeak: + playSpotSoundSync(kShuttleCantHoldIn, kShuttleCantHoldOut); + _tractorBeam.hide(); + _utilityFuse.lightFuse(); + break; + case kCapturedRobotShip: + _tractorBeam.hide(); + _shuttleHUD.hide(); + _robotShip.cleanUpRobotShip(); + _planetMovie.stop(); + _planetMovie.stopDisplaying(); + _planetMovie.releaseMovie(); + + // Shameless reuse of a variable :P + initOneMovie(&_canyonChaseMovie, "Images/Mars/M98EAS.movie", kShuttleTractorBeamMovieOrder, + kShuttleWindowLeft, kShuttleWindowTop, true); + _canyonChaseMovie.redrawMovieWorld(); + playMovieSegment(&_canyonChaseMovie, 0, _canyonChaseMovie.getDuration()); + + // wait here until any junk clears... + while (_junk.junkFlying()) { + _vm->checkCallBacks(); + _vm->refreshDisplay(); + _vm->_system->delayMillis(10); + } + + _upperRightShuttleMovie.show(); + _upperRightShuttleMovie.setTime(kShuttleUpperRightOverloadTime); + _upperRightShuttleMovie.redrawMovieWorld(); + + playSpotSoundSync(kShuttleOverloadedIn, kShuttleOverloadedOut); + _centerShuttleMovie.setTime(kShuttleCenterVerifyingTime); + _centerShuttleMovie.redrawMovieWorld(); + + playSpotSoundSync(kShuttleCoordinatesIn, kShuttleCoordinatesOut); + _centerShuttleMovie.setTime(kShuttleCenterScanningTime); + _centerShuttleMovie.redrawMovieWorld(); + + playSpotSoundSync(kShuttleScanningIn, kShuttleScanningOut); + _centerShuttleMovie.setTime(kShuttleCenterSafeTime); + _centerShuttleMovie.redrawMovieWorld(); + + playSpotSoundSync(kShuttleSafeIn, kShuttleSafeOut); + _lowerRightShuttleMovie.setTime(kShuttleLowerRightTransportTime); + _lowerRightShuttleMovie.redrawMovieWorld(); + GameState.setMarsReadyForShuttleTransport(true); + break; + } + } else { + playSpotSoundSync(kShuttleTractorLimitedIn, kShuttleTractorLimitedOut); + } + break; + default: + break; + } + break; + case kShuttleTransportSpotID: + _lowerRightShuttleMovie.setTime(kShuttleLowerRightTransportHiliteTime); + _lowerRightShuttleMovie.redrawMovieWorld(); + _neighborhoodNotification.setNotificationFlags(kTimeToTransportFlag, kTimeToTransportFlag); + break; + } +} + +void Mars::showBigExplosion(const Common::Rect &r, const DisplayOrder order) { + if (_explosions.isMovieValid()) { + _explosions.setDisplayOrder(order); + + Common::Rect r2 = r; + int dx = r.width() / 2; + int dy = r.height() / 2; + r2.left -= dx; + r2.right += dx; + r2.top -= dy; + r2.bottom += dy; + + _explosions.setBounds(r2); + _explosions.show(); + _explosions.stop(); + _explosions.setSegment(kBigExplosionStart, kBigExplosionStop); + _explosions.setTime(kBigExplosionStart); + _explosionCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + _explosions.start(); + } +} + +void Mars::showLittleExplosion(const Common::Rect &r, const DisplayOrder order) { + if (_explosions.isMovieValid()) { + _explosions.setDisplayOrder(order); + + Common::Rect r2 = r; + int dx = r.width() / 2; + int dy = r.height() / 2; + r2.left -= dx; + r2.right += dx; + r2.top -= dy; + r2.bottom += dy; + _explosions.setBounds(r2); + + _explosions.show(); + _explosions.stop(); + _explosions.setSegment(kLittleExplosionStart, kLittleExplosionStop); + _explosions.setTime(kLittleExplosionStart); + _explosionCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + _explosions.start(); + } +} + +void Mars::hitByJunk() { + _leftDamageShuttleMovie.setTime(_leftDamageShuttleMovie.getTime() - 40); + _leftDamageShuttleMovie.redrawMovieWorld(); + + playSpotSoundSync(kMarsJunkCollisionIn, kMarsJunkCollisionOut); + + if (_leftDamageShuttleMovie.getTime() == 0) { + die(kDeathRanIntoSpaceJunk); + } else { + TimeValue t = _leftDamageShuttleMovie.getTime() / 40; + + if (t == 1) + playSpotSoundSync(kShuttleHullBreachIn, kShuttleHullBreachOut); + + t = _leftShuttleMovie.getTime(); + _leftShuttleMovie.setTime(kShuttleLeftDamagedTime); + _leftShuttleMovie.redrawMovieWorld(); + _vm->delayShell(1, 3); + _leftShuttleMovie.setTime(t); + _leftShuttleMovie.redrawMovieWorld(); + } +} + +void Mars::setUpNextDropTime() { + _robotShip.setUpNextDropTime(); +} + +void Mars::decreaseRobotShuttleEnergy(const int delta, Common::Point impactPoint) { + _rightDamageShuttleMovie.setTime(_rightDamageShuttleMovie.getTime() - 40 * delta); + _rightDamageShuttleMovie.redrawMovieWorld(); + + if (_rightDamageShuttleMovie.getTime() == 0) { + Common::Rect r; + _robotShip.getShuttleBounds(r); + int size = MAX(r.width(), r.height()); + r = Common::Rect::center(impactPoint.x, impactPoint.y, size, size); + _robotShip.killRobotShip(); + showBigExplosion(r, kShuttleRobotShipOrder); + } else if (delta > 1) { + Common::Rect r; + _robotShip.getShuttleBounds(r); + int size = MIN(r.width(), r.height()); + r = Common::Rect::center(impactPoint.x, impactPoint.y, size, size); + showLittleExplosion(r, kShuttleWeaponBackOrder); + TimeValue t = _rightShuttleMovie.getTime(); + _rightShuttleMovie.setTime(kShuttleRightDamagedTime); + _rightShuttleMovie.redrawMovieWorld(); + _vm->delayShell(1, 3); + _rightShuttleMovie.setTime(t); + _rightShuttleMovie.redrawMovieWorld(); + } + + if (_rightDamageShuttleMovie.getTime() <= 40) { + GameState.setScoringStoppedRobotsShuttle(); + if (!GameState.getMarsHitRobotWithCannon()) + GameState.setScoringMarsGandhi(); + } +} + +void Mars::updateCursor(const Common::Point cursorLocation, const Hotspot *cursorSpot) { + if (cursorSpot && cursorSpot->getObjectID() == kShuttleViewSpotID) { + if (_weaponSelection != kNoWeapon) + _vm->_cursor->setCurrentFrameIndex(6); + else + _vm->_cursor->setCurrentFrameIndex(0); + } else { + Neighborhood::updateCursor(cursorLocation, cursorSpot); + } +} + +AirQuality Mars::getAirQuality(const RoomID room) { + if ((room >= kMars36 && room <= kMars39) || (room >= kMarsMaze004 && room <= kMarsMaze200)) + return kAirQualityVacuum; + if (room == kMars35 && !GameState.getMarsAirlockOpen()) + return kAirQualityVacuum; + if (room == kMars60 && !GameState.getMarsAirlockOpen()) + return kAirQualityVacuum; + + return Neighborhood::getAirQuality(room); +} + +// Start up panting sound if necessary. + +void Mars::checkAirMask() { + Neighborhood::checkAirMask(); + + if (getAirQuality(GameState.getCurrentRoom()) == kAirQualityVacuum) { + if (g_airMask->isAirMaskOn()) { + if (_noAirFuse.isFuseLit()) { + _noAirFuse.stopFuse(); + loadLoopSound2(""); + loadAmbientLoops(); + playSpotSoundSync(kMarsOxyMaskOnIn, kMarsOxyMaskOnOut); + } + } else { + if (!_noAirFuse.isFuseLit()) { + loadLoopSound2("Sounds/Mars/SukWind1.22K.AIFF"); + _noAirFuse.primeFuse(kVacuumSurvivalTimeLimit); + _noAirFuse.lightFuse(); + } + } + } else { + if (_noAirFuse.isFuseLit()) { + _noAirFuse.stopFuse(); + loadLoopSound2(""); + loadAmbientLoops(); + } + } +} + +void Mars::airStageExpired() { + if (((PegasusEngine *)g_engine)->playerHasItemID(kAirMask)) + die(kDeathNoAirInMaze); + else + die(kDeathNoMaskInMaze); +} + +void Mars::lockThawed() { + startExtraSequence(kMars57ThawLock, kExtraCompletedFlag, kFilterNoInput); +} + +void Mars::setUpReactorLevel1() { + _reactorStage = 1; + makeColorSequence(); + _guessObject.initReactorGuess(); + _undoPict.initFromPICTResource(_vm->_resFork, kReactorUndoHilitePICTID); + _undoPict.setDisplayOrder(kMonitorLayer); + _undoPict.moveElementTo(kUndoHiliteLeft, kUndoHiliteTop); + _undoPict.startDisplaying(); + _guessHistory.initReactorHistory(); + _choiceHighlight.initReactorChoiceHighlight(); + setCurrentActivation(kActivateReactorInGame); + _bombFuse.primeFuse(kColorMatchingTimeLimit); + _bombFuse.setFunctor(new Common::Functor0Mem<void, Mars>(this, &Mars::bombExplodesInGame)); + _bombFuse.lightFuse(); +} + +void Mars::setUpNextReactorLevel() { + _guessObject.show(); + _guessHistory.show(); + _guessHistory.clearHistory(); + _choiceHighlight.show(); + _reactorStage++; + makeColorSequence(); +} + +void Mars::makeColorSequence() { + int32 code[5]; + int32 highest = _reactorStage + 2; + + for (int32 i = 0; i < highest; i++) + code[i] = i; + + _vm->shuffleArray(code, highest); + _currentGuess[0] = -1; + _currentGuess[1] = -1; + _currentGuess[2] = -1; + _nextGuess = 0; + _guessObject.setGuess(-1, -1, -1); + _guessHistory.setAnswer(code[0], code[1], code[2]); +} + +void Mars::doUndoOneGuess() { + if (_nextGuess > 0) { + _undoPict.show(); + _vm->delayShell(1, 2); + _undoPict.hide(); + _nextGuess--; + _currentGuess[_nextGuess] = -1; + _guessObject.setGuess(_currentGuess[0], _currentGuess[1], _currentGuess[2]); + _choiceHighlight.resetHighlight(); + + if (_currentGuess[0] != -1) { + _choiceHighlight.highlightChoice(_currentGuess[0]); + + if (_currentGuess[1] != -1) { + _choiceHighlight.highlightChoice(_currentGuess[1]); + + if (_currentGuess[2] != -1) + _choiceHighlight.highlightChoice(_currentGuess[2]); + } + } + } +} + +void Mars::doReactorGuess(int32 guess) { + _choiceHighlight.highlightChoice(guess); + _currentGuess[_nextGuess] = guess; + _guessObject.setGuess(_currentGuess[0], _currentGuess[1], _currentGuess[2]); + + switch (guess) { + case 0: + playSpotSoundSync(kColorMatchRedIn, kColorMatchRedOut); + break; + case 1: + playSpotSoundSync(kColorMatchYellowIn, kColorMatchYellowOut); + break; + case 2: + playSpotSoundSync(kColorMatchGreenIn, kColorMatchGreenOut); + break; + case 3: + playSpotSoundSync(kColorMatchBlueIn, kColorMatchBlueOut); + break; + case 4: + playSpotSoundSync(kColorMatchPurpleIn, kColorMatchPurpleOut); + break; + } + + _nextGuess++; + + if (_nextGuess == 3) { + _vm->delayShell(1, 2); + _nextGuess = 0; + _guessHistory.addGuess(_currentGuess[0], _currentGuess[1], _currentGuess[2]); + + switch (_guessHistory.getCurrentNumCorrect()) { + case 0: + playSpotSoundSync(kColorMatchZeroNodesIn, kColorMatchZeroNodesOut); + break; + case 1: + playSpotSoundSync(kColorMatchOneNodeIn, kColorMatchOneNodeOut); + break; + case 2: + playSpotSoundSync(kColorMatchTwoNodesIn, kColorMatchTwoNodesOut); + break; + case 3: + playSpotSoundSync(kColorMatchThreeNodesIn, kColorMatchThreeNodesOut); + break; + } + + _currentGuess[0] = -1; + _currentGuess[1] = -1; + _currentGuess[2] = -1; + _guessObject.setGuess(-1, -1, -1); + _choiceHighlight.resetHighlight(); + + if (_guessHistory.isSolved()) { + _guessHistory.showAnswer(); + _vm->delayShell(1, 2); + _guessObject.hide(); + _guessHistory.hide(); + _choiceHighlight.hide(); + + switch (_reactorStage) { + case 1: + startExtraSequence(kMars57GameLevel2, kExtraCompletedFlag, kFilterNoInput); + break; + case 2: + startExtraSequence(kMars57GameLevel3, kExtraCompletedFlag, kFilterNoInput); + break; + case 3: + _bombFuse.stopFuse(); + _guessObject.disposeReactorGuess(); + _undoPict.deallocateSurface(); + _guessHistory.disposeReactorHistory(); + _choiceHighlight.disposeReactorChoiceHighlight(); + GameState.setScoringDisarmedCardBomb(); + startExtraSequence(kMars57GameSolved, kExtraCompletedFlag, kFilterNoInput); + break; + } + } else if (_guessHistory.getNumGuesses() >= 5) { + _vm->delayShell(2, 1); + bombExplodesInGame(); + } + } +} + +void Mars::bombExplodesInGame() { + _guessObject.disposeReactorGuess(); + _undoPict.deallocateSurface(); + _guessHistory.disposeReactorHistory(); + _choiceHighlight.disposeReactorChoiceHighlight(); + startExtraSequence(kMars57BombExplodesInGame, kExtraCompletedFlag, kFilterNoInput); +} + +void Mars::didntFindBomb() { + die(kDeathDidntFindMarsBomb); +} + +Common::String Mars::getBriefingMovie() { + Common::String movieName = Neighborhood::getBriefingMovie(); + + if (!movieName.empty()) + return movieName; + + return "Images/AI/Mars/XM01"; +} + +Common::String Mars::getEnvScanMovie() { + Common::String movieName = Neighborhood::getEnvScanMovie(); + + if (movieName.empty()) { + RoomID room = GameState.getCurrentRoom(); + + if (room >= kMars0A && room <= kMars21) + return "Images/AI/Mars/XME1"; + else if (room >= kMars22 && room <= kMars31South) + return "Images/AI/Mars/XME2"; + else if (room >= kMars52 && room <= kMars58) + return "Images/AI/Mars/XMREACE"; + + return "Images/AI/Mars/XME3"; + } + + return movieName; +} + +uint Mars::getNumHints() { + uint numHints = Neighborhood::getNumHints(); + + if (numHints == 0) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kMars27, kNorth): + case MakeRoomView(kMars28, kNorth): + case MakeRoomView(kMars49, kSouth): + numHints = 1; + break; + case MakeRoomView(kMars31, kSouth): + case MakeRoomView(kMars31South, kSouth): + if (!GameState.isTakenItemID(kMarsCard)) + numHints = 1; + break; + case MakeRoomView(kMars34, kNorth): + if (!GameState.isTakenItemID(kMarsCard)) + numHints = 2; + break; + case MakeRoomView(kMars34, kSouth): + case MakeRoomView(kMars45, kNorth): + if (!GameState.isTakenItemID(kCrowbar)) + numHints = 1; + break; + case MakeRoomView(kMars51, kEast): + if (GameState.isCurrentDoorOpen() && !GameState.getShieldOn()) { + if (GameState.isTakenItemID(kShieldBiochip)) + numHints = 1; + else + numHints = 2; + } + break; + case MakeRoomView(kMars52, kNorth): + case MakeRoomView(kMars52, kSouth): + case MakeRoomView(kMars52, kEast): + case MakeRoomView(kMars52, kWest): + case MakeRoomView(kMars54, kNorth): + case MakeRoomView(kMars54, kSouth): + case MakeRoomView(kMars54, kEast): + case MakeRoomView(kMars54, kWest): + case MakeRoomView(kMars56, kNorth): + case MakeRoomView(kMars56, kSouth): + case MakeRoomView(kMars56, kWest): + case MakeRoomView(kMars58, kNorth): + case MakeRoomView(kMars58, kSouth): + case MakeRoomView(kMars58, kEast): + case MakeRoomView(kMars58, kWest): + if (!GameState.getShieldOn()) { + if (GameState.isTakenItemID(kShieldBiochip)) + numHints = 1; + else + numHints = 2; + } + break; + case MakeRoomView(kMars56, kEast): + if (getCurrentActivation() == kActivateReactorReadyForNitrogen) { + if ((ExtraID)_lastExtra == kMars57LowerScreenClosed) + numHints = 3; + } else if (getCurrentActivation() == kActivateReactorPlatformOut) { + if (!GameState.getShieldOn()) { + if (GameState.isTakenItemID(kShieldBiochip)) + numHints = 1; + else + numHints = 2; + } + } + break; + } + } + + return numHints; +} + +Common::String Mars::getHintMovie(uint hintNum) { + Common::String movieName = Neighborhood::getHintMovie(hintNum); + + if (movieName.empty()) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kMars27, kNorth): + case MakeRoomView(kMars28, kNorth): + return "Images/AI/Globals/XGLOB5C"; + case MakeRoomView(kMars31, kSouth): + case MakeRoomView(kMars31South, kSouth): + case MakeRoomView(kMars34, kSouth): + case MakeRoomView(kMars45, kNorth): + return "Images/AI/Globals/XGLOB1C"; + case MakeRoomView(kMars34, kNorth): + if (hintNum == 1) + return "Images/AI/Globals/XGLOB2C"; + + return "Images/AI/Globals/XGLOB3G"; + case MakeRoomView(kMars49, kSouth): + if (GameState.isTakenItemID(kAirMask)) + return "Images/AI/Globals/XGLOB3E"; + + return "Images/AI/Globals/XGLOB1C"; + case MakeRoomView(kMars51, kEast): + if (GameState.isTakenItemID(kShieldBiochip)) + return "Images/AI/Mars/XM52NW"; + + if (hintNum == 1) + return "Images/AI/Globals/XGLOB2D"; + + return "Images/AI/Globals/XGLOB3F"; + case MakeRoomView(kMars52, kNorth): + case MakeRoomView(kMars52, kSouth): + case MakeRoomView(kMars52, kEast): + case MakeRoomView(kMars52, kWest): + case MakeRoomView(kMars54, kNorth): + case MakeRoomView(kMars54, kSouth): + case MakeRoomView(kMars54, kEast): + case MakeRoomView(kMars54, kWest): + case MakeRoomView(kMars56, kNorth): + case MakeRoomView(kMars56, kSouth): + case MakeRoomView(kMars56, kWest): + case MakeRoomView(kMars58, kNorth): + case MakeRoomView(kMars58, kSouth): + case MakeRoomView(kMars58, kEast): + case MakeRoomView(kMars58, kWest): + if (hintNum == 1) { + if (GameState.isTakenItemID(kShieldBiochip)) + return "Images/AI/Mars/XM52NW"; + + return "Images/AI/Globals/XGLOB2D"; + } + + return "Images/AI/Globals/XGLOB3F"; + case MakeRoomView(kMars56, kEast): + if (getCurrentActivation() == kActivateReactorReadyForNitrogen) + return Common::String::format("Images/AI/Mars/XM57SD%d", hintNum); + + if (hintNum == 1) { + if (GameState.isTakenItemID(kShieldBiochip)) + return "Images/AI/Mars/XM52NW"; + + return "Images/AI/Globals/XGLOB2D"; + } + + return "Images/AI/Globals/XGLOB3F"; + } + } + + return movieName; +} + +bool Mars::inColorMatchingGame() { + return _guessObject.isDisplaying(); +} + +bool Mars::canSolve() { + return GameState.getCurrentRoomAndView() == MakeRoomView(kMars56, kEast) && (getCurrentActivation() == kActivateReactorReadyForNitrogen || + getCurrentActivation() == kActivateReactorReadyForCrowBar || inColorMatchingGame()); +} + +void Mars::doSolve() { + if (getCurrentActivation() == kActivateReactorReadyForNitrogen || getCurrentActivation() == kActivateReactorReadyForCrowBar) { + _utilityFuse.stopFuse(); + GameState.setMarsLockBroken(true); + GameState.setMarsLockFrozen(false); + startExtraLongSequence(kMars57OpenPanel, kMars57OpenPanelChoices, kExtraCompletedFlag, kFilterNoInput); + } else if (inColorMatchingGame()) { + _bombFuse.stopFuse(); + _guessObject.disposeReactorGuess(); + _undoPict.deallocateSurface(); + _guessHistory.disposeReactorHistory(); + _choiceHighlight.disposeReactorChoiceHighlight(); + startExtraSequence(kMars57GameSolved, kExtraCompletedFlag, kFilterNoInput); + } +} + +Common::String Mars::getSoundSpotsName() { + return "Sounds/Mars/Mars Spots"; +} + +Common::String Mars::getNavMovieName() { + return "Images/Mars/Mars.movie"; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/mars/mars.h b/engines/pegasus/neighborhood/mars/mars.h new file mode 100644 index 0000000000..0859522890 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/mars.h @@ -0,0 +1,238 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_NEIGHBORHOOD_MARS_MARS_H +#define PEGASUS_NEIGHBORHOOD_MARS_MARS_H + +#include "pegasus/neighborhood/neighborhood.h" +#include "pegasus/neighborhood/mars/constants.h" +#include "pegasus/neighborhood/mars/energybeam.h" +#include "pegasus/neighborhood/mars/gravitoncannon.h" +#include "pegasus/neighborhood/mars/planetmover.h" +#include "pegasus/neighborhood/mars/reactor.h" +#include "pegasus/neighborhood/mars/robotship.h" +#include "pegasus/neighborhood/mars/shuttleenergymeter.h" +#include "pegasus/neighborhood/mars/shuttlehud.h" +#include "pegasus/neighborhood/mars/spacejunk.h" +#include "pegasus/neighborhood/mars/tractorbeam.h" + +namespace Pegasus { + +class InventoryItem; +class Mars; + +enum MarsTimerCode { + kMarsLaunchTubeReached, + kMarsCanyonChaseFinished, + kMarsSpaceChaseFinished // Player ran out of time... +}; + +struct MarsTimerEvent { + Mars *mars; + MarsTimerCode event; + + void fire(); +}; + +enum ShuttleWeaponSelection { + kNoWeapon, + kEnergyBeam, + kGravitonCannon, + kTractorBeam +}; + +class Mars : public Neighborhood { +friend struct MarsTimerEvent; +public: + Mars(InputHandler *, PegasusEngine *); + virtual ~Mars(); + + void flushGameState(); + + virtual uint16 getDateResID() const; + + virtual AirQuality getAirQuality(const RoomID); + + void checkAirMask(); + + void showBigExplosion(const Common::Rect &, const DisplayOrder); + void showLittleExplosion(const Common::Rect &, const DisplayOrder); + void hitByJunk(); + void decreaseRobotShuttleEnergy(const int, Common::Point impactPoint); + void setUpNextDropTime(); + + Common::String getBriefingMovie(); + Common::String getEnvScanMovie(); + uint getNumHints(); + Common::String getHintMovie(uint); + + virtual void shieldOn(); + virtual void shieldOff(); + + void checkContinuePoint(const RoomID, const DirectionConstant); + + void setSoundFXLevel(const uint16); + + bool canSolve(); + void doSolve(); + + bool inColorMatchingGame(); + +protected: + enum { + kMarsPrivatePodStorageOpenFlag, + kMarsPrivatePodTurnLeftFlag, + kMarsPrivatePodTurnRightFlag, + kMarsPrivateRobotTiredOfWaitingFlag, + kMarsPrivatePlatformZoomedInFlag, + kMarsPrivateBombExposedFlag, + kMarsPrivateDraggingBombFlag, + kMarsPrivateInSpaceChaseFlag, + kMarsPrivateGotMapChipFlag, + kMarsPrivateGotOpticalChipFlag, + kMarsPrivateGotShieldChipFlag, + kNumMarsPrivateFlags + }; + + void init(); + void start(); + void setUpAIRules(); + void arriveAt(const RoomID, const DirectionConstant); + void takeItemFromRoom(Item *); + void dropItemIntoRoom(Item *, Hotspot *); + void activateHotspots(); + void activateOneHotspot(HotspotInfoTable::Entry &, Hotspot *); + void clickInHotspot(const Input &, const Hotspot *); + InputBits getInputFilter(); + + TimeValue getViewTime(const RoomID, const DirectionConstant); + void getZoomEntry(const HotSpotID, ZoomTable::Entry &); + void findSpotEntry(const RoomID, const DirectionConstant, SpotFlags, SpotTable::Entry &); + CanOpenDoorReason canOpenDoor(DoorTable::Entry &); + void openDoor(); + void closeDoorOffScreen(const RoomID, const DirectionConstant); + int16 getStaticCompassAngle(const RoomID, const DirectionConstant); + void getExitCompassMove(const ExitTable::Entry &, FaderMoveSpec &); + void getExtraCompassMove(const ExtraTable::Entry &, FaderMoveSpec &); + void turnTo(const DirectionConstant); + void receiveNotification(Notification *, const NotificationFlags); + void doorOpened(); + void setUpReactorEnergyDrain(); + Hotspot *getItemScreenSpot(Item *, DisplayElement *); + void lockThawed(); + void robotTiredOfWaiting(); + + void setUpReactorLevel1(); + void setUpNextReactorLevel(); + void makeColorSequence(); + void doUndoOneGuess(); + void doReactorGuess(int32 guess); + void bombExplodesInGame(); + void didntFindBomb(); + CanMoveForwardReason canMoveForward(ExitTable::Entry &); + void cantMoveThatWay(CanMoveForwardReason); + void moveForward(); + void bumpIntoWall(); + void turnLeft(); + void turnRight(); + void airStageExpired(); + void loadAmbientLoops(); + void checkAirlockDoors(); + void pickedUpItem(Item *item); + void cantOpenDoor(CanOpenDoorReason); + void launchMaze007Robot(); + void launchMaze015Robot(); + void launchMaze101Robot(); + void launchMaze104Robot(); + void launchMaze133Robot(); + void launchMaze136Robot(); + void launchMaze184Robot(); + void timerExpired(const uint32); + void spotCompleted(); + + void doCanyonChase(void); + void startMarsTimer(TimeValue, TimeScale, MarsTimerCode); + void marsTimerExpired(MarsTimerEvent &); + void throwAwayMarsShuttle(); + void startUpFromFinishedSpaceChase(); + void startUpFromSpaceChase(); + void transportToRobotShip(); + void spaceChaseClick(const Input &, const HotSpotID); + void updateCursor(const Common::Point, const Hotspot *); + + Common::String getSoundSpotsName(); + Common::String getNavMovieName(); + + InventoryItem *_attackingItem; + FuseFunction _bombFuse; + FuseFunction _noAirFuse; + FuseFunction _utilityFuse; + FlagsArray<byte, kNumMarsPrivateFlags> _privateFlags; + uint _reactorStage, _nextGuess; + int32 _currentGuess[3]; + ReactorGuess _guessObject; + Picture _undoPict; + ReactorHistory _guessHistory; + ReactorChoiceHighlight _choiceHighlight; + + Picture _shuttleInterface1; + Picture _shuttleInterface2; + Picture _shuttleInterface3; + Picture _shuttleInterface4; + Movie _canyonChaseMovie; + + MarsTimerEvent _marsEvent; + + Movie _leftShuttleMovie; + Movie _rightShuttleMovie; + Movie _lowerLeftShuttleMovie; + Movie _lowerRightShuttleMovie; + Movie _centerShuttleMovie; + Movie _upperLeftShuttleMovie; + Movie _upperRightShuttleMovie; + Movie _leftDamageShuttleMovie; + Movie _rightDamageShuttleMovie; + ShuttleEnergyMeter _shuttleEnergyMeter; + Movie _planetMovie; + PlanetMover _planetMover; + RobotShip _robotShip; + ShuttleHUD _shuttleHUD; + TractorBeam _tractorBeam; + SpaceJunk _junk; + EnergyBeam _energyBeam; + GravitonCannon _gravitonCannon; + Hotspot _energyChoiceSpot; + Hotspot _gravitonChoiceSpot; + Hotspot _tractorChoiceSpot; + Hotspot _shuttleViewSpot; + Hotspot _shuttleTransportSpot; + ShuttleWeaponSelection _weaponSelection; + ScalingMovie _explosions; + NotificationCallBack _explosionCallBack; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/mars/planetmover.cpp b/engines/pegasus/neighborhood/mars/planetmover.cpp new file mode 100644 index 0000000000..a340120c12 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/planetmover.cpp @@ -0,0 +1,104 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/movie.h" +#include "pegasus/pegasus.h" +#include "pegasus/neighborhood/mars/constants.h" +#include "pegasus/neighborhood/mars/hermite.h" +#include "pegasus/neighborhood/mars/planetmover.h" +#include "pegasus/neighborhood/mars/shuttleenergymeter.h" + +namespace Pegasus { + +static const TimeScale kRovingScale = kTractorBeamScale; +static const TimeValue kRovingTime = kTenSeconds * kRovingScale; +static const TimeValue kRovingSlop = kTwoSeconds * kRovingScale; + +static const CoordType kMaxVelocity = 20; + +PlanetMover::PlanetMover() { + setScale(kRovingScale); + _dropping = false; + _planetMovie = 0; +} + +void PlanetMover::startMoving(Movie *planetMovie) { + _planetMovie = planetMovie; + _p4 = kPlanetStartTop; + _r4 = ((PegasusEngine *)g_engine)->getRandomNumber(kMaxVelocity - 1); + if (_r4 + _p4 < kPlanetStopTop) + _r4 = kPlanetStopTop - _p4; + newDestination(); +} + +void PlanetMover::stopMoving() { + stop(); +} + +void PlanetMover::dropPlanetOutOfSight() { + stop(); + CoordType currentLoc = hermite(_p1, _p4, _r1, _r4, _lastTime, _duration); + CoordType currentV = dHermite(_p1, _p4, _r1, _r4, _lastTime, _duration); + _p1 = currentLoc; + _r1 = currentV; + _p4 = kPlanetStartTop; + _r4 = 0; + _duration = kTractorBeamTime - kTractorBeamScale; + _dropping = true; + setSegment(0, _duration); + setTime(0); + start(); +} + +void PlanetMover::newDestination() { + _p1 = _p4; + _r1 = _r4; + + _p4 = kPlanetStopTop + ((PegasusEngine *)g_engine)->getRandomNumber(kPlanetStartTop - kPlanetStopTop - 1); + _r4 = ((PegasusEngine *)g_engine)->getRandomNumber(kMaxVelocity - 1); + + if (_r4 + _p4 < kPlanetStopTop) + _r4 = kPlanetStopTop - _p4; + + stop(); + _duration = kRovingTime + ((PegasusEngine *)g_engine)->getRandomNumber(kRovingSlop - 1); + setSegment(0, _duration); + setTime(0); + start(); +} + +void PlanetMover::timeChanged(const TimeValue) { + if (_planetMovie) { + _planetMovie->moveElementTo(kPlanetStartLeft, hermite(_p1, _p4, _r1, _r4, _lastTime, _duration)); + if (_lastTime == _duration) { + if (_dropping) + stop(); + else + newDestination(); + } + } +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/mars/planetmover.h b/engines/pegasus/neighborhood/mars/planetmover.h new file mode 100644 index 0000000000..2c195387e8 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/planetmover.h @@ -0,0 +1,57 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_NEIGHBORHOOD_MARS_PLANETMOVER_H +#define PEGASUS_NEIGHBORHOOD_MARS_PLANETMOVER_H + +#include "pegasus/timers.h" + +namespace Pegasus { + +class Movie; + +class PlanetMover : IdlerTimeBase { +public: + PlanetMover(); + virtual ~PlanetMover() {} + + void startMoving(Movie *); + void stopMoving(); + + void dropPlanetOutOfSight(); + +protected: + void newDestination(); + virtual void timeChanged(const TimeValue); + + Movie *_planetMovie; + CoordType _p1, _p4, _r1, _r4; + TimeValue _duration; + bool _dropping; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/mars/reactor.cpp b/engines/pegasus/neighborhood/mars/reactor.cpp new file mode 100644 index 0000000000..3a7ef9d7eb --- /dev/null +++ b/engines/pegasus/neighborhood/mars/reactor.cpp @@ -0,0 +1,297 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 + * aint32 with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/pegasus.h" +#include "pegasus/neighborhood/mars/reactor.h" + +namespace Pegasus { + +static const CoordType kCurrentGuessWidth = 121; +static const CoordType kCurrentGuessHeight = 23; + +static const CoordType kOneGuessWidth = 25; +static const CoordType kOneGuessHeight = 23; + +static const ResIDType kReactorChoicesPICTID = 905; + +static const CoordType kCurrentGuessLeft = kNavAreaLeft + 146; +static const CoordType kCurrentGuessTop = kNavAreaTop + 90; + +ReactorGuess::ReactorGuess(const DisplayElementID id) : DisplayElement(id) { + setBounds(kCurrentGuessLeft, kCurrentGuessTop, kCurrentGuessLeft + kCurrentGuessWidth, + kCurrentGuessTop + kCurrentGuessHeight); + setDisplayOrder(kMonitorLayer); + _currentGuess[0] = -1; + _currentGuess[1] = -1; + _currentGuess[2] = -1; +} + +void ReactorGuess::initReactorGuess() { + _colors.getImageFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kReactorChoicesPICTID); + startDisplaying(); + show(); +} + +void ReactorGuess::disposeReactorGuess() { + stopDisplaying(); + _colors.deallocateSurface(); +} + +void ReactorGuess::setGuess(int32 a, int32 b, int32 c) { + _currentGuess[0] = a; + _currentGuess[1] = b; + _currentGuess[2] = c; + triggerRedraw(); +} + +void ReactorGuess::draw(const Common::Rect &) { + if (_colors.isSurfaceValid()) { + Common::Rect r1(0, 0, kOneGuessWidth, kOneGuessHeight); + Common::Rect r2 = r1; + + for (int i = 0; i < 3; i++) { + if (_currentGuess[i] >= 0) { + r1.moveTo(kOneGuessWidth * _currentGuess[i], 0); + r2.moveTo(kCurrentGuessLeft + 48 * i, kCurrentGuessTop); + _colors.copyToCurrentPortTransparent(r1, r2); + } + } + } +} + +static const CoordType kReactorChoiceHiliteWidth = 166; +static const CoordType kReactorChoiceHiliteHeight = 26; + +static const CoordType kChoiceHiliteLefts[6] = { + 0, + 34, + 34 + 34, + 34 + 34 + 32, + 34 + 34 + 32 + 34, + 34 + 34 + 32 + 34 + 32 +}; + +static const ResIDType kReactorChoiceHilitePICTID = 901; + +static const CoordType kReactorChoiceHiliteLeft = kNavAreaLeft + 116; +static const CoordType kReactorChoiceHiliteTop = kNavAreaTop + 158; + +ReactorChoiceHighlight::ReactorChoiceHighlight(const DisplayElementID id) : DisplayElement(id) { + setBounds(kReactorChoiceHiliteLeft, kReactorChoiceHiliteTop, kReactorChoiceHiliteLeft + kReactorChoiceHiliteWidth, + kReactorChoiceHiliteTop + kReactorChoiceHiliteHeight); + setDisplayOrder(kMonitorLayer); +} + +void ReactorChoiceHighlight::initReactorChoiceHighlight() { + _colors.getImageFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kReactorChoiceHilitePICTID); + startDisplaying(); + show(); +} + +void ReactorChoiceHighlight::disposeReactorChoiceHighlight() { + stopDisplaying(); + _colors.deallocateSurface(); +} + +void ReactorChoiceHighlight::draw(const Common::Rect &) { + if (_colors.isSurfaceValid()) { + for (int i = 0; i < 5; ++i) { + if (_choices.getFlag(i)) { + Common::Rect r1(0, 0, kChoiceHiliteLefts[i + 1] - kChoiceHiliteLefts[i], kReactorChoiceHiliteHeight); + Common::Rect r2 = r1; + r1.moveTo(kChoiceHiliteLefts[i], 0); + r2.moveTo(kReactorChoiceHiliteLeft + kChoiceHiliteLefts[i], kReactorChoiceHiliteTop); + _colors.copyToCurrentPort(r1, r2); + } + } + } +} + +static const CoordType kReactorHistoryWidth = 128; +static const CoordType kReactorHistoryHeight = 168; + +static const CoordType kColorWidths[5] = { 24, 25, 25, 26, 27 }; +static const CoordType kColorHeights[5] = { 14, 15, 17, 17, 19}; + +static const CoordType kHistoryLefts[5][3] = { + { 302 + kNavAreaLeft, 329 + kNavAreaLeft, 357 + kNavAreaLeft }, + { 302 + kNavAreaLeft, 331 + kNavAreaLeft, 360 + kNavAreaLeft }, + { 303 + kNavAreaLeft, 333 + kNavAreaLeft, 363 + kNavAreaLeft }, + { 304 + kNavAreaLeft, 335 + kNavAreaLeft, 366 + kNavAreaLeft }, + { 305 + kNavAreaLeft, 337 + kNavAreaLeft, 369 + kNavAreaLeft } +}; + +static const CoordType kHistoryTops[5] = { + 39 + kNavAreaTop, + 61 + kNavAreaTop, + 84 + kNavAreaTop, + 110 + kNavAreaTop, + 137 + kNavAreaTop +}; + +static const CoordType kOneAnswerWidth = 35; +static const CoordType kOneAnswerHeight = 27; + +static const CoordType kDigitWidth = 16; +static const CoordType kDigitHeight = 12; + +static const CoordType kCorrectCountLefts[5] = { + 388 + kNavAreaLeft, + 392 + kNavAreaLeft, + 398 + kNavAreaLeft, + 402 + kNavAreaLeft, + 406 + kNavAreaLeft +}; + +static const CoordType kCorrectCountTops[5] = { + 40 + kNavAreaTop, + 62 + kNavAreaTop, + 86 + kNavAreaTop, + 112 + kNavAreaTop, + 140 + kNavAreaTop +}; + +static const ResIDType kReactorDigitsPICTID = 902; +static const ResIDType kReactorHistoryPICTID = 903; +static const ResIDType kReactorAnswerPICTID = 904; + +static const CoordType kReactorHistoryLeft = kNavAreaLeft + 302; +static const CoordType kReactorHistoryTop = kNavAreaTop + 39; + +static const CoordType kAnswerLeft = kNavAreaLeft + 304; +static const CoordType kAnswerTop = kNavAreaTop + 180; + +ReactorHistory::ReactorHistory(const DisplayElementID id) : DisplayElement(id) { + setBounds(kReactorHistoryLeft, kReactorHistoryTop, kReactorHistoryLeft + kReactorHistoryWidth, + kReactorHistoryTop + kReactorHistoryHeight); + setDisplayOrder(kMonitorLayer); + _numGuesses = 0; + _answer[0] = -1; + _answer[1] = -1; + _answer[2] = -1; + _showAnswer = false; +} + +void ReactorHistory::initReactorHistory() { + _colors.getImageFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kReactorHistoryPICTID); + _digits.getImageFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kReactorDigitsPICTID); + _answerColors.getImageFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kReactorAnswerPICTID); + startDisplaying(); + show(); +} + +void ReactorHistory::disposeReactorHistory() { + stopDisplaying(); + _colors.deallocateSurface(); +} + +void ReactorHistory::addGuess(int32 a, int32 b, int32 c) { + _history[_numGuesses][0] = a; + _history[_numGuesses][1] = b; + _history[_numGuesses][2] = c; + _numGuesses++; + triggerRedraw(); +} + +void ReactorHistory::clearHistory() { + _numGuesses = 0; + _showAnswer = false; + triggerRedraw(); +} + +void ReactorHistory::setAnswer(int32 a, int32 b, int32 c) { + _answer[0] = a; + _answer[1] = b; + _answer[2] = c; +} + +void ReactorHistory::showAnswer() { + _showAnswer = true; + triggerRedraw(); +} + +bool ReactorHistory::isSolved() { + for (int i = 0; i < _numGuesses; i++) + if (_history[i][0] == _answer[0] && _history[i][1] == _answer[1] && _history[i][2] == _answer[2]) + return true; + + return false; +} + +void ReactorHistory::draw(const Common::Rect &) { + static const CoordType kColorTops[5] = { + 0, + kColorHeights[0], + (CoordType)(kColorHeights[0] + kColorHeights[1]), + (CoordType)(kColorHeights[0] + kColorHeights[1] + kColorHeights[2]), + (CoordType)(kColorHeights[0] + kColorHeights[1] + kColorHeights[2] + kColorHeights[3]), + }; + + if (_colors.isSurfaceValid() && _digits.isSurfaceValid()) { + for (int i = 0; i < _numGuesses; ++i) { + Common::Rect r1(0, 0, kColorWidths[i], kColorHeights[i]); + Common::Rect r2 = r1; + Common::Rect r3(0, 0, kDigitWidth, kDigitHeight); + Common::Rect r4 = r3; + int correct = 0; + + for (int j = 0; j < 3; ++j) { + r1.moveTo(kColorWidths[i] * _history[i][j], kColorTops[i]); + r2.moveTo(kHistoryLefts[i][j], kHistoryTops[i]); + _colors.copyToCurrentPortTransparent(r1, r2); + + if (_history[i][j] == _answer[j]) + correct++; + } + + r3.moveTo(kDigitWidth * correct, 0); + r4.moveTo(kCorrectCountLefts[i], kCorrectCountTops[i]); + _digits.copyToCurrentPort(r3, r4); + } + + if (_showAnswer && _answerColors.isSurfaceValid()) { + Common::Rect r1(0, 0, kOneAnswerWidth, kOneAnswerHeight); + Common::Rect r2 = r1; + + for (int i = 0; i < 3; i++) { + r1.moveTo(kOneAnswerWidth * _answer[i], 0); + r2.moveTo(kAnswerLeft + 34 * i, kAnswerTop); + _answerColors.copyToCurrentPortTransparent(r1, r2); + } + } + } +} + +int32 ReactorHistory::getCurrentNumCorrect() { + int correct = 0; + + for (int i = 0; i < 3; i++) + if (_history[_numGuesses - 1][i] == _answer[i]) + correct++; + + return correct; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/mars/reactor.h b/engines/pegasus/neighborhood/mars/reactor.h new file mode 100644 index 0000000000..86338f8266 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/reactor.h @@ -0,0 +1,108 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_NEIGHBORHOOD_MARS_REACTOR_H +#define PEGASUS_NEIGHBORHOOD_MARS_REACTOR_H + +#include "pegasus/elements.h" +#include "pegasus/surface.h" +#include "pegasus/util.h" + +namespace Pegasus { + +class ReactorGuess : public DisplayElement { +public: + ReactorGuess(const DisplayElementID); + virtual ~ReactorGuess() {} + + void initReactorGuess(); + void disposeReactorGuess(); + + void setGuess(int32, int32, int32); + + void draw(const Common::Rect &); + +protected: + int32 _currentGuess[3]; + + Surface _colors; +}; + +class ReactorChoiceHighlight : public DisplayElement { +public: + ReactorChoiceHighlight(const DisplayElementID); + virtual ~ReactorChoiceHighlight() {} + + void initReactorChoiceHighlight(); + void disposeReactorChoiceHighlight(); + + void resetHighlight() { + _choices.clearAllFlags(); + triggerRedraw(); + } + + bool choiceHighlighted(uint32 whichChoice) { return _choices.getFlag(whichChoice); } + + void draw(const Common::Rect &); + + void highlightChoice(uint32 whichChoice) { + _choices.setFlag(whichChoice); + triggerRedraw(); + } + +protected: + Surface _colors; + FlagsArray<byte, 5> _choices; +}; + +class ReactorHistory : public DisplayElement { +public: + ReactorHistory(const DisplayElementID); + virtual ~ReactorHistory() {} + + void initReactorHistory(); + void disposeReactorHistory(); + + void draw(const Common::Rect &); + + void addGuess(int32, int32, int32); + int32 getNumGuesses() { return _numGuesses; } + void clearHistory(); + void setAnswer(int32, int32, int32); + void showAnswer(); + bool isSolved(); + int32 getCurrentNumCorrect(); + +protected: + Surface _colors, _digits, _answerColors; + int32 _answer[3]; + int32 _history[5][3]; + int32 _numGuesses; + bool _showAnswer; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/mars/robotship.cpp b/engines/pegasus/neighborhood/mars/robotship.cpp new file mode 100644 index 0000000000..1f4bbc1779 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/robotship.cpp @@ -0,0 +1,267 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/neighborhood/mars/hermite.h" +#include "pegasus/neighborhood/mars/mars.h" +#include "pegasus/neighborhood/mars/robotship.h" +#include "pegasus/neighborhood/mars/spacechase3d.h" +#include "pegasus/neighborhood/mars/spacejunk.h" + +namespace Pegasus { + +static const TimeScale kRovingScale = kTractorBeamScale; +static const TimeValue kRovingTime = kSixSeconds * kRovingScale; +static const TimeValue kRovingSlop = kThreeSeconds * kRovingScale; + +static const int kNumSpriteColumns = 15; +static const int kNumSpriteRows = 16; + +static const CoordType kInitialLocationLeft = kShuttleWindowLeft - 50; +static const CoordType kInitialLocationTop = kShuttleWindowTop - 50; +static const CoordType kInitialLocationWidth = kShuttleWindowWidth + 100; +static const CoordType kInitialLocationHeight = kShuttleWindowHeight + 100; + +static const CoordType kVelocityVectorLength = 100; +static const CoordType kVelocityVectorSlop = 50; + +static const CoordType kRovingLeft = kShuttleWindowLeft + 20; +static const CoordType kRovingTop = kShuttleWindowTop + 20; +static const CoordType kRovingWidth = kShuttleWindowMidH - kRovingLeft; +static const CoordType kRovingHeight = kShuttleWindowMidV - kRovingTop; + +RobotShip *g_robotShip = 0; + +RobotShip::RobotShip() : _spritesMovie(kNoDisplayElement) { + g_robotShip = this; + _shipRange = Common::Rect(kShuttleWindowLeft, kShuttleWindowTop, kShuttleWindowLeft + kShuttleWindowWidth, + kShuttleWindowTop + kShuttleWindowHeight); + setScale(kRovingScale); + _currentLocation.x = 0; + _currentLocation.y = 0; + _snaring = false; + _dropJunkFuse.setFunctor(new Common::Functor0Mem<void, RobotShip>(this, &RobotShip::timeToDropJunk)); + _duration = 0xFFFFFFFF; +} + +RobotShip::~RobotShip() { + g_robotShip = 0; +} + +void RobotShip::initRobotShip() { + _spritesMovie.initFromMovieFile("Images/Mars/Ship.movie", true); + _spritesMovie.setDisplayOrder(kShuttleRobotShipOrder); + _spritesMovie.moveElementTo(kPlanetStartLeft, kPlanetStartTop); + _spritesMovie.startDisplaying(); + _spritesMovie.show(); + + Common::Rect r; + _spritesMovie.getBounds(r); + _shipWidth = r.width(); + _shipHeight = r.height(); + _dead = false; +} + +void RobotShip::cleanUpRobotShip() { + _dropJunkFuse.stopFuse(); + _spritesMovie.stopDisplaying(); + _spritesMovie.releaseMovie(); +} + +void RobotShip::startMoving() { + if (((PegasusEngine *)g_engine)->getRandomBit()) { + _p4.x = kInitialLocationLeft + ((PegasusEngine *)g_engine)->getRandomNumber(kInitialLocationWidth - 1); + if (((PegasusEngine *)g_engine)->getRandomBit()) + _p4.y = kInitialLocationTop; + else + _p4.y = kInitialLocationTop + kInitialLocationHeight; + } else { + _p4.y = kInitialLocationTop + ((PegasusEngine *)g_engine)->getRandomNumber(kInitialLocationHeight - 1); + if (((PegasusEngine *)g_engine)->getRandomBit()) + _p4.x = kInitialLocationLeft; + else + _p4.x = kInitialLocationLeft + kInitialLocationWidth; + } + + makeVelocityVector(_p4.x, _p4.y, kShuttleWindowLeft + kShuttleWindowWidth / 2, + kShuttleWindowTop + kShuttleWindowHeight / 2, _r4); + newDestination(); + setUpNextDropTime(); +} + +void RobotShip::killRobotShip() { + cleanUpRobotShip(); + _dead = true; +} + +void RobotShip::setUpNextDropTime() { + if (!isSnared()) { + _dropJunkFuse.primeFuse(kJunkDropBaseTime + ((PegasusEngine *)g_engine)->getRandomNumber(kJunkDropSlopTime)); + _dropJunkFuse.lightFuse(); + } +} + +void RobotShip::timeToDropJunk() { + if (g_spaceJunk) { + CoordType x, y; + _spritesMovie.getCenter(x, y); + g_spaceJunk->launchJunk(((PegasusEngine *)g_engine)->getRandomNumber(24), x, y); + } +} + +void RobotShip::newDestination() { + _p1 = _p4; + _r1 = _r4; + + _p4.x = kRovingLeft + ((PegasusEngine *)g_engine)->getRandomNumber(kRovingWidth - 1); + _p4.y = kRovingTop + ((PegasusEngine *)g_engine)->getRandomNumber(kRovingHeight - 1); + + if (((PegasusEngine *)g_engine)->getRandomNumber(7) < 6) { + if (!sameSign(_p4.x - kShuttleWindowMidH, kShuttleWindowMidH - _p1.x)) { + if (sign(_p4.x - kShuttleWindowMidH) > 0) + _p4.x -= kRovingWidth; + else + _p4.x += kRovingWidth; + } + } + + if (((PegasusEngine *)g_engine)->getRandomNumber(7) < 6) { + if (!sameSign(_p4.y - kShuttleWindowMidV, kShuttleWindowMidV - _p1.y)) { + if (sign(_p4.y - kShuttleWindowMidV) > 0) + _p4.y -= kRovingHeight; + else + _p4.y += kRovingHeight; + } + } + + makeVelocityVector(_p4.x, _p4.y, kShuttleWindowLeft + kShuttleWindowWidth / 2, + kShuttleWindowTop + kShuttleWindowHeight / 2, _r4); + stop(); + _duration = kRovingTime + ((PegasusEngine *)g_engine)->getRandomNumber(kRovingSlop - 1); + setSegment(0, _duration); + setTime(0); + start(); +} + +void RobotShip::moveRobotTo(CoordType x, CoordType y) { + _currentLocation.x = x; + _currentLocation.y = y; + + if (_spritesMovie.isMovieValid()) { + _spritesMovie.moveElementTo(x - (_shipWidth >> 1), y - (_shipHeight >> 1)); + + if (x < _shipRange.left) + x = 0; + else if (x > _shipRange.right - 1) + x = _shipRange.width() - 1; + else + x -= _shipRange.left; + + if (y < _shipRange.top) + y = 0; + else if (y > _shipRange.bottom - 1) + y = _shipRange.height() - 1; + else + y -= _shipRange.top; + + x = kNumSpriteColumns * x / _shipRange.width(); + y = kNumSpriteRows * y / _shipRange.height(); + + _spritesMovie.setTime(40 * (x + y * kNumSpriteColumns)); + _spritesMovie.redrawMovieWorld(); + } +} + +bool RobotShip::pointInShuttle(Common::Point &pt) { + Common::Rect r; + _spritesMovie.getBounds(r); + + int dx = r.width() / 4; + int dy = r.height() / 6; + + r.left += dx; + r.right -= dx; + r.top += dy; + r.bottom -= dy; + + return r.contains(pt); +} + +void RobotShip::hitByEnergyBeam(Common::Point impactPoint) { + ((Mars *)g_neighborhood)->decreaseRobotShuttleEnergy(1, impactPoint); + setGlowing(true); + ((PegasusEngine *)g_engine)->delayShell(1, 3); + setGlowing(false); +} + +void RobotShip::hitByGravitonCannon(Common::Point impactPoint) { + GameState.setMarsHitRobotWithCannon(true); + ((Mars *)g_neighborhood)->decreaseRobotShuttleEnergy(6, impactPoint); +} + +void RobotShip::snareByTractorBeam() { + _dropJunkFuse.stopFuse(); + stop(); + + Common::Point currentV; + dHermite(_p1, _p4, _r1, _r4, _lastTime, _duration, currentV); + + _p1 = _currentLocation; + _r1 = currentV; + _p4.x = kShuttleWindowMidH; + _p4.y = kShuttleWindowMidV; + _r4.x = 0; + _r4.y = 0; + _duration = kTractorBeamTime; + _snaring = true; + setSegment(0, _duration); + setTime(0); + start(); +} + +void RobotShip::timeChanged(const TimeValue) { + Common::Point newLocation; + hermite(_p1, _p4, _r1, _r4, _lastTime, _duration, newLocation); + moveRobotTo(newLocation.x, newLocation.y); + + if (_lastTime == _duration) { + if (_snaring) + stop(); + else + newDestination(); + } +} + +void RobotShip::makeVelocityVector(CoordType x1, CoordType y1, CoordType x2, CoordType y2, Common::Point &vector) { + CoordType length = ((PegasusEngine *)g_engine)->getRandomNumber(kVelocityVectorSlop - 1) + kVelocityVectorLength; + vector.x = x2 - x1; + vector.y = y2 - y1; + float oldLength = sqrt((float)(vector.x * vector.x + vector.y * vector.y)); + vector.x = (int)(vector.x * length / oldLength); + vector.y = (int)(vector.y * length / oldLength); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/mars/robotship.h b/engines/pegasus/neighborhood/mars/robotship.h new file mode 100644 index 0000000000..04be3ea56e --- /dev/null +++ b/engines/pegasus/neighborhood/mars/robotship.h @@ -0,0 +1,84 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_NEIGHBORHOOD_MARS_ROBOTSHIP_H +#define PEGASUS_NEIGHBORHOOD_MARS_ROBOTSHIP_H + +#include "pegasus/movie.h" + +namespace Pegasus { + +static const CoordType kShuttleMovieWidth = 114; +static const CoordType kShuttleMovieHeight = 42; + +class RobotShip : IdlerTimeBase { +public: + RobotShip(); + virtual ~RobotShip(); + + void initRobotShip(); + void cleanUpRobotShip(); + + void startMoving(); + + void killRobotShip(); + + bool pointInShuttle(Common::Point&); + + void hitByEnergyBeam(Common::Point impactPoint); + void hitByGravitonCannon(Common::Point impactPoint); + + void getShuttleBounds(Common::Rect &r) { _spritesMovie.getBounds(r); } + + void setGlowing(const bool glowing) { _spritesMovie.setGlowing(glowing); } + + void snareByTractorBeam(); + bool isSnared() { return _snaring && getTime() == _duration; } + + bool isDead() { return _dead; } + + void setUpNextDropTime(); + +protected: + void newDestination(); + void moveRobotTo(CoordType, CoordType); + void timeToDropJunk(); + virtual void timeChanged(const TimeValue); + void makeVelocityVector(CoordType, CoordType, CoordType, CoordType, Common::Point &); + + GlowingMovie _spritesMovie; + Common::Rect _shipRange; + int _shipWidth, _shipHeight; + Common::Point _p1, _p4, _r1, _r4, _currentLocation; + FuseFunction _dropJunkFuse; + TimeValue _duration; + bool _snaring, _dead; +}; + +extern RobotShip *g_robotShip; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/mars/shuttleenergymeter.cpp b/engines/pegasus/neighborhood/mars/shuttleenergymeter.cpp new file mode 100644 index 0000000000..cd08dbae6a --- /dev/null +++ b/engines/pegasus/neighborhood/mars/shuttleenergymeter.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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/neighborhood/mars/constants.h" +#include "pegasus/neighborhood/mars/shuttleenergymeter.h" + +namespace Pegasus { + +ShuttleEnergyMeter::ShuttleEnergyMeter() : FaderAnimation(kNoDisplayElement) { + setBounds(kShuttleEnergyLeft, kShuttleEnergyTop, kShuttleEnergyLeft + kShuttleEnergyWidth, + kShuttleEnergyTop + kShuttleEnergyHeight); + setDisplayOrder(kShuttleStatusOrder); + setFaderValue(0); +} + +void ShuttleEnergyMeter::initShuttleEnergyMeter() { + _meterImage.getImageFromPICTFile("Images/Mars/Shuttle Energy.pict"); + _lowWarning.getImageFromPICTFile("Images/Mars/Shuttle Low Energy.pict"); + startDisplaying(); + show(); +} + +void ShuttleEnergyMeter::disposeShuttleEnergyMeter() { + stopFader(); + hide(); + stopDisplaying(); + _meterImage.deallocateSurface(); + _lowWarning.deallocateSurface(); +} + +void ShuttleEnergyMeter::draw(const Common::Rect &) { + int32 currentValue = getFaderValue(); + + Common::Rect r1, r2, bounds; + getBounds(bounds); + + if (currentValue < kLowShuttleEnergy) { + _lowWarning.getSurfaceBounds(r1); + r2 = r1; + r2.moveTo(bounds.left, bounds.top); + _lowWarning.copyToCurrentPort(r1, r2); + } + + _meterImage.getSurfaceBounds(r1); + r1.right = r1.left + r1.width() * currentValue / kFullShuttleEnergy; + r2 = r1; + r2.moveTo(bounds.left + 102, bounds.top + 6); + _meterImage.copyToCurrentPort(r1, r2); +} + +void ShuttleEnergyMeter::powerUpMeter() { + FaderMoveSpec moveSpec; + moveSpec.makeTwoKnotFaderSpec(kThirtyTicksPerSecond, 0, 0, 45, kFullShuttleEnergy); + startFader(moveSpec); +} + +void ShuttleEnergyMeter::setEnergyValue(const int32 value) { + stopFader(); + FaderMoveSpec moveSpec; + moveSpec.makeTwoKnotFaderSpec(kFifteenTicksPerSecond, value * 3, value, kFullShuttleEnergy * 3, kFullShuttleEnergy); + startFader(moveSpec); +} + +void ShuttleEnergyMeter::drainForTractorBeam() { + stopFader(); + TimeValue startTime = 0, stopTime; + int32 startValue = getFaderValue(), stopValue; + + if (startValue < kTractorBeamEnergy) { + stopTime = startValue * kTractorBeamTime / kTractorBeamEnergy; + stopValue = 0; + } else { + stopTime = kTractorBeamTime; + stopValue = startValue - kTractorBeamEnergy; + } + + FaderMoveSpec moveSpec; + moveSpec.makeTwoKnotFaderSpec(kTractorBeamScale, startTime, startValue, stopTime, stopValue); + startFader(moveSpec); +} + +int32 ShuttleEnergyMeter::getEnergyValue() const { + return getFaderValue(); +} + +void ShuttleEnergyMeter::dropEnergyValue(const int32 delta) { + setEnergyValue(getFaderValue() - delta); +} + +bool ShuttleEnergyMeter::enoughEnergyForTractorBeam() const { + return getEnergyValue() >= kTractorBeamEnergy; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/mars/shuttleenergymeter.h b/engines/pegasus/neighborhood/mars/shuttleenergymeter.h new file mode 100644 index 0000000000..51161e094e --- /dev/null +++ b/engines/pegasus/neighborhood/mars/shuttleenergymeter.h @@ -0,0 +1,73 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_NEIGHBORHOOD_MARS_SHUTTLEENERGYMETER_H +#define PEGASUS_NEIGHBORHOOD_MARS_SHUTTLEENERGYMETER_H + +#include "pegasus/fader.h" +#include "pegasus/surface.h" + +namespace Pegasus { + +static const int32 kFullShuttleEnergy = 100; +// Low is 20 percent +static const int32 kLowShuttleEnergy = kFullShuttleEnergy * 20 / 100; + +static const int32 kMinDampingEnergy = 15; +static const int32 kMinGravitonEnergy = 63; + +static const TimeScale kTractorBeamScale = kFifteenTicksPerSecond; +static const TimeValue kTractorBeamTime = kFiveSeconds * kTractorBeamScale; +static const int32 kTractorBeamEnergy = kLowShuttleEnergy; + +class ShuttleEnergyMeter : public FaderAnimation { +public: + ShuttleEnergyMeter(); + ~ShuttleEnergyMeter() {} + + void initShuttleEnergyMeter(); + void disposeShuttleEnergyMeter(); + + void powerUpMeter(); + + void setEnergyValue(const int32); + int32 getEnergyValue() const; + + void dropEnergyValue(const int32); + + void drainForTractorBeam(); + + bool enoughEnergyForTractorBeam() const; + + void draw(const Common::Rect &); + +protected: + Surface _meterImage; + Surface _lowWarning; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/mars/shuttlehud.cpp b/engines/pegasus/neighborhood/mars/shuttlehud.cpp new file mode 100644 index 0000000000..11e826278b --- /dev/null +++ b/engines/pegasus/neighborhood/mars/shuttlehud.cpp @@ -0,0 +1,246 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/pegasus.h" +#include "pegasus/neighborhood/mars/constants.h" +#include "pegasus/neighborhood/mars/robotship.h" +#include "pegasus/neighborhood/mars/shuttlehud.h" + +namespace Pegasus { + +static const CoordType kHUDTargetGridLeft = kShuttleWindowLeft + 16; +static const CoordType kHUDTargetGridTop = kShuttleWindowTop + 8; +static const CoordType kHUDTargetGridWidth = 328; +static const CoordType kHUDTargetGridHeight = 206; + +static const CoordType kHUDRS232Left = kHUDTargetGridLeft + 264; +static const CoordType kHUDRS232Top = kHUDTargetGridTop + 2; + +static const CoordType kHUDLockLeft = kShuttleWindowLeft + 101; +static const CoordType kHUDLockTop = kShuttleWindowTop + 49; +static const CoordType kHUDLockWidth = 145; +static const CoordType kHUDLockHeight = 124; + +static const CoordType kTractorLockWidth = 50; +static const CoordType kTractorLockHeight = 30; + +static const CoordType kTractorLockLeft = kShuttleWindowMidH - kTractorLockWidth / 2; +static const CoordType kTractorLockTop = kShuttleWindowMidV - kTractorLockHeight / 2; +static const CoordType kTractorLockRight = kTractorLockLeft + kTractorLockWidth; +static const CoordType kTractorLockBottom = kTractorLockTop + kTractorLockHeight; + +static const uint16 s_RS232Data[] = { + 0xF0E1, 0xCE70, + 0xF9E1, 0xEF78, + 0x4900, 0x2108, + 0x79C0, 0xE738, + 0x70E1, 0xC770, + 0x5821, 0x0140, + 0x4DE1, 0xEF78, + 0x45C1, 0xEE78 +}; + +static const uint16 s_lockData[] = { + 0xE007, 0xFE1F, 0xF8E0, 0x7000, + 0xE00F, 0xFF3F, 0xFCE0, 0xE000, + 0xE00E, 0x0738, 0x1CE1, 0xC000, + 0xE00E, 0x0738, 0x00FF, 0x8000, + 0xE00E, 0x0738, 0x00FF, 0x0000, + 0xE00E, 0x0738, 0x00E3, 0x8000, + 0xE00E, 0x0738, 0x1CE1, 0xC000, + 0xFFCF, 0xFF3F, 0xFCE0, 0xE000, + 0xFFC7, 0xFE1F, 0xF8E0, 0x7000 +}; + +#define drawHUDLockLine(x1, y1, x2, y2, penX, penY, color) \ + screen->drawThickLine((x1) + kHUDLockLeft, (y1) + kHUDLockTop, \ + (x2) + kHUDLockLeft, (y2) + kHUDLockTop, penX, penY, color) + +#define drawHUDLockArrows(offset, color) \ + drawHUDLockLine(63, 0 + (offset), 68, 5 + (offset), 1, 3, color); \ + drawHUDLockLine(71, 8 + (offset), 77, 14 + (offset), 1, 3, color); \ + drawHUDLockLine(78, 14 + (offset), 84, 8 + (offset), 1, 3, color); \ + drawHUDLockLine(87, 5 + (offset), 92, 0 + (offset), 1, 3, color); \ + drawHUDLockLine(63, 121 - (offset), 68, 116 - (offset), 1, 3, color); \ + drawHUDLockLine(71, 113 - (offset), 77, 107 - (offset), 1, 3, color); \ + drawHUDLockLine(78, 107 - (offset), 84, 113 - (offset), 1, 3, color); \ + drawHUDLockLine(87, 116 - (offset), 92, 121 - (offset), 1, 3, color); \ +\ + drawHUDLockLine(13 + (offset), 47, 18 + (offset), 52, 3, 1, color); \ + drawHUDLockLine(21 + (offset), 55, 27 + (offset), 61, 3, 1, color); \ + drawHUDLockLine(27 + (offset), 62, 21 + (offset), 68, 3, 1, color); \ + drawHUDLockLine(18 + (offset), 71, 13 + (offset), 76, 3, 1, color); \ + drawHUDLockLine(142 - (offset), 47, 137 - (offset), 52, 3, 1, color); \ + drawHUDLockLine(134 - (offset), 55, 128 - (offset), 61, 3, 1, color); \ + drawHUDLockLine(128 - (offset), 62, 134 - (offset), 68, 3, 1, color); \ + drawHUDLockLine(137 - (offset), 71, 142 - (offset), 76, 3, 1, color) + +ShuttleHUD::ShuttleHUD() : DisplayElement(kNoDisplayElement) { + _lightGreen = g_system->getScreenFormat().RGBToColor(0, 204, 0); + _gridDarkGreen = g_system->getScreenFormat().RGBToColor(0, 85, 0); + _lockDarkGreen1 = g_system->getScreenFormat().RGBToColor(0, 68, 0); + _lockDarkGreen2 = g_system->getScreenFormat().RGBToColor(0, 65, 0); + + _targetLocked = false; + setBounds(kShuttleWindowLeft, kShuttleWindowTop, kShuttleWindowLeft + kShuttleWindowWidth, + kShuttleWindowTop + kShuttleWindowHeight); + setDisplayOrder(kShuttleHUDOrder); +} + +void ShuttleHUD::initShuttleHUD() { + startDisplaying(); + startIdling(); +} + +void ShuttleHUD::cleanUpShuttleHUD() { + stopIdling(); + stopDisplaying(); +} + +void ShuttleHUD::showTargetGrid() { + show(); +} + +void ShuttleHUD::hideTargetGrid() { + hide(); + unlockOnTarget(); +} + +void ShuttleHUD::useIdleTime() { + if (isVisible()) { + Common::Rect r; + g_robotShip->getShuttleBounds(r); + if (r.left < kTractorLockRight && r.right > kTractorLockLeft && r.top < kTractorLockBottom && r.bottom > kTractorLockTop) + lockOnTarget(); + else + unlockOnTarget(); + } +} + +void ShuttleHUD::lockOnTarget() { + if (!_targetLocked) { + _targetLocked = true; + triggerRedraw(); + } +} + +void ShuttleHUD::unlockOnTarget() { + if (_targetLocked) { + _targetLocked = false; + triggerRedraw(); + } +} + +void ShuttleHUD::draw(const Common::Rect &) { + Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getWorkArea(); + + for (int y = 0; y < 35; y++) { + Common::Rect r; + + if (y & 1) { + if (y == 17) { + r = Common::Rect(0, 0, 4, 2); + r.moveTo(kHUDTargetGridLeft + 8, y * 6 + kHUDTargetGridTop); + screen->fillRect(r, _gridDarkGreen); + r.moveTo(kHUDTargetGridLeft + kHUDTargetGridWidth - 12, y * 6 + kHUDTargetGridTop); + screen->fillRect(r, _gridDarkGreen); + + r = Common::Rect(0, 0, 6, 2); + r.moveTo(kHUDTargetGridLeft + 2, y * 6 + kHUDTargetGridTop); + screen->fillRect(r, _lightGreen); + r.moveTo(kHUDTargetGridLeft + kHUDTargetGridWidth - 8, y * 6 + kHUDTargetGridTop); + screen->fillRect(r, _lightGreen); + + r = Common::Rect(0, 0, 23, 2); + r.moveTo(kHUDTargetGridLeft + 12, y * 6 + kHUDTargetGridTop); + screen->fillRect(r, _lightGreen); + r.moveTo(kHUDTargetGridLeft + kHUDTargetGridWidth - 35, y * 6 + kHUDTargetGridTop); + screen->fillRect(r, _lightGreen); + } else if (y == 1 || y == 15 || y == 19 || y == 33) { + r = Common::Rect(0, 0, 4, 2); + r.moveTo(kHUDTargetGridLeft + 2, y * 6 + kHUDTargetGridTop); + screen->fillRect(r, _gridDarkGreen); + r.moveTo(kHUDTargetGridLeft + kHUDTargetGridWidth - 6, y * 6 + kHUDTargetGridTop); + screen->fillRect(r, _gridDarkGreen); + + r = Common::Rect(0, 0, 15, 2); + r.moveTo(kHUDTargetGridLeft + 8, y * 6 + kHUDTargetGridTop); + screen->fillRect(r, _gridDarkGreen); + r.moveTo(kHUDTargetGridLeft + kHUDTargetGridWidth - 23, y * 6 + kHUDTargetGridTop); + screen->fillRect(r, _gridDarkGreen); + } else { + r = Common::Rect(0, 0, 4, 2); + r.moveTo(kHUDTargetGridLeft + 2, y * 6 + kHUDTargetGridTop); + screen->fillRect(r, _gridDarkGreen); + r.moveTo(kHUDTargetGridLeft + kHUDTargetGridWidth - 6, y * 6 + kHUDTargetGridTop); + screen->fillRect(r, _gridDarkGreen); + + r = Common::Rect(0, 0, 10, 2); + r.moveTo(kHUDTargetGridLeft + 8, y * 6 + kHUDTargetGridTop); + screen->fillRect(r, _gridDarkGreen); + r.moveTo(kHUDTargetGridLeft + kHUDTargetGridWidth - 18, y * 6 + kHUDTargetGridTop); + screen->fillRect(r, _gridDarkGreen); + } + } else { + r = Common::Rect(0, 0, 2, 2); + r.moveTo(kHUDTargetGridLeft, y * 6 + kHUDTargetGridTop); + screen->fillRect(r, _gridDarkGreen); + r.moveTo(kHUDTargetGridLeft + kHUDTargetGridWidth - 2, y * 6 + kHUDTargetGridTop); + screen->fillRect(r, _gridDarkGreen); + + r = Common::Rect(0, 0, 4, 2); + r.moveTo(kHUDTargetGridLeft + 8, y * 6 + kHUDTargetGridTop); + screen->fillRect(r, _gridDarkGreen); + r.moveTo(kHUDTargetGridLeft + kHUDTargetGridWidth - 12, y * 6 + kHUDTargetGridTop); + screen->fillRect(r, _gridDarkGreen); + } + } + + drawOneBitImageOr(screen, s_RS232Data, 2, Common::Rect(kHUDRS232Left, kHUDRS232Top, + kHUDRS232Left + 29, kHUDRS232Top + 8), _gridDarkGreen); + + if (_targetLocked) { + drawHUDLockArrows(0, _lockDarkGreen2); + drawHUDLockArrows(12, _lockDarkGreen1); + drawHUDLockArrows(24, _lightGreen); + drawOneBitImageOr(screen, s_lockData, 4, Common::Rect(kHUDLockLeft, kHUDLockTop + 115, + kHUDLockLeft + 52, kHUDLockTop + 115 + 9), _lightGreen); + } +} + +void ShuttleHUD::drawOneBitImageOr(Graphics::Surface *screen, const uint16 *data, int pitch, const Common::Rect &bounds, uint32 color) { + for (int y = 0; y < bounds.height(); y++) { + for (int x = 0; x < bounds.width(); x++) { + if ((data[y * pitch + x / 16] & (1 << (15 - (x % 16)))) != 0) { + if (screen->format.bytesPerPixel == 2) + WRITE_UINT16((byte *)screen->getBasePtr(x + bounds.left, y + bounds.top), color); + else + WRITE_UINT32((byte *)screen->getBasePtr(x + bounds.left, y + bounds.top), color); + } + } + } +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/mars/shuttlehud.h b/engines/pegasus/neighborhood/mars/shuttlehud.h new file mode 100644 index 0000000000..f7dbbaeae8 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/shuttlehud.h @@ -0,0 +1,60 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_NEIGHBORHOOD_MARS_SHUTTLEHUD_H +#define PEGASUS_NEIGHBORHOOD_MARS_SHUTTLEHUD_H + +#include "pegasus/elements.h" +#include "pegasus/timers.h" + +namespace Pegasus { + +class ShuttleHUD : public DisplayElement, public Idler { +public: + ShuttleHUD(); + + void showTargetGrid(); + void hideTargetGrid(); + + void initShuttleHUD(); + void cleanUpShuttleHUD(); + + bool isTargetLocked() { return _targetLocked; } + + void draw(const Common::Rect &); + +protected: + void useIdleTime(); + void lockOnTarget(); + void unlockOnTarget(); + void drawOneBitImageOr(Graphics::Surface *, const uint16 *, int, const Common::Rect &, uint32); + + bool _targetLocked; + uint32 _lightGreen, _gridDarkGreen, _lockDarkGreen1, _lockDarkGreen2; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/mars/shuttleweapon.cpp b/engines/pegasus/neighborhood/mars/shuttleweapon.cpp new file mode 100644 index 0000000000..b4c360b280 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/shuttleweapon.cpp @@ -0,0 +1,129 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/neighborhood/mars/constants.h" +#include "pegasus/neighborhood/mars/robotship.h" +#include "pegasus/neighborhood/mars/shuttleweapon.h" +#include "pegasus/neighborhood/mars/spacejunk.h" + +namespace Pegasus { + +ShuttleWeapon::ShuttleWeapon() : IdlerAnimation(kNoDisplayElement) { + setScale(kShuttleWeaponScale); + _weaponDuration = kShuttleWeaponScale * 2; + setSegment(0, _weaponDuration); + setBounds(kShuttleWindowLeft, kShuttleWindowTop, kShuttleWindowLeft + kShuttleWindowWidth, + kShuttleWindowTop + kShuttleWindowHeight); + setDisplayOrder(kShuttleWeaponFrontOrder); +} + +void ShuttleWeapon::initShuttleWeapon() { + startDisplaying(); +} + +void ShuttleWeapon::cleanUpShuttleWeapon() { + stop(); + hide(); + stopDisplaying(); +} + +bool ShuttleWeapon::canFireWeapon() { + return !isRunning(); +} + +void ShuttleWeapon::fireWeapon(const CoordType hStop, const CoordType vStop) { + if (!isRunning()) { + stop(); + setTime(0); + show(); + + Common::Point pt2D(hStop, vStop); + project2DTo3D(pt2D, kShuttleDistance, _weaponTarget); + _weaponTime = 0; + setDisplayOrder(kShuttleWeaponFrontOrder); + start(); + } +} + +void ShuttleWeapon::updateWeaponPosition() { + _weaponTime = (float)_lastTime / _weaponDuration; + linearInterp(_weaponOrigin, _weaponTarget, _weaponTime, _weaponLocation); + + if (_weaponTime == 1.0) { + stop(); + hide(); + } else { + triggerRedraw(); + } +} + +void ShuttleWeapon::timeChanged(const TimeValue) { + updateWeaponPosition(); + + bool hit = false; + Common::Point impactPoint; + + if (g_spaceJunk->isJunkFlying()) { + hit = collisionWithJunk(impactPoint); + if (hit) { + stop(); + hide(); + hitJunk(impactPoint); + } + } + + if (!hit && _weaponTime == 1.0 && collisionWithShuttle(impactPoint)) + hitShuttle(impactPoint); +} + +bool ShuttleWeapon::collisionWithJunk(Common::Point &impactPoint) { + if (getDisplayOrder() == kShuttleWeaponFrontOrder) { + Point3D junkPosition; + g_spaceJunk->getJunkPosition(junkPosition); + + if (junkPosition.z < _weaponLocation.z) { + setDisplayOrder(kShuttleWeaponBackOrder); + project3DTo2D(_weaponLocation, impactPoint); + return g_spaceJunk->pointInJunk(impactPoint); + } + } + + return false; +} + +bool ShuttleWeapon::collisionWithShuttle(Common::Point &impactPoint) { + project3DTo2D(_weaponLocation, impactPoint); + return g_robotShip->pointInShuttle(impactPoint); +} + +void ShuttleWeapon::hitJunk(Common::Point impactPoint) { + g_spaceJunk->hitByEnergyBeam(impactPoint); +} + +void ShuttleWeapon::hitShuttle(Common::Point impactPoint) { + g_robotShip->hitByEnergyBeam(impactPoint); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/mars/shuttleweapon.h b/engines/pegasus/neighborhood/mars/shuttleweapon.h new file mode 100644 index 0000000000..38529c8919 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/shuttleweapon.h @@ -0,0 +1,68 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_NEIGHBORHOOD_MARS_SHUTTLEWEAPON_H +#define PEGASUS_NEIGHBORHOOD_MARS_SHUTTLEWEAPON_H + +#include "pegasus/elements.h" +#include "pegasus/neighborhood/mars/spacechase3d.h" + +namespace Pegasus { + +// Can fire multiple times? +// For now, no... +// clone2727 adds: And now forever + +static const TimeScale kShuttleWeaponScale = kFifteenTicksPerSecond; + +class ShuttleWeapon : public IdlerAnimation { +public: + ShuttleWeapon(); + virtual ~ShuttleWeapon() {} + + virtual void initShuttleWeapon(); + virtual void cleanUpShuttleWeapon(); + + virtual void fireWeapon(const CoordType, const CoordType); + + bool canFireWeapon(); + +protected: + void timeChanged(const TimeValue); + virtual void updateWeaponPosition(); + virtual bool collisionWithJunk(Common::Point &impactPoint); + bool collisionWithShuttle(Common::Point &impactPoint); + virtual void hitJunk(Common::Point impactPoint); + virtual void hitShuttle(Common::Point impactPoint); + + Point3D _weaponOrigin, _weaponTarget; + Point3D _weaponLocation; + float _weaponTime; + TimeValue _weaponDuration; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/mars/spacechase3d.cpp b/engines/pegasus/neighborhood/mars/spacechase3d.cpp new file mode 100644 index 0000000000..05f8233763 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/spacechase3d.cpp @@ -0,0 +1,106 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/neighborhood/mars/spacechase3d.h" + +namespace Pegasus { + +void project3DTo2D(const Point3D &pt3D, Common::Point &pt2D) { + pt2D.x = (int)convertSpaceXToScreenH(pt3D.x, pt3D.z); + pt2D.y = (int)convertSpaceYToScreenV(pt3D.y, pt3D.z); +} + +void project2DTo3D(const Common::Point &pt2D, const float screenDistance, Point3D &pt3D) { + pt3D.x = convertScreenHToSpaceX(pt2D.x, screenDistance); + pt3D.y = convertScreenVToSpaceY(pt2D.y, screenDistance); + pt3D.z = screenDistance; +} + +void linearInterp(const Point3D &pt1, const Point3D &pt2, const float t, Point3D &pt3) { + pt3.x = pt1.x + (pt2.x - pt1.x) * t; + pt3.y = pt1.y + (pt2.y - pt1.y) * t; + pt3.z = pt1.z + (pt2.z - pt1.z) * t; +} + +void linearInterp(const Point3D &pt1, const float x2, const float y2, const float z2, const float t, Point3D &pt3) { + pt3.x = pt1.x + (x2 - pt1.x) * t; + pt3.y = pt1.y + (y2 - pt1.y) * t; + pt3.z = pt1.z + (z2 - pt1.z) * t; +} + +void linearInterp(const float x1, const float y1, const float z1, const Point3D &pt2, const float t, Point3D &pt3) { + pt3.x = x1 + (pt2.x - x1) * t; + pt3.y = y1 + (pt2.y - y1) * t; + pt3.z = z1 + (pt2.z - z1) * t; +} + +void linearInterp(const float x1, const float y1, const float z1, const float x2, const float y2, const float z2, + const float t, Point3D &pt3) { + pt3.x = x1 + (x2 - x1) * t; + pt3.y = y1 + (y2 - y1) * t; + pt3.z = z1 + (z2 - z1) * t; +} + +void linearInterp(const Common::Point &pt1, const Common::Point &pt2, const float t, Common::Point &pt3) { + pt3.x = (int)(pt1.x + (pt2.x - pt1.x) * t); + pt3.y = (int)(pt1.y + (pt2.y - pt1.y) * t); +} + +void linearInterp(const Common::Point &pt1, const float h2, const float v2, const float t, Common::Point &pt3) { + pt3.x = (int)(pt1.x + (h2 - pt1.x) * t); + pt3.y = (int)(pt1.y + (v2 - pt1.y) * t); +} + +void linearInterp(const float h1, const float v1, const Common::Point &pt2, const float t, Common::Point &pt3) { + pt3.x = (int)(h1 + (pt2.x - h1) * t); + pt3.y = (int)(v1 + (pt2.y - v1) * t); +} + +void linearInterp(const float h1, const float v1, const float h2, const float v2, const float t, Common::Point &pt3) { + pt3.x = (int)(h1 + (h2 - h1) * t); + pt3.y = (int)(v1 + (v2 - v1) * t); +} + +float linearInterp(const float arg1, const float arg2, const float t) { + return arg1 + (arg2 - arg1) * t; +} + +bool isNegative(int a) { + return a < 0; +} + +bool isPositive(int a) { + return a > 0; +} + +int sign(int a) { + return isNegative(a) ? -1 : isPositive(a) ? 1 : 0; +} + +bool sameSign(int a, int b) { + return sign(a) == sign(b); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/mars/spacechase3d.h b/engines/pegasus/neighborhood/mars/spacechase3d.h new file mode 100644 index 0000000000..f6815e69bd --- /dev/null +++ b/engines/pegasus/neighborhood/mars/spacechase3d.h @@ -0,0 +1,91 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_NEIGHBORHOOD_MARS_SPACECHASE3D_H +#define PEGASUS_NEIGHBORHOOD_MARS_SPACECHASE3D_H + +#include "pegasus/neighborhood/mars/constants.h" + +namespace Pegasus { + +// This is approximately right for a field of view of 72 degrees +// (Should be set to the tangent of FOV). +//static const float kTangentFOV = 0.76254; +static const float kTangentFOV = 1.0; + +// Define these as macros and they can be used to define constants... +#define convertSpaceXToScreenH(x, z) \ + ((x) / (z) * (kScreenWidth / (2 * kTangentFOV)) + kShuttleWindowMidH) + +#define convertSpaceYToScreenV(y, z) \ + (kShuttleWindowMidV - (y) / (z) * (kScreenWidth / (2 * kTangentFOV))) + +#define convertScreenHToSpaceX(x, d) \ + (((2.0 * kTangentFOV) / kScreenWidth) * ((float)(x) - kShuttleWindowMidH) * (d)) + +#define convertScreenVToSpaceY(y, d) \ + (((2.0 * kTangentFOV) / kScreenWidth) * ((float)kShuttleWindowMidV - (y)) * (d)) + +struct Point3D { + float x, y, z; + + Point3D() : x(0), y(0), z(0) {} + Point3D(float x1, float y1, float z1) : x(x1), y(y1), z(z1) {} + bool operator==(const Point3D &p) const { return x == p.x && y == p.y && z == p.z; } + bool operator!=(const Point3D &p) const { return x != p.x || y != p.y || z != p.z; } + + void translate(float dx, float dy, float dz) { + x += dx; + y += dy; + z += dz; + } +}; + +static const int kScreenWidth = kShuttleWindowWidth; + +bool isNegative(int a); +bool isPositive(int a); +int sign(int a); +bool sameSign(int a, int b); + +void project3DTo2D(const Point3D &pt3D, Common::Point &pt2D); +void project2DTo3D(const Common::Point &pt2D, const float screenDistance, Point3D &pt3D); + +void linearInterp(const Point3D &pt1, const Point3D &pt2, const float t, Point3D &pt3); +void linearInterp(const Point3D &pt1, const float x2, const float y2, const float z2, const float t, Point3D &pt3); +void linearInterp(const float x1, const float y1, const float z1, const Point3D &pt2, const float t, Point3D &pt3); +void linearInterp(const float x1, const float y1, const float z1, const float x2, + const float y2, const float z2, const float t, Point3D &pt3); + +void linearInterp(const Common::Point &pt1, const Common::Point &pt2, const float t, Common::Point &pt3); +void linearInterp(const Common::Point &pt1, const float h2, const float v2, const float t, Common::Point &pt3); +void linearInterp(const float h1, const float v1, const Common::Point &pt2, const float t, Common::Point &pt3); +void linearInterp(const float h1, const float v1, const float h2, const float v2, const float t, Common::Point &pt3); + +float linearInterp(const float arg1, const float arg2, const float t); + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/mars/spacejunk.cpp b/engines/pegasus/neighborhood/mars/spacejunk.cpp new file mode 100644 index 0000000000..3912e659c2 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/spacejunk.cpp @@ -0,0 +1,212 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/pegasus.h" +#include "pegasus/neighborhood/mars/mars.h" +#include "pegasus/neighborhood/mars/spacejunk.h" + +namespace Pegasus { + +static const CoordType kMaxBounceSize = 90; +static const CoordType kBounceTargetHRange = 640 - kMaxBounceSize - 2; +static const CoordType kBounceTargetVRange = 480 - kMaxBounceSize - 2; + +static const float kJunkXTarget = 0; +static const float kJunkYTarget = 0; +static const float kJunkZTarget = kJunkMinDistance; + +SpaceJunk *g_spaceJunk = 0; + +SpaceJunk::SpaceJunk(const DisplayElementID id) : ScalingMovie(id) { + _timer.setScale(kJunkTimeScale); + _bouncing = false; + g_spaceJunk = this; +} + +SpaceJunk::~SpaceJunk() { + g_spaceJunk = 0; +} + +void SpaceJunk::launchJunk(int16 whichJunk, CoordType xOrigin, CoordType yOrigin) { + _bouncing = false; + TimeValue startTime = whichJunk * 16 * 40; + TimeValue stopTime = startTime + 16 * 40; + + _launchPoint = Point3D(convertScreenHToSpaceX(xOrigin, kJunkMaxDistance), + convertScreenVToSpaceY(yOrigin, kJunkMaxDistance), kJunkMaxDistance); + startIdling(); + stop(); + setFlags(0); + setSegment(startTime, stopTime); + setFlags(kLoopTimeBase); + setTime(startTime); + start(); + show(); + _timer.stop(); + _timer.setSegment(0, kJunkTravelTime); + _timer.setTime(0); + + // Force it to set up correctly from the get-go + useIdleTime(); + + _timer.start(); +} + +void SpaceJunk::setCenter(const CoordType centerX, const CoordType centerY) { + _center.x = centerX; + _center.y = centerY; + + Common::Rect r; + getBounds(r); + r.moveTo(CLIP<int>(centerX - (r.width() >> 1), 0, 640 - r.width()), CLIP<int>(centerY - (r.height() >> 1), 0, 480 - r.height())); + setBounds(r); +} + +void SpaceJunk::setScaleSize(const CoordType size) { + Common::Rect r; + r.left = _center.x - (size >> 1); + r.top = _center.y - (size >> 1); + r.right = r.left + size; + r.bottom = r.top + size; + setBounds(r); +} + +void SpaceJunk::useIdleTime() { + if (_bouncing) { + TimeValue time = _timer.getTime(); + Common::Point pt; + pt.x = linearInterp(0, _bounceTime, time, _bounceStart.x, _bounceStop.x); + pt.y = linearInterp(0, _bounceTime, time, _bounceStart.y, _bounceStop.y); + CoordType size = linearInterp(0, _bounceTime, time, _bounceSizeStart, _bounceSizeStop); + setCenter(pt.x, pt.y); + setScaleSize(size); + + if (time == _bounceTime) { + stop(); + stopIdling(); + hide(); + ((Mars *)g_neighborhood)->setUpNextDropTime(); + } + } else { + float t = (float)_timer.getTime() / kJunkTravelTime; + linearInterp(_launchPoint, kJunkXTarget, kJunkYTarget, kJunkZTarget, t, _junkPosition); + + Common::Point pt2D; + project3DTo2D(_junkPosition, pt2D); + setCenter(pt2D.x, pt2D.y); + setScaleSize((int)(convertSpaceYToScreenV(_junkPosition.y - kJunkSize / 2, _junkPosition.z) - + convertSpaceYToScreenV(_junkPosition.y + kJunkSize / 2, _junkPosition.z))); + + if (t == 1.0) { + rebound(kCollisionReboundTime); + ((Mars *)g_neighborhood)->hitByJunk(); + } + } +} + +bool SpaceJunk::pointInJunk(const Common::Point &pt) { + Common::Rect r; + getBounds(r); + + int dx = r.width() / 4; + int dy = r.height() / 4; + + r.left += dx; + r.right -= dx; + r.top += dy; + r.top -= dy; + + return r.contains(pt); +} + +void SpaceJunk::rebound(const TimeValue reboundTime) { + Common::Rect bounds; + getBounds(bounds); + + _bounceStart.x = (bounds.left + bounds.right) >> 1; + _bounceStart.y = (bounds.top + bounds.bottom) >> 1; + + PegasusEngine *vm = (PegasusEngine *)g_engine; + + switch (vm->getRandomNumber(3)) { + case 0: + _bounceStop.x = kMaxBounceSize / 2 + 1 + vm->getRandomNumber(kBounceTargetHRange - 1); + _bounceStop.y = kMaxBounceSize / 2 + 1; + break; + case 1: + _bounceStop.x = kMaxBounceSize / 2 + 1 + vm->getRandomNumber(kBounceTargetHRange - 1); + _bounceStop.y = 480 - kMaxBounceSize / 2 + 1; + break; + case 2: + _bounceStop.x = kMaxBounceSize / 2 + 1; + _bounceStop.y = kMaxBounceSize / 2 + 1 + vm->getRandomNumber(kBounceTargetVRange - 1); + break; + case 3: + _bounceStop.x = 640 - kMaxBounceSize / 2 + 1; + _bounceStop.y = kMaxBounceSize / 2 + 1 + vm->getRandomNumber(kBounceTargetVRange - 1); + break; + } + + _bounceSizeStart = bounds.width(); + _bounceSizeStop = MIN(_bounceSizeStart, kMaxBounceSize); + + _timer.stop(); + _timer.setSegment(0, reboundTime); + _bounceTime = reboundTime; + _timer.setTime(0); + _timer.start(); + + _bouncing = true; +} + +void SpaceJunk::hitByEnergyBeam(Common::Point) { + rebound(kWeaponReboundTime); + setGlowing(true); + ((PegasusEngine *)g_engine)->delayShell(1, 3); + setGlowing(false); +} + +void SpaceJunk::hitByGravitonCannon(Common::Point impactPoint) { + stop(); + stopIdling(); + hide(); + + Common::Rect r; + getBounds(r); + r = Common::Rect::center(impactPoint.x, impactPoint.y, r.width(), r.height()); + + ((Mars *)g_neighborhood)->showBigExplosion(r, kShuttleJunkOrder); + ((Mars *)g_neighborhood)->setUpNextDropTime(); +} + +void SpaceJunk::getJunkPosition(Point3D &position) { + position = _junkPosition; +} + +bool SpaceJunk::isJunkFlying() { + return isIdling(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/mars/spacejunk.h b/engines/pegasus/neighborhood/mars/spacejunk.h new file mode 100644 index 0000000000..2ec9192513 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/spacejunk.h @@ -0,0 +1,78 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_NEIGHBORHOOD_MARS_SPACEJUNK_H +#define PEGASUS_NEIGHBORHOOD_MARS_SPACEJUNK_H + +#include "pegasus/movie.h" +#include "pegasus/neighborhood/mars/constants.h" +#include "pegasus/neighborhood/mars/spacechase3d.h" + +namespace Pegasus { + +static const CoordType kJunkMaxScreenSize = 250; + +static const float kJunkSize = convertScreenVToSpaceY(kShuttleWindowMidV - kJunkMaxScreenSize / 2, kJunkMinDistance) - + convertScreenVToSpaceY(kShuttleWindowMidV + kJunkMaxScreenSize / 2, kJunkMinDistance); + +class SpaceJunk : public ScalingMovie, public Idler { +public: + SpaceJunk(const DisplayElementID); + virtual ~SpaceJunk(); + + void setCenter(const CoordType, const CoordType); + void setScaleSize(const CoordType); + + void useIdleTime(); + + void launchJunk(int16, CoordType, CoordType); + + void getJunkPosition(Point3D &); + bool isJunkFlying(); + + bool pointInJunk(const Common::Point &); + + void hitByEnergyBeam(Common::Point impactPoint); + void hitByGravitonCannon(Common::Point impactPoint); + + bool junkFlying() { return _timer.isRunning(); } + +protected: + void rebound(const TimeValue); + + TimeBase _timer; + Point3D _launchPoint, _junkPosition; + Common::Point _center; + bool _bouncing; + Common::Point _bounceStart, _bounceStop; + CoordType _bounceSizeStart, _bounceSizeStop; + TimeValue _bounceTime; +}; + +extern SpaceJunk *g_spaceJunk; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/mars/tractorbeam.cpp b/engines/pegasus/neighborhood/mars/tractorbeam.cpp new file mode 100644 index 0000000000..d3f9c94328 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/tractorbeam.cpp @@ -0,0 +1,139 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/pegasus.h" +#include "pegasus/neighborhood/mars/constants.h" +#include "pegasus/neighborhood/mars/tractorbeam.h" + +namespace Pegasus { + +TractorBeam::TractorBeam() : DisplayElement(kNoDisplayElement) { + setBounds(kShuttleTractorLeft, kShuttleTractorTop, kShuttleTractorLeft + kShuttleTractorWidth, + kShuttleTractorTop + kShuttleTractorHeight); + setDisplayOrder(kShuttleTractorBeamOrder); + +} + +static const int kHalfWidth = kShuttleTractorWidth >> 1; +static const int kHalfHeight = kShuttleTractorHeight >> 1; + +static const int kW3Vert = kHalfHeight * kHalfHeight * kHalfHeight; +static const int kW3Div2Vert = kW3Vert >> 1; + +static const int kW3Horiz = kHalfWidth * kHalfWidth * kHalfWidth; +static const int kW3Div2Horiz = kW3Horiz >> 1; + +static const int kMaxLevel = 50; + +static const int kAVert = -2 * kMaxLevel; +static const int kBVert = 3 * kMaxLevel * kHalfHeight; + +#define READ_PIXEL(ptr) \ + if (screen->format.bytesPerPixel == 2) \ + color = READ_UINT16(ptr); \ + else \ + color = READ_UINT32(ptr); \ + screen->format.colorToRGB(color, r, g, b) + +#define WRITE_PIXEL(ptr) \ + color = screen->format.RGBToColor(r, g, b); \ + if (screen->format.bytesPerPixel == 2) \ + WRITE_UINT16(ptr, color); \ + else \ + WRITE_UINT32(ptr, color) + +#define DO_BLEND(ptr) \ + READ_PIXEL(ptr); \ + g += (((0xff - g) * blendHoriz) >> 8); \ + b += (((0xff - b) * blendHoriz) >> 8); \ + WRITE_PIXEL(ptr) + +void TractorBeam::draw(const Common::Rect &) { + Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getWorkArea(); + + // Set up vertical DDA. + int blendVert = 0; + int dVert = 0; + int d1Vert = kAVert + kBVert; + int d2Vert = 6 * kAVert + 2 * kBVert; + int d3Vert = 6 * kAVert; + + byte *rowPtrTop = (byte *)screen->getBasePtr(_bounds.left, _bounds.top); + byte *rowPtrBottom = (byte *)screen->getBasePtr(_bounds.left, _bounds.top + ((kHalfHeight << 1) - 1)); + + for (int y = kHalfHeight; y > 0; y--) { + // Set up horizontal DDA + int A = -2 * blendVert; + int B = 3 * blendVert * kHalfWidth; + int blendHoriz = 0; + int dHoriz = 0; + int d1Horiz = A + B; + int d2Horiz = 6 * A + 2 * B; + int d3Horiz = 6 * A; + + byte *pTopLeft = rowPtrTop; + byte *pTopRight = rowPtrTop + (kHalfWidth * 2 - 1) * screen->format.bytesPerPixel; + byte *pBottomLeft = rowPtrBottom; + byte *pBottomRight = rowPtrBottom + (kHalfWidth * 2 - 1) * screen->format.bytesPerPixel; + + for (int x = kHalfWidth; x > 0; x--) { + byte r, g, b; + uint32 color; + + DO_BLEND(pTopLeft); + DO_BLEND(pTopRight); + DO_BLEND(pBottomLeft); + DO_BLEND(pBottomRight); + + pTopLeft += screen->format.bytesPerPixel; + pBottomLeft += screen->format.bytesPerPixel; + pTopRight -= screen->format.bytesPerPixel; + pBottomRight -= screen->format.bytesPerPixel; + + while (dHoriz > kW3Div2Horiz) { + blendHoriz++; + dHoriz -= kW3Horiz; + } + + dHoriz += d1Horiz; + d1Horiz += d2Horiz; + d2Horiz += d3Horiz; + } + + rowPtrTop += screen->pitch; + rowPtrBottom -= screen->pitch; + + while (dVert > kW3Div2Vert) { + blendVert++; + dVert -= kW3Vert; + } + + dVert += d1Vert; + d1Vert += d2Vert; + d2Vert += d3Vert; + } +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/mars/tractorbeam.h b/engines/pegasus/neighborhood/mars/tractorbeam.h new file mode 100644 index 0000000000..cd87992d11 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/tractorbeam.h @@ -0,0 +1,43 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_NEIGHBORHOOD_MARS_TRACTORBEAM_H +#define PEGASUS_NEIGHBORHOOD_MARS_TRACTORBEAM_H + +#include "pegasus/elements.h" + +namespace Pegasus { + +class TractorBeam : public DisplayElement { +public: + TractorBeam(); + virtual ~TractorBeam() {} + + void draw(const Common::Rect &); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/neighborhood.cpp b/engines/pegasus/neighborhood/neighborhood.cpp new file mode 100644 index 0000000000..38366c4ba2 --- /dev/null +++ b/engines/pegasus/neighborhood/neighborhood.cpp @@ -0,0 +1,1774 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "common/debug.h" +#include "common/stream.h" + +#include "pegasus/compass.h" +#include "pegasus/cursor.h" +#include "pegasus/energymonitor.h" +#include "pegasus/gamestate.h" +#include "pegasus/graphics.h" +#include "pegasus/input.h" +#include "pegasus/interaction.h" +#include "pegasus/interface.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/biochips/mapchip.h" +#include "pegasus/neighborhood/neighborhood.h" +#include "pegasus/neighborhood/tsa/fulltsa.h" +#include "pegasus/neighborhood/tsa/tinytsa.h" + +namespace Pegasus { + +StriderCallBack::StriderCallBack(Neighborhood *neighborhood) { + _neighborhood = neighborhood; +} + +void StriderCallBack::callBack() { + _neighborhood->checkStriding(); +} + +static const TimeValue kStridingSlop = 39; + +Neighborhood *g_neighborhood = 0; + +Neighborhood::Neighborhood(InputHandler *nextHandler, PegasusEngine *vm, const Common::String &resName, NeighborhoodID id) + : InputHandler(nextHandler), IDObject(id), _vm(vm), _resName(resName), _navMovie(kNavMovieID), _stridingCallBack(this), + _neighborhoodNotification(kNeighborhoodNotificationID, (NotificationManager *)vm), _pushIn(kNoDisplayElement), + _turnPush(kTurnPushID), _croppedMovie(kCroppedMovieID) { + GameState.setOpenDoorLocation(kNoRoomID, kNoDirection); + _currentAlternate = 0; + _currentActivation = kActivateHotSpotAlways; + _interruptionFilter = kFilterAllInput; + allowInput(true); + resetLastExtra(); + g_neighborhood = this; + _currentInteraction = 0; + _doneWithInteraction = false; + _croppedMovie.setDisplayOrder(kCroppedMovieLayer); +} + +Neighborhood::~Neighborhood() { + for (HotspotIterator it = _neighborhoodHotspots.begin(); it != _neighborhoodHotspots.end(); it++) + _vm->getAllHotspots().remove(*it); + + _neighborhoodHotspots.deleteHotspots(); + g_neighborhood = 0; + + loadLoopSound1(""); + loadLoopSound2(""); + newInteraction(kNoInteractionID); + + if (g_AIArea) + g_AIArea->removeAllRules(); +} + +void Neighborhood::init() { + _neighborhoodNotification.notifyMe(this, kNeighborhoodFlags, kNeighborhoodFlags); + _navMovieCallBack.setNotification(&_neighborhoodNotification); + _turnPushCallBack.setNotification(&_neighborhoodNotification); + _delayCallBack.setNotification(&_neighborhoodNotification); + _spotSoundCallBack.setNotification(&_neighborhoodNotification); + + debug(0, "Loading '%s' neighborhood resources", _resName.c_str()); + + Common::SeekableReadStream *stream = _vm->_resFork->getResource(_doorTable.getResTag(), _resName); + if (!stream) + error("Failed to load doors"); + _doorTable.loadFromStream(stream); + delete stream; + + stream = _vm->_resFork->getResource(_exitTable.getResTag(), _resName); + if (!stream) + error("Failed to load exits"); + _exitTable.loadFromStream(stream); + delete stream; + + stream = _vm->_resFork->getResource(_extraTable.getResTag(), _resName); + if (!stream) + error("Failed to load extras"); + _extraTable.loadFromStream(stream); + delete stream; + + stream = _vm->_resFork->getResource(_hotspotInfoTable.getResTag(), _resName); + if (!stream) + error("Failed to load hotspot info"); + _hotspotInfoTable.loadFromStream(stream); + delete stream; + + stream = _vm->_resFork->getResource(_spotTable.getResTag(), _resName); + if (!stream) + error("Failed to load spots"); + _spotTable.loadFromStream(stream); + delete stream; + + stream = _vm->_resFork->getResource(_turnTable.getResTag(), _resName); + if (!stream) + error("Failed to load turns"); + _turnTable.loadFromStream(stream); + delete stream; + + stream = _vm->_resFork->getResource(_viewTable.getResTag(), _resName); + if (!stream) + error("Failed to load views"); + _viewTable.loadFromStream(stream); + delete stream; + + stream = _vm->_resFork->getResource(_zoomTable.getResTag(), _resName); + if (!stream) + error("Failed to load zooms"); + _zoomTable.loadFromStream(stream); + delete stream; + + createNeighborhoodSpots(); + + _navMovie.initFromMovieFile(getNavMovieName()); + _navMovie.setVolume(_vm->getSoundFXLevel()); + + Common::String soundSpotsName = getSoundSpotsName(); + if (soundSpotsName.empty()) { + _spotSounds.disposeSound(); + } else { + _spotSounds.initFromQuickTime(getSoundSpotsName()); + _spotSounds.setVolume(_vm->getSoundFXLevel()); + } + + _navMovie.setDisplayOrder(kNavMovieOrder); + _navMovie.startDisplaying(); + + Common::Rect bounds; + _navMovie.getBounds(bounds); + _pushIn.allocateSurface(bounds); + + _turnPush.setInAndOutElements(&_pushIn, &_navMovie); + _turnPush.setDisplayOrder(kTurnPushOrder); + _turnPush.startDisplaying(); + _navMovieCallBack.initCallBack(&_navMovie, kCallBackAtExtremes); + _stridingCallBack.initCallBack(&_navMovie, kCallBackAtTime); + _turnPushCallBack.initCallBack(&_turnPush, kCallBackAtExtremes); + _delayCallBack.initCallBack(&_delayTimer, kCallBackAtExtremes); + _spotSoundCallBack.initCallBack(&_spotSounds, kCallBackAtExtremes); + + setUpAIRules(); + + if (g_compass) + g_compass->setFaderValue(getStaticCompassAngle(GameState.getCurrentRoom(), GameState.getCurrentDirection())); + + _soundLoop1.attachFader(&_loop1Fader); + _soundLoop2.attachFader(&_loop2Fader); + startIdling(); +} + +void Neighborhood::start() { + GameState.setCurrentRoom(GameState.getLastRoom()); + GameState.setCurrentDirection(GameState.getLastDirection()); + arriveAt(GameState.getNextRoom(), GameState.getNextDirection()); +} + +void Neighborhood::receiveNotification(Notification *, const NotificationFlags flags) { + if ((flags & (kNeighborhoodMovieCompletedFlag | kTurnCompletedFlag)) != 0 && g_AIArea) + g_AIArea->unlockAI(); + if (flags & kMoveForwardCompletedFlag) + arriveAt(GameState.getNextRoom(), GameState.getNextDirection()); + if (flags & kTurnCompletedFlag) + turnTo(GameState.getNextDirection()); + if (flags & kSpotCompletedFlag) + spotCompleted(); + if (flags & kDoorOpenCompletedFlag) + doorOpened(); + if (flags & kActionRequestCompletedFlag) + popActionQueue(); + if (flags & kDeathExtraCompletedFlag) + die(_extraDeathReason); +} + +void Neighborhood::arriveAt(const RoomID room, const DirectionConstant direction) { + if (g_map) + g_map->moveToMapLocation(GameState.getCurrentNeighborhood(), room, direction); + + GameState.setCurrentNeighborhood(getObjectID()); + + _currentActivation = kActivateHotSpotAlways; + _interruptionFilter = kFilterAllInput; + + if (room != GameState.getCurrentRoom() || direction != GameState.getCurrentDirection()) { + GameState.setCurrentRoom(room); + GameState.setCurrentDirection(direction); + loadAmbientLoops(); + activateCurrentView(room, direction, kSpotOnArrivalMask); + } else { + loadAmbientLoops(); + showViewFrame(getViewTime(GameState.getCurrentRoom(), GameState.getCurrentDirection())); + } + + if (GameState.getOpenDoorRoom() != kNoRoomID) { + // Arriving always closes a door. + loadAmbientLoops(); + closeDoorOffScreen(GameState.getOpenDoorRoom(), GameState.getOpenDoorDirection()); + GameState.setOpenDoorLocation(kNoRoomID, kNoDirection); + } + + if (g_compass) + g_compass->setFaderValue(getStaticCompassAngle(GameState.getCurrentRoom(), GameState.getCurrentDirection())); + + if (g_AIArea) + g_AIArea->checkMiddleArea(); + + checkContinuePoint(room, direction); +} + +// These functions can be overridden to tweak the exact frames used. + +void Neighborhood::getExitEntry(const RoomID room, const DirectionConstant direction, ExitTable::Entry &entry) { + entry = _exitTable.findEntry(room, direction, _currentAlternate); + + if (entry.isEmpty()) + entry = _exitTable.findEntry(room, direction, kNoAlternateID); +} + +TimeValue Neighborhood::getViewTime(const RoomID room, const DirectionConstant direction) { + if (GameState.getOpenDoorRoom() == room && GameState.getOpenDoorDirection() == direction) { + // If we get here, the door entry for this location must exist. + DoorTable::Entry doorEntry = _doorTable.findEntry(room, direction, _currentAlternate); + + if (doorEntry.isEmpty()) + doorEntry = _doorTable.findEntry(room, direction, kNoAlternateID); + + return doorEntry.movieEnd - 1; + } + + ViewTable::Entry viewEntry = _viewTable.findEntry(room, direction, _currentAlternate); + + if (viewEntry.isEmpty()) + viewEntry = _viewTable.findEntry(room, direction, kNoAlternateID); + + return viewEntry.time; +} + +void Neighborhood::getDoorEntry(const RoomID room, const DirectionConstant direction, DoorTable::Entry &doorEntry) { + doorEntry = _doorTable.findEntry(room, direction, _currentAlternate); + + if (doorEntry.isEmpty()) + doorEntry = _doorTable.findEntry(room, direction, kNoAlternateID); +} + +DirectionConstant Neighborhood::getTurnEntry(const RoomID room, const DirectionConstant direction, const TurnDirection turnDirection) { + TurnTable::Entry turnEntry = _turnTable.findEntry(room, direction, turnDirection, _currentAlternate); + + if (turnEntry.isEmpty()) + turnEntry = _turnTable.findEntry(room, direction, turnDirection, kNoAlternateID); + + return turnEntry.endDirection; +} + +void Neighborhood::findSpotEntry(const RoomID room, const DirectionConstant direction, SpotFlags flags, SpotTable::Entry &spotEntry) { + spotEntry = _spotTable.findEntry(room, direction, flags, _currentAlternate); + + if (spotEntry.isEmpty()) + spotEntry = _spotTable.findEntry(room, direction, flags, kNoAlternateID); +} + +void Neighborhood::getZoomEntry(const HotSpotID id, ZoomTable::Entry &zoomEntry) { + zoomEntry = _zoomTable.findEntry(id); +} + +void Neighborhood::getHotspotEntry(const HotSpotID id, HotspotInfoTable::Entry &hotspotEntry) { + hotspotEntry = _hotspotInfoTable.findEntry(id); +} + +void Neighborhood::getExtraEntry(const uint32 id, ExtraTable::Entry &extraEntry) { + extraEntry = _extraTable.findEntry(id); +} + +///////////////////////////////////////////// +// +// "Can" functions: Called to see whether or not a user is allowed to do something + +CanMoveForwardReason Neighborhood::canMoveForward(ExitTable::Entry &entry) { + DoorTable::Entry door; + + getExitEntry(GameState.getCurrentRoom(), GameState.getCurrentDirection(), entry); + getDoorEntry(GameState.getCurrentRoom(), GameState.getCurrentDirection(), door); + + // Fixed this so that doors that don't lead anywhere can be opened, but not walked + // through. + if (door.flags & kDoorPresentMask) { + if (GameState.isCurrentDoorOpen()) { + if (entry.exitRoom == kNoRoomID) + return kCantMoveBlocked; + else + return kCanMoveForward; + } else if (door.flags & kDoorLockedMask) { + return kCantMoveDoorLocked; + } else { + return kCantMoveDoorClosed; + } + } else if (entry.exitRoom == kNoRoomID) { + return kCantMoveBlocked; + } + + return kCanMoveForward; +} + +CanTurnReason Neighborhood::canTurn(TurnDirection turnDirection, DirectionConstant &nextDir) { + nextDir = getTurnEntry(GameState.getCurrentRoom(), GameState.getCurrentDirection(), turnDirection); + + if (nextDir == kNoDirection) + return kCantTurnNoTurn; + + return kCanTurn; +} + +CanOpenDoorReason Neighborhood::canOpenDoor(DoorTable::Entry &entry) { + getDoorEntry(GameState.getCurrentRoom(), GameState.getCurrentDirection(), entry); + + if (entry.flags & kDoorPresentMask) { + if (GameState.isCurrentDoorOpen()) + return kCantOpenAlreadyOpen; + + if (entry.flags & kDoorLockedMask) + return kCantOpenLocked; + + return kCanOpenDoor; + } + + return kCantOpenNoDoor; +} + +void Neighborhood::createNeighborhoodSpots() { + Common::SeekableReadStream *hotspotList = _vm->_resFork->getResource(MKTAG('H', 'S', 'L', 's'), _resName); + if (!hotspotList) + error("Could not load neighborhood hotspots"); + + uint32 hotspotCount = hotspotList->readUint32BE(); + + while (hotspotCount--) { + uint16 id = hotspotList->readUint16BE(); + uint32 flags = hotspotList->readUint32BE(); + uint32 rgnSize = hotspotList->readUint32BE(); + + int32 startPos = hotspotList->pos(); + + debug(0, "Hotspot %d:", id); + Region region(hotspotList); + + hotspotList->seek(startPos + rgnSize); + + Hotspot *hotspot = new Hotspot(id); + hotspot->setHotspotFlags(flags); + hotspot->setArea(region); + + _vm->getAllHotspots().push_back(hotspot); + _neighborhoodHotspots.push_back(hotspot); + } + + delete hotspotList; +} + +void Neighborhood::popActionQueue() { + if (!_actionQueue.empty()) { + QueueRequest topRequest = _actionQueue.pop(); + + switch (topRequest.requestType) { + case kNavExtraRequest: + _navMovie.stop(); + break; + case kSpotSoundRequest: + _spotSounds.stopSound(); + break; + case kDelayRequest: + _delayTimer.stop(); + break; + } + + serviceActionQueue(); + } +} + +void Neighborhood::serviceActionQueue() { + if (!_actionQueue.empty()) { + QueueRequest &topRequest = _actionQueue.front(); + + if (!topRequest.playing) { + topRequest.playing = true; + switch (topRequest.requestType) { + case kNavExtraRequest: + startExtraSequence(topRequest.extra, topRequest.flags, topRequest.interruptionFilter); + break; + case kSpotSoundRequest: + _spotSounds.stopSound(); + _spotSounds.playSoundSegment(topRequest.start, topRequest.stop); + _interruptionFilter = topRequest.interruptionFilter; + _spotSoundCallBack.setCallBackFlag(topRequest.flags); + _spotSoundCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + break; + case kDelayRequest: + _delayTimer.stop(); + _delayCallBack.setCallBackFlag(topRequest.flags); + _delayTimer.setSegment(0, topRequest.start, topRequest.stop); + _delayTimer.setTime(0); + _delayCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + _interruptionFilter = topRequest.interruptionFilter; + _delayTimer.start(); + break; + } + } + } else { + _interruptionFilter = kFilterAllInput; + } +} + +void Neighborhood::requestAction(const QueueRequestType requestType, const ExtraID extra, const TimeValue in, const TimeValue out, + const InputBits interruptionFilter, const NotificationFlags flags) { + + QueueRequest request; + + request.requestType = requestType; + request.extra = extra; + request.start = in; + request.stop = out; + request.interruptionFilter = interruptionFilter; + request.playing = false; + request.flags = flags | kActionRequestCompletedFlag; + request.notification = &_neighborhoodNotification; + _actionQueue.push(request); + if (_actionQueue.size() == 1) + serviceActionQueue(); +} + +void Neighborhood::requestExtraSequence(const ExtraID whichExtra, const NotificationFlags flags, const InputBits interruptionFilter) { + requestAction(kNavExtraRequest, whichExtra, 0, 0, interruptionFilter, flags); +} + +void Neighborhood::requestSpotSound(const TimeValue in, const TimeValue out, const InputBits interruptionFilter, const NotificationFlags flags) { + requestAction(kSpotSoundRequest, 0xFFFFFFFF, in, out, interruptionFilter, flags); +} + +void Neighborhood::playSpotSoundSync(const TimeValue in, const TimeValue out) { + // Let the action queue play out first... + while (!actionQueueEmpty()) { + _vm->checkCallBacks(); + _vm->refreshDisplay(); + _vm->checkNotifications(); + _vm->_system->delayMillis(10); + } + + _spotSounds.stopSound(); + _spotSounds.playSoundSegment(in, out); + + while (_spotSounds.isPlaying()) { + _vm->checkCallBacks(); + _vm->refreshDisplay(); + _vm->_system->delayMillis(10); + } +} + +void Neighborhood::requestDelay(const TimeValue delayDuration, const TimeScale delayScale, const InputBits interruptionFilter, const NotificationFlags flags) { + requestAction(kDelayRequest, 0xFFFFFFFF, delayDuration, delayScale, interruptionFilter, flags); +} + +bool operator==(const QueueRequest &arg1, const QueueRequest &arg2) { + return arg1.requestType == arg2.requestType && arg1.extra == arg2.extra && + arg1.start == arg2.start && arg1.stop == arg2.stop; +} + +bool operator!=(const QueueRequest &arg1, const QueueRequest &arg2) { + return !operator==(arg1, arg2); +} + +Common::String Neighborhood::getBriefingMovie() { + if (_currentInteraction) + return _currentInteraction->getBriefingMovie(); + + return Common::String(); +} + +Common::String Neighborhood::getEnvScanMovie() { + if (_currentInteraction) + return _currentInteraction->getEnvScanMovie(); + + return Common::String(); +} + +uint Neighborhood::getNumHints() { + if (_currentInteraction) + return _currentInteraction->getNumHints(); + + return 0; +} + +Common::String Neighborhood::getHintMovie(uint hintNum) { + if (_currentInteraction) + return _currentInteraction->getHintMovie(hintNum); + + return Common::String(); +} + +bool Neighborhood::canSolve() { + if (_currentInteraction) + return _currentInteraction->canSolve(); + + return false; +} + +void Neighborhood::doSolve() { + if (_currentInteraction) + _currentInteraction->doSolve(); +} + +bool Neighborhood::okayToJump() { + return !_vm->playerHasItemID(kGasCanister) && !_vm->playerHasItemID(kMachineGun); +} + +AirQuality Neighborhood::getAirQuality(const RoomID) { + return kAirQualityGood; +} + +void Neighborhood::checkStriding() { + if (stillMoveForward()) { + ExitTable::Entry nextExit; + getExitEntry(GameState.getNextRoom(), GameState.getNextDirection(), nextExit); + keepStriding(nextExit); + } else { + stopStriding(); + } +} + +bool Neighborhood::stillMoveForward() { + Input input; + + InputHandler::readInputDevice(input); + return input.upButtonAnyDown(); +} + +void Neighborhood::keepStriding(ExitTable::Entry &nextExitEntry) { + FaderMoveSpec compassMove; + + if (g_map) + g_map->moveToMapLocation(GameState.getCurrentNeighborhood(), GameState.getNextRoom(), GameState.getNextDirection()); + + if (g_compass) + getExitCompassMove(nextExitEntry, compassMove); + + GameState.setCurrentRoom(GameState.getNextRoom()); + GameState.setCurrentDirection(GameState.getNextDirection()); + GameState.setNextRoom(nextExitEntry.exitRoom); + GameState.setNextDirection(nextExitEntry.exitDirection); + + if (nextExitEntry.movieEnd == nextExitEntry.exitEnd) + scheduleNavCallBack(kNeighborhoodMovieCompletedFlag | kMoveForwardCompletedFlag); + else + scheduleStridingCallBack(nextExitEntry.movieEnd - kStridingSlop, kStrideCompletedFlag); + + if (g_compass) + g_compass->startFader(compassMove); +} + +void Neighborhood::stopStriding() { + _navMovie.stop(); + _neighborhoodNotification.setNotificationFlags(kNeighborhoodMovieCompletedFlag | + kMoveForwardCompletedFlag, kNeighborhoodMovieCompletedFlag | kMoveForwardCompletedFlag); +} + +// Compass support +int16 Neighborhood::getStaticCompassAngle(const RoomID, const DirectionConstant dir) { + // North, south, east, west + static const int16 compassAngles[] = { 0, 180, 90, 270 }; + return compassAngles[dir]; +} + +void Neighborhood::getExitCompassMove(const ExitTable::Entry &exitEntry, FaderMoveSpec &compassMove) { + int32 startAngle = getStaticCompassAngle(exitEntry.room, exitEntry.direction); + int32 stopAngle = getStaticCompassAngle(exitEntry.exitRoom, exitEntry.exitDirection); + + if (startAngle > stopAngle) { + if (stopAngle + 180 < startAngle) + stopAngle += 360; + } else { + if (startAngle + 180 < stopAngle) + startAngle += 360; + } + + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), exitEntry.movieStart, startAngle, exitEntry.movieEnd, stopAngle); +} + +void Neighborhood::scheduleNavCallBack(NotificationFlags flags) { + _navMovieCallBack.cancelCallBack(); + + if (flags != 0) { + _navMovieCallBack.setCallBackFlag(flags); + _navMovieCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + } +} + +void Neighborhood::scheduleStridingCallBack(const TimeValue strideStop, NotificationFlags flags) { + _stridingCallBack.cancelCallBack(); + + if (flags != 0) + _stridingCallBack.scheduleCallBack(kTriggerTimeFwd, strideStop, _navMovie.getScale()); +} + +void Neighborhood::moveNavTo(const CoordType h, const CoordType v) { + CoordType oldH, oldV; + _navMovie.getLocation(oldH, oldV); + + CoordType offH = h - oldH; + CoordType offV = v - oldV; + + _navMovie.moveElementTo(h, v); + _turnPush.moveElementTo(h, v); + + if (offH != 0 || offV != 0) + for (HotspotList::iterator it = _neighborhoodHotspots.begin(); it != _neighborhoodHotspots.end(); it++) + if ((*it)->getHotspotFlags() & kNeighborhoodSpotFlag) + (*it)->moveSpot(offH, offV); +} + +void Neighborhood::activateHotspots() { + InputHandler::activateHotspots(); + + for (HotspotInfoTable::iterator it = _hotspotInfoTable.begin(); it != _hotspotInfoTable.end(); it++) { + HotspotInfoTable::Entry entry = *it; + + if (entry.hotspotRoom == GameState.getCurrentRoom() && entry.hotspotDirection == GameState.getCurrentDirection() + && (entry.hotspotActivation == _currentActivation || entry.hotspotActivation == kActivateHotSpotAlways)) { + Hotspot *hotspot = _vm->getAllHotspots().findHotspotByID(entry.hotspot); + if (hotspot) + activateOneHotspot(entry, hotspot); + } + } +} + +void Neighborhood::clickInHotspot(const Input &input, const Hotspot *clickedSpot) { + HotSpotFlags flags = clickedSpot->getHotspotFlags(); + + if ((flags & (kPickUpItemSpotFlag | kPickUpBiochipSpotFlag)) != 0) { + ItemID itemID = kNoItemID; + + for (HotspotInfoTable::iterator it = _hotspotInfoTable.begin(); it != _hotspotInfoTable.end(); it++) { + if (it->hotspot == clickedSpot->getObjectID()) { + itemID = it->hotspotItem; + break; + } + } + + if (itemID != kNoItemID) { + Item *draggingItem = _vm->getAllItems().findItemByID(itemID); + + if (draggingItem) { + takeItemFromRoom(draggingItem); + + if ((flags & kPickUpItemSpotFlag) != 0) + _vm->dragItem(input, draggingItem, kDragInventoryPickup); + else + _vm->dragItem(input, draggingItem, kDragBiochipPickup); + } + } + } else { + // Check other flags here? + if ((flags & kZoomSpotFlags) != 0) { + zoomTo(clickedSpot); + } else if ((flags & kPlayExtraSpotFlag) != 0) { + HotspotInfoTable::Entry hotspotEntry; + getHotspotEntry(clickedSpot->getObjectID(), hotspotEntry); + startExtraSequence(hotspotEntry.hotspotExtra, kExtraCompletedFlag, kFilterNoInput); + } else if ((flags & kOpenDoorSpotFlag) != 0) { + openDoor(); + } else { + InputHandler::clickInHotspot(input, clickedSpot); + } + } +} + +void Neighborhood::cantMoveThatWay(CanMoveForwardReason reason) { + switch (reason) { + case kCantMoveDoorClosed: + case kCantMoveDoorLocked: + openDoor(); + break; + case kCantMoveBlocked: + zoomUpOrBump(); + break; + default: + bumpIntoWall(); + break; + } +} + +void Neighborhood::cantOpenDoor(CanOpenDoorReason) { + bumpIntoWall(); +} + +void Neighborhood::turnTo(const DirectionConstant direction) { + if (g_map) + g_map->moveToMapLocation(GameState.getCurrentNeighborhood(), GameState.getCurrentRoom(), direction); + + // clone2727 says: Is this necessary? + _vm->_gfx->setCurSurface(_navMovie.getSurface()); + _pushIn.copyToCurrentPort(); + _vm->_gfx->setCurSurface(_vm->_gfx->getWorkArea()); + + // Added 2/10/97. Shouldn't this be here? Shouldn't we set the current activation to + // always when turning to a new view? + _currentActivation = kActivateHotSpotAlways; + + _interruptionFilter = kFilterAllInput; + + if (direction != GameState.getCurrentDirection()) { + GameState.setCurrentDirection(direction); + activateCurrentView(GameState.getCurrentRoom(), direction, kSpotOnTurnMask); + } else { + showViewFrame(getViewTime(GameState.getCurrentRoom(), GameState.getCurrentDirection())); + } + + if (GameState.getOpenDoorRoom() != kNoRoomID) { + // Turning always closes a door. + loadAmbientLoops(); + closeDoorOffScreen(GameState.getOpenDoorRoom(), GameState.getOpenDoorDirection()); + GameState.setOpenDoorLocation(kNoRoomID, kNoDirection); + } + + if (g_AIArea) + g_AIArea->checkMiddleArea(); + + checkContinuePoint(GameState.getCurrentRoom(), direction); + + _vm->_cursor->hideUntilMoved(); +} + +void Neighborhood::spotCompleted() { + _interruptionFilter = kFilterAllInput; + showViewFrame(getViewTime(GameState.getCurrentRoom(), GameState.getCurrentDirection())); +} + +void Neighborhood::doorOpened() { + _interruptionFilter = kFilterAllInput; + + // 2/23/97 + // Fixes funny bug with doors that are opened by dropping things on them... + setCurrentActivation(kActivateHotSpotAlways); + + GameState.setOpenDoorLocation(GameState.getCurrentRoom(), GameState.getCurrentDirection()); + + SpotTable::Entry entry; + findSpotEntry(GameState.getCurrentRoom(), GameState.getCurrentDirection(), kSpotOnDoorOpenMask, entry); + + if (entry.dstFlags & kSpotOnDoorOpenMask) { + startSpotOnceOnly(entry.movieStart, entry.movieEnd); + } else { + findSpotEntry(GameState.getCurrentRoom(), GameState.getCurrentDirection(), kSpotOnDoorOpenMask | kSpotLoopsMask, entry); + + if (entry.dstFlags & kSpotOnDoorOpenMask) + startSpotLoop(entry.movieStart, entry.movieEnd); + } + + loadAmbientLoops(); + + if (g_map) + g_map->moveToMapLocation(GameState.getCurrentNeighborhood(), GameState.getNextRoom(), GameState.getNextDirection()); + + if (g_AIArea) + g_AIArea->checkMiddleArea(); +} + +void Neighborhood::moveForward() { + ExitTable::Entry exitEntry; + CanMoveForwardReason moveReason = canMoveForward(exitEntry); + + if (moveReason == kCanMoveForward) + startExitMovie(exitEntry); + else + cantMoveThatWay(moveReason); +} + +void Neighborhood::turn(const TurnDirection turnDirection) { + DirectionConstant nextDir; + CanTurnReason turnReason = canTurn(turnDirection, nextDir); + + if (turnReason == kCanTurn) + startTurnPush(turnDirection, getViewTime(GameState.getCurrentRoom(), nextDir), nextDir); + else + cantTurnThatWay(turnReason); +} + +void Neighborhood::turnLeft() { + turn(kTurnLeft); +} + +void Neighborhood::turnRight() { + turn(kTurnRight); +} + +void Neighborhood::turnUp() { + turn(kTurnUp); +} + +void Neighborhood::turnDown() { + turn(kTurnDown); +} + +void Neighborhood::openDoor() { + DoorTable::Entry door; + CanOpenDoorReason doorReason = canOpenDoor(door); + + if (doorReason == kCanOpenDoor) + startDoorOpenMovie(door.movieStart, door.movieEnd); + else + cantOpenDoor(doorReason); +} + +void Neighborhood::zoomTo(const Hotspot *hotspot) { + ZoomTable::Entry zoomEntry; + getZoomEntry(hotspot->getObjectID(), zoomEntry); + if (!zoomEntry.isEmpty()) + startZoomMovie(zoomEntry); +} + +void Neighborhood::updateViewFrame() { + showViewFrame(getViewTime(GameState.getCurrentRoom(), GameState.getCurrentDirection())); +} + +void Neighborhood::startSpotLoop(TimeValue startTime, TimeValue stopTime, NotificationFlags flags) { + _turnPush.hide(); + startMovieSequence(startTime, stopTime, flags, true, kFilterAllInput); +} + +void Neighborhood::showViewFrame(TimeValue viewTime) { + if ((int32)viewTime >= 0) { + _turnPush.hide(); + _navMovie.stop(); + _navMovie.setFlags(0); + _navMovie.setSegment(0, _navMovie.getDuration()); + _navMovie.setTime(viewTime); + + Common::Rect pushBounds; + _turnPush.getBounds(pushBounds); + + _navMovie.moveElementTo(pushBounds.left, pushBounds.top); + _navMovie.show(); + _navMovie.redrawMovieWorld(); + } +} + +void Neighborhood::startExtraSequence(const ExtraID extraID, const NotificationFlags flags, const InputBits interruptionFilter) { + ExtraTable::Entry entry; + getExtraEntry(extraID, entry); + + if (entry.movieStart != 0xffffffff) + playExtraMovie(entry, flags, interruptionFilter); +} + +bool Neighborhood::startExtraSequenceSync(const ExtraID extraID, const InputBits interruptionFilter) { + InputDevice.waitInput(interruptionFilter); + return prepareExtraSync(extraID) && waitMovieFinish(&_navMovie, interruptionFilter); +} + +void Neighborhood::loopExtraSequence(const uint32 extraID, NotificationFlags flags) { + ExtraTable::Entry entry; + getExtraEntry(extraID, entry); + + if (entry.movieStart != 0xffffffff) { + _lastExtra = extraID; + startSpotLoop(entry.movieStart, entry.movieEnd, flags); + } +} + +bool Neighborhood::navMoviePlaying() { + return _navMovie.isRunning(); +} + +void Neighborhood::playDeathExtra(ExtraID extra, DeathReason deathReason) { + _extraDeathReason = deathReason; + startExtraSequence(extra, kDeathExtraCompletedFlag, kFilterNoInput); +} + +void Neighborhood::die(const DeathReason deathReason) { + loadLoopSound1(""); + loadLoopSound2(""); + _vm->die(deathReason); +} + +void Neighborhood::setSoundFXLevel(const uint16 fxLevel) { + if (_navMovie.isSurfaceValid()) + _navMovie.setVolume(fxLevel); + if (_spotSounds.isSoundLoaded()) + _spotSounds.setVolume(fxLevel); + if (_currentInteraction) + _currentInteraction->setSoundFXLevel(fxLevel); +} + +void Neighborhood::setAmbienceLevel(const uint16 ambientLevel) { + if (_soundLoop1.isSoundLoaded()) + _loop1Fader.setMasterVolume(_vm->getAmbienceLevel()); + if (_soundLoop2.isSoundLoaded()) + _loop2Fader.setMasterVolume(_vm->getAmbienceLevel()); + if (_currentInteraction) + _currentInteraction->setAmbienceLevel(ambientLevel); +} + +// Force the exit taken from (room, direction, alternate) to come to a stop. +void Neighborhood::forceStridingStop(const RoomID room, const DirectionConstant direction, const AlternateID alternate) { + ExitTable::Entry entry = _exitTable.findEntry(room, direction, alternate); + + if (entry.movieStart != 0xffffffff) { + TimeValue strideStop = entry.exitEnd; + TimeValue exitStop = entry.movieEnd; + + if (strideStop != exitStop) { + for (ExitTable::iterator it = _exitTable.begin(); it != _exitTable.end(); it++) { + entry = *it; + + if (entry.exitEnd == strideStop && entry.movieEnd <= exitStop) { + entry.exitEnd = exitStop; + *it = entry; + } + } + } + } +} + +// Restore the exit taken from (room, direction, alternate) to stride. +void Neighborhood::restoreStriding(const RoomID room, const DirectionConstant direction, const AlternateID alternate) { + ExitTable::Entry entry = _exitTable.findEntry(room, direction, alternate); + + if (entry.movieStart != 0xffffffff) { + TimeValue strideStop = entry.exitEnd; + TimeValue exitStop = entry.movieEnd; + + if (strideStop != entry.originalEnd) { + for (ExitTable::iterator it = _exitTable.begin(); it != _exitTable.end(); it++) { + entry = *it; + + if (entry.exitEnd == strideStop && entry.movieEnd <= exitStop) { + entry.exitEnd = entry.originalEnd; + *it = entry; + } + } + } + } +} + +HotspotInfoTable::Entry *Neighborhood::findHotspotEntry(const HotSpotID id) { + for (HotspotInfoTable::iterator it = _hotspotInfoTable.begin(); it != _hotspotInfoTable.end(); it++) + if (it->hotspot == id) + return &(*it); + + return 0; +} + +void Neighborhood::hideNav() { + _isRunning = _navMovie.isRunning(); + _navMovie.stop(); + _navMovie.hide(); + _turnPush.stopFader(); + _turnPush.hide(); +} + +void Neighborhood::showNav() { + _navMovie.show(); + _turnPush.hide(); + if (_isRunning) + _navMovie.start(); +} + +void Neighborhood::startExitMovie(const ExitTable::Entry &exitEntry) { + FaderMoveSpec compassMove; + + if (g_compass) + getExitCompassMove(exitEntry, compassMove); + + GameState.setNextRoom(exitEntry.exitRoom); + GameState.setNextDirection(exitEntry.exitDirection); + + if (exitEntry.movieEnd == exitEntry.exitEnd) // Just a walk. + startMovieSequence(exitEntry.movieStart, exitEntry.movieEnd, kMoveForwardCompletedFlag, kFilterNoInput, false); + else // We're stridin'! + startMovieSequence(exitEntry.movieStart, exitEntry.exitEnd, kStrideCompletedFlag, kFilterNoInput, false, exitEntry.movieEnd); + + if (g_compass) + g_compass->startFader(compassMove); +} + +void Neighborhood::startZoomMovie(const ZoomTable::Entry &zoomEntry) { + FaderMoveSpec compassMove; + + if (g_compass) + getZoomCompassMove(zoomEntry, compassMove); + + GameState.setNextRoom(zoomEntry.room); + GameState.setNextDirection(zoomEntry.direction); + + startMovieSequence(zoomEntry.movieStart, zoomEntry.movieEnd, kMoveForwardCompletedFlag, kFilterNoInput, false); + + if (g_compass) + g_compass->startFader(compassMove); +} + +void Neighborhood::startDoorOpenMovie(const TimeValue startTime, const TimeValue stopTime) { + startMovieSequence(startTime, stopTime, kDoorOpenCompletedFlag, kFilterNoInput, false); +} + +void Neighborhood::startTurnPush(const TurnDirection turnDirection, const TimeValue newView, const DirectionConstant nextDir) { + if (g_AIArea) + g_AIArea->lockAIOut(); + + _vm->_cursor->hide(); + + GameState.setNextDirection(nextDir); + + _interruptionFilter = kFilterNoInput; + _turnPush.stopFader(); + + // Set up callback. + _turnPushCallBack.setCallBackFlag(kTurnCompletedFlag); + _turnPushCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + + // Stop nav movie. + _navMovie.stop(); + _navMovie.setFlags(0); + + // Set segment of nav movie to whole movie, so that subsequent initFromMovieFrame + // will work. + _navMovie.setSegment(0, _navMovie.getDuration()); + + _pushIn.initFromMovieFrame(_navMovie.getMovie(), newView); + + _navMovie.hide(); + + switch (turnDirection) { + case kTurnLeft: + _turnPush.setSlideDirection(kSlideRightMask); + break; + case kTurnRight: + _turnPush.setSlideDirection(kSlideLeftMask); + break; + case kTurnUp: + _turnPush.setSlideDirection(kSlideDownMask); + break; + case kTurnDown: + _turnPush.setSlideDirection(kSlideUpMask); + break; + } + + _turnPush.show(); + + FaderMoveSpec moveSpec; + moveSpec.makeTwoKnotFaderSpec(60, 0, 0, 15, 1000); + _turnPush.startFader(moveSpec); + + if (g_compass) { + _turnPush.pauseFader(); + + int32 startAngle = getStaticCompassAngle(GameState.getCurrentRoom(), GameState.getCurrentDirection()); + int32 stopAngle = getStaticCompassAngle(GameState.getCurrentRoom(), nextDir); + + if (turnDirection == kTurnLeft) { + if (startAngle < stopAngle) + startAngle += 360; + } else { + if (stopAngle < startAngle) + stopAngle += 360; + } + + FaderMoveSpec turnSpec; + _turnPush.getCurrentFaderMove(turnSpec); + + FaderMoveSpec compassMove; + compassMove.makeTwoKnotFaderSpec(turnSpec.getFaderScale(), turnSpec.getNthKnotTime(0), startAngle, turnSpec.getNthKnotTime(1), stopAngle); + g_compass->startFader(compassMove); + } + + _turnPushCallBack.cancelCallBack(); + _turnPush.continueFader(); + + do { + _vm->checkCallBacks(); + _vm->refreshDisplay(); + _vm->_system->delayMillis(10); + } while (_turnPush.isFading()); + + _turnPush.stopFader(); + _neighborhoodNotification.setNotificationFlags(kTurnCompletedFlag, kTurnCompletedFlag); +} + +void Neighborhood::playExtraMovie(const ExtraTable::Entry &extraEntry, const NotificationFlags flags, const InputBits interruptionInput) { + FaderMoveSpec compassMove; + + if (g_compass) + getExtraCompassMove(extraEntry, compassMove); + + _lastExtra = extraEntry.extra; + _turnPush.hide(); + startMovieSequence(extraEntry.movieStart, extraEntry.movieEnd, flags, false, interruptionInput); + + if (g_compass) + g_compass->startFader(compassMove); +} + +void Neighborhood::activateCurrentView(const RoomID room, const DirectionConstant direction, SpotFlags flag) { + SpotTable::Entry entry; + findSpotEntry(room, direction, flag, entry); + + if (entry.dstFlags & flag) { + startSpotOnceOnly(entry.movieStart, entry.movieEnd); + } else { + findSpotEntry(room, direction, flag | kSpotLoopsMask, entry); + + if (entry.dstFlags & flag) + startSpotLoop(entry.movieStart, entry.movieEnd); + else + showViewFrame(getViewTime(room, direction)); + } +} + +void Neighborhood::activateOneHotspot(HotspotInfoTable::Entry &entry, Hotspot *hotspot) { + switch (_vm->getDragType()) { + case kDragInventoryUse: + if ((hotspot->getHotspotFlags() & kDropItemSpotFlag) != 0 && + _vm->getDraggingItem()->getObjectID() == entry.hotspotItem) + hotspot->setActive(); + break; + case kDragInventoryPickup: + case kDragBiochipPickup: + // Do nothing -- neighborhoods activate no hot spots in this case... + break; + default: + if ((hotspot->getHotspotFlags() & kPickUpBiochipSpotFlag) != 0) { + Item *item = _vm->getAllItems().findItemByID(entry.hotspotItem); + if (item && item->getItemNeighborhood() == getObjectID()) + hotspot->setActive(); + } else { + HotSpotFlags flags = hotspot->getHotspotFlags(); + + if ((flags & kNeighborhoodSpotFlag) != 0) { + if (flags & kOpenDoorSpotFlag) { + if (!GameState.isCurrentDoorOpen()) + hotspot->setActive(); + } else if ((flags & (kZoomSpotFlags | kClickSpotFlag | kPlayExtraSpotFlag)) != 0) { + hotspot->setActive(); + } else if ((flags & kPickUpItemSpotFlag) != 0) { + // Changed this 2/19/96 + // Should only light up this hot spot if the item's taken flag is not + // set. It's not based on neighborhood ID since that can be reset by the + // destroying process. + + if (!GameState.isTakenItemID(entry.hotspotItem)) + hotspot->setActive(); + } + } + } + break; + } +} + +void Neighborhood::startSpotOnceOnly(TimeValue startTime, TimeValue stopTime) { + _turnPush.hide(); + startMovieSequence(startTime, stopTime, kSpotCompletedFlag, kFilterNoInput, false); +} + +void Neighborhood::startMovieSequence(const TimeValue startTime, const TimeValue stopTime, NotificationFlags flags, bool loopSequence, + const InputBits interruptionInput, const TimeValue strideStop) { + if (!loopSequence && g_AIArea) + g_AIArea->lockAIOut(); + + _interruptionFilter = interruptionInput; + + // Stop the movie before doing anything else + _navMovie.stop(); + + Common::Rect pushBounds; + _turnPush.getBounds(pushBounds); + + _navMovie.moveElementTo(pushBounds.left, pushBounds.top); + _navMovie.show(); + _navMovie.setFlags(0); + _navMovie.setSegment(startTime, stopTime); + _navMovie.setTime(startTime); + + if (loopSequence) + _navMovie.setFlags(kLoopTimeBase); + else + flags |= kNeighborhoodMovieCompletedFlag; + + if (strideStop != 0xffffffff) + // Subtract a little slop from the striding stop time to keep from "pumping" at the + // end of a walk. + // 40 is one frame (scale == 600, 15 fps). + scheduleStridingCallBack(strideStop - kStridingSlop, flags); + else + scheduleNavCallBack(flags); + + _navMovie.start(); +} + +void Neighborhood::throwAwayInterface() { + _doorTable.clear(); + _exitTable.clear(); + _extraTable.clear(); + _hotspotInfoTable.clear(); + _spotTable.clear(); + _turnTable.clear(); + _viewTable.clear(); + _zoomTable.clear(); + + _navMovie.stopDisplaying(); + _navMovie.releaseMovie(); + _pushIn.deallocateSurface(); + _turnPush.stopDisplaying(); + _turnPush.setInAndOutElements(0, 0); + _turnPush.disposeAllCallBacks(); + + for (HotspotList::iterator it = _neighborhoodHotspots.begin(); it != _neighborhoodHotspots.end(); it++) + _vm->getAllHotspots().remove(*it); + + _neighborhoodHotspots.deleteHotspots(); + _spotSounds.disposeSound(); + _delayTimer.disposeAllCallBacks(); + + if (g_AIArea) { + g_AIArea->saveAIState(); + g_AIArea->removeAllRules(); + } + + if (_currentInteraction) + newInteraction(kNoInteractionID); + + _croppedMovie.releaseMovie(); + + loadLoopSound1(""); + loadLoopSound2(""); + + if (g_energyMonitor) { + g_energyMonitor->stopEnergyDraining(); + g_energyMonitor->saveCurrentEnergyValue(); + } + + delete g_interface; +} + +bool Neighborhood::prepareExtraSync(const ExtraID extraID) { + ExtraTable::Entry extraEntry; + FaderMoveSpec compassMove; + + if (g_compass) { + getExtraEntry(extraID, extraEntry); + getExtraCompassMove(extraEntry, compassMove); + } + + ExtraTable::Entry entry; + getExtraEntry(extraID, entry); + bool result; + + if (entry.movieStart != 0xffffffff) { + _turnPush.hide(); + + // Stop the movie before doing anything else + _navMovie.stop(); + + Common::Rect pushBounds; + _turnPush.getBounds(pushBounds); + _navMovie.moveElementTo(pushBounds.left, pushBounds.top); + + _navMovie.show(); + _navMovie.setFlags(0); + _navMovie.setSegment(entry.movieStart, entry.movieEnd); + _navMovie.setTime(entry.movieStart); + _navMovie.start(); + result = true; + } else { + result = false; + } + + if (result && g_compass) + g_compass->startFader(compassMove); + + return result; +} + +bool Neighborhood::waitMovieFinish(Movie *movie, const InputBits interruptionFilter) { + Input input; + bool result = true; + bool saveAllowed = _vm->swapSaveAllowed(false); + bool openAllowed = _vm->swapLoadAllowed(false); + + while (movie->isRunning()) { + InputDevice.getInput(input, interruptionFilter); + + if (input.anyInput() || _vm->shouldQuit()) { + result = false; + break; + } + + _vm->checkCallBacks(); + _vm->refreshDisplay(); + _vm->_system->delayMillis(10); + } + + movie->stop(); + _vm->swapSaveAllowed(saveAllowed); + _vm->swapLoadAllowed(openAllowed); + + return result; +} + +InputBits Neighborhood::getInputFilter() { + return _interruptionFilter & InputHandler::getInputFilter(); +} + +void Neighborhood::getZoomCompassMove(const ZoomTable::Entry &zoomEntry, FaderMoveSpec &compassMove) { + int32 startAngle = getStaticCompassAngle(GameState.getCurrentRoom(), GameState.getCurrentDirection()); + int32 stopAngle = getStaticCompassAngle(zoomEntry.room, zoomEntry.direction); + + if (startAngle > stopAngle) { + if (stopAngle + 180 < startAngle) + stopAngle += 360; + } else { + if (startAngle + 180 < stopAngle) + startAngle += 360; + } + + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), zoomEntry.movieStart, startAngle, zoomEntry.movieEnd, stopAngle); +} + +void Neighborhood::getExtraCompassMove(const ExtraTable::Entry &, FaderMoveSpec &compassMove) { + compassMove.makeOneKnotFaderSpec(g_compass->getFaderValue()); +} + +void Neighborhood::setUpAIRules() { + // Set up default rules here: + // -- Energy warning rules. + + if (g_AIArea) { + g_AIArea->forceAIUnlocked(); + + if (!_vm->isDemo() && (getObjectID() == kPrehistoricID || getObjectID() == kNoradAlphaID || + getObjectID() == kNoradDeltaID || getObjectID() == kMarsID || getObjectID() == kWSCID)) { + + AIEnergyMonitorCondition *condition50 = new AIEnergyMonitorCondition(kWorriedEnergy); + AIPlayMessageAction *message = new AIPlayMessageAction("Images/AI/Globals/XGLOB4A", false); + AIRule *rule50 = new AIRule(condition50, message); + + AIEnergyMonitorCondition *condition25 = new AIEnergyMonitorCondition(kNervousEnergy); + AICompoundAction *compound = new AICompoundAction(); + message = new AIPlayMessageAction("Images/AI/Globals/XGLOB4B", false); + compound->addAction(message); + AIDeactivateRuleAction *deactivate = new AIDeactivateRuleAction(rule50); + compound->addAction(deactivate); + AIRule *rule25 = new AIRule(condition25, compound); + + AIEnergyMonitorCondition *condition5 = new AIEnergyMonitorCondition(kPanicStrickenEnergy); + compound = new AICompoundAction(); + message = new AIPlayMessageAction("Images/AI/Globals/XGLOB4C", false); + compound->addAction(message); + deactivate = new AIDeactivateRuleAction(rule50); + compound->addAction(deactivate); + deactivate = new AIDeactivateRuleAction(rule25); + compound->addAction(deactivate); + AIRule *rule5 = new AIRule(condition5, compound); + + g_AIArea->addAIRule(rule5); + g_AIArea->addAIRule(rule25); + g_AIArea->addAIRule(rule50); + } + } +} + +GameInteraction *Neighborhood::makeInteraction(const InteractionID interactionID) { + if (interactionID == kNoInteractionID) + return 0; + + return new GameInteraction(interactionID, this); +} + +void Neighborhood::newInteraction(const InteractionID interactionID) { + GameInteraction *interaction = makeInteraction(interactionID); + _doneWithInteraction = false; + + if (_currentInteraction) { + _currentInteraction->stopInteraction(); + delete _currentInteraction; + } + + _currentInteraction = interaction; + + if (_currentInteraction) + _currentInteraction->startInteraction(); + + if (g_AIArea) + g_AIArea->checkMiddleArea(); +} + +void Neighborhood::bumpIntoWall() { + _vm->_gfx->shakeTheWorld(15, 30); +} + +void Neighborhood::zoomUpOrBump() { + Hotspot *zoomSpot = 0; + + for (HotspotList::iterator it = _vm->getAllHotspots().begin(); it != _vm->getAllHotspots().end(); it++) { + Hotspot *hotspot = *it; + + if ((hotspot->getHotspotFlags() & (kNeighborhoodSpotFlag | kZoomInSpotFlag)) == (kNeighborhoodSpotFlag | kZoomInSpotFlag)) { + HotspotInfoTable::Entry *entry = findHotspotEntry(hotspot->getObjectID()); + + if (entry && entry->hotspotRoom == GameState.getCurrentRoom() && entry->hotspotDirection == GameState.getCurrentDirection()) { + if (zoomSpot) { + zoomSpot = 0; + break; + } else { + zoomSpot = hotspot; + } + } + } + } + + if (zoomSpot) + zoomTo(zoomSpot); + else + bumpIntoWall(); +} + +void Neighborhood::loadLoopSound1(const Common::String &soundName, uint16 volume, TimeValue fadeOut, TimeValue fadeIn, TimeScale fadeScale) { + FaderMoveSpec faderMove; + + if (!loop1Loaded(soundName)) { + _loop1SoundString = soundName; + + if (_soundLoop1.isSoundLoaded()) { + faderMove.makeTwoKnotFaderSpec(fadeScale, 0, _loop1Fader.getFaderValue(), fadeOut, 0); + _loop1Fader.startFaderSync(faderMove); + } + + if (!_loop1SoundString.empty()) { + _soundLoop1.initFromAIFFFile(_loop1SoundString); + _soundLoop1.loopSound(); + _loop1Fader.setMasterVolume(_vm->getAmbienceLevel()); + _loop1Fader.setFaderValue(0); + faderMove.makeTwoKnotFaderSpec(fadeScale, 0, 0, fadeIn, volume); + _loop1Fader.startFaderSync(faderMove); + } else { + _soundLoop1.disposeSound(); + } + } else if (_loop1Fader.getFaderValue() != volume) { + faderMove.makeTwoKnotFaderSpec(fadeScale, 0, _loop1Fader.getFaderValue(), fadeIn, volume); + _loop1Fader.startFaderSync(faderMove); + } +} + +void Neighborhood::loadLoopSound2(const Common::String &soundName, uint16 volume, TimeValue fadeOut, TimeValue fadeIn, TimeScale fadeScale) { + FaderMoveSpec faderMove; + + if (!loop2Loaded(soundName)) { + _loop2SoundString = soundName; + + if (_soundLoop2.isSoundLoaded()) { + faderMove.makeTwoKnotFaderSpec(fadeScale, 0, _loop2Fader.getFaderValue(), fadeOut, 0); + _loop2Fader.startFaderSync(faderMove); + } + + if (!_loop2SoundString.empty()) { + _soundLoop2.initFromAIFFFile(_loop2SoundString); + _soundLoop2.loopSound(); + _loop2Fader.setMasterVolume(_vm->getAmbienceLevel()); + _loop2Fader.setFaderValue(0); + faderMove.makeTwoKnotFaderSpec(fadeScale, 0, 0, fadeIn, volume); + _loop2Fader.startFaderSync(faderMove); + } else { + _soundLoop2.disposeSound(); + } + } else if (_loop2Fader.getFaderValue() != volume) { + faderMove.makeTwoKnotFaderSpec(fadeScale, 0, _loop2Fader.getFaderValue(), fadeIn, volume); + _loop2Fader.startFaderSync(faderMove); + } +} + +void Neighborhood::takeItemFromRoom(Item *item) { + item->setItemRoom(kNoNeighborhoodID, kNoRoomID, kNoDirection); + // Also set the taken item flag. Do this before updating the view frame. + GameState.setTakenItem(item, true); + updateViewFrame(); +} + +void Neighborhood::dropItemIntoRoom(Item *item, Hotspot *) { + item->setItemRoom(getObjectID(), GameState.getCurrentRoom(), GameState.getCurrentDirection()); + // Also set the taken item flag. Do this before updating the view frame. + GameState.setTakenItem(item, false); + updateViewFrame(); +} + +void Neighborhood::makeContinuePoint() { + _vm->makeContinuePoint(); +} + +void Neighborhood::startLoop1Fader(const FaderMoveSpec &faderMove) { + _loop1Fader.startFader(faderMove); +} + +void Neighborhood::startLoop2Fader(const FaderMoveSpec &faderMove) { + _loop2Fader.startFader(faderMove); +} + +// *** Revised 6/13/96 to use the last frame of the extra sequence. +// Necessary for Cinepak buildup. +void Neighborhood::showExtraView(uint32 extraID) { + ExtraTable::Entry entry; + getExtraEntry(extraID, entry); + + if (entry.movieEnd != 0xffffffff) + showViewFrame(entry.movieEnd - 1); +} + +void Neighborhood::startExtraLongSequence(const uint32 firstExtra, const uint32 lastExtra, NotificationFlags flags, + const InputBits interruptionFilter) { + ExtraTable::Entry firstEntry, lastEntry; + getExtraEntry(firstExtra, firstEntry); + + if (firstEntry.movieStart != 0xffffffff) { + getExtraEntry(lastExtra, lastEntry); + _lastExtra = firstExtra; + _turnPush.hide(); + startMovieSequence(firstEntry.movieStart, lastEntry.movieEnd, flags, kFilterNoInput, interruptionFilter); + } +} + +void Neighborhood::openCroppedMovie(const Common::String &movieName, CoordType left, CoordType top) { + if (_croppedMovie.isMovieValid()) + closeCroppedMovie(); + + _croppedMovie.initFromMovieFile(movieName); + _croppedMovie.moveElementTo(left, top); + _croppedMovie.startDisplaying(); + _croppedMovie.show(); +} + +void Neighborhood::loopCroppedMovie(const Common::String &movieName, CoordType left, CoordType top) { + openCroppedMovie(movieName, left, top); + _croppedMovie.redrawMovieWorld(); + _croppedMovie.setFlags(kLoopTimeBase); + _croppedMovie.start(); +} + +void Neighborhood::closeCroppedMovie() { + _croppedMovie.releaseMovie(); +} + +void Neighborhood::playCroppedMovieOnce(const Common::String &movieName, CoordType left, CoordType top, const InputBits interruptionFilter) { + openCroppedMovie(movieName, left, top); + _croppedMovie.redrawMovieWorld(); + _croppedMovie.start(); + + InputBits oldInterruptionFilter = _interruptionFilter; + if (oldInterruptionFilter != kFilterNoInput) + _interruptionFilter = kFilterNoInput; + + bool saveAllowed = _vm->swapSaveAllowed(false); + bool openAllowed = _vm->swapLoadAllowed(false); + + Input input; + while (_croppedMovie.isRunning() && !_vm->shouldQuit()) { + _vm->processShell(); + InputDevice.getInput(input, interruptionFilter); + if (input.anyInput() || _vm->saveRequested() || _vm->loadRequested() || _vm->shouldQuit()) + break; + _vm->_system->delayMillis(10); + } + + if (oldInterruptionFilter != kFilterNoInput) + _interruptionFilter = oldInterruptionFilter; + + closeCroppedMovie(); + _vm->swapSaveAllowed(saveAllowed); + _vm->swapLoadAllowed(openAllowed); +} + +void Neighborhood::playMovieSegment(Movie *movie, TimeValue startTime, TimeValue stopTime) { + TimeValue oldStart, oldStop; + movie->getSegment(oldStart, oldStop); + + if (stopTime == 0xffffffff) + stopTime = movie->getDuration(); + + movie->setSegment(startTime, stopTime); + movie->setTime(startTime); + movie->start(); + + while (movie->isRunning()) { + _vm->checkCallBacks(); + _vm->refreshDisplay(); + _vm->_system->delayMillis(10); + } + + movie->stop(); + movie->setSegment(oldStart, oldStop); +} + +void Neighborhood::recallToTSASuccess() { + if (GameState.allTimeZonesFinished()) + _vm->jumpToNewEnvironment(kFullTSAID, kTSA37, kNorth); + else + _vm->jumpToNewEnvironment(kTinyTSAID, kTinyTSA37, kNorth); +} + +void Neighborhood::recallToTSAFailure() { + _vm->jumpToNewEnvironment(kTinyTSAID, kTinyTSA37, kNorth); +} + +void Neighborhood::handleInput(const Input &input, const Hotspot *cursorSpot) { + if (_vm->getGameMode() == kModeNavigation) { + if (input.upButtonAnyDown()) + upButton(input); + else if (input.downButtonAnyDown()) + downButton(input); + else if (input.leftButtonAnyDown()) + leftButton(input); + else if (input.rightButtonAnyDown()) + rightButton(input); + } + + InputHandler::handleInput(input, cursorSpot); +} + +void Neighborhood::setHotspotFlags(const HotSpotID id, const HotSpotFlags flags) { + Hotspot *hotspot = _vm->getAllHotspots().findHotspotByID(id); + hotspot->setMaskedHotspotFlags(flags, flags); +} + +void Neighborhood::setIsItemTaken(const ItemID id) { + GameState.setTakenItemID(id, _vm->playerHasItemID(id)); +} + +void Neighborhood::upButton(const Input &) { + moveForward(); +} + +void Neighborhood::leftButton(const Input &) { + turnLeft(); +} + +void Neighborhood::rightButton(const Input &) { + turnRight(); +} + +void Neighborhood::downButton(const Input &) { + if (_inputHandler->wantsCursor()) { + _vm->getAllHotspots().deactivateAllHotspots(); + _inputHandler->activateHotspots(); + + for (HotspotList::iterator it = _vm->getAllHotspots().begin(); it != _vm->getAllHotspots().end(); it++) { + Hotspot *hotspot = *it; + + if (hotspot->isSpotActive() && (hotspot->getHotspotFlags() & (kNeighborhoodSpotFlag | kZoomOutSpotFlag)) == (kNeighborhoodSpotFlag | kZoomOutSpotFlag)) { + HotspotInfoTable::Entry *entry = findHotspotEntry(hotspot->getObjectID()); + + if (entry && entry->hotspotRoom == GameState.getCurrentRoom() && entry->hotspotDirection == GameState.getCurrentDirection()) { + Input scratch; + _inputHandler->clickInHotspot(scratch, hotspot); + return; + } + } + } + } +} + +void Neighborhood::initOnePicture(Picture *picture, const Common::String &pictureName, DisplayOrder order, CoordType left, CoordType top, bool show) { + picture->initFromPICTFile(pictureName); + picture->setDisplayOrder(order); + picture->moveElementTo(left, top); + picture->startDisplaying(); + if (show) + picture->show(); +} + +void Neighborhood::initOneMovie(Movie *movie, const Common::String &movieName, DisplayOrder order, CoordType left, CoordType top, bool show) { + movie->initFromMovieFile(movieName); + movie->setDisplayOrder(order); + movie->moveElementTo(left, top); + movie->startDisplaying(); + + if (show) + movie->show(); + + movie->redrawMovieWorld(); +} + +void Neighborhood::reinstateMonocleInterface() { + _vm->_gfx->disableErase(); + + _vm->createInterface(); + + if (g_AIArea) + setNextHandler(g_AIArea); + + init(); + + moveNavTo(kNavAreaLeft, kNavAreaTop); + + if (g_interface) + g_interface->setDate(getDateResID()); + + if (g_AIArea) + g_AIArea->restoreAIState(); +} + +void Neighborhood::useIdleTime() { + if (_doneWithInteraction) { + newInteraction(kNoInteractionID); + loadAmbientLoops(); + } +} + +void Neighborhood::timerFunction() { + timerExpired(getTimerEvent()); +} + +void Neighborhood::scheduleEvent(const TimeValue time, const TimeScale scale, const uint32 eventType) { + _eventTimer.stopFuse(); + _eventTimer.primeFuse(time, scale); + _timerEvent = eventType; + _eventTimer.setFunctor(new Common::Functor0Mem<void, Neighborhood>(this, &Neighborhood::timerFunction)); + _eventTimer.lightFuse(); +} + +void Neighborhood::cancelEvent() { + _eventTimer.stopFuse(); +} + +void Neighborhood::pauseTimer() { + _eventTimer.pauseFuse(); +} + +void Neighborhood::resumeTimer() { + // NOTE: The original calls pauseFuse() here, which causes a bug with the robot + // in WSC on the catwalk, causing him never to come after you if you don't act + // against him. + _eventTimer.resumeFuse(); +} + +bool Neighborhood::timerPaused() { + return _eventTimer.isFusePaused(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/neighborhood.h b/engines/pegasus/neighborhood/neighborhood.h new file mode 100644 index 0000000000..3c1c5eac92 --- /dev/null +++ b/engines/pegasus/neighborhood/neighborhood.h @@ -0,0 +1,408 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_NEIGHBORHOOD_H +#define PEGASUS_NEIGHBORHOOD_H + +#include "common/queue.h" +#include "common/str.h" + +#include "pegasus/fader.h" +#include "pegasus/hotspot.h" +#include "pegasus/input.h" +#include "pegasus/movie.h" +#include "pegasus/notification.h" +#include "pegasus/sound.h" +#include "pegasus/timers.h" +#include "pegasus/transition.h" +#include "pegasus/util.h" +#include "pegasus/neighborhood/door.h" +#include "pegasus/neighborhood/exit.h" +#include "pegasus/neighborhood/extra.h" +#include "pegasus/neighborhood/hotspotinfo.h" +#include "pegasus/neighborhood/spot.h" +#include "pegasus/neighborhood/turn.h" +#include "pegasus/neighborhood/view.h" +#include "pegasus/neighborhood/zoom.h" + +namespace Pegasus { + +class PegasusEngine; + +// Pegasus Prime neighborhood id's +static const NeighborhoodID kCaldoriaID = 0; +static const NeighborhoodID kFullTSAID = 1; +static const NeighborhoodID kFinalTSAID = 2; +static const NeighborhoodID kTinyTSAID = 3; +static const NeighborhoodID kPrehistoricID = 4; +static const NeighborhoodID kMarsID = 5; +static const NeighborhoodID kWSCID = 6; +static const NeighborhoodID kNoradAlphaID = 7; +static const NeighborhoodID kNoradDeltaID = 8; +// The sub chase is not really a neighborhood, but we define a constant that is used +// to allow an easy transition out of Norad Alpha. +static const NeighborhoodID kNoradSubChaseID = 1000; + +static const TimeScale kDefaultLoopFadeScale = kThirtyTicksPerSecond; +static const TimeValue kDefaultLoopFadeOut = kHalfSecondPerThirtyTicks; +static const TimeValue kDefaultLoopFadeIn = kHalfSecondPerThirtyTicks; + +enum QueueRequestType { + kNavExtraRequest, + kSpotSoundRequest, + kDelayRequest +}; + +// For delay requests, start is interpreted as the total delay and stop is interpreted +// as the scale the delay is in. +// For extra requests, start and stop are not used. +struct QueueRequest { + QueueRequestType requestType; + ExtraID extra; + TimeValue start, stop; + InputBits interruptionFilter; + bool playing; + NotificationFlags flags; + Notification *notification; +}; + +bool operator==(const QueueRequest &arg1, const QueueRequest &arg2); +bool operator!=(const QueueRequest &arg1, const QueueRequest &arg2); + +class GameInteraction; +class Item; +class Neighborhood; + +class StriderCallBack : public TimeBaseCallBack { +public: + StriderCallBack(Neighborhood *); + virtual ~StriderCallBack() {} + +protected: + virtual void callBack(); + + Neighborhood *_neighborhood; +}; + +typedef Common::Queue<QueueRequest> NeighborhoodActionQueue; + +class Neighborhood : public IDObject, public NotificationReceiver, public InputHandler, public Idler { +friend class StriderCallBack; + +public: + Neighborhood(InputHandler *nextHandler, PegasusEngine *vm, const Common::String &resName, NeighborhoodID id); + virtual ~Neighborhood(); + + virtual void init(); + virtual void start(); + virtual void moveNavTo(const CoordType, const CoordType); + virtual void checkContinuePoint(const RoomID, const DirectionConstant) = 0; + void makeContinuePoint(); + + virtual void activateHotspots(); + virtual void clickInHotspot(const Input &, const Hotspot *); + + virtual CanMoveForwardReason canMoveForward(ExitTable::Entry &entry); + virtual CanTurnReason canTurn(TurnDirection turn, DirectionConstant &nextDir); + virtual CanOpenDoorReason canOpenDoor(DoorTable::Entry &entry); + + virtual void cantMoveThatWay(CanMoveForwardReason); + virtual void cantTurnThatWay(CanTurnReason) {} + virtual void cantOpenDoor(CanOpenDoorReason); + virtual void arriveAt(const RoomID room, const DirectionConstant direction); + virtual void turnTo(const DirectionConstant); + virtual void spotCompleted(); + virtual void doorOpened(); + virtual void closeDoorOffScreen(const RoomID, const DirectionConstant) {} + + virtual void moveForward(); + virtual void turn(const TurnDirection); + virtual void turnLeft(); + virtual void turnRight(); + virtual void turnUp(); + virtual void turnDown(); + virtual void openDoor(); + virtual void zoomTo(const Hotspot *); + + virtual void updateViewFrame(); + + void requestExtraSequence(const ExtraID, const NotificationFlags, const InputBits interruptionFilter); + void requestSpotSound(const TimeValue, const TimeValue, const InputBits interruptionFilter, const NotificationFlags); + void playSpotSoundSync(const TimeValue in, const TimeValue out); + void requestDelay(const TimeValue, const TimeScale, const InputBits interruptionFilter, const NotificationFlags); + + Notification *getNeighborhoodNotification() { return &_neighborhoodNotification; } + + virtual void getExtraEntry(const uint32 id, ExtraTable::Entry &extraEntry); + virtual void startSpotLoop(TimeValue, TimeValue, NotificationFlags = 0); + virtual bool actionQueueEmpty() { return _actionQueue.empty(); } + virtual void showViewFrame(TimeValue); + virtual void findSpotEntry(const RoomID room, const DirectionConstant direction, SpotFlags flags, SpotTable::Entry &spotEntry); + virtual void startExtraSequence(const ExtraID, const NotificationFlags, const InputBits interruptionFilter); + bool startExtraSequenceSync(const ExtraID, const InputBits); + virtual void loopExtraSequence(const uint32, NotificationFlags = 0); + int32 getLastExtra() const { return _lastExtra; } + virtual void scheduleNavCallBack(NotificationFlags); + + Movie *getNavMovie() { return &_navMovie; } + bool navMoviePlaying(); + + void setCurrentAlternate(const AlternateID alt) { _currentAlternate = alt; } + AlternateID getCurrentAlternate() const { return _currentAlternate; } + + void setCurrentActivation(const HotSpotActivationID a) { _currentActivation = a; } + HotSpotActivationID getCurrentActivation() { return _currentActivation; } + + virtual void playDeathExtra(ExtraID, DeathReason); + virtual void die(const DeathReason); + + virtual void setSoundFXLevel(const uint16); + virtual void setAmbienceLevel(const uint16); + + void forceStridingStop(const RoomID, const DirectionConstant, const AlternateID); + void restoreStriding(const RoomID, const DirectionConstant, const AlternateID); + + HotspotInfoTable::Entry *findHotspotEntry(const HotSpotID); + + Push *getTurnPush() { return &_turnPush; } + Picture *getTurnPushPicture() { return &_pushIn; } + + void hideNav(); + void showNav(); + + virtual void loadAmbientLoops() {} + + virtual void flushGameState() {} + + virtual Common::String getBriefingMovie(); + virtual Common::String getEnvScanMovie(); + virtual uint getNumHints(); + virtual Common::String getHintMovie(uint); + virtual bool canSolve(); + virtual void prepareForAIHint(const Common::String &) {} + virtual void cleanUpAfterAIHint(const Common::String &) {} + virtual void doSolve(); + + virtual bool okayToJump(); + + virtual AirQuality getAirQuality(const RoomID); + virtual void checkAirMask() {} + virtual void checkFlashlight() {} + virtual void shieldOn() {} + virtual void shieldOff() {} + + virtual void loadLoopSound1(const Common::String &, const uint16 volume = 0x100, + const TimeValue fadeOut = kDefaultLoopFadeOut, const TimeValue fadeIn = kDefaultLoopFadeIn, + const TimeScale fadeScale = kDefaultLoopFadeScale); + virtual void loadLoopSound2(const Common::String &, const uint16 volume = 0x100, + const TimeValue fadeOut = kDefaultLoopFadeOut, const TimeValue fadeIn = kDefaultLoopFadeIn, + const TimeScale fadeScale = kDefaultLoopFadeScale); + bool loop1Loaded(const Common::String &soundName) { return _loop1SoundString == soundName; } + bool loop2Loaded(const Common::String &soundName) { return _loop2SoundString == soundName; } + void startLoop1Fader(const FaderMoveSpec &); + void startLoop2Fader(const FaderMoveSpec &); + + virtual void takeItemFromRoom(Item *); + virtual void dropItemIntoRoom(Item *, Hotspot *); + virtual Hotspot *getItemScreenSpot(Item *, DisplayElement *) { return 0; } + + virtual GameInteraction *makeInteraction(const InteractionID); + virtual void requestDeleteCurrentInteraction() { _doneWithInteraction = true; } + + virtual uint16 getDateResID() const = 0; + + virtual void showExtraView(uint32); + virtual void startExtraLongSequence(const uint32, const uint32, NotificationFlags, const InputBits interruptionFilter); + + void openCroppedMovie(const Common::String &, CoordType, CoordType); + void loopCroppedMovie(const Common::String &, CoordType, CoordType); + void closeCroppedMovie(); + void playCroppedMovieOnce(const Common::String &, CoordType, CoordType, const InputBits interruptionFilter = kFilterNoInput); + + void playMovieSegment(Movie *, TimeValue = 0, TimeValue = 0xffffffff); + + virtual void recallToTSASuccess(); + virtual void recallToTSAFailure(); + + virtual void pickedUpItem(Item *) {} + + virtual void handleInput(const Input &, const Hotspot *); +protected: + PegasusEngine *_vm; + Common::String _resName; + + virtual Common::String getSoundSpotsName() = 0; + virtual Common::String getNavMovieName() = 0; + + // Notification function. + virtual void receiveNotification(Notification *, const NotificationFlags); + + // Map info functions. + virtual void getExitEntry(const RoomID room, const DirectionConstant direction, ExitTable::Entry &entry); + virtual TimeValue getViewTime(const RoomID room, const DirectionConstant direction); + virtual void getDoorEntry(const RoomID room, const DirectionConstant direction, DoorTable::Entry &doorEntry); + virtual DirectionConstant getTurnEntry(const RoomID room, const DirectionConstant direction, const TurnDirection turn); + virtual void getZoomEntry(const HotSpotID id, ZoomTable::Entry &zoomEntry); + virtual void getHotspotEntry(const HotSpotID id, HotspotInfoTable::Entry &hotspotEntry); + + // Nav movie sequences. + virtual void startExitMovie(const ExitTable::Entry &); + virtual void keepStriding(ExitTable::Entry &); + virtual void stopStriding(); + virtual void checkStriding(); + virtual bool stillMoveForward(); + virtual void scheduleStridingCallBack(const TimeValue, NotificationFlags flags); + virtual void startZoomMovie(const ZoomTable::Entry &); + virtual void startDoorOpenMovie(const TimeValue, const TimeValue); + virtual void startTurnPush(const TurnDirection, const TimeValue, const DirectionConstant); + virtual void playExtraMovie(const ExtraTable::Entry &, const NotificationFlags, const InputBits interruptionFilter); + + virtual void activateCurrentView(const RoomID, const DirectionConstant, SpotFlags); + + virtual void activateOneHotspot(HotspotInfoTable::Entry &, Hotspot *); + + virtual void startSpotOnceOnly(TimeValue, TimeValue); + + virtual void startMovieSequence(const TimeValue, const TimeValue, NotificationFlags, + bool loopSequence, const InputBits interruptionFilter, const TimeValue strideStop = 0xffffffff); + + virtual void createNeighborhoodSpots(); + + void resetLastExtra() { _lastExtra = -1; } + + virtual void throwAwayInterface(); + + // Action queue stuff + void popActionQueue(); + void serviceActionQueue(); + void requestAction(const QueueRequestType, const ExtraID, const TimeValue, const TimeValue, const InputBits, const NotificationFlags); + + virtual bool prepareExtraSync(const ExtraID); + virtual bool waitMovieFinish(Movie *, const InputBits); + + virtual InputBits getInputFilter(); + + // Misc. + virtual int16 getStaticCompassAngle(const RoomID, const DirectionConstant dir); + virtual void getExitCompassMove(const ExitTable::Entry &, FaderMoveSpec &); + virtual void getZoomCompassMove(const ZoomTable::Entry &, FaderMoveSpec&); + virtual void getExtraCompassMove(const ExtraTable::Entry &, FaderMoveSpec&); + + virtual void setUpAIRules(); + virtual void setHotspotFlags(const HotSpotID, const HotSpotFlags); + virtual void setIsItemTaken(const ItemID); + + virtual void upButton(const Input &); + virtual void leftButton(const Input &); + virtual void rightButton(const Input &); + virtual void downButton(const Input &); + + void initOnePicture(Picture *, const Common::String &, DisplayOrder, CoordType, CoordType, bool); + void initOneMovie(Movie *, const Common::String &, DisplayOrder, CoordType, CoordType, bool); + + void reinstateMonocleInterface(); + + virtual void newInteraction(const InteractionID); + virtual void useIdleTime(); + virtual void bumpIntoWall(); + virtual void zoomUpOrBump(); + + void scheduleEvent(const TimeValue, const TimeScale, const uint32); + void cancelEvent(); + virtual void timerExpired(const uint32) {} + bool isEventTimerRunning() { return _eventTimer.isFuseLit(); } + uint32 getTimerEvent() { return _timerEvent; } + void timerFunction(); + + void pauseTimer(); + void resumeTimer(); + bool timerPaused(); + + // Navigation Data + DoorTable _doorTable; + ExitTable _exitTable; + ExtraTable _extraTable; + HotspotInfoTable _hotspotInfoTable; + SpotTable _spotTable; + TurnTable _turnTable; + ViewTable _viewTable; + ZoomTable _zoomTable; + AlternateID _currentAlternate; + HotSpotActivationID _currentActivation; + + int32 _lastExtra; + DeathReason _extraDeathReason; + + // Graphics + Movie _navMovie; + Picture _pushIn; + Push _turnPush; + + // Callbacks + Notification _neighborhoodNotification; + NotificationCallBack _navMovieCallBack; + StriderCallBack _stridingCallBack; + NotificationCallBack _turnPushCallBack; + NotificationCallBack _spotSoundCallBack; + NotificationCallBack _delayCallBack; + + // Hotspots + HotspotList _neighborhoodHotspots; + + // Sounds + SoundTimeBase _spotSounds; + + // Action queue + NeighborhoodActionQueue _actionQueue; + TimeBase _delayTimer; + + // Interruptibility... + InputBits _interruptionFilter; + + // Nav hiding (for info support...) + bool _isRunning; + + GameInteraction *_currentInteraction; + bool _doneWithInteraction; + Movie _croppedMovie; + + Sound _soundLoop1; + Common::String _loop1SoundString; + SoundFader _loop1Fader; + + Sound _soundLoop2; + Common::String _loop2SoundString; + SoundFader _loop2Fader; + + // The event timer... + FuseFunction _eventTimer; + uint32 _timerEvent; +}; + +extern Neighborhood *g_neighborhood; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/norad/alpha/ecrmonitor.cpp b/engines/pegasus/neighborhood/norad/alpha/ecrmonitor.cpp new file mode 100644 index 0000000000..e2a0267231 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/alpha/ecrmonitor.cpp @@ -0,0 +1,219 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/pegasus.h" +#include "pegasus/neighborhood/norad/constants.h" +#include "pegasus/neighborhood/norad/norad.h" +#include "pegasus/neighborhood/norad/alpha/ecrmonitor.h" + +namespace Pegasus { + +static const NotificationFlags kECRSection1FinishedFlag = 1; +static const NotificationFlags kECRPanFinishedFlag = kECRSection1FinishedFlag << 1; +static const NotificationFlags kECRSection2FinishedFlag = kECRPanFinishedFlag << 1; +static const NotificationFlags kECRNotificationFlags = kECRSection1FinishedFlag | + kECRPanFinishedFlag | + kECRSection2FinishedFlag; + +static const TimeValue kSection1Start = 0; +static const TimeValue kSection1Stop = 25; +static const TimeValue kPanStart = 0; +static const TimeValue kPanStop = 20; +static const TimeValue kSection2Start = 26; +static const TimeValue kSection2Stop = 1000; + +// Seems to be a good value for a 20 second pan. +static const CoordType kPanPixelsPerFrame = 8; + +// Interesting times are in seconds. +static const TimeValue s_ECRInterestingTimes[] = { + 0, 1, 2, 10, 25, 26, 56, 64, 72, 80, 88, 94, 102, 108, 116, 999 +}; + +// Index into s_ECRInterestingTimes of interesting time before security pan. +static const int kBeforePanTime = 3; + +// Index into s_ECRInterestingTimes of interesting time after security pan. +static const int kAfterPanTime = 5; + +NoradAlphaECRMonitor::NoradAlphaECRMonitor(Neighborhood *nextHandler) : GameInteraction(kNoradECRMonitorInteractionID, nextHandler), + _ecrSlideShowNotification(kNoradECRNotificationID, (PegasusEngine *)g_engine), _ecrMovie(kECRSlideShowMovieID), + _ecrPan(kECRPanID) { +} + +void NoradAlphaECRMonitor::receiveNotification(Notification *, const NotificationFlags flags) { + if (flags & kECRSection1FinishedFlag) + ecrSection1Finished(); + else if (flags & kECRPanFinishedFlag) + ecrPanFinished(); + else if (flags & kECRSection2FinishedFlag) + ecrSection2Finished(); +} + +int NoradAlphaECRMonitor::findCurrentInterestingTime() { + TimeValue time = _ecrMovie.getTime(); + TimeScale scale = _ecrMovie.getScale(); + + for (int i = ARRAYSIZE(s_ECRInterestingTimes) - 1; i >= 0; i--) + if (time >= s_ECRInterestingTimes[i] * scale) + return i; + + return 0; +} + +void NoradAlphaECRMonitor::skipToNextInterestingTime() { + if (_ecrMovie.isRunning()) { + int interestingTime = findCurrentInterestingTime(); + _ecrMovie.setTime(s_ECRInterestingTimes[interestingTime + 1] * _ecrMovie.getScale()); + _ecrMovie.redrawMovieWorld(); + } else if (_ecrPan.isRunning()) { + _ecrPanCallBack.cancelCallBack(); + ecrPanFinished(); + } +} + +void NoradAlphaECRMonitor::skipToPreviousInterestingTime() { + if (_ecrPan.isRunning()) { + _ecrPan.stop(); + _ecrPan.stopDisplaying(); + _ecrPanCallBack.cancelCallBack(); + + _ecrMovieCallBack.setCallBackFlag(kECRSection1FinishedFlag); + _ecrMovieCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + + TimeScale scale = _ecrMovie.getScale(); + _ecrMovie.setSegment(kSection1Start * scale, kSection1Stop * scale + 1); + _ecrMovie.setTime(s_ECRInterestingTimes[kBeforePanTime] * scale); + _ecrMovie.start(); + } else { + int interestingTime = findCurrentInterestingTime(); + + if (interestingTime == kAfterPanTime) { + _ecrMovieCallBack.cancelCallBack(); + TimeScale scale = _ecrMovie.getScale(); + _ecrMovie.setSegment(kSection1Start * scale, kSection1Stop * scale + 1); + _ecrMovie.setTime(kSection1Stop * scale); + ecrSection1Finished(); + } else if (interestingTime == 0) { + _ecrMovie.setTime(kSection1Start * _ecrMovie.getScale()); + _ecrMovie.redrawMovieWorld(); + } else { + _ecrMovie.setTime(s_ECRInterestingTimes[interestingTime - 1] * _ecrMovie.getScale()); + _ecrMovie.redrawMovieWorld(); + } + } +} + +void NoradAlphaECRMonitor::handleInput(const Input &input, const Hotspot *cursorSpot) { + if (isInteracting()) { + if (input.rightButtonDown()) + skipToNextInterestingTime(); + else if (input.leftButtonDown()) + skipToPreviousInterestingTime(); + else + InputHandler::handleInput(input, cursorSpot); + } else { + InputHandler::handleInput(input, cursorSpot); + } +} + +void NoradAlphaECRMonitor::ecrSection1Finished() { + _ecrMovie.stop(); + _ecrPanCallBack.setNotification(&_ecrSlideShowNotification); + _ecrPanCallBack.initCallBack(&_ecrPan, kCallBackAtExtremes); + _ecrPanCallBack.setCallBackFlag(kECRPanFinishedFlag); + _ecrSlideShowNotification.notifyMe(this, kECRNotificationFlags, kECRNotificationFlags); + _ecrPanCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + _ecrPan.startDisplaying(); + _ecrPan.show(); + + TimeScale scale = _ecrPan.getScale(); + _ecrPan.setSegment(kPanStart * scale, kPanStop * scale); + _ecrPan.setTime(0); + _ecrPan.start(); +} + +void NoradAlphaECRMonitor::ecrPanFinished() { + _ecrPan.stop(); + _ecrPan.stopDisplaying(); + _ecrMovieCallBack.setCallBackFlag(kECRSection2FinishedFlag); + _ecrMovieCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + + TimeScale scale = _ecrMovie.getScale(); + _ecrMovie.setSegment(kSection2Start * scale, kSection2Stop * scale); + _ecrMovie.start(); +} + +void NoradAlphaECRMonitor::ecrSection2Finished() { + _ecrMovie.stop(); + _ecrMovie.stopDisplaying(); +} + +void NoradAlphaECRMonitor::openInteraction() { + // Initialize the security pan. + _ecrPan.initFromMovieFile("Images/Norad Alpha/Security Pan.pano"); + _ecrPan.initMaskFromPICTFile("Images/Norad Alpha/Security Pan Mask"); + _ecrPan.setBounds(Common::Rect(kECRPanLeft, kECRPanTop, kECRPanRight, kECRPanBottom)); + _ecrPan.setDisplayOrder(kECRPanOrder); + _ecrPan.setScale(15); // 15 fps. + + // Begin the lame ECR slide show. + // clone2727: I didn't say it :P + _ecrMovie.initFromMovieFile("Images/Norad Alpha/ECR Monitor Movie"); + + _ecrMovieCallBack.setNotification(&_ecrSlideShowNotification); + _ecrMovieCallBack.initCallBack(&_ecrMovie, kCallBackAtExtremes); + _ecrMovieCallBack.setCallBackFlag(kECRSection1FinishedFlag); + + _ecrSlideShowNotification.notifyMe(this, kECRNotificationFlags, kECRNotificationFlags); + _ecrMovieCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + + _ecrMovie.moveElementTo(kECRSlideShowLeft, kECRSlideShowTop); + _ecrMovie.setDisplayOrder(kECRMonitorOrder); + _ecrMovie.startDisplaying(); + _ecrMovie.show(); + _ecrMovie.redrawMovieWorld(); + + TimeScale scale = _ecrMovie.getScale(); + _ecrMovie.setSegment(kSection1Start * scale, kSection1Stop * scale + 1); + + _ecrMovie.start(); +} + +void NoradAlphaECRMonitor::closeInteraction() { + _ecrMovieCallBack.releaseCallBack(); + _ecrMovie.stop(); + _ecrMovie.stopDisplaying(); + _ecrMovie.releaseMovie(); + _ecrMovieCallBack.releaseCallBack(); + + _ecrPanCallBack.releaseCallBack(); + _ecrPan.stop(); + _ecrPan.stopDisplaying(); + _ecrPan.releasePanorama(); + _ecrPanCallBack.releaseCallBack(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/norad/alpha/ecrmonitor.h b/engines/pegasus/neighborhood/norad/alpha/ecrmonitor.h new file mode 100644 index 0000000000..9e286ed337 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/alpha/ecrmonitor.h @@ -0,0 +1,65 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_ecrMONITOR_H +#define PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_ecrMONITOR_H + +#include "pegasus/interaction.h" +#include "pegasus/notification.h" +#include "pegasus/neighborhood/norad/alpha/panoramascroll.h" + +namespace Pegasus { + +class NoradAlphaECRMonitor : public GameInteraction, public NotificationReceiver { +public: + NoradAlphaECRMonitor(Neighborhood *); + virtual ~NoradAlphaECRMonitor() {} + + virtual void handleInput(const Input &, const Hotspot *); + +protected: + virtual void openInteraction(); + virtual void closeInteraction(); + + virtual void receiveNotification(Notification *, const NotificationFlags); + + void ecrSection1Finished(); + void ecrPanFinished(); + void ecrSection2Finished(); + + int findCurrentInterestingTime(); + void skipToNextInterestingTime(); + void skipToPreviousInterestingTime(); + + Notification _ecrSlideShowNotification; + Movie _ecrMovie; + NotificationCallBack _ecrMovieCallBack; + PanoramaScroll _ecrPan; + NotificationCallBack _ecrPanCallBack; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/norad/alpha/fillingstation.cpp b/engines/pegasus/neighborhood/norad/alpha/fillingstation.cpp new file mode 100644 index 0000000000..169f75f7d2 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/alpha/fillingstation.cpp @@ -0,0 +1,445 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/items/inventory/airmask.h" +#include "pegasus/neighborhood/norad/constants.h" +#include "pegasus/neighborhood/norad/alpha/fillingstation.h" +#include "pegasus/neighborhood/norad/alpha/noradalpha.h" + +namespace Pegasus { + +static const NotificationFlags kFSPowerUpFinishedFlag = 1; +static const NotificationFlags kFSSplashFinishedFlag = kFSPowerUpFinishedFlag << 1; +static const NotificationFlags kFSIntakeWarningFinishedFlag = kFSSplashFinishedFlag << 1; +static const NotificationFlags kFSIntakeHiliteFinishedFlag = kFSIntakeWarningFinishedFlag << 1; +static const NotificationFlags kFSDispenseHiliteFinishedFlag = kFSIntakeHiliteFinishedFlag << 1; +static const NotificationFlags kFSArHiliteFinishedFlag = kFSDispenseHiliteFinishedFlag << 1; +static const NotificationFlags kFSCO2HiliteFinishedFlag = kFSArHiliteFinishedFlag << 1; +static const NotificationFlags kFSHeHiliteFinishedFlag = kFSCO2HiliteFinishedFlag << 1; +static const NotificationFlags kFSOHiliteFinishedFlag = kFSHeHiliteFinishedFlag << 1; +static const NotificationFlags kFSNHiliteFinishedFlag = kFSOHiliteFinishedFlag << 1; + +static const NotificationFlags kFSNotificationFlags = kFSPowerUpFinishedFlag | + kFSSplashFinishedFlag | + kFSIntakeWarningFinishedFlag | + kFSIntakeHiliteFinishedFlag | + kFSDispenseHiliteFinishedFlag | + kFSArHiliteFinishedFlag | + kFSCO2HiliteFinishedFlag | + kFSHeHiliteFinishedFlag | + kFSOHiliteFinishedFlag | + kFSNHiliteFinishedFlag; + +static const int16 kNoState = 0; +static const int16 kMainMenu = 1; +static const int16 kWaitingForAttach = 2; +static const int16 kDispenseMenu = 3; +static const int16 kWaitingForDispense = 4; + +// Dummy itemIDs +static const ItemID kCO2Item = 10000; +static const ItemID kHeItem = 10001; + +// Interactive points. +static const TimeValue kFSPowerUpStartStart = 0; +static const TimeValue kFSPowerUpStartStop = 600; +static const TimeValue kFSSplashStart = 600; +static const TimeValue kFSSplashStop = 7800; +static const TimeValue kFSSplashIntakeStart = 7800; +static const TimeValue kFSSplashIntakeStop = 18600; + +static const TimeValue kFSMainMenu = 18600; +static const TimeValue kFSIntakeHiliteStart = 19200; +static const TimeValue kFSIntakeHiliteStop = 19800; +static const TimeValue kFSDispenseHiliteStart = 19800; +static const TimeValue kFSDispenseHiliteStop = 20400; + +static const TimeValue kFSDispenseMenu = 20400; + +static const TimeValue kFSArHiliteStart = 21000; +static const TimeValue kFSArHiliteStop = 21600; +static const TimeValue kFSArAttach = 21600; +static const TimeValue kFSArFilledStart = 22200; +static const TimeValue kFSArFilledStop = 25200; +static const TimeValue kFSArIncompatibleStart = 25200; +static const TimeValue kFSArIncompatibleStop = 30000; + +static const TimeValue kFSCO2HiliteStart = 30000; +static const TimeValue kFSCO2HiliteStop = 30600; +static const TimeValue kFSCO2Attach = 30600; +static const TimeValue kFSCO2FilledStart = 31200; +static const TimeValue kFSCO2FilledStop = 34200; +static const TimeValue kFSCO2IncompatibleStart = 34200; +static const TimeValue kFSCO2IncompatibleStop = 39000; + +static const TimeValue kFSHeHiliteStart = 39000; +static const TimeValue kFSHeHiliteStop = 39600; +static const TimeValue kFSHeAttach = 39600; +static const TimeValue kFSHeFilledStart = 40200; +static const TimeValue kFSHeFilledStop = 43200; +static const TimeValue kFSHeIncompatibleStart = 43200; +static const TimeValue kFSHeIncompatibleStop = 48000; + +static const TimeValue kFSOHiliteStart = 48000; +static const TimeValue kFSOHiliteStop = 48600; +static const TimeValue kFSOAttach = 48600; +static const TimeValue kFSOFilledStart = 49200; +static const TimeValue kFSOFilledStop = 52200; +static const TimeValue kFSOIncompatibleStart = 52200; +static const TimeValue kFSOIncompatibleStop = 57000; + +static const TimeValue kFSNHiliteStart = 57000; +static const TimeValue kFSNHiliteStop = 57600; +static const TimeValue kFSNAttach = 57600; +static const TimeValue kFSNFilledStart = 58200; +static const TimeValue kFSNFilledStop = 61200; +static const TimeValue kFSNIncompatibleStart = 61200; +static const TimeValue kFSNIncompatibleStop = 66000; + +static const TimeValue kFSIntakeMenu = 66000; +static const TimeValue kFSIntakeInProgressStart = 66600; +static const TimeValue kFSIntakeInProgressStop = 69600; + +NoradAlphaFillingStation::NoradAlphaFillingStation(Neighborhood *owner) : GameInteraction(kNoradFillingStationInteractionID, owner), + _rightSideMovie(kN01RightSideID), _rightSideNotification(kNoradFillingStationNotificationID, ((PegasusEngine *)g_engine)) { + _state = kNoState; +} + +void NoradAlphaFillingStation::openInteraction() { + _rightSideMovie.initFromMovieFile("Images/Norad Alpha/N01W Right Side"); + _rightSideMovie.moveElementTo(kNoradAlpha01RightSideLeft, kNoradAlpha01RightSideTop); + _rightSideMovie.setDisplayOrder(kN01RightSideOrder); + _rightSideMovie.startDisplaying(); + _rightSideCallBack.setNotification(&_rightSideNotification); + _rightSideCallBack.initCallBack(&_rightSideMovie, kCallBackAtExtremes); + _rightSideCallBack.setCallBackFlag(kFSPowerUpFinishedFlag); + _rightSideNotification.notifyMe(this, kFSNotificationFlags, kFSNotificationFlags); + _rightSideCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + _rightSideMovie.show(); + _rightSideMovie.redrawMovieWorld(); + _rightSideMovie.setSegment(kFSPowerUpStartStart, kFSPowerUpStartStop); +} + +void NoradAlphaFillingStation::initInteraction() { + allowInput(false); + + _rightSideMovie.setRate(2); +} + +void NoradAlphaFillingStation::closeInteraction() { + _rightSideMovie.stop(); + _rightSideMovie.stopDisplaying(); + _rightSideMovie.releaseMovie(); + _rightSideCallBack.releaseCallBack(); + ((NoradAlpha *)getOwner())->turnOffFillingStation(); +} + +void NoradAlphaFillingStation::setStaticState(TimeValue time, int16 state) { + _rightSideMovie.stop(); + _rightSideMovie.setSegment(0, _rightSideMovie.getDuration()); + _rightSideMovie.setTime(time); + _rightSideMovie.redrawMovieWorld(); + _state = state; + allowInput(true); +} + +void NoradAlphaFillingStation::setSegmentState(TimeValue start, TimeValue stop, NotificationFlags flag, int16 state) { + _rightSideMovie.stop(); + _rightSideMovie.setSegment(start, stop); + _rightSideMovie.setTime(start); + _rightSideCallBack.setCallBackFlag(flag); + _rightSideCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + _state = state; + allowInput(false); + _rightSideMovie.setRate(2); +} + +void NoradAlphaFillingStation::powerUpFinished() { + ((NoradAlpha *)getOwner())->turnOnFillingStation(); + setSegmentState(kFSSplashStart, kFSSplashStop, kFSSplashFinishedFlag, kNoState); +} + +void NoradAlphaFillingStation::splashFinished() { + if (GameState.getNoradGassed()) + setSegmentState(kFSSplashIntakeStart, kFSSplashIntakeStop, kFSIntakeWarningFinishedFlag, kNoState); + else + intakeWarningFinished(); +} + +void NoradAlphaFillingStation::intakeWarningFinished() { + setStaticState(kFSMainMenu, kMainMenu); +} + +void NoradAlphaFillingStation::showIntakeInProgress(uint16 numSeconds) { + if (numSeconds == 0) { + setSegmentState(kFSIntakeInProgressStart, kFSIntakeInProgressStop, kFSIntakeWarningFinishedFlag, kNoState); + Item *item = ((NoradAlpha *)getOwner())->getFillingItem(); + + if (item->getObjectID() == kGasCanister) { + GameState.setNoradGassed(true); + ((NoradAlpha *)getOwner())->loadAmbientLoops(); + getOwner()->restoreStriding(kNorad03, kEast, kAltNoradAlphaNormal); + } + } else { + setSegmentState(kFSIntakeInProgressStart, kFSIntakeInProgressStart + _rightSideMovie.getScale() * numSeconds, + kFSIntakeWarningFinishedFlag, kNoState); + } +} + +void NoradAlphaFillingStation::intakeHighlightFinished() { + _rightSideMovie.stop(); + + if (GameState.getNoradGassed()) { + showIntakeInProgress(2); + } else { + Item *item = ((NoradAlpha *)getOwner())->getFillingItem(); + if (item) + showIntakeInProgress(0); + else + setStaticState(kFSIntakeMenu, kWaitingForAttach); + } +} + +void NoradAlphaFillingStation::dispenseHighlightFinished() { + setStaticState(kFSDispenseMenu, kDispenseMenu); +} + +void NoradAlphaFillingStation::dispenseGas() { + Item *item = ((NoradAlpha *)getOwner())->getFillingItem(); + + if (item) { + if (item->getObjectID() != _dispenseItemID) + switch (_dispenseItemID) { + case kArgonCanister: + setSegmentState(kFSArIncompatibleStart, kFSArIncompatibleStop, + kFSIntakeWarningFinishedFlag, kNoState); + break; + case kCO2Item: + setSegmentState(kFSCO2IncompatibleStart, kFSCO2IncompatibleStop, + kFSIntakeWarningFinishedFlag, kNoState); + break; + case kHeItem: + setSegmentState(kFSHeIncompatibleStart, kFSHeIncompatibleStop, + kFSIntakeWarningFinishedFlag, kNoState); + break; + case kAirMask: + setSegmentState(kFSOIncompatibleStart, kFSOIncompatibleStop, + kFSIntakeWarningFinishedFlag, kNoState); + break; + case kNitrogenCanister: + setSegmentState(kFSNIncompatibleStart, kFSNIncompatibleStop, + kFSIntakeWarningFinishedFlag, kNoState); + break; + } + else { + if (_dispenseItemID == kArgonCanister) { + setSegmentState(kFSArFilledStart, kFSArFilledStop, kFSIntakeWarningFinishedFlag, kNoState); + item->setItemState(kArgonFull); + GameState.setScoringFilledArgonCanister(true); + } else if (_dispenseItemID == kAirMask) { + setSegmentState(kFSOFilledStart, kFSOFilledStop, kFSIntakeWarningFinishedFlag, kNoState); + ((AirMask *)item)->refillAirMask(); + GameState.setScoringFilledOxygenCanister(true); + } else if (_dispenseItemID == kNitrogenCanister) { + setSegmentState(kFSNFilledStart, kFSNFilledStop, kFSIntakeWarningFinishedFlag, kNoState); + item->setItemState(kNitrogenFull); + } + } + } else { + switch (_dispenseItemID) { + case kArgonCanister: + setStaticState(kFSArAttach, kWaitingForDispense); + break; + case kCO2Item: + setStaticState(kFSCO2Attach, kWaitingForDispense); + break; + case kHeItem: + setStaticState(kFSHeAttach, kWaitingForDispense); + break; + case kAirMask: + setStaticState(kFSOAttach, kWaitingForDispense); + break; + case kNitrogenCanister: + setStaticState(kFSNAttach, kWaitingForDispense); + break; + } + } +} + +void NoradAlphaFillingStation::ArHighlightFinished() { + _dispenseItemID = kArgonCanister; + dispenseGas(); +} + +void NoradAlphaFillingStation::CO2HighlightFinished() { + _dispenseItemID = kCO2Item; + dispenseGas(); +} + +void NoradAlphaFillingStation::HeHighlightFinished() { + _dispenseItemID = kHeItem; + dispenseGas(); +} + +void NoradAlphaFillingStation::OHighlightFinished() { + _dispenseItemID = kAirMask; + dispenseGas(); +} + +void NoradAlphaFillingStation::NHighlightFinished() { + _dispenseItemID = kNitrogenCanister; + dispenseGas(); +} + +void NoradAlphaFillingStation::receiveNotification(Notification *, const NotificationFlags flags) { + switch (flags) { + case kFSPowerUpFinishedFlag: + powerUpFinished(); + break; + case kFSSplashFinishedFlag: + splashFinished(); + break; + case kFSIntakeWarningFinishedFlag: + intakeWarningFinished(); + break; + case kFSIntakeHiliteFinishedFlag: + intakeHighlightFinished(); + break; + case kFSDispenseHiliteFinishedFlag: + dispenseHighlightFinished(); + break; + case kFSArHiliteFinishedFlag: + ArHighlightFinished(); + break; + case kFSCO2HiliteFinishedFlag: + CO2HighlightFinished(); + break; + case kFSHeHiliteFinishedFlag: + HeHighlightFinished(); + break; + case kFSOHiliteFinishedFlag: + OHighlightFinished(); + break; + case kFSNHiliteFinishedFlag: + NHighlightFinished(); + break; + } +} + +void NoradAlphaFillingStation::handleInput(const Input &input, const Hotspot *cursorSpot) { + InputHandler::handleInput(input, cursorSpot); +} + +void NoradAlphaFillingStation::clickInIntake() { + setSegmentState(kFSIntakeHiliteStart, kFSIntakeHiliteStop, kFSIntakeHiliteFinishedFlag, kNoState); +} + +void NoradAlphaFillingStation::clickInDispense() { + setSegmentState(kFSDispenseHiliteStart, kFSDispenseHiliteStop, kFSDispenseHiliteFinishedFlag, kNoState); +} + +void NoradAlphaFillingStation::clickInAr() { + setSegmentState(kFSArHiliteStart, kFSArHiliteStop, kFSArHiliteFinishedFlag, kNoState); +} + +void NoradAlphaFillingStation::clickInCO2() { + setSegmentState(kFSCO2HiliteStart, kFSCO2HiliteStop, kFSCO2HiliteFinishedFlag, kNoState); +} + +void NoradAlphaFillingStation::clickInHe() { + setSegmentState(kFSHeHiliteStart, kFSHeHiliteStop, kFSHeHiliteFinishedFlag, kNoState); +} + +void NoradAlphaFillingStation::clickInO() { + setSegmentState(kFSOHiliteStart, kFSOHiliteStop, kFSOHiliteFinishedFlag, kNoState); +} + +void NoradAlphaFillingStation::clickInN() { + setSegmentState(kFSNHiliteStart, kFSNHiliteStop, kFSNHiliteFinishedFlag, kNoState); +} + +void NoradAlphaFillingStation::clickInHotspot(const Input &input, const Hotspot *spot) { + GameInteraction::clickInHotspot(input, spot); + + switch (spot->getObjectID()) { + case kNorad01IntakeSpotID: + clickInIntake(); + break; + case kNorad01DispenseSpotID: + clickInDispense(); + break; + case kNorad01ArSpotID: + clickInAr(); + break; + case kNorad01CO2SpotID: + clickInCO2(); + break; + case kNorad01HeSpotID: + clickInHe(); + break; + case kNorad01OSpotID: + clickInO(); + break; + case kNorad01NSpotID: + clickInN(); + break; + } +} + +void NoradAlphaFillingStation::activateHotspots() { + GameInteraction::activateHotspots(); + + switch (_state) { + case kMainMenu: + g_allHotspots.activateOneHotspot(kNorad01IntakeSpotID); + g_allHotspots.activateOneHotspot(kNorad01DispenseSpotID); + break; + case kDispenseMenu: + g_allHotspots.activateOneHotspot(kNorad01ArSpotID); + g_allHotspots.activateOneHotspot(kNorad01CO2SpotID); + g_allHotspots.activateOneHotspot(kNorad01HeSpotID); + g_allHotspots.activateOneHotspot(kNorad01OSpotID); + g_allHotspots.activateOneHotspot(kNorad01NSpotID); + break; + } +} + +void NoradAlphaFillingStation::newFillingItem(Item *item) { + switch (_state) { + case kWaitingForAttach: + if (item) + showIntakeInProgress(0); + break; + case kWaitingForDispense: + dispenseGas(); + break; + default: + break; + } +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/norad/alpha/fillingstation.h b/engines/pegasus/neighborhood/norad/alpha/fillingstation.h new file mode 100644 index 0000000000..eb2088e373 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/alpha/fillingstation.h @@ -0,0 +1,91 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_FILLINGSTATION_H +#define PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_FILLINGSTATION_H + +#include "pegasus/interaction.h" +#include "pegasus/movie.h" +#include "pegasus/notification.h" + +namespace Pegasus { + +class Item; + +class NoradAlphaFillingStation : public GameInteraction, public NotificationReceiver { +public: + NoradAlphaFillingStation(Neighborhood *); + virtual ~NoradAlphaFillingStation() {} + + virtual void handleInput(const Input &, const Hotspot *); + + virtual void clickInHotspot(const Input &, const Hotspot *); + virtual void activateHotspots(); + + void newFillingItem(Item *); + +protected: + void receiveNotification(Notification *, const NotificationFlags); + + virtual void openInteraction(); + virtual void initInteraction(); + virtual void closeInteraction(); + + void powerUpFinished(); + void splashFinished(); + void intakeWarningFinished(); + void intakeHighlightFinished(); + void dispenseHighlightFinished(); + void ArHighlightFinished(); + void CO2HighlightFinished(); + void HeHighlightFinished(); + void OHighlightFinished(); + void NHighlightFinished(); + + void showIntakeInProgress(uint16); + + void clickInIntake(); + void clickInDispense(); + void clickInAr(); + void clickInCO2(); + void clickInHe(); + void clickInO(); + void clickInN(); + + void dispenseGas(); + + void setStaticState(TimeValue, int16); + void setSegmentState(TimeValue, TimeValue, NotificationFlags, int16); + + Movie _rightSideMovie; + Notification _rightSideNotification; + NotificationCallBack _rightSideCallBack; + int16 _state; + ItemID _dispenseItemID; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/norad/alpha/noradalpha.cpp b/engines/pegasus/neighborhood/norad/alpha/noradalpha.cpp new file mode 100644 index 0000000000..e4a5e26473 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/alpha/noradalpha.cpp @@ -0,0 +1,763 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/energymonitor.h" +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/inventory/airmask.h" +#include "pegasus/neighborhood/norad/constants.h" +#include "pegasus/neighborhood/norad/subcontrolroom.h" +#include "pegasus/neighborhood/norad/alpha/ecrmonitor.h" +#include "pegasus/neighborhood/norad/alpha/fillingstation.h" +#include "pegasus/neighborhood/norad/alpha/noradalpha.h" + +namespace Pegasus { + +const uint32 NoradAlpha::_noradAlphaClawExtras[22] = { + kN22ClawFromAToB, + kN22ClawALoop, + kN22ClawAPinch, + kN22ClawACounterclockwise, + kN22ClawAClockwise, + kN22ClawFromBToA, + kN22ClawFromBToC, + kN22ClawFromBToD, + kN22ClawBLoop, + kN22ClawBPinch, + kN22ClawBCounterclockwise, + kN22ClawBClockwise, + kN22ClawFromCToB, + kN22ClawCLoop, + kN22ClawCPinch, + kN22ClawCCounterclockwise, + kN22ClawCClockwise, + kN22ClawFromDToB, + kN22ClawDLoop, + kN22ClawDPinch, + kN22ClawDCounterclockwise, + kN22ClawDClockwise +}; + +NoradAlpha::NoradAlpha(InputHandler *nextHandler, PegasusEngine *owner) : Norad(nextHandler, owner, "Norad Alpha", kNoradAlphaID) { + _elevatorUpRoomID = kNorad11South; + _elevatorDownRoomID = kNorad12South; + _elevatorUpSpotID = kNorad12ElevatorUpSpotID; + _elevatorDownSpotID = kNorad11ElevatorDownSpotID; + + _subRoomEntryRoom1 = kNorad10; + _subRoomEntryDir1 = kEast; + _subRoomEntryRoom2 = kNorad21; + _subRoomEntryDir2 = kWest; + _upperPressureDoorRoom = kNorad10East; + _lowerPressureDoorRoom = kNorad21West; + + _upperPressureDoorUpSpotID = kAlphaUpperPressureDoorUpSpotID; + _upperPressureDoorDownSpotID = kAlphaUpperPressureDoorDownSpotID; + _upperPressureDoorAbortSpotID = kNorad10EastOutSpotID; + + _lowerPressureDoorUpSpotID = kAlphaLowerPressureDoorUpSpotID; + _lowerPressureDoorDownSpotID = kAlphaLowerPressureDoorDownSpotID; + _lowerPressureDoorAbortSpotID = kNorad21WestOutSpotID; + + _pressureSoundIn = kPressureDoorIntro1In; + _pressureSoundOut = kPressureDoorIntro1Out; + _equalizeSoundIn = kPressureDoorIntro2In; + _equalizeSoundOut = kPressureDoorIntro2Out; + _accessDeniedIn = kAlphaAccessDeniedIn; + _accessDeniedOut = kAlphaAccessDeniedOut; + + _platformRoom = kNorad19West; + _subControlRoom = kNorad22West; + + _subPrepFailed = false; + + setIsItemTaken(kGasCanister); +} + +void NoradAlpha::init() { + Norad::init(); + + Hotspot *hotspot = _vm->getAllHotspots().findHotspotByID(kN01GasCanisterSpotID); + hotspot->setMaskedHotspotFlags(kPickUpItemSpotFlag, kPickUpItemSpotFlag); + HotspotInfoTable::Entry *hotspotEntry = findHotspotEntry(kN01GasCanisterSpotID); + hotspotEntry->hotspotItem = kGasCanister; + + hotspot = _vm->getAllHotspots().findHotspotByID(kN01ArgonCanisterSpotID); + hotspot->setMaskedHotspotFlags(kPickUpItemSpotFlag, kPickUpItemSpotFlag); + hotspotEntry = findHotspotEntry(kN01ArgonCanisterSpotID); + hotspotEntry->hotspotItem = kArgonCanister; + + hotspot = _vm->getAllHotspots().findHotspotByID(kN01NitrogenCanisterSpotID); + hotspot->setMaskedHotspotFlags(kPickUpItemSpotFlag, kPickUpItemSpotFlag); + hotspotEntry = findHotspotEntry(kN01NitrogenCanisterSpotID); + hotspotEntry->hotspotItem = kNitrogenCanister; + + hotspot = _vm->getAllHotspots().findHotspotByID(kN01AirMaskSpotID); + hotspot->setMaskedHotspotFlags(kPickUpItemSpotFlag, kPickUpItemSpotFlag); + hotspotEntry = findHotspotEntry(kN01AirMaskSpotID); + hotspotEntry->hotspotItem = kAirMask; + + hotspot = _vm->getAllHotspots().findHotspotByID(kN01GasOutletSpotID); + hotspot->setMaskedHotspotFlags(kDropItemSpotFlag, kDropItemSpotFlag); +} + +void NoradAlpha::start() { + if (g_energyMonitor) { + g_energyMonitor->stopEnergyDraining(); + g_energyMonitor->restoreLastEnergyValue(); + _vm->resetEnergyDeathReason(); + g_energyMonitor->startEnergyDraining(); + } + + NeighborhoodID itemNeighborhood; + RoomID itemRoom; + DirectionConstant itemDirection; + + Item *item = (Item *)_vm->getAllItems().findItemByID(kGasCanister); + item->getItemRoom(itemNeighborhood, itemRoom, itemDirection); + + if (itemNeighborhood == getObjectID()) { + _fillingStationItem = item; + } else { + item = (Item *)_vm->getAllItems().findItemByID(kAirMask); + item->getItemRoom(itemNeighborhood, itemRoom, itemDirection); + + if (itemNeighborhood == getObjectID()) { + _fillingStationItem = item; + } else { + item = (Item *)_vm->getAllItems().findItemByID(kNitrogenCanister); + item->getItemRoom(itemNeighborhood, itemRoom, itemDirection); + + if (itemNeighborhood == getObjectID()) { + _fillingStationItem = item; + } else { + item = (Item *)_vm->getAllItems().findItemByID(kArgonCanister); + item->getItemRoom(itemNeighborhood, itemRoom, itemDirection); + if (itemNeighborhood == getObjectID()) + _fillingStationItem = item; + else + _fillingStationItem = 0; + } + } + } + + if (!GameState.getNoradGassed()) + forceStridingStop(kNorad03, kEast, kAltNoradAlphaNormal); + + GameState.setNoradArrivedFromSub(false); + Norad::start(); +} + +void NoradAlpha::setUpAIRules() { + Neighborhood::setUpAIRules(); + + if (g_AIArea) { + AIPlayMessageAction *messageAction = new AIPlayMessageAction("Images/AI/Norad/XN01WD1", false); + AIHasItemCondition *hasGasCanisterCondition = new AIHasItemCondition(kGasCanister); + AIRule *rule = new AIRule(hasGasCanisterCondition, messageAction); + g_AIArea->addAIRule(rule); + } +} + +bool NoradAlpha::okayToJump() { + bool result = Neighborhood::okayToJump(); + + if (!result) + playSpotSoundSync(kAlphaCantTransportIn, kAlphaCantTransportOut); + + return result; +} + +void NoradAlpha::getExtraCompassMove(const ExtraTable::Entry &entry, FaderMoveSpec &compassMove) { + if (entry.extra == kNorad19ExitToSub) { + compassMove.makeTwoKnotFaderSpec(kNoradAlphaMovieScale, entry.movieStart, 270 + kSubPlatformCompassAngle, + entry.movieEnd, 90 + 20 + 360); + compassMove.insertFaderKnot(entry.movieStart + 10 * kNoradAlphaFrameDuration, 270 + kSubPlatformCompassAngle); + compassMove.insertFaderKnot(entry.movieStart + 29 * kNoradAlphaFrameDuration, 270 + kSubPlatformCompassAngle + 20); + compassMove.insertFaderKnot(entry.movieStart + 52 * kNoradAlphaFrameDuration, 270 + kSubPlatformCompassAngle + 20); + compassMove.insertFaderKnot(entry.movieStart + 84 * kNoradAlphaFrameDuration, 360 + 90); + compassMove.insertFaderKnot(entry.movieStart + 198 * kNoradAlphaFrameDuration, 360 + 90); + compassMove.insertFaderKnot(entry.movieStart + 270 * kNoradAlphaFrameDuration, 360 + 90 + 15); + compassMove.insertFaderKnot(entry.movieStart + 280 * kNoradAlphaFrameDuration, 360 + 90 + 20); + } else { + Norad::getExtraCompassMove(entry, compassMove); + } +} + +void NoradAlpha::playClawMonitorIntro() { + playSpotSoundSync(kLoadClawIntroIn, kLoadClawIntroOut); +} + +GameInteraction *NoradAlpha::makeInteraction(const InteractionID interactionID) { + switch (interactionID) { + case kNoradECRMonitorInteractionID: + return new NoradAlphaECRMonitor(this); + case kNoradFillingStationInteractionID: + return new NoradAlphaFillingStation(this); + } + + return Norad::makeInteraction(interactionID); +} + +void NoradAlpha::loadAmbientLoops() { + // clone2727 would like to point out that the following comment does not quite + // match the code logic below + +/* + Logic: + + loop sound 1: + if gassed, + play warning loop of some sort + else + play nothing + loop sound 2: + if gassed and not wearing air mask + if in ECR + play breathing water loop + else + play breathing + else + if in ECR + play water loop + if at N07 north + play unmanned loop +*/ + + if (!GameState.getNoradSeenTimeStream()) + return; + + RoomID room = GameState.getCurrentRoom(); + if (GameState.getNoradGassed()) { + if (room >= kNorad11 && room <= kNorad19West) + loadLoopSound1("Sounds/Norad/NEW SUB AMB.22K.AIFF", kNoradWarningVolume * 3); + else if (room >= kNorad21 && room <= kNorad22West) + loadLoopSound1("Sounds/Norad/SUB CONTRL LOOP.22K.AIFF", kNoradWarningVolume * 3); + else + loadLoopSound1("Sounds/Norad/WARNING LOOP.22K.AIFF", kNoradWarningVolume); + } else { + loadLoopSound1(""); + } + + if (GameState.getNoradGassed() && !g_airMask->isAirFilterOn()) { + if (room >= kNorad01 && room <= kNorad01West) { + loadLoopSound2("Sounds/Norad/Breathing Water.22K.AIFF", kNoradSuckWindVolume); + } else if (room == kNorad02) { + if (GameState.isCurrentDoorOpen()) + loadLoopSound2("Sounds/Norad/Breathing Water.22K.AIFF", kNoradSuckWindVolume); + else + loadLoopSound2("Sounds/Norad/SUCKING WIND.22K.AIFF", kNoradSuckWindVolume, 0, 0); + } else { + loadLoopSound2("Sounds/Norad/SUCKING WIND.22K.AIFF", kNoradSuckWindVolume, 0, 0); + } + } else { + if (room >= kNorad01 && room <= kNorad01West) { + loadLoopSound2("Sounds/Norad/WATER FLOWING.AIFF", 0x100 / 2); + } else if (room == kNorad02) { + if (GameState.isCurrentDoorOpen()) + loadLoopSound2("Sounds/Norad/WATER FLOWING.AIFF", 0x100 / 2); + else + loadLoopSound2(""); + } else { + loadLoopSound2(""); + } + } + +} + +void NoradAlpha::checkContinuePoint(const RoomID room, const DirectionConstant direction) { + switch (MakeRoomView(room, direction)) { + case MakeRoomView(kNorad02, kEast): + case MakeRoomView(kNorad06, kEast): + case MakeRoomView(kNorad11, kEast): + case MakeRoomView(kNorad15, kEast): + case MakeRoomView(kNorad19, kWest): + case MakeRoomView(kNorad21, kSouth): + makeContinuePoint(); + break; + } +} + +void NoradAlpha::arriveAt(const RoomID room, const DirectionConstant direction) { + Norad::arriveAt(room, direction); + + switch (GameState.getCurrentRoom()) { + case kNorad01: + arriveAtNorad01(); + break; + case kNorad01East: + arriveAtNorad01East(); + break; + case kNorad01West: + arriveAtNorad01West(); + break; + case kNorad04: + arriveAtNorad04(); + break; + case kNorad07North: + GameState.setScoringSawUnconsciousOperator(true); + break; + case kNorad11: + GameState.setScoringWentThroughPressureDoor(true); + break; + case kNorad22: + arriveAtNorad22(); + break; + } +} + +void NoradAlpha::arriveAtNorad01() { + if (!GameState.getNoradSeenTimeStream() && GameState.getCurrentDirection() == kSouth) { + GameState.setNoradN22MessagePlayed(false); + requestExtraSequence(kNoradArriveFromTSA, kExtraCompletedFlag, kFilterNoInput); + // You are no match for me, human. + requestExtraSequence(kNorad01RobotTaunt, kExtraCompletedFlag, kFilterNoInput); + } +} + +void NoradAlpha::arriveAtNorad01East() { + GameState.setScoringSawSecurityMonitor(true); + newInteraction(kNoradECRMonitorInteractionID); +} + +void NoradAlpha::arriveAtNorad01West() { + newInteraction(kNoradFillingStationInteractionID); +} + +void NoradAlpha::arriveAtNorad04() { + if (GameState.getCurrentDirection() == kEast && !GameState.getNoradGassed()) + playDeathExtra(kNorad04EastDeath, kDeathWokeUpNorad); +} + +void NoradAlpha::arriveAtNorad22() { + if (!GameState.getNoradN22MessagePlayed() && GameState.getCurrentDirection() == kSouth) { + startExtraSequence(kNorad22SouthIntro, kExtraCompletedFlag, kFilterNoInput); + GameState.setNoradN22MessagePlayed(true); + } +} + +void NoradAlpha::bumpIntoWall() { + requestSpotSound(kAlphaBumpIntoWallIn, kAlphaBumpIntoWallOut, kFilterNoInput, 0); + Neighborhood::bumpIntoWall(); +} + +void NoradAlpha::receiveNotification(Notification *notification, const NotificationFlags flags) { + if ((flags & kExtraCompletedFlag) != 0) { + switch (_lastExtra) { + case kNoradArriveFromTSA: + GameState.setNoradSeenTimeStream(true); + loadAmbientLoops(); + break; + case kNorad01RobotTaunt: + g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Norad/XN01SB", false, kWarningInterruption); + _interruptionFilter = kFilterAllInput; + makeContinuePoint(); + break; + } + } + + Norad::receiveNotification(notification, flags); + + if ((flags & kExtraCompletedFlag) != 0) { + switch (_lastExtra) { + case kNorad22SouthIntro: + loopExtraSequence(kNorad22SouthReply); + playSpotSoundSync(kN22ReplyIn, kN22ReplyOut); + startExtraSequence(kNorad22SouthFinish, kExtraCompletedFlag, kFilterNoInput); + break; + case kNorad22SouthFinish: + _interruptionFilter = kFilterAllInput; + // Force ArriveAt to do its thing... + GameState.setCurrentRoom(kNorad21); + arriveAt(kNorad22, kSouth); + break; + } + } + + g_AIArea->checkMiddleArea(); +} + +void NoradAlpha::getZoomEntry(const HotSpotID spotID, ZoomTable::Entry &entry) { + Norad::getZoomEntry(spotID, entry); + + ExtraTable::Entry extra; + + if (spotID == kNorad01GasSpotID) { + if (_fillingStationItem) { + if (_fillingStationItem->getObjectID() == kGasCanister) { + getExtraEntry(kNorad01ZoomInWithGasCanister, extra); + entry.movieStart = extra.movieStart; + entry.movieEnd = extra.movieEnd; + } else { + entry.clear(); + } + } + } else if (spotID == kNorad01GasOutSpotID) { + if (_fillingStationItem) { + if (_fillingStationItem->getObjectID() == kGasCanister) { + getExtraEntry(kNorad01ZoomOutWithGasCanister, extra); + entry.movieStart = extra.movieStart; + entry.movieEnd = extra.movieEnd; + } else { + entry.clear(); + } + } + } +} + +TimeValue NoradAlpha::getViewTime(const RoomID room, const DirectionConstant direction) { + ExtraTable::Entry entry; + + if (room == kNorad01 && direction == kSouth && !GameState.getNoradSeenTimeStream()) { + getExtraEntry(kNoradArriveFromTSA, entry); + return entry.movieStart; + } + + if (room == kNorad01 && direction == kWest) { + if (!_fillingStationItem) { + return Norad::getViewTime(room, direction); + } else { + getExtraEntry(kN01WGasCanister, entry); + return entry.movieStart; + } + } else if (room == kNorad01West && direction == kWest) { + uint32 extraID = 0xffffffff; + if (_fillingStationItem) { + switch (_fillingStationItem->getObjectID()) { + case kArgonCanister: + if (GameState.getNoradFillingStationOn()) + extraID = kN01WZArgonCanisterLit; + else + extraID = kN01WZArgonCanisterDim; + break; + case kGasCanister: + if (GameState.getNoradFillingStationOn()) + extraID = kN01WZGasCanisterLit; + else + extraID = kN01WZGasCanisterDim; + break; + case kAirMask: + if (GameState.getNoradFillingStationOn()) + extraID = kN01WZAirMaskLit; + else + extraID = kN01WZAirMaskDim; + break; + case kNitrogenCanister: + if (GameState.getNoradFillingStationOn()) + extraID = kN01WZNitrogenCanisterLit; + else + extraID = kN01WZNitrogenCanisterDim; + break; + default: + // Should never happen. + break; + } + } else if (GameState.getNoradFillingStationOn()) { + extraID = kN01WZEmptyLit; + } + + if (extraID == 0xffffffff) { + return Norad::getViewTime(room, direction); + } else { + getExtraEntry(extraID, entry); + return entry.movieStart; + } + } + + return Norad::getViewTime(room, direction); +} + +void NoradAlpha::turnOnFillingStation() { + if (GameState.getCurrentRoom() == kNorad01West && !GameState.getNoradFillingStationOn()) { + GameState.setNoradFillingStationOn(true); + updateViewFrame(); + } +} + +void NoradAlpha::turnOffFillingStation() { + if (GameState.getCurrentRoom() == kNorad01West && GameState.getNoradFillingStationOn()) { + GameState.setNoradFillingStationOn(false); + updateViewFrame(); + } +} + +void NoradAlpha::activateHotspots() { + Norad::activateHotspots(); + + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kNorad01West, kWest): + if (_vm->getDragType() == kDragInventoryUse) { + if (!_fillingStationItem) { + ItemID itemID = _vm->getDraggingItem()->getObjectID(); + if (itemID == kArgonCanister || itemID == kGasCanister || itemID == kAirMask || + itemID == kNitrogenCanister) + _vm->getAllHotspots().activateOneHotspot(kN01GasOutletSpotID); + } + } else { + HotSpotID spotID; + + if (_fillingStationItem) { + switch (_fillingStationItem->getObjectID()) { + case kArgonCanister: + spotID = kN01ArgonCanisterSpotID; + _vm->getAllHotspots().deactivateOneHotspot(kNorad01GasOutSpotID); + break; + case kGasCanister: + spotID = kN01GasCanisterSpotID; + break; + case kAirMask: + spotID = kN01AirMaskSpotID; + _vm->getAllHotspots().deactivateOneHotspot(kNorad01GasOutSpotID); + break; + case kNitrogenCanister: + spotID = kN01NitrogenCanisterSpotID; + _vm->getAllHotspots().deactivateOneHotspot(kNorad01GasOutSpotID); + break; + default: + // Should never happen. + spotID = kNoHotSpotID; + break; + } + _vm->getAllHotspots().activateOneHotspot(spotID); + } + } + break; + case MakeRoomView(kNorad10, kEast): + if (GameState.isCurrentDoorOpen()) + _vm->getAllHotspots().deactivateOneHotspot(kNorad10DoorSpotID); + break; + case MakeRoomView(kNorad21, kWest): + if (GameState.isCurrentDoorOpen()) + _vm->getAllHotspots().deactivateOneHotspot(kNorad21WestSpotID); + break; + } +} + +void NoradAlpha::clickInHotspot(const Input &input, const Hotspot *cursorSpot) { + Norad::clickInHotspot(input, cursorSpot); + + if (_vm->getDragType() == kDragInventoryUse) { + if (GameState.getCurrentRoomAndView() == MakeRoomView(kNorad01West, kWest)) { + Item *item = _vm->getDraggingItem(); + if (item->getObjectID() == kAirMask || item->getObjectID() == kArgonCanister || + item->getObjectID() == kNitrogenCanister || item->getObjectID() == kGasCanister) { + HotspotInfoTable::Entry *hotspotEntry = findHotspotEntry(kN01GasOutletSpotID); + hotspotEntry->hotspotItem = item->getObjectID(); + } + } + } +} + +void NoradAlpha::takeItemFromRoom(Item *item) { + if (GameState.getCurrentRoom() == kNorad01West) { + if (_fillingStationItem == item) { + _fillingStationItem = 0; + GameState.setNoradGassed(false); + loadAmbientLoops(); + ((NoradAlphaFillingStation *)_currentInteraction)->newFillingItem(0); + forceStridingStop(kNorad03, kEast, kAltNoradAlphaNormal); + } + } + + Norad::takeItemFromRoom(item); +} + +void NoradAlpha::dropItemIntoRoom(Item *item, Hotspot *droppedSpot) { + if (GameState.getCurrentRoom() == kNorad01West) { + if (!_fillingStationItem) { + _fillingStationItem = item; + ((NoradAlphaFillingStation *)_currentInteraction)->newFillingItem(item); + } + } + + Norad::dropItemIntoRoom(item, droppedSpot); +} + +void NoradAlpha::getClawInfo(HotSpotID &outSpotID, HotSpotID &prepSpotID, HotSpotID &clawControlSpotID, HotSpotID &pinchClawSpotID, + HotSpotID &moveClawDownSpotID, HotSpotID &moveClawRightSpotID, HotSpotID &moveClawLeftSpotID, HotSpotID &moveClawUpSpotID, + HotSpotID &clawCCWSpotID, HotSpotID &clawCWSpotID, uint32 &clawPosition, const uint32 *&clawExtraIDs) { + outSpotID = kNorad22MonitorOutSpotID; + prepSpotID = kNorad22LaunchPrepSpotID; + clawControlSpotID = kNorad22ClawControlSpotID; + pinchClawSpotID = kNorad22ClawPinchSpotID; + moveClawDownSpotID = kNorad22ClawDownSpotID; + moveClawRightSpotID = kNorad22ClawRightSpotID; + moveClawLeftSpotID = kNorad22ClawLeftSpotID; + moveClawUpSpotID = kNorad22ClawUpSpotID; + clawCCWSpotID = kNorad22ClawCCWSpotID; + clawCWSpotID = kNorad22ClawCWSpotID; + clawPosition = kClawAtD; + clawExtraIDs = _noradAlphaClawExtras; +} + +Hotspot *NoradAlpha::getItemScreenSpot(Item *item, DisplayElement *element) { + switch (item->getObjectID()) { + case kGasCanister: + return _vm->getAllHotspots().findHotspotByID(kN01GasCanisterSpotID); + case kAirMask: + return _vm->getAllHotspots().findHotspotByID(kN01AirMaskSpotID); + case kArgonCanister: + return _vm->getAllHotspots().findHotspotByID(kN01ArgonCanisterSpotID); + case kNitrogenCanister: + return _vm->getAllHotspots().findHotspotByID(kN01NitrogenCanisterSpotID); + } + + return Norad::getItemScreenSpot(item, element); +} + +Common::String NoradAlpha::getEnvScanMovie() { + Common::String movieName = Neighborhood::getEnvScanMovie(); + + if (movieName.empty()) { + RoomID room = GameState.getCurrentRoom(); + if (room >= kNorad01 && room <= kNorad01West) + return "Images/AI/Norad/XNE1"; + else if ((room >= kNorad02 && room <= kNorad19West)) + return "Images/AI/Norad/XNE2"; + + return "Images/AI/Norad/XNE3"; + } + + return movieName; +} + +uint NoradAlpha::getNumHints() { + uint numHints = Neighborhood::getNumHints(); + + if (numHints == 0) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kNorad01, kNorth): + case MakeRoomView(kNorad01, kSouth): + case MakeRoomView(kNorad01, kEast): + case MakeRoomView(kNorad01, kWest): + case MakeRoomView(kNorad01East, kEast): + case MakeRoomView(kNorad01West, kWest): + if (GameState.getNoradGassed()) { + if (g_airMask->isAirFilterOn()) + numHints = 0; + else + numHints = 3; + } else { + numHints = 2; + } + break; + case MakeRoomView(kNorad19West, kWest): + if (getSubPrepFailed() && GameState.getNoradSubPrepState() != kSubPrepped) + numHints = 1; + break; + case MakeRoomView(kNorad22, kWest): + numHints = 1; + break; + } + } + + return numHints; +} + +Common::String NoradAlpha::getHintMovie(uint hintNum) { + Common::String movieName = Neighborhood::getHintMovie(hintNum); + + if (movieName.empty()) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kNorad01, kNorth): + case MakeRoomView(kNorad01, kSouth): + case MakeRoomView(kNorad01, kEast): + case MakeRoomView(kNorad01, kWest): + case MakeRoomView(kNorad01East, kEast): + case MakeRoomView(kNorad01West, kWest): + switch (hintNum) { + case 1: + if (GameState.getNoradGassed()) + return "Images/AI/Norad/XN01SW"; + + return "Images/AI/Norad/XN01WD2"; + case 2: + if (GameState.getNoradGassed()) { + if (_vm->playerHasItemID(kAirMask)) + // Mask must not be on if we get here... + return "Images/AI/Globals/XGLOB1A"; + + return "Images/AI/Globals/XGLOB3D"; + } + + return "Images/AI/Globals/XGLOB5C"; + case 3: + return "Images/AI/Norad/XN01SH"; + } + break; + case MakeRoomView(kNorad19West, kWest): + return "Images/AI/Norad/XN19NH"; + case MakeRoomView(kNorad22, kWest): + return "Images/AI/Globals/XGLOB1C"; + } + } + + return movieName; +} + +void NoradAlpha::closeDoorOffScreen(const RoomID room, const DirectionConstant) { + switch (room) { + case kNorad12: + case kNorad13: + case kNorad18: + case kNorad19: + playSpotSoundSync(kAlphaElevatorDoorCloseIn, kAlphaElevatorDoorCloseOut); + break; + default: + playSpotSoundSync(kAlphaRegDoorCloseIn, kAlphaRegDoorCloseOut); + break; + } +} + +void NoradAlpha::findSpotEntry(const RoomID room, const DirectionConstant direction, SpotFlags flags, SpotTable::Entry &spotEntry) { + if (room == kNorad01 && direction == kSouth) + spotEntry.clear(); + else + Norad::findSpotEntry(room, direction, flags, spotEntry); +} + +bool NoradAlpha::canSolve() { + return Norad::canSolve() || getHintMovie(1) == "Images/AI/Norad/XN01SW"; +} + +void NoradAlpha::doSolve() { + Norad::doSolve(); + + if (getHintMovie(1) == "Images/AI/Norad/XN01SW") { + _vm->addItemToInventory(g_airMask); + g_airMask->putMaskOn(); + } +} + +Common::String NoradAlpha::getNavMovieName() { + return "Images/Norad Alpha/Norad Alpha.movie"; +} + +Common::String NoradAlpha::getSoundSpotsName() { + return "Sounds/Norad/Norad Alpha Spots"; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/norad/alpha/noradalpha.h b/engines/pegasus/neighborhood/norad/alpha/noradalpha.h new file mode 100644 index 0000000000..582d6c2bb3 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/alpha/noradalpha.h @@ -0,0 +1,115 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_NORADALPHA_H +#define PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_NORADALPHA_H + +#include "pegasus/neighborhood/norad/norad.h" + +namespace Pegasus { + +class Item; + +class NoradAlpha : public Norad { +public: + NoradAlpha(InputHandler *, PegasusEngine *); + virtual ~NoradAlpha() {} + + virtual void init(); + void start(); + + virtual bool okayToJump(); + + void playClawMonitorIntro(); + + void getExtraCompassMove(const ExtraTable::Entry &, FaderMoveSpec &); + + void turnOnFillingStation(); + void turnOffFillingStation(); + Item *getFillingItem() { return _fillingStationItem; } + bool gasCanisterIntake(); + + virtual void takeItemFromRoom(Item *); + virtual void dropItemIntoRoom(Item *, Hotspot *); + + virtual GameInteraction *makeInteraction(const InteractionID); + + virtual void getClawInfo(HotSpotID &outSpotID, HotSpotID &prepSpotID, HotSpotID &clawControlSpotID, + HotSpotID &pinchClawSpotID, HotSpotID &moveClawDownSpotID, HotSpotID &moveClawRightSpotID, + HotSpotID &moveClawLeftSpotID, HotSpotID &moveClawUpSpotID, HotSpotID &clawCCWSpotID, + HotSpotID &clawCWSpotID, uint32 &, const uint32 *&); + + void loadAmbientLoops(); + + Common::String getEnvScanMovie(); + uint getNumHints(); + Common::String getHintMovie(uint); + void setUpAIRules(); + + void setSubPrepFailed(bool value) { _subPrepFailed = value; } + bool getSubPrepFailed() { return _subPrepFailed; } + + void closeDoorOffScreen(const RoomID, const DirectionConstant); + void findSpotEntry(const RoomID, const DirectionConstant, SpotFlags, SpotTable::Entry &); + void clickInHotspot(const Input &, const Hotspot *); + + void checkContinuePoint(const RoomID, const DirectionConstant); + + bool canSolve(); + void doSolve(); + +protected: + static const uint32 _noradAlphaClawExtras[22]; + + virtual void arriveAtNorad01(); + virtual void arriveAtNorad01East(); + virtual void arriveAtNorad01West(); + virtual void arriveAtNorad04(); + virtual void arriveAtNorad22(); + + virtual void arriveAt(const RoomID, const DirectionConstant); + + virtual void getZoomEntry(const HotSpotID, ZoomTable::Entry &); + virtual TimeValue getViewTime(const RoomID, const DirectionConstant); + + virtual void receiveNotification(Notification *, const NotificationFlags); + + virtual void activateHotspots(); + + Hotspot *getItemScreenSpot(Item *, DisplayElement *); + + void bumpIntoWall(); + + Item *_fillingStationItem; + + bool _subPrepFailed; + + Common::String getSoundSpotsName(); + Common::String getNavMovieName(); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/norad/alpha/panorama.cpp b/engines/pegasus/neighborhood/norad/alpha/panorama.cpp new file mode 100644 index 0000000000..5a717a84e7 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/alpha/panorama.cpp @@ -0,0 +1,239 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "common/macresman.h" +#include "common/stream.h" +#include "pegasus/neighborhood/norad/alpha/panorama.h" + +namespace Pegasus { + +Panorama::Panorama() : _panoramaMovie(kNoDisplayElement) { + blankFields(); +} + +Panorama::~Panorama() { + releasePanorama(); +} + +void Panorama::blankFields() { + _viewBounds = Common::Rect(); + _drawBounds = Common::Rect(); + _mask = 0; + _panoramaWidth = 0; + _panoramaHeight = 0; + _stripWidth = 0; + _stripLeft = -1; + _stripRight = -1; +} + +void Panorama::releasePanorama() { + if (_panoramaMovie.isMovieValid()) { + _panoramaMovie.releaseMovie(); + _panoramaWorld.deallocateSurface(); + blankFields(); + } +} + +static const uint32 kPanoramaResType = MKTAG('P', 'a', 'n', 'I'); // Panorama Information. +static const uint16 kPanoramaResID = 128; + +void Panorama::initFromMovieFile(const Common::String &fileName) { + // First, we need the resource fork for other reasons -- PanI resource + Common::MacResManager *resFork = new Common::MacResManager(); + if (!resFork->open(fileName) || !resFork->hasResFork()) + error("Could not open the resource fork of '%s'", fileName.c_str()); + + Common::SeekableReadStream *resource = resFork->getResource(kPanoramaResType, kPanoramaResID); + if (!resource) + error("No panorama information in the resource fork of '%s'", fileName.c_str()); + + _panoramaWidth = resource->readUint16BE(); + _panoramaHeight = resource->readUint16BE(); + _stripWidth = resource->readUint16BE(); + + delete resource; + delete resFork; + + // Now we open the movie like normal + _panoramaMovie.initFromMovieFile(fileName); +} + +void Panorama::setMask(Surface *mask) { + _mask = mask; +} + +// If the panorama is not open, do nothing and return. +// Otherwise, set up the view bounds. +void Panorama::setViewBounds(const Common::Rect &newView) { + if (!isPanoramaOpen()) + return; + + if (newView.isEmpty()) + return; + + Common::Rect r = newView; + + if (r.width() > _panoramaWidth) { + r.left = 0; + r.right = _panoramaWidth; + } else { + if (r.right > _panoramaWidth) + r.translate(_panoramaWidth - r.right, 0); + + if (r.left < 0) + r.translate(-r.left, 0); + } + + if (r.height() > _panoramaHeight) { + r.top = 0; + r.bottom = _panoramaHeight; + } else { + if (r.bottom > _panoramaHeight) + r.translate(0, _panoramaHeight - r.bottom); + + if (r.top < 0) + r.translate(0, -r.top); + } + + if (_viewBounds != r) { + CoordType stripLeft = 0; + + if (r.width() != _viewBounds.width() || !_panoramaWorld.isSurfaceValid()) { + _panoramaWorld.deallocateSurface(); + makeNewSurface(r); + } else { + CoordType stripRight; + calcStripRange(r, stripLeft, stripRight); + loadStrips(stripLeft, stripRight); + } + + _viewBounds = r; + _drawBounds = r; + _drawBounds.translate(-stripLeft * _stripWidth, 0); + } +} + +void Panorama::getViewBounds(Common::Rect &r) const { + r = _viewBounds; +} + +void Panorama::getPanoramaBounds(Common::Rect &r) const { + r = Common::Rect(0, 0, _panoramaWidth, _panoramaHeight); +} + +void Panorama::drawPanorama(const Common::Rect &destRect) { + if (_panoramaWorld.isSurfaceValid()) { + if (_mask) + _panoramaWorld.copyToCurrentPortMasked(_drawBounds, destRect, _mask); + else + _panoramaWorld.copyToCurrentPortTransparent(_drawBounds, destRect); + } +} + +// Make a new Surface big enough to show r, which is assumed to be a valid view bounds. +// Assumptions: +// r is a valid view bounds. +// _panoramaWorld is not allocated. +// _panoramaHeight, _stripWidth is correct. +// _panoramaMovie is allocated. +void Panorama::makeNewSurface(const Common::Rect& view) { + CoordType stripLeft, stripRight; + calcStripRange(view, stripLeft, stripRight); + + Common::Rect r(0, 0, (stripRight - stripLeft + 1) * _stripWidth, _panoramaHeight); + _panoramaWorld.allocateSurface(r); + _panoramaMovie.shareSurface(&_panoramaWorld); + loadStrips(stripLeft, stripRight); +} + +// Assumes view is not empty. +void Panorama::calcStripRange(const Common::Rect &view, CoordType &stripLeft, CoordType &stripRight) { + stripLeft = view.left / _stripWidth; + stripRight = (view.left - view.left % _stripWidth + _stripWidth - 1 + view.width()) / _stripWidth; +} + +// Load in all needed strips to put range (stripLeft, stripRight) into the +// panorama's Surface. Try to optimize by saving any pixels already in the Surface. +// Assumptions: +// Surface is allocated and is big enough for maximum range of +// stripLeft and stripRight +void Panorama::loadStrips(CoordType stripLeft, CoordType stripRight) { + if (_stripLeft == -1) { + // Surface has just been allocated. + // Load in all strips. + for (CoordType i = stripLeft; i <= stripRight; i++) + loadOneStrip(i, stripLeft); + + _stripLeft = stripLeft; + _stripRight = stripRight; + } else if (stripLeft != _stripLeft) { + CoordType overlapLeft = MAX(stripLeft, _stripLeft); + CoordType overlapRight = MIN(stripRight, _stripRight); + + if (overlapLeft <= overlapRight) { + Common::Rect r1((overlapLeft - _stripLeft) * _stripWidth, 0, + (overlapRight - _stripLeft + 1) * _stripWidth, _panoramaHeight); + + if (stripLeft < _stripLeft) { + Common::Rect bounds; + _panoramaWorld.getSurfaceBounds(bounds); + _panoramaWorld.getSurface()->move(bounds.right - r1.right, 0, _panoramaHeight); + + for (CoordType i = stripLeft; i < _stripLeft; i++) + loadOneStrip(i, stripLeft); + } else { + _panoramaWorld.getSurface()->move(-r1.left, 0, _panoramaHeight); + + for (CoordType i = _stripRight + 1; i <= stripRight; i++) + loadOneStrip(i, stripLeft); + } + } else { + // No overlap. + // Load everything. + for (CoordType i = stripLeft; i <= stripRight; i++) + loadOneStrip(i, stripLeft); + } + + _stripLeft = stripLeft; + _stripRight = stripRight; + } else if (stripRight > _stripRight) { + // Need to add one or more strips. + for (CoordType i = _stripRight + 1; i <= stripRight; i++) + loadOneStrip(i, _stripLeft); + + _stripRight = stripRight; + } else if (stripRight < _stripRight) { + // Need to chop off one strip. + _stripRight = stripRight; + } +} + +void Panorama::loadOneStrip(CoordType stripToLoad, CoordType leftStrip) { + _panoramaMovie.moveMovieBoxTo((stripToLoad - leftStrip) * _stripWidth, 0); + _panoramaMovie.setTime(stripToLoad, 1); + _panoramaMovie.redrawMovieWorld(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/norad/alpha/panorama.h b/engines/pegasus/neighborhood/norad/alpha/panorama.h new file mode 100644 index 0000000000..87c7b3bd4e --- /dev/null +++ b/engines/pegasus/neighborhood/norad/alpha/panorama.h @@ -0,0 +1,98 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_PANORAMA_H +#define PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_PANORAMA_H + +#include "pegasus/movie.h" + +namespace Pegasus { + +/* + + Panorama implements a wide image using a specially constructed movie file. + The movie holds the image as a series of vertical strips, say 16 or 32 pixels wide. + + The panorama bounds defines the entire panorama. The view bounds represents the + area on the panorama that is kept in memory. + + The panorama bounds is also stored in the movie file; it cannot be changed. The + view bounds must always be a subset of the panorama bounds. + + In actuality, the area kept in memory is at least as wide as the view bounds (but + may be wider to coincide with the width of the movies slices), and is as tall as + the panorama bounds. The view bounds is used by the drawPanorama function to draw + a piece of the panorama to the current screen. + + The panorama movie is built at a time scale of 1, with each strip lasting for one + second, so that strip number corresponds exactly with the time value at which the + strip is stored. + + TO USE: + + Call one initFromMovieFile to open the movie. Then set up a view rect by + calling setViewBounds. Once these two functions have been called, drawPanorama + will draw the panorama. + +*/ + +class Panorama { +public: + Panorama(); + virtual ~Panorama(); + + void initFromMovieFile(const Common::String &); + void releasePanorama(); + bool isPanoramaOpen() { return _panoramaMovie.isMovieValid(); } + + void setViewBounds(const Common::Rect &); + void getViewBounds(Common::Rect &) const; + + void setMask(Surface *); + + void getPanoramaBounds(Common::Rect &) const; + + void drawPanorama(const Common::Rect &); + +protected: + void blankFields(); + void makeNewSurface(const Common::Rect &); + void calcStripRange(const Common::Rect &, CoordType &, CoordType &); + void loadStrips(CoordType, CoordType); + void loadOneStrip(CoordType, CoordType); + + Movie _panoramaMovie; + Surface _panoramaWorld, *_mask; + Common::Rect _viewBounds; + Common::Rect _drawBounds; + CoordType _panoramaWidth, _panoramaHeight; + CoordType _stripWidth; // Pixels per strip. + CoordType _numStrips; + CoordType _stripLeft, _stripRight; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/norad/alpha/panoramascroll.cpp b/engines/pegasus/neighborhood/norad/alpha/panoramascroll.cpp new file mode 100644 index 0000000000..7865bbb442 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/alpha/panoramascroll.cpp @@ -0,0 +1,91 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/neighborhood/norad/alpha/panoramascroll.h" + +namespace Pegasus { + +PanoramaScroll::PanoramaScroll(const DisplayElementID id) : IdlerAnimation(id) { + _boundsWidth = 0; + _totalWidth = 0; +} + +void PanoramaScroll::initFromMovieFile(const Common::String &fileName) { + _panorama.initFromMovieFile(fileName); + + Common::Rect r; + _panorama.getPanoramaBounds(r); + _totalWidth = r.width(); +} + +void PanoramaScroll::initMaskFromPICTFile(const Common::String &fileName) { + if (!_panorama.isPanoramaOpen()) + return; + + _mask.getImageFromPICTFile(fileName); + _panorama.setMask(&_mask); +} + +void PanoramaScroll::releasePanorama() { + if (_panorama.isPanoramaOpen()) + _panorama.releasePanorama(); + + _mask.deallocateSurface(); +} + +void PanoramaScroll::setBounds(const Common::Rect &r) { + Animation::setBounds(r); + + _boundsWidth = r.width(); + + Common::Rect r2; + _panorama.getViewBounds(r2); + r2.right = r2.left + _boundsWidth; + r2.bottom = r2.top + r.height(); + _panorama.setViewBounds(r2); +} + +void PanoramaScroll::draw(const Common::Rect &) { + _panorama.drawPanorama(_bounds); +} + +void PanoramaScroll::timeChanged(const TimeValue newTime) { + CoordType leftPixel = (_totalWidth - _boundsWidth) * newTime / getDuration(); + + Common::Rect r; + _panorama.getViewBounds(r); + if (leftPixel != r.left) { + _panorama.getViewBounds(r); + r.moveTo(leftPixel, 0); + _panorama.setViewBounds(r); + triggerRedraw(); + } +} + +void PanoramaScroll::getPanoramaBounds(Common::Rect &r) const { + _panorama.getPanoramaBounds(r); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/norad/alpha/panoramascroll.h b/engines/pegasus/neighborhood/norad/alpha/panoramascroll.h new file mode 100644 index 0000000000..6a3e1515e2 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/alpha/panoramascroll.h @@ -0,0 +1,60 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_PANORAMASCROLL_H +#define PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_PANORAMASCROLL_H + +#include "pegasus/neighborhood/norad/alpha/panorama.h" + +namespace Pegasus { + +class PanoramaScroll : public IdlerAnimation { +public: + PanoramaScroll(const DisplayElementID); + virtual ~PanoramaScroll() {} + + void initFromMovieFile(const Common::String &); + void initMaskFromPICTFile(const Common::String &); + + void releasePanorama(); + + bool isPanoramaOpen() { return _panorama.isPanoramaOpen(); } + void getPanoramaBounds(Common::Rect &) const; + + void setBounds(const Common::Rect&); + + void draw(const Common::Rect &); + +protected: + void timeChanged(const TimeValue); + + Panorama _panorama; + Surface _mask; + CoordType _totalWidth, _boundsWidth; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/norad/constants.h b/engines/pegasus/neighborhood/norad/constants.h new file mode 100644 index 0000000000..37c1769309 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/constants.h @@ -0,0 +1,755 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_NEIGHBORHOOD_NORAD_CONSTANTS_H +#define PEGASUS_NEIGHBORHOOD_NORAD_CONSTANTS_H + +#include "pegasus/constants.h" + +namespace Pegasus { + +// Norad Alpha spot constants + +static const TimeValue kAlphaBumpIntoWallIn = 0; +static const TimeValue kAlphaBumpIntoWallOut = 303; + +static const TimeValue kAlphaAccessDeniedIn = 303; +static const TimeValue kAlphaAccessDeniedOut = 3045; + +static const TimeValue kAlphaRegDoorCloseIn = 3045; +static const TimeValue kAlphaRegDoorCloseOut = 4476; + +static const TimeValue kAlphaElevatorDoorCloseIn = 4476; +static const TimeValue kAlphaElevatorDoorCloseOut = 5071; + +static const TimeValue kAlphaCantTransportIn = 5071; +static const TimeValue kAlphaCantTransportOut = 9348; + +static const TimeValue kAlphaPressureDoorIntro1In = 9348; +static const TimeValue kAlphaPressureDoorIntro1Out = 11061; + +static const TimeValue kAlphaPressureDoorIntro2In = 11061; +static const TimeValue kAlphaPressureDoorIntro2Out = 14098; + +static const TimeValue kN22ReplyIn = 14098; +static const TimeValue kN22ReplyOut = 18442; + +static const TimeValue kAlphaLoadClawIntroIn = 18442; +static const TimeValue kAlphaLoadClawIntroOut = 20698; + +// Norad Delta spot constants + +static const TimeValue kDeltaBumpIntoWallIn = 0; +static const TimeValue kDeltaBumpIntoWallOut = 303; + +static const TimeValue kDeltaAccessDeniedIn = 303; +static const TimeValue kDeltaAccessDeniedOut = 3045; + +static const TimeValue kDeltaRegDoorCloseIn = 3045; +static const TimeValue kDeltaRegDoorCloseOut = 4476; + +static const TimeValue kDeltaElevatorDoorCloseIn = 4476; +static const TimeValue kDeltaElevatorDoorCloseOut = 5071; + +static const TimeValue kPressureDoorIntro1In = 5071; +static const TimeValue kPressureDoorIntro1Out = 6784; + +static const TimeValue kPressureDoorIntro2In = 6784; +static const TimeValue kPressureDoorIntro2Out = 9821; + +static const TimeValue kLoadClawIntroIn = 9821; +static const TimeValue kLoadClawIntroOut = 12077; + +static const TimeValue kHoldForRetinalIn = 12077; +static const TimeValue kHoldForRetinalOut = 14104; + +static const TimeValue kRetinalScanFailedIn = 14104; +static const TimeValue kRetinalScanFailedOut = 17538; + +static const TimeValue kAddisAbabaIn = 17538; +static const TimeValue kAddisAbabaOut = 19263; + +static const TimeValue kBangkokIn = 19263; +static const TimeValue kBangkokOut = 20201; + +static const TimeValue kBonnIn = 20201; +static const TimeValue kBonnOut = 20915; + +static const TimeValue kDublinIn = 20915; +static const TimeValue kDublinOut = 21660; + +static const TimeValue kHonoluluIn = 21660; +static const TimeValue kHonoluluOut = 22498; + +static const TimeValue kMadridIn = 22498; +static const TimeValue kMadridOut = 23474; + +static const TimeValue kReykjavikIn = 23474; +static const TimeValue kReykjavikOut = 24488; + +static const TimeValue kSanAntonioIn = 24488; +static const TimeValue kSanAntonioOut = 25561; + +static const TimeValue kSeoulIn = 25561; +static const TimeValue kSeoulOut = 26461; + +static const TimeValue kSvortalskIn = 26461; +static const TimeValue kSvortalskOut = 27582; + +static const TimeValue kSiloBeepIn = 27582; +static const TimeValue kSiloBeepOut = 27721; + +static const TimeValue kAllSilosDeactivatedIn = 27721; +static const TimeValue kAllSilosDeactivatedOut = 28928; + +static const TimeValue kGlobalLaunchOverrideIn = 28928; +static const TimeValue kGlobalLaunchOverrideOut = 30736; + +static const TimeValue kLaunchSiloSelectedIn = 30736; +static const TimeValue kLaunchSiloSelectedOut = 31660; + +static const TimeValue kLaunchToProceedIn = 31660; +static const TimeValue kLaunchToProceedOut = 32536; + +static const TimeValue kMaximumDeactivationIn = 32536; +static const TimeValue kMaximumDeactivationOut = 34337; + +static const TimeValue kMissileLaunchedIn = 34337; +static const TimeValue kMissileLaunchedOut = 35082; + +static const TimeValue kNewLaunchSiloIn = 35082; +static const TimeValue kNewLaunchSiloOut = 36320; + +static const TimeValue kStrikeAuthorizedIn = 36320; +static const TimeValue kStrikeAuthorizedOut = 37393; + +static const TimeValue kPrimaryTargetIn = 37393; +static const TimeValue kPrimaryTargetOut = 38628; + +static const TimeValue kSiloDeactivatedIn = 38628; +static const TimeValue kSiloDeactivatedOut = 39566; + +static const TimeValue kStrikeCodeRejectedIn = 39566; +static const TimeValue kStrikeCodeRejectedOut = 41056; + +static const TimeValue kToDeactivateIn = 41056; +static const TimeValue kToDeactivateOut = 46494; + +static const TimeValue kTwoMinutesIn = 46494; +static const TimeValue kTwoMinutesOut = 47166; + +static const TimeValue kOneMinuteIn = 47166; +static const TimeValue kOneMinuteOut = 47856; + +static const TimeValue kFiftySecondsIn = 47856; +static const TimeValue kFiftySecondsOut = 48691; + +static const TimeValue kFortySecondsIn = 48691; +static const TimeValue kFortySecondsOut = 49500; + +static const TimeValue kThirtySecondsIn = 49500; +static const TimeValue kThirtySecondsOut = 50362; + +static const TimeValue kTwentySecondsIn = 50362; +static const TimeValue kTwentySecondsOut = 51245; + +static const TimeValue kTenSecondsIn = 51245; +static const TimeValue kTenSecondsOut = 52069; + +static const TimeValue kGiveUpHumanIn = 52069; +static const TimeValue kGiveUpHumanOut = 55023; + +static const TimeValue kIJustBrokeIn = 55023; +static const TimeValue kIJustBrokeOut = 59191; + +static const TimeValue kTheOnlyGoodHumanIn = 59191; +static const TimeValue kTheOnlyGoodHumanOut = 62379; + +static const TimeValue kYouAreRunningIn = 62379; +static const TimeValue kYouAreRunningOut = 64201; + +static const TimeValue kYouCannotPossiblyIn = 64201; +static const TimeValue kYouCannotPossiblyOut = 65740; + +static const TimeValue kYouWillFailIn = 65740; +static const TimeValue kYouWillFailOut = 67217; + +static const CanOpenDoorReason kCantOpenBadPressure = kCantOpenLastReason + 1; + +static const NotificationFlags kAirTimerExpiredFlag = kLastNeighborhoodNotificationFlag << 1; + +static const uint16 kNoradWarningVolume = 0x100 / 3; +static const uint16 kNoradSuckWindVolume = 0x100 / 2; + +static const int16 kElevatorCompassAngle = -40; +static const int16 kSubPlatformCompassAngle = 45; +static const int16 kSubControlCompassAngle = -10; + +// Norad interactions. + +static const InteractionID kNoradGlobeGameInteractionID = 0; +static const InteractionID kNoradECRMonitorInteractionID = 1; +static const InteractionID kNoradFillingStationInteractionID = 2; +static const InteractionID kNoradElevatorInteractionID = 3; +static const InteractionID kNoradPressureDoorInteractionID = 4; +static const InteractionID kNoradSubControlRoomInteractionID = 5; +static const InteractionID kNoradSubPlatformInteractionID = 6; + +///////////////////////////////////////////// +// +// Norad Alpha + +static const CoordType kECRSlideShowLeft = kNavAreaLeft + 78; +static const CoordType kECRSlideShowTop = kNavAreaTop + 1; + +static const CoordType kECRPanLeft = kNavAreaLeft + 78 + 5; +static const CoordType kECRPanTop = kNavAreaTop + 1 + 4; +static const CoordType kECRPanRight = kECRPanLeft + 213; +static const CoordType kECRPanBottom = kECRPanTop + 241; + +static const CoordType kNoradAlphaElevatorControlsLeft = kNavAreaLeft + 332; +static const CoordType kNoradAlphaElevatorControlsTop = kNavAreaTop + 127; + +static const CoordType kNoradAlpha01LeftSideLeft = kNavAreaLeft + 0; +static const CoordType kNoradAlpha01LeftSideTop = kNavAreaTop + 0; + +static const CoordType kNoradAlpha01RightSideLeft = kNavAreaLeft + 240; +static const CoordType kNoradAlpha01RightSideTop = kNavAreaTop + 12; + +static const CoordType kNoradUpperLevelsLeft = kNavAreaLeft + 98; +static const CoordType kNoradUpperLevelsTop = kNavAreaTop + 31; + +static const CoordType kNoradUpperTypeLeft = kNoradUpperLevelsLeft + 114; +static const CoordType kNoradUpperTypeTop = kNoradUpperLevelsTop + 8; + +static const CoordType kNoradUpperUpLeft = kNavAreaLeft + 361; +static const CoordType kNoradUpperUpTop = kNavAreaTop + 32; + +static const CoordType kNoradUpperDownLeft = kNavAreaLeft + 367; +static const CoordType kNoradUpperDownTop = kNavAreaTop + 66; + +static const CoordType kNoradLowerLevelsLeft = kNavAreaLeft + 74; +static const CoordType kNoradLowerLevelsTop = kNavAreaTop + 157; + +static const CoordType kNoradLowerTypeLeft = kNoradLowerLevelsLeft + 144; +static const CoordType kNoradLowerTypeTop = kNoradLowerLevelsTop + 9; + +static const CoordType kNoradLowerUpLeft = kNavAreaLeft + 380; +static const CoordType kNoradLowerUpTop = kNavAreaTop + 164; + +static const CoordType kNoradLowerDownLeft = kNavAreaLeft + 388; +static const CoordType kNoradLowerDownTop = kNavAreaTop + 212; + +static const CoordType kNoradPlatformLeft = kNavAreaLeft + 36; +static const CoordType kNoradPlatformTop = kNavAreaTop + 87; + +static const CoordType kNoradSubControlLeft = kNavAreaLeft + 0; +static const CoordType kNoradSubControlTop = kNavAreaTop + 84; + +static const CoordType kNoradSubControlPinchLeft = kNoradSubControlLeft + 106; +static const CoordType kNoradSubControlPinchTop = kNoradSubControlTop + 86; + +static const CoordType kNoradSubControlDownLeft = kNoradSubControlLeft + 66; +static const CoordType kNoradSubControlDownTop = kNoradSubControlTop + 106; + +static const CoordType kNoradSubControlRightLeft = kNoradSubControlLeft + 83; +static const CoordType kNoradSubControlRightTop = kNoradSubControlTop + 90; + +static const CoordType kNoradSubControlLeftLeft = kNoradSubControlLeft + 56; +static const CoordType kNoradSubControlLeftTop = kNoradSubControlTop + 91; + +static const CoordType kNoradSubControlUpLeft = kNoradSubControlLeft + 66; +static const CoordType kNoradSubControlUpTop = kNoradSubControlTop + 81; + +static const CoordType kNoradSubControlCCWLeft = kNoradSubControlLeft + 29; +static const CoordType kNoradSubControlCCWTop = kNoradSubControlTop + 88; + +static const CoordType kNoradSubControlCWLeft = kNoradSubControlLeft + 0; +static const CoordType kNoradSubControlCWTop = kNoradSubControlTop + 89; + +static const CoordType kNoradClawMonitorLeft = kNavAreaLeft + 288; +static const CoordType kNoradClawMonitorTop = kNavAreaTop + 97; + +static const CoordType kNoradGreenBallAtALeft = kNoradClawMonitorLeft + 179; +static const CoordType kNoradGreenBallAtATop = kNoradClawMonitorTop + 82; + +static const CoordType kNoradGreenBallAtBLeft = kNoradClawMonitorLeft + 130; +static const CoordType kNoradGreenBallAtBTop = kNoradClawMonitorTop + 73; + +static const CoordType kNoradGreenBallAtCLeft = kNoradClawMonitorLeft + 110; +static const CoordType kNoradGreenBallAtCTop = kNoradClawMonitorTop + 26; + +static const CoordType kNoradGreenBallAtDLeft = kNoradClawMonitorLeft + 21; +static const CoordType kNoradGreenBallAtDTop = kNoradClawMonitorTop + 49; + +///////////////////////////////////////////// +// +// Norad Delta + +static const CoordType kGlobeMonitorLeft = kNavAreaLeft + 360; +static const CoordType kGlobeMonitorTop = kNavAreaTop + 144; + +static const CoordType kGlobeLeft = kNavAreaLeft + 172; +static const CoordType kGlobeTop = kNavAreaTop; + +static const CoordType kGlobeCircleLeftLeft = kNavAreaLeft + 186; +static const CoordType kGlobeCircleLeftTop = kNavAreaTop + 41; + +static const CoordType kGlobeCircleRightLeft = kNavAreaLeft + 321; +static const CoordType kGlobeCircleRightTop = kNavAreaTop + 41; + +static const CoordType kGlobeCircleUpLeft = kNavAreaLeft + 220; +static const CoordType kGlobeCircleUpTop = kNavAreaTop + 7; + +static const CoordType kGlobeCircleDownLeft = kNavAreaLeft + 220; +static const CoordType kGlobeCircleDownTop = kNavAreaTop + 142; + +static const CoordType kGlobeUpperLeftHiliteLeft = kNavAreaLeft + 207; +static const CoordType kGlobeUpperLeftHiliteTop = kNavAreaTop + 28; + +static const CoordType kGlobeUpperRightHiliteLeft = kNavAreaLeft + 307; +static const CoordType kGlobeUpperRightHiliteTop = kNavAreaTop + 28; + +static const CoordType kGlobeLowerLeftHiliteLeft = kNavAreaLeft + 207; +static const CoordType kGlobeLowerLeftHiliteTop = kNavAreaTop + 128; + +static const CoordType kGlobeLowerRightHiliteLeft = kNavAreaLeft + 307; +static const CoordType kGlobeLowerRightHiliteTop = kNavAreaTop + 128; + +static const CoordType kGlobeLeftMotionHiliteLeft = kNavAreaLeft + 182; +static const CoordType kGlobeLeftMotionHiliteTop = kNavAreaTop + 60; + +static const CoordType kGlobeRightMotionHiliteLeft = kNavAreaLeft + 331; +static const CoordType kGlobeRightMotionHiliteTop = kNavAreaTop + 60; + +static const CoordType kGlobeUpMotionHiliteLeft = kNavAreaLeft + 239; +static const CoordType kGlobeUpMotionHiliteTop = kNavAreaTop + 3; + +static const CoordType kGlobeDownMotionHiliteLeft = kNavAreaLeft + 239; +static const CoordType kGlobeDownMotionHiliteTop = kNavAreaTop + 152; + +static const CoordType kGlobeUpperNamesLeft = kNavAreaLeft + 368; +static const CoordType kGlobeUpperNamesTop = kNavAreaTop + 188; + +static const CoordType kGlobeLowerNamesLeft = kNavAreaLeft + 368; +static const CoordType kGlobeLowerNamesTop = kNavAreaTop + 212; + +static const CoordType kGlobeCountdownLeft = kNavAreaLeft + 478; +static const CoordType kGlobeCountdownTop = kNavAreaTop + 164; + +// Norad Alpha display IDs. + +static const DisplayElementID kECRSlideShowMovieID = kNeighborhoodDisplayID; +static const DisplayElementID kECRPanID = kECRSlideShowMovieID + 1; +static const DisplayElementID kNoradAlphaDeathMovieID = kECRPanID + 1; +static const DisplayElementID kNoradElevatorControlsID = kNoradAlphaDeathMovieID + 1; +static const DisplayElementID kN01LeftSideID = kNoradElevatorControlsID + 1; +static const DisplayElementID kN01RightSideID = kN01LeftSideID + 1; +static const DisplayElementID kPressureDoorLevelsID = kN01RightSideID + 1; +static const DisplayElementID kPressureDoorTypeID = kPressureDoorLevelsID + 1; +static const DisplayElementID kPressureDoorUpButtonID = kPressureDoorTypeID + 1; +static const DisplayElementID kPressureDoorDownButtonID = kPressureDoorUpButtonID + 1; +static const DisplayElementID kPlatformMonitorID = kPressureDoorDownButtonID + 1; +static const DisplayElementID kSubControlMonitorID = kPlatformMonitorID + 1; +static const DisplayElementID kClawMonitorID = kSubControlMonitorID + 1; +static const DisplayElementID kSubControlPinchID = kClawMonitorID + 1; +static const DisplayElementID kSubControlDownID = kSubControlPinchID + 1; +static const DisplayElementID kSubControlRightID = kSubControlDownID + 1; +static const DisplayElementID kSubControlLeftID = kSubControlRightID + 1; +static const DisplayElementID kSubControlUpID = kSubControlLeftID + 1; +static const DisplayElementID kSubControlCCWID = kSubControlUpID + 1; +static const DisplayElementID kSubControlCWID = kSubControlCCWID + 1; +static const DisplayElementID kClawMonitorGreenBallID = kSubControlCWID + 1; + +// Norad Delta display IDs. + +static const DisplayElementID kGlobeMonitorID = kNeighborhoodDisplayID; +static const DisplayElementID kGlobeMovieID = kGlobeMonitorID + 14; +static const DisplayElementID kGlobeCircleLeftID = kGlobeMovieID + 1; +static const DisplayElementID kGlobeCircleRightID = kGlobeCircleLeftID + 1; +static const DisplayElementID kGlobeCircleUpID = kGlobeCircleRightID + 1; +static const DisplayElementID kGlobeCircleDownID = kGlobeCircleUpID + 1; +static const DisplayElementID kMotionHiliteLeftID = kGlobeCircleDownID + 1; +static const DisplayElementID kMotionHiliteRightID = kMotionHiliteLeftID + 1; +static const DisplayElementID kMotionHiliteUpID = kMotionHiliteRightID + 1; +static const DisplayElementID kMotionHiliteDownID = kMotionHiliteUpID + 1; +static const DisplayElementID kTargetHiliteUpperLeftID = kMotionHiliteDownID + 1; +static const DisplayElementID kTargetHiliteUpperRightID = kTargetHiliteUpperLeftID + 1; +static const DisplayElementID kTargetHiliteLowerLeftID = kTargetHiliteUpperRightID + 1; +static const DisplayElementID kTargetHiliteLowerRightID = kTargetHiliteLowerLeftID + 1; +static const DisplayElementID kGlobeUpperNamesID = kTargetHiliteLowerRightID + 1; +static const DisplayElementID kGlobeLowerNamesID = kGlobeUpperNamesID + 1; +static const DisplayElementID kGlobeCountdownID = kGlobeLowerNamesID + 1; + +// Norad Alpha: + +static const DisplayOrder kECRMonitorOrder = kMonitorLayer; +static const DisplayOrder kECRPanOrder = kECRMonitorOrder + 1; + +static const DisplayOrder kN01LeftSideOrder = kMonitorLayer; +static const DisplayOrder kN01RightSideOrder = kN01LeftSideOrder + 1; + +static const DisplayOrder kElevatorControlsOrder = kMonitorLayer; + +static const DisplayOrder kPressureLevelsOrder = kMonitorLayer; +static const DisplayOrder kPressureTypeOrder = kPressureLevelsOrder + 1; +static const DisplayOrder kPressureUpOrder = kPressureTypeOrder + 1; +static const DisplayOrder kPressureDownOrder = kPressureUpOrder + 1; + +static const DisplayOrder kPlatformOrder = kMonitorLayer; + +static const DisplayOrder kSubControlOrder = kMonitorLayer; +static const DisplayOrder kClawMonitorOrder = kSubControlOrder + 1; +static const DisplayOrder kSubControlPinchOrder = kClawMonitorOrder + 1; +static const DisplayOrder kSubControlDownOrder = kSubControlPinchOrder + 1; +static const DisplayOrder kSubControlRightOrder = kSubControlDownOrder + 1; +static const DisplayOrder kSubControlLeftOrder = kSubControlRightOrder + 1; +static const DisplayOrder kSubControlUpOrder = kSubControlLeftOrder + 1; +static const DisplayOrder kSubControlCCWOrder = kSubControlUpOrder + 1; +static const DisplayOrder kSubControlCWOrder = kSubControlCCWOrder + 1; +static const DisplayOrder kClawMonitorGreenBallOrder = kSubControlCWOrder + 1; + +// Norad Delta: + +static const DisplayOrder kGlobeMonitorLayer = kMonitorLayer; +static const DisplayOrder kGlobeMovieLayer = kGlobeMonitorLayer + 1; +static const DisplayOrder kGlobeCircleLayer = kGlobeMovieLayer + 1; +static const DisplayOrder kGlobeHilitesLayer = kGlobeCircleLayer + 1; +static const DisplayOrder kGlobeUpperNamesLayer = kGlobeHilitesLayer + 1; +static const DisplayOrder kGlobeLowerNamesLayer = kGlobeUpperNamesLayer + 1; +static const DisplayOrder kGlobeCountdownLayer = kGlobeLowerNamesLayer + 1; + +// Norad Alpha Tables + +static const TimeScale kNoradAlphaMovieScale = 600; +static const TimeScale kNoradAlphaFramesPerSecond = 15; +static const TimeScale kNoradAlphaFrameDuration = 40; + +// Alternate IDs. + +static const AlternateID kAltNoradAlphaNormal = 0; + +// Room IDs. + +static const RoomID kNorad01 = 0; +static const RoomID kNorad01East = 1; +static const RoomID kNorad01West = 2; +static const RoomID kNorad02 = 3; +static const RoomID kNorad03 = 4; +static const RoomID kNorad04 = 5; +static const RoomID kNorad05 = 6; +static const RoomID kNorad06 = 7; +static const RoomID kNorad07 = 8; +static const RoomID kNorad07North = 9; +static const RoomID kNorad08 = 10; +static const RoomID kNorad09 = 11; +static const RoomID kNorad10 = 12; +static const RoomID kNorad10East = 13; +static const RoomID kNorad11 = 14; +static const RoomID kNorad11South = 15; +static const RoomID kNorad12 = 16; +static const RoomID kNorad12South = 17; +static const RoomID kNorad13 = 18; +static const RoomID kNorad14 = 19; +static const RoomID kNorad15 = 20; +static const RoomID kNorad16 = 21; +static const RoomID kNorad17 = 22; +static const RoomID kNorad18 = 23; +static const RoomID kNorad19 = 24; +static const RoomID kNorad19West = 25; +static const RoomID kNorad21 = 26; +static const RoomID kNorad21West = 27; +static const RoomID kNorad22 = 28; +static const RoomID kNorad22West = 29; + +// Hot Spot Activation IDs. + + +// Hot Spot IDs. + +static const HotSpotID kNorad01ECRSpotID = 5000; +static const HotSpotID kNorad01GasSpotID = 5001; +static const HotSpotID kNorad01ECROutSpotID = 5002; +static const HotSpotID kNorad01GasOutSpotID = 5003; +static const HotSpotID kNorad01MonitorSpotID = 5004; +static const HotSpotID kNorad01IntakeSpotID = 5005; +static const HotSpotID kNorad01DispenseSpotID = 5006; +static const HotSpotID kNorad01ArSpotID = 5007; +static const HotSpotID kNorad01CO2SpotID = 5008; +static const HotSpotID kNorad01HeSpotID = 5009; +static const HotSpotID kNorad01OSpotID = 5010; +static const HotSpotID kNorad01NSpotID = 5011; +static const HotSpotID kN01GasCanisterSpotID = 5012; +static const HotSpotID kN01ArgonCanisterSpotID = 5013; +static const HotSpotID kN01AirMaskSpotID = 5014; +static const HotSpotID kN01NitrogenCanisterSpotID = 5015; +static const HotSpotID kN01GasOutletSpotID = 5016; +static const HotSpotID kNorad07DoorSpotID = 5017; +static const HotSpotID kNorad07DoorOutSpotID = 5018; +static const HotSpotID kNorad10DoorSpotID = 5019; +static const HotSpotID kNorad10EastOutSpotID = 5020; +static const HotSpotID kAlphaUpperPressureDoorUpSpotID = 5021; +static const HotSpotID kAlphaUpperPressureDoorDownSpotID = 5022; +static const HotSpotID kNorad11ElevatorSpotID = 5023; +static const HotSpotID kNorad11ElevatorOutSpotID = 5024; +static const HotSpotID kNorad11ElevatorDownSpotID = 5025; +static const HotSpotID kNorad12ElevatorSpotID = 5026; +static const HotSpotID kNorad12ElevatorOutSpotID = 5027; +static const HotSpotID kNorad12ElevatorUpSpotID = 5028; +static const HotSpotID kNorad19MonitorSpotID = 5029; +static const HotSpotID kNorad19MonitorOutSpotID = 5030; +static const HotSpotID kNorad19ActivateMonitorSpotID = 5031; +static const HotSpotID kNorad21WestSpotID = 5032; +static const HotSpotID kNorad21WestOutSpotID = 5033; +static const HotSpotID kAlphaLowerPressureDoorUpSpotID = 5034; +static const HotSpotID kAlphaLowerPressureDoorDownSpotID = 5035; +static const HotSpotID kNorad22MonitorSpotID = 5036; +static const HotSpotID kNorad22MonitorOutSpotID = 5037; +static const HotSpotID kNorad22LaunchPrepSpotID = 5038; +static const HotSpotID kNorad22ClawControlSpotID = 5039; +static const HotSpotID kNorad22ClawPinchSpotID = 5040; +static const HotSpotID kNorad22ClawDownSpotID = 5041; +static const HotSpotID kNorad22ClawRightSpotID = 5042; +static const HotSpotID kNorad22ClawLeftSpotID = 5043; +static const HotSpotID kNorad22ClawUpSpotID = 5044; +static const HotSpotID kNorad22ClawCCWSpotID = 5045; +static const HotSpotID kNorad22ClawCWSpotID = 5046; + +// Extra sequence IDs. + +static const ExtraID kNoradArriveFromTSA = 0; +static const ExtraID kNorad01RobotTaunt = 1; +static const ExtraID kNorad01ZoomInWithGasCanister = 2; +static const ExtraID kN01WGasCanister = 3; +static const ExtraID kNorad01ZoomOutWithGasCanister = 4; +static const ExtraID kN01WZEmptyLit = 5; +static const ExtraID kN01WZGasCanisterDim = 6; +static const ExtraID kN01WZGasCanisterLit = 7; +static const ExtraID kN01WZArgonCanisterDim = 8; +static const ExtraID kN01WZArgonCanisterLit = 9; +static const ExtraID kN01WZAirMaskDim = 10; +static const ExtraID kN01WZAirMaskLit = 11; +static const ExtraID kN01WZNitrogenCanisterDim = 12; +static const ExtraID kN01WZNitrogenCanisterLit = 13; +static const ExtraID kNorad04EastDeath = 14; +static const ExtraID kNorad19PrepSub = 15; +static const ExtraID kNorad19ExitToSub = 16; +static const ExtraID kNorad22SouthIntro = 17; +static const ExtraID kNorad22SouthReply = 18; +static const ExtraID kNorad22SouthFinish = 19; +static const ExtraID kN22ClawFromAToB = 20; +static const ExtraID kN22ClawALoop = 21; +static const ExtraID kN22ClawAPinch = 22; +static const ExtraID kN22ClawACounterclockwise = 23; +static const ExtraID kN22ClawAClockwise = 24; +static const ExtraID kN22ClawFromBToA = 25; +static const ExtraID kN22ClawFromBToC = 26; +static const ExtraID kN22ClawFromBToD = 27; +static const ExtraID kN22ClawBLoop = 28; +static const ExtraID kN22ClawBPinch = 29; +static const ExtraID kN22ClawBCounterclockwise = 30; +static const ExtraID kN22ClawBClockwise = 31; +static const ExtraID kN22ClawFromCToB = 32; +static const ExtraID kN22ClawCLoop = 33; +static const ExtraID kN22ClawCPinch = 34; +static const ExtraID kN22ClawCCounterclockwise = 35; +static const ExtraID kN22ClawCClockwise = 36; +static const ExtraID kN22ClawFromDToB = 37; +static const ExtraID kN22ClawDLoop = 38; +static const ExtraID kN22ClawDPinch = 39; +static const ExtraID kN22ClawDCounterclockwise = 40; +static const ExtraID kN22ClawDClockwise = 41; + + +// Norad Delta Extra sequence IDs. + +static const ExtraID kArriveFromSubChase = 0; +static const ExtraID kN59ZoomWithRobot = 1; +static const ExtraID kN59RobotApproaches = 2; +static const ExtraID kN59RobotPunchLoop = 3; +static const ExtraID kN59PlayerWins1 = 4; +static const ExtraID kN59PlayerWins2 = 5; +static const ExtraID kN59RobotWins = 6; +static const ExtraID kN59RobotHeadOpens = 7; +static const ExtraID kN59Biochips111 = 8; +static const ExtraID kN59Biochips011 = 9; +static const ExtraID kN59Biochips101 = 10; +static const ExtraID kN59Biochips001 = 11; +static const ExtraID kN59Biochips110 = 12; +static const ExtraID kN59Biochips010 = 13; +static const ExtraID kN59Biochips100 = 14; +static const ExtraID kN59Biochips000 = 15; +static const ExtraID kN59RobotDisappears = 16; +static const ExtraID kN60ClawFromAToB = 17; +static const ExtraID kN60ClawALoop = 18; +static const ExtraID kN60ClawAPinch = 19; +static const ExtraID kN60ClawACounterclockwise = 20; +static const ExtraID kN60ClawAClockwise = 21; +static const ExtraID kN60ClawFromBToA = 22; +static const ExtraID kN60ClawFromBToC = 23; +static const ExtraID kN60ClawFromBToD = 24; +static const ExtraID kN60ClawBLoop = 25; +static const ExtraID kN60ClawBPinch = 26; +static const ExtraID kN60ClawBCounterclockwise = 27; +static const ExtraID kN60ClawBClockwise = 28; +static const ExtraID kN60ClawFromCToB = 29; +static const ExtraID kN60ClawCLoop = 30; +static const ExtraID kN60ClawCPinch = 31; +static const ExtraID kN60ClawCCounterclockwise = 32; +static const ExtraID kN60ClawCClockwise = 33; +static const ExtraID kN60ClawFromDToB = 34; +static const ExtraID kN60ClawDLoop = 35; +static const ExtraID kN60ClawDPinch = 36; +static const ExtraID kN60ClawDCounterclockwise = 37; +static const ExtraID kN60ClawDClockwise = 38; +static const ExtraID kN60RobotApproaches = 39; +static const ExtraID kN60FirstMistake = 40; +static const ExtraID kN60ArmActivated = 41; +static const ExtraID kN60SecondMistake = 42; +static const ExtraID kN60ArmToPositionB = 43; +static const ExtraID kN60ThirdMistake = 44; +static const ExtraID kN60ArmGrabsRobot = 45; +static const ExtraID kN60FourthMistake = 46; +static const ExtraID kN60ArmCarriesRobotToPositionA = 47; +static const ExtraID kN60PlayerFollowsRobotToDoor = 48; +static const ExtraID kN60RobotHeadOpens = 49; +static const ExtraID kN60Biochips111 = 50; +static const ExtraID kN60Biochips011 = 51; +static const ExtraID kN60Biochips101 = 52; +static const ExtraID kN60Biochips001 = 53; +static const ExtraID kN60Biochips110 = 54; +static const ExtraID kN60Biochips010 = 55; +static const ExtraID kN60Biochips100 = 56; +static const ExtraID kN60Biochips000 = 57; +static const ExtraID kN60RobotDisappears = 58; +static const ExtraID kNoradDeltaRetinalScanBad = 59; +static const ExtraID kNoradDeltaRetinalScanGood = 60; +static const ExtraID kN79BrightView = 61; + +// Norad Delta Tables + +static const TimeScale kNoradDeltaMovieScale = 600; +static const TimeScale kNoradDeltaFramesPerSecond = 15; +static const TimeScale kNoradDeltaFrameDuration = 40; + +// Alternate IDs. + +static const AlternateID kAltNoradDeltaNormal = 0; + +// Room IDs. + +static const RoomID kNorad41 = 0; +static const RoomID kNorad42 = 1; +static const RoomID kNorad43 = 2; +static const RoomID kNorad44 = 3; +static const RoomID kNorad45 = 4; +static const RoomID kNorad46 = 5; +static const RoomID kNorad47 = 6; +static const RoomID kNorad48 = 7; +static const RoomID kNorad48South = 8; +static const RoomID kNorad49 = 9; +static const RoomID kNorad49South = 10; +static const RoomID kNorad50 = 11; +static const RoomID kNorad50East = 12; +static const RoomID kNorad51 = 13; +static const RoomID kNorad52 = 14; +static const RoomID kNorad53 = 15; +static const RoomID kNorad54 = 16; +static const RoomID kNorad54North = 17; +static const RoomID kNorad55 = 18; +static const RoomID kNorad56 = 19; +static const RoomID kNorad57 = 20; +static const RoomID kNorad58 = 21; +static const RoomID kNorad59 = 22; +static const RoomID kNorad59West = 23; +static const RoomID kNorad60 = 24; +static const RoomID kNorad60West = 25; +static const RoomID kNorad61 = 26; +static const RoomID kNorad62 = 27; +static const RoomID kNorad63 = 28; +static const RoomID kNorad64 = 29; +static const RoomID kNorad65 = 30; +static const RoomID kNorad66 = 31; +static const RoomID kNorad67 = 32; +static const RoomID kNorad68 = 33; +static const RoomID kNorad68West = 34; +static const RoomID kNorad69 = 35; +static const RoomID kNorad78 = 36; +static const RoomID kNorad79 = 37; +static const RoomID kNorad79West = 38; + +// Hot Spot Activation IDs. + + +// Hot Spot IDs. + +static const HotSpotID kNorad48ElevatorSpotID = 5000; +static const HotSpotID kNorad48ElevatorOutSpotID = 5001; +static const HotSpotID kNorad48ElevatorUpSpotID = 5002; +static const HotSpotID kNorad49ElevatorSpotID = 5003; +static const HotSpotID kNorad49ElevatorOutSpotID = 5004; +static const HotSpotID kNorad49ElevatorDownSpotID = 5005; +static const HotSpotID kNorad50DoorSpotID = 5006; +static const HotSpotID kNorad50DoorOutSpotID = 5007; +static const HotSpotID kDeltaUpperPressureDoorUpSpotID = 5008; +static const HotSpotID kDeltaUpperPressureDoorDownSpotID = 5009; +static const HotSpotID kNorad54DoorSpotID = 5010; +static const HotSpotID kNorad54DoorOutSpotID = 5011; +static const HotSpotID kNorad59WestSpotID = 5012; +static const HotSpotID kNorad59WestOutSpotID = 5013; +static const HotSpotID kDeltaLowerPressureDoorUpSpotID = 5014; +static const HotSpotID kDeltaLowerPressureDoorDownSpotID = 5015; +static const HotSpotID kDelta59RobotHeadSpotID = 5016; +static const HotSpotID kDelta59RobotShieldBiochipSpotID = 5017; +static const HotSpotID kDelta59RobotOpMemBiochipSpotID = 5018; +static const HotSpotID kDelta59RobotRetinalBiochipSpotID = 5019; +static const HotSpotID kNorad60MonitorSpotID = 5020; +static const HotSpotID kNorad60MonitorOutSpotID = 5021; +static const HotSpotID kNorad60LaunchPrepSpotID = 5022; +static const HotSpotID kNorad60ClawControlSpotID = 5023; +static const HotSpotID kNorad60ClawPinchSpotID = 5024; +static const HotSpotID kNorad60ClawDownSpotID = 5025; +static const HotSpotID kNorad60ClawRightSpotID = 5026; +static const HotSpotID kNorad60ClawLeftSpotID = 5027; +static const HotSpotID kNorad60ClawUpSpotID = 5028; +static const HotSpotID kNorad60ClawCCWSpotID = 5029; +static const HotSpotID kNorad60ClawCWSpotID = 5030; +static const HotSpotID kDelta60RobotHeadSpotID = 5031; +static const HotSpotID kDelta60RobotShieldBiochipSpotID = 5032; +static const HotSpotID kDelta60RobotOpMemBiochipSpotID = 5033; +static const HotSpotID kDelta60RobotRetinalBiochipSpotID = 5034; +static const HotSpotID kNorad68WestSpotID = 5035; +static const HotSpotID kNorad68WestOutSpotID = 5036; +static const HotSpotID kNorad79WestSpotID = 5037; +static const HotSpotID kNorad79WestOutSpotID = 5038; +static const HotSpotID kNorad79SpinLeftSpotID = 5039; +static const HotSpotID kNorad79SpinRightSpotID = 5040; +static const HotSpotID kNorad79SpinUpSpotID = 5041; +static const HotSpotID kNorad79SpinDownSpotID = 5042; +static const HotSpotID kNorad79SiloAreaSpotID = 5043; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/norad/delta/globegame.cpp b/engines/pegasus/neighborhood/norad/delta/globegame.cpp new file mode 100644 index 0000000000..06e40c2b3a --- /dev/null +++ b/engines/pegasus/neighborhood/norad/delta/globegame.cpp @@ -0,0 +1,1062 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/cursor.h" +#include "pegasus/pegasus.h" +#include "pegasus/neighborhood/norad/constants.h" +#include "pegasus/neighborhood/norad/delta/globegame.h" +#include "pegasus/neighborhood/norad/delta/noraddelta.h" + +namespace Pegasus { + +static const TimeValue kDurationPerFrame = 600 / 15; +static const TimeValue kDurationPerRow = kNumLongSlices * kDurationPerFrame; +static const short kVerticalDuration = 16; + +GlobeTracker::GlobeTracker(Movie *globeMovie, Picture *leftHighlight, Picture *rightHighlight, + Picture *upHighlight, Picture *downHighlight) { + _globeMovie = globeMovie; + _leftHighlight = leftHighlight; + _rightHighlight = rightHighlight; + _upHighlight = upHighlight; + _downHighlight = downHighlight; +} + +void GlobeTracker::setTrackParameters(const Hotspot *trackSpot, GlobeTrackDirection direction) { + _trackSpot = trackSpot; + _trackDirection = direction; + + TimeValue time, newTime, start; + + switch (_trackDirection) { + case kTrackLeft: + time = _globeMovie->getTime(); + + if (((time / kDurationPerRow) & 1) == 0) { + start = (time / kDurationPerRow + 1) * kDurationPerRow; + newTime = start + kDurationPerRow - time % kDurationPerRow; + } else { + start = (time / kDurationPerRow) * kDurationPerRow; + newTime = time; + } + + _globeMovie->setSegment(start, start + kDurationPerRow); + + if (newTime != time) { + _globeMovie->setTime(newTime); + _globeMovie->redrawMovieWorld(); + } + + _globeMovie->setFlags(kLoopTimeBase); + break; + case kTrackRight: + time = _globeMovie->getTime(); + + if (((time / kDurationPerRow) & 1) == 0) { + start = (time / kDurationPerRow) * kDurationPerRow; + newTime = time; + } else { + start = (time / kDurationPerRow - 1) * kDurationPerRow; + newTime = start + kDurationPerRow - time % kDurationPerRow; + } + + _globeMovie->setSegment(start, start + kDurationPerRow); + + if (newTime != time) { + _globeMovie->setTime(newTime); + _globeMovie->redrawMovieWorld(); + } + + _globeMovie->setFlags(kLoopTimeBase); + break; + case kTrackUp: + case kTrackDown: + _globeMovie->setSegment(0, _globeMovie->getDuration()); + _globeMovie->setFlags(0); + break; + } +} + +void GlobeTracker::activateHotspots() { + Tracker::activateHotspots(); + + if (_trackSpot) + g_allHotspots.activateOneHotspot(_trackSpot->getObjectID()); +} + +bool GlobeTracker::stopTrackingInput(const Input &input) { + return !JMPPPInput::isPressingInput(input); +} + +void GlobeTracker::continueTracking(const Input &input) { + Common::Point where; + input.getInputLocation(where); + + if (g_allHotspots.findHotspot(where) == _trackSpot) + trackGlobeMovie(); + else + stopGlobeMovie(); +} + +void GlobeTracker::startTracking(const Input &input) { + Tracker::startTracking(input); + trackGlobeMovie(); +} + +void GlobeTracker::stopTracking(const Input &input) { + Tracker::stopTracking(input); + stopGlobeMovie(); +} + +void GlobeTracker::trackGlobeMovie() { + TimeValue time; + + switch (_trackDirection) { + case kTrackLeft: + if (!_globeMovie->isRunning()) + _globeMovie->start(); + + _leftHighlight->show(); + break; + case kTrackRight: + if (!_globeMovie->isRunning()) + _globeMovie->start(); + + _rightHighlight->show(); + break; + case kTrackUp: + time = _globeMovie->getTime(); + + if (_trackTime == 0) { + _trackTime = tickCount(); + } else if ((int)time - (int)kDurationPerRow * 2 >= 0 && (int)tickCount() >= _trackTime + kVerticalDuration) { + _trackTime = tickCount(); + _globeMovie->setTime(time - kDurationPerRow * 2); + _globeMovie->redrawMovieWorld(); + } + + _upHighlight->show(); + break; + case kTrackDown: + time = _globeMovie->getTime(); + + if (_trackTime == 0) { + _trackTime = tickCount(); + } else if (time + kDurationPerRow * 2 < _globeMovie->getDuration() && (int)tickCount() >= _trackTime + kVerticalDuration) { + _trackTime = tickCount(); + _globeMovie->setTime(time + kDurationPerRow * 2); + _globeMovie->redrawMovieWorld(); + } + + _downHighlight->show(); + break; + } +} + +void GlobeTracker::stopGlobeMovie() { + switch (_trackDirection) { + case kTrackLeft: + _leftHighlight->hide(); + _globeMovie->stop(); + break; + case kTrackRight: + _rightHighlight->hide(); + _globeMovie->stop(); + break; + case kTrackUp: + _upHighlight->hide(); + _trackTime = tickCount() - kVerticalDuration; + break; + case kTrackDown: + _downHighlight->hide(); + _trackTime = tickCount() - kVerticalDuration; + break; + } +} + +// Globe game PICTs: +static const ResIDType kGlobeCircleLeftPICTID = 300; +static const ResIDType kGlobeCircleRightPICTID = 301; +static const ResIDType kGlobeCircleUpPICTID = 302; +static const ResIDType kGlobeCircleDownPICTID = 303; +static const ResIDType kTargetUpperLeftPICTID = 304; +static const ResIDType kTargetUpperRightPICTID = 305; +static const ResIDType kTargetLowerLeftPICTID = 306; +static const ResIDType kTargetLowerRightPICTID = 307; +static const ResIDType kMotionHiliteLeftPICTID = 308; +static const ResIDType kMotionHiliteRightPICTID = 309; +static const ResIDType kMotionHiliteUpPICTID = 310; +static const ResIDType kMotionHiliteDownPICTID = 311; + +static const ResIDType kGlobeCountdownDigitsID = 350; + +static const int kGlobeCountdownWidth = 28; +static const int kGlobeCountdownHeight = 12; +static const int kGlobeCountdownOffset1 = 12; +static const int kGlobeCountdownOffset2 = 20; + +GlobeCountdown::GlobeCountdown(const DisplayElementID id) : IdlerAnimation(id) { + _digits.getImageFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kGlobeCountdownDigitsID); + + Common::Rect r; + _digits.getSurfaceBounds(r); + _digitOffset = r.width() / 10; + setScale(1); + sizeElement(kGlobeCountdownWidth, kGlobeCountdownHeight); +} + +void GlobeCountdown::setDisplayOrder(const DisplayOrder order) { + IdlerAnimation::setDisplayOrder(order); +} + +void GlobeCountdown::show() { + IdlerAnimation::show(); +} + +void GlobeCountdown::hide() { + IdlerAnimation::hide(); +} + +void GlobeCountdown::moveElementTo(const CoordType x, const CoordType y) { + IdlerAnimation::moveElementTo(x, y); +} + +void GlobeCountdown::setCountdownTime(const int numSeconds) { + stop(); + setSegment(0, numSeconds); + setTime(numSeconds); +} + +void GlobeCountdown::startCountdown() { + setRate(-1); +} + +void GlobeCountdown::stopCountdown() { + stop(); +} + +void GlobeCountdown::draw(const Common::Rect &) { + Common::Rect r1; + _digits.getSurfaceBounds(r1); + r1.right = r1.left + _digitOffset; + Common::Rect r2 = r1; + TimeValue time = getTime(); + + Common::Rect bounds; + getBounds(bounds); + + if (time > 60 * 9 + 59) { + r2.moveTo(bounds.left, bounds.top); + r1.moveTo(9 * _digitOffset, 0); + _digits.copyToCurrentPort(r1, r2); + + r2.moveTo(bounds.left + kGlobeCountdownOffset1, bounds.top); + r1.moveTo(5 * _digitOffset, 0); + _digits.copyToCurrentPort(r1, r2); + + r2.moveTo(bounds.left + kGlobeCountdownOffset2, bounds.top); + r1.moveTo(9 * _digitOffset, 0); + _digits.copyToCurrentPort(r1, r2); + } else { + r2.moveTo(bounds.left, bounds.top); + r1.moveTo((time / 60) * _digitOffset, 0); + _digits.copyToCurrentPort(r1, r2); + + time %= 60; + r2.moveTo(bounds.left + kGlobeCountdownOffset1, bounds.top); + r1.moveTo((time / 10) * _digitOffset, 0); + _digits.copyToCurrentPort(r1, r2); + + r2.moveTo(bounds.left + kGlobeCountdownOffset2, bounds.top); + r1.moveTo((time % 10) * _digitOffset, 0); + _digits.copyToCurrentPort(r1, r2); + } +} + +const int16 GlobeGame::_siloCoords[kNumAllSilos][2] = { + { 60, -151 }, // Anchorage, Alaska + { 6, 39 }, // Addis Ababa, Ethiopia + { -22, 44 }, // Antaro, Madagascar + { 30, -83 }, // Atlanta, Georgia + { -41, 173 }, // Auckland, New Zealand + { 39, -78 }, // Baltimore, Maryland + { 11, 101 }, // Bangkok, Thailand + { 2, -75 }, // Bogota, Colombia + { 46, 4 }, // Bonn, Germany + { 51, -7 }, // Dublin, Ireland + { 28, -1 }, // El Menia, Algeria + { 67, -111 }, // Ellesmere, Canada + { 43, -107 }, // Glasgow, Montana + { 61, -48 }, // Godthab, Greenland + { 19, -157 }, // Honolulu, Hawaii + { 6, 5 }, // Ibadan, Nigeria + { -29, 26 }, // Johannesburg, South Africa + { 46, 92 }, // Kobdo, Mongolia + { -15, -63 }, // La Paz, Bolivia + { -35, -61 }, // La Plata, Argentina + { -9, -76 }, // Lima, Peru + { 38, -4 }, // Madrid, Spain + { -8, -51 }, // Manaus, Brazil + { 13, 120 }, // Manila, Phillipines + { -35, 143 }, // Melbourne, Australia + { 60, -161 }, // Nome, Alaska + { -7, 142 }, // Papua, New Guinea + { -32, 117 }, // Perth, West Australia + { 34, -114 }, // Phoenix, Arizona + { 18, -71 }, // Port-Au-Prince, Haiti + { 42, -121 }, // Portland, Oregon + { 61, -20 }, // Reykjavik, Iceland + { -22, -46 }, // Rio de Janeiro + { 27, -101 }, // San Antonio, Texas + { 34, 126 }, // Seoul, Korea + { 37, -87 }, // Saint Louis, Missouri + { 60, 30 }, // Saint Petersberg, Russia + { 56, 12 }, // Stockholm, Sweden + { 51, 105 }, // Svortalsk, Siberia + { 36, -96 } // Tulsa, Oklahoma +}; + +const int16 GlobeGame::_targetSilo[kNumTargetSilos] = { + 14, 9, 1, 33, 6, 8, 34, 31, 38, 21 +}; + +const short GlobeGame::_timeLimit[kNumTargetSilos] = { + 120, 110, 100, 90, 80, 70, 60, 50, 40, 30 +}; + +const TimeValue GlobeGame::_siloName[kNumTargetSilos][2] = { + { kHonoluluIn, kHonoluluOut }, + { kDublinIn, kDublinOut }, + { kAddisAbabaIn, kAddisAbabaOut }, + { kSanAntonioIn, kSanAntonioOut }, + { kBangkokIn, kBangkokOut }, + { kBonnIn, kBonnOut }, + { kSeoulIn, kSeoulOut }, + { kReykjavikIn, kReykjavikOut }, + { kSvortalskIn, kSvortalskOut }, + { kMadridIn, kMadridOut } +}; + +// From globe room models + +static const GlobeGame::Point3D kCameraLocation = { 0.53f, 4.4f, -0.86f }; +static const GlobeGame::Point3D kGlobeCenter = { -31.5f, 8.0f, 0.0f }; +static const float kGlobeRadius = 8.25f; +static const int16 kDegreesPerLongSlice = 360 / kNumLongSlices; +static const int16 kDegreesPerLatSlice = 25; +static const int16 kLongOrigin = -95; + +// Other constants. + +static const float kTanFieldOfView = 0.7082373180482f; +static const float kPicturePlaneDistance = 10.0f; // Completely arbitrary. +static const int16 kLatError = 2; +static const int16 kLongError = 2; +static const TimeValue kGlobeMovieStartTime = 2 * 2 * kNumLongSlices * 600 / 15; + +static const TimeValue kTimePerGlobeFrame = 40; + +static const NotificationFlags kGlobeSplash1Finished = 1; +static const NotificationFlags kGlobeTimerExpired = kGlobeSplash1Finished << 1; +static const NotificationFlags kMaxDeactivatedFinished = kGlobeTimerExpired << 1; + +static const NotificationFlags kGlobeNotificationFlags = kGlobeSplash1Finished | + kGlobeTimerExpired | + kMaxDeactivatedFinished; + +static const int16 kSplash1End = 4; +static const int16 kSplash2End = 5; +static const int16 kSplash3Start = 8; +static const int16 kSplash3Stop = 9; +static const int16 kSplash4Start = 9; +static const int16 kSplash4Stop = 10; +static const int16 kNewLaunchSiloTime = 10; +static const int16 kSiloDeactivatedTime = 11; +static const int16 kMissileLaunchedTime = 12; +static const int16 kMaxDeactivatedStart = 13; +static const int16 kMaxDeactivatedStop = 23; + +static const int16 kGamePlaying = 1; +static const int16 kGameOver = 2; + +enum { + kGameIntro, + kPlayingRobotIntro, + kPlayingStrikeAuthorized, + kPlayingPrimaryTarget, + kPlayingNewSilo1, + kPlayingNewSilo2, + kPlayingNewSilo3, + kPlayingTime, + kPlayingInstructions, + kWaitingForPlayer, + kSiloDeactivated, + kRobotTaunting, + kDelayingPlayer, + kPlayerWon1, + kPlayerWon2, + kPlayerLost1 +}; + +// TODO: Use ScummVM equivalent +static const float kPI = 3.1415926535f; + +float degreesToRadians(float angle) { + return (angle * kPI) / 180; +} + +float radiansToDegrees(float angle) { + return (angle * 180) / kPI; +} + +GlobeGame::GlobeGame(Neighborhood *handler) : GameInteraction(kNoradGlobeGameInteractionID, handler), + _monitorMovie(kGlobeMonitorID), _globeMovie(kGlobeMovieID), _upperNamesMovie(kGlobeUpperNamesID), + _lowerNamesMovie(kGlobeLowerNamesID), _globeNotification(kNoradGlobeNotificationID, (PegasusEngine *)g_engine), + _globeCircleLeft(kGlobeCircleLeftID), _globeCircleRight(kGlobeCircleRightID), + _globeCircleUp(kGlobeCircleUpID), _globeCircleDown(kGlobeCircleDownID), + _motionHighlightLeft(kMotionHiliteLeftID), _motionHighlightRight(kMotionHiliteRightID), + _motionHighlightUp(kMotionHiliteUpID), _motionHighlightDown(kMotionHiliteDownID), + _targetHighlightUpperLeft(kTargetHiliteUpperLeftID), _targetHighlightUpperRight(kTargetHiliteUpperRightID), + _targetHighlightLowerLeft(kTargetHiliteLowerLeftID), _targetHighlightLowerRight(kTargetHiliteLowerRightID), + _globeTracker(&_globeMovie, &_motionHighlightLeft, &_motionHighlightRight, &_motionHighlightUp, + &_motionHighlightDown), _countdown(kGlobeCountdownID) { + _neighborhoodNotification = handler->getNeighborhoodNotification(); +} + +void GlobeGame::openInteraction() { + _monitorMovie.initFromMovieFile("Images/Norad Delta/N79 Left Monitor"); + _monitorMovie.moveElementTo(kGlobeMonitorLeft, kGlobeMonitorTop); + _monitorMovie.setDisplayOrder(kGlobeMonitorLayer); + _monitorMovie.startDisplaying(); + _monitorMovie.setSegment(0, kSplash1End * _monitorMovie.getScale()); + _monitorMovie.show(); + + _monitorCallBack.setNotification(&_globeNotification); + _monitorCallBack.initCallBack(&_monitorMovie, kCallBackAtExtremes); + _monitorCallBack.setCallBackFlag(kGlobeSplash1Finished); + _monitorCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + + _upperNamesMovie.initFromMovieFile("Images/Norad Delta/Upper Names"); + _upperNamesMovie.moveElementTo(kGlobeUpperNamesLeft, kGlobeUpperNamesTop); + _upperNamesMovie.setDisplayOrder(kGlobeUpperNamesLayer); + _upperNamesMovie.startDisplaying(); + + _lowerNamesMovie.initFromMovieFile("Images/Norad Delta/Lower Names"); + _lowerNamesMovie.moveElementTo(kGlobeLowerNamesLeft, kGlobeLowerNamesTop); + _lowerNamesMovie.setDisplayOrder(kGlobeLowerNamesLayer); + _lowerNamesMovie.startDisplaying(); + + _globeMovie.initFromMovieFile("Images/Norad Delta/Spinning Globe"); + _globeMovie.moveElementTo(kGlobeLeft, kGlobeTop); + _globeMovie.setDisplayOrder(kGlobeMovieLayer); + _globeMovie.startDisplaying(); + _globeMovie.setTime(kGlobeMovieStartTime); + _globeMovie.redrawMovieWorld(); + + _globeCircleLeft.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kGlobeCircleLeftPICTID, true); + _globeCircleLeft.moveElementTo(kGlobeCircleLeftLeft, kGlobeCircleLeftTop); + _globeCircleLeft.setDisplayOrder(kGlobeCircleLayer); + _globeCircleLeft.startDisplaying(); + + _globeCircleRight.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kGlobeCircleRightPICTID, true); + _globeCircleRight.moveElementTo(kGlobeCircleRightLeft, kGlobeCircleRightTop); + _globeCircleRight.setDisplayOrder(kGlobeCircleLayer); + _globeCircleRight.startDisplaying(); + + _globeCircleUp.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kGlobeCircleUpPICTID, true); + _globeCircleUp.moveElementTo(kGlobeCircleUpLeft, kGlobeCircleUpTop); + _globeCircleUp.setDisplayOrder(kGlobeCircleLayer); + _globeCircleUp.startDisplaying(); + + _globeCircleDown.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kGlobeCircleDownPICTID, true); + _globeCircleDown.moveElementTo(kGlobeCircleDownLeft, kGlobeCircleDownTop); + _globeCircleDown.setDisplayOrder(kGlobeCircleLayer); + _globeCircleDown.startDisplaying(); + + _motionHighlightLeft.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kMotionHiliteLeftPICTID, true); + _motionHighlightLeft.moveElementTo(kGlobeLeftMotionHiliteLeft, kGlobeLeftMotionHiliteTop); + _motionHighlightLeft.setDisplayOrder(kGlobeHilitesLayer); + _motionHighlightLeft.startDisplaying(); + + _motionHighlightRight.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kMotionHiliteRightPICTID, true); + _motionHighlightRight.moveElementTo(kGlobeRightMotionHiliteLeft, kGlobeRightMotionHiliteTop); + _motionHighlightRight.setDisplayOrder(kGlobeCircleLayer); + _motionHighlightRight.startDisplaying(); + + _motionHighlightUp.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kMotionHiliteUpPICTID, true); + _motionHighlightUp.moveElementTo(kGlobeUpMotionHiliteLeft, kGlobeUpMotionHiliteTop); + _motionHighlightUp.setDisplayOrder(kGlobeHilitesLayer); + _motionHighlightUp.startDisplaying(); + + _motionHighlightDown.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kMotionHiliteDownPICTID, true); + _motionHighlightDown.moveElementTo(kGlobeDownMotionHiliteLeft, kGlobeDownMotionHiliteTop); + _motionHighlightDown.setDisplayOrder(kGlobeHilitesLayer); + _motionHighlightDown.startDisplaying(); + + _targetHighlightUpperLeft.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kTargetUpperLeftPICTID, true); + _targetHighlightUpperLeft.moveElementTo(kGlobeUpperLeftHiliteLeft, kGlobeUpperLeftHiliteTop); + _targetHighlightUpperLeft.setDisplayOrder(kGlobeHilitesLayer); + _targetHighlightUpperLeft.startDisplaying(); + + _targetHighlightUpperRight.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kTargetUpperRightPICTID, true); + _targetHighlightUpperRight.moveElementTo(kGlobeUpperRightHiliteLeft, kGlobeUpperRightHiliteTop); + _targetHighlightUpperRight.setDisplayOrder(kGlobeHilitesLayer); + _targetHighlightUpperRight.startDisplaying(); + + _targetHighlightLowerLeft.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kTargetLowerLeftPICTID, true); + _targetHighlightLowerLeft.moveElementTo(kGlobeLowerLeftHiliteLeft, kGlobeLowerLeftHiliteTop); + _targetHighlightLowerLeft.setDisplayOrder(kGlobeHilitesLayer); + _targetHighlightLowerLeft.startDisplaying(); + + _targetHighlightLowerRight.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kTargetLowerRightPICTID, true); + _targetHighlightLowerRight.moveElementTo(kGlobeLowerRightHiliteLeft, kGlobeLowerRightHiliteTop); + _targetHighlightLowerRight.setDisplayOrder(kGlobeHilitesLayer); + _targetHighlightLowerRight.startDisplaying(); + + _countdown.setDisplayOrder(kGlobeCountdownLayer); + _countdown.moveElementTo(kGlobeCountdownLeft, kGlobeCountdownTop); + _countdown.startDisplaying(); + _countdown.setCountdownTime(_timeLimit[0]); + + _countdownCallBack.setNotification(&_globeNotification); + _countdownCallBack.initCallBack(&_countdown, kCallBackAtExtremes); + _countdownCallBack.setCallBackFlag(kGlobeTimerExpired); + _countdownCallBack.scheduleCallBack(kTriggerAtStart, 0, 0); + + _globeNotification.notifyMe(this, kGlobeNotificationFlags, kGlobeNotificationFlags); + + _gameState = kGameIntro; + _currentSiloIndex = 0; + _playedInstructions = false; + + _neighborhoodNotification->notifyMe(this, kDelayCompletedFlag | kSpotSoundCompletedFlag, kDelayCompletedFlag | kSpotSoundCompletedFlag); +} + +void GlobeGame::initInteraction() { + _monitorMovie.start(); + _monitorMovie.redrawMovieWorld(); +} + +void GlobeGame::closeInteraction() { + _monitorMovie.stop(); + _monitorMovie.stopDisplaying(); + _monitorMovie.releaseMovie(); + _monitorCallBack.releaseCallBack(); + + _globeMovie.stop(); + _globeMovie.stopDisplaying(); + _globeMovie.releaseMovie(); + _globeNotification.cancelNotification(this); + + _upperNamesMovie.stop(); + _upperNamesMovie.stopDisplaying(); + _upperNamesMovie.releaseMovie(); + + _lowerNamesMovie.stop(); + _lowerNamesMovie.stopDisplaying(); + _lowerNamesMovie.releaseMovie(); + + _countdown.hide(); + _countdown.stopDisplaying(); + _countdownCallBack.releaseCallBack(); + + _globeCircleLeft.stopDisplaying(); + _globeCircleLeft.deallocateSurface(); + _globeCircleRight.stopDisplaying(); + _globeCircleRight.deallocateSurface(); + _globeCircleUp.stopDisplaying(); + _globeCircleUp.deallocateSurface(); + _globeCircleDown.stopDisplaying(); + _globeCircleDown.deallocateSurface(); + + _motionHighlightLeft.stopDisplaying(); + _motionHighlightLeft.deallocateSurface(); + _motionHighlightRight.stopDisplaying(); + _motionHighlightRight.deallocateSurface(); + _motionHighlightUp.stopDisplaying(); + _motionHighlightUp.deallocateSurface(); + _motionHighlightDown.stopDisplaying(); + _motionHighlightDown.deallocateSurface(); + + _targetHighlightUpperLeft.stopDisplaying(); + _targetHighlightUpperLeft.deallocateSurface(); + _targetHighlightUpperRight.stopDisplaying(); + _targetHighlightUpperRight.deallocateSurface(); + _targetHighlightLowerLeft.stopDisplaying(); + _targetHighlightLowerLeft.deallocateSurface(); + _targetHighlightLowerRight.stopDisplaying(); + _targetHighlightLowerRight.deallocateSurface(); + + _neighborhoodNotification->cancelNotification(this); +} + +void GlobeGame::receiveNotification(Notification *notification, const NotificationFlags flags) { + TimeScale scale = _monitorMovie.getScale(); + + if (notification == _neighborhoodNotification) { + switch (_gameState) { + case kPlayingRobotIntro: + _monitorMovie.stop(); + _monitorMovie.setSegment(0, _monitorMovie.getDuration()); + _monitorMovie.setTime(kSplash2End * scale - 1); + _monitorMovie.setFlags(0); + + _owner->requestDelay(1, 2, kFilterNoInput, 0); + _owner->requestSpotSound(kStrikeAuthorizedIn, kStrikeAuthorizedOut, + kFilterNoInput, kSpotSoundCompletedFlag); + _gameState = kPlayingStrikeAuthorized; + break; + case kPlayingStrikeAuthorized: + _monitorMovie.setSegment(kSplash3Start * scale, kSplash3Stop * scale); + _monitorMovie.setTime(kSplash3Start * scale); + _monitorMovie.redrawMovieWorld(); + + _owner->requestDelay(1, 3, kFilterNoInput, 0); + _owner->requestSpotSound(kPrimaryTargetIn, kPrimaryTargetOut, kFilterNoInput, 0); + _owner->requestDelay(1, 5, kFilterNoInput, kDelayCompletedFlag); + _monitorMovie.start(); + _gameState = kPlayingPrimaryTarget; + break; + case kPlayingPrimaryTarget: + _monitorMovie.stop(); + _monitorMovie.setSegment(0, _monitorMovie.getDuration()); + _monitorMovie.setTime(kNewLaunchSiloTime * scale); + _owner->requestSpotSound(kNewLaunchSiloIn, kNewLaunchSiloOut, kFilterNoInput, + kSpotSoundCompletedFlag); + _gameState = kPlayingNewSilo1; + break; + case kPlayingNewSilo1: + _monitorMovie.stop(); + _monitorMovie.setSegment(0, _monitorMovie.getDuration()); + _owner->requestDelay(1, 3, kFilterNoInput, kDelayCompletedFlag); + _gameState = kPlayingNewSilo2; + break; + case kPlayingNewSilo2: + _upperNamesMovie.show(); + _upperNamesMovie.setTime(_currentSiloIndex * _upperNamesMovie.getScale()); + _upperNamesMovie.redrawMovieWorld(); + _monitorMovie.setTime(kSplash4Stop * scale - 1); + _monitorMovie.redrawMovieWorld(); + _owner->requestSpotSound(_siloName[_currentSiloIndex][0], _siloName[_currentSiloIndex][1], kFilterNoInput, 0); + _owner->requestDelay(1, 3, kFilterNoInput, 0); + _owner->requestSpotSound(kLaunchToProceedIn, kLaunchToProceedOut, kFilterNoInput, 0); + _owner->requestDelay(1, 5, kFilterNoInput, kDelayCompletedFlag); + _gameState = kPlayingNewSilo3; + break; + case kPlayingNewSilo3: + _countdown.stopCountdown(); + _countdown.setCountdownTime(_timeLimit[_currentSiloIndex]); + _countdown.show(); + _gameState = kPlayingTime; + + if (_timeLimit[_currentSiloIndex] >= 120) + _owner->requestSpotSound(kTwoMinutesIn, kTwoMinutesOut, kFilterNoInput, 0); + else if (_timeLimit[_currentSiloIndex] >= 60) + _owner->requestSpotSound(kOneMinuteIn, kOneMinuteOut, kFilterNoInput, 0); + + switch (_timeLimit[_currentSiloIndex] % 60) { + case 0: + _owner->requestDelay(1, 5, kFilterNoInput, kDelayCompletedFlag); + break; + case 10: + _owner->requestDelay(1, 5, kFilterNoInput, 0); + _owner->requestSpotSound(kTenSecondsIn, kTenSecondsOut, kFilterNoInput, + kSpotSoundCompletedFlag); + break; + case 20: + _owner->requestDelay(1, 5, kFilterNoInput, 0); + _owner->requestSpotSound(kTwentySecondsIn, kTwentySecondsOut, + kFilterNoInput, kSpotSoundCompletedFlag); + break; + case 30: + _owner->requestDelay(1, 5, kFilterNoInput, 0); + _owner->requestSpotSound(kThirtySecondsIn, kThirtySecondsOut, + kFilterNoInput, kSpotSoundCompletedFlag); + break; + case 40: + _owner->requestDelay(1, 5, kFilterNoInput, 0); + _owner->requestSpotSound(kFortySecondsIn, kFortySecondsOut, + kFilterNoInput, kSpotSoundCompletedFlag); + break; + case 50: + _owner->requestDelay(1, 5, kFilterNoInput, 0); + _owner->requestSpotSound(kFiftySecondsIn, kFiftySecondsOut, + kFilterNoInput, kSpotSoundCompletedFlag); + break; + } + case kPlayingTime: + _gameState = kPlayingInstructions; + _globeMovie.show(); + _globeCircleLeft.show(); + _globeCircleRight.show(); + _globeCircleUp.show(); + _globeCircleDown.show(); + + if (_playedInstructions) { + receiveNotification(notification, flags); + } else { + _owner->requestSpotSound(kToDeactivateIn, kToDeactivateOut, kFilterNoInput, + kSpotSoundCompletedFlag); + _playedInstructions = true; + } + break; + case kPlayingInstructions: + _gameState = kWaitingForPlayer; + _countdown.startCountdown(); + break; + case kSiloDeactivated: + _gameState = kRobotTaunting; + + switch (_currentSiloIndex) { + case 3: + _owner->requestSpotSound(kYouCannotPossiblyIn, kYouCannotPossiblyOut, + kFilterNoInput, kSpotSoundCompletedFlag); + break; + case 5: + _owner->requestSpotSound(kYouWillFailIn, kYouWillFailOut, kFilterNoInput, + kSpotSoundCompletedFlag); + break; + case 7: + _owner->requestSpotSound(kGiveUpHumanIn, kGiveUpHumanOut, kFilterNoInput, + kSpotSoundCompletedFlag); + break; + case 9: + _owner->requestSpotSound(kYouAreRunningIn, kYouAreRunningOut, + kFilterNoInput, kSpotSoundCompletedFlag); + break; + default: + _owner->requestSpotSound(kNewLaunchSiloIn, kNewLaunchSiloOut, + kFilterNoInput, kSpotSoundCompletedFlag); + _monitorMovie.setTime(kNewLaunchSiloTime * scale); + _monitorMovie.redrawMovieWorld(); + _gameState = kPlayingNewSilo1; + break; + } + break; + case kRobotTaunting: + _owner->requestDelay(1, 1, kFilterNoInput, 0); + _owner->requestSpotSound(kNewLaunchSiloIn, kNewLaunchSiloOut, kFilterNoInput, kSpotSoundCompletedFlag); + _monitorMovie.setTime(kNewLaunchSiloTime * scale); + _monitorMovie.redrawMovieWorld(); + _gameState = kPlayingNewSilo1; + break; + case kDelayingPlayer: + _gameState = kWaitingForPlayer; + break; + case kPlayerLost1: + _owner->recallToTSAFailure(); + break; + case kPlayerWon2: + ((NoradDelta *)_owner)->finishedGlobeGame(); + _owner->requestDeleteCurrentInteraction(); + break; + default: + break; + } + } else if (notification == &_globeNotification) { + ExtraTable::Entry entry; + + switch (flags) { + case kGlobeSplash1Finished: + _owner->getExtraEntry(kN79BrightView, entry); + _monitorMovie.stop(); + _monitorMovie.setSegment(kSplash1End * scale, kSplash2End * scale); + _monitorMovie.setFlags(kLoopTimeBase); + _monitorMovie.start(); + _owner->showViewFrame(entry.movieStart); + _owner->requestSpotSound(kIJustBrokeIn, kIJustBrokeOut, kFilterNoInput, 0); + _owner->requestDelay(1, 2, kFilterNoInput, kDelayCompletedFlag); + _gameState = kPlayingRobotIntro; + break; + case kGlobeTimerExpired: + // Missile launched, player loses. + _owner->requestSpotSound(kMissileLaunchedIn, kMissileLaunchedOut, kFilterNoInput, kSpotSoundCompletedFlag); + _gameState = kPlayerLost1; + break; + case kMaxDeactivatedFinished: + _monitorMovie.stop(); + _monitorMovie.setSegment(0, _monitorMovie.getDuration()); + _owner->requestDelay(1, 2, kFilterNoInput, 0); + _owner->requestSpotSound(kTheOnlyGoodHumanIn, kTheOnlyGoodHumanOut, kFilterNoInput, 0); + _owner->requestDelay(1, 2, kFilterNoInput, kDelayCompletedFlag); + _gameState = kPlayerWon2; + break; + default: + break; + } + } +} + +// Prevent the player from getting up until the game is over. + +void GlobeGame::handleInput(const Input &input, const Hotspot *cursorSpot) { + Common::Point where; + input.getInputLocation(where); + Hotspot *spot = g_allHotspots.findHotspot(where); + + if (((PegasusEngine *)g_engine)->_cursor->isVisible() && spot != 0 && + spot->getObjectID() == kNorad79SiloAreaSpotID && findClickedSilo(input) != -1) { + _targetHighlightUpperLeft.show(); + _targetHighlightUpperRight.show(); + _targetHighlightLowerLeft.show(); + _targetHighlightLowerRight.show(); + } else { + _targetHighlightUpperLeft.hide(); + _targetHighlightUpperRight.hide(); + _targetHighlightLowerLeft.hide(); + _targetHighlightLowerRight.hide(); + } + + // Interrupt certain inputs to prevent player from switching modes. + InputHandler::handleInput(input, cursorSpot); +} + +int16 GlobeGame::findClickedSilo(const Input &input) { + Common::Point screenPoint; + input.getInputLocation(screenPoint); + screenPoint.x -= kNavAreaLeft; + screenPoint.y -= kNavAreaTop; + + Line3D ray; + screenPointTo3DPoint(screenPoint.x, screenPoint.y, ray.pt2); + ray.pt1 = kCameraLocation; + + Point3D globePoint; + if (lineHitsGlobe(ray, globePoint)) { + int16 latOrigin, longOrigin, latitude, longitude; + globeMovieFrameToOrigin(_globeMovie.getTime() / kTimePerGlobeFrame, latOrigin, longOrigin); + globePointToLatLong(globePoint, latOrigin, longOrigin, latitude, longitude); + + for (int16 i = 0; i < kNumAllSilos; i++) + if (_siloCoords[i][0] >= latitude - kLatError && _siloCoords[i][0] <= latitude + kLatError && + _siloCoords[i][1] >= longitude - kLongError && _siloCoords[i][1] <= longitude + kLongError) + return i; + } + + return -1; +} + +void GlobeGame::spinGlobe(const Input &input, const Hotspot *spot, GlobeTrackDirection trackDirection) { + _globeTracker.setTrackParameters(spot, trackDirection); + _globeTracker.startTracking(input); +} + +void GlobeGame::clickGlobe(const Input &input) { + int16 newSilo = findClickedSilo(input); + + if (newSilo != -1) { + _targetHighlightUpperLeft.hide(); + _targetHighlightUpperRight.hide(); + _targetHighlightLowerLeft.hide(); + _targetHighlightLowerRight.hide(); + _lowerNamesMovie.show(); + _lowerNamesMovie.setTime(newSilo * _lowerNamesMovie.getScale()); + _lowerNamesMovie.redrawMovieWorld(); + _owner->requestSpotSound(kSiloBeepIn, kSiloBeepOut, kFilterNoInput, 0); + + if (newSilo == _targetSilo[_currentSiloIndex]) { + _currentSiloIndex++; + _countdown.stopCountdown(); + _owner->requestSpotSound(kSiloDeactivatedIn, kSiloDeactivatedOut, kFilterNoInput, 0); + + if (_currentSiloIndex == kNumTargetSilos) { + // Player won. + _owner->requestDelay(1, 2, kFilterNoInput, 0); + _upperNamesMovie.hide(); + _lowerNamesMovie.hide(); + _countdown.hide(); + _monitorMovie.setSegment(kMaxDeactivatedStart * _monitorMovie.getScale(), + kMaxDeactivatedStop * _monitorMovie.getScale()); + _monitorMovie.setTime(kMaxDeactivatedStart * _monitorMovie.getScale()); + _monitorCallBack.setCallBackFlag(kMaxDeactivatedFinished); + _monitorCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + _monitorMovie.start(); + _owner->requestSpotSound(kMaximumDeactivationIn, kMaximumDeactivationOut, + kFilterNoInput, kSpotSoundCompletedFlag); + _gameState = kPlayerWon1; + } else { + _owner->requestDelay(2, 1, kFilterNoInput, kDelayCompletedFlag); + _upperNamesMovie.hide(); + _lowerNamesMovie.hide(); + _countdown.hide(); + _monitorMovie.setTime(kSiloDeactivatedTime * _monitorMovie.getScale()); + _monitorMovie.redrawMovieWorld(); + _gameState = kSiloDeactivated; + } + } else { + _owner->requestDelay(5, 1, kFilterNoInput, kDelayCompletedFlag); + _gameState = kDelayingPlayer; + // Play "incorrect" sound? + } + } +} + +void GlobeGame::clickInHotspot(const Input &input, const Hotspot *spot) { + switch (spot->getObjectID()) { + case kNorad79SpinLeftSpotID: + spinGlobe(input, spot, kTrackLeft); + break; + case kNorad79SpinRightSpotID: + spinGlobe(input, spot, kTrackRight); + break; + case kNorad79SpinUpSpotID: + spinGlobe(input, spot, kTrackUp); + break; + case kNorad79SpinDownSpotID: + spinGlobe(input, spot, kTrackDown); + break; + case kNorad79SiloAreaSpotID: + clickGlobe(input); + break; + default: + GameInteraction::clickInHotspot(input, spot); + break; + } +} + +void GlobeGame::activateHotspots() { + GameInteraction::activateHotspots(); + + switch (_gameState) { + case kWaitingForPlayer: + g_allHotspots.deactivateOneHotspot(kNorad79WestOutSpotID); + g_allHotspots.activateOneHotspot(kNorad79SpinLeftSpotID); + g_allHotspots.activateOneHotspot(kNorad79SpinRightSpotID); + g_allHotspots.activateOneHotspot(kNorad79SpinUpSpotID); + g_allHotspots.activateOneHotspot(kNorad79SpinDownSpotID); + g_allHotspots.activateOneHotspot(kNorad79SiloAreaSpotID); + break; + default: + g_allHotspots.deactivateOneHotspot(kNorad79WestOutSpotID); + break; + } +} + +void GlobeGame::globeMovieFrameToOrigin(int16 frameNum, int16 &latOrigin, int16 &longOrigin) { + latOrigin = kDegreesPerLatSlice * 2 - (frameNum / (kNumLongSlices * 2)) * kDegreesPerLatSlice; + frameNum %= kNumLongSlices * 2; + + if (frameNum >= kNumLongSlices) + longOrigin = kLongOrigin + (kNumLongSlices * 2 - 1 - frameNum) * kDegreesPerLongSlice; + else + longOrigin = kLongOrigin + frameNum * kDegreesPerLongSlice; + + if (longOrigin > 180) + longOrigin -= 360; +} + +void GlobeGame::globePointToLatLong(const GlobeGame::Point3D &pt, int16 latOrigin, int16 longOrigin, + int16 &latitude, int16 &longitude) { + Point3D scratch = pt; + + // Translate globe center to origin. + scratch.x -= kGlobeCenter.x; + scratch.y -= kGlobeCenter.y; + scratch.z -= kGlobeCenter.z; + + // Rotate around z axis latOrigin degrees to bring equator parallel with XZ plane + float theta = degreesToRadians(latOrigin); + float s = sin(theta); + float c = cos(theta); + float x = scratch.x * c - scratch.y * s; + float y = scratch.y * c + scratch.x * s; + scratch.x = x; + scratch.y = y; + + // Calculate latitude + latitude = (int16)radiansToDegrees(asin(scratch.y / kGlobeRadius)); + + // Rotate around y axis longOrigin degrees to bring longitude 0 to positive X axis + theta = degreesToRadians(longOrigin); + s = sin(theta); + c = cos(theta); + x = scratch.x * c - scratch.z * s; + float z = scratch.z * c + scratch.x * s; + scratch.x = x; + scratch.z = z; + + // Calculate longitude + longitude = (int16)radiansToDegrees(acos(scratch.x / sqrt(scratch.x * scratch.x + scratch.z * scratch.z))); + + if (scratch.z < 0) + longitude = -longitude; +} + +// h, v in [0, 511][0, 255] +// Looking down negative x axis. +void GlobeGame::screenPointTo3DPoint(int16 h, int16 v, GlobeGame::Point3D &pt) { + pt.x = kCameraLocation.x - kPicturePlaneDistance; + pt.y = kCameraLocation.y + (128 - v) * kPicturePlaneDistance * kTanFieldOfView / 256; + pt.z = kCameraLocation.z + (h - 256) * kPicturePlaneDistance * kTanFieldOfView / 256; +} + +// Fundamentals of Three-Dimensional Graphics, by Alan Watt +// pp. 163-164 +bool GlobeGame::lineHitsGlobe(const GlobeGame::Line3D &line, GlobeGame::Point3D &pt) { + float i = line.pt2.x - line.pt1.x; + float j = line.pt2.y - line.pt1.y; + float k = line.pt2.z - line.pt1.z; + float a = i * i + j * j + k * k; + float b = 2 * i * (line.pt1.x - kGlobeCenter.x) + 2 * j * (line.pt1.y - kGlobeCenter.y) + + 2 * k * (line.pt1.z - kGlobeCenter.z); + float c = kGlobeCenter.x * kGlobeCenter.x + kGlobeCenter.y * kGlobeCenter.y + + kGlobeCenter.z * kGlobeCenter.z + line.pt1.x * line.pt1.x + line.pt1.y * line.pt1.y + + line.pt1.z * line.pt1.z + -2 * (kGlobeCenter.x * line.pt1.x + kGlobeCenter.y * line.pt1.y + + kGlobeCenter.z * line.pt1.z) - kGlobeRadius * kGlobeRadius; + + // Solve quadratic equation of a, b, c. + float t = b * b - 4 * a * c; + + if (t >= 0.0f) { + // Return smaller root, which corresponds to closest intersection point. + t = (-b - sqrt(t)) / (2 * a); + pt.x = i * t + line.pt1.x; + pt.y = j * t + line.pt1.y; + pt.z = k * t + line.pt1.z; + return true; + } + + return false; +} + +bool GlobeGame::canSolve() { + return _gameState != kPlayerWon1 && _gameState != kPlayerWon2 && _gameState != kPlayerLost1; +} + +void GlobeGame::doSolve() { + _owner->requestDelay(1, 2, kFilterNoInput, 0); + _upperNamesMovie.hide(); + _lowerNamesMovie.hide(); + _countdown.hide(); + _monitorMovie.setSegment(kMaxDeactivatedStart * _monitorMovie.getScale(), kMaxDeactivatedStop * _monitorMovie.getScale()); + _monitorMovie.setTime(kMaxDeactivatedStart * _monitorMovie.getScale()); + _monitorCallBack.setCallBackFlag(kMaxDeactivatedFinished); + _monitorCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + _monitorMovie.start(); + _owner->requestSpotSound(kMaximumDeactivationIn, kMaximumDeactivationOut, kFilterNoInput, kSpotSoundCompletedFlag); + _gameState = kPlayerWon1; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/norad/delta/globegame.h b/engines/pegasus/neighborhood/norad/delta/globegame.h new file mode 100644 index 0000000000..73ed48866f --- /dev/null +++ b/engines/pegasus/neighborhood/norad/delta/globegame.h @@ -0,0 +1,169 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_NEIGHBORHOOD_NORAD_DELTA_GLOBEGAME_H +#define PEGASUS_NEIGHBORHOOD_NORAD_DELTA_GLOBEGAME_H + +#include "pegasus/interaction.h" +#include "pegasus/movie.h" +#include "pegasus/notification.h" + +namespace Pegasus { + +enum GlobeTrackDirection { + kTrackLeft, + kTrackRight, + kTrackUp, + kTrackDown +}; + +// This class assumes that the globe movie is built at 15 frames per second with a +// time scale of 600, yielding 40 time unit per frame. + +class GlobeTracker : public Tracker { +public: + GlobeTracker(Movie *, Picture *, Picture *, Picture *, Picture *); + virtual ~GlobeTracker() {} + + void setTrackParameters(const Hotspot *, GlobeTrackDirection); + void continueTracking(const Input &); + void startTracking(const Input &); + void stopTracking(const Input &); + void activateHotspots(); + bool stopTrackingInput(const Input &); + +protected: + void trackGlobeMovie(); + void stopGlobeMovie(); + + Movie *_globeMovie; + Picture *_leftHighlight; + Picture *_rightHighlight; + Picture *_upHighlight; + Picture *_downHighlight; + const Hotspot *_trackSpot; + int _trackTime; + GlobeTrackDirection _trackDirection; +}; + +class GlobeCountdown : public IdlerAnimation { +public: + GlobeCountdown(const DisplayElementID); + virtual ~GlobeCountdown() {} + + void setCountdownTime(const int); + void startCountdown(); + void stopCountdown(); + + void setDisplayOrder(const DisplayOrder); + void show(); + void hide(); + void moveElementTo(const CoordType, const CoordType); + + void draw(const Common::Rect &); + +protected: + Surface _digits; + int16 _digitOffset; +}; + +static const int16 kNumAllSilos = 40; +static const int16 kNumTargetSilos = 10; +static const int16 kNumLongSlices = 72; + +class GlobeGame : public GameInteraction, public NotificationReceiver { +public: + GlobeGame(Neighborhood *); + virtual ~GlobeGame() {} + + void handleInput(const Input &, const Hotspot *); + void clickInHotspot(const Input &, const Hotspot *); + void activateHotspots(); + + bool canSolve(); + void doSolve(); + + struct Point3D { + float x, y, z; + }; + + struct Line3D { + Point3D pt1, pt2; + }; + +protected: + // Latitude (-90 - 90) and longitude (-180 - 180) + static const int16 _siloCoords[kNumAllSilos][2]; + + static const int16 _targetSilo[kNumTargetSilos]; + static const int16 _timeLimit[kNumTargetSilos]; + static const TimeValue _siloName[kNumTargetSilos][2]; + + void openInteraction(); + void initInteraction(); + void closeInteraction(); + + void receiveNotification(Notification *, const NotificationFlags); + + void spinGlobe(const Input &, const Hotspot *, GlobeTrackDirection); + void clickGlobe(const Input &); + + int16 findClickedSilo(const Input &); + + void globeMovieFrameToOrigin(int16, int16 &, int16 &); + void globePointToLatLong(const Point3D &, int16, int16, int16 &, int16 &); + void screenPointTo3DPoint(int16, int16, Point3D &); + bool lineHitsGlobe(const Line3D &, Point3D &); + + Movie _monitorMovie; + Movie _globeMovie; + Movie _upperNamesMovie; + Movie _lowerNamesMovie; + Notification _globeNotification; + NotificationCallBack _monitorCallBack; + GlobeTracker _globeTracker; + Picture _globeCircleLeft; + Picture _globeCircleRight; + Picture _globeCircleUp; + Picture _globeCircleDown; + Picture _motionHighlightLeft; + Picture _motionHighlightRight; + Picture _motionHighlightUp; + Picture _motionHighlightDown; + Picture _targetHighlightUpperLeft; + Picture _targetHighlightUpperRight; + Picture _targetHighlightLowerLeft; + Picture _targetHighlightLowerRight; + GlobeCountdown _countdown; + NotificationCallBack _countdownCallBack; + int16 _gameState; + int16 _currentSiloIndex; + Notification *_neighborhoodNotification; + bool _playedInstructions; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/norad/delta/noraddelta.cpp b/engines/pegasus/neighborhood/norad/delta/noraddelta.cpp new file mode 100644 index 0000000000..f2ea53ff89 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/delta/noraddelta.cpp @@ -0,0 +1,869 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/energymonitor.h" +#include "pegasus/gamestate.h" +#include "pegasus/interface.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/biochips/opticalchip.h" +#include "pegasus/items/biochips/retscanchip.h" +#include "pegasus/items/inventory/airmask.h" +#include "pegasus/neighborhood/norad/constants.h" +#include "pegasus/neighborhood/norad/subcontrolroom.h" +#include "pegasus/neighborhood/norad/delta/globegame.h" +#include "pegasus/neighborhood/norad/delta/noraddelta.h" + +namespace Pegasus { + +const uint32 NoradDelta::_noradDeltaClawExtras[22] = { + kN60ClawFromAToB, + kN60ClawALoop, + kN60ClawAPinch, + kN60ClawACounterclockwise, + kN60ClawAClockwise, + kN60ClawFromBToA, + kN60ClawFromBToC, + kN60ClawFromBToD, + kN60ClawBLoop, + kN60ClawBPinch, + kN60ClawBCounterclockwise, + kN60ClawBClockwise, + kN60ClawFromCToB, + kN60ClawCLoop, + kN60ClawCPinch, + kN60ClawCCounterclockwise, + kN60ClawCClockwise, + kN60ClawFromDToB, + kN60ClawDLoop, + kN60ClawDPinch, + kN60ClawDCounterclockwise, + kN60ClawDClockwise +}; + +NoradDelta::NoradDelta(InputHandler *nextHandler, PegasusEngine *owner) : Norad(nextHandler, owner, "Norad Delta", kNoradDeltaID) { + _elevatorUpRoomID = kNorad49South; + _elevatorDownRoomID = kNorad48South; + _elevatorUpSpotID = kNorad48ElevatorUpSpotID; + _elevatorDownSpotID = kNorad49ElevatorDownSpotID; + + // Pressure door stuff. + + _subRoomEntryRoom1 = kNorad50; + _subRoomEntryDir1 = kEast; + _subRoomEntryRoom2 = kNorad59; + _subRoomEntryDir2 = kWest; + _upperPressureDoorRoom = kNorad50East; + _lowerPressureDoorRoom = kNorad59West; + + _upperPressureDoorUpSpotID = kDeltaUpperPressureDoorUpSpotID; + _upperPressureDoorDownSpotID = kDeltaUpperPressureDoorDownSpotID; + _upperPressureDoorAbortSpotID = kNorad50DoorOutSpotID; + + _lowerPressureDoorUpSpotID = kDeltaLowerPressureDoorUpSpotID; + _lowerPressureDoorDownSpotID = kDeltaLowerPressureDoorDownSpotID; + _lowerPressureDoorAbortSpotID = kNorad59WestOutSpotID; + + _pressureSoundIn = kPressureDoorIntro1In; + _pressureSoundOut = kPressureDoorIntro1Out; + _equalizeSoundIn = kPressureDoorIntro2In; + _equalizeSoundOut = kPressureDoorIntro2Out; + _accessDeniedIn = kDeltaAccessDeniedIn; + _accessDeniedOut = kDeltaAccessDeniedOut; + + GameState.setNoradSubPrepState(kSubDamaged); + + _subControlRoom = kNorad60West; +} + +void NoradDelta::init() { + Norad::init(); + + // Little fix for the retinal scan zoom in spot... + Hotspot *hotspot = _vm->getAllHotspots().findHotspotByID(kNorad68WestSpotID); + hotspot->setMaskedHotspotFlags(kZoomInSpotFlag, kZoomInSpotFlag | kZoomOutSpotFlag); + + hotspot = _vm->getAllHotspots().findHotspotByID(kNorad79WestSpotID); + hotspot->setMaskedHotspotFlags(kZoomInSpotFlag, kZoomInSpotFlag | kZoomOutSpotFlag); + + hotspot = _vm->getAllHotspots().findHotspotByID(kDelta59RobotShieldBiochipSpotID); + hotspot->setMaskedHotspotFlags(kPickUpBiochipSpotFlag, kPickUpBiochipSpotFlag); + HotspotInfoTable::Entry *hotspotEntry = findHotspotEntry(kDelta59RobotShieldBiochipSpotID); + hotspotEntry->hotspotItem = kShieldBiochip; + + hotspot = _vm->getAllHotspots().findHotspotByID(kDelta59RobotOpMemBiochipSpotID); + hotspot->setMaskedHotspotFlags(kPickUpBiochipSpotFlag, kPickUpBiochipSpotFlag); + hotspotEntry = findHotspotEntry(kDelta59RobotOpMemBiochipSpotID); + hotspotEntry->hotspotItem = kOpticalBiochip; + + hotspot = _vm->getAllHotspots().findHotspotByID(kDelta59RobotRetinalBiochipSpotID); + hotspot->setMaskedHotspotFlags(kPickUpBiochipSpotFlag, kPickUpBiochipSpotFlag); + hotspotEntry = findHotspotEntry(kDelta59RobotRetinalBiochipSpotID); + hotspotEntry->hotspotItem = kRetinalScanBiochip; + + hotspot = _vm->getAllHotspots().findHotspotByID(kDelta60RobotShieldBiochipSpotID); + hotspot->setMaskedHotspotFlags(kPickUpBiochipSpotFlag, kPickUpBiochipSpotFlag); + hotspotEntry = findHotspotEntry(kDelta60RobotShieldBiochipSpotID); + hotspotEntry->hotspotItem = kShieldBiochip; + + hotspot = _vm->getAllHotspots().findHotspotByID(kDelta60RobotOpMemBiochipSpotID); + hotspot->setMaskedHotspotFlags(kPickUpBiochipSpotFlag, kPickUpBiochipSpotFlag); + hotspotEntry = findHotspotEntry(kDelta60RobotOpMemBiochipSpotID); + hotspotEntry->hotspotItem = kOpticalBiochip; + + hotspot = _vm->getAllHotspots().findHotspotByID(kDelta60RobotRetinalBiochipSpotID); + hotspot->setMaskedHotspotFlags(kPickUpBiochipSpotFlag, kPickUpBiochipSpotFlag); + hotspotEntry = findHotspotEntry(kDelta60RobotRetinalBiochipSpotID); + hotspotEntry->hotspotItem = kRetinalScanBiochip; +} + +void NoradDelta::start() { + if (g_energyMonitor) { + g_energyMonitor->stopEnergyDraining(); + g_energyMonitor->restoreLastEnergyValue(); + _vm->resetEnergyDeathReason(); + g_energyMonitor->startEnergyDraining(); + } + + Norad::start(); +} + +void NoradDelta::setUpAIRules() { + Neighborhood::setUpAIRules(); + + if (g_AIArea) { + AIPlayMessageAction *messageAction = new AIPlayMessageAction("Images/AI/Norad/XN07NE", false); + AILocationCondition *locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kNorad68, kWest)); + AIRule *rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + } +} + +void NoradDelta::getExtraCompassMove(const ExtraTable::Entry &entry, FaderMoveSpec &compassMove) { + switch (entry.extra) { + case kArriveFromSubChase: + compassMove.makeTwoKnotFaderSpec(kNoradDeltaMovieScale, entry.movieStart, 20, entry.movieEnd, 90); + compassMove.insertFaderKnot(entry.movieStart + 25 * kNoradDeltaFrameDuration, 20); + compassMove.insertFaderKnot(entry.movieStart + 94 * kNoradDeltaFrameDuration, 45); + compassMove.insertFaderKnot(entry.movieStart + 101 * kNoradDeltaFrameDuration, 45); + compassMove.insertFaderKnot(entry.movieStart + 146 * kNoradDeltaFrameDuration, 90 + 15); + compassMove.insertFaderKnot(entry.movieStart + 189 * kNoradDeltaFrameDuration, 90 + 15); + compassMove.insertFaderKnot(entry.movieStart + 204 * kNoradDeltaFrameDuration, 90 + 30); + compassMove.insertFaderKnot(entry.movieStart + 214 * kNoradDeltaFrameDuration, 90 + 20); + compassMove.insertFaderKnot(entry.movieStart + 222 * kNoradDeltaFrameDuration, 90 + 20); + compassMove.insertFaderKnot(entry.movieStart + 228 * kNoradDeltaFrameDuration, 90 + 10); + compassMove.insertFaderKnot(entry.movieStart + 245 * kNoradDeltaFrameDuration, 90 + 85); + compassMove.insertFaderKnot(entry.movieStart + 262 * kNoradDeltaFrameDuration, 90 + 70); + compassMove.insertFaderKnot(entry.movieStart + 273 * kNoradDeltaFrameDuration, 90 + 80); + compassMove.insertFaderKnot(entry.movieStart + 287 * kNoradDeltaFrameDuration, 90); + break; + case kN60PlayerFollowsRobotToDoor: + compassMove.makeTwoKnotFaderSpec(kNoradDeltaMovieScale, entry.movieStart, 270 + kSubControlCompassAngle, + entry.movieEnd, 270 - 15); + compassMove.insertFaderKnot(entry.movieStart + 280, 270 + kSubControlCompassAngle); + compassMove.insertFaderKnot(entry.movieStart + 920, 360); + compassMove.insertFaderKnot(entry.movieStart + 1840, 360); + compassMove.insertFaderKnot(entry.movieStart + 2520, 270); + compassMove.insertFaderKnot(entry.movieStart + 3760, 270); + compassMove.insertFaderKnot(entry.movieStart + 4640, 270 + kSubControlCompassAngle); + break; + case kN59PlayerWins2: + compassMove.makeTwoKnotFaderSpec(kNoradDeltaMovieScale, entry.movieStart, 270, entry.movieEnd, 280); + compassMove.insertFaderKnot(entry.movieEnd - 1000, 270); + default: + Norad::getExtraCompassMove(entry, compassMove); + break; + } +} + +GameInteraction *NoradDelta::makeInteraction(const InteractionID interactionID) { + if (interactionID == kNoradGlobeGameInteractionID) + return new GlobeGame(this); + + return Norad::makeInteraction(interactionID); +} + +void NoradDelta::playClawMonitorIntro() { + playSpotSoundSync(kLoadClawIntroIn, kLoadClawIntroOut); +} + +void NoradDelta::getExitEntry(const RoomID room, const DirectionConstant direction, ExitTable::Entry &entry) { + Norad::getExitEntry(room, direction, entry); + + if (room == kNorad61 && direction == kSouth) + entry.movieStart += kNoradDeltaFrameDuration; +} + +void NoradDelta::getZoomEntry(const HotSpotID id, ZoomTable::Entry &zoomEntry) { + Norad::getZoomEntry(id, zoomEntry); + + if (id == kNorad59WestSpotID && GameState.getNoradPlayedGlobeGame()) { + ExtraTable::Entry extraEntry; + getExtraEntry(kN59ZoomWithRobot, extraEntry); + zoomEntry.movieStart = extraEntry.movieStart; + zoomEntry.movieEnd = extraEntry.movieEnd; + } +} + +void NoradDelta::loadAmbientLoops() { +/* + Logic: + + loop sound 1: + if room == kNorad79West + if player globe game + play kNoradGlobeLoop2SoundNum + else + play kNoradRedAlertLoopSoundNum + else if room >= kNorad78 && room <= kNorad79 + play kNoradGlobeLoop2SoundNum + else if gassed, + if room >= kNorad41 && room <= kNorad49South + play kNoradNewSubLoopSoundNum, kNoradWarningVolume + else if room >= kNorad59 && room <= kNorad60West + play kNoradSubControlLoopSoundNum, kNoradWarningVolume + else + play kNoradWarningLoopSoundNum, kNoradWarningVolume + else + play nothing + loop sound 2: + if gassed and not wearing air mask + if room == kNorad54North + play breathing unmanned loop + else + play breathing + else + if room == kNorad54North + play unmanned loop + else + play nothing +*/ + + if (GameState.getNoradArrivedFromSub()) { + RoomID room = GameState.getCurrentRoom(); + + if (room == kNorad79West) { + if (_privateFlags.getFlag(kNoradPrivateFinishedGlobeGameFlag)) + loadLoopSound1("Sounds/Norad/GlobAmb2.22K.AIFF"); + else + loadLoopSound1("Sounds/Norad/RedAlert.22K.AIFF"); + } else if (room >= kNorad78 && room <= kNorad79) { + // clone2727 says: This looks like it should be loadLoopSound1... + loadLoopSound2("Sounds/Norad/RedAlert.22K.AIFF"); + } else if (GameState.getNoradGassed()) { + if (room >= kNorad41 && room <= kNorad49South) + loadLoopSound1("Sounds/Norad/NEW SUB AMB.22K.AIFF", kNoradWarningVolume * 3); + else if (room >= kNorad59 && room <= kNorad60West) + loadLoopSound1("Sounds/Norad/SUB CONTRL LOOP.22K.AIFF", kNoradWarningVolume * 3); + else + loadLoopSound1("Sounds/Norad/WARNING LOOP.22K.AIFF", kNoradWarningVolume); + } else { + loadLoopSound1(""); + } + + if (GameState.getNoradGassed() && !g_airMask->isAirFilterOn()) { + if (room == kNorad54North) + loadLoopSound2("Sounds/Norad/Breathing Typing.22K.AIFF", 0x100 / 2); + else + loadLoopSound2("Sounds/Norad/SUCKING WIND.22K.AIFF", kNoradSuckWindVolume, 0, 0); + } else { + if (room == kNorad54North) + loadLoopSound2("Sounds/Norad/N54NAS.22K.AIFF", 0x100 / 2); + else + loadLoopSound2(""); + } + } else { + // Start them off at zero... + if (GameState.getNoradGassed()) + loadLoopSound1("Sounds/Norad/NEW SUB AMB.22K.AIFF", 0, 0, 0); + if (!g_airMask->isAirFilterOn()) + loadLoopSound2("Sounds/Norad/SUCKING WIND.22K.AIFF", 0, 0, 0); + } +} + +void NoradDelta::checkContinuePoint(const RoomID room, const DirectionConstant direction) { + switch (MakeRoomView(room, direction)) { + case MakeRoomView(kNorad41, kEast): + case MakeRoomView(kNorad49, kEast): + case MakeRoomView(kNorad49, kWest): + case MakeRoomView(kNorad61, kSouth): + case MakeRoomView(kNorad68, kEast): + case MakeRoomView(kNorad79, kWest): + makeContinuePoint(); + break; + } +} + +void NoradDelta::arriveAt(const RoomID room, const DirectionConstant direction) { + if (room != kNorad68) + GameState.setNoradRetScanGood(false); + + Norad::arriveAt(room, direction); + + FaderMoveSpec loop1Spec, loop2Spec; + ExtraTable::Entry entry; + + switch (room) { + case kNorad41: + if (direction == kEast && !GameState.getNoradArrivedFromSub()) { + GameState.setNoradPlayedGlobeGame(false); + + GameState.setNoradBeatRobotWithClaw(false); + GameState.setNoradBeatRobotWithDoor(false); + GameState.setNoradRetScanGood(false); + + GameState.setScoringExitedSub(true); + + getExtraEntry(kArriveFromSubChase, entry); + + loop1Spec.makeTwoKnotFaderSpec(kNoradDeltaMovieScale, 0, 0, entry.movieEnd - + entry.movieStart, kNoradWarningVolume); + loop1Spec.insertFaderKnot(7320, 0); + loop1Spec.insertFaderKnot(7880, kNoradWarningVolume); + + loop2Spec.makeTwoKnotFaderSpec(kNoradDeltaMovieScale, 0, 0, entry.movieEnd - + entry.movieStart, kNoradSuckWindVolume); + loop1Spec.insertFaderKnot(7320, 0); + loop1Spec.insertFaderKnot(7880, kNoradSuckWindVolume); + + startExtraSequence(kArriveFromSubChase, kExtraCompletedFlag, kFilterNoInput); + + startLoop1Fader(loop1Spec); + startLoop2Fader(loop2Spec); + } + break; + case kNorad54North: + GameState.setScoringSawRobotAt54North(true); + break; + case kNorad68: + if (GameState.getNoradRetScanGood()) + openDoor(); + break; + case kNorad68West: + arriveAtNorad68West(); + break; + case kNorad79West: + arriveAtNorad79West(); + break; + default: + break; + } +} + +void NoradDelta::doorOpened() { + Norad::doorOpened(); + GameState.setNoradRetScanGood(false); +} + +void NoradDelta::arriveAtNorad68West() { + playSpotSoundSync(kHoldForRetinalIn, kHoldForRetinalOut); + + BiochipItem *retScan = _vm->getCurrentBiochip(); + + if (retScan != 0 && retScan->getObjectID() == kRetinalScanBiochip) { + ((RetScanChip *)retScan)->searchForLaser(); + succeedRetinalScan(); + } else { + failRetinalScan(); + } +} + +void NoradDelta::arriveAtNorad79West() { + if (!GameState.getNoradPlayedGlobeGame()) + newInteraction(kNoradGlobeGameInteractionID); +} + +void NoradDelta::bumpIntoWall() { + requestSpotSound(kDeltaBumpIntoWallIn, kDeltaBumpIntoWallOut, kFilterNoInput, 0); + Neighborhood::bumpIntoWall(); +} + +void NoradDelta::failRetinalScan() { + startExtraSequence(kNoradDeltaRetinalScanBad, kExtraCompletedFlag, kFilterNoInput); +} + +void NoradDelta::succeedRetinalScan() { + startExtraSequence(kNoradDeltaRetinalScanGood, kExtraCompletedFlag, kFilterNoInput); + GameState.setNoradRetScanGood(true); + GameState.setScoringUsedRetinalChip(true); +} + +void NoradDelta::getDoorEntry(const RoomID room, const DirectionConstant direction, DoorTable::Entry &entry) { + Norad::getDoorEntry(room, direction, entry); + + if (room == kNorad68 && direction == kWest && !GameState.getNoradRetScanGood()) + entry.flags = kDoorPresentMask | kDoorLockedMask; +} + +void NoradDelta::finishedGlobeGame() { + GameState.setNoradPlayedGlobeGame(true); + _privateFlags.setFlag(kNoradPrivateFinishedGlobeGameFlag, true); + GameState.setScoringFinishedGlobeGame(true); + loadAmbientLoops(); + g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Norad/XN60WD1", false, kWarningInterruption); +} + +bool NoradDelta::playingAgainstRobot() { + return GameState.getNoradPlayedGlobeGame(); +} + +void NoradDelta::getClawInfo(HotSpotID &outSpotID, HotSpotID &prepSpotID, HotSpotID &clawControlSpotID, HotSpotID &pinchClawSpotID, + HotSpotID &moveClawDownSpotID, HotSpotID &moveClawRightSpotID, HotSpotID &moveClawLeftSpotID, HotSpotID &moveClawUpSpotID, + HotSpotID &clawCCWSpotID, HotSpotID &clawCWSpotID, uint32 &clawPosition, const uint32 *&clawExtraIDs) { + outSpotID = kNorad60MonitorOutSpotID; + prepSpotID = kNorad60LaunchPrepSpotID; + clawControlSpotID = kNorad60ClawControlSpotID; + pinchClawSpotID = kNorad60ClawPinchSpotID; + moveClawDownSpotID = kNorad60ClawDownSpotID; + moveClawRightSpotID = kNorad60ClawRightSpotID; + moveClawLeftSpotID = kNorad60ClawLeftSpotID; + moveClawUpSpotID = kNorad60ClawUpSpotID; + clawCCWSpotID = kNorad60ClawCCWSpotID; + clawCWSpotID = kNorad60ClawCWSpotID; + clawPosition = kClawAtC; + clawExtraIDs = _noradDeltaClawExtras; +} + +void NoradDelta::playerBeatRobotWithDoor() { + GameState.setNoradBeatRobotWithDoor(true); + updateViewFrame(); + GameState.setScoringStoppedNoradRobot(true); + g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Norad/XN59WD", false, kWarningInterruption); +} + +void NoradDelta::playerBeatRobotWithClaw() { + GameState.setNoradBeatRobotWithClaw(true); + updateViewFrame(); + GameState.setScoringStoppedNoradRobot(true); + GameState.setScoringNoradGandhi(true); + g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Norad/XN59WD", false, kWarningInterruption); +} + +TimeValue NoradDelta::getViewTime(const RoomID room, const DirectionConstant direction) { + ExtraTable::Entry entry; + + if (room == kNorad41 && direction == kSouth && !GameState.getNoradArrivedFromSub()) { + getExtraEntry(kArriveFromSubChase, entry); + return entry.movieStart; + } + + if (GameState.getNoradBeatRobotWithDoor()) { + if (_privateFlags.getFlag(kNoradPrivateRobotHeadOpenFlag)) { + uint32 extraID = kN59Biochips111; + if (_privateFlags.getFlag(kNoradPrivateGotShieldChipFlag)) + extraID += 1; + if (_privateFlags.getFlag(kNoradPrivateGotOpticalChipFlag)) + extraID += 2; + if (_privateFlags.getFlag(kNoradPrivateGotRetScanChipFlag)) + extraID += 4; + getExtraEntry(extraID, entry); + return entry.movieStart; + } + + getExtraEntry(kN59RobotHeadOpens, entry); + return entry.movieStart; + } else if (GameState.getNoradBeatRobotWithClaw()) { + if (_privateFlags.getFlag(kNoradPrivateRobotHeadOpenFlag)) { + uint32 extraID = kN60Biochips111; + if (_privateFlags.getFlag(kNoradPrivateGotShieldChipFlag)) + extraID += 1; + if (_privateFlags.getFlag(kNoradPrivateGotOpticalChipFlag)) + extraID += 2; + if (_privateFlags.getFlag(kNoradPrivateGotRetScanChipFlag)) + extraID += 4; + getExtraEntry(extraID, entry); + return entry.movieStart; + } + + getExtraEntry(kN60RobotHeadOpens, entry); + return entry.movieStart; + } + + return Norad::getViewTime(room, direction); +} + +void NoradDelta::openDoor() { + if (GameState.getCurrentRoom() == kNorad59 && GameState.getCurrentDirection() == kWest && GameState.getNoradPlayedGlobeGame()) { + Input scratch; + InputHandler::_inputHandler->clickInHotspot(scratch, _vm->getAllHotspots().findHotspotByID(kNorad59WestSpotID)); + } else { + Norad::openDoor(); + } +} + +void NoradDelta::activateHotspots() { + Norad::activateHotspots(); + + if (GameState.getCurrentRoom() == kNorad59West && GameState.getCurrentDirection() == kWest && GameState.getNoradBeatRobotWithDoor()) { + _vm->getAllHotspots().deactivateOneHotspot(kNorad59WestOutSpotID); + + if (_privateFlags.getFlag(kNoradPrivateRobotHeadOpenFlag)) { + if (!_privateFlags.getFlag(kNoradPrivateGotShieldChipFlag)) + _vm->getAllHotspots().activateOneHotspot(kDelta59RobotShieldBiochipSpotID); + else + _vm->getAllHotspots().deactivateOneHotspot(kDelta59RobotShieldBiochipSpotID); + + if (!_privateFlags.getFlag(kNoradPrivateGotOpticalChipFlag)) + _vm->getAllHotspots().activateOneHotspot(kDelta59RobotOpMemBiochipSpotID); + else + _vm->getAllHotspots().deactivateOneHotspot(kDelta59RobotOpMemBiochipSpotID); + + if (!_privateFlags.getFlag(kNoradPrivateGotRetScanChipFlag)) + _vm->getAllHotspots().activateOneHotspot(kDelta59RobotRetinalBiochipSpotID); + else + _vm->getAllHotspots().deactivateOneHotspot(kDelta59RobotRetinalBiochipSpotID); + } else + _vm->getAllHotspots().activateOneHotspot(kDelta59RobotHeadSpotID); + } else if (GameState.getCurrentRoom() == kNorad60West && GameState.getCurrentDirection() == kWest && + GameState.getNoradBeatRobotWithClaw()) { + _vm->getAllHotspots().deactivateOneHotspot(kNorad60MonitorOutSpotID); + + if (_privateFlags.getFlag(kNoradPrivateRobotHeadOpenFlag)) { + if (!_privateFlags.getFlag(kNoradPrivateGotShieldChipFlag)) + _vm->getAllHotspots().activateOneHotspot(kDelta60RobotShieldBiochipSpotID); + else + _vm->getAllHotspots().deactivateOneHotspot(kDelta60RobotShieldBiochipSpotID); + + if (!_privateFlags.getFlag(kNoradPrivateGotOpticalChipFlag)) + _vm->getAllHotspots().activateOneHotspot(kDelta60RobotOpMemBiochipSpotID); + else + _vm->getAllHotspots().deactivateOneHotspot(kDelta60RobotOpMemBiochipSpotID); + + if (!_privateFlags.getFlag(kNoradPrivateGotRetScanChipFlag)) + _vm->getAllHotspots().activateOneHotspot(kDelta60RobotRetinalBiochipSpotID); + else + _vm->getAllHotspots().deactivateOneHotspot(kDelta60RobotRetinalBiochipSpotID); + } else { + _vm->getAllHotspots().activateOneHotspot(kDelta60RobotHeadSpotID); + } + } else if (GameState.getCurrentRoomAndView() == MakeRoomView(kNorad50, kEast)) { + if (GameState.isCurrentDoorOpen()) + _vm->getAllHotspots().deactivateOneHotspot(kNorad50DoorSpotID); + } else if (GameState.getCurrentRoomAndView() == MakeRoomView(kNorad59, kWest)) { + if (GameState.isCurrentDoorOpen()) + _vm->getAllHotspots().deactivateOneHotspot(kNorad59WestSpotID); + } +} + +void NoradDelta::clickInHotspot(const Input &input, const Hotspot *clickedSpot) { + switch (clickedSpot->getObjectID()) { + case kDelta59RobotHeadSpotID: + startExtraSequence(kN59RobotHeadOpens, kExtraCompletedFlag, kFilterNoInput); + break; + case kDelta60RobotHeadSpotID: + startExtraSequence(kN60RobotHeadOpens, kExtraCompletedFlag, kFilterNoInput); + break; + default: + Norad::clickInHotspot(input, clickedSpot); + break; + } +} + +void NoradDelta::receiveNotification(Notification *notification, const NotificationFlags flags) { + Norad::receiveNotification(notification, flags); + + if ((flags & kExtraCompletedFlag) != 0) { + RetScanChip *retScan; + Input dummy; + + switch (_lastExtra) { + case kArriveFromSubChase: + GameState.setNoradArrivedFromSub(true); + GameState.setCurrentRoom(kNoRoomID); + GameState.setCurrentDirection(kNoDirection); + arriveAt(kNorad41, kEast); + break; + case kN59RobotHeadOpens: + case kN60RobotHeadOpens: + _privateFlags.setFlag(kNoradPrivateRobotHeadOpenFlag, true); + break; + case kNoradDeltaRetinalScanBad: + retScan = (RetScanChip *)_vm->getCurrentBiochip(); + retScan->setItemState(kNormalItem); + playSpotSoundSync(kRetinalScanFailedIn, kRetinalScanFailedOut); + downButton(dummy); + break; + case kNoradDeltaRetinalScanGood: + retScan = (RetScanChip *)_vm->getCurrentBiochip(); + retScan->setItemState(kNormalItem); + downButton(dummy); + break; + case kN59RobotDisappears: + case kN60RobotDisappears: + recallToTSASuccess(); + break; + } + + _interruptionFilter = kFilterAllInput; + } + + g_AIArea->checkMiddleArea(); +} + +void NoradDelta::pickedUpItem(Item *item) { + switch (item->getObjectID()) { + case kShieldBiochip: + if (_privateFlags.getFlag(kNoradPrivateGotShieldChipFlag) && + _privateFlags.getFlag(kNoradPrivateGotRetScanChipFlag) && + _privateFlags.getFlag(kNoradPrivateGotOpticalChipFlag)) { + GameState.setNoradFinished(true); + + if (GameState.getCurrentRoom() == kNorad59West) + startExtraSequence(kN59RobotDisappears, kExtraCompletedFlag, kFilterNoInput); + else + startExtraSequence(kN60RobotDisappears, kExtraCompletedFlag, kFilterNoInput); + } + break; + case kRetinalScanBiochip: + if (_privateFlags.getFlag(kNoradPrivateGotShieldChipFlag) && + _privateFlags.getFlag(kNoradPrivateGotRetScanChipFlag) && + _privateFlags.getFlag(kNoradPrivateGotOpticalChipFlag)) { + GameState.setNoradFinished(true); + + if (GameState.getCurrentRoom() == kNorad59West) + startExtraSequence(kN59RobotDisappears, kExtraCompletedFlag, kFilterNoInput); + else + startExtraSequence(kN60RobotDisappears, kExtraCompletedFlag, kFilterNoInput); + } + break; + case kOpticalBiochip: + g_opticalChip->addPoseidon(); + GameState.setScoringGotNoradOpMemChip(); + + if (_privateFlags.getFlag(kNoradPrivateGotShieldChipFlag) && + _privateFlags.getFlag(kNoradPrivateGotRetScanChipFlag) && + _privateFlags.getFlag(kNoradPrivateGotOpticalChipFlag)) { + GameState.setNoradFinished(true); + + if (GameState.getCurrentRoom() == kNorad59West) + startExtraSequence(kN59RobotDisappears, kExtraCompletedFlag, kFilterNoInput); + else + startExtraSequence(kN60RobotDisappears, kExtraCompletedFlag, kFilterNoInput); + } + break; + } + + Norad::pickedUpItem(item); +} + +void NoradDelta::takeItemFromRoom(Item *item) { + switch (item->getObjectID()) { + case kShieldBiochip: + _privateFlags.setFlag(kNoradPrivateGotShieldChipFlag, true); + break; + case kRetinalScanBiochip: + _privateFlags.setFlag(kNoradPrivateGotRetScanChipFlag, true); + break; + case kOpticalBiochip: + _privateFlags.setFlag(kNoradPrivateGotOpticalChipFlag, true); + break; + } + + Norad::takeItemFromRoom(item); +} + +void NoradDelta::dropItemIntoRoom(Item *item, Hotspot *hotspot) { + switch (item->getObjectID()) { + case kShieldBiochip: + _privateFlags.setFlag(kNoradPrivateGotShieldChipFlag, false); + break; + case kOpticalBiochip: + _privateFlags.setFlag(kNoradPrivateGotOpticalChipFlag, false); + break; + case kRetinalScanBiochip: + _privateFlags.setFlag(kNoradPrivateGotRetScanChipFlag, false); + break; + } + + Norad::dropItemIntoRoom(item, hotspot); +} + +Hotspot *NoradDelta::getItemScreenSpot(Item *item, DisplayElement *element) { + HotSpotID id = kNoHotSpotID; + + switch (item->getObjectID()) { + case kShieldBiochip: + if (GameState.getNoradBeatRobotWithDoor()) + id = kDelta59RobotShieldBiochipSpotID; + else + id = kDelta60RobotShieldBiochipSpotID; + break; + case kOpticalBiochip: + if (GameState.getNoradBeatRobotWithDoor()) + id = kDelta59RobotOpMemBiochipSpotID; + else + id = kDelta60RobotOpMemBiochipSpotID; + break; + case kRetinalScanBiochip: + if (GameState.getNoradBeatRobotWithDoor()) + id = kDelta59RobotRetinalBiochipSpotID; + else + id = kDelta60RobotRetinalBiochipSpotID; + break; + } + + if (id != kNoHotSpotID) + return _vm->getAllHotspots().findHotspotByID(id); + + return Norad::getItemScreenSpot(item, element); +} + +Common::String NoradDelta::getEnvScanMovie() { + return "Images/AI/Norad/XNE2"; +} + +uint NoradDelta::getNumHints() { + uint numHints = Neighborhood::getNumHints(); + + if (numHints == 0) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kNorad60, kWest): + if (GameState.getNoradPlayedGlobeGame()) + numHints = 2; + else + numHints = 1; + break; + case MakeRoomView(kNorad59, kNorth): + case MakeRoomView(kNorad59, kSouth): + case MakeRoomView(kNorad59, kEast): + case MakeRoomView(kNorad59, kWest): + case MakeRoomView(kNorad60, kNorth): + case MakeRoomView(kNorad60, kSouth): + case MakeRoomView(kNorad60, kEast): + if (GameState.getNoradPlayedGlobeGame()) + numHints = 2; + break; + case MakeRoomView(kNorad68, kWest): + if (_vm->playerHasItemID(kRetinalScanBiochip)) { + BiochipItem *retScan = _vm->getCurrentBiochip(); + if (retScan == 0 || retScan->getObjectID() != kRetinalScanBiochip) + numHints = 2; + } else if (!GameState.isCurrentDoorOpen()) { + numHints = 2; + } + break; + } + } + + return numHints; +} + +Common::String NoradDelta::getHintMovie(uint hintNum) { + Common::String movieName = Neighborhood::getHintMovie(hintNum); + + if (movieName.empty()) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kNorad60, kWest): + if (GameState.getNoradPlayedGlobeGame()) { + if (hintNum == 1) + return "Images/AI/Norad/XN60WD2"; + + return "Images/AI/Norad/XN60WD3"; + } + + return "Images/AI/Globals/XGLOB1C"; + case MakeRoomView(kNorad59, kNorth): + case MakeRoomView(kNorad59, kSouth): + case MakeRoomView(kNorad59, kEast): + case MakeRoomView(kNorad59, kWest): + case MakeRoomView(kNorad60, kNorth): + case MakeRoomView(kNorad60, kSouth): + case MakeRoomView(kNorad60, kEast): + if (hintNum == 1) + return "Images/AI/Norad/XN60WD2"; + + return "Images/AI/Norad/XN60WD3"; + case MakeRoomView(kNorad68, kWest): + if (_vm->playerHasItemID(kRetinalScanBiochip)) { + if (hintNum == 1) + return "Images/AI/Globals/XGLOB1A"; + + return "Images/AI/Globals/XGLOB1C"; + } + + if (hintNum == 1) + return "Images/AI/Globals/XGLOB1B"; + + return "Images/AI/Globals/XGLOB3B"; + } + } + + return movieName; +} + +void NoradDelta::closeDoorOffScreen(const RoomID room, const DirectionConstant) { + switch (room) { + case kNorad47: + case kNorad48: + case kNorad41: + case kNorad42: + playSpotSoundSync(kDeltaElevatorDoorCloseIn, kDeltaElevatorDoorCloseOut); + break; + default: + playSpotSoundSync(kDeltaRegDoorCloseIn, kDeltaRegDoorCloseOut); + break; + } +} + +bool NoradDelta::canSolve() { + if (Norad::canSolve()) + return true; + + if (GameState.getCurrentRoomAndView() == MakeRoomView(kNorad68, kWest)) { + BiochipItem *biochip = _vm->getCurrentBiochip(); + if (biochip != 0 && biochip->getObjectID() != kRetinalScanBiochip) + return true; + } + + return false; +} + +void NoradDelta::doSolve() { + Norad::doSolve(); + + if (GameState.getCurrentRoomAndView() == MakeRoomView(kNorad68, kWest)) { + if (!_vm->playerHasItemID(kRetinalScanBiochip)) + _vm->addItemToBiochips((BiochipItem *)_vm->getAllItems().findItemByID(kRetinalScanBiochip)); + + BiochipItem *biochip = _vm->getCurrentBiochip(); + if (biochip != 0 && biochip->getObjectID() != kRetinalScanBiochip && g_interface) + g_interface->setCurrentBiochipID(kRetinalScanBiochip); + + Hotspot *spot = _vm->getAllHotspots().findHotspotByID(kNorad68WestSpotID); + Input scratch; + InputHandler::_inputHandler->clickInHotspot(scratch, spot); + } +} + +Common::String NoradDelta::getSoundSpotsName() { + return "Sounds/Norad/Norad Delta Spots"; +} + +Common::String NoradDelta::getNavMovieName() { + return "Images/Norad Delta/Norad Delta.movie"; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/norad/delta/noraddelta.h b/engines/pegasus/neighborhood/norad/delta/noraddelta.h new file mode 100644 index 0000000000..11065f2c9d --- /dev/null +++ b/engines/pegasus/neighborhood/norad/delta/noraddelta.h @@ -0,0 +1,117 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_NEIGHBORHOOD_NORAD_DELTA_NORADDELTA_H +#define PEGASUS_NEIGHBORHOOD_NORAD_DELTA_NORADDELTA_H + +#include "pegasus/neighborhood/norad/norad.h" + +namespace Pegasus { + +class NoradDelta : public Norad { +public: + NoradDelta(InputHandler *, PegasusEngine *); + virtual ~NoradDelta() {} + + void init(); + + void start(); + + void getExtraCompassMove(const ExtraTable::Entry &, FaderMoveSpec &); + + void finishedGlobeGame(); + + virtual GameInteraction *makeInteraction(const InteractionID); + + void playClawMonitorIntro(); + + virtual void getClawInfo(HotSpotID &outSpotID, HotSpotID &prepSpotID, HotSpotID &clawControlSpotID, + HotSpotID &pinchClawSpotID, HotSpotID &moveClawDownSpotID, HotSpotID &moveClawRightSpotID, + HotSpotID &moveClawLeftSpotID, HotSpotID &moveClawUpSpotID, HotSpotID &clawCCWSpotID, + HotSpotID &clawCWSpotID, uint32 &, const uint32 *&); + + void playerBeatRobotWithClaw(); + void playerBeatRobotWithDoor(); + + void loadAmbientLoops(); + + void setUpAIRules(); + Common::String getEnvScanMovie(); + uint getNumHints(); + Common::String getHintMovie(uint); + void closeDoorOffScreen(const RoomID, const DirectionConstant); + + void checkContinuePoint(const RoomID, const DirectionConstant); + + bool canSolve(); + void doSolve(); + + void doorOpened(); + +protected: + enum { + kNoradPrivateArrivedFromSubFlag, + kNoradPrivateFinishedGlobeGameFlag, + kNoradPrivateRobotHeadOpenFlag, + kNoradPrivateGotShieldChipFlag, + kNoradPrivateGotOpticalChipFlag, + kNoradPrivateGotRetScanChipFlag, + kNumNoradPrivateFlags + }; + + static const uint32 _noradDeltaClawExtras[22]; + + void getExitEntry(const RoomID, const DirectionConstant, ExitTable::Entry &); + void getZoomEntry(const HotSpotID, ZoomTable::Entry &); + virtual void arriveAt(const RoomID, const DirectionConstant); + void arriveAtNorad68West(); + void arriveAtNorad79West(); + TimeValue getViewTime(const RoomID, const DirectionConstant); + void openDoor(); + void activateHotspots(); + void clickInHotspot(const Input &, const Hotspot *); + void receiveNotification(Notification *, const NotificationFlags); + void pickedUpItem(Item *item); + void takeItemFromRoom(Item *item); + void dropItemIntoRoom(Item *item, Hotspot *); + Hotspot *getItemScreenSpot(Item *, DisplayElement *); + + virtual bool playingAgainstRobot(); + + void failRetinalScan(); + void succeedRetinalScan(); + void getDoorEntry(const RoomID, const DirectionConstant, DoorTable::Entry &); + + void bumpIntoWall(); + + FlagsArray<byte, kNumNoradPrivateFlags> _privateFlags; + + Common::String getSoundSpotsName(); + Common::String getNavMovieName(); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/norad/norad.cpp b/engines/pegasus/neighborhood/norad/norad.cpp new file mode 100644 index 0000000000..578f062dea --- /dev/null +++ b/engines/pegasus/neighborhood/norad/norad.cpp @@ -0,0 +1,285 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/energymonitor.h" +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/inventory/airmask.h" +#include "pegasus/neighborhood/norad/constants.h" +#include "pegasus/neighborhood/norad/norad.h" +#include "pegasus/neighborhood/norad/noradelevator.h" +#include "pegasus/neighborhood/norad/pressuredoor.h" +#include "pegasus/neighborhood/norad/subcontrolroom.h" +#include "pegasus/neighborhood/norad/subplatform.h" + +namespace Pegasus { + +const NotificationFlags kDoneWithPressureDoorNotification = 1; + +const NotificationFlags kNoradNotificationFlags = kDoneWithPressureDoorNotification; + +// This class handles everything that Norad Alpha and Delta have in common, such as +// oxygen mask usage, the elevator and the pressure doors. + +Norad::Norad(InputHandler *nextHandler, PegasusEngine *vm, const Common::String &resName, NeighborhoodID id) : + Neighborhood(nextHandler, vm, resName, id), _noradNotification(kNoradNotificationID, vm) { + _elevatorUpSpotID = kNoHotSpotID; + _elevatorDownSpotID = kNoHotSpotID; + _elevatorUpRoomID = kNoHotSpotID; + _elevatorDownRoomID = kNoHotSpotID; + + _subRoomEntryRoom1 = kNoRoomID; + _subRoomEntryDir1 = kNoDirection; + _subRoomEntryRoom2 = kNoRoomID; + _subRoomEntryDir2 = kNoDirection; + _upperPressureDoorRoom = kNoRoomID; + _lowerPressureDoorRoom = kNoRoomID; + + _upperPressureDoorUpSpotID = kNoHotSpotID; + _upperPressureDoorDownSpotID = kNoHotSpotID; + _upperPressureDoorAbortSpotID = kNoHotSpotID; + + _lowerPressureDoorUpSpotID = kNoHotSpotID; + _lowerPressureDoorDownSpotID = kNoHotSpotID; + _lowerPressureDoorAbortSpotID = kNoHotSpotID; + + _pressureSoundIn = 0xffffffff; + _pressureSoundOut = 0xffffffff; + _equalizeSoundIn = 0xffffffff; + _equalizeSoundOut = 0xffffffff; + _accessDeniedIn = 0xffffffff; + _accessDeniedOut = 0xffffffff; + + _platformRoom = kNoRoomID; + _subControlRoom = kNoRoomID; + + _doneWithPressureDoor = false; + + _noradNotification.notifyMe(this, kNoradNotificationFlags, kNoradNotificationFlags); +} + +GameInteraction *Norad::makeInteraction(const InteractionID interactionID) { + PressureDoor *pressureDoor; + SubControlRoom *subControl; + + switch (interactionID) { + case kNoradElevatorInteractionID: + return new NoradElevator(this, _elevatorUpRoomID, _elevatorDownRoomID, _elevatorUpSpotID, _elevatorDownSpotID); + case kNoradPressureDoorInteractionID: + if (GameState.getCurrentRoom() == _upperPressureDoorRoom) + pressureDoor = new PressureDoor(this, true, _upperPressureDoorUpSpotID, _upperPressureDoorDownSpotID, + _upperPressureDoorAbortSpotID, _pressureSoundIn, _pressureSoundOut, _equalizeSoundIn, _equalizeSoundOut); + else + pressureDoor = new PressureDoor(this, false, _lowerPressureDoorUpSpotID, _lowerPressureDoorDownSpotID, + _lowerPressureDoorAbortSpotID, _pressureSoundIn, _pressureSoundOut, _equalizeSoundIn, _equalizeSoundOut); + + if (GameState.getCurrentRoom() == kNorad59West && playingAgainstRobot()) + pressureDoor->playAgainstRobot(); + + return pressureDoor; + case kNoradSubControlRoomInteractionID: + subControl = new SubControlRoom(this); + + if (GameState.getCurrentRoom() == kNorad60West && playingAgainstRobot()) + subControl->playAgainstRobot(); + + return subControl; + case kNoradSubPlatformInteractionID: + return new SubPlatform(this); + default: + return 0; + } +} + +void Norad::flushGameState() { + g_energyMonitor->saveCurrentEnergyValue(); +} + +void Norad::start() { + setUpAirMask(); + Neighborhood::start(); +} + +void Norad::activateHotspots() { + Neighborhood::activateHotspots(); + + RoomID room = GameState.getCurrentRoom(); + if (room == _elevatorUpRoomID) + _neighborhoodHotspots.activateOneHotspot(_elevatorDownSpotID); + else if (room == _elevatorDownRoomID) + _neighborhoodHotspots.activateOneHotspot(_elevatorUpSpotID); +} + +void Norad::arriveAt(const RoomID room, const DirectionConstant direction) { + Neighborhood::arriveAt(room, direction); + + if (GameState.getCurrentRoom() == _elevatorUpRoomID || GameState.getCurrentRoom() == _elevatorDownRoomID) + arriveAtNoradElevator(); + else if (GameState.getCurrentRoom() == _upperPressureDoorRoom) + arriveAtUpperPressureDoorRoom(); + else if (GameState.getCurrentRoom() == _lowerPressureDoorRoom) + arriveAtLowerPressureDoorRoom(); + else if (GameState.getCurrentRoom() == _platformRoom) + arriveAtSubPlatformRoom(); + else if (GameState.getCurrentRoom() == _subControlRoom) + arriveAtSubControlRoom(); + + if (_doneWithPressureDoor) { + _doneWithPressureDoor = false; + openDoor(); + } +} + +void Norad::arriveAtNoradElevator() { + if (_currentInteraction) + _currentInteraction->startOverInteraction(); + else + newInteraction(kNoradElevatorInteractionID); +} + +void Norad::arriveAtUpperPressureDoorRoom() { + newInteraction(kNoradPressureDoorInteractionID); +} + +void Norad::arriveAtLowerPressureDoorRoom() { + newInteraction(kNoradPressureDoorInteractionID); +} + +void Norad::arriveAtSubPlatformRoom() { + newInteraction(kNoradSubPlatformInteractionID); +} + +void Norad::arriveAtSubControlRoom() { + newInteraction(kNoradSubControlRoomInteractionID); +} + +int16 Norad::getStaticCompassAngle(const RoomID room, const DirectionConstant dir) { + int16 result = Neighborhood::getStaticCompassAngle(room, dir); + + if (room == _elevatorUpRoomID || room == _elevatorDownRoomID) + result += kElevatorCompassAngle; + else if (room == _platformRoom) + result += kSubPlatformCompassAngle; + else if (room == _subControlRoom) + result += kSubControlCompassAngle; + + return result; +} + +CanOpenDoorReason Norad::canOpenDoor(DoorTable::Entry &entry) { + if (((GameState.getCurrentRoom() == _subRoomEntryRoom1 && GameState.getCurrentDirection() == _subRoomEntryDir1) || + (GameState.getCurrentRoom() == _subRoomEntryRoom2 && GameState.getCurrentDirection() == _subRoomEntryDir2)) && + GameState.getNoradSubRoomPressure() != kNormalSubRoomPressure) + return kCantOpenBadPressure; + + return Neighborhood::canOpenDoor(entry); +} + +void Norad::cantOpenDoor(CanOpenDoorReason reason) { + if (reason == kCantOpenBadPressure) + playSpotSoundSync(_pressureSoundIn, _pressureSoundOut); + else + playSpotSoundSync(_accessDeniedIn, _accessDeniedOut); +} + +void Norad::startExitMovie(const ExitTable::Entry &exitEntry) { + if (GameState.getCurrentRoom() == _elevatorUpRoomID) { + if (exitEntry.exitRoom != _elevatorDownRoomID) + newInteraction(kNoInteractionID); + } else if (GameState.getCurrentRoom() == _elevatorDownRoomID) { + if (exitEntry.exitRoom != _elevatorUpRoomID) + newInteraction(kNoInteractionID); + } else { + newInteraction(kNoInteractionID); + } + + Neighborhood::startExitMovie(exitEntry); +} + +void Norad::startZoomMovie(const ZoomTable::Entry &zoomEntry) { + newInteraction(kNoInteractionID); + Neighborhood::startZoomMovie(zoomEntry); +} + +void Norad::upButton(const Input &input) { + if (GameState.getCurrentRoom() != _elevatorUpRoomID && GameState.getCurrentRoom() != _elevatorDownRoomID) + Neighborhood::upButton(input); +} + +void Norad::setUpAirMask() { + _airMaskCallBack.setNotification(&_neighborhoodNotification); + _airMaskCallBack.initCallBack(&_airMaskTimer, kCallBackAtExtremes); + _airMaskCallBack.setCallBackFlag(kAirTimerExpiredFlag); + _neighborhoodNotification.notifyMe(this, kAirTimerExpiredFlag, kAirTimerExpiredFlag); + _airMaskCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + _airMaskTimer.setScale(1); + _airMaskTimer.setSegment(0, kNoradAirMaskTimeLimit); + checkAirMask(); +} + +void Norad::checkAirMask() { + if (g_airMask && g_airMask->isAirFilterOn()) { + _airMaskTimer.stop(); + } else if (GameState.getNoradGassed() && !_airMaskTimer.isRunning()) { + _airMaskTimer.setTime(0); + _airMaskTimer.start(); + } + + loadAmbientLoops(); +} + +void Norad::receiveNotification(Notification *notification, const NotificationFlags flags) { + if (notification == &_neighborhoodNotification && (flags & kAirTimerExpiredFlag) != 0) + ((PegasusEngine *)g_engine)->die(kDeathGassedInNorad); + + Neighborhood::receiveNotification(notification, flags); + + if (notification == &_noradNotification) { + // Must be kDoneWithPressureDoorNotification... + Input scratch; + _doneWithPressureDoor = true; + downButton(scratch); + } +} + +uint16 Norad::getDateResID() const { + return kDate2112ID; +} + +Common::String Norad::getBriefingMovie() { + return "Images/AI/Norad/XNO"; +} + +void Norad::pickedUpItem(Item *item) { + Neighborhood::pickedUpItem(item); + g_AIArea->checkMiddleArea(); +} + +void Norad::doneWithPressureDoor() { + _noradNotification.setNotificationFlags(kDoneWithPressureDoorNotification, kDoneWithPressureDoorNotification); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/norad/norad.h b/engines/pegasus/neighborhood/norad/norad.h new file mode 100644 index 0000000000..3cd77cc54b --- /dev/null +++ b/engines/pegasus/neighborhood/norad/norad.h @@ -0,0 +1,121 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_NEIGHBORHOOD_NORAD_NORAD_H +#define PEGASUS_NEIGHBORHOOD_NORAD_NORAD_H + +#include "pegasus/neighborhood/neighborhood.h" + +namespace Pegasus { + +// This is the code common to both Norad Alpha and Norad Delta + +class Norad : public Neighborhood { +public: + Norad(InputHandler *, PegasusEngine *owner, const Common::String &resName, const NeighborhoodID); + virtual ~Norad() {} + + void flushGameState(); + + virtual void start(); + + virtual void getClawInfo(HotSpotID &outSpotID, HotSpotID &prepSpotID, + HotSpotID &clawControlSpotID, HotSpotID &pinchClawSpotID, + HotSpotID &moveClawDownSpotID, HotSpotID &moveClawRightSpotID, + HotSpotID &moveClawLeftSpotID,HotSpotID &moveClawUpSpotID, + HotSpotID &clawCCWSpotID, HotSpotID &clawCWSpotID, uint32 &, const uint32 *&) = 0; + void checkAirMask(); + + virtual uint16 getDateResID() const; + + virtual GameInteraction *makeInteraction(const InteractionID); + + Common::String getBriefingMovie(); + + void pickedUpItem(Item *); + + virtual void playClawMonitorIntro() {} + + void doneWithPressureDoor(); + +protected: + CanOpenDoorReason canOpenDoor(DoorTable::Entry &); + void cantOpenDoor(CanOpenDoorReason); + int16 getStaticCompassAngle(const RoomID, const DirectionConstant); + virtual void startExitMovie(const ExitTable::Entry &); + void startZoomMovie(const ZoomTable::Entry &); + virtual void upButton(const Input &); + virtual void activateHotspots(); + + virtual void arriveAt(const RoomID, const DirectionConstant); + virtual void arriveAtNoradElevator(); + virtual void arriveAtUpperPressureDoorRoom(); + virtual void arriveAtLowerPressureDoorRoom(); + virtual void arriveAtSubPlatformRoom(); + virtual void arriveAtSubControlRoom(); + void setUpAirMask(); + virtual void receiveNotification(Notification *, const NotificationFlags); + virtual bool playingAgainstRobot() { return false; } + + Notification _noradNotification; + bool _doneWithPressureDoor; + + RoomID _elevatorUpRoomID; + RoomID _elevatorDownRoomID; + HotSpotID _elevatorUpSpotID; + HotSpotID _elevatorDownSpotID; + + TimeBase _airMaskTimer; + NotificationCallBack _airMaskCallBack; + + RoomID _subRoomEntryRoom1; + DirectionConstant _subRoomEntryDir1; + RoomID _subRoomEntryRoom2; + DirectionConstant _subRoomEntryDir2; + RoomID _upperPressureDoorRoom; + RoomID _lowerPressureDoorRoom; + + HotSpotID _upperPressureDoorUpSpotID; + HotSpotID _upperPressureDoorDownSpotID; + HotSpotID _upperPressureDoorAbortSpotID; + + HotSpotID _lowerPressureDoorUpSpotID; + HotSpotID _lowerPressureDoorDownSpotID; + HotSpotID _lowerPressureDoorAbortSpotID; + + TimeValue _pressureSoundIn; + TimeValue _pressureSoundOut; + TimeValue _equalizeSoundIn; + TimeValue _equalizeSoundOut; + TimeValue _accessDeniedIn; + TimeValue _accessDeniedOut; + + RoomID _platformRoom; + RoomID _subControlRoom; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/norad/noradelevator.cpp b/engines/pegasus/neighborhood/norad/noradelevator.cpp new file mode 100644 index 0000000000..1751f4dcb6 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/noradelevator.cpp @@ -0,0 +1,130 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/neighborhood/norad/constants.h" +#include "pegasus/neighborhood/norad/norad.h" +#include "pegasus/neighborhood/norad/noradelevator.h" + +namespace Pegasus { + +// Norad elevator PICTs: +static const ResIDType kElevatorLabelID = 200; +static const ResIDType kElevatorButtonsID = 201; +static const ResIDType kElevatorDownOnID = 202; +static const ResIDType kElevatorUpOnID = 203; + +NoradElevator::NoradElevator(Neighborhood *handler, const RoomID upRoom, const RoomID downRoom, + const HotSpotID upHotspot, const HotSpotID downHotspot) : GameInteraction(kNoradElevatorInteractionID, handler), + _elevatorControls(kNoradElevatorControlsID), _elevatorNotification(kNoradElevatorNotificationID, ((PegasusEngine *)g_engine)) { + _timerExpired = false; + _upRoom = upRoom; + _downRoom = downRoom; + _upHotspot = upHotspot; + _downHotspot = downHotspot; +} + +void NoradElevator::openInteraction() { + SpriteFrame *frame = new SpriteFrame(); + frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kElevatorLabelID, true); + _elevatorControls.addFrame(frame, 0, 0); + + frame = new SpriteFrame(); + frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kElevatorButtonsID, true); + _elevatorControls.addFrame(frame, 0, 0); + + frame = new SpriteFrame(); + frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kElevatorDownOnID, true); + _elevatorControls.addFrame(frame, 0, 0); + + frame = new SpriteFrame(); + frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kElevatorUpOnID, true); + _elevatorControls.addFrame(frame, 0, 0); + + _elevatorControls.setCurrentFrameIndex(0); + _elevatorControls.setDisplayOrder(kElevatorControlsOrder); + + Common::Rect r; + frame->getSurfaceBounds(r); + r.moveTo(kNoradAlphaElevatorControlsLeft, kNoradAlphaElevatorControlsTop); + + _elevatorControls.setBounds(r); + _elevatorControls.startDisplaying(); + _elevatorControls.show(); +} + +void NoradElevator::initInteraction() { + _elevatorTimer.setScale(2); + _elevatorTimer.setSegment(0, 1); + _elevatorCallBack.initCallBack(&_elevatorTimer, kCallBackAtExtremes); + _elevatorCallBack.setCallBackFlag(1); + _elevatorCallBack.setNotification(&_elevatorNotification); + _elevatorNotification.notifyMe(this, 1, 1); + _elevatorCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + _elevatorTimer.start(); +} + +void NoradElevator::closeInteraction() { + _elevatorControls.stopDisplaying(); + _elevatorControls.discardFrames(); + _elevatorCallBack.releaseCallBack(); +} + +void NoradElevator::resetInteraction() { + _elevatorControls.setCurrentFrameIndex(1); +} + +void NoradElevator::activateHotspots() { + GameInteraction::activateHotspots(); + + if (_timerExpired) { + if (GameState.getCurrentRoom() == _upRoom) + g_allHotspots.activateOneHotspot(_downHotspot); + else if (GameState.getCurrentRoom() == _downRoom) + g_allHotspots.activateOneHotspot(_upHotspot); + } +} + +void NoradElevator::clickInHotspot(const Input &input, const Hotspot *spot) { + HotSpotID id = spot->getObjectID(); + + if (id == _upHotspot || id == _downHotspot) { + g_neighborhood->moveForward(); + if (id == _downHotspot) + _elevatorControls.setCurrentFrameIndex(2); + else + _elevatorControls.setCurrentFrameIndex(3); + } else { + GameInteraction::clickInHotspot(input, spot); + } +} + +void NoradElevator::receiveNotification(Notification *, const NotificationFlags) { + _elevatorControls.setCurrentFrameIndex(1); + _timerExpired = true; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/norad/noradelevator.h b/engines/pegasus/neighborhood/norad/noradelevator.h new file mode 100644 index 0000000000..24aa488534 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/noradelevator.h @@ -0,0 +1,67 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_NEIGHBORHOOD_NORAD_NORADELEVATOR_H +#define PEGASUS_NEIGHBORHOOD_NORAD_NORADELEVATOR_H + +#include "pegasus/interaction.h" +#include "pegasus/notification.h" +#include "pegasus/surface.h" +#include "pegasus/timers.h" + +namespace Pegasus { + +class Neighborhood; + +class NoradElevator : public GameInteraction, private NotificationReceiver { +public: + NoradElevator(Neighborhood *, const RoomID, const RoomID, const HotSpotID, const HotSpotID); + virtual ~NoradElevator() {} + +protected: + virtual void openInteraction(); + virtual void initInteraction(); + virtual void closeInteraction(); + virtual void resetInteraction(); + + virtual void activateHotspots(); + virtual void clickInHotspot(const Input &, const Hotspot *); + + virtual void receiveNotification(Notification *, const NotificationFlags); + + RoomID _upRoom; + RoomID _downRoom; + HotSpotID _upHotspot; + HotSpotID _downHotspot; + Sprite _elevatorControls; + TimeBase _elevatorTimer; + NotificationCallBack _elevatorCallBack; + Notification _elevatorNotification; + bool _timerExpired; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/norad/pressuredoor.cpp b/engines/pegasus/neighborhood/norad/pressuredoor.cpp new file mode 100644 index 0000000000..a12e971d10 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/pressuredoor.cpp @@ -0,0 +1,555 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/neighborhood/norad/constants.h" +#include "pegasus/neighborhood/norad/norad.h" +#include "pegasus/neighborhood/norad/pressuredoor.h" +#include "pegasus/neighborhood/norad/delta/noraddelta.h" + +namespace Pegasus { + +static const TimeValue kLevelsSplashStart = 0; +static const TimeValue kLevelsSplashStop = 1; +static const TimeValue kPressureBase = 1; + +static const TimeValue kDoorSealedTime = 0; +static const TimeValue kEqualizeTime = 1; +static const TimeValue kMaxPressureLoopStart = 2; +static const TimeValue kMaxPressureLoopStop = 3; +static const TimeValue kOpeningDoorLoopStart = 3; +static const TimeValue kOpeningDoorLoopStop = 4; +static const TimeValue kIncreasingPressureTime = 4; +static const TimeValue kDecreasingPressureTime = 5; +static const TimeValue kCautionLoopStart = 6; +static const TimeValue kCautionLoopStop = 7; + +static const NotificationFlags kSplashFinished = 1; +static const NotificationFlags kPressureDroppingFlag = kSplashFinished << 1; + +static const NotificationFlags kPressureNotificationFlags = kSplashFinished | + kPressureDroppingFlag; + +static const NotificationFlags kDoorJumpsUpFlag = 1; +static const NotificationFlags kDoorJumpsBackFlag = kDoorJumpsUpFlag << 1; +static const NotificationFlags kDoorCrushedFlag = kDoorJumpsBackFlag << 1; + +static const NotificationFlags kUtilityNotificationFlags = kDoorJumpsUpFlag | + kDoorJumpsBackFlag | + kDoorCrushedFlag; + +enum { + kPlayingRobotApproaching, + kRobotPunching, + kRobotComingThrough, + kRobotDying, + kRobotDead +}; + +const short kMaxPunches = 5; + +enum { + kPlayingSplash, + kPlayingPressureMessage, + kPlayingEqualizeMessage, + kWaitingForPlayer, + kPlayingDoneMessage, + kGameOver +}; + +// Pressure values range from 0 to 11. +static const short kMinPressure = 0; +static const short kMaxPressure = 11; + +static const TimeScale kNavTimeScale = 600; +static const TimeValue kNavFrameRate = 15; +static const TimeValue kNavTimePerFrame = kNavTimeScale / kNavFrameRate; + +static const TimeValue kApproachPunchInTime = 122 * kNavTimePerFrame; +static const TimeValue kLoopPunchInTime = 38 * kNavTimePerFrame; +static const TimeValue kPunchThroughTime = 38 * kNavTimePerFrame; + +// Pressure door PICTs: +static const ResIDType kUpperPressureUpOffPICTID = 400; +static const ResIDType kUpperPressureUpOnPICTID = 401; +static const ResIDType kUpperPressureDownOffPICTID = 402; +static const ResIDType kUpperPressureDownOnPICTID = 403; + +static const ResIDType kLowerPressureUpOffPICTID = 404; +static const ResIDType kLowerPressureUpOnPICTID = 405; +static const ResIDType kLowerPressureDownOffPICTID = 406; +static const ResIDType kLowerPressureDownOnPICTID = 407; + +PressureDoor::PressureDoor(Neighborhood *handler, bool isUpperDoor, const HotSpotID upSpotID, const HotSpotID downSpotID, + const HotSpotID outSpotID, TimeValue pressureSoundIn, TimeValue pressureSoundOut, TimeValue equalizeSoundIn, + TimeValue equalizeSoundOut) : GameInteraction(kNoradPressureDoorInteractionID, handler), + _levelsMovie(kPressureDoorLevelsID), _typeMovie(kPressureDoorTypeID), _upButton(kPressureDoorUpButtonID), + _downButton(kPressureDoorDownButtonID), _pressureNotification(kNoradPressureNotificationID, ((PegasusEngine *)g_engine)), + _doorTracker(this), _utilityNotification(kNoradUtilityNotificationID, ((PegasusEngine *)g_engine)) { + _neighborhoodNotification = handler->getNeighborhoodNotification(); + _upHotspotID = upSpotID; + _downHotspotID = downSpotID; + _outHotspotID = outSpotID; + _pressureSoundIn = pressureSoundIn; + _pressureSoundOut = pressureSoundOut; + _equalizeSoundIn = equalizeSoundIn; + _equalizeSoundOut = equalizeSoundOut; + _playingAgainstRobot = false; + _isUpperDoor = isUpperDoor; +} + +void PressureDoor::openInteraction() { + if (_isUpperDoor) { + _levelsMovie.initFromMovieFile("Images/Norad Alpha/Upper Levels Movie"); + _levelsMovie.moveElementTo(kNoradUpperLevelsLeft, kNoradUpperLevelsTop); + } else { + _levelsMovie.initFromMovieFile("Images/Norad Alpha/Lower Levels Movie"); + _levelsMovie.moveElementTo(kNoradLowerLevelsLeft, kNoradLowerLevelsTop); + } + + _levelsScale = _levelsMovie.getScale(); + _levelsMovie.setDisplayOrder(kPressureLevelsOrder); + _levelsMovie.startDisplaying(); + _levelsMovie.setSegment(kLevelsSplashStart * _levelsScale, kLevelsSplashStop * _levelsScale); + _levelsMovie.setTime(kLevelsSplashStart * _levelsScale); + _levelsMovie.redrawMovieWorld(); + _levelsMovie.show(); + + _pressureCallBack.setNotification(&_pressureNotification); + _pressureCallBack.initCallBack(&_levelsMovie, kCallBackAtExtremes); + _pressureCallBack.setCallBackFlag(kSplashFinished); + _pressureCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + + _pressureNotification.notifyMe(this, kPressureNotificationFlags, kPressureNotificationFlags); + + if (_isUpperDoor) { + _typeMovie.initFromMovieFile("Images/Norad Alpha/Upper Type Movie"); + _typeMovie.moveElementTo(kNoradUpperTypeLeft, kNoradUpperTypeTop); + } else { + _typeMovie.initFromMovieFile("Images/Norad Alpha/Lower Type Movie"); + _typeMovie.moveElementTo(kNoradLowerTypeLeft, kNoradLowerTypeTop); + } + + _typeScale = _typeMovie.getScale(); + _typeMovie.setDisplayOrder(kPressureTypeOrder); + _typeMovie.startDisplaying(); + _typeMovie.setTime(kDoorSealedTime * _typeScale); + _typeMovie.redrawMovieWorld(); + + SpriteFrame *frame = new SpriteFrame(); + if (_isUpperDoor) + frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kLowerPressureUpOffPICTID); + else + frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kUpperPressureUpOffPICTID); + _upButton.addFrame(frame, 0, 0); + + frame = new SpriteFrame(); + if (_isUpperDoor) + frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kLowerPressureUpOnPICTID); + else + frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kUpperPressureUpOnPICTID); + _upButton.addFrame(frame, 0, 0); + + _upButton.setCurrentFrameIndex(0); + _upButton.setDisplayOrder(kPressureUpOrder); + + Common::Rect r; + frame->getSurfaceBounds(r); + if (_isUpperDoor) + r.moveTo(kNoradUpperUpLeft, kNoradUpperUpTop); + else + r.moveTo(kNoradLowerUpLeft, kNoradLowerUpTop); + + _upButton.setBounds(r); + _upButton.startDisplaying(); + _upButton.show(); + + frame = new SpriteFrame(); + if (_isUpperDoor) + frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kLowerPressureDownOffPICTID); + else + frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kUpperPressureDownOffPICTID); + _downButton.addFrame(frame, 0, 0); + + frame = new SpriteFrame(); + if (_isUpperDoor) + frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kLowerPressureDownOnPICTID); + else + frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kUpperPressureDownOnPICTID); + _downButton.addFrame(frame, 0, 0); + + _downButton.setCurrentFrameIndex(0); + _downButton.setDisplayOrder(kPressureDownOrder); + + frame->getSurfaceBounds(r); + if (_isUpperDoor) + r.moveTo(kNoradUpperDownLeft, kNoradUpperDownTop); + else + r.moveTo(kNoradLowerDownLeft, kNoradLowerDownTop); + + _downButton.setBounds(r); + _downButton.startDisplaying(); + _downButton.show(); + + _utilityCallBack.setNotification(&_utilityNotification); + _utilityCallBack.initCallBack(&_utilityTimer, kCallBackAtTime); + _utilityNotification.notifyMe(this, kUtilityNotificationFlags, kUtilityNotificationFlags); + _utilityTimer.setMasterTimeBase(getOwner()->getNavMovie()); + + if (_playingAgainstRobot) + _neighborhoodNotification->notifyMe(this, kExtraCompletedFlag | kDelayCompletedFlag | + kSpotSoundCompletedFlag, kExtraCompletedFlag | kDelayCompletedFlag | kSpotSoundCompletedFlag); + else + _neighborhoodNotification->notifyMe(this, kDelayCompletedFlag | kSpotSoundCompletedFlag, + kDelayCompletedFlag | kSpotSoundCompletedFlag); + + _gameState = kPlayingSplash; +} + +void PressureDoor::initInteraction() { + _levelsMovie.start(); + + if (_playingAgainstRobot) { + ExtraTable::Entry entry; + _owner->getExtraEntry(kN59RobotApproaches, entry); + _utilityTimer.setSegment(entry.movieStart, entry.movieEnd); + _utilityCallBack.setCallBackFlag(kDoorJumpsUpFlag); + _punchInTime = kApproachPunchInTime + entry.movieStart; + _utilityCallBack.scheduleCallBack(kTriggerTimeFwd, _punchInTime, kNavTimeScale); + _utilityTimer.setTime(entry.movieStart); + _owner->startExtraSequence(kN59RobotApproaches, kExtraCompletedFlag, kFilterAllInput); + _utilityTimer.start(); + _robotState = kPlayingRobotApproaching; + } + + _levelsMovie.redrawMovieWorld(); +} + +void PressureDoor::closeInteraction() { + _pressureNotification.cancelNotification(this); + _pressureCallBack.releaseCallBack(); + _utilityNotification.cancelNotification(this); + _utilityCallBack.releaseCallBack(); + _neighborhoodNotification->cancelNotification(this); +} + +void PressureDoor::playAgainstRobot() { + _playingAgainstRobot = true; +} + +void PressureDoor::receiveNotification(Notification *notification, const NotificationFlags flags) { + Neighborhood *owner = getOwner(); + + if (notification == _neighborhoodNotification) { + if (_playingAgainstRobot && (flags & kExtraCompletedFlag) != 0) { + ExtraTable::Entry entry; + + switch (_robotState) { + case kPlayingRobotApproaching: + _utilityTimer.stop(); + if (GameState.getNoradSubRoomPressure() == kMaxPressure) { + owner->getExtraEntry(kN59PlayerWins1, entry); + _utilityTimer.setSegment(entry.movieStart, entry.movieEnd); + _utilityTimer.setTime(entry.movieStart); + _utilityCallBack.setCallBackFlag(kDoorJumpsUpFlag); + _punchInTime = kLoopPunchInTime + entry.movieStart; + _utilityCallBack.scheduleCallBack(kTriggerTimeFwd, _punchInTime, kNavTimeScale); + owner->startExtraSequence(kN59PlayerWins1, kExtraCompletedFlag, kFilterNoInput); + _utilityTimer.start(); + _robotState = kRobotDying; + } else { + owner->getExtraEntry(kN59RobotPunchLoop, entry); + _utilityTimer.setSegment(entry.movieStart, entry.movieEnd); + _utilityTimer.setTime(entry.movieStart); + _utilityCallBack.setCallBackFlag(kDoorJumpsUpFlag); + _punchInTime = kLoopPunchInTime + entry.movieStart; + _utilityCallBack.scheduleCallBack(kTriggerTimeFwd, _punchInTime, kNavTimeScale); + owner->startSpotLoop(entry.movieStart, entry.movieEnd, kExtraCompletedFlag); + _utilityTimer.start(); + _robotState = kRobotPunching; + _punchCount = 1; + } + break; + case kRobotPunching: + if (GameState.getNoradSubRoomPressure() == kMaxPressure) { + owner->startExtraSequence(kN59PlayerWins1, kExtraCompletedFlag, kFilterNoInput); + _robotState = kRobotDying; + } else if (++_punchCount >= kMaxPunches) { + _robotState = kRobotComingThrough; + owner->getExtraEntry(kN59RobotWins, entry); + _utilityTimer.stop(); + _utilityTimer.setSegment(entry.movieStart, entry.movieEnd); + _utilityTimer.setTime(entry.movieStart); + _utilityCallBack.cancelCallBack(); + _utilityCallBack.setCallBackFlag(kDoorCrushedFlag); + _utilityCallBack.scheduleCallBack(kTriggerTimeFwd, kPunchThroughTime + entry.movieStart, kNavTimeScale); + owner->startExtraSequence(kN59RobotWins, kExtraCompletedFlag, kFilterNoInput); + _utilityTimer.start(); + } else { + _utilityCallBack.setCallBackFlag(kDoorJumpsUpFlag); + _utilityCallBack.scheduleCallBack(kTriggerTimeFwd, _punchInTime, kNavTimeScale); + owner->scheduleNavCallBack(kExtraCompletedFlag); + } + break; + case kRobotComingThrough: + g_system->delayMillis(2 * 1000); + ((PegasusEngine *)g_engine)->die(kDeathRobotThroughNoradDoor); + break; + case kRobotDying: + _robotState = kRobotDead; + _levelsMovie.stop(); + _levelsMovie.setSegment((kNormalSubRoomPressure + kPressureBase) * _levelsScale, + (GameState.getNoradSubRoomPressure() + kPressureBase) * _levelsScale + 1); + _levelsMovie.setTime((GameState.getNoradSubRoomPressure() + kPressureBase) * _levelsScale); + _pressureCallBack.setCallBackFlag(kPressureDroppingFlag); + _pressureCallBack.scheduleCallBack(kTriggerAtStart, 0, 0); + _typeMovie.stop(); + _typeMovie.setSegment(0, _typeMovie.getDuration()); + _typeMovie.setTime(kDecreasingPressureTime * _typeScale); + _typeMovie.redrawMovieWorld(); + _typeMovie.show(); + _downButton.show(); + _downButton.setCurrentFrameIndex(1); + _gameState = kGameOver; + allowInput(false); + _levelsMovie.setRate(Common::Rational(-4, 3)); // Should match door tracker. + break; + case kRobotDead: + allowInput(true); + ((NoradDelta *)owner)->playerBeatRobotWithDoor(); + owner->requestDeleteCurrentInteraction(); + break; + } + } + + if ((flags & (kDelayCompletedFlag | kSpotSoundCompletedFlag)) != 0) { + switch (_gameState) { + case kPlayingPressureMessage: + _typeMovie.setTime(kEqualizeTime * _typeScale); + _typeMovie.redrawMovieWorld(); + owner->requestDelay(1, 5, kFilterNoInput, 0); + owner->requestSpotSound(_equalizeSoundIn, _equalizeSoundOut, kFilterNoInput, 0); + owner->requestDelay(1, 5, kFilterNoInput, kDelayCompletedFlag); + _gameState = kPlayingEqualizeMessage; + break; + case kPlayingEqualizeMessage: + _gameState = kWaitingForPlayer; + stopChangingPressure(); + break; + case kPlayingDoneMessage: + _gameState = kWaitingForPlayer; + _typeMovie.stop(); + _typeMovie.setFlags(0); + _typeMovie.hide(); + if (!_playingAgainstRobot) + ((Norad *)_owner)->doneWithPressureDoor(); + break; + } + } + } else if (notification == &_pressureNotification) { + switch (flags) { + case kSplashFinished: + _levelsMovie.stop(); + _levelsMovie.setSegment(0, _levelsMovie.getDuration()); + _levelsMovie.setTime((GameState.getNoradSubRoomPressure() + kPressureBase) * _levelsScale); + _levelsMovie.redrawMovieWorld(); + + if (GameState.getNoradSubRoomPressure() != kNormalSubRoomPressure) { + _typeMovie.show(); + owner->requestDelay(1, 5, kFilterNoInput, 0); + owner->requestSpotSound(_pressureSoundIn, _pressureSoundOut, kFilterNoInput, 0); + owner->requestDelay(1, 5, kFilterNoInput, kDelayCompletedFlag); + _gameState = kPlayingPressureMessage; + } else { + _gameState = kWaitingForPlayer; + } + break; + case kPressureDroppingFlag: + _levelsMovie.stop(); + _levelsMovie.hide(); + _typeMovie.stop(); + _typeMovie.hide(); + _upButton.hide(); + _downButton.hide(); + owner->startExtraSequence(kN59PlayerWins2, kExtraCompletedFlag, kFilterNoInput); + break; + } + } else if (notification == &_utilityNotification) { + switch (flags) { + case kDoorJumpsUpFlag: + _utilityCallBack.setCallBackFlag(kDoorJumpsBackFlag); + _utilityCallBack.scheduleCallBack(kTriggerTimeFwd, _punchInTime + kNavTimePerFrame, kNavTimeScale); + _levelsMovie.hide(); + _typePunched = _typeMovie.isVisible(); + if (_typePunched == true) + _typeMovie.hide(); + _upButton.hide(); + _downButton.hide(); + break; + case kDoorJumpsBackFlag: + _levelsMovie.show(); + _upButton.show(); + _downButton.show(); + if (_typePunched) + _typeMovie.show(); + break; + case kDoorCrushedFlag: + _levelsMovie.hide(); + _typeMovie.hide(); + _upButton.hide(); + _downButton.hide(); + break; + } + } +} + +void PressureDoor::activateHotspots() { + GameInteraction::activateHotspots(); + + switch (_gameState) { + case kWaitingForPlayer: + g_allHotspots.activateOneHotspot(_upHotspotID); + g_allHotspots.activateOneHotspot(_downHotspotID); + if (!_playingAgainstRobot) + g_allHotspots.activateOneHotspot(_outHotspotID); + break; + default: + break; + } +} + +void PressureDoor::clickInHotspot(const Input &input, const Hotspot *spot) { + HotSpotID id = spot->getObjectID(); + + if (id == _upHotspotID || id == _downHotspotID) { + if (id == _upHotspotID) + _doorTracker.setTrackParameters(spot, &_upButton); + else + _doorTracker.setTrackParameters(spot, &_downButton); + + _doorTracker.startTracking(input); + } else { + GameInteraction::clickInHotspot(input, spot); + } +} + +void PressureDoor::incrementPressure(const HotSpotID id) { + _typeMovie.stop(); + _typeMovie.setSegment(0, _typeMovie.getDuration()); + _typeMovie.setFlags(0); + + if (id == _upHotspotID) { + if (GameState.getNoradSubRoomPressure() < kMaxPressure) { + GameState.setNoradSubRoomPressure(GameState.getNoradSubRoomPressure() + 1); + _levelsMovie.setTime((GameState.getNoradSubRoomPressure() + kPressureBase) * _levelsScale); + _levelsMovie.redrawMovieWorld(); + _typeMovie.setTime(kIncreasingPressureTime * _typeScale); + _typeMovie.redrawMovieWorld(); + _typeMovie.show(); + g_AIArea->checkMiddleArea(); + } else { + _typeMovie.hide(); + } + } else if (id == _downHotspotID) { + if (GameState.getNoradSubRoomPressure() > kMinPressure) { + GameState.setNoradSubRoomPressure(GameState.getNoradSubRoomPressure() - 1); + _levelsMovie.setTime((GameState.getNoradSubRoomPressure() + kPressureBase) * _levelsScale); + _levelsMovie.redrawMovieWorld(); + _typeMovie.setTime(kDecreasingPressureTime * _typeScale); + _typeMovie.redrawMovieWorld(); + _typeMovie.show(); + g_AIArea->checkMiddleArea(); + } else { + _typeMovie.hide(); + } + } +} + +void PressureDoor::stopChangingPressure() { + Neighborhood *owner; + + switch (GameState.getNoradSubRoomPressure()) { + case 11: + _typeMovie.setSegment(kMaxPressureLoopStart * _typeScale, kMaxPressureLoopStop * _typeScale); + _typeMovie.setFlags(kLoopTimeBase); + _typeMovie.show(); + _typeMovie.start(); + break; + case 10: + _typeMovie.setSegment(kCautionLoopStart * _typeScale, kCautionLoopStop * _typeScale); + _typeMovie.setFlags(kLoopTimeBase); + _typeMovie.show(); + _typeMovie.start(); + break; + case kNormalSubRoomPressure: + owner = getOwner(); + _typeMovie.setSegment(kOpeningDoorLoopStart * _typeScale, kOpeningDoorLoopStop * _typeScale); + _typeMovie.setFlags(kLoopTimeBase); + _typeMovie.show(); + _gameState = kPlayingDoneMessage; + owner->requestDelay(2, 1, kFilterNoInput, kDelayCompletedFlag); + _typeMovie.start(); + break; + default: + _typeMovie.hide(); + break; + } +} + +bool PressureDoor::canSolve() { + if (_playingAgainstRobot) + return GameState.getNoradSubRoomPressure() < 11; + + return GameState.getNoradSubRoomPressure() != kNormalSubRoomPressure; +} + +void PressureDoor::doSolve() { + if (_playingAgainstRobot) { + GameState.setNoradSubRoomPressure(11); + _levelsMovie.setTime((11 + kPressureBase) * _levelsScale); + _levelsMovie.redrawMovieWorld(); + _typeMovie.setSegment(kMaxPressureLoopStart * _typeScale, kMaxPressureLoopStop * _typeScale); + _typeMovie.setFlags(kLoopTimeBase); + _typeMovie.show(); + _typeMovie.start(); + g_AIArea->checkMiddleArea(); + } else { + GameState.setNoradSubRoomPressure(kNormalSubRoomPressure); + _levelsMovie.setTime((kNormalSubRoomPressure + kPressureBase) * _levelsScale); + _levelsMovie.redrawMovieWorld(); + _typeMovie.setSegment(kOpeningDoorLoopStart * _typeScale, kOpeningDoorLoopStop * _typeScale); + _typeMovie.setFlags(kLoopTimeBase); + _typeMovie.show(); + Neighborhood *owner = getOwner(); + owner->requestDelay(2, 1, kFilterNoInput, kDelayCompletedFlag); + _gameState = kPlayingDoneMessage; + _typeMovie.start(); + g_AIArea->checkMiddleArea(); + } +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/norad/pressuredoor.h b/engines/pegasus/neighborhood/norad/pressuredoor.h new file mode 100644 index 0000000000..b2018bfcf7 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/pressuredoor.h @@ -0,0 +1,93 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_NEIGHBORHOOD_NORAD_PRESSUREDOOR_H +#define PEGASUS_NEIGHBORHOOD_NORAD_PRESSUREDOOR_H + +#include "pegasus/interaction.h" +#include "pegasus/movie.h" +#include "pegasus/notification.h" +#include "pegasus/neighborhood/norad/pressuretracker.h" + +namespace Pegasus { + +static const short kNormalSubRoomPressure = 2; + +class PressureDoor : public GameInteraction, public NotificationReceiver { +public: + PressureDoor(Neighborhood *, bool isUpperDoor, const HotSpotID, const HotSpotID, + const HotSpotID, TimeValue pressureSoundIn, TimeValue pressureSoundOut, + TimeValue equalizeSoundIn, TimeValue equalizeSoundOut); + virtual ~PressureDoor() {} + + void incrementPressure(const HotSpotID); + void stopChangingPressure(); + + void playAgainstRobot(); + + bool canSolve(); + void doSolve(); + +protected: + virtual void openInteraction(); + virtual void initInteraction(); + virtual void closeInteraction(); + + virtual void activateHotspots(); + virtual void clickInHotspot(const Input &, const Hotspot *); + + virtual void receiveNotification(Notification *, const NotificationFlags); + + Movie _levelsMovie; + TimeScale _levelsScale; + Movie _typeMovie; + TimeScale _typeScale; + Sprite _upButton; + Sprite _downButton; + Notification _pressureNotification; + NotificationCallBack _pressureCallBack; + Notification *_neighborhoodNotification; + int _gameState; + HotSpotID _upHotspotID; + HotSpotID _downHotspotID; + HotSpotID _outHotspotID; + PressureTracker _doorTracker; + TimeValue _pressureSoundIn; + TimeValue _pressureSoundOut; + TimeValue _equalizeSoundIn; + TimeValue _equalizeSoundOut; + bool _isUpperDoor; + + bool _playingAgainstRobot, _typePunched; + int _robotState, _punchCount; + TimeBase _utilityTimer; + Notification _utilityNotification; + NotificationCallBack _utilityCallBack; + TimeValue _punchInTime; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/norad/pressuretracker.cpp b/engines/pegasus/neighborhood/norad/pressuretracker.cpp new file mode 100644 index 0000000000..5aac19dcbe --- /dev/null +++ b/engines/pegasus/neighborhood/norad/pressuretracker.cpp @@ -0,0 +1,87 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/hotspot.h" +#include "pegasus/pegasus.h" +#include "pegasus/neighborhood/norad/pressuredoor.h" +#include "pegasus/neighborhood/norad/pressuretracker.h" + +namespace Pegasus { + +PressureTracker::PressureTracker(PressureDoor *pressureDoor) { + _pressureDoor = pressureDoor; + _trackSpot = 0; + _trackTime = 0; +} + +void PressureTracker::setTrackParameters(const Hotspot *trackSpot, Sprite *trackButton) { + _trackSpot = trackSpot; + _trackButton = trackButton; + _trackTime = 0; +} + +void PressureTracker::activateHotspots() { + Tracker::activateHotspots(); + + if (_trackSpot) + g_allHotspots.activateOneHotspot(_trackSpot->getObjectID()); +} + +// For click-hold dragging. +bool PressureTracker::stopTrackingInput(const Input &input) { + return !JMPPPInput::isPressingInput(input); +} + +void PressureTracker::continueTracking(const Input &input) { + Common::Point where; + input.getInputLocation(where); + + if (g_allHotspots.findHotspot(where) == _trackSpot) { + trackPressure(); + _trackButton->setCurrentFrameIndex(1); + } else { + _trackButton->setCurrentFrameIndex(0); + } +} + +void PressureTracker::startTracking(const Input &input) { + Tracker::startTracking(input); + trackPressure(); +} + +void PressureTracker::stopTracking(const Input &input) { + _trackButton->setCurrentFrameIndex(0); + _pressureDoor->stopChangingPressure(); + Tracker::stopTracking(input); +} + +void PressureTracker::trackPressure() { + if (g_system->getMillis() - _trackTime > kPressureDoorTrackInterval * 1000 / 60) { + _pressureDoor->incrementPressure(_trackSpot->getObjectID()); + _trackTime = g_system->getMillis(); + } +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/norad/pressuretracker.h b/engines/pegasus/neighborhood/norad/pressuretracker.h new file mode 100644 index 0000000000..6ca7252e22 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/pressuretracker.h @@ -0,0 +1,69 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_NEIGHBORHOOD_NORAD_PRESSURETRACKER_H +#define PEGASUS_NEIGHBORHOOD_NORAD_PRESSURETRACKER_H + +#include "pegasus/input.h" + +namespace Pegasus { + +// This class assumes that the globe movie is built at 15 frames per second with a +// time scale of 600, yielding 40 time unit per frame. + +enum PressureTrackDirection { + kTrackPressureUp, + kTrackPressureDown +}; + +static const int kPressureDoorTrackInterval = 45; + +class PressureDoor; +class Sprite; + +class PressureTracker : public Tracker { +public: + PressureTracker(PressureDoor *); + virtual ~PressureTracker() {} + + void setTrackParameters(const Hotspot *, Sprite *); + void continueTracking(const Input &); + void startTracking(const Input &); + void stopTracking(const Input &); + void activateHotspots(); + bool stopTrackingInput(const Input &); + +protected: + void trackPressure(); + + PressureDoor *_pressureDoor; + const Hotspot *_trackSpot; + Sprite *_trackButton; + long _trackTime; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/norad/subcontrolroom.cpp b/engines/pegasus/neighborhood/norad/subcontrolroom.cpp new file mode 100644 index 0000000000..d48481e925 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/subcontrolroom.cpp @@ -0,0 +1,1178 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/neighborhood/norad/constants.h" +#include "pegasus/neighborhood/norad/norad.h" +#include "pegasus/neighborhood/norad/subcontrolroom.h" +#include "pegasus/neighborhood/norad/delta/noraddelta.h" + +namespace Pegasus { + +// Right Monitor times + +static const TimeValue kAlphaClawSplashStart = 0; +static const TimeValue kAlphaClawSplashStop = 4000; + +static const TimeValue kDeltaClawSplashStart = 4000; +static const TimeValue kDeltaClawSplashStop = 8000; + +static const TimeValue kClawAtATime = 8000; +static const TimeValue kClawAtAPinchedTime = 8600; +static const TimeValue kClawAtATurnedTime = 9200; +static const TimeValue kClawAtAWithRobotPinchedTime = 9800; + +static const TimeValue kClawAtBTime = 10400; +static const TimeValue kClawAtBPinchedTime = 11000; +static const TimeValue kClawAtBTurnedTime = 11600; +static const TimeValue kClawAtBWithRobotTime = 12200; +static const TimeValue kClawAtBWithRobotPinchedTime = 12800; + +static const TimeValue kClawAtCTime = 13400; +static const TimeValue kClawAtCPinchedTime = 14000; +static const TimeValue kClawAtCTurnedTime = 14600; + +static const TimeValue kClawAtDTime = 15200; +static const TimeValue kClawAtDPinchedTime = 15800; +static const TimeValue kClawAtDTurnedTime = 16400; + +static const TimeValue kAToBStart = 17000; +static const TimeValue kAToBStop = 18680; +static const TimeValue kAPinchStart = 18680; +static const TimeValue kAPinchStop = 20200; +static const TimeValue kACCWStart = 20200; +static const TimeValue kACCWStop = 21600; +static const TimeValue kACWStart = 21600; +static const TimeValue kACWStop = 23000; + +static const TimeValue kBToAStart = 23000; +static const TimeValue kBToAStop = 24680; +static const TimeValue kBToCStart = 24680; +static const TimeValue kBToCStop = 26520; +static const TimeValue kBToDStart = 26520; +static const TimeValue kBToDStop = 28320; +static const TimeValue kBPinchStart = 28320; +static const TimeValue kBPinchStop = 29680; +static const TimeValue kBCCWStart = 29680; +static const TimeValue kBCCWStop = 31200; +static const TimeValue kBCWStart = 31200; +static const TimeValue kBCWStop = 32720; + +static const TimeValue kCToBStart = 32720; +static const TimeValue kCToBStop = 34560; +static const TimeValue kCPinchStart = 34560; +static const TimeValue kCPinchStop = 36400; +static const TimeValue kCCCWStart = 36400; +static const TimeValue kCCCWStop = 37840; +static const TimeValue kCCWStart = 37840; +static const TimeValue kCCWStop = 39280; + +static const TimeValue kDToBStart = 39280; +static const TimeValue kDToBStop = 41080; +static const TimeValue kDPinchStart = 41080; +static const TimeValue kDPinchStop = 42600; +static const TimeValue kDCCWStart = 42600; +static const TimeValue kDCCWStop = 44000; +static const TimeValue kDCWStart = 44000; +static const TimeValue kDCWStop = 45400; + +static const TimeValue kRobotApproachStart = 45400; +static const TimeValue kRobotApproachStop = 56800; + +static const TimeValue kCToBWithRobotStart = 56800; +static const TimeValue kCToBWithRobotStop = 58600; + +static const TimeValue kBPinchWithRobotStart = 58600; +static const TimeValue kBPinchWithRobotStop = 60400; +static const TimeValue kBToAWithRobotStart = 60400; +static const TimeValue kBToAWithRobotStop = 62240; + +// As usual, times here are in seconds. + +// Left monitor times. + +static const TimeValue kAlphaSplashStart = 0; +static const TimeValue kAlphaSplashStop = 2; + +static const TimeValue kMainMenuTime = 2; +static const TimeValue kLaunchPrepRolloverTime = 3; +static const TimeValue kLaunchPrepHighlightStart = 4; +static const TimeValue kLaunchPrepHighlightStop = 5; +static const TimeValue kClawControlRolloverTime = 5; +static const TimeValue kClawControlHighlightStart = 6; +static const TimeValue kClawControlHighlightStop = 7; + +static const TimeValue kAlphaLaunchPrepStart = 7; +static const TimeValue kAlphaLaunchPrepStop = 17; + +static const TimeValue kClawMenuStart = 17; +static const TimeValue kClawMenuStop = 18; + +static const TimeValue kClawMenuTime = 18; + +static const TimeValue kDeltaSplashStart = 19; +static const TimeValue kDeltaSplashStop = 21; + +static const TimeValue kDeltaLaunchPrepStart = 21; +static const TimeValue kDeltaLaunchPrepStop = 30; + +// Right monitor times. + +static const NotificationFlags kAlphaSplashFinished = 1; +static const NotificationFlags kAlphaPrepFinished = kAlphaSplashFinished << 1; +static const NotificationFlags kPrepHighlightFinished = kAlphaPrepFinished << 1; +static const NotificationFlags kClawHighlightFinished = kPrepHighlightFinished << 1; +static const NotificationFlags kClawMenuFinished = kClawHighlightFinished << 1; +static const NotificationFlags kDeltaSplashFinished = kClawMenuFinished << 1; +static const NotificationFlags kDeltaPrepFinished = kDeltaSplashFinished << 1; + +static const NotificationFlags kSubControlNotificationFlags = kAlphaSplashFinished | + kAlphaPrepFinished | + kPrepHighlightFinished | + kClawHighlightFinished | + kClawMenuFinished | + kDeltaSplashFinished | + kDeltaPrepFinished; + +static const NotificationFlags kOneSecondOfMoveFinished = 1; + +static const NotificationFlags kGreenBallNotificationFlags = kOneSecondOfMoveFinished; + +enum { + kButtonDimFrame, + kButtonActiveFrame, + kButtonHighlightedFrame +}; + +enum { + kAlphaSplash, + kAlphaMainMenu, + kDeltaSplash, + kDeltaMainMenu, + kClawMenu, + kPlayingHighlight, + kPuttingClawAway +}; + +// The owning neighborhood must provide an array of longs which hold the various +// extra IDs for moving the claw around. In addition, the owner must tell the sub +// control room interaction what position the claw starts out in (which is also the +// position the claw must be in before leaving). + +// Standard array indices: +enum { + kClawFromAToBIndex, + kClawALoopIndex, + kClawAPinchIndex, + kClawACounterclockwiseIndex, + kClawAClockwiseIndex, + kClawFromBToAIndex, + kClawFromBToCIndex, + kClawFromBToDIndex, + kClawBLoopIndex, + kClawBPinchIndex, + kClawBCounterclockwiseIndex, + kClawBClockwiseIndex, + kClawFromCToBIndex, + kClawCLoopIndex, + kClawCPinchIndex, + kClawCCounterclockwiseIndex, + kClawCClockwiseIndex, + kClawFromDToBIndex, + kClawDLoopIndex, + kClawDPinchIndex, + kClawDCounterclockwiseIndex, + kClawDClockwiseIndex +}; + +// Action indices for s_clawStateTable: +// Can also be used as indices into _buttons (except for kNoActionIndex and kLoopActionIndex). +enum { + kNoActionIndex = -1, + kPinchActionIndex = 0, + kMoveDownActionIndex, + kMoveRightActionIndex, + kMoveLeftActionIndex, + kMoveUpActionIndex, + kCCWActionIndex, + kCWActionIndex, + kLoopActionIndex +}; + +/* + _currentAction and _nextAction: + + At any time, _currentAction contains an action index (defined above). The current + action index is what the claw is doing right now. If the player presses a button + before the current action completes, _nextAction saves the new action and input + is disabled. This has the effect of implementing a queue of commands for the claw + that can save at most one extra command. + + The general strategy for using _currentAction and _nextAction are: + -- If the player presses a claw button and _currentAction is kNoActionIndex, + do the command immediately and set _currentAction accordingly. + -- If the player presses a claw button and _currentAction is not kNoActionIndex, + save the appropriate action index in _nextAction. + -- When a command animation completes, set _nextAction to kNoActionIndex, then + check if _nextAction has a command waiting in it. If so, play the appriate + animation, copy _nextAction into _currentAction and set _nextAction to + kNoActionIndex. + -- If the player tries to get up, disable input (including all claw buttons) until + the player rises. Then, if the claw is in its original position, play the + animation of the player rising. + -- If the claw needs to be put back, play the first move required to put the + claw back by setting _currentAction and playing the appropriate animation. + Leave _nextAction alone. When the animation, completes, check to see if the + claw is in its original position or not. If so, complete the player rising + sequence by playing the rising animation. If not, repeat this whole step. + + Using this general strategy allows the use of a single function, + DispatchClawAction, which can both cause the claw to perform a command and saving + the next command in _nextAction. +*/ + +// Array indexed by [claw position] [action] +// array yields an index into the neighborhood's extra id table for claw animation or -1. +static const int s_clawStateTable[4][8] = { + { + kClawAPinchIndex, + kNoActionIndex, + kNoActionIndex, + kClawFromAToBIndex, + kNoActionIndex, + kClawACounterclockwiseIndex, + kClawAClockwiseIndex, + kClawALoopIndex + }, + { + kClawBPinchIndex, + kNoActionIndex, + kClawFromBToAIndex, + kClawFromBToDIndex, + kClawFromBToCIndex, + kClawBCounterclockwiseIndex, + kClawBClockwiseIndex, + kClawBLoopIndex + }, + { + kClawCPinchIndex, + kClawFromCToBIndex, + kNoActionIndex, + kNoActionIndex, + kNoActionIndex, + kClawCCounterclockwiseIndex, + kClawCClockwiseIndex, + kClawCLoopIndex + }, + { + kClawDPinchIndex, + kNoActionIndex, + kClawFromDToBIndex, + kNoActionIndex, + kNoActionIndex, + kClawDCounterclockwiseIndex, + kClawDClockwiseIndex, + kClawDLoopIndex + } +}; + +// Move directions for s_clawMovieTable: +enum { + kMoveClawDown, + kMoveClawRight, + kMoveClawLeft, + kMoveClawUp +}; + +static const int kClawNowhere = -1; + +// Array indexed by [claw position] [move direction] +// array yields new claw position or -1. +static const int s_clawMovieTable[4][4] = { + { + kClawNowhere, + kClawNowhere, + kClawAtB, + kClawNowhere + }, + { + kClawNowhere, + kClawAtA, + kClawAtD, + kClawAtC + }, + { + kClawAtB, + kClawNowhere, + kClawNowhere, + kClawNowhere + }, + { + kClawNowhere, + kClawAtB, + kClawNowhere, + kClawNowhere + } +}; + +// Indexed by claw action index, claw position, plus 0 for start, 1 for stop. +// (Never indexed with kLoopActionIndex.) +static const TimeValue s_clawMonitorTable[7][4][2] = { + { + { kAPinchStart, kAPinchStop }, + { kBPinchStart, kBPinchStop }, + { kCPinchStart, kCPinchStop }, + { kDPinchStart, kDPinchStop } + }, + { + { 0xffffffff, 0xffffffff }, + { 0xffffffff, 0xffffffff }, + { kCToBStart, kCToBStop }, + { 0xffffffff, 0xffffffff } + }, + { + { 0xffffffff, 0xffffffff }, + { kBToAStart, kBToAStop }, + { 0xffffffff, 0xffffffff }, + { kDToBStart, kDToBStop } + }, + { + { kAToBStart, kAToBStop }, + { kBToDStart, kBToDStop }, + { 0xffffffff, 0xffffffff }, + { 0xffffffff, 0xffffffff } + }, + { + { 0xffffffff, 0xffffffff }, + { kBToCStart, kBToCStop }, + { 0xffffffff, 0xffffffff }, + { 0xffffffff, 0xffffffff } + }, + { + { kACCWStart, kACCWStop }, + { kBCCWStart, kBCCWStop }, + { kCCCWStart, kCCCWStop }, + { kDCCWStart, kDCCWStop } + }, + { + { kACWStart, kACWStop }, + { kBCWStart, kBCWStop }, + { kCCWStart, kCCWStop }, + { kDCWStart, kDCWStop } + } +}; + +// Frame indices for the green ball sprite. +enum { + kGreenBallAtA, + kGreenBallAtAWithClaw, + kGreenBallAtAWithClawAndRobot, + kGreenBallAtB, + kGreenBallAtBWithClaw, + kGreenBallAtBWithClawAndRobot, + kGreenBallAtCArmAtA, + kGreenBallAtCArmAtB, + kGreenBallAtCArmAtD, + kGreenBallAtCWithClaw, + kGreenBallAtD, + kGreenBallAtDWithClaw, + kNumClawGreenBalls +}; + +// State constants for _robotState. +enum { + kNoRobot, + kRobotApproaching, + kPunchingOnce, + kPunchingTwice, + kPunchingThrice, + kCarriedToDoor, + kPlayerWon, + kRobotWon +}; + +// Sub Control Room button PICTs: +static const ResIDType kSubControlButtonBaseID = 500; +static const ResIDType kClawMonitorGreenBallBaseID = 600; + +// Constructor +SubControlRoom::SubControlRoom(Neighborhood *handler) : GameInteraction(kNoradSubControlRoomInteractionID, handler), + _subControlMovie(kSubControlMonitorID), _subControlNotification(kSubControlNotificationID, (PegasusEngine *)g_engine), + _clawMonitorMovie(kClawMonitorID), _pinchButton(kSubControlPinchID), _downButton(kSubControlDownID), + _rightButton(kSubControlRightID), _leftButton(kSubControlLeftID), _upButton(kSubControlUpID), + _ccwButton(kSubControlCCWID), _cwButton(kSubControlCWID), _greenBall(kClawMonitorGreenBallID), + _greenBallNotification(kNoradGreenBallNotificationID, (PegasusEngine *)g_engine) { + _neighborhoodNotification = handler->getNeighborhoodNotification(); + _playingAgainstRobot = false; + _robotState = kNoRobot; +} + +void SubControlRoom::playAgainstRobot() { + _playingAgainstRobot = true; +} + +void SubControlRoom::openInteraction() { + _currentAction = kNoActionIndex; + _nextAction = kNoActionIndex; + + Norad *owner = (Norad *)getOwner(); + owner->getClawInfo(_outSpotID, _prepSpotID, _clawControlSpotID, _clawButtonSpotIDs[0], + _clawButtonSpotIDs[1], _clawButtonSpotIDs[2], _clawButtonSpotIDs[3], + _clawButtonSpotIDs[4], _clawButtonSpotIDs[5], _clawButtonSpotIDs[6], + _clawStartPosition, _clawExtraIDs); + + _clawPosition = _clawStartPosition; + _clawNextPosition = _clawPosition; + _subControlMovie.initFromMovieFile("Images/Norad Alpha/N22 Left Monitor Movie"); + _subControlMovie.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel()); + _subControlMovie.moveElementTo(kNoradSubControlLeft, kNoradSubControlTop); + _subControlScale = _subControlMovie.getScale(); + _subControlMovie.setDisplayOrder(kSubControlOrder); + _subControlMovie.startDisplaying(); + _subControlCallBack.setNotification(&_subControlNotification); + _subControlCallBack.initCallBack(&_subControlMovie, kCallBackAtExtremes); + + _clawMonitorMovie.initFromMovieFile("Images/Norad Alpha/N22:N60 Right Monitor"); + _clawMonitorMovie.moveElementTo(kNoradClawMonitorLeft, kNoradClawMonitorTop); + _clawMonitorMovie.setDisplayOrder(kClawMonitorOrder); + _clawMonitorMovie.startDisplaying(); + _clawMonitorCallBack.setNotification(&_subControlNotification); + _clawMonitorCallBack.initCallBack(&_clawMonitorMovie, kCallBackAtExtremes); + + _subControlNotification.notifyMe(this, kSubControlNotificationFlags, kSubControlNotificationFlags); + + _neighborhoodNotification->notifyMe(this, kExtraCompletedFlag, kExtraCompletedFlag); + + _buttons[0] = &_pinchButton; + _buttons[1] = &_downButton; + _buttons[2] = &_rightButton; + _buttons[3] = &_leftButton; + _buttons[4] = &_upButton; + _buttons[5] = &_ccwButton; + _buttons[6] = &_cwButton; + + _pinchButton.setDisplayOrder(kSubControlPinchOrder); + _downButton.setDisplayOrder(kSubControlDownOrder); + _rightButton.setDisplayOrder(kSubControlRightOrder); + _leftButton.setDisplayOrder(kSubControlLeftOrder); + _upButton.setDisplayOrder(kSubControlUpOrder); + _ccwButton.setDisplayOrder(kSubControlCCWOrder); + _cwButton.setDisplayOrder(kSubControlCWOrder); + + for (int i = 0; i < kNumClawButtons; i++) { + SpriteFrame *frame = new SpriteFrame(); + frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kSubControlButtonBaseID + i * 3, true); + _buttons[i]->addFrame(frame, 0, 0); + + frame = new SpriteFrame(); + frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kSubControlButtonBaseID + i * 3 + 1, true); + _buttons[i]->addFrame(frame, 0, 0); + + frame = new SpriteFrame(); + frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kSubControlButtonBaseID + i * 3 + 2, true); + _buttons[i]->addFrame(frame, 0, 0); + + _buttons[i]->setCurrentFrameIndex(0); + _buttons[i]->startDisplaying(); + } + + _pinchButton.moveElementTo(kNoradSubControlPinchLeft, kNoradSubControlPinchTop); + _downButton.moveElementTo(kNoradSubControlDownLeft, kNoradSubControlDownTop); + _rightButton.moveElementTo(kNoradSubControlRightLeft, kNoradSubControlRightTop); + _leftButton.moveElementTo(kNoradSubControlLeftLeft, kNoradSubControlLeftTop); + _upButton.moveElementTo(kNoradSubControlUpLeft, kNoradSubControlUpTop); + _ccwButton.moveElementTo(kNoradSubControlCCWLeft, kNoradSubControlCCWTop); + _cwButton.moveElementTo(kNoradSubControlCWLeft, kNoradSubControlCWTop); + + _greenBall.setDisplayOrder(kClawMonitorGreenBallOrder); + + for (int i = 0; i < kNumClawGreenBalls; i++) { + SpriteFrame *frame = new SpriteFrame(); + frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kClawMonitorGreenBallBaseID + i); + _greenBall.addFrame(frame, 0, 0); + } + + _greenBall.setCurrentFrameIndex(0); + _greenBall.startDisplaying(); + + _greenBallTimer.setScale(owner->getNavMovie()->getScale()); + _greenBallCallBack.setNotification(&_greenBallNotification); + _greenBallCallBack.initCallBack(&_greenBallTimer, kCallBackAtExtremes); + _greenBallCallBack.setCallBackFlag(kOneSecondOfMoveFinished); + _greenBallNotification.notifyMe(this, kGreenBallNotificationFlags, kGreenBallNotificationFlags); + + _subControlMovie.show(); + _clawMonitorMovie.show(); +} + +void SubControlRoom::initInteraction() { + if (GameState.getNoradSubPrepState() == kSubDamaged) { + playControlMonitorSection(kDeltaSplashStart * _subControlScale, kDeltaSplashStop * _subControlScale, + 0, kDeltaSplash, false); + playClawMonitorSection(kDeltaClawSplashStart, kDeltaClawSplashStop, kDeltaSplashFinished, _gameState, false); + } else { + playControlMonitorSection(kAlphaSplashStart * _subControlScale, kAlphaSplashStop * _subControlScale, + 0, kAlphaSplash, false); + playClawMonitorSection(kAlphaClawSplashStart, kAlphaClawSplashStop, kAlphaSplashFinished, _gameState, false); + } + + _subControlMovie.redrawMovieWorld(); + _clawMonitorMovie.redrawMovieWorld(); +} + +void SubControlRoom::closeInteraction() { + _subControlNotification.cancelNotification(this); + _subControlCallBack.releaseCallBack(); + _greenBallNotification.cancelNotification(this); + _greenBallCallBack.releaseCallBack(); + _neighborhoodNotification->cancelNotification(this); +} + +void SubControlRoom::setSoundFXLevel(const uint16 fxLevel) { + _subControlMovie.setVolume(fxLevel); +} + +void SubControlRoom::receiveNotification(Notification *notification, const NotificationFlags flags) { + Norad *owner = (Norad *)getOwner(); + + if (notification == &_subControlNotification) { + switch (flags) { + case kAlphaSplashFinished: + setControlMonitorToTime(kMainMenuTime * _subControlScale, kAlphaMainMenu, true); + break; + case kPrepHighlightFinished: + if (GameState.getNoradSubPrepState() == kSubDamaged) + playControlMonitorSection(kDeltaLaunchPrepStart * _subControlScale, + kDeltaLaunchPrepStop * _subControlScale, kDeltaPrepFinished, _gameState, false); + else + playControlMonitorSection(kAlphaLaunchPrepStart * _subControlScale, + kAlphaLaunchPrepStop * _subControlScale, kAlphaPrepFinished, _gameState, false); + break; + case kAlphaPrepFinished: + GameState.setNoradSubPrepState(kSubPrepped); + GameState.setScoringPreppedSub(true); + setControlMonitorToTime(kMainMenuTime * _subControlScale, kAlphaMainMenu, true); + break; + case kClawHighlightFinished: + playControlMonitorSection(kClawMenuStart * _subControlScale, kClawMenuStop * _subControlScale, + kClawMenuFinished, _gameState, false); + break; + case kClawMenuFinished: + owner->playClawMonitorIntro(); + showButtons(); + setControlMonitorToTime(kClawMenuTime * _subControlScale, kClawMenu, true); + + if (!_playingAgainstRobot) { + updateClawMonitor(); + owner->loopExtraSequence(_clawExtraIDs[s_clawStateTable[_clawPosition][kLoopActionIndex]]); + } + break; + case kDeltaSplashFinished: + setControlMonitorToTime(kMainMenuTime * _subControlScale, kDeltaMainMenu, true); + + if (_playingAgainstRobot) { + _robotState = kRobotApproaching; + playClawMonitorSection(kRobotApproachStart, kRobotApproachStop, 0, _gameState, true); + owner->startExtraSequence(kN60RobotApproaches, kExtraCompletedFlag, kFilterAllInput); + } + break; + case kDeltaPrepFinished: + setControlMonitorToTime(kMainMenuTime * _subControlScale, kDeltaMainMenu, true); + break; + } + } else if (notification == &_greenBallNotification) { + if (_robotState == kRobotWon) { + // We are using the green ball notification to hide stuff when the robot comes through + // the glass. + hideEverything(); + } else { + // We are now midway through a move, time to update the claw's position and the green + // ball. + _clawPosition = _clawNextPosition; + updateClawMonitor(); + updateGreenBall(); + } + } else if (notification == _neighborhoodNotification) { + _currentAction = kNoActionIndex; + if (_playingAgainstRobot) { + switch (_robotState) { + case kRobotApproaching: + if (_gameState == kClawMenu) { + _robotState = kPunchingOnce; + dispatchClawAction(kNoActionIndex); + } else { + robotKillsPlayer(kN60FirstMistake, owner); + } + break; + case kPunchingOnce: + if (_nextAction == kMoveDownActionIndex) { + _robotState = kPunchingTwice; + performActionImmediately(_nextAction, _clawExtraIDs[s_clawStateTable[_clawPosition][_nextAction]], owner); + } else { + robotKillsPlayer(kN60SecondMistake, owner); + } + break; + case kPunchingTwice: + if (_nextAction == kPinchActionIndex) { + _robotState = kPunchingThrice; + performActionImmediately(_nextAction, _clawExtraIDs[s_clawStateTable[_clawPosition][_nextAction]], owner); + } else { + robotKillsPlayer(kN60ThirdMistake, owner); + } + break; + case kPunchingThrice: + if (_nextAction == kMoveRightActionIndex) { + _robotState = kCarriedToDoor; + performActionImmediately(_nextAction, _clawExtraIDs[s_clawStateTable[_clawPosition][_nextAction]], owner); + } else { + robotKillsPlayer(kN60FourthMistake, owner); + } + break; + case kCarriedToDoor: + hideEverything(); + _robotState = kPlayerWon; + owner->startExtraSequence(kN60PlayerFollowsRobotToDoor, kExtraCompletedFlag, kFilterAllInput); + break; + case kPlayerWon: + ((NoradDelta *)owner)->playerBeatRobotWithClaw(); + owner->requestDeleteCurrentInteraction(); + break; + case kRobotWon: + g_system->delayMillis(2 * 1000); // 120 ticks + ((PegasusEngine *)g_engine)->die(kDeathRobotSubControlRoom); + break; + } + } else { + if (_gameState == kPuttingClawAway && _nextAction == kNoActionIndex) { + if (_clawPosition == _clawStartPosition) { + Input scratch; + GameInteraction::clickInHotspot(scratch, g_allHotspots.findHotspotByID(_outSpotID)); + } else { + switch (_clawPosition) { + case kClawAtA: + dispatchClawAction(kMoveLeftActionIndex); + break; + case kClawAtB: + if (_clawStartPosition == kClawAtD) // Norad Alpha + dispatchClawAction(kMoveLeftActionIndex); + else if (_clawStartPosition == kClawAtC) // Norad Delta + dispatchClawAction(kMoveUpActionIndex); + break; + case kClawAtC: + dispatchClawAction(kMoveDownActionIndex); + break; + case kClawAtD: + dispatchClawAction(kMoveRightActionIndex); + break; + } + } + } else { + dispatchClawAction(_nextAction); + } + } + } +} + +void SubControlRoom::hideEverything() { + hideButtons(); + _subControlMovie.hide(); + _clawMonitorMovie.hide(); + _greenBall.hide(); +} + +void SubControlRoom::robotKillsPlayer(const uint32 extraID, Neighborhood *owner) { + _robotState = kRobotWon; + owner->startExtraSequence(extraID, kExtraCompletedFlag, kFilterAllInput); + _greenBallTimer.stop(); + _greenBallTimer.setSegment(0, 32 * _greenBallTimer.getScale() / 15); + _greenBallTimer.setTime(0); + _greenBallCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + _greenBallTimer.start(); +} + +void SubControlRoom::activateHotspots() { + if (_robotState == kRobotWon || _robotState == kPlayerWon) + return; + + GameInteraction::activateHotspots(); + + switch (_gameState) { + case kAlphaMainMenu: + case kDeltaMainMenu: + g_allHotspots.activateOneHotspot(_prepSpotID); + g_allHotspots.activateOneHotspot(_clawControlSpotID); + break; + case kClawMenu: + // This could be called during a move, so use _clawNextPosition. + if (_playingAgainstRobot) { + g_allHotspots.deactivateOneHotspot(_outSpotID); + if (_robotState != kRobotApproaching && _nextAction == kNoActionIndex) + for (int i = 0; i < kNumClawButtons; i++) + if (s_clawStateTable[_clawNextPosition][i] != kNoActionIndex) + g_allHotspots.activateOneHotspot(_clawButtonSpotIDs[i]); + } else if (_nextAction == kNoActionIndex) { + for (int i = 0; i < kNumClawButtons; i++) + if (s_clawStateTable[_clawNextPosition][i] != kNoActionIndex) + g_allHotspots.activateOneHotspot(_clawButtonSpotIDs[i]); + } + break; + default: + break; + } +} + +void SubControlRoom::showButtons() { + if (_playingAgainstRobot && _robotState == kRobotApproaching) { + for (int i = 0; i < kNumClawButtons; i++) { + _buttons[i]->show(); + _buttons[i]->setCurrentFrameIndex(kButtonDimFrame); + } + } else if (_nextAction != kNoActionIndex) { + for (int i = 0; i < kNumClawButtons; i++) { + _buttons[i]->show(); + if (i == _currentAction || i == _nextAction) + _buttons[i]->setCurrentFrameIndex(kButtonHighlightedFrame); + else + _buttons[i]->setCurrentFrameIndex(kButtonDimFrame); + } + } else { + for (int i = 0; i < kNumClawButtons; i++) { + _buttons[i]->show(); + if (i == _currentAction) + _buttons[i]->setCurrentFrameIndex(kButtonHighlightedFrame); + else if (s_clawStateTable[_clawNextPosition][i] != kNoActionIndex && + _gameState != kPuttingClawAway) // this could be called during a move, so check _clawNextPosition + _buttons[i]->setCurrentFrameIndex(kButtonActiveFrame); + else + _buttons[i]->setCurrentFrameIndex(kButtonDimFrame); + } + } +} + +void SubControlRoom::hideButtons() { + for (int i = 0; i < kNumClawButtons; i++) + _buttons[i]->hide(); +} + +int SubControlRoom::findActionIndex(HotSpotID id) { + for (int i = 0; i < kNumClawButtons; i++) + if (id == _clawButtonSpotIDs[i]) + return i; + + return kNoActionIndex; +} + +void SubControlRoom::clickInHotspot(const Input &input, const Hotspot *spot) { + HotSpotID clickedID = spot->getObjectID(); + int actionIndex = findActionIndex(clickedID); + + if (actionIndex != kNoActionIndex) { + dispatchClawAction(actionIndex); + } else if (clickedID == _prepSpotID) { + playControlMonitorSection(kLaunchPrepHighlightStart * _subControlScale, + kLaunchPrepHighlightStop * _subControlScale, + kPrepHighlightFinished, kPlayingHighlight, false); + } else if (clickedID == _clawControlSpotID) { + playControlMonitorSection(kClawControlHighlightStart * _subControlScale, + kClawControlHighlightStop * _subControlScale, + kClawHighlightFinished, kPlayingHighlight, false); + } else if (clickedID == _outSpotID) { + _gameState = kPuttingClawAway; + + if (_currentAction == kNoActionIndex) { + if (_clawPosition == _clawStartPosition) { + GameInteraction::clickInHotspot(input, spot); + } else { + switch (_clawPosition) { + case kClawAtA: + dispatchClawAction(kMoveLeftActionIndex); + break; + case kClawAtB: + if (_clawStartPosition == kClawAtD) // Norad Alpha + dispatchClawAction(kMoveLeftActionIndex); + else if (_clawStartPosition == kClawAtC) // Norad Delta + dispatchClawAction(kMoveUpActionIndex); + break; + case kClawAtC: + dispatchClawAction(kMoveDownActionIndex); + break; + case kClawAtD: + dispatchClawAction(kMoveRightActionIndex); + break; + } + } + } + } else { + GameInteraction::clickInHotspot(input, spot); + } +} + +void SubControlRoom::dispatchClawAction(const int newAction) { + GameState.setScoringPlayedWithClaw(true); + + Neighborhood *owner = getOwner(); + + if (newAction == kNoActionIndex) { + _currentAction = kNoActionIndex; + _nextAction = kNoActionIndex; + showButtons(); + updateGreenBall(); + + if (_playingAgainstRobot) + owner->startExtraSequence(kN60ArmActivated, kExtraCompletedFlag, kFilterAllInput); + else + owner->loopExtraSequence(_clawExtraIDs[s_clawStateTable[_clawPosition][kLoopActionIndex]]); + } else { + if (_currentAction == kNoActionIndex) { + if (_playingAgainstRobot) { + _nextAction = newAction; + showButtons(); + updateGreenBall(); + } else { + performActionImmediately(newAction, _clawExtraIDs[s_clawStateTable[_clawPosition][newAction]], owner); + } + } else if (_nextAction == kNoActionIndex) { + _nextAction = newAction; + showButtons(); + updateGreenBall(); + } + } +} + +void SubControlRoom::performActionImmediately(const int action, const uint32 extraID, Neighborhood *owner) { + _currentAction = action; + _nextAction = kNoActionIndex; + ExtraTable::Entry entry; + + switch (action) { + case kMoveDownActionIndex: + case kMoveRightActionIndex: + case kMoveLeftActionIndex: + case kMoveUpActionIndex: + // Set up green ball callback. + owner->getExtraEntry(extraID, entry); + _greenBallTimer.stop(); + _greenBallTimer.setSegment(entry.movieStart, entry.movieStart + owner->getNavMovie()->getScale()); + _greenBallTimer.setTime(entry.movieStart); + _greenBallCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + // Start move. + _greenBallTimer.start(); + break; + } + + if (_playingAgainstRobot) { + switch (_robotState) { + case kPunchingTwice: + owner->startExtraSequence(kN60ArmToPositionB, kExtraCompletedFlag, kFilterAllInput); + break; + case kPunchingThrice: + owner->startExtraSequence(kN60ArmGrabsRobot, kExtraCompletedFlag, kFilterAllInput); + break; + case kCarriedToDoor: + owner->startExtraSequence(kN60ArmCarriesRobotToPositionA, kExtraCompletedFlag, kFilterAllInput); + break; + } + } else { + owner->startExtraSequence(extraID, kExtraCompletedFlag, kFilterAllInput); + } + + switch (action) { + case kMoveDownActionIndex: + _clawNextPosition = s_clawMovieTable[_clawPosition][kMoveClawDown]; + break; + case kMoveRightActionIndex: + _clawNextPosition = s_clawMovieTable[_clawPosition][kMoveClawRight]; + break; + case kMoveLeftActionIndex: + _clawNextPosition = s_clawMovieTable[_clawPosition][kMoveClawLeft]; + break; + case kMoveUpActionIndex: + _clawNextPosition = s_clawMovieTable[_clawPosition][kMoveClawUp]; + break; + case kLoopActionIndex: + // Do nothing. + break; + default: + playClawMonitorSection(s_clawMonitorTable[action][_clawPosition][0], + s_clawMonitorTable[action][_clawPosition][1], 0, _gameState, true); + break; + } + + showButtons(); + updateGreenBall(); +} + +void SubControlRoom::setControlMonitorToTime(const TimeValue newTime, const int newState, const bool shouldAllowInput) { + _subControlMovie.stop(); + _subControlMovie.setSegment(0, _subControlMovie.getDuration()); + _subControlMovie.setTime(newTime); + _subControlMovie.redrawMovieWorld(); + _gameState = newState; + allowInput(shouldAllowInput); +} + +void SubControlRoom::playControlMonitorSection(const TimeValue in, const TimeValue out, const NotificationFlags flags, + const int newState, const bool shouldAllowInput) { + _subControlMovie.stop(); + _subControlMovie.setSegment(in, out); + _subControlMovie.setTime(in); + + if (flags != 0) { + _subControlCallBack.setCallBackFlag(flags); + _subControlCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + } + + _gameState = newState; + allowInput(shouldAllowInput); + _subControlMovie.start(); +} + +void SubControlRoom::updateClawMonitor() { + switch (_clawPosition) { + case kClawAtA: + setClawMonitorToTime(kClawAtATime); + break; + case kClawAtB: + setClawMonitorToTime(kClawAtBTime); + break; + case kClawAtC: + setClawMonitorToTime(kClawAtCTime); + break; + case kClawAtD: + setClawMonitorToTime(kClawAtDTime); + break; + } +} + +void SubControlRoom::setClawMonitorToTime(const TimeValue newTime) { + _clawMonitorMovie.stop(); + _clawMonitorMovie.setSegment(0, _clawMonitorMovie.getDuration()); + _clawMonitorMovie.setTime(newTime); + _clawMonitorMovie.redrawMovieWorld(); +} + +void SubControlRoom::playClawMonitorSection(const TimeValue in, const TimeValue out, const NotificationFlags flags, + const int newState, const bool shouldAllowInput) { + _clawMonitorMovie.stop(); + _clawMonitorMovie.setSegment(in, out); + _clawMonitorMovie.setTime(in); + + if (flags != 0) { + _clawMonitorCallBack.setCallBackFlag(flags); + _clawMonitorCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + } + + _gameState = newState; + allowInput(shouldAllowInput); + _clawMonitorMovie.start(); +} + +void SubControlRoom::updateGreenBall() { + switch (_currentAction) { + case kMoveDownActionIndex: + switch (_nextAction) { + case kMoveRightActionIndex: + moveGreenBallToA(); + break; + case kMoveLeftActionIndex: + moveGreenBallToD(); + break; + case kMoveUpActionIndex: + moveGreenBallToC(); + break; + default: + moveGreenBallToB(); + break; + } + break; + case kMoveRightActionIndex: + if (_clawNextPosition == kClawAtA) { + switch (_nextAction) { + case kMoveLeftActionIndex: + moveGreenBallToB(); + break; + default: + moveGreenBallToA(); + break; + } + } else { + switch (_nextAction) { + case kMoveRightActionIndex: + moveGreenBallToA(); + break; + case kMoveLeftActionIndex: + moveGreenBallToD(); + break; + case kMoveUpActionIndex: + moveGreenBallToC(); + break; + default: + moveGreenBallToB(); + break; + } + } + break; + case kMoveLeftActionIndex: + if (_clawNextPosition == kClawAtB) { + switch (_nextAction) { + case kMoveRightActionIndex: + moveGreenBallToA(); + break; + case kMoveLeftActionIndex: + moveGreenBallToD(); + break; + case kMoveUpActionIndex: + moveGreenBallToC(); + break; + default: + moveGreenBallToB(); + break; + } + } else { + switch (_nextAction) { + case kMoveRightActionIndex: + moveGreenBallToB(); + break; + default: + moveGreenBallToD(); + break; + } + } + break; + case kMoveUpActionIndex: + switch (_nextAction) { + case kMoveDownActionIndex: + moveGreenBallToB(); + break; + default: + moveGreenBallToC(); + break; + } + break; + default: + switch (_nextAction) { + case kMoveDownActionIndex: + moveGreenBallToB(); + break; + case kMoveRightActionIndex: + if (_clawPosition == kClawAtB) + moveGreenBallToA(); + else + moveGreenBallToB(); + break; + case kMoveLeftActionIndex: + if (_clawPosition == kClawAtB) + moveGreenBallToD(); + else + moveGreenBallToB(); + break; + case kMoveUpActionIndex: + moveGreenBallToC(); + break; + default: + _greenBall.hide(); + break; + } + break; + } +} + +void SubControlRoom::moveGreenBallToA() { + if (_clawPosition == kClawAtA) { + if (_playingAgainstRobot) + _greenBall.setCurrentFrameIndex(kGreenBallAtAWithClawAndRobot); + else + _greenBall.setCurrentFrameIndex(kGreenBallAtAWithClaw); + } else { + _greenBall.setCurrentFrameIndex(kGreenBallAtA); + } + + _greenBall.moveElementTo(kNoradGreenBallAtALeft, kNoradGreenBallAtATop); + _greenBall.show(); +} + +void SubControlRoom::moveGreenBallToB() { + if (_clawPosition == kClawAtB) { + if (_playingAgainstRobot) + _greenBall.setCurrentFrameIndex(kGreenBallAtBWithClawAndRobot); + else + _greenBall.setCurrentFrameIndex(kGreenBallAtBWithClaw); + } else { + _greenBall.setCurrentFrameIndex(kGreenBallAtB); + } + + _greenBall.moveElementTo(kNoradGreenBallAtBLeft, kNoradGreenBallAtBTop); + _greenBall.show(); +} + +void SubControlRoom::moveGreenBallToC() { + switch (_clawPosition) { + case kClawAtA: + _greenBall.setCurrentFrameIndex(kGreenBallAtCArmAtA); + break; + case kClawAtB: + _greenBall.setCurrentFrameIndex(kGreenBallAtCArmAtB); + break; + case kClawAtC: + _greenBall.setCurrentFrameIndex(kGreenBallAtCWithClaw); + break; + case kClawAtD: + _greenBall.setCurrentFrameIndex(kGreenBallAtCArmAtD); + break; + } + + _greenBall.moveElementTo(kNoradGreenBallAtCLeft, kNoradGreenBallAtCTop); + _greenBall.show(); +} + +void SubControlRoom::moveGreenBallToD() { + if (_clawPosition == kClawAtD) + _greenBall.setCurrentFrameIndex(kGreenBallAtDWithClaw); + else + _greenBall.setCurrentFrameIndex(kGreenBallAtD); + + _greenBall.moveElementTo(kNoradGreenBallAtDLeft, kNoradGreenBallAtDTop); + _greenBall.show(); +} + +bool SubControlRoom::canSolve() { + return _playingAgainstRobot && _robotState < kCarriedToDoor; +} + +void SubControlRoom::doSolve() { + _robotState = kCarriedToDoor; + hideEverything(); + getOwner()->startExtraSequence(kN60ArmGrabsRobot, kExtraCompletedFlag, kFilterAllInput); +} + +InputBits SubControlRoom::getInputFilter() { + if (_playingAgainstRobot) + return GameInteraction::getInputFilter() & ~kFilterDownButtonAny; + + return GameInteraction::getInputFilter(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/norad/subcontrolroom.h b/engines/pegasus/neighborhood/norad/subcontrolroom.h new file mode 100644 index 0000000000..6ce599db42 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/subcontrolroom.h @@ -0,0 +1,133 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_NEIGHBORHOOD_NORAD_SUBCONTROLROOM_H +#define PEGASUS_NEIGHBORHOOD_NORAD_SUBCONTROLROOM_H + +#include "pegasus/interaction.h" +#include "pegasus/notification.h" + +namespace Pegasus { + +static const uint32 kClawAtA = 0; +static const uint32 kClawAtB = 1; +static const uint32 kClawAtC = 2; +static const uint32 kClawAtD = 3; + +static const int kNumClawButtons = 7; + +class Norad; + +class SubControlRoom : public GameInteraction, public NotificationReceiver { +public: + SubControlRoom(Neighborhood *); + virtual ~SubControlRoom() {} + + void playAgainstRobot(); + + virtual void setSoundFXLevel(const uint16); + + bool canSolve(); + void doSolve(); + +protected: + virtual void openInteraction(); + virtual void initInteraction(); + virtual void closeInteraction(); + + virtual void activateHotspots(); + virtual void clickInHotspot(const Input &, const Hotspot *); + + virtual void receiveNotification(Notification *, const NotificationFlags); + + void robotKillsPlayer(const uint32, Neighborhood *); + InputBits getInputFilter(); + + int findActionIndex(HotSpotID); + void dispatchClawAction(const int); + void performActionImmediately(const int, const uint32, Neighborhood *); + + void hideEverything(); + void showButtons(); + void hideButtons(); + + void updateGreenBall(); + void moveGreenBallToA(); + void moveGreenBallToB(); + void moveGreenBallToC(); + void moveGreenBallToD(); + + void setControlMonitorToTime(const TimeValue, const int, const bool); + void playControlMonitorSection(const TimeValue, const TimeValue, const NotificationFlags, + const int, const bool); + + void updateClawMonitor(); + void setClawMonitorToTime(const TimeValue); + void playClawMonitorSection(const TimeValue, const TimeValue, const NotificationFlags, + const int, const bool); + + Movie _subControlMovie; + TimeScale _subControlScale; + Notification _subControlNotification; + NotificationCallBack _subControlCallBack; + Movie _clawMonitorMovie; + NotificationCallBack _clawMonitorCallBack; + int _gameState; + uint32 _clawStartPosition; + uint32 _clawPosition; + uint32 _clawNextPosition; + const uint32 *_clawExtraIDs; + + int _currentAction; + int _nextAction; + + Sprite *_buttons[kNumClawButtons]; + Sprite _pinchButton; + Sprite _downButton; + Sprite _rightButton; + Sprite _leftButton; + Sprite _upButton; + Sprite _ccwButton; + Sprite _cwButton; + + Sprite _greenBall; + TimeBase _greenBallTimer; + Notification _greenBallNotification; + NotificationCallBack _greenBallCallBack; + + HotSpotID _outSpotID; + HotSpotID _prepSpotID; + HotSpotID _clawControlSpotID; + HotSpotID _clawButtonSpotIDs[kNumClawButtons]; + + Notification *_neighborhoodNotification; + + bool _playingAgainstRobot; + int _robotState; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/norad/subplatform.cpp b/engines/pegasus/neighborhood/norad/subplatform.cpp new file mode 100644 index 0000000000..97079a9f53 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/subplatform.cpp @@ -0,0 +1,205 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/neighborhood/norad/constants.h" +#include "pegasus/neighborhood/norad/norad.h" +#include "pegasus/neighborhood/norad/subplatform.h" +#include "pegasus/neighborhood/norad/alpha/noradalpha.h" + +namespace Pegasus { + +// As usual, times here are in seconds. + +static const TimeValue kNormalSplashStart = 0; +static const TimeValue kNormalSplashStop = 5; + +static const TimeValue kPrepSubStart = 5; +static const TimeValue kPrepSubStop = 15; + +static const TimeValue kPrepIncompleteStart = 15; +static const TimeValue kPrepIncompleteStop = 19; + +static const TimeValue kDamagedStart = 19; +static const TimeValue kDamagedStop = 28; + +static const NotificationFlags kNormalSplashFinished = 1; +static const NotificationFlags kPrepSubFinished = kNormalSplashFinished << 1; +static const NotificationFlags kPrepIncompleteFinished = kPrepSubFinished << 1; +static const NotificationFlags kDamagedFinished = kPrepIncompleteFinished << 1; + +static const NotificationFlags kPlatformNotificationFlags = kNormalSplashFinished | + kPrepSubFinished | + kPrepIncompleteFinished | + kDamagedFinished; + +static const uint16 kSubPreppedBit = (1 << 0); +static const uint16 kWaitingForPlayerBit = (1 << 1); + +SubPlatform::SubPlatform(Neighborhood *handler) : GameInteraction(kNoradSubPlatformInteractionID, handler), + _platformMovie(kPlatformMonitorID), _platformNotification(kNoradSubPlatformNotificationID, (PegasusEngine *)g_engine) { + _neighborhoodNotification = handler->getNeighborhoodNotification(); +} + +void SubPlatform::openInteraction() { + _stateBits = 0; + + // TODO: These next two lines seem unused? + if (GameState.getNoradSubPrepState() == kSubPrepped) + _stateBits |= kSubPreppedBit; + + _stateBits |= kWaitingForPlayerBit; + _platformMovie.initFromMovieFile("Images/Norad Alpha/Platform Monitor Movie"); + _platformMovie.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel()); + _platformMovie.moveElementTo(kNoradPlatformLeft, kNoradPlatformTop); + _platformScale = _platformMovie.getScale(); + _platformMovie.setDisplayOrder(kPlatformOrder); + _platformMovie.startDisplaying(); + _platformCallBack.setNotification(&_platformNotification); + _platformCallBack.initCallBack(&_platformMovie, kCallBackAtExtremes); + + _platformNotification.notifyMe(this, kPlatformNotificationFlags, kPlatformNotificationFlags); +} + +void SubPlatform::initInteraction() { + _neighborhoodNotification->notifyMe(this, kExtraCompletedFlag, kExtraCompletedFlag); +} + +void SubPlatform::closeInteraction() { + _platformNotification.cancelNotification(this); + _platformCallBack.releaseCallBack(); + _neighborhoodNotification->cancelNotification(this); +} + +void SubPlatform::setSoundFXLevel(const uint16 fxLevel) { + _platformMovie.setVolume(fxLevel); +} + +void SubPlatform::receiveNotification(Notification *notification, const NotificationFlags flags) { + FaderMoveSpec loop1Spec, loop2Spec; + ExtraTable::Entry entry; + + Norad *owner = (Norad *)getOwner(); + + if (notification == &_platformNotification) { + switch (flags) { + case kNormalSplashFinished: + _platformMovie.stop(); + switch (GameState.getNoradSubPrepState()) { + case kSubNotPrepped: + _platformMovie.setSegment(kPrepIncompleteStart * _platformScale, kPrepIncompleteStop * _platformScale); + _platformMovie.setTime(kPrepIncompleteStart * _platformScale); + _platformCallBack.setCallBackFlag(kPrepIncompleteFinished); + _platformCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + _platformMovie.start(); + break; + case kSubPrepped: + _platformMovie.setSegment(kPrepSubStart * _platformScale, kPrepSubStop * _platformScale); + _platformMovie.setTime(kPrepSubStart * _platformScale); + _platformCallBack.setCallBackFlag(kPrepSubFinished); + _platformCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + owner->startExtraSequence(kNorad19PrepSub, 0, kFilterNoInput); + _platformMovie.start(); + break; + case kSubDamaged: + // Shouldn't happen. + break; + } + break; + case kPrepSubFinished: + _platformMovie.stop(); + _platformMovie.stopDisplaying(); + + owner->getExtraEntry(kNorad19ExitToSub, entry); + + loop1Spec.makeTwoKnotFaderSpec(kNoradAlphaMovieScale, 0, kNoradWarningVolume, + entry.movieEnd - entry.movieStart, 0); + loop1Spec.insertFaderKnot(4560, kNoradWarningVolume); + loop1Spec.insertFaderKnot(5080, 0); + + loop2Spec.makeTwoKnotFaderSpec(kNoradAlphaMovieScale, 0, kNoradSuckWindVolume, + entry.movieEnd - entry.movieStart, 0); + loop1Spec.insertFaderKnot(4560, kNoradSuckWindVolume); + loop1Spec.insertFaderKnot(5080, 0); + + owner->startExtraSequence(kNorad19ExitToSub, kExtraCompletedFlag, kFilterNoInput); + + owner->startLoop1Fader(loop1Spec); + owner->startLoop2Fader(loop2Spec); + break; + case kPrepIncompleteFinished: + ((NoradAlpha *)owner)->setSubPrepFailed(true); + g_AIArea->checkMiddleArea(); + // Fall through... + case kDamagedFinished: + _platformMovie.stop(); + _platformMovie.hide(); + _stateBits |= kWaitingForPlayerBit; + allowInput(true); + break; + } + } else if (notification == _neighborhoodNotification) { + allowInput(true); + ((PegasusEngine *)g_engine)->jumpToNewEnvironment(kNoradSubChaseID, kNoRoomID, kNoDirection); + GameState.setScoringEnteredSub(true); + } +} + +void SubPlatform::activateHotspots() { + if (_stateBits & kWaitingForPlayerBit) + g_allHotspots.activateOneHotspot(kNorad19ActivateMonitorSpotID); + + GameInteraction::activateHotspots(); +} + +void SubPlatform::clickInHotspot(const Input &input, const Hotspot *spot) { + if (spot->getObjectID() == kNorad19ActivateMonitorSpotID) { + if (GameState.getNoradSubPrepState() == kSubDamaged) { + _platformMovie.setSegment(kDamagedStart * _platformScale, kDamagedStop * _platformScale); + _platformMovie.setTime(kDamagedStart * _platformScale); + _platformCallBack.setCallBackFlag(kDamagedFinished); + } else { + _platformMovie.setSegment(kNormalSplashStart * _platformScale, kNormalSplashStop * _platformScale); + _platformMovie.setTime(kNormalSplashStart * _platformScale); + _platformCallBack.setCallBackFlag(kNormalSplashFinished); + } + + _platformCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + + _platformMovie.show(); + _platformMovie.start(); + _platformMovie.redrawMovieWorld(); + + _stateBits &= ~kWaitingForPlayerBit; + + allowInput(false); + } else { + GameInteraction::clickInHotspot(input, spot); + } +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/norad/subplatform.h b/engines/pegasus/neighborhood/norad/subplatform.h new file mode 100644 index 0000000000..82e86ecae2 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/subplatform.h @@ -0,0 +1,63 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_NEIGHBORHOOD_NORAD_SUBPLATFORM_H +#define PEGASUS_NEIGHBORHOOD_NORAD_SUBPLATFORM_H + +#include "pegasus/interaction.h" +#include "pegasus/movie.h" +#include "pegasus/notification.h" +#include "pegasus/timers.h" + +namespace Pegasus { + +class SubPlatform : public GameInteraction, public NotificationReceiver { +public: + SubPlatform(Neighborhood *); + virtual ~SubPlatform() {} + + virtual void setSoundFXLevel(const uint16); + +protected: + virtual void openInteraction(); + virtual void initInteraction(); + virtual void closeInteraction(); + + virtual void activateHotspots(); + virtual void clickInHotspot(const Input &, const Hotspot *); + + virtual void receiveNotification(Notification *, const NotificationFlags); + + Movie _platformMovie; + TimeScale _platformScale; + Notification _platformNotification; + NotificationCallBack _platformCallBack; + Notification *_neighborhoodNotification; + uint16 _stateBits; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/prehistoric/prehistoric.cpp b/engines/pegasus/neighborhood/prehistoric/prehistoric.cpp new file mode 100644 index 0000000000..814d7717de --- /dev/null +++ b/engines/pegasus/neighborhood/prehistoric/prehistoric.cpp @@ -0,0 +1,689 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/compass.h" +#include "pegasus/energymonitor.h" +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_action.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/ai/ai_condition.h" +#include "pegasus/ai/ai_rule.h" +#include "pegasus/neighborhood/prehistoric/prehistoric.h" + +namespace Pegasus { + +static const int16 s_prehistoricCompass[kPrehistoric25 + 1][4] = { + { 0, 170, 90, 270 }, // kPrehistoric01 + { 0, 180, 90, 270 }, // kPrehistoric02 + { 10, 180, 90, 270 }, // kPrehistoric03 + { 10, 190, 90, 270 }, // kPrehistoric04 + { 10, 195, 90, 270 }, // kPrehistoric05 + { 20, 210, 90, 270 }, // kPrehistoric06 + { 20, 200, 130, 276 }, // kPrehistoric07 + { 20, 176, 110, 260 }, // kPrehistoric08 + { 20, 200, 100, 270 }, // kPrehistoric09 + { 14, 186, 100, 280 }, // kPrehistoric10 + { 26, 206, 116, 296 }, // kPrehistoric11 + { 60, 226, 140, 320 }, // kPrehistoric12 + { 0, 180, 80, 270 }, // kPrehistoric13 + { 14, 200, 106, 286 }, // kPrehistoric14 + { -10, 174, 80, 260 }, // kPrehistoric15 + { 54, 236, 140, 210 }, // kPrehistoric16 + { -24, 160, 70, 250 }, // kPrehistoric17 + { 26, 206, 140, 296 }, // kPrehistoric18 + { -16, 160, 70, 250 }, // kPrehistoric19 + { -16, 160, 70, 250 }, // kPrehistoric20 + { -10, 160, 90, 250 }, // kPrehistoric21 + { -20, 160, 70, 244 }, // kPrehistoric22 + { -20, 160, 70, 244 }, // kPrehistoric22North + { 60, 234, 150, 330 }, // kPrehistoric23 + { 50, 230, 140, 320 }, // kPrehistoric24 + { 60, 240, 140, 330 } // kPrehistoric25 +}; + +static const TimeValue kPrehistoricFlashlightClickIn = 0; +static const TimeValue kPrehistoricFlashlightClickOut = 138; + +static const TimeValue kPrehistoricBumpIntoWallIn = 138; +static const TimeValue kPrehistoricBumpIntoWallOut = 291; + +static const TimeValue kBridgeRetractIn = 291; +static const TimeValue kBridgeRetractOut = 1499; + +static const TimeValue kPrehistoricWarningTimeLimit = kTenMinutes; + +Prehistoric::Prehistoric(InputHandler *nextHandler, PegasusEngine *owner) : Neighborhood(nextHandler, owner, "Prehistoric", kPrehistoricID) { + setIsItemTaken(kHistoricalLog); +} + +uint16 Prehistoric::getDateResID() const { + return kDatePrehistoricID; +} + +void Prehistoric::init() { + Neighborhood::init(); + + // Forces a stop so the flashlight can turn off... + forceStridingStop(kPrehistoric12, kSouth, kNoAlternateID); +} + +void Prehistoric::start() { + if (g_energyMonitor) { + g_energyMonitor->stopEnergyDraining(); + g_energyMonitor->restoreLastEnergyValue(); + _vm->resetEnergyDeathReason(); + g_energyMonitor->startEnergyDraining(); + } + + Neighborhood::start(); +} + +class FinishPrehistoricAction : public AIPlayMessageAction { +public: + FinishPrehistoricAction() : AIPlayMessageAction("Images/AI/Prehistoric/XP25W", false) {} + ~FinishPrehistoricAction() {} + + void performAIAction(AIRule *); + +}; + +void FinishPrehistoricAction::performAIAction(AIRule *rule) { + AIPlayMessageAction::performAIAction(rule); + ((PegasusEngine *)g_engine)->die(kPlayerWonGame); +} + +void Prehistoric::setUpAIRules() { + Neighborhood::setUpAIRules(); + + if (g_AIArea) { + if (_vm->isDemo()) { + FinishPrehistoricAction *doneAction = new FinishPrehistoricAction(); + AIHasItemCondition *hasLogCondition = new AIHasItemCondition(kHistoricalLog); + AIRule *rule = new AIRule(hasLogCondition, doneAction); + g_AIArea->addAIRule(rule); + } else { + AIPlayMessageAction *messageAction = new AIPlayMessageAction("Images/AI/Prehistoric/XP1NB", false); + AILocationCondition *locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kPrehistoric16, kNorth)); + AIRule *rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Prehistoric/XP2SB", false); + locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kPrehistoric01, kSouth)); + rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Prehistoric/XP2SB", false); + locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kPrehistoric08, kEast)); + rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Prehistoric/XP2SB", false); + locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kPrehistoric25, kWest)); + rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Prehistoric/XP16NB", false); + locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kPrehistoric23, kNorth)); + rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Prehistoric/XP18NB", false); + AITimerCondition *timerCondition = new AITimerCondition(kPrehistoricWarningTimeLimit, 1, true); + rule = new AIRule(timerCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Prehistoric/XP25W", false); + AIHasItemCondition *hasLogCondition = new AIHasItemCondition(kHistoricalLog); + rule = new AIRule(hasLogCondition, messageAction); + g_AIArea->addAIRule(rule); + } + } +} + +TimeValue Prehistoric::getViewTime(const RoomID room, const DirectionConstant direction) { + ExtraTable::Entry extra; + uint32 extraID = 0xffffffff; + + switch (MakeRoomView(room, direction)) { + case MakeRoomView(kPrehistoric02, kSouth): + if (!GameState.getPrehistoricSeenTimeStream()) { + getExtraEntry(kPreArrivalFromTSA, extra); + return extra.movieStart; + } + break; + case MakeRoomView(kPrehistoric25, kEast): + if (_privateFlags.getFlag(kPrehistoricPrivateVaultOpenFlag)) { + if (_vm->itemInLocation(kHistoricalLog, kPrehistoricID, kPrehistoric25, kEast)) + extraID = kPre25EastViewWithLog; + else + extraID = kPre25EastViewNoLog; + } + break; + } + + if (extraID == 0xffffffff) + return Neighborhood::getViewTime(room, direction); + + getExtraEntry(extraID, extra); + return extra.movieEnd - 1; +} + + +void Prehistoric::findSpotEntry(const RoomID room, const DirectionConstant direction, SpotFlags flags, SpotTable::Entry &entry) { + Neighborhood::findSpotEntry(room, direction, flags, entry); + + switch (MakeRoomView(room, direction)) { + case MakeRoomView(kPrehistoric01, kSouth): + case MakeRoomView(kPrehistoric25, kSouth): + entry.clear(); + break; + case MakeRoomView(kPrehistoric01, kEast): + if (GameState.getPrehistoricSeenFlyer1()) + entry.clear(); + else + GameState.setPrehistoricSeenFlyer1(true); + break; + case MakeRoomView(kPrehistoric08, kEast): + if (GameState.getPrehistoricSeenFlyer2()) + entry.clear(); + else + GameState.setPrehistoricSeenFlyer2(true); + break; + } +} + +int16 Prehistoric::getStaticCompassAngle(const RoomID room, const DirectionConstant dir) { + if (room == kPrehistoricDeath) + return g_compass->getFaderValue(); + + return s_prehistoricCompass[room][dir]; +} + +void Prehistoric::getExitCompassMove(const ExitTable::Entry &exitEntry, FaderMoveSpec &compassMove) { + uint32 angle; + Neighborhood::getExitCompassMove(exitEntry, compassMove); + + switch (MakeRoomView(exitEntry.room, exitEntry.direction)) { + case MakeRoomView(kPrehistoric01, kNorth): + compassMove.insertFaderKnot(exitEntry.movieStart + (exitEntry.movieEnd - exitEntry.movieStart) / 2, -10); + break; + case MakeRoomView(kPrehistoric06, kEast): + compassMove.insertFaderKnot(exitEntry.movieStart + (exitEntry.movieEnd - exitEntry.movieStart) / 4, 95); + compassMove.insertFaderKnot(exitEntry.movieStart + (exitEntry.movieEnd - exitEntry.movieStart) / 4 * 1, 100); + break; + case MakeRoomView(kPrehistoric18, kEast): + if (getCurrentAlternate() == kAltPrehistoricBridgeSet) { + compassMove.insertFaderKnot(exitEntry.movieStart + kPrehistoricFrameDuration * 11, 145); + compassMove.insertFaderKnot(exitEntry.movieStart + kPrehistoricFrameDuration * 26, 145); + compassMove.insertFaderKnot(exitEntry.movieStart + kPrehistoricFrameDuration * 39, 148); + compassMove.insertFaderKnot(exitEntry.movieStart + kPrehistoricFrameDuration * 114, 140); + } else { + compassMove.insertFaderKnot(exitEntry.movieStart + kPrehistoricFrameDuration * 10, 140); + compassMove.insertFaderKnot(exitEntry.movieStart + kPrehistoricFrameDuration * 16, 145); + compassMove.insertFaderKnot(exitEntry.movieEnd, 145); + } + break; + case MakeRoomView(kPrehistoric23, kWest): + angle = compassMove.getNthKnotValue(0); + compassMove.insertFaderKnot(exitEntry.movieStart + kPrehistoricFrameDuration * 17, angle); + compassMove.insertFaderKnot(exitEntry.movieStart + kPrehistoricFrameDuration * 32, angle - 90); + compassMove.insertFaderKnot(exitEntry.movieEnd, angle - 90); + break; + } +} + +void Prehistoric::turnTo(const DirectionConstant newDirection) { + setCurrentAlternate(kAltPrehistoricNormal); + _privateFlags.setFlag(kPrehistoricPrivateVaultOpenFlag, false); + Neighborhood::turnTo(newDirection); + + Item *keyCard; + + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kPrehistoric18, kEast): + zoomToVault(); + break; + case MakeRoomView(kPrehistoric18, kNorth): + case MakeRoomView(kPrehistoric18, kSouth): + if (_privateFlags.getFlag(kPrehistoricPrivateExtendedBridgeFlag)) { + playSpotSoundSync(kBridgeRetractIn, kBridgeRetractOut); + _privateFlags.setFlag(kPrehistoricPrivateExtendedBridgeFlag, false); + loadAmbientLoops(); + } + // fall through + case MakeRoomView(kPrehistoric25, kEast): + setCurrentActivation(kActivationVaultClosed); + break; + case MakeRoomView(kPrehistoric16, kNorth): + case MakeRoomView(kPrehistoric21, kWest): + keyCard = _vm->getAllItems().findItemByID(kKeyCard); + if (keyCard->getItemState() == kFlashlightOff) { + keyCard->setItemState(kFlashlightOn); + playSpotSoundSync(kPrehistoricFlashlightClickIn, kPrehistoricFlashlightClickOut); + } + break; + case MakeRoomView(kPrehistoric16, kEast): + case MakeRoomView(kPrehistoric16, kWest): + case MakeRoomView(kPrehistoric21, kNorth): + case MakeRoomView(kPrehistoric21, kSouth): + keyCard = _vm->getAllItems().findItemByID(kKeyCard); + if (keyCard->getItemState() == kFlashlightOn) { + keyCard->setItemState(kFlashlightOff); + playSpotSoundSync(kPrehistoricFlashlightClickIn, kPrehistoricFlashlightClickOut); + } + break; + } +} + +void Prehistoric::zoomToVault() { + if (!GameState.getPrehistoricSeenBridgeZoom()) + startExtraSequence(kPre18EastZoom, kExtraCompletedFlag, kFilterNoInput); +} + +void Prehistoric::checkContinuePoint(const RoomID room, const DirectionConstant direction) { + switch (MakeRoomView(room, direction)) { + case MakeRoomView(kPrehistoric08, kEast): + case MakeRoomView(kPrehistoric18, kSouth): + case MakeRoomView(kPrehistoric16, kNorth): + case MakeRoomView(kPrehistoric21, kNorth): + case MakeRoomView(kPrehistoric25, kNorth): + makeContinuePoint(); + break; + } +} + +void Prehistoric::arriveAt(const RoomID room, const DirectionConstant direction) { + Item *keyCard; + + if (MakeRoomView(room, direction) == MakeRoomView(kPrehistoric25, kEast) && + _privateFlags.getFlag(kPrehistoricPrivateExtendedBridgeFlag)) { + _navMovie.stop(); + playSpotSoundSync(kBridgeRetractIn, kBridgeRetractOut); + _privateFlags.setFlag(kPrehistoricPrivateExtendedBridgeFlag, false); + } + + Neighborhood::arriveAt(room, direction); + + switch (MakeRoomView(room, direction)) { + case MakeRoomView(kPrehistoricDeath, kNorth): + case MakeRoomView(kPrehistoricDeath, kSouth): + case MakeRoomView(kPrehistoricDeath, kEast): + case MakeRoomView(kPrehistoricDeath, kWest): + if (GameState.getLastRoom() == kPrehistoric23) + die(kDeathEatenByDinosaur); + else + die(kDeathFallOffCliff); + break; + case MakeRoomView(kPrehistoric02, kSouth): + if (!GameState.getPrehistoricSeenTimeStream()) { + GameState.setPrehistoricTriedToExtendBridge(false); + GameState.setPrehistoricSeenFlyer1(false); + GameState.setPrehistoricSeenFlyer2(false); + GameState.setPrehistoricSeenBridgeZoom(false); + GameState.setPrehistoricBreakerThrown(false); + startExtraSequence(kPreArrivalFromTSA, kExtraCompletedFlag, kFilterNoInput); + } + break; + case MakeRoomView(kPrehistoric18, kEast): + zoomToVault(); + break; + case MakeRoomView(kPrehistoric16, kNorth): + keyCard = _vm->getAllItems().findItemByID(kKeyCard); + + if (keyCard->getItemState() == kFlashlightOff) { + keyCard->setItemState(kFlashlightOn); + playSpotSoundSync(kPrehistoricFlashlightClickIn, kPrehistoricFlashlightClickOut); + } + + if (g_AIArea) + g_AIArea->checkRules(); + break; + case MakeRoomView(kPrehistoric01, kSouth): + case MakeRoomView(kPrehistoric23, kNorth): + if (g_AIArea) + g_AIArea->checkRules(); + break; + case MakeRoomView(kPrehistoric08, kSouth): + case MakeRoomView(kPrehistoric10, kSouth): + case MakeRoomView(kPrehistoric12, kSouth): + case MakeRoomView(kPrehistoric13, kNorth): + case MakeRoomView(kPrehistoric14, kSouth): + case MakeRoomView(kPrehistoric15, kNorth): + case MakeRoomView(kPrehistoric16, kSouth): + case MakeRoomView(kPrehistoric17, kNorth): + case MakeRoomView(kPrehistoric18, kSouth): + case MakeRoomView(kPrehistoric19, kNorth): + case MakeRoomView(kPrehistoric20, kNorth): + case MakeRoomView(kPrehistoric21, kEast): + keyCard = _vm->getAllItems().findItemByID(kKeyCard); + + if (keyCard->getItemState() == kFlashlightOn) { + keyCard->setItemState(kFlashlightOff); + playSpotSoundSync(kPrehistoricFlashlightClickIn, kPrehistoricFlashlightClickOut); + } + break; + case MakeRoomView(kPrehistoric25, kEast): + setCurrentActivation(kActivationVaultClosed); + break; + } +} + +void Prehistoric::loadAmbientLoops() { + RoomID room = GameState.getCurrentRoom(); + + switch (room) { + case kPrehistoric02: + // 1/4 volume. + if (GameState.getPrehistoricSeenTimeStream()) + loadLoopSound1("Sounds/Prehistoric/P02SAL00.22k.AIFF", 64); + break; + case kPrehistoric01: + case kPrehistoric03: + case kPrehistoric04: + case kPrehistoric05: + case kPrehistoric06: + case kPrehistoric07: + case kPrehistoric09: + case kPrehistoric11: + case kPrehistoric13: + case kPrehistoric15: + case kPrehistoric17: + case kPrehistoric19: + case kPrehistoric20: + // 1/4 volume. + loadLoopSound1("Sounds/Prehistoric/P02SAL00.22k.AIFF", 64); + break; + case kPrehistoric08: + case kPrehistoric10: + case kPrehistoric12: + case kPrehistoric14: + case kPrehistoric16: + case kPrehistoric18: + case kPrehistoric21: + // 3/16 volume. + loadLoopSound1("Sounds/Prehistoric/P02SAL00.22k.AIFF", 48); + break; + case kPrehistoric25: + // 1/8 volume. + loadLoopSound1("Sounds/Prehistoric/P02SAL00.22k.AIFF", 32); + break; + case kPrehistoric22: + case kPrehistoric22North: + case kPrehistoric23: + case kPrehistoric24: + case kPrehistoricDeath: + // 0 volume. + loadLoopSound1(""); + break; + } + + switch (room) { + case kPrehistoric02: + case kPrehistoric03: + case kPrehistoric04: + case kPrehistoric05: + case kPrehistoric06: + case kPrehistoric07: + case kPrehistoric08: + case kPrehistoric09: + case kPrehistoric10: + case kPrehistoric11: + case kPrehistoric12: + case kPrehistoric13: + case kPrehistoric14: + case kPrehistoric15: + case kPrehistoric16: + case kPrehistoric17: + case kPrehistoric19: + case kPrehistoric20: + case kPrehistoric21: + case kPrehistoricDeath: + loadLoopSound2(""); + break; + case kPrehistoric01: + case kPrehistoric25: + loadLoopSound2("Sounds/Prehistoric/VolcLoop.22K.AIFF", 64); + break; + case kPrehistoric18: + if (_privateFlags.getFlag(kPrehistoricPrivateExtendedBridgeFlag)) + loadLoopSound2("Sounds/Prehistoric/P18EAL00.22k.AIFF", 0x100, 0, 0); + else + loadLoopSound2(""); + break; + case kPrehistoric23: + case kPrehistoric24: + case kPrehistoric22: + case kPrehistoric22North: + loadLoopSound2("Sounds/Prehistoric/P24NAL00.22k.AIFF", 64); + break; + } +} + +void Prehistoric::activateHotspots() { + Neighborhood::activateHotspots(); + + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kPrehistoric18, kEast): + if (!_privateFlags.getFlag(kPrehistoricPrivateExtendedBridgeFlag)) + _vm->getAllHotspots().activateOneHotspot(kPre18EastSpotID); + break; + case MakeRoomView(kPrehistoric22North, kNorth): + _vm->getAllHotspots().activateOneHotspot(kPre22NorthBreakerSpotID); + break; + } +} + +void Prehistoric::clickInHotspot(const Input &input, const Hotspot *spot) { + switch (spot->getObjectID()) { + case kPre18EastSpotID: + if (GameState.getPrehistoricBreakerThrown()) + startExtraSequence(kPre18EastBridgeOn, kExtraCompletedFlag, kFilterNoInput); + else + startExtraSequence(kPre18EastBridgeOut, kExtraCompletedFlag, kFilterNoInput); + break; + case kPre22NorthBreakerSpotID: + startExtraSequence(kPre22ThrowBreaker, kExtraCompletedFlag, kFilterNoInput); + break; + default: + Neighborhood::clickInHotspot(input, spot); + break; + } +} + +void Prehistoric::receiveNotification(Notification *notification, const NotificationFlags flags) { + Neighborhood::receiveNotification(notification, flags); + + if ((flags & kExtraCompletedFlag) != 0) { + _interruptionFilter = kFilterAllInput; + + switch (_lastExtra) { + case kPreArrivalFromTSA: + GameState.setPrehistoricSeenTimeStream(true); + loadAmbientLoops(); + makeContinuePoint(); + break; + case kPre18EastZoom: + startExtraSequence(kPre18EastZoomOut, kExtraCompletedFlag, kFilterNoInput); + break; + case kPre18EastZoomOut: + GameState.setPrehistoricSeenBridgeZoom(true); + break; + case kPre18EastBridgeOn: + _privateFlags.setFlag(kPrehistoricPrivateExtendedBridgeFlag, true); + setCurrentAlternate(kAltPrehistoricBridgeSet); + GameState.setPrehistoricTriedToExtendBridge(false); + loadAmbientLoops(); + GameState.setScoringExtendedBridge(true); + break; + case kPre18EastBridgeOut: + GameState.setPrehistoricTriedToExtendBridge(true); + if (g_AIArea) + g_AIArea->checkMiddleArea(); + break; + case kPre22ThrowBreaker: + GameState.setPrehistoricBreakerThrown(true); + GameState.setScoringThrewBreaker(true); + break; + case kPre25EastUnlockingVaultNoLog: + case kPre25EastUnlockingVaultWithLog: + _vm->addItemToInventory((InventoryItem *)_vm->getAllItems().findItemByID(kJourneymanKey)); + break; + } + } + + g_AIArea->checkMiddleArea(); +} + +Common::String Prehistoric::getBriefingMovie() { + Common::String movieName = Neighborhood::getBriefingMovie(); + + if (movieName.empty()) + movieName = "Images/AI/Prehistoric/XPE"; + + return movieName; +} + +Common::String Prehistoric::getEnvScanMovie() { + Common::String movieName = Neighborhood::getEnvScanMovie(); + + if (movieName.empty()) { + if (!_vm->isDemo()) { + switch (GameState.getCurrentRoom()) { + case kPrehistoric16: + case kPrehistoric23: + case kPrehistoric24: + return "Images/AI/Prehistoric/XP7WB"; + } + } + + return "Images/AI/Prehistoric/XP17NB"; + } + + return movieName; +} + +uint Prehistoric::getNumHints() { + uint numHints = Neighborhood::getNumHints(); + + if (numHints == 0) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kPrehistoric18, kEast): + if (!GameState.getPrehistoricBreakerThrown() && GameState.getPrehistoricTriedToExtendBridge() && + !_privateFlags.getFlag(kPrehistoricPrivateExtendedBridgeFlag)) + numHints = 1; + break; + case MakeRoomView(kPrehistoric25, kEast): + if (!_privateFlags.getFlag(kPrehistoricPrivateVaultOpenFlag)) + numHints = 1; + break; + } + } + + return numHints; +} + +Common::String Prehistoric::getHintMovie(uint hintNum) { + Common::String movieName = Neighborhood::getHintMovie(hintNum); + + if (movieName.empty()) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kPrehistoric18, kEast): + return "Images/AI/Prehistoric/XP18WD"; + case MakeRoomView(kPrehistoric25, kEast): + return "Images/AI/Globals/XGLOB1A"; + } + } + + return movieName; +} + +bool Prehistoric::canSolve() { + return GameState.getCurrentRoomAndView() == MakeRoomView(kPrehistoric18, kEast) && + !GameState.getPrehistoricBreakerThrown() && + GameState.getPrehistoricTriedToExtendBridge() && + !_privateFlags.getFlag(kPrehistoricPrivateExtendedBridgeFlag); +} + +void Prehistoric::doSolve() { + GameState.setPrehistoricBreakerThrown(true); + startExtraSequence(kPre18EastBridgeOn, kExtraCompletedFlag, kFilterNoInput); +} + +Hotspot *Prehistoric::getItemScreenSpot(Item *item, DisplayElement *element) { + if (item->getObjectID() == kHistoricalLog) + return _vm->getAllHotspots().findHotspotByID(kPrehistoricHistoricalLogSpotID); + + return Neighborhood::getItemScreenSpot(item, element); +} + +void Prehistoric::pickedUpItem(Item *item) { + switch (item->getObjectID()) { + case kHistoricalLog: + GameState.setScoringGotHistoricalLog(true); + break; + } + + Neighborhood::pickedUpItem(item); +} + +void Prehistoric::dropItemIntoRoom(Item *item, Hotspot *dropSpot) { + switch (item->getObjectID()) { + case kJourneymanKey: + Neighborhood::dropItemIntoRoom(item, dropSpot); + + if (GameState.isTakenItemID(kHistoricalLog)) + startExtraLongSequence(kPre25EastUnlockingVaultNoLog, kPre25EastVaultOpenNoLog, kExtraCompletedFlag, kFilterNoInput); + else + startExtraLongSequence(kPre25EastUnlockingVaultWithLog, kPre25EastVaultOpenWithLog, kExtraCompletedFlag, kFilterNoInput); + + _privateFlags.setFlag(kPrehistoricPrivateVaultOpenFlag, true); + setCurrentActivation(kActivationVaultOpen); + break; + default: + Neighborhood::dropItemIntoRoom(item, dropSpot); + break; + } +} + +void Prehistoric::bumpIntoWall() { + requestSpotSound(kPrehistoricBumpIntoWallIn, kPrehistoricBumpIntoWallOut, kFilterAllInput, 0); + Neighborhood::bumpIntoWall(); +} + +Common::String Prehistoric::getNavMovieName() { + return "Images/Prehistoric/Prehistoric.movie"; +} + +Common::String Prehistoric::getSoundSpotsName() { + return "Sounds/Prehistoric/Prehistoric Spots"; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/prehistoric/prehistoric.h b/engines/pegasus/neighborhood/prehistoric/prehistoric.h new file mode 100644 index 0000000000..17f9993014 --- /dev/null +++ b/engines/pegasus/neighborhood/prehistoric/prehistoric.h @@ -0,0 +1,158 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_NEIGHBORHOOD_PREHISTORIC_H +#define PEGASUS_NEIGHBORHOOD_PREHISTORIC_H + +#include "pegasus/neighborhood/neighborhood.h" + +namespace Pegasus { + +static const TimeScale kPrehistoricMovieScale = 600; +static const TimeScale kPrehistoricFramesPerSecond = 15; +static const TimeScale kPrehistoricFrameDuration = 40; + +// Alternate IDs. + +static const AlternateID kAltPrehistoricNormal = 0; +static const AlternateID kAltPrehistoricBridgeSet = 1; + +// Room IDs. + +static const RoomID kPrehistoric01 = 0; +static const RoomID kPrehistoric02 = 1; +static const RoomID kPrehistoric03 = 2; +static const RoomID kPrehistoric04 = 3; +static const RoomID kPrehistoric05 = 4; +static const RoomID kPrehistoric06 = 5; +static const RoomID kPrehistoric07 = 6; +static const RoomID kPrehistoric08 = 7; +static const RoomID kPrehistoric09 = 8; +static const RoomID kPrehistoric10 = 9; +static const RoomID kPrehistoric11 = 10; +static const RoomID kPrehistoric12 = 11; +static const RoomID kPrehistoric13 = 12; +static const RoomID kPrehistoric14 = 13; +static const RoomID kPrehistoric15 = 14; +static const RoomID kPrehistoric16 = 15; +static const RoomID kPrehistoric17 = 16; +static const RoomID kPrehistoric18 = 17; +static const RoomID kPrehistoric19 = 18; +static const RoomID kPrehistoric20 = 19; +static const RoomID kPrehistoric21 = 20; +static const RoomID kPrehistoric22 = 21; +static const RoomID kPrehistoric22North = 22; +static const RoomID kPrehistoric23 = 23; +static const RoomID kPrehistoric24 = 24; +static const RoomID kPrehistoric25 = 25; +static const RoomID kPrehistoricDeath = 26; + +// Hot Spot Activation IDs. + +static const HotSpotActivationID kActivationVaultClosed = 1; +static const HotSpotActivationID kActivationVaultOpen = 2; + +// Hot Spot IDs. + +static const HotSpotID kPre18EastSpotID = 5000; +static const HotSpotID kPre22NorthSpotID = 5001; +static const HotSpotID kPre22NorthOutSpotID = 5002; +static const HotSpotID kPre22NorthBreakerSpotID = 5003; +static const HotSpotID kPrehistoricKeyDropSpotID = 5004; +static const HotSpotID kPrehistoricHistoricalLogSpotID = 5005; + +// Extra sequence IDs. + +static const ExtraID kPreArrivalFromTSA = 0; +static const ExtraID kPre18EastBridgeOut = 1; +static const ExtraID kPre18EastBridgeOn = 2; +static const ExtraID kPre18EastZoom = 3; +static const ExtraID kPre18EastZoomOut = 4; +static const ExtraID kPre22ThrowBreaker = 5; +static const ExtraID kPre25EastUnlockingVaultWithLog = 6; +static const ExtraID kPre25EastVaultOpenWithLog = 7; +static const ExtraID kPre25EastViewWithLog = 8; +static const ExtraID kPre25EastUnlockingVaultNoLog = 9; +static const ExtraID kPre25EastVaultOpenNoLog = 10; +static const ExtraID kPre25EastViewNoLog = 11; + +class PegasusEngine; + +class Prehistoric : public Neighborhood { +public: + Prehistoric(InputHandler *, PegasusEngine *); + virtual ~Prehistoric() {} + + virtual uint16 getDateResID() const; + virtual void init(); + + virtual void arriveAt(const RoomID, const DirectionConstant); + virtual void activateHotspots(); + virtual void clickInHotspot(const Input &, const Hotspot *); + Common::String getBriefingMovie(); + Common::String getEnvScanMovie(); + uint getNumHints(); + Common::String getHintMovie(uint); + + Hotspot *getItemScreenSpot(Item *, DisplayElement *); + void dropItemIntoRoom(Item *, Hotspot *); + void pickedUpItem(Item *); + + void start(); + + void bumpIntoWall(); + + void checkContinuePoint(const RoomID, const DirectionConstant); + + bool canSolve(); + void doSolve(); + +protected: + enum { + kPrehistoricPrivateVaultOpenFlag, + kPrehistoricPrivateExtendedBridgeFlag, + kNumPrehistoricPrivateFlags + }; + + void setUpAIRules(); + int16 getStaticCompassAngle(const RoomID, const DirectionConstant); + void getExitCompassMove(const ExitTable::Entry &, FaderMoveSpec &); + virtual void receiveNotification(Notification *, const NotificationFlags); + void turnTo(const DirectionConstant); + void zoomToVault(); + TimeValue getViewTime(const RoomID, const DirectionConstant); + void findSpotEntry(const RoomID, const DirectionConstant, SpotFlags, SpotTable::Entry &); + + void loadAmbientLoops(); + + FlagsArray<byte, kNumPrehistoricPrivateFlags> _privateFlags; + + Common::String getNavMovieName(); + Common::String getSoundSpotsName(); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/spot.cpp b/engines/pegasus/neighborhood/spot.cpp new file mode 100644 index 0000000000..f285bf9bc2 --- /dev/null +++ b/engines/pegasus/neighborhood/spot.cpp @@ -0,0 +1,70 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "common/debug.h" +#include "common/stream.h" +#include "common/textconsole.h" + +#include "pegasus/neighborhood/spot.h" + +namespace Pegasus { + +void SpotTable::loadFromStream(Common::SeekableReadStream *stream) { + uint32 count = stream->readUint32BE(); + _entries.resize(count); + + for (uint32 i = 0; i < count; i++) { + _entries[i].room = stream->readUint16BE(); + _entries[i].direction = stream->readByte(); + _entries[i].srcFlags = stream->readByte(); + _entries[i].altCode = stream->readByte(); + stream->readByte(); // alignment + _entries[i].movieStart = stream->readUint32BE(); + _entries[i].movieEnd = stream->readUint32BE(); + _entries[i].dstFlags = stream->readByte(); + stream->readByte(); // alignment + debug(0, "Spot[%d]: %d %d %d %d %d %d %d", i, _entries[i].room, _entries[i].direction, + _entries[i].srcFlags, _entries[i].altCode, _entries[i].movieStart, + _entries[i].movieEnd, _entries[i].dstFlags); + } +} + +void SpotTable::clear() { + _entries.clear(); +} + +// Two SpotTable::Entries are equal if +// In addition to having their rooms, directions and alt codes identical... +// They are both either loops or once only animations AND +// They overlap in at least one of the on arrival, on turn and on door open bits. +SpotTable::Entry SpotTable::findEntry(RoomID room, DirectionConstant direction, SpotFlags srcFlags, AlternateID altCode) { + for (uint32 i = 0; i < _entries.size(); i++) + if (_entries[i].room == room && _entries[i].direction == direction && _entries[i].altCode == altCode && (_entries[i].srcFlags & kSpotLoopsMask) == (srcFlags & kSpotLoopsMask) && ((_entries[i].srcFlags & srcFlags) & kSpotTriggers) != 0) + return _entries[i]; + + return Entry(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/spot.h b/engines/pegasus/neighborhood/spot.h new file mode 100644 index 0000000000..a985420b7c --- /dev/null +++ b/engines/pegasus/neighborhood/spot.h @@ -0,0 +1,97 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_NEIGHBORHOOD_SPOT_H +#define PEGASUS_NEIGHBORHOOD_SPOT_H + +#include "common/array.h" +#include "common/endian.h" + +#include "pegasus/constants.h" + +namespace Common { + class SeekableReadStream; +} + +namespace Pegasus { + +typedef byte SpotFlags; + +enum { + kSpotLoopsBit, // Loop or once only? + kSpotOnArrivalBit, + kSpotOnTurnBit, + kSpotOnDoorOpenBit +}; + +static const SpotFlags kNoSpotFlags = 0; +static const SpotFlags kSpotLoopsMask = 1 << kSpotLoopsBit; +static const SpotFlags kSpotOnArrivalMask = 1 << kSpotOnArrivalBit; +static const SpotFlags kSpotOnTurnMask = 1 << kSpotOnTurnBit; +static const SpotFlags kSpotOnDoorOpenMask = 1 << kSpotOnDoorOpenBit; + +static const SpotFlags kSpotTriggers = kSpotOnArrivalMask | kSpotOnTurnMask | kSpotOnDoorOpenMask; + +class SpotTable { +public: + SpotTable() {} + ~SpotTable() {} + + static uint32 getResTag() { return MKTAG('S', 'p', 'o', 't'); } + + void loadFromStream(Common::SeekableReadStream *stream); + void clear(); + + struct Entry { + Entry() { clear(); } + bool isEmpty() { return movieStart == 0xffffffff; } + void clear() { + room = kNoRoomID; + direction = kNoDirection; + srcFlags = kNoSpotFlags; + altCode = kNoAlternateID; + movieStart = 0xffffffff; + movieEnd = 0xffffffff; + dstFlags = kNoSpotFlags; + } + + RoomID room; + DirectionConstant direction; + SpotFlags srcFlags; + AlternateID altCode; + TimeValue movieStart; + TimeValue movieEnd; + SpotFlags dstFlags; + }; + + Entry findEntry(RoomID room, DirectionConstant direction, SpotFlags srcFlags, AlternateID altCode); + +private: + Common::Array<Entry> _entries; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/tsa/fulltsa.cpp b/engines/pegasus/neighborhood/tsa/fulltsa.cpp new file mode 100644 index 0000000000..9b843da5d6 --- /dev/null +++ b/engines/pegasus/neighborhood/tsa/fulltsa.cpp @@ -0,0 +1,3030 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/cursor.h" +#include "pegasus/energymonitor.h" +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/biochips/aichip.h" +#include "pegasus/items/biochips/opticalchip.h" +#include "pegasus/neighborhood/caldoria/caldoria.h" +#include "pegasus/neighborhood/norad/constants.h" +#include "pegasus/neighborhood/prehistoric/prehistoric.h" +#include "pegasus/neighborhood/mars/constants.h" +#include "pegasus/neighborhood/tsa/fulltsa.h" +#include "pegasus/neighborhood/wsc/wsc.h" + +namespace Pegasus { + +// TSA PICTs: + +static const ResIDType kTBPCloseBoxPICTID = 800; +static const ResIDType kTBPRewindPICTID = 801; +static const ResIDType kUnresolvedPICTID = 802; +static const ResIDType kResolvedPICTID = 803; +static const ResIDType kJumpMenuPICTID = 804; +static const ResIDType kJumpMenuHilitedPICTID = 805; +static const ResIDType kExitPICTID = 806; +static const ResIDType kExitHilitedPICTID = 807; +static const ResIDType kLeftRipPICTID = 808; +static const ResIDType kComparisonCloseBoxPICTID = 809; +static const ResIDType kComparisonLeftRewindPICTID = 810; +static const ResIDType kComparisonRightRewindPICTID = 811; +static const ResIDType kComparisonHiliteNoradPICTID = 812; +static const ResIDType kComparisonHiliteMarsPICTID = 813; +static const ResIDType kComparisonHiliteCaldoriaPICTID = 814; +static const ResIDType kComparisonHiliteWSCPICTID = 815; +static const ResIDType kComparisonChancesNoradPICTID = 816; +static const ResIDType kComparisonChancesMarsPICTID = 817; +static const ResIDType kComparisonChancesCaldoriaPICTID = 818; +static const ResIDType kComparisonChancesWSCPICTID = 819; +static const ResIDType kRedirectionCCRolloverPICTID = 820; +static const ResIDType kRedirectionRRRolloverPICTID = 821; +static const ResIDType kRedirectionFDRolloverPICTID = 822; +static const ResIDType kRedirectionCCDoorPICTID = 823; +static const ResIDType kRedirectionRRDoorPICTID = 824; +static const ResIDType kRedirectionFDDoorPICTID = 825; +static const ResIDType kRedirectionSecuredPICTID = 826; +static const ResIDType kRedirectionNewTargetPICTID = 827; +static const ResIDType kRedirectionClosePICTID = 828; + +static const int16 kCompassShift = 15; + +static const TimeScale kFullTSAMovieScale = 600; +static const TimeScale kFullTSAFramesPerSecond = 15; +static const TimeScale kFullTSAFrameDuration = 40; + +// Alternate IDs. +static const AlternateID kAltTSANormal = 0; +static const AlternateID kAltTSARobotsAtReadyRoom = 1; +static const AlternateID kAltTSARobotsAtFrontDoor = 2; +static const AlternateID kAltTSARedAlert = 3; + +// Room IDs. +static const RoomID kTSA01 = 1; +static const RoomID kTSA02 = 2; +static const RoomID kTSA03 = 3; +static const RoomID kTSA04 = 4; +static const RoomID kTSA05 = 5; +static const RoomID kTSA0A = 6; +static const RoomID kTSA06 = 7; +static const RoomID kTSA07 = 8; +static const RoomID kTSA08 = 9; +static const RoomID kTSA09 = 10; +static const RoomID kTSA10 = 11; +static const RoomID kTSA11 = 12; +static const RoomID kTSA12 = 13; +static const RoomID kTSA13 = 14; +static const RoomID kTSA14 = 15; +static const RoomID kTSA15 = 16; +static const RoomID kTSA16 = 17; +static const RoomID kTSA17 = 18; +static const RoomID kTSA18 = 19; +static const RoomID kTSA19 = 20; +static const RoomID kTSA0B = 21; +static const RoomID kTSA21Cyan = 22; +static const RoomID kTSA22Cyan = 23; +static const RoomID kTSA23Cyan = 24; +static const RoomID kTSA24Cyan = 25; +static const RoomID kTSA25Cyan = 26; +static const RoomID kTSA21Red = 27; +static const RoomID kTSA23Red = 29; +static const RoomID kTSA24Red = 30; +static const RoomID kTSA25Red = 31; +static const RoomID kTSA26 = 32; +static const RoomID kTSA27 = 33; +static const RoomID kTSA28 = 34; +static const RoomID kTSA29 = 35; +static const RoomID kTSA30 = 36; +static const RoomID kTSA31 = 37; +static const RoomID kTSA32 = 38; +static const RoomID kTSA33 = 39; +static const RoomID kTSA34 = 40; +static const RoomID kTSA35 = 41; +static const RoomID kTSADeathRoom = 43; + +// Hot Spot Activation IDs. +static const HotSpotActivationID kActivateTSAReadyForCard = 1; +static const HotSpotActivationID kActivateTSAReadyToTransport = 2; +static const HotSpotActivationID kActivateTSARobotsAwake = 3; +static const HotSpotActivationID kActivateTSA0BZoomedOut = 4; +static const HotSpotActivationID kActivateTSA0BZoomedIn = 5; +static const HotSpotActivationID kActivateTSA0BComparisonVideo = 6; +static const HotSpotActivationID kActivationLogReaderOpen = 7; +static const HotSpotActivationID kActivateTSA0BTBPVideo = 8; +static const HotSpotActivationID kActivationDoesntHaveKey = 9; +static const HotSpotActivationID kActivationKeyVaultOpen = 10; +static const HotSpotActivationID kActivationDoesntHaveChips = 11; +static const HotSpotActivationID kActivationChipVaultOpen = 12; +static const HotSpotActivationID kActivationJumpToPrehistoric = 13; +static const HotSpotActivationID kActivationJumpToNorad = 14; +static const HotSpotActivationID kActivationJumpToMars = 15; +static const HotSpotActivationID kActivationJumpToWSC = 16; +static const HotSpotActivationID kActivationReadyToExit = 17; +static const HotSpotActivationID kActivationReadyForJumpMenu = 18; +static const HotSpotActivationID kActivationMainJumpMenu = 19; + +// Hot Spot IDs. +static const HotSpotID kTSAGTCardDropSpotID = 5000; +static const HotSpotID kTSAGTTokyoSpotID = 5001; +static const HotSpotID kTSAGTCaldoriaSpotID = 5002; +static const HotSpotID kTSAGTBeachSpotID = 5003; +static const HotSpotID kTSAGTOtherSpotID = 5004; +static const HotSpotID kTSA02DoorSpotID = 5005; +static const HotSpotID kTSA03EastJimenezSpotID = 5006; +static const HotSpotID kTSA03WestCrenshawSpotID = 5007; +static const HotSpotID kTSA04EastMatsumotoSpotID = 5008; +static const HotSpotID kTSA04WestCastilleSpotID = 5009; +static const HotSpotID kTSA05EastSinclairSpotID = 5010; +static const HotSpotID kTSA05WestWhiteSpotID = 5011; +static const HotSpotID kTSA0AEastSpotID = 5012; +static const HotSpotID kTSA0AWastSpotID = 5013; +static const HotSpotID kTSA0BEastMonitorSpotID = 5014; +static const HotSpotID kTSA0BEastMonitorOutSpotID = 5015; +static const HotSpotID kTSA0BEastCompareNoradSpotID = 5016; +static const HotSpotID kTSA0BEastCompareMarsSpotID = 5017; +static const HotSpotID kTSA0BEastCompareCaldoriaSpotID = 5018; +static const HotSpotID kTSA0BEastCompareWSCSpotID = 5019; +static const HotSpotID kTSA0BEastLeftRewindSpotID = 5020; +static const HotSpotID kTSA0BEastLeftPlaySpotID = 5021; +static const HotSpotID kTSA0BEastRightRewindSpotID = 5022; +static const HotSpotID kTSA0BEastRightPlaySpotID = 5023; +static const HotSpotID kTSA0BEastCloseVideoSpotID = 5024; +static const HotSpotID kTSA0BNorthMonitorSpotID = 5025; +static const HotSpotID kTSA0BNorthMonitorOutSpotID = 5026; +static const HotSpotID kTSA0BNorthHistLogSpotID = 5027; +static const HotSpotID kTSA0BNorthRobotsToCommandCenterSpotID = 5028; +static const HotSpotID kTSA0BNorthRobotsToReadyRoomSpotID = 5029; +static const HotSpotID kTSA0BNorthRobotsToFrontDoorSpotID = 5030; +static const HotSpotID kTSA0BWestMonitorSpotID = 5031; +static const HotSpotID kTSA0BWestMonitorOutSpotID = 5032; +static const HotSpotID kTSA0BWestTheorySpotID = 5033; +static const HotSpotID kTSA0BWestBackgroundSpotID = 5034; +static const HotSpotID kTSA0BWestProcedureSpotID = 5035; +static const HotSpotID kTSA0BWestCloseVideoSpotID = 5036; +static const HotSpotID kTSA0BWestPlayVideoSpotID = 5037; +static const HotSpotID kTSA0BWestRewindVideoSpotID = 5038; +static const HotSpotID kTSA22EastMonitorSpotID = 5039; +static const HotSpotID kTSA22EastKeySpotID = 5040; +static const HotSpotID kTSA23WestMonitorSpotID = 5041; +static const HotSpotID kTSA23WestChipsSpotID = 5042; +static const HotSpotID kTSA34NorthDoorSpotID = 5043; +static const HotSpotID kTSA37NorthJumpToPrehistoricSpotID = 5044; +static const HotSpotID kTSA37NorthJumpToNoradSpotID = 5045; +static const HotSpotID kTSA37NorthCancelNoradSpotID = 5046; +static const HotSpotID kTSA37NorthJumpToMarsSpotID = 5047; +static const HotSpotID kTSA37NorthCancelMarsSpotID = 5048; +static const HotSpotID kTSA37NorthJumpToWSCSpotID = 5049; +static const HotSpotID kTSA37NorthCancelWSCSpotID = 5050; +static const HotSpotID kTSA37NorthExitSpotID = 5051; +static const HotSpotID kTSA37NorthJumpMenuSpotID = 5052; +static const HotSpotID kTSA37NorthNoradMenuSpotID = 5053; +static const HotSpotID kTSA37NorthMarsMenuSpotID = 5054; +static const HotSpotID kTSA37NorthWSCMenuSpotID = 5055; + +// Extra sequence IDs. +static const ExtraID kTSATransporterArrowLoop = 0; +static const ExtraID kTSAArriveFromCaldoria = 1; +static const ExtraID kTSAGTOtherChoice = 2; +static const ExtraID kTSAGTCardSwipe = 3; +static const ExtraID kTSAGTSelectCaldoria = 4; +static const ExtraID kTSAGTGoToCaldoria = 5; +static const ExtraID kTSAGTSelectBeach = 6; +static const ExtraID kTSAGTGoToBeach = 7; +static const ExtraID kTSAGTArriveAtBeach = 8; +static const ExtraID kTSAGTSelectTokyo = 9; +static const ExtraID kTSAGTGoToTokyo = 10; +static const ExtraID kTSAGTArriveAtTokyo = 11; +static const ExtraID kTSA02NorthZoomIn = 12; +static const ExtraID kTSA02NorthTenSecondDoor = 13; +static const ExtraID kTSA02NorthZoomOut = 14; +static const ExtraID kTSA02NorthDoorWithAgent3 = 15; +static const ExtraID kTSA03JimenezZoomIn = 16; +static const ExtraID kTSA03JimenezSpeech = 17; +static const ExtraID kTSA03JimenezZoomOut = 18; +static const ExtraID kTSA03CrenshawZoomIn = 19; +static const ExtraID kTSA03CrenshawSpeech = 20; +static const ExtraID kTSA03CrenshawZoomOut = 21; +static const ExtraID kTSA03SouthRobotDeath = 22; +static const ExtraID kTSA04NorthRobotGreeting = 23; +static const ExtraID kTSA04MatsumotoZoomIn = 24; +static const ExtraID kTSA04MatsumotoSpeech = 25; +static const ExtraID kTSA04MatsumotoZoomOut = 26; +static const ExtraID kTSA04CastilleZoomIn = 27; +static const ExtraID kTSA04CastilleSpeech = 28; +static const ExtraID kTSA04CastilleZoomOut = 29; +static const ExtraID kTSA05SinclairZoomIn = 30; +static const ExtraID kTSA05SinclairSpeech = 31; +static const ExtraID kTSA05SinclairZoomOut = 32; +static const ExtraID kTSA05WhiteZoomIn = 33; +static const ExtraID kTSA05WhiteSpeech = 34; +static const ExtraID kTSA05WhiteZoomOut = 35; +static const ExtraID kTSA0AEastRobot = 36; +static const ExtraID kTSA0AWestRobot = 37; +static const ExtraID kTSA16NorthRobotDeath = 38; +static const ExtraID kTSA0BEastZoomIn = 39; +static const ExtraID kTSA0BEastZoomedView = 40; +static const ExtraID kTSA0BEastZoomOut = 41; +static const ExtraID kTSA0BEastTurnLeft = 42; +static const ExtraID kTSA0BComparisonStartup = 43; +static const ExtraID kTSA0BComparisonView0000 = 44; +static const ExtraID kTSA0BComparisonView0002 = 45; +static const ExtraID kTSA0BComparisonView0020 = 46; +static const ExtraID kTSA0BComparisonView0022 = 47; +static const ExtraID kTSA0BComparisonView0200 = 48; +static const ExtraID kTSA0BComparisonView0202 = 49; +static const ExtraID kTSA0BComparisonView0220 = 50; +static const ExtraID kTSA0BComparisonView0222 = 51; +static const ExtraID kTSA0BComparisonView2000 = 52; +static const ExtraID kTSA0BComparisonView2002 = 53; +static const ExtraID kTSA0BComparisonView2020 = 54; +static const ExtraID kTSA0BComparisonView2022 = 55; +static const ExtraID kTSA0BComparisonView2200 = 56; +static const ExtraID kTSA0BComparisonView2202 = 57; +static const ExtraID kTSA0BComparisonView2220 = 58; +static const ExtraID kTSA0BComparisonView2222 = 59; +static const ExtraID kTSA0BNoradComparisonView = 60; +static const ExtraID kTSA0BNoradUnaltered = 61; +static const ExtraID kTSA0BNoradAltered = 62; +static const ExtraID kTSA0BMarsComparisonView = 63; +static const ExtraID kTSA0BMarsUnaltered = 64; +static const ExtraID kTSA0BMarsAltered = 65; +static const ExtraID kTSA0BWSCComparisonView = 66; +static const ExtraID kTSA0BWSCUnaltered = 67; +static const ExtraID kTSA0BWSCAltered = 68; +static const ExtraID kTSA0BCaldoriaComparisonView = 69; +static const ExtraID kTSA0BCaldoriaUnaltered = 70; +static const ExtraID kTSA0BCaldoriaAltered = 71; +static const ExtraID kTSA0BNorthZoomIn = 72; +static const ExtraID kTSA0BNorthZoomedView = 73; +static const ExtraID kTSA0BNorthZoomOut = 74; +static const ExtraID kTSA0BNorthTurnLeft = 75; +static const ExtraID kTSA0BNorthTurnRight = 76; +static const ExtraID kTSA0BNorthHistLogOpen = 77; +static const ExtraID kTSA0BNorthHistLogClose = 78; +static const ExtraID kTSA0BNorthHistLogCloseWithLog = 79; +static const ExtraID kTSA0BNorthCantChangeHistory = 80; +static const ExtraID kTSA0BNorthYoureBusted = 81; +static const ExtraID kTSA0BNorthFinallyHappened = 82; +static const ExtraID kTSA0BShowRip1 = 83; +static const ExtraID kTSA0BNorthRipView1 = 84; +static const ExtraID kTSA0BShowRip2 = 85; +static const ExtraID kTSA0BShowGuardRobots = 86; +static const ExtraID kTSA0BAIInterruption = 87; +static const ExtraID kTSA0BRobotsToCommandCenter = 88; +static const ExtraID kTSA0BNorthRobotsAtCCView = 89; +static const ExtraID kTSA0BNorthRobotsAtRRView = 90; +static const ExtraID kTSA0BNorthRobotsAtFDView = 91; +static const ExtraID kTSA0BRobotsFromCommandCenterToReadyRoom = 92; +static const ExtraID kTSA0BRobotsFromReadyRoomToCommandCenter = 93; +static const ExtraID kTSA0BRobotsFromCommandCenterToFrontDoor = 94; +static const ExtraID kTSA0BRobotsFromFrontDoorToCommandCenter = 95; +static const ExtraID kTSA0BRobotsFromFrontDoorToReadyRoom = 96; +static const ExtraID kTSA0BRobotsFromReadyRoomToFrontDoor = 97; +static const ExtraID kTSA0BWestZoomIn = 98; +static const ExtraID kTSA0BWestZoomedView = 99; +static const ExtraID kTSA0BWestZoomOut = 100; +static const ExtraID kTSA0BWestTurnRight = 101; +static const ExtraID kTSA0BTBPTheoryHighlight = 102; +static const ExtraID kTSA0BTBPBackgroundHighlight = 103; +static const ExtraID kTSA0BTBPProcedureHighlight = 104; +static const ExtraID kTSA0BTBPTheory = 105; +static const ExtraID kTSA0BTBPBackground = 106; +static const ExtraID kTSA0BTBPProcedure = 107; +static const ExtraID kTSA0BRipAlarmScreen = 108; +static const ExtraID kTSA22RedEastZoomInSequence = 109; +static const ExtraID kTSA22RedEastVaultViewWithKey = 110; +static const ExtraID kTSA22RedEastVaultViewNoKey = 111; +static const ExtraID kTSA23RedWestVaultZoomInSequence = 112; +static const ExtraID kTSA23RedWestVaultViewWithChips = 113; +static const ExtraID kTSA23RedWestVaultViewNoChips = 114; +static const ExtraID kTSA25NorthDeniedNoKey = 115; +static const ExtraID kTSA25NorthDeniedNoChip = 116; +static const ExtraID kTSA25NorthPutOnSuit = 117; +static const ExtraID kTSA25NorthAlreadyHaveSuit = 118; +static const ExtraID kTSA25NorthDescending1 = 119; +static const ExtraID kTSA25NorthDescending2 = 120; +static const ExtraID kTSA37HorseToAI1 = 121; +static const ExtraID kTSA37PegasusAI1 = 122; +static const ExtraID kTSA37AI1ToCommissioner1 = 123; +static const ExtraID kTSA37Commissioner1 = 124; +static const ExtraID kTSA37Commissioner1ToZoom = 125; +static const ExtraID kTSA37ZoomToPrehistoric = 126; +static const ExtraID kTSA37PrehistoricToAI2 = 127; +static const ExtraID kTSA37PegasusAI2 = 128; +static const ExtraID kTSA37AI2ToPrehistoric = 129; +static const ExtraID kTSA37PrehistoricToDepart = 130; +static const ExtraID kTSA37PegasusDepart = 131; +static const ExtraID kTSA37TimeJumpToPegasus = 132; +static const ExtraID kTSA37RecallToDownload = 133; +static const ExtraID kTSA37DownloadToColonel1 = 134; +static const ExtraID kTSA37Colonel1 = 135; +static const ExtraID kTSA37Colonel1ToReviewRequired = 136; +static const ExtraID kTSA37ReviewRequiredToExit = 137; +static const ExtraID kTSA37ExitHilited = 138; +static const ExtraID kTSA37ExitToHorse = 139; +static const ExtraID kTSA37HorseToColonel2 = 140; +static const ExtraID kTSA37Colonel2 = 141; +static const ExtraID kTSA37PegasusAI3 = 142; +static const ExtraID kTSA37AI3ToHorse = 143; +static const ExtraID kTSA37HorseToZoom = 144; +static const ExtraID kTSA37ZoomToMainMenu = 145; +static const ExtraID kTSA37MainMenuToAI4 = 146; +static const ExtraID kTSA37PegasusAI4 = 147; +static const ExtraID kTSA37AI4ToMainMenu = 148; +static const ExtraID kTSA37JumpMenu000 = 149; +static const ExtraID kTSA37JumpMenu001 = 150; +static const ExtraID kTSA37JumpMenu010 = 151; +static const ExtraID kTSA37JumpMenu011 = 152; +static const ExtraID kTSA37JumpMenu100 = 153; +static const ExtraID kTSA37JumpMenu101 = 154; +static const ExtraID kTSA37JumpMenu110 = 155; +static const ExtraID kTSA37JumpMenu111 = 156; +static const ExtraID kTSA37JumpToWSCMenu = 157; +static const ExtraID kTSA37CancelWSC = 158; +static const ExtraID kTSA37JumpToWSC = 159; +static const ExtraID kTSA37WSCToAI5 = 160; +static const ExtraID kTSA37PegasusAI5 = 161; +static const ExtraID kTSA37AI5ToWSC = 162; +static const ExtraID kTSA37WSCToDepart = 163; +static const ExtraID kTSA37JumpToMarsMenu = 164; +static const ExtraID kTSA37CancelMars = 165; +static const ExtraID kTSA37JumpToMars = 166; +static const ExtraID kTSA37MarsToAI6 = 167; +static const ExtraID kTSA37PegasusAI6 = 168; +static const ExtraID kTSA37AI6ToMars = 169; +static const ExtraID kTSA37MarsToDepart = 170; +static const ExtraID kTSA37JumpToNoradMenu = 171; +static const ExtraID kTSA37CancelNorad = 172; +static const ExtraID kTSA37JumpToNorad = 173; +static const ExtraID kTSA37NoradToAI7 = 174; +static const ExtraID kTSA37PegasusAI7 = 175; +static const ExtraID kTSA37AI7ToNorad = 176; +static const ExtraID kTSA37NoradToDepart = 177; +static const ExtraID kTSA37EnvironmentalScan = 178; +static const ExtraID kTSA37DownloadToMainMenu = 179; +static const ExtraID kTSA37DownloadToOpMemReview = 180; +static const ExtraID kTSA37OpMemReviewToMainMenu = 181; +static const ExtraID kTSA37OpMemReviewToAllClear = 182; +static const ExtraID kTSA37AllClearToCongratulations = 183; +static const ExtraID kTSA37Congratulations = 184; +static const ExtraID kTSA37CongratulationsToExit = 185; + +const DisplayOrder kRipTimerOrder = kMonitorLayer; + + +const CoordType kUnresolvedLeft = kNavAreaLeft + 14; +const CoordType kUnresolvedTop = kNavAreaTop + 236; + +const CoordType kResolvedLeft = kNavAreaLeft + 36; +const CoordType kResolvedTop = kNavAreaTop + 236; + +const CoordType kJumpMenuLeft = kNavAreaLeft + 360; +const CoordType kJumpMenuTop = kNavAreaTop + 202; + +const CoordType kJumpMenuHilitedLeft = kNavAreaLeft + 354; +const CoordType kJumpMenuHilitedTop = kNavAreaTop + 196; + +const CoordType kExitLeft = kNavAreaLeft + 360; +const CoordType kExitTop = kNavAreaTop + 216; + +const CoordType kExitHilitedLeft = kNavAreaLeft + 354; +const CoordType kExitHilitedTop = kNavAreaTop + 210; + +const CoordType kRipTimerLeft = kNavAreaLeft + 95; +const CoordType kRipTimerTop = kNavAreaTop + 87; + +const CoordType kTBPCloseLeft = kNavAreaLeft + 30; +const CoordType kTBPCloseTop = kNavAreaTop + 16; + +const CoordType kTBPRewindLeft = kNavAreaLeft + 86; +const CoordType kTBPRewindTop = kNavAreaTop + 218; + +const CoordType kComparisonCloseLeft = kNavAreaLeft + 50; +const CoordType kComparisonCloseTop = kNavAreaTop + 14; + +const CoordType kComparisonLeftRewindLeft = kNavAreaLeft + 96; +const CoordType kComparisonLeftRewindTop = kNavAreaTop + 190; + +const CoordType kComparisonRightRewindLeft = kNavAreaLeft + 282; +const CoordType kComparisonRightRewindTop = kNavAreaTop + 190; + +const CoordType kComparisonHiliteSpriteLeft = kNavAreaLeft + 45; +const CoordType kComparisonHiliteSpriteTop = kNavAreaTop + 65; + +const CoordType kComparisonHiliteNoradLeft = kNavAreaLeft + 45; +const CoordType kComparisonHiliteNoradTop = kNavAreaTop + 65; + +const CoordType kComparisonHiliteMarsLeft = kNavAreaLeft + 45 + 4; +const CoordType kComparisonHiliteMarsTop = kNavAreaTop + 65 + 23; + +const CoordType kComparisonHiliteCaldoriaLeft = kNavAreaLeft + 45 + 7; +const CoordType kComparisonHiliteCaldoriaTop = kNavAreaTop + 65 + 46; + +const CoordType kComparisonHiliteWSCLeft = kNavAreaLeft + 45 + 11; +const CoordType kComparisonHiliteWSCTop = kNavAreaTop + 65 + 68; + +const CoordType kComparisonChancesSpriteLeft = kNavAreaLeft + 148; +const CoordType kComparisonChancesSpriteTop = kNavAreaTop + 162; + +const CoordType kComparisonChancesNoradLeft = kNavAreaLeft + 148; +const CoordType kComparisonChancesNoradTop = kNavAreaTop + 162; + +const CoordType kComparisonChancesMarsLeft = kNavAreaLeft + 148; +const CoordType kComparisonChancesMarsTop = kNavAreaTop + 162; + +const CoordType kComparisonChancesCaldoriaLeft = kNavAreaLeft + 148; +const CoordType kComparisonChancesCaldoriaTop = kNavAreaTop + 162 + 1; + +const CoordType kComparisonChancesWSCLeft = kNavAreaLeft + 148; +const CoordType kComparisonChancesWSCTop = kNavAreaTop + 162; + +const CoordType kRedirectionSprite1Left = kNavAreaLeft + 58; +const CoordType kRedirectionSprite1Top = kNavAreaTop + 16; + +const CoordType kRedirectionSprite2Left = kNavAreaLeft + 36; +const CoordType kRedirectionSprite2Top = kNavAreaTop + 166; + +const CoordType kRedirectionCCRolloverLeft = kNavAreaLeft + 58; +const CoordType kRedirectionCCRolloverTop = kNavAreaTop + 16; + +const CoordType kRedirectionRRRolloverLeft = kNavAreaLeft + 430; +const CoordType kRedirectionRRRolloverTop = kNavAreaTop + 30; + +const CoordType kRedirectionFDRolloverLeft = kNavAreaLeft + 278; +const CoordType kRedirectionFDRolloverTop = kNavAreaTop + 160; + +const CoordType kRedirectionCCDoorLeft = kNavAreaLeft + 174; +const CoordType kRedirectionCCDoorTop = kNavAreaTop + 36; + +const CoordType kRedirectionRRDoorLeft = kNavAreaLeft + 418; +const CoordType kRedirectionRRDoorTop = kNavAreaTop + 32; + +const CoordType kRedirectionFDDoorLeft = kNavAreaLeft + 298; +const CoordType kRedirectionFDDoorTop = kNavAreaTop + 240; + +const CoordType kRedirectionSecuredLeft = kNavAreaLeft + 36; +const CoordType kRedirectionSecuredTop = kNavAreaTop + 166; + +const CoordType kRedirectionNewTargetLeft = kNavAreaLeft + 36; +const CoordType kRedirectionNewTargetTop = kNavAreaTop + 166; + +const CoordType kRedirectionCloseLeft = kNavAreaLeft + 56; +const CoordType kRedirectionCloseTop = kNavAreaTop + 220; + +static const TimeValue kTSABumpIntoWallIn = 0; +static const TimeValue kTSABumpIntoWallOut = 148; + +static const TimeValue kTSAGTDoorCloseIn = 148; +static const TimeValue kTSAGTDoorCloseOut = 1570; + +static const TimeValue kTSANoOtherDestinationIn = 1570; +static const TimeValue kTSANoOtherDestinationOut = 3601; + +static const TimeValue kTSAEntryDoorCloseIn = 3601; +static const TimeValue kTSAEntryDoorCloseOut = 4200; + +static const TimeValue kTSAInsideDoorCloseIn = 4200; +static const TimeValue kTSAInsideDoorCloseOut = 4800; + +static const TimeValue kTSAVaultCloseIn = 4800; +static const TimeValue kTSAVaultCloseOut = 5388; + +static const TimeValue kTSAPegasusDoorCloseIn = 5388; +static const TimeValue kTSAPegasusDoorCloseOut = 6457; + +static const bool kPegasusUnresolved = false; +static const bool kPegasusResolved = true; +static const bool kPegasusCantExit = false; +static const bool kPegasusCanExit = true; + +// Monitor modes +enum { + kMonitorNeutral = 0, + kMonitorTheory = 1, + kMonitorProcedure = 2, + kMonitorBackground = 3, + kMonitorNoradComparison = 4, + kMonitorMarsComparison = 5, + kMonitorCaldoriaComparison = 6, + kMonitorWSCComparison = 7, + + kRawModeMask = 0x0F, + kPlayingTBPMask = 0x10, + kPlayingLeftComparisonMask = 0x20, + kPlayingRightComparisonMask = 0x40, + + kPlayingAnyMask = kPlayingTBPMask | + kPlayingLeftComparisonMask | + kPlayingRightComparisonMask, + + kMonitorPlayingTheory = kMonitorTheory | kPlayingTBPMask, + kMonitorPlayingProcedure = kMonitorProcedure | kPlayingTBPMask, + kMonitorPlayingBackground = kMonitorBackground | kPlayingTBPMask, + + kMonitorPlayingLeftNoradComparison = kMonitorNoradComparison | + kPlayingLeftComparisonMask, + kMonitorPlayingRightNoradComparison = kMonitorNoradComparison | + kPlayingRightComparisonMask, + kMonitorPlayingLeftMarsComparison = kMonitorMarsComparison | + kPlayingLeftComparisonMask, + kMonitorPlayingRightMarsComparison = kMonitorMarsComparison | + kPlayingRightComparisonMask, + kMonitorPlayingLeftCaldoriaComparison = kMonitorCaldoriaComparison | + kPlayingLeftComparisonMask, + kMonitorPlayingRightCaldoriaComparison = kMonitorCaldoriaComparison | + kPlayingRightComparisonMask, + kMonitorPlayingLeftWSCComparison = kMonitorWSCComparison | + kPlayingLeftComparisonMask, + kMonitorPlayingRightWSCComparison = kMonitorWSCComparison | + kPlayingRightComparisonMask +}; + +static const ExtraID s_historicalLogViews[16] = { + kTSA0BComparisonView0000, + kTSA0BComparisonView0002, + kTSA0BComparisonView0020, + kTSA0BComparisonView0022, + kTSA0BComparisonView0200, + kTSA0BComparisonView0202, + kTSA0BComparisonView0220, + kTSA0BComparisonView0222, + kTSA0BComparisonView2000, + kTSA0BComparisonView2002, + kTSA0BComparisonView2020, + kTSA0BComparisonView2022, + kTSA0BComparisonView2200, + kTSA0BComparisonView2202, + kTSA0BComparisonView2220, + kTSA0BComparisonView2222 +}; + +static const int kRedirectionCCRolloverSprite = 0; +static const int kRedirectionRRRolloverSprite = 1; +static const int kRedirectionFDRolloverSprite = 2; +static const int kRedirectionCCDoorSprite = 3; +static const int kRedirectionRRDoorSprite = 4; +static const int kRedirectionFDDoorSprite = 5; +static const int kRedirectionCloseSprite = 6; +static const int kRedirectionSecuredSprite = 0; +static const int kRedirectionNewTargetSprite = 1; + +void RipTimer::initImage() { + _middle = -1; + + _timerImage.getImageFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kLeftRipPICTID); + + Common::Rect r; + _timerImage.getSurfaceBounds(r); + setBounds(r); +} + +void RipTimer::releaseImage() { + _timerImage.deallocateSurface(); +} + +void RipTimer::draw(const Common::Rect &updateRect) { + Common::Rect bounds; + getBounds(bounds); + + Common::Rect r1 = bounds; + r1.right = _middle; + r1 = updateRect.findIntersectingRect(r1); + + if (!r1.isEmpty()) { + Common::Rect r2 = r1; + r2.moveTo(r1.left - _bounds.left, r1.top - bounds.top); + _timerImage.copyToCurrentPort(r2, r1); + } +} + +void RipTimer::timeChanged(const TimeValue newTime) { + // WORKAROUND: If the timer isn't running, don't run the following code. + // Fixes use of the code when it shouldn't be running (since this is an + // IdlerAnimation, this is called on useIdleTime() but this specific + // timer only makes sense when used as an actual timer). + if (!isRunning()) + return; + + Common::Rect bounds; + getBounds(bounds); + + CoordType newMiddle = bounds.left + bounds.width() * newTime / getDuration(); + + if (newMiddle != _middle) { + _middle = newMiddle; + triggerRedraw(); + } + + if (newTime == getStop()) + ((PegasusEngine *)g_engine)->die(kDeathUncreatedInTSA); +} + +FullTSA::FullTSA(InputHandler *nextHandler, PegasusEngine *owner) : Neighborhood(nextHandler, owner, "Full TSA", kFullTSAID), + _ripTimer(kNoDisplayElement), _sprite1(kNoDisplayElement), _sprite2(kNoDisplayElement), _sprite3(kNoDisplayElement) { + setIsItemTaken(kJourneymanKey); + setIsItemTaken(kPegasusBiochip); + setIsItemTaken(kMapBiochip); +} + +void FullTSA::init() { + Neighborhood::init(); + _ripTimer.setDisplayOrder(kRipTimerOrder); + _ripTimer.startDisplaying(); + + if (!GameState.getTSASeenRobotGreeting()) + forceStridingStop(kTSA03, kNorth, kNoAlternateID); + + _sprite1.setDisplayOrder(kMonitorLayer); + _sprite1.startDisplaying(); + _sprite2.setDisplayOrder(kMonitorLayer); + _sprite2.startDisplaying(); + _sprite3.setDisplayOrder(kMonitorLayer); + _sprite3.startDisplaying(); + + // Fix a mistake in the world builder tables. + HotspotInfoTable::Entry *entry = findHotspotEntry(kTSA23WestChipsSpotID); + entry->hotspotItem = kPegasusBiochip; +} + +void FullTSA::dieUncreatedInTSA() { + die(kDeathUncreatedInTSA); +} + +void FullTSA::start() { + g_energyMonitor->stopEnergyDraining(); + + if (!GameState.getScoringEnterTSA()) { + _utilityFuse.primeFuse(GameState.getTSAFuseTimeLimit()); + _utilityFuse.setFunctor(new Common::Functor0Mem<void, FullTSA>(this, &FullTSA::dieUncreatedInTSA)); + _utilityFuse.lightFuse(); + } else if (GameState.getTSAState() == kTSAPlayerDetectedRip || GameState.getTSAState() == kTSAPlayerNeedsHistoricalLog) { + _ripTimer.initImage(); + _ripTimer.moveElementTo(kRipTimerLeft, kRipTimerTop); + _ripTimer.setSegment(0, kRipTimeLimit, kRipTimeScale); + _ripTimer.setTime(GameState.getRipTimerTime()); + _ripTimer.start(); + } + + Neighborhood::start(); +} + +void FullTSA::flushGameState() { + GameState.setRipTimerTime(_ripTimer.getTime()); + GameState.setTSAFuseTimeLimit(_utilityFuse.getTimeRemaining()); +} + +Common::String FullTSA::getBriefingMovie() { + Common::String movieName = Neighborhood::getBriefingMovie(); + + if (movieName.empty()) { + RoomID room = GameState.getCurrentRoom(); + + switch (GameState.getTSAState()) { + case kTSAPlayerNotArrived: + case kTSAPlayerForcedReview: + if (room >= kTSA16 && room <= kTSA0B) + return "Images/AI/TSA/XT01A"; + + return "Images/AI/TSA/XT01"; + case kTSAPlayerDetectedRip: + case kTSAPlayerNeedsHistoricalLog: + return "Images/AI/TSA/XT02"; + case kTSAPlayerGotHistoricalLog: + case kTSAPlayerInstalledHistoricalLog: + return "Images/AI/TSA/XT03"; + default: + switch (getCurrentActivation()) { + case kActivationJumpToPrehistoric: + g_AIChip->showBriefingClicked(); + startExtraSequenceSync(kTSA37PegasusAI2, kHintInterruption); + startExtraSequenceSync(kTSA37AI2ToPrehistoric, kFilterNoInput); + g_AIChip->clearClicked(); + break; + case kActivationJumpToNorad: + g_AIChip->showBriefingClicked(); + startExtraSequenceSync(kTSA37PegasusAI7, kHintInterruption); + startExtraSequenceSync(kTSA37AI7ToNorad, kFilterNoInput); + g_AIChip->clearClicked(); + break; + case kActivationJumpToMars: + g_AIChip->showBriefingClicked(); + startExtraSequenceSync(kTSA37PegasusAI6, kHintInterruption); + startExtraSequenceSync(kTSA37AI6ToMars, kFilterNoInput); + g_AIChip->clearClicked(); + break; + case kActivationJumpToWSC: + g_AIChip->showBriefingClicked(); + startExtraSequenceSync(kTSA37PegasusAI5, kHintInterruption); + startExtraSequenceSync(kTSA37AI5ToWSC, kFilterNoInput); + g_AIChip->clearClicked(); + break; + default: + if (GameState.allTimeZonesFinished()) + return "Images/AI/TSA/XT05"; + + return "Images/AI/TSA/XT04"; + } + break; + } + } + + return movieName; +} + +Common::String FullTSA::getEnvScanMovie() { + Common::String movieName = Neighborhood::getEnvScanMovie(); + + if (movieName.empty()) { + switch (GameState.getTSAState()) { + case kTSAPlayerNotArrived: + case kTSAPlayerForcedReview: + case kTSAPlayerDetectedRip: + case kTSAPlayerNeedsHistoricalLog: + return "Images/AI/TSA/XTE1"; + default: + if (GameState.getCurrentRoom() == kTSA37) { + g_AIChip->showEnvScanClicked(); + startExtraSequenceSync(kTSA37EnvironmentalScan, kHintInterruption); + + switch (getCurrentActivation()) { + case kActivationJumpToPrehistoric: + startExtraSequenceSync(kTSA37AI2ToPrehistoric, kFilterNoInput); + break; + case kActivationJumpToNorad: + startExtraSequenceSync(kTSA37AI7ToNorad, kFilterNoInput); + showExtraView(kTSA37JumpToNoradMenu); + break; + case kActivationJumpToMars: + startExtraSequenceSync(kTSA37AI6ToMars, kFilterNoInput); + showExtraView(kTSA37JumpToMarsMenu); + break; + case kActivationJumpToWSC: + startExtraSequenceSync(kTSA37AI5ToWSC, kFilterNoInput); + showExtraView(kTSA37JumpToWSCMenu); + break; + default: + startExtraSequenceSync(kTSA37AI4ToMainMenu, kFilterNoInput); + break; + } + + g_AIChip->clearClicked(); + } else if (GameState.allTimeZonesFinished()) { + return "Images/AI/TSA/XTE1"; + } else { + return "Images/AI/TSA/XTE2"; + } + break; + } + } + + return movieName; +} + +uint FullTSA::getNumHints() { + uint numHints = Neighborhood::getNumHints(); + + if (numHints == 0) { + switch (GameState.getTSAState()) { + case kRobotsAtCommandCenter: + case kRobotsAtFrontDoor: + case kRobotsAtReadyRoom: + if (GameState.getCurrentRoom() == kTSA0B && GameState.getTSA0BZoomedIn()) + numHints = 3; + break; + } + } + + return numHints; +} + +Common::String FullTSA::getHintMovie(uint hintNum) { + Common::String movieName = Neighborhood::getHintMovie(hintNum); + + if (movieName.empty()) + movieName = Common::String::format("Images/AI/TSA/XT20NH%d", hintNum); + + return movieName; +} + +void FullTSA::loadAmbientLoops() { + RoomID room = GameState.getCurrentRoom(); + + switch (GameState.getTSAState()) { + case kTSAPlayerDetectedRip: + case kTSAPlayerNeedsHistoricalLog: + if ((room >= kTSA16 && room <= kTSA0B) || (room >= kTSA21Cyan && room <= kTSA24Cyan) || (room >= kTSA21Red && room <= kTSA24Red)) + loadLoopSound1("Sounds/TSA/TSA CLAXON.22K.AIFF", 0x100 / 4, 0, 0); + else if (room == kTSA25Cyan || room == kTSA25Red) + loadLoopSound1("Sounds/TSA/TSA CLAXON.22K.AIFF", 0x100 / 6, 0, 0); + else + loadLoopSound1("Sounds/TSA/TSA EchoClaxon.22K.AIFF", 0x100 / 4, 0, 0); + break; + default: + if (room >= kTSA00 && room <= kTSA02) + loadLoopSound1("Sounds/TSA/T01NAE.NEW.22K.AIFF"); + else if (room >= kTSA03 && room <= kTSA15) + loadLoopSound1("Sounds/TSA/T01NAE.NEW.22K.AIFF"); + else if (room >= kTSA16 && room <= kTSA0B) + loadLoopSound1("Sounds/TSA/T14SAEO1.22K.AIFF"); + else if (room >= kTSA21Cyan && room <= kTSA25Red) + loadLoopSound1("Sounds/TSA/T15SAE01.22K.AIFF"); + else if (room >= kTSA26 && room <= kTSA37) + loadLoopSound1("Sounds/TSA/T01NAE.NEW.22K.AIFF"); + break; + } +} + +short FullTSA::getStaticCompassAngle(const RoomID room, const DirectionConstant dir) { + int16 result = Neighborhood::getStaticCompassAngle(room, dir); + + switch (room) { + case kTSA08: + result += kCompassShift; + break; + case kTSA09: + result -= kCompassShift; + break; + case kTSA10: + result += kCompassShift * 2; + break; + case kTSA11: + case kTSA22Cyan: + case kTSA22Red: + result -= kCompassShift * 2; + break; + case kTSA12: + result += kCompassShift * 3; + break; + case kTSA13: + result -= kCompassShift * 3; + break; + case kTSA14: + case kTSA16: + case kTSA17: + case kTSA18: + case kTSA19: + result += kCompassShift * 4; + break; + case kTSA0B: + result += kCompassShift * 4; + + if (dir == kWest) + result += 30; + else if (dir == kEast) + result -= 30; + break; + case kTSA33: + result += kCompassShift * 4; + break; + case kTSA15: + case kTSA21Cyan: + case kTSA24Cyan: + case kTSA25Cyan: + case kTSA21Red: + case kTSA24Red: + case kTSA25Red: + case kTSA26: + case kTSA27: + case kTSA28: + case kTSA29: + case kTSA30: + result -= kCompassShift * 4; + break; + case kTSA23Cyan: + case kTSA23Red: + result -= kCompassShift * 6; + break; + case kTSA32: + result -= kCompassShift * 8; + break; + case kTSA34: + result -= kCompassShift * 12; + break; + case kTSA35: + result += kCompassShift * 8; + break; + case kTSA37: + result -= kCompassShift * 2; + break; + } + + return result; +} + +void FullTSA::getExitCompassMove(const ExitTable::Entry &exitEntry, FaderMoveSpec &compassMove) { + Neighborhood::getExitCompassMove(exitEntry, compassMove); + + switch (MakeRoomView(exitEntry.room, exitEntry.direction)) { + case MakeRoomView(kTSA01, kSouth): + compassMove.insertFaderKnot(exitEntry.movieStart, -180); + compassMove.insertFaderKnot(exitEntry.movieStart + kFullTSAFrameDuration * 3, -180); + compassMove.insertFaderKnot(exitEntry.movieStart + kFullTSAFrameDuration * 33, + getStaticCompassAngle(exitEntry.exitRoom, exitEntry.exitDirection)); + break; + case MakeRoomView(kTSA11, kEast): + if (getCurrentAlternate() == kAltTSARobotsAtReadyRoom) { + compassMove.makeTwoKnotFaderSpec(kFullTSAMovieScale, exitEntry.movieStart, + getStaticCompassAngle(kTSA11, kEast), exitEntry.movieEnd, + getStaticCompassAngle(kTSA13, kEast)); + compassMove.insertFaderKnot(exitEntry.movieStart + kFullTSAFrameDuration * 13, compassMove.getNthKnotValue(1)); + } + break; + case MakeRoomView(kTSA34, kNorth): + compassMove.insertFaderKnot(exitEntry.movieStart + kFullTSAFrameDuration * 48, + getStaticCompassAngle(exitEntry.room, exitEntry.direction)); + compassMove.insertFaderKnot(exitEntry.movieStart + kFullTSAFrameDuration * 68, + getStaticCompassAngle(exitEntry.exitRoom, exitEntry.exitDirection)); + break; + case MakeRoomView(kTSA37, kNorth): + compassMove.insertFaderKnot(exitEntry.movieStart + kFullTSAFrameDuration * 38, + getStaticCompassAngle(exitEntry.room, exitEntry.direction)); + compassMove.insertFaderKnot(exitEntry.movieStart + kFullTSAFrameDuration * 64, + getStaticCompassAngle(exitEntry.room, exitEntry.direction) + kCompassShift * 3 / 2); + compassMove.insertFaderKnot(exitEntry.movieStart + kFullTSAFrameDuration * 105, + getStaticCompassAngle(exitEntry.exitRoom, exitEntry.exitDirection)); + break; + } +} + +void FullTSA::getExtraCompassMove(const ExtraTable::Entry &extraEntry, FaderMoveSpec &compassMove) { + int16 angle; + + switch (extraEntry.extra) { + case kTSA0BEastTurnLeft: + case kTSA0BNorthTurnLeft: + angle =getStaticCompassAngle(GameState.getCurrentRoom(), GameState.getCurrentDirection()); + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), extraEntry.movieStart, angle, + extraEntry.movieEnd, angle - 60); + break; + case kTSA0BNorthTurnRight: + case kTSA0BWestTurnRight: + angle = getStaticCompassAngle(GameState.getCurrentRoom(), GameState.getCurrentDirection()); + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), extraEntry.movieStart, angle, + extraEntry.movieEnd, angle + 60); + break; + case kTSA22RedEastZoomInSequence: + angle = getStaticCompassAngle(GameState.getCurrentRoom(), GameState.getCurrentDirection()); + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), extraEntry.movieStart, angle, + extraEntry.movieEnd, angle); + compassMove.insertFaderKnot(extraEntry.movieStart + 1200, angle - kCompassShift * 2); + compassMove.insertFaderKnot(extraEntry.movieStart + 8160, angle - kCompassShift * 2); + compassMove.insertFaderKnot(extraEntry.movieStart + 9840, angle); + break; + case kTSA23RedWestVaultZoomInSequence: + angle = getStaticCompassAngle(GameState.getCurrentRoom(), GameState.getCurrentDirection()); + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), extraEntry.movieStart, angle, + extraEntry.movieEnd, angle); + compassMove.insertFaderKnot(extraEntry.movieStart + 1200, angle - kCompassShift * 2); + compassMove.insertFaderKnot(extraEntry.movieStart + 10100, angle - kCompassShift * 2); + compassMove.insertFaderKnot(extraEntry.movieStart + 11880, angle); + break; + default: + Neighborhood::getExtraCompassMove(extraEntry, compassMove); + break; + } +} + +uint16 FullTSA::getDateResID() const { + return kDate2318ID; +} + +TimeValue FullTSA::getViewTime(const RoomID room, const DirectionConstant direction) { + ExtraID extraID = 0xffffffff; + + switch (MakeRoomView(room, direction)) { + case MakeRoomView(kTSA0B, kEast): + if (GameState.getTSA0BZoomedIn()) + switch (GameState.getTSAState()) { + case kTSAPlayerInstalledHistoricalLog: + case kTSABossSawHistoricalLog: + case kRobotsAtCommandCenter: + case kRobotsAtFrontDoor: + case kRobotsAtReadyRoom: + extraID = s_historicalLogViews[getHistoricalLogIndex()]; + break; + default: + extraID = kTSA0BEastZoomedView; + break; + } + break; + case MakeRoomView(kTSA0B, kNorth): + if (GameState.getTSA0BZoomedIn()) + switch (GameState.getTSAState()) { + case kTSAPlayerNeedsHistoricalLog: + extraID = kTSA0BNorthRipView1; + break; + default: + extraID = kTSA0BNorthZoomedView; + break; + } + break; + case MakeRoomView(kTSA0B, kWest): + if (GameState.getTSA0BZoomedIn()) + extraID = kTSA0BWestZoomedView; + break; + case MakeRoomView(kTSA22Red, kEast): + if (_privateFlags.getFlag(kTSAPrivateKeyVaultOpenFlag)) { + if (_vm->itemInLocation(kJourneymanKey, kFullTSAID, kTSA22Red, kEast)) + extraID = kTSA22RedEastVaultViewWithKey; + else + extraID = kTSA22RedEastVaultViewNoKey; + } + break; + case MakeRoomView(kTSA23Red, kWest): + if (_privateFlags.getFlag(kTSAPrivateChipVaultOpenFlag)) { + if (_vm->itemInLocation(kPegasusBiochip, kFullTSAID, kTSA23Red, kWest)) + extraID = kTSA23RedWestVaultViewWithChips; + else + extraID = kTSA23RedWestVaultViewNoChips; + } + break; + case MakeRoomView(kTSA37, kNorth): + switch (GameState.getTSAState()) { + case kTSAPlayerGotHistoricalLog: + extraID = kTSA37ReviewRequiredToExit; + break; + case kPlayerFinishedWithTSA: + extraID = kTSA37CongratulationsToExit; + break; + default: + extraID = kTSA37AI3ToHorse; + break; + } + break; + } + + if (extraID != 0xffffffff) { + ExtraTable::Entry entry; + getExtraEntry(extraID, entry); + return entry.movieEnd - 1; + } + + return Neighborhood::getViewTime(room, direction); +} + +void FullTSA::findSpotEntry(const RoomID room, const DirectionConstant direction, SpotFlags flags, SpotTable::Entry &entry) { + switch (MakeRoomView(room, direction)) { + case MakeRoomView(kTSA0B, kNorth): + case MakeRoomView(kTSA0B, kEast): + case MakeRoomView(kTSA0B, kWest): + if (!GameState.getTSA0BZoomedIn()) + Neighborhood::findSpotEntry(room, direction, flags, entry); + break; + default: + Neighborhood::findSpotEntry(room, direction, flags, entry); + break; + } +} + +void FullTSA::getExtraEntry(const uint32 id, ExtraTable::Entry &extraEntry) { + Neighborhood::getExtraEntry(id, extraEntry); + + if (id == kTSA0BShowGuardRobots) + extraEntry.movieStart += kFullTSAFrameDuration * 3; +} + +void FullTSA::pickedUpItem(Item *item) { + BiochipItem *biochip; + + switch (item->getObjectID()) { + case kJourneymanKey: + GameState.setScoringGotJourneymanKey(true); + break; + case kPegasusBiochip: + biochip = (BiochipItem *)_vm->getAllItems().findItemByID(kMapBiochip); + _vm->addItemToBiochips(biochip); + GameState.setScoringGotPegasusBiochip(true); + break; + } +} + +void FullTSA::playExtraMovie(const ExtraTable::Entry &extraEntry, const NotificationFlags flags, const InputBits interruptionInput) { + switch (extraEntry.extra) { + case kTSA0BNorthZoomIn: + if (_privateFlags.getFlag(kTSAPrivateLogReaderOpenFlag)) { + _privateFlags.setFlag(kTSAPrivateLogReaderOpenFlag, false); + requestExtraSequence(kTSA0BNorthHistLogClose, 0, kFilterNoInput); + requestExtraSequence(kTSA0BNorthZoomIn, kExtraCompletedFlag, kFilterNoInput); + } else { + Neighborhood::playExtraMovie(extraEntry, flags, interruptionInput); + } + break; + case kTSA0BNorthZoomOut: + if (_ripTimer.isVisible()) + _ripTimer.hide(); + + shutDownRobotMonitor(); + Neighborhood::playExtraMovie(extraEntry, flags, interruptionInput); + break; + case kTSA0BEastZoomOut: + shutDownComparisonMonitor(); + Neighborhood::playExtraMovie(extraEntry, flags, interruptionInput); + break; + default: + Neighborhood::playExtraMovie(extraEntry, flags, interruptionInput); + break; + } +} + +void FullTSA::startDoorOpenMovie(const TimeValue startTime, const TimeValue stopTime) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kTSA00, kNorth): + if (GameState.getLastNeighborhood() != kFullTSAID) { + startExtraSequence(kTSAArriveFromCaldoria, kDoorOpenCompletedFlag, kFilterNoInput); + return; + } + break; + case MakeRoomView(kTSA02, kNorth): + if (!GameState.getTSAIDedAtDoor()) { + GameState.setTSAIDedAtDoor(true); + requestExtraSequence(kTSA02NorthZoomIn, 0, kFilterNoInput); + requestExtraSequence(kTSA02NorthTenSecondDoor, 0, kFilterNoInput); + + if (GameState.getTSASeenAgent3AtDoor()) { + requestExtraSequence(kTSA02NorthZoomOut, kExtraCompletedFlag, kFilterNoInput); + } else { + GameState.setTSASeenAgent3AtDoor(true); + requestExtraSequence(kTSA02NorthZoomOut, 0, kFilterNoInput); + requestExtraSequence(kTSA02NorthDoorWithAgent3, kDoorOpenCompletedFlag, kFilterNoInput); + } + return; + } + break; + case MakeRoomView(kTSA03, kSouth): + if (GameState.getTSAState() == kRobotsAtFrontDoor) { + playDeathExtra(kTSA03SouthRobotDeath, kDeathShotByTSARobots); + return; + } + break; + case MakeRoomView(kTSA16, kNorth): + if (GameState.getTSAState() == kRobotsAtCommandCenter) { + playDeathExtra(kTSA16NorthRobotDeath, kDeathShotByTSARobots); + return; + } + break; + } + + Neighborhood::startDoorOpenMovie(startTime, stopTime); +} + +InputBits FullTSA::getInputFilter() { + InputBits result = Neighborhood::getInputFilter(); + + switch (GameState.getCurrentRoom()) { + case kTSA0B: + if (GameState.getT0BMonitorMode() != kMonitorNeutral) + // Only allow a click. + result &= JMPPPInput::getClickInputFilter(); + break; + case kTSA37: + // Can't move forward in Pegasus. Only press the exit button. + result &= ~(kFilterUpButton | kFilterUpAuto); + break; + } + + return result; +} + +void FullTSA::turnLeft() { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kTSA15, kNorth): + if (GameState.getTSAState() == kTSAPlayerNeedsHistoricalLog) + setCurrentAlternate(kAltTSANormal); + break; + case MakeRoomView(kTSA0B, kNorth): + if (_ripTimer.isVisible()) + _ripTimer.hide(); + releaseSprites(); + break; + case MakeRoomView(kTSA0B, kEast): + shutDownComparisonMonitor(); + break; + } + + Neighborhood::turnLeft(); +} + +void FullTSA::turnRight() { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kTSA15, kSouth): + if (GameState.getTSAState() == kTSAPlayerNeedsHistoricalLog) + setCurrentAlternate(kAltTSANormal); + break; + case MakeRoomView(kTSA0B, kNorth): + if (_ripTimer.isVisible()) + _ripTimer.hide(); + releaseSprites(); + break; + case MakeRoomView(kTSA0B, kEast): + shutDownComparisonMonitor(); + break; + } + + Neighborhood::turnRight(); +} + +void FullTSA::openDoor() { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kTSA15, kSouth): + if (GameState.getTSAState() == kTSAPlayerNeedsHistoricalLog || GameState.getTSAState() == kRobotsAtFrontDoor) + setCurrentAlternate(kAltTSARedAlert); + break; + } + + Neighborhood::openDoor(); +} + +CanMoveForwardReason FullTSA::canMoveForward(ExitTable::Entry &entry) { + if (GameState.getCurrentRoomAndView() == MakeRoomView(kTSA25Red, kNorth)) + return kCantMoveBlocked; + + return Neighborhood::canMoveForward(entry); +} + +CanOpenDoorReason FullTSA::canOpenDoor(DoorTable::Entry &entry) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kTSA02, kNorth): + if (!GameState.getTSAFrontDoorUnlockedOutside()) + return kCantOpenLocked; + break; + case MakeRoomView(kTSA03, kSouth): + if (!GameState.getTSAFrontDoorUnlockedInside()) + return kCantOpenLocked; + break; + case MakeRoomView(kTSA16, kNorth): + if (GameState.getTSACommandCenterLocked()) + return kCantOpenLocked; + break; + } + + return Neighborhood::canOpenDoor(entry); +} + +void FullTSA::bumpIntoWall() { + requestSpotSound(kTSABumpIntoWallIn, kTSABumpIntoWallOut, kFilterAllInput, 0); + Neighborhood::bumpIntoWall(); +} + +void FullTSA::downButton(const Input &input) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kTSA0B, kEast): + if (GameState.getTSA0BZoomedIn()) + startExtraSequence(kTSA0BEastZoomOut, kExtraCompletedFlag, kFilterNoInput); + break; + case MakeRoomView(kTSA0B, kNorth): + if (GameState.getTSA0BZoomedIn()) + startExtraSequence(kTSA0BNorthZoomOut, kExtraCompletedFlag, kFilterNoInput); + break; + case MakeRoomView(kTSA0B, kWest): + if (GameState.getTSA0BZoomedIn() && GameState.getT0BMonitorMode() == kMonitorNeutral) + startExtraSequence(kTSA0BWestZoomOut, kExtraCompletedFlag, kFilterNoInput); + break; + default: + Neighborhood::downButton(input); + } +} + +void FullTSA::activateOneHotspot(HotspotInfoTable::Entry &entry, Hotspot *spot) { + switch (spot->getObjectID()) { + case kTSA0BEastLeftRewindSpotID: + case kTSA0BEastLeftPlaySpotID: + if (_privateFlags.getFlag(kTSAPrivatePlayingRightComparisonFlag)) + spot->setInactive(); + else + Neighborhood::activateOneHotspot(entry, spot); + break; + case kTSA0BEastRightRewindSpotID: + case kTSA0BEastRightPlaySpotID: + if (_privateFlags.getFlag(kTSAPrivatePlayingLeftComparisonFlag)) + spot->setInactive(); + else + Neighborhood::activateOneHotspot(entry, spot); + break; + default: + Neighborhood::activateOneHotspot(entry, spot); + break; + } +} + +void FullTSA::activateHotspots() { + Neighborhood::activateHotspots(); + + switch (MakeRoomView(GameState.getCurrentRoom(), GameState.getCurrentDirection())) { + case MakeRoomView(kTSA02, kNorth): + if (!GameState.getTSAFrontDoorUnlockedOutside()) + _vm->getAllHotspots().activateOneHotspot(kTSA02DoorSpotID); + break; + case MakeRoomView(kTSA0B, kEast): + if (GameState.getTSA0BZoomedIn()) + switch (GameState.getTSAState()) { + case kTSAPlayerInstalledHistoricalLog: + case kTSABossSawHistoricalLog: + case kRobotsAtCommandCenter: + case kRobotsAtFrontDoor: + case kRobotsAtReadyRoom: + if (getCurrentActivation() != kActivateTSA0BComparisonVideo) { + _vm->getAllHotspots().activateOneHotspot(kTSA0BEastCompareNoradSpotID); + _vm->getAllHotspots().activateOneHotspot(kTSA0BEastCompareMarsSpotID); + _vm->getAllHotspots().activateOneHotspot(kTSA0BEastCompareCaldoriaSpotID); + _vm->getAllHotspots().activateOneHotspot(kTSA0BEastCompareWSCSpotID); + } + break; + } + break; + case MakeRoomView(kTSA0B, kNorth): + if (GameState.getTSA0BZoomedIn()) + switch (GameState.getTSAState()) { + case kRobotsAtCommandCenter: + case kRobotsAtFrontDoor: + case kRobotsAtReadyRoom: + _vm->getAllHotspots().activateOneHotspot(kTSA0BNorthRobotsToCommandCenterSpotID); + _vm->getAllHotspots().activateOneHotspot(kTSA0BNorthRobotsToReadyRoomSpotID); + _vm->getAllHotspots().activateOneHotspot(kTSA0BNorthRobotsToFrontDoorSpotID); + break; + } + break; + } +} + +void FullTSA::clickInHotspot(const Input &input, const Hotspot *clickedSpot) { + switch (clickedSpot->getObjectID()) { + case kTSAGTOtherSpotID: + showExtraView(kTSAGTOtherChoice); + playSpotSoundSync(kTSANoOtherDestinationIn, kTSANoOtherDestinationOut); + showExtraView(kTSAGTCardSwipe); + break; + case kTSA02DoorSpotID: + GameState.setTSAFrontDoorUnlockedOutside(true); + Neighborhood::clickInHotspot(input, clickedSpot); + break; + case kTSA03EastJimenezSpotID: + startExtraLongSequence(kTSA03JimenezZoomIn, kTSA03JimenezZoomOut, kExtraCompletedFlag, kFilterNoInput); + break; + case kTSA03WestCrenshawSpotID: + startExtraLongSequence(kTSA03CrenshawZoomIn, kTSA03CrenshawZoomOut, kExtraCompletedFlag, kFilterNoInput); + break; + case kTSA04EastMatsumotoSpotID: + startExtraLongSequence(kTSA04MatsumotoZoomIn, kTSA04MatsumotoZoomOut, kExtraCompletedFlag, kFilterNoInput); + break; + case kTSA04WestCastilleSpotID: + startExtraLongSequence(kTSA04CastilleZoomIn, kTSA04CastilleZoomOut, kExtraCompletedFlag, kFilterNoInput); + break; + case kTSA05EastSinclairSpotID: + startExtraLongSequence(kTSA05SinclairZoomIn, kTSA05SinclairZoomOut, kExtraCompletedFlag, kFilterNoInput); + break; + case kTSA05WestWhiteSpotID: + startExtraLongSequence(kTSA05WhiteZoomIn, kTSA05WhiteZoomOut, kExtraCompletedFlag, kFilterNoInput); + break; + case kTSA0BEastCompareNoradSpotID: + initializeComparisonMonitor(kMonitorNoradComparison, kTSA0BNoradComparisonView); + break; + case kTSA0BEastCompareMarsSpotID: + initializeComparisonMonitor(kMonitorMarsComparison, kTSA0BMarsComparisonView); + break; + case kTSA0BEastCompareCaldoriaSpotID: + initializeComparisonMonitor(kMonitorCaldoriaComparison, kTSA0BCaldoriaComparisonView); + break; + case kTSA0BEastCompareWSCSpotID: + initializeComparisonMonitor(kMonitorWSCComparison, kTSA0BWSCComparisonView); + break; + case kTSA0BEastCloseVideoSpotID: + _navMovie.stop(); + _sprite3.show(); + _vm->delayShell(1, 2); + _sprite3.hide(); + initializeComparisonMonitor(kMonitorNeutral, 0); + break; + case kTSA0BEastLeftPlaySpotID: + playLeftComparison(); + break; + case kTSA0BEastRightPlaySpotID: + playRightComparison(); + break; + + // Command center + case kTSA0BWestTheorySpotID: + initializeTBPMonitor(kMonitorTheory, kTSA0BTBPTheoryHighlight); + break; + case kTSA0BWestBackgroundSpotID: + initializeTBPMonitor(kMonitorBackground, kTSA0BTBPBackgroundHighlight); + break; + case kTSA0BWestProcedureSpotID: + initializeTBPMonitor(kMonitorProcedure, kTSA0BTBPProcedureHighlight); + break; + case kTSA0BWestCloseVideoSpotID: + _navMovie.stop(); + _sprite2.show(); + _vm->delayShell(1, 2); + _sprite2.hide(); + initializeTBPMonitor(kMonitorNeutral, 0); + break; + case kTSA0BWestPlayVideoSpotID: + playTBPMonitor(); + break; + case kTSA0BEastLeftRewindSpotID: + case kTSA0BEastRightRewindSpotID: + case kTSA0BWestRewindVideoSpotID: + if ((GameState.getT0BMonitorMode() & kPlayingAnyMask) != 0) { + bool playing = _navMovie.isRunning(); + if (playing) + _navMovie.stop(); + + if (clickedSpot->getObjectID() == kTSA0BEastRightRewindSpotID) + _sprite2.show(); + else + _sprite1.show(); + + _vm->delayShell(1, 2); + + if (clickedSpot->getObjectID() == kTSA0BEastRightRewindSpotID) + _sprite2.hide(); + else + _sprite1.hide(); + + _navMovie.setTime(GameState.getT0BMonitorStart()); + + if (playing) { + _navMovie.start(); + } else { + _privateFlags.setFlag(kTSAPrivatePlayingLeftComparisonFlag, false); + _privateFlags.setFlag(kTSAPrivatePlayingRightComparisonFlag, false); + } + } + break; + case kTSA22EastMonitorSpotID: + requestExtraSequence(kTSA22RedEastZoomInSequence, kExtraCompletedFlag, kFilterNoInput); + break; + case kTSA23WestMonitorSpotID: + requestExtraSequence(kTSA23RedWestVaultZoomInSequence, kExtraCompletedFlag, kFilterNoInput); + break; + case kTSA0BNorthRobotsToCommandCenterSpotID: + _sprite1.setCurrentFrameIndex(kRedirectionCCDoorSprite); + _sprite1.show(); + _vm->delayShell(1, 2); + _sprite1.hide(); + + switch (GameState.getTSAState()) { + case kRobotsAtCommandCenter: + // Nothing + break; + case kRobotsAtFrontDoor: + GameState.setTSAState(kRobotsAtCommandCenter); + _sprite2.setCurrentFrameIndex(kRedirectionNewTargetSprite); + startExtraSequence(kTSA0BRobotsFromFrontDoorToCommandCenter, kExtraCompletedFlag, kFilterNoInput); + break; + case kRobotsAtReadyRoom: + GameState.setTSAState(kRobotsAtCommandCenter); + _sprite2.setCurrentFrameIndex(kRedirectionNewTargetSprite); + startExtraSequence(kTSA0BRobotsFromReadyRoomToCommandCenter, kExtraCompletedFlag, kFilterNoInput); + break; + } + break; + case kTSA0BNorthRobotsToReadyRoomSpotID: + _sprite1.setCurrentFrameIndex(kRedirectionRRDoorSprite); + _sprite1.show(); + _vm->delayShell(1, 2); + _sprite1.hide(); + + switch (GameState.getTSAState()) { + case kRobotsAtCommandCenter: + GameState.setTSAState(kRobotsAtReadyRoom); + _sprite2.setCurrentFrameIndex(kRedirectionNewTargetSprite); + startExtraSequence(kTSA0BRobotsFromCommandCenterToReadyRoom, kExtraCompletedFlag, kFilterNoInput); + break; + case kRobotsAtFrontDoor: + GameState.setTSAState(kRobotsAtReadyRoom); + _sprite2.setCurrentFrameIndex(kRedirectionNewTargetSprite); + startExtraSequence(kTSA0BRobotsFromFrontDoorToReadyRoom, kExtraCompletedFlag, kFilterNoInput); + break; + case kRobotsAtReadyRoom: + // Nothing + break; + } + break; + case kTSA0BNorthRobotsToFrontDoorSpotID: + _sprite1.setCurrentFrameIndex(kRedirectionFDDoorSprite); + _sprite1.show(); + _vm->delayShell(1, 2); + _sprite1.hide(); + + switch (GameState.getTSAState()) { + case kRobotsAtCommandCenter: + GameState.setTSAState(kRobotsAtFrontDoor); + _sprite2.setCurrentFrameIndex(kRedirectionNewTargetSprite); + startExtraSequence(kTSA0BRobotsFromCommandCenterToFrontDoor, kExtraCompletedFlag, kFilterNoInput); + break; + case kRobotsAtFrontDoor: + // Nothing + break; + case kRobotsAtReadyRoom: + GameState.setTSAState(kRobotsAtFrontDoor); + _sprite2.setCurrentFrameIndex(kRedirectionNewTargetSprite); + startExtraSequence(kTSA0BRobotsFromReadyRoomToFrontDoor, kExtraCompletedFlag, kFilterNoInput); + break; + } + break; + + // Pegasus + case kTSA37NorthJumpToPrehistoricSpotID: + startExtraSequence(kTSA37PegasusDepart, kExtraCompletedFlag, kFilterNoInput); + break; + case kTSA37NorthExitSpotID: + _sprite2.setCurrentFrameIndex(1); + _vm->delayShell(1, 2); + releaseSprites(); + moveForward(); + break; + case kTSA37NorthJumpMenuSpotID: + _sprite2.setCurrentFrameIndex(1); + _vm->delayShell(1, 2); + releaseSprites(); + break; + case kTSA37NorthJumpToNoradSpotID: + GameState.setTSAState(kPlayerOnWayToNorad); + requestExtraSequence(kTSA37JumpToNorad, 0, kFilterNoInput); + + if (!GameState.getBeenToNorad()) { + requestExtraSequence(kTSA37NoradToAI7, 0, kFilterNoInput); + requestExtraSequence(kTSA37PegasusAI7, 0, kFilterNoInput); + requestExtraSequence(kTSA37AI7ToNorad, 0, kFilterNoInput); + GameState.setBeenToNorad(true); + } + + requestExtraSequence(kTSA37NoradToDepart, 0, kFilterNoInput); + requestExtraSequence(kTSA37PegasusDepart, kExtraCompletedFlag, kFilterNoInput); + break; + case kTSA37NorthJumpToMarsSpotID: + GameState.setTSAState(kPlayerOnWayToMars); + requestExtraSequence(kTSA37JumpToMars, 0, kFilterNoInput); + + if (!GameState.getBeenToMars()) { + requestExtraSequence(kTSA37MarsToAI6, 0, kFilterNoInput); + requestExtraSequence(kTSA37PegasusAI6, 0, kFilterNoInput); + requestExtraSequence(kTSA37AI6ToMars, 0, kFilterNoInput); + GameState.setBeenToMars(true); + } + + requestExtraSequence(kTSA37MarsToDepart, 0, kFilterNoInput); + requestExtraSequence(kTSA37PegasusDepart, kExtraCompletedFlag, kFilterNoInput); + break; + case kTSA37NorthJumpToWSCSpotID: + GameState.setTSAState(kPlayerOnWayToWSC); + requestExtraSequence(kTSA37JumpToWSC, 0, kFilterNoInput); + + if (!GameState.getBeenToWSC()) { + requestExtraSequence(kTSA37WSCToAI5, 0, kFilterNoInput); + requestExtraSequence(kTSA37PegasusAI5, 0, kFilterNoInput); + requestExtraSequence(kTSA37AI5ToWSC, 0, kFilterNoInput); + GameState.setBeenToWSC(true); + } + + requestExtraSequence(kTSA37WSCToDepart, 0, kFilterNoInput); + requestExtraSequence(kTSA37PegasusDepart, kExtraCompletedFlag, kFilterNoInput); + break; + default: + Neighborhood::clickInHotspot(input, clickedSpot); + break; + } +} + +void FullTSA::showMainJumpMenu() { + ExtraID jumpMenuView = kTSA37JumpMenu000; + + if (GameState.getNoradFinished()) + jumpMenuView += 4; + if (GameState.getMarsFinished()) + jumpMenuView += 2; + if (GameState.getWSCFinished()) + jumpMenuView += 1; + + showExtraView(jumpMenuView); + setCurrentActivation(kActivationMainJumpMenu); +} + +void FullTSA::playTBPMonitor() { + InputDevice.waitInput(kFilterAllButtons); + + if ((GameState.getT0BMonitorMode() & kPlayingTBPMask) == 0) { + ExtraID extra; + + switch (GameState.getT0BMonitorMode() & kRawModeMask) { + case kMonitorTheory: + GameState.setTSASeenTheory(true); + extra = kTSA0BTBPTheory; + GameState.setScoringSawTheory(true); + break; + case kMonitorBackground: + GameState.setTSASeenBackground(true); + extra = kTSA0BTBPBackground; + GameState.setScoringSawBackground(true); + break; + case kMonitorProcedure: + GameState.setTSASeenProcedure(true); + extra = kTSA0BTBPProcedure; + GameState.setScoringSawProcedure(true); + break; + default: + error("Invalid monitor mode"); + } + + GameState.setT0BMonitorMode(GameState.getT0BMonitorMode() | kPlayingTBPMask); + + ExtraTable::Entry entry; + getExtraEntry(extra, entry); + _lastExtra = extra; + + GameState.setT0BMonitorStart(entry.movieStart + kFullTSAFrameDuration * 5); + startMovieSequence(GameState.getT0BMonitorStart(), entry.movieEnd, kExtraCompletedFlag, false, kFilterAllInput); + } else if (_navMovie.isRunning()) { + _navMovie.stop(); + } else { + _navMovie.start(); + } +} + +void FullTSA::initializeTBPMonitor(const int newMode, const ExtraID highlightExtra) { + GameState.setT0BMonitorMode(newMode); + + if (newMode != kMonitorNeutral) { + showExtraView(highlightExtra); + _vm->delayShell(1, 2); + setCurrentActivation(kActivateTSA0BTBPVideo); + _sprite1.addPICTResourceFrame(kTBPRewindPICTID, false, 0, 0); + _sprite1.moveElementTo(kTBPRewindLeft, kTBPRewindTop); + _sprite1.setCurrentFrameIndex(0); + _sprite2.addPICTResourceFrame(kTBPCloseBoxPICTID, false, 0, 0); + _sprite2.moveElementTo(kTBPCloseLeft, kTBPCloseTop); + _sprite2.setCurrentFrameIndex(0); + playTBPMonitor(); + } else { + if (GameState.getTSAState() == kTSAPlayerForcedReview && GameState.getTSASeenTheory() && + GameState.getTSASeenBackground() && GameState.getTSASeenProcedure()) { + setOffRipAlarm(); + } else { + setCurrentActivation(kActivateTSA0BZoomedIn); + updateViewFrame(); + } + + releaseSprites(); + } + + _interruptionFilter = kFilterAllInput; +} + +void FullTSA::startUpComparisonMonitor() { + releaseSprites(); + + _sprite1.addPICTResourceFrame(kComparisonHiliteNoradPICTID, false, + kComparisonHiliteNoradLeft - kComparisonHiliteSpriteLeft, + kComparisonHiliteNoradTop - kComparisonHiliteSpriteTop); + _sprite1.addPICTResourceFrame(kComparisonHiliteMarsPICTID, false, + kComparisonHiliteMarsLeft - kComparisonHiliteSpriteLeft, + kComparisonHiliteMarsTop - kComparisonHiliteSpriteTop); + _sprite1.addPICTResourceFrame(kComparisonHiliteCaldoriaPICTID, false, + kComparisonHiliteCaldoriaLeft - kComparisonHiliteSpriteLeft, + kComparisonHiliteCaldoriaTop - kComparisonHiliteSpriteTop); + _sprite1.addPICTResourceFrame(kComparisonHiliteWSCPICTID, false, + kComparisonHiliteWSCLeft - kComparisonHiliteSpriteLeft, + kComparisonHiliteWSCTop - kComparisonHiliteSpriteTop); + + _sprite1.setCurrentFrameIndex(0); + _sprite1.moveElementTo(kComparisonHiliteSpriteLeft, kComparisonHiliteSpriteTop); + + _sprite2.addPICTResourceFrame(kComparisonChancesNoradPICTID, false, + kComparisonChancesNoradLeft - kComparisonChancesSpriteLeft, + kComparisonChancesNoradTop - kComparisonChancesSpriteTop); + _sprite2.addPICTResourceFrame(kComparisonChancesMarsPICTID, false, + kComparisonChancesMarsLeft - kComparisonChancesSpriteLeft, + kComparisonChancesMarsTop - kComparisonChancesSpriteTop); + _sprite2.addPICTResourceFrame(kComparisonChancesCaldoriaPICTID, false, + kComparisonChancesCaldoriaLeft - kComparisonChancesSpriteLeft, + kComparisonChancesCaldoriaTop - kComparisonChancesSpriteTop); + _sprite2.addPICTResourceFrame(kComparisonChancesWSCPICTID, false, + kComparisonChancesWSCLeft - kComparisonChancesSpriteLeft, + kComparisonChancesWSCTop - kComparisonChancesSpriteTop); + + _sprite2.setCurrentFrameIndex(0); + _sprite2.moveElementTo(kComparisonChancesSpriteLeft, kComparisonChancesSpriteTop); + updateViewFrame(); +} + +void FullTSA::shutDownComparisonMonitor() { + releaseSprites(); +} + +void FullTSA::initializeComparisonMonitor(const int newMode, const ExtraID comparisonView) { + GameState.setT0BMonitorMode(newMode); + _privateFlags.setFlag(kTSAPrivatePlayingLeftComparisonFlag, false); + _privateFlags.setFlag(kTSAPrivatePlayingRightComparisonFlag, false); + + if (newMode != kMonitorNeutral) { + shutDownComparisonMonitor(); + setCurrentActivation(kActivateTSA0BComparisonVideo); + _sprite1.addPICTResourceFrame(kComparisonLeftRewindPICTID, false, 0, 0); + _sprite1.moveElementTo(kComparisonLeftRewindLeft, kComparisonLeftRewindTop); + _sprite1.setCurrentFrameIndex(0); + _sprite2.addPICTResourceFrame(kComparisonRightRewindPICTID, false, 0, 0); + _sprite2.moveElementTo(kComparisonRightRewindLeft, kComparisonRightRewindTop); + _sprite2.setCurrentFrameIndex(0); + _sprite3.addPICTResourceFrame(kComparisonCloseBoxPICTID, false, 0, 0); + _sprite3.moveElementTo(kComparisonCloseLeft, kComparisonCloseTop); + _sprite3.setCurrentFrameIndex(0); + showExtraView(comparisonView); + } else { + if (GameState.getTSAState() == kTSAPlayerInstalledHistoricalLog && + GameState.getTSASeenNoradNormal() && + GameState.getTSASeenNoradAltered() && + GameState.getTSASeenMarsNormal() && + GameState.getTSASeenMarsAltered() && + GameState.getTSASeenCaldoriaNormal() && + GameState.getTSASeenCaldoriaAltered() && + GameState.getTSASeenWSCNormal() && + GameState.getTSASeenWSCAltered()) { + GameState.setTSAState(kTSABossSawHistoricalLog); + requestExtraSequence(kTSA0BEastZoomOut, kExtraCompletedFlag, kFilterNoInput); + requestExtraSequence(kTSA0BEastTurnLeft, kExtraCompletedFlag, kFilterNoInput); + requestExtraSequence(kTSA0BNorthZoomIn, kExtraCompletedFlag, kFilterNoInput); + } else { + setCurrentActivation(kActivateTSA0BZoomedIn); + releaseSprites(); + startUpComparisonMonitor(); + } + } + + _interruptionFilter = kFilterAllInput; +} + +void FullTSA::playLeftComparison() { + InputDevice.waitInput(kFilterAllButtons); + + if ((GameState.getT0BMonitorMode() & kPlayingLeftComparisonMask) == 0) { + ExtraID extra; + + switch (GameState.getT0BMonitorMode() & kRawModeMask) { + case kMonitorNoradComparison: + GameState.setTSASeenNoradAltered(true); + extra = kTSA0BNoradAltered; + GameState.setScoringSawNoradAltered(true); + break; + case kMonitorMarsComparison: + GameState.setTSASeenMarsAltered(true); + extra = kTSA0BMarsAltered; + GameState.setScoringSawMarsAltered(true); + break; + case kMonitorCaldoriaComparison: + GameState.setTSASeenCaldoriaAltered(true); + extra = kTSA0BCaldoriaAltered; + GameState.setScoringSawCaldoriaAltered(true); + break; + case kMonitorWSCComparison: + GameState.setTSASeenWSCAltered(true); + extra = kTSA0BWSCAltered; + GameState.setScoringSawWSCAltered(true); + break; + default: + error("Invalid monitor mode"); + } + + GameState.setT0BMonitorMode(GameState.getT0BMonitorMode() | kPlayingLeftComparisonMask); + + ExtraTable::Entry entry; + getExtraEntry(extra, entry); + _lastExtra = extra; + + // skip first five frames of movie + // (this is a dissolve that doesn't belong...) + GameState.setT0BMonitorStart(entry.movieStart + kFullTSAFrameDuration * 5); + _privateFlags.setFlag(kTSAPrivatePlayingLeftComparisonFlag); + + // Allow clicking... + startMovieSequence(GameState.getT0BMonitorStart(), entry.movieEnd, + kExtraCompletedFlag, false, JMPPPInput::getClickInputFilter()); + } else if (_navMovie.isRunning()) { + _navMovie.stop(); + } else { + _navMovie.start(); + } +} + +void FullTSA::playRightComparison() { + InputDevice.waitInput(kFilterAllButtons); + + if ((GameState.getT0BMonitorMode() & kPlayingRightComparisonMask) == 0) { + ExtraID extra; + + switch (GameState.getT0BMonitorMode() & kRawModeMask) { + case kMonitorNoradComparison: + GameState.setTSASeenNoradNormal(true); + extra = kTSA0BNoradUnaltered; + GameState.setScoringSawNoradNormal(true); + break; + case kMonitorMarsComparison: + GameState.setTSASeenMarsNormal(true); + extra = kTSA0BMarsUnaltered; + GameState.setScoringSawMarsNormal(true); + break; + case kMonitorCaldoriaComparison: + GameState.setTSASeenCaldoriaNormal(true); + extra = kTSA0BCaldoriaUnaltered; + GameState.setScoringSawCaldoriaNormal(true); + break; + case kMonitorWSCComparison: + GameState.setTSASeenWSCNormal(true); + extra = kTSA0BWSCUnaltered; + GameState.setScoringSawWSCNormal(true); + break; + default: + error("Invalid monitor mode"); + } + + GameState.setT0BMonitorMode(GameState.getT0BMonitorMode() | kPlayingRightComparisonMask); + + ExtraTable::Entry entry; + getExtraEntry(extra, entry); + _lastExtra = extra; + + // skip first five frames of movie + // (this is a dissolve that doesn't belong...) + GameState.setT0BMonitorStart(entry.movieStart + kFullTSAFrameDuration * 5); + _privateFlags.setFlag(kTSAPrivatePlayingRightComparisonFlag); + + // Allow clicking... + startMovieSequence(GameState.getT0BMonitorStart(), entry.movieEnd, + kExtraCompletedFlag, false, JMPPPInput::getClickInputFilter()); + } else if (_navMovie.isRunning()) { + _navMovie.stop(); + } else { + _navMovie.start(); + } +} + +// When this function is called, the player is zoomed up on the center monitor, and the +// TSA state is kTSABossSawHistoricalLog. +void FullTSA::startRobotGame() { + requestExtraSequence(kTSA0BNorthCantChangeHistory, 0, kFilterNoInput); + requestExtraSequence(kTSA0BAIInterruption, 0, kFilterNoInput); + requestExtraSequence(kTSA0BShowGuardRobots, 0, kFilterNoInput); + requestExtraSequence(kTSA0BRobotsToCommandCenter, kExtraCompletedFlag, kFilterNoInput); +} + +void FullTSA::startUpRobotMonitor() { + releaseSprites(); + + _sprite1.addPICTResourceFrame(kRedirectionCCRolloverPICTID, true, + kRedirectionCCRolloverLeft - kRedirectionSprite1Left, + kRedirectionCCRolloverTop - kRedirectionSprite1Top); + _sprite1.addPICTResourceFrame(kRedirectionRRRolloverPICTID, true, + kRedirectionRRRolloverLeft - kRedirectionSprite1Left, + kRedirectionRRRolloverTop - kRedirectionSprite1Top); + _sprite1.addPICTResourceFrame(kRedirectionFDRolloverPICTID, false, + kRedirectionFDRolloverLeft - kRedirectionSprite1Left, + kRedirectionFDRolloverTop - kRedirectionSprite1Top); + _sprite1.addPICTResourceFrame(kRedirectionCCDoorPICTID, true, + kRedirectionCCDoorLeft - kRedirectionSprite1Left, + kRedirectionCCDoorTop - kRedirectionSprite1Top); + _sprite1.addPICTResourceFrame(kRedirectionRRDoorPICTID, true, + kRedirectionRRDoorLeft - kRedirectionSprite1Left, + kRedirectionRRDoorTop - kRedirectionSprite1Top); + _sprite1.addPICTResourceFrame(kRedirectionFDDoorPICTID, false, + kRedirectionFDDoorLeft - kRedirectionSprite1Left, + kRedirectionFDDoorTop - kRedirectionSprite1Top); + _sprite1.addPICTResourceFrame(kRedirectionClosePICTID, false, + kRedirectionCloseLeft - kRedirectionSprite1Left, + kRedirectionCloseTop - kRedirectionSprite1Top); + _sprite1.moveElementTo(kRedirectionSprite1Left, kRedirectionSprite1Top); + + _sprite2.addPICTResourceFrame(kRedirectionSecuredPICTID, false, + kRedirectionSecuredLeft - kRedirectionSprite2Left, + kRedirectionSecuredTop - kRedirectionSprite2Top); + _sprite2.addPICTResourceFrame(kRedirectionNewTargetPICTID, false, + kRedirectionNewTargetLeft - kRedirectionSprite2Left, + kRedirectionNewTargetTop - kRedirectionSprite2Top); + _sprite2.moveElementTo(kRedirectionSprite2Left, kRedirectionSprite2Top); + + switch (GameState.getTSAState()) { + case kRobotsAtCommandCenter: + showExtraView(kTSA0BNorthRobotsAtCCView); + break; + case kRobotsAtFrontDoor: + showExtraView(kTSA0BNorthRobotsAtFDView); + break; + case kRobotsAtReadyRoom: + showExtraView(kTSA0BNorthRobotsAtRRView); + break; + } +} + +void FullTSA::shutDownRobotMonitor() { + releaseSprites(); +} + +// Assume this is called only when zoomed in at T0B west +void FullTSA::setOffRipAlarm() { + GameState.setTSAState(kTSAPlayerDetectedRip); + _ripTimer.initImage(); + _ripTimer.moveElementTo(kRipTimerLeft, kRipTimerTop); + _ripTimer.setSegment(0, kRipTimeLimit, kRipTimeScale); + _ripTimer.start(); + loadAmbientLoops(); + startExtraSequenceSync(kTSA0BRipAlarmScreen, kFilterNoInput); + _vm->delayShell(2, 1); // Two seconds.. + requestExtraSequence(kTSA0BWestZoomOut, kExtraCompletedFlag, kFilterNoInput); + requestExtraSequence(kTSA0BWestTurnRight, 0, kFilterNoInput); + requestExtraSequence(kTSA0BNorthZoomIn, kExtraCompletedFlag, kFilterNoInput); + requestExtraSequence(kTSA0BNorthFinallyHappened, 0, kFilterNoInput); + requestExtraSequence(kTSA0BShowRip1, kExtraCompletedFlag, kFilterNoInput); +} + +void FullTSA::checkContinuePoint(const RoomID room, const DirectionConstant direction) { + switch (MakeRoomView(room, direction)) { + case MakeRoomView(kTSA04, kNorth): + case MakeRoomView(kTSA14, kEast): + case MakeRoomView(kTSA15, kWest): + case MakeRoomView(kTSA16, kNorth): + case MakeRoomView(kTSA16, kSouth): + case MakeRoomView(kTSA21Cyan, kSouth): + case MakeRoomView(kTSA21Red, kSouth): + case MakeRoomView(kTSA26, kNorth): + makeContinuePoint(); + break; + } +} + +void FullTSA::arriveAt(const RoomID room, const DirectionConstant direction) { + checkRobotLocations(room, direction); + Neighborhood::arriveAt(room, direction); + + switch (MakeRoomView(room, direction)) { + case MakeRoomView(kTSADeathRoom, kNorth): + case MakeRoomView(kTSADeathRoom, kSouth): + case MakeRoomView(kTSADeathRoom, kEast): + case MakeRoomView(kTSADeathRoom, kWest): + die(kDeathShotByTSARobots); + break; + case MakeRoomView(kTSA00, kNorth): + if (GameState.getLastNeighborhood() != kFullTSAID) { + makeContinuePoint(); + openDoor(); + } else { + setCurrentActivation(kActivateTSAReadyForCard); + loopExtraSequence(kTSATransporterArrowLoop, 0); + } + break; + case MakeRoomView(kTSA03, kNorth): + case MakeRoomView(kTSA05, kNorth): + case MakeRoomView(kTSA0A, kNorth): + case MakeRoomView(kTSA06, kNorth): + case MakeRoomView(kTSA07, kNorth): + if (_utilityFuse.isFuseLit()) + _utilityFuse.stopFuse(); + GameState.setScoringEnterTSA(true); + break; + case MakeRoomView(kTSA04, kNorth): + if (_utilityFuse.isFuseLit()) + _utilityFuse.stopFuse(); + if (!GameState.getTSASeenRobotGreeting()) + startExtraSequence(kTSA04NorthRobotGreeting, kExtraCompletedFlag, kFilterNoInput); + break; + case MakeRoomView(kTSA03, kSouth): + GameState.setTSAFrontDoorUnlockedInside(GameState.getTSAState() == kRobotsAtFrontDoor || GameState.allTimeZonesFinished()); + break; + case MakeRoomView(kTSA0A, kEast): + case MakeRoomView(kTSA0A, kWest): + if (GameState.getTSAState() == kTSAPlayerNotArrived) + setCurrentActivation(kActivateTSARobotsAwake); + break; + case MakeRoomView(kTSA0B, kNorth): + if (GameState.getTSA0BZoomedIn()) { + setCurrentActivation(kActivateTSA0BZoomedIn); + + switch (GameState.getTSAState()) { + case kTSAPlayerNeedsHistoricalLog: + _ripTimer.show(); + break; + case kRobotsAtCommandCenter: + case kRobotsAtFrontDoor: + case kRobotsAtReadyRoom: + startUpRobotMonitor(); + break; + } + } else { + setCurrentActivation(kActivateTSA0BZoomedOut); + + switch (GameState.getTSAState()) { + case kTSAPlayerNotArrived: + requestExtraSequence(kTSA0BNorthZoomIn, kExtraCompletedFlag, kFilterNoInput); + requestExtraSequence(kTSA0BNorthYoureBusted, 0, kFilterNoInput); + requestExtraSequence(kTSA0BNorthZoomOut, kExtraCompletedFlag, kFilterNoInput); + requestExtraSequence(kTSA0BNorthTurnLeft, 0, kFilterNoInput); + requestExtraSequence(kTSA0BWestZoomIn, kExtraCompletedFlag, kFilterNoInput); + break; + case kTSAPlayerGotHistoricalLog: + startExtraSequence(kTSA0BNorthHistLogOpen, kExtraCompletedFlag, kFilterNoInput); + break; + } + } + break; + case MakeRoomView(kTSA0B, kSouth): + GameState.setTSA0BZoomedIn(false); + setCurrentActivation(kActivateTSA0BZoomedOut); + break; + case MakeRoomView(kTSA0B, kWest): + if (GameState.getTSA0BZoomedIn()) { + setCurrentActivation(kActivateTSA0BZoomedIn); + initializeTBPMonitor(kMonitorNeutral, 0); + } else { + setCurrentActivation(kActivateTSA0BZoomedOut); + } + break; + case MakeRoomView(kTSA0B, kEast): + if (GameState.getTSA0BZoomedIn()) { + setCurrentActivation(kActivateTSA0BZoomedIn); + + switch (GameState.getTSAState()) { + case kTSAPlayerInstalledHistoricalLog: + case kTSABossSawHistoricalLog: + case kRobotsAtCommandCenter: + case kRobotsAtFrontDoor: + case kRobotsAtReadyRoom: + initializeComparisonMonitor(kMonitorNeutral, 0); + break; + } + } else { + setCurrentActivation(kActivateTSA0BZoomedOut); + } + break; + case MakeRoomView(kTSA21Red, kSouth): + if (GameState.getTSAState() == kRobotsAtFrontDoor) + GameState.setScoringWentToReadyRoom2(true); + break; + case MakeRoomView(kTSA22Red, kEast): + if (!_vm->playerHasItemID(kJourneymanKey)) + setCurrentActivation(kActivationDoesntHaveKey); + break; + case MakeRoomView(kTSA23Red, kWest): + if (!_vm->playerHasItemID(kPegasusBiochip)) + setCurrentActivation(kActivationDoesntHaveChips); + break; + case MakeRoomView(kTSA25Red, kNorth): + arriveAtTSA25Red(); + break; + case MakeRoomView(kTSA34, kSouth): + if (GameState.getLastRoom() == kTSA37) + closeDoorOffScreen(kTSA37, kNorth); + break; + case MakeRoomView(kTSA37, kNorth): + arriveAtTSA37(); + break; + } +} + +void FullTSA::checkRobotLocations(const RoomID room, const DirectionConstant dir) { + switch (room) { + case kTSA03: + case kTSA04: + case kTSA05: + case kTSA06: + case kTSA0A: + case kTSA07: + case kTSA08: + case kTSA09: + case kTSA10: + case kTSA11: + case kTSA12: + case kTSA13: + case kTSA14: + case kTSA15: + switch (GameState.getTSAState()) { + case kRobotsAtFrontDoor: + setCurrentAlternate(kAltTSARobotsAtFrontDoor); + break; + case kRobotsAtReadyRoom: + setCurrentAlternate(kAltTSARobotsAtReadyRoom); + break; + } + break; + case kTSA16: + if (dir == kNorth) { + switch (GameState.getTSAState()) { + case kRobotsAtCommandCenter: + if (!_privateFlags.getFlag(kTSAPrivateSeenRobotWarningFlag)) { + g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/TSA/XT11WB", false, kWarningInterruption); + _privateFlags.setFlag(kTSAPrivateSeenRobotWarningFlag, true); + } + break; + case kRobotsAtFrontDoor: + setCurrentAlternate(kAltTSARobotsAtFrontDoor); + break; + case kRobotsAtReadyRoom: + setCurrentAlternate(kAltTSARobotsAtReadyRoom); + break; + } + } + break; + } +} + +void FullTSA::arriveAtTSA25Red() { + if (!_vm->playerHasItemID(kJourneymanKey)) + startExtraSequence(kTSA25NorthDeniedNoKey, kExtraCompletedFlag, kFilterNoInput); + else if (!_vm->playerHasItemID(kPegasusBiochip)) + startExtraSequence(kTSA25NorthDeniedNoChip, kExtraCompletedFlag, kFilterNoInput); + else if (GameState.getTSABiosuitOn()) + startExtraSequence(kTSA25NorthAlreadyHaveSuit, kExtraCompletedFlag, kFilterNoInput); + else + startExtraSequence(kTSA25NorthPutOnSuit, kExtraCompletedFlag, kFilterNoInput); +} + +void FullTSA::arriveAtTSA37() { + _ripTimer.stop(); + _ripTimer.releaseImage(); + + switch (GameState.getTSAState()) { + case kTSAPlayerNeedsHistoricalLog: + startExtraLongSequence(kTSA37HorseToAI1, kTSA37AI2ToPrehistoric, kExtraCompletedFlag, kFilterNoInput); + break; + case kPlayerOnWayToPrehistoric: + setCurrentActivation(kActivationJumpToPrehistoric); + showExtraView(kTSA37AI2ToPrehistoric); + break; + case kTSAPlayerGotHistoricalLog: + initializePegasusButtons(false); + break; + case kPlayerWentToPrehistoric: + case kPlayerOnWayToNorad: + case kPlayerOnWayToMars: + case kPlayerOnWayToWSC: + startExtraSequence(kTSA37TimeJumpToPegasus, kExtraCompletedFlag, kFilterNoInput); + break; + case kRobotsAtFrontDoor: + startExtraLongSequence(kTSA37HorseToColonel2, kTSA37AI4ToMainMenu, kExtraCompletedFlag, kFilterNoInput); + break; + case kPlayerLockedInPegasus: + showMainJumpMenu(); + break; + case kPlayerFinishedWithTSA: + initializePegasusButtons(true); + break; + } +} + +void FullTSA::turnTo(const DirectionConstant newDirection) { + Neighborhood::turnTo(newDirection); + + switch (MakeRoomView(GameState.getCurrentRoom(), newDirection)) { + case MakeRoomView(kTSA03, kSouth): + if (GameState.getTSAState() == kRobotsAtFrontDoor || GameState.allTimeZonesFinished()) + GameState.setTSAFrontDoorUnlockedInside(true); + else + GameState.setTSAFrontDoorUnlockedInside(false); + break; + case MakeRoomView(kTSA0A, kEast): + case MakeRoomView(kTSA0A, kWest): + setCurrentActivation(kActivateTSARobotsAwake); + break; + case MakeRoomView(kTSA0B, kEast): + if (GameState.getTSA0BZoomedIn()) + setCurrentActivation(kActivateTSA0BZoomedIn); + else + setCurrentActivation(kActivateTSA0BZoomedOut); + + GameState.setT0BMonitorMode(GameState.getT0BMonitorMode() & ~kPlayingAnyMask); + + if (_privateFlags.getFlag(kTSAPrivateLogReaderOpenFlag)) + _privateFlags.setFlag(kTSAPrivateLogReaderOpenFlag, false); + + switch (GameState.getTSAState()) { + case kTSAPlayerInstalledHistoricalLog: + case kTSABossSawHistoricalLog: + case kRobotsAtCommandCenter: + case kRobotsAtFrontDoor: + case kRobotsAtReadyRoom: + if (GameState.getTSA0BZoomedIn()) + startUpComparisonMonitor(); + break; + } + break; + case MakeRoomView(kTSA0B, kNorth): + if (GameState.getTSA0BZoomedIn()) + setCurrentActivation(kActivateTSA0BZoomedIn); + else + setCurrentActivation(kActivateTSA0BZoomedOut); + + GameState.setT0BMonitorMode(GameState.getT0BMonitorMode() & ~kPlayingAnyMask); + + switch (GameState.getTSAState()) { + case kTSAPlayerNeedsHistoricalLog: + if (GameState.getTSA0BZoomedIn()) + _ripTimer.show(); + break; + case kTSAPlayerGotHistoricalLog: + if (!GameState.getTSA0BZoomedIn()) + startExtraSequence(kTSA0BNorthHistLogOpen, kExtraCompletedFlag, kFilterNoInput); + break; + case kTSAPlayerInstalledHistoricalLog: + if (GameState.getTSA0BZoomedIn()) { + if ((GameState.getTSASeenNoradNormal() || GameState.getTSASeenNoradAltered()) && + (GameState.getTSASeenMarsNormal() || GameState.getTSASeenMarsAltered()) && + (GameState.getTSASeenCaldoriaNormal() || GameState.getTSASeenCaldoriaAltered()) && + (GameState.getTSASeenWSCNormal() || GameState.getTSASeenWSCAltered())) { + GameState.setTSAState(kTSABossSawHistoricalLog); + startRobotGame(); + } + } + break; + case kRobotsAtCommandCenter: + case kRobotsAtFrontDoor: + case kRobotsAtReadyRoom: + if (GameState.getTSA0BZoomedIn()) + startExtraSequence(kTSA0BShowGuardRobots, kExtraCompletedFlag, kFilterNoInput); + break; + } + break; + case MakeRoomView(kTSA0B, kWest): + if (GameState.getTSA0BZoomedIn()) + setCurrentActivation(kActivateTSA0BZoomedIn); + else + setCurrentActivation(kActivateTSA0BZoomedOut); + + GameState.setT0BMonitorMode(GameState.getT0BMonitorMode() & ~kPlayingAnyMask); + + if (_privateFlags.getFlag(kTSAPrivateLogReaderOpenFlag)) + _privateFlags.setFlag(kTSAPrivateLogReaderOpenFlag, false); + + if (GameState.getTSA0BZoomedIn()) + initializeTBPMonitor(kMonitorNeutral, 0); + break; + case MakeRoomView(kTSA0B, kSouth): + GameState.setTSA0BZoomedIn(false); + setCurrentActivation(kActivateTSA0BZoomedOut); + break; + case MakeRoomView(kTSA16, kNorth): + switch (GameState.getTSAState()) { + case kRobotsAtCommandCenter: + if (!_privateFlags.getFlag(kTSAPrivateSeenRobotWarningFlag)) { + g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/TSA/XT11WB", false, kWarningInterruption); + _privateFlags.setFlag(kTSAPrivateSeenRobotWarningFlag, true); + } + break; + case kRobotsAtFrontDoor: + setCurrentAlternate(kAltTSARobotsAtFrontDoor); + break; + case kRobotsAtReadyRoom: + setCurrentAlternate(kAltTSARobotsAtReadyRoom); + break; + } + break; + case MakeRoomView(kTSA22Red, kEast): + if (!_vm->playerHasItemID(kJourneymanKey)) + setCurrentActivation(kActivationDoesntHaveKey); + break; + case MakeRoomView(kTSA22Red, kNorth): + case MakeRoomView(kTSA22Red, kSouth): + if (_privateFlags.getFlag(kTSAPrivateKeyVaultOpenFlag)) { + playSpotSoundSync(kTSAVaultCloseIn, kTSAVaultCloseOut); + _privateFlags.setFlag(kTSAPrivateKeyVaultOpenFlag, false); + } + + setCurrentActivation(kActivateHotSpotAlways); + break; + case MakeRoomView(kTSA23Red, kWest): + if (!_vm->playerHasItemID(kPegasusBiochip)) + setCurrentActivation(kActivationDoesntHaveChips); + break; + case MakeRoomView(kTSA23Red, kNorth): + case MakeRoomView(kTSA23Red, kSouth): + if (_privateFlags.getFlag(kTSAPrivateChipVaultOpenFlag)) { + playSpotSoundSync(kTSAVaultCloseIn, kTSAVaultCloseOut); + _privateFlags.setFlag(kTSAPrivateChipVaultOpenFlag, false); + } + + setCurrentActivation(kActivateHotSpotAlways); + break; + } + + // Make sure the TBP monitor is forced neutral. + GameState.setT0BMonitorMode(kMonitorNeutral); +} + +void FullTSA::closeDoorOffScreen(const RoomID room, const DirectionConstant) { + switch (room) { + case kTSA00: + case kTSA01: + if (GameState.getCurrentRoom() == kTSA01 || GameState.getCurrentRoom() == kTSA02) + playSpotSoundSync(kTSAGTDoorCloseIn, kTSAGTDoorCloseOut); + break; + case kTSA02: + case kTSA03: + playSpotSoundSync(kTSAEntryDoorCloseIn, kTSAEntryDoorCloseOut); + break; + case kTSA14: + case kTSA15: + case kTSA16: + case kTSA21Cyan: + case kTSA21Red: + playSpotSoundSync(kTSAInsideDoorCloseIn, kTSAInsideDoorCloseOut); + break; + case kTSA34: + case kTSA37: + playSpotSoundSync(kTSAPegasusDoorCloseIn, kTSAPegasusDoorCloseOut); + break; + } +} + +void FullTSA::receiveNotification(Notification *notification, const NotificationFlags flags) { + ExtraID lastExtra = _lastExtra; + + if ((flags & kExtraCompletedFlag) != 0) { + switch (lastExtra) { + case kTSA0BEastTurnLeft: + // Need to check this here because turnTo will call _navMovie.stop, + // so it has to happen before Neighborhood::receiveNotification, + // which may end up starting another sequence... + turnTo(kNorth); + break; + } + } + + Neighborhood::receiveNotification(notification, flags); + + InventoryItem *item; + + if ((flags & kExtraCompletedFlag) != 0) { + // Only allow input if we're not in the middle of series of queue requests. + if (actionQueueEmpty()) + _interruptionFilter = kFilterAllInput; + + switch (lastExtra) { + case kTSAGTCardSwipe: + item = (InventoryItem *)_vm->getAllItems().findItemByID(kKeyCard); + _vm->addItemToInventory(item); + setCurrentActivation(kActivateTSAReadyToTransport); + break; + case kTSAGTGoToCaldoria: + _vm->jumpToNewEnvironment(kCaldoriaID, kCaldoria44, kEast); + + if (GameState.allTimeZonesFinished()) + GameState.setScoringWentAfterSinclair(true); + break; + case kTSAGTGoToTokyo: + case kTSAGTGoToBeach: + if (GameState.allTimeZonesFinished()) + die(kDeathSinclairShotDelegate); + else + die(kDeathUncreatedInTSA); + break; + case kTSA02NorthZoomOut: + openDoor(); + break; + + // Hall of suspects. + case kTSA04NorthRobotGreeting: + GameState.setTSASeenRobotGreeting(true); + restoreStriding(kTSA03, kNorth, kNoAlternateID); + break; + case kTSA03JimenezZoomIn: + GameState.setScoringSawBust1(true); + break; + case kTSA03CrenshawZoomIn: + GameState.setScoringSawBust2(true); + break; + case kTSA04MatsumotoZoomIn: + GameState.setScoringSawBust3(true); + break; + case kTSA04CastilleZoomIn: + GameState.setScoringSawBust4(true); + break; + case kTSA05SinclairZoomIn: + GameState.setScoringSawBust5(true); + break; + case kTSA05WhiteZoomIn: + GameState.setScoringSawBust6(true); + break; + + // Command center + // Historical comparison... + case kTSA0BEastZoomIn: + GameState.setTSA0BZoomedIn(true); + setCurrentActivation(kActivateTSA0BZoomedIn); + GameState.setT0BMonitorMode(GameState.getT0BMonitorMode() & ~kPlayingAnyMask); + + switch (GameState.getTSAState()) { + case kTSAPlayerInstalledHistoricalLog: + case kTSABossSawHistoricalLog: + case kRobotsAtCommandCenter: + case kRobotsAtFrontDoor: + case kRobotsAtReadyRoom: + startUpComparisonMonitor(); + break; + } + break; + case kTSA0BEastZoomOut: + GameState.setTSA0BZoomedIn(false); + setCurrentActivation(kActivateTSA0BZoomedOut); + + switch (GameState.getTSAState()) { + case kTSABossSawHistoricalLog: + // Prevent current view from activating. + break; + default: + activateCurrentView(GameState.getCurrentRoom(), GameState.getCurrentDirection(), + kSpotOnTurnMask); + break; + } + break; + case kTSA0BComparisonStartup: + if ((flags & kActionRequestCompletedFlag) != 0) { + _privateFlags.setFlag(kTSAPrivateLogReaderOpenFlag, false); + GameState.setTSAState(kTSAPlayerInstalledHistoricalLog); + turnTo(kEast); + } + + startUpComparisonMonitor(); + break; + case kTSA0BNoradAltered: + case kTSA0BMarsAltered: + case kTSA0BCaldoriaAltered: + case kTSA0BWSCAltered: + case kTSA0BNoradUnaltered: + case kTSA0BMarsUnaltered: + case kTSA0BCaldoriaUnaltered: + case kTSA0BWSCUnaltered: + initializeComparisonMonitor(kMonitorNeutral, 0); + break; + + // Center monitor. + case kTSA0BNorthZoomIn: + GameState.setTSA0BZoomedIn(true); + setCurrentActivation(kActivateTSA0BZoomedIn); + GameState.setT0BMonitorMode(GameState.getT0BMonitorMode() & ~kPlayingAnyMask); + + switch (GameState.getTSAState()) { + case kTSAPlayerNeedsHistoricalLog: + startExtraSequence(kTSA0BShowRip1, kExtraCompletedFlag, kFilterNoInput); + break; + case kTSABossSawHistoricalLog: + case kTSAPlayerInstalledHistoricalLog: + if ((GameState.getTSASeenNoradNormal() || GameState.getTSASeenNoradAltered()) && + (GameState.getTSASeenMarsNormal() || GameState.getTSASeenMarsAltered()) && + (GameState.getTSASeenCaldoriaNormal() || GameState.getTSASeenCaldoriaAltered()) && + (GameState.getTSASeenWSCNormal() || GameState.getTSASeenWSCAltered())) { + GameState.setTSAState(kTSABossSawHistoricalLog); + startRobotGame(); + } + break; + case kRobotsAtCommandCenter: + case kRobotsAtFrontDoor: + case kRobotsAtReadyRoom: + startExtraSequence(kTSA0BShowGuardRobots, kExtraCompletedFlag, kFilterNoInput); + break; + } + break; + case kTSA0BNorthZoomOut: + GameState.setTSA0BZoomedIn(false); + setCurrentActivation(kActivateTSA0BZoomedOut); + break; + case kTSA0BShowRip1: + GameState.setTSAState(kTSAPlayerNeedsHistoricalLog); + GameState.setTSACommandCenterLocked(false); + + if ((flags & kActionRequestCompletedFlag) != 0) + turnTo(kNorth); + + _ripTimer.show(); + break; + case kTSA0BNorthHistLogOpen: + setCurrentActivation(kActivationLogReaderOpen); + _privateFlags.setFlag(kTSAPrivateLogReaderOpenFlag, true); + break; + case kTSA0BRobotsToCommandCenter: + GameState.setTSAState(kRobotsAtCommandCenter); + // Fall through + case kTSA0BShowGuardRobots: + startUpRobotMonitor(); + // Fall through + case kTSA0BRobotsFromCommandCenterToReadyRoom: + case kTSA0BRobotsFromReadyRoomToCommandCenter: + case kTSA0BRobotsFromCommandCenterToFrontDoor: + case kTSA0BRobotsFromFrontDoorToCommandCenter: + case kTSA0BRobotsFromFrontDoorToReadyRoom: + case kTSA0BRobotsFromReadyRoomToFrontDoor: + _sprite2.setCurrentFrameIndex(kRedirectionSecuredSprite); + _sprite2.show(); + break; + + // TBP monitor. + case kTSA0BWestZoomIn: + GameState.setTSA0BZoomedIn(true); + setCurrentActivation(kActivateTSA0BZoomedIn); + + if (GameState.getTSAState() == kTSAPlayerNotArrived) { + turnTo(kWest); + GameState.setTSACommandCenterLocked(true); + GameState.setTSAState(kTSAPlayerForcedReview); + } + + initializeTBPMonitor(kMonitorNeutral, 0); + break; + case kTSA0BWestZoomOut: + GameState.setTSA0BZoomedIn(false); + setCurrentActivation(kActivateTSA0BZoomedOut); + GameState.setT0BMonitorMode(kMonitorNeutral); + + switch (GameState.getTSAState()) { + case kTSAPlayerDetectedRip: + // Keep the current view from activating. + break; + default: + activateCurrentView(GameState.getCurrentRoom(), GameState.getCurrentDirection(), + kSpotOnTurnMask); + break; + } + break; + case kTSA0BTBPTheory: + case kTSA0BTBPBackground: + case kTSA0BTBPProcedure: + initializeTBPMonitor(kMonitorNeutral, 0); + break; + + // Ready room + case kTSA22RedEastZoomInSequence: + _privateFlags.setFlag(kTSAPrivateKeyVaultOpenFlag, true); + setCurrentActivation(kActivationKeyVaultOpen); + break; + case kTSA23RedWestVaultZoomInSequence: + _privateFlags.setFlag(kTSAPrivateChipVaultOpenFlag, true); + setCurrentActivation(kActivationChipVaultOpen); + break; + case kTSA25NorthPutOnSuit: + GameState.setTSABiosuitOn(true); + GameState.setScoringGotBiosuit(true); + // Fall through... + case kTSA25NorthAlreadyHaveSuit: + requestExtraSequence(kTSA25NorthDescending1, 0, kFilterNoInput); + requestExtraSequence(kTSA25NorthDescending2, kExtraCompletedFlag, kFilterNoInput); + break; + case kTSA25NorthDescending2: + arriveAt(kTSA26, kNorth); + break; + + // Pegasus. + case kTSA37HorseToAI1: + case kTSA37AI2ToPrehistoric: + setCurrentActivation(kActivationJumpToPrehistoric); + GameState.setTSAState(kPlayerOnWayToPrehistoric); + break; + case kTSA37PegasusDepart: + _vm->setLastEnergyValue(kFullEnergy); + + switch (GameState.getTSAState()) { + case kPlayerOnWayToPrehistoric: + _vm->jumpToNewEnvironment(kPrehistoricID, kPrehistoric02, kSouth); + GameState.setPrehistoricSeenTimeStream(false); + GameState.setPrehistoricSeenFlyer1(false); + GameState.setPrehistoricSeenFlyer2(false); + GameState.setPrehistoricSeenBridgeZoom(false); + GameState.setPrehistoricBreakerThrown(false); + GameState.setScoringGoToPrehistoric(true); + GameState.setTSAState(kPlayerWentToPrehistoric); + break; + case kPlayerOnWayToNorad: + _vm->jumpToNewEnvironment(kNoradAlphaID, kNorad01, kSouth); + GameState.setNoradSeenTimeStream(false); + GameState.setNoradGassed(true); + GameState.setNoradFillingStationOn(false); + GameState.setNoradN22MessagePlayed(false); + GameState.setNoradPlayedGlobeGame(false); + GameState.setNoradBeatRobotWithClaw(false); + GameState.setNoradBeatRobotWithDoor(false); + GameState.setNoradRetScanGood(false); + GameState.setNoradWaitingForLaser(false); + GameState.setNoradSubRoomPressure(9); + GameState.setNoradSubPrepState(kSubNotPrepped); + break; + case kPlayerOnWayToMars: + _vm->jumpToNewEnvironment(kMarsID, kMars0A, kNorth); + GameState.setMarsSeenTimeStream(false); + GameState.setMarsHeardUpperPodMessage(false); + GameState.setMarsRobotThrownPlayer(false); + GameState.setMarsHeardCheckInMessage(false); + GameState.setMarsPodAtUpperPlatform(false); + GameState.setMarsSeenThermalScan(false); + GameState.setMarsArrivedBelow(false); + GameState.setMarsSeenRobotAtReactor(false); + GameState.setMarsAvoidedReactorRobot(false); + GameState.setMarsLockFrozen(false); + GameState.setMarsLockBroken(false); + GameState.setMarsSecurityDown(false); + GameState.setMarsAirlockOpen(false); + GameState.setMarsReadyForShuttleTransport(false); + GameState.setMarsFinishedCanyonChase(false); + GameState.setMarsThreadedMaze(false); + break; + case kPlayerOnWayToWSC: + _vm->jumpToNewEnvironment(kWSCID, kWSC01, kWest); + GameState.setWSCSeenTimeStream(false); + GameState.setWSCPoisoned(false); + GameState.setWSCAnsweredAboutDart(false); + GameState.setWSCRemovedDart(false); + GameState.setWSCAnalyzerOn(false); + GameState.setWSCDartInAnalyzer(false); + GameState.setWSCAnalyzedDart(false); + GameState.setWSCPickedUpAntidote(false); + GameState.setWSCSawMorph(false); + GameState.setWSCDesignedAntidote(false); + GameState.setWSCOfficeMessagesOpen(false); + GameState.setWSCSeenNerd(false); + GameState.setWSCHeardPage1(false); + GameState.setWSCHeardPage2(false); + GameState.setWSCHeardCheckIn(false); + GameState.setWSCDidPlasmaDodge(false); + GameState.setWSCSeenSinclairLecture(false); + GameState.setWSCBeenAtWSC93(false); + GameState.setWSCCatwalkDark(false); + GameState.setWSCRobotDead(false); + GameState.setWSCRobotGone(false); + break; + }; + break; + case kTSA37TimeJumpToPegasus: + if (g_energyMonitor) + g_energyMonitor->stopEnergyDraining(); + + switch (GameState.getTSAState()) { + case kPlayerWentToPrehistoric: + arriveFromPrehistoric(); + break; + case kPlayerOnWayToNorad: + arriveFromNorad(); + break; + case kPlayerOnWayToMars: + arriveFromMars(); + break; + case kPlayerOnWayToWSC: + arriveFromWSC(); + break; + default: + break; + } + break; + case kTSA37DownloadToOpMemReview: + switch (GameState.getTSAState()) { + case kPlayerOnWayToNorad: + g_opticalChip->playOpMemMovie(kPoseidonSpotID); + break; + case kPlayerOnWayToMars: + g_opticalChip->playOpMemMovie(kAriesSpotID); + break; + case kPlayerOnWayToWSC: + g_opticalChip->playOpMemMovie(kMercurySpotID); + break; + } + + if (GameState.allTimeZonesFinished()) { + requestExtraSequence(kTSA37OpMemReviewToAllClear, 0, kFilterNoInput); + requestExtraSequence(kTSA37AllClearToCongratulations, 0, kFilterNoInput); + requestExtraSequence(kTSA37Congratulations, 0, kFilterNoInput); + requestExtraSequence(kTSA37CongratulationsToExit, kExtraCompletedFlag, kFilterNoInput); + } else { + requestExtraSequence(kTSA37OpMemReviewToMainMenu, kExtraCompletedFlag, kFilterNoInput); + } + break; + case kTSA37RecallToDownload: + case kTSA37ReviewRequiredToExit: + GameState.setTSAState(kTSAPlayerGotHistoricalLog); + initializePegasusButtons(kPegasusUnresolved); + break; + case kTSA37ZoomToMainMenu: + case kTSA37HorseToColonel2: + case kTSA37DownloadToMainMenu: + case kTSA37OpMemReviewToMainMenu: + case kTSA37AI4ToMainMenu: + GameState.setTSAState(kPlayerLockedInPegasus); + showMainJumpMenu(); + makeContinuePoint(); + break; + case kTSA37JumpToNoradMenu: + setCurrentActivation(kActivationJumpToNorad); + break; + case kTSA37JumpToMarsMenu: + setCurrentActivation(kActivationJumpToMars); + break; + case kTSA37JumpToWSCMenu: + setCurrentActivation(kActivationJumpToWSC); + break; + case kTSA37CancelNorad: + case kTSA37CancelMars: + case kTSA37CancelWSC: + showMainJumpMenu(); + break; + case kTSA37CongratulationsToExit: + GameState.setTSAState(kPlayerFinishedWithTSA); + initializePegasusButtons(true); + break; + } + } + + g_AIArea->checkMiddleArea(); +} + +void FullTSA::arriveFromPrehistoric() { + if (_vm->playerHasItemID(kHistoricalLog)) { + GameState.setScoringFinishedPrehistoric(); + requestExtraSequence(kTSA37RecallToDownload, 0, kFilterNoInput); + requestExtraSequence(kTSA37DownloadToColonel1, 0, kFilterNoInput); + requestExtraSequence(kTSA37Colonel1, 0, kFilterNoInput); + requestExtraSequence(kTSA37Colonel1ToReviewRequired, 0, kFilterNoInput); + requestExtraSequence(kTSA37ReviewRequiredToExit, kExtraCompletedFlag, kFilterNoInput); + } else { + // Make sure rip timer is going... + startExtraSequence(kTSA37DownloadToMainMenu, kExtraCompletedFlag, kFilterNoInput); + } +} + +void FullTSA::arriveFromNorad() { + requestExtraSequence(kTSA37RecallToDownload, 0, kFilterNoInput); + + if (GameState.getNoradFinished() && !GameState.getScoringFinishedNorad()) { + GameState.setScoringFinishedNorad(); + requestExtraSequence(kTSA37DownloadToOpMemReview, kExtraCompletedFlag, kFilterNoInput); + } else { + requestExtraSequence(kTSA37DownloadToMainMenu, kExtraCompletedFlag, kFilterNoInput); + } +} + +void FullTSA::arriveFromMars() { + requestExtraSequence(kTSA37RecallToDownload, 0, kFilterNoInput); + + if (GameState.getMarsFinished() && !GameState.getScoringFinishedMars()) { + GameState.setScoringFinishedMars(); + requestExtraSequence(kTSA37DownloadToOpMemReview, kExtraCompletedFlag, kFilterNoInput); + } else { + requestExtraSequence(kTSA37DownloadToMainMenu, kExtraCompletedFlag, kFilterNoInput); + } +} + +void FullTSA::arriveFromWSC() { + requestExtraSequence(kTSA37RecallToDownload, 0, kFilterNoInput); + + if (GameState.getWSCFinished() && !GameState.getScoringFinishedWSC()) { + GameState.setScoringFinishedWSC(); + requestExtraSequence(kTSA37DownloadToOpMemReview, kExtraCompletedFlag, kFilterNoInput); + } else { + requestExtraSequence(kTSA37DownloadToMainMenu, kExtraCompletedFlag, kFilterNoInput); + } +} + +void FullTSA::initializePegasusButtons(bool resolved) { + if (resolved) { + _sprite1.addPICTResourceFrame(kResolvedPICTID, false, 0, 0); + _sprite1.moveElementTo(kResolvedLeft, kResolvedTop); + } else { + _sprite1.addPICTResourceFrame(kUnresolvedPICTID, false, 0, 0); + _sprite1.moveElementTo(kUnresolvedLeft, kUnresolvedTop); + } + + _sprite1.setCurrentFrameIndex(0); + _sprite1.show(); + + _sprite2.addPICTResourceFrame(kExitPICTID, false, kExitLeft - kExitHilitedLeft, kExitTop - kExitHilitedTop); + _sprite2.addPICTResourceFrame(kExitHilitedPICTID, false, 0, 0); + _sprite2.moveElementTo(kExitHilitedLeft, kExitHilitedTop); + setCurrentActivation(kActivationReadyToExit); + _sprite2.setCurrentFrameIndex(0); + _sprite2.show(); +} + +Hotspot *FullTSA::getItemScreenSpot(Item *item, DisplayElement *element) { + switch (item->getObjectID()) { + case kJourneymanKey: + return _vm->getAllHotspots().findHotspotByID(kTSA22EastKeySpotID); + break; + case kPegasusBiochip: + return _vm->getAllHotspots().findHotspotByID(kTSA23WestChipsSpotID); + break; + } + + return Neighborhood::getItemScreenSpot(item, element); +} + +void FullTSA::dropItemIntoRoom(Item *item, Hotspot *dropSpot) { + Neighborhood::dropItemIntoRoom(item, dropSpot); + + switch (item->getObjectID()) { + case kKeyCard: + if (dropSpot->getObjectID() == kTSAGTCardDropSpotID) + startExtraSequence(kTSAGTCardSwipe, kExtraCompletedFlag, kFilterNoInput); + break; + case kHistoricalLog: + if (dropSpot->getObjectID() == kTSA0BNorthHistLogSpotID) { + requestExtraSequence(kTSA0BNorthHistLogCloseWithLog, 0, kFilterNoInput); + requestExtraSequence(kTSA0BNorthTurnRight, 0, kFilterNoInput); + requestExtraSequence(kTSA0BEastZoomIn, kExtraCompletedFlag, kFilterNoInput); + requestExtraSequence(kTSA0BComparisonStartup, kExtraCompletedFlag, kFilterNoInput); + GameState.setScoringPutLogInReader(true); + } + break; + } +} + +uint FullTSA::getHistoricalLogIndex() { + uint index; + + if (GameState.getTSASeenNoradNormal() && GameState.getTSASeenNoradAltered()) + index = 8; + else + index = 0; + + if (GameState.getTSASeenMarsNormal() && GameState.getTSASeenMarsAltered()) + index += 4; + + if (GameState.getTSASeenCaldoriaNormal() && GameState.getTSASeenCaldoriaAltered()) + index += 2; + + if (GameState.getTSASeenWSCNormal() && GameState.getTSASeenWSCAltered()) + index += 1; + + return index; +} + +void FullTSA::handleInput(const Input &input, const Hotspot *cursorSpot) { + switch (MakeRoomView(GameState.getCurrentRoom(), GameState.getCurrentDirection())) { + case MakeRoomView(kTSA0B, kEast): + if (GameState.getTSA0BZoomedIn() && !_navMovie.isRunning() && GameState.getT0BMonitorMode() == kMonitorNeutral) { + switch (GameState.getTSAState()) { + case kTSAPlayerInstalledHistoricalLog: + case kTSABossSawHistoricalLog: + case kRobotsAtCommandCenter: + case kRobotsAtFrontDoor: + case kRobotsAtReadyRoom: + if (cursorSpot) { + switch (cursorSpot->getObjectID()) { + case kTSA0BEastCompareNoradSpotID: + _sprite1.setCurrentFrameIndex(0); + _sprite2.setCurrentFrameIndex(0); + _sprite1.show(); + _sprite2.show(); + break; + case kTSA0BEastCompareMarsSpotID: + _sprite1.setCurrentFrameIndex(1); + _sprite2.setCurrentFrameIndex(1); + _sprite1.show(); + _sprite2.show(); + break; + case kTSA0BEastCompareCaldoriaSpotID: + _sprite1.setCurrentFrameIndex(2); + _sprite2.setCurrentFrameIndex(2); + _sprite1.show(); + _sprite2.show(); + break; + case kTSA0BEastCompareWSCSpotID: + _sprite1.setCurrentFrameIndex(3); + _sprite2.setCurrentFrameIndex(3); + _sprite1.show(); + _sprite2.show(); + break; + default: + _sprite1.hide(); + _sprite2.hide(); + break; + } + } else { + _sprite1.hide(); + _sprite2.hide(); + } + break; + } + } + break; + case MakeRoomView(kTSA0B, kNorth): + if (GameState.getTSA0BZoomedIn() && !_navMovie.isRunning()) { + switch (GameState.getTSAState()) { + case kRobotsAtCommandCenter: + case kRobotsAtFrontDoor: + case kRobotsAtReadyRoom: + if (cursorSpot) { + switch (cursorSpot->getObjectID()) { + case kTSA0BNorthRobotsToCommandCenterSpotID: + _sprite1.setCurrentFrameIndex(kRedirectionCCRolloverSprite); + _sprite1.show(); + break; + case kTSA0BNorthRobotsToReadyRoomSpotID: + _sprite1.setCurrentFrameIndex(kRedirectionRRRolloverSprite); + _sprite1.show(); + break; + case kTSA0BNorthRobotsToFrontDoorSpotID: + _sprite1.setCurrentFrameIndex(kRedirectionFDRolloverSprite); + _sprite1.show(); + break; + default: + _sprite1.hide(); + break; + } + } else { + _sprite1.hide(); + } + break; + } + } + break; + } + + Neighborhood::handleInput(input, cursorSpot); +} + +void FullTSA::releaseSprites() { + _sprite1.hide(); + _sprite2.hide(); + _sprite3.hide(); + _sprite1.discardFrames(); + _sprite2.discardFrames(); + _sprite3.discardFrames(); +} + +bool FullTSA::canSolve() { + return GameState.getCurrentRoomAndView() == MakeRoomView(kTSA0B, kNorth) && + GameState.getTSA0BZoomedIn() && + (GameState.getTSAState() == kRobotsAtCommandCenter || + GameState.getTSAState() == kRobotsAtFrontDoor || + GameState.getTSAState() == kRobotsAtReadyRoom); +} + +void FullTSA::doSolve() { + // REROUTING ROBOTS + + _sprite1.setCurrentFrameIndex(kRedirectionFDDoorSprite); + _sprite1.show(); + _vm->delayShell(1, 2); + _sprite1.hide(); + + switch (GameState.getTSAState()) { + case kRobotsAtCommandCenter: + GameState.setTSAState(kRobotsAtFrontDoor); + _sprite2.setCurrentFrameIndex(kRedirectionNewTargetSprite); + startExtraSequence(kTSA0BRobotsFromCommandCenterToFrontDoor, kExtraCompletedFlag, kFilterNoInput); + break; + case kRobotsAtFrontDoor: + // Nothing + break; + case kRobotsAtReadyRoom: + GameState.setTSAState(kRobotsAtFrontDoor); + _sprite2.setCurrentFrameIndex(kRedirectionNewTargetSprite); + startExtraSequence(kTSA0BRobotsFromReadyRoomToFrontDoor, kExtraCompletedFlag, kFilterNoInput); + break; + } +} + +void FullTSA::updateCursor(const Common::Point where, const Hotspot *cursorSpot) { + if (cursorSpot) { + switch (cursorSpot->getObjectID()) { + case kTSA0BEastMonitorSpotID: + case kTSA0BNorthMonitorSpotID: + case kTSA0BWestMonitorSpotID: + case kTSA22EastMonitorSpotID: + case kTSA23WestMonitorSpotID: + _vm->_cursor->setCurrentFrameIndex(1); + return; + case kTSA0BEastMonitorOutSpotID: + case kTSA0BNorthMonitorOutSpotID: + case kTSA0BWestMonitorOutSpotID: + _vm->_cursor->setCurrentFrameIndex(2); + return; + } + } + + Neighborhood::updateCursor(where, cursorSpot); +} + +Common::String FullTSA::getNavMovieName() { + return "Images/TSA/Full TSA.movie"; +} + +Common::String FullTSA::getSoundSpotsName() { + return "Sounds/TSA/TSA Spots"; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/tsa/fulltsa.h b/engines/pegasus/neighborhood/tsa/fulltsa.h new file mode 100644 index 0000000000..a646d57e6c --- /dev/null +++ b/engines/pegasus/neighborhood/tsa/fulltsa.h @@ -0,0 +1,159 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_NEIGHBORHOOD_TSA_FULLTSA_H +#define PEGASUS_NEIGHBORHOOD_TSA_FULLTSA_H + +#include "pegasus/neighborhood/neighborhood.h" + +namespace Pegasus { + +class RipTimer : public IdlerAnimation { +public: + RipTimer(const DisplayElementID id) : IdlerAnimation(id) {} + virtual ~RipTimer() {} + + void initImage(); + void releaseImage(); + + void draw(const Common::Rect &); + +protected: + void timeChanged(const TimeValue); + + CoordType _middle; + Surface _timerImage; +}; + +// Room IDs. + +static const RoomID kTSA00 = 0; +static const RoomID kTSA22Red = 28; +static const RoomID kTSA37 = 42; + +class FullTSA : public Neighborhood { +public: + FullTSA(InputHandler *, PegasusEngine *); + virtual ~FullTSA() {} + + virtual void init(); + + void start(); + + virtual uint16 getDateResID() const; + + void flushGameState(); + + void checkContinuePoint(const RoomID, const DirectionConstant); + + bool canSolve(); + void doSolve(); + + void updateCursor(const Common::Point, const Hotspot *); + +protected: + enum { + kTSAPrivateLogReaderOpenFlag, + kTSAPrivateKeyVaultOpenFlag, + kTSAPrivateChipVaultOpenFlag, + kTSAPrivatePlayingLeftComparisonFlag, + kTSAPrivatePlayingRightComparisonFlag, + kTSAPrivateSeenRobotWarningFlag, + kNumTSAPrivateFlags + }; + + Common::String getBriefingMovie(); + Common::String getEnvScanMovie(); + uint getNumHints(); + Common::String getHintMovie(uint); + void loadAmbientLoops(); + virtual void clickInHotspot(const Input &, const Hotspot *); + + virtual int16 getStaticCompassAngle(const RoomID, const DirectionConstant); + void activateOneHotspot(HotspotInfoTable::Entry &, Hotspot *spot); + virtual void activateHotspots(); + void getExitCompassMove(const ExitTable::Entry &, FaderMoveSpec &); + void dropItemIntoRoom(Item *, Hotspot *); + void downButton(const Input &); + void startDoorOpenMovie(const TimeValue, const TimeValue); + TimeValue getViewTime(const RoomID, const DirectionConstant); + void findSpotEntry(const RoomID, const DirectionConstant, SpotFlags, SpotTable::Entry &); + void turnTo(const DirectionConstant); + CanMoveForwardReason canMoveForward(ExitTable::Entry &); + CanOpenDoorReason canOpenDoor(DoorTable::Entry &); + void bumpIntoWall(); + void initializeTBPMonitor(const int, const ExtraID); + void playTBPMonitor(); + void getExtraCompassMove(const ExtraTable::Entry &, FaderMoveSpec &); + Hotspot *getItemScreenSpot(Item *, DisplayElement *); + void openDoor(); + void turnRight(); + void turnLeft(); + void closeDoorOffScreen(const RoomID, const DirectionConstant); + void playExtraMovie(const ExtraTable::Entry &, const NotificationFlags, const InputBits interruptionInput); + void handleInput(const Input &, const Hotspot *); + void arriveAtTSA25Red(); + void startUpComparisonMonitor(); + void shutDownComparisonMonitor(); + void initializeComparisonMonitor(const int, const ExtraID); + void playLeftComparison(); + void playRightComparison(); + void startRobotGame(); + void setOffRipAlarm(); + uint getHistoricalLogIndex(); + void startUpRobotMonitor(); + void shutDownRobotMonitor(); + void pickedUpItem(Item *item); + void arriveFromPrehistoric(); + + void arriveFromNorad(); + void arriveFromMars(); + void arriveFromWSC(); + + InputBits getInputFilter(); + void arriveAt(const RoomID, const DirectionConstant); + void initializePegasusButtons(bool); + void releaseSprites(); + void showMainJumpMenu(); + void arriveAtTSA37(); + void receiveNotification(Notification *, const NotificationFlags); + void checkRobotLocations(const RoomID, const DirectionConstant); + void getExtraEntry(const uint32, ExtraTable::Entry &); + + Sprite _sprite1, _sprite2, _sprite3; + FuseFunction _utilityFuse; + RipTimer _ripTimer; + + FlagsArray<byte, kNumTSAPrivateFlags> _privateFlags; + + Common::String getNavMovieName(); + Common::String getSoundSpotsName(); + + void dieUncreatedInTSA(); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/tsa/tinytsa.cpp b/engines/pegasus/neighborhood/tsa/tinytsa.cpp new file mode 100644 index 0000000000..4f109620c1 --- /dev/null +++ b/engines/pegasus/neighborhood/tsa/tinytsa.cpp @@ -0,0 +1,453 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/energymonitor.h" +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/biochips/aichip.h" +#include "pegasus/items/biochips/opticalchip.h" +#include "pegasus/neighborhood/mars/constants.h" +#include "pegasus/neighborhood/norad/constants.h" +#include "pegasus/neighborhood/tsa/tinytsa.h" +#include "pegasus/neighborhood/wsc/wsc.h" + +namespace Pegasus { + +static const int16 kCompassShift = 30; + +static const TimeScale kTinyTSAMovieScale = 600; +static const TimeScale kTinyTSAFramesPerSecond = 15; +static const TimeScale kTinyTSAFrameDuration = 40; + +// Alternate IDs. +static const AlternateID kAltTinyTSANormal = 0; + +// Hot Spot Activation IDs. +static const HotSpotActivationID kActivationTinyTSAJumpToNorad = 1; +static const HotSpotActivationID kActivationTinyTSAJumpToMars = 2; +static const HotSpotActivationID kActivationTinyTSAJumpToWSC = 3; +static const HotSpotActivationID kActivationTinyTSAReadyForJumpMenu = 4; +static const HotSpotActivationID kActivationTinyTSAMainJumpMenu = 5; + +// Hot Spot IDs. +static const HotSpotID kTinyTSA37NorthJumpToNoradSpotID = 5000; +static const HotSpotID kTinyTSA37NorthCancelNoradSpotID = 5001; +static const HotSpotID kTinyTSA37NorthJumpToMarsSpotID = 5002; +static const HotSpotID kTinyTSA37NorthCancelMarsSpotID = 5003; +static const HotSpotID kTinyTSA37NorthJumpToWSCSpotID = 5004; +static const HotSpotID kTinyTSA37NorthCancelWSCSpotID = 5005; +static const HotSpotID kTinyTSA37NorthJumpMenuSpotID = 5006; +static const HotSpotID kTinyTSA37NorthNoradMenuSpotID = 5007; +static const HotSpotID kTinyTSA37NorthMarsMenuSpotID = 5008; +static const HotSpotID kTinyTSA37NorthWSCMenuSpotID = 5009; + +// Extra sequence IDs. +static const ExtraID kTinyTSA37PegasusDepart = 0; +static const ExtraID kTinyTSA37TimeJumpToPegasus = 1; +static const ExtraID kTinyTSA37RecallToDownload = 2; +static const ExtraID kTinyTSA37ExitHilited = 3; +static const ExtraID kTinyTSA37ExitToHorse = 4; +static const ExtraID kTinyTSA37JumpMenu000 = 5; +static const ExtraID kTinyTSA37JumpMenu001 = 6; +static const ExtraID kTinyTSA37JumpMenu010 = 7; +static const ExtraID kTinyTSA37JumpMenu011 = 8; +static const ExtraID kTinyTSA37JumpMenu100 = 9; +static const ExtraID kTinyTSA37JumpMenu101 = 10; +static const ExtraID kTinyTSA37JumpMenu110 = 11; +static const ExtraID kTinyTSA37JumpMenu111 = 12; +static const ExtraID kTinyTSA37JumpToWSCMenu = 13; +static const ExtraID kTinyTSA37CancelWSC = 14; +static const ExtraID kTinyTSA37JumpToWSC = 15; +static const ExtraID kTinyTSA37WSCToAI5 = 16; +static const ExtraID kTinyTSA37PegasusAI5 = 17; +static const ExtraID kTinyTSA37AI5ToWSC = 18; +static const ExtraID kTinyTSA37WSCToDepart = 19; +static const ExtraID kTinyTSA37JumpToMarsMenu = 20; +static const ExtraID kTinyTSA37CancelMars = 21; +static const ExtraID kTinyTSA37JumpToMars = 22; +static const ExtraID kTinyTSA37MarsToAI6 = 23; +static const ExtraID kTinyTSA37PegasusAI6 = 24; +static const ExtraID kTinyTSA37AI6ToMars = 25; +static const ExtraID kTinyTSA37MarsToDepart = 26; +static const ExtraID kTinyTSA37JumpToNoradMenu = 27; +static const ExtraID kTinyTSA37CancelNorad = 28; +static const ExtraID kTinyTSA37JumpToNorad = 29; +static const ExtraID kTinyTSA37NoradToAI7 = 30; +static const ExtraID kTinyTSA37PegasusAI7 = 31; +static const ExtraID kTinyTSA37AI7ToNorad = 32; +static const ExtraID kTinyTSA37NoradToDepart = 33; +static const ExtraID kTinyTSA37EnvironmentalScan = 34; +static const ExtraID kTinyTSA37DownloadToMainMenu = 35; +static const ExtraID kTinyTSA37DownloadToOpMemReview = 36; +static const ExtraID kTinyTSA37OpMemReviewToMainMenu = 37; + +TinyTSA::TinyTSA(InputHandler *nextHandler, PegasusEngine *owner) : Neighborhood(nextHandler, owner, "Tiny TSA", kTinyTSAID) { +} + +void TinyTSA::start() { + g_energyMonitor->stopEnergyDraining(); + Neighborhood::start(); +} + +Common::String TinyTSA::getBriefingMovie() { + Common::String movieName = Neighborhood::getBriefingMovie(); + + if (movieName.empty()) { + switch (getCurrentActivation()) { + case kActivationTinyTSAJumpToNorad: + g_AIChip->showBriefingClicked(); + startExtraSequenceSync(kTinyTSA37PegasusAI7, kHintInterruption); + startExtraSequenceSync(kTinyTSA37AI7ToNorad, kFilterNoInput); + g_AIChip->clearClicked(); + movieName = ""; + break; + case kActivationTinyTSAJumpToMars: + g_AIChip->showBriefingClicked(); + startExtraSequenceSync(kTinyTSA37PegasusAI6, kHintInterruption); + startExtraSequenceSync(kTinyTSA37AI6ToMars, kFilterNoInput); + g_AIChip->clearClicked(); + movieName = ""; + break; + case kActivationTinyTSAJumpToWSC: + g_AIChip->showBriefingClicked(); + startExtraSequenceSync(kTinyTSA37PegasusAI5, kHintInterruption); + startExtraSequenceSync(kTinyTSA37AI5ToWSC, kFilterNoInput); + g_AIChip->clearClicked(); + movieName = ""; + break; + default: + movieName = "Images/AI/TSA/XT04"; + break; + } + } + + return movieName; +} + +Common::String TinyTSA::getEnvScanMovie() { + Common::String movieName = Neighborhood::getEnvScanMovie(); + + if (movieName.empty()) { + g_AIChip->showEnvScanClicked(); + startExtraSequenceSync(kTinyTSA37EnvironmentalScan, kHintInterruption); + + switch (getCurrentActivation()) { + case kActivationTinyTSAJumpToNorad: + startExtraSequenceSync(kTinyTSA37AI7ToNorad, kFilterNoInput); + showExtraView(kTinyTSA37JumpToNoradMenu); + break; + case kActivationTinyTSAJumpToMars: + startExtraSequenceSync(kTinyTSA37AI6ToMars, kFilterNoInput); + showExtraView(kTinyTSA37JumpToMarsMenu); + break; + case kActivationTinyTSAJumpToWSC: + startExtraSequenceSync(kTinyTSA37AI5ToWSC, kFilterNoInput); + showExtraView(kTinyTSA37JumpToWSCMenu); + break; + default: + showMainJumpMenu(); + break; + } + + g_AIChip->clearClicked(); + } + + return movieName; +} + +void TinyTSA::loadAmbientLoops() { + loadLoopSound1("Sounds/TSA/T01NAE.NEW.22K.AIFF"); +} + +int16 TinyTSA::getStaticCompassAngle(const RoomID room, const DirectionConstant dir) { + return Neighborhood::getStaticCompassAngle(room, dir) - kCompassShift; +} + +uint16 TinyTSA::getDateResID() const { + return kDate2318ID; +} + +InputBits TinyTSA::getInputFilter() { + // Can't move forward... + return Neighborhood::getInputFilter() & ~(kFilterUpButton | kFilterUpAuto); +} + +void TinyTSA::clickInHotspot(const Input &input, const Hotspot *clickedSpot) { + if (clickedSpot) { + switch (clickedSpot->getObjectID()) { + case kTinyTSA37NorthJumpMenuSpotID: + // This hotspot isn't accessable from Tiny TSA + warning("jump menu spot"); + return; + case kTinyTSA37NorthJumpToNoradSpotID: + GameState.setTSAState(kPlayerOnWayToNorad); + requestExtraSequence(kTinyTSA37JumpToNorad, 0, kFilterNoInput); + if (!GameState.getBeenToNorad()) { + requestExtraSequence(kTinyTSA37NoradToAI7, 0, kFilterNoInput); + requestExtraSequence(kTinyTSA37PegasusAI7, 0, kFilterNoInput); + requestExtraSequence(kTinyTSA37AI7ToNorad, 0, kFilterNoInput); + GameState.setBeenToNorad(true); + } + + requestExtraSequence(kTinyTSA37NoradToDepart, 0, kFilterNoInput); + requestExtraSequence(kTinyTSA37PegasusDepart, kExtraCompletedFlag, kFilterNoInput); + return; + case kTinyTSA37NorthJumpToMarsSpotID: + GameState.setTSAState(kPlayerOnWayToMars); + requestExtraSequence(kTinyTSA37JumpToMars, 0, kFilterNoInput); + if (!GameState.getBeenToMars()) { + requestExtraSequence(kTinyTSA37MarsToAI6, 0, kFilterNoInput); + requestExtraSequence(kTinyTSA37PegasusAI6, 0, kFilterNoInput); + requestExtraSequence(kTinyTSA37AI6ToMars, 0, kFilterNoInput); + GameState.setBeenToMars(true); + } + + requestExtraSequence(kTinyTSA37MarsToDepart, 0, kFilterNoInput); + requestExtraSequence(kTinyTSA37PegasusDepart, kExtraCompletedFlag, kFilterNoInput); + return; + case kTinyTSA37NorthJumpToWSCSpotID: + GameState.setTSAState(kPlayerOnWayToWSC); + requestExtraSequence(kTinyTSA37JumpToWSC, 0, kFilterNoInput); + if (!GameState.getBeenToWSC()) { + requestExtraSequence(kTinyTSA37WSCToAI5, 0, kFilterNoInput); + requestExtraSequence(kTinyTSA37PegasusAI5, 0, kFilterNoInput); + requestExtraSequence(kTinyTSA37AI5ToWSC, 0, kFilterNoInput); + GameState.setBeenToWSC(true); + } + + requestExtraSequence(kTinyTSA37WSCToDepart, 0, kFilterNoInput); + requestExtraSequence(kTinyTSA37PegasusDepart, kExtraCompletedFlag, kFilterNoInput); + return; + } + } + + Neighborhood::clickInHotspot(input, clickedSpot); +} + +void TinyTSA::showMainJumpMenu() { + ExtraID jumpMenuView = kTinyTSA37JumpMenu000; + + if (GameState.getNoradFinished()) + jumpMenuView += 4; + if (GameState.getMarsFinished()) + jumpMenuView += 2; + if (GameState.getWSCFinished()) + jumpMenuView += 1; + + showExtraView(jumpMenuView); + setCurrentActivation(kActivationTinyTSAMainJumpMenu); +} + +void TinyTSA::checkContinuePoint(const RoomID, const DirectionConstant) { + makeContinuePoint(); +} + +void TinyTSA::arriveAt(const RoomID room, const DirectionConstant direction) { + Neighborhood::arriveAt(room, direction); + + switch (GameState.getTSAState()) { + case kPlayerOnWayToNorad: + case kPlayerOnWayToMars: + case kPlayerOnWayToWSC: + startExtraSequence(kTinyTSA37TimeJumpToPegasus, kExtraCompletedFlag, kFilterNoInput); + break; + case kPlayerLockedInPegasus: + showMainJumpMenu(); + break; + } +} + +void TinyTSA::receiveNotification(Notification *notification, const NotificationFlags flags) { + ExtraID lastExtra = _lastExtra; + + Neighborhood::receiveNotification(notification, flags); + + if ((flags & kExtraCompletedFlag) != 0) { + // Only allow input if we're not in the middle of series of queue requests. + if (actionQueueEmpty()) + _interruptionFilter = kFilterAllInput; + + switch (lastExtra) { + case kTinyTSA37PegasusDepart: + _vm->setLastEnergyValue(kFullEnergy); + + switch (GameState.getTSAState()) { + case kPlayerOnWayToNorad: + _vm->jumpToNewEnvironment(kNoradAlphaID, kNorad01, kSouth); + GameState.setNoradSeenTimeStream(false); + GameState.setNoradGassed(true); + GameState.setNoradFillingStationOn(false); + GameState.setNoradN22MessagePlayed(false); + GameState.setNoradPlayedGlobeGame(false); + GameState.setNoradBeatRobotWithClaw(false); + GameState.setNoradBeatRobotWithDoor(false); + GameState.setNoradRetScanGood(false); + GameState.setNoradWaitingForLaser(false); + GameState.setNoradSubRoomPressure(9); + GameState.setNoradSubPrepState(kSubNotPrepped); + break; + case kPlayerOnWayToMars: + _vm->jumpToNewEnvironment(kMarsID, kMars0A, kNorth); + GameState.setMarsSeenTimeStream(false); + GameState.setMarsHeardUpperPodMessage(false); + GameState.setMarsRobotThrownPlayer(false); + GameState.setMarsHeardCheckInMessage(false); + GameState.setMarsPodAtUpperPlatform(false); + GameState.setMarsSeenThermalScan(false); + GameState.setMarsArrivedBelow(false); + GameState.setMarsSeenRobotAtReactor(false); + GameState.setMarsAvoidedReactorRobot(false); + GameState.setMarsLockFrozen(false); + GameState.setMarsLockBroken(false); + GameState.setMarsSecurityDown(false); + GameState.setMarsAirlockOpen(false); + GameState.setMarsReadyForShuttleTransport(false); + GameState.setMarsFinishedCanyonChase(false); + GameState.setMarsThreadedMaze(false); + break; + case kPlayerOnWayToWSC: + _vm->jumpToNewEnvironment(kWSCID, kWSC01, kWest); + GameState.setWSCSeenTimeStream(false); + GameState.setWSCPoisoned(false); + GameState.setWSCAnsweredAboutDart(false); + GameState.setWSCDartInAnalyzer(false); + GameState.setWSCRemovedDart(false); + GameState.setWSCAnalyzerOn(false); + GameState.setWSCAnalyzedDart(false); + GameState.setWSCPickedUpAntidote(false); + GameState.setWSCSawMorph(false); + GameState.setWSCDesignedAntidote(false); + GameState.setWSCOfficeMessagesOpen(false); + GameState.setWSCSeenNerd(false); + GameState.setWSCHeardPage1(false); + GameState.setWSCHeardPage2(false); + GameState.setWSCHeardCheckIn(false); + GameState.setWSCDidPlasmaDodge(false); + GameState.setWSCSeenSinclairLecture(false); + GameState.setWSCBeenAtWSC93(false); + GameState.setWSCCatwalkDark(false); + GameState.setWSCRobotDead(false); + GameState.setWSCRobotGone(false); + break; + }; + break; + case kTinyTSA37TimeJumpToPegasus: + if (g_energyMonitor) + g_energyMonitor->stopEnergyDraining(); + + switch (GameState.getTSAState()) { + case kPlayerOnWayToNorad: + arriveFromNorad(); + break; + case kPlayerOnWayToMars: + arriveFromMars(); + break; + case kPlayerOnWayToWSC: + arriveFromWSC(); + break; + default: + break; + } + break; + case kTinyTSA37DownloadToOpMemReview: + switch (GameState.getTSAState()) { + case kPlayerOnWayToNorad: + g_opticalChip->playOpMemMovie(kPoseidonSpotID); + break; + case kPlayerOnWayToMars: + g_opticalChip->playOpMemMovie(kAriesSpotID); + break; + case kPlayerOnWayToWSC: + g_opticalChip->playOpMemMovie(kMercurySpotID); + break; + } + + requestExtraSequence(kTinyTSA37OpMemReviewToMainMenu, kExtraCompletedFlag, kFilterNoInput); + break; + case kTinyTSA37DownloadToMainMenu: + case kTinyTSA37OpMemReviewToMainMenu: + GameState.setTSAState(kPlayerLockedInPegasus); + showMainJumpMenu(); + makeContinuePoint(); + break; + case kTinyTSA37JumpToNoradMenu: + setCurrentActivation(kActivationTinyTSAJumpToNorad); + break; + case kTinyTSA37JumpToMarsMenu: + setCurrentActivation(kActivationTinyTSAJumpToMars); + break; + case kTinyTSA37JumpToWSCMenu: + setCurrentActivation(kActivationTinyTSAJumpToWSC); + break; + case kTinyTSA37CancelNorad: + case kTinyTSA37CancelMars: + case kTinyTSA37CancelWSC: + showMainJumpMenu(); + break; + } + } + + g_AIArea->checkMiddleArea(); +} + +void TinyTSA::arriveFromNorad() { + requestExtraSequence(kTinyTSA37RecallToDownload, 0, kFilterNoInput); + + if (GameState.getNoradFinished() && !GameState.getScoringFinishedNorad()) { + GameState.setScoringFinishedNorad(); + requestExtraSequence(kTinyTSA37DownloadToOpMemReview, kExtraCompletedFlag, kFilterNoInput); + } else { + requestExtraSequence(kTinyTSA37DownloadToMainMenu, kExtraCompletedFlag, kFilterNoInput); + } +} + +void TinyTSA::arriveFromMars() { + requestExtraSequence(kTinyTSA37RecallToDownload, 0, kFilterNoInput); + + if (GameState.getMarsFinished() && !GameState.getScoringFinishedMars()) { + GameState.setScoringFinishedMars(); + requestExtraSequence(kTinyTSA37DownloadToOpMemReview, kExtraCompletedFlag, kFilterNoInput); + } else { + requestExtraSequence(kTinyTSA37DownloadToMainMenu, kExtraCompletedFlag, kFilterNoInput); + } +} + +void TinyTSA::arriveFromWSC() { + requestExtraSequence(kTinyTSA37RecallToDownload, 0, kFilterNoInput); + + if (GameState.getWSCFinished() && !GameState.getScoringFinishedWSC()) { + GameState.setScoringFinishedWSC(); + requestExtraSequence(kTinyTSA37DownloadToOpMemReview, kExtraCompletedFlag, kFilterNoInput); + } else { + requestExtraSequence(kTinyTSA37DownloadToMainMenu, kExtraCompletedFlag, kFilterNoInput); + } +} + +Common::String TinyTSA::getNavMovieName() { + return "Images/TSA/Tiny TSA.movie"; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/tsa/tinytsa.h b/engines/pegasus/neighborhood/tsa/tinytsa.h new file mode 100644 index 0000000000..2dc234675d --- /dev/null +++ b/engines/pegasus/neighborhood/tsa/tinytsa.h @@ -0,0 +1,71 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_NEIGHBORHOOD_TSA_TINYTSA_H +#define PEGASUS_NEIGHBORHOOD_TSA_TINYTSA_H + +#include "pegasus/neighborhood/neighborhood.h" + +namespace Pegasus { + +// Room IDs. + +static const RoomID kTinyTSA37 = 0; + +class TinyTSA : public Neighborhood { +public: + TinyTSA(InputHandler *, PegasusEngine *); + virtual ~TinyTSA() {} + + virtual uint16 getDateResID() const; + + void start(); + + void checkContinuePoint(const RoomID, const DirectionConstant); + +protected: + Common::String getBriefingMovie(); + Common::String getEnvScanMovie(); + void loadAmbientLoops(); + virtual void clickInHotspot(const Input &, const Hotspot *); + + virtual int16 getStaticCompassAngle(const RoomID, const DirectionConstant); + + void arriveFromNorad(); + void arriveFromMars(); + void arriveFromWSC(); + + InputBits getInputFilter(); + void arriveAt(const RoomID, const DirectionConstant); + void showMainJumpMenu(); + void receiveNotification(Notification *, const NotificationFlags); + + Common::String getNavMovieName(); + Common::String getSoundSpotsName() { return ""; } +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/turn.cpp b/engines/pegasus/neighborhood/turn.cpp new file mode 100644 index 0000000000..1157796f55 --- /dev/null +++ b/engines/pegasus/neighborhood/turn.cpp @@ -0,0 +1,63 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "common/debug.h" +#include "common/stream.h" +#include "common/textconsole.h" + +#include "pegasus/neighborhood/turn.h" + +namespace Pegasus { + +void TurnTable::loadFromStream(Common::SeekableReadStream *stream) { + uint32 count = stream->readUint32BE(); + _entries.resize(count); + + for (uint32 i = 0; i < count; i++) { + _entries[i].room = stream->readUint16BE(); + _entries[i].direction = stream->readByte(); + _entries[i].turnDirection = stream->readByte(); + _entries[i].altCode = stream->readByte(); + stream->readByte(); // alignment + _entries[i].endDirection = stream->readByte(); + stream->readByte(); // alignment + debug(0, "Turn[%d]: %d %d %d %d %d", i, _entries[i].room, _entries[i].direction, + _entries[i].turnDirection, _entries[i].altCode, _entries[i].endDirection); + } +} + +void TurnTable::clear() { + _entries.clear(); +} + +TurnTable::Entry TurnTable::findEntry(RoomID room, DirectionConstant direction, TurnDirection turnDirection, AlternateID altCode) { + for (uint32 i = 0; i < _entries.size(); i++) + if (_entries[i].room == room && _entries[i].direction == direction && _entries[i].turnDirection == turnDirection && _entries[i].altCode == altCode) + return _entries[i]; + + return Entry(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/turn.h b/engines/pegasus/neighborhood/turn.h new file mode 100644 index 0000000000..329b03eddb --- /dev/null +++ b/engines/pegasus/neighborhood/turn.h @@ -0,0 +1,69 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_NEIGHBORHOOD_TURN_H +#define PEGASUS_NEIGHBORHOOD_TURN_H + +#include "common/array.h" +#include "common/endian.h" + +#include "pegasus/constants.h" + +namespace Common { + class SeekableReadStream; +} + +namespace Pegasus { + +class TurnTable { +public: + TurnTable() {} + ~TurnTable() {} + + static uint32 getResTag() { return MKTAG('T', 'u', 'r', 'n'); } + + void loadFromStream(Common::SeekableReadStream *stream); + void clear(); + + struct Entry { + Entry() { endDirection = kNoDirection; } + bool isEmpty() { return endDirection == kNoDirection; } + + RoomID room; + DirectionConstant direction; + TurnDirection turnDirection; + AlternateID altCode; + DirectionConstant endDirection; + }; + + Entry findEntry(RoomID room, DirectionConstant direction, TurnDirection turnDirection, AlternateID altCode); + +private: + Common::Array<Entry> _entries; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/view.cpp b/engines/pegasus/neighborhood/view.cpp new file mode 100644 index 0000000000..4e46f5374e --- /dev/null +++ b/engines/pegasus/neighborhood/view.cpp @@ -0,0 +1,60 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "common/debug.h" +#include "common/stream.h" +#include "common/textconsole.h" + +#include "pegasus/neighborhood/view.h" + +namespace Pegasus { + +void ViewTable::loadFromStream(Common::SeekableReadStream *stream) { + uint32 count = stream->readUint32BE(); + _entries.resize(count); + + for (uint32 i = 0; i < count; i++) { + _entries[i].room = stream->readUint16BE(); + _entries[i].direction = stream->readByte(); + _entries[i].altCode = stream->readByte(); + _entries[i].time = stream->readUint32BE(); + debug(0, "View[%d]: %d %d %d %d", i, _entries[i].room, _entries[i].direction, + _entries[i].altCode, _entries[i].time); + } +} + +void ViewTable::clear() { + _entries.clear(); +} + +ViewTable::Entry ViewTable::findEntry(RoomID room, DirectionConstant direction, AlternateID altCode) { + for (uint32 i = 0; i < _entries.size(); i++) + if (_entries[i].room == room && _entries[i].direction == direction && _entries[i].altCode == altCode) + return _entries[i]; + + return Entry(); +} + +} // End of namespace pegasus diff --git a/engines/pegasus/neighborhood/view.h b/engines/pegasus/neighborhood/view.h new file mode 100644 index 0000000000..3397508b61 --- /dev/null +++ b/engines/pegasus/neighborhood/view.h @@ -0,0 +1,68 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_NEIGHBORHOOD_VIEW_H +#define PEGASUS_NEIGHBORHOOD_VIEW_H + +#include "common/array.h" +#include "common/endian.h" + +#include "pegasus/constants.h" + +namespace Common { + class SeekableReadStream; +} + +namespace Pegasus { + +class ViewTable { +public: + ViewTable() {} + ~ViewTable() {} + + static uint32 getResTag() { return MKTAG('V', 'i', 'e', 'w'); } + + void loadFromStream(Common::SeekableReadStream *stream); + void clear(); + + struct Entry { + Entry() { time = 0xffffffff; } + bool isEmpty() { return time == 0xffffffff; } + + RoomID room; + DirectionConstant direction; + AlternateID altCode; + TimeValue time; + }; + + Entry findEntry(RoomID room, DirectionConstant direction, AlternateID altCode); + +private: + Common::Array<Entry> _entries; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/wsc/moleculebin.cpp b/engines/pegasus/neighborhood/wsc/moleculebin.cpp new file mode 100644 index 0000000000..210c0ad313 --- /dev/null +++ b/engines/pegasus/neighborhood/wsc/moleculebin.cpp @@ -0,0 +1,127 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/graphics.h" +#include "pegasus/neighborhood/wsc/moleculebin.h" +#include "pegasus/neighborhood/wsc/wsc.h" + +namespace Pegasus { + +static const CoordType kMoleculeBinWidth = 138; +static const CoordType kMoleculeBinHeight = 128; + +static const CoordType kMoleculeWidth = 66; +static const CoordType kMoleculeHeight = 40; + +static const CoordType kMoleculeBinLeft = kNavAreaLeft + 286; +static const CoordType kMoleculeBinTop = kNavAreaLeft + 96; + +// Layouts: + +MoleculeBin::MoleculeBin() : DisplayElement(kNoDisplayElement) { + _highlightColor = g_system->getScreenFormat().RGBToColor(0xff, 0xff, 102); + _selectedMolecule = -1; +} + +void MoleculeBin::initMoleculeBin() { + if (!isDisplaying()) { + for (int i = 0; i < 6; i++) + _binLayout[i] = i; + + resetBin(); + _binImages.getImageFromPICTFile("Images/World Science Center/Molecules"); + setDisplayOrder(kWSCMoleculeBinOrder); + setBounds(kMoleculeBinLeft, kMoleculeBinTop, kMoleculeBinLeft + kMoleculeBinWidth, + kMoleculeBinTop + kMoleculeBinHeight); + startDisplaying(); + show(); + } +} + +void MoleculeBin::cleanUpMoleculeBin() { + if (isDisplaying()) { + stopDisplaying(); + _binImages.deallocateSurface(); + } +} + +void MoleculeBin::setBinLayout(const uint32 *layout) { + for (int i = 0; i < 6; i++) + _binLayout[i] = layout[i]; +} + +void MoleculeBin::highlightMolecule(const uint32 whichMolecule) { + if (!_moleculeFlags.getFlag(whichMolecule)) { + _moleculeFlags.setFlag(whichMolecule, true); + triggerRedraw(); + } +} + +bool MoleculeBin::isMoleculeHighlighted(uint32 whichMolecule) { + return _moleculeFlags.getFlag(whichMolecule); +} + +void MoleculeBin::selectMolecule(const int whichMolecule) { + if (_selectedMolecule != whichMolecule) { + _selectedMolecule = whichMolecule; + triggerRedraw(); + } +} + +void MoleculeBin::resetBin() { + _moleculeFlags.clearAllFlags(); + _selectedMolecule = -1; + triggerRedraw(); +} + +void MoleculeBin::draw(const Common::Rect &) { + Common::Rect r1(0, 0, kMoleculeWidth, kMoleculeHeight); + Common::Rect r2 = r1; + + for (int i = 0; i < 6; i++) { + r1.moveTo(i * (kMoleculeWidth * 2), 0); + + if (_moleculeFlags.getFlag(_binLayout[i])) + r1.translate(kMoleculeWidth, 0); + + r2.moveTo((_binLayout[i] & 1) * (kMoleculeWidth + 2) + _bounds.left + 2, + (_binLayout[i] >> 1) * (kMoleculeHeight + 2) + _bounds.top + 2); + + _binImages.copyToCurrentPort(r1, r2); + } + + if (_selectedMolecule >= 0) { + r2.moveTo((_selectedMolecule & 1) * (kMoleculeWidth + 2) + _bounds.left + 2, + (_selectedMolecule >> 1) * (kMoleculeHeight + 2) + _bounds.top + 2); + + Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getWorkArea(); + + screen->frameRect(r2, _highlightColor); + r2.grow(1); + screen->frameRect(r2, _highlightColor); + } +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/wsc/moleculebin.h b/engines/pegasus/neighborhood/wsc/moleculebin.h new file mode 100644 index 0000000000..3de4b5ed2a --- /dev/null +++ b/engines/pegasus/neighborhood/wsc/moleculebin.h @@ -0,0 +1,72 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_NEIGHBORHOOD_WSC_MOLECULEBIN_H +#define PEGASUS_NEIGHBORHOOD_WSC_MOLECULEBIN_H + +#include "pegasus/elements.h" +#include "pegasus/surface.h" +#include "pegasus/util.h" + +namespace Pegasus { + +enum { + kMolecule1, + kMolecule2, + kMolecule3, + kMolecule4, + kMolecule5, + kMolecule6 +}; + +class MoleculeBin : public DisplayElement { +public: + MoleculeBin(); + virtual ~MoleculeBin() {} + + void initMoleculeBin(); + void cleanUpMoleculeBin(); + + void setBinLayout(const uint32 *); + + void highlightMolecule(const uint32 whichMolecule); + void selectMolecule(const int whichMolecule); + void resetBin(); + + bool isMoleculeHighlighted(uint32); + +protected: + void draw(const Common::Rect &); + + Surface _binImages; + FlagsArray<byte, kMolecule6 + 1> _moleculeFlags; + int _selectedMolecule; + uint32 _binLayout[6]; + uint32 _highlightColor; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/wsc/wsc.cpp b/engines/pegasus/neighborhood/wsc/wsc.cpp new file mode 100644 index 0000000000..50b7774da4 --- /dev/null +++ b/engines/pegasus/neighborhood/wsc/wsc.cpp @@ -0,0 +1,2546 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/energymonitor.h" +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/biochips/opticalchip.h" +#include "pegasus/items/biochips/shieldchip.h" +#include "pegasus/neighborhood/wsc/wsc.h" + +namespace Pegasus { + +static const CanMoveForwardReason kCantMoveWatchingDiagnosis = kCantMoveLastReason + 1; + +static const CanTurnReason kCantTurnWatchingDiagnosis = kCantTurnLastReason + 1; +static const CanTurnReason kCantTurnWatchingAnalysis = kCantTurnWatchingDiagnosis + 1; +static const CanTurnReason kCantTurnInMoleculeGame = kCantTurnWatchingAnalysis + 1; + +static const TimeScale kMoleculesMovieScale = 600; +static const TimeValue kMoleculeLoopTime = 4 * kMoleculesMovieScale; +static const TimeValue kMoleculeFailTime = 2 * kMoleculesMovieScale; + +enum { + kMoleculeLoop0Time = 0, + kMoleculeFail0Time = kMoleculeLoop0Time + kMoleculeLoopTime, + kMoleculeLoop1Time = kMoleculeFail0Time + kMoleculeFailTime, + kMoleculeFail1Time = kMoleculeLoop1Time + kMoleculeLoopTime, + kMoleculeLoop2Time = kMoleculeFail1Time + kMoleculeFailTime, + kMoleculeFail2Time = kMoleculeLoop2Time + kMoleculeLoopTime, + kMoleculeLoop3Time = kMoleculeFail2Time + kMoleculeFailTime, + kMoleculeFail3Time = kMoleculeLoop3Time + kMoleculeLoopTime, + kMoleculeLoop4Time = kMoleculeFail3Time + kMoleculeFailTime, + kMoleculeFail4Time = kMoleculeLoop4Time + kMoleculeLoopTime, + kMoleculeLoop5Time = kMoleculeFail4Time + kMoleculeFailTime, + kMoleculeFail5Time = kMoleculeLoop5Time + kMoleculeLoopTime, + kMoleculeLoop6Time = kMoleculeFail5Time + kMoleculeFailTime +}; + +static const TimeValue s_moleculeLoopTimes[] = { + kMoleculeLoop0Time, + kMoleculeLoop1Time, + kMoleculeLoop2Time, + kMoleculeLoop3Time, + kMoleculeLoop4Time, + kMoleculeLoop5Time, + kMoleculeLoop6Time +}; + +static const TimeValue s_moleculeFailTimes[] = { + kMoleculeFail0Time, + kMoleculeFail1Time, + kMoleculeFail2Time, + kMoleculeFail3Time, + kMoleculeFail4Time, + kMoleculeFail5Time +}; + +static const int16 kAuditoriumAngleOffset = 5; + +static const int kPlasmaEnergyWithShield = kMaxJMPEnergy * 10 / 100; +static const int kPlasmaEnergyNoShield = kMaxJMPEnergy * 20 / 100; + +static const int kTimerEventPlasmaHit = 0; +static const int kTimerEventPlayerGawkingAtRobot = 1; +static const int kTimerEventPlayerGawkingAtRobot2 = 2; + +static const TimeValue kWSCMolecule1In = 0; +static const TimeValue kWSCMolecule1Out = 937; + +static const TimeValue kWSCMolecule2In = 937; +static const TimeValue kWSCMolecule2Out = 1864; + +static const TimeValue kWSCMolecule3In = 1864; +static const TimeValue kWSCMolecule3Out = 2790; + +static const TimeValue kWSCClick1In = 2790; +static const TimeValue kWSCClick1Out = 2890; + +static const TimeValue kWSCClick2In = 2890; +static const TimeValue kWSCClick2Out = 3059; + +static const TimeValue kWSCClick3In = 3059; +static const TimeValue kWSCClick3Out = 3156; + +static const TimeValue kWSCFlashlightClickIn = 3156; +static const TimeValue kWSCFlashlightClickOut = 3211; + +static const TimeValue kWSCBumpIntoWallIn = 3211; +static const TimeValue kWSCBumpIntoWallOut = 3514; + +static const TimeValue kWSCCantTransportIn = 3514; +static const TimeValue kWSCCantTransportOut = 7791; + +static const TimeValue kHernandezNotHomeIn = 7791; +static const TimeValue kHernandezNotHomeOut = 10199; + +static const TimeValue kWashingtonNotHomeIn = 10199; +static const TimeValue kWashingtonNotHomeOut = 12649; + +static const TimeValue kSullivanNotHomeIn = 12649; +static const TimeValue kSullivanNotHomeOut = 15031; + +static const TimeValue kNakamuraNotHomeIn = 15031; +static const TimeValue kNakamuraNotHomeOut = 17545; + +static const TimeValue kGrailisNotHomeIn = 17545; +static const TimeValue kGrailisNotHomeOut = 19937; + +static const TimeValue kTheriaultNotHomeIn = 19937; +static const TimeValue kTheriaultNotHomeOut = 22395; + +static const TimeValue kGlennerNotHomeIn = 22395; +static const TimeValue kGlennerNotHomeOut = 24770; + +static const TimeValue kSinclairNotHomeIn = 24770; +static const TimeValue kSinclairNotHomeOut = 27328; + +static const TimeValue kWSCLabClosedIn = 27328; +static const TimeValue kWSCLabClosedOut = 28904; + +static const TimeValue kSlidingDoorCloseIn = 28904; +static const TimeValue kSlidingDoorCloseOut = 29295; + +static const TimeValue kSlimyDoorCloseIn = 29295; +static const TimeValue kSlimyDoorCloseOut = 29788; + +static const TimeValue kPaging1In = 29788; +static const TimeValue kPaging1Out = 32501; + +static const TimeValue kPaging2In = 32501; +static const TimeValue kPaging2Out = 34892; + +static const TimeValue kCheckInIn = 34892; +static const TimeValue kCheckInOut = 37789; + +static const TimeValue kDrinkAntidoteIn = 37789; +static const TimeValue kDrinkAntidoteOut = 39725; + +static const TimeScale kWSCMovieScale = 600; +static const TimeScale kWSCFramesPerSecond = 15; +static const TimeScale kWSCFrameDuration = 40; + +// Alternate IDs. +static const AlternateID kAltWSCNormal = 0; +static const AlternateID kAltWSCTookMachineGun = 1; +static const AlternateID kAltWSCW0ZDoorOpen = 2; +static const AlternateID kAltWSCPeopleAtW19North = 3; + +// Room IDs. +static const RoomID kWSC02 = 1; +static const RoomID kWSC03 = 4; +static const RoomID kWSC04 = 5; +static const RoomID kWSC06 = 6; +static const RoomID kWSC07 = 7; +static const RoomID kWSC08 = 8; +static const RoomID kWSC09 = 9; +static const RoomID kWSC10 = 10; +static const RoomID kWSC11 = 11; +static const RoomID kWSC13 = 12; +static const RoomID kWSC14 = 13; +static const RoomID kWSC15 = 14; +static const RoomID kWSC16 = 15; +static const RoomID kWSC17 = 16; +static const RoomID kWSC18 = 17; +static const RoomID kWSC19 = 18; +static const RoomID kWSC20 = 19; +static const RoomID kWSC21 = 20; +static const RoomID kWSC22 = 21; +static const RoomID kWSC23 = 22; +static const RoomID kWSC24 = 23; +static const RoomID kWSC25 = 24; +static const RoomID kWSC26 = 25; +static const RoomID kWSC27 = 26; +static const RoomID kWSC28 = 27; +static const RoomID kWSC29 = 28; +static const RoomID kWSC31 = 29; +static const RoomID kWSC32 = 30; +static const RoomID kWSC33 = 31; +static const RoomID kWSC34 = 32; +static const RoomID kWSC35 = 33; +static const RoomID kWSC36 = 34; +static const RoomID kWSC37 = 35; +static const RoomID kWSC38 = 36; +static const RoomID kWSC39 = 37; +static const RoomID kWSC40 = 38; +static const RoomID kWSC41 = 39; +static const RoomID kWSC42 = 40; +static const RoomID kWSC43 = 41; +static const RoomID kWSC44 = 42; +static const RoomID kWSC45 = 43; +static const RoomID kWSC46 = 44; +static const RoomID kWSC47 = 45; +static const RoomID kWSC48 = 46; +static const RoomID kWSC49 = 47; +static const RoomID kWSC50 = 48; +static const RoomID kWSC52 = 49; +static const RoomID kWSC53 = 50; +static const RoomID kWSC54 = 51; +static const RoomID kWSC55 = 52; +static const RoomID kWSC56 = 53; +static const RoomID kWSC57 = 54; +static const RoomID kWSC58 = 55; +static const RoomID kWSC60 = 56; +static const RoomID kWSC60East = 57; +static const RoomID kWSC60North = 58; +static const RoomID kWSC61 = 59; +static const RoomID kWSC61South = 60; +static const RoomID kWSC61West = 61; +static const RoomID kWSC63 = 63; +static const RoomID kWSC64 = 64; +static const RoomID kWSC65 = 65; +static const RoomID kWSC65Screen = 66; +static const RoomID kWSC66 = 67; +static const RoomID kWSC67 = 68; +static const RoomID kWSC68 = 69; +static const RoomID kWSC69 = 70; +static const RoomID kWSC70 = 71; +static const RoomID kWSC71 = 72; +static const RoomID kWSC72 = 73; +static const RoomID kWSC73 = 74; +static const RoomID kWSC74 = 75; +static const RoomID kWSC75 = 76; +static const RoomID kWSC0Z = 77; +static const RoomID kWSC76 = 78; +static const RoomID kWSC77 = 79; +static const RoomID kWSC78 = 80; +static const RoomID kWSC79 = 81; +static const RoomID kWSC80 = 82; +static const RoomID kWSC81 = 83; +static const RoomID kWSC82 = 84; +static const RoomID kWSC83 = 85; +static const RoomID kWSC84 = 86; +static const RoomID kWSC85 = 87; +static const RoomID kWSC86 = 88; +static const RoomID kWSC87 = 89; +static const RoomID kWSC88 = 90; +static const RoomID kWSC89 = 91; +static const RoomID kWSC90 = 92; +static const RoomID kWSC91 = 93; +static const RoomID kWSC92 = 94; +static const RoomID kWSC93 = 95; +static const RoomID kWSC94 = 96; +static const RoomID kWSC95 = 97; +static const RoomID kWSC96 = 98; +static const RoomID kWSC97 = 99; +static const RoomID kWSC98 = 100; +static const RoomID kWSCDeathRoom = 101; + +// Hot Spot Activation IDs. +static const HotSpotActivationID kActivationZoomedInToAnalyzer = 1; +static const HotSpotActivationID kActivationShotByRobot = 2; +static const HotSpotActivationID kActivationWarnedAboutPoison = 3; +static const HotSpotActivationID kActivationMorphScreenOff = 4; +static const HotSpotActivationID kActivationReadyForMorph = 5; +static const HotSpotActivationID kActivationMorphLooping = 6; +static const HotSpotActivationID kActivationMorphInterrupted = 7; +static const HotSpotActivationID kActivationW03NorthOff = 8; +static const HotSpotActivationID kActivationW03NorthReadyForInstructions = 9; +static const HotSpotActivationID kActivationW03NorthSawInstructions = 10; +static const HotSpotActivationID kActivationW03NorthInGame = 11; +static const HotSpotActivationID kActivationReadyForSynthesis = 12; +static const HotSpotActivationID kActivationSynthesizerLooping = 13; +static const HotSpotActivationID kActivationReadyForMap = 14; +static const HotSpotActivationID kActivationSinclairOfficeLocked = 15; +static const HotSpotActivationID kActivationW58SouthDoorLocked = 16; +static const HotSpotActivationID kActivationW61SouthOff = 17; +static const HotSpotActivationID kActivationW61SouthOn = 18; +static const HotSpotActivationID kActivationW61MessagesOff = 19; +static const HotSpotActivationID kActivationW61MessagesOn = 20; +static const HotSpotActivationID kActivationWSCRobotHeadOpen = 21; +static const HotSpotActivationID kActivationRobotTurning = 22; +static const HotSpotActivationID kActivationRobotDead = 23; +static const HotSpotActivationID kActivationRobotGone = 24; + +// Hot Spot IDs. +static const HotSpotID kWSCDropDartSpotID = 5000; +static const HotSpotID kWSCTurnOnAnalyzerSpotID = 5001; +static const HotSpotID kWSCAnalyzerScreenSpotID = 5002; +static const HotSpotID kWSCSpinRobotSpotID = 5003; +static const HotSpotID kWSC01YesSpotID = 5004; +static const HotSpotID kWSC01NoSpotID = 5005; +static const HotSpotID kWSC01AcknowledgeWarningSpotID = 5006; +static const HotSpotID kWSC02SouthMorphSpotID = 5007; +static const HotSpotID kWSC02SouthMessagesSpotID = 5008; +static const HotSpotID kWSC02SouthMorphOutSpotID = 5009; +static const HotSpotID kWSC02ActivateMorphScreenSpotID = 5010; +static const HotSpotID kWSC02SouthStartMorphSpotID = 5011; +static const HotSpotID kWSC02SouthInterruptMorphSpotID = 5012; +static const HotSpotID kWSC02SouthMorphFinishedSpotID = 5013; +static const HotSpotID kWSC02SouthTakeArgonSpotID = 5014; +static const HotSpotID kWSC02SouthMessagesOutSpotID = 5015; +static const HotSpotID kWSC02SouthTakeNitrogenSpotID = 5016; +static const HotSpotID kWSC02SouthPlayMessagesSpotID = 5017; +static const HotSpotID kWSC03NorthActivateScreenSpotID = 5018; +static const HotSpotID kWSC03NorthBuildMoleculeSpotID = 5019; +static const HotSpotID kWSC03NorthProceedSpotID = 5020; +static const HotSpotID kWSC03NorthMolecule1SpotID = 5021; +static const HotSpotID kWSC03NorthMolecule2SpotID = 5022; +static const HotSpotID kWSC03NorthMolecule3SpotID = 5023; +static const HotSpotID kWSC03NorthMolecule4SpotID = 5024; +static const HotSpotID kWSC03NorthMolecule5SpotID = 5025; +static const HotSpotID kWSC03NorthMolecule6SpotID = 5026; +static const HotSpotID kWSC03SouthActivateSynthesizerSpotID = 5027; +static const HotSpotID kWSC03SouthPickUpAntidoteSpotID = 5028; +static const HotSpotID kWSC07SouthMapSpotID = 5029; +static const HotSpotID kW42EastUnlockDoorSpotID = 5030; +static const HotSpotID kW56NorthMapSpotID = 5031; +static const HotSpotID kW58SouthPryDoorSpotID = 5032; +static const HotSpotID kWSC60EastSpotID = 5033; +static const HotSpotID kWSC60NorthSpotID = 5034; +static const HotSpotID kWSC60EastOutSpotID = 5035; +static const HotSpotID kWSC60NorthOutSpotID = 5036; +static const HotSpotID kWSC61EastSpotID = 5037; +static const HotSpotID kWSC61SouthSpotID = 5038; +static const HotSpotID kW61SouthMachineGunSpotID = 5039; +static const HotSpotID kW61SouthDropMachineGunSpotID = 5040; +static const HotSpotID kWSC61WestSpotID = 5041; +static const HotSpotID kWSC61SouthOutSpotID = 5042; +static const HotSpotID kW61SouthActivateSpotID = 5043; +static const HotSpotID kW61SmartAlloysSpotID = 5044; +static const HotSpotID kW61MorphingSpotID = 5045; +static const HotSpotID kW61TimeBendingSpotID = 5046; +static const HotSpotID kWSC61WestOutSpotID = 5047; +static const HotSpotID kW61TurnOnMessagesSpotID = 5048; +static const HotSpotID kW61WhiteMessageSpotID = 5049; +static const HotSpotID kW61WalchekMessageSpotID = 5050; +static const HotSpotID kWSC65SouthScreenSpotID = 5051; +static const HotSpotID kWSC65SouthScreenOutSpotID = 5052; +static const HotSpotID kW98RetinalChipSpotID = 5053; +static const HotSpotID kW98MapChipSpotID = 5054; +static const HotSpotID kW98OpticalChipSpotID = 5055; +static const HotSpotID kW98DropArgonSpotID = 5056; +static const HotSpotID kW98GrabCableSpotID = 5057; +static const HotSpotID kW98OpenRobotSpotID = 5058; +static const HotSpotID kW98StunGunSpotID = 5059; + +// Extra sequence IDs. +static const ExtraID kWSCArrivalFromTSA = 0; +static const ExtraID kWSCShotByRobot = 1; +static const ExtraID kWSCDartScan1 = 2; +static const ExtraID kWSCDartScan2 = 3; +static const ExtraID kWSCDartScanNo = 4; +static const ExtraID kWSCDartScan3 = 5; +static const ExtraID kWSCAnalyzerPowerUp = 6; +static const ExtraID kWSCAnalyzerPowerUpWithDart = 7; +static const ExtraID kWSCDropDartIntoAnalyzer = 8; +static const ExtraID kWSCAnalyzeDart = 9; +static const ExtraID kWSCZoomOutFromAnalyzer = 10; +static const ExtraID kWSCSpinRobot = 11; +static const ExtraID kWSC02MorphZoomNoArgon = 12; +static const ExtraID kWSC02MessagesZoomNoNitrogen = 13; +static const ExtraID kWSC02ZoomOutNoArgon = 14; +static const ExtraID kWSC02TurnOnMorphScreen = 15; +static const ExtraID kWSC02DropToMorphExperiment = 16; +static const ExtraID kWSC02MorphLoop = 17; +static const ExtraID kWSC02MorphInterruption = 18; +static const ExtraID kWSC02MorphFinished = 19; +static const ExtraID kWSC02TurnOffMorphScreen = 20; +static const ExtraID kWSC02SouthViewNoArgon = 21; +static const ExtraID kMessagesMovedToOffice = 22; +static const ExtraID kMessagesOff = 23; +static const ExtraID kMessagesZoomOutNoNitrogen = 24; +static const ExtraID kMessagesMovedToOfficeNoNitrogen = 25; +static const ExtraID kMessagesOffNoNitrogen = 26; +static const ExtraID kMessagesViewNoNitrogen = 27; +static const ExtraID kMessagesViewMachineOnNoNitrogen = 28; +static const ExtraID kW03NorthActivate = 29; +static const ExtraID kW03NorthGetData = 30; +static const ExtraID kW03NorthInstructions = 31; +static const ExtraID kW03NorthPrepMolecule1 = 32; +static const ExtraID kW03NorthPrepMolecule2 = 33; +static const ExtraID kW03NorthPrepMolecule3 = 34; +static const ExtraID kW03NorthFinishSynthesis = 35; +static const ExtraID kW03SouthCreateAntidote = 36; +static const ExtraID kW03SouthAntidoteLoop = 37; +static const ExtraID kW03SouthDeactivate = 38; +static const ExtraID kW03SouthViewNoAntidote = 39; +static const ExtraID kWSC07SouthMap = 40; +static const ExtraID kW17WestPeopleCrossing = 41; +static const ExtraID kW17WestPeopleCrossingView = 42; +static const ExtraID kW21SouthPeopleCrossing = 43; +static const ExtraID kW24SouthPeopleCrossing = 44; +static const ExtraID kW34EastPeopleCrossing = 45; +static const ExtraID kW36WestPeopleCrossing = 46; +static const ExtraID kW38NorthPeopleCrossing = 47; +static const ExtraID kW46SouthPeopleCrossing = 48; +static const ExtraID kW49NorthPeopleCrossing = 49; +static const ExtraID kW49NorthPeopleCrossingView = 50; +static const ExtraID kWSC56SouthMap = 51; +static const ExtraID kNerdAtTheDoor1 = 52; +static const ExtraID kNerdAtTheDoor2 = 53; +static const ExtraID kW61SouthZoomInNoGun = 54; +static const ExtraID kW61Brochure = 55; +static const ExtraID kW61SouthScreenOnWithGun = 56; +static const ExtraID kW61SouthScreenOffWithGun = 57; +static const ExtraID kW61SouthSmartAlloysWithGun = 58; +static const ExtraID kW61SouthMorphingWithGun = 59; +static const ExtraID kW61SouthTimeBendingWithGun = 60; +static const ExtraID kW61SouthZoomOutNoGun = 61; +static const ExtraID kW61SouthScreenOnNoGun = 62; +static const ExtraID kW61SouthScreenOffNoGun = 63; +static const ExtraID kW61SouthSmartAlloysNoGun = 64; +static const ExtraID kW61SouthMorphingNoGun = 65; +static const ExtraID kW61SouthTimeBendingNoGun = 66; +static const ExtraID kW61MessagesOn = 67; +static const ExtraID kW61MessagesOff = 68; +static const ExtraID kW61WhiteMessage = 69; +static const ExtraID kW61WalchekMessage = 70; +static const ExtraID kW61WalchekEasterEgg1 = 71; +static const ExtraID kW62SouthPlasmaRobotAppears = 72; +static const ExtraID kW62ZoomToRobot = 73; +static const ExtraID kW62ZoomOutFromRobot = 74; +static const ExtraID kW62PlasmaDodgeSurvive = 75; +static const ExtraID kW62PlasmaDodgeDie = 76; +static const ExtraID kW65SouthSinclairLecture = 77; +static const ExtraID kW73WestPeopleCrossing = 78; +static const ExtraID kW73WestPeopleCrossingView = 79; +static const ExtraID kW0ZSpottedByWomen = 80; +static const ExtraID kW95RobotShoots = 81; +static const ExtraID kW98MorphsToRobot = 82; +static const ExtraID kW98RobotShoots = 83; +static const ExtraID kW98RobotShocked = 84; +static const ExtraID kW98RobotGassed = 85; +static const ExtraID kW98RobotHeadOpensDark = 86; +static const ExtraID kW98RobotHead000Dark = 87; +static const ExtraID kW98RobotHead001Dark = 88; +static const ExtraID kW98RobotHead010Dark = 89; +static const ExtraID kW98RobotHead011Dark = 90; +static const ExtraID kW98RobotHead100Dark = 91; +static const ExtraID kW98RobotHead101Dark = 92; +static const ExtraID kW98RobotHead110Dark = 93; +static const ExtraID kW98RobotHead111Dark = 94; +static const ExtraID kW98RobotHeadClosesDark = 95; +static const ExtraID kW98WestViewWithGunDark = 96; +static const ExtraID kW98WestViewNoGunDark = 97; +static const ExtraID kW98RobotHeadOpensLight = 98; +static const ExtraID kW98RobotHead000Light = 99; +static const ExtraID kW98RobotHead001Light = 100; +static const ExtraID kW98RobotHead010Light = 101; +static const ExtraID kW98RobotHead011Light = 102; +static const ExtraID kW98RobotHead100Light = 103; +static const ExtraID kW98RobotHead101Light = 104; +static const ExtraID kW98RobotHead110Light = 105; +static const ExtraID kW98RobotHead111Light = 106; +static const ExtraID kW98RobotHeadClosesLight = 107; +static const ExtraID kW98WestViewWithGunLight = 108; +static const ExtraID kW98WestViewNoGunLight = 109; + +static const CoordType kMoleculesMovieLeft = kNavAreaLeft + 112; +static const CoordType kMoleculesMovieTop = kNavAreaTop + 40; + +WSC::WSC(InputHandler *nextHandler, PegasusEngine *owner) : Neighborhood(nextHandler, owner, "WSC", kWSCID), + _moleculesMovie(kNoDisplayElement) { + setIsItemTaken(kArgonCanister); + setIsItemTaken(kSinclairKey); + setIsItemTaken(kNitrogenCanister); + setIsItemTaken(kPoisonDart); + setIsItemTaken(kAntidote); + setIsItemTaken(kMachineGun); + setIsItemTaken(kStunGun); + + GameState.setTakenItemID(kArgonPickup, GameState.isTakenItemID(kArgonCanister) && + GameState.isTakenItemID(kSinclairKey)); +} + +uint16 WSC::getDateResID() const { + return kDate2310ID; +} + +void WSC::init() { + Neighborhood::init(); + + _cachedZoomSpot = 0; + _argonSprite = 0; + + // HACK: Fix the drag item for picking up the Sinclair Key Card + HotspotInfoTable::Entry *entry = findHotspotEntry(kWSC02SouthTakeArgonSpotID); + entry->hotspotItem = kArgonPickup; +} + +void WSC::flushGameState() { + g_energyMonitor->saveCurrentEnergyValue(); +} + +void WSC::start() { + if (g_energyMonitor) { + g_energyMonitor->stopEnergyDraining(); + g_energyMonitor->restoreLastEnergyValue(); + _vm->resetEnergyDeathReason(); + g_energyMonitor->startEnergyDraining(); + } + + if (!GameState.getWSCDidPlasmaDodge()) + forceStridingStop(kWSC58, kSouth, kAltWSCNormal); + + Neighborhood::start(); +} + +class PryDoorMessage : public AIPlayMessageAction { +public: + PryDoorMessage() : AIPlayMessageAction("Images/AI/WSC/XW59SD3", false) {} + +protected: + virtual void performAIAction(AIRule *); +}; + +void PryDoorMessage::performAIAction(AIRule *rule) { + if (((PegasusEngine *)g_engine)->playerHasItemID(kShieldBiochip) + && ((PegasusEngine *)g_engine)->getCurrentBiochip()->getObjectID() != kShieldBiochip) + AIPlayMessageAction::performAIAction(rule); +} + +void WSC::setUpAIRules() { + Neighborhood::setUpAIRules(); + + if (g_AIArea) { + AIPlayMessageAction *messageAction = new AIPlayMessageAction("Images/AI/WSC/XW1WB1", false); + AILastExtraCondition *extraCondition = new AILastExtraCondition(kWSCDartScan1); + AIRule *rule = new AIRule(extraCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Globals/XGLOB5A", false); + AILocationCondition *locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kWSC06, kNorth)); + rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Globals/XGLOB5A", false); + locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kWSC10, kWest)); + rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Globals/XGLOB5A", false); + locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kWSC28, kWest)); + rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Globals/XGLOB5A", false); + locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kWSC49, kWest)); + rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Globals/XGLOB5A", false); + locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kWSC65, kSouth)); + rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Globals/XGLOB5A", false); + locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kWSC73, kSouth)); + rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Globals/XGLOB5A", false); + locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kWSC79, kWest)); + rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/WSC/XW59SD1", false); + locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kWSC58, kSouth)); + rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + + PryDoorMessage *pryDoorMessage = new PryDoorMessage(); + AIDoorOpenedCondition *doorCondition = new AIDoorOpenedCondition(MakeRoomView(kWSC58, kSouth)); + rule = new AIRule(doorCondition, pryDoorMessage); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/WSC/XW61E", false); + AIHasItemCondition *itemCondition = new AIHasItemCondition(kMachineGun); + rule = new AIRule(itemCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Globals/XGLOB1E", false); + locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kWSC95, kWest)); + rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + } +} + +Common::String WSC::getBriefingMovie() { + return "Images/AI/WSC/XWO"; +} + +Common::String WSC::getEnvScanMovie() { + RoomID room = GameState.getCurrentRoom(); + + if (room >= kWSC01 && room <= kWSC04) + return "Images/AI/WSC/XWE1"; + else if (room >= kWSC06 && room <= kWSC58) + return "Images/AI/WSC/XWE2"; + else if (room >= kWSC60 && room <= kWSC61West) + return "Images/AI/WSC/XWE3"; + else if (room >= kWSC64 && room <= kWSC98) + return "Images/AI/WSC/XWE4"; + + return "Images/AI/WSC/XWE5"; +} + +uint WSC::getNumHints() { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kWSC10, kWest): + case MakeRoomView(kWSC28, kWest): + case MakeRoomView(kWSC49, kWest): + case MakeRoomView(kWSC65, kSouth): + case MakeRoomView(kWSC75, kSouth): + case MakeRoomView(kWSC79, kWest): + return 2; + case MakeRoomView(kWSC02, kSouth): + if (_vm->getEnergyDeathReason() == kDeathDidntStopPoison && + !_privateFlags.getFlag(kWSCPrivateInMoleculeGameFlag) && + !GameState.getWSCDesignedAntidote()) + return 3; + else if (!GameState.getScoringGotNitrogenCanister() || + !GameState.getScoringGotSinclairKey()) + return 1; + break; + case MakeRoomView(kWSC03, kNorth): + // WORKAROUND: The original game is missing the first two hint movies and + // just plays nothing in its stead. We'll just return that we have one + // hint available. + if (inSynthesizerGame()) + return 1; + + // fall through + case MakeRoomView(kWSC01, kNorth): + case MakeRoomView(kWSC01, kSouth): + case MakeRoomView(kWSC01, kEast): + case MakeRoomView(kWSC01, kWest): + case MakeRoomView(kWSC02, kNorth): + case MakeRoomView(kWSC02, kEast): + case MakeRoomView(kWSC02, kWest): + case MakeRoomView(kWSC02Morph, kNorth): + case MakeRoomView(kWSC02Morph, kEast): + case MakeRoomView(kWSC02Morph, kWest): + case MakeRoomView(kWSC02Messages, kNorth): + case MakeRoomView(kWSC02Messages, kEast): + case MakeRoomView(kWSC02Messages, kWest): + case MakeRoomView(kWSC03, kSouth): + case MakeRoomView(kWSC03, kEast): + case MakeRoomView(kWSC03, kWest): + case MakeRoomView(kWSC04, kNorth): + case MakeRoomView(kWSC04, kSouth): + case MakeRoomView(kWSC04, kEast): + case MakeRoomView(kWSC04, kWest): + if (_vm->getEnergyDeathReason() == kDeathDidntStopPoison && + !_privateFlags.getFlag(kWSCPrivateInMoleculeGameFlag) && + !GameState.getWSCDesignedAntidote()) + return 3; + break; + case MakeRoomView(kWSC02Messages, kSouth): + if (_vm->getEnergyDeathReason() == kDeathDidntStopPoison && + !_privateFlags.getFlag(kWSCPrivateInMoleculeGameFlag) && + !GameState.getWSCDesignedAntidote()) + return 3; + else if (!GameState.getScoringGotNitrogenCanister()) + return 1; + break; + case MakeRoomView(kWSC02Morph, kSouth): + if (_vm->getEnergyDeathReason() == kDeathDidntStopPoison && + !_privateFlags.getFlag(kWSCPrivateInMoleculeGameFlag) && + !GameState.getWSCDesignedAntidote()) + return 3; + else if (!GameState.getScoringGotSinclairKey()) + return 1; + break; + case MakeRoomView(kWSC42, kEast): + if (!GameState.isCurrentDoorOpen()) + return 1; + break; + case MakeRoomView(kWSC58, kSouth): + if (GameState.isCurrentDoorOpen()) { + if (GameState.getWSCDidPlasmaDodge()) + return 0; + else + return 1; + } else if (_vm->playerHasItemID(kCrowbar)) + return 2; + + return 3; + case MakeRoomView(kWSC61, kEast): + if (!GameState.getScoringSawBrochure()) + return 1; + break; + case MakeRoomView(kWSC61, kSouth): + if (!GameState.getScoringSawSinclairEntry1() || + !GameState.getScoringSawSinclairEntry2() || + !GameState.getScoringSawSinclairEntry3()) + return 1; + break; + case MakeRoomView(kWSC98, kWest): + if (getCurrentActivation() == kActivationRobotTurning) + return 1; + break; + } + + return 0; +} + +Common::String WSC::getHintMovie(uint hintNum) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kWSC10, kWest): + case MakeRoomView(kWSC28, kWest): + case MakeRoomView(kWSC49, kWest): + case MakeRoomView(kWSC65, kSouth): + case MakeRoomView(kWSC75, kSouth): + case MakeRoomView(kWSC79, kWest): + if (hintNum == 1) + return "Images/AI/Globals/XGLOB5B"; + + return "Images/AI/Globals/XGLOB5C"; + case MakeRoomView(kWSC02, kSouth): + if (_vm->getEnergyDeathReason() == kDeathDidntStopPoison && + !_privateFlags.getFlag(kWSCPrivateInMoleculeGameFlag) && + !GameState.getWSCDesignedAntidote()) + return Common::String::format("Images/AI/WSC/XWPH%d", hintNum); + + return "Images/AI/Globals/XGLOB1C"; + case MakeRoomView(kWSC61, kEast): + case MakeRoomView(kWSC61, kSouth): + return "Images/AI/Globals/XGLOB1C"; + case MakeRoomView(kWSC42, kEast): + if (_vm->playerHasItemID(kSinclairKey)) + return "Images/AI/Globals/XGLOB1A"; + + return "Images/AI/Globals/XGLOB2C"; + case MakeRoomView(kWSC58, kSouth): + switch (hintNum) { + case 1: + if (GameState.isCurrentDoorOpen()) { + // Only get here if we haven't done the plasma dodge game... + if (_vm->playerHasItemID(kShieldBiochip)) + return "Images/AI/Globals/XGLOB1A"; + else + return "Images/AI/Globals/XGLOB3F"; + } else if (_vm->playerHasItemID(kCrowbar)) { + return "Images/AI/Globals/XGLOB1A"; + } + + return "Images/AI/Globals/XGLOB1B"; + case 2: + // Only get here if the door is still locked... + if (_vm->playerHasItemID(kCrowbar)) + return "Images/AI/WSC/XW59SD2"; + + return "Images/AI/Globals/XGLOB2D"; + case 3: + // Only get here if the door is still locked and we don't have the + // crowbar... + return "Images/AI/WSC/XW59SD2"; + } + break; + case MakeRoomView(kWSC03, kNorth): + // WORKAROUND: The original game is missing the first two hint movies and + // just plays nothing in its stead. We just make it the first hint. + if (inSynthesizerGame()) + return "Images/AI/WSC/XW03NH3"; + + // fall through + case MakeRoomView(kWSC01, kNorth): + case MakeRoomView(kWSC01, kSouth): + case MakeRoomView(kWSC01, kEast): + case MakeRoomView(kWSC01, kWest): + case MakeRoomView(kWSC02, kNorth): + case MakeRoomView(kWSC02, kEast): + case MakeRoomView(kWSC02, kWest): + case MakeRoomView(kWSC02Morph, kNorth): + case MakeRoomView(kWSC02Morph, kEast): + case MakeRoomView(kWSC02Morph, kWest): + case MakeRoomView(kWSC02Messages, kNorth): + case MakeRoomView(kWSC02Messages, kEast): + case MakeRoomView(kWSC02Messages, kWest): + case MakeRoomView(kWSC03, kSouth): + case MakeRoomView(kWSC03, kEast): + case MakeRoomView(kWSC03, kWest): + case MakeRoomView(kWSC04, kNorth): + case MakeRoomView(kWSC04, kSouth): + case MakeRoomView(kWSC04, kEast): + case MakeRoomView(kWSC04, kWest): + // analyzer hint + return Common::String::format("Images/AI/WSC/XWPH%d", hintNum); + case MakeRoomView(kWSC02Messages, kSouth): + case MakeRoomView(kWSC02Morph, kSouth): + if (_vm->getEnergyDeathReason() == kDeathDidntStopPoison && + !_privateFlags.getFlag(kWSCPrivateInMoleculeGameFlag) && + !GameState.getWSCDesignedAntidote()) + // analyzer hint + return Common::String::format("Images/AI/WSC/XWPH%d", hintNum); + + return "Images/AI/Globals/XGLOB1C"; + case MakeRoomView(kWSC98, kWest): + return "Images/AI/WSC/XW98WH2"; + } + + return ""; +} + +void WSC::prepareForAIHint(const Common::String &movieName) { + if (movieName == "Images/AI/WSC/XW98WH2" && isEventTimerRunning()) + pauseTimer(); +} + +void WSC::cleanUpAfterAIHint(const Common::String &movieName) { + if (movieName == "Images/AI/WSC/XW98WH2" && isEventTimerRunning()) + resumeTimer(); +} + +bool WSC::okayToJump() { + if (GameState.getWSCPoisoned()) { + die(kDeathDidntStopPoison); + return false; + } + + bool result = Neighborhood::okayToJump(); + if (!result) + playSpotSoundSync(kWSCCantTransportIn, kWSCCantTransportOut); + + return result; +} + +TimeValue WSC::getViewTime(const RoomID room, const DirectionConstant direction) { + ExtraID viewExtra = 0xffffffff; + ExtraTable::Entry extra; + + switch (MakeRoomView(room, direction)) { + case MakeRoomView(kWSC01, kWest): + if (!GameState.getWSCSeenTimeStream()) { + getExtraEntry(kWSCArrivalFromTSA, extra); + return extra.movieStart; + } else if (GameState.getWSCPoisoned() && !GameState.getWSCAnsweredAboutDart()) { + viewExtra = kWSCDartScan1; + } + break; + case MakeRoomView(kWSC02Morph, kSouth): + if (GameState.isTakenItemID(kArgonPickup) || GameState.isTakenItemID(kArgonCanister)) + viewExtra = kWSC02SouthViewNoArgon; + break; + case MakeRoomView(kWSC02Messages, kSouth): + if (GameState.isTakenItemID(kNitrogenCanister)) { + if (_privateFlags.getFlag(kWSCPrivateLabMessagesOpenFlag)) + viewExtra = kMessagesViewMachineOnNoNitrogen; + else + viewExtra = kMessagesViewNoNitrogen; + } + break; + case MakeRoomView(kWSC03, kSouth): + if (_privateFlags.getFlag(kWSCDraggingAntidoteFlag)) + viewExtra = kW03SouthViewNoAntidote; + break; + case MakeRoomView(kWSC17, kWest): + if (_privateFlags.getFlag(kWSCPrivateNeedPeopleAt17WestFlag)) + viewExtra = kW17WestPeopleCrossingView; + break; + case MakeRoomView(kWSC49, kNorth): + if (_privateFlags.getFlag(kWSCPrivateNeedPeopleAt49NorthFlag)) + viewExtra = kW49NorthPeopleCrossingView; + break; + case MakeRoomView(kWSC73, kWest): + if (_privateFlags.getFlag(kWSCPrivateNeedPeopleAt73WestFlag)) + viewExtra = kW73WestPeopleCrossingView; + break; + case MakeRoomView(kWSC98, kWest): + if (GameState.getWSCRobotDead()) { + if (GameState.getWSCRobotGone()) { + if (GameState.isTakenItemID(kStunGun)) { + if (GameState.getWSCCatwalkDark()) + viewExtra = kW98WestViewNoGunDark; + else + viewExtra = kW98WestViewNoGunLight; + } else { + if (GameState.getWSCCatwalkDark()) + viewExtra = kW98WestViewWithGunDark; + else + viewExtra = kW98WestViewWithGunLight; + } + } else if (_privateFlags.getFlag(kWSCPrivateRobotHeadOpenFlag)) { + if (GameState.getWSCCatwalkDark()) + viewExtra = kW98RobotHead111Dark; + else + viewExtra = kW98RobotHead111Light; + + if (_privateFlags.getFlag(kWSCPrivateGotRetScanChipFlag)) + viewExtra -= 1; + if (_privateFlags.getFlag(kWSCPrivateGotMapChipFlag)) + viewExtra -= 2; + if (_privateFlags.getFlag(kWSCPrivateGotOpticalChipFlag)) + viewExtra -= 4; + } else if (GameState.getWSCRobotDead()) { + // Should only happen on loading a saved game, so it can take its time. + if (GameState.getWSCCatwalkDark()) + viewExtra = kW98RobotShocked; + else + viewExtra = kW98RobotGassed; + } + } + break; + } + + if (viewExtra != 0xffffffff) { + getExtraEntry(viewExtra, extra); + return extra.movieEnd - 1; + } + + return Neighborhood::getViewTime(room, direction); +} + +void WSC::findSpotEntry(const RoomID room, const DirectionConstant direction, SpotFlags flags, SpotTable::Entry &spotEntry) { + switch (MakeRoomView(room, direction)) { + case MakeRoomView(kWSC58, kSouth): + case MakeRoomView(kWSC79, kWest): + if ((flags & kSpotOnTurnMask) != 0) { + spotEntry.clear(); + return; + } + break; + } + + Neighborhood::findSpotEntry(room, direction, flags, spotEntry); +} + +void WSC::getZoomEntry(const HotSpotID id, ZoomTable::Entry &zoomEntry) { + Neighborhood::getZoomEntry(id, zoomEntry); + + ExtraTable::Entry extra; + ExtraID zoomExtra = 0xffffffff; + + switch (id) { + case kWSC02SouthMessagesSpotID: + if (GameState.isTakenItemID(kNitrogenCanister)) + zoomExtra = kWSC02MessagesZoomNoNitrogen; + break; + case kWSC02SouthMessagesOutSpotID: + if (GameState.isTakenItemID(kNitrogenCanister)) + zoomExtra = kMessagesZoomOutNoNitrogen; + break; + case kWSC02SouthMorphSpotID: + if (GameState.isTakenItemID(kArgonCanister)) + zoomExtra = kWSC02MorphZoomNoArgon; + break; + case kWSC02SouthMorphOutSpotID: + if (GameState.isTakenItemID(kArgonCanister)) + zoomExtra = kWSC02ZoomOutNoArgon; + break; + case kWSC61SouthSpotID: + if (GameState.isTakenItemID(kMachineGun)) + zoomExtra = kW61SouthZoomInNoGun; + break; + case kWSC61SouthOutSpotID: + if (GameState.isTakenItemID(kMachineGun)) + zoomExtra = kW61SouthZoomOutNoGun; + break; + } + + if (zoomExtra != 0xffffffff) { + getExtraEntry(zoomExtra, extra); + zoomEntry.movieStart = extra.movieStart; + zoomEntry.movieEnd = extra.movieEnd; + } +} + +void WSC::getExtraEntry(const uint32 id, ExtraTable::Entry &extraEntry) { + switch (id) { + case kWSCZoomOutFromAnalyzer: + Neighborhood::getExtraEntry(kWSCZoomOutFromAnalyzer, extraEntry); + extraEntry.movieEnd = extraEntry.movieStart + 14 * kWSCFrameDuration; + break; + case kW61WalchekMessage: + if (GameState.getEasterEgg()) + Neighborhood::getExtraEntry(kW61WalchekEasterEgg1, extraEntry); + else + Neighborhood::getExtraEntry(id, extraEntry); + break; + case kW61SouthScreenOnWithGun: + if (GameState.isTakenItemID(kMachineGun)) + Neighborhood::getExtraEntry(id, extraEntry); + else + Neighborhood::getExtraEntry(kW61SouthScreenOnNoGun, extraEntry); + break; + case kW61SouthSmartAlloysWithGun: + if (GameState.isTakenItemID(kMachineGun)) + Neighborhood::getExtraEntry(id, extraEntry); + else + Neighborhood::getExtraEntry(kW61SouthSmartAlloysNoGun, extraEntry); + break; + case kW61SouthMorphingWithGun: + if (GameState.isTakenItemID(kMachineGun)) + Neighborhood::getExtraEntry(id, extraEntry); + else + Neighborhood::getExtraEntry(kW61SouthMorphingNoGun, extraEntry); + break; + case kW61SouthTimeBendingWithGun: + if (GameState.isTakenItemID(kMachineGun)) + Neighborhood::getExtraEntry(id, extraEntry); + else + Neighborhood::getExtraEntry(kW61SouthTimeBendingNoGun, extraEntry); + break; + case kW98RobotHeadOpensLight: + if (GameState.getWSCCatwalkDark()) + Neighborhood::getExtraEntry(kW98RobotHeadOpensDark, extraEntry); + else + Neighborhood::getExtraEntry(id, extraEntry); + break; + default: + Neighborhood::getExtraEntry(id, extraEntry); + break; + } +} + +CanMoveForwardReason WSC::canMoveForward(ExitTable::Entry &entry) { + if (GameState.getCurrentRoomAndView() == MakeRoomView(kWSC01, kWest) && + getCurrentActivation() != kActivateHotSpotAlways) + return kCantMoveWatchingDiagnosis; + + return Neighborhood::canMoveForward(entry); +} + +// Also add cases here for compound analyzer... +CanTurnReason WSC::canTurn(TurnDirection turnDirection, DirectionConstant &nextDir) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kWSC01, kWest): + if (getCurrentActivation() != kActivateHotSpotAlways) + return kCantTurnWatchingDiagnosis; + break; + case MakeRoomView(kWSC01, kEast): + if (getCurrentActivation() != kActivateHotSpotAlways) + return kCantTurnWatchingAnalysis; + break; + case MakeRoomView(kWSC03, kNorth): + if (_privateFlags.getFlag(kWSCPrivateInMoleculeGameFlag)) + return kCantTurnInMoleculeGame; + break; + } + + return Neighborhood::canTurn(turnDirection, nextDir); +} + +CanOpenDoorReason WSC::canOpenDoor(DoorTable::Entry &entry) { + switch (GameState.getCurrentRoom()) { + case kWSC42: + if (!_privateFlags.getFlag(kWSCPrivateSinclairOfficeOpenFlag)) + return kCantOpenLocked; + break; + case kWSC58: + if (!_privateFlags.getFlag(kWSCPrivate58SouthOpenFlag)) + return kCantOpenLocked; + break; + } + + return Neighborhood::canOpenDoor(entry); +} + +void WSC::bumpIntoWall() { + requestSpotSound(kWSCBumpIntoWallIn, kWSCBumpIntoWallOut, kFilterAllInput, 0); + Neighborhood::bumpIntoWall(); +} + +void WSC::closeDoorOffScreen(const RoomID room, const DirectionConstant) { + Item *keyCard; + + switch (room) { + case kWSC58: + case kWSC62: + case kWSC63: + case kWSC64: + case kWSC85: + case kWSC86: + case kWSC88: + case kWSC89: + playSpotSoundSync(kSlidingDoorCloseIn, kSlidingDoorCloseOut); + break; + case kWSC81: + case kWSC82: + case kWSC92: + case kWSC93: + keyCard = _vm->getAllItems().findItemByID(kKeyCard); + if (keyCard->getItemState() == kFlashlightOn && (GameState.getCurrentRoom() == kWSC81 || + GameState.getCurrentRoom() == kWSC93)) { + keyCard->setItemState(kFlashlightOff); + playSpotSoundSync(kWSCFlashlightClickIn, kWSCFlashlightClickOut); + } else if (keyCard->getItemState() == kFlashlightOff && (GameState.getCurrentRoom() == kWSC82 || + GameState.getCurrentRoom() == kWSC92)) { + keyCard->setItemState(kFlashlightOn); + playSpotSoundSync(kWSCFlashlightClickIn, kWSCFlashlightClickOut); + } + + playSpotSoundSync(kSlimyDoorCloseIn, kSlimyDoorCloseOut); + break; + default: + playSpotSoundSync(kSlimyDoorCloseIn, kSlimyDoorCloseOut); + break; + } +} + +void WSC::cantMoveThatWay(CanMoveForwardReason reason) { + if (reason != kCantMoveWatchingDiagnosis) + Neighborhood::cantMoveThatWay(reason); +} + +void WSC::cantOpenDoor(CanOpenDoorReason reason) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kWSC22, kWest): + playSpotSoundSync(kNakamuraNotHomeIn, kNakamuraNotHomeOut); + break; + case MakeRoomView(kWSC23, kEast): + playSpotSoundSync(kHernandezNotHomeIn, kHernandezNotHomeOut); + break; + case MakeRoomView(kWSC26, kWest): + playSpotSoundSync(kGrailisNotHomeIn, kGrailisNotHomeOut); + break; + case MakeRoomView(kWSC27, kEast): + playSpotSoundSync(kWashingtonNotHomeIn, kWashingtonNotHomeOut); + break; + case MakeRoomView(kWSC32, kWest): + playSpotSoundSync(kTheriaultNotHomeIn, kTheriaultNotHomeOut); + break; + case MakeRoomView(kWSC33, kEast): + playSpotSoundSync(kSullivanNotHomeIn, kSullivanNotHomeOut); + break; + case MakeRoomView(kWSC41, kWest): + playSpotSoundSync(kGlennerNotHomeIn, kGlennerNotHomeOut); + break; + case MakeRoomView(kWSC42, kEast): + playSpotSoundSync(kSinclairNotHomeIn, kSinclairNotHomeOut); + break; + case MakeRoomView(kWSC15, kWest): + case MakeRoomView(kWSC25, kWest): + case MakeRoomView(kWSC33, kWest): + case MakeRoomView(kWSC41, kEast): + case MakeRoomView(kWSC46, kWest): + playSpotSoundSync(kWSCLabClosedIn, kWSCLabClosedOut); + break; + default: + Neighborhood::cantOpenDoor(reason); + break; + } +} + +void WSC::doorOpened() { + Neighborhood::doorOpened(); + + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kWSC42, kEast): + _vm->addItemToInventory((InventoryItem *)_vm->getAllItems().findItemByID(kSinclairKey)); + break; + case MakeRoomView(kWSC58, kSouth): + GameState.setScoringUsedCrowBarInWSC(); + _vm->addItemToInventory((InventoryItem *)_vm->getAllItems().findItemByID(kCrowbar)); + break; + case MakeRoomView(kWSC06, kNorth): + case MakeRoomView(kWSC79, kWest): + die(kDeathArrestedInWSC); + break; + case MakeRoomView(kWSC60, kWest): + if (_vm->itemInInventory(kMachineGun)) + startExtraSequence(kNerdAtTheDoor2, kExtraCompletedFlag, kFilterNoInput); + else if (!GameState.getWSCSeenNerd()) + startExtraSequence(kNerdAtTheDoor1, kExtraCompletedFlag, kFilterNoInput); + break; + case MakeRoomView(kWSC95, kWest): + GameState.setScoringOpenedCatwalk(); + scheduleEvent(kGawkAtRobotTime, 1, kTimerEventPlayerGawkingAtRobot); + break; + } +} + +void WSC::turnLeft() { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kWSC17, kNorth): + if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt17WestFlag) && _vm->getRandomNumber(2) == 0) + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt17WestFlag, true); + break; + case MakeRoomView(kWSC49, kEast): + if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt49NorthFlag) && _vm->getRandomNumber(2) == 0) + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt49NorthFlag, true); + break; + case MakeRoomView(kWSC73, kNorth): + if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt73WestFlag) && _vm->getRandomNumber(2) == 0) + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt73WestFlag, true); + break; + case MakeRoomView(kWSC73, kWest): + if (!GameState.getWSCBeenAtWSC93()) + setCurrentAlternate(kAltWSCW0ZDoorOpen); + break; + case MakeRoomView(kWSC95, kWest): + cancelEvent(); + break; + } + + Neighborhood::turnLeft(); +} + +void WSC::turnRight() { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kWSC17, kSouth): + if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt17WestFlag) && _vm->getRandomNumber(2) == 0) + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt17WestFlag, true); + break; + case MakeRoomView(kWSC49, kWest): + if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt49NorthFlag) && _vm->getRandomNumber(2) == 0) + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt49NorthFlag, true); + break; + case MakeRoomView(kWSC73, kSouth): + if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt73WestFlag) && _vm->getRandomNumber(2) == 0) + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt73WestFlag, true); + break; + case MakeRoomView(kWSC73, kEast): + if (!GameState.getWSCBeenAtWSC93()) + setCurrentAlternate(kAltWSCW0ZDoorOpen); + break; + case MakeRoomView(kWSC95, kWest): + cancelEvent(); + break; + } + + Neighborhood::turnRight(); +} + +void WSC::moveForward() { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kWSC19, kNorth): + if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt19NorthFlag)) + setCurrentAlternate(kAltWSCPeopleAtW19North); + break; + case MakeRoomView(kWSC95, kWest): + cancelEvent(); + break; + } + + Neighborhood::moveForward(); +} + +void WSC::zoomTo(const Hotspot *hotspot) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kWSC02Messages, kSouth): + if (_privateFlags.getFlag(kWSCPrivateLabMessagesOpenFlag)) { + _cachedZoomSpot = hotspot; + if (GameState.isTakenItemID(kNitrogenCanister)) + startExtraSequence(kMessagesOffNoNitrogen, kExtraCompletedFlag, kFilterNoInput); + else + startExtraSequence(kMessagesOff, kExtraCompletedFlag, kFilterNoInput); + return; + } + break; + case MakeRoomView(kWSC61West, kWest): + if (GameState.getWSCOfficeMessagesOpen()) { + _cachedZoomSpot = hotspot; + startExtraSequence(kW61MessagesOff, kExtraCompletedFlag, kFilterNoInput); + return; + } + break; + case MakeRoomView(kWSC61South, kSouth): + if (_privateFlags.getFlag(kWSCPrivateOfficeLogOpenFlag)) { + _cachedZoomSpot = hotspot; + if (GameState.isTakenItemID(kMachineGun)) + startExtraSequence(kW61SouthScreenOffNoGun, kExtraCompletedFlag, kFilterNoInput); + else + startExtraSequence(kW61SouthScreenOffWithGun, kExtraCompletedFlag, kFilterNoInput); + return; + } + break; + } + + Neighborhood::zoomTo(hotspot); +} + +void WSC::startExtraSequence(const ExtraID extraID, const NotificationFlags flags, const InputBits interruptionFilter) { + if (extraID == kW61Brochure) + loadLoopSound1(""); + + Neighborhood::startExtraSequence(extraID, flags, interruptionFilter); +} + +int16 WSC::getStaticCompassAngle(const RoomID room, const DirectionConstant dir) { + int16 angle = Neighborhood::getStaticCompassAngle(room, dir); + + switch (room) { + case kWSC02Messages: + angle -= 50; + break; + case kWSC02Morph: + angle += 5; + break; + case kWSC60East: + angle -= 10; + break; + case kWSC66: + angle -= kAuditoriumAngleOffset; + break; + case kWSC67: + angle += kAuditoriumAngleOffset; + break; + case kWSC68: + angle -= kAuditoriumAngleOffset * 2; + break; + case kWSC69: + angle += kAuditoriumAngleOffset * 2; + break; + case kWSC70: + angle -= kAuditoriumAngleOffset * 3; + break; + case kWSC71: + angle += kAuditoriumAngleOffset * 3; + break; + case kWSC72: + if (dir == kEast || dir == kWest) + angle -= kAuditoriumAngleOffset * 4; + break; + case kWSC73: + if (dir == kEast || dir == kWest) + angle += kAuditoriumAngleOffset * 4; + break; + } + + return angle; +} + +void WSC::getExitCompassMove(const ExitTable::Entry &exitEntry, FaderMoveSpec &compassMove) { + Neighborhood::getExitCompassMove(exitEntry, compassMove); + + if (exitEntry.room == kWSC65 && exitEntry.direction == kSouth) { + compassMove.insertFaderKnot(exitEntry.movieStart + 100 * kWSCFrameDuration, 180); + compassMove.insertFaderKnot(exitEntry.movieStart + 108 * kWSCFrameDuration, 150); + compassMove.insertFaderKnot(exitEntry.movieEnd, 150); + } +} + +void WSC::getExtraCompassMove(const ExtraTable::Entry &entry, FaderMoveSpec &compassMove) { + switch (entry.extra) { + case kW61Brochure: + compassMove.insertFaderKnot(entry.movieStart + 15 * kWSCFrameDuration, 85); + compassMove.insertFaderKnot(entry.movieEnd - 15 * kWSCFrameDuration, 85); + compassMove.insertFaderKnot(entry.movieEnd, 90); + break; + default: + Neighborhood::getExtraCompassMove(entry, compassMove); + break; + } +} + +void WSC::loadAmbientLoops() { + RoomID room = GameState.getCurrentRoom(); + + if (room >= kWSC01 && room <= kWSC04) { + if (GameState.getWSCSeenTimeStream()) + loadLoopSound1("Sounds/World Science Center/WLabLoop.22K.AIFF", 0x100 / 2); + } else if ((room >= kWSC06 && room <= kWSC58) || (room >= kWSC62 && room <= kWSC63)) + loadLoopSound1("Sounds/World Science Center/Organic Walls.22K.AIFF", 0x100 / 2); + else if (room >= kWSC82 && room <= kWSC92) + loadLoopSound1("Sounds/World Science Center/Creature Feature.22K.AIFF"); + else if ((room >= kWSC60 && room <= kWSC61West) || (room >= kWSC64 && room <= kWSC81) || + (room >= kWSC93 && room <= kWSC97)) + loadLoopSound1("Sounds/World Science Center/The Other Side.22K.AIFF", 0x100 / 12); + else if (room == kWSC98) + loadLoopSound1("Sounds/World Science Center/WCatLoop.22K.AIFF"); +} + +void WSC::checkContinuePoint(const RoomID room, const DirectionConstant direction) { + switch (MakeRoomView(room, direction)) { + case MakeRoomView(kWSC07, kNorth): + case MakeRoomView(kWSC11, kSouth): + case MakeRoomView(kWSC13, kSouth): + case MakeRoomView(kWSC13, kWest): + case MakeRoomView(kWSC16, kWest): + case MakeRoomView(kWSC17, kEast): + case MakeRoomView(kWSC19, kWest): + case MakeRoomView(kWSC28, kNorth): + case MakeRoomView(kWSC28, kSouth): + case MakeRoomView(kWSC28, kEast): + case MakeRoomView(kWSC28, kWest): + case MakeRoomView(kWSC29, kNorth): + case MakeRoomView(kWSC29, kSouth): + case MakeRoomView(kWSC29, kEast): + case MakeRoomView(kWSC29, kWest): + case MakeRoomView(kWSC40, kEast): + case MakeRoomView(kWSC42, kEast): + case MakeRoomView(kWSC49, kWest): + case MakeRoomView(kWSC49, kNorth): + case MakeRoomView(kWSC50, kNorth): + case MakeRoomView(kWSC55, kEast): + case MakeRoomView(kWSC65, kSouth): + case MakeRoomView(kWSC65, kEast): + case MakeRoomView(kWSC65, kWest): + case MakeRoomView(kWSC72, kEast): + case MakeRoomView(kWSC72, kSouth): + case MakeRoomView(kWSC73, kWest): + case MakeRoomView(kWSC73, kSouth): + case MakeRoomView(kWSC79, kWest): + case MakeRoomView(kWSC81, kEast): + case MakeRoomView(kWSC93, kNorth): + case MakeRoomView(kWSC95, kWest): + makeContinuePoint(); + break; + case MakeRoomView(kWSC58, kSouth): + if (!GameState.getWSCDidPlasmaDodge()) + makeContinuePoint(); + break; + case MakeRoomView(kWSC60, kWest): + if (_vm->playerHasItemID(kMachineGun)) + makeContinuePoint(); + break; + } +} + +void WSC::arriveAt(const RoomID room, const DirectionConstant dir) { + switch (MakeRoomView(room, dir)) { + case MakeRoomView(kWSC60, kNorth): + case MakeRoomView(kWSC60, kSouth): + case MakeRoomView(kWSC60, kEast): + case MakeRoomView(kWSC60, kWest): + case MakeRoomView(kWSC60East, kNorth): + case MakeRoomView(kWSC60East, kSouth): + case MakeRoomView(kWSC60East, kEast): + case MakeRoomView(kWSC60East, kWest): + case MakeRoomView(kWSC60North, kNorth): + case MakeRoomView(kWSC60North, kSouth): + case MakeRoomView(kWSC60North, kEast): + case MakeRoomView(kWSC60North, kWest): + case MakeRoomView(kWSC61, kNorth): + case MakeRoomView(kWSC61, kSouth): + case MakeRoomView(kWSC61, kEast): + case MakeRoomView(kWSC61, kWest): + case MakeRoomView(kWSC61South, kNorth): + case MakeRoomView(kWSC61South, kSouth): + case MakeRoomView(kWSC61South, kEast): + case MakeRoomView(kWSC61South, kWest): + case MakeRoomView(kWSC61West, kNorth): + case MakeRoomView(kWSC61West, kSouth): + case MakeRoomView(kWSC61West, kEast): + case MakeRoomView(kWSC61West, kWest): + if (GameState.isTakenItemID(kMachineGun)) + setCurrentAlternate(kAltWSCTookMachineGun); + else + setCurrentAlternate(kAltWSCNormal); + break; + case MakeRoomView(kWSC73, kSouth): + case MakeRoomView(kWSC75, kNorth): + case MakeRoomView(kWSC75, kSouth): + case MakeRoomView(kWSC75, kEast): + case MakeRoomView(kWSC75, kWest): + if (!GameState.getWSCBeenAtWSC93()) + setCurrentAlternate(kAltWSCW0ZDoorOpen); + break; + } + + Neighborhood::arriveAt(room, dir); + + switch (MakeRoomView(room, dir)) { + case MakeRoomView(kWSC01, kWest): + if (!GameState.getWSCSeenTimeStream()) { + requestExtraSequence(kWSCArrivalFromTSA, kExtraCompletedFlag, kFilterNoInput); + requestExtraSequence(kWSCShotByRobot, 0, kFilterNoInput); + requestExtraSequence(kWSCDartScan1, kExtraCompletedFlag, kFilterNoInput); + } else if (GameState.getWSCPoisoned() && !GameState.getWSCAnsweredAboutDart()) { + setCurrentActivation(kActivationShotByRobot); + } + break; + case MakeRoomView(kWSC01, kEast): + if (GameState.getWSCDartInAnalyzer()) + requestExtraSequence(kWSCDropDartIntoAnalyzer, kExtraCompletedFlag, kFilterNoInput); + break; + case MakeRoomView(kWSC02Morph, kSouth): + setCurrentActivation(kActivationMorphScreenOff); + break; + case MakeRoomView(kWSC03, kNorth): + setCurrentActivation(kActivationW03NorthOff); + break; + case MakeRoomView(kWSC03, kSouth): + if (GameState.getWSCDesignedAntidote() && !GameState.getWSCPickedUpAntidote()) + setCurrentActivation(kActivationReadyForSynthesis); + break; + case MakeRoomView(kWSC16, kNorth): + if (getCurrentAlternate() == kAltWSCPeopleAtW19North) { + setCurrentAlternate(kAltWSCNormal); + _privateFlags.setFlag(kWSCPrivateSeenPeopleAt19NorthFlag, true); + } + break; + case MakeRoomView(kWSC07, kSouth): + case MakeRoomView(kWSC56, kNorth): + setCurrentActivation(kActivationReadyForMap); + break; + case MakeRoomView(kWSC42, kWest): + setCurrentAlternate(kAltWSCNormal); + break; + case MakeRoomView(kWSC42, kEast): + _privateFlags.setFlag(kWSCPrivateSinclairOfficeOpenFlag, false); + setCurrentActivation(kActivationSinclairOfficeLocked); + break; + case MakeRoomView(kWSC58, kSouth): + setCurrentActivation(kActivationW58SouthDoorLocked); + _privateFlags.setFlag(kWSCPrivate58SouthOpenFlag, false); + break; + case MakeRoomView(kWSC60, kEast): + GameState.setScoringEnteredSinclairOffice(); + break; + case MakeRoomView(kWSC61West, kWest): + setCurrentActivation(kActivationW61MessagesOff); + break; + case MakeRoomView(kWSC61South, kSouth): + setCurrentActivation(kActivationW61SouthOff); + break; + case MakeRoomView(kWSC62, kSouth): + if (!GameState.getWSCDidPlasmaDodge()) { + g_AIArea->lockAIOut(); + loadLoopSound1("Sounds/World Science Center/Plasma Rock.22K.AIFF"); + requestExtraSequence(kW62SouthPlasmaRobotAppears, 0, kFilterNoInput); + requestExtraSequence(kW62ZoomToRobot, 0, kFilterNoInput); + requestExtraSequence(kW62ZoomOutFromRobot, kExtraCompletedFlag, kFilterNoInput); + } + break; + case MakeRoomView(kWSC65Screen, kSouth): + if (!GameState.getWSCSeenSinclairLecture()) { + GameState.setWSCSeenSinclairLecture(true); + startExtraSequence(kW65SouthSinclairLecture, kExtraCompletedFlag, kFilterAllInput); + } + break; + case MakeRoomView(kWSC66, kWest): + case MakeRoomView(kWSC67, kEast): + if (!GameState.getWSCHeardPage2()) { + playSpotSoundSync(kPaging2In, kPaging2Out); + GameState.setWSCHeardPage2(true); + } + case MakeRoomView(kWSC10, kNorth): + case MakeRoomView(kWSC26, kSouth): + case MakeRoomView(kWSC72, kWest): + case MakeRoomView(kWSC83, kWest): + if (!GameState.getWSCHeardCheckIn()) { + playSpotSoundSync(kCheckInIn, kCheckInOut); + GameState.setWSCHeardCheckIn(true); + } + break; + case MakeRoomView(kWSC0Z, kSouth): + if (getCurrentAlternate() == kAltWSCW0ZDoorOpen) + turnLeft(); + break; + case MakeRoomView(kWSC93, kEast): + GameState.setWSCBeenAtWSC93(true); + break; + case MakeRoomView(kWSC98, kWest): + if (!GameState.getWSCRobotDead()) { + scheduleEvent(kGawkAtRobotTime2, 1, kTimerEventPlayerGawkingAtRobot2); + setCurrentActivation(kActivationRobotTurning); + if (g_AIArea) + g_AIArea->checkMiddleArea(); + } else if (!GameState.getWSCRobotGone()) { + setCurrentActivation(kActivationRobotDead); + } else { + if (GameState.getWSCCatwalkDark()) { + // Change the gun hot spot... + _vm->getAllHotspots().setHotspotRect(kW98StunGunSpotID, Common::Rect(181 + kNavAreaLeft, + 99 + kNavAreaTop,372 + kNavAreaLeft, 149 + kNavAreaTop)); + } + setCurrentActivation(kActivationRobotGone); + } + break; + case MakeRoomView(kWSCDeathRoom, kNorth): + case MakeRoomView(kWSCDeathRoom, kSouth): + case MakeRoomView(kWSCDeathRoom, kEast): + case MakeRoomView(kWSCDeathRoom, kWest): + die(kDeathArrestedInWSC); + break; + } + + checkPeopleCrossing(); + setUpPoison(); +} + +void WSC::turnTo(const DirectionConstant direction) { + Neighborhood::turnTo(direction); + + switch (MakeRoomView(GameState.getCurrentRoom(), direction)) { + case MakeRoomView(kWSC01, kNorth): + case MakeRoomView(kWSC01, kSouth): + GameState.setWSCAnalyzerOn(false); + break; + case MakeRoomView(kWSC03, kNorth): + setCurrentActivation(kActivationW03NorthOff); + break; + case MakeRoomView(kWSC03, kSouth): + if (GameState.getWSCDesignedAntidote() && !GameState.getWSCPickedUpAntidote()) + setCurrentActivation(kActivationReadyForSynthesis); + break; + case MakeRoomView(kWSC07, kSouth): + case MakeRoomView(kWSC56, kNorth): + setCurrentActivation(kActivationReadyForMap); + break; + case MakeRoomView(kWSC18, kSouth): + case MakeRoomView(kWSC57, kEast): + case MakeRoomView(kWSC75, kEast): + case MakeRoomView(kWSC90, kSouth): + if (!GameState.getWSCHeardCheckIn()) { + playSpotSoundSync(kCheckInIn, kCheckInOut); + GameState.setWSCHeardCheckIn(true); + } + break; + case MakeRoomView(kWSC56, kSouth): + if (!GameState.getWSCHeardPage1()) { + playSpotSoundSync(kPaging1In, kPaging1Out); + GameState.setWSCHeardPage1(true); + } + // clone2727 says: This falls through?!??! WTF? + case MakeRoomView(kWSC42, kEast): + _privateFlags.setFlag(kWSCPrivateSinclairOfficeOpenFlag, false); + setCurrentActivation(kActivationSinclairOfficeLocked); + break; + case MakeRoomView(kWSC58, kSouth): + setCurrentActivation(kActivationW58SouthDoorLocked); + _privateFlags.setFlag(kWSCPrivate58SouthOpenFlag, false); + break; + case MakeRoomView(kWSC73, kWest): + setCurrentAlternate(kAltWSCNormal); + break; + case MakeRoomView(kWSC0Z, kEast): + if (getCurrentAlternate() == kAltWSCW0ZDoorOpen) + startExtraSequence(kW0ZSpottedByWomen, kExtraCompletedFlag, kFilterNoInput); + break; + } + + checkPeopleCrossing(); +} + +void WSC::receiveNotification(Notification *notification, const NotificationFlags flags) { + int32 currentEnergy; + Item *item; + + if (flags & kExtraCompletedFlag) { + _interruptionFilter = kFilterAllInput; + + switch (_lastExtra) { + case kWSCArrivalFromTSA: + GameState.setWSCSeenTimeStream(true); + loadAmbientLoops(); + break; + case kWSCDartScan1: + setCurrentActivation(kActivationShotByRobot); + GameState.setWSCPoisoned(true); + setUpPoison(); + makeContinuePoint(); + break; + case kWSCDartScan2: + _vm->addItemToInventory((InventoryItem *)_vm->getAllItems().findItemByID(kPoisonDart)); + GameState.setScoringRemovedDart(); + GameState.setWSCRemovedDart(true); + setUpPoison(); + g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/WSC/XW1WB2", false, kHintInterruption); + // Fall through... + case kWSCDartScanNo: + GameState.setWSCAnsweredAboutDart(true); + startExtraSequence(kWSCDartScan3, kExtraCompletedFlag, kFilterNoInput); + break; + case kWSCDartScan3: + setCurrentActivation(kActivateHotSpotAlways); + break; + case kWSCAnalyzerPowerUp: + case kWSCAnalyzerPowerUpWithDart: + GameState.setWSCAnalyzerOn(true); + break; + case kWSCDropDartIntoAnalyzer: + setCurrentActivation(kActivationZoomedInToAnalyzer); + break; + case kWSCAnalyzeDart: + GameState.setWSCAnalyzedDart(true); + GameState.setScoringAnalyzedDart(); + break; + case kWSCZoomOutFromAnalyzer: + setCurrentActivation(kActivateHotSpotAlways); + GameState.setWSCAnalyzerOn(false); + GameState.setWSCDartInAnalyzer(false); + updateViewFrame(); + break; + case kMessagesMovedToOffice: + case kMessagesMovedToOfficeNoNitrogen: + _privateFlags.setFlag(kWSCPrivateLabMessagesOpenFlag, true); + GameState.setScoringPlayedWithMessages(); + break; + case kMessagesOff: + case kMessagesOffNoNitrogen: + _privateFlags.setFlag(kWSCPrivateLabMessagesOpenFlag, false); + if (_cachedZoomSpot) { + zoomTo(_cachedZoomSpot); + _cachedZoomSpot = 0; + } + break; + case kWSC02TurnOnMorphScreen: + setCurrentActivation(kActivationReadyForMorph); + break; + case kWSC02DropToMorphExperiment: + loopExtraSequence(kWSC02MorphLoop, kExtraCompletedFlag); + setCurrentActivation(kActivationMorphLooping); + break; + case kWSC02MorphLoop: + if (_privateFlags.getFlag(kWSCPrivateInterruptedMorphFlag)) + startExtraSequence(kWSC02MorphInterruption, kExtraCompletedFlag, kFilterNoInput); + else + scheduleNavCallBack(kExtraCompletedFlag); + break; + case kWSC02MorphInterruption: + setCurrentActivation(kActivationMorphInterrupted); + GameState.setScoringSawMorphExperiment(); + break; + case kWSC02TurnOffMorphScreen: + setCurrentActivation(kActivationMorphScreenOff); + GameState.setWSCSawMorph(true); + break; + case kW03NorthActivate: + if (GameState.getWSCAnalyzedDart() && !GameState.getWSCDesignedAntidote()) + startExtraSequence(kW03NorthGetData, kExtraCompletedFlag, kFilterNoInput); + else + setCurrentActivation(kActivateHotSpotAlways); + break; + case kW03NorthGetData: + setCurrentActivation(kActivationW03NorthReadyForInstructions); + break; + case kW03NorthInstructions: + setCurrentActivation(kActivationW03NorthSawInstructions); + break; + case kW03NorthPrepMolecule1: + setUpMoleculeGame(); + break; + case kW03NorthPrepMolecule2: + case kW03NorthPrepMolecule3: + nextMoleculeGameLevel(); + break; + case kW03NorthFinishSynthesis: + setCurrentActivation(kActivateHotSpotAlways); + _privateFlags.setFlag(kWSCPrivateInMoleculeGameFlag, false); + GameState.setWSCDesignedAntidote(true); + GameState.setScoringBuiltAntidote(); + break; + case kW03SouthCreateAntidote: + setCurrentActivation(kActivationSynthesizerLooping); + loopExtraSequence(kW03SouthAntidoteLoop); + break; + case kW03SouthDeactivate: + setCurrentActivation(kActivateHotSpotAlways); + break; + case kWSC07SouthMap: + case kWSC56SouthMap: + setCurrentActivation(kActivateHotSpotAlways); + GameState.setScoringSawWSCDirectory(); + break; + case kNerdAtTheDoor1: + GameState.setWSCSeenNerd(true); + break; + case kNerdAtTheDoor2: + die(kDeathArrestedInWSC); + break; + case kW61Brochure: + GameState.setScoringSawBrochure(); + loadAmbientLoops(); + break; + case kW61SouthSmartAlloysWithGun: + case kW61SouthSmartAlloysNoGun: + GameState.setScoringSawSinclairEntry1(); + break; + case kW61SouthMorphingWithGun: + case kW61SouthMorphingNoGun: + GameState.setScoringSawSinclairEntry2(); + break; + case kW61SouthTimeBendingWithGun: + case kW61SouthTimeBendingNoGun: + GameState.setScoringSawSinclairEntry3(); + break; + case kW61MessagesOn: + GameState.setWSCOfficeMessagesOpen(true); + setCurrentActivation(kActivationW61MessagesOn); + break; + case kW61MessagesOff: + GameState.setWSCOfficeMessagesOpen(false); + setCurrentActivation(kActivationW61MessagesOff); + if (_cachedZoomSpot) { + zoomTo(_cachedZoomSpot); + _cachedZoomSpot = 0; + } + break; + case kW61SouthScreenOnWithGun: + case kW61SouthScreenOnNoGun: + _privateFlags.setFlag(kWSCPrivateOfficeLogOpenFlag, true); + setCurrentActivation(kActivationW61SouthOn); + break; + case kW61SouthScreenOffWithGun: + case kW61SouthScreenOffNoGun: + _privateFlags.setFlag(kWSCPrivateOfficeLogOpenFlag, false); + setCurrentActivation(kActivationW61SouthOff); + if (_cachedZoomSpot) { + zoomTo(_cachedZoomSpot); + _cachedZoomSpot = 0; + } + break; + case kW62ZoomOutFromRobot: + // Handle action queue before starting new movie sequences. + Neighborhood::receiveNotification(notification, flags); + _energyDrainRate = g_energyMonitor->getEnergyDrainRate(); + g_energyMonitor->setEnergyDrainRate(0); + currentEnergy = g_energyMonitor->getCurrentEnergy(); + _vm->setEnergyDeathReason(kDeathHitByPlasma); + + if (GameState.getShieldOn()) + currentEnergy -= kPlasmaEnergyWithShield; + else + currentEnergy -= kPlasmaEnergyNoShield; + + if (currentEnergy <= 0) + startExtraSequence(kW62PlasmaDodgeDie, kExtraCompletedFlag, kFilterNoInput); + else + startExtraSequence(kW62PlasmaDodgeSurvive, kExtraCompletedFlag, kFilterNoInput); + + scheduleEvent(kPlasmaImpactTime, kOneTickPerSecond, kTimerEventPlasmaHit); + break; + case kW62PlasmaDodgeDie: + g_energyMonitor->setEnergyValue(0); + break; + case kW62PlasmaDodgeSurvive: + if (GameState.getShieldOn()) { + g_shield->setItemState(kShieldNormal); + g_energyMonitor->drainEnergy(kPlasmaEnergyWithShield); + } else { + g_energyMonitor->drainEnergy(kPlasmaEnergyNoShield); + } + + g_energyMonitor->setEnergyDrainRate(_energyDrainRate); + g_AIArea->unlockAI(); + GameState.setScoringFinishedPlasmaDodge(); + GameState.setWSCDidPlasmaDodge(true); + restoreStriding(kWSC58, kSouth, kAltWSCNormal); + loadAmbientLoops(); + break; + case kW0ZSpottedByWomen: + die(kDeathArrestedInWSC); + break; + case kW17WestPeopleCrossing: + _privateFlags.setFlag(kWSCPrivateSeenPeopleAt17WestFlag, true); + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt17WestFlag, false); + break; + case kW21SouthPeopleCrossing: + _privateFlags.setFlag(kWSCPrivateSeenPeopleAt21SouthFlag, true); + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt21SouthFlag, true); + break; + case kW24SouthPeopleCrossing: + _privateFlags.setFlag(kWSCPrivateSeenPeopleAt24SouthFlag, true); + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt24SouthFlag, true); + break; + case kW34EastPeopleCrossing: + _privateFlags.setFlag(kWSCPrivateSeenPeopleAt34EastFlag, true); + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt34EastFlag, true); + break; + case kW36WestPeopleCrossing: + _privateFlags.setFlag(kWSCPrivateSeenPeopleAt36WestFlag, true); + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt36WestFlag, true); + break; + case kW38NorthPeopleCrossing: + _privateFlags.setFlag(kWSCPrivateSeenPeopleAt38NorthFlag, true); + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt38NorthFlag, true); + break; + case kW46SouthPeopleCrossing: + _privateFlags.setFlag(kWSCPrivateSeenPeopleAt46SouthFlag, true); + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt46SouthFlag, true); + break; + case kW49NorthPeopleCrossing: + _privateFlags.setFlag(kWSCPrivateSeenPeopleAt49NorthFlag, true); + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt49NorthFlag, false); + break; + case kW73WestPeopleCrossing: + _privateFlags.setFlag(kWSCPrivateSeenPeopleAt73WestFlag, true); + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt73WestFlag, false); + break; + case kW95RobotShoots: + case kW98RobotShoots: + die(kDeathShotOnCatwalk); + break; + case kW98MorphsToRobot: + if (_argonSprite) { + delete _argonSprite; _argonSprite = 0; + startExtraSequence(kW98RobotGassed, kExtraCompletedFlag, kFilterNoInput); + } else if (_privateFlags.getFlag(kWSCPrivateClickedCatwalkCableFlag)) { + startExtraSequence(kW98RobotShocked, kExtraCompletedFlag, kFilterNoInput); + } else { + startExtraSequence(kW98RobotShoots, kExtraCompletedFlag, kFilterNoInput); + } + break; + case kW98RobotShocked: + GameState.setWSCCatwalkDark(true); + // Change the gun hot spot... + _vm->getAllHotspots().setHotspotRect(kW98StunGunSpotID, Common::Rect(181 + kNavAreaLeft, 99 + kNavAreaTop, + 372 + kNavAreaLeft, 149 + kNavAreaTop)); + setCurrentActivation(kActivationRobotDead); + GameState.setWSCRobotDead(true); + GameState.setScoringStoppedWSCRobot(); + + // Video is not present + //g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/WSC/XN59WD", false, kWarningInterruption); + break; + case kW98RobotGassed: + item = (Item *)_vm->getAllItems().findItemByID(kArgonCanister); + _vm->addItemToInventory((InventoryItem *)item); + setCurrentActivation(kActivationRobotDead); + GameState.setWSCRobotDead(true); + GameState.setScoringStoppedWSCRobot(); + + // Video is not present + //g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/WSC/XN59WD", false, kWarningInterruption); + break; + case kW98RobotHeadOpensLight: + case kW98RobotHeadOpensDark: + setCurrentActivation(kActivationWSCRobotHeadOpen); + _privateFlags.setFlag(kWSCPrivateRobotHeadOpenFlag, true); + break; + case kW98RobotHeadClosesDark: + case kW98RobotHeadClosesLight: + setCurrentActivation(kActivationRobotGone); + _privateFlags.setFlag(kWSCPrivateRobotHeadOpenFlag, false); + GameState.setWSCRobotGone(true); + break; + } + } + + Neighborhood::receiveNotification(notification, flags); + g_AIArea->checkMiddleArea(); +} + +void WSC::timerExpired(const uint32 event) { + switch (event) { + case kTimerEventPlasmaHit: + if (GameState.getShieldOn()) + g_shield->setItemState(kShieldPlasma); + break; + case kTimerEventPlayerGawkingAtRobot: + startExtraSequence(kW95RobotShoots, kExtraCompletedFlag, kFilterNoInput); + break; + case kTimerEventPlayerGawkingAtRobot2: + startExtraSequence(kW98MorphsToRobot, kExtraCompletedFlag, kFilterAllInput); + break; + } +} + +void WSC::setUpMoleculeGame() { + _privateFlags.setFlag(kWSCPrivateInMoleculeGameFlag, true); + setCurrentActivation(kActivationW03NorthInGame); + initOneMovie(&_moleculesMovie, "Images/World Science Center/Molecules.movie", + kWSCMoleculesMovieOrder, kMoleculesMovieLeft, kMoleculesMovieTop, true); + _moleculesMovie.redrawMovieWorld(); + _moleculeBin.initMoleculeBin(); + _moleculeGameLevel = 0; + nextMoleculeGameLevel(); +} + +void WSC::nextMoleculeGameLevel() { + _moleculeGameLevel++; + + for (byte i = 0; i < 6; ++i) + _levelArray[i] = i; + + _vm->shuffleArray((int32 *)_levelArray, 6); + _moleculeBin.setBinLayout(_levelArray); + startMoleculeGameLevel(); +} + +void WSC::startMoleculeGameLevel() { + _moleculeBin.resetBin(); + _numCorrect = 0; + _moleculesMovie.stop(); + _moleculesMovie.setFlags(0); + _moleculesMovie.setSegment(s_moleculeLoopTimes[0], s_moleculeLoopTimes[0] + kMoleculeLoopTime); + _moleculesMovie.setTime(s_moleculeLoopTimes[0]); + _moleculesMovie.setFlags(kLoopTimeBase); + _moleculesMovie.show(); + + switch (_moleculeGameLevel) { + case 1: + playSpotSoundSync(kWSCMolecule1In, kWSCMolecule1Out); + break; + case 2: + playSpotSoundSync(kWSCMolecule2In, kWSCMolecule2Out); + break; + case 3: + playSpotSoundSync(kWSCMolecule3In, kWSCMolecule3Out); + break; + } + + _moleculesMovie.start(); +} + +void WSC::moleculeGameClick(const HotSpotID id) { + uint32 molecule = id - kWSC03NorthMolecule1SpotID; + + _moleculeBin.highlightMolecule(molecule); + _moleculeBin.selectMolecule(molecule); + + if (molecule == _levelArray[_numCorrect]) { + playSpotSoundSync(kWSCClick2In, kWSCClick2Out); + _numCorrect++; + _moleculesMovie.stop(); + _moleculesMovie.setFlags(0); + + TimeValue time = _moleculesMovie.getTime(); + _moleculesMovie.setSegment(s_moleculeLoopTimes[_numCorrect], s_moleculeLoopTimes[_numCorrect] + kMoleculeLoopTime); + _moleculesMovie.setTime(s_moleculeLoopTimes[_numCorrect] + time - s_moleculeLoopTimes[_numCorrect - 1]); + + if (_numCorrect == 6) { + _moleculesMovie.start(); + + while (_moleculesMovie.isRunning()) { + _vm->checkCallBacks(); + _vm->refreshDisplay(); + _vm->_system->delayMillis(10); + } + + _moleculesMovie.stop(); + _moleculesMovie.hide(); + + switch (_moleculeGameLevel) { + case 1: + startExtraSequence(kW03NorthPrepMolecule2, kExtraCompletedFlag, kFilterNoInput); + break; + case 2: + startExtraSequence(kW03NorthPrepMolecule3, kExtraCompletedFlag, kFilterNoInput); + break; + case 3: + _moleculesMovie.releaseMovie(); + _moleculeBin.cleanUpMoleculeBin(); + requestExtraSequence(kW03NorthFinishSynthesis, kExtraCompletedFlag, kFilterNoInput); + break; + } + } else { + _moleculesMovie.setFlags(kLoopTimeBase); + _moleculesMovie.start(); + } + } else { + // FAIL + playSpotSoundSync(kWSCClick3In, kWSCClick3Out); + + _moleculesMovie.stop(); + _moleculesMovie.setFlags(0); + _moleculesMovie.start(); + + while (_moleculesMovie.isRunning()) { + _vm->checkCallBacks(); + _vm->refreshDisplay(); + _vm->_system->delayMillis(10); + } + + _moleculesMovie.stop(); + _moleculesMovie.setFlags(0); + _moleculesMovie.setSegment(s_moleculeFailTimes[_numCorrect], s_moleculeFailTimes[_numCorrect] + kMoleculeFailTime); + _moleculesMovie.setTime(s_moleculeFailTimes[_numCorrect]); + _moleculesMovie.start(); + + + while (_moleculesMovie.isRunning()) { + _vm->checkCallBacks(); + _vm->refreshDisplay(); + _vm->_system->delayMillis(10); + } + + _moleculesMovie.stop(); + startMoleculeGameLevel(); + } +} + +void WSC::activateOneHotspot(HotspotInfoTable::Entry &entry, Hotspot *hotspot) { + Neighborhood::activateOneHotspot(entry, hotspot); + + Item *argonCanister; + + switch (hotspot->getObjectID()) { + case kWSCTurnOnAnalyzerSpotID: + if (GameState.getWSCAnalyzerOn()) + hotspot->setInactive(); + break; + case kWSC02SouthTakeArgonSpotID: + if (!GameState.getWSCSawMorph() || GameState.isTakenItemID(kArgonCanister)) + hotspot->setInactive(); + break; + case kWSC02ActivateMorphScreenSpotID: + if (GameState.getWSCSawMorph()) + hotspot->setInactive(); + break; + case kWSC03NorthMolecule1SpotID: + case kWSC03NorthMolecule2SpotID: + case kWSC03NorthMolecule3SpotID: + case kWSC03NorthMolecule4SpotID: + case kWSC03NorthMolecule5SpotID: + case kWSC03NorthMolecule6SpotID: + if (_moleculeBin.isMoleculeHighlighted(hotspot->getObjectID() - kWSC03NorthMolecule1SpotID)) + hotspot->setInactive(); + break; + case kWSC03SouthPickUpAntidoteSpotID: + if (getCurrentActivation() == kActivationSynthesizerLooping) + hotspot->setActive(); + break; + case kW98DropArgonSpotID: + argonCanister = _vm->getAllItems().findItemByID(kArgonCanister); + if (argonCanister->getItemState() != kArgonFull) + hotspot->setInactive(); + break; + } +} + +void WSC::activateHotspots() { + Neighborhood::activateHotspots(); + + if (GameState.getCurrentRoomAndView() == MakeRoomView(kWSC98, kWest) && _privateFlags.getFlag(kWSCPrivateRobotHeadOpenFlag)) { + if (_privateFlags.getFlag(kWSCPrivateGotRetScanChipFlag)) + _vm->getAllHotspots().deactivateOneHotspot(kW98RetinalChipSpotID); + else + _vm->getAllHotspots().activateOneHotspot(kW98RetinalChipSpotID); + + if (_privateFlags.getFlag(kWSCPrivateGotMapChipFlag)) + _vm->getAllHotspots().deactivateOneHotspot(kW98MapChipSpotID); + else + _vm->getAllHotspots().activateOneHotspot(kW98MapChipSpotID); + + if (_privateFlags.getFlag(kWSCPrivateGotOpticalChipFlag)) + _vm->getAllHotspots().deactivateOneHotspot(kW98OpticalChipSpotID); + else + _vm->getAllHotspots().activateOneHotspot(kW98OpticalChipSpotID); + } +} + +void WSC::clickInHotspot(const Input &input, const Hotspot *clickedSpot) { + if (JMPPPInput::isEasterEggModifierInput(input)) + GameState.setEasterEgg(true); + + if (clickedSpot) { + switch (clickedSpot->getObjectID()) { + case kWSCAnalyzerScreenSpotID: + requestExtraSequence(kWSCAnalyzeDart, kExtraCompletedFlag, kFilterNoInput); + requestExtraSequence(kWSCZoomOutFromAnalyzer, kExtraCompletedFlag, kFilterNoInput); + break; + case kWSC02SouthPlayMessagesSpotID: + if (GameState.isTakenItemID(kNitrogenCanister)) { + if (_lastExtra == (uint32)kMessagesMovedToOfficeNoNitrogen) + startExtraSequence(kMessagesOffNoNitrogen, kExtraCompletedFlag, kFilterNoInput); + else + startExtraSequence(kMessagesMovedToOfficeNoNitrogen, kExtraCompletedFlag, kFilterNoInput); + } else { + if (_lastExtra == (uint32)kMessagesMovedToOffice) + startExtraSequence(kMessagesOff, kExtraCompletedFlag, kFilterNoInput); + else + startExtraSequence(kMessagesMovedToOffice, kExtraCompletedFlag, kFilterNoInput); + } + break; + case kWSC02SouthInterruptMorphSpotID: + _privateFlags.setFlag(kWSCPrivateInterruptedMorphFlag, true); + break; + case kWSC02SouthMorphFinishedSpotID: + requestExtraSequence(kWSC02MorphFinished, 0, kFilterNoInput); + requestExtraSequence(kWSC02TurnOffMorphScreen, kExtraCompletedFlag, kFilterNoInput); + break; + case kWSC03NorthMolecule1SpotID: + case kWSC03NorthMolecule2SpotID: + case kWSC03NorthMolecule3SpotID: + case kWSC03NorthMolecule4SpotID: + case kWSC03NorthMolecule5SpotID: + case kWSC03NorthMolecule6SpotID: + moleculeGameClick(clickedSpot->getObjectID()); + break; + case kW98GrabCableSpotID: + if (isEventTimerRunning()) { + cancelEvent(); + startExtraSequence(kW98MorphsToRobot, kExtraCompletedFlag, kFilterAllInput); + } + + _privateFlags.setFlag(kWSCPrivateClickedCatwalkCableFlag, true); + break; + default: + Neighborhood::clickInHotspot(input, clickedSpot); + break; + } + } else { + Neighborhood::clickInHotspot(input, clickedSpot); + } + + GameState.setEasterEgg(false); +} + +void WSC::dropItemIntoRoom(Item *item, Hotspot *dropSpot) { + CoordType h, v; + + switch (item->getObjectID()) { + case kPoisonDart: + Neighborhood::dropItemIntoRoom(item, dropSpot); + GameState.setWSCDartInAnalyzer(true); + if (dropSpot && dropSpot->getObjectID() == kWSCDropDartSpotID) { + if (!GameState.getWSCAnalyzerOn()) + requestExtraSequence(kWSCAnalyzerPowerUpWithDart, kExtraCompletedFlag, kFilterNoInput); + + requestExtraSequence(kWSCDropDartIntoAnalyzer, kExtraCompletedFlag, kFilterNoInput); + } + break; + case kAntidote: + _privateFlags.setFlag(kWSCDraggingAntidoteFlag, false); + Neighborhood::dropItemIntoRoom(item, dropSpot); + loopExtraSequence(kW03SouthAntidoteLoop); + break; + case kSinclairKey: + Neighborhood::dropItemIntoRoom(item, dropSpot); + _privateFlags.setFlag(kWSCPrivateSinclairOfficeOpenFlag, true); + openDoor(); + break; + case kCrowbar: + Neighborhood::dropItemIntoRoom(item, dropSpot); + _privateFlags.setFlag(kWSCPrivate58SouthOpenFlag, true); + openDoor(); + break; + case kMachineGun: + setCurrentAlternate(kAltWSCNormal); + Neighborhood::dropItemIntoRoom(item, dropSpot); + break; + case kArgonCanister: + item->setItemState(kArgonEmpty); + _argonSprite = item->getDragSprite(0); + _argonSprite->setCurrentFrameIndex(1); + _argonSprite->setDisplayOrder(kDragSpriteOrder); + dropSpot->getCenter(h, v); + _argonSprite->centerElementAt(h, v); + _argonSprite->startDisplaying(); + _argonSprite->show(); + + if (isEventTimerRunning()) { + cancelEvent(); + startExtraSequence(kW98MorphsToRobot, kExtraCompletedFlag, kFilterAllInput); + } + break; + case kRetinalScanBiochip: + _privateFlags.setFlag(kWSCPrivateGotRetScanChipFlag, false); + Neighborhood::dropItemIntoRoom(item, dropSpot); + break; + case kMapBiochip: + _privateFlags.setFlag(kWSCPrivateGotMapChipFlag, false); + Neighborhood::dropItemIntoRoom(item, dropSpot); + break; + case kOpticalBiochip: + _privateFlags.setFlag(kWSCPrivateGotOpticalChipFlag, false); + Neighborhood::dropItemIntoRoom(item, dropSpot); + break; + default: + Neighborhood::dropItemIntoRoom(item, dropSpot); + break; + } +} + +void WSC::takeItemFromRoom(Item *item) { + switch (item->getObjectID()) { + case kAntidote: + _privateFlags.setFlag(kWSCDraggingAntidoteFlag, true); + Neighborhood::takeItemFromRoom(item); + break; + case kMachineGun: + setCurrentAlternate(kAltWSCTookMachineGun); + Neighborhood::takeItemFromRoom(item); + break; + case kRetinalScanBiochip: + _privateFlags.setFlag(kWSCPrivateGotRetScanChipFlag, true); + Neighborhood::takeItemFromRoom(item); + break; + case kMapBiochip: + _privateFlags.setFlag(kWSCPrivateGotMapChipFlag, true); + Neighborhood::takeItemFromRoom(item); + break; + case kOpticalBiochip: + _privateFlags.setFlag(kWSCPrivateGotOpticalChipFlag, true); + Neighborhood::takeItemFromRoom(item); + break; + default: + Neighborhood::takeItemFromRoom(item); + break; + } +} + +Hotspot *WSC::getItemScreenSpot(Item *item, DisplayElement *element) { + HotSpotID destSpotID; + + switch (item->getObjectID()) { + case kNitrogenCanister: + destSpotID = kWSC02SouthTakeNitrogenSpotID; + break; + case kArgonPickup: + destSpotID = kWSC02SouthTakeArgonSpotID; + break; + case kAntidote: + destSpotID = kWSC03SouthPickUpAntidoteSpotID; + break; + case kMachineGun: + destSpotID = kW61SouthMachineGunSpotID; + break; + case kRetinalScanBiochip: + destSpotID = kW98RetinalChipSpotID; + break; + case kMapBiochip: + destSpotID = kW98MapChipSpotID; + break; + case kOpticalBiochip: + destSpotID = kW98OpticalChipSpotID; + break; + default: + destSpotID = kNoHotSpotID; + break; + } + + if (destSpotID == kNoHotSpotID) + return Neighborhood::getItemScreenSpot(item, element); + + return _vm->getAllHotspots().findHotspotByID(destSpotID); +} + +void WSC::pickedUpItem(Item *item) { + switch (item->getObjectID()) { + case kAntidote: + if (!GameState.getWSCPickedUpAntidote()) { + GameState.setWSCPoisoned(false); + GameState.setWSCRemovedDart(false); + GameState.setWSCPickedUpAntidote(true); + _privateFlags.setFlag(kWSCDraggingAntidoteFlag, false); + playSpotSoundSync(kDrinkAntidoteIn, kDrinkAntidoteOut); + setUpPoison(); + startExtraSequence(kW03SouthDeactivate, kExtraCompletedFlag, kFilterNoInput); + } + break; + case kArgonPickup: + _vm->removeItemFromInventory((InventoryItem *)item); + item = (Item *)_vm->getAllItems().findItemByID(kArgonCanister); + _vm->addItemToInventory((InventoryItem *)item); + item = (Item *)_vm->getAllItems().findItemByID(kSinclairKey); + _vm->addItemToInventory((InventoryItem *)item); + _vm->getAllHotspots().setHotspotRect(kWSC02SouthMorphOutSpotID, + Common::Rect(kNavAreaLeft, kNavAreaTop, 512 + kNavAreaLeft, 256 + kNavAreaTop)); + break; + case kArgonCanister: + GameState.setScoringGotArgonCanister(); + break; + case kSinclairKey: + GameState.setScoringGotSinclairKey(); + break; + case kNitrogenCanister: + GameState.setScoringGotNitrogenCanister(); + break; + case kRetinalScanBiochip: + if (_privateFlags.getFlag(kWSCPrivateGotMapChipFlag) && _privateFlags.getFlag(kWSCPrivateGotOpticalChipFlag)) { + if (GameState.getWSCCatwalkDark()) + startExtraSequence(kW98RobotHeadClosesDark, kExtraCompletedFlag, kFilterNoInput); + else + startExtraSequence(kW98RobotHeadClosesLight, kExtraCompletedFlag, kFilterNoInput); + } + break; + case kMapBiochip: + if (_privateFlags.getFlag(kWSCPrivateGotRetScanChipFlag) && _privateFlags.getFlag(kWSCPrivateGotOpticalChipFlag)) { + if (GameState.getWSCCatwalkDark()) + startExtraSequence(kW98RobotHeadClosesDark, kExtraCompletedFlag, kFilterNoInput); + else + startExtraSequence(kW98RobotHeadClosesLight, kExtraCompletedFlag, kFilterNoInput); + } + break; + case kOpticalBiochip: + g_opticalChip->addMercury(); + GameState.setScoringGotWSCOpMemChip(); + if (_privateFlags.getFlag(kWSCPrivateGotRetScanChipFlag) && _privateFlags.getFlag(kWSCPrivateGotMapChipFlag)) { + if (GameState.getWSCCatwalkDark()) + startExtraSequence(kW98RobotHeadClosesDark, kExtraCompletedFlag, kFilterNoInput); + else + startExtraSequence(kW98RobotHeadClosesLight, kExtraCompletedFlag, kFilterNoInput); + } + break; + case kStunGun: + GameState.setWSCFinished(true); + + if (!GameState.getWSCCatwalkDark()) + GameState.setScoringWSCGandhi(); + + recallToTSASuccess(); + break; + } +} + +void WSC::checkPeopleCrossing() { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kWSC17, kWest): + if (_privateFlags.getFlag(kWSCPrivateNeedPeopleAt17WestFlag)) + startExtraSequence(kW17WestPeopleCrossing, kExtraCompletedFlag, kFilterNoInput); + break; + case MakeRoomView(kWSC21, kSouth): + if (_privateFlags.getFlag(kWSCPrivateNeedPeopleAt21SouthFlag)) + startExtraSequence(kW21SouthPeopleCrossing, kExtraCompletedFlag, kFilterNoInput); + break; + case MakeRoomView(kWSC24, kSouth): + if (_privateFlags.getFlag(kWSCPrivateNeedPeopleAt24SouthFlag)) + startExtraSequence(kW24SouthPeopleCrossing, kExtraCompletedFlag, kFilterNoInput); + break; + case MakeRoomView(kWSC34, kEast): + if (_privateFlags.getFlag(kWSCPrivateNeedPeopleAt34EastFlag)) + startExtraSequence(kW34EastPeopleCrossing, kExtraCompletedFlag, kFilterNoInput); + break; + case MakeRoomView(kWSC36, kWest): + if (_privateFlags.getFlag(kWSCPrivateNeedPeopleAt36WestFlag)) + startExtraSequence(kW36WestPeopleCrossing, kExtraCompletedFlag, kFilterNoInput); + break; + case MakeRoomView(kWSC38, kNorth): + if (_privateFlags.getFlag(kWSCPrivateNeedPeopleAt38NorthFlag)) + startExtraSequence(kW38NorthPeopleCrossing, kExtraCompletedFlag, kFilterNoInput); + break; + case MakeRoomView(kWSC46, kSouth): + if (_privateFlags.getFlag(kWSCPrivateNeedPeopleAt46SouthFlag)) + startExtraSequence(kW46SouthPeopleCrossing, kExtraCompletedFlag, kFilterNoInput); + break; + case MakeRoomView(kWSC49, kNorth): + if (_privateFlags.getFlag(kWSCPrivateNeedPeopleAt49NorthFlag)) + startExtraSequence(kW49NorthPeopleCrossing, kExtraCompletedFlag, kFilterNoInput); + break; + case MakeRoomView(kWSC73, kWest): + if (_privateFlags.getFlag(kWSCPrivateNeedPeopleAt73WestFlag)) + startExtraSequence(kW73WestPeopleCrossing, kExtraCompletedFlag, kFilterNoInput); + break; + default: + if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt21SouthFlag) && _vm->getRandomNumber(2) == 0) { + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt21SouthFlag, true); + forceStridingStop(kWSC18, kSouth, kAltWSCNormal); + } else { + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt21SouthFlag, false); + restoreStriding(kWSC18, kSouth, kAltWSCNormal); + } + + if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt19NorthFlag) && _vm->getRandomNumber(2) == 0) { + forceStridingStop(kWSC22, kNorth, kAltWSCNormal); + } else { + restoreStriding(kWSC22, kNorth, kAltWSCNormal); + } + + if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt24SouthFlag) && _vm->getRandomNumber(2) == 0) { + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt24SouthFlag, true); + forceStridingStop(kWSC22, kSouth, kAltWSCNormal); + } else { + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt24SouthFlag, false); + restoreStriding(kWSC22, kSouth, kAltWSCNormal); + } + + if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt34EastFlag) && _vm->getRandomNumber(2) == 0) { + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt34EastFlag, true); + forceStridingStop(kWSC28, kEast, kAltWSCNormal); + } else { + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt34EastFlag, false); + restoreStriding(kWSC28, kEast, kAltWSCNormal); + } + + if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt36WestFlag) && _vm->getRandomNumber(2) == 0) { + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt36WestFlag, true); + forceStridingStop(kWSC40, kWest, kAltWSCNormal); + } else { + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt36WestFlag, false); + restoreStriding(kWSC40, kWest, kAltWSCNormal); + } + + if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt38NorthFlag) && _vm->getRandomNumber(2) == 0) { + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt38NorthFlag, true); + forceStridingStop(kWSC42, kNorth, kAltWSCNormal); + } else { + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt38NorthFlag, false); + restoreStriding(kWSC42, kNorth, kAltWSCNormal); + } + + if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt46SouthFlag) && _vm->getRandomNumber(2) == 0) { + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt46SouthFlag, true); + forceStridingStop(kWSC44, kSouth, kAltWSCNormal); + } else { + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt46SouthFlag, false); + restoreStriding(kWSC44, kSouth, kAltWSCNormal); + } + break; + } +} + +void WSC::setUpPoison() { + if (GameState.getWSCPoisoned()) { + if (GameState.getWSCRemovedDart()) { + if (g_energyMonitor->getEnergyDrainRate() != kWSCPoisonEnergyDrainNoDart) { + g_energyMonitor->setEnergyDrainRate(kWSCPoisonEnergyDrainNoDart); + _vm->setEnergyDeathReason(kDeathDidntStopPoison); + } + } else { + if (g_energyMonitor->getEnergyDrainRate() != kWSCPoisonEnergyDrainWithDart) { + g_energyMonitor->setEnergyDrainRate(kWSCPoisonEnergyDrainWithDart); + _vm->setEnergyDeathReason(kDeathDidntStopPoison); + } + } + } else if (g_energyMonitor->getEnergyDrainRate() != kEnergyDrainNormal) { + g_energyMonitor->setEnergyDrainRate(kEnergyDrainNormal); + _vm->resetEnergyDeathReason(); + } +} + +bool WSC::inSynthesizerGame() { + return _moleculesMovie.isMovieValid(); +} + +bool WSC::canSolve() { + return (inSynthesizerGame() || (GameState.getCurrentRoom() == kWSC98 && !GameState.getWSCRobotDead())); +} + +void WSC::doSolve() { + if (inSynthesizerGame()) { + _moleculesMovie.releaseMovie(); + _moleculeBin.cleanUpMoleculeBin(); + requestExtraSequence(kW03NorthFinishSynthesis, kExtraCompletedFlag, kFilterNoInput); + } else if (GameState.getCurrentRoom() == kWSC98 && !GameState.getWSCRobotDead()) { + cancelEvent(); + startExtraSequence(kW98RobotShocked, kExtraCompletedFlag, kFilterNoInput); + } +} + +Common::String WSC::getNavMovieName() { + return "Images/World Science Center/WSC.movie"; +} + +Common::String WSC::getSoundSpotsName() { + return "Sounds/World Science Center/WSC Spots"; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/wsc/wsc.h b/engines/pegasus/neighborhood/wsc/wsc.h new file mode 100644 index 0000000000..d9634b3539 --- /dev/null +++ b/engines/pegasus/neighborhood/wsc/wsc.h @@ -0,0 +1,166 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_NEIGHBORHOOD_WSC_WSC_H +#define PEGASUS_NEIGHBORHOOD_WSC_WSC_H + +#include "pegasus/neighborhood/neighborhood.h" +#include "pegasus/neighborhood/wsc/moleculebin.h" + +namespace Pegasus { + +static const DisplayOrder kWSCMoleculeBinOrder = kMonitorLayer; +static const DisplayOrder kWSCMoleculesMovieOrder = kWSCMoleculeBinOrder + 1; + +static const RoomID kWSC01 = 0; +static const RoomID kWSC02Morph = 2; +static const RoomID kWSC02Messages = 3; +static const RoomID kWSC62 = 62; + +class WSC : public Neighborhood { +public: + WSC(InputHandler *, PegasusEngine *); + virtual ~WSC() {} + + void flushGameState(); + + virtual uint16 getDateResID() const; + + bool okayToJump(); + + void checkContinuePoint(const RoomID, const DirectionConstant); + + bool inSynthesizerGame(); + + bool canSolve(); + void doSolve(); + + virtual void prepareForAIHint(const Common::String &); + virtual void cleanUpAfterAIHint(const Common::String &); + + void init(); + void start(); + +protected: + enum { + kWSCDraggingAntidoteFlag, + + kWSCPrivateLabMessagesOpenFlag, + kWSCPrivateInterruptedMorphFlag, + kWSCPrivateInMoleculeGameFlag, + kWSCPrivateSinclairOfficeOpenFlag, + kWSCPrivateOfficeLogOpenFlag, + kWSCPrivate58SouthOpenFlag, + kWSCPrivateClickedCatwalkCableFlag, + kWSCPrivateRobotHeadOpenFlag, + + kWSCPrivateSeenPeopleAt17WestFlag, + kWSCPrivateSeenPeopleAt19NorthFlag, + kWSCPrivateSeenPeopleAt21SouthFlag, + kWSCPrivateSeenPeopleAt24SouthFlag, + kWSCPrivateSeenPeopleAt34EastFlag, + kWSCPrivateSeenPeopleAt36WestFlag, + kWSCPrivateSeenPeopleAt38NorthFlag, + kWSCPrivateSeenPeopleAt46SouthFlag, + kWSCPrivateSeenPeopleAt49NorthFlag, + kWSCPrivateSeenPeopleAt73WestFlag, + + kWSCPrivateNeedPeopleAt17WestFlag, + kWSCPrivateNeedPeopleAt21SouthFlag, + kWSCPrivateNeedPeopleAt24SouthFlag, + kWSCPrivateNeedPeopleAt34EastFlag, + kWSCPrivateNeedPeopleAt36WestFlag, + kWSCPrivateNeedPeopleAt38NorthFlag, + kWSCPrivateNeedPeopleAt46SouthFlag, + kWSCPrivateNeedPeopleAt49NorthFlag, + kWSCPrivateNeedPeopleAt73WestFlag, + + kWSCPrivateGotRetScanChipFlag, + kWSCPrivateGotMapChipFlag, + kWSCPrivateGotOpticalChipFlag, + + kNumWSCPrivateFlags + }; + + void arriveAt(const RoomID, const DirectionConstant); + void turnTo(const DirectionConstant); + void receiveNotification(Notification *, const NotificationFlags); + void dropItemIntoRoom(Item *, Hotspot *); + void clickInHotspot(const Input &, const Hotspot *); + TimeValue getViewTime(const RoomID, const DirectionConstant); + void getZoomEntry(const HotSpotID, ZoomTable::Entry &); + CanMoveForwardReason canMoveForward(ExitTable::Entry &entry); + void cantMoveThatWay(CanMoveForwardReason reason); + CanTurnReason canTurn(TurnDirection turn, DirectionConstant &nextDir); + void zoomTo(const Hotspot *hotspot); + void activateOneHotspot(HotspotInfoTable::Entry &, Hotspot *); + void setUpMoleculeGame(); + void nextMoleculeGameLevel(); + void startMoleculeGameLevel(); + void moleculeGameClick(const HotSpotID); + void loadAmbientLoops(); + CanOpenDoorReason canOpenDoor(DoorTable::Entry &); + void cantOpenDoor(CanOpenDoorReason); + void pickedUpItem(Item *); + void doorOpened(); + void startExtraSequence(const ExtraID, const NotificationFlags, const InputBits); + void getExtraEntry(const uint32, ExtraTable::Entry &); + void takeItemFromRoom(Item *item); + void checkPeopleCrossing(); + void turnLeft(); + void turnRight(); + void moveForward(); + Hotspot *getItemScreenSpot(Item *, DisplayElement *); + int16 getStaticCompassAngle(const RoomID, const DirectionConstant); + void getExitCompassMove(const ExitTable::Entry &exitEntry, FaderMoveSpec &compassMove); + void getExtraCompassMove(const ExtraTable::Entry &entry, FaderMoveSpec &compassMove); + void bumpIntoWall(); + void activateHotspots(); + void setUpAIRules(); + Common::String getBriefingMovie(); + Common::String getEnvScanMovie(); + uint getNumHints(); + Common::String getHintMovie(uint); + void closeDoorOffScreen(const RoomID, const DirectionConstant); + void setUpPoison(); + void findSpotEntry(const RoomID, const DirectionConstant, SpotFlags, SpotTable::Entry &); + void timerExpired(const uint32); + + Common::String getSoundSpotsName(); + Common::String getNavMovieName(); + + FlagsArray<byte, kNumWSCPrivateFlags> _privateFlags; + const Hotspot *_cachedZoomSpot; + MoleculeBin _moleculeBin; + int32 _moleculeGameLevel, _numCorrect; + Movie _moleculesMovie; + uint32 _levelArray[6]; + Common::Rational _energyDrainRate; + Sprite *_argonSprite; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/zoom.cpp b/engines/pegasus/neighborhood/zoom.cpp new file mode 100644 index 0000000000..478ec6e493 --- /dev/null +++ b/engines/pegasus/neighborhood/zoom.cpp @@ -0,0 +1,74 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "common/debug.h" +#include "common/stream.h" +#include "common/textconsole.h" + +#include "pegasus/neighborhood/zoom.h" + +namespace Pegasus { + +void ZoomTable::loadFromStream(Common::SeekableReadStream *stream) { + uint32 count = stream->readUint32BE(); + _entries.resize(count); + + for (uint32 i = 0; i < count; i++) { + _entries[i].hotspot = stream->readUint16BE(); + _entries[i].movieStart = stream->readUint32BE(); + _entries[i].movieEnd = stream->readUint32BE(); + _entries[i].room = stream->readUint16BE(); + _entries[i].direction = stream->readByte(); + debug(0, "Zoom[%d]: %d %d %d %d %d", i, _entries[i].hotspot, _entries[i].movieStart, + _entries[i].movieEnd, _entries[i].room, _entries[i].direction); + stream->readByte(); // alignment + } +} + +void ZoomTable::clear() { + _entries.clear(); +} + +ZoomTable::Entry::Entry() { + clear(); +} + +void ZoomTable::Entry::clear() { + hotspot = kNoHotSpotID; + movieStart = 0xffffffff; + movieEnd = 0xffffffff; + room = kNoRoomID; + direction = kNoDirection; +} + +ZoomTable::Entry ZoomTable::findEntry(HotSpotID hotspot) { + for (uint32 i = 0; i < _entries.size(); i++) + if (_entries[i].hotspot == hotspot) + return _entries[i]; + + return Entry(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/zoom.h b/engines/pegasus/neighborhood/zoom.h new file mode 100644 index 0000000000..8bcf8974f8 --- /dev/null +++ b/engines/pegasus/neighborhood/zoom.h @@ -0,0 +1,70 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_NEIGHBORHOOD_ZOOM_H +#define PEGASUS_NEIGHBORHOOD_ZOOM_H + +#include "common/array.h" +#include "common/endian.h" + +#include "pegasus/constants.h" + +namespace Common { + class SeekableReadStream; +} + +namespace Pegasus { + +class ZoomTable { +public: + ZoomTable() {} + ~ZoomTable() {} + + static uint32 getResTag() { return MKTAG('Z', 'o', 'o', 'm'); } + + void loadFromStream(Common::SeekableReadStream *stream); + void clear(); + + struct Entry { + Entry(); + void clear(); + bool isEmpty() { return movieStart == 0xffffffff; } + + HotSpotID hotspot; + TimeValue movieStart; + TimeValue movieEnd; + RoomID room; + DirectionConstant direction; + }; + + Entry findEntry(HotSpotID hotspot); + +private: + Common::Array<Entry> _entries; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/notification.cpp b/engines/pegasus/notification.cpp new file mode 100644 index 0000000000..2d57fcc5e7 --- /dev/null +++ b/engines/pegasus/notification.cpp @@ -0,0 +1,149 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/constants.h" +#include "pegasus/notification.h" + +namespace Pegasus { + +typedef ReceiverList::iterator ReceiverIterator; + +Notification::Notification(const NotificationID id, NotificationManager *owner) : IDObject(id) { + _owner = owner; + _currentFlags = kNoNotificationFlags; + if (_owner) + _owner->addNotification(this); +} + +Notification::~Notification() { + for (uint i = 0; i < _receivers.size(); i++) + _receivers[i].receiver->newNotification(NULL); + + if (_owner) + _owner->removeNotification(this); +} + +// Selectively set or clear notificiation bits. +// Wherever mask is 0, leave existing bits untouched. +// Wherever mask is 1, set bit equivalent to flags. +void Notification::notifyMe(NotificationReceiver *receiver, NotificationFlags flags, NotificationFlags mask) { + for (uint i = 0; i < _receivers.size(); i++) { + if (_receivers[i].receiver == receiver) { + _receivers[i].mask = (_receivers[i].mask & ~mask) | (flags & mask); + receiver->newNotification(this); + return; + } + } + + ReceiverEntry newEntry; + newEntry.receiver = receiver; + newEntry.mask = flags; + _receivers.push_back(newEntry); + + receiver->newNotification(this); +} + +void Notification::cancelNotification(NotificationReceiver *receiver) { + for (uint i = 0; i < _receivers.size(); i++) { + if (_receivers[i].receiver == receiver) { + _receivers.remove_at(i); + i--; + } + } +} + +void Notification::setNotificationFlags(NotificationFlags flags, NotificationFlags mask) { + _currentFlags = (_currentFlags & ~mask) | flags; +} + +void Notification::checkReceivers() { + NotificationFlags currentFlags = _currentFlags; + _currentFlags = kNoNotificationFlags; + + for (uint i = 0; i < _receivers.size(); i++) + if (_receivers[i].mask & currentFlags) + _receivers[i].receiver->receiveNotification(this, currentFlags); +} + +// Receiver entries are equal if their receivers are equal. + +int operator==(const ReceiverEntry &entry1, const ReceiverEntry &entry2) { + return entry1.receiver == entry2.receiver; +} + +int operator!=(const ReceiverEntry &entry1, const ReceiverEntry &entry2) { + return entry1.receiver != entry2.receiver; +} + +NotificationReceiver::NotificationReceiver() { + _notification = NULL; +} + +NotificationReceiver::~NotificationReceiver() { + if (_notification) + _notification->cancelNotification(this); +} + +void NotificationReceiver::receiveNotification(Notification *, const NotificationFlags) { +} + +void NotificationReceiver::newNotification(Notification *notification) { + _notification = notification; +} + +typedef NotificationList::iterator NotificationIterator; + +NotificationManager::NotificationManager() { +} + +NotificationManager::~NotificationManager() { + detachNotifications(); +} + +void NotificationManager::addNotification(Notification *notification) { + _notifications.push_back(notification); +} + +void NotificationManager::removeNotification(Notification *notification) { + for (NotificationIterator it = _notifications.begin(); it != _notifications.end();) { + if ((*it) == notification) + it = _notifications.erase(it); + else + it++; + } +} + +void NotificationManager::detachNotifications() { + for (NotificationIterator it = _notifications.begin(); it != _notifications.end(); it++) + (*it)->_owner = 0; +} + +void NotificationManager::checkNotifications() { + for (NotificationIterator it = _notifications.begin(); it != _notifications.end(); it++) + if ((*it)->_currentFlags != kNoNotificationFlags) + (*it)->checkReceivers(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/notification.h b/engines/pegasus/notification.h new file mode 100644 index 0000000000..19b69829be --- /dev/null +++ b/engines/pegasus/notification.h @@ -0,0 +1,123 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_NOTIFICATION_H +#define PEGASUS_NOTIFICATION_H + +#include "common/array.h" +#include "common/list.h" + +#include "pegasus/types.h" +#include "pegasus/util.h" + +namespace Pegasus { + +class NotificationManager; +class NotificationReceiver; + +struct ReceiverEntry { + NotificationReceiver *receiver; + NotificationFlags mask; +}; + +int operator==(const ReceiverEntry &entry1, const ReceiverEntry &entry2); +int operator!=(const ReceiverEntry &entry1, const ReceiverEntry &entry2); + +typedef Common::Array<ReceiverEntry> ReceiverList; + +/* + A notification can have 32 flags associated with it, which can be user-defined. +*/ + +class Notification : public IDObject { +friend class NotificationManager; + +public: + Notification(const NotificationID id, NotificationManager *owner); + virtual ~Notification(); + + // notifyMe will have this receiver notified when any of the specified notification + // flags are set. + // If there is already a notification set for this receiver, notifyMe does a bitwise + // OR with the receiver's current notification flags. + + // Can selectively set or clear notification bits by using the flags and mask argument. + + void notifyMe(NotificationReceiver*, NotificationFlags flags, NotificationFlags mask); + void cancelNotification(NotificationReceiver *receiver); + + void setNotificationFlags(NotificationFlags flags, NotificationFlags mask); + NotificationFlags getNotificationFlags() { return _currentFlags; } + + void clearNotificationFlags() { setNotificationFlags(0, ~(NotificationFlags)0); } + +protected: + void checkReceivers(); + + NotificationManager *_owner; + ReceiverList _receivers; + NotificationFlags _currentFlags; +}; + +class NotificationReceiver { +friend class Notification; + +public: + NotificationReceiver(); + virtual ~NotificationReceiver(); + +protected: + // receiveNotification is called automatically whenever a notification that this + // receiver depends on has its flags set + + virtual void receiveNotification(Notification *, const NotificationFlags); + virtual void newNotification(Notification *notification); + +private: + Notification *_notification; +}; + +typedef Common::List<Notification *> NotificationList; + +class NotificationManager : public NotificationReceiver { +friend class Notification; + +public: + NotificationManager(); + virtual ~NotificationManager(); + + void checkNotifications(); + +protected: + void addNotification(Notification *notification); + void removeNotification(Notification *notification); + void detachNotifications(); + + NotificationList _notifications; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/pegasus.cpp b/engines/pegasus/pegasus.cpp new file mode 100644 index 0000000000..98f0553783 --- /dev/null +++ b/engines/pegasus/pegasus.cpp @@ -0,0 +1,2395 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "common/config-manager.h" +#include "common/error.h" +#include "common/events.h" +#include "common/fs.h" +#include "common/file.h" +#include "common/memstream.h" +#include "common/savefile.h" +#include "common/textconsole.h" +#include "common/translation.h" +#include "common/random.h" +#include "backends/keymapper/keymapper.h" +#include "base/plugins.h" +#include "base/version.h" +#include "gui/saveload.h" +#include "video/qt_decoder.h" + +#include "pegasus/console.h" +#include "pegasus/cursor.h" +#include "pegasus/energymonitor.h" +#include "pegasus/gamestate.h" +#include "pegasus/interface.h" +#include "pegasus/menu.h" +#include "pegasus/movie.h" +#include "pegasus/pegasus.h" +#include "pegasus/timers.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/itemlist.h" +#include "pegasus/items/biochips/aichip.h" +#include "pegasus/items/biochips/biochipitem.h" +#include "pegasus/items/biochips/mapchip.h" +#include "pegasus/items/biochips/opticalchip.h" +#include "pegasus/items/biochips/pegasuschip.h" +#include "pegasus/items/biochips/retscanchip.h" +#include "pegasus/items/biochips/shieldchip.h" +#include "pegasus/items/inventory/airmask.h" +#include "pegasus/items/inventory/gascanister.h" +#include "pegasus/items/inventory/inventoryitem.h" +#include "pegasus/items/inventory/keycard.h" +#include "pegasus/neighborhood/neighborhood.h" +#include "pegasus/neighborhood/caldoria/caldoria.h" +#include "pegasus/neighborhood/mars/mars.h" +#include "pegasus/neighborhood/norad/constants.h" +#include "pegasus/neighborhood/norad/alpha/noradalpha.h" +#include "pegasus/neighborhood/norad/delta/noraddelta.h" +#include "pegasus/neighborhood/prehistoric/prehistoric.h" +#include "pegasus/neighborhood/tsa/fulltsa.h" +#include "pegasus/neighborhood/tsa/tinytsa.h" +#include "pegasus/neighborhood/wsc/wsc.h" + +namespace Pegasus { + +PegasusEngine::PegasusEngine(OSystem *syst, const PegasusGameDescription *gamedesc) : Engine(syst), InputHandler(0), _gameDescription(gamedesc), + _shellNotification(kJMPDCShellNotificationID, this), _returnHotspot(kInfoReturnSpotID), _itemDragger(this), _bigInfoMovie(kNoDisplayElement), + _smallInfoMovie(kNoDisplayElement) { + _continuePoint = 0; + _saveAllowed = _loadAllowed = true; + _saveRequested = _loadRequested = false; + _gameMenu = 0; + _deathReason = kDeathStranded; + _neighborhood = 0; + _FXLevel = 0x80; + _ambientLevel = 0x80; + _gameMode = kNoMode; + _switchModesSync = false; + _draggingItem = 0; + _dragType = kDragNoDrag; + _idlerHead = 0; + _currentCD = 1; + _introTimer = 0; + _aiSaveStream = 0; +} + +PegasusEngine::~PegasusEngine() { + delete _resFork; + delete _console; + delete _cursor; + delete _continuePoint; + delete _gameMenu; + delete _neighborhood; + delete _rnd; + delete _introTimer; + delete _aiSaveStream; + + for (ItemIterator it = _allItems.begin(); it != _allItems.end(); it++) + delete *it; + + InputDeviceManager::destroy(); + GameStateManager::destroy(); + + // NOTE: This must be deleted last! + delete _gfx; +} + +Common::Error PegasusEngine::run() { + _console = new PegasusConsole(this); + _gfx = new GraphicsManager(this); + _resFork = new Common::MacResManager(); + _cursor = new Cursor(); + _rnd = new Common::RandomSource("Pegasus"); + + if (!_resFork->open("JMP PP Resources") || !_resFork->hasResFork()) + error("Could not load JMP PP Resources"); + + // Initialize items + createItems(); + + // Initialize cursors + _cursor->addCursorFrames(0x80); // Main + _cursor->addCursorFrames(900); // Mars Shuttle + + // Initialize the item dragger bounds + _itemDragger.setHighlightBounds(); + + if (!isDemo() && !detectOpeningClosingDirectory()) { + Common::String message = "Missing intro directory. "; + + // Give Mac OS X a more specific message because we can +#ifdef MACOSX + message += "Make sure \"Opening/Closing\" is present."; +#else + message += "Be sure to rename \"Opening/Closing\" to \"Opening_Closing\"."; +#endif + + GUIErrorMessage(message); + warning("%s", message.c_str()); + return Common::kNoGameDataFoundError; + } + + // Set up input + initKeymap(); + InputHandler::setInputHandler(this); + allowInput(true); + + // Set up inventories + _items.setWeightLimit(9); + _items.setOwnerID(kPlayerID); + _biochips.setWeightLimit(8); + _biochips.setOwnerID(kPlayerID); + + _returnHotspot.setArea(Common::Rect(kNavAreaLeft, kNavAreaTop, 512 + kNavAreaLeft, 256 + kNavAreaTop)); + _returnHotspot.setHotspotFlags(kInfoReturnSpotFlag); + _allHotspots.push_back(&_returnHotspot); + + _screenDimmer.setBounds(Common::Rect(0, 0, 640, 480)); + _screenDimmer.setDisplayOrder(kScreenDimmerOrder); + + // Load from the launcher/cli if requested (and don't show the intro in those cases) + bool doIntro = true; + if (ConfMan.hasKey("save_slot")) { + uint32 gameToLoad = ConfMan.getInt("save_slot"); + doIntro = (loadGameState(gameToLoad).getCode() != Common::kNoError); + } + + _shellNotification.notifyMe(this, kJMPShellNotificationFlags, kJMPShellNotificationFlags); + + if (doIntro) + // Start up the first notification + _shellNotification.setNotificationFlags(kGameStartingFlag, kGameStartingFlag); + + if (!isDemo()) { + _introTimer = new FuseFunction(); + _introTimer->setFunctor(new Common::Functor0Mem<void, PegasusEngine>(this, &PegasusEngine::introTimerExpired)); + } + + while (!shouldQuit()) { + processShell(); + _system->delayMillis(10); // Ease off the CPU + } + + return Common::kNoError; +} + +bool PegasusEngine::canLoadGameStateCurrently() { + return _loadAllowed && !isDemo(); +} + +bool PegasusEngine::canSaveGameStateCurrently() { + return _saveAllowed && !isDemo() && g_neighborhood; +} + +bool PegasusEngine::detectOpeningClosingDirectory() { + // We need to detect what our Opening/Closing directory is listed as + // On the original disc, it was 'Opening/Closing' but only HFS(+) supports the slash + // Mac OS X will display this as 'Opening:Closing' and we can use that directly + // On other systems, users will need to rename to "Opening_Closing" + + Common::FSNode gameDataDir(ConfMan.get("path")); + gameDataDir = gameDataDir.getChild("Images"); + + if (!gameDataDir.exists()) + return false; + + Common::FSList fsList; + if (!gameDataDir.getChildren(fsList, Common::FSNode::kListDirectoriesOnly, true)) + return false; + + for (uint i = 0; i < fsList.size() && _introDirectory.empty(); i++) { + Common::String name = fsList[i].getName(); + + if (name.equalsIgnoreCase("Opening:Closing")) + _introDirectory = name; + else if (name.equalsIgnoreCase("Opening_Closing")) + _introDirectory = name; + } + + if (_introDirectory.empty()) + return false; + + debug(0, "Detected intro location as '%s'", _introDirectory.c_str()); + _introDirectory = Common::String("Images/") + _introDirectory; + return true; +} + +void PegasusEngine::createItems() { + Common::SeekableReadStream *res = _resFork->getResource(MKTAG('N', 'I', 't', 'm'), 0x80); + + uint16 entryCount = res->readUint16BE(); + + for (uint16 i = 0; i < entryCount; i++) { + ItemID itemID = res->readUint16BE(); + NeighborhoodID neighborhoodID = res->readUint16BE(); + RoomID roomID = res->readUint16BE(); + DirectionConstant direction = res->readByte(); + res->readByte(); // alignment + + createItem(itemID, neighborhoodID, roomID, direction); + } + + delete res; +} + +void PegasusEngine::createItem(ItemID itemID, NeighborhoodID neighborhoodID, RoomID roomID, DirectionConstant direction) { + switch (itemID) { + case kInterfaceBiochip: + // Unused in game, but still in the data and we need to create + // it because it's saved/loaded from save files. + new BiochipItem(itemID, neighborhoodID, roomID, direction); + break; + case kAIBiochip: + new AIChip(itemID, neighborhoodID, roomID, direction); + break; + case kPegasusBiochip: + new PegasusChip(itemID, neighborhoodID, roomID, direction); + break; + case kOpticalBiochip: + new OpticalChip(itemID, neighborhoodID, roomID, direction); + break; + case kMapBiochip: + new MapChip(itemID, neighborhoodID, roomID, direction); + break; + case kRetinalScanBiochip: + new RetScanChip(itemID, neighborhoodID, roomID, direction); + break; + case kShieldBiochip: + new ShieldChip(itemID, neighborhoodID, roomID, direction); + break; + case kAirMask: + new AirMask(itemID, neighborhoodID, roomID, direction); + break; + case kKeyCard: + new KeyCard(itemID, neighborhoodID, roomID, direction); + break; + case kGasCanister: + new GasCanister(itemID, neighborhoodID, roomID, direction); + break; + default: + // Everything else is a normal inventory item + new InventoryItem(itemID, neighborhoodID, roomID, direction); + break; + } +} + +void PegasusEngine::runIntro() { + stopIntroTimer(); + + bool skipped = false; + + Video::VideoDecoder *video = new Video::QuickTimeDecoder(); + if (video->loadFile(_introDirectory + "/BandaiLogo.movie")) { + video->start(); + + while (!shouldQuit() && !video->endOfVideo() && !skipped) { + if (video->needsUpdate()) { + const Graphics::Surface *frame = video->decodeNextFrame(); + + if (frame) { + _system->copyRectToScreen((byte *)frame->pixels, frame->pitch, 0, 0, frame->w, frame->h); + _system->updateScreen(); + } + } + + Input input; + InputDevice.getInput(input, kFilterAllInput); + if (input.anyInput()) + skipped = true; + + _system->delayMillis(10); + } + } + + delete video; + + if (shouldQuit() || skipped) + return; + + video = new Video::QuickTimeDecoder(); + + if (!video->loadFile(_introDirectory + "/Big Movie.movie")) + error("Could not load intro movie"); + + video->seek(Audio::Timestamp(0, 10 * 600, 600)); + video->start(); + + playMovieScaled(video, 0, 0); + + delete video; +} + +Common::Error PegasusEngine::showLoadDialog() { + GUI::SaveLoadChooser slc(_("Load game:"), _("Load"), false); + + Common::String gameId = ConfMan.get("gameid"); + + const EnginePlugin *plugin = 0; + EngineMan.findGame(gameId, &plugin); + + int slot = slc.runModalWithPluginAndTarget(plugin, ConfMan.getActiveDomainName()); + + Common::Error result; + + if (slot >= 0) { + if (loadGameState(slot).getCode() == Common::kNoError) + result = Common::kNoError; + else + result = Common::kUnknownError; + } else { + result = Common::kUserCanceled; + } + + return result; +} + +Common::Error PegasusEngine::showSaveDialog() { + GUI::SaveLoadChooser slc(_("Save game:"), _("Save"), true); + + Common::String gameId = ConfMan.get("gameid"); + + const EnginePlugin *plugin = 0; + EngineMan.findGame(gameId, &plugin); + + int slot = slc.runModalWithPluginAndTarget(plugin, ConfMan.getActiveDomainName()); + + Common::Error result; + + if (slot >= 0) { + if (saveGameState(slot, slc.getResultString()).getCode() == Common::kNoError) + result = Common::kNoError; + else + result = Common::kUnknownError; + } else { + result = Common::kUserCanceled; + } + + return result; +} + +GUI::Debugger *PegasusEngine::getDebugger() { + return _console; +} + +void PegasusEngine::addIdler(Idler *idler) { + idler->_nextIdler = _idlerHead; + if (_idlerHead) + _idlerHead->_prevIdler = idler; + idler->_prevIdler = 0; + _idlerHead = idler; +} + +void PegasusEngine::removeIdler(Idler *idler) { + if (idler->_prevIdler) + idler->_prevIdler->_nextIdler = idler->_nextIdler; + if (idler->_nextIdler) + idler->_nextIdler->_prevIdler = idler->_prevIdler; + if (idler == _idlerHead) + _idlerHead = idler->_nextIdler; + idler->_nextIdler = 0; + idler->_prevIdler = 0; +} + +void PegasusEngine::giveIdleTime() { + for (Idler *idler = _idlerHead; idler != 0; idler = idler->_nextIdler) + idler->useIdleTime(); +} + +void PegasusEngine::addTimeBase(TimeBase *timeBase) { + _timeBases.push_back(timeBase); +} + +void PegasusEngine::removeTimeBase(TimeBase *timeBase) { + _timeBases.remove(timeBase); +} + +bool PegasusEngine::loadFromStream(Common::ReadStream *stream) { + // Dispose currently running stuff + useMenu(0); + useNeighborhood(0); + removeAllItemsFromInventory(); + removeAllItemsFromBiochips(); + _currentItemID = kNoItemID; + _currentBiochipID = kNoItemID; + + if (!g_interface) + createInterface(); + + // Signature + uint32 creator = stream->readUint32BE(); + if (creator != kPegasusPrimeCreator) { + warning("Bad save creator '%s'", tag2str(creator)); + return false; + } + + uint32 gameType = stream->readUint32BE(); + int saveType; + + switch (gameType) { + case kPegasusPrimeDisk1GameType: + case kPegasusPrimeDisk2GameType: + case kPegasusPrimeDisk3GameType: + case kPegasusPrimeDisk4GameType: + _currentCD = gameType - kPegasusPrimeDisk1GameType + 1; + saveType = kNormalSave; + break; + case kPegasusPrimeContinueType: + saveType = kContinueSave; + break; + default: + // There are five other possible game types on the Pippin + // version, but hopefully we don't see any of those here + warning("Unhandled pegasus game type '%s'", tag2str(gameType)); + return false; + } + + uint32 version = stream->readUint32BE(); + if (version != kPegasusPrimeVersion) { + warning("Where did you get this save? It's a beta (v%04x)!", version & 0x7fff); + return false; + } + + // Game State + GameState.readGameState(stream); + + // Energy + setLastEnergyValue(stream->readUint32BE()); + + // Death reason + setEnergyDeathReason(stream->readByte()); + + // Items + _allItems.readFromStream(stream); + + // Inventory + byte itemCount = stream->readByte(); + + if (itemCount > 0) { + for (byte i = 0; i < itemCount; i++) { + InventoryItem *inv = (InventoryItem *)_allItems.findItemByID((ItemID)stream->readUint16BE()); + addItemToInventory(inv); + } + + g_interface->setCurrentInventoryItemID((ItemID)stream->readUint16BE()); + } + + // Biochips + byte biochipCount = stream->readByte(); + + if (biochipCount > 0) { + for (byte i = 0; i < biochipCount; i++) { + BiochipItem *biochip = (BiochipItem *)_allItems.findItemByID((ItemID)stream->readUint16BE()); + addItemToBiochips(biochip); + } + + g_interface->setCurrentBiochipID((ItemID)stream->readUint16BE()); + } + + + // TODO: Disc check + + // Jump to environment + jumpToNewEnvironment(GameState.getCurrentNeighborhood(), GameState.getCurrentRoom(), GameState.getCurrentDirection()); + _shellNotification.setNotificationFlags(0, kNeedNewJumpFlag); + performJump(GameState.getCurrentNeighborhood()); + + // AI rules + if (g_AIArea) + g_AIArea->readAIRules(stream); + + startNeighborhood(); + + // Make a new continue point if this isn't already one + if (saveType == kNormalSave) + makeContinuePoint(); + + return true; +} + +bool PegasusEngine::writeToStream(Common::WriteStream *stream, int saveType) { + // WORKAROUND: If we don't have the interface, we can't actually save. + // However, we should still have a continue point, so we will just dump that + // out. This is needed for saving a game while in the space chase. + if (!g_interface) { + // Saving a continue stream from a continue stream should + // never happen. In addition, we do need to have a continue + // stream for this to work. + if (saveType != kNormalSave || !_continuePoint) + return false; + + writeContinueStream(stream); + return true; + } + + if (g_neighborhood) + g_neighborhood->flushGameState(); + + // Signature + stream->writeUint32BE(kPegasusPrimeCreator); + + if (saveType == kNormalSave) + stream->writeUint32BE(kPegasusPrimeDisk1GameType + _currentCD - 1); + else // Continue + stream->writeUint32BE(kPegasusPrimeContinueType); + + stream->writeUint32BE(kPegasusPrimeVersion); + + // Game State + GameState.writeGameState(stream); + + // Energy + stream->writeUint32BE(getSavedEnergyValue()); + + // Death reason + stream->writeByte(getEnergyDeathReason()); + + // Items + _allItems.writeToStream(stream); + + // Inventory + byte itemCount = _items.getNumItems(); + stream->writeByte(itemCount); + + if (itemCount > 0) { + for (uint32 i = 0; i < itemCount; i++) + stream->writeUint16BE(_items.getItemIDAt(i)); + + stream->writeUint16BE(g_interface->getCurrentInventoryItem()->getObjectID()); + } + + // Biochips + byte biochipCount = _biochips.getNumItems(); + stream->writeByte(biochipCount); + + if (biochipCount > 0) { + for (uint32 i = 0; i < biochipCount; i++) + stream->writeUint16BE(_biochips.getItemIDAt(i)); + + stream->writeUint16BE(g_interface->getCurrentBiochip()->getObjectID()); + } + + // AI rules + if (g_AIArea) + g_AIArea->writeAIRules(stream); + + return true; +} + +void PegasusEngine::makeContinuePoint() { + // WORKAROUND: Do not attempt to make a continue point if the interface is not set + // up. The original did *not* do this and attempting to restore the game using the pause + // menu during the canyon/space chase sequence would segfault the game and crash the + // system. Nice! + if (!g_interface) + return; + + delete _continuePoint; + + Common::MemoryWriteStreamDynamic newPoint(DisposeAfterUse::NO); + writeToStream(&newPoint, kContinueSave); + _continuePoint = new Common::MemoryReadStream(newPoint.getData(), newPoint.size(), DisposeAfterUse::YES); +} + +void PegasusEngine::loadFromContinuePoint() { + // Failure to load a continue point is fatal + + if (!_continuePoint) + error("Attempting to load from non-existant continue point"); + + _continuePoint->seek(0); + + if (!loadFromStream(_continuePoint)) + error("Failed loading continue point"); +} + +void PegasusEngine::writeContinueStream(Common::WriteStream *stream) { + // We're going to pretty much copy the stream, except for the save type + _continuePoint->seek(0); + stream->writeUint32BE(_continuePoint->readUint32BE()); + _continuePoint->readUint32BE(); // skip the continue type + stream->writeUint32BE(kPegasusPrimeDisk1GameType + _currentCD - 1); + + // Now just copy over the rest + uint32 size = _continuePoint->size() - _continuePoint->pos(); + byte *data = new byte[size]; + _continuePoint->read(data, size); + stream->write(data, size); + delete[] data; +} + +Common::Error PegasusEngine::loadGameState(int slot) { + Common::StringArray filenames = _saveFileMan->listSavefiles("pegasus-*.sav"); + Common::InSaveFile *loadFile = _saveFileMan->openForLoading(filenames[slot]); + if (!loadFile) + return Common::kUnknownError; + + bool valid = loadFromStream(loadFile); + delete loadFile; + + return valid ? Common::kNoError : Common::kUnknownError; +} + +Common::Error PegasusEngine::saveGameState(int slot, const Common::String &desc) { + Common::String output = Common::String::format("pegasus-%s.sav", desc.c_str()); + Common::OutSaveFile *saveFile = _saveFileMan->openForSaving(output, false); + if (!saveFile) + return Common::kUnknownError; + + bool valid = writeToStream(saveFile, kNormalSave); + delete saveFile; + + return valid ? Common::kNoError : Common::kUnknownError; +} + +void PegasusEngine::receiveNotification(Notification *notification, const NotificationFlags flags) { + if (&_shellNotification == notification) { + switch (flags) { + case kGameStartingFlag: { + useMenu(new MainMenu()); + + if (!isDemo()) { + runIntro(); + resetIntroTimer(); + } else { + showTempScreen("Images/Demo/NGsplashScrn.pict"); + } + + if (shouldQuit()) + return; + + _gfx->invalRect(Common::Rect(0, 0, 640, 480)); + _gfx->updateDisplay(); + ((MainMenu *)_gameMenu)->startMainMenuLoop(); + break; + } + case kPlayerDiedFlag: + doDeath(); + break; + case kNeedNewJumpFlag: + performJump(GameState.getNextNeighborhood()); + startNeighborhood(); + break; + default: + break; + } + } +} + +void PegasusEngine::checkCallBacks() { + for (Common::List<TimeBase *>::iterator it = _timeBases.begin(); it != _timeBases.end(); it++) + (*it)->checkCallBacks(); +} + +void PegasusEngine::resetIntroTimer() { + if (!isDemo() && _gameMenu && _gameMenu->getObjectID() == kMainMenuID) { + _introTimer->stopFuse(); + _introTimer->primeFuse(kIntroTimeOut); + _introTimer->lightFuse(); + } +} + +void PegasusEngine::introTimerExpired() { + if (_gameMenu && _gameMenu->getObjectID() == kMainMenuID) { + ((MainMenu *)_gameMenu)->stopMainMenuLoop(); + + bool skipped = false; + + Video::VideoDecoder *video = new Video::QuickTimeDecoder(); + if (!video->loadFile(_introDirectory + "/LilMovie.movie")) + error("Failed to load little movie"); + + bool saveAllowed = swapSaveAllowed(false); + bool openAllowed = swapLoadAllowed(false); + + video->start(); + skipped = playMovieScaled(video, 0, 0); + + delete video; + + if (shouldQuit()) + return; + + if (!skipped) { + runIntro(); + + if (shouldQuit()) + return; + } + + resetIntroTimer(); + _gfx->invalRect(Common::Rect(0, 0, 640, 480)); + + swapSaveAllowed(saveAllowed); + swapLoadAllowed(openAllowed); + + _gfx->updateDisplay(); + ((MainMenu *)_gameMenu)->startMainMenuLoop(); + } +} + +void PegasusEngine::stopIntroTimer() { + if (_introTimer) + _introTimer->stopFuse(); +} + +void PegasusEngine::delayShell(TimeValue time, TimeScale scale) { + if (time == 0 || scale == 0) + return; + + uint32 startTime = g_system->getMillis(); + uint32 timeInMillis = time * 1000 / scale; + + while (g_system->getMillis() < startTime + timeInMillis) { + checkCallBacks(); + _gfx->updateDisplay(); + } +} + +void PegasusEngine::useMenu(GameMenu *newMenu) { + if (_gameMenu) { + _gameMenu->restorePreviousHandler(); + delete _gameMenu; + } + + _gameMenu = newMenu; + + if (_gameMenu) + _gameMenu->becomeCurrentHandler(); +} + +bool PegasusEngine::checkGameMenu() { + GameMenuCommand command = kMenuCmdNoCommand; + + if (_gameMenu) { + command = _gameMenu->getLastCommand(); + if (command != kMenuCmdNoCommand) { + _gameMenu->clearLastCommand(); + doGameMenuCommand(command); + } + } + + return command != kMenuCmdNoCommand; +} + +void PegasusEngine::doGameMenuCommand(const GameMenuCommand command) { + Common::Error result; + + switch (command) { + case kMenuCmdStartAdventure: + stopIntroTimer(); + GameState.setWalkthroughMode(false); + startNewGame(); + break; + case kMenuCmdCredits: + if (isDemo()) { + showTempScreen("Images/Demo/DemoCredits.pict"); + _gfx->doFadeOutSync(); + _gfx->updateDisplay(); + _gfx->doFadeInSync(); + } else { + stopIntroTimer(); + _gfx->doFadeOutSync(); + useMenu(new CreditsMenu()); + _gfx->updateDisplay(); + _gfx->doFadeInSync(); + } + break; + case kMenuCmdQuit: + case kMenuCmdDeathQuitDemo: + if (isDemo()) + showTempScreen("Images/Demo/NGquitScrn.pict"); + _gfx->doFadeOutSync(); + quitGame(); + break; + case kMenuCmdOverview: + stopIntroTimer(); + doInterfaceOverview(); + resetIntroTimer(); + break; + case kMenuCmdStartWalkthrough: + stopIntroTimer(); + GameState.setWalkthroughMode(true); + startNewGame(); + break; + case kMenuCmdRestore: + stopIntroTimer(); + // fall through + case kMenuCmdDeathRestore: + result = showLoadDialog(); + if (command == kMenuCmdRestore && result.getCode() != Common::kNoError) + resetIntroTimer(); + break; + case kMenuCmdCreditsMainMenu: + _gfx->doFadeOutSync(); + useMenu(new MainMenu()); + _gfx->updateDisplay(); + ((MainMenu *)_gameMenu)->startMainMenuLoop(); + _gfx->doFadeInSync(); + resetIntroTimer(); + break; + case kMenuCmdDeathContinue: + if (((DeathMenu *)_gameMenu)->playerWon()) { + if (isDemo()) { + showTempScreen("Images/Demo/DemoCredits.pict"); + _gfx->doFadeOutSync(); + _gfx->updateDisplay(); + _gfx->doFadeInSync(); + } else { + _gfx->doFadeOutSync(); + useMenu(0); + _gfx->clearScreen(); + _gfx->updateDisplay(); + + Video::VideoDecoder *video = new Video::QuickTimeDecoder(); + if (!video->loadFile(_introDirectory + "/Closing.movie")) + error("Could not load closing movie"); + + uint16 x = (640 - video->getWidth() * 2) / 2; + uint16 y = (480 - video->getHeight() * 2) / 2; + + video->start(); + playMovieScaled(video, x, y); + + delete video; + + if (shouldQuit()) + return; + + useMenu(new MainMenu()); + _gfx->updateDisplay(); + ((MainMenu *)_gameMenu)->startMainMenuLoop(); + _gfx->doFadeInSync(); + resetIntroTimer(); + } + } else { + loadFromContinuePoint(); + } + break; + case kMenuCmdDeathMainMenuDemo: + case kMenuCmdDeathMainMenu: + _gfx->doFadeOutSync(); + useMenu(new MainMenu()); + _gfx->updateDisplay(); + ((MainMenu *)_gameMenu)->startMainMenuLoop(); + _gfx->doFadeInSync(); + resetIntroTimer(); + break; + case kMenuCmdPauseSave: + if (showSaveDialog().getCode() != Common::kUserCanceled) + pauseMenu(false); + break; + case kMenuCmdPauseContinue: + pauseMenu(false); + break; + case kMenuCmdPauseRestore: + makeContinuePoint(); + result = showLoadDialog(); + + if (result.getCode() == Common::kNoError) { + // Successfully loaded, unpause the game + pauseMenu(false); + } else if (result.getCode() != Common::kUserCanceled) { + // Try to get us back to a sane state + loadFromContinuePoint(); + } + break; + case kMenuCmdPauseQuit: + _gfx->doFadeOutSync(); + throwAwayEverything(); + pauseMenu(false); + useMenu(new MainMenu()); + _gfx->updateDisplay(); + ((MainMenu *)_gameMenu)->startMainMenuLoop(); + _gfx->doFadeInSync(); + resetIntroTimer(); + break; + case kMenuCmdNoCommand: + break; + default: + error("Unknown menu command %d", command); + } +} + +void PegasusEngine::handleInput(const Input &input, const Hotspot *cursorSpot) { + if (!checkGameMenu()) + shellGameInput(input, cursorSpot); + + // Handle the console here + if (input.isConsoleRequested()) { + _console->attach(); + _console->onFrame(); + } + + // Handle save requests here + if (_saveRequested && _saveAllowed) { + _saveRequested = false; + + // Can only save during a game and not in the demo + if (g_neighborhood && !isDemo()) { + pauseEngine(true); + showSaveDialog(); + pauseEngine(false); + } + } + + // Handle load requests here + if (_loadRequested && _loadAllowed) { + _loadRequested = false; + + // WORKAROUND: Do not entertain load requests when the pause menu is up + // The original did and the game entered a bad state after loading. + // It's theoretically possible to make it so it does work while the + // pause menu is up, but the pause state of the engine is just too weird. + // Just use the pause menu's restore button since it's there for that + // for you to load anyway. + if (!isDemo() && !(_gameMenu && _gameMenu->getObjectID() == kPauseMenuID)) { + pauseEngine(true); + + if (g_neighborhood) { + makeContinuePoint(); + + Common::Error result = showLoadDialog(); + if (result.getCode() != Common::kNoError && result.getCode() != Common::kUserCanceled) + loadFromContinuePoint(); + } else { + if (_introTimer) + _introTimer->stopFuse(); + + Common::Error result = showLoadDialog(); + if (result.getCode() != Common::kNoError) { + if (!_gameMenu) { + useMenu(new MainMenu()); + ((MainMenu *)_gameMenu)->startMainMenuLoop(); + } + + resetIntroTimer(); + } + } + + pauseEngine(false); + } + } +} + +void PegasusEngine::doInterfaceOverview() { + static const short kNumOverviewSpots = 11; + static const Common::Rect overviewSpots[kNumOverviewSpots] = { + Common::Rect(354, 318, 354 + 204, 318 + 12), + Common::Rect(211, 34, 211 + 114, 34 + 28), + Common::Rect(502, 344, 502 + 138, 344 + 120), + Common::Rect(132, 40, 132 + 79, 40 + 22), + Common::Rect(325, 40, 332 + 115, 40 + 22), + Common::Rect(70, 318, 70 + 284, 318 + 12), + Common::Rect(76, 334, 76 + 96, 334 + 96), + Common::Rect(64, 64, 64 + 512, 64 + 256), + Common::Rect(364, 334, 364 + 96, 334 + 96), + Common::Rect(172, 334, 172 + 192, 334 + 96), + Common::Rect(542, 36, 542 + 58, 36 + 20) + }; + + _gfx->doFadeOutSync(); + useMenu(0); + + Picture leftBackground(kNoDisplayElement); + leftBackground.initFromPICTFile("Images/Interface/OVLeft.mac"); + leftBackground.setDisplayOrder(0); + leftBackground.moveElementTo(kBackground1Left, kBackground1Top); + leftBackground.startDisplaying(); + leftBackground.show(); + + Picture topBackground(kNoDisplayElement); + topBackground.initFromPICTFile("Images/Interface/OVTop.mac"); + topBackground.setDisplayOrder(0); + topBackground.moveElementTo(kBackground2Left, kBackground2Top); + topBackground.startDisplaying(); + topBackground.show(); + + Picture rightBackground(kNoDisplayElement); + rightBackground.initFromPICTFile("Images/Interface/OVRight.mac"); + rightBackground.setDisplayOrder(0); + rightBackground.moveElementTo(kBackground3Left, kBackground3Top); + rightBackground.startDisplaying(); + rightBackground.show(); + + Picture bottomBackground(kNoDisplayElement); + bottomBackground.initFromPICTFile("Images/Interface/OVBottom.mac"); + bottomBackground.setDisplayOrder(0); + bottomBackground.moveElementTo(kBackground4Left, kBackground4Top); + bottomBackground.startDisplaying(); + bottomBackground.show(); + + Picture controllerHighlight(kNoDisplayElement); + controllerHighlight.initFromPICTFile("Images/Interface/OVcontrollerHilite.mac"); + controllerHighlight.setDisplayOrder(0); + controllerHighlight.moveElementTo(kOverviewControllerLeft, kOverviewControllerTop); + controllerHighlight.startDisplaying(); + + Movie overviewText(kNoDisplayElement); + overviewText.initFromMovieFile("Images/Interface/Overview Mac.movie"); + overviewText.setDisplayOrder(0); + overviewText.moveElementTo(kNavAreaLeft, kNavAreaTop); + overviewText.startDisplaying(); + overviewText.show(); + overviewText.redrawMovieWorld(); + + DropHighlight highlight(kNoDisplayElement); + highlight.setDisplayOrder(1); + highlight.startDisplaying(); + highlight.setHighlightThickness(4); + highlight.setHighlightColor(g_system->getScreenFormat().RGBToColor(239, 239, 0)); + highlight.setHighlightCornerDiameter(8); + + Input input; + InputDevice.getInput(input, kFilterAllInput); + + Common::Point cursorLoc; + input.getInputLocation(cursorLoc); + + uint16 i; + for (i = 0; i < kNumOverviewSpots; ++i) + if (overviewSpots[i].contains(cursorLoc)) + break; + + TimeValue time; + if (i == kNumOverviewSpots) + time = 5; + else if (i > 4) + time = i + 1; + else + time = i; + + if (time == 2) { + highlight.hide(); + controllerHighlight.show(); + } else if (i != kNumOverviewSpots) { + controllerHighlight.hide(); + Common::Rect r = overviewSpots[i]; + r.grow(5); + highlight.setBounds(r); + highlight.show(); + } else { + highlight.hide(); + controllerHighlight.hide(); + } + + overviewText.setTime(time * 3 + 2, 15); + overviewText.redrawMovieWorld(); + + _cursor->setCurrentFrameIndex(3); + _cursor->show(); + + _gfx->updateDisplay(); + _gfx->doFadeInSync(); + + for (;;) { + InputDevice.getInput(input, kFilterAllInput); + + if (input.anyInput() || shouldQuit() || _loadRequested || _saveRequested) + break; + + input.getInputLocation(cursorLoc); + for (i = 0; i < kNumOverviewSpots; ++i) + if (overviewSpots[i].contains(cursorLoc)) + break; + + if (i == kNumOverviewSpots) + time = 5; + else if (i > 4) + time = i + 1; + else + time = i; + + if (time == 2) { + highlight.hide(); + controllerHighlight.show(); + } else if (i != kNumOverviewSpots) { + controllerHighlight.hide(); + Common::Rect r = overviewSpots[i]; + r.grow(5); + highlight.setBounds(r); + highlight.show(); + } else { + highlight.hide(); + controllerHighlight.hide(); + } + + // The original just constantly redraws the frame, but that + // doesn't actually need to be done. + if ((time * 3 + 2) * 40 != overviewText.getTime()) { + overviewText.setTime(time * 3 + 2, 15); + overviewText.redrawMovieWorld(); + } + + refreshDisplay(); + _system->delayMillis(10); + } + + if (shouldQuit()) + return; + + highlight.hide(); + _cursor->hide(); + + _gfx->doFadeOutSync(); + useMenu(new MainMenu()); + _gfx->updateDisplay(); + ((MainMenu *)_gameMenu)->startMainMenuLoop(); + _gfx->doFadeInSync(); + + _saveRequested = false; + _loadRequested = false; +} + +void PegasusEngine::showTempScreen(const Common::String &fileName) { + _gfx->doFadeOutSync(); + + Picture picture(0); + picture.initFromPICTFile(fileName); + picture.setDisplayOrder(kMaxAvailableOrder); + picture.startDisplaying(); + picture.show(); + _gfx->updateDisplay(); + + _gfx->doFadeInSync(); + + // Wait for the next event + bool done = false; + while (!shouldQuit() && !done) { + Common::Event event; + while (_eventMan->pollEvent(event)) { + switch (event.type) { + case Common::EVENT_LBUTTONUP: + case Common::EVENT_RBUTTONUP: + case Common::EVENT_KEYDOWN: + done = true; + break; + default: + break; + } + } + + _system->delayMillis(10); + } +} + +void PegasusEngine::refreshDisplay() { + giveIdleTime(); + _gfx->updateDisplay(); +} + +void PegasusEngine::resetEnergyDeathReason() { + switch (getCurrentNeighborhoodID()) { + case kMarsID: + _deathReason = kDeathArrestedInMars; + break; + case kNoradAlphaID: + case kNoradDeltaID: + _deathReason = kDeathArrestedInNorad; + break; + case kWSCID: + _deathReason = kDeathArrestedInWSC; + break; + default: + _deathReason = kDeathStranded; + break; + } +} + +bool PegasusEngine::playerHasItem(const Item *item) { + return playerHasItemID(item->getObjectID()); +} + +bool PegasusEngine::playerHasItemID(const ItemID itemID) { + return itemInInventory(itemID) || itemInBiochips(itemID); +} + +InventoryItem *PegasusEngine::getCurrentInventoryItem() { + if (g_interface) + return g_interface->getCurrentInventoryItem(); + + return 0; +} + +bool PegasusEngine::itemInInventory(InventoryItem *item) { + return _items.itemInInventory(item); +} + +bool PegasusEngine::itemInInventory(ItemID id) { + return _items.itemInInventory(id); +} + +BiochipItem *PegasusEngine::getCurrentBiochip() { + if (g_interface) + return g_interface->getCurrentBiochip(); + + return 0; +} + +bool PegasusEngine::itemInBiochips(BiochipItem *item) { + return _biochips.itemInInventory(item); +} + +bool PegasusEngine::itemInBiochips(ItemID id) { + return _biochips.itemInInventory(id); +} + +bool PegasusEngine::playerAlive() { + return (_shellNotification.getNotificationFlags() & kPlayerDiedFlag) == 0; +} + +Common::String PegasusEngine::getBriefingMovie() { + if (_neighborhood) + return _neighborhood->getBriefingMovie(); + + return Common::String(); +} + +Common::String PegasusEngine::getEnvScanMovie() { + if (_neighborhood) + return _neighborhood->getEnvScanMovie(); + + return Common::String(); +} + +uint PegasusEngine::getNumHints() { + if (_neighborhood) + return _neighborhood->getNumHints(); + + return 0; +} + +Common::String PegasusEngine::getHintMovie(uint hintNum) { + if (_neighborhood) + return _neighborhood->getHintMovie(hintNum); + + return Common::String(); +} + +bool PegasusEngine::canSolve() { + if (_neighborhood) + return _neighborhood->canSolve(); + + return false; +} + +void PegasusEngine::prepareForAIHint(const Common::String &movieName) { + if (g_neighborhood) + g_neighborhood->prepareForAIHint(movieName); +} + +void PegasusEngine::cleanUpAfterAIHint(const Common::String &movieName) { + if (g_neighborhood) + g_neighborhood->cleanUpAfterAIHint(movieName); +} + +void PegasusEngine::jumpToNewEnvironment(const NeighborhoodID neighborhoodID, const RoomID roomID, const DirectionConstant direction) { + GameState.setNextLocation(neighborhoodID, roomID, direction); + _shellNotification.setNotificationFlags(kNeedNewJumpFlag, kNeedNewJumpFlag); +} + +void PegasusEngine::checkFlashlight() { + if (_neighborhood) + _neighborhood->checkFlashlight(); +} + +bool PegasusEngine::playMovieScaled(Video::VideoDecoder *video, uint16 x, uint16 y) { + bool skipped = false; + + while (!shouldQuit() && !video->endOfVideo() && !skipped) { + if (video->needsUpdate()) { + const Graphics::Surface *frame = video->decodeNextFrame(); + + if (frame) + drawScaledFrame(frame, x, y); + } + + Input input; + InputDevice.getInput(input, kFilterAllInput); + if (input.anyInput() || _saveRequested || _loadRequested) + skipped = true; + + _system->delayMillis(10); + } + + return skipped; +} + +void PegasusEngine::die(const DeathReason reason) { + Input dummy; + if (isDragging()) + _itemDragger.stopTracking(dummy); + + _deathReason = reason; + _shellNotification.setNotificationFlags(kPlayerDiedFlag, kPlayerDiedFlag); +} + +void PegasusEngine::doDeath() { + _gfx->doFadeOutSync(); + throwAwayEverything(); + useMenu(new DeathMenu(_deathReason)); + _gfx->updateDisplay(); + _gfx->doFadeInSync(); +} + +void PegasusEngine::throwAwayEverything() { + if (_items.getNumItems() != 0 && g_interface) + _currentItemID = g_interface->getCurrentInventoryItem()->getObjectID(); + else + _currentItemID = kNoItemID; + + if (_biochips.getNumItems() != 0 && g_interface) + _currentBiochipID = g_interface->getCurrentBiochip()->getObjectID(); + else + _currentBiochipID = kNoItemID; + + useMenu(0); + useNeighborhood(0); + + delete g_interface; + g_interface = 0; +} + +void PegasusEngine::processShell() { + checkCallBacks(); + checkNotifications(); + InputHandler::pollForInput(); + refreshDisplay(); +} + +void PegasusEngine::createInterface() { + if (!g_interface) + new Interface(); + + g_interface->createInterface(); +} + +void PegasusEngine::setGameMode(const GameMode newMode) { + if (newMode != _gameMode && canSwitchGameMode(newMode, _gameMode)) { + switchGameMode(newMode, _gameMode); + _gameMode = newMode; + } +} + +void PegasusEngine::switchGameMode(const GameMode newMode, const GameMode oldMode) { + // Start raising panels before lowering panels, to give the activating panel time + // to set itself up without cutting into the lowering panel's animation time. + + if (_switchModesSync) { + if (newMode == kModeInventoryPick) + raiseInventoryDrawerSync(); + else if (newMode == kModeBiochipPick) + raiseBiochipDrawerSync(); + else if (newMode == kModeInfoScreen) + showInfoScreen(); + + if (oldMode == kModeInventoryPick) + lowerInventoryDrawerSync(); + else if (oldMode == kModeBiochipPick) + lowerBiochipDrawerSync(); + else if (oldMode == kModeInfoScreen) + hideInfoScreen(); + } else { + if (newMode == kModeInventoryPick) + raiseInventoryDrawer(); + else if (newMode == kModeBiochipPick) + raiseBiochipDrawer(); + else if (newMode == kModeInfoScreen) + showInfoScreen(); + + if (oldMode == kModeInventoryPick) + lowerInventoryDrawer(); + else if (oldMode == kModeBiochipPick) + lowerBiochipDrawer(); + else if (oldMode == kModeInfoScreen) + hideInfoScreen(); + } +} + +bool PegasusEngine::canSwitchGameMode(const GameMode newMode, const GameMode oldMode) { + // WORKAROUND: Don't allow game mode switches when the interface is not set up. + // Prevents segfaults when pressing 'i' when in the space chase. + if (!g_interface) + return false; + if (newMode == kModeInventoryPick && oldMode == kModeBiochipPick) + return false; + if (newMode == kModeBiochipPick && oldMode == kModeInventoryPick) + return false; + return true; +} + +bool PegasusEngine::itemInLocation(const ItemID itemID, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) { + NeighborhoodID itemNeighborhood; + RoomID itemRoom; + DirectionConstant itemDirection; + + Item *item = _allItems.findItemByID(itemID); + item->getItemRoom(itemNeighborhood, itemRoom, itemDirection); + + return itemNeighborhood == neighborhood && itemRoom == room && itemDirection == direction; +} + +InventoryResult PegasusEngine::addItemToInventory(InventoryItem *item) { + InventoryResult result; + + do { + if (g_interface) + result = g_interface->addInventoryItem(item); + else + result = _items.addItem(item); + + if (result == kTooMuchWeight) + destroyInventoryItem(pickItemToDestroy()); + } while (result != kInventoryOK); + + GameState.setTakenItem(item, true); + if (g_neighborhood) + g_neighborhood->pickedUpItem(item); + + g_AIArea->checkMiddleArea(); + + return result; +} + +void PegasusEngine::useNeighborhood(Neighborhood *neighborhood) { + delete _neighborhood; + _neighborhood = neighborhood; + + if (_neighborhood) { + InputHandler::setInputHandler(_neighborhood); + _neighborhood->init(); + _neighborhood->moveNavTo(kNavAreaLeft, kNavAreaTop); + g_interface->setDate(_neighborhood->getDateResID()); + } else { + InputHandler::setInputHandler(this); + } +} + +void PegasusEngine::performJump(NeighborhoodID neighborhoodID) { + if (_neighborhood) + useNeighborhood(0); + + // Sub chase is special + if (neighborhoodID == kNoradSubChaseID) { + throwAwayEverything(); + _loadAllowed = false; + doSubChase(); + + if (shouldQuit()) + return; + + neighborhoodID = kNoradDeltaID; + GameState.setNextRoom(kNorad41); + GameState.setNextDirection(kEast); + _loadAllowed = true; + } + + Neighborhood *neighborhood; + makeNeighborhood(neighborhoodID, neighborhood); + useNeighborhood(neighborhood); + + // Update the CD variable (used just for saves currently) + _currentCD = getNeighborhoodCD(neighborhoodID); +} + +void PegasusEngine::startNeighborhood() { + if (g_interface && _currentItemID != kNoItemID) + g_interface->setCurrentInventoryItemID(_currentItemID); + + if (g_interface && _currentBiochipID != kNoItemID) + g_interface->setCurrentBiochipID(_currentBiochipID); + + setGameMode(kModeNavigation); + + if (_neighborhood) + _neighborhood->start(); +} + +void PegasusEngine::startNewGame() { + // WORKAROUND: The original game ignored the menu difficulty + // setting. We're going to pass it through here so that + // the menu actually makes sense now. + bool isWalkthrough = GameState.getWalkthroughMode(); + GameState.resetGameState(); + GameState.setWalkthroughMode(isWalkthrough); + + // TODO: Enable erase + _gfx->doFadeOutSync(); + useMenu(0); + _gfx->updateDisplay(); + _gfx->enableUpdates(); + + createInterface(); + + if (isDemo()) { + setLastEnergyValue(kFullEnergy); + jumpToNewEnvironment(kPrehistoricID, kPrehistoric02, kSouth); + GameState.setPrehistoricSeenTimeStream(false); + GameState.setPrehistoricSeenFlyer1(false); + GameState.setPrehistoricSeenFlyer2(false); + GameState.setPrehistoricSeenBridgeZoom(false); + GameState.setPrehistoricBreakerThrown(false); + } else { + jumpToNewEnvironment(kCaldoriaID, kCaldoria00, kEast); + } + + removeAllItemsFromInventory(); + removeAllItemsFromBiochips(); + + BiochipItem *biochip = (BiochipItem *)_allItems.findItemByID(kAIBiochip); + addItemToBiochips(biochip); + + if (isDemo()) { + biochip = (BiochipItem *)_allItems.findItemByID(kPegasusBiochip); + addItemToBiochips(biochip); + biochip = (BiochipItem *)_allItems.findItemByID(kMapBiochip); + addItemToBiochips(biochip); + InventoryItem *item = (InventoryItem *)_allItems.findItemByID(kKeyCard); + addItemToInventory(item); + item = (InventoryItem *)_allItems.findItemByID(kJourneymanKey); + addItemToInventory(item); + _currentItemID = kJourneymanKey; + } else { + _currentItemID = kNoItemID; + } + + _currentBiochipID = kAIBiochip; + + // Clear jump notification flags and just perform the jump... + _shellNotification.setNotificationFlags(0, kNeedNewJumpFlag); + + performJump(GameState.getNextNeighborhood()); + + startNeighborhood(); +} + +void PegasusEngine::makeNeighborhood(NeighborhoodID neighborhoodID, Neighborhood *&neighborhood) { + // TODO: CD check + + switch (neighborhoodID) { + case kCaldoriaID: + neighborhood = new Caldoria(g_AIArea, this); + break; + case kMarsID: + neighborhood = new Mars(g_AIArea, this); + break; + case kPrehistoricID: + neighborhood = new Prehistoric(g_AIArea, this); + break; + case kFullTSAID: + neighborhood = new FullTSA(g_AIArea, this); + break; + case kTinyTSAID: + neighborhood = new TinyTSA(g_AIArea, this); + break; + case kWSCID: + neighborhood = new WSC(g_AIArea, this); + break; + case kNoradAlphaID: + neighborhood = new NoradAlpha(g_AIArea, this); + break; + case kNoradDeltaID: + createInterface(); + neighborhood = new NoradDelta(g_AIArea, this); + break; + default: + error("Unknown neighborhood %d", neighborhoodID); + } +} + +bool PegasusEngine::wantsCursor() { + return _gameMenu == 0; +} + +void PegasusEngine::updateCursor(const Common::Point, const Hotspot *cursorSpot) { + if (_itemDragger.isTracking()) { + _cursor->setCurrentFrameIndex(5); + } else { + if (!cursorSpot) { + _cursor->setCurrentFrameIndex(0); + } else { + uint32 id = cursorSpot->getObjectID(); + + switch (id) { + case kCurrentItemSpotID: + if (countInventoryItems() != 0) + _cursor->setCurrentFrameIndex(4); + else + _cursor->setCurrentFrameIndex(0); + break; + default: + HotSpotFlags flags = cursorSpot->getHotspotFlags(); + + if (flags & kZoomInSpotFlag) + _cursor->setCurrentFrameIndex(1); + else if (flags & kZoomOutSpotFlag) + _cursor->setCurrentFrameIndex(2); + else if (flags & (kPickUpItemSpotFlag | kPickUpBiochipSpotFlag)) + _cursor->setCurrentFrameIndex(4); + else if (flags & kJMPClickingSpotFlags) + _cursor->setCurrentFrameIndex(3); + else + _cursor->setCurrentFrameIndex(0); + } + } + } +} + +void PegasusEngine::toggleInventoryDisplay() { + if (_gameMode == kModeInventoryPick) + setGameMode(kModeNavigation); + else + setGameMode(kModeInventoryPick); +} + +void PegasusEngine::toggleBiochipDisplay() { + if (_gameMode == kModeBiochipPick) + setGameMode(kModeNavigation); + else + setGameMode(kModeBiochipPick); +} + +void PegasusEngine::showInfoScreen() { + if (g_neighborhood) { + // Break the input handler chain... + _savedHandler = InputHandler::getCurrentHandler(); + InputHandler::setInputHandler(this); + + Picture *pushPicture = ((Neighborhood *)g_neighborhood)->getTurnPushPicture(); + + _bigInfoMovie.shareSurface(pushPicture); + _smallInfoMovie.shareSurface(pushPicture); + + g_neighborhood->hideNav(); + + _smallInfoMovie.initFromMovieFile("Images/Items/Info Right Movie"); + _smallInfoMovie.setDisplayOrder(kInfoSpinOrder); + _smallInfoMovie.moveElementTo(kNavAreaLeft + 304, kNavAreaTop + 8); + _smallInfoMovie.moveMovieBoxTo(304, 8); + _smallInfoMovie.startDisplaying(); + _smallInfoMovie.show(); + + TimeValue startTime, stopTime; + g_AIArea->getSmallInfoSegment(startTime, stopTime); + _smallInfoMovie.setSegment(startTime, stopTime); + _smallInfoMovie.setTime(startTime); + _smallInfoMovie.setFlags(kLoopTimeBase); + + _bigInfoMovie.initFromMovieFile("Images/Items/Info Left Movie"); + _bigInfoMovie.setDisplayOrder(kInfoBackgroundOrder); + _bigInfoMovie.moveElementTo(kNavAreaLeft, kNavAreaTop); + _bigInfoMovie.startDisplaying(); + _bigInfoMovie.show(); + _bigInfoMovie.setTime(g_AIArea->getBigInfoTime()); + + _bigInfoMovie.redrawMovieWorld(); + _smallInfoMovie.redrawMovieWorld(); + _smallInfoMovie.start(); + } +} + +void PegasusEngine::hideInfoScreen() { + if (g_neighborhood) { + InputHandler::setInputHandler(_savedHandler); + + _bigInfoMovie.hide(); + _bigInfoMovie.stopDisplaying(); + _bigInfoMovie.releaseMovie(); + + _smallInfoMovie.hide(); + _smallInfoMovie.stopDisplaying(); + _smallInfoMovie.stop(); + _smallInfoMovie.releaseMovie(); + + g_neighborhood->showNav(); + } +} + +void PegasusEngine::raiseInventoryDrawer() { + if (g_interface) + g_interface->raiseInventoryDrawer(); +} + +void PegasusEngine::raiseBiochipDrawer() { + if (g_interface) + g_interface->raiseBiochipDrawer(); +} + +void PegasusEngine::lowerInventoryDrawer() { + if (g_interface) + g_interface->lowerInventoryDrawer(); +} + +void PegasusEngine::lowerBiochipDrawer() { + if (g_interface) + g_interface->lowerBiochipDrawer(); +} + +void PegasusEngine::raiseInventoryDrawerSync() { + if (g_interface) + g_interface->raiseInventoryDrawerSync(); +} + +void PegasusEngine::raiseBiochipDrawerSync() { + if (g_interface) + g_interface->raiseBiochipDrawerSync(); +} + +void PegasusEngine::lowerInventoryDrawerSync() { + if (g_interface) + g_interface->lowerInventoryDrawerSync(); +} + +void PegasusEngine::lowerBiochipDrawerSync() { + if (g_interface) + g_interface->lowerBiochipDrawerSync(); +} + +void PegasusEngine::toggleInfo() { + if (_gameMode == kModeInfoScreen) + setGameMode(kModeNavigation); + else if (_gameMode == kModeNavigation) + setGameMode(kModeInfoScreen); +} + +void PegasusEngine::dragTerminated(const Input &) { + Hotspot *finalSpot = _itemDragger.getLastHotspot(); + InventoryResult result; + + if (_dragType == kDragInventoryPickup) { + if (finalSpot && finalSpot->getObjectID() == kInventoryDropSpotID) + result = addItemToInventory((InventoryItem *)_draggingItem); + else + result = kTooMuchWeight; + + if (result != kInventoryOK) + autoDragItemIntoRoom(_draggingItem, _draggingSprite); + else + delete _draggingSprite; + } else if (_dragType == kDragBiochipPickup) { + if (finalSpot && finalSpot->getObjectID() == kBiochipDropSpotID) + result = addItemToBiochips((BiochipItem *)_draggingItem); + else + result = kTooMuchWeight; + + if (result != kInventoryOK) + autoDragItemIntoRoom(_draggingItem, _draggingSprite); + else + delete _draggingSprite; + } else if (_dragType == kDragInventoryUse) { + if (finalSpot && (finalSpot->getHotspotFlags() & kDropItemSpotFlag) != 0) { + // *** Need to decide on a case by case basis what to do here. + // the crowbar should break the cover off the Mars reactor if its frozen, the + // global transport card should slide through the slot, the oxygen mask should + // attach to the filling station, and so on... + _neighborhood->dropItemIntoRoom(_draggingItem, finalSpot); + delete _draggingSprite; + } else { + autoDragItemIntoInventory(_draggingItem, _draggingSprite); + } + } + + _dragType = kDragNoDrag; + + if (g_AIArea) + g_AIArea->unlockAI(); +} + + +void PegasusEngine::dragItem(const Input &input, Item *item, DragType type) { + _draggingItem = item; + _dragType = type; + + // Create the sprite. + _draggingSprite = _draggingItem->getDragSprite(kDraggingSpriteID); + Common::Point where; + input.getInputLocation(where); + Common::Rect r1; + _draggingSprite->getBounds(r1); + r1 = Common::Rect::center(where.x, where.y, r1.width(), r1.height()); + _draggingSprite->setBounds(r1); + + // Set up drag constraints. + DisplayElement *navMovie = _gfx->findDisplayElement(kNavMovieID); + Common::Rect r2; + navMovie->getBounds(r2); + r2.left -= r1.width() / 3; + r2.right += r1.width() / 3; + r2.top -= r1.height() / 3; + r2.bottom += r2.height() / 3; + + r1 = Common::Rect(-30000, -30000, 30000, 30000); + _itemDragger.setDragConstraints(r2, r1); + + // Start dragging. + _draggingSprite->setDisplayOrder(kDragSpriteOrder); + _draggingSprite->startDisplaying(); + _draggingSprite->show(); + _itemDragger.setDragSprite(_draggingSprite); + _itemDragger.setNextHandler(_neighborhood); + _itemDragger.startTracking(input); + + if (g_AIArea) + g_AIArea->lockAIOut(); +} + +void PegasusEngine::shellGameInput(const Input &input, const Hotspot *cursorSpot) { + if (_gameMode == kModeInfoScreen) { + if (JMPPPInput::isToggleAIMiddleInput(input)) { + LowerClientSignature middleOwner = g_AIArea->getMiddleAreaOwner(); + g_AIArea->toggleMiddleAreaOwner(); + + if (middleOwner != g_AIArea->getMiddleAreaOwner()) { + _bigInfoMovie.setTime(g_AIArea->getBigInfoTime()); + _smallInfoMovie.stop(); + _smallInfoMovie.setFlags(0); + + TimeValue startTime, stopTime; + g_AIArea->getSmallInfoSegment(startTime, stopTime); + _smallInfoMovie.setSegment(startTime, stopTime); + _smallInfoMovie.setTime(startTime); + _smallInfoMovie.setFlags(kLoopTimeBase); + + _bigInfoMovie.redrawMovieWorld(); + _smallInfoMovie.redrawMovieWorld(); + _smallInfoMovie.start(); + } + } + } else { + if (JMPPPInput::isRaiseInventoryInput(input)) + toggleInventoryDisplay(); + + if (JMPPPInput::isRaiseBiochipsInput(input)) + toggleBiochipDisplay(); + + if (JMPPPInput::isTogglePauseInput(input) && _neighborhood) + pauseMenu(!isPaused()); + } + + if (JMPPPInput::isToggleInfoInput(input)) + toggleInfo(); +} + +void PegasusEngine::activateHotspots() { + if (_gameMode == kModeInfoScreen) { + _allHotspots.activateOneHotspot(kInfoReturnSpotID); + } else { + // Set up hot spots. + if (_dragType == kDragInventoryPickup) + _allHotspots.activateOneHotspot(kInventoryDropSpotID); + else if (_dragType == kDragBiochipPickup) + _allHotspots.activateOneHotspot(kBiochipDropSpotID); + else if (_dragType == kDragNoDrag) + _allHotspots.activateMaskedHotspots(kShellSpotFlag); + } +} + +bool PegasusEngine::isClickInput(const Input &input, const Hotspot *cursorSpot) { + if (_cursor->isVisible() && cursorSpot) + return JMPPPInput::isClickInput(input); + else + return false; +} + +InputBits PegasusEngine::getClickFilter() { + return JMPPPInput::getClickInputFilter(); +} + +void PegasusEngine::clickInHotspot(const Input &input, const Hotspot *clickedSpot) { + if (clickedSpot->getObjectID() == kCurrentItemSpotID) { + InventoryItem *currentItem = getCurrentInventoryItem(); + if (currentItem) { + removeItemFromInventory(currentItem); + dragItem(input, currentItem, kDragInventoryUse); + } + } else if (clickedSpot->getObjectID() == kInfoReturnSpotID) { + toggleInfo(); + } +} + +InventoryResult PegasusEngine::removeItemFromInventory(InventoryItem *item) { + InventoryResult result; + + if (g_interface) + result = g_interface->removeInventoryItem(item); + else + result = _items.removeItem(item); + + // This should never happen + assert(result == kInventoryOK); + + return result; +} + +void PegasusEngine::removeAllItemsFromInventory() { + if (g_interface) + g_interface->removeAllItemsFromInventory(); + else + _items.removeAllItems(); +} + +///////////////////////////////////////////// +// +// Biochip handling. + +// Adding biochips to the biochip drawer is a little funny, because of two things: +// -- We get the map biochip and pegasus biochip at the same time by dragging +// one sprite in TSA +// -- We can drag in more than one copy of the various biochips. +// Because of this we need to make sure that no more than one copy of each biochip +// is ever added. + +InventoryResult PegasusEngine::addItemToBiochips(BiochipItem *biochip) { + InventoryResult result; + + if (g_interface) + result = g_interface->addBiochip(biochip); + else + result = _biochips.addItem(biochip); + + // This can never happen + assert(result == kInventoryOK); + + GameState.setTakenItem(biochip, true); + + if (g_neighborhood) + g_neighborhood->pickedUpItem(biochip); + + g_AIArea->checkMiddleArea(); + + return result; +} + +void PegasusEngine::removeAllItemsFromBiochips() { + if (g_interface) + g_interface->removeAllItemsFromBiochips(); + else + _biochips.removeAllItems(); +} + +void PegasusEngine::setSoundFXLevel(uint16 fxLevel) { + _FXLevel = fxLevel; + if (_neighborhood) + _neighborhood->setSoundFXLevel(fxLevel); + if (g_AIArea) + g_AIArea->setAIVolume(fxLevel); +} + +void PegasusEngine::setAmbienceLevel(uint16 ambientLevel) { + _ambientLevel = ambientLevel; + if (_neighborhood) + _neighborhood->setAmbienceLevel(ambientLevel); +} + +void PegasusEngine::pauseMenu(bool menuUp) { + if (menuUp) { + pauseEngine(true); + _screenDimmer.startDisplaying(); + _screenDimmer.show(); + _gfx->updateDisplay(); + useMenu(new PauseMenu()); + } else { + pauseEngine(false); + _screenDimmer.hide(); + _screenDimmer.stopDisplaying(); + useMenu(0); + g_AIArea->checkMiddleArea(); + } +} + +void PegasusEngine::autoDragItemIntoRoom(Item *item, Sprite *draggingSprite) { + if (g_AIArea) + g_AIArea->lockAIOut(); + + Common::Point start, stop; + draggingSprite->getLocation(start.x, start.y); + + Hotspot *dropSpot = _neighborhood->getItemScreenSpot(item, draggingSprite); + + if (dropSpot) { + dropSpot->getCenter(stop.x, stop.y); + } else { + stop.x = kNavAreaLeft + 256; + stop.y = kNavAreaTop + 128; + } + + Common::Rect bounds; + draggingSprite->getBounds(bounds); + stop.x -= bounds.width() >> 1; + stop.y -= bounds.height() >> 1; + + int dx = ABS(stop.x - start.x); + int dy = ABS(stop.y - start.y); + TimeValue time = MAX(dx, dy); + + allowInput(false); + _autoDragger.autoDrag(draggingSprite, start, stop, time, kDefaultTimeScale); + + while (_autoDragger.isDragging()) { + checkCallBacks(); + refreshDisplay(); + _system->delayMillis(10); + } + + _neighborhood->dropItemIntoRoom(_draggingItem, dropSpot); + allowInput(true); + delete _draggingSprite; + + if (g_AIArea) + g_AIArea->unlockAI(); +} + +void PegasusEngine::autoDragItemIntoInventory(Item *, Sprite *draggingSprite) { + if (g_AIArea) + g_AIArea->lockAIOut(); + + Common::Point start; + draggingSprite->getLocation(start.x, start.y); + + Common::Rect r; + draggingSprite->getBounds(r); + + Common::Point stop((76 + 172 - r.width()) / 2, 334 - (2 * r.height() / 3)); + + int dx = ABS(stop.x - start.x); + int dy = ABS(stop.y - start.y); + TimeValue time = MAX(dx, dy); + + allowInput(false); + _autoDragger.autoDrag(draggingSprite, start, stop, time, kDefaultTimeScale); + + while (_autoDragger.isDragging()) { + checkCallBacks(); + refreshDisplay(); + _system->delayMillis(10); + } + + addItemToInventory((InventoryItem *)_draggingItem); + allowInput(true); + delete _draggingSprite; + + if (g_AIArea) + g_AIArea->unlockAI(); +} + +NeighborhoodID PegasusEngine::getCurrentNeighborhoodID() const { + if (_neighborhood) + return _neighborhood->getObjectID(); + + return kNoNeighborhoodID; +} + +void PegasusEngine::pauseEngineIntern(bool pause) { + Engine::pauseEngineIntern(pause); + + if (pause) { + for (Common::List<TimeBase *>::iterator it = _timeBases.begin(); it != _timeBases.end(); it++) + (*it)->pause(); + } else { + for (Common::List<TimeBase *>::iterator it = _timeBases.begin(); it != _timeBases.end(); it++) + (*it)->resume(); + } +} + +uint PegasusEngine::getRandomBit() { + return _rnd->getRandomBit(); +} + +uint PegasusEngine::getRandomNumber(uint max) { + return _rnd->getRandomNumber(max); +} + +void PegasusEngine::shuffleArray(int32 *arr, int32 count) { + if (count > 1) { + for (int32 i = 1; i < count; ++i) { + int32 j = _rnd->getRandomNumber(i); + if (j != i) + SWAP(arr[i], arr[j]); + } + } +} + +void PegasusEngine::playEndMessage() { + if (g_interface) { + allowInput(false); + g_interface->playEndMessage(); + allowInput(true); + } + + die(kPlayerWonGame); +} + +void PegasusEngine::doSubChase() { + Video::VideoDecoder *video = new Video::QuickTimeDecoder(); + if (!video->loadFile("Images/Norad Alpha/Sub Chase Movie")) + error("Failed to load sub chase"); + + video->setEndTime(Audio::Timestamp(0, 133200, 600)); + video->start(); + + while (!shouldQuit() && !video->endOfVideo()) { + if (video->needsUpdate()) { + const Graphics::Surface *frame = video->decodeNextFrame(); + + if (frame) + drawScaledFrame(frame, 0, 0); + } + + Common::Event event; + while (_eventMan->pollEvent(event)) + ; + + _system->delayMillis(10); + } + + delete video; +} + +template<typename PixelInt> +static void scaleFrame(const PixelInt *src, PixelInt *dst, int w, int h, int srcPitch) { + assert((srcPitch % sizeof(PixelInt)) == 0); // sanity check; allows some simpler code + + PixelInt *dst1 = dst; + PixelInt *dst2 = dst + w * 2; + + int srcInc = (srcPitch / sizeof(PixelInt)) - w; + int dstInc = w * 2; + + while (h--) { + for (int x = 0; x < w; x++) { + PixelInt pixel = *src++; + *dst1++ = pixel; + *dst1++ = pixel; + *dst2++ = pixel; + *dst2++ = pixel; + } + + src += srcInc; + dst1 += dstInc; + dst2 += dstInc; + } +} + +void PegasusEngine::drawScaledFrame(const Graphics::Surface *frame, uint16 x, uint16 y) { + // Scale up the frame doing some simple scaling + Graphics::Surface scaledFrame; + scaledFrame.create(frame->w * 2, frame->h * 2, frame->format); + + if (frame->format.bytesPerPixel == 2) + scaleFrame<uint16>((uint16 *)frame->pixels, (uint16 *)scaledFrame.pixels, frame->w, frame->h, frame->pitch); + else + scaleFrame<uint32>((uint32 *)frame->pixels, (uint32 *)scaledFrame.pixels, frame->w, frame->h, frame->pitch); + + _system->copyRectToScreen((byte *)scaledFrame.pixels, scaledFrame.pitch, x, y, scaledFrame.w, scaledFrame.h); + _system->updateScreen(); + scaledFrame.free(); +} + +void PegasusEngine::destroyInventoryItem(const ItemID itemID) { + InventoryItem *item = (InventoryItem *)_allItems.findItemByID(itemID); + + ItemExtraEntry entry; + + switch (itemID) { + case kAirMask: + item->findItemExtra(kRemoveAirMask, entry); + item->setItemRoom(kMarsID, kMars49, kSouth); + break; + case kArgonCanister: + item->findItemExtra(kRemoveArgon, entry); + item->setItemRoom(kWSCID, kWSC02Morph, kSouth); + break; + case kCrowbar: + item->findItemExtra(kRemoveCrowbar, entry); + item->setItemRoom(kMarsID, kMars34, kSouth); + break; + case kJourneymanKey: + item->findItemExtra(kRemoveJourneymanKey, entry); + item->setItemRoom(kFullTSAID, kTSA22Red, kEast); + break; + case kMarsCard: + item->findItemExtra(kRemoveMarsCard, entry); + item->setItemRoom(kMarsID, kMars31South, kSouth); + break; + case kNitrogenCanister: + item->findItemExtra(kRemoveNitrogen, entry); + item->setItemRoom(kWSCID, kWSC02Messages, kSouth); + break; + case kOrangeJuiceGlassEmpty: + item->findItemExtra(kRemoveGlass, entry); + item->setItemRoom(kCaldoriaID, kCaldoriaReplicator, kNorth); + break; + case kPoisonDart: + item->findItemExtra(kRemoveDart, entry); + item->setItemRoom(kWSCID, kWSC01, kWest); + break; + case kSinclairKey: + item->findItemExtra(kRemoveSinclairKey, entry); + item->setItemRoom(kWSCID, kWSC02Morph, kSouth); + break; + default: + return; + } + + g_interface->setCurrentInventoryItemID(itemID); + g_AIArea->playAIAreaSequence(kInventorySignature, kMiddleAreaSignature, entry.extraStart, entry.extraStop); + removeItemFromInventory(item); +} + +ItemID PegasusEngine::pickItemToDestroy() { +/* + Must pick an item to destroy + + Part I: Polite -- try to find an item that's been used + Part II: Desperate -- return the first available item. +*/ + + // Polite: + if (playerHasItemID(kOrangeJuiceGlassEmpty)) + return kOrangeJuiceGlassEmpty; + if (playerHasItemID(kPoisonDart)) { + if (GameState.getCurrentNeighborhood() != kWSCID || + GameState.getWSCAnalyzedDart()) + return kPoisonDart; + } + if (playerHasItemID(kJourneymanKey)) { + if (GameState.getTSAState() >= kTSAPlayerGotHistoricalLog && + GameState.getTSAState() != kPlayerOnWayToPrehistoric && + GameState.getTSAState() != kPlayerWentToPrehistoric) + return kJourneymanKey; + } + if (playerHasItemID(kMarsCard)) { + if (GameState.getCurrentNeighborhood() != kMarsID || GameState.getMarsArrivedBelow()) + return kMarsCard; + } + + // Don't want to deal with deleting the sinclair key and argon canister, since it's + // impossible to pick them up one at a time. + + if (playerHasItemID(kNitrogenCanister)) { + if (GameState.getScoringGotCardBomb() && GameState.getCurrentNeighborhood() != kMarsID) + return kNitrogenCanister; + } + if (playerHasItemID(kCrowbar)) { + if (GameState.getCurrentNeighborhood() == kWSCID) { + if (GameState.getCurrentRoom() >= kWSC62) + return kCrowbar; + } else if (GameState.getCurrentNeighborhood() == kMarsID) { + if (GameState.getScoringGotCardBomb()) + return kCrowbar; + } else + return kCrowbar; + } + if (playerHasItemID(kAirMask)) { + if (GameState.getCurrentNeighborhood() == kMarsID) { + if (g_neighborhood->getAirQuality(GameState.getCurrentRoom()) == kAirQualityGood) + return kAirMask; + } else if (GameState.getCurrentNeighborhood() != kNoradAlphaID && + GameState.getCurrentNeighborhood() != kNoradDeltaID) { + return kAirMask; + } + } + + // Desperate: + if (playerHasItemID(kPoisonDart)) + return kPoisonDart; + if (playerHasItemID(kJourneymanKey)) + return kJourneymanKey; + if (playerHasItemID(kMarsCard)) + return kMarsCard; + if (playerHasItemID(kNitrogenCanister)) + return kNitrogenCanister; + if (playerHasItemID(kCrowbar)) + return kCrowbar; + if (playerHasItemID(kAirMask)) + return kAirMask; + + // Should never get this far... + error("Could not find item to delete"); + + return kNoItemID; +} + +uint PegasusEngine::getNeighborhoodCD(const NeighborhoodID neighborhood) const { + switch (neighborhood) { + case kCaldoriaID: + case kNoradAlphaID: + case kNoradSubChaseID: + return 1; + case kFullTSAID: + case kPrehistoricID: + return 2; + case kMarsID: + return 3; + case kWSCID: + case kNoradDeltaID: + return 4; + case kTinyTSAID: + // Tiny TSA exists on three of the CD's, so just continue + // with the CD we're on + return _currentCD; + } + + // Can't really happen, but it's a good fallback anyway :P + return 1; +} + +void PegasusEngine::initKeymap() { +#ifdef ENABLE_KEYMAPPER + static const char *const kKeymapName = "pegasus"; + Common::Keymapper *const mapper = _eventMan->getKeymapper(); + + // Do not try to recreate same keymap over again + if (mapper->getKeymap(kKeymapName) != 0) + return; + + Common::Keymap *const engineKeyMap = new Common::Keymap(kKeymapName); + + // Since the game has multiple built-in keys for each of these anyway, + // this just attempts to remap one of them. + const Common::KeyActionEntry keyActionEntries[] = { + { Common::KEYCODE_UP, "UP", _("Up/Zoom In/Move Forward/Open Doors") }, + { Common::KEYCODE_DOWN, "DWN", _("Down/Zoom Out") }, + { Common::KEYCODE_LEFT, "TL", _("Turn Left") }, + { Common::KEYCODE_RIGHT, "TR", _("Turn Right") }, + { Common::KEYCODE_BACKQUOTE, "TIV", _("Display/Hide Inventory Tray") }, + { Common::KEYCODE_BACKSPACE, "TBI", _("Display/Hide Biochip Tray") }, + { Common::KEYCODE_RETURN, "ENT", _("Action/Select") }, + { Common::KEYCODE_t, "TMA", _("Toggle Center Data Display") }, + { Common::KEYCODE_i, "TIN", _("Display/Hide Info Screen") }, + { Common::KEYCODE_ESCAPE, "PM", _("Display/Hide Pause Menu") }, + { Common::KEYCODE_e, "WTF", _("???") } // easter egg key (without being completely upfront about it) + }; + + for (uint i = 0; i < ARRAYSIZE(keyActionEntries); i++) { + Common::Action *const act = new Common::Action(engineKeyMap, keyActionEntries[i].id, keyActionEntries[i].description); + act->addKeyEvent(keyActionEntries[i].ks); + } + + mapper->addGameKeymap(engineKeyMap); + mapper->pushKeymap(kKeymapName, true); +#endif +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/pegasus.h b/engines/pegasus/pegasus.h new file mode 100644 index 0000000000..a8f2e1ffca --- /dev/null +++ b/engines/pegasus/pegasus.h @@ -0,0 +1,328 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_H +#define PEGASUS_H + +#include "common/list.h" +#include "common/macresman.h" +#include "common/rect.h" +#include "common/scummsys.h" +#include "common/system.h" +#include "common/util.h" + +#include "engines/engine.h" + +#include "pegasus/graphics.h" +#include "pegasus/hotspot.h" +#include "pegasus/input.h" +#include "pegasus/notification.h" +#include "pegasus/timers.h" +#include "pegasus/items/autodragger.h" +#include "pegasus/items/inventory.h" +#include "pegasus/items/itemdragger.h" +#include "pegasus/neighborhood/neighborhood.h" + +namespace Common { + class RandomSource; +} + +namespace Video { + class VideoDecoder; +} + +namespace Pegasus { + +class PegasusConsole; +struct PegasusGameDescription; +class SoundManager; +class GraphicsManager; +class Idler; +class Cursor; +class TimeBase; +class GameMenu; +class InventoryItem; +class BiochipItem; +class Neighborhood; + +class PegasusEngine : public ::Engine, public InputHandler, public NotificationManager { +friend class InputHandler; + +public: + PegasusEngine(OSystem *syst, const PegasusGameDescription *gamedesc); + virtual ~PegasusEngine(); + + // Engine stuff + const PegasusGameDescription *_gameDescription; + bool hasFeature(EngineFeature f) const; + GUI::Debugger *getDebugger(); + bool canLoadGameStateCurrently(); + bool canSaveGameStateCurrently(); + Common::Error loadGameState(int slot); + Common::Error saveGameState(int slot, const Common::String &desc); + + // Base classes + GraphicsManager *_gfx; + Common::MacResManager *_resFork; + Cursor *_cursor; + + // Menu + void useMenu(GameMenu *menu); + bool checkGameMenu(); + + // Misc. + bool isDemo() const; + void addIdler(Idler *idler); + void removeIdler(Idler *idler); + void addTimeBase(TimeBase *timeBase); + void removeTimeBase(TimeBase *timeBase); + void delayShell(TimeValue time, TimeScale scale); + void resetIntroTimer(); + void introTimerExpired(); + void refreshDisplay(); + bool playerAlive(); + void processShell(); + void checkCallBacks(); + void createInterface(); + void setGameMode(const GameMode); + GameMode getGameMode() const { return _gameMode; } + uint getRandomBit(); + uint getRandomNumber(uint max); + void shuffleArray(int32 *arr, int32 count); + void drawScaledFrame(const Graphics::Surface *frame, uint16 x, uint16 y); + HotspotList &getAllHotspots() { return _allHotspots; } + + // Energy + void setLastEnergyValue(const int32 value) { _savedEnergyValue = value; } + int32 getSavedEnergyValue() { return _savedEnergyValue; } + + // Death + void setEnergyDeathReason(const DeathReason reason) { _deathReason = reason; } + DeathReason getEnergyDeathReason() { return _deathReason; } + void resetEnergyDeathReason(); + void die(const DeathReason); + void playEndMessage(); + + // Volume + uint16 getSoundFXLevel() { return _FXLevel; } + void setSoundFXLevel(uint16); + uint16 getAmbienceLevel() { return _ambientLevel; } + void setAmbienceLevel(uint16); + + // Items + ItemList &getAllItems() { return _allItems; } + bool playerHasItem(const Item *); + bool playerHasItemID(const ItemID); + void checkFlashlight(); + bool itemInLocation(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant); + + // Inventory Items + InventoryItem *getCurrentInventoryItem(); + bool itemInInventory(InventoryItem *); + bool itemInInventory(ItemID); + Inventory *getItemsInventory() { return &_items; } + InventoryResult addItemToInventory(InventoryItem *); + void removeAllItemsFromInventory(); + InventoryResult removeItemFromInventory(InventoryItem *); + uint32 countInventoryItems() { return _items.getNumItems(); } + + // Biochips + BiochipItem *getCurrentBiochip(); + bool itemInBiochips(BiochipItem *); + bool itemInBiochips(ItemID); + Inventory *getBiochipsInventory() { return &_biochips; } + void removeAllItemsFromBiochips(); + InventoryResult addItemToBiochips(BiochipItem *); + + // AI + Common::String getBriefingMovie(); + Common::String getEnvScanMovie(); + uint getNumHints(); + Common::String getHintMovie(uint); + bool canSolve(); + void prepareForAIHint(const Common::String &); + void cleanUpAfterAIHint(const Common::String &); + Common::SeekableReadStream *_aiSaveStream; + + // Neighborhood + void jumpToNewEnvironment(const NeighborhoodID, const RoomID, const DirectionConstant); + NeighborhoodID getCurrentNeighborhoodID() const; + + // Dragging + void dragItem(const Input &, Item *, DragType); + bool isDragging() const { return _dragType != kDragNoDrag; } + DragType getDragType() const { return _dragType; } + Item *getDraggingItem() const { return _draggingItem; } + void dragTerminated(const Input &); + void autoDragItemIntoRoom(Item *, Sprite *); + void autoDragItemIntoInventory(Item *, Sprite*); + + // Save/Load + void makeContinuePoint(); + bool swapSaveAllowed(bool allow) { + bool old = _saveAllowed; + _saveAllowed = allow; + return old; + } + bool swapLoadAllowed(bool allow) { + bool old = _loadAllowed; + _loadAllowed = allow; + return old; + } + void requestSave() { _saveRequested = true; } + bool saveRequested() const { return _saveRequested; } + void requestLoad() { _loadRequested = true; } + bool loadRequested() const { return _loadRequested; } + +protected: + Common::Error run(); + void pauseEngineIntern(bool pause); + + Notification _shellNotification; + virtual void receiveNotification(Notification *notification, const NotificationFlags flags); + + void handleInput(const Input &input, const Hotspot *cursorSpot); + virtual bool isClickInput(const Input &, const Hotspot *); + virtual InputBits getClickFilter(); + + void clickInHotspot(const Input &, const Hotspot *); + void activateHotspots(void); + + void updateCursor(const Common::Point, const Hotspot *); + bool wantsCursor(); + +private: + // Console + PegasusConsole *_console; + + // Intro + void runIntro(); + void stopIntroTimer(); + bool detectOpeningClosingDirectory(); + Common::String _introDirectory; + FuseFunction *_introTimer; + + // Idlers + Idler *_idlerHead; + void giveIdleTime(); + + // Items + ItemList _allItems; + void createItems(); + void createItem(ItemID itemID, NeighborhoodID neighborhoodID, RoomID roomID, DirectionConstant direction); + Inventory _items; + Inventory _biochips; + ItemID _currentItemID; + ItemID _currentBiochipID; + void destroyInventoryItem(const ItemID itemID); + ItemID pickItemToDestroy(); + + // TimeBases + Common::List<TimeBase *> _timeBases; + + // Save/Load + bool loadFromStream(Common::ReadStream *stream); + bool writeToStream(Common::WriteStream *stream, int saveType); + void loadFromContinuePoint(); + void writeContinueStream(Common::WriteStream *stream); + Common::SeekableReadStream *_continuePoint; + bool _saveAllowed, _loadAllowed; // It's so nice that this was in the original code already :P + Common::Error showLoadDialog(); + Common::Error showSaveDialog(); + bool _saveRequested, _loadRequested; + + // Misc. + Hotspot _returnHotspot; + HotspotList _allHotspots; + InputHandler *_savedHandler; + void showTempScreen(const Common::String &fileName); + bool playMovieScaled(Video::VideoDecoder *video, uint16 x, uint16 y); + void throwAwayEverything(); + void shellGameInput(const Input &input, const Hotspot *cursorSpot); + Common::RandomSource *_rnd; + void doSubChase(); + uint getNeighborhoodCD(const NeighborhoodID neighborhood) const; + uint _currentCD; + void initKeymap(); + + // Menu + GameMenu *_gameMenu; + void doGameMenuCommand(const GameMenuCommand); + void doInterfaceOverview(); + ScreenDimmer _screenDimmer; + void pauseMenu(bool menuUp); + + // Energy + int32 _savedEnergyValue; + + // Death + DeathReason _deathReason; + void doDeath(); + + // Neighborhood + Neighborhood *_neighborhood; + void useNeighborhood(Neighborhood *neighborhood); + void performJump(NeighborhoodID start); + void startNewGame(); + void startNeighborhood(); + void makeNeighborhood(NeighborhoodID, Neighborhood *&); + + // Sound + uint16 _ambientLevel; + uint16 _FXLevel; + + // Game Mode + GameMode _gameMode; + bool _switchModesSync; + void switchGameMode(const GameMode, const GameMode); + bool canSwitchGameMode(const GameMode, const GameMode); + + // Dragging + ItemDragger _itemDragger; + Item *_draggingItem; + Sprite *_draggingSprite; + DragType _dragType; + AutoDragger _autoDragger; + + // Interface + void toggleInventoryDisplay(); + void toggleBiochipDisplay(); + void raiseInventoryDrawer(); + void raiseBiochipDrawer(); + void lowerInventoryDrawer(); + void lowerBiochipDrawer(); + void raiseInventoryDrawerSync(); + void raiseBiochipDrawerSync(); + void lowerInventoryDrawerSync(); + void lowerBiochipDrawerSync(); + void showInfoScreen(); + void hideInfoScreen(); + void toggleInfo(); + Movie _bigInfoMovie, _smallInfoMovie; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/scoring.h b/engines/pegasus/scoring.h new file mode 100644 index 0000000000..fbf8641ecb --- /dev/null +++ b/engines/pegasus/scoring.h @@ -0,0 +1,281 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_SCORING_H +#define PEGASUS_SCORING_H + +#include "pegasus/types.h" + +namespace Pegasus { + +///////////////////////////////////////////// +// +// Scoring. + +static const CoordType kDeathScreenScoreLeft = 151; +static const CoordType kDeathScreenScoreTop = 212; +static const CoordType kDeathScreenScoreWidth = 124; +static const CoordType kDeathScreenScoreHeight = 12; +static const CoordType kDeathScreenScoreSkipVert = -16; + +// Caldoria & TSA + +static const GameScoreType kSawINNScore = 5; +static const GameScoreType kTookShowerScore = 2; +static const GameScoreType kFixedHairScore = 2; +static const GameScoreType kGotKeyCardScore = 5; +static const GameScoreType kReadPaperScore = 2; +static const GameScoreType kLookThroughTelescopeScore = 2; +static const GameScoreType kSawCaldoriaKioskScore = 2; +static const GameScoreType kGoToTSAScore = 3; + +static const GameScoreType kEnterTSAScore = 2; +static const GameScoreType kSawBust1Score = 2; +static const GameScoreType kSawBust2Score = 2; +static const GameScoreType kSawBust3Score = 2; +static const GameScoreType kSawBust4Score = 2; +static const GameScoreType kSawBust5Score = 2; +static const GameScoreType kSawBust6Score = 2; +static const GameScoreType kSawTheoryScore = 4; +static const GameScoreType kSawBackgroundScore = 4; +static const GameScoreType kSawProcedureScore = 4; +static const GameScoreType kGotJourneymanKeyScore = 5; +static const GameScoreType kGotPegasusBiochipScore = 5; +static const GameScoreType kGotBiosuitScore = 5; +static const GameScoreType kGoToPrehistoricScore = 5; + +static const GameScoreType kPutLogInReaderScore = 5; +static const GameScoreType kSawCaldoriaNormalScore = 2; +static const GameScoreType kSawCaldoriaAlteredScore = 2; +static const GameScoreType kSawNoradNormalScore = 2; +static const GameScoreType kSawNoradAlteredScore = 2; +static const GameScoreType kSawMarsNormalScore = 2; +static const GameScoreType kSawMarsAlteredScore = 2; +static const GameScoreType kSawWSCNormalScore = 2; +static const GameScoreType kSawWSCAlteredScore = 2; +static const GameScoreType kWentToReadyRoom2Score = 5; +static const GameScoreType kWentAfterSinclairScore = 5; +static const GameScoreType kUsedCardBombScore = 10; +static const GameScoreType kShieldedCardBombScore = 5; +static const GameScoreType kStunnedSinclairScore = 10; +static const GameScoreType kDisarmedNukeScore = 10; + +static const GameScoreType kMaxCaldoriaTSAScoreBefore = kSawINNScore + + kTookShowerScore + + kFixedHairScore + + kGotKeyCardScore + + kReadPaperScore + + kLookThroughTelescopeScore + + kSawCaldoriaKioskScore + + kGoToTSAScore + + kEnterTSAScore + + kSawBust1Score + + kSawBust2Score + + kSawBust3Score + + kSawBust4Score + + kSawBust5Score + + kSawBust6Score + + kSawTheoryScore + + kSawBackgroundScore + + kSawProcedureScore + + kGotJourneymanKeyScore + + kGotPegasusBiochipScore + + kGotBiosuitScore + + kGoToPrehistoricScore + + kPutLogInReaderScore + + kSawCaldoriaNormalScore + + kSawCaldoriaAlteredScore + + kSawNoradNormalScore + + kSawNoradAlteredScore + + kSawMarsNormalScore + + kSawMarsAlteredScore + + kSawWSCNormalScore + + kSawWSCAlteredScore + + kWentToReadyRoom2Score; + +static const GameScoreType kMaxCaldoriaTSAScoreAfter = kWentAfterSinclairScore + + kUsedCardBombScore + + kShieldedCardBombScore + + kStunnedSinclairScore + + kDisarmedNukeScore; + +static const GameScoreType kMaxCaldoriaTSAScore = kMaxCaldoriaTSAScoreBefore + + kMaxCaldoriaTSAScoreAfter; + +// Prehistoric + +static const GameScoreType kThrewBreakerScore = 10; +static const GameScoreType kExtendedBridgeScore = 10; +static const GameScoreType kGotHistoricalLogScore = 5; +static const GameScoreType kFinishedPrehistoricScore = 10; + +static const GameScoreType kMaxPrehistoricScore = kThrewBreakerScore + + kExtendedBridgeScore + + kGotHistoricalLogScore + + kFinishedPrehistoricScore; + +// Mars + +static const GameScoreType kThrownByRobotScore = 3; +static const GameScoreType kGotMarsCardScore = 5; +static const GameScoreType kSawMarsKioskScore = 2; +static const GameScoreType kSawTransportMapScore = 2; +static const GameScoreType kGotCrowBarScore = 5; +static const GameScoreType kTurnedOnTransportScore = 5; +static const GameScoreType kGotOxygenMaskScore = 5; +static const GameScoreType kAvoidedRobotScore = 5; +static const GameScoreType kActivatedPlatformScore = 2; +static const GameScoreType kUsedLiquidNitrogenScore = 3; +static const GameScoreType kUsedCrowBarScore = 3; +static const GameScoreType kFoundCardBombScore = 4; +static const GameScoreType kDisarmedCardBombScore = 8; +static const GameScoreType kGotCardBombScore = 5; +static const GameScoreType kThreadedMazeScore = 5; +static const GameScoreType kThreadedGearRoomScore = 2; +static const GameScoreType kEnteredShuttleScore = 2; +static const GameScoreType kEnteredLaunchTubeScore = 4; +static const GameScoreType kStoppedRobotsShuttleScore = 10; +static const GameScoreType kGotMarsOpMemChipScore = 10; +static const GameScoreType kFinishedMarsScore = 10; + +static const GameScoreType kMaxMarsScore = kThrownByRobotScore + + kGotMarsCardScore + + kSawMarsKioskScore + + kSawTransportMapScore + + kGotCrowBarScore + + kTurnedOnTransportScore + + kGotOxygenMaskScore + + kAvoidedRobotScore + + kActivatedPlatformScore + + kUsedLiquidNitrogenScore + + kUsedCrowBarScore + + kFoundCardBombScore + + kDisarmedCardBombScore + + kGotCardBombScore + + kThreadedMazeScore + + kThreadedGearRoomScore + + kEnteredShuttleScore + + kEnteredLaunchTubeScore + + kStoppedRobotsShuttleScore + + kGotMarsOpMemChipScore + + kFinishedMarsScore; + +// Norad + +static const GameScoreType kSawSecurityMonitorScore = 5; +static const GameScoreType kFilledOxygenCanisterScore = 5; +static const GameScoreType kFilledArgonCanisterScore = 5; +static const GameScoreType kSawUnconsciousOperatorScore = 5; +static const GameScoreType kWentThroughPressureDoorScore = 5; +static const GameScoreType kPreppedSubScore = 5; +static const GameScoreType kEnteredSubScore = 5; +static const GameScoreType kExitedSubScore = 10; +static const GameScoreType kSawRobotAt54NorthScore = 5; +static const GameScoreType kPlayedWithClawScore = 5; +static const GameScoreType kUsedRetinalChipScore = 5; +static const GameScoreType kFinishedGlobeGameScore = 10; +static const GameScoreType kStoppedNoradRobotScore = 10; +static const GameScoreType kGotNoradOpMemChipScore = 10; +static const GameScoreType kFinishedNoradScore = 10; + +static const GameScoreType kMaxNoradScore = kSawSecurityMonitorScore + + kFilledOxygenCanisterScore + + kFilledArgonCanisterScore + + kSawUnconsciousOperatorScore + + kWentThroughPressureDoorScore + + kPreppedSubScore + + kEnteredSubScore + + kExitedSubScore + + kSawRobotAt54NorthScore + + kPlayedWithClawScore + + kUsedRetinalChipScore + + kFinishedGlobeGameScore + + kStoppedNoradRobotScore + + kGotNoradOpMemChipScore + + kFinishedNoradScore; + +// WSC + +static const GameScoreType kRemovedDartScore = 5; +static const GameScoreType kAnalyzedDartScore = 5; +static const GameScoreType kBuiltAntidoteScore = 5; +static const GameScoreType kGotSinclairKeyScore = 5; +static const GameScoreType kGotArgonCanisterScore = 5; +static const GameScoreType kGotNitrogenCanisterScore = 5; +static const GameScoreType kPlayedWithMessagesScore = 2; +static const GameScoreType kSawMorphExperimentScore = 3; +static const GameScoreType kEnteredSinclairOfficeScore = 2; +static const GameScoreType kSawBrochureScore = 3; +static const GameScoreType kSawSinclairEntry1Score = 3; +static const GameScoreType kSawSinclairEntry2Score = 3; +static const GameScoreType kSawSinclairEntry3Score = 3; +static const GameScoreType kSawWSCDirectoryScore = 3; +static const GameScoreType kUsedCrowBarInWSCScore = 5; +static const GameScoreType kFinishedPlasmaDodgeScore = 10; +static const GameScoreType kOpenedCatwalkScore = 3; +static const GameScoreType kStoppedWSCRobotScore = 10; +static const GameScoreType kGotWSCOpMemChipScore = 10; +static const GameScoreType kFinishedWSCScore = 10; + +static const GameScoreType kMaxWSCScore = kRemovedDartScore + + kAnalyzedDartScore + + kBuiltAntidoteScore + + kGotSinclairKeyScore + + kGotArgonCanisterScore + + kGotNitrogenCanisterScore + + kPlayedWithMessagesScore + + kSawMorphExperimentScore + + kEnteredSinclairOfficeScore + + kSawBrochureScore + + kSawSinclairEntry1Score + + kSawSinclairEntry2Score + + kSawSinclairEntry3Score + + kSawWSCDirectoryScore + + kUsedCrowBarInWSCScore + + kFinishedPlasmaDodgeScore + + kOpenedCatwalkScore + + kStoppedWSCRobotScore + + kGotWSCOpMemChipScore + + kFinishedWSCScore; + +// Gandhi + +static const GameScoreType kMarsGandhiScore = 10; +static const GameScoreType kNoradGandhiScore = 10; +static const GameScoreType kWSCGandhiScore = 10; + +static const GameScoreType kMaxGandhiScore = kMarsGandhiScore + + kNoradGandhiScore + + kWSCGandhiScore; + +static const GameScoreType kMaxTotalScore = kMaxCaldoriaTSAScore + + kMaxPrehistoricScore + + kMaxMarsScore + + kMaxNoradScore + + kMaxWSCScore + + kMaxGandhiScore; +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/sound.cpp b/engines/pegasus/sound.cpp new file mode 100644 index 0000000000..bf15858e80 --- /dev/null +++ b/engines/pegasus/sound.cpp @@ -0,0 +1,181 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "audio/audiostream.h" +#include "audio/decoders/aiff.h" +#include "audio/decoders/quicktime.h" +#include "common/file.h" +#include "common/system.h" + +#include "pegasus/fader.h" +#include "pegasus/sound.h" + +namespace Pegasus { + +Sound::Sound() { + _stream = 0; + _volume = 0xFF; + _fader = 0; +} + +Sound::~Sound() { + disposeSound(); +} + +void Sound::disposeSound() { + stopSound(); + delete _stream; _stream = 0; +} + +void Sound::initFromAIFFFile(const Common::String &fileName) { + disposeSound(); + + Common::File *file = new Common::File(); + if (!file->open(fileName)) { + warning("Failed to open AIFF file '%s'", fileName.c_str()); + delete file; + return; + } + + _stream = Audio::makeAIFFStream(file, DisposeAfterUse::YES); +} + +void Sound::initFromQuickTime(const Common::String &fileName) { + disposeSound(); + + _stream = Audio::makeQuickTimeStream(fileName); + + if (!_stream) + warning("Failed to open QuickTime file '%s'", fileName.c_str()); +} + +void Sound::attachFader(SoundFader *fader) { + if (_fader) + _fader->attachSound(0); + + _fader = fader; + + if (_fader) + _fader->attachSound(this); +} + +void Sound::playSound() { + if (!isSoundLoaded()) + return; + + stopSound(); + + if (_fader) + setVolume(_fader->getFaderValue()); + + g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &_handle, _stream, -1, _volume, 0, DisposeAfterUse::NO); +} + +void Sound::loopSound() { + if (!isSoundLoaded()) + return; + + stopSound(); + + // Create a looping stream + Audio::AudioStream *loopStream = new Audio::LoopingAudioStream(_stream, 0, DisposeAfterUse::NO); + + // Assume that if there is a fader, we're going to fade the sound in. + if (_fader) + setVolume(0); + + g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &_handle, loopStream, -1, _volume, 0, DisposeAfterUse::YES); +} + +void Sound::playSoundSegment(uint32 start, uint32 end) { + if (!isSoundLoaded()) + return; + + stopSound(); + + Audio::AudioStream *subStream = new Audio::SubSeekableAudioStream(_stream, Audio::Timestamp(0, start, 600), Audio::Timestamp(0, end, 600), DisposeAfterUse::NO); + + g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &_handle, subStream, -1, _volume, 0, DisposeAfterUse::YES); +} + +void Sound::stopSound() { + g_system->getMixer()->stopHandle(_handle); +} + +void Sound::setVolume(const uint16 volume) { + // Clipping the volume to [0x00, 0xFF] instead of Apple's [0, 0x100] + // We store the volume in case SetVolume is called before the sound starts + + _volume = (volume == 0x100) ? 0xFF : volume; + g_system->getMixer()->setChannelVolume(_handle, _volume); +} + +bool Sound::isPlaying() { + return isSoundLoaded() && g_system->getMixer()->isSoundHandleActive(_handle); +} + +bool Sound::isSoundLoaded() const { + return _stream != 0; +} + +SoundTimeBase::SoundTimeBase() { + setScale(600); + _startScale = 600; + _stopScale = 600; + _setToStart = false; +} + +void SoundTimeBase::playSoundSegment(uint32 startTime, uint32 endTime) { + _startTime = startTime; + _stopTime = endTime; + _setToStart = true; + _time = Common::Rational(startTime, getScale()); + setRate(1); + Sound::playSoundSegment(startTime, endTime); +} + +void SoundTimeBase::updateTime() { + if (_setToStart) { + if (isPlaying()) { + // Not at the end, let's get the time + uint numFrames = g_system->getMixer()->getSoundElapsedTime(_handle) * 600 / 1000; + + // WORKAROUND: Our mixer is woefully inaccurate and quite often returns + // times that exceed the actual length of the clip. We'll just fake times + // that are under the final time to ensure any trigger for the end time is + // only sent when the sound has actually stopped. + if (numFrames >= (_stopTime - _startTime)) + numFrames = _stopTime - _startTime - 1; + + _time = Common::Rational(_startTime + numFrames, getScale()); + } else { + // Assume we reached the end + _setToStart = false; + _time = Common::Rational(_stopTime, getScale()); + } + } +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/sound.h b/engines/pegasus/sound.h new file mode 100644 index 0000000000..57cfd52e41 --- /dev/null +++ b/engines/pegasus/sound.h @@ -0,0 +1,108 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_SOUND_H +#define PEGASUS_SOUND_H + +#include "audio/mixer.h" +#include "common/str.h" +#include "pegasus/timers.h" + +namespace Audio { + class SeekableAudioStream; +} + +namespace Pegasus { + +class SoundFader; + +// Things you might want to do with sound: +// - Start it +// - Stop it +// - Loop it +// - Pause it +// - Set the volume +// - Set the pitch (rate) +// - Pan the sound +// - Change these settings dynamically over time + +class Sound { +public: + Sound(); + ~Sound(); + + // We only have one access point here because we should + // only be opening an AIFF file from a file name. We're + // not using the resource fork string resources. + void initFromAIFFFile(const Common::String &fileName); + + // Unlike the original game, we're going to use a regular + // audio stream for sound spots. The original treated them + // as movies. + void initFromQuickTime(const Common::String &fileName); + + void disposeSound(); + bool isSoundLoaded() const; + void playSound(); + void loopSound(); + void playSoundSegment(uint32 start, uint32 end); + void stopSound(); + void setVolume(const uint16 volume); + bool isPlaying(); + + void attachFader(SoundFader *fader); + +protected: + Audio::SeekableAudioStream *_stream; + Audio::SoundHandle _handle; + byte _volume; + + SoundFader *_fader; +}; + +// TODO: Make this class follow TimeBase better +// Right now it's just a loose wrapper to plug callbacks +// into sounds. Since this is only used for spot sounds, +// I'm not too worried about it right now as its usage +// is very limited. +// At the very least, the regular TimeBase functions for +// setting/getting should be neutered. +class SoundTimeBase : public Sound, public TimeBase { +public: + SoundTimeBase(); + ~SoundTimeBase() {} + + void playSoundSegment(uint32 start, uint32 end); + +protected: + void updateTime(); + +private: + bool _setToStart; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/surface.cpp b/engines/pegasus/surface.cpp new file mode 100644 index 0000000000..cdcb3c6e79 --- /dev/null +++ b/engines/pegasus/surface.cpp @@ -0,0 +1,396 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "common/file.h" +#include "common/macresman.h" +#include "common/stream.h" +#include "common/system.h" +#include "graphics/surface.h" +#include "graphics/decoders/pict.h" +#include "video/video_decoder.h" + +#include "pegasus/pegasus.h" +#include "pegasus/surface.h" + +namespace Pegasus { + +Surface::Surface() { + _ownsSurface = false; + _surface = 0; +} + +Surface::~Surface() { + deallocateSurface(); +} + +void Surface::deallocateSurface() { + if (_surface) { + if (_ownsSurface) { + _surface->free(); + delete _surface; + } + + _surface = 0; + _bounds = Common::Rect(); + _ownsSurface = false; + } +} + +void Surface::shareSurface(Surface *surface) { + deallocateSurface(); + + if (surface) { + _ownsSurface = false; + _surface = surface->getSurface(); + surface->getSurfaceBounds(_bounds); + } +} + +void Surface::allocateSurface(const Common::Rect &bounds) { + deallocateSurface(); + + if (bounds.isEmpty()) + return; + + _bounds = bounds; + _surface = new Graphics::Surface(); + _surface->create(bounds.width(), bounds.height(), g_system->getScreenFormat()); + _ownsSurface = true; +} + +void Surface::getImageFromPICTFile(const Common::String &fileName) { + Common::File pict; + if (!pict.open(fileName)) + error("Could not open picture '%s'", fileName.c_str()); + + if (!getImageFromPICTStream(&pict)) + error("Failed to load PICT '%s'", fileName.c_str()); +} + +void Surface::getImageFromPICTResource(Common::MacResManager *resFork, uint16 id) { + Common::SeekableReadStream *res = resFork->getResource(MKTAG('P', 'I', 'C', 'T'), id); + if (!res) + error("Could not open PICT resource %d from '%s'", id, resFork->getBaseFileName().c_str()); + + if (!getImageFromPICTStream(res)) + error("Failed to load PICT resource %d from '%s'", id, resFork->getBaseFileName().c_str()); + + delete res; +} + +bool Surface::getImageFromPICTStream(Common::SeekableReadStream *stream) { + Graphics::PICTDecoder pict; + + if (!pict.loadStream(*stream)) + return false; + + _surface = pict.getSurface()->convertTo(g_system->getScreenFormat(), pict.getPalette()); + _ownsSurface = true; + _bounds = Common::Rect(0, 0, _surface->w, _surface->h); + return true; +} + +void Surface::getImageFromMovieFrame(Video::VideoDecoder *video, TimeValue time) { + video->seek(Audio::Timestamp(0, time, 600)); + const Graphics::Surface *frame = video->decodeNextFrame(); + + if (frame) { + if (!_surface) + _surface = new Graphics::Surface(); + + _surface->copyFrom(*frame); + _ownsSurface = true; + _bounds = Common::Rect(0, 0, _surface->w, _surface->h); + } else { + deallocateSurface(); + } +} + +void Surface::copyToCurrentPort() const { + copyToCurrentPort(_bounds); +} + +void Surface::copyToCurrentPortTransparent() const { + copyToCurrentPortTransparent(_bounds); +} + +void Surface::copyToCurrentPort(const Common::Rect &rect) const { + copyToCurrentPort(rect, rect); +} + +void Surface::copyToCurrentPortTransparent(const Common::Rect &rect) const { + copyToCurrentPortTransparent(rect, rect); +} + +void Surface::copyToCurrentPort(const Common::Rect &srcRect, const Common::Rect &dstRect) const { + Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getCurSurface(); + byte *src = (byte *)_surface->getBasePtr(srcRect.left, srcRect.top); + byte *dst = (byte *)screen->getBasePtr(dstRect.left, dstRect.top); + + int lineSize = srcRect.width() * _surface->format.bytesPerPixel; + + for (int y = 0; y < srcRect.height(); y++) { + memcpy(dst, src, lineSize); + src += _surface->pitch; + dst += screen->pitch; + } +} + +void Surface::copyToCurrentPortTransparent(const Common::Rect &srcRect, const Common::Rect &dstRect) const { + Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getCurSurface(); + byte *src = (byte *)_surface->getBasePtr(srcRect.left, srcRect.top); + byte *dst = (byte *)screen->getBasePtr(dstRect.left, dstRect.top); + + int lineSize = srcRect.width() * _surface->format.bytesPerPixel; + + for (int y = 0; y < srcRect.height(); y++) { + for (int x = 0; x < srcRect.width(); x++) { + if (g_system->getScreenFormat().bytesPerPixel == 2) { + uint16 color = READ_UINT16(src); + if (!isTransparent(color)) + memcpy(dst, src, 2); + } else if (g_system->getScreenFormat().bytesPerPixel == 4) { + uint32 color = READ_UINT32(src); + if (!isTransparent(color)) + memcpy(dst, src, 4); + } + + src += g_system->getScreenFormat().bytesPerPixel; + dst += g_system->getScreenFormat().bytesPerPixel; + } + + src += _surface->pitch - lineSize; + dst += screen->pitch - lineSize; + } +} + +void Surface::copyToCurrentPortMasked(const Common::Rect &srcRect, const Common::Rect &dstRect, const Surface *mask) const { + Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getCurSurface(); + byte *src = (byte *)_surface->getBasePtr(srcRect.left, srcRect.top); + byte *dst = (byte *)screen->getBasePtr(dstRect.left, dstRect.top); + + int lineSize = srcRect.width() * _surface->format.bytesPerPixel; + + for (int y = 0; y < srcRect.height(); y++) { + byte *maskSrc = (byte *)mask->getSurface()->getBasePtr(0, y); + + for (int x = 0; x < srcRect.width(); x++) { + if (g_system->getScreenFormat().bytesPerPixel == 2) { + uint16 color = READ_UINT16(maskSrc); + if (!isTransparent(color)) + memcpy(dst, src, 2); + } else if (g_system->getScreenFormat().bytesPerPixel == 4) { + uint32 color = READ_UINT32(maskSrc); + if (!isTransparent(color)) + memcpy(dst, src, 4); + } + + src += g_system->getScreenFormat().bytesPerPixel; + maskSrc += g_system->getScreenFormat().bytesPerPixel; + dst += g_system->getScreenFormat().bytesPerPixel; + } + + src += _surface->pitch - lineSize; + dst += screen->pitch - lineSize; + } +} + +void Surface::copyToCurrentPortTransparentGlow(const Common::Rect &srcRect, const Common::Rect &dstRect) const { + // This is the same as copyToCurrentPortTransparent(), but turns the red value of each + // pixel all the way up. + + Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getCurSurface(); + byte *src = (byte *)_surface->getBasePtr(srcRect.left, srcRect.top); + byte *dst = (byte *)screen->getBasePtr(dstRect.left, dstRect.top); + + int lineSize = srcRect.width() * _surface->format.bytesPerPixel; + + for (int y = 0; y < srcRect.height(); y++) { + for (int x = 0; x < srcRect.width(); x++) { + if (g_system->getScreenFormat().bytesPerPixel == 2) { + uint16 color = READ_UINT16(src); + if (!isTransparent(color)) + WRITE_UINT16(dst, getGlowColor(color)); + } else if (g_system->getScreenFormat().bytesPerPixel == 4) { + uint32 color = READ_UINT32(src); + if (!isTransparent(color)) + WRITE_UINT32(dst, getGlowColor(color)); + } + + src += g_system->getScreenFormat().bytesPerPixel; + dst += g_system->getScreenFormat().bytesPerPixel; + } + + src += _surface->pitch - lineSize; + dst += screen->pitch - lineSize; + } +} + +void Surface::scaleTransparentCopy(const Common::Rect &srcRect, const Common::Rect &dstRect) const { + // I'm doing simple linear scaling here + // dstRect(x, y) = srcRect(x * srcW / dstW, y * srcH / dstH); + + Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getCurSurface(); + + int srcW = srcRect.width(); + int srcH = srcRect.height(); + int dstW = dstRect.width(); + int dstH = dstRect.height(); + + for (int y = 0; y < dstH; y++) { + for (int x = 0; x < dstW; x++) { + if (g_system->getScreenFormat().bytesPerPixel == 2) { + uint16 color = READ_UINT16((byte *)_surface->getBasePtr( + x * srcW / dstW + srcRect.left, + y * srcH / dstH + srcRect.top)); + if (!isTransparent(color)) + WRITE_UINT16((byte *)screen->getBasePtr(x + dstRect.left, y + dstRect.top), color); + } else if (g_system->getScreenFormat().bytesPerPixel == 4) { + uint32 color = READ_UINT32((byte *)_surface->getBasePtr( + x * srcW / dstW + srcRect.left, + y * srcH / dstH + srcRect.top)); + if (!isTransparent(color)) + WRITE_UINT32((byte *)screen->getBasePtr(x + dstRect.left, y + dstRect.top), color); + } + } + } +} + +void Surface::scaleTransparentCopyGlow(const Common::Rect &srcRect, const Common::Rect &dstRect) const { + // This is the same as scaleTransparentCopy(), but turns the red value of each + // pixel all the way up. + + Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getCurSurface(); + + int srcW = srcRect.width(); + int srcH = srcRect.height(); + int dstW = dstRect.width(); + int dstH = dstRect.height(); + + for (int y = 0; y < dstH; y++) { + for (int x = 0; x < dstW; x++) { + if (g_system->getScreenFormat().bytesPerPixel == 2) { + uint16 color = READ_UINT16((byte *)_surface->getBasePtr( + x * srcW / dstW + srcRect.left, + y * srcH / dstH + srcRect.top)); + if (!isTransparent(color)) + WRITE_UINT16((byte *)screen->getBasePtr(x + dstRect.left, y + dstRect.top), getGlowColor(color)); + } else if (g_system->getScreenFormat().bytesPerPixel == 4) { + uint32 color = READ_UINT32((byte *)_surface->getBasePtr( + x * srcW / dstW + srcRect.left, + y * srcH / dstH + srcRect.top)); + if (!isTransparent(color)) + WRITE_UINT32((byte *)screen->getBasePtr(x + dstRect.left, y + dstRect.top), getGlowColor(color)); + } + } + } +} + +uint32 Surface::getGlowColor(uint32 color) const { + // Can't just 'or' it on like the original did :P + byte r, g, b; + g_system->getScreenFormat().colorToRGB(color, r, g, b); + return g_system->getScreenFormat().RGBToColor(0xff, g, b); +} + +bool Surface::isTransparent(uint32 color) const { + // HACK: Seems we're truncating some color data somewhere... + uint32 transColor1 = g_system->getScreenFormat().RGBToColor(0xff, 0xff, 0xff); + uint32 transColor2 = g_system->getScreenFormat().RGBToColor(0xf8, 0xf8, 0xf8); + + return color == transColor1 || color == transColor2; +} + +PixelImage::PixelImage() { + _transparent = false; +} + +void PixelImage::drawImage(const Common::Rect &sourceBounds, const Common::Rect &destBounds) { + if (!isSurfaceValid()) + return; + + // Draw from sourceBounds to destBounds based on _transparent + if (_transparent) + copyToCurrentPortTransparent(sourceBounds, destBounds); + else + copyToCurrentPort(sourceBounds, destBounds); +} + +void Frame::initFromPICTFile(const Common::String &fileName, bool transparent) { + getImageFromPICTFile(fileName); + _transparent = transparent; +} + +void Frame::initFromPICTResource(Common::MacResManager *resFork, uint16 id, bool transparent) { + getImageFromPICTResource(resFork, id); + _transparent = transparent; +} + +void Frame::initFromMovieFrame(Video::VideoDecoder *video, TimeValue time, bool transparent) { + getImageFromMovieFrame(video, time); + _transparent = transparent; +} + +void Picture::draw(const Common::Rect &r) { + Common::Rect surfaceBounds; + getSurfaceBounds(surfaceBounds); + Common::Rect r1 = r; + + Common::Rect bounds; + getBounds(bounds); + surfaceBounds.moveTo(bounds.left, bounds.top); + r1 = r1.findIntersectingRect(surfaceBounds); + getSurfaceBounds(surfaceBounds); + + Common::Rect r2 = r1; + r2.translate(surfaceBounds.left - bounds.left, surfaceBounds.top - bounds.top); + drawImage(r2, r1); +} + +void Picture::initFromPICTFile(const Common::String &fileName, bool transparent) { + Frame::initFromPICTFile(fileName, transparent); + + Common::Rect surfaceBounds; + getSurfaceBounds(surfaceBounds); + sizeElement(surfaceBounds.width(), surfaceBounds.height()); +} + +void Picture::initFromPICTResource(Common::MacResManager *resFork, uint16 id, bool transparent) { + Frame::initFromPICTResource(resFork, id, transparent); + + Common::Rect surfaceBounds; + getSurfaceBounds(surfaceBounds); + sizeElement(surfaceBounds.width(), surfaceBounds.height()); +} + +void Picture::initFromMovieFrame(Video::VideoDecoder *video, TimeValue time, bool transparent) { + Frame::initFromMovieFrame(video, time, transparent); + + Common::Rect surfaceBounds; + getSurfaceBounds(surfaceBounds); + sizeElement(surfaceBounds.width(), surfaceBounds.height()); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/surface.h b/engines/pegasus/surface.h new file mode 100644 index 0000000000..47e3ef538c --- /dev/null +++ b/engines/pegasus/surface.h @@ -0,0 +1,140 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_SURFACE_H +#define PEGASUS_SURFACE_H + +#include "common/rect.h" +#include "common/str.h" + +#include "pegasus/elements.h" +#include "pegasus/types.h" + +namespace Common { + class MacResManager; +} + +namespace Graphics { + struct Surface; +} + +namespace Video { + class VideoDecoder; +} + +namespace Pegasus { + +// Surface bounds are always normalized. + +class Surface { +public: + Surface(); + virtual ~Surface(); + + virtual void allocateSurface(const Common::Rect &); + virtual void deallocateSurface(); + virtual void shareSurface(Surface *surface); + bool isSurfaceValid() const { return _surface != 0; } + + Graphics::Surface *getSurface() const { return _surface; } + void getSurfaceBounds(Common::Rect &r) { r = _bounds; } + + // None of the copyToCurrentPort* functions do any sanity checks. + // It's up to clients to make sure that the Surface is valid. + void copyToCurrentPort() const; + void copyToCurrentPortTransparent() const; + void copyToCurrentPort(const Common::Rect &) const; + void copyToCurrentPortTransparent(const Common::Rect &) const; + void copyToCurrentPort(const Common::Rect &, const Common::Rect &) const; + void copyToCurrentPortTransparent(const Common::Rect &, const Common::Rect &) const; + void copyToCurrentPortMasked(const Common::Rect &, const Common::Rect &, const Surface *) const; + void copyToCurrentPortTransparentGlow(const Common::Rect &, const Common::Rect &) const; + void scaleTransparentCopy(const Common::Rect &, const Common::Rect &) const; + void scaleTransparentCopyGlow(const Common::Rect &, const Common::Rect &) const; + + virtual void getImageFromPICTFile(const Common::String &fileName); + virtual void getImageFromPICTResource(Common::MacResManager *resFork, uint16 id); + virtual void getImageFromMovieFrame(Video::VideoDecoder *, TimeValue); + +protected: + bool _ownsSurface; + Graphics::Surface *_surface; + Common::Rect _bounds; + +private: + bool getImageFromPICTStream(Common::SeekableReadStream *stream); + + uint32 getGlowColor(uint32 color) const; + bool isTransparent(uint32 color) const; +}; + +class PixelImage : public Surface { +public: + PixelImage(); + virtual ~PixelImage() {} + + void drawImage(const Common::Rect &, const Common::Rect &); + +protected: + virtual void setTransparent(bool transparent) { _transparent = transparent; } + + bool _transparent; +}; + +class Frame : public PixelImage { +public: + Frame() {} + virtual ~Frame() {} + + virtual void initFromPICTFile(const Common::String &fileName, bool transparent = false); + virtual void initFromPICTResource(Common::MacResManager *resFork, uint16 id, bool transparent = false); + virtual void initFromMovieFrame(Video::VideoDecoder *, TimeValue, bool transparent = false); +}; + +class SpriteFrame : public Frame { +friend class Sprite; +public: + SpriteFrame() { _referenceCount = 0; } + virtual ~SpriteFrame() {} + +protected: + uint32 _referenceCount; +}; + +class Picture : public DisplayElement, public Frame { +public: + Picture(const DisplayElementID id) : DisplayElement(id) {} + virtual ~Picture() {} + + virtual void initFromPICTFile(const Common::String &fileName, bool transparent = false); + virtual void initFromPICTResource(Common::MacResManager *resFork, uint16 id, bool transparent = false); + virtual void initFromMovieFrame(Video::VideoDecoder *, TimeValue, bool transparent = false); + + virtual void draw(const Common::Rect &); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/timers.cpp b/engines/pegasus/timers.cpp new file mode 100644 index 0000000000..3b875038cc --- /dev/null +++ b/engines/pegasus/timers.cpp @@ -0,0 +1,429 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "pegasus/pegasus.h" +#include "pegasus/notification.h" +#include "pegasus/timers.h" + +namespace Pegasus { + +Idler::Idler() { + _isIdling = false; + _nextIdler = 0; + _prevIdler = 0; +} + +Idler::~Idler() { + stopIdling(); +} + +void Idler::startIdling() { + if (!isIdling()) { + ((PegasusEngine *)g_engine)->addIdler(this); + _isIdling = true; + } +} + +void Idler::stopIdling() { + if (isIdling()) { + ((PegasusEngine *)g_engine)->removeIdler(this); + _isIdling = false; + } +} + +TimeBase::TimeBase(const TimeScale preferredScale) { + _preferredScale = preferredScale; + _callBackList = 0; + _paused = false; + _flags = 0; + _lastMillis = 0; + _time = 0; + _rate = 0; + _startTime = 0; + _startScale = 1; + _stopTime = 0xffffffff; + _stopScale = 1; + _master = 0; + _pausedRate = 0; + _pauseStart = 0; + + ((PegasusEngine *)g_engine)->addTimeBase(this); +} + +TimeBase::~TimeBase() { + if (_master) + _master->_slaves.remove(this); + + ((PegasusEngine *)g_engine)->removeTimeBase(this); + disposeAllCallBacks(); + + // TODO: Remove slaves? Make them remove themselves? +} + +void TimeBase::setTime(const TimeValue time, const TimeScale scale) { + _time = Common::Rational(time, (scale == 0) ? _preferredScale : scale); + _lastMillis = 0; +} + +TimeValue TimeBase::getTime(const TimeScale scale) { + return _time.getNumerator() * ((scale == 0) ? _preferredScale : scale) / _time.getDenominator(); +} + +void TimeBase::setRate(const Common::Rational rate) { + _rate = rate; + + if (_rate == 0) + _paused = false; +} + +Common::Rational TimeBase::getEffectiveRate() const { + return _rate * ((_master == 0) ? 1 : _master->getEffectiveRate()); +} + +void TimeBase::start() { + if (_paused) + _pausedRate = 1; + else + setRate(1); +} + +void TimeBase::stop() { + setRate(0); + _paused = false; +} + +void TimeBase::pause() { + if (isRunning() && !_paused) { + _pausedRate = getRate(); + stop(); + _paused = true; + _pauseStart = g_system->getMillis(); + } +} + +void TimeBase::resume() { + if (_paused) { + setRate(_pausedRate); + _paused = false; + + if (isRunning()) + _lastMillis += g_system->getMillis() - _pauseStart; + } +} + +bool TimeBase::isRunning() { + if (_paused && _pausedRate != 0) + return true; + + Common::Rational rate = getRate(); + + if (rate == 0) + return false; + + if (getFlags() & kLoopTimeBase) + return true; + + if (rate > 0) + return getTime() != getStop(); + + return getTime() != getStart(); +} + +void TimeBase::setStart(const TimeValue startTime, const TimeScale scale) { + _startTime = startTime; + _startScale = (scale == 0) ? _preferredScale : scale; +} + +TimeValue TimeBase::getStart(const TimeScale scale) const { + if (scale) + return _startTime * scale / _startScale; + + return _startTime * _preferredScale / _startScale; +} + +void TimeBase::setStop(const TimeValue stopTime, const TimeScale scale) { + _stopTime = stopTime; + _stopScale = (scale == 0) ? _preferredScale : scale; +} + +TimeValue TimeBase::getStop(const TimeScale scale) const { + if (scale) + return _stopTime * scale / _stopScale; + + return _stopTime * _preferredScale / _stopScale; +} + +void TimeBase::setSegment(const TimeValue startTime, const TimeValue stopTime, const TimeScale scale) { + setStart(startTime, scale); + setStop(stopTime, scale); +} + +void TimeBase::getSegment(TimeValue &startTime, TimeValue &stopTime, const TimeScale scale) const { + startTime = getStart(scale); + stopTime = getStop(scale); +} + +TimeValue TimeBase::getDuration(const TimeScale scale) const { + TimeValue startTime, stopTime; + getSegment(startTime, stopTime, scale); + return stopTime - startTime; +} + +void TimeBase::setMasterTimeBase(TimeBase *tb) { + // TODO: We're just ignoring the master (except for effective rate) + // for now to simplify things + if (_master) + _master->_slaves.remove(this); + + _master = tb; + + if (_master) + _master->_slaves.push_back(this); +} + +void TimeBase::updateTime() { + if (_lastMillis == 0) { + _lastMillis = g_system->getMillis(); + } else { + uint32 curTime = g_system->getMillis(); + if (_lastMillis == curTime) // No change + return; + + _time += Common::Rational(curTime - _lastMillis, 1000) * getEffectiveRate(); + _lastMillis = curTime; + } +} + +void TimeBase::checkCallBacks() { + // Nothing to do if we're paused or not running + if (_paused || !isRunning()) + return; + + Common::Rational startTime = Common::Rational(_startTime, _startScale); + Common::Rational stopTime = Common::Rational(_stopTime, _stopScale); + + // First step: update the times + updateTime(); + + // Clip time to the boundaries + if (_time >= stopTime) + _time = stopTime; + else if (_time <= startTime) + _time = startTime; + + // TODO: Update the slaves? + + Common::Rational time = Common::Rational(getTime(), getScale()); + + // Check if we've triggered any callbacks + for (TimeBaseCallBack *runner = _callBackList; runner != 0; runner = runner->_nextCallBack) { + if (runner->_hasBeenTriggered) + continue; + + if (runner->_type == kCallBackAtTime && runner->_trigger == kTriggerTimeFwd) { + if (getTime() >= (runner->_param2 * _preferredScale / runner->_param3) && getRate() > 0) { + uint param2 = runner->_param2, param3 = runner->_param3; + runner->callBack(); + // HACK: Only stop future time forward callbacks if the parameters have not been changed + // This fixes striding callbacks. Since only striding callbacks do this kind of + // craziness, I'm not too worried about this. + runner->_hasBeenTriggered = (runner->_param2 == param2 && runner->_param3 == param3); + } + } else if (runner->_type == kCallBackAtExtremes) { + if (runner->_trigger == kTriggerAtStop) { + if (time == stopTime) { + runner->callBack(); + runner->_hasBeenTriggered = true; + } + } else if (runner->_trigger == kTriggerAtStart) { + if (time == startTime) { + runner->callBack(); + runner->_hasBeenTriggered = true; + } + } + } + } + + if (getFlags() & kLoopTimeBase) { + // Loop if necessary + if (getRate() < 0 && time == startTime) + setTime(_stopTime, _stopScale); + else if (getRate() > 0 && time == stopTime) + setTime(_startTime, _startScale); + } else { + // Stop at the end + if ((getRate() > 0 && time == stopTime) || (getRate() < 0 && time == startTime)) + stop(); + } +} + +// Protected functions only called by TimeBaseCallBack. + +void TimeBase::addCallBack(TimeBaseCallBack *callBack) { + callBack->_nextCallBack = _callBackList; + _callBackList = callBack; +} + +void TimeBase::removeCallBack(TimeBaseCallBack *callBack) { + if (_callBackList == callBack) { + _callBackList = callBack->_nextCallBack; + } else { + TimeBaseCallBack *runner, *prevRunner; + + for (runner = _callBackList->_nextCallBack, prevRunner = _callBackList; runner != callBack; prevRunner = runner, runner = runner->_nextCallBack) + ; + + prevRunner->_nextCallBack = runner->_nextCallBack; + } + + callBack->_nextCallBack = 0; +} + +void TimeBase::disposeAllCallBacks() { + TimeBaseCallBack *nextRunner; + + for (TimeBaseCallBack *runner = _callBackList; runner != 0; runner = nextRunner) { + nextRunner = runner->_nextCallBack; + runner->disposeCallBack(); + runner->_nextCallBack = 0; + } + + _callBackList = 0; +} + +TimeBaseCallBack::TimeBaseCallBack() { + _timeBase = 0; + _nextCallBack = 0; + _trigger = kTriggerNone; + _type = kCallBackNone; + _hasBeenTriggered = false; +} + +TimeBaseCallBack::~TimeBaseCallBack() { + releaseCallBack(); +} + +void TimeBaseCallBack::initCallBack(TimeBase *tb, CallBackType type) { + releaseCallBack(); + _timeBase = tb; + _timeBase->addCallBack(this); + _type = type; +} + +void TimeBaseCallBack::releaseCallBack() { + if (_timeBase) + _timeBase->removeCallBack(this); + disposeCallBack(); +} + +void TimeBaseCallBack::disposeCallBack() { + _timeBase = 0; + _hasBeenTriggered = false; +} + +void TimeBaseCallBack::scheduleCallBack(CallBackTrigger trigger, uint32 param2, uint32 param3) { + // TODO: Rename param2/param3? + _trigger = trigger; + _param2 = param2; + _param3 = param3; + _hasBeenTriggered = false; +} + +void TimeBaseCallBack::cancelCallBack() { + _trigger = kTriggerNone; + _hasBeenTriggered = false; +} + +IdlerTimeBase::IdlerTimeBase() { + _lastTime = 0xffffffff; + startIdling(); +} + +void IdlerTimeBase::useIdleTime() { + uint32 currentTime = getTime(); + if (currentTime != _lastTime) { + _lastTime = currentTime; + timeChanged(_lastTime); + } +} + +NotificationCallBack::NotificationCallBack() { + _callBackFlag = 0; + _notifier = 0; +} + +void NotificationCallBack::callBack() { + if (_notifier) + _notifier->setNotificationFlags(_callBackFlag, _callBackFlag); +} + +static const NotificationFlags kFuseExpiredFlag = 1; + +Fuse::Fuse() : _fuseNotification(0, (NotificationManager *)((PegasusEngine *)g_engine)) { + _fuseNotification.notifyMe(this, kFuseExpiredFlag, kFuseExpiredFlag); + _fuseCallBack.setNotification(&_fuseNotification); + _fuseCallBack.initCallBack(&_fuseTimer, kCallBackAtExtremes); + _fuseCallBack.setCallBackFlag(kFuseExpiredFlag); +} + +void Fuse::primeFuse(const TimeValue frequency, const TimeScale scale) { + stopFuse(); + _fuseTimer.setScale(scale); + _fuseTimer.setSegment(0, frequency); + _fuseTimer.setTime(0); +} + +void Fuse::lightFuse() { + if (!_fuseTimer.isRunning()) { + _fuseCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + _fuseTimer.start(); + } +} + +void Fuse::stopFuse() { + _fuseTimer.stop(); + _fuseCallBack.cancelCallBack(); + // Make sure the fuse has not triggered but not been caught yet... + _fuseNotification.setNotificationFlags(0, 0xffffffff); +} + +void Fuse::advanceFuse(const TimeValue time) { + if (_fuseTimer.isRunning()) { + _fuseTimer.stop(); + _fuseTimer.setTime(_fuseTimer.getTime() + time); + _fuseTimer.start(); + } +} + +TimeValue Fuse::getTimeRemaining() { + return _fuseTimer.getStop() - _fuseTimer.getTime(); +} + +void Fuse::receiveNotification(Notification *, const NotificationFlags) { + stopFuse(); + invokeAction(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/timers.h b/engines/pegasus/timers.h new file mode 100644 index 0000000000..bcdca6e860 --- /dev/null +++ b/engines/pegasus/timers.h @@ -0,0 +1,260 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_TIMERS_H +#define PEGASUS_TIMERS_H + +#include "common/list.h" +#include "common/rational.h" +#include "common/func.h" + +#include "pegasus/constants.h" +#include "pegasus/notification.h" +#include "pegasus/util.h" + +namespace Pegasus { + +class Idler { +friend class PegasusEngine; + +public: + Idler(); + virtual ~Idler(); + + virtual void startIdling(); + virtual void stopIdling(); + bool isIdling() const { return _isIdling; } + +protected: + virtual void useIdleTime() {} + + bool _isIdling; + Idler *_nextIdler, *_prevIdler; +}; + +enum { + kLoopTimeBase = 1, + kPalindromeLoopTimeBase = 2, + kMaintainTimeBaseZero = 4 +}; + +class TimeBaseCallBack; + +class TimeBase { +friend class TimeBaseCallBack; +public: + TimeBase(const TimeScale = kDefaultTimeScale); + virtual ~TimeBase(); + + virtual void setTime(const TimeValue, const TimeScale = 0); + virtual TimeValue getTime(const TimeScale = 0); + + virtual void setScale(const TimeScale scale) { _preferredScale = scale; } + virtual TimeScale getScale() const { return _preferredScale; } + + virtual void setRate(const Common::Rational); + virtual Common::Rational getRate() const { return _rate; } + + virtual void start(); + virtual void stop(); + virtual bool isRunning(); + + virtual void pause(); + virtual void resume(); + bool isPaused() const { return _paused; } + + virtual void setFlags(const uint32 flags) { _flags = flags; } + virtual uint32 getFlags() const { return _flags; } + + virtual void setStart(const TimeValue, const TimeScale = 0); + virtual TimeValue getStart(const TimeScale = 0) const; + + virtual void setStop(const TimeValue, const TimeScale = 0); + virtual TimeValue getStop(const TimeScale = 0) const; + + virtual void setSegment(const TimeValue, const TimeValue, const TimeScale = 0); + virtual void getSegment(TimeValue&, TimeValue&, const TimeScale = 0) const; + + virtual TimeValue getDuration(const TimeScale = 0) const; + + virtual void setMasterTimeBase(TimeBase *timeBase); + + void disposeAllCallBacks(); + + // ScummVM's API additions (to replace the need for actual timers) + virtual void checkCallBacks(); + +protected: + void addCallBack(TimeBaseCallBack *); + void removeCallBack(TimeBaseCallBack *); + virtual void updateTime(); + + TimeBase *_master; + TimeScale _preferredScale; + TimeBaseCallBack *_callBackList; + Common::Rational _rate, _pausedRate; + bool _paused; + uint32 _startTime, _startScale; + uint32 _stopTime, _stopScale; + uint32 _flags; + + Common::Rational _time; + uint32 _lastMillis, _pauseStart; + +private: + Common::Rational getEffectiveRate() const; + + Common::List<TimeBase *> _slaves; +}; + +// Type passed to initCallBack() +enum CallBackType { + kCallBackNone = 0, + kCallBackAtTime = 1, + kCallBackAtExtremes = 4 +}; + +// Trigger passed to scheduleCallBack() +enum CallBackTrigger { + kTriggerNone = 0, + + // AtTime flags + kTriggerTimeFwd = 1, + + // AtExtremes flags + kTriggerAtStart = 1, + kTriggerAtStop = 2 +}; + +class TimeBaseCallBack { +friend class TimeBase; + +public: + TimeBaseCallBack(); + virtual ~TimeBaseCallBack(); + + void initCallBack(TimeBase *, CallBackType type); + + void releaseCallBack(); + + void scheduleCallBack(CallBackTrigger trigger, uint32 param2, uint32 param3); + void cancelCallBack(); + +protected: + virtual void callBack() = 0; + + TimeBase *_timeBase; + + // Owned and operated by TimeBase; + TimeBaseCallBack *_nextCallBack; + + // Our storage of the QuickTime timer crap + CallBackType _type; + CallBackTrigger _trigger; + uint32 _param2, _param3; + bool _hasBeenTriggered; + +private: + void disposeCallBack(); +}; + +class IdlerTimeBase : public Idler, public TimeBase { +public: + IdlerTimeBase(); + virtual ~IdlerTimeBase() { stopIdling(); } + + TimeValue getLastTime() const { return _lastTime; } + +protected: + virtual void useIdleTime(); + virtual void timeChanged(const TimeValue) {} + + TimeValue _lastTime; + +}; + +class NotificationCallBack : public TimeBaseCallBack { +public: + NotificationCallBack(); + virtual ~NotificationCallBack() {} + + void setNotification(Notification *notifier) { _notifier = notifier; } + + void setCallBackFlag(const NotificationFlags flag) { _callBackFlag = flag; } + NotificationFlags getCallBackFlag() const { return _callBackFlag; } + +protected: + void callBack(); + + Notification *_notifier; + NotificationFlags _callBackFlag; +}; + +class DynamicElement : public TimeBase { +public: + TimeValue percentSeconds(const uint32 percent) { return getScale() * percent / 100; } +}; + +class Fuse : private NotificationReceiver { +public: + Fuse(); + virtual ~Fuse() {} + + void primeFuse(const TimeValue, const TimeScale = 1); // An appropriately named function :P + void lightFuse(); + void stopFuse(); + bool isFuseLit() { return _fuseTimer.isRunning() || _fuseTimer.isPaused(); } + void advanceFuse(const TimeValue); + TimeValue getTimeRemaining(); + TimeScale getFuseScale() { return _fuseTimer.getScale(); } + + void pauseFuse() { _fuseTimer.pause(); } + void resumeFuse() { _fuseTimer.resume(); } + bool isFusePaused() { return _fuseTimer.isPaused(); } + +protected: + virtual void receiveNotification(Notification *, const NotificationFlags); + virtual void invokeAction() {} + + TimeBase _fuseTimer; + NotificationCallBack _fuseCallBack; + Notification _fuseNotification; +}; + +class FuseFunction : public Fuse { +public: + FuseFunction() : _functor(0) {} + virtual ~FuseFunction() { delete _functor; } + + void setFunctor(Common::Functor0<void> *functor) { delete _functor; _functor = functor; } +protected: + virtual void invokeAction() { if (_functor && _functor->isValid()) (*_functor)(); } + + Common::Functor0<void> *_functor; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/transition.cpp b/engines/pegasus/transition.cpp new file mode 100644 index 0000000000..1ae212df85 --- /dev/null +++ b/engines/pegasus/transition.cpp @@ -0,0 +1,200 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "common/system.h" +#include "graphics/surface.h" + +#include "pegasus/transition.h" + +namespace Pegasus { + +ScreenFader::ScreenFader() { + _isBlack = true; + // Initially, assume screens are on at full brightness. + Fader::setFaderValue(100); + _screen = new Graphics::Surface(); +} + +ScreenFader::~ScreenFader() { + _screen->free(); + delete _screen; +} + +void ScreenFader::doFadeOutSync(const TimeValue duration, const TimeValue scale, bool isBlack) { + _isBlack = isBlack; + _screen->copyFrom(*g_system->lockScreen()); + g_system->unlockScreen(); + + FaderMoveSpec spec; + spec.makeTwoKnotFaderSpec(scale, 0, getFaderValue(), duration, 0); + startFaderSync(spec); + + _screen->free(); +} + +void ScreenFader::doFadeInSync(const TimeValue duration, const TimeValue scale, bool isBlack) { + _isBlack = isBlack; + _screen->copyFrom(*g_system->lockScreen()); + g_system->unlockScreen(); + + FaderMoveSpec spec; + spec.makeTwoKnotFaderSpec(scale, 0, getFaderValue(), duration, 100); + startFaderSync(spec); + + _screen->free(); +} + +void ScreenFader::setFaderValue(const int32 value) { + if (value != getFaderValue()) { + Fader::setFaderValue(value); + + if (_screen->pixels) { + // The original game does a gamma fade here using the Mac API. In order to do + // that, it would require an immense amount of CPU processing. This does a + // linear fade instead, which looks fairly well, IMO. + Graphics::Surface *screen = g_system->lockScreen(); + + for (uint y = 0; y < _screen->h; y++) { + for (uint x = 0; x < _screen->w; x++) { + if (_screen->format.bytesPerPixel == 2) + WRITE_UINT16(screen->getBasePtr(x, y), fadePixel(READ_UINT16(_screen->getBasePtr(x, y)), value)); + else + WRITE_UINT32(screen->getBasePtr(x, y), fadePixel(READ_UINT32(_screen->getBasePtr(x, y)), value)); + } + } + + g_system->unlockScreen(); + g_system->updateScreen(); + } + } +} + +static inline byte fadeComponent(byte comp, int32 percent) { + return comp * percent / 100; +} + +uint32 ScreenFader::fadePixel(uint32 color, int32 percent) const { + byte r, g, b; + g_system->getScreenFormat().colorToRGB(color, r, g, b); + + if (_isBlack) { + r = fadeComponent(r, percent); + g = fadeComponent(g, percent); + b = fadeComponent(b, percent); + } else { + r = 0xFF - fadeComponent(0xFF - r, percent); + g = 0xFF - fadeComponent(0xFF - g, percent); + b = 0xFF - fadeComponent(0xFF - b, percent); + } + + return g_system->getScreenFormat().RGBToColor(r, g, b); +} + +Transition::Transition(const DisplayElementID id) : FaderAnimation(id) { + _outPicture = 0; + _inPicture = 0; +} + +void Transition::setBounds(const Common::Rect &r) { + FaderAnimation::setBounds(r); + _boundsWidth = _bounds.width(); + _boundsHeight = _bounds.height(); +} + +void Transition::setInAndOutElements(DisplayElement *inElement, DisplayElement *outElement) { + _inPicture = inElement; + _outPicture = outElement; + + Common::Rect r; + + if (_outPicture) + _outPicture->getBounds(r); + else if (_inPicture) + _inPicture->getBounds(r); + + setBounds(r); +} + +void Slide::draw(const Common::Rect &r) { + Common::Rect oldBounds, newBounds; + + adjustSlideRects(oldBounds, newBounds); + drawElements(r, oldBounds, newBounds); +} + +void Slide::adjustSlideRects(Common::Rect &oldBounds, Common::Rect &newBounds) { + oldBounds = _bounds; + newBounds = _bounds; +} + +void Slide::drawElements(const Common::Rect &drawRect, const Common::Rect &oldBounds, const Common::Rect &newBounds) { + drawSlideElement(drawRect, oldBounds, _outPicture); + drawSlideElement(drawRect, newBounds, _inPicture); +} + +void Slide::drawSlideElement(const Common::Rect &drawRect, const Common::Rect &oldBounds, DisplayElement *picture) { + if (picture && drawRect.intersects(oldBounds)) { + picture->moveElementTo(oldBounds.left, oldBounds.top); + picture->draw(drawRect.findIntersectingRect(oldBounds)); + } +} + +void Push::adjustSlideRects(Common::Rect &oldBounds, Common::Rect &newBounds) { + switch (_direction & kSlideHorizMask) { + case kSlideLeftMask: + newBounds.left = oldBounds.right = _bounds.right - pegasusRound(getFaderValue() * _boundsWidth, kTransitionRange); + newBounds.right = newBounds.left + _boundsWidth; + oldBounds.left = oldBounds.right - _boundsWidth; + break; + case kSlideRightMask: + oldBounds.left = newBounds.right = _bounds.left + pegasusRound(getFaderValue() * _boundsWidth, kTransitionRange); + oldBounds.right = oldBounds.left + _boundsWidth; + newBounds.left = newBounds.right - _boundsWidth; + break; + default: + newBounds.left = oldBounds.left = _bounds.left; + newBounds.right = oldBounds.right = _bounds.right; + break; + } + + switch (_direction & kSlideVertMask) { + case kSlideDownMask: + oldBounds.top = newBounds.bottom = _bounds.top + pegasusRound(getFaderValue() * _boundsHeight, kTransitionRange); + oldBounds.bottom = oldBounds.top + _boundsHeight; + newBounds.top = newBounds.bottom - _boundsHeight; + break; + case kSlideUpMask: + newBounds.top = oldBounds.bottom = _bounds.bottom - pegasusRound(getFaderValue() * _boundsHeight, kTransitionRange); + newBounds.bottom = newBounds.top + _boundsHeight; + oldBounds.top = oldBounds.bottom - _boundsHeight; + break; + default: + newBounds.top = oldBounds.top = _bounds.top; + newBounds.bottom = oldBounds.bottom = _bounds.bottom; + break; + } +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/transition.h b/engines/pegasus/transition.h new file mode 100644 index 0000000000..84241a2bd2 --- /dev/null +++ b/engines/pegasus/transition.h @@ -0,0 +1,108 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_TRANSITION_H +#define PEGASUS_TRANSITION_H + +#include "pegasus/fader.h" + +namespace Graphics { +struct Surface; +} + +namespace Pegasus { + +class ScreenFader : public Fader { +public: + ScreenFader(); + virtual ~ScreenFader(); + + void doFadeOutSync(const TimeValue = kOneSecondPerThirtyTicks, const TimeScale = kThirtyTicksPerSecond, bool isBlack = true); + void doFadeInSync(const TimeValue = kHalfSecondPerThirtyTicks, const TimeScale = kThirtyTicksPerSecond, bool isBlack = true); + + void setFaderValue(const int32); + +private: + bool _isBlack; + uint32 fadePixel(uint32 color, int32 percent) const; + Graphics::Surface *_screen; +}; + +// Transitions are faders that range over [0,1000], which makes their +// "resolution" one tenth of a percent + +static const int kTransitionBottom = 0; +static const int kTransitionTop = 1000; + +static const int kTransitionRange = kTransitionTop - kTransitionBottom; + +class Transition : public FaderAnimation { +public: + Transition(const DisplayElementID id); + virtual ~Transition() {} + + virtual void setBounds(const Common::Rect &); + + virtual void setInAndOutElements(DisplayElement *, DisplayElement *); + DisplayElement *getInElement() { return _inPicture; } + DisplayElement *getOutElement() { return _outPicture; } + +protected: + DisplayElement *_outPicture; + DisplayElement *_inPicture; + + CoordType _boundsWidth, _boundsHeight; +}; + +class Slide : public Transition { +public: + Slide(const DisplayElementID id) : Transition(id) {} + virtual ~Slide() {} + + virtual void setSlideDirection(SlideDirection dir) { _direction = dir; } + virtual void draw(const Common::Rect &); + + virtual void setDirection(const SlideDirection dir) { _direction = dir; } + +protected: + virtual void adjustSlideRects(Common::Rect &, Common::Rect &); + virtual void drawElements(const Common::Rect &, const Common::Rect &, const Common::Rect &); + virtual void drawSlideElement(const Common::Rect &, const Common::Rect &, DisplayElement *); + + SlideDirection _direction; +}; + +class Push : public Slide { +public: + Push(const DisplayElementID id) : Slide(id) {} + virtual ~Push() {} + +protected: + virtual void adjustSlideRects(Common::Rect &, Common::Rect &); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/types.h b/engines/pegasus/types.h new file mode 100644 index 0000000000..64ab4e5bb2 --- /dev/null +++ b/engines/pegasus/types.h @@ -0,0 +1,161 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_TYPES_H +#define PEGASUS_TYPES_H + +#include "common/scummsys.h" + +namespace Pegasus { + +// TODO: Probably all of these don't really need to be typedef'd... + +typedef int32 DisplayElementID; +typedef int32 DisplayOrder; + +typedef int16 HotSpotID; +typedef uint32 HotSpotFlags; + +typedef byte ButtonState; +typedef uint32 InputBits; + +typedef int32 NotificationID; +typedef uint32 NotificationFlags; + +// Mac types. +typedef int16 ResIDType; +typedef int16 CoordType; + +enum SlideDirection { + kSlideLeftMask = 1, + kSlideRightMask = kSlideLeftMask << 1, + kSlideUpMask = kSlideRightMask << 1 << 1, + kSlideDownMask = kSlideUpMask << 1, + + kSlideHorizMask = kSlideLeftMask | kSlideRightMask, + kSlideVertMask = kSlideUpMask | kSlideDownMask, + + kSlideUpLeftMask = kSlideLeftMask | kSlideUpMask, + kSlideUpRightMask = kSlideRightMask | kSlideUpMask, + kSlideDownLeftMask = kSlideLeftMask | kSlideDownMask, + kSlideDownRightMask = kSlideRightMask | kSlideDownMask +}; + +// ScummVM QuickTime/QuickDraw replacement types +typedef uint TimeValue; +typedef uint TimeScale; + +typedef int16 GameID; + +typedef GameID ItemID; +typedef GameID ActorID; +typedef GameID RoomID; +typedef GameID NeighborhoodID; +typedef byte AlternateID; +typedef int8 HotSpotActivationID; + +typedef int16 WeightType; + +typedef byte DirectionConstant; +typedef byte TurnDirection; + +// Meant to be room in low 16 bits and direction in high 16 bits. +typedef uint32 RoomViewID; + +#define MakeRoomView(room, direction) (((RoomViewID) (room)) | (((RoomViewID) (direction)) << 16)) + +typedef uint32 ExtraID; + +typedef int16 GameMode; + +typedef int16 WeightType; + +typedef int16 ItemState; + +typedef int8 DeathReason; + +typedef int32 GameMenuCommand; + +typedef int32 GameScoreType; + +typedef long CanMoveForwardReason; + +typedef long CanTurnReason; + +typedef long CanOpenDoorReason; + +enum InventoryResult { + kInventoryOK, + kTooMuchWeight, + kItemNotInInventory +}; + +typedef int32 InteractionID; + +typedef int32 AIConditionID; + +enum EnergyStage { + kStageNoStage, + kStageCasual, // more than 50% energy + kStageWorried, // more than 25% energy + kStageNervous, // more than 5% energy + kStagePanicStricken // less than 5% energy +}; + +enum NoradSubPrepState { + kSubNotPrepped, + kSubPrepped, + kSubDamaged +}; + +enum LowerClientSignature { + kNoClientSignature, + kInventorySignature, + kBiochipSignature, + kAISignature +}; + +enum LowerAreaSignature { + kLeftAreaSignature, + kMiddleAreaSignature, + kRightAreaSignature +}; + +enum AirQuality { + kAirQualityGood, + kAirQualityDirty, + kAirQualityVacuum +}; + +enum DragType { + kDragNoDrag, + kDragInventoryPickup, + kDragBiochipPickup, + kDragInventoryUse +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/util.cpp b/engines/pegasus/util.cpp new file mode 100644 index 0000000000..59df610c33 --- /dev/null +++ b/engines/pegasus/util.cpp @@ -0,0 +1,77 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 "common/random.h" +#include "common/system.h" +#include "common/util.h" + +#include "pegasus/util.h" + +namespace Pegasus { + +IDObject::IDObject(const int32 id) { + _objectID = id; +} + +IDObject::~IDObject() { +} + +int32 IDObject::getObjectID() const { + return _objectID; +} + +int operator==(const IDObject &arg1, const IDObject &arg2) { + return arg1.getObjectID() == arg2.getObjectID(); +} + +int operator!=(const IDObject &arg1, const IDObject &arg2) { + return arg1.getObjectID() != arg2.getObjectID(); +} + +int32 pegasusRound(const int32 a, const int32 b) { + if (b < 0) + if (a < 0) + return -((a - (-b >> 1)) / -b); + else + return -((a + (-b >> 1)) / -b); + else + if (a < 0) + return (a - (b >> 1)) / b; + else + return (a + (b >> 1)) / b; +} + +int32 linearInterp(const int32 start1, const int32 stop1, const int32 current1, const int32 start2, const int32 stop2) { + if (start2 == stop2) + return start2; + else + return start2 + pegasusRound((current1 - start1) * (stop2 - start2), (stop1 - start1)); +} + +uint32 tickCount() { + return g_system->getMillis() * 60 / 1000; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/util.h b/engines/pegasus/util.h new file mode 100644 index 0000000000..97ba1c20c3 --- /dev/null +++ b/engines/pegasus/util.h @@ -0,0 +1,117 @@ +/* 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. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * 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 PEGASUS_UTIL_H +#define PEGASUS_UTIL_H + +#include "common/stream.h" + +#include "pegasus/types.h" + +namespace Common { + class RandomSource; +} + +namespace Pegasus { + +class IDObject { +public: + IDObject(const int32 id); + ~IDObject(); + + int32 getObjectID() const; + +private: + int32 _objectID; +}; + +#define NUM_FLAGS (sizeof(Unit) * 8) +#define BIT_INDEX_SHIFT (sizeof(Unit) + 2 - (sizeof(Unit)) / 3) +#define BIT_INDEX_MASK (NUM_FLAGS - 1) + +template <typename Unit, uint32 kNumFlags> +class FlagsArray { +public: + FlagsArray() { clearAllFlags(); } + void clearAllFlags() { memset(_flags, 0, sizeof(_flags)); } + void setAllFlags() { memset(_flags, ~((Unit)0), sizeof(_flags)); } + void setFlag(uint32 flag) { _flags[flag >> BIT_INDEX_SHIFT] |= 1 << (flag & BIT_INDEX_MASK); } + void clearFlag(uint32 flag) { _flags[flag >> BIT_INDEX_SHIFT] &= ~(1 << (flag & BIT_INDEX_MASK)); } + void setFlag(uint32 flag, bool val) { if (val) setFlag(flag); else clearFlag(flag); } + bool getFlag(uint32 flag) { return (_flags[flag >> BIT_INDEX_SHIFT] & (1 << (flag & BIT_INDEX_MASK))) != 0; } + bool anyFlagSet() { + for (uint32 i = 0; i < sizeof(_flags); i++) + if (_flags[i] != 0) + return true; + return false; + } + + void readFromStream(Common::ReadStream *stream) { + // Shortcut + if (sizeof(Unit) == 1) { + stream->read(_flags, sizeof(_flags)); + return; + } + + for (uint32 i = 0; i < ARRAYSIZE(_flags); i++) { + if (sizeof(Unit) == 2) + _flags[i] = stream->readUint16BE(); + else /* if (sizeof(Unit) == 4) */ + _flags[i] = stream->readUint32BE(); + } + } + + void writeToStream(Common::WriteStream *stream) { + // Shortcut + if (sizeof(Unit) == 1) { + stream->write(_flags, sizeof(_flags)); + return; + } + + for (uint32 i = 0; i < ARRAYSIZE(_flags); i++) { + if (sizeof(Unit) == 2) + stream->writeUint16BE(_flags[i]); + else /* if (sizeof(Unit) == 4) */ + stream->writeUint32BE(_flags[i]); + } + } + +private: + Unit _flags[(kNumFlags - 1) / NUM_FLAGS + 1]; +}; + +#undef NUM_FLAGS +#undef BIT_INDEX_SHIFT +#undef BIT_INDEX_MASK + +int32 linearInterp(const int32 start1, const int32 stop1, const int32 current1, const int32 start2, const int32 stop2); + +int32 pegasusRound(const int32 a, const int32 b); + +uint32 tickCount(); + +} // End of namespace Pegasus + +#endif diff --git a/engines/plugins_table.h b/engines/plugins_table.h index fac956755e..010de0d5e2 100644 --- a/engines/plugins_table.h +++ b/engines/plugins_table.h @@ -56,6 +56,9 @@ LINK_PLUGIN(MOHAWK) #if PLUGIN_ENABLED_STATIC(PARALLACTION) LINK_PLUGIN(PARALLACTION) #endif +#if PLUGIN_ENABLED_STATIC(PEGASUS) +LINK_PLUGIN(PEGASUS) +#endif #if PLUGIN_ENABLED_STATIC(QUEEN) LINK_PLUGIN(QUEEN) #endif @@ -89,6 +92,9 @@ LINK_PLUGIN(TINSEL) #if PLUGIN_ENABLED_STATIC(TOLTECS) LINK_PLUGIN(TOLTECS) #endif +#if PLUGIN_ENABLED_STATIC(TONY) +LINK_PLUGIN(TONY) +#endif #if PLUGIN_ENABLED_STATIC(TOON) LINK_PLUGIN(TOON) #endif @@ -101,3 +107,6 @@ LINK_PLUGIN(TOUCHE) #if PLUGIN_ENABLED_STATIC(TUCKER) LINK_PLUGIN(TUCKER) #endif +#if PLUGIN_ENABLED_STATIC(WINTERMUTE) +LINK_PLUGIN(WINTERMUTE) +#endif diff --git a/engines/queen/display.cpp b/engines/queen/display.cpp index 83dc1a9f60..d7b20c203e 100644 --- a/engines/queen/display.cpp +++ b/engines/queen/display.cpp @@ -23,9 +23,14 @@ #include "common/system.h" #include "common/events.h" +#include "common/stream.h" +#include "common/memstream.h" #include "graphics/cursorman.h" #include "graphics/palette.h" +#include "graphics/surface.h" +#include "graphics/decoders/iff.h" +#include "graphics/decoders/pcx.h" #include "queen/display.h" #include "queen/input.h" @@ -697,7 +702,7 @@ void Display::setupPanel() { uint8 *data = _vm->resource()->loadFile(dataName, 0, &dataSize); if (_vm->resource()->getPlatform() == Common::kPlatformAmiga) { - decodeLBM(data, dataSize, _panelBuf, PANEL_W, &panelWidth, &panelHeight, _pal.panel, 0, 32, 144); + decodeIFF(data, dataSize, _panelBuf, PANEL_W, &panelWidth, &panelHeight, _pal.panel, 0, 32, 144); } else { WRITE_LE_UINT16(data + 14, PANEL_H - 10); decodePCX(data, dataSize, _panelBuf + PANEL_W * 10, PANEL_W, &panelWidth, &panelHeight, _pal.panel, 144, 256); @@ -716,7 +721,7 @@ void Display::setupNewRoom(const char *name, uint16 room) { uint8 *data = _vm->resource()->loadFile(dataName, 0, &dataSize); if (_vm->resource()->getPlatform() == Common::kPlatformAmiga) { - decodeLBM(data, dataSize, _backdropBuf, BACKDROP_W, &_bdWidth, &_bdHeight, _pal.room, 0, 32); + decodeIFF(data, dataSize, _backdropBuf, BACKDROP_W, &_bdWidth, &_bdHeight, _pal.room, 0, 32); if (_bdHeight < BACKDROP_H) { memset(_backdropBuf + _bdHeight * BACKDROP_W, 0, (BACKDROP_H - _bdHeight) * BACKDROP_W); } @@ -806,97 +811,40 @@ void Display::fill(uint8 *dstBuf, uint16 dstPitch, uint16 x, uint16 y, uint16 w, } void Display::decodePCX(const uint8 *src, uint32 srcSize, uint8 *dst, uint16 dstPitch, uint16 *w, uint16 *h, uint8 *pal, uint16 palStart, uint16 palEnd) { - *w = READ_LE_UINT16(src + 12); - *h = READ_LE_UINT16(src + 14); + Common::MemoryReadStream str(src, srcSize); + + ::Graphics::PCXDecoder pcx; + if (!pcx.loadStream(str)) + error("Error while reading PCX image"); + + const ::Graphics::Surface *pcxSurface = pcx.getSurface(); + if (pcxSurface->format.bytesPerPixel != 1) + error("Invalid bytes per pixel in PCX surface (%d)", pcxSurface->format.bytesPerPixel); + *w = pcxSurface->w; + *h = pcxSurface->h; assert(palStart <= palEnd && palEnd <= 256); - const uint8 *palData = src + srcSize - 768; - memcpy(pal, palData + palStart * 3, (palEnd - palStart) * 3); - - src += 128; - for (int y = 0; y < *h; ++y) { - uint8 *p = dst; - while (p < dst + *w) { - uint8 col = *src++; - if ((col & 0xC0) == 0xC0) { - uint8 len = col & 0x3F; - memset(p, *src++, len); - p += len; - } else { - *p++ = col; - } - } - dst += dstPitch; - } + memcpy(pal, pcx.getPalette() + palStart * 3, (palEnd - palStart) * 3); + for (uint16 y = 0; y < pcxSurface->h; y++) + memcpy(dst + y * dstPitch, pcxSurface->getBasePtr(0, y), pcxSurface->w); } -void Display::decodeLBM(const uint8 *src, uint32 srcSize, uint8 *dst, uint16 dstPitch, uint16 *w, uint16 *h, uint8 *pal, uint16 palStart, uint16 palEnd, uint8 colorBase) { - int planeCount = 0, planePitch = 0; - const uint8 *srcEnd = src + srcSize; - src += 12; - while (src < srcEnd) { - uint32 type = READ_BE_UINT32(src); - uint32 size = READ_BE_UINT32(src + 4); - src += 8; - switch (type) { - case MKTAG('B','M','H','D'): { - *w = READ_BE_UINT16(src + 0); - *h = READ_BE_UINT16(src + 2); - planeCount = src[8]; - planePitch = ((*w + 15) >> 4) * 2; - } - break; - case MKTAG('C','M','A','P'): { - assert(palStart <= palEnd && palEnd <= size / 3); - memcpy(pal, src + palStart * 3, (palEnd - palStart) * 3); - } - break; - case MKTAG('B','O','D','Y'): { - uint32 planarSize = (*h) * planeCount * planePitch; - uint8 *planarBuf = new uint8[planarSize]; - uint8 *dstPlanar = planarBuf; - for (int y = 0; y < *h; ++y) { - for (int p = 0; p < planeCount; ++p) { - const uint8 *end = dstPlanar + planePitch; - while (dstPlanar < end) { - int code = (int8)*src++; - if (code != -128) { - if (code < 0) { - code = -code + 1; - memset(dstPlanar, *src++, code); - } else { - ++code; - memcpy(dstPlanar, src, code); - src += code; - } - dstPlanar += code; - } - } - } - } - src = planarBuf; - for (int y = 0; y < *h; ++y) { - for (int x = 0; x < *w / 8; ++x) { - for (int b = 0; b < 8; ++b) { - const uint8 mask = (1 << (7 - b)); - uint8 color = 0; - for (int p = 0; p < planeCount; ++p) { - if (src[planePitch * p + x] & mask) { - color |= 1 << p; - } - } - dst[x * 8 + b] = colorBase + color; - } - } - src += planeCount * planePitch; - dst += dstPitch; - } - delete[] planarBuf; - } - return; - } - src += size; - } +void Display::decodeIFF(const uint8 *src, uint32 srcSize, uint8 *dst, uint16 dstPitch, uint16 *w, uint16 *h, uint8 *pal, uint16 palStart, uint16 palEnd, uint8 colorBase) { + Common::MemoryReadStream str(src, srcSize); + + ::Graphics::IFFDecoder iff; + if (!iff.loadStream(str)) + error("Error while reading IFF image"); + + const ::Graphics::Surface *iffSurface = iff.getSurface(); + *w = iffSurface->w; + *h = iffSurface->h; + + assert(palStart <= palEnd && palEnd <= 256); + memcpy(pal, iff.getPalette() + palStart * 3, (palEnd - palStart) * 3); + for (uint16 y = 0; y < iffSurface->h; y++) + for(uint16 x = 0; x < iffSurface->w; x++) + dst[(y * dstPitch) + x] = *(const byte *)iffSurface->getBasePtr(x, y) + colorBase; } void Display::horizontalScrollUpdate(int16 xCamera) { diff --git a/engines/queen/display.h b/engines/queen/display.h index 4256b19d72..8a8aaef5a6 100644 --- a/engines/queen/display.h +++ b/engines/queen/display.h @@ -116,8 +116,8 @@ public: //! decode PCX picture data void decodePCX(const uint8 *src, uint32 srcSize, uint8 *dst, uint16 dstPitch, uint16 *w, uint16 *h, uint8 *pal, uint16 palStart, uint16 palEnd); - //! decode ILBM picture data - void decodeLBM(const uint8 *src, uint32 srcSize, uint8 *dst, uint16 dstPitch, uint16 *w, uint16 *h, uint8 *pal, uint16 palStart, uint16 palEnd, uint8 colorBase = 0); + //! decode IFF picture data + void decodeIFF(const uint8 *src, uint32 srcSize, uint8 *dst, uint16 dstPitch, uint16 *w, uint16 *h, uint8 *pal, uint16 palStart, uint16 palEnd, uint8 colorBase = 0); void horizontalScrollUpdate(int16 xCamera); void horizontalScroll(int16 scroll); diff --git a/engines/queen/journal.cpp b/engines/queen/journal.cpp index 704019641b..474f72eca5 100644 --- a/engines/queen/journal.cpp +++ b/engines/queen/journal.cpp @@ -400,7 +400,7 @@ static void removeLeadingAndTrailingSpaces(char *dst, size_t dstSize, const char while (src[lastNonSpaceIndex] == ' ') --lastNonSpaceIndex; - size_t newLen = lastNonSpaceIndex - firstNonSpaceIndex + 1; + uint newLen = lastNonSpaceIndex - firstNonSpaceIndex + 1; assert(newLen < dstSize); for (size_t i = 0; i < newLen; ++i) { dst[i] = src[firstNonSpaceIndex + i]; @@ -559,7 +559,7 @@ void Journal::updateTextField(uint16 ascii, int keycode) { } break; default: - if (isprint((char)ascii) && + if (Common::isPrint((char)ascii) && _textField.textCharsCount < (sizeof(_textField.text) - 1) && _vm->display()->textWidth(_textField.text) < _textField.w) { _textField.text[_textField.textCharsCount] = (char)ascii; diff --git a/engines/queen/queen.cpp b/engines/queen/queen.cpp index 3acc87b856..c403536e22 100644 --- a/engines/queen/queen.cpp +++ b/engines/queen/queen.cpp @@ -56,8 +56,8 @@ static const PlainGameDescriptor queenGameDescriptor = { }; static const ExtraGuiOption queenExtraGuiOption = { - _s("Floppy intro"), - _s("Use the floppy version's intro (CD version only)"), + _s("Alternative intro"), + _s("Use an alternative game intro (CD version only)"), "alt_intro", false }; @@ -113,12 +113,12 @@ int QueenMetaEngine::getMaximumSaveSlot() const { return 99; } const ExtraGuiOptions QueenMetaEngine::getExtraGuiOptions(const Common::String &target) const { Common::String guiOptions; ExtraGuiOptions options; - + if (target.empty()) { options.push_back(queenExtraGuiOption); return options; } - + if (ConfMan.hasKey("guioptions", target)) { guiOptions = ConfMan.get("guioptions", target); guiOptions = parseGameGUIOptions(guiOptions); diff --git a/engines/queen/talk.cpp b/engines/queen/talk.cpp index 94bc105bb0..1531510ba4 100644 --- a/engines/queen/talk.cpp +++ b/engines/queen/talk.cpp @@ -96,7 +96,6 @@ void Talk::talk(const char *filename, int personInRoom, char *cutawayFilename) { } int16 oldLevel = 0; - bool personWalking = false; // FIXME: unused // Lines 828-846 in talk.c for (i = 1; i <= 4; i++) { @@ -174,8 +173,7 @@ void Talk::talk(const char *filename, int personInRoom, char *cutawayFilename) { if (1 == choicesLeft) { // Automatically run the final dialogue option - if (speak(_talkString[0], &person, otherVoiceFilePrefix)) - personWalking = true; + speak(_talkString[0], &person, otherVoiceFilePrefix); if (_vm->input()->talkQuit()) break; @@ -251,8 +249,7 @@ void Talk::talk(const char *filename, int personInRoom, char *cutawayFilename) { findDialogueString(_person1PtrOff, head, _pMax, _talkString[0]); if (_talkString[0][0] != '\0') { sprintf(otherVoiceFilePrefix, "%2d%4xP", _talkKey, head); - if (speak(_talkString[0], &person, otherVoiceFilePrefix)) - personWalking = true; + speak(_talkString[0], &person, otherVoiceFilePrefix); } } } diff --git a/engines/saga/detection.cpp b/engines/saga/detection.cpp index 9c178559f2..f6872c41ad 100644 --- a/engines/saga/detection.cpp +++ b/engines/saga/detection.cpp @@ -156,7 +156,8 @@ bool SagaMetaEngine::hasFeature(MetaEngineFeature f) const { (f == kSupportsDeleteSave) || (f == kSavesSupportMetaInfo) || (f == kSavesSupportThumbnail) || - (f == kSavesSupportCreationDate); + (f == kSavesSupportCreationDate) || + (f == kSavesSupportPlayTime); } bool Saga::SagaEngine::hasFeature(EngineFeature f) const { @@ -270,7 +271,10 @@ SaveStateDescriptor SagaMetaEngine::querySaveMetaInfos(const char *target, int s desc.setSaveTime(hour, minutes); - // TODO: played time + if (version >= 8) { + uint32 playTime = in->readUint32BE(); + desc.setPlayTime(playTime * 1000); + } } delete in; diff --git a/engines/saga/events.cpp b/engines/saga/events.cpp index ec3ef2f6f9..d98cef0740 100644 --- a/engines/saga/events.cpp +++ b/engines/saga/events.cpp @@ -332,13 +332,22 @@ int Events::handleOneShot(Event *event) { #ifdef ENABLE_IHNM if (_vm->getGameId() == GID_IHNM) { + PalEntry portraitBgColor = _vm->_interface->_portraitBgColor; + byte portraitColor = (_vm->getLanguage() == Common::ES_ESP) ? 253 : 254; + + // Set the portrait bg color, in case a saved state is restored from the + // launcher. In this case, sfSetPortraitBgColor is not called, thus the + // portrait color will always be 0 (black). + if (portraitBgColor.red == 0 && portraitBgColor.green == 0 && portraitBgColor.blue == 0) + portraitBgColor.green = 255; + if (_vm->_spiritualBarometer > 255) - _vm->_gfx->setPaletteColor(kIHNMColorPortrait, 0xff, 0xff, 0xff); + _vm->_gfx->setPaletteColor(portraitColor, 0xff, 0xff, 0xff); else - _vm->_gfx->setPaletteColor(kIHNMColorPortrait, - _vm->_spiritualBarometer * _vm->_interface->_portraitBgColor.red / 256, - _vm->_spiritualBarometer * _vm->_interface->_portraitBgColor.green / 256, - _vm->_spiritualBarometer * _vm->_interface->_portraitBgColor.blue / 256); + _vm->_gfx->setPaletteColor(portraitColor, + _vm->_spiritualBarometer * portraitBgColor.red / 256, + _vm->_spiritualBarometer * portraitBgColor.green / 256, + _vm->_spiritualBarometer * portraitBgColor.blue / 256); } #endif diff --git a/engines/saga/introproc_saga2.cpp b/engines/saga/introproc_saga2.cpp index b6470370af..260eca98e6 100644 --- a/engines/saga/introproc_saga2.cpp +++ b/engines/saga/introproc_saga2.cpp @@ -32,6 +32,7 @@ #include "common/keyboard.h" #include "common/system.h" #include "common/textconsole.h" +#include "graphics/palette.h" #include "graphics/surface.h" #include "video/smk_decoder.h" @@ -92,7 +93,7 @@ int Scene::FTA2EndProc(FTA2Endings whichEnding) { } void Scene::playMovie(const char *filename) { - Video::SmackerDecoder *smkDecoder = new Video::SmackerDecoder(_vm->_mixer); + Video::SmackerDecoder *smkDecoder = new Video::SmackerDecoder(); if (!smkDecoder->loadFile(filename)) return; @@ -101,6 +102,8 @@ void Scene::playMovie(const char *filename) { uint16 y = (g_system->getHeight() - smkDecoder->getHeight()) / 2; bool skipVideo = false; + smkDecoder->start(); + while (!_vm->shouldQuit() && !smkDecoder->endOfVideo() && !skipVideo) { if (smkDecoder->needsUpdate()) { const Graphics::Surface *frame = smkDecoder->decodeNextFrame(); @@ -108,7 +111,7 @@ void Scene::playMovie(const char *filename) { _vm->_system->copyRectToScreen(frame->pixels, frame->pitch, x, y, frame->w, frame->h); if (smkDecoder->hasDirtyPalette()) - smkDecoder->setSystemPalette(); + _vm->_system->getPaletteManager()->setPalette(smkDecoder->getPalette(), 0, 256); _vm->_system->updateScreen(); } diff --git a/engines/saga/music.cpp b/engines/saga/music.cpp index 13850a0b6d..0eebf3f175 100644 --- a/engines/saga/music.cpp +++ b/engines/saga/music.cpp @@ -30,6 +30,7 @@ #include "audio/audiostream.h" #include "audio/mididrv.h" #include "audio/midiparser.h" +#include "audio/midiparser_qt.h" #include "audio/decoders/raw.h" #include "common/config-manager.h" #include "common/file.h" @@ -76,19 +77,14 @@ void MusicDriver::play(SagaEngine *vm, ByteArray *buffer, bool loop) { } // Check if the game is using XMIDI or SMF music - if (vm->getGameId() == GID_IHNM && vm->isMacResources()) { - // Just set an XMIDI parser for Mac IHNM for now + if (!memcmp(buffer->getBuffer(), "FORM", 4)) { _parser = MidiParser::createParser_XMIDI(); + // ITE had MT32 mapped instruments + _isGM = (vm->getGameId() != GID_ITE); } else { - if (!memcmp(buffer->getBuffer(), "FORM", 4)) { - _parser = MidiParser::createParser_XMIDI(); - // ITE had MT32 mapped instruments - _isGM = (vm->getGameId() != GID_ITE); - } else { - _parser = MidiParser::createParser_SMF(); - // ITE with standalone MIDI files is General MIDI - _isGM = (vm->getGameId() == GID_ITE); - } + _parser = MidiParser::createParser_SMF(); + // ITE with standalone MIDI files is General MIDI + _isGM = (vm->getGameId() == GID_ITE); } if (!_parser->loadMusic(buffer->getBuffer(), buffer->size())) @@ -107,6 +103,27 @@ void MusicDriver::play(SagaEngine *vm, ByteArray *buffer, bool loop) { _isPlaying = true; } +void MusicDriver::playQuickTime(const Common::String &musicName, bool loop) { + // IHNM Mac uses QuickTime MIDI + _parser = MidiParser::createParser_QT(); + _isGM = true; + + if (!((MidiParser_QT *)_parser)->loadFromContainerFile(musicName)) + error("MusicDriver::playQuickTime(): Failed to load file '%s'", musicName.c_str()); + + _parser->setTrack(0); + _parser->setMidiDriver(this); + _parser->setTimerRate(_driver->getBaseTempo()); + _parser->property(MidiParser::mpCenterPitchWheelOnUnload, 1); + _parser->property(MidiParser::mpSendSustainOffOnNotesOff, 1); + + // Handle music looping + _parser->property(MidiParser::mpAutoLoop, loop); +// _isLooping = loop; + + _isPlaying = true; +} + void MusicDriver::pause() { _isPlaying = false; } @@ -343,31 +360,19 @@ void Music::play(uint32 resourceId, MusicFlags flags) { // Load MIDI/XMI resource data if (_vm->getGameId() == GID_IHNM && _vm->isMacResources()) { - // Load the external music file for Mac IHNM -#if 0 - Common::File musicFile; - char musicFileName[40]; - sprintf(musicFileName, "Music/Music%02x", resourceId); - musicFile.open(musicFileName); - resourceSize = musicFile.size(); - resourceData = new byte[resourceSize]; - musicFile.read(resourceData, resourceSize); - musicFile.close(); - - // TODO: The Mac music format is unsupported (QuickTime MIDI) - // so stop here -#endif - return; + // Load the external music file for Mac IHNM + _player->playQuickTime(Common::String::format("Music/Music%02x", resourceId), flags & MUSIC_LOOP); } else { if (_currentMusicBuffer == &_musicBuffer[1]) { _currentMusicBuffer = &_musicBuffer[0]; } else { _currentMusicBuffer = &_musicBuffer[1]; } + _vm->_resource->loadResource(_musicContext, resourceId, *_currentMusicBuffer); + _player->play(_vm, _currentMusicBuffer, (flags & MUSIC_LOOP)); } - _player->play(_vm, _currentMusicBuffer, (flags & MUSIC_LOOP)); setVolume(_vm->_musicVolume); } diff --git a/engines/saga/music.h b/engines/saga/music.h index 5a4e662af4..081fab21f6 100644 --- a/engines/saga/music.h +++ b/engines/saga/music.h @@ -46,6 +46,7 @@ public: MusicDriver(); void play(SagaEngine *vm, ByteArray *buffer, bool loop); + void playQuickTime(const Common::String &musicName, bool loop); virtual void pause(); virtual void resume(); diff --git a/engines/saga/resource.cpp b/engines/saga/resource.cpp index 1b0dfa2f22..8025a949d4 100644 --- a/engines/saga/resource.cpp +++ b/engines/saga/resource.cpp @@ -200,15 +200,15 @@ bool Resource::createContexts() { //// Detect and add voice files ///////////////////////////////////////////// SoundFileInfo voiceFiles[] = { - { GID_ITE, "voices.rsc", false , (_soundFileName[0] == 0) ? GAME_SOUNDFILE : 0}, - { GID_ITE, "voices.cmp", true , (_soundFileName[0] == 0) ? GAME_SOUNDFILE : 0}, - { GID_ITE, "voicesd.rsc", false , (_soundFileName[0] == 0) ? GAME_SOUNDFILE : 0}, - { GID_ITE, "voicesd.cmp", true , (_soundFileName[0] == 0) ? GAME_SOUNDFILE : 0}, + { GID_ITE, "voices.rsc", false , (uint16)((_soundFileName[0] == 0) ? GAME_SOUNDFILE : 0)}, + { GID_ITE, "voices.cmp", true , (uint16)((_soundFileName[0] == 0) ? GAME_SOUNDFILE : 0)}, + { GID_ITE, "voicesd.rsc", false , (uint16)((_soundFileName[0] == 0) ? GAME_SOUNDFILE : 0)}, + { GID_ITE, "voicesd.cmp", true , (uint16)((_soundFileName[0] == 0) ? GAME_SOUNDFILE : 0)}, // The resources in the Wyrmkeep combined Windows/Mac/Linux CD version are little endian, but // the voice file is big endian. If we got such a version with mixed files, mark this voice file // as big endian - { GID_ITE, "inherit the earth voices", false , _vm->isBigEndian() ? 0 : GAME_SWAPENDIAN}, - { GID_ITE, "inherit the earth voices.cmp", true , _vm->isBigEndian() ? 0 : GAME_SWAPENDIAN}, + { GID_ITE, "inherit the earth voices", false , (uint16)(_vm->isBigEndian() ? 0 : GAME_SWAPENDIAN)}, + { GID_ITE, "inherit the earth voices.cmp", true , (uint16)(_vm->isBigEndian() ? 0 : GAME_SWAPENDIAN)}, { GID_ITE, "ite voices.bin", false , GAME_MACBINARY}, #ifdef ENABLE_IHNM { GID_IHNM, "voicess.res", false , 0}, diff --git a/engines/saga/saga.cpp b/engines/saga/saga.cpp index 6e272d37c0..239a3be9db 100644 --- a/engines/saga/saga.cpp +++ b/engines/saga/saga.cpp @@ -202,6 +202,8 @@ SagaEngine::~SagaEngine() { } Common::Error SagaEngine::run() { + setTotalPlayTime(0); + // Assign default values to the config manager, in case settings are missing ConfMan.registerDefault("talkspeed", "255"); ConfMan.registerDefault("subtitles", "true"); diff --git a/engines/saga/saga.h b/engines/saga/saga.h index 829425aeaf..01cab21f5d 100644 --- a/engines/saga/saga.h +++ b/engines/saga/saga.h @@ -395,9 +395,7 @@ enum ColorId { kITEColorBlue = 0x93, kITEColorLightBlue94 = 0x94, kITEColorLightBlue96 = 0x96, - kITEColorGreen = 0xba, - - kIHNMColorPortrait = 0xfe + kITEColorGreen = 0xba }; enum KnownColor { diff --git a/engines/saga/saveload.cpp b/engines/saga/saveload.cpp index 6a5a7d8e14..3cd44eba40 100644 --- a/engines/saga/saveload.cpp +++ b/engines/saga/saveload.cpp @@ -34,7 +34,7 @@ #include "saga/scene.h" #include "saga/script.h" -#define CURRENT_SAGA_VER 7 +#define CURRENT_SAGA_VER 8 namespace Saga { @@ -204,10 +204,11 @@ void SagaEngine::save(const char *fileName, const char *saveName) { uint32 saveDate = ((curTime.tm_mday & 0xFF) << 24) | (((curTime.tm_mon + 1) & 0xFF) << 16) | ((curTime.tm_year + 1900) & 0xFFFF); uint16 saveTime = ((curTime.tm_hour & 0xFF) << 8) | ((curTime.tm_min) & 0xFF); + uint32 playTime = g_engine->getTotalPlayTime() / 1000; out->writeUint32BE(saveDate); out->writeUint16BE(saveTime); - // TODO: played time + out->writeUint32BE(playTime); // Surrounding scene out->writeSint32LE(_scene->getOutsetSceneNumber()); @@ -299,7 +300,11 @@ void SagaEngine::load(const char *fileName) { in->readUint32BE(); // save date in->readUint16BE(); // save time - // TODO: played time + + if (_saveHeader.version >= 8) { + uint32 playTime = in->readUint32BE(); + g_engine->setTotalPlayTime(playTime * 1000); + } } // Clear pending events here, and don't process queued music events diff --git a/engines/saga/scene.cpp b/engines/saga/scene.cpp index 35d923f821..75876b1c90 100644 --- a/engines/saga/scene.cpp +++ b/engines/saga/scene.cpp @@ -41,9 +41,10 @@ #include "saga/actor.h" #include "saga/resource.h" -#include "graphics/iff.h" #include "common/util.h" +#include "graphics/decoders/iff.h" + namespace Saga { static int initSceneDoors[SCENE_DOORS_MAX] = { @@ -450,11 +451,11 @@ void Scene::changeScene(int16 sceneNumber, int actorsEntrance, SceneTransitionTy debug(5, "Scene::changeScene(%d, %d, %d, %d)", sceneNumber, actorsEntrance, transitionType, chapter); // This is used for latter ITE demos where all places on world map except - // Tent Faire are substituted with LBM picture and short description + // Tent Faire are substituted with IFF picture and short description if (_vm->_hasITESceneSubstitutes) { for (int i = 0; i < ARRAYSIZE(sceneSubstitutes); i++) { if (sceneSubstitutes[i].sceneId == sceneNumber) { - byte *pal, colors[768]; + const byte *pal; Common::File file; Rect rect; PalEntry cPal[PAL_ENTRIES]; @@ -462,12 +463,12 @@ void Scene::changeScene(int16 sceneNumber, int actorsEntrance, SceneTransitionTy _vm->_interface->setMode(kPanelSceneSubstitute); if (file.open(sceneSubstitutes[i].image)) { - Graphics::Surface bbmBuffer; - Graphics::decodePBM(file, bbmBuffer, colors); - pal = colors; - rect.setWidth(bbmBuffer.w); - rect.setHeight(bbmBuffer.h); - _vm->_gfx->drawRegion(rect, (const byte*)bbmBuffer.pixels); + Graphics::IFFDecoder decoder; + decoder.loadStream(file); + pal = decoder.getPalette(); + rect.setWidth(decoder.getSurface()->w); + rect.setHeight(decoder.getSurface()->h); + _vm->_gfx->drawRegion(rect, (const byte *)decoder.getSurface()->pixels); for (int j = 0; j < PAL_ENTRIES; j++) { cPal[j].red = *pal++; cPal[j].green = *pal++; diff --git a/engines/saga/script.cpp b/engines/saga/script.cpp index 96746b538c..3efc554cb3 100644 --- a/engines/saga/script.cpp +++ b/engines/saga/script.cpp @@ -948,7 +948,7 @@ void Script::opSpeak(SCRIPTOP_PARAMS) { // scripts change to scene 5, but do not clear the cutaway that appears // before Gorrister's speech starts, resulting in a deadlock. We do this // manually here. - if (_vm->getGameId() == GID_IHNM && _vm->_scene->currentChapterNumber() == 1 && + if (_vm->getGameId() == GID_IHNM && _vm->_scene->currentChapterNumber() == 1 && _vm->_scene->currentSceneNumber() == 5 && _vm->_anim->hasCutaway()) { _vm->_anim->returnFromCutaway(); } diff --git a/engines/saga/sfuncs_ihnm.cpp b/engines/saga/sfuncs_ihnm.cpp index 3fbf3b6e67..fdfd0fdf2c 100644 --- a/engines/saga/sfuncs_ihnm.cpp +++ b/engines/saga/sfuncs_ihnm.cpp @@ -168,17 +168,25 @@ void Script::sfSetChapterPoints(SCRIPTFUNC_PARAMS) { _vm->_ethicsPoints[chapter] = thread->pop(); int16 barometer = thread->pop(); static PalEntry cur_pal[PAL_ENTRIES]; + PalEntry portraitBgColor = _vm->_interface->_portraitBgColor; + byte portraitColor = (_vm->getLanguage() == Common::ES_ESP) ? 253 : 254; _vm->_spiritualBarometer = _vm->_ethicsPoints[chapter] * 256 / barometer; _vm->_scene->setChapterPointsChanged(true); // don't save this music when saving in IHNM + // Set the portrait bg color, in case a saved state is restored from the + // launcher. In this case, sfSetPortraitBgColor is not called, thus the + // portrait color will always be 0 (black). + if (portraitBgColor.red == 0 && portraitBgColor.green == 0 && portraitBgColor.blue == 0) + portraitBgColor.green = 255; + if (_vm->_spiritualBarometer > 255) - _vm->_gfx->setPaletteColor(kIHNMColorPortrait, 0xff, 0xff, 0xff); + _vm->_gfx->setPaletteColor(portraitColor, 0xff, 0xff, 0xff); else - _vm->_gfx->setPaletteColor(kIHNMColorPortrait, - _vm->_spiritualBarometer * _vm->_interface->_portraitBgColor.red / 256, - _vm->_spiritualBarometer * _vm->_interface->_portraitBgColor.green / 256, - _vm->_spiritualBarometer * _vm->_interface->_portraitBgColor.blue / 256); + _vm->_gfx->setPaletteColor(portraitColor, + _vm->_spiritualBarometer * portraitBgColor.red / 256, + _vm->_spiritualBarometer * portraitBgColor.green / 256, + _vm->_spiritualBarometer * portraitBgColor.blue / 256); _vm->_gfx->getCurrentPal(cur_pal); _vm->_gfx->setPalette(cur_pal); diff --git a/engines/saga/shorten.cpp b/engines/saga/shorten.cpp index 5efc8d1f67..69c27b6a6b 100644 --- a/engines/saga/shorten.cpp +++ b/engines/saga/shorten.cpp @@ -519,9 +519,6 @@ byte *loadShortenFromStream(Common::ReadStream &stream, int &size, int &rate, by if (maxLPC > 0) free(lpc); - if (size > 0) - free(unpackedBuffer); - delete gReader; return unpackedBuffer; } diff --git a/engines/sci/console.cpp b/engines/sci/console.cpp index 564bbbbd79..1bf3323a7b 100644 --- a/engines/sci/console.cpp +++ b/engines/sci/console.cpp @@ -250,20 +250,18 @@ void Console::postEnter() { #endif if (_videoFile.hasSuffix(".seq")) { - SeqDecoder *seqDecoder = new SeqDecoder(); - seqDecoder->setFrameDelay(_videoFrameDelay); - videoDecoder = seqDecoder; + videoDecoder = new SEQDecoder(_videoFrameDelay); #ifdef ENABLE_SCI32 } else if (_videoFile.hasSuffix(".vmd")) { - videoDecoder = new Video::VMDDecoder(g_system->getMixer()); + videoDecoder = new Video::AdvancedVMDDecoder(); } else if (_videoFile.hasSuffix(".rbt")) { - videoDecoder = new RobotDecoder(g_system->getMixer(), _engine->getPlatform() == Common::kPlatformMacintosh); + videoDecoder = new RobotDecoder(_engine->getPlatform() == Common::kPlatformMacintosh); } else if (_videoFile.hasSuffix(".duk")) { duckMode = true; - videoDecoder = new Video::AviDecoder(g_system->getMixer()); + videoDecoder = new Video::AVIDecoder(); #endif } else if (_videoFile.hasSuffix(".avi")) { - videoDecoder = new Video::AviDecoder(g_system->getMixer()); + videoDecoder = new Video::AVIDecoder(); } else { warning("Unrecognized video type"); } @@ -2884,17 +2882,17 @@ bool Console::cmdDisassemble(int argc, const char **argv) { reg_t addr = NULL_REG; if (!obj) { - DebugPrintf("Not an object."); + DebugPrintf("Not an object.\n"); return true; } if (selectorId < 0) { - DebugPrintf("Not a valid selector name."); + DebugPrintf("Not a valid selector name.\n"); return true; } if (lookupSelector(_engine->_gamestate->_segMan, objAddr, selectorId, NULL, &addr) != kSelectorMethod) { - DebugPrintf("Not a method."); + DebugPrintf("Not a method.\n"); return true; } @@ -3635,6 +3633,8 @@ bool Console::cmdAddresses(int argc, const char **argv) { DebugPrintf(" - ?obj -- Looks up an object with the specified name, uses its address. This will abort if\n"); DebugPrintf(" the object name is ambiguous; in that case, a list of addresses and indices is provided.\n"); DebugPrintf(" ?obj.idx may be used to disambiguate 'obj' by the index 'idx'.\n"); + DebugPrintf(" Underscores are used as substitute characters for spaces in object names.\n"); + DebugPrintf(" For example, an object named \"Glass Jar\" can be accessed as \"Glass_Jar\".\n"); return true; } @@ -3766,6 +3766,8 @@ static int parse_reg_t(EngineState *s, const char *str, reg_t *dest, bool mayBeV charsCountObject++; if ((*strLoop >= 'I') && (*strLoop <= 'Z')) charsCountObject++; + if (*strLoop == '_') // underscores are used as substitutes for spaces in object names + charsCountObject++; } strLoop++; } @@ -3838,10 +3840,16 @@ static int parse_reg_t(EngineState *s, const char *str, reg_t *dest, bool mayBeV index = strtol(tmp + 1, &endptr, 16); if (*endptr) return -1; - // Chop of the index + // Chop off the index str_objname = Common::String(str_objname.c_str(), tmp); } + // Replace all underscores in the name with spaces + for (uint i = 0; i < str_objname.size(); i++) { + if (str_objname[i] == '_') + str_objname.setChar(' ', i); + } + // Now all values are available; iterate over all objects. *dest = s->_segMan->findObjectByName(str_objname, index); if (dest->isNull()) diff --git a/engines/sci/decompressor.cpp b/engines/sci/decompressor.cpp index 82af6eca43..306825008d 100644 --- a/engines/sci/decompressor.cpp +++ b/engines/sci/decompressor.cpp @@ -590,6 +590,8 @@ void DecompressorLZW::reorderView(byte *src, byte *dest) { if (celindex < cel_total) { warning("View decompression generated too few (%d / %d) headers", celindex, cel_total); + free(cc_pos); + free(cc_lengths); return; } diff --git a/engines/sci/detection.cpp b/engines/sci/detection.cpp index 58ac5f1fa6..ebad3d039a 100644 --- a/engines/sci/detection.cpp +++ b/engines/sci/detection.cpp @@ -103,6 +103,7 @@ static const PlainGameDescriptor s_sciGameTitles[] = { {"pq4", "Police Quest IV: Open Season"}, // floppy is SCI2, CD SCI2.1 {"qfg4", "Quest for Glory IV: Shadows of Darkness"}, // floppy is SCI2, CD SCI2.1 // === SCI2.1 games ======================================================== + {"chest", "Inside the Chest"}, // aka Behind the Developer's Shield {"gk2", "The Beast Within: A Gabriel Knight Mystery"}, // TODO: Inside The Chest/Behind the Developer's Shield {"kq7", "King's Quest VII: The Princeless Bride"}, @@ -132,6 +133,7 @@ static const GameIdStrToEnum s_gameIdStrToEnum[] = { { "astrochicken", GID_ASTROCHICKEN }, { "camelot", GID_CAMELOT }, { "castlebrain", GID_CASTLEBRAIN }, + { "chest", GID_CHEST }, { "christmas1988", GID_CHRISTMAS1988 }, { "christmas1990", GID_CHRISTMAS1990 }, { "christmas1992", GID_CHRISTMAS1992 }, @@ -208,6 +210,7 @@ struct OldNewIdTableEntry { }; static const OldNewIdTableEntry s_oldNewTable[] = { + { "archive", "chest", SCI_VERSION_NONE }, { "arthur", "camelot", SCI_VERSION_NONE }, { "brain", "castlebrain", SCI_VERSION_1_MIDDLE }, // Amiga { "brain", "castlebrain", SCI_VERSION_1_LATE }, @@ -834,12 +837,16 @@ Common::Error SciEngine::saveGameState(int slot, const Common::String &desc) { return Common::kNoError; } +// Before enabling the load option in the ScummVM menu, the main game loop must +// have run at least once. When the game loop runs, kGameIsRestarting is invoked, +// thus the speed throttler is initialized. Hopefully fixes bug #3565505. + bool SciEngine::canLoadGameStateCurrently() { - return !_gamestate->executionStackBase; + return !_gamestate->executionStackBase && (_gamestate->_throttleLastTime > 0 || _gamestate->_throttleTrigger); } bool SciEngine::canSaveGameStateCurrently() { - return !_gamestate->executionStackBase; + return !_gamestate->executionStackBase && (_gamestate->_throttleLastTime > 0 || _gamestate->_throttleTrigger); } } // End of namespace Sci diff --git a/engines/sci/detection_tables.h b/engines/sci/detection_tables.h index 8a6184c7e4..5640319e3f 100644 --- a/engines/sci/detection_tables.h +++ b/engines/sci/detection_tables.h @@ -129,6 +129,16 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Castle of Dr. Brain - English DOS 5.25" Floppy VGA 1.1 (from rnjacobs, bug report #3578286) + {"castlebrain", "", { + {"resource.map", 0, "a1deac2647ad09472c63656bfb950a4d", 2739}, + {"resource.000", 0, "27ec5fa09cd12a7fd16e86d96a2ed245", 347071}, + {"resource.001", 0, "13e81e1839cd7b216d2bb5615c1ca160", 356812}, + {"resource.002", 0, "583d348c908f89f94f8551d7fe0a2eca", 991752}, + {"resource.003", 0, "6c3d1bb26ad532c94046bc9ac49b5ff4", 728315}, + AD_LISTEND}, + Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Castle of Dr. Brain - English DOS Floppy 1.1 {"castlebrain", "", { {"resource.map", 0, "f77728304c70017c54793eb6ca648174", 2745}, @@ -162,6 +172,16 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::ES_ESP, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, +#ifdef ENABLE_SCI32 + // Inside the Chest / Behind the Developer's Shield + // SCI interpreter version 2.000.000 + {"chest", "", { + {"resource.map", 0, "9dd015e79cac4f91e7de805448f39775", 1912}, + {"resource.000", 0, "e4efcd042f86679dd4e1834bb3a38edb", 3770943}, + AD_LISTEND}, + Common::EN_ANY, Common::kPlatformPC, ADGF_UNSTABLE, GUIO3(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_FB01_MIDI) }, +#endif + // Christmas Card 1988 - English DOS // SCI interpreter version 0.000.294 {"christmas1988", "", { @@ -268,7 +288,7 @@ static const struct ADGameDescription SciGameDescriptions[] = { {"resource.006", 0, "08050329aa113a9f14ed99cbfe3536ec", 232942}, {"resource.007", 0, "64f342463f6f35ba71b3509ef696ae3f", 267702}, AD_LISTEND}, - Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, // Conquests of Camelot - English Amiga (from www.back2roots.org) // Executable scanning reports "1.002.030" @@ -561,6 +581,15 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Freddy Pharkas - French DOS Floppy (supplied by misterhands in bug report #3589449) + // Executable scanning reports "1.cfs.081" + {"freddypharkas", "Floppy", { + {"resource.map", 0, "a32674e7fbf7b213b4a066c8037f16b6", 5816}, + {"resource.000", 0, "fed4808fdb72486908ac7ad0044b14d8", 5233230}, + {"resource.msg", 0, "4dc478f5c73b57e5d690bdfffdcf1c44", 816518}, + AD_LISTEND}, + Common::FR_FRA, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Freddy Pharkas - Windows (supplied by abevi in bug report #2612718) // Executable scanning reports "1.cfs.081" // SCI interpreter version 1.001.132 (just a guess) @@ -1241,10 +1270,8 @@ static const struct ADGameDescription SciGameDescriptions[] = { // King's Quest 5 - English DOS Floppy // VERSION file reports "0.000.051" // Supplied by misterhands in bug report #3536863. - // FIXME: According to bug #3536863, there exists another English version - // with the same file checksums as the vanilla English version, patched to - // Polish. We need a better way of distinguishing the two versions. Until - // we do, this is a duplicate entry of the Polish version below. + // This is the original English version, which has been externally patched to + // Polish in the Polish release below. {"kq5", "", { {"resource.map", 0, "70010c20138541f89013bb5e1b30f16a", 7998}, {"resource.000", 0, "a591bd4b879fc832b8095c0b3befe9e2", 276398}, @@ -1328,6 +1355,21 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // King's Quest 5 DOS Spanish Floppy 0.000.062 VGA (5 x 3.5" disks) + // Supplied by dianiu in bug report #3555646 + {"kq5", "", { + {"resource.map", 0, "c09896a2a30c9b002c5cbbc62f5a5c3a", 8169}, + {"resource.000", 0, "1f1d03aead44da46362ff40c0074a3ec", 335871}, + {"resource.001", 0, "d1803ad904127ae091edb274ee8c047f", 1180637}, + {"resource.002", 0, "d9cd5972016f650cc31fb7c2a2b0953a", 1102207}, + {"resource.003", 0, "829c8caeff793f3cfcea2cb01aaa4150", 965586}, + {"resource.004", 0, "0bd9e570ee04b025e43d3075998fae5b", 1117965}, + {"resource.005", 0, "4aaa2e9a69089b9afbaaccbbf2c4e647", 1202936}, + {"resource.006", 0, "65b520e60c4217e6a6572d9edf77193b", 1141985}, + {"resource.007", 0, "f42b0100f0a1c30806814f8648b6bc28", 1145583}, + AD_LISTEND}, + Common::ES_ESP, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // King's Quest 5 - German DOS Floppy (supplied by markcoolio in bug report #2727101, also includes english language) // SCI interpreter version 1.000.060 {"kq5", "", { @@ -1374,12 +1416,10 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::IT_ITA, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, - // King's Quest 5 - Polish DOS Floppy (supplied by jacek909 in bug report #2725722, includes english language?!) + // King's Quest 5 - Polish DOS Floppy (supplied by jacek909 in bug report #2725722) // SCI interpreter version 1.000.060 - // FIXME: According to bug #3536863, this is actually a patched English version. - // The vanilla English version has the same MD5 checksums. - // We need a better way of detecting this. Until we do, a duplicate English - // entry has been placed above. + // VERSION file reports "0.000.051". + // This is actually an English version with external text resource patches (bug #3536863). {"kq5", "", { {"resource.map", 0, "70010c20138541f89013bb5e1b30f16a", 7998}, {"resource.000", 0, "a591bd4b879fc832b8095c0b3befe9e2", 276398}, @@ -1390,6 +1430,7 @@ static const struct ADGameDescription SciGameDescriptions[] = { {"resource.005", 0, "6556ff8e7c4d1acf6a78aea154daa76c", 1287869}, {"resource.006", 0, "da82e4beb744731d0a151f1d4922fafa", 1170456}, {"resource.007", 0, "431def14ca29cdb5e6a5e84d3f38f679", 1240176}, + {"text.000", 0, "601aa35a3ddeb558e1280e0963e955a2", 1517}, AD_LISTEND}, Common::PL_POL, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, @@ -2684,6 +2725,13 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::DE_DEU, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Police Quest 3 - Spanish DOS v1.000 - Supplied by dianiu in bug report #3555647 + {"pq3", "", { + {"resource.map", 0, "ffa0b4631c4e36d69631256d19ba29e7", 5421}, + {"resource.000", 0, "5ee460af3d70c06a745cc482b6c783ba", 5410263}, + AD_LISTEND}, + Common::ES_ESP, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Police Quest 3 EGA // Reported by musiclyinspired in bug report #3046573 {"pq3", "", { @@ -2801,6 +2849,31 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Quest for Glory 1 / Hero's Quest - English DOS 3.5" Floppy v1.102 Int#0.000.629 (suppled by digitoxin1 in bug report #3554611) + {"qfg1", "", { + {"resource.map", 0, "b162dbd4632250d4d83bed46d0783c10", 6396}, + {"resource.000", 0, "40332d3ebfc70a4b6a6a0443c2763287", 78800}, + {"resource.001", 0, "a270012fa74445d74c044d1b65a9ff8c", 459835}, + {"resource.002", 0, "e64004e020fdf1813be52b639b08be89", 635561}, + {"resource.003", 0, "f0af87c60ec869946da442833aa5afa8", 640502}, + {"resource.004", 0, "f0af87c60ec869946da442833aa5afa8", 644575}, + AD_LISTEND}, + Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + + // Quest for Glory 1 / Hero's Quest - English DOS 5.25" Floppy v1.102 Int#0.000.629 (suppled by digitoxin1 in bug report #3554611) + {"qfg1", "", { + {"resource.map", 0, "5772a2c1bfae46f26582582c9901121e", 6858}, + {"resource.000", 0, "40332d3ebfc70a4b6a6a0443c2763287", 78800}, + {"resource.001", 0, "a270012fa74445d74c044d1b65a9ff8c", 75090}, + {"resource.002", 0, "d22695c53835dfdece056d86f26c251e", 271354}, + {"resource.003", 0, "3cd085e27078f269b3ece5838812ff41", 258084}, + {"resource.004", 0, "8927c7a04a78f1e76f342db3ccc9d879", 267835}, + {"resource.005", 0, "13d16cc9b90b51e2c8643cdf52a62957", 268807}, + {"resource.006", 0, "48b2b3c964dcbeccb68e984e6d4e97db", 278473}, + {"resource.007", 0, "f0af87c60ec869946da442833aa5afa8", 269237}, + AD_LISTEND}, + Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Quest for Glory 1 / Hero's Quest - English DOS 5.25" Floppy (supplied by markcoolio in bug report #2723843) // Executable scanning reports "0.000.566" {"qfg1", "", { @@ -2888,17 +2961,6 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, - // Quest for Glory 1 (from abevi, bug report #2612718) - {"qfg1", "", { - {"resource.map", 0, "b162dbd4632250d4d83bed46d0783c10", 6396}, - {"resource.000", 0, "40332d3ebfc70a4b6a6a0443c2763287", 78800}, - {"resource.001", 0, "a270012fa74445d74c044d1b65a9ff8c", 459835}, - {"resource.002", 0, "e64004e020fdf1813be52b639b08be89", 635561}, - {"resource.003", 0, "f0af87c60ec869946da442833aa5afa8", 640502}, - {"resource.004", 0, "f0af87c60ec869946da442833aa5afa8", 644575}, - AD_LISTEND}, - Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, - // Quest for Glory 1 - English DOS // SCI interpreter version 0.000.629 {"qfg1", "", { @@ -3005,6 +3067,21 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Quest for Glory 2 - English DOS (supplied by digitoxin1 in bug report #3554614) + // v1.102 9x3.5" (label: Int#11.20.90) + {"qfg2", "", { + {"resource.map", 0, "367023314ea33e3156297402f6c1da49", 8166}, + {"resource.000", 0, "a17e374c4d33b81208c862bc0ffc1a38", 212119}, + {"resource.001", 0, "e08d7887e30b12008c40f9570447711a", 331995}, + {"resource.002", 0, "df137dc7869cab07e1149ba2333c815c", 467461}, + {"resource.003", 0, "df137dc7869cab07e1149ba2333c815c", 502560}, + {"resource.004", 0, "df137dc7869cab07e1149ba2333c815c", 488532}, + {"resource.005", 0, "df137dc7869cab07e1149ba2333c815c", 478574}, + {"resource.006", 0, "b1944bd664ddbd2859cdaa0c4a0d6281", 507489}, + {"resource.007", 0, "cd2de58e27665d5853530de93fae7cd6", 490794}, + AD_LISTEND}, + Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Quest for Glory 2 - English DOS Non-Interactive Demo // Executable scanning reports "1.000.046" {"qfg2", "Demo", { @@ -3053,6 +3130,15 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::DE_DEU, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Quest for Glory 3 - French DOS v1.1 (supplied by misterhands in bug report #3586214) + // Executable scanning reports "L.rry.083" + {"qfg3", "", { + {"resource.map", 0, "19e2bf9b693932b5e2bb59b9f9ab86c9", 5958}, + {"resource.000", 0, "6178ad2e83e58e4671ca03315f7a6498", 5868000}, + {"resource.msg", 0, "0fa1047002df904b8d1807bb7bab4fab", 267210}, + AD_LISTEND}, + Common::FR_FRA, Common::kPlatformPC, 0, GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Quest for Glory 3 - Spanish DOS CD (from jvprat) // Executable scanning reports "L.rry.083", VERSION file reports "1.000.000, June 30, 1994" {"qfg3", "", { @@ -3213,7 +3299,7 @@ static const struct ADGameDescription SciGameDescriptions[] = { {"resource.msg", 0, "1aeafe2b495de288d002109650b66614", 1364}, {"resource.000", 0, "8e10d4f05c1fd9f883384fa38a898489", 377394}, AD_LISTEND}, - Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, // Slater & Charlie Go Camping - English DOS/Windows {"slater", "", { @@ -3221,7 +3307,7 @@ static const struct ADGameDescription SciGameDescriptions[] = { {"resource.map", 0, "21f85414124dc23e54544a5536dc35cd", 4044}, {"resource.msg", 0, "c44f51fb955eae266fecf360ebcd5ad2", 1132}, AD_LISTEND}, - Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + Common::EN_ANY, Common::kPlatformPC, 0, GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, // Slater & Charlie Go Camping - English DOS/Windows (Sierra Originals) @@ -3230,7 +3316,7 @@ static const struct ADGameDescription SciGameDescriptions[] = { {"resource.map", 0, "21f85414124dc23e54544a5536dc35cd", 4044}, {"resource.msg", 0, "c44f51fb955eae266fecf360ebcd5ad2", 1132}, AD_LISTEND}, - Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + Common::EN_ANY, Common::kPlatformPC, 0, GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, // Slater & Charlie Go Camping - English Macintosh {"slater", "", { @@ -3560,7 +3646,7 @@ static const struct ADGameDescription SciGameDescriptions[] = { {"resource.map", 0, "ed90a8e3ccc53af6633ff6ab58392bae", 7054}, {"resource.000", 0, "63247e3901ab8963d4eece73747832e0", 5157378}, AD_LISTEND}, - Common::EN_ANY, Common::kPlatformPC, ADGF_CD, GUIO5(GUIO_MIDIGM, GAMEOPTION_SQ4_SILVER_CURSORS, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + Common::EN_ANY, Common::kPlatformPC, ADGF_CD, GUIO4(GAMEOPTION_SQ4_SILVER_CURSORS, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, // Space Quest 4 - English Windows CD (from the Space Quest Collection) // Executable scanning reports "1.001.064", VERSION file reports "1.0" diff --git a/engines/sci/engine/features.cpp b/engines/sci/engine/features.cpp index 22c0a1479d..49e2bfc79f 100644 --- a/engines/sci/engine/features.cpp +++ b/engines/sci/engine/features.cpp @@ -466,6 +466,14 @@ bool GameFeatures::autoDetectSci21KernelType() { // This case doesn't occur in early SCI2.1 games, and we've only // seen it happen in the RAMA demo, thus we can assume that the // game is using a SCI2.1 table + + // HACK: The Inside the Chest Demo doesn't have sounds at all, but + // it's using a SCI2 kernel + if (g_sci->getGameId() == GID_CHEST) { + _sci21KernelType = SCI_VERSION_2; + return true; + } + warning("autoDetectSci21KernelType(): Sound object not loaded, assuming a SCI2.1 table"); _sci21KernelType = SCI_VERSION_2_1; return true; diff --git a/engines/sci/engine/file.cpp b/engines/sci/engine/file.cpp index a0f7ebf4a2..3dc042389e 100644 --- a/engines/sci/engine/file.cpp +++ b/engines/sci/engine/file.cpp @@ -95,7 +95,7 @@ reg_t file_open(EngineState *s, const Common::String &filename, int mode, bool u outFile = saveFileMan->openForSaving(wrappedName, isCompressed); if (!outFile) debugC(kDebugLevelFile, " -> file_open(_K_FILE_MODE_CREATE): failed to create file '%s'", englishName.c_str()); - + // QfG1 opens the character export file with _K_FILE_MODE_CREATE first, // closes it immediately and opens it again with this here. Perhaps // other games use this for read access as well. I guess changing this @@ -387,7 +387,7 @@ uint32 VirtualIndexFile::read(char *buffer, uint32 size) { uint32 VirtualIndexFile::write(const char *buffer, uint32 size) { _changed = true; uint32 curPos = _ptr - _buffer; - + // Check if the buffer needs to be resized if (curPos + size >= _bufferSize) { _bufferSize = curPos + size + 1; diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h index 441ea2624f..e3ebce80fb 100644 --- a/engines/sci/engine/kernel.h +++ b/engines/sci/engine/kernel.h @@ -412,6 +412,7 @@ reg_t kListAt(EngineState *s, int argc, reg_t *argv); reg_t kString(EngineState *s, int argc, reg_t *argv); reg_t kMulDiv(EngineState *s, int argc, reg_t *argv); reg_t kCantBeHere32(EngineState *s, int argc, reg_t *argv); +reg_t kRemapColors32(EngineState *s, int argc, reg_t *argv); // "Screen items" in SCI32 are views reg_t kAddScreenItem(EngineState *s, int argc, reg_t *argv); reg_t kUpdateScreenItem(EngineState *s, int argc, reg_t *argv); @@ -426,18 +427,23 @@ reg_t kUpdatePlane(EngineState *s, int argc, reg_t *argv); reg_t kSetShowStyle(EngineState *s, int argc, reg_t *argv); reg_t kGetHighPlanePri(EngineState *s, int argc, reg_t *argv); reg_t kFrameOut(EngineState *s, int argc, reg_t *argv); + reg_t kIsOnMe(EngineState *s, int argc, reg_t *argv); // kOnMe for SCI2, kIsOnMe for SCI2.1 +reg_t kInPolygon(EngineState *s, int argc, reg_t *argv); +reg_t kObjectIntersect(EngineState *s, int argc, reg_t *argv); + reg_t kListIndexOf(EngineState *s, int argc, reg_t *argv); reg_t kListEachElementDo(EngineState *s, int argc, reg_t *argv); reg_t kListFirstTrue(EngineState *s, int argc, reg_t *argv); reg_t kListAllTrue(EngineState *s, int argc, reg_t *argv); -reg_t kInPolygon(EngineState *s, int argc, reg_t *argv); -reg_t kObjectIntersect(EngineState *s, int argc, reg_t *argv); + reg_t kEditText(EngineState *s, int argc, reg_t *argv); reg_t kMakeSaveCatName(EngineState *s, int argc, reg_t *argv); reg_t kMakeSaveFileName(EngineState *s, int argc, reg_t *argv); reg_t kSetScroll(EngineState *s, int argc, reg_t *argv); reg_t kPalCycle(EngineState *s, int argc, reg_t *argv); +reg_t kPalVaryUnknown(EngineState *s, int argc, reg_t *argv); +reg_t kPalVaryUnknown2(EngineState *s, int argc, reg_t *argv); // SCI2.1 Kernel Functions reg_t kText(EngineState *s, int argc, reg_t *argv); @@ -512,7 +518,6 @@ reg_t kPalVaryDeinit(EngineState *s, int argc, reg_t *argv); reg_t kPalVaryChangeTarget(EngineState *s, int argc, reg_t *argv); reg_t kPalVaryChangeTicks(EngineState *s, int argc, reg_t *argv); reg_t kPalVaryPauseResume(EngineState *s, int argc, reg_t *argv); -reg_t kPalVaryUnknown(EngineState *s, int argc, reg_t *argv); reg_t kPaletteSetFromResource(EngineState *s, int argc, reg_t *argv); reg_t kPaletteSetFlag(EngineState *s, int argc, reg_t *argv); diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h index 825ec90fa9..d7858180f1 100644 --- a/engines/sci/engine/kernel_tables.h +++ b/engines/sci/engine/kernel_tables.h @@ -94,7 +94,7 @@ static const SciKernelMapSubEntry kDoSound_subops[] = { { SIG_SOUNDSCI0, 7, MAP_CALL(DoSoundResumeAfterRestore), "", NULL }, { SIG_SOUNDSCI0, 8, MAP_CALL(DoSoundMasterVolume), "(i)", NULL }, { SIG_SOUNDSCI0, 9, MAP_CALL(DoSoundUpdate), "o", NULL }, - { SIG_SOUNDSCI0, 10, MAP_CALL(DoSoundFade), "o", kDoSoundFade_workarounds }, + { SIG_SOUNDSCI0, 10, MAP_CALL(DoSoundFade), "[o0]", kDoSoundFade_workarounds }, { SIG_SOUNDSCI0, 11, MAP_CALL(DoSoundGetPolyphony), "", NULL }, { SIG_SOUNDSCI0, 12, MAP_CALL(DoSoundStopAll), "", NULL }, { SIG_SOUNDSCI1EARLY, 0, MAP_CALL(DoSoundMasterVolume), NULL, NULL }, @@ -156,7 +156,7 @@ static const SciKernelMapSubEntry kDoSound_subops[] = { // signature for SCI21 should be "o" { SIG_SOUNDSCI21, 9, MAP_CALL(DoSoundStop), NULL, NULL }, { SIG_SOUNDSCI21, 10, MAP_CALL(DoSoundPause), NULL, NULL }, - { SIG_SOUNDSCI21, 11, MAP_CALL(DoSoundFade), NULL, NULL }, + { SIG_SOUNDSCI21, 11, MAP_CALL(DoSoundFade), NULL, kDoSoundFade_workarounds }, { SIG_SOUNDSCI21, 12, MAP_CALL(DoSoundSetHold), NULL, NULL }, { SIG_SOUNDSCI21, 13, MAP_CALL(DoSoundDummy), NULL, NULL }, { SIG_SOUNDSCI21, 14, MAP_CALL(DoSoundSetVolume), NULL, NULL }, @@ -202,7 +202,10 @@ static const SciKernelMapSubEntry kPalVary_subops[] = { { SIG_SCIALL, 4, MAP_CALL(PalVaryChangeTarget), "i", NULL }, { SIG_SCIALL, 5, MAP_CALL(PalVaryChangeTicks), "i", NULL }, { SIG_SCIALL, 6, MAP_CALL(PalVaryPauseResume), "i", NULL }, +#ifdef ENABLE_SCI32 { SIG_SCI32, 8, MAP_CALL(PalVaryUnknown), "i", NULL }, + { SIG_SCI32, 9, MAP_CALL(PalVaryUnknown2), "i", NULL }, +#endif SCI_SUBOPENTRY_TERMINATOR }; @@ -419,7 +422,10 @@ static SciKernelMapEntry s_kernelMap[] = { { MAP_CALL(PriCoord), SIG_EVERYWHERE, "i", NULL, NULL }, { MAP_CALL(Random), SIG_EVERYWHERE, "i(i)(i)", NULL, NULL }, { MAP_CALL(ReadNumber), SIG_EVERYWHERE, "r", NULL, NULL }, - { MAP_CALL(RemapColors), SIG_EVERYWHERE, "i(i)(i)(i)(i)(i)", NULL, NULL }, + { MAP_CALL(RemapColors), SIG_SCI11, SIGFOR_ALL, "i(i)(i)(i)(i)", NULL, NULL }, +#ifdef ENABLE_SCI32 + { "RemapColors", kRemapColors32, SIG_SCI32, SIGFOR_ALL, "i(i)(i)(i)(i)(i)", NULL, NULL }, +#endif { MAP_CALL(ResCheck), SIG_EVERYWHERE, "ii(iiii)", NULL, NULL }, { MAP_CALL(RespondsTo), SIG_EVERYWHERE, ".i", NULL, NULL }, { MAP_CALL(RestartGame), SIG_EVERYWHERE, "", NULL, NULL }, @@ -521,7 +527,7 @@ static SciKernelMapEntry s_kernelMap[] = { { MAP_CALL(PalCycle), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // SCI2 Empty functions - + // Debug function used to track resources { MAP_EMPTY(ResourceTrack), SIG_EVERYWHERE, "(.*)", NULL, NULL }, // Future TODO: This call is used in the floppy version of QFG4 to add diff --git a/engines/sci/engine/kfile.cpp b/engines/sci/engine/kfile.cpp index 786276221c..e977f15c0c 100644 --- a/engines/sci/engine/kfile.cpp +++ b/engines/sci/engine/kfile.cpp @@ -197,8 +197,15 @@ reg_t kCD(EngineState *s, int argc, reg_t *argv) { // TODO: Stub switch (argv[0].toUint16()) { case 0: - // Return whether the contents of disc argv[1] is available. - return TRUE_REG; + if (argc == 1) { + // Check if a disc is in the drive + return TRUE_REG; + } else { + // Check if the specified disc is in the drive + // and return the current disc number. We just + // return the requested disc number. + return argv[1]; + } case 1: // Return the current CD number return make_reg(0, 1); @@ -333,7 +340,7 @@ reg_t kFileIOClose(EngineState *s, int argc, reg_t *argv) { if (argv[0] == SIGNAL_REG) return s->r_acc; - + uint16 handle = argv[0].toUint16(); #ifdef ENABLE_SCI32 @@ -617,7 +624,7 @@ reg_t kFileIOExists(EngineState *s, int argc, reg_t *argv) { // Special case for KQ6 Mac: The game checks for two video files to see // if they exist before it plays them. Since we support multiple naming // schemes for resource fork files, we also need to support that here in - // case someone has a "HalfDome.bin" file, etc. + // case someone has a "HalfDome.bin" file, etc. if (!exists && g_sci->getGameId() == GID_KQ6 && g_sci->getPlatform() == Common::kPlatformMacintosh && (name == "HalfDome" || name == "Kq6Movie")) exists = Common::MacResManager::exists(name); @@ -991,7 +998,7 @@ reg_t kMakeSaveFileName(EngineState *s, int argc, reg_t *argv) { if ((virtualId < SAVEGAMEID_OFFICIALRANGE_START) || (virtualId > SAVEGAMEID_OFFICIALRANGE_END)) error("kMakeSaveFileName: invalid savegame ID specified"); uint saveSlot = virtualId - SAVEGAMEID_OFFICIALRANGE_START; - + Common::Array<SavegameDesc> saves; listSavegames(saves); diff --git a/engines/sci/engine/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp index 6d938b6d22..a65bcc215e 100644 --- a/engines/sci/engine/kgraphics.cpp +++ b/engines/sci/engine/kgraphics.cpp @@ -722,11 +722,6 @@ reg_t kPalVaryPauseResume(EngineState *s, int argc, reg_t *argv) { return NULL_REG; } -reg_t kPalVaryUnknown(EngineState *s, int argc, reg_t *argv) { - // Unknown (seems to be SCI32 exclusive) - return NULL_REG; -} - reg_t kAssertPalette(EngineState *s, int argc, reg_t *argv) { GuiResourceId paletteId = argv[0].toUint16(); @@ -1221,73 +1216,27 @@ reg_t kShow(EngineState *s, int argc, reg_t *argv) { return s->r_acc; } +// Early variant of the SCI32 kRemapColors kernel function, used in the demo of QFG4 reg_t kRemapColors(EngineState *s, int argc, reg_t *argv) { uint16 operation = argv[0].toUint16(); switch (operation) { - case 0: { // Set remapping to base. 0 turns remapping off. - int16 base = (argc >= 2) ? argv[1].toSint16() : 0; - if (base != 0) // 0 is the default behavior when changing rooms in GK1, thus silencing the warning - warning("kRemapColors: Set remapping to base %d", base); - } - break; - case 1: { // unknown - // The demo of QFG4 calls this with 1+3 parameters, thus there are differences here - //int16 unk1 = argv[1].toSint16(); - //int16 unk2 = argv[2].toSint16(); - //int16 unk3 = argv[3].toSint16(); - //uint16 unk4 = argv[4].toUint16(); - //uint16 unk5 = (argc >= 6) ? argv[5].toUint16() : 0; - kStub(s, argc, argv); + case 0: { // remap by percent + uint16 percent = argv[1].toUint16(); + g_sci->_gfxPalette->resetRemapping(); + g_sci->_gfxPalette->setRemappingPercent(254, percent); } break; - case 2: { // remap by percent - // This adjusts the alpha value of a specific color, and it operates on - // an RGBA palette. Since we're operating on an RGB palette, we just - // modify the color intensity instead - // TODO: From what I understand, palette remapping should be placed - // separately, so that it can be reset by case 0 above. Thus, we - // should adjust the functionality of the Palette class accordingly. - int16 color = argv[1].toSint16(); - if (color >= 10) - color -= 10; - uint16 percent = argv[2].toUint16(); // 0 - 100 - if (argc >= 4) - warning("RemapByPercent called with 4 parameters, unknown parameter is %d", argv[3].toUint16()); - warning("kRemapColors: RemapByPercent color %d by %d percent", color, percent); - // TODO: It's not correct to set intensity here - //g_sci->_gfxPalette->kernelSetIntensity(color, 255, percent, false); + case 1: { // remap by range + uint16 from = argv[1].toUint16(); + uint16 to = argv[2].toUint16(); + uint16 base = argv[3].toUint16(); + g_sci->_gfxPalette->resetRemapping(); + g_sci->_gfxPalette->setRemappingRange(254, from, to, base); } break; - case 3: { // remap to gray - // NOTE: This adjusts the alpha value of a specific color, and it operates on - // an RGBA palette - int16 color = argv[1].toSint16(); // this is subtracted from a maximum color value, and can be offset by 10 - int16 percent = argv[2].toSint16(); // 0 - 100 - uint16 unk3 = (argc >= 4) ? argv[3].toUint16() : 0; - warning("kRemapColors: RemapToGray color %d by %d percent (unk3 = %d)", color, percent, unk3); - } - break; - case 4: { // unknown - //int16 unk1 = argv[1].toSint16(); - //uint16 unk2 = argv[2].toUint16(); - //uint16 unk3 = argv[3].toUint16(); - //uint16 unk4 = (argc >= 5) ? argv[4].toUint16() : 0; - kStub(s, argc, argv); - } - break; - case 5: { // set color intensity - // TODO: This isn't right, it should be setting a mapping table instead. - // For PQ4, we can emulate this with kernelSetIntensity(). In QFG4, this - // won't do. - //int16 mapping = argv[1].toSint16(); - uint16 intensity = argv[2].toUint16(); - // HACK for PQ4 - if (g_sci->getGameId() == GID_PQ4) - g_sci->_gfxPalette->kernelSetIntensity(0, 255, intensity, true); - - kStub(s, argc, argv); - } + case 2: // turn remapping off (unused) + error("Unused subop kRemapColors(2) has been called"); break; default: break; diff --git a/engines/sci/engine/kgraphics32.cpp b/engines/sci/engine/kgraphics32.cpp index 16e54a5429..cd735d1233 100644 --- a/engines/sci/engine/kgraphics32.cpp +++ b/engines/sci/engine/kgraphics32.cpp @@ -39,6 +39,7 @@ #include "sci/graphics/cache.h" #include "sci/graphics/compare.h" #include "sci/graphics/controls16.h" +#include "sci/graphics/coordadjuster.h" #include "sci/graphics/cursor.h" #include "sci/graphics/palette.h" #include "sci/graphics/paint16.h" @@ -171,8 +172,13 @@ reg_t kCreateTextBitmap(EngineState *s, int argc, reg_t *argv) { debugC(kDebugLevelStrings, "kCreateTextBitmap case 0 (%04x:%04x, %04x:%04x, %04x:%04x)", PRINT_REG(argv[1]), PRINT_REG(argv[2]), PRINT_REG(argv[3])); debugC(kDebugLevelStrings, "%s", text.c_str()); - uint16 maxWidth = argv[1].toUint16(); // nsRight - nsLeft + 1 - uint16 maxHeight = argv[2].toUint16(); // nsBottom - nsTop + 1 + int16 maxWidth = argv[1].toUint16(); + int16 maxHeight = argv[2].toUint16(); + g_sci->_gfxCoordAdjuster->fromScriptToDisplay(maxHeight, maxWidth); + // These values can be larger than the screen in the SQ6 demo, room 100 + // TODO: Find out why. For now, don't show any text in that room. + if (g_sci->getGameId() == GID_SQ6 && g_sci->isDemo() && s->currentRoomNumber() == 100) + return NULL_REG; return g_sci->_gfxText32->createTextBitmap(object, maxWidth, maxHeight); } case 1: { @@ -317,19 +323,29 @@ reg_t kScrollWindow(EngineState *s, int argc, reg_t *argv) { uint16 op = argv[0].toUint16(); switch (op) { case 0: // Init + // TODO: Init reads the nsLeft, nsTop, nsRight, nsBottom, + // borderColor, fore, back, mode, font, plane selectors + // from the window in argv[1]. g_sci->_gfxFrameout->initScrollText(argv[2].toUint16()); // maxItems g_sci->_gfxFrameout->clearScrollTexts(); return argv[1]; // kWindow case 1: // Show message, called by ScrollableWindow::addString case 14: // Modify message, called by ScrollableWindow::modifyString - // 5 or 6 parameters - // Seems to be called with 5 parameters when the narrator speaks, and - // with 6 when Roger speaks + // TODO: The parameters in Modify are shifted by one: the first + // argument is the handle of the text to modify. The others + // are as Add. { Common::String text = s->_segMan->getString(argv[2]); - uint16 x = 0;//argv[3].toUint16(); // TODO: can't be x (values are all wrong) - uint16 y = 0;//argv[4].toUint16(); // TODO: can't be y (values are all wrong) - // TODO: argv[5] is an optional unknown parameter (an integer set to 0) + uint16 x = 0; + uint16 y = 0; + // TODO: argv[3] is font + // TODO: argv[4] is color + // TODO: argv[5] is alignment (0 = left, 1 = center, 2 = right) + // font,color,alignment may also be -1. (Maybe same as previous?) + // TODO: argv[6] is an optional bool, defaulting to true if not present. + // If true, the old contents are scrolled out of view. + // TODO: Return a handle of the inserted text. (Used for modify/insert) + // This handle looks like it should also be usable by kString. g_sci->_gfxFrameout->addScrollTextEntry(text, kWindow, x, y, (op == 14)); } break; @@ -357,21 +373,27 @@ reg_t kScrollWindow(EngineState *s, int argc, reg_t *argv) { g_sci->_gfxFrameout->lastScrollText(); break; case 9: // Resize, called by ScrollableWindow::resize and ScrollerWindow::resize - // TODO + // TODO: This reads the nsLeft, nsTop, nsRight, nsBottom + // selectors from the SCI object passed in argv[2]. kStub(s, argc, argv); break; case 10: // Where, called by ScrollableWindow::where - // TODO - // argv[2] is an unknown integer - kStub(s, argc, argv); + // TODO: + // Gives the current relative scroll location as a fraction + // with argv[2] as the denominator. (Return value is the numerator.) + // Silenced the warnings because of the high amount of console spam + //kStub(s, argc, argv); break; case 11: // Go, called by ScrollableWindow::scrollTo - // 2 extra parameters here - // TODO + // TODO: + // Two arguments provide a fraction: argv[2] is num., argv[3] is denom. + // Scrolls to the relative location given by the fraction. kStub(s, argc, argv); break; case 12: // Insert, called by ScrollableWindow::insertString - // 3 extra parameters here + // 5 extra parameters here: + // handle of insert location (new string takes that position). + // text, font, color, alignment // TODO kStub(s, argc, argv); break; @@ -661,6 +683,22 @@ reg_t kSetScroll(EngineState *s, int argc, reg_t *argv) { return kStub(s, argc, argv); } +reg_t kPalVaryUnknown(EngineState *s, int argc, reg_t *argv) { + // TODO: Unknown (seems to be SCI32 exclusive) + return kStub(s, argc, argv); +} + +reg_t kPalVaryUnknown2(EngineState *s, int argc, reg_t *argv) { + // TODO: Unknown (seems to be SCI32 exclusive) + // It seems to be related to the day/night palette effects in QFG4, and + // accepts a palette resource ID. It is triggered right when the night + // effect is initially applied (when exiting the caves). + // In QFG4, there are two scene palettes: 790 for night, and 791 for day. + // Initially, the game starts at night time, but this is called with the + // ID of the day time palette (i.e. 791). + return kStub(s, argc, argv); +} + reg_t kPalCycle(EngineState *s, int argc, reg_t *argv) { // Examples: GK1 room 480 (Bayou ritual), LSL6 room 100 (title screen) @@ -727,6 +765,79 @@ reg_t kPalCycle(EngineState *s, int argc, reg_t *argv) { return s->r_acc; } +reg_t kRemapColors32(EngineState *s, int argc, reg_t *argv) { + uint16 operation = argv[0].toUint16(); + + switch (operation) { + case 0: { // turn remapping off + // WORKAROUND: Game scripts in QFG4 erroneously turn remapping off in room + // 140 (the character point allocation screen) and never turn it back on, + // even if it's clearly used in that screen. + if (g_sci->getGameId() == GID_QFG4 && s->currentRoomNumber() == 140) + return s->r_acc; + + int16 base = (argc >= 2) ? argv[1].toSint16() : 0; + if (base > 0) + warning("kRemapColors(0) called with base %d", base); + g_sci->_gfxPalette->resetRemapping(); + } + break; + case 1: { // remap by range + uint16 color = argv[1].toUint16(); + uint16 from = argv[2].toUint16(); + uint16 to = argv[3].toUint16(); + uint16 base = argv[4].toUint16(); + uint16 unk5 = (argc >= 6) ? argv[5].toUint16() : 0; + if (unk5 > 0) + warning("kRemapColors(1) called with 6 parameters, unknown parameter is %d", unk5); + g_sci->_gfxPalette->setRemappingRange(color, from, to, base); + } + break; + case 2: { // remap by percent + uint16 color = argv[1].toUint16(); + uint16 percent = argv[2].toUint16(); // 0 - 100 + if (argc >= 4) + warning("RemapByPercent called with 4 parameters, unknown parameter is %d", argv[3].toUint16()); + g_sci->_gfxPalette->setRemappingPercent(color, percent); + } + break; + case 3: { // remap to gray + // Example call: QFG4 room 490 (Baba Yaga's hut) - params are color 253, 75% and 0. + // In this room, it's used for the cloud before Baba Yaga appears. + int16 color = argv[1].toSint16(); + int16 percent = argv[2].toSint16(); // 0 - 100 + if (argc >= 4) + warning("RemapToGray called with 4 parameters, unknown parameter is %d", argv[3].toUint16()); + g_sci->_gfxPalette->setRemappingPercentGray(color, percent); + } + break; + case 4: { // remap to percent gray + // Example call: QFG4 rooms 530/535 (swamp) - params are 253, 100%, 200 + int16 color = argv[1].toSint16(); + int16 percent = argv[2].toSint16(); // 0 - 100 + // argv[3] is unknown (a number, e.g. 200) - start color, perhaps? + if (argc >= 5) + warning("RemapToGrayPercent called with 5 parameters, unknown parameter is %d", argv[4].toUint16()); + g_sci->_gfxPalette->setRemappingPercentGray(color, percent); + } + break; + case 5: { // don't map to range + //int16 mapping = argv[1].toSint16(); + uint16 intensity = argv[2].toUint16(); + // HACK for PQ4 + if (g_sci->getGameId() == GID_PQ4) + g_sci->_gfxPalette->kernelSetIntensity(0, 255, intensity, true); + + kStub(s, argc, argv); + } + break; + default: + break; + } + + return s->r_acc; +} + #endif } // End of namespace Sci diff --git a/engines/sci/engine/kmath.cpp b/engines/sci/engine/kmath.cpp index a643fbe37a..b2aaa01b45 100644 --- a/engines/sci/engine/kmath.cpp +++ b/engines/sci/engine/kmath.cpp @@ -77,35 +77,7 @@ reg_t kSqrt(EngineState *s, int argc, reg_t *argv) { return make_reg(0, (int16) sqrt((float) ABS(argv[0].toSint16()))); } -/** - * Returns the angle (in degrees) between the two points determined by (x1, y1) - * and (x2, y2). The angle ranges from 0 to 359 degrees. - * What this function does is pretty simple but apparently the original is not - * accurate. - */ -uint16 kGetAngleWorker(int16 x1, int16 y1, int16 x2, int16 y2) { - // TODO: This has been implemented based on behavior observed with a test - // program created with SCI Studio. However, the return values have subtle - // differences from the original, which uses custom implementation of atan(). - // The differences in the return values are the cause of bug #3540976 - // and perhaps bug #3037267 as well. - // The results of this function match the expected results of SCI0, but not - // SCI1 (hence the bug in Longbow). We need to find the point in history - // when this function was changed. - - // HACK: Return the expected value for Longbow, scene 150 (bug #3540976). - // This is a temporary solution, till the function returns the expected - // results. - if (g_sci->getGameId() == GID_LONGBOW && g_sci->getEngineState()->currentRoomNumber() == 150) { - if (x1 == 207 && y1 == 88 && x2 == 107 && y2 == 184) - return 226; - } - -#if 0 - // A simpler atan2-based implementation - return (int16)(360 - atan2((double)(x1 - x2), (double)(y1 - y2)) * 57.2958) % 360; -#endif - +uint16 kGetAngle_SCI0(int16 x1, int16 y1, int16 x2, int16 y2) { int16 xRel = x2 - x1; int16 yRel = y1 - y2; // y-axis is mirrored. int16 angle; @@ -135,6 +107,75 @@ uint16 kGetAngleWorker(int16 x1, int16 y1, int16 x2, int16 y2) { return angle; } +// atan2 for first octant, x >= y >= 0. Returns [0,45] (inclusive) +int kGetAngle_SCI1_atan2_base(int y, int x) { + if (x == 0) + return 0; + + // fixed point tan(a) + int tan_fp = 10000 * y / x; + + if ( tan_fp >= 1000 ) { + // For tan(a) >= 0.1, interpolate between multiples of 5 degrees + + // 10000 * tan([5, 10, 15, 20, 25, 30, 35, 40, 45]) + const int tan_table[] = { 875, 1763, 2679, 3640, 4663, 5774, + 7002, 8391, 10000 }; + + // Look up tan(a) in our table + int i = 1; + while (tan_fp > tan_table[i]) ++i; + + // The angle a is between 5*i and 5*(i+1). We linearly interpolate. + int dist = tan_table[i] - tan_table[i-1]; + int interp = (5 * (tan_fp - tan_table[i-1]) + dist/2) / dist; + return 5*i + interp; + } else { + // for tan(a) < 0.1, tan(a) is approximately linear in a. + // tan'(0) = 1, so in degrees the slope of atan is 180/pi = 57.29... + return (57 * y + x/2) / x; + } +} + +int kGetAngle_SCI1_atan2(int y, int x) { + if (y < 0) { + int a = kGetAngle_SCI1_atan2(-y, -x); + if (a == 180) + return 0; + else + return 180 + a; + } + if (x < 0) + return 90 + kGetAngle_SCI1_atan2(-x, y); + if (y > x) + return 90 - kGetAngle_SCI1_atan2_base(x, y); + else + return kGetAngle_SCI1_atan2_base(y, x); + +} + +uint16 kGetAngle_SCI1(int16 x1, int16 y1, int16 x2, int16 y2) { + // We flip things around to get into the standard atan2 coordinate system + return kGetAngle_SCI1_atan2(x2 - x1, y1 - y2); + +} + +/** + * Returns the angle (in degrees) between the two points determined by (x1, y1) + * and (x2, y2). The angle ranges from 0 to 359 degrees. + * What this function does is pretty simple but apparently the original is not + * accurate. + */ + +uint16 kGetAngleWorker(int16 x1, int16 y1, int16 x2, int16 y2) { + if (getSciVersion() >= SCI_VERSION_1_EGA_ONLY) + return kGetAngle_SCI1(x1, y1, x2, y2); + else + return kGetAngle_SCI0(x1, y1, x2, y2); +} + + + reg_t kGetAngle(EngineState *s, int argc, reg_t *argv) { // Based on behavior observed with a test program created with // SCI Studio. diff --git a/engines/sci/engine/kpathing.cpp b/engines/sci/engine/kpathing.cpp index 002ef1ff07..b839ac51c3 100644 --- a/engines/sci/engine/kpathing.cpp +++ b/engines/sci/engine/kpathing.cpp @@ -31,6 +31,9 @@ #include "common/debug-channels.h" #include "common/list.h" #include "common/system.h" +#include "common/math.h" + +//#define DEBUG_MERGEPOLY namespace Sci { @@ -71,11 +74,25 @@ enum { struct FloatPoint { FloatPoint() : x(0), y(0) {} FloatPoint(float x_, float y_) : x(x_), y(y_) {} + FloatPoint(Common::Point p) : x(p.x), y(p.y) {} Common::Point toPoint() { return Common::Point((int16)(x + 0.5), (int16)(y + 0.5)); } + float operator*(const FloatPoint &p) const { + return x*p.x + y*p.y; + } + FloatPoint operator*(float l) const { + return FloatPoint(l*x, l*y); + } + FloatPoint operator-(const FloatPoint &p) const { + return FloatPoint(x-p.x, y-p.y); + } + float norm() const { + return x*x+y*y; + } + float x, y; }; @@ -135,15 +152,20 @@ public: return _head; } - void insertHead(Vertex *elm) { + void insertAtEnd(Vertex *elm) { if (_head == NULL) { elm->_next = elm->_prev = elm; + _head = elm; } else { elm->_next = _head; elm->_prev = _head->_prev; _head->_prev = elm; elm->_prev->_next = elm; } + } + + void insertHead(Vertex *elm) { + insertAtEnd(elm); _head = elm; } @@ -788,10 +810,10 @@ int PathfindingState::findNearPoint(const Common::Point &p, Polygon *polygon, Co * including the vertices themselves) * Parameters: (const Common::Point &) a, b: The line segment (a, b) * (Vertex *) vertex: The first vertex of the edge - * Returns : (int) FP_OK on success, PF_ERROR otherwise + * Returns : (int) PF_OK on success, PF_ERROR otherwise * (FloatPoint) *ret: The intersection point */ -static int intersection(const Common::Point &a, const Common::Point &b, Vertex *vertex, FloatPoint *ret) { +static int intersection(const Common::Point &a, const Common::Point &b, const Vertex *vertex, FloatPoint *ret) { // Parameters of parametric equations float s, t; // Numerator and denominator of equations @@ -1344,7 +1366,16 @@ static void AStar(PathfindingState *s) { // other, while we apply a penalty to paths traversing it. // This difference might lead to problems, but none are // known at the time of writing. - if (s->pointOnScreenBorder(vertex->v)) + + // WORKAROUND: This check fails in QFG1VGA, room 81 (bug report #3568452). + // However, it is needed in other SCI1.1 games, such as LB2. Therefore, we + // add this workaround for that scene in QFG1VGA, until our algorithm matches + // better what SSCI is doing. With this workaround, QFG1VGA no longer freezes + // in that scene. + bool qfg1VgaWorkaround = (g_sci->getGameId() == GID_QFG1VGA && + g_sci->getEngineState()->currentRoomNumber() == 81); + + if (s->pointOnScreenBorder(vertex->v) && !qfg1VgaWorkaround) new_dist += 10000; if (new_dist < vertex->costG) { @@ -1783,39 +1814,619 @@ reg_t kIntersections(EngineState *s, int argc, reg_t *argv) { } } +// ========================================================================== +// kMergePoly utility functions + +// Compute square of the distance of p to the segment a-b. +static float pointSegDistance(const Common::Point &a, const Common::Point &b, + const Common::Point &p) { + FloatPoint ba(b-a); + FloatPoint pa(p-a); + FloatPoint bp(b-p); + + // Check if the projection of p on the line a-b lies between a and b + if (ba*pa >= 0.0f && ba*bp >= 0.0f) { + // If yes, return the (squared) distance of p to the line a-b: + // translate a to origin, project p and subtract + float linedist = (ba*((ba*pa)/(ba*ba)) - pa).norm(); + + return linedist; + } else { + // If no, return the (squared) distance to either a or b, whichever + // is closest. + + // distance to a: + float adist = pa.norm(); + // distance to b: + float bdist = FloatPoint(p-b).norm(); + + return MIN(adist, bdist); + } +} + +// find intersection between edges of two polygons. +// endpoints count, except v2->_next +static bool segSegIntersect(const Vertex *v1, const Vertex *v2, Common::Point &intp) { + const Common::Point &a = v1->v; + const Common::Point &b = v1->_next->v; + const Common::Point &c = v2->v; + const Common::Point &d = v2->_next->v; + + // First handle the endpoint cases manually + + if (collinear(a, b, c) && collinear(a, b, d)) + return false; + + if (collinear(a, b, c)) { + // a, b, c collinear + // return true/c if c is between a and b + intp = c; + if (a.x != b.x) { + if ((a.x <= c.x && c.x <= b.x) || (b.x <= c.x && c.x <= a.x)) + return true; + } else { + if ((a.y <= c.y && c.y <= b.y) || (b.y <= c.y && c.y <= a.y)) + return true; + } + } + + if (collinear(a, b, d)) { + intp = d; + // a, b, d collinear + // return false/d if d is between a and b + if (a.x != b.x) { + if ((a.x <= d.x && d.x <= b.x) || (b.x <= d.x && d.x <= a.x)) + return false; + } else { + if ((a.y <= d.y && d.y <= b.y) || (b.y <= d.y && d.y <= a.y)) + return false; + } + } + + int len_dc = c.sqrDist(d); + + if (!len_dc) error("zero length edge in polygon"); + + if (pointSegDistance(c, d, a) <= 2.0f) { + intp = a; + return true; + } + + if (pointSegDistance(c, d, b) <= 2.0f) { + intp = b; + return true; + } + + // If not an endpoint, call the generic intersection function + + FloatPoint p; + if (intersection(a, b, v2, &p) == PF_OK) { + intp = p.toPoint(); + return true; + } else { + return false; + } +} + +// For intersecting polygon segments, determine if +// * the v2 edge enters polygon 1 at this intersection: positive return value +// * the v2 edge and the v1 edges are parallel: zero return value +// * the v2 edge exits polygon 1 at this intersection: negative return value +static int intersectDir(const Vertex *v1, const Vertex *v2) { + Common::Point p1 = v1->_next->v - v1->v; + Common::Point p2 = v2->_next->v - v2->v; + return (p1.x*p2.y - p2.x*p1.y); +} + +// Direction of edge in degrees from pos. x-axis, between -180 and 180 +static int edgeDir(const Vertex *v) { + Common::Point p = v->_next->v - v->v; + int deg = (int)Common::rad2deg(atan2((double)p.y, (double)p.x)); + if (deg < -180) deg += 360; + if (deg > 180) deg -= 360; + return deg; +} + +// For points p1, p2 on the polygon segment v, determine if +// * p1 lies before p2: negative return value +// * p1 and p2 are the same: zero return value +// * p1 lies after p2: positive return value +static int liesBefore(const Vertex *v, const Common::Point &p1, const Common::Point &p2) { + return v->v.sqrDist(p1) - v->v.sqrDist(p2); +} + +// Structure describing an "extension" to the work polygon following edges +// of the polygon being merged. + +// The patch begins on the point intersection1, being the intersection +// of the edges starting at indexw1/vertexw1 on the work polygon, and at +// indexp1/vertexp1 on the polygon being merged. +// It ends with the point intersection2, being the analogous intersection. +struct Patch { + unsigned int indexw1; + unsigned int indexp1; + const Vertex *vertexw1; + const Vertex *vertexp1; + Common::Point intersection1; + + unsigned int indexw2; + unsigned int indexp2; + const Vertex *vertexw2; + const Vertex *vertexp2; + Common::Point intersection2; + + bool disabled; // If true, this Patch was made superfluous by another Patch +}; + + +// Check if the given vertex on the work polygon is bypassed by this patch. +static bool isVertexCovered(const Patch &p, unsigned int wi) { + + // / v (outside) + // ---w1--1----p----w2--2---- + // ^ \ (inside) + if (wi > p.indexw1 && wi <= p.indexw2) + return true; + + // v / (outside) + // ---w2--2----p----w1--1---- + // \ ^ (inside) + if (p.indexw1 > p.indexw2 && (wi <= p.indexw2 || wi > p.indexw1)) + return true; + + // v / (outside) + // ---w1--2--1-------p----- + // w2 \ ^ (inside) + if (p.indexw1 == p.indexw2 && liesBefore(p.vertexw1, p.intersection1, p.intersection2) > 0) + return true; // This patch actually covers _all_ vertices on work + + return false; +} + +// Check if patch p1 makes patch p2 superfluous. +static bool isPatchCovered(const Patch &p1, const Patch &p2) { + + // Same exit and entry points + if (p1.intersection1 == p2.intersection1 && p1.intersection2 == p2.intersection2) + return true; + + // / * v (outside) + // ---p1w1--1----p2w1-1---p1w2--2---- + // ^ * \ (inside) + if (p1.indexw1 < p2.indexw1 && p2.indexw1 < p1.indexw2) + return true; + if (p1.indexw1 > p1.indexw2 && (p2.indexw1 > p1.indexw1 || p2.indexw1 < p1.indexw2)) + return true; + + + // / * v (outside) + // ---p1w1--11----p2w2-2---p1w2--12---- + // ^ * \ (inside) + if (p1.indexw1 < p2.indexw2 && p2.indexw2 < p1.indexw2) + return true; + if (p1.indexw1 > p1.indexw2 && (p2.indexw2 > p1.indexw1 || p2.indexw2 < p1.indexw2)) + return true; + + // Opposite of two above situations + if (p2.indexw1 < p1.indexw1 && p1.indexw1 < p2.indexw2) + return false; + if (p2.indexw1 > p2.indexw2 && (p1.indexw1 > p2.indexw1 || p1.indexw1 < p2.indexw2)) + return false; + + if (p2.indexw1 < p1.indexw2 && p1.indexw2 < p2.indexw2) + return false; + if (p2.indexw1 > p2.indexw2 && (p1.indexw2 > p2.indexw1 || p1.indexw2 < p2.indexw2)) + return false; + + + // The above checks covered the cases where one patch covers the other and + // the intersections of the patches are on different edges. + + // So, if we passed the above checks, we have to check the order of + // intersections on edges. + + + if (p1.indexw1 != p1.indexw2) { + + // / * v (outside) + // ---p1w1--11---21--------p1w2--2---- + // p2w1 ^ * \ (inside) + if (p1.indexw1 == p2.indexw1) + return (liesBefore(p1.vertexw1, p1.intersection1, p2.intersection1) < 0); + + // / * v (outside) + // ---p1w1--11---------p1w2--21---12---- + // ^ p2w1 * \ (inside) + if (p1.indexw2 == p2.indexw1) + return (liesBefore(p1.vertexw2, p1.intersection2, p2.intersection1) > 0); + + // If neither of the above, then the intervals of the polygon + // covered by patch1 and patch2 are disjoint + return false; + } + + // p1w1 == p1w2 + // Also, p1w1/p1w2 isn't strictly between p2 + + + // v / * (outside) + // ---p1w1--12--11-------p2w1-21---- + // p1w2 \ ^ * (inside) + + // v / / (outside) + // ---p1w1--12--21--11--------- + // p1w2 \ ^ ^ (inside) + // p2w1 + if (liesBefore(p1.vertexw1, p1.intersection1, p1.intersection2) > 0) + return (p1.indexw1 != p2.indexw1); + + // CHECKME: This is meaningless if p2w1 != p2w2 ?? + if (liesBefore(p2.vertexw1, p2.intersection1, p2.intersection2) > 0) + return false; + + // CHECKME: This is meaningless if p1w1 != p2w1 ?? + if (liesBefore(p2.vertexw1, p2.intersection1, p1.intersection1) <= 0) + return false; + + // CHECKME: This is meaningless if p1w2 != p2w1 ?? + if (liesBefore(p2.vertexw1, p2.intersection1, p1.intersection2) >= 0) + return false; + + return true; +} + +// Merge a single polygon into the work polygon. +// If there is an intersection between work and polygon, this function +// returns true, and replaces the vertex list of work by an extended version, +// that covers polygon. +// +// NOTE: The strategy used matches qfg1new closely, and is a bit error-prone. +// A more robust strategy would be inserting all intersection points directly +// into both vertex lists as a first pass. This would make finding the merged +// polygon a much more straightforward edge-walk, and avoid cases where SSCI's +// algorithm mixes up the order of multiple intersections on a single edge. +bool mergeSinglePolygon(Polygon &work, const Polygon &polygon) { +#ifdef DEBUG_MERGEPOLY + const Vertex *vertex; + debugN("work:"); + CLIST_FOREACH(vertex, &(work.vertices)) { + debugN(" (%d,%d) ", vertex->v.x, vertex->v.y); + } + debugN("\n"); + debugN("poly:"); + CLIST_FOREACH(vertex, &(polygon.vertices)) { + debugN(" (%d,%d) ", vertex->v.x, vertex->v.y); + } + debugN("\n"); +#endif + uint workSize = work.vertices.size(); + uint polygonSize = polygon.vertices.size(); + + int patchCount = 0; + Patch patchList[8]; + + const Vertex *workv = work.vertices._head; + const Vertex *polyv = polygon.vertices._head; + for (uint wi = 0; wi < workSize; ++wi, workv = workv->_next) { + for (uint pi = 0; pi < polygonSize; ++pi, polyv = polyv->_next) { + Common::Point intersection1; + Common::Point intersection2; + + bool intersects = segSegIntersect(workv, polyv, intersection1); + if (!intersects) + continue; + +#ifdef DEBUG_MERGEPOLY + debug("mergePoly: intersection at work %d, poly %d", wi, pi); +#endif + + if (intersectDir(workv, polyv) >= 0) + continue; + +#ifdef DEBUG_MERGEPOLY + debug("mergePoly: intersection in right direction"); +#endif + + int angle = 0; + int baseAngle = edgeDir(workv); + + // We now found the point where an edge of 'polygon' left 'work'. + // Now find the re-entry point. + + // NOTE: The order in which this searches does not always work + // properly if the correct patch would only use a single partial + // edge of poly. Because it starts at polyv->_next, it will skip + // the correct re-entry and proceed to the next. + + const Vertex *workv2; + const Vertex *polyv2 = polyv->_next; + + intersects = false; + + uint pi2, wi2; + for (pi2 = 0; pi2 < polygonSize; ++pi2, polyv2 = polyv2->_next) { + + int newAngle = edgeDir(polyv2); + + int relAngle = newAngle - baseAngle; + if (relAngle > 180) relAngle -= 360; + if (relAngle < -180) relAngle += 360; + + angle += relAngle; + baseAngle = newAngle; + + workv2 = workv; + for (wi2 = 0; wi2 < workSize; ++wi2, workv2 = workv2->_next) { + intersects = segSegIntersect(workv2, polyv2, intersection2); + if (!intersects) + continue; +#ifdef DEBUG_MERGEPOLY + debug("mergePoly: re-entry intersection at work %d, poly %d", (wi + wi2) % workSize, (pi + 1 + pi2) % polygonSize); +#endif + + if (intersectDir(workv2, polyv2) > 0) { +#ifdef DEBUG_MERGEPOLY + debug("mergePoly: re-entry intersection in right direction, angle = %d", angle); +#endif + break; // found re-entry point + } + + } + + if (intersects) + break; + + } + + if (!intersects || angle < 0) + continue; + + + if (patchCount >= 8) + error("kMergePoly: Too many patches"); + + // convert relative to absolute vertex indices + pi2 = (pi + 1 + pi2) % polygonSize; + wi2 = (wi + wi2) % workSize; + + Patch &newPatch = patchList[patchCount]; + newPatch.indexw1 = wi; + newPatch.vertexw1 = workv; + newPatch.indexp1 = pi; + newPatch.vertexp1 = polyv; + newPatch.intersection1 = intersection1; + + newPatch.indexw2 = wi2; + newPatch.vertexw2 = workv2; + newPatch.indexp2 = pi2; + newPatch.vertexp2 = polyv2; + newPatch.intersection2 = intersection2; + newPatch.disabled = false; + +#ifdef DEBUG_MERGEPOLY + debug("mergePoly: adding patch at work %d, poly %d", wi, pi); +#endif + + if (patchCount == 0) { + patchCount++; + continue; + } + + bool necessary = true; + for (int i = 0; i < patchCount; ++i) { + if (isPatchCovered(patchList[i], newPatch)) { + necessary = false; + break; + } + } + + if (!necessary) + continue; + + patchCount++; + + if (patchCount > 1) { + // check if this patch makes other patches superfluous + for (int i = 0; i < patchCount-1; ++i) + if (isPatchCovered(newPatch, patchList[i])) + patchList[i].disabled = true; + } + } + } + + + if (patchCount == 0) + return false; // nothing changed + + + // Determine merged work by doing a walk over the edges + // of work, crossing over to polygon when encountering a patch. + + Polygon output(0); + + workv = work.vertices._head; + for (uint wi = 0; wi < workSize; ++wi, workv = workv->_next) { + + bool covered = false; + for (int p = 0; p < patchCount; ++p) { + if (patchList[p].disabled) continue; + if (isVertexCovered(patchList[p], wi)) { + covered = true; + break; + } + } + + if (!covered) { + // Add vertex to output + output.vertices.insertAtEnd(new Vertex(workv->v)); + } + + + // CHECKME: Why is this the correct order in which to process + // the patches? (What if two of them start on this line segment + // in the opposite order?) + + for (int p = 0; p < patchCount; ++p) { + + const Patch &patch = patchList[p]; + if (patch.disabled) continue; + if (patch.indexw1 != wi) continue; + if (patch.intersection1 != workv->v) { + // Add intersection point to output + output.vertices.insertAtEnd(new Vertex(patch.intersection1)); + } + + // Add vertices from polygon between vertexp1 (excl) and vertexp2 (incl) + for (polyv = patch.vertexp1->_next; polyv != patch.vertexp2; polyv = polyv->_next) + output.vertices.insertAtEnd(new Vertex(polyv->v)); + + output.vertices.insertAtEnd(new Vertex(patch.vertexp2->v)); + + if (patch.intersection2 != patch.vertexp2->v) { + // Add intersection point to output + output.vertices.insertAtEnd(new Vertex(patch.intersection2)); + } + + // TODO: We could continue after the re-entry point here? + } + } + // Remove last vertex if it's the same as the first vertex + if (output.vertices._head->v == output.vertices._head->_prev->v) + output.vertices.remove(output.vertices._head->_prev); + + + // Slight hack: swap vertex lists of output and work polygons. + SWAP(output.vertices._head, work.vertices._head); + + return true; +} + + /** * This is a quite rare kernel function. An example of when it's called * is in QFG1VGA, after killing any monster. + * + * It takes a polygon, and extends it to also cover any polygons from the + * input list with which it intersects. Any of those polygons so covered + * from the input list are marked by adding 0x10 to their type field. */ reg_t kMergePoly(EngineState *s, int argc, reg_t *argv) { -#if 0 // 3 parameters: raw polygon data, polygon list, list size reg_t polygonData = argv[0]; List *list = s->_segMan->lookupList(argv[1]); - Node *node = s->_segMan->lookupNode(list->first); - // List size is not needed - Polygon *polygon; - int count = 0; + // The size of the "work" point list SSCI uses. We use a dynamic one instead + //reg_t listSize = argv[2]; + + SegmentRef pointList = s->_segMan->dereference(polygonData); + if (!pointList.isValid() || pointList.skipByte) { + warning("kMergePoly: Polygon data pointer is invalid"); + return make_reg(0, 0); + } + + Node *node; + +#ifdef DEBUG_MERGEPOLY + node = s->_segMan->lookupNode(list->first); + while (node) { + draw_polygon(s, node->value, 320, 190); + node = s->_segMan->lookupNode(node->succ); + } + Common::Point prev, first; + prev = first = readPoint(pointList, 0); + for (int i = 1; readPoint(pointList, i).x != 0x7777; i++) { + Common::Point point = readPoint(pointList, i); + draw_line(s, prev, point, 1, 320, 190); + prev = point; + } + draw_line(s, prev, first, 1, 320, 190); + // Update the whole screen + g_sci->_gfxScreen->copyToScreen(); + g_system->updateScreen(); + g_system->delayMillis(1000); +#endif + + // The work polygon which we're going to merge with the polygons in list + Polygon work(0); + + for (int i = 0; true; ++i) { + Common::Point p = readPoint(pointList, i); + if (p.x == POLY_LAST_POINT) + break; + Vertex *vertex = new Vertex(p); + work.vertices.insertAtEnd(vertex); + } + + // TODO: Check behaviour for single-vertex polygons + node = s->_segMan->lookupNode(list->first); while (node) { - polygon = convert_polygon(s, node->value); + Polygon *polygon = convert_polygon(s, node->value); if (polygon) { - count += readSelectorValue(s->_segMan, node->value, SELECTOR(size)); + // CHECKME: Confirm vertex order that convert_polygon and + // fix_vertex_order output. For now, we re-reverse the order since + // convert_polygon reads the vertices reversed, and fix up head. + polygon->vertices.reverse(); + polygon->vertices._head = polygon->vertices._head->_next; + + // Merge this polygon into the work polygon if there is an + // intersection. + bool intersected = mergeSinglePolygon(work, *polygon); + + // If so, flag it + if (intersected) { + writeSelectorValue(s->_segMan, node->value, + SELECTOR(type), polygon->type + 0x10); +#ifdef DEBUG_MERGEPOLY + debugN("Merged polygon: "); + // Iterate over edges + Vertex *vertex; + CLIST_FOREACH(vertex, &(work.vertices)) { + debugN(" (%d,%d) ", vertex->v.x, vertex->v.y); + } + debugN("\n"); +#endif + } } node = s->_segMan->lookupNode(node->succ); } -#endif - // TODO: actually merge the polygon. We return an empty polygon for now. - // In QFG1VGA, you can walk over enemy bodies after killing them, since - // this is a stub. - reg_t output = allocateOutputArray(s->_segMan, 1); + + // Allocate output array + reg_t output = allocateOutputArray(s->_segMan, work.vertices.size()+1); SegmentRef arrayRef = s->_segMan->dereference(output); - writePoint(arrayRef, 0, Common::Point(POLY_LAST_POINT, POLY_LAST_POINT)); - warning("Stub: kMergePoly"); + + // Copy work.vertices into arrayRef + Vertex *vertex; + unsigned int n = 0; + CLIST_FOREACH(vertex, &work.vertices) { + if (vertex == work.vertices._head || vertex->v != vertex->_prev->v) + writePoint(arrayRef, n++, vertex->v); + } + + writePoint(arrayRef, n, Common::Point(POLY_LAST_POINT, POLY_LAST_POINT)); + +#ifdef DEBUG_MERGEPOLY + prev = first = readPoint(arrayRef, 0); + for (int i = 1; readPoint(arrayRef, i).x != 0x7777; i++) { + Common::Point point = readPoint(arrayRef, i); + draw_line(s, prev, point, 3, 320, 190); + prev = point; + } + + draw_line(s, prev, first, 3, 320, 190); + + // Update the whole screen + g_sci->_gfxScreen->copyToScreen(); + g_system->updateScreen(); + if (!g_sci->_gfxPaint16) + g_system->delayMillis(1000); + + debug("kMergePoly done"); +#endif + return output; } diff --git a/engines/sci/engine/ksound.cpp b/engines/sci/engine/ksound.cpp index b378b4d58b..0633267db4 100644 --- a/engines/sci/engine/ksound.cpp +++ b/engines/sci/engine/ksound.cpp @@ -140,12 +140,14 @@ reg_t kDoAudio(EngineState *s, int argc, reg_t *argv) { ((argv[3].toUint16() & 0xff) << 16) | ((argv[4].toUint16() & 0xff) << 8) | (argv[5].toUint16() & 0xff); - if (argc == 8) { + // Removed warning because of the high amount of console spam + /*if (argc == 8) { + // TODO: Handle the extra 2 SCI21 params // argv[6] is always 1 // argv[7] is the contents of global 229 (0xE5) warning("kDoAudio: Play called with SCI2.1 extra parameters: %04x:%04x and %04x:%04x", PRINT_REG(argv[6]), PRINT_REG(argv[7])); - } + }*/ } else { warning("kDoAudio: Play called with an unknown number of parameters (%d)", argc); return NULL_REG; @@ -244,6 +246,11 @@ reg_t kDoAudio(EngineState *s, int argc, reg_t *argv) { // Used in Pharkas whenever a speech sample starts (takes no params) //warning("kDoAudio: Unhandled case 13, %d extra arguments passed", argc - 1); break; + case 17: + // Seems to be some sort of audio sync, used in SQ6. Silenced the + // warning due to the high level of spam it produces. (takes no params) + //warning("kDoAudio: Unhandled case 17, %d extra arguments passed", argc - 1); + break; default: warning("kDoAudio: Unhandled case %d, %d extra arguments passed", argv[0].toUint16(), argc - 1); } diff --git a/engines/sci/engine/kstring.cpp b/engines/sci/engine/kstring.cpp index c22d7c7b1e..65e139e1ee 100644 --- a/engines/sci/engine/kstring.cpp +++ b/engines/sci/engine/kstring.cpp @@ -155,29 +155,47 @@ reg_t kReadNumber(EngineState *s, int argc, reg_t *argv) { source++; /* Skip whitespace */ int16 result = 0; + int16 sign = 1; + if (*source == '-') { + sign = -1; + source++; + } if (*source == '$') { // Hexadecimal input - result = (int16)strtol(source + 1, NULL, 16); + source++; + char c; + while ((c = *source++) != 0) { + int16 x = 0; + if ((c >= '0') && (c <= '9')) + x = c - '0'; + else if ((c >= 'a') && (c <= 'f')) + x = c - 'a' + 10; + else if ((c >= 'A') && (c <= 'F')) + x = c - 'A' + 10; + else + // Stop if we encounter anything other than a digit (like atoi) + break; + result *= 16; + result += x; + } } else { // Decimal input. We can not use strtol/atoi in here, because while // Sierra used atoi, it was a non standard compliant atoi, that didn't // do clipping. In SQ4 we get the door code in here and that's even // larger than uint32! - if (*source == '-') { - result = -1; - source++; - } - while (*source) { - if ((*source < '0') || (*source > '9')) + char c; + while ((c = *source++) != 0) { + if ((c < '0') || (c > '9')) // Stop if we encounter anything other than a digit (like atoi) break; result *= 10; - result += *source - 0x30; - source++; + result += c - '0'; } } + result *= sign; + return make_reg(0, result); } @@ -488,6 +506,7 @@ reg_t kGetMessage(EngineState *s, int argc, reg_t *argv) { reg_t kMessage(EngineState *s, int argc, reg_t *argv) { uint func = argv[0].toUint16(); + uint16 module = (argc >= 2) ? argv[1].toUint16() : 0; #ifdef ENABLE_SCI32 if (getSciVersion() >= SCI_VERSION_2) { @@ -517,19 +536,44 @@ reg_t kMessage(EngineState *s, int argc, reg_t *argv) { if (argc >= 6) tuple = MessageTuple(argv[2].toUint16(), argv[3].toUint16(), argv[4].toUint16(), argv[5].toUint16()); + // WORKAROUND for a script bug in Pepper. When using objects together, + // there is code inside script 894 that shows appropriate messages. + // In the case of the jar of cabbage (noun 26), the relevant message + // shown when using any object with it is missing. This leads to the + // script code being triggered, which modifies the jar's noun and + // message selectors, and renders it useless. Thus, when using any + // object with the jar of cabbage, it's effectively corrupted, and + // can't be used on the goat to empty it, therefore the game reaches + // an unsolvable state. It's almost impossible to patch the offending + // script, as it is used in many cases. But we can prevent the + // corruption of the jar here: if the message is found, the offending + // code is never reached and the jar is never corrupted. To do this, + // we substitute all verbs on the cabbage jar with the default verb, + // which shows the "Cannot use this object with the jar" message, and + // never triggers the offending script code that corrupts the object. + // This only affects the jar of cabbage - any other object, including + // the empty jar has a different noun, thus it's unaffected. + // Fixes bug #3601090. + // NOTE: To fix a corrupted jar object, type "send Glass_Jar message 52" + // in the debugger. + if (g_sci->getGameId() == GID_PEPPER && func == 0 && argc >= 6 && module == 894 && + tuple.noun == 26 && tuple.cond == 0 && tuple.seq == 1 && + !s->_msgState->getMessage(module, tuple, NULL_REG)) + tuple.verb = 0; + switch (func) { case K_MESSAGE_GET: - return make_reg(0, s->_msgState->getMessage(argv[1].toUint16(), tuple, (argc == 7 ? argv[6] : NULL_REG))); + return make_reg(0, s->_msgState->getMessage(module, tuple, (argc == 7 ? argv[6] : NULL_REG))); case K_MESSAGE_NEXT: return make_reg(0, s->_msgState->nextMessage((argc == 2 ? argv[1] : NULL_REG))); case K_MESSAGE_SIZE: - return make_reg(0, s->_msgState->messageSize(argv[1].toUint16(), tuple)); + return make_reg(0, s->_msgState->messageSize(module, tuple)); case K_MESSAGE_REFCOND: case K_MESSAGE_REFVERB: case K_MESSAGE_REFNOUN: { MessageTuple t; - if (s->_msgState->messageRef(argv[1].toUint16(), tuple, t)) { + if (s->_msgState->messageRef(module, tuple, t)) { switch (func) { case K_MESSAGE_REFCOND: return make_reg(0, t.cond); @@ -544,9 +588,9 @@ reg_t kMessage(EngineState *s, int argc, reg_t *argv) { } case K_MESSAGE_LASTMESSAGE: { MessageTuple msg; - int module; + int lastModule; - s->_msgState->lastQuery(module, msg); + s->_msgState->lastQuery(lastModule, msg); bool ok = false; @@ -555,7 +599,7 @@ reg_t kMessage(EngineState *s, int argc, reg_t *argv) { if (buffer) { ok = true; - WRITE_LE_UINT16(buffer, module); + WRITE_LE_UINT16(buffer, lastModule); WRITE_LE_UINT16(buffer + 2, msg.noun); WRITE_LE_UINT16(buffer + 4, msg.verb); WRITE_LE_UINT16(buffer + 6, msg.cond); @@ -566,7 +610,7 @@ reg_t kMessage(EngineState *s, int argc, reg_t *argv) { if (buffer) { ok = true; - buffer[0] = make_reg(0, module); + buffer[0] = make_reg(0, lastModule); buffer[1] = make_reg(0, msg.noun); buffer[2] = make_reg(0, msg.verb); buffer[3] = make_reg(0, msg.cond); diff --git a/engines/sci/engine/kvideo.cpp b/engines/sci/engine/kvideo.cpp index cb2a763da9..9b0cb38f51 100644 --- a/engines/sci/engine/kvideo.cpp +++ b/engines/sci/engine/kvideo.cpp @@ -50,6 +50,8 @@ void playVideo(Video::VideoDecoder *videoDecoder, VideoState videoState) { if (!videoDecoder) return; + videoDecoder->start(); + byte *scaleBuffer = 0; byte bytesPerPixel = videoDecoder->getPixelFormat().bytesPerPixel; uint16 width = videoDecoder->getWidth(); @@ -162,9 +164,8 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) { } else { // DOS SEQ // SEQ's are called with no subops, just the string and delay - SeqDecoder *seqDecoder = new SeqDecoder(); - seqDecoder->setFrameDelay(argv[1].toUint16()); // Time between frames in ticks - videoDecoder = seqDecoder; + // Time is specified as ticks + videoDecoder = new SEQDecoder(argv[1].toUint16()); if (!videoDecoder->loadFile(filename)) { warning("Failed to open movie file %s", filename.c_str()); @@ -190,7 +191,7 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) { switch (argv[0].toUint16()) { case 0: { Common::String filename = s->_segMan->getString(argv[1]); - videoDecoder = new Video::AviDecoder(g_system->getMixer()); + videoDecoder = new Video::AVIDecoder(); if (filename.equalsIgnoreCase("gk2a.avi")) { // HACK: Switch to 16bpp graphics for Indeo3. @@ -252,6 +253,7 @@ reg_t kRobot(EngineState *s, int argc, reg_t *argv) { int16 y = argv[5].toUint16(); warning("kRobot(init), id %d, obj %04x:%04x, flag %d, x=%d, y=%d", id, PRINT_REG(obj), flag, x, y); g_sci->_robotDecoder->load(id); + g_sci->_robotDecoder->start(); g_sci->_robotDecoder->setPos(x, y); } break; @@ -267,13 +269,13 @@ reg_t kRobot(EngineState *s, int argc, reg_t *argv) { warning("kRobot(%d)", subop); break; case 8: // sync - //if (false) { // debug: automatically skip all robot videos - if ((uint32)g_sci->_robotDecoder->getCurFrame() != g_sci->_robotDecoder->getFrameCount() - 1) { - writeSelector(s->_segMan, argv[1], SELECTOR(signal), NULL_REG); - } else { + //if (true) { // debug: automatically skip all robot videos + if (g_sci->_robotDecoder->endOfVideo()) { g_sci->_robotDecoder->close(); // Signal the engine scripts that the video is done writeSelector(s->_segMan, argv[1], SELECTOR(signal), SIGNAL_REG); + } else { + writeSelector(s->_segMan, argv[1], SELECTOR(signal), NULL_REG); } break; default: @@ -348,7 +350,7 @@ reg_t kPlayVMD(EngineState *s, int argc, reg_t *argv) { break; } case 6: // Play - videoDecoder = new Video::VMDDecoder(g_system->getMixer()); + videoDecoder = new Video::AdvancedVMDDecoder(); if (s->_videoState.fileName.empty()) { // Happens in Lighthouse @@ -406,7 +408,7 @@ reg_t kPlayDuck(EngineState *s, int argc, reg_t *argv) { s->_videoState.reset(); s->_videoState.fileName = Common::String::format("%d.duk", argv[1].toUint16()); - videoDecoder = new Video::AviDecoder(g_system->getMixer()); + videoDecoder = new Video::AVIDecoder(); if (!videoDecoder->loadFile(s->_videoState.fileName)) { warning("Could not open Duck %s", s->_videoState.fileName.c_str()); diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp index ff3f19b53d..b2d95c599e 100644 --- a/engines/sci/engine/savegame.cpp +++ b/engines/sci/engine/savegame.cpp @@ -696,8 +696,9 @@ void GfxPalette::saveLoadWithSerializer(Common::Serializer &s) { s.syncAsSint32LE(_palVaryPaused); } + _palVarySignal = 0; + if (s.isLoading() && _palVaryResourceId != -1) { - _palVarySignal = 0; palVaryInstallTimer(); } } diff --git a/engines/sci/engine/script.cpp b/engines/sci/engine/script.cpp index 037f4ab700..36d2841b07 100644 --- a/engines/sci/engine/script.cpp +++ b/engines/sci/engine/script.cpp @@ -144,7 +144,7 @@ void Script::load(int script_nr, ResourceManager *resMan) { _heapStart = _buf + _scriptSize; - assert(_bufSize - _scriptSize <= heap->size); + assert(_bufSize - _scriptSize >= heap->size); memcpy(_heapStart, heap->data, heap->size); } diff --git a/engines/sci/engine/script_patches.cpp b/engines/sci/engine/script_patches.cpp index 659c13b13e..c928cf3569 100644 --- a/engines/sci/engine/script_patches.cpp +++ b/engines/sci/engine/script_patches.cpp @@ -848,10 +848,73 @@ const uint16 qfg1vgaPatchFightEvents[] = { PATCH_END }; +// Script 814 of QFG1VGA is responsible for showing dialogs. However, the death +// screen message shown when the hero dies in room 64 (ghost room) is too large +// (254 chars long). Since the window header and main text are both stored in +// temp space, this is an issue, as the scripts read the window header, then the +// window text, which erases the window header text because of its length. To +// fix that, we allocate more temp space and move the pointer used for the +// window header a little bit, wherever it's used in script 814. +// Fixes bug #3568431. + +// Patch 1: Increase temp space +const byte qfg1vgaSignatureTempSpace[] = { + 4, + 0x3f, 0xba, // link 0xba + 0x87, 0x00, // lap 0 + 0 +}; + +const uint16 qfg1vgaPatchTempSpace[] = { + 0x3f, 0xca, // link 0xca + PATCH_END +}; + +// Patch 2: Move the pointer used for the window header a little bit +const byte qfg1vgaSignatureDialogHeader[] = { + 4, + 0x5b, 0x04, 0x80, // lea temp[0x80] + 0x36, // push + 0 +}; + +const uint16 qfg1vgaPatchDialogHeader[] = { + 0x5b, 0x04, 0x90, // lea temp[0x90] + PATCH_END +}; + +// When clicking on the crusher in room 331, Ego approaches him to talk to him, +// an action that is handled by moveToCrusher::changeState in script 331. The +// scripts set Ego to move close to the crusher, but when Ego is running instead +// of walking, the target coordinates specified by script 331 are never reached, +// as Ego is making larger steps, and never reaches the required spot. This is an +// edge case that can occur when Ego is set to run. Normally, when clicking on +// the crusher, ego is supposed to move close to position 79, 165. We change it +// to 85, 165, which is not an edge case thus the freeze is avoided. +// Fixes bug #3585189. +const byte qfg1vgaSignatureMoveToCrusher[] = { + 9, + 0x51, 0x1f, // class Motion + 0x36, // push + 0x39, 0x4f, // pushi 4f (79 - x) + 0x38, 0xa5, 0x00, // pushi 00a5 (165 - y) + 0x7c, // pushSelf + 0 +}; + +const uint16 qfg1vgaPatchMoveToCrusher[] = { + PATCH_ADDTOOFFSET | +3, + 0x39, 0x55, // pushi 55 (85 - x) + PATCH_END +}; + // script, description, magic DWORD, adjust const SciScriptSignature qfg1vgaSignatures[] = { { 215, "fight event issue", 1, PATCH_MAGICDWORD(0x6d, 0x76, 0x51, 0x07), -1, qfg1vgaSignatureFightEvents, qfg1vgaPatchFightEvents }, { 216, "weapon master event issue", 1, PATCH_MAGICDWORD(0x6d, 0x76, 0x51, 0x07), -1, qfg1vgaSignatureFightEvents, qfg1vgaPatchFightEvents }, + { 814, "window text temp space", 1, PATCH_MAGICDWORD(0x3f, 0xba, 0x87, 0x00), 0, qfg1vgaSignatureTempSpace, qfg1vgaPatchTempSpace }, + { 814, "dialog header offset", 3, PATCH_MAGICDWORD(0x5b, 0x04, 0x80, 0x36), 0, qfg1vgaSignatureDialogHeader, qfg1vgaPatchDialogHeader }, + { 331, "moving to crusher", 1, PATCH_MAGICDWORD(0x51, 0x1f, 0x36, 0x39), 0, qfg1vgaSignatureMoveToCrusher, qfg1vgaPatchMoveToCrusher }, SCI_SIGNATUREENTRY_TERMINATOR }; @@ -915,9 +978,53 @@ const uint16 qfg3PatchImportDialog[] = { PATCH_END }; + + +// =========================================================================== +// Patch for the Woo dialog option in Uhura's conversation. Bug #3040722 +// Problem: The Woo dialog option (0xffb5) is negative, and therefore +// treated as an option opening a submenu. This leads to uhuraTell::doChild +// being called, which calls hero::solvePuzzle and then proceeds with +// Teller::doChild to open the submenu. However, there is no actual submenu +// defined for option -75 since -75 does not show up in uhuraTell::keys. +// This will cause Teller::doChild to run out of bounds while scanning through +// uhuraTell::keys. +// Strategy: there is another conversation option in uhuraTell::doChild calling +// hero::solvePuzzle (0xfffc) which does a ret afterwards without going to +// Teller::doChild. We jump to this call of hero::solvePuzzle to get that same +// behaviour. + +const byte qfg3SignatureWooDialog[] = { + 30, + 0x67, 0x12, // pTos 12 (query) + 0x35, 0xb6, // ldi b6 + 0x1a, // eq? + 0x2f, 0x05, // bt 05 + 0x67, 0x12, // pTos 12 (query) + 0x35, 0x9b, // ldi 9b + 0x1a, // eq? + 0x31, 0x0c, // bnt 0c + 0x38, 0x97, 0x02, // pushi 0297 + 0x7a, // push2 + 0x38, 0x0c, 0x01, // pushi 010c + 0x7a, // push2 + 0x81, 0x00, // lag 00 + 0x4a, 0x08, // send 08 + 0x67, 0x12, // pTos 12 (query) + 0x35, 0xb5, // ldi b5 + 0 +}; + +const uint16 qfg3PatchWooDialog[] = { + PATCH_ADDTOOFFSET | +0x29, + 0x33, 0x11, // jmp to 0x6a2, the call to hero::solvePuzzle for 0xFFFC + PATCH_END +}; + // script, description, magic DWORD, adjust const SciScriptSignature qfg3Signatures[] = { { 944, "import dialog continuous calls", 1, PATCH_MAGICDWORD(0x2a, 0x31, 0x0b, 0x7a), -1, qfg3SignatureImportDialog, qfg3PatchImportDialog }, + { 440, "dialog crash when asking about Woo", 1, PATCH_MAGICDWORD(0x67, 0x12, 0x35, 0xb5), -26, qfg3SignatureWooDialog, qfg3PatchWooDialog }, SCI_SIGNATUREENTRY_TERMINATOR }; diff --git a/engines/sci/engine/scriptdebug.cpp b/engines/sci/engine/scriptdebug.cpp index d3abb9ff41..b2f22aa985 100644 --- a/engines/sci/engine/scriptdebug.cpp +++ b/engines/sci/engine/scriptdebug.cpp @@ -514,7 +514,7 @@ void Kernel::dissectScript(int scriptNumber, Vocabulary *vocab) { if (!objType) { debugN("End of script object (#0) encountered.\n"); - debugN("Classes: %i, Objects: %i, Export: %i,\n Var: %i (all base 10)", + debugN("Classes: %i, Objects: %i, Export: %i,\n Var: %i (all base 10)\n", objectctr[6], objectctr[1], objectctr[7], objectctr[10]); return; } @@ -527,7 +527,8 @@ void Kernel::dissectScript(int scriptNumber, Vocabulary *vocab) { _seeker += objsize; - objectctr[objType]++; + if (objType >= 0 && objType < ARRAYSIZE(objectctr)) + objectctr[objType]++; switch (objType) { case SCI_OBJ_OBJECT: diff --git a/engines/sci/engine/seg_manager.cpp b/engines/sci/engine/seg_manager.cpp index 951fc7c363..04c1dab158 100644 --- a/engines/sci/engine/seg_manager.cpp +++ b/engines/sci/engine/seg_manager.cpp @@ -825,7 +825,7 @@ byte *SegManager::allocDynmem(int size, const char *descr, reg_t *addr) { } bool SegManager::freeDynmem(reg_t addr) { - if (addr.getSegment() < 1 || addr.getSegment() >= _heap.size() || + if (addr.getSegment() < 1 || addr.getSegment() >= _heap.size() || !_heap[addr.getSegment()] || _heap[addr.getSegment()]->getType() != SEG_TYPE_DYNMEM) return false; // error diff --git a/engines/sci/engine/vm.cpp b/engines/sci/engine/vm.cpp index 5a2a39def2..ef8f165084 100644 --- a/engines/sci/engine/vm.cpp +++ b/engines/sci/engine/vm.cpp @@ -228,7 +228,7 @@ ExecStack *execute_method(EngineState *s, uint16 script, uint16 pubfunct, StackP uint32 exportAddr = scr->validateExportFunc(pubfunct, false); if (!exportAddr) return NULL; - + // Check if a breakpoint is set on this method g_sci->checkExportBreakpoint(script, pubfunct); @@ -1173,6 +1173,7 @@ void run_vm(EngineState *s) { case op_line: // 0x3f (63) // Debug opcode (line number) + //debug("Script %d, line %d", scr->getScriptNumber(), opparams[0]); break; case op_lag: // 0x40 (64) diff --git a/engines/sci/engine/workarounds.cpp b/engines/sci/engine/workarounds.cpp index 15fca0322c..ff7e601fcb 100644 --- a/engines/sci/engine/workarounds.cpp +++ b/engines/sci/engine/workarounds.cpp @@ -36,13 +36,15 @@ const SciWorkaroundEntry arithmeticWorkarounds[] = { { GID_ECOQUEST2, 100, 0, 0, "Rain", "points", 0xcc6, 0, { WORKAROUND_FAKE, 0 } }, // op_or: when giving the papers to the customs officer, gets called against a pointer instead of a number - bug #3034464 { GID_ECOQUEST2, 100, 0, 0, "Rain", "points", 0xce0, 0, { WORKAROUND_FAKE, 0 } }, // Same as above, for the Spanish version - bug #3313962 { GID_FANMADE, 516, 983, 0, "Wander", "setTarget", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_mul: The Legend of the Lost Jewel Demo (fan made): called with object as second parameter when attacked by insects - bug #3038913 + { GID_GK1, 800,64992, 0, "Fwd", "doit", -1, 0, { WORKAROUND_FAKE, 1 } }, // op_gt: when Mosely finds Gabriel and Grace near the end of the game, compares the Grooper object with 7 { GID_ICEMAN, 199, 977, 0, "Grooper", "doit", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_add: While dancing with the girl { GID_MOTHERGOOSE256, -1, 999, 0, "Event", "new", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_and: constantly during the game (SCI1 version) { GID_MOTHERGOOSE256, -1, 4, 0, "rm004", "doit", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_or: when going north and reaching the castle (rooms 4 and 37) - bug #3038228 { GID_MOTHERGOOSEHIRES,90, 90, 0, "newGameButton", "select", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_ge: MUMG Deluxe, when selecting "New Game" in the main menu. It tries to compare an integer with a list. Needs to return false for the game to continue. + { GID_PHANTASMAGORIA, 902, 0, 0, "", "export 7", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_shr: when starting a chapter in Phantasmagoria { GID_QFG1VGA, 301, 928, 0, "Blink", "init", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_div: when entering the inn, gets called with 1 parameter, but 2nd parameter is used for div which happens to be an object { GID_QFG2, 200, 200, 0, "astro", "messages", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_lsi: when getting asked for your name by the astrologer bug #3039879 - { GID_GK1, 800,64992, 0, "Fwd", "doit", -1, 0, { WORKAROUND_FAKE, 1 } }, // op_gt: when Mosely finds Gabriel and Grace near the end of the game, compares the Grooper object with 7 + { GID_QFG4, 710,64941, 0, "RandCycle", "doit", -1, 0, { WORKAROUND_FAKE, 1 } }, // op_gt: when the tentacle appears in the third room of the caves SCI_WORKAROUNDENTRY_TERMINATOR }; @@ -138,7 +140,7 @@ const SciWorkaroundEntry uninitializedReadWorkarounds[] = { { GID_QFG2, 260, 260, 0, "jabbarS", "changeState",0x2d22, -1, { WORKAROUND_FAKE, 0 } }, // During the thief's first mission (in the house), just before Jabbar is about to enter the house (where you have to hide in the wardrobe), bug #3040469, temps 1 and 2 { GID_QFG2, 500, 500, 0, "lightNextCandleS", "changeState", -1, -1, { WORKAROUND_FAKE, 0 } }, // Inside the last room, while Ad Avis performs the ritual to summon the genie - bug #3148418 { GID_QFG2, -1, 700, 0, NULL, "showSign", -1, 10, { WORKAROUND_FAKE, 0 } }, // Occurs sometimes when reading a sign in Raseir, Shapeir et al - bugs #3272735, #3275413 - { GID_QFG3, 510, 510, 0, "awardPrize", "changeState", -1, 0, { WORKAROUND_FAKE, 0 } }, // Simbani warrior challenge, after throwing the spears and retrieving the ring - bug #3049435 + { GID_QFG3, 510, 510, 0, "awardPrize", "changeState", -1, 0, { WORKAROUND_FAKE, 1 } }, // Simbani warrior challenge, after throwing the spears and retrieving the ring - bug #3049435. Must be non-zero, otherwise the prize is awarded twice - bug #3575570. { GID_QFG3, 140, 140, 0, "rm140", "init", 0x1008, 0, { WORKAROUND_FAKE, 0 } }, // when importing a character and selecting the previous profession - bug #3040460 { GID_QFG3, 330, 330, -1, "Teller", "doChild", -1, -1, { WORKAROUND_FAKE, 0 } }, // when talking to King Rajah about "Rajah" (bug #3036390, temp 1) or "Tarna" (temp 0), or when clicking on yourself and saying "Greet" (bug #3039774, temp 1) { GID_QFG3, 700, 700, -1, "monsterIsDead", "changeState", -1, 0, { WORKAROUND_FAKE, 0 } }, // in the jungle, after winning any fight, bug #3040624 @@ -149,6 +151,8 @@ const SciWorkaroundEntry uninitializedReadWorkarounds[] = { { GID_QFG4, -1, 15, -1, "charInitScreen", "dispatchEvent", -1, 5, { WORKAROUND_FAKE, 0 } }, // floppy version, when viewing the character screen { GID_QFG4, -1, 64917, -1, "controlPlane", "setBitmap", -1, 3, { WORKAROUND_FAKE, 0 } }, // floppy version, when entering the game menu { GID_QFG4, -1, 64917, -1, "Plane", "setBitmap", -1, 3, { WORKAROUND_FAKE, 0 } }, // floppy version, happens sometimes in fight scenes + { GID_QFG4, 520, 64950, 0, "fLake2", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // CD version, at the lake, when meeting the Rusalka and attempting to leave + { GID_QFG4, 800, 64950, 0, "View", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // CD version, in the room with the spider pillar, when climbing on the pillar { GID_RAMA, 12, 64950, -1, "InterfaceFeature", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // Demo, right when it starts { GID_RAMA, 12, 64950, -1, "hiliteOptText", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // Demo, right when it starts { GID_RAMA, 12, 64950, -1, "View", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // Demo, right when it starts @@ -165,7 +169,7 @@ const SciWorkaroundEntry uninitializedReadWorkarounds[] = { { GID_SQ4, -1, 928, -1, "Narrator", "startText", -1, 1000, { WORKAROUND_FAKE, 1 } }, // CD: happens in the options dialog and in-game when speech and subtitles are used simultaneously { GID_SQ5, 201, 201, 0, "buttonPanel", "doVerb", -1, 0, { WORKAROUND_FAKE, 1 } }, // when looking at the orange or red button - bug #3038563 { GID_SQ6, -1, 0, 0, "SQ6", "init", -1, 2, { WORKAROUND_FAKE, 0 } }, // Demo and full version: called when the game starts (demo: room 0, full: room 100) - { GID_SQ6, 100, 64950, 0, "View", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // called when pressing "Start game" in the main menu + { GID_SQ6, -1, 64950, -1, "Feature", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // called when pressing "Start game" in the main menu, when entering the Orion's Belt bar (room 300), and perhaps other places { GID_SQ6, -1, 64964, 0, "DPath", "init", -1, 1, { WORKAROUND_FAKE, 0 } }, // during the game { GID_TORIN, -1, 64017, 0, "oFlags", "clear", -1, 0, { WORKAROUND_FAKE, 0 } }, // entering Torin's home in the French version SCI_WORKAROUNDENTRY_TERMINATOR @@ -241,12 +245,10 @@ const SciWorkaroundEntry kDisposeScript_workarounds[] = { // gameID, room,script,lvl, object-name, method-name, call,index, workaround const SciWorkaroundEntry kDoSoundFade_workarounds[] = { - { GID_CAMELOT, -1, 989, 0, "rmMusic", "fade", -1, 0, { WORKAROUND_IGNORE, 0 } }, // gets called frequently with a NULL reference (i.e. 0:0) - bug #3035149 - { GID_KQ1, -1, 989, 0, "gameSound", "fade", -1, 0, { WORKAROUND_IGNORE, 0 } }, // gets called in several scenes (e.g. graham cracker) with 0:0 - { GID_KQ4, -1, 989, 0, "mySound", "", -1, 0, { WORKAROUND_IGNORE, 0 } }, // gets called in the demo when trying to open the non-existent menu with 0:0 - bug #3036942 { GID_KQ5, 213, 989, 0, "globalSound3", "fade", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // english floppy: when bandits leave the secret temple, parameter 4 is an object - bug #3037594 { GID_KQ6, 105, 989, 0, "globalSound", "fade", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // floppy: during intro, parameter 4 is an object { GID_KQ6, 460, 989, 0, "globalSound2", "fade", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // after pulling the black widow's web on the isle of wonder, parameter 4 is an object - bug #3034567 + { GID_QFG4, -1, 64989, 0, "longSong", "fade", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // CD version: many places, parameter 4 is an object (longSong) SCI_WORKAROUNDENTRY_TERMINATOR }; @@ -397,6 +399,7 @@ const SciWorkaroundEntry kUnLoad_workarounds[] = { { GID_LSL6, 740, 740, 0, "showCartoon", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // during ending, 4 additional parameters are passed by accident { GID_LSL6HIRES, 130, 130, 0, "recruitLarryScr", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // during intro, a 3rd parameter is passed by accident { GID_SQ1, 43, 303, 0, "slotGuy", "dispose", -1, 0, { WORKAROUND_IGNORE, 0 } }, // when leaving ulence flats bar, parameter 1 is not passed - script error + { GID_QFG4, -1, 110, 0, "dreamer", "dispose", -1, 0, { WORKAROUND_IGNORE, 0 } }, // during the dream sequence, a 3rd parameter is passed by accident SCI_WORKAROUNDENTRY_TERMINATOR }; diff --git a/engines/sci/event.cpp b/engines/sci/event.cpp index 14443db1e2..7318fe2f68 100644 --- a/engines/sci/event.cpp +++ b/engines/sci/event.cpp @@ -160,9 +160,15 @@ SciEvent EventManager::getScummVMEvent() { noEvent.mousePos = input.mousePos = mousePos; - if (!found || ev.type == Common::EVENT_MOUSEMOVE) - return noEvent; + if (!found || ev.type == Common::EVENT_MOUSEMOVE) { + int modifiers = em->getModifierState(); + noEvent.modifiers = + ((modifiers & Common::KBD_ALT) ? SCI_KEYMOD_ALT : 0) | + ((modifiers & Common::KBD_CTRL) ? SCI_KEYMOD_CTRL : 0) | + ((modifiers & Common::KBD_SHIFT) ? SCI_KEYMOD_LSHIFT | SCI_KEYMOD_RSHIFT : 0); + return noEvent; + } if (ev.type == Common::EVENT_QUIT) { input.type = SCI_EVENT_QUIT; return input; diff --git a/engines/sci/graphics/animate.h b/engines/sci/graphics/animate.h index 5e2e39ea1a..52da7d6ec6 100644 --- a/engines/sci/graphics/animate.h +++ b/engines/sci/graphics/animate.h @@ -51,7 +51,6 @@ enum ViewScaleSignals { kScaleSignalDoScaling = 0x0001, // enables scaling when drawing that cel (involves scaleX and scaleY) kScaleSignalGlobalScaling = 0x0002, // means that global scaling shall get applied on that cel (sets scaleX/scaleY) kScaleSignalHoyle4SpecialHandling = 0x0004 // HOYLE4-exclusive: special handling inside kAnimate, is used when giving out cards - }; struct AnimateEntry { diff --git a/engines/sci/graphics/controls32.cpp b/engines/sci/graphics/controls32.cpp index ad1d9e8623..5535a7408a 100644 --- a/engines/sci/graphics/controls32.cpp +++ b/engines/sci/graphics/controls32.cpp @@ -68,7 +68,7 @@ void GfxControls32::kernelTexteditChange(reg_t controlObject) { while (captureEvents) { curEvent = g_sci->getEventManager()->getSciEvent(SCI_EVENT_KEYBOARD | SCI_EVENT_PEEK); - + if (curEvent.type == SCI_EVENT_NONE) { eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event } else { @@ -170,11 +170,11 @@ void GfxControls32::kernelTexteditChange(reg_t controlObject) { // Note: the following checkAltInput call might make the text // too wide to fit, but SSCI fails to check that too. } - + reg_t hunkId = readSelector(_segMan, controlObject, SELECTOR(bitmap)); Common::Rect nsRect = g_sci->_gfxCompare->getNSRect(controlObject); //texteditCursorErase(); // TODO: Cursor - + // Write back string _segMan->strcpy(textReference, text.c_str()); // Modify the buffer and show it diff --git a/engines/sci/graphics/font.cpp b/engines/sci/graphics/font.cpp index fcdd057509..30184cc091 100644 --- a/engines/sci/graphics/font.cpp +++ b/engines/sci/graphics/font.cpp @@ -54,7 +54,7 @@ GfxFontFromResource::GfxFontFromResource(ResourceManager *resMan, GfxScreen *scr } GfxFontFromResource::~GfxFontFromResource() { - delete []_chars; + delete[] _chars; _resMan->unlockResource(_resource); } diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp index cb56e24de9..e251bd3dc0 100644 --- a/engines/sci/graphics/frameout.cpp +++ b/engines/sci/graphics/frameout.cpp @@ -28,6 +28,7 @@ #include "common/system.h" #include "common/textconsole.h" #include "engines/engine.h" +#include "graphics/palette.h" #include "graphics/surface.h" #include "sci/sci.h" @@ -125,23 +126,17 @@ void GfxFrameout::kernelAddPlane(reg_t object) { if (_planes.empty()) { // There has to be another way for sierra sci to do this or maybe script resolution is compiled into // interpreter (TODO) - uint16 tmpRunningWidth = readSelectorValue(_segMan, object, SELECTOR(resX)); - uint16 tmpRunningHeight = readSelectorValue(_segMan, object, SELECTOR(resY)); + uint16 scriptWidth = readSelectorValue(_segMan, object, SELECTOR(resX)); + uint16 scriptHeight = readSelectorValue(_segMan, object, SELECTOR(resY)); - // The above can be 0 in SCI3 (e.g. Phantasmagoria 2) - if (tmpRunningWidth == 0 && tmpRunningHeight == 0) { - tmpRunningWidth = 320; - tmpRunningHeight = 200; - } - - // HACK: Phantasmagoria 1 sets a window size of 630x450. - // We can't set a width of 630, as that messes up the pitch, so we hack - // the internal script width here - if (g_sci->getGameId() == GID_PHANTASMAGORIA) { - tmpRunningWidth = 325; + // Phantasmagoria 2 doesn't specify a script width/height + if (g_sci->getGameId() == GID_PHANTASMAGORIA2) { + scriptWidth = 640; + scriptHeight = 480; } - _coordAdjuster->setScriptsResolution(tmpRunningWidth, tmpRunningHeight); + assert(scriptWidth > 0 && scriptHeight > 0); + _coordAdjuster->setScriptsResolution(scriptWidth, scriptHeight); } // Import of QfG character files dialog is shown in QFG4. @@ -159,7 +154,7 @@ void GfxFrameout::kernelAddPlane(reg_t object) { newPlane.object = object; newPlane.priority = readSelectorValue(_segMan, object, SELECTOR(priority)); - newPlane.lastPriority = 0xFFFF; // hidden + newPlane.lastPriority = -1; // hidden newPlane.planeOffsetX = 0; newPlane.planeOffsetY = 0; newPlane.pictureId = kPlanePlainColored; @@ -202,7 +197,7 @@ void GfxFrameout::kernelUpdatePlane(reg_t object) { } else { it->planeOffsetX = 0; } - + if (it->planeRect.top < 0) { it->planeOffsetY = -it->planeRect.top; it->planeRect.top = 0; @@ -354,6 +349,36 @@ void GfxFrameout::deletePlaneLine(reg_t object, reg_t hunkId) { } } +// Adapted from GfxAnimate::applyGlobalScaling() +void GfxFrameout::applyGlobalScaling(FrameoutEntry *itemEntry, Common::Rect planeRect, int16 celHeight) { + // Global scaling uses global var 2 and some other stuff to calculate scaleX/scaleY + int16 maxScale = readSelectorValue(_segMan, itemEntry->object, SELECTOR(maxScale)); + int16 maxCelHeight = (maxScale * celHeight) >> 7; + reg_t globalVar2 = g_sci->getEngineState()->variables[VAR_GLOBAL][2]; // current room object + int16 vanishingY = readSelectorValue(_segMan, globalVar2, SELECTOR(vanishingY)); + + int16 fixedPortY = planeRect.bottom - vanishingY; + int16 fixedEntryY = itemEntry->y - vanishingY; + if (!fixedEntryY) + fixedEntryY = 1; + + if ((celHeight == 0) || (fixedPortY == 0)) + error("global scaling panic"); + + itemEntry->scaleY = (maxCelHeight * fixedEntryY) / fixedPortY; + itemEntry->scaleY = (itemEntry->scaleY * maxScale) / celHeight; + + // Make sure that the calculated value is sane + if (itemEntry->scaleY < 1 /*|| itemEntry->scaleY > 128*/) + itemEntry->scaleY = 128; + + itemEntry->scaleX = itemEntry->scaleY; + + // and set objects scale selectors + //writeSelectorValue(_segMan, itemEntry->object, SELECTOR(scaleX), itemEntry->scaleX); + //writeSelectorValue(_segMan, itemEntry->object, SELECTOR(scaleY), itemEntry->scaleY); +} + void GfxFrameout::kernelAddScreenItem(reg_t object) { // Ignore invalid items if (!_segMan->isObject(object)) { @@ -395,8 +420,15 @@ void GfxFrameout::kernelUpdateScreenItem(reg_t object) { itemEntry->priority = itemEntry->y; itemEntry->signal = readSelectorValue(_segMan, object, SELECTOR(signal)); - itemEntry->scaleX = readSelectorValue(_segMan, object, SELECTOR(scaleX)); - itemEntry->scaleY = readSelectorValue(_segMan, object, SELECTOR(scaleY)); + itemEntry->scaleSignal = readSelectorValue(_segMan, object, SELECTOR(scaleSignal)); + + if (itemEntry->scaleSignal & kScaleSignalDoScaling32) { + itemEntry->scaleX = readSelectorValue(_segMan, object, SELECTOR(scaleX)); + itemEntry->scaleY = readSelectorValue(_segMan, object, SELECTOR(scaleY)); + } else { + itemEntry->scaleX = 128; + itemEntry->scaleY = 128; + } itemEntry->visible = true; // Check if the entry can be hidden @@ -425,7 +457,7 @@ void GfxFrameout::deletePlaneItems(reg_t planeObject) { } else { objectMatches = true; } - + if (objectMatches) { FrameoutEntry *itemEntry = *listIterator; listIterator = _screenItems.erase(listIterator); @@ -465,15 +497,10 @@ bool sortHelper(const FrameoutEntry* entry1, const FrameoutEntry* entry2) { } bool planeSortHelper(const PlaneEntry &entry1, const PlaneEntry &entry2) { -// SegManager *segMan = g_sci->getEngineState()->_segMan; - -// uint16 plane1Priority = readSelectorValue(segMan, entry1, SELECTOR(priority)); -// uint16 plane2Priority = readSelectorValue(segMan, entry2, SELECTOR(priority)); - - if (entry1.priority == 0xffff) + if (entry1.priority < 0) return true; - if (entry2.priority == 0xffff) + if (entry2.priority < 0) return false; return entry1.priority < entry2.priority; @@ -499,7 +526,7 @@ void GfxFrameout::showVideo() { uint16 y = videoDecoder->getPos().y; if (videoDecoder->hasDirtyPalette()) - videoDecoder->setSystemPalette(); + g_system->getPaletteManager()->setPalette(videoDecoder->getPalette(), 0, 256); while (!g_engine->shouldQuit() && !videoDecoder->endOfVideo() && !skipVideo) { if (videoDecoder->needsUpdate()) { @@ -508,7 +535,7 @@ void GfxFrameout::showVideo() { g_system->copyRectToScreen(frame->pixels, frame->pitch, x, y, frame->w, frame->h); if (videoDecoder->hasDirtyPalette()) - videoDecoder->setSystemPalette(); + g_system->getPaletteManager()->setPalette(videoDecoder->getPalette(), 0, 256); g_system->updateScreen(); } @@ -639,13 +666,13 @@ void GfxFrameout::kernelFrameout() { _screen->drawLine(startPoint, endPoint, it2->color, it2->priority, it2->control); } - uint16 planeLastPriority = it->lastPriority; + int16 planeLastPriority = it->lastPriority; // Update priority here, sq6 sets it w/o UpdatePlane - uint16 planePriority = it->priority = readSelectorValue(_segMan, planeObject, SELECTOR(priority)); + int16 planePriority = it->priority = readSelectorValue(_segMan, planeObject, SELECTOR(priority)); it->lastPriority = planePriority; - if (planePriority == 0xffff) { // Plane currently not meant to be shown + if (planePriority < 0) { // Plane currently not meant to be shown // If plane was shown before, delete plane rect if (planePriority != planeLastPriority) _paint32->fillRect(it->planeRect, 0); @@ -660,7 +687,13 @@ void GfxFrameout::kernelFrameout() { _paint32->fillRect(it->planeRect, it->planeBack); _coordAdjuster->pictureSetDisplayArea(it->planeRect); - _palette->drewPicture(it->pictureId); + // Invoking drewPicture() with an invalid picture ID in SCI32 results in + // invalidating the palVary palette when a palVary effect is active. This + // is quite obvious in QFG4, where the day time palette is incorrectly + // shown when exiting the caves, and the correct night time palette + // flashes briefly each time that kPalVaryInit is called. + if (it->pictureId != 0xFFFF) + _palette->drewPicture(it->pictureId); FrameoutList itemList; @@ -671,7 +704,7 @@ void GfxFrameout::kernelFrameout() { if (!itemEntry->visible) continue; - + if (itemEntry->object.isNull()) { // Picture cel data _coordAdjuster->fromScriptToDisplay(itemEntry->y, itemEntry->x); @@ -709,13 +742,21 @@ void GfxFrameout::kernelFrameout() { // TODO: maybe we should clip the cels rect with this, i'm not sure // the only currently known usage is game menu of gk1 } else if (view) { - if ((itemEntry->scaleX == 128) && (itemEntry->scaleY == 128)) - view->getCelRect(itemEntry->loopNo, itemEntry->celNo, - itemEntry->x, itemEntry->y, itemEntry->z, itemEntry->celRect); - else - view->getCelScaledRect(itemEntry->loopNo, itemEntry->celNo, - itemEntry->x, itemEntry->y, itemEntry->z, itemEntry->scaleX, - itemEntry->scaleY, itemEntry->celRect); + // Process global scaling, if needed. + // TODO: Seems like SCI32 always processes global scaling for scaled objects + // TODO: We can only process symmetrical scaling for now (i.e. same value for scaleX/scaleY) + if ((itemEntry->scaleSignal & kScaleSignalDoScaling32) && + !(itemEntry->scaleSignal & kScaleSignalDisableGlobalScaling32) && + (itemEntry->scaleX == itemEntry->scaleY)) + applyGlobalScaling(itemEntry, it->planeRect, view->getHeight(itemEntry->loopNo, itemEntry->celNo)); + + if ((itemEntry->scaleX == 128) && (itemEntry->scaleY == 128)) + view->getCelRect(itemEntry->loopNo, itemEntry->celNo, + itemEntry->x, itemEntry->y, itemEntry->z, itemEntry->celRect); + else + view->getCelScaledRect(itemEntry->loopNo, itemEntry->celNo, + itemEntry->x, itemEntry->y, itemEntry->z, itemEntry->scaleX, + itemEntry->scaleY, itemEntry->celRect); Common::Rect nsRect = itemEntry->celRect; // Translate back to actual coordinate within scrollable plane @@ -736,10 +777,17 @@ void GfxFrameout::kernelFrameout() { continue; } - g_sci->_gfxCompare->setNSRect(itemEntry->object, nsRect); + // FIXME: We should not update the object's NS rect here. + // This breaks the sliders in the control panel screen in + // QFG4, but disabling it does not change any functionality, + // as the object(s) will be drawn on screen with the + // calculated coordinates. + //g_sci->_gfxCompare->setNSRect(itemEntry->object, nsRect); } - // FIXME: When does this happen, and why? + // Don't attempt to draw sprites that are outside the visible + // screen area. An example is the random people walking in + // Jackson Square in GK1. if (itemEntry->celRect.bottom < 0 || itemEntry->celRect.top >= _screen->getDisplayHeight() || itemEntry->celRect.right < 0 || itemEntry->celRect.left >= _screen->getDisplayWidth()) continue; @@ -763,10 +811,10 @@ void GfxFrameout::kernelFrameout() { if (view) { if (!clipRect.isEmpty()) { if ((itemEntry->scaleX == 128) && (itemEntry->scaleY == 128)) - view->draw(itemEntry->celRect, clipRect, translatedClipRect, + view->draw(itemEntry->celRect, clipRect, translatedClipRect, itemEntry->loopNo, itemEntry->celNo, 255, 0, view->isSci2Hires()); else - view->drawScaled(itemEntry->celRect, clipRect, translatedClipRect, + view->drawScaled(itemEntry->celRect, clipRect, translatedClipRect, itemEntry->loopNo, itemEntry->celNo, 255, itemEntry->scaleX, itemEntry->scaleY); } } @@ -825,7 +873,7 @@ void GfxFrameout::printPlaneItemList(Console *con, reg_t planeObject) { for (FrameoutList::iterator listIterator = _screenItems.begin(); listIterator != _screenItems.end(); listIterator++) { FrameoutEntry *e = *listIterator; reg_t itemPlane = readSelector(_segMan, e->object, SELECTOR(plane)); - + if (planeObject == itemPlane) { Common::String curItemName = _segMan->getObjectName(e->object); Common::Rect icr = e->celRect; diff --git a/engines/sci/graphics/frameout.h b/engines/sci/graphics/frameout.h index ecaf450d89..5ef770486f 100644 --- a/engines/sci/graphics/frameout.h +++ b/engines/sci/graphics/frameout.h @@ -40,8 +40,8 @@ typedef Common::List<PlaneLineEntry> PlaneLineList; struct PlaneEntry { reg_t object; - uint16 priority; - uint16 lastPriority; + int16 priority; + int16 lastPriority; int16 planeOffsetX; int16 planeOffsetY; GuiResourceId pictureId; @@ -97,11 +97,18 @@ struct ScrollTextEntry { typedef Common::Array<ScrollTextEntry> ScrollTextList; +enum ViewScaleSignals32 { + kScaleSignalDoScaling32 = 0x0001, // enables scaling when drawing that cel (involves scaleX and scaleY) + kScaleSignalUnk1 = 0x0002, // unknown + kScaleSignalDisableGlobalScaling32 = 0x0004 +}; + class GfxCache; class GfxCoordAdjuster32; class GfxPaint32; class GfxPalette; class GfxScreen; + /** * Frameout class, kFrameout and relevant functions for SCI32 games */ @@ -113,6 +120,7 @@ public: void kernelAddPlane(reg_t object); void kernelUpdatePlane(reg_t object); void kernelDeletePlane(reg_t object); + void applyGlobalScaling(FrameoutEntry *itemEntry, Common::Rect planeRect, int16 celHeight); void kernelAddScreenItem(reg_t object); void kernelUpdateScreenItem(reg_t object); void kernelDeleteScreenItem(reg_t object); diff --git a/engines/sci/graphics/paint16.cpp b/engines/sci/graphics/paint16.cpp index 89f3625e2c..d20aa80c77 100644 --- a/engines/sci/graphics/paint16.cpp +++ b/engines/sci/graphics/paint16.cpp @@ -559,8 +559,8 @@ reg_t GfxPaint16::kernelDisplay(const char *text, int argc, reg_t *argv) { SciTrackOriginReply originReply; SciWorkaroundSolution solution = trackOriginAndFindWorkaround(0, kDisplay_workarounds, &originReply); if (solution.type == WORKAROUND_NONE) - error("Unknown kDisplay argument (%04x:%04x) from method %s::%s (script %d, localCall %x)", - PRINT_REG(displayArg), originReply.objectName.c_str(), originReply.methodName.c_str(), + error("Unknown kDisplay argument (%04x:%04x) from method %s::%s (script %d, localCall %x)", + PRINT_REG(displayArg), originReply.objectName.c_str(), originReply.methodName.c_str(), originReply.scriptNr, originReply.localCallOffset); assert(solution.type == WORKAROUND_IGNORE); break; diff --git a/engines/sci/graphics/palette.cpp b/engines/sci/graphics/palette.cpp index ea154c5037..9b6eff6edc 100644 --- a/engines/sci/graphics/palette.cpp +++ b/engines/sci/graphics/palette.cpp @@ -100,6 +100,9 @@ GfxPalette::GfxPalette(ResourceManager *resMan, GfxScreen *screen) default: error("GfxPalette: Unknown view type"); } + + _remapOn = false; + resetRemapping(); } GfxPalette::~GfxPalette() { @@ -140,8 +143,9 @@ void GfxPalette::createFromData(byte *data, int bytesLeft, Palette *paletteOut) memset(paletteOut, 0, sizeof(Palette)); // Setup 1:1 mapping - for (colorNo = 0; colorNo < 256; colorNo++) + for (colorNo = 0; colorNo < 256; colorNo++) { paletteOut->mapping[colorNo] = colorNo; + } if (bytesLeft < 37) { // This happens when loading palette of picture 0 in sq5 - the resource is broken and doesn't contain a full @@ -329,6 +333,79 @@ void GfxPalette::set(Palette *newPalette, bool force, bool forceRealMerge) { } } +byte GfxPalette::remapColor(byte remappedColor, byte screenColor) { + assert(_remapOn); + if (_remappingType[remappedColor] == kRemappingByRange) + return _remappingByRange[screenColor]; + else if (_remappingType[remappedColor] == kRemappingByPercent) + return _remappingByPercent[screenColor]; + else + error("remapColor(): Color %d isn't remapped", remappedColor); + + return 0; // should never reach here +} + +void GfxPalette::resetRemapping() { + _remapOn = false; + _remappingPercentToSet = 0; + + for (int i = 0; i < 256; i++) { + _remappingType[i] = kRemappingNone; + _remappingByPercent[i] = i; + _remappingByRange[i] = i; + } +} + +void GfxPalette::setRemappingPercent(byte color, byte percent) { + _remapOn = true; + + // We need to defer the setup of the remapping table every time the screen + // palette is changed, so that kernelFindColor() can find the correct + // colors. Set it once here, in case the palette stays the same and update + // it on each palette change by copySysPaletteToScreen(). + _remappingPercentToSet = percent; + + for (int i = 0; i < 256; i++) { + byte r = _sysPalette.colors[i].r * _remappingPercentToSet / 100; + byte g = _sysPalette.colors[i].g * _remappingPercentToSet / 100; + byte b = _sysPalette.colors[i].b * _remappingPercentToSet / 100; + _remappingByPercent[i] = kernelFindColor(r, g, b); + } + + _remappingType[color] = kRemappingByPercent; +} + +void GfxPalette::setRemappingPercentGray(byte color, byte percent) { + _remapOn = true; + + // We need to defer the setup of the remapping table every time the screen + // palette is changed, so that kernelFindColor() can find the correct + // colors. Set it once here, in case the palette stays the same and update + // it on each palette change by copySysPaletteToScreen(). + _remappingPercentToSet = percent; + + // Note: This is not what the original does, but the results are the same visually + for (int i = 0; i < 256; i++) { + byte rComponent = (byte)(_sysPalette.colors[i].r * _remappingPercentToSet * 0.30 / 100); + byte gComponent = (byte)(_sysPalette.colors[i].g * _remappingPercentToSet * 0.59 / 100); + byte bComponent = (byte)(_sysPalette.colors[i].b * _remappingPercentToSet * 0.11 / 100); + byte luminosity = rComponent + gComponent + bComponent; + _remappingByPercent[i] = kernelFindColor(luminosity, luminosity, luminosity); + } + + _remappingType[color] = kRemappingByPercent; +} + +void GfxPalette::setRemappingRange(byte color, byte from, byte to, byte base) { + _remapOn = true; + + for (int i = from; i <= to; i++) { + _remappingByRange[i] = i + base; + } + + _remappingType[color] = kRemappingByRange; +} + bool GfxPalette::insert(Palette *newPalette, Palette *destPalette) { bool paletteChanged = false; @@ -491,6 +568,16 @@ void GfxPalette::copySysPaletteToScreen() { } } + // Check if we need to reset remapping by percent with the new colors. + if (_remappingPercentToSet) { + for (int i = 0; i < 256; i++) { + byte r = _sysPalette.colors[i].r * _remappingPercentToSet / 100; + byte g = _sysPalette.colors[i].g * _remappingPercentToSet / 100; + byte b = _sysPalette.colors[i].b * _remappingPercentToSet / 100; + _remappingByPercent[i] = kernelFindColor(r, g, b); + } + } + g_system->getPaletteManager()->setPalette(bpal, 0, 256); } @@ -635,11 +722,6 @@ void GfxPalette::kernelRestore(reg_t memoryHandle) { } void GfxPalette::kernelAssertPalette(GuiResourceId resourceId) { - // Sometimes invalid viewIds are asked for, ignore those (e.g. qfg1vga) - //if (!_resMan->testResource(ResourceId(kResourceTypeView, resourceId))) - // return; - // maybe we took the wrong parameter before, if this causes invalid view again, enable to commented out code again - GfxView *view = g_sci->_gfxCache->getView(resourceId); Palette *viewPalette = view->getPalette(); if (viewPalette) { @@ -999,8 +1081,9 @@ bool GfxPalette::loadClut(uint16 clutId) { memset(&pal, 0, sizeof(Palette)); // Setup 1:1 mapping - for (int i = 0; i < 256; i++) + for (int i = 0; i < 256; i++) { pal.mapping[i] = i; + } // Now load in the palette for (int i = 1; i <= 236; i++) { diff --git a/engines/sci/graphics/palette.h b/engines/sci/graphics/palette.h index a9ea1c32de..e974781d49 100644 --- a/engines/sci/graphics/palette.h +++ b/engines/sci/graphics/palette.h @@ -31,6 +31,12 @@ namespace Sci { class ResourceManager; class GfxScreen; +enum ColorRemappingType { + kRemappingNone = 0, + kRemappingByRange = 1, + kRemappingByPercent = 2 +}; + /** * Palette class, handles palette operations like changing intensity, setting up the palette, merging different palettes */ @@ -53,6 +59,15 @@ public: void getSys(Palette *pal); uint16 getTotalColorCount() const { return _totalScreenColors; } + void resetRemapping(); + void setRemappingPercent(byte color, byte percent); + void setRemappingPercentGray(byte color, byte percent); + void setRemappingRange(byte color, byte from, byte to, byte base); + bool isRemapped(byte color) const { + return _remapOn && (_remappingType[color] != kRemappingNone); + } + byte remapColor(byte remappedColor, byte screenColor); + void setOnScreen(); void copySysPaletteToScreen(); @@ -123,6 +138,12 @@ private: int _palVarySignal; uint16 _totalScreenColors; + bool _remapOn; + ColorRemappingType _remappingType[256]; + byte _remappingByPercent[256]; + byte _remappingByRange[256]; + uint16 _remappingPercentToSet; + void loadMacIconBarPalette(); byte *_macClut; diff --git a/engines/sci/graphics/ports.cpp b/engines/sci/graphics/ports.cpp index 6b4c8180bf..8acdeed763 100644 --- a/engines/sci/graphics/ports.cpp +++ b/engines/sci/graphics/ports.cpp @@ -380,17 +380,50 @@ Window *GfxPorts::addWindow(const Common::Rect &dims, const Common::Rect *restor int16 oldtop = pwnd->dims.top; int16 oldleft = pwnd->dims.left; - if (wmprect.top > pwnd->dims.top) + // WORKAROUND: We also adjust the restore rect when adjusting the window + // rect. + // SSCI does not do this. It wasn't necessary in the original interpreter, + // but it is needed for Freddy Pharkas CD. This version does not normally + // have text, but we allow this by modifying the text/speech setting + // according to what is set in the ScummVM GUI (refer to syncIngameAudioOptions() + // in sci.cpp). Since the text used in Freddy Pharkas CD is quite large in + // some cases, it ends up being offset in order to fit inside the screen, + // but the associated restore rect isn't adjusted accordingly, leading to + // artifacts being left on screen when some text boxes are removed. The + // fact that the restore rect wasn't ever adjusted doesn't make sense, and + // adjusting it shouldn't have any negative side-effects (it *should* be + // adjusted, normally, but SCI doesn't do it). The big text boxes are still + // odd-looking, because the text rect is drawn outside the text window rect, + // but at least there aren't any leftover textbox artifacts left when the + // boxes are removed. Adjusting the text window rect would require more + // invasive changes than this one, thus it's not really worth the effort + // for a feature that was not present in the original game, and its + // implementation is buggy in the first place. + // Adjusting the restore rect properly fixes bug #3575276. + + if (wmprect.top > pwnd->dims.top) { pwnd->dims.moveTo(pwnd->dims.left, wmprect.top); + if (restoreRect) + pwnd->restoreRect.moveTo(pwnd->restoreRect.left, wmprect.top); + } - if (wmprect.bottom < pwnd->dims.bottom) + if (wmprect.bottom < pwnd->dims.bottom) { pwnd->dims.moveTo(pwnd->dims.left, wmprect.bottom - pwnd->dims.bottom + pwnd->dims.top); + if (restoreRect) + pwnd->restoreRect.moveTo(pwnd->restoreRect.left, wmprect.bottom - pwnd->restoreRect.bottom + pwnd->restoreRect.top); + } - if (wmprect.right < pwnd->dims.right) + if (wmprect.right < pwnd->dims.right) { pwnd->dims.moveTo(wmprect.right + pwnd->dims.left - pwnd->dims.right, pwnd->dims.top); + if (restoreRect) + pwnd->restoreRect.moveTo(wmprect.right + pwnd->restoreRect.left - pwnd->restoreRect.right, pwnd->restoreRect.top); + } - if (wmprect.left > pwnd->dims.left) + if (wmprect.left > pwnd->dims.left) { pwnd->dims.moveTo(wmprect.left, pwnd->dims.top); + if (restoreRect) + pwnd->restoreRect.moveTo(wmprect.left, pwnd->restoreRect.top); + } pwnd->rect.moveTo(pwnd->rect.left + pwnd->dims.left - oldleft, pwnd->rect.top + pwnd->dims.top - oldtop); diff --git a/engines/sci/graphics/screen.cpp b/engines/sci/graphics/screen.cpp index 3030fb4386..246b6bfff9 100644 --- a/engines/sci/graphics/screen.cpp +++ b/engines/sci/graphics/screen.cpp @@ -53,23 +53,35 @@ GfxScreen::GfxScreen(ResourceManager *resMan) : _resMan(resMan) { #ifdef ENABLE_SCI32 // GK1 Mac uses a 640x480 resolution too - if (g_sci->getGameId() == GID_GK1 && g_sci->getPlatform() == Common::kPlatformMacintosh) - _upscaledHires = GFX_SCREEN_UPSCALED_640x480; + if (g_sci->getPlatform() == Common::kPlatformMacintosh) { + if (g_sci->getGameId() == GID_GK1) + _upscaledHires = GFX_SCREEN_UPSCALED_640x480; + } #endif if (_resMan->detectHires()) { _width = 640; + _pitch = 640; _height = 480; } else { _width = 320; + _pitch = 320; _height = getLowResScreenHeight(); } +#ifdef ENABLE_SCI32 + // Phantasmagoria 1 sets a window area of 630x450 + if (g_sci->getGameId() == GID_PHANTASMAGORIA) { + _width = 630; + _height = 450; + } +#endif + // Japanese versions of games use hi-res font on upscaled version of the game. if ((g_sci->getLanguage() == Common::JA_JPN) && (getSciVersion() <= SCI_VERSION_1_1)) _upscaledHires = GFX_SCREEN_UPSCALED_640x400; - _pixels = _width * _height; + _pixels = _pitch * _height; switch (_upscaledHires) { case GFX_SCREEN_UPSCALED_640x400: @@ -91,19 +103,12 @@ GfxScreen::GfxScreen(ResourceManager *resMan) : _resMan(resMan) { _upscaledMapping[i] = (i * 12) / 5; break; default: - _displayWidth = _width; + _displayWidth = _pitch; _displayHeight = _height; memset(&_upscaledMapping, 0, sizeof(_upscaledMapping) ); break; } - // Phantasmagoria 1 sets a window area of 630x450 - if (g_sci->getGameId() == GID_PHANTASMAGORIA) { - // TODO: Also set width to 630 (can't be set right now, as it messes up - // the pitch). For now, a hack has been placed in GfxFrameout::kernelAddPlane() - _height = 450; - } - _displayPixels = _displayWidth * _displayHeight; _visualScreen = (byte *)calloc(_pixels, 1); _priorityScreen = (byte *)calloc(_pixels, 1); @@ -214,7 +219,7 @@ byte GfxScreen::getDrawingMask(byte color, byte prio, byte control) { } void GfxScreen::putPixel(int x, int y, byte drawMask, byte color, byte priority, byte control) { - int offset = y * _width + x; + int offset = y * _pitch + x; if (drawMask & GFX_SCREEN_MASK_VISUAL) { _visualScreen[offset] = color; @@ -247,7 +252,7 @@ void GfxScreen::putFontPixel(int startingY, int x, int y, byte color) { // Do not scale ourselves, but put it on the display directly putPixelOnDisplay(x, y + startingY, color); } else { - int offset = (startingY + y) * _width + x; + int offset = (startingY + y) * _pitch + x; _visualScreen[offset] = color; if (!_upscaledHires) { @@ -349,19 +354,19 @@ void GfxScreen::putKanjiChar(Graphics::FontSJIS *commonFont, int16 x, int16 y, u } byte GfxScreen::getVisual(int x, int y) { - return _visualScreen[y * _width + x]; + return _visualScreen[y * _pitch + x]; } byte GfxScreen::getPriority(int x, int y) { - return _priorityScreen[y * _width + x]; + return _priorityScreen[y * _pitch + x]; } byte GfxScreen::getControl(int x, int y) { - return _controlScreen[y * _width + x]; + return _controlScreen[y * _pitch + x]; } byte GfxScreen::isFillMatch(int16 x, int16 y, byte screenMask, byte t_color, byte t_pri, byte t_con, bool isEGA) { - int offset = y * _width + x; + int offset = y * _pitch + x; byte match = 0; if (screenMask & GFX_SCREEN_MASK_VISUAL) { @@ -422,14 +427,14 @@ void GfxScreen::bitsSave(Common::Rect rect, byte mask, byte *memoryPtr) { memcpy(memoryPtr, (void *)&mask, sizeof(mask)); memoryPtr += sizeof(mask); if (mask & GFX_SCREEN_MASK_VISUAL) { - bitsSaveScreen(rect, _visualScreen, _width, memoryPtr); + bitsSaveScreen(rect, _visualScreen, _pitch, memoryPtr); bitsSaveDisplayScreen(rect, memoryPtr); } if (mask & GFX_SCREEN_MASK_PRIORITY) { - bitsSaveScreen(rect, _priorityScreen, _width, memoryPtr); + bitsSaveScreen(rect, _priorityScreen, _pitch, memoryPtr); } if (mask & GFX_SCREEN_MASK_CONTROL) { - bitsSaveScreen(rect, _controlScreen, _width, memoryPtr); + bitsSaveScreen(rect, _controlScreen, _pitch, memoryPtr); } if (mask & GFX_SCREEN_MASK_DISPLAY) { if (!_upscaledHires) @@ -482,14 +487,14 @@ void GfxScreen::bitsRestore(byte *memoryPtr) { memcpy((void *)&mask, memoryPtr, sizeof(mask)); memoryPtr += sizeof(mask); if (mask & GFX_SCREEN_MASK_VISUAL) { - bitsRestoreScreen(rect, memoryPtr, _visualScreen, _width); + bitsRestoreScreen(rect, memoryPtr, _visualScreen, _pitch); bitsRestoreDisplayScreen(rect, memoryPtr); } if (mask & GFX_SCREEN_MASK_PRIORITY) { - bitsRestoreScreen(rect, memoryPtr, _priorityScreen, _width); + bitsRestoreScreen(rect, memoryPtr, _priorityScreen, _pitch); } if (mask & GFX_SCREEN_MASK_CONTROL) { - bitsRestoreScreen(rect, memoryPtr, _controlScreen, _width); + bitsRestoreScreen(rect, memoryPtr, _controlScreen, _pitch); } if (mask & GFX_SCREEN_MASK_DISPLAY) { if (!_upscaledHires) @@ -567,7 +572,7 @@ void GfxScreen::dither(bool addToFlag) { if (!_unditheringEnabled) { // Do dithering on visual and display-screen for (y = 0; y < _height; y++) { - for (x = 0; x < _width; x++) { + for (x = 0; x < _pitch; x++) { color = *visualPtr; if (color & 0xF0) { color ^= color << 4; @@ -592,7 +597,7 @@ void GfxScreen::dither(bool addToFlag) { memset(&_ditheredPicColors, 0, sizeof(_ditheredPicColors)); // Do dithering on visual screen and put decoded but undithered byte onto display-screen for (y = 0; y < _height; y++) { - for (x = 0; x < _width; x++) { + for (x = 0; x < _pitch; x++) { color = *visualPtr; if (color & 0xF0) { color ^= color << 4; diff --git a/engines/sci/graphics/screen.h b/engines/sci/graphics/screen.h index 73ea596ba1..01fb899edb 100644 --- a/engines/sci/graphics/screen.h +++ b/engines/sci/graphics/screen.h @@ -132,6 +132,7 @@ public: private: uint16 _width; + uint16 _pitch; uint16 _height; uint _pixels; uint16 _displayWidth; diff --git a/engines/sci/graphics/view.cpp b/engines/sci/graphics/view.cpp index 4e5c4da8b2..36aaae9232 100644 --- a/engines/sci/graphics/view.cpp +++ b/engines/sci/graphics/view.cpp @@ -741,8 +741,14 @@ void GfxView::draw(const Common::Rect &rect, const Common::Rect &clipRect, const const int x2 = clipRectTranslated.left + x; const int y2 = clipRectTranslated.top + y; if (!upscaledHires) { - if (priority >= _screen->getPriority(x2, y2)) - _screen->putPixel(x2, y2, drawMask, palette->mapping[color], priority, 0); + if (priority >= _screen->getPriority(x2, y2)) { + if (!_palette->isRemapped(palette->mapping[color])) { + _screen->putPixel(x2, y2, drawMask, palette->mapping[color], priority, 0); + } else { + byte remappedColor = _palette->remapColor(palette->mapping[color], _screen->getVisual(x2, y2)); + _screen->putPixel(x2, y2, drawMask, remappedColor, priority, 0); + } + } } else { // UpscaledHires means view is hires and is supposed to // get drawn onto lowres screen. @@ -851,7 +857,12 @@ void GfxView::drawScaled(const Common::Rect &rect, const Common::Rect &clipRect, const int x2 = clipRectTranslated.left + x; const int y2 = clipRectTranslated.top + y; if (color != clearKey && priority >= _screen->getPriority(x2, y2)) { - _screen->putPixel(x2, y2, drawMask, palette->mapping[color], priority, 0); + if (!_palette->isRemapped(palette->mapping[color])) { + _screen->putPixel(x2, y2, drawMask, palette->mapping[color], priority, 0); + } else { + byte remappedColor = _palette->remapColor(palette->mapping[color], _screen->getVisual(x2, y2)); + _screen->putPixel(x2, y2, drawMask, remappedColor, priority, 0); + } } } } diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp index d43a9d06fc..1f5c354d1f 100644 --- a/engines/sci/sci.cpp +++ b/engines/sci/sci.cpp @@ -632,7 +632,7 @@ void SciEngine::initGraphics() { _gfxPaint = _gfxPaint32; _gfxText32 = new GfxText32(_gamestate->_segMan, _gfxCache, _gfxScreen); _gfxControls32 = new GfxControls32(_gamestate->_segMan, _gfxCache, _gfxScreen, _gfxText32); - _robotDecoder = new RobotDecoder(g_system->getMixer(), getPlatform() == Common::kPlatformMacintosh); + _robotDecoder = new RobotDecoder(getPlatform() == Common::kPlatformMacintosh); _gfxFrameout = new GfxFrameout(_gamestate->_segMan, _resMan, _gfxCoordAdjuster, _gfxCache, _gfxScreen, _gfxPalette, _gfxPaint32); } else { #endif @@ -699,9 +699,11 @@ void SciEngine::runGame() { patchGameSaveRestore(); setLauncherLanguage(); _gamestate->gameIsRestarting = GAMEISRESTARTING_RESTART; + _gamestate->_throttleLastTime = 0; if (_gfxMenu) _gfxMenu->reset(); _gamestate->abortScriptProcessing = kAbortNone; + _gamestate->_syncedAudioOptions = false; } else if (_gamestate->abortScriptProcessing == kAbortLoadGame) { _gamestate->abortScriptProcessing = kAbortNone; _gamestate->_executionStack.clear(); @@ -713,6 +715,7 @@ void SciEngine::runGame() { syncSoundSettings(); syncIngameAudioOptions(); + // Games do not set their audio settings when loading } else { break; // exit loop } @@ -876,6 +879,7 @@ void SciEngine::syncIngameAudioOptions() { if (getGameId() == GID_SQ4 || getGameId() == GID_FREDDYPHARKAS || getGameId() == GID_ECOQUEST + || getGameId() == GID_LSL6 // TODO: The following need script patches for simultaneous speech and subtitles //|| getGameId() == GID_KQ6 //|| getGameId() == GID_LAURABOW2 diff --git a/engines/sci/sci.h b/engines/sci/sci.h index 9f18219cb7..3b9844b326 100644 --- a/engines/sci/sci.h +++ b/engines/sci/sci.h @@ -110,6 +110,7 @@ enum SciGameId { GID_ASTROCHICKEN, GID_CAMELOT, GID_CASTLEBRAIN, + GID_CHEST, GID_CHRISTMAS1988, GID_CHRISTMAS1990, GID_CHRISTMAS1992, @@ -228,6 +229,26 @@ public: bool canLoadGameStateCurrently(); bool canSaveGameStateCurrently(); void syncSoundSettings(); + + /** + * Syncs the audio options of the ScummVM launcher (speech, subtitles or + * both) with the in-game audio options of certain CD game versions. For + * some games, this allows simultaneous playing of speech and subtitles, + * even if the original games didn't support this feature. + * + * SCI1.1 games which support simultaneous speech and subtitles: + * - EcoQuest 1 CD + * - Leisure Suit Larry 6 CD + * SCI1.1 games which don't support simultaneous speech and subtitles, + * and we add this functionality in ScummVM: + * - Space Quest 4 CD + * - Freddy Pharkas CD + * SCI1.1 games which don't support simultaneous speech and subtitles, + * and we haven't added any extra functionality in ScummVM because extra + * script patches are needed: + * - Laura Bow 2 CD + * - King's Quest 6 CD + */ void syncIngameAudioOptions(); const SciGameId &getGameId() const { return _gameId; } diff --git a/engines/sci/sound/audio.cpp b/engines/sci/sound/audio.cpp index 123dd21894..528bb51393 100644 --- a/engines/sci/sound/audio.cpp +++ b/engines/sci/sound/audio.cpp @@ -67,7 +67,8 @@ int AudioPlayer::startAudio(uint16 module, uint32 number) { if (audioStream) { _wPlayFlag = false; - _mixer->playStream(Audio::Mixer::kSpeechSoundType, &_audioHandle, audioStream); + Audio::Mixer::SoundType soundType = (module == 65535) ? Audio::Mixer::kSFXSoundType : Audio::Mixer::kSpeechSoundType; + _mixer->playStream(soundType, &_audioHandle, audioStream); return sampleLen; } else { // Don't throw a warning in this case. getAudioStream() already has. Some games diff --git a/engines/sci/sound/drivers/adlib.cpp b/engines/sci/sound/drivers/adlib.cpp index db9317e071..191e13db0a 100644 --- a/engines/sci/sound/drivers/adlib.cpp +++ b/engines/sci/sound/drivers/adlib.cpp @@ -807,6 +807,9 @@ int MidiPlayer_AdLib::open(ResourceManager *resMan) { int size = f.size(); const uint patchSize = 1344; + // Note: Funseeker's Guide also has another version of adl.drv, 8803 bytes. + // This isn't supported, but it's not really used anywhere, as that demo + // doesn't have sound anyway. if ((size == 5684) || (size == 5720) || (size == 5727)) { byte *buf = new byte[patchSize]; diff --git a/engines/sci/sound/drivers/fmtowns.cpp b/engines/sci/sound/drivers/fmtowns.cpp index 6d8bb2e525..21cb2f1e43 100644 --- a/engines/sci/sound/drivers/fmtowns.cpp +++ b/engines/sci/sound/drivers/fmtowns.cpp @@ -52,8 +52,8 @@ public: uint16 _duration; private: - uint8 _id; - uint8 _velo; + uint8 _id; + uint8 _velo; uint8 _program; MidiDriver_FMTowns *_drv; @@ -76,7 +76,7 @@ public: void addChannels(int num); void dropChannels(int num); - + uint8 currentProgram() const; private: @@ -132,7 +132,7 @@ private: TownsMidiPart **_parts; TownsChannel **_out; - + uint8 _masterVolume; bool _soundOn; @@ -590,7 +590,7 @@ void MidiDriver_FMTowns::addMissingChannels() { avlChan -= _parts[i]->_chanMissing; uint8 m = _parts[i]->_chanMissing; _parts[i]->_chanMissing = 0; - _parts[i]->addChannels(m); + _parts[i]->addChannels(m); } else { _parts[i]->_chanMissing -= avlChan; _parts[i]->addChannels(avlChan); @@ -601,7 +601,7 @@ void MidiDriver_FMTowns::addMissingChannels() { void MidiDriver_FMTowns::updateParser() { if (_timerProc) - _timerProc(_timerProcPara); + _timerProc(_timerProcPara); } void MidiDriver_FMTowns::updateChannels() { diff --git a/engines/sci/sound/midiparser_sci.cpp b/engines/sci/sound/midiparser_sci.cpp index 422948f975..4e54797960 100644 --- a/engines/sci/sound/midiparser_sci.cpp +++ b/engines/sci/sound/midiparser_sci.cpp @@ -98,7 +98,7 @@ bool MidiParser_SCI::loadMusic(SoundResource::Track *track, MusicEntry *psnd, in midiMixChannels(); } - _num_tracks = 1; + _numTracks = 1; _tracks[0] = _mixedData; if (_pSnd) setTrack(0); @@ -144,7 +144,7 @@ byte *MidiParser_SCI::midiMixChannels() { _mixedData = outData; long ticker = 0; byte channelNr, curDelta; - byte midiCommand = 0, midiParam, global_prev = 0; + byte midiCommand = 0, midiParam, globalPrev = 0; long newDelta; SoundResource::Channel *channel; @@ -190,13 +190,13 @@ byte *MidiParser_SCI::midiMixChannels() { byte midiChannel = midiCommand & 0xF; _channelUsed[midiChannel] = true; - if (midiCommand != global_prev) + if (midiCommand != globalPrev) *outData++ = midiCommand; *outData++ = midiParam; if (nMidiParams[(midiCommand >> 4) - 8] == 2) *outData++ = channel->data[channel->curPos++]; channel->prev = midiCommand; - global_prev = midiCommand; + globalPrev = midiCommand; } } @@ -372,8 +372,8 @@ void MidiParser_SCI::unloadMusic() { resetTracking(); allNotesOff(); } - _num_tracks = 0; - _active_track = 255; + _numTracks = 0; + _activeTrack = 255; _resetOnPause = false; if (_mixedData) { @@ -454,26 +454,26 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) { debugC(4, kDebugLevelSound, "signal %04x", _signalToSet); } - info.start = _position._play_pos; + info.start = _position._playPos; info.delta = 0; - while (*_position._play_pos == 0xF8) { + while (*_position._playPos == 0xF8) { info.delta += 240; - _position._play_pos++; + _position._playPos++; } - info.delta += *(_position._play_pos++); + info.delta += *(_position._playPos++); // Process the next info. - if ((_position._play_pos[0] & 0xF0) >= 0x80) - info.event = *(_position._play_pos++); + if ((_position._playPos[0] & 0xF0) >= 0x80) + info.event = *(_position._playPos++); else - info.event = _position._running_status; + info.event = _position._runningStatus; if (info.event < 0x80) return; - _position._running_status = info.event; + _position._runningStatus = info.event; switch (info.command()) { case 0xC: - info.basic.param1 = *(_position._play_pos++); + info.basic.param1 = *(_position._playPos++); info.basic.param2 = 0; if (info.channel() == 0xF) {// SCI special case if (info.basic.param1 != kSetSignalLoop) { @@ -488,23 +488,23 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) { // in glitches (e.g. the intro of LB1 Amiga gets stuck - bug // #3297883). Refer to MusicEntry::setSignal() in sound/music.cpp. if (_soundVersion <= SCI_VERSION_0_LATE || - _position._play_tick || info.delta) { + _position._playTick || info.delta) { _signalSet = true; _signalToSet = info.basic.param1; } } else { - _loopTick = _position._play_tick + info.delta; + _loopTick = _position._playTick + info.delta; } } break; case 0xD: - info.basic.param1 = *(_position._play_pos++); + info.basic.param1 = *(_position._playPos++); info.basic.param2 = 0; break; case 0xB: - info.basic.param1 = *(_position._play_pos++); - info.basic.param2 = *(_position._play_pos++); + info.basic.param1 = *(_position._playPos++); + info.basic.param2 = *(_position._playPos++); // Reference for some events: // http://wiki.scummvm.org/index.php/SCI/Specifications/Sound/SCI0_Resource_Format#Status_Reference @@ -588,8 +588,8 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) { case 0x9: case 0xA: case 0xE: - info.basic.param1 = *(_position._play_pos++); - info.basic.param2 = *(_position._play_pos++); + info.basic.param1 = *(_position._playPos++); + info.basic.param2 = *(_position._playPos++); if (info.command() == 0x9 && info.basic.param2 == 0) info.event = info.channel() | 0x80; info.length = 0; @@ -598,12 +598,12 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) { case 0xF: // System Common, Meta or SysEx event switch (info.event & 0x0F) { case 0x2: // Song Position Pointer - info.basic.param1 = *(_position._play_pos++); - info.basic.param2 = *(_position._play_pos++); + info.basic.param1 = *(_position._playPos++); + info.basic.param2 = *(_position._playPos++); break; case 0x3: // Song Select - info.basic.param1 = *(_position._play_pos++); + info.basic.param1 = *(_position._playPos++); info.basic.param2 = 0; break; @@ -617,16 +617,16 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) { break; case 0x0: // SysEx - info.length = readVLQ(_position._play_pos); - info.ext.data = _position._play_pos; - _position._play_pos += info.length; + info.length = readVLQ(_position._playPos); + info.ext.data = _position._playPos; + _position._playPos += info.length; break; case 0xF: // META event - info.ext.type = *(_position._play_pos++); - info.length = readVLQ(_position._play_pos); - info.ext.data = _position._play_pos; - _position._play_pos += info.length; + info.ext.type = *(_position._playPos++); + info.length = readVLQ(_position._playPos); + info.ext.data = _position._playPos; + _position._playPos += info.length; if (info.ext.type == 0x2F) {// end of track reached if (_pSnd->loop) _pSnd->loop--; @@ -677,21 +677,21 @@ void MidiParser_SCI::allNotesOff() { // Turn off all active notes for (i = 0; i < 128; ++i) { for (j = 0; j < 16; ++j) { - if ((_active_notes[i] & (1 << j)) && (_channelRemap[j] != -1)){ + if ((_activeNotes[i] & (1 << j)) && (_channelRemap[j] != -1)){ sendToDriver(0x80 | j, i, 0); } } } // Turn off all hanging notes - for (i = 0; i < ARRAYSIZE(_hanging_notes); i++) { - byte midiChannel = _hanging_notes[i].channel; - if ((_hanging_notes[i].time_left) && (_channelRemap[midiChannel] != -1)) { - sendToDriver(0x80 | midiChannel, _hanging_notes[i].note, 0); - _hanging_notes[i].time_left = 0; + for (i = 0; i < ARRAYSIZE(_hangingNotes); i++) { + byte midiChannel = _hangingNotes[i].channel; + if ((_hangingNotes[i].timeLeft) && (_channelRemap[midiChannel] != -1)) { + sendToDriver(0x80 | midiChannel, _hangingNotes[i].note, 0); + _hangingNotes[i].timeLeft = 0; } } - _hanging_notes_count = 0; + _hangingNotesCount = 0; // To be sure, send an "All Note Off" event (but not all MIDI devices // support this...). @@ -703,7 +703,7 @@ void MidiParser_SCI::allNotesOff() { } } - memset(_active_notes, 0, sizeof(_active_notes)); + memset(_activeNotes, 0, sizeof(_activeNotes)); } void MidiParser_SCI::setMasterVolume(byte masterVolume) { diff --git a/engines/sci/sound/midiparser_sci.h b/engines/sci/sound/midiparser_sci.h index 82f34070a4..d3fd337644 100644 --- a/engines/sci/sound/midiparser_sci.h +++ b/engines/sci/sound/midiparser_sci.h @@ -65,7 +65,7 @@ public: void setMasterVolume(byte masterVolume); void setVolume(byte volume); void stop() { - _abort_parse = true; + _abortParse = true; allNotesOff(); } void pause() { diff --git a/engines/sci/sound/music.cpp b/engines/sci/sound/music.cpp index 918b045cb9..a8a65d2aa4 100644 --- a/engines/sci/sound/music.cpp +++ b/engines/sci/sound/music.cpp @@ -125,7 +125,13 @@ void SciMusic::init() { _pMidiDrv->setTimerCallback(this, &miditimerCallback); _dwTempo = _pMidiDrv->getBaseTempo(); } else { - error("Failed to initialize sound driver"); + if (g_sci->getGameId() == GID_FUNSEEKER) { + // HACK: The Fun Seeker's Guide demo doesn't have patch 3 and the version + // of the Adlib driver (adl.drv) that it includes is unsupported. That demo + // doesn't have any sound anyway, so this shouldn't be fatal. + } else { + error("Failed to initialize sound driver"); + } } // Find out what the first possible channel is (used, when doing channel diff --git a/engines/sci/sound/soundcmd.cpp b/engines/sci/sound/soundcmd.cpp index cbb5cab4fe..7782ab4e48 100644 --- a/engines/sci/sound/soundcmd.cpp +++ b/engines/sci/sound/soundcmd.cpp @@ -96,7 +96,7 @@ void SoundCommandParser::initSoundResource(MusicEntry *newSound) { if (_useDigitalSFX || !newSound->soundRes) { int sampleLen; newSound->pStreamAud = _audio->getAudioStream(newSound->resourceId, 65535, &sampleLen); - newSound->soundType = Audio::Mixer::kSpeechSoundType; + newSound->soundType = Audio::Mixer::kSFXSoundType; } } @@ -340,6 +340,12 @@ reg_t SoundCommandParser::kDoSoundMasterVolume(int argc, reg_t *argv, reg_t acc) reg_t SoundCommandParser::kDoSoundFade(int argc, reg_t *argv, reg_t acc) { reg_t obj = argv[0]; + // The object can be null in several SCI0 games (e.g. Camelot, KQ1, KQ4, MUMG). + // Check bugs #3035149, #3036942 and #3578335. + // In this case, we just ignore the call. + if (obj.isNull() && argc == 1) + return acc; + MusicEntry *musicSlot = _music->getSlot(obj); if (!musicSlot) { debugC(kDebugLevelSound, "kDoSound(fade): Slot not found (%04x:%04x)", PRINT_REG(obj)); @@ -367,29 +373,36 @@ reg_t SoundCommandParser::kDoSoundFade(int argc, reg_t *argv, reg_t acc) { case 4: // SCI01+ case 5: // SCI1+ (SCI1 late sound scheme), with fade and continue - musicSlot->fadeTo = CLIP<uint16>(argv[1].toUint16(), 0, MUSIC_VOLUME_MAX); - // Check if the song is already at the requested volume. If it is, don't - // perform any fading. Happens for example during the intro of Longbow. - if (musicSlot->fadeTo == musicSlot->volume) - return acc; - - // sometimes we get objects in that position, fix it up (ffs. workarounds) - if (!argv[1].getSegment()) - musicSlot->fadeStep = volume > musicSlot->fadeTo ? -argv[3].toUint16() : argv[3].toUint16(); - else - musicSlot->fadeStep = volume > musicSlot->fadeTo ? -5 : 5; - musicSlot->fadeTickerStep = argv[2].toUint16() * 16667 / _music->soundGetTempo(); - musicSlot->fadeTicker = 0; - if (argc == 5) { // TODO: We currently treat this argument as a boolean, but may // have to handle different non-zero values differently. (e.g., - // some KQ6 scripts pass 3 here) - musicSlot->stopAfterFading = (argv[4].toUint16() != 0); + // some KQ6 scripts pass 3 here). + // There is a script bug in KQ6, room 460 (the room with the flying + // books). An object is passed here, which should not be treated as + // a true flag. Fixes bugs #3555404 and #3291115. + musicSlot->stopAfterFading = (argv[4].isNumber() && argv[4].toUint16() != 0); } else { musicSlot->stopAfterFading = false; } + musicSlot->fadeTo = CLIP<uint16>(argv[1].toUint16(), 0, MUSIC_VOLUME_MAX); + // Check if the song is already at the requested volume. If it is, don't + // perform any fading. Happens for example during the intro of Longbow. + if (musicSlot->fadeTo != musicSlot->volume) { + // sometimes we get objects in that position, fix it up (ffs. workarounds) + if (!argv[1].getSegment()) + musicSlot->fadeStep = volume > musicSlot->fadeTo ? -argv[3].toUint16() : argv[3].toUint16(); + else + musicSlot->fadeStep = volume > musicSlot->fadeTo ? -5 : 5; + musicSlot->fadeTickerStep = argv[2].toUint16() * 16667 / _music->soundGetTempo(); + } else { + // Stop the music, if requested. Fixes bug #3555404. + if (musicSlot->stopAfterFading) + processStopSound(obj, false); + } + + musicSlot->fadeTicker = 0; + // WORKAROUND/HACK: In the labyrinth in KQ6, when falling in the pit and // lighting the lantern, the game scripts perform a fade in of the game // music, but set it to stop after fading. Remove that flag here. This is diff --git a/engines/sci/video/robot_decoder.cpp b/engines/sci/video/robot_decoder.cpp index ebcfac6054..0337a8d306 100644 --- a/engines/sci/video/robot_decoder.cpp +++ b/engines/sci/video/robot_decoder.cpp @@ -22,11 +22,13 @@ #include "common/archive.h" #include "common/stream.h" +#include "common/substream.h" #include "common/system.h" #include "common/textconsole.h" #include "common/util.h" #include "graphics/surface.h" +#include "audio/audiostream.h" #include "audio/decoders/raw.h" #include "sci/resource.h" @@ -63,57 +65,26 @@ namespace Sci { // our graphics engine, it looks just like a part of the room. A RBT can move // around the screen and go behind other objects. (...) -#ifdef ENABLE_SCI32 - -enum robotPalTypes { +enum RobotPalTypes { kRobotPalVariable = 0, kRobotPalConstant = 1 }; -RobotDecoder::RobotDecoder(Audio::Mixer *mixer, bool isBigEndian) { - _surface = 0; - _width = 0; - _height = 0; +RobotDecoder::RobotDecoder(bool isBigEndian) { _fileStream = 0; - _audioStream = 0; - _dirtyPalette = false; _pos = Common::Point(0, 0); - _mixer = mixer; _isBigEndian = isBigEndian; + _frameTotalSize = 0; } RobotDecoder::~RobotDecoder() { close(); } -bool RobotDecoder::load(GuiResourceId id) { - // TODO: RAMA's robot 1003 cannot be played (shown at the menu screen) - - // its drawn at odd coordinates. SV can't play it either (along with some - // others), so it must be some new functionality added in RAMA's robot - // videos. Skip it for now. - if (g_sci->getGameId() == GID_RAMA && id == 1003) - return false; - - // TODO: The robot video in the Lighthouse demo gets stuck - if (g_sci->getGameId() == GID_LIGHTHOUSE && id == 16) - return false; - - Common::String fileName = Common::String::format("%d.rbt", id); - Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(fileName); - - if (!stream) { - warning("Unable to open robot file %s", fileName.c_str()); - return false; - } - - return loadStream(stream); -} - bool RobotDecoder::loadStream(Common::SeekableReadStream *stream) { close(); _fileStream = new Common::SeekableSubReadStreamEndian(stream, 0, stream->size(), _isBigEndian, DisposeAfterUse::YES); - _surface = new Graphics::Surface(); readHeaderChunk(); @@ -125,131 +96,60 @@ bool RobotDecoder::loadStream(Common::SeekableReadStream *stream) { if (_header.version < 4 || _header.version > 6) error("Unknown robot version: %d", _header.version); - if (_header.hasSound) { - _audioStream = Audio::makeQueuingAudioStream(11025, false); - _mixer->playStream(Audio::Mixer::kMusicSoundType, &_audioHandle, _audioStream, -1, getVolume(), getBalance()); - } + RobotVideoTrack *videoTrack = new RobotVideoTrack(_header.frameCount); + addTrack(videoTrack); - readPaletteChunk(_header.paletteDataSize); - readFrameSizesChunk(); - calculateVideoDimensions(); - _surface->create(_width, _height, Graphics::PixelFormat::createFormatCLUT8()); + if (_header.hasSound) + addTrack(new RobotAudioTrack()); + videoTrack->readPaletteChunk(_fileStream, _header.paletteDataSize); + readFrameSizesChunk(); + videoTrack->calculateVideoDimensions(_fileStream, _frameTotalSize); return true; } -void RobotDecoder::readHeaderChunk() { - // Header (60 bytes) - _fileStream->skip(6); - _header.version = _fileStream->readUint16(); - _header.audioChunkSize = _fileStream->readUint16(); - _header.audioSilenceSize = _fileStream->readUint16(); - _fileStream->skip(2); - _header.frameCount = _fileStream->readUint16(); - _header.paletteDataSize = _fileStream->readUint16(); - _header.unkChunkDataSize = _fileStream->readUint16(); - _fileStream->skip(5); - _header.hasSound = _fileStream->readByte(); - _fileStream->skip(34); - - // Some videos (e.g. robot 1305 in Phantasmagoria and - // robot 184 in Lighthouse) have an unknown chunk before - // the palette chunk (probably used for sound preloading). - // Skip it here. - if (_header.unkChunkDataSize) - _fileStream->skip(_header.unkChunkDataSize); -} - -void RobotDecoder::readPaletteChunk(uint16 chunkSize) { - byte *paletteData = new byte[chunkSize]; - _fileStream->read(paletteData, chunkSize); +bool RobotDecoder::load(GuiResourceId id) { + // TODO: RAMA's robot 1003 cannot be played (shown at the menu screen) - + // its drawn at odd coordinates. SV can't play it either (along with some + // others), so it must be some new functionality added in RAMA's robot + // videos. Skip it for now. + if (g_sci->getGameId() == GID_RAMA && id == 1003) + return false; - // SCI1.1 palette - byte palFormat = paletteData[32]; - uint16 palColorStart = paletteData[25]; - uint16 palColorCount = READ_SCI11ENDIAN_UINT16(paletteData + 29); + // TODO: The robot video in the Lighthouse demo gets stuck + if (g_sci->getGameId() == GID_LIGHTHOUSE && id == 16) + return false; - int palOffset = 37; - memset(_palette, 0, 256 * 3); + Common::String fileName = Common::String::format("%d.rbt", id); + Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(fileName); - for (uint16 colorNo = palColorStart; colorNo < palColorStart + palColorCount; colorNo++) { - if (palFormat == kRobotPalVariable) - palOffset++; - _palette[colorNo * 3 + 0] = paletteData[palOffset++]; - _palette[colorNo * 3 + 1] = paletteData[palOffset++]; - _palette[colorNo * 3 + 2] = paletteData[palOffset++]; + if (!stream) { + warning("Unable to open robot file %s", fileName.c_str()); + return false; } - _dirtyPalette = true; - delete[] paletteData; + return loadStream(stream); } +void RobotDecoder::close() { + VideoDecoder::close(); -void RobotDecoder::readFrameSizesChunk() { - // The robot video file contains 2 tables, with one entry for each frame: - // - A table containing the size of the image in each video frame - // - A table containing the total size of each video frame. - // In v5 robots, the tables contain 16-bit integers, whereas in v6 robots, - // they contain 32-bit integers. - - _frameTotalSize = new uint32[_header.frameCount]; - - // TODO: The table reading code can probably be removed once the - // audio chunk size is figured out (check the TODO inside processNextFrame()) -#if 0 - // We don't need any of the two tables to play the video, so we ignore - // both of them. - uint16 wordSize = _header.version == 6 ? 4 : 2; - _fileStream->skip(_header.frameCount * wordSize * 2); -#else - switch (_header.version) { - case 4: - case 5: // sizes are 16-bit integers - // Skip table with frame image sizes, as we don't need it - _fileStream->skip(_header.frameCount * 2); - for (int i = 0; i < _header.frameCount; ++i) - _frameTotalSize[i] = _fileStream->readUint16(); - break; - case 6: // sizes are 32-bit integers - // Skip table with frame image sizes, as we don't need it - _fileStream->skip(_header.frameCount * 4); - for (int i = 0; i < _header.frameCount; ++i) - _frameTotalSize[i] = _fileStream->readUint32(); - break; - default: - error("Can't yet handle index table for robot version %d", _header.version); - } -#endif - - // 2 more unknown tables - _fileStream->skip(1024 + 512); + delete _fileStream; + _fileStream = 0; - // Pad to nearest 2 kilobytes - uint32 curPos = _fileStream->pos(); - if (curPos & 0x7ff) - _fileStream->seek((curPos & ~0x7ff) + 2048); + delete[] _frameTotalSize; + _frameTotalSize = 0; } -void RobotDecoder::calculateVideoDimensions() { - // This is an O(n) operation, as each frame has a different size. - // We need to know the actual frame size to have a constant video size. - uint32 pos = _fileStream->pos(); - - for (uint32 curFrame = 0; curFrame < _header.frameCount; curFrame++) { - _fileStream->skip(4); - uint16 frameWidth = _fileStream->readUint16(); - uint16 frameHeight = _fileStream->readUint16(); - if (frameWidth > _width) - _width = frameWidth; - if (frameHeight > _height) - _height = frameHeight; - _fileStream->skip(_frameTotalSize[curFrame] - 8); - } +void RobotDecoder::readNextPacket() { + // Get our track + RobotVideoTrack *videoTrack = (RobotVideoTrack *)getTrack(0); + videoTrack->increaseCurFrame(); + Graphics::Surface *surface = videoTrack->getSurface(); - _fileStream->seek(pos); -} + if (videoTrack->endOfTrack()) + return; -const Graphics::Surface *RobotDecoder::decodeNextFrame() { // Read frame image header (24 bytes) _fileStream->skip(3); byte frameScale = _fileStream->readByte(); @@ -258,23 +158,28 @@ const Graphics::Surface *RobotDecoder::decodeNextFrame() { _fileStream->skip(4); // unknown, almost always 0 uint16 frameX = _fileStream->readUint16(); uint16 frameY = _fileStream->readUint16(); + // TODO: In v4 robot files, frameX and frameY have a different meaning. // Set them both to 0 for v4 for now, so that robots in PQ:SWAT show up // correctly. if (_header.version == 4) frameX = frameY = 0; + uint16 compressedSize = _fileStream->readUint16(); uint16 frameFragments = _fileStream->readUint16(); _fileStream->skip(4); // unknown uint32 decompressedSize = frameWidth * frameHeight * frameScale / 100; + // FIXME: A frame's height + position can go off limits... why? With the // following, we cut the contents to fit the frame - uint16 scaledHeight = CLIP<uint16>(decompressedSize / frameWidth, 0, _height - frameY); + uint16 scaledHeight = CLIP<uint16>(decompressedSize / frameWidth, 0, surface->h - frameY); + // FIXME: Same goes for the frame's width + position. In this case, we // modify the position to fit the contents on screen. - if (frameWidth + frameX > _width) - frameX = _width - frameWidth; - assert (frameWidth + frameX <= _width && scaledHeight + frameY <= _height); + if (frameWidth + frameX > surface->w) + frameX = surface->w - frameWidth; + + assert(frameWidth + frameX <= surface->w && scaledHeight + frameY <= surface->h); DecompressorLZS lzs; byte *decompressedFrame = new byte[decompressedSize]; @@ -305,24 +210,23 @@ const Graphics::Surface *RobotDecoder::decodeNextFrame() { // Copy over the decompressed frame byte *inFrame = decompressedFrame; - byte *outFrame = (byte *)_surface->pixels; + byte *outFrame = (byte *)surface->pixels; // Black out the surface - memset(outFrame, 0, _width * _height); + memset(outFrame, 0, surface->w * surface->h); // Move to the correct y coordinate - outFrame += _width * frameY; + outFrame += surface->w * frameY; for (uint16 y = 0; y < scaledHeight; y++) { memcpy(outFrame + frameX, inFrame, frameWidth); inFrame += frameWidth; - outFrame += _width; + outFrame += surface->w; } delete[] decompressedFrame; - // +1 because we start with frame number -1 - uint32 audioChunkSize = _frameTotalSize[_curFrame + 1] - (24 + compressedSize); + uint32 audioChunkSize = _frameTotalSize[videoTrack->getCurFrame()] - (24 + compressedSize); // TODO: The audio chunk size below is usually correct, but there are some // exceptions (e.g. robot 4902 in Phantasmagoria, towards its end) @@ -337,51 +241,166 @@ const Graphics::Surface *RobotDecoder::decodeNextFrame() { // Queue the next audio frame // FIXME: For some reason, there are audio hiccups/gaps if (_header.hasSound) { - _fileStream->skip(8); // header - _audioStream->queueBuffer(g_sci->_audio->getDecodedRobotAudioFrame(_fileStream, audioChunkSize - 8), - (audioChunkSize - 8) * 2, DisposeAfterUse::NO, - Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN); + RobotAudioTrack *audioTrack = (RobotAudioTrack *)getTrack(1); + _fileStream->skip(8); // header + audioChunkSize -= 8; + audioTrack->queueBuffer(g_sci->_audio->getDecodedRobotAudioFrame(_fileStream, audioChunkSize), audioChunkSize * 2); } else { _fileStream->skip(audioChunkSize); } +} - if (_curFrame == -1) - _startTime = g_system->getMillis(); - - _curFrame++; +void RobotDecoder::readHeaderChunk() { + // Header (60 bytes) + _fileStream->skip(6); + _header.version = _fileStream->readUint16(); + _header.audioChunkSize = _fileStream->readUint16(); + _header.audioSilenceSize = _fileStream->readUint16(); + _fileStream->skip(2); + _header.frameCount = _fileStream->readUint16(); + _header.paletteDataSize = _fileStream->readUint16(); + _header.unkChunkDataSize = _fileStream->readUint16(); + _fileStream->skip(5); + _header.hasSound = _fileStream->readByte(); + _fileStream->skip(34); - return _surface; + // Some videos (e.g. robot 1305 in Phantasmagoria and + // robot 184 in Lighthouse) have an unknown chunk before + // the palette chunk (probably used for sound preloading). + // Skip it here. + if (_header.unkChunkDataSize) + _fileStream->skip(_header.unkChunkDataSize); } -void RobotDecoder::close() { - if (!_fileStream) - return; +void RobotDecoder::readFrameSizesChunk() { + // The robot video file contains 2 tables, with one entry for each frame: + // - A table containing the size of the image in each video frame + // - A table containing the total size of each video frame. + // In v5 robots, the tables contain 16-bit integers, whereas in v6 robots, + // they contain 32-bit integers. - delete _fileStream; - _fileStream = 0; + _frameTotalSize = new uint32[_header.frameCount]; + + // TODO: The table reading code can probably be removed once the + // audio chunk size is figured out (check the TODO inside processNextFrame()) +#if 0 + // We don't need any of the two tables to play the video, so we ignore + // both of them. + uint16 wordSize = _header.version == 6 ? 4 : 2; + _fileStream->skip(_header.frameCount * wordSize * 2); +#else + switch (_header.version) { + case 4: + case 5: // sizes are 16-bit integers + // Skip table with frame image sizes, as we don't need it + _fileStream->skip(_header.frameCount * 2); + for (int i = 0; i < _header.frameCount; ++i) + _frameTotalSize[i] = _fileStream->readUint16(); + break; + case 6: // sizes are 32-bit integers + // Skip table with frame image sizes, as we don't need it + _fileStream->skip(_header.frameCount * 4); + for (int i = 0; i < _header.frameCount; ++i) + _frameTotalSize[i] = _fileStream->readUint32(); + break; + default: + error("Can't yet handle index table for robot version %d", _header.version); + } +#endif + + // 2 more unknown tables + _fileStream->skip(1024 + 512); + // Pad to nearest 2 kilobytes + uint32 curPos = _fileStream->pos(); + if (curPos & 0x7ff) + _fileStream->seek((curPos & ~0x7ff) + 2048); +} + +RobotDecoder::RobotVideoTrack::RobotVideoTrack(int frameCount) : _frameCount(frameCount) { + _surface = new Graphics::Surface(); + _curFrame = -1; + _dirtyPalette = false; +} + +RobotDecoder::RobotVideoTrack::~RobotVideoTrack() { _surface->free(); delete _surface; - _surface = 0; +} - if (_header.hasSound) { - _mixer->stopHandle(_audioHandle); - //delete _audioStream; _audioStream = 0; +uint16 RobotDecoder::RobotVideoTrack::getWidth() const { + return _surface->w; +} + +uint16 RobotDecoder::RobotVideoTrack::getHeight() const { + return _surface->h; +} + +Graphics::PixelFormat RobotDecoder::RobotVideoTrack::getPixelFormat() const { + return _surface->format; +} + +void RobotDecoder::RobotVideoTrack::readPaletteChunk(Common::SeekableSubReadStreamEndian *stream, uint16 chunkSize) { + byte *paletteData = new byte[chunkSize]; + stream->read(paletteData, chunkSize); + + // SCI1.1 palette + byte palFormat = paletteData[32]; + uint16 palColorStart = paletteData[25]; + uint16 palColorCount = READ_SCI11ENDIAN_UINT16(paletteData + 29); + + int palOffset = 37; + memset(_palette, 0, 256 * 3); + + for (uint16 colorNo = palColorStart; colorNo < palColorStart + palColorCount; colorNo++) { + if (palFormat == kRobotPalVariable) + palOffset++; + _palette[colorNo * 3 + 0] = paletteData[palOffset++]; + _palette[colorNo * 3 + 1] = paletteData[palOffset++]; + _palette[colorNo * 3 + 2] = paletteData[palOffset++]; } - reset(); + _dirtyPalette = true; + delete[] paletteData; } -void RobotDecoder::updateVolume() { - if (g_system->getMixer()->isSoundHandleActive(_audioHandle)) - g_system->getMixer()->setChannelVolume(_audioHandle, getVolume()); +void RobotDecoder::RobotVideoTrack::calculateVideoDimensions(Common::SeekableSubReadStreamEndian *stream, uint32 *frameSizes) { + // This is an O(n) operation, as each frame has a different size. + // We need to know the actual frame size to have a constant video size. + uint32 pos = stream->pos(); + + uint16 width = 0, height = 0; + + for (int curFrame = 0; curFrame < _frameCount; curFrame++) { + stream->skip(4); + uint16 frameWidth = stream->readUint16(); + uint16 frameHeight = stream->readUint16(); + if (frameWidth > width) + width = frameWidth; + if (frameHeight > height) + height = frameHeight; + stream->skip(frameSizes[curFrame] - 8); + } + + stream->seek(pos); + + _surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8()); } -void RobotDecoder::updateBalance() { - if (g_system->getMixer()->isSoundHandleActive(_audioHandle)) - g_system->getMixer()->setChannelBalance(_audioHandle, getBalance()); +RobotDecoder::RobotAudioTrack::RobotAudioTrack() { + _audioStream = Audio::makeQueuingAudioStream(11025, false); } -#endif +RobotDecoder::RobotAudioTrack::~RobotAudioTrack() { + delete _audioStream; +} + +void RobotDecoder::RobotAudioTrack::queueBuffer(byte *buffer, int size) { + _audioStream->queueBuffer(buffer, size, DisposeAfterUse::YES, Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN); +} + +Audio::AudioStream *RobotDecoder::RobotAudioTrack::getAudioStream() const { + return _audioStream; +} } // End of namespace Sci diff --git a/engines/sci/video/robot_decoder.h b/engines/sci/video/robot_decoder.h index e9cefe7d91..437954f7fb 100644 --- a/engines/sci/video/robot_decoder.h +++ b/engines/sci/video/robot_decoder.h @@ -25,84 +25,103 @@ #include "common/rational.h" #include "common/rect.h" -#include "common/stream.h" -#include "common/substream.h" -#include "audio/audiostream.h" -#include "audio/mixer.h" -#include "graphics/pixelformat.h" #include "video/video_decoder.h" -namespace Sci { +namespace Audio { +class QueuingAudioStream; +} -#ifdef ENABLE_SCI32 - -struct RobotHeader { - // 6 bytes, identifier bytes - uint16 version; - uint16 audioChunkSize; - uint16 audioSilenceSize; - // 2 bytes, unknown - uint16 frameCount; - uint16 paletteDataSize; - uint16 unkChunkDataSize; - // 5 bytes, unknown - byte hasSound; - // 34 bytes, unknown -}; +namespace Common { +class SeekableSubReadStreamEndian; +} + +namespace Sci { -class RobotDecoder : public Video::FixedRateVideoDecoder { +class RobotDecoder : public Video::VideoDecoder { public: - RobotDecoder(Audio::Mixer *mixer, bool isBigEndian); + RobotDecoder(bool isBigEndian); virtual ~RobotDecoder(); bool loadStream(Common::SeekableReadStream *stream); bool load(GuiResourceId id); void close(); - bool isVideoLoaded() const { return _fileStream != 0; } - uint16 getWidth() const { return _width; } - uint16 getHeight() const { return _height; } - uint32 getFrameCount() const { return _header.frameCount; } - const Graphics::Surface *decodeNextFrame(); - Graphics::PixelFormat getPixelFormat() const { return Graphics::PixelFormat::createFormatCLUT8(); } - const byte *getPalette() { _dirtyPalette = false; return _palette; } - bool hasDirtyPalette() const { return _dirtyPalette; } void setPos(uint16 x, uint16 y) { _pos = Common::Point(x, y); } Common::Point getPos() const { return _pos; } protected: - // VideoDecoder API - void updateVolume(); - void updateBalance(); - - // FixedRateVideoDecoder API - Common::Rational getFrameRate() const { return Common::Rational(60, 10); } + void readNextPacket(); private: + class RobotVideoTrack : public FixedRateVideoTrack { + public: + RobotVideoTrack(int frameCount); + ~RobotVideoTrack(); + + uint16 getWidth() const; + uint16 getHeight() const; + Graphics::PixelFormat getPixelFormat() const; + int getCurFrame() const { return _curFrame; } + int getFrameCount() const { return _frameCount; } + const Graphics::Surface *decodeNextFrame() { return _surface; } + const byte *getPalette() const { _dirtyPalette = false; return _palette; } + bool hasDirtyPalette() const { return _dirtyPalette; } + + void readPaletteChunk(Common::SeekableSubReadStreamEndian *stream, uint16 chunkSize); + void calculateVideoDimensions(Common::SeekableSubReadStreamEndian *stream, uint32 *frameSizes); + Graphics::Surface *getSurface() { return _surface; } + void increaseCurFrame() { _curFrame++; } + + protected: + Common::Rational getFrameRate() const { return Common::Rational(60, 10); } + + private: + int _frameCount; + int _curFrame; + byte _palette[256 * 3]; + mutable bool _dirtyPalette; + Graphics::Surface *_surface; + }; + + class RobotAudioTrack : public AudioTrack { + public: + RobotAudioTrack(); + ~RobotAudioTrack(); + + Audio::Mixer::SoundType getSoundType() const { return Audio::Mixer::kMusicSoundType; } + + void queueBuffer(byte *buffer, int size); + + protected: + Audio::AudioStream *getAudioStream() const; + + private: + Audio::QueuingAudioStream *_audioStream; + }; + + struct RobotHeader { + // 6 bytes, identifier bytes + uint16 version; + uint16 audioChunkSize; + uint16 audioSilenceSize; + // 2 bytes, unknown + uint16 frameCount; + uint16 paletteDataSize; + uint16 unkChunkDataSize; + // 5 bytes, unknown + byte hasSound; + // 34 bytes, unknown + } _header; + void readHeaderChunk(); - void readPaletteChunk(uint16 chunkSize); void readFrameSizesChunk(); - void calculateVideoDimensions(); - void freeData(); - - RobotHeader _header; Common::Point _pos; bool _isBigEndian; + uint32 *_frameTotalSize; Common::SeekableSubReadStreamEndian *_fileStream; - - uint16 _width; - uint16 _height; - uint32 *_frameTotalSize; - byte _palette[256 * 3]; - bool _dirtyPalette; - Graphics::Surface *_surface; - Audio::QueuingAudioStream *_audioStream; - Audio::SoundHandle _audioHandle; - Audio::Mixer *_mixer; }; -#endif } // End of namespace Sci diff --git a/engines/sci/video/seq_decoder.cpp b/engines/sci/video/seq_decoder.cpp index abd64911a7..a7b6346eca 100644 --- a/engines/sci/video/seq_decoder.cpp +++ b/engines/sci/video/seq_decoder.cpp @@ -41,33 +41,44 @@ enum seqFrameTypes { kSeqFrameDiff = 1 }; -SeqDecoder::SeqDecoder() { - _fileStream = 0; - _surface = 0; - _dirtyPalette = false; +SEQDecoder::SEQDecoder(uint frameDelay) : _frameDelay(frameDelay) { } -SeqDecoder::~SeqDecoder() { +SEQDecoder::~SEQDecoder() { close(); } -bool SeqDecoder::loadStream(Common::SeekableReadStream *stream) { +bool SEQDecoder::loadStream(Common::SeekableReadStream *stream) { close(); + addTrack(new SEQVideoTrack(stream, _frameDelay)); + + return true; +} + +SEQDecoder::SEQVideoTrack::SEQVideoTrack(Common::SeekableReadStream *stream, uint frameDelay) { + assert(stream); + assert(frameDelay != 0); _fileStream = stream; + _frameDelay = frameDelay; + _curFrame = -1; + _surface = new Graphics::Surface(); _surface->create(SEQ_SCREEN_WIDTH, SEQ_SCREEN_HEIGHT, Graphics::PixelFormat::createFormatCLUT8()); _frameCount = _fileStream->readUint16LE(); - // Set palette - int paletteChunkSize = _fileStream->readUint32LE(); - readPaletteChunk(paletteChunkSize); + // Set initial palette + readPaletteChunk(_fileStream->readUint32LE()); +} - return true; +SEQDecoder::SEQVideoTrack::~SEQVideoTrack() { + delete _fileStream; + _surface->free(); + delete _surface; } -void SeqDecoder::readPaletteChunk(uint16 chunkSize) { +void SEQDecoder::SEQVideoTrack::readPaletteChunk(uint16 chunkSize) { byte *paletteData = new byte[chunkSize]; _fileStream->read(paletteData, chunkSize); @@ -91,23 +102,7 @@ void SeqDecoder::readPaletteChunk(uint16 chunkSize) { delete[] paletteData; } -void SeqDecoder::close() { - if (!_fileStream) - return; - - _frameDelay = 0; - - delete _fileStream; - _fileStream = 0; - - _surface->free(); - delete _surface; - _surface = 0; - - reset(); -} - -const Graphics::Surface *SeqDecoder::decodeNextFrame() { +const Graphics::Surface *SEQDecoder::SEQVideoTrack::decodeNextFrame() { int16 frameWidth = _fileStream->readUint16LE(); int16 frameHeight = _fileStream->readUint16LE(); int16 frameLeft = _fileStream->readUint16LE(); @@ -142,9 +137,6 @@ const Graphics::Surface *SeqDecoder::decodeNextFrame() { delete[] buf; } - if (_curFrame == -1) - _startTime = g_system->getMillis(); - _curFrame++; return _surface; } @@ -159,7 +151,7 @@ const Graphics::Surface *SeqDecoder::decodeNextFrame() { } \ memcpy(dest + writeRow * SEQ_SCREEN_WIDTH + writeCol, litData + litPos, n); -bool SeqDecoder::decodeFrame(byte *rleData, int rleSize, byte *litData, int litSize, byte *dest, int left, int width, int height, int colorKey) { +bool SEQDecoder::SEQVideoTrack::decodeFrame(byte *rleData, int rleSize, byte *litData, int litSize, byte *dest, int left, int width, int height, int colorKey) { int writeRow = 0; int writeCol = left; int litPos = 0; @@ -237,4 +229,9 @@ bool SeqDecoder::decodeFrame(byte *rleData, int rleSize, byte *litData, int litS return true; } +const byte *SEQDecoder::SEQVideoTrack::getPalette() const { + _dirtyPalette = false; + return _palette; +} + } // End of namespace Sci diff --git a/engines/sci/video/seq_decoder.h b/engines/sci/video/seq_decoder.h index 800a3c9024..890f349feb 100644 --- a/engines/sci/video/seq_decoder.h +++ b/engines/sci/video/seq_decoder.h @@ -40,44 +40,49 @@ namespace Sci { /** * Implementation of the Sierra SEQ decoder, used in KQ6 DOS floppy/CD and GK1 DOS */ -class SeqDecoder : public Video::FixedRateVideoDecoder { +class SEQDecoder : public Video::VideoDecoder { public: - SeqDecoder(); - virtual ~SeqDecoder(); + SEQDecoder(uint frameDelay); + virtual ~SEQDecoder(); bool loadStream(Common::SeekableReadStream *stream); - void close(); - - void setFrameDelay(int frameDelay) { _frameDelay = frameDelay; } - - bool isVideoLoaded() const { return _fileStream != 0; } - uint16 getWidth() const { return SEQ_SCREEN_WIDTH; } - uint16 getHeight() const { return SEQ_SCREEN_HEIGHT; } - uint32 getFrameCount() const { return _frameCount; } - const Graphics::Surface *decodeNextFrame(); - Graphics::PixelFormat getPixelFormat() const { return Graphics::PixelFormat::createFormatCLUT8(); } - const byte *getPalette() { _dirtyPalette = false; return _palette; } - bool hasDirtyPalette() const { return _dirtyPalette; } - -protected: - Common::Rational getFrameRate() const { assert(_frameDelay); return Common::Rational(60, _frameDelay); } private: - enum { - SEQ_SCREEN_WIDTH = 320, - SEQ_SCREEN_HEIGHT = 200 + class SEQVideoTrack : public FixedRateVideoTrack { + public: + SEQVideoTrack(Common::SeekableReadStream *stream, uint frameDelay); + ~SEQVideoTrack(); + + uint16 getWidth() const { return SEQ_SCREEN_WIDTH; } + uint16 getHeight() const { return SEQ_SCREEN_HEIGHT; } + Graphics::PixelFormat getPixelFormat() const { return Graphics::PixelFormat::createFormatCLUT8(); } + int getCurFrame() const { return _curFrame; } + int getFrameCount() const { return _frameCount; } + const Graphics::Surface *decodeNextFrame(); + const byte *getPalette() const; + bool hasDirtyPalette() const { return _dirtyPalette; } + + protected: + Common::Rational getFrameRate() const { return Common::Rational(60, _frameDelay); } + + private: + enum { + SEQ_SCREEN_WIDTH = 320, + SEQ_SCREEN_HEIGHT = 200 + }; + + void readPaletteChunk(uint16 chunkSize); + bool decodeFrame(byte *rleData, int rleSize, byte *litData, int litSize, byte *dest, int left, int width, int height, int colorKey); + + Common::SeekableReadStream *_fileStream; + int _curFrame, _frameCount; + byte _palette[256 * 3]; + mutable bool _dirtyPalette; + Graphics::Surface *_surface; + uint _frameDelay; }; - void readPaletteChunk(uint16 chunkSize); - bool decodeFrame(byte *rleData, int rleSize, byte *litData, int litSize, byte *dest, int left, int width, int height, int colorKey); - - uint16 _width, _height; - uint16 _frameDelay; - Common::SeekableReadStream *_fileStream; - byte _palette[256 * 3]; - bool _dirtyPalette; - uint32 _frameCount; - Graphics::Surface *_surface; + uint _frameDelay; }; } // End of namespace Sci diff --git a/engines/scumm/actor.cpp b/engines/scumm/actor.cpp index b8722b6963..0c375efcdd 100644 --- a/engines/scumm/actor.cpp +++ b/engines/scumm/actor.cpp @@ -573,19 +573,19 @@ bool Actor_v2::checkWalkboxesHaveDirectPath(Common::Point &foundPath) { return false; } -bool Actor_v0::intersectLineSegments(const Common::Point &line1Start, const Common::Point &line1End, - const Common::Point &line2Start, const Common::Point &line2End, Common::Point &result) +bool Actor_v0::intersectLineSegments(const Common::Point &line1Start, const Common::Point &line1End, + const Common::Point &line2Start, const Common::Point &line2End, Common::Point &result) { const Common::Point v1 = line1End - line1Start; // line1(n1) = line1Start + n1 * v1 const Common::Point v2 = line2End - line2Start; // line2(n2) = line2Start + n2 * v2 - + double det = v2.x * v1.y - v1.x * v2.y; if (det == 0) return false; - double n1 = ((double)v2.x * (line2Start.y - line1Start.y) - + double n1 = ((double)v2.x * (line2Start.y - line1Start.y) - (double)v2.y * (line2Start.x - line1Start.x)) / det; - double n2 = ((double)v1.x * (line2Start.y - line1Start.y) - + double n2 = ((double)v1.x * (line2Start.y - line1Start.y) - (double)v1.y * (line2Start.x - line1Start.x)) / det; // both coefficients have to be in [0, 1], otherwise the intersection is @@ -599,16 +599,16 @@ bool Actor_v0::intersectLineSegments(const Common::Point &line1Start, const Comm } /* - * MM v0 allows the actor to walk in a direct line between boxes to the target + * MM v0 allows the actor to walk in a direct line between boxes to the target * if actor and target share a horizontal or vertical corridor. - * If such a corridor is found the actor is not forced to go horizontally or + * If such a corridor is found the actor is not forced to go horizontally or * vertically from one box to the next but can also walk diagonally. * - * Note: the original v0 interpreter sets the target destination for diagonal + * Note: the original v0 interpreter sets the target destination for diagonal * walking only once and then rechecks whenever the actor reaches a new box if the - * walk destination is still suitable for the current box. - * ScummVM does not perform such a check, so it is possible to leave the walkboxes - * in some cases, for example L-shaped rooms like the swimming pool (actor walks over water) + * walk destination is still suitable for the current box. + * ScummVM does not perform such a check, so it is possible to leave the walkboxes + * in some cases, for example L-shaped rooms like the swimming pool (actor walks over water) * or the medical room (actor walks over examination table). * To solve this we intersect the new walk destination with the actor's walkbox borders, * so a recheck is done when the actor leaves his box. This is done by the @@ -992,7 +992,7 @@ void Actor_v0::setDirection(int direction) { res = 7; // Face Camera break; } - + _animFrameRepeat = -1; animateActor(res); if (_moving) @@ -1408,7 +1408,7 @@ void Actor::showActor() { if (_vm->_game.version == 0) { Actor_v0 *a = ((Actor_v0 *)this); - + a->_costCommand = a->_costCommandNew = 0xFF; for (int i = 0; i < 8; ++i) { @@ -2056,7 +2056,7 @@ void Actor_v0::animateCostume() { void Actor_v0::speakCheck() { if (v0ActorTalkArray[_number] & 0x80) return; - + int cmd = newDirToOldDir(_facing); if (_speaking & 0x80) @@ -2884,7 +2884,7 @@ void Actor_v0::animateActor(int anim) { _costCommandNew = anim; _vm->_costumeLoader->costumeDecodeData(this, 0, 0); - + if (dir == -1) return; diff --git a/engines/scumm/actor.h b/engines/scumm/actor.h index 0ed239d005..a733fdb4ed 100644 --- a/engines/scumm/actor.h +++ b/engines/scumm/actor.h @@ -377,7 +377,7 @@ public: virtual void saveLoadWithSerializer(Serializer *ser); protected: - bool intersectLineSegments(const Common::Point &line1Start, const Common::Point &line1End, + bool intersectLineSegments(const Common::Point &line1Start, const Common::Point &line1End, const Common::Point &line2Start, const Common::Point &line2End, Common::Point &result); virtual bool checkWalkboxesHaveDirectPath(Common::Point &foundPath); }; diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp index 4064853b6b..9ae75b6683 100644 --- a/engines/scumm/charset.cpp +++ b/engines/scumm/charset.cpp @@ -69,7 +69,7 @@ void ScummEngine::loadCJKFont() { _cjkFont->setDrawingMode(Graphics::FontSJIS::kShadowMode); _2byteWidth = _2byteHeight = 12; - _useCJKMode = true; + _useCJKMode = true; #endif } else if (_game.id == GID_MONKEY && _game.platform == Common::kPlatformSegaCD && _language == Common::JA_JPN) { int numChar = 1413; @@ -499,7 +499,7 @@ int CharsetRendererV3::getCharWidth(uint16 chr) { if (_vm->_useCJKMode && (chr & 0x80)) spacing = _vm->_2byteWidth / 2; - + if (!spacing) spacing = *(_widthTable + chr); @@ -644,7 +644,7 @@ void CharsetRendererV3::printChar(int chr, bool ignoreCharsetMask) { drawBits1(*vs, _left + vs->xstart, drawTop, charPtr, drawTop, origWidth, origHeight); else drawBits1(_vm->_textSurface, _left * _vm->_textSurfaceMultiplier, _top * _vm->_textSurfaceMultiplier, charPtr, drawTop, origWidth, origHeight); - + if (is2byte) { origWidth /= _vm->_textSurfaceMultiplier; height /= _vm->_textSurfaceMultiplier; @@ -1399,7 +1399,7 @@ void CharsetRendererTownsClassic::drawBitsN(const Graphics::Surface&, byte *dst, _vm->_cjkFont->drawChar(_vm->_textSurface, _sjisCurChar, _left * _vm->_textSurfaceMultiplier, (_top - _vm->_screenTop) * _vm->_textSurfaceMultiplier, _vm->_townsCharsetColorMap[1], _shadowColor); return; } - + bool scale2x = (_vm->_textSurfaceMultiplier == 2); dst = (byte *)_vm->_textSurface.pixels + (_top - _vm->_screenTop) * _vm->_textSurface.pitch * _vm->_textSurfaceMultiplier + _left * _vm->_textSurfaceMultiplier; diff --git a/engines/scumm/charset.h b/engines/scumm/charset.h index b8f1d84045..1c1df51921 100644 --- a/engines/scumm/charset.h +++ b/engines/scumm/charset.h @@ -112,7 +112,7 @@ public: class CharsetRendererClassic : public CharsetRendererCommon { protected: virtual void drawBitsN(const Graphics::Surface &s, byte *dst, const byte *src, byte bpp, int drawTop, int width, int height); - void printCharIntern(bool is2byte, const byte *charPtr, int origWidth, int origHeight, int width, int height, VirtScreen *vs, bool ignoreCharsetMask); + void printCharIntern(bool is2byte, const byte *charPtr, int origWidth, int origHeight, int width, int height, VirtScreen *vs, bool ignoreCharsetMask); virtual bool prepareDraw(uint16 chr); int _width, _height, _origWidth, _origHeight; @@ -195,7 +195,7 @@ public: int getCharWidth(uint16 chr); int getFontHeight(); - + private: void enableShadow(bool enable); void drawBits1(Graphics::Surface &dest, int x, int y, const byte *src, int drawTop, int width, int height); diff --git a/engines/scumm/costume.cpp b/engines/scumm/costume.cpp index 6e7e9ff688..4ebdd00fdc 100644 --- a/engines/scumm/costume.cpp +++ b/engines/scumm/costume.cpp @@ -592,7 +592,7 @@ void ClassicCostumeRenderer::proc3_ami(Codec1 &v1) { } while (1); } -void PCESetCostumeData(byte block[16][16], int index, byte value) { +static void PCESetCostumeData(byte block[16][16], int index, byte value) { int row = (index % 16); int plane = (index / 16) % 4; int colOffset = (index < 64) ? 8 : 0; @@ -1188,7 +1188,7 @@ byte V0CostumeRenderer::drawLimb(const Actor *a, int limb) { _draw_top = 200; _draw_bottom = 0; } - + // Invalid current position? if (a->_cost.curpos[limb] == 0xFFFF) return 0; @@ -1377,7 +1377,7 @@ byte V0CostumeLoader::increaseAnim(Actor *a, int limb) { // Reset the comstume command a0->_costCommandNew = 0xFF; a0->_costCommand = 0xFF; - + // Set the frame/start to invalid a0->_cost.frame[limb] = 0xFFFF; a0->_cost.start[limb] = 0xFFFF; diff --git a/engines/scumm/cursor.cpp b/engines/scumm/cursor.cpp index 88681898f5..269ae9e10a 100644 --- a/engines/scumm/cursor.cpp +++ b/engines/scumm/cursor.cpp @@ -180,7 +180,7 @@ void ScummEngine_v70he::setDefaultCursor() { 0xff, 0xff, 0xff, 0, 0, 0, }; - + memset(_grabbedCursor, 5, sizeof(_grabbedCursor)); _cursor.hotspotX = _cursor.hotspotY = 2; diff --git a/engines/scumm/debugger.cpp b/engines/scumm/debugger.cpp index edcf2e6fea..9b6dd1e687 100644 --- a/engines/scumm/debugger.cpp +++ b/engines/scumm/debugger.cpp @@ -382,7 +382,7 @@ bool ScummDebugger::Cmd_Actor(int argc, const char **argv) { DebugPrintf("Actor[%d].costume = %d\n", actnum, a->_costume); } } else if (!strcmp(argv[2], "name")) { - DebugPrintf("Name of actor %d: %s\n", actnum, + DebugPrintf("Name of actor %d: %s\n", actnum, _vm->getObjOrActorName(_vm->actorToObj(actnum))); } else if (!strcmp(argv[2], "condmask")) { if (argc > 3) { @@ -741,10 +741,6 @@ bool ScummDebugger::Cmd_PrintDraft(int argc, const char **argv) { "Silence", "Shaping", "Unmaking", "Transcendence" }; - int odds[] = { - 15162, 15676, 16190, 64, 16961, 17475, 17989, 18503, - 73, 19274, 76, 77, 20302, 20816, 21330, 84 - }; const char *notes = "cdefgabC"; int i, base, draft; @@ -754,9 +750,9 @@ bool ScummDebugger::Cmd_PrintDraft(int argc, const char **argv) { return true; } - // There are 16 drafts, stored from variable 50 or 100 and upwards. - // Each draft occupies two variables. Even-numbered variables contain - // the notes for each draft, and a number of flags: + // There are 16 drafts, stored from variable 50, 55 or 100 and upwards. + // Each draft occupies two variables, the first of which contains the + // notes for the draft and a number of flags. // // +---+---+---+---+-----+-----+-----+-----+ // | A | B | C | D | 444 | 333 | 222 | 111 | @@ -771,13 +767,16 @@ bool ScummDebugger::Cmd_PrintDraft(int argc, const char **argv) { // 222 The second note // 111 The first note // - // I don't yet know what the odd-numbered variables are used for. - // Possibly they store information on where and/or how the draft can - // be used. They appear to remain constant throughout the game. + // I don't yet know what the second variable is used for. Possibly to + // store information on where and/or how the draft can be used. They + // appear to remain constant throughout the game. if (_vm->_game.version == 4 || _vm->_game.platform == Common::kPlatformPCEngine) { // DOS CD version / PC-Engine version base = 100; + } else if (_vm->_game.platform == Common::kPlatformMacintosh) { + // Macintosh version + base = 55; } else { // All (?) other versions base = 50; @@ -801,28 +800,13 @@ bool ScummDebugger::Cmd_PrintDraft(int argc, const char **argv) { DebugPrintf("Learned all drafts and notes.\n"); return true; } - - // During the testing of EGA Loom we had some trouble with the - // drafts data structure being overwritten. I don't expect - // this command is particularly useful any more, but it will - // attempt to repair the (probably) static part of it. - - if (strcmp(argv[1], "fix") == 0) { - for (i = 0; i < 16; i++) - _vm->_scummVars[base + 2 * i + 1] = odds[i]; - DebugPrintf( - "An attempt has been made to repair\n" - "the internal drafts data structure.\n" - "Continue on your own risk.\n"); - return true; - } } // Probably the most useful command for ordinary use: list the drafts. for (i = 0; i < 16; i++) { draft = _vm->_scummVars[base + i * 2]; - DebugPrintf("%d %-13s %c%c%c%c %c%c %5d %c\n", + DebugPrintf("%d %-13s %c%c%c%c %c%c\n", base + 2 * i, names[i], notes[draft & 0x0007], @@ -830,9 +814,7 @@ bool ScummDebugger::Cmd_PrintDraft(int argc, const char **argv) { notes[(draft & 0x01c0) >> 6], notes[(draft & 0x0e00) >> 9], (draft & 0x2000) ? 'K' : ' ', - (draft & 0x4000) ? 'U' : ' ', - _vm->_scummVars[base + 2 * i + 1], - (_vm->_scummVars[base + 2 * i + 1] != odds[i]) ? '!' : ' '); + (draft & 0x4000) ? 'U' : ' '); } return true; diff --git a/engines/scumm/detection.cpp b/engines/scumm/detection.cpp index 87305921c9..e5c3023380 100644 --- a/engines/scumm/detection.cpp +++ b/engines/scumm/detection.cpp @@ -20,9 +20,6 @@ * */ -// FIXME: Avoid using printf -#define FORBIDDEN_SYMBOL_EXCEPTION_printf - #include "base/plugins.h" #include "common/archive.h" @@ -245,6 +242,10 @@ static Common::String generateFilenameForDetection(const char *pattern, Filename return result; } +bool ScummEngine::isMacM68kIMuse() const { + return _game.platform == Common::kPlatformMacintosh && (_game.id == GID_MONKEY2 || _game.id == GID_INDY4) && !(_game.features & GF_MAC_CONTAINER); +} + struct DetectorDesc { Common::FSNode node; Common::String md5; @@ -476,6 +477,11 @@ static void computeGameSettingsFromMD5(const Common::FSList &fslist, const GameF if (dr.language == UNK_LANG) { dr.language = detectLanguage(fslist, dr.game.id); } + + // HACK: Detect between 68k and PPC versions + if (dr.game.platform == Common::kPlatformMacintosh && dr.game.version >= 5 && dr.game.heversion == 0 && strstr(gfp->pattern, "Data")) + dr.game.features |= GF_MAC_CONTAINER; + break; } } @@ -1066,15 +1072,19 @@ Common::Error ScummMetaEngine::createInstance(OSystem *syst, Engine **engine) co // unknown MD5, or with a medium debug level in case of a known MD5 (for // debugging purposes). if (!findInMD5Table(res.md5.c_str())) { - printf("Your game version appears to be unknown. If this is *NOT* a fan-modified\n"); - printf("version (in particular, not a fan-made translation), please, report the\n"); - printf("following data to the ScummVM team along with name of the game you tried\n"); - printf("to add and its version/language/etc.:\n"); + Common::String md5Warning; + + md5Warning = "Your game version appears to be unknown. If this is *NOT* a fan-modified\n"; + md5Warning += "version (in particular, not a fan-made translation), please, report the\n"; + md5Warning += "following data to the ScummVM team along with name of the game you tried\n"; + md5Warning += "to add and its version/language/etc.:\n"; - printf(" SCUMM gameid '%s', file '%s', MD5 '%s'\n\n", + md5Warning += Common::String::format(" SCUMM gameid '%s', file '%s', MD5 '%s'\n\n", res.game.gameid, generateFilenameForDetection(res.fp.pattern, res.fp.genMethod).c_str(), res.md5.c_str()); + + g_system->logMessage(LogMessageType::kWarning, md5Warning.c_str()); } else { debug(1, "Using MD5 '%s'", res.md5.c_str()); } diff --git a/engines/scumm/detection_tables.h b/engines/scumm/detection_tables.h index be1b90e356..3120017db6 100644 --- a/engines/scumm/detection_tables.h +++ b/engines/scumm/detection_tables.h @@ -236,7 +236,7 @@ static const GameSettings gameVariantsTable[] = { {"monkey", "VGA", "vga", GID_MONKEY_VGA, 4, 0, MDT_PCSPK | MDT_PCJR | MDT_CMS | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO1(GUIO_NOSPEECH)}, {"monkey", "EGA", "ega", GID_MONKEY_EGA, 4, 0, MDT_PCSPK | MDT_PCJR | MDT_CMS | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, GF_16COLOR, Common::kPlatformPC, GUIO1(GUIO_NOSPEECH)}, {"monkey", "No AdLib", "ega", GID_MONKEY_EGA, 4, 0, MDT_PCSPK | MDT_PCJR, GF_16COLOR, Common::kPlatformAtariST, GUIO2(GUIO_NOSPEECH, GUIO_NOMIDI)}, - {"monkey", "Demo", "ega", GID_MONKEY_EGA, 4, 0, MDT_PCSPK | MDT_PCJR | MDT_ADLIB, GF_16COLOR, Common::kPlatformPC, GUIO2(GUIO_NOSPEECH, GUIO_NOMIDI)}, + {"monkey", "Demo", "ega", GID_MONKEY_EGA, 4, 0, MDT_PCSPK | MDT_PCJR | MDT_CMS | MDT_ADLIB, GF_16COLOR, Common::kPlatformPC, GUIO2(GUIO_NOSPEECH, GUIO_NOMIDI)}, {"monkey", "CD", 0, GID_MONKEY, 5, 0, MDT_ADLIB, GF_AUDIOTRACKS, UNK, GUIO2(GUIO_NOSPEECH, GUIO_NOMIDI)}, {"monkey", "FM-TOWNS", 0, GID_MONKEY, 5, 0, MDT_TOWNS, GF_AUDIOTRACKS, Common::kPlatformFMTowns, GUIO4(GUIO_NOSPEECH, GUIO_NOMIDI, GUIO_MIDITOWNS, GUIO_NOASPECT)}, {"monkey", "SEGA", 0, GID_MONKEY, 5, 0, MDT_NONE, GF_AUDIOTRACKS, Common::kPlatformSegaCD, GUIO2(GUIO_NOSPEECH, GUIO_NOMIDI)}, diff --git a/engines/scumm/dialogs.cpp b/engines/scumm/dialogs.cpp index 0e531daf73..945c5b6611 100644 --- a/engines/scumm/dialogs.cpp +++ b/engines/scumm/dialogs.cpp @@ -180,7 +180,7 @@ static const ResString string_map_table_v345[] = { // "Moechten Sie wirklich neu starten? (J/N)J" // Will react to J as 'Yes' {5, _s("Are you sure you want to restart? (Y/N)")}, - // I18N: you may specify 'Yes' symbol at the end of the line. See previous comment + // I18N: you may specify 'Yes' symbol at the end of the line. See previous comment {6, _s("Are you sure you want to quit? (Y/N)")}, // Added in SCUMM4 diff --git a/engines/scumm/gfx.cpp b/engines/scumm/gfx.cpp index ffff329036..50ff0b3988 100644 --- a/engines/scumm/gfx.cpp +++ b/engines/scumm/gfx.cpp @@ -3609,7 +3609,7 @@ void Gdi::unkDecode9(byte *dst, int dstPitch, const byte *src, int height) const int i; uint buffer = 0, mask = 128; int h = height; - i = run = 0; + run = 0; int x = 8; for (;;) { diff --git a/engines/scumm/he/animation_he.cpp b/engines/scumm/he/animation_he.cpp index 40e99c26a8..be17a3b305 100644 --- a/engines/scumm/he/animation_he.cpp +++ b/engines/scumm/he/animation_he.cpp @@ -40,7 +40,7 @@ MoviePlayer::MoviePlayer(ScummEngine_v90he *vm, Audio::Mixer *mixer) : _vm(vm) { _video = new Video::BinkDecoder(); else #endif - _video = new Video::SmackerDecoder(mixer); + _video = new Video::SmackerDecoder(); _flags = 0; _wizResNum = 0; @@ -61,11 +61,16 @@ int MoviePlayer::load(const char *filename, int flags, int image) { if (_video->isVideoLoaded()) _video->close(); + // Ensure that Bink will use our PixelFormat + _video->setDefaultHighColorFormat(g_system->getScreenFormat()); + if (!_video->loadFile(filename)) { warning("Failed to load video file %s", filename); return -1; } + _video->start(); + debug(1, "Playing video %s", filename); if (flags & 2) diff --git a/engines/scumm/he/logic/soccer.cpp b/engines/scumm/he/logic/soccer.cpp index 05f377a736..2b29f93173 100644 --- a/engines/scumm/he/logic/soccer.cpp +++ b/engines/scumm/he/logic/soccer.cpp @@ -303,7 +303,7 @@ int LogicHEsoccer::op_1008(int outArray, int srcX, int srcY, int srcZ, int vecX, putInArray(outArray, segmentsSoFar, 5, vecX); putInArray(outArray, segmentsSoFar, 6, vecY); putInArray(outArray, segmentsSoFar++, 7, vecZ); - } + } } else { srcY = 0; int thisVecX = vecX; @@ -628,7 +628,7 @@ int LogicHEsoccer::op_1014(int32 srcX, int32 srcY, int32 srcZ, int32 velX, int32 adjustedVelZ = ((double)srcZ - 3869.0) / 100.0; break; } - + int foundCollision = 0; // work out which collision objects we might collide with (if any) diff --git a/engines/scumm/he/sound_he.cpp b/engines/scumm/he/sound_he.cpp index f94b74ac45..1afb1b4074 100644 --- a/engines/scumm/he/sound_he.cpp +++ b/engines/scumm/he/sound_he.cpp @@ -804,7 +804,7 @@ void ScummEngine_v80he::createSound(int snd1id, int snd2id) { byte *snd1Ptr, *snd2Ptr; byte *sbng1Ptr, *sbng2Ptr; byte *sdat1Ptr, *sdat2Ptr; - byte *src, *dst, *tmp; + byte *src, *dst; int len, offs, size; int sdat1size, sdat2size; @@ -844,6 +844,7 @@ void ScummEngine_v80he::createSound(int snd1id, int snd2id) { if (sbng1Ptr != NULL && sbng2Ptr != NULL) { if (chan != -1 && ((SoundHE *)_sound)->_heChannel[chan].codeOffs > 0) { + // Copy any code left over to the beginning of the code block int curOffs = ((SoundHE *)_sound)->_heChannel[chan].codeOffs; src = snd1Ptr + curOffs; @@ -851,29 +852,33 @@ void ScummEngine_v80he::createSound(int snd1id, int snd2id) { size = READ_BE_UINT32(sbng1Ptr + 4); len = sbng1Ptr - snd1Ptr + size - curOffs; - byte *data = (byte *)malloc(len); - memcpy(data, src, len); - memcpy(dst, data, len); - free(data); + memmove(dst, src, len); + // Now seek to the end of this code block dst = sbng1Ptr + 8; while ((size = READ_LE_UINT16(dst)) != 0) dst += size; } else { + // We're going to overwrite the code block completely dst = sbng1Ptr + 8; } - ((SoundHE *)_sound)->_heChannel[chan].codeOffs = sbng1Ptr - snd1Ptr + 8; + // Reset the current code offset to the beginning of the code block + if (chan >= 0) + ((SoundHE *)_sound)->_heChannel[chan].codeOffs = sbng1Ptr - snd1Ptr + 8; - tmp = sbng2Ptr + 8; + // Seek to the end of the code block for sound 2 + byte *tmp = sbng2Ptr + 8; while ((offs = READ_LE_UINT16(tmp)) != 0) { tmp += offs; } + // Copy the code block for sound 2 to the code block for sound 1 src = sbng2Ptr + 8; len = tmp - sbng2Ptr - 6; memcpy(dst, src, len); + // Rewrite the time for this new code block to be after the sound 1 code block int32 time; while ((size = READ_LE_UINT16(dst)) != 0) { time = READ_LE_UINT32(dst + 2); @@ -883,6 +888,7 @@ void ScummEngine_v80he::createSound(int snd1id, int snd2id) { } } + // Find the data pointers and sizes if (findSoundTag(MKTAG('d','a','t','a'), snd1Ptr)) { sdat1Ptr = findSoundTag(MKTAG('d','a','t','a'), snd1Ptr); assert(sdat1Ptr); @@ -906,6 +912,8 @@ void ScummEngine_v80he::createSound(int snd1id, int snd2id) { sdat1size = _sndDataSize - _sndPtrOffs; if (sdat2size < sdat1size) { + // We have space leftover at the end of sound 1 + // -> Just append sound 2 src = sdat2Ptr + 8; dst = sdat1Ptr + 8 + _sndPtrOffs; len = sdat2size; @@ -915,6 +923,8 @@ void ScummEngine_v80he::createSound(int snd1id, int snd2id) { _sndPtrOffs += sdat2size; _sndTmrOffs += sdat2size; } else { + // We might not have enough space leftover at the end of sound 1 + // -> Append as much of possible of sound 2 to sound 1 src = sdat2Ptr + 8; dst = sdat1Ptr + 8 + _sndPtrOffs; len = sdat1size; @@ -922,6 +932,8 @@ void ScummEngine_v80he::createSound(int snd1id, int snd2id) { memcpy(dst, src, len); if (sdat2size != sdat1size) { + // We don't have enough space + // -> Start overwriting the beginning of the sound again src = sdat2Ptr + 8 + sdat1size; dst = sdat1Ptr + 8; len = sdat2size - sdat1size; diff --git a/engines/scumm/he/wiz_he.cpp b/engines/scumm/he/wiz_he.cpp index 47b4b8ad33..798f703db6 100644 --- a/engines/scumm/he/wiz_he.cpp +++ b/engines/scumm/he/wiz_he.cpp @@ -2286,8 +2286,7 @@ void Wiz::fillWizLine(const WizParameters *params) { lineP.depth = bitDepth; if (params->processFlags & kWPFParams) { - assert (params->params2 == 1); // Catch untested usage - Graphics::drawThickLine(x1, y1, x2, y2, params->params1, color, drawProc, &lineP); + Graphics::drawThickLine(x1, y1, x2, y2, params->params1, params->params2, color, drawProc, &lineP); } else { Graphics::drawLine(x1, y1, x2, y2, color, drawProc, &lineP); } diff --git a/engines/scumm/imuse/imuse.cpp b/engines/scumm/imuse/imuse.cpp index 27a72c2afe..b69ce552bc 100644 --- a/engines/scumm/imuse/imuse.cpp +++ b/engines/scumm/imuse/imuse.cpp @@ -164,7 +164,7 @@ bool IMuseInternal::isMT32(int sound) { return true; case MKTAG('M', 'A', 'C', ' '): // Occurs in the Mac version of FOA and MI2 - return true; + return false; case MKTAG('G', 'M', 'D', ' '): return false; @@ -226,6 +226,45 @@ bool IMuseInternal::isMIDI(int sound) { return false; } +bool IMuseInternal::supportsPercussion(int sound) { + byte *ptr = g_scumm->_res->_types[rtSound][sound]._address; + if (ptr == NULL) + return false; + + uint32 tag = READ_BE_UINT32(ptr); + switch (tag) { + case MKTAG('A', 'D', 'L', ' '): + case MKTAG('A', 'S', 'F', 'X'): // Special AD class for old AdLib sound effects + case MKTAG('S', 'P', 'K', ' '): + return false; + + case MKTAG('A', 'M', 'I', ' '): + case MKTAG('R', 'O', 'L', ' '): + return true; + + case MKTAG('M', 'A', 'C', ' '): // Occurs in the Mac version of FOA and MI2 + // This is MIDI, i.e. uses MIDI style program changes, but without a + // special percussion channel. + return false; + + case MKTAG('G', 'M', 'D', ' '): + case MKTAG('M', 'I', 'D', 'I'): // Occurs in Sam & Max + return true; + } + + // Old style 'RO' has equivalent properties to 'ROL' + if (ptr[0] == 'R' && ptr[1] == 'O') + return true; + // Euphony tracks show as 'SO' and have equivalent properties to 'ADL' + // FIXME: Right now we're pretending it's GM. + if (ptr[4] == 'S' && ptr[5] == 'O') + return true; + + error("Unknown music type: '%c%c%c%c'", (char)tag >> 24, (char)tag >> 16, (char)tag >> 8, (char)tag); + + return false; +} + MidiDriver *IMuseInternal::getBestMidiDriver(int sound) { MidiDriver *driver = NULL; @@ -324,7 +363,7 @@ void IMuseInternal::pause(bool paused) { _paused = paused; } -int IMuseInternal::save_or_load(Serializer *ser, ScummEngine *scumm) { +int IMuseInternal::save_or_load(Serializer *ser, ScummEngine *scumm, bool fixAfterLoad) { Common::StackLock lock(_mutex, "IMuseInternal::save_or_load()"); const SaveLoadEntry mainEntries[] = { MKLINE(IMuseInternal, _queue_end, sleUint8, VER(8)), @@ -401,7 +440,16 @@ int IMuseInternal::save_or_load(Serializer *ser, ScummEngine *scumm) { for (i = 0; i < 8; ++i) ser->saveLoadEntries(0, volumeFaderEntries); - if (ser->isLoading()) { + // Normally, we have to fix up the data structures after loading a + // saved game. But there are cases where we don't. For instance, The + // Macintosh version of Monkey Island 1 used to convert the Mac0 music + // resources to General MIDI and play it through iMUSE as a rough + // approximation. Now it has its own player, but old savegame still + // have the iMUSE data in them. We have to skip that data, using a + // dummy iMUSE object, but since the resource is no longer recognizable + // to iMUSE, the fixup fails hard. So yes, this is a bit of a hack. + + if (ser->isLoading() && fixAfterLoad) { // Load all sounds that we need fix_players_after_load(scumm); fix_parts_after_load(); diff --git a/engines/scumm/imuse/imuse.h b/engines/scumm/imuse/imuse.h index 23449e470b..cce5309229 100644 --- a/engines/scumm/imuse/imuse.h +++ b/engines/scumm/imuse/imuse.h @@ -62,7 +62,7 @@ public: public: virtual void on_timer(MidiDriver *midi) = 0; virtual void pause(bool paused) = 0; - virtual int save_or_load(Serializer *ser, ScummEngine *scumm) = 0; + virtual int save_or_load(Serializer *ser, ScummEngine *scumm, bool fixAfterLoad = true) = 0; virtual bool get_sound_active(int sound) const = 0; virtual int32 doCommand(int numargs, int args[]) = 0; virtual int clear_queue() = 0; diff --git a/engines/scumm/imuse/imuse_internal.h b/engines/scumm/imuse/imuse_internal.h index 3b0d36e119..6be564a517 100644 --- a/engines/scumm/imuse/imuse_internal.h +++ b/engines/scumm/imuse/imuse_internal.h @@ -204,6 +204,7 @@ protected: bool _isMT32; bool _isMIDI; + bool _supportsPercussion; protected: // Player part @@ -458,6 +459,7 @@ protected: byte *findStartOfSound(int sound, int ct = (kMThd | kFORM)); bool isMT32(int sound); bool isMIDI(int sound); + bool supportsPercussion(int sound); int get_queue_sound_status(int sound) const; void handle_marker(uint id, byte data); int get_channel_volume(uint a); @@ -516,7 +518,7 @@ protected: public: // IMuse interface void pause(bool paused); - int save_or_load(Serializer *ser, ScummEngine *scumm); + int save_or_load(Serializer *ser, ScummEngine *scumm, bool fixAfterLoad = true); bool get_sound_active(int sound) const; int32 doCommand(int numargs, int args[]); uint32 property(int prop, uint32 value); diff --git a/engines/scumm/imuse/imuse_part.cpp b/engines/scumm/imuse/imuse_part.cpp index 73e7704469..5e928f3d44 100644 --- a/engines/scumm/imuse/imuse_part.cpp +++ b/engines/scumm/imuse/imuse_part.cpp @@ -27,6 +27,7 @@ #include "common/util.h" #include "scumm/imuse/imuse_internal.h" #include "scumm/saveload.h" +#include "scumm/scumm.h" namespace Scumm { @@ -110,8 +111,19 @@ void Part::saveLoadWithSerializer(Serializer *ser) { } void Part::set_detune(int8 detune) { - _detune_eff = clamp((_detune = detune) + _player->getDetune(), -128, 127); - sendPitchBend(); + // Sam&Max does not have detune, so we just ignore this here. We still get + // this called, since Sam&Max uses the same controller for a different + // purpose. + if (_se->_game_id == GID_SAMNMAX) { +#if 0 + if (_mc) { + _mc->controlChange(17, detune + 0x40); + } +#endif + } else { + _detune_eff = clamp((_detune = detune) + _player->getDetune(), -128, 127); + sendPitchBend(); + } } void Part::pitchBend(int16 value) { @@ -365,7 +377,17 @@ void Part::set_instrument(uint b) { _bank = (byte)(b >> 8); if (_bank) error("Non-zero instrument bank selection. Please report this"); - _instrument.program((byte)b, _player->isMT32()); + // HACK: Horrible hack to allow tracing of program change source. + // The Mac m68k versions of MI2 and Indy4 use a different program "bank" + // when it gets program change events through the iMuse SysEx handler. + // We emulate this by introducing a special instrument, which sets + // the instrument via sysEx_customInstrument. This seems to be + // exclusively used for special sound effects like the "spit" sound. + if (g_scumm->isMacM68kIMuse()) { + _instrument.macSfx(b); + } else { + _instrument.program((byte)b, _player->isMT32()); + } if (clearToTransmit()) _instrument.send(_mc); } diff --git a/engines/scumm/imuse/imuse_player.cpp b/engines/scumm/imuse/imuse_player.cpp index 53ccfb3734..3a9c42a920 100644 --- a/engines/scumm/imuse/imuse_player.cpp +++ b/engines/scumm/imuse/imuse_player.cpp @@ -78,6 +78,7 @@ Player::Player() : _speed(128), _isMT32(false), _isMIDI(false), + _supportsPercussion(false), _se(0), _vol_chan(0) { } @@ -103,6 +104,7 @@ bool Player::startSound(int sound, MidiDriver *midi) { _isMT32 = _se->isMT32(sound); _isMIDI = _se->isMIDI(sound); + _supportsPercussion = _se->supportsPercussion(sound); _parts = NULL; _active = true; @@ -386,6 +388,8 @@ void Player::sysEx(const byte *p, uint16 len) { // SysEx manufacturer 0x97 has been spotted in the // Monkey Island 2 AdLib music, so don't make this a // fatal error. See bug #1481383. + // The Macintosh version of Monkey Island 2 simply + // ignores these SysEx events too. if (a == 0) warning("Unknown SysEx manufacturer 0x00 0x%02X 0x%02X", p[0], p[1]); else @@ -1009,6 +1013,7 @@ void Player::fixAfterLoad() { _parser->jumpToTick(_music_tick); // start_seq_sound already switched tracks _isMT32 = _se->isMT32(_id); _isMIDI = _se->isMIDI(_id); + _supportsPercussion = _se->supportsPercussion(_id); } } diff --git a/engines/scumm/imuse/instrument.cpp b/engines/scumm/imuse/instrument.cpp index 11bb4e7605..61c73b1e2d 100644 --- a/engines/scumm/imuse/instrument.cpp +++ b/engines/scumm/imuse/instrument.cpp @@ -278,6 +278,21 @@ private: byte _instrument[23]; }; +class Instrument_MacSfx : public InstrumentInternal { +private: + byte _program; + +public: + Instrument_MacSfx(byte program); + Instrument_MacSfx(Serializer *s); + void saveOrLoad(Serializer *s); + void send(MidiChannel *mc); + void copy_to(Instrument *dest) { dest->macSfx(_program); } + bool is_valid() { + return (_program < 128); + } +}; + //////////////////////////////////////// // // Instrument class members @@ -326,6 +341,14 @@ void Instrument::pcspk(const byte *instrument) { _instrument = new Instrument_PcSpk(instrument); } +void Instrument::macSfx(byte prog) { + clear(); + if (prog > 127) + return; + _type = itMacSfx; + _instrument = new Instrument_MacSfx(prog); +} + void Instrument::saveOrLoad(Serializer *s) { if (s->isSaving()) { s->saveByte(_type); @@ -349,6 +372,9 @@ void Instrument::saveOrLoad(Serializer *s) { case itPcSpk: _instrument = new Instrument_PcSpk(s); break; + case itMacSfx: + _instrument = new Instrument_MacSfx(s); + break; default: warning("No known instrument classification #%d", (int)_type); _type = itNone; @@ -528,4 +554,38 @@ void Instrument_PcSpk::send(MidiChannel *mc) { mc->sysEx_customInstrument('SPK ', (byte *)&_instrument); } +//////////////////////////////////////// +// +// Instrument_MacSfx class members +// +//////////////////////////////////////// + +Instrument_MacSfx::Instrument_MacSfx(byte program) : + _program(program) { + if (program > 127) { + _program = 255; + } +} + +Instrument_MacSfx::Instrument_MacSfx(Serializer *s) { + _program = 255; + if (!s->isSaving()) { + saveOrLoad(s); + } +} + +void Instrument_MacSfx::saveOrLoad(Serializer *s) { + if (s->isSaving()) { + s->saveByte(_program); + } else { + _program = s->loadByte(); + } +} + +void Instrument_MacSfx::send(MidiChannel *mc) { + if (_program > 127) { + return; + } + mc->sysEx_customInstrument('MAC ', &_program); +} } // End of namespace Scumm diff --git a/engines/scumm/imuse/instrument.h b/engines/scumm/imuse/instrument.h index a855c64155..7e09e86fa5 100644 --- a/engines/scumm/imuse/instrument.h +++ b/engines/scumm/imuse/instrument.h @@ -52,7 +52,8 @@ public: itProgram = 1, itAdLib = 2, itRoland = 3, - itPcSpk = 4 + itPcSpk = 4, + itMacSfx = 5 }; Instrument() : _type(0), _instrument(0) { } @@ -72,6 +73,7 @@ public: void adlib(const byte *instrument); void roland(const byte *instrument); void pcspk(const byte *instrument); + void macSfx(byte program); byte getType() { return _type; } bool isValid() { return (_instrument ? _instrument->is_valid() : false); } diff --git a/engines/scumm/imuse/mac_m68k.cpp b/engines/scumm/imuse/mac_m68k.cpp new file mode 100644 index 0000000000..0980ef1fd2 --- /dev/null +++ b/engines/scumm/imuse/mac_m68k.cpp @@ -0,0 +1,514 @@ +/* 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 "scumm/imuse/mac_m68k.h" + +#include "common/util.h" +#include "common/macresman.h" +#include "common/stream.h" + +namespace Scumm { + +MacM68kDriver::MacM68kDriver(Audio::Mixer *mixer) + : MidiDriver_Emulated(mixer) { +} + +MacM68kDriver::~MacM68kDriver() { +} + +int MacM68kDriver::open() { + if (_isOpen) { + return MERR_ALREADY_OPEN; + } + + const int error = MidiDriver_Emulated::open(); + if (error) { + return error; + } + + for (uint i = 0; i < ARRAYSIZE(_channels); ++i) { + _channels[i].init(this, i); + } + + memset(_voiceChannels, 0, sizeof(_voiceChannels)); + _lastUsedVoiceChannel = 0; + + loadAllInstruments(); + + _pitchTable[116] = 1664510; + _pitchTable[117] = 1763487; + _pitchTable[118] = 1868350; + _pitchTable[119] = 1979447; + _pitchTable[120] = 2097152; + _pitchTable[121] = 2221855; + _pitchTable[122] = 2353973; + _pitchTable[123] = 2493948; + _pitchTable[124] = 2642246; + _pitchTable[125] = 2799362; + _pitchTable[126] = 2965820; + _pitchTable[127] = 3142177; + for (int i = 115; i >= 0; --i) { + _pitchTable[i] = _pitchTable[i + 12] / 2; + } + + _volumeTable = new byte[8192]; + for (int i = 0; i < 32; ++i) { + for (int j = 0; j < 256; ++j) { + _volumeTable[i * 256 + j] = ((-128 + j) * _volumeBaseTable[i]) / 127 - 128; + } + } + + _mixBuffer = 0; + _mixBufferLength = 0; + + // We set the output sound type to music here to allow sound volume + // adjustment. The drawback here is that we can not control the music and + // sfx separately here. But the AdLib output has the same issue so it + // should not be that bad. + _mixer->playStream(Audio::Mixer::kMusicSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); + + return 0; +} + +void MacM68kDriver::close() { + if (!_isOpen) { + return; + } + + _mixer->stopHandle(_mixerSoundHandle); + _isOpen = false; + for (InstrumentMap::iterator i = _instruments.begin(); i != _instruments.end(); ++i) { + delete[] i->_value.data; + } + _instruments.clear(); + delete[] _volumeTable; + _volumeTable = 0; + delete[] _mixBuffer; + _mixBuffer = 0; + _mixBufferLength = 0; +} + +void MacM68kDriver::send(uint32 d) { + assert(false); +} + +void MacM68kDriver::sysEx_customInstrument(byte channel, uint32 type, const byte *instr) { + assert(false); +} + +MidiChannel *MacM68kDriver::allocateChannel() { + for (uint i = 0; i < ARRAYSIZE(_channels); ++i) { + if (_channels[i].allocate()) { + return &_channels[i]; + } + } + + return 0; +} + +MacM68kDriver::Instrument MacM68kDriver::getInstrument(int idx) const { + InstrumentMap::const_iterator i = _instruments.find(idx); + if (i != _instruments.end()) { + return i->_value; + } else { + return _defaultInstrument; + } +} + +void MacM68kDriver::generateSamples(int16 *buf, int len) { + int silentChannels = 0; + + if (_mixBufferLength < len) { + delete[] _mixBuffer; + + _mixBufferLength = len; + _mixBuffer = new int[_mixBufferLength]; + assert(_mixBuffer); + } + memset(_mixBuffer, 0, sizeof(int) * _mixBufferLength); + + for (int i = 0; i < kChannelCount; ++i) { + OutputChannel &out = _voiceChannels[i].out; + if (out.isFinished) { + ++silentChannels; + continue; + } + + byte *volumeTable = &_volumeTable[(out.volume / 4) * 256]; + int *buffer = _mixBuffer; + + int samplesLeft = len; + while (samplesLeft) { + out.subPos += out.pitchModifier; + while (out.subPos >= 0x10000) { + out.subPos -= 0x10000; + out.instrument++; + } + + if (out.instrument >= out.end) { + if (!out.start) { + break; + } + + out.instrument = out.start; + out.subPos = 0; + } + + *buffer++ += volumeTable[*out.instrument]; + --samplesLeft; + } + + if (samplesLeft) { + out.isFinished = true; + while (samplesLeft--) { + *buffer++ += 0x80; + } + } + } + + const int *buffer = _mixBuffer; + const int silenceAdd = silentChannels << 7; + while (len--) { + *buf++ = (((*buffer++ + silenceAdd) >> 3) << 8) ^ 0x8000; + } +} + +void MacM68kDriver::loadAllInstruments() { + Common::MacResManager resource; + if (resource.open("iMUSE Setups")) { + if (!resource.hasResFork()) { + error("MacM68kDriver::loadAllInstruments: \"iMUSE Setups\" loaded, but no resource fork present"); + } + + for (int i = 0x3E7; i < 0x468; ++i) { + Common::SeekableReadStream *stream = resource.getResource(MKTAG('s', 'n', 'd', ' '), i); + if (stream) { + addInstrument(i, stream); + delete stream; + } + } + + for (int i = 0x7D0; i < 0x8D0; ++i) { + Common::SeekableReadStream *stream = resource.getResource(MKTAG('s', 'n', 'd', ' '), i); + if (stream) { + addInstrument(i, stream); + delete stream; + } + } + + InstrumentMap::iterator inst = _instruments.find(kDefaultInstrument); + if (inst != _instruments.end()) { + _defaultInstrument = inst->_value; + } else { + error("MacM68kDriver::loadAllInstruments: Could not load default instrument"); + } + } else { + error("MacM68kDriver::loadAllInstruments: Could not load \"iMUSE Setups\""); + } +} + +void MacM68kDriver::addInstrument(int idx, Common::SeekableReadStream *data) { + // We parse the "SND" files manually here, since we need special data + // from their header and need to work on them raw while mixing. + data->skip(2); + int count = data->readUint16BE(); + data->skip(2 * (3 * count)); + count = data->readUint16BE(); + data->skip(2 * (4 * count)); + + Instrument inst; + // Skip (optional) pointer to data + data->skip(4); + inst.length = data->readUint32BE(); + inst.sampleRate = data->readUint32BE(); + inst.loopStart = data->readUint32BE(); + inst.loopEnd = data->readUint32BE(); + // Skip encoding + data->skip(1); + inst.baseFrequency = data->readByte(); + + inst.data = new byte[inst.length]; + assert(inst.data); + data->read(inst.data, inst.length); + _instruments[idx] = inst; +} + +void MacM68kDriver::setPitch(OutputChannel *out, int frequency) { + out->frequency = frequency; + out->isFinished = false; + + const int pitchIdx = (frequency >> 7) + 60 - out->baseFrequency; + assert(pitchIdx >= 0); + + const int low7Bits = frequency & 0x7F; + if (low7Bits) { + out->pitchModifier = _pitchTable[pitchIdx] + (((_pitchTable[pitchIdx + 1] - _pitchTable[pitchIdx]) * low7Bits) >> 7); + } else { + out->pitchModifier = _pitchTable[pitchIdx]; + } +} + +void MacM68kDriver::VoiceChannel::off() { + if (out.start) { + out.isFinished = true; + } + + part->removeVoice(this); + part = 0; +} + +void MacM68kDriver::MidiChannel_MacM68k::release() { + _allocated = false; + while (_voice) { + _voice->off(); + } +} + +void MacM68kDriver::MidiChannel_MacM68k::send(uint32 b) { + uint8 type = b & 0xF0; + uint8 p1 = (b >> 8) & 0xFF; + uint8 p2 = (b >> 16) & 0xFF; + + switch (type) { + case 0x80: + noteOff(p1); + break; + + case 0x90: + if (p2) { + noteOn(p1, p2); + } else { + noteOff(p1); + } + break; + + case 0xB0: + controlChange(p1, p2); + break; + + case 0xE0: + pitchBend((p1 | (p2 << 7)) - 0x2000); + break; + + default: + break; + } +} + +void MacM68kDriver::MidiChannel_MacM68k::noteOff(byte note) { + for (VoiceChannel *i = _voice; i; i = i->next) { + if (i->note == note) { + if (_sustain) { + i->sustainNoteOff = true; + } else { + i->off(); + } + } + } +} + +void MacM68kDriver::MidiChannel_MacM68k::noteOn(byte note, byte velocity) { + // Do not start a not unless there is an instrument set up + if (!_instrument.data) { + return; + } + + // Allocate a voice channel + VoiceChannel *voice = _owner->allocateVoice(_priority); + if (!voice) { + return; + } + addVoice(voice); + + voice->note = note; + // This completly ignores the note's volume, but is in accordance + // to the original. + voice->out.volume = _volume; + + // Set up the instrument data + voice->out.baseFrequency = _instrument.baseFrequency; + voice->out.soundStart = _instrument.data; + voice->out.soundEnd = _instrument.data + _instrument.length; + if (_instrument.loopEnd && _instrument.loopEnd - 12 > _instrument.loopStart) { + voice->out.loopStart = _instrument.data + _instrument.loopStart; + voice->out.loopEnd = _instrument.data + _instrument.loopEnd; + } else { + voice->out.loopStart = 0; + voice->out.loopEnd = voice->out.soundEnd; + } + + voice->out.start = voice->out.loopStart; + voice->out.end = voice->out.loopEnd; + + // Set up the pitch + _owner->setPitch(&voice->out, (note << 7) + _pitchBend); + + // Set up the sample position + voice->out.instrument = voice->out.soundStart; + voice->out.subPos = 0; +} + +void MacM68kDriver::MidiChannel_MacM68k::programChange(byte program) { + _instrument = _owner->getInstrument(program + kProgramChangeBase); +} + +void MacM68kDriver::MidiChannel_MacM68k::pitchBend(int16 bend) { + _pitchBend = (bend * _pitchBendFactor) >> 6; + for (VoiceChannel *i = _voice; i; i = i->next) { + _owner->setPitch(&i->out, (i->note << 7) + _pitchBend); + } +} + +void MacM68kDriver::MidiChannel_MacM68k::controlChange(byte control, byte value) { + switch (control) { + // volume change + case 7: + _volume = value; + for (VoiceChannel *i = _voice; i; i = i->next) { + i->out.volume = value; + i->out.isFinished = false; + } + break; + + // sustain + case 64: + _sustain = value; + if (!_sustain) { + for (VoiceChannel *i = _voice; i; i = i->next) { + if (i->sustainNoteOff) { + i->off(); + } + } + } + break; + + // all notes off + case 123: + for (VoiceChannel *i = _voice; i; i = i->next) { + i->off(); + } + break; + + default: + break; + } +} + +void MacM68kDriver::MidiChannel_MacM68k::pitchBendFactor(byte value) { + _pitchBendFactor = value; +} + +void MacM68kDriver::MidiChannel_MacM68k::priority(byte value) { + _priority = value; +} + +void MacM68kDriver::MidiChannel_MacM68k::sysEx_customInstrument(uint32 type, const byte *instr) { + assert(instr); + if (type == 'MAC ') { + _instrument = _owner->getInstrument(*instr + kSysExBase); + } +} + +void MacM68kDriver::MidiChannel_MacM68k::init(MacM68kDriver *owner, byte channel) { + _owner = owner; + _number = channel; + _allocated = false; +} + +bool MacM68kDriver::MidiChannel_MacM68k::allocate() { + if (_allocated) { + return false; + } + + _allocated = true; + _voice = 0; + _priority = 0; + memset(&_instrument, 0, sizeof(_instrument)); + _pitchBend = 0; + _pitchBendFactor = 0; + _volume = 0; + return true; +} + +void MacM68kDriver::MidiChannel_MacM68k::addVoice(VoiceChannel *voice) { + voice->next = _voice; + voice->prev = 0; + voice->part = this; + if (_voice) { + _voice->prev = voice; + } + _voice = voice; +} + +void MacM68kDriver::MidiChannel_MacM68k::removeVoice(VoiceChannel *voice) { + VoiceChannel *i = _voice; + while (i && i != voice) { + i = i->next; + } + + if (i) { + if (i->next) { + i->next->prev = i->prev; + } + + if (i->prev) { + i->prev->next = i->next; + } else { + _voice = i->next; + } + } +} + +MacM68kDriver::VoiceChannel *MacM68kDriver::allocateVoice(int priority) { + VoiceChannel *channel = 0; + for (int i = 0; i < kChannelCount; ++i) { + if (++_lastUsedVoiceChannel == kChannelCount) { + _lastUsedVoiceChannel = 0; + } + + VoiceChannel *cur = &_voiceChannels[_lastUsedVoiceChannel]; + if (!cur->part) { + memset(cur, 0, sizeof(*cur)); + return cur; + } else if (!cur->next) { + if (cur->part->_priority <= priority) { + priority = cur->part->_priority; + channel = cur; + } + } + } + + if (channel) { + channel->off(); + memset(channel, 0, sizeof(*channel)); + } + + return channel; +} + +const int MacM68kDriver::_volumeBaseTable[32] = { + 0, 0, 1, 1, 2, 3, 5, 6, + 8, 11, 13, 16, 19, 22, 26, 30, + 34, 38, 43, 48, 53, 58, 64, 70, + 76, 83, 89, 96, 104, 111, 119, 127 +}; + +} // End of namespace Scumm diff --git a/engines/scumm/imuse/mac_m68k.h b/engines/scumm/imuse/mac_m68k.h new file mode 100644 index 0000000000..59e2f68b9b --- /dev/null +++ b/engines/scumm/imuse/mac_m68k.h @@ -0,0 +1,177 @@ +/* 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 SCUMM_IMUSE_MAC_M68K_H +#define SCUMM_IMUSE_MAC_M68K_H + +#include "audio/softsynth/emumidi.h" + +#include "common/hashmap.h" + +namespace Common { +class SeekableReadStream; +} + +namespace Scumm { + +class MacM68kDriver : public MidiDriver_Emulated { + friend class MidiChannel_MacM68k; +public: + MacM68kDriver(Audio::Mixer *mixer); + ~MacM68kDriver(); + + virtual int open(); + virtual void close(); + + virtual void send(uint32 d); + virtual void sysEx_customInstrument(byte channel, uint32 type, const byte *instr); + + virtual MidiChannel *allocateChannel(); + virtual MidiChannel *getPercussionChannel() { return 0; } + + virtual bool isStereo() const { return false; } + virtual int getRate() const { + // The original is using a frequency of approx. 22254.54546 here. + // To be precise it uses the 16.16 fixed point value 0x56EE8BA3. + return 22254; + } + +protected: + virtual void generateSamples(int16 *buf, int len); + virtual void onTimer() {} + +private: + int *_mixBuffer; + int _mixBufferLength; + + struct Instrument { + uint length; + uint sampleRate; + uint loopStart; + uint loopEnd; + int baseFrequency; + + byte *data; + }; + + enum { + kDefaultInstrument = 0x3E7, + kProgramChangeBase = 0x3E8, + kSysExBase = 0x7D0 + }; + + Instrument getInstrument(int idx) const; + typedef Common::HashMap<int, Instrument> InstrumentMap; + InstrumentMap _instruments; + Instrument _defaultInstrument; + void loadAllInstruments(); + void addInstrument(int idx, Common::SeekableReadStream *data); + + struct OutputChannel { + int pitchModifier; + + const byte *instrument; + uint subPos; + + const byte *start; + const byte *end; + + const byte *soundStart; + const byte *soundEnd; + const byte *loopStart; + const byte *loopEnd; + + int frequency; + int volume; + + bool isFinished; + + int baseFrequency; + }; + + void setPitch(OutputChannel *out, int frequency); + int _pitchTable[128]; + + byte *_volumeTable; + static const int _volumeBaseTable[32]; + + class MidiChannel_MacM68k; + + struct VoiceChannel { + MidiChannel_MacM68k *part; + VoiceChannel *prev, *next; + int channel; + int note; + bool sustainNoteOff; + OutputChannel out; + + void off(); + }; + + class MidiChannel_MacM68k : public MidiChannel { + friend class MacM68kDriver; + public: + virtual MidiDriver *device() { return _owner; } + virtual byte getNumber() { return _number; } + virtual void release(); + + virtual void send(uint32 b); + virtual void noteOff(byte note); + virtual void noteOn(byte note, byte velocity); + virtual void programChange(byte program); + virtual void pitchBend(int16 bend); + virtual void controlChange(byte control, byte value); + virtual void pitchBendFactor(byte value); + virtual void priority(byte value); + virtual void sysEx_customInstrument(uint32 type, const byte *instr); + + void init(MacM68kDriver *owner, byte channel); + bool allocate(); + + void addVoice(VoiceChannel *voice); + void removeVoice(VoiceChannel *voice); + private: + MacM68kDriver *_owner; + bool _allocated; + int _number; + + VoiceChannel *_voice; + int _priority; + int _sustain; + Instrument _instrument; + int _pitchBend; + int _pitchBendFactor; + int _volume; + }; + + MidiChannel_MacM68k _channels[32]; + + enum { + kChannelCount = 8 + }; + VoiceChannel _voiceChannels[kChannelCount]; + int _lastUsedVoiceChannel; + VoiceChannel *allocateVoice(int priority); +}; + +} // End of namespace Scumm + +#endif diff --git a/engines/scumm/imuse/sysex_scumm.cpp b/engines/scumm/imuse/sysex_scumm.cpp index 85ffc86f47..8f230ebac3 100644 --- a/engines/scumm/imuse/sysex_scumm.cpp +++ b/engines/scumm/imuse/sysex_scumm.cpp @@ -71,7 +71,7 @@ void sysexHandler_Scumm(Player *player, const byte *msg, uint16 len) { part->set_pri(buf[2]); part->volume(buf[3]); part->set_pan(buf[4]); - part->_percussion = player->_isMIDI ? ((buf[5] & 0x80) > 0) : false; + part->_percussion = player->_supportsPercussion ? ((buf[5] & 0x80) > 0) : false; part->set_transpose(buf[5]); part->set_detune(buf[6]); part->pitchBendFactor(buf[7]); diff --git a/engines/scumm/midiparser_ro.cpp b/engines/scumm/midiparser_ro.cpp index 1a31d1ca82..8549a9262d 100644 --- a/engines/scumm/midiparser_ro.cpp +++ b/engines/scumm/midiparser_ro.cpp @@ -62,13 +62,13 @@ void MidiParser_RO::parseNextEvent (EventInfo &info) { info.delta = 0; do { - info.start = _position._play_pos; - info.event = *(_position._play_pos++); + info.start = _position._playPos; + info.event = *(_position._playPos++); if (info.command() == 0xA) { ++_lastMarkerCount; info.event = 0xF0; } else if (info.event == 0xF0 || info.event == 0xF1) { - byte delay = *(_position._play_pos++); + byte delay = *(_position._playPos++); info.delta += delay; if (info.event == 0xF1) { // This event is, as far as we have been able @@ -95,16 +95,16 @@ void MidiParser_RO::parseNextEvent (EventInfo &info) { if (info.event < 0x80) return; - _position._running_status = info.event; + _position._runningStatus = info.event; switch (info.command()) { case 0xC: - info.basic.param1 = *(_position._play_pos++); + info.basic.param1 = *(_position._playPos++); info.basic.param2 = 0; break; case 0x8: case 0x9: case 0xB: - info.basic.param1 = *(_position._play_pos++); - info.basic.param2 = *(_position._play_pos++); + info.basic.param1 = *(_position._playPos++); + info.basic.param2 = *(_position._playPos++); if (info.command() == 0x9 && info.basic.param2 == 0) info.event = info.channel() | 0x80; info.length = 0; @@ -133,7 +133,7 @@ bool MidiParser_RO::loadMusic (byte *data, uint32 size) { return false; } - _num_tracks = 1; + _numTracks = 1; _ppqn = 120; _tracks[0] = pos + 2; _markerCount = _lastMarkerCount = 0; diff --git a/engines/scumm/module.mk b/engines/scumm/module.mk index 1f219f5187..28884d7f78 100644 --- a/engines/scumm/module.mk +++ b/engines/scumm/module.mk @@ -27,6 +27,7 @@ MODULE_OBJS := \ imuse/imuse_part.o \ imuse/imuse_player.o \ imuse/instrument.o \ + imuse/mac_m68k.o \ imuse/pcspk.o \ imuse/sysex_samnmax.o \ imuse/sysex_scumm.o \ @@ -35,6 +36,7 @@ MODULE_OBJS := \ object.o \ palette.o \ player_apple2.o \ + player_mac.o \ player_mod.o \ player_nes.o \ player_pce.o \ @@ -46,7 +48,9 @@ MODULE_OBJS := \ player_v2base.o \ player_v2cms.o \ player_v3a.o \ + player_v3m.o \ player_v4a.o \ + player_v5m.o \ resource_v2.o \ resource_v3.o \ resource_v4.o \ diff --git a/engines/scumm/music.h b/engines/scumm/music.h index a527c77b72..9fd14d830e 100644 --- a/engines/scumm/music.h +++ b/engines/scumm/music.h @@ -24,6 +24,7 @@ #define SCUMM_MUSIC_H #include "common/scummsys.h" +#include "engines/scumm/saveload.h" namespace Scumm { @@ -78,6 +79,11 @@ public: * @return the music timer */ virtual int getMusicTimer() { return 0; } + + /** + * Save or load the music state. + */ + virtual void saveLoadWithSerializer(Serializer *ser) {} }; } // End of namespace Scumm diff --git a/engines/scumm/object.cpp b/engines/scumm/object.cpp index 399cd91324..ed77a863cd 100644 --- a/engines/scumm/object.cpp +++ b/engines/scumm/object.cpp @@ -335,7 +335,7 @@ int ScummEngine::whereIsObject(int object) const { return WIO_NOT_FOUND; if ((_game.version != 0 || OBJECT_V0_TYPE(object) == 0) && - _objectOwnerTable[object] != OF_OWNER_ROOM) + _objectOwnerTable[object] != OF_OWNER_ROOM) { for (i = 0; i < _numInventory; i++) if (_inventory[i] == object) @@ -433,10 +433,14 @@ void ScummEngine::getObjectXYPos(int object, int &x, int &y, int &dir) { y = od.y_pos + (int16)READ_LE_UINT16(&imhd->old.hotspot[state].y); } } else if (_game.version <= 2) { - if (od.actordir) { - x = od.walk_x; - y = od.walk_y; - } else { + x = od.walk_x; + y = od.walk_y; + + // Adjust x, y when no actor direction is set, but only perform this + // adjustment for V0 games (e.g. MM C64), otherwise certain scenes in + // newer games are affected as well (e.g. the interior of the Shuttle + // Bus scene in Zak V2, where no actor is present). Refer to bug #3526089. + if (!od.actordir && _game.version == 0) { x = od.x_pos + od.width / 2; y = od.y_pos + od.height / 2; } @@ -1225,7 +1229,7 @@ byte *ScummEngine::getOBCDFromObject(int obj, bool v0CheckInventory) { byte *ptr; if ((_game.version != 0 || OBJECT_V0_TYPE(obj) == 0) && - _objectOwnerTable[obj] != OF_OWNER_ROOM) + _objectOwnerTable[obj] != OF_OWNER_ROOM) { if (_game.version == 0 && !v0CheckInventory) return 0; diff --git a/engines/scumm/player_apple2.cpp b/engines/scumm/player_apple2.cpp index a8e150caa9..58e4f78a94 100644 --- a/engines/scumm/player_apple2.cpp +++ b/engines/scumm/player_apple2.cpp @@ -61,8 +61,8 @@ public: private: void _update(int interval /*a*/, int count /*y*/) { // D076 - assert(interval > 0); // 0 == 256? - assert(count > 0); // 0 == 256? + assert(interval > 0); // 0 == 256? + assert(count > 0); // 0 == 256? for (; count >= 0; --count) { _player->speakerToggle(); @@ -99,7 +99,7 @@ public: ++_pos; return false; - } + } return true; } @@ -112,7 +112,7 @@ private: assert(interval > 0); // 0 == 256? int a = (interval >> 3) + count; - for (int y = a; y > 0; --y) { + for (int y = a; y > 0; --y) { _player->generateSamples(1292 - 5*interval); _player->speakerToggle(); @@ -206,7 +206,7 @@ private: _bitmask1 = 0x3; _bitmask2 = 0x3; - + _updateInterval2 = param0; if (_updateInterval2 == 0) _bitmask2 = 0x0; @@ -234,9 +234,9 @@ private: if (_updateRemain2 == 0) { _updateRemain2 = _updateInterval2; - // use only first voice's data (bitmask1) if both voices are triggered + // use only first voice's data (bitmask1) if both voices are triggered if (_updateRemain1 != 0) { - _speakerShiftReg ^= _bitmask2; + _speakerShiftReg ^= _bitmask2; } } @@ -256,7 +256,7 @@ private: protected: const byte *_params; - + byte _updateRemain1; byte _updateRemain2; @@ -309,7 +309,7 @@ private: for (int i = count; i > 0; --i) { _player->generateSamples(10 + 5*interval); _player->speakerToggle(); - + _player->generateSamples(5 + 5*interval); _player->speakerToggle(); } @@ -332,20 +332,20 @@ private: // LD000[loc] ^ LD00A[loc] const byte AppleII_SoundFunction5_Noise::_noiseTable[256] = { - 0x65, 0x1b, 0xda, 0x11, 0x61, 0xe5, 0x77, 0x57, 0x92, 0xc8, 0x51, 0x1c, 0xd4, 0x91, 0x62, 0x63, - 0x00, 0x38, 0x57, 0xd5, 0x18, 0xd8, 0xdc, 0x40, 0x03, 0x86, 0xd3, 0x2f, 0x10, 0x11, 0xd8, 0x3c, - 0xbe, 0x00, 0x19, 0xc5, 0xd2, 0xc3, 0xca, 0x34, 0x00, 0x28, 0xbf, 0xb9, 0x18, 0x20, 0x01, 0xcc, - 0xda, 0x08, 0xbc, 0x75, 0x7c, 0xb0, 0x8d, 0xe0, 0x09, 0x18, 0xbf, 0x5d, 0xe9, 0x8c, 0x75, 0x64, + 0x65, 0x1b, 0xda, 0x11, 0x61, 0xe5, 0x77, 0x57, 0x92, 0xc8, 0x51, 0x1c, 0xd4, 0x91, 0x62, 0x63, + 0x00, 0x38, 0x57, 0xd5, 0x18, 0xd8, 0xdc, 0x40, 0x03, 0x86, 0xd3, 0x2f, 0x10, 0x11, 0xd8, 0x3c, + 0xbe, 0x00, 0x19, 0xc5, 0xd2, 0xc3, 0xca, 0x34, 0x00, 0x28, 0xbf, 0xb9, 0x18, 0x20, 0x01, 0xcc, + 0xda, 0x08, 0xbc, 0x75, 0x7c, 0xb0, 0x8d, 0xe0, 0x09, 0x18, 0xbf, 0x5d, 0xe9, 0x8c, 0x75, 0x64, 0xe5, 0xb5, 0x5d, 0xe0, 0xb7, 0x7d, 0xe9, 0x8c, 0x55, 0x65, 0xc5, 0xb5, 0x5d, 0xd8, 0x09, 0x0d, - 0x64, 0xf0, 0xf0, 0x08, 0x63, 0x03, 0x00, 0x55, 0x35, 0xc0, 0x00, 0x20, 0x74, 0xa5, 0x1e, 0xe3, - 0x00, 0x06, 0x3c, 0x52, 0xd1, 0x70, 0xd0, 0x57, 0x02, 0xf0, 0x00, 0xb6, 0xfc, 0x02, 0x11, 0x9a, - 0x3b, 0xc8, 0x38, 0xdf, 0x1a, 0xb0, 0xd1, 0xb8, 0xd0, 0x18, 0x8a, 0x4a, 0xea, 0x1b, 0x12, 0x5d, - 0x29, 0x58, 0xd8, 0x43, 0xb8, 0x2d, 0xd2, 0x61, 0x10, 0x3c, 0x0c, 0x5d, 0x1b, 0x61, 0x10, 0x3c, + 0x64, 0xf0, 0xf0, 0x08, 0x63, 0x03, 0x00, 0x55, 0x35, 0xc0, 0x00, 0x20, 0x74, 0xa5, 0x1e, 0xe3, + 0x00, 0x06, 0x3c, 0x52, 0xd1, 0x70, 0xd0, 0x57, 0x02, 0xf0, 0x00, 0xb6, 0xfc, 0x02, 0x11, 0x9a, + 0x3b, 0xc8, 0x38, 0xdf, 0x1a, 0xb0, 0xd1, 0xb8, 0xd0, 0x18, 0x8a, 0x4a, 0xea, 0x1b, 0x12, 0x5d, + 0x29, 0x58, 0xd8, 0x43, 0xb8, 0x2d, 0xd2, 0x61, 0x10, 0x3c, 0x0c, 0x5d, 0x1b, 0x61, 0x10, 0x3c, 0x0a, 0x5d, 0x1d, 0x61, 0x10, 0x3c, 0x0b, 0x19, 0x88, 0x21, 0xc0, 0x21, 0x07, 0x00, 0x65, 0x62, - 0x08, 0xe9, 0x36, 0x40, 0x20, 0x41, 0x06, 0x00, 0x20, 0x00, 0x00, 0xed, 0xa3, 0x00, 0x88, 0x06, - 0x98, 0x01, 0x5d, 0x7f, 0x02, 0x1d, 0x78, 0x03, 0x60, 0xcb, 0x3a, 0x01, 0xbd, 0x78, 0x02, 0x5d, - 0x7e, 0x03, 0x1d, 0xf5, 0xa6, 0x40, 0x81, 0xb4, 0xd0, 0x8d, 0xd3, 0xd0, 0x6d, 0xd5, 0x61, 0x48, - 0x61, 0x4d, 0xd1, 0xc8, 0xb1, 0xd8, 0x69, 0xff, 0x61, 0xd9, 0xed, 0xa0, 0xfe, 0x19, 0x91, 0x37, + 0x08, 0xe9, 0x36, 0x40, 0x20, 0x41, 0x06, 0x00, 0x20, 0x00, 0x00, 0xed, 0xa3, 0x00, 0x88, 0x06, + 0x98, 0x01, 0x5d, 0x7f, 0x02, 0x1d, 0x78, 0x03, 0x60, 0xcb, 0x3a, 0x01, 0xbd, 0x78, 0x02, 0x5d, + 0x7e, 0x03, 0x1d, 0xf5, 0xa6, 0x40, 0x81, 0xb4, 0xd0, 0x8d, 0xd3, 0xd0, 0x6d, 0xd5, 0x61, 0x48, + 0x61, 0x4d, 0xd1, 0xc8, 0xb1, 0xd8, 0x69, 0xff, 0x61, 0xd9, 0xed, 0xa0, 0xfe, 0x19, 0x91, 0x37, 0x19, 0x37, 0x00, 0xf1, 0x00, 0x01, 0x1f, 0x00, 0xad, 0xc1, 0x01, 0x01, 0x2e, 0x00, 0x40, 0xc6, 0x7a, 0x9b, 0x95, 0x43, 0xfc, 0x18, 0xd2, 0x9e, 0x2a, 0x5a, 0x4b, 0x2a, 0xb6, 0x87, 0x30, 0x6c }; @@ -394,20 +394,20 @@ void Player_AppleII::startSound(int nr) { case 0: // empty (nothing to play) resetState(); return; - case 1: - _soundFunc = new AppleII_SoundFunction1_FreqUpDown(); + case 1: + _soundFunc = new AppleII_SoundFunction1_FreqUpDown(); break; - case 2: - _soundFunc = new AppleII_SoundFunction2_SymmetricWave(); + case 2: + _soundFunc = new AppleII_SoundFunction2_SymmetricWave(); break; - case 3: - _soundFunc = new AppleII_SoundFunction3_AsymmetricWave(); + case 3: + _soundFunc = new AppleII_SoundFunction3_AsymmetricWave(); break; - case 4: - _soundFunc = new AppleII_SoundFunction4_Polyphone(); + case 4: + _soundFunc = new AppleII_SoundFunction4_Polyphone(); break; - case 5: - _soundFunc = new AppleII_SoundFunction5_Noise(); + case 5: + _soundFunc = new AppleII_SoundFunction5_Noise(); break; } _soundFunc->init(this, _params); @@ -484,7 +484,7 @@ int Player_AppleII::readBuffer(int16 *buffer, const int numSamples) { // toggle speaker on/off void Player_AppleII::speakerToggle() { - _speakerState ^= 0x1; + _speakerState ^= 0x1; } void Player_AppleII::generateSamples(int cycles) { @@ -492,8 +492,8 @@ void Player_AppleII::generateSamples(int cycles) { } void Player_AppleII::wait(int interval, int count /*y*/) { - assert(count > 0); // 0 == 256? - assert(interval > 0); // 0 == 256? + assert(count > 0); // 0 == 256? + assert(interval > 0); // 0 == 256? generateSamples(11 + count*(8 + 5 * interval)); } diff --git a/engines/scumm/player_apple2.h b/engines/scumm/player_apple2.h index b4a7d409fb..e1ec9d8946 100644 --- a/engines/scumm/player_apple2.h +++ b/engines/scumm/player_apple2.h @@ -36,7 +36,7 @@ namespace Scumm { class ScummEngine; /* - * Optimized for use with periodical read/write phases when the buffer + * Optimized for use with periodical read/write phases when the buffer * is filled in a write phase and completely read in a read phase. * The growing strategy is optimized for repeated small (e.g. 2 bytes) * single writes resulting in large buffers @@ -133,7 +133,7 @@ static const double APPLEII_CPU_CLOCK = 1020484.5; // ~ 1.02 MHz /* * Converts the 1-bit speaker state values into audio samples. - * This is done by aggregation of the speaker states at each + * This is done by aggregation of the speaker states at each * CPU cycle in a sampling period into an audio sample. */ class SampleConverter { @@ -144,7 +144,7 @@ private: } public: - SampleConverter() : + SampleConverter() : _cyclesPerSampleFP(0), _missingCyclesFP(0), _sampleCyclesSumFP(0), @@ -156,7 +156,7 @@ public: void reset() { _missingCyclesFP = 0; _sampleCyclesSumFP = 0; - _buffer.clear(); + _buffer.clear(); } uint32 availableSize() const { @@ -245,7 +245,7 @@ public: virtual void setMusicVolume(int vol) { _sampleConverter.setMusicVolume(vol); } void setSampleRate(int rate) { _sampleRate = rate; - _sampleConverter.setSampleRate(rate); + _sampleConverter.setSampleRate(rate); } virtual void startSound(int sound); virtual void stopSound(int sound); diff --git a/engines/scumm/player_mac.cpp b/engines/scumm/player_mac.cpp new file mode 100644 index 0000000000..c16c85bff3 --- /dev/null +++ b/engines/scumm/player_mac.cpp @@ -0,0 +1,415 @@ +/* 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 "common/macresman.h" +#include "common/translation.h" +#include "engines/engine.h" +#include "gui/message.h" +#include "scumm/player_mac.h" +#include "scumm/resource.h" +#include "scumm/scumm.h" +#include "scumm/imuse/imuse.h" + +namespace Scumm { + +Player_Mac::Player_Mac(ScummEngine *scumm, Audio::Mixer *mixer, int numberOfChannels, int channelMask, bool fadeNoteEnds) + : _vm(scumm), + _mixer(mixer), + _sampleRate(_mixer->getOutputRate()), + _soundPlaying(-1), + _numberOfChannels(numberOfChannels), + _channelMask(channelMask), + _fadeNoteEnds(fadeNoteEnds) { + assert(scumm); + assert(mixer); +} + +void Player_Mac::init() { + _channel = new Player_Mac::Channel[_numberOfChannels]; + + int i; + + for (i = 0; i < _numberOfChannels; i++) { + _channel[i]._looped = false; + _channel[i]._length = 0; + _channel[i]._data = NULL; + _channel[i]._pos = 0; + _channel[i]._pitchModifier = 0; + _channel[i]._velocity = 0; + _channel[i]._remaining = 0; + _channel[i]._notesLeft = false; + _channel[i]._instrument._data = NULL; + _channel[i]._instrument._size = 0; + _channel[i]._instrument._rate = 0; + _channel[i]._instrument._loopStart = 0; + _channel[i]._instrument._loopEnd = 0; + _channel[i]._instrument._baseFreq = 0; + _channel[i]._instrument._pos = 0; + _channel[i]._instrument._subPos = 0; + } + + _pitchTable[116] = 1664510; + _pitchTable[117] = 1763487; + _pitchTable[118] = 1868350; + _pitchTable[119] = 1979447; + _pitchTable[120] = 2097152; + _pitchTable[121] = 2221855; + _pitchTable[122] = 2353973; + _pitchTable[123] = 2493948; + _pitchTable[124] = 2642246; + _pitchTable[125] = 2799362; + _pitchTable[126] = 2965820; + _pitchTable[127] = 3142177; + for (i = 115; i >= 0; --i) { + _pitchTable[i] = _pitchTable[i + 12] / 2; + } + + setMusicVolume(255); + + if (!checkMusicAvailable()) { + return; + } + + _mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); +} + +Player_Mac::~Player_Mac() { + Common::StackLock lock(_mutex); + _mixer->stopHandle(_soundHandle); + stopAllSounds_Internal(); + delete[] _channel; +} + +void Player_Mac::saveLoadWithSerializer(Serializer *ser) { + Common::StackLock lock(_mutex); + if (ser->getVersion() < VER(94)) { + if (_vm->_game.id == GID_MONKEY && ser->isLoading()) { + IMuse *dummyImuse = IMuse::create(_vm->_system, NULL, NULL); + dummyImuse->save_or_load(ser, _vm, false); + delete dummyImuse; + } + } else { + static const SaveLoadEntry musicEntries[] = { + MKLINE(Player_Mac, _sampleRate, sleUint32, VER(94)), + MKLINE(Player_Mac, _soundPlaying, sleInt16, VER(94)), + MKEND() + }; + + static const SaveLoadEntry channelEntries[] = { + MKLINE(Channel, _pos, sleUint16, VER(94)), + MKLINE(Channel, _pitchModifier, sleInt32, VER(94)), + MKLINE(Channel, _velocity, sleUint8, VER(94)), + MKLINE(Channel, _remaining, sleUint32, VER(94)), + MKLINE(Channel, _notesLeft, sleUint8, VER(94)), + MKEND() + }; + + static const SaveLoadEntry instrumentEntries[] = { + MKLINE(Instrument, _pos, sleUint32, VER(94)), + MKLINE(Instrument, _subPos, sleUint32, VER(94)), + MKEND() + }; + + uint32 mixerSampleRate = _sampleRate; + int i; + + ser->saveLoadEntries(this, musicEntries); + + if (ser->isLoading() && _soundPlaying != -1) { + const byte *ptr = _vm->getResourceAddress(rtSound, _soundPlaying); + assert(ptr); + loadMusic(ptr); + } + + ser->saveLoadArrayOf(_channel, _numberOfChannels, sizeof(Channel), channelEntries); + for (i = 0; i < _numberOfChannels; i++) { + ser->saveLoadEntries(&_channel[i], instrumentEntries); + } + + if (ser->isLoading()) { + // If necessary, adjust the channel data to fit the + // current sample rate. + if (_soundPlaying != -1 && _sampleRate != mixerSampleRate) { + double mult = (double)_sampleRate / (double)mixerSampleRate; + for (i = 0; i < _numberOfChannels; i++) { + _channel[i]._pitchModifier = (int)((double)_channel[i]._pitchModifier * mult); + _channel[i]._remaining = (int)((double)_channel[i]._remaining / mult); + } + } + _sampleRate = mixerSampleRate; + } + } +} + +void Player_Mac::setMusicVolume(int vol) { + debug(5, "Player_Mac::setMusicVolume(%d)", vol); +} + +void Player_Mac::stopAllSounds_Internal() { + if (_soundPlaying != -1) { + _vm->_res->unlock(rtSound, _soundPlaying); + } + _soundPlaying = -1; + for (int i = 0; i < _numberOfChannels; i++) { + // The channel data is managed by the resource manager, so + // don't delete that. + delete[] _channel[i]._instrument._data; + _channel[i]._instrument._data = NULL; + + _channel[i]._remaining = 0; + _channel[i]._notesLeft = false; + } +} + +void Player_Mac::stopAllSounds() { + Common::StackLock lock(_mutex); + debug(5, "Player_Mac::stopAllSounds()"); + stopAllSounds_Internal(); +} + +void Player_Mac::stopSound(int nr) { + Common::StackLock lock(_mutex); + debug(5, "Player_Mac::stopSound(%d)", nr); + + if (nr == _soundPlaying) { + stopAllSounds(); + } +} + +void Player_Mac::startSound(int nr) { + Common::StackLock lock(_mutex); + debug(5, "Player_Mac::startSound(%d)", nr); + + stopAllSounds_Internal(); + + const byte *ptr = _vm->getResourceAddress(rtSound, nr); + assert(ptr); + + if (!loadMusic(ptr)) { + return; + } + + _vm->_res->lock(rtSound, nr); + _soundPlaying = nr; +} + +bool Player_Mac::Channel::loadInstrument(Common::SeekableReadStream *stream) { + uint16 soundType = stream->readUint16BE(); + if (soundType != 1) { + warning("Player_Mac::loadInstrument: Unsupported sound type %d", soundType); + return false; + } + uint16 typeCount = stream->readUint16BE(); + if (typeCount != 1) { + warning("Player_Mac::loadInstrument: Unsupported data type count %d", typeCount); + return false; + } + uint16 dataType = stream->readUint16BE(); + if (dataType != 5) { + warning("Player_Mac::loadInstrument: Unsupported data type %d", dataType); + return false; + } + + stream->readUint32BE(); // initialization option + + uint16 cmdCount = stream->readUint16BE(); + if (cmdCount != 1) { + warning("Player_Mac::loadInstrument: Unsupported command count %d", cmdCount); + return false; + } + uint16 command = stream->readUint16BE(); + if (command != 0x8050 && command != 0x8051) { + warning("Player_Mac::loadInstrument: Unsupported command 0x%04X", command); + return false; + } + + stream->readUint16BE(); // 0 + uint32 soundHeaderOffset = stream->readUint32BE(); + + stream->seek(soundHeaderOffset); + + uint32 soundDataOffset = stream->readUint32BE(); + uint32 size = stream->readUint32BE(); + uint32 rate = stream->readUint32BE() >> 16; + uint32 loopStart = stream->readUint32BE(); + uint32 loopEnd = stream->readUint32BE(); + byte encoding = stream->readByte(); + byte baseFreq = stream->readByte(); + + if (encoding != 0) { + warning("Player_Mac::loadInstrument: Unsupported encoding %d", encoding); + return false; + } + + stream->skip(soundDataOffset); + + byte *data = new byte[size]; + stream->read(data, size); + + _instrument._data = data; + _instrument._size = size; + _instrument._rate = rate; + _instrument._loopStart = loopStart; + _instrument._loopEnd = loopEnd; + _instrument._baseFreq = baseFreq; + + return true; +} + +int Player_Mac::getMusicTimer() { + return 0; +} + +int Player_Mac::getSoundStatus(int nr) const { + return _soundPlaying == nr; +} + +uint32 Player_Mac::durationToSamples(uint16 duration) { + // The correct formula should be: + // + // (duration * 473 * _sampleRate) / (4 * 480 * 480) + // + // But that's likely to cause integer overflow, so we do it in two + // steps and hope that the rounding error won't be noticeable. + // + // The original code is a bit unclear on if it should be 473 or 437, + // but since the comments indicated 473 I'm assuming 437 was a typo. + uint32 samples = (duration * _sampleRate) / (4 * 480); + samples = (samples * 473) / 480; + return samples; +} + +int Player_Mac::noteToPitchModifier(byte note, Instrument *instrument) { + if (note > 0) { + const int pitchIdx = note + 60 - instrument->_baseFreq; + // I don't want to use floating-point arithmetics here, but I + // ran into overflow problems with the church music in Monkey + // Island. It's only once per note, so it should be ok. + double mult = (double)instrument->_rate / (double)_sampleRate; + return (int)(mult * _pitchTable[pitchIdx]); + } else { + return 0; + } +} + +int Player_Mac::readBuffer(int16 *data, const int numSamples) { + Common::StackLock lock(_mutex); + + memset(data, 0, numSamples * 2); + if (_soundPlaying == -1) { + return numSamples; + } + + bool notesLeft = false; + + for (int i = 0; i < _numberOfChannels; i++) { + if (!(_channelMask & (1 << i))) { + continue; + } + + uint samplesLeft = numSamples; + int16 *ptr = data; + + while (samplesLeft > 0) { + int generated; + if (_channel[i]._remaining == 0) { + uint32 samples; + int pitchModifier; + byte velocity; + if (getNextNote(i, samples, pitchModifier, velocity)) { + _channel[i]._remaining = samples; + _channel[i]._pitchModifier = pitchModifier; + _channel[i]._velocity = velocity; + + } else { + _channel[i]._pitchModifier = 0; + _channel[i]._velocity = 0; + _channel[i]._remaining = samplesLeft; + } + } + generated = MIN<uint32>(_channel[i]._remaining, samplesLeft); + if (_channel[i]._velocity != 0) { + _channel[i]._instrument.generateSamples(ptr, _channel[i]._pitchModifier, _channel[i]._velocity, generated, _channel[i]._remaining, _fadeNoteEnds); + } + ptr += generated; + samplesLeft -= generated; + _channel[i]._remaining -= generated; + } + + if (_channel[i]._notesLeft) { + notesLeft = true; + } + } + + if (!notesLeft) { + stopAllSounds_Internal(); + } + + return numSamples; +} + +void Player_Mac::Instrument::generateSamples(int16 *data, int pitchModifier, int volume, int numSamples, int remainingSamplesOnNote, bool fadeNoteEnds) { + int samplesLeft = numSamples; + while (samplesLeft) { + _subPos += pitchModifier; + while (_subPos >= 0x10000) { + _subPos -= 0x10000; + _pos++; + if (_pos >= _loopEnd) { + _pos = _loopStart; + } + } + + int newSample = (((int16)((_data[_pos] << 8) ^ 0x8000)) * volume) / 255; + + if (fadeNoteEnds) { + // Fade out the last 100 samples on each note. Even at + // low output sample rates this is just a fraction of a + // second, but it gets rid of distracting "pops" at the + // end when the sample would otherwise go abruptly from + // something to nothing. This was particularly + // noticeable on the distaff notes in Loom. + // + // The reason it's conditional is that Monkey Island + // appears to have a "hold current note" command, and + // if we fade out the current note in that case we + // will actually introduce new "pops". + + remainingSamplesOnNote--; + if (remainingSamplesOnNote < 100) { + newSample = (newSample * remainingSamplesOnNote) / 100; + } + } + + int sample = *data + newSample; + if (sample > 32767) { + sample = 32767; + } else if (sample < -32768) { + sample = -32768; + } + + *data++ = sample; + samplesLeft--; + } +} + +} // End of namespace Scumm diff --git a/engines/scumm/player_mac.h b/engines/scumm/player_mac.h new file mode 100644 index 0000000000..09307b4e57 --- /dev/null +++ b/engines/scumm/player_mac.h @@ -0,0 +1,133 @@ +/* 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 SCUMM_PLAYER_MAC_H +#define SCUMM_PLAYER_MAC_H + +#include "common/scummsys.h" +#include "common/util.h" +#include "common/mutex.h" +#include "scumm/music.h" +#include "scumm/saveload.h" +#include "audio/audiostream.h" +#include "audio/mixer.h" + +#define RES_SND MKTAG('s', 'n', 'd', ' ') + +class Mixer; + +namespace Scumm { + +class ScummEngine; + +/** + * Scumm Macintosh music driver, base class. + */ +class Player_Mac : public Audio::AudioStream, public MusicEngine { +public: + Player_Mac(ScummEngine *scumm, Audio::Mixer *mixer, int numberOfChannels, int channelMask, bool fadeNoteEnds); + virtual ~Player_Mac(); + + void init(); + + // MusicEngine API + virtual void setMusicVolume(int vol); + virtual void startSound(int sound); + virtual void stopSound(int sound); + virtual void stopAllSounds(); + virtual int getMusicTimer(); + virtual int getSoundStatus(int sound) const; + + // AudioStream API + virtual int readBuffer(int16 *buffer, const int numSamples); + virtual bool isStereo() const { return false; } + virtual bool endOfData() const { return false; } + virtual int getRate() const { return _sampleRate; } + + virtual void saveLoadWithSerializer(Serializer *ser); + +private: + Common::Mutex _mutex; + Audio::Mixer *const _mixer; + Audio::SoundHandle _soundHandle; + uint32 _sampleRate; + int _soundPlaying; + + void stopAllSounds_Internal(); + + struct Instrument { + byte *_data; + uint32 _size; + uint32 _rate; + uint32 _loopStart; + uint32 _loopEnd; + byte _baseFreq; + + uint _pos; + uint _subPos; + + void newNote() { + _pos = 0; + _subPos = 0; + } + + void generateSamples(int16 *data, int pitchModifier, int volume, int numSamples, int remainingSamplesOnNote, bool fadeNoteEnds); + }; + + int _pitchTable[128]; + int _numberOfChannels; + int _channelMask; + bool _fadeNoteEnds; + + virtual bool checkMusicAvailable() { return false; } + virtual bool loadMusic(const byte *ptr) { return false; } + virtual bool getNextNote(int ch, uint32 &samples, int &pitchModifier, byte &velocity) { return false; } + +protected: + struct Channel { + virtual ~Channel() {} + + Instrument _instrument; + bool _looped; + uint32 _length; + const byte *_data; + + uint _pos; + int _pitchModifier; + byte _velocity; + uint32 _remaining; + + bool _notesLeft; + + bool loadInstrument(Common::SeekableReadStream *stream); + }; + + ScummEngine *const _vm; + Channel *_channel; + + uint32 durationToSamples(uint16 duration); + int noteToPitchModifier(byte note, Instrument *instrument); +}; + +} // End of namespace Scumm + +#endif diff --git a/engines/scumm/player_towns.cpp b/engines/scumm/player_towns.cpp index 2588026e59..33e3e40e39 100644 --- a/engines/scumm/player_towns.cpp +++ b/engines/scumm/player_towns.cpp @@ -87,7 +87,7 @@ void Player_Towns::restoreAfterLoad() { if (!_v2) restoredSounds.push_back(_pcmCurrentSound[i].index); - + uint8 *ptr = _vm->getResourceAddress(rtSound, _pcmCurrentSound[i].index); if (!ptr) continue; diff --git a/engines/scumm/player_v2cms.cpp b/engines/scumm/player_v2cms.cpp index d4b21774ed..c1242e0645 100644 --- a/engines/scumm/player_v2cms.cpp +++ b/engines/scumm/player_v2cms.cpp @@ -718,38 +718,38 @@ void Player_V2CMS::playMusicChips(const MusicChip *table) { } const Player_V2CMS::MidiNote Player_V2CMS::_midiNotes[132] = { - { 3, 0 }, { 31, 0 }, { 58, 0 }, { 83, 0 }, - { 107, 0 }, { 130, 0 }, { 151, 0 }, { 172, 0 }, - { 191, 0 }, { 209, 0 }, { 226, 0 }, { 242, 0 }, - { 3, 1 }, { 31, 1 }, { 58, 1 }, { 83, 1 }, - { 107, 1 }, { 130, 1 }, { 151, 1 }, { 172, 1 }, - { 191, 1 }, { 209, 1 }, { 226, 1 }, { 242, 1 }, - { 3, 2 }, { 31, 2 }, { 58, 2 }, { 83, 2 }, - { 107, 2 }, { 130, 2 }, { 151, 2 }, { 172, 2 }, - { 191, 2 }, { 209, 2 }, { 226, 2 }, { 242, 2 }, - { 3, 3 }, { 31, 3 }, { 58, 3 }, { 83, 3 }, - { 107, 3 }, { 130, 3 }, { 151, 3 }, { 172, 3 }, - { 191, 3 }, { 209, 3 }, { 226, 3 }, { 242, 3 }, - { 3, 4 }, { 31, 4 }, { 58, 4 }, { 83, 4 }, - { 107, 4 }, { 130, 4 }, { 151, 4 }, { 172, 4 }, - { 191, 4 }, { 209, 4 }, { 226, 4 }, { 242, 4 }, - { 3, 5 }, { 31, 5 }, { 58, 5 }, { 83, 5 }, - { 107, 5 }, { 130, 5 }, { 151, 5 }, { 172, 5 }, - { 191, 5 }, { 209, 5 }, { 226, 5 }, { 242, 5 }, - { 3, 6 }, { 31, 6 }, { 58, 6 }, { 83, 6 }, - { 107, 6 }, { 130, 6 }, { 151, 6 }, { 172, 6 }, - { 191, 6 }, { 209, 6 }, { 226, 6 }, { 242, 6 }, - { 3, 7 }, { 31, 7 }, { 58, 7 }, { 83, 7 }, - { 107, 7 }, { 130, 7 }, { 151, 7 }, { 172, 7 }, - { 191, 7 }, { 209, 7 }, { 226, 7 }, { 242, 7 }, - { 3, 8 }, { 31, 8 }, { 58, 8 }, { 83, 8 }, - { 107, 8 }, { 130, 8 }, { 151, 8 }, { 172, 8 }, - { 191, 8 }, { 209, 8 }, { 226, 8 }, { 242, 8 }, - { 3, 9 }, { 31, 9 }, { 58, 9 }, { 83, 9 }, - { 107, 9 }, { 130, 9 }, { 151, 9 }, { 172, 9 }, - { 191, 9 }, { 209, 9 }, { 226, 9 }, { 242, 9 }, - { 3, 10 }, { 31, 10 }, { 58, 10 }, { 83, 10 }, - { 107, 10 }, { 130, 10 }, { 151, 10 }, { 172, 10 }, + { 3, 0 }, { 31, 0 }, { 58, 0 }, { 83, 0 }, + { 107, 0 }, { 130, 0 }, { 151, 0 }, { 172, 0 }, + { 191, 0 }, { 209, 0 }, { 226, 0 }, { 242, 0 }, + { 3, 1 }, { 31, 1 }, { 58, 1 }, { 83, 1 }, + { 107, 1 }, { 130, 1 }, { 151, 1 }, { 172, 1 }, + { 191, 1 }, { 209, 1 }, { 226, 1 }, { 242, 1 }, + { 3, 2 }, { 31, 2 }, { 58, 2 }, { 83, 2 }, + { 107, 2 }, { 130, 2 }, { 151, 2 }, { 172, 2 }, + { 191, 2 }, { 209, 2 }, { 226, 2 }, { 242, 2 }, + { 3, 3 }, { 31, 3 }, { 58, 3 }, { 83, 3 }, + { 107, 3 }, { 130, 3 }, { 151, 3 }, { 172, 3 }, + { 191, 3 }, { 209, 3 }, { 226, 3 }, { 242, 3 }, + { 3, 4 }, { 31, 4 }, { 58, 4 }, { 83, 4 }, + { 107, 4 }, { 130, 4 }, { 151, 4 }, { 172, 4 }, + { 191, 4 }, { 209, 4 }, { 226, 4 }, { 242, 4 }, + { 3, 5 }, { 31, 5 }, { 58, 5 }, { 83, 5 }, + { 107, 5 }, { 130, 5 }, { 151, 5 }, { 172, 5 }, + { 191, 5 }, { 209, 5 }, { 226, 5 }, { 242, 5 }, + { 3, 6 }, { 31, 6 }, { 58, 6 }, { 83, 6 }, + { 107, 6 }, { 130, 6 }, { 151, 6 }, { 172, 6 }, + { 191, 6 }, { 209, 6 }, { 226, 6 }, { 242, 6 }, + { 3, 7 }, { 31, 7 }, { 58, 7 }, { 83, 7 }, + { 107, 7 }, { 130, 7 }, { 151, 7 }, { 172, 7 }, + { 191, 7 }, { 209, 7 }, { 226, 7 }, { 242, 7 }, + { 3, 8 }, { 31, 8 }, { 58, 8 }, { 83, 8 }, + { 107, 8 }, { 130, 8 }, { 151, 8 }, { 172, 8 }, + { 191, 8 }, { 209, 8 }, { 226, 8 }, { 242, 8 }, + { 3, 9 }, { 31, 9 }, { 58, 9 }, { 83, 9 }, + { 107, 9 }, { 130, 9 }, { 151, 9 }, { 172, 9 }, + { 191, 9 }, { 209, 9 }, { 226, 9 }, { 242, 9 }, + { 3, 10 }, { 31, 10 }, { 58, 10 }, { 83, 10 }, + { 107, 10 }, { 130, 10 }, { 151, 10 }, { 172, 10 }, { 191, 10 }, { 209, 10 }, { 226, 10 }, { 242, 10 } }; diff --git a/engines/scumm/player_v3m.cpp b/engines/scumm/player_v3m.cpp new file mode 100644 index 0000000000..e61463128a --- /dev/null +++ b/engines/scumm/player_v3m.cpp @@ -0,0 +1,202 @@ +/* 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. + * + */ + +/* + We have the following information from Lars Christensen (lechimp) and + Jamieson Christian (jamieson630): + + RESOURCE DATA + LE 2 bytes Resource size + 2 bytes Unknown + 2 bytes 'so' + 14 bytes Unknown + BE 2 bytes Instrument for Stream 1 + BE 2 bytes Instrument for Stream 2 + BE 2 bytes Instrument for Stream 3 + BE 2 bytes Instrument for Stream 4 + BE 2 bytes Instrument for Stream 5 + BE 2 bytes Offset to Stream 1 + BE 2 bytes Offset to Stream 2 + BE 2 bytes Offset to Stream 3 + BE 2 bytes Offset to Stream 4 + BE 2 bytes Offset to Stream 5 + ? bytes The streams + + STREAM DATA + BE 2 bytes Unknown (always 1?) + 2 bytes Unknown (always 0?) + BE 2 bytes Number of events in stream + ? bytes Stream data + + Each stream event is exactly 3 bytes, therefore one can + assert that numEvents == (streamSize - 6) / 3. The + polyphony of a stream appears to be 1; in other words, only + one note at a time can be playing in each stream. The next + event is not executed until the current note (or rest) is + finished playing; therefore, note duration also serves as the + time delta between events. + + FOR EACH EVENTS + BE 2 bytes Note duration + 1 byte Note number to play (0 = rest/silent) + + Oh, and quick speculation -- Stream 1 may be used for a + single-voice interleaved version of the music, where Stream 2- + 5 represent a version of the music in up to 4-voice + polyphony, one voice per stream. I postulate thus because + the first stream of the Mac Loom theme music contains + interleaved voices, whereas the second stream seemed to + contain only the pizzicato bottom-end harp. Stream 5, in this + example, is empty, so if my speculation is correct, this + particular musical number supports 3-voice polyphony at + most. I must check out Streams 3 and 4 to see what they + contain. + + ========== + + The instruments appear to be identified by their resource IDs: + + 1000 Dual Harp + 10895 harp1 + 11445 strings1 + 11548 silent + 13811 staff1 + 15703 brass1 + 16324 flute1 + 25614 accordian 1 + 28110 f horn1 + 29042 bassoon1 +*/ + +#include "common/macresman.h" +#include "common/translation.h" +#include "engines/engine.h" +#include "gui/message.h" +#include "scumm/player_v3m.h" +#include "scumm/scumm.h" + +namespace Scumm { + +Player_V3M::Player_V3M(ScummEngine *scumm, Audio::Mixer *mixer) + : Player_Mac(scumm, mixer, 5, 0x1E, true) { + assert(_vm->_game.id == GID_LOOM); + + // Channel 0 seems to be what was played on low-end macs, that couldn't + // handle multi-channel music and play the game at the same time. I'm + // not sure if stream 4 is ever used, but let's use it just in case. +} + +// \xAA is a trademark glyph in Mac OS Roman. We try that, but also the Windows +// version, the UTF-8 version, and just plain without in case the file system +// can't handle exotic characters like that. + +static const char *loomFileNames[] = { + "Loom\xAA", + "Loom\x99", + "Loom\xE2\x84\xA2", + "Loom" +}; + +bool Player_V3M::checkMusicAvailable() { + Common::MacResManager resource; + + for (int i = 0; i < ARRAYSIZE(loomFileNames); i++) { + if (resource.exists(loomFileNames[i])) { + return true; + } + } + + GUI::MessageDialog dialog(_( + "Could not find the 'Loom' Macintosh executable to read the\n" + "instruments from. Music will be disabled."), _("OK")); + dialog.runModal(); + return false; +} + +bool Player_V3M::loadMusic(const byte *ptr) { + Common::MacResManager resource; + bool found = false; + + for (int i = 0; i < ARRAYSIZE(loomFileNames); i++) { + if (resource.open(loomFileNames[i])) { + found = true; + break; + } + } + + if (!found) { + return false; + } + + assert(ptr[4] == 's' && ptr[5] == 'o'); + + uint i; + for (i = 0; i < 5; i++) { + int instrument = READ_BE_UINT16(ptr + 20 + 2 * i); + int offset = READ_BE_UINT16(ptr + 30 + 2 * i); + + _channel[i]._looped = false; + _channel[i]._length = READ_BE_UINT16(ptr + offset + 4) * 3; + _channel[i]._data = ptr + offset + 6; + _channel[i]._pos = 0; + _channel[i]._pitchModifier = 0; + _channel[i]._velocity = 0; + _channel[i]._remaining = 0; + _channel[i]._notesLeft = true; + + Common::SeekableReadStream *stream = resource.getResource(RES_SND, instrument); + if (_channel[i].loadInstrument(stream)) { + debug(6, "Player_V3M::loadMusic: Channel %d - Loaded Instrument %d (%s)", i, instrument, resource.getResName(RES_SND, instrument).c_str()); + } else { + resource.close(); + return false; + } + } + + resource.close(); + return true; +} + +bool Player_V3M::getNextNote(int ch, uint32 &samples, int &pitchModifier, byte &velocity) { + _channel[ch]._instrument.newNote(); + if (_channel[ch]._pos >= _channel[ch]._length) { + if (!_channel[ch]._looped) { + _channel[ch]._notesLeft = false; + return false; + } + _channel[ch]._pos = 0; + } + uint16 duration = READ_BE_UINT16(&_channel[ch]._data[_channel[ch]._pos]); + byte note = _channel[ch]._data[_channel[ch]._pos + 2]; + samples = durationToSamples(duration); + if (note > 0) { + pitchModifier = noteToPitchModifier(note, &_channel[ch]._instrument); + velocity = 127; + } else { + pitchModifier = 0; + velocity = 0; + } + _channel[ch]._pos += 3; + return true; +} + +} // End of namespace Scumm diff --git a/engines/scumm/player_v3m.h b/engines/scumm/player_v3m.h new file mode 100644 index 0000000000..359bab32a9 --- /dev/null +++ b/engines/scumm/player_v3m.h @@ -0,0 +1,54 @@ +/* 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 SCUMM_PLAYER_V3M_H +#define SCUMM_PLAYER_V3M_H + +#include "common/scummsys.h" +#include "common/util.h" +#include "common/mutex.h" +#include "scumm/music.h" +#include "scumm/player_mac.h" +#include "audio/audiostream.h" +#include "audio/mixer.h" + +class Mixer; + +namespace Scumm { + +class ScummEngine; + +/** + * Scumm V3 Macintosh music driver. + */ +class Player_V3M : public Player_Mac { +public: + Player_V3M(ScummEngine *scumm, Audio::Mixer *mixer); + + virtual bool checkMusicAvailable(); + virtual bool loadMusic(const byte *ptr); + virtual bool getNextNote(int ch, uint32 &samples, int &pitchModifier, byte &velocity); +}; + +} // End of namespace Scumm + +#endif diff --git a/engines/scumm/player_v5m.cpp b/engines/scumm/player_v5m.cpp new file mode 100644 index 0000000000..500f3bbc40 --- /dev/null +++ b/engines/scumm/player_v5m.cpp @@ -0,0 +1,246 @@ +/* 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. + * + */ + +/* + From Markus Magnuson (superqult) we got this information: + Mac0 + --- + 4 bytes - 'SOUN' + BE 4 bytes - block length + + 4 bytes - 'Mac0' + BE 4 bytes - (blockLength - 27) + 28 bytes - ??? + + do this three times (once for each channel): + 4 bytes - 'Chan' + BE 4 bytes - channel length + 4 bytes - instrument name (e.g. 'MARI') + + do this for ((chanLength-24)/4) times: + 2 bytes - note duration + 1 byte - note value + 1 byte - note velocity + + 4 bytes - ??? + 4 bytes - 'Loop'/'Done' + 4 bytes - ??? + + 1 byte - 0x09 + --- + + The instruments presumably correspond to the snd resource names in the + Monkey Island executable: + + Instruments + "MARI" - MARIMBA + "PLUC" - PLUCK + "HARM" - HARMONIC + "PIPE" - PIPEORGAN + "TROM" - TROMBONE + "STRI" - STRINGS + "HORN" - HORN + "VIBE" - VIBES + "SHAK" - SHAKUHACHI + "PANP" - PANPIPE + "WHIS" - WHISTLE + "ORGA" - ORGAN3 + "BONG" - BONGO + "BASS" - BASS + + --- + + Note values <= 1 are silent. +*/ + +#include "common/macresman.h" +#include "common/translation.h" +#include "engines/engine.h" +#include "gui/message.h" +#include "scumm/player_v5m.h" +#include "scumm/scumm.h" + +namespace Scumm { + +Player_V5M::Player_V5M(ScummEngine *scumm, Audio::Mixer *mixer) + : Player_Mac(scumm, mixer, 3, 0x07, false) { + assert(_vm->_game.id == GID_MONKEY); +} + +// Try both with and without underscore in the filename, because hfsutils may +// turn the space into an underscore. At least, it did for me. + +static const char *monkeyIslandFileNames[] = { + "Monkey Island", + "Monkey_Island" +}; + +bool Player_V5M::checkMusicAvailable() { + Common::MacResManager resource; + + for (int i = 0; i < ARRAYSIZE(monkeyIslandFileNames); i++) { + if (resource.exists(monkeyIslandFileNames[i])) { + return true; + } + } + + GUI::MessageDialog dialog(_( + "Could not find the 'Monkey Island' Macintosh executable to read the\n" + "instruments from. Music will be disabled."), _("OK")); + dialog.runModal(); + return false; +} + +bool Player_V5M::loadMusic(const byte *ptr) { + Common::MacResManager resource; + bool found = false; + uint i; + + for (i = 0; i < ARRAYSIZE(monkeyIslandFileNames); i++) { + if (resource.open(monkeyIslandFileNames[i])) { + found = true; + break; + } + } + + if (!found) { + return false; + } + + ptr += 8; + // TODO: Decipher the unknown bytes in the header. For now, skip 'em + ptr += 28; + + Common::MacResIDArray idArray = resource.getResIDArray(RES_SND); + + // Load the three channels and their instruments + for (i = 0; i < 3; i++) { + assert(READ_BE_UINT32(ptr) == MKTAG('C', 'h', 'a', 'n')); + uint32 len = READ_BE_UINT32(ptr + 4); + uint32 instrument = READ_BE_UINT32(ptr + 8); + + _channel[i]._length = len - 20; + _channel[i]._data = ptr + 12; + _channel[i]._looped = (READ_BE_UINT32(ptr + len - 8) == MKTAG('L', 'o', 'o', 'p')); + _channel[i]._pos = 0; + _channel[i]._pitchModifier = 0; + _channel[i]._velocity = 0; + _channel[i]._remaining = 0; + _channel[i]._notesLeft = true; + + for (uint j = 0; j < idArray.size(); j++) { + Common::String name = resource.getResName(RES_SND, idArray[j]); + if (instrument == READ_BE_UINT32(name.c_str())) { + debug(6, "Player_V5M::loadMusic: Channel %d: Loading instrument '%s'", i, name.c_str()); + Common::SeekableReadStream *stream = resource.getResource(RES_SND, idArray[j]); + + if (!_channel[i].loadInstrument(stream)) { + resource.close(); + return false; + } + + break; + } + } + + ptr += len; + } + + resource.close(); + + // The last note of each channel is just zeroes. We will adjust this + // note so that all the channels end at the same time. + + uint32 samples[3]; + uint32 maxSamples = 0; + for (i = 0; i < 3; i++) { + samples[i] = 0; + for (uint j = 0; j < _channel[i]._length; j += 4) { + samples[i] += durationToSamples(READ_BE_UINT16(&_channel[i]._data[j])); + } + if (samples[i] > maxSamples) { + maxSamples = samples[i]; + } + } + + for (i = 0; i < 3; i++) { + _lastNoteSamples[i] = maxSamples - samples[i]; + } + + return true; +} + +bool Player_V5M::getNextNote(int ch, uint32 &samples, int &pitchModifier, byte &velocity) { + if (_channel[ch]._pos >= _channel[ch]._length) { + if (!_channel[ch]._looped) { + _channel[ch]._notesLeft = false; + return false; + } + // FIXME: Jamieson630: The jump seems to be happening + // too quickly! There should maybe be a pause after + // the last Note Off? But I couldn't find one in the + // MI1 Lookout music, where I was hearing problems. + _channel[ch]._pos = 0; + } + uint16 duration = READ_BE_UINT16(&_channel[ch]._data[_channel[ch]._pos]); + byte note = _channel[ch]._data[_channel[ch]._pos + 2]; + samples = durationToSamples(duration); + + if (note != 1) { + _channel[ch]._instrument.newNote(); + } + + if (note > 1) { + pitchModifier = noteToPitchModifier(note, &_channel[ch]._instrument); + velocity = _channel[ch]._data[_channel[ch]._pos + 3]; + } else if (note == 1) { + // This is guesswork, but Monkey Island uses two different + // "special" note values: 0, which is clearly a rest, and 1 + // which is... I thought at first it was a "soft" key off, to + // fade out the note, but listening to the music in a Mac + // emulator (which unfortunately doesn't work all that well), + // I hear no trace of fading out. + // + // It could mean "change the volume on the current note", but + // I can't hear that either, and it always seems to use the + // exact same velocity on this note. + // + // So it appears it really just is a "hold the current note", + // but why? Couldn't they just have made the original note + // longer? + + pitchModifier = _channel[ch]._pitchModifier; + velocity = _channel[ch]._velocity; + } else { + pitchModifier = 0; + velocity = 0; + } + + _channel[ch]._pos += 4; + + if (_channel[ch]._pos >= _channel[ch]._length) { + samples = _lastNoteSamples[ch]; + } + return true; +} + +} // End of namespace Scumm diff --git a/engines/scumm/player_v5m.h b/engines/scumm/player_v5m.h new file mode 100644 index 0000000000..b2079ee331 --- /dev/null +++ b/engines/scumm/player_v5m.h @@ -0,0 +1,57 @@ +/* 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 SCUMM_PLAYER_V5M_H +#define SCUMM_PLAYER_V5M_H + +#include "common/scummsys.h" +#include "common/util.h" +#include "common/mutex.h" +#include "scumm/music.h" +#include "scumm/player_mac.h" +#include "audio/audiostream.h" +#include "audio/mixer.h" + +class Mixer; + +namespace Scumm { + +class ScummEngine; + +/** + * Scumm V5 Macintosh music driver. + */ +class Player_V5M : public Player_Mac { +public: + Player_V5M(ScummEngine *scumm, Audio::Mixer *mixer); + + virtual bool checkMusicAvailable(); + virtual bool loadMusic(const byte *ptr); + virtual bool getNextNote(int ch, uint32 &samples, int &pitchModifier, byte &velocity); + +private: + uint32 _lastNoteSamples[3]; +}; + +} // End of namespace Scumm + +#endif diff --git a/engines/scumm/proc3ARM.s b/engines/scumm/proc3ARM.s index ca44386b5c..75dd4b4a7f 100644 --- a/engines/scumm/proc3ARM.s +++ b/engines/scumm/proc3ARM.s @@ -25,37 +25,40 @@ .global _ClassicProc3RendererShadowARM -.set _scaleIndexY , 112 -.set _numStrips , 108 -.set _palette , 104 -.set _shadow_table , 100 -.set _scaleIndexX , 96 -.set _scaleX , 92 -.set _height , 88 -.set store_r14 , 84 -.set store_r11 , 80 -.set store_r10 , 76 -.set store_r9 , 72 -.set store_r8 , 68 -.set store_r7 , 64 -.set store_r6 , 60 -.set store_r5 , 56 -.set store_r4 , 52 -.set src , 48 -.set height , 44 -.set len , 40 -.set v1_shr , 36 -.set v1_skip_width , 32 -.set v1_destptr , 28 -.set v1_scaleXstep , 24 -.set v1_mask_ptr , 20 -.set v1_y , 16 -.set v1_scaletable , 12 -.set pitch , 8 -.set scaleIdxXPtr , 4 -.set scaleIdxYPtr , 0 -.set space , 48 +.set space, 48 + +.set _scaleIndexY, store_r14 + 28 +.set _numStrips, store_r14 + 24 +.set _palette, store_r14 + 20 +.set _shadow_table, store_r14 + 16 +.set _scaleIndexX, store_r14 + 12 +.set _scaleX, store_r14 + 8 +.set _height, store_r14 + 4 + +.set store_r14, space + 36 +.set store_r11, space + 32 +.set store_r10, space + 28 +.set store_r9, space + 24 +.set store_r8, space + 20 +.set store_r7, space + 16 +.set store_r6, space + 12 +.set store_r5, space + 8 +.set store_r4, space + 4 + +.set src, 48 +.set height, 44 +.set len, 40 +.set v1_shr, 36 +.set v1_skip_width, 32 +.set v1_destptr, 28 +.set v1_scaleXstep, 24 +.set v1_mask_ptr, 20 +.set v1_y, 16 +.set v1_scaletable, 12 +.set pitch, 8 +.set scaleIdxXPtr, 4 +.set scaleIdxYPtr, 0 @ r0 = _scaleY @ r1 = v1 @@ -103,7 +106,7 @@ _ClassicProc3RendererShadowARM: LDRB r1, [r1,#30] @ r1 = repcolor STR r8, [r13,#v1_shr] STR r9, [r13,#v1_destptr] - STR r10,[r13,#v1_mask_ptr] + STR r10,[r13,#v1_mask_ptr] STR r11,[r13,#v1_scaleXstep] LDR r12,[r13,#_height] @@ -123,10 +126,10 @@ _ClassicProc3RendererShadowARM: @ r12= _height @ r14= v1.replen - MOV r8,#0x80 + MOV r8,#0x80 AND r11,r3,#7 @ r11= v1.x & 7 MOV r8,r8,LSR r11 @ r8 = maskbit = revBitMask(v1.x & 7) - ADD r10,r10,r3,ASR #3 @ r10= mask = v1.mask_ptr + (v1.x>>3) + ADD r10,r10,r3,ASR #3 @ r10= mask = v1.mask_ptr + (v1.x>>3) @ r0 = _scaleY @ r1 = color = v1.repcolor @@ -152,7 +155,7 @@ _ClassicProc3RendererShadowARM: SUB r14,r14,r5 STR r12,[r13,#height] STR r14,[r13,#len] - LDR r12,[r13,#pitch] + LDR r12,[r13,#pitch] LDR r11,[r13,#_numStrips] B startpos @@ -174,13 +177,13 @@ outerloop: LDR r11,[r13,#src] LDR r5,[r13,#v1_shr] - @ stall + @ stall LDRB r14,[r11],#1 @ r14= len = *src++ @ stall @ stall MOV r1, r14,LSR r5 @ r1 = color = len>>v1.shr - BICS r14,r14,r1,LSL r5 @ r14= len - LDREQB r14,[r11],#1 @ if (!len) r14 = len = *src++ + BICS r14,r14,r1,LSL r5 @ r14= len + LDREQB r14,[r11],#1 @ if (!len) r14 = len = *src++ STR r11,[r13,#src] CMP r14,#0 middleloop: @@ -232,7 +235,7 @@ innerloop: CMPLE r0,r14 @ || _scaleY >= r14 BLE startpos - ADDS r4,r4,#1 @ y >= 0 (equiv to y>-1,y+1>0) + ADDS r4,r4,#1 @ y >= 0 (equiv to y>-1,y+1>0) CMPGT r1,#0 @ && color > 0 CMPGT r6,r4 @ && _out.h+1 > y+1 CMNGT r3,#1 @ && x >= 0 (equiv to x>-1,x+1>0) @@ -248,12 +251,12 @@ innerloop: @ stall @ stall CMP r14,#13 @ if (pcolor == 13) - LDREQ r12,[r13,#_shadow_table] + LDREQ r12,[r13,#_shadow_table] LDREQB r14,[r9] @ r14 = *dst @ stallEQ @ stallEQ LDREQB r14,[r12,r14] @ r14 = pcolor=_shadow_tab[r14] - LDREQ r12,[r13,#pitch] + LDREQ r12,[r13,#pitch] @ stallEQ STRB r14,[r9] @ *dst = pcolor masked: @@ -282,7 +285,7 @@ startpos: BLE noXstep SUB r11,r7,#1 - ADDS r3,r3,r12 @ v1.x += v1.scaleXstep + ADDS r3,r3,r12 @ v1.x += v1.scaleXstep @ if v1.x < 0 || CMPGE r11,r3 @ _out.w-1 < v1.x BLT end @@ -297,7 +300,7 @@ noXstep: LDR r12,[r13,#_height] @ r12= height = _height LDR r4,[r13,#v1_y] @ r4 = y = v1.y LDR r2,[r13,#scaleIdxYPtr] @ r2 = v1.scaletable[sclIdxY] - ADD r10,r10,r3,ASR #3 @ mask=v1.mask_ptr+(v1.x>>3) + ADD r10,r10,r3,ASR #3 @ mask=v1.mask_ptr+(v1.x>>3) notheight: CMP r14,#0 @ while (len > 0) BGT middleloop @@ -305,5 +308,5 @@ notheight: end: LDR r0,[r13,#v1_scaletable] SUB r0,r2,r0 - ADD r13,r13,#space + ADD r13,r13,#space LDMFD r13!,{r3-r11,PC} diff --git a/engines/scumm/saveload.cpp b/engines/scumm/saveload.cpp index beac077fd1..3453e53a18 100644 --- a/engines/scumm/saveload.cpp +++ b/engines/scumm/saveload.cpp @@ -1298,7 +1298,7 @@ void ScummEngine::saveOrLoad(Serializer *s) { s->saveLoadArrayOf(_16BitPalette, 512, sizeof(_16BitPalette[0]), sleUint16); } - + // FM-Towns specific (extra palette data, color cycle data, etc.) // In earlier save game versions (below 87) the FM-Towns specific data would get saved (and loaded) even in non FM-Towns games. // This would cause an unnecessary save file incompatibility between DS (which uses the DISABLE_TOWNS_DUAL_LAYER_MODE setting) @@ -1477,9 +1477,13 @@ void ScummEngine::saveOrLoad(Serializer *s) { } - // Save/load FM-Towns audio status - if (_townsPlayer) - _townsPlayer->saveLoadWithSerializer(s); + // + // Save/load music engine status + // + if (_musicEngine) { + _musicEngine->saveLoadWithSerializer(s); + } + // // Save/load the charset renderer state diff --git a/engines/scumm/saveload.h b/engines/scumm/saveload.h index d5f7ea526e..7b2ff91ad3 100644 --- a/engines/scumm/saveload.h +++ b/engines/scumm/saveload.h @@ -47,7 +47,7 @@ namespace Scumm { * only saves/loads those which are valid for the version of the savegame * which is being loaded/saved currently. */ -#define CURRENT_VER 92 +#define CURRENT_VER 94 /** * An auxillary macro, used to specify savegame versions. We use this instead @@ -74,7 +74,7 @@ namespace Scumm { * what POD means refer to <http://en.wikipedia.org/wiki/Plain_Old_Data_Structures> or * to <http://www.informit.com/guides/content.asp?g=cplusplus&seqNum=32&rl=1>) */ -#define OFFS(type,item) (((ptrdiff_t)(&((type *)42)->type::item))-42) +#define OFFS(type,item) ((uint32)(((ptrdiff_t)(&((type *)42)->type::item))-42)) /** * Similar to the OFFS macro, this macro computes the size (in bytes) of a @@ -84,19 +84,19 @@ namespace Scumm { // Any item that is still in use automatically gets a maxVersion equal to CURRENT_VER #define MKLINE(type,item,saveas,minVer) {OFFS(type,item),saveas,SIZE(type,item),minVer,CURRENT_VER} -#define MKARRAY(type,item,saveas,dim,minVer) {OFFS(type,item),128|saveas,SIZE(type,item),minVer,CURRENT_VER}, {dim,1,0,0,0} -#define MKARRAY2(type,item,saveas,dim,dim2,rowlen,minVer) {OFFS(type,item),128|saveas,SIZE(type,item),minVer,CURRENT_VER}, {dim,dim2,rowlen,0,0} +#define MKARRAY(type,item,saveas,dim,minVer) {OFFS(type,item),128|saveas,SIZE(type,item),minVer,CURRENT_VER}, {(uint32)(dim),1,0,0,0} +#define MKARRAY2(type,item,saveas,dim,dim2,rowlen,minVer) {OFFS(type,item),128|saveas,SIZE(type,item),minVer,CURRENT_VER}, {(uint32)(dim),(uint32)(dim2),(uint16)(rowlen),0,0} // Use this if you have an entry that used to be smaller: #define MKLINE_OLD(type,item,saveas,minVer,maxVer) {OFFS(type,item),saveas,SIZE(type,item),minVer,maxVer} -#define MKARRAY_OLD(type,item,saveas,dim,minVer,maxVer) {OFFS(type,item),128|saveas,SIZE(type,item),minVer,maxVer}, {dim,1,0,0,0} -#define MKARRAY2_OLD(type,item,saveas,dim,dim2,rowlen,minVer,maxVer) {OFFS(type,item),128|saveas,SIZE(type,item),minVer,maxVer}, {dim,dim2,rowlen,0,0} +#define MKARRAY_OLD(type,item,saveas,dim,minVer,maxVer) {OFFS(type,item),128|saveas,SIZE(type,item),minVer,maxVer}, {(uint32)(dim),1,0,0,0} +#define MKARRAY2_OLD(type,item,saveas,dim,dim2,rowlen,minVer,maxVer) {OFFS(type,item),128|saveas,SIZE(type,item),minVer,maxVer}, {(uint32)(dim),(uint32)(dim2),(uint16)(rowlen),0,0} // An obsolete item/array, to be ignored upon load. We retain the type/item params to make it easier to debug. // Obsolete items have size == 0. #define MK_OBSOLETE(type,item,saveas,minVer,maxVer) {0,saveas,0,minVer,maxVer} -#define MK_OBSOLETE_ARRAY(type,item,saveas,dim,minVer,maxVer) {0,128|saveas,0,minVer,maxVer}, {dim,1,0,0,0} -#define MK_OBSOLETE_ARRAY2(type,item,saveas,dim,dim2,rowlen,minVer,maxVer) {0,128|saveas,0,minVer,maxVer}, {dim,dim2,rowlen,0,0} +#define MK_OBSOLETE_ARRAY(type,item,saveas,dim,minVer,maxVer) {0,128|saveas,0,minVer,maxVer}, {(uint32)(dim),1,0,0,0} +#define MK_OBSOLETE_ARRAY2(type,item,saveas,dim,dim2,rowlen,minVer,maxVer) {0,128|saveas,0,minVer,maxVer}, {(uint32)(dim),(uint32)(dim2),(uint16)(rowlen),0,0} // End marker #define MKEND() {0xFFFF,0xFF,0xFF,0,0} diff --git a/engines/scumm/script.cpp b/engines/scumm/script.cpp index d8c4948ea8..8587fb8092 100644 --- a/engines/scumm/script.cpp +++ b/engines/scumm/script.cpp @@ -1366,9 +1366,15 @@ void ScummEngine::runInputScript(int clickArea, int val, int mode) { // Clicks are handled differently in Indy3 mac: param 2 of the // input script is set to 0 for normal clicks, and to 1 for double clicks. + // The EGA DOS version of Loom also checks that the second click happens + // close enough to the first one, but that seems like overkill. uint32 time = _system->getMillis(); args[2] = (time < _lastInputScriptTime + 500); // 500 ms double click delay _lastInputScriptTime = time; + } else if (_game.id == GID_LOOM && _game.platform == Common::kPlatformMacintosh) { + uint32 time = _system->getMillis(); + VAR(52) = (time < _lastInputScriptTime + 500); // 500 ms double click delay + _lastInputScriptTime = time; } if (verbScript) diff --git a/engines/scumm/script_v0.cpp b/engines/scumm/script_v0.cpp index 44b77f1d18..361287d29f 100644 --- a/engines/scumm/script_v0.cpp +++ b/engines/scumm/script_v0.cpp @@ -602,7 +602,7 @@ void ScummEngine_v0::o_loadRoomWithEgo() { x = r.x; y = r.y; a->putActor(x, y, _currentRoom); - + camera._dest.x = camera._cur.x = a->getPos().x; setCameraAt(a->getPos().x, a->getPos().y); setCameraFollows(a); @@ -635,18 +635,18 @@ void ScummEngine_v0::setMode(byte mode) { case kModeCutscene: _redrawSentenceLine = false; // Note: do not change freeze state here - state = USERSTATE_SET_IFACE | + state = USERSTATE_SET_IFACE | USERSTATE_SET_CURSOR; break; case kModeKeypad: _redrawSentenceLine = false; - state = USERSTATE_SET_IFACE | + state = USERSTATE_SET_IFACE | USERSTATE_SET_CURSOR | USERSTATE_CURSOR_ON | USERSTATE_SET_FREEZE | USERSTATE_FREEZE_ON; break; case kModeNormal: case kModeNoNewKid: - state = USERSTATE_SET_IFACE | USERSTATE_IFACE_ALL | + state = USERSTATE_SET_IFACE | USERSTATE_IFACE_ALL | USERSTATE_SET_CURSOR | USERSTATE_CURSOR_ON | USERSTATE_SET_FREEZE; break; @@ -688,7 +688,7 @@ void ScummEngine_v0::o_animateActor() { Actor_v0 *a = (Actor_v0*) derefActor(act, "o_animateActor"); a->_animFrameRepeat = repeat; - + switch (anim) { case 0xFE: @@ -700,7 +700,7 @@ void ScummEngine_v0::o_animateActor() { // 0x69A3 a->_speaking = 0x00; return; - + case 0xFF: a->stopActorMoving(); return; diff --git a/engines/scumm/script_v2.cpp b/engines/scumm/script_v2.cpp index ce162b4a6a..96d422d5bb 100644 --- a/engines/scumm/script_v2.cpp +++ b/engines/scumm/script_v2.cpp @@ -993,7 +993,7 @@ void ScummEngine_v2::o2_drawSentence() { const byte *temp; int slot = getVerbSlot(VAR(VAR_SENTENCE_VERB), 0); - if (!((_userState & USERSTATE_IFACE_SENTENCE) || + if (!((_userState & USERSTATE_IFACE_SENTENCE) || (_game.platform == Common::kPlatformNES && (_userState & USERSTATE_IFACE_ALL)))) return; @@ -1486,8 +1486,8 @@ void ScummEngine_v2::o2_cutscene() { VAR(VAR_CURSORSTATE) = 200; // Hide inventory, freeze scripts, hide cursor - setUserState(USERSTATE_SET_IFACE | - USERSTATE_SET_CURSOR | + setUserState(USERSTATE_SET_IFACE | + USERSTATE_SET_CURSOR | USERSTATE_SET_FREEZE | USERSTATE_FREEZE_ON); _sentenceNum = 0; diff --git a/engines/scumm/script_v5.cpp b/engines/scumm/script_v5.cpp index a5591b701f..0bf51a2816 100644 --- a/engines/scumm/script_v5.cpp +++ b/engines/scumm/script_v5.cpp @@ -1097,7 +1097,7 @@ void ScummEngine_v5::o5_getDist() { int r; getResultPos(); - + o1 = getVarOrDirectWord(PARAM_1); o2 = getVarOrDirectWord(PARAM_2); diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp index d0f46f3e56..3afeeda13d 100644 --- a/engines/scumm/scumm.cpp +++ b/engines/scumm/scumm.cpp @@ -61,7 +61,9 @@ #include "scumm/player_v2cms.h" #include "scumm/player_v2a.h" #include "scumm/player_v3a.h" +#include "scumm/player_v3m.h" #include "scumm/player_v4a.h" +#include "scumm/player_v5m.h" #include "scumm/resource.h" #include "scumm/he/resource_he.h" #include "scumm/scumm_v0.h" @@ -73,6 +75,7 @@ #include "scumm/util.h" #include "scumm/verbs.h" #include "scumm/imuse/pcspk.h" +#include "scumm/imuse/mac_m68k.h" #include "backends/audiocd/audiocd.h" @@ -1818,6 +1821,12 @@ void ScummEngine::setupMusic(int midi) { #endif } else if (_game.platform == Common::kPlatformAmiga && _game.version <= 4) { _musicEngine = new Player_V4A(this, _mixer); + } else if (_game.platform == Common::kPlatformMacintosh && _game.id == GID_LOOM) { + _musicEngine = new Player_V3M(this, _mixer); + ((Player_V3M *)_musicEngine)->init(); + } else if (_game.platform == Common::kPlatformMacintosh && _game.id == GID_MONKEY) { + _musicEngine = new Player_V5M(this, _mixer); + ((Player_V5M *)_musicEngine)->init(); } else if (_game.id == GID_MANIAC && _game.version == 1) { _musicEngine = new Player_V1(this, _mixer, MidiDriver::getMusicType(dev) != MT_PCSPK); } else if (_game.version <= 2) { @@ -1835,17 +1844,33 @@ void ScummEngine::setupMusic(int midi) { } else if (_game.version >= 3 && _game.heversion <= 62) { MidiDriver *nativeMidiDriver = 0; MidiDriver *adlibMidiDriver = 0; - - if (_sound->_musicType != MDT_ADLIB && _sound->_musicType != MDT_TOWNS && _sound->_musicType != MDT_PCSPK) + bool multi_midi = ConfMan.getBool("multi_midi") && _sound->_musicType != MDT_NONE && _sound->_musicType != MDT_PCSPK && (midi & MDT_ADLIB); + bool useOnlyNative = false; + + if (isMacM68kIMuse()) { + // We setup this driver as native MIDI driver to avoid playback + // of the Mac music via a selected MIDI device. + nativeMidiDriver = new MacM68kDriver(_mixer); + // The Mac driver is never MT-32. + _native_mt32 = false; + // Ignore non-native drivers. This also ignores the multi MIDI setting. + useOnlyNative = true; + } else if (_sound->_musicType != MDT_ADLIB && _sound->_musicType != MDT_TOWNS && _sound->_musicType != MDT_PCSPK) { nativeMidiDriver = MidiDriver::createMidi(dev); + } + if (nativeMidiDriver != NULL && _native_mt32) nativeMidiDriver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE); - bool multi_midi = ConfMan.getBool("multi_midi") && _sound->_musicType != MDT_NONE && _sound->_musicType != MDT_PCSPK && (midi & MDT_ADLIB); - if (_sound->_musicType == MDT_ADLIB || _sound->_musicType == MDT_TOWNS || multi_midi) { - adlibMidiDriver = MidiDriver::createMidi(MidiDriver::detectDevice(_sound->_musicType == MDT_TOWNS ? MDT_TOWNS : MDT_ADLIB)); - adlibMidiDriver->property(MidiDriver::PROP_OLD_ADLIB, (_game.features & GF_SMALL_HEADER) ? 1 : 0); - } else if (_sound->_musicType == MDT_PCSPK) { - adlibMidiDriver = new PcSpkDriver(_mixer); + + if (!useOnlyNative) { + if (_sound->_musicType == MDT_ADLIB || _sound->_musicType == MDT_TOWNS || multi_midi) { + adlibMidiDriver = MidiDriver::createMidi(MidiDriver::detectDevice(_sound->_musicType == MDT_TOWNS ? MDT_TOWNS : MDT_ADLIB)); + adlibMidiDriver->property(MidiDriver::PROP_OLD_ADLIB, (_game.features & GF_SMALL_HEADER) ? 1 : 0); + // Try to use OPL3 mode for Sam&Max when possible. + adlibMidiDriver->property(MidiDriver::PROP_SCUMM_OPL3, (_game.id == GID_SAMNMAX) ? 1 : 0); + } else if (_sound->_musicType == MDT_PCSPK) { + adlibMidiDriver = new PcSpkDriver(_mixer); + } } _imuse = IMuse::create(_system, nativeMidiDriver, adlibMidiDriver); @@ -1971,11 +1996,11 @@ Common::Error ScummEngine::go() { if (delta < 1) // Ensure we don't get into an endless loop delta = 1; // by not decreasing sleepers. - // WORKAROUND: walking speed in the original v0/v1 interpreter + // WORKAROUND: walking speed in the original v0/v1 interpreter // is sometimes slower (e.g. during scrolling) than in ScummVM. // This is important for the door-closing action in the dungeon, - // otherwise (delta < 6) a single kid is able to escape. - if ((_game.version == 0 && isScriptRunning(132)) || + // otherwise (delta < 6) a single kid is able to escape. + if ((_game.version == 0 && isScriptRunning(132)) || (_game.version == 1 && isScriptRunning(137))) delta = 6; diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h index c8cf096a19..a77c1c0141 100644 --- a/engines/scumm/scumm.h +++ b/engines/scumm/scumm.h @@ -150,7 +150,13 @@ enum GameFeatures { GF_HE_985 = 1 << 14, /** HE games with 16 bit color */ - GF_16BIT_COLOR = 1 << 15 + GF_16BIT_COLOR = 1 << 15, + + /** + * SCUMM v5-v7 Mac games stored in a container file + * Used to differentiate between m68k and PPC versions of Indy4 + */ + GF_MAC_CONTAINER = 1 << 16 }; /* SCUMM Debug Channels */ @@ -713,6 +719,9 @@ public: bool openFile(BaseScummFile &file, const Common::String &filename, bool resourceFile = false); + /** Is this game a Mac m68k v5 game with iMuse? */ + bool isMacM68kIMuse() const; + protected: int _resourceHeaderSize; byte _resourceMapper[128]; @@ -1363,7 +1372,7 @@ public: public: bool towns_isRectInStringBox(int x1, int y1, int x2, int y2); byte _townsPaletteFlags; - byte _townsCharsetColorMap[16]; + byte _townsCharsetColorMap[16]; protected: void towns_drawStripToScreen(VirtScreen *vs, int dstX, int dstY, int srcX, int srcY, int w, int h); diff --git a/engines/scumm/sound.cpp b/engines/scumm/sound.cpp index 1dc026ad52..2fe16c5441 100644 --- a/engines/scumm/sound.cpp +++ b/engines/scumm/sound.cpp @@ -248,7 +248,10 @@ void Sound::playSound(int soundID) { _mixer->playStream(Audio::Mixer::kSFXSoundType, NULL, stream, soundID); } // Support for sampled sound effects in Monkey Island 1 and 2 - else if (_vm->_game.platform != Common::kPlatformFMTowns && READ_BE_UINT32(ptr) == MKTAG('S','B','L',' ')) { + else if (_vm->_game.platform != Common::kPlatformFMTowns + // The Macintosh m68k versions of MI2/Indy4 just ignore SBL effects. + && !_vm->isMacM68kIMuse() + && READ_BE_UINT32(ptr) == MKTAG('S','B','L',' ')) { debugC(DEBUG_SOUND, "Using SBL sound effect"); // SBL resources essentially contain VOC sound data. @@ -343,29 +346,6 @@ void Sound::playSound(int soundID) { warning("Scumm::Sound::playSound: encountered audio resoure with chunk type 'SOUN' and sound type %d", type); } } - else if ((_vm->_game.id == GID_LOOM) && (_vm->_game.platform == Common::kPlatformMacintosh)) { - // Mac version of Loom uses yet another sound format - /* - playSound #9 (room 70) - 000000: 55 00 00 45 73 6f 00 64 01 00 00 00 00 00 00 00 |U..Eso.d........| - 000010: 00 05 00 8e 2a 8f 2d 1c 2a 8f 2a 8f 2d 1c 00 28 |....*.-.*.*.-..(| - 000020: 00 31 00 3a 00 43 00 4c 00 01 00 00 00 01 00 64 |.1.:.C.L.......d| - 000030: 5a 00 01 00 00 00 01 00 64 00 00 01 00 00 00 01 |Z.......d.......| - 000040: 00 64 5a 00 01 00 00 00 01 00 64 5a 00 01 00 00 |.dZ.......dZ....| - 000050: 00 01 00 64 00 00 00 00 00 00 00 07 00 00 00 64 |...d...........d| - 000060: 64 00 00 4e 73 6f 00 64 01 00 00 00 00 00 00 00 |d..Nso.d........| - 000070: 00 05 00 89 3d 57 2d 1c 3d 57 3d 57 2d 1c 00 28 |....=W-.=W=W-..(| - playSound #16 (room 69) - 000000: dc 00 00 a5 73 6f 00 64 01 00 00 00 00 00 00 00 |....so.d........| - 000010: 00 05 00 00 2a 8f 03 e8 03 e8 03 e8 03 e8 00 28 |....*..........(| - 000020: 00 79 00 7f 00 85 00 d6 00 01 00 00 00 19 01 18 |.y..............| - 000030: 2f 00 18 00 01 18 32 00 18 00 01 18 36 00 18 00 |/.....2.....6...| - 000040: 01 18 3b 00 18 00 01 18 3e 00 18 00 01 18 42 00 |..;.....>.....B.| - 000050: 18 00 01 18 47 00 18 00 01 18 4a 00 18 00 01 18 |....G.....J.....| - 000060: 4e 00 10 00 01 18 53 00 10 00 01 18 56 00 10 00 |N.....S.....V...| - 000070: 01 18 5a 00 10 00 02 28 5f 00 01 00 00 00 00 00 |..Z....(_.......| - */ - } else if ((_vm->_game.platform == Common::kPlatformMacintosh) && (_vm->_game.id == GID_INDY3) && READ_BE_UINT16(ptr + 8) == 0x1C) { // Sound format as used in Indy3 EGA Mac. // It seems to be closely related to the Amiga format, see player_v3a.cpp @@ -411,8 +391,7 @@ void Sound::playSound(int soundID) { } else { - if (_vm->_game.id == GID_MONKEY_VGA || _vm->_game.id == GID_MONKEY_EGA - || (_vm->_game.id == GID_MONKEY && _vm->_game.platform == Common::kPlatformMacintosh)) { + if (_vm->_game.id == GID_MONKEY_VGA || _vm->_game.id == GID_MONKEY_EGA) { // Works around the fact that in some places in MonkeyEGA/VGA, // the music is never explicitly stopped. // Rather it seems that starting a new music is supposed to @@ -954,7 +933,7 @@ void Sound::setupSfxFile() { if (file.open(tmp)) _sfxFilename = tmp; - + if (_vm->_game.heversion <= 74) _sfxFileEncByte = 0x69; @@ -1083,9 +1062,6 @@ void Sound::saveLoadWithSerializer(Serializer *ser) { #pragma mark --- Sound resource handling --- #pragma mark - -static void convertMac0Resource(ResourceManager *res, ResId idx, byte *src_ptr, int size); - - /* * TODO: The way we handle sound/music resources really is one huge hack. * We probably should reconsider how we do this, and maybe come up with a @@ -1179,7 +1155,7 @@ int ScummEngine::readSoundResource(ResId idx) { // its sound resources, and Amiga games, which feature only ROL // resources, since we are a doing Midi -> AdLib conversion for // these. - if ((_sound->_musicType == MDT_ADLIB || _sound->_musicType == MDT_TOWNS) && pri != 16 + if ((_sound->_musicType == MDT_ADLIB || _sound->_musicType == MDT_TOWNS) && pri != 16 && pri != 15 && pri != 10 && pri != 2 && _game.platform != Common::kPlatformAmiga) pri = -1; @@ -1205,11 +1181,9 @@ int ScummEngine::readSoundResource(ResId idx) { case MKTAG('M','a','c','0'): _fileHandle->seek(-12, SEEK_CUR); total_size = _fileHandle->readUint32BE() - 8; - ptr = (byte *)calloc(total_size, 1); + ptr = _res->createResource(rtSound, idx, total_size); _fileHandle->read(ptr, total_size); //dumpResource("sound-", idx, ptr); - convertMac0Resource(_res, idx, ptr, total_size); - free(ptr); return 1; case MKTAG('M','a','c','1'): @@ -1442,219 +1416,6 @@ static byte *writeVLQ(byte *ptr, int value) { return ptr; } -static byte Mac0ToGMInstrument(uint32 type, int &transpose) { - transpose = 0; - switch (type) { - case MKTAG('M','A','R','I'): return 12; - case MKTAG('P','L','U','C'): return 45; - case MKTAG('H','A','R','M'): return 22; - case MKTAG('P','I','P','E'): return 19; - case MKTAG('T','R','O','M'): transpose = -12; return 57; - case MKTAG('S','T','R','I'): return 48; - case MKTAG('H','O','R','N'): return 60; - case MKTAG('V','I','B','E'): return 11; - case MKTAG('S','H','A','K'): return 77; - case MKTAG('P','A','N','P'): return 75; - case MKTAG('W','H','I','S'): return 76; - case MKTAG('O','R','G','A'): return 17; - case MKTAG('B','O','N','G'): return 115; - case MKTAG('B','A','S','S'): transpose = -24; return 35; - default: - error("Unknown Mac0 instrument %s found", tag2str(type)); - } -} - -static void convertMac0Resource(ResourceManager *res, ResId idx, byte *src_ptr, int size) { - /* - From Markus Magnuson (superqult) we got this information: - Mac0 - --- - 4 bytes - 'SOUN' - BE 4 bytes - block length - - 4 bytes - 'Mac0' - BE 4 bytes - (blockLength - 27) - 28 bytes - ??? - - do this three times (once for each channel): - 4 bytes - 'Chan' - BE 4 bytes - channel length - 4 bytes - instrument name (e.g. 'MARI') - - do this for ((chanLength-24)/4) times: - 2 bytes - note duration - 1 byte - note value - 1 byte - note velocity - - 4 bytes - ??? - 4 bytes - 'Loop'/'Done' - 4 bytes - ??? - - 1 byte - 0x09 - --- - - Instruments (General Midi): - "MARI" - Marimba (12) - "PLUC" - Pizzicato Strings (45) - "HARM" - Harmonica (22) - "PIPE" - Church Organ? (19) or Flute? (73) or Bag Pipe (109) - "TROM" - Trombone (57) - "STRI" - String Ensemble (48 or 49) - "HORN" - French Horn? (60) or English Horn? (69) - "VIBE" - Vibraphone (11) - "SHAK" - Shakuhachi? (77) - "PANP" - Pan Flute (75) - "WHIS" - Whistle (78) / Bottle (76) - "ORGA" - Drawbar Organ (16; but could also be 17-20) - "BONG" - Woodblock? (115) - "BASS" - Bass (32-39) - - - Now the task could be to convert this into MIDI, to be fed into iMuse. - Or we do something similiar to what is done in Player_V3, assuming - we can identify SFX in the MI datafiles for each of the instruments - listed above. - */ - -#if 0 - byte *ptr = _res->createResource(rtSound, idx, size); - memcpy(ptr, src_ptr, size); -#else - const int ppqn = 480; - byte *ptr, *start_ptr; - - int total_size = 0; - total_size += kMIDIHeaderSize; // Header - total_size += 7; // Tempo META - total_size += 3 * 3; // Three program change mesages - total_size += 22; // Possible jump SysEx - total_size += 5; // EOT META - - int i, len; - byte track_instr[3]; - byte *track_data[3]; - int track_len[3]; - int track_transpose[3]; - bool looped = false; - - src_ptr += 8; - // TODO: Decipher the unknown bytes in the header. For now, skip 'em - src_ptr += 28; - - // Parse the three channels - for (i = 0; i < 3; i++) { - assert(READ_BE_UINT32(src_ptr) == MKTAG('C','h','a','n')); - len = READ_BE_UINT32(src_ptr + 4); - track_len[i] = len - 24; - track_instr[i] = Mac0ToGMInstrument(READ_BE_UINT32(src_ptr + 8), track_transpose[i]); - track_data[i] = src_ptr + 12; - src_ptr += len; - looped = (READ_BE_UINT32(src_ptr - 8) == MKTAG('L','o','o','p')); - - // For each note event, we need up to 6 bytes for the - // Note On (3 VLQ, 3 event), and 6 bytes for the Note - // Off (3 VLQ, 3 event). So 12 bytes total. - total_size += 12 * track_len[i]; - } - assert(*src_ptr == 0x09); - - // Create sound resource - start_ptr = res->createResource(rtSound, idx, total_size); - - // Insert MIDI header - ptr = writeMIDIHeader(start_ptr, "GMD ", ppqn, total_size); - - // Write a tempo change Meta event - // 473 / 4 Hz, convert to micro seconds. - uint32 dw = 1000000 * 437 / 4 / ppqn; // 1000000 * ppqn * 4 / 473; - memcpy(ptr, "\x00\xFF\x51\x03", 4); ptr += 4; - *ptr++ = (byte)((dw >> 16) & 0xFF); - *ptr++ = (byte)((dw >> 8) & 0xFF); - *ptr++ = (byte)(dw & 0xFF); - - // Insert program change messages - *ptr++ = 0; // VLQ - *ptr++ = 0xC0; - *ptr++ = track_instr[0]; - *ptr++ = 0; // VLQ - *ptr++ = 0xC1; - *ptr++ = track_instr[1]; - *ptr++ = 0; // VLQ - *ptr++ = 0xC2; - *ptr++ = track_instr[2]; - - // And now, the actual composition. Please turn all cell phones - // and pagers off during the performance. Thank you. - uint16 nextTime[3] = { 1, 1, 1 }; - int stage[3] = { 0, 0, 0 }; - - while (track_len[0] | track_len[1] | track_len[2]) { - int best = -1; - uint16 bestTime = 0xFFFF; - for (i = 0; i < 3; ++i) { - if (track_len[i] && nextTime[i] < bestTime) { - bestTime = nextTime[i]; - best = i; - } - } - assert (best != -1); - - if (!stage[best]) { - // We are STARTING this event. - if (track_data[best][2] > 1) { - // Note On - ptr = writeVLQ(ptr, nextTime[best]); - *ptr++ = 0x90 | best; - *ptr++ = track_data[best][2] + track_transpose[best]; - *ptr++ = track_data[best][3] * 127 / 100; // Scale velocity - for (i = 0; i < 3; ++i) - nextTime[i] -= bestTime; - } - nextTime[best] += READ_BE_UINT16 (track_data[best]); - stage[best] = 1; - } else { - // We are ENDING this event. - if (track_data[best][2] > 1) { - // There was a Note On, so do a Note Off - ptr = writeVLQ(ptr, nextTime[best]); - *ptr++ = 0x80 | best; - *ptr++ = track_data[best][2] + track_transpose[best]; - *ptr++ = track_data[best][3] * 127 / 100; // Scale velocity - for (i = 0; i < 3; ++i) - nextTime[i] -= bestTime; - } - track_data[best] += 4; - track_len[best] -= 4; - stage[best] = 0; - } - } - - // Is this a looped song? If so, effect a loop by - // using the S&M maybe_jump SysEx command. - // FIXME: Jamieson630: The jump seems to be happening - // too quickly! There should maybe be a pause after - // the last Note Off? But I couldn't find one in the - // MI1 Lookout music, where I was hearing problems. - if (looped) { - memcpy(ptr, "\x00\xf0\x13\x7d\x30\00", 6); ptr += 6; // maybe_jump - memcpy(ptr, "\x00\x00", 2); ptr += 2; // cmd -> 0 means always jump - memcpy(ptr, "\x00\x00\x00\x00", 4); ptr += 4; // track -> 0 (only track) - memcpy(ptr, "\x00\x00\x00\x01", 4); ptr += 4; // beat -> 1 (first beat) - memcpy(ptr, "\x00\x00\x00\x01", 4); ptr += 4; // tick -> 1 - memcpy(ptr, "\x00\xf7", 2); ptr += 2; // SysEx end marker - } - - // Insert end of song META - memcpy(ptr, "\x00\xff\x2f\x00\x00", 5); ptr += 5; - - assert(ptr <= start_ptr + total_size); - - // Rewrite MIDI header, this time with true size - total_size = ptr - start_ptr; - ptr = writeMIDIHeader(start_ptr, "GMD ", ppqn, total_size); -#endif -} - static void convertADResource(ResourceManager *res, const GameSettings& game, ResId idx, byte *src_ptr, int size) { // We will ignore the PPQN in the original resource, because // it's invalid anyway. We use a constant PPQN of 480. diff --git a/engines/scumm/verbs.cpp b/engines/scumm/verbs.cpp index 567ca31485..0d0f6cdb95 100644 --- a/engines/scumm/verbs.cpp +++ b/engines/scumm/verbs.cpp @@ -84,7 +84,7 @@ int ScummEngine_v0::verbPrepIdType(int verbid) { switch (verbid) { case kVerbUse: // depends on object1 return kVerbPrepObject; - case kVerbGive: + case kVerbGive: return kVerbPrepTo; case kVerbUnlock: case kVerbFix: return kVerbPrepWith; @@ -693,7 +693,7 @@ void ScummEngine_v0::verbExec() { if (_activeVerb == kVerbWhatIs) return; - + if (!(_activeVerb == kVerbWalkTo && _activeObject == 0)) { doSentence(_activeVerb, _activeObject, _activeObject2); if (_activeVerb != kVerbWalkTo) { diff --git a/engines/sky/compact.cpp b/engines/sky/compact.cpp index cf9bd55b1a..ee165934a0 100644 --- a/engines/sky/compact.cpp +++ b/engines/sky/compact.cpp @@ -34,7 +34,7 @@ namespace Sky { #define SKY_CPT_SIZE 419427 -#define OFFS(type,item) (((ptrdiff_t)(&((type *)42)->item))-42) +#define OFFS(type,item) ((uint32)(((ptrdiff_t)(&((type *)42)->item))-42)) #define MK32(type,item) OFFS(type, item),0,0,0 #define MK16(type,item) OFFS(type, item),0 #define MK32_A5(type, item) MK32(type, item[0]), MK32(type, item[1]), \ diff --git a/engines/sky/detection.cpp b/engines/sky/detection.cpp index dfa3ded50b..8f6c2bb6a2 100644 --- a/engines/sky/detection.cpp +++ b/engines/sky/detection.cpp @@ -119,12 +119,12 @@ GameList SkyMetaEngine::getSupportedGames() const { const ExtraGuiOptions SkyMetaEngine::getExtraGuiOptions(const Common::String &target) const { Common::String guiOptions; ExtraGuiOptions options; - + if (target.empty()) { options.push_back(skyExtraGuiOption); return options; } - + if (ConfMan.hasKey("guioptions", target)) { guiOptions = ConfMan.get("guioptions", target); guiOptions = parseGameGUIOptions(guiOptions); diff --git a/engines/sword1/animation.cpp b/engines/sword1/animation.cpp index ddafd964eb..ff3c897dba 100644 --- a/engines/sword1/animation.cpp +++ b/engines/sword1/animation.cpp @@ -37,6 +37,7 @@ #include "gui/message.h" +#include "video/dxa_decoder.h" #include "video/psx_decoder.h" #include "video/smk_decoder.h" @@ -68,7 +69,7 @@ static const char *const sequenceList[20] = { }; // This is the list of the names of the PlayStation videos -// TODO: fight.str, flashy.str, +// TODO: fight.str, flashy.str, static const char *const sequenceListPSX[20] = { "e_ferr1", "ladder1", @@ -96,9 +97,8 @@ static const char *const sequenceListPSX[20] = { // Basic movie player /////////////////////////////////////////////////////////////////////////////// -MoviePlayer::MoviePlayer(SwordEngine *vm, Text *textMan, ResMan *resMan, Audio::Mixer *snd, OSystem *system, Audio::SoundHandle *bgSoundHandle, Video::VideoDecoder *decoder, DecoderType decoderType) - : _vm(vm), _textMan(textMan), _resMan(resMan), _snd(snd), _bgSoundHandle(bgSoundHandle), _system(system) { - _bgSoundStream = NULL; +MoviePlayer::MoviePlayer(SwordEngine *vm, Text *textMan, ResMan *resMan, OSystem *system, Video::VideoDecoder *decoder, DecoderType decoderType) + : _vm(vm), _textMan(textMan), _resMan(resMan), _system(system) { _decoderType = decoderType; _decoder = decoder; @@ -107,7 +107,6 @@ MoviePlayer::MoviePlayer(SwordEngine *vm, Text *textMan, ResMan *resMan, Audio:: } MoviePlayer::~MoviePlayer() { - delete _bgSoundHandle; delete _decoder; } @@ -116,16 +115,12 @@ MoviePlayer::~MoviePlayer() { * @param id the id of the file */ bool MoviePlayer::load(uint32 id) { - Common::File f; Common::String filename; - if (_decoderType == kVideoDecoderDXA) - _bgSoundStream = Audio::SeekableAudioStream::openStreamFile(sequenceList[id]); - else - _bgSoundStream = NULL; - if (SwordEngine::_systemVars.showText) { + Common::File f; filename = Common::String::format("%s.txt", sequenceList[id]); + if (f.open(filename)) { Common::String line; int lineNo = 0; @@ -157,19 +152,18 @@ bool MoviePlayer::load(uint32 id) { warning("%s:%d startFrame (%d) <= lastEnd (%d)", filename.c_str(), lineNo, startFrame, lastEnd); continue; } - + int color = 0; if (*ptr == '@') { ++ptr; color = strtoul(ptr, const_cast<char **>(&ptr), 10); while (*ptr && Common::isSpace(*ptr)) ptr++; - } + } _movieTexts.push_back(MovieText(startFrame, endFrame, ptr, color)); lastEnd = endFrame; } - f.close(); } } @@ -189,6 +183,7 @@ bool MoviePlayer::load(uint32 id) { // Need to load here in case it fails in which case we'd need // to go back to paletted mode if (_decoder->loadFile(filename)) { + _decoder->start(); return true; } else { initGraphics(g_system->getWidth(), g_system->getHeight(), true); @@ -197,30 +192,27 @@ bool MoviePlayer::load(uint32 id) { break; } - return _decoder->loadFile(filename.c_str()); -} + if (!_decoder->loadFile(filename)) + return false; -void MoviePlayer::play() { - if (_bgSoundStream) - _snd->playStream(Audio::Mixer::kSFXSoundType, _bgSoundHandle, _bgSoundStream); + // For DXA, also add the external sound file + if (_decoderType == kVideoDecoderDXA) + _decoder->addStreamFileTrack(sequenceList[id]); - bool terminated = false; + _decoder->start(); + return true; +} +void MoviePlayer::play() { _textX = 0; _textY = 0; - terminated = !playVideo(); - - if (terminated) - _snd->stopHandle(*_bgSoundHandle); + playVideo(); _textMan->releaseText(2, false); _movieTexts.clear(); - while (_snd->isSoundHandleActive(*_bgSoundHandle)) - _system->delayMillis(100); - // It's tempting to call _screen->fullRefresh() here to restore the old // palette. However, that causes glitches with DXA movies, where the // previous location would be momentarily drawn, before switching to @@ -320,7 +312,7 @@ bool MoviePlayer::playVideo() { } if (_decoder->hasDirtyPalette()) { - _decoder->setSystemPalette(); + _vm->_system->getPaletteManager()->setPalette(_decoder->getPalette(), 0, 256); if (!_movieTexts.empty()) { // Look for the best color indexes to use to display the subtitles @@ -506,24 +498,12 @@ void MoviePlayer::drawFramePSX(const Graphics::Surface *frame) { scaledFrame.free(); } -DXADecoderWithSound::DXADecoderWithSound(Audio::Mixer *mixer, Audio::SoundHandle *bgSoundHandle) - : _mixer(mixer), _bgSoundHandle(bgSoundHandle) { -} - -uint32 DXADecoderWithSound::getTime() const { - if (_mixer->isSoundHandleActive(*_bgSoundHandle)) - return _mixer->getSoundElapsedTime(*_bgSoundHandle); - - return DXADecoder::getTime(); -} - /////////////////////////////////////////////////////////////////////////////// // Factory function for creating the appropriate cutscene player /////////////////////////////////////////////////////////////////////////////// -MoviePlayer *makeMoviePlayer(uint32 id, SwordEngine *vm, Text *textMan, ResMan *resMan, Audio::Mixer *snd, OSystem *system) { +MoviePlayer *makeMoviePlayer(uint32 id, SwordEngine *vm, Text *textMan, ResMan *resMan, OSystem *system) { Common::String filename; - Audio::SoundHandle *bgSoundHandle = new Audio::SoundHandle; // For the PSX version, we'll try the PlayStation stream files if (vm->isPsx()) { @@ -534,7 +514,7 @@ MoviePlayer *makeMoviePlayer(uint32 id, SwordEngine *vm, Text *textMan, ResMan * #ifdef USE_RGB_COLOR // All BS1 PSX videos run the videos at 2x speed Video::VideoDecoder *psxDecoder = new Video::PSXStreamDecoder(Video::PSXStreamDecoder::kCD2x); - return new MoviePlayer(vm, textMan, resMan, snd, system, bgSoundHandle, psxDecoder, kVideoDecoderPSX); + return new MoviePlayer(vm, textMan, resMan, system, psxDecoder, kVideoDecoderPSX); #else GUI::MessageDialog dialog(Common::String::format(_("PSX stream cutscene '%s' cannot be played in paletted mode"), filename.c_str()), _("OK")); dialog.runModal(); @@ -546,20 +526,20 @@ MoviePlayer *makeMoviePlayer(uint32 id, SwordEngine *vm, Text *textMan, ResMan * filename = Common::String::format("%s.smk", sequenceList[id]); if (Common::File::exists(filename)) { - Video::SmackerDecoder *smkDecoder = new Video::SmackerDecoder(snd); - return new MoviePlayer(vm, textMan, resMan, snd, system, bgSoundHandle, smkDecoder, kVideoDecoderSMK); + Video::SmackerDecoder *smkDecoder = new Video::SmackerDecoder(); + return new MoviePlayer(vm, textMan, resMan, system, smkDecoder, kVideoDecoderSMK); } filename = Common::String::format("%s.dxa", sequenceList[id]); if (Common::File::exists(filename)) { #ifdef USE_ZLIB - DXADecoderWithSound *dxaDecoder = new DXADecoderWithSound(snd, bgSoundHandle); - return new MoviePlayer(vm, textMan, resMan, snd, system, bgSoundHandle, dxaDecoder, kVideoDecoderDXA); + Video::VideoDecoder *dxaDecoder = new Video::DXADecoder(); + return new MoviePlayer(vm, textMan, resMan, system, dxaDecoder, kVideoDecoderDXA); #else GUI::MessageDialog dialog(_("DXA cutscenes found but ScummVM has been built without zlib support"), _("OK")); dialog.runModal(); - return NULL; + return 0; #endif } @@ -569,7 +549,7 @@ MoviePlayer *makeMoviePlayer(uint32 id, SwordEngine *vm, Text *textMan, ResMan * if (Common::File::exists(filename)) { GUI::MessageDialog dialog(_("MPEG2 cutscenes are no longer supported"), _("OK")); dialog.runModal(); - return NULL; + return 0; } if (!vm->isPsx() || scumm_stricmp(sequenceList[id], "enddemo") != 0) { @@ -578,7 +558,7 @@ MoviePlayer *makeMoviePlayer(uint32 id, SwordEngine *vm, Text *textMan, ResMan * dialog.runModal(); } - return NULL; + return 0; } } // End of namespace Sword1 diff --git a/engines/sword1/animation.h b/engines/sword1/animation.h index c2ed86a1a3..d0c61f5eb3 100644 --- a/engines/sword1/animation.h +++ b/engines/sword1/animation.h @@ -23,16 +23,19 @@ #ifndef SWORD1_ANIMATION_H #define SWORD1_ANIMATION_H -#include "video/dxa_decoder.h" -#include "video/video_decoder.h" - #include "common/list.h" -#include "audio/audiostream.h" - #include "sword1/screen.h" #include "sword1/sound.h" +namespace Graphics { +struct Surface; +} + +namespace Video { +class VideoDecoder; +} + namespace Sword1 { enum DecoderType { @@ -55,21 +58,9 @@ public: } }; -class DXADecoderWithSound : public Video::DXADecoder { -public: - DXADecoderWithSound(Audio::Mixer *mixer, Audio::SoundHandle *bgSoundHandle); - ~DXADecoderWithSound() {} - - uint32 getTime() const; - -private: - Audio::Mixer *_mixer; - Audio::SoundHandle *_bgSoundHandle; -}; - class MoviePlayer { public: - MoviePlayer(SwordEngine *vm, Text *textMan, ResMan *resMan, Audio::Mixer *snd, OSystem *system, Audio::SoundHandle *bgSoundHandle, Video::VideoDecoder *decoder, DecoderType decoderType); + MoviePlayer(SwordEngine *vm, Text *textMan, ResMan *resMan, OSystem *system, Video::VideoDecoder *decoder, DecoderType decoderType); virtual ~MoviePlayer(); bool load(uint32 id); void play(); @@ -78,7 +69,6 @@ protected: SwordEngine *_vm; Text *_textMan; ResMan *_resMan; - Audio::Mixer *_snd; OSystem *_system; Common::List<MovieText> _movieTexts; int _textX, _textY, _textWidth, _textHeight; @@ -88,8 +78,6 @@ protected: DecoderType _decoderType; Video::VideoDecoder *_decoder; - Audio::SoundHandle *_bgSoundHandle; - Audio::AudioStream *_bgSoundStream; bool playVideo(); void performPostProcessing(byte *screen); @@ -100,7 +88,7 @@ protected: void convertColor(byte r, byte g, byte b, float &h, float &s, float &v); }; -MoviePlayer *makeMoviePlayer(uint32 id, SwordEngine *vm, Text *textMan, ResMan *resMan, Audio::Mixer *snd, OSystem *system); +MoviePlayer *makeMoviePlayer(uint32 id, SwordEngine *vm, Text *textMan, ResMan *resMan, OSystem *system); } // End of namespace Sword1 diff --git a/engines/sword1/control.cpp b/engines/sword1/control.cpp index 6e395116f9..7cd85dff54 100644 --- a/engines/sword1/control.cpp +++ b/engines/sword1/control.cpp @@ -541,7 +541,9 @@ void Control::setupMainPanel() { if (SwordEngine::_systemVars.controlPanelMode == CP_DEATHSCREEN) panelId = SR_DEATHPANEL; else { - if (SwordEngine::_systemVars.language <= BS1_SPANISH) + if (SwordEngine::_systemVars.realLanguage == Common::EN_USA) + panelId = SR_PANEL_AMERICAN; + else if (SwordEngine::_systemVars.language <= BS1_SPANISH) panelId = SR_PANEL_ENGLISH + SwordEngine::_systemVars.language; else panelId = SR_PANEL_ENGLISH; diff --git a/engines/sword1/detection.cpp b/engines/sword1/detection.cpp index 5662e4672b..f3af04c81d 100644 --- a/engines/sword1/detection.cpp +++ b/engines/sword1/detection.cpp @@ -143,9 +143,24 @@ GameDescriptor SwordMetaEngine::findGame(const char *gameid) const { return GameDescriptor(); } -void Sword1CheckDirectory(const Common::FSList &fslist, bool *filesFound) { +void Sword1CheckDirectory(const Common::FSList &fslist, bool *filesFound, bool recursion = false) { for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) { if (!file->isDirectory()) { + // The required game data files can be located in the game directory, or in + // a subdirectory called "clusters". In the latter case, we don't want to + // detect the game in that subdirectory, as this will detect the game twice + // when mass add is searching inside a directory. In this case, the first + // result (the game directory) will be correct, but the second result (the + // clusters subdirectory) will be wrong, as the optional speech, music and + // video data files will be ignored. Note that this fix will skip the game + // data files if the user has placed them inside a "clusters" subdirectory, + // or if he/she points ScummVM directly to the "clusters" directory of the + // game CD. Fixes bug #3049346. + Common::String directory = file->getParent().getName(); + directory.toLowercase(); + if (directory.hasPrefix("clusters") && directory.size() <= 9 && !recursion) + continue; + const char *fileName = file->getName().c_str(); for (int cnt = 0; cnt < NUM_FILES_TO_CHECK; cnt++) if (scumm_stricmp(fileName, g_filesToCheck[cnt]) == 0) @@ -155,7 +170,7 @@ void Sword1CheckDirectory(const Common::FSList &fslist, bool *filesFound) { if (scumm_stricmp(file->getName().c_str(), g_dirNames[cnt]) == 0) { Common::FSList fslist2; if (file->getChildren(fslist2, Common::FSNode::kListFilesOnly)) - Sword1CheckDirectory(fslist2, filesFound); + Sword1CheckDirectory(fslist2, filesFound, true); } } } diff --git a/engines/sword1/logic.cpp b/engines/sword1/logic.cpp index 8e04861edf..757d768780 100644 --- a/engines/sword1/logic.cpp +++ b/engines/sword1/logic.cpp @@ -959,7 +959,7 @@ int Logic::fnPlaySequence(Object *cpt, int32 id, int32 sequenceId, int32 d, int3 // meantime, we don't want any looping sound effects still playing. _sound->quitScreen(); - MoviePlayer *player = makeMoviePlayer(sequenceId, _vm, _textMan, _resMan, _mixer, _system); + MoviePlayer *player = makeMoviePlayer(sequenceId, _vm, _textMan, _resMan, _system); if (player) { _screen->clearScreen(); if (player->load(sequenceId)) diff --git a/engines/sword1/objectman.cpp b/engines/sword1/objectman.cpp index 5d1864d58d..3e70a95699 100644 --- a/engines/sword1/objectman.cpp +++ b/engines/sword1/objectman.cpp @@ -107,7 +107,7 @@ char *ObjectMan::lockText(uint32 textId) { warning("Missing translation for textId %u (\"%s\")", textId, text); unlockText(textId, BS1_ENGLISH); } - + return _missingSubTitleStr; } return text; @@ -164,7 +164,7 @@ char *ObjectMan::lockText(uint32 textId, uint8 lang) { // We use the hardcoded text in this case. if (textId == 2950145) return const_cast<char *>(_translationId2950145[lang]); - + warning("ObjectMan::lockText(%d): text number has no text lines", textId); return NULL; } diff --git a/engines/sword1/sound.cpp b/engines/sword1/sound.cpp index 3574074b00..61bf5257ab 100644 --- a/engines/sword1/sound.cpp +++ b/engines/sword1/sound.cpp @@ -142,7 +142,7 @@ void Sound::checkSpeechFileEndianness() { be_diff_sum += fabs((double)(be_value - prev_be_value)); prev_be_value = be_value; } - delete [] data; + delete[] data; } // Set the big endian flag _bigEndianSpeech = (be_diff_sum < le_diff_sum); diff --git a/engines/sword1/sword1.cpp b/engines/sword1/sword1.cpp index 75e8f72d9d..fa593b8df4 100644 --- a/engines/sword1/sword1.cpp +++ b/engines/sword1/sword1.cpp @@ -116,8 +116,9 @@ Common::Error SwordEngine::init() { _systemVars.controlPanelMode = CP_NEWGAME; _systemVars.forceRestart = false; _systemVars.wantFade = true; + _systemVars.realLanguage = Common::parseLanguage(ConfMan.get("language")); - switch (Common::parseLanguage(ConfMan.get("language"))) { + switch (_systemVars.realLanguage) { case Common::DE_DEU: _systemVars.language = BS1_GERMAN; break; @@ -138,6 +139,7 @@ Common::Error SwordEngine::init() { break; default: _systemVars.language = BS1_ENGLISH; + break; } _systemVars.showText = ConfMan.getBool("subtitles"); diff --git a/engines/sword1/sword1.h b/engines/sword1/sword1.h index ccdc2d3a59..ec6555b4b3 100644 --- a/engines/sword1/sword1.h +++ b/engines/sword1/sword1.h @@ -75,6 +75,7 @@ struct SystemVars { uint8 language; bool isDemo; Common::Platform platform; + Common::Language realLanguage; }; class SwordEngine : public Engine { diff --git a/engines/sword2/animation.cpp b/engines/sword2/animation.cpp index 5e3f8929e9..00260f789a 100644 --- a/engines/sword2/animation.cpp +++ b/engines/sword2/animation.cpp @@ -38,8 +38,11 @@ #include "sword2/screen.h" #include "sword2/animation.h" +#include "graphics/palette.h" + #include "gui/message.h" +#include "video/dxa_decoder.h" #include "video/smk_decoder.h" #include "video/psx_decoder.h" @@ -51,9 +54,8 @@ namespace Sword2 { // Basic movie player /////////////////////////////////////////////////////////////////////////////// -MoviePlayer::MoviePlayer(Sword2Engine *vm, Audio::Mixer *snd, OSystem *system, Audio::SoundHandle *bgSoundHandle, Video::VideoDecoder *decoder, DecoderType decoderType) - : _vm(vm), _snd(snd), _bgSoundHandle(bgSoundHandle), _system(system) { - _bgSoundStream = NULL; +MoviePlayer::MoviePlayer(Sword2Engine *vm, OSystem *system, Video::VideoDecoder *decoder, DecoderType decoderType) + : _vm(vm), _system(system) { _decoderType = decoderType; _decoder = decoder; @@ -62,7 +64,6 @@ MoviePlayer::MoviePlayer(Sword2Engine *vm, Audio::Mixer *snd, OSystem *system, A } MoviePlayer::~MoviePlayer() { - delete _bgSoundHandle; delete _decoder; } @@ -75,11 +76,6 @@ bool MoviePlayer::load(const char *name) { if (_vm->shouldQuit()) return false; - if (_decoderType == kVideoDecoderDXA) - _bgSoundStream = Audio::SeekableAudioStream::openStreamFile(name); - else - _bgSoundStream = NULL; - _textSurface = NULL; Common::String filename; @@ -99,6 +95,7 @@ bool MoviePlayer::load(const char *name) { // Need to load here in case it fails in which case we'd need // to go back to paletted mode if (_decoder->loadFile(filename)) { + _decoder->start(); return true; } else { initGraphics(640, 480, true); @@ -106,7 +103,15 @@ bool MoviePlayer::load(const char *name) { } } - return _decoder->loadFile(filename.c_str()); + if (!_decoder->loadFile(filename)) + return false; + + // For DXA, also add the external sound file + if (_decoderType == kVideoDecoderDXA) + _decoder->addStreamFileTrack(name); + + _decoder->start(); + return true; } void MoviePlayer::play(MovieText *movieTexts, uint32 numMovieTexts, uint32 leadIn, uint32 leadOut) { @@ -122,24 +127,15 @@ void MoviePlayer::play(MovieText *movieTexts, uint32 numMovieTexts, uint32 leadI if (leadIn) _vm->_sound->playMovieSound(leadIn, kLeadInSound); - if (_bgSoundStream) - _snd->playStream(Audio::Mixer::kSFXSoundType, _bgSoundHandle, _bgSoundStream); - - bool terminated = false; - - terminated = !playVideo(); + bool terminated = !playVideo(); closeTextObject(_currentMovieText, NULL, 0); if (terminated) { - _snd->stopHandle(*_bgSoundHandle); _vm->_sound->stopMovieSounds(); _vm->_sound->stopSpeech(); } - while (_snd->isSoundHandleActive(*_bgSoundHandle)) - _system->delayMillis(100); - if (_decoderType == kVideoDecoderPSX) { // Need to jump back to paletted color initGraphics(640, 480, true); @@ -336,7 +332,7 @@ bool MoviePlayer::playVideo() { } if (_decoder->hasDirtyPalette()) { - _decoder->setSystemPalette(); + _vm->_system->getPaletteManager()->setPalette(_decoder->getPalette(), 0, 256); uint32 maxWeight = 0; uint32 minWeight = 0xFFFFFFFF; @@ -406,31 +402,19 @@ void MoviePlayer::drawFramePSX(const Graphics::Surface *frame) { scaledFrame.free(); } -DXADecoderWithSound::DXADecoderWithSound(Audio::Mixer *mixer, Audio::SoundHandle *bgSoundHandle) - : _mixer(mixer), _bgSoundHandle(bgSoundHandle) { -} - -uint32 DXADecoderWithSound::getTime() const { - if (_mixer->isSoundHandleActive(*_bgSoundHandle)) - return _mixer->getSoundElapsedTime(*_bgSoundHandle); - - return DXADecoder::getTime(); -} - /////////////////////////////////////////////////////////////////////////////// // Factory function for creating the appropriate cutscene player /////////////////////////////////////////////////////////////////////////////// -MoviePlayer *makeMoviePlayer(const char *name, Sword2Engine *vm, Audio::Mixer *snd, OSystem *system, uint32 frameCount) { +MoviePlayer *makeMoviePlayer(const char *name, Sword2Engine *vm, OSystem *system, uint32 frameCount) { Common::String filename; - Audio::SoundHandle *bgSoundHandle = new Audio::SoundHandle; filename = Common::String::format("%s.str", name); if (Common::File::exists(filename)) { #ifdef USE_RGB_COLOR Video::VideoDecoder *psxDecoder = new Video::PSXStreamDecoder(Video::PSXStreamDecoder::kCD2x, frameCount); - return new MoviePlayer(vm, snd, system, bgSoundHandle, psxDecoder, kVideoDecoderPSX); + return new MoviePlayer(vm, system, psxDecoder, kVideoDecoderPSX); #else GUI::MessageDialog dialog(_("PSX cutscenes found but ScummVM has been built without RGB color support"), _("OK")); dialog.runModal(); @@ -441,16 +425,16 @@ MoviePlayer *makeMoviePlayer(const char *name, Sword2Engine *vm, Audio::Mixer *s filename = Common::String::format("%s.smk", name); if (Common::File::exists(filename)) { - Video::SmackerDecoder *smkDecoder = new Video::SmackerDecoder(snd); - return new MoviePlayer(vm, snd, system, bgSoundHandle, smkDecoder, kVideoDecoderSMK); + Video::SmackerDecoder *smkDecoder = new Video::SmackerDecoder(); + return new MoviePlayer(vm, system, smkDecoder, kVideoDecoderSMK); } filename = Common::String::format("%s.dxa", name); if (Common::File::exists(filename)) { #ifdef USE_ZLIB - DXADecoderWithSound *dxaDecoder = new DXADecoderWithSound(snd, bgSoundHandle); - return new MoviePlayer(vm, snd, system, bgSoundHandle, dxaDecoder, kVideoDecoderDXA); + Video::DXADecoder *dxaDecoder = new Video::DXADecoder(); + return new MoviePlayer(vm, system, dxaDecoder, kVideoDecoderDXA); #else GUI::MessageDialog dialog(_("DXA cutscenes found but ScummVM has been built without zlib support"), _("OK")); dialog.runModal(); diff --git a/engines/sword2/animation.h b/engines/sword2/animation.h index 3d5c42b7f7..b2a243b2ca 100644 --- a/engines/sword2/animation.h +++ b/engines/sword2/animation.h @@ -25,12 +25,16 @@ #ifndef SWORD2_ANIMATION_H #define SWORD2_ANIMATION_H -#include "video/dxa_decoder.h" -#include "video/video_decoder.h" -#include "audio/mixer.h" - #include "sword2/screen.h" +namespace Graphics { +struct Surface; +} + +namespace Video { +class VideoDecoder; +} + namespace Sword2 { enum DecoderType { @@ -55,20 +59,9 @@ struct MovieText { } }; -class DXADecoderWithSound : public Video::DXADecoder { -public: - DXADecoderWithSound(Audio::Mixer *mixer, Audio::SoundHandle *bgSoundHandle); - ~DXADecoderWithSound() {} - - uint32 getTime() const; -private: - Audio::Mixer *_mixer; - Audio::SoundHandle *_bgSoundHandle; -}; - class MoviePlayer { public: - MoviePlayer(Sword2Engine *vm, Audio::Mixer *snd, OSystem *system, Audio::SoundHandle *bgSoundHandle, Video::VideoDecoder *decoder, DecoderType decoderType); + MoviePlayer(Sword2Engine *vm, OSystem *system, Video::VideoDecoder *decoder, DecoderType decoderType); virtual ~MoviePlayer(); bool load(const char *name); @@ -76,7 +69,6 @@ public: protected: Sword2Engine *_vm; - Audio::Mixer *_snd; OSystem *_system; MovieText *_movieTexts; uint32 _numMovieTexts; @@ -87,8 +79,6 @@ protected: DecoderType _decoderType; Video::VideoDecoder *_decoder; - Audio::SoundHandle *_bgSoundHandle; - Audio::AudioStream *_bgSoundStream; uint32 _leadOut; int _leadOutFrame; @@ -105,7 +95,7 @@ protected: uint32 getWhiteColor(); }; -MoviePlayer *makeMoviePlayer(const char *name, Sword2Engine *vm, Audio::Mixer *snd, OSystem *system, uint32 frameCount); +MoviePlayer *makeMoviePlayer(const char *name, Sword2Engine *vm, OSystem *system, uint32 frameCount); } // End of namespace Sword2 diff --git a/engines/sword2/function.cpp b/engines/sword2/function.cpp index 836b252d6c..07fcaa094b 100644 --- a/engines/sword2/function.cpp +++ b/engines/sword2/function.cpp @@ -2139,7 +2139,7 @@ int32 Logic::fnPlaySequence(int32 *params) { uint32 frameCount = Sword2Engine::isPsx() ? params[1] : 0; - _moviePlayer = makeMoviePlayer(filename, _vm, _vm->_mixer, _vm->_system, frameCount); + _moviePlayer = makeMoviePlayer(filename, _vm, _vm->_system, frameCount); if (_moviePlayer && _moviePlayer->load(filename)) { _moviePlayer->play(_sequenceTextList, _sequenceTextLines, _smackerLeadIn, _smackerLeadOut); diff --git a/engines/sword2/sprite.cpp b/engines/sword2/sprite.cpp index cb0923cc2f..91a5e2e86b 100644 --- a/engines/sword2/sprite.cpp +++ b/engines/sword2/sprite.cpp @@ -772,7 +772,7 @@ int32 Screen::drawSprite(SpriteInfo *s) { src = sprite + rs.top * srcPitch + rs.left; dst = _buffer + _screenWide * rd.top + rd.left; - if (s->type & RDSPR_BLEND) { + if (s->type & RDSPR_BLEND) { // The original code had two different blending cases. One for // s->blend & 0x01 and one for s->blend & 0x02. However, the // only values that actually appear in the cluster files are @@ -783,7 +783,7 @@ int32 Screen::drawSprite(SpriteInfo *s) { // The only correct way to simulate this would be using 16-bit mode. // As this is not yet available for this engine, fake transparency is used // as placeholder. - if (!(_renderCaps & RDBLTFX_SPRITEBLEND) || Sword2Engine::isPsx()) { + if (!(_renderCaps & RDBLTFX_SPRITEBLEND) || Sword2Engine::isPsx()) { for (i = 0; i < rs.height(); i++) { for (j = 0; j < rs.width(); j++) { if (src[j] && ((i & 1) == (j & 1))) diff --git a/engines/sword2/sword2.cpp b/engines/sword2/sword2.cpp index 458a2d33ed..dfa6a23320 100644 --- a/engines/sword2/sword2.cpp +++ b/engines/sword2/sword2.cpp @@ -144,22 +144,52 @@ GameDescriptor Sword2MetaEngine::findGame(const char *gameid) const { return GameDescriptor(g->gameid, g->description); } -GameList Sword2MetaEngine::detectGames(const Common::FSList &fslist) const { +bool isFullGame(const Common::FSList &fslist) { + Common::FSList::const_iterator file; + + // We distinguish between the two versions by the presense of paris.clu + for (file = fslist.begin(); file != fslist.end(); ++file) { + if (!file->isDirectory()) { + if (file->getName().equalsIgnoreCase("paris.clu")) + return true; + } + } + + return false; +} + +GameList detectGamesImpl(const Common::FSList &fslist, bool recursion = false) { GameList detectedGames; const Sword2::GameSettings *g; Common::FSList::const_iterator file; - - // TODO: It would be nice if we had code here which distinguishes - // between the 'sword2' and 'sword2demo' targets. The current code - // can't do that since they use the same detectname. + bool isFullVersion = isFullGame(fslist); for (g = Sword2::sword2_settings; g->gameid; ++g) { // Iterate over all files in the given directory for (file = fslist.begin(); file != fslist.end(); ++file) { if (!file->isDirectory()) { - const char *fileName = file->getName().c_str(); + // The required game data files can be located in the game directory, or in + // a subdirectory called "clusters". In the latter case, we don't want to + // detect the game in that subdirectory, as this will detect the game twice + // when mass add is searching inside a directory. In this case, the first + // result (the game directory) will be correct, but the second result (the + // clusters subdirectory) will be wrong, as the optional speech, music and + // video data files will be ignored. Note that this fix will skip the game + // data files if the user has placed them inside a "clusters" subdirectory, + // or if he/she points ScummVM directly to the "clusters" directory of the + // game CD. Fixes bug #3049336. + Common::String directory = file->getParent().getName(); + directory.toLowercase(); + if (directory.hasPrefix("clusters") && directory.size() <= 9 && !recursion) + continue; + + if (file->getName().equalsIgnoreCase(g->detectname)) { + // Make sure that the sword2 demo is not mixed up with the + // full version, since they use the same filename for detection + if ((g->features == Sword2::GF_DEMO && isFullVersion) || + (g->features == 0 && !isFullVersion)) + continue; - if (0 == scumm_stricmp(g->detectname, fileName)) { // Match found, add to list of candidates, then abort inner loop. detectedGames.push_back(GameDescriptor(g->gameid, g->description, Common::UNK_LANG, Common::kPlatformUnknown, GUIO2(GUIO_NOMIDI, GUIO_NOASPECT))); break; @@ -174,12 +204,10 @@ GameList Sword2MetaEngine::detectGames(const Common::FSList &fslist) const { // present e.g. if the user copied the data straight from CD. for (file = fslist.begin(); file != fslist.end(); ++file) { if (file->isDirectory()) { - const char *fileName = file->getName().c_str(); - - if (0 == scumm_stricmp("clusters", fileName)) { + if (file->getName().equalsIgnoreCase("clusters")) { Common::FSList recList; if (file->getChildren(recList, Common::FSNode::kListAll)) { - GameList recGames(detectGames(recList)); + GameList recGames(detectGamesImpl(recList, true)); if (!recGames.empty()) { detectedGames.push_back(recGames); break; @@ -194,6 +222,10 @@ GameList Sword2MetaEngine::detectGames(const Common::FSList &fslist) const { return detectedGames; } +GameList Sword2MetaEngine::detectGames(const Common::FSList &fslist) const { + return detectGamesImpl(fslist); +} + SaveStateList Sword2MetaEngine::listSaves(const char *target) const { Common::SaveFileManager *saveFileMan = g_system->getSavefileManager(); Common::StringArray filenames; diff --git a/engines/sword25/fmv/movieplayer.cpp b/engines/sword25/fmv/movieplayer.cpp index 9ee13b4b6d..a95532ec65 100644 --- a/engines/sword25/fmv/movieplayer.cpp +++ b/engines/sword25/fmv/movieplayer.cpp @@ -61,6 +61,7 @@ bool MoviePlayer::loadMovie(const Common::String &filename, uint z) { // Get the file and load it into the decoder Common::SeekableReadStream *in = Kernel::getInstance()->getPackage()->getStream(filename); _decoder.loadStream(in); + _decoder.start(); GraphicEngine *pGfx = Kernel::getInstance()->getGfx(); diff --git a/engines/sword25/fmv/movieplayer.h b/engines/sword25/fmv/movieplayer.h index 1d256e56ba..2f5614b505 100644 --- a/engines/sword25/fmv/movieplayer.h +++ b/engines/sword25/fmv/movieplayer.h @@ -39,7 +39,7 @@ #include "sword25/gfx/bitmap.h" #ifdef USE_THEORADEC -#include "sword25/fmv/theora_decoder.h" +#include "video/theora_decoder.h" #endif #define THEORA_INDIRECT_RENDERING @@ -141,7 +141,7 @@ private: #ifdef USE_THEORADEC - TheoraDecoder _decoder; + Video::TheoraDecoder _decoder; Graphics::Surface *_backSurface; int _outX, _outY; diff --git a/engines/sword25/fmv/theora_decoder.cpp b/engines/sword25/fmv/theora_decoder.cpp deleted file mode 100644 index d38f5a26cf..0000000000 --- a/engines/sword25/fmv/theora_decoder.cpp +++ /dev/null @@ -1,565 +0,0 @@ -/* 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. - * - */ - -/* - * Source is based on the player example from libvorbis package, - * available at: http://svn.xiph.org/trunk/theora/examples/player_example.c - * - * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. - * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS - * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE - * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. - * - * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 - * by the Xiph.Org Foundation and contributors http://www.xiph.org/ - * - */ - -#include "sword25/fmv/theora_decoder.h" - -#ifdef USE_THEORADEC -#include "common/system.h" -#include "common/textconsole.h" -#include "common/util.h" -#include "graphics/yuv_to_rgb.h" -#include "audio/decoders/raw.h" -#include "sword25/kernel/common.h" - -namespace Sword25 { - -#define AUDIOFD_FRAGSIZE 10240 - -static double rint(double v) { - return floor(v + 0.5); -} - -TheoraDecoder::TheoraDecoder(Audio::Mixer::SoundType soundType) { - _fileStream = 0; - - _theoraPacket = 0; - _vorbisPacket = 0; - _theoraDecode = 0; - _theoraSetup = 0; - _nextFrameStartTime = 0.0; - - _soundType = soundType; - _audStream = 0; - _audHandle = new Audio::SoundHandle(); - - ogg_sync_init(&_oggSync); - - _curFrame = -1; - _audiobuf = (ogg_int16_t *)malloc(AUDIOFD_FRAGSIZE * sizeof(ogg_int16_t)); - - reset(); -} - -TheoraDecoder::~TheoraDecoder() { - close(); - delete _fileStream; - delete _audHandle; - free(_audiobuf); -} - -void TheoraDecoder::queuePage(ogg_page *page) { - if (_theoraPacket) - ogg_stream_pagein(&_theoraOut, page); - - if (_vorbisPacket) - ogg_stream_pagein(&_vorbisOut, page); -} - -int TheoraDecoder::bufferData() { - char *buffer = ogg_sync_buffer(&_oggSync, 4096); - int bytes = _fileStream->read(buffer, 4096); - - ogg_sync_wrote(&_oggSync, bytes); - - return bytes; -} - -bool TheoraDecoder::loadStream(Common::SeekableReadStream *stream) { - close(); - - _endOfAudio = false; - _endOfVideo = false; - _fileStream = stream; - - // start up Ogg stream synchronization layer - ogg_sync_init(&_oggSync); - - // init supporting Vorbis structures needed in header parsing - vorbis_info_init(&_vorbisInfo); - vorbis_comment_init(&_vorbisComment); - - // init supporting Theora structures needed in header parsing - th_comment_init(&_theoraComment); - th_info_init(&_theoraInfo); - - // Ogg file open; parse the headers - // Only interested in Vorbis/Theora streams - bool foundHeader = false; - while (!foundHeader) { - int ret = bufferData(); - - if (ret == 0) - break; - - while (ogg_sync_pageout(&_oggSync, &_oggPage) > 0) { - ogg_stream_state test; - - // is this a mandated initial header? If not, stop parsing - if (!ogg_page_bos(&_oggPage)) { - // don't leak the page; get it into the appropriate stream - queuePage(&_oggPage); - foundHeader = true; - break; - } - - ogg_stream_init(&test, ogg_page_serialno(&_oggPage)); - ogg_stream_pagein(&test, &_oggPage); - ogg_stream_packetout(&test, &_oggPacket); - - // identify the codec: try theora - if (!_theoraPacket && th_decode_headerin(&_theoraInfo, &_theoraComment, &_theoraSetup, &_oggPacket) >= 0) { - // it is theora - memcpy(&_theoraOut, &test, sizeof(test)); - _theoraPacket = 1; - } else if (!_vorbisPacket && vorbis_synthesis_headerin(&_vorbisInfo, &_vorbisComment, &_oggPacket) >= 0) { - // it is vorbis - memcpy(&_vorbisOut, &test, sizeof(test)); - _vorbisPacket = 1; - } else { - // whatever it is, we don't care about it - ogg_stream_clear(&test); - } - } - // fall through to non-bos page parsing - } - - // we're expecting more header packets. - while ((_theoraPacket && _theoraPacket < 3) || (_vorbisPacket && _vorbisPacket < 3)) { - int ret; - - // look for further theora headers - while (_theoraPacket && (_theoraPacket < 3) && (ret = ogg_stream_packetout(&_theoraOut, &_oggPacket))) { - if (ret < 0) - error("Error parsing Theora stream headers; corrupt stream?"); - - if (!th_decode_headerin(&_theoraInfo, &_theoraComment, &_theoraSetup, &_oggPacket)) - error("Error parsing Theora stream headers; corrupt stream?"); - - _theoraPacket++; - } - - // look for more vorbis header packets - while (_vorbisPacket && (_vorbisPacket < 3) && (ret = ogg_stream_packetout(&_vorbisOut, &_oggPacket))) { - if (ret < 0) - error("Error parsing Vorbis stream headers; corrupt stream?"); - - if (vorbis_synthesis_headerin(&_vorbisInfo, &_vorbisComment, &_oggPacket)) - error("Error parsing Vorbis stream headers; corrupt stream?"); - - _vorbisPacket++; - - if (_vorbisPacket == 3) - break; - } - - // The header pages/packets will arrive before anything else we - // care about, or the stream is not obeying spec - - if (ogg_sync_pageout(&_oggSync, &_oggPage) > 0) { - queuePage(&_oggPage); // demux into the appropriate stream - } else { - ret = bufferData(); // someone needs more data - - if (ret == 0) - error("End of file while searching for codec headers."); - } - } - - // and now we have it all. initialize decoders - if (_theoraPacket) { - _theoraDecode = th_decode_alloc(&_theoraInfo, _theoraSetup); - debugN(1, "Ogg logical stream %lx is Theora %dx%d %.02f fps", - _theoraOut.serialno, _theoraInfo.pic_width, _theoraInfo.pic_height, - (double)_theoraInfo.fps_numerator / _theoraInfo.fps_denominator); - - switch (_theoraInfo.pixel_fmt) { - case TH_PF_420: - debug(1, " 4:2:0 video"); - break; - case TH_PF_422: - debug(1, " 4:2:2 video"); - break; - case TH_PF_444: - debug(1, " 4:4:4 video"); - break; - case TH_PF_RSVD: - default: - debug(1, " video\n (UNKNOWN Chroma sampling!)"); - break; - } - - if (_theoraInfo.pic_width != _theoraInfo.frame_width || _theoraInfo.pic_height != _theoraInfo.frame_height) - debug(1, " Frame content is %dx%d with offset (%d,%d).", - _theoraInfo.frame_width, _theoraInfo.frame_height, _theoraInfo.pic_x, _theoraInfo.pic_y); - - switch (_theoraInfo.colorspace){ - case TH_CS_UNSPECIFIED: - /* nothing to report */ - break; - case TH_CS_ITU_REC_470M: - debug(1, " encoder specified ITU Rec 470M (NTSC) color."); - break; - case TH_CS_ITU_REC_470BG: - debug(1, " encoder specified ITU Rec 470BG (PAL) color."); - break; - default: - debug(1, "warning: encoder specified unknown colorspace (%d).", _theoraInfo.colorspace); - break; - } - - debug(1, "Encoded by %s", _theoraComment.vendor); - if (_theoraComment.comments) { - debug(1, "theora comment header:"); - for (int i = 0; i < _theoraComment.comments; i++) { - if (_theoraComment.user_comments[i]) { - int len = _theoraComment.comment_lengths[i]; - char *value = (char *)malloc(len + 1); - if (value) { - memcpy(value, _theoraComment.user_comments[i], len); - value[len] = '\0'; - debug(1, "\t%s", value); - free(value); - } - } - } - } - - th_decode_ctl(_theoraDecode, TH_DECCTL_GET_PPLEVEL_MAX, &_ppLevelMax, sizeof(_ppLevelMax)); - _ppLevel = _ppLevelMax; - th_decode_ctl(_theoraDecode, TH_DECCTL_SET_PPLEVEL, &_ppLevel, sizeof(_ppLevel)); - _ppInc = 0; - } else { - // tear down the partial theora setup - th_info_clear(&_theoraInfo); - th_comment_clear(&_theoraComment); - } - - th_setup_free(_theoraSetup); - _theoraSetup = 0; - - if (_vorbisPacket) { - vorbis_synthesis_init(&_vorbisDSP, &_vorbisInfo); - vorbis_block_init(&_vorbisDSP, &_vorbisBlock); - debug(3, "Ogg logical stream %lx is Vorbis %d channel %ld Hz audio.", - _vorbisOut.serialno, _vorbisInfo.channels, _vorbisInfo.rate); - - _audStream = Audio::makeQueuingAudioStream(_vorbisInfo.rate, _vorbisInfo.channels); - - // Get enough audio data to start us off - while (_audStream->numQueuedStreams() == 0) { - // Queue more data - bufferData(); - while (ogg_sync_pageout(&_oggSync, &_oggPage) > 0) - queuePage(&_oggPage); - - queueAudio(); - } - - if (_audStream) - g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, _audHandle, _audStream, -1, getVolume(), getBalance()); - } else { - // tear down the partial vorbis setup - vorbis_info_clear(&_vorbisInfo); - vorbis_comment_clear(&_vorbisComment); - _endOfAudio = true; - } - - _surface.create(_theoraInfo.frame_width, _theoraInfo.frame_height, g_system->getScreenFormat()); - - // Set up a display surface - _displaySurface.pixels = _surface.getBasePtr(_theoraInfo.pic_x, _theoraInfo.pic_y); - _displaySurface.w = _theoraInfo.pic_width; - _displaySurface.h = _theoraInfo.pic_height; - _displaySurface.format = _surface.format; - _displaySurface.pitch = _surface.pitch; - - // Set the frame rate - _frameRate = Common::Rational(_theoraInfo.fps_numerator, _theoraInfo.fps_denominator); - - return true; -} - -void TheoraDecoder::close() { - if (_vorbisPacket) { - ogg_stream_clear(&_vorbisOut); - vorbis_block_clear(&_vorbisBlock); - vorbis_dsp_clear(&_vorbisDSP); - vorbis_comment_clear(&_vorbisComment); - vorbis_info_clear(&_vorbisInfo); - - g_system->getMixer()->stopHandle(*_audHandle); - - _audStream = 0; - _vorbisPacket = false; - } - if (_theoraPacket) { - ogg_stream_clear(&_theoraOut); - th_decode_free(_theoraDecode); - th_comment_clear(&_theoraComment); - th_info_clear(&_theoraInfo); - _theoraDecode = 0; - _theoraPacket = false; - } - - if (!_fileStream) - return; - - ogg_sync_clear(&_oggSync); - - delete _fileStream; - _fileStream = 0; - - _surface.free(); - _displaySurface.pixels = 0; - _displaySurface.free(); - - reset(); -} - -const Graphics::Surface *TheoraDecoder::decodeNextFrame() { - // First, let's get our frame - while (_theoraPacket) { - // theora is one in, one out... - if (ogg_stream_packetout(&_theoraOut, &_oggPacket) > 0) { - - if (_ppInc) { - _ppLevel += _ppInc; - th_decode_ctl(_theoraDecode, TH_DECCTL_SET_PPLEVEL, &_ppLevel, sizeof(_ppLevel)); - _ppInc = 0; - } - - if (th_decode_packetin(_theoraDecode, &_oggPacket, NULL) == 0) { - _curFrame++; - - // Convert YUV data to RGB data - th_ycbcr_buffer yuv; - th_decode_ycbcr_out(_theoraDecode, yuv); - translateYUVtoRGBA(yuv); - - if (_curFrame == 0) - _startTime = g_system->getMillis(); - - double time = th_granule_time(_theoraDecode, _oggPacket.granulepos); - - // We need to calculate when the next frame should be shown - // This is all in floating point because that's what the Ogg code gives us - // Ogg is a lossy container format, so it doesn't always list the time to the - // next frame. In such cases, we need to calculate it ourselves. - if (time == -1.0) - _nextFrameStartTime += _frameRate.getInverse().toDouble(); - else - _nextFrameStartTime = time; - - // break out - break; - } - } else { - // If we can't get any more frames, we're done. - if (_theoraOut.e_o_s || _fileStream->eos()) { - _endOfVideo = true; - break; - } - - // Queue more data - bufferData(); - while (ogg_sync_pageout(&_oggSync, &_oggPage) > 0) - queuePage(&_oggPage); - } - - // Update audio if we can - queueAudio(); - } - - // Force at least some audio to be buffered - // TODO: 5 is very arbitrary. We probably should do something like QuickTime does. - while (!_endOfAudio && _audStream->numQueuedStreams() < 5) { - bufferData(); - while (ogg_sync_pageout(&_oggSync, &_oggPage) > 0) - queuePage(&_oggPage); - - bool queuedAudio = queueAudio(); - if ((_vorbisOut.e_o_s || _fileStream->eos()) && !queuedAudio) { - _endOfAudio = true; - break; - } - } - - return &_displaySurface; -} - -bool TheoraDecoder::queueAudio() { - if (!_audStream) - return false; - - // An audio buffer should have been allocated (either in the constructor or after queuing the current buffer) - if (!_audiobuf) { - warning("[TheoraDecoder::queueAudio] Invalid audio buffer"); - return false; - } - - bool queuedAudio = false; - - for (;;) { - float **pcm; - - // if there's pending, decoded audio, grab it - int ret = vorbis_synthesis_pcmout(&_vorbisDSP, &pcm); - if (ret > 0) { - int count = _audiobufFill / 2; - int maxsamples = ((AUDIOFD_FRAGSIZE - _audiobufFill) / _vorbisInfo.channels) >> 1; - int i; - for (i = 0; i < ret && i < maxsamples; i++) - for (int j = 0; j < _vorbisInfo.channels; j++) { - int val = CLIP((int)rint(pcm[j][i] * 32767.f), -32768, 32767); - _audiobuf[count++] = val; - } - - vorbis_synthesis_read(&_vorbisDSP, i); - _audiobufFill += (i * _vorbisInfo.channels) << 1; - - if (_audiobufFill == AUDIOFD_FRAGSIZE) { - byte flags = Audio::FLAG_16BITS | Audio::FLAG_STEREO; -#ifdef SCUMM_LITTLE_ENDIAN - flags |= Audio::FLAG_LITTLE_ENDIAN; -#endif - _audStream->queueBuffer((byte *)_audiobuf, AUDIOFD_FRAGSIZE, DisposeAfterUse::NO, flags); - - // The audio mixer is now responsible for the old audio buffer. - // We need to create a new one. - _audiobuf = (ogg_int16_t *)malloc(AUDIOFD_FRAGSIZE * sizeof(ogg_int16_t)); - if (!_audiobuf) { - warning("[TheoraDecoder::queueAudio] Cannot allocate memory for audio buffer"); - return false; - } - - _audiobufFill = 0; - queuedAudio = true; - } - } else { - // no pending audio; is there a pending packet to decode? - if (ogg_stream_packetout(&_vorbisOut, &_oggPacket) > 0) { - if (vorbis_synthesis(&_vorbisBlock, &_oggPacket) == 0) // test for success! - vorbis_synthesis_blockin(&_vorbisDSP, &_vorbisBlock); - } else // we've buffered all we have, break out for now - return queuedAudio; - } - } - - // Unreachable - return false; -} - -void TheoraDecoder::reset() { - VideoDecoder::reset(); - - // FIXME: This does a rewind() instead of a reset()! - - if (_fileStream) - _fileStream->seek(0); - - _audiobufFill = 0; - _audiobufReady = false; - - _curFrame = -1; - - _theoraPacket = 0; - _vorbisPacket = 0; -} - -bool TheoraDecoder::endOfVideo() const { - return !isVideoLoaded() || (_endOfVideo && (!_audStream || (_audStream->endOfData() && _endOfAudio))); -} - -uint32 TheoraDecoder::getTimeToNextFrame() const { - if (endOfVideo() || _curFrame < 0) - return 0; - - uint32 elapsedTime = getTime(); - uint32 nextFrameStartTime = (uint32)(_nextFrameStartTime * 1000); - - if (nextFrameStartTime <= elapsedTime) - return 0; - - return nextFrameStartTime - elapsedTime; -} - -uint32 TheoraDecoder::getTime() const { - if (_audStream) - return g_system->getMixer()->getSoundElapsedTime(*_audHandle); - - return VideoDecoder::getTime(); -} - -void TheoraDecoder::pauseVideoIntern(bool pause) { - if (_audStream) - g_system->getMixer()->pauseHandle(*_audHandle, pause); -} - -enum TheoraYUVBuffers { - kBufferY = 0, - kBufferU = 1, - kBufferV = 2 -}; - -void TheoraDecoder::translateYUVtoRGBA(th_ycbcr_buffer &YUVBuffer) { - // Width and height of all buffers have to be divisible by 2. - assert((YUVBuffer[kBufferY].width & 1) == 0); - assert((YUVBuffer[kBufferY].height & 1) == 0); - assert((YUVBuffer[kBufferU].width & 1) == 0); - assert((YUVBuffer[kBufferV].width & 1) == 0); - - // UV images have to have a quarter of the Y image resolution - assert(YUVBuffer[kBufferU].width == YUVBuffer[kBufferY].width >> 1); - assert(YUVBuffer[kBufferV].width == YUVBuffer[kBufferY].width >> 1); - assert(YUVBuffer[kBufferU].height == YUVBuffer[kBufferY].height >> 1); - assert(YUVBuffer[kBufferV].height == YUVBuffer[kBufferY].height >> 1); - - Graphics::convertYUV420ToRGB(&_surface, YUVBuffer[kBufferY].data, YUVBuffer[kBufferU].data, YUVBuffer[kBufferV].data, YUVBuffer[kBufferY].width, YUVBuffer[kBufferY].height, YUVBuffer[kBufferY].stride, YUVBuffer[kBufferU].stride); -} - -void TheoraDecoder::updateVolume() { - if (g_system->getMixer()->isSoundHandleActive(*_audHandle)) - g_system->getMixer()->setChannelVolume(*_audHandle, getVolume()); -} - -void TheoraDecoder::updateBalance() { - if (g_system->getMixer()->isSoundHandleActive(*_audHandle)) - g_system->getMixer()->setChannelBalance(*_audHandle, getBalance()); -} - -} // End of namespace Sword25 - -#endif diff --git a/engines/sword25/fmv/theora_decoder.h b/engines/sword25/fmv/theora_decoder.h deleted file mode 100644 index 739040024f..0000000000 --- a/engines/sword25/fmv/theora_decoder.h +++ /dev/null @@ -1,144 +0,0 @@ -/* 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 SWORD25_THEORADECODER_H -#define SWORD25_THEORADECODER_H - -#include "common/scummsys.h" // for USE_THEORADEC - -#ifdef USE_THEORADEC - -#include "common/rational.h" -#include "video/video_decoder.h" -#include "audio/audiostream.h" -#include "audio/mixer.h" -#include "graphics/pixelformat.h" -#include "graphics/surface.h" - -#include <theora/theoradec.h> -#include <vorbis/codec.h> - -namespace Common { -class SeekableReadStream; -} - -namespace Sword25 { - -/** - * - * Decoder for Theora videos. - * Video decoder used in engines: - * - sword25 - */ -class TheoraDecoder : public Video::VideoDecoder { -public: - TheoraDecoder(Audio::Mixer::SoundType soundType = Audio::Mixer::kMusicSoundType); - virtual ~TheoraDecoder(); - - /** - * Load a video file - * @param stream the stream to load - */ - bool loadStream(Common::SeekableReadStream *stream); - void close(); - void reset(); - - /** - * Decode the next frame and return the frame's surface - * @note the return surface should *not* be freed - * @note this may return 0, in which case the last frame should be kept on screen - */ - const Graphics::Surface *decodeNextFrame(); - - bool isVideoLoaded() const { return _fileStream != 0; } - uint16 getWidth() const { return _displaySurface.w; } - uint16 getHeight() const { return _displaySurface.h; } - - uint32 getFrameCount() const { - // It is not possible to get frame count easily - // I.e. seeking is required - assert(0); - return 0; - } - - Graphics::PixelFormat getPixelFormat() const { return _displaySurface.format; } - uint32 getTime() const; - uint32 getTimeToNextFrame() const; - - bool endOfVideo() const; - -protected: - // VideoDecoder API - void updateVolume(); - void updateBalance(); - void pauseVideoIntern(bool pause); - -private: - void queuePage(ogg_page *page); - bool queueAudio(); - int bufferData(); - void translateYUVtoRGBA(th_ycbcr_buffer &YUVBuffer); - - Common::SeekableReadStream *_fileStream; - Graphics::Surface _surface; - Graphics::Surface _displaySurface; - Common::Rational _frameRate; - double _nextFrameStartTime; - bool _endOfVideo; - bool _endOfAudio; - - Audio::Mixer::SoundType _soundType; - Audio::SoundHandle *_audHandle; - Audio::QueuingAudioStream *_audStream; - - ogg_sync_state _oggSync; - ogg_page _oggPage; - ogg_packet _oggPacket; - ogg_stream_state _vorbisOut; - ogg_stream_state _theoraOut; - th_info _theoraInfo; - th_comment _theoraComment; - th_dec_ctx *_theoraDecode; - th_setup_info *_theoraSetup; - vorbis_info _vorbisInfo; - vorbis_dsp_state _vorbisDSP; - vorbis_block _vorbisBlock; - vorbis_comment _vorbisComment; - - int _theoraPacket; - int _vorbisPacket; - - int _ppLevelMax; - int _ppLevel; - int _ppInc; - - // single audio fragment audio buffering - int _audiobufFill; - bool _audiobufReady; - ogg_int16_t *_audiobuf; -}; - -} // End of namespace Sword25 - -#endif - -#endif diff --git a/engines/sword25/gfx/graphicengine.cpp b/engines/sword25/gfx/graphicengine.cpp index 6f5da32bc4..216d801f3e 100644 --- a/engines/sword25/gfx/graphicengine.cpp +++ b/engines/sword25/gfx/graphicengine.cpp @@ -376,10 +376,10 @@ bool GraphicEngine::saveThumbnailScreenshot(const Common::String &filename) { void GraphicEngine::ARGBColorToLuaColor(lua_State *L, uint color) { lua_Number components[4] = { - (color >> 16) & 0xff, // Rot - (color >> 8) & 0xff, // Grün - color & 0xff, // Blau - color >> 24, // Alpha + (lua_Number)((color >> 16) & 0xff), // Rot + (lua_Number)((color >> 8) & 0xff), // Grün + (lua_Number)(color & 0xff), // Blau + (lua_Number)(color >> 24), // Alpha }; lua_newtable(L); diff --git a/engines/sword25/module.mk b/engines/sword25/module.mk index 302120c500..e24a221244 100644 --- a/engines/sword25/module.mk +++ b/engines/sword25/module.mk @@ -85,11 +85,6 @@ MODULE_OBJS := \ util/pluto/pluto.o \ util/pluto/plzio.o -ifdef USE_THEORADEC -MODULE_OBJS += \ - fmv/theora_decoder.o -endif - # This module can be built as a plugin ifeq ($(ENABLE_SWORD25), DYNAMIC_PLUGIN) PLUGIN := 1 diff --git a/engines/sword25/sfx/soundengine.cpp b/engines/sword25/sfx/soundengine.cpp index 78b2db19eb..61d53c89a7 100644 --- a/engines/sword25/sfx/soundengine.cpp +++ b/engines/sword25/sfx/soundengine.cpp @@ -239,16 +239,20 @@ void SoundEngine::setSoundVolume(uint handle, float volume) { debugC(1, kDebugSound, "SoundEngine::setSoundVolume(%d, %f)", handle, volume); SndHandle* sndHandle = findHandle(handle); - if (sndHandle != NULL) + if (sndHandle != NULL) { + sndHandle->volume = volume; _mixer->setChannelVolume(sndHandle->handle, (byte)(volume * 255)); + } } void SoundEngine::setSoundPanning(uint handle, float pan) { debugC(1, kDebugSound, "SoundEngine::setSoundPanning(%d, %f)", handle, pan); SndHandle* sndHandle = findHandle(handle); - if (sndHandle != NULL) + if (sndHandle != NULL) { + sndHandle->pan = pan; _mixer->setChannelBalance(sndHandle->handle, (int8)(pan * 127)); + } } void SoundEngine::pauseSound(uint handle) { @@ -324,13 +328,16 @@ bool SoundEngine::canLoadResource(const Common::String &fileName) { return fname.hasSuffix(".ogg"); } - - bool SoundEngine::persist(OutputPersistenceBlock &writer) { +bool SoundEngine::persist(OutputPersistenceBlock &writer) { writer.write(_maxHandleId); for (uint i = 0; i < SOUND_HANDLES; i++) { writer.write(_handles[i].id); + // Don't restart sounds which already finished playing. + if (_handles[i].type != kFreeHandle && !_mixer->isSoundHandleActive(_handles[i].handle)) + _handles[i].type = kFreeHandle; + writer.writeString(_handles[i].fileName); writer.write((int)_handles[i].sndType); writer.write(_handles[i].volume); @@ -363,7 +370,7 @@ bool SoundEngine::unpersist(InputPersistenceBlock &reader) { int loopStart; int loopEnd; uint layer; - + reader.readString(fileName); reader.read(sndType); reader.read(volume); @@ -374,7 +381,8 @@ bool SoundEngine::unpersist(InputPersistenceBlock &reader) { reader.read(layer); if (reader.isGood()) { - playSoundEx(fileName, (SOUND_TYPES)sndType, volume, pan, loop, loopStart, loopEnd, layer, i); + if (sndType != kFreeHandle) + playSoundEx(fileName, (SOUND_TYPES)sndType, volume, pan, loop, loopStart, loopEnd, layer, i); } else return false; } diff --git a/engines/sword25/util/lua/llex.cpp b/engines/sword25/util/lua/llex.cpp index f8433d3afa..423f0285ca 100644 --- a/engines/sword25/util/lua/llex.cpp +++ b/engines/sword25/util/lua/llex.cpp @@ -4,6 +4,8 @@ ** See Copyright Notice in lua.h */ +// FIXME: Do not directly use iscntrl from ctype.h. +#define FORBIDDEN_SYMBOL_EXCEPTION_iscntrl #include "common/util.h" diff --git a/engines/sword25/util/pluto/pluto.cpp b/engines/sword25/util/pluto/pluto.cpp index d645e5ed2a..b7f5e30340 100644 --- a/engines/sword25/util/pluto/pluto.cpp +++ b/engines/sword25/util/pluto/pluto.cpp @@ -895,10 +895,10 @@ static void unpersistnumber(UnpersistInfo *upi) static void unpersiststring(UnpersistInfo *upi) { /* perms reftbl sptbl ref */ - int length; + size_t length; char* string; lua_checkstack(upi->L, 1); - verify(LIF(Z,read)(&upi->zio, &length, sizeof(int)) == 0); + verify(LIF(Z,read)(&upi->zio, &length, sizeof(size_t)) == 0); string = pdep_newvector(upi->L, length, char); verify(LIF(Z,read)(&upi->zio, string, length) == 0); lua_pushlstring(upi->L, string, length); diff --git a/engines/teenagent/actor.cpp b/engines/teenagent/actor.cpp index cb8c798fb6..d65a367309 100644 --- a/engines/teenagent/actor.cpp +++ b/engines/teenagent/actor.cpp @@ -22,47 +22,46 @@ #include "teenagent/actor.h" #include "teenagent/objects.h" #include "teenagent/resources.h" +#include "teenagent/teenagent.h" #include "common/random.h" #include "common/textconsole.h" namespace TeenAgent { -Actor::Actor() : head_index(0), idle_type(0) {} +Actor::Actor(TeenAgentEngine *vm) : _vm(vm), headIndex(0), idleType(0) {} -//idle animation lists at dseg: 0x6540 -Common::Rect Actor::renderIdle(Graphics::Surface *surface, const Common::Point &position, uint8 orientation, int delta_frame, uint zoom, Common::RandomSource &rnd) { +Common::Rect Actor::renderIdle(Graphics::Surface *surface, const Common::Point &position, uint8 orientation, int deltaFrame, uint zoom, Common::RandomSource &rnd) { if (index == 0) { - idle_type = rnd.getRandomNumber(2); - debug(0, "switched to idle animation %u", idle_type); + idleType = rnd.getRandomNumber(2); + debugC(kDebugActor, "switched to idle animation %u", idleType); } - Resources *res = Resources::instance(); - byte *frames_idle; + byte *framesIdle; do { - frames_idle = res->dseg.ptr(res->dseg.get_word(0x6540 + idle_type * 2)) + index; - index += delta_frame; - if (*frames_idle == 0) { - idle_type = rnd.getRandomNumber(2); - debug(0, "switched to idle animation %u[loop]", idle_type); + framesIdle = _vm->res->dseg.ptr(_vm->res->dseg.get_word(dsAddr_idleAnimationListPtr + idleType * 2)) + index; + index += deltaFrame; + if (*framesIdle == 0) { + idleType = rnd.getRandomNumber(2); + debugC(kDebugActor, "switched to idle animation %u[loop]", idleType); index = 3; //put 4th frame (base 1) if idle animation loops } - } while (*frames_idle == 0); + } while (*framesIdle == 0); bool mirror = orientation == kActorLeft; - Surface *s = frames + *frames_idle - 1; + Surface *s = frames + *framesIdle - 1; - ///\todo remove copy-paste here and below + //TODO: remove copy-paste here and below int xp = position.x - s->w * zoom / 512 - s->x, yp = position.y - 62 * zoom / 256 - s->y; //hardcoded in original game return s->render(surface, xp, yp, mirror, Common::Rect(), zoom); } -Common::Rect Actor::render(Graphics::Surface *surface, const Common::Point &position, uint8 orientation, int delta_frame, bool render_head, uint zoom) { - const uint8 frames_left_right[] = {0, 1, 2, 3, 4, 5, /* step */ 6, 7, 8, 9}; - const uint8 frames_up[] = {18, 19, 20, 21, 22, 23, 24, 25, }; - const uint8 frames_down[] = {10, 11, 12, 13, 14, 15, 16, 17, }; +Common::Rect Actor::render(Graphics::Surface *surface, const Common::Point &position, uint8 orientation, int deltaFrame, bool renderHead, uint zoom) { + const uint8 framesLeftRight[] = {0, 1, 2, 3, 4, 5, /* step */ 6, 7, 8, 9}; + const uint8 framesUp[] = {18, 19, 20, 21, 22, 23, 24, 25, }; + const uint8 framesDown[] = {10, 11, 12, 13, 14, 15, 16, 17, }; - const uint8 frames_head_left_right[] = { + const uint8 framesHeadLeftRight[] = { 0x27, 0x1a, 0x1b, 0x27, 0x1c, 0x1d, 0x27, 0x1a, @@ -73,14 +72,14 @@ Common::Rect Actor::render(Graphics::Surface *surface, const Common::Point &posi 0x27, 0x1a, }; - const uint8 frames_head_up[] = { + const uint8 framesHeadUp[] = { 0x29, 0x25, 0x29, 0x29, 0x26, 0x29, 0x26, 0x29, 0x29, 0x25, 0x29, 0x25, 0x29, 0x29, 0x29, 0x25, 0x25, 0x29, 0x29, 0x26 }; - const uint8 frames_head_down[] = { + const uint8 framesHeadDown[] = { 0x20, 0x21, 0x22, 0x23, 0x28, 0x24, 0x28, 0x28, 0x24, 0x28, 0x20, 0x21, @@ -91,45 +90,45 @@ Common::Rect Actor::render(Graphics::Surface *surface, const Common::Point &posi Surface *s = NULL, *head = NULL; bool mirror = orientation == kActorLeft; - index += delta_frame; + index += deltaFrame; switch (orientation) { case kActorLeft: case kActorRight: - if (render_head) { - if (head_index >= ARRAYSIZE(frames_head_left_right)) - head_index = 0; - head = frames + frames_head_left_right[head_index]; - ++head_index; + if (renderHead) { + if (headIndex >= ARRAYSIZE(framesHeadLeftRight)) + headIndex = 0; + head = frames + framesHeadLeftRight[headIndex]; + ++headIndex; } - if (index >= ARRAYSIZE(frames_left_right)) + if (index >= ARRAYSIZE(framesLeftRight)) index = 1; - s = frames + frames_left_right[index]; + s = frames + framesLeftRight[index]; break; case kActorUp: - if (render_head) { - if (head_index >= ARRAYSIZE(frames_head_up)) - head_index = 0; - head = frames + frames_head_up[head_index]; - ++head_index; + if (renderHead) { + if (headIndex >= ARRAYSIZE(framesHeadUp)) + headIndex = 0; + head = frames + framesHeadUp[headIndex]; + ++headIndex; } - if (index >= ARRAYSIZE(frames_up)) + if (index >= ARRAYSIZE(framesUp)) index = 1; - s = frames + frames_up[index]; + s = frames + framesUp[index]; break; case kActorDown: - if (render_head) { - if (head_index >= ARRAYSIZE(frames_head_down)) - head_index = 0; - head = frames + frames_head_down[head_index]; - ++head_index; + if (renderHead) { + if (headIndex >= ARRAYSIZE(framesHeadDown)) + headIndex = 0; + head = frames + framesHeadDown[headIndex]; + ++headIndex; } - if (index >= ARRAYSIZE(frames_down)) + if (index >= ARRAYSIZE(framesDown)) index = 1; - s = frames + frames_down[index]; + s = frames + framesDown[index]; break; default: return Common::Rect(); diff --git a/engines/teenagent/actor.h b/engines/teenagent/actor.h index 9a7d395547..942397c636 100644 --- a/engines/teenagent/actor.h +++ b/engines/teenagent/actor.h @@ -28,13 +28,20 @@ class RandomSource; namespace TeenAgent { +class TeenAgentEngine; + class Actor : public Animation { - uint head_index; - uint idle_type; +private: + TeenAgentEngine *_vm; + + uint headIndex; + uint idleType; + public: - Actor(); - Common::Rect render(Graphics::Surface *surface, const Common::Point &position, uint8 orientation, int delta_frame, bool head, uint zoom); - Common::Rect renderIdle(Graphics::Surface *surface, const Common::Point &position, uint8 orientation, int delta_frame, uint zoom, Common::RandomSource &rnd); + Actor(TeenAgentEngine *vm); + + Common::Rect render(Graphics::Surface *surface, const Common::Point &position, uint8 orientation, int deltaFrame, bool renderHead, uint zoom); + Common::Rect renderIdle(Graphics::Surface *surface, const Common::Point &position, uint8 orientation, int deltaFrame, uint zoom, Common::RandomSource &rnd); }; } // End of namespace TeenAgent diff --git a/engines/teenagent/animation.cpp b/engines/teenagent/animation.cpp index 56107b67ca..effafcaac6 100644 --- a/engines/teenagent/animation.cpp +++ b/engines/teenagent/animation.cpp @@ -19,24 +19,30 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "teenagent/teenagent.h" #include "teenagent/animation.h" + #include "common/endian.h" #include "common/textconsole.h" namespace TeenAgent { -Animation::Animation() : id(0), x(0), y(0), loop(true), paused(false), ignore(false), data(0), data_size(0), frames_count(0), frames(0), index(0) { +Animation::Animation() : id(0), x(0), y(0), loop(true), paused(false), ignore(false), data(0), dataSize(0), framesCount(0), frames(0), index(0) { +} + +Animation::~Animation() { + free(); } Surface *Animation::firstFrame() { - if (frames == NULL || frames_count == 0) + if (frames == NULL || framesCount == 0) return NULL; Surface *r = frames; uint16 pos = READ_LE_UINT16(data + 1); if (pos != 0) { - r->x = pos % 320; - r->y = pos / 320; + r->x = pos % kScreenWidth; + r->y = pos / kScreenWidth; } return r; } @@ -45,38 +51,38 @@ Surface *Animation::currentFrame(int dt) { if (paused) return firstFrame(); - if (frames == NULL || frames_count == 0) + if (frames == NULL || framesCount == 0) return NULL; Surface *r; if (data != NULL) { uint32 frame = 3 * index; - //debug(0, "%u/%u", index, data_size / 3); + debugC(2, kDebugAnimation, "%u/%u", index, dataSize / 3); index += dt; - if (!loop && index >= data_size / 3) { + if (!loop && index >= dataSize / 3) { return NULL; } - if (data[frame] - 1 >= frames_count) { - warning("invalid frame %u(0x%x) (max %u) index %u, mod %u", frame, frame, frames_count, index - 1, data_size / 3); + if (data[frame] - 1 >= framesCount) { + warning("invalid frame %u(0x%x) (max %u) index %u, mod %u", frame, frame, framesCount, index - 1, dataSize / 3); return NULL; } r = frames + data[frame] - 1; uint16 pos = READ_LE_UINT16(data + frame + 1); - index %= (data_size / 3); + index %= (dataSize / 3); if (pos != 0) { - x = r->x = pos % 320; - y = r->y = pos / 320; + x = r->x = pos % kScreenWidth; + y = r->y = pos / kScreenWidth; } } else { - //debug(0, "index %u", index); + debugC(2, kDebugAnimation, "index %u", index); r = frames + index; index += dt; - index %= frames_count; + index %= framesCount; } return r; @@ -97,9 +103,9 @@ void Animation::free() { delete[] data; data = NULL; - data_size = 0; + dataSize = 0; - frames_count = 0; + framesCount = 0; delete[] frames; frames = NULL; @@ -107,44 +113,42 @@ void Animation::free() { } void Animation::load(Common::SeekableReadStream &s, Type type) { - //fixme: do not reload the same animation each time + //FIXME: do not reload the same animation each time free(); if (s.size() <= 1) { - debug(1, "empty animation"); + debugC(1, kDebugAnimation, "empty animation"); return; } - //uint16 pos = 0; + uint16 pos = 0; int off = 0; switch (type) { case kTypeLan: - data_size = s.readUint16LE(); + dataSize = s.readUint16LE(); if (s.eos()) { - debug(1, "empty animation"); + debugC(1, kDebugAnimation, "empty animation"); return; } - data_size -= 2; - data = new byte[data_size]; - data_size = s.read(data, data_size); - /* for (int i = 0; i < data_size; ++i) { - debug(0, "%02x ", data[i]); - } - debug(0, ", %u frames", data_size / 3); - */ - frames_count = s.readByte(); - debug(1, "%u physical frames", frames_count); - if (frames_count == 0) + dataSize -= 2; + data = new byte[dataSize]; + dataSize = s.read(data, dataSize); + for (int i = 0; i < dataSize; ++i) + debugC(2, kDebugAnimation, "%02x ", data[i]); + debugC(2, kDebugAnimation, ", %u frames", dataSize / 3); + framesCount = s.readByte(); + debugC(1, kDebugAnimation, "%u physical frames", framesCount); + if (framesCount == 0) return; - frames = new Surface[frames_count]; + frames = new Surface[framesCount]; - s.skip(frames_count * 2 - 2); //sizes - /*pos = */s.readUint16LE(); - //debug(0, "pos?: %04x", pos); + s.skip(framesCount * 2 - 2); //sizes + pos = s.readUint16LE(); + debugC(3, kDebugAnimation, "pos?: 0x%04x", pos); - for (uint16 i = 0; i < frames_count; ++i) { + for (uint16 i = 0; i < framesCount; ++i) { frames[i].load(s, Surface::kTypeLan); frames[i].x = 0; frames[i].y = 0; @@ -152,43 +156,43 @@ void Animation::load(Common::SeekableReadStream &s, Type type) { break; case kTypeInventory: { - data_size = 3 * s.readByte(); - data = new byte[data_size]; + dataSize = 3 * s.readByte(); + data = new byte[dataSize]; - frames_count = 0; - for (byte i = 0; i < data_size / 3; ++i) { + framesCount = 0; + for (byte i = 0; i < dataSize / 3; ++i) { int idx = i * 3; - /* byte unk = */ - s.readByte(); + byte unk = s.readByte(); + debugC(3, kDebugAnimation, "unk?: 0x%02x", unk); data[idx] = s.readByte(); if (data[idx] == 0) data[idx] = 1; //fixme: investigate - if (data[idx] > frames_count) - frames_count = data[idx]; + if (data[idx] > framesCount) + framesCount = data[idx]; data[idx + 1] = 0; data[idx + 2] = 0; - //debug(0, "frame #%u", data[idx]); + debugC(2, kDebugAnimation, "frame #%u", data[idx]); } - frames = new Surface[frames_count]; + frames = new Surface[framesCount]; - for (uint16 i = 0; i < frames_count; ++i) { + for (uint16 i = 0; i < framesCount; ++i) { frames[i].load(s, Surface::kTypeOns); } } break; case kTypeVaria: - frames_count = s.readByte(); - debug(1, "loading varia resource, %u physical frames", frames_count); + framesCount = s.readByte(); + debugC(1, kDebugAnimation, "loading varia resource, %u physical frames", framesCount); uint16 offset[255]; - for (byte i = 0; i < frames_count; ++i) { + for (byte i = 0; i < framesCount; ++i) { offset[i] = s.readUint16LE(); - //debug(0, "%u: %04x", i, offset[i]); + debugC(0, kDebugAnimation, "%u: %04x", i, offset[i]); } - frames = new Surface[frames_count]; - for (uint16 i = 0; i < frames_count; ++i) { - //debug(0, "%04x", offset[i]); + frames = new Surface[framesCount]; + for (uint16 i = 0; i < framesCount; ++i) { + debugC(0, kDebugAnimation, "%04x", offset[i]); s.seek(offset[i] + off); frames[i].load(s, Surface::kTypeOns); } @@ -196,11 +200,7 @@ void Animation::load(Common::SeekableReadStream &s, Type type) { break; } - debug(0, "%u frames", data_size / 3); -} - -Animation::~Animation() { - free(); + debugC(2, kDebugAnimation, "%u frames", dataSize / 3); } } // End of namespace TeenAgent diff --git a/engines/teenagent/animation.h b/engines/teenagent/animation.h index 6942cc74eb..9be21a4c3d 100644 --- a/engines/teenagent/animation.h +++ b/engines/teenagent/animation.h @@ -35,6 +35,8 @@ public: enum Type {kTypeLan, kTypeVaria, kTypeInventory}; Animation(); + ~Animation(); + void load(Common::SeekableReadStream &, Type type = kTypeLan); void free(); @@ -43,8 +45,6 @@ public: uint16 currentIndex() const { return index; } void resetIndex() { index = 0; } - ~Animation(); - bool empty() const { return frames == NULL; } void restart(); @@ -53,9 +53,9 @@ public: protected: byte *data; - uint16 data_size; + uint16 dataSize; - uint16 frames_count; + uint16 framesCount; Surface *frames; uint16 index; }; diff --git a/engines/teenagent/callbacks.cpp b/engines/teenagent/callbacks.cpp index 8882531d27..2de81abb37 100644 --- a/engines/teenagent/callbacks.cpp +++ b/engines/teenagent/callbacks.cpp @@ -19,8 +19,9 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include "teenagent/scene.h" #include "teenagent/teenagent.h" +#include "teenagent/scene.h" +#include "teenagent/inventory.h" #include "teenagent/resources.h" #include "teenagent/dialog.h" @@ -33,159 +34,536 @@ namespace TeenAgent { #define GET_FLAG(addr) (res->dseg.get_byte(addr)) #define INC_FLAG(addr) (++*res->dseg.ptr(addr)) -void TeenAgentEngine::rejectMessage() { - Resources *res = Resources::instance(); - //random reject message: - uint i = _rnd.getRandomNumber(3); - //debug(0, "reject message: %s", (const char *)res->dseg.ptr(res->dseg.get_word(0x339e + 2 * i))); - displayMessage(res->dseg.get_word(0x339e + 2 * i)); +void TeenAgentEngine::fnIntro() { + hideActor(); + + loadScene(41, 139, 156, 3); + playSound(41, 12); + playAnimation(912, 1); + setOns(0, 108); + playSound(62, 8); + playSound(58, 40); + playAnimation(913, 1); + setOns(1, 109); + setLan(2, 1); + dialog->show(192, scene, 914, 915, textColorGoldDriver, textColorBankGuard, 2, 1); + displayCredits(dsAddr_introCredits1); + + loadScene(42, 139, 156, 3); + playSound(15, 20); + playAnimation(916, 1); + playSound(40, 18); + playSound(40, 22); + for (byte i = 27; i < 37; i += 2) + playSound(40, i); + playSound(29, 44); + playAnimation(918, 0, true); + playAnimation(917, 1, true); + waitAnimation(); + displayCredits(dsAddr_introCredits2); + + loadScene(40, 139, 156, 3); + playMusic(3); + dialog->show(193, scene, 920, 924, textColorRGBBoss, textColorFortuneTeller, 1, 2); + playSound(26, 50); + playAnimation(925, 0, true); + playAnimation(926, 1, true); + waitAnimation(); + dialog->show(194, scene, 927, 920, textColorFortuneTeller, textColorRGBBoss, 2, 1); + displayCredits(dsAddr_introCredits3); + + loadScene(39, 139, 156, 3); + playMusic(11); + playSound(81, 2); + playSound(81, 8); + playSound(81, 11); + playSound(81, 14); + playSound(81, 16); + playSound(81, 18); + playSound(81, 20); + playSound(81, 21); + playAnimation(928, 1); + setOns(0, 112); + dialog->showMono(195, scene, 929, textColorMark, 1); + showActor(); + moveTo(319, 150, 1, true); + moveTo(63, 150, 1); + displayAsyncMessage(dsAddr_HeyWtmQMsg, 4, 62, 18, 36); // hey, what's the matter? + playAnimation(851, 0, true); + playActorAnimation(930, true); + waitAnimation(); + playSound(24, 11); + playActorAnimation(931); + + displayCredits(dsAddr_introCredits4); + + playMusic(3); + loadScene(40, 50, 186, 1); + setOns(0, 113); + dialog->show(196, scene, 919, 0, textColorRGBBoss, textColorMark, 1, 0); + moveTo(196, 186, 1); + dialog->show(197, scene, 0, 920, textColorMark, textColorRGBBoss, 0, 1); + playActorAnimation(932); + dialog->show(198, scene, 0, 920, textColorMark, textColorRGBBoss, 0, 1); + playActorAnimation(932); + dialog->show(199, scene, 0, 920, textColorMark, textColorRGBBoss, 0, 1); + playActorAnimation(932); + dialog->show(200, scene, 0, 922, textColorMark, textColorRGBBoss, 0, 1); + playActorAnimation(933); + dialog->show(201, scene, 0, 920, textColorMark, textColorRGBBoss, 0, 1); + moveTo(174, 186, 1); + playAnimation(851, 0, true); + playActorAnimation(934, true); + waitAnimation(); + loadScene(10, 136, 153, 3); } +void TeenAgentEngine::fnPoleClimbFail() { + moveTo(86, 195, 1, true); + playActorAnimation(868); +} -bool TeenAgentEngine::processCallback(uint16 addr) { - if (addr == 0) - return false; +void TeenAgentEngine::fnGotAnchor() { + SET_FLAG(dsAddr_timedCallbackState, 0); + setTimerCallback(0, 0); + scene->getActorAnimation()->free(); + playSound(64, 7); + playActorAnimation(618); + disableObject(5); + setOns(0, 0); + playSound(31, 1); + playActorAnimation(619); + fnGetOutOfLake(); + inventory->add(kInvItemAnchor); + displayMessage(dsAddr_hookedAnchorMsg); // "I was really hooked on this anchor!" +} - Resources *res = Resources::instance(); - debug(0, "processCallback(%04x)", addr); - byte *code = res->cseg.ptr(addr); - - //try trivial callbacks first - if (code[0] == 0xbb && code[3] == 0xe8 && code[6] == 0xc3) { - //call display_message, r - uint16 msg = READ_LE_UINT16(code + 1); - uint16 func = 6 + addr + READ_LE_UINT16(code + 4); - debug(0, "call %04x", func); - //debug(0, "trivial callback, showing message %s", (const char *)res->dseg.ptr(addr)); - switch (func) { - case 0xa055: - displayMessage(msg); - return true; - } +void TeenAgentEngine::fnGetOutOfLake() { + loadScene(15, 156, 180, 3); + playSound(5, 5); + playSound(38, 14); + playSound(38, 20); + playSound(5, 25); + playActorAnimation(616); +} + +void TeenAgentEngine::fnGuardDrinking() { + SET_FLAG(dsAddr_timedCallbackState, 0); + setTimerCallback(0, 0); + scene->getAnimation(0)->free(); + SET_FLAG(dsAddr_scaredGuardAlreadyFlag, 1); + + displayAsyncMessage(dsAddr_BooMsg, 300, 130, 1, 5); // "Booo!" + setOns(0, 16); + enableObject(2); + + playSound(17, 5); + playAnimation(545, 0); + + dialog->show(5, scene, 0, 546, textColorMark, textColorMansionGuard, 0, 1); + SET_FLAG(dsAddr_spokenWithMansionGuardFlag, 1); + SET_FLAG(dsAddr_haveNotSpokenWithMansionGuardFlag, 0); +} + +void TeenAgentEngine::fnEgoDefaultPosition() { + if (scene->getPosition().y <= 149) + moveTo(94, 115, 4); + else + moveTo(51, 149, 4); +} + +void TeenAgentEngine::fnEnterCave() { + loadScene(24, 230, 170, 1); + playSound(52, 3); + playSound(52, 7); + playSound(52, 11); + playSound(52, 14); + playSound(52, 18); + playSound(52, 21); + playSound(52, 25); + playActorAnimation(601); + moveTo(230, 179, 3); + if (!CHECK_FLAG(dsAddr_lightOnFlag, 1)) + displayMessage(dsAddr_kindaDarkMsg); // "It's kinda dark here" +} + +void TeenAgentEngine::fnEgoScaredBySpider() { + if (CHECK_FLAG(dsAddr_egoAlreadyScaredBySpiderFlag, 1)) { + fnMoveToLadderAndLeaveCellar(); + dialog->showMark(75, scene); + } else { + dialog->showMark(73, scene); + fnMoveToLadderAndLeaveCellar(); + wait(100); + dialog->showMark(74, scene); + SET_FLAG(dsAddr_egoAlreadyScaredBySpiderFlag, 1); } +} - if (code[0] == 0xe8 && code[3] == 0xc3) { - uint func = 3 + addr + READ_LE_UINT16(code + 1); - debug(0, "call %04x and return", func); - if (func == 0xa4d6) { - rejectMessage(); +void TeenAgentEngine::fnMoveToLadderAndLeaveCellar() { + Object *objTemp = scene->getObject(3); + moveTo(objTemp); + fnLeaveCellar(); + moveTo(48, 190, 3); +} + +void TeenAgentEngine::fnLeaveCellar() { + playSound(52, 10); + playSound(52, 14); + playSound(52, 18); + playSound(52, 21); + playSound(52, 25); + playSound(52, 28); + playSound(52, 32); + playActorAnimation(600); + loadScene(21, 297, 178, 3); +} + +void TeenAgentEngine::fnPutRockInHole() { + if (CHECK_FLAG(dsAddr_timedCallbackState, 0)) { + playSound(5, 2); + playSound(15, 12); + playActorAnimation(638); + inventory->remove(kInvItemMouse); + setTimerCallback(csAddr_mouseOutOfHoleTimeout, 100); + SET_FLAG(dsAddr_timedCallbackState, 1); + } else if (CHECK_FLAG(dsAddr_timedCallbackState, 1)) { + playSound(5, 2); + playSound(52, 13); + playActorAnimation(648); + setOns(1, 46); + inventory->remove(kInvItemRock); + setTimerCallback(csAddr_mouseOutOfHoleTimeout, 100); + SET_FLAG(dsAddr_timedCallbackState, 2); + } else if (CHECK_FLAG(dsAddr_timedCallbackState, 2)) { + playActorAnimation(649); + setOns(1, 47); + wait(300); + for (byte i = 1; i <= 37; i += 4) + playSound(68, i); + playAnimation(639, 2); + setOns(0, 42); + enableObject(6); + disableObject(5); + SET_FLAG(dsAddr_mouseGotGoldNuggetFlag, 1); + SET_FLAG(dsAddr_timedCallbackState, 0); + setTimerCallback(0, 0); + } +} + +void TeenAgentEngine::fnEgoBottomRightTurn() { + Common::Point p = scene->getPosition(); + if (p.x == 208 && p.y == 151) + moveRel(0, 0, 2); + else + moveTo(208, 151, 1); +} + +bool TeenAgentEngine::fnCheckingDrawers() { + uint16 v = GET_FLAG(dsAddr_drawerPuzzleBookValue) - 1; + if (GET_FLAG(dsAddr_blueDrawerOpenFlag + v) != 1) + return false; + else { + uint16 sum = 0; + for (uint i = 0; i < 6; ++i) + sum += GET_FLAG(dsAddr_blueDrawerOpenFlag + i); + if (sum != 1) + return false; + else return true; - } } +} - if (code[0] == 0xc7 && code[1] == 0x06 && code[2] == 0xf3 && code[3] == 0xb4 && - code[6] == 0xb8 && code[9] == 0xbb && code[12] == 0xbf && - code[22] == 0xe8 && code[25] == 0xc3) { - loadScene(code[4], Common::Point( - (READ_LE_UINT16(code + 7) + READ_LE_UINT16(code + 13) + 1) / 2 , - READ_LE_UINT16(code + 10))); - scene->setOrientation(code[21]); +void TeenAgentEngine::fnDrawerOpenMessage() { + if (CHECK_FLAG(dsAddr_drawerPuzzleHintGivenFlag, 1)) + displayMessage(dsAddr_drawerOpenMsg); // "I cannot open the drawer if the next one is open!" + else { + displayMessage(dsAddr_strangeDrawerMsg); // "Strange, but the drawer is stuck if the next drawer is open" + displayMessage(dsAddr_notOrdinaryDrawersMsg); // "Maybe these are not just ordinary drawers!" + SET_FLAG(dsAddr_drawerPuzzleHintGivenFlag, 1); + } +} + +bool TeenAgentEngine::fnRobotSafeAlreadyUnlockedCheck() { + if (CHECK_FLAG(dsAddr_MansionRobotSafeUnlockedFlag, 1)) { return true; + } else { + displayMessage(dsAddr_noReasonMsg); // "There's no reason to do it" + return false; } +} - switch (addr) { +void TeenAgentEngine::fnRobotSafeUnlockCheck() { + if (CHECK_FLAG(dsAddr_MansionRobotSafeVoiceTestPassedFlag, 1) && + CHECK_FLAG(dsAddr_MansionRobotSafeScentTestPassedFlag, 1) && + CHECK_FLAG(dsAddr_MansionRobotSafeViewTestPassedFlag, 1)) { + waitLanAnimationFrame(1, 1); + playSound(89, 2); + playActorAnimation(731); + setOns(0, 70); + setLan(1, 0); + disableObject(1); + enableObject(2); + enableObject(3); + } +} - case 0x024c: //intro - hideActor(); +bool TeenAgentEngine::fnMansionIntrusionAttempt() { + wait(50); + byte attempts = res->dseg.get_byte(dsAddr_mansionEntryCount) + 1; + res->dseg.set_byte(dsAddr_mansionEntryCount, attempts); + debugC(0, kDebugCallbacks, "mansion intrusion attempt #%u", attempts); + if (attempts >= 7) + return false; + else { + byte id = scene->getId(); - loadScene(41, 139, 156, 3); - playSound(41, 12); - playAnimation(912, 1); - setOns(0, 108); - playSound(62, 8); - playSound(58, 40); - playAnimation(913, 1); - setOns(1, 109); - setLan(2, 1); - Dialog::show(scene, 0x748e, 914, 915, 0xe7, 0xd7, 2, 1); - displayCredits(0xe3c2); - - loadScene(42, 139, 156, 3); - playSound(15, 20); - playAnimation(916, 1); - playSound(40, 18); - playSound(40, 22); - for (byte i = 27; i < 37; i += 2) - playSound(40, i); - playSound(29, 44); - playAnimation(918, 0, true); - playAnimation(917, 1, true); - waitAnimation(); - displayCredits(0xe3e6); + playMusic(11); + displayCutsceneMessage(dsAddr_cutsceneMsg2, 84, 95); // "Meanwhile in the mansion" + switch (attempts) { + case 2: + fnSecondMansionIntrusion(); + break; + case 3: + fnThirdMansionIntrusion(); + break; + case 4: + fnFourthMansionIntrusion(); + break; + case 5: + fnFifthMansionIntrusion(); + break; + case 6: + fnSixthMansionIntrusion(); + break; + default: + error("mansion intrusion attempts out of range!"); + break; + } + playMusic(6); + if (getFlag(dsAddr_johnNotyOutsideMansionDoorFlag) != 1 || attempts != 6) + loadScene(id, scene->getPosition()); + return true; + } +} - loadScene(40, 139, 156, 3); - playMusic(3); - Dialog::show(scene, 0x750d, 920, 924, 0xe7, 0xeb, 1, 2); //as i told you, our organization... - playSound(26, 50); - playAnimation(925, 0, true); - playAnimation(926, 1, true); - waitAnimation(); - Dialog::show(scene, 0x78a6, 927, 920, 0xeb, 0xe7, 2, 1); - displayCredits(0xe3ff); +void TeenAgentEngine::fnSecondMansionIntrusion() { + hideActor(); + loadScene(34, scene->getPosition()); + playAnimation(986, 0, true); + playAnimation(987, 1, true); + waitAnimation(); + dialog->show(178, scene, 988, 989, textColorMansionGuard, textColorJohnNoty, 1, 2); + playAnimation(990, 0, true); + playAnimation(991, 1, true); + waitAnimation(); + showActor(); +} - loadScene(39, 139, 156, 3); - playMusic(11); - playSound(81, 2); - playSound(81, 8); - playSound(81, 11); - playSound(81, 14); - playSound(81, 16); - playSound(81, 18); - playSound(81, 20); - playSound(81, 21); - playAnimation(928, 1); - setOns(0, 112); - Dialog::showMono(scene, 0x78e1, 929, 0xd1, 1); //he's coming - showActor(); - moveTo(319, 150, 1, true); - moveTo(63, 150, 1); - displayAsyncMessage(0x5da8, 19844, 18, 36); //hey, what's the matter? - playAnimation(851, 0, true); - playActorAnimation(930, true); - waitAnimation(); - playSound(24, 11); - playActorAnimation(931); +void TeenAgentEngine::fnThirdMansionIntrusion() { + hideActor(); + loadScene(30, scene->getPosition()); + playAnimation(887, 1); + playAnimation(888, 2, true, true, true); + //waitAnimation(); + dialog->showMono(179, scene, 889, textColorMansionGuard, 2); + playSound(26, 3); + playAnimation(891, 1, true, true, true); + playAnimation(892, 2); + waitAnimation(); + dialog->show(180, scene, 890, 889, textColorJohnNoty, textColorMansionGuard, 3, 2); + showActor(); +} - displayCredits(0xe42f); +void TeenAgentEngine::fnFourthMansionIntrusion() { + hideActor(); + loadScene(32, scene->getPosition()); + playAnimation(894, 1, true, true, true); + playAnimation(893, 2, true); + waitAnimation(); + dialog->showMono(181, scene, 895, textColorMansionGuard, 3); + playSound(75, 9); + playAnimation(898, 1, true); + playAnimation(897, 2, true); + dialog->show(182, scene, 896, 895, textColorJohnNoty, textColorMansionGuard, 2, 3); + showActor(); +} - playMusic(3); - loadScene(40, 50, 186, 1); - setOns(0, 113); - Dialog::show(scene, 0x78f1, 919, 0, 0xe7, 0xd1, 1, 0); - moveTo(196, 186, 1); - Dialog::show(scene, 0x7958, 0, 920, 0xd1, 0xe7, 0, 1); - playActorAnimation(932); - Dialog::show(scene, 0x7e07, 0, 920, 0xd1, 0xe7, 0, 1); - playActorAnimation(932); - Dialog::show(scene, 0x7e1a, 0, 920, 0xd1, 0xe7, 0, 1); - playActorAnimation(932); - Dialog::show(scene, 0x7e2c, 0, 922, 0xd1, 0xe7, 0, 1); - playActorAnimation(933); - Dialog::show(scene, 0x7e70, 0, 920, 0xd1, 0xe7, 0, 1); - moveTo(174, 186, 1); - playAnimation(851, 0, true); - playActorAnimation(934, true); - waitAnimation(); - loadScene(10, 136, 153, 3); +void TeenAgentEngine::fnFifthMansionIntrusion() { + hideActor(); + loadScene(29, scene->getPosition()); + playActorAnimation(901, true); + playAnimation(900, 1, true); + waitAnimation(); + dialog->show(183, scene, 903, 902, textColorJohnNoty, textColorMansionGuard, 2, 3); + for (byte i = 3; i <= 9; i += 2) + playSound(56, i); + + playActorAnimation(905, true); + playAnimation(904, 1, true); + dialog->show(184, scene, 903, 902, textColorJohnNoty, textColorMansionGuard, 2, 3); + showActor(); +} + +void TeenAgentEngine::fnSixthMansionIntrusion() { + hideActor(); + loadScene(35, scene->getPosition()); + playAnimation(907, 2, true); + playAnimation(906, 3, true); + waitAnimation(); + dialog->show(185, scene, 908, 909, textColorMansionGuard, textColorJohnNoty, 2, 3); + dialog->show(186, scene, 910, 908, textColorJohnNoty, textColorMansionGuard, 3, 2); + loadScene(11, scene->getPosition()); + showActor(); + setOns(3, 51); + playAnimation(911, 1); + playAnimation(899, 1); + setFlag(dsAddr_johnNotyOutsideMansionDoorFlag, 1); + reloadLan(); + wait(200); + enableObject(8); + setLan(2, 8); +} +void TeenAgentEngine::fnTooDark() { + displayMessage(dsAddr_TooDarkMsg); // "It's too dark to see clearly" +} + +bool TeenAgentEngine::fnIsCookGone() { + if (CHECK_FLAG(dsAddr_MansionCookGoneFlag, 1)) { return true; + } else { + displayMessage(dsAddr_cookAroundMsg); // "I can't do anything with this cook around" + return false; + } +} + +void TeenAgentEngine::fnEgoSuspiciousPosition() { + Common::Point p = scene->getPosition(); + if (p.x != 203 && p.y != 171) + moveTo(203, 169, 2); + else + moveTo(203, 169, 1); +} + +void TeenAgentEngine::fnGivingFlowerToOldLady() { + playSound(5, 2); + dialog->show(37, scene, 0, 523, textColorMark, textColorOldLady, 0, 1); + playActorAnimation(537, true); + playAnimation(538, 0, true); + waitAnimation(); + wait(100); + dialog->show(38, scene, 0, 523, textColorMark, textColorOldLady, 0, 1); +} + +void TeenAgentEngine::fnGiveAnotherFlowerToOldLady() { + dialog->pop(scene, dsAddr_dialogStackOldLady, 0, 523, textColorMark, textColorOldLady, 0, 1); +} + +void TeenAgentEngine::fnGivingFlowerToAnne() { + dialog->show(53, scene, 0, 524, textColorMark, textColorAnne, 0, 2); + playSound(5, 10); + playActorAnimation(540, true); + playAnimation(539, 1, true); + waitAnimation(); + wait(100); + dialog->show(54, scene, 0, 524, textColorMark, textColorAnne, 0, 2); + wait(50); + dialog->show(55, scene, 0, 524, textColorMark, textColorAnne, 0, 2); + dialog->show(56, scene, 0, 524, textColorMark, textColorAnne, 0, 2); + wait(50); + moveRel(0, 1, 0); + dialog->show(57, scene, 0, 524, textColorMark, textColorAnne, 0, 2); + moveRel(0, -1, 0); + wait(50); +} + +void TeenAgentEngine::fnGiveAnotherFlowerToAnne() { + dialog->pop(scene, dsAddr_dialogStackAnotherFlowerToAnne, 0, 524, textColorMark, textColorAnne, 0, 2); +} + +void TeenAgentEngine::rejectMessage() { + uint i = _rnd.getRandomNumber(3); + switch (i) { + case 0: + displayMessage(dsAddr_rejectMsg0); // "I have no idea what to do with it" + break; + case 1: + displayMessage(dsAddr_rejectMsg1); // "I can't imagine what I could do with this" + break; + case 2: + displayMessage(dsAddr_rejectMsg2); // "I can't figure out what I should do with this" + break; + case 3: + displayMessage(dsAddr_rejectMsg3); // "I can't find any reason to mess with it" + break; + default: + error("rejectMessage() index out of range"); + break; + } +} + +bool TeenAgentEngine::processCallback(uint16 addr) { + if (addr == 0) + return false; + + debugC(0, kDebugCallbacks, "processCallback(%04x)", addr); + + bool retVal = true; + switch (addr) { + case csAddr_intro: // intro + fnIntro(); + break; + + case 0x3fed: + loadScene(3, Common::Point(305, 104)); + scene->setOrientation(4); + break; + + case 0x4007: + loadScene(5, Common::Point(300, 131)); + scene->setOrientation(3); + break; case 0x4021: - //pulling out mysterious object - if (CHECK_FLAG(0xdbe1, 1)) { + // pulling out mysterious object + if (CHECK_FLAG(dsAddr_cutFenceFlag, 1)) { playActorAnimation(844); playActorAnimation(846); playActorAnimation(845); - displayMessage(0x5696); + displayMessage(dsAddr_pullObjMsg1); // "I can't pull it out" } else { - displayMessage(0x570f); + displayMessage(dsAddr_pullObjMsg2); // "I can't reach it" } - return true; - - case 0x4094: //climbing to the pole near mudpool - if (CHECK_FLAG(0xDBE4, 1)) { - displayMessage(0x57b2); - return true; + break; + + case 0x4048: + displayMessage(dsAddr_dontWantToTouchMsg); // "I don't want to touch it - I might get hurt" + break; + + case 0x404f: + displayMessage(dsAddr_notWantToSleepMsg); // "I don't want to sleep" + break; + + case 0x4056: + // FIXME - This is the bird use callback in the first act at + // the mudpool. Current Code based on behaviour. Need to analyse cseg data. + dialog->popMark(scene, dsAddr_dialogStackMudpoolBird); + break; + + case 0x4060: + loadScene(2, Common::Point(28, 180)); + scene->setOrientation(2); + break; + + case 0x407a: + loadScene(4, Common::Point(297, 128)); + scene->setOrientation(4); + break; + + case 0x4094: // climbing to the pole near mudpool + if (CHECK_FLAG(dsAddr_gotMugOfMudFlag, 1)) { + displayMessage(dsAddr_poleClimbDoneMsg); // "Never Again!" } else { for (byte i = 11; i <= 27; i += 4) playSound(76, i); @@ -196,39 +574,49 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playAnimation(865, 1); playActorAnimation(866); //InventoryObject *obj = inventory->selectedObject(); - //if (obj != NULL && obj->id == 0x55) { + //if (obj != NULL && obj->id == kInvItemMug) { - //implement pause and using real object: - if (inventory->has(0x55)) { + // FIXME: implement pause in mudpool and using of Mug object, as per original interpreter + if (inventory->has(kInvItemMug)) { playSound(5, 4); playSound(5, 19); playSound(64, 11); playActorAnimation(867); - inventory->remove(0x55); - inventory->add(0x56); + inventory->remove(kInvItemMug); + inventory->add(kInvItemMugOfMud); moveTo(86, 195, 1, true); playActorAnimation(868); - SET_FLAG(0xDBE4, 1); + SET_FLAG(dsAddr_gotMugOfMudFlag, 1); } else { - processCallback(0x4173); - Dialog::pop(scene, 0xDB72, 0, 0, 0xd1, 0xd1, 0, 0); + fnPoleClimbFail(); + dialog->popMark(scene, dsAddr_dialogStackFallIntoMudpool); } - return true; } - case 0x4173: - //fail! - moveTo(86, 195, 1, true); - playActorAnimation(868); - return true; + break; + + case csAddr_poleClimbFail: + fnPoleClimbFail(); + break; - case 0x419c: //getting the bird + case 0x4195: + displayMessage(dsAddr_preferWaterMsg); // "I prefer water" + break; + + case 0x419c: // getting the bird setOns(0, 0); playSound(56, 10); playActorAnimation(875); disableObject(6); - inventory->add(0x5c); - return true; + inventory->add(kInvItemBird); + break; + case 0x41c3: + displayMessage(dsAddr_pullObjMsg2); // "I can't reach it" + break; + + case 0x41ca: + rejectMessage(); + break; case 0x41ce: moveTo(197, 159, 4); @@ -236,9 +624,23 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playSound(71, 8); playActorAnimation(833); moveTo(225, 159, 4); - inventory->add(0x4e); + inventory->add(kInvItemDelicatePlant); disableObject(3); - return true; + break; + + case 0x422c: + displayMessage(dsAddr_tooWeakToClimbMsg); // "I'm too weak to climb it" + break; + + case 0x4233: + loadScene(3, Common::Point(216, 199)); + scene->setOrientation(1); + break; + + case 0x424d: + loadScene(5, Common::Point(18, 174)); + scene->setOrientation(2); + break; case 0x4267: hideActor(); @@ -252,92 +654,111 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playSound(5, 26); playActorAnimation(842); wait(100); - //shown in different positions - displayMessage(0x5656, 0xd1, 0x5510); + // shown in different positions + displayMessage(dsAddr_fnMsg2, textColorMark, 16, 68); // "And how am I supposed to get back?" wait(50); - displayMessage(0x567a, 0xd1, 0x555c); + displayMessage(dsAddr_fnMsg3, textColorMark, 92, 68); // "Great" wait(50); - displayMessage(0x5682, 0xd1, 0x553e); + displayMessage(dsAddr_fnMsg4, textColorMark, 62, 68); // "Oh, yeah, right" wait(50); playActorAnimation(843); showActor(); moveTo(223, 149, 0, true); disableObject(7); disableObject(1); - inventory->add(0x51); - displayMessage(0x5646); - return true; + inventory->add(kInvItemShovelAct1); + displayMessage(dsAddr_fnMsg1); // "Piece of cake" + break; + + case 0x433a: + loadScene(10, Common::Point(294, 183)); + scene->setOrientation(4); + break; + + case 0x4354: + loadScene(4, Common::Point(300, 185)); + scene->setOrientation(4); + break; + + case 0x436e: + loadScene(2, Common::Point(219, 199)); + scene->setOrientation(1); + break; case 0x4388: playSound(80, 4); playActorAnimation(961); loadScene(8, 155, 199, 1); - return true; + break; - case 0x43b5: //HQ, first trial - prison + case 0x43b5: // HQ, first trial - prison playSound(70, 6); playActorAnimation(962); loadScene(7, 30, 184, 2); - if (res->dseg.get_byte(0xDBDF) < 2) { + if (res->dseg.get_byte(dsAddr_FirstActTrialState) < 2) { wait(150); moveTo(134, 167, 2); - displayMessage(0x54f7); + displayMessage(dsAddr_firstTrialMsg); // "Sir, I'm Mark. A rookie" setLan(1, 0); playAnimation(812, 0, true); playActorAnimation(811); - Dialog::show(scene, 0x6117, 0, 813, 0xd1, 0xec, 0, 1); + dialog->show(148, scene, 0, 813, textColorMark, textColorCaptain, 0, 1); loadScene(6, 230, 184); playMusic(5); - Dialog::show(scene, 0x626a, 0, 814, 0xd1, 0xec, 0, 1); + dialog->show(149, scene, 0, 814, textColorMark, textColorCaptain, 0, 1); playSound(4, 14); playAnimation(815, 0); setOns(1, 0); - Dialog::showMono(scene, 0x62dc, 0, 0xd1, 0); + dialog->showMono(150, scene, 0, textColorMark, 0); - SET_FLAG(0xDBDF, 1); + SET_FLAG(dsAddr_FirstActTrialState, 1); } - return true; + break; case 0x4482: - if (CHECK_FLAG(0xDBDF, 0)) { + if (CHECK_FLAG(dsAddr_FirstActTrialState, 0)) { playActorAnimation(968); - displayMessage(0x5511); + displayMessage(dsAddr_lockedMsg); // "It's Locked!" } else { playSound(80, 3); playSound(79, 4); playActorAnimation(968); loadScene(6, 280, 186, 4); } - return true; + break; - case 0x44fc: //pull out spring from bed + case 0x44fc: // pull out spring from bed playSound(53, 25); playSound(24, 27); playSound(5, 36); playActorAnimation(839); moveTo(278, scene->getPosition().y, 0, true); - inventory->add(0x50); + inventory->add(kInvItemSpring); disableObject(1); - return true; + break; case 0x44cb: - if (CHECK_FLAG(0xDBE5, 1)) { - displayMessage(0x57c0); + if (CHECK_FLAG(dsAddr_gotRopeAct1Flag, 1)) { + displayMessage(dsAddr_vacMsg); // "What am I? A vacuum cleaner?!" } else { playSound(49, 14); playSound(5, 21); playActorAnimation(869); - inventory->add(0x58); - SET_FLAG(0xDBE5, 1); + inventory->add(kInvItemRopeAct1); + SET_FLAG(dsAddr_gotRopeAct1Flag, 1); } - return true; + break; + + case 0x4532: + displayMessage(dsAddr_springPrickMsg); // "The springs would prick my back" + break; - case 0x4539: //prison cell: use crates - if (CHECK_FLAG(0xdbdd, 2)) { - //finished the meal - trap - displayMessage(0x55c0); + case 0x4539: // prison cell: use crates + if (CHECK_FLAG(dsAddr_JailCableAndBowlState, 2)) { + // finished the meal - trap + displayMessage(dsAddr_mealFinishedMsg); // "Hey! I finished my meal." moveTo(306, 196, 2); wait(50); //playAnimation(825, 1); //very long empty animation. what for? @@ -354,66 +775,65 @@ bool TeenAgentEngine::processCallback(uint16 addr) { loadScene(6, scene->getPosition()); setOns(3, 0x5b); wait(50); - displayMessage(0x55db); - SET_FLAG(0xdbdd, 3); + displayMessage(dsAddr_bowlWeldedMsg); // "Wow. He got welded to the bowl" + SET_FLAG(dsAddr_JailCableAndBowlState, 3); scene->getObject(4)->setName("body"); } else { - if (Dialog::pop(scene, 0xdb5c, 0, 0, 0xd1, 0xd1, 0, 0) != 0x636b) //not 'im getting hungry' - return true; - - wait(100); - playSound(52, 8); - playSound(52, 13); - playAnimation(820, 1); - setOns(3, 0x59); - wait(50); - moveTo(scene->getPosition().x, scene->getPosition().y + 1, 3); - wait(150); - moveTo(scene->getPosition().x, scene->getPosition().y - 1, 2); - wait(100); - displayMessage(0x551f); - enableObject(4); - SET_FLAG(0xdbdc, 1); + if (dialog->pop(scene, dsAddr_dialogStackJailDoorGrates, 0, 0, textColorMark, textColorMark, 0, 0) == 0x636b) { // 'im getting hungry' + wait(100); + playSound(52, 8); + playSound(52, 13); + playAnimation(820, 1); + setOns(3, 0x59); + wait(50); + moveTo(scene->getPosition().x, scene->getPosition().y + 1, 3); + wait(150); + moveTo(scene->getPosition().x, scene->getPosition().y - 1, 2); + wait(100); + displayMessage(dsAddr_ThanksMsg); // "Thanks." + enableObject(4); + SET_FLAG(dsAddr_GotFoodBowlInJailFlag, 1); + } } - return true; + break; case 0x4662: - if (CHECK_FLAG(0xDBDD, 3)) { - if (CHECK_FLAG(0xDBDE, 1)) { - displayMessage(0x5608); + if (CHECK_FLAG(dsAddr_JailCableAndBowlState, 3)) { + if (CHECK_FLAG(dsAddr_GotJailKeyFlag, 1)) { + displayMessage(dsAddr_noPocketMsg); // "I don't want to touch his pockets again." } else { moveTo(280, 179, 2); playSound(49, 7); playSound(5, 17); playActorAnimation(827); - inventory->add(0x4d); - SET_FLAG(0xDBDE, 1); + inventory->add(kInvItemJailKey); + SET_FLAG(dsAddr_GotJailKeyFlag, 1); } } else - displayMessage(0x5905); - return true; + displayMessage(dsAddr_foodAliveMsg); // "No, thanks. This food seems still alive" + break; - case 0x46af: //prison cell: use live cable - if (CHECK_FLAG(0xdbdc, 1)) { - displayMessage(0x555d); + case 0x46af: // prison cell: use live cable + if (CHECK_FLAG(dsAddr_GotFoodBowlInJailFlag, 1)) { + displayMessage(dsAddr_ideaMsg); // "That gives me an idea" setOns(2, 0); playActorAnimation(821); setOns(2, 0x5a); setOns(3, 0); playSound(22, 2); playActorAnimation(822); - displayMessage(0x5577); + displayMessage(dsAddr_checkWorksMsg); // "Now I got to check if it works" disableObject(5); - SET_FLAG(0xdbdd, 1); + SET_FLAG(dsAddr_JailCableAndBowlState, 1); } else - displayMessage(0x5528); - return true; + displayMessage(dsAddr_unkUsageMsg); // "I don't have any idea what to do with it right now" + break; - case 0x4705: //prison: getting lamp bulb + case 0x4705: // prison: getting lamp bulb wait(50); moveTo(144, 185, 4); playSound(56, 15); - setOns(0, 86); //hiding lamp + setOns(0, 86); // hiding lamp playActorAnimation(816, true); playAnimation(817, 0, true); waitAnimation(); @@ -430,69 +850,102 @@ bool TeenAgentEngine::processCallback(uint16 addr) { disableObject(6); enableObject(5); - inventory->add(0x4c); - return true; + inventory->add(kInvItemBulb); + break; - case 0x4794: //prison cell door - if (res->dseg.get_byte(0xDBDF) >= 2) { + case 0x4794: // prison cell door + if (res->dseg.get_byte(dsAddr_FirstActTrialState) >= 2) { loadScene(5, 287, 143); } else { - displayMessage(0x592f); + displayMessage(dsAddr_doorClosedMsg); // "The door is closed. What a surprise." } - return true; + break; - case 0x47bc: //prison: examining trash can + case 0x47bc: // prison: examining trash can playSound(79, 5); playSound(1, 14); playActorAnimation(966); - displayMessage(0x5955); - return true; + displayMessage(dsAddr_emptyMsg); // "It's Empty" + break; - case 0x47db: //prison: use switch - if (CHECK_FLAG(0xDBDF, 1)) { + case 0x47db: // prison: use switch + if (CHECK_FLAG(dsAddr_FirstActTrialState, 1)) { playSound(71, 4); playActorAnimation(823); - if (CHECK_FLAG(0xDBDD, 0)) { - displayMessage(0x4d80); + if (CHECK_FLAG(dsAddr_JailCableAndBowlState, 0)) { + displayMessage(dsAddr_NotHappenMsg); // "Nothing happened" } else { playSound(74, 1); playAnimation(824, 1); - if (CHECK_FLAG(0xDBDD, 1)) { + if (CHECK_FLAG(dsAddr_JailCableAndBowlState, 1)) { wait(100); - displayMessage(0x559a); - SET_FLAG(0xDBDD, 2); + displayMessage(dsAddr_timeToCallMsg); // "I think it is time to call captain" + SET_FLAG(dsAddr_JailCableAndBowlState, 2); } } } else { - displayMessage(0x52f6); + displayMessage(dsAddr_nahMsg); // "Nah" } - return true; + break; + + case 0x4836: + rejectMessage(); + break; case 0x4871: playActorAnimation(965); - displayMessage(0x5511); - return true; + displayMessage(dsAddr_lockedMsg); // "It's Locked!" + break; + + case 0x487e: + displayMessage(dsAddr_geographyClassMsg); // "I should have paid more attention in geography classes." + break; + + case 0x4885: + displayMessage(dsAddr_dontNeedMessMsg); // "I don't need this mess" + break; + + case 0x488c: + displayMessage(dsAddr_seenSofterRocksMsg); // "Thanks, but I've seen softer rocks" + break; - case 0x4893: //taking pills - if (CHECK_FLAG(0xDBE6, 1)) { - SET_FLAG(0xDBE6, 2); + case 0x4893: // taking pills + if (CHECK_FLAG(dsAddr_captainDrawerState, 1)) { + SET_FLAG(dsAddr_captainDrawerState, 2); setOns(1, 0x67); playSound(5, 9); playActorAnimation(872); - inventory->add(0x5a); + inventory->add(kInvItemMedicine); disableObject(7); } else { playActorAnimation(964); - displayMessage(0x5511); + displayMessage(dsAddr_lockedMsg); // "It's Locked!" } - return true; + break; + + case 0x48d4: + displayMessage(dsAddr_tooBluntMsg); // "They are too blunt to be of any use" + break; + + case 0x48db: + displayMessage(dsAddr_uselessModelsMsg); // "What's the use of the models?" + break; + + case 0x48e2: + case 0x48e6: + rejectMessage(); + break; + + case 0x4911: + displayMessage(dsAddr_barmanWillNoticeMsg); // "The barman will surely notice its disappearing" + break; - case 0x4918: //talking with barmen - if (CHECK_FLAG(0xDBE7, 1)) { + case 0x4918: // talking with barmen + if (CHECK_FLAG(dsAddr_birdOnBarRadioAntennaFlag, 1)) { moveTo(140, 152, 1); - if (CHECK_FLAG(0xDBE8, 1)) { - Dialog::showMono(scene, 0x6f20, 0, 0xd1, 0); //aren't you thirsty? - displayMessage(0x5883, 0xef, 21472); + if (CHECK_FLAG(dsAddr_swappedBarmanMugFlag, 1)) { + dialog->showMono(177, scene, 0, textColorMark, 0); + displayMessage(dsAddr_yeahRightMsg, textColorBarman, 32, 67); // "Yeah right!" //reloadLan(); setLan(1, 0); playAnimation(882, 0); @@ -504,86 +957,145 @@ bool TeenAgentEngine::processCallback(uint16 addr) { shakeScreen(); disableObject(1); disableObject(2); - SET_FLAG(0xDBE9, 1); + SET_FLAG(dsAddr_barmanPassedOutFlag, 1); } else - displayMessage(0x5855); + displayMessage(dsAddr_talkNotNowMsg); // "I've got no reason to talk to him right now." } else { - if (CHECK_FLAG(0xDBDF, 3)) { - if (CHECK_FLAG(0xDBE3, 1)) { - Dialog::show(scene, 0x6BD6, 0, 857, 0xd1, 0xef, 0, 1); + if (CHECK_FLAG(dsAddr_FirstActTrialState, 3)) { + if (CHECK_FLAG(dsAddr_spokeToBarmanAboutThirdTrialFlag, 1)) { + dialog->show(168, scene, 0, 857, textColorMark, textColorBarman, 0, 1); } else { - Dialog::show(scene, 0x69B5, 0, 857, 0xd1, 0xef, 0, 1); //taking mug + dialog->show(166, scene, 0, 857, textColorMark, textColorBarman, 0, 1); // taking mug playActorAnimation(859, true); playAnimation(858, 0, true); waitAnimation(); playSound(75, 6); playActorAnimation(860); - Dialog::show(scene, 0x69C2, 0, 857, 0xd1, 0xef, 0, 1); - inventory->add(0x55); - SET_FLAG(0xDBE3, 1); - SET_FLAG(0xDBF0, 0); + dialog->show(167, scene, 0, 857, textColorMark, textColorBarman, 0, 1); + inventory->add(kInvItemMug); + SET_FLAG(dsAddr_spokeToBarmanAboutThirdTrialFlag, 1); + SET_FLAG(dsAddr_gotPasswordNeedSpeakBarmanFlag, 0); } } else { - Dialog::pop(scene, 0xDB68, 0, 857, 0xd1, 0xef, 0, 1); + dialog->pop(scene, dsAddr_dialogStackBarman, 0, 857, textColorMark, textColorBarman, 0, 1); } } - return true; + break; - case 0x4f14: //use the hollow - displayMessage(CHECK_FLAG(0xDBA1, 1) ? 0x370f : 0x36c2); - return true; + case 0x4d7d: + case 0x4d81: + rejectMessage(); + break; + + case 0x4d89: + displayMessage(dsAddr_getRidOfGuardFirstMsg); // "If I want to get inside I must get rid of this guard first..." + break; + + case 0x4d90: + rejectMessage(); + break; + + case 0x4e47: + loadScene(13, Common::Point(9, 172)); + scene->setOrientation(2); + break; + + case 0x4e85: + loadScene(15, Common::Point(291, 162)); + scene->setOrientation(4); + break; + + case 0x4e9f: + loadScene(12, Common::Point(310, 152)); + scene->setOrientation(4); + break; + + case 0x4f14: // use the hollow + if (CHECK_FLAG(dsAddr_mansionTreeHollowEmptyFlag, 1)) + displayMessage(dsAddr_totalEmptyMsg); // "I can see it's totally empty" + else + displayMessage(dsAddr_noHandsMsg); // "I'd better not put my hands in there..." + break; case 0x4a64: - if (CHECK_FLAG(0xDBF0, 1)) { - displayMessage(0x5e25); - } else { + if (CHECK_FLAG(dsAddr_gotPasswordNeedSpeakBarmanFlag, 1)) + displayMessage(dsAddr_firstBusinessMsg); // "First I've got some business to take care of" + else loadScene(5, 35, 162); - } - return true; + break; case 0x4bf5: playActorAnimation(959); loadScene(8, 40, 152, 3); - return true; + break; + + case 0x4c18: + rejectMessage(); + break; + + case 0x4c29: + displayMessage(dsAddr_tooManyToSearchMsg); // "There are too many of them to search" + break; + + case 0x4c30: + case 0x4c37: + displayMessage(dsAddr_captainWouldNotFitMsg); // "Captain surely wouldn't fit them. I must look elsewhere" + break; case 0x483a: - Dialog::pop(scene, 0xdb82, 0, 0, 0xd1, 0xd1, 0, 0); - return true; + dialog->popMark(scene, dsAddr_dialogStackInterrogateCaptain); + break; case 0x4844: playSound(80, 4); playActorAnimation(963); loadScene(5, 166, 158); - return true; + break; case 0x48ea: setOns(0, 0); playSound(5, 9); playActorAnimation(836); - inventory->add(0x4f); + inventory->add(kInvItemSwissArmyKnife); disableObject(12); - return true; + break; case 0x4a8c: - if (CHECK_FLAG(0xDBE9, 1)) { + if (CHECK_FLAG(dsAddr_barmanPassedOutFlag, 1)) { playSound(89, 5); playActorAnimation(958); loadScene(9, 240, 182, 4); - } else if (CHECK_FLAG(0xDBE7, 1)) { - displayMessage(0x5894); + } else if (CHECK_FLAG(dsAddr_birdOnBarRadioAntennaFlag, 1)) { + displayMessage(dsAddr_barmanTooCloseMsg); // "The barman is too close" } else { - Dialog::pop(scene, 0xDB8A, 0, 857, 0xd1, 0xef, 0, 1); + dialog->pop(scene, dsAddr_dialogStackBarCellarDoor, 0, 857, textColorMark, textColorBarman, 0, 1); } - return true; + break; - case 0x4af4: //taking the crumbs + case 0x4aed: + displayMessage(dsAddr_tooBigMsg); // "It's too big and I doubt if I'll ever need it" + break; + + case 0x4af4: // taking the crumbs setOns(0, 0); playSound(49, 6); playSound(5, 13); playActorAnimation(861); - inventory->add(0x57); + inventory->add(kInvItemCrumbs); disableObject(6); - return true; + break; + + case 0x4b23: + rejectMessage(); + break; + + case 0x4b27: + displayMessage(dsAddr_tooMuchToDrinkMsg); // "It'd take too much time to drink it..." + break; + + case 0x4b2e: + displayMessage(dsAddr_notThiefMsg); // "I'm not a thief. And it's empty, by the way." + break; case 0x4b35: playSound(15, 7); @@ -591,140 +1103,149 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playSound(55, 1); playSound(24, 12); playAnimation(885, 0); - Dialog::show(scene, 0x67e5, 886, 0, 0xd0, 0xd1, 1, 0); + dialog->show(164, scene, 886, 0, textColorJohnNoty, textColorMark, 1, 0); playMusic(3); loadScene(40, 198, 186, 1); - Dialog::show(scene, 0x7f20, 0, 920, 0xd1, 0xe7, 0, 1); + dialog->show(202, scene, 0, 920, textColorMark, textColorRGBBoss, 0, 1); inventory->clear(); - inventory->add(0x1d); - displayCredits(0xe45c); + inventory->add(kInvItemSuperGlue); + displayCredits(dsAddr_credits5); loadScene(1, 198, 186); hideActor(); playActorAnimation(956); - Dialog::showMono(scene, 0x8bc4, 957, 0xd1, 1); + dialog->showMono(212, scene, 957, textColorMark, 1); waitAnimation(); loadScene(15, 157, 199, 1); playMusic(6); - return true; + break; - case 0x4c3e: //get the grenade + case 0x4c3e: // get the grenade playSound(32, 24); playActorAnimation(862); reloadLan(); playAnimation(863, 1); - inventory->add(0x54); + inventory->add(kInvItemGrenade); disableObject(1); - SET_FLAG(0xDBE2, 2); - return true; + SET_FLAG(dsAddr_act1GuardState, 2); + break; case 0x4c70: - if (CHECK_FLAG(0xDBE2, 0)) { - if (CHECK_FLAG(0xDBDA, 1)) { //papers are shown - Dialog::pop(scene, 0xDB4C, 0, 809, 0xd1, 0xd0, 0, 1); + if (CHECK_FLAG(dsAddr_act1GuardState, 0)) { + if (CHECK_FLAG(dsAddr_ShownPassToGuardFlag, 1)) { // papers are shown + dialog->pop(scene, dsAddr_dialogStackCampGuardReadingNews, 0, 809, textColorMark, textColorCampGuard, 0, 1); } else { - Dialog::pop(scene, 0xDB40, 0, 809, 0xd1, 0xd0, 0, 1); + dialog->pop(scene, dsAddr_dialogStackCampGuardWantsDocuments, 0, 809, textColorMark, textColorCampGuard, 0, 1); } } else { - displayMessage(0x5722); + displayMessage(dsAddr_helloQMsg); // "Hello?" wait(100); - displayMessage(0x572a); + displayMessage(dsAddr_totallyAddictedMsg); // "He's totally addicted" } - return true; + break; case 0x4c1c: playActorAnimation(960); - displayMessage(0x5511); - return true; + displayMessage(dsAddr_lockedMsg); // "It's Locked!" + break; + + case 0x4ca5: + displayMessage(dsAddr_chickenNeverMsg); // "Chickening? Me? Never!" + break; case 0x4cac: - if (CHECK_FLAG(0xdbda, 1)) { //papers are shown + if (CHECK_FLAG(dsAddr_ShownPassToGuardFlag, 1)) { // papers are shown loadScene(5, 124, 199); } else { - Dialog::show(scene, 0x5FE9, 0, 809, 0xd1, 0xd0, 0, 1); + dialog->show(144, scene, 0, 809, textColorMark, textColorCampGuard, 0, 1); moveTo(269, 175, 4); - Dialog::pop(scene, 0xDB56, 0, 809, 0xd1, 0xd0, 0, 1); + dialog->pop(scene, dsAddr_dialogStackCampGuardShowPass, 0, 809, textColorMark, textColorCampGuard, 0, 1); } - return true; - - case 0x4cf1: { //talking with mansion guard - SET_FLAG(0xda96, 1); - if (Dialog::pop(scene, 0xdaa6, 0, 529, 0xd1, 0xd9, 0, 1) != 0x1b4) - return true; + break; - Common::Point p = scene->getPosition(); - moveTo(189, 159, 0); - //waitLanAnimationFrame(1, 1); + case 0x4cf1: // talking with mansion guard + SET_FLAG(dsAddr_spokenWithMansionGuardFlag, 1); + if (dialog->pop(scene, dsAddr_dialogStackPleadingToMansionGuard, 0, 529, textColorMark, textColorMansionGuard, 0, 1) == 0x01b4) { // 2nd try + Common::Point p = scene->getPosition(); + moveTo(189, 159, 0); + //waitLanAnimationFrame(1, 1); - playSound(5, 2); - playSound(5, 19); - playActorAnimation(550, true); - playAnimation(551, 0, true); - waitAnimation(); + playSound(5, 2); + playSound(5, 19); + playActorAnimation(550, true); + playAnimation(551, 0, true); + waitAnimation(); - moveTo(p, 2); - inventory->add(0x13); - Dialog::pop(scene, 0xdaa6, 0, 529, 0xd1, 0xd9, 0, 1); - } - return true; + moveTo(p, 2); + inventory->add(kInvItemChocCandy); + dialog->pop(scene, dsAddr_dialogStackPleadingToMansionGuard, 0, 529, textColorMark, textColorMansionGuard, 0, 1); + } + break; - case 0x4d94: //talking with fatso - Dialog::show(scene, 0x33bd, 0, 666, 0xd1, 0xd0, 0, 2); - displayAsyncMessage(0x49ae, /*25060*/ 35000, 1, 10, 0xd0); + case 0x4d94: // talking with fatso + dialog->show(87, scene, 0, 666, textColorMark, textColorJohnNoty, 0, 2); + displayAsyncMessage(dsAddr_BribeMsg, 120, 109, 1, 10, textColorJohnNoty); // FIXME: Original (x,y) was (100, 78), rather than (120, 109)? playSound(5, 3); playAnimation(667, 1); playAnimation(668, 1); setOns(2, 50); - Dialog::show(scene, 0x36c7, 0, 666, 0xd1, 0xd0, 0, 2); + dialog->show(88, scene, 0, 666, textColorMark, textColorJohnNoty, 0, 2); setOns(3, 0); - setFlag(0xDBEC, 0); + setFlag(dsAddr_johnNotyOutsideMansionDoorFlag, 0); reloadLan(); playSound(82, 19); playAnimation(669, 1); - Dialog::showMark(scene, 0x3779); + dialog->showMark(89, scene); enableObject(15); disableObject(8); - return true; + break; case 0x4e61: loadScene(14, 280, 198); - return true; + break; case 0x4ee5: setOns(2, 0); playSound(5, 12); playActorAnimation(676); - displayMessage(0x4ab0); + displayMessage(dsAddr_WimpMsg); // "I'm a pathetic little wimp" disableObject(15); - inventory->add(51); - return true; + inventory->add(kInvItemBanknote); + break; case 0x4d56: - inventory->add(16); + inventory->add(kInvItemWhisky); disableObject(2); setOns(0, 0); playSound(5, 12); playActorAnimation(547); - return true; + break; + case 0x4d85: + rejectMessage(); + break; - case 0x4eb9://Pick up wrapper + case 0x4eb9: // Pick up wrapper playSound(5, 12); playSound(5, 18); - inventory->add(0x12); + inventory->add(kInvItemWrapper); setOns(1, 0); playActorAnimation(549); disableObject(13); - return true; + break; + + case 0x4ee1: + rejectMessage(); + break; case 0x4f25: playActorAnimation(967); - displayMessage(0x3542); - return true; + displayMessage(dsAddr_tooHardWoodMsg); // "This wood is too hard to break" + break; - case 0x4f32: //use tree near the mansion - if (CHECK_FLAG(0xDBA1, 1)) { - if (CHECK_FLAG(0xDBA2, 1)) { - displayMessage(0x3766); + case 0x4f32: // use tree near the mansion + if (CHECK_FLAG(dsAddr_mansionTreeHollowEmptyFlag, 1)) { + if (CHECK_FLAG(dsAddr_climbedMansionTreeAlreadyFlag, 1)) { + displayMessage(dsAddr_noChanceMsg); // "I won't take my chances a second time" } else { playSound(26, 13); playSound(26, 15); @@ -739,95 +1260,126 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playSound(60, 16); playActorAnimation(591); wait(50); - displayMessage(0x372e); - SET_FLAG(0xDBA2, 1); - processCallback(0x9d45); + displayMessage(dsAddr_oneSmallStepMsg); // "One small step for man, one big pain in the head" + SET_FLAG(dsAddr_climbedMansionTreeAlreadyFlag, 1); + fnMansionIntrusionAttempt(); } } else { playActorAnimation(49); playSound(56, 8); playSound(56, 12); playSound(49, 10); - displayAsyncMessage(0x4652, 31579, 16, 24); + displayAsyncMessage(dsAddr_laughterMsg, 219, 98, 16, 24); // "(laughter)" playActorAnimation(587); moveRel(0, 0, 2); wait(100); - displayMessage(0x3668); + displayMessage(dsAddr_tickledMsg); // "Something tickled me!" } - return true; + break; - case 0x500d: //picking up wild plant - if (CHECK_FLAG(0xDB9E, 1)) { - displayMessage(0x35E8); //there are no more + case 0x500d: // picking up wild plant + if (CHECK_FLAG(dsAddr_gotPotatoAlreadyFlag, 1)) { + displayMessage(dsAddr_noPotatoMsg); // "There are no more potatoes" } else { - SET_FLAG(0xDB9E, 1); + SET_FLAG(dsAddr_gotPotatoAlreadyFlag, 1); setOns(2, 0); playSound(21, 9); playSound(34, 21); playSound(26, 30); playActorAnimation(552); setOns(2, 0x12); - inventory->add(0x14); + inventory->add(kInvItemPotato); } - return true; + break; + + case 0x505f: + displayMessage(dsAddr_wallTooSmoothMsg); // "The wall surface is too smooth to climb" + break; + + case 0x5066: + loadScene(11, Common::Point(183, 109)); + scene->setOrientation(3); + break; + + case 0x5080: + loadScene(13, Common::Point(290, 181)); + scene->setOrientation(4); + break; + + case 0x50f6: + displayMessage(dsAddr_tooMuchResinToClimbMsg); // "I could climb it if there wasn't so much resin" + break; + + case 0x50fd: + displayMessage(dsAddr_onlyGreenRectMsg); // "The only green stuff that I like is that rectangular piece of paper with..." + break; case 0x5104: loadScene(11, 319, 198, 4); - if (CHECK_FLAG(0xDB9C, 1)) - return true; - - //guard's drinking - SET_FLAG(0, 3); - setTimerCallback(0x516d, 40); - playAnimation(544, 0, true, true); //ignore busy flag for this animation - return true; + if (!CHECK_FLAG(dsAddr_scaredGuardAlreadyFlag, 1)) { + // guard is drinking + SET_FLAG(dsAddr_timedCallbackState, 3); + setTimerCallback(csAddr_guardScareTimeout, 40); + playAnimation(544, 0, true, true); // ignore busy flag for this animation + } + break; - case 0x516d: //too late to scare guard, resetting - SET_FLAG(0, 0); - return true; + case csAddr_guardScareTimeout: // too late to scare guard, resetting + SET_FLAG(dsAddr_timedCallbackState, 0); + break; - case 0x5189: //guard's drinking, boo! - SET_FLAG(0, 0); - setTimerCallback(0, 0); - scene->getAnimation(0)->free(); - SET_FLAG(0xDB9C, 1); + case csAddr_guardDrinking: + fnGuardDrinking(); + break; - displayAsyncMessage(0x3563, 320 * 130 + 300, 1, 5); - setOns(0, 16); - enableObject(2); + case 0x51c8: + displayMessage(dsAddr_wallTooSmoothMsg); // "The wall surface is too smooth to climb" + break; - playSound(17, 5); - playAnimation(545, 0); + case 0x51cf: + loadScene(12, Common::Point(15, 189)); + scene->setOrientation(2); + break; - Dialog::show(scene, 0x0917, 0, 546, 0xd1, 0xd9, 0, 1); - SET_FLAG(0xDA96, 1); - SET_FLAG(0xDA97, 0); - return true; + case 0x51e9: + displayMessage(dsAddr_dontWannaTouchHedgehogMsg); // "I don't wanna touch it. Its spines could hurt my delicate hands" + break; case 0x51f0: setOns(0, 0); playSound(5, 11); playActorAnimation(637); disableObject(7); - inventory->add(49); - return true; + inventory->add(kInvItemRock); + break; case 0x5217: - displayMessage(CHECK_FLAG(0xDB9F, 1) ? 0x402e : 0x34e1); - return true; + if (CHECK_FLAG(dsAddr_beesGoneFlag, 1)) + displayMessage(dsAddr_notHungryMsg); // "Thanks, I'm not hungry" + else + displayMessage(dsAddr_avoidBeesMsg); // "I'm going to stay at least five meters away from these bees!" + break; + + case 0x522c: + displayMessage(dsAddr_avoidBeesMsg); // "I'm going to stay at least five meters away from these bees!" + break; + + case 0x5233: + rejectMessage(); + break; case 0x5237: - if (!CHECK_FLAG(0xDB9F, 1)) { - displayMessage(0x34e1); - } else if (CHECK_FLAG(0xDBA0, 1)) - displayMessage(0x3E31); + if (!CHECK_FLAG(dsAddr_beesGoneFlag, 1)) { + displayMessage(dsAddr_avoidBeesMsg); // "I'm going to stay at least five meters away from these bees!" + } else if (CHECK_FLAG(dsAddr_mansionTunnelDoneFlag, 1)) + displayMessage(dsAddr_roadNowhereMsg); // "Nah. It's a road to nowhere" else { moveTo(173, 138, 2); playSound(28, 5); playActorAnimation(583); playActorAnimation(584); - loadScene(0, 0, 0, 0); //clear background + loadScene(0, 0, 0, 0); // clear background playSound(72, 18); playSound(73, 39); @@ -837,82 +1389,257 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playSound(28, 2); playActorAnimation(586); moveTo(138, 163, 3); - displayMessage(0x3650); - SET_FLAG(0xDBA0, 1); - processCallback(0x9d45); //another mansion try + displayMessage(dsAddr_lifeBrutalMsg); // "Life is really brutal" + SET_FLAG(dsAddr_mansionTunnelDoneFlag, 1); + fnMansionIntrusionAttempt(); } - return true; - - case 0x55a8: { - uint16 d = Dialog::pop(scene, 0xdb08, 0, 0, 0xd1, 0xd1, 0, 0); - if (d == 0x2c5d) { - waitLanAnimationFrame(1, 0x23); - setOns(0, 0); - playSound(52, 9); - playSound(52, 11); - playSound(52, 13); - playSound(53, 32); - playAnimation(570, 0); - wait(50); - displayMessage(0x551f); - disableObject(5); - SET_FLAG(0xDBB0, 1); - } else if (d != 0x2c9b) { - waitLanAnimationFrame(1, 0x23); - playSound(52, 9); - playSound(52, 11); - playSound(52, 13); - playAnimation(569, 0); + break; + + case 0x5320: + loadScene(11, Common::Point(30, 124)); + scene->setOrientation(2); + break; + + case 0x533a: + displayMessage(dsAddr_noLongHandsMsg); // "I really don't have such long hands" + break; + + case 0x5341: + displayMessage(dsAddr_tooFarToSwimMsg); // "It's too far to swim there" + break; + + case 0x5403: + displayMessage(dsAddr_noBucketMsg); // "It's not a barrel-organ. And there's no bucket." + break; + + case 0x540a: + loadScene(20, Common::Point(10, 185)); + scene->setOrientation(2); + break; + + case 0x5424: + loadScene(11, Common::Point(30, 170)); + scene->setOrientation(2); + break; + + case 0x543e: + loadScene(18, Common::Point(224, 199)); + scene->setOrientation(4); + break; + + case 0x5547: + loadScene(15, Common::Point(15, 172)); + scene->setOrientation(2); + break; + + case 0x55a8: + { + uint16 d = dialog->popMark(scene, dsAddr_dialogStackSquirrel); + if (d == 0x2c5d) { // 4th try - Throw Nut + waitLanAnimationFrame(1, 0x23); + setOns(0, 0); + playSound(52, 9); + playSound(52, 11); + playSound(52, 13); + playSound(53, 32); + playAnimation(570, 0); + wait(50); + displayMessage(dsAddr_ThanksMsg); // "Thanks." + disableObject(5); + SET_FLAG(dsAddr_squirrelNutState, 1); + } else if (d != 0x2c9b) { // 5th (last) try + waitLanAnimationFrame(1, 0x23); + playSound(52, 9); + playSound(52, 11); + playSound(52, 13); + playAnimation(569, 0); + } } - } - return true; + break; case 0x5663: - displayMessage(CHECK_FLAG(0xDBB0, 1) ? 0x41b1 : 0x417e); - return true; + if (CHECK_FLAG(dsAddr_squirrelNutState, 1)) + displayMessage(dsAddr_findNutMsg); // "I won't find the nut just like that. The grass is too dense" + else + displayMessage(dsAddr_hmmGrassMsg); // "Hmmm. Grass..." + break; + + case 0x5674: + loadScene(18, Common::Point(94, 115)); + scene->setOrientation(3); + break; + + case 0x568e: + displayMessage(dsAddr_notHornyMsg); // "I'm not horny" + break; + + case 0x5695: + displayMessage(dsAddr_dontNeedToOpenMsg); // "I don't need to open it" + break; case 0x569c: playSound(67, 5); playActorAnimation(983); - displayMessage(0x5955); - return true; + displayMessage(dsAddr_emptyMsg); // "It's Empty" + break; + + case 0x56b3: + rejectMessage(); + break; case 0x56b7: playSound(66, 5); playSound(67, 11); playActorAnimation(984); - displayMessage(0x5955); - return true; + displayMessage(dsAddr_emptyMsg); // "It's Empty" + break; + + case 0x56d6: + displayMessage(dsAddr_CantJumpMsg); // "No way I can jump so high, cause, err, white men can't jump" + break; + + case 0x56dd: + displayMessage(dsAddr_dontNeedItMsg); // "I don't need it" + break; + + case 0x56e4: + displayMessage(dsAddr_notSantaClausMsg); // "I'm not Santa Claus" + break; + + case 0x56eb: + displayMessage(dsAddr_noPlasticImitationsMsg); // "I don't need plastic imitations" + break; + + case 0x56f2: + rejectMessage(); + break; + + case 0x5721: + displayMessage(dsAddr_dontNeedItMsg); // "I don't need it" + break; case 0x5728: - inventory->add(0x0d); + inventory->add(kInvItemChainsaw); disableObject(14); setOns(0, 0); playSound(5, 10); playActorAnimation(566); - return true; + break; + + case 0x574f: + displayMessage(dsAddr_tooFragileMsg); // "It's too fragile to carry around" + break; case 0x5793: - if (!CHECK_FLAG(0xDB94, 1)) { - displayMessage(0x3e63); - } else if (CHECK_FLAG(0xDB95, 1)) { - displayMessage(0x3e75); + if (!CHECK_FLAG(dsAddr_alreadyPulledTrunkReleaseLeverFlag, 1)) { + displayMessage(dsAddr_shutTightMsg); // "It's shut tight" + } else if (CHECK_FLAG(dsAddr_carTrunkEmptyFlag, 1)) { + displayMessage(dsAddr_bootEmptyMsg); // "There's nothing else in the boot" } else { - SET_FLAG(0xDB95, 1); + SET_FLAG(dsAddr_carTrunkEmptyFlag, 1); moveTo(188, 179, 0); playSound(7, 16); playActorAnimation(519); wait(150); moveTo(168, 179, 2); - inventory->add(3); + inventory->add(kInvItemToolboxFull); } - return true; + break; + + case 0x57fa: + displayMessage(dsAddr_dontNeedItMsg); // "I don't need it" + break; + + case 0x5801: + rejectMessage(); + break; + + case 0x583f: + case 0x5846: + displayMessage(dsAddr_dontNeedToOpenMsg); + break; + + case 0x584d: + displayMessage(dsAddr_pullObjMsg2); + break; + + case 0x5854: + loadScene(15, Common::Point(157, 199)); + scene->setOrientation(1); + break; + + case 0x586e: + loadScene(21, Common::Point(24, 187)); + scene->setOrientation(2); + break; + + case 0x5888: + loadScene(27, Common::Point(108, 199)); + scene->setOrientation(2); + break; + + case 0x5903: + displayMessage(dsAddr_keepItOpenMsg); // "I'd like to keep it open" + break; + + case 0x590a: + loadScene(20, Common::Point(304, 190)); + scene->setOrientation(4); + break; + + case 0x5924: + loadScene(25, Common::Point(298, 146)); + scene->setOrientation(4); + break; + + case 0x5978: + displayMessage(dsAddr_notTakingSocksMsg); // "I really don't want to walk around with someone else's socks" + break; + + case 0x597f: + case 0x5986: + case 0x598d: + displayMessage(dsAddr_dontNeedToOpenMsg); // "I don't need to open it" + break; + + case 0x5b44: + // FIXME - This is the doorbell use callback on House #2 + // i.e. Granny and Anne's House. Need to analyse cseg data properly. + // Current code inferred from behaviour. + // FIXME - Add animation call for Ego pushing doorbell. + displayMessage(dsAddr_ItsOpenMsg); + break; + + case 0x5c72: + displayMessage(dsAddr_notTiredMsg); // "Thanks, I'm not tired" + break; + + case 0x5c79: + displayMessage(dsAddr_dontNeedToOpenMsg); // "I don't need to open it" + break; + + case 0x5c80: + rejectMessage(); + break; + + case 0x5cdb: + case 0x5ce2: + displayMessage(dsAddr_dontNeedItMsg); // "I don't need it" + break; + + case 0x5ce9: + displayMessage(dsAddr_tooBigMsg); // "It's too big and I doubt if I'll ever need it" + break; + + case 0x5d1d: + displayMessage(dsAddr_CantJumpMsg); // "No way I can jump so high, cause, err, white men can't jump" + break; case 0x5d88: - if (CHECK_FLAG(0xDBA5, 1)) { //dry laundry - SET_FLAG(0xDBA5, 2); - Dialog::show(scene, 0x1F4F, 0, 523, 0xd1, 0xe5, 0, 1); - //waitLanAnimationFrame(1, 1); //another long waiting + if (CHECK_FLAG(dsAddr_laundryState, 1)) { // dry laundry + SET_FLAG(dsAddr_laundryState, 2); + dialog->show(46, scene, 0, 523, textColorMark, textColorOldLady, 0, 1); + //waitLanAnimationFrame(1, 1); // another long waiting playAnimation(604, 0); loadScene(21, scene->getPosition()); @@ -924,184 +1651,393 @@ bool TeenAgentEngine::processCallback(uint16 addr) { setOns(0, 33); loadScene(23, scene->getPosition()); playAnimation(605, 0); - Dialog::show(scene, 0x2002, 0, 523, 0xd1, 0xe5, 0, 1); + dialog->show(47, scene, 0, 523, textColorMark, textColorOldLady, 0, 1); } else { - uint16 d = Dialog::pop(scene, 0xdada, 0, 523, 0xd1, 0xe5, 0, 1); - if (d == 0x1913) { + uint16 d = dialog->pop(scene, dsAddr_dialogStackAskOldLadyOK, 0, 523, textColorMark, textColorOldLady, 0, 1); + if (d == 0x1913) { // 3rd time wait(100); moveRel(0, 0, 3); wait(50); - displayMessage(0x34d5); //I give up + displayMessage(dsAddr_giveUpMsg); // "I give up" wait(50); } } - return true; + break; + + case 0x5f9a: + case 0x5fa1: + displayMessage(dsAddr_dontNeedToOpenMsg); // "I don't need to open it" + break; - case 0x5ff3: //get duster - if (CHECK_FLAG(0xDB9A, 0)) { - Dialog::pop(scene, 0xdaf6, 0, 523, 0xd1, 0xe5, 0, 1); + case 0x5fa8: + displayMessage(dsAddr_CantJumpMsg); // "No way I can jump so high, cause, err, white men can't jump" + break; + + case 0x5faf: + displayMessage(dsAddr_noSecretPassageMsg); // "I don't think there's any secret passage inside" + break; + + case 0x5fe5: + displayMessage(dsAddr_jugMeMsg); // "They can jug me if I steal this" + break; + + case 0x5fec: + displayMessage(dsAddr_leaveFlowersAloneMsg); // "I'd better leave it. Women are really oversensitive about flowers." + break; + + case 0x5ff3: // get duster + if (CHECK_FLAG(dsAddr_givenFlowerToOldLadyAlreadyFlag, 0)) { + dialog->pop(scene, dsAddr_dialogStackBorrowDusterFromOldLady, 0, 523, textColorMark, textColorOldLady, 0, 1); } else { - Dialog::show(scene, 0x1e1e, 0, 523, 0xd1, 0xe5, 0, 1); + dialog->show(43, scene, 0, 523, textColorMark, textColorOldLady, 0, 1); wait(50); - inventory->add(12); + inventory->add(kInvItemFeatherDusterClean); disableObject(12); setOns(0, 0); playSound(5, 6); playActorAnimation(541); } - return true; + break; + + case 0x603a: + rejectMessage(); + break; case 0x603e: - if (CHECK_FLAG(0xDBB3, 1)) { - displayMessage(0x44a7); + if (CHECK_FLAG(dsAddr_spokenToMirrorFlag, 1)) { + displayMessage(dsAddr_busyThinkingMsg); // "I'd better not interrupt it's thought process" } else { - displayMessage(0x4412); + displayMessage(dsAddr_mirrorMirrorMsg); // "Mirror, Mirror on the wall...." wait(150); - displayMessage(0x444f); + displayMessage(dsAddr_thinkTooLongMsg); // "Hey, don't think too long" wait(150); - displayMessage(0x446b); + displayMessage(dsAddr_HintMaleMsg); // "A hint: Someone in this room, a male" wait(150); - displayMessage(0x4492); + displayMessage(dsAddr_okWaitMsg); // "OK, take your time" wait(150); - SET_FLAG(0xDBB3, 1); + SET_FLAG(dsAddr_spokenToMirrorFlag, 1); } - return true; + break; + + case 0x6074: + rejectMessage(); + break; + + case 0x6078: + displayMessage(dsAddr_tooBigMsg); // "It's too big and I doubt if I'll ever need it" + break; case 0x6205: - if (CHECK_FLAG(0xDBA4, 1)) - displayMessage(0x450e); + if (CHECK_FLAG(dsAddr_lightOnFlag, 1)) + displayMessage(dsAddr_tooHeavyMsg); // "It's too heavy. Not that I'm wimp" else - processCallback(0x61fe); - return true; + fnTooDark(); + break; case 0x6217: - if (CHECK_FLAG(0xDBA4, 1)) - displayMessage(0x44d6); + if (CHECK_FLAG(dsAddr_lightOnFlag, 1)) + displayMessage(dsAddr_noDentistsMsg); // "I don't want to have anything in common with dentists" else - processCallback(0x61fe); - return true; + fnTooDark(); + break; case 0x62c1: - if (CHECK_FLAG(0xDBA4, 1)) - return false; + if (CHECK_FLAG(dsAddr_lightOnFlag, 1)) + retVal = false; + else + fnTooDark(); + break; - processCallback(0x61fe); - return true; + case 0x634a: + displayMessage(dsAddr_noHandsSharpThornsMsg); // "I can't remove it with my hands. these thorns look really sharp" + break; + + case 0x637f: + loadScene(21, Common::Point(201, 199)); + scene->setOrientation(1); + break; + + case 0x6399: + displayMessage(dsAddr_rockWalkingGeeMsg); // "Yeah, great idea. Let's take this rock and walk around a bit. Gee..." + break; + + case 0x63a0: + case 0x63a7: + displayMessage(dsAddr_butterflyMsg); // "I'd better leave them alone, they make this place beautiful" + break; + + case 0x63ae: + displayMessage(dsAddr_notSureIfAliveMsg); // "I'm not sure if it's alive" + break; case 0x63bc: playMusic(6); loadScene(25, 151, 156, 2); - return true; + break; case 0x63dc: - Dialog::showMono(scene, 0x3375, 0, 0xd1, 0); - return true; + dialog->showMono(86, scene, 0, textColorMark, 0); + break; + + case 0x63e3: + displayMessage(dsAddr_holeTooNarrowMsg); // "The hole is too narrow to fit my hand" + break; case 0x646e: case 0x6475: - Dialog::showMono(scene, 0x32C1, 0, 0xd1, 0); - return true; + dialog->showMono(85, scene, 0, textColorMark, 0); + break; case 0x6479: - Dialog::showMono(scene, 0x325e, 0, 0xd1, 0); - return true; + dialog->showMono(84, scene, 0, textColorMark, 0); + break; case 0x6507: - if (CHECK_FLAG(0xDB96, 1)) { + if (CHECK_FLAG(dsAddr_birdsGoneFromScarecrowFlag, 1)) rejectMessage(); - } else - displayMessage(0x47e7); - return true; + else + displayMessage(dsAddr_birdAttackMsg); // "Hey You! Wake up! Bird attack!" + break; + + case 0x6541: + loadScene(20, Common::Point(10, 131)); + scene->setOrientation(3); + break; + + case 0x6635: + displayMessage(dsAddr_uninterestingHaystackMsg); // "I don't see anything interesting about this haystack" + break; + + case 0x666a: + displayMessage(dsAddr_moreComplicatedMsg); // "It's more complicated than that" + break; case 0x65c3: - if (CHECK_FLAG(0xDBA9, 1)) { + if (CHECK_FLAG(dsAddr_mouseHoleState, 1)) { playActorAnimation(635); setOns(5, 0); playSound(63, 11); playSound(15, 20); playSound(32, 31); playActorAnimation(636); - inventory->add(47); - inventory->add(48); + inventory->add(kInvItemHandkerchief); + inventory->add(kInvItemMouse); moveTo(scene->getPosition().x - 1, 139, 1, true); - displayMessage(0x3b83); - SET_FLAG(0xDBA9, 2); - SET_FLAG(0xDBA8, 0); + displayMessage(dsAddr_yikesMsg); // "Yikes!" + SET_FLAG(dsAddr_mouseHoleState, 2); + SET_FLAG(dsAddr_HankerchiefInMouseholeFlag, 0); } else - displayMessage(0x4808); - return true; + displayMessage(dsAddr_noSearchWarrantMsg); // "I don't have a search-warrant" + break; + + case 0x6671: + displayMessage(dsAddr_cantOpenItMsg); // "I can't open it" + break; + + case 0x6678: + rejectMessage(); + break; + + case 0x670f: + displayMessage(dsAddr_dontNeedThemMsg); // "I don't need them" + break; + + case 0x6716: + displayMessage(dsAddr_pullObjMsg2); // "I can't reach it" + break; + + case 0x6772: + loadScene(31, Common::Point(20, 188)); + scene->setOrientation(2); + break; + + case 0x678c: + loadScene(28, Common::Point(189, 153)); + scene->setOrientation(4); + break; + + case 0x67fa: + rejectMessage(); + break; + + case 0x67fe: + displayMessage(dsAddr_troubleWithStairsMsg); // "If I put it on I might have trouble walking up the stairs" + break; + + case 0x6911: + displayMessage(dsAddr_9LivesToReadMsg); // "I'd need 9 lives to read them all" + break; + + case 0x6954: + displayMessage(dsAddr_thanksNotTiredMsg); // "Thanks, I'm not so tired" + break; + + case 0x695b: + displayMessage(dsAddr_noNeedToTurnOnMsg); // "There's no need to turn it on" + break; + + case 0x6ba6: + displayMessage(dsAddr_wontBearWeightMsg); // "It won't bear my weight" + break; + + case 0x6bda: + displayMessage(dsAddr_peepingTomMsg); // "What am I? A Peeping Tom?" + break; + + case 0x6c1c: + case 0x6c20: + rejectMessage(); + break; + + case 0x6c24: + displayMessage(dsAddr_dontNeedThemMsg); // "I don't need them" + break; + + case 0x6c2b: + loadScene(29, Common::Point(300, 188)); + scene->setOrientation(4); + break; + + case 0x6c7c: + displayMessage(dsAddr_bigPocketsMsg); // "I have big pockets, but there are limits" + break; + + case 0x724e: + displayMessage(dsAddr_soSharpMsg); // "They're so sharp they'd rip my trousers!" + break; + + case 0x72be: + rejectMessage(); + break; + + case 0x7305: + rejectMessage(); + break; + + case 0x7328: + displayMessage(dsAddr_noTimeForPleasuresMsg); // "I don't have time for pleasures" + break; + + case 0x732f: + displayMessage(dsAddr_notSocksWithBareHandsMsg); // "I won't touch these socks with my bare hands!" + break; + + case 0x739c: + displayMessage(dsAddr_notHalloweenMsg); // "It's not Halloween" + break; + + case 0x7401: + displayMessage(dsAddr_NotManualMsg); // "It can't be controlled manually! I hate it!" + break; + + case 0x746f: + displayMessage(dsAddr_nothingToPlayMsg); // "I have nothing to play" + break; + + case 0x74b3: + loadScene(29, Common::Point(256, 171)); + scene->setOrientation(3); + break; + + case 0x74cd: + rejectMessage(); + break; + + case 0x74f9: + loadScene(38, Common::Point(160, 199)); + scene->setOrientation(1); + break; + + case 0x784a: + displayMessage(dsAddr_notMineMsg); // "I can't take it. It's not mine." + break; + + case 0x7851: + displayMessage(dsAddr_lockedMsg); // "It's Locked!" + break; + + case 0x7858: + displayMessage(dsAddr_lockedMsg); // "It's Locked!" + break; + + case 0x785f: + displayMessage(dsAddr_pullObjMsg2); // "I can't reach it" + break; case 0x7866: - if (CHECK_FLAG(0xdbdd, 3)) { - displayMessage(0x55ff); - return true; - } else - return false; + if (CHECK_FLAG(dsAddr_JailCableAndBowlState, 3)) + displayMessage(dsAddr_gotchaMsg); // "Gotcha" + else + retVal = false; + break; - case 0x7878: { - byte v = res->dseg.get_byte(0xDBDB) + 1; - if (v <= 6) - SET_FLAG(0xDBDB, v); + case 0x7878: + { + byte v = res->dseg.get_byte(dsAddr_graffitiMsgId) + 1; + if (v <= 6) + SET_FLAG(dsAddr_graffitiMsgId, v); - switch (v) { - case 1: - displayMessage(0x5411); - return true; - case 2: - displayMessage(0x5463); - return true; - case 3: - displayMessage(0x5475); - return true; - case 4: - displayMessage(0x5484); - return true; - case 5: - displayMessage(0x54c4); - return true; - default: - displayMessage(0x54d5); - return true; + switch (v) { + case 1: + displayMessage(dsAddr_SavingFineMsg); // "Saving is a very fine thing..." + break; + case 2: + displayMessage(dsAddr_loveCaptainMsg); // "I love captain" + break; + case 3: + displayMessage(dsAddr_soccerRulzMsg); // "Soccer rulz" + break; + case 4: + displayMessage(dsAddr_treeCutMsg); // "Don't cut the trees..." + break; + case 5: + displayMessage(dsAddr_visaAcceptedMsg); // "VISA Accepted" + break; + default: + displayMessage(dsAddr_otherGraffitiMsg); // "The rest of graffiti is obscene" + break; + } } - } + break; case 0x78a9: - if (CHECK_FLAG(0xDBE6, 1)) { - displayMessage(0x5827); - return true; - } else - return false; + if (CHECK_FLAG(dsAddr_captainDrawerState, 1)) + displayMessage(dsAddr_nowOpenMsg); // "Now it's open" + else + retVal = false; + break; case 0x78bb: - if (CHECK_FLAG(0xDBE8, 1)) { - displayMessage(0x58b0); - return true; - } else - return false; + if (CHECK_FLAG(dsAddr_swappedBarmanMugFlag, 1)) + displayMessage(dsAddr_yuckMsg); // "Yuck!" + else + retVal = false; + break; case 0x78ce: - if (!CHECK_FLAG(0xDBA1, 1)) { - displayMessage(0x3694); - return true; - } else - return false; + if (!CHECK_FLAG(dsAddr_mansionTreeHollowEmptyFlag, 1)) + displayMessage(dsAddr_monstersMsg); // "Who knows what monsters live in there" + else + retVal = false; + break; - case 0x792b: //left click on ann + case 0x792b: // left click on ann moveTo(245, 198, 1); - if (CHECK_FLAG(0xDBAF, 1)) - return false; - - Dialog::showMono(scene, 0x2193, 0, 0xd1, 0); - SET_FLAG(0xDBAF, 1); - return true; + if (!CHECK_FLAG(dsAddr_alreadySaidAnneBeautifulFlag, 1)) { + dialog->showMono(50, scene, 0, textColorMark, 0); + SET_FLAG(dsAddr_alreadySaidAnneBeautifulFlag, 1); + } else + retVal = false; + break; case 0x79c3: - if (CHECK_FLAG(0xDBA4, 1)) - return false; - processCallback(0x61fe); - return true; + if (CHECK_FLAG(dsAddr_lightOnFlag, 1)) + retVal = false; + else + fnTooDark(); + break; - case 0x7b26: //cutting the fence + case 0x7b26: // cutting the fence setOns(0, 0); playSound(5, 2); playSound(51, 11); @@ -1114,11 +2050,11 @@ bool TeenAgentEngine::processCallback(uint16 addr) { setOns(0, 0x60); moveTo(281, scene->getPosition().y, 0, true); disableObject(4); - SET_FLAG(0xDBE1, 1); - return true; + SET_FLAG(dsAddr_cutFenceFlag, 1); + break; - case 0x7b89: //digging mysterious object - if (CHECK_FLAG(0xDBE1, 1)) { + case 0x7b89: // digging mysterious object + if (CHECK_FLAG(dsAddr_cutFenceFlag, 1)) { playActorAnimation(844); setOns(1, 0); playSound(5, 5); @@ -1130,11 +2066,15 @@ bool TeenAgentEngine::processCallback(uint16 addr) { setOns(1, 0x64); playActorAnimation(845); disableObject(3); - inventory->add(0x52); - inventory->remove(0x51); + inventory->add(kInvItemKaleidoscope); + inventory->remove(kInvItemShovelAct1); } else - displayMessage(0x56da); - return true; + displayMessage(dsAddr_fenceBlocksMsg); // "The fence blocks the way" + break; + + case 0x7bf6: + displayMessage(dsAddr_noDiggingKnifeMsg); // "Digging it out with the knife could take a hundred years" + break; case 0x7bfd: playSound(76, 18); @@ -1149,7 +2089,7 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playSound(76, 63); playActorAnimation(873); moveTo(240, 163, 4); - displayMessage(0x5837); + displayMessage(dsAddr_cmonBabyMsg); // "C'mon baby, it's all yours!" waitLanAnimationFrame(1, 0x22); playSound(77, 2); playSound(77, 12); @@ -1164,23 +2104,36 @@ bool TeenAgentEngine::processCallback(uint16 addr) { setLan(1, 0); playAnimation(874, 1); setOns(0, 0x68); - inventory->remove(0x5b); + inventory->remove(kInvItemDruggedFood); enableObject(6); disableObject(1); - return true; + break; + + case 0x7cc9: + case 0x7cd0: + displayMessage(dsAddr_throwCrumbsToBirdQMsg); // "Should I throw the crumbs to the bird?" + break; + + case 0x7cd7: + displayMessage(dsAddr_dontWasteCrumbs); // "I don't want to waste these tasty crumbs" + break; - case 0x7ce5: //put spring on the solid ground + case 0x7cde: + displayMessage(dsAddr_mightSlipFallInMsg); // "Better not... I might slip and fall in..." + break; + + case 0x7ce5: // put spring on the solid ground playSound(5, 2); playSound(19, 11); playActorAnimation(840); setOns(1, 0x61); - inventory->remove(0x50); + inventory->remove(kInvItemSpring); disableObject(2); enableObject(7); - return true; + break; - case 0x7d1a: //captain's key + door - if (res->dseg.get_byte(0xDBDF) <= 1) { + case 0x7d1a: // captain's key + door + if (res->dseg.get_byte(dsAddr_FirstActTrialState) <= 1) { playSound(5, 2); playSound(57, 12); playSound(70, 19); @@ -1198,24 +2151,23 @@ bool TeenAgentEngine::processCallback(uint16 addr) { wait(200); playAnimation(0, 1); setOns(0, 0); - Dialog::showMono(scene, 0x63a5, 830, 0xd0, 1); + dialog->showMono(156, scene, 830, textColorShockedCaptain, 1); loadScene(7, 130, 195, 2); playMusic(4); setLan(1, 1); wait(100); - Dialog::show(scene, 0x6406, 0, 832, 0xd1, 0xec, 0, 1); + dialog->show(157, scene, 0, 832, textColorMark, textColorCaptain, 0, 1); //playAnimation(831, 1); - SET_FLAG(0xDBDF, 2); - + SET_FLAG(dsAddr_FirstActTrialState, 2); } else - displayMessage(0x52f6); - return true; + displayMessage(dsAddr_nahMsg); // "Nah" + break; - case 0x7e02: //tickling the captain - if (CHECK_FLAG(0xdbe0, 1)) { - displayMessage(0x5632); + case 0x7e02: // tickling the captain + if (CHECK_FLAG(dsAddr_AlreadyTickledCaptainFlag, 1)) { + displayMessage(dsAddr_doesNotWorkMsg); // "That doesn't work" } else { playSound(5, 6); playSound(27, 49); @@ -1224,37 +2176,37 @@ bool TeenAgentEngine::processCallback(uint16 addr) { waitAnimation(); setOns(0, 94); - Dialog::show(scene, 0x65e9, 0, 832, 0xd1, 0xec, 0, 1); + dialog->show(161, scene, 0, 832, textColorMark, textColorCaptain, 0, 1); enableObject(12); - SET_FLAG(0xdbe0, 1); + SET_FLAG(dsAddr_AlreadyTickledCaptainFlag, 1); } - return true; + break; - case 0x7e4f: //giving magazine to captain - Dialog::show(scene, 0x66c0, 0, 856, 0xd1, 0xec, 0, 1); + case 0x7e4f: // giving magazine to captain + dialog->show(162, scene, 0, 856, textColorMark, textColorCaptain, 0, 1); playSound(5, 3); playActorAnimation(852, true); playActorAnimation(853, true); - displayMessage(0x5742); - displayMessage(0x5757); - displayMessage(0x5770); - displayMessage(0x5782); - displayMessage(0x5799); + displayMessage(dsAddr_whatAboutMsg); // "What about a new" + displayMessage(dsAddr_hotOffMsg); // "hot off the press" + displayMessage(dsAddr_fullColorMsg); // "full-color" + displayMessage(dsAddr_specialEdMsg); // "special edition" + displayMessage(dsAddr_soldierNewsMsg); // "of Soldier News?!" playAnimation(856, 1); playSound(5, 3); //playActorAnimation(854); - Dialog::show(scene, 0x66fe, 0, 856, 0xd1, 0xec, 0, 1); + dialog->show(163, scene, 0, 856, textColorMark, textColorCaptain, 0, 1); playAnimation(855, 1); wait(200); moveTo(30, 181, 0); disableObject(1); setLan(1, 0); - SET_FLAG(0xDBDF, 3); - SET_FLAG(0xDBF0, 1); + SET_FLAG(dsAddr_FirstActTrialState, 3); + SET_FLAG(dsAddr_gotPasswordNeedSpeakBarmanFlag, 1); loadScene(8, 155, 199); - return true; + break; - case 0x7fbd: //using bird & bartender + case 0x7fbd: // using bird & bartender playSound(5, 3); playActorAnimation(876); setOns(1, 0); @@ -1263,15 +2215,15 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playAnimation(877, 1); playAnimation(880, 1, true); - Dialog::show(scene, 0x6f0e, 0, 857, 0xd1, 0xef, 0, 1); + dialog->show(176, scene, 0, 857, textColorMark, textColorBarman, 0, 1); setOns(2, 0x6a); reloadLan(); playAnimation(878, 0); - //playAnimation(879, 0); //background bartender animation - inventory->remove(0x5c); + //playAnimation(879, 0); // background bartender animation + inventory->remove(kInvItemBird); enableObject(1); - SET_FLAG(0xDBE7, 1); - return true; + SET_FLAG(dsAddr_birdOnBarRadioAntennaFlag, 1); + break; case 0x8047: playSound(32, 5); @@ -1279,27 +2231,26 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playSound(52, 23); playActorAnimation(881); setOns(2, 0x6b); - inventory->remove(0x56); - inventory->add(0x55); - SET_FLAG(0xDBE8, 1); - return true; + inventory->remove(kInvItemMugOfMud); + inventory->add(kInvItemMug); + SET_FLAG(dsAddr_swappedBarmanMugFlag, 1); + break; case 0x808b: - if (CHECK_FLAG(0xDBDA, 1)) { - //alredy shown - displayMessage(0x53F2); + if (CHECK_FLAG(dsAddr_ShownPassToGuardFlag, 1)) { + displayMessage(dsAddr_gotPermissionMsg); // "I already got the permission" } else { - displayMessage(0x53DD); + displayMessage(dsAddr_showPapersMsg); // "Here are my papers" playSound(5, 2); playSound(5, 18); playActorAnimation(810); - Dialog::show(scene, 0x60BF, 0, 809, 0xd1, 0xd0, 0, 1); - SET_FLAG(0xDBDA, 1); + dialog->show(147, scene, 0, 809, textColorMark, textColorCampGuard, 0, 1); + SET_FLAG(dsAddr_ShownPassToGuardFlag, 1); } - return true; + break; - case 0x80c3: //show kaleydoscope to the guard - Dialog::show(scene, 0x6811, 0, 809, 0xd1, 0xd0, 0, 1); + case 0x80c3: // show kaleidoscope to the guard + dialog->show(165, scene, 0, 809, textColorMark, textColorCampGuard, 0, 1); playSound(5, 3); playSound(5, 30); playSound(26, 14); @@ -1309,124 +2260,146 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playAnimation(851, 0); playAnimation(850, 0); reloadLan(); - inventory->add(0x53); - inventory->remove(0x52); + inventory->add(kInvItemSoldierNews); + inventory->remove(kInvItemKaleidoscope); enableObject(1); - SET_FLAG(0xDBE2, 1); - return true; + SET_FLAG(dsAddr_act1GuardState, 1); + break; + + case 0x8398: + displayMessage(dsAddr_trySomewhereElseMsg); // "I'd better try somewhere else - I suppose this side is heavily guarded" + break; + + case 0x85dd: + displayMessage(dsAddr_branchNotPaddleMsg); // "This branch is not a paddle. It doesn't even look like one" + break; + + case 0x85e4: + displayMessage(dsAddr_sharpenNotPulverizeMsg); // "I needed to sharpen it, not pulverize" + break; + + case 0x8d42: + displayMessage(dsAddr_bluntSickleMsg); // "The sickle is too blunt" + break; - //Shore + case 0x8d49: + displayMessage(dsAddr_noChainsawFuelMsg); // "There's no fuel in the chainsaw" + break; + + case 0x8d50: + displayMessage(dsAddr_thornsTooThinMsg); // "Thorns are too thin, the chainsaw is useless here" + break; + + // Shore case 0x5348: - if (CHECK_FLAG(0xdb99, 1)) { //got broken paddle from boat - displayMessage(0x351f); + if (CHECK_FLAG(dsAddr_alreadyGotBrokenPaddleFlag, 1)) { // got broken paddle from boat + displayMessage(dsAddr_boatEmptyMsg); // "There's nothing else in the boat" } else { - SET_FLAG(0xDB99, 1); + SET_FLAG(dsAddr_alreadyGotBrokenPaddleFlag, 1); playSound(57, 6); playActorAnimation(536); - Dialog::showMono(scene, 0x30c3, 0, 0xd1, 0); - inventory->add(0x8); + dialog->showMono(77, scene, 0, textColorMark, 0); + inventory->add(kInvItemBrokenPaddle); } - return true; + break; case 0x53a1: - if (CHECK_FLAG(0xdbb2, 1)) { //spoken to man in well - displayMessage(0x411d); + if (CHECK_FLAG(dsAddr_spokenToManInWellFlag, 1)) { // spoken to man in well + displayMessage(dsAddr_stillThereMsg); // "Are you still there?" } else { - displayMessage(0x408a); - displayMessage(0x4091, 0xe5, 52728); - displayMessage(0x4098); - displayMessage(0x40a7, 0xe5, 52705); - displayMessage(0x40b6); - displayMessage(0x40ce, 0xe5, 52652); - displayMessage(0x40e8); - displayMessage(0x410f, 0xe5, 52712); + displayMessage(dsAddr_echoMsg); // "Echo!" + displayMessage(dsAddr_loudEchoMsg, textColorWellEcho, 248, 164); // "ECHO!" + displayMessage(dsAddr_whoThereMsg); // "Who's there?!" + displayMessage(dsAddr_loudWhoThereMsg, textColorWellEcho, 225, 164); // "WHO'S THERE?!" + displayMessage(dsAddr_dontCopyMsg); // "DON'T COPY ME!" + displayMessage(dsAddr_loudDontCopyMsg, textColorWellEcho, 172, 164); // "DON'T COPY ME!!!" + displayMessage(dsAddr_throwRockMsg); // "OR I WILL THROW A ROCK DOWN THERE!" + displayMessage(dsAddr_orIWillMsg, textColorWellEcho, 232, 164); // "OR I WILL" wait(100); - displayMessage(0x4091, 0xe5, 52728); - SET_FLAG(0xDBB2, 1); + displayMessage(dsAddr_loudEchoMsg, textColorWellEcho, 248, 164); + SET_FLAG(dsAddr_spokenToManInWellFlag, 1); } - return true; + break; + case 0x5458: + { + setOns(2, 0); + playSound(34, 7); + playActorAnimation(535); + inventory->add(kInvItemSecondFlower); + disableObject(1); - case 0x5458: { - setOns(2, 0); - playSound(34, 7); - playActorAnimation(535); - inventory->add(11); - disableObject(1); - - byte *scene_15_ons = scene->getOns(15); //patch ons for the scene 15 - scene_15_ons[0] = 0; + byte *scene_15_ons = scene->getOns(15); // patch ons for the scene 15 + scene_15_ons[0] = 0; - byte f = GET_FLAG(0xDB98) + 1; - SET_FLAG(0xDB98, f); - if (f >= 2) { - //disable object boat for scene 15!! - disableObject(1, 15); + byte f = GET_FLAG(dsAddr_flowerIsleState) + 1; + SET_FLAG(dsAddr_flowerIsleState, f); + if (f >= 2) { + // disable object boat for scene 15!! + disableObject(1, 15); + } } - } - return true; + break; - case 0x54b3: { - setOns(1, 0); - setOns(3, 0); - playSound(33, 6); - playActorAnimation(534); - inventory->add(10); - disableObject(2); - setOns(1, 10); - setOns(1, 0, 15); - byte f = GET_FLAG(0xDB98) + 1; - SET_FLAG(0xDB98, f); - if (f >= 2) { - //disable object boat for scene 15!! - disableObject(1, 15); + case 0x54b3: + { + setOns(1, 0); + setOns(3, 0); + playSound(33, 6); + playActorAnimation(534); + inventory->add(kInvItemFirstFlower); + disableObject(2); + setOns(1, 10); + setOns(1, 0, 15); + byte f = GET_FLAG(dsAddr_flowerIsleState) + 1; + SET_FLAG(dsAddr_flowerIsleState, f); + if (f >= 2) { + // disable object boat for scene 15!! + disableObject(1, 15); + } } - } - return true; + break; case 0x5502: setOns(0, 0); loadScene(15, 115, 180, 1); playMusic(6); playActorAnimation(568); - return true; + break; - case 0x5561://Enter lakeside house - moveTo(94, 115, 4); //call 557e, but it's not needed I guess + case 0x5561: // Enter lakeside house + fnEgoDefaultPosition(); loadScene(19, 223, 199, 1); - return true; + break; case 0x55a1: - processCallback(0x557e); + fnEgoDefaultPosition(); rejectMessage(); - return true; + break; - case 0x557e: - if (scene->getPosition().y <= 149) - moveTo(94, 115, 4); - else - moveTo(51, 149, 4); - return true; + case csAddr_egoDefaultPosition: + fnEgoDefaultPosition(); + break; case 0x563b: playSound(5, 10); setOns(1, 0); playActorAnimation(561); - inventory->add(26); + inventory->add(kInvItemNut); disableObject(6); - return true; + break; case 0x56f6: playSound(32, 7); setOns(1, 0); playActorAnimation(626); disableObject(12); - inventory->add(45); - displayMessage(0x3b04); - return true; + inventory->add(kInvItemCheese); + displayMessage(dsAddr_foundFoodMsg); // "People leave food in unbelievable places" + break; - case 0x5756://Open car door + case 0x5756: // Open car door playSound(11, 4); playActorAnimation(514); setOns(4, 8); @@ -1435,106 +2408,94 @@ bool TeenAgentEngine::processCallback(uint16 addr) { enableObject(15); enableObject(16); disableObject(1); - return true; + break; - case 0x5805://Enter basketball house + case 0x5805: // Enter basketball house playSound(70, 6); playActorAnimation(513); loadScene(22, 51, 180, 2); - return true; + break; - case 0x5832://Ring doorbell + case 0x5832: // Ring doorbell playActorAnimation(509); - displayMessage(0x5dce); - return true; + displayMessage(dsAddr_outOfOrderMsg); // "It's out of order" + break; case 0x58a2: - Dialog::pop(scene, 0xdaba, 0, 502, 0xd1, 0xe5, 0, 1); - scene->getObject(13)->setName((const char *)res->dseg.ptr(0x92e5)); - return true; + dialog->pop(scene, dsAddr_dialogStackSonny, 0, 502, textColorMark, textColorSonny, 0, 1); + scene->getObject(13)->setName((const char *)res->dseg.ptr(dsAddr_scnObjNameSonny)); + break; - case 0x58b7://Get comb from car + case 0x58b7: // Get comb from car disableObject(14); setOns(4, 0); playSound(5, 7); playActorAnimation(521); setOns(4, 0); - inventory->add(0x6); - return true; + inventory->add(kInvItemComb); + break; - case 0x58df://Pull trunk lever in car - SET_FLAG(0xDB94, 1); + case 0x58df: // Pull trunk lever in car + SET_FLAG(dsAddr_alreadyPulledTrunkReleaseLeverFlag, 1); playSound(6, 1); setOns(3, 6); playActorAnimation(515); - return true; + break; - case 0x593e://Enter annes house + case 0x593e: // Enter annes house playSound(89, 4); playActorAnimation(980); loadScene(23, 76, 199, 1); - if (CHECK_FLAG(0xDBEE, 1)) + if (CHECK_FLAG(dsAddr_lovestruckByAnneFlag, 1)) playMusic(7); - return true; + break; case 0x5994: - processCallback(0x599b); - processCallback(0x5a21); - return true; + fnEnterCave(); + break; - case 0x599b: - return true; + case csAddr_caveNOP: + break; - case 0x5a21: - loadScene(24, 230, 170, 1); - playSound(52, 3); - playSound(52, 7); - playSound(52, 11); - playSound(52, 14); - playSound(52, 18); - playSound(52, 21); - playSound(52, 25); - playActorAnimation(601); - moveTo(230, 179, 3); - if (!CHECK_FLAG(0xDBA4, 1)) - displayMessage(0x37ea); //it's kinda dark here - return true; + case csAddr_enterCave: + fnEnterCave(); + break; case 0x5a8b: - if (!CHECK_FLAG(0xDBAD, 1)) { - playSound(43, 4); //grrrrrr + if (!CHECK_FLAG(dsAddr_dogHasBoneFlag, 1)) { + playSound(43, 4); // grrrrrr playSound(42, 15); playSound(42, 17); playSound(42, 19); playAnimation(656, 0); wait(50); - displayMessage(0x3c16); - } else if (!CHECK_FLAG(0xDBA3, 1)) {//Dog has bone + displayMessage(dsAddr_goodDoggyMsg); // "I understand. Good doggy" + } else if (!CHECK_FLAG(dsAddr_cellarDoorOpenFlag, 1)) { // Dog has bone playSound(28, 3); playActorAnimation(596); setOns(1, 30); - SET_FLAG(0xDBA3, 1); + SET_FLAG(dsAddr_cellarDoorOpenFlag, 1); enableObject(8); } else { setOns(1, 0); playSound(4, 4); playActorAnimation(597); - SET_FLAG(0xDBA3, 0); + SET_FLAG(dsAddr_cellarDoorOpenFlag, 0); disableObject(8); - displayMessage(0x37b8); + displayMessage(dsAddr_wallShakenMsg); // "Wow! This must have shaken all the nearby walls!" setOns(1, 32, 24); enableObject(4, 24); } - return true; + break; - case 0x5b3a://Click on dog - Dialog::popMark(scene, 0xDB14); - return true; + case 0x5b3a: // Click on dog + dialog->popMark(scene, dsAddr_dialogStackDog); + break; - case 0x5b59: //picking up the rope - Dialog::showMark(scene, 0x2cbd); + case 0x5b59: // picking up the rope + dialog->showMark(70, scene); wait(150); - Dialog::showMark(scene, 0x2dc2); + dialog->showMark(71, scene); moveRel(0, -12, 0); playSound(34, 5); playActorAnimation(607); @@ -1545,81 +2506,81 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playSound(5, 25); playActorAnimation(611); moveTo(16, scene->getPosition().y, 4, true); - inventory->add(38); + inventory->add(kInvItemRopeAct2); disableObject(12); - return true; + break; - case 0x5be1://Talk to grandpa - Dialog::pop(scene, 0xDAC4, 0, 522, 0xd1, 0xd8, 0, 1); - return true; + case 0x5be1: // Talk to grandpa + dialog->pop(scene, dsAddr_dialogStackGrandpa, 0, 522, textColorMark, textColorGrandpa, 0, 1); + break; case 0x5bee: playSound(89, 5); playSound(67, 11); playActorAnimation(982); - displayMessage(0x5955); - return true; + displayMessage(dsAddr_emptyMsg); // "It's Empty" + break; - case 0x5c0d: //grandpa - drawers - if (CHECK_FLAG(0xDBA7, 1)) { - displayMessage(0x3bac); + case 0x5c0d: // grandpa - drawers + if (CHECK_FLAG(dsAddr_SearchedGrandpaDrawersFlag, 1)) { + displayMessage(dsAddr_drawersEmptyMsg); // "There's nothing else in the drawers" } else { - if (!CHECK_FLAG(0xDB92, 1)) - Dialog::show(scene, 0x15a0, 0, 522, 0xd1, 0xd8, 0, 1); //can I search your drawers? + if (!CHECK_FLAG(dsAddr_alreadyAdjustedHoopPoleFlag, 1)) + dialog->show(24, scene, 0, 522, textColorMark, textColorGrandpa, 0, 1); playSound(66, 5); playSound(67, 20); playSound(5, 23); playActorAnimation(631); - inventory->add(47); - SET_FLAG(0xDBA7, 1); + inventory->add(kInvItemHandkerchief); + SET_FLAG(dsAddr_SearchedGrandpaDrawersFlag, 1); } - return true; + break; case 0x5c84: - if (CHECK_FLAG(0xDB92, 1)) { - inventory->add(2); + if (CHECK_FLAG(dsAddr_alreadyAdjustedHoopPoleFlag, 1)) { + inventory->add(kInvItemShotgun); disableObject(7); playSound(32, 7); setOns(0, 0); playActorAnimation(520); } else { - Dialog::pop(scene, 0xDACE, 0, 522, 0xd1, 0xd8, 0, 1); + dialog->pop(scene, dsAddr_dialogStackGrandpaShotgun, 0, 522, textColorMark, textColorGrandpa, 0, 1); } - return true; + break; - case 0x5cf0://Exit basketball house + case 0x5cf0:// Exit basketball house playSound(88, 5); playActorAnimation(981); loadScene(20, 161, 165); - return true; + break; - case 0x5d24: //getting the fan - if (CHECK_FLAG(0xDB92, 1)) { + case 0x5d24: // getting the fan + if (CHECK_FLAG(dsAddr_alreadyAdjustedHoopPoleFlag, 1)) { setLan(2, 0); playSound(32, 7); playActorAnimation(508); disableObject(13); - inventory->add(7); + inventory->add(kInvItemFan); } else { - Dialog::pop(scene, 0xDAD4, 0, 522, 0xd1, 0xd8, 0, 1); + dialog->pop(scene, dsAddr_dialogStackGrandpaFan, 0, 522, textColorMark, textColorGrandpa, 0, 1); } - return true; + break; - case 0x5e4d: //right click on ann - if (!CHECK_FLAG(0xDB97, 0)) { - displayMessage(0x3d59); + case 0x5e4d: // right click on ann + if (!CHECK_FLAG(dsAddr_alreadySpokenToAnneFlag, 0)) { + displayMessage(dsAddr_girlTalkMsg); // "I really don't know how to talk to girls" } else { moveTo(245, 198, 1); - Dialog::show(scene, 0x21d7, 0, 524, 0xd1, 0xe5, 0, 2); - //waitLanAnimationFrame(2, 1); //too long, about 200 frames! seems to be present in original game (sic) - SET_FLAG(0xDB97, 1); + dialog->show(51, scene, 0, 524, textColorMark, textColorAnne, 0, 2); + //waitLanAnimationFrame(2, 1); // too long, about 200 frames! seems to be present in original game (sic) + SET_FLAG(dsAddr_alreadySpokenToAnneFlag, 1); for (byte i = 10; i <= 20; i += 2) playSound(13, i); playAnimation(528, 1); wait(50); playMusic(7); - SET_FLAG(0xDBEE, 1); + SET_FLAG(dsAddr_lovestruckByAnneFlag, 1); for (byte i = 3; i <= 17; i += 2) playSound(56, i); playActorAnimation(525); @@ -1633,163 +2594,146 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playSound(55, 5); playActorAnimation(527); wait(50); - Dialog::show(scene, 0x2219, 0, 524, 0xd1, 0xe5, 0, 2); - scene->getObject(2)->setName((const char *)res->dseg.ptr(0x9820)); + dialog->show(52, scene, 0, 524, textColorMark, textColorAnne, 0, 2); + scene->getObject(2)->setName((const char *)res->dseg.ptr(dsAddr_scnObjNameAnne)); } - return true; + break; - case 0x5f73: //exiting ann's house - if (CHECK_FLAG(0xDBEE, 1)) + case 0x5f73: // exiting ann's house + if (CHECK_FLAG(dsAddr_lovestruckByAnneFlag, 1)) playMusic(6); loadScene(21, 99, 180, 3); - return true; + break; case 0x5fba: - if (CHECK_FLAG(0xDBB1, 1)) { - displayMessage(0x4380); + if (CHECK_FLAG(dsAddr_nutSwappedForAppleFlag, 1)) { + displayMessage(dsAddr_noFruitMsg); // "There are no more interesting fruits here" } else { - Dialog::pop(scene, 0xDAFC, 0, 523, 0xd1, 0xe5, 0, 1); + dialog->pop(scene, dsAddr_dialogStackGetAppleOldLady, 0, 523, textColorMark, textColorOldLady, 0, 1); } - return true; + break; case 0x607f: - return processCallback(0x60b5); + fnEgoScaredBySpider(); + break; case 0x6083: - if (CHECK_FLAG(0xDBA4, 1)) { + if (CHECK_FLAG(dsAddr_lightOnFlag, 1)) { setOns(0, 0); playSound(56, 10); playActorAnimation(599); - inventory->add(37); + inventory->add(kInvItemShovelAct2); disableObject(2); } else - processCallback(0x60b5); - return true; + fnEgoScaredBySpider(); + break; - case 0x60b5: - if (CHECK_FLAG(0xDBAE, 1)) { - processCallback(0x60d9); - Dialog::showMark(scene, 0x2fdd); - } else { - Dialog::showMark(scene, 0x2e41); - processCallback(0x60d9); - wait(100); - Dialog::showMark(scene, 0x2e6d); - } - return true; + case csAddr_egoScaredBySpider: + fnEgoScaredBySpider(); + break; - case 0x60d9: { - Object *obj = scene->getObject(3); - moveTo(obj); - processCallback(0x612b); - moveTo(48, 190, 3); - } - return true; + case csAddr_moveToLadderAndLeaveCellar: + fnMoveToLadderAndLeaveCellar(); + break; - case 0x612b: - playSound(52, 10); - playSound(52, 14); - playSound(52, 18); - playSound(52, 21); - playSound(52, 25); - playSound(52, 28); - playSound(52, 32); - playActorAnimation(600); - loadScene(21, 297, 178, 3); - return true; + case csAddr_leaveCellar: + fnLeaveCellar(); + break; case 0x6176: - if (CHECK_FLAG(0xDBA4, 1)) { - displayMessage(0x3801); - return true; + if (CHECK_FLAG(dsAddr_lightOnFlag, 1)) { + displayMessage(dsAddr_notInDarkMsg); // "I'm not going to wander here in the dark again" + } else { + playSound(71, 6); + playActorAnimation(598); + loadScene(24, scene->getPosition()); + setOns(2, 0); + setLan(1, 0); + playAnimation(660, 0); + disableObject(1); + SET_FLAG(dsAddr_lightOnFlag, 1); + loadScene(24, scene->getPosition()); } - playSound(71, 6); - playActorAnimation(598); - loadScene(24, scene->getPosition()); - setOns(2, 0); - setLan(1, 0); - playAnimation(660, 0); - disableObject(1); - SET_FLAG(0xDBA4, 1); - loadScene(24, scene->getPosition()); - - return true; + break; case 0x61e9: - if (CHECK_FLAG(0xDBA4, 1)) { - Dialog::popMark(scene, 0xdb1e); - } else - processCallback(0x61fe); + if (CHECK_FLAG(dsAddr_lightOnFlag, 1)) + dialog->popMark(scene, dsAddr_dialogStackTakeAxe); + else + fnTooDark(); + break; - return true; + case csAddr_TooDark: + displayMessage(dsAddr_TooDarkMsg); // "It's too dark to see clearly" + break; - case 0x6229: //shelves in cellar - if (CHECK_FLAG(0xDBA4, 1)) { + case 0x6229: // shelves in cellar + if (CHECK_FLAG(dsAddr_lightOnFlag, 1)) { Common::Point p = scene->getPosition(); - byte v = GET_FLAG(0xDBB4); + byte v = GET_FLAG(dsAddr_cellarShelfExamineCount); switch (v) { case 0: - displayMessage(0x4532); + displayMessage(dsAddr_whatGotMsg); // "Let's look what we've got here" moveRel(-34, 0, 1); - displayMessage(0x4555); + displayMessage(dsAddr_strawberryJamMsg); // "Strawberry jam" moveRel(20, 0, 1); - displayMessage(0x4568); + displayMessage(dsAddr_gooseberryJamMsg); // "Gooseberry jam" moveRel(20, 0, 1); - displayMessage(0x457b); + displayMessage(dsAddr_blackberryJamMsg); // "Blackberry jam" moveRel(20, 0, 1); - displayMessage(0x458e); + displayMessage(dsAddr_bilberryJamMsg); // "Bilberry jam" moveTo(p, 3); - displayMessage(0x459f); - SET_FLAG(0xDBB4, 1); + displayMessage(dsAddr_getMeOutJamMsg); // "Get me out of this jam!" + SET_FLAG(dsAddr_cellarShelfExamineCount, 1); break; case 1: - displayMessage(0x45b8); + displayMessage(dsAddr_rosemaryJamMsg); // "Oh, and there is Rosemary jam" wait(100); - displayMessage(0x45da); - SET_FLAG(0xDBB4, 2); + displayMessage(dsAddr_knowRosemaryMsg); // "I used to know someone called Rosemary" + SET_FLAG(dsAddr_cellarShelfExamineCount, 2); break; default: - displayMessage(0x4603); + displayMessage(dsAddr_unwantedJamsMsg); // "I don't want those jams" + break; } } else - processCallback(0x61fe); - - return true; + fnTooDark(); + break; - case 0x6480: //dive mask - if (CHECK_FLAG(0xDB96, 1)) { + case 0x6480: // dive mask + if (CHECK_FLAG(dsAddr_birdsGoneFromScarecrowFlag, 1)) { playSound(56, 7); playSound(5, 15); playActorAnimation(613); setOns(3, 36); - inventory->add(39); + inventory->add(kInvItemMask); disableObject(5); - displayMessage(0x387c); + displayMessage(dsAddr_needSunglassesMsg); // "Sorry buddy, but I need your sunglasses" } else - displayMessage(0x3eb2); - return true; + displayMessage(dsAddr_crowKillMsg); // "I'm sure these crows will kill me" + break; - case 0x64c4: //flippers - if (CHECK_FLAG(0xDB96, 1)) { + case 0x64c4: // flippers + if (CHECK_FLAG(dsAddr_birdsGoneFromScarecrowFlag, 1)) { setOns(2, 35); playSound(63, 8); playSound(24, 10); playActorAnimation(612); - inventory->add(40); + inventory->add(kInvItemFins); disableObject(6); } else - displayMessage(0x3eb2); - return true; + displayMessage(dsAddr_crowKillMsg); // "I'm sure these crows will kill me" + break; - case 0x7907://Describe car lever - if (CHECK_FLAG(0xdb94, 1)) {//Already pulled lever? - displayMessage(0x3e4f); - return true; + case 0x7907: // Describe car lever + if (CHECK_FLAG(dsAddr_alreadyPulledTrunkReleaseLeverFlag, 1)) { // Already pulled lever? + displayMessage(dsAddr_openBootMsg); // "It opens the boot" } else - return false; + retVal = false; + break; - case 0x62d0://Get bone from under rock - displayAsyncMessage(0x463c, 30938, 16, 24); + case 0x62d0: // Get bone from under rock + displayAsyncMessage(dsAddr_yeowMsg, 218, 96, 16, 24); // "YEEEOOOWWWW!" playSound(26, 6); playSound(26, 10); playSound(24, 13); @@ -1798,35 +2742,34 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playActorAnimation(594); setOns(0, 29); disableObject(1); - inventory->add(36); + inventory->add(kInvItemBone); playSound(5, 2); playActorAnimation(595); - displayMessage(0x3790); - return true; + displayMessage(dsAddr_dinoBoneMsg); // "I really hope this is DINOSAUR bone" + break; case 0x6351: - if (CHECK_FLAG(0xdaca, 1)) { //cave bush is cut down + if (CHECK_FLAG(dsAddr_caveThornsCutDownFlag, 1)) { // cave bush is cut down playMusic(8); loadScene(26, 319, 169, 4); } else - displayMessage(0x3bd2); - return true; + displayMessage(dsAddr_ridBushMsg); // "I must get rid of this bush first" + break; case 0x63ea: playSound(5, 10); setOns(0, 0); playActorAnimation(640); - inventory->add(50); + inventory->add(kInvItemNugget); disableObject(6); - return true; + break; - case 0x6411://Kick hen - if (CHECK_FLAG(0xdb93, 1)) { //already kicked hen - displayMessage(0x3e08); - return true; + case 0x6411: // Kick hen + if (CHECK_FLAG(dsAddr_alreadyKickedHenFlag, 1)) { // already kicked hen + displayMessage(dsAddr_ridFrustationsMsg); // "I'd already got rid of my frustrations" } else { - SET_FLAG(0xdb93, 1); - displayMessage(0x3dc6); + SET_FLAG(dsAddr_alreadyKickedHenFlag, 1); + displayMessage(dsAddr_henFlyMsg); // "I wonder if hens can fly. Come here, baby" waitLanAnimationFrame(1, 87); playSound(30, 26); playSound(29, 49); @@ -1835,198 +2778,191 @@ bool TeenAgentEngine::processCallback(uint16 addr) { waitAnimation(); setOns(0, 1); enableObject(14); - displayMessage(0x3df4); - return true; + displayMessage(dsAddr_firstTestFailMsg); // "First test failed" } + break; - case 0x6592: //Rake + case 0x6592: // Rake setOns(1, 0); playSound(18, 10); playActorAnimation(553); - inventory->add(0x15); + inventory->add(kInvItemRakeBroken); wait(50); - displayMessage(0x3605); + displayMessage(dsAddr_trousersMsg); // "Good I always asked mum for trousers with BIG pockets" disableObject(11); - return true; + break; case 0x66b5: playSound(89, 5); playActorAnimation(969); loadScene(33, 319, 181, 4); - return true; + break; - case 0x6519://Sickle + case 0x6519: // Sickle setOns(4, 0); playSound(5, 11); playActorAnimation(625); - inventory->add(0x2c); + inventory->add(kInvItemSickleBlunt); disableObject(8); - return true; + break; - case 0x655b://Get needle from haystack - if (CHECK_FLAG(0xdb9d, 1)) { //already have needle - displayMessage(0x356a); - return true; + case 0x655b: // Get needle from haystack + if (CHECK_FLAG(dsAddr_gotNeedleAlreadyFlag, 1)) { // already have needle + displayMessage(dsAddr_dontPushLuckMsg); // "I don't think I should push my luck" } else { - SET_FLAG(0xdb9d, 1); + SET_FLAG(dsAddr_gotNeedleAlreadyFlag, 1); playSound(49, 3); playActorAnimation(548); - inventory->add(0x11); - displayMessage(0x35b2); - return true; + inventory->add(kInvItemNeedle); + displayMessage(dsAddr_needleHaystackMsg); // "And they say you can't find a needle in a haystack" } + break; - case 0x663c://Feather + case 0x663c: // Feather setOns(0, 0); playSound(5, 9); playActorAnimation(511); - inventory->add(1); + inventory->add(kInvItemFeather); disableObject(15); - return true; + break; case 0x667c: playSound(70, 4); playActorAnimation(972); loadScene(29, 160, 199, 1); - return true; + break; case 0x66a9: - displayMessage(0x4a7e); + displayMessage(dsAddr_dontLeaveMansionMsg); // "I don't want to leave the mansion, I want blood!" disableObject(4); - return true; + break; case 0x66e2: playSound(88, 4); playActorAnimation(970); loadScene(35, 160, 199, 1); - return true; + break; case 0x70bb: - Dialog::pop(scene, 0xdb24, 0, 709, 0xd1, 0xef, 0, 1); - return true; + dialog->pop(scene, dsAddr_dialogStackBusyCook, 0, 709, textColorMark, textColorCook, 0, 1); + break; case 0x71ae: - if (CHECK_FLAG(0xDBCD, 1)) { - if (CHECK_FLAG(0xDBCE, 1)) { - displayMessage(0x4f9b); + if (CHECK_FLAG(dsAddr_MansionRadioBrokenFlag, 1)) { + if (CHECK_FLAG(dsAddr_MansionGotRadioBatteriesFlag, 1)) { + displayMessage(dsAddr_restUselessMsg); // "The rest is useless" } else { - displayMessage(0x4fb1); + displayMessage(dsAddr_twoBatteriesMsg); // "Wow! Two 1.5V batteries!" playSound(32, 6); playActorAnimation(717); - inventory->add(66); - SET_FLAG(0xDBCE, 1); + inventory->add(kInvItemBatteries); + SET_FLAG(dsAddr_MansionGotRadioBatteriesFlag, 1); } } else - Dialog::showMark(scene, 0x3c9d); - return true; + dialog->showMark(97, scene); + break; case 0x70c8: - if (!processCallback(0x70e0)) - return true; - moveTo(81, 160, 4); - displayMessage(0x5cac); - return true; - - case 0x70e0: - if (!CHECK_FLAG(0xDBCC, 1)) { - displayMessage(0x4ece); - return false; + if (fnIsCookGone()) { + moveTo(81, 160, 4); + displayMessage(dsAddr_cognacMsg); // "Pfui! The cognac really didn't do any good" } - return true; + break; + + case csAddr_isCookGone: + retVal = fnIsCookGone(); + break; case 0x70ef: - if (!processCallback(0x70e0)) - return true; - displayMessage(0x5046); - return true; + if (fnIsCookGone()) + displayMessage(dsAddr_tooHotMsg); // "It's too hot to touch!" + break; case 0x70f9: - if (inventory->has(68)) { - inventory->remove(68); + if (inventory->has(kInvItemBurningPaper)) { + inventory->remove(kInvItemBurningPaper); loadScene(29, 40, 176, 2); - displayMessage(0x500a); + displayMessage(dsAddr_paperBurntMsg); // "The paper burnt out completely!" } else loadScene(29, 40, 176, 2); - return true; + break; case 0x712c: - if (!processCallback(0x70e0)) - return true; - - if (CHECK_FLAG(0xDBCF, 1)) { - playSound(89, 4); - playActorAnimation(719); - setOns(4, 67); - ++ *res->dseg.ptr(READ_LE_UINT16(res->dseg.ptr(0x6746 + (scene->getId() - 1) * 2))); - disableObject(5); - enableObject(12); - } else { - playSound(89, 4); - playSound(89, 4); - playSound(87, 45); - displayAsyncMessage(0x4fcb, 34672, 11, 35, 0xe5); - playActorAnimation(718); - wait(100); - displayMessage(0x4fe2); - SET_FLAG(0xDBCF, 1); + if (fnIsCookGone()) { + if (CHECK_FLAG(dsAddr_MansionHaveOpenedFridgeBeforeFlag, 1)) { + playSound(89, 4); + playActorAnimation(719); + setOns(4, 67); + ++ *res->dseg.ptr(READ_LE_UINT16(res->dseg.ptr(dsAddr_sceneWalkboxTablePtr + (scene->getId() - 1) * 2))); + disableObject(5); + enableObject(12); + } else { + playSound(89, 4); + playSound(89, 4); + playSound(87, 45); + displayAsyncMessage(dsAddr_oneTakenMsg, 112, 108, 11, 35, textColorEskimo); // "This one's taken, OK?" + playActorAnimation(718); + wait(100); + displayMessage(dsAddr_slightMadMsg); // "It finally happened. I'm slightly mad" + SET_FLAG(dsAddr_MansionHaveOpenedFridgeBeforeFlag, 1); + } } - return true; + break; case 0x71eb: setOns(2, 0); playSound(32, 7); playActorAnimation(710); - inventory->add(62); + inventory->add(kInvItemChilliWithLabel); disableObject(7); enableObject(8); - return true; + break; case 0x7244: - if (!processCallback(0x70e0)) - return true; - displayMessage(0x5c60); - return true; + if (fnIsCookGone()) + displayMessage(dsAddr_neverLearntMsg); // "I never learnt to how use one" + break; case 0x7255: - if (CHECK_FLAG(0xDBD0, 1)) { + if (CHECK_FLAG(dsAddr_MansionPutBurningPaperInFridgeFlag, 1)) { setOns(4, 69); playSound(32, 5); playActorAnimation(725); disableObject(12); - inventory->add(69); + inventory->add(kInvItemMeat); } else { playActorAnimation(721); - displayMessage(0x505e); + displayMessage(dsAddr_frozenShelfMsg); // "It has frozen hard onto the shelf!" } - return true; + break; case 0x721c: setOns(3, 0); playSound(32, 7); playActorAnimation(715); - inventory->add(63); + inventory->add(kInvItemPastryRoller); disableObject(9); - return true; + break; case 0x7336: setOns(1, 0); playSound(5, 42); - displayAsyncMessage(0x4d02, 32642, 20, 38); + displayAsyncMessage(dsAddr_noDepraveMsg, 2, 102, 20, 38); // "Nah, I don't want to deprave the kids" playActorAnimation(697); - inventory->add(56); + inventory->add(kInvItemCognac); disableObject(1); - return true; + break; case 0x7381: playSound(5, 12); playActorAnimation(704); disableObject(2); - inventory->add(58); - return true; + inventory->add(kInvItemIceTongs); + break; case 0x7408: - if (CHECK_FLAG(0xDBC4, 1)) { - displayMessage(0x4d2a); + if (CHECK_FLAG(dsAddr_mansionReadNewspaperFlag, 1)) { + displayMessage(dsAddr_noReadAgainMsg); // "I don't want to read it again. I might like it." } else { setOns(0, 0); playSound(26, 17); @@ -2038,45 +2974,45 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playActorAnimation(698); setOns(0, 52); setOns(2, 61); - Dialog::showMark(scene, 0x38b6); + dialog->showMark(92, scene); enableObject(11); - SET_FLAG(0xDBC4, 1); + SET_FLAG(dsAddr_mansionReadNewspaperFlag, 1); } - return true; + break; case 0x7476: - if (CHECK_FLAG(0xDBC9, 1)) { - displayMessage(0x4dbb); + if (CHECK_FLAG(dsAddr_mansionExaminedCouchBeforeFlag, 1)) { + displayMessage(dsAddr_noSleepMsg); // "I don't want to sleep" } else { - SET_FLAG(0xDBC9, 1); - Dialog::showMark(scene, 0x3aca); + SET_FLAG(dsAddr_mansionExaminedCouchBeforeFlag, 1); + dialog->showMark(94, scene); playSound(61, 5); playSound(5, 14); playActorAnimation(705); - displayMessage(0x4dd3); - inventory->add(59); + displayMessage(dsAddr_justCorkMsg); // "It's just a cork" + inventory->add(kInvItemCork); } - return true; + break; case 0x74d1: setOns(2, 0); playSound(5, 12); playActorAnimation(699); - inventory->add(57); + inventory->add(kInvItemRemoteControl); disableObject(11); - return true; + break; - case 0x7513: //fatso + doctor: pre-final - if (CHECK_FLAG(0xDBD7, 1)) { - if (CHECK_FLAG(0xDBD8, 1)) { + case 0x7513: // fatso + doctor: pre-final + if (CHECK_FLAG(dsAddr_MansionThruFanByTimePillFlag, 1)) { + if (CHECK_FLAG(dsAddr_MansionVentFanStoppedFlag, 1)) { playSound(88, 4); playActorAnimation(979); loadScene(37, 51, 183); - Dialog::show(scene, 0x54ea, 768, 769, 0xd9, 0xe5, 1, 2); + dialog->show(125, scene, 768, 769, textColorMansionGuard, textColorProfessor, 1, 2); playAnimation(770, 0, true, true, true); playAnimation(771, 1, true, true, true); - Dialog::showMono(scene, 0x5523, 0, 0xd1, 0); + dialog->showMono(126, scene, 0, textColorMark, 0); playAnimation(770, 0, true, true, true); playAnimation(771, 1, true, true, true); playSound(5, 3); @@ -2090,11 +3026,11 @@ bool TeenAgentEngine::processCallback(uint16 addr) { waitAnimation(); setOns(0, 74); hideActor(); - Dialog::showMono(scene, 0x5556, 775, 0xd0, 1); + dialog->showMono(127, scene, 775, textColorJohnNoty, 1); playAnimation(771, 1, true, true, true); playAnimation(776, 0); - Dialog::show(scene, 0x55f7, 777, 778, 0xd0, 0xe5, 1, 2); //i have to kill you anyway + dialog->show(128, scene, 777, 778, textColorJohnNoty, textColorProfessor, 1, 2); playAnimation(779, 0, true, true, true); playAnimation(780, 1, true, true, true); @@ -2151,7 +3087,7 @@ bool TeenAgentEngine::processCallback(uint16 addr) { setOns(0, 80); playAnimation(792, 3, true, true, true); - Dialog::show(scene, 0x5665, 0, 791, 0xd1, 0xd0, 0, 4); + dialog->show(129, scene, 0, 791, textColorMark, textColorJohnNoty, 0, 4); playAnimation(792, 3, true, true, true); moveTo(40, 171, 4); @@ -2161,43 +3097,47 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playAnimation(0, 3); loadScene(31, 298, 177, 4); - SET_FLAG(0xDBD9, 1); - } else { - displayMessage(0x52fe); - } + SET_FLAG(dsAddr_MansionJohnNotyEscapingFlag, 1); + } else + displayMessage(dsAddr_ventFirstMsg); // "I'd better stop this ventilator first" } else - displayMessage(0x52cb); - return true; + displayMessage(dsAddr_noSaladMsg); // "I don't want to turn myself into a salad" + break; case 0x783d: - Dialog::pop(scene, 0xdb36, 0, 797, 0xd1, 0xd0, 0, 1); - return true; + dialog->pop(scene, dsAddr_dialogStackJohnNotyEndgame, 0, 797, textColorMark, textColorJohnNoty, 0, 1); + break; case 0x7966: - if (CHECK_FLAG(0xDBA4, 1)) - return false; - return processCallback(0x60b5); + if (CHECK_FLAG(dsAddr_lightOnFlag, 1)) + retVal = false; + else + fnEgoScaredBySpider(); + break; case 0x7ad0: case 0x7ad7: - return !processCallback(0x70e0); + retVal = !fnIsCookGone(); + break; case 0x7ab9: - if (CHECK_FLAG(0xDBB6, 1)) - return false; - Dialog::showMono(scene, 0x37d0, 0, 0xd1, 0); - SET_FLAG(0xDBB6, 1); - return true; + if (CHECK_FLAG(dsAddr_vgaArtistQuipAlreadySaidFlag, 1)) + retVal = false; + else { + dialog->showMono(90, scene, 0, textColorMark, 0); + SET_FLAG(dsAddr_vgaArtistQuipAlreadySaidFlag, 1); + } + break; case 0x7ade: - if (CHECK_FLAG(0xdbcd, 1)) { - displayMessage(0x4f69); - return true; - } else - return false; + if (CHECK_FLAG(dsAddr_MansionRadioBrokenFlag, 1)) + displayMessage(dsAddr_whatInsideMsg); // "I was always curious what's inside these things" + else + retVal = false; + break; - case 0x7f23://Use grenade on captains drawer - if (CHECK_FLAG(0xDBDF, 3)) { + case 0x7f23: // Use grenade on captains drawer + if (CHECK_FLAG(dsAddr_FirstActTrialState, 3)) { enableOn(false); playSound(5, 3); playSound(58, 11); @@ -2207,123 +3147,120 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playActorAnimation(870); playSound(54, 15); playActorAnimation(871); - SET_FLAG(0xDBE6, 1); + SET_FLAG(dsAddr_captainDrawerState, 1); setOns(1, 0x66); moveTo(224, 194, 0, true); - displayCutsceneMessage(0x57df, 30423); - inventory->remove(0x59); + displayCutsceneMessage(dsAddr_cutsceneMsg1, 23, 95); // "sixty seven rude words later" + inventory->remove(kInvItemRopeAndGrenade); enableOn(true); - } else { - displayMessage(0x5de2); - } - return true; + } else + displayMessage(dsAddr_captainWatchingMsg); // "with captain watching? Better not" + break; - case 0x505c: { - //suspicious stuff - Common::Point p = scene->getPosition(); - if (p.x != 203 && p.y != 171) - moveTo(203, 169, 2); - else - moveTo(203, 169, 1); - } - return true; + case csAddr_egoSuspiciousPosition: + fnEgoSuspiciousPosition(); + break; case 0x509a: - processCallback(0x505c); + fnEgoSuspiciousPosition(); setOns(1, 0); playSound(5, 10); playActorAnimation(543); - inventory->add(15); + inventory->add(kInvItemBranch); disableObject(9); - return true; + break; case 0x7802: - if (CHECK_FLAG(0xDBD7, 1)) { - if (CHECK_FLAG(0xDBD8, 1)) - displayMessage(0x52f6); + if (CHECK_FLAG(dsAddr_MansionThruFanByTimePillFlag, 1)) { + if (CHECK_FLAG(dsAddr_MansionVentFanStoppedFlag, 1)) + displayMessage(dsAddr_nahMsg); // "Nah" else { playSound(71, 4); playActorAnimation(796); setLan(1, 0); - SET_FLAG(0xDBD8, 1); + SET_FLAG(dsAddr_MansionVentFanStoppedFlag, 1); } } else - displayMessage(0x52cb); - return true; + displayMessage(dsAddr_noSaladMsg); // "I don't want to turn myself into a salad" + break; case 0x78e0: - processCallback(0x50c5); - return false; + fnEgoSuspiciousPosition(); + retVal = false; + break; case 0x78e7: - processCallback(0x557e); - return false; - case 0x78ee: - processCallback(0x557e); - return false; + fnEgoDefaultPosition(); + retVal = false; + break; case 0x78f5: - if (CHECK_FLAG(0xDB95, 1)) { - displayMessage(0x3575); - return true; + if (CHECK_FLAG(dsAddr_carTrunkEmptyFlag, 1)) { + displayMessage(dsAddr_bootEmptyMsg); // "There's nothing else in the boot" } else - return false; + retVal = false; + break; case 0x7919: - if (!CHECK_FLAG(0xDBA5, 1)) - return false; - displayMessage(0x3E98); - return true; + if (!CHECK_FLAG(dsAddr_laundryState, 1)) + retVal = false; + else + displayMessage(dsAddr_clothesDryMsg); // "The clothes are dry now." + break; case 0x7950: - if (!CHECK_FLAG(0xDBB1, 1)) - return false; - - displayMessage(0x3DAF); - return true; + if (CHECK_FLAG(dsAddr_nutSwappedForAppleFlag, 1)) + displayMessage(dsAddr_nutRealMsg); // "Only the nut is real" + else + retVal = false; + break; case 0x7975: - if (CHECK_FLAG(0xDBA4, 1)) - return false; - displayMessage(0x3832); - return true; + if (CHECK_FLAG(dsAddr_lightOnFlag, 1)) + retVal = false; + else + displayMessage(dsAddr_shutValveMsg); // "Shutting the valve shook the dirt from the wall..." + break; case 0x7987: case 0x7996: case 0x79a5: case 0x79b4: - if (CHECK_FLAG(0xDBA4, 1)) - return false; - return processCallback(0x61fe); + if (CHECK_FLAG(dsAddr_lightOnFlag, 1)) + retVal = false; + else + fnTooDark(); + break; case 0x79d2: - if (!CHECK_FLAG(0xDB9D, 1)) - return false; - displayMessage(0x3590); - return true; + if (!CHECK_FLAG(dsAddr_gotNeedleAlreadyFlag, 1)) + retVal = false; + else + displayMessage(dsAddr_ordinaryHaystackMsg); // "Just an ordinary hay stack. Now." + break; case 0x7af0: - if (!processCallback(0x70e0)) - return true; - return false; + if (fnIsCookGone()) + retVal = false; + break; case 0x8117: - Dialog::show(scene, 0x0a41, 0, 529, 0xd1, 0xd9, 0, 1); + dialog->show(9, scene, 0, 529, textColorMark, textColorMansionGuard, 0, 1); playSound(5, 2); playSound(5, 44); playAnimation(642, 0, true); playActorAnimation(641, true); waitAnimation(); - Dialog::show(scene, 0x0aff, 0, 529, 0xd1, 0xd9, 0, 1); + dialog->show(10, scene, 0, 529, textColorMark, textColorMansionGuard, 0, 1); wait(170); - Dialog::show(scene, 0x0ba0, 0, 529, 0xd1, 0xd9, 0, 1); + dialog->show(11, scene, 0, 529, textColorMark, textColorMansionGuard, 0, 1); moveRel(0, 1, 0); wait(100); - Dialog::show(scene, 0x0c10, 0, 529, 0xd1, 0xd9, 0, 1); - inventory->remove(50); - processCallback(0x9d45); - return true; + dialog->show(12, scene, 0, 529, textColorMark, textColorMansionGuard, 0, 1); + inventory->remove(kInvItemNugget); + fnMansionIntrusionAttempt(); + break; case 0x8174: setOns(0, 0); @@ -2336,7 +3273,7 @@ bool TeenAgentEngine::processCallback(uint16 addr) { setOns(1, 15); disableObject(3); enableObject(9); - return true; + break; case 0x81c2: playSound(56, 11); @@ -2353,12 +3290,12 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playActorAnimation(588, true); waitAnimation(); wait(50); - displayMessage(0x367f); - inventory->remove(34); - SET_FLAG(0xDBA1, 1); - return true; + displayMessage(dsAddr_itsGoneMsg); // "At least it's gone" + inventory->remove(kInvItemPaintedPotato); + SET_FLAG(dsAddr_mansionTreeHollowEmptyFlag, 1); + break; - case 0x823d: //grappling hook on the wall + case 0x823d: // grappling hook on the wall playSound(5, 3); for (byte i = 16; i <= 28; i += 2) playSound(65, i); @@ -2367,27 +3304,26 @@ bool TeenAgentEngine::processCallback(uint16 addr) { for (byte i = 3; i <= 18; i += 3) playSound(56, i); - displayAsyncMessage(0x3ace, 3878, 20, 37, 0xd9); + displayAsyncMessage(dsAddr_heyLetGoMsg, 38, 12, 20, 37, textColorMansionGuard); // "Hey, let go, will ya?!" playActorAnimation(621, true); playAnimation(623, 1, true); waitAnimation(); - displayAsyncMessage(0x3ae6, 3870, 1, 9, 0xd9); + displayAsyncMessage(dsAddr_aaahhhMsg, 30, 12, 1, 9, textColorMansionGuard); // "Aaaaaaaaaaaaahhh!" playSound(35, 1); playActorAnimation(622, true); playAnimation(624, 0, true); waitAnimation(); wait(150); - displayMessage(0x3afd); - - inventory->remove(43); - processCallback(0x9d45); - return true; + displayMessage(dsAddr_oopsMsg); // "Oops" + inventory->remove(kInvItemGrapplingHook); + fnMansionIntrusionAttempt(); + break; - case 0x8312: //hedgehog + plastic apple - Dialog::showMark(scene, 0x3000); + case 0x8312: // hedgehog + plastic apple + dialog->showMark(76, scene); setLan(1, 0); playSound(5, 24); playSound(26, 32); @@ -2405,13 +3341,13 @@ bool TeenAgentEngine::processCallback(uint16 addr) { waitAnimation(); disableObject(6); - displayMessage(0x363f); - inventory->remove(27); - inventory->add(28); - return true; + displayMessage(dsAddr_lifeIsBrutalMsg); // "Life is brutal" + inventory->remove(kInvItemPlasticApple); + inventory->add(kInvItemCone); + break; case 0x839f: - inventory->remove(32); + inventory->remove(kInvItemDart); playSound(37, 14); playSound(16, 17); playActorAnimation(564, true); @@ -2437,10 +3373,10 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playSound(55, 18); playAnimation(581, 1); disableObject(2); - SET_FLAG(0xDB9F, 1); - return true; + SET_FLAG(dsAddr_beesGoneFlag, 1); + break; - case 0x84c7: //using paddle on boat + case 0x84c7: // using paddle on boat playSound(20, 9); playActorAnimation(530); loadScene(16, 236, 95, 1); @@ -2451,12 +3387,12 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playActorAnimation(533); setOns(0, 9); moveTo(236, 95, 1, true); - return true; + break; - case 0x8538://Sharpen sickle on well + case 0x8538: // Sharpen sickle on well moveTo(236, 190, 0); setOns(2, 0); - //TODO: Remove handle sprite + // FIXME: Add code to Remove handle sprite (visible GFX glitch) playSound(5, 4); playSound(14, 14); playSound(14, 33); @@ -2464,52 +3400,58 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playActorAnimation(643); setOns(2, 43); moveTo(236, 179, 3); - inventory->remove(0x2c); - inventory->add(0x2e); - return true; + inventory->remove(kInvItemSickleBlunt); + inventory->add(kInvItemSickleSharp); + break; + + case 0x85d6: + displayMessage(dsAddr_paddleBrokenMsg); // "The paddle is BROKEN" + break; case 0x85eb: - if (CHECK_FLAG(0xDBB0, 1)) { + if (CHECK_FLAG(dsAddr_squirrelNutState, 1)) { enableObject(6); playSound(25, 10); playSound(25, 14); playSound(25, 18); playActorAnimation(559); setOns(1, 23); - SET_FLAG(0xDBB0, 2); + SET_FLAG(dsAddr_squirrelNutState, 2); } else - displayMessage(0x3d86); - - return true; + displayMessage(dsAddr_dontWorkPurposeMsg); // "I usually don't work without a purpose" + break; case 0x863d: playSound(12, 4); playSound(50, 20); playSound(50, 29); playActorAnimation(554); - inventory->remove(19); - inventory->add(22); - return true; + inventory->remove(kInvItemChocCandy); + inventory->add(kInvItemHeartShapedCandy); + break; case 0x8665: playSound(5, 3); for (byte i = 12; i <= 24; i += 2) playSound(56, i); playActorAnimation(567); - inventory->remove(12); - inventory->add(33); - return true; + inventory->remove(kInvItemFeatherDusterClean); + inventory->add(kInvItemFeatherDusterDirty); + break; case 0x862c: - displayMessage(CHECK_FLAG(0xDBB0, 1) ? 0x4882 : 0x3457); - return true; + if (CHECK_FLAG(dsAddr_squirrelNutState, 1)) + displayMessage(dsAddr_nutRakeMsg); // "It's pointless, the nut will slip between the rake's teeth" + else + displayMessage(dsAddr_objErrorMsg); // "That's no good" + break; - case 0x86a9: //correcting height of the pole with spanner - if (CHECK_FLAG(0xDB92, 1)) { - displayMessage(0x3d40); + case 0x86a9: // correcting height of the pole with spanner + if (CHECK_FLAG(dsAddr_alreadyAdjustedHoopPoleFlag, 1)) { + displayMessage(dsAddr_noNeedMsg); // "No need to do it again" } else { - SET_FLAG(0xDB92, 1); - Dialog::show(scene, 0x0fcd, 0, 502, 0xd0, 0xe5, 0, 1); + SET_FLAG(dsAddr_alreadyAdjustedHoopPoleFlag, 1); + dialog->show(17, scene, 0, 502, textColorMark, textColorSonny, 0, 1); waitLanAnimationFrame(1, 7); playSound(5, 16); playSound(1, 25); @@ -2528,7 +3470,7 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playSound(5, 43); playSound(61, 70); playSound(61, 91); - displayAsyncMessage(0x3cfb, 28877, 6, 17); + displayAsyncMessage(dsAddr_ConfusionMsg, 77, 90, 6, 17); // "!?&!" playActorAnimation(505, true); playAnimation(507, 0, true); waitAnimation(); @@ -2552,8 +3494,8 @@ bool TeenAgentEngine::processCallback(uint16 addr) { disableObject(15); disableObject(16); moveTo(162, 164, 2); - displayMessage(0x3d01, 0xe5, 24390); - displayMessage(0x3d20, 0xd8, 24410); + displayMessage(dsAddr_grandpaPromiseMsg, textColorSonny, 70, 76); // "But grandpa, you promised!" + displayMessage(dsAddr_ohLetsGoMsg, textColorGrandpa, 90, 76); // "Oh all right. Let's go" moveTo(162, 191, 2); setOns(1, 0); setOns(2, 0); @@ -2572,133 +3514,118 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playAnimation(512, 0); wait(100); - displayMessage(0x3d3a); + displayMessage(dsAddr_byeMsg); // "Bye." { Object *obj = scene->getObject(7); - obj->actor_rect.left = obj->actor_rect.right = 228; - obj->actor_rect.top = obj->actor_rect.bottom = 171; - obj->actor_rect.save(); + obj->actorRect.left = obj->actorRect.right = 228; + obj->actorRect.top = obj->actorRect.bottom = 171; + obj->actorRect.save(); } { Object *obj = scene->getObject(8); - obj->actor_rect.left = obj->actor_rect.right = 290; - obj->actor_rect.top = obj->actor_rect.bottom = 171; - obj->actor_rect.save(); + obj->actorRect.left = obj->actorRect.right = 290; + obj->actorRect.top = obj->actorRect.bottom = 171; + obj->actorRect.save(); } } - return true; + break; - case 0x88c9: //give flower to old lady - if (CHECK_FLAG(0xDB9A, 1)) - return processCallback(0x890b); - - inventory->remove(10); - SET_FLAG(0xDB9A, 1); - processCallback(0x88DE); - return true; - - case 0x88de: - playSound(5, 2); - Dialog::show(scene, 0x1B5F, 0, 523, 0xd1, 0xe5, 0, 1); - playActorAnimation(537, true); - playAnimation(538, 0, true); - waitAnimation(); - wait(100); - Dialog::show(scene, 0x1BE0, 0, 523, 0xd1, 0xe5, 0, 1); - return true; + case 0x88c9: // give flower to old lady + if (CHECK_FLAG(dsAddr_givenFlowerToOldLadyAlreadyFlag, 1)) + fnGiveAnotherFlowerToOldLady(); + else { + inventory->remove(kInvItemFirstFlower); + SET_FLAG(dsAddr_givenFlowerToOldLadyAlreadyFlag, 1); + fnGivingFlowerToOldLady(); + } + break; - case 0x890b: - Dialog::pop(scene, 0xDAF0, 0, 523, 0xd1, 0xe5, 0, 1); - return true; + case csAddr_givingFlowerToOldLady: + fnGivingFlowerToOldLady(); + break; - case 0x8918://give flower to old lady - if (CHECK_FLAG(0xDB9A, 1)) - return processCallback(0x890B); + case csAddr_giveAnotherFlowerToOldLady: + fnGiveAnotherFlowerToOldLady(); + break; - inventory->remove(11); - SET_FLAG(0xDB9A, 1); - processCallback(0x88DE); - return true; + case 0x8918: // give flower to old lady + if (CHECK_FLAG(dsAddr_givenFlowerToOldLadyAlreadyFlag, 1)) + fnGiveAnotherFlowerToOldLady(); + else { + inventory->remove(kInvItemSecondFlower); + SET_FLAG(dsAddr_givenFlowerToOldLadyAlreadyFlag, 1); + fnGivingFlowerToOldLady(); + } + break; case 0x892d: - if (CHECK_FLAG(0xDB9B, 1)) - return processCallback(0x89aa); + if (CHECK_FLAG(dsAddr_givenFlowerToAnneAlreadyFlag, 1)) + fnGiveAnotherFlowerToAnne(); + else { + fnGivingFlowerToAnne(); + inventory->remove(kInvItemFirstFlower); + SET_FLAG(dsAddr_givenFlowerToAnneAlreadyFlag, 1); + } + break; - processCallback(0x8942); - inventory->remove(10); - SET_FLAG(0xDB9B, 1); - return true; + case csAddr_givingFlowerToAnne: + fnGivingFlowerToAnne(); + break; - case 0x8942: - Dialog::show(scene, 0x2293, 0, 524, 0xd1, 0xe5, 0, 2); - playSound(5, 10); - playActorAnimation(540, true); - playAnimation(539, 1, true); - waitAnimation(); - wait(100); - Dialog::show(scene, 0x24b1, 0, 524, 0xd1, 0xe5, 0, 2); - wait(50); - Dialog::show(scene, 0x24d7, 0, 524, 0xd1, 0xe5, 0, 2); - Dialog::show(scene, 0x2514, 0, 524, 0xd1, 0xe5, 0, 2); - wait(50); - moveRel(0, 1, 0); - Dialog::show(scene, 0x2570, 0, 524, 0xd1, 0xe5, 0, 2); - moveRel(0, -1, 0); - wait(50); - return true; - - case 0x89aa: - Dialog::pop(scene, 0xdb02, 0, 524, 0xd1, 0xe5, 0, 2); - return true; + case csAddr_giveAnotherFlowerToAnne: + fnGiveAnotherFlowerToAnne(); + break; case 0x89b7: - if (CHECK_FLAG(0xDB9B, 1)) - return processCallback(0x89aa); - - processCallback(0x8942); - inventory->remove(11); - SET_FLAG(0xDB9B, 1); - return true; + if (CHECK_FLAG(dsAddr_givenFlowerToAnneAlreadyFlag, 1)) + fnGiveAnotherFlowerToAnne(); + else { + fnGivingFlowerToAnne(); + inventory->remove(kInvItemSecondFlower); + SET_FLAG(dsAddr_givenFlowerToAnneAlreadyFlag, 1); + } + break; case 0x89cc: - inventory->remove(23); + inventory->remove(kInvItemWrappedCandy); playSound(5, 6); - Dialog::show(scene, 0x2634, 0, 524, 0xd1, 0xe5, 0, 2); + dialog->show(60, scene, 0, 524, textColorMark, textColorAnne, 0, 2); + // FIXME - Dialog #61 not explicitly called. Does Dialog #60 run on somehow? playActorAnimation(555, true); playAnimation(556, 1, true); waitAnimation(); playActorAnimation(557, true); playAnimation(558, 1, true); waitAnimation(); - Dialog::show(scene, 0x2971, 0, 524, 0xd1, 0xe5, 0, 2); - inventory->add(24); - return true; + dialog->show(62, scene, 0, 524, textColorMark, textColorAnne, 0, 2); + inventory->add(kInvItemRibbon); + break; case 0x8a22: playSound(45, 16); playActorAnimation(560); - inventory->remove(26); - inventory->add(27); + inventory->remove(kInvItemNut); + inventory->add(kInvItemPlasticApple); wait(50); - Dialog::show(scene, 0x1ecd, 0, 523, 0xd1, 0xe5, 0, 1); - Dialog::show(scene, 0x1f09, 0, 523, 0xd1, 0xe5, 0, 1); - SET_FLAG(0xDBB1, 1); - return true; - - case 0x8a6f: //banknote + ann - if (CHECK_FLAG(0xDBB5, 1)) { - Dialog::show(scene, 0x2992, 0, 524, 0xd1, 0xe5, 0, 2); + dialog->show(44, scene, 0, 523, textColorMark, textColorOldLady, 0, 1); + dialog->show(45, scene, 0, 523, textColorMark, textColorOldLady, 0, 1); + SET_FLAG(dsAddr_nutSwappedForAppleFlag, 1); + break; + + case 0x8a6f: // banknote + ann + if (CHECK_FLAG(dsAddr_examinedBanknoteFlag, 1)) { + dialog->show(63, scene, 0, 524, textColorMark, textColorAnne, 0, 2); playSound(5, 3); playSound(5, 20); playAnimation(671, 1, true); playActorAnimation(670, true); waitAnimation(); //playAnimation(672, 1); - Dialog::show(scene, 0x2a00, 524, 672, 0xd1, 0xe5, 0, 2); + dialog->show(64, scene, 524, 672, textColorMark, textColorAnne, 0, 2); //playAnimation(672, 1); playSound(83, 12); - displayAsyncMessage(0x4a5b, 36684, 23, 38, 0xe5); + displayAsyncMessage(dsAddr_hundredBucksMsg, 204, 114, 23, 38, textColorAnne); // "A hundred bucks!!!" playActorAnimation(673); loadScene(11, scene->getPosition()); playSound(24, 31); @@ -2710,14 +3637,14 @@ bool TeenAgentEngine::processCallback(uint16 addr) { loadScene(28, 0, 167, 2); playMusic(10); moveTo(66, 167, 2); - displayMessage(0x4a6f); + displayMessage(dsAddr_wantBloodMsg); // "I want Blood!" inventory->clear(); - inventory->add(29); + inventory->add(kInvItemSuperGlue); } else - displayMessage(0x4a29); - return true; + displayMessage(dsAddr_showHerMoneyMsg); // "If I just show her the money, she might take it" + break; - case 0x8b82: //use fan on laundry + case 0x8b82: // use fan on laundry setOns(0, 0); playSound(5, 3); playSound(5, 6); @@ -2725,16 +3652,16 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playSound(92, 20); playSound(92, 38); playSound(92, 58); - displayAsyncMessage(0x464a, 36510, 58, 67); + displayAsyncMessage(dsAddr_yawnMsg, 30, 114, 58, 67); // "(yawn)" playActorAnimation(602); playSound(5, 3); playActorAnimation(603); setOns(0, 27); - SET_FLAG(0xDBA5, 1); - return true; + SET_FLAG(dsAddr_laundryState, 1); + break; - case 0x8bfc://Give bone to dog - displayMessage(0x3c31); + case 0x8bfc: // Give bone to dog + displayMessage(dsAddr_hereBoyMsg); // "Here, boy" playSound(5, 3); playSound(26, 13); playActorAnimation(657, true); @@ -2744,20 +3671,20 @@ bool TeenAgentEngine::processCallback(uint16 addr) { reloadLan(); playAnimation(659, 0); - inventory->remove(36); - SET_FLAG(0xDBAD, 1); + inventory->remove(kInvItemBone); + SET_FLAG(dsAddr_dogHasBoneFlag, 1); { Object *o = scene->getObject(7); - o->actor_rect.left = o->actor_rect.right = 297; - o->actor_rect.top = o->actor_rect.bottom = 181; - o->actor_orientation = 1; + o->actorRect.left = o->actorRect.right = 297; + o->actorRect.top = o->actorRect.bottom = 181; + o->actorOrientation = 1; o->save(); } { Object *o = scene->getObject(9); - o->actor_rect.left = o->actor_rect.right = 297; - o->actor_rect.top = o->actor_rect.bottom = 181; - o->actor_orientation = 1; + o->actorRect.left = o->actorRect.right = 297; + o->actorRect.top = o->actorRect.bottom = 181; + o->actorOrientation = 1; o->save(); } { @@ -2767,10 +3694,10 @@ bool TeenAgentEngine::processCallback(uint16 addr) { w->save(); } wait(100); - displayMessage(0x3c3d); - return true; + displayMessage(dsAddr_friendsNowMsg); // "I hope we're friends now" + break; - case 0x8c6e://Use car jack on rock + case 0x8c6e: // Use car jack on rock playSound(5, 3); playSound(26, 13); playSound(24, 22); @@ -2782,10 +3709,10 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playActorAnimation(593); setOns(0, 28); enableObject(1); - inventory->remove(35); - return true; + inventory->remove(kInvItemCarJack); + break; - case 0x8cc8://Cut bush with sickle + case 0x8cc8: // Cut bush with sickle playSound(5, 3); playActorAnimation(644); setOns(1, 45); @@ -2799,16 +3726,16 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playActorAnimation(646); playSound(5, 21); playActorAnimation(647); - SET_FLAG(0xdaca, 1); - inventory->remove(0x2e); + SET_FLAG(dsAddr_caveThornsCutDownFlag, 1); + inventory->remove(kInvItemSickleSharp); disableObject(2); - scene->getObject(3)->actor_rect.right = 156; + scene->getObject(3)->actorRect.right = 156; scene->getObject(3)->save(); - return true; + break; - case 0x8d79: //mouse falls back from the hole (cave) - if (CHECK_FLAG(0, 1)) { - inventory->add(48); + case csAddr_mouseOutOfHoleTimeout: // mouse falls back from the hole (cave) + if (CHECK_FLAG(dsAddr_timedCallbackState, 1)) { + inventory->add(kInvItemMouse); playSound(24, 26); playActorAnimation(650, true); playAnimation(651, 2, true); @@ -2824,54 +3751,26 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playActorAnimation(654, true); playAnimation(655, 2, true); waitAnimation(); - displayMessage(0x3bf6); - inventory->add(49); + displayMessage(dsAddr_mouseGoneMsg); // "The mouse has gone!" + inventory->add(kInvItemRock); setLan(2, 4, 27); enableObject(4, 27); - SET_FLAG(0xdba9, 0); + SET_FLAG(dsAddr_mouseHoleState, 0); } - SET_FLAG(0, 0); - return true; + SET_FLAG(dsAddr_timedCallbackState, 0); + break; - case 0x8d57: - if (CHECK_FLAG(0, 0)) { - playSound(5, 2); - playSound(15, 12); - playActorAnimation(638); - inventory->remove(48); - setTimerCallback(0x8d79, 100); - SET_FLAG(0, 1); - } else if (CHECK_FLAG(0, 1)) { - playSound(5, 2); - playSound(52, 13); - playActorAnimation(648); - setOns(1, 46); - inventory->remove(49); - setTimerCallback(0x8d79, 100); - SET_FLAG(0, 2); - } else if (CHECK_FLAG(0, 2)) { - playActorAnimation(649); - setOns(1, 47); - wait(300); - for (byte i = 1; i <= 37; i += 4) - playSound(68, i); - playAnimation(639, 2); - setOns(0, 42); - enableObject(6); - disableObject(5); - SET_FLAG(0xDBAB, 1); - SET_FLAG(0, 0); - setTimerCallback(0, 0); - } - return true; + case csAddr_putRockInHole: + fnPutRockInHole(); + break; case 0x8f1d: - Dialog::showMark(scene, 0x2dd6); + dialog->showMark(72, scene); for (uint i = 16; i <= 30; i += 2) playSound(56, i); playSound(2, 64); playSound(3, 74); - displayAsyncMessage(0x34c7, 25812, 35, 50); + displayAsyncMessage(dsAddr_lastChanceMsg, 212, 80, 35, 50); // "Last chance?" playActorAnimation(516, true); playAnimation(517, 2, true); playAnimation(518, 3, true); @@ -2880,12 +3779,12 @@ bool TeenAgentEngine::processCallback(uint16 addr) { setLan(4, 0); disableObject(2); disableObject(3); - inventory->remove(2); - SET_FLAG(0xDB96, 1); - return true; + inventory->remove(kInvItemShotgun); + SET_FLAG(dsAddr_birdsGoneFromScarecrowFlag, 1); + break; case 0x8fc8: - displayMessage(0x3b2f); + displayMessage(dsAddr_comeHereMsg); // "Come here, I've got something for you" waitLanAnimationFrame(2, 4); playSound(5, 3); playActorAnimation(627, true); @@ -2894,25 +3793,25 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playSound(41, 10); playSound(41, 47); playSound(55, 52); - if (CHECK_FLAG(0xDBA8, 1)) { + if (CHECK_FLAG(dsAddr_HankerchiefInMouseholeFlag, 1)) { setLan(2, 0); playActorAnimation(628, true); playAnimation(634, 1, true); waitAnimation(); disableObject(4); - displayMessage(0x3b6c); - SET_FLAG(0xDBA9, 1); + displayMessage(dsAddr_trappedMouseMsg); // "The mouse is trapped!" + SET_FLAG(dsAddr_mouseHoleState, 1); } else { playActorAnimation(628, true); playAnimation(630, 1, true); waitAnimation(); - displayMessage(0x3b59); + displayMessage(dsAddr_cantCatchMsg); // "I can't catch it!" } - return true; + break; - case 0x9054: //mouse hole - if (CHECK_FLAG(0xDBAB, 1)) { - displayMessage(0x3c0b); + case 0x9054: // mouse hole + if (CHECK_FLAG(dsAddr_mouseGotGoldNuggetFlag, 1)) { + displayMessage(dsAddr_nonsenseMsg); // "Nonsense" } else { playSound(5, 11); playSound(49, 21); @@ -2920,50 +3819,48 @@ bool TeenAgentEngine::processCallback(uint16 addr) { setOns(5, 40); moveTo(239, 139, 0, true); playActorAnimation(633); - SET_FLAG(0xDBA8, 1); - inventory->remove(47); - if (!CHECK_FLAG(0xDBAA, 1)) { - SET_FLAG(0xDBAA, 1); - displayMessage(0x3b8b); + SET_FLAG(dsAddr_HankerchiefInMouseholeFlag, 1); + inventory->remove(kInvItemHandkerchief); + if (!CHECK_FLAG(dsAddr_mouseNerveMsgSaidFlag, 1)) { + SET_FLAG(dsAddr_mouseNerveMsgSaidFlag, 1); + displayMessage(dsAddr_mouseNerveMsg); // "Boy, this mouse has some nerve!" } } - return true; + break; case 0x933d: - if (!processCallback(0x70e0)) - return true; - - if (CHECK_FLAG(0xdbcd, 1)) { - displayMessage(0x4f3d); - return true; + if (fnIsCookGone()) { + if (CHECK_FLAG(dsAddr_MansionRadioBrokenFlag, 1)) + displayMessage(dsAddr_breakFlattenMsg); // "I wanted to break it, not to flatten it!" + else { + setOns(1, 0); + playSound(5, 3); + playSound(5, 33); + playSound(24, 13); + playSound(24, 19); + playSound(24, 23); + playSound(24, 26); + playSound(24, 29); + playSound(23, 21); + playSound(74, 25); + playActorAnimation(716); + setOns(1, 66); + SET_FLAG(dsAddr_MansionRadioBrokenFlag, 1); + } } + break; - setOns(1, 0); - playSound(5, 3); - playSound(5, 33); - playSound(24, 13); - playSound(24, 19); - playSound(24, 23); - playSound(24, 26); - playSound(24, 29); - playSound(23, 21); - playSound(74, 25); - playActorAnimation(716); - setOns(1, 66); - SET_FLAG(0xDBCD, 1); - return true; - - case 0x93af: //sheet + hot plate - if (!processCallback(0x70e0)) - return true; - playSound(5, 3); - playSound(86, 11); - playActorAnimation(720); - inventory->add(68); - inventory->remove(55); - return true; + case 0x93af: // sheet + hot plate + if (fnIsCookGone()) { + playSound(5, 3); + playSound(86, 11); + playActorAnimation(720); + inventory->add(kInvItemBurningPaper); + inventory->remove(kInvItemSheetOfPaper); + } + break; - case 0x93d5: //burning sheet + plate + case 0x93d5: // burning sheet + plate setOns(4, 0); playSound(87, 7); playActorAnimation(722); @@ -2971,323 +3868,298 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playSound(88, 12); playSound(87, 24); playActorAnimation(723); - displayMessage(0x502b); + displayMessage(dsAddr_burnBabyMsg); // "Burn, baby, burn!" wait(100); playSound(89, 4); playActorAnimation(724); setOns(4, 68); - displayMessage(0x503e); - inventory->remove(68); - SET_FLAG(0xDBD0, 1); - return true; - - case 0x98fa://Right click to open toolbox - inventory->remove(3); - inventory->add(4); - inventory->add(35); + displayMessage(dsAddr_voilaMsg); // "Voila" + inventory->remove(kInvItemBurningPaper); + SET_FLAG(dsAddr_MansionPutBurningPaperInFridgeFlag, 1); + break; + + case csAddr_openFullToolbox: // Right click to open toolbox + inventory->remove(kInvItemToolboxFull); + inventory->add(kInvItemToolboxHalfEmpty); + inventory->add(kInvItemCarJack); inventory->activate(false); inventory->resetSelectedObject(); - displayMessage(0x3468); - return true; + displayMessage(dsAddr_carJackMsg); // "Wow! There's a car jack inside! Great!" + break; - case 0x9910: - inventory->remove(4); - inventory->add(5); + case csAddr_openHalfEmptyToolbox: + inventory->remove(kInvItemToolboxHalfEmpty); + inventory->add(kInvItemSpanner); inventory->activate(false); inventory->resetSelectedObject(); - displayMessage(0x3490); - return true; + displayMessage(dsAddr_spannerMsg); // "There's something else inside the toolbox! A spanner!" + break; - - //very last part of the game: - case 0x671d: + case 0x671d: // very last part of the game moveTo(153, 163, 4); playActorAnimation(973); - if (CHECK_FLAG(0xDBC1, 0)) { - SET_FLAG(0xDBC1, _rnd.getRandomNumber(5) + 1); + if (CHECK_FLAG(dsAddr_drawerPuzzleBookValue, 0)) { + SET_FLAG(dsAddr_drawerPuzzleBookValue, _rnd.getRandomNumber(5) + 1); } loadScene(30, 18, 159, 2); - return true; + break; case 0x67a6: loadScene(29, 149, 163, 1); playActorAnimation(974); moveTo(160, 188, 0); - return true; + break; case 0x6805: - processCallback(0x6849); + fnEgoBottomRightTurn(); playSound(32, 12); playActorAnimation(694); playSound(15, 8); playAnimation(693, 0); setOns(6, 0); - displayMessage(0x4cc7); - inventory->add(54); + displayMessage(dsAddr_fullAutomaticMsg); // "Fully Automatic" + inventory->add(kInvItemVideoTape); disableObject(4); - return true; + break; - case 0x6849: { - Common::Point p = scene->getPosition(); - if (p.x == 208 && p.y == 151) { - moveRel(0, 0, 2); - } else - moveTo(208, 151, 1); - } - return true; + case csAddr_egoBottomRightTurn: + fnEgoBottomRightTurn(); + break; - case 0x687a: //using the book - if (CHECK_FLAG(0xDBC2, 1)) { - displayMessage(0x4ca0); + case 0x687a: // using the book + if (CHECK_FLAG(dsAddr_drawerPuzzleSolvedFlag, 1)) { + displayMessage(dsAddr_dontMessMsg); // "I don't need to mess with it anymore" } else { playSound(49, 5); playSound(49, 17); playActorAnimation(691); - if (!processCallback(0x68e6)) { - if (!CHECK_FLAG(0xDBC0, 1)) { - displayMessage(0x4c61); - SET_FLAG(0xDBC0, 1); + if (!fnCheckingDrawers()) { + if (!CHECK_FLAG(dsAddr_drawerPuzzleBookMessageFlag, 1)) { + displayMessage(dsAddr_bookHeldMsg); // "Something's got hold of the book!" + SET_FLAG(dsAddr_drawerPuzzleBookMessageFlag, 1); } } else { - playSound(15, 8); //secret compartment + playSound(15, 8); // secret compartment playAnimation(692, 0); setOns(6, 59); enableObject(4); - displayMessage(0x4c84); - SET_FLAG(0xDBC2, 1); + displayMessage(dsAddr_secretCompartmentMsg); // "Wow! A secret compartment!" + SET_FLAG(dsAddr_drawerPuzzleSolvedFlag, 1); } } - return true; + break; - case 0x68e6: { //checking drawers - uint16 v = GET_FLAG(0xDBC1) - 1; - uint bx = 0xDBB7; - if (GET_FLAG(bx + v) != 1) - return false; - - uint16 sum = 0; - for (uint i = 0; i < 6; ++i) { - sum += GET_FLAG(bx + i); - } - return sum == 1; - } + case csAddr_checkingDrawers: + fnCheckingDrawers(); + break; case 0x6918: - if (inventory->has(55)) { - displayMessage(0x4cd9); - return true; - } - if (!CHECK_FLAG(0xDBC3, 1)) { - playActorAnimation(695); - Dialog::showMark(scene, 0x386a); - SET_FLAG(0xDBC3, 1); - } + if (inventory->has(kInvItemSheetOfPaper)) + displayMessage(dsAddr_noMoreSheetsMsg); // "Right now I don't need any more sheets" + else { + if (!CHECK_FLAG(dsAddr_mansionTrashcanSearchedFlag, 1)) { + playActorAnimation(695); + dialog->showMark(91, scene); + SET_FLAG(dsAddr_mansionTrashcanSearchedFlag, 1); + } - playSound(5, 11); - playActorAnimation(696); - inventory->add(55); - return true; + playSound(5, 11); + playActorAnimation(696); + inventory->add(kInvItemSheetOfPaper); + } + break; case 0x6962: - if (CHECK_FLAG(0xDBB7, 1)) { + if (CHECK_FLAG(dsAddr_blueDrawerOpenFlag, 1)) { setOns(0, 0); playSound(67, 4); playActorAnimation(678); - SET_FLAG(0xDBB7, 0); - } else if (CHECK_FLAG(0xDBB8, 1)) { - processCallback(0x6b86); + SET_FLAG(dsAddr_blueDrawerOpenFlag, 0); + } else if (CHECK_FLAG(dsAddr_redDrawerOpenFlag, 1)) { + fnDrawerOpenMessage(); } else { playSound(66, 4); playActorAnimation(677); setOns(0, 53); - SET_FLAG(0xDBB7, 1); + SET_FLAG(dsAddr_blueDrawerOpenFlag, 1); } - return true; + break; case 0x69b8: - if (CHECK_FLAG(0xDBB8, 1)) { + if (CHECK_FLAG(dsAddr_redDrawerOpenFlag, 1)) { setOns(1, 0); playSound(67, 4); playActorAnimation(680); - SET_FLAG(0xDBB8, 0); - } else if (CHECK_FLAG(0xDBB7, 1)) { - processCallback(0x6b86); - } else if (CHECK_FLAG(0xDBB9, 1)) { - processCallback(0x6b86); + SET_FLAG(dsAddr_redDrawerOpenFlag, 0); + } else if (CHECK_FLAG(dsAddr_blueDrawerOpenFlag, 1)) { + fnDrawerOpenMessage(); + } else if (CHECK_FLAG(dsAddr_greyDrawerOpenFlag, 1)) { + fnDrawerOpenMessage(); } else { playSound(66, 5); playActorAnimation(679); setOns(1, 54); - SET_FLAG(0xDBB8, 1); + SET_FLAG(dsAddr_redDrawerOpenFlag, 1); } - return true; + break; case 0x6a1b: - if (CHECK_FLAG(0xDBB9, 1)) { + if (CHECK_FLAG(dsAddr_greyDrawerOpenFlag, 1)) { setOns(2, 0); playSound(67, 5); playActorAnimation(682); - SET_FLAG(0xDBB9, 0); - } else if (CHECK_FLAG(0xDBB8, 1)) { - processCallback(0x6b86); + SET_FLAG(dsAddr_greyDrawerOpenFlag, 0); + } else if (CHECK_FLAG(dsAddr_redDrawerOpenFlag, 1)) { + fnDrawerOpenMessage(); } else { playSound(67, 5); playActorAnimation(681); setOns(2, 55); - SET_FLAG(0xDBB9, 1); + SET_FLAG(dsAddr_greyDrawerOpenFlag, 1); } - return true; + break; case 0x6a73: - if (CHECK_FLAG(0xDBBA, 1)) { + if (CHECK_FLAG(dsAddr_greenDrawerOpenFlag, 1)) { setOns(3, 0); playSound(67, 4); playActorAnimation(684); - SET_FLAG(0xDBBA, 0); - } else if (!CHECK_FLAG(0xDBBB, 1)) { + SET_FLAG(dsAddr_greenDrawerOpenFlag, 0); + } else if (!CHECK_FLAG(dsAddr_brownDrawerOpenFlag, 1)) { playSound(66, 4); playActorAnimation(683); setOns(3, 56); - SET_FLAG(0xDBBA, 1); + SET_FLAG(dsAddr_greenDrawerOpenFlag, 1); } else - processCallback(0x6b86); - return true; + fnDrawerOpenMessage(); + break; case 0x6acb: - if (CHECK_FLAG(0xDBBB, 1)) { + if (CHECK_FLAG(dsAddr_brownDrawerOpenFlag, 1)) { setOns(4, 0); playSound(67, 4); playActorAnimation(686); - SET_FLAG(0xDBBB, 0); - } else if (CHECK_FLAG(0xDBBA, 1)) { - processCallback(0x6b86); - } else if (CHECK_FLAG(0xDBBC, 1)) { - processCallback(0x6b86); + SET_FLAG(dsAddr_brownDrawerOpenFlag, 0); + } else if (CHECK_FLAG(dsAddr_greenDrawerOpenFlag, 1)) { + fnDrawerOpenMessage(); + } else if (CHECK_FLAG(dsAddr_pinkDrawerOpenFlag, 1)) { + fnDrawerOpenMessage(); } else { playSound(66, 5); playActorAnimation(685); setOns(4, 57); - SET_FLAG(0xDBBB, 1); + SET_FLAG(dsAddr_brownDrawerOpenFlag, 1); } - return true; + break; case 0x6b2e: - if (CHECK_FLAG(0xdbbc, 1)) { + if (CHECK_FLAG(dsAddr_pinkDrawerOpenFlag, 1)) { setOns(5, 0); playSound(67, 5); playActorAnimation(688); - SET_FLAG(0xdbbc, 0); - } else if (CHECK_FLAG(0xdbbb, 1)) { - processCallback(0x6b86); + SET_FLAG(dsAddr_pinkDrawerOpenFlag, 0); + } else if (CHECK_FLAG(dsAddr_brownDrawerOpenFlag, 1)) { + fnDrawerOpenMessage(); } else { playSound(66, 6); playActorAnimation(687); setOns(5, 58); - SET_FLAG(0xDBBC, 1); + SET_FLAG(dsAddr_pinkDrawerOpenFlag, 1); } - return true; - + break; - case 0x6b86: - if (CHECK_FLAG(0xDBBD, 1)) { - displayMessage(0x4b39); - } else { - displayMessage(0x4acd); - displayMessage(0x4b0d); - SET_FLAG(0xDBBD, 1); - } - return true; + case csAddr_DrawerOpenMessage: + fnDrawerOpenMessage(); + break; - case 0x6be1: //handle to the bathroom - if (CHECK_FLAG(0xDBD9, 1)) { - displayMessage(0x5326); //i'd better catch johnny - } else { + case 0x6be1: // handle to the bathroom + if (CHECK_FLAG(dsAddr_MansionJohnNotyEscapingFlag, 1)) + displayMessage(dsAddr_catchJohnFirstMsg); // "I'd better catch John Noty first" + else { playSound(88, 4); playActorAnimation(808); loadScene(36, 41, 195, 2); } - return true; + break; case 0x6bad: playSound(80, 4); playActorAnimation(971); loadScene(32, 139, 199, 1); - return true; + break; case 0x6c45: playSound(89, 6); - playActorAnimation(CHECK_FLAG(0xDBEF, 1) ? 985 : 806); + playActorAnimation(CHECK_FLAG(dsAddr_mansionHandleInDoorHoleFlag, 1) ? 985 : 806); loadScene(34, 40, 133, 2); - return true; + break; case 0x6c83: waitLanAnimationFrame(1, 1); - Dialog::pop(scene, 0xdb2e, 0, 727, 0xd1, 0xef, 0, 1); - scene->getObject(1)->setName((const char *)res->dseg.ptr(0xaa94)); - SET_FLAG(0xDBD1, 1); - return true; + dialog->pop(scene, dsAddr_dialogStackRobotSafe, 0, 727, textColorMark, textColorMike, 0, 1); + scene->getObject(1)->setName((const char *)res->dseg.ptr(dsAddr_scnObjNameMike)); + SET_FLAG(dsAddr_MansionRobotSafeUnlockedFlag, 1); + break; - case 0x6c9d: //getting jar + case 0x6c9d: // getting jar setOns(0, 71); playSound(32, 5); playActorAnimation(732); disableObject(2); - inventory->add(72); - return true; + inventory->add(kInvItemTimePills); + break; - case 0x6cc4: //secret diary + case 0x6cc4: // secret diary playActorAnimation(754); hideActor(); - displayCutsceneMessage(0x517b, 30430); + displayCutsceneMessage(dsAddr_cutsceneMsg0, 30, 95); // "A secret diary of ..." playMusic(3); loadScene(11, scene->getPosition()); playAnimation(750, 2); - Dialog::show(scene, 0x4f50, 751, 529, 0xe5, 0xd9, 2, 1); + dialog->show(117, scene, 751, 529, textColorProfessor, textColorMansionGuard, 2, 1); playAnimation(752, 0, true); playAnimation(753, 1, true); waitAnimation(); - Dialog::show(scene, 0x5168, 529, 751, 0xd9, 0xe5, 1, 2); + dialog->show(118, scene, 529, 751, textColorMansionGuard, textColorProfessor, 1, 2); loadScene(30, scene->getPosition()); - Dialog::show(scene, 0x449e, 733, 734, 0xe5, 0xd0, 2, 3); + dialog->show(108, scene, 733, 734, textColorProfessor, textColorJohnNoty, 2, 3); playSound(75, 13); playSound(32, 22); playAnimation(735, 1, true); playAnimation(736, 2, true); waitAnimation(); - Dialog::show(scene, 0x46cf, 737, 738, 0xd0, 0xe5, 3, 2); - + dialog->show(109, scene, 737, 738, textColorJohnNoty, textColorProfessor, 3, 2); playSound(32, 1); playAnimation(739, 1, true); playAnimation(740, 2, true); waitAnimation(); - Dialog::show(scene, 0x4772, 733, 734, 0xe5, 0xd0, 2, 3); + dialog->show(110, scene, 733, 734, textColorProfessor, textColorJohnNoty, 2, 3); playAnimation(742, 1, true); playAnimation(741, 2, true); waitAnimation(); - Dialog::show(scene, 0x481c, 743, 733, 0xd0, 0xe5, 3, 2); //where's my wallet?? + dialog->show(111, scene, 743, 733, textColorJohnNoty, textColorProfessor, 3, 2); playAnimation(744, 1, true); playAnimation(745, 2, true); waitAnimation(); - Dialog::show(scene, 0x4873, 734, 733, 0xd0, 0xe5, 3, 2); + dialog->show(112, scene, 734, 733, textColorJohnNoty, textColorProfessor, 3, 2); playAnimation(746, 1, true); playAnimation(747, 2, true); waitAnimation(); - - Dialog::show(scene, 0x4da5, 734, 734, 0xd0, 0xd0, 3, 3); - Dialog::show(scene, 0x4eb9, 748, 748, 0xd0, 0xd0, 3, 3); - Dialog::show(scene, 0x4f15, 749, 749, 0xd0, 0xd0, 3, 3); - Dialog::show(scene, 0x4f2f, 748, 748, 0xd0, 0xd0, 3, 3); + dialog->show(113, scene, 734, 734, textColorJohnNoty, textColorJohnNoty, 3, 3); + dialog->show(114, scene, 748, 748, textColorJohnNoty, textColorJohnNoty, 3, 3); + dialog->show(115, scene, 749, 749, textColorJohnNoty, textColorJohnNoty, 3, 3); + dialog->show(116, scene, 748, 748, textColorJohnNoty, textColorJohnNoty, 3, 3); playMusic(10); loadScene(32, scene->getPosition()); @@ -3296,35 +4168,34 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playActorAnimation(755); moveRel(0, 0, 3); - Dialog::show(scene, 0x51bf, 0, 0, 0xd1, 0xd1, 0, 0); + dialog->showMark(119, scene); hideActor(); loadScene(31, scene->getPosition()); - Dialog::show(scene, 0x539f, 763, 764, 0xd9, 0xd0, 1, 2); + dialog->show(123, scene, 763, 764, textColorMansionGuard, textColorJohnNoty, 1, 2); loadScene(32, scene->getPosition()); showActor(); - Dialog::show(scene, 0x52c3, 0, 0, 0xd1, 0xd1, 0, 0); //i have to hide somewhere + dialog->showMark(120, scene); disableObject(3); enableObject(7); - SET_FLAG(0xDBD5, 1); - return true; + SET_FLAG(dsAddr_MansionJohnNotyOutsideBathroomFlag, 1); + break; case 0x6f20: - if (CHECK_FLAG(0xDBD5, 1)) { - displayMessage(0x51a7); - } else { + if (CHECK_FLAG(dsAddr_MansionJohnNotyOutsideBathroomFlag, 1)) + displayMessage(dsAddr_cantHideMsg); // "I can't hide here!" + else rejectMessage(); - } - return true; + break; - case 0x6f75: //hiding in left corner + case 0x6f75: // hiding in left corner moveRel(0, 0, 3); playActorAnimation(756); hideActor(); playAnimation(758, 1); - Dialog::show(scene, 0x52e6, 759, 759, 0xd0, 0xd0, 2, 2); //I have to buy... + dialog->show(121, scene, 759, 759, textColorJohnNoty, textColorJohnNoty, 2, 2); playSound(40, 5); playSound(52, 13); @@ -3340,272 +4211,265 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playSound(58, 12); playSound(58, 14); playAnimation(765, 1); - Dialog::show(scene, 0x5443, 766, 766, 0xd9, 0xd9, 1, 1); + dialog->show(124, scene, 766, 766, textColorMansionGuard, textColorMansionGuard, 1, 1); loadScene(32, scene->getPosition()); - Dialog::show(scene, 0x5358, 761, 761, 0xd0, 0xd0, 2, 2); + dialog->show(122, scene, 761, 761, textColorJohnNoty, textColorJohnNoty, 2, 2); playAnimation(762, 1); setOns(2, 0); showActor(); playActorAnimation(757); moveRel(0, 0, 1); - displayMessage(0x51e7); + displayMessage(dsAddr_wasCloseMsg); // "That was close" enableObject(8); disableObject(7); - SET_FLAG(0xDBD5, 0); - return true; + SET_FLAG(dsAddr_MansionJohnNotyOutsideBathroomFlag, 0); + break; case 0x6f4d: - if (CHECK_FLAG(0xDBD5, 1)) { - displayMessage(0x51bb); - } else { + if (CHECK_FLAG(dsAddr_MansionJohnNotyOutsideBathroomFlag, 1)) + displayMessage(dsAddr_johnOutsideMsg); // "There's John Noty outside! I can't go out!" + else loadScene(31, 139, 172, 3); - } - return true; + break; case 0x6f32: - if (CHECK_FLAG(0xDBD5, 1)) { - displayMessage(0x51a7); + if (CHECK_FLAG(dsAddr_MansionJohnNotyOutsideBathroomFlag, 1)) { + displayMessage(dsAddr_cantHideMsg); // "I can't hide here!" } else { playActorAnimation(977); - displayMessage(0x5511); + displayMessage(dsAddr_lockedMsg); // "It's Locked!" } - return true; + break; case 0x7096: playSound(32, 5); playActorAnimation(767); setOns(1, 0); - inventory->add(73); + inventory->add(kInvItemHandle); disableObject(8); - return true; + break; + + case 0x7218: + rejectMessage(); + break; case 0x7291: playSound(89, 3); playActorAnimation(975); loadScene(31, 298, 177, 4); - return true; + break; case 0x72c2: - if (CHECK_FLAG(0xDBD6, 2)) { - displayMessage(0x522c); + if (CHECK_FLAG(dsAddr_MansionSinkState, 2)) { + displayMessage(dsAddr_enoughWaterMsg); // "There's enough water in the sink" } else { playSound(79, 6); playSound(84, 9); playActorAnimation(801); wait(50); - if (CHECK_FLAG(0xDBD6, 1)) { - displayMessage(0x538d); - SET_FLAG(0xDBD6, 2); + if (CHECK_FLAG(dsAddr_MansionSinkState, 1)) { + displayMessage(dsAddr_sinkFullMsg); // "The sink is full of hot water" + SET_FLAG(dsAddr_MansionSinkState, 2); } else - displayMessage(0x5372); + displayMessage(dsAddr_waterHotMsg); // "The water looks very hot" } - return true; + break; case 0x7309: playSound(66, 5); playSound(67, 11); playActorAnimation(976); - displayMessage(0x5955); - return true; + displayMessage(dsAddr_emptyMsg); // "It's Empty" + break; case 0x77d5: - if (CHECK_FLAG(0xdbd7, 1) && !CHECK_FLAG(0xdbd8, 1)) { //disallow exiting through the first door until switch turned on, not present in original game - displayMessage(0x52cb); - return true; + if (CHECK_FLAG(dsAddr_MansionThruFanByTimePillFlag, 1) && !CHECK_FLAG(dsAddr_MansionVentFanStoppedFlag, 1)) { // disallow exiting through the first door until switch turned on, not present in original game + displayMessage(dsAddr_noSaladMsg); // "I don't want to turn myself into a salad" + } else { + playSound(89, 6); + playActorAnimation(978); + loadScene(31, 298, 177, 4); } - playSound(89, 6); - playActorAnimation(978); - loadScene(31, 298, 177, 4); - return true; + break; case 0x79e4: - processCallback(0x6849); - return false; + fnEgoBottomRightTurn(); + retVal = false; + break; - case 0x79eb: //color of the book - displayMessage(res->dseg.get_word(0x5f3c + GET_FLAG(0xDBC1) * 2 - 2)); - return true; + case 0x79eb: // color of the book + // FIXME - Replace with internal lookup and switch + displayMessage(res->dseg.get_word(dsAddr_bookColorMsgPtr + GET_FLAG(dsAddr_drawerPuzzleBookValue) * 2 - 2)); + break; case 0x79fd: - if (CHECK_FLAG(0xDBB7, 1)) { - displayMessage(0x4b6c); - return true; - } else - return false; + if (CHECK_FLAG(dsAddr_blueDrawerOpenFlag, 1)) + displayMessage(dsAddr_blueInteriorMsg); // "It's got a blue interior" + else + retVal = false; + break; case 0x7a0f: - if (CHECK_FLAG(0xDBB8, 1)) { - if (!CHECK_FLAG(0xDBBF, 1)) { - displayMessage(0x4c32); + if (CHECK_FLAG(dsAddr_redDrawerOpenFlag, 1)) { + if (!CHECK_FLAG(dsAddr_drawerGotPolaroidFlag, 1)) { + displayMessage(dsAddr_foundPolaroidMsg); // "There's a polaroid inside! I might need that" playSound(5, 11); playActorAnimation(690); - inventory->add(53); - SET_FLAG(0xDBBF, 1); + inventory->add(kInvItemPolaroidCamera); + SET_FLAG(dsAddr_drawerGotPolaroidFlag, 1); } - displayMessage(0x4b87); - return true; + displayMessage(dsAddr_redInteriorMsg); // "It's got a red interior" } else - return false; + retVal = false; + break; case 0x7a49: - if (CHECK_FLAG(0xDBB9, 1)) { - displayMessage(0x4ba1); - return true; - } else - return false; + if (CHECK_FLAG(dsAddr_greyDrawerOpenFlag, 1)) + displayMessage(dsAddr_greyInteriorMsg); // "It's got a grey interior" + else + retVal = false; + break; case 0x7a5b: - if (CHECK_FLAG(0xDBBA, 1)) { - displayMessage(0x4bbc); - return true; - } else - return false; + if (CHECK_FLAG(dsAddr_greenDrawerOpenFlag, 1)) + displayMessage(dsAddr_greenInteriorMsg); // "It's got a green interior" + else + retVal = false; + break; case 0x7a6d: - if (CHECK_FLAG(0xDBBB, 1)) { - displayMessage(0x4bd8); - return true; - } else - return false; + if (CHECK_FLAG(dsAddr_brownDrawerOpenFlag, 1)) + displayMessage(dsAddr_brownInteriorMsg); // "It's got a brown interior" + else + retVal = false; + break; case 0x7a7f: - if (CHECK_FLAG(0xDBBC, 1)) { - if (!CHECK_FLAG(0xDBBE, 1)) { - displayMessage(0x4c0f); //there's dictaphone inside! + if (CHECK_FLAG(dsAddr_pinkDrawerOpenFlag, 1)) { + if (!CHECK_FLAG(dsAddr_drawerGotDictaphoneFlag, 1)) { + displayMessage(dsAddr_dictaphoneInsideMsg); // "Wow! There's a dictaphone inside!" playSound(5, 12); playActorAnimation(689); - inventory->add(52); - SET_FLAG(0xDBBE, 1); + inventory->add(kInvItemDictaphoneNoBatteries); + SET_FLAG(dsAddr_drawerGotDictaphoneFlag, 1); } - displayMessage(0x4bf4); - return true; + displayMessage(dsAddr_pinkInteriorMsg); // "It's got a pink interior" } else - return false; + retVal = false; + break; case 0x7af7: - if (CHECK_FLAG(0xDBD0, 1)) { - displayMessage(0x5082); - return true; - } else - return false; - - case 0x7b09: { - byte v = GET_FLAG(0xDBD6); - switch (v) { - case 1: - displayMessage(0x51f8); - return true; - case 2: - displayMessage(0x538d); - return true; - default: - return false; - } - } + if (CHECK_FLAG(dsAddr_MansionPutBurningPaperInFridgeFlag, 1)) + displayMessage(dsAddr_yummyMsg); // "Yummy" + else + retVal = false; + break; - case 0x9166: - if (CHECK_FLAG(0xDBD1, 1)) { - return true; - } else { - displayMessage(0x50a6); - return false; + case 0x7b09: + { + byte v = GET_FLAG(dsAddr_MansionSinkState); + switch (v) { + case 1: + displayMessage(dsAddr_corkInHoleMsg); // "The cork is stuck in the hole" + break; + case 2: + displayMessage(dsAddr_sinkFullMsg); // "The sink is full of hot water" + break; + default: + retVal = false; + break; + } } + break; - case 0x9175: - if (CHECK_FLAG(0xDBD2, 0) || CHECK_FLAG(0xDBD3, 0) || CHECK_FLAG(0xDBD4, 0)) - return true; + case csAddr_robotSafeAlreadyUnlockedCheck: + fnRobotSafeAlreadyUnlockedCheck(); + break; - waitLanAnimationFrame(1, 1); - playSound(89, 2); - playActorAnimation(731); - setOns(0, 70); - setLan(1, 0); - disableObject(1); - enableObject(2); - enableObject(3); - return true; + case csAddr_robotSafeUnlockCheck: + fnRobotSafeUnlockCheck(); + break; - case 0x90bc: //handle on the hole + case 0x90bc: // handle on the hole playSound(5, 3); playSound(6, 9); playActorAnimation(807); setOns(0, 83); - inventory->remove(73); + inventory->remove(kInvItemHandle); disableObject(2); enableObject(3); - SET_FLAG(0xDBEF, 1); - return true; - - case 0x90fc: //dictaphone on robot - if (!processCallback(0x9166)) - return true; - - if (CHECK_FLAG(0xDBD2, 1)) { - displayMessage(0x50c3); - return true; - } + SET_FLAG(dsAddr_mansionHandleInDoorHoleFlag, 1); + break; - if (!CHECK_FLAG(0xDBCB, 1)) { - displayMessage(0x5101); - return true; + case 0x90fc: // dictaphone on robot + if (fnRobotSafeAlreadyUnlockedCheck()) { + if (CHECK_FLAG(dsAddr_MansionRobotSafeVoiceTestPassedFlag, 1)) { + displayMessage(dsAddr_fooledOnceMsg); // "I'd already fooled him once" + } else { + if (!CHECK_FLAG(dsAddr_usedDictaphoneOnTVFlag, 1)) { + displayMessage(dsAddr_notMyVoiceMsg); // "I won't cheat Mike with MY voice" + } else { + displayMessage(dsAddr_mikeVoiceTestMsg); // "Mike, activate the voice test" + waitLanAnimationFrame(1, 1); + + playSound(5, 3); + playSound(5, 39); + displayAsyncMessage(dsAddr_singingMsg, 68, 126, 9, 35, textColorJohnNoty); // "siiiiinging!" + playActorAnimation(728); + + waitLanAnimationFrame(1, 1); + dialog->show(98, scene, 0, 727, textColorMark, textColorMike, 0, 1); + SET_FLAG(dsAddr_MansionRobotSafeVoiceTestPassedFlag, 1); + fnRobotSafeUnlockCheck(); + } + } } + break; - displayMessage(0x50e1); - waitLanAnimationFrame(1, 1); - - playSound(5, 3); - playSound(5, 39); - displayAsyncMessage(0x5124, 40388, 9, 35, 0xd0); - playActorAnimation(728); - - waitLanAnimationFrame(1, 1); - Dialog::show(scene, 0x3d17, 0, 727, 0xd1, 0xef, 0, 1); - SET_FLAG(0xDBD2, 1); - processCallback(0x9175); - return true; + case 0x91cb: // use socks on robot + if (fnRobotSafeAlreadyUnlockedCheck()) { + if (CHECK_FLAG(dsAddr_MansionRobotSafeScentTestPassedFlag, 1)) { + displayMessage(dsAddr_fooledOnceMsg); // "I'd already fooled him once" + } else { + displayMessage(dsAddr_mikeScentTestMsg); // "Mike, let's get on with the scent test" - case 0x91cb: //use socks on robot - if (!processCallback(0x9166)) - return true; + waitLanAnimationFrame(1, 1); + playSound(5, 3); + playSound(5, 23); + playActorAnimation(729); - if (CHECK_FLAG(0xDBD3, 1)) { - displayMessage(0x50c3); - return true; + waitLanAnimationFrame(1, 1); + dialog->show(99, scene, 0, 727, textColorMark, textColorMike, 0, 1); + SET_FLAG(dsAddr_MansionRobotSafeScentTestPassedFlag, 1); + fnRobotSafeUnlockCheck(); + } } - displayMessage(0x5138); - - waitLanAnimationFrame(1, 1); - playSound(5, 3); - playSound(5, 23); - playActorAnimation(729); + break; - waitLanAnimationFrame(1, 1); - Dialog::show(scene, 0x3d70, 0, 727, 0xd1, 0xef, 0, 1); - SET_FLAG(0xDBD3, 1); - processCallback(0x9175); - return true; + case 0x9209: // photo on robot + if (fnRobotSafeAlreadyUnlockedCheck()) { + if (CHECK_FLAG(dsAddr_MansionRobotSafeViewTestPassedFlag, 1)) { + displayMessage(dsAddr_fooledOnceMsg); // "I'd already fooled him once" + } else { + displayMessage(dsAddr_mikeViewTestMsg); // "Mike, run the view test" + waitLanAnimationFrame(1, 1); - case 0x9209: //photo on robot - if (!processCallback(0x9166)) - return true; + playSound(5, 3); + playSound(5, 25); + playActorAnimation(730); - if (CHECK_FLAG(0xDBD4, 1)) { - displayMessage(0x50c3); - return true; + waitLanAnimationFrame(1, 1); + dialog->show(100, scene, 0, 727, textColorMark, textColorMike, 0, 1); + SET_FLAG(dsAddr_MansionRobotSafeViewTestPassedFlag, 1); + fnRobotSafeUnlockCheck(); + } } - displayMessage(0x5161); - waitLanAnimationFrame(1, 1); - - playSound(5, 3); - playSound(5, 25); - playActorAnimation(730); + break; - waitLanAnimationFrame(1, 1); - Dialog::show(scene, 0x3dd6, 0, 727, 0xd1, 0xef, 0, 1); - SET_FLAG(0xDBD4, 1); - processCallback(0x9175); - return true; + case 0x9247: + displayMessage(dsAddr_sameBottleMsg); // "The bottle's the same, but I doubt if it's enough to fool anyone" + break; case 0x924e: setOns(2, 64); @@ -3613,9 +4477,9 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playSound(52, 10); playActorAnimation(711); moveRel(0, 0, 4); - Dialog::show(scene, 0x3b21, 0, 709, 0xd1, 0xef, 0, 1); + dialog->show(95, scene, 0, 709, textColorMark, textColorCook, 0, 1); moveTo(300, 190, 4); - inventory->remove(64); + inventory->remove(kInvItemFakeChilli); disableObject(8); playAnimation(712, 0); setOns(2, 0); @@ -3623,90 +4487,90 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playSound(15, 28); playSound(16, 37); playAnimation(713, 0); - Dialog::show(scene, 0x3c0d, 0, 709, 0xd1, 0xef, 0, 1); + dialog->show(96, scene, 0, 709, textColorMark, textColorCook, 0, 1); playSound(85, 2); playAnimation(714, 0); setLan(1, 0); disableObject(1); { Object *obj = scene->getObject(2); - obj->actor_rect.left = obj->actor_rect.right = 81; - obj->actor_rect.top = obj->actor_rect.bottom = 160; - obj->actor_orientation = 4; + obj->actorRect.left = obj->actorRect.right = 81; + obj->actorRect.top = obj->actorRect.bottom = 160; + obj->actorOrientation = 4; obj->save(); } { Object *obj = scene->getObject(3); - obj->actor_rect.left = obj->actor_rect.right = 64; - obj->actor_rect.top = obj->actor_rect.bottom = 168; - obj->actor_orientation = 4; + obj->actorRect.left = obj->actorRect.right = 64; + obj->actorRect.top = obj->actorRect.bottom = 168; + obj->actorOrientation = 4; obj->save(); } { Object *obj = scene->getObject(10); - obj->actor_rect.left = obj->actor_rect.right = 105; - obj->actor_rect.top = obj->actor_rect.bottom = 160; - obj->actor_orientation = 1; + obj->actorRect.left = obj->actorRect.right = 105; + obj->actorRect.top = obj->actorRect.bottom = 160; + obj->actorOrientation = 1; obj->save(); } - SET_FLAG(0xDBCC, 1); - return true; + SET_FLAG(dsAddr_MansionCookGoneFlag, 1); + break; case 0x9472: playSound(5, 4); playSound(19, 14); playActorAnimation(793); - displayMessage(0x5218); - inventory->remove(60); - SET_FLAG(0xDBD6, 1); - return true; + displayMessage(dsAddr_fitsPerfectMsg); // "It fits perfectly!" + inventory->remove(kInvItemWrappedCork); + SET_FLAG(dsAddr_MansionSinkState, 1); + break; - case 0x9449: //meat + stew + case 0x9449: // meat + stew playSound(5, 4); playSound(63, 12); playActorAnimation(726); - displayMessage(0x508a); - inventory->remove(69); - inventory->add(70); - return true; + displayMessage(dsAddr_dislikeVealMsg); // "I never liked veal anyway" + inventory->remove(kInvItemMeat); + inventory->add(kInvItemPlasticBag); + break; case 0x949b: - if (CHECK_FLAG(0xDBD6, 2)) { + if (CHECK_FLAG(dsAddr_MansionSinkState, 2)) { playSound(5, 4); playSound(5, 25); playActorAnimation(802); - displayMessage(0x5272); - inventory->remove(62); - inventory->add(74); - inventory->add(65); + displayMessage(dsAddr_labelOffMsg); // "The label has come off!" + inventory->remove(kInvItemChilliWithLabel); + inventory->add(kInvItemChilliNoLabel); + inventory->add(kInvItemLabel); } else - displayMessage(0x524f); - return true; + displayMessage(dsAddr_noHotWaterMsg); // "There's no hot water in the sink" + break; case 0x94d4: - if (inventory->has(70)) { + if (inventory->has(kInvItemPlasticBag)) { setOns(0, 0); playSound(5, 3); playSound(5, 18); playSound(13, 12); playActorAnimation(803); disableObject(7); - inventory->remove(70); - inventory->add(71); + inventory->remove(kInvItemPlasticBag); + inventory->add(kInvItemSocks); } else - displayMessage(0x53ad); - return true; + displayMessage(dsAddr_noSockStoreMsg); // "I don't have anything to store these socks in" + break; case 0x951b: playSound(5, 4); playSound(5, 22); playActorAnimation(804); - displayMessage(0x528b); - return true; + displayMessage(dsAddr_corkTooSmallMsg); // "The cork is a bit too small" + break; case 0x73a3: - if (CHECK_FLAG(0xdbc5, 1)) { - SET_FLAG(0xdbc5, 0); + if (CHECK_FLAG(dsAddr_mansionTVOnFlag, 1)) { + SET_FLAG(dsAddr_mansionTVOnFlag, 0); //call 73e6 playSound(71, 3); @@ -3714,92 +4578,94 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playAnimation(0, 0, true); reloadLan(); - if (CHECK_FLAG(0xDBC6, 1)) { - displayMessage(0x4da6); + if (CHECK_FLAG(dsAddr_mansionVCRPlayingTapeFlag, 1)) { + displayMessage(dsAddr_muchBetterMsg); // "That's much better" } } else { - SET_FLAG(0xdbc5, 1); + SET_FLAG(dsAddr_mansionTVOnFlag, 1); //call 73e6 playSound(71, 3); playActorAnimation(700); reloadLan(); } - return true; + break; - case 0x9537: //using remote on VCR + case 0x9537: // using remote on VCR playSound(5, 3); playSound(5, 16); playActorAnimation(703); - if (!CHECK_FLAG(0xDBC8, 1)) { - displayMessage(0x4D80); //nothing happened - return true; - } - - //0x955a - if (CHECK_FLAG(0xDBC6, 0)) { - if (CHECK_FLAG(0xDBC5, 1)) { //tv on - if (!CHECK_FLAG(0xDBC7, 1)) - displayMessage(0x4d93); //the tape started - - SET_FLAG(0xDBC6, 1); - reloadLan(); - if (!CHECK_FLAG(0xDBC7, 1)) { - Dialog::show(scene, 0x392c, 0, 702, 0xd1, 0xd0, 0, 1); - SET_FLAG(0xDBC7, 1); + if (!CHECK_FLAG(dsAddr_mansionVCRTapeLoadedFlag, 1)) + displayMessage(dsAddr_NotHappenMsg); // "Nothing happened" + else { + //0x955a + if (CHECK_FLAG(dsAddr_mansionVCRPlayingTapeFlag, 0)) { + if (CHECK_FLAG(dsAddr_mansionTVOnFlag, 1)) { + if (!CHECK_FLAG(dsAddr_mansionVCRPlayedTapeBeforeFlag, 1)) + displayMessage(dsAddr_tapeStartedMsg); // "The tape started!" + + SET_FLAG(dsAddr_mansionVCRPlayingTapeFlag, 1); + reloadLan(); + if (!CHECK_FLAG(dsAddr_mansionVCRPlayedTapeBeforeFlag, 1)) { + dialog->show(93, scene, 0, 702, textColorMark, textColorJohnNoty, 0, 1); + SET_FLAG(dsAddr_mansionVCRPlayedTapeBeforeFlag, 1); + } + } else + displayMessage(dsAddr_tvOffMsg); // "I just realised that the TV is off" + } else { + SET_FLAG(dsAddr_mansionVCRPlayingTapeFlag, 0); + if (CHECK_FLAG(dsAddr_mansionTVOnFlag, 1)) { + reloadLan(); + displayMessage(dsAddr_muchBetterMsg); // "That's much better" } - } else - displayMessage(0x4d5b); //i just realized that tv is off - } else { - SET_FLAG(0xDBC6, 0); - if (CHECK_FLAG(0xDBC5, 1)) { //tv on - reloadLan(); - displayMessage(0x4da6); //much better! } } - return true; + break; - case 0x95eb: //polaroid + tv - if (CHECK_FLAG(0xDBC6, 1)) { - if (CHECK_FLAG(0xDBCA, 1)) { - displayMessage(0x4de6); + case 0x95eb: // polaroid + tv + if (CHECK_FLAG(dsAddr_mansionVCRPlayingTapeFlag, 1)) { + if (CHECK_FLAG(dsAddr_usedPolaroidOnTVFlag, 1)) { + displayMessage(dsAddr_enoughPhotosMsg); // "I don't need any more photos" } else { playSound(5, 3); playSound(5, 24); playSound(90, 18); playActorAnimation(707); - inventory->add(61); - SET_FLAG(0xDBCA, 1); + inventory->add(kInvItemPhoto); + SET_FLAG(dsAddr_usedPolaroidOnTVFlag, 1); } } else - displayMessage(0x4ea5); - return true; + displayMessage(dsAddr_notRightMomentMsg); // "I don't think this is the right moment" + break; - case 0x962f: //polaroid + tv - if (CHECK_FLAG(0xDBC6, 1)) { - if (CHECK_FLAG(0xDBCB, 1)) { - displayMessage(0x4e32); + case 0x962f: // dictaphone + tv + if (CHECK_FLAG(dsAddr_mansionVCRPlayingTapeFlag, 1)) { + if (CHECK_FLAG(dsAddr_usedDictaphoneOnTVFlag, 1)) { + displayMessage(dsAddr_alreadyRecordedMsg); // "I already recorded what I wanted to" } else { - displayMessage(0x4e05); + displayMessage(dsAddr_recordScareMsg); // "Yeah, I can record this and scare the cats" playSound(5, 3); playSound(5, 27); playActorAnimation(708); - SET_FLAG(0xDBCB, 1); + SET_FLAG(dsAddr_usedDictaphoneOnTVFlag, 1); } } else - displayMessage(0x4ea5); - return true; - + displayMessage(dsAddr_notRightMomentMsg); // "I don't think this is the right moment" + break; case 0x95c8: playSound(5, 3); playSound(91, 12); playActorAnimation(706); - inventory->remove(54); - SET_FLAG(0xDBC8, 1); - return true; + inventory->remove(kInvItemVideoTape); + SET_FLAG(dsAddr_mansionVCRTapeLoadedFlag, 1); + break; - case 0x9673: //hit fatso - final scene + case 0x966c: + displayMessage(dsAddr_cantRecordNoBatteriesMsg); // "I can't record anything until I find some batteries" + break; + + case 0x9673: // hit fatso - final scene playSound(5, 3); playSound(24, 10); playActorAnimation(798); @@ -3811,12 +4677,12 @@ bool TeenAgentEngine::processCallback(uint16 addr) { wait(100); playActorAnimation(805); moveTo(50, 170, 3); - displayMessage(0x5349); + displayMessage(dsAddr_onlyChilliMsg); // "Good this red stuff is only a chilli" //moveTo(105, 157, 0, true); playMusic(3); loadScene(11, 105, 157, 4); - Dialog::show(scene, 0x8409, 0, 938, 0xd1, 0xec, 0, 1); + dialog->show(203, scene, 0, 938, textColorMark, textColorCaptain, 0, 1); playAnimation(939, 0, true, true); playActorAnimation(942, true); @@ -3837,9 +4703,9 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playActorAnimation(945, true); waitAnimation(); - Dialog::show(scene, 0x844f, 0, 938, 0xd1, 0xec, 0, 1); + dialog->show(204, scene, 0, 938, textColorMark, textColorCaptain, 0, 1); playAnimation(946, 0); - Dialog::show(scene, 0x87c7, 0, 938, 0xd1, 0xec, 0, 1); + dialog->show(205, scene, 0, 938, textColorMark, textColorCaptain, 0, 1); playSound(24, 7); playAnimation(948, 0, true); @@ -3847,16 +4713,16 @@ bool TeenAgentEngine::processCallback(uint16 addr) { waitAnimation(); loadScene(40, 198, 186, 1); - Dialog::show(scene, 0x8890, 0, 920, 0xd1, 0xe7, 0, 1); - Dialog::show(scene, 0x8a2f, 0, 921, 0xd1, 0xe7, 0, 1); + dialog->show(206, scene, 0, 920, textColorMark, textColorRGBBoss, 0, 1); + dialog->show(207, scene, 0, 921, textColorMark, textColorRGBBoss, 0, 1); playAnimation(923, 0); - Dialog::show(scene, 0x8aa7, 0, 920, 0xd1, 0xe7, 0, 1); + dialog->show(208, scene, 0, 920, textColorMark, textColorRGBBoss, 0, 1); moveTo(237, 186, 0); moveTo(237, 177, 0); moveTo(192, 177, 4); playAnimation(949, 0); - Dialog::showMono(scene, 0x8af6, 950, 0xe7, 1); + dialog->showMono(209, scene, 950, textColorRGBBoss, 1); playSound(32, 5); playSound(40, 14); @@ -3869,98 +4735,89 @@ bool TeenAgentEngine::processCallback(uint16 addr) { displayCredits(); loadScene(39, 192, 177, 0); hideActor(); - Dialog::showMono(scene, 0x8b4d, 953, 0xe3, 1); //well... + dialog->showMono(210, scene, 953, textColorMarkEnd, 1); playSound(5, 15); playAnimation(954, 0); - Dialog::showMono(scene, 0x8b7a, 955, 0xe3, 1); //that's all folks + dialog->showMono(211, scene, 955, textColorMarkEnd, 1); playMusic(2); - displayCredits(0xe47c, 4500); //3 minutes (infinite until key pressed in original) + displayCredits(dsAddr_finalCredits6, 4500); // 3 minutes (infinite until key pressed in original) scene->push(SceneEvent(SceneEvent::kQuit)); + break; - return true; - - case 0x9921: { //using diving eq - int id = scene->getId(); - if (id != 15) { - displayMessage(id == 16 ? 0x38ce : 0x38a7); - } else { - playSound(5, 3); - playSound(38, 16); - playSound(38, 22); - playActorAnimation(614); - playSound(5, 3); - playSound(44, 10); - playSound(20, 26); - playActorAnimation(615); - loadScene(17, 156, 180, 3); - SET_FLAG(0, 4); - playSound(64, 7); - playSound(64, 21); - playSound(64, 42); - playSound(64, 63); - setTimerCallback(0x9a1d, 30); - playActorAnimation(617, false, true); + case csAddr_useDivingEquipment: // using diving eq + // FIXME - Some code is missing here as displayMessage(dsAddr_cantTalkUnderwaterMsg), + // displayMessage(dsAddr_notSwimmingThereMsg), displayMessage(dsAddr_tooLittleAirMsg) + // displayMessage(dsAddr_fishDontWorryMsg) are never called. + { + int id = scene->getId(); + if (id != 15) { + if (id == 16) + displayMessage(dsAddr_notHereMsg); // "Not here" + else + displayMessage(dsAddr_notBestPlaceMsg); // "It's not the best place for diving" + } else { + playSound(5, 3); + playSound(38, 16); + playSound(38, 22); + playActorAnimation(614); + playSound(5, 3); + playSound(44, 10); + playSound(20, 26); + playActorAnimation(615); + loadScene(17, 156, 180, 3); + SET_FLAG(dsAddr_timedCallbackState, 4); + playSound(64, 7); + playSound(64, 21); + playSound(64, 42); + playSound(64, 63); + setTimerCallback(csAddr_noAnchorTimeout, 30); + playActorAnimation(617, false, true); + } } - } - return true; + break; - case 0x9a1d: //no anchor, timeout - SET_FLAG(0, 0); - processCallback(0x9a7a); - INC_FLAG(0xDBA6); - switch (GET_FLAG(0xDBA6)) { + case csAddr_noAnchorTimeout: // no anchor, timeout + SET_FLAG(dsAddr_timedCallbackState, 0); + fnGetOutOfLake(); + INC_FLAG(dsAddr_lakeDivingExitMessage); + switch (GET_FLAG(dsAddr_lakeDivingExitMessage)) { case 1: - displayMessage(0x39ae); + displayMessage(dsAddr_seaweedMsg); // "This seaweed is just like the flowers I gave mum on her last birthday" break; case 2: - displayMessage(0x39f6); + displayMessage(dsAddr_fishBoatMsg); // "I wonder what fish do inside this boat at night" break; case 3: - displayMessage(0x3a28); + displayMessage(dsAddr_fishSomethingMsg); // "I think I have to fish out something down there" break; case 4: - displayMessage(0x3a85); + displayMessage(dsAddr_notRedHerringMsg); // "I hope all this fish stuff is not a red herring" break; case 5: - displayMessage(0x39ae); + displayMessage(dsAddr_seaweedMsg); // "This seaweed is just like the flowers I gave mum on her last birthday" break; default: - displayMessage(0x39b7); + displayMessage(dsAddr_niceDownMsg); // "It's nice down there" + break; } - return true; + break; - case 0x99e0: //success getting an anchor - SET_FLAG(0, 0); - setTimerCallback(0, 0); - scene->getActorAnimation()->free(); - playSound(64, 7); - playActorAnimation(618); - disableObject(5); - setOns(0, 0); - playSound(31, 1); - playActorAnimation(619); - processCallback(0x9a7a); - inventory->add(42); - displayMessage(0x3989); - return true; + case csAddr_gotAnchor: + fnGotAnchor(); + break; - case 0x9a7a: - loadScene(15, 156, 180, 3); - playSound(5, 5); - playSound(38, 14); - playSound(38, 20); - playSound(5, 25); - playActorAnimation(616); - return true; + case csAddr_getOutOfLake: + fnGetOutOfLake(); + break; - case 0x9aca: + case csAddr_digMansionWall: if (scene->getId() == 13) { moveTo(172, 181, 1); playSound(26, 19); for (uint i = 0; i < 8; ++i) playSound(26, 30 + i * 11); playActorAnimation(661); - displayCutsceneMessage(0x3c80, 30484); + displayCutsceneMessage(dsAddr_cutsceneMsgA, 84, 95); // "Hundred moments later" playSound(56, 10); playSound(56, 21); @@ -3974,7 +4831,7 @@ bool TeenAgentEngine::processCallback(uint16 addr) { waitAnimation(); setOns(1, 49); - displayCutsceneMessage(0x3c9a, 30453); + displayCutsceneMessage(dsAddr_cutsceneMsgB, 53, 95); // "Another hundred moments later" moveTo(162, 184, 0, true); playSound(26, 6); playSound(26, 17); @@ -3986,27 +4843,32 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playActorAnimation(664); playAnimation(665, 1); wait(100); - displayMessage(0x3cbc); + displayMessage(dsAddr_foundCrudeOilMsg); // "At least I found crude oil and I'll be rich" wait(100); - displayMessage(0x3cea); - inventory->remove(37); - processCallback(0x9d45); //another mansion try + displayMessage(dsAddr_myLifeMsg); // "That's my life" + inventory->remove(kInvItemShovelAct2); + fnMansionIntrusionAttempt(); } else - displayMessage(0x3c58); - return true; + displayMessage(dsAddr_notThinkRightPlaceMsg); // "I don't think this is the right place" + break; - case 0x9c6d: - displayMessage(0x49d1); - SET_FLAG(0xDBB5, 1); - return false; + case csAddr_tooDarkHere: + displayMessage(dsAddr_cantDoTooDarkMsg); // "I can't do anything here, it's too dark" + break; + + case csAddr_examineBanknote: + displayMessage(dsAddr_bankNoteMsg); // "It's a note from some bank..." + SET_FLAG(dsAddr_examinedBanknoteFlag, 1); + retVal = false; + break; - case 0x9c79: //use pills + case csAddr_useTimePills: // use pills if (scene->getId() != 36) { - displayMessage(0x52a9); - } else if (CHECK_FLAG(0xDBF1, 1)) { - displayMessage(0x52F6); + displayMessage(dsAddr_notTryNowMsg); // "There's no need to try them now" + } else if (CHECK_FLAG(dsAddr_mansionAlreadyUsedTimePillsFlag, 1)) { + displayMessage(dsAddr_nahMsg); // "Nah" } else { - SET_FLAG(0xDBF1, 1); + SET_FLAG(dsAddr_mansionAlreadyUsedTimePillsFlag, 1); moveTo(102, 195, 2); playSound(5, 3); playSound(75, 12); @@ -4020,131 +4882,59 @@ bool TeenAgentEngine::processCallback(uint16 addr) { { Walkbox *w = scene->getWalkbox(0); w->rect.left = 0; - w->rect.bottom = 199; + w->rect.bottom = kScreenHeight-1; w->save(); } setLan(1, 0xff); - Dialog::showMark(scene, 0x58a9); + dialog->showMark(130, scene); Object *obj = scene->getObject(1); - obj->actor_rect.left = obj->actor_rect.right = 270; - obj->actor_rect.top = obj->actor_rect.bottom = 193; - obj->actor_orientation = 2; + obj->actorRect.left = obj->actorRect.right = 270; + obj->actorRect.top = obj->actorRect.bottom = 193; + obj->actorOrientation = 2; obj->save(); obj = scene->getObject(3); - obj->actor_rect.left = obj->actor_rect.right = 254; - obj->actor_rect.top = obj->actor_rect.bottom = 193; - obj->actor_orientation = 1; + obj->actorRect.left = obj->actorRect.right = 254; + obj->actorRect.top = obj->actorRect.bottom = 193; + obj->actorOrientation = 1; obj->save(); - SET_FLAG(0xDBD7, 1); + SET_FLAG(dsAddr_MansionThruFanByTimePillFlag, 1); } - return true; + break; - case 0x9d45: { - wait(50); - byte attempts = ++ *(res->dseg.ptr(0xDBEA)); - debug(0, "mansion intrusion attempt #%u", attempts); - if (attempts >= 7) - return false; + case csAddr_mansionIntrusionAttempt: + retVal = fnMansionIntrusionAttempt(); + break; - uint16 ptr = res->dseg.get_word((attempts - 2) * 2 + 0x6035); - debug(0, "mansion callback = %04x", ptr); - byte id = scene->getId(); + case csAddr_secondMansionIntrusion: + fnSecondMansionIntrusion(); + break; - playMusic(11); - displayCutsceneMessage(0x580a, 30484); - processCallback(ptr); - playMusic(6); - if (getFlag(0xdbec) != 1 || ptr != 0x9f3e) //ptr check eq. scene_id == 11 - loadScene(id, scene->getPosition()); - return true; - } + case csAddr_thirdMansionIntrusion: + fnThirdMansionIntrusion(); + break; - case 0x9d90: - hideActor(); - loadScene(34, scene->getPosition()); - playAnimation(986, 0, true); - playAnimation(987, 1, true); - waitAnimation(); - Dialog::show(scene, 0x6f60, 988, 989, 0xd9, 0xd0, 1, 2); - playAnimation(990, 0, true); - playAnimation(991, 1, true); - waitAnimation(); - showActor(); - return true; - - case 0x9de5: - hideActor(); - loadScene(30, scene->getPosition()); - playAnimation(887, 1); - playAnimation(888, 2, true, true, true); - //waitAnimation(); - Dialog::showMono(scene, 0x6fb8, 889, 0xd9, 2); - playSound(26, 3); - playAnimation(891, 1, true, true, true); - playAnimation(892, 2); - waitAnimation(); - Dialog::show(scene, 0x6ff0, 890, 889, 0xd0, 0xd9, 3, 2); - showActor(); - return true; - - case 0x9e54: - hideActor(); - loadScene(32, scene->getPosition()); - playAnimation(894, 1, true, true, true); - playAnimation(893, 2, true); - waitAnimation(); - Dialog::showMono(scene, 0x706e, 895, 0xd9, 3); - playSound(75, 9); - playAnimation(898, 1, true); - playAnimation(897, 2, true); - Dialog::show(scene, 0x7096, 896, 895, 0xd0, 0xd9, 2, 3); - showActor(); - return true; + case csAddr_fourthMansionIntrusion: + fnFourthMansionIntrusion(); + break; - case 0x9ec3: - hideActor(); - loadScene(29, scene->getPosition()); - playActorAnimation(901, true); - playAnimation(900, 1, true); - waitAnimation(); - Dialog::show(scene, 0x7161, 903, 902, 0xd0, 0xd9, 2, 3); - for (byte i = 3; i <= 9; i += 2) - playSound(56, i); + case csAddr_fifthMansionIntrusion: + fnFifthMansionIntrusion(); + break; - playActorAnimation(905, true); - playAnimation(904, 1, true); - Dialog::show(scene, 0x71c6, 903, 902, 0xd0, 0xd9, 2, 3); - showActor(); - return true; + case csAddr_sixthMansionIntrusion: + fnSixthMansionIntrusion(); + break; - case 0x9f3e: - hideActor(); - loadScene(35, scene->getPosition()); - playAnimation(907, 2, true); - playAnimation(906, 3, true); - waitAnimation(); - Dialog::show(scene, 0x7243, 908, 909, 0xd9, 0xd0, 2, 3); - Dialog::show(scene, 0x7318, 910, 908, 0xd0, 0xd9, 3, 2); - loadScene(11, scene->getPosition()); - showActor(); - setOns(3, 51); - playAnimation(911, 1); - playAnimation(899, 1); - setFlag(0xDBEC, 1); - reloadLan(); - wait(200); - enableObject(8); - setLan(2, 8); - return true; + default: + error("unknown callback 0x%04x called", addr); + break; } - //error("invalid callback %04x called", addr); - warning("invalid callback %04x called", addr); - return true; + return retVal; } } // End of namespace TeenAgent diff --git a/engines/teenagent/detection.cpp b/engines/teenagent/detection.cpp index 2de6f49c44..0c1268a5fc 100644 --- a/engines/teenagent/detection.cpp +++ b/engines/teenagent/detection.cpp @@ -26,6 +26,7 @@ #include "base/plugins.h" #include "engines/advancedDetector.h" +#include "teenagent/resources.h" #include "teenagent/teenagent.h" #include "graphics/thumbnail.h" @@ -168,7 +169,7 @@ public: Common::String desc = buf; - in->seek(0x777a); + in->seek(TeenAgent::saveStateSize); if (!Graphics::checkThumbnailHeader(*in)) return SaveStateDescriptor(slot, desc); diff --git a/engines/teenagent/dialog.cpp b/engines/teenagent/dialog.cpp index 400bd7cec2..870aca6400 100644 --- a/engines/teenagent/dialog.cpp +++ b/engines/teenagent/dialog.cpp @@ -22,99 +22,103 @@ #include "teenagent/dialog.h" #include "teenagent/resources.h" #include "teenagent/scene.h" +#include "teenagent/teenagent.h" namespace TeenAgent { +void Dialog::show(uint16 dialogNum, Scene *scene, uint16 animation1, uint16 animation2, byte color1, byte color2, byte slot1, byte slot2) { + uint16 addr = _vm->res->getDialogAddr(dialogNum); + // WORKAROUND: For Dialog 163, The usage of this in the engine overlaps the previous dialog i.e. the + // starting offset used is two bytes early, thus implicitly changing the first command of this dialog + // from NEW_LINE to CHANGE_CHARACTER. + // FIXME: Unsure if this is correct behaviour or if this is a regression from the original. Check this. + // Similar issue occurs with Dialog 190 which is used from dialogue stack at 0x7403, rather than start of 0x7405 + // Similar issue occurs with Dialog 0 which is used from dialogue stack at 0x0001, rather than start of 0x0000 + if (dialogNum == 163) + addr -= 2; + show(scene, addr, animation1, animation2, color1, color2, slot1, slot2); +} + void Dialog::show(Scene *scene, uint16 addr, uint16 animation1, uint16 animation2, byte color1, byte color2, byte slot1, byte slot2) { - debug(0, "Dialog::show(%04x, %u:%u, %u:%u)", addr, slot1, animation1, slot2, animation2); - Resources *res = Resources::instance(); + debugC(0, kDebugDialog, "Dialog::show(%04x, %u:%u, %u:%u)", addr, slot1, animation1, slot2, animation2); int n = 0; Common::String message; byte color = color1; if (animation1 != 0) { - SceneEvent e(SceneEvent::kPlayAnimation); - e.animation = animation1; - e.slot = 0xc0 | slot1; //looped, paused - scene->push(e); + SceneEvent e1(SceneEvent::kPlayAnimation); + e1.animation = animation1; + e1.slot = 0xc0 | slot1; //looped, paused + scene->push(e1); } if (animation2 != 0) { - SceneEvent e(SceneEvent::kPlayAnimation); - e.animation = animation2; - e.slot = 0xc0 | slot2; //looped, paused - scene->push(e); + SceneEvent e2(SceneEvent::kPlayAnimation); + e2.animation = animation2; + e2.slot = 0xc0 | slot2; //looped, paused + scene->push(e2); } while (n < 4) { - byte c = res->eseg.get_byte(addr++); - //debug(0, "%02x: %c", c, c > 0x20? c: '.'); + byte c = _vm->res->eseg.get_byte(addr++); + debugC(1, kDebugDialog, "%02x: %c", c, c > 0x20? c: '.'); switch (c) { case 0: ++n; switch (n) { case 1: - //debug(0, "new line\n"); + debugC(1, kDebugDialog, "new line\n"); if (!message.empty()) message += '\n'; break; case 2: - //debug(0, "displaymessage %s", message.c_str()); + debugC(1, kDebugDialog, "displaymessage %s", message.c_str()); if (color == color2) { //pause animation in other slot - { - SceneEvent e(SceneEvent::kPauseAnimation); - e.slot = 0x80 | slot1; - scene->push(e); - } - { - SceneEvent e(SceneEvent::kPlayAnimation); - e.animation = animation2; - e.slot = 0x80 | slot2; - scene->push(e); - } + SceneEvent e1(SceneEvent::kPauseAnimation); + e1.slot = 0x80 | slot1; + scene->push(e1); + + SceneEvent e2(SceneEvent::kPlayAnimation); + e2.animation = animation2; + e2.slot = 0x80 | slot2; + scene->push(e2); } else if (color == color1) { //pause animation in other slot - { - SceneEvent e(SceneEvent::kPauseAnimation); - e.slot = 0x80 | slot2; - scene->push(e); - } - { - SceneEvent e(SceneEvent::kPlayAnimation); - e.animation = animation1; - e.slot = 0x80 | slot1; - scene->push(e); - } - } + SceneEvent e2(SceneEvent::kPauseAnimation); + e2.slot = 0x80 | slot2; + scene->push(e2); - { - message.trim(); - if (message.empty()) - break; + SceneEvent e1(SceneEvent::kPlayAnimation); + e1.animation = animation1; + e1.slot = 0x80 | slot1; + scene->push(e1); + } - SceneEvent e(SceneEvent::kMessage); - e.message = message; - e.color = color; + message.trim(); + if (!message.empty()) { + SceneEvent em(SceneEvent::kMessage); + em.message = message; + em.color = color; if (color == color1) - e.slot = slot1; + em.slot = slot1; if (color == color2) - e.slot = slot2; - scene->push(e); + em.slot = slot2; + scene->push(em); message.clear(); } break; case 3: - color = color == color1 ? color2 : color1; - //debug(0, "changing color to %02x", color); + color = (color == color1) ? color2 : color1; + debugC(1, kDebugDialog, "changing color to %02x", color); break; } break; case 0xff: { - //fixme : wait for the next cycle of the animation + //FIXME : wait for the next cycle of the animation } break; @@ -124,21 +128,20 @@ void Dialog::show(Scene *scene, uint16 addr, uint16 animation1, uint16 animation } } - SceneEvent e(SceneEvent::kClearAnimations); - scene->push(e); + SceneEvent ec(SceneEvent::kClearAnimations); + scene->push(ec); } uint16 Dialog::pop(Scene *scene, uint16 addr, uint16 animation1, uint16 animation2, byte color1, byte color2, byte slot1, byte slot2) { - debug(0, "Dialog::pop(%04x, %u:%u, %u:%u)", addr, slot1, animation1, slot2, animation2); - Resources *res = Resources::instance(); + debugC(0, kDebugDialog, "Dialog::pop(%04x, %u:%u, %u:%u)", addr, slot1, animation1, slot2, animation2); uint16 next; do { - next = res->dseg.get_word(addr); + next = _vm->res->dseg.get_word(addr); addr += 2; } while (next == 0); - uint16 next2 = res->dseg.get_word(addr); + uint16 next2 = _vm->res->dseg.get_word(addr); if (next2 != 0xffff) - res->dseg.set_word(addr - 2, 0); + _vm->res->dseg.set_word(addr - 2, 0); show(scene, next, animation1, animation2, color1, color2, slot1, slot2); return next; } diff --git a/engines/teenagent/dialog.h b/engines/teenagent/dialog.h index 3bb7d818c1..6672ce7206 100644 --- a/engines/teenagent/dialog.h +++ b/engines/teenagent/dialog.h @@ -27,20 +27,59 @@ namespace TeenAgent { +// Text Color Symbols +enum { + textColorJohnNoty = 0xd0, + textColorCampGuard = 0xd0, + textColorShockedCaptain = 0xd0, + textColorMark = 0xd1, + textColorCredits = 0xd1, + textColorBankGuard = 0xd7, + textColorGrandpa = 0xd8, + textColorMansionGuard = 0xd9, + textColorMarkEnd = 0xe3, + textColorProfessor = 0xe5, + textColorOldLady = 0xe5, + textColorAnne = 0xe5, + textColorWellEcho = 0xe5, + textColorSonny = 0xe5, + textColorEskimo = 0xe5, + textColorRGBBoss = 0xe7, + textColorGoldDriver = 0xe7, + textColorFortuneTeller = 0xeb, + textColorCaptain = 0xec, + textColorMike = 0xef, + textColorCook = 0xef, + textColorBarman = 0xef +}; + class Scene; +class TeenAgentEngine; + class Dialog { public: - static uint16 pop(Scene *scene, uint16 addr, uint16 animation1, uint16 animation2, byte color1, byte color2, byte slot1, byte slot2); - static uint16 popMark(Scene *scene, uint16 addr) { - return pop(scene, addr, 0, 0, 0xd1, 0xd1, 0, 0); + Dialog(TeenAgentEngine *vm) : _vm(vm) { } + + uint16 pop(Scene *scene, uint16 addr, uint16 animation1, uint16 animation2, byte color1, byte color2, byte slot1, byte slot2); + + uint16 popMark(Scene *scene, uint16 addr) { + return pop(scene, addr, 0, 0, textColorMark, textColorMark, 0, 0); } - static void show(Scene *scene, uint16 addr, uint16 animation1, uint16 animation2, byte color1, byte color2, byte slot1, byte slot2); - static void showMono(Scene *scene, uint16 addr, uint16 animation, byte color, byte slot) { - show(scene, addr, animation, animation, color, color, slot, slot); + + void show(uint16 dialogNum, Scene *scene, uint16 animation1, uint16 animation2, byte color1, byte color2, byte slot1, byte slot2); + + void showMono(uint16 dialogNum, Scene *scene, uint16 animation, byte color, byte slot) { + show(dialogNum, scene, animation, animation, color, color, slot, slot); } - static void showMark(Scene *scene, uint16 addr) { - show(scene, addr, 0, 0, 0xd1, 0xd1, 0, 0); + + void showMark(uint16 dialogNum, Scene *scene) { + show(dialogNum, scene, 0, 0, textColorMark, textColorMark, 0, 0); } + +private: + TeenAgentEngine *_vm; + + void show(Scene *scene, uint16 addr, uint16 animation1, uint16 animation2, byte color1, byte color2, byte slot1, byte slot2); }; } // End of namespace TeenAgent diff --git a/engines/teenagent/font.cpp b/engines/teenagent/font.cpp index f7558b60f2..47f52ff90f 100644 --- a/engines/teenagent/font.cpp +++ b/engines/teenagent/font.cpp @@ -20,8 +20,10 @@ */ #include "teenagent/font.h" + #include "teenagent/pack.h" -#include "common/debug.h" +#include "teenagent/teenagent.h" + #include "common/endian.h" #include "common/stream.h" #include "common/textconsole.h" @@ -30,34 +32,41 @@ namespace TeenAgent { -Font::Font() : grid_color(0xd0), shadow_color(0), height(0), width_pack(0), data(0) { +Font::Font() : _gridColor(0xd0), _shadowColor(0), _height(0), _widthPack(0), _data(0) { +} + +Font::~Font() { + delete[] _data; } -void Font::load(const Pack &pack, int id) { - delete[] data; - data = NULL; +void Font::load(const Pack &pack, int id, byte height, byte widthPack) { + delete[] _data; + _data = NULL; Common::ScopedPtr<Common::SeekableReadStream> s(pack.getStream(id)); if (!s) error("loading font %d failed", id); - data = new byte[s->size()]; - s->read(data, s->size()); - debug(0, "font size: %d", s->size()); + _data = new byte[s->size()]; + s->read(_data, s->size()); + debugC(0, kDebugFont, "font size: %d", s->size()); + + _height = height; + _widthPack = widthPack; } uint Font::render(Graphics::Surface *surface, int x, int y, char c, byte color) { unsigned idx = (unsigned char)c; if (idx < 0x20 || idx >= 0x81) { - debug(0, "unhandled char 0x%02x", idx); + debugC(0, kDebugFont, "unhandled char 0x%02x", idx); return 0; } idx -= 0x20; - byte *glyph = data + READ_LE_UINT16(data + idx * 2); + byte *glyph = _data + READ_LE_UINT16(_data + idx * 2); int h = glyph[0], w = glyph[1]; - if (surface == NULL || surface->pixels == NULL || y + h <= 0 || y >= 200 || x + w <= 0 || x >= 320) - return w - width_pack; + if (surface == NULL || surface->pixels == NULL || y + h <= 0 || y >= kScreenHeight || x + w <= 0 || x >= kScreenWidth) + return w - _widthPack; int i0 = 0, j0 = 0; if (x < 0) { @@ -68,7 +77,7 @@ uint Font::render(Graphics::Surface *surface, int x, int y, char c, byte color) i0 = -y; y = 0; } - //debug(0, "char %c, width: %dx%d", c, w, h); + debugC(0, kDebugFont, "char %c, width: %dx%d", c, w, h); glyph += 2; glyph += i0 * w + j0; byte *dst = (byte *)surface->getBasePtr(x, y); @@ -80,7 +89,7 @@ uint Font::render(Graphics::Surface *surface, int x, int y, char c, byte color) case 0: break; case 1: - dst[j] = shadow_color; + dst[j] = _shadowColor; break; case 2: dst[j] = color; @@ -91,57 +100,57 @@ uint Font::render(Graphics::Surface *surface, int x, int y, char c, byte color) } dst += surface->pitch; } - return w - width_pack; + return w - _widthPack; } -static uint find_in_str(const Common::String &str, char c, uint pos = 0) { +static uint findInStr(const Common::String &str, char c, uint pos = 0) { while (pos < str.size() && str[pos] != c) ++pos; return pos; } -uint Font::render(Graphics::Surface *surface, int x, int y, const Common::String &str, byte color, bool show_grid) { +uint Font::render(Graphics::Surface *surface, int x, int y, const Common::String &str, byte color, bool showGrid) { if (surface != NULL) { - uint max_w = render(NULL, 0, 0, str, false); - if (show_grid) - grid(surface, x - 4, y - 2, max_w + 8, 8 + 6, grid_color); + uint maxW = render(NULL, 0, 0, str, false); + if (showGrid) + grid(surface, x - 4, y - 2, maxW + 8, 8 + 6, _gridColor); uint i = 0, j; do { - j = find_in_str(str, '\n', i); + j = findInStr(str, '\n', i); Common::String line(str.c_str() + i, j - i); - //debug(0, "line: %s", line.c_str()); + debugC(0, kDebugFont, "line: %s", line.c_str()); - if (y + (int)height >= 0) { + if (y + (int)_height >= 0) { uint w = render(NULL, 0, 0, line, false); - int xp = x + (max_w - w) / 2; + int xp = x + (maxW - w) / 2; for (uint k = 0; k < line.size(); ++k) { xp += render(surface, xp, y, line[k], color); } - } else if (y >= 200) + } else if (y >= kScreenHeight) break; - y += height; + y += _height; i = j + 1; } while (i < str.size()); - return max_w; + return maxW; } else { - //surface == NULL; - uint w = 0, max_w = 0; + // surface == NULL; + uint w = 0, maxW = 0; for (uint i = 0; i < str.size(); ++i) { char c = str[i]; if (c == '\n') { - y += height; - if (w > max_w) - max_w = w; + y += _height; + if (w > maxW) + maxW = w; w = 0; continue; } w += render(NULL, 0, 0, c, color); } - if (w > max_w) - max_w = w; + if (w > maxW) + maxW = w; - return max_w; + return maxW; } } @@ -156,8 +165,4 @@ void Font::grid(Graphics::Surface *surface, int x, int y, int w, int h, byte col } } -Font::~Font() { - delete[] data; -} - } // End of namespace TeenAgent diff --git a/engines/teenagent/font.h b/engines/teenagent/font.h index 5146ace21f..a61f145fa6 100644 --- a/engines/teenagent/font.h +++ b/engines/teenagent/font.h @@ -28,20 +28,24 @@ namespace TeenAgent { class Pack; + class Font { public: - byte grid_color, shadow_color; - byte height, width_pack; - Font(); - void load(const Pack &pack, int id); - uint render(Graphics::Surface *surface, int x, int y, const Common::String &str, byte color, bool grid = false); + ~Font(); + + void load(const Pack &pack, int id, byte height, byte widthPack); + uint render(Graphics::Surface *surface, int x, int y, const Common::String &str, byte color, bool showGrid = false); uint render(Graphics::Surface *surface, int x, int y, char c, byte color); static void grid(Graphics::Surface *surface, int x, int y, int w, int h, byte color); - ~Font(); + byte getHeight() { return _height; } + void setShadowColor(byte color) { _shadowColor = color; } private: - byte *data; + byte *_data; + + byte _gridColor, _shadowColor; + byte _height, _widthPack; }; } // End of namespace TeenAgent diff --git a/engines/teenagent/inventory.cpp b/engines/teenagent/inventory.cpp index 59dd44baa3..354371666c 100644 --- a/engines/teenagent/inventory.cpp +++ b/engines/teenagent/inventory.cpp @@ -24,6 +24,7 @@ #include "common/textconsole.h" #include "teenagent/inventory.h" + #include "teenagent/resources.h" #include "teenagent/objects.h" #include "teenagent/teenagent.h" @@ -31,47 +32,44 @@ namespace TeenAgent { -Inventory::Inventory(TeenAgentEngine *engine) { - _engine = engine; +Inventory::Inventory(TeenAgentEngine *vm) : _vm(vm) { _active = false; FilePack varia; varia.open("varia.res"); - { - Common::ScopedPtr<Common::SeekableReadStream> s(varia.getStream(3)); - if (!s) - error("no inventory background"); - debug(0, "loading inventory background..."); - _background.load(*s, Surface::kTypeOns); - } + Common::ScopedPtr<Common::SeekableReadStream> s(varia.getStream(3)); + if (!s) + error("no inventory background"); + debugC(0, kDebugInventory, "loading inventory background..."); + _background.load(*s, Surface::kTypeOns); - uint32 items_size = varia.getSize(4); - if (items_size == 0) + uint32 itemsSize = varia.getSize(4); + if (itemsSize == 0) error("invalid inventory items size"); - debug(0, "loading items, size: %u", items_size); - _items = new byte[items_size]; - varia.read(4, _items, items_size); + debugC(0, kDebugInventory, "loading items, size: %u", itemsSize); + _items = new byte[itemsSize]; + varia.read(4, _items, itemsSize); byte offsets = _items[0]; - assert(offsets == 92); + assert(offsets == kNumInventoryItems); for (byte i = 0; i < offsets; ++i) { _offset[i] = READ_LE_UINT16(_items + i * 2 + 1); } - _offset[92] = items_size; + _offset[kNumInventoryItems] = itemsSize; - Resources *res = Resources::instance(); - for (byte i = 0; i <= 92; ++i) { + InventoryObject ioBlank; + _objects.push_back(ioBlank); + for (byte i = 0; i < kNumInventoryItems; ++i) { InventoryObject io; - uint16 obj_addr = res->dseg.get_word(0xc4a4 + i * 2); - if (obj_addr != 0) - io.load(res->dseg.ptr(obj_addr)); + uint16 objAddr = vm->res->dseg.get_word(dsAddr_inventoryItemDataPtrTable + i * 2); + io.load(vm->res->dseg.ptr(objAddr)); _objects.push_back(io); } - _inventory = res->dseg.ptr(0xc48d); + _inventory = vm->res->dseg.ptr(dsAddr_inventory); - for (int y = 0; y < 4; ++y) + for (int y = 0; y < 4; ++y) { for (int x = 0; x < 6; ++x) { int i = y * 6 + x; _graphics[i]._rect.left = 28 + 45 * x - 1; @@ -79,6 +77,7 @@ Inventory::Inventory(TeenAgentEngine *engine) { _graphics[i]._rect.right = _graphics[i]._rect.left + 40; _graphics[i]._rect.bottom = _graphics[i]._rect.top + 26; } + } varia.close(); _hoveredObj = _selectedObj = NULL; @@ -89,7 +88,7 @@ Inventory::~Inventory() { } bool Inventory::has(byte item) const { - for (int i = 0; i < 24; ++i) { + for (int i = 0; i < kInventorySize; ++i) { if (_inventory[i] == item) return true; } @@ -97,34 +96,34 @@ bool Inventory::has(byte item) const { } void Inventory::remove(byte item) { - debug(0, "removing %u from inventory", item); + debugC(0, kDebugInventory, "removing %u from inventory", item); int i; - for (i = 0; i < 24; ++i) { + for (i = 0; i < kInventorySize; ++i) { if (_inventory[i] == item) { break; } } - for (; i < 23; ++i) { + for (; i < (kInventorySize - 1); ++i) { _inventory[i] = _inventory[i + 1]; _graphics[i].free(); } - _inventory[23] = 0; - _graphics[23].free(); + _inventory[kInventorySize - 1] = kInvItemNoItem; + _graphics[kInventorySize - 1].free(); } void Inventory::clear() { - debug(0, "clearing inventory"); - for (int i = 0; i < 24; ++i) { - _inventory[i] = 0; + debugC(0, kDebugInventory, "clearing inventory"); + for (int i = 0; i < kInventorySize; ++i) { + _inventory[i] = kInvItemNoItem; _graphics[i].free(); } } void Inventory::reload() { - for (int i = 0; i < 24; ++i) { + for (int i = 0; i < kInventorySize; ++i) { _graphics[i].free(); uint item = _inventory[i]; - if (item != 0) + if (item != kInvItemNoItem) _graphics[i].load(this, item); } } @@ -132,9 +131,9 @@ void Inventory::reload() { void Inventory::add(byte item) { if (has(item)) return; - debug(0, "adding %u to inventory", item); - for (int i = 0; i < 24; ++i) { - if (_inventory[i] == 0) { + debugC(0, kDebugInventory, "adding %u to inventory", item); + for (int i = 0; i < kInventorySize; ++i) { + if (_inventory[i] == kInvItemNoItem) { _inventory[i] = item; return; } @@ -143,13 +142,14 @@ void Inventory::add(byte item) { } bool Inventory::tryObjectCallback(InventoryObject *obj) { - byte id = obj->id; - uint i = 0; - for (byte *table = Resources::instance()->dseg.ptr(0xBB6F + 3); table[0] != 0 && i < 7; table += 3, ++i) { - if (table[0] == id) { + byte objId = obj->id; + for (uint i = 0; i < 7; ++i) { + byte tableId = _vm->res->dseg.get_byte(dsAddr_objCallbackTablePtr + (3 * i)); + uint16 callbackAddr = _vm->res->dseg.get_word(dsAddr_objCallbackTablePtr + (3 * i) + 1); + if (tableId == objId) { resetSelectedObject(); activate(false); - if (_engine->processCallback(READ_LE_UINT16(table + 1))) + if (_vm->processCallback(callbackAddr)) return true; } } @@ -157,8 +157,6 @@ bool Inventory::tryObjectCallback(InventoryObject *obj) { } bool Inventory::processEvent(const Common::Event &event) { - Resources *res = Resources::instance(); - switch (event.type) { case Common::EVENT_MOUSEMOVE: @@ -178,9 +176,9 @@ bool Inventory::processEvent(const Common::Event &event) { _mouse = event.mouse; _hoveredObj = NULL; - for (int i = 0; i < 24; ++i) { + for (int i = 0; i < kInventorySize; ++i) { byte item = _inventory[i]; - if (item == 0) + if (item == kInvItemNoItem) continue; _graphics[i]._hovered = _graphics[i]._rect.in(_mouse); @@ -197,14 +195,14 @@ bool Inventory::processEvent(const Common::Event &event) { if (_hoveredObj == NULL) return true; - debug(0, "lclick on %u:%s", _hoveredObj->id, _hoveredObj->name.c_str()); + debugC(0, kDebugInventory, "lclick on %u:%s", _hoveredObj->id, _hoveredObj->name.c_str()); if (_selectedObj == NULL) { if (tryObjectCallback(_hoveredObj)) return true; //activate(false); - int w = res->font7.render(NULL, 0, 0, _hoveredObj->description, 0xd1); - _engine->scene->displayMessage(_hoveredObj->description, 0xd1, Common::Point((320 - w) / 2, 162)); + int w = _vm->res->font7.render(NULL, 0, 0, _hoveredObj->description, textColorMark); + _vm->scene->displayMessage(_hoveredObj->description, textColorMark, Common::Point((kScreenWidth - w) / 2, 162)); return true; } @@ -213,30 +211,27 @@ bool Inventory::processEvent(const Common::Event &event) { if (id1 == id2) return true; - debug(0, "combine(%u, %u)!", id1, id2); - byte *table = res->dseg.ptr(0xC335); + debugC(0, kDebugInventory, "combine(%u, %u)!", id1, id2); + byte *table = _vm->res->dseg.ptr(dsAddr_objCombiningTablePtr); while (table[0] != 0 && table[1] != 0) { - if ( - (id1 == table[0] && id2 == table[1]) || - (id2 == table[0] && id1 == table[1]) - ) { - byte new_obj = table[2]; - if (new_obj != 0) { + if ((id1 == table[0] && id2 == table[1]) || (id2 == table[0] && id1 == table[1])) { + byte newObj = table[2]; + if (newObj != 0) { remove(id1); remove(id2); - debug(0, "adding object %u", new_obj); - add(new_obj); - _engine->playSoundNow(69); + debugC(0, kDebugInventory, "adding object %u", newObj); + add(newObj); + _vm->playSoundNow(69); } uint16 msg = READ_LE_UINT16(table + 3); - _engine->displayMessage(msg); + _vm->displayMessage(msg); activate(false); resetSelectedObject(); return true; } table += 5; } - _engine->displayMessage(0xc3e2); + _vm->displayMessage(dsAddr_objCombineErrorMsg); activate(false); resetSelectedObject(); return true; @@ -247,14 +242,15 @@ bool Inventory::processEvent(const Common::Event &event) { return false; if (_hoveredObj != NULL) { - debug(0, "rclick object %u:%s", _hoveredObj->id, _hoveredObj->name.c_str()); - if (_hoveredObj->id != 51 && tryObjectCallback(_hoveredObj)) //do not process callback for banknote on r-click + debugC(0, kDebugInventory, "rclick object %u:%s", _hoveredObj->id, _hoveredObj->name.c_str()); + // do not process callback for banknote on r-click + if (_hoveredObj->id != kInvItemBanknote && tryObjectCallback(_hoveredObj)) return true; } _selectedObj = _hoveredObj; if (_selectedObj) - debug(0, "selected object %s", _selectedObj->name.c_str()); + debugC(0, kDebugInventory, "selected object %s", _selectedObj->name.c_str()); return true; case Common::EVENT_KEYDOWN: @@ -262,7 +258,7 @@ bool Inventory::processEvent(const Common::Event &event) { activate(false); return true; } - if (event.kbd.keycode == Common::KEYCODE_RETURN) { //triangle button on psp + if (event.kbd.keycode == Common::KEYCODE_RETURN) { activate(!_active); return true; } @@ -277,7 +273,6 @@ bool Inventory::processEvent(const Common::Event &event) { } } - void Inventory::Item::free() { _animation.free(); _surface.free(); @@ -294,30 +289,29 @@ void Inventory::Item::backgroundEffect(Graphics::Surface *s) { } } -void Inventory::Item::load(Inventory *inventory, uint item_id) { - InventoryObject *obj = &inventory->_objects[item_id]; +void Inventory::Item::load(Inventory *inventory, uint itemId) { + InventoryObject *obj = &inventory->_objects[itemId]; if (obj->animated) { if (_animation.empty()) { - debug(0, "loading item %d from offset %x", obj->id, inventory->_offset[obj->id - 1]); + debugC(0, kDebugInventory, "loading item %d from offset %x", obj->id, inventory->_offset[obj->id - 1]); Common::MemoryReadStream s(inventory->_items + inventory->_offset[obj->id - 1], inventory->_offset[obj->id] - inventory->_offset[obj->id - 1]); _animation.load(s, Animation::kTypeInventory); } } else { if (_surface.empty()) { - debug(0, "loading item %d from offset %x", obj->id, inventory->_offset[obj->id - 1]); + debugC(0, kDebugInventory, "loading item %d from offset %x", obj->id, inventory->_offset[obj->id - 1]); Common::MemoryReadStream s(inventory->_items + inventory->_offset[obj->id - 1], inventory->_offset[obj->id] - inventory->_offset[obj->id - 1]); _surface.load(s, Surface::kTypeOns); } } } -void Inventory::Item::render(Inventory *inventory, uint item_id, Graphics::Surface *dst, int delta) { - InventoryObject *obj = &inventory->_objects[item_id]; - Resources *res = Resources::instance(); +void Inventory::Item::render(Inventory *inventory, uint itemId, Graphics::Surface *dst, int delta) { + InventoryObject *obj = &inventory->_objects[itemId]; backgroundEffect(dst); _rect.render(dst, _hovered ? 233 : 234); - load(inventory, item_id); + load(inventory, itemId); if (obj->animated) { if (_hovered) { Surface *s = _animation.currentFrame(delta); @@ -342,15 +336,16 @@ void Inventory::Item::render(Inventory *inventory, uint item_id, Graphics::Surfa if (inventory->_selectedObj != inventory->_hoveredObj) name += obj->name; - if (_hovered && inventory->_engine->scene->getMessage().empty()) { - int w = res->font7.render(NULL, 0, 0, name, 0xd1, true); - res->font7.render(dst, (320 - w) / 2, 180, name, 0xd1, true); + if (_hovered && inventory->_vm->scene->getMessage().empty()) { + int w = inventory->_vm->res->font7.render(NULL, 0, 0, name, textColorMark, true); + inventory->_vm->res->font7.render(dst, (kScreenWidth - w) / 2, 180, name, textColorMark, true); } } void Inventory::render(Graphics::Surface *surface, int delta) { if (!_active) return; + debugC(0, kDebugInventory, "Inventory::render()"); _background.render(surface); @@ -358,11 +353,10 @@ void Inventory::render(Graphics::Surface *surface, int delta) { for (int x = 0; x < 6; x++) { int idx = x + 6 * y; byte item = _inventory[idx]; - if (item == 0) - continue; - - //debug(0, "%d,%d -> %u", x0, y0, item); - _graphics[idx].render(this, item, surface, delta); + if (item != 0) { + debugC(0, kDebugInventory, "\t(x, y): %d,%d -> item: %u", x, y, item); + _graphics[idx].render(this, item, surface, delta); + } } } } diff --git a/engines/teenagent/inventory.h b/engines/teenagent/inventory.h index 61e5364542..d487848c2c 100644 --- a/engines/teenagent/inventory.h +++ b/engines/teenagent/inventory.h @@ -33,9 +33,112 @@ namespace TeenAgent { struct InventoryObject; class TeenAgentEngine; +// Maximum number of items found within game +const uint8 kNumInventoryItems = 92; + +// Inventory Item Ids +enum { + kInvItemNoItem = 0, // No item i.e. empty inventory slot + kInvItemFeather = 1, + kInvItemShotgun = 2, + kInvItemToolboxFull = 3, // Contains Car Jack and Spanner + kInvItemToolboxHalfEmpty = 4, // Contains Spanner + kInvItemSpanner = 5, + kInvItemComb = 6, + kInvItemFan = 7, + kInvItemBrokenPaddle = 8, + kInvItemPaddle = 9, // Repaired - BrokenPaddle combined with Branch (with Glue) + kInvItemFirstFlower = 10, // Smells nice + kInvItemSecondFlower = 11, // Really beautiful + kInvItemFeatherDusterClean = 12, + kInvItemChainsaw = 13, // Unfueled + kInvItemDrunkenChainsaw = 14, // Fueled with Whisky (Chainsaw combined with Whiskey) + kInvItemBranch = 15, + kInvItemWhisky = 16, + kInvItemNeedle = 17, + kInvItemWrapper = 18, + kInvItemChocCandy = 19, + kInvItemPotato = 20, + kInvItemRakeBroken = 21, + kInvItemHeartShapedCandy = 22, + kInvItemWrappedCandy = 23, // HeartShapedCandy combined with Wrapper + kInvItemRibbon = 24, + kInvItemRakeFixed = 25, // Rake combined with Ribbon + kInvItemNut = 26, + kInvItemPlasticApple = 27, + kInvItemCone = 28, + kInvItemSuperGlue = 29, + kInvItemConeAndNeedle = 30, // Cone combined with Needle + kInvItemConeAndFeather = 31, // Cone combined with Feather + kInvItemDart = 32, // Needle combined with ConeAndFeather or Feather combined with ConeAndNeedle + kInvItemFeatherDusterDirty = 33, + kInvItemPaintedPotato = 34, // Potato combined with Dirty Feather Duster (Soot) + kInvItemCarJack = 35, + kInvItemBone = 36, + kInvItemShovelAct2 = 37, + kInvItemRopeAct2 = 38, + kInvItemMask = 39, + kInvItemFins = 40, + kInvItemDiveEquipment = 41, // Mask combined with Fins + kInvItemAnchor = 42, + kInvItemGrapplingHook = 43, + kInvItemSickleBlunt = 44, + kInvItemCheese = 45, + kInvItemSickleSharp = 46, + kInvItemHandkerchief = 47, + kInvItemMouse = 48, + kInvItemRock = 49, + kInvItemNugget = 50, + kInvItemBanknote = 51, + kInvItemDictaphoneNoBatteries = 52, + kInvItemPolaroidCamera = 53, + kInvItemVideoTape = 54, + kInvItemSheetOfPaper = 55, + kInvItemCognac = 56, + kInvItemRemoteControl = 57, + kInvItemIceTongs = 58, + kInvItemCork = 59, + kInvItemWrappedCork = 60, // Cork combined with Sheet Of Paper + kInvItemPhoto = 61, + kInvItemChilliWithLabel = 62, + kInvItemPastryRoller = 63, + kInvItemFakeChilli = 64, + kInvItemLabel = 65, + kInvItemBatteries = 66, + kInvItemDictaphoneWithBatteries = 67, // Dictaphone combined with Batteries + kInvItemBurningPaper = 68, + kInvItemMeat = 69, + kInvItemPlasticBag = 70, + kInvItemSocks = 71, + kInvItemTimePills = 72, + kInvItemHandle = 73, + kInvItemChilliNoLabel = 74, + kInvItemPass = 75, + kInvItemBulb = 76, + kInvItemJailKey = 77, + kInvItemDelicatePlant = 78, + kInvItemSwissArmyKnife = 79, + kInvItemSpring = 80, + kInvItemShovelAct1 = 81, + kInvItemKaleidoscope = 82, + kInvItemSoldierNews = 83, + kInvItemGrenade = 84, + kInvItemMug = 85, // Empty + kInvItemMugOfMud = 86, // Full of mud + kInvItemCrumbs = 87, + kInvItemRopeAct1 = 88, + kInvItemRopeAndGrenade = 89, // Rope combined with Grenade + kInvItemMedicine = 90, + kInvItemDruggedFood = 91, // Crumbs combined with Medicine + kInvItemBird = 92 +}; + +// Maximum number of inventory items held by Ego (Mark) +const uint8 kInventorySize = 24; + class Inventory { public: - Inventory(TeenAgentEngine *engine); + Inventory(TeenAgentEngine *vm); ~Inventory(); void render(Graphics::Surface *surface, int delta); @@ -55,10 +158,10 @@ public: void resetSelectedObject() { _selectedObj = NULL; } private: - TeenAgentEngine *_engine; + TeenAgentEngine *_vm; Surface _background; byte *_items; - uint _offset[93]; + uint _offset[kNumInventoryItems+1]; Common::Array<InventoryObject> _objects; byte *_inventory; @@ -71,12 +174,12 @@ private: Item() : _hovered(false) {} void free(); - void load(Inventory *inventory, uint item_id); + void load(Inventory *inventory, uint itemId); void backgroundEffect(Graphics::Surface *s); - void render(Inventory *inventory, uint item_id, Graphics::Surface *surface, int delta); + void render(Inventory *inventory, uint itemId, Graphics::Surface *surface, int delta); }; - Item _graphics[24]; + Item _graphics[kInventorySize]; bool _active; Common::Point _mouse; diff --git a/engines/teenagent/music.cpp b/engines/teenagent/music.cpp index 1f44e9cfcb..b06a5f1f5e 100644 --- a/engines/teenagent/music.cpp +++ b/engines/teenagent/music.cpp @@ -22,6 +22,8 @@ #include "teenagent/music.h" #include "teenagent/resources.h" +#include "teenagent/teenagent.h" + #include "common/debug.h" #include "common/ptr.h" #include "common/textconsole.h" @@ -34,36 +36,36 @@ static const uint32 noteToPeriod[3][12] = { {214, 201, 189, 179, 170, 160, 151, 143, 135, 127, 120, 113} }; -MusicPlayer::MusicPlayer() : Paula(false, 44100, 5000), _id(0) { +MusicPlayer::MusicPlayer(TeenAgentEngine *vm) : Paula(false, 44100, 5000), _vm(vm), _id(0) { } MusicPlayer::~MusicPlayer() { + stop(); } bool MusicPlayer::load(int id) { - Resources *res = Resources::instance(); - - Common::ScopedPtr<Common::SeekableReadStream> stream(res->mmm.getStream(id)); + debugC(0, kDebugMusic, "MusicPlayer::load(%d)", id); + Common::ScopedPtr<Common::SeekableReadStream> stream(_vm->res->mmm.getStream(id)); if (!stream) return false; char header[4]; stream->read(header, 4); - //check header? + // check header? Common::StackLock lock(_mutex); // Load the samples sampleCount = stream->readByte(); - debug(0, "sampleCount = %d", sampleCount); + debugC(0, kDebugMusic, "sampleCount = %d", sampleCount); for (byte currSample = 0; currSample < sampleCount; currSample++) { byte sample = stream->readByte(); // Load the sample data - byte sampleResource = ((sample >> 4) & 0x0F) * 10 + (sample & 0x0F); - debug(0, "currSample = %d, sample = 0x%02x, resource: %d", currSample, sample, sampleResource); - uint32 sampleSize = res->sam_mmm.getSize(sampleResource); + byte sampleResource = ((sample >> 4) & 0x0f) * 10 + (sample & 0x0f); + debugC(0, kDebugMusic, "currSample = %d, sample = 0x%02x, resource: %d", currSample, sample, sampleResource); + uint32 sampleSize = _vm->res->sam_mmm.getSize(sampleResource); if (sampleSize == 0) { warning("load: invalid sample %d (0x%02x)", sample, sample); _samples[sample].clear(); @@ -71,7 +73,7 @@ bool MusicPlayer::load(int id) { } _samples[sample].resize(sampleSize); - res->sam_mmm.read(sampleResource, _samples[sample].data, sampleSize); + _vm->res->sam_mmm.read(sampleResource, _samples[sample].data, sampleSize); } // Load the music data @@ -87,17 +89,17 @@ bool MusicPlayer::load(int id) { row.channels[1].note = stream->readByte(); row.channels[2].note = stream->readByte(); _rows.push_back(row); - } else if ((cmd & 0xF0) == 0x50) { + } else if ((cmd & 0xf0) == 0x50) { byte sample = stream->readByte(); - //debug(1, "%02x: set sample %02x", cmd, sample); - row.channels[(cmd & 0x0F) - 1].sample = sample; - } else if ((cmd & 0xF0) == 0x40) { + debugC(1, kDebugMusic, "%02x: set sample %02x", cmd, sample); + row.channels[(cmd & 0x0f) - 1].sample = sample; + } else if ((cmd & 0xf0) == 0x40) { byte vol = stream->readByte(); - //debug(1, "%02x: set volume %02x -> %02x", cmd, row.channels[(cmd & 0x0F) - 1].volume, vol); - //channel volume 0x40 * music volume 0x40 mixed with high bytes - row.channels[(cmd & 0x0F) - 1].volume = vol * 16; + debugC(1, kDebugMusic, "%02x: set volume %02x -> %02x", cmd, row.channels[(cmd & 0x0f) - 1].volume, vol); + // channel volume 0x40 * music volume 0x40 mixed with high bytes + row.channels[(cmd & 0x0f) - 1].volume = vol * 16; } else { - debug(0, "unhandled music command %02x", cmd); + debugC(0, kDebugMusic, "unhandled music command %02x", cmd); } } _currRow = 0; @@ -124,13 +126,13 @@ void MusicPlayer::interrupt() { for (int chn = 0; chn < 3; ++chn) { setChannelVolume(chn, row->channels[chn].volume); - //debug(0, "row->channels[%d].volume = %d", chn, row->channels[chn].volume); + debugC(2, kDebugMusic, "row->channels[%d].volume = %d", chn, row->channels[chn].volume); byte sample = (row->channels[chn].sample); if (row->channels[chn].note != 0 && sample != 0) { - //debug(0, "row->channels[%d].note = %d", chn, row->channels[chn].note); - //debug(0, "row->channels[%d].sample = %d", chn, row->channels[chn].sample); + debugC(2, kDebugMusic, "row->channels[%d].note = %d", chn, row->channels[chn].note); + debugC(2, kDebugMusic, "row->channels[%d].sample = %d", chn, row->channels[chn].sample); byte note = row->channels[chn].note; if (_samples[sample].size == 0) { @@ -139,11 +141,11 @@ void MusicPlayer::interrupt() { } setChannelData(chn, (const int8 *)_samples[sample].data, NULL, _samples[sample].size, 0); - setChannelPeriod(chn, noteToPeriod[((note >> 4) & 0x0F) - 1][(note & 0x0F)]); + setChannelPeriod(chn, noteToPeriod[((note >> 4) & 0x0f) - 1][(note & 0x0f)]); } } - //debug(0, "------------------------------------------------"); + debugC(2, kDebugMusic, "------------------------------------------------"); ++_currRow; } diff --git a/engines/teenagent/music.h b/engines/teenagent/music.h index 22b4fa5e8e..408436cf3a 100644 --- a/engines/teenagent/music.h +++ b/engines/teenagent/music.h @@ -28,10 +28,11 @@ namespace TeenAgent { +class TeenAgentEngine; + class MusicPlayer : public Audio::Paula { public: - - MusicPlayer(); + MusicPlayer(TeenAgentEngine *vm); ~MusicPlayer(); bool load(int id); @@ -41,6 +42,8 @@ public: void stop(); private: + TeenAgentEngine *_vm; + int _id; struct Row { diff --git a/engines/teenagent/objects.cpp b/engines/teenagent/objects.cpp index 748f342d54..5dad9ab99d 100644 --- a/engines/teenagent/objects.cpp +++ b/engines/teenagent/objects.cpp @@ -21,8 +21,10 @@ #include "common/debug.h" #include "common/memstream.h" + #include "teenagent/objects.h" #include "teenagent/resources.h" +#include "teenagent/teenagent.h" namespace TeenAgent { @@ -51,7 +53,6 @@ void Rect::render(Graphics::Surface *surface, uint8 color) const { surface->vLine(right, bottom, top, color); } - void Object::load(byte *src) { _base = src; @@ -59,39 +60,39 @@ void Object::load(byte *src) { rect.load(src); src += 8; - actor_rect.load(src); + actorRect.load(src); src += 8; - actor_orientation = *src++; + actorOrientation = *src++; enabled = *src++; name = (const char *)src; - description = parse_description((const char *)src); + description = parseDescription((const char *)src); } void Object::save() const { assert(_base != NULL); rect.save(); - actor_rect.save(); - _base[17] = actor_orientation; + actorRect.save(); + _base[17] = actorOrientation; _base[18] = enabled; } -void Object::setName(const Common::String &new_name) { +void Object::setName(const Common::String &newName) { assert(_base != 0); - strcpy((char *)(_base + 19), new_name.c_str()); - name = new_name; + strcpy((char *)(_base + 19), newName.c_str()); + name = newName; } void Object::dump(int level) const { - debug(level, "object: %u %u [%u,%u,%u,%u], actor: [%u,%u,%u,%u], orientation: %u, name: %s", id, enabled, + debugC(level, kDebugObject, "object: %u %u [%u,%u,%u,%u], actor: [%u,%u,%u,%u], orientation: %u, name: %s", id, enabled, rect.left, rect.top, rect.right, rect.bottom, - actor_rect.left, actor_rect.top, actor_rect.right, actor_rect.bottom, - actor_orientation, name.c_str() + actorRect.left, actorRect.top, actorRect.right, actorRect.bottom, + actorOrientation, name.c_str() ); } -Common::String Object::parse_description(const char *name) { +Common::String Object::parseDescription(const char *name) { const char *desc = name + strlen(name) + 1; if (*desc == 0) return Common::String(); @@ -101,7 +102,7 @@ Common::String Object::parse_description(const char *name) { while (*desc != 1 && *desc != 0) { Common::String line; while (*desc != 1 && *desc != 0) { - //debug(0, "%02x ", *desc); + debugC(2, kDebugObject, "%02x ", *desc); line += *desc++; } @@ -115,7 +116,7 @@ Common::String Object::parse_description(const char *name) { if (!result.empty()) result.deleteLastChar(); else - result = "Cool."; + result = "Cool."; // FIXME - Use dsAddr_coolMsg ? return result; } @@ -124,31 +125,31 @@ void InventoryObject::load(byte *src) { id = *src++; animated = *src++; name = (const char *)src; - description = Object::parse_description((const char *)src); + description = Object::parseDescription((const char *)src); } void UseHotspot::load(byte *src) { Common::MemoryReadStream in(src, 9); - inventory_id = in.readByte(); - object_id = in.readByte(); + inventoryId = in.readByte(); + objectId = in.readByte(); orientation = in.readByte(); - actor_x = in.readUint16LE(); - actor_y = in.readUint16LE(); + actorX = in.readUint16LE(); + actorY = in.readUint16LE(); callback = in.readUint16LE(); } void UseHotspot::dump(int level) const { - debug(level, - "hotspot: inv_id: %02x, obj_id: %02x, orientation?: %02x, actor position: (%d,%d), callback: %04x", - inventory_id, object_id, orientation, actor_x, actor_y, callback + debugC(level, kDebugObject, + "hotspot: invId: %02x, objId: %02x, orientation: %02x, actor position: (%d,%d), callback: %04x", + inventoryId, objectId, orientation, actorX, actorY, callback ); } void Walkbox::dump(int level) const { - debug(level, "walkbox %02x %02x [%d, %d, %d, %d] top: %u, right: %u, bottom: %u, left: %u", + debugC(level, kDebugObject, "walkbox %02x %02x [%d, %d, %d, %d] top: %u, right: %u, bottom: %u, left: %u", type, orientation, rect.left, rect.top, rect.right, rect.bottom, - side_hint[0], side_hint[1], side_hint[2], side_hint[3]); + sideHint[0], sideHint[1], sideHint[2], sideHint[3]); } void Walkbox::load(byte *src) { @@ -159,7 +160,7 @@ void Walkbox::load(byte *src) { rect.load(src); src += 8; for (byte i = 0; i < 4; ++i) - side_hint[i] = *src++; + sideHint[i] = *src++; } void Walkbox::save() const { diff --git a/engines/teenagent/objects.h b/engines/teenagent/objects.h index 555287fc56..6e7955766f 100644 --- a/engines/teenagent/objects.h +++ b/engines/teenagent/objects.h @@ -26,6 +26,8 @@ #include "common/rect.h" #include "graphics/surface.h" +#include "teenagent/teenagent.h" + namespace TeenAgent { enum {kActorUp = 1, kActorRight = 2, kActorDown = 3, kActorLeft = 4 }; @@ -46,13 +48,13 @@ struct Rect { } inline bool valid() const { - return left >= 0 && left < 320 && right >= 0 && right < 320 && top >= 0 && top < 200 && bottom >= 0 && bottom < 200; + return left >= 0 && left < kScreenWidth && right >= 0 && right < kScreenWidth && top >= 0 && top < kScreenHeight && bottom >= 0 && bottom < kScreenHeight; } void render(Graphics::Surface *surface, uint8 color) const; void dump(int level = 0) const { - debug(level, "rect[%u, %u, %u, %u]", left, top, right, bottom); + debugC(level, kDebugObject, "rect[%u, %u, %u, %u]", left, top, right, bottom); } inline void clear() { @@ -154,22 +156,21 @@ protected: }; struct Object { - byte id; //0 Rect rect; //1 - Rect actor_rect; //9 - byte actor_orientation; //17 + Rect actorRect; //9 + byte actorOrientation; //17 byte enabled; //18 //19 Common::String name, description; Object(): _base(NULL) {} void dump(int level = 0) const; - void setName(const Common::String &name); + void setName(const Common::String &newName); void load(byte *addr); void save() const; - static Common::String parse_description(const char *name); + static Common::String parseDescription(const char *name); protected: byte *_base; @@ -188,10 +189,10 @@ protected: }; struct UseHotspot { - byte inventory_id; - byte object_id; + byte inventoryId; + byte objectId; byte orientation; - uint16 actor_x, actor_y; + uint16 actorX, actorY; uint16 callback; void load(byte *src); void dump(int level = 0) const; @@ -201,7 +202,7 @@ struct Walkbox { byte type; byte orientation; Rect rect; - byte side_hint[4]; + byte sideHint[4]; Walkbox() : _base(NULL) {} void dump(int level = 0) const; diff --git a/engines/teenagent/pack.cpp b/engines/teenagent/pack.cpp index 5302e2eceb..2e6c913a72 100644 --- a/engines/teenagent/pack.cpp +++ b/engines/teenagent/pack.cpp @@ -20,6 +20,8 @@ */ #include "teenagent/pack.h" +#include "teenagent/teenagent.h" + #include "common/util.h" #include "common/debug.h" #include "common/memstream.h" @@ -44,7 +46,7 @@ bool FilePack::open(const Common::String &filename) { return false; _fileCount = file.readUint32LE(); - debug(0, "opened %s, found %u entries", filename.c_str(), _fileCount); + debugC(0, kDebugPack, "opened %s, found %u entries", filename.c_str(), _fileCount); offsets = new uint32[_fileCount + 1]; for (uint32 i = 0; i <= _fileCount; ++i) { offsets[i] = file.readUint32LE(); @@ -65,18 +67,17 @@ uint32 FilePack::read(uint32 id, byte *dst, uint32 size) const { file.seek(offsets[id - 1]); uint32 rsize = offsets[id] - offsets[id - 1]; uint32 r = file.read(dst, MIN(rsize, size)); - //debug(0, "read(%u, %u) = %u", id, size, r); + debugC(0, kDebugPack, "read(%u, %u) = %u", id, size, r); return r; } Common::SeekableReadStream *FilePack::getStream(uint32 id) const { if (id < 1 || id > _fileCount) return NULL; - //debug(0, "stream: %04x-%04x", offsets[id - 1], offsets[id]); + debugC(0, kDebugPack, "stream: %04x-%04x", offsets[id - 1], offsets[id]); return new Common::SeekableSubReadStream(&file, offsets[id - 1], offsets[id]); } - TransientFilePack::TransientFilePack() : offsets(0) {} TransientFilePack::~TransientFilePack() { @@ -97,7 +98,7 @@ bool TransientFilePack::open(const Common::String &filename) { return false; _fileCount = file.readUint32LE(); - debug(0, "opened %s, found %u entries", filename.c_str(), _fileCount); + debugC(0, kDebugPack, "opened %s, found %u entries", filename.c_str(), _fileCount); offsets = new uint32[_fileCount + 1]; for (uint32 i = 0; i <= _fileCount; ++i) { offsets[i] = file.readUint32LE(); @@ -124,14 +125,14 @@ uint32 TransientFilePack::read(uint32 id, byte *dst, uint32 size) const { uint32 rsize = offsets[id] - offsets[id - 1]; uint32 r = file.read(dst, MIN(rsize, size)); file.close(); - //debug(0, "read(%u, %u) = %u", id, size, r); + debugC(0, kDebugPack, "read(%u, %u) = %u", id, size, r); return r; } Common::SeekableReadStream *TransientFilePack::getStream(uint32 id) const { if (id < 1 || id > _fileCount) return NULL; - //debug(0, "stream: %04x-%04x", offsets[id - 1], offsets[id]); + debugC(0, kDebugPack, "stream: %04x-%04x", offsets[id - 1], offsets[id]); Common::File file; if (!file.open(_filename)) return NULL; @@ -146,7 +147,6 @@ Common::SeekableReadStream *TransientFilePack::getStream(uint32 id) const { return new Common::MemoryReadStream(ptr, r, DisposeAfterUse::YES); } - void MemoryPack::close() { chunks.clear(); } @@ -157,7 +157,7 @@ bool MemoryPack::open(const Common::String &filename) { return false; uint32 count = file.readUint32LE(); - debug(0, "opened %s, found %u entries [memory]", filename.c_str(), count); + debugC(0, kDebugPack, "opened %s, found %u entries [memory]", filename.c_str(), count); for (uint32 i = 0; i < count; ++i) { uint32 offset = file.readUint32LE(); int32 pos = file.pos(); @@ -199,5 +199,4 @@ Common::SeekableReadStream *MemoryPack::getStream(uint32 id) const { return new Common::MemoryReadStream(c.data, c.size, DisposeAfterUse::NO); } - } // End of namespace TeenAgent diff --git a/engines/teenagent/resources.cpp b/engines/teenagent/resources.cpp index 597ca670c0..442d0abf16 100644 --- a/engines/teenagent/resources.cpp +++ b/engines/teenagent/resources.cpp @@ -22,18 +22,15 @@ #include "teenagent/resources.h" #include "teenagent/teenagent.h" #include "common/textconsole.h" +#include "common/translation.h" #include "common/zlib.h" namespace TeenAgent { -Resources::Resources() {} - -Resources *Resources::instance() { - static Resources i; - return &i; +Resources::Resources() { } -void Resources::deinit() { +Resources::~Resources() { off.close(); on.close(); ons.close(); @@ -60,32 +57,80 @@ quick note on varia resources: 11: quit shareware */ +#define CSEG_SIZE 46000 // 0xb3b0 +#define DSEG_SIZE 59280 // 0xe790 +#define ESEG_SIZE 35810 // 0x8be2 + +void Resources::precomputeDialogOffsets() { + dialogOffsets.push_back(0); + int n = 0; + uint8 current, last = 0xff; + for (uint i = 0; i < eseg.size(); i++) { + current = eseg.get_byte(i); + + if (n == 4) { + dialogOffsets.push_back(i); + n = 0; + } + + if (current != 0x00 && last == 0x00) + n = 0; + + if (current == 0x00) + n++; + + last = current; + } + + debug(1, "Resources::precomputeDialogOffsets() - Found %d dialogs", dialogOffsets.size()); + for (uint i = 0; i < dialogOffsets.size(); i++) + debug(1, "\tDialog #%d: Offset 0x%04x", i, dialogOffsets[i]); +} + bool Resources::loadArchives(const ADGameDescription *gd) { Common::File *dat_file = new Common::File(); if (!dat_file->open("teenagent.dat")) { delete dat_file; - Common::String errorMessage = "You're missing the 'teenagent.dat' file. Get it from the ScummVM website"; - GUIErrorMessage(errorMessage); + Common::String errorMessage = _("You're missing the 'teenagent.dat' file. Get it from the ScummVM website"); warning("%s", errorMessage.c_str()); + GUIErrorMessage(errorMessage); return false; } + + // teenagent.dat used to be compressed with zlib compression. The usage of + // zlib here is no longer needed, and it's maintained only for backwards + // compatibility. Common::SeekableReadStream *dat = Common::wrapCompressedReadStream(dat_file); - cseg.read(dat, 0xb3b0); - dseg.read(dat, 0xe790); - eseg.read(dat, 0x8be2); +#if !defined(USE_ZLIB) + uint16 header = dat->readUint16BE(); + bool isCompressed = (header == 0x1F8B || + ((header & 0x0F00) == 0x0800 && + header % 31 == 0)); + dat->seek(-2, SEEK_CUR); + + if (isCompressed) { + // teenagent.dat is compressed, but zlib hasn't been compiled in + delete dat; + Common::String errorMessage = _("The teenagent.dat file is compressed and zlib hasn't been included in this executable. Please decompress it"); + warning("%s", errorMessage.c_str()); + GUIErrorMessage(errorMessage); + return false; + } +#endif + + dat->skip(CSEG_SIZE); + dseg.read(dat, DSEG_SIZE); + eseg.read(dat, ESEG_SIZE); delete dat; - { - FilePack varia; - varia.open("varia.res"); - font7.load(varia, 7); - font7.width_pack = 1; - font7.height = 11; - font8.load(varia, 8); - font8.height = 31; - varia.close(); - } + precomputeDialogOffsets(); + + FilePack varia; + varia.open("varia.res"); + font7.load(varia, 7, 11, 1); + font8.load(varia, 8, 31, 0); + varia.close(); off.open("off.res"); on.open("on.res"); @@ -130,13 +175,13 @@ Common::SeekableReadStream *Resources::loadLan000(uint32 id) const { switch (id) { case 81: - if (dseg.get_byte(0xDBAD)) + if (dseg.get_byte(dsAddr_dogHasBoneFlag)) return lan500.getStream(160); break; case 137: - if (dseg.get_byte(0xDBC5) == 1) { - if (dseg.get_byte(0xDBC6) == 1) + if (dseg.get_byte(dsAddr_mansionTVOnFlag) == 1) { + if (dseg.get_byte(dsAddr_mansionVCRPlayingTapeFlag) == 1) return lan500.getStream(203); else return lan500.getStream(202); @@ -144,31 +189,31 @@ Common::SeekableReadStream *Resources::loadLan000(uint32 id) const { break; case 25: - if (dseg.get_byte(0xDBDF) == 2) { + if (dseg.get_byte(dsAddr_FirstActTrialState) == 2) { return lan500.getStream(332); } break; case 37: - if (dseg.get_byte(0xdbe2) == 1) { + if (dseg.get_byte(dsAddr_act1GuardState) == 1) { return lan500.getStream(351); - } else if (dseg.get_byte(0xdbe2) == 2) { + } else if (dseg.get_byte(dsAddr_act1GuardState) == 2) { return lan500.getStream(364); } break; case 29: - if (dseg.get_byte(0xDBE7) == 1) { + if (dseg.get_byte(dsAddr_birdOnBarRadioAntennaFlag) == 1) { return lan500.getStream(380); } case 30: - if (dseg.get_byte(0xDBE7) == 1) { + if (dseg.get_byte(dsAddr_birdOnBarRadioAntennaFlag) == 1) { return lan500.getStream(381); } case 42: - if (dseg.get_byte(0xDBEC) == 1) { + if (dseg.get_byte(dsAddr_johnNotyOutsideMansionDoorFlag) == 1) { return lan500.getStream(400); } } diff --git a/engines/teenagent/resources.h b/engines/teenagent/resources.h index 5c08a46489..7aae2f9ec8 100644 --- a/engines/teenagent/resources.h +++ b/engines/teenagent/resources.h @@ -31,18 +31,1144 @@ struct ADGameDescription; namespace TeenAgent { +// Code Segment Addresses (Read Only) +// Intro function : 0x024c +const uint16 csAddr_intro = 0x024c; +// Pole Climb Fail function : 0x4173 +const uint16 csAddr_poleClimbFail = 0x4173; +// Move Ego (Mark) To Suspicious Position function : 0x505c +const uint16 csAddr_egoSuspiciousPosition = 0x505c; +// Guard Scare Timeout function : 0x516d +const uint16 csAddr_guardScareTimeout = 0x516d; +// Guard Drinking function : 0x5189 +const uint16 csAddr_guardDrinking = 0x5189; +// Move Ego (Mark) To Default Position function : 0x557e +const uint16 csAddr_egoDefaultPosition = 0x557e; +// Cave NOP function : 0x599b +const uint16 csAddr_caveNOP = 0x599b; +// Enter Cave function : 0x5a21 +const uint16 csAddr_enterCave = 0x5a21; +// Ego (Mark) Scared By Spider function : 0x60b5 +const uint16 csAddr_egoScaredBySpider = 0x60b5; +// Move to Ladder and Leave Cellar function : 0x60d9 +const uint16 csAddr_moveToLadderAndLeaveCellar = 0x60d9; +// Leave Cellar function : 0x612b +const uint16 csAddr_leaveCellar = 0x612b; +// Too Dark function : 0x61fe +const uint16 csAddr_TooDark = 0x61fe; +// Move Ego (Mark) To Bottom-Right or Turn function : 0x6849 +const uint16 csAddr_egoBottomRightTurn = 0x6849; +// Checking Drawers function : 0x68e6 +const uint16 csAddr_checkingDrawers = 0x68e6; +// Drawer Open Message function : 0x6b86 +const uint16 csAddr_DrawerOpenMessage = 0x6b86; +// Is Cook Gone function : 0x70e0 +const uint16 csAddr_isCookGone = 0x70e0; +// Giving Flower to Old Lady function : 0x88de +const uint16 csAddr_givingFlowerToOldLady = 0x88de; +// Give Another Flower to Old Lady function : 0x890b +const uint16 csAddr_giveAnotherFlowerToOldLady = 0x890b; +// Giving Flower to Anne function : 0x8942 +const uint16 csAddr_givingFlowerToAnne = 0x8942; +// Give Another Flower to Anne function : 0x89aa +const uint16 csAddr_giveAnotherFlowerToAnne = 0x89aa; +// Putting Rock in Hole function : 0x8d57 +const uint16 csAddr_putRockInHole = 0x8d57; +// Mouse Out Of Hole Timeout function : 0x8d79 +const uint16 csAddr_mouseOutOfHoleTimeout = 0x8d79; +// Robot Safe (Mike) Already Unlocked Check function : 0x9166 +const uint16 csAddr_robotSafeAlreadyUnlockedCheck = 0x9166; +// Robot Safe (Mike) Unlock Check function : 0x9175 +const uint16 csAddr_robotSafeUnlockCheck = 0x9175; +// Open Full Toolbox function : 0x98fa +const uint16 csAddr_openFullToolbox = 0x98fa; +// Open Half Empty Toolbox function : 0x9910 +const uint16 csAddr_openHalfEmptyToolbox = 0x9910; +// Use Diving Equipment function : 0x9921 +const uint16 csAddr_useDivingEquipment = 0x9921; +// Successfully Got Anchor function : 0x99e0 +const uint16 csAddr_gotAnchor = 0x99e0; +// No Anchor Timeout function : 0x9a1d +const uint16 csAddr_noAnchorTimeout = 0x9a1d; +// Get Out of Lake function : 0x9a7a +const uint16 csAddr_getOutOfLake = 0x9a7a; +// Dig Under Mansion Wall function : 0x9aca +const uint16 csAddr_digMansionWall = 0x9aca; +// Too Dark Here function : 0x9c66 +const uint16 csAddr_tooDarkHere = 0x9c66; +// Examine Banknote function : 0x9c6d +const uint16 csAddr_examineBanknote = 0x9c6d; +// Use Time Pills function : 0x9c79 +const uint16 csAddr_useTimePills = 0x9c79; +// Mansion Intrusion Attempt function : 0x9d45 +const uint16 csAddr_mansionIntrusionAttempt = 0x9d45; +// Second Mansion Intrusion function : 0x9d90 +const uint16 csAddr_secondMansionIntrusion = 0x9d90; +// Third Mansion Intrusion function : 0x9de5 +const uint16 csAddr_thirdMansionIntrusion = 0x9de5; +// Fourth Mansion Intrusion function : 0x9e54 +const uint16 csAddr_fourthMansionIntrusion = 0x9e54; +// Fifth Mansion Intrusion function : 0x9ec3 +const uint16 csAddr_fifthMansionIntrusion = 0x9ec3; +// Sixth Mansion Intrusion function : 0x9f3e +const uint16 csAddr_sixthMansionIntrusion = 0x9f3e; +// Display Message function : 0xa055 +const uint16 csAddr_displayMsg = 0xa055; +// Reject Message function : 0xa4d6 +const uint16 csAddr_rejectMsg = 0xa4d6; + +// Data Segment Addresses +// Timed Callback State Variable : 0x0000 +const uint16 dsAddr_timedCallbackState = 0x0000; // 1 byte + +// Cursor Graphic 8*12px : 0x00da to 0x0139 (Read Only) +const uint16 dsAddr_cursor = 0x00da; + +// Reject Message Address Pointers : (4 * 2-byte) = 0x339e to 0x33a5 +const uint16 dsAddr_rejectMsgPtr = 0x339e; +// Reject Message #0 : 0x33a6 to 0x33c9 +const uint16 dsAddr_rejectMsg0 = 0x33a6; // "I have no idea what to do with it" +// Reject Message #1 : 0x33ca to 0x33f5 +const uint16 dsAddr_rejectMsg1 = 0x33ca; // "I can't imagine what I could do with this" +// Reject Message #2 : 0x33f6 to 0x3425 +const uint16 dsAddr_rejectMsg2 = 0x33f6; // "I can't figure out what I should do with this" +// Reject Message #3 : 0x3426 to 0x344f +const uint16 dsAddr_rejectMsg3 = 0x3426; // "I can't find any reason to mess with it" +// Cool Message : 0x3450 to 0x3456 +const uint16 dsAddr_coolMsg = 0x3450; // "Cool." +// Object Usage Error Message : 0x3457 to 0x3467 +const uint16 dsAddr_objErrorMsg = 0x3457; // "That's no good" +// Car Jack Message : 0x3468 to 0x348f +const uint16 dsAddr_carJackMsg = 0x3468; // "Wow! There's a car jack inside! Great!" +// Spanner Message : 0x3490 to 0x34c6 +const uint16 dsAddr_spannerMsg = 0x3490; // "There's something else inside the toolbox! A spanner!" +// Last Chance Message : 0x34c7 to 0x34d4 +const uint16 dsAddr_lastChanceMsg = 0x34c7; // "Last chance?" +// Give Up Message : 0x34d5 to 0x34e0 +const uint16 dsAddr_giveUpMsg = 0x34d5; // "I give up" +// Avoid Bees Message : 0x34e1 to 0x351e +const uint16 dsAddr_avoidBeesMsg = 0x34e1; // "I'm going to stay at least five meters away from these bees!" +// Boat Empty Message : 0x351f to 0x3541 +const uint16 dsAddr_boatEmptyMsg = 0x351f; // "There's nothing else in the boat" +// Too Hard Wood Message : 0x3542 to 0x3562 +const uint16 dsAddr_tooHardWoodMsg = 0x3542; // "This wood is too hard to break" +// Boo Message : 0x3563 to 0x3569 +const uint16 dsAddr_BooMsg = 0x3563; // "Booo!" +// Dont Push Luck Message : 0x356a to 0x358f +const uint16 dsAddr_dontPushLuckMsg = 0x356a; // "I don't think I should push my luck" +// Ordinary Haystack Message : 0x3590 to 0x35b1 +const uint16 dsAddr_ordinaryHaystackMsg = 0x3590; // "Just an ordinary hay stack. Now." +// Needle Haystack Message : 0x35b2 to 0x35e7 +const uint16 dsAddr_needleHaystackMsg = 0x35b2; // "And they say you can't find a needle in a haystack" +// No Potatoes Message : 0x35e8 to 0x3604 +const uint16 dsAddr_noPotatoMsg = 0x35e8; // "There are no more potatoes" +// Trousers Message : 0x3605 to 0x363e +const uint16 dsAddr_trousersMsg = 0x3605; // "Good I always asked mum for trousers with BIG pockets" +// Life Is Brutal Message : 0x363f to 0x364f +const uint16 dsAddr_lifeIsBrutalMsg = 0x363f; // "Life is brutal" +// Life Brutal Message : 0x3650 to 0x3667 +const uint16 dsAddr_lifeBrutalMsg = 0x3650; // "Life is really brutal" +// Tickled Message : 0x3668 to 0x367e +const uint16 dsAddr_tickledMsg = 0x3668; // "Something tickled me!" +// Its Gone Message : 0x367f to 0x3693 +const uint16 dsAddr_itsGoneMsg = 0x367f; // "At least it's gone" +// Monsters Message : 0x3694 to 0x36c1 +const uint16 dsAddr_monstersMsg = 0x3694; // "Who knows what monsters live in there" +// No Hands Message : 0x36c2 to 0x370e +const uint16 dsAddr_noHandsMsg = 0x36c2; // "I'd better not put my hands in there..." +// Totally Empty Message : 0x370f to 0x372d +const uint16 dsAddr_totalEmptyMsg = 0x370f; // "I can see it's totally empty" +// One Small Step Message : 0x372e to 0x3765 +const uint16 dsAddr_oneSmallStepMsg = 0x372e; // "One small step for man, one big pain in the head" +// No Chance Message : 0x3766 to 0x378f +const uint16 dsAddr_noChanceMsg = 0x3766; // "I won't take my chances a second time" +// Dinosaur Bone Message : 0x3790 to 0x37b7 +const uint16 dsAddr_dinoBoneMsg = 0x3790; // "I really hope this is DINOSAUR bone" +// Wall Shaken Message : 0x37b8 to 0x37e9 +const uint16 dsAddr_wallShakenMsg = 0x37b8; // "Wow! This must have shaken all the nearby walls!" +// Kinda Dark Message : 0x37ea to 0x3800 +const uint16 dsAddr_kindaDarkMsg = 0x37ea; // "It's kinda dark here" +// Not in Dark Message : 0x3801 to 0x3831 +const uint16 dsAddr_notInDarkMsg = 0x3801; // "I'm not going to wander here in the dark again" +// Shut Valve Message : 0x3832 to 0x387b +const uint16 dsAddr_shutValveMsg = 0x3832; // "Shutting the valve shook the dirt from the wall..." +// Need Sunglasses Message : 0x387c to 0x38a6 +const uint16 dsAddr_needSunglassesMsg = 0x387c; // "Sorry buddy, but I need your sunglasses" +// Not Best Place Message : 0x38a7 to 0x38cd +const uint16 dsAddr_notBestPlaceMsg = 0x38a7; // "It's not the best place for diving" +// Not Here Message : 0x38ce to 0x38da +const uint16 dsAddr_notHereMsg = 0x38ce; // "Not here" +// Can't Talk Underwater Message : 0x38db to 0x38fe +const uint16 dsAddr_cantTalkUnderwaterMsg = 0x38db; // "I really can't talk underwater!" +// Not Swimming There Message : 0x38ff to 0x3931 +const uint16 dsAddr_notSwimmingThereMsg = 0x38ff; // "I don't think swimming there is worth the effort" +// Too Little Air Message : 0x3932 to 0x3988 +const uint16 dsAddr_tooLittleAirMsg = 0x3932; // "If I want to get this anchor I have to swim there when I have more air in my lungs" +// Hooked Anchor Message : 0x3989 to 0x39ad +const uint16 dsAddr_hookedAnchorMsg = 0x3989; // "I was really hooked on this anchor!" +// Seaweed Message : 0x39ae to 0x39f5 +const uint16 dsAddr_seaweedMsg = 0x39ae; // "This seaweed is just like the flowers I gave mum on her last birthday" +// Fish Boat Message : 0x39f6 to 0x3a27 +const uint16 dsAddr_fishBoatMsg = 0x39f6; // "I wonder what fish do inside this boat at night" +// Fish Something Message : 0x3a28 to 0x3a59 +const uint16 dsAddr_fishSomethingMsg = 0x3a28; // "I think I have to fish out something down there" +// Fish Don't Worry Message : 0x3a5a to 0x3a84 +const uint16 dsAddr_fishDontWorryMsg = 0x3a5a; // "At least fish don't worry about the rain" +// Not Red Herring Message : 0x3a85 to 0x3ab6 +const uint16 dsAddr_notRedHerringMsg = 0x3a85; // "I hope all this fish stuff is not a red herring" +// Nice Down Message : 0x3ab7 to 0x3acd +const uint16 dsAddr_niceDownMsg = 0x3ab7; // "It's nice down there" +// Hey Let Go Message : 0x3ace to 0x3ae5 +const uint16 dsAddr_heyLetGoMsg = 0x3ace; // "Hey, let go, will ya?!" +// Aaahhh Message : 0x3ae6 to 0x3afc +const uint16 dsAddr_aaahhhMsg = 0x3ae6; // "Aaaaaaaaaaaaahhh!" +// Oops Message : 0x3afd to 0x3b03 +const uint16 dsAddr_oopsMsg = 0x3afd; // "Oops" +// Found Food Message : 0x3b04 to 0x3b2e +const uint16 dsAddr_foundFoodMsg = 0x3b04; // "People leave food in unbelievable places" +// Come Here Message : 0x3b2f to 0x3b58 +const uint16 dsAddr_comeHereMsg = 0x3b2f; // "Come here, I've got something for you" +// Cant Catch Message : 0x3b59 to 0x3b6b +const uint16 dsAddr_cantCatchMsg = 0x3b59; // "I can't catch it!" +// Trapped Mouse Message : 0x3b6c to 0x3b82 +const uint16 dsAddr_trappedMouseMsg = 0x3b6c; // "The mouse is trapped!" +// Yikes Message : 0x3b83 to 0x3b8a +const uint16 dsAddr_yikesMsg = 0x3b83; // "Yikes!" +// Mouse Nerve Message : 0x3b8b to 0x3bab +const uint16 dsAddr_mouseNerveMsg = 0x3b8b; // "Boy, this mouse has some nerve!" +// Drawers Empty Message : 0x3bac to 0x3bd1 +const uint16 dsAddr_drawersEmptyMsg = 0x3bac; // "There's nothing else in the drawers" +// Rid Bush Message 0x3bd2 to 0x3bf5 +const uint16 dsAddr_ridBushMsg = 0x3bd2; // "I must get rid of this bush first" +// Mouse Gone Message : 0x3bf6 to 0x3c0a +const uint16 dsAddr_mouseGoneMsg = 0x3bf6; // "The mouse has gone!" +// Nonsense Message : 0x3c0b to 0x3c15 +const uint16 dsAddr_nonsenseMsg = 0x3c0b; // "Nonsense" +// Good Doggy Message : 0x3c16 to 0x3c30 +const uint16 dsAddr_goodDoggyMsg = 0x3c16; // "I understand. Good doggy" +// Here Boy Message : 0x3c31 to 0x3c3c +const uint16 dsAddr_hereBoyMsg = 0x3c31; // "Here, boy" +// Friends Now Message : 0x3c3d to 0x3c57 +const uint16 dsAddr_friendsNowMsg = 0x3c3d; // "I hope we're friends now" +// Not Think Right Place Message : 0x3c58 to 0x3c7f +const uint16 dsAddr_notThinkRightPlaceMsg = 0x3c58; // "I don't think this is the right place" +// Cutscene Message A : 0x3c80 to 0x3c99 +const uint16 dsAddr_cutsceneMsgA = 0x3c80; // "Hundred moments later" +// Cutscene Message B : 0x3c9a to 0x3cbb +const uint16 dsAddr_cutsceneMsgB = 0x3c9a; // "Another hundred moments later" +// Found Crude Oil Message : 0x3cbc to 0x3ce9 +const uint16 dsAddr_foundCrudeOilMsg = 0x3cbc; // "At least I found crude oil and I'll be rich" +// My Life Message : 0x3cea to 0x3cfa +const uint16 dsAddr_myLifeMsg = 0x3cea; // "That's my life" +// Confusion Message : 0x3cfb to 0x3d00 +const uint16 dsAddr_ConfusionMsg = 0x3cfb; // "!?&!" +// Grandpa Promise Message : 0x3d01 to 0x3d1f +const uint16 dsAddr_grandpaPromiseMsg = 0x3d01; // "But grandpa, you promised!" +// Oh Lets Go Message : 0x3d20 to 0x3d39 +const uint16 dsAddr_ohLetsGoMsg = 0x3d20; // "Oh all right. Let's go" +// Bye Message : 0x3d3a to 0x3d3f +const uint16 dsAddr_byeMsg = 0x3d3a; // "Bye." +// No Need Message : 0x3d40 to 0x3d58 +const uint16 dsAddr_noNeedMsg = 0x3d40; // "No need to do it again" +// Girl Talk Message : 0x3d59 to 0x3d85 +const uint16 dsAddr_girlTalkMsg = 0x3d59; // "I really don't know how to talk to girls" +// Dont Work Purpose Message : 0x3d86 to 0x3dae +const uint16 dsAddr_dontWorkPurposeMsg = 0x3d86; // "I usually don't work without a purpose" +// Nut Real Message : 0x3daf to 0x3dc5 +const uint16 dsAddr_nutRealMsg = 0x3daf; // "Only the nut is real" +// Hen Fly Message : 0x3dc6 to 0x3df3 +const uint16 dsAddr_henFlyMsg = 0x3dc6; // "I wonder if hens can fly. Come here, baby" +// First Test Fail Message : 0x3df4 to 0x3e07 +const uint16 dsAddr_firstTestFailMsg = 0x3df4; // "First test failed" +// Rid Frustations Message : 0x3e08 to 3e30 +const uint16 dsAddr_ridFrustationsMsg = 0x3e08; // "I'd already got rid of my frustrations" +// Road Nowhere Message : 0x3e31 to 0x3e4e +const uint16 dsAddr_roadNowhereMsg = 0x3e31; // "Nah. It's a road to nowhere" +// Open Boot Message 0x3e4f to 0x3e62 +const uint16 dsAddr_openBootMsg = 0x3e4f; // "It opens the boot" +// Shut Tight Message : 0x3e63 to 0x3e74 +const uint16 dsAddr_shutTightMsg = 0x3e63; // "It's shut tight" +// Boot Empty Message : 0x3e75 to 0x3e97 +const uint16 dsAddr_bootEmptyMsg = 0x3e75; // "There's nothing else in the boot" +// Clothes Dry Message : 0x3e98 to 0x3eb1 +const uint16 dsAddr_clothesDryMsg = 0x3e98; // "The clothes are dry now." +// Crow Kill Message : 0x3eb2 to 0x3ed5 +const uint16 dsAddr_crowKillMsg = 0x3eb2; // "I'm sure these crows will kill me" +// Get Rid Of Guard First Message : 0x3ed6 to 0x3f29 +const uint16 dsAddr_getRidOfGuardFirstMsg = 0x3ed6; // "If I want to get inside I must get rid of this guard first..." +// Wall Too Smooth Message : 0x3f2a to 0x3f53 +const uint16 dsAddr_wallTooSmoothMsg = 0x3f2a; // "The wall surface is too smooth to climb" +// Too Much Resin To Climb Message : 0x3f54 to 0x3f84 +const uint16 dsAddr_tooMuchResinToClimbMsg = 0x3f54; // "I could climb it if there wasn't so much resin" +// Only Green Rect Message : 0x3f85 to 0x3feb +const uint16 dsAddr_onlyGreenRectMsg = 0x3f85; // "The only green stuff that I like is that rectangular piece of paper with..." +// Don't Wanna Touch Hedgehog Message : 0x3fec to 0x402d +const uint16 dsAddr_dontWannaTouchHedgehogMsg = 0x3fec; // "I don't wanna touch it. Its spines could hurt my delicate hands" +// Not Hungry Message : 0x402e to 0x4046 +const uint16 dsAddr_notHungryMsg = 0x402e; // "Thanks, I'm not hungry" +// No Long Hands Message : 0x4047 to 0x406c +const uint16 dsAddr_noLongHandsMsg = 0x4047; // "I really don't have such long hands" +// Too Far To Swim Message : 0x406d to 0x4089 +const uint16 dsAddr_tooFarToSwimMsg = 0x406d; // "It's too far to swim there" +// Echo Message : 0x408a to 0x4090 +const uint16 dsAddr_echoMsg = 0x408a; // "Echo!" +// Loud Echo Message : 0x4091 to 0x4097 +const uint16 dsAddr_loudEchoMsg = 0x4091; // "ECHO!" +// Who There Message : 0x4098 to 0x40a6 +const uint16 dsAddr_whoThereMsg = 0x4098; // "Who's there?!" +// Loud Who There Message : 0x40a7 to 0x40b5 +const uint16 dsAddr_loudWhoThereMsg = 0x40a7; // "WHO'S THERE?!" +// Dont Copy Message : 0x40b6 to 0x40cd +const uint16 dsAddr_dontCopyMsg = 0x40b6; // "DON'T COPY ME!" +// Loud Dont Copy Message : 0x40ce to 0x40e7 +const uint16 dsAddr_loudDontCopyMsg = 0x40ce; // "DON'T COPY ME!!!" +// Throw Rock Message : 0x40e8 to 0x410e +const uint16 dsAddr_throwRockMsg = 0x40e8; // "OR I WILL THROW A ROCK DOWN THERE!" +// Or I Will Message : 0x410f to 0x411c +const uint16 dsAddr_orIWillMsg = 0x410f; // "OR I WILL" +// Still There Message : 0x411d to 0x4132 +const uint16 dsAddr_stillThereMsg = 0x411d; // "Are you still there?" +// No Bucket Message : 0x4133 to 0x4163 +const uint16 dsAddr_noBucketMsg = 0x4133; // "It's not a barrel-organ. And there's no bucket." +// Dont Need To Open Message : 0x4164 to 0x417d +const uint16 dsAddr_dontNeedToOpenMsg = 0x4164; // "I don't need to open it" +// Hmm Grass Message : 0x417e to 41b0 +const uint16 dsAddr_hmmGrassMsg = 0x417e; // "Hmmm. Grass..." +// Find Nut Message : 0x41b1 to 0x41ee +const uint16 dsAddr_findNutMsg = 0x41b1; // "I won't find the nut just like that. The grass is too dense" +// Not Horny Message : 0x41ef to 0x41fe +const uint16 dsAddr_notHornyMsg = 0x41ef; // "I'm not horny" +// Can't Jump So High Message : 0x41ff to 0x423e +const uint16 dsAddr_CantJumpMsg = 0x41ff; // "No way I can jump so high, cause, err, white men can't jump" +// Don't Need It Message : 0x423f to 0x4250 +const uint16 dsAddr_dontNeedItMsg = 0x423f; // "I don't need it" +// Not Santa Claus Message : 0x4251 to 0x4266 +const uint16 dsAddr_notSantaClausMsg = 0x4251; // "I'm not Santa Claus" +// No Plastic Imitations Message : 0x4267 to 0x4288 +const uint16 dsAddr_noPlasticImitationsMsg = 0x4267; // "I don't need plastic imitations" +// Too Fragile Message : 0x4289 to 0x42ab +const uint16 dsAddr_tooFragileMsg = 0x4289; // "It's too fragile to carry around" +// Keep It Open Message : 0x42ac to 0x42c6 +const uint16 dsAddr_keepItOpenMsg = 0x42ac; // "I'd like to keep it open" +// Not Taking Socks Message : 0x42c7 to 0x4305 +const uint16 dsAddr_notTakingSocksMsg = 0x42c7; // "I really don't want to walk around with someone else's socks" +// Not Tired Message : 0x4306 to 0x431d +const uint16 dsAddr_notTiredMsg = 0x4306; // "Thanks, I'm not tired" +// Too Big Message : 0x431e to 0x434d +const uint16 dsAddr_tooBigMsg = 0x431e; // "It's too big and I doubt if I'll ever need it" +// No Secret Passage Message : 0x434e to 0x437f +const uint16 dsAddr_noSecretPassageMsg = 0x434e; // "I don't think there's any secret passage inside" +// No Fruit Message : 0x4380 to 0x43ab +const uint16 dsAddr_noFruitMsg = 0x4380; // "There are no more interesting fruits here" +// Jug Me Message : 0x43ac to 0x43cd +const uint16 dsAddr_jugMeMsg = 0x43ac; // "They can jug me if I steal this" +// Leave Flowers Alone Message : 0x43ce to 0x4411 +const uint16 dsAddr_leaveFlowersAloneMsg = 0x43ce; // "I'd better leave it. Women are really oversensitive about flowers." +// Mirror Mirror Message : 0x4412 to 0x444e +const uint16 dsAddr_mirrorMirrorMsg = 0x4412; // "Mirror, Mirror on the wall...." +// Think Too Long Message : 0x444f to 0x446a +const uint16 dsAddr_thinkTooLongMsg = 0x444f; // "Hey, don't think too long" +// Hint Male Message : 0x446b to 0x4491 +const uint16 dsAddr_HintMaleMsg = 0x446b; // "A hint: Someone in this room, a male" +// OK Wait Message : 0x4492 to 0x44a6 +const uint16 dsAddr_okWaitMsg = 0x4492; // "OK, take your time" +// Busy Thinking Message : 0x44a7 to 0x44d5 +const uint16 dsAddr_busyThinkingMsg = 0x44a7; // "I'd better not interrupt it's thought process" +// No Dentists Message : 0x44d6 to 0x450d +const uint16 dsAddr_noDentistsMsg = 0x44d6; // "I don't want to have anything in common with dentists" +// Too Heavy Message : 0x450e to 0x4531 +const uint16 dsAddr_tooHeavyMsg = 0x450e; // "It's too heavy. Not that I'm wimp" +// What Got Message : 0x4532 to 0x4554 +const uint16 dsAddr_whatGotMsg = 0x4532; // "Let's look what we've got here" +// Strawberry Jam Message : 0x4555 to 0x4567 +const uint16 dsAddr_strawberryJamMsg = 0x4555; // "Strawberry jam" +// Gooseberry Jam Message : 0x4568 to 0x457a +const uint16 dsAddr_gooseberryJamMsg = 0x4568; // "Gooseberry jam" +// Blackberry Jam Message : 0x457b to 0x458d +const uint16 dsAddr_blackberryJamMsg = 0x457b; // "Blackberry jam" +// Bilberry Jam Message : 0x458e to 0x459e +const uint16 dsAddr_bilberryJamMsg = 0x458e; // "Bilberry jam" +// Get Me Out Jam Message : 0x459f to 0x45b7 +const uint16 dsAddr_getMeOutJamMsg = 0x459f; // "Get me out of this jam!" +// Rosemary Jam Message : 0x45b8 to 0x45d9 +const uint16 dsAddr_rosemaryJamMsg = 0x45b8; // "Oh, and there is Rosemary jam" +// Know Rosemary Message : 0x45da to 0x4602 +const uint16 dsAddr_knowRosemaryMsg = 0x45da; // "I used to know someone called Rosemary" +// Unwanted Jams Message : 0x4603 to 0x461c +const uint16 dsAddr_unwantedJamsMsg = 0x4603; // "I don't want those jams" +// Too Dark Message : 0x461d to 0x463b +const uint16 dsAddr_TooDarkMsg = 0x461d; // "It's too dark to see clearly" +// Yeow Message : 0x463c to 0x4649 +const uint16 dsAddr_yeowMsg = 0x463c; // "YEEEOOOWWWW!" +// Yawn Message : 0x464a to 0x4651 +const uint16 dsAddr_yawnMsg = 0x464a; // "(yawn)" +// Laughter Message : 0x4652 to 0x465d +const uint16 dsAddr_laughterMsg = 0x4652; // "(laughter)" +// No Hands Sharp Thorns Message : 0x465e to 0x46a0 +const uint16 dsAddr_noHandsSharpThornsMsg = 0x465e; // "I can't remove it with my hands. these thorns look really sharp" +// No Chainsaw Fuel Message : 0x46a1 to 0x46c2 +const uint16 dsAddr_noChainsawFuelMsg = 0x46a1; // "There's no fuel in the chainsaw" +// Thorns Too Thin Message : 0x46c3 to 0x46f6 +const uint16 dsAddr_thornsTooThinMsg = 0x46c3; // "Thorns are too thin, the chainsaw is useless here" +// Rock Walking Gee Message : 0x46f7 to 0x473c +const uint16 dsAddr_rockWalkingGeeMsg = 0x46f7; // "Yeah, great idea. Let's take this rock and walk around a bit. Gee..." +// Butterfly Message : 0x473d to 0x477a +const uint16 dsAddr_butterflyMsg = 0x473d; // "I'd better leave them alone, they make this place beautiful" +// Not Sure If Alive Message : 0x477b to 0x4797 +const uint16 dsAddr_notSureIfAliveMsg = 0x477b; // "I'm not sure if it's alive" + +// FIXME - Unknown where this is used.. Talking to SOMETHING... +// Unknown Language Message : 0x4798 to 0x47be +const uint16 dsAddr_unknownLanguageMsg = 0x4798; // "I don't know what language it speaks" + +// Hole Too Narrow Message : 0x47bf to 0x47e6 +const uint16 dsAddr_holeTooNarrowMsg = 0x47bf; // "The hole is too narrow to fit my hand" +// Bird Attack Message : 0x47e7 to 0x4807 +const uint16 dsAddr_birdAttackMsg = 0x47e7; // "Hey You! Wake up! Bird attack!" +// No Search Warrant Message : 0x4808 to 0x4827 +const uint16 dsAddr_noSearchWarrantMsg = 0x4808; // "I don't have a search-warrant" +// Uninteresting Haystack Message : 0x4828 to 0x485f +const uint16 dsAddr_uninterestingHaystackMsg = 0x4828; // "I don't see anything interesting about this haystack" +// More Complicated Message : 0x4860 to 0x4881 +const uint16 dsAddr_moreComplicatedMsg = 0x4860; // "It's more complicated than that" +// Nut Rake Message : 0x4882 to 0x48be +const uint16 dsAddr_nutRakeMsg = 0x4882; // "It's pointless, the nut will slip between the rake's teeth" +// Paddle Broken Message : 0x48bf to 0x48d5 +const uint16 dsAddr_paddleBrokenMsg = 0x48bf; // "The paddle is BROKEN" +// Branch Not Paddle Message : 0x48d6 to 0x4912 +const uint16 dsAddr_branchNotPaddleMsg = 0x48d6; // "This branch is not a paddle. It doesn't even look like one" +// Try Somewhere Else Message : 0x4913 to 0x495b +const uint16 dsAddr_trySomewhereElseMsg = 0x4913; // "I'd better try somewhere else - I suppose this side is heavily guarded" +// Sharpen Not Pulverize Message : 0x495c to 0x4983 +const uint16 dsAddr_sharpenNotPulverizeMsg = 0x495c; // "I needed to sharpen it, not pulverize" +// Can't Do Anything Too Dark Message : 0x4984 to 0x49ad +const uint16 dsAddr_cantDoTooDarkMsg = 0x4984; // "I can't do anything here, it's too dark" +// Bribe Message : 0x49ae to 0x49d0 +const uint16 dsAddr_BribeMsg = 0x49ae; // "Here, let's make your pocket fat." +// Bank Note Message : 0x49d1 to 0x4a28 +const uint16 dsAddr_bankNoteMsg = 0x49d1; // "It's a note from some bank..." +// Show Her Money Message : 0x4a29 to 0x4a5a +const uint16 dsAddr_showHerMoneyMsg = 0x4a29; // "If I just show her the money, she might take it" +// Hundred Bucks Message : 0x4a5b to 0x4a6e +const uint16 dsAddr_hundredBucksMsg = 0x4a5b; // "A hundred bucks!!!" +// Want Blood Message : 0x4a6f to 0x4a7d +const uint16 dsAddr_wantBloodMsg = 0x4a6f; // "I want Blood!" +// Dont Leave Mansion Message : 0x4a7e to 0x4aaf +const uint16 dsAddr_dontLeaveMansionMsg = 0x4a7e; // "I don't want to leave the mansion, I want blood!" +// Wimp Message : 0x4ab0 to 0x4acc +const uint16 dsAddr_WimpMsg = 0x4ab0; // "I'm a pathetic little wimp" +// Strange Drawer Message : 0x4acd to 0x4b0c +const uint16 dsAddr_strangeDrawerMsg = 0x4acd; // "Strange, but the drawer is stuck if the next drawer is open" +// Not Ordinary Drawers Message : 0x4b0d to 0x4b38 +const uint16 dsAddr_notOrdinaryDrawersMsg = 0x4b0d; // "Maybe these are not just ordinary drawers!" +// Drawer Open Message : 0x4b39 to 0x4b6b +const uint16 dsAddr_drawerOpenMsg = 0x4b39; // "I cannot open the drawer if the next one is open!" +// Blue Interior Message 0x4b6c to 0x4b86 +const uint16 dsAddr_blueInteriorMsg = 0x4b6c; // "It's got a blue interior" +// Red Interior Message : 0x4b87 to 0x4ba0 +const uint16 dsAddr_redInteriorMsg = 0x4b87; // "It's got a red interior" +// Grey Interior Message : 0x4ba1 to 0x4bbb +const uint16 dsAddr_greyInteriorMsg = 0x4ba1; // "It's got a grey interior" +// Green Interior Message : 0x4bbc to 0x4bd7 +const uint16 dsAddr_greenInteriorMsg = 0x4bbc; // "It's got a green interior" +// Brown Interior Message : 0x4bd8 to 0x4bf3 +const uint16 dsAddr_brownInteriorMsg = 0x4bd8; // "It's got a brown interior" +// Pink Interior Message : 0x4bf4 to 0x4c0e +const uint16 dsAddr_pinkInteriorMsg = 0x4bf4; // "It's got a pink interior" +// Dictaphone Inside Message : 0x4c0f to 0x4c31 +const uint16 dsAddr_dictaphoneInsideMsg = 0x4c0f; // "Wow! There's a dictaphone inside!" +// Found Polaroid Message : 0x4c32 to 0x4c60 +const uint16 dsAddr_foundPolaroidMsg = 0x4c32; // "There's a polaroid inside! I might need that" +// Book Held Message : 0x4c61 to 0x4c83 +const uint16 dsAddr_bookHeldMsg = 0x4c61; // "Something's got hold of the book!" +// Secret Compartment Message : 0x4c84 to 0x4c9f +const uint16 dsAddr_secretCompartmentMsg = 0x4c84; // "Wow! A secret compartment!" +// Dont Mess Message : 0x4ca0 to 0x4cc6 +const uint16 dsAddr_dontMessMsg = 0x4ca0; // "I don't need to mess with it anymore" +// Full Automatic Message : 0x4cc7 to 0x4cd8 +const uint16 dsAddr_fullAutomaticMsg = 0x4cc7; // "Fully Automatic" +// No More Sheets Message : 0x4cd9 to 0x4d01 +const uint16 dsAddr_noMoreSheetsMsg = 0x4cd9; // "Right now I don't need any more sheets" +// No Deprave Message : 0x4d02 to 0x4d29 +const uint16 dsAddr_noDepraveMsg = 0x4d02; // "Nah, I don't want to deprave the kids" +// No Read Again Message : 0x4d2a to 0x4d5a +const uint16 dsAddr_noReadAgainMsg = 0x4d2a; // "I don't want to read it again. I might like it." +// TV Off Message : 0x4d5b to 0x4d7f +const uint16 dsAddr_tvOffMsg = 0x4d5b; // "I just realised that the TV is off" +// Not Happen Message : 0x4d80 to 0x4d92 +const uint16 dsAddr_NotHappenMsg = 0x4d80; // "Nothing happened" +// Tape Started Message : 0x4d93 to 0x4da5 +const uint16 dsAddr_tapeStartedMsg = 0x4d93; // "The tape started!" +// Much Better Message : 0x4da6 to 0x4dba +const uint16 dsAddr_muchBetterMsg = 0x4da6; // "That's much better" +// No Sleep Message : 0x4dbb to 0x4dd2 +const uint16 dsAddr_noSleepMsg = 0x4dbb; // "I don't want to sleep" +// Just Cork Message : 0x4dd3 to 0x4de5 +const uint16 dsAddr_justCorkMsg = 0x4dd3; // "It's just a cork" +// Enough Photos Message : 0x4de6 to 0x4e04 +const uint16 dsAddr_enoughPhotosMsg = 0x4de6; // "I don't need any more photos" +// Record Scare Message : 0x4e05 to 0x4e31 +const uint16 dsAddr_recordScareMsg = 0x4e05; // "Yeah, I can record this and scare the cats" +// Already Recorded Message : 0x4e32 to 0x4e57 +const uint16 dsAddr_alreadyRecordedMsg = 0x4e32; // "I already recorded what I wanted to" +// Can't Record No Batteries Message : 0x4e58 to 0x4e8d +const uint16 dsAddr_cantRecordNoBatteriesMsg = 0x4e58; // "I can't record anything until I find some batteries" + +// FIXME - Not sure how to get this message. Dictaphone with no batteries somewhere? Radio? +// No Batteries No Fun Message : 0x4e8e to 0x4ea4 +const uint16 dsAddr_NoBatteriesNoFunMsg = 0x4e8e; // "No batteries, no fun" + +// Not Right Moment Message : 0x4ea5 to 0x4ecd +const uint16 dsAddr_notRightMomentMsg = 0x4ea5; // "I don't think this is the right moment" +// Cook Around Message : 0x4ece to 0x4ef9 +const uint16 dsAddr_cookAroundMsg = 0x4ece; // "I can't do anything with this cook around" +// Same Bottle Message : 0x4efa to 0x4f3c +const uint16 dsAddr_sameBottleMsg = 0x4efa; // "The bottle's the same, but I doubt if it's enough to fool anyone" +// Break Flatten Message : 0x4f3d to 0x4f68 +const uint16 dsAddr_breakFlattenMsg = 0x4f3d; // "I wanted to break it, not to flatten it!" +// What Inside Message : 0x4f69 to 0x4f9a +const uint16 dsAddr_whatInsideMsg = 0x4f69; // "I was always curious what's inside these things" +// Rest Useless Message : 0x4f9b to 0x4fb0 +const uint16 dsAddr_restUselessMsg = 0x4f9b; // "The rest is useless" +// Two Batteries Message : 0x4fb1 to 0x4fca +const uint16 dsAddr_twoBatteriesMsg = 0x4fb1; // "Wow! Two 1.5V batteries!" +// One Taken Message : 0x4fcb to 0x4fe1 +const uint16 dsAddr_oneTakenMsg = 0x4fcb; // "This one's taken, OK?" +// Slight Mad Message : 0x4fe2 to 0x5009 +const uint16 dsAddr_slightMadMsg = 0x4fe2; // "It finally happened. I'm slightly mad" +// Paper Burnt Message : 0x500a to 0x502a +const uint16 dsAddr_paperBurntMsg = 0x500a; // "The paper burnt out completely!" +// Burn Baby Message : 0x502b to 0x503d +const uint16 dsAddr_burnBabyMsg = 0x502b; // "Burn, baby, burn!" +// Voila Message : 0x503e to 0x5045 +const uint16 dsAddr_voilaMsg = 0x503e; // "Voila" +// Too Hot Message : 0x5046 to 0x505d +const uint16 dsAddr_tooHotMsg = 0x5046; // "It's too hot to touch!" +// Frozen Shelf Message : 0x505e to 0x5081 +const uint16 dsAddr_frozenShelfMsg = 0x505e; // "It has frozen hard onto the shelf!" +// Yummy Message : 0x5082 to 0x5089 +const uint16 dsAddr_yummyMsg = 0x5082; // "Yummy" +// Dislike Veal Message : 0x508a to 0x50a5 +const uint16 dsAddr_dislikeVealMsg = 0x508a; // "I never liked veal anyway" +// No Reason Message : 0x50a6 to 0x50c2 +const uint16 dsAddr_noReasonMsg = 0x50a6; // "There's no reason to do it" +// Fooled Once Message : 0x50c3 to 0x50e0 +const uint16 dsAddr_fooledOnceMsg = 0x50c3; // "I'd already fooled him once" +// Mike Voice Test Message : 0x50e1 to 0x5100 +const uint16 dsAddr_mikeVoiceTestMsg = 0x50e1; // "Mike, activate the voice test" +// Not My Voice Message : 0x5101 to 0x5123 +const uint16 dsAddr_notMyVoiceMsg = 0x5101; // "I won't cheat Mike with MY voice" +// Singing Message : 0x5124 to 0x5137 +const uint16 dsAddr_singingMsg = 0x5124; // "siiiiinging!" +// Mike Scent Test Message : 0x5138 to 0x5160 +const uint16 dsAddr_mikeScentTestMsg = 0x5138; // "Mike, let's get on with the scent test" +// Mike View Test Message : 0x5161 to 0x517a +const uint16 dsAddr_mikeViewTestMsg = 0x5161; // "Mike, run the view test" +// Cutscene Message #0 : 0x517b to 0x51a6 +const uint16 dsAddr_cutsceneMsg0 = 0x517b; // "A secret diary of ..." +// Cant Hide Message : 0x51a7 to 0x51ba +const uint16 dsAddr_cantHideMsg = 0x51a7; // "I can't hide here!" +// John Outside Message : 0x51bb to 0x51e6 +const uint16 dsAddr_johnOutsideMsg = 0x51bb; // "There's John Noty outside! I can't go out!" +// Was Close Message : 0x51e7 to 0x51f7 +const uint16 dsAddr_wasCloseMsg = 0x51e7; // "That was close" +// Cork In Hole Message : 0x51f8 to 0x5217 +const uint16 dsAddr_corkInHoleMsg = 0x51f8; // "The cork is stuck in the hole" +// Fits Perfect Message : 0x5218 to 0x522b +const uint16 dsAddr_fitsPerfectMsg = 0x5218; // "It fits perfectly!" +// Enough Water Message : 0x522c to 0x524e +const uint16 dsAddr_enoughWaterMsg = 0x522c; // "There's enough water in the sink" +// No Hot Water Message : 0x524f to 0x5271 +const uint16 dsAddr_noHotWaterMsg = 0x524f; // "There's no hot water in the sink" +// Label Off Message : 0x5272 to 0x528a +const uint16 dsAddr_labelOffMsg = 0x5272; // "The label has come off!" +// Cork Too Small Message : 0x528b to 0x52a8 +const uint16 dsAddr_corkTooSmallMsg = 0x528b; // "The cork is a bit too small" +// Not Try Now Message : 0x52a9 to 0x52ca +const uint16 dsAddr_notTryNowMsg = 0x52a9; // "There's no need to try them now" +// No Salad Message : 0x52cb to 0x52f5 +const uint16 dsAddr_noSaladMsg = 0x52cb; // "I don't want to turn myself into a salad" +// Nah Message : 0x52f6 to 0x52fd +const uint16 dsAddr_nahMsg = 0x52f6; // "Nah" +// Vent First Message : 0x52fe to 0x5325 +const uint16 dsAddr_ventFirstMsg = 0x52fe; // "I'd better stop this ventilator first" +// Catch John First Message : 0x5326 to 0x5348 +const uint16 dsAddr_catchJohnFirstMsg = 0x5326; // "I'd better catch John Noty first" +// Only Chilli Message : 0x5349 to 0x5371 +const uint16 dsAddr_onlyChilliMsg = 0x5349; // "Good this red stuff is only a chilli" +// Water Hot Message : 0x5372 to 0x538c +const uint16 dsAddr_waterHotMsg = 0x5372; // "The water looks very hot" +// Sink Full Message : 0x538d to 0x53ac +const uint16 dsAddr_sinkFullMsg = 0x538d; // "The sink is full of hot water" +// No Sock Store Message : 0x53ad to 0x53dc +const uint16 dsAddr_noSockStoreMsg = 0x53ad; // "I don't have anything to store these socks in" +// Show Papers Message : 0x53dd to 0x53f1 +const uint16 dsAddr_showPapersMsg = 0x53dd; // "Here are my papers" +// Got Permission Message : 0x53f2 to 0x5410 +const uint16 dsAddr_gotPermissionMsg = 0x53f2; // "I already got the permission" +// Saving Fine Message : 0x5411 to 0x5462 +const uint16 dsAddr_SavingFineMsg = 0x5411; // "Saving is a very fine thing..." +// Love Captain Message : 0x5463 to 0x5474 +const uint16 dsAddr_loveCaptainMsg = 0x5463; // "I love captain" +// Soccer Rulz Message : 0x5475 to 0x5483 +const uint16 dsAddr_soccerRulzMsg = 0x5475; // "Soccer rulz" +// Tree Cut Message : 0x5484 to 0x54c3 +const uint16 dsAddr_treeCutMsg = 0x5484; // "Don't cut the trees..." +// Visa Accepted Message : 0x54c4 to 0x54d4 +const uint16 dsAddr_visaAcceptedMsg = 0x54c4; // "VISA Accepted" +// Other Graffiti Message : 0x54d5 to 0x54f6 +const uint16 dsAddr_otherGraffitiMsg = 0x54d5; // "The rest of graffiti is obscene" +// First Trial Message : 0x54f7 to 0x5510 +const uint16 dsAddr_firstTrialMsg = 0x54f7; // "Sir, I'm Mark. A rookie" +// Locked Message : 0x5511 to 0x551e +const uint16 dsAddr_lockedMsg = 0x5511; // "It's Locked!" +// Thanks Message : 0x551f to 0x5527 +const uint16 dsAddr_ThanksMsg = 0x551f; // "Thanks." +// Unknown Usage Message : 0x5528 to 0x555c +const uint16 dsAddr_unkUsageMsg = 0x5528; // "I don't have any idea what to do with it right now" +// Idea Message : 0x555d to 0x5576 +const uint16 dsAddr_ideaMsg = 0x555d; // "That gives me an idea" +// Check Works Message : 0x5577 to 0x5599 +const uint16 dsAddr_checkWorksMsg = 0x5577; // "Now I got to check if it works" +// Time To Call Message : 0x559a to 0x55bf +const uint16 dsAddr_timeToCallMsg = 0x559a; // "I think it is time to call captain" +// Meal Finished Message : 0x55c0 to 0x55da +const uint16 dsAddr_mealFinishedMsg = 0x55c0; // "Hey! I finished my meal." +// Bowl Welded Message : 0x55db to 0x55fe +const uint16 dsAddr_bowlWeldedMsg = 0x55db; // "Wow. He got welded to the bowl" +// Gotcha Message : 0x55ff to 0x5607 +const uint16 dsAddr_gotchaMsg = 0x55ff; // "Gotcha" +// No Pocket Message : 0x5608 to 0x5631 +const uint16 dsAddr_noPocketMsg = 0x5608; // "I don't want to touch his pockets again." +// Does Not Work Message : 0x5632 to 0x5645 +const uint16 dsAddr_doesNotWorkMsg = 0x5632; // "That doesn't work" +// Message : 0x5646 to 0x5655 +const uint16 dsAddr_fnMsg1 = 0x5646; // "Piece of cake" +// Message : 0x5656 to 0x5679 +const uint16 dsAddr_fnMsg2 = 0x5656; // "And how am I supposed to get back?" +// Message : 0x567a to 0x5681 +const uint16 dsAddr_fnMsg3 = 0x567a; // "Great" +// Message : 0x5682 to 0x5695 +const uint16 dsAddr_fnMsg4 = 0x5682; // "Oh, yeah, right" +// Pull Object Message #1 : 0x5696 to 0x56ab +const uint16 dsAddr_pullObjMsg1 = 0x5696; // "I can't pull it out" +// Dont Want To Touch Message : 0x56ac to 0x56d9 +const uint16 dsAddr_dontWantToTouchMsg = 0x56ac; // "I don't want to touch it - I might get hurt" +// Fence Blocks Message : 0x56da to 0x56f6 +const uint16 dsAddr_fenceBlocksMsg = 0x56da; // "The fence blocks the way" +// Not Want To Sleep Message : 0x56f7 to 0x570e +const uint16 dsAddr_notWantToSleepMsg = 0x56f7; // "I don't want to sleep" +// Pull Object Message #2 : 0x570f to 0x5721 +const uint16 dsAddr_pullObjMsg2 = 0x570f; // "I can't reach it" +// Hello Question Message : 0x5722 to 0x5729 +const uint16 dsAddr_helloQMsg = 0x5722; // "Hello?" +// Totally Addicted Message : 0x572a to 0x5741 +const uint16 dsAddr_totallyAddictedMsg = 0x572a; // "He's totally addicted" +// What About Message : 0x5742 to 0x5756 +const uint16 dsAddr_whatAboutMsg = 0x5742; // "What about a new" +// Hot Off Message : 0x5757 to 0x576f +const uint16 dsAddr_hotOffMsg = 0x5757; // "hot off the press" +// Full Color Message : 0x5770 to 0x5781 +const uint16 dsAddr_fullColorMsg = 0x5770; // "full-color" +// Special Edition Message : 0x5782 to 0x5798 +const uint16 dsAddr_specialEdMsg = 0x5782; // "special edition" +// Soldier News Message : 0x5799 to 0x57b1 +const uint16 dsAddr_soldierNewsMsg = 0x5799; // "of Soldier News?!" +// Pole Climb Done Message : 0x57b2 to 0x57bf +const uint16 dsAddr_poleClimbDoneMsg = 0x57b2; // "Never Again!" +// Vac Message : 0x57c0 to 0x57de +const uint16 dsAddr_vacMsg = 0x57c0; // "What am I? A vacuum cleaner?!" +// Cutscene Message #1 : 0x57df to 0x5809 +const uint16 dsAddr_cutsceneMsg1 = 0x57df; // "sixty seven rude words later" +// Cutscene Message #2 : 0x580a to 0x5826 +const uint16 dsAddr_cutsceneMsg2 = 0x580a; // "Meanwhile in the mansion" +// Now Open Message : 0x5827 to 0x5836 +const uint16 dsAddr_nowOpenMsg = 0x5827; // "Now it's open" +// Cmon Baby Message : 0x5837 to 0x5854 +const uint16 dsAddr_cmonBabyMsg = 0x5837; // "C'mon baby, it's all yours!" +// Talk Not Now Message : 0x5855 to 0x5882 +const uint16 dsAddr_talkNotNowMsg = 0x5855; // "I've got no reason to talk to him right now." +// Yeah Right Message : 0x5883 to 0x5893 +const uint16 dsAddr_yeahRightMsg = 0x5883; // "Yeah right!" +// Barman Too Close Message : 0x5894 to 0x58af +const uint16 dsAddr_barmanTooCloseMsg = 0x5894; // "The barman is too close" +// Yuck Message : 0x58b0 to 0x58b6 +const uint16 dsAddr_yuckMsg = 0x58b0; // "Yuck!" +// Prefer Water Message : 0x58b7 to 0x58c7 +const uint16 dsAddr_preferWaterMsg = 0x58b7; // "I prefer water" +// Too Weak To Climb Message : 0x58c8 to 0x58e2 +const uint16 dsAddr_tooWeakToClimbMsg = 0x58c8; // "I'm too weak to climb it" +// Spring Prick Message : 0x58e3 to 0x5904 +const uint16 dsAddr_springPrickMsg = 0x58e3; // "The springs would prick my back" +// Food Alive Message : 0x5905 to 0x592e +const uint16 dsAddr_foodAliveMsg = 0x5905; // "No, thanks. This food seems still alive" +// Door Closed Message : 0x592f to 0x5954 +const uint16 dsAddr_doorClosedMsg = 0x592f; // "The door is closed. What a surprise." +// Empty Message : 0x5955 to 0x5961 +const uint16 dsAddr_emptyMsg = 0x5955; // "It's Empty" +// Geography Class Message : 0x5962 to 0x599c +const uint16 dsAddr_geographyClassMsg = 0x5962; // "I should have paid more attention in geography classes." +// Don't Need Mess Message : 0x599d to 0x59b5 +const uint16 dsAddr_dontNeedMessMsg = 0x599d; // "I don't need this mess" +// Seen Softer Rocks Message : 0x59b6 to 0x59da +const uint16 dsAddr_seenSofterRocksMsg = 0x59b6; // "Thanks, but I've seen softer rocks" +// Too Blunt Message : 0x59db to 0x5a00 +const uint16 dsAddr_tooBluntMsg = 0x59db; // "They are too blunt to be of any use" +// Useless Models Message : 0x5a01 to 0x5a1f +const uint16 dsAddr_uselessModelsMsg = 0x5a01; // "What's the use of the models?" +// Barman Will Notice Message : 0x5a20 to 0x5a50 +const uint16 dsAddr_barmanWillNoticeMsg = 0x5a20; // "The barman will surely notice its disappearing" +// Too Much To Drink Message : 0x5a51 to 0x5a95 +const uint16 dsAddr_tooMuchToDrinkMsg = 0x5a51; // "It'd take too much time to drink it..." +// 0x5a96 to 0x5a97 : 2 extra null bytes (padding?) +// Not Thief Message : 0x5a98 to 0x5ac5 +const uint16 dsAddr_notThiefMsg = 0x5a98; // "I'm not a thief. And it's empty, by the way." +// Too Many To Search Message : 0x5ac6 to 0x5aec +const uint16 dsAddr_tooManyToSearchMsg = 0x5ac6; // "There are too many of them to search" +// Captain Would Not Fit Message : 0x5aed to 0x5b26 +const uint16 dsAddr_captainWouldNotFitMsg = 0x5aed; // "Captain surely wouldn't fit them. I must look elsewhere" +// Chickening Never Message : 0x5b27 to 0x5b3e +const uint16 dsAddr_chickenNeverMsg = 0x5b27; // "Chickening? Me? Never!" +// Can't Open It Message : 0x5b3f to 0x5b50 +const uint16 dsAddr_cantOpenItMsg = 0x5b3f; // "I can't open it" +// Don't Need Them Message : 0x5b51 to 0x5b64 +const uint16 dsAddr_dontNeedThemMsg = 0x5b51; // "I don't need them" +// Peeping Tom Message : 0x5b65 to 0x5b7f +const uint16 dsAddr_peepingTomMsg = 0x5b65; // "What am I? A Peeping Tom?" +// Big Pockets Message : 0x5b80 to 0x5baa +const uint16 dsAddr_bigPocketsMsg = 0x5b80; // "I have big pockets, but there are limits" +// Trouble With Stairs Message : 0x5bab to 0x5be6 +const uint16 dsAddr_troubleWithStairsMsg = 0x5bab; // "If I put it on I might have trouble walking up the stairs" +// 9 Lives To Read Message : 0x5be7 to 0x5c0a +const uint16 dsAddr_9LivesToReadMsg = 0x5be7; // "I'd need 9 lives to read them all" +// Thanks Not Tired Message : 0x5c0b to 0x5c25 +const uint16 dsAddr_thanksNotTiredMsg = 0x5c0b; // "Thanks, I'm not so tired" +// No Need To Turn On Message : 0x5c26 to 0x5c45 +const uint16 dsAddr_noNeedToTurnOnMsg = 0x5c26; // "There's no need to turn it on" +// Won't Bear Weight Message : 0x5c46 to 0x5c5f +const uint16 dsAddr_wontBearWeightMsg = 0x5c46; // "It won't bear my weight" +// Never Learnt Message : 0x5c60 to 0x5c81 +const uint16 dsAddr_neverLearntMsg = 0x5c60; // "I never learnt to how use one" +// So Sharp Message : 0x5c82 to 0x5cab +const uint16 dsAddr_soSharpMsg = 0x5c82; // "They're so sharp they'd rip my trousers!" +// Cognac Message : 0x5cac to 0x5cda +const uint16 dsAddr_cognacMsg = 0x5cac; // "Pfui! The cognac really didn't do any good" +// No Time For Pleasures Message : 0x5cdb to 0x5cfc +const uint16 dsAddr_noTimeForPleasuresMsg = 0x5cdb; // "I don't have time for pleasures" +// Not Socks With Bare Hands Message : 0x5cfd to 0x5d2b +const uint16 dsAddr_notSocksWithBareHandsMsg = 0x5cfd; // "I won't touch these socks with my bare hands!" +// Not Halloween Message : 0x5d2c to 0x5d40 +const uint16 dsAddr_notHalloweenMsg = 0x5d2c; // "It's not Halloween" +// Not Manual Message : 0x5d41 to 0x5d6d +const uint16 dsAddr_NotManualMsg = 0x5d41; // "It can't be controlled manually! I hate it!" +// Nothing To Play Message : 0x5d6e to 0x5d86 +const uint16 dsAddr_nothingToPlayMsg = 0x5d6e; // "I have nothing to play" +// Not Mine Message : 0x5d87 to 0x5da7 +const uint16 dsAddr_notMineMsg = 0x5d87; // "I can't take it. It's not mine." +// Hey What's The Matter Message : 0x5da8 to 0x5dc1 +const uint16 dsAddr_HeyWtmQMsg = 0x5da8; // "Hey! What's the matter?!" +// Its Open Message : 0x5dc2 to 0x5dcd +const uint16 dsAddr_ItsOpenMsg = 0x5dc2; // "It's Open!" +// Out Of Order Message : 0x5dce to 0x5de1 +const uint16 dsAddr_outOfOrderMsg = 0x5dce; // "It's out of order" +// Captain Watching Message : 0x5de2 to 0x5e0a +const uint16 dsAddr_captainWatchingMsg = 0x5de2; // "with captain watching? Better not" +// Blunt Sickle Message : 0x5e0b to 0x5e24 +const uint16 dsAddr_bluntSickleMsg = 0x5e0b; // "The sickle is too blunt" +// First Business Message : 0x5e25 to 0x5e53 +const uint16 dsAddr_firstBusinessMsg = 0x5e25; // "First I've got some business to take care of" +// No Digging Knife Message : 0x5e54 to 0x5e8e +const uint16 dsAddr_noDiggingKnifeMsg = 0x5e54; // "Digging it out with the knife could take a hundred years" + +// FIXME - Where is this message used?! Unused? +// No Mess On Table Message : 0x5e8f to 0x5ebd +const uint16 dsAddr_noMessOnTableMsg = 0x5e8f; // "I don't want to make more mess on this table" + +// Throw Crumbs To Bird Question Message : 0x5ebe to 0x5ee5 +const uint16 dsAddr_throwCrumbsToBirdQMsg = 0x5ebe; // "Should I throw the crumbs to the bird?" +// Don't Waste Crumbs Message : 0x5ee6 to 0x5f10 +const uint16 dsAddr_dontWasteCrumbs = 0x5ee6; // "I don't want to waste these tasty crumbs" +// Might Slip Fall In Message : 0x5f11 to 0x5f3b +const uint16 dsAddr_mightSlipFallInMsg = 0x5f11; // "Better not... I might slip and fall in..." +// Book Color Message Address Pointers : (6 * 2-byte) = 0x5f3c to 0x5f47 +const uint16 dsAddr_bookColorMsgPtr = 0x5f3c; +// Book Color Message #0 : 0x5f48 to 0x5f60 +const uint16 dsAddr_bookColorMsg0 = 0x5f48; // ""The history of blues"" +// Book Color Message #1 : 0x5f61 to 0x5f8f +const uint16 dsAddr_bookColorMsg1 = 0x5f61; // ""Manchester United, or the Red Devils story"" +// Book Color Message #2 : 0x5f90 to 0x5fb5 +const uint16 dsAddr_bookColorMsg2 = 0x5f90; // ""Greyhounds and other hunting dogs"" +// Book Color Message #3 : 0x5fb6 to 0x5fe6 +const uint16 dsAddr_bookColorMsg3 = 0x5fb6; // ""Greenhorn, or my adventures in the Wild West"" +// Book Color Message #4 : 0x5fe7 to 0x6008 +const uint16 dsAddr_bookColorMsg4 = 0x5fe7; // ""Charlie Brown and his company"" +// Book Color Message #5 : 0x6009 to 0x6034 +const uint16 dsAddr_bookColorMsg5 = 0x6009; // ""Pink Panther: an unauthorised biography"" + +// Mansion Intrusion Function Pointers : (5 * 2-byte) = 0x6035 to 0x603e +const uint16 dsAddr_MansionIntrusionFnPtr = 0x6035; + +// Save State Region : 0x6478 to 0xdbf1 +const uint16 dsAddr_saveState = 0x6478; +const uint16 saveStateSize = 0x777a; + +// Save Description String (24 bytes) : 0x6478 to 0x648f + +// Ego (Mark) position in scene : 0x64af to 0x64b2 +const uint16 dsAddr_egoX = 0x64af; // 2 bytes +const uint16 dsAddr_egoY = 0x64b1; // 2 bytes + +// Idle Animation List Table : 0x6540 to 0x???? +const uint16 dsAddr_idleAnimationListPtr = 0x6540; + +// Palette Effect Data : 0x6609 to 0x???? +const uint16 dsAddr_paletteEffectData = 0x6609; + +// Scene Fade Table (2 byte address * 42): 0x663e to 0x6691 +const uint16 dsAddr_sceneFadeTablePtr = 0x663e; + +// Scene Walkbox Table (2 byte LE address * 42) : 0x6746 to 0x6799 +const uint16 dsAddr_sceneWalkboxTablePtr = 0x6746; + +// Scene Zoom Table (2 byte address * 42) : 0x70f4 to 0x7147 +const uint16 dsAddr_sceneZoomTablePtr = 0x70f4; + +// Scene Object Table (2 byte address * 42) : 0x7254 to 0x72a7 +const uint16 dsAddr_sceneObjectTablePtr = 0x7254; + +// Scene Object Name : Sonny or whatever : 0x92e5 to 0x92f6 +const uint16 dsAddr_scnObjNameSonny = 0x92e5; // "Sonny or whatever" + +// Scene Object Name - Anne : 0x9820 to 0x9824 +const uint16 dsAddr_scnObjNameAnne = 0x9820; // "Anne" + +// Scene Object Name - Mike : 0xaa94 to 0xaa98 +const uint16 dsAddr_scnObjNameMike = 0xaa94; // "Mike" + +// Current Scene Id : 0xb4f3 +const uint16 dsAddr_currentScene = 0xb4f3; // 1 byte + +// Ons Animation Table (2 byte address * ??) : 0xb4f5 to 0x???? +const uint16 dsAddr_onsAnimationTablePtr = 0xb4f5; + +// Examine Object Callback Table (2 byte LE address * ??) : 0xb5ce to 0x???? +const uint16 dsAddr_objExamineCallbackTablePtr = 0xb5ce; + +// Use Object Callback Table (2 byte LE address * ??) : 0xb89c to 0x???? +const uint16 dsAddr_objUseCallbackTablePtr = 0xb89c; + +// Inventory Object Callback Table (3 byte (id, callbackAddr) * 7) : 0xbb72 to 0xbb86 +const uint16 dsAddr_objCallbackTablePtr = 0xbb72; +// invItemToolboxFull = csAddr_openFullToolbox +// invItemToolboxHalfEmpty = csAddr_openHalfEmptyToolbox +// invItemDiveEquipment = csAddr_useDivingEquipment +// invItemShovelAct2 = csAddr_digMansionWall +// 0xff = csAddr_tooDarkHere // TODO: No object has id 0xff - Callback Disabled? +// invItemBanknote = csAddr_examineBanknote +// invItemTimePills = csAddr_useTimePills + +// Scene Hotspots Table (2 byte LE address * ??) : 0xbb87 to 0x???? +const uint16 dsAddr_sceneHotspotsPtr = 0xbb87; + +// Inventory Object Combining Table (5 byte (id, id, new object id, msgAddr) * 34) : 0xc335 to 0xc3de +const uint16 dsAddr_objCombiningTablePtr = 0xc335; +// 3 byte null terminator for Combining table 0xc3df to 0xc3e1 + +// Object Combine Error Message : 0xc3e2 to 0xc41e +const uint16 dsAddr_objCombineErrorMsg = 0xc3e2; // "Using these two objects ..." + +// Inventory (item ids held by Ego) (1 byte * 24) : 0xc48d to 0xc4a4 +const uint16 dsAddr_inventory = 0xc48d; +// 0xc4a5 is null word alignment byte +// Inventory item data address table (2 bytes * 92) : 0xc4a6 to 0xc55d +const uint16 dsAddr_inventoryItemDataPtrTable = 0xc4a6; + +// Lans Animation Table (4 byte * ??) : 0xd89e to 0x???? +const uint16 dsAddr_lansAnimationTablePtr = 0xd89e; + +// Spoken With Mansion Guard Flag : 0xda96 +// FIXME - This is probably unecessary as although this location is set, it +// doesn't now appear to be read. +const uint16 dsAddr_spokenWithMansionGuardFlag = 0xda96; // 1 byte +// Have Not Spoken With Mansion Guard Flag : 0xda97 +// FIXME - This is probably unecessary as although this location is set, it +// doesn't now appear to be read. +const uint16 dsAddr_haveNotSpokenWithMansionGuardFlag = 0xda97; // 1 byte + +// Dialog Stack - Pleading with Mansion Guard : 0xdaa6 to 0xdab1 +const uint16 dsAddr_dialogStackPleadingToMansionGuard = 0xdaa6; +// Dialog Stack - Mansion Guard Drinking : 0xdab2 to 0xdab9 +// FIXME - Can't find where this is used... +const uint16 dsAddr_dialogStackMansionGuardDrinking = 0xdab2; +// Dialog Stack - Talking To Sonny : 0xdaba to 0xdac3 +const uint16 dsAddr_dialogStackSonny = 0xdaba; +// Dialog Stack - Talking To Grandpa : 0xdac4 to 0xdacd +const uint16 dsAddr_dialogStackGrandpa = 0xdac4; +// Cave Thorns Cut Down Flag : 0xdaca +// FIXME - Cave Thorns Flag overlap with dsAddr_dialogStackGrandpa. Bug or typo? +const uint16 dsAddr_caveThornsCutDownFlag = 0xdaca; // 1 byte +// Dialog Stack - Trying To Borrow Shotgun From Grandpa : 0xdace to 0xdad3 +const uint16 dsAddr_dialogStackGrandpaShotgun = 0xdace; +// Dialog Stack - Trying To Borrow Fan From Grandpa : 0xdad4 to 0xdad9 +const uint16 dsAddr_dialogStackGrandpaFan = 0xdad4; +// Dialog Stack - Ask Old Lady if OK : 0xdada to 0xdaef +const uint16 dsAddr_dialogStackAskOldLadyOK = 0xdada; +// Dialog Stack - Talking To Old Lady : 0xdaf0 to 0xdaf5 +const uint16 dsAddr_dialogStackOldLady = 0xdaf0; +// Dialog Stack - Borrow Duster From Old Lady : 0xdaf6 to 0xdafb +const uint16 dsAddr_dialogStackBorrowDusterFromOldLady = 0xdaf6; +// Dialog Stack - Get Old Lady's Apple : 0xdafc to 0xdb01 +const uint16 dsAddr_dialogStackGetAppleOldLady = 0xdafc; +// Dialog Stack - Giving Another Flower To Anne : 0xdb02 to 0xdb07 +const uint16 dsAddr_dialogStackAnotherFlowerToAnne = 0xdb02; +// Dialog Stack - Talking To Squirrel : 0xdb08 to 0xdb13 +const uint16 dsAddr_dialogStackSquirrel = 0xdb08; +// Dialog Stack - Talking To Dog : 0xdb14 to 0xdb1d +const uint16 dsAddr_dialogStackDog = 0xdb14; +// Dialog Stack - Take Axe : 0xdb1e to 0xdb23 +const uint16 dsAddr_dialogStackTakeAxe = 0xdb1e; +// Dialog Stack - Talking To Busy Cook : 0xdb24 to 0xdb2d +const uint16 dsAddr_dialogStackBusyCook = 0xdb24; +// Dialog Stack - Talking To Mike the Robot Safe : 0xdb2e to 0xdb35 +const uint16 dsAddr_dialogStackRobotSafe = 0xdb2e; +// Dialog Stack - Talking To John Noty At Endgame : 0xdb36 to 0xdb3f +const uint16 dsAddr_dialogStackJohnNotyEndgame = 0xdb36; +// Dialog Stack - Camp Guard Waiting For Documents : 0xdb40 to 0xdb4b +const uint16 dsAddr_dialogStackCampGuardWantsDocuments = 0xdb40; +// Dialog Stack - Camp Guard Reading Soldier News : 0xdb4c to 0xdb55 +const uint16 dsAddr_dialogStackCampGuardReadingNews = 0xdb4c; +// Dialog Stack - Camp Guard Show Pass : 0xdb56 to 0xdb5b +const uint16 dsAddr_dialogStackCampGuardShowPass = 0xdb56; +// Dialog Stack - Jail Door Grates : 0xdb5c to 0xdb67 +const uint16 dsAddr_dialogStackJailDoorGrates = 0xdb5c; +// Dialog Stack - Talking to Barman : 0xdb68 to 0xdb71 +const uint16 dsAddr_dialogStackBarman = 0xdb68; +// Dialog Stack - Fall Into Mudpool : 0xdb72 to 0xdb79 +const uint16 dsAddr_dialogStackFallIntoMudpool = 0xdb72; +// Dialog Stack - Talking To Mudpool Bird : 0xdb7a to 0xdb81 +const uint16 dsAddr_dialogStackMudpoolBird = 0xdb7a; +// Dialog Stack - Interrogate Captain : 0xdb82 to 0xdb89 +const uint16 dsAddr_dialogStackInterrogateCaptain = 0xdb82; +// Dialog Stack - Bar Cellar Door : 0xdb8a to 0xdb8f +const uint16 dsAddr_dialogStackBarCellarDoor = 0xdb8a; +// Current Music Id Playing : 0xdb90 +const uint16 dsAddr_currentMusic = 0xdb90; // 1 byte +// Unused Byte : 0xdb91 +// Already Adjusted Hoop Pole Flag : 0xdb92 +const uint16 dsAddr_alreadyAdjustedHoopPoleFlag = 0xdb92; // 1 byte +// Already Kicked Hen Flag : 0xdb93 +const uint16 dsAddr_alreadyKickedHenFlag = 0xdb93; // 1 byte +// Already Pulled Trunk Release Lever Flag : 0xdb94 +const uint16 dsAddr_alreadyPulledTrunkReleaseLeverFlag = 0xdb94; // 1 byte +// Car Trunk Empty Flag : 0xdb95 +const uint16 dsAddr_carTrunkEmptyFlag = 0xdb95; // 1 byte +// Birds Gone From Scarecrow Flag : 0xdb96 +const uint16 dsAddr_birdsGoneFromScarecrowFlag = 0xdb96; // 1 byte +// Already Spoken To Anne Flag : 0xdb97 +const uint16 dsAddr_alreadySpokenToAnneFlag = 0xdb97; // 1 byte +// Flower Isle in Lake State (0 = Both Flowers Present, 1 = One Flower Taken, 2+ = Both Flowers Taken): 0xdb98 +const uint16 dsAddr_flowerIsleState = 0xdb98; // 1 byte +// Already Got Broken Paddle Flag : 0xdb99 +const uint16 dsAddr_alreadyGotBrokenPaddleFlag = 0xdb99; // 1 byte +// Given Flower To OldLady Already Flag : 0xdb9a +const uint16 dsAddr_givenFlowerToOldLadyAlreadyFlag = 0xdb9a; // 1 byte +// Given Flower To Anne Already Flag : 0xdb9b +const uint16 dsAddr_givenFlowerToAnneAlreadyFlag = 0xdb9b; // 1 byte +// Scared Guard Already Flag : 0xdb9c +const uint16 dsAddr_scaredGuardAlreadyFlag = 0xdb9c; // 1 byte +// Got Needle Already Flag : 0xdb9d +const uint16 dsAddr_gotNeedleAlreadyFlag = 0xdb9d; // 1 byte +// Got Potato Already Flag : 0xdb9e +const uint16 dsAddr_gotPotatoAlreadyFlag = 0xdb9e; // 1 byte +// Bees Gone Flag : 0xdb9f +const uint16 dsAddr_beesGoneFlag = 0xdb9f; // 1 byte +// Mansion Already Been Through Tunnel Flag : 0xdba0 +const uint16 dsAddr_mansionTunnelDoneFlag = 0xdba0; // 1 byte +// Mansion Tree Hollow Empty Flag : 0xdba1 +const uint16 dsAddr_mansionTreeHollowEmptyFlag = 0xdba1; // 1 byte +// Climbed Mansion Tree Already Flag : 0xdba2 +const uint16 dsAddr_climbedMansionTreeAlreadyFlag = 0xdba2; // 1 byte +// Cellar Door Open Flag : 0xdba3 +const uint16 dsAddr_cellarDoorOpenFlag = 0xdba3; // 1 byte +// Cellar Light On Flag : 0xdba4 +const uint16 dsAddr_lightOnFlag = 0xdba4; // 1 byte +// Laundry State (0 = Wet on Line, 1 = Dry on Line, 2 = Not Present): 0xdba5 +const uint16 dsAddr_laundryState = 0xdba5; // 1 byte +// Lake Diving Exit Message (0 to 5+) : 0xdba6 +const uint16 dsAddr_lakeDivingExitMessage = 0xdba6; // 1 byte +// Searched Grandpa Drawers Flag : 0xdba7 +const uint16 dsAddr_SearchedGrandpaDrawersFlag = 0xdba7; // 1 byte +// Hankerchief in Mousehole Flag : 0xdba8 +const uint16 dsAddr_HankerchiefInMouseholeFlag = 0xdba8; // 1 byte +// Mouse Hole State : 0xdba9, 0 = Mouse Gone, 1 = Mouse Trapped, 2 = Mouse Success(?) +const uint16 dsAddr_mouseHoleState = 0xdba9; // 1 byte +// Mouse Nerve Message Said Flag : 0xdbaa +const uint16 dsAddr_mouseNerveMsgSaidFlag = 0xdbaa; // 1 byte +// Mouse Already Got Gold Nugget Flag : 0xdbab +const uint16 dsAddr_mouseGotGoldNuggetFlag = 0xdbab; // 1 byte +// Unused Byte : 0xdbac +// Dog Has Bone Flag : 0xdbad +const uint16 dsAddr_dogHasBoneFlag = 0xdbad; // 1 byte +// Ego Already Scared By Spider Flag : 0xdbae +const uint16 dsAddr_egoAlreadyScaredBySpiderFlag = 0xdbae; // 1 byte +// Already Said That Anne is Beautiful Flag : 0xdbaf +const uint16 dsAddr_alreadySaidAnneBeautifulFlag = 0xdbaf; // 1 byte +// Squirrel's Nut State (0 = Nut in Tree, 1 = Nut in Grass, 2 = Nut Found with Rake) : 0xdbb0 +const uint16 dsAddr_squirrelNutState = 0xdbb0; // 1 byte +// Nut Swapped For Apple in Fruit Bowl Flag : 0xdbb1 +const uint16 dsAddr_nutSwappedForAppleFlag = 0xdbb1; // 1 byte +// Spoken To Man In Well Flag : 0xdbb2 +const uint16 dsAddr_spokenToManInWellFlag = 0xdbb2; // 1 byte +// Spoken To Mirror Flag : 0xdbb3 +const uint16 dsAddr_spokenToMirrorFlag = 0xdbb3; // 1 byte +// Cellar Shelves Examine Count (0 to 2(clamped))) : 0xdbb4 +const uint16 dsAddr_cellarShelfExamineCount = 0xdbb4; // 1 byte +// Examined Bank Note Flag : 0xdbb5 +const uint16 dsAddr_examinedBanknoteFlag = 0xdbb5; // 1 byte +// VGA Artist Quip Already Said Flag : 0xdbb6 +const uint16 dsAddr_vgaArtistQuipAlreadySaidFlag = 0xdbb6; // 1 byte +// Mansion Desk Blue Drawer Open Flag : 0xdbb7 +const uint16 dsAddr_blueDrawerOpenFlag = 0xdbb7; // 1 byte +// Mansion Desk Red Drawer Open Flag : 0xdbb8 +const uint16 dsAddr_redDrawerOpenFlag = 0xdbb8; // 1 byte +// Mansion Desk Grey Drawer Open Flag : 0xdbb9 +const uint16 dsAddr_greyDrawerOpenFlag = 0xdbb9; // 1 byte +// Mansion Desk Green Drawer Open Flag : 0xdbba +const uint16 dsAddr_greenDrawerOpenFlag = 0xdbba; // 1 byte +// Mansion Desk Brown Drawer Open Flag : 0xdbbb +const uint16 dsAddr_brownDrawerOpenFlag = 0xdbbb; // 1 byte +// Mansion Desk Pink Drawer Open Flag : 0xdbbc +const uint16 dsAddr_pinkDrawerOpenFlag = 0xdbbc; // 1 byte +// Mansion Colored Drawer Puzzle Hint Message Given Flag : 0xdbbd +const uint16 dsAddr_drawerPuzzleHintGivenFlag = 0xdbbd; // 1 byte +// Mansion Colored Drawer Got Dictaphone Flag : 0xdbbe +const uint16 dsAddr_drawerGotDictaphoneFlag = 0xdbbe; // 1 byte +// Mansion Colored Drawer Got Polaroid Flag : 0xdbbf +const uint16 dsAddr_drawerGotPolaroidFlag = 0xdbbf; // 1 byte +// Mansion Colored Drawer Puzzle Book Message Flag : 0xdbc0 +const uint16 dsAddr_drawerPuzzleBookMessageFlag = 0xdbc0; // 1 byte +// Mansion Colored Drawer Puzzle - Random Book Color Value (0 = No Book, 1 to 6 = Books) : 0xdbc1 +const uint16 dsAddr_drawerPuzzleBookValue = 0xdbc1; // 1 byte +// Mansion Colored Drawer Puzzle Solved Flag : 0xdbc2 +const uint16 dsAddr_drawerPuzzleSolvedFlag = 0xdbc2; // 1 byte +// Mansion Trashcan Searched Flag : 0xdbc3 +const uint16 dsAddr_mansionTrashcanSearchedFlag = 0xdbc3; // 1 byte +// Mansion Read Newspaper Flag : 0xdbc4 +const uint16 dsAddr_mansionReadNewspaperFlag = 0xdbc4; // 1 byte +// Mansion TV On Flag : 0xdbc5 +const uint16 dsAddr_mansionTVOnFlag = 0xdbc5; // 1 byte +// Mansion VCR Playing Tape Flag : 0xdbc6 +const uint16 dsAddr_mansionVCRPlayingTapeFlag = 0xdbc6; // 1 byte +// Mansion VCR Played Tape Before Flag : 0xdbc7 +const uint16 dsAddr_mansionVCRPlayedTapeBeforeFlag = 0xdbc7; // 1 byte +// Mansion VCR Tape Loaded Flag : 0xdbc8 +const uint16 dsAddr_mansionVCRTapeLoadedFlag = 0xdbc8; // 1 byte +// Mansion Examined Couch Before Flag : 0xdbc9 +const uint16 dsAddr_mansionExaminedCouchBeforeFlag = 0xdbc9; // 1 byte +// Mansion Used Polaroid on TV Flag : 0xdbca +const uint16 dsAddr_usedPolaroidOnTVFlag = 0xdbca; // 1 byte +// Mansion Used Dictaphone on TV Flag : 0xdbcb +const uint16 dsAddr_usedDictaphoneOnTVFlag = 0xdbcb; // 1 byte +// Mansion Cook Gone Flag : 0xdbcc +const uint16 dsAddr_MansionCookGoneFlag = 0xdbcc; // 1 byte +// Mansion Radio Broken Flag : 0xdbcd +const uint16 dsAddr_MansionRadioBrokenFlag = 0xdbcd; // 1 byte +// Mansion Got Radio Batteries Flag : 0xdbce +const uint16 dsAddr_MansionGotRadioBatteriesFlag = 0xdbce; // 1 byte +// Mansion Have Opened Fridge Before Flag : 0xdbcf +const uint16 dsAddr_MansionHaveOpenedFridgeBeforeFlag = 0xdbcf; // 1 byte +// Mansion Put Burning Paper In Fridge Flag : 0xdbd0 +const uint16 dsAddr_MansionPutBurningPaperInFridgeFlag = 0xdbd0; // 1 byte +// Mansion Robot Safe Unlocked Flag : 0xdbd1 +const uint16 dsAddr_MansionRobotSafeUnlockedFlag = 0xdbd1; // 1 byte +// Mansion Robot Safe Voice Test Passed Flag : 0xdbd2 +const uint16 dsAddr_MansionRobotSafeVoiceTestPassedFlag = 0xdbd2; // 1 byte +// Mansion Robot Safe Scent Test Passed Flag : 0xdbd3 +const uint16 dsAddr_MansionRobotSafeScentTestPassedFlag = 0xdbd3; // 1 byte +// Mansion Robot Safe View Test Passed Flag : 0xdbd4 +const uint16 dsAddr_MansionRobotSafeViewTestPassedFlag = 0xdbd4; // 1 byte +// Mansion John Noty Outside Bathroom Flag : 0xdbd5 +const uint16 dsAddr_MansionJohnNotyOutsideBathroomFlag = 0xdbd5; // 1 byte +// Mansion Sink State (0 - No Plug, Sink Empty, 1 - Plug, Sink Empty, 2 - Plug, Sink Full) : 0xdbd6 +const uint16 dsAddr_MansionSinkState = 0xdbd6; // 1 byte +// Mansion Through Fan By Time Pill Flag : 0xdbd7 +const uint16 dsAddr_MansionThruFanByTimePillFlag = 0xdbd7; // 1 byte +// Mansion Ventilator Fan Stopped Flag : 0xdbd8 +const uint16 dsAddr_MansionVentFanStoppedFlag = 0xdbd8; // 1 byte +// Mansion John Noty Escaping Flag : 0xdbd9 +const uint16 dsAddr_MansionJohnNotyEscapingFlag = 0xdbd9; // 1 byte +// Shown Pass To Guard Flag : 0xdbda +const uint16 dsAddr_ShownPassToGuardFlag = 0xdbda; // 1 byte +// Graffiti Message Id (0 to 6) : 0xdbdb +const uint16 dsAddr_graffitiMsgId = 0xdbdb; // 1 byte +// Got Food Bowl in Jail Flag : 0xdbdc +const uint16 dsAddr_GotFoodBowlInJailFlag = 0xdbdc; // 1 byte +// Jail Cable and Bowl State (0 = Cable not in Bowl, 1 = Cable in Bowl, 2 = Bowl Electrified 3 = Captain Shocked) : 0xdbdd +const uint16 dsAddr_JailCableAndBowlState = 0xdbdd; // 1 byte +// Got Jail Key Flag : 0xdbde +const uint16 dsAddr_GotJailKeyFlag = 0xdbde; // 1 byte +// First Act Trial State (0 = Before First Trial, 1 to 3 = Trial 1st to 3rd) : 0xdbdf +const uint16 dsAddr_FirstActTrialState = 0xdbdf; // 1 byte +// Already Tickled Captain Flag : 0xdbe0 +const uint16 dsAddr_AlreadyTickledCaptainFlag = 0xdbe0; // 1 byte +// Cut Fence Flag : 0xdbe1 +const uint16 dsAddr_cutFenceFlag = 0xdbe1; // 1 byte +// Act 1 Guard State (0 = Normal, 1 = With Kaleidoscope & Grenade, 2 = Kaleidoscope & No Grenade) : 0xdbe2 +const uint16 dsAddr_act1GuardState = 0xdbe2; // 1 byte +// Spoken to Barman About Third Trial Flag : 0xdbe3 +const uint16 dsAddr_spokeToBarmanAboutThirdTrialFlag = 0xdbe3; // 1 byte +// Got Mug Of Mud Flag : 0xdbe4 +const uint16 dsAddr_gotMugOfMudFlag = 0xdbe4; // 1 byte +// Got Rope In Act 1 Flag : 0xdbe5 +const uint16 dsAddr_gotRopeAct1Flag = 0xdbe5; // 1 byte +// Captain Drawer State : 0xdbe6 +const uint16 dsAddr_captainDrawerState = 0xdbe6; // 1 byte +// Bird on Bar Radio Antenna Flag : 0xdbe7 +const uint16 dsAddr_birdOnBarRadioAntennaFlag = 0xdbe7; // 1 byte +// Swapped Barman Mug Flag : 0xdbe8 +const uint16 dsAddr_swappedBarmanMugFlag = 0xdbe8; // 1 byte +// Barman Passed Out Flag : 0xdbe9 +const uint16 dsAddr_barmanPassedOutFlag = 0xdbe9; // 1 byte +// Counter for Mansion Intrusion Attempts : 0xdbea +const uint16 dsAddr_mansionEntryCount = 0xdbea;// 1 byte +// Unused Byte : 0xdbeb +// John Noty Outside Mansion Door Flag : 0xdbec +const uint16 dsAddr_johnNotyOutsideMansionDoorFlag = 0xdbec; // 1 byte +// Unused Byte : 0xdbed +// Lovestruck By Anne Flag : 0xdbee +const uint16 dsAddr_lovestruckByAnneFlag = 0xdbee;// 1 byte +// Mansion Handle in Door Hole Flag : 0xdbef +const uint16 dsAddr_mansionHandleInDoorHoleFlag = 0xdbef;// 1 byte +// Got Password Need to Speak To Barman Flag : 0xdbf0 +const uint16 dsAddr_gotPasswordNeedSpeakBarmanFlag = 0xdbf0; // 1 byte +// Mansion Already Used Time Pills Flag : 0xdbf1 +const uint16 dsAddr_mansionAlreadyUsedTimePillsFlag = 0xdbf1; // 1 byte + +// Intro Credits #1 : 0xe3c2 to 0xe3e5 (Read Only) +const uint16 dsAddr_introCredits1 = 0xe3c2; // "backgrounds ..." +// Intro Credits #2 : 0xe3e6 to 0xe3fe (Read Only) +const uint16 dsAddr_introCredits2 = 0xe3e6; // "music ..." +// Intro Credits #3 : 0xe3ff to 0xe42e (Read Only) +const uint16 dsAddr_introCredits3 = 0xe3ff; // "animation..." +// Intro Credits #4 : 0xe42f to 0xe45b (Read Only) +const uint16 dsAddr_introCredits4 = 0xe42f; // "programming..." +// Credits #5 : 0xe45c to 0xe47b (Read Only) +const uint16 dsAddr_credits5 = 0xe45c; // "after the tiring journey..." +// Final Credits #6 : 0xe47c to 0xe487 (Read Only) +const uint16 dsAddr_finalCredits6 = 0xe47c; // "THE END..." +// Final Credits #7 : 0xe488 to 0xe782 (Read Only) +const uint16 dsAddr_finalCredits7 = 0xe488; // "programming..." +// 0xe783 to 0xe78f: 13 null bytes at end of dseg data - segment alignment padding? + class Resources { -protected: - Resources(); public: - static Resources *instance(); + Resources(); + ~Resources(); bool loadArchives(const ADGameDescription *gd); - void deinit(); + void loadOff(Graphics::Surface &surface, byte *palette, int id); Common::SeekableReadStream *loadLan(uint32 id) const; Common::SeekableReadStream *loadLan000(uint32 id) const; - //void loadOn(Graphics::Surface &surface, int id, uint16 &dst, uint16 *flags); - //void loadOns(Graphics::Surface &surface, int id, uint16 &dst); /* * PSP (as the other sony playstation consoles - to be confirmed and 'ifdef'ed here too) @@ -56,8 +1182,17 @@ public: FilePack off, on, ons, lan000, lan500, sam_mmm, sam_sam, mmm, voices; #endif - Segment cseg, dseg, eseg; + Segment dseg; Font font7, font8; + + //const byte *getDialog(uint16 dialogNum) { return eseg.ptr(dialogOffsets[dialogNum]); } + uint16 getDialogAddr(uint16 dialogNum) { return dialogOffsets[dialogNum]; } + + Segment eseg; +private: + void precomputeDialogOffsets(); + + Common::Array<uint16> dialogOffsets; }; } // End of namespace TeenAgent diff --git a/engines/teenagent/scene.cpp b/engines/teenagent/scene.cpp index 038c8ea05e..bdeb11a841 100644 --- a/engines/teenagent/scene.cpp +++ b/engines/teenagent/scene.cpp @@ -21,6 +21,7 @@ #include "common/config-manager.h" #include "common/debug.h" +#include "common/events.h" #include "common/algorithm.h" #include "common/ptr.h" #include "common/textconsole.h" @@ -28,24 +29,23 @@ #include "graphics/palette.h" #include "teenagent/scene.h" +#include "teenagent/inventory.h" #include "teenagent/resources.h" #include "teenagent/surface.h" #include "teenagent/objects.h" #include "teenagent/teenagent.h" -#include "teenagent/dialog.h" #include "teenagent/music.h" namespace TeenAgent { -Scene::Scene(TeenAgentEngine *engine, OSystem *system) : intro(false), _id(0), ons(0), - orientation(kActorRight), actor_talking(false), - message_timer(0), message_first_frame(0), message_last_frame(0), message_animation(NULL), - current_event(SceneEvent::kNone), hide_actor(false), callback(0), callback_timer(0), _idle_timer(0) { - _engine = engine; - _system = system; +Scene::Scene(TeenAgentEngine *vm) : _vm(vm), intro(false), _id(0), ons(0), + orientation(kActorRight), actorTalking(false), teenagent(vm), teenagentIdle(vm), + messageTimer(0), messageFirstFrame(0), messageLastFrame(0), messageAnimation(NULL), + currentEvent(SceneEvent::kNone), hideActor(false), callback(0), callbackTimer(0), _idleTimer(0) { - _fade_timer = 0; - on_enabled = true; + _fadeTimer = 0; + _fadeOld = 0; + onEnabled = true; memset(palette, 0, sizeof(palette)); background.pixels = 0; @@ -65,8 +65,8 @@ Scene::Scene(TeenAgentEngine *engine, OSystem *system) : intro(false), _id(0), o if (!s) error("invalid resource data"); - teenagent_idle.load(*s, Animation::kTypeVaria); - if (teenagent_idle.empty()) + teenagentIdle.load(*s, Animation::kTypeVaria); + if (teenagentIdle.empty()) error("invalid mark animation"); varia.close(); @@ -91,10 +91,10 @@ void Scene::warp(const Common::Point &_point, byte o) { bool Scene::findPath(Scene::Path &p, const Common::Point &src, const Common::Point &dst) const { const Common::Array<Walkbox> &scene_walkboxes = walkboxes[_id - 1]; - if (dst.x < 0 || dst.x > 319 || dst.y < 0 || dst.y > 199) + if (dst.x < 0 || dst.x >= kScreenWidth || dst.y < 0 || dst.y >= kScreenHeight) return false; - debug(1, "findPath %d,%d -> %d,%d", src.x, src.y, dst.x, dst.y); + debugC(1, kDebugScene, "findPath %d,%d -> %d,%d", src.x, src.y, dst.x, dst.y); p.clear(); p.push_back(src); p.push_back(dst); @@ -113,7 +113,7 @@ bool Scene::findPath(Scene::Path &p, const Common::Point &src, const Common::Poi break; const Common::Point &p1 = *i, &p2 = *next; - debug(1, "%d,%d -> %d,%d", p1.x, p1.y, p2.x, p2.y); + debugC(1, kDebugScene, "%d,%d -> %d,%d", p1.x, p1.y, p2.x, p2.y); Common::List<uint>::iterator wi; for (wi = boxes.begin(); wi != boxes.end(); ++wi) { @@ -124,14 +124,14 @@ bool Scene::findPath(Scene::Path &p, const Common::Point &src, const Common::Poi } w.dump(1); - debug(1, "%u: intersection mask 0x%04x, searching hints", *wi, mask); + debugC(1, kDebugScene, "%u: intersection mask 0x%04x, searching hints", *wi, mask); int dx = p2.x - p1.x, dy = p2.y - p1.y; if (dx >= 0) { - if ((mask & 8) != 0 && w.side_hint[3] != 0) { - debug(1, "hint left: %u", w.side_hint[3]); + if ((mask & 8) != 0 && w.sideHint[3] != 0) { + debugC(1, kDebugScene, "hint left: %u", w.sideHint[3]); Common::Point w1, w2; - w.rect.side(w1, w2, w.side_hint[3], p1); - debug(1, "hint: %d,%d-%d,%d", w1.x, w1.y, w2.x, w2.y); + w.rect.side(w1, w2, w.sideHint[3], p1); + debugC(1, kDebugScene, "hint: %d,%d-%d,%d", w1.x, w1.y, w2.x, w2.y); p.insert(next, w1); if (mask & 2) p.insert(next, w2); @@ -139,11 +139,11 @@ bool Scene::findPath(Scene::Path &p, const Common::Point &src, const Common::Poi break; } } else { - if ((mask & 2) != 0 && w.side_hint[1] != 0) { - debug(1, "hint right: %u", w.side_hint[1]); + if ((mask & 2) != 0 && w.sideHint[1] != 0) { + debugC(1, kDebugScene, "hint right: %u", w.sideHint[1]); Common::Point w1, w2; - w.rect.side(w1, w2, w.side_hint[1], p1); - debug(1, "hint: %d,%d-%d,%d", w1.x, w1.y, w2.x, w2.y); + w.rect.side(w1, w2, w.sideHint[1], p1); + debugC(1, kDebugScene, "hint: %d,%d-%d,%d", w1.x, w1.y, w2.x, w2.y); p.insert(next, w1); if (mask & 8) p.insert(next, w2); @@ -153,11 +153,11 @@ bool Scene::findPath(Scene::Path &p, const Common::Point &src, const Common::Poi } if (dy >= 0) { - if ((mask & 1) != 0 && w.side_hint[0] != 0) { - debug(1, "hint top: %u", w.side_hint[0]); + if ((mask & 1) != 0 && w.sideHint[0] != 0) { + debugC(1, kDebugScene, "hint top: %u", w.sideHint[0]); Common::Point w1, w2; - w.rect.side(w1, w2, w.side_hint[0], p1); - debug(1, "hint: %d,%d-%d,%d", w1.x, w1.y, w2.x, w2.y); + w.rect.side(w1, w2, w.sideHint[0], p1); + debugC(1, kDebugScene, "hint: %d,%d-%d,%d", w1.x, w1.y, w2.x, w2.y); p.insert(next, w1); if (mask & 4) p.insert(next, w2); @@ -165,11 +165,11 @@ bool Scene::findPath(Scene::Path &p, const Common::Point &src, const Common::Poi break; } } else { - if ((mask & 4) != 0 && w.side_hint[2] != 0) { - debug(1, "hint bottom: %u", w.side_hint[2]); + if ((mask & 4) != 0 && w.sideHint[2] != 0) { + debugC(1, kDebugScene, "hint bottom: %u", w.sideHint[2]); Common::Point w1, w2; - w.rect.side(w1, w2, w.side_hint[2], p1); - debug(1, "hint: %d,%d-%d,%d", w1.x, w1.y, w2.x, w2.y); + w.rect.side(w1, w2, w.sideHint[2], p1); + debugC(1, kDebugScene, "hint: %d,%d-%d,%d", w1.x, w1.y, w2.x, w2.y); p.insert(next, w1); if (mask & 1) p.insert(next, w2); @@ -187,13 +187,13 @@ bool Scene::findPath(Scene::Path &p, const Common::Point &src, const Common::Poi void Scene::moveTo(const Common::Point &_point, byte orient, bool validate) { Common::Point point(_point); - debug(0, "moveTo(%d, %d, %u)", point.x, point.y, orient); + debugC(0, kDebugScene, "moveTo(%d, %d, %u)", point.x, point.y, orient); const Common::Array<Walkbox> &scene_walkboxes = walkboxes[_id - 1]; for (byte i = 0; i < scene_walkboxes.size(); ++i) { const Walkbox &w = scene_walkboxes[i]; if (w.rect.in(point)) { - debug(0, "bumped into walkbox %u", i); + debugC(0, kDebugScene, "bumped into walkbox %u", i); w.dump(); byte o = w.orientation; switch (o) { @@ -229,7 +229,7 @@ void Scene::moveTo(const Common::Point &_point, byte orient, bool validate) { } if (!findPath(path, position, point)) { - _engine->cancel(); + _vm->cancel(); return; } @@ -237,55 +237,53 @@ void Scene::moveTo(const Common::Point &_point, byte orient, bool validate) { } void Scene::loadObjectData() { - Resources *res = Resources::instance(); - //loading objects & walkboxes objects.resize(42); walkboxes.resize(42); fades.resize(42); for (byte i = 0; i < 42; ++i) { - Common::Array<Object> &scene_objects = objects[i]; - scene_objects.clear(); + Common::Array<Object> &sceneObjects = objects[i]; + sceneObjects.clear(); - uint16 scene_table = res->dseg.get_word(0x7254 + i * 2); - uint16 object_addr; - while ((object_addr = res->dseg.get_word(scene_table)) != 0) { + uint16 sceneTable = _vm->res->dseg.get_word(dsAddr_sceneObjectTablePtr + (i * 2)); + uint16 objectAddr; + while ((objectAddr = _vm->res->dseg.get_word(sceneTable)) != 0) { Object obj; - obj.load(res->dseg.ptr(object_addr)); + obj.load(_vm->res->dseg.ptr(objectAddr)); //obj.dump(); - scene_objects.push_back(obj); - scene_table += 2; + sceneObjects.push_back(obj); + sceneTable += 2; } - debug(0, "scene[%u] has %u object(s)", i + 1, scene_objects.size()); + debugC(0, kDebugScene, "scene[%u] has %u object(s)", i + 1, sceneObjects.size()); - byte *walkboxes_base = res->dseg.ptr(READ_LE_UINT16(res->dseg.ptr(0x6746 + i * 2))); - byte walkboxes_n = *walkboxes_base++; - debug(0, "scene[%u] has %u walkboxes", i + 1, walkboxes_n); + byte *walkboxesBase = _vm->res->dseg.ptr(READ_LE_UINT16(_vm->res->dseg.ptr(dsAddr_sceneWalkboxTablePtr + i * 2))); + byte walkboxesCount = *walkboxesBase++; + debugC(0, kDebugScene, "scene[%u] has %u walkboxes", i + 1, walkboxesCount); - Common::Array<Walkbox> &scene_walkboxes = walkboxes[i]; - for (byte j = 0; j < walkboxes_n; ++j) { + Common::Array<Walkbox> &sceneWalkboxes = walkboxes[i]; + for (byte j = 0; j < walkboxesCount; ++j) { Walkbox w; - w.load(walkboxes_base + 14 * j); - if ((w.side_hint[0] | w.side_hint[1] | w.side_hint[2] | w.side_hint[3]) == 0) { - w.side_hint[0] = 2; - w.side_hint[1] = 3; - w.side_hint[2] = 4; - w.side_hint[3] = 1; + w.load(walkboxesBase + 14 * j); + if ((w.sideHint[0] | w.sideHint[1] | w.sideHint[2] | w.sideHint[3]) == 0) { + w.sideHint[0] = 2; + w.sideHint[1] = 3; + w.sideHint[2] = 4; + w.sideHint[3] = 1; } //walkbox[i]->dump(); - scene_walkboxes.push_back(w); + sceneWalkboxes.push_back(w); } - byte *fade_table = res->dseg.ptr(res->dseg.get_word(0x663e + i * 2)); - Common::Array<FadeType> &scene_fades = fades[i]; - while (READ_LE_UINT16(fade_table) != 0xffff) { + byte *fadeTable = _vm->res->dseg.ptr(_vm->res->dseg.get_word(dsAddr_sceneFadeTablePtr + i * 2)); + Common::Array<FadeType> &sceneFades = fades[i]; + while (READ_LE_UINT16(fadeTable) != 0xffff) { FadeType fade; - fade.load(fade_table); - fade_table += 9; - scene_fades.push_back(fade); + fade.load(fadeTable); + fadeTable += 9; + sceneFades.push_back(fade); } - debug(0, "scene[%u] has %u fadeboxes", i + 1, scene_fades.size()); + debugC(0, kDebugScene, "scene[%u] has %u fadeboxes", i + 1, sceneFades.size()); } } @@ -293,10 +291,10 @@ Object *Scene::findObject(const Common::Point &point) { if (_id == 0) return NULL; - Common::Array<Object> &scene_objects = objects[_id - 1]; + Common::Array<Object> &sceneObjects = objects[_id - 1]; - for (uint i = 0; i < scene_objects.size(); ++i) { - Object &obj = scene_objects[i]; + for (uint i = 0; i < sceneObjects.size(); ++i) { + Object &obj = sceneObjects[i]; if (obj.enabled != 0 && obj.rect.in(point)) return &obj; } @@ -304,41 +302,38 @@ Object *Scene::findObject(const Common::Point &point) { } byte *Scene::getOns(int id) { - Resources *res = Resources::instance(); - return res->dseg.ptr(res->dseg.get_word(0xb4f5 + (id - 1) * 2)); + return _vm->res->dseg.ptr(_vm->res->dseg.get_word(dsAddr_onsAnimationTablePtr + (id - 1) * 2)); } byte *Scene::getLans(int id) { - Resources *res = Resources::instance(); - return res->dseg.ptr(0xd89e + (id - 1) * 4); + return _vm->res->dseg.ptr(dsAddr_lansAnimationTablePtr + (id - 1) * 4); } void Scene::loadOns() { - debug(0, "loading ons animation"); - Resources *res = Resources::instance(); + debugC(0, kDebugScene, "loading ons animation"); - uint16 addr = res->dseg.get_word(0xb4f5 + (_id - 1) * 2); - //debug(0, "ons index: %04x", addr); + uint16 addr = _vm->res->dseg.get_word(dsAddr_onsAnimationTablePtr + (_id - 1) * 2); + debugC(0, kDebugScene, "ons index: %04x", addr); - ons_count = 0; + onsCount = 0; byte b; - byte on_id[16]; - while ((b = res->dseg.get_byte(addr)) != 0xff) { - debug(0, "on: %04x = %02x", addr, b); + byte onId[16]; + while ((b = _vm->res->dseg.get_byte(addr)) != 0xff) { + debugC(0, kDebugScene, "on: %04x = %02x", addr, b); ++addr; if (b == 0) continue; - on_id[ons_count++] = b; + onId[onsCount++] = b; } delete[] ons; ons = NULL; - if (ons_count > 0) { - ons = new Surface[ons_count]; - for (uint32 i = 0; i < ons_count; ++i) { - Common::ScopedPtr<Common::SeekableReadStream> s(res->ons.getStream(on_id[i])); + if (onsCount > 0) { + ons = new Surface[onsCount]; + for (uint32 i = 0; i < onsCount; ++i) { + Common::ScopedPtr<Common::SeekableReadStream> s(_vm->res->ons.getStream(onId[i])); if (s) { ons[i].load(*s, Surface::kTypeOns); } @@ -347,21 +342,20 @@ void Scene::loadOns() { } void Scene::loadLans() { - debug(0, "loading lans animation"); - Resources *res = Resources::instance(); - //load lan000 + debugC(0, kDebugScene, "loading lans animation"); + // load lan000 for (byte i = 0; i < 4; ++i) { animation[i].free(); - uint16 bx = 0xd89e + (_id - 1) * 4 + i; - byte bxv = res->dseg.get_byte(bx); - uint16 res_id = 4 * (_id - 1) + i + 1; - debug(0, "lan[%u]@%04x = %02x, resource id: %u", i, bx, bxv, res_id); + uint16 bx = dsAddr_lansAnimationTablePtr + (_id - 1) * 4 + i; + byte bxv = _vm->res->dseg.get_byte(bx); + uint16 resId = 4 * (_id - 1) + i + 1; + debugC(0, kDebugScene, "lan[%u]@%04x = %02x, resource id: %u", i, bx, bxv, resId); if (bxv == 0) continue; - Common::ScopedPtr<Common::SeekableReadStream> s(res->loadLan000(res_id)); + Common::ScopedPtr<Common::SeekableReadStream> s(_vm->res->loadLan000(resId)); if (s) { animation[i].load(*s, Animation::kTypeLan); if (bxv != 0 && bxv != 0xff) @@ -371,24 +365,23 @@ void Scene::loadLans() { } void Scene::init(int id, const Common::Point &pos) { - debug(0, "init(%d)", id); + debugC(0, kDebugScene, "init(%d)", id); _id = id; - on_enabled = true; //reset on-rendering flag on loading. + onEnabled = true; //reset on-rendering flag on loading. sounds.clear(); for (byte i = 0; i < 4; ++i) - custom_animation[i].free(); + customAnimation[i].free(); if (background.pixels == NULL) - background.create(320, 200, Graphics::PixelFormat::createFormatCLUT8()); + background.create(kScreenWidth, kScreenHeight, Graphics::PixelFormat::createFormatCLUT8()); warp(pos); - Resources *res = Resources::instance(); - res->loadOff(background, palette, id); + _vm->res->loadOff(background, palette, id); if (id == 24) { - //dark scene - if (res->dseg.get_byte(0xDBA4) != 1) { - //dim down palette + // dark scene + if (_vm->res->dseg.get_byte(dsAddr_lightOnFlag) != 1) { + // dim down palette uint i; for (i = 0; i < 624; ++i) { palette[i] = palette[i] > 0x20 ? palette[i] - 0x20 : 0; @@ -399,62 +392,62 @@ void Scene::init(int id, const Common::Point &pos) { } } - Common::ScopedPtr<Common::SeekableReadStream> stream(res->on.getStream(id)); - int sub_hack = 0; - if (id == 7) { //something patched in the captains room - switch (res->dseg.get_byte(0xdbe6)) { + Common::ScopedPtr<Common::SeekableReadStream> stream(_vm->res->on.getStream(id)); + int subHack = 0; + if (id == 7) { // something patched in the captains room + switch (_vm->res->dseg.get_byte(dsAddr_captainDrawerState)) { case 2: break; case 1: - sub_hack = 1; + subHack = 1; break; default: - sub_hack = 2; + subHack = 2; } } - on.load(*stream, SurfaceList::kTypeOn, sub_hack); + on.load(*stream, subHack); loadOns(); loadLans(); - //check music - int now_playing = _engine->music->getId(); + // check music + int nowPlaying = _vm->music->getId(); - if (now_playing != res->dseg.get_byte(0xDB90)) - _engine->music->load(res->dseg.get_byte(0xDB90)); + if (nowPlaying != _vm->res->dseg.get_byte(dsAddr_currentMusic)) + _vm->music->load(_vm->res->dseg.get_byte(dsAddr_currentMusic)); - _system->copyRectToScreen(background.pixels, background.pitch, 0, 0, background.w, background.h); + _vm->_system->copyRectToScreen(background.pixels, background.pitch, 0, 0, background.w, background.h); setPalette(0); } void Scene::playAnimation(byte idx, uint id, bool loop, bool paused, bool ignore) { - debug(0, "playAnimation(%u, %u, loop:%s, paused:%s, ignore:%s)", idx, id, loop ? "true" : "false", paused ? "true" : "false", ignore ? "true" : "false"); + debugC(0, kDebugScene, "playAnimation(%u, %u, loop:%s, paused:%s, ignore:%s)", idx, id, loop ? "true" : "false", paused ? "true" : "false", ignore ? "true" : "false"); assert(idx < 4); - Common::ScopedPtr<Common::SeekableReadStream> s(Resources::instance()->loadLan(id + 1)); + Common::ScopedPtr<Common::SeekableReadStream> s(_vm->res->loadLan(id + 1)); if (!s) error("playing animation %u failed", id); - custom_animation[idx].load(*s); - custom_animation[idx].loop = loop; - custom_animation[idx].paused = paused; - custom_animation[idx].ignore = ignore; + customAnimation[idx].load(*s); + customAnimation[idx].loop = loop; + customAnimation[idx].paused = paused; + customAnimation[idx].ignore = ignore; } void Scene::playActorAnimation(uint id, bool loop, bool ignore) { - debug(0, "playActorAnimation(%u, loop:%s, ignore:%s)", id, loop ? "true" : "false", ignore ? "true" : "false"); - Common::ScopedPtr<Common::SeekableReadStream> s(Resources::instance()->loadLan(id + 1)); + debugC(0, kDebugScene, "playActorAnimation(%u, loop:%s, ignore:%s)", id, loop ? "true" : "false", ignore ? "true" : "false"); + Common::ScopedPtr<Common::SeekableReadStream> s(_vm->res->loadLan(id + 1)); if (!s) error("playing animation %u failed", id); - actor_animation.load(*s); - actor_animation.loop = loop; - actor_animation.ignore = ignore; - actor_animation.id = id; + actorAnimation.load(*s); + actorAnimation.loop = loop; + actorAnimation.ignore = ignore; + actorAnimation.id = id; } Animation *Scene::getAnimation(byte slot) { assert(slot < 4); - return custom_animation + slot; + return customAnimation + slot; } byte Scene::peekFlagEvent(uint16 addr) const { @@ -463,17 +456,17 @@ byte Scene::peekFlagEvent(uint16 addr) const { if (e.type == SceneEvent::kSetFlag && e.callback == addr) return e.color; } - return Resources::instance()->dseg.get_byte(addr); + return _vm->res->dseg.get_byte(addr); } void Scene::push(const SceneEvent &event) { - //debug(0, "push"); + debugC(0, kDebugScene, "push"); //event.dump(); if (event.type == SceneEvent::kWalk && !events.empty()) { SceneEvent &prev = events.back(); if (prev.type == SceneEvent::kWalk && prev.color == event.color) { - debug(0, "fixing double-move [skipping event!]"); - if ((event.color & 2) != 0) { //relative move + debugC(0, kDebugScene, "fixing double-move [skipping event!]"); + if ((event.color & 2) != 0) { // relative move prev.dst.x += event.dst.x; prev.dst.y += event.dst.y; } else { @@ -489,7 +482,7 @@ bool Scene::processEvent(const Common::Event &event) { switch (event.type) { case Common::EVENT_LBUTTONDOWN: case Common::EVENT_RBUTTONDOWN: - if (!message.empty() && message_first_frame == 0) { + if (!message.empty() && messageFirstFrame == 0) { clearMessage(); nextEvent(); return true; @@ -505,16 +498,16 @@ bool Scene::processEvent(const Common::Event &event) { clearMessage(); events.clear(); sounds.clear(); - current_event.clear(); - message_color = 0xd1; + currentEvent.clear(); + messageColor = textColorMark; for (int i = 0; i < 4; ++i) - custom_animation[i].free(); - _engine->playMusic(4); - _engine->loadScene(10, Common::Point(136, 153)); + customAnimation[i].free(); + _vm->playMusic(4); + _vm->loadScene(10, Common::Point(136, 153)); return true; } - if (!message.empty() && message_first_frame == 0) { + if (!message.empty() && messageFirstFrame == 0) { clearMessage(); nextEvent(); return true; @@ -534,8 +527,8 @@ bool Scene::processEvent(const Common::Event &event) { if (event.kbd.flags & Common::KBD_CTRL) { uint feature = event.kbd.keycode - '1'; if (feature < DebugFeatures::kMax) { - debug_features.feature[feature] = !debug_features.feature[feature]; - debug(0, "switched feature %u %s", feature, debug_features.feature[feature] ? "on" : "off"); + debugFeatures.feature[feature] = !debugFeatures.feature[feature]; + debugC(0, kDebugScene, "switched feature %u %s", feature, debugFeatures.feature[feature] ? "on" : "off"); } } break; @@ -556,25 +549,22 @@ struct ZOrderCmp { }; int Scene::lookupZoom(uint y) const { - Resources *res = Resources::instance(); - for (byte *zoom_table = res->dseg.ptr(res->dseg.get_word(0x70f4 + (_id - 1) * 2)); - zoom_table[0] != 0xff && zoom_table[1] != 0xff; - zoom_table += 2) { - //debug(0, "%d %d->%d", y, zoom_table[0], zoom_table[1]); - if (y <= zoom_table[0]) { - //debug(0, "%d %d->%d", y, zoom_table[0], zoom_table[1]); - return 256u * (100 - zoom_table[1]) / 100; + debugC(2, kDebugScene, "lookupZoom(%d)", y); + for (byte *zoomTable = _vm->res->dseg.ptr(_vm->res->dseg.get_word(dsAddr_sceneZoomTablePtr + (_id - 1) * 2)); + zoomTable[0] != 0xff && zoomTable[1] != 0xff; + zoomTable += 2) { + debugC(2, kDebugScene, "\t%d %d->%d", y, zoomTable[0], zoomTable[1]); + if (y <= zoomTable[0]) { + return 256u * (100 - zoomTable[1]) / 100; } } return 256; } - void Scene::paletteEffect(byte step) { - Resources *res = Resources::instance(); - byte *src = res->dseg.ptr(0x6609); - byte *dst = palette + 3 * 0xf2; - for (byte i = 0; i < 0xd; ++i) { + byte *src = _vm->res->dseg.ptr(dsAddr_paletteEffectData); + byte *dst = palette + (3 * 242); + for (byte i = 0; i < 13; ++i) { for (byte c = 0; c < 3; ++c, ++src) *dst++ = *src > step ? *src - step : 0; } @@ -584,9 +574,9 @@ byte Scene::findFade() const { if (_id <= 0) return 0; - const Common::Array<FadeType> &scene_fades = fades[_id - 1]; - for (uint i = 0; i < scene_fades.size(); ++i) { - const FadeType &fade = scene_fades[i]; + const Common::Array<FadeType> &sceneFades = fades[_id - 1]; + for (uint i = 0; i < sceneFades.size(); ++i) { + const FadeType &fade = sceneFades[i]; if (fade.rect.in(position)) { return fade.value; } @@ -594,110 +584,109 @@ byte Scene::findFade() const { return 0; } -bool Scene::render(bool tick_game, bool tick_mark, uint32 delta) { - Resources *res = Resources::instance(); +bool Scene::render(bool tickGame, bool tickMark, uint32 messageDelta) { bool busy; bool restart; - uint32 game_delta = tick_game ? 1 : 0; - uint32 mark_delta = tick_mark ? 1 : 0; + uint32 gameDelta = tickGame ? 1 : 0; + uint32 markDelta = tickMark ? 1 : 0; do { restart = false; busy = processEventQueue(); - if (_fade_timer && game_delta != 0) { - if (_fade_timer > 0) { - _fade_timer -= game_delta; - setPalette(_fade_timer); + if (_fadeTimer && gameDelta != 0) { + if (_fadeTimer > 0) { + _fadeTimer -= gameDelta; + setPalette(_fadeTimer); } else { - _fade_timer += game_delta; - setPalette(_fade_timer + 4); + _fadeTimer += gameDelta; + setPalette(_fadeTimer + 4); } } - switch (current_event.type) { + switch (currentEvent.type) { case SceneEvent::kCredits: { - _system->fillScreen(0); - ///\todo: optimize me - Graphics::Surface *surface = _system->lockScreen(); - res->font7.render(surface, current_event.dst.x, current_event.dst.y -= game_delta, current_event.message, current_event.color); - _system->unlockScreen(); - - if (current_event.dst.y < -(int)current_event.timer) - current_event.clear(); + _vm->_system->fillScreen(0); + // TODO: optimize me + Graphics::Surface *surface = _vm->_system->lockScreen(); + _vm->res->font7.render(surface, currentEvent.dst.x, currentEvent.dst.y -= gameDelta, currentEvent.message, currentEvent.color); + _vm->_system->unlockScreen(); + + if (currentEvent.dst.y < -(int)currentEvent.timer) + currentEvent.clear(); } return true; default: ; } - if (!message.empty() && message_timer != 0) { - if (message_timer <= delta) { + if (!message.empty() && messageTimer != 0) { + if (messageTimer <= messageDelta) { clearMessage(); nextEvent(); continue; } else - message_timer -= delta; + messageTimer -= messageDelta; } - if (current_event.type == SceneEvent::kCreditsMessage) { - _system->fillScreen(0); - Graphics::Surface *surface = _system->lockScreen(); - if (current_event.lan == 8) { - res->font8.shadow_color = current_event.orientation; - res->font8.render(surface, current_event.dst.x, current_event.dst.y, message, current_event.color); + if (currentEvent.type == SceneEvent::kCreditsMessage) { + _vm->_system->fillScreen(0); + Graphics::Surface *surface = _vm->_system->lockScreen(); + if (currentEvent.lan == 8) { + _vm->res->font8.setShadowColor(currentEvent.orientation); + _vm->res->font8.render(surface, currentEvent.dst.x, currentEvent.dst.y, message, currentEvent.color); } else { - res->font7.render(surface, current_event.dst.x, current_event.dst.y, message, 0xd1); + _vm->res->font7.render(surface, currentEvent.dst.x, currentEvent.dst.y, message, textColorCredits); } - _system->unlockScreen(); + _vm->_system->unlockScreen(); return true; } - if (background.pixels && debug_features.feature[DebugFeatures::kShowBack]) { - _system->copyRectToScreen(background.pixels, background.pitch, 0, 0, background.w, background.h); + if (background.pixels && debugFeatures.feature[DebugFeatures::kShowBack]) { + _vm->_system->copyRectToScreen(background.pixels, background.pitch, 0, 0, background.w, background.h); } else - _system->fillScreen(0); + _vm->_system->fillScreen(0); - Graphics::Surface *surface = _system->lockScreen(); + Graphics::Surface *surface = _vm->_system->lockScreen(); - bool got_any_animation = false; + bool gotAnyAnimation = false; - if (ons != NULL && debug_features.feature[DebugFeatures::kShowOns]) { - for (uint32 i = 0; i < ons_count; ++i) { + if (ons != NULL && debugFeatures.feature[DebugFeatures::kShowOns]) { + for (uint32 i = 0; i < onsCount; ++i) { Surface *s = ons + i; if (s != NULL) s->render(surface); } } - Common::List<Surface *> z_order; + Common::List<Surface *> zOrder; for (byte i = 0; i < 4; ++i) { - Animation *a = custom_animation + i; - Surface *s = a->currentFrame(game_delta); + Animation *a = customAnimation + i; + Surface *s = a->currentFrame(gameDelta); if (s != NULL) { if (!a->ignore) busy = true; if (!a->paused && !a->loop) - got_any_animation = true; + gotAnyAnimation = true; } else { a = animation + i; - if (!custom_animation[i].empty()) { - debug(0, "custom animation ended, restart animation in the same slot."); - custom_animation[i].free(); + if (!customAnimation[i].empty()) { + debugC(0, kDebugScene, "custom animation ended, restart animation in the same slot."); + customAnimation[i].free(); a->restart(); } - s = a->currentFrame(game_delta); + s = a->currentFrame(gameDelta); } - if (current_event.type == SceneEvent::kWaitLanAnimationFrame && current_event.slot == i) { + if (currentEvent.type == SceneEvent::kWaitLanAnimationFrame && currentEvent.slot == i) { if (s == NULL) { restart |= nextEvent(); continue; } int index = a->currentIndex(); - if (index == current_event.animation) { - debug(0, "kWaitLanAnimationFrame(%d, %d) complete", current_event.slot, current_event.animation); + if (index == currentEvent.animation) { + debugC(0, kDebugScene, "kWaitLanAnimationFrame(%d, %d) complete", currentEvent.slot, currentEvent.animation); restart |= nextEvent(); } } @@ -705,8 +694,8 @@ bool Scene::render(bool tick_game, bool tick_mark, uint32 delta) { if (s == NULL) continue; - if (debug_features.feature[DebugFeatures::kShowLan]) - z_order.push_back(s); + if (debugFeatures.feature[DebugFeatures::kShowLan]) + zOrder.push_back(s); if (a->id == 0) continue; @@ -722,38 +711,36 @@ bool Scene::render(bool tick_game, bool tick_mark, uint32 delta) { } } - Common::sort(z_order.begin(), z_order.end(), ZOrderCmp()); - Common::List<Surface *>::iterator z_order_it; + Common::sort(zOrder.begin(), zOrder.end(), ZOrderCmp()); + Common::List<Surface *>::iterator zOrderIter; - Surface *mark = actor_animation.currentFrame(game_delta); + Surface *mark = actorAnimation.currentFrame(gameDelta); int horizon = position.y; - for (z_order_it = z_order.begin(); z_order_it != z_order.end(); ++z_order_it) { - Surface *s = *z_order_it; + for (zOrderIter = zOrder.begin(); zOrderIter != zOrder.end(); ++zOrderIter) { + Surface *s = *zOrderIter; if (s->y + s->h > horizon) break; s->render(surface); } if (mark != NULL) { - actor_animation_position = mark->render(surface); - if (!actor_animation.ignore) + actorAnimationPosition = mark->render(surface); + if (!actorAnimation.ignore) busy = true; else busy = false; - got_any_animation = true; - } else if (!hide_actor) { - actor_animation.free(); + gotAnyAnimation = true; + } else if (!hideActor) { + actorAnimation.free(); uint zoom = lookupZoom(position.y); - { - byte fade = findFade(); - static byte old_fade = 0; - if (fade != old_fade) { - old_fade = fade; - paletteEffect(fade); - if (_fade_timer == 0) - setPalette(4); - } + + byte fade = findFade(); + if (fade != _fadeOld) { + _fadeOld = fade; + paletteEffect(fade); + if (_fadeTimer == 0) + setPalette(4); } if (!path.empty()) { @@ -767,31 +754,31 @@ bool Scene::render(bool tick_game, bool tick_mark, uint32 delta) { o = dp.y > 0 ? kActorDown : kActorUp; } - if (tick_mark) { - int speed_x = zoom / 32; //8 * zoom / 256 - int speed_y = (o == kActorDown || o == kActorUp ? 2 : 1) * zoom / 256; - if (speed_x == 0) - speed_x = 1; - if (speed_y == 0) - speed_y = 1; + if (tickMark) { + int speedX = zoom / 32; // 8 * zoom / 256 + int speedY = (o == kActorDown || o == kActorUp ? 2 : 1) * zoom / 256; + if (speedX == 0) + speedX = 1; + if (speedY == 0) + speedY = 1; - position.y += (ABS(dp.y) < speed_y ? dp.y : SIGN(dp.y) * speed_y); + position.y += (ABS(dp.y) < speedY ? dp.y : SIGN(dp.y) * speedY); position.x += (o == kActorDown || o == kActorUp) ? - (ABS(dp.x) < speed_y ? dp.x : SIGN(dp.x) * speed_y) : - (ABS(dp.x) < speed_x ? dp.x : SIGN(dp.x) * speed_x); + (ABS(dp.x) < speedY ? dp.x : SIGN(dp.x) * speedY) : + (ABS(dp.x) < speedX ? dp.x : SIGN(dp.x) * speedX); } - _idle_timer = 0; - teenagent_idle.resetIndex(); - actor_animation_position = teenagent.render(surface, position, o, mark_delta, false, zoom); + _idleTimer = 0; + teenagentIdle.resetIndex(); + actorAnimationPosition = teenagent.render(surface, position, o, markDelta, false, zoom); - if (tick_mark && position == destination) { + if (tickMark && position == destination) { path.pop_front(); if (path.empty()) { if (orientation == 0) - orientation = o; //save last orientation + orientation = o; // save last orientation nextEvent(); - got_any_animation = true; + gotAnyAnimation = true; restart = true; } busy = true; @@ -799,65 +786,63 @@ bool Scene::render(bool tick_game, bool tick_mark, uint32 delta) { busy = true; } else { teenagent.resetIndex(); - _idle_timer += mark_delta; - if (_idle_timer < 50) - actor_animation_position = teenagent.render(surface, position, orientation, 0, actor_talking, zoom); + _idleTimer += markDelta; + if (_idleTimer < 50) + actorAnimationPosition = teenagent.render(surface, position, orientation, 0, actorTalking, zoom); else - actor_animation_position = teenagent_idle.renderIdle(surface, position, orientation, mark_delta, zoom, _engine->_rnd); + actorAnimationPosition = teenagentIdle.renderIdle(surface, position, orientation, markDelta, zoom, _vm->_rnd); } } if (restart) { - _system->unlockScreen(); + _vm->_system->unlockScreen(); continue; } - //removed mark == null. In final scene of chapter 2 mark rendered above table. - //if it'd cause any bugs, add hack here. (_id != 23 && mark == NULL) - if (on_enabled && - debug_features.feature[DebugFeatures::kShowOn]) { - on.render(surface, actor_animation_position); - } + // removed mark == null. In final scene of chapter 2 mark rendered above table. + // if it'd cause any bugs, add hack here. (_id != 23 && mark == NULL) + if (onEnabled && debugFeatures.feature[DebugFeatures::kShowOn]) + on.render(surface, actorAnimationPosition); - for (; z_order_it != z_order.end(); ++z_order_it) { - Surface *s = *z_order_it; + for (; zOrderIter != zOrder.end(); ++zOrderIter) { + Surface *s = *zOrderIter; s->render(surface); } if (!message.empty()) { bool visible = true; - if (message_first_frame != 0 && message_animation != NULL) { - int index = message_animation->currentIndex() + 1; - //debug(0, "message: %s first: %u index: %u", message.c_str(), message_first_frame, index); - if (index < message_first_frame) + if (messageFirstFrame != 0 && messageAnimation != NULL) { + int index = messageAnimation->currentIndex() + 1; + debugC(0, kDebugScene, "message: %s first: %u index: %u", message.c_str(), messageFirstFrame, index); + if (index < messageFirstFrame) visible = false; - if (index > message_last_frame) { + if (index > messageLastFrame) { clearMessage(); visible = false; } } if (visible) { - res->font7.render(surface, message_pos.x, message_pos.y, message, message_color); + _vm->res->font7.render(surface, messagePos.x, messagePos.y, message, messageColor); busy = true; } } - if (!busy && !restart && tick_game && callback_timer) { - if (--callback_timer == 0) { - if (_engine->inventory->active()) - _engine->inventory->activate(false); - _engine->processCallback(callback); + if (!busy && !restart && tickGame && callbackTimer) { + if (--callbackTimer == 0) { + if (_vm->inventory->active()) + _vm->inventory->activate(false); + _vm->processCallback(callback); } - //debug(0, "callback timer = %u", callback_timer); + debugC(0, kDebugScene, "callback timer = %u", callbackTimer); } //if (!current_event.empty()) // current_event.dump(); - if (!debug_features.feature[DebugFeatures::kHidePath]) { - const Common::Array<Walkbox> & scene_walkboxes = walkboxes[_id - 1]; - for (uint i = 0; i < scene_walkboxes.size(); ++i) { - scene_walkboxes[i].rect.render(surface, 0xd0 + i); + if (!debugFeatures.feature[DebugFeatures::kHidePath]) { + const Common::Array<Walkbox> & sceneWalkboxes = walkboxes[_id - 1]; + for (uint i = 0; i < sceneWalkboxes.size(); ++i) { + sceneWalkboxes[i].rect.render(surface, 0xd0 + i); } Common::Point last_p = position; @@ -872,39 +857,39 @@ bool Scene::render(bool tick_game, bool tick_mark, uint32 delta) { } } - _system->unlockScreen(); + _vm->_system->unlockScreen(); - if (current_event.type == SceneEvent::kWait) { - if (current_event.timer > delta) { + if (currentEvent.type == SceneEvent::kWait) { + if (currentEvent.timer > messageDelta) { busy = true; - current_event.timer -= delta; + currentEvent.timer -= messageDelta; } - if (current_event.timer <= delta) + if (currentEvent.timer <= messageDelta) restart |= nextEvent(); } - if (!restart && current_event.type == SceneEvent::kWaitForAnimation && !got_any_animation) { - debug(0, "no animations, nextevent"); + if (!restart && currentEvent.type == SceneEvent::kWaitForAnimation && !gotAnyAnimation) { + debugC(0, kDebugScene, "no animations, nextevent"); nextEvent(); restart = true; } if (busy) { - _idle_timer = 0; - teenagent_idle.resetIndex(); + _idleTimer = 0; + teenagentIdle.resetIndex(); } } while (restart); for (Sounds::iterator i = sounds.begin(); i != sounds.end();) { Sound &sound = *i; if (sound.delay == 0) { - debug(1, "sound %u started", sound.id); - _engine->playSoundNow(sound.id); + debugC(1, kDebugScene, "sound %u started", sound.id); + _vm->playSoundNow(sound.id); i = sounds.erase(i); } else { - sound.delay -= game_delta; + sound.delay -= gameDelta; ++i; } } @@ -913,363 +898,364 @@ bool Scene::render(bool tick_game, bool tick_mark, uint32 delta) { } bool Scene::processEventQueue() { - while (!events.empty() && current_event.empty()) { - //debug(0, "processing next event"); - current_event = events.front(); + while (!events.empty() && currentEvent.empty()) { + debugC(0, kDebugScene, "processing next event"); + currentEvent = events.front(); events.pop_front(); - switch (current_event.type) { + switch (currentEvent.type) { case SceneEvent::kSetOn: { - byte on_id = current_event.ons; - if (on_id != 0) { - --on_id; - byte *ptr = getOns(current_event.scene == 0 ? _id : current_event.scene); - debug(0, "on[%u] = %02x", on_id, current_event.color); - ptr[on_id] = current_event.color; + byte onId = currentEvent.ons; + if (onId != 0) { + --onId; + byte *ptr = getOns(currentEvent.scene == 0 ? _id : currentEvent.scene); + debugC(0, kDebugScene, "on[%u] = %02x", onId, currentEvent.color); + ptr[onId] = currentEvent.color; } else { - on_enabled = current_event.color != 0; - debug(0, "%s on rendering", on_enabled ? "enabling" : "disabling"); + onEnabled = currentEvent.color != 0; + debugC(0, kDebugScene, "%s on rendering", onEnabled ? "enabling" : "disabling"); } loadOns(); - current_event.clear(); + currentEvent.clear(); } break; case SceneEvent::kSetLan: { - if (current_event.lan != 0) { - debug(0, "lan[%u] = %02x", current_event.lan - 1, current_event.color); - byte *ptr = getLans(current_event.scene == 0 ? _id : current_event.scene); - ptr[current_event.lan - 1] = current_event.color; + if (currentEvent.lan != 0) { + debugC(0, kDebugScene, "lan[%u] = %02x", currentEvent.lan - 1, currentEvent.color); + byte *ptr = getLans(currentEvent.scene == 0 ? _id : currentEvent.scene); + ptr[currentEvent.lan - 1] = currentEvent.color; } loadLans(); - current_event.clear(); + currentEvent.clear(); } break; case SceneEvent::kLoadScene: { - if (current_event.scene != 0) { - init(current_event.scene, current_event.dst); - if (current_event.orientation != 0) - orientation = current_event.orientation; + if (currentEvent.scene != 0) { + init(currentEvent.scene, currentEvent.dst); + if (currentEvent.orientation != 0) + orientation = currentEvent.orientation; } else { - //special case, empty scene + // special case, empty scene background.free(); on.free(); delete[] ons; ons = NULL; for (byte i = 0; i < 4; ++i) { animation[i].free(); - custom_animation[i].free(); + customAnimation[i].free(); } } - current_event.clear(); + currentEvent.clear(); } break; case SceneEvent::kWalk: { - Common::Point dst = current_event.dst; - if ((current_event.color & 2) != 0) { //relative move + Common::Point dst = currentEvent.dst; + if ((currentEvent.color & 2) != 0) { // relative move dst.x += position.x; dst.y += position.y; } - if ((current_event.color & 1) != 0) { - warp(dst, current_event.orientation); - current_event.clear(); + if ((currentEvent.color & 1) != 0) { + warp(dst, currentEvent.orientation); + currentEvent.clear(); } else - moveTo(dst, current_event.orientation); + moveTo(dst, currentEvent.orientation); } break; case SceneEvent::kCreditsMessage: case SceneEvent::kMessage: { - message = current_event.message; - message_animation = NULL; - if (current_event.first_frame) { - message_timer = 0; - message_first_frame = current_event.first_frame; - message_last_frame = current_event.last_frame; - if (current_event.slot > 0) { - message_animation = custom_animation + (current_event.slot - 1); - //else if (!animation[current_event.slot].empty()) - // message_animation = animation + current_event.slot; + message = currentEvent.message; + messageAnimation = NULL; + if (currentEvent.firstFrame) { + messageTimer = 0; + messageFirstFrame = currentEvent.firstFrame; + messageLastFrame = currentEvent.lastFrame; + if (currentEvent.slot > 0) { + messageAnimation = customAnimation + (currentEvent.slot - 1); + //else if (!animation[currentEvent.slot].empty()) + // messageAnimation = animation + currentEvent.slot; } else - message_animation = &actor_animation; - debug(0, "async message %d-%d (slot %u)", message_first_frame, message_last_frame, current_event.slot); + messageAnimation = &actorAnimation; + debugC(0, kDebugScene, "async message %d-%d (slot %u)", messageFirstFrame, messageLastFrame, currentEvent.slot); } else { - message_timer = current_event.timer ? current_event.timer * 110 : messageDuration(message); - message_first_frame = message_last_frame = 0; + messageTimer = currentEvent.timer ? currentEvent.timer * 110 : messageDuration(message); + messageFirstFrame = messageLastFrame = 0; } Common::Point p; - if (current_event.dst.x == 0 && current_event.dst.y == 0) { - p = Common::Point((actor_animation_position.left + actor_animation_position.right) / 2, - actor_animation_position.top); + if (currentEvent.dst.x == 0 && currentEvent.dst.y == 0) { + p = Common::Point((actorAnimationPosition.left + actorAnimationPosition.right) / 2, + actorAnimationPosition.top); } else { - p = current_event.dst; + p = currentEvent.dst; } - byte message_slot = current_event.slot; - if (message_slot != 0) { - --message_slot; - assert(message_slot < 4); - const Surface *s = custom_animation[message_slot].currentFrame(0); + byte messageSlot = currentEvent.slot; + if (messageSlot != 0) { + --messageSlot; + assert(messageSlot < 4); + const Surface *s = customAnimation[messageSlot].currentFrame(0); if (s == NULL) - s = animation[message_slot].currentFrame(0); + s = animation[messageSlot].currentFrame(0); if (s != NULL) { p.x = s->x + s->w / 2; p.y = s->y; } else - warning("no animation in slot %u", message_slot); + warning("no animation in slot %u", messageSlot); } - message_pos = messagePosition(message, p); - message_color = current_event.color; + messagePos = messagePosition(message, p); + messageColor = currentEvent.color; - if (message_first_frame) - current_event.clear(); //async message, clearing event + if (messageFirstFrame) + currentEvent.clear(); // async message, clearing event } break; case SceneEvent::kPlayAnimation: { - byte slot = current_event.slot & 7; //0 - mark's - if (current_event.animation != 0) { - debug(0, "playing animation %u in slot %u(%02x)", current_event.animation, slot, current_event.slot); + byte slot = currentEvent.slot & 7; // 0 - mark's + if (currentEvent.animation != 0) { + debugC(0, kDebugScene, "playing animation %u in slot %u(%02x)", currentEvent.animation, slot, currentEvent.slot); if (slot != 0) { --slot; assert(slot < 4); - playAnimation(slot, current_event.animation, (current_event.slot & 0x80) != 0, (current_event.slot & 0x40) != 0, (current_event.slot & 0x20) != 0); + playAnimation(slot, currentEvent.animation, (currentEvent.slot & 0x80) != 0, (currentEvent.slot & 0x40) != 0, (currentEvent.slot & 0x20) != 0); } else - actor_talking = true; + actorTalking = true; } else { if (slot != 0) { --slot; - debug(0, "cancelling animation in slot %u", slot); + debugC(0, kDebugScene, "cancelling animation in slot %u", slot); assert(slot < 4); - custom_animation[slot].free(); + customAnimation[slot].free(); } else - actor_talking = true; + actorTalking = true; } - current_event.clear(); + currentEvent.clear(); } break; case SceneEvent::kPauseAnimation: { - byte slot = current_event.slot & 7; //0 - mark's + byte slot = currentEvent.slot & 7; // 0 - mark's if (slot != 0) { --slot; - debug(1, "pause animation in slot %u", slot); - custom_animation[slot].paused = (current_event.slot & 0x80) != 0; + debugC(1, kDebugScene, "pause animation in slot %u", slot); + customAnimation[slot].paused = (currentEvent.slot & 0x80) != 0; } else { - actor_talking = false; + actorTalking = false; } - current_event.clear(); + currentEvent.clear(); } break; case SceneEvent::kClearAnimations: for (byte i = 0; i < 4; ++i) - custom_animation[i].free(); - actor_talking = false; - current_event.clear(); + customAnimation[i].free(); + actorTalking = false; + currentEvent.clear(); break; case SceneEvent::kPlayActorAnimation: - debug(0, "playing actor animation %u", current_event.animation); - playActorAnimation(current_event.animation, (current_event.slot & 0x80) != 0, (current_event.slot & 0x20) != 0); - current_event.clear(); + debugC(0, kDebugScene, "playing actor animation %u", currentEvent.animation); + playActorAnimation(currentEvent.animation, (currentEvent.slot & 0x80) != 0, (currentEvent.slot & 0x20) != 0); + currentEvent.clear(); break; case SceneEvent::kPlayMusic: - debug(0, "setting music %u", current_event.music); - _engine->setMusic(current_event.music); - Resources::instance()->dseg.set_byte(0xDB90, current_event.music); - current_event.clear(); + debugC(0, kDebugScene, "setting music %u", currentEvent.music); + _vm->setMusic(currentEvent.music); + _vm->res->dseg.set_byte(dsAddr_currentMusic, currentEvent.music); + currentEvent.clear(); break; case SceneEvent::kPlaySound: - debug(0, "playing sound %u, delay: %u", current_event.sound, current_event.color); - sounds.push_back(Sound(current_event.sound, current_event.color)); - current_event.clear(); + debugC(0, kDebugScene, "playing sound %u, delay: %u", currentEvent.sound, currentEvent.color); + sounds.push_back(Sound(currentEvent.sound, currentEvent.color)); + currentEvent.clear(); break; case SceneEvent::kEnableObject: { - debug(0, "%s object #%u", current_event.color ? "enabling" : "disabling", current_event.object - 1); - Object *obj = getObject(current_event.object - 1, current_event.scene == 0 ? _id : current_event.scene); - obj->enabled = current_event.color; + debugC(0, kDebugScene, "%s object #%u", currentEvent.color ? "enabling" : "disabling", currentEvent.object - 1); + Object *obj = getObject(currentEvent.object - 1, currentEvent.scene == 0 ? _id : currentEvent.scene); + obj->enabled = currentEvent.color; obj->save(); - current_event.clear(); + currentEvent.clear(); } break; case SceneEvent::kHideActor: - hide_actor = current_event.color != 0; - current_event.clear(); + hideActor = currentEvent.color != 0; + currentEvent.clear(); break; case SceneEvent::kWaitForAnimation: - debug(0, "waiting for the animation"); + debugC(0, kDebugScene, "waiting for the animation"); break; case SceneEvent::kWaitLanAnimationFrame: - debug(0, "waiting for the frame %d in slot %d", current_event.animation, current_event.slot); + debugC(0, kDebugScene, "waiting for the frame %d in slot %d", currentEvent.animation, currentEvent.slot); break; case SceneEvent::kTimer: - callback = current_event.callback; - callback_timer = current_event.timer; - debug(0, "triggering callback %04x in %u frames", callback, callback_timer); - current_event.clear(); + callback = currentEvent.callback; + callbackTimer = currentEvent.timer; + debugC(0, kDebugScene, "triggering callback %04x in %u frames", callback, callbackTimer); + currentEvent.clear(); break; case SceneEvent::kEffect: - _system->delayMillis(80); //2 vsyncs - _system->setShakePos(8); - _system->updateScreen(); + _vm->_system->delayMillis(80); // 2 vsyncs + _vm->_system->setShakePos(8); + _vm->_system->updateScreen(); - _system->delayMillis(80); //2 vsyncs - _system->setShakePos(0); - _system->updateScreen(); + _vm->_system->delayMillis(80); // 2 vsyncs + _vm->_system->setShakePos(0); + _vm->_system->updateScreen(); - _system->delayMillis(80); //2 vsyncs - _system->setShakePos(4); - _system->updateScreen(); + _vm->_system->delayMillis(80); // 2 vsyncs + _vm->_system->setShakePos(4); + _vm->_system->updateScreen(); - _system->delayMillis(80); //2 vsyncs - _system->setShakePos(0); - _system->updateScreen(); + _vm->_system->delayMillis(80); // 2 vsyncs + _vm->_system->setShakePos(0); + _vm->_system->updateScreen(); - current_event.clear(); + currentEvent.clear(); break; case SceneEvent::kFade: - _fade_timer = current_event.orientation != 0 ? 5 : -5; - current_event.clear(); + _fadeTimer = currentEvent.orientation != 0 ? 5 : -5; + currentEvent.clear(); break; case SceneEvent::kWait: - debug(0, "wait %u", current_event.timer); + debugC(0, kDebugScene, "wait %u", currentEvent.timer); break; case SceneEvent::kCredits: - debug(0, "showing credits"); + debugC(0, kDebugScene, "showing credits"); break; case SceneEvent::kQuit: - debug(0, "quit!"); - _engine->quitGame(); + debugC(0, kDebugScene, "quit!"); + _vm->quitGame(); break; case SceneEvent::kSetFlag: - debug(0, "async set_flag(%04x, %d)", current_event.callback, current_event.color); - Resources::instance()->dseg.set_byte(current_event.callback, current_event.color); - current_event.clear(); + debugC(0, kDebugScene, "async set_flag(%04x, %d)", currentEvent.callback, currentEvent.color); + _vm->res->dseg.set_byte(currentEvent.callback, currentEvent.color); + currentEvent.clear(); break; default: - error("empty/unhandler event[%d]", (int)current_event.type); + error("empty/unhandler event[%d]", (int)currentEvent.type); } } + if (events.empty()) { - message_color = 0xd1; - hide_actor = false; + messageColor = textColorMark; + hideActor = false; } - return !current_event.empty(); + + return !currentEvent.empty(); } void Scene::setPalette(unsigned mul) { - //debug(0, "setPalette(%u)", mul); + debugC(0, kDebugScene, "setPalette(%u)", mul); byte p[3 * 256]; for (int i = 0; i < 3 * 256; ++i) { p[i] = (unsigned)palette[i] * mul; } - _system->getPaletteManager()->setPalette(p, 0, 256); + _vm->_system->getPaletteManager()->setPalette(p, 0, 256); } -Object *Scene::getObject(int id, int scene_id) { +Object *Scene::getObject(int id, int sceneId) { assert(id > 0); - if (scene_id == 0) - scene_id = _id; + if (sceneId == 0) + sceneId = _id; - if (scene_id == 0) + if (sceneId == 0) return NULL; - Common::Array<Object> &scene_objects = objects[scene_id - 1]; + Common::Array<Object> &sceneObjects = objects[sceneId - 1]; --id; - if (id >= (int)scene_objects.size()) + if (id >= (int)sceneObjects.size()) return NULL; - return &scene_objects[id]; + return &sceneObjects[id]; } -Common::Point Scene::messagePosition(const Common::String &str, Common::Point message_position) { - Resources *res = Resources::instance(); +Common::Point Scene::messagePosition(const Common::String &str, Common::Point pos) { int lines = 1; for (uint i = 0; i < str.size(); ++i) if (str[i] == '\n') ++lines; - uint w = res->font7.render(NULL, 0, 0, str, 0); - uint h = res->font7.height * lines + 3; + uint w = _vm->res->font7.render(NULL, 0, 0, str, 0); + uint h = _vm->res->font7.getHeight() * lines + 3; - message_position.x -= w / 2; - message_position.y -= h; + pos.x -= w / 2; + pos.y -= h; - if (message_position.x + w > 320) - message_position.x = 320 - w; - if (message_position.x < 0) - message_position.x = 0; - if (message_position.y + h > 320) - message_position.y = 200 - h; - if (message_position.y < 0) - message_position.y = 0; + if (pos.x + w > kScreenWidth) + pos.x = kScreenWidth - w; + if (pos.x < 0) + pos.x = 0; + if (pos.y + h > kScreenHeight) + pos.y = kScreenHeight - h; + if (pos.y < 0) + pos.y = 0; - return message_position; + return pos; } uint Scene::messageDuration(const Common::String &str) { - //original game uses static delays: 100-slow, 50, 20 and 1 tick - crazy speed. - //total delay = total message length * delay / 8 + 60. - uint total_width = str.size(); + // original game uses static delays: 100-slow, 50, 20 and 1 tick - crazy speed. + // total delay = total message length * delay / 8 + 60. + uint totalWidth = str.size(); - int speed = Common::ConfigManager::instance().getInt("talkspeed"); + int speed = ConfMan.getInt("talkspeed"); if (speed < 0) speed = 60; - uint delay_delta = 1 + (255 - speed) * 99 / 255; + uint delayDelta = 1 + (255 - speed) * 99 / 255; - uint delay = 60 + (total_width * delay_delta) / 8; - //debug(0, "delay = %u, delta: %u", delay, delay_delta); + uint delay = 60 + (totalWidth * delayDelta) / 8; + debugC(0, kDebugScene, "delay = %u, delta: %u", delay, delayDelta); return delay * 10; } void Scene::displayMessage(const Common::String &str, byte color, const Common::Point &pos) { //assert(!str.empty()); - //debug(0, "displayMessage: %s", str.c_str()); + debugC(0, kDebugScene, "displayMessage: %s", str.c_str()); message = str; - message_pos = (pos.x | pos.y) ? pos : messagePosition(str, position); - message_color = color; - message_timer = messageDuration(message); + messagePos = (pos.x | pos.y) ? pos : messagePosition(str, position); + messageColor = color; + messageTimer = messageDuration(message); } void Scene::clear() { clearMessage(); events.clear(); - current_event.clear(); + currentEvent.clear(); for (int i = 0; i < 4; ++i) { animation[i].free(); - custom_animation[i].free(); + customAnimation[i].free(); } callback = 0; - callback_timer = 0; + callbackTimer = 0; } void Scene::clearMessage() { message.clear(); - message_timer = 0; - message_color = 0xd1; - message_first_frame = 0; - message_last_frame = 0; - message_animation = NULL; + messageTimer = 0; + messageColor = textColorMark; + messageFirstFrame = 0; + messageLastFrame = 0; + messageAnimation = NULL; } } // End of namespace TeenAgent diff --git a/engines/teenagent/scene.h b/engines/teenagent/scene.h index 32e784bb60..14aefa0cca 100644 --- a/engines/teenagent/scene.h +++ b/engines/teenagent/scene.h @@ -27,8 +27,8 @@ #include "teenagent/objects.h" #include "teenagent/surface.h" #include "teenagent/surface_list.h" +#include "teenagent/teenagent.h" -#include "common/system.h" #include "common/array.h" #include "common/list.h" @@ -39,7 +39,6 @@ struct Event; namespace TeenAgent { class TeenAgentEngine; -class Dialog; struct SceneEvent { enum Type { @@ -84,22 +83,22 @@ struct SceneEvent { byte lan; union { byte music; - byte first_frame; + byte firstFrame; }; union { byte sound; - byte last_frame; + byte lastFrame; }; byte object; SceneEvent(Type type_) : - type(type_), message(), color(0xd1), slot(0), animation(0), timer(0), orientation(0), dst(), + type(type_), message(), color(textColorMark), slot(0), animation(0), timer(0), orientation(0), dst(), scene(0), ons(0), lan(0), music(0), sound(0), object(0) {} void clear() { type = kNone; message.clear(); - color = 0xd1; + color = textColorMark; slot = 0; orientation = 0; animation = 0; @@ -118,7 +117,7 @@ struct SceneEvent { } void dump() const { - debug(0, "event[%d]: \"%s\"[%02x], slot: %d, animation: %u, timer: %u, dst: (%d, %d) [%u], scene: %u, ons: %u, lan: %u, object: %u, music: %u, sound: %u", + debugC(0, kDebugScene, "event[%d]: \"%s\"[%02x], slot: %d, animation: %u, timer: %u, dst: (%d, %d) [%u], scene: %u, ons: %u, lan: %u, object: %u, music: %u, sound: %u", (int)type, message.c_str(), color, slot, animation, timer, dst.x, dst.y, orientation, scene, ons, lan, object, music, sound ); } @@ -126,13 +125,13 @@ struct SceneEvent { class Scene { public: - Scene(TeenAgentEngine *engine, OSystem *system); + Scene(TeenAgentEngine *engine); ~Scene(); bool intro; void init(int id, const Common::Point &pos); - bool render(bool tick_game, bool tick_mark, uint32 message_delta); + bool render(bool tickGame, bool tickMark, uint32 messageDelta); int getId() const { return _id; } void warp(const Common::Point &point, byte orientation = 0); @@ -140,7 +139,7 @@ public: void moveTo(const Common::Point &point, byte orientation = 0, bool validate = false); Common::Point getPosition() const { return position; } - void displayMessage(const Common::String &str, byte color = 0xd1, const Common::Point &pos = Common::Point()); + void displayMessage(const Common::String &str, byte color = textColorMark, const Common::Point &pos = Common::Point()); void setOrientation(uint8 o) { orientation = o; } void push(const SceneEvent &event); byte peekFlagEvent(uint16 addr) const; @@ -153,15 +152,15 @@ public: byte *getOns(int id); byte *getLans(int id); - bool eventRunning() const { return !current_event.empty(); } + bool eventRunning() const { return !currentEvent.empty(); } Walkbox *getWalkbox(byte id) { return &walkboxes[_id - 1][id]; } - Object *getObject(int id, int scene_id = 0); + Object *getObject(int id, int sceneId = 0); Object *findObject(const Common::Point &point); void loadObjectData(); Animation *getAnimation(byte slot); - inline Animation *getActorAnimation() { return &actor_animation; } + inline Animation *getActorAnimation() { return &actorAnimation; } inline const Common::String &getMessage() const { return message; } void setPalette(unsigned mul); int lookupZoom(uint y) const; @@ -173,39 +172,38 @@ private: void playAnimation(byte idx, uint id, bool loop, bool paused, bool ignore); void playActorAnimation(uint id, bool loop, bool ignore); - byte palette[768]; + byte palette[3 * 256]; void paletteEffect(byte step); byte findFade() const; - static Common::Point messagePosition(const Common::String &str, Common::Point position); - static uint messageDuration(const Common::String &str); + Common::Point messagePosition(const Common::String &str, Common::Point pos); + uint messageDuration(const Common::String &str); bool processEventQueue(); inline bool nextEvent() { - current_event.clear(); + currentEvent.clear(); return processEventQueue(); } void clearMessage(); - TeenAgentEngine *_engine; - OSystem *_system; + TeenAgentEngine *_vm; int _id; Graphics::Surface background; SurfaceList on; - bool on_enabled; + bool onEnabled; Surface *ons; - uint32 ons_count; - Animation actor_animation, animation[4], custom_animation[4]; - Common::Rect actor_animation_position, animation_position[4]; + uint32 onsCount; + Animation actorAnimation, animation[4], customAnimation[4]; + Common::Rect actorAnimationPosition, animationPosition[4]; - Actor teenagent, teenagent_idle; + Actor teenagent, teenagentIdle; Common::Point position; typedef Common::List<Common::Point> Path; Path path; uint8 orientation; - bool actor_talking; + bool actorTalking; bool findPath(Path &p, const Common::Point &src, const Common::Point &dst) const; @@ -214,22 +212,24 @@ private: Common::Array<Common::Array<FadeType> > fades; Common::String message; - Common::Point message_pos; - byte message_color; - uint message_timer; - byte message_first_frame; - byte message_last_frame; - Animation *message_animation; + Common::Point messagePos; + byte messageColor; + uint messageTimer; + byte messageFirstFrame; + byte messageLastFrame; + Animation *messageAnimation; typedef Common::List<SceneEvent> EventList; EventList events; - SceneEvent current_event; - bool hide_actor; + SceneEvent currentEvent; + bool hideActor; - uint16 callback, callback_timer; + uint16 callback, callbackTimer; - int _fade_timer; - uint _idle_timer; + int _fadeTimer; + byte _fadeOld; + + uint _idleTimer; struct Sound { byte id, delay; @@ -254,7 +254,7 @@ private: feature[i] = true; } } - } debug_features; + } debugFeatures; }; } // End of namespace TeenAgent diff --git a/engines/teenagent/segment.h b/engines/teenagent/segment.h index 303198b071..286337d120 100644 --- a/engines/teenagent/segment.h +++ b/engines/teenagent/segment.h @@ -41,26 +41,21 @@ public: assert(offset < _size); return _data[offset]; } + inline uint16 get_word(uint32 offset) const { assert(offset + 1 < _size); return READ_LE_UINT16(_data + offset); } - inline uint32 get_quad(uint32 offset) const { - assert(offset + 3 < _size); - return READ_LE_UINT32(_data + offset); - } + inline void set_byte(uint32 offset, byte v) const { assert(offset < _size); _data[offset] = v; } + inline void set_word(uint32 offset, uint16 v) const { assert(offset + 1 < _size); return WRITE_LE_UINT16(_data + offset, v); } - inline void set_quad(uint32 offset, uint32 v) const { - assert(offset + 3 < _size); - return WRITE_LE_UINT32(_data + offset, v); - } const byte *ptr(uint32 addr) const { assert(addr < _size); @@ -71,6 +66,7 @@ public: assert(addr < _size); return _data + addr; } + uint size() const { return _size; } }; diff --git a/engines/teenagent/surface.cpp b/engines/teenagent/surface.cpp index 63312990ee..4db25bc749 100644 --- a/engines/teenagent/surface.cpp +++ b/engines/teenagent/surface.cpp @@ -21,6 +21,8 @@ #include "teenagent/surface.h" #include "teenagent/pack.h" +#include "teenagent/teenagent.h" + #include "common/stream.h" #include "common/debug.h" @@ -34,7 +36,7 @@ Surface::~Surface() { } void Surface::load(Common::SeekableReadStream &stream, Type type) { - //debug(0, "load()"); + debugC(0, kDebugSurface, "load()"); free(); x = y = 0; @@ -44,71 +46,71 @@ void Surface::load(Common::SeekableReadStream &stream, Type type) { if (type != kTypeLan) { uint16 pos = stream.readUint16LE(); - x = pos % 320; - y = pos / 320; + x = pos % kScreenWidth; + y = pos / kScreenWidth; } - //debug(0, "declared info: %ux%u (%04xx%04x) -> %u,%u", w_, h_, w_, h_, x, y); + debugC(0, kDebugSurface, "declared info: %ux%u (%04xx%04x) -> %u,%u", w_, h_, w_, h_, x, y); if (stream.eos() || w_ == 0) return; if (w_ * h_ > stream.size()) { - debug(0, "invalid surface %ux%u -> %u,%u", w_, h_, x, y); + debugC(0, kDebugSurface, "invalid surface %ux%u -> %u,%u", w_, h_, x, y); return; } - //debug(0, "creating surface %ux%u -> %u,%u", w_, h_, x, y); + debugC(0, kDebugSurface, "creating surface %ux%u -> %u,%u", w_, h_, x, y); create(w_, h_, Graphics::PixelFormat::createFormatCLUT8()); stream.read(pixels, w_ * h_); } -Common::Rect Surface::render(Graphics::Surface *surface, int dx, int dy, bool mirror, Common::Rect src_rect, uint zoom) const { - if (src_rect.isEmpty()) { - src_rect = Common::Rect(0, 0, w, h); +Common::Rect Surface::render(Graphics::Surface *surface, int dx, int dy, bool mirror, Common::Rect srcRect, uint zoom) const { + if (srcRect.isEmpty()) { + srcRect = Common::Rect(0, 0, w, h); } - Common::Rect dst_rect(x + dx, y + dy, x + dx + zoom * src_rect.width() / 256, y + dy + zoom * src_rect.height() / 256); - if (dst_rect.left < 0) { - src_rect.left = -dst_rect.left; - dst_rect.left = 0; + Common::Rect dstRect(x + dx, y + dy, x + dx + zoom * srcRect.width() / 256, y + dy + zoom * srcRect.height() / 256); + if (dstRect.left < 0) { + srcRect.left = -dstRect.left; + dstRect.left = 0; } - if (dst_rect.right > surface->w) { - src_rect.right -= dst_rect.right - surface->w; - dst_rect.right = surface->w; + if (dstRect.right > surface->w) { + srcRect.right -= dstRect.right - surface->w; + dstRect.right = surface->w; } - if (dst_rect.top < 0) { - src_rect.top -= dst_rect.top; - dst_rect.top = 0; + if (dstRect.top < 0) { + srcRect.top -= dstRect.top; + dstRect.top = 0; } - if (dst_rect.bottom > surface->h) { - src_rect.bottom -= dst_rect.bottom - surface->h; - dst_rect.bottom = surface->h; + if (dstRect.bottom > surface->h) { + srcRect.bottom -= dstRect.bottom - surface->h; + dstRect.bottom = surface->h; } - if (src_rect.isEmpty() || dst_rect.isEmpty()) + if (srcRect.isEmpty() || dstRect.isEmpty()) return Common::Rect(); if (zoom == 256) { - const byte *src = (const byte *)getBasePtr(0, src_rect.top); - byte *dst_base = (byte *)surface->getBasePtr(dst_rect.left, dst_rect.top); + const byte *src = (const byte *)getBasePtr(0, srcRect.top); + byte *dstBase = (byte *)surface->getBasePtr(dstRect.left, dstRect.top); - for (int i = src_rect.top; i < src_rect.bottom; ++i) { - byte *dst = dst_base; - for (int j = src_rect.left; j < src_rect.right; ++j) { + for (int i = srcRect.top; i < srcRect.bottom; ++i) { + byte *dst = dstBase; + for (int j = srcRect.left; j < srcRect.right; ++j) { byte p = src[(mirror ? w - j - 1 : j)]; if (p != 0xff) *dst++ = p; else ++dst; } - dst_base += surface->pitch; + dstBase += surface->pitch; src += pitch; } } else { - byte *dst = (byte *)surface->getBasePtr(dst_rect.left, dst_rect.top); - for (int i = 0; i < dst_rect.height(); ++i) { - for (int j = 0; j < dst_rect.width(); ++j) { + byte *dst = (byte *)surface->getBasePtr(dstRect.left, dstRect.top); + for (int i = 0; i < dstRect.height(); ++i) { + for (int j = 0; j < dstRect.width(); ++j) { int px = j * 256 / zoom; - const byte *src = (const byte *)getBasePtr(src_rect.left + (mirror ? w - px - 1 : px), src_rect.top + i * 256 / zoom); + const byte *src = (const byte *)getBasePtr(srcRect.left + (mirror ? w - px - 1 : px), srcRect.top + i * 256 / zoom); byte p = *src; if (p != 0xff) dst[j] = p; @@ -116,7 +118,7 @@ Common::Rect Surface::render(Graphics::Surface *surface, int dx, int dy, bool mi dst += surface->pitch; } } - return dst_rect; + return dstRect; } } // End of namespace TeenAgent diff --git a/engines/teenagent/surface.h b/engines/teenagent/surface.h index 51368c6bee..3e591ed3e0 100644 --- a/engines/teenagent/surface.h +++ b/engines/teenagent/surface.h @@ -33,16 +33,17 @@ namespace TeenAgent { class Surface : public Graphics::Surface { public: - enum Type {kTypeOns, kTypeLan}; - - uint16 x, y; - Surface(); ~Surface(); + + enum Type {kTypeOns, kTypeLan}; + void load(Common::SeekableReadStream &, Type type); - Common::Rect render(Graphics::Surface *surface, int dx = 0, int dy = 0, bool mirror = false, Common::Rect src_rect = Common::Rect(), uint zoom = 256) const; + Common::Rect render(Graphics::Surface *surface, int dx = 0, int dy = 0, bool mirror = false, Common::Rect srcRect = Common::Rect(), uint zoom = 256) const; bool empty() const { return pixels == NULL; } + + uint16 x, y; }; } // End of namespace TeenAgent diff --git a/engines/teenagent/surface_list.cpp b/engines/teenagent/surface_list.cpp index 31387ac3cb..e293ce6470 100644 --- a/engines/teenagent/surface_list.cpp +++ b/engines/teenagent/surface_list.cpp @@ -19,34 +19,35 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include "teenagent/surface.h" #include "teenagent/surface_list.h" -#include "objects.h" +#include "teenagent/surface.h" +#include "teenagent/objects.h" +#include "teenagent/teenagent.h" namespace TeenAgent { -SurfaceList::SurfaceList() : surfaces(NULL), surfaces_n(0) {} +SurfaceList::SurfaceList() : surfaces(NULL), surfacesCount(0) {} SurfaceList::~SurfaceList() { free(); } -void SurfaceList::load(Common::SeekableReadStream &stream, Type type, int sub_hack) { +void SurfaceList::load(Common::SeekableReadStream &stream, int subHack) { free(); byte fn = stream.readByte(); if (stream.eos()) return; - surfaces_n = fn - sub_hack; - debug(0, "loading %u surfaces from list (skip %d)", surfaces_n, sub_hack); + surfacesCount = fn - subHack; + debugC(0, kDebugSurface, "loading %u surfaces from list (skip %d)", surfacesCount, subHack); - if (surfaces_n == 0) + if (surfacesCount == 0) return; - surfaces = new Surface[surfaces_n]; + surfaces = new Surface[surfacesCount]; - for (byte i = 0; i < surfaces_n; ++i) { + for (byte i = 0; i < surfacesCount; ++i) { uint offset = stream.readUint16LE(); uint pos = stream.pos(); stream.seek(offset); @@ -58,11 +59,11 @@ void SurfaceList::load(Common::SeekableReadStream &stream, Type type, int sub_ha void SurfaceList::free() { delete[] surfaces; surfaces = NULL; - surfaces_n = 0; + surfacesCount = 0; } void SurfaceList::render(Graphics::Surface *surface, const Common::Rect &clip) const { - for (uint i = 0; i < surfaces_n; ++i) { + for (uint i = 0; i < surfacesCount; ++i) { const Surface &s = surfaces[i]; Common::Rect r(s.x, s.y, s.x + s.w, s.y + s.h); if (r.bottom < clip.bottom || !clip.intersects(r)) diff --git a/engines/teenagent/surface_list.h b/engines/teenagent/surface_list.h index 2d7be0d52b..73a41fb5f8 100644 --- a/engines/teenagent/surface_list.h +++ b/engines/teenagent/surface_list.h @@ -23,23 +23,23 @@ #define TEENAGENT_SURFACE_LIST_H__ #include "common/stream.h" +#include "graphics/surface.h" namespace TeenAgent { class Surface; class SurfaceList { public: - enum Type { kTypeOn }; - SurfaceList(); ~SurfaceList(); - void load(Common::SeekableReadStream &, Type type, int sub_hack = 0); + + void load(Common::SeekableReadStream &, int subHack = 0); void free(); void render(Graphics::Surface *surface, const Common::Rect &clip) const; protected: Surface *surfaces; - uint surfaces_n; + uint surfacesCount; }; } diff --git a/engines/teenagent/teenagent.cpp b/engines/teenagent/teenagent.cpp index f06de6f803..0b48a18b26 100644 --- a/engines/teenagent/teenagent.cpp +++ b/engines/teenagent/teenagent.cpp @@ -21,6 +21,7 @@ #include "common/config-manager.h" #include "common/debug.h" +#include "common/debug-channels.h" #include "common/events.h" #include "common/savefile.h" #include "common/system.h" @@ -39,26 +40,56 @@ #include "graphics/thumbnail.h" #include "teenagent/console.h" +#include "teenagent/dialog.h" +#include "teenagent/inventory.h" #include "teenagent/music.h" #include "teenagent/objects.h" #include "teenagent/pack.h" +#include "teenagent/resources.h" #include "teenagent/scene.h" #include "teenagent/teenagent.h" namespace TeenAgent { TeenAgentEngine::TeenAgentEngine(OSystem *system, const ADGameDescription *gd) - : Engine(system), action(kActionNone), _gameDescription(gd), - _rnd("teenagent") { - music = new MusicPlayer(); + : Engine(system), _action(kActionNone), _gameDescription(gd), _rnd("teenagent") { + DebugMan.addDebugChannel(kDebugActor, "Actor", "Enable Actor Debug"); + DebugMan.addDebugChannel(kDebugAnimation, "Animation", "Enable Animation Debug"); + DebugMan.addDebugChannel(kDebugCallbacks, "Callbacks", "Enable Callbacks Debug"); + DebugMan.addDebugChannel(kDebugDialog, "Dialog", "Enable Dialog Debug"); + DebugMan.addDebugChannel(kDebugFont, "Font", "Enable Font Debug"); + DebugMan.addDebugChannel(kDebugInventory, "Inventory", "Enable Inventory Debug"); + DebugMan.addDebugChannel(kDebugMusic, "Music", "Enable Music Debug"); + DebugMan.addDebugChannel(kDebugObject, "Object", "Enable Object Debug"); + DebugMan.addDebugChannel(kDebugPack, "Pack", "Enable Pack Debug"); + DebugMan.addDebugChannel(kDebugScene, "Scene", "Enable Scene Debug"); + DebugMan.addDebugChannel(kDebugSurface, "Surface", "Enable Surface Debug"); + + music = new MusicPlayer(this); + dialog = new Dialog(this); + res = new Resources(); console = 0; } TeenAgentEngine::~TeenAgentEngine() { + delete dialog; + dialog = 0; + delete scene; + scene = 0; + delete inventory; + inventory = 0; delete music; + music = 0; + _mixer->stopAll(); + _useHotspots.clear(); + delete res; + res = 0; + + CursorMan.popCursor(); delete console; + DebugMan.clearAllDebugChannels(); } bool TeenAgentEngine::trySelectedObject() { @@ -66,66 +97,64 @@ bool TeenAgentEngine::trySelectedObject() { if (inv == NULL) return false; - Resources *res = Resources::instance(); - debug(0, "checking active object %u on %u", inv->id, dst_object->id); + debugC(0, kDebugObject, "checking active object %u on %u", inv->id, _dstObject->id); //mouse time challenge hack: - if ((res->dseg.get_byte(0) == 1 && inv->id == 49 && dst_object->id == 5) || - (res->dseg.get_byte(0) == 2 && inv->id == 29 && dst_object->id == 5)) { + if ((res->dseg.get_byte(dsAddr_timedCallbackState) == 1 && inv->id == kInvItemRock && _dstObject->id == 5) || + (res->dseg.get_byte(dsAddr_timedCallbackState) == 2 && inv->id == kInvItemSuperGlue && _dstObject->id == 5)) { //putting rock into hole or superglue on rock - processCallback(0x8d57); + fnPutRockInHole(); return true; } - const Common::Array<UseHotspot> &hotspots = use_hotspots[scene->getId() - 1]; + const Common::Array<UseHotspot> &hotspots = _useHotspots[scene->getId() - 1]; for (uint i = 0; i < hotspots.size(); ++i) { const UseHotspot &spot = hotspots[i]; - if (spot.inventory_id == inv->id && dst_object->id == spot.object_id) { - debug(0, "use object on hotspot!"); + if (spot.inventoryId == inv->id && _dstObject->id == spot.objectId) { + debugC(0, kDebugObject, "use object on hotspot!"); spot.dump(); - if (spot.actor_x != 0xffff && spot.actor_y != 0xffff) - moveTo(spot.actor_x, spot.actor_y, spot.orientation); + if (spot.actorX != 0xffff && spot.actorY != 0xffff) + moveTo(spot.actorX, spot.actorY, spot.orientation); if (!processCallback(spot.callback)) - debug(0, "fixme! display proper description"); + debugC(0, kDebugObject, "FIXME: display proper description"); inventory->resetSelectedObject(); return true; } } - //error + // error inventory->resetSelectedObject(); - displayMessage(0x3457); + displayMessage(dsAddr_objErrorMsg); // "That's no good" return true; } void TeenAgentEngine::processObject() { - if (dst_object == NULL) + if (_dstObject == NULL) return; - Resources *res = Resources::instance(); - switch (action) { + switch (_action) { case kActionExamine: { if (trySelectedObject()) break; - byte *dcall = res->dseg.ptr(0xb5ce); + byte *dcall = res->dseg.ptr(dsAddr_objExamineCallbackTablePtr); dcall = res->dseg.ptr(READ_LE_UINT16(dcall + scene->getId() * 2 - 2)); - dcall += 2 * dst_object->id - 2; + dcall += 2 * _dstObject->id - 2; uint16 callback = READ_LE_UINT16(dcall); if (callback == 0 || !processCallback(callback)) - displayMessage(dst_object->description); + displayMessage(_dstObject->description); } break; case kActionUse: { if (trySelectedObject()) break; - byte *dcall = res->dseg.ptr(0xb89c); + byte *dcall = res->dseg.ptr(dsAddr_objUseCallbackTablePtr); dcall = res->dseg.ptr(READ_LE_UINT16(dcall + scene->getId() * 2 - 2)); - dcall += 2 * dst_object->id - 2; + dcall += 2 * _dstObject->id - 2; uint16 callback = READ_LE_UINT16(dcall); if (!processCallback(callback)) - displayMessage(dst_object->description); + displayMessage(_dstObject->description); } break; @@ -134,20 +163,19 @@ void TeenAgentEngine::processObject() { } } - void TeenAgentEngine::use(Object *object) { if (object == NULL || scene->eventRunning()) return; - dst_object = object; + _dstObject = object; object->rect.dump(); - object->actor_rect.dump(); + object->actorRect.dump(); - action = kActionUse; - if (object->actor_rect.valid()) - scene->moveTo(Common::Point(object->actor_rect.right, object->actor_rect.bottom), object->actor_orientation); - else if (object->actor_orientation > 0) - scene->setOrientation(object->actor_orientation); + _action = kActionUse; + if (object->actorRect.valid()) + scene->moveTo(Common::Point(object->actorRect.right, object->actorRect.bottom), object->actorOrientation); + else if (object->actorOrientation > 0) + scene->setOrientation(object->actorOrientation); } void TeenAgentEngine::examine(const Common::Point &point, Object *object) { @@ -155,53 +183,39 @@ void TeenAgentEngine::examine(const Common::Point &point, Object *object) { return; if (object != NULL) { - Common::Point dst = object->actor_rect.center(); - debug(0, "click %d, %d, object %d, %d", point.x, point.y, dst.x, dst.y); - action = kActionExamine; - if (object->actor_rect.valid()) - scene->moveTo(dst, object->actor_orientation, true); //validate examine message. Original engine does not let you into walkboxes - dst_object = object; - } else if (!scene_busy) { - //do not reset anything while scene is busy, but allow interrupts while walking. - debug(0, "click %d, %d", point.x, point.y); - action = kActionNone; + Common::Point dst = object->actorRect.center(); + debugC(0, kDebugObject, "click %d, %d, object %d, %d", point.x, point.y, dst.x, dst.y); + _action = kActionExamine; + if (object->actorRect.valid()) + scene->moveTo(dst, object->actorOrientation, true); // validate examine message. Original engine does not let you into walkboxes + _dstObject = object; + } else if (!_sceneBusy) { + // do not reset anything while scene is busy, but allow interrupts while walking. + debugC(0, kDebugObject, "click %d, %d", point.x, point.y); + _action = kActionNone; scene->moveTo(point, 0, true); - dst_object = NULL; + _dstObject = NULL; } } void TeenAgentEngine::init() { - _mark_delay = 80; - _game_delay = 110; + _markDelay = 80; + _gameDelay = 110; - Resources *res = Resources::instance(); - use_hotspots.resize(42); - byte *scene_hotspots = res->dseg.ptr(0xbb87); + _useHotspots.resize(42); + byte *sceneHotspots = res->dseg.ptr(dsAddr_sceneHotspotsPtr); for (byte i = 0; i < 42; ++i) { - Common::Array<UseHotspot> & hotspots = use_hotspots[i]; - byte *hotspots_ptr = res->dseg.ptr(READ_LE_UINT16(scene_hotspots + i * 2)); - while (*hotspots_ptr) { + Common::Array<UseHotspot> & hotspots = _useHotspots[i]; + byte *hotspotsPtr = res->dseg.ptr(READ_LE_UINT16(sceneHotspots + i * 2)); + while (*hotspotsPtr) { UseHotspot h; - h.load(hotspots_ptr); - hotspots_ptr += 9; + h.load(hotspotsPtr); + hotspotsPtr += 9; hotspots.push_back(h); } } } -void TeenAgentEngine::deinit() { - _mixer->stopAll(); - delete scene; - scene = NULL; - delete inventory; - inventory = NULL; - //delete music; - //music = NULL; - use_hotspots.clear(); - Resources::instance()->deinit(); - CursorMan.popCursor(); -} - Common::Error TeenAgentEngine::loadGameState(int slot) { debug(0, "loading from slot %d", slot); Common::ScopedPtr<Common::InSaveFile> in(_saveFileMan->openForLoading(Common::String::format("teenagent.%02d", slot))); @@ -211,22 +225,19 @@ Common::Error TeenAgentEngine::loadGameState(int slot) { if (!in) return Common::kReadPermissionDenied; - Resources *res = Resources::instance(); - - const uint dataSize = 0x777a; - assert(res->dseg.size() >= 0x6478 + dataSize); + assert(res->dseg.size() >= dsAddr_saveState + saveStateSize); - char *data = (char *)malloc(dataSize); + char *data = (char *)malloc(saveStateSize); if (!data) error("[TeenAgentEngine::loadGameState] Cannot allocate buffer"); in->seek(0); - if (in->read(data, dataSize) != dataSize) { + if (in->read(data, saveStateSize) != saveStateSize) { free(data); return Common::kReadingFailed; } - memcpy(res->dseg.ptr(0x6478), data, dataSize); + memcpy(res->dseg.ptr(dsAddr_saveState), data, saveStateSize); free(data); @@ -234,10 +245,10 @@ Common::Error TeenAgentEngine::loadGameState(int slot) { inventory->activate(false); inventory->reload(); - setMusic(Resources::instance()->dseg.get_byte(0xDB90)); + setMusic(res->dseg.get_byte(dsAddr_currentMusic)); - int id = res->dseg.get_byte(0xB4F3); - uint16 x = res->dseg.get_word(0x64AF), y = res->dseg.get_word(0x64B1); + int id = res->dseg.get_byte(dsAddr_currentScene); + uint16 x = res->dseg.get_word(dsAddr_egoX), y = res->dseg.get_word(dsAddr_egoY); scene->loadObjectData(); scene->init(id, Common::Point(x, y)); scene->setPalette(4); @@ -251,15 +262,15 @@ Common::Error TeenAgentEngine::saveGameState(int slot, const Common::String &des if (!out) return Common::kWritingFailed; - Resources *res = Resources::instance(); - res->dseg.set_byte(0xB4F3, scene->getId()); + res->dseg.set_byte(dsAddr_currentScene, scene->getId()); Common::Point pos = scene->getPosition(); - res->dseg.set_word(0x64AF, pos.x); - res->dseg.set_word(0x64B1, pos.y); + res->dseg.set_word(dsAddr_egoX, pos.x); + res->dseg.set_word(dsAddr_egoY, pos.y); - assert(res->dseg.size() >= 0x6478 + 0x777a); - strncpy((char *)res->dseg.ptr(0x6478), desc.c_str(), 0x16); - out->write(res->dseg.ptr(0x6478), 0x777a); + assert(res->dseg.size() >= dsAddr_saveState + saveStateSize); + // FIXME: Description string is 24 bytes and null based on detection.cpp code, not 22? + strncpy((char *)res->dseg.ptr(dsAddr_saveState), desc.c_str(), 22); + out->write(res->dseg.ptr(dsAddr_saveState), saveStateSize); if (!Graphics::saveThumbnail(*out)) warning("saveThumbnail failed"); @@ -267,7 +278,6 @@ Common::Error TeenAgentEngine::saveGameState(int slot, const Common::String &des return Common::kNoError; } - int TeenAgentEngine::skipEvents() const { Common::EventManager *_event = _system->getEventManager(); Common::Event event; @@ -295,7 +305,7 @@ bool TeenAgentEngine::showCDLogo() { if (!cdlogo.exists("cdlogo.res") || !cdlogo.open("cdlogo.res")) return true; - const uint bgSize = 0xfa00; + const uint bgSize = kScreenWidth * kScreenHeight; const uint paletteSize = 3 * 256; byte *bg = (byte *)malloc(bgSize); @@ -314,8 +324,8 @@ bool TeenAgentEngine::showCDLogo() { for (uint c = 0; c < paletteSize; ++c) palette[c] *= 4; - _system->getPaletteManager()->setPalette(palette, 0, 0x100); - _system->copyRectToScreen(bg, 320, 0, 0, 320, 200); + _system->getPaletteManager()->setPalette(palette, 0, 256); + _system->copyRectToScreen(bg, kScreenWidth, 0, 0, kScreenWidth, kScreenHeight); _system->updateScreen(); free(bg); @@ -341,7 +351,7 @@ bool TeenAgentEngine::showLogo() { if (!frame) return true; - const uint bgSize = 0xfa00; + const uint bgSize = kScreenWidth * kScreenHeight; const uint paletteSize = 3 * 256; byte *bg = (byte *)malloc(bgSize); @@ -360,7 +370,7 @@ bool TeenAgentEngine::showLogo() { for (uint c = 0; c < paletteSize; ++c) palette[c] *= 4; - _system->getPaletteManager()->setPalette(palette, 0, 0x100); + _system->getPaletteManager()->setPalette(palette, 0, 256); free(palette); @@ -374,7 +384,7 @@ bool TeenAgentEngine::showLogo() { return r > 0 ? true : false; } } - _system->copyRectToScreen(bg, 320, 0, 0, 320, 200); + _system->copyRectToScreen(bg, kScreenWidth, 0, 0, kScreenWidth, kScreenHeight); frame.reset(logo.getStream(i)); if (!frame) { @@ -419,23 +429,23 @@ bool TeenAgentEngine::showMetropolis() { palette[c] *= 4; } - _system->getPaletteManager()->setPalette(palette, 0, 0x100); + _system->getPaletteManager()->setPalette(palette, 0, 256); free(palette); const uint varia6Size = 21760; const uint varia9Size = 18302; - byte *varia_6 = (byte *)malloc(varia6Size); - byte *varia_9 = (byte *)malloc(varia9Size); - if (!varia_6 || !varia_9) { - free(varia_6); - free(varia_9); + byte *varia6Data = (byte *)malloc(varia6Size); + byte *varia9Data = (byte *)malloc(varia9Size); + if (!varia6Data || !varia9Data) { + free(varia6Data); + free(varia9Data); error("[TeenAgentEngine::showMetropolis] Cannot allocate buffer"); } - varia.read(6, varia_6, varia6Size); - varia.read(9, varia_9, varia9Size); + varia.read(6, varia6Data, varia6Size); + varia.read(9, varia9Data, varia9Size); const uint colorsSize = 56 * 160 * 2; byte *colors = (byte *)malloc(colorsSize); @@ -449,8 +459,8 @@ bool TeenAgentEngine::showMetropolis() { { int r = skipEvents(); if (r != 0) { - free(varia_6); - free(varia_9); + free(varia6Data); + free(varia9Data); free(colors); return r > 0 ? true : false; } @@ -458,7 +468,7 @@ bool TeenAgentEngine::showMetropolis() { Graphics::Surface *surface = _system->lockScreen(); if (logo_y > 0) { - surface->fillRect(Common::Rect(0, 0, 320, logo_y), 0); + surface->fillRect(Common::Rect(0, 0, kScreenWidth, logo_y), 0); } { @@ -485,7 +495,7 @@ bool TeenAgentEngine::showMetropolis() { } byte *dst = (byte *)surface->getBasePtr(0, 131); - byte *src = varia_6; + byte *src = varia6Data; for (uint y = 0; y < 68; ++y) { for (uint x = 0; x < 320; ++x) { if (*src++ == 1) { @@ -497,7 +507,7 @@ bool TeenAgentEngine::showMetropolis() { _system->unlockScreen(); _system->copyRectToScreen( - varia_9 + (logo_y < 0 ? -logo_y * 320 : 0), 320, + varia9Data + (logo_y < 0 ? -logo_y * 320 : 0), 320, 0, logo_y >= 0 ? logo_y : 0, 320, logo_y >= 0 ? 57 : 57 + logo_y); @@ -509,39 +519,37 @@ bool TeenAgentEngine::showMetropolis() { _system->delayMillis(100); } - free(varia_6); - free(varia_9); + free(varia6Data); + free(varia9Data); free(colors); return true; } Common::Error TeenAgentEngine::run() { - Resources *res = Resources::instance(); if (!res->loadArchives(_gameDescription)) return Common::kUnknownError; Common::EventManager *_event = _system->getEventManager(); - initGraphics(320, 200, false); + initGraphics(kScreenWidth, kScreenHeight, false); console = new Console(this); - scene = new Scene(this, _system); + scene = new Scene(this); inventory = new Inventory(this); init(); - CursorMan.pushCursor(res->dseg.ptr(0x00da), 8, 12, 0, 0, 1); + CursorMan.pushCursor(res->dseg.ptr(dsAddr_cursor), 8, 12, 0, 0, 1); syncSoundSettings(); - _mixer->playStream(Audio::Mixer::kMusicSoundType, &_musicHandle, music, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, false); setMusic(1); - music->start(); + _mixer->playStream(Audio::Mixer::kMusicSoundType, &_musicHandle, music, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, false); - int load_slot = Common::ConfigManager::instance().getInt("save_slot"); - if (load_slot >= 0) { - loadGameState(load_slot); + int loadSlot = ConfMan.getInt("save_slot"); + if (loadSlot >= 0) { + loadGameState(loadSlot); } else { if (!showCDLogo()) return Common::kNoError; @@ -550,32 +558,30 @@ Common::Error TeenAgentEngine::run() { if (!showMetropolis()) return Common::kNoError; scene->intro = true; - scene_busy = true; - processCallback(0x24c); + _sceneBusy = true; + fnIntro(); } CursorMan.showMouse(true); - uint32 game_timer = 0; - uint32 mark_timer = 0; + uint32 gameTimer = 0; + uint32 markTimer = 0; Common::Event event; Common::Point mouse; uint32 timer = _system->getMillis(); do { - Object *current_object = scene->findObject(mouse); + Object *currentObject = scene->findObject(mouse); while (_event->pollEvent(event)) { - if (event.type == Common::EVENT_RTL) { - deinit(); + if (event.type == Common::EVENT_RTL) return Common::kNoError; - } - if ((!scene_busy && inventory->processEvent(event)) || scene->processEvent(event)) + if ((!_sceneBusy && inventory->processEvent(event)) || scene->processEvent(event)) continue; - //debug(0, "event"); + debug(5, "event"); switch (event.type) { case Common::EVENT_KEYDOWN: if ((event.kbd.hasFlags(Common::KBD_CTRL) && event.kbd.keycode == Common::KEYCODE_d) || @@ -584,33 +590,33 @@ Common::Error TeenAgentEngine::run() { } else if (event.kbd.hasFlags(0) && event.kbd.keycode == Common::KEYCODE_F5) { openMainMenuDialog(); } if (event.kbd.hasFlags(Common::KBD_CTRL) && event.kbd.keycode == Common::KEYCODE_f) { - _mark_delay = _mark_delay == 80 ? 40 : 80; - debug(0, "mark_delay = %u", _mark_delay); + _markDelay = _markDelay == 80 ? 40 : 80; + debug(5, "markDelay = %u", _markDelay); } break; case Common::EVENT_LBUTTONDOWN: if (scene->getId() < 0) break; - examine(event.mouse, current_object); + examine(event.mouse, currentObject); break; case Common::EVENT_RBUTTONDOWN: - //if (current_object) - // debug(0, "%d, %s", current_object->id, current_object->name.c_str()); + if (currentObject) + debugC(0, kDebugObject, "%d, %s", currentObject->id, currentObject->name.c_str()); if (scene->getId() < 0) break; - if (current_object == NULL) + if (currentObject == NULL) break; - if (res->dseg.get_byte(0) == 3 && current_object->id == 1) { - processCallback(0x5189); //boo! + if (res->dseg.get_byte(dsAddr_timedCallbackState) == 3 && currentObject->id == 1) { + fnGuardDrinking(); break; } - if (res->dseg.get_byte(0) == 4 && current_object->id == 5) { - processCallback(0x99e0); //getting an anchor + if (res->dseg.get_byte(dsAddr_timedCallbackState) == 4 && currentObject->id == 5) { + fnGotAnchor(); break; } - use(current_object); + use(currentObject); break; case Common::EVENT_MOUSEMOVE: mouse = event.mouse; @@ -623,60 +629,60 @@ Common::Error TeenAgentEngine::run() { //game delays: slow 16, normal 11, fast 5, crazy 1 //mark delays: 4 * (3 - hero_speed), normal == 1 //game delays in 1/100th of seconds - uint32 new_timer = _system->getMillis(); - uint32 delta = new_timer - timer; - timer = new_timer; + uint32 newTimer = _system->getMillis(); + uint32 delta = newTimer - timer; + timer = newTimer; - bool tick_game = game_timer <= delta; - if (tick_game) - game_timer = _game_delay - ((delta - game_timer) % _game_delay); + bool tickGame = gameTimer <= delta; + if (tickGame) + gameTimer = _gameDelay - ((delta - gameTimer) % _gameDelay); else - game_timer -= delta; + gameTimer -= delta; - bool tick_mark = mark_timer <= delta; - if (tick_mark) - mark_timer = _mark_delay - ((delta - mark_timer) % _mark_delay); + bool tickMark = markTimer <= delta; + if (tickMark) + markTimer = _markDelay - ((delta - markTimer) % _markDelay); else - mark_timer -= delta; + markTimer -= delta; - if (tick_game || tick_mark) { - bool b = scene->render(tick_game, tick_mark, delta); - if (!inventory->active() && !b && action != kActionNone) { + if (tickGame || tickMark) { + bool b = scene->render(tickGame, tickMark, delta); + if (!inventory->active() && !b && _action != kActionNone) { processObject(); - action = kActionNone; - dst_object = NULL; + _action = kActionNone; + _dstObject = NULL; } - scene_busy = b; + _sceneBusy = b; } - _system->showMouse(scene->getMessage().empty() && !scene_busy); + _system->showMouse(scene->getMessage().empty() && !_sceneBusy); - bool busy = inventory->active() || scene_busy; + bool busy = inventory->active() || _sceneBusy; Graphics::Surface *surface = _system->lockScreen(); if (!busy) { - InventoryObject *selected_object = inventory->selectedObject(); - if (current_object || selected_object) { + InventoryObject *selectedObject = inventory->selectedObject(); + if (currentObject || selectedObject) { Common::String name; - if (selected_object) { - name += selected_object->name; + if (selectedObject) { + name += selectedObject->name; name += " & "; } - if (current_object) - name += current_object->name; + if (currentObject) + name += currentObject->name; - uint w = res->font7.render(NULL, 0, 0, name, 0xd1); - res->font7.render(surface, (320 - w) / 2, 180, name, 0xd1, true); + uint w = res->font7.render(NULL, 0, 0, name, textColorMark); + res->font7.render(surface, (kScreenWidth - w) / 2, 180, name, textColorMark, true); #if 0 - if (current_object) { - current_object->rect.render(surface, 0x80); - current_object->actor_rect.render(surface, 0x81); + if (currentObject) { + currentObject->rect.render(surface, 0x80); + currentObject->actorRect.render(surface, 0x81); } #endif } } - inventory->render(surface, tick_game ? 1 : 0); + inventory->render(surface, tickGame ? 1 : 0); _system->unlockScreen(); @@ -684,20 +690,19 @@ Common::Error TeenAgentEngine::run() { console->onFrame(); - uint32 next_tick = MIN(game_timer, mark_timer); - if (next_tick > 0) { - _system->delayMillis(next_tick > 40 ? 40 : next_tick); + uint32 nextTick = MIN(gameTimer, markTimer); + if (nextTick > 0) { + _system->delayMillis(nextTick > 40 ? 40 : nextTick); } } while (!shouldQuit()); - deinit(); return Common::kNoError; } Common::String TeenAgentEngine::parseMessage(uint16 addr) { Common::String message; for ( - const char *str = (const char *)Resources::instance()->dseg.ptr(addr); + const char *str = (const char *)res->dseg.ptr(addr); str[0] != 0 || str[1] != 0; ++str) { char c = str[0]; @@ -709,12 +714,12 @@ Common::String TeenAgentEngine::parseMessage(uint16 addr) { return message; } -void TeenAgentEngine::displayMessage(const Common::String &str, byte color, uint16 position) { +void TeenAgentEngine::displayMessage(const Common::String &str, byte color, uint16 x, uint16 y) { if (str.empty()) { return; } - if (color == 0xd1) { //mark's + if (color == textColorMark) { // mark's SceneEvent e(SceneEvent::kPlayAnimation); e.animation = 0; e.slot = 0x80; @@ -726,8 +731,8 @@ void TeenAgentEngine::displayMessage(const Common::String &str, byte color, uint event.message = str; event.color = color; event.slot = 0; - event.dst.x = position % 320; - event.dst.y = position / 320; + event.dst.x = x; + event.dst.y = y; scene->push(event); } @@ -739,46 +744,45 @@ void TeenAgentEngine::displayMessage(const Common::String &str, byte color, uint } } -void TeenAgentEngine::displayMessage(uint16 addr, byte color, uint16 position) { - displayMessage(parseMessage(addr), color, position); +void TeenAgentEngine::displayMessage(uint16 addr, byte color, uint16 x, uint16 y) { + displayMessage(parseMessage(addr), color, x, y); } -void TeenAgentEngine::displayAsyncMessage(uint16 addr, uint16 position, uint16 first_frame, uint16 last_frame, byte color) { +void TeenAgentEngine::displayAsyncMessage(uint16 addr, uint16 x, uint16 y, uint16 firstFrame, uint16 lastFrame, byte color) { SceneEvent event(SceneEvent::kMessage); event.message = parseMessage(addr); event.slot = 0; event.color = color; - event.dst.x = position % 320; - event.dst.y = position / 320; - event.first_frame = first_frame; - event.last_frame = last_frame; + event.dst.x = x; + event.dst.y = y; + event.firstFrame = firstFrame; + event.lastFrame = lastFrame; scene->push(event); } -void TeenAgentEngine::displayAsyncMessageInSlot(uint16 addr, byte slot, uint16 first_frame, uint16 last_frame, byte color) { +void TeenAgentEngine::displayAsyncMessageInSlot(uint16 addr, byte slot, uint16 firstFrame, uint16 lastFrame, byte color) { SceneEvent event(SceneEvent::kMessage); event.message = parseMessage(addr); event.slot = slot + 1; event.color = color; - event.first_frame = first_frame; - event.last_frame = last_frame; + event.firstFrame = firstFrame; + event.lastFrame = lastFrame; scene->push(event); } - void TeenAgentEngine::displayCredits(uint16 addr, uint16 timer) { SceneEvent event(SceneEvent::kCreditsMessage); - const byte *src = Resources::instance()->dseg.ptr(addr); + const byte *src = res->dseg.ptr(addr); event.orientation = *src++; event.color = *src++; event.lan = 8; event.dst.y = *src; while (true) { - ++src; //skip y position + ++src; // skip y position Common::String line((const char *)src); event.message += line; src += line.size() + 1; @@ -786,33 +790,33 @@ void TeenAgentEngine::displayCredits(uint16 addr, uint16 timer) { break; event.message += "\n"; } - int w = Resources::instance()->font8.render(NULL, 0, 0, event.message, 0xd1); - event.dst.x = (320 - w) / 2; + int w = res->font8.render(NULL, 0, 0, event.message, textColorCredits); + event.dst.x = (kScreenWidth - w) / 2; event.timer = timer; scene->push(event); } void TeenAgentEngine::displayCredits() { SceneEvent event(SceneEvent::kCredits); - event.message = parseMessage(0xe488); - event.dst.y = 200; + event.message = parseMessage(dsAddr_finalCredits7); + event.dst.y = kScreenHeight; int lines = 1; for (uint i = 0; i < event.message.size(); ++i) if (event.message[i] == '\n') ++lines; - event.dst.x = (320 - Resources::instance()->font7.render(NULL, 0, 0, event.message, 0xd1)) / 2; + event.dst.x = (kScreenWidth - res->font7.render(NULL, 0, 0, event.message, textColorCredits)) / 2; event.timer = 11 * lines - event.dst.y + 22; - //debug(0, "credits = %s", event.message.c_str()); + debug(2, "credits = %s", event.message.c_str()); scene->push(event); } -void TeenAgentEngine::displayCutsceneMessage(uint16 addr, uint16 position) { +void TeenAgentEngine::displayCutsceneMessage(uint16 addr, uint16 x, uint16 y) { SceneEvent event(SceneEvent::kCreditsMessage); event.message = parseMessage(addr); - event.dst.x = position % 320; - event.dst.y = position / 320; + event.dst.x = x; + event.dst.y = y; event.lan = 7; scene->push(event); @@ -823,7 +827,7 @@ void TeenAgentEngine::moveTo(const Common::Point &dst, byte o, bool warp) { } void TeenAgentEngine::moveTo(Object *obj) { - moveTo(obj->actor_rect.right, obj->actor_rect.bottom, obj->actor_orientation); + moveTo(obj->actorRect.right, obj->actorRect.bottom, obj->actorOrientation); } void TeenAgentEngine::moveTo(uint16 x, uint16 y, byte o, bool warp) { @@ -866,7 +870,6 @@ void TeenAgentEngine::playActorAnimation(uint16 id, bool async, bool ignore) { waitAnimation(); } - void TeenAgentEngine::loadScene(byte id, const Common::Point &pos, byte o) { loadScene(id, pos.x, pos.y, o); } @@ -891,21 +894,21 @@ void TeenAgentEngine::enableOn(bool enable) { scene->push(event); } -void TeenAgentEngine::setOns(byte id, byte value, byte scene_id) { +void TeenAgentEngine::setOns(byte id, byte value, byte sceneId) { SceneEvent event(SceneEvent::kSetOn); event.ons = id + 1; event.color = value; - event.scene = scene_id; + event.scene = sceneId; scene->push(event); } -void TeenAgentEngine::setLan(byte id, byte value, byte scene_id) { +void TeenAgentEngine::setLan(byte id, byte value, byte sceneId) { if (id == 0) error("setting lan 0 is invalid"); SceneEvent event(SceneEvent::kSetLan); event.lan = id; event.color = value; - event.scene = scene_id; + event.scene = sceneId; scene->push(event); } @@ -926,35 +929,34 @@ void TeenAgentEngine::reloadLan() { scene->push(event); } - void TeenAgentEngine::playMusic(byte id) { SceneEvent event(SceneEvent::kPlayMusic); event.music = id; scene->push(event); } -void TeenAgentEngine::playSound(byte id, byte skip_frames) { - if (skip_frames > 0) - --skip_frames; +void TeenAgentEngine::playSound(byte id, byte skipFrames) { + if (skipFrames > 0) + --skipFrames; SceneEvent event(SceneEvent::kPlaySound); event.sound = id; - event.color = skip_frames; + event.color = skipFrames; scene->push(event); } -void TeenAgentEngine::enableObject(byte id, byte scene_id) { +void TeenAgentEngine::enableObject(byte id, byte sceneId) { SceneEvent event(SceneEvent::kEnableObject); event.object = id + 1; event.color = 1; - event.scene = scene_id; + event.scene = sceneId; scene->push(event); } -void TeenAgentEngine::disableObject(byte id, byte scene_id) { +void TeenAgentEngine::disableObject(byte id, byte sceneId) { SceneEvent event(SceneEvent::kEnableObject); event.object = id + 1; event.color = 0; - event.scene = scene_id; + event.scene = sceneId; scene->push(event); } @@ -1016,7 +1018,6 @@ void TeenAgentEngine::wait(uint16 frames) { } void TeenAgentEngine::playSoundNow(byte id) { - Resources *res = Resources::instance(); uint size = res->sam_sam.getSize(id); if (size == 0) { warning("skipping invalid sound %u", id); @@ -1025,28 +1026,26 @@ void TeenAgentEngine::playSoundNow(byte id) { byte *data = (byte *)malloc(size); res->sam_sam.read(id, data, size); - //debug(0, "playing %u samples...", size); + debug(3, "playing %u samples...", size); Audio::AudioStream *stream = Audio::makeRawStream(data, size, 11025, 0); - _mixer->playStream(Audio::Mixer::kSFXSoundType, &_soundHandle, stream); //dispose is YES by default + _mixer->playStream(Audio::Mixer::kSFXSoundType, &_soundHandle, stream); // dispose is YES by default } - void TeenAgentEngine::setMusic(byte id) { - debug(0, "starting music %u", id); - Resources *res = Resources::instance(); + debugC(0, kDebugMusic, "starting music %u", id); - if (id != 1) //intro music - *res->dseg.ptr(0xDB90) = id; + if (id != 1) // intro music + res->dseg.set_byte(dsAddr_currentMusic, id); if (_gameDescription->flags & ADGF_CD) { byte track2cd[] = {7, 2, 0, 9, 3, 6, 8, 10, 4, 5, 11}; if (id == 0 || id > 11 || track2cd[id - 1] == 0) { - debug(0, "no cd music for id %u", id); + debugC(0, kDebugMusic, "no cd music for id %u", id); return; } byte track = track2cd[id - 1]; - debug(0, "playing cd track %u", track); + debugC(0, kDebugMusic, "playing cd track %u", track); _system->getAudioCDManager()->play(track, -1, 0, 0); } else if (music->load(id)) music->start(); diff --git a/engines/teenagent/teenagent.h b/engines/teenagent/teenagent.h index 737f07ba85..d6a2c0d3c6 100644 --- a/engines/teenagent/teenagent.h +++ b/engines/teenagent/teenagent.h @@ -23,12 +23,15 @@ #define TEENAGENT_ENGINE_H #include "engines/engine.h" -#include "teenagent/pack.h" -#include "teenagent/resources.h" -#include "teenagent/inventory.h" + #include "audio/audiostream.h" #include "audio/mixer.h" + #include "common/random.h" +#include "common/rect.h" +#include "common/array.h" + +#include "teenagent/dialog.h" struct ADGameDescription; @@ -43,14 +46,34 @@ struct ADGameDescription; namespace TeenAgent { struct Object; +struct UseHotspot; class Scene; class MusicPlayer; +class Dialog; class Console; +class Resources; +class Inventory; + +// Engine Debug Flags +enum { + kDebugActor = (1 << 0), + kDebugAnimation = (1 << 1), + kDebugCallbacks = (1 << 2), + kDebugDialog = (1 << 3), + kDebugFont = (1 << 4), + kDebugInventory = (1 << 5), + kDebugMusic = (1 << 6), + kDebugObject = (1 << 7), + kDebugPack = (1 << 8), + kDebugScene = (1 << 9), + kDebugSurface = (1 << 10) +}; + +const uint16 kScreenWidth = 320; +const uint16 kScreenHeight = 200; class TeenAgentEngine : public Engine { public: - enum Action { kActionNone, kActionExamine, kActionUse }; - TeenAgentEngine(OSystem *system, const ADGameDescription *gd); ~TeenAgentEngine(); @@ -58,15 +81,16 @@ public: virtual Common::Error loadGameState(int slot); virtual Common::Error saveGameState(int slot, const Common::String &desc); virtual bool canLoadGameStateCurrently() { return true; } - virtual bool canSaveGameStateCurrently() { return !scene_busy; } + virtual bool canSaveGameStateCurrently() { return !_sceneBusy; } virtual bool hasFeature(EngineFeature f) const; void init(); - void deinit(); + + enum Action { kActionNone, kActionExamine, kActionUse }; void examine(const Common::Point &point, Object *object); void use(Object *object); - inline void cancel() { action = kActionNone; } + inline void cancel() { _action = kActionNone; } bool processCallback(uint16 addr); inline Scene *getScene() { return scene; } @@ -76,15 +100,15 @@ public: bool showMetropolis(); int skipEvents() const; - static Common::String parseMessage(uint16 addr); + Common::String parseMessage(uint16 addr); //event driven: - void displayMessage(uint16 addr, byte color = 0xd1, uint16 position = 0); - void displayMessage(const Common::String &str, byte color = 0xd1, uint16 position = 0); - void displayAsyncMessage(uint16 addr, uint16 position, uint16 first_frame, uint16 last_frame, byte color = 0xd1); - void displayAsyncMessageInSlot(uint16 addr, byte slot, uint16 first_frame, uint16 last_frame, byte color = 0xd1); + void displayMessage(uint16 addr, byte color = textColorMark, uint16 x = 0, uint16 y = 0); + void displayMessage(const Common::String &str, byte color = textColorMark, uint16 x = 0, uint16 y = 0); + void displayAsyncMessage(uint16 addr, uint16 x, uint16 y, uint16 firstFrame, uint16 lastFrame, byte color = textColorMark); + void displayAsyncMessageInSlot(uint16 addr, byte slot, uint16 firstFrame, uint16 lastFrame, byte color = textColorMark); void displayCredits(uint16 addr, uint16 timer = 0); - void displayCutsceneMessage(uint16 addr, uint16 position); + void displayCutsceneMessage(uint16 addr, uint16 x, uint16 y); void moveTo(const Common::Point &dst, byte o, bool warp = false); void moveTo(uint16 x, uint16 y, byte o, bool warp = false); void moveTo(Object *obj); @@ -94,18 +118,18 @@ public: void loadScene(byte id, const Common::Point &pos, byte o = 0); void loadScene(byte id, uint16 x, uint16 y, byte o = 0); void enableOn(bool enable = true); - void setOns(byte id, byte value, byte scene_id = 0); - void setLan(byte id, byte value, byte scene_id = 0); + void setOns(byte id, byte value, byte sceneId = 0); + void setLan(byte id, byte value, byte sceneId = 0); void setFlag(uint16 addr, byte value); byte getFlag(uint16 addr); void reloadLan(); void rejectMessage(); void playMusic(byte id); //schedules play - void playSound(byte id, byte skip_frames); + void playSound(byte id, byte skipFrames); void playSoundNow(byte id); - void enableObject(byte id, byte scene_id = 0); - void disableObject(byte id, byte scene_id = 0); + void enableObject(byte id, byte sceneId = 0); + void disableObject(byte id, byte sceneId = 0); void hideActor(); void showActor(); void waitAnimation(); @@ -119,9 +143,11 @@ public: Common::RandomSource _rnd; + Resources *res; Scene *scene; Inventory *inventory; MusicPlayer *music; + Dialog *dialog; Console *console; void setMusic(byte id); @@ -130,17 +156,47 @@ private: void processObject(); bool trySelectedObject(); - bool scene_busy; - Action action; - Object *dst_object; + bool _sceneBusy; + Action _action; + Object *_dstObject; Audio::AudioStream *_musicStream; Audio::SoundHandle _musicHandle, _soundHandle; const ADGameDescription *_gameDescription; - uint _mark_delay, _game_delay; - - Common::Array<Common::Array<UseHotspot> > use_hotspots; + uint _markDelay, _gameDelay; + + Common::Array<Common::Array<UseHotspot> > _useHotspots; + + void fnIntro(); + void fnPoleClimbFail(); + void fnGotAnchor(); + void fnGetOutOfLake(); + void fnGuardDrinking(); + void fnEgoDefaultPosition(); + void fnEnterCave(); + void fnEgoScaredBySpider(); + void fnMoveToLadderAndLeaveCellar(); + void fnLeaveCellar(); + void fnPutRockInHole(); + void fnEgoBottomRightTurn(); + bool fnCheckingDrawers(); + void fnDrawerOpenMessage(); + bool fnRobotSafeAlreadyUnlockedCheck(); + void fnRobotSafeUnlockCheck(); + bool fnMansionIntrusionAttempt(); + void fnSecondMansionIntrusion(); + void fnThirdMansionIntrusion(); + void fnFourthMansionIntrusion(); + void fnFifthMansionIntrusion(); + void fnSixthMansionIntrusion(); + void fnTooDark(); + bool fnIsCookGone(); + void fnEgoSuspiciousPosition(); + void fnGivingFlowerToOldLady(); + void fnGiveAnotherFlowerToOldLady(); + void fnGivingFlowerToAnne(); + void fnGiveAnotherFlowerToAnne(); }; } // End of namespace TeenAgent diff --git a/engines/testbed/graphics.cpp b/engines/testbed/graphics.cpp index 082694b728..590e6c6d81 100644 --- a/engines/testbed/graphics.cpp +++ b/engines/testbed/graphics.cpp @@ -1028,7 +1028,7 @@ TestExitStatus GFXtests::paletteRotation() { GFXTestSuite::setCustomColor(255, 0, 0); Testsuite::clearScreen(); - if(Testsuite::handleInteractiveInput("Did you see a rotation in colors of rectangles displayed on screen?", "Yes", "No", kOptionRight)) { + if (Testsuite::handleInteractiveInput("Did you see a rotation in colors of rectangles displayed on screen?", "Yes", "No", kOptionRight)) { return kTestFailed; } @@ -1121,7 +1121,7 @@ TestExitStatus GFXtests::pixelFormats() { g_system->updateScreen(); g_system->delayMillis(500); - if(Testsuite::handleInteractiveInput("Were you able to notice the colored rectangles on the screen for this format?", "Yes", "No", kOptionLeft)) { + if (Testsuite::handleInteractiveInput("Were you able to notice the colored rectangles on the screen for this format?", "Yes", "No", kOptionLeft)) { numPassed++; } else { numFailed++; diff --git a/engines/testbed/testsuite.cpp b/engines/testbed/testsuite.cpp index 655179aa74..39eeca31bd 100644 --- a/engines/testbed/testsuite.cpp +++ b/engines/testbed/testsuite.cpp @@ -289,7 +289,7 @@ void Testsuite::execute() { continue; } - if((*i)->isInteractive && !ConfParams.isSessionInteractive()) { + if ((*i)->isInteractive && !ConfParams.isSessionInteractive()) { logPrintf("Info! Skipping Test: %s, non-interactive environment is selected\n", ((*i)->featureName).c_str()); _numTestsSkipped++; continue; diff --git a/engines/tinsel/actors.cpp b/engines/tinsel/actors.cpp index a784ff5788..531a8e3d12 100644 --- a/engines/tinsel/actors.cpp +++ b/engines/tinsel/actors.cpp @@ -319,8 +319,8 @@ static void ActorRestoredProcess(CORO_PARAM, const void *param) { CORO_BEGIN_CODE(_ctx); _ctx->pic = RestoreInterpretContext(r->pic); - - // The newly added check here specially sets the process to RES_NOT when loading a savegame. + + // The newly added check here specially sets the process to RES_NOT when loading a savegame. // This is needed particularly for the Psychiatrist scene in Discworld 1 - otherwise Rincewind // can't go upstairs without leaving the building and returning. If this patch causes problems // in other scenes, an added check for the hCode == 1174490602 could be added. @@ -406,7 +406,7 @@ void ActorEvent(CORO_PARAM, int ano, TINSEL_EVENT tEvent, bool bWait, int myEsca * @param bRunScript Flag for whether to run actor's script for the scene */ void StartActor(const T1_ACTOR_STRUC *as, bool bRunScript) { - SCNHANDLE hActorId = FROM_LE_32(as->hActorId); + SCNHANDLE hActorId = FROM_32(as->hActorId); // Zero-out many things actorInfo[hActorId - 1].bHidden = false; @@ -418,15 +418,15 @@ void StartActor(const T1_ACTOR_STRUC *as, bool bRunScript) { actorInfo[hActorId - 1].presObj = NULL; // Store current scene's parameters for this actor - actorInfo[hActorId - 1].mtype = FROM_LE_32(as->masking); - actorInfo[hActorId - 1].actorCode = FROM_LE_32(as->hActorCode); + actorInfo[hActorId - 1].mtype = FROM_32(as->masking); + actorInfo[hActorId - 1].actorCode = FROM_32(as->hActorCode); // Run actor's script for this scene if (bRunScript) { if (bActorsOn) actorInfo[hActorId - 1].bAlive = true; - if (actorInfo[hActorId - 1].bAlive && FROM_LE_32(as->hActorCode)) + if (actorInfo[hActorId - 1].bAlive && FROM_32(as->hActorCode)) ActorEvent(hActorId, STARTUP, PLR_NOEVENT); } } @@ -465,11 +465,11 @@ void StartTaggedActors(SCNHANDLE ah, int numActors, bool bRunScript) { assert(as->hActorCode); // Store current scene's parameters for this tagged actor - taggedActors[i].id = FROM_LE_32(as->hActorId); - taggedActors[i].hTagText = FROM_LE_32(as->hTagText); - taggedActors[i].tagPortionV = FROM_LE_32(as->tagPortionV); - taggedActors[i].tagPortionH = FROM_LE_32(as->tagPortionH); - taggedActors[i].hActorCode = FROM_LE_32(as->hActorCode); + taggedActors[i].id = FROM_32(as->hActorId); + taggedActors[i].hTagText = FROM_32(as->hTagText); + taggedActors[i].tagPortionV = FROM_32(as->tagPortionV); + taggedActors[i].tagPortionH = FROM_32(as->tagPortionH); + taggedActors[i].hActorCode = FROM_32(as->hActorCode); // Run actor's script for this scene if (bRunScript) { @@ -1310,9 +1310,9 @@ void SetActorRGB(int ano, COLORREF color) { assert(ano >= 0 && ano <= NumActors); if (ano) - actorInfo[ano - 1].textColor = TO_LE_32(color); + actorInfo[ano - 1].textColor = TO_32(color); else - defaultColor = TO_LE_32(color); + defaultColor = TO_32(color); } /** diff --git a/engines/tinsel/anim.cpp b/engines/tinsel/anim.cpp index 034296ccc7..a1ec02186c 100644 --- a/engines/tinsel/anim.cpp +++ b/engines/tinsel/anim.cpp @@ -44,9 +44,9 @@ SCRIPTSTATE DoNextFrame(ANIM *pAnim) { while (1) { // repeat until a real image debugC(DEBUG_DETAILED, kTinselDebugAnimations, "DoNextFrame %ph index=%d, op=%xh", (byte *)pAnim, pAnim->scriptIndex, - FROM_LE_32(pAni[pAnim->scriptIndex].op)); + FROM_32(pAni[pAnim->scriptIndex].op)); - switch ((int32)FROM_LE_32(pAni[pAnim->scriptIndex].op)) { + switch ((int32)FROM_32(pAni[pAnim->scriptIndex].op)) { case ANI_END: // end of animation script // move to next opcode @@ -61,7 +61,7 @@ SCRIPTSTATE DoNextFrame(ANIM *pAnim) { pAnim->scriptIndex++; // jump to new frame position - pAnim->scriptIndex += (int32)FROM_LE_32(pAni[pAnim->scriptIndex].op); + pAnim->scriptIndex += (int32)FROM_32(pAni[pAnim->scriptIndex].op); // go fetch a real image break; @@ -101,7 +101,7 @@ SCRIPTSTATE DoNextFrame(ANIM *pAnim) { // move to x adjustment operand pAnim->scriptIndex++; - MultiAdjustXY(pAnim->pObject, (int32)FROM_LE_32(pAni[pAnim->scriptIndex].op), 0); + MultiAdjustXY(pAnim->pObject, (int32)FROM_32(pAni[pAnim->scriptIndex].op), 0); // next opcode pAnim->scriptIndex++; @@ -114,7 +114,7 @@ SCRIPTSTATE DoNextFrame(ANIM *pAnim) { // move to y adjustment operand pAnim->scriptIndex++; - MultiAdjustXY(pAnim->pObject, 0, (int32)FROM_LE_32(pAni[pAnim->scriptIndex].op)); + MultiAdjustXY(pAnim->pObject, 0, (int32)FROM_32(pAni[pAnim->scriptIndex].op)); // next opcode pAnim->scriptIndex++; @@ -128,11 +128,11 @@ SCRIPTSTATE DoNextFrame(ANIM *pAnim) { // move to x adjustment operand pAnim->scriptIndex++; - x = (int32)FROM_LE_32(pAni[pAnim->scriptIndex].op); + x = (int32)FROM_32(pAni[pAnim->scriptIndex].op); // move to y adjustment operand pAnim->scriptIndex++; - y = (int32)FROM_LE_32(pAni[pAnim->scriptIndex].op); + y = (int32)FROM_32(pAni[pAnim->scriptIndex].op); MultiAdjustXY(pAnim->pObject, x, y); @@ -189,7 +189,7 @@ SCRIPTSTATE DoNextFrame(ANIM *pAnim) { default: // must be an actual animation frame handle // set objects new animation frame - pAnim->pObject->hShape = FROM_LE_32(pAni[pAnim->scriptIndex].hFrame); + pAnim->pObject->hShape = FROM_32(pAni[pAnim->scriptIndex].hFrame); // re-shape the object MultiReshape(pAnim->pObject); @@ -273,7 +273,7 @@ void SkipFrames(ANIM *pAnim, int numFrames) { while (1) { // repeat until a real image - switch ((int32)FROM_LE_32(pAni[pAnim->scriptIndex].op)) { + switch ((int32)FROM_32(pAni[pAnim->scriptIndex].op)) { case ANI_END: // end of animation script // going off the end is probably a error, but only in Tinsel 1 if (!TinselV2) @@ -286,7 +286,7 @@ void SkipFrames(ANIM *pAnim, int numFrames) { pAnim->scriptIndex++; // jump to new frame position - pAnim->scriptIndex += (int32)FROM_LE_32(pAni[pAnim->scriptIndex].op); + pAnim->scriptIndex += (int32)FROM_32(pAni[pAnim->scriptIndex].op); if (TinselV2) // Done if skip to jump @@ -323,7 +323,7 @@ void SkipFrames(ANIM *pAnim, int numFrames) { // move to x adjustment operand pAnim->scriptIndex++; - MultiAdjustXY(pAnim->pObject, (int32)FROM_LE_32(pAni[pAnim->scriptIndex].op), 0); + MultiAdjustXY(pAnim->pObject, (int32)FROM_32(pAni[pAnim->scriptIndex].op), 0); // next opcode pAnim->scriptIndex++; @@ -334,7 +334,7 @@ void SkipFrames(ANIM *pAnim, int numFrames) { // move to y adjustment operand pAnim->scriptIndex++; - MultiAdjustXY(pAnim->pObject, 0, (int32)FROM_LE_32(pAni[pAnim->scriptIndex].op)); + MultiAdjustXY(pAnim->pObject, 0, (int32)FROM_32(pAni[pAnim->scriptIndex].op)); // next opcode pAnim->scriptIndex++; @@ -346,11 +346,11 @@ void SkipFrames(ANIM *pAnim, int numFrames) { // move to x adjustment operand pAnim->scriptIndex++; - x = (int32)FROM_LE_32(pAni[pAnim->scriptIndex].op); + x = (int32)FROM_32(pAni[pAnim->scriptIndex].op); // move to y adjustment operand pAnim->scriptIndex++; - y = (int32)FROM_LE_32(pAni[pAnim->scriptIndex].op); + y = (int32)FROM_32(pAni[pAnim->scriptIndex].op); MultiAdjustXY(pAnim->pObject, x, y); @@ -389,7 +389,7 @@ void SkipFrames(ANIM *pAnim, int numFrames) { pAnim->scriptIndex++; } else { // set objects new animation frame - pAnim->pObject->hShape = FROM_LE_32(pAni[pAnim->scriptIndex].hFrame); + pAnim->pObject->hShape = FROM_32(pAni[pAnim->scriptIndex].hFrame); // re-shape the object MultiReshape(pAnim->pObject); @@ -414,7 +414,7 @@ bool AboutToJumpOrEnd(PANIM pAnim) { for (;;) { // repeat until a real image - switch (FROM_LE_32(pAni[zzz].op)) { + switch (FROM_32(pAni[zzz].op)) { case ANI_END: // end of animation script case ANI_JUMP: // do animation jump return true; diff --git a/engines/tinsel/bg.cpp b/engines/tinsel/bg.cpp index a3e21a8227..ed15bfef2a 100644 --- a/engines/tinsel/bg.cpp +++ b/engines/tinsel/bg.cpp @@ -124,28 +124,28 @@ static void BGmainProcess(CORO_PARAM, const void *param) { pReel = (const FREEL *)param; // Get the MULTI_INIT structure - pmi = (const MULTI_INIT *)LockMem(FROM_LE_32(pReel->mobj)); + pmi = (const MULTI_INIT *)LockMem(FROM_32(pReel->mobj)); // Initialize and insert the object, and initialize its script. g_pBG[0] = MultiInitObject(pmi); MultiInsertObject(GetPlayfieldList(FIELD_WORLD), g_pBG[0]); - InitStepAnimScript(&g_thisAnim[0], g_pBG[0], FROM_LE_32(pReel->script), g_BGspeed); + InitStepAnimScript(&g_thisAnim[0], g_pBG[0], FROM_32(pReel->script), g_BGspeed); g_bgReels = 1; } else { /*** At start of scene ***/ pFilm = (const FILM *)LockMem(g_hBackground); - g_bgReels = FROM_LE_32(pFilm->numreels); + g_bgReels = FROM_32(pFilm->numreels); int i; for (i = 0; i < g_bgReels; i++) { // Get the MULTI_INIT structure - pmi = (PMULTI_INIT) LockMem(FROM_LE_32(pFilm->reels[i].mobj)); + pmi = (PMULTI_INIT) LockMem(FROM_32(pFilm->reels[i].mobj)); // Initialize and insert the object, and initialize its script. g_pBG[i] = MultiInitObject(pmi); MultiInsertObject(GetPlayfieldList(FIELD_WORLD), g_pBG[i]); MultiSetZPosition(g_pBG[i], 0); - InitStepAnimScript(&g_thisAnim[i], g_pBG[i], FROM_LE_32(pFilm->reels[i].script), g_BGspeed); + InitStepAnimScript(&g_thisAnim[i], g_pBG[i], FROM_32(pFilm->reels[i].script), g_BGspeed); if (i > 0) g_pBG[i-1]->pSlave = g_pBG[i]; @@ -170,11 +170,11 @@ static void BGmainProcess(CORO_PARAM, const void *param) { // New background during scene if (!TinselV2) { pReel = (const FREEL *)param; - InitStepAnimScript(&g_thisAnim[0], g_pBG[0], FROM_LE_32(pReel->script), g_BGspeed); + InitStepAnimScript(&g_thisAnim[0], g_pBG[0], FROM_32(pReel->script), g_BGspeed); StepAnimScript(&g_thisAnim[0]); } else { pFilm = (const FILM *)LockMem(g_hBackground); - assert(g_bgReels == (int32)FROM_LE_32(pFilm->numreels)); + assert(g_bgReels == (int32)FROM_32(pFilm->numreels)); // Just re-initialize the scripts. for (int i = 0; i < g_bgReels; i++) { @@ -198,7 +198,7 @@ static void BGotherProcess(CORO_PARAM, const void *param) { CORO_END_CONTEXT(_ctx); const FREEL *pReel = (const FREEL *)param; - const MULTI_INIT *pmi = (const MULTI_INIT *)LockMem(FROM_LE_32(pReel->mobj)); + const MULTI_INIT *pmi = (const MULTI_INIT *)LockMem(FROM_32(pReel->mobj)); CORO_BEGIN_CODE(_ctx); @@ -206,7 +206,7 @@ static void BGotherProcess(CORO_PARAM, const void *param) { _ctx->pObj = MultiInitObject(pmi); MultiInsertObject(GetPlayfieldList(FIELD_WORLD), _ctx->pObj); - InitStepAnimScript(&_ctx->anim, g_pBG[0], FROM_LE_32(pReel->script), g_BGspeed); + InitStepAnimScript(&_ctx->anim, g_pBG[0], FROM_32(pReel->script), g_BGspeed); while (StepAnimScript(&_ctx->anim) != ScriptFinished) CORO_SLEEP(1); @@ -249,16 +249,16 @@ void StartupBackground(CORO_PARAM, SCNHANDLE hFilm) { pim = GetImageFromFilm(hFilm, 0, NULL, NULL, &pfilm); - SetBackPal(FROM_LE_32(pim->hImgPal)); + SetBackPal(FROM_32(pim->hImgPal)); // Extract the film speed - g_BGspeed = ONE_SECOND / FROM_LE_32(pfilm->frate); + g_BGspeed = ONE_SECOND / FROM_32(pfilm->frate); // Start display process for each reel in the film CoroScheduler.createProcess(PID_REEL, BGmainProcess, &pfilm->reels[0], sizeof(FREEL)); if (TinselV0) { - for (uint i = 1; i < FROM_LE_32(pfilm->numreels); ++i) + for (uint i = 1; i < FROM_32(pfilm->numreels); ++i) CoroScheduler.createProcess(PID_REEL, BGotherProcess, &pfilm->reels[i], sizeof(FREEL)); } diff --git a/engines/tinsel/bmv.cpp b/engines/tinsel/bmv.cpp index 438fd52a81..106e1542d5 100644 --- a/engines/tinsel/bmv.cpp +++ b/engines/tinsel/bmv.cpp @@ -529,9 +529,9 @@ int BMVPlayer::MovieCommand(char cmd, int commandOffset) { if (cmd & CD_PRINT) { PRINT_CMD *pCmd = (PRINT_CMD *)(bigBuffer + commandOffset); - MovieText(Common::nullContext, (int16)READ_LE_UINT16(&pCmd->stringId), - (int16)READ_LE_UINT16(&pCmd->x), - (int16)READ_LE_UINT16(&pCmd->y), + MovieText(Common::nullContext, (int16)READ_16(&pCmd->stringId), + (int16)READ_16(&pCmd->x), + (int16)READ_16(&pCmd->y), pCmd->fontId, NULL, pCmd->duration); @@ -542,9 +542,9 @@ int BMVPlayer::MovieCommand(char cmd, int commandOffset) { TALK_CMD *pCmd = (TALK_CMD *)(bigBuffer + commandOffset); talkColor = TINSEL_RGB(pCmd->r, pCmd->g, pCmd->b); - MovieText(Common::nullContext, (int16)READ_LE_UINT16(&pCmd->stringId), - (int16)READ_LE_UINT16(&pCmd->x), - (int16)READ_LE_UINT16(&pCmd->y), + MovieText(Common::nullContext, (int16)READ_16(&pCmd->stringId), + (int16)READ_16(&pCmd->x), + (int16)READ_16(&pCmd->y), 0, &talkColor, pCmd->duration); @@ -622,7 +622,7 @@ int BMVPlayer::FollowingPacket(int thisPacket, bool bReallyImportant) { if (nextReadSlot*SLOT_SIZE >= thisPacket && thisPacket+3 >= nextReadSlot*SLOT_SIZE) return thisPacket + 3; } - length = (int32)READ_LE_UINT32(bigBuffer + thisPacket + 1); + length = (int32)READ_32(bigBuffer + thisPacket + 1); length &= 0x00ffffff; return thisPacket + length + 4; } @@ -886,7 +886,7 @@ bool BMVPlayer::DoBMVFrame() { return true; default: - length = (int32)READ_LE_UINT32(data + 1); + length = (int32)READ_32(data + 1); length &= 0x00ffffff; graphOffset = nextUseOffset + 4; // Skip command byte and length @@ -922,7 +922,7 @@ bool BMVPlayer::DoBMVFrame() { } if (*data & CD_XSCR) { - xscr = (int16)READ_LE_UINT16(bigBuffer + graphOffset); + xscr = (int16)READ_16(bigBuffer + graphOffset); graphOffset += sz_XSCR_pkt; // Skip scroll offset length -= sz_XSCR_pkt; } else if (*data & BIT0) diff --git a/engines/tinsel/cursor.cpp b/engines/tinsel/cursor.cpp index bf901c03b6..a83e7cd9ca 100644 --- a/engines/tinsel/cursor.cpp +++ b/engines/tinsel/cursor.cpp @@ -125,7 +125,7 @@ static void InitCurTrailObj(int i, int x, int y) { pim = GetImageFromFilm(g_hCursorFilm, i+1, &pfr, &pmi, &pfilm);// Get pointer to image assert(BgPal()); // No background palette - pim->hImgPal = TO_LE_32(BgPal()); + pim->hImgPal = TO_32(BgPal()); // Initialize and insert the object, set its Z-pos, and hide it g_ntrailData[i].trailObj = MultiInitObject(pmi); @@ -134,7 +134,7 @@ static void InitCurTrailObj(int i, int x, int y) { MultiSetAniXY(g_ntrailData[i].trailObj, x, y); // Initialize the animation script - InitStepAnimScript(&g_ntrailData[i].trailAnim, g_ntrailData[i].trailObj, FROM_LE_32(pfr->script), ONE_SECOND / FROM_LE_32(pfilm->frate)); + InitStepAnimScript(&g_ntrailData[i].trailAnim, g_ntrailData[i].trailObj, FROM_32(pfr->script), ONE_SECOND / FROM_32(pfilm->frate)); StepAnimScript(&g_ntrailData[i].trailAnim); } @@ -237,7 +237,7 @@ void RestoreMainCursor() { if (g_McurObj != NULL) { pfilm = (const FILM *)LockMem(g_hCursorFilm); - InitStepAnimScript(&g_McurAnim, g_McurObj, FROM_LE_32(pfilm->reels->script), ONE_SECOND / FROM_LE_32(pfilm->frate)); + InitStepAnimScript(&g_McurAnim, g_McurObj, FROM_32(pfilm->reels->script), ONE_SECOND / FROM_32(pfilm->frate)); StepAnimScript(&g_McurAnim); } g_bHiddenCursor = false; @@ -324,14 +324,14 @@ IMAGE *GetImageFromReel(const FREEL *pfr, const MULTI_INIT **ppmi) { const MULTI_INIT *pmi; const FRAME *pFrame; - pmi = (const MULTI_INIT *)LockMem(FROM_LE_32(pfr->mobj)); + pmi = (const MULTI_INIT *)LockMem(FROM_32(pfr->mobj)); if (ppmi) *ppmi = pmi; - pFrame = (const FRAME *)LockMem(FROM_LE_32(pmi->hMulFrame)); + pFrame = (const FRAME *)LockMem(FROM_32(pmi->hMulFrame)); // get pointer to image - return (IMAGE *)LockMem(READ_LE_UINT32(pFrame)); + return (IMAGE *)LockMem(READ_32(pFrame)); } /** @@ -379,18 +379,18 @@ void SetAuxCursor(SCNHANDLE hFilm) { pim = GetImageFromFilm(hFilm, 0, &pfr, &pmi, &pfilm);// Get pointer to image assert(BgPal()); // no background palette - pim->hImgPal = TO_LE_32(BgPal()); // Poke in the background palette + pim->hImgPal = TO_32(BgPal()); // Poke in the background palette - g_ACoX = (short)(FROM_LE_16(pim->imgWidth)/2 - ((int16) FROM_LE_16(pim->anioffX))); - g_ACoY = (short)((FROM_LE_16(pim->imgHeight) & ~C16_FLAG_MASK)/2 - - ((int16) FROM_LE_16(pim->anioffY))); + g_ACoX = (short)(FROM_16(pim->imgWidth)/2 - ((int16) FROM_16(pim->anioffX))); + g_ACoY = (short)((FROM_16(pim->imgHeight) & ~C16_FLAG_MASK)/2 - + ((int16) FROM_16(pim->anioffY))); // Initialize and insert the auxillary cursor object g_AcurObj = MultiInitObject(pmi); MultiInsertObject(GetPlayfieldList(FIELD_STATUS), g_AcurObj); // Initialize the animation and set its position - InitStepAnimScript(&g_AcurAnim, g_AcurObj, FROM_LE_32(pfr->script), ONE_SECOND / FROM_LE_32(pfilm->frate)); + InitStepAnimScript(&g_AcurAnim, g_AcurObj, FROM_32(pfr->script), ONE_SECOND / FROM_32(pfilm->frate)); MultiSetAniXY(g_AcurObj, x - g_ACoX, y - g_ACoY); MultiSetZPosition(g_AcurObj, Z_ACURSOR); @@ -481,14 +481,14 @@ static void InitCurObj() { if (TinselV2) { pFilm = (const FILM *)LockMem(g_hCursorFilm); pfr = (const FREEL *)&pFilm->reels[0]; - pmi = (MULTI_INIT *)LockMem(FROM_LE_32(pfr->mobj)); + pmi = (MULTI_INIT *)LockMem(FROM_32(pfr->mobj)); PokeInPalette(pmi); } else { assert(BgPal()); // no background palette pim = GetImageFromFilm(g_hCursorFilm, 0, &pfr, &pmi, &pFilm);// Get pointer to image - pim->hImgPal = TO_LE_32(BgPal()); + pim->hImgPal = TO_32(BgPal()); g_AcurObj = NULL; // No auxillary cursor } @@ -496,7 +496,7 @@ static void InitCurObj() { g_McurObj = MultiInitObject(pmi); MultiInsertObject(GetPlayfieldList(FIELD_STATUS), g_McurObj); - InitStepAnimScript(&g_McurAnim, g_McurObj, FROM_LE_32(pfr->script), ONE_SECOND / FROM_LE_32(pFilm->frate)); + InitStepAnimScript(&g_McurAnim, g_McurObj, FROM_32(pfr->script), ONE_SECOND / FROM_32(pFilm->frate)); } /** @@ -620,7 +620,7 @@ void DwInitCursor(SCNHANDLE bfilm) { g_hCursorFilm = bfilm; pfilm = (const FILM *)LockMem(g_hCursorFilm); - g_numTrails = FROM_LE_32(pfilm->numreels) - 1; + g_numTrails = FROM_32(pfilm->numreels) - 1; assert(g_numTrails <= MAX_TRAILERS); } diff --git a/engines/tinsel/detection.cpp b/engines/tinsel/detection.cpp index 0f662e22bd..2e4be33e53 100644 --- a/engines/tinsel/detection.cpp +++ b/engines/tinsel/detection.cpp @@ -63,6 +63,14 @@ uint16 TinselEngine::getVersion() const { return _gameDescription->version; } +bool TinselEngine::getIsADGFDemo() const { + return (bool)(_gameDescription->desc.flags & ADGF_DEMO); +} + +bool TinselEngine::isCD() const { + return (bool)(_gameDescription->desc.flags & ADGF_CD); +} + } // End of namespace Tinsel static const PlainGameDescriptor tinselGames[] = { diff --git a/engines/tinsel/detection_tables.h b/engines/tinsel/detection_tables.h index ef562a5e08..f6b1487561 100644 --- a/engines/tinsel/detection_tables.h +++ b/engines/tinsel/detection_tables.h @@ -47,7 +47,7 @@ static const TinselGameDescription gameDescriptions[] = { }, GID_DW1, 0, - GF_DEMO, + 0, TINSEL_V0, }, @@ -61,15 +61,15 @@ static const TinselGameDescription gameDescriptions[] = { }, Common::EN_ANY, Common::kPlatformPC, - ADGF_DEMO, + ADGF_DEMO | ADGF_CD, GUIO0() }, GID_DW1, 0, - GF_CD, + 0, TINSEL_V1, }, -#if 0 + { // Macintosh CD Demo V1 version, with *.scn files, see tracker #3110936 { "dw", @@ -81,15 +81,15 @@ static const TinselGameDescription gameDescriptions[] = { }, Common::EN_ANY, Common::kPlatformMacintosh, - ADGF_DEMO, + ADGF_DEMO | ADGF_CD, GUIO0() }, GID_DW1, 0, - GF_CD | GF_SCNFILES | GF_BIG_ENDIAN, + GF_SCNFILES, TINSEL_V1, }, -#endif + { // Multilingual Floppy V1 with *.gra files. // Note: It contains no english subtitles. { @@ -110,7 +110,7 @@ static const TinselGameDescription gameDescriptions[] = { }, GID_DW1, 0, - GF_FLOPPY | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT, + GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT, TINSEL_V1, }, @@ -133,7 +133,7 @@ static const TinselGameDescription gameDescriptions[] = { }, GID_DW1, 0, - GF_FLOPPY | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT, + GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT, TINSEL_V1, }, @@ -156,7 +156,7 @@ static const TinselGameDescription gameDescriptions[] = { }, GID_DW1, 0, - GF_FLOPPY | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT, + GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT, TINSEL_V1, }, @@ -179,7 +179,7 @@ static const TinselGameDescription gameDescriptions[] = { }, GID_DW1, 0, - GF_FLOPPY | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT, + GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT, TINSEL_V1, }, @@ -195,7 +195,7 @@ static const TinselGameDescription gameDescriptions[] = { }, GID_DW1, 0, - GF_FLOPPY | GF_ENHANCED_AUDIO_SUPPORT, + GF_ENHANCED_AUDIO_SUPPORT, TINSEL_V1, }, @@ -214,7 +214,7 @@ static const TinselGameDescription gameDescriptions[] = { }, GID_DW1, 0, - GF_CD | GF_ENHANCED_AUDIO_SUPPORT, + GF_ENHANCED_AUDIO_SUPPORT, TINSEL_V1, }, @@ -228,12 +228,12 @@ static const TinselGameDescription gameDescriptions[] = { }, Common::EN_ANY, Common::kPlatformPC, - ADGF_NO_FLAGS, + ADGF_CD, GUIO_NONE }, GID_DW1, 0, - GF_CD | GF_ENHANCED_AUDIO_SUPPORT, + GF_ENHANCED_AUDIO_SUPPORT, TINSEL_V1, }, @@ -249,7 +249,7 @@ static const TinselGameDescription gameDescriptions[] = { }, GID_DW1, 0, - GF_FLOPPY | GF_ENHANCED_AUDIO_SUPPORT, + GF_ENHANCED_AUDIO_SUPPORT, TINSEL_V1, }, @@ -271,7 +271,7 @@ static const TinselGameDescription gameDescriptions[] = { }, GID_DW1, 0, - GF_CD | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT, + GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT, TINSEL_V1, }, @@ -296,7 +296,7 @@ static const TinselGameDescription gameDescriptions[] = { }, GID_DW1, 0, - GF_CD | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT, + GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT, TINSEL_V1, }, @@ -315,12 +315,12 @@ static const TinselGameDescription gameDescriptions[] = { }, Common::DE_DEU, Common::kPlatformPC, - ADGF_DROPLANGUAGE | ADGF_CD, + ADGF_DROPLANGUAGE, GUIO0() }, GID_DW1, 0, - GF_CD | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT, + GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT, TINSEL_V1, }, { @@ -343,7 +343,7 @@ static const TinselGameDescription gameDescriptions[] = { }, GID_DW1, 0, - GF_CD | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT, + GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT, TINSEL_V1, }, { @@ -366,7 +366,7 @@ static const TinselGameDescription gameDescriptions[] = { }, GID_DW1, 0, - GF_CD | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT, + GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT, TINSEL_V1, }, @@ -386,7 +386,7 @@ static const TinselGameDescription gameDescriptions[] = { }, GID_DW1, 0, - GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT, + GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT, TINSEL_V1, }, @@ -406,7 +406,7 @@ static const TinselGameDescription gameDescriptions[] = { }, GID_DW1, 0, - GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT, + GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT, TINSEL_V1, }, @@ -425,7 +425,7 @@ static const TinselGameDescription gameDescriptions[] = { }, GID_DW1, 0, - GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT, + GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT, TINSEL_V1, }, @@ -443,12 +443,12 @@ static const TinselGameDescription gameDescriptions[] = { }, Common::EN_ANY, Common::kPlatformPSX, - ADGF_DEMO, + ADGF_CD | ADGF_DEMO, GUIO0() }, GID_DW1, 0, - GF_CD | GF_SCNFILES, + GF_SCNFILES, TINSEL_V1, }, @@ -469,12 +469,11 @@ static const TinselGameDescription gameDescriptions[] = { }, GID_DW1, 0, - GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT, + GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT, TINSEL_V1, }, #endif -#if 0 { // Mac multilanguage CD { "dw", @@ -491,12 +490,10 @@ static const TinselGameDescription gameDescriptions[] = { }, GID_DW1, 0, - GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT | GF_BIG_ENDIAN, + GF_SCNFILES, TINSEL_V1, }, -#endif - { // German CD re-release "Neon Edition" // Note: This release has ENGLISH.TXT (with german content) instead of GERMAN.TXT { @@ -510,7 +507,7 @@ static const TinselGameDescription gameDescriptions[] = { }, GID_DW1, 0, - GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT | GF_ALT_MIDI, + GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT | GF_ALT_MIDI, TINSEL_V1, }, @@ -531,30 +528,10 @@ static const TinselGameDescription gameDescriptions[] = { }, GID_DW1, 0, - GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT, + GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT, TINSEL_V1, }, - { // English DW2 demo - { - "dw2", - "Demo", - { - {"dw2.scn", 0, "853ab998f5136b69bc586991175d6eeb", 4231121}, - {"english.smp", 0, "b5660a0e031cb4710bcb0ef5629ea61d", 28562357}, - {NULL, 0, NULL, 0} - }, - Common::EN_ANY, - Common::kPlatformPC, - ADGF_DEMO, - GUIO1(GUIO_NOASPECT) - }, - GID_DW2, - 0, - GF_CD | GF_SCNFILES | GF_DEMO, - TINSEL_V2, - }, - { // Polish fan translaction Discworld 1 { "dw", @@ -567,15 +544,35 @@ static const TinselGameDescription gameDescriptions[] = { }, Common::PL_POL, Common::kPlatformPC, - ADGF_NO_FLAGS, + ADGF_CD, GUIO_NONE }, GID_DW1, 0, - GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT, + GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT, TINSEL_V1, }, + { // English Discworld 2 demo + { + "dw2", + "Demo", + { + {"dw2.scn", 0, "853ab998f5136b69bc586991175d6eeb", 4231121}, + {"english.smp", 0, "b5660a0e031cb4710bcb0ef5629ea61d", 28562357}, + {NULL, 0, NULL, 0} + }, + Common::EN_ANY, + Common::kPlatformPC, + ADGF_DEMO | ADGF_CD, + GUIO1(GUIO_NOASPECT) + }, + GID_DW2, + 0, + GF_SCNFILES, + TINSEL_V2, + }, + { // European/Australian Discworld 2 release { "dw2", @@ -587,12 +584,12 @@ static const TinselGameDescription gameDescriptions[] = { }, Common::EN_GRB, Common::kPlatformPC, - ADGF_NO_FLAGS, + ADGF_CD, GUIO1(GUIO_NOASPECT) }, GID_DW2, 0, - GF_CD | GF_SCNFILES, + GF_SCNFILES, TINSEL_V2, }, @@ -607,12 +604,12 @@ static const TinselGameDescription gameDescriptions[] = { }, Common::EN_USA, Common::kPlatformPC, - ADGF_NO_FLAGS, + ADGF_CD, GUIO1(GUIO_NOASPECT) }, GID_DW2, 0, - GF_CD | GF_SCNFILES, + GF_SCNFILES, TINSEL_V2, }, @@ -627,12 +624,12 @@ static const TinselGameDescription gameDescriptions[] = { }, Common::FR_FRA, Common::kPlatformPC, - ADGF_NO_FLAGS, + ADGF_CD, GUIO1(GUIO_NOASPECT) }, GID_DW2, 0, - GF_CD | GF_SCNFILES, + GF_SCNFILES, TINSEL_V2, }, @@ -647,12 +644,12 @@ static const TinselGameDescription gameDescriptions[] = { }, Common::DE_DEU, Common::kPlatformPC, - ADGF_NO_FLAGS, + ADGF_CD, GUIO1(GUIO_NOASPECT) }, GID_DW2, 0, - GF_CD | GF_SCNFILES, + GF_SCNFILES, TINSEL_V2, }, @@ -668,12 +665,12 @@ static const TinselGameDescription gameDescriptions[] = { }, Common::IT_ITA, Common::kPlatformPC, - ADGF_NO_FLAGS, + ADGF_CD, GUIO1(GUIO_NOASPECT) }, GID_DW2, 0, - GF_CD | GF_SCNFILES, + GF_SCNFILES, TINSEL_V2, }, { @@ -688,12 +685,12 @@ static const TinselGameDescription gameDescriptions[] = { }, Common::ES_ESP, Common::kPlatformPC, - ADGF_NO_FLAGS, + ADGF_CD, GUIO1(GUIO_NOASPECT) }, GID_DW2, 0, - GF_CD | GF_SCNFILES, + GF_SCNFILES, TINSEL_V2, }, @@ -709,12 +706,12 @@ static const TinselGameDescription gameDescriptions[] = { }, Common::RU_RUS, Common::kPlatformPC, - ADGF_NO_FLAGS, + ADGF_CD, GUIO1(GUIO_NOASPECT) }, GID_DW2, 0, - GF_CD | GF_SCNFILES, + GF_SCNFILES, TINSEL_V2, }, diff --git a/engines/tinsel/dialogs.cpp b/engines/tinsel/dialogs.cpp index fbe9e8d1f6..d0c99f7830 100644 --- a/engines/tinsel/dialogs.cpp +++ b/engines/tinsel/dialogs.cpp @@ -234,8 +234,8 @@ enum PARTS_INDEX { #define NM_RS_R_INSET 4 #define NM_RS_THICKNESS 5 #define NM_MOVE_AREA_B_Y 30 -#define NM_SLIDE_INSET (TinselV2 ? 18 : 15) // X offset (from right) of left of scroll region -#define NM_SLIDE_THICKNESS (TinselV2 ? 13 : 4) // thickness of scroll region +#define NM_SLIDE_INSET (TinselV2 ? 18 : 9) // X offset (from right) of left of scroll region +#define NM_SLIDE_THICKNESS (TinselV2 ? 13 : 7) // thickness of scroll region #define NM_UP_ARROW_TOP 34 // Y offset of top of up arrow #define NM_UP_ARROW_BOTTOM 49 // Y offset of bottom of up arrow #define NM_DN_ARROW_TOP 22 // Y offset (from bottom) of top of down arrow @@ -753,6 +753,11 @@ static CONFBOX t1RestartBox[] = { #endif }; +static CONFBOX t1RestartBoxPSX[] = { + { AAGBUT, INITGAME, TM_NONE, NULL, USE_POINTER, 122, 48, 23, 19, NULL, IX1_TICK1 }, + { AAGBUT, CLOSEWIN, TM_NONE, NULL, USE_POINTER, 82, 48, 23, 19, NULL, IX1_CROSS1 } +}; + static CONFBOX t2RestartBox[] = { { AAGBUT, INITGAME, TM_NONE, NULL, 0, 140, 78, BW, BH, NULL, IX2_TICK1 }, { AAGBUT, CLOSEWIN, TM_NONE, NULL, 0, 60, 78, BW, BH, NULL, IX2_CROSS1 } @@ -763,10 +768,10 @@ static CONFINIT t1ciRestart = { 6, 2, 72, 53, false, t1RestartBox, ARRAYSIZE(t1R #else static CONFINIT t1ciRestart = { 4, 2, 98, 53, false, t1RestartBox, ARRAYSIZE(t1RestartBox), SIX_RESTART_HEADING }; #endif +static CONFINIT t1ciRestartPSX = { 8, 2, 46, 53, false, t1RestartBoxPSX, ARRAYSIZE(t1RestartBoxPSX), SIX_RESTART_HEADING }; static CONFINIT t2ciRestart = { 4, 2, 196, 53, false, t2RestartBox, sizeof(t2RestartBox)/sizeof(CONFBOX), SS_RESTART_HEADING }; -#define ciRestart (TinselV2 ? t2ciRestart : t1ciRestart) -#define restartBox (TinselV2 ? t2RestartBox : t1RestartBox) +#define ciRestart (TinselV2 ? t2ciRestart : (TinselV1PSX ? t1ciRestartPSX : t1ciRestart)) /*-------------------------------------------------------------*\ | This is the sound control 'menu'. In Discworld 2, it also | @@ -1038,18 +1043,20 @@ static bool RePosition(); static bool LanguageChange() { LANGUAGE nLang = _vm->_config->_language; - if (_vm->getFeatures() & GF_USE_3FLAGS) { - // VERY quick dodgy bodge - if (cd.selBox == 0) - nLang = TXT_FRENCH; // = 1 - else if (cd.selBox == 1) - nLang = TXT_GERMAN; // = 2 - else - nLang = TXT_SPANISH; // = 4 - } else if (_vm->getFeatures() & GF_USE_4FLAGS) { - nLang = (LANGUAGE)(cd.selBox + 1); - } else if (_vm->getFeatures() & GF_USE_5FLAGS) { - nLang = (LANGUAGE)cd.selBox; + if ((_vm->getFeatures() & GF_USE_3FLAGS) || (_vm->getFeatures() & GF_USE_4FLAGS) || (_vm->getFeatures() & GF_USE_5FLAGS)) { + // Languages: TXT_ENGLISH, TXT_FRENCH, TXT_GERMAN, TXT_ITALIAN, TXT_SPANISH + // 5 flag versions include English + int selected = (_vm->getFeatures() & GF_USE_5FLAGS) ? cd.selBox : cd.selBox + 1; + // Make sure that a language flag has been selected. If the user has + // changed the language speed slider and hasn't clicked on a flag, it + // won't be selected. + if (selected >= 0 && selected <= 4) { + nLang = (LANGUAGE)selected; + + // 3 flag versions don't include Italian + if (selected >= 3 && (_vm->getFeatures() & GF_USE_3FLAGS)) + nLang = TXT_SPANISH; + } } if (nLang != _vm->_config->_language) { @@ -1136,7 +1143,7 @@ static void FirstScene(int first) { // Fill in the rest for (i = 0; i < NUM_RGROUP_BOXES && i + first < g_numScenes; i++) { cd.box[i].textMethod = TM_STRINGNUM; - cd.box[i].ixText = FROM_LE_32(g_pHopper[i + first].hSceneDesc); + cd.box[i].ixText = FROM_32(g_pHopper[i + first].hSceneDesc); } // Blank out the spare ones (if any) while (i < NUM_RGROUP_BOXES) { @@ -1159,10 +1166,10 @@ static void SetChosenScene() { static void FirstEntry(int first) { int i; - g_InvD[INV_MENU].hInvTitle = FROM_LE_32(g_pChosenScene->hSceneDesc); + g_InvD[INV_MENU].hInvTitle = FROM_32(g_pChosenScene->hSceneDesc); // get number of entrances - g_numEntries = FROM_LE_32(g_pChosenScene->numEntries); + g_numEntries = FROM_32(g_pChosenScene->numEntries); // Force first to a sensible value if (first > g_numEntries-NUM_RGROUP_BOXES) @@ -1172,7 +1179,7 @@ static void FirstEntry(int first) { for (i = 0; i < NUM_RGROUP_BOXES && i < g_numEntries; i++) { cd.box[i].textMethod = TM_STRINGNUM; - cd.box[i].ixText = FROM_LE_32(g_pEntries[FROM_LE_32(g_pChosenScene->entryIndex) + i + first].hDesc); + cd.box[i].ixText = FROM_32(g_pEntries[FROM_32(g_pChosenScene->entryIndex) + i + first].hDesc); } // Blank out the spare ones (if any) while (i < NUM_RGROUP_BOXES) { @@ -1184,17 +1191,17 @@ static void FirstEntry(int first) { } static void HopAction() { - PHOPENTRY pEntry = g_pEntries + FROM_LE_32(g_pChosenScene->entryIndex) + cd.selBox + cd.extraBase; + PHOPENTRY pEntry = g_pEntries + FROM_32(g_pChosenScene->entryIndex) + cd.selBox + cd.extraBase; - uint32 hScene = FROM_LE_32(g_pChosenScene->hScene); - uint32 eNumber = FROM_LE_32(pEntry->eNumber); + uint32 hScene = FROM_32(g_pChosenScene->hScene); + uint32 eNumber = FROM_32(pEntry->eNumber); debugC(DEBUG_BASIC, kTinselDebugAnimations, "Scene hopper chose scene %xh,%d\n", hScene, eNumber); - if (FROM_LE_32(pEntry->flags) & fCall) { + if (FROM_32(pEntry->flags) & fCall) { SaveScene(Common::nullContext); NewScene(Common::nullContext, g_pChosenScene->hScene, pEntry->eNumber, TRANS_FADE); } - else if (FROM_LE_32(pEntry->flags) & fHook) + else if (FROM_32(pEntry->flags) & fHook) HookScene(hScene, eNumber, TRANS_FADE); else NewScene(Common::nullContext, hScene, eNumber, TRANS_CUT); @@ -1256,6 +1263,20 @@ static INV_OBJECT *GetInvObject(int id) { } /** + * Returns true if the given id represents a valid inventory object + */ +bool GetIsInvObject(int id) { + INV_OBJECT *pObject = g_invObjects; + + for (int i = 0; i < g_numObjects; i++, pObject++) { + if (pObject->id == id) + return true; + } + + return false; +} + +/** * Convert item ID number to index. */ static int GetObjectIndex(int id) { @@ -2547,7 +2568,7 @@ static OBJECT *AddInvObject(int num, const FREEL **pfreel, const FILM **pfilm) { pim = GetImageFromFilm(invObj->hIconFilm, 0, pfreel, &pmi, pfilm); // Poke in the background palette - pim->hImgPal = TO_LE_32(BgPal()); + pim->hImgPal = TO_32(BgPal()); // Set up the multi-object pPlayObj = MultiInitObject(pmi); @@ -2588,7 +2609,7 @@ static void FillInInventory() { MultiSetAniXY(g_iconArray[n], g_InvD[g_ino].inventoryX + xpos , g_InvD[g_ino].inventoryY + ypos); MultiSetZPosition(g_iconArray[n], Z_INV_ICONS); - InitStepAnimScript(&g_iconAnims[n], g_iconArray[n], FROM_LE_32(pfr->script), ONE_SECOND / FROM_LE_32(pfilm->frate)); + InitStepAnimScript(&g_iconAnims[n], g_iconArray[n], FROM_32(pfr->script), ONE_SECOND / FROM_32(pfilm->frate)); n++; } @@ -2676,17 +2697,17 @@ static OBJECT *AddObject(const FREEL *pfreel, int num) { pim = GetImageFromReel(pfreel, &pmi); // Poke in the background palette - pim->hImgPal = TO_LE_32(BgPal()); + pim->hImgPal = TO_32(BgPal()); // Horrible bodge involving global variables to save // width and/or height of some window frame components if (num == g_TL) { - g_TLwidth = FROM_LE_16(pim->imgWidth); - g_TLheight = FROM_LE_16(pim->imgHeight) & ~C16_FLAG_MASK; + g_TLwidth = FROM_16(pim->imgWidth); + g_TLheight = FROM_16(pim->imgHeight) & ~C16_FLAG_MASK; } else if (num == g_TR) { - g_TRwidth = FROM_LE_16(pim->imgWidth); + g_TRwidth = FROM_16(pim->imgWidth); } else if (num == g_BL) { - g_BLheight = FROM_LE_16(pim->imgHeight) & ~C16_FLAG_MASK; + g_BLheight = FROM_16(pim->imgHeight) & ~C16_FLAG_MASK; } // Set up and insert the multi-object @@ -2702,7 +2723,7 @@ static OBJECT *AddObject(const FREEL *pfreel, int num) { static void AddSlider(OBJECT **slide, const FILM *pfilm) { g_SlideObject = *slide = AddObject(&pfilm->reels[IX_SLIDE], -1); - MultiSetAniXY(*slide, MultiRightmost(g_RectObject) + (TinselV2 ? NM_SLX : -M_SXOFF + 2) - 1, + MultiSetAniXY(*slide, MultiRightmost(g_RectObject) + (TinselV2 ? NM_SLX : -M_SXOFF + 2), g_InvD[g_ino].inventoryY + g_sliderYpos); MultiSetZPosition(*slide, Z_INV_MFRAME); } @@ -3297,7 +3318,7 @@ static void ConstructInventory(InventoryType filling) { } } } else if (g_InvD[g_ino].NoofItems > g_InvD[g_ino].NoofHicons*g_InvD[g_ino].NoofVicons) { - g_sliderYmin = g_TLheight - (TinselV2 ? 2 : 1); + g_sliderYmin = g_TLheight - (TinselV2 ? 1 : 2); g_sliderYmax = g_TLheight + eV + (TinselV2 ? 12 : 10); AddSlider(&retObj[n++], pfilm); } @@ -3385,9 +3406,9 @@ static void AlterCursor(int num) { pim = GetImageFromFilm(g_hWinParts, num, &pfreel); // Poke in the background palette - pim->hImgPal = TO_LE_32(BgPal()); + pim->hImgPal = TO_32(BgPal()); - SetTempCursor(FROM_LE_32(pfreel->script)); + SetTempCursor(FROM_32(pfreel->script)); } enum InvCursorFN {IC_AREA, IC_DROP}; @@ -4840,50 +4861,101 @@ static void InvDragEnd() { g_Xchange = g_Ychange = 0; // Probably no need, but does no harm! } -static void MenuPageDown() { +static bool MenuDown(int lines) { if (cd.box == loadBox || cd.box == saveBox) { - if (cd.extraBase < MAX_SAVED_FILES-NUM_RGROUP_BOXES) { - FirstFile(cd.extraBase+(NUM_RGROUP_BOXES - 1)); + if (cd.extraBase < MAX_SAVED_FILES - NUM_RGROUP_BOXES) { + FirstFile(cd.extraBase + lines); AddBoxes(true); - cd.selBox = NUM_RGROUP_BOXES - 1; - Select(cd.selBox, true); + return true; } } else if (cd.box == hopperBox1) { if (cd.extraBase < g_numScenes - NUM_RGROUP_BOXES) { - FirstScene(cd.extraBase + (NUM_RGROUP_BOXES - 1)); + FirstScene(cd.extraBase + lines); AddBoxes(true); - if (cd.selBox) - cd.selBox = NUM_RGROUP_BOXES - 1; - Select(cd.selBox, true); + return true; } } else if (cd.box == hopperBox2) { if (cd.extraBase < g_numEntries - NUM_RGROUP_BOXES) { - FirstEntry(cd.extraBase+(NUM_RGROUP_BOXES - 1)); + FirstEntry(cd.extraBase + lines); AddBoxes(true); - if (cd.selBox) - cd.selBox = NUM_RGROUP_BOXES - 1; - Select(cd.selBox, true); + return true; } } + return false; } -static void MenuPageUp() { +static bool MenuUp(int lines) { if (cd.extraBase > 0) { if (cd.box == loadBox || cd.box == saveBox) - FirstFile(cd.extraBase-(NUM_RGROUP_BOXES - 1)); + FirstFile(cd.extraBase - lines); else if (cd.box == hopperBox1) - FirstScene(cd.extraBase-(NUM_RGROUP_BOXES - 1)); + FirstScene(cd.extraBase - lines); else if (cd.box == hopperBox2) - FirstEntry(cd.extraBase-(NUM_RGROUP_BOXES - 1)); + FirstEntry(cd.extraBase - lines); else - return; + return false; AddBoxes(true); + return true; + } + return false; +} + +static void MenuRollDown() { + if (MenuDown(1)) { + if (cd.selBox > 0) + cd.selBox--; + Select(cd.selBox, true); + } +} + +static void MenuRollUp() { + if (MenuUp(1)) { + if (cd.selBox < NUM_RGROUP_BOXES - 1) + cd.selBox++; + Select(cd.selBox, true); + } +} + +static void MenuPageDown() { + if (MenuDown(NUM_RGROUP_BOXES - 1)) { + cd.selBox = NUM_RGROUP_BOXES - 1; + Select(cd.selBox, true); + } +} + +static void MenuPageUp() { + if (MenuUp(NUM_RGROUP_BOXES - 1)) { cd.selBox = 0; Select(cd.selBox, true); } } +static void InventoryDown() { + // This code is a copy of the IB_SLIDE_DOWN case in InvWalkTo + // TODO: So share this duplicate code + if (g_InvD[g_ino].NoofVicons == 1) + if (g_InvD[g_ino].FirstDisp + g_InvD[g_ino].NoofHicons*g_InvD[g_ino].NoofVicons < g_InvD[g_ino].NoofItems) + g_InvD[g_ino].FirstDisp += g_InvD[g_ino].NoofHicons; + for (int i = 1; i < g_InvD[g_ino].NoofVicons; i++) { + if (g_InvD[g_ino].FirstDisp + g_InvD[g_ino].NoofHicons*g_InvD[g_ino].NoofVicons < g_InvD[g_ino].NoofItems) + g_InvD[g_ino].FirstDisp += g_InvD[g_ino].NoofHicons; + } + g_ItemsChanged = true; +} + +static void InventoryUp() { + // This code is a copy of the I_SLIDE_UP case in InvWalkTo + // TODO: So share this duplicate code + if (g_InvD[g_ino].NoofVicons == 1) + g_InvD[g_ino].FirstDisp -= g_InvD[g_ino].NoofHicons; + for (int i = 1; i < g_InvD[g_ino].NoofVicons; i++) + g_InvD[g_ino].FirstDisp -= g_InvD[g_ino].NoofHicons; + if (g_InvD[g_ino].FirstDisp < 0) + g_InvD[g_ino].FirstDisp = 0; + g_ItemsChanged = true; +} + /**************************************************************************/ /************** Incoming events - further processing **********************/ /**************************************************************************/ @@ -5378,42 +5450,47 @@ extern void EventToInventory(PLR_EVENT pEvent, const Common::Point &coOrds) { case PLR_PGDN: if (g_ino == INV_MENU) { - // Only act if load or save screen + // Load or Save screen MenuPageDown(); } else { - // This code is a copy of the IB_SLIDE_DOWN case in InvWalkTo - // TODO: So share this duplicate code - if (g_InvD[g_ino].NoofVicons == 1) - if (g_InvD[g_ino].FirstDisp + g_InvD[g_ino].NoofHicons*g_InvD[g_ino].NoofVicons < g_InvD[g_ino].NoofItems) - g_InvD[g_ino].FirstDisp += g_InvD[g_ino].NoofHicons; - for (int i = 1; i < g_InvD[g_ino].NoofVicons; i++) { - if (g_InvD[g_ino].FirstDisp + g_InvD[g_ino].NoofHicons*g_InvD[g_ino].NoofVicons < g_InvD[g_ino].NoofItems) - g_InvD[g_ino].FirstDisp += g_InvD[g_ino].NoofHicons; - } - g_ItemsChanged = true; + // Inventory window + InventoryDown(); } break; case PLR_PGUP: if (g_ino == INV_MENU) { - // Only act if load or save screen + // Load or Save screen MenuPageUp(); } else { - // This code is a copy of the I_SLIDE_UP case in InvWalkTo - // TODO: So share this duplicate code - if (g_InvD[g_ino].NoofVicons == 1) - g_InvD[g_ino].FirstDisp -= g_InvD[g_ino].NoofHicons; - for (int i = 1; i < g_InvD[g_ino].NoofVicons; i++) - g_InvD[g_ino].FirstDisp -= g_InvD[g_ino].NoofHicons; - if (g_InvD[g_ino].FirstDisp < 0) - g_InvD[g_ino].FirstDisp = 0; - g_ItemsChanged = true; + // Inventory window + InventoryUp(); + } + break; + + case PLR_WHEEL_DOWN: + if (g_ino == INV_MENU) { + // Load or Save screen + MenuRollDown(); + } else { + // Inventory window + InventoryDown(); + } + break; + + case PLR_WHEEL_UP: + if (g_ino == INV_MENU) { + // Load or Save screen + MenuRollUp(); + } else { + // Inventory window + InventoryUp(); } break; case PLR_HOME: if (g_ino == INV_MENU) { - // Only act if load or save screen + // Load or Save screen if (cd.box == loadBox || cd.box == saveBox) FirstFile(0); else if (cd.box == hopperBox1) @@ -5427,6 +5504,7 @@ extern void EventToInventory(PLR_EVENT pEvent, const Common::Point &coOrds) { cd.selBox = 0; Select(cd.selBox, true); } else { + // Inventory window g_InvD[g_ino].FirstDisp = 0; g_ItemsChanged = true; } @@ -5434,6 +5512,7 @@ extern void EventToInventory(PLR_EVENT pEvent, const Common::Point &coOrds) { case PLR_END: if (g_ino == INV_MENU) { + // Load or Save screen if (cd.box == loadBox || cd.box == saveBox) FirstFile(MAX_SAVED_FILES); // Will get reduced to appropriate value else if (cd.box == hopperBox1) @@ -5447,6 +5526,7 @@ extern void EventToInventory(PLR_EVENT pEvent, const Common::Point &coOrds) { cd.selBox = 0; Select(cd.selBox, true); } else { + // Inventory window g_InvD[g_ino].FirstDisp = g_InvD[g_ino].NoofItems - g_InvD[g_ino].NoofHicons*g_InvD[g_ino].NoofVicons; if (g_InvD[g_ino].FirstDisp < 0) g_InvD[g_ino].FirstDisp = 0; @@ -5539,21 +5619,6 @@ extern void RegisterIcons(void *cptr, int num) { memmove(destP, srcP, 12); destP->attribute = 0; } - } else if (TinselV1Mac) { - // Macintosh version has BE encoded resources, so the values need to be byte swapped - MEM_NODE *node = MemoryAllocFixed(g_numObjects * sizeof(INV_OBJECT)); - assert(node); - g_invObjects = (INV_OBJECT *)MemoryDeref(node); - assert(g_invObjects); - INV_OBJECT *srcP = (INV_OBJECT *)cptr; - INV_OBJECT *destP = (INV_OBJECT *)g_invObjects; - - for (int i = 0; i < num; ++i, ++destP, ++srcP) { - destP->id = FROM_BE_32(srcP->id); - destP->hIconFilm = FROM_BE_32(srcP->hIconFilm); - destP->hScript = FROM_BE_32(srcP->hScript); - destP->attribute = FROM_BE_32(srcP->attribute); - } } else if (TinselV2) { if (g_invFilms == NULL) { // First time - allocate memory @@ -5592,7 +5657,7 @@ extern void setInvWinParts(SCNHANDLE hf) { #ifdef DEBUG pfilm = (const FILM *)LockMem(hf); - assert(FROM_LE_32(pfilm->numreels) >= (uint32)(TinselV2 ? T2_HOPEDFORREELS : T1_HOPEDFORREELS)); // not as many reels as expected + assert(FROM_32(pfilm->numreels) >= (uint32)(TinselV2 ? T2_HOPEDFORREELS : T1_HOPEDFORREELS)); // not as many reels as expected #endif } @@ -5609,7 +5674,7 @@ extern void setFlagFilms(SCNHANDLE hf) { #ifdef DEBUG pfilm = (const FILM *)LockMem(hf); - assert(FROM_LE_32(pfilm->numreels) >= HOPEDFORFREELS); // not as many reels as expected + assert(FROM_32(pfilm->numreels) >= HOPEDFORFREELS); // not as many reels as expected #endif } diff --git a/engines/tinsel/dialogs.h b/engines/tinsel/dialogs.h index 8c48eb8b76..ab53ba771c 100644 --- a/engines/tinsel/dialogs.h +++ b/engines/tinsel/dialogs.h @@ -152,6 +152,8 @@ void InvSetLimit(int invno, int n); void InvSetSize(int invno, int MinWidth, int MinHeight, int StartWidth, int StartHeight, int MaxWidth, int MaxHeight); +bool GetIsInvObject(int id); + int WhichInventoryOpen(); bool IsTopWindow(); diff --git a/engines/tinsel/drives.cpp b/engines/tinsel/drives.cpp index 5c4b939e4e..3ecef83753 100644 --- a/engines/tinsel/drives.cpp +++ b/engines/tinsel/drives.cpp @@ -149,7 +149,7 @@ bool GotoCD() { bool TinselFile::_warningShown = false; -TinselFile::TinselFile() : ReadStreamEndian((_vm->getFeatures() & GF_BIG_ENDIAN) != 0) { +TinselFile::TinselFile() : ReadStreamEndian(TinselV1Mac) { _stream = NULL; } diff --git a/engines/tinsel/events.cpp b/engines/tinsel/events.cpp index 1aa4d34227..61d3903f1a 100644 --- a/engines/tinsel/events.cpp +++ b/engines/tinsel/events.cpp @@ -389,7 +389,8 @@ void PlayerEvent(PLR_EVENT pEvent, const Common::Point &coOrds) { "PLR_PROV_WALKTO", "PLR_WALKTO", "PLR_LOOK", "PLR_ACTION", "PLR_ESCAPE", "PLR_MENU", "PLR_QUIT", "PLR_PGUP", "PLR_PGDN", "PLR_HOME", "PLR_END", "PLR_DRAG1_START", "PLR_DRAG1_END", "PLR_DRAG2_START", "PLR_DRAG2_END", - "PLR_JUMP", "PLR_NOEVENT"}; + "PLR_JUMP", "PLR_NOEVENT", "PLR_SAVE", "PLR_LOAD", "PLR_WHEEL_UP", + "PLR_WHEEL_DOWN"}; debugC(DEBUG_BASIC, kTinselDebugActions, "%s - (%d,%d)", actionList[pEvent], coOrds.x, coOrds.y); static uint32 lastRealAction = 0; // FIXME: Avoid non-const global vars diff --git a/engines/tinsel/events.h b/engines/tinsel/events.h index cdf5ae2ae4..51669e4680 100644 --- a/engines/tinsel/events.h +++ b/engines/tinsel/events.h @@ -65,6 +65,8 @@ enum PLR_EVENT { PLR_NOEVENT = 16, PLR_SAVE = 17, PLR_LOAD = 18, + PLR_WHEEL_UP = 19, + PLR_WHEEL_DOWN = 20, // Aliases used for DW1 actions PLR_SLEFT = PLR_WALKTO, diff --git a/engines/tinsel/faders.cpp b/engines/tinsel/faders.cpp index c1574ff963..e9517103a9 100644 --- a/engines/tinsel/faders.cpp +++ b/engines/tinsel/faders.cpp @@ -106,7 +106,7 @@ static void FadeProcess(CORO_PARAM, const void *param) { FadingPalette(pFade->pPalQ, true); // get pointer to palette - reduce pointer indirection a bit - _ctx->pPalette = (PALETTE *)LockMem(pFade->pPalQ->hPal); + _ctx->pPalette = (PALETTE *)LockMem(FROM_32(pFade->pPalQ->hPal)); for (_ctx->pColMult = pFade->pColorMultTable; *_ctx->pColMult >= 0; _ctx->pColMult++) { // go through all multipliers in table - until a negative entry @@ -117,10 +117,10 @@ static void FadeProcess(CORO_PARAM, const void *param) { pFade->pPalQ->numColors, (uint32) *_ctx->pColMult); else FadePalette(_ctx->fadeRGB, _ctx->pPalette->palRGB, - FROM_LE_32(_ctx->pPalette->numColors), (uint32) *_ctx->pColMult); + FROM_32(_ctx->pPalette->numColors), (uint32) *_ctx->pColMult); // send new palette to video DAC - UpdateDACqueue(pFade->pPalQ->posInDAC, FROM_LE_32(_ctx->pPalette->numColors), _ctx->fadeRGB); + UpdateDACqueue(pFade->pPalQ->posInDAC, FROM_32(_ctx->pPalette->numColors), _ctx->fadeRGB); // allow time for video DAC to be updated CORO_SLEEP(1); diff --git a/engines/tinsel/font.cpp b/engines/tinsel/font.cpp index 54aa7cc15f..3dba56468b 100644 --- a/engines/tinsel/font.cpp +++ b/engines/tinsel/font.cpp @@ -102,16 +102,16 @@ void FettleFontPal(SCNHANDLE fontPal) { assert(g_hTalkFont); // Talk font not declared pFont = (const FONT *)LockMem(g_hTagFont); - pImg = (IMAGE *)LockMem(FROM_LE_32(pFont->fontInit.hObjImg)); // get image for char 0 + pImg = (IMAGE *)LockMem(FROM_32(pFont->fontInit.hObjImg)); // get image for char 0 if (!TinselV2) - pImg->hImgPal = TO_LE_32(fontPal); + pImg->hImgPal = TO_32(fontPal); else pImg->hImgPal = 0; pFont = (const FONT *)LockMem(g_hTalkFont); - pImg = (IMAGE *)LockMem(FROM_LE_32(pFont->fontInit.hObjImg)); // get image for char 0 + pImg = (IMAGE *)LockMem(FROM_32(pFont->fontInit.hObjImg)); // get image for char 0 if (!TinselV2) - pImg->hImgPal = TO_LE_32(fontPal); + pImg->hImgPal = TO_32(fontPal); else pImg->hImgPal = 0; diff --git a/engines/tinsel/graphics.cpp b/engines/tinsel/graphics.cpp index 9b06b1a501..b917775360 100644 --- a/engines/tinsel/graphics.cpp +++ b/engines/tinsel/graphics.cpp @@ -73,7 +73,7 @@ uint8* psxPJCRLEUnwinder(uint16 imageWidth, uint16 imageHeight, uint8 *srcIdx) { while (remainingBlocks) { // Repeat until all blocks are decompressed if (!controlBits) { - controlData = READ_LE_UINT16(srcIdx); + controlData = READ_16(srcIdx); srcIdx += 2; // If bit 15 of controlData is enabled, compression data is type 1. @@ -92,7 +92,7 @@ uint8* psxPJCRLEUnwinder(uint16 imageWidth, uint16 imageHeight, uint8 *srcIdx) { // If there is compression, we need to fetch an index // to be treated as "base" for compression. if (compressionType != 0) { - controlData = READ_LE_UINT16(srcIdx); + controlData = READ_16(srcIdx); srcIdx += 2; baseIndex = controlData; } @@ -114,7 +114,7 @@ uint8* psxPJCRLEUnwinder(uint16 imageWidth, uint16 imageHeight, uint8 *srcIdx) { switch (compressionType) { case 0: // No compression, plain copy of indexes while (decremTiles) { - WRITE_LE_UINT16(dstIdx, READ_LE_UINT16(srcIdx)); + WRITE_LE_UINT16(dstIdx, READ_16(srcIdx)); srcIdx += 2; dstIdx += 2; decremTiles--; @@ -212,6 +212,82 @@ static void t0WrtNonZero(DRAWOBJECT *pObj, uint8 *srcP, uint8 *destP, bool apply } /** + * Straight rendering with transparency support, Mac variant + */ +static void MacDrawTiles(DRAWOBJECT *pObj, uint8 *srcP, uint8 *destP, bool applyClipping) { + int yClip = 0; + + if (applyClipping) { + // Adjust the height down to skip any bottom clipping + pObj->height -= pObj->botClip; + yClip = pObj->topClip; + } + + // Simple RLE-like scheme: the two first bytes of each data chunk determine + // if bytes should be repeated or copied. + // Example: 10 00 00 20 will repeat byte 0x0 0x10 times, and will copy 0x20 + // bytes from the input stream afterwards + + // Vertical loop + for (int y = 0; y < pObj->height; ++y) { + // Get the start of the next line output + uint8 *tempDest = destP; + + int leftClip = applyClipping ? pObj->leftClip : 0; + int rightClip = applyClipping ? pObj->rightClip : 0; + + // Horizontal loop + for (int x = 0; x < pObj->width; ) { + byte repeatBytes = *srcP++; + + if (repeatBytes) { + uint clipAmount = MIN<int>(repeatBytes, leftClip); + leftClip -= clipAmount; + x += clipAmount; + + // Repeat of a given color + byte color = *srcP++; + int runLength = repeatBytes - clipAmount; + int rptLength = MAX(MIN(runLength, pObj->width - rightClip - x), 0); + if (yClip == 0) { + if (color != 0) + memset(tempDest, color, rptLength); + tempDest += rptLength; + } + + x += runLength; + } else { + // Copy a specified sequence length of pixels + byte copyBytes = *srcP++; + + uint clipAmount = MIN<int>(copyBytes, leftClip); + leftClip -= clipAmount; + x += clipAmount; + srcP += clipAmount; + + int runLength = copyBytes - clipAmount; + int rptLength = MAX(MIN(runLength, pObj->width - rightClip - x), 0); + if (yClip == 0) { + memmove(tempDest, srcP, rptLength); + tempDest += rptLength; + } + + int overflow = (copyBytes % 2) == 0 ? 0 : 2 - (copyBytes % 2); + x += runLength; + srcP += runLength + overflow; + } + } // horizontal loop + + // Move to next line + if (yClip > 0) + --yClip; + else + destP += SCREEN_WIDTH; + } // vertical loop +} + + +/** * Straight rendering with transparency support, PSX variant supporting also 4-BIT clut data */ static void PsxDrawTiles(DRAWOBJECT *pObj, uint8 *srcP, uint8 *destP, bool applyClipping, bool fourBitClut, uint32 psxSkipBytes, byte *psxMapperTable, bool transparency) { @@ -822,120 +898,59 @@ void DrawObject(DRAWOBJECT *pObj) { // Handle various draw types uint8 typeId = pObj->flags & 0xff; + int packType = pObj->flags >> 14; // TinselV2 - if (TinselV2) { - // Tinsel v2 decoders - // Initial switch statement for the different bit packing types - int packType = pObj->flags >> 14; - - if (packType == 0) { - // No color packing - switch (typeId) { - case 0x01: - case 0x11: - case 0x41: - case 0x51: - case 0x81: - case 0xC1: - t2WrtNonZero(pObj, srcPtr, destPtr, typeId >= 0x40, (typeId & 0x10) != 0); - break; - case 0x02: - case 0x42: - // This renderer called 'RlWrtAll', but is the same as t2WrtNonZero - t2WrtNonZero(pObj, srcPtr, destPtr, typeId >= 0x40, false); - break; - case 0x04: - case 0x44: - // WrtConst with/without clipping - WrtConst(pObj, destPtr, typeId == 0x44); - break; - case 0x08: - case 0x48: - WrtAll(pObj, srcPtr, destPtr, typeId >= 0x40); - break; - case 0x84: - case 0xC4: - // WrtTrans with/without clipping - WrtTrans(pObj, destPtr, typeId == 0xC4); - break; - default: - error("Unknown drawing type %d", typeId); - } - } else { - // 1 = 16 from 240 - // 2 = 16 from 224 - // 3 = variable color - if (packType == 1) pObj->baseCol = 0xF0; - else if (packType == 2) pObj->baseCol = 0xE0; - - PackedWrtNonZero(pObj, srcPtr, destPtr, (pObj->flags & DMA_CLIP) != 0, - (pObj->flags & DMA_FLIPH), packType); - } + if (TinselV2 && packType != 0) { + // Color packing for TinselV2 + + if (packType == 1) + pObj->baseCol = 0xF0; // 16 from 240 + else if (packType == 2) + pObj->baseCol = 0xE0; // 16 from 224 + // 3 = variable color - } else if (TinselV1PSX) { - // Tinsel v1 decoders, PSX specific variants + PackedWrtNonZero(pObj, srcPtr, destPtr, (pObj->flags & DMA_CLIP) != 0, + (pObj->flags & DMA_FLIPH), packType); + } else { switch (typeId) { - case 0x01: - case 0x41: - PsxDrawTiles(pObj, srcPtr, destPtr, typeId >= 0x40, psxFourBitClut, psxSkipBytes, psxMapperTable, true); - break; - case 0x08: - case 0x48: - PsxDrawTiles(pObj, srcPtr, destPtr, typeId >= 0x40, psxFourBitClut, psxSkipBytes, psxMapperTable, false); - break; - case 0x84: - case 0xC4: - // WrtTrans with/without clipping - WrtTrans(pObj, destPtr, typeId == 0xC4); - break; - case 0x04: - case 0x44: - // WrtConst with/without clipping - WrtConst(pObj, destPtr, typeId == 0x44); + case 0x01: // all versions, draw sprite without clipping + case 0x41: // all versions, draw sprite with clipping + case 0x02: // TinselV2, draw sprite without clipping + case 0x11: // TinselV2, draw sprite without clipping, flipped horizontally + case 0x42: // TinselV2, draw sprite with clipping + case 0x51: // TinselV2, draw sprite with clipping, flipped horizontally + case 0x81: // TinselV2, draw sprite with clipping + case 0xC1: // TinselV2, draw sprite with clipping + assert(TinselV2 || (typeId == 0x01 || typeId == 0x41)); + + if (TinselV2) + t2WrtNonZero(pObj, srcPtr, destPtr, typeId >= 0x40, (typeId & 0x10) != 0); + else if (TinselV1PSX) + PsxDrawTiles(pObj, srcPtr, destPtr, typeId == 0x41, psxFourBitClut, psxSkipBytes, psxMapperTable, true); + else if (TinselV1Mac) + MacDrawTiles(pObj, srcPtr, destPtr, typeId == 0x41); + else if (TinselV1) + WrtNonZero(pObj, srcPtr, destPtr, typeId == 0x41); + else if (TinselV0) + t0WrtNonZero(pObj, srcPtr, destPtr, typeId == 0x41); break; - default: - error("Unknown drawing type %d", typeId); - } - } else if (TinselV1) { - // Tinsel v1 decoders - switch (typeId) { - case 0x01: - case 0x08: - case 0x41: - case 0x48: - WrtNonZero(pObj, srcPtr, destPtr, typeId >= 0x40); + case 0x08: // draw background without clipping + case 0x48: // draw background with clipping + if (TinselV2 || TinselV1Mac || TinselV0) + WrtAll(pObj, srcPtr, destPtr, typeId == 0x48); + else if (TinselV1PSX) + PsxDrawTiles(pObj, srcPtr, destPtr, typeId == 0x48, psxFourBitClut, psxSkipBytes, psxMapperTable, false); + else if (TinselV1) + WrtNonZero(pObj, srcPtr, destPtr, typeId == 0x48); break; - - case 0x04: - case 0x44: - // WrtConst with/without clipping + case 0x04: // fill with constant color without clipping + case 0x44: // fill with constant color with clipping WrtConst(pObj, destPtr, typeId == 0x44); break; - - case 0x84: - case 0xC4: - // WrtTrans with/without clipping + case 0x84: // draw transparent surface without clipping + case 0xC4: // draw transparent surface with clipping WrtTrans(pObj, destPtr, typeId == 0xC4); break; - - default: - error("Unknown drawing type %d", typeId); - } - } else { - // Tinsel v0 decoders - switch (typeId) { - case 0x01: - case 0x41: - t0WrtNonZero(pObj, srcPtr, destPtr, typeId >= 0x40); - break; - case 0x08: - case 0x48: - WrtAll(pObj, srcPtr, destPtr, typeId >= 0x40); - break; - case 0x84: - case 0xC4: - WrtTrans(pObj, destPtr, (typeId & 0x40) != 0); - break; default: error("Unknown drawing type %d", typeId); } diff --git a/engines/tinsel/handle.cpp b/engines/tinsel/handle.cpp index c3089db990..3921414b01 100644 --- a/engines/tinsel/handle.cpp +++ b/engines/tinsel/handle.cpp @@ -30,6 +30,7 @@ #include "tinsel/dw.h" #include "tinsel/handle.h" #include "tinsel/heapmem.h" // heap memory manager +#include "tinsel/scn.h" // for the DW1 Mac resource handler #include "tinsel/timers.h" // for DwGetCurrentTime() #include "tinsel/tinsel.h" #include "tinsel/scene.h" @@ -85,7 +86,6 @@ static char g_szCdPlayFile[100]; static void LoadFile(MEMHANDLE *pH); // load a memory block as a file - /** * Loads the graphics handle table index file and preloads all the * permanent graphics etc. @@ -99,14 +99,16 @@ void SetupHandleTable() { MEMHANDLE *pH; TinselFile f; - if (f.open(TinselV1PSX? PSX_INDEX_FILENAME : INDEX_FILENAME)) { + const char *indexFileName = TinselV1PSX ? PSX_INDEX_FILENAME : INDEX_FILENAME; + + if (f.open(indexFileName)) { // get size of index file len = f.size(); if (len > 0) { if ((len % RECORD_SIZE) != 0) { // index file is corrupt - error(FILE_IS_CORRUPT, TinselV1PSX? PSX_INDEX_FILENAME : INDEX_FILENAME); + error(FILE_IS_CORRUPT, indexFileName); } // calc number of handles @@ -132,16 +134,16 @@ void SetupHandleTable() { if (f.eos() || f.err()) { // index file is corrupt - error(FILE_IS_CORRUPT, (TinselV1PSX? PSX_INDEX_FILENAME : INDEX_FILENAME)); + error(FILE_IS_CORRUPT, indexFileName); } // close the file f.close(); } else { // index file is corrupt - error(FILE_IS_CORRUPT, (TinselV1PSX? PSX_INDEX_FILENAME : INDEX_FILENAME)); + error(FILE_IS_CORRUPT, indexFileName); } } else { // cannot find the index file - error(CANNOT_FIND_FILE, (TinselV1PSX? PSX_INDEX_FILENAME : INDEX_FILENAME)); + error(CANNOT_FIND_FILE, indexFileName); } // allocate memory nodes and load all permanent graphics @@ -298,7 +300,7 @@ void LoadFile(MEMHANDLE *pH) { // discardable - unlock the memory MemoryUnlock(pH->_node); - + // set the loaded flag pH->filesize |= fLoaded; @@ -320,6 +322,7 @@ void LoadFile(MEMHANDLE *pH) { */ byte *LockMem(SCNHANDLE offset) { uint32 handle = offset >> SCNHANDLE_SHIFT; // calc memory handle to use + //debug("Locking offset of type %d (%x), offset %d, handle %d", (offset & HANDLEMASK) >> SCNHANDLE_SHIFT, (offset & HANDLEMASK) >> SCNHANDLE_SHIFT, offset & OFFSETMASK, handle); MEMHANDLE *pH; // points to table entry // range check the memory handle diff --git a/engines/tinsel/multiobj.cpp b/engines/tinsel/multiobj.cpp index c48fefdd22..37769a7819 100644 --- a/engines/tinsel/multiobj.cpp +++ b/engines/tinsel/multiobj.cpp @@ -40,22 +40,22 @@ OBJECT *MultiInitObject(const MULTI_INIT *pInitTbl) { OBJECT *pFirst, *pObj; // object pointers FRAME *pFrame; // list of images for the multi-part object - if (FROM_LE_32(pInitTbl->hMulFrame)) { + if (FROM_32(pInitTbl->hMulFrame)) { // we have a frame handle - pFrame = (FRAME *)LockMem(FROM_LE_32(pInitTbl->hMulFrame)); + pFrame = (FRAME *)LockMem(FROM_32(pInitTbl->hMulFrame)); - obj_init.hObjImg = READ_LE_UINT32(pFrame); // first objects shape + obj_init.hObjImg = READ_32(pFrame); // first objects shape } else { // this must be a animation list for a NULL object pFrame = NULL; obj_init.hObjImg = 0; // first objects shape } // init the object init table - obj_init.objFlags = (int)FROM_LE_32(pInitTbl->mulFlags); // all objects have same flags - obj_init.objID = (int)FROM_LE_32(pInitTbl->mulID); // all objects have same ID - obj_init.objX = (int)FROM_LE_32(pInitTbl->mulX); // all objects have same X ani pos - obj_init.objY = (int)FROM_LE_32(pInitTbl->mulY); // all objects have same Y ani pos - obj_init.objZ = (int)FROM_LE_32(pInitTbl->mulZ); // all objects have same Z pos + obj_init.objFlags = (int)FROM_32(pInitTbl->mulFlags); // all objects have same flags + obj_init.objID = (int)FROM_32(pInitTbl->mulID); // all objects have same ID + obj_init.objX = (int)FROM_32(pInitTbl->mulX); // all objects have same X ani pos + obj_init.objY = (int)FROM_32(pInitTbl->mulY); // all objects have same Y ani pos + obj_init.objZ = (int)FROM_32(pInitTbl->mulZ); // all objects have same Z pos // create and init the first object pObj = pFirst = InitObject(&obj_init); @@ -65,9 +65,9 @@ OBJECT *MultiInitObject(const MULTI_INIT *pInitTbl) { pFrame++; - while (READ_LE_UINT32(pFrame) != 0) { + while (READ_32(pFrame) != 0) { // set next objects shape - obj_init.hObjImg = READ_LE_UINT32(pFrame); + obj_init.hObjImg = READ_32(pFrame); // create next object and link to previous pObj = pObj->pSlave = InitObject(&obj_init); @@ -378,9 +378,9 @@ void MultiReshape(OBJECT *pMultiObj) { // update previous pMultiObj->hMirror = hFrame; - while (READ_LE_UINT32(pFrame) != 0 && pMultiObj != NULL) { + while (READ_32(pFrame) != 0 && pMultiObj != NULL) { // a normal image - update the current object with this image - AnimateObject(pMultiObj, READ_LE_UINT32(pFrame)); + AnimateObject(pMultiObj, READ_32(pFrame)); // move to next image for this frame pFrame++; diff --git a/engines/tinsel/music.cpp b/engines/tinsel/music.cpp index a226feb656..dab2a897fc 100644 --- a/engines/tinsel/music.cpp +++ b/engines/tinsel/music.cpp @@ -131,13 +131,15 @@ bool PlayMidiSequence(uint32 dwFileOffset, bool bLoop) { g_currentMidi = dwFileOffset; g_currentLoop = bLoop; - if (_vm->_config->_musicVolume != 0) { - bool mute = false; - if (ConfMan.hasKey("mute")) - mute = ConfMan.getBool("mute"); + bool mute = false; + if (ConfMan.hasKey("mute")) + mute = ConfMan.getBool("mute"); - SetMidiVolume(mute ? 0 : _vm->_config->_musicVolume); - } + // The Macintosh version of DW1 uses raw PCM for music + if (TinselV1Mac) + return _vm->_sound->playDW1MacMusic(dwFileOffset); + + SetMidiVolume(mute ? 0 : _vm->_config->_musicVolume); // the index and length of the last tune loaded uint32 dwSeqLen = 0; // length of the sequence @@ -270,27 +272,7 @@ int GetMidiVolume() { */ void SetMidiVolume(int vol) { assert(vol >= 0 && vol <= Audio::Mixer::kMaxChannelVolume); - - static int priorVolMusic = 0; // FIXME: Avoid non-const global vars - - if (vol == 0 && priorVolMusic == 0) { - // Nothing to do - } else if (vol == 0 && priorVolMusic != 0) { - // Stop current midi sequence - StopMidi(); - _vm->_midiMusic->setVolume(vol); - } else if (vol != 0 && priorVolMusic == 0) { - // Perhaps restart last midi sequence - if (g_currentLoop) - PlayMidiSequence(g_currentMidi, true); - - _vm->_midiMusic->setVolume(vol); - } else if (vol != 0 && priorVolMusic != 0) { - // Alter current volume - _vm->_midiMusic->setVolume(vol); - } - - priorVolMusic = vol; + _vm->_midiMusic->setVolume(vol); } /** @@ -300,7 +282,12 @@ void OpenMidiFiles() { Common::File midiStream; // Demo version has no midi file - if ((_vm->getFeatures() & GF_DEMO) || (TinselVersion == TINSEL_V2)) + if (TinselV0 || TinselV2) + return; + + // The Macintosh version of DW1 does not use MIDI for music. + // It uses PCM music instead, which is quite big to be preloaded here. + if (TinselV1Mac) return; if (g_midiBuffer.pDat) @@ -806,8 +793,8 @@ bool PCMMusicPlayer::getNextChunk() { // Set parameters for this chunk of music id = _scriptNum; while (id--) - script = scriptBuffer + READ_LE_UINT32(script); - snum = FROM_LE_32(script[_scriptIndex++]); + script = scriptBuffer + READ_32(script); + snum = FROM_32(script[_scriptIndex++]); if (snum == MUSIC_JUMP || snum == MUSIC_END) { // Let usual code sort it out! @@ -819,11 +806,11 @@ bool PCMMusicPlayer::getNextChunk() { musicSegments = (MusicSegment *) LockMem(_hSegment); - assert(FROM_LE_32(musicSegments[snum].numChannels) == 1); - assert(FROM_LE_32(musicSegments[snum].bitsPerSample) == 16); + assert(FROM_32(musicSegments[snum].numChannels) == 1); + assert(FROM_32(musicSegments[snum].bitsPerSample) == 16); - sampleOffset = FROM_LE_32(musicSegments[snum].sampleOffset); - sampleLength = FROM_LE_32(musicSegments[snum].sampleLength); + sampleOffset = FROM_32(musicSegments[snum].sampleOffset); + sampleLength = FROM_32(musicSegments[snum].sampleLength); sampleCLength = (((sampleLength + 63) & ~63)*33)/64; if (!file.open(_filename)) @@ -861,14 +848,14 @@ bool PCMMusicPlayer::getNextChunk() { id = _scriptNum; while (id--) - script = scriptBuffer + READ_LE_UINT32(script); - snum = FROM_LE_32(script[_scriptIndex]); + script = scriptBuffer + READ_32(script); + snum = FROM_32(script[_scriptIndex]); if (snum == MUSIC_END) { _state = S_END2; } else { if (snum == MUSIC_JUMP) - _scriptIndex = FROM_LE_32(script[_scriptIndex+1]); + _scriptIndex = FROM_32(script[_scriptIndex+1]); _state = _forcePlay ? S_NEW : S_NEXT; _forcePlay = false; @@ -933,14 +920,12 @@ void RestoreMidiFacts(SCNHANDLE Midi, bool Loop) { g_currentMidi = Midi; g_currentLoop = Loop; - if (_vm->_config->_musicVolume != 0 && Loop) { - bool mute = false; - if (ConfMan.hasKey("mute")) - mute = ConfMan.getBool("mute"); + bool mute = false; + if (ConfMan.hasKey("mute")) + mute = ConfMan.getBool("mute"); - PlayMidiSequence(g_currentMidi, true); - SetMidiVolume(mute ? 0 : _vm->_config->_musicVolume); - } + PlayMidiSequence(g_currentMidi, true); + SetMidiVolume(mute ? 0 : _vm->_config->_musicVolume); } #if 0 diff --git a/engines/tinsel/object.cpp b/engines/tinsel/object.cpp index cbe5b0a88f..cbf1c86649 100644 --- a/engines/tinsel/object.cpp +++ b/engines/tinsel/object.cpp @@ -302,23 +302,23 @@ void GetAniOffset(SCNHANDLE hImg, int flags, int *pAniX, int *pAniY) { const IMAGE *pImg = (const IMAGE *)LockMem(hImg); // set ani X - *pAniX = (int16) FROM_LE_16(pImg->anioffX); + *pAniX = (int16) FROM_16(pImg->anioffX); // set ani Y - *pAniY = (int16) FROM_LE_16(pImg->anioffY); + *pAniY = (int16) FROM_16(pImg->anioffY); if (flags & DMA_FLIPH) { // we are flipped horizontally // set ani X = -ani X + width - 1 - *pAniX = -*pAniX + FROM_LE_16(pImg->imgWidth) - 1; + *pAniX = -*pAniX + FROM_16(pImg->imgWidth) - 1; } if (flags & DMA_FLIPV) { // we are flipped vertically // set ani Y = -ani Y + height - 1 - *pAniY = -*pAniY + (FROM_LE_16(pImg->imgHeight) & ~C16_FLAG_MASK) - 1; + *pAniY = -*pAniY + (FROM_16(pImg->imgHeight) & ~C16_FLAG_MASK) - 1; } } else // null image @@ -385,13 +385,13 @@ OBJECT *InitObject(const OBJ_INIT *pInitTbl) { pObj->pPal = pPalQ; // set objects size - pObj->width = FROM_LE_16(pImg->imgWidth); - pObj->height = FROM_LE_16(pImg->imgHeight) & ~C16_FLAG_MASK; + pObj->width = FROM_16(pImg->imgWidth); + pObj->height = FROM_16(pImg->imgHeight) & ~C16_FLAG_MASK; pObj->flags &= ~C16_FLAG_MASK; - pObj->flags |= FROM_LE_16(pImg->imgHeight) & C16_FLAG_MASK; + pObj->flags |= FROM_16(pImg->imgHeight) & C16_FLAG_MASK; // set objects bitmap definition - pObj->hBits = FROM_LE_32(pImg->hImgBits); + pObj->hBits = FROM_32(pImg->hImgBits); // get animation offset of object GetAniOffset(pObj->hImg, pInitTbl->objFlags, &aniX, &aniY); @@ -442,13 +442,13 @@ void AnimateObjectFlags(OBJECT *pAniObj, int newflags, SCNHANDLE hNewImg) { const IMAGE *pNewImg = (IMAGE *)LockMem(hNewImg); // setup new shape - pAniObj->width = FROM_LE_16(pNewImg->imgWidth); - pAniObj->height = FROM_LE_16(pNewImg->imgHeight) & ~C16_FLAG_MASK; + pAniObj->width = FROM_16(pNewImg->imgWidth); + pAniObj->height = FROM_16(pNewImg->imgHeight) & ~C16_FLAG_MASK; newflags &= ~C16_FLAG_MASK; - newflags |= FROM_LE_16(pNewImg->imgHeight) & C16_FLAG_MASK; + newflags |= FROM_16(pNewImg->imgHeight) & C16_FLAG_MASK; // set objects bitmap definition - pAniObj->hBits = FROM_LE_32(pNewImg->hImgBits); + pAniObj->hBits = FROM_32(pNewImg->hImgBits); } else { // null image pAniObj->width = 0; pAniObj->height = 0; @@ -494,7 +494,7 @@ OBJECT *RectangleObject(SCNHANDLE hPal, int color, int width, int height) { OBJECT *pRect = InitObject(&rectObj); // allocate a palette for this object - pPalQ = AllocPalette(hPal); + pPalQ = AllocPalette(FROM_32(hPal)); // make sure palette allocated assert(pPalQ != NULL); diff --git a/engines/tinsel/palette.cpp b/engines/tinsel/palette.cpp index e6c9467fab..04018172c0 100644 --- a/engines/tinsel/palette.cpp +++ b/engines/tinsel/palette.cpp @@ -102,7 +102,7 @@ void psxPaletteMapper(PALQ *originalPal, uint8 *psxClut, byte *mapperTable) { memset(mapperTable, 0, 16); for (int j = 1; j < 16; j++) { - clutEntry = READ_LE_UINT16(psxClut + (sizeof(uint16) * j)); + clutEntry = READ_16(psxClut + (sizeof(uint16) * j)); if (clutEntry) { if (clutEntry == 0x7EC0) { // This is an already known value, used by the in-game text mapperTable[j] = 232; @@ -110,7 +110,7 @@ void psxPaletteMapper(PALQ *originalPal, uint8 *psxClut, byte *mapperTable) { } // Check for correspondent color - for (uint i = 0; (i < FROM_LE_32(pal->numColors)) && !colorFound; i++) { + for (uint i = 0; (i < FROM_32(pal->numColors)) && !colorFound; i++) { // get R G B values in the same way as psx format converters uint16 psxEquivalent = TINSEL_PSX_RGB(TINSEL_GetRValue(pal->palRGB[i]) >> 3, TINSEL_GetGValue(pal->palRGB[i]) >> 3, TINSEL_GetBValue(pal->palRGB[i]) >> 3); @@ -152,7 +152,7 @@ void PalettesToVideoDAC() { // we are using a palette handle // get hardware palette pointer - pPalette = (const PALETTE *)LockMem(pDACtail->pal.hRGBarray); + pPalette = (const PALETTE *)LockMem(FROM_32(pDACtail->pal.hRGBarray)); // get RGB pointer pColors = pPalette->palRGB; @@ -170,6 +170,12 @@ void PalettesToVideoDAC() { pal[i * 3 + 2] = TINSEL_GetBValue(pColors[i]); } + // In DW1 Mac, color 254 should be black, like in the PC version. + // We fix it here. + if (TinselV1Mac) { + pal[254 * 3 + 0] = pal[254 * 3 + 1] = pal[254 * 3 + 2] = 0; + } + // update the system palette g_system->getPaletteManager()->setPalette(pal, pDACtail->destDACindex, pDACtail->numColors); @@ -298,7 +304,7 @@ PALQ *AllocPalette(SCNHANDLE hNewPal) { PALETTE *pNewPal; // get pointer to new palette - pNewPal = (PALETTE *)LockMem(hNewPal); + pNewPal = (PALETTE *)LockMem(FROM_32(hNewPal)); // search all structs in palette allocator - see if palette already allocated for (p = g_palAllocData; p < g_palAllocData + NUM_PALETTES; p++) { @@ -318,7 +324,7 @@ PALQ *AllocPalette(SCNHANDLE hNewPal) { p->objCount = 1; // init number of objects using palette p->posInDAC = iDAC; // set palettes start pos in video DAC p->hPal = hNewPal; // set hardware palette data - p->numColors = FROM_LE_32(pNewPal->numColors); // set number of colors in palette + p->numColors = FROM_32(pNewPal->numColors); // set number of colors in palette if (TinselV2) // Copy all the colors @@ -430,24 +436,24 @@ void SwapPalette(PALQ *pPalQ, SCNHANDLE hNewPal) { // validate palette Q pointer assert(pPalQ >= g_palAllocData && pPalQ <= g_palAllocData + NUM_PALETTES - 1); - if (pPalQ->numColors >= (int)FROM_LE_32(pNewPal->numColors)) { + if (pPalQ->numColors >= (int)FROM_32(pNewPal->numColors)) { // new palette will fit the slot // install new palette pPalQ->hPal = hNewPal; if (TinselV2) { - pPalQ->numColors = FROM_LE_32(pNewPal->numColors); + pPalQ->numColors = FROM_32(pNewPal->numColors); // Copy all the colors - memcpy(pPalQ->palRGB, pNewPal->palRGB, FROM_LE_32(pNewPal->numColors) * sizeof(COLORREF)); + memcpy(pPalQ->palRGB, pNewPal->palRGB, FROM_32(pNewPal->numColors) * sizeof(COLORREF)); if (!pPalQ->bFading) // Q the change to the video DAC - UpdateDACqueue(pPalQ->posInDAC, FROM_LE_32(pNewPal->numColors), pPalQ->palRGB); + UpdateDACqueue(pPalQ->posInDAC, FROM_32(pNewPal->numColors), pPalQ->palRGB); } else { // Q the change to the video DAC - UpdateDACqueueHandle(pPalQ->posInDAC, FROM_LE_32(pNewPal->numColors), hNewPal); + UpdateDACqueueHandle(pPalQ->posInDAC, FROM_32(pNewPal->numColors), hNewPal); } } else { // # colors are different - will have to update all following palette entries @@ -546,7 +552,7 @@ void CreateTranslucentPalette(SCNHANDLE hPalette) { // leave background color alone g_transPalette[0] = 0; - for (uint i = 0; i < FROM_LE_32(pPal->numColors); i++) { + for (uint i = 0; i < FROM_32(pPal->numColors); i++) { // get the RGB color model values uint8 red = TINSEL_GetRValue(pPal->palRGB[i]); uint8 green = TINSEL_GetGValue(pPal->palRGB[i]); @@ -574,7 +580,7 @@ void CreateGhostPalette(SCNHANDLE hPalette) { // leave background color alone g_ghostPalette[0] = 0; - for (i = 0; i < (int)FROM_LE_32(pPal->numColors); i++) { + for (i = 0; i < (int)FROM_32(pPal->numColors); i++) { // get the RGB color model values uint8 red = TINSEL_GetRValue(pPal->palRGB[i]); uint8 green = TINSEL_GetGValue(pPal->palRGB[i]); diff --git a/engines/tinsel/palette.h b/engines/tinsel/palette.h index af58a7ffbd..49a78ae236 100644 --- a/engines/tinsel/palette.h +++ b/engines/tinsel/palette.h @@ -30,11 +30,11 @@ namespace Tinsel { typedef uint32 COLORREF; -#define TINSEL_RGB(r,g,b) ((COLORREF)TO_LE_32(((uint8)(r)|((uint16)(g)<<8))|(((uint32)(uint8)(b))<<16))) +#define TINSEL_RGB(r,g,b) ((COLORREF)TO_32(((uint8)(r)|((uint16)(g)<<8))|(((uint32)(uint8)(b))<<16))) -#define TINSEL_GetRValue(rgb) ((uint8)(FROM_LE_32(rgb))) -#define TINSEL_GetGValue(rgb) ((uint8)(((uint16)(FROM_LE_32(rgb)))>>8)) -#define TINSEL_GetBValue(rgb) ((uint8)((FROM_LE_32(rgb))>>16)) +#define TINSEL_GetRValue(rgb) ((uint8)(FROM_32(rgb))) +#define TINSEL_GetGValue(rgb) ((uint8)(((uint16)(FROM_32(rgb)))>>8)) +#define TINSEL_GetBValue(rgb) ((uint8)((FROM_32(rgb))>>16)) #define TINSEL_PSX_RGB(r,g,b) ((uint16)(((uint8)(r))|((uint16)(g)<<5)|(((uint16)(b))<<10))) diff --git a/engines/tinsel/pcode.cpp b/engines/tinsel/pcode.cpp index 60f04b47fd..7e439e83a9 100644 --- a/engines/tinsel/pcode.cpp +++ b/engines/tinsel/pcode.cpp @@ -122,6 +122,8 @@ static uint32 g_hMasterScript; struct WorkaroundEntry { TinselEngineVersion version; ///< Engine version this workaround applies to bool scnFlag; ///< Only applicable for Tinsel 1 (DW 1) + bool isDemo; ///< Flags whether it's for a demo + Common::Platform platform; ///< Platform filter SCNHANDLE hCode; ///< Script to apply fragment to int ip; ///< Script offset to run this fragment before int numBytes; ///< Number of bytes in the script @@ -129,6 +131,7 @@ struct WorkaroundEntry { }; #define FRAGMENT_WORD(x) (byte)(x & 0xFF), (byte)(x >> 8) +#define FRAGMENT_DWORD(x) (byte)(x & 0xFF), (byte)(x >> 8), (byte)(x >> 16), (byte)(x >> 24) static const byte fragment1[] = {OP_ZERO, OP_GSTORE | OPSIZE16, 206, 0}; static const byte fragment2[] = {OP_LIBCALL | OPSIZE8, 110}; @@ -149,6 +152,10 @@ static const byte fragment12[] = {OP_JMPTRUE | OPSIZE16, FRAGMENT_WORD(1491), OP_IMM | OPSIZE16, FRAGMENT_WORD(322), OP_LIBCALL | OPSIZE8, 46, // Give back the whistle OP_JUMP | OPSIZE16, FRAGMENT_WORD(1568)}; static const byte fragment13[] = {OP_ZERO, OP_GSTORE | OPSIZE16, FRAGMENT_WORD(306)}; +static const byte fragment14[] = {OP_LIBCALL | OPSIZE8, 58, + OP_IMM, FRAGMENT_DWORD((42 << 23)), OP_ONE, OP_ZERO, OP_LIBCALL | OPSIZE8, 44, + OP_LIBCALL | OPSIZE8, 97, OP_JUMP | OPSIZE16, FRAGMENT_WORD(2220) +}; #undef FRAGMENT_WORD @@ -157,7 +164,7 @@ const WorkaroundEntry workaroundList[] = { // book back to the present. In the GRA version, it was global 373, // and was reset when he is returned to the past, but was forgotten // in the SCN version, so this ensures the flag is properly reset. - {TINSEL_V1, true, 427942095, 1, sizeof(fragment1), fragment1}, + {TINSEL_V1, true, false, Common::kPlatformUnknown, 427942095, 1, sizeof(fragment1), fragment1}, // DW1-GRA: Rincewind exiting the Inn is blocked by the luggage. // Whilst you can then move into walkable areas, saving and @@ -165,26 +172,26 @@ const WorkaroundEntry workaroundList[] = { // fragment turns off NPC blocking for the Outside Inn rooms so that // the luggage won't block Past Outside Inn. // See bug report #2525010. - {TINSEL_V1, false, 444622076, 0, sizeof(fragment2), fragment2}, + {TINSEL_V1, false, false, Common::kPlatformUnknown, 444622076, 0, sizeof(fragment2), fragment2}, // Present Outside Inn - {TINSEL_V1, false, 352600876, 0, sizeof(fragment2), fragment2}, + {TINSEL_V1, false, false, Common::kPlatformUnknown, 352600876, 0, sizeof(fragment2), fragment2}, // DW1-GRA: Talking to palace guards in Act 2 gives !!!HIGH // STRING||| - this happens if you initiate dialog with one of the // guards, but not the other. So these fragments provide the correct // talk parameters where needed. // See bug report #2831159. - {TINSEL_V1, false, 310506872, 463, sizeof(fragment4), fragment4}, - {TINSEL_V1, false, 310506872, 485, sizeof(fragment5), fragment5}, - {TINSEL_V1, false, 310506872, 513, sizeof(fragment6), fragment6}, - {TINSEL_V1, false, 310506872, 613, sizeof(fragment7), fragment7}, - {TINSEL_V1, false, 310506872, 641, sizeof(fragment8), fragment8}, + {TINSEL_V1, false, false, Common::kPlatformUnknown, 310506872, 463, sizeof(fragment4), fragment4}, + {TINSEL_V1, false, false, Common::kPlatformUnknown, 310506872, 485, sizeof(fragment5), fragment5}, + {TINSEL_V1, false, false, Common::kPlatformUnknown, 310506872, 513, sizeof(fragment6), fragment6}, + {TINSEL_V1, false, false, Common::kPlatformUnknown, 310506872, 613, sizeof(fragment7), fragment7}, + {TINSEL_V1, false, false, Common::kPlatformUnknown, 310506872, 641, sizeof(fragment8), fragment8}, // DW1-SCN: The script for the lovable street-Starfish does a // 'StopSample' after flicking the coin to ensure it's sound is // stopped, but which also accidentally can stop any active // conversation with the Amazon. - {TINSEL_V1, true, 394640351, 121, sizeof(fragment9), fragment9}, + {TINSEL_V1, true, false, Common::kPlatformUnknown, 394640351, 121, sizeof(fragment9), fragment9}, // DW2: In the garden, global #490 is set when the bees begin their // 'out of hive' animation, and reset when done. But if the game is @@ -197,25 +204,29 @@ const WorkaroundEntry workaroundList[] = { // * Stealing the mallets from the wizards (bug #2820788). // This fix ensures that the global is reset when the Garden scene // is loaded (both entering and restoring a game). - {TINSEL_V2, true, 2888147476U, 0, sizeof(fragment3), fragment3}, + {TINSEL_V2, true, false, Common::kPlatformUnknown, 2888147476U, 0, sizeof(fragment3), fragment3}, // DW1-GRA: Corrects text being drawn partially off-screen during // the blackboard description of the Librarian. - {TINSEL_V1, false, 293831402, 133, sizeof(fragment10), fragment10}, + {TINSEL_V1, false, false, Common::kPlatformUnknown, 293831402, 133, sizeof(fragment10), fragment10}, // DW1-GRA/SCN: Corrects the dead-end of being able to give the // whistle back to the pirate before giving him the parrot. // See bug report #2934211. - {TINSEL_V1, true, 352601285, 1569, sizeof(fragment11), fragment11}, - {TINSEL_V1, false, 352602304, 1488, sizeof(fragment12), fragment12}, + {TINSEL_V1, true, false, Common::kPlatformUnknown, 352601285, 1569, sizeof(fragment11), fragment11}, + {TINSEL_V1, false, false, Common::kPlatformUnknown, 352602304, 1488, sizeof(fragment12), fragment12}, // DW2: Corrects a bug with global 306 not being cleared if you leave // the marketplace scene whilst D'Blah is talking (even if it's not // actually audible); returning to the scene and clicking on him multiple // times would cause the game to crash - {TINSEL_V2, true, 1109294728, 0, sizeof(fragment13), fragment13}, + {TINSEL_V2, true, false, Common::kPlatformUnknown, 1109294728, 0, sizeof(fragment13), fragment13}, + + // DW1 PSX DEMO: Alters a script in the PSX DW1 demo to show the Idle animation scene rather than + // quitting the game when no user input happens for a while + {TINSEL_V1, true, true, Common::kPlatformPSX, 0, 2186, sizeof(fragment14), fragment14}, - {TINSEL_V0, false, 0, 0, 0, NULL} + {TINSEL_V0, false, false, Common::kPlatformUnknown, 0, 0, 0, NULL} }; //----------------- LOCAL GLOBAL DATA -------------------- @@ -409,7 +420,7 @@ void RegisterGlobals(int num) { g_numGlobals = num; g_hMasterScript = !TinselV2 ? 0 : - READ_LE_UINT32(FindChunk(MASTER_SCNHANDLE, CHUNK_MASTER_SCRIPT)); + READ_32(FindChunk(MASTER_SCNHANDLE, CHUNK_MASTER_SCRIPT)); // Allocate RAM for pGlobals and make sure it's allocated g_pGlobals = (int32 *)calloc(g_numGlobals, sizeof(int32)); @@ -582,6 +593,8 @@ void Interpret(CORO_PARAM, INT_CONTEXT *ic) { if ((wkEntry->version == TinselVersion) && (wkEntry->hCode == ic->hCode) && (wkEntry->ip == ip) && + (wkEntry->isDemo == _vm->getIsADGFDemo()) && + ((wkEntry->platform == Common::kPlatformUnknown) || (wkEntry->platform == _vm->getPlatform())) && (!TinselV1 || (wkEntry->scnFlag == ((_vm->getFeatures() & GF_SCNFILES) != 0)))) { // Point to start of workaround fragment ip = 0; diff --git a/engines/tinsel/play.cpp b/engines/tinsel/play.cpp index 9e0baa749e..e202278953 100644 --- a/engines/tinsel/play.cpp +++ b/engines/tinsel/play.cpp @@ -81,9 +81,9 @@ static void PokeInPalette(SCNHANDLE hMulFrame) { pFrame = (const FRAME *)LockMem(hMulFrame); // get pointer to image - pim = (IMAGE *)LockMem(READ_LE_UINT32(pFrame)); // handle to image + pim = (IMAGE *)LockMem(READ_32(pFrame)); // handle to image - pim->hImgPal = TO_LE_32(BgPal()); + pim->hImgPal = TO_32(BgPal()); } } @@ -96,12 +96,12 @@ void PokeInPalette(const MULTI_INIT *pmi) { // Could be an empty column if (pmi->hMulFrame) { - pFrame = (FRAME *)LockMem(FROM_LE_32(pmi->hMulFrame)); + pFrame = (FRAME *)LockMem(FROM_32(pmi->hMulFrame)); // get pointer to image - pim = (IMAGE *)LockMem(READ_LE_UINT32(pFrame)); // handle to image + pim = (IMAGE *)LockMem(READ_32(pFrame)); // handle to image - pim->hImgPal = TO_LE_32(BgPal()); + pim->hImgPal = TO_32(BgPal()); } } @@ -234,8 +234,8 @@ static void SoundReel(CORO_PARAM, SCNHANDLE hFilm, int column, int speed, PMULTI_INIT pmi; // MULTI_INIT structure pReel = GetReel(hFilm, actorCol - 1); - pmi = (PMULTI_INIT) LockMem(FROM_LE_32(pReel->mobj)); - _ctx->reelActor = (int32)FROM_LE_32(pmi->mulID); + pmi = (PMULTI_INIT) LockMem(FROM_32(pReel->mobj)); + _ctx->reelActor = (int32)FROM_32(pmi->mulID); } else _ctx->reelActor = 0; @@ -251,27 +251,27 @@ static void SoundReel(CORO_PARAM, SCNHANDLE hFilm, int column, int speed, pFilm = (FILM *)LockMem(hFilm); pReel = &pFilm->reels[column]; - pAni = (ANI_SCRIPT *)LockMem(FROM_LE_32(pReel->script)); + pAni = (ANI_SCRIPT *)LockMem(FROM_32(pReel->script)); if (_ctx->speed == -1) { - _ctx->speed = (ONE_SECOND/FROM_LE_32(pFilm->frate)); + _ctx->speed = (ONE_SECOND/FROM_32(pFilm->frate)); // Restored reel for (;;) { - if (FROM_LE_32(pAni[_ctx->frameNumber].op) == ANI_END) + if (FROM_32(pAni[_ctx->frameNumber].op) == ANI_END) break; - else if (FROM_LE_32(pAni[_ctx->frameNumber].op) == ANI_JUMP) { + else if (FROM_32(pAni[_ctx->frameNumber].op) == ANI_JUMP) { _ctx->frameNumber++; - _ctx->frameNumber += FROM_LE_32(pAni[_ctx->frameNumber].op); + _ctx->frameNumber += FROM_32(pAni[_ctx->frameNumber].op); break; } // Could check for the other stuff here // but they really dont happen // OH YES THEY DO - else if (FROM_LE_32(pAni[_ctx->frameNumber].op) == ANI_ADJUSTX - || FROM_LE_32(pAni[_ctx->frameNumber].op) == ANI_ADJUSTY) { + else if (FROM_32(pAni[_ctx->frameNumber].op) == ANI_ADJUSTX + || FROM_32(pAni[_ctx->frameNumber].op) == ANI_ADJUSTY) { _ctx->frameNumber += 2; - } else if (FROM_LE_32(pAni[_ctx->frameNumber].op) == ANI_ADJUSTXY) { + } else if (FROM_32(pAni[_ctx->frameNumber].op) == ANI_ADJUSTXY) { _ctx->frameNumber += 3; } else { // ANI_STOP, ANI_HIDE, ANI_HFLIP, @@ -281,7 +281,7 @@ static void SoundReel(CORO_PARAM, SCNHANDLE hFilm, int column, int speed, } } - switch (FROM_LE_32(pAni[_ctx->frameNumber].op)) { + switch (FROM_32(pAni[_ctx->frameNumber].op)) { case ANI_END: // Stop this sample if repeating if (_ctx->sampleNumber && _ctx->bLooped) @@ -292,9 +292,9 @@ static void SoundReel(CORO_PARAM, SCNHANDLE hFilm, int column, int speed, case ANI_JUMP: _ctx->frameNumber++; - assert((int32)FROM_LE_32(pAni[_ctx->frameNumber].op) < 0); + assert((int32)FROM_32(pAni[_ctx->frameNumber].op) < 0); - _ctx->frameNumber += FROM_LE_32(pAni[_ctx->frameNumber].op); + _ctx->frameNumber += FROM_32(pAni[_ctx->frameNumber].op); assert(_ctx->frameNumber >= 0); continue; @@ -329,15 +329,15 @@ static void SoundReel(CORO_PARAM, SCNHANDLE hFilm, int column, int speed, if (_ctx->sampleNumber) _vm->_sound->stopSpecSample(_ctx->sampleNumber, 0); - _ctx->sampleNumber = FROM_LE_32(pAni[_ctx->frameNumber++].op); + _ctx->sampleNumber = FROM_32(pAni[_ctx->frameNumber++].op); if (_ctx->sampleNumber > 0) _ctx->bLooped = false; else { _ctx->sampleNumber = ~_ctx->sampleNumber; _ctx->bLooped = true; } - x = (short)(FROM_LE_32(pAni[_ctx->frameNumber].op) >> 16); - y = (short)(FROM_LE_32(pAni[_ctx->frameNumber].op) & 0xffff); + x = (short)(FROM_32(pAni[_ctx->frameNumber].op) >> 16); + y = (short)(FROM_32(pAni[_ctx->frameNumber].op) & 0xffff); if (x == 0) x = -1; @@ -451,10 +451,10 @@ static void t1PlayReel(CORO_PARAM, const PPINIT *ppi) { _ctx->pfreel = &pfilm->reels[ppi->column]; // Get the MULTI_INIT structure - pmi = (const MULTI_INIT *)LockMem(FROM_LE_32(_ctx->pfreel->mobj)); + pmi = (const MULTI_INIT *)LockMem(FROM_32(_ctx->pfreel->mobj)); // Save actor's ID - _ctx->reelActor = (int32)FROM_LE_32(pmi->mulID); + _ctx->reelActor = (int32)FROM_32(pmi->mulID); /**** New (experimental? bit 5/1/95 ****/ if (!TinselV0 && !actorAlive(_ctx->reelActor)) @@ -488,7 +488,7 @@ static void t1PlayReel(CORO_PARAM, const PPINIT *ppi) { return; // Poke in the background palette - PokeInPalette(FROM_LE_32(pmi->hMulFrame)); + PokeInPalette(FROM_32(pmi->hMulFrame)); // Set up and insert the multi-object _ctx->pPlayObj = MultiInitObject(pmi); @@ -534,7 +534,7 @@ static void t1PlayReel(CORO_PARAM, const PPINIT *ppi) { if (ppi->actorid == 0 && !actorAlive(_ctx->reelActor)) _ctx->lifeNoMatter = true; - InitStepAnimScript(&_ctx->thisAnim, _ctx->pPlayObj, FROM_LE_32(_ctx->pfreel->script), ppi->speed); + InitStepAnimScript(&_ctx->thisAnim, _ctx->pPlayObj, FROM_32(_ctx->pfreel->script), ppi->speed); // If first column, set Z position as per // Otherwise, column 0's + column number @@ -706,16 +706,16 @@ static void t2PlayReel(CORO_PARAM, int x, int y, bool bRestore, int speed, SCNHA // Get the reel and MULTI_INIT structure _ctx->pFreel = GetReel(hFilm, column); - _ctx->pmi = (MULTI_INIT *)LockMem(FROM_LE_32(_ctx->pFreel->mobj)); + _ctx->pmi = (MULTI_INIT *)LockMem(FROM_32(_ctx->pFreel->mobj)); - if ((int32)FROM_LE_32(_ctx->pmi->mulID) == -2) { + if ((int32)FROM_32(_ctx->pmi->mulID) == -2) { CORO_INVOKE_ARGS(SoundReel, (CORO_SUBCTX, hFilm, column, speed, myescEvent, - FROM_LE_32(_ctx->pmi->otherFlags) & OTH_RELATEDACTOR)); + FROM_32(_ctx->pmi->otherFlags) & OTH_RELATEDACTOR)); return; } // Save actor's ID - _ctx->reelActor = FROM_LE_32(_ctx->pmi->mulID); + _ctx->reelActor = FROM_32(_ctx->pmi->mulID); UpdateActorEsc(_ctx->reelActor, myescEvent); @@ -759,8 +759,8 @@ static void t2PlayReel(CORO_PARAM, int x, int y, bool bRestore, int speed, SCNHA // Set ghost bit if wanted if (ActorIsGhost(_ctx->reelActor)) { - assert(FROM_LE_32(_ctx->pmi->mulFlags) == DMA_WNZ || FROM_LE_32(_ctx->pmi->mulFlags) == (DMA_WNZ | DMA_GHOST)); - _ctx->pmi->mulFlags = TO_LE_32(FROM_LE_32(_ctx->pmi->mulFlags) | DMA_GHOST); + assert(FROM_32(_ctx->pmi->mulFlags) == DMA_WNZ || FROM_32(_ctx->pmi->mulFlags) == (DMA_WNZ | DMA_GHOST)); + _ctx->pmi->mulFlags = TO_32(FROM_32(_ctx->pmi->mulFlags) | DMA_GHOST); } // Set up and insert the multi-object @@ -793,10 +793,10 @@ static void t2PlayReel(CORO_PARAM, int x, int y, bool bRestore, int speed, SCNHA /* * Sort out x and y */ - assert( ((FROM_LE_32(_ctx->pmi->otherFlags) & OTH_RELATIVE) && !(FROM_LE_32(_ctx->pmi->otherFlags) & OTH_ABSOLUTE)) - || ((FROM_LE_32(_ctx->pmi->otherFlags) & OTH_ABSOLUTE) && !(FROM_LE_32(_ctx->pmi->otherFlags) & OTH_RELATIVE)) ); + assert( ((FROM_32(_ctx->pmi->otherFlags) & OTH_RELATIVE) && !(FROM_32(_ctx->pmi->otherFlags) & OTH_ABSOLUTE)) + || ((FROM_32(_ctx->pmi->otherFlags) & OTH_ABSOLUTE) && !(FROM_32(_ctx->pmi->otherFlags) & OTH_RELATIVE)) ); - _ctx->bRelative = FROM_LE_32(_ctx->pmi->otherFlags) & OTH_RELATIVE; + _ctx->bRelative = FROM_32(_ctx->pmi->otherFlags) & OTH_RELATIVE; if (_ctx->bRelative) { // Use actor's position. If (x, y) specified, move the actor. @@ -808,7 +808,7 @@ static void t2PlayReel(CORO_PARAM, int x, int y, bool bRestore, int speed, SCNHA x = y = 0; // Use (0,0) if no specified // Add embedded co-ords - MultiSetAniXY(_ctx->pPlayObj, x + FROM_LE_32(_ctx->pmi->mulX), y + FROM_LE_32(_ctx->pmi->mulY)); + MultiSetAniXY(_ctx->pPlayObj, x + FROM_32(_ctx->pmi->mulX), y + FROM_32(_ctx->pmi->mulY)); /* * Sort out z @@ -824,10 +824,10 @@ static void t2PlayReel(CORO_PARAM, int x, int y, bool bRestore, int speed, SCNHA // N.B. It HAS been ensured that the first column gets here first - if ((int32)FROM_LE_32(_ctx->pmi->mulZ) != -1) { + if ((int32)FROM_32(_ctx->pmi->mulZ) != -1) { // Z override in script - baseZfact = FROM_LE_32(_ctx->pmi->mulZ); + baseZfact = FROM_32(_ctx->pmi->mulZ); baseZposn = (baseZfact << ZSHIFT) + MultiLowest(_ctx->pPlayObj); if (bTop) baseZposn += Z_TOPPLAY; @@ -850,7 +850,7 @@ static void t2PlayReel(CORO_PARAM, int x, int y, bool bRestore, int speed, SCNHA * another reel starts up for this actor, * or the actor gets killed. */ - InitStepAnimScript(&_ctx->thisAnim, _ctx->pPlayObj, FROM_LE_32(_ctx->pFreel->script), speed); + InitStepAnimScript(&_ctx->thisAnim, _ctx->pPlayObj, FROM_32(_ctx->pFreel->script), speed); if (bRestore || (ActorEsc(_ctx->reelActor) == true && ActorEev(_ctx->reelActor) != GetEscEvents())) { @@ -952,10 +952,10 @@ void NewestFilm(SCNHANDLE film, const FREEL *reel) { const MULTI_INIT *pmi; // MULTI_INIT structure // Get the MULTI_INIT structure - pmi = (const MULTI_INIT *)LockMem(FROM_LE_32(reel->mobj)); + pmi = (const MULTI_INIT *)LockMem(FROM_32(reel->mobj)); - if (!TinselV2 || ((int32)FROM_LE_32(pmi->mulID) != -2)) - SetActorLatestFilm((int32)FROM_LE_32(pmi->mulID), film); + if (!TinselV2 || ((int32)FROM_32(pmi->mulID) != -2)) + SetActorLatestFilm((int32)FROM_32(pmi->mulID), film); } // ******************************************************* @@ -988,7 +988,7 @@ void PlayFilm(CORO_PARAM, SCNHANDLE hFilm, int x, int y, int actorid, bool splay ppi.y = y; ppi.z = 0; ppi.bRestore = false; - ppi.speed = (ONE_SECOND / FROM_LE_32(pFilm->frate)); + ppi.speed = (ONE_SECOND / FROM_32(pFilm->frate)); ppi.actorid = actorid; ppi.splay = splay; ppi.bTop = bTop; @@ -997,7 +997,7 @@ void PlayFilm(CORO_PARAM, SCNHANDLE hFilm, int x, int y, int actorid, bool splay ppi.myescEvent = myescEvent; // Start display process for each reel in the film - for (int i = FROM_LE_32(pFilm->numreels) - 1; i >= 0; i--) { + for (int i = FROM_32(pFilm->numreels) - 1; i >= 0; i--) { NewestFilm(hFilm, &pFilm->reels[i]); ppi.column = i; @@ -1049,7 +1049,7 @@ void PlayFilmc(CORO_PARAM, SCNHANDLE hFilm, int x, int y, int actorid, bool spla _ctx->ppi.y = y; _ctx->ppi.z = 0; _ctx->ppi.bRestore = false; - _ctx->ppi.speed = (ONE_SECOND / FROM_LE_32(pFilm->frate)); + _ctx->ppi.speed = (ONE_SECOND / FROM_32(pFilm->frate)); _ctx->ppi.actorid = actorid; _ctx->ppi.splay = splay; _ctx->ppi.bTop = bTop; @@ -1059,7 +1059,7 @@ void PlayFilmc(CORO_PARAM, SCNHANDLE hFilm, int x, int y, int actorid, bool spla // Start display process for each secondary reel in the film in Tinsel 1, // or all of them in Tinsel 2 - for (int i = FROM_LE_32(pFilm->numreels) - 1; i >= (TinselV2 ? 0 : 1); i--) { + for (int i = FROM_32(pFilm->numreels) - 1; i >= (TinselV2 ? 0 : 1); i--) { NewestFilm(hFilm, &pFilm->reels[i]); _ctx->ppi.column = i; @@ -1109,7 +1109,7 @@ void RestoreActorReels(SCNHANDLE hFilm, short reelnum, short z, int x, int y) { ppi.x = x; ppi.y = y; ppi.z = z; - ppi.speed = (ONE_SECOND / FROM_LE_32(pfilm->frate)); + ppi.speed = (ONE_SECOND / FROM_32(pfilm->frate)); ppi.actorid = 0; ppi.splay = false; ppi.bTop = false; @@ -1147,15 +1147,15 @@ void RestoreActorReels(SCNHANDLE hFilm, int actor, int x, int y) { ppi.x = (short)x; ppi.y = (short)y; ppi.bRestore = true; - ppi.speed = (short)(ONE_SECOND/FROM_LE_32(pFilm->frate)); + ppi.speed = (short)(ONE_SECOND/FROM_32(pFilm->frate)); ppi.bTop = false; ppi.myescEvent = 0; // Search backwards for now as later column will be the one - for (i = (int)FROM_LE_32(pFilm->numreels) - 1; i >= 0; i--) { + for (i = (int)FROM_32(pFilm->numreels) - 1; i >= 0; i--) { pFreel = &pFilm->reels[i]; - pmi = (PMULTI_INIT) LockMem(FROM_LE_32(pFreel->mobj)); - if ((int32)FROM_LE_32(pmi->mulID) == actor) { + pmi = (PMULTI_INIT) LockMem(FROM_32(pFreel->mobj)); + if ((int32)FROM_32(pmi->mulID) == actor) { ppi.column = (short)i; NewestFilm(hFilm, &pFilm->reels[i]); @@ -1173,8 +1173,8 @@ void RestoreActorReels(SCNHANDLE hFilm, int actor, int x, int y) { int ExtractActor(SCNHANDLE hFilm) { const FILM *pFilm = (const FILM *)LockMem(hFilm); const FREEL *pReel = &pFilm->reels[0]; - const MULTI_INIT *pmi = (const MULTI_INIT *)LockMem(FROM_LE_32(pReel->mobj)); - return (int)FROM_LE_32(pmi->mulID); + const MULTI_INIT *pmi = (const MULTI_INIT *)LockMem(FROM_32(pReel->mobj)); + return (int)FROM_32(pmi->mulID); } } // End of namespace Tinsel diff --git a/engines/tinsel/polygons.cpp b/engines/tinsel/polygons.cpp index d8c1cef0b6..8a984c78f9 100644 --- a/engines/tinsel/polygons.cpp +++ b/engines/tinsel/polygons.cpp @@ -154,13 +154,13 @@ public: void setIndex(int index); - POLY_TYPE getType() const { return (POLY_TYPE)FROM_LE_32(type); } - int getNodecount() const { return (int)FROM_LE_32(nodecount); } - int getNodeX(int i) const { return (int)FROM_LE_32(nlistx[i]); } - int getNodeY(int i) const { return (int)FROM_LE_32(nlisty[i]); } + POLY_TYPE getType() const { return (POLY_TYPE)FROM_32(type); } + int getNodecount() const { return (int)FROM_32(nodecount); } + int getNodeX(int i) const { return (int)FROM_32(nlistx[i]); } + int getNodeY(int i) const { return (int)FROM_32(nlisty[i]); } // get Inter-node line structure - const LINEINFO *getLineinfo(int i) const { return ((const LINEINFO *)(_pStart + (int)FROM_LE_32(plinelist))) + i; } + const LINEINFO *getLineinfo(int i) const { return ((const LINEINFO *)(_pStart + (int)FROM_32(plinelist))) + i; } protected: POLY_TYPE type; ///< type of polygon @@ -230,8 +230,8 @@ void Poly::nextPoly() { const byte *pRecord = _pData; int typeVal = nextLong(_pData); - if ((FROM_LE_32(typeVal) == 5) && TinselV2) - typeVal = TO_LE_32(6); + if ((FROM_32(typeVal) == 5) && TinselV2) + typeVal = TO_32(6); type = (POLY_TYPE)typeVal; for (int i = 0; i < 4; ++i) @@ -275,8 +275,8 @@ void Poly::nextPoly() { pnodelisty = nextLong(_pData); plinelist = nextLong(_pData); - nlistx = (const int32 *)(_pStart + (int)FROM_LE_32(pnodelistx)); - nlisty = (const int32 *)(_pStart + (int)FROM_LE_32(pnodelisty)); + nlistx = (const int32 *)(_pStart + (int)FROM_32(pnodelistx)); + nlisty = (const int32 *)(_pStart + (int)FROM_32(pnodelisty)); if (TinselV0) // Skip to the last 4 bytes of the record for the hScript value @@ -591,16 +591,16 @@ void FindBestPoint(HPOLYGON hp, int *x, int *y, int *pline) { for (int i = 0; i < ptp.getNodecount() - 1; i++) { const LINEINFO *line = ptp.getLineinfo(i); - const int32 a = (int)FROM_LE_32(line->a); - const int32 b = (int)FROM_LE_32(line->b); - const int32 c = (int)FROM_LE_32(line->c); + const int32 a = (int)FROM_32(line->a); + const int32 b = (int)FROM_32(line->b); + const int32 c = (int)FROM_32(line->c); #if 1 // TODO: If the comments of the LINEINFO struct are correct, then it contains mostly // duplicate data, probably in an effort to safe CPU cycles. Even on the slowest devices // we support, calculating a product of two ints is not an issue. // So we can just load & endian convert a,b,c, then replace stuff like - // (int)FROM_LE_32(line->ab) + // (int)FROM_32(line->ab) // by simply a*b, which makes it easier to understand what the code does, too. // Just in case there is some bugged data, I leave this code here for verifying it. // Let's leave it in for some time. @@ -608,14 +608,14 @@ void FindBestPoint(HPOLYGON hp, int *x, int *y, int *pline) { // One bad thing: We use sqrt to compute a square root. Might not be a good idea, // speed wise. Maybe we should take Vicent's fp_sqroot. But that's a problem for later. - int32 a2 = (int)FROM_LE_32(line->a2); ///< a squared - int32 b2 = (int)FROM_LE_32(line->b2); ///< b squared - int32 a2pb2 = (int)FROM_LE_32(line->a2pb2); ///< a squared + b squared - int32 ra2pb2 = (int)FROM_LE_32(line->ra2pb2); ///< root(a squared + b squared) + int32 a2 = (int)FROM_32(line->a2); ///< a squared + int32 b2 = (int)FROM_32(line->b2); ///< b squared + int32 a2pb2 = (int)FROM_32(line->a2pb2); ///< a squared + b squared + int32 ra2pb2 = (int)FROM_32(line->ra2pb2); ///< root(a squared + b squared) - int32 ab = (int)FROM_LE_32(line->ab); - int32 ac = (int)FROM_LE_32(line->ac); - int32 bc = (int)FROM_LE_32(line->bc); + int32 ab = (int)FROM_32(line->ab); + int32 ac = (int)FROM_32(line->ac); + int32 bc = (int)FROM_32(line->bc); assert(a*a == a2); assert(b*b == b2); @@ -676,9 +676,9 @@ void FindBestPoint(HPOLYGON hp, int *x, int *y, int *pline) { // A point on a line is nearest const LINEINFO *line = ptp.getLineinfo(nearestL); - const int32 a = (int)FROM_LE_32(line->a); - const int32 b = (int)FROM_LE_32(line->b); - const int32 c = (int)FROM_LE_32(line->c); + const int32 a = (int)FROM_32(line->a); + const int32 b = (int)FROM_32(line->b); + const int32 c = (int)FROM_32(line->c); dropX = ((b*b * h) - (a*b * k) - a*c) / (a*a + b*b); dropY = ((a*a * k) - (a*b * h) - b*c) / (a*a + b*b); *x = dropX; @@ -994,15 +994,15 @@ int GetScale(HPOLYGON hPath, int y) { Poly ptp(LockMem(pHandle), Polys[hPath]->pIndex); // Path is of a constant scale? - if (FROM_LE_32(ptp.scale2) == 0) - return FROM_LE_32(ptp.scale1); + if (FROM_32(ptp.scale2) == 0) + return FROM_32(ptp.scale1); - assert(FROM_LE_32(ptp.scale1) >= FROM_LE_32(ptp.scale2)); + assert(FROM_32(ptp.scale1) >= FROM_32(ptp.scale2)); - zones = FROM_LE_32(ptp.scale1) - FROM_LE_32(ptp.scale2) + 1; + zones = FROM_32(ptp.scale1) - FROM_32(ptp.scale2) + 1; zlen = (Polys[hPath]->pbottom - Polys[hPath]->ptop) / zones; - scale = FROM_LE_32(ptp.scale1); + scale = FROM_32(ptp.scale1); top = Polys[hPath]->ptop; do { @@ -1011,7 +1011,7 @@ int GetScale(HPOLYGON hPath, int y) { return scale; } while (--scale); - return FROM_LE_32(ptp.scale2); + return FROM_32(ptp.scale2); } /** @@ -1033,15 +1033,15 @@ int GetBrightness(HPOLYGON hPath, int y) { Poly ptp(LockMem(pHandle), Polys[hPath]->pIndex); // Path is of a constant brightness? - if (FROM_LE_32(ptp.bright1) == FROM_LE_32(ptp.bright2)) - return FROM_LE_32(ptp.bright1); + if (FROM_32(ptp.bright1) == FROM_32(ptp.bright2)) + return FROM_32(ptp.bright1); - assert(FROM_LE_32(ptp.bright1) >= FROM_LE_32(ptp.bright2)); + assert(FROM_32(ptp.bright1) >= FROM_32(ptp.bright2)); - zones = FROM_LE_32(ptp.bright1) - FROM_LE_32(ptp.bright2) + 1; + zones = FROM_32(ptp.bright1) - FROM_32(ptp.bright2) + 1; zlen = (Polys[hPath]->pbottom - Polys[hPath]->ptop) / zones; - brightness = FROM_LE_32(ptp.bright1); + brightness = FROM_32(ptp.bright1); top = Polys[hPath]->ptop; do { @@ -1050,7 +1050,7 @@ int GetBrightness(HPOLYGON hPath, int y) { return brightness; } while (--brightness); - return FROM_LE_32(ptp.bright2); + return FROM_32(ptp.bright2); } @@ -1079,9 +1079,9 @@ void GetTagTag(HPOLYGON hp, SCNHANDLE *hTagText, int *tagx, int *tagy) { Poly ptp(LockMem(pHandle), Polys[hp]->pIndex); - *tagx = (int)FROM_LE_32(ptp.tagx) + (TinselV2 ? volatileStuff[hp].xoff : 0); - *tagy = (int)FROM_LE_32(ptp.tagy) + (TinselV2 ? volatileStuff[hp].yoff : 0); - *hTagText = FROM_LE_32(ptp.hTagtext); + *tagx = (int)FROM_32(ptp.tagx) + (TinselV2 ? volatileStuff[hp].xoff : 0); + *tagy = (int)FROM_32(ptp.tagy) + (TinselV2 ? volatileStuff[hp].yoff : 0); + *hTagText = FROM_32(ptp.hTagtext); } /** @@ -1092,7 +1092,7 @@ SCNHANDLE GetPolyFilm(HPOLYGON hp) { Poly ptp(LockMem(pHandle), Polys[hp]->pIndex); - return FROM_LE_32(ptp.hFilm); + return FROM_32(ptp.hFilm); } /** @@ -1103,7 +1103,7 @@ SCNHANDLE GetPolyScript(HPOLYGON hp) { Poly ptp(LockMem(pHandle), Polys[hp]->pIndex); - return FROM_LE_32(ptp.hScript); + return FROM_32(ptp.hScript); } REEL GetPolyReelType(HPOLYGON hp) { @@ -1115,7 +1115,7 @@ REEL GetPolyReelType(HPOLYGON hp) { Poly ptp(LockMem(pHandle), Polys[hp]->pIndex); - return (REEL)FROM_LE_32(ptp.reel); + return (REEL)FROM_32(ptp.reel); } int32 GetPolyZfactor(HPOLYGON hp) { @@ -1124,7 +1124,7 @@ int32 GetPolyZfactor(HPOLYGON hp) { Poly ptp(LockMem(pHandle), Polys[hp]->pIndex); - return (int)FROM_LE_32(ptp.zFactor); + return (int)FROM_32(ptp.zFactor); } int numNodes(HPOLYGON hp) { @@ -1319,11 +1319,11 @@ static bool MatchingLevels(PPOLYGON p1, PPOLYGON p2) { Poly pp1(pps, p1->pIndex); // This polygon 1 Poly pp2(pps, p2->pIndex); // This polygon 2 - assert((int32)FROM_LE_32(pp1.level1) <= (int32)FROM_LE_32(pp1.level2)); - assert((int32)FROM_LE_32(pp2.level1) <= (int32)FROM_LE_32(pp2.level2)); + assert((int32)FROM_32(pp1.level1) <= (int32)FROM_32(pp1.level2)); + assert((int32)FROM_32(pp2.level1) <= (int32)FROM_32(pp2.level2)); - for (int pl = (int32)FROM_LE_32(pp1.level1); pl <= (int32)FROM_LE_32(pp1.level2); pl++) { - if (pl >= (int32)FROM_LE_32(pp2.level1) && pl <= (int32)FROM_LE_32(pp2.level2)) + for (int pl = (int32)FROM_32(pp1.level1); pl <= (int32)FROM_32(pp1.level2); pl++) { + if (pl >= (int32)FROM_32(pp2.level1) && pl <= (int32)FROM_32(pp2.level2)) return true; } @@ -1604,17 +1604,17 @@ static PPOLYGON CommonInits(PTYPE polyType, int pno, const Poly &ptp, bool bRest p->pIndex = pno; for (i = 0; i < 4; i++) { // Polygon definition - p->cx[i] = (short)FROM_LE_32(ptp.x[i]); - p->cy[i] = (short)FROM_LE_32(ptp.y[i]); + p->cx[i] = (short)FROM_32(ptp.x[i]); + p->cy[i] = (short)FROM_32(ptp.y[i]); } if (!bRestart) { hp = PolygonIndex(p); - volatileStuff[hp].xoff = (short)FROM_LE_32(ptp.xoff); - volatileStuff[hp].yoff = (short)FROM_LE_32(ptp.yoff); + volatileStuff[hp].xoff = (short)FROM_32(ptp.xoff); + volatileStuff[hp].yoff = (short)FROM_32(ptp.yoff); } - p->polyID = FROM_LE_32(ptp.id); // Identifier + p->polyID = FROM_32(ptp.id); // Identifier FiddlyBit(p); @@ -1731,7 +1731,7 @@ static void InitEffect(const Poly &ptp, int pno, bool bRestart) { static void InitRefer(const Poly &ptp, int pno, bool bRestart) { PPOLYGON p = CommonInits(REFER, pno, ptp, bRestart); - p->subtype = FROM_LE_32(ptp.reftype); // Refer type + p->subtype = FROM_32(ptp.reftype); // Refer type } @@ -1990,8 +1990,8 @@ void GetPolyNode(HPOLYGON hp, int *pNodeX, int *pNodeY) { *pNodeX = 480; *pNodeY = 408; } else { - *pNodeX = FROM_LE_32(ptp.nodex); - *pNodeY = FROM_LE_32(ptp.nodey); + *pNodeX = FROM_32(ptp.nodex); + *pNodeY = FROM_32(ptp.nodey); } if (TinselV2) { diff --git a/engines/tinsel/rince.cpp b/engines/tinsel/rince.cpp index ba8f47f9cf..3e6334f583 100644 --- a/engines/tinsel/rince.cpp +++ b/engines/tinsel/rince.cpp @@ -550,7 +550,7 @@ void AlterMover(PMOVER pMover, SCNHANDLE film, AR_FUNCTION fn) { pfilm = (const FILM *)LockMem(film); assert(pfilm != NULL); - InitStepAnimScript(&pMover->actorAnim, pMover->actorObj, FROM_LE_32(pfilm->reels[0].script), ONE_SECOND / FROM_LE_32(pfilm->frate)); + InitStepAnimScript(&pMover->actorAnim, pMover->actorObj, FROM_32(pfilm->reels[0].script), ONE_SECOND / FROM_32(pfilm->frate)); if (!TinselV2) pMover->stepCount = 0; @@ -643,7 +643,7 @@ void SetMoverWalkReel(PMOVER pMover, DIRECTION reel, int scale, bool force) { pfilm = (const FILM *)LockMem(whichReel); assert(pfilm != NULL); // no film - InitStepAnimScript(&pMover->actorAnim, pMover->actorObj, FROM_LE_32(pfilm->reels[0].script), 1); + InitStepAnimScript(&pMover->actorAnim, pMover->actorObj, FROM_32(pfilm->reels[0].script), 1); // Synchronised walking reels assert(pMover->stepCount >= 0); @@ -704,14 +704,14 @@ static void MoverProcessHelper(int X, int Y, int id, PMOVER pMover) { InitialPathChecks(pMover, X, Y); pfilm = (const FILM *)LockMem(pMover->walkReels[0][FORWARD]); - pmi = (const MULTI_INIT *)LockMem(FROM_LE_32(pfilm->reels[0].mobj)); + pmi = (const MULTI_INIT *)LockMem(FROM_32(pfilm->reels[0].mobj)); //--- - pFrame = (const FRAME *)LockMem(FROM_LE_32(pmi->hMulFrame)); + pFrame = (const FRAME *)LockMem(FROM_32(pmi->hMulFrame)); // get pointer to image - pim = (IMAGE *)LockMem(READ_LE_UINT32(pFrame)); // handle to image - pim->hImgPal = TO_LE_32(BgPal()); + pim = (IMAGE *)LockMem(READ_32(pFrame)); // handle to image + pim->hImgPal = TO_32(BgPal()); //--- pMover->actorObj = MultiInitObject(pmi); @@ -722,7 +722,7 @@ static void MoverProcessHelper(int X, int Y, int id, PMOVER pMover) { MultiInsertObject(GetPlayfieldList(FIELD_WORLD), pMover->actorObj); storeActorReel(id, NULL, 0, pMover->actorObj, 0, 0, 0); - InitStepAnimScript(&pMover->actorAnim, pMover->actorObj, FROM_LE_32(pfilm->reels[0].script), ONE_SECOND / FROM_LE_32(pfilm->frate)); + InitStepAnimScript(&pMover->actorAnim, pMover->actorObj, FROM_32(pfilm->reels[0].script), ONE_SECOND / FROM_32(pfilm->frate)); pMover->stepCount = 0; MultiSetAniXY(pMover->actorObj, pMover->objX, pMover->objY); @@ -802,7 +802,7 @@ void T2MoverProcess(CORO_PARAM, const void *param) { InitialPathChecks(pMover, rpos->X, rpos->Y); pFilm = (FILM *)LockMem(pMover->walkReels[i][FORWARD]); // Any old reel - pmi = (PMULTI_INIT)LockMem(FROM_LE_32(pFilm->reels[0].mobj)); + pmi = (PMULTI_INIT)LockMem(FROM_32(pFilm->reels[0].mobj)); // Poke in the background palette PokeInPalette(pmi); diff --git a/engines/tinsel/saveload.cpp b/engines/tinsel/saveload.cpp index 0a552c8c2b..acff196916 100644 --- a/engines/tinsel/saveload.cpp +++ b/engines/tinsel/saveload.cpp @@ -55,8 +55,7 @@ namespace Tinsel { * only saves/loads those which are valid for the version of the savegame * which is being loaded/saved currently. */ -#define CURRENT_VER 1 -// TODO: Not yet used +#define CURRENT_VER 2 /** * An auxillary macro, used to specify savegame versions. We use this instead @@ -97,12 +96,13 @@ struct SaveGameHeader { TimeDate dateTime; bool scnFlag; byte language; + uint16 numInterpreters; // Savegame version 2 or later only }; enum { DW1_SAVEGAME_ID = 0x44575399, // = 'DWSc' = "DiscWorld 1 ScummVM" DW2_SAVEGAME_ID = 0x44573253, // = 'DW2S' = "DiscWorld 2 ScummVM" - SAVEGAME_HEADER_SIZE = 4 + 4 + 4 + SG_DESC_LEN + 7 + 1 + 1 + SAVEGAME_HEADER_SIZE = 4 + 4 + 4 + SG_DESC_LEN + 7 + 1 + 1 + 2 }; #define SAVEGAME_ID (TinselV2 ? (uint32)DW2_SAVEGAME_ID : (uint32)DW1_SAVEGAME_ID) @@ -186,6 +186,15 @@ static bool syncSaveGameHeader(Common::Serializer &s, SaveGameHeader &hdr) { } } + // Handle the number of interpreter contexts that will be saved in the savegame + if (tmp >= 2) { + tmp -= 2; + hdr.numInterpreters = NUM_INTERPRET; + s.syncAsUint16LE(hdr.numInterpreters); + } else { + hdr.numInterpreters = (TinselV2 ? 70 : 64) - 20; + } + // Skip over any extra bytes s.skip(tmp); return true; @@ -262,7 +271,7 @@ static void syncSoundReel(Common::Serializer &s, SOUNDREELS &sr) { s.syncAsSint32LE(sr.actorCol); } -static void syncSavedData(Common::Serializer &s, SAVED_DATA &sd) { +static void syncSavedData(Common::Serializer &s, SAVED_DATA &sd, int numInterp) { s.syncAsUint32LE(sd.SavedSceneHandle); s.syncAsUint32LE(sd.SavedBgroundHandle); for (int i = 0; i < MAX_MOVERS; ++i) @@ -273,7 +282,7 @@ static void syncSavedData(Common::Serializer &s, SAVED_DATA &sd) { s.syncAsSint32LE(sd.NumSavedActors); s.syncAsSint32LE(sd.SavedLoffset); s.syncAsSint32LE(sd.SavedToffset); - for (int i = 0; i < NUM_INTERPRET; ++i) + for (int i = 0; i < numInterp; ++i) sd.SavedICInfo[i].syncWithSerializer(s); for (int i = 0; i < MAX_POLY; ++i) s.syncAsUint32LE(sd.SavedDeadPolys[i]); @@ -422,7 +431,7 @@ char *ListEntry(int i, letype which) { return NULL; } -static void DoSync(Common::Serializer &s) { +static bool DoSync(Common::Serializer &s, int numInterp) { int sg = 0; if (TinselV2) { @@ -434,7 +443,7 @@ static void DoSync(Common::Serializer &s) { if (TinselV2 && s.isLoading()) HoldItem(INV_NOICON); - syncSavedData(s, *g_srsd); + syncSavedData(s, *g_srsd, numInterp); syncGlobInfo(s); // Glitter globals syncInvInfo(s); // Inventory data @@ -443,6 +452,10 @@ static void DoSync(Common::Serializer &s) { sg = WhichItemHeld(); s.syncAsSint32LE(sg); if (s.isLoading()) { + if (sg != -1 && !GetIsInvObject(sg)) + // Not a valid inventory object, so return false + return false; + if (TinselV2) g_thingHeld = sg; else @@ -459,7 +472,7 @@ static void DoSync(Common::Serializer &s) { if (*g_SaveSceneSsCount != 0) { SAVED_DATA *sdPtr = g_SaveSceneSsData; for (int i = 0; i < *g_SaveSceneSsCount; ++i, ++sdPtr) - syncSavedData(s, *sdPtr); + syncSavedData(s, *sdPtr, numInterp); // Flag that there is a saved scene to return to. Note that in this context 'saved scene' // is a stored scene to return to from another scene, such as from the Summoning Book close-up @@ -469,6 +482,8 @@ static void DoSync(Common::Serializer &s) { if (!TinselV2) syncAllActorsAlive(s); + + return true; } /** @@ -488,7 +503,22 @@ static bool DoRestore() { return false; } - DoSync(s); + // Load in the data. For older savegame versions, we potentially need to load the data twice, once + // for pre 1.5 savegames, and if that fails, a second time for 1.5 savegames + int numInterpreters = hdr.numInterpreters; + int32 currentPos = f->pos(); + for (int tryNumber = 0; tryNumber < ((hdr.ver >= 2) ? 1 : 2); ++tryNumber) { + // If it's the second loop iteration, try with the 1.5 savegame number of interpreter contexts + if (tryNumber == 1) { + f->seek(currentPos); + numInterpreters = 80; + } + + // Load the savegame data + if (DoSync(s, numInterpreters)) + // Data load was successful (or likely), so break out of loop + break; + } uint32 id = f->readSint32LE(); if (id != (uint32)0xFEEDFACE) @@ -510,8 +540,8 @@ static void SaveFailure(Common::OutSaveFile *f) { if (f) { delete f; _vm->getSaveFileMan()->removeSavefile(g_SaveSceneName); - g_SaveSceneName = NULL; // Invalidate save name } + g_SaveSceneName = NULL; // Invalidate save name GUI::MessageDialog dialog(_("Failed to save game state to file.")); dialog.runModal(); } @@ -575,7 +605,7 @@ static void DoSave() { return; } - DoSync(s); + DoSync(s, hdr.numInterpreters); // Write out the special Id for Discworld savegames f->writeUint32LE(0xFEEDFACE); diff --git a/engines/tinsel/scene.cpp b/engines/tinsel/scene.cpp index 79bb30f7a3..9181f85afb 100644 --- a/engines/tinsel/scene.cpp +++ b/engines/tinsel/scene.cpp @@ -86,8 +86,8 @@ struct SCENE_STRUC { SCNHANDLE hTaggedActor; // handle to table of tagged actors int32 numProcess; // number of processes in this scene SCNHANDLE hProcess; // handle to table of processes - SCNHANDLE hMusicScript; // handle to music script data - SCNHANDLE hMusicSegment;// handle to music segments + SCNHANDLE hMusicScript; // handle to music script data - Tinsel 2 only + SCNHANDLE hMusicSegment;// handle to music segments - Tinsel 2 only } PACKED_STRUCT; /** entrance structure - one per entrance */ @@ -130,15 +130,15 @@ const SCENE_STRUC *GetSceneStruc(const byte *pStruc) { const byte *p = pStruc; memset(&g_tempStruc, 0, sizeof(SCENE_STRUC)); - g_tempStruc.numEntrance = READ_UINT32(p); p += sizeof(uint32); - g_tempStruc.numPoly = READ_UINT32(p); p += sizeof(uint32); - g_tempStruc.numTaggedActor = READ_UINT32(p); p += sizeof(uint32); - g_tempStruc.defRefer = READ_UINT32(p); p += sizeof(uint32); - g_tempStruc.hSceneScript = READ_UINT32(p); p += sizeof(uint32); - g_tempStruc.hEntrance = READ_UINT32(p); p += sizeof(uint32); - g_tempStruc.hPoly = READ_UINT32(p); p += sizeof(uint32); - g_tempStruc.hTaggedActor = READ_UINT32(p); p += sizeof(uint32); - + g_tempStruc.numEntrance = FROM_LE_32(READ_32(p)); p += sizeof(uint32); + g_tempStruc.numPoly = FROM_LE_32(READ_32(p)); p += sizeof(uint32); + g_tempStruc.numTaggedActor = FROM_LE_32(READ_32(p)); p += sizeof(uint32); + g_tempStruc.defRefer = FROM_LE_32(READ_32(p)); p += sizeof(uint32); + g_tempStruc.hSceneScript = FROM_LE_32(READ_32(p)); p += sizeof(uint32); + g_tempStruc.hEntrance = FROM_LE_32(READ_32(p)); p += sizeof(uint32); + g_tempStruc.hPoly = FROM_LE_32(READ_32(p)); p += sizeof(uint32); + g_tempStruc.hTaggedActor = FROM_LE_32(READ_32(p)); p += sizeof(uint32); + return &g_tempStruc; } @@ -158,7 +158,9 @@ static void SceneTinselProcess(CORO_PARAM, const void *param) { // The following myEscape value setting is used for enabling title screen skipping in DW1 if (TinselV1 && (g_sceneCtr == 1)) g_initialMyEscape = GetEscEvents(); - _ctx->myEscape = (TinselV1 && (g_sceneCtr < 4)) ? g_initialMyEscape : 0; + // DW1 PSX has its own scene skipping script code for scenes 2 and 3 (bug #3541542). + // Same goes for DW1 Mac. + _ctx->myEscape = (TinselV1 && (g_sceneCtr < ((TinselV1PSX || TinselV1Mac) ? 2 : 4))) ? g_initialMyEscape : 0; // get the stuff copied to process when it was created _ctx->pInit = (const TP_INIT *)param; @@ -166,7 +168,7 @@ static void SceneTinselProcess(CORO_PARAM, const void *param) { assert(_ctx->pInit->hTinselCode); // Must have some code to run _ctx->pic = InitInterpretContext(GS_SCENE, - READ_LE_UINT32(&_ctx->pInit->hTinselCode), + _ctx->pInit->hTinselCode, TinselV2 ? _ctx->pInit->event : NOEVENT, NOPOLY, // No polygon 0, // No actor @@ -208,7 +210,7 @@ void SendSceneTinselProcess(TINSEL_EVENT event) { */ static void LoadScene(SCNHANDLE scene, int entry) { - uint i; + int32 i; TP_INIT init; const SCENE_STRUC *ss; const ENTRANCE_STRUC *es; @@ -222,7 +224,7 @@ static void LoadScene(SCNHANDLE scene, int entry) { // CdPlay() stuff byte *cptr = FindChunk(scene, CHUNK_CDPLAY_FILENUM); assert(cptr); - i = READ_LE_UINT32(cptr); + i = READ_32(cptr); assert(i < 512); cptr = FindChunk(scene, CHUNK_CDPLAY_FILENAME); assert(cptr); @@ -237,18 +239,17 @@ static void LoadScene(SCNHANDLE scene, int entry) { // Music stuff char *cptr = (char *)FindChunk(scene, CHUNK_MUSIC_FILENAME); assert(cptr); - _vm->_pcmMusic->setMusicSceneDetails(FROM_LE_32(ss->hMusicScript), - FROM_LE_32(ss->hMusicSegment), cptr); + _vm->_pcmMusic->setMusicSceneDetails(ss->hMusicScript, ss->hMusicSegment, cptr); } if (entry == NO_ENTRY_NUM) { // Restoring scene // Initialize all the polygons for this scene - InitPolygons(FROM_LE_32(ss->hPoly), FROM_LE_32(ss->numPoly), true); + InitPolygons(ss->hPoly, ss->numPoly, true); // Initialize the actors for this scene - StartTaggedActors(FROM_LE_32(ss->hTaggedActor), FROM_LE_32(ss->numTaggedActor), false); + StartTaggedActors(ss->hTaggedActor, ss->numTaggedActor, false); if (TinselV2) // Returning from cutscene @@ -258,18 +259,18 @@ static void LoadScene(SCNHANDLE scene, int entry) { // Genuine new scene // Initialize all the polygons for this scene - InitPolygons(FROM_LE_32(ss->hPoly), FROM_LE_32(ss->numPoly), false); + InitPolygons(ss->hPoly, ss->numPoly, false); // Initialize the actors for this scene - StartTaggedActors(FROM_LE_32(ss->hTaggedActor), FROM_LE_32(ss->numTaggedActor), true); + StartTaggedActors(ss->hTaggedActor, ss->numTaggedActor, true); // Run the appropriate entrance code (if any) - es = (const ENTRANCE_STRUC *)LockMem(FROM_LE_32(ss->hEntrance)); - for (i = 0; i < FROM_LE_32(ss->numEntrance); i++) { - if (FROM_LE_32(es->eNumber) == (uint)entry) { + es = (const ENTRANCE_STRUC *)LockMem(ss->hEntrance); + for (i = 0; i < ss->numEntrance; i++) { + if (FROM_32(es->eNumber) == (uint)entry) { if (es->hScript) { init.event = STARTUP; - init.hTinselCode = es->hScript; + init.hTinselCode = FROM_32(es->hScript); CoroScheduler.createProcess(PID_TCODE, SceneTinselProcess, &init, sizeof(init)); } @@ -284,7 +285,7 @@ static void LoadScene(SCNHANDLE scene, int entry) { } - if (i == FROM_LE_32(ss->numEntrance)) + if (i == ss->numEntrance) error("Non-existant scene entry number"); if (ss->hSceneScript) { @@ -296,10 +297,10 @@ static void LoadScene(SCNHANDLE scene, int entry) { } // Default refer type - SetDefaultRefer(FROM_LE_32(ss->defRefer)); + SetDefaultRefer(ss->defRefer); // Scene's processes - SceneProcesses(FROM_LE_32(ss->numProcess), FROM_LE_32(ss->hProcess)); + SceneProcesses(ss->numProcess, ss->hProcess); } diff --git a/engines/tinsel/scene.h b/engines/tinsel/scene.h index baaff27a3e..06e5c096d9 100644 --- a/engines/tinsel/scene.h +++ b/engines/tinsel/scene.h @@ -75,9 +75,9 @@ enum REEL { typedef enum { TRANS_DEF, TRANS_CUT, TRANS_FADE } TRANSITS; // amount to shift scene handles by -#define SCNHANDLE_SHIFT ((TinselV2 && !IsDemo) ? 25 : 23) -#define OFFSETMASK ((TinselV2 && !IsDemo) ? 0x01ffffffL : 0x007fffffL) -#define HANDLEMASK ((TinselV2 && !IsDemo) ? 0xFE000000L : 0xFF800000L) +#define SCNHANDLE_SHIFT ((TinselV2 && !TinselV2Demo) ? 25 : 23) +#define OFFSETMASK ((TinselV2 && !TinselV2Demo) ? 0x01ffffffL : 0x007fffffL) +#define HANDLEMASK ((TinselV2 && !TinselV2Demo) ? 0xFE000000L : 0xFF800000L) void DoHailScene(SCNHANDLE scene); diff --git a/engines/tinsel/sched.cpp b/engines/tinsel/sched.cpp index 4bf356ba36..a73b4b9b97 100644 --- a/engines/tinsel/sched.cpp +++ b/engines/tinsel/sched.cpp @@ -109,7 +109,7 @@ void RestoreSceneProcess(INT_CONTEXT *pic) { pStruc = (PROCESS_STRUC *)LockMem(g_hSceneProcess); for (i = 0; i < g_numSceneProcess; i++) { - if (FROM_LE_32(pStruc[i].hProcessCode) == pic->hCode) { + if (FROM_32(pStruc[i].hProcessCode) == pic->hCode) { CoroScheduler.createProcess(PID_PROCESS + i, RestoredProcessProcess, &pic, sizeof(pic)); break; @@ -137,11 +137,11 @@ void SceneProcessEvent(CORO_PARAM, uint32 procID, TINSEL_EVENT event, bool bWait _ctx->pStruc = (PROCESS_STRUC *)LockMem(g_hSceneProcess); for (i = 0; i < g_numSceneProcess; i++) { - if (FROM_LE_32(_ctx->pStruc[i].processId) == procID) { + if (FROM_32(_ctx->pStruc[i].processId) == procID) { assert(_ctx->pStruc[i].hProcessCode); // Must have some code to run _ctx->pic = InitInterpretContext(GS_PROCESS, - FROM_LE_32(_ctx->pStruc[i].hProcessCode), + FROM_32(_ctx->pStruc[i].hProcessCode), event, NOPOLY, // No polygon 0, // No actor @@ -176,7 +176,7 @@ void KillSceneProcess(uint32 procID) { pStruc = (PROCESS_STRUC *) LockMem(g_hSceneProcess); for (i = 0; i < g_numSceneProcess; i++) { - if (FROM_LE_32(pStruc[i].processId) == procID) { + if (FROM_32(pStruc[i].processId) == procID) { CoroScheduler.killMatchingProcess(PID_PROCESS + i, -1); break; } @@ -293,8 +293,8 @@ void GlobalProcesses(uint32 numProcess, byte *pProcess) { byte *p = pProcess; for (uint i = 0; i < numProcess; ++i, p += 8) { - g_pGlobalProcess[i].processId = READ_LE_UINT32(p); - g_pGlobalProcess[i].hProcessCode = READ_LE_UINT32(p + 4); + g_pGlobalProcess[i].processId = READ_32(p); + g_pGlobalProcess[i].hProcessCode = READ_32(p + 4); } } diff --git a/engines/tinsel/sound.cpp b/engines/tinsel/sound.cpp index f575b03270..03aa3767c4 100644 --- a/engines/tinsel/sound.cpp +++ b/engines/tinsel/sound.cpp @@ -74,8 +74,8 @@ SoundManager::~SoundManager() { */ // playSample for DiscWorld 1 bool SoundManager::playSample(int id, Audio::Mixer::SoundType type, Audio::SoundHandle *handle) { - // Floppy version has no sample file - if (_vm->getFeatures() & GF_FLOPPY) + // Floppy version has no sample file. + if (!_vm->isCD()) return false; // no sample driver? @@ -102,7 +102,7 @@ bool SoundManager::playSample(int id, Audio::Mixer::SoundType type, Audio::Sound error(FILE_IS_CORRUPT, _vm->getSampleFile(g_sampleLanguage)); // read the length of the sample - uint32 sampleLen = _sampleStream.readUint32(); + uint32 sampleLen = _sampleStream.readUint32LE(); if (_sampleStream.eos() || _sampleStream.err()) error(FILE_IS_CORRUPT, _vm->getSampleFile(g_sampleLanguage)); @@ -177,12 +177,54 @@ bool SoundManager::playSample(int id, Audio::Mixer::SoundType type, Audio::Sound return true; } +bool SoundManager::playDW1MacMusic(int dwFileOffset) { + Common::File s; + + if (!s.open("midi.dat")) + error(CANNOT_FIND_FILE, "midi.dat"); + + s.seek(dwFileOffset); + uint32 length = s.readUint32BE(); + + // TODO: It's a bad idea to load the music track in a buffer. + // We should use a SubReadStream instead, and keep midi.dat open. + // However, the track lengths aren't that big (about 1-4MB), + // so this shouldn't be a major issue. + byte *soundData = (byte *)malloc(length); + assert(soundData); + + // read all of the sample + if (s.read(soundData, length) != length) + error(FILE_IS_CORRUPT, "midi.dat"); + + Common::SeekableReadStream *memStream = new Common::MemoryReadStream(soundData, length); + + Audio::SoundHandle *handle = &_channels[kChannelDW1MacMusic].handle; + //_channels[kChannelDW1MacMusic].sampleNum = dwFileOffset; + + // Stop any previously playing music track + _vm->_mixer->stopHandle(*handle); + + // FIXME: Should set this in a different place ;) + _vm->_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, _vm->_config->_musicVolume); + + // TODO: Compression support (MP3/OGG/FLAC) for midi.dat in DW1 Mac + Audio::RewindableAudioStream *musicStream = Audio::makeRawStream(memStream, 22050, Audio::FLAG_UNSIGNED, DisposeAfterUse::YES); + + if (musicStream) + _vm->_mixer->playStream(Audio::Mixer::kMusicSoundType, handle, Audio::makeLoopingAudioStream(musicStream, 0)); + + s.close(); + + return true; +} + // playSample for DiscWorld 2 bool SoundManager::playSample(int id, int sub, bool bLooped, int x, int y, int priority, Audio::Mixer::SoundType type, Audio::SoundHandle *handle) { // Floppy version has no sample file - if (_vm->getFeatures() & GF_FLOPPY) + if (!_vm->isCD()) return false; // no sample driver? @@ -257,7 +299,7 @@ bool SoundManager::playSample(int id, int sub, bool bLooped, int x, int y, int p error(FILE_IS_CORRUPT, _vm->getSampleFile(g_sampleLanguage)); // read the length of the sample - uint32 sampleLen = _sampleStream.readUint32(); + uint32 sampleLen = _sampleStream.readUint32LE(); if (_sampleStream.eos() || _sampleStream.err()) error(FILE_IS_CORRUPT, _vm->getSampleFile(g_sampleLanguage)); @@ -270,12 +312,12 @@ bool SoundManager::playSample(int id, int sub, bool bLooped, int x, int y, int p // Skipping for (int32 i = 0; i < sub; i++) { - sampleLen = _sampleStream.readUint32(); + sampleLen = _sampleStream.readUint32LE(); _sampleStream.skip(sampleLen); if (_sampleStream.eos() || _sampleStream.err()) error(FILE_IS_CORRUPT, _vm->getSampleFile(g_sampleLanguage)); } - sampleLen = _sampleStream.readUint32(); + sampleLen = _sampleStream.readUint32LE(); if (_sampleStream.eos() || _sampleStream.err()) error(FILE_IS_CORRUPT, _vm->getSampleFile(g_sampleLanguage)); } @@ -369,7 +411,6 @@ bool SoundManager::offscreenChecks(int x, int &y) { } int8 SoundManager::getPan(int x) { - if (x == -1) return 0; @@ -416,14 +457,13 @@ bool SoundManager::sampleExists(int id) { /** * Returns true if a sample is currently playing. */ -bool SoundManager::sampleIsPlaying(int id) { +bool SoundManager::sampleIsPlaying() { if (!TinselV2) return _vm->_mixer->isSoundHandleActive(_channels[kChannelTinsel1].handle); for (int i = 0; i < kNumChannels; i++) - if (_channels[i].sampleNum == id) - if (_vm->_mixer->isSoundHandleActive(_channels[i].handle)) - return true; + if (_vm->_mixer->isSoundHandleActive(_channels[i].handle)) + return true; return false; } @@ -432,8 +472,6 @@ bool SoundManager::sampleIsPlaying(int id) { * Stops any currently playing sample. */ void SoundManager::stopAllSamples() { - // stop currently playing sample - if (!TinselV2) { _vm->_mixer->stopHandle(_channels[kChannelTinsel1].handle); return; @@ -466,12 +504,21 @@ void SoundManager::setSFXVolumes(uint8 volume) { _vm->_mixer->setChannelVolume(_channels[i].handle, volume); } +void SoundManager::showSoundError(const char *errorMsg, const char *soundFile) { + Common::String msg; + msg = Common::String::format(errorMsg, soundFile); + GUI::MessageDialog dialog(msg, "OK"); + dialog.runModal(); + + error("%s", msg.c_str()); +} + /** * Opens and inits all sound sample files. */ void SoundManager::openSampleFiles() { // Floppy and demo versions have no sample files, except for the Discworld 2 demo - if (_vm->getFeatures() & GF_FLOPPY || (IsDemo && !TinselV2)) + if (!_vm->isCD()) return; TinselFile f; @@ -480,42 +527,26 @@ void SoundManager::openSampleFiles() { // already allocated return; - // open sample index file in binary mode + // Open sample index (*.idx) in binary mode if (f.open(_vm->getSampleIndex(g_sampleLanguage))) { - // get length of index file - f.seek(0, SEEK_END); // move to end of file - _sampleIndexLen = f.pos(); // get file pointer - f.seek(0, SEEK_SET); // back to beginning - + uint32 fileSize = f.size(); + _sampleIndex = (uint32 *)malloc(fileSize); if (_sampleIndex == NULL) { - // allocate a buffer for the indices - _sampleIndex = (uint32 *)malloc(_sampleIndexLen); - - // make sure memory allocated - if (_sampleIndex == NULL) { - // disable samples if cannot alloc buffer for indices - // TODO: Disabled sound if we can't load the sample index? - return; - } + showSoundError(NO_MEM, _vm->getSampleIndex(g_sampleLanguage)); + return; } - // load data - if (f.read(_sampleIndex, _sampleIndexLen) != (uint32)_sampleIndexLen) - // file must be corrupt if we get to here - error(FILE_IS_CORRUPT, _vm->getSampleFile(g_sampleLanguage)); + _sampleIndexLen = fileSize / 4; // total sample of indices (DWORDs) - // close the file - f.close(); - - // convert file size to size in DWORDs - _sampleIndexLen /= sizeof(uint32); - -#ifdef SCUMM_BIG_ENDIAN - // Convert all ids from LE to native format + // Load data for (int i = 0; i < _sampleIndexLen; ++i) { - _sampleIndex[i] = SWAP_BYTES_32(_sampleIndex[i]); + _sampleIndex[i] = f.readUint32LE(); + if (f.err()) { + showSoundError(FILE_READ_ERROR, _vm->getSampleIndex(g_sampleLanguage)); + } } -#endif + + f.close(); // Detect format of soundfile by looking at 1st sample-index switch (TO_BE_32(_sampleIndex[0])) { @@ -523,48 +554,31 @@ void SoundManager::openSampleFiles() { debugC(DEBUG_DETAILED, kTinselDebugSound, "Detected MP3 sound-data"); _soundMode = kMP3Mode; break; - case MKTAG('O','G','G',' '): debugC(DEBUG_DETAILED, kTinselDebugSound, "Detected OGG sound-data"); _soundMode = kVorbisMode; break; - case MKTAG('F','L','A','C'): debugC(DEBUG_DETAILED, kTinselDebugSound, "Detected FLAC sound-data"); _soundMode = kFLACMode; break; - default: debugC(DEBUG_DETAILED, kTinselDebugSound, "Detected original sound-data"); break; } - // Normally the 1st sample-index points to nothing at all + + // Normally the 1st sample index points to nothing at all. We use it to + // determine if the game's sample files have been compressed, thus restore + // it here _sampleIndex[0] = 0; } else { - char buf[50]; - sprintf(buf, CANNOT_FIND_FILE, _vm->getSampleIndex(g_sampleLanguage)); - GUI::MessageDialog dialog(buf, "OK"); - dialog.runModal(); - - error(CANNOT_FIND_FILE, _vm->getSampleIndex(g_sampleLanguage)); + showSoundError(FILE_READ_ERROR, _vm->getSampleIndex(g_sampleLanguage)); } - // open sample file in binary mode + // Open sample file (*.smp) in binary mode if (!_sampleStream.open(_vm->getSampleFile(g_sampleLanguage))) { - char buf[50]; - sprintf(buf, CANNOT_FIND_FILE, _vm->getSampleFile(g_sampleLanguage)); - GUI::MessageDialog dialog(buf, "OK"); - dialog.runModal(); - - error(CANNOT_FIND_FILE, _vm->getSampleFile(g_sampleLanguage)); + showSoundError(FILE_READ_ERROR, _vm->getSampleFile(g_sampleLanguage)); } - -/* - // gen length of the largest sample - sampleBuffer.size = _sampleStream.readUint32LE(); - if (_sampleStream.eos() || _sampleStream.err()) - error(FILE_IS_CORRUPT, _vm->getSampleFile(g_sampleLanguage)); -*/ } void SoundManager::closeSampleStream() { diff --git a/engines/tinsel/sound.h b/engines/tinsel/sound.h index d7083b3b21..8510c1618f 100644 --- a/engines/tinsel/sound.h +++ b/engines/tinsel/sound.h @@ -51,7 +51,8 @@ protected: enum { kChannelTalk = 0, kChannelTinsel1 = 0, // Always using this channel for DW1 - kChannelSFX = 1 + kChannelSFX = 1, + kChannelDW1MacMusic = 2 }; static const int kNumChannels = kChannelSFX + kNumSFX; @@ -108,6 +109,7 @@ public: bool playSample(int id, Audio::Mixer::SoundType type, Audio::SoundHandle *handle = 0); bool playSample(int id, int sub, bool bLooped, int x, int y, int priority, Audio::Mixer::SoundType type, Audio::SoundHandle *handle = 0); + bool playDW1MacMusic(int dwFileOffset); void stopAllSamples(); // Stops any currently playing sample void stopSpecSample(int id, int sub = 0); // Stops a specific sample @@ -115,11 +117,13 @@ public: void setSFXVolumes(uint8 volume); bool sampleExists(int id); - bool sampleIsPlaying(int id = -1); + bool sampleIsPlaying(); - // TODO: Internal method, make this protected? void openSampleFiles(); void closeSampleStream(); + +private: + void showSoundError(const char *errorMsg, const char *soundFile); }; } // End of namespace Tinsel diff --git a/engines/tinsel/strres.cpp b/engines/tinsel/strres.cpp index 5a29a4d2cd..19a1ee94d6 100644 --- a/engines/tinsel/strres.cpp +++ b/engines/tinsel/strres.cpp @@ -165,15 +165,15 @@ static byte *FindStringBase(int id) { // skip to the correct chunk while (chunkSkip-- != 0) { // make sure chunk id is correct - assert(READ_LE_UINT32(pText + index) == CHUNK_STRING || READ_LE_UINT32(pText + index) == CHUNK_MBSTRING); + assert(READ_32(pText + index) == CHUNK_STRING || READ_32(pText + index) == CHUNK_MBSTRING); - if (READ_LE_UINT32(pText + index + sizeof(uint32)) == 0) { + if (READ_32(pText + index + sizeof(uint32)) == 0) { // string does not exist return NULL; } // get index to next chunk - index = READ_LE_UINT32(pText + index + sizeof(uint32)); + index = READ_32(pText + index + sizeof(uint32)); } // skip over chunk id and offset diff --git a/engines/tinsel/text.cpp b/engines/tinsel/text.cpp index 5eb092d00d..150eb2bdde 100644 --- a/engines/tinsel/text.cpp +++ b/engines/tinsel/text.cpp @@ -46,24 +46,24 @@ int StringLengthPix(char *szStr, const FONT *pFont) { if (c & 0x80) c = ((c & ~0x80) << 8) + *++szStr; } - hImg = FROM_LE_32(pFont->fontDef[c]); + hImg = FROM_32(pFont->fontDef[c]); if (hImg) { // there is a IMAGE for this character const IMAGE *pChar = (const IMAGE *)LockMem(hImg); // add width of font bitmap - strLen += FROM_LE_16(pChar->imgWidth); + strLen += FROM_16(pChar->imgWidth); } else // use width of space character - strLen += FROM_LE_32(pFont->spaceSize); + strLen += FROM_32(pFont->spaceSize); // finally add the inter-character spacing - strLen += FROM_LE_32(pFont->xSpacing); + strLen += FROM_32(pFont->xSpacing); } // return length of line in pixels - minus inter-char spacing for last character - strLen -= FROM_LE_32(pFont->xSpacing); + strLen -= FROM_32(pFont->xSpacing); return (strLen > 0) ? strLen : 0; } @@ -125,10 +125,10 @@ OBJECT *ObjectTextOut(OBJECT **pList, char *szStr, int color, // get image for capital W assert(pFont->fontDef[(int)'W']); - pImg = (const IMAGE *)LockMem(FROM_LE_32(pFont->fontDef[(int)'W'])); + pImg = (const IMAGE *)LockMem(FROM_32(pFont->fontDef[(int)'W'])); // get height of capital W for offset to next line - yOffset = FROM_LE_16(pImg->imgHeight) & ~C16_FLAG_MASK; + yOffset = FROM_16(pImg->imgHeight) & ~C16_FLAG_MASK; while (*szStr) { // x justify the text according to the mode flags @@ -140,24 +140,24 @@ OBJECT *ObjectTextOut(OBJECT **pList, char *szStr, int color, if (c & 0x80) c = ((c & ~0x80) << 8) + *++szStr; } - hImg = FROM_LE_32(pFont->fontDef[c]); + hImg = FROM_32(pFont->fontDef[c]); if (hImg == 0) { // no image for this character // add font spacing for a space character - xJustify += FROM_LE_32(pFont->spaceSize); + xJustify += FROM_32(pFont->spaceSize); } else { // printable character int aniX, aniY; // char image animation offsets OBJ_INIT oi; - oi.hObjImg = FROM_LE_32(pFont->fontInit.hObjImg); - oi.objFlags = FROM_LE_32(pFont->fontInit.objFlags); - oi.objID = FROM_LE_32(pFont->fontInit.objID); - oi.objX = FROM_LE_32(pFont->fontInit.objX); - oi.objY = FROM_LE_32(pFont->fontInit.objY); - oi.objZ = FROM_LE_32(pFont->fontInit.objZ); + oi.hObjImg = FROM_32(pFont->fontInit.hObjImg); + oi.objFlags = FROM_32(pFont->fontInit.objFlags); + oi.objID = FROM_32(pFont->fontInit.objID); + oi.objX = FROM_32(pFont->fontInit.objX); + oi.objY = FROM_32(pFont->fontInit.objY); + oi.objZ = FROM_32(pFont->fontInit.objZ); // allocate and init a character object if (pFirst == NULL) @@ -172,9 +172,9 @@ OBJECT *ObjectTextOut(OBJECT **pList, char *szStr, int color, // fill in character object pChar->hImg = hImg; // image def - pChar->width = FROM_LE_16(pImg->imgWidth); // width of chars bitmap - pChar->height = FROM_LE_16(pImg->imgHeight) & ~C16_FLAG_MASK; // height of chars bitmap - pChar->hBits = FROM_LE_32(pImg->hImgBits); // bitmap + pChar->width = FROM_16(pImg->imgWidth); // width of chars bitmap + pChar->height = FROM_16(pImg->imgHeight) & ~C16_FLAG_MASK; // height of chars bitmap + pChar->hBits = FROM_32(pImg->hImgBits); // bitmap // check for absolute positioning if (mode & TXT_ABSOLUTE) @@ -203,8 +203,8 @@ OBJECT *ObjectTextOut(OBJECT **pList, char *szStr, int color, CopyObject(pShad, pChar); // add shadow offsets to characters position - pShad->xPos += intToFrac(FROM_LE_32(pFont->xShadow)); - pShad->yPos += intToFrac(FROM_LE_32(pFont->yShadow)); + pShad->xPos += intToFrac(FROM_32(pFont->xShadow)); + pShad->yPos += intToFrac(FROM_32(pFont->yShadow)); // shadow is behind the character pShad->zPos--; @@ -232,18 +232,18 @@ OBJECT *ObjectTextOut(OBJECT **pList, char *szStr, int color, pChar = pChar->pSlave; // add character spacing - xJustify += FROM_LE_16(pImg->imgWidth); + xJustify += FROM_16(pImg->imgWidth); } // finally add the inter-character spacing - xJustify += FROM_LE_32(pFont->xSpacing); + xJustify += FROM_32(pFont->xSpacing); // next character in string ++szStr; } // adjust the text y position and add the inter-line spacing - yPos += yOffset + FROM_LE_32(pFont->ySpacing); + yPos += yOffset + FROM_32(pFont->ySpacing); // check for newline if (c == LF_CHAR) diff --git a/engines/tinsel/tinlib.cpp b/engines/tinsel/tinlib.cpp index 5dda836144..6a396b9b01 100644 --- a/engines/tinsel/tinlib.cpp +++ b/engines/tinsel/tinlib.cpp @@ -1625,10 +1625,6 @@ static void Play(CORO_PARAM, SCNHANDLE hFilm, int x, int y, bool bComplete, int * Play a midi file. */ static void PlayMidi(CORO_PARAM, SCNHANDLE hMidi, int loop, bool complete) { - // FIXME: This is a workaround for the FIXME below - if (GetMidiVolume() == 0) - return; - CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); @@ -1637,18 +1633,13 @@ static void PlayMidi(CORO_PARAM, SCNHANDLE hMidi, int loop, bool complete) { PlayMidiSequence(hMidi, loop == MIDI_LOOP); - // FIXME: The following check messes up the script arguments when - // entering the secret door in the bookshelf in the library, - // leading to a crash, when the music volume is set to 0 (MidiPlaying() - // always false then). - // - // Why exactly this happens is unclear. An analysis of the involved - // script(s) might reveal more. - // - // Note: This check&sleep was added in DW v2. It was most likely added - // to ensure that the MIDI song started playing before the next opcode + // This check&sleep was added in DW v2. It was most likely added to + // ensure that the MIDI song started playing before the next opcode // is executed. - if (!MidiPlaying()) + // In DW1, it messes up the script arguments when entering the secret + // door in the bookshelf in the library, leading to a crash, when the + // music volume is set to 0. + if (!MidiPlaying() && TinselV2) CORO_SLEEP(1); if (complete) { @@ -3412,7 +3403,7 @@ static void TalkOrSay(CORO_PARAM, SPEECH_TYPE speechType, SCNHANDLE hText, int x // Kick off the sample now (perhaps with a delay) if (g_bNoPause) g_bNoPause = false; - else if (!IsDemo) + else if (!TinselV2Demo) CORO_SLEEP(SysVar(SV_SPEECHDELAY)); //SamplePlay(VOICE, hText, _ctx->sub, false, -1, -1, PRIORITY_TALK); @@ -3693,7 +3684,7 @@ static void TranslucentIndex(unsigned index) { } /** - * Play a sample. + * Play a sample (DW1 only). */ static void TryPlaySample(CORO_PARAM, int sample, bool bComplete, bool escOn, int myEscape) { CORO_BEGIN_CONTEXT; @@ -4244,7 +4235,7 @@ int CallLibraryRoutine(CORO_PARAM, int operand, int32 *pp, const INT_CONTEXT *pi int libCode; if (TinselV0) libCode = DW1DEMO_CODES[operand]; else if (!TinselV2) libCode = DW1_CODES[operand]; - else if (_vm->getFeatures() & GF_DEMO) libCode = DW2DEMO_CODES[operand]; + else if (TinselV2Demo) libCode = DW2DEMO_CODES[operand]; else libCode = DW2_CODES[operand]; debug(7, "CallLibraryRoutine op %d (escOn %d, myEscape %d)", operand, pic->escOn, pic->myEscape); diff --git a/engines/tinsel/tinsel.cpp b/engines/tinsel/tinsel.cpp index e09e2c1dcf..e836fdff1f 100644 --- a/engines/tinsel/tinsel.cpp +++ b/engines/tinsel/tinsel.cpp @@ -430,6 +430,14 @@ static void MouseProcess(CORO_PARAM, const void *) { ProcessButEvent(PLR_DRAG2_END); break; + case Common::EVENT_WHEELUP: + PlayerEvent(PLR_WHEEL_UP, mousePos); + break; + + case Common::EVENT_WHEELDOWN: + PlayerEvent(PLR_WHEEL_DOWN, mousePos); + break; + default: break; } @@ -722,21 +730,20 @@ void LoadBasicChunks() { cptr = FindChunk(INV_OBJ_SCNHANDLE, CHUNK_OBJECTS); -#ifdef SCUMM_BIG_ENDIAN - //convert to native endianness + // Convert to native endianness INV_OBJECT *io = (INV_OBJECT *)cptr; for (int i = 0; i < numObjects; i++, io++) { - io->id = FROM_LE_32(io->id); - io->hIconFilm = FROM_LE_32(io->hIconFilm); - io->hScript = FROM_LE_32(io->hScript); - io->attribute = FROM_LE_32(io->attribute); + io->id = FROM_32(io->id); + io->hIconFilm = FROM_32(io->hIconFilm); + io->hScript = FROM_32(io->hScript); + io->attribute = FROM_32(io->attribute); } -#endif RegisterIcons(cptr, numObjects); cptr = FindChunk(MASTER_SCNHANDLE, CHUNK_TOTAL_POLY); - if (cptr != NULL) + // Max polygons are 0 in DW1 Mac (both in the demo and the full version) + if (cptr != NULL && *cptr != 0) MaxPolygons(*cptr); if (TinselV2) { @@ -1046,6 +1053,8 @@ bool TinselEngine::pollEvent() { case Common::EVENT_LBUTTONUP: case Common::EVENT_RBUTTONDOWN: case Common::EVENT_RBUTTONUP: + case Common::EVENT_WHEELUP: + case Common::EVENT_WHEELDOWN: // Add button to queue for the mouse process _mouseButtons.push_back(event.type); break; @@ -1053,7 +1062,7 @@ bool TinselEngine::pollEvent() { case Common::EVENT_MOUSEMOVE: { // This fragment takes care of Tinsel 2 when it's been compiled with - // blank areas at the top and bottom of thes creen + // blank areas at the top and bottom of the screen int ySkip = TinselV2 ? (g_system->getHeight() - _vm->screen().h) / 2 : 0; if ((event.mouse.y >= ySkip) && (event.mouse.y < (g_system->getHeight() - ySkip))) _mousePos = Common::Point(event.mouse.x, event.mouse.y - ySkip); diff --git a/engines/tinsel/tinsel.h b/engines/tinsel/tinsel.h index bac7ef6efb..ec504b69cd 100644 --- a/engines/tinsel/tinsel.h +++ b/engines/tinsel/tinsel.h @@ -63,27 +63,22 @@ enum TinselGameID { }; enum TinselGameFeatures { - GF_DEMO = 1 << 0, - GF_CD = 1 << 1, - GF_FLOPPY = 1 << 2, - GF_SCNFILES = 1 << 3, - GF_ENHANCED_AUDIO_SUPPORT = 1 << 4, - GF_ALT_MIDI = 1 << 5, // Alternate sequence in midi.dat file + GF_SCNFILES = 1 << 0, + GF_ENHANCED_AUDIO_SUPPORT = 1 << 1, + GF_ALT_MIDI = 1 << 2, // Alternate sequence in midi.dat file // The GF_USE_?FLAGS values specify how many country flags are displayed // in the subtitles options dialog. // None of these defined -> 1 language, in ENGLISH.TXT - GF_USE_3FLAGS = 1 << 6, // French, German, Spanish - GF_USE_4FLAGS = 1 << 7, // French, German, Spanish, Italian - GF_USE_5FLAGS = 1 << 8, // All 5 flags - - GF_BIG_ENDIAN = 1 << 9 + GF_USE_3FLAGS = 1 << 3, // French, German, Spanish + GF_USE_4FLAGS = 1 << 4, // French, German, Spanish, Italian + GF_USE_5FLAGS = 1 << 5 // All 5 flags }; /** * The following is the ScummVM definitions of the various Tinsel versions: * TINSEL_V0 - This was an early engine version that was only used in the Discworld 1 - * demo. It is not currently supported. + * demo. * TINSEL_V1 - This was the engine version used by Discworld 1. Note that there were two * major releases: an earlier version that used *.gra files, and a later one that * used *.scn files, and contained certain script and engine bugfixes. In ScummVM, @@ -134,13 +129,15 @@ typedef bool (*KEYFPTR)(const Common::KeyState &); #define TinselV0 (TinselVersion == TINSEL_V0) #define TinselV1 (TinselVersion == TINSEL_V1) #define TinselV2 (TinselVersion == TINSEL_V2) +#define TinselV2Demo (TinselVersion == TINSEL_V2 && _vm->getIsADGFDemo()) #define TinselV1PSX (TinselVersion == TINSEL_V1 && _vm->getPlatform() == Common::kPlatformPSX) #define TinselV1Mac (TinselVersion == TINSEL_V1 && _vm->getPlatform() == Common::kPlatformMacintosh) -#define IsDemo (_vm->getFeatures() & GF_DEMO) - -#define READ_16(v) ((_vm->getFeatures() & GF_BIG_ENDIAN) ? READ_BE_UINT16(v) : READ_LE_UINT16(v)) -#define READ_32(v) ((_vm->getFeatures() & GF_BIG_ENDIAN) ? READ_BE_UINT32(v) : READ_LE_UINT32(v)) +#define READ_16(v) (TinselV1Mac ? READ_BE_UINT16(v) : READ_LE_UINT16(v)) +#define READ_32(v) (TinselV1Mac ? READ_BE_UINT32(v) : READ_LE_UINT32(v)) +#define FROM_16(v) (TinselV1Mac ? FROM_BE_16(v) : FROM_LE_16(v)) +#define FROM_32(v) (TinselV1Mac ? FROM_BE_32(v) : FROM_LE_32(v)) +#define TO_32(v) (TinselV1Mac ? TO_BE_32(v) : TO_LE_32(v)) // Global reference to the TinselEngine object extern TinselEngine *_vm; @@ -186,6 +183,8 @@ public: uint16 getVersion() const; uint32 getFlags() const; Common::Platform getPlatform() const; + bool getIsADGFDemo() const; + bool isCD() const; const char *getSampleIndex(LANGUAGE lang); const char *getSampleFile(LANGUAGE lang); diff --git a/engines/toltecs/animation.cpp b/engines/toltecs/animation.cpp index eef9cef9ed..084332cf83 100644 --- a/engines/toltecs/animation.cpp +++ b/engines/toltecs/animation.cpp @@ -53,7 +53,7 @@ void AnimationPlayer::start(uint resIndex) { _vm->_arc->closeResource(); debug(1, "AnimationPlayer::start() width = %d; height = %d; frameCount = %d", _width, _height, _frameCount); - + _vm->_sceneWidth = _width; _vm->_sceneHeight = _height; @@ -63,7 +63,7 @@ void AnimationPlayer::start(uint resIndex) { _frameNumber = 0; // TODO mov screenFlag01, 0FFFFh // TODO mov animDrawFrameFlag, 0FFFFh - + _firstNextFrameOffset = _nextFrameOffset; _firstCurFrameSize = _curFrameSize; _firstNextFrameSize = _nextFrameSize; @@ -81,25 +81,25 @@ void AnimationPlayer::nextFrame() { } else { _frameNumber++; } - + debug(1, "AnimationPlayer::nextFrame() frameNumber = %d", _frameNumber); if (_keepFrameCounter > 0) { _keepFrameCounter--; return; } - + _vm->_arc->openResource(_resIndex); _vm->_arc->seek(_nextFrameOffset, SEEK_CUR); _curFrameSize = _nextFrameSize; - + if (_curFrameSize == 0) _curFrameSize = 1; - + _vm->_arc->read(_animBuffer, _curFrameSize); _nextFrameSize = _vm->_arc->readUint32LE(); _nextFrameOffset += _curFrameSize + 4; - + if (_curFrameSize > 1) { unpackFrame(); // TODO mov animDrawFrameFlag, 0FFFFh diff --git a/engines/toltecs/animation.h b/engines/toltecs/animation.h index 22576d7535..54ec5d8afa 100644 --- a/engines/toltecs/animation.h +++ b/engines/toltecs/animation.h @@ -54,7 +54,7 @@ public: uint16 _width, _height; uint16 _frameNumber, _frameCount; uint32 _keepFrameCounter; - + uint32 _curFrameSize; uint32 _nextFrameSize, _nextFrameOffset; diff --git a/engines/toltecs/console.cpp b/engines/toltecs/console.cpp new file mode 100644 index 0000000000..f3394909ed --- /dev/null +++ b/engines/toltecs/console.cpp @@ -0,0 +1,79 @@ +/* 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 "gui/debugger.h" + +#include "toltecs/console.h" +//#include "toltecs/palette.h" +#include "toltecs/resource.h" +//#include "toltecs/sound.h" +#include "toltecs/toltecs.h" + +namespace Toltecs { + +Console::Console(ToltecsEngine *vm) : GUI::Debugger(), _vm(vm) { + DCmd_Register("room", WRAP_METHOD(Console, Cmd_Room)); + DCmd_Register("dump", WRAP_METHOD(Console, Cmd_Dump)); +} + +Console::~Console() { +} + +bool Console::Cmd_Room(int argc, const char **argv) { + if (argc < 2) { + DebugPrintf("Current room number is %d\n", _vm->_sceneResIndex); +#if 0 + DebugPrintf("Calling this command with the room number changes the room\n"); + DebugPrintf("WARNING: It's a bad idea to warp to rooms with this, as the room object scripts are not loaded\n"); +#endif + return true; +#if 0 + } else { + int roomNum = atoi(argv[1]); + + // sfClearPaletteFragments + _vm->_palette->clearFragments(); + + // sfLoadScene + _vm->_sound->stopAll(); + _vm->_res->purgeCache(); + _vm->loadScene(roomNum); +#endif + } + + return false; +} + +bool Console::Cmd_Dump(int argc, const char **argv) { + if (argc < 2) { + DebugPrintf("Usage: dump <resource number>\n"); + return true; + } + + int resNum = atoi(argv[1]); + _vm->_arc->dump(resNum); + DebugPrintf("Resource %d has been dumped to disk\n", resNum); + + return true; +} + +} // End of namespace Toltecs diff --git a/devtools/create_teenagent/md5.h b/engines/toltecs/console.h index 3746521002..bcdfd0cf04 100644 --- a/devtools/create_teenagent/md5.h +++ b/engines/toltecs/console.h @@ -20,21 +20,26 @@ * */ -#ifndef COMMON_MD5_H -#define COMMON_MD5_H +#ifndef TOLTECS_CONSOLE_H +#define TOLTECS_CONSOLE_H -#include "util.h" +#include "gui/debugger.h" -typedef struct { - uint32 total[2]; - uint32 state[4]; - uint8 buffer[64]; -} md5_context; +namespace Toltecs { -void md5_starts(md5_context *ctx); -void md5_update(md5_context *ctx, const uint8 *input, uint32 length); -void md5_finish(md5_context *ctx, uint8 digest[16]); +class ToltecsEngine; -bool md5_file(const char *name, uint8 digest[16], uint32 length = 0); +class Console : public GUI::Debugger { +public: + Console(ToltecsEngine *vm); + virtual ~Console(void); +private: + ToltecsEngine *_vm; + + bool Cmd_Dump(int argc, const char **argv); + bool Cmd_Room(int argc, const char **argv); +}; + +} // End of namespace Toltecs #endif diff --git a/engines/toltecs/detection.cpp b/engines/toltecs/detection.cpp index c1a57638c2..4016becb82 100644 --- a/engines/toltecs/detection.cpp +++ b/engines/toltecs/detection.cpp @@ -24,6 +24,8 @@ #include "base/plugins.h" #include "engines/advancedDetector.h" + +#include "common/translation.h" #include "common/savefile.h" #include "common/str-array.h" #include "common/system.h" @@ -97,19 +99,6 @@ static const ToltecsGameDescription gameDescriptions[] = { }, { - // 3 Skulls of the Toltecs German Demo version - { - "toltecs", - 0, - AD_ENTRY1s("WESTERN", "1c85e82712d24f1d5c1ea2a66ddd75c2", 47730038), - Common::DE_DEU, - Common::kPlatformPC, - ADGF_NO_FLAGS, - GUIO1(GUIO_NONE) - }, - }, - - { // 3 Skulls of the Toltecs French version { "toltecs", @@ -149,11 +138,44 @@ static const ToltecsGameDescription gameDescriptions[] = { }, }, + { + // 3 Skulls of the Toltecs English Demo version + { + "toltecs", + 0, + AD_ENTRY1s("WESTERN", "53a0abd1c0bc5cad8ba18f0e56877705", 46241833), + Common::EN_ANY, + Common::kPlatformPC, + ADGF_DEMO, + GUIO1(GUIO_NONE) + }, + }, + + { + // 3 Skulls of the Toltecs German Demo version + { + "toltecs", + 0, + AD_ENTRY1s("WESTERN", "1c85e82712d24f1d5c1ea2a66ddd75c2", 47730038), + Common::DE_DEU, + Common::kPlatformPC, + ADGF_DEMO, + GUIO1(GUIO_NONE) + }, + }, + { AD_TABLE_END_MARKER } }; } // End of namespace Toltecs +static const ExtraGuiOption toltecsExtraGuiOption = { + _s("Use original save/load screens"), + _s("Use the original save/load screens, instead of the ScummVM ones"), + "originalsaveload", + false +}; + class ToltecsMetaEngine : public AdvancedMetaEngine { public: ToltecsMetaEngine() : AdvancedMetaEngine(Toltecs::gameDescriptions, sizeof(Toltecs::ToltecsGameDescription), toltecsGames) { @@ -170,6 +192,7 @@ public: virtual bool hasFeature(MetaEngineFeature f) const; virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const; + virtual const ExtraGuiOptions getExtraGuiOptions(const Common::String &target) const; SaveStateList listSaves(const char *target) const; virtual int getMaximumSaveSlot() const; void removeSaveState(const char *target, int slot) const; @@ -202,6 +225,12 @@ bool ToltecsMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADG return gd != 0; } +const ExtraGuiOptions ToltecsMetaEngine::getExtraGuiOptions(const Common::String &target) const { + ExtraGuiOptions options; + options.push_back(toltecsExtraGuiOption); + return options; +} + SaveStateList ToltecsMetaEngine::listSaves(const char *target) const { Common::SaveFileManager *saveFileMan = g_system->getSavefileManager(); Toltecs::ToltecsEngine::SaveHeader header; diff --git a/engines/toltecs/menu.cpp b/engines/toltecs/menu.cpp index 415f19ca31..b52d7dad82 100644 --- a/engines/toltecs/menu.cpp +++ b/engines/toltecs/menu.cpp @@ -21,7 +21,13 @@ * */ +#include "audio/mixer.h" + #include "common/savefile.h" +#include "common/config-manager.h" +#include "common/translation.h" + +#include "gui/saveload.h" #include "toltecs/toltecs.h" #include "toltecs/menu.h" @@ -37,10 +43,7 @@ MenuSystem::MenuSystem(ToltecsEngine *vm) : _vm(vm) { MenuSystem::~MenuSystem() { } -int MenuSystem::run() { - - //debug("MenuSystem::run()"); - +int MenuSystem::run(MenuID menuId) { _background = new Graphics::Surface(); _background->create(640, 400, Graphics::PixelFormat::createFormatCLUT8()); @@ -50,27 +53,21 @@ int MenuSystem::run() { memcpy(backgroundOrig.getBasePtr(0,0), _vm->_screen->_frontScreen, 640 * 400); _currMenuID = kMenuIdNone; - _newMenuID = kMenuIdMain; + _newMenuID = menuId; _currItemID = kItemIdNone; _editingDescription = false; - _cfgText = true; - _cfgVoices = true; - _cfgMasterVolume = 10; - _cfgVoicesVolume = 10; - _cfgMusicVolume = 10; - _cfgSoundFXVolume = 10; - _cfgBackgroundVolume = 10; - _running = true; + + _running = true; _top = 30 - _vm->_guiHeight / 2; + _needRedraw = false; - // TODO: buildColorTransTable2 _vm->_palette->buildColorTransTable(0, 16, 7); _vm->_screen->_renderQueue->clear(); // Draw the menu background and frame _vm->_screen->blastSprite(0x140 + _vm->_cameraX, 0x175 + _vm->_cameraY, 0, 1, 0x4000); - shadeRect(60, 39, 520, 246, 30, 94); + shadeRect(60, 39, 520, 247, 225, 229); memcpy(_background->pixels, _vm->_screen->_frontScreen, 640 * 400); @@ -78,7 +75,7 @@ int MenuSystem::run() { update(); _vm->_system->updateScreen(); } - + // Restore original background memcpy(_vm->_screen->_frontScreen, backgroundOrig.getBasePtr(0,0), 640 * 400); _vm->_system->copyRectToScreen(_vm->_screen->_frontScreen, 640, 0, 0, 640, 400); @@ -89,11 +86,10 @@ int MenuSystem::run() { _background->free(); delete _background; - return 0; + return 0; } void MenuSystem::update() { - if (_currMenuID != _newMenuID) { _currMenuID = _newMenuID; //debug("_currMenuID = %d", _currMenuID); @@ -104,17 +100,14 @@ void MenuSystem::update() { if (_needRedraw) { //_vm->_system->copyRectToScreen(_vm->_screen->_frontScreen + 39 * 640 + 60, 640, 60, 39, 520, 247); - _vm->_system->copyRectToScreen(_vm->_screen->_frontScreen, 640, 0, 0, 640, 400); - //debug("redraw"); + _vm->_system->copyRectToScreen(_vm->_screen->_frontScreen, 640, 0, _top, 640, 400 - _top); _needRedraw = false; } _vm->_system->delayMillis(5); - } void MenuSystem::handleEvents() { - Common::Event event; Common::EventManager *eventMan = _vm->_system->getEventManager(); while (eventMan->pollEvent(event)) { @@ -128,18 +121,18 @@ void MenuSystem::handleEvents() { case Common::EVENT_MOUSEMOVE: handleMouseMove(event.mouse.x, event.mouse.y); break; - case Common::EVENT_LBUTTONDOWN: + case Common::EVENT_LBUTTONUP: handleMouseClick(event.mouse.x, event.mouse.y); break; default: break; } } - } void MenuSystem::addClickTextItem(ItemID id, int x, int y, int w, uint fontNum, const char *caption, byte defaultColor, byte activeColor) { Item item; + item.enabled = true; item.id = id; item.defaultColor = defaultColor; item.activeColor = activeColor; @@ -204,7 +197,7 @@ void MenuSystem::handleKeyDown(const Common::KeyState& kbd) { ItemID MenuSystem::findItemAt(int x, int y) { for (Common::Array<Item>::iterator iter = _items.begin(); iter != _items.end(); iter++) { - if ((*iter).rect.contains(x, y)) + if ((*iter).enabled && (*iter).rect.contains(x, y - _top)) return (*iter).id; } return kItemIdNone; @@ -222,6 +215,8 @@ void MenuSystem::setItemCaption(Item *item, const char *caption) { Font font(_vm->_res->load(_vm->_screen->getFontResIndex(item->fontNum))->data); int width = font.getTextWidth((const byte*)caption); int height = font.getHeight(); + if (width & 1) + width++; item->rect = Common::Rect(item->x, item->y - height, item->x + width, item->y); if (item->w) { item->rect.translate(item->w - width / 2, 0); @@ -238,59 +233,87 @@ void MenuSystem::initMenu(MenuID menuID) { switch (menuID) { case kMenuIdMain: - drawString(0, 74, 320, 1, 229, _vm->getSysString(kStrWhatCanIDoForYou)); - addClickTextItem(kItemIdLoad, 0, 115, 320, 0, _vm->getSysString(kStrLoad), 229, 255); - addClickTextItem(kItemIdSave, 0, 135, 320, 0, _vm->getSysString(kStrSave), 229, 255); - addClickTextItem(kItemIdToggleText, 0, 165, 320, 0, _vm->getSysString(kStrTextOn), 229, 255); - addClickTextItem(kItemIdToggleVoices, 0, 185, 320, 0, _vm->getSysString(kStrVoicesOn), 229, 255); - addClickTextItem(kItemIdVolumesMenu, 0, 215, 320, 0, _vm->getSysString(kStrVolume), 229, 255); - addClickTextItem(kItemIdPlay, 0, 245, 320, 0, _vm->getSysString(kStrPlay), 229, 255); - addClickTextItem(kItemIdQuit, 0, 275, 320, 0, _vm->getSysString(kStrQuit), 229, 255); + drawString(0, 75, 320, 1, 229, _vm->getSysString(kStrWhatCanIDoForYou)); + addClickTextItem(kItemIdLoad, 0, 116, 320, 0, _vm->getSysString(kStrLoad), 253, 255); + addClickTextItem(kItemIdSave, 0, 136, 320, 0, _vm->getSysString(kStrSave), 253, 255); + addClickTextItem(kItemIdToggleText, 0, 166, 320, 0, _vm->getSysString(_vm->_cfgText ? kStrTextOn : kStrTextOff), 253, 255); + addClickTextItem(kItemIdToggleVoices, 0, 186, 320, 0, _vm->getSysString(_vm->_cfgVoices ? kStrVoicesOn : kStrVoicesOff), 253, 255); + addClickTextItem(kItemIdVolumesMenu, 0, 216, 320, 0, _vm->getSysString(kStrVolume), 253, 255); + addClickTextItem(kItemIdPlay, 0, 246, 320, 0, _vm->getSysString(kStrPlay), 253, 255); + addClickTextItem(kItemIdQuit, 0, 276, 320, 0, _vm->getSysString(kStrQuit), 253, 255); break; case kMenuIdLoad: - drawString(0, 74, 320, 1, 229, _vm->getSysString(kStrLoadGame)); - addClickTextItem(kItemIdSavegameUp, 0, 155, 545, 1, "^", 255, 253); - addClickTextItem(kItemIdSavegameDown, 0, 195, 545, 1, "\\", 255, 253); - addClickTextItem(kItemIdCancel, 0, 275, 320, 0, _vm->getSysString(kStrCancel), 255, 253); - for (int i = 1; i <= 7; i++) { - Common::String saveDesc = Common::String::format("SAVEGAME %d", i); - addClickTextItem((ItemID)(kItemIdSavegame1 + i - 1), 0, 115 + 20 * (i - 1), 300, 0, saveDesc.c_str(), 231, 234); + if (ConfMan.getBool("originalsaveload")) { + shadeRect(80, 92, 440, 141, 226, 225); + drawString(0, 75, 320, 1, 229, _vm->getSysString(kStrLoadGame)); + addClickTextItem(kItemIdSavegameUp, 0, 156, 545, 1, "^", 253, 255); + addClickTextItem(kItemIdSavegameDown, 0, 196, 545, 1, "\\", 253, 255); + addClickTextItem(kItemIdCancel, 0, 276, 320, 0, _vm->getSysString(kStrCancel), 253, 255); + for (int i = 1; i <= 7; i++) { + Common::String saveDesc = Common::String::format("SAVEGAME %d", i); + addClickTextItem((ItemID)(kItemIdSavegame1 + i - 1), 0, 116 + 20 * (i - 1), 300, 0, saveDesc.c_str(), 231, 234); + } + loadSavegamesList(); + setSavegameCaptions(true); + } else { + GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Restore game:"), _("Restore"), false); + int slot = dialog->runModalWithCurrentTarget(); + delete dialog; + + if (slot >= 0) + _vm->requestLoadgame(slot); + + _running = false; } - loadSavegamesList(); - setSavegameCaptions(); break; case kMenuIdSave: - drawString(0, 74, 320, 1, 229, _vm->getSysString(kStrSaveGame)); - addClickTextItem(kItemIdSavegameUp, 0, 155, 545, 1, "^", 255, 253); - addClickTextItem(kItemIdSavegameDown, 0, 195, 545, 1, "\\", 255, 253); - addClickTextItem(kItemIdCancel, 0, 275, 320, 0, _vm->getSysString(kStrCancel), 255, 253); - for (int i = 1; i <= 7; i++) { - Common::String saveDesc = Common::String::format("SAVEGAME %d", i); - addClickTextItem((ItemID)(kItemIdSavegame1 + i - 1), 0, 115 + 20 * (i - 1), 300, 0, saveDesc.c_str(), 231, 234); + if (ConfMan.getBool("originalsaveload")) { + shadeRect(80, 92, 440, 141, 226, 225); + drawString(0, 75, 320, 1, 229, _vm->getSysString(kStrSaveGame)); + addClickTextItem(kItemIdSavegameUp, 0, 156, 545, 1, "^", 253, 255); + addClickTextItem(kItemIdSavegameDown, 0, 196, 545, 1, "\\", 253, 255); + addClickTextItem(kItemIdCancel, 0, 276, 320, 0, _vm->getSysString(kStrCancel), 253, 255); + for (int i = 1; i <= 7; i++) { + Common::String saveDesc = Common::String::format("SAVEGAME %d", i); + addClickTextItem((ItemID)(kItemIdSavegame1 + i - 1), 0, 116 + 20 * (i - 1), 300, 0, saveDesc.c_str(), 231, 234); + } + newSlotNum = loadSavegamesList() + 1; + _savegames.push_back(SavegameItem(newSlotNum, Common::String::format("GAME %04d", _savegames.size()))); + setSavegameCaptions(true); + } else { + GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"), true); + int slot = dialog->runModalWithCurrentTarget(); + Common::String desc = dialog->getResultString(); + if (desc.empty()) { + // Create our own description for the saved game, the user didn't enter one + desc = dialog->createDefaultSaveDescription(slot); + } + + if (slot >= 0) + _vm->requestSavegame(slot, desc); + + _running = false; } - newSlotNum = loadSavegamesList() + 1; - _savegames.push_back(SavegameItem(newSlotNum, Common::String::format("GAME %03d", _savegames.size() + 1))); - setSavegameCaptions(); break; case kMenuIdVolumes: - drawString(0, 74, 320, 1, 229, _vm->getSysString(kStrAdjustVolume)); - drawString(0, 130, 200, 0, 246, _vm->getSysString(kStrMaster)); - drawString(0, 155, 200, 0, 244, _vm->getSysString(kStrVoices)); - drawString(0, 180, 200, 0, 244, _vm->getSysString(kStrMusic)); - drawString(0, 205, 200, 0, 244, _vm->getSysString(kStrSoundFx)); - drawString(0, 230, 200, 0, 244, _vm->getSysString(kStrBackground)); - addClickTextItem(kItemIdDone, 0, 275, 200, 0, _vm->getSysString(kStrDone), 229, 253); - addClickTextItem(kItemIdCancel, 0, 275, 440, 0, _vm->getSysString(kStrCancel), 229, 253); - addClickTextItem(kItemIdMasterDown, 0, 130 + 25 * 0, 348, 1, "[", 229, 253); - addClickTextItem(kItemIdVoicesDown, 0, 130 + 25 * 1, 348, 1, "[", 229, 253); - addClickTextItem(kItemIdMusicDown, 0, 130 + 25 * 2, 348, 1, "[", 229, 253); - addClickTextItem(kItemIdSoundFXDown, 0, 130 + 25 * 3, 348, 1, "[", 229, 253); - addClickTextItem(kItemIdBackgroundDown, 0, 130 + 25 * 4, 348, 1, "[", 229, 253); - addClickTextItem(kItemIdMasterUp, 0, 130 + 25 * 0, 372, 1, "]", 229, 253); - addClickTextItem(kItemIdVoicesUp, 0, 130 + 25 * 1, 372, 1, "]", 229, 253); - addClickTextItem(kItemIdMusicUp, 0, 130 + 25 * 2, 372, 1, "]", 229, 253); - addClickTextItem(kItemIdSoundFXUp, 0, 130 + 25 * 3, 372, 1, "]", 229, 253); - addClickTextItem(kItemIdBackgroundUp, 0, 130 + 25 * 4, 372, 1, "]", 229, 253); + drawString(0, 75, 320, 1, 229, _vm->getSysString(kStrAdjustVolume)); + drawString(0, 131, 200, 0, 246, _vm->getSysString(kStrMaster)); + drawString(0, 156, 200, 0, 244, _vm->getSysString(kStrVoices)); + drawString(0, 181, 200, 0, 244, _vm->getSysString(kStrMusic)); + drawString(0, 206, 200, 0, 244, _vm->getSysString(kStrSoundFx)); + drawString(0, 231, 200, 0, 244, _vm->getSysString(kStrBackground)); + addClickTextItem(kItemIdDone, 0, 276, 200, 0, _vm->getSysString(kStrDone), 253, 255); + addClickTextItem(kItemIdCancel, 0, 276, 440, 0, _vm->getSysString(kStrCancel), 253, 255); + addClickTextItem(kItemIdMasterDown, 0, 131 + 25 * 0, 348, 1, "[", 243, 246); + addClickTextItem(kItemIdVoicesDown, 0, 131 + 25 * 1, 348, 1, "[", 243, 246); + addClickTextItem(kItemIdMusicDown, 0, 131 + 25 * 2, 348, 1, "[", 243, 246); + addClickTextItem(kItemIdSoundFXDown, 0, 131 + 25 * 3, 348, 1, "[", 243, 246); + addClickTextItem(kItemIdBackgroundDown, 0, 131 + 25 * 4, 348, 1, "[", 243, 246); + addClickTextItem(kItemIdMasterUp, 0, 131 + 25 * 0, 372, 1, "]", 243, 246); + addClickTextItem(kItemIdVoicesUp, 0, 131 + 25 * 1, 372, 1, "]", 243, 246); + addClickTextItem(kItemIdMusicUp, 0, 131 + 25 * 2, 372, 1, "]", 243, 246); + addClickTextItem(kItemIdSoundFXUp, 0, 131 + 25 * 3, 372, 1, "]", 243, 246); + addClickTextItem(kItemIdBackgroundUp, 0, 131 + 25 * 4, 372, 1, "]", 243, 246); drawVolumeBar(kItemIdMaster); drawVolumeBar(kItemIdVoices); drawVolumeBar(kItemIdMusic); @@ -302,9 +325,36 @@ void MenuSystem::initMenu(MenuID menuID) { } for (Common::Array<Item>::iterator iter = _items.begin(); iter != _items.end(); iter++) { - drawItem((*iter).id, false); + if ((*iter).enabled) + drawItem((*iter).id, false); } + // Check if the mouse is already over an item + _currItemID = kItemIdNone; + Common::Point mousePos = _vm->_system->getEventManager()->getMousePos(); + handleMouseMove(mousePos.x, mousePos.y); +} + +void MenuSystem::enableItem(ItemID id) { + Item *item = getItem(id); + if (item) { + item->enabled = true; + drawItem(id, false); + _currItemID = kItemIdNone; + Common::Point mousePos = _vm->_system->getEventManager()->getMousePos(); + handleMouseMove(mousePos.x, mousePos.y); + } +} + +void MenuSystem::disableItem(ItemID id) { + Item *item = getItem(id); + if (item) { + item->enabled = false; + restoreRect(item->rect.left, item->rect.top, item->rect.width(), item->rect.height()); + if (_currItemID == id) { + _currItemID = kItemIdNone; + } + } } void MenuSystem::enterItem(ItemID id) { @@ -326,13 +376,13 @@ void MenuSystem::clickItem(ItemID id) { _newMenuID = kMenuIdLoad; break; case kItemIdToggleText: - setCfgText(!_cfgText, true); - if (!_cfgVoices && !_cfgText) + setCfgText(!_vm->_cfgText, true); + if (!_vm->_cfgVoices && !_vm->_cfgText) setCfgVoices(true, false); break; case kItemIdToggleVoices: - setCfgVoices(!_cfgVoices, true); - if (!_cfgVoices && !_cfgText) + setCfgVoices(!_vm->_cfgVoices, true); + if (!_vm->_cfgVoices && !_vm->_cfgText) setCfgText(true, false); break; case kItemIdVolumesMenu: @@ -416,7 +466,7 @@ void MenuSystem::restoreRect(int x, int y, int w, int h) { } void MenuSystem::shadeRect(int x, int y, int w, int h, byte color1, byte color2) { - byte *src = (byte *)_background->getBasePtr(x, y); + byte *src = (byte *)_vm->_screen->_frontScreen + x + y * 640; for (int xc = 0; xc < w; xc++) { src[xc] = color2; src[xc + h * 640] = color1; @@ -435,14 +485,16 @@ void MenuSystem::drawString(int16 x, int16 y, int w, uint fontNum, byte color, c fontNum = _vm->_screen->getFontResIndex(fontNum); Font font(_vm->_res->load(fontNum)->data); if (w) { - x = x + w - font.getTextWidth((const byte*)text) / 2; + int width = font.getTextWidth((const byte*)text); + if (width & 1) + width++; + x = x + w - width / 2; } _vm->_screen->drawString(x, y - font.getHeight(), color, fontNum, (const byte*)text, -1, NULL, true); _needRedraw = true; } int MenuSystem::loadSavegamesList() { - int maxSlotNum = -1; _savegameListTopIndex = 0; @@ -485,17 +537,32 @@ MenuSystem::SavegameItem *MenuSystem::getSavegameItemByID(ItemID id) { return NULL; } -void MenuSystem::setSavegameCaptions() { - uint index = _savegameListTopIndex; +void MenuSystem::setSavegameCaptions(bool scrollToBottom) { + int size = _savegames.size(); + if (scrollToBottom && size > 0) { + while (_savegameListTopIndex + 7 <= size) + _savegameListTopIndex += 6; + } + int index = _savegameListTopIndex; for (int i = 1; i <= 7; i++) - setItemCaption(getItem((ItemID)(kItemIdSavegame1 + i - 1)), index < _savegames.size() ? _savegames[index++]._description.c_str() : ""); + setItemCaption(getItem((ItemID)(kItemIdSavegame1 + i - 1)), index < size ? _savegames[index++]._description.c_str() : ""); + if (_savegameListTopIndex == 0) { + disableItem(kItemIdSavegameUp); + } else { + enableItem(kItemIdSavegameUp); + } + if (_savegameListTopIndex + 7 > size) { + disableItem(kItemIdSavegameDown); + } else { + enableItem(kItemIdSavegameDown); + } } void MenuSystem::scrollSavegames(int delta) { int newPos = CLIP<int>(_savegameListTopIndex + delta, 0, _savegames.size() - 1); _savegameListTopIndex = newPos; restoreRect(80, 92, 440, 140); - setSavegameCaptions(); + setSavegameCaptions(false); for (int i = 1; i <= 7; i++) drawItem((ItemID)(kItemIdSavegame1 + i - 1), false); } @@ -518,49 +585,51 @@ void MenuSystem::clickSavegameItem(ItemID id) { } void MenuSystem::setCfgText(bool value, bool active) { - if (_cfgText != value) { + if (_vm->_cfgText != value) { Item *item = getItem(kItemIdToggleText); - _cfgText = value; + _vm->_cfgText = value; restoreRect(item->rect.left, item->rect.top, item->rect.width() + 1, item->rect.height() - 2); - setItemCaption(item, _vm->getSysString(_cfgText ? kStrTextOn : kStrTextOff)); + setItemCaption(item, _vm->getSysString(_vm->_cfgText ? kStrTextOn : kStrTextOff)); drawItem(kItemIdToggleText, true); + ConfMan.setBool("subtitles", value); } } void MenuSystem::setCfgVoices(bool value, bool active) { - if (_cfgVoices != value) { + if (_vm->_cfgVoices != value) { Item *item = getItem(kItemIdToggleVoices); - _cfgVoices = value; + _vm->_cfgVoices = value; restoreRect(item->rect.left, item->rect.top, item->rect.width() + 1, item->rect.height() - 2); - setItemCaption(item, _vm->getSysString(_cfgVoices ? kStrVoicesOn : kStrVoicesOff)); + setItemCaption(item, _vm->getSysString(_vm->_cfgVoices ? kStrVoicesOn : kStrVoicesOff)); drawItem(kItemIdToggleVoices, true); + ConfMan.setBool("speech_mute", !value); } } void MenuSystem::drawVolumeBar(ItemID itemID) { int w = 440, y, volume; char text[21]; - + switch (itemID) { - case kItemIdMaster: + case kItemIdMaster: // unused in ScummVM, always 20 y = 130 + 25 * 0; - volume = _cfgMasterVolume; + volume = 20; break; case kItemIdVoices: y = 130 + 25 * 1; - volume = _cfgVoicesVolume; + volume = _vm->_cfgVoicesVolume; break; case kItemIdMusic: y = 130 + 25 * 2; - volume = _cfgMusicVolume; + volume = _vm->_cfgMusicVolume; break; case kItemIdSoundFX: y = 130 + 25 * 3; - volume = _cfgSoundFXVolume; + volume = _vm->_cfgSoundFXVolume; break; - case kItemIdBackground: + case kItemIdBackground: // unused in ScummVM, always 20 y = 130 + 25 * 4; - volume = _cfgBackgroundVolume; + volume = 20; break; default: return; @@ -568,46 +637,47 @@ void MenuSystem::drawVolumeBar(ItemID itemID) { Font font(_vm->_res->load(_vm->_screen->getFontResIndex(1))->data); restoreRect(390, y - font.getHeight(), 100, 25); - + for (int i = 0; i < volume; i++) text[i] = '|'; text[volume] = 0; - + drawString(0, y, w, 0, 246, text); - } void MenuSystem::changeVolumeBar(ItemID itemID, int delta) { - - int *volume, newVolume; + byte newVolume; switch (itemID) { - case kItemIdMaster: - volume = &_cfgMasterVolume; - break; case kItemIdVoices: - volume = &_cfgVoicesVolume; + _vm->_cfgVoicesVolume = CLIP(_vm->_cfgVoicesVolume + delta, 0, 20); + // Always round volume up instead of down. + newVolume = (_vm->_cfgVoicesVolume * Audio::Mixer::kMaxChannelVolume + 19) / 20; + _vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, newVolume); + ConfMan.setInt("speech_volume", newVolume); break; case kItemIdMusic: - volume = &_cfgMusicVolume; + _vm->_cfgMusicVolume = CLIP(_vm->_cfgMusicVolume + delta, 0, 20); + newVolume = (_vm->_cfgMusicVolume * Audio::Mixer::kMaxChannelVolume + 19) / 20; + _vm->_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, newVolume); + ConfMan.setInt("music_volume", newVolume); break; case kItemIdSoundFX: - volume = &_cfgSoundFXVolume; + _vm->_cfgSoundFXVolume = CLIP(_vm->_cfgSoundFXVolume + delta, 0, 20); + newVolume = (_vm->_cfgSoundFXVolume * Audio::Mixer::kMaxChannelVolume + 19) / 20; + _vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, newVolume); + ConfMan.setInt("sfx_volume", newVolume); break; + case kItemIdMaster: case kItemIdBackground: - volume = &_cfgBackgroundVolume; + // unused in ScummVM break; default: return; } - newVolume = CLIP(*volume + delta, 0, 20); - - if (newVolume != *volume) { - *volume = newVolume; - drawVolumeBar(itemID); - } - + _vm->syncSoundSettings(); + drawVolumeBar(itemID); } } // End of namespace Toltecs diff --git a/engines/toltecs/menu.h b/engines/toltecs/menu.h index 3e2c2da8d9..a5eca7c8ff 100644 --- a/engines/toltecs/menu.h +++ b/engines/toltecs/menu.h @@ -29,14 +29,6 @@ namespace Toltecs { -enum MenuID { - kMenuIdNone, - kMenuIdMain, - kMenuIdSave, - kMenuIdLoad, - kMenuIdVolumes -}; - enum ItemID { kItemIdNone, // Main menu @@ -85,13 +77,14 @@ public: MenuSystem(ToltecsEngine *vm); ~MenuSystem(); - int run(); + int run(MenuID menuId); void update(); void handleEvents(); - + protected: struct Item { + bool enabled; Common::Rect rect; ItemID id; Common::String caption; @@ -99,7 +92,7 @@ protected: int x, y, w; uint fontNum; }; - + struct SavegameItem { int _slotNum; Common::String _description; @@ -124,9 +117,6 @@ protected: Common::Array<Item> _items; Common::Array<SavegameItem> _savegames; - - bool _cfgText, _cfgVoices; - int _cfgMasterVolume, _cfgVoicesVolume, _cfgMusicVolume, _cfgSoundFXVolume, _cfgBackgroundVolume; void addClickTextItem(ItemID id, int x, int y, int w, uint fontNum, const char *caption, byte defaultColor, byte activeColor); @@ -134,13 +124,16 @@ protected: void handleMouseMove(int x, int y); void handleMouseClick(int x, int y); void handleKeyDown(const Common::KeyState& kbd); - + ItemID findItemAt(int x, int y); Item *getItem(ItemID id); void setItemCaption(Item *item, const char *caption); void initMenu(MenuID menuID); - + + void enableItem(ItemID id); + void disableItem(ItemID id); + void enterItem(ItemID id); void leaveItem(ItemID id); void clickItem(ItemID id); @@ -152,7 +145,7 @@ protected: SavegameItem *getSavegameItemByID(ItemID id); int loadSavegamesList(); - void setSavegameCaptions(); + void setSavegameCaptions(bool scrollToBottom); void scrollSavegames(int delta); void clickSavegameItem(ItemID id); void setCfgText(bool value, bool active); diff --git a/engines/toltecs/microtiles.cpp b/engines/toltecs/microtiles.cpp index 0b61ac38a5..9181480351 100644 --- a/engines/toltecs/microtiles.cpp +++ b/engines/toltecs/microtiles.cpp @@ -119,8 +119,6 @@ Common::Rect * MicroTileArray::getRectangles(int *num_rects, int min_x, int min_ for (y = 0; y < _tilesH; ++y) { for (x = 0; x < _tilesW; ++x) { - - int start; int finish = 0; BoundingBox boundingBox; @@ -138,11 +136,9 @@ Common::Rect * MicroTileArray::getRectangles(int *num_rects, int min_x, int min_ x0 = CLIP (x0, min_x, max_x); y0 = CLIP (y0, min_y, max_y); y1 = CLIP (y1, min_y, max_y); - + // FIXME: Why is the following code in an #if block? #if 1 - start = i; - if (TileX1(boundingBox) == TileSize - 1 && x != _tilesW - 1) { // check if the tile continues while (!finish) { ++x; diff --git a/engines/toltecs/module.mk b/engines/toltecs/module.mk index aa4a6f376b..0de1eef733 100644 --- a/engines/toltecs/module.mk +++ b/engines/toltecs/module.mk @@ -2,6 +2,7 @@ MODULE := engines/toltecs MODULE_OBJS = \ animation.o \ + console.o \ detection.o \ menu.o \ microtiles.o \ diff --git a/engines/toltecs/movie.cpp b/engines/toltecs/movie.cpp index 76d42ebf0a..8bc00511e9 100644 --- a/engines/toltecs/movie.cpp +++ b/engines/toltecs/movie.cpp @@ -45,14 +45,13 @@ enum ChunkTypes { kChunkStopSubtitles = 8 }; -MoviePlayer::MoviePlayer(ToltecsEngine *vm) : _vm(vm) { +MoviePlayer::MoviePlayer(ToltecsEngine *vm) : _vm(vm), _isPlaying(false) { } MoviePlayer::~MoviePlayer() { } void MoviePlayer::playMovie(uint resIndex) { - const uint32 subtitleSlot = kMaxScriptSlots - 1; int16 savedSceneWidth = _vm->_sceneWidth; int16 savedSceneHeight = _vm->_sceneHeight; @@ -61,7 +60,8 @@ void MoviePlayer::playMovie(uint resIndex) { int16 savedCameraY = _vm->_cameraY; int16 savedGuiHeight = _vm->_guiHeight; byte moviePalette[768]; - + + _isPlaying = true; _vm->_isSaveAllowed = false; memset(moviePalette, 0, sizeof(moviePalette)); @@ -78,7 +78,7 @@ void MoviePlayer::playMovie(uint resIndex) { _vm->_arc->readUint32LE(); _vm->_arc->readUint32LE(); _framesPerSoundChunk = _vm->_arc->readUint32LE(); - _vm->_arc->readUint32LE(); + int rate = _vm->_arc->readUint32LE(); _vm->_sceneWidth = 640; _vm->_sceneHeight = 400; @@ -87,7 +87,7 @@ void MoviePlayer::playMovie(uint resIndex) { _vm->_cameraY = 0; _vm->_guiHeight = 0; - _audioStream = Audio::makeQueuingAudioStream(22050, false); + _audioStream = Audio::makeQueuingAudioStream(rate, false); _vm->_mixer->playStream(Audio::Mixer::kPlainSoundType, &_audioStreamHandle, _audioStream); @@ -96,49 +96,54 @@ void MoviePlayer::playMovie(uint resIndex) { fetchAudioChunks(); - uint32 lastTime = _vm->_mixer->getSoundElapsedTime(_audioStreamHandle); - - while (_chunkCount--) { + byte *chunkBuffer = NULL; + uint32 chunkBufferSize = 0; + uint32 frame = 0; + bool abortMovie = false; + while (_chunkCount-- && !abortMovie) { byte chunkType = _vm->_arc->readByte(); uint32 chunkSize = _vm->_arc->readUint32LE(); - byte *chunkBuffer = NULL; - uint32 movieOffset; debug(0, "chunkType = %d; chunkSize = %d", chunkType, chunkSize); - + // Skip audio chunks - we've already queued them in - // fetchAudioChunks() above + // fetchAudioChunks() if (chunkType == kChunkAudio) { _vm->_arc->skip(chunkSize); } else { - chunkBuffer = new byte[chunkSize]; + // Only reallocate the chunk buffer if the new chunk is bigger + if (chunkSize > chunkBufferSize) { + delete[] chunkBuffer; + chunkBuffer = new byte[chunkSize]; + chunkBufferSize = chunkSize; + } + _vm->_arc->read(chunkBuffer, chunkSize); } - movieOffset = _vm->_arc->pos(); - switch (chunkType) { case kChunkFirstImage: case kChunkSubsequentImages: unpackRle(chunkBuffer, _vm->_screen->_backScreen); - // TODO: Rework this - _vm->_screen->updateShakeScreen(); _vm->_screen->_fullRefresh = true; - _vm->updateInput(); - _vm->drawScreen(); _soundChunkFramesLeft--; if (_soundChunkFramesLeft <= _framesPerSoundChunk) { fetchAudioChunks(); } - while (_vm->_mixer->getSoundElapsedTime(_audioStreamHandle) < lastTime + 111) { - g_system->delayMillis(10); + while (_vm->_mixer->getSoundElapsedTime(_audioStreamHandle) < (1000 * frame) / 9) { + if (_vm->_screen->_shakeActive && _vm->_screen->updateShakeScreen()) { + _vm->_screen->_fullRefresh = true; + } + if (!handleInput()) + abortMovie = true; + _vm->drawScreen(); + // Note: drawScreen() calls delayMillis() } - lastTime = _vm->_mixer->getSoundElapsedTime(_audioStreamHandle); - + frame++; break; case kChunkPalette: unpackPalette(chunkBuffer, moviePalette, 256, 3); @@ -150,10 +155,11 @@ void MoviePlayer::playMovie(uint resIndex) { // Already processed break; case kChunkShowSubtitle: - // TODO: Check if the text is a subtitle (last character == 0xFE). - // If so, don't show it if text display is disabled. memcpy(_vm->_script->getSlotData(subtitleSlot), chunkBuffer, chunkSize); - _vm->_screen->updateTalkText(subtitleSlot, 0); + // The last character of the subtitle determines if it should + // always be displayed or not. If it's 0xFF, it should always be + // displayed, otherwise, if it's 0xFE, it can be toggled. + _vm->_screen->updateTalkText(subtitleSlot, 0, (chunkBuffer[chunkSize - 1] == 0xFF)); break; case kChunkShakeScreen: // start/stop shakescreen effect if (chunkBuffer[0] == 0xFF) @@ -176,20 +182,17 @@ void MoviePlayer::playMovie(uint resIndex) { error("MoviePlayer::playMovie(%04X) Unknown chunk type %d at %08X", resIndex, chunkType, _vm->_arc->pos() - 5 - chunkSize); } - delete[] chunkBuffer; - - _vm->_arc->seek(movieOffset, SEEK_SET); - if (!handleInput()) - break; - + abortMovie = true; } + delete[] chunkBuffer; + _audioStream->finish(); _vm->_mixer->stopHandle(_audioStreamHandle); _vm->_arc->closeResource(); - + debug(0, "playMovie() done"); _vm->_sceneWidth = savedSceneWidth; @@ -200,10 +203,10 @@ void MoviePlayer::playMovie(uint resIndex) { _vm->_guiHeight = savedGuiHeight; _vm->_isSaveAllowed = true; + _isPlaying = false; } void MoviePlayer::fetchAudioChunks() { - uint32 startOfs = _vm->_arc->pos(); uint32 chunkCount = _chunkCount; uint prefetchChunkCount = 0; @@ -214,7 +217,7 @@ void MoviePlayer::fetchAudioChunks() { while (chunkCount-- && prefetchChunkCount < _framesPerSoundChunk / 2) { byte chunkType = _vm->_arc->readByte(); uint32 chunkSize = _vm->_arc->readUint32LE(); - if (chunkType == 4) { + if (chunkType == kChunkAudio) { byte *chunkBuffer = (byte *)malloc(chunkSize); _vm->_arc->read(chunkBuffer, chunkSize); _audioStream->queueBuffer(chunkBuffer, chunkSize, DisposeAfterUse::YES, Audio::FLAG_UNSIGNED); @@ -229,7 +232,6 @@ void MoviePlayer::fetchAudioChunks() { _lastPrefetchOfs = _vm->_arc->pos(); _vm->_arc->seek(startOfs, SEEK_SET); - } void MoviePlayer::unpackPalette(byte *source, byte *dest, int elemCount, int elemSize) { @@ -249,10 +251,12 @@ void MoviePlayer::unpackPalette(byte *source, byte *dest, int elemCount, int ele } void MoviePlayer::unpackRle(byte *source, byte *dest) { - int size = 256000; + int size = 256000; // 640x400 + //int packedSize = 0; while (size > 0) { byte a = *source++; byte b = *source++; + //packedSize += 2; if (a == 0) { dest += b; size -= b; @@ -262,6 +266,7 @@ void MoviePlayer::unpackRle(byte *source, byte *dest) { size -= a; } } + //debug("Packed RLE size: %d", packedSize); } bool MoviePlayer::handleInput() { @@ -272,12 +277,15 @@ bool MoviePlayer::handleInput() { case Common::EVENT_KEYDOWN: if (event.kbd.keycode == Common::KEYCODE_ESCAPE) return false; + if (event.kbd.keycode == Common::KEYCODE_F10) { + // TODO: The original would bring up a stripped down + // main menu dialog, without the save/restore options. + } break; case Common::EVENT_LBUTTONDOWN: case Common::EVENT_RBUTTONDOWN: return false; case Common::EVENT_QUIT: - _vm->quitGame(); return false; default: break; diff --git a/engines/toltecs/movie.h b/engines/toltecs/movie.h index aecfac240f..c1ed6d7ba0 100644 --- a/engines/toltecs/movie.h +++ b/engines/toltecs/movie.h @@ -36,22 +36,26 @@ public: ~MoviePlayer(); void playMovie(uint resIndex); - + + bool isPlaying() { return _isPlaying; } + protected: ToltecsEngine *_vm; Audio::QueuingAudioStream *_audioStream; Audio::SoundHandle _audioStreamHandle; + bool _isPlaying; + uint32 _chunkCount, _frameCount, _lastPrefetchOfs; uint32 _soundChunkFramesLeft, _framesPerSoundChunk; void unpackPalette(byte *source, byte *dest, int elemCount, int elemSize); void unpackRle(byte *source, byte *dest); - + void fetchAudioChunks(); - + bool handleInput(); - + }; } // End of namespace Toltecs diff --git a/engines/toltecs/music.cpp b/engines/toltecs/music.cpp index c322961077..830e4a97da 100644 --- a/engines/toltecs/music.cpp +++ b/engines/toltecs/music.cpp @@ -20,15 +20,13 @@ * */ -// FIXME: This code is taken from SAGA and needs more work (e.g. setVolume). +#include "audio/midiparser.h" +#include "common/textconsole.h" #include "toltecs/toltecs.h" #include "toltecs/music.h" #include "toltecs/resource.h" -#include "audio/midiparser.h" -#include "common/textconsole.h" - namespace Toltecs { MusicPlayer::MusicPlayer(bool isGM) : _isGM(isGM), _buffer(NULL) { @@ -62,7 +60,7 @@ void MusicPlayer::playMIDI(const byte *data, uint32 size, bool loop) { memcpy(_buffer, data, size); MidiParser *parser; - + if (!memcmp(data, "FORM", 4)) parser = MidiParser::createParser_XMIDI(NULL); else @@ -77,7 +75,7 @@ void MusicPlayer::playMIDI(const byte *data, uint32 size, bool loop) { _parser = parser; - setVolume(127); + syncVolume(); _isLooping = loop; _isPlaying = true; @@ -86,16 +84,6 @@ void MusicPlayer::playMIDI(const byte *data, uint32 size, bool loop) { } } -void MusicPlayer::pause() { - setVolume(-1); - _isPlaying = false; -} - -void MusicPlayer::resume() { - setVolume(127); - _isPlaying = true; -} - void MusicPlayer::stopAndClear() { Common::StackLock lock(_mutex); stop(); diff --git a/engines/toltecs/music.h b/engines/toltecs/music.h index 79df1ea2f5..8d364dbb9f 100644 --- a/engines/toltecs/music.h +++ b/engines/toltecs/music.h @@ -37,8 +37,6 @@ public: MusicPlayer(bool isGM = true); void playMIDI(const byte *data, uint32 size, bool loop = false); - void pause(); - void resume(); void stopAndClear(); // MidiDriver_BASE interface implementation diff --git a/engines/toltecs/palette.cpp b/engines/toltecs/palette.cpp index 706218e0ba..b93bb8b510 100644 --- a/engines/toltecs/palette.cpp +++ b/engines/toltecs/palette.cpp @@ -31,7 +31,9 @@ namespace Toltecs { Palette::Palette(ToltecsEngine *vm) : _vm(vm) { clearFragments(); - + + memset(_mainPalette, 0, sizeof(_mainPalette)); + memset(_animPalette, 0, sizeof(_animPalette)); memset(_colorTransTable, 0, sizeof(_colorTransTable)); } @@ -81,7 +83,7 @@ void Palette::setDeltaPalette(byte *palette, byte mask, int8 deltaValue, int16 c if (mask & 4) colors[index * 3 + 2] = CLIP<int>(rgb + deltaValue, 0, 63) << 2; index++; } - + debug(0, "startIndex = %d; colorCount = %d", startIndex, colorCount); _vm->_system->getPaletteManager()->setPalette((const byte *)colors, 0, 256); @@ -101,9 +103,9 @@ void Palette::addFragment(uint resIndex, int16 id) { Resource *fragmentResource = _vm->_res->load(resIndex); byte count = fragmentResource->size / 3; - + memcpy(&_mainPalette[_fragmentIndex * 3], fragmentResource->data, count * 3); - + PaletteFragment fragment; fragment.id = id; fragment.index = _fragmentIndex; @@ -126,7 +128,7 @@ uint16 Palette::findFragment(int16 id) { break; } } - + debug(0, "Palette::findFragment() result = %04X", result); return result; @@ -138,52 +140,48 @@ void Palette::clearFragments() { _fragments.clear(); } +byte Palette::getMatchingColor(byte r, byte g, byte b) { + int bestIndex = 0; + uint16 bestMatch = 0xFFFF; + + for (int j = 0; j < 256; j++) { + byte distance = ABS(_mainPalette[j * 3 + 0] - r) + ABS(_mainPalette[j * 3 + 1] - g) + ABS(_mainPalette[j * 3 + 2] - b); + byte maxColor = MAX(_mainPalette[j * 3 + 0], MAX(_mainPalette[j * 3 + 1], _mainPalette[j * 3 + 2])); + uint16 match = (distance << 8) | maxColor; + if (match < bestMatch) { + bestMatch = match; + bestIndex = j; + } + } + + return bestIndex; +} + void Palette::buildColorTransTable(byte limit, int8 deltaValue, byte mask) { byte r = 0, g = 0, b = 0; - + mask &= 7; - - for (int i = 0; i < 256; i++) { - if (deltaValue < 0) { - // TODO (probably unused) - warning("Palette::buildColorTransTable(%d, %d, %02X) not yet implemented!", limit, deltaValue, mask); - } else { - r = _mainPalette[i * 3 + 0]; - g = _mainPalette[i * 3 + 1]; - b = _mainPalette[i * 3 + 2]; - if (MAX(r, MAX(b, g)) >= limit) { - if ((mask & 1) && r >= deltaValue) - r -= deltaValue; - if ((mask & 2) && g >= deltaValue) - g -= deltaValue; - if ((mask & 4) && b >= deltaValue) - b -= deltaValue; - } - } - - int bestIndex = 0; - uint16 bestMatch = 0xFFFF; - - for (int j = 0; j < 256; j++) { - byte distance = ABS(_mainPalette[j * 3 + 0] - r) + ABS(_mainPalette[j * 3 + 1] - g) + ABS(_mainPalette[j * 3 + 2] - b); - byte maxColor = MAX(_mainPalette[j * 3 + 0], MAX(_mainPalette[j * 3 + 1], _mainPalette[j * 3 + 2])); - uint16 match = (distance << 8) | maxColor; - if (match < bestMatch) { - bestMatch = match; - bestIndex = j; - } + if (deltaValue < 0) // unused + error("buildColorTransTable called with a negative delta value(limit %d, delta %d, mask %02X)", limit, deltaValue, mask); + + for (int i = 0; i < 256; i++) { + r = _mainPalette[i * 3 + 0]; + g = _mainPalette[i * 3 + 1]; + b = _mainPalette[i * 3 + 2]; + if (MAX(r, MAX(b, g)) >= limit) { + if ((mask & 1) && r >= deltaValue) + r -= deltaValue; + if ((mask & 2) && g >= deltaValue) + g -= deltaValue; + if ((mask & 4) && b >= deltaValue) + b -= deltaValue; } - - _colorTransTable[i] = bestIndex; + _colorTransTable[i] = getMatchingColor(r, g, b); } } -void Palette::buildColorTransTable2(byte limit, int8 deltaValue, byte mask) { - // TODO -} - void Palette::saveState(Common::WriteStream *out) { // Save currently active palette byte palette[768]; diff --git a/engines/toltecs/palette.h b/engines/toltecs/palette.h index 7bcf06e027..4777a82699 100644 --- a/engines/toltecs/palette.h +++ b/engines/toltecs/palette.h @@ -50,8 +50,8 @@ public: uint16 findFragment(int16 id); void clearFragments(); + byte getMatchingColor(byte r, byte g, byte b); void buildColorTransTable(byte limit, int8 deltaValue, byte mask); - void buildColorTransTable2(byte limit, int8 deltaValue, byte mask); byte getColorTransPixel(byte pixel) const { return _colorTransTable[pixel]; } byte *getMainPalette() { return _mainPalette; } @@ -66,7 +66,7 @@ protected: int16 id; byte index, count; }; - + typedef Common::Array<PaletteFragment> PaletteFragmentArray; ToltecsEngine *_vm; diff --git a/engines/toltecs/render.cpp b/engines/toltecs/render.cpp index 3f5356493e..4c41e6ce00 100644 --- a/engines/toltecs/render.cpp +++ b/engines/toltecs/render.cpp @@ -114,7 +114,7 @@ void RenderQueue::addMask(SegmapMaskRect &mask) { void RenderQueue::update() { bool doFullRefresh = _vm->_screen->_fullRefresh; - + _updateUta->clear(); if (!doFullRefresh) { @@ -166,7 +166,7 @@ void RenderQueue::update() { for (RenderQueueArray::iterator iter = _currQueue->begin(); iter != _currQueue->end(); iter++) { const RenderQueueItem *item = &(*iter); - + if (item->flags == kRefresh || doFullRefresh) { switch (item->type) { @@ -200,7 +200,7 @@ void RenderQueue::update() { SWAP(_currQueue, _prevQueue); _currQueue->clear(); - + } void RenderQueue::clear() { @@ -249,16 +249,16 @@ bool RenderQueue::hasItemChanged(const RenderQueueItem &item1, const RenderQueue if (item1.type != item2.type) return true; - + if (item1.rect.left != item2.rect.left || item1.rect.top != item2.rect.top || item1.rect.right != item2.rect.right || item1.rect.bottom != item2.rect.bottom) return true; - + if (item1.type == kText && item1.text.color != item2.text.color) return true; - + return false; } @@ -268,7 +268,7 @@ void RenderQueue::invalidateItemsByRect(const Common::Rect &rect, const RenderQu if (item != subItem && subItem->flags == kUnchanged && rect.intersects(subItem->rect)) { - + subItem->flags = kRefresh; invalidateItemsByRect(subItem->rect, subItem); } diff --git a/engines/toltecs/render.h b/engines/toltecs/render.h index bb9ec29959..59d7a3ddb9 100644 --- a/engines/toltecs/render.h +++ b/engines/toltecs/render.h @@ -75,7 +75,7 @@ public: void addMask(SegmapMaskRect &mask); void update(); void clear(); - + protected: typedef Common::List<RenderQueueItem> RenderQueueArray; @@ -87,7 +87,7 @@ protected: RenderQueueItem *findItemInQueue(RenderQueueArray *queue, const RenderQueueItem &item); bool hasItemChanged(const RenderQueueItem &item1, const RenderQueueItem &item2); void invalidateItemsByRect(const Common::Rect &rect, const RenderQueueItem *item); - + void addDirtyRect(const Common::Rect &rect); void restoreDirtyBackground(); void updateDirtyRects(); diff --git a/engines/toltecs/resource.cpp b/engines/toltecs/resource.cpp index b95e0444b1..d66075004b 100644 --- a/engines/toltecs/resource.cpp +++ b/engines/toltecs/resource.cpp @@ -61,16 +61,11 @@ uint32 ArchiveReader::getResourceSize(uint resIndex) { return _offsets[resIndex + 1] - _offsets[resIndex]; } -void ArchiveReader::dump(uint resIndex, const char *prefix) { +void ArchiveReader::dump(uint resIndex) { int32 resourceSize = getResourceSize(resIndex); byte *data = new byte[resourceSize]; - Common::String fn; - - if (prefix) - fn = Common::String::format("%s_%04X.0", prefix, resIndex); - else - fn = Common::String::format("%04X.0", resIndex); + Common::String fn = Common::String::format("toltecs_res.%03d", resIndex); openResource(resIndex); read(data, resourceSize); @@ -112,16 +107,18 @@ Resource *ResourceCache::load(uint resIndex) { } else { debug(1, "ResourceCache::load(%d) From disk", resIndex); + int32 curPos = _vm->_arc->pos(); Resource *resItem = new Resource(); resItem->size = _vm->_arc->openResource(resIndex); resItem->data = new byte[resItem->size]; _vm->_arc->read(resItem->data, resItem->size); _vm->_arc->closeResource(); - + _vm->_arc->seek(curPos); + _cache[resIndex] = resItem; - + return resItem; - + } } diff --git a/engines/toltecs/resource.h b/engines/toltecs/resource.h index 3fed2e11ca..3d45d9fb1b 100644 --- a/engines/toltecs/resource.h +++ b/engines/toltecs/resource.h @@ -50,7 +50,7 @@ public: // Returns the size of the resource uint32 getResourceSize(uint resIndex); - void dump(uint resIndex, const char *prefix = NULL); + void dump(uint resIndex); protected: uint32 *_offsets; diff --git a/engines/toltecs/saveload.cpp b/engines/toltecs/saveload.cpp index c24d2149b0..6c195a34c2 100644 --- a/engines/toltecs/saveload.cpp +++ b/engines/toltecs/saveload.cpp @@ -36,12 +36,11 @@ namespace Toltecs { /* TODO: - - Save with F7; Load with F9 - Saving during an animation (AnimationPlayer) is not working correctly yet - Maybe switch to SCUMM/Tinsel serialization approach? */ -#define TOLTECS_SAVEGAME_VERSION 3 +#define TOLTECS_SAVEGAME_VERSION 4 ToltecsEngine::kReadSaveHeaderError ToltecsEngine::readSaveHeader(Common::SeekableReadStream *in, bool loadThumbnail, SaveHeader &header) { @@ -93,7 +92,7 @@ void ToltecsEngine::savegame(const char *filename, const char *description) { byte descriptionLen = strlen(description); out->writeByte(descriptionLen); out->write(description, descriptionLen); - + Graphics::saveThumbnail(*out); // Not used yet, reserved for future usage @@ -141,8 +140,8 @@ void ToltecsEngine::savegame(const char *filename, const char *description) { } void ToltecsEngine::loadgame(const char *filename) { - Common::InSaveFile *in; - if (!(in = g_system->getSavefileManager()->openForLoading(filename))) { + Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(filename); + if (!in) { warning("Can't open file '%s', game not loaded", filename); return; } @@ -150,13 +149,13 @@ void ToltecsEngine::loadgame(const char *filename) { SaveHeader header; kReadSaveHeaderError errorCode = readSaveHeader(in, false, header); - + if (errorCode != kRSHENoError) { warning("Error loading savegame '%s'", filename); delete in; return; } - + _sound->stopAll(); _music->stopSequence(); g_engine->setTotalPlayTime(header.playTime * 1000); @@ -182,7 +181,7 @@ void ToltecsEngine::loadgame(const char *filename) { _mouseX = in->readUint16LE(); _mouseY = in->readUint16LE(); _mouseDisabled = in->readUint16LE(); - + _system->warpMouse(_mouseX, _mouseY); _system->showMouse(_mouseDisabled == 0); @@ -191,7 +190,7 @@ void ToltecsEngine::loadgame(const char *filename) { _anim->loadState(in); _screen->loadState(in); if (header.version >= 2) - _sound->loadState(in); + _sound->loadState(in, header.version); if (header.version >= 3) _music->loadState(in); diff --git a/engines/toltecs/screen.cpp b/engines/toltecs/screen.cpp index 634917a7b1..be91130c0a 100644 --- a/engines/toltecs/screen.cpp +++ b/engines/toltecs/screen.cpp @@ -33,7 +33,6 @@ namespace Toltecs { Screen::Screen(ToltecsEngine *vm) : _vm(vm) { - _frontScreen = new byte[268800]; _backScreen = new byte[870400]; @@ -43,9 +42,11 @@ Screen::Screen(ToltecsEngine *vm) : _vm(vm) { // Screen shaking _shakeActive = false; + _shakeTime = 0; _shakeCounterInit = 0; _shakeCounter = 0; _shakePos = 0; + _shakeTime = 0; // Verb line _verbLineNum = 0; @@ -66,16 +67,13 @@ Screen::Screen(ToltecsEngine *vm) : _vm(vm) { _renderQueue = new RenderQueue(_vm); _fullRefresh = false; _guiRefresh = false; - } Screen::~Screen() { - delete[] _frontScreen; delete[] _backScreen; - - delete _renderQueue; + delete _renderQueue; } void Screen::unpackRle(byte *source, byte *dest, uint16 width, uint16 height) { @@ -118,7 +116,6 @@ void Screen::loadMouseCursor(uint resIndex) { } void Screen::drawGuiImage(int16 x, int16 y, uint resIndex) { - byte *imageData = _vm->_res->load(resIndex)->data; int16 headerSize = READ_LE_UINT16(imageData); int16 width = imageData[2]; @@ -129,7 +126,7 @@ void Screen::drawGuiImage(int16 x, int16 y, uint resIndex) { byte *dest = _frontScreen + x + (y + _vm->_cameraHeight) * 640; //debug(0, "Screen::drawGuiImage() x = %d; y = %d; w = %d; h = %d; resIndex = %d", x, y, width, height, resIndex); - + while (workHeight > 0) { int count = 1; byte pixel = *imageData++; @@ -151,11 +148,11 @@ void Screen::drawGuiImage(int16 x, int16 y, uint resIndex) { } _guiRefresh = true; - } void Screen::startShakeScreen(int16 shakeCounter) { _shakeActive = true; + _shakeTime = 0; _shakeCounterInit = shakeCounter; _shakeCounter = shakeCounter; _shakePos = 0; @@ -166,19 +163,22 @@ void Screen::stopShakeScreen() { _vm->_system->setShakePos(0); } -void Screen::updateShakeScreen() { - if (_shakeActive) { +bool Screen::updateShakeScreen() { + // Assume shaking happens no more often than 50 times per second + if (_shakeActive && _vm->_system->getMillis() - _shakeTime >= 20) { + _shakeTime = _vm->_system->getMillis(); _shakeCounter--; if (_shakeCounter == 0) { _shakeCounter = _shakeCounterInit; _shakePos ^= 8; _vm->_system->setShakePos(_shakePos); + return true; } } + return false; } void Screen::addStaticSprite(byte *spriteItem) { - DrawRequest drawRequest; memset(&drawRequest, 0, sizeof(drawRequest)); @@ -193,11 +193,9 @@ void Screen::addStaticSprite(byte *spriteItem) { debug(0, "Screen::addStaticSprite() x = %d; y = %d; baseColor = %d; resIndex = %d; flags = %04X", drawRequest.x, drawRequest.y, drawRequest.baseColor, drawRequest.resIndex, drawRequest.flags); addDrawRequest(drawRequest); - } void Screen::addAnimatedSprite(int16 x, int16 y, int16 fragmentId, byte *data, int16 *spriteArray, bool loop, int mode) { - //debug(0, "Screen::addAnimatedSprite(%d, %d, %d)", x, y, fragmentId); DrawRequest drawRequest; @@ -247,12 +245,10 @@ void Screen::addAnimatedSprite(int16 x, int16 y, int16 fragmentId, byte *data, i } else { loopNum |= 0x8000; } - + WRITE_LE_UINT16(spriteItem + 0, loopNum); WRITE_LE_UINT16(spriteItem + 4, frameNum); - } - } void Screen::clearSprites() { @@ -260,7 +256,6 @@ void Screen::clearSprites() { } void Screen::blastSprite(int16 x, int16 y, int16 fragmentId, int16 resIndex, uint16 flags) { - DrawRequest drawRequest; SpriteDrawItem sprite; @@ -276,11 +271,9 @@ void Screen::blastSprite(int16 x, int16 y, int16 fragmentId, int16 resIndex, uin sprite.y -= _vm->_cameraY; drawSprite(sprite); } - } void Screen::updateVerbLine(int16 slotIndex, int16 slotOffset) { - debug(0, "Screen::updateVerbLine() _verbLineNum = %d; _verbLineX = %d; _verbLineY = %d; _verbLineWidth = %d; _verbLineCount = %d", _verbLineNum, _verbLineX, _verbLineY, _verbLineWidth, _verbLineCount); @@ -308,9 +301,9 @@ void Screen::updateVerbLine(int16 slotIndex, int16 slotOffset) { wrapState.len2 = 0; y = _verbLineY; - + memset(wrapState.textBuffer, 0, sizeof(wrapState.textBuffer)); - + for (int16 i = 0; i <= _verbLineNum; i++) { wrapState.sourceString = _vm->_script->getSlotData(_verbLineItems[i].slotIndex) + _verbLineItems[i].slotOffset; len = wrapGuiText(_fontResIndexArray[0], _verbLineWidth, wrapState); @@ -331,30 +324,28 @@ void Screen::updateVerbLine(int16 slotIndex, int16 slotOffset) { wrapState.sourceString++; wrapState.len1 -= len; wrapState.len2 = len + 1; - - drawGuiText(_verbLineX - 1 - (wrapState.width / 2), y, 0xF9, 0xFF, _fontResIndexArray[0], wrapState); + + drawGuiText(_verbLineX - 1 - (wrapState.width / 2), y - 1, 0xF9, 0xFF, _fontResIndexArray[0], wrapState); wrapState.destString = wrapState.textBuffer; wrapState.width = 0; len = wrapGuiText(_fontResIndexArray[0], _verbLineWidth, wrapState); wrapState.len1 += len; - + y += 9; } y += 9; } - + wrapState.len1 -= len; wrapState.len2 = len; - drawGuiText(_verbLineX - 1 - (wrapState.width / 2), y, 0xF9, 0xFF, _fontResIndexArray[0], wrapState); + drawGuiText(_verbLineX - 1 - (wrapState.width / 2), y - 1, 0xF9, 0xFF, _fontResIndexArray[0], wrapState); _guiRefresh = true; - } -void Screen::updateTalkText(int16 slotIndex, int16 slotOffset) { - +void Screen::updateTalkText(int16 slotIndex, int16 slotOffset, bool alwaysDisplayed) { int16 x, y, maxWidth, width, length; byte durationModifier = 1; byte *textData = _vm->_script->getSlotData(slotIndex) + slotOffset; @@ -363,6 +354,7 @@ void Screen::updateTalkText(int16 slotIndex, int16 slotOffset) { item->fontNum = 0; item->color = _talkTextFontColor; + item->alwaysDisplayed = alwaysDisplayed; x = CLIP<int16>(_talkTextX - _vm->_cameraX, 120, _talkTextMaxWidth); y = CLIP<int16>(_talkTextY - _vm->_cameraY, 4, _vm->_cameraHeight - 16); @@ -448,11 +440,9 @@ void Screen::updateTalkText(int16 slotIndex, int16 slotOffset) { textDurationMultiplier += 100; } item->duration = 4 * textDurationMultiplier * durationModifier; - } void Screen::addTalkTextRect(Font &font, int16 x, int16 &y, int16 length, int16 width, TalkTextItem *item) { - if (width > 0) { TextRect *textRect = &item->lines[item->lineCount]; width = width + 1 - font.getSpacing(); @@ -463,13 +453,11 @@ void Screen::addTalkTextRect(Font &font, int16 x, int16 &y, int16 length, int16 textRect->x = CLIP<int16>(x - width / 2, 0, 640); item->lineCount++; } - - y += font.getHeight() - 1; + y += font.getHeight() - 1; } void Screen::addTalkTextItemsToRenderQueue() { - for (int16 i = 0; i <= _talkTextItemNum; i++) { TalkTextItem *item = &_talkTextItems[i]; byte *text = _vm->_script->getSlotData(item->slotIndex) + item->slotOffset; @@ -482,14 +470,24 @@ void Screen::addTalkTextItemsToRenderQueue() { if (item->duration < 0) item->duration = 0; + if (!_vm->_cfgText && !item->alwaysDisplayed) + return; + for (byte j = 0; j < item->lineCount; j++) { - _renderQueue->addText(item->lines[j].x, item->lines[j].y, item->color, _fontResIndexArray[item->fontNum], - text, item->lines[j].length); + _renderQueue->addText(item->lines[j].x, item->lines[j].y, item->color, + _fontResIndexArray[item->fontNum], text, item->lines[j].length); text += item->lines[j].length; } - } +} +bool Screen::isTalkTextActive(int16 slotIndex) { + for (int16 i = 0; i <= _talkTextItemNum; i++) { + if (_talkTextItems[i].slotIndex == slotIndex && _talkTextItems[i].duration > 0) + return true; + } + + return false; } int16 Screen::getTalkTextDuration() { @@ -517,7 +515,6 @@ void Screen::registerFont(uint fontIndex, uint resIndex) { } void Screen::drawGuiTextMulti(byte *textData) { - int16 x = 0, y = 0; // Really strange stuff. @@ -547,19 +544,18 @@ void Screen::drawGuiTextMulti(byte *textData) { wrapState.width = 0; wrapState.len1 = 0; wrapState.len2 = wrapGuiText(_fontResIndexArray[1], 640, wrapState); - drawGuiText(x - wrapState.width / 2, y, _fontColor1, _fontColor2, _fontResIndexArray[1], wrapState); + drawGuiText(x - wrapState.width / 2, y - 1, _fontColor1, _fontColor2, _fontResIndexArray[1], wrapState); } } while (*wrapState.sourceString != 0xFF); _guiRefresh = true; - } int16 Screen::wrapGuiText(uint fontResIndex, int maxWidth, GuiTextWrapState &wrapState) { Font font(_vm->_res->load(fontResIndex)->data); int16 len = 0; - + while (*wrapState.sourceString >= 0x20 && *wrapState.sourceString < 0xF0) { byte ch = *wrapState.sourceString; byte charWidth; @@ -573,9 +569,8 @@ int16 Screen::wrapGuiText(uint fontResIndex, int maxWidth, GuiTextWrapState &wra wrapState.width += charWidth; *wrapState.destString++ = *wrapState.sourceString++; } - + return len; - } void Screen::drawGuiText(int16 x, int16 y, byte fontColor1, byte fontColor2, uint fontResIndex, GuiTextWrapState &wrapState) { @@ -586,11 +581,9 @@ void Screen::drawGuiText(int16 x, int16 y, byte fontColor1, byte fontColor2, uin x = drawString(x + 1, y + _vm->_cameraHeight, fontColor1, fontResIndex, wrapState.textBuffer, wrapState.len1, &ywobble, false); x = drawString(x, y + _vm->_cameraHeight, fontColor2, fontResIndex, wrapState.textBuffer + wrapState.len1, wrapState.len2, &ywobble, false); - } int16 Screen::drawString(int16 x, int16 y, byte color, uint fontResIndex, const byte *text, int len, int16 *ywobble, bool outline) { - //debug(0, "Screen::drawString(%d, %d, %d, %d)", x, y, color, fontResIndex); Font font(_vm->_res->load(fontResIndex)->data); @@ -607,7 +600,7 @@ int16 Screen::drawString(int16 x, int16 y, byte color, uint fontResIndex, const if (ch <= 0x20) { x += font.getWidth(); } else { - drawChar(font, _frontScreen, x, y - yadd, ch, color, outline); + drawChar(font, _frontScreen, x, y + yadd, ch, color, outline); x += font.getCharWidth(ch) + font.getSpacing() - 1; yadd = -yadd; } @@ -617,11 +610,9 @@ int16 Screen::drawString(int16 x, int16 y, byte color, uint fontResIndex, const *ywobble = yadd; return x; - } void Screen::drawChar(const Font &font, byte *dest, int16 x, int16 y, byte ch, byte color, bool outline) { - int16 charWidth, charHeight; byte *charData; @@ -650,11 +641,9 @@ void Screen::drawChar(const Font &font, byte *dest, int16 x, int16 y, byte ch, b } dest += 640 - charWidth; } - } void Screen::drawSurface(int16 x, int16 y, Graphics::Surface *surface) { - int16 skipX = 0; int16 width = surface->w; int16 height = surface->h; @@ -699,11 +688,9 @@ void Screen::drawSurface(int16 x, int16 y, Graphics::Surface *surface) { frontScreen += 640 - width; surfacePixels += surface->w - width - skipX; } - } void Screen::saveState(Common::WriteStream *out) { - // Save verb line out->writeUint16LE(_verbLineNum); out->writeUint16LE(_verbLineX); @@ -750,11 +737,9 @@ void Screen::saveState(Common::WriteStream *out) { out->writeUint32LE(_fontResIndexArray[i]); out->writeByte(_fontColor1); out->writeByte(_fontColor2); - } void Screen::loadState(Common::ReadStream *in) { - // Load verb line _verbLineNum = in->readUint16LE(); _verbLineX = in->readUint16LE(); @@ -765,7 +750,7 @@ void Screen::loadState(Common::ReadStream *in) { _verbLineItems[i].slotIndex = in->readUint16LE(); _verbLineItems[i].slotOffset = in->readUint16LE(); } - + // Load talk text items _talkTextX = in->readUint16LE(); _talkTextY = in->readUint16LE(); @@ -779,6 +764,7 @@ void Screen::loadState(Common::ReadStream *in) { _talkTextItems[i].fontNum = in->readUint16LE(); _talkTextItems[i].color = in->readByte(); _talkTextItems[i].lineCount = in->readByte(); + _talkTextItems[i].alwaysDisplayed = false; for (int j = 0; j < _talkTextItems[i].lineCount; j++) { _talkTextItems[i].lines[j].x = in->readUint16LE(); _talkTextItems[i].lines[j].y = in->readUint16LE(); @@ -786,7 +772,7 @@ void Screen::loadState(Common::ReadStream *in) { _talkTextItems[i].lines[j].length = in->readUint16LE(); } } - + // Load GUI bitmap { byte *gui = _frontScreen + _vm->_cameraHeight * 640; @@ -802,7 +788,6 @@ void Screen::loadState(Common::ReadStream *in) { _fontResIndexArray[i] = in->readUint32LE(); _fontColor1 = in->readByte(); _fontColor2 = in->readByte(); - } } // End of namespace Toltecs diff --git a/engines/toltecs/screen.h b/engines/toltecs/screen.h index 988f59c840..ee565e1882 100644 --- a/engines/toltecs/screen.h +++ b/engines/toltecs/screen.h @@ -136,6 +136,7 @@ struct TalkTextItem { byte color; byte lineCount; TextRect lines[15]; + bool alwaysDisplayed; }; struct GuiTextWrapState { @@ -154,15 +155,15 @@ public: ~Screen(); void unpackRle(byte *source, byte *dest, uint16 width, uint16 height); - + void loadMouseCursor(uint resIndex); - + void drawGuiImage(int16 x, int16 y, uint resIndex); - + void startShakeScreen(int16 shakeCounter); void stopShakeScreen(); - void updateShakeScreen(); - + bool updateShakeScreen(); + // Sprite list void addStaticSprite(byte *spriteItem); void addAnimatedSprite(int16 x, int16 y, int16 fragmentId, byte *data, int16 *spriteArray, bool loop, int mode); @@ -175,12 +176,13 @@ public: // Verb line void updateVerbLine(int16 slotIndex, int16 slotOffset); - + // Talk text - void updateTalkText(int16 slotIndex, int16 slotOffset); + void updateTalkText(int16 slotIndex, int16 slotOffset, bool alwaysDisplayed); void addTalkTextRect(Font &font, int16 x, int16 &y, int16 length, int16 width, TalkTextItem *item); void addTalkTextItemsToRenderQueue(); int16 getTalkTextDuration(); + bool isTalkTextActive(int16 slotIndex); void finishTalkTextItems(); void keepTalkTextItemsAlive(); @@ -207,7 +209,7 @@ public: int16 slotIndex; int16 slotOffset; }; - + struct Rect { int16 x, y, width, height; }; @@ -215,12 +217,13 @@ public: ToltecsEngine *_vm; byte *_frontScreen, *_backScreen; - + uint _fontResIndexArray[10]; byte _fontColor1, _fontColor2; // Screen shaking bool _shakeActive; + uint32 _shakeTime; int16 _shakeCounterInit, _shakeCounter; int _shakePos; @@ -229,7 +232,7 @@ public: VerbLineItem _verbLineItems[8]; int16 _verbLineX, _verbLineY, _verbLineWidth; int16 _verbLineCount; - + // Talk text int16 _talkTextX, _talkTextY; int16 _talkTextMaxWidth; diff --git a/engines/toltecs/script.cpp b/engines/toltecs/script.cpp index 9683831980..9ea95a2cd1 100644 --- a/engines/toltecs/script.cpp +++ b/engines/toltecs/script.cpp @@ -40,6 +40,22 @@ namespace Toltecs { +static const VarType varTypes[] = { + vtByte, vtWord, vtWord, vtByte, vtWord, // 0 - 4 + vtWord, vtWord, vtWord, vtWord, vtWord, // 5 - 9 + vtWord, vtWord, vtByte, vtWord, vtWord, // 10 - 14 + vtWord, vtWord, vtWord, vtWord, vtWord, // 15 - 19 + vtWord, vtWord // 20 - 21 +}; + +static const char *varNames[] = { + "mouseDisabled", "mouseY", "mouseX", "mouseButton", "verbLineY", // 0 - 4 + "verbLineX", "verbLineWidth", "verbLineCount", "verbLineNum", "talkTextItemNum", // 5 - 9 + "talkTextY", "talkTextX", "talkTextFontColor", "cameraY", "cameraX", // 10 - 14 + "walkSpeedY", "walkSpeedX", "flag01", "sceneResIndex", "guiHeight", // 15 - 19 + "sceneHeight", "sceneWidth" // 20 - 21 +}; + ScriptInterpreter::ScriptInterpreter(ToltecsEngine *vm) : _vm(vm) { _stack = new byte[kScriptStackSize]; @@ -154,7 +170,6 @@ void ScriptInterpreter::setupScriptFunctions() { } void ScriptInterpreter::loadScript(uint resIndex, uint slotIndex) { - delete[] _slots[slotIndex].data; _slots[slotIndex].resIndex = resIndex; @@ -162,7 +177,6 @@ void ScriptInterpreter::loadScript(uint resIndex, uint slotIndex) { _slots[slotIndex].size = scriptResource->size; _slots[slotIndex].data = new byte[_slots[slotIndex].size]; memcpy(_slots[slotIndex].data, scriptResource->data, _slots[slotIndex].size); - } void ScriptInterpreter::setMainScript(uint slotIndex) { @@ -183,10 +197,7 @@ void ScriptInterpreter::setMainScript(uint slotIndex) { } void ScriptInterpreter::runScript() { - uint32 lastScreenUpdate = 0; - while (!_vm->shouldQuit()) { - if (_vm->_movieSceneFlag) _vm->_mouseButton = 0; @@ -197,7 +208,7 @@ void ScriptInterpreter::runScript() { _vm->saveGameState(_vm->_saveLoadSlot, _vm->_saveLoadDescription); _vm->_saveLoadRequested = 0; } - + if (_switchLocalDataNear) { _switchLocalDataNear = false; _localData = getSlotData(_regs.reg4); @@ -214,20 +225,10 @@ void ScriptInterpreter::runScript() { _localData = _stack + 2; _switchLocalDataNear = true; } - + byte opcode = readByte(); execOpcode(opcode); - - // Update the screen at semi-regular intervals, else the mouse - // cursor will be jerky. - uint32 now = _vm->_system->getMillis(); - if (now < lastScreenUpdate || now - lastScreenUpdate > 10) { - _vm->_system->updateScreen(); - lastScreenUpdate = _vm->_system->getMillis(); - } - } - } byte ScriptInterpreter::readByte() { @@ -241,10 +242,9 @@ int16 ScriptInterpreter::readInt16() { } void ScriptInterpreter::execOpcode(byte opcode) { - int16 ofs; - debug(1, "opcode = %d", opcode); + debug(2, "opcode = %d", opcode); switch (opcode) { case 0: @@ -252,35 +252,32 @@ void ScriptInterpreter::execOpcode(byte opcode) { // ok _subCode = _code; byte length = readByte(); - debug(1, "length = %d", length); + if (length == 0) { + warning("Possible script bug detected - opcode length is 0 when calling script function"); + return; + } + debug(2, "length = %d", length); uint16 index = readInt16(); - debug(1, "callScriptFunction %d", index); execScriptFunction(index); _code += length - 2; break; } case 1: - // ok _regs.reg0 = readInt16(); break; case 2: - // ok _regs.reg1 = readInt16(); break; case 3: - // ok _regs.reg3 = readInt16(); break; case 4: - // ok _regs.reg5 = _regs.reg0; break; case 5: - // ok _regs.reg3 = _regs.reg0; break; case 6: - // ok _regs.reg1 = _regs.reg0; break; case 7: @@ -481,73 +478,15 @@ void ScriptInterpreter::execOpcode(byte opcode) { } void ScriptInterpreter::execScriptFunction(uint16 index) { - debug(4, "execScriptFunction(%d)", index); if (index >= _scriptFuncs.size()) error("ScriptInterpreter::execScriptFunction() Invalid script function index %d", index); - debug(4, "%s", _scriptFuncNames[index]); + debug(1, "execScriptFunction %s (%d)", _scriptFuncNames[index], index); (*_scriptFuncs[index])(); } -VarType ScriptInterpreter::getGameVarType(uint variable) { - switch (variable) { - case 0: return vtByte; - case 1: return vtWord; - case 2: return vtWord; - case 3: return vtByte; - case 4: return vtWord; - case 5: return vtWord; - case 6: return vtWord; - case 7: return vtWord; - case 8: return vtWord; - case 9: return vtWord; - case 10: return vtWord; - case 11: return vtWord; - case 12: return vtByte; - case 13: return vtWord; - case 14: return vtWord; - case 15: return vtWord; - case 16: return vtWord; - case 17: return vtWord; - case 18: return vtWord; - case 19: return vtWord; - case 20: return vtWord; - case 21: return vtWord; - default: - error("Invalid game variable"); - } -} - -const char *getVarName(uint variable) { - switch (variable) { - case 0: return "mouseDisabled"; - case 1: return "mouseY"; - case 2: return "mouseX"; - case 3: return "mouseButton"; - case 4: return "verbLineY"; - case 5: return "verbLineX"; - case 6: return "verbLineWidth"; - case 7: return "verbLineCount"; - case 8: return "verbLineNum"; - case 9: return "talkTextItemNum"; - case 10: return "talkTextY"; - case 11: return "talkTextX"; - case 12: return "talkTextFontColor"; - case 13: return "cameraY"; - case 14: return "cameraX"; - case 15: return "walkSpeedY"; - case 16: return "walkSpeedX"; - case 17: return "flag01"; - case 18: return "sceneResIndex"; - case 19: return "guiHeight"; - case 20: return "sceneHeight"; - case 21: return "sceneWidth"; - } - return "(invalid)"; -} - int16 ScriptInterpreter::getGameVar(uint variable) { - debug(0, "ScriptInterpreter::getGameVar(%d{%s})", variable, getVarName(variable)); - + debug(2, "ScriptInterpreter::getGameVar(%d{%s})", variable, varNames[variable]); + switch (variable) { case 0: return _vm->_mouseDisabled; case 1: return _vm->_mouseY; @@ -572,14 +511,14 @@ int16 ScriptInterpreter::getGameVar(uint variable) { case 20: return _vm->_sceneHeight; case 21: return _vm->_sceneWidth; default: - warning("Getting unimplemented game variable %s (%d)", getVarName(variable), variable); + warning("Getting unimplemented game variable %s (%d)", varNames[variable], variable); return 0; } } void ScriptInterpreter::setGameVar(uint variable, int16 value) { - debug(0, "ScriptInterpreter::setGameVar(%d{%s}, %d)", variable, getVarName(variable), value); - + debug(2, "ScriptInterpreter::setGameVar(%d{%s}, %d)", variable, varNames[variable], value); + switch (variable) { case 0: _vm->_mouseDisabled = value; @@ -645,10 +584,9 @@ void ScriptInterpreter::setGameVar(uint variable, int16 value) { case 1: case 2: default: - warning("Setting unimplemented game variable %s (%d) to %d", getVarName(variable), variable, value); + warning("Setting unimplemented game variable %s (%d) to %d", varNames[variable], variable, value); break; } - } byte ScriptInterpreter::arg8(int16 offset) { @@ -670,32 +608,31 @@ int16 ScriptInterpreter::popInt16() { } void ScriptInterpreter::localWrite8(int16 offset, byte value) { - //debug(1, "localWrite8(%d, %d)", offset, value); + //debug(2, "localWrite8(%d, %d)", offset, value); _localData[offset] = value; } byte ScriptInterpreter::localRead8(int16 offset) { - //debug(1, "localRead8(%d) -> %d", offset, _localData[offset]); + //debug(2, "localRead8(%d) -> %d", offset, _localData[offset]); return _localData[offset]; } void ScriptInterpreter::localWrite16(int16 offset, int16 value) { - //debug(1, "localWrite16(%d, %d)", offset, value); + //debug(2, "localWrite16(%d, %d)", offset, value); WRITE_LE_UINT16(&_localData[offset], value); } int16 ScriptInterpreter::localRead16(int16 offset) { - //debug(1, "localRead16(%d) -> %d", offset, (int16)READ_LE_UINT16(&_localData[offset])); + //debug(2, "localRead16(%d) -> %d", offset, (int16)READ_LE_UINT16(&_localData[offset])); return (int16)READ_LE_UINT16(&_localData[offset]); } byte *ScriptInterpreter::localPtr(int16 offset) { - //debug(1, "localPtr(%d)", offset); + //debug(2, "localPtr(%d)", offset); return &_localData[offset]; } void ScriptInterpreter::saveState(Common::WriteStream *out) { - // Save registers out->writeUint16LE(_regs.reg0); out->writeUint16LE(_regs.reg1); @@ -718,14 +655,12 @@ void ScriptInterpreter::saveState(Common::WriteStream *out) { // Save stack out->write(_stack, kScriptStackSize); out->writeUint16LE(_savedSp); - + // Save IP out->writeUint16LE((int16)(_code - getSlotData(_regs.reg4))); - } void ScriptInterpreter::loadState(Common::ReadStream *in) { - // Load registers _regs.reg0 = in->readUint16LE(); _regs.reg1 = in->readUint16LE(); @@ -754,7 +689,6 @@ void ScriptInterpreter::loadState(Common::ReadStream *in) { // Load IP _code = getSlotData(_regs.reg4) + in->readUint16LE(); - } void ScriptInterpreter::sfNop() { @@ -768,7 +702,9 @@ void ScriptInterpreter::sfGetGameVar() { void ScriptInterpreter::sfSetGameVar() { int16 varIndex = arg16(3); - VarType varType = getGameVarType(varIndex); + assert(varIndex <= 21); + + VarType varType = varTypes[varIndex]; int16 value = 0; if (varType == vtByte) value = arg8(5); @@ -823,8 +759,7 @@ void ScriptInterpreter::sfSetDeltaAnimPalette() { } void ScriptInterpreter::sfSetUnkPaletteEffect() { - // TODO - debug("ScriptInterpreter::sfSetUnkPaletteEffect"); + error("ScriptInterpreter::sfSetUnkPaletteEffect called"); // unused } void ScriptInterpreter::sfBuildColorTransTable() { @@ -1005,7 +940,8 @@ void ScriptInterpreter::sfStopShakeScreen() { void ScriptInterpreter::sfStartSequence() { int16 sequenceResIndex = arg16(3); - //debug("ScriptInterpreter::sfStartSequence(%d)", sequenceResIndex); + debug(1, "ScriptInterpreter::sfStartSequence(%d)", sequenceResIndex); + if (sequenceResIndex >= 0) { //_vm->_arc->dump(sequenceResIndex, "music"); // DEBUG: Dump music so we know what's in there @@ -1014,7 +950,6 @@ void ScriptInterpreter::sfStartSequence() { } void ScriptInterpreter::sfEndSequence() { - //debug("ScriptInterpreter::sfEndSequence"); _vm->_music->stopSequence(); } @@ -1042,41 +977,34 @@ void ScriptInterpreter::sfHandleInput() { if (_vm->_rightButtonDown) { keyCode = 1; } else { - /* Convert keyboard scancode to IBM PC scancode - Only scancodes known to be used (so far) are converted - */ + // Convert keyboard scancode to IBM PC scancode. + // Only scancodes known to be used (so far) are converted. switch (_vm->_keyState.keycode) { - case Common::KEYCODE_ESCAPE: + case Common::KEYCODE_ESCAPE: keyCode = 1; break; case Common::KEYCODE_F10: keyCode = 68; break; default: - break; + break; } } localWrite16(varOfs, keyCode); } void ScriptInterpreter::sfRunOptionsScreen() { - _vm->_screen->loadMouseCursor(12); - _vm->_palette->loadAddPalette(9, 224); - _vm->_palette->setDeltaPalette(_vm->_palette->getMainPalette(), 7, 0, 31, 224); - _vm->_screen->finishTalkTextItems(); - _vm->_screen->clearSprites(); - CursorMan.showMouse(true); - _vm->_menuSystem->run(); - _vm->_keyState.reset(); - _switchLocalDataNear = true; + _vm->showMenu(kMenuIdMain); } -/* NOTE: The opcodes sfPrecacheSprites, sfPrecacheSounds1, sfPrecacheSounds2 and - sfDeletePrecachedFiles were used by the original engine to handle precaching - of data so the game doesn't stall while playing (due to the slow speed of - CD-Drives back then). This is not needed in ScummVM since all supported - systems are fast enough to load data in-game. */ - +/** + * NOTE: The opcodes sfPrecacheSprites, sfPrecacheSounds1, sfPrecacheSounds2 and + * sfDeletePrecachedFiles were used by the original engine to handle precaching + * of data so the game doesn't stall while playing (due to the slow speed of + * CD-Drives back then). This is not needed in ScummVM since all supported + * systems are fast enough to load data in-game. + */ + void ScriptInterpreter::sfPrecacheSprites() { // See note above } @@ -1102,7 +1030,9 @@ void ScriptInterpreter::sfSaveStackPtr() { } void ScriptInterpreter::sfPlayMovie() { + CursorMan.showMouse(false); _vm->_moviePlayer->playMovie(arg16(3)); + CursorMan.showMouse(true); } } // End of namespace Toltecs diff --git a/engines/toltecs/script.h b/engines/toltecs/script.h index 0c1898c525..4c880dfef5 100644 --- a/engines/toltecs/script.h +++ b/engines/toltecs/script.h @@ -49,13 +49,14 @@ public: byte *getSlotData(int slotIndex) const { return _slots[slotIndex].data; } - VarType getGameVarType(uint variable); int16 getGameVar(uint variable); void setGameVar(uint variable, int16 value); void saveState(Common::WriteStream *out); void loadState(Common::ReadStream *in); + void setSwitchLocalDataNear(bool newValue) { _switchLocalDataNear = newValue; } + protected: struct ScriptRegs { @@ -88,13 +89,13 @@ protected: bool _cmpBitTest; ScriptSlot _slots[kMaxScriptSlots]; - + ScriptRegs _regs; int16 _savedSp; byte readByte(); int16 readInt16(); - + void execOpcode(byte opcode); void setupScriptFunctions(); diff --git a/engines/toltecs/segmap.cpp b/engines/toltecs/segmap.cpp index f7d806c67b..b06c0af675 100644 --- a/engines/toltecs/segmap.cpp +++ b/engines/toltecs/segmap.cpp @@ -48,7 +48,7 @@ void SegmentMap::load(byte *source) { uint16 maskRectCount = READ_LE_UINT16(source); source += 2; uint16 maskRectDataSize = maskRectCount * 12 + 2; - + debug(0, "SegmentMap::load() maskRectCount = %d", maskRectCount); for (uint16 i = 0; i < maskRectCount; i++) { @@ -74,25 +74,25 @@ void SegmentMap::load(byte *source) { // Load path rects source += 2; // skip rects array size - + uint16 pathRectCount = READ_LE_UINT16(source); source += 2; - + debug(0, "SegmentMap::load() pathRectCount = %d", pathRectCount); - + for (uint16 i = 0; i < pathRectCount; i++) { SegmapPathRect pathRect; pathRect.y1 = READ_LE_UINT16(source); pathRect.x1 = READ_LE_UINT16(source + 2); pathRect.y2 = pathRect.y1 + READ_LE_UINT16(source + 4); pathRect.x2 = pathRect.x1 + READ_LE_UINT16(source + 6); - + debug(0, "SegmentMap::load() (%d, %d, %d, %d)", pathRect.x1, pathRect.y1, pathRect.x2, pathRect.y2); source += 8; _pathRects.push_back(pathRect); } - + // Load info rects source += 2; // skip rects array size @@ -141,7 +141,7 @@ void SegmentMap::adjustPathPoint(int16 &x, int16 &y) { uint32 minDistance = 0xFFFFFFFF, distance; int16 adjustedX = 0, adjustedY = 0, x2, y2; - + for (int16 rectIndex = 0; rectIndex < (int16)_pathRects.size(); rectIndex++) { if (x >= _pathRects[rectIndex].x1 && x < _pathRects[rectIndex].x2) { @@ -174,7 +174,7 @@ void SegmentMap::adjustPathPoint(int16 &x, int16 &y) { } } - + x = adjustedX; y = adjustedY; @@ -318,7 +318,7 @@ void SegmentMap::findPath(int16 *pointsArray, int16 destX, int16 destY, int16 so pointsArray[0] = 0; pointsArray[1] = TO_LE_16(_pathNodesCount + 1); } - + debug(0, "SegmentMap::findPath() count = %d", FROM_LE_16(pointsArray[1])); #if 0 // DEBUG: Draw the path we found @@ -335,7 +335,7 @@ void SegmentMap::findPath(int16 *pointsArray, int16 destX, int16 destY, int16 so sy = y; } #endif - + } int8 SegmentMap::getScalingAtPoint(int16 x, int16 y) { diff --git a/engines/toltecs/segmap.h b/engines/toltecs/segmap.h index 30182a6b71..dda0edeb88 100644 --- a/engines/toltecs/segmap.h +++ b/engines/toltecs/segmap.h @@ -61,14 +61,14 @@ public: void getRgbModifiertAtPoint(int16 x, int16 y, int16 id, byte &r, byte &g, byte &b); void addMasksToRenderQueue(); - + //protected: public: // for debugging purposes struct SegmapPathRect { int16 x1, y1, x2, y2; }; - + struct SegmapInfoRect { int16 y, x; int16 height, width; @@ -78,11 +78,11 @@ public: // for debugging purposes return py >= y && py <= y + height && px >= x && px <= x + width; } }; - + struct PathPoint { int16 y, x; }; - + typedef Common::Array<SegmapMaskRect> SegmapMaskRectArray; typedef Common::Array<SegmapPathRect> SegmapPathRectArray; typedef Common::Array<SegmapInfoRect> SegmapInfoRectArray; diff --git a/engines/toltecs/sound.cpp b/engines/toltecs/sound.cpp index c9ef00e31b..8afc0e7890 100644 --- a/engines/toltecs/sound.cpp +++ b/engines/toltecs/sound.cpp @@ -34,48 +34,38 @@ namespace Toltecs { Sound::Sound(ToltecsEngine *vm) : _vm(vm) { for (int i = 0; i < kMaxChannels; i++) { - channels[i].type = kChannelTypeEmpty; - channels[i].resIndex = -1; + clearChannel(i); } } Sound::~Sound() { } +void Sound::clearChannel(int channel) { + channels[channel].type = kChannelTypeEmpty; + channels[channel].resIndex = -1; + channels[channel].volume = 0; + channels[channel].panning = 0; +} + void Sound::playSpeech(int16 resIndex) { debug(0, "playSpeech(%d)", resIndex); - internalPlaySound(resIndex, kChannelTypeSpeech, 50 /*TODO*/, 0); + + if (_vm->_cfgVoices) + internalPlaySound(resIndex, kChannelTypeSpeech, 50 /*TODO*/, 0); } void Sound::playSound(int16 resIndex, int16 type, int16 volume) { - - // TODO: Use the right volumes - debug(0, "playSound(%d, %d, %d)", resIndex, type, volume); - - if (volume == -1 || type == -2) { - if (type == kChannelTypeBackground) { - internalPlaySound(resIndex, type, 50 /*TODO*/, 0); - } else { - internalPlaySound(resIndex, type, 100 /*TODO*/, 0); - } - } else { - internalPlaySound(resIndex, type, 100 /*TODO*/, 0); - } + internalPlaySound(resIndex, type, volume, 0); } void Sound::playSoundAtPos(int16 resIndex, int16 x, int16 y) { - debug(0, "playSoundAtPos(%d, %d, %d)", resIndex, x, y); - int16 volume, panning = 0, deltaX = 0; - int8 scaling = _vm->_segmap->getScalingAtPoint(x, y); - - if (scaling >= 0) - volume = 50 + ABS(scaling) / 2; - else - volume = 50 - ABS(scaling) / 2; + int16 volume = 50 + ABS(_vm->_segmap->getScalingAtPoint(x, y)) / 2; + int16 panning = 0, deltaX = 0; if (_vm->_cameraX > x) deltaX = _vm->_cameraX - x; @@ -85,41 +75,39 @@ void Sound::playSoundAtPos(int16 resIndex, int16 x, int16 y) { deltaX = 600; volume = ((100 - deltaX / 6) * volume) / 100; - + if (_vm->_cameraX + 320 != x) { panning = CLIP(x - (_vm->_cameraX + 320), -381, 381) / 3; } internalPlaySound(resIndex, 1, volume, panning); - } void Sound::internalPlaySound(int16 resIndex, int16 type, int16 volume, int16 panning) { + // Change the game's sound volume (0 - 100) to Scummvm's scale (0 - 255) + volume = (volume == -1) ? 255 : volume * 255 / 100; if (resIndex == -1) { // Stop all sounds _vm->_mixer->stopAll(); _vm->_screen->keepTalkTextItemsAlive(); for (int i = 0; i < kMaxChannels; i++) { - channels[i].type = kChannelTypeEmpty; - channels[i].resIndex = -1; + clearChannel(i); } } else if (type == -2) { // Stop sounds with specified resIndex for (int i = 0; i < kMaxChannels; i++) { if (channels[i].resIndex == resIndex) { _vm->_mixer->stopHandle(channels[i].handle); - channels[i].type = kChannelTypeEmpty; - channels[i].resIndex = -1; + clearChannel(i); } } } else { - - if (type == -3) { + if (type == kChannelTypeSpeech) { // Stop speech and play new sound stopSpeech(); } - + // Play new sound in empty channel int freeChannel = -1; for (int i = 0; i < kMaxChannels; i++) { @@ -128,7 +116,7 @@ void Sound::internalPlaySound(int16 resIndex, int16 type, int16 volume, int16 pa break; } } - + // If all channels are in use no new sound will be played if (freeChannel >= 0) { Resource *soundResource = _vm->_res->load(resIndex); @@ -141,19 +129,15 @@ void Sound::internalPlaySound(int16 resIndex, int16 type, int16 volume, int16 pa channels[freeChannel].type = type; channels[freeChannel].resIndex = resIndex; + channels[freeChannel].volume = volume; + channels[freeChannel].panning = panning; - Audio::Mixer::SoundType soundType = Audio::Mixer::kPlainSoundType; - /* - switch (type) { - } - */ + Audio::Mixer::SoundType soundType = getScummVMSoundType((SoundChannelType)type); _vm->_mixer->playStream(soundType, &channels[freeChannel].handle, - stream, -1, volume, panning); - } - - } - + stream, -1, volume, panning); + } // if (freeChannel >= 0) + } // resIndex } void Sound::updateSpeech() { @@ -170,8 +154,7 @@ void Sound::stopSpeech() { if (channels[i].type == kChannelTypeSpeech) { _vm->_mixer->stopHandle(channels[i].handle); _vm->_screen->keepTalkTextItemsAlive(); - channels[i].type = kChannelTypeEmpty; - channels[i].resIndex = -1; + clearChannel(i); } } } @@ -180,8 +163,7 @@ void Sound::stopAll() { for (int i = 0; i < kMaxChannels; i++) { _vm->_mixer->stopHandle(channels[i].handle); _vm->_screen->keepTalkTextItemsAlive(); - channels[i].type = kChannelTypeEmpty; - channels[i].resIndex = -1; + clearChannel(i); } } @@ -189,13 +171,22 @@ void Sound::saveState(Common::WriteStream *out) { for (int i = 0; i < kMaxChannels; i++) { out->writeSint16LE(channels[i].type); out->writeSint16LE(channels[i].resIndex); + out->writeSint16LE(channels[i].volume); + out->writeSint16LE(channels[i].panning); } } -void Sound::loadState(Common::ReadStream *in) { +void Sound::loadState(Common::ReadStream *in, int version) { for (int i = 0; i < kMaxChannels; i++) { channels[i].type = in->readSint16LE(); channels[i].resIndex = in->readSint16LE(); + if (version < 4) { + channels[i].volume = (channels[i].type == kChannelTypeBackground) ? 50 : 100; + channels[i].panning = 0; + } else { + channels[i].volume = in->readSint16LE(); + channels[i].panning = in->readSint16LE(); + } if (channels[i].type != kChannelTypeEmpty) { Resource *soundResource = _vm->_res->load(channels[i].resIndex); @@ -206,19 +197,26 @@ void Sound::loadState(Common::ReadStream *in) { DisposeAfterUse::NO), channels[i].type == kChannelTypeBackground ? 0 : 1); - Audio::Mixer::SoundType soundType = Audio::Mixer::kPlainSoundType; - /* - switch (type) { - } - */ - - // TODO: Volume and panning - int16 volume = (channels[i].type == kChannelTypeBackground) ? 50 : 100; + Audio::Mixer::SoundType soundType = getScummVMSoundType((SoundChannelType)channels[i].type); _vm->_mixer->playStream(soundType, &channels[i].handle, - stream, -1, volume, /*panning*/0); + stream, -1, channels[i].volume, channels[i].panning); } } } +Audio::Mixer::SoundType Sound::getScummVMSoundType(SoundChannelType type) const { + switch (type) { + case kChannelTypeBackground: + case kChannelTypeSfx: + return Audio::Mixer::kSFXSoundType; + case kChannelTypeSpeech: + return Audio::Mixer::kSpeechSoundType; + break; + default: + return Audio::Mixer::kSFXSoundType; + break; + } +} + } // End of namespace Toltecs diff --git a/engines/toltecs/sound.h b/engines/toltecs/sound.h index e292d22c0f..48a6cd1318 100644 --- a/engines/toltecs/sound.h +++ b/engines/toltecs/sound.h @@ -42,6 +42,8 @@ enum SoundChannelType { struct SoundChannel { int16 resIndex; int16 type; + int16 volume; + int16 panning; Audio::SoundHandle handle; }; @@ -60,15 +62,16 @@ public: void stopAll(); void saveState(Common::WriteStream *out); - void loadState(Common::ReadStream *in); + void loadState(Common::ReadStream *in, int version); protected: ToltecsEngine *_vm; SoundChannel channels[kMaxChannels]; + void clearChannel(int channel); void internalPlaySound(int16 resIndex, int16 type, int16 volume, int16 panning); - + Audio::Mixer::SoundType getScummVMSoundType(SoundChannelType type) const; }; diff --git a/engines/toltecs/sprite.cpp b/engines/toltecs/sprite.cpp index 7a02663793..6101eb7d85 100644 --- a/engines/toltecs/sprite.cpp +++ b/engines/toltecs/sprite.cpp @@ -199,7 +199,7 @@ bool Screen::createSpriteDrawItem(const DrawRequest &drawRequest, SpriteDrawItem sprite.frameNum = frameNum; spriteData = _vm->_res->load(drawRequest.resIndex)->data; - + if (drawRequest.flags & 0x1000) { sprite.flags |= 4; } @@ -207,7 +207,7 @@ bool Screen::createSpriteDrawItem(const DrawRequest &drawRequest, SpriteDrawItem if (drawRequest.flags & 0x2000) { sprite.flags |= 0x10; } - + if (drawRequest.flags & 0x4000) { sprite.flags |= 0x40; } @@ -218,7 +218,7 @@ bool Screen::createSpriteDrawItem(const DrawRequest &drawRequest, SpriteDrawItem if (spriteFrameEntry.w == 0 || spriteFrameEntry.h == 0) return false; - + sprite.offset = spriteFrameEntry.offset; sprite.width = spriteFrameEntry.w; @@ -263,12 +263,12 @@ bool Screen::createSpriteDrawItem(const DrawRequest &drawRequest, SpriteDrawItem xoffs -= (xoffs * scaleValue) / 100; yoffs -= (yoffs * scaleValue) / 100; } - + } - + sprite.x -= xoffs; sprite.y -= yoffs; - + sprite.yerror = sprite.ydelta; // Now we check if the sprite needs to be clipped @@ -283,7 +283,7 @@ bool Screen::createSpriteDrawItem(const DrawRequest &drawRequest, SpriteDrawItem sprite.height -= clipHeight; if (sprite.height <= 0) return false; - + sprite.y = _vm->_cameraY; // If the sprite is scaled @@ -311,7 +311,7 @@ bool Screen::createSpriteDrawItem(const DrawRequest &drawRequest, SpriteDrawItem } sprite.yerror = chopHeight; } - + spriteFrameData = spriteData + sprite.offset; // Now the sprite's offset is adjusted to point to the starting line if ((sprite.flags & 0x10) == 0) { @@ -439,7 +439,7 @@ void Screen::drawSpriteCore(byte *dest, SpriteFilter &reader, const SpriteDrawIt SpriteReaderStatus status; PixelPacket packet; - + byte *destp = dest; int16 skipX = sprite.skipX; @@ -459,7 +459,7 @@ void Screen::drawSpriteCore(byte *dest, SpriteFilter &reader, const SpriteDrawIt status = reader.readPacket(packet); } } - + if (w - packet.count < 0) packet.count = w; diff --git a/engines/toltecs/toltecs.cpp b/engines/toltecs/toltecs.cpp index 6d6c37dffd..1a399dacc0 100644 --- a/engines/toltecs/toltecs.cpp +++ b/engines/toltecs/toltecs.cpp @@ -39,6 +39,7 @@ #include "toltecs/toltecs.h" #include "toltecs/animation.h" +#include "toltecs/console.h" #include "toltecs/menu.h" #include "toltecs/movie.h" #include "toltecs/music.h" @@ -62,10 +63,8 @@ struct GameSettings { }; ToltecsEngine::ToltecsEngine(OSystem *syst, const ToltecsGameDescription *gameDesc) : Engine(syst), _gameDescription(gameDesc) { - - // Setup mixer - _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume")); - _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume")); + // Assign default values to the config manager, in case settings are missing + ConfMan.registerDefault("originalsaveload", "false"); _rnd = new Common::RandomSource("toltecs"); } @@ -85,7 +84,7 @@ Common::Error ToltecsEngine::run() { _flag01 = 0; _saveLoadRequested = 0; - + _cameraX = 0; _cameraY = 0; _newCameraX = 0; @@ -96,7 +95,7 @@ Common::Error ToltecsEngine::run() { _sceneWidth = 0; _sceneHeight = 0; - + _doSpeech = true; _doText = true; @@ -126,17 +125,28 @@ Common::Error ToltecsEngine::run() { _moviePlayer = new MoviePlayer(this); _music = new Music(_arc); _menuSystem = new MenuSystem(this); - + _sound = new Sound(this); + _console = new Console(this); + + _cfgText = ConfMan.getBool("subtitles"); + _cfgVoices = !ConfMan.getBool("speech_mute"); + + bool mute = false; + if (ConfMan.hasKey("mute")) + mute = ConfMan.getBool("mute"); + _mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, mute ? 0 : ConfMan.getInt("speech_volume")); + _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, mute ? 0 : ConfMan.getInt("music_volume")); + _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, mute ? 0 : ConfMan.getInt("sfx_volume")); syncSoundSettings(); CursorMan.showMouse(true); setupSysStrings(); -//#define TEST_MENU -#ifdef TEST_MENU +#if 0 + // Menu test _screen->registerFont(0, 0x0D); _screen->registerFont(1, 0x0E); _screen->loadMouseCursor(12); @@ -171,6 +181,7 @@ Common::Error ToltecsEngine::run() { _music->stopSequence(); _sound->stopAll(); + delete _console; delete _arc; delete _res; delete _screen; @@ -181,7 +192,7 @@ Common::Error ToltecsEngine::run() { delete _music; delete _moviePlayer; delete _menuSystem; - + delete _sound; return Common::kNoError; @@ -210,7 +221,6 @@ void ToltecsEngine::requestLoadgame(int slotNum) { } void ToltecsEngine::loadScene(uint resIndex) { - Resource *sceneResource = _res->load(resIndex); byte *scene = sceneResource->data; @@ -245,13 +255,10 @@ void ToltecsEngine::loadScene(uint resIndex) { _screen->_fullRefresh = true; _screen->_renderQueue->clear(); - } void ToltecsEngine::updateScreen() { - _sound->updateSpeech(); - _screen->updateShakeScreen(); // TODO: Set quit flag @@ -284,7 +291,6 @@ void ToltecsEngine::updateScreen() { _counter02 = (currUpdateTime - prevUpdateTime) / 13; } while (_counter02 == 0); prevUpdateTime = currUpdateTime; - } void ToltecsEngine::drawScreen() { @@ -305,13 +311,14 @@ void ToltecsEngine::drawScreen() { _screen->_guiRefresh = false; } + _console->onFrame(); _system->updateScreen(); + _system->delayMillis(10); updateCamera(); } void ToltecsEngine::updateInput() { - Common::Event event; Common::EventManager *eventMan = _system->getEventManager(); while (eventMan->pollEvent(event)) { @@ -321,15 +328,17 @@ void ToltecsEngine::updateInput() { //debug("key: flags = %02X; keycode = %d", _keyState.flags, _keyState.keycode); - // FIXME: This is just for debugging + if (event.kbd.hasFlags(Common::KBD_CTRL) && event.kbd.keycode == Common::KEYCODE_d) + _console->attach(); + switch (event.kbd.keycode) { - case Common::KEYCODE_F7: - savegame("toltecs.001", "Quicksave"); + case Common::KEYCODE_F5: + showMenu(kMenuIdSave); break; - case Common::KEYCODE_F9: - loadgame("toltecs.001"); + case Common::KEYCODE_F7: + showMenu(kMenuIdLoad); break; - case Common::KEYCODE_ESCAPE: + case Common::KEYCODE_SPACE: // Skip current dialog line, if a dialog is active if (_screen->getTalkTextDuration() > 0) { _sound->stopSpeech(); @@ -345,9 +354,6 @@ void ToltecsEngine::updateInput() { case Common::EVENT_KEYUP: _keyState.reset(); break; - case Common::EVENT_QUIT: - quitGame(); - break; case Common::EVENT_MOUSEMOVE: _mouseX = event.mouse.x; _mouseY = event.mouse.y; @@ -403,9 +409,7 @@ void ToltecsEngine::updateInput() { _mouseWaitForRelease = false; _mouseButton = 0; } - } - } void ToltecsEngine::setGuiHeight(int16 guiHeight) { @@ -422,7 +426,7 @@ void ToltecsEngine::setCamera(int16 x, int16 y) { _screen->finishTalkTextItems(); _screen->clearSprites(); - + _cameraX = x; _newCameraX = x; @@ -473,7 +477,6 @@ void ToltecsEngine::scrollCameraRight(int16 delta) { } void ToltecsEngine::updateCamera() { - if (_cameraX != _newCameraX) { _cameraX = _newCameraX; _screen->_fullRefresh = true; @@ -487,13 +490,16 @@ void ToltecsEngine::updateCamera() { } //debug(0, "ToltecsEngine::updateCamera() _cameraX = %d; _cameraY = %d", _cameraX, _cameraY); - } void ToltecsEngine::talk(int16 slotIndex, int16 slotOffset) { - byte *scanData = _script->getSlotData(slotIndex) + slotOffset; - + + // If there's another talk text at the requested slot and it's still + // active, don't overwrite it. Fixes bug #3600166. + if (_screen->isTalkTextActive(slotIndex)) + return; + while (*scanData < 0xF0) { if (*scanData == 0x19) { scanData++; @@ -506,26 +512,25 @@ void ToltecsEngine::talk(int16 slotIndex, int16 slotOffset) { } scanData++; } - + if (*scanData == 0xFE) { if (_doSpeech) { int16 resIndex = READ_LE_UINT16(scanData + 1); debug(0, "ToltecsEngine::talk() playSound(resIndex: %d)", resIndex); _sound->playSpeech(resIndex); } + if (_doText) { - _screen->updateTalkText(slotIndex, slotOffset); + _screen->updateTalkText(slotIndex, slotOffset, false); } else { _screen->keepTalkTextItemsAlive(); } } else { - _screen->updateTalkText(slotIndex, slotOffset); + _screen->updateTalkText(slotIndex, slotOffset, true); } - } void ToltecsEngine::walk(byte *walkData) { - int16 xdelta, ydelta, v8, v10, v11; int16 xstep, ystep; ScriptWalk walkInfo; @@ -540,7 +545,7 @@ void ToltecsEngine::walk(byte *walkData) { walkInfo.xerror = READ_LE_UINT16(walkData + 14); walkInfo.mulValue = READ_LE_UINT16(walkData + 16); walkInfo.scaling = READ_LE_UINT16(walkData + 18); - + walkInfo.scaling = -_segmap->getScalingAtPoint(walkInfo.x, walkInfo.y); if (walkInfo.y1 < walkInfo.y2) @@ -548,7 +553,7 @@ void ToltecsEngine::walk(byte *walkData) { else ystep = 1; ydelta = ABS(walkInfo.y1 - walkInfo.y2) * _walkSpeedY; - + if (walkInfo.x1 < walkInfo.x2) xstep = -1; else @@ -608,14 +613,13 @@ void ToltecsEngine::walk(byte *walkData) { WRITE_LE_UINT16(walkData + 14, walkInfo.xerror); WRITE_LE_UINT16(walkData + 16, walkInfo.mulValue); WRITE_LE_UINT16(walkData + 18, walkInfo.scaling); - } -int16 ToltecsEngine::findRectAtPoint(byte *rectData, int16 x, int16 y, int16 index, int16 itemSize, +int16 ToltecsEngine::findRectAtPoint(byte *rectData, int16 x, int16 y, int16 index, int16 itemSize, byte *rectDataEnd) { rectData += index * itemSize; - + while (rectData < rectDataEnd) { int16 rectY = READ_LE_UINT16(rectData); if (rectY == -10) @@ -633,9 +637,32 @@ int16 ToltecsEngine::findRectAtPoint(byte *rectData, int16 x, int16 y, int16 ind index++; rectData += itemSize; } - + return -1; +} + +void ToltecsEngine::showMenu(MenuID menuId) { + _screen->loadMouseCursor(12); + _palette->loadAddPalette(9, 224); + _palette->setDeltaPalette(_palette->getMainPalette(), 7, 0, 31, 224); + _screen->finishTalkTextItems(); + _screen->clearSprites(); + CursorMan.showMouse(true); + _menuSystem->run(menuId); + _keyState.reset(); + _script->setSwitchLocalDataNear(true); +} + +void ToltecsEngine::syncSoundSettings() { + Engine::syncSoundSettings(); + + bool mute = false; + if (ConfMan.hasKey("mute")) + mute = ConfMan.getBool("mute"); + _cfgVoicesVolume = (mute ? 0 : ConfMan.getInt("speech_volume")) * 20 / Audio::Mixer::kMaxChannelVolume; + _cfgMusicVolume = (mute ? 0 : ConfMan.getInt("music_volume")) * 20 / Audio::Mixer::kMaxChannelVolume; + _cfgSoundFXVolume = (mute ? 0 : ConfMan.getInt("sfx_volume")) * 20 / Audio::Mixer::kMaxChannelVolume; } } // End of namespace Toltecs diff --git a/engines/toltecs/toltecs.h b/engines/toltecs/toltecs.h index efa1f9d13a..0be2d2a646 100644 --- a/engines/toltecs/toltecs.h +++ b/engines/toltecs/toltecs.h @@ -42,6 +42,7 @@ struct ToltecsGameDescription; class AnimationPlayer; class ArchiveReader; +class Console; class Input; class MenuSystem; class MoviePlayer; @@ -81,6 +82,14 @@ enum SysString { kSysStrCount }; +enum MenuID { + kMenuIdNone, + kMenuIdMain, + kMenuIdSave, + kMenuIdLoad, + kMenuIdVolumes +}; + class ToltecsEngine : public ::Engine { Common::KeyState _keyPressed; @@ -99,6 +108,7 @@ public: uint32 getFeatures() const; Common::Language getLanguage() const; const Common::String& getTargetName() const { return _targetName; } + void syncSoundSettings(); void setupSysStrings(); void requestSavegame(int slotNum, Common::String &description); @@ -119,18 +129,23 @@ public: void scrollCameraLeft(int16 delta); void scrollCameraRight(int16 delta); void updateCamera(); - + + void showMenu(MenuID menuId); + void talk(int16 slotIndex, int16 slotOffset); void walk(byte *walkData); - - int16 findRectAtPoint(byte *rectData, int16 x, int16 y, int16 index, int16 itemSize, + + int16 findRectAtPoint(byte *rectData, int16 x, int16 y, int16 index, int16 itemSize, byte *rectDataEnd); + int _cfgVoicesVolume, _cfgMusicVolume, _cfgSoundFXVolume; + bool _cfgText, _cfgVoices; public: AnimationPlayer *_anim; ArchiveReader *_arc; + Console *_console; Input *_input; MenuSystem *_menuSystem; MoviePlayer *_moviePlayer; @@ -150,7 +165,7 @@ public: uint _sceneResIndex; int16 _sceneWidth, _sceneHeight; - + int _counter01, _counter02; bool _movieSceneFlag; byte _flag01; @@ -161,7 +176,7 @@ public: int16 _guiHeight; bool _doSpeech, _doText; - + int16 _walkSpeedY, _walkSpeedX; Common::KeyState _keyState; diff --git a/engines/tony/custom.cpp b/engines/tony/custom.cpp new file mode 100644 index 0000000000..f5c580c8c5 --- /dev/null +++ b/engines/tony/custom.cpp @@ -0,0 +1,2505 @@ +/* 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. + * + * + */ + +/* + * This code is based on original Tony Tough source code + * + * Copyright (c) 1997-2003 Nayma Software + */ + +#include "common/system.h" +#include "common/savefile.h" +#include "tony/mpal/mpal.h" +#include "tony/mpal/memory.h" +#include "tony/custom.h" +#include "tony/font.h" +#include "tony/game.h" +#include "tony/gfxcore.h" +#include "tony/tony.h" +#include "tony/tonychar.h" +#include "tony/utils.h" + +namespace Tony { + +static const char *const kAmbianceFile[] = { + "None", + "1.ADP", // Grilli.WAV + "2.ADP", // Grilli-Ovattati.WAV + "3.ADP", // Grilli-Vento.WAV + "3.ADP", // Grilli-Vento1.WAV + "5.ADP", // Vento1.WAV + "4.ADP", // Mare1.WAV + "6.ADP" // Mare1.WAV half volume +}; + +static const MusicFileEntry kMusicFiles[] = { + {"00.ADP", 0}, {"01.ADP", 0}, {"02.ADP", 0}, {"03.ADP", 0}, + {"04.ADP", 0}, {"05.ADP", 0}, {"06.ADP", 0}, {"07.ADP", 0}, + {"08.ADP", 2450}, {"09.ADP", 0}, {"10.ADP", 0}, {"11.ADP", 0}, + {"12.ADP", 0}, {"13.ADP", 0}, {"14.ADP", 0}, {"15.ADP", 0}, + {"16.ADP", 0}, {"17.ADP", 0}, {"18.ADP", 0}, {"19.ADP", 0}, + {"20.ADP", 0}, {"21.ADP", 0}, {"22.ADP", 0}, {"23.ADP", 0}, + {"24.ADP", 0}, {"25.ADP", 0}, {"26.ADP", 0}, {"27.ADP", 0}, + {"28.ADP", 1670}, {"29.ADP", 0}, {"30.ADP", 0}, {"31.ADP", 0}, + {"32.ADP", 2900}, {"33.ADP", 0}, {"34.ADP", 0}, {"35.ADP", 0}, + {"36.ADP", 0}, {"37.ADP", 0}, {"38.ADP", 0}, {"39.ADP", 0}, + {"40.ADP", 0}, {"41.ADP", 1920}, {"42.ADP", 1560}, {"43.ADP", 1920}, + {"44.ADP", 1920}, {"45.ADP", 1920}, {"46.ADP", 1920}, {"47.ADP", 1920}, + {"48.ADP", 1920}, {"49.ADP", 1920}, {"50.ADP", 1920}, {"51.ADP", 1920}, + {"52.ADP", 1920}, {"53.ADP", 0}, {"54.ADP", 0}, {"55.ADP", 0}, + {"56.ADP", 0}, {"57.ADP", 0}, {"58.ADP", 0}, {"59.ADP", 0} +}; + + +static const char *const kJingleFileNames[] = { + "S00.ADP", "S01.ADP", "S02.ADP", "S03.ADP", "S04.ADP", + "S05.ADP", "S06.ADP", "S07.ADP", "S08.ADP", "S09.ADP", + "S10.ADP", "S11.ADP", "S12.ADP", "S13.ADP", "S14.ADP", + "S15.ADP", "S16.ADP", "S17.ADP", "S18.ADP" +}; + +void reapplyChangedHotspot() { + for (int i = 0; i < GLOBALS._curChangedHotspot; i++) + GLOBALS._loc->getItemFromCode(GLOBALS._changedHotspot[i]._dwCode)->changeHotspot(RMPoint(GLOBALS._changedHotspot[i]._nX, GLOBALS._changedHotspot[i]._nY)); +} + +void saveChangedHotspot(Common::OutSaveFile *f) { + f->writeByte(GLOBALS._curChangedHotspot); + if (GLOBALS._curChangedHotspot > 0) { + for (int i = 0; i < GLOBALS._curChangedHotspot; ++i) + GLOBALS._changedHotspot[i].save(f); + } +} + +void loadChangedHotspot(Common::InSaveFile *f) { + GLOBALS._curChangedHotspot = f->readByte(); + + if (GLOBALS._curChangedHotspot > 0) { + for (int i = 0; i < GLOBALS._curChangedHotspot; ++i) + GLOBALS._changedHotspot[i].load(f); + } +} + +/** + * Classes required for custom functions + * + * Tony (To Move him) -> You can do MPAL through the animation? I really think so + * + * SendMessage -> I'd say just theEngine.SendMessage() + * ChangeLocation -> theEngine.ChangeLocation() + * AddInventory -> theEngine.AddInventory() +*/ + +void mCharResetCodes() { + for (int i = 0; i < 10; i++) + GLOBALS._mCharacter[i]._item = GLOBALS._loc->getItemFromCode(GLOBALS._mCharacter[i]._code); + for (int i = 0; i < 10; i++) + GLOBALS._character[i]._item = GLOBALS._loc->getItemFromCode(GLOBALS._character[i]._code); +} + +void charsSaveAll(Common::OutSaveFile *f) { + for (int i = 0; i < 10; i++) { + f->writeByte(GLOBALS._isMChar[i]); + if (GLOBALS._isMChar[i]) { + GLOBALS._mCharacter[i].save(f); + } else { + GLOBALS._character[i].save(f); + } + } +} + +void charsLoadAll(Common::InSaveFile *f) { + for (int i = 0; i < 10; i++) { + GLOBALS._isMChar[i] = f->readByte(); + if (GLOBALS._isMChar[i]) + GLOBALS._mCharacter[i].load(f); + else + GLOBALS._character[i].load(f); + } +} + +void faceToMe(CORO_PARAM, uint32, uint32, uint32, uint32) { + GLOBALS._tony->setPattern(GLOBALS._tony->PAT_STANDDOWN); +} + +void backToMe(CORO_PARAM, uint32, uint32, uint32, uint32) { + GLOBALS._tony->setPattern(GLOBALS._tony->PAT_STANDUP); +} + +void leftToMe(CORO_PARAM, uint32, uint32, uint32, uint32) { + GLOBALS._tony->setPattern(GLOBALS._tony->PAT_STANDLEFT); +} + +void rightToMe(CORO_PARAM, uint32, uint32, uint32, uint32) { + GLOBALS._tony->setPattern(GLOBALS._tony->PAT_STANDRIGHT); +} + +void tonySetPerorate(CORO_PARAM, uint32 bStatus, uint32, uint32, uint32) { + g_vm->getEngine()->setPerorate(bStatus); +} + +void mySleep(CORO_PARAM, uint32 dwTime, uint32, uint32, uint32) { + CORO_BEGIN_CONTEXT; + int i; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + if (!GLOBALS._bSkipIdle) + CORO_INVOKE_1(CoroScheduler.sleep, dwTime); + + CORO_END_CODE; +} + +void setAlwaysDisplay(CORO_PARAM, uint32 val, uint32, uint32, uint32) { + GLOBALS._bAlwaysDisplay = (val != 0); +} + +void setPointer(CORO_PARAM, uint32 dwPointer, uint32, uint32, uint32) { + switch (dwPointer) { + case 1: + GLOBALS._pointer->setSpecialPointer(GLOBALS._pointer->PTR_ARROWUP); + break; + case 2: + GLOBALS._pointer->setSpecialPointer(GLOBALS._pointer->PTR_ARROWDOWN); + break; + case 3: + GLOBALS._pointer->setSpecialPointer(GLOBALS._pointer->PTR_ARROWLEFT); + break; + case 4: + GLOBALS._pointer->setSpecialPointer(GLOBALS._pointer->PTR_ARROWRIGHT); + break; + case 5: + GLOBALS._pointer->setSpecialPointer(GLOBALS._pointer->PTR_ARROWMAP); + break; + + default: + GLOBALS._pointer->setSpecialPointer(GLOBALS._pointer->PTR_NONE); + break; + } +} + +VoiceHeader *searchVoiceHeader(uint32 codehi, uint32 codelo) { + int code = (codehi << 16) | codelo; + + if (g_vm->_voices.size() == 0) + return NULL; + + for (uint i = 0; i < g_vm->_voices.size(); i++) { + if (g_vm->_voices[i]._code == code) + return &g_vm->_voices[i]; + } + + return NULL; +} + +void sendTonyMessage(CORO_PARAM, uint32 dwMessage, uint32 nX, uint32 nY, uint32) { + CORO_BEGIN_CONTEXT; + RMMessage msg; + int i; + int curOffset; + VoiceHeader *curVoc; + FPSfx *voice; + RMTextDialog text; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + _ctx->curOffset = 0; + + if (GLOBALS._bSkipIdle) + return; + + _ctx->msg.load(dwMessage); + if (!_ctx->msg.isValid()) + return; + + _ctx->curVoc = searchVoiceHeader(0, dwMessage); + _ctx->voice = NULL; + if (_ctx->curVoc) { + // Is positioned within the database of entries beginning at the first + _ctx->curOffset = _ctx->curVoc->_offset; + + // First time allocation + g_vm->_vdbFP.seek(_ctx->curOffset); + g_vm->_theSound.createSfx(&_ctx->voice); + + _ctx->voice->loadVoiceFromVDB(g_vm->_vdbFP); + _ctx->curOffset = g_vm->_vdbFP.pos(); + + _ctx->voice->setLoop(false); + } + + if (GLOBALS._nTonyNextTalkType != GLOBALS._tony->TALK_NORMAL) { + CORO_INVOKE_1(GLOBALS._tony->startTalk, GLOBALS._nTonyNextTalkType); + + if (!GLOBALS._bStaticTalk) + GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_NORMAL; + } else { + if (_ctx->msg.numPeriods() > 1) + CORO_INVOKE_1(GLOBALS._tony->startTalk, GLOBALS._tony->TALK_HIPS); + else + CORO_INVOKE_1(GLOBALS._tony->startTalk, GLOBALS._tony->TALK_NORMAL); + } + + if (GLOBALS._curBackText) + CORO_INVOKE_0(GLOBALS._curBackText->hide); + + GLOBALS._bTonyIsSpeaking = true; + + for (_ctx->i = 0; _ctx->i < _ctx->msg.numPeriods() && !GLOBALS._bSkipIdle; _ctx->i++) { + _ctx->text.setInput(GLOBALS._input); + + // Alignment + _ctx->text.setAlignType(RMText::HCENTER, RMText::VBOTTOM); + + // Color + _ctx->text.setColor(0, 255, 0); + + // Writes the text + _ctx->text.writeText(_ctx->msg[_ctx->i], 0); + + // Set the position + if (nX == 0 && nY == 0) + _ctx->text.setPosition(GLOBALS._tony->position() - RMPoint(0, 130) - GLOBALS._loc->scrollPosition()); + else + _ctx->text.setPosition(RMPoint(nX, nY) - GLOBALS._loc->scrollPosition()); + + // Handling for always display + if (GLOBALS._bAlwaysDisplay) { + _ctx->text.setAlwaysDisplay(); + _ctx->text.forceTime(); + } + + // Record the text + g_vm->getEngine()->linkGraphicTask(&_ctx->text); + + if (_ctx->curVoc) { + if (_ctx->i == 0) { + _ctx->voice->play(); + _ctx->text.setCustomSkipHandle2(_ctx->voice->_hEndOfBuffer); + } else { + g_vm->_vdbFP.seek(_ctx->curOffset); + g_vm->_theSound.createSfx(&_ctx->voice); + _ctx->voice->loadVoiceFromVDB(g_vm->_vdbFP); + + _ctx->curOffset = g_vm->_vdbFP.pos(); + _ctx->voice->setLoop(false); + _ctx->voice->play(); + _ctx->text.setCustomSkipHandle2(_ctx->voice->_hEndOfBuffer); + } + } + + // Wait for the end of the display + _ctx->text.setCustomSkipHandle(GLOBALS._hSkipIdle); + CORO_INVOKE_0(_ctx->text.waitForEndDisplay); + + if (_ctx->curVoc) { + _ctx->voice->stop(); + _ctx->voice->release(); + _ctx->voice = NULL; + } + } + + GLOBALS._bTonyIsSpeaking = false; + if (GLOBALS._curBackText) + GLOBALS._curBackText->show(); + + CORO_INVOKE_0(GLOBALS._tony->endTalk); + + CORO_END_CODE; +} + +void changeBoxStatus(CORO_PARAM, uint32 nLoc, uint32 nBox, uint32 nStatus, uint32) { + GLOBALS._boxes->changeBoxStatus(nLoc, nBox, nStatus); +} + +void custLoadLocation(CORO_PARAM, uint32 nLoc, uint32 tX, uint32 tY, uint32 bUseStartPos) { + CORO_BEGIN_CONTEXT; + uint32 h; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + GLOBALS._curChangedHotspot = 0; + if (bUseStartPos != 0) + g_vm->getEngine()->loadLocation(nLoc, RMPoint(tX, tY), GLOBALS._startLocPos[nLoc]); + else + g_vm->getEngine()->loadLocation(nLoc, RMPoint(tX, tY), RMPoint(-1, -1)); + + _ctx->h = mpalQueryDoAction(0, nLoc, 0); + + // On Enter? + if (_ctx->h != CORO_INVALID_PID_VALUE) + CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _ctx->h, CORO_INFINITE); + + CORO_END_CODE; +} + +void sendFullscreenMsgStart(CORO_PARAM, uint32 nMsg, uint32 nFont, uint32, uint32) { + CORO_BEGIN_CONTEXT; + RMMessage *msg; + RMGfxClearTask clear; + int i; + RMTextDialog text; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + _ctx->msg = new RMMessage(nMsg); + + GLOBALS._fullScreenMessageLoc = GLOBALS._loc->TEMPGetNumLoc(); + GLOBALS._fullScreenMessagePt = GLOBALS._tony->position(); + + if (GLOBALS._bSkipIdle) + return; + + CORO_INVOKE_2(g_vm->getEngine()->unloadLocation, false, NULL); + GLOBALS._tony->hide(); + + for (_ctx->i = 0; _ctx->i < _ctx->msg->numPeriods() && !GLOBALS._bSkipIdle; _ctx->i++) { + _ctx->text.setInput(GLOBALS._input); + + // Alignment + _ctx->text.setAlignType(RMText::HCENTER, RMText::VCENTER); + + // Forces the text to disappear in time + _ctx->text.forceTime(); + + // Color + _ctx->text.setColor(255, 255, 255); + + // Write the text + if (nFont == 0) + _ctx->text.writeText((*_ctx->msg)[_ctx->i], 1); + else if (nFont == 1) + _ctx->text.writeText((*_ctx->msg)[_ctx->i], 0); + + // Set the position + _ctx->text.setPosition(RMPoint(320, 240)); + + _ctx->text.setAlwaysDisplay(); + _ctx->text.forceTime(); + + // Record the text + g_vm->getEngine()->linkGraphicTask(&_ctx->clear); + g_vm->getEngine()->linkGraphicTask(&_ctx->text); + + // Wait for the end of display + _ctx->text.setCustomSkipHandle(GLOBALS._hSkipIdle); + CORO_INVOKE_0(_ctx->text.waitForEndDisplay); + } + + delete _ctx->msg; + + CORO_END_CODE; +} + +void clearScreen(CORO_PARAM, uint32, uint32, uint32, uint32) { + CORO_BEGIN_CONTEXT; + char buf[256]; + RMGfxClearTask clear; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + g_vm->getEngine()->linkGraphicTask(&_ctx->clear); + + CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE); + + // WORKAROUND: This fixes a bug in the original source where the linked clear task + // didn't have time to be drawn and removed from the draw list before the method + // ended, thus remaining in the draw list and causing a later crash + CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE); + + CORO_END_CODE; +} + +void sendFullscreenMsgEnd(CORO_PARAM, uint32 bNotEnableTony, uint32, uint32, uint32) { + g_vm->getEngine()->loadLocation(GLOBALS._fullScreenMessageLoc, RMPoint(GLOBALS._fullScreenMessagePt._x, GLOBALS._fullScreenMessagePt._y), RMPoint(-1, -1)); + if (!bNotEnableTony) + GLOBALS._tony->show(); + + mCharResetCodes(); + reapplyChangedHotspot(); +} + + +void sendFullscreenMessage(CORO_PARAM, uint32 nMsg, uint32 nFont, uint32, uint32) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + CORO_INVOKE_4(sendFullscreenMsgStart, nMsg, nFont, 0, 0); + CORO_INVOKE_4(sendFullscreenMsgEnd, 0, 0, 0, 0); + + CORO_END_CODE; +} + +void noBullsEye(CORO_PARAM, uint32, uint32, uint32, uint32) { + GLOBALS._bNoBullsEye = true; +} + +void closeLocation(CORO_PARAM, uint32, uint32, uint32, uint32) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + if (!GLOBALS._bNoBullsEye) { + g_vm->getEngine()->initWipe(1); + CORO_INVOKE_0(g_vm->getEngine()->waitWipeEnd); + } + + g_vm->stopMusic(4); + + // On exit, unload + CORO_INVOKE_2(g_vm->getEngine()->unloadLocation, true, NULL); + + CORO_END_CODE; +} + +void changeLocation(CORO_PARAM, uint32 nLoc, uint32 tX, uint32 tY, uint32 bUseStartPos) { + CORO_BEGIN_CONTEXT; + uint32 h; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + if (!GLOBALS._bNoBullsEye) { + g_vm->getEngine()->initWipe(1); + CORO_INVOKE_0(g_vm->getEngine()->waitWipeEnd); + } + + if (GLOBALS._lastTappeto != GLOBALS._ambiance[nLoc]) { + g_vm->stopMusic(4); + } + + // On exit, unfreeze + CORO_INVOKE_2(g_vm->getEngine()->unloadLocation, true, NULL); + + GLOBALS._curChangedHotspot = 0; + if (bUseStartPos != 0) + g_vm->getEngine()->loadLocation(nLoc, RMPoint(tX, tY), GLOBALS._startLocPos[nLoc]); + else + g_vm->getEngine()->loadLocation(nLoc, RMPoint(tX, tY), RMPoint(-1, -1)); + + if (GLOBALS._lastTappeto != GLOBALS._ambiance[nLoc]) { + GLOBALS._lastTappeto = GLOBALS._ambiance[nLoc]; + if (GLOBALS._lastTappeto != 0) + g_vm->playMusic(4, kAmbianceFile[GLOBALS._lastTappeto], 0, true, 2000); + } + + if (!GLOBALS._bNoBullsEye) { + g_vm->getEngine()->initWipe(2); + } + + _ctx->h = mpalQueryDoAction(0, nLoc, 0); + + if (!GLOBALS._bNoBullsEye) { + CORO_INVOKE_0(g_vm->getEngine()->waitWipeEnd); + g_vm->getEngine()->closeWipe(); + } + + GLOBALS._bNoBullsEye = false; + + // On Enter? + if (_ctx->h != CORO_INVALID_PID_VALUE) + CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _ctx->h, CORO_INFINITE); + + CORO_END_CODE; +} + +void setLocStartPosition(CORO_PARAM, uint32 nLoc, uint32 lX, uint32 lY, uint32) { + GLOBALS._startLocPos[nLoc].set(lX, lY); +} + +void saveTonyPosition(CORO_PARAM, uint32, uint32, uint32, uint32) { + GLOBALS._saveTonyPos = GLOBALS._tony->position(); + GLOBALS._saveTonyLoc = GLOBALS._loc->TEMPGetNumLoc(); +} + +void restoreTonyPosition(CORO_PARAM, uint32, uint32, uint32, uint32) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + CORO_INVOKE_4(changeLocation, GLOBALS._saveTonyLoc, GLOBALS._saveTonyPos._x, GLOBALS._saveTonyPos._y, 0); + + mCharResetCodes(); + + CORO_END_CODE; +} + +void disableInput(CORO_PARAM, uint32, uint32, uint32, uint32) { + g_vm->getEngine()->disableInput(); +} + +void enableInput(CORO_PARAM, uint32, uint32, uint32, uint32) { + g_vm->getEngine()->enableInput(); +} + +void stopTony(CORO_PARAM, uint32, uint32, uint32, uint32) { + GLOBALS._tony->stopNoAction(coroParam); +} + +void custEnableGUI(CORO_PARAM, uint32, uint32, uint32, uint32) { + GLOBALS.EnableGUI(); +} + +void custDisableGUI(CORO_PARAM, uint32, uint32, uint32, uint32) { + GLOBALS.DisableGUI(); +} + +void tonyGenericTake1(CORO_PARAM, uint32 nDirection) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + GLOBALS._tony->take(nDirection, 0); + + if (!GLOBALS._bSkipIdle) + CORO_INVOKE_0(GLOBALS._tony->waitForEndPattern); + + CORO_END_CODE; +} + +void tonyGenericTake2(CORO_PARAM, uint32 nDirection) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + GLOBALS._tony->take(nDirection, 1); + + if (!GLOBALS._bSkipIdle) + CORO_INVOKE_0(GLOBALS._tony->waitForEndPattern); + + GLOBALS._tony->take(nDirection, 2); + + CORO_END_CODE; +} + +void tonyGenericPut1(CORO_PARAM, uint32 nDirection) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + GLOBALS._tony->put(nDirection, 0); + + if (!GLOBALS._bSkipIdle) + CORO_INVOKE_0(GLOBALS._tony->waitForEndPattern); + + CORO_END_CODE; +} + +void tonyGenericPut2(CORO_PARAM, uint32 nDirection) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + GLOBALS._tony->put(nDirection, 1); + + if (!GLOBALS._bSkipIdle) + CORO_INVOKE_0(GLOBALS._tony->waitForEndPattern); + + GLOBALS._tony->put(nDirection, 2); + + CORO_END_CODE; +} + +void tonyTakeUp1(CORO_PARAM, uint32, uint32, uint32, uint32) { + tonyGenericTake1(coroParam, 0); +} + +void tonyTakeMid1(CORO_PARAM, uint32, uint32, uint32, uint32) { + tonyGenericTake1(coroParam, 1); +} + +void tonyTakeDown1(CORO_PARAM, uint32, uint32, uint32, uint32) { + tonyGenericTake1(coroParam, 2); +} + +void tonyTakeUp2(CORO_PARAM, uint32, uint32, uint32, uint32) { + tonyGenericTake2(coroParam, 0); +} + +void tonyTakeMid2(CORO_PARAM, uint32, uint32, uint32, uint32) { + tonyGenericTake2(coroParam, 1); +} + +void tonyTakeDown2(CORO_PARAM, uint32, uint32, uint32, uint32) { + tonyGenericTake2(coroParam, 2); +} + +void tonyPutUp1(CORO_PARAM, uint32, uint32, uint32, uint32) { + tonyGenericPut1(coroParam, 0); +} + +void tonyPutMid1(CORO_PARAM, uint32, uint32, uint32, uint32) { + tonyGenericPut1(coroParam, 1); +} + +void tonyPutDown1(CORO_PARAM, uint32, uint32, uint32, uint32) { + tonyGenericPut1(coroParam, 2); +} + +void tonyPutUp2(CORO_PARAM, uint32, uint32, uint32, uint32) { + tonyGenericPut2(coroParam, 0); +} + +void tonyPutMid2(CORO_PARAM, uint32, uint32, uint32, uint32) { + tonyGenericPut2(coroParam, 1); +} + +void tonyPutDown2(CORO_PARAM, uint32, uint32, uint32, uint32) { + tonyGenericPut2(coroParam, 2); +} + +void tonyOnTheFloor(CORO_PARAM, uint32 dwParte, uint32, uint32, uint32) { + if (dwParte == 0) + GLOBALS._tony->setPattern(GLOBALS._tony->PAT_ONTHEFLOORLEFT); + else + GLOBALS._tony->setPattern(GLOBALS._tony->PAT_ONTHEFLOORRIGHT); +} + +void tonyGetUp(CORO_PARAM, uint32 dwParte, uint32, uint32, uint32) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + if (dwParte == 0) + GLOBALS._tony->setPattern(GLOBALS._tony->PAT_GETUPLEFT); + else + GLOBALS._tony->setPattern(GLOBALS._tony->PAT_GETUPRIGHT); + + if (!GLOBALS._bSkipIdle) + CORO_INVOKE_0(GLOBALS._tony->waitForEndPattern); + + CORO_END_CODE; +} + +void tonyShepherdess(CORO_PARAM, uint32 bIsPast, uint32, uint32, uint32) { + GLOBALS._tony->setShepherdess(bIsPast); +} + +void tonyWhistle(CORO_PARAM, uint32, uint32, uint32, uint32) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + GLOBALS._tony->setPattern(GLOBALS._tony->PAT_WHISTLERIGHT); + if (!GLOBALS._bSkipIdle) + CORO_INVOKE_0(GLOBALS._tony->waitForEndPattern); + + GLOBALS._tony->setPattern(GLOBALS._tony->PAT_STANDRIGHT); + + CORO_END_CODE; +} + +void tonySetNumTexts(uint32 dwText) { + GLOBALS._dwTonyNumTexts = dwText; + GLOBALS._bTonyInTexts = false; +} + +void tonyLaugh(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { + tonySetNumTexts(dwText); + GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_LAUGH; +} + +void tonyGiggle(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { + tonySetNumTexts(dwText); + GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_LAUGH2; +} + +void tonyHips(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { + tonySetNumTexts(dwText); + GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_HIPS; +} + +void tonySing(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { + tonySetNumTexts(dwText); + GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_SING; +} + +void tonyIndicate(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { + tonySetNumTexts(dwText); + GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_INDICATE; +} + +void tonyScaredWithHands(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { + tonySetNumTexts(dwText); + GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_SCARED; +} + +void tonyScaredWithoutHands(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { + tonySetNumTexts(dwText); + GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_SCARED2; +} + +void tonyWithHammer(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { + tonySetNumTexts(dwText); + GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHHAMMER; + GLOBALS._tony->setPattern(GLOBALS._tony->PAT_WITHHAMMER); +} + +void tonyWithGlasses(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { + tonySetNumTexts(dwText); + GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHGLASSES; + GLOBALS._tony->setPattern(GLOBALS._tony->PAT_WITHGLASSES); +} + +void tonyWithWorm(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { + tonySetNumTexts(dwText); + GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHWORM; + GLOBALS._tony->setPattern(GLOBALS._tony->PAT_WITHWORM); +} + +void tonyWithRope(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { + tonySetNumTexts(dwText); + GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHROPE; + GLOBALS._tony->setPattern(GLOBALS._tony->PAT_WITHROPE); +} + +void tonyWithSecretary(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { + tonySetNumTexts(dwText); + GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHSECRETARY; + GLOBALS._tony->setPattern(GLOBALS._tony->PAT_WITHSECRETARY); +} + +void tonyWithRabbitANIM(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { + tonySetNumTexts(dwText); + GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHRABBIT; +} + +void tonyWithRecipeANIM(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { + tonySetNumTexts(dwText); + GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHRECIPE; +} + +void tonyWithCardsANIM(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { + tonySetNumTexts(dwText); + GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHCARDS; +} + +void tonyWithSnowmanANIM(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { + tonySetNumTexts(dwText); + GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHSNOWMAN; +} + +void tonyWithSnowmanStart(CORO_PARAM, uint32, uint32, uint32, uint32) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHSNOWMANSTATIC; + GLOBALS._bStaticTalk = true; + CORO_INVOKE_1(GLOBALS._tony->startStatic, GLOBALS._tony->TALK_WITHSNOWMANSTATIC); + + CORO_END_CODE; +} + +void tonyWithSnowmanEnd(CORO_PARAM, uint32, uint32, uint32, uint32) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + CORO_INVOKE_1(GLOBALS._tony->endStatic, GLOBALS._tony->TALK_WITHSNOWMANSTATIC); + GLOBALS._bStaticTalk = false; + GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_NORMAL; + + CORO_END_CODE; +} + +void tonyWithRabbitStart(CORO_PARAM, uint32, uint32, uint32, uint32) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHRABBITSTATIC; + GLOBALS._bStaticTalk = true; + CORO_INVOKE_1(GLOBALS._tony->startStatic, GLOBALS._tony->TALK_WITHRABBITSTATIC); + + CORO_END_CODE; +} + +void tonyWithRabbitEnd(CORO_PARAM, uint32, uint32, uint32, uint32) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + CORO_INVOKE_1(GLOBALS._tony->endStatic, GLOBALS._tony->TALK_WITHRABBITSTATIC); + GLOBALS._bStaticTalk = false; + GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_NORMAL; + + CORO_END_CODE; +} + +void tonyWithRecipeStart(CORO_PARAM, uint32, uint32, uint32, uint32) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHRECIPESTATIC; + GLOBALS._bStaticTalk = true; + CORO_INVOKE_1(GLOBALS._tony->startStatic, GLOBALS._tony->TALK_WITHRECIPESTATIC); + + CORO_END_CODE; +} + +void tonyWithRecipeEnd(CORO_PARAM, uint32, uint32, uint32, uint32) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + CORO_INVOKE_1(GLOBALS._tony->endStatic, GLOBALS._tony->TALK_WITHRECIPESTATIC); + GLOBALS._bStaticTalk = false; + GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_NORMAL; + + CORO_END_CODE; +} + +void tonyWithCardsStart(CORO_PARAM, uint32, uint32, uint32, uint32) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHCARDSSTATIC; + GLOBALS._bStaticTalk = true; + CORO_INVOKE_1(GLOBALS._tony->startStatic, GLOBALS._tony->TALK_WITHCARDSSTATIC); + + CORO_END_CODE; +} + +void tonyWithCardsEnd(CORO_PARAM, uint32, uint32, uint32, uint32) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + CORO_INVOKE_1(GLOBALS._tony->endStatic, GLOBALS._tony->TALK_WITHCARDSSTATIC); + GLOBALS._bStaticTalk = false; + GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_NORMAL; + + CORO_END_CODE; +} + +void tonyWithNotebookStart(CORO_PARAM, uint32, uint32, uint32, uint32) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITH_NOTEBOOK; + GLOBALS._bStaticTalk = true; + CORO_INVOKE_1(GLOBALS._tony->startStatic, GLOBALS._tony->TALK_WITH_NOTEBOOK); + + CORO_END_CODE; +} + +void tonyWithNotebookEnd(CORO_PARAM, uint32, uint32, uint32, uint32) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + CORO_INVOKE_1(GLOBALS._tony->endStatic, GLOBALS._tony->TALK_WITH_NOTEBOOK); + GLOBALS._bStaticTalk = false; + GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_NORMAL; + + CORO_END_CODE; +} + +void tonyWithMegaphoneStart(CORO_PARAM, uint32, uint32, uint32, uint32) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHMEGAPHONESTATIC; + GLOBALS._bStaticTalk = true; + CORO_INVOKE_1(GLOBALS._tony->startStatic, GLOBALS._tony->TALK_WITHMEGAPHONESTATIC); + + CORO_END_CODE; +} + +void tonyWithMegaphoneEnd(CORO_PARAM, uint32, uint32, uint32, uint32) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + CORO_INVOKE_1(GLOBALS._tony->endStatic, GLOBALS._tony->TALK_WITHMEGAPHONESTATIC); + GLOBALS._bStaticTalk = false; + GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_NORMAL; + + CORO_END_CODE; +} + +void tonyWithBeardStart(CORO_PARAM, uint32, uint32, uint32, uint32) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHBEARDSTATIC; + GLOBALS._bStaticTalk = true; + CORO_INVOKE_1(GLOBALS._tony->startStatic, GLOBALS._tony->TALK_WITHBEARDSTATIC); + + CORO_END_CODE; +} + +void tonyWithBeardEnd(CORO_PARAM, uint32, uint32, uint32, uint32) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + CORO_INVOKE_1(GLOBALS._tony->endStatic, GLOBALS._tony->TALK_WITHBEARDSTATIC); + GLOBALS._bStaticTalk = false; + GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_NORMAL; + + CORO_END_CODE; +} + +void tonyScaredStart(CORO_PARAM, uint32, uint32, uint32, uint32) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_SCAREDSTATIC; + GLOBALS._bStaticTalk = true; + CORO_INVOKE_1(GLOBALS._tony->startStatic, GLOBALS._tony->TALK_SCAREDSTATIC); + + CORO_END_CODE; +} + +void tonyScaredEnd(CORO_PARAM, uint32, uint32, uint32, uint32) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + CORO_INVOKE_1(GLOBALS._tony->endStatic, GLOBALS._tony->TALK_SCAREDSTATIC); + GLOBALS._bStaticTalk = false; + GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_NORMAL; + + CORO_END_CODE; +} + +void tonyDisgusted(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { + tonySetNumTexts(dwText); + GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_DISGUSTED; +} + +void tonySniffLeft(CORO_PARAM, uint32, uint32, uint32, uint32) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + GLOBALS._tony->setPattern(GLOBALS._tony->PAT_SNIFF_LEFT); + CORO_INVOKE_0(GLOBALS._tony->waitForEndPattern); + CORO_INVOKE_4(leftToMe, 0, 0, 0, 0); + + CORO_END_CODE; +} + +void tonySniffRight(CORO_PARAM, uint32, uint32, uint32, uint32) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + GLOBALS._tony->setPattern(GLOBALS._tony->PAT_SNIFF_RIGHT); + CORO_INVOKE_0(GLOBALS._tony->waitForEndPattern); + CORO_INVOKE_4(rightToMe, 0, 0, 0, 0); + + CORO_END_CODE; +} + +void tonySarcastic(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { + tonySetNumTexts(dwText); + GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_SARCASTIC; +} + +void tonyMacbeth(CORO_PARAM, uint32 nPos, uint32, uint32, uint32) { + switch (nPos) { + case 1: + GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_MACBETH1; + break; + case 2: + GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_MACBETH2; + break; + case 3: + GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_MACBETH3; + break; + case 4: + GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_MACBETH4; + break; + case 5: + GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_MACBETH5; + break; + case 6: + GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_MACBETH6; + break; + case 7: + GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_MACBETH7; + break; + case 8: + GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_MACBETH8; + break; + case 9: + GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_MACBETH9; + break; + } +} + +void enableTony(CORO_PARAM, uint32, uint32, uint32, uint32) { + GLOBALS._tony->show(); +} + +void disableTony(CORO_PARAM, uint32 bShowShadow, uint32, uint32, uint32) { + GLOBALS._tony->hide(bShowShadow); +} + +void waitForPatternEnd(CORO_PARAM, uint32 nItem, uint32, uint32, uint32) { + CORO_BEGIN_CONTEXT; + RMItem *item; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + _ctx->item = GLOBALS._loc->getItemFromCode(nItem); + + if (!GLOBALS._bSkipIdle && _ctx->item != NULL) + CORO_INVOKE_1(_ctx->item->waitForEndPattern, GLOBALS._hSkipIdle); + + CORO_END_CODE; +} + +void setTonyPosition(CORO_PARAM, uint32 nX, uint32 nY, uint32 nLoc, uint32) { + GLOBALS._tony->setPosition(RMPoint(nX, nY), nLoc); +} + +void moveTonyAndWait(CORO_PARAM, uint32 nX, uint32 nY, uint32, uint32) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + // WORKAROUND: Delay for a frame before starting the move to give any previous move time to finish. + // This fixes a bug in the first scene where if you immediately 'Use Door', Tony moves to the door, + // and then floats to the right rather than properly walking. + CORO_SLEEP(1); + + CORO_INVOKE_1(GLOBALS._tony->move, RMPoint(nX, nY)); + + if (!GLOBALS._bSkipIdle) + CORO_INVOKE_0(GLOBALS._tony->waitForEndMovement); + + CORO_END_CODE; +} + +void moveTony(CORO_PARAM, uint32 nX, uint32 nY, uint32, uint32) { + GLOBALS._tony->move(coroParam, RMPoint(nX, nY)); +} + +void scrollLocation(CORO_PARAM, uint32 nX, uint32 nY, uint32 sX, uint32 sY) { + CORO_BEGIN_CONTEXT; + int lx, ly; + RMPoint pt; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + // Take the scroll coordinates + _ctx->lx = (int32)nX; + _ctx->ly = (int32)nY; + + _ctx->pt = GLOBALS._loc->scrollPosition(); + + while ((_ctx->lx != 0 || _ctx->ly != 0) && !GLOBALS._bSkipIdle) { + if (_ctx->lx > 0) { + _ctx->lx -= (int32)sX; + if (_ctx->lx < 0) + _ctx->lx = 0; + _ctx->pt.offset((int32)sX, 0); + } else if (_ctx->lx < 0) { + _ctx->lx += (int32)sX; + if (_ctx->lx > 0) + _ctx->lx = 0; + _ctx->pt.offset(-(int32)sX, 0); + } + + if (_ctx->ly > 0) { + _ctx->ly -= sY; + if (_ctx->ly < 0) + _ctx->ly = 0; + _ctx->pt.offset(0, sY); + } else if (_ctx->ly < 0) { + _ctx->ly += sY; + if (_ctx->ly > 0) + _ctx->ly = 0; + _ctx->pt.offset(0, -(int32)sY); + } + + CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE); + + GLOBALS._loc->setScrollPosition(_ctx->pt); + GLOBALS._tony->setScrollPosition(_ctx->pt); + } + + CORO_END_CODE; +} + +void syncScrollLocation(CORO_PARAM, uint32 nX, uint32 nY, uint32 sX, uint32 sY) { + CORO_BEGIN_CONTEXT; + int lx, ly; + RMPoint pt, startpt; + uint32 dwStartTime, dwCurTime, dwTotalTime; + uint32 stepX, stepY; + int dimx, dimy; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + // Take the scroll coordinates + _ctx->lx = (int32)nX; + _ctx->ly = (int32)nY; + _ctx->dimx = _ctx->lx; + _ctx->dimy = _ctx->ly; + if (_ctx->lx < 0) + _ctx->dimx = -_ctx->lx; + + if (_ctx->ly < 0) + _ctx->dimy = -_ctx->ly; + + _ctx->stepX = sX; + _ctx->stepY = sY; + + _ctx->startpt = GLOBALS._loc->scrollPosition(); + + _ctx->dwStartTime = g_vm->getTime(); + + if (sX) + _ctx->dwTotalTime = _ctx->dimx * (1000 / 35) / sX; + else + _ctx->dwTotalTime = _ctx->dimy * (1000 / 35) / sY; + + while ((_ctx->lx != 0 || _ctx->ly != 0) && !GLOBALS._bSkipIdle) { + _ctx->dwCurTime = g_vm->getTime() - _ctx->dwStartTime; + if (_ctx->dwCurTime > _ctx->dwTotalTime) + break; + + _ctx->pt = _ctx->startpt; + + if (sX) { + if (_ctx->lx > 0) + _ctx->pt._x += (_ctx->dimx * _ctx->dwCurTime) / _ctx->dwTotalTime; + else + _ctx->pt._x -= (_ctx->dimx * _ctx->dwCurTime) / _ctx->dwTotalTime; + } else { + if (_ctx->ly > 0) + _ctx->pt._y += (_ctx->dimy * _ctx->dwCurTime) / _ctx->dwTotalTime; + else + _ctx->pt._y -= (_ctx->dimy * _ctx->dwCurTime) / _ctx->dwTotalTime; + } + + CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE); + + GLOBALS._loc->setScrollPosition(_ctx->pt); + GLOBALS._tony->setScrollPosition(_ctx->pt); + } + + // Set the position finale + if (sX) { + if (_ctx->lx > 0) + _ctx->pt._x = _ctx->startpt._x + _ctx->dimx; + else + _ctx->pt._x = _ctx->startpt._x - _ctx->dimx; + } else { + if (_ctx->ly > 0) + _ctx->pt._y = _ctx->startpt._y + _ctx->dimy; + else + _ctx->pt._y = _ctx->startpt._y - _ctx->dimy; + } + + GLOBALS._loc->setScrollPosition(_ctx->pt); + GLOBALS._tony->setScrollPosition(_ctx->pt); + + CORO_END_CODE; +} + +void changeHotspot(CORO_PARAM, uint32 dwCode, uint32 nX, uint32 nY, uint32) { + int i; + + for (i = 0; i < GLOBALS._curChangedHotspot; i++) { + if (GLOBALS._changedHotspot[i]._dwCode == dwCode) { + GLOBALS._changedHotspot[i]._nX = nX; + GLOBALS._changedHotspot[i]._nY = nY; + break; + } + } + + if (i == GLOBALS._curChangedHotspot) { + GLOBALS._changedHotspot[i]._dwCode = dwCode; + GLOBALS._changedHotspot[i]._nX = nX; + GLOBALS._changedHotspot[i]._nY = nY; + GLOBALS._curChangedHotspot++; + } + + GLOBALS._loc->getItemFromCode(dwCode)->changeHotspot(RMPoint(nX, nY)); +} + +void autoSave(CORO_PARAM, uint32, uint32, uint32, uint32) { + g_vm->autoSave(coroParam); +} + +void abortGame(CORO_PARAM, uint32, uint32, uint32, uint32) { + debug(1, "script called abortGame"); + g_vm->quitGame(); +} + +void shakeScreen(CORO_PARAM, uint32 nScosse, uint32, uint32, uint32) { + CORO_BEGIN_CONTEXT; + uint32 i; + uint32 curTime; + int dirx, diry; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + _ctx->curTime = g_vm->getTime(); + + _ctx->dirx = 1; + _ctx->diry = 1; + + while (g_vm->getTime() < _ctx->curTime + nScosse) { + CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE); + + GLOBALS._loc->setFixedScroll(RMPoint(1 * _ctx->dirx, 1 * _ctx->diry)); + GLOBALS._tony->setFixedScroll(RMPoint(1 * _ctx->dirx, 1 * _ctx->diry)); + + _ctx->i = g_vm->_randomSource.getRandomNumber(2); + + if (_ctx->i == 0 || _ctx->i == 2) + _ctx->dirx = -_ctx->dirx; + else if (_ctx->i == 1 || _ctx->i == 2) + _ctx->diry = -_ctx->diry; + } + + GLOBALS._loc->setFixedScroll(RMPoint(0, 0)); + GLOBALS._tony->setFixedScroll(RMPoint(0, 0)); + + CORO_END_CODE; +} + +/* + * Characters + */ + +void charSetCode(CORO_PARAM, uint32 nChar, uint32 nCode, uint32, uint32) { + assert(nChar < 16); + GLOBALS._character[nChar]._code = nCode; + GLOBALS._character[nChar]._item = GLOBALS._loc->getItemFromCode(nCode); + GLOBALS._character[nChar]._r = 255; + GLOBALS._character[nChar]._g = 255; + GLOBALS._character[nChar]._b = 255; + GLOBALS._character[nChar]._talkPattern = 0; + GLOBALS._character[nChar]._startTalkPattern = 0; + GLOBALS._character[nChar]._endTalkPattern = 0; + GLOBALS._character[nChar]._standPattern = 0; + + GLOBALS._isMChar[nChar] = false; +} + +void charSetColor(CORO_PARAM, uint32 nChar, uint32 r, uint32 g, uint32 b) { + assert(nChar < 16); + GLOBALS._character[nChar]._r = r; + GLOBALS._character[nChar]._g = g; + GLOBALS._character[nChar]._b = b; +} + +void charSetTalkPattern(CORO_PARAM, uint32 nChar, uint32 tp, uint32 sp, uint32) { + assert(nChar < 16); + GLOBALS._character[nChar]._talkPattern = tp; + GLOBALS._character[nChar]._standPattern = sp; +} + +void charSetStartEndTalkPattern(CORO_PARAM, uint32 nChar, uint32 sp, uint32 ep, uint32) { + assert(nChar < 16); + GLOBALS._character[nChar]._startTalkPattern = sp; + GLOBALS._character[nChar]._endTalkPattern = ep; +} + +void charSendMessage(CORO_PARAM, uint32 nChar, uint32 dwMessage, uint32 bIsBack, uint32) { + CORO_BEGIN_CONTEXT; + RMMessage *msg; + int i; + RMPoint pt; + RMTextDialog *text; + int curOffset; + VoiceHeader *curVoc; + FPSfx *voice; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + _ctx->msg = new RMMessage(dwMessage); + _ctx->curOffset = 0; + + assert(nChar < 16); + _ctx->pt = GLOBALS._character[nChar]._item->calculatePos() - RMPoint(-60, 20) - GLOBALS._loc->scrollPosition(); + + if (GLOBALS._character[nChar]._startTalkPattern != 0) { + GLOBALS._character[nChar]._item->setPattern(GLOBALS._character[nChar]._startTalkPattern); + + CORO_INVOKE_0(GLOBALS._character[nChar]._item->waitForEndPattern); + } + + GLOBALS._character[nChar]._item->setPattern(GLOBALS._character[nChar]._talkPattern); + + _ctx->curVoc = searchVoiceHeader(0, dwMessage); + _ctx->voice = NULL; + if (_ctx->curVoc) { + // Position within the database of entries, beginning at the first + g_vm->_vdbFP.seek(_ctx->curVoc->_offset); + _ctx->curOffset = _ctx->curVoc->_offset; + } + + for (_ctx->i = 0; _ctx->i < _ctx->msg->numPeriods() && !GLOBALS._bSkipIdle; _ctx->i++) { + if (bIsBack) { + GLOBALS._curBackText = _ctx->text = new RMTextDialogScrolling(GLOBALS._loc); + if (GLOBALS._bTonyIsSpeaking) + CORO_INVOKE_0(GLOBALS._curBackText->hide); + } else + _ctx->text = new RMTextDialog; + + _ctx->text->setInput(GLOBALS._input); + + // Skipping + _ctx->text->setSkipStatus(!bIsBack); + + // Alignment + _ctx->text->setAlignType(RMText::HCENTER, RMText::VBOTTOM); + + // Color + _ctx->text->setColor(GLOBALS._character[nChar]._r, GLOBALS._character[nChar]._g, GLOBALS._character[nChar]._b); + + // Write the text + _ctx->text->writeText((*_ctx->msg)[_ctx->i], 0); + + // Set the position + _ctx->text->setPosition(_ctx->pt); + + // Set the always display + if (GLOBALS._bAlwaysDisplay) { + _ctx->text->setAlwaysDisplay(); + _ctx->text->forceTime(); + } + + // Record the text + g_vm->getEngine()->linkGraphicTask(_ctx->text); + + if (_ctx->curVoc) { + g_vm->_theSound.createSfx(&_ctx->voice); + g_vm->_vdbFP.seek(_ctx->curOffset); + _ctx->voice->loadVoiceFromVDB(g_vm->_vdbFP); + _ctx->voice->setLoop(false); + if (bIsBack) + _ctx->voice->setVolume(55); + _ctx->voice->play(); + _ctx->text->setCustomSkipHandle2(_ctx->voice->_hEndOfBuffer); + _ctx->curOffset = g_vm->_vdbFP.pos(); + } + + // Wait for the end of display + _ctx->text->setCustomSkipHandle(GLOBALS._hSkipIdle); + CORO_INVOKE_0(_ctx->text->waitForEndDisplay); + + if (_ctx->curVoc) { + _ctx->voice->stop(); + _ctx->voice->release(); + _ctx->voice = NULL; + } + + GLOBALS._curBackText = NULL; + delete _ctx->text; + } + + if (GLOBALS._character[nChar]._endTalkPattern != 0) { + GLOBALS._character[nChar]._item->setPattern(GLOBALS._character[nChar]._endTalkPattern); + CORO_INVOKE_0(GLOBALS._character[nChar]._item->waitForEndPattern); + } + + GLOBALS._character[nChar]._item->setPattern(GLOBALS._character[nChar]._standPattern); + delete _ctx->msg; + + CORO_END_CODE; +} + +void addInventory(CORO_PARAM, uint32 dwCode, uint32, uint32, uint32) { + GLOBALS._inventory->addItem(dwCode); +} + +void removeInventory(CORO_PARAM, uint32 dwCode, uint32, uint32, uint32) { + GLOBALS._inventory->removeItem(dwCode); +} + +void changeInventoryStatus(CORO_PARAM, uint32 dwCode, uint32 dwStatus, uint32, uint32) { + GLOBALS._inventory->changeItemStatus(dwCode, dwStatus); +} + +/* + * Master Characters + */ + +void mCharSetCode(CORO_PARAM, uint32 nChar, uint32 nCode, uint32, uint32) { + assert(nChar < 10); + GLOBALS._mCharacter[nChar]._code = nCode; + if (nCode == 0) + GLOBALS._mCharacter[nChar]._item = NULL; + else + GLOBALS._mCharacter[nChar]._item = GLOBALS._loc->getItemFromCode(nCode); + GLOBALS._mCharacter[nChar]._r = 255; + GLOBALS._mCharacter[nChar]._g = 255; + GLOBALS._mCharacter[nChar]._b = 255; + GLOBALS._mCharacter[nChar]._x = -1; + GLOBALS._mCharacter[nChar]._y = -1; + GLOBALS._mCharacter[nChar]._bAlwaysBack = 0; + + for (int i = 0; i < 10; i++) + GLOBALS._mCharacter[nChar]._numTalks[i] = 1; + + GLOBALS._mCharacter[nChar]._curGroup = 0; + + GLOBALS._isMChar[nChar] = true; +} + +void mCharResetCode(CORO_PARAM, uint32 nChar, uint32, uint32, uint32) { + GLOBALS._mCharacter[nChar]._item = GLOBALS._loc->getItemFromCode(GLOBALS._mCharacter[nChar]._code); +} + +void mCharSetPosition(CORO_PARAM, uint32 nChar, uint32 nX, uint32 nY, uint32) { + assert(nChar < 10); + GLOBALS._mCharacter[nChar]._x = nX; + GLOBALS._mCharacter[nChar]._y = nY; +} + +void mCharSetColor(CORO_PARAM, uint32 nChar, uint32 r, uint32 g, uint32 b) { + assert(nChar < 10); + GLOBALS._mCharacter[nChar]._r = r; + GLOBALS._mCharacter[nChar]._g = g; + GLOBALS._mCharacter[nChar]._b = b; +} + +void mCharSetNumTalksInGroup(CORO_PARAM, uint32 nChar, uint32 nGroup, uint32 nTalks, uint32) { + assert(nChar < 10); + assert(nGroup < 10); + + GLOBALS._mCharacter[nChar]._numTalks[nGroup] = nTalks; +} + +void mCharSetCurrentGroup(CORO_PARAM, uint32 nChar, uint32 nGroup, uint32, uint32) { + assert(nChar < 10); + assert(nGroup < 10); + + GLOBALS._mCharacter[nChar]._curGroup = nGroup; +} + +void mCharSetNumTexts(CORO_PARAM, uint32 nChar, uint32 nTexts, uint32, uint32) { + assert(nChar < 10); + + GLOBALS._mCharacter[nChar]._numTexts = nTexts - 1; + GLOBALS._mCharacter[nChar]._bInTexts = false; +} + +void mCharSetAlwaysBack(CORO_PARAM, uint32 nChar, uint32 bAlwaysBack, uint32, uint32) { + assert(nChar < 10); + + GLOBALS._mCharacter[nChar]._bAlwaysBack = bAlwaysBack; +} + +void mCharSendMessage(CORO_PARAM, uint32 nChar, uint32 dwMessage, uint32 bIsBack, uint32 nFont) { + CORO_BEGIN_CONTEXT; + RMMessage *msg; + int i; + int parm; + RMPoint pt; + uint32 h; + RMTextDialog *text; + int curOffset; + VoiceHeader *curVoc; + FPSfx *voice; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + _ctx->msg = new RMMessage(dwMessage); + _ctx->curOffset = 0; + + assert(nChar < 10); + + bIsBack |= GLOBALS._mCharacter[nChar]._bAlwaysBack ? 1 : 0; + + // Calculates the position of the text according to the current frame + if (GLOBALS._mCharacter[nChar]._x == -1) + _ctx->pt = GLOBALS._mCharacter[nChar]._item->calculatePos() - RMPoint(-60, 20) - GLOBALS._loc->scrollPosition(); + else + _ctx->pt = RMPoint(GLOBALS._mCharacter[nChar]._x, GLOBALS._mCharacter[nChar]._y); + + // Parameter for special actions: random between the spoken + _ctx->parm = (GLOBALS._mCharacter[nChar]._curGroup * 10) + g_vm->_randomSource.getRandomNumber( + GLOBALS._mCharacter[nChar]._numTalks[GLOBALS._mCharacter[nChar]._curGroup] - 1) + 1; + + // Try to run the custom function to initialize the speech + if (GLOBALS._mCharacter[nChar]._item) { + _ctx->h = mpalQueryDoAction(30, GLOBALS._mCharacter[nChar]._item->mpalCode(), _ctx->parm); + if (_ctx->h != CORO_INVALID_PID_VALUE) { + CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _ctx->h, CORO_INFINITE); + } + } + + _ctx->curVoc = searchVoiceHeader(0, dwMessage); + _ctx->voice = NULL; + if (_ctx->curVoc) { + // Position within the database of entries, beginning at the first + g_vm->_vdbFP.seek(_ctx->curVoc->_offset); + _ctx->curOffset = _ctx->curVoc->_offset; + } + + for (_ctx->i = 0; _ctx->i < _ctx->msg->numPeriods() && !GLOBALS._bSkipIdle; _ctx->i++) { + // Create a different object depending on whether it's background or not + if (bIsBack) { + GLOBALS._curBackText = _ctx->text = new RMTextDialogScrolling(GLOBALS._loc); + if (GLOBALS._bTonyIsSpeaking) + CORO_INVOKE_0(GLOBALS._curBackText->hide); + } else + _ctx->text = new RMTextDialog; + + _ctx->text->setInput(GLOBALS._input); + + // Skipping + _ctx->text->setSkipStatus(!bIsBack); + + // Alignment + _ctx->text->setAlignType(RMText::HCENTER, RMText::VBOTTOM); + + // Color + _ctx->text->setColor(GLOBALS._mCharacter[nChar]._r, GLOBALS._mCharacter[nChar]._g, GLOBALS._mCharacter[nChar]._b); + + // Write the text + _ctx->text->writeText((*_ctx->msg)[_ctx->i], nFont); + + // Set the position + _ctx->text->setPosition(_ctx->pt); + + // Set the always display + if (GLOBALS._bAlwaysDisplay) { + _ctx->text->setAlwaysDisplay(); + _ctx->text->forceTime(); + } + + // Record the text + g_vm->getEngine()->linkGraphicTask(_ctx->text); + + if (_ctx->curVoc) { + g_vm->_theSound.createSfx(&_ctx->voice); + g_vm->_vdbFP.seek(_ctx->curOffset); + _ctx->voice->loadVoiceFromVDB(g_vm->_vdbFP); + _ctx->voice->setLoop(false); + if (bIsBack) + _ctx->voice->setVolume(55); + _ctx->voice->play(); + _ctx->text->setCustomSkipHandle2(_ctx->voice->_hEndOfBuffer); + _ctx->curOffset = g_vm->_vdbFP.pos(); + } + + // Wait for the end of display + _ctx->text->setCustomSkipHandle(GLOBALS._hSkipIdle); + CORO_INVOKE_0(_ctx->text->waitForEndDisplay); + + if (_ctx->curVoc) { + _ctx->voice->stop(); + _ctx->voice->release(); + _ctx->voice = NULL; + } + + GLOBALS._curBackText = NULL; + delete _ctx->text; + } + + delete _ctx->msg; + + // Try to run the custom function to close the speech + if (GLOBALS._mCharacter[nChar]._item) { + _ctx->h = mpalQueryDoAction(31, GLOBALS._mCharacter[nChar]._item->mpalCode(), _ctx->parm); + if (_ctx->h != CORO_INVALID_PID_VALUE) + CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _ctx->h, CORO_INFINITE); + } + + CORO_END_CODE; +} + +/* + * Dialogs + */ + +void sendDialogMessage(CORO_PARAM, uint32 nPers, uint32 nMsg, uint32, uint32) { + CORO_BEGIN_CONTEXT; + char *string; + RMTextDialog *text; + int parm; + uint32 h; + bool bIsBack; + VoiceHeader *curVoc; + FPSfx *voice; + RMPoint pt; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + _ctx->bIsBack = false; + + // The SendDialogMessage can go in the background if it is a character + if (nPers != 0 && GLOBALS._isMChar[nPers] && GLOBALS._mCharacter[nPers]._bAlwaysBack) + _ctx->bIsBack = true; + + _ctx->curVoc = searchVoiceHeader(GLOBALS._curDialog, nMsg); + _ctx->voice = NULL; + + if (_ctx->curVoc) { + // Position within the database of entries, beginning at the first + g_vm->_vdbFP.seek(_ctx->curVoc->_offset); + g_vm->_theSound.createSfx(&_ctx->voice); + _ctx->voice->loadVoiceFromVDB(g_vm->_vdbFP); + _ctx->voice->setLoop(false); + if (_ctx->bIsBack) + _ctx->voice->setVolume(55); + } + + _ctx->string = mpalQueryDialogPeriod(nMsg); + + if (nPers == 0) { + _ctx->text = new RMTextDialog; + _ctx->text->setColor(0, 255, 0); + _ctx->text->setPosition(GLOBALS._tony->position() - RMPoint(0, 130) - GLOBALS._loc->scrollPosition()); + _ctx->text->writeText(_ctx->string, 0); + + if (GLOBALS._dwTonyNumTexts > 0) { + if (!GLOBALS._bTonyInTexts) { + if (GLOBALS._nTonyNextTalkType != GLOBALS._tony->TALK_NORMAL) { + CORO_INVOKE_1(GLOBALS._tony->startTalk, GLOBALS._nTonyNextTalkType); + if (!GLOBALS._bStaticTalk) + GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_NORMAL; + } else + CORO_INVOKE_1(GLOBALS._tony->startTalk, GLOBALS._tony->TALK_NORMAL); + + GLOBALS._bTonyInTexts = true; + } + GLOBALS._dwTonyNumTexts--; + } else { + CORO_INVOKE_1(GLOBALS._tony->startTalk, GLOBALS._nTonyNextTalkType); + if (!GLOBALS._bStaticTalk) + GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_NORMAL; + } + } else if (!GLOBALS._isMChar[nPers]) { + _ctx->text = new RMTextDialog; + + _ctx->pt = GLOBALS._character[nPers]._item->calculatePos() - RMPoint(-60, 20) - GLOBALS._loc->scrollPosition(); + + if (GLOBALS._character[nPers]._startTalkPattern != 0) { + GLOBALS._character[nPers]._item->setPattern(GLOBALS._character[nPers]._startTalkPattern); + CORO_INVOKE_0(GLOBALS._character[nPers]._item->waitForEndPattern); + } + + GLOBALS._character[nPers]._item->setPattern(GLOBALS._character[nPers]._talkPattern); + + _ctx->text->setColor(GLOBALS._character[nPers]._r, GLOBALS._character[nPers]._g, GLOBALS._character[nPers]._b); + _ctx->text->writeText(_ctx->string, 0); + _ctx->text->setPosition(_ctx->pt); + } else { + if (GLOBALS._mCharacter[nPers]._x == -1) + _ctx->pt = GLOBALS._mCharacter[nPers]._item->calculatePos() - RMPoint(-60, 20) - GLOBALS._loc->scrollPosition(); + else + _ctx->pt = RMPoint(GLOBALS._mCharacter[nPers]._x, GLOBALS._mCharacter[nPers]._y); + + // Parameter for special actions. Random between the spoken. + _ctx->parm = (GLOBALS._mCharacter[nPers]._curGroup * 10) + g_vm->_randomSource.getRandomNumber( + GLOBALS._mCharacter[nPers]._numTalks[GLOBALS._mCharacter[nPers]._curGroup] - 1) + 1; + + if (GLOBALS._mCharacter[nPers]._numTexts != 0 && GLOBALS._mCharacter[nPers]._bInTexts) { + GLOBALS._mCharacter[nPers]._numTexts--; + } else { + // Try to run the custom function to initialize the speech + _ctx->h = mpalQueryDoAction(30, GLOBALS._mCharacter[nPers]._item->mpalCode(), _ctx->parm); + if (_ctx->h != CORO_INVALID_PID_VALUE) + CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _ctx->h, CORO_INFINITE); + + GLOBALS._mCharacter[nPers]._curTalk = _ctx->parm; + + if (GLOBALS._mCharacter[nPers]._numTexts != 0) { + GLOBALS._mCharacter[nPers]._bInTexts = true; + GLOBALS._mCharacter[nPers]._numTexts--; + } + } + + if (GLOBALS._mCharacter[nPers]._bAlwaysBack) { + _ctx->text = GLOBALS._curBackText = new RMTextDialogScrolling(GLOBALS._loc); + if (GLOBALS._bTonyIsSpeaking) + CORO_INVOKE_0(GLOBALS._curBackText->hide); + + _ctx->bIsBack = true; + } else + _ctx->text = new RMTextDialog; + + _ctx->text->setSkipStatus(!GLOBALS._mCharacter[nPers]._bAlwaysBack); + _ctx->text->setColor(GLOBALS._mCharacter[nPers]._r, GLOBALS._mCharacter[nPers]._g, GLOBALS._mCharacter[nPers]._b); + _ctx->text->writeText(_ctx->string, 0); + _ctx->text->setPosition(_ctx->pt); + } + + if (!GLOBALS._bSkipIdle) { + _ctx->text->setInput(GLOBALS._input); + if (GLOBALS._bAlwaysDisplay) { + _ctx->text->setAlwaysDisplay(); + _ctx->text->forceTime(); + } + _ctx->text->setAlignType(RMText::HCENTER, RMText::VBOTTOM); + g_vm->getEngine()->linkGraphicTask(_ctx->text); + + if (_ctx->curVoc) { + _ctx->voice->play(); + _ctx->text->setCustomSkipHandle2(_ctx->voice->_hEndOfBuffer); + } + + // Wait for the end of display + _ctx->text->setCustomSkipHandle(GLOBALS._hSkipIdle); + CORO_INVOKE_0(_ctx->text->waitForEndDisplay); + } + + if (_ctx->curVoc) { + _ctx->voice->stop(); + _ctx->voice->release(); + _ctx->voice = NULL; + } + + if (nPers != 0) { + if (!GLOBALS._isMChar[nPers]) { + if (GLOBALS._character[nPers]._endTalkPattern != 0) { + GLOBALS._character[nPers]._item->setPattern(GLOBALS._character[nPers]._endTalkPattern); + CORO_INVOKE_0(GLOBALS._character[nPers]._item->waitForEndPattern); + } + + GLOBALS._character[nPers]._item->setPattern(GLOBALS._character[nPers]._standPattern); + delete _ctx->text; + } else { + if ((GLOBALS._mCharacter[nPers]._bInTexts && GLOBALS._mCharacter[nPers]._numTexts == 0) || !GLOBALS._mCharacter[nPers]._bInTexts) { + // Try to run the custom function to close the speech + GLOBALS._mCharacter[nPers]._curTalk = (GLOBALS._mCharacter[nPers]._curTalk % 10) + GLOBALS._mCharacter[nPers]._curGroup * 10; + _ctx->h = mpalQueryDoAction(31, GLOBALS._mCharacter[nPers]._item->mpalCode(), GLOBALS._mCharacter[nPers]._curTalk); + if (_ctx->h != CORO_INVALID_PID_VALUE) + CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _ctx->h, CORO_INFINITE); + + GLOBALS._mCharacter[nPers]._bInTexts = false; + GLOBALS._mCharacter[nPers]._numTexts = 0; + } + + GLOBALS._curBackText = NULL; + delete _ctx->text; + } + } else { + if ((GLOBALS._dwTonyNumTexts == 0 && GLOBALS._bTonyInTexts) || !GLOBALS._bTonyInTexts) { + CORO_INVOKE_0(GLOBALS._tony->endTalk); + GLOBALS._dwTonyNumTexts = 0; + GLOBALS._bTonyInTexts = false; + } + + delete _ctx->text; + } + + globalDestroy(_ctx->string); + + CORO_END_CODE; +} + +// @@@@ This cannot be skipped!!!!!!!!!!!!!!!!!!! + +void startDialog(CORO_PARAM, uint32 nDialog, uint32 nStartGroup, uint32, uint32) { + CORO_BEGIN_CONTEXT; + uint32 nChoice; + uint32 *sl; + uint32 i, num; + char *string; + RMDialogChoice dc; + int sel; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + GLOBALS._curDialog = nDialog; + + // Call MPAL to start the dialog + mpalQueryDoDialog(nDialog, nStartGroup); + + // Wait until a choice is selected + mpalQueryDialogWaitForChoice(&_ctx->nChoice); + while (_ctx->nChoice != (uint32) - 1) { + // Get the list of options + _ctx->sl = mpalQueryDialogSelectList(_ctx->nChoice); + for (_ctx->num = 0; _ctx->sl[_ctx->num] != 0; _ctx->num++) + ; + + // If there is only one option, do it automatically, and wait for the next choice + if (_ctx->num == 1) { + mpalQueryDialogSelectionDWORD(_ctx->nChoice, _ctx->sl[0]); + globalDestroy(_ctx->sl); + + // Wait for the next choice to be made + mpalQueryDialogWaitForChoice(&_ctx->nChoice); + continue; + } + + // Making a choice for dialog + _ctx->dc.init(); + _ctx->dc.setNumChoices(_ctx->num); + + // Writeall the possible options + for (_ctx->i = 0; _ctx->i < _ctx->num; _ctx->i++) { + _ctx->string = mpalQueryDialogPeriod(_ctx->sl[_ctx->i]); + assert(_ctx->string != NULL); + _ctx->dc.addChoice(_ctx->string); + globalDestroy(_ctx->string); + } + + // Activate the object + g_vm->getEngine()->linkGraphicTask(&_ctx->dc); + CORO_INVOKE_0(_ctx->dc.show); + + // Draw the pointer + GLOBALS._pointer->setSpecialPointer(GLOBALS._pointer->PTR_NONE); + g_vm->getEngine()->enableMouse(); + + while (!(GLOBALS._input->mouseLeftClicked() && ((_ctx->sel = _ctx->dc.getSelection()) != -1))) { + CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE); + CORO_INVOKE_1(_ctx->dc.doFrame, GLOBALS._input->mousePos()); + } + + // Hide the pointer + g_vm->getEngine()->disableMouse(); + + CORO_INVOKE_0(_ctx->dc.hide); + mpalQueryDialogSelectionDWORD(_ctx->nChoice, _ctx->sl[_ctx->sel]); + + // Closes the choice + _ctx->dc.close(); + + globalDestroy(_ctx->sl); + + // Wait for the next choice to be made + mpalQueryDialogWaitForChoice(&_ctx->nChoice); + } + + CORO_END_CODE; +} + +/* + * Sync between idle and mpal + */ + +void takeOwnership(CORO_PARAM, uint32 num, uint32, uint32, uint32) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + if (GLOBALS._mut[num]._ownerPid != (uint32)CoroScheduler.getCurrentPID()) { + // The mutex is currently owned by a different process. + // Wait for the event to be signalled, which means the mutex is free. + CORO_INVOKE_2(CoroScheduler.waitForSingleObject, GLOBALS._mut[num]._eventId, CORO_INFINITE); + GLOBALS._mut[num]._ownerPid = (uint32)CoroScheduler.getCurrentPID(); + } + + GLOBALS._mut[num]._lockCount++; + + CORO_END_CODE; +} + +void releaseOwnership(CORO_PARAM, uint32 num, uint32, uint32, uint32) { + if (!GLOBALS._mut[num]._lockCount) { + warning("ReleaseOwnership tried to release mutex %d, which isn't held", num); + return; + } + + if (GLOBALS._mut[num]._ownerPid != (uint32)CoroScheduler.getCurrentPID()) { + warning("ReleaseOwnership tried to release mutex %d, which is held by a different process", num); + return; + } + + GLOBALS._mut[num]._lockCount--; + if (!GLOBALS._mut[num]._lockCount) { + GLOBALS._mut[num]._ownerPid = 0; + + // Signal the event, to wake up processes waiting for the lock. + CoroScheduler.setEvent(GLOBALS._mut[num]._eventId); + } +} + +/* + * Music + * ----- + * + * Fadeout effects supposed: + * + * nFX = 0 - The new music replaces the old one + * nFX=1 - The new music interfades with the old one + * nFX=2 - The new music takes over in time from the old + * + */ + +void threadFadeInMusic(CORO_PARAM, const void *nMusic) { + CORO_BEGIN_CONTEXT; + int i; + CORO_END_CONTEXT(_ctx); + + int nChannel = *(const int *)nMusic; + + CORO_BEGIN_CODE(_ctx); + + debugC(DEBUG_INTERMEDIATE, kTonyDebugSound, "Start FadeIn Music"); + + for (_ctx->i = 0; _ctx->i < 16; _ctx->i++) { + g_vm->setMusicVolume(nChannel, _ctx->i * 4); + + CORO_INVOKE_1(CoroScheduler.sleep, 100); + } + g_vm->setMusicVolume(nChannel, 64); + + debugC(DEBUG_INTERMEDIATE, kTonyDebugSound, "End FadeIn Music"); + + CORO_KILL_SELF(); + + CORO_END_CODE; +} + +void threadFadeOutMusic(CORO_PARAM, const void *nMusic) { + CORO_BEGIN_CONTEXT; + int i; + int startVolume; + CORO_END_CONTEXT(_ctx); + + int nChannel = *(const int *)nMusic; + + CORO_BEGIN_CODE(_ctx); + + _ctx->startVolume = g_vm->getMusicVolume(nChannel); + + for (_ctx->i = 16; _ctx->i > 0 && !GLOBALS._bFadeOutStop; _ctx->i--) { + if (_ctx->i * 4 < _ctx->startVolume) + g_vm->setMusicVolume(nChannel, _ctx->i * 4); + + CORO_INVOKE_1(CoroScheduler.sleep, 100); + } + + if (!GLOBALS._bFadeOutStop) + g_vm->setMusicVolume(nChannel, 0); + + // If a jingle is played, stop it + if (nChannel == 2) + g_vm->stopMusic(2); + + CORO_KILL_SELF(); + + CORO_END_CODE; +} + +void fadeInSoundEffect(CORO_PARAM, uint32, uint32, uint32, uint32) { + CoroScheduler.createProcess(threadFadeInMusic, &GLOBALS._curSoundEffect, sizeof(int)); +} + +void fadeOutSoundEffect(CORO_PARAM, uint32, uint32, uint32, uint32) { + GLOBALS._bFadeOutStop = false; + CoroScheduler.createProcess(threadFadeOutMusic, &GLOBALS._curSoundEffect, sizeof(int)); +} + +void fadeOutJingle(CORO_PARAM, uint32, uint32, uint32, uint32) { + GLOBALS._bFadeOutStop = false; + int channel = 2; + CoroScheduler.createProcess(threadFadeOutMusic, &channel, sizeof(int)); +} + +void fadeInJingle(CORO_PARAM, uint32, uint32, uint32, uint32) { + int channel = 2; + CoroScheduler.createProcess(threadFadeInMusic, &channel, sizeof(int)); +} + +void stopSoundEffect(CORO_PARAM, uint32, uint32, uint32, uint32) { + g_vm->stopMusic(GLOBALS._curSoundEffect); +} + +void stopJingle(CORO_PARAM, uint32, uint32, uint32, uint32) { + g_vm->stopMusic(2); +} + +void muteSoundEffect(CORO_PARAM, uint32, uint32, uint32, uint32) { + g_vm->setMusicVolume(GLOBALS._curSoundEffect, 0); +} + +void demuteSoundEffect(CORO_PARAM, uint32, uint32, uint32, uint32) { + GLOBALS._bFadeOutStop = true; + g_vm->setMusicVolume(GLOBALS._curSoundEffect, 64); +} + +void muteJingle(CORO_PARAM, uint32, uint32, uint32, uint32) { + g_vm->setMusicVolume(2, 0); +} + +void demuteJingle(CORO_PARAM, uint32, uint32, uint32, uint32) { + g_vm->setMusicVolume(2, 64); +} + +void custPlayMusic(uint32 nChannel, const char *mFN, uint32 nFX, bool bLoop, int nSync = 0) { + if (nSync == 0) + nSync = 2000; + debugC(DEBUG_INTERMEDIATE, kTonyDebugMusic, "Start CustPlayMusic"); + g_vm->playMusic(nChannel, mFN, nFX, bLoop, nSync); + debugC(DEBUG_INTERMEDIATE, kTonyDebugMusic, "End CustPlayMusic"); +} + +void playSoundEffect(CORO_PARAM, uint32 nMusic, uint32 nFX, uint32 bNoLoop, uint32) { + if (nFX == 0 || nFX == 1 || nFX == 2) { + debugC(DEBUG_INTERMEDIATE, kTonyDebugSound, "PlaySoundEffect stop fadeout"); + GLOBALS._bFadeOutStop = true; + } + + GLOBALS._lastMusic = nMusic; + custPlayMusic(GLOBALS._curSoundEffect, kMusicFiles[nMusic]._name, nFX, bNoLoop ? false : true, kMusicFiles[nMusic]._sync); +} + +void playJingle(CORO_PARAM, uint32 nMusic, uint32 nFX, uint32 bLoop, uint32) { + custPlayMusic(2, kJingleFileNames[nMusic], nFX, bLoop); +} + +void playItemSfx(CORO_PARAM, uint32 nItem, uint32 nSFX, uint32, uint32) { + if (nItem == 0) { + GLOBALS._tony->playSfx(nSFX); + } else { + RMItem *item = GLOBALS._loc->getItemFromCode(nItem); + if (item) + item->playSfx(nSFX); + } +} + +void restoreMusic(CORO_PARAM) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + CORO_INVOKE_4(playSoundEffect, GLOBALS._lastMusic, 0, 0, 0); + + if (GLOBALS._lastTappeto != 0) + custPlayMusic(4, kAmbianceFile[GLOBALS._lastTappeto], 0, true); + + CORO_END_CODE; +} + +void saveMusic(Common::OutSaveFile *f) { + f->writeByte(GLOBALS._lastMusic); + f->writeByte(GLOBALS._lastTappeto); +} + +void loadMusic(Common::InSaveFile *f) { + GLOBALS._lastMusic = f->readByte(); + GLOBALS._lastTappeto = f->readByte(); +} + +void jingleFadeStart(CORO_PARAM, uint32 nJingle, uint32 bLoop, uint32, uint32) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + CORO_INVOKE_4(fadeOutSoundEffect, 0, 0, 0, 0); + CORO_INVOKE_4(muteJingle, 0, 0, 0, 0); + CORO_INVOKE_4(playJingle, nJingle, 0, bLoop, 0); + CORO_INVOKE_4(fadeInJingle, 0, 0, 0, 0); + + CORO_END_CODE; +} + +void jingleFadeEnd(CORO_PARAM, uint32 nJingle, uint32 bLoop, uint32, uint32) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + CORO_INVOKE_4(fadeOutJingle, 0, 0, 0, 0); + CORO_INVOKE_4(fadeInSoundEffect, 0, 0, 0, 0); + + CORO_END_CODE; +} + +void mustSkipIdleStart(CORO_PARAM, uint32, uint32, uint32, uint32) { + GLOBALS._bSkipIdle = true; + CoroScheduler.setEvent(GLOBALS._hSkipIdle); +} + +void mustSkipIdleEnd(CORO_PARAM, uint32, uint32, uint32, uint32) { + GLOBALS._bSkipIdle = false; + CoroScheduler.resetEvent(GLOBALS._hSkipIdle); +} + +void patIrqFreeze(CORO_PARAM, uint32 bStatus, uint32, uint32, uint32) { + // Unused in ScummVM. +} + +void openInitLoadMenu(CORO_PARAM, uint32, uint32, uint32, uint32) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + CORO_INVOKE_0(g_vm->openInitLoadMenu); + + CORO_END_CODE; +} + +void openInitOptions(CORO_PARAM, uint32, uint32, uint32, uint32) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + CORO_INVOKE_0(g_vm->openInitOptions); + + CORO_END_CODE; +} + +void doCredits(CORO_PARAM, uint32 nMsg, uint32 dwTime, uint32, uint32) { + CORO_BEGIN_CONTEXT; + RMMessage *msg; + RMTextDialog *text; + uint32 hDisable; + int i; + uint32 startTime; + + ~CoroContextTag() { + delete msg; + delete[] text; + } + + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + _ctx->msg = new RMMessage(nMsg); + _ctx->hDisable = CoroScheduler.createEvent(true, false); + + _ctx->text = new RMTextDialog[_ctx->msg->numPeriods()]; + + for (_ctx->i = 0; _ctx->i < _ctx->msg->numPeriods(); _ctx->i++) { + _ctx->text[_ctx->i].setInput(GLOBALS._input); + + // Alignment + if ((*_ctx->msg)[_ctx->i][0] == '@') { + _ctx->text[_ctx->i].setAlignType(RMText::HCENTER, RMText::VTOP); + _ctx->text[_ctx->i].writeText(&(*_ctx->msg)[_ctx->i][1], 3); + _ctx->text[_ctx->i].setPosition(RMPoint(414, 70 + _ctx->i * 26)); // 70 + } else { + _ctx->text[_ctx->i].setAlignType(RMText::HLEFT, RMText::VTOP); + _ctx->text[_ctx->i].writeText((*_ctx->msg)[_ctx->i], 3); + _ctx->text[_ctx->i].setPosition(RMPoint(260, 70 + _ctx->i * 26)); + } + + // Set the position + _ctx->text[_ctx->i].setAlwaysDisplay(); + _ctx->text[_ctx->i].setForcedTime(dwTime * 1000); + _ctx->text[_ctx->i].setNoTab(); + + // Wait for the end of display + _ctx->text[_ctx->i].setCustomSkipHandle(_ctx->hDisable); + + // Record the text + g_vm->getEngine()->linkGraphicTask(&_ctx->text[_ctx->i]); + } + + _ctx->startTime = g_vm->getTime(); + + while (_ctx->startTime + dwTime * 1000 > g_vm->getTime()) { + CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE); + if (GLOBALS._input->mouseLeftClicked() || GLOBALS._input->mouseRightClicked()) + break; + if (g_vm->getEngine()->getInput().getAsyncKeyState(Common::KEYCODE_TAB)) + break; + } + + CoroScheduler.setEvent(_ctx->hDisable); + + CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE); + CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE); + + delete[] _ctx->text; + delete _ctx->msg; + _ctx->text = NULL; + _ctx->msg = NULL; + + CORO_END_CODE; +} + +BEGIN_CUSTOM_FUNCTION_MAP() + +ASSIGN(1, custLoadLocation) +ASSIGN(2, mySleep) +ASSIGN(3, setPointer) +ASSIGN(5, moveTony) +ASSIGN(6, faceToMe) +ASSIGN(7, backToMe) +ASSIGN(8, leftToMe) +ASSIGN(9, rightToMe) +ASSIGN(10, sendTonyMessage) +ASSIGN(11, changeBoxStatus) +ASSIGN(12, changeLocation) +ASSIGN(13, disableTony) +ASSIGN(14, enableTony) +ASSIGN(15, waitForPatternEnd) +ASSIGN(16, setLocStartPosition) +ASSIGN(17, scrollLocation) +ASSIGN(18, moveTonyAndWait) +ASSIGN(19, changeHotspot) +ASSIGN(20, addInventory) +ASSIGN(21, removeInventory) +ASSIGN(22, changeInventoryStatus) +ASSIGN(23, setTonyPosition) +ASSIGN(24, sendFullscreenMessage) +ASSIGN(25, saveTonyPosition) +ASSIGN(26, restoreTonyPosition) +ASSIGN(27, disableInput) +ASSIGN(28, enableInput) +ASSIGN(29, stopTony) + +ASSIGN(30, tonyTakeUp1) +ASSIGN(31, tonyTakeMid1) +ASSIGN(32, tonyTakeDown1) +ASSIGN(33, tonyTakeUp2) +ASSIGN(34, tonyTakeMid2) +ASSIGN(35, tonyTakeDown2) + +ASSIGN(72, tonyPutUp1) +ASSIGN(73, tonyPutMid1) +ASSIGN(74, tonyPutDown1) +ASSIGN(75, tonyPutUp2) +ASSIGN(76, tonyPutMid2) +ASSIGN(77, tonyPutDown2) + +ASSIGN(36, tonyOnTheFloor) +ASSIGN(37, tonyGetUp) +ASSIGN(38, tonyShepherdess) +ASSIGN(39, tonyWhistle) + +ASSIGN(40, tonyLaugh) +ASSIGN(41, tonyHips) +ASSIGN(42, tonySing) +ASSIGN(43, tonyIndicate) +ASSIGN(44, tonyScaredWithHands) +ASSIGN(49, tonyScaredWithoutHands) +ASSIGN(45, tonyWithGlasses) +ASSIGN(46, tonyWithWorm) +ASSIGN(47, tonyWithHammer) +ASSIGN(48, tonyWithRope) +ASSIGN(90, tonyWithRabbitANIM) +ASSIGN(91, tonyWithRecipeANIM) +ASSIGN(92, tonyWithCardsANIM) +ASSIGN(93, tonyWithSnowmanANIM) +ASSIGN(94, tonyWithSnowmanStart) +ASSIGN(95, tonyWithSnowmanEnd) +ASSIGN(96, tonyWithRabbitStart) +ASSIGN(97, tonyWithRabbitEnd) +ASSIGN(98, tonyWithRecipeStart) +ASSIGN(99, tonyWithRecipeEnd) +ASSIGN(100, tonyWithCardsStart) +ASSIGN(101, tonyWithCardsEnd) +ASSIGN(102, tonyWithNotebookStart) +ASSIGN(103, tonyWithNotebookEnd) +ASSIGN(104, tonyWithMegaphoneStart) +ASSIGN(105, tonyWithMegaphoneEnd) +ASSIGN(106, tonyWithBeardStart) +ASSIGN(107, tonyWithBeardEnd) +ASSIGN(108, tonyGiggle) +ASSIGN(109, tonyDisgusted) +ASSIGN(110, tonySarcastic) +ASSIGN(111, tonyMacbeth) +ASSIGN(112, tonySniffLeft) +ASSIGN(113, tonySniffRight) +ASSIGN(114, tonyScaredStart) +ASSIGN(115, tonyScaredEnd) +ASSIGN(116, tonyWithSecretary) + +ASSIGN(50, charSetCode) +ASSIGN(51, charSetColor) +ASSIGN(52, charSetTalkPattern) +ASSIGN(53, charSendMessage) +ASSIGN(54, charSetStartEndTalkPattern) + +ASSIGN(60, mCharSetCode) +ASSIGN(61, mCharSetColor) +ASSIGN(62, mCharSetCurrentGroup) +ASSIGN(63, mCharSetNumTalksInGroup) +ASSIGN(64, mCharSetNumTexts) +ASSIGN(65, mCharSendMessage) +ASSIGN(66, mCharSetPosition) +ASSIGN(67, mCharSetAlwaysBack) +ASSIGN(68, mCharResetCode) + +ASSIGN(70, startDialog) +ASSIGN(71, sendDialogMessage) + +ASSIGN(80, takeOwnership) +ASSIGN(81, releaseOwnership) + +ASSIGN(86, playSoundEffect) +ASSIGN(87, playJingle) +ASSIGN(88, fadeInSoundEffect) +ASSIGN(89, fadeOutSoundEffect) +ASSIGN(123, fadeInJingle) +ASSIGN(124, fadeOutJingle) +ASSIGN(125, muteSoundEffect) +ASSIGN(126, demuteSoundEffect) +ASSIGN(127, muteJingle) +ASSIGN(128, demuteJingle) +ASSIGN(84, stopSoundEffect) +ASSIGN(85, stopJingle) +ASSIGN(83, playItemSfx) +ASSIGN(129, jingleFadeStart) +ASSIGN(130, jingleFadeEnd) + +ASSIGN(120, shakeScreen) +ASSIGN(121, autoSave) +ASSIGN(122, abortGame) +ASSIGN(131, noBullsEye) +ASSIGN(132, sendFullscreenMsgStart) +ASSIGN(133, sendFullscreenMsgEnd) +ASSIGN(134, custEnableGUI) +ASSIGN(135, custDisableGUI) +ASSIGN(136, clearScreen) +ASSIGN(137, patIrqFreeze) +ASSIGN(138, tonySetPerorate) +ASSIGN(139, openInitLoadMenu) +ASSIGN(140, openInitOptions) +ASSIGN(141, syncScrollLocation) +ASSIGN(142, closeLocation) +ASSIGN(143, setAlwaysDisplay) +ASSIGN(144, doCredits) + +ASSIGN(200, mustSkipIdleStart); +ASSIGN(201, mustSkipIdleEnd); + +END_CUSTOM_FUNCTION_MAP() + +void processKilledCallback(Common::PROCESS *p) { + for (uint i = 0; i < 10; i++) { + if (GLOBALS._mut[i]._ownerPid == p->pid) { + // Handle scripts which don't call ReleaseOwnership, such as + // the one in loc37's vEnter when Tony is chasing the mouse. + debug(DEBUG_BASIC, "Force-releasing mutex %d after process died", i); + + GLOBALS._mut[i]._ownerPid = 0; + GLOBALS._mut[i]._lockCount = 0; + CoroScheduler.setEvent(GLOBALS._mut[i]._eventId); + } + } +} + +void setupGlobalVars(RMTony *tony, RMPointer *ptr, RMGameBoxes *box, RMLocation *loc, RMInventory *inv, RMInput *input) { + GLOBALS._tony = tony; + GLOBALS._pointer = ptr; + GLOBALS._boxes = box; + GLOBALS._loc = loc; + GLOBALS._inventory = inv; + GLOBALS._input = input; + + GLOBALS.DisableGUI = mainDisableGUI; + GLOBALS.EnableGUI = mainEnableGUI; + + GLOBALS._bAlwaysDisplay = false; + + CoroScheduler.setResourceCallback(processKilledCallback); + for (int i = 0; i < 10; i++) + GLOBALS._mut[i]._eventId = CoroScheduler.createEvent(false, true); + + for (int i = 0; i < 200; i++) + GLOBALS._ambiance[i] = 0; + + GLOBALS._ambiance[6] = AMBIANCE_CRICKETS; + GLOBALS._ambiance[7] = AMBIANCE_CRICKETS; + GLOBALS._ambiance[8] = AMBIANCE_CRICKETSMUFFLED; + GLOBALS._ambiance[10] = AMBIANCE_CRICKETS; + GLOBALS._ambiance[12] = AMBIANCE_CRICKETS; + GLOBALS._ambiance[13] = AMBIANCE_CRICKETSMUFFLED; + GLOBALS._ambiance[15] = AMBIANCE_CRICKETS; + GLOBALS._ambiance[16] = AMBIANCE_CRICKETSWIND; + GLOBALS._ambiance[18] = AMBIANCE_CRICKETS; + GLOBALS._ambiance[19] = AMBIANCE_CRICKETSWIND; + GLOBALS._ambiance[20] = AMBIANCE_CRICKETS; + GLOBALS._ambiance[23] = AMBIANCE_CRICKETS; + GLOBALS._ambiance[26] = AMBIANCE_SEAHALFVOLUME; + GLOBALS._ambiance[27] = AMBIANCE_CRICKETS; + GLOBALS._ambiance[28] = AMBIANCE_CRICKETSWIND; + GLOBALS._ambiance[31] = AMBIANCE_CRICKETS; + GLOBALS._ambiance[33] = AMBIANCE_SEA; + GLOBALS._ambiance[35] = AMBIANCE_SEA; + GLOBALS._ambiance[36] = AMBIANCE_CRICKETS; + GLOBALS._ambiance[37] = AMBIANCE_CRICKETS; + GLOBALS._ambiance[40] = AMBIANCE_CRICKETS; + GLOBALS._ambiance[41] = AMBIANCE_CRICKETS; + GLOBALS._ambiance[42] = AMBIANCE_CRICKETS; + GLOBALS._ambiance[45] = AMBIANCE_CRICKETS; + GLOBALS._ambiance[51] = AMBIANCE_CRICKETS; + GLOBALS._ambiance[52] = AMBIANCE_CRICKETSWIND1; + GLOBALS._ambiance[53] = AMBIANCE_CRICKETS; + GLOBALS._ambiance[54] = AMBIANCE_CRICKETS; + GLOBALS._ambiance[57] = AMBIANCE_WIND; + GLOBALS._ambiance[58] = AMBIANCE_WIND; + GLOBALS._ambiance[60] = AMBIANCE_WIND; + + // Create an event for the idle skipping + GLOBALS._hSkipIdle = CoroScheduler.createEvent(true, false); +} + +} // end of namespace Tony diff --git a/engines/tony/custom.h b/engines/tony/custom.h new file mode 100644 index 0000000000..0f1061e8cd --- /dev/null +++ b/engines/tony/custom.h @@ -0,0 +1,85 @@ +/* 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. + * + */ + +/* + * This code is based on original Tony Tough source code + * + * Copyright (c) 1997-2003 Nayma Software + */ + +#ifndef TONY_CUSTOM_H +#define TONY_CUSTOM_H + +#include "common/str.h" +#include "tony/mpal/mpal.h" + +namespace Tony { + +using namespace MPAL; + +struct MusicFileEntry { + const char *_name; + int _sync; +}; + +#define INIT_CUSTOM_FUNCTION MapCustomFunctions + +#define BEGIN_CUSTOM_FUNCTION_MAP() \ + static void AssignError(int num) { \ + error("Custom function %u has been already assigned!", num); \ + } \ + void INIT_CUSTOM_FUNCTION(LPCUSTOMFUNCTION *lpMap, Common::String *lpStrMap) \ + { + +#define END_CUSTOM_FUNCTION_MAP() \ + } + +#define ASSIGN(num, func) \ + if (lpMap[num] != NULL) \ + AssignError(num); \ + lpMap[num] = func; \ + lpStrMap[num] = #func; + +class RMTony; +class RMPointer; +class RMGameBoxes; +class RMLocation; +class RMInventory; +class RMInput; + +void charsSaveAll(Common::OutSaveFile *f); +void charsLoadAll(Common::InSaveFile *f); +void mCharResetCodes(); +void saveChangedHotspot(Common::OutSaveFile *f); +void loadChangedHotspot(Common::InSaveFile *f); +void reapplyChangedHotspot(); + +void restoreMusic(CORO_PARAM); +void saveMusic(Common::OutSaveFile *f); +void loadMusic(Common::InSaveFile *f); + +void INIT_CUSTOM_FUNCTION(LPCUSTOMFUNCTION *lpMap, Common::String *lpStrMap); +void setupGlobalVars(RMTony *tony, RMPointer *ptr, RMGameBoxes *box, RMLocation *loc, RMInventory *inv, RMInput *input); + +#endif + +} // end of namespace Tony diff --git a/engines/tony/debugger.cpp b/engines/tony/debugger.cpp new file mode 100644 index 0000000000..84f05b0d25 --- /dev/null +++ b/engines/tony/debugger.cpp @@ -0,0 +1,129 @@ +/* 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 "common/coroutines.h" +#include "tony/debugger.h" +#include "tony/globals.h" +#include "tony/tony.h" + +namespace Tony { + +Debugger::Debugger() : GUI::Debugger() { + DCmd_Register("continue", WRAP_METHOD(Debugger, Cmd_Exit)); + DCmd_Register("scene", WRAP_METHOD(Debugger, Cmd_Scene)); + DCmd_Register("dirty_rects", WRAP_METHOD(Debugger, Cmd_DirtyRects)); +} + +static int strToInt(const char *s) { + if (!*s) + // No string at all + return 0; + else if (toupper(s[strlen(s) - 1]) != 'H') + // Standard decimal string + return atoi(s); + + // Hexadecimal string + uint tmp = 0; + int read = sscanf(s, "%xh", &tmp); + if (read < 1) + error("strToInt failed on string \"%s\"", s); + return (int)tmp; +} + +/** + * Support process for changing the scene + */ +struct ChangeSceneDetails { + int sceneNumber; + int x; + int y; +}; + +void DebugChangeScene(CORO_PARAM, const void *param) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + uint32 result; + const ChangeSceneDetails *details = (const ChangeSceneDetails *)param; + RMPoint scenePos(details->x, details->y); + + CORO_BEGIN_CODE(_ctx); + + CORO_INVOKE_2(g_vm->getEngine()->unloadLocation, false, &result); + + g_vm->getEngine()->loadLocation(details->sceneNumber, scenePos, RMPoint(-1, -1)); + + mainEnableGUI(); + + CORO_END_CODE; +} + +/** + * This command loads up the specified new scene number + */ +bool Debugger::Cmd_Scene(int argc, const char **argv) { + if (argc < 2) { + DebugPrintf("Usage: %s <scene number> [<x> <y>]\n", argv[0]); + return true; + } + + int sceneNumber = strToInt(argv[1]); + if (sceneNumber >= g_vm->_theBoxes.getLocBoxesCount()) { + DebugPrintf("Invalid scene\n"); + return true; + } + + RMPoint scenePos; + if (argc >= 4) { + scenePos._x = strToInt(argv[2]); + scenePos._y = strToInt(argv[3]); + } else { + // Get the box areas for the scene, and choose one so as to have a default + // position for Tony that will be in the walkable areas + RMBoxLoc *box = g_vm->_theBoxes.getBoxes(sceneNumber); + scenePos.set(box->_boxes[0]._hotspot[0]._hotx, box->_boxes[0]._hotspot[0]._hoty); + } + + // Set up a process to change the scene + ChangeSceneDetails details; + details.sceneNumber = sceneNumber; + details.x = scenePos._x; + details.y = scenePos._y; + CoroScheduler.createProcess(DebugChangeScene, &details, sizeof(ChangeSceneDetails)); + + return false; +} + +/** + * Turns showing dirty rects on or off + */ +bool Debugger::Cmd_DirtyRects(int argc, const char **argv) { + if (argc != 2) { + DebugPrintf("Usage; %s [on | off]\n", argv[0]); + return true; + } else { + g_vm->_window.showDirtyRects(strcmp(argv[1], "on") == 0); + return false; + } +} + +} // End of namespace Tony diff --git a/engines/tony/debugger.h b/engines/tony/debugger.h new file mode 100644 index 0000000000..85ba9d75b6 --- /dev/null +++ b/engines/tony/debugger.h @@ -0,0 +1,43 @@ +/* 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 TONY_DEBUGGER_H +#define TONY_DEBUGGER_H + +#include "common/scummsys.h" +#include "gui/debugger.h" + +namespace Tony { + +class Debugger : public GUI::Debugger { +public: + Debugger(); + virtual ~Debugger() {} + +protected: + bool Cmd_Scene(int argc, const char **argv); + bool Cmd_DirtyRects(int argc, const char **argv); +}; + +} // End of namespace Tony + +#endif diff --git a/engines/tony/detection.cpp b/engines/tony/detection.cpp new file mode 100644 index 0000000000..1094950e2c --- /dev/null +++ b/engines/tony/detection.cpp @@ -0,0 +1,193 @@ +/* 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 "base/plugins.h" + +#include "common/memstream.h" +#include "engines/advancedDetector.h" +#include "common/system.h" +#include "graphics/colormasks.h" +#include "graphics/surface.h" + +#include "tony/tony.h" +#include "tony/game.h" + +namespace Tony { + +enum { + GF_COMPRESSED = (1 << 0) +}; + +struct TonyGameDescription { + ADGameDescription desc; +}; + +uint32 TonyEngine::getFeatures() const { + return _gameDescription->desc.flags; +} + +Common::Language TonyEngine::getLanguage() const { + return _gameDescription->desc.language; +} + +bool TonyEngine::getIsDemo() const { + return _gameDescription->desc.flags & ADGF_DEMO; +} + +bool TonyEngine::isCompressed() const { + return _gameDescription->desc.flags & GF_COMPRESSED; +} + +} // End of namespace Tony + +static const PlainGameDescriptor tonyGames[] = { + {"tony", "Tony Tough and the Night of Roasted Moths"}, + {0, 0} +}; + +#include "tony/detection_tables.h" + +class TonyMetaEngine : public AdvancedMetaEngine { +public: + TonyMetaEngine() : AdvancedMetaEngine(Tony::gameDescriptions, sizeof(Tony::TonyGameDescription), tonyGames) { + } + + virtual const char *getName() const { + return "Tony Engine"; + } + + virtual const char *getOriginalCopyright() const { + return "Tony Engine (C) Protonic Interactive"; + } + + virtual bool hasFeature(MetaEngineFeature f) const; + virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const; + virtual SaveStateList listSaves(const char *target) const; + virtual int getMaximumSaveSlot() const; + virtual void removeSaveState(const char *target, int slot) const; + SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const; +}; + +bool TonyMetaEngine::hasFeature(MetaEngineFeature f) const { + return + (f == kSupportsListSaves) || + (f == kSupportsLoadingDuringStartup) || + (f == kSupportsDeleteSave) || + (f == kSavesSupportMetaInfo) || + (f == kSavesSupportThumbnail); +} + +bool Tony::TonyEngine::hasFeature(EngineFeature f) const { + return + (f == kSupportsRTL) || + (f == kSupportsLoadingDuringRuntime) || + (f == kSupportsSavingDuringRuntime); +} + +bool TonyMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const { + const Tony::TonyGameDescription *gd = (const Tony::TonyGameDescription *)desc; + if (gd) { + *engine = new Tony::TonyEngine(syst, gd); + } + return gd != 0; +} + +SaveStateList TonyMetaEngine::listSaves(const char *target) const { + Common::SaveFileManager *saveFileMan = g_system->getSavefileManager(); + Common::StringArray filenames; + Common::String saveDesc; + Common::String pattern = "tony.0??"; + + filenames = saveFileMan->listSavefiles(pattern); + sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..) + + SaveStateList saveList; + for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) { + // Obtain the last 3 digits of the filename, since they correspond to the save slot + int slotNum = atoi(file->c_str() + file->size() - 3); + + if (slotNum >= 0 && slotNum <= 999) { + byte thumbnailData[160 * 120 * 2]; + Common::String saveName; + byte difficulty; + + if (Tony::RMOptionScreen::loadThumbnailFromSaveState(slotNum, thumbnailData, saveName, difficulty)) { + // Add the save name to the savegame list + saveList.push_back(SaveStateDescriptor(slotNum, saveName)); + } + } + } + + return saveList; +} + +int TonyMetaEngine::getMaximumSaveSlot() const { + return 99; +} + +void TonyMetaEngine::removeSaveState(const char *target, int slot) const { + Common::String filename = Tony::TonyEngine::getSaveStateFileName(slot); + + g_system->getSavefileManager()->removeSavefile(filename); +} + +SaveStateDescriptor TonyMetaEngine::querySaveMetaInfos(const char *target, int slot) const { + Common::String saveName; + byte difficulty; + byte thumbData[160 * 120 * 2]; + + if (Tony::RMOptionScreen::loadThumbnailFromSaveState(slot, thumbData, saveName, difficulty)) { + // Convert the 565 thumbnail data to the needed overlay format + Common::MemoryReadStream thumbStream(thumbData, 160 * 120 * 2); + Graphics::PixelFormat destFormat = g_system->getOverlayFormat(); + Graphics::Surface *to = new Graphics::Surface(); + to->create(160, 120, destFormat); + + OverlayColor *pixels = (OverlayColor *)to->pixels; + for (int y = 0; y < to->h; ++y) { + for (int x = 0; x < to->w; ++x) { + uint8 r, g, b; + Graphics::colorToRGB<Graphics::ColorMasks<555> >(thumbStream.readUint16LE(), r, g, b); + + // converting to current OSystem Color + *pixels++ = destFormat.RGBToColor(r, g, b); + } + } + + // Create the return descriptor + SaveStateDescriptor desc(slot, saveName); + desc.setDeletableFlag(true); + desc.setWriteProtectedFlag(false); + desc.setThumbnail(to); + + return desc; + } + + return SaveStateDescriptor(); +} + +#if PLUGIN_ENABLED_DYNAMIC(TONY) +REGISTER_PLUGIN_DYNAMIC(TONY, PLUGIN_TYPE_ENGINE, TonyMetaEngine); +#else +REGISTER_PLUGIN_STATIC(TONY, PLUGIN_TYPE_ENGINE, TonyMetaEngine); +#endif diff --git a/engines/tony/detection_tables.h b/engines/tony/detection_tables.h new file mode 100644 index 0000000000..ee137927dc --- /dev/null +++ b/engines/tony/detection_tables.h @@ -0,0 +1,202 @@ +/* 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. + * + */ + +namespace Tony { + +static const TonyGameDescription gameDescriptions[] = { + { + // Tony Tough English + { + "tony", + 0, + { + // TODO: AdvancedDetector seems to have a problem where it thinks data1.cab is unrecognized. + // Is it perhaps because the Agos engine also has detection entries for data1.cab? + {"data1.cab", 0, "ce82907242166bfb594d97bdb68f96d2", 4350}, + /*{"roasted.mpr", 0, "06203dbbc85fdd1e6dc8fc211c1a6207", 135911071}, + {"roasted.mpc", 0, "57c4a3860cf899443c357e0078ea6f49", 366773},*/ + AD_LISTEND + }, + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_NO_FLAGS, + GUIO1(GUIO_NONE) + }, + }, + + { + // Tony Tough English Demo + { + "tony", + "Extracted Demo", + { + {"roasted.mpr", 0, "06203dbbc85fdd1e6dc8fc211c1a6207", 14972409}, + {"roasted.mpc", 0, "1e247922ec869712bfd96625bc4d3c7c", 39211}, + AD_LISTEND + }, + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_DEMO, + GUIO1(GUIO_NONE) + }, + }, + + { + // Tony Tough English Demo (Compressed) + { + "tony", + "Demo", + { + {"data1.cab", 0, "7d8b6d308f96aee3968ad7910fb11e6d", 58660608}, + AD_LISTEND + }, + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_DEMO | GF_COMPRESSED, + GUIO1(GUIO_NONE) + }, + }, + + { + // Tony Tough French "Collection Aventure" provided by Strangerke + { + "tony", + 0, + { + {"roasted.mpr", 0, "06203dbbc85fdd1e6dc8fc211c1a6207", 135911071}, + {"roasted.mpc", 0, "e890c6a41238827bdfa9874a65618b69", 374135}, + AD_LISTEND + }, + Common::FR_FRA, + Common::kPlatformWindows, + ADGF_NO_FLAGS, + GUIO1(GUIO_NONE) + }, + }, + + { + // Tony Tough German "Shoe Box" provided by Strangerke + { + "tony", + 0, + { + {"roasted.mpr", 0, "06203dbbc85fdd1e6dc8fc211c1a6207", 135911071}, + {"roasted.mpc", 0, "ccf7ab939a34de1b13df538596431684", 389554}, + AD_LISTEND + }, + Common::DE_DEU, + Common::kPlatformWindows, + ADGF_NO_FLAGS, + GUIO1(GUIO_NONE) + }, + }, + + { + // Tony Tough Italian provided by Fabio Barzagli + { + "tony", + 0, + { + {"roasted.mpr", 0, "06203dbbc85fdd1e6dc8fc211c1a6207", 135911071}, + {"roasted.mpc", 0, "1dc896cdb945170d7408598f803411c1", 380001}, + AD_LISTEND + }, + Common::IT_ITA, + Common::kPlatformWindows, + ADGF_NO_FLAGS, + GUIO1(GUIO_NONE) + }, + }, + + { + // Tony Tough Italian provided by Giovanni Bajo + { + "tony", + 0, + { + {"roasted.mpr", 0, "06203dbbc85fdd1e6dc8fc211c1a6207", 135911071}, + {"roasted.mpc", 0, "6202816f991b15af82aab84e3e4be011", 380183}, + AD_LISTEND + }, + Common::IT_ITA, + Common::kPlatformWindows, + ADGF_NO_FLAGS, + GUIO1(GUIO_NONE) + }, + }, + + { + // Tony Tough Polish provided by Fabio Barzagli + { + "tony", + 0, + { + {"roasted.mpr", 0, "06203dbbc85fdd1e6dc8fc211c1a6207", 135911071}, + {"roasted.mpc", 0, "89733ea710669acc8e7900b115f4afef", 389625}, + AD_LISTEND + }, + Common::PL_POL, + Common::kPlatformWindows, + ADGF_NO_FLAGS, + GUIO1(GUIO_NONE) + }, + }, + + { + // Tony Tough German "Gamestar" provided in bug #3566035 + { + "tony", + 0, + { + {"roasted.mpr", 0, "06203dbbc85fdd1e6dc8fc211c1a6207", 135911071}, + {"roasted.mpc", 0, "187de6f88f4083808cb66342ab55a7fd", 389904}, + AD_LISTEND + }, + Common::DE_DEU, + Common::kPlatformWindows, + ADGF_NO_FLAGS, + GUIO1(GUIO_NONE) + }, + }, + + { + // Tony Tough Czech provided in bug #3565765 + { + "tony", + 0, + { + // {"data1.cab", 0, "c6d5dd8f0c1241a6e3f7861b7f27bf7b", 4350}, + {"roasted.mpr", 0, "06203dbbc85fdd1e6dc8fc211c1a6207", 135911071}, + {"roasted.mpc", 0, "a8283a101878f3ca105f1f83f07e2c40", 386491}, + AD_LISTEND + }, + Common::CZ_CZE, + Common::kPlatformWindows, + ADGF_NO_FLAGS, + GUIO1(GUIO_NONE) + }, + }, + + { AD_TABLE_END_MARKER } +}; + +} // End of namespace Tony diff --git a/engines/tony/font.cpp b/engines/tony/font.cpp new file mode 100644 index 0000000000..1729052d42 --- /dev/null +++ b/engines/tony/font.cpp @@ -0,0 +1,1169 @@ +/* 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. + * + */ + +/* + * This code is based on original Tony Tough source code + * + * Copyright (c) 1997-2003 Nayma Software + */ + +#include "common/textconsole.h" +#include "tony/mpal/mpalutils.h" +#include "tony/font.h" +#include "tony/input.h" +#include "tony/inventory.h" +#include "tony/loc.h" +#include "tony/tony.h" + +namespace Tony { + +/****************************************************************************\ +* RMFont Methods +\****************************************************************************/ + +RMFont::RMFont() { + _letter = NULL; + _nLetters = _fontDimx = _fontDimy = _dimx = _dimy = 0; +} + +RMFont::~RMFont() { + unload(); +} + +void RMFont::load(const byte *buf, int nChars, int dimx, int dimy, uint32 palResID) { + _letter = new RMGfxSourceBuffer8RLEByte[nChars]; + + // Initialize the fonts + for (int i = 0; i < nChars; i++) { + // Initialize the buffer with the letters + _letter[i].init(buf + i * (dimx * dimy + 8) + 8, dimx, dimy); + _letter[i].loadPaletteWA(palResID); + } + + _fontDimx = dimx; + _fontDimy = dimy; + + _nLetters = nChars; +} + +void RMFont::load(uint32 resID, int nChars, int dimx, int dimy, uint32 palResID) { + RMRes res(resID); + + if ((int)res.size() < nChars * (dimy * dimx + 8)) + nChars = res.size() / (dimy * dimx + 8); + + load(res, nChars, dimx, dimy, palResID); +} + +void RMFont::unload() { + if (_letter != NULL) { + delete[] _letter; + _letter = NULL; + } +} + +RMGfxPrimitive *RMFont::makeLetterPrimitive(byte bChar, int &nLength) { + RMFontPrimitive *prim; + + // Convert from character to glyph index + int nLett = convertToLetter(bChar); + assert(nLett < _nLetters); + + // Create primitive font + prim = new RMFontPrimitive(this); + prim->_nChar = nLett; + + // Get the length of the character in pixels + nLength = letterLength(bChar); + + return prim; +} + +void RMFont::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim2) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + RMFontPrimitive *prim = (RMFontPrimitive *)prim2; + + CORO_BEGIN_CODE(_ctx); + + // Call the draw method of the letter assigned to the primitive + if (prim->_nChar != -1) + CORO_INVOKE_2(_letter[prim->_nChar].draw, bigBuf, prim); + + CORO_END_CODE; +} + +void RMFont::close() { + unload(); +} + +int RMFont::stringLen(const Common::String &text) { + if (text.empty()) + return letterLength('\0'); + + uint len = 0; + uint i; + for (i = 0; i < text.size() - 1; i++) + len += letterLength(text[i], text[i + 1]); + len += letterLength(text[i]); + + return len; +} + +int RMFont::stringLen(char bChar, char bNext) { + return letterLength(bChar, bNext); +} + +/****************************************************************************\ +* RMFontColor Methods +\****************************************************************************/ + +RMFontColor::RMFontColor() : RMFont() { + _fontR = _fontG = _fontB = 255; +} + +RMFontColor::~RMFontColor() { +} + +void RMFontColor::setBaseColor(byte r1, byte g1, byte b1) { + int r = (int)r1 << 16; + int g = (int)g1 << 16; + int b = (int)b1 << 16; + + int rstep = r / 14; + int gstep = g / 14; + int bstep = b / 14; + + byte pal[768 * 3]; + + // Check if we are already on the right color + if (_fontR == r1 && _fontG == g1 && _fontB == b1) + return; + + _fontR = r1; + _fontG = g1; + _fontB = b1; + + // Constructs a new palette for the font + for (int i = 1; i < 16; i++) { + pal[i * 3 + 0] = r >> 16; + pal[i * 3 + 1] = g >> 16; + pal[i * 3 + 2] = b >> 16; + + r -= rstep; + g -= gstep; + b -= bstep; + } + + pal[15 * 3 + 0] += 8; + pal[15 * 3 + 1] += 8; + pal[15 * 3 + 2] += 8; + + // Puts in all the letters + for (int i = 0; i < _nLetters; i++) + _letter[i].loadPaletteWA(pal); +} + +/***************************************************************************\ +* RMFontWithTables Methods +\****************************************************************************/ +int RMFontWithTables::convertToLetter(byte nChar) { + return _cTable[nChar]; +} + +int RMFontWithTables::letterLength(int nChar, int nNext) { + return (nChar != -1 ? _lTable[(byte)nChar] + _l2Table[(byte)nChar][(byte)nNext] : _lDefault); +} + +/***************************************************************************\ +* RMFontDialog Methods +\****************************************************************************/ + +void RMFontDialog::init() { + // bernie: Number of characters in the font + int nchars = + 112 // base + + 18 // polish + + 66 // russian + + 30 // czech + + 8 // french + + 5; // deutsch + + load(RES_F_PARL, nchars, 20, 20); + + // Initialize the font table + _lDefault = 13; + _hDefault = 18; + Common::fill(&_l2Table[0][0], &_l2Table[0][0] + (256 * 256), '\0'); + + for (int i = 0; i < 256; i++) { + _cTable[i] = g_vm->_cTableDialog[i]; + _lTable[i] = g_vm->_lTableDialog[i]; + } +} + +/***************************************************************************\ +* RMFontMacc Methods +\****************************************************************************/ + +void RMFontMacc::init() { + // bernie: Number of characters in the font + int nchars = + 102 // base + + 18 // polish + + 66 // russian + + 30 // czech + + 8 // francais + + 5; // deutsch + + load(RES_F_MACC, nchars, 11, 16); + + // Default + _lDefault = 10; + _hDefault = 17; + Common::fill(&_l2Table[0][0], &_l2Table[0][0] + (256 * 256), '\0'); + + for (int i = 0; i < 256; i++) { + _cTable[i] = g_vm->_cTableMacc[i]; + _lTable[i] = g_vm->_lTableMacc[i]; + } +} + +/***************************************************************************\ +* RMFontCredits Methods +\****************************************************************************/ + +void RMFontCredits::init() { + // bernie: Number of characters in the font + int nchars = + 112 // base + + 18 // polish + + 66 // russian + + 30 // czech + + 8 // french + + 2; // deutsch + + load(RES_F_CREDITS, nchars, 27, 28, RES_F_CPAL); + + // Default + _lDefault = 10; + _hDefault = 28; + Common::fill(&_l2Table[0][0], &_l2Table[0][0] + (256 * 256), '\0'); + + for (int i = 0; i < 256; i++) { + _cTable[i] = g_vm->_cTableCred[i]; + _lTable[i] = g_vm->_lTableCred[i]; + } +} + +/***************************************************************************\ +* RMFontObj Methods +\****************************************************************************/ + +#define TOUPPER(a) ((a) >= 'a' && (a) <= 'z' ? (a) + 'A' - 'a' : (a)) +#define TOLOWER(a) ((a) >= 'A' && (a) <= 'Z' ? (a) + 'a' - 'A' : (a)) + +void RMFontObj::setBothCase(int nChar, int nNext, signed char spiazz) { + _l2Table[TOUPPER(nChar)][TOUPPER(nNext)] = spiazz; + _l2Table[TOUPPER(nChar)][TOLOWER(nNext)] = spiazz; + _l2Table[TOLOWER(nChar)][TOUPPER(nNext)] = spiazz; + _l2Table[TOLOWER(nChar)][TOLOWER(nNext)] = spiazz; +} + +void RMFontObj::init() { + //bernie: Number of characters in the font (solo maiuscolo) + int nchars = + 85 // base + + 9 // polish + + 33 // russian + + 15 // czech + + 0 // francais (no uppercase chars) + + 1; // deutsch + + load(RES_F_OBJ, nchars, 25, 30); + + // Initialize the font table + _lDefault = 26; + _hDefault = 30; + Common::fill(&_l2Table[0][0], &_l2Table[0][0] + (256 * 256), '\0'); + + for (int i = 0; i < 256; i++) { + _cTable[i] = g_vm->_cTableObj[i]; + _lTable[i] = g_vm->_lTableObj[i]; + } + + // Special case + setBothCase('C', 'C', 2); + setBothCase('A', 'T', -2); + setBothCase('R', 'S', 2); + setBothCase('H', 'I', -2); + setBothCase('T', 'S', 2); + setBothCase('O', 'R', 2); + setBothCase('O', 'L', 2); + setBothCase('O', 'G', 2); + setBothCase('Z', 'A', -1); + setBothCase('R', 'R', 1); + setBothCase('R', 'U', 3); +} + +/****************************************************************************\ +* RMText Methods +\****************************************************************************/ + +RMFontColor *RMText::_fonts[4] = { NULL, NULL, NULL, NULL }; + +void RMText::initStatics() { + Common::fill(&_fonts[0], &_fonts[4], (RMFontColor *)NULL); +} + +RMText::RMText() { + // Default color: white + _textR = _textG = _textB = 255; + + // Default length + _maxLineLength = 350; + + _bTrasp0 = true; + _aHorType = HCENTER; + _aVerType = VTOP; + setPriority(150); +} + +RMText::~RMText() { +} + +void RMText::unload() { + if (_fonts[0] != NULL) { + delete _fonts[0]; + delete _fonts[1]; + delete _fonts[2]; + delete _fonts[3]; + _fonts[0] = _fonts[1] = _fonts[2] = _fonts[3] = 0; + } +} + +void RMText::setMaxLineLength(int max) { + _maxLineLength = max; +} + +void RMText::removeThis(CORO_PARAM, bool &result) { + // Here we can do checks on the number of frames, time spent, etc. + result = true; +} + +void RMText::writeText(const Common::String &text, int nFont, int *time) { + // Initializes the font (only once) + if (_fonts[0] == NULL) { + _fonts[0] = new RMFontDialog; + _fonts[0]->init(); + _fonts[1] = new RMFontObj; + _fonts[1]->init(); + _fonts[2] = new RMFontMacc; + _fonts[2]->init(); + _fonts[3] = new RMFontCredits; + _fonts[3]->init(); + } + + writeText(text, _fonts[nFont], time); +} + +void RMText::writeText(Common::String text, RMFontColor *font, int *time) { + RMGfxPrimitive *prim; + + // Set the base color + font->setBaseColor(_textR, _textG, _textB); + + // Destroy the buffer before starting + destroy(); + + // If the string is empty, do nothing + if (text.empty()) + return; + + // Divide the words into lines. In this cycle, X contains the maximum length reached by a line, + // and the number of lines + Common::Array<Common::String> lines; + uint p = 0; + int j = 0; + int x = 0; + while (p < text.size()) { + j += font->stringLen(text[p]); + if (j > (((_aHorType == HLEFTPAR) && (lines.size() > 0)) ? _maxLineLength - 25 : _maxLineLength)) { + j -= font->stringLen(text[p], (p + 1 == text.size()) ? '\0' : text[p + 1]); + if (j > x) + x = j; + + // Back to the first usable space + // + // BERNIE: In the original, sentences containing words that exceed the + // width of a line caused discontinuation of the whole sentence. + // This workaround has the partial word broken up so it will still display + // + uint old_p = p; + while (text[p] != ' ' && text[p] != '-' && p > 0) + p--; + + if (p == 0) + p = old_p; + + // Check if there are any blanks to end + while ((text[p] == ' ' || text[p] == '-') && p + 1 < text.size()) + p++; + if (p == text.size()) + break; + lines.push_back(Common::String(text.c_str(), p)); + if (text[p] == ' ') + p++; + text = text.c_str() + p; + p = 0; + j = 0; + continue; + } + p++; + } + + if (j > x) + x = j; + + // Add the last line of text. + lines.push_back(text); + + x += 8; + + // Starting position for the surface: X1, Y + int width = x; + int height = (lines.size() - 1) * font->letterHeight() + font->_fontDimy; + + // Create the surface + create(width, height); + Common::fill(_buf, _buf + width * height * 2, 0); + + p = 0; + + int y = 0; + int numchar = 0; + for (uint i = 0; i < lines.size(); ++i) { + const Common::String &line = lines[i]; + + // Measure the length of the line + x = 0; + j = font->stringLen(line); + + switch (_aHorType) { + case HLEFT: + x = 0; + break; + + case HLEFTPAR: + if (i == 0) + x = 0; + else + x = 25; + break; + + case HCENTER: + x = width / 2 - j / 2; + break; + + case HRIGHT: + x = width - j - 1; + break; + } + + p = 0; + while (p < line.size()) { + if (line[p] == ' ') { + x += font->stringLen(line[p]); + p++; + continue; + } + + int len; + prim = font->makeLetterPrimitive(line[p], len); + prim->getDst()._x1 = x; + prim->getDst()._y1 = y; + addPrim(prim); + + numchar++; + + x += font->stringLen(line[p], (p + 1 == line.size()) ? '\0' : line[p + 1]); + p++; + } + p++; + y += font->letterHeight(); + } + + if (time != NULL) + *time = 1000 + numchar * (11 - GLOBALS._nCfgTextSpeed) * 14; +} + +void RMText::clipOnScreen(RMGfxPrimitive *prim) { + // Don't let it go outside the screen + if (prim->getDst()._x1 < 5) + prim->getDst()._x1 = 5; + if (prim->getDst()._y1 < 5) + prim->getDst()._y1 = 5; + if (prim->getDst()._x1 + _dimx > 635) + prim->getDst()._x1 = 635 - _dimx; + if (prim->getDst()._y1 + _dimy > 475) + prim->getDst()._y1 = 475 - _dimy; +} + +void RMText::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + // Horizontally + if (_aHorType == HCENTER) + prim->getDst().topLeft() -= RMPoint(_dimx / 2, 0); + else if (_aHorType == HRIGHT) + prim->getDst().topLeft() -= RMPoint(_dimx, 0); + + // Vertically + if (_aVerType == VTOP) { + + } else if (_aVerType == VCENTER) { + prim->getDst()._y1 -= _dimy / 2; + + } else if (_aVerType == VBOTTOM) { + prim->getDst()._y1 -= _dimy; + } + + clipOnScreen(prim); + + CORO_INVOKE_2(RMGfxWoodyBuffer::draw, bigBuf, prim); + + CORO_END_CODE; +} + +/** + * Set the alignment type + */ +void RMText::setAlignType(HorAlign aHor, VerAlign aVer) { + _aHorType = aHor; + _aVerType = aVer; +} + +/** + * Set the base color + */ +void RMText::setColor(byte r, byte g, byte b) { + _textR = r; + _textG = g; + _textB = b; +} + +/****************************************************************************\ +* RMTextDialog Methods +\****************************************************************************/ + +RMTextDialog::RMTextDialog() : RMText() { + _time = _startTime = 0; + _dst = RMPoint(0, 0); + + _bSkipStatus = true; + _bShowed = true; + _bForceTime = false; + _bForceNoTime = false; + _bAlwaysDisplay = false; + _bNoTab = false; + _hCustomSkip = CORO_INVALID_PID_VALUE; + _hCustomSkip2 = CORO_INVALID_PID_VALUE; + _input = NULL; + + // Create the event for displaying the end + _hEndDisplay = CoroScheduler.createEvent(false, false); +} + +RMTextDialog::~RMTextDialog() { + CoroScheduler.closeEvent(_hEndDisplay); +} + +void RMTextDialog::show() { + _bShowed = true; +} + +void RMTextDialog::hide(CORO_PARAM) { + _bShowed = false; +} + +void RMTextDialog::writeText(const Common::String &text, int font, int *time) { + RMText::writeText(text, font, &_time); + + if (time != NULL) + *time = _time; +} + +void RMTextDialog::writeText(const Common::String &text, RMFontColor *font, int *time) { + RMText::writeText(text, font, &_time); + + if (time != NULL) + *time = _time; +} + +void RMTextDialog::setSkipStatus(bool bEnabled) { + _bSkipStatus = bEnabled; +} + +void RMTextDialog::forceTime() { + _bForceTime = true; +} + +void RMTextDialog::forceNoTime() { + _bForceNoTime = true; +} + +void RMTextDialog::setNoTab() { + _bNoTab = true; +} + +void RMTextDialog::setForcedTime(uint32 dwTime) { + _time = dwTime; +} + +void RMTextDialog::setAlwaysDisplay() { + _bAlwaysDisplay = true; +} + +void RMTextDialog::removeThis(CORO_PARAM, bool &result) { + CORO_BEGIN_CONTEXT; + bool expired; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + // Presume successful result + result = true; + + // Don't erase the background + if (_bSkipStatus) { + if (!(GLOBALS._bCfgDubbing && _hCustomSkip2 != CORO_INVALID_PID_VALUE)) { + if (GLOBALS._bCfgTimerizedText) { + if (!_bForceNoTime) { + if (g_vm->getTime() > (uint32)_time + _startTime) + return; + } + } + } + + if (!_bNoTab) { + if (g_vm->getEngine()->getInput().getAsyncKeyState(Common::KEYCODE_TAB)) + return; + } + + if (!_bNoTab) { + if (_input) { + if (_input->mouseLeftClicked() || _input->mouseRightClicked()) + return; + } + } + } + + // Erase the background + else if (!(GLOBALS._bCfgDubbing && _hCustomSkip2 != CORO_INVALID_PID_VALUE)) { + if (!_bForceNoTime) { + if (g_vm->getTime() > (uint32)_time + _startTime) + return; + } + } + + // If time is forced + if (_bForceTime) { + if (g_vm->getTime() > (uint32)_time + _startTime) + return; + } + + if (_hCustomSkip != CORO_INVALID_PID_VALUE) { + CORO_INVOKE_3(CoroScheduler.waitForSingleObject, _hCustomSkip, 0, &_ctx->expired); + // == WAIT_OBJECT_0 + if (!_ctx->expired) + return; + } + + if (GLOBALS._bCfgDubbing && _hCustomSkip2 != CORO_INVALID_PID_VALUE) { + CORO_INVOKE_3(CoroScheduler.waitForSingleObject, _hCustomSkip2, 0, &_ctx->expired); + // == WAIT_OBJECT_0 + if (!_ctx->expired) + return; + } + + result = false; + + CORO_END_CODE; +} + +void RMTextDialog::unregister() { + RMGfxTask::unregister(); + assert(_nInList == 0); + CoroScheduler.setEvent(_hEndDisplay); +} + +void RMTextDialog::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + if (_startTime == 0) + _startTime = g_vm->getTime(); + + if (_bShowed) { + if (GLOBALS._bShowSubtitles || _bAlwaysDisplay) { + prim->getDst().topLeft() = _dst; + CORO_INVOKE_2(RMText::draw, bigBuf, prim); + } + } + + CORO_END_CODE; +} + +void RMTextDialog::setCustomSkipHandle(uint32 hCustom) { + _hCustomSkip = hCustom; +} + +void RMTextDialog::setCustomSkipHandle2(uint32 hCustom) { + _hCustomSkip2 = hCustom; +} + +void RMTextDialog::waitForEndDisplay(CORO_PARAM) { + CoroScheduler.waitForSingleObject(coroParam, _hEndDisplay, CORO_INFINITE); +} + +void RMTextDialog::setInput(RMInput *input) { + _input = input; +} + +/** + * Set the position + */ +void RMTextDialog::setPosition(const RMPoint &pt) { + _dst = pt; +} + +/****************************************************************************\ +* RMTextDialogScrolling Methods +\****************************************************************************/ + +RMTextDialogScrolling::RMTextDialogScrolling() { + _curLoc = NULL; +} + +RMTextDialogScrolling::RMTextDialogScrolling(RMLocation *loc) { + _curLoc = loc; + _startScroll = loc->scrollPosition(); +} + +RMTextDialogScrolling::~RMTextDialogScrolling() { +} + +void RMTextDialogScrolling::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) { + CORO_BEGIN_CONTEXT; + RMPoint curDst; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + _ctx->curDst = _dst; + + if (_curLoc != NULL) + _dst -= _curLoc->scrollPosition() - _startScroll; + + CORO_INVOKE_2(RMTextDialog::draw, bigBuf, prim); + + _dst = _ctx->curDst; + + CORO_END_CODE; +} + +void RMTextDialogScrolling::clipOnScreen(RMGfxPrimitive *prim) { + // We must not do anything! +} + +/****************************************************************************\ +* RMTextItemName Methods +\****************************************************************************/ + +RMTextItemName::RMTextItemName() : RMText() { + _item = NULL; + setPriority(220); +} + +RMTextItemName::~RMTextItemName() { +} + +void RMTextItemName::doFrame(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMLocation &loc, RMPointer &ptr, RMInventory &inv) { + CORO_BEGIN_CONTEXT; + RMItem *lastItem; + uint32 hThread; + CORO_END_CONTEXT(_ctx); + + Common::String itemName; + + CORO_BEGIN_CODE(_ctx); + + _ctx->lastItem = _item; + + // Adds to the list if there is need + if (!_nInList) + bigBuf.addPrim(new RMGfxPrimitive(this)); + + // Update the scrolling co-ordinates + _curscroll = loc.scrollPosition(); + + // Check if we are on the inventory + if (inv.itemInFocus(_mpos)) + _item = inv.whichItemIsIn(_mpos); + else + _item = loc.whichItemIsIn(_mpos); + + // If there an item, get its name + if (_item != NULL) + _item->getName(itemName); + + // Write it + writeText(itemName, 1); + + // Handle the change If the selected item is different from the previous one + if (_ctx->lastItem != _item) { + if (_item == NULL) + ptr.setSpecialPointer(RMPointer::PTR_NONE); + else { + _ctx->hThread = mpalQueryDoAction(20, _item->mpalCode(), 0); + if (_ctx->hThread == CORO_INVALID_PID_VALUE) + ptr.setSpecialPointer(RMPointer::PTR_NONE); + else + CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _ctx->hThread, CORO_INFINITE); + } + } + + CORO_END_CODE; +} + +void RMTextItemName::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + // If there is no text, it's pointless to continue + if (_buf == NULL) + return; + + // Set the destination coordinates of the mouse + prim->getDst().topLeft() = _mpos - RMPoint(0, 30); + + CORO_INVOKE_2(RMText::draw, bigBuf, prim); + + CORO_END_CODE; +} + +RMPoint RMTextItemName::getHotspot() { + if (_item == NULL) + return _mpos + _curscroll; + else + return _item->getHotspot(); +} + +RMItem *RMTextItemName::getSelectedItem() { + return _item; +} + +bool RMTextItemName::isItemSelected() { + return _item != NULL; +} + +void RMTextItemName::setMouseCoord(const RMPoint &m) { + _mpos = m; +} + +void RMTextItemName::removeThis(CORO_PARAM, bool &result) { + result = true; +} + +/****************************************************************************\ +* RMDialogChoice Methods +\****************************************************************************/ + +RMDialogChoice::RMDialogChoice() { + RMResRaw dlg1(RES_I_DLGTEXT); + RMResRaw dlg2(RES_I_DLGTEXTLINE); + RMRes dlgpal(RES_I_DLGTEXTPAL); + + _dlgText.init(dlg1, dlg1.width(), dlg1.height()); + _dlgTextLine.init(dlg2, dlg2.width(), dlg2.height()); + + _dlgText.loadPaletteWA(dlgpal); + _dlgTextLine.loadPaletteWA(dlgpal); + + _hUnreg = CoroScheduler.createEvent(false, false); + _bRemoveFromOT = false; + + _curAdded = 0; + _bShow = false; +} + +RMDialogChoice::~RMDialogChoice() { + CoroScheduler.closeEvent(_hUnreg); +} + +void RMDialogChoice::unregister() { + RMGfxWoodyBuffer::unregister(); + assert(!_nInList); + CoroScheduler.pulseEvent(_hUnreg); + + _bRemoveFromOT = false; +} + +void RMDialogChoice::init() { + _numChoices = 0; + _drawedStrings = NULL; + _ptDrawStrings = NULL; + _curSelection = -1; + + create(640, 477); + setPriority(140); +} + +void RMDialogChoice::close() { + if (_drawedStrings != NULL) { + delete[] _drawedStrings; + _drawedStrings = NULL; + } + + if (_ptDrawStrings != NULL) { + delete[] _ptDrawStrings; + _ptDrawStrings = NULL; + } + + destroy(); +} + +void RMDialogChoice::setNumChoices(int num) { + _numChoices = num; + _curAdded = 0; + + // Allocate space for drawn strings + _drawedStrings = new RMText[num]; + _ptDrawStrings = new RMPoint[num]; + + // Initialization + for (int i = 0; i < _numChoices; i++) { + _drawedStrings[i].setColor(0, 255, 0); + _drawedStrings[i].setAlignType(RMText::HLEFTPAR, RMText::VTOP); + _drawedStrings[i].setMaxLineLength(600); + _drawedStrings[i].setPriority(10); + } +} + +void RMDialogChoice::addChoice(const Common::String &string) { + // Draw the string + assert(_curAdded < _numChoices); + _drawedStrings[_curAdded++].writeText(string, 0); +} + +void RMDialogChoice::prepare(CORO_PARAM) { + CORO_BEGIN_CONTEXT; + int i; + RMPoint ptPos; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + addPrim(new RMGfxPrimitive(&_dlgText, RMPoint(0, 0))); + addPrim(new RMGfxPrimitive(&_dlgTextLine, RMPoint(0, 155))); + addPrim(new RMGfxPrimitive(&_dlgTextLine, RMPoint(0, 155 + 83))); + addPrim(new RMGfxPrimitive(&_dlgTextLine, RMPoint(0, 155 + 83 + 83))); + addPrim(new RMGfxPrimitive(&_dlgTextLine, RMPoint(0, 155 + 83 + 83 + 83))); + + _ctx->ptPos.set(20, 90); + + for (_ctx->i = 0; _ctx->i < _numChoices; _ctx->i++) { + addPrim(new RMGfxPrimitive(&_drawedStrings[_ctx->i], _ctx->ptPos)); + _ptDrawStrings[_ctx->i] = _ctx->ptPos; + _ctx->ptPos.offset(0, _drawedStrings[_ctx->i].getDimy() + 15); + } + + CORO_INVOKE_0(drawOT); + clearOT(); + + _ptDrawPos.set(0, 480 - _ctx->ptPos._y); + + CORO_END_CODE; +} + +void RMDialogChoice::setSelected(CORO_PARAM, int pos) { + CORO_BEGIN_CONTEXT; + RMGfxBox box; + RMRect rc; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + if (pos == _curSelection) + return; + + _ctx->box.setPriority(5); + + if (_curSelection != -1) { + _ctx->box.setColor(0xCC, 0xCC, 0xFF); + _ctx->rc.topLeft() = RMPoint(18, _ptDrawStrings[_curSelection]._y); + _ctx->rc.bottomRight() = _ctx->rc.topLeft() + RMPoint(597, _drawedStrings[_curSelection].getDimy()); + addPrim(new RMGfxPrimitive(&_ctx->box, _ctx->rc)); + + addPrim(new RMGfxPrimitive(&_drawedStrings[_curSelection], _ptDrawStrings[_curSelection])); + CORO_INVOKE_0(drawOT); + clearOT(); + } + + if (pos != -1) { + _ctx->box.setColor(100, 100, 100); + _ctx->rc.topLeft() = RMPoint(18, _ptDrawStrings[pos]._y); + _ctx->rc.bottomRight() = _ctx->rc.topLeft() + RMPoint(597, _drawedStrings[pos].getDimy()); + addPrim(new RMGfxPrimitive(&_ctx->box, _ctx->rc)); + addPrim(new RMGfxPrimitive(&_drawedStrings[pos], _ptDrawStrings[pos])); + } + + CORO_INVOKE_0(drawOT); + clearOT(); + + _curSelection = pos; + + CORO_END_CODE; +} + +void RMDialogChoice::show(CORO_PARAM, RMGfxTargetBuffer *bigBuf) { + CORO_BEGIN_CONTEXT; + RMPoint destpt; + int deltay; + int starttime; + int elaps; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + CORO_INVOKE_0(prepare); + _bShow = false; + + if (!_nInList && bigBuf != NULL) + bigBuf->addPrim(new RMGfxPrimitive(this)); + + if (0) { + _bShow = true; + } else { + _ctx->starttime = g_vm->getTime(); + _ctx->deltay = 480 - _ptDrawPos._y; + _ctx->destpt = _ptDrawPos; + _ptDrawPos.set(0, 480); + + if (!_nInList && bigBuf != NULL) + bigBuf->addPrim(new RMGfxPrimitive(this)); + _bShow = true; + + _ctx->elaps = 0; + while (_ctx->elaps < 700) { + CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE); + _ctx->elaps = g_vm->getTime() - _ctx->starttime; + _ptDrawPos._y = 480 - ((_ctx->deltay * 100) / 700 * _ctx->elaps) / 100; + } + + _ptDrawPos._y = _ctx->destpt._y; + } + + CORO_END_CODE; +} + +void RMDialogChoice::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + if (_bShow == false) + return; + + prim->setDst(_ptDrawPos); + CORO_INVOKE_2(RMGfxSourceBuffer16::draw, bigBuf, prim); + + CORO_END_CODE; +} + +void RMDialogChoice::hide(CORO_PARAM) { + CORO_BEGIN_CONTEXT; + int deltay; + int starttime; + int elaps; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + if (1) { + _ctx->starttime = g_vm->getTime(); + + _ctx->deltay = 480 - _ptDrawPos._y; + _ctx->elaps = 0; + while (_ctx->elaps < 700) { + CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE); + _ctx->elaps = g_vm->getTime() - _ctx->starttime; + _ptDrawPos._y = 480 - ((_ctx->deltay * 100) / 700 * (700 - _ctx->elaps)) / 100; + } + } + + _bShow = false; + _bRemoveFromOT = true; + CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _hUnreg, CORO_INFINITE); + + CORO_END_CODE; +} + +void RMDialogChoice::removeThis(CORO_PARAM, bool &result) { + result = _bRemoveFromOT; +} + +void RMDialogChoice::doFrame(CORO_PARAM, RMPoint ptMousePos) { + CORO_BEGIN_CONTEXT; + int i; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + if (ptMousePos._y > _ptDrawPos._y) { + for (_ctx->i = 0; _ctx->i < _numChoices; _ctx->i++) { + if ((ptMousePos._y >= _ptDrawPos._y + _ptDrawStrings[_ctx->i]._y) && (ptMousePos._y < _ptDrawPos._y + _ptDrawStrings[_ctx->i]._y + _drawedStrings[_ctx->i].getDimy())) { + CORO_INVOKE_1(setSelected, _ctx->i); + break; + } + } + + if (_ctx->i == _numChoices) + CORO_INVOKE_1(setSelected, -1); + } + + CORO_END_CODE; +} + +int RMDialogChoice::getSelection() { + return _curSelection; +} + +} // End of namespace Tony diff --git a/engines/tony/font.h b/engines/tony/font.h new file mode 100644 index 0000000000..9ef50b99ec --- /dev/null +++ b/engines/tony/font.h @@ -0,0 +1,374 @@ +/* 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. + * + */ +/* + * This code is based on original Tony Tough source code + * + * Copyright (c) 1997-2003 Nayma Software + */ + +#ifndef TONY_FONT_H +#define TONY_FONT_H + +#include "common/system.h" +#include "common/coroutines.h" +#include "tony/gfxcore.h" +#include "tony/resid.h" + +namespace Tony { + +class RMInput; +class RMInventory; +class RMItem; +class RMLoc; +class RMLocation; +class RMPointer; + +/** + * Manages a font, in which there is a different surface for each letter + */ +class RMFont : public RMGfxTaskSetPrior { +protected: + int _nLetters; + RMGfxSourceBuffer8RLEByte *_letter; +public: + int _fontDimx, _fontDimy; + +private: + int _dimx, _dimy; + + class RMFontPrimitive : public RMGfxPrimitive { + public: + RMFontPrimitive() : RMGfxPrimitive() { _nChar = 0; } + RMFontPrimitive(RMGfxTask *task) : RMGfxPrimitive(task) { _nChar = 0; } + virtual ~RMFontPrimitive() { } + virtual RMGfxPrimitive *duplicate() { + return new RMFontPrimitive(*this); + } + + int _nChar; + }; + +protected: + // Loads the font + void load(uint32 resID, int nChars, int dimx, int dimy, uint32 palResID = RES_F_PAL); + void load(const byte *buf, int nChars, int dimx, int dimy, uint32 palResID = RES_F_PAL); + + // Remove the font + void unload(); + +protected: + // Conversion form character to font index + virtual int convertToLetter(byte nChar) = 0; + + // Character width + virtual int letterLength(int nChar, int nNext = 0) = 0; + +public: + virtual int letterHeight() = 0; + +public: + RMFont(); + virtual ~RMFont(); + + // Initialization and closing + virtual void init() = 0; + virtual void close(); + + // Drawing + virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBug, RMGfxPrimitive *prim); + + // Create a primitive for a letter + RMGfxPrimitive *makeLetterPrimitive(byte bChar, int &nLength); + + // Length in pixels of a string with the current font + int stringLen(const Common::String &text); + int stringLen(char bChar, char bNext = 0); +}; + +class RMFontColor : public virtual RMFont { +private: + byte _fontR, _fontG, _fontB; + +public: + RMFontColor(); + virtual ~RMFontColor(); + virtual void setBaseColor(byte r, byte g, byte b); +}; + +class RMFontWithTables : public virtual RMFont { +protected: + int _cTable[256]; + int _lTable[256]; + int _lDefault; + int _hDefault; + signed char _l2Table[256][256]; + +protected: + // Overloaded methods + int convertToLetter(byte nChar); + int letterLength(int nChar, int nNext = 0); + +public: + int letterHeight() { + return _hDefault; + } + virtual ~RMFontWithTables() {} +}; + +class RMFontDialog : public RMFontColor, public RMFontWithTables { +public: + void init(); + virtual ~RMFontDialog() {} +}; + +class RMFontObj : public RMFontColor, public RMFontWithTables { +private: + void setBothCase(int nChar, int nNext, signed char spiazz); + +public: + void init(); + virtual ~RMFontObj() {} +}; + +class RMFontMacc : public RMFontColor, public RMFontWithTables { +public: + void init(); + virtual ~RMFontMacc() {} +}; + +class RMFontCredits : public RMFontColor, public RMFontWithTables { +public: + void init(); + virtual ~RMFontCredits() {} + virtual void setBaseColor(byte r, byte g, byte b) {} +}; + +/** + * Manages writing text onto9 the screen + */ +class RMText : public RMGfxWoodyBuffer { +private: + static RMFontColor *_fonts[4]; + int _maxLineLength; + +public: + enum HorAlign { + HLEFT, + HLEFTPAR, + HCENTER, + HRIGHT + }; + + enum VerAlign { + VTOP, + VCENTER, + VBOTTOM + }; + +private: + HorAlign _aHorType; + VerAlign _aVerType; + byte _textR, _textG, _textB; + +protected: + virtual void clipOnScreen(RMGfxPrimitive *prim); + +public: + RMText(); + virtual ~RMText(); + static void initStatics(); + static void unload(); + + // Set the alignment type + void setAlignType(HorAlign aHor, VerAlign aVer); + + // Sets the maximum length of a line in pixels (used to format the text) + void setMaxLineLength(int max); + + // Write the text + void writeText(const Common::String &text, int font, int *time = NULL); + void writeText(Common::String text, RMFontColor *font, int *time = NULL); + + // Overloaded function to decide when you delete the object from the OT list + virtual void removeThis(CORO_PARAM, bool &result); + + // Overloading of the Draw to center the text, if necessary + virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim); + + // Set the base color + void setColor(byte r, byte g, byte b); +}; + +/** + * Manages text in a dialog + */ +class RMTextDialog : public RMText { +protected: + int _startTime; + int _time; + bool _bSkipStatus; + RMPoint _dst; + uint32 _hEndDisplay; + bool _bShowed; + bool _bForceTime; + bool _bForceNoTime; + uint32 _hCustomSkip; + uint32 _hCustomSkip2; + RMInput *_input; + bool _bAlwaysDisplay; + bool _bNoTab; + +public: + RMTextDialog(); + virtual ~RMTextDialog(); + + // Write the text + void writeText(const Common::String &text, int font, int *time = NULL); + void writeText(const Common::String &text, RMFontColor *font, int *time = NULL); + + // Overloaded function to decide when you delete the object from the OT list + virtual void removeThis(CORO_PARAM, bool &result); + + // Overloaded de-registration + virtual void unregister(); + + // Overloading of the Draw to center the text, if necessary + virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim); + + // Set the position + void setPosition(const RMPoint &pt); + + // Waiting + void waitForEndDisplay(CORO_PARAM); + void setCustomSkipHandle(uint32 hCustomSkip); + void setCustomSkipHandle2(uint32 hCustomSkip); + void setSkipStatus(bool bEnabled); + void setForcedTime(uint32 dwTime); + void setNoTab(); + void forceTime(); + void forceNoTime(); + void setAlwaysDisplay(); + + // Set the input device, to allow skip from mouse + void setInput(RMInput *input); + + void show(); + void hide(CORO_PARAM); +}; + +class RMTextDialogScrolling : public RMTextDialog { +protected: + RMLocation *_curLoc; + RMPoint _startScroll; + + virtual void clipOnScreen(RMGfxPrimitive *prim); + +public: + RMTextDialogScrolling(); + RMTextDialogScrolling(RMLocation *loc); + virtual ~RMTextDialogScrolling(); + + virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim); +}; + +/** + * Manages the name of a selected item on the screen + */ +class RMTextItemName : protected RMText { +protected: + RMPoint _mpos; + RMPoint _curscroll; + RMItem *_item; + +public: + RMTextItemName(); + virtual ~RMTextItemName(); + + void setMouseCoord(const RMPoint &m); + + void doFrame(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMLocation &loc, RMPointer &ptr, RMInventory &inv); + virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim); + + RMPoint getHotspot(); + RMItem *getSelectedItem(); + bool isItemSelected(); + + virtual void removeThis(CORO_PARAM, bool &result); +}; + +/** + * Manages the selection of screen items in a box + */ +class RMDialogChoice : public RMGfxWoodyBuffer { +private: + int _curSelection; + int _numChoices; + RMText *_drawedStrings; + RMPoint *_ptDrawStrings; + int _curAdded; + bool _bShow; + RMGfxSourceBuffer8 _dlgText; + RMGfxSourceBuffer8 _dlgTextLine; + RMPoint _ptDrawPos; + uint32 _hUnreg; + bool _bRemoveFromOT; + +protected: + void prepare(CORO_PARAM); + void setSelected(CORO_PARAM, int pos); + +public: + virtual void removeThis(CORO_PARAM, bool &result); + virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim); + void unregister(); + +public: + // Initialization + RMDialogChoice(); + virtual ~RMDialogChoice(); + + // Initialization and closure + void init(); + void close(); + + // Sets the number of possible sentences, which then be added with AddChoice() + void setNumChoices(int num); + + // Adds a string with the choice + void addChoice(const Common::String &string); + + // Show and hide the selection, with possible animations. + // NOTE: If no parameter is passed to Show(), it is the obligation of + // caller to ensure that the class is inserted into OT list + void show(CORO_PARAM, RMGfxTargetBuffer *bigBuf = NULL); + void hide(CORO_PARAM); + + // Polling Update + void doFrame(CORO_PARAM, RMPoint ptMousePos); + + // Returns the currently selected item, or -1 if none is selected + int getSelection(); +}; + +} // End of namespace Tony + +#endif diff --git a/engines/tony/game.cpp b/engines/tony/game.cpp new file mode 100644 index 0000000000..501a588ff5 --- /dev/null +++ b/engines/tony/game.cpp @@ -0,0 +1,1600 @@ +/* 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. + * + */ + +/* + * This code is based on original Tony Tough source code + * + * Copyright (c) 1997-2003 Nayma Software + */ + +#include "common/file.h" +#include "common/savefile.h" +#include "common/textconsole.h" +#include "graphics/cursorman.h" +#include "tony/mpal/lzo.h" +#include "tony/mpal/memory.h" +#include "tony/mpal/mpal.h" +#include "tony/mpal/mpalutils.h" +#include "tony/game.h" +#include "tony/gfxengine.h" +#include "tony/tony.h" + +namespace Tony { + +using namespace MPAL; + +// Global functions +void mainEnableGUI() { + g_vm->getEngine()->_bGUIInterface = true; + g_vm->getEngine()->_bGUIInventory = true; + g_vm->getEngine()->_bGUIOption = true; +} + +void mainDisableGUI() { + g_vm->getEngine()->_bGUIInterface = false; + g_vm->getEngine()->_bGUIInventory = false; + g_vm->getEngine()->_bGUIOption = false; +} + +/****************************************************************************\ +* RMOptionButton Methods +\****************************************************************************/ + +RMOptionButton::RMOptionButton(uint32 dwRes, RMPoint pt, bool bDoubleState) { + RMResRaw raw(dwRes); + assert(raw.isValid()); + _buf = new RMGfxSourceBuffer16(false); + _buf->init(raw, raw.width(), raw.height()); + + _rect.setRect(pt._x, pt._y, pt._x + raw.width() - 1, pt._y + raw.height() - 1); + _bActive = false; + _bHasGfx = true; + _bDoubleState = bDoubleState; +} + +RMOptionButton::RMOptionButton(const RMRect &pt) { + _rect = pt; + _bActive = false; + _bHasGfx = false; + _bDoubleState = false; + _buf = NULL; +} + +RMOptionButton::~RMOptionButton() { + if (_bHasGfx) + delete _buf; +} + +bool RMOptionButton::doFrame(const RMPoint &mousePos, bool bLeftClick, bool bRightClick) { + if (!_bDoubleState) { + if (_rect.ptInRect(mousePos)) { + if (!_bActive) { + _bActive = true; + return true; + } + } else { + if (_bActive) { + _bActive = false; + return true; + } + } + } else { + if (bLeftClick && _rect.ptInRect(mousePos)) { + _bActive = !_bActive; + return true; + } + } + + return false; +} + +void RMOptionButton::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + if (!_bActive) + return; + + if (_bHasGfx) + CORO_INVOKE_2(_buf->draw, bigBuf, prim); + + CORO_END_CODE; +} + +void RMOptionButton::addToList(RMGfxTargetBuffer &bigBuf) { + if (_bHasGfx) + bigBuf.addPrim(new RMGfxPrimitive(this, _rect)); +} + +bool RMOptionButton::isActive() { + return _bActive; +} + +void RMOptionButton::setActiveState(bool bState) { + _bActive = bState; +} + +/****************************************************************************\ +* RMOptionSlide Methods +\****************************************************************************/ + +RMOptionSlide::RMOptionSlide(const RMPoint &pt, int nRange, int nStartValue, int slideSize) { + RMResRaw *raw; + + _pos = pt; + _nSlideSize = slideSize; + _nMax = nRange; + _nStep = 100 / _nMax; + _nValue = nStartValue; + + _sliderCenter = NULL; + _sliderLeft = NULL; + _sliderRight = NULL; + _sliderSingle = NULL; + + // Sliders + INIT_GFX16_FROMRAW(20029, _sliderCenter); + INIT_GFX16_FROMRAW(20030, _sliderLeft); + INIT_GFX16_FROMRAW(20031, _sliderRight); + INIT_GFX16_FROMRAW(20032, _sliderSingle); + + // Buttons + _pushLeft = new RMOptionButton(RMRect(pt._x - 23, pt._y, pt._x - 23 + 22, pt._y + 26)); + _pushRight = new RMOptionButton(RMRect(pt._x + _nSlideSize, pt._y, pt._x + _nSlideSize + 5 + 22, pt._y + 26)); +} + +RMOptionSlide::~RMOptionSlide() { + delete _sliderCenter; + _sliderCenter = NULL; + delete _sliderLeft; + _sliderLeft = NULL; + delete _sliderRight; + _sliderRight = NULL; + delete _sliderSingle; + _sliderSingle = NULL; + + delete _pushLeft; + _pushLeft = NULL; + delete _pushRight; + _pushRight = NULL; +} + +bool RMOptionSlide::doFrame(const RMPoint &mousePos, bool bLeftClick, bool bRightClick) { + bool bRefresh = false; + + // Do the button DoFrame's + _pushLeft->doFrame(mousePos, bLeftClick, bRightClick); + _pushRight->doFrame(mousePos, bLeftClick, bRightClick); + + if (_pushLeft->isActive()) { + if (bLeftClick) { + bRefresh = true; + _nValue--; + } else if (bRightClick) { + bRefresh = true; + _nValue -= 3; + } + if (_nValue < 1) + _nValue = 1; + } else if (_pushRight->isActive()) { + bRefresh = true; + + if (bLeftClick) { + bRefresh = true; + _nValue++; + } else if (bRightClick) { + bRefresh = true; + _nValue += 3; + } + if (_nValue > _nMax) + _nValue = _nMax; + } + + return bRefresh; +} + +void RMOptionSlide::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) { + CORO_BEGIN_CONTEXT; + int i; + int val; + RMPoint pos; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + _ctx->pos = _pos; + _ctx->pos._x += 4; + _ctx->pos._y += 4; + + _ctx->val = _nValue * _nStep; + if (_ctx->val < 1) + _ctx->val = 1; + else if (_ctx->val > 100) + _ctx->val = 100; + + if (_ctx->val == 1) { + prim->setDst(_ctx->pos); + CORO_INVOKE_2(_sliderSingle->draw, bigBuf, prim); + } else { + prim->setDst(_ctx->pos); + CORO_INVOKE_2(_sliderLeft->draw, bigBuf, prim); + _ctx->pos._x += 3; + + for (_ctx->i = 1; _ctx->i < _ctx->val - 1; _ctx->i++) { + prim->setDst(_ctx->pos); + CORO_INVOKE_2(_sliderCenter->draw, bigBuf, prim); + _ctx->pos._x += 3; + } + + prim->setDst(_ctx->pos); + CORO_INVOKE_2(_sliderRight->draw, bigBuf, prim); + _ctx->pos._x += 3; + } + + CORO_END_CODE; +} + +void RMOptionSlide::addToList(RMGfxTargetBuffer &bigBuf) { + bigBuf.addPrim(new RMGfxPrimitive(this)); +} + +int RMOptionSlide::getValue() { + return _nValue; +} + +/****************************************************************************\ +* RMOptionScreen Methods +\****************************************************************************/ + +RMOptionScreen::RMOptionScreen() { + _nState = MENUNONE; + _menu = NULL; + _hideLoadSave = NULL; + _quitConfirm = NULL; + _bQuitConfirm = false; + + create(RM_SX, RM_SY); + + _buttonExit = NULL; + _buttonLoad = NULL; + _buttonSave = NULL; + _buttonGameMenu = NULL; + _buttonGfxMenu = NULL; + _buttonSoundMenu = NULL; + _buttonSave_ArrowLeft = NULL; + _buttonSave_ArrowRight = NULL; + _bEditSaveName = false; + + for (int i = 0; i < 6; i++) { + _curThumb[i] = NULL; + _buttonSave_States[i] = NULL; + } + + _statePos = 0; + _buttonQuitYes = NULL; + _buttonQuitNo = NULL; + _buttonQuit = NULL; + _saveEasy = NULL; + _saveHard = NULL; + _buttonGfx_Tips = NULL; + _buttonSound_DubbingOn = NULL; + _buttonSound_MusicOn = NULL; + _buttonSound_SFXOn = NULL; + _slideTonySpeed = NULL; + _slideTextSpeed = NULL; + _buttonGame_Lock = NULL; + _buttonGfx_Anni30 = NULL; + _sliderSound_Music = NULL; + _buttonGame_TimerizedText = NULL; + _buttonGfx_AntiAlias = NULL; + _sliderSound_SFX = NULL; + _buttonGame_Scrolling = NULL; + _buttonGfx_Sottotitoli = NULL; + _sliderSound_Dubbing = NULL; + _buttonGame_InterUp = NULL; + _buttonGfx_Trans = NULL; + + _fadeStep = 0; + _fadeY = 0; + _fadeTime = 0; + _nEditPos = 0; + _nLastState = MENUGAME; +} + +RMOptionScreen::~RMOptionScreen() { + closeState(); +} + +void RMOptionScreen::refreshAll(CORO_PARAM) { + CORO_BEGIN_CONTEXT; + RMGfxSourceBuffer16 *thumb; + RMText *title; + RMText *num[6]; + int i; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + clearOT(); + + addPrim(new RMGfxPrimitive(_menu)); + + if (_bNoLoadSave) + addPrim(new RMGfxPrimitive(_hideLoadSave, RMPoint(0, 401))); + + if (_bQuitConfirm) { + addPrim(new RMGfxPrimitive(_quitConfirm, RMPoint(270, 200))); + _buttonQuitYes->addToList(*this); + _buttonQuitNo->addToList(*this); + } + + _buttonExit->addToList(*this); + + if (_nState == MENUGAME || _nState == MENUGFX || _nState == MENUSOUND) { + _buttonQuit->addToList(*this); + _buttonLoad->addToList(*this); + _buttonSave->addToList(*this); + } + + if (_nState == MENUGAME) { + _buttonGame_Lock->addToList(*this); + _buttonGame_TimerizedText->addToList(*this); + _buttonGame_Scrolling->addToList(*this); + _buttonGame_InterUp->addToList(*this); + _slideTextSpeed->addToList(*this); + _slideTonySpeed->addToList(*this); + } else if (_nState == MENUGFX) { + _buttonGfx_Anni30->addToList(*this); + _buttonGfx_AntiAlias->addToList(*this); + _buttonGfx_Sottotitoli->addToList(*this); + _buttonGfx_Trans->addToList(*this); + _buttonGfx_Tips->addToList(*this); + } else if (_nState == MENUSOUND) { + _sliderSound_Dubbing->addToList(*this); + _sliderSound_Music->addToList(*this); + _sliderSound_SFX->addToList(*this); + _buttonSound_DubbingOn->addToList(*this); + _buttonSound_MusicOn->addToList(*this); + _buttonSound_SFXOn->addToList(*this); + } + + _ctx->thumb = NULL; + _ctx->title = NULL; + Common::fill(&_ctx->num[0], &_ctx->num[6], (RMText *)NULL); + + if (_nState == MENULOAD || _nState == MENUSAVE) { + _ctx->title = new RMText; + if (_nState == MENULOAD) { + RMMessage msg(10); + _ctx->title->writeText(msg[0], 1); + } else { + RMMessage msg(11); + _ctx->title->writeText(msg[0], 1); + } + + addPrim(new RMGfxPrimitive(_ctx->title, RMPoint(320, 10))); + + if (_curThumbDiff[0] == 0) + addPrim(new RMGfxPrimitive(_saveHard, RMPoint(48, 57))); + else if (_curThumbDiff[0] == 1) + addPrim(new RMGfxPrimitive(_saveEasy, RMPoint(48, 57))); + if (_curThumbDiff[1] == 0) + addPrim(new RMGfxPrimitive(_saveHard, RMPoint(240, 57))); + else if (_curThumbDiff[1] == 1) + addPrim(new RMGfxPrimitive(_saveEasy, RMPoint(240, 57))); + if (_curThumbDiff[2] == 0) + addPrim(new RMGfxPrimitive(_saveHard, RMPoint(432, 57))); + else if (_curThumbDiff[2] == 1) + addPrim(new RMGfxPrimitive(_saveEasy, RMPoint(432, 57))); + if (_curThumbDiff[3] == 0) + addPrim(new RMGfxPrimitive(_saveHard, RMPoint(48, 239))); + else if (_curThumbDiff[3] == 1) + addPrim(new RMGfxPrimitive(_saveEasy, RMPoint(48, 239))); + if (_curThumbDiff[4] == 0) + addPrim(new RMGfxPrimitive(_saveHard, RMPoint(240, 239))); + else if (_curThumbDiff[4] == 1) + addPrim(new RMGfxPrimitive(_saveEasy, RMPoint(240, 239))); + if (_curThumbDiff[5] == 0) + addPrim(new RMGfxPrimitive(_saveHard, RMPoint(432, 239))); + else if (_curThumbDiff[5] == 1) + addPrim(new RMGfxPrimitive(_saveEasy, RMPoint(432, 239))); + + if (_curThumb[0] && !(_bEditSaveName && _nEditPos == 0)) + addPrim(new RMGfxPrimitive(_curThumb[0], RMPoint(48, 57))); + if (_curThumb[1] && !(_bEditSaveName && _nEditPos == 1)) + addPrim(new RMGfxPrimitive(_curThumb[1], RMPoint(240, 57))); + if (_curThumb[2] && !(_bEditSaveName && _nEditPos == 2)) + addPrim(new RMGfxPrimitive(_curThumb[2], RMPoint(432, 57))); + if (_curThumb[3] && !(_bEditSaveName && _nEditPos == 3)) + addPrim(new RMGfxPrimitive(_curThumb[3], RMPoint(48, 239))); + if (_curThumb[4] && !(_bEditSaveName && _nEditPos == 4)) + addPrim(new RMGfxPrimitive(_curThumb[4], RMPoint(240, 239))); + if (_curThumb[5] && !(_bEditSaveName && _nEditPos == 5)) + addPrim(new RMGfxPrimitive(_curThumb[5], RMPoint(432, 239))); + + if (_bEditSaveName) { + _ctx->thumb = new RMGfxSourceBuffer16; + _ctx->thumb->init((byte *)g_vm->getThumbnail(), 640 / 4, 480 / 4); + + if (_nEditPos == 0) + addPrim(new RMGfxPrimitive(_ctx->thumb, RMPoint(48, 57))); + else if (_nEditPos == 1) + addPrim(new RMGfxPrimitive(_ctx->thumb, RMPoint(240, 57))); + else if (_nEditPos == 2) + addPrim(new RMGfxPrimitive(_ctx->thumb, RMPoint(432, 57))); + else if (_nEditPos == 3) + addPrim(new RMGfxPrimitive(_ctx->thumb, RMPoint(48, 239))); + else if (_nEditPos == 4) + addPrim(new RMGfxPrimitive(_ctx->thumb, RMPoint(240, 239))); + else if (_nEditPos == 5) + addPrim(new RMGfxPrimitive(_ctx->thumb, RMPoint(432, 239))); + } + + for (_ctx->i = 0; _ctx->i < 6; _ctx->i++) { + Common::String s; + + if (_bEditSaveName && _nEditPos == _ctx->i) + s = Common::String::format("%02d)%s*", _statePos + _ctx->i, _editName); + else { + if (_statePos == 0 && _ctx->i == 0) + s = "Autosave"; + else + s = Common::String::format("%02d)%s", _statePos + _ctx->i, _curThumbName[_ctx->i].c_str()); + } + + _ctx->num[_ctx->i] = new RMText; + _ctx->num[_ctx->i]->setAlignType(RMText::HLEFT, RMText::VTOP); + _ctx->num[_ctx->i]->writeText(s, 2); + } + + addPrim(new RMGfxPrimitive(_ctx->num[0], RMPoint(55 - 3, 180 + 14))); + addPrim(new RMGfxPrimitive(_ctx->num[1], RMPoint(247 - 3, 180 + 14))); + addPrim(new RMGfxPrimitive(_ctx->num[2], RMPoint(439 - 3, 180 + 14))); + addPrim(new RMGfxPrimitive(_ctx->num[3], RMPoint(55 - 3, 362 + 14))); + addPrim(new RMGfxPrimitive(_ctx->num[4], RMPoint(247 - 3, 362 + 14))); + addPrim(new RMGfxPrimitive(_ctx->num[5], RMPoint(439 - 3, 362 + 14))); + + _buttonSave_ArrowLeft->addToList(*this); + _buttonSave_ArrowRight->addToList(*this); + } + + CORO_INVOKE_0(drawOT); + + if (_nState == MENULOAD || _nState == MENUSAVE) { + if (_ctx->thumb) + delete _ctx->thumb; + + if (_ctx->title) + delete _ctx->title; + + for (_ctx->i = 0; _ctx->i < 6; _ctx->i++) { + if (_ctx->num[_ctx->i]) + delete _ctx->num[_ctx->i]; + } + } + + CORO_END_CODE; +} + +void RMOptionScreen::refreshThumbnails() { + for (int i = 0; i < 6; i++) { + if (_curThumb[i]) + delete _curThumb[i]; + + _curThumb[i] = new RMGfxSourceBuffer16; + _curThumb[i]->create(640 / 4, 480 / 4); + if (!loadThumbnailFromSaveState(_statePos + i, *_curThumb[i], _curThumbName[i], _curThumbDiff[i])) { + delete _curThumb[i]; + _curThumb[i] = NULL; + _curThumbName[i].clear(); + _curThumbDiff[i] = 11; + } + } +} + +void RMOptionScreen::initState(CORO_PARAM) { + CORO_BEGIN_CONTEXT; + RMResRaw *raw; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + if (_nState == MENUGAME || _nState == MENUGFX || _nState == MENUSOUND) + _ctx->raw = new RMResRaw(20000 + _nState); + else if (_nState == MENULOAD || _nState == MENUSAVE) { + if (_bAlterGfx) + _ctx->raw = new RMResRaw(20024); + else + _ctx->raw = new RMResRaw(20003); + } else { + error("Invalid state"); + } + + assert(_ctx->raw->isValid()); + assert(_menu == NULL); + _menu = new RMGfxSourceBuffer16(false); + _menu->init(*_ctx->raw, _ctx->raw->width(), _ctx->raw->height()); + delete _ctx->raw; + + if (_nState == MENULOAD || _nState == MENUSAVE) { + if (_bAlterGfx) { + assert(_buttonExit == NULL); + _buttonExit = new RMOptionButton(20025, RMPoint(561, 406)); + } else { + assert(_buttonExit == NULL); + _buttonExit = new RMOptionButton(20012, RMPoint(560, 404)); + } + + INIT_GFX8_FROMRAW(_ctx->raw, 20036, _saveEasy); + INIT_GFX8_FROMRAW(_ctx->raw, 20037, _saveHard); + + refreshThumbnails(); + + assert(_buttonSave_States[0] == NULL); + _buttonSave_States[0] = new RMOptionButton(RMRect(48, 57, 48 + 160, 57 + 120)); + assert(_buttonSave_States[1] == NULL); + _buttonSave_States[1] = new RMOptionButton(RMRect(240, 57, 240 + 160, 57 + 120)); + assert(_buttonSave_States[2] == NULL); + _buttonSave_States[2] = new RMOptionButton(RMRect(432, 57, 432 + 160, 57 + 120)); + assert(_buttonSave_States[3] == NULL); + _buttonSave_States[3] = new RMOptionButton(RMRect(48, 239, 48 + 160, 239 + 120)); + assert(_buttonSave_States[4] == NULL); + _buttonSave_States[4] = new RMOptionButton(RMRect(240, 239, 240 + 160, 239 + 120)); + assert(_buttonSave_States[5] == NULL); + _buttonSave_States[5] = new RMOptionButton(RMRect(432, 239, 432 + 160, 239 + 120)); + + if (_bAlterGfx) { + assert(_buttonSave_ArrowLeft == NULL); + _buttonSave_ArrowLeft = new RMOptionButton(20026, RMPoint(3, 196)); + assert(_buttonSave_ArrowRight == NULL); + _buttonSave_ArrowRight = new RMOptionButton(20027, RMPoint(601, 197)); + } else { + assert(_buttonSave_ArrowLeft == NULL); + _buttonSave_ArrowLeft = new RMOptionButton(20013, RMPoint(0, 197)); + assert(_buttonSave_ArrowRight == NULL); + _buttonSave_ArrowRight = new RMOptionButton(20014, RMPoint(601, 197)); + } + } else if (_nState == MENUGAME || _nState == MENUGFX || _nState == MENUSOUND) { + assert(_buttonExit == NULL); + _buttonExit = new RMOptionButton(20005, RMPoint(560, 405)); + assert(_buttonQuit == NULL); + _buttonQuit = new RMOptionButton(20020, RMPoint(7, 408)); + assert(_buttonLoad == NULL); + _buttonLoad = new RMOptionButton(20006, RMPoint(231, 401)); + assert(_buttonSave == NULL); + _buttonSave = new RMOptionButton(20007, RMPoint(325, 401)); + + assert(_buttonGameMenu == NULL); + _buttonGameMenu = new RMOptionButton(RMRect(24, 32, 118, 64)); + assert(_buttonGfxMenu == NULL); + _buttonGfxMenu = new RMOptionButton(RMRect(118, 32, 212, 64)); + assert(_buttonSoundMenu == NULL); + _buttonSoundMenu = new RMOptionButton(RMRect(212, 32, 306, 64)); + + _ctx->raw = new RMResRaw(20021); + assert(_ctx->raw->isValid()); + assert(_quitConfirm == NULL); + _quitConfirm = new RMGfxSourceBuffer16(false); + _quitConfirm->init(*_ctx->raw, _ctx->raw->width(), _ctx->raw->height()); + delete _ctx->raw; + + assert(_buttonQuitYes == NULL); + _buttonQuitYes = new RMOptionButton(20022, RMPoint(281, 265)); + _buttonQuitYes->setPriority(30); + assert(_buttonQuitNo == NULL); + _buttonQuitNo = new RMOptionButton(20023, RMPoint(337, 264)); + _buttonQuitNo->setPriority(30); + + if (_bNoLoadSave) { + _ctx->raw = new RMResRaw(20028); + assert(_ctx->raw->isValid()); + assert(_hideLoadSave == NULL); + _hideLoadSave = new RMGfxSourceBuffer16(false); + _hideLoadSave->init(*_ctx->raw, _ctx->raw->width(), _ctx->raw->height()); + delete _ctx->raw; + } + + // Menu GAME + if (_nState == MENUGAME) { + assert(_buttonGame_Lock == NULL); + _buttonGame_Lock = new RMOptionButton(20008, RMPoint(176, 262), true); + _buttonGame_Lock->setActiveState(GLOBALS._bCfgInvLocked); + assert(_buttonGame_TimerizedText == NULL); + _buttonGame_TimerizedText = new RMOptionButton(20009, RMPoint(463, 273), true); + _buttonGame_TimerizedText->setActiveState(!GLOBALS._bCfgTimerizedText); + assert(_buttonGame_Scrolling == NULL); + _buttonGame_Scrolling = new RMOptionButton(20010, RMPoint(315, 263), true); + _buttonGame_Scrolling->setActiveState(GLOBALS._bCfgInvNoScroll); + assert(_buttonGame_InterUp == NULL); + _buttonGame_InterUp = new RMOptionButton(20011, RMPoint(36, 258), true); + _buttonGame_InterUp->setActiveState(GLOBALS._bCfgInvUp); + + assert(_slideTextSpeed == NULL); + _slideTextSpeed = new RMOptionSlide(RMPoint(165, 122), 10, GLOBALS._nCfgTextSpeed); + assert(_slideTonySpeed == NULL); + _slideTonySpeed = new RMOptionSlide(RMPoint(165, 226), 5, GLOBALS._nCfgTonySpeed); + } + // Menu Graphics + else if (_nState == MENUGFX) { + assert(_buttonGfx_Anni30 == NULL); + _buttonGfx_Anni30 = new RMOptionButton(20015, RMPoint(247, 178), true); + _buttonGfx_Anni30->setActiveState(GLOBALS._bCfgAnni30); + assert(_buttonGfx_AntiAlias == NULL); + _buttonGfx_AntiAlias = new RMOptionButton(20016, RMPoint(430, 83), true); + _buttonGfx_AntiAlias->setActiveState(!GLOBALS._bCfgAntiAlias); + assert(_buttonGfx_Sottotitoli == NULL); + _buttonGfx_Sottotitoli = new RMOptionButton(20017, RMPoint(98, 82), true); + _buttonGfx_Sottotitoli->setActiveState(!GLOBALS._bShowSubtitles); + assert(_buttonGfx_Tips == NULL); + _buttonGfx_Tips = new RMOptionButton(20018, RMPoint(431, 246), true); + _buttonGfx_Tips->setActiveState(GLOBALS._bCfgInterTips); + assert(_buttonGfx_Trans == NULL); + _buttonGfx_Trans = new RMOptionButton(20019, RMPoint(126, 271), true); + _buttonGfx_Trans->setActiveState(!GLOBALS._bCfgTransparence); + + } else if (_nState == MENUSOUND) { + assert(_sliderSound_Dubbing == NULL); + _sliderSound_Dubbing = new RMOptionSlide(RMPoint(165, 122), 10, GLOBALS._nCfgDubbingVolume); + assert(_sliderSound_Music == NULL); + _sliderSound_Music = new RMOptionSlide(RMPoint(165, 226), 10, GLOBALS._nCfgMusicVolume); + assert(_sliderSound_SFX == NULL); + _sliderSound_SFX = new RMOptionSlide(RMPoint(165, 330), 10, GLOBALS._nCfgSFXVolume); + + assert(_buttonSound_DubbingOn == NULL); + _buttonSound_DubbingOn = new RMOptionButton(20033, RMPoint(339, 75), true); + _buttonSound_DubbingOn->setActiveState(GLOBALS._bCfgDubbing); + assert(_buttonSound_MusicOn == NULL); + _buttonSound_MusicOn = new RMOptionButton(20034, RMPoint(338, 179), true); + _buttonSound_MusicOn->setActiveState(GLOBALS._bCfgMusic); + assert(_buttonSound_SFXOn == NULL); + _buttonSound_SFXOn = new RMOptionButton(20035, RMPoint(338, 283), true); + _buttonSound_SFXOn->setActiveState(GLOBALS._bCfgSFX); + } + } + + CORO_INVOKE_0(refreshAll); + + CORO_END_CODE; +} + +void RMOptionScreen::closeState() { + delete _menu; + _menu = NULL; + + delete _buttonExit; + _buttonExit = NULL; + + if (_nState == MENULOAD || _nState == MENUSAVE) { + for (int i = 0; i < 6; i++) { + if (_curThumb[i] != NULL) { + delete _curThumb[i]; + _curThumb[i] = NULL; + } + + delete _buttonSave_States[i]; + _buttonSave_States[i] = NULL; + } + + delete _buttonSave_ArrowLeft; + _buttonSave_ArrowLeft = NULL; + delete _buttonSave_ArrowRight; + _buttonSave_ArrowRight = NULL; + + delete _saveEasy; + _saveEasy = NULL; + delete _saveHard; + _saveHard = NULL; + } + + if (_nState == MENUGAME || _nState == MENUGFX || _nState == MENUSOUND) { + delete _buttonQuit; + _buttonQuit = NULL; + delete _buttonLoad; + _buttonLoad = NULL; + delete _buttonSave; + _buttonSave = NULL; + delete _buttonGameMenu; + _buttonGameMenu = NULL; + delete _buttonGfxMenu; + _buttonGfxMenu = NULL; + delete _buttonSoundMenu; + _buttonSoundMenu = NULL; + delete _quitConfirm; + _quitConfirm = NULL; + delete _buttonQuitYes; + _buttonQuitYes = NULL; + delete _buttonQuitNo; + _buttonQuitNo = NULL; + + if (_bNoLoadSave) { + delete _hideLoadSave; + _hideLoadSave = NULL; + } + + if (_nState == MENUGAME) { + GLOBALS._bCfgInvLocked = _buttonGame_Lock->isActive(); + delete _buttonGame_Lock; + _buttonGame_Lock = NULL; + + GLOBALS._bCfgTimerizedText = !_buttonGame_TimerizedText->isActive(); + delete _buttonGame_TimerizedText; + _buttonGame_TimerizedText = NULL; + + GLOBALS._bCfgInvNoScroll = _buttonGame_Scrolling->isActive(); + delete _buttonGame_Scrolling; + _buttonGame_Scrolling = NULL; + + GLOBALS._bCfgInvUp = _buttonGame_InterUp->isActive(); + delete _buttonGame_InterUp; + _buttonGame_InterUp = NULL; + + GLOBALS._nCfgTextSpeed = _slideTextSpeed->getValue(); + delete _slideTextSpeed; + _slideTextSpeed = NULL; + + GLOBALS._nCfgTonySpeed = _slideTonySpeed->getValue(); + delete _slideTonySpeed; + _slideTonySpeed = NULL; + } else if (_nState == MENUGFX) { + GLOBALS._bCfgAnni30 = _buttonGfx_Anni30->isActive(); + delete _buttonGfx_Anni30; + _buttonGfx_Anni30 = NULL; + + GLOBALS._bCfgAntiAlias = !_buttonGfx_AntiAlias->isActive(); + delete _buttonGfx_AntiAlias; + _buttonGfx_AntiAlias = NULL; + + GLOBALS._bShowSubtitles = !_buttonGfx_Sottotitoli->isActive(); + delete _buttonGfx_Sottotitoli; + _buttonGfx_Sottotitoli = NULL; + + GLOBALS._bCfgInterTips = _buttonGfx_Tips->isActive(); + delete _buttonGfx_Tips; + _buttonGfx_Tips = NULL; + + GLOBALS._bCfgTransparence = !_buttonGfx_Trans->isActive(); + delete _buttonGfx_Trans; + _buttonGfx_Trans = NULL; + } else if (_nState == MENUSOUND) { + GLOBALS._nCfgDubbingVolume = _sliderSound_Dubbing->getValue(); + delete _sliderSound_Dubbing; + _sliderSound_Dubbing = NULL; + + GLOBALS._nCfgMusicVolume = _sliderSound_Music->getValue(); + delete _sliderSound_Music; + _sliderSound_Music = NULL; + + GLOBALS._nCfgSFXVolume = _sliderSound_SFX->getValue(); + delete _sliderSound_SFX; + _sliderSound_SFX = NULL; + + GLOBALS._bCfgDubbing = _buttonSound_DubbingOn->isActive(); + delete _buttonSound_DubbingOn; + _buttonSound_DubbingOn = NULL; + + GLOBALS._bCfgMusic = _buttonSound_MusicOn->isActive(); + delete _buttonSound_MusicOn; + _buttonSound_MusicOn = NULL; + + GLOBALS._bCfgSFX = _buttonSound_SFXOn->isActive(); + delete _buttonSound_SFXOn; + _buttonSound_SFXOn = NULL; + } + + // Save the new settings to ScummVM + g_vm->saveSoundSettings(); + } + + _nState = MENUNONE; +} + +void RMOptionScreen::reInit(RMGfxTargetBuffer &bigBuf) { + bigBuf.addPrim(new RMGfxPrimitive(this)); +} + +void RMOptionScreen::init(CORO_PARAM, RMGfxTargetBuffer &bigBuf, bool &result) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + if (_fadeStep != 0) { + result = false; + return; + } + + _fadeStep = 1; + _fadeY = -20; + _fadeTime = -1; + _bExit = false; + _bLoadMenuOnly = false; + _bNoLoadSave = false; + _bAlterGfx = false; + + bigBuf.addPrim(new RMGfxPrimitive(this)); + + if (_nState == MENULOAD || _nState == MENUSAVE || _nState == MENUNONE) + _nState = MENUGAME; + + CORO_INVOKE_0(initState); + + result = true; + + CORO_END_CODE; +} + +void RMOptionScreen::initLoadMenuOnly(CORO_PARAM, RMGfxTargetBuffer &bigBuf, bool bAlternateGfx, bool &result) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + if (_fadeStep != 0) { + result = false; + return; + } + + _fadeStep = 1; + _fadeY = -20; + _fadeTime = -1; + _bExit = false; + _bLoadMenuOnly = true; + _bNoLoadSave = false; + _bAlterGfx = bAlternateGfx; + + bigBuf.addPrim(new RMGfxPrimitive(this)); + + _nState = MENULOAD; + CORO_INVOKE_0(initState); + + result = true; + + CORO_END_CODE; +} + +void RMOptionScreen::initSaveMenuOnly(CORO_PARAM, RMGfxTargetBuffer &bigBuf, bool bAlternateGfx, bool &result) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + if (_fadeStep != 0) { + result = false; + return; + } + + _fadeStep = 1; + _fadeY = -20; + _fadeTime = -1; + _bExit = false; + _bLoadMenuOnly = true; + _bNoLoadSave = false; + _bAlterGfx = bAlternateGfx; + + bigBuf.addPrim(new RMGfxPrimitive(this)); + + _nState = MENUSAVE; + CORO_INVOKE_0(initState); + + result = true; + + CORO_END_CODE; +} + +void RMOptionScreen::initNoLoadSave(CORO_PARAM, RMGfxTargetBuffer &bigBuf, bool &result) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + if (_fadeStep != 0) { + result = false; + return; + } + + _fadeStep = 1; + _fadeY = -20; + _fadeTime = -1; + _bExit = false; + _bLoadMenuOnly = false; + _bNoLoadSave = true; + + bigBuf.addPrim(new RMGfxPrimitive(this)); + + _nState = MENUGAME; + CORO_INVOKE_0(initState); + + result = true; + + CORO_END_CODE; +} + +bool RMOptionScreen::close() { + if (_fadeStep != 6) + return false; + + // Start fade out + _fadeStep++; + _fadeTime = g_vm->getTime(); + return true; +} + +bool RMOptionScreen::isClosing() { + return _bExit; +} + +int RMOptionScreen::priority() { + // Just below the mouse + return 190; +} + +void RMOptionScreen::changeState(CORO_PARAM, OptionScreenState newState) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + _nLastState = _nState; + closeState(); + _nState = newState; + CORO_INVOKE_0(initState); + + CORO_END_CODE; +} + +void RMOptionScreen::doFrame(CORO_PARAM, RMInput *input) { + CORO_BEGIN_CONTEXT; + bool bLeftClick, bRightClick; + RMPoint mousePos; + bool bRefresh; + int i; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + // If it is not fully open, do nothing + if (_fadeStep != 6) + return; + + // Reads input + _ctx->mousePos = input->mousePos(); + _ctx->bLeftClick = input->mouseLeftClicked(); + _ctx->bRightClick = input->mouseRightClicked(); + + _ctx->bRefresh = false; + + if (_bQuitConfirm) { + _ctx->bRefresh |= _buttonQuitYes->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick); + _ctx->bRefresh |= _buttonQuitNo->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick); + } else { + _ctx->bRefresh |= _buttonExit->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick); + + // Check if you have clicked on the output + if (_nState == MENUGAME || _nState == MENUGFX || _nState == MENUSOUND) { + // Buttons without graphics... + _buttonGameMenu->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick); + _buttonGfxMenu->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick); + _buttonSoundMenu->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick); + + // Buttons with graphics + if (!_bNoLoadSave) { + if (!g_vm->getIsDemo()) { + _ctx->bRefresh |= _buttonLoad->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick); + _ctx->bRefresh |= _buttonSave->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick); + } + + _ctx->bRefresh |= _buttonQuit->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick); + } + } + + if (_nState == MENUGAME) { + _ctx->bRefresh |= _buttonGame_Lock->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick); + _ctx->bRefresh |= _buttonGame_TimerizedText->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick); + _ctx->bRefresh |= _buttonGame_Scrolling->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick); + _ctx->bRefresh |= _buttonGame_InterUp->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick); + _ctx->bRefresh |= _slideTextSpeed->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick); + _ctx->bRefresh |= _slideTonySpeed->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick); + + } else if (_nState == MENUGFX) { + _ctx->bRefresh |= _buttonGfx_Anni30->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick); + _ctx->bRefresh |= _buttonGfx_AntiAlias->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick); + _ctx->bRefresh |= _buttonGfx_Sottotitoli->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick); + _ctx->bRefresh |= _buttonGfx_Tips->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick); + _ctx->bRefresh |= _buttonGfx_Trans->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick); + + } else if (_nState == MENUSOUND) { + _ctx->bRefresh |= _sliderSound_Dubbing->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick); + _ctx->bRefresh |= _sliderSound_Music->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick); + _ctx->bRefresh |= _sliderSound_SFX->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick); + _ctx->bRefresh |= _buttonSound_DubbingOn->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick); + _ctx->bRefresh |= _buttonSound_MusicOn->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick); + _ctx->bRefresh |= _buttonSound_SFXOn->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick); + + } else if (_nState == MENULOAD || _nState == MENUSAVE) { + for (_ctx->i = 0; _ctx->i < 6; _ctx->i++) + _buttonSave_States[_ctx->i]->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick); + + if (_statePos > 0) + _ctx->bRefresh |= _buttonSave_ArrowLeft->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick); + if (_statePos < 90) + _ctx->bRefresh |= _buttonSave_ArrowRight->doFrame(_ctx->mousePos, _ctx->bLeftClick, _ctx->bRightClick); + } + } + +#define KEYPRESS(c) (g_vm->getEngine()->getInput().getAsyncKeyState(c)) +#define PROCESS_CHAR(cod, c) if (KEYPRESS(cod)) { \ + _editName[strlen(_editName) + 1] = '\0'; _editName[strlen(_editName)] = c; _ctx->bRefresh = true; } + + // State Buttons + if (_bEditSaveName) { + if (KEYPRESS(Common::KEYCODE_BACKSPACE)) { + if (_editName[0] != '\0') { + _editName[strlen(_editName) - 1] = '\0'; + _ctx->bRefresh = true; + } + } + + for (_ctx->i = 0; _ctx->i < 26 && strlen(_editName) < 12; _ctx->i++) { + if (KEYPRESS(Common::KEYCODE_LSHIFT) || + KEYPRESS(Common::KEYCODE_RSHIFT)) { + PROCESS_CHAR((Common::KeyCode)((int)'a' + _ctx->i), _ctx->i + 'A'); + } else { + PROCESS_CHAR((Common::KeyCode)((int)'a' + _ctx->i), _ctx->i + 'a'); + } + } + + for (_ctx->i = 0; _ctx->i < 10 && strlen(_editName) < 12; _ctx->i++) + PROCESS_CHAR((Common::KeyCode)((int)'0' + _ctx->i), _ctx->i + '0'); + + if (strlen(_editName) < 12) + PROCESS_CHAR(Common::KEYCODE_SPACE, ' '); + + if (strlen(_editName) < 12) + PROCESS_CHAR(Common::KEYCODE_KP0, '0'); + if (strlen(_editName) < 12) + PROCESS_CHAR(Common::KEYCODE_KP1, '1'); + if (strlen(_editName) < 12) + PROCESS_CHAR(Common::KEYCODE_KP2, '2'); + if (strlen(_editName) < 12) + PROCESS_CHAR(Common::KEYCODE_KP3, '3'); + if (strlen(_editName) < 12) + PROCESS_CHAR(Common::KEYCODE_KP4, '4'); + if (strlen(_editName) < 12) + PROCESS_CHAR(Common::KEYCODE_KP5, '5'); + if (strlen(_editName) < 12) + PROCESS_CHAR(Common::KEYCODE_KP6, '6'); + if (strlen(_editName) < 12) + PROCESS_CHAR(Common::KEYCODE_KP7, '7'); + if (strlen(_editName) < 12) + PROCESS_CHAR(Common::KEYCODE_KP8, '8'); + if (strlen(_editName) < 12) + PROCESS_CHAR(Common::KEYCODE_KP9, '9'); + + // Cancel + if (KEYPRESS(Common::KEYCODE_ESCAPE)) { + _bEditSaveName = false; + _ctx->bRefresh = true; + } + + // OK + if (KEYPRESS(Common::KEYCODE_RETURN)) { + _bEditSaveName = false; + g_vm->saveState(_statePos + _nEditPos, _editName); + close(); + } + + } else if (_ctx->bLeftClick) { + if (_nState == MENULOAD || _nState == MENUSAVE) { + if (_buttonExit->isActive()) { + if (_bLoadMenuOnly) { + // If only the loading menu, close + close(); + } else { + CORO_INVOKE_1(changeState, _nLastState); + _ctx->bRefresh = true; + } + } else if (_buttonSave_ArrowLeft->isActive()) { + if (_statePos > 0) { + _statePos -= 6; + if (_statePos < 0) + _statePos = 0; + _buttonSave_ArrowLeft->setActiveState(false); + _ctx->bRefresh = true; + refreshThumbnails(); + } + } else if (_buttonSave_ArrowRight->isActive()) { + if (_statePos < 90) { + _statePos += 6; + if (_statePos > 90) + _statePos = 90; + _buttonSave_ArrowRight->setActiveState(false); + _ctx->bRefresh = true; + refreshThumbnails(); + } + } else { + for (_ctx->i = 0; _ctx->i < 6; _ctx->i++) + if (_buttonSave_States[_ctx->i]->isActive()) { + // There by saving or loading!!! + if (_nState == MENULOAD && _curThumb[_ctx->i] != NULL) { + // Loading + CORO_INVOKE_1(g_vm->loadState, _statePos + _ctx->i); + close(); + } else if (_nState == MENUSAVE && (_statePos != 0 || _ctx->i != 0)) { + // Turn on edit mode + _bEditSaveName = true; + _nEditPos = _ctx->i; + strcpy(_editName, _curThumbName[_ctx->i].c_str()); + _ctx->bRefresh = true; + } + + break; + } + } + } + + if (_nState == MENUGAME || _nState == MENUGFX || _nState == MENUSOUND) { + if (_bQuitConfirm) { + if (_buttonQuitNo->isActive()) { + _bQuitConfirm = false; + _ctx->bRefresh = true; + } else if (_buttonQuitYes->isActive()) { + _bQuitConfirm = false; + _ctx->bRefresh = true; + + g_vm->quitGame(); + } + } else { + if (_buttonQuit->isActive()) { + _bQuitConfirm = true; + _buttonQuitNo->setActiveState(false); + _buttonQuitYes->setActiveState(false); + _ctx->bRefresh = true; + } else if (_buttonExit->isActive()) + close(); + else if (_buttonLoad->isActive()) { + CORO_INVOKE_1(changeState, MENULOAD); + _ctx->bRefresh = true; + } else if (_buttonSave->isActive()) { + CORO_INVOKE_1(changeState, MENUSAVE); + _ctx->bRefresh = true; + } else if (_buttonGameMenu->isActive() && _nState != MENUGAME) { + CORO_INVOKE_1(changeState, MENUGAME); + _ctx->bRefresh = true; + } else if (_buttonGfxMenu->isActive() && _nState != MENUGFX) { + CORO_INVOKE_1(changeState, MENUGFX); + _ctx->bRefresh = true; + } else if (_buttonSoundMenu->isActive() && _nState != MENUSOUND) { + CORO_INVOKE_1(changeState, MENUSOUND); + _ctx->bRefresh = true; + } + + if (_nState == MENUGFX) { + // These options take effect immediately + if (_buttonGfx_Anni30->isActive()) + GLOBALS._bCfgAnni30 = true; + else + GLOBALS._bCfgAnni30 = false; + + if (_buttonGfx_AntiAlias->isActive()) + GLOBALS._bCfgAntiAlias = false; + else + GLOBALS._bCfgAntiAlias = true; + + if (_buttonGfx_Trans->isActive()) + GLOBALS._bCfgTransparence = false; + else + GLOBALS._bCfgTransparence = true; + } + } + } + } + + if (_nState == MENUGAME || _nState == MENUGFX || _nState == MENUSOUND) { + if (!_bQuitConfirm && KEYPRESS(Common::KEYCODE_ESCAPE)) + close(); + } + + if (_ctx->bRefresh) + CORO_INVOKE_0(refreshAll); + + CORO_END_CODE; +} + +void RMOptionScreen::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) { + CORO_BEGIN_CONTEXT; + int curTime; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + _ctx->curTime = g_vm->getTime(); + +#define FADE_SPEED 20 +#define SYNC (_ctx->curTime - _fadeTime) / 25 + + if (_bExit) + return; + + if (_fadeStep == 1) { + // Downhill fast + if (_fadeTime == -1) + _fadeY += FADE_SPEED; + else + _fadeY += FADE_SPEED * SYNC; + if (_fadeY > 480) { + _fadeY = 480; + _fadeStep++; + } + + // Set the part to draw the scrolling + prim->setSrc(RMRect(0, 480 - _fadeY, 640, 480)); + + } else if (_fadeStep == 2) { + // Bounce 1 + _fadeY -= FADE_SPEED / 2 * SYNC; + if (_fadeY < 400) { + _fadeY = 400; + _fadeStep++; + } + + prim->setSrc(RMRect(0, 480 - _fadeY, 640, 480)); + + } else if (_fadeStep == 3) { + _fadeY -= FADE_SPEED / 4 * SYNC; + if (_fadeY < 380) { + _fadeY = 380; + _fadeStep++; + } + + prim->setSrc(RMRect(0, 480 - _fadeY, 640, 480)); + + } else if (_fadeStep == 4) { + // Bounce 1 - 2 + _fadeY += FADE_SPEED / 3 * SYNC; + if (_fadeY > 420) { + _fadeY = 420; + _fadeStep++; + } + + prim->setSrc(RMRect(0, 480 - _fadeY, 640, 480)); + + } else if (_fadeStep == 5) { + _fadeY += FADE_SPEED / 2 * SYNC; + if (_fadeY > 480) { + _fadeY = 480; + _fadeStep++; + g_vm->hideLocation(); + } + + prim->setSrc(RMRect(0, 480 - _fadeY, 640, 480)); + + } else if (_fadeStep == 6) { + // Menu ON + + } else if (_fadeStep == 7) { + // Menu OFF + g_vm->showLocation(); + _fadeStep++; + + } else if (_fadeStep == 8) { + _fadeY -= FADE_SPEED * SYNC; + if (_fadeY < 0) { + _fadeY = 0; + _fadeStep++; + } + prim->setSrc(RMRect(0, 480 - _fadeY, 640, 480)); + + } else if (_fadeStep == 9) { + // Hello hello! + _bExit = true; + _fadeStep = 0; + + // Free memory + closeState(); + return; + + } else { + _fadeStep = 0; + } + + _fadeTime = _ctx->curTime; + + CORO_INVOKE_2(RMGfxWoodyBuffer::draw, bigBuf, prim); + + CORO_END_CODE; +} + +void RMOptionScreen::removeThis(CORO_PARAM, bool &result) { + if (_bExit) + result = true; + else + result = false; +} + +bool RMOptionScreen::loadThumbnailFromSaveState(int nState, byte *lpDestBuf, Common::String &name, byte &diff) { + char namebuf[256]; + Common::InSaveFile *f; + char id[4]; + + // Cleans the destination + Common::fill(lpDestBuf, lpDestBuf + 160 * 120 * 2, 0); + name = "No name"; + diff = 10; + + // Get the savegame filename for the given slot + Common::String buf = g_vm->getSaveStateFileName(nState); + + // Try and open the savegame + f = g_system->getSavefileManager()->openForLoading(buf); + if (f == NULL) + return false; + + // Check to see if the file has a valid header + f->read(id, 4); + if (id[0] != 'R' || id[1] != 'M' || id[2] != 'S') { + delete f; + return false; + } + + if (id[3] < 0x3) { + // Very old version that doesn't have screenshots + delete f; + return true; + } + + // Load the screenshot + if ((id[3] >= 0x5) && (id[3] < 0x8)) { + // Read it as an LZO compressed data block + byte *cmpbuf; + uint32 cmpsize, size; + + cmpbuf = new byte[160 * 120 * 4]; + + // Read in the compressed data + cmpsize = f->readUint32LE(); + f->read(cmpbuf, cmpsize); + + lzo1x_decompress(cmpbuf, cmpsize, lpDestBuf, &size); + + delete[] cmpbuf; + } else { + // Read in the screenshot as an uncompressed data block + if (id[3] >= 8) + // Recent versions use hardcoded 160x120 uncomrpessed data, so size can be skipped + f->skip(4); + + f->read(lpDestBuf, 160 * 120 * 2); + } + + if (id[3] >= 0x5) { + // Read in the difficulty level + diff = f->readByte(); + } + + if (id[3] < 0x4) { + // Savegame version doesn't have a stored name + delete f; + return true; + } + + int bufSize = f->readByte(); + f->read(namebuf, bufSize); + namebuf[bufSize] = '\0'; + name = namebuf; + + delete f; + return true; +} + +/****************************************************************************\ +* RMPointer Methods +\****************************************************************************/ + +RMPointer::RMPointer() { + Common::fill(_pointer, _pointer + 16, (RMGfxSourceBuffer8 *)NULL); + Common::fill(_specialPointer, _specialPointer + 16, (RMItem *)NULL); + + _nCurPointer = _nCurSpecialPointer = 0; + _nCurCustomPointer = NULL; +} + +RMPointer::~RMPointer() { + close(); +} + +void RMPointer::init() { + for (int i = 0; i < 5; i++) { + RMResRaw res(RES_P_GO + i); + + _pointer[i] = new RMGfxSourceBuffer8RLEByteAA; + _pointer[i]->init(res, res.width(), res.height(), false); + _pointer[i]->loadPaletteWA(RES_P_PAL); + } + + for (int i = 0; i < 5; i++) { + RMRes res(RES_P_PAP1 + i); + Common::SeekableReadStream *ds = res.getReadStream(); + _specialPointer[i] = new RMItem; + _specialPointer[i]->readFromStream(*ds); + delete ds; + } + + //m_hotspot[0].set(19,5); + _hotspot[0].set(5, 1); + _hotspot[1].set(32, 28); + _hotspot[2].set(45, 23); + _hotspot[3].set(35, 25); + _hotspot[4].set(32, 28); + + // Default=GO + _nCurPointer = 0; + _nCurSpecialPointer = 0; +} + +void RMPointer::close() { + for (int i = 0; i < 5; i++) { + if (_pointer[i] != NULL) { + delete _pointer[i]; + _pointer[i] = NULL; + } + + if (_specialPointer[i] != NULL) { + delete _specialPointer[i]; + _specialPointer[i] = NULL; + } + } +} + +void RMPointer::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) { + CORO_BEGIN_CONTEXT; + int n; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + // Check the pointer + _ctx->n = _nCurPointer; + if (_ctx->n == TA_COMBINE) + _ctx->n = TA_USE; + + _cursorHotspot = _hotspot[_ctx->n]; + + // Call the Draw method of the pointer + if (_nCurSpecialPointer == 0) { + // WORKAROUND: updateCursor gets called too early sometimes (for example, when + // the cursor is released over the TA_PERORATE option), via setAction. + if (_ctx->n > 4) + _ctx->n = 0; + + CORO_INVOKE_2(_pointer[_ctx->n]->draw, bigBuf, prim); + } else { + if (_nCurSpecialPointer == PTR_CUSTOM) + CORO_INVOKE_2(_nCurCustomPointer->draw, bigBuf, prim); + else + // Call the draw on the special pointer + CORO_INVOKE_2(_specialPointer[_nCurSpecialPointer - 1]->draw, bigBuf, prim); + } + + CORO_END_CODE; +} + +int RMPointer::curAction() { + if (_nCurSpecialPointer != 0) + return 0; + + return _nCurPointer; +} + +/** + * Show the cursor + */ +void RMPointer::showCursor() { + if (!CursorMan.isVisible()) { + CursorMan.showMouse(true); + + updateCursor(); + } +} + +/** + * Hide the cursor + */ +void RMPointer::hideCursor() { + if (CursorMan.isVisible()) { + CursorMan.showMouse(false); + } +} + +void RMPointer::doFrame() { + // Update the cursor animation if needed. + if (_nCurSpecialPointer == 0 || _nCurSpecialPointer == PTR_CUSTOM) + return; + + RMGfxTargetBuffer buf; + if (_specialPointer[_nCurSpecialPointer - 1]->doFrame(&buf, false)) + updateCursor(); +} + +void RMPointer::updateCursor() { + // Create an intermediate buffer and draw the cursor onto it + RMGfxTargetBuffer buf; + buf.create(64, 64, 16); + RMGfxPrimitive prim; + + draw(Common::nullContext, buf, &prim); + + // Get a pointer to the cursor data + byte *cursorData = buf; + + // If in black & white mode, convert the cursor + if (GLOBALS._bCfgAnni30) { + if (!RMGfxTargetBuffer::_precalcTable) { + RMGfxTargetBuffer::createBWPrecalcTable(); + } + uint16 *src = (uint16 *)cursorData; + for (int i = 0; i < 64; i++) { + uint16 *lineP = src; + for (int j = 0; j < 64; j++) { + lineP[j] = RMGfxTargetBuffer::_precalcTable[lineP[j] & 0x7FFF]; + } + src += 64; + } + } + + // Get the raw pixel data and set the cursor to it + Graphics::PixelFormat pixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0); + CursorMan.replaceCursor(cursorData, 64, 64, _cursorHotspot._x, _cursorHotspot._y, 0, 1, &pixelFormat); +} + +/** + * Sets a new action as current + */ +void RMPointer::setAction(RMTonyAction action) { + _nCurPointer = action; + updateCursor(); +} + +/** + * Sets a new pointer + */ +void RMPointer::setSpecialPointer(PointerType ptr) { + _nCurSpecialPointer = ptr; + if (_nCurSpecialPointer && _nCurSpecialPointer != PTR_CUSTOM) + _specialPointer[ptr - 1]->setPattern(1); + + updateCursor(); +} + +RMPointer::PointerType RMPointer::getSpecialPointer() { + return (PointerType)_nCurSpecialPointer; +} + +/** + * Set the new custom pointer + */ +void RMPointer::setCustomPointer(RMGfxSourceBuffer8 *ptr) { + _nCurCustomPointer = ptr; + updateCursor(); +} + +} // End of namespace Tony diff --git a/engines/tony/game.h b/engines/tony/game.h new file mode 100644 index 0000000000..fdf62a2a5d --- /dev/null +++ b/engines/tony/game.h @@ -0,0 +1,339 @@ +/* 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. + * + */ + +/* + * This code is based on original Tony Tough source code + * + * Copyright (c) 1997-2003 Nayma Software + */ + +#ifndef TONY_GAME_H +#define TONY_GAME_H + +#include "tony/gfxcore.h" +#include "tony/input.h" +#include "tony/loc.h" +#include "tony/utils.h" + +namespace Tony { + +#define INIT_GFX16_FROMRAW(dwRes, buf16) \ + raw = new RMResRaw(dwRes); \ + assert(raw->isValid()); \ + assert((buf16) == NULL); \ + (buf16) = new RMGfxSourceBuffer16(false); \ + (buf16)->init(*raw, raw->width(), raw->height()); \ + delete raw; + +#define INIT_GFX8_FROMRAW(raw, dwRes, buf8) \ + raw = new RMResRaw(dwRes); \ + assert(raw->isValid()); \ + assert((buf8) == NULL); \ + (buf8) = new RMGfxSourceBuffer8RLEByte(); \ + (buf8)->init(*raw, raw->width(), raw->height(), true); \ + delete raw; + +// X & Y dimensions of the adventure +#define RM_SX 640 +#define RM_SY 480 + +// X & Y dimensions of bigbuf +#define RM_BBX (RM_SX) +#define RM_BBY (RM_SY) + +// Skipping X & Y +#define RM_SKIPY ((RM_BBY - RM_SY) / 2) +#define RM_SKIPX 0 + +// Tony's actions +enum RMTonyAction { + TA_GOTO = 0, + TA_TAKE, + TA_USE, + TA_EXAMINE, + TA_TALK, + TA_PERORATE, + + TA_COMBINE = 10, + TA_RECEIVECOMBINE, + TA_COMBINEGIVE, + TA_RECEIVECOMBINEGIVE +}; + +// Global Functions +void mainEnableGUI(); +void mainDisableGUI(); + +// Classes +class RMPointer { +public: + enum PointerType { + PTR_NONE = 0, + PTR_ARROWUP, + PTR_ARROWDOWN, + PTR_ARROWLEFT, + PTR_ARROWRIGHT, + PTR_ARROWMAP, + PTR_CUSTOM + }; + +private: + RMGfxSourceBuffer8 *_pointer[16]; + RMPoint _hotspot[16]; + RMPoint _cursorHotspot; + + RMItem *_specialPointer[16]; + + int _nCurPointer; + int _nCurSpecialPointer; + + RMGfxSourceBuffer8 *_nCurCustomPointer; + +public: + /** + * Constructor & destructor + */ + RMPointer(); + virtual ~RMPointer(); + + /** + * Initialization + */ + void init(); + + /** + * Deinitialization + */ + void close(); + + /** + * Process a frame + */ + void doFrame(); + + /** + * draw method + */ + void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim); + + /** + * Sets a new action as current + */ + void setAction(RMTonyAction action); + + /** + * Sets a new pointer + */ + void setSpecialPointer(PointerType ptr); + + PointerType getSpecialPointer(); + + /** + * Set the new custom pointer + */ + void setCustomPointer(RMGfxSourceBuffer8 *ptr); + + /** + * Return the current action to be applied according to the pointer + */ + int curAction(); + + /** + * Update the cursor + */ + void updateCursor(); + + /** + * Show the cursor + */ + void showCursor(); + + /** + * Hide the cursor + */ + void hideCursor(); +}; + +class RMOptionButton: public RMGfxTaskSetPrior { +public: + RMRect _rect; + RMGfxSourceBuffer16 *_buf; + bool _bActive; + bool _bHasGfx; + bool _bDoubleState; + +public: + RMOptionButton(uint32 dwRes, RMPoint pt, bool bDoubleState = false); + RMOptionButton(const RMRect &pt); + virtual ~RMOptionButton(); + + bool doFrame(const RMPoint &mousePos, bool bLeftClick, bool bRightClick); + virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim); + void addToList(RMGfxTargetBuffer &bigBuf); + bool isActive(); + void setActiveState(bool bState); +}; + +class RMOptionSlide : public RMGfxTaskSetPrior { +private: + RMOptionButton *_pushLeft; + RMOptionButton *_pushRight; + RMGfxSourceBuffer16 *_sliderCenter; + RMGfxSourceBuffer16 *_sliderLeft; + RMGfxSourceBuffer16 *_sliderRight; + RMGfxSourceBuffer16 *_sliderSingle; + int _nSlideSize; + RMPoint _pos; + int _nValue; + int _nMax; + int _nStep; + +public: + RMOptionSlide(const RMPoint &pt, int m_nRange = 100, int m_nStartValue = 0, int slideSize = 300); + virtual ~RMOptionSlide(); + + bool doFrame(const RMPoint &mousePos, bool bLeftClick, bool bRightClick); + virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim); + void addToList(RMGfxTargetBuffer &bigBuf); + + int getValue(); +}; + +class RMOptionScreen : public RMGfxWoodyBuffer { +private: + RMGfxSourceBuffer16 *_menu; + RMGfxSourceBuffer16 *_quitConfirm; + RMGfxSourceBuffer16 *_hideLoadSave; + RMOptionButton *_buttonQuitYes; + RMOptionButton *_buttonQuitNo; + RMOptionButton *_buttonExit; + RMOptionButton *_buttonQuit; + RMOptionButton *_buttonLoad; + RMOptionButton *_buttonSave; + RMOptionButton *_buttonGameMenu; + RMOptionButton *_buttonGfxMenu; + RMOptionButton *_buttonSoundMenu; + RMGfxSourceBuffer8 *_saveEasy; + RMGfxSourceBuffer8 *_saveHard; + RMGfxSourceBuffer16 *_curThumb[6]; + Common::String _curThumbName[6]; + byte _curThumbDiff[6]; + RMOptionButton *_buttonSave_States[6]; + RMOptionButton *_buttonSave_ArrowLeft; + RMOptionButton *_buttonSave_ArrowRight; + RMOptionButton *_buttonGfx_Tips; + + RMOptionButton *_buttonSound_DubbingOn; + RMOptionButton *_buttonSound_MusicOn; + RMOptionButton *_buttonSound_SFXOn; + + RMOptionSlide *_slideTonySpeed; + RMOptionSlide *_slideTextSpeed; + + + int _statePos; + bool _bEditSaveName; + int _nEditPos; + char _editName[256]; + + union { + RMOptionButton *_buttonGame_Lock; + RMOptionButton *_buttonGfx_Anni30; + RMOptionSlide *_sliderSound_Music; + }; + union { + RMOptionButton *_buttonGame_TimerizedText; + RMOptionButton *_buttonGfx_AntiAlias; + RMOptionSlide *_sliderSound_SFX; + }; + union { + RMOptionButton *_buttonGame_Scrolling; + RMOptionButton *_buttonGfx_Sottotitoli; + RMOptionSlide *_sliderSound_Dubbing; + }; + union { + RMOptionButton *_buttonGame_InterUp; + RMOptionButton *_buttonGfx_Trans; + }; + + int _fadeStep; + bool _bExit; + bool _bQuitConfirm; + int _fadeY; + int _fadeTime; + bool _bLoadMenuOnly; + bool _bNoLoadSave; + bool _bAlterGfx; + + enum OptionScreenState { + MENUGAME, + MENUGFX, + MENUSOUND, + MENULOAD, + MENUSAVE, + MENUNONE + }; + + OptionScreenState _nState; + OptionScreenState _nLastState; + +public: + RMOptionScreen(); + virtual ~RMOptionScreen(); + + void init(CORO_PARAM, RMGfxTargetBuffer &bigBuf, bool &result); + void initLoadMenuOnly(CORO_PARAM, RMGfxTargetBuffer &bigBuf, bool bAlternateGfx, bool &result); + void initSaveMenuOnly(CORO_PARAM, RMGfxTargetBuffer &bigBuf, bool bAlternateGfx, bool &result); + void initNoLoadSave(CORO_PARAM, RMGfxTargetBuffer &bigBuf, bool &result); + void reInit(RMGfxTargetBuffer &bigBuf); + bool close(); + bool isClosing(); + + // Overloaded methods + virtual int priority(); + virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim); + virtual void removeThis(CORO_PARAM, bool &result); + + /** + * Polling for the option screen + */ + void doFrame(CORO_PARAM, RMInput *m_input); + + /** + * Retrieves a savegame's thumbnail, description, and difficulty level + */ + static bool loadThumbnailFromSaveState(int numState, byte *lpDestBuf, Common::String &name, byte &diff); + +protected: + // Initialization and state change + void initState(CORO_PARAM); + void closeState(); + void changeState(CORO_PARAM, OptionScreenState newState); + + // Repaint the options menu + void refreshAll(CORO_PARAM); + void refreshThumbnails(); +}; + +} // End of namespace Tony + +#endif diff --git a/engines/tony/gfxcore.cpp b/engines/tony/gfxcore.cpp new file mode 100644 index 0000000000..dc82c78ee5 --- /dev/null +++ b/engines/tony/gfxcore.cpp @@ -0,0 +1,2158 @@ +/* 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. + * + */ + +/* + * This code is based on original Tony Tough source code + * + * Copyright (c) 1997-2003 Nayma Software + */ + +#include "tony/gfxengine.h" +#include "tony/mpal/mpalutils.h" +#include "tony/tony.h" + +namespace Tony { + +/****************************************************************************\ +* RMGfxTask Methods +\****************************************************************************/ + +RMGfxTask::RMGfxTask() { + _nPrior = 0; + _nInList = 0; +} + +int RMGfxTask::priority() { + return _nPrior; +} + +void RMGfxTask::removeThis(CORO_PARAM, bool &result) { + result = true; +} + +/** + * Registration + */ +void RMGfxTask::Register() { + _nInList++; +} + +void RMGfxTask::unregister() { + _nInList--; + assert(_nInList >= 0); +} + +/****************************************************************************\ +* RMGfxTaskSetPrior Methods +\****************************************************************************/ + +void RMGfxTaskSetPrior::setPriority(int nPrior) { + _nPrior = nPrior; +} + +/****************************************************************************\ +* RMGfxBuffer Methods +\****************************************************************************/ + +RMGfxBuffer::RMGfxBuffer() { + _dimx = _dimy = 0; + _origBuf = _buf = NULL; +} + +RMGfxBuffer::~RMGfxBuffer() { + destroy(); +} + +void RMGfxBuffer::create(int dimx, int dimy, int nBpp) { + // Destroy the buffer it is already exists + if (_buf != NULL) + destroy(); + + // Copy the parameters in the private members + _dimx = dimx; + _dimy = dimy; + + // Allocate a buffer + _origBuf = _buf = new byte[_dimx * _dimy * nBpp / 8]; + assert(_buf != NULL); + Common::fill(_origBuf, _origBuf + _dimx * _dimy * nBpp / 8, 0); +} + +void RMGfxBuffer::destroy() { + if (_origBuf != NULL && _origBuf == _buf) { + delete[] _origBuf; + _origBuf = _buf = NULL; + } +} + +void RMGfxBuffer::offsetY(int nLines, int nBpp) { + _buf += nLines * getDimx() * nBpp / 8; +} + +RMGfxBuffer::operator byte *() { + return _buf; +} + +RMGfxBuffer::operator void *() { + return (void *)_buf; +} + +RMGfxBuffer::RMGfxBuffer(int dimx, int dimy, int nBpp) { + create(dimx, dimy, nBpp); +} + +int RMGfxBuffer::getDimx() { + return _dimx; +} + +int RMGfxBuffer::getDimy() { + return _dimy; +} + +/****************************************************************************\ +* RMGfxSourceBuffer Methods +\****************************************************************************/ + +int RMGfxSourceBuffer::init(const byte *buf, int dimx, int dimy, bool bLoadPalette) { + create(dimx, dimy, getBpp()); + memcpy(_buf, buf, dimx * dimy * getBpp() / 8); + + // Invokes the method for preparing the surface (inherited) + prepareImage(); + + return dimx * dimy * getBpp() / 8; +} + +void RMGfxSourceBuffer::init(Common::ReadStream &ds, int dimx, int dimy, bool bLoadPalette) { + create(dimx, dimy, getBpp()); + ds.read(_buf, dimx * dimy * getBpp() / 8); + + // Invokes the method for preparing the surface (inherited) + prepareImage(); +} + +RMGfxSourceBuffer::~RMGfxSourceBuffer() { +} + +void RMGfxSourceBuffer::prepareImage() { + // Do nothing. Can be overloaded if necessary +} + +bool RMGfxSourceBuffer::clip2D(int &x1, int &y1, int &u, int &v, int &width, int &height, bool bUseSrc, RMGfxTargetBuffer *buf) { + int destw, desth; + + destw = buf->getDimx(); + desth = buf->getDimy(); + + if (!bUseSrc) { + u = v = 0; + width = _dimx; + height = _dimy; + } + + if (x1 > destw - 1) + return false; + + if (y1 > desth - 1) + return false; + + if (x1 < 0) { + width += x1; + if (width < 0) + return false; + u -= x1; + x1 = 0; + } + + if (y1 < 0) { + height += y1; + if (height < 0) + return false; + v -= y1; + y1 = 0; + } + + if (x1 + width - 1 > destw - 1) + width = destw - x1; + + if (y1 + height - 1 > desth - 1) + height = desth - y1; + + return (width > 1 && height > 1); +} + +/** + * Initializes a surface by resource Id + * + * @param resID Resource ID + * @param dimx Buffer X dimension + * @param dimy Buffer Y dimension + */ +int RMGfxSourceBuffer::init(uint32 resID, int dimx, int dimy, bool bLoadPalette) { + return init(RMRes(resID), dimx, dimy, bLoadPalette); +} + +void RMGfxSourceBuffer::offsetY(int nLines) { + RMGfxBuffer::offsetY(nLines, getBpp()); +} + +/****************************************************************************\ +* RMGfxWoodyBuffer Methods +\****************************************************************************/ + +RMGfxWoodyBuffer::~RMGfxWoodyBuffer() { +} + +void RMGfxWoodyBuffer::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + // Draw the OT list + CORO_INVOKE_0(drawOT); + + // Draw itself into the target buffer + CORO_INVOKE_2(RMGfxSourceBuffer16::draw, bigBuf, prim); + + CORO_END_CODE; +} + +RMGfxWoodyBuffer::RMGfxWoodyBuffer() { +} + +RMGfxWoodyBuffer::RMGfxWoodyBuffer(int dimx, int dimy) + : RMGfxBuffer(dimx, dimy, 16) { +} + +/****************************************************************************\ +* RMGfxTargetBuffer Methods +\****************************************************************************/ + +RMGfxTargetBuffer::RMGfxTargetBuffer() { + _otlist = NULL; + _otSize = 0; + _trackDirtyRects = false; +} + +RMGfxTargetBuffer::~RMGfxTargetBuffer() { + clearOT(); +} + +void RMGfxTargetBuffer::clearOT() { + OTList *cur, *n; + + cur = _otlist; + + while (cur != NULL) { + cur->_prim->_task->unregister(); + delete cur->_prim; + n = cur->_next; + delete cur; + cur = n; + } + + _otlist = NULL; +} + +void RMGfxTargetBuffer::drawOT(CORO_PARAM) { + CORO_BEGIN_CONTEXT; + OTList *cur; + OTList *prev; + OTList *next; + RMGfxPrimitive *myprim; + bool result; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + _ctx->prev = NULL; + _ctx->cur = _otlist; + + while (_ctx->cur != NULL) { + // Call the task Draw method, passing it a copy of the original + _ctx->myprim = _ctx->cur->_prim->duplicate(); + CORO_INVOKE_2(_ctx->cur->_prim->_task->draw, *this, _ctx->myprim); + delete _ctx->myprim; + + // Check if it's time to remove the task from the OT list + CORO_INVOKE_1(_ctx->cur->_prim->_task->removeThis, _ctx->result); + if (_ctx->result) { + // De-register the task + _ctx->cur->_prim->_task->unregister(); + + // Delete task, freeing the memory + delete _ctx->cur->_prim; + _ctx->next = _ctx->cur->_next; + delete _ctx->cur; + + // If it was the first item, update the list head + if (_ctx->prev == NULL) + _otlist = _ctx->next; + // Otherwise update the next pinter of the previous item + else + _ctx->prev->_next = _ctx->next; + + _ctx->cur = _ctx->next; + } else { + // Update the pointer to the previous item, and the current to the next + _ctx->prev = _ctx->cur; + _ctx->cur = _ctx->cur->_next; + } + } + + CORO_END_CODE; +} + +void RMGfxTargetBuffer::addPrim(RMGfxPrimitive *prim) { + int nPrior; + OTList *cur, *n; + + // Warn of the OT listing + prim->_task->Register(); + + // Check the priority + nPrior = prim->_task->priority(); + n = new OTList(prim); + + // Empty list + if (_otlist == NULL) { + _otlist = n; + _otlist->_next = NULL; + } + // Inclusion in the head + else if (nPrior < _otlist->_prim->_task->priority()) { + n->_next = _otlist; + _otlist = n; + } else { + cur = _otlist; + while (cur->_next != NULL && nPrior > cur->_next->_prim->_task->priority()) + cur = cur->_next; + + n->_next = cur->_next; + cur->_next = n; + } +} + +void RMGfxTargetBuffer::addDirtyRect(const Common::Rect &r) { + assert(r.isValidRect()); + if (_trackDirtyRects && r.width() > 0 && r.height() > 0) + _currentDirtyRects.push_back(r); +} + +Common::List<Common::Rect> &RMGfxTargetBuffer::getDirtyRects() { + // Copy rects from both the current and previous frame into the output dirty rects list + Common::List<Common::Rect>::iterator i; + _dirtyRects.clear(); + for (i = _previousDirtyRects.begin(); i != _previousDirtyRects.end(); ++i) + _dirtyRects.push_back(*i); + for (i = _currentDirtyRects.begin(); i != _currentDirtyRects.end(); ++i) + _dirtyRects.push_back(*i); + + mergeDirtyRects(); + return _dirtyRects; +} + +/** + * Move the set of dirty rects from the finished current frame into the previous frame list. + */ +void RMGfxTargetBuffer::clearDirtyRects() { + Common::List<Common::Rect>::iterator i; + _previousDirtyRects.clear(); + for (i = _currentDirtyRects.begin(); i != _currentDirtyRects.end(); ++i) + _previousDirtyRects.push_back(*i); + + _currentDirtyRects.clear(); +} + +/** + * Merges any clipping rectangles that overlap to try and reduce + * the total number of clip rectangles. + */ +void RMGfxTargetBuffer::mergeDirtyRects() { + if (_dirtyRects.size() <= 1) + return; + + Common::List<Common::Rect>::iterator rOuter, rInner; + + for (rOuter = _dirtyRects.begin(); rOuter != _dirtyRects.end(); ++rOuter) { + rInner = rOuter; + while (++rInner != _dirtyRects.end()) { + + if ((*rOuter).intersects(*rInner)) { + // these two rectangles overlap or + // are next to each other - merge them + + (*rOuter).extend(*rInner); + + // remove the inner rect from the list + _dirtyRects.erase(rInner); + + // move back to beginning of list + rInner = rOuter; + } + } + } +} + +uint16 *RMGfxTargetBuffer::_precalcTable = NULL; + +/** + * Set up the black & white precalculated mapping table. This is only + * called if the user selects the black & white option. + */ +void RMGfxTargetBuffer::createBWPrecalcTable() { + _precalcTable = new uint16[0x8000]; + + for (int i = 0; i < 0x8000; i++) { + int r = (i >> 10) & 0x1F; + int g = (i >> 5) & 0x1F; + int b = i & 0x1F; + + int min = MIN(r, MIN(g, b)); + int max = MAX(r, MAX(g, b)); + + min = (min + max) / 2; + + r = CLIP(min + 8 - 8, 0, 31); + g = CLIP(min + 5 - 8, 0, 31); + b = CLIP(min + 0 - 8, 0, 31); + + _precalcTable[i] = (r << 10) | (g << 5) | b; + } +} + +/** + * Frees the black & white precalculated mapping table. + */ +void RMGfxTargetBuffer::freeBWPrecalcTable() { + delete[] _precalcTable; + _precalcTable = NULL; +} + +RMGfxTargetBuffer::operator byte *() { + return _buf; +} + +RMGfxTargetBuffer::operator void *() { + return (void *)_buf; +} + +RMGfxTargetBuffer::operator uint16 *() { + // FIXME: This may not be endian safe + return (uint16 *)_buf; +} + +/** + * Offseting buffer + */ +void RMGfxTargetBuffer::offsetY(int nLines) { + RMGfxBuffer::offsetY(nLines, 16); +} + +void RMGfxTargetBuffer::setTrackDirtyRects(bool v) { + _trackDirtyRects = v; +} + +bool RMGfxTargetBuffer::getTrackDirtyRects() const { + return _trackDirtyRects; +} + +/****************************************************************************\ +* RMGfxSourceBufferPal Methods +\****************************************************************************/ + +RMGfxSourceBufferPal::~RMGfxSourceBufferPal() { +} + +int RMGfxSourceBufferPal::loadPaletteWA(const byte *buf, bool bSwapped) { + if (bSwapped) { + for (int i = 0; i < (1 << getBpp()); i++) { + _pal[i * 3 + 0] = buf[i * 3 + 2]; + _pal[i * 3 + 1] = buf[i * 3 + 1]; + _pal[i * 3 + 2] = buf[i * 3 + 0]; + } + } else { + memcpy(_pal, buf, (1 << getBpp()) * 3); + } + + preparePalette(); + + return (1 << getBpp()) * 3; +} + +int RMGfxSourceBufferPal::loadPalette(const byte *buf) { + for (int i = 0; i < 256; i++) + memcpy(_pal + i * 3, buf + i * 4, 3); + + preparePalette(); + + return (1 << getBpp()) * 4; +} + +void RMGfxSourceBufferPal::preparePalette() { + for (int i = 0; i < 256; i++) { + _palFinal[i] = (((int)_pal[i * 3 + 0] >> 3) << 10) | + (((int)_pal[i * 3 + 1] >> 3) << 5) | + (((int)_pal[i * 3 + 2] >> 3) << 0); + } +} + +int RMGfxSourceBufferPal::init(const byte *buf, int dimx, int dimy, bool bLoadPalette) { + // Load the RAW image + int read = RMGfxSourceBuffer::init(buf, dimx, dimy); + + // Load the palette if necessary + if (bLoadPalette) + read += loadPaletteWA(&buf[read]); + + return read; +} + +void RMGfxSourceBufferPal::init(Common::ReadStream &ds, int dimx, int dimy, bool bLoadPalette) { + // Load the RAW image + RMGfxSourceBuffer::init(ds, dimx, dimy); + + // Load the palette if necessary + if (bLoadPalette) { + byte *suxpal = new byte[256 * 3]; + ds.read(suxpal, 256 * 3); + loadPaletteWA(suxpal); + delete[] suxpal; + } +} + +int RMGfxSourceBufferPal::loadPalette(uint32 resID) { + return loadPalette(RMRes(resID)); +} + +int RMGfxSourceBufferPal::loadPaletteWA(uint32 resID, bool bSwapped) { + return loadPaletteWA(RMRes(resID), bSwapped); +} + +/****************************************************************************\ +* RMGfxSourceBuffer4 Methods +\****************************************************************************/ + +void RMGfxSourceBuffer4::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) { +} + +RMGfxSourceBuffer4::RMGfxSourceBuffer4(int dimx, int dimy) + : RMGfxBuffer(dimx, dimy, 4) { + setPriority(0); +} + +/** + * Returns the number of bits per pixel of the surface + * + * @returns Bit per pixel + */ +int RMGfxSourceBuffer4::getBpp() { + return 4; +} + +void RMGfxSourceBuffer4::create(int dimx, int dimy) { + RMGfxBuffer::create(dimx, dimy, 4); +} + +/****************************************************************************\ +* RMGfxSourceBuffer8 Methods +\****************************************************************************/ + +RMGfxSourceBuffer8::~RMGfxSourceBuffer8() { +} + +void RMGfxSourceBuffer8::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) { + int width = 0, height = 0, u = 0, v = 0; + int bufx = bigBuf.getDimx(); + uint16 *buf = bigBuf; + byte *raw = _buf; + + // Destination buffer + RMRect dst; + if (prim->haveDst()) + dst = prim->getDst(); + + // Clipping + if (prim->haveSrc()) { + u = prim->getSrc()._x1; + v = prim->getSrc()._y1; + + width = prim->getSrc().width(); + height = prim->getSrc().height(); + } + + if (!clip2D(dst._x1, dst._y1, u, v, width, height, prim->haveSrc(), &bigBuf)) + return; + + // Starting offset into the buffer + buf += dst._y1 * bufx + dst._x1; + + // Normal step + if (_bTrasp0) { + for (int y = 0; y < height; y++) { + raw = _buf + (y + v) * _dimx + u; + + for (int x = 0; x < width; x++) { + if (*raw) + *buf = _palFinal[*raw]; + buf++; + raw++; + } + + buf += bufx - width; + } + } else { + for (int y = 0; y < height; y++) { + raw = _buf + (y + v) * _dimx + u; + + for (int x = 0; x < width; x += 2) { + buf[0] = _palFinal[raw[0]]; + buf[1] = _palFinal[raw[1]]; + + buf += 2; + raw += 2; + } + + buf += bufx - width; + } + } + + // Specify the drawn area + bigBuf.addDirtyRect(Common::Rect(dst._x1, dst._y1, dst._x1 + width, dst._y1 + height)); +} + +RMGfxSourceBuffer8::RMGfxSourceBuffer8(int dimx, int dimy) + : RMGfxBuffer(dimx, dimy, 8) { + setPriority(0); + _bTrasp0 = false; +} + +RMGfxSourceBuffer8::RMGfxSourceBuffer8(bool bTrasp0) { + _bTrasp0 = bTrasp0; +} + +/** + * Returns the number of bits per pixel of the surface + * + * @returns Bit per pixel + */ +int RMGfxSourceBuffer8::getBpp() { + return 8; +} + +void RMGfxSourceBuffer8::create(int dimx, int dimy) { + RMGfxBuffer::create(dimx, dimy, 8); +} + +#define GETRED(x) (((x) >> 10) & 0x1F) +#define GETGREEN(x) (((x) >> 5) & 0x1F) +#define GETBLUE(x) ((x) & 0x1F) + +/****************************************************************************\ +* RMGfxSourceBuffer8AB Methods +\****************************************************************************/ + +RMGfxSourceBuffer8AB::~RMGfxSourceBuffer8AB() { +} + +int RMGfxSourceBuffer8AB::calcTrasp(int fore, int back) { + int r = (GETRED(fore) >> 2) + (GETRED(back) >> 1); + int g = (GETGREEN(fore) >> 2) + (GETGREEN(back) >> 1); + int b = (GETBLUE(fore) >> 2) + (GETBLUE(back) >> 1); + + if (r > 0x1F) + r = 0x1F; + + if (g > 0x1F) + g = 0x1F; + + if (b > 0x1F) + b = 0x1F; + + return (r << 10) | (g << 5) | b; +} + +void RMGfxSourceBuffer8AB::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) { + int width = 0, height = 0, u = 0, v = 0; + int bufx = bigBuf.getDimx(); + uint16 *buf = bigBuf; + byte *raw = _buf; + + // Destination buffer + RMRect dst; + if (prim->haveDst()) + dst = prim->getDst(); + + // Clipping + if (prim->haveSrc()) { + u = prim->getSrc()._x1; + v = prim->getSrc()._y1; + + width = prim->getSrc().width(); + height = prim->getSrc().height(); + } + + if (!clip2D(dst._x1, dst._y1, u, v, width, height, prim->haveSrc(), &bigBuf)) + return; + + // Starting offset into the buffer + buf += dst._y1 * bufx + dst._x1; + + // Passaggio normale + if (_bTrasp0) { + for (int y = 0; y < height; y++) { + raw = _buf + (y + v) * _dimx + u; + + for (int x = 0; x < width; x++) { + if (*raw) + *buf = calcTrasp(_palFinal[*raw], *buf); + + buf++; + raw++; + } + + buf += bufx - width; + } + } else { + for (int y = 0; y < height; y++) { + raw = _buf + (y + v) * _dimx + u; + + for (int x = 0; x < width; x += 2) { + buf[0] = calcTrasp(_palFinal[raw[0]], buf[0]); + buf[1] = calcTrasp(_palFinal[raw[1]], buf[1]); + + buf += 2; + raw += 2; + } + + buf += bufx - width; + } + } + + // Specify the drawn area + bigBuf.addDirtyRect(Common::Rect(dst._x1, dst._y1, dst._x1 + width, dst._y1 + height)); +} + +/****************************************************************************\ +* RMGfxSourceBuffer8RLE Methods +\****************************************************************************/ + +byte RMGfxSourceBuffer8RLE::_megaRLEBuf[512 * 1024]; + +void RMGfxSourceBuffer8RLE::setAlphaBlendColor(int color) { + _alphaBlendColor = color; +} + +RMGfxSourceBuffer8RLE::RMGfxSourceBuffer8RLE() { + _alphaBlendColor = -1; + _bNeedRLECompress = true; + _buf = NULL; + + _alphaR = _alphaG = _alphaB = 0; +} + +RMGfxSourceBuffer8RLE::~RMGfxSourceBuffer8RLE() { + if (_buf != NULL) { + delete[] _buf; + _buf = NULL; + } +} + +int RMGfxSourceBuffer8RLE::init(const byte *buf, int dimx, int dimy, bool bLoadPalette) { + return RMGfxSourceBufferPal::init(buf, dimx, dimy, bLoadPalette); +} + +void RMGfxSourceBuffer8RLE::init(Common::ReadStream &ds, int dimx, int dimy, bool bLoadPalette) { + if (_bNeedRLECompress) { + RMGfxSourceBufferPal::init(ds, dimx, dimy, bLoadPalette); + } else { + int size = ds.readSint32LE(); + _buf = new byte[size]; + ds.read(_buf, size); + + _dimx = dimx; + _dimy = dimy; + } +} + +void RMGfxSourceBuffer8RLE::preparePalette() { + // Invoke the parent method + RMGfxSourceBuffer8::preparePalette(); + + // Handle RGB alpha blending + if (_alphaBlendColor != -1) { + _alphaR = (_palFinal[_alphaBlendColor] >> 10) & 0x1F; + _alphaG = (_palFinal[_alphaBlendColor] >> 5) & 0x1F; + _alphaB = (_palFinal[_alphaBlendColor]) & 0x1F; + } +} + +void RMGfxSourceBuffer8RLE::prepareImage() { + // Invoke the parent method + RMGfxSourceBuffer::prepareImage(); + + // Compress + compressRLE(); +} + +void RMGfxSourceBuffer8RLE::setAlreadyCompressed() { + _bNeedRLECompress = false; +} + +void RMGfxSourceBuffer8RLE::compressRLE() { + byte *startline; + byte *cur; + byte curdata; + byte *src; + byte *startsrc; + int rep; + + // Perform RLE compression for lines + cur = _megaRLEBuf; + src = _buf; + for (int y = 0; y < _dimy; y++) { + // Save the beginning of the line + startline = cur; + + // Leave space for the length of the line + cur += 2; + + // It starts from the empty space + curdata = 0; + rep = 0; + startsrc = src; + for (int x = 0; x < _dimx;) { + if ((curdata == 0 && *src == 0) || (curdata == 1 && *src == _alphaBlendColor) + || (curdata == 2 && (*src != _alphaBlendColor && *src != 0))) { + src++; + rep++; + x++; + } else { + if (curdata == 0) { + rleWriteTrasp(cur, rep); + curdata++; + } else if (curdata == 1) { + rleWriteAlphaBlend(cur, rep); + curdata++; + } else { + rleWriteData(cur, rep, startsrc); + curdata = 0; + } + + rep = 0; + startsrc = src; + } + } + + // Pending data? + if (curdata == 1) { + rleWriteAlphaBlend(cur, rep); + rleWriteData(cur, 0, NULL); + } + + if (curdata == 2) { + rleWriteData(cur, rep, startsrc); + } + + // End of line + rleWriteEOL(cur); + + // Write the length of the line + WRITE_LE_UINT16(startline, (uint16)(cur - startline)); + } + + // Delete the original image + delete[] _buf; + + // Copy the compressed image + int bufSize = cur - _megaRLEBuf; + _buf = new byte[bufSize]; + Common::copy(_megaRLEBuf, _megaRLEBuf + bufSize, _buf); +} + +void RMGfxSourceBuffer8RLE::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) { + byte *src; + uint16 *buf = bigBuf; + int u, v, width, height; + + // Clipping + int x1 = prim->getDst()._x1; + int y1 = prim->getDst()._y1; + if (!clip2D(x1, y1, u, v, width, height, false, &bigBuf)) + return; + + // Go forward through the RLE lines + src = _buf; + for (int y = 0; y < v; y++) + src += READ_LE_UINT16(src); + + // Calculate the position in the destination buffer + buf += y1 * bigBuf.getDimx(); + + // Loop + if (prim->isFlipped()) { +// Eliminate horizontal clipping +// width = m_dimx; +// x1=prim->Dst().x1; + + // Clipping + u = _dimx - (width + u); + x1 = (prim->getDst()._x1 + _dimx - 1) - u; + + if (width > x1) + width = x1; + + // Specify the drawn area + bigBuf.addDirtyRect(Common::Rect(x1 - width, y1, x1 + 1, y1 + height)); + + for (int y = 0; y < height; y++) { + // Decompression + rleDecompressLineFlipped(buf + x1, src + 2, u, width); + + // Next line + src += READ_LE_UINT16(src); + + // Skip to the next line + buf += bigBuf.getDimx(); + } + } else { + // Specify the drawn area + bigBuf.addDirtyRect(Common::Rect(x1, y1, x1 + width, y1 + height)); + + for (int y = 0; y < height; y++) { + // Decompression + rleDecompressLine(buf + x1, src + 2, u, width); + + // Next line + src += READ_LE_UINT16(src); + + // Skip to the next line + buf += bigBuf.getDimx(); + } + } +} + +/****************************************************************************\ +* RMGfxSourceBuffer8RLEByte Methods +\****************************************************************************/ + +RMGfxSourceBuffer8RLEByte::~RMGfxSourceBuffer8RLEByte() { +} + +void RMGfxSourceBuffer8RLEByte::rleWriteTrasp(byte *&cur, int rep) { + assert(rep < 255); + *cur ++ = rep; +} + +void RMGfxSourceBuffer8RLEByte::rleWriteAlphaBlend(byte *&cur, int rep) { + assert(rep < 255); + *cur ++ = rep; +} + +void RMGfxSourceBuffer8RLEByte::rleWriteData(byte *&cur, int rep, byte *src) { + assert(rep < 256); + + *cur ++ = rep; + if (rep > 0) { + memcpy(cur, src, rep); + cur += rep; + src += rep; + } + + return; +} + +void RMGfxSourceBuffer8RLEByte::rleWriteEOL(byte *&cur) { + *cur ++ = 0xFF; +} + +void RMGfxSourceBuffer8RLEByte::rleDecompressLine(uint16 *dst, byte *src, int nStartSkip, int nLength) { + int n; + + if (nStartSkip == 0) + goto RLEByteDoTrasp; + + while (1) { + assert(nStartSkip > 0); + + // TRASP + n = *src++; + if (n == 0xFF) + return; + + if (n >= nStartSkip) { + dst += n - nStartSkip; + nLength -= n - nStartSkip; + if (nLength > 0) + goto RLEByteDoAlpha; + else + return; + } + nStartSkip -= n; + + + assert(nStartSkip > 0); + + // ALPHA + n = *src++; + if (n >= nStartSkip) { + n -= nStartSkip; + goto RLEByteDoAlpha2; + } + nStartSkip -= n; + + assert(nStartSkip > 0); + + // DATA + n = *src++; + if (n >= nStartSkip) { + src += nStartSkip; + n -= nStartSkip; + goto RLEByteDoCopy2; + } + nStartSkip -= n; + src += n; + } + + while (1) { +RLEByteDoTrasp: + // Get the trasp of s**t + n = *src++; + + // EOL? + if (n == 0xFF) + return; + + dst += n; + nLength -= n; + if (nLength <= 0) + return; + +RLEByteDoAlpha: + // Alpha + n = *src++; + +RLEByteDoAlpha2: + if (n > nLength) + n = nLength; + for (int i = 0; i < n; i++) { + int r = (*dst >> 10) & 0x1F; + int g = (*dst >> 5) & 0x1F; + int b = *dst & 0x1F; + + r = (r >> 2) + (_alphaR >> 1); + g = (g >> 2) + (_alphaG >> 1); + b = (b >> 2) + (_alphaB >> 1); + + *dst ++ = (r << 10) | (g << 5) | b; + } + + nLength -= n; + if (!nLength) + return; + assert(nLength > 0); + +//RLEByteDoCopy: + // Copy the stuff + n = *src++; + +RLEByteDoCopy2: + if (n > nLength) + n = nLength; + + for (int i = 0; i < n; i++) + *dst ++ = _palFinal[*src++]; + + nLength -= n; + if (!nLength) + return; + assert(nLength > 0); + } +} + +void RMGfxSourceBuffer8RLEByte::rleDecompressLineFlipped(uint16 *dst, byte *src, int nStartSkip, int nLength) { + int n; + + if (nStartSkip == 0) + goto RLEByteFlippedDoTrasp; + + while (1) { + assert(nStartSkip > 0); + + // TRASP + n = *src++; + if (n == 0xFF) + return; + + if (n >= nStartSkip) { + dst -= n - nStartSkip; + nLength -= n - nStartSkip; + if (nLength > 0) + goto RLEByteFlippedDoAlpha; + else + return; + } + nStartSkip -= n; + + + assert(nStartSkip > 0); + + // ALPHA + n = *src++; + if (n >= nStartSkip) { + n -= nStartSkip; + goto RLEByteFlippedDoAlpha2; + } + nStartSkip -= n; + + assert(nStartSkip > 0); + + // DATA + n = *src++; + if (n >= nStartSkip) { + src += nStartSkip; + n -= nStartSkip; + goto RLEByteFlippedDoCopy2; + } + nStartSkip -= n; + src += n; + } + + while (1) { +RLEByteFlippedDoTrasp: + // Get the trasp of s**t + n = *src++; + + // EOL? + if (n == 0xFF) + return; + + dst -= n; + nLength -= n; + if (nLength <= 0) + return; + +RLEByteFlippedDoAlpha: + // Alpha + n = *src++; + +RLEByteFlippedDoAlpha2: + if (n > nLength) + n = nLength; + for (int i = 0; i < n; i++) { + int r = (*dst >> 10) & 0x1F; + int g = (*dst >> 5) & 0x1F; + int b = *dst & 0x1F; + + r = (r >> 2) + (_alphaR >> 1); + g = (g >> 2) + (_alphaG >> 1); + b = (b >> 2) + (_alphaB >> 1); + + *dst-- = (r << 10) | (g << 5) | b; + } + + nLength -= n; + if (!nLength) + return; + assert(nLength > 0); + +//RLEByteFlippedDoCopy: + // Copy the data + n = *src++; + +RLEByteFlippedDoCopy2: + if (n > nLength) + n = nLength; + + for (int i = 0; i < n; i++) + *dst-- = _palFinal[*src++]; + + nLength -= n; + if (!nLength) + return; + assert(nLength > 0); + } +} + +/****************************************************************************\ +* RMGfxSourceBuffer8RLEWord Methods +\****************************************************************************/ + +RMGfxSourceBuffer8RLEWord::~RMGfxSourceBuffer8RLEWord() { +} + +void RMGfxSourceBuffer8RLEWord::rleWriteTrasp(byte *&cur, int rep) { + WRITE_LE_UINT16(cur, rep); + cur += 2; +} + +void RMGfxSourceBuffer8RLEWord::rleWriteAlphaBlend(byte *&cur, int rep) { + WRITE_LE_UINT16(cur, rep); + cur += 2; +} + +void RMGfxSourceBuffer8RLEWord::rleWriteData(byte *&cur, int rep, byte *src) { + WRITE_LE_UINT16(cur, rep); + cur += 2; + + if (rep > 0) { + memcpy(cur, src, rep); + cur += rep; + src += rep; + } +} + +void RMGfxSourceBuffer8RLEWord::rleWriteEOL(byte *&cur) { + *cur ++ = 0xFF; + *cur ++ = 0xFF; +} + +void RMGfxSourceBuffer8RLEWord::rleDecompressLine(uint16 *dst, byte *src, int nStartSkip, int nLength) { + int n; + + if (nStartSkip == 0) + goto RLEWordDoTrasp; + + while (1) { + assert(nStartSkip > 0); + + // TRASP + n = READ_LE_UINT16(src); + src += 2; + + if (n == 0xFFFF) + return; + + if (n >= nStartSkip) { + dst += n - nStartSkip; + nLength -= n - nStartSkip; + + if (nLength > 0) + goto RLEWordDoAlpha; + else + return; + } + nStartSkip -= n; + + assert(nStartSkip > 0); + + // ALPHA + n = READ_LE_UINT16(src); + src += 2; + + if (n >= nStartSkip) { + n -= nStartSkip; + goto RLEWordDoAlpha2; + } + nStartSkip -= n; + + // DATA + n = READ_LE_UINT16(src); + src += 2; + + if (n >= nStartSkip) { + src += nStartSkip; + n -= nStartSkip; + goto RLEWordDoCopy2; + } + nStartSkip -= n; + src += n; + } + + while (1) { +RLEWordDoTrasp: + // Get the trasp of s**t + n = READ_LE_UINT16(src); + src += 2; + + // EOL? + if (n == 0xFFFF) + return; + + dst += n; + + nLength -= n; + if (nLength <= 0) + return; + +RLEWordDoAlpha: + n = READ_LE_UINT16(src); + src += 2; + +RLEWordDoAlpha2: + if (n > nLength) + n = nLength; + + for (int i = 0; i < n; i++) { + int r = (*dst >> 10) & 0x1F; + int g = (*dst >> 5) & 0x1F; + int b = *dst & 0x1F; + + r = (r >> 2) + (_alphaR >> 1); + g = (g >> 2) + (_alphaG >> 1); + b = (b >> 2) + (_alphaB >> 1); + + *dst++ = (r << 10) | (g << 5) | b; + } + + nLength -= n; + if (!nLength) + return; + + assert(nLength > 0); + +//RLEWordDoCopy: + // Copy the data + n = READ_LE_UINT16(src); + src += 2; + +RLEWordDoCopy2: + if (n > nLength) + n = nLength; + + for (int i = 0; i < n; i++) + *dst++ = _palFinal[*src++]; + + nLength -= n; + if (!nLength) + return; + + assert(nLength > 0); + } +} + +void RMGfxSourceBuffer8RLEWord::rleDecompressLineFlipped(uint16 *dst, byte *src, int nStartSkip, int nLength) { + int n; + + if (nStartSkip == 0) + goto RLEWordFlippedDoTrasp; + + while (1) { + assert(nStartSkip > 0); + + // TRASP + n = READ_LE_UINT16(src); + src += 2; + + if (n == 0xFFFF) + return; + + if (n >= nStartSkip) { + dst -= n - nStartSkip; + nLength -= n - nStartSkip; + + if (nLength > 0) + goto RLEWordFlippedDoAlpha; + else + return; + } + nStartSkip -= n; + + assert(nStartSkip > 0); + + // ALPHA + n = READ_LE_UINT16(src); + src += 2; + + if (n >= nStartSkip) { + n -= nStartSkip; + goto RLEWordFlippedDoAlpha2; + } + nStartSkip -= n; + + // DATA + n = READ_LE_UINT16(src); + src += 2; + + if (n >= nStartSkip) { + src += nStartSkip; + n -= nStartSkip; + goto RLEWordFlippedDoCopy2; + } + nStartSkip -= n; + src += n; + } + + while (1) { +RLEWordFlippedDoTrasp: + // Get the trasp of s**t + n = READ_LE_UINT16(src); + src += 2; + + // EOL? + if (n == 0xFFFF) + return; + + dst -= n; + + nLength -= n; + if (nLength <= 0) + return; + +RLEWordFlippedDoAlpha: + n = READ_LE_UINT16(src); + src += 2; + +RLEWordFlippedDoAlpha2: + if (n > nLength) + n = nLength; + + for (int i = 0; i < n; i++) { + int r = (*dst >> 10) & 0x1F; + int g = (*dst >> 5) & 0x1F; + int b = *dst & 0x1F; + + r = (r >> 2) + (_alphaR >> 1); + g = (g >> 2) + (_alphaG >> 1); + b = (b >> 2) + (_alphaB >> 1); + + *dst-- = (r << 10) | (g << 5) | b; + } + + nLength -= n; + if (!nLength) + return; + + assert(nLength > 0); + +//RLEWordFlippedDoCopy: + // Copy the data + n = READ_LE_UINT16(src); + src += 2; + +RLEWordFlippedDoCopy2: + if (n > nLength) + n = nLength; + + for (int i = 0; i < n; i++) + *dst-- = _palFinal[*src++]; + + nLength -= n; + if (!nLength) + return; + + assert(nLength > 0); + } +} + +/****************************************************************************\ +* Methods for RMGfxSourceBuffer8RLEWord +\****************************************************************************/ + +RMGfxSourceBuffer8RLEWordAB::~RMGfxSourceBuffer8RLEWordAB() { +} + +void RMGfxSourceBuffer8RLEWordAB::rleDecompressLine(uint16 *dst, byte *src, int nStartSkip, int nLength) { + int n; + + if (!GLOBALS._bCfgTransparence) { + RMGfxSourceBuffer8RLEWord::rleDecompressLine(dst, src, nStartSkip, nLength); + return; + } + + if (nStartSkip == 0) + goto RLEWordDoTrasp; + + while (1) { + assert(nStartSkip > 0); + + // TRASP + n = READ_LE_UINT16(src); + src += 2; + + if (n == 0xFFFF) + return; + + if (n >= nStartSkip) { + dst += n - nStartSkip; + nLength -= n - nStartSkip; + + if (nLength > 0) + goto RLEWordDoAlpha; + else + return; + } + nStartSkip -= n; + + assert(nStartSkip > 0); + + // ALPHA + n = READ_LE_UINT16(src); + src += 2; + + if (n >= nStartSkip) { + n -= nStartSkip; + goto RLEWordDoAlpha2; + } + nStartSkip -= n; + + // DATA + n = READ_LE_UINT16(src); + src += 2; + + if (n >= nStartSkip) { + src += nStartSkip; + n -= nStartSkip; + goto RLEWordDoCopy2; + } + nStartSkip -= n; + src += n; + } + + while (1) { +RLEWordDoTrasp: + // Get the trasp of s**t + n = READ_LE_UINT16(src); + src += 2; + + // EOL? + if (n == 0xFFFF) + return; + + dst += n; + + nLength -= n; + if (nLength <= 0) + return; + +RLEWordDoAlpha: + n = READ_LE_UINT16(src); + src += 2; + +RLEWordDoAlpha2: + if (n > nLength) + n = nLength; + + // @@@ SHOULD NOT BE THERE !!!!! + for (int i = 0; i < n; i++) { + int r = (*dst >> 10) & 0x1F; + int g = (*dst >> 5) & 0x1F; + int b = *dst & 0x1F; + + r = (r >> 2) + (_alphaR >> 1); + g = (g >> 2) + (_alphaG >> 1); + b = (b >> 2) + (_alphaB >> 1); + + *dst++ = (r << 10) | (g << 5) | b; + } + + nLength -= n; + if (!nLength) + return; + + assert(nLength > 0); + +//RLEWordDoCopy: + // Copy the data + n = READ_LE_UINT16(src); + src += 2; + +RLEWordDoCopy2: + if (n > nLength) + n = nLength; + + for (int i = 0; i < n; i++) { + int r = (*dst >> 10) & 0x1F; + int g = (*dst >> 5) & 0x1F; + int b = *dst & 0x1F; + + int r2 = (_palFinal[*src] >> 10) & 0x1F; + int g2 = (_palFinal[*src] >> 5) & 0x1F; + int b2 = _palFinal[*src] & 0x1F; + + r = (r >> 1) + (r2 >> 1); + g = (g >> 1) + (g2 >> 1); + b = (b >> 1) + (b2 >> 1); + + *dst ++ = (r << 10) | (g << 5) | b; + src++; + } + + nLength -= n; + if (!nLength) + return; + + assert(nLength > 0); + } +} + +/****************************************************************************\ +* Methods for RMGfxSourceBuffer8AA +\****************************************************************************/ + +byte RMGfxSourceBuffer8AA::_megaAABuf[256 * 1024]; +byte RMGfxSourceBuffer8AA::_megaAABuf2[64 * 1024]; + +void RMGfxSourceBuffer8AA::prepareImage() { + // Invoke the parent method + RMGfxSourceBuffer::prepareImage(); + + // Prepare the buffer for anti-aliasing + calculateAA(); +} + +void RMGfxSourceBuffer8AA::calculateAA() { + byte *src, *srcaa; + + // First pass: fill the edges + Common::fill(_megaAABuf, _megaAABuf + _dimx * _dimy, 0); + + src = _buf; + srcaa = _megaAABuf; + for (int y = 0; y < _dimy; y++) { + for (int x = 0; x < _dimx; x++) { + if (*src == 0) { + if ((y > 0 && src[-_dimx] != 0) || + (y < _dimy - 1 && src[_dimx] != 0) || + (x > 0 && src[-1] != 0) || + (x < _dimx - 1 && src[1] != 0)) + *srcaa = 1; + } + + src++; + srcaa++; + } + } + + src = _buf; + srcaa = _megaAABuf; + for (int y = 0; y < _dimy; y++) { + for (int x = 0; x < _dimx; x++) { + if (*src != 0) { + if ((y > 0 && srcaa[-_dimx] == 1) || + (y < _dimy - 1 && srcaa[_dimx] == 1) || + (x > 0 && srcaa[-1] == 1) || + (x < _dimx - 1 && srcaa[1] == 1)) + *srcaa = 2; + } + + src++; + srcaa++; + } + } + + if (_aabuf != NULL) + delete[] _aabuf; + + _aabuf = new byte[_dimx * _dimy]; + memcpy(_aabuf, _megaAABuf, _dimx * _dimy); +} + +RMGfxSourceBuffer8AA::RMGfxSourceBuffer8AA() : RMGfxSourceBuffer8() { + _aabuf = NULL; +} + +RMGfxSourceBuffer8AA::~RMGfxSourceBuffer8AA() { + if (_aabuf != NULL) + delete[] _aabuf; +} + +void RMGfxSourceBuffer8AA::drawAA(RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) { + byte *src; + uint16 *mybuf; + uint16 *buf; + int u, v, width, height; + + // Clip the sprite + int x1 = prim->getDst()._x1; + int y1 = prim->getDst()._y1; + if (!clip2D(x1, y1, u, v, width, height, false, &bigBuf)) + return; + + // Go forward through the RLE lines + src = _buf; + for (int y = 0; y < v; y++) + src += READ_LE_UINT16(src); + + // Eliminate horizontal clipping + if (prim->isFlipped()) { + u = _dimx - (width + u); + x1 = (prim->getDst()._x1 + _dimx - 1) - u; + + if (width > x1) + width = x1; + + // Specify the drawn area + bigBuf.addDirtyRect(Common::Rect(x1 - width, y1, x1 + 1, y1 + height)); + } else { + // Specify the drawn area + bigBuf.addDirtyRect(Common::Rect(x1, y1, x1 + width, y1 + height)); + } + + //width = _dimx; + //x1 = prim->Dst().x1; + + // Position into the destination buffer + buf = bigBuf; + buf += y1 * bigBuf.getDimx(); + + int step; + if (prim->isFlipped()) + step = -1; + else + step = 1; + + // Loop + buf += bigBuf.getDimx(); // Skip the first line + for (int y = 1; y < height - 1; y++) { + // if (prim->IsFlipped()) + // mybuf=&buf[x1+m_dimx-1]; + // else + mybuf = &buf[x1]; + + for (int x = 0; x < width; x++, mybuf += step) { + if (_aabuf[(y + v) * _dimx + x + u] == 2 && x != 0 && x != width - 1) { + int r = GETRED(mybuf[1]) + GETRED(mybuf[-1]) + GETRED(mybuf[-bigBuf.getDimx()]) + GETRED(mybuf[bigBuf.getDimx()]); + int g = GETGREEN(mybuf[1]) + GETGREEN(mybuf[-1]) + GETGREEN(mybuf[-bigBuf.getDimx()]) + GETGREEN(mybuf[bigBuf.getDimx()]); + int b = GETBLUE(mybuf[1]) + GETBLUE(mybuf[-1]) + GETBLUE(mybuf[-bigBuf.getDimx()]) + GETBLUE(mybuf[bigBuf.getDimx()]); + + r += GETRED(mybuf[0]); + g += GETGREEN(mybuf[0]); + b += GETBLUE(mybuf[0]); + + r /= 5; + g /= 5; + b /= 5; + + if (r > 31) + r = 31; + if (g > 31) + g = 31; + if (b > 31) + b = 31; + + mybuf[0] = (r << 10) | (g << 5) | b; + } + } + + // Skip to the next line + buf += bigBuf.getDimx(); + } + + // Position into the destination buffer + buf = bigBuf; + buf += y1 * bigBuf.getDimx(); + + // Looppone + buf += bigBuf.getDimx(); + for (int y = 1; y < height - 1; y++) { + // if (prim->IsFlipped()) + // mybuf=&buf[x1+m_dimx-1]; + // else + mybuf = &buf[x1]; + + for (int x = 0; x < width; x++, mybuf += step) { + if (_aabuf[(y + v) * _dimx + x + u] == 1 && x != 0 && x != width - 1) { + int r = GETRED(mybuf[1]) + GETRED(mybuf[-1]) + GETRED(mybuf[-bigBuf.getDimx()]) + GETRED(mybuf[bigBuf.getDimx()]); + int g = GETGREEN(mybuf[1]) + GETGREEN(mybuf[-1]) + GETGREEN(mybuf[-bigBuf.getDimx()]) + GETGREEN(mybuf[bigBuf.getDimx()]); + int b = GETBLUE(mybuf[1]) + GETBLUE(mybuf[-1]) + GETBLUE(mybuf[-bigBuf.getDimx()]) + GETBLUE(mybuf[bigBuf.getDimx()]); + + r += GETRED(mybuf[0]) * 2; + g += GETGREEN(mybuf[0]) * 2; + b += GETBLUE(mybuf[0]) * 2; + + r /= 6; + g /= 6; + b /= 6; + + if (r > 31) + r = 31; + if (g > 31) + g = 31; + if (b > 31) + b = 31; + + mybuf[0] = (r << 10) | (g << 5) | b; + } + } + + // Skip to the next line + buf += bigBuf.getDimx(); + } +} + +void RMGfxSourceBuffer8AA::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + CORO_INVOKE_2(RMGfxSourceBuffer8::draw, bigBuf, prim); + drawAA(bigBuf, prim); + + CORO_END_CODE; +} + +/****************************************************************************\ +* RMGfxSourceBuffer8RLEAA Methods +\****************************************************************************/ + +RMGfxSourceBuffer8RLEByteAA::~RMGfxSourceBuffer8RLEByteAA() { +} + +void RMGfxSourceBuffer8RLEByteAA::prepareImage() { + RMGfxSourceBuffer::prepareImage(); + calculateAA(); + compressRLE(); +} + +void RMGfxSourceBuffer8RLEByteAA::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + CORO_INVOKE_2(RMGfxSourceBuffer8RLE::draw, bigBuf, prim); + if (GLOBALS._bCfgAntiAlias) + drawAA(bigBuf, prim); + + CORO_END_CODE; +} + +int RMGfxSourceBuffer8RLEByteAA::init(const byte *buf, int dimx, int dimy, bool bLoadPalette) { + return RMGfxSourceBuffer8RLE::init(buf, dimx, dimy, bLoadPalette); +} + +void RMGfxSourceBuffer8RLEByteAA::init(Common::ReadStream &ds, int dimx, int dimy, bool bLoadPalette) { + RMGfxSourceBuffer8RLE::init(ds, dimx, dimy, bLoadPalette); + + if (!_bNeedRLECompress) { + // Load the anti-aliasing mask + _aabuf = new byte[dimx * dimy]; + ds.read(_aabuf, dimx * dimy); + } +} + +RMGfxSourceBuffer8RLEWordAA::~RMGfxSourceBuffer8RLEWordAA() { +} + +void RMGfxSourceBuffer8RLEWordAA::prepareImage() { + RMGfxSourceBuffer::prepareImage(); + calculateAA(); + compressRLE(); +} + +void RMGfxSourceBuffer8RLEWordAA::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + CORO_INVOKE_2(RMGfxSourceBuffer8RLE::draw, bigBuf, prim); + if (GLOBALS._bCfgAntiAlias) + drawAA(bigBuf, prim); + + CORO_END_CODE; +} + +int RMGfxSourceBuffer8RLEWordAA::init(byte *buf, int dimx, int dimy, bool bLoadPalette) { + return RMGfxSourceBuffer8RLE::init(buf, dimx, dimy, bLoadPalette); +} + +void RMGfxSourceBuffer8RLEWordAA::init(Common::ReadStream &ds, int dimx, int dimy, bool bLoadPalette) { + RMGfxSourceBuffer8RLE::init(ds, dimx, dimy, bLoadPalette); + + if (!_bNeedRLECompress) { + // Load the anti-aliasing mask + _aabuf = new byte[dimx * dimy]; + ds.read(_aabuf, dimx * dimy); + } +} + +/****************************************************************************\ +* RMGfxSourceBuffer16 Methods +\****************************************************************************/ + +RMGfxSourceBuffer16::RMGfxSourceBuffer16(bool bTrasp0) { + _bTrasp0 = bTrasp0; +} + +RMGfxSourceBuffer16::~RMGfxSourceBuffer16() { +} + +void RMGfxSourceBuffer16::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) { + uint16 *buf = bigBuf; + uint16 *raw = (uint16 *)_buf; + + int dimx = _dimx; + int dimy = _dimy; + int u = 0; + int v = 0; + int x1 = 0; + int y1 = 0; + + if (prim->haveSrc()) { + u = prim->getSrc()._x1; + v = prim->getSrc()._y1; + dimx = prim->getSrc().width(); + dimy = prim->getSrc().height(); + } + + if (prim->haveDst()) { + x1 = prim->getDst()._x1; + y1 = prim->getDst()._y1; + } + + if (!clip2D(x1, y1, u, v, dimx, dimy, true, &bigBuf)) + return; + + raw += v * _dimx + u; + buf += y1 * bigBuf.getDimx() + x1; + + if (_bTrasp0) { + for (int y = 0; y < dimy; y++) { + for (int x = 0; x < dimx;) { + while (x < dimx && raw[x] == 0) + x++; + + while (x < dimx && raw[x] != 0) { + buf[x] = raw[x]; + x++; + } + } + + raw += _dimx; + buf += bigBuf.getDimx(); + } + } else { + for (int y = 0; y < dimy; y++) { + Common::copy(raw, raw + dimx, buf); + buf += bigBuf.getDimx(); + raw += _dimx; + } + } + + // Specify the drawn area + bigBuf.addDirtyRect(Common::Rect(x1, y1, x1 + dimx, y1 + dimy)); +} + +void RMGfxSourceBuffer16::prepareImage() { + // Color space conversion if necessary! + uint16 *buf = (uint16 *)_buf; + + for (int i = 0; i < _dimx * _dimy; i++) + buf[i] = FROM_LE_16(buf[i]) & 0x7FFF; +} + +RMGfxSourceBuffer16::RMGfxSourceBuffer16(int dimx, int dimy) + : RMGfxBuffer(dimx, dimy, 16) { + setPriority(0); + _bTrasp0 = false; +} + +/** + * Returns the number of bits per pixel of the surface + * + * @returns Bit per pixel + */ +int RMGfxSourceBuffer16::getBpp() { + return 16; +} + +void RMGfxSourceBuffer16::create(int dimx, int dimy) { + RMGfxBuffer::create(dimx, dimy, 16); +} + +/****************************************************************************\ +* RMGfxBox Methods +\****************************************************************************/ + +void RMGfxBox::removeThis(CORO_PARAM, bool &result) { + result = true; +} + +void RMGfxBox::setColor(byte r, byte g, byte b) { + r >>= 3; + g >>= 3; + b >>= 3; + _wFillColor = (r << 10) | (g << 5) | b; +} + +void RMGfxBox::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) { + uint16 *buf = bigBuf; + RMRect rcDst; + + // It takes the destination rectangle + rcDst = prim->getDst(); + buf += rcDst._y1 * bigBuf.getDimx() + rcDst._x1; + + // Loop through the pixels + for (int j = 0; j < rcDst.height(); j++) { + for (int i = 0; i < rcDst.width(); i++) + *buf++ = _wFillColor; + + buf += bigBuf.getDimx() - rcDst.width(); + } + + // Specify the drawn area + bigBuf.addDirtyRect(rcDst); +} + +/****************************************************************************\ +* RMGfxClearTask Methods +\****************************************************************************/ + +int RMGfxClearTask::priority() { + // Maximum priority (must be done first) + return 1; +} + +void RMGfxClearTask::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *) { + // Clean the target buffer + Common::fill((byte *)bigBuf, (byte *)bigBuf + (bigBuf.getDimx() * bigBuf.getDimy() * 2), 0x0); + bigBuf.addDirtyRect(Common::Rect(bigBuf.getDimx(), bigBuf.getDimy())); +} + +void RMGfxClearTask::removeThis(CORO_PARAM, bool &result) { + // The task is fine to be removed + result = true; +} + +/****************************************************************************\ +* RMGfxPrimitive Methods +\****************************************************************************/ + +RMGfxPrimitive::RMGfxPrimitive() { + _bFlag = 0; + _task = NULL; + _src.setEmpty(); + _dst.setEmpty(); + _bStretch = false; +} + +RMGfxPrimitive::RMGfxPrimitive(RMGfxTask *task) { + _task = task; + _bFlag = 0; + _bStretch = false; +} + +RMGfxPrimitive::RMGfxPrimitive(RMGfxTask *task, const RMRect &src, RMRect &dst) { + _task = task; + _src = src; + _dst = dst; + _bFlag = 0; + _bStretch = (src.width() != dst.width() || src.height() != dst.height()); +} + +RMGfxPrimitive::RMGfxPrimitive(RMGfxTask *task, const RMPoint &src, RMRect &dst) { + _task = task; + _src.topLeft() = src; + _dst = dst; + _bFlag = 0; + _bStretch = false; +} + +RMGfxPrimitive::RMGfxPrimitive(RMGfxTask *task, const RMPoint &src, RMPoint &dst) { + _task = task; + _src.topLeft() = src; + _dst.topLeft() = dst; + _bFlag = 0; + _bStretch = false; +} + +RMGfxPrimitive::RMGfxPrimitive(RMGfxTask *task, const RMRect &src, RMPoint &dst) { + _task = task; + _src = src; + _dst.topLeft() = dst; + _bFlag = 0; + _bStretch = false; +} + +RMGfxPrimitive::RMGfxPrimitive(RMGfxTask *task, const RMRect &dst) { + _task = task; + _dst = dst; + _src.setEmpty(); + _bFlag = 0; + _bStretch = false; +} + +RMGfxPrimitive::RMGfxPrimitive(RMGfxTask *task, const RMPoint &dst) { + _task = task; + _dst.topLeft() = dst; + _src.setEmpty(); + _bFlag = 0; + _bStretch = false; +} + +RMGfxPrimitive::~RMGfxPrimitive() { +} + +void RMGfxPrimitive::setFlag(byte bFlag) { + _bFlag = bFlag; +} + +void RMGfxPrimitive::setTask(RMGfxTask *task) { + _task = task; +} + +void RMGfxPrimitive::setSrc(const RMRect &src) { + _src = src; +} + +void RMGfxPrimitive::setSrc(const RMPoint &src) { + _src.topLeft() = src; +} + +void RMGfxPrimitive::setDst(const RMRect &dst) { + _dst = dst; +} + +void RMGfxPrimitive::setDst(const RMPoint &dst) { + _dst.topLeft() = dst; +} + +void RMGfxPrimitive::setStretch(bool bStretch) { + _bStretch = bStretch; +} + +bool RMGfxPrimitive::haveDst() { + return !_dst.isEmpty(); +} + +RMRect &RMGfxPrimitive::getDst() { + return _dst; +} + +bool RMGfxPrimitive::haveSrc() { + return !_src.isEmpty(); +} + +RMRect &RMGfxPrimitive::getSrc() { + return _src; +} + +/** + * Flags + */ +bool RMGfxPrimitive::isFlipped() { + return _bFlag & 1; +} + +/** + * Duplicate + */ +RMGfxPrimitive *RMGfxPrimitive::duplicate() { + return new RMGfxPrimitive(*this); +} + +} // End of namespace Tony diff --git a/engines/tony/gfxcore.h b/engines/tony/gfxcore.h new file mode 100644 index 0000000000..1bacf7e5a9 --- /dev/null +++ b/engines/tony/gfxcore.h @@ -0,0 +1,500 @@ +/* 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. + * + */ + +/* + * This code is based on original Tony Tough source code + * + * Copyright (c) 1997-2003 Nayma Software + */ + +#ifndef TONY_GFXCORE_H +#define TONY_GFXCORE_H + +#include "common/system.h" +#include "common/coroutines.h" +#include "tony/utils.h" + +namespace Tony { + +/****************************************************************************\ +* Class prototype +\****************************************************************************/ + +// Class Name Family Treee Abstract? +class RMGfxTask; // Yes +class RMGfxTaskSetPrior; // Task Yes +class RMGfxBuffer; // +class RMGfxSourceBuffer; // TaskP+[Buffer] Yes +class RMGfxTargetBuffer; // [Buffer] +class RMGfxSourceBufferPal; // Source Yes +class RMGfxSourceBuffer4; // SourcePal +class RMGfxSourceBuffer8; // SourcePal +class RMGfxSourceBuffer16; // Source +class RMGfxWoodyBuffer; // Source16+Target +class RMGfxClearTask; // Task + +/** + * Graphics buffer + */ +class RMGfxBuffer { +protected: + int _dimx, _dimy; + byte *_buf; + byte *_origBuf; + +public: + RMGfxBuffer(); + RMGfxBuffer(int dimx, int dimy, int nBpp); + virtual ~RMGfxBuffer(); + + // Attributes + int getDimx(); + int getDimy(); + + // Creation + virtual void create(int dimx, int dimy, int nBpp); + virtual void destroy(); + + // These are valid only if the buffer is locked + operator byte *(); + operator void *(); + + // Getting the offset for a given Y position + void offsetY(int nLines, int nBpp); +}; + +/** + * Graphics primitive + */ +class RMGfxPrimitive { +public: + RMGfxTask *_task; + +protected: + RMRect _src; + RMRect _dst; + + bool _bStretch; + byte _bFlag; + +public: + RMGfxPrimitive(); + RMGfxPrimitive(RMGfxTask *task); + RMGfxPrimitive(RMGfxTask *task, const RMRect &src, RMRect &dst); + RMGfxPrimitive(RMGfxTask *task, const RMPoint &src, RMRect &dst); + RMGfxPrimitive(RMGfxTask *task, const RMPoint &src, RMPoint &dst); + RMGfxPrimitive(RMGfxTask *task, const RMRect &src, RMPoint &dst); + RMGfxPrimitive(RMGfxTask *task, const RMRect &dst); + RMGfxPrimitive(RMGfxTask *task, const RMPoint &dst); + virtual ~RMGfxPrimitive(); + void setFlag(byte bFlag); + void setTask(RMGfxTask *task); + void setSrc(const RMRect &src); + void setSrc(const RMPoint &src); + void setDst(const RMRect &dst); + void setDst(const RMPoint &dst); + void setStretch(bool bStretch); + bool haveDst(); + RMRect &getDst(); + bool haveSrc(); + RMRect &getSrc(); + + // Flags + bool isFlipped(); + + // Duplicate + virtual RMGfxPrimitive *duplicate(); +}; + +/** + * Graphic drawing task + */ +class RMGfxTask { +protected: + int _nPrior; + int _nInList; + +public: + // Standard constructor + RMGfxTask(); + virtual ~RMGfxTask() { } + + virtual int priority(); + virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) = 0; + virtual void removeThis(CORO_PARAM, bool &result); + + // Registration + virtual void Register(); + virtual void unregister(); +}; + +/** + * Graphic drawing with priority + */ +class RMGfxTaskSetPrior : public RMGfxTask { +public: + virtual ~RMGfxTaskSetPrior() { } + void setPriority(int nPrior); +}; + +/** + * Task that cleans the destination buffer + */ +class RMGfxClearTask : public RMGfxTask { +public: + virtual ~RMGfxClearTask() { } + + int priority(); + virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim); + virtual void removeThis(CORO_PARAM, bool &result); +}; + +/** + * Task that draws a colored box + */ +class RMGfxBox : public RMGfxTaskSetPrior { +protected: + uint16 _wFillColor; + +public: + virtual ~RMGfxBox() { } + + void setColor(byte r, byte g, byte b); + virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim); + virtual void removeThis(CORO_PARAM, bool &result); +}; + +/** + * Buffer source for the design, which is a task. This is an abstract base. + */ +class RMGfxSourceBuffer : public virtual RMGfxBuffer, public RMGfxTaskSetPrior { +public: + // Load the data for the surface + virtual int init(uint32 resID, int dimx, int dimy, bool bLoadPalette = false); + virtual int init(const byte *buf, int dimx, int dimy, bool bLoadPalette = false); + virtual void init(Common::ReadStream &ds, int dimx, int dimy, bool bLoadPalette = false); + + virtual ~RMGfxSourceBuffer(); + +protected: + virtual void prepareImage(); + bool clip2D(int &x1, int &y1, int &u, int &v, int &width, int &height, bool bUseSrc, RMGfxTargetBuffer *buf); + void offsetY(int nLines); + +public: + virtual int getBpp() = 0; +}; + +/** + * 16-bit color source + */ +class RMGfxSourceBuffer16 : public RMGfxSourceBuffer { +protected: + virtual void prepareImage(); + bool _bTrasp0; + +public: + RMGfxSourceBuffer16(bool bUseTrasp = false); + RMGfxSourceBuffer16(int dimx, int dimy); + virtual ~RMGfxSourceBuffer16(); + + // Initialization + void create(int dimx, int dimy); + + int getBpp(); + virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim); +}; + +/** + * Buffer source with palette + */ +class RMGfxSourceBufferPal : public RMGfxSourceBuffer { +protected: + // The size of the palette is (1 << Bpp()) * 4 + byte _pal[256 * 3]; + uint16 _palFinal[256]; + + // Post process to prepare the palette for drawing + virtual void preparePalette(); + +public: + virtual ~RMGfxSourceBufferPal(); + + virtual int init(const byte *buf, int dimx, int dimy, bool bLoadPalette = false); + virtual void init(Common::ReadStream &ds, int dimx, int dimy, bool bLoadPalette = false); + + int loadPaletteWA(uint32 resID, bool bSwapped = false); + int loadPaletteWA(const byte *buf, bool bSwapped = false); + int loadPalette(uint32 resID); + int loadPalette(const byte *buf); +}; + +/** + * Buffer source with a 256 color palette + */ +class RMGfxSourceBuffer8 : public RMGfxSourceBufferPal { +protected: + bool _bTrasp0; + +public: + RMGfxSourceBuffer8(bool bTrasp0 = true); + RMGfxSourceBuffer8(int dimx, int dimy); + virtual ~RMGfxSourceBuffer8(); + + // Initialization + void create(int dimx, int dimy); + + int getBpp(); + virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim); +}; + +/** + * Buffer source with a 256 color palette, and alpha blending + */ +class RMGfxSourceBuffer8AB : public RMGfxSourceBuffer8 { +protected: + int calcTrasp(int f, int b); + +public: + virtual ~RMGfxSourceBuffer8AB(); + virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim); +}; + +/** + * Buffer source with a 256 color palette, RLE compressed + */ + +class RMGfxSourceBuffer8RLE : public virtual RMGfxSourceBuffer8 { +protected: + int _alphaBlendColor; + int _alphaR, _alphaB, _alphaG; + bool _bNeedRLECompress; + +protected: + static byte _megaRLEBuf[]; + + virtual void rleWriteTrasp(byte *&cur, int rep) = 0; + virtual void rleWriteData(byte *&cur, int rep, byte *src) = 0; + virtual void rleWriteEOL(byte *&cur) = 0; + virtual void rleWriteAlphaBlend(byte *&cur, int rep) = 0; + virtual void rleDecompressLine(uint16 *dst, byte *src, int nStartSkip, int nLength) = 0; + virtual void rleDecompressLineFlipped(uint16 *dst, byte *src, int nStartSkip, int nLength) = 0; + + // Perform image compression in RLE + void compressRLE(); + +protected: + // Overriding initialization methods + virtual void prepareImage(); + virtual void preparePalette(); + +public: + RMGfxSourceBuffer8RLE(); + virtual ~RMGfxSourceBuffer8RLE(); + + // Overload of the initialization method + virtual void init(Common::ReadStream &ds, int dimx, int dimy, bool bLoadPalette = false); + virtual int init(const byte *buf, int dimx, int dimy, bool bLoadPalette = false); + + // Draw image with RLE decompression + virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim); + + // Sets the color that will be alpha blended + void setAlphaBlendColor(int color); + + // Warn if the data is already compressed + void setAlreadyCompressed(); +}; + +class RMGfxSourceBuffer8RLEByte : public RMGfxSourceBuffer8RLE { +protected: + void rleWriteTrasp(byte * &cur, int rep); + void rleWriteAlphaBlend(byte * &cur, int rep); + void rleWriteData(byte * &cur, int rep, byte *src); + void rleWriteEOL(byte * &cur); + void rleDecompressLine(uint16 *dst, byte *src, int nStartSkip, int nLength); + void rleDecompressLineFlipped(uint16 *dst, byte *src, int nStartSkip, int nLength); + +public: + virtual ~RMGfxSourceBuffer8RLEByte(); +}; + +class RMGfxSourceBuffer8RLEWord : public RMGfxSourceBuffer8RLE { +protected: + void rleWriteTrasp(byte * &cur, int rep); + void rleWriteAlphaBlend(byte * &cur, int rep); + void rleWriteData(byte * &cur, int rep, byte *src); + void rleWriteEOL(byte * &cur); + virtual void rleDecompressLine(uint16 *dst, byte *src, int nStartSkip, int nLength); + virtual void rleDecompressLineFlipped(uint16 *dst, byte *src, int nStartSkip, int nLength); + +public: + virtual ~RMGfxSourceBuffer8RLEWord(); +}; + +class RMGfxSourceBuffer8RLEWordAB : public RMGfxSourceBuffer8RLEWord { +protected: + virtual void rleDecompressLine(uint16 *dst, byte *src, int nStartSkip, int nLength); + +public: + virtual ~RMGfxSourceBuffer8RLEWordAB(); +}; + +/** + * Buffer source with a 256 color palette, with anti-aliasing + */ +class RMGfxSourceBuffer8AA : public virtual RMGfxSourceBuffer8 { +protected: + static byte _megaAABuf[]; + static byte _megaAABuf2[]; + byte *_aabuf; + + // Calculate the buffer for the anti-aliasing + void calculateAA(); + + // Draw the AA + void drawAA(RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim); + +protected: + void prepareImage(); + +public: + RMGfxSourceBuffer8AA(); + virtual ~RMGfxSourceBuffer8AA(); + + // Draw with anti-aliasing + virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim); +}; + +class RMGfxSourceBuffer8RLEByteAA : public RMGfxSourceBuffer8RLEByte, public RMGfxSourceBuffer8AA { +protected: + void prepareImage(); + +public: + virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim); + + // Overloaded initialization methods + virtual void init(Common::ReadStream &ds, int dimx, int dimy, bool bLoadPalette = false); + virtual int init(const byte *buf, int dimx, int dimy, bool bLoadPalette = false); + + virtual ~RMGfxSourceBuffer8RLEByteAA(); +}; + +class RMGfxSourceBuffer8RLEWordAA : public RMGfxSourceBuffer8RLEWord, public RMGfxSourceBuffer8AA { +protected: + void prepareImage(); + +public: + virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim); + + // Overloaded initialization methods + virtual void init(Common::ReadStream &ds, int dimx, int dimy, bool bLoadPalette = false); + virtual int init(byte *buf, int dimx, int dimy, bool bLoadPalette = false); + + virtual ~RMGfxSourceBuffer8RLEWordAA(); +}; + +/** + * Source buffer with 16 colors + */ +class RMGfxSourceBuffer4 : public RMGfxSourceBufferPal { +public: + RMGfxSourceBuffer4(); + RMGfxSourceBuffer4(int dimx, int dimy); + + // Initialization + void create(int dimx, int dimy); + + int getBpp(); + virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim); +}; + +/** + * Destination buffer which manages its own internal list of tasks + */ +class RMGfxTargetBuffer : public virtual RMGfxBuffer { +private: + struct OTList { + RMGfxPrimitive *_prim; + OTList *_next; + + OTList(); + OTList(RMGfxPrimitive *pr) { + _prim = pr; + } + }; + + bool _trackDirtyRects; + Common::List<Common::Rect> _currentDirtyRects, _previousDirtyRects, _dirtyRects; + + void mergeDirtyRects(); + +private: + //OSystem::MutexRef csModifyingOT; + +protected: + OTList *_otlist; + int _otSize; + +public: + RMGfxTargetBuffer(); + virtual ~RMGfxTargetBuffer(); + + static uint16 *_precalcTable; + static void createBWPrecalcTable(); + static void freeBWPrecalcTable(); + + // management of the OT list + void clearOT(); + void drawOT(CORO_PARAM); + void addPrim(RMGfxPrimitive *prim); // The pointer must be delted + + operator byte *(); + operator void *(); + operator uint16 *(); + + // Offseting buffer + void offsetY(int nLines); + + // Dirty rect methods + void addDirtyRect(const Common::Rect &r); + Common::List<Common::Rect> &getDirtyRects(); + void clearDirtyRects(); + void setTrackDirtyRects(bool v); + bool getTrackDirtyRects() const; +}; + +/** + * Ring buffer, which is both source and by destination + */ +class RMGfxWoodyBuffer: public RMGfxSourceBuffer16, public RMGfxTargetBuffer { +public: + RMGfxWoodyBuffer(); + RMGfxWoodyBuffer(int dimx, int dimy); + virtual ~RMGfxWoodyBuffer(); + + virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim); +}; + +} // End of namespace Tony + +#endif diff --git a/engines/tony/gfxengine.cpp b/engines/tony/gfxengine.cpp new file mode 100644 index 0000000000..cb27e20ab1 --- /dev/null +++ b/engines/tony/gfxengine.cpp @@ -0,0 +1,841 @@ +/* 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. + * + */ + +/* + * This code is based on original Tony Tough source code + * + * Copyright (c) 1997-2003 Nayma Software + */ + +#include "common/savefile.h" +#include "tony/mpal/lzo.h" +#include "tony/mpal/mpalutils.h" +#include "tony/custom.h" +#include "tony/gfxengine.h" +#include "tony/tony.h" + +namespace Tony { + +/****************************************************************************\ +* RMGfxEngine Methods +\****************************************************************************/ + +void exitAllIdles(CORO_PARAM, const void *param) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + int nCurLoc = *(const int *)param; + + CORO_BEGIN_CODE(_ctx); + + // Closes idle + GLOBALS._bSkipSfxNoLoop = true; + + CORO_INVOKE_2(mpalEndIdlePoll, nCurLoc, NULL); + + GLOBALS._bIdleExited = true; + GLOBALS._bSkipSfxNoLoop = false; + + CORO_END_CODE; +} + +RMGfxEngine::RMGfxEngine() { + // Create big buffer where the frame will be rendered + _bigBuf.create(RM_BBX, RM_BBY, 16); + _bigBuf.offsetY(RM_SKIPY); + _bigBuf.setTrackDirtyRects(true); + + _nCurLoc = 0; + _curAction = TA_GOTO; + _curActionObj = 0; + _nWipeType = 0; + _hWipeEvent = 0; + _nWipeStep = 0; + _bMustEnterMenu = false; + _bWiping = false; + _bGUIOption = false; + _bGUIInterface = false; + _bGUIInventory = false; + _bAlwaysDrawMouse = false; + _bOption = false; + _bLocationLoaded = false; + _bInput = false; +} + +RMGfxEngine::~RMGfxEngine() { + // Close the buffer + _bigBuf.destroy(); +} + +void RMGfxEngine::openOptionScreen(CORO_PARAM, int type) { + CORO_BEGIN_CONTEXT; + bool bRes; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + _ctx->bRes = false; + + if (type == 0) + CORO_INVOKE_2(_opt.init, _bigBuf, _ctx->bRes); + else if (type == 1) + CORO_INVOKE_3(_opt.initLoadMenuOnly, _bigBuf, true, _ctx->bRes); + else if (type == 2) + CORO_INVOKE_2(_opt.initNoLoadSave, _bigBuf, _ctx->bRes); + else if (type == 3) + CORO_INVOKE_3(_opt.initLoadMenuOnly, _bigBuf, false, _ctx->bRes); + else if (type == 4) + CORO_INVOKE_3(_opt.initSaveMenuOnly, _bigBuf, false, _ctx->bRes); + + if (_ctx->bRes) { + g_vm->pauseSound(true); + + disableInput(); + _inv.endCombine(); + _curActionObj = 0; + _curAction = TA_GOTO; + _point.setAction(_curAction); + _point.setSpecialPointer(RMPointer::PTR_NONE); + _point.setCustomPointer(NULL); + enableMouse(); + g_vm->grabThumbnail(); + + // Exists the IDLE to avoid premature death in loading + _bMustEnterMenu = true; + if (type == 1 || type == 2) { + GLOBALS._bIdleExited = true; + } else { + CORO_INVOKE_0(_tony.stopNoAction); + + GLOBALS._bIdleExited = false; + + CoroScheduler.createProcess(exitAllIdles, &_nCurLoc, sizeof(int)); + } + } + + CORO_END_CODE; +} + +void RMGfxEngine::doFrame(CORO_PARAM, bool bDrawLocation) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + // Poll of input devices + _input.poll(); + + if (_bMustEnterMenu && GLOBALS._bIdleExited) { + _bOption = true; + _bMustEnterMenu = false; + GLOBALS._bIdleExited = false; + } + + if (_bOption) { + CORO_INVOKE_1(_opt.doFrame, &_input); + _bOption = !_opt.isClosing(); + if (!_bOption) { + disableMouse(); + enableInput(); + mpalStartIdlePoll(_nCurLoc); + g_vm->pauseSound(false); + } + } + + if (bDrawLocation && _bLocationLoaded) { + // Location and objects + _loc.doFrame(&_bigBuf); + + // Check the mouse input + if (_bInput && !_tony.inAction()) { + // If we are on the inventory, it is it who controls all input + if (_inv.haveFocus(_input.mousePos()) && !_inter.active()) { + // Left Click + // ********** + if (_input.mouseLeftClicked()/* && m_itemName.IsItemSelected()*/) { + // Left click activates the combine, if we are on an object + if (_inv.leftClick(_input.mousePos(), _curActionObj)) { + _curAction = TA_COMBINE; + _point.setAction(_curAction); + } + } else + + // Right Click + // *********** + if (_input.mouseRightClicked()) { + if (_itemName.isItemSelected()) { + _curActionObj = 0; + _inv.rightClick(_input.mousePos()); + } else + _inv.rightClick(_input.mousePos()); + } else + + // Right Release + // ************* + if (_input.mouseRightReleased()) { + if (_inv.rightRelease(_input.mousePos(), _curAction)) { + CORO_INVOKE_3(_tony.moveAndDoAction, _itemName.getHotspot(), _itemName.getSelectedItem(), _curAction); + + _curAction = TA_GOTO; + _point.setAction(_curAction); + } + } + } else { + // Options Menu + // ************ + if (_bGUIOption) { + if (!_tony.inAction() && _bInput) { + if ((_input.mouseLeftClicked() && _input.mousePos()._x < 3 && _input.mousePos()._y < 3)) { + CORO_INVOKE_1(openOptionScreen, 0); + goto SKIPCLICKSINISTRO; + } else if (_input.getAsyncKeyState(Common::KEYCODE_ESCAPE)) + CORO_INVOKE_1(openOptionScreen, 0); + else if (!g_vm->getIsDemo()) { + if (_input.getAsyncKeyState(Common::KEYCODE_F3) || _input.getAsyncKeyState(Common::KEYCODE_F5)) + // Save game screen + CORO_INVOKE_1(openOptionScreen, 4); + else if (_input.getAsyncKeyState(Common::KEYCODE_F2) || _input.getAsyncKeyState(Common::KEYCODE_F7)) + // Load game screen + CORO_INVOKE_1(openOptionScreen, 3); + } + } + } + + // Left Click + // ************** + if (_input.mouseLeftClicked() && !_inter.active()) { + + if (_curAction != TA_COMBINE) + CORO_INVOKE_3(_tony.moveAndDoAction, _itemName.getHotspot(), _itemName.getSelectedItem(), _point.curAction()); + else if (_itemName.getSelectedItem() != NULL) + CORO_INVOKE_4(_tony.moveAndDoAction, _itemName.getHotspot(), _itemName.getSelectedItem(), TA_COMBINE, _curActionObj); + + if (_curAction == TA_COMBINE) { + _inv.endCombine(); + _point.setSpecialPointer(RMPointer::PTR_NONE); + } + + _curAction = TA_GOTO; + _point.setAction(_curAction); + } + +SKIPCLICKSINISTRO: + // Right Click + // ************ + if (_curAction == TA_COMBINE) { + // During a combine, it cancels it + if (_input.mouseRightClicked()) { + _inv.endCombine(); + _curActionObj = 0; + _curAction = TA_GOTO; + _point.setAction(_curAction); + _point.setSpecialPointer(RMPointer::PTR_NONE); + } + } else if (_input.mouseRightClicked() && _itemName.isItemSelected() && _point.getSpecialPointer() == RMPointer::PTR_NONE) { + if (_bGUIInterface) { + // Before opening the interface, replaces GOTO + _curAction = TA_GOTO; + _curActionObj = 0; + _point.setAction(_curAction); + _inter.clicked(_input.mousePos()); + } + } + + // Right Release + // ************* + if (_input.mouseRightReleased()) { + if (_bGUIInterface) { + if (_inter.released(_input.mousePos(), _curAction)) { + _point.setAction(_curAction); + CORO_INVOKE_3(_tony.moveAndDoAction, _itemName.getHotspot(), _itemName.getSelectedItem(), _curAction); + + _curAction = TA_GOTO; + _point.setAction(_curAction); + } + } + } + } + + // Update the name under the mouse pointer + _itemName.setMouseCoord(_input.mousePos()); + if (!_inter.active() && !_inv.miniActive()) + CORO_INVOKE_4(_itemName.doFrame, _bigBuf, _loc, _point, _inv); + } + + // Interface & Inventory + _inter.doFrame(_bigBuf, _input.mousePos()); + _inv.doFrame(_bigBuf, _point, _input.mousePos(), (!_tony.inAction() && !_inter.active() && _bGUIInventory)); + } + + // Animate Tony + CORO_INVOKE_2(_tony.doFrame, &_bigBuf, _nCurLoc); + + // Update screen scrolling to keep Tony in focus + if (_tony.mustUpdateScrolling() && _bLocationLoaded) { + RMPoint showThis = _tony.position(); + showThis._y -= 60; + _loc.updateScrolling(showThis); + } + + if (_bLocationLoaded) + _tony.setScrollPosition(_loc.scrollPosition()); + + if ((!_tony.inAction() && _bInput) || _bAlwaysDrawMouse) { + _point.showCursor(); + } else { + _point.hideCursor(); + } + _point.doFrame(); + + // ********************** + // Draw the list in the OT + // ********************** + CORO_INVOKE_0(_bigBuf.drawOT); + +#define FSTEP (480/32) + + // Wipe + if (_bWiping) { + switch (_nWipeType) { + case 1: + if (!(_rcWipeEllipse.bottom - _rcWipeEllipse.top >= FSTEP * 2)) { + CoroScheduler.setEvent(_hWipeEvent); + _nWipeType = 3; + break; + } + + _rcWipeEllipse.top += FSTEP; + _rcWipeEllipse.left += FSTEP; + _rcWipeEllipse.right -= FSTEP; + _rcWipeEllipse.bottom -= FSTEP; + break; + + case 2: + if (!(_rcWipeEllipse.bottom - _rcWipeEllipse.top < 480 - FSTEP)) { + CoroScheduler.setEvent(_hWipeEvent); + _nWipeType = 3; + break; + } + + _rcWipeEllipse.top -= FSTEP; + _rcWipeEllipse.left -= FSTEP; + _rcWipeEllipse.right += FSTEP; + _rcWipeEllipse.bottom += FSTEP; + break; + } + } + + CORO_END_CODE; +} + +void RMGfxEngine::initCustomDll() { + setupGlobalVars(&_tony, &_point, &g_vm->_theBoxes, &_loc, &_inv, &_input); +} + +void RMGfxEngine::itemIrq(uint32 dwItem, int nPattern, int nStatus) { + RMItem *item; + assert(GLOBALS._gfxEngine); + + if (GLOBALS._gfxEngine->_bLocationLoaded) { + item = GLOBALS._gfxEngine->_loc.getItemFromCode(dwItem); + if (item != NULL) { + if (nPattern != -1) { + item->setPattern(nPattern, true); + } + if (nStatus != -1) + item->setStatus(nStatus); + } + } +} + +void RMGfxEngine::initForNewLocation(int nLoc, RMPoint ptTonyStart, RMPoint start) { + if (start._x == -1 || start._y == -1) { + start._x = ptTonyStart._x - RM_SX / 2; + start._y = ptTonyStart._y - RM_SY / 2; + } + + _loc.setScrollPosition(start); + + if (ptTonyStart._x == 0 && ptTonyStart._y == 0) { + } else { + _tony.setPosition(ptTonyStart, nLoc); + _tony.setScrollPosition(start); + } + + _curAction = TA_GOTO; + _point.setCustomPointer(NULL); + _point.setSpecialPointer(RMPointer::PTR_NONE); + _point.setAction(_curAction); + _inter.reset(); + _inv.reset(); + + mpalStartIdlePoll(_nCurLoc); +} + +uint32 RMGfxEngine::loadLocation(int nLoc, RMPoint ptTonyStart, RMPoint start) { + _nCurLoc = nLoc; + + bool bLoaded = false; + for (int i = 0; i < 5; i++) { + // Try the loading of the location + RMRes res(_nCurLoc); + if (!res.isValid()) + continue; + + Common::SeekableReadStream *ds = res.getReadStream(); + _loc.load(*ds); + delete ds; + + initForNewLocation(nLoc, ptTonyStart, start); + bLoaded = true; + break; + } + + if (!bLoaded) + error("Location was not loaded"); + + if (_bOption) + _opt.reInit(_bigBuf); + + _bLocationLoaded = true; + + // On entering the location + return CORO_INVALID_PID_VALUE; //mpalQueryDoAction(0, m_nCurLoc, 0); +} + +void RMGfxEngine::unloadLocation(CORO_PARAM, bool bDoOnExit, uint32 *result) { + CORO_BEGIN_CONTEXT; + uint32 h; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + // Release the location + CORO_INVOKE_2(mpalEndIdlePoll, _nCurLoc, NULL); + + // On Exit? + if (bDoOnExit) { + _ctx->h = mpalQueryDoAction(1, _nCurLoc, 0); + if (_ctx->h != CORO_INVALID_PID_VALUE) + CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _ctx->h, CORO_INFINITE); + } + + _bLocationLoaded = false; + + _bigBuf.clearOT(); + _loc.unload(); + + if (result != NULL) + *result = CORO_INVALID_PID_VALUE; + + CORO_END_CODE; +} + +void RMGfxEngine::init() { + // Screen loading + RMResRaw *raw; + RMGfxSourceBuffer16 *load = NULL; + INIT_GFX16_FROMRAW(20038, load); + _bigBuf.addPrim(new RMGfxPrimitive(load)); + _bigBuf.drawOT(Common::nullContext); + _bigBuf.clearOT(); + delete load; + + // Display 'Loading' screen + _bigBuf.addDirtyRect(Common::Rect(0, 0, RM_SX, RM_SY)); + g_vm->_window.getNewFrame(*this, NULL); + g_vm->_window.repaint(); + + // Activate GUI + _bGUIOption = true; + _bGUIInterface = true; + _bGUIInventory = true; + + GLOBALS._bSkipSfxNoLoop = false; + _bMustEnterMenu = false; + GLOBALS._bIdleExited = false; + _bOption = false; + _bWiping = false; + _hWipeEvent = CoroScheduler.createEvent(false, false); + + // Initialize the IRQ function for items for MPAL + GLOBALS._gfxEngine = this; + mpalInstallItemIrq(itemIrq); + + // Initialize the mouse pointer + _point.init(); + + // Initialize Tony + _tony.init(); + _tony.linkToBoxes(&g_vm->_theBoxes); + + // Initialize the inventory and the interface + _inv.init(); + _inter.init(); + + // Download the location and set priorities @@@@@ + _bLocationLoaded = false; + + enableInput(); + + // Starting the game + _tony.executeAction(20, 1, 0); +} + +void RMGfxEngine::close() { + _bigBuf.clearOT(); + + _inter.close(); + _inv.close(); + _tony.close(); + _point.close(); +} + +void RMGfxEngine::enableInput() { + _bInput = true; +} + +void RMGfxEngine::disableInput() { + _bInput = false; + _inter.reset(); +} + +void RMGfxEngine::enableMouse() { + _bAlwaysDrawMouse = true; +} + +void RMGfxEngine::disableMouse() { + _bAlwaysDrawMouse = false; +} + +#define TONY_SAVEGAME_VERSION 8 + +void RMGfxEngine::saveState(const Common::String &fn, byte *curThumb, const Common::String &name) { + Common::OutSaveFile *f; + byte *state; + char buf[4]; + RMPoint tp = _tony.position(); + + // Saving: MPAL variables, current location, and Tony inventory position + + // For now, we only save the MPAL state + uint size = mpalGetSaveStateSize(); + state = new byte[size]; + mpalSaveState(state); + + uint thumbsize = 160 * 120 * 2; + + buf[0] = 'R'; + buf[1] = 'M'; + buf[2] = 'S'; + buf[3] = TONY_SAVEGAME_VERSION; + + f = g_system->getSavefileManager()->openForSaving(fn); + if (f == NULL) + return; + + f->write(buf, 4); + f->writeUint32LE(thumbsize); + f->write(curThumb, thumbsize); + + // Difficulty level + int i = mpalQueryGlobalVar("VERSIONEFACILE"); + f->writeByte(i); + + i = strlen(name.c_str()); + f->writeByte(i); + f->write(name.c_str(), i); + f->writeUint32LE(_nCurLoc); + f->writeUint32LE(tp._x); + f->writeUint32LE(tp._y); + + f->writeUint32LE(size); + f->write(state, size); + delete[] state; + + // Inventory + size = _inv.getSaveStateSize(); + state = new byte[size]; + _inv.saveState(state); + f->writeUint32LE(size); + f->write(state, size); + delete[] state; + + // boxes + size = g_vm->_theBoxes.getSaveStateSize(); + state = new byte[size]; + g_vm->_theBoxes.saveState(state); + f->writeUint32LE(size); + f->write(state, size); + delete[] state; + + // New Ver5 + // Saves the state of the shepherdess and show yourself + bool bStat = _tony.getShepherdess(); + f->writeByte(bStat); + bStat = _inter.getPerorate(); + f->writeByte(bStat); + + // Save the chars + charsSaveAll(f); + + // Save the options + f->writeByte(GLOBALS._bCfgInvLocked); + f->writeByte(GLOBALS._bCfgInvNoScroll); + f->writeByte(GLOBALS._bCfgTimerizedText); + f->writeByte(GLOBALS._bCfgInvUp); + f->writeByte(GLOBALS._bCfgAnni30); + f->writeByte(GLOBALS._bCfgAntiAlias); + f->writeByte(GLOBALS._bShowSubtitles); + f->writeByte(GLOBALS._bCfgTransparence); + f->writeByte(GLOBALS._bCfgInterTips); + f->writeByte(GLOBALS._bCfgDubbing); + f->writeByte(GLOBALS._bCfgMusic); + f->writeByte(GLOBALS._bCfgSFX); + f->writeByte(GLOBALS._nCfgTonySpeed); + f->writeByte(GLOBALS._nCfgTextSpeed); + f->writeByte(GLOBALS._nCfgDubbingVolume); + f->writeByte(GLOBALS._nCfgMusicVolume); + f->writeByte(GLOBALS._nCfgSFXVolume); + + // Save the hotspots + saveChangedHotspot(f); + + // Save the music + saveMusic(f); + + f->finalize(); + delete f; +} + +void RMGfxEngine::loadState(CORO_PARAM, const Common::String &fn) { + // PROBLEM: You should change the location in a separate process to do the OnEnter + CORO_BEGIN_CONTEXT; + Common::InSaveFile *f; + byte *state, *statecmp; + uint32 size, sizecmp; + char buf[4]; + RMPoint tp; + int loc; + int ver; + int i; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + _ctx->f = g_system->getSavefileManager()->openForLoading(fn); + if (_ctx->f == NULL) + return; + _ctx->f->read(_ctx->buf, 4); + + if (_ctx->buf[0] != 'R' || _ctx->buf[1] != 'M' || _ctx->buf[2] != 'S') { + delete _ctx->f; + return; + } + + _ctx->ver = _ctx->buf[3]; + + if (_ctx->ver == 0 || _ctx->ver > TONY_SAVEGAME_VERSION) { + delete _ctx->f; + return; + } + + if (_ctx->ver >= 0x3) { + // There is a thumbnail. If the version is between 5 and 7, it's compressed + if ((_ctx->ver >= 0x5) && (_ctx->ver <= 0x7)) { + _ctx->i = 0; + _ctx->i = _ctx->f->readUint32LE(); + _ctx->f->seek(_ctx->i); + } else { + if (_ctx->ver >= 8) + // Skip thumbnail size + _ctx->f->skip(4); + + _ctx->f->seek(160 * 120 * 2, SEEK_CUR); + } + } + + if (_ctx->ver >= 0x5) { + // Skip the difficulty level + _ctx->f->seek(1, SEEK_CUR); + } + + if (_ctx->ver >= 0x4) { // Skip the savegame name, which serves no purpose + _ctx->i = _ctx->f->readByte(); + _ctx->f->seek(_ctx->i, SEEK_CUR); + } + + _ctx->loc = _ctx->f->readUint32LE(); + _ctx->tp._x = _ctx->f->readUint32LE(); + _ctx->tp._y = _ctx->f->readUint32LE(); + _ctx->size = _ctx->f->readUint32LE(); + + if ((_ctx->ver >= 0x5) && (_ctx->ver <= 7)) { + // MPAL was packed! + _ctx->sizecmp = _ctx->f->readUint32LE(); + _ctx->state = new byte[_ctx->size]; + _ctx->statecmp = new byte[_ctx->sizecmp]; + _ctx->f->read(_ctx->statecmp, _ctx->sizecmp); + lzo1x_decompress(_ctx->statecmp, _ctx->sizecmp, _ctx->state, &_ctx->size); + delete[] _ctx->statecmp; + } else { + // Read uncompressed MPAL data + _ctx->state = new byte[_ctx->size]; + _ctx->f->read(_ctx->state, _ctx->size); + } + + mpalLoadState(_ctx->state); + delete[] _ctx->state; + + // Inventory + _ctx->size = _ctx->f->readUint32LE(); + _ctx->state = new byte[_ctx->size]; + _ctx->f->read(_ctx->state, _ctx->size); + _inv.loadState(_ctx->state); + delete[] _ctx->state; + + if (_ctx->ver >= 0x2) { // Version 2: box please + _ctx->size = _ctx->f->readUint32LE(); + _ctx->state = new byte[_ctx->size]; + _ctx->f->read(_ctx->state, _ctx->size); + g_vm->_theBoxes.loadState(_ctx->state); + delete[] _ctx->state; + } + + if (_ctx->ver >= 5) { + // Version 5 + bool bStat = false; + + bStat = _ctx->f->readByte(); + _tony.setShepherdess(bStat); + bStat = _ctx->f->readByte(); + _inter.setPerorate(bStat); + + charsLoadAll(_ctx->f); + } + + if (_ctx->ver >= 6) { + // Load options + GLOBALS._bCfgInvLocked = _ctx->f->readByte(); + GLOBALS._bCfgInvNoScroll = _ctx->f->readByte(); + GLOBALS._bCfgTimerizedText = _ctx->f->readByte(); + GLOBALS._bCfgInvUp = _ctx->f->readByte(); + GLOBALS._bCfgAnni30 = _ctx->f->readByte(); + GLOBALS._bCfgAntiAlias = _ctx->f->readByte(); + GLOBALS._bShowSubtitles = _ctx->f->readByte(); + GLOBALS._bCfgTransparence = _ctx->f->readByte(); + GLOBALS._bCfgInterTips = _ctx->f->readByte(); + GLOBALS._bCfgDubbing = _ctx->f->readByte(); + GLOBALS._bCfgMusic = _ctx->f->readByte(); + GLOBALS._bCfgSFX = _ctx->f->readByte(); + GLOBALS._nCfgTonySpeed = _ctx->f->readByte(); + GLOBALS._nCfgTextSpeed = _ctx->f->readByte(); + GLOBALS._nCfgDubbingVolume = _ctx->f->readByte(); + GLOBALS._nCfgMusicVolume = _ctx->f->readByte(); + GLOBALS._nCfgSFXVolume = _ctx->f->readByte(); + + // Load hotspots + loadChangedHotspot(_ctx->f); + } + + if (_ctx->ver >= 7) { + loadMusic(_ctx->f); + } + + delete _ctx->f; + + CORO_INVOKE_2(unloadLocation, false, NULL); + loadLocation(_ctx->loc, _ctx->tp, RMPoint(-1, -1)); + _tony.setPattern(RMTony::PAT_STANDRIGHT); + + // On older versions, need to an enter action + if (_ctx->ver < 5) + mpalQueryDoAction(0, _ctx->loc, 0); + else { + // In the new ones, we just reset the mcode + mCharResetCodes(); + } + + if (_ctx->ver >= 6) + reapplyChangedHotspot(); + + CORO_INVOKE_0(restoreMusic); + + _bGUIInterface = true; + _bGUIInventory = true; + _bGUIOption = true; + + CORO_END_CODE; +} + +void RMGfxEngine::pauseSound(bool bPause) { + if (_bLocationLoaded) + _loc.pauseSound(bPause); +} + +void RMGfxEngine::initWipe(int type) { + _bWiping = true; + _nWipeType = type; + _nWipeStep = 0; + + if (_nWipeType == 1) + _rcWipeEllipse = Common::Rect(80, 0, 640 - 80, 480); + else if (_nWipeType == 2) + _rcWipeEllipse = Common::Rect(320 - FSTEP, 240 - FSTEP, 320 + FSTEP, 240 + FSTEP); +} + +void RMGfxEngine::closeWipe() { + _bWiping = false; +} + +void RMGfxEngine::waitWipeEnd(CORO_PARAM) { + CoroScheduler.waitForSingleObject(coroParam, _hWipeEvent, CORO_INFINITE); +} + +bool RMGfxEngine::canLoadSave() { + return _bInput && !_tony.inAction() && !g_vm->getIsDemo(); +} + +RMGfxEngine::operator RMGfxTargetBuffer &() { + return _bigBuf; +} + +RMInput &RMGfxEngine::getInput() { + return _input; +} + +RMPointer &RMGfxEngine::getPointer() { + return _point; +} + +/** + * Link to graphic task + */ +void RMGfxEngine::linkGraphicTask(RMGfxTask *task) { + _bigBuf.addPrim(new RMGfxPrimitive(task)); +} + +void RMGfxEngine::setPerorate(bool bpal) { + _inter.setPerorate(bpal); +} + +} // End of namespace Tony diff --git a/engines/tony/gfxengine.h b/engines/tony/gfxengine.h new file mode 100644 index 0000000000..ab32a01972 --- /dev/null +++ b/engines/tony/gfxengine.h @@ -0,0 +1,139 @@ +/* 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. + * + */ + +/* + * This code is based on original Tony Tough source code + * + * Copyright (c) 1997-2003 Nayma Software + */ + +#ifndef TONY_GFXENGINE_H +#define TONY_GFXENGINE_H + +#include "common/scummsys.h" +#include "common/system.h" +#include "common/rect.h" +#include "tony/mpal/memory.h" +#include "tony/game.h" +#include "tony/gfxcore.h" +#include "tony/input.h" +#include "tony/inventory.h" +#include "tony/tonychar.h" +#include "tony/utils.h" + +namespace Tony { + +class RMGfxEngine { +private: + RMGfxTargetBuffer _bigBuf; + RMInput _input; + RMPointer _point; + RMLocation _loc; + RMOptionScreen _opt; + RMTony _tony; + RMInventory _inv; + RMInterface _inter; + RMTextItemName _itemName; + + bool _bOption; + bool _bLocationLoaded; + + bool _bInput; + bool _bAlwaysDrawMouse; + + int _nCurLoc; + RMTonyAction _curAction; + int _curActionObj; + + int _nWipeType; + uint32 _hWipeEvent; + int _nWipeStep; + + bool _bMustEnterMenu; +protected: + static void itemIrq(uint32 dwItem, int nPattern, int nStatus); + void initForNewLocation(int nLoc, RMPoint ptTonyStart, RMPoint start); +public: + bool _bWiping; + Common::Rect _rcWipeEllipse; + bool _bGUIOption; + bool _bGUIInterface; + bool _bGUIInventory; +public: + RMGfxEngine(); + virtual ~RMGfxEngine(); + + // Draw the next frame + void doFrame(CORO_PARAM, bool bDrawLocation); + + // Initializes the graphics engine + void init(); + + // Closes the graphics engine + void close(); + + // Warns when entering or exits the options menu + void openOptionScreen(CORO_PARAM, int type); + + // Enables or disables mouse input + void enableInput(); + void disableInput(); + + // Enables and disables mouse draw + void enableMouse(); + void disableMouse(); + + operator RMGfxTargetBuffer &(); + RMInput &getInput(); + RMPointer &getPointer(); + + // Link to the custom function list + void initCustomDll(); + + // Link to graphic task + void linkGraphicTask(RMGfxTask *task); + + // Manage a location + uint32 loadLocation(int nLoc, RMPoint ptTonyStart, RMPoint start); + void unloadLocation(CORO_PARAM, bool bDoOnExit, uint32 *result); + int getCurrentLocation() const { return _nCurLoc; } + + // State management + void saveState(const Common::String &fn, byte *curThumb, const Common::String &name); + void loadState(CORO_PARAM, const Common::String &fn); + + // Pauses sound + void pauseSound(bool bPause); + + // Wipe + void initWipe(int type); + void closeWipe(); + void waitWipeEnd(CORO_PARAM); + + void setPerorate(bool bpal); + + bool canLoadSave(); +}; + +} // End of namespace Tony + +#endif diff --git a/engines/tony/globals.cpp b/engines/tony/globals.cpp new file mode 100644 index 0000000000..8e4ae240a0 --- /dev/null +++ b/engines/tony/globals.cpp @@ -0,0 +1,135 @@ +/* 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 "common/algorithm.h" +#include "tony/globals.h" + +namespace Tony { + +Globals::Globals() { + _nextLoop = false; + _nextChannel = 0; + _nextSync = 0; + _curChannel = 0; + _flipflop = 0; + _curBackText = NULL; + _bTonyIsSpeaking = false; + _curChangedHotspot = 0; + _tony = NULL; + _pointer = NULL; + _boxes = NULL; + _loc = NULL; + _inventory = NULL; + _input = NULL; + _gfxEngine = NULL; + EnableGUI = NULL; + DisableGUI = NULL; + + _dwTonyNumTexts = 0; + _bTonyInTexts = false; + _bStaticTalk = false; + _bAlwaysDisplay = false; + _bIdleExited = false; + _bSkipSfxNoLoop = false; + _bNoBullsEye = false; + _curDialog = 0; + _curSoundEffect = 0; + _bFadeOutStop = false; + + _bSkipIdle = false; + _hSkipIdle = 0; + _lastMusic = 0; + _lastTappeto = 0; + Common::fill(&_ambiance[0], &_ambiance[200], 0); + _fullScreenMessageLoc = 0; + + // MPAL global variables + _mpalError = 0; + _lpiifCustom = NULL; + _lplpFunctions = NULL; + _lplpFunctionStrings = NULL; + _nObjs = 0; + _nVars = 0; + _hVars = NULL; + _lpmvVars = NULL; + _nMsgs = 0; + _hMsgs = NULL; + _lpmmMsgs = NULL; + _nDialogs = 0; + _hDialogs = NULL; + _lpmdDialogs = NULL; + _nItems = 0; + _hItems = NULL; + _lpmiItems = NULL; + _nLocations = 0; + _hLocations = NULL; + _lpmlLocations = NULL; + _nScripts = 0; + _hScripts = NULL; + _lpmsScripts = NULL; + _nResources = 0; + _lpResources = NULL; + _bExecutingAction = false; + _bExecutingDialog = false; + Common::fill(&_nPollingLocations[0], &_nPollingLocations[MAXPOLLINGLOCATIONS], 0); + Common::fill(&_hEndPollingLocations[0], &_hEndPollingLocations[MAXPOLLINGLOCATIONS], 0); + Common::fill(&_pollingThreads[0], &_pollingThreads[MAXPOLLINGLOCATIONS], 0); + _hAskChoice = 0; + _hDoneChoice = 0; + _nExecutingAction = 0; + _nExecutingDialog = 0; + _nExecutingChoice = 0; + _nSelectedChoice = 0; + _nTonyNextTalkType = RMTony::TALK_NORMAL; + _saveTonyLoc = 0; + + for (int i = 0; i < 16; ++i) { + Common::fill((byte *)&_character[i], (byte *)&_character[i] + sizeof(CharacterStruct), 0); + _isMChar[i] = false; + } + + for (int i = 0; i < 10; ++i) + Common::fill((byte *)&_mCharacter[i], (byte *)&_mCharacter[i] + sizeof(MCharacterStruct), 0); + for (int i = 0; i < 256; ++i) + Common::fill((byte *)&_changedHotspot[i], (byte *)&_changedHotspot[i] + sizeof(ChangedHotspotStruct), 0); + + // Set up globals that have explicit initial values + _bCfgInvLocked = false; + _bCfgInvNoScroll = false; + _bCfgTimerizedText = true; + _bCfgInvUp = false; + _bCfgAnni30 = false; + _bCfgAntiAlias = false; + _bCfgTransparence = true; + _bCfgInterTips = true; + _bShowSubtitles = true; + _nCfgTonySpeed = 3; + _nCfgTextSpeed = 5; + _bCfgDubbing = true; + _bCfgMusic = true; + _bCfgSFX = true; + _nCfgDubbingVolume = 10; + _nCfgMusicVolume = 7; + _nCfgSFXVolume = 10; +} + +} // End of namespace Tony diff --git a/engines/tony/globals.h b/engines/tony/globals.h new file mode 100644 index 0000000000..0ff243b374 --- /dev/null +++ b/engines/tony/globals.h @@ -0,0 +1,288 @@ +/* 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 TONY_GLOBALS +#define TONY_GLOBALS + +#include "common/savefile.h" +#include "tony/gfxengine.h" +#include "tony/input.h" +#include "tony/inventory.h" +#include "tony/loc.h" +#include "tony/tonychar.h" +#include "tony/mpal/mpal.h" +#include "tony/mpal/mpaldll.h" + +namespace Tony { + +#define AMBIANCE_CRICKETS 1 +#define AMBIANCE_CRICKETSMUFFLED 2 +#define AMBIANCE_CRICKETSWIND 3 +#define AMBIANCE_CRICKETSWIND1 4 +#define AMBIANCE_WIND 5 +#define AMBIANCE_SEA 6 +#define AMBIANCE_SEAHALFVOLUME 7 + +struct CharacterStruct { + uint32 _code; + RMItem *_item; + byte _r, _g, _b; + int _talkPattern; + int _standPattern; + int _startTalkPattern, _endTalkPattern; + int _numTexts; + + void save(Common::OutSaveFile *f) { + f->writeUint32LE(_code); + f->writeUint32LE(0); + f->writeByte(_r); + f->writeByte(_g); + f->writeByte(_b); + f->writeUint32LE(_talkPattern); + f->writeUint32LE(_standPattern); + f->writeUint32LE(_startTalkPattern); + f->writeUint32LE(_endTalkPattern); + f->writeUint32LE(_numTexts); + } + + void load(Common::InSaveFile *f) { + _code = f->readUint32LE(); + f->readUint32LE(); + _item = NULL; + _r = f->readByte(); + _g = f->readByte(); + _b = f->readByte(); + _talkPattern = f->readUint32LE(); + _standPattern = f->readUint32LE(); + _startTalkPattern = f->readUint32LE(); + _endTalkPattern = f->readUint32LE(); + _numTexts = f->readUint32LE(); + } +}; + +struct MCharacterStruct { + uint32 _code; + RMItem *_item; + byte _r, _g, _b; + int _x, _y; + int _numTalks[10]; + int _curGroup; + int _numTexts; + bool _bInTexts; + int _curTalk; + bool _bAlwaysBack; + + void save(Common::OutSaveFile *f) { + f->writeUint32LE(_code); + f->writeUint32LE(0); + f->writeByte(_r); + f->writeByte(_g); + f->writeByte(_b); + f->writeUint32LE(_x); + f->writeUint32LE(_y); + for (int i = 0; i < 10; ++i) + f->writeUint32LE(_numTalks[i]); + f->writeUint32LE(_curGroup); + f->writeUint32LE(_numTexts); + f->writeByte(_bInTexts); + f->writeUint32LE(_curTalk); + f->writeByte(_bAlwaysBack); + } + + void load(Common::InSaveFile *f) { + _code = f->readUint32LE(); + f->readUint32LE(); + _item = NULL; + _r = f->readByte(); + _g = f->readByte(); + _b = f->readByte(); + _x = f->readUint32LE(); + _y = f->readUint32LE(); + for (int i = 0; i < 10; ++i) + _numTalks[i] = f->readUint32LE(); + _curGroup = f->readUint32LE(); + _numTexts = f->readUint32LE(); + _bInTexts = f->readByte(); + _curTalk = f->readUint32LE(); + _bAlwaysBack = f->readByte(); + } +}; + +struct ChangedHotspotStruct { + uint32 _dwCode; + uint32 _nX, _nY; + + void save(Common::OutSaveFile *f) { + f->writeUint32LE(_dwCode); + f->writeUint32LE(_nX); + f->writeUint32LE(_nY); + } + + void load(Common::InSaveFile *f) { + _dwCode = f->readUint32LE(); + _nX = f->readUint32LE(); + _nY = f->readUint32LE(); + } +}; + +/** + * Description of a call to a custom function. + */ +typedef struct { + int _nCf; + int _arg1, _arg2, _arg3, _arg4; +} CfCall; + +typedef CfCall *LpCfCall; + +struct CoroutineMutex { + CoroutineMutex() : _eventId(0), _ownerPid(0), _lockCount(0) { } + + uint32 _eventId; + uint32 _ownerPid; + uint32 _lockCount; +}; + +/****************************************************************************\ +* Global variables +\****************************************************************************/ + +/** + * Globals class + */ +class Globals { +public: + Globals(); + + Common::String _nextMusic; + bool _nextLoop; + int _nextChannel; + int _nextSync; + int _curChannel; + int _flipflop; + CharacterStruct _character[16]; + MCharacterStruct _mCharacter[10]; + ChangedHotspotStruct _changedHotspot[256]; + bool _isMChar[16]; + bool _bAlwaysDisplay; + RMPoint _saveTonyPos; + int _saveTonyLoc; + RMTextDialog *_curBackText; + bool _bTonyIsSpeaking; + int _curChangedHotspot; + bool _bCfgInvLocked; + bool _bCfgInvNoScroll; + bool _bCfgTimerizedText; + bool _bCfgInvUp; + bool _bCfgAnni30; + bool _bCfgAntiAlias; + bool _bShowSubtitles; + bool _bCfgTransparence; + bool _bCfgInterTips; + bool _bCfgDubbing; + bool _bCfgMusic; + bool _bCfgSFX; + int _nCfgTonySpeed; + int _nCfgTextSpeed; + int _nCfgDubbingVolume; + int _nCfgMusicVolume; + int _nCfgSFXVolume; + bool _bSkipSfxNoLoop; + bool _bIdleExited; + bool _bNoBullsEye; + int _curDialog; + int _curSoundEffect; + bool _bFadeOutStop; + + RMTony *_tony; + RMPointer *_pointer; + RMGameBoxes *_boxes; + RMLocation *_loc; + RMInventory *_inventory; + RMInput *_input; + RMGfxEngine *_gfxEngine; + + void (*EnableGUI)(); + void (*DisableGUI)(); + + uint32 _dwTonyNumTexts; + bool _bTonyInTexts; + bool _bStaticTalk; + RMTony::CharacterTalkType _nTonyNextTalkType; + + RMPoint _startLocPos[256]; + CoroutineMutex _mut[10]; + + bool _bSkipIdle; + uint32 _hSkipIdle; + + int _lastMusic, _lastTappeto; + + int _ambiance[200]; + RMPoint _fullScreenMessagePt; + int _fullScreenMessageLoc; + + /** + * @defgroup MPAL variables + */ + uint32 _mpalError; + LPITEMIRQFUNCTION _lpiifCustom; + LPLPCUSTOMFUNCTION _lplpFunctions; + Common::String *_lplpFunctionStrings; + uint16 _nObjs; + uint16 _nVars; + MpalHandle _hVars; + LpMpalVar _lpmvVars; + uint16 _nMsgs; + MpalHandle _hMsgs; + LpMpalMsg _lpmmMsgs; + uint16 _nDialogs; + MpalHandle _hDialogs; + LpMpalDialog _lpmdDialogs; + uint16 _nItems; + MpalHandle _hItems; + LpMpalItem _lpmiItems; + uint16 _nLocations; + MpalHandle _hLocations; + LpMpalLocation _lpmlLocations; + uint16 _nScripts; + MpalHandle _hScripts; + LpMpalScript _lpmsScripts; + Common::File _hMpr; + uint16 _nResources; + uint32 *_lpResources; + bool _bExecutingAction; + bool _bExecutingDialog; + uint32 _nPollingLocations[MAXPOLLINGLOCATIONS]; + uint32 _hEndPollingLocations[MAXPOLLINGLOCATIONS]; + uint32 _pollingThreads[MAXPOLLINGLOCATIONS]; + uint32 _hAskChoice; + uint32 _hDoneChoice; + uint32 _nExecutingAction; + uint32 _nExecutingDialog; + uint32 _nExecutingChoice; + uint32 _nSelectedChoice; +}; + +} // End of namespace Tony + +#endif // TONY_GLOBALS diff --git a/engines/tony/input.cpp b/engines/tony/input.cpp new file mode 100644 index 0000000000..e84da04d97 --- /dev/null +++ b/engines/tony/input.cpp @@ -0,0 +1,135 @@ +/* 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. + * + */ + +/* + * This code is based on original Tony Tough source code + * + * Copyright (c) 1997-2003 Nayma Software + */ + +#include "tony/gfxengine.h" +#include "tony/tony.h" + +namespace Tony { + +RMInput::RMInput() { + _leftClickMouse = _leftReleaseMouse = false; + _rightClickMouse = _rightReleaseMouse = false; +} + +void RMInput::poll() { + _leftClickMouse = _leftReleaseMouse = _rightClickMouse = _rightReleaseMouse = false; + + // Get pending events + while (g_system->getEventManager()->pollEvent(_event) && !g_vm->shouldQuit()) { + switch (_event.type) { + case Common::EVENT_MOUSEMOVE: + case Common::EVENT_LBUTTONDOWN: + case Common::EVENT_LBUTTONUP: + case Common::EVENT_RBUTTONDOWN: + case Common::EVENT_RBUTTONUP: + _mousePos = _event.mouse; + + if (_event.type == Common::EVENT_LBUTTONDOWN) { + _leftClickMouse = true; + } else if (_event.type == Common::EVENT_LBUTTONUP) { + _leftReleaseMouse = true; + } else if (_event.type == Common::EVENT_RBUTTONDOWN) { + _rightClickMouse = true; + } else if (_event.type == Common::EVENT_RBUTTONUP) { + _rightReleaseMouse = true; + } else + continue; + + // Since a mouse button has changed, don't do any further event processing this frame + return; + + case Common::EVENT_KEYDOWN: + // Check for debugger + if ((_event.kbd.keycode == Common::KEYCODE_d) && (_event.kbd.flags & Common::KBD_CTRL)) { + // Attach to the debugger + g_vm->_debugger->attach(); + g_vm->_debugger->onFrame(); + } else { + // Flag the given key as being down + _keyDown.push_back(_event.kbd.keycode); + } + return; + + case Common::EVENT_KEYUP: + for (uint i = 0; i < _keyDown.size(); i++) { + if (_keyDown[i] == _event.kbd.keycode) { + _keyDown.remove_at(i); + break; + } + } + return; + + default: + break; + } + } +} + +/** + * Return true if a key has been pressed + */ +bool RMInput::getAsyncKeyState(Common::KeyCode kc) { + // The act of testing for a particular key automatically clears the state, to prevent + // the same key being registered in multiple different frames + for (uint i = 0; i < _keyDown.size(); i++) { + if (_keyDown[i] == kc) { + _keyDown.remove_at(i); + return true; + } + } + return false; +} + +/** + * Reading of the mouse + */ +RMPoint RMInput::mousePos() { + RMPoint p(_mousePos.x, _mousePos.y); + return p; +} + +/** + * Events of mouse clicks + */ +bool RMInput::mouseLeftClicked() { + return _leftClickMouse; +} + +bool RMInput::mouseRightClicked() { + return _rightClickMouse; +} + +bool RMInput::mouseLeftReleased() { + return _leftReleaseMouse; +} + +bool RMInput::mouseRightReleased() { + return _rightReleaseMouse; +} + +} // End of namespace Tony diff --git a/engines/tony/input.h b/engines/tony/input.h new file mode 100644 index 0000000000..274aa8c491 --- /dev/null +++ b/engines/tony/input.h @@ -0,0 +1,80 @@ +/* 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. + * + */ + +/* + * This code is based on original Tony Tough source code + * + * Copyright (c) 1997-2003 Nayma Software + */ + +#ifndef TONY_INPUT_H +#define TONY_INPUT_H + +#include "common/events.h" +#include "common/rect.h" +#include "common/array.h" +#include "common/keyboard.h" +#include "tony/utils.h" + +namespace Tony { + +class RMInput { +private: + Common::Event _event; + + // Mouse related fields + Common::Point _mousePos; + bool _leftClickMouse, _leftReleaseMouse, _rightClickMouse, _rightReleaseMouse; + + // Keyboard related fields + Common::Array<Common::KeyCode> _keyDown; + +public: + RMInput(); + + /** + * Polling (must be performed once per frame) + */ + void poll(); + + /** + * Reading of the mouse + */ + RMPoint mousePos(); + + /** + * Events of mouse clicks + */ + bool mouseLeftClicked(); + bool mouseRightClicked(); + bool mouseLeftReleased(); + bool mouseRightReleased(); + + /** + * Returns true if the given key is pressed + */ + bool getAsyncKeyState(Common::KeyCode kc); +}; + +} // End of namespace Tony + +#endif diff --git a/engines/tony/inventory.cpp b/engines/tony/inventory.cpp new file mode 100644 index 0000000000..6b023d5990 --- /dev/null +++ b/engines/tony/inventory.cpp @@ -0,0 +1,938 @@ +/* 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. + * + */ + +/* + * This code is based on original Tony Tough source code + * + * Copyright (c) 1997-2003 Nayma Software + */ + +#include "common/textconsole.h" +#include "tony/mpal/mpalutils.h" +#include "tony/inventory.h" +#include "tony/game.h" +#include "tony/tony.h" + +namespace Tony { + +/****************************************************************************\ +* RMInventory Methods +\****************************************************************************/ + +RMInventory::RMInventory() { + _items = NULL; + _state = CLOSED; + _bCombining = false; + _csModifyInterface = g_system->createMutex(); + _nItems = 0; + + Common::fill(_inv, _inv + 256, 0); + _nInv = 0; + _curPutY = 0; + _curPutTime = 0; + _curPos = 0; + _bHasFocus = false; + _nSelectObj = 0; + _nCombine = 0; + _bBlinkingRight = false; + _bBlinkingLeft = false; + _miniAction = 0; +} + +RMInventory::~RMInventory() { + close(); + g_system->deleteMutex(_csModifyInterface); +} + +bool RMInventory::checkPointInside(const RMPoint &pt) { + if (!GLOBALS._bCfgInvUp) + return pt._y > RM_SY - 70; + else + return pt._y < 70; +} + +void RMInventory::init() { + // Create the main buffer + create(RM_SX, 68); + setPriority(185); + + // Setup the inventory + _nInv = 0; + _curPos = 0; + _bCombining = false; + + // New items + _nItems = 78; // @@@ Number of takeable items + _items = new RMInventoryItem[_nItems + 1]; + + int curres = 10500; + + // Loop through the items + for (int i = 0; i <= _nItems; i++) { + // Load the items from the resource + RMRes res(curres); + assert(res.isValid()); + Common::SeekableReadStream *ds = res.getReadStream(); + + // Initialize the MPAL inventory item by reading it in. + _items[i]._icon.setInitCurPattern(false); + _items[i]._icon.readFromStream(*ds); + delete ds; + + // Puts in the default pattern 1 + _items[i]._pointer = NULL; + _items[i]._status = 1; + _items[i]._icon.setPattern(1); + _items[i]._icon.doFrame(this, false); + + curres++; + if (i == 0 || i == 28 || i == 29) + continue; + + _items[i]._pointer = new RMGfxSourceBuffer8RLEByteAA[_items[i]._icon.numPattern()]; + + for (int j = 0; j < _items[i]._icon.numPattern(); j++) { + RMResRaw raw(curres); + + assert(raw.isValid()); + + _items[i]._pointer[j].init((const byte *)raw, raw.width(), raw.height(), true); + curres++; + } + } + + _items[28]._icon.setPattern(1); + _items[29]._icon.setPattern(1); + + // Download interface + RMRes res(RES_I_MINIINTER); + assert(res.isValid()); + Common::SeekableReadStream *ds = res.getReadStream(); + _miniInterface.readFromStream(*ds); + _miniInterface.setPattern(1); + delete ds; + + // Create the text for hints on the mini interface + _hints[0].setAlignType(RMText::HCENTER, RMText::VTOP); + _hints[1].setAlignType(RMText::HCENTER, RMText::VTOP); + _hints[2].setAlignType(RMText::HCENTER, RMText::VTOP); + + // The text is taken from MPAL for translation + RMMessage msg1(15); + RMMessage msg2(13); + RMMessage msg3(14); + + _hints[0].writeText(msg1[0], 1); // Examine + _hints[1].writeText(msg2[0], 1); // Take + _hints[2].writeText(msg3[0], 1); // Use + + // Prepare initial inventory + prepare(); + drawOT(Common::nullContext); + clearOT(); +} + +void RMInventory::close() { + // Has memory + if (_items != NULL) { + // Delete the item pointers + for (int i = 0; i <= _nItems; i++) + delete[] _items[i]._pointer; + + // Delete the items array + delete[] _items; + _items = NULL; + } + + destroy(); +} + +void RMInventory::reset() { + _state = CLOSED; + endCombine(); +} + +void RMInventory::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) { + CORO_BEGIN_CONTEXT; + RMPoint pos; + RMPoint pos2; + RMGfxPrimitive *p; + RMGfxPrimitive *p2; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + prim->setDst(RMPoint(0, _curPutY)); + g_system->lockMutex(_csModifyInterface); + CORO_INVOKE_2(RMGfxWoodyBuffer::draw, bigBuf, prim); + g_system->unlockMutex(_csModifyInterface); + + if (_state == SELECTING) { + + if (!GLOBALS._bCfgInvUp) { + _ctx->pos.set((_nSelectObj + 1) * 64 - 20, RM_SY - 113); + _ctx->pos2.set((_nSelectObj + 1) * 64 + 34, RM_SY - 150); + } else { + _ctx->pos.set((_nSelectObj + 1) * 64 - 20, 72 - 4); // The brown part is at the top :( + _ctx->pos2.set((_nSelectObj + 1) * 64 + 34, 119 - 4); + } + + _ctx->p = new RMGfxPrimitive(prim->_task, _ctx->pos); + _ctx->p2 = new RMGfxPrimitive(prim->_task, _ctx->pos2); + + // Draw the mini interface + CORO_INVOKE_2(_miniInterface.draw, bigBuf, _ctx->p); + + if (GLOBALS._bCfgInterTips) { + if (_miniAction == 1) // Examine + CORO_INVOKE_2(_hints[0].draw, bigBuf, _ctx->p2); + else if (_miniAction == 2) // Talk + CORO_INVOKE_2(_hints[1].draw, bigBuf, _ctx->p2); + else if (_miniAction == 3) // Use + CORO_INVOKE_2(_hints[2].draw, bigBuf, _ctx->p2); + } + + delete _ctx->p; + delete _ctx->p2; + } + + CORO_END_CODE; +} + +void RMInventory::removeThis(CORO_PARAM, bool &result) { + if (_state == CLOSED) + result = true; + else + result = false; +} + +void RMInventory::removeItem(int code) { + for (int i = 0; i < _nInv; i++) { + if (_inv[i] == code - 10000) { + g_system->lockMutex(_csModifyInterface); + + Common::copy(&_inv[i + 1], &_inv[i + 1] + (_nInv - i), &_inv[i]); + _nInv--; + + prepare(); + drawOT(Common::nullContext); + clearOT(); + g_system->unlockMutex(_csModifyInterface); + return; + } + } +} + +void RMInventory::addItem(int code) { + if (code <= 10000 || code >= 10101) { + // If we are here, it means that we are adding an item that should not be in the inventory + warning("RMInventory::addItem(%d) - Cannot find a valid icon for this item, and then it will not be added to the inventory", code); + } else { + g_system->lockMutex(_csModifyInterface); + if (_curPos + 8 == _nInv) { + // Break through the inventory! On the flashing pattern + _items[28]._icon.setPattern(2); + } + + _inv[_nInv++] = code - 10000; + + prepare(); + drawOT(Common::nullContext); + clearOT(); + g_system->unlockMutex(_csModifyInterface); + } +} + +void RMInventory::changeItemStatus(uint32 code, uint32 dwStatus) { + if (code <= 10000 || code >= 10101) { + error("RMInventory::changeItemStatus(%d) - Specified object code is not valid", code); + } else { + g_system->lockMutex(_csModifyInterface); + _items[code - 10000]._icon.setPattern(dwStatus); + _items[code - 10000]._status = dwStatus; + + prepare(); + drawOT(Common::nullContext); + clearOT(); + g_system->unlockMutex(_csModifyInterface); + } +} + +void RMInventory::prepare() { + for (int i = 1; i < RM_SX / 64 - 1; i++) { + if (i - 1 + _curPos < _nInv) + addPrim(new RMGfxPrimitive(&_items[_inv[i - 1 + _curPos]]._icon, RMPoint(i * 64, 0))); + else + addPrim(new RMGfxPrimitive(&_items[0]._icon, RMPoint(i * 64, 0))); + } + + // Frecce + addPrim(new RMGfxPrimitive(&_items[29]._icon, RMPoint(0, 0))); + addPrim(new RMGfxPrimitive(&_items[28]._icon, RMPoint(640 - 64, 0))); +} + +bool RMInventory::miniActive() { + return _state == SELECTING; +} + +bool RMInventory::haveFocus(const RMPoint &mpos) { + // When we combine, have the focus only if we are on an arrow (to scroll) + if (_state == OPENED && _bCombining && checkPointInside(mpos) && (mpos._x < 64 || mpos._x > RM_SX - 64)) + return true; + + // If the inventory is open, focus we we go over it + if (_state == OPENED && !_bCombining && checkPointInside(mpos)) + return true; + + // If we are selecting a verb (and then right down), we always focus + if (_state == SELECTING) + return true; + + return false; +} + +void RMInventory::endCombine() { + _bCombining = false; +} + +bool RMInventory::leftClick(const RMPoint &mpos, int &nCombineObj) { + // The left click picks an item from your inventory to use it with the background + int n = mpos._x / 64; + + if (_state == OPENED) { + if (n > 0 && n < RM_SX / 64 - 1 && _inv[n - 1 + _curPos] != 0) { + _bCombining = true; //m_state = COMBINING; + _nCombine = _inv[n - 1 + _curPos]; + nCombineObj = _nCombine + 10000; + + g_vm->playUtilSFX(1); + return true; + } + } + + // Click the right arrow + if ((_state == OPENED) && _bBlinkingRight) { + g_system->lockMutex(_csModifyInterface); + _curPos++; + + if (_curPos + 8 >= _nInv) { + _bBlinkingRight = false; + _items[28]._icon.setPattern(1); + } + + if (_curPos > 0) { + _bBlinkingLeft = true; + _items[29]._icon.setPattern(2); + } + + prepare(); + drawOT(Common::nullContext); + clearOT(); + g_system->unlockMutex(_csModifyInterface); + } + + // Click the left arrow + else if ((_state == OPENED) && _bBlinkingLeft) { + assert(_curPos > 0); + g_system->lockMutex(_csModifyInterface); + _curPos--; + + if (_curPos == 0) { + _bBlinkingLeft = false; + _items[29]._icon.setPattern(1); + } + + if (_curPos + 8 < _nInv) { + _bBlinkingRight = true; + _items[28]._icon.setPattern(2); + } + + prepare(); + drawOT(Common::nullContext); + clearOT(); + g_system->unlockMutex(_csModifyInterface); + } + + return false; +} + +void RMInventory::rightClick(const RMPoint &mpos) { + assert(checkPointInside(mpos)); + + if (_state == OPENED && !_bCombining) { + // Open the context interface + int n = mpos._x / 64; + + if (n > 0 && n < RM_SX / 64 - 1 && _inv[n - 1 + _curPos] != 0) { + _state = SELECTING; + _miniAction = 0; + _nSelectObj = n - 1; + + g_vm->playUtilSFX(0); + } + } + + if ((_state == OPENED) && _bBlinkingRight) { + g_system->lockMutex(_csModifyInterface); + _curPos += 7; + if (_curPos + 8 > _nInv) + _curPos = _nInv - 8; + + if (_curPos + 8 <= _nInv) { + _bBlinkingRight = false; + _items[28]._icon.setPattern(1); + } + + if (_curPos > 0) { + _bBlinkingLeft = true; + _items[29]._icon.setPattern(2); + } + + prepare(); + drawOT(Common::nullContext); + clearOT(); + g_system->unlockMutex(_csModifyInterface); + } else if ((_state == OPENED) && _bBlinkingLeft) { + assert(_curPos > 0); + g_system->lockMutex(_csModifyInterface); + _curPos -= 7; + if (_curPos < 0) + _curPos = 0; + + if (_curPos == 0) { + _bBlinkingLeft = false; + _items[29]._icon.setPattern(1); + } + + if (_curPos + 8 < _nInv) { + _bBlinkingRight = true; + _items[28]._icon.setPattern(2); + } + + prepare(); + drawOT(Common::nullContext); + clearOT(); + g_system->unlockMutex(_csModifyInterface); + } +} + +bool RMInventory::rightRelease(const RMPoint &mpos, RMTonyAction &curAction) { + if (_state == SELECTING) { + _state = OPENED; + + if (_miniAction == 1) { // Examine + curAction = TA_EXAMINE; + return true; + } else if (_miniAction == 2) { // Talk + curAction = TA_TALK; + return true; + } else if (_miniAction == 3) { // Use + curAction = TA_USE; + return true; + } + } + + return false; +} + +#define INVSPEED 20 + +void RMInventory::doFrame(RMGfxTargetBuffer &bigBuf, RMPointer &ptr, RMPoint mpos, bool bCanOpen) { + bool bNeedRedraw = false; + + if (_state != CLOSED) { + // Clean up the OT list + g_system->lockMutex(_csModifyInterface); + clearOT(); + + // DoFrame makes all the objects currently in the inventory be displayed + // @@@ Maybe we should do all takeable objects? Please does not help + for (int i = 0; i < _nInv; i++) { + if (_items[_inv[i]]._icon.doFrame(this, false) && (i >= _curPos && i <= _curPos + 7)) + bNeedRedraw = true; + } + + if ((_state == CLOSING || _state == OPENING || _state == OPENED) && checkPointInside(mpos)) { + if (mpos._x > RM_SX - 64) { + if (_curPos + 8 < _nInv && !_bBlinkingRight) { + _items[28]._icon.setPattern(3); + _bBlinkingRight = true; + bNeedRedraw = true; + } + } else if (_bBlinkingRight) { + _items[28]._icon.setPattern(2); + _bBlinkingRight = false; + bNeedRedraw = true; + } + + if (mpos._x < 64) { + if (_curPos > 0 && !_bBlinkingLeft) { + _items[29]._icon.setPattern(3); + _bBlinkingLeft = true; + bNeedRedraw = true; + } + } else if (_bBlinkingLeft) { + _items[29]._icon.setPattern(2); + _bBlinkingLeft = false; + bNeedRedraw = true; + } + } + + if (_items[28]._icon.doFrame(this, false)) + bNeedRedraw = true; + + if (_items[29]._icon.doFrame(this, false)) + bNeedRedraw = true; + + if (bNeedRedraw) + prepare(); + + g_system->unlockMutex(_csModifyInterface); + } + + if (g_vm->getEngine()->getInput().getAsyncKeyState(Common::KEYCODE_i)) { + GLOBALS._bCfgInvLocked = !GLOBALS._bCfgInvLocked; + } + + if (_bCombining) { // m_state == COMBINING) + ptr.setCustomPointer(&_items[_nCombine]._pointer[_items[_nCombine]._status - 1]); + ptr.setSpecialPointer(RMPointer::PTR_CUSTOM); + } + + if (!GLOBALS._bCfgInvUp) { + if ((_state == CLOSED) && (mpos._y > RM_SY - 10 || GLOBALS._bCfgInvLocked) && bCanOpen) { + if (!GLOBALS._bCfgInvNoScroll) { + _state = OPENING; + _curPutY = RM_SY - 1; + _curPutTime = g_vm->getTime(); + } else { + _state = OPENED; + _curPutY = RM_SY - 68; + } + } else if (_state == OPENED) { + if ((mpos._y < RM_SY - 70 && !GLOBALS._bCfgInvLocked) || !bCanOpen) { + if (!GLOBALS._bCfgInvNoScroll) { + _state = CLOSING; + _curPutY = RM_SY - 68; + _curPutTime = g_vm->getTime(); + } else { + _state = CLOSED; + } + } + } else if (_state == OPENING) { + while (_curPutTime + INVSPEED < g_vm->getTime()) { + _curPutY -= 3; + _curPutTime += INVSPEED; + } + + if (_curPutY <= RM_SY - 68) { + _state = OPENED; + _curPutY = RM_SY - 68; + } + + } else if (_state == CLOSING) { + while (_curPutTime + INVSPEED < g_vm->getTime()) { + _curPutY += 3; + _curPutTime += INVSPEED; + } + + if (_curPutY > 480) + _state = CLOSED; + } + } else { + if ((_state == CLOSED) && (mpos._y < 10 || GLOBALS._bCfgInvLocked) && bCanOpen) { + if (!GLOBALS._bCfgInvNoScroll) { + _state = OPENING; + _curPutY = - 68; + _curPutTime = g_vm->getTime(); + } else { + _state = OPENED; + _curPutY = 0; + } + } else if (_state == OPENED) { + if ((mpos._y > 70 && !GLOBALS._bCfgInvLocked) || !bCanOpen) { + if (!GLOBALS._bCfgInvNoScroll) { + _state = CLOSING; + _curPutY = -2; + _curPutTime = g_vm->getTime(); + } else { + _state = CLOSED; + } + } + } else if (_state == OPENING) { + while (_curPutTime + INVSPEED < g_vm->getTime()) { + _curPutY += 3; + _curPutTime += INVSPEED; + } + + if (_curPutY >= 0) { + _state = OPENED; + _curPutY = 0; + } + } else if (_state == CLOSING) { + while (_curPutTime + INVSPEED < g_vm->getTime()) { + _curPutY -= 3; + _curPutTime += INVSPEED; + } + + if (_curPutY < -68) + _state = CLOSED; + } + } + + if (_state == SELECTING) { + int startx = (_nSelectObj + 1) * 64 - 20; + int starty; + + if (!GLOBALS._bCfgInvUp) + starty = RM_SY - 109; + else + starty = 70; + + // Make sure it is on one of the verbs + if (mpos._y > starty && mpos._y < starty + 45) { + if (mpos._x > startx && mpos._x < startx + 40) { + if (_miniAction != 1) { + _miniInterface.setPattern(2); + _miniAction = 1; + g_vm->playUtilSFX(1); + } + } else if (mpos._x >= startx + 40 && mpos._x < startx + 80) { + if (_miniAction != 2) { + _miniInterface.setPattern(3); + _miniAction = 2; + g_vm->playUtilSFX(1); + } + } else if (mpos._x >= startx + 80 && mpos._x < startx + 108) { + if (_miniAction != 3) { + _miniInterface.setPattern(4); + _miniAction = 3; + g_vm->playUtilSFX(1); + } + } else { + _miniInterface.setPattern(1); + _miniAction = 0; + } + } else { + _miniInterface.setPattern(1); + _miniAction = 0; + } + + // Update the mini-interface + _miniInterface.doFrame(&bigBuf, false); + } + + if ((_state != CLOSED) && !_nInList) { + bigBuf.addPrim(new RMGfxPrimitive(this)); + } +} + +bool RMInventory::itemInFocus(const RMPoint &mpt) { + if ((_state == OPENED || _state == OPENING) && checkPointInside(mpt)) + return true; + else + return false; +} + +RMItem *RMInventory::whichItemIsIn(const RMPoint &mpt) { + if (_state == OPENED) { + if (checkPointInside(mpt)) { + int n = mpt._x / 64; + if (n > 0 && n < RM_SX / 64 - 1 && _inv[n - 1 + _curPos] != 0 && (!_bCombining || _inv[n - 1 + _curPos] != _nCombine)) + return &_items[_inv[n - 1 + _curPos]]._icon; + } + } + + return NULL; +} + +int RMInventory::getSaveStateSize() { + // m_inv pattern m_nInv + return 256 * 4 + 256 * 4 + 4; +} + +void RMInventory::saveState(byte *state) { + WRITE_LE_UINT32(state, _nInv); + state += 4; + for (int i = 0; i < 256; ++i) { + WRITE_LE_UINT32(state, _inv[i]); + state += 4; + } + + int x; + for (int i = 0; i < 256; i++) { + if (i < _nItems) + x = _items[i]._status; + else + x = 0; + + WRITE_LE_UINT32(state, x); + state += 4; + } +} + +int RMInventory::loadState(byte *state) { + _nInv = READ_LE_UINT32(state); + state += 4; + for (int i = 0; i < 256; ++i) { + _inv[i] = READ_LE_UINT32(state); + state += 4; + } + + int x; + for (int i = 0; i < 256; i++) { + x = READ_LE_UINT32(state); + state += 4; + + if (i < _nItems) { + _items[i]._status = x; + _items[i]._icon.setPattern(x); + } + } + + _curPos = 0; + _bCombining = false; + + _items[29]._icon.setPattern(1); + + if (_nInv > 8) + _items[28]._icon.setPattern(2); + else + _items[28]._icon.setPattern(1); + + prepare(); + drawOT(Common::nullContext); + clearOT(); + + return getSaveStateSize(); +} + +RMInventory &RMInventory::operator+=(RMItem *item) { + addItem(item->mpalCode()); + return *this; +} + +RMInventory &RMInventory::operator+=(RMItem &item) { + addItem(item.mpalCode()); + return *this; +} + +RMInventory &RMInventory::operator+=(int code) { + addItem(code); + return *this; +} + +/****************************************************************************\ +* RMInterface methods +\****************************************************************************/ + +RMInterface::RMInterface() : RMGfxSourceBuffer8RLEByte() { + _bActive = _bPerorate = false; + _lastHotZone = -1; +} + +RMInterface::~RMInterface() { +} + +bool RMInterface::active() { + return _bActive; +} + +int RMInterface::onWhichBox(RMPoint pt) { + pt -= _openStart; + + // Check how many verbs you have to consider + int max = 4; + if (_bPerorate) + max = 5; + + // Find the verb + for (int i = 0; i < max; i++) { + if (_hotbbox[i].ptInRect(pt)) + return i; + } + + // Found no verb + return -1; +} + +void RMInterface::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) { + CORO_BEGIN_CONTEXT; + int h; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + prim->getDst().topLeft() = _openStart; + CORO_INVOKE_2(RMGfxSourceBuffer8RLEByte::draw, bigBuf, prim); + + // Check if there is a draw hot zone + _ctx->h = onWhichBox(_mpos); + if (_ctx->h != -1) { + prim->getDst().topLeft() = _openStart; + CORO_INVOKE_2(_hotzone[_ctx->h].draw, bigBuf, prim); + + if (_lastHotZone != _ctx->h) { + _lastHotZone = _ctx->h; + g_vm->playUtilSFX(1); + } + + if (GLOBALS._bCfgInterTips) { + prim->getDst().topLeft() = _openStart + RMPoint(70, 177); + CORO_INVOKE_2(_hints[_ctx->h].draw, bigBuf, prim); + } + } else + _lastHotZone = -1; + + CORO_END_CODE; +} + +void RMInterface::doFrame(RMGfxTargetBuffer &bigBuf, RMPoint mousepos) { + // If needed, add to the OT schedule list + if (!_nInList && _bActive) + bigBuf.addPrim(new RMGfxPrimitive(this)); + + _mpos = mousepos; +} + +void RMInterface::clicked(const RMPoint &mousepos) { + _bActive = true; + _openPos = mousepos; + + // Calculate the top left corner of the interface + _openStart = _openPos - RMPoint(_dimx / 2, _dimy / 2); + _lastHotZone = -1; + + // Keep it inside the screen + if (_openStart._x < 0) + _openStart._x = 0; + if (_openStart._y < 0) + _openStart._y = 0; + if (_openStart._x + _dimx > RM_SX) + _openStart._x = RM_SX - _dimx; + if (_openStart._y + _dimy > RM_SY) + _openStart._y = RM_SY - _dimy; + + // Play the sound effect + g_vm->playUtilSFX(0); +} + +bool RMInterface::released(const RMPoint &mousepos, RMTonyAction &action) { + if (!_bActive) + return false; + + _bActive = false; + + switch (onWhichBox(mousepos)) { + case 0: + action = TA_TAKE; + break; + + case 1: + action = TA_TALK; + break; + + case 2: + action = TA_USE; + break; + + case 3: + action = TA_EXAMINE; + break; + + case 4: + action = TA_PERORATE; + break; + + default: // No verb + return false; + } + + return true; +} + +void RMInterface::reset() { + _bActive = false; +} + +void RMInterface::setPerorate(bool bOn) { + _bPerorate = bOn; +} + +bool RMInterface::getPerorate() { + return _bPerorate; +} + +void RMInterface::init() { + RMResRaw inter(RES_I_INTERFACE); + RMRes pal(RES_I_INTERPPAL); + + setPriority(191); + + RMGfxSourceBuffer::init(inter, inter.width(), inter.height()); + loadPaletteWA(RES_I_INTERPAL); + + for (int i = 0; i < 5; i++) { + RMResRaw part(RES_I_INTERP1 + i); + + _hotzone[i].init(part, part.width(), part.height()); + _hotzone[i].loadPaletteWA(pal); + } + + _hotbbox[0].setRect(126, 123, 159, 208); // Take + _hotbbox[1].setRect(90, 130, 125, 186); // About + _hotbbox[2].setRect(110, 60, 152, 125); + _hotbbox[3].setRect(56, 51, 93, 99); + _hotbbox[4].setRect(51, 105, 82, 172); + + _hints[0].setAlignType(RMText::HRIGHT, RMText::VTOP); + _hints[1].setAlignType(RMText::HRIGHT, RMText::VTOP); + _hints[2].setAlignType(RMText::HRIGHT, RMText::VTOP); + _hints[3].setAlignType(RMText::HRIGHT, RMText::VTOP); + _hints[4].setAlignType(RMText::HRIGHT, RMText::VTOP); + + // The text is taken from MPAL for translation + RMMessage msg0(12); + RMMessage msg1(13); + RMMessage msg2(14); + RMMessage msg3(15); + RMMessage msg4(16); + + _hints[0].writeText(msg0[0], 1); // Take + _hints[1].writeText(msg1[0], 1); // Talk + _hints[2].writeText(msg2[0], 1); // Use + _hints[3].writeText(msg3[0], 1); // Examine + _hints[4].writeText(msg4[0], 1); // Show Yourself + + _bActive = false; + _bPerorate = false; + _lastHotZone = 0; +} + +void RMInterface::close() { + destroy(); + + for (int i = 0; i < 5; i++) + _hotzone[i].destroy(); +} + +} // End of namespace Tony diff --git a/engines/tony/inventory.h b/engines/tony/inventory.h new file mode 100644 index 0000000000..1d660d51cd --- /dev/null +++ b/engines/tony/inventory.h @@ -0,0 +1,240 @@ +/* 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. + * + */ + +/* + * This code is based on original Tony Tough source code + * + * Copyright (c) 1997-2003 Nayma Software + */ + +#ifndef TONY_INVENTORY_H +#define TONY_INVENTORY_H + +#include "common/scummsys.h" +#include "common/system.h" +#include "tony/font.h" +#include "tony/game.h" +#include "tony/gfxcore.h" +#include "tony/loc.h" + +namespace Tony { + +struct RMInventoryItem { + RMItem _icon; + RMGfxSourceBuffer8RLEByteAA *_pointer; + int _status; +}; + +class RMInventory : public RMGfxWoodyBuffer { +private: + enum InventoryState { + CLOSED, + OPENING, + OPENED, + CLOSING, + SELECTING + }; + +protected: + int _nItems; + RMInventoryItem *_items; + + int _inv[256]; + int _nInv; + int _curPutY; + uint32 _curPutTime; + + int _curPos; + InventoryState _state; + bool _bHasFocus; + int _nSelectObj; + int _nCombine; + bool _bCombining; + + bool _bBlinkingRight, _bBlinkingLeft; + + int _miniAction; + RMItem _miniInterface; + RMText _hints[3]; + + OSystem::MutexRef _csModifyInterface; + +protected: + /** + * Prepare the image inventory. It should be recalled whenever the inventory changes + */ + void prepare(); + + /** + * Check if the mouse Y position is conrrect, even under the inventory portion of the screen + */ + bool checkPointInside(const RMPoint &pt); + +public: + RMInventory(); + virtual ~RMInventory(); + + /** + * Prepare a frame + */ + void doFrame(RMGfxTargetBuffer &bigBuf, RMPointer &ptr, RMPoint mpos, bool bCanOpen); + + /** + * Initialization and closing + */ + void init(); + void close(); + void reset(); + + /** + * Overload test for removal from OT list + */ + virtual void removeThis(CORO_PARAM, bool &result); + + /** + * Overload the drawing of the inventory + */ + virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim); + + /** + * Method for determining whether the inventory currently has the focus + */ + bool haveFocus(const RMPoint &mpos); + + /** + * Method for determining if the mini interface is active + */ + bool miniActive(); + + /** + * Handle the left mouse click (only when the inventory has the focus) + */ + bool leftClick(const RMPoint &mpos, int &nCombineObj); + + /** + * Handle the right mouse button (only when the inventory has the focus) + */ + void rightClick(const RMPoint &mpos); + bool rightRelease(const RMPoint &mpos, RMTonyAction &curAction); + + /** + * Warn that an item combine is over + */ + void endCombine(); + +public: + /** + * Add an item to the inventory + */ + void addItem(int code); + RMInventory &operator+=(RMItem *item); + RMInventory &operator+=(RMItem &item); + RMInventory &operator+=(int code); + + /** + * Removes an item + */ + void removeItem(int code); + + /** + * We are on an object? + */ + RMItem *whichItemIsIn(const RMPoint &mpt); + bool itemInFocus(const RMPoint &mpt); + + /** + * Change the icon of an item + */ + void changeItemStatus(uint32 dwCode, uint32 dwStatus); + + /** + * Save methods + */ + int getSaveStateSize(); + void saveState(byte *state); + int loadState(byte *state); +}; + +class RMInterface : public RMGfxSourceBuffer8RLEByte { +private: + bool _bActive; + RMPoint _mpos; + RMPoint _openPos; + RMPoint _openStart; + RMText _hints[5]; + RMGfxSourceBuffer8RLEByte _hotzone[5]; + RMRect _hotbbox[5]; + bool _bPerorate; + int _lastHotZone; + +protected: + /** + * Return which box a given point is in + */ + int onWhichBox(RMPoint pt); + +public: + RMInterface(); + virtual ~RMInterface(); + + /** + * The usual DoFrame (poll the graphics engine) + */ + void doFrame(RMGfxTargetBuffer &bigBuf, RMPoint mousepos); + + /** + * TRUE if it is active (you can select items) + */ + bool active(); + + /** + * Initialization + */ + void init(); + void close(); + + /** + * Reset the interface + */ + void reset(); + + /** + * Warns of mouse clicks and releases + */ + void clicked(const RMPoint &mousepos); + bool released(const RMPoint &mousepos, RMTonyAction &action); + + /** + * Enables or disables the fifth verb + */ + void setPerorate(bool bOn); + bool getPerorate(); + + /** + * Overloaded Draw + */ + virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim); +}; + +} // End of namespace Tony + +#endif diff --git a/engines/tony/loc.cpp b/engines/tony/loc.cpp new file mode 100644 index 0000000000..5beac842f9 --- /dev/null +++ b/engines/tony/loc.cpp @@ -0,0 +1,2259 @@ +/* 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. + * + */ + +/* + * This code is based on original Tony Tough source code + * + * Copyright (c) 1997-2003 Nayma Software + */ + +#include "common/memstream.h" +#include "common/scummsys.h" +#include "tony/mpal/mpalutils.h" +#include "tony/game.h" +#include "tony/loc.h" +#include "tony/tony.h" + +namespace Tony { + +using namespace ::Tony::MPAL; + +/****************************************************************************\ +* RMPalette Methods +\****************************************************************************/ + +void RMPalette::readFromStream(Common::ReadStream &ds) { + ds.read(_data, 1024); +} + +/****************************************************************************\ +* RMSlot Methods +\****************************************************************************/ + +void RMPattern::RMSlot::readFromStream(Common::ReadStream &ds, bool bLOX) { + // Type + byte type = ds.readByte(); + _type = (RMPattern::RMSlotType)type; + + // Data + _data = ds.readSint32LE(); + + // Position + _pos.readFromStream(ds); + + // Generic flag + _flag = ds.readByte(); +} + +/****************************************************************************\ +* RMPattern Methods +\****************************************************************************/ + +void RMPattern::readFromStream(Common::ReadStream &ds, bool bLOX) { + // Pattern name + if (!bLOX) + _name = readString(ds); + + // Velocity + _speed = ds.readSint32LE(); + + // Position + _pos.readFromStream(ds); + + // Flag for pattern looping + _bLoop = ds.readSint32LE(); + + // Number of slots + _nSlots = ds.readSint32LE(); + + // Create and read the slots + _slots = new RMSlot[_nSlots]; + + for (int i = 0; i < _nSlots && !ds.err(); i++) { + if (bLOX) + _slots[i].readFromStream(ds, true); + else + _slots[i].readFromStream(ds, false); + } +} + +void RMPattern::updateCoord() { + _curPos = _pos + _slots[_nCurSlot].pos(); +} + +void RMPattern::stopSfx(RMSfx *sfx) { + for (int i = 0; i < _nSlots; i++) { + if (_slots[i]._type == SOUND) { + if (!sfx[_slots[i]._data]._name.empty() && sfx[_slots[i]._data]._name[0] == '_') + sfx[_slots[i]._data].stop(); + else if (GLOBALS._bSkipSfxNoLoop) + sfx[_slots[i]._data].stop(); + } + } +} + +int RMPattern::init(RMSfx *sfx, bool bPlayP0, byte *bFlag) { + // Read the current time + _nStartTime = g_vm->getTime(); + _nCurSlot = 0; + + // Find the first frame of the pattern + int i = 0; + while (_slots[i]._type != SPRITE) { + assert(i + 1 < _nSlots); + i++; + } + + _nCurSlot = i; + _nCurSprite = _slots[i]._data; + if (bFlag) + *bFlag = _slots[i]._flag; + + // Calculate the current coordinates + updateCoord(); + + // Check for sound: + // If the slot is 0, play + // If speed == 0, must play unless it goes into loop '_', or if specified by the parameter + // If speed != 0, play only the loop + for (i = 0; i < _nSlots; i++) { + if (_slots[i]._type == SOUND) { + if (i == 0) { + if (!sfx[_slots[i]._data]._name.empty() && sfx[_slots[i]._data]._name[0] == '_') { + sfx[_slots[i]._data].setVolume(_slots[i].pos()._x); + sfx[_slots[i]._data].play(true); + } else { + sfx[_slots[i]._data].setVolume(_slots[i].pos()._x); + sfx[_slots[i]._data].play(); + } + } else if (_speed == 0) { + if (bPlayP0) { + sfx[_slots[i]._data].setVolume(_slots[i].pos()._x); + sfx[_slots[i]._data].play(); + } else if (!sfx[_slots[i]._data]._name.empty() && sfx[_slots[i]._data]._name[0] == '_') { + sfx[_slots[i]._data].setVolume(_slots[i].pos()._x); + sfx[_slots[i]._data].play(true); + } + } else { + if (_bLoop && !sfx[_slots[i]._data]._name.empty() && sfx[_slots[i]._data]._name[0] == '_') { + sfx[_slots[i]._data].setVolume(_slots[i].pos()._x); + sfx[_slots[i]._data].play(true); + } + } + } + } + + return _nCurSprite; +} + +int RMPattern::update(uint32 hEndPattern, byte &bFlag, RMSfx *sfx) { + int CurTime = g_vm->getTime(); + + // If the speed is 0, then the pattern never advances + if (_speed == 0) { + CoroScheduler.pulseEvent(hEndPattern); + bFlag = _slots[_nCurSlot]._flag; + return _nCurSprite; + } + + // Is it time to change the slots? + while (_nStartTime + _speed <= (uint32)CurTime) { + _nStartTime += _speed; + if (_slots[_nCurSlot]._type == SPRITE) + _nCurSlot++; + if (_nCurSlot == _nSlots) { + _nCurSlot = 0; + bFlag = _slots[_nCurSlot]._flag; + + CoroScheduler.pulseEvent(hEndPattern); + + // @@@ If there is no loop pattern, and there's a warning that it's the final + // frame, then remain on the last frame + if (!_bLoop) { + _nCurSlot = _nSlots - 1; + bFlag = _slots[_nCurSlot]._flag; + return _nCurSprite; + } + } + + for (;;) { + switch (_slots[_nCurSlot]._type) { + case SPRITE: + // Read the next sprite + _nCurSprite = _slots[_nCurSlot]._data; + + // Update the parent & child coordinates + updateCoord(); + break; + + case SOUND: + if (sfx != NULL) { + sfx[_slots[_nCurSlot]._data].setVolume(_slots[_nCurSlot].pos()._x); + + if (sfx[_slots[_nCurSlot]._data]._name.empty() || sfx[_slots[_nCurSlot]._data]._name[0] != '_') + sfx[_slots[_nCurSlot]._data].play(false); + else + sfx[_slots[_nCurSlot]._data].play(true); + } + break; + + case COMMAND: + assert(0); + break; + + default: + assert(0); + break; + } + + if (_slots[_nCurSlot]._type == SPRITE) + break; + _nCurSlot++; + } + } + + // Return the current sprite + bFlag = _slots[_nCurSlot]._flag; + return _nCurSprite; +} + +RMPattern::RMPattern() { + _slots = NULL; + _speed = 0; + _bLoop = 0; + _nSlots = 0; + _nCurSlot = 0; + _nCurSprite = 0; + _nStartTime = 0; + _slots = NULL; +} + +/** + * Reads the position of the pattern + */ +RMPoint RMPattern::pos() { + return _curPos; +} + +RMPattern::~RMPattern() { + if (_slots != NULL) { + delete[] _slots; + _slots = NULL; + } +} + +/****************************************************************************\ +* RMSprite Methods +\****************************************************************************/ + +void RMSprite::init(RMGfxSourceBuffer *buf) { + _buf = buf; +} + +void RMSprite::LOXGetSizeFromStream(Common::SeekableReadStream &ds, int *dimx, int *dimy) { + uint32 pos = ds.pos(); + + *dimx = ds.readSint32LE(); + *dimy = ds.readSint32LE(); + + ds.seek(pos); +} + +void RMSprite::getSizeFromStream(Common::SeekableReadStream &ds, int *dimx, int *dimy) { + uint32 pos = ds.pos(); + + _name = readString(ds); + *dimx = ds.readSint32LE(); + *dimy = ds.readSint32LE(); + + ds.seek(pos); +} + +void RMSprite::readFromStream(Common::SeekableReadStream &ds, bool bLOX) { + // Sprite name + if (!bLOX) + _name = readString(ds); + + // Dimensions + int dimx = ds.readSint32LE(); + int dimy = ds.readSint32LE(); + + // Bounding box + _rcBox.readFromStream(ds); + + // Unused space + if (!bLOX) + ds.skip(32); + + // Create buffer and read + _buf->init(ds, dimx, dimy); +} + +void RMSprite::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) { + _buf->draw(coroParam, bigBuf, prim); +} + +void RMSprite::setPalette(byte *buf) { + ((RMGfxSourceBufferPal *)_buf)->loadPalette(buf); +} + +RMSprite::RMSprite() { + _buf = NULL; +} + +RMSprite::~RMSprite() { + if (_buf) { + delete _buf; + _buf = NULL; + } +} + +/****************************************************************************\ +* RMSfx Methods +\****************************************************************************/ + +void RMSfx::readFromStream(Common::ReadStream &ds, bool bLOX) { + // sfx name + _name = readString(ds); + + int size = ds.readSint32LE(); + + // Read the entire buffer into a MemoryReadStream + byte *buffer = (byte *)malloc(size); + ds.read(buffer, size); + Common::SeekableReadStream *stream = new Common::MemoryReadStream(buffer, size, DisposeAfterUse::YES); + + // Create the sound effect + _fx = g_vm->createSFX(stream); + _fx->setLoop(false); +} + +RMSfx::RMSfx() { + _fx = NULL; + _bPlayingLoop = false; +} + +RMSfx::~RMSfx() { + if (_fx) { + _fx->release(); + _fx = NULL; + } +} + +void RMSfx::play(bool bLoop) { + if (_fx && !_bPlayingLoop) { + _fx->setLoop(bLoop); + _fx->play(); + + if (bLoop) + _bPlayingLoop = true; + } +} + +void RMSfx::setVolume(int vol) { + if (_fx) { + _fx->setVolume(vol); + } +} + +void RMSfx::pause(bool bPause) { + if (_fx) { + _fx->setPause(bPause); + } +} + +void RMSfx::stop() { + if (_fx) { + _fx->stop(); + _bPlayingLoop = false; + } +} + +/****************************************************************************\ +* RMItem Methods +\****************************************************************************/ + +int RMItem::getCurPattern() { + return _nCurPattern; +} + +RMGfxSourceBuffer *RMItem::newItemSpriteBuffer(int dimx, int dimy, bool bPreRLE) { + if (_cm == CM_256) { + RMGfxSourceBuffer8RLE *spr; + + if (_FX == 2) { // AB + spr = new RMGfxSourceBuffer8RLEWordAB; + } else if (_FX == 1) { // OMBRA+AA + if (dimx == -1 || dimx > 255) + spr = new RMGfxSourceBuffer8RLEWordAA; + else + spr = new RMGfxSourceBuffer8RLEByteAA; + + spr->setAlphaBlendColor(_FXparm); + if (bPreRLE) + spr->setAlreadyCompressed(); + } else { + if (dimx == -1 || dimx > 255) + spr = new RMGfxSourceBuffer8RLEWord; + else + spr = new RMGfxSourceBuffer8RLEByte; + + if (bPreRLE) + spr->setAlreadyCompressed(); + } + + return spr; + } else + return new RMGfxSourceBuffer16; +} + +bool RMItem::isIn(const RMPoint &pt, int *size) { + RMRect rc; + + if (!_bIsActive) + return false; + + // Search for the right bounding box to use - use the sprite's if it has one, otherwise use the generic one + if (_nCurPattern != 0 && !_sprites[_nCurSprite]._rcBox.isEmpty()) + rc = _sprites[_nCurSprite]._rcBox + calculatePos(); + else if (!_rcBox.isEmpty()) + rc = _rcBox; + // If no box, return immediately + else + return false; + + if (size != NULL) + *size = rc.size(); + + return rc.ptInRect(pt + _curScroll); +} + +void RMItem::readFromStream(Common::SeekableReadStream &ds, bool bLOX) { + // MPAL code + _mpalCode = ds.readSint32LE(); + + // Object name + _name = readString(ds); + + // Z (signed) + _z = ds.readSint32LE(); + + // Parent position + _pos.readFromStream(ds); + + // Hotspot + _hot.readFromStream(ds); + + // Bounding box + _rcBox.readFromStream(ds); + + // Number of sprites, sound effects, and patterns + _nSprites = ds.readSint32LE(); + _nSfx = ds.readSint32LE(); + _nPatterns = ds.readSint32LE(); + + // Color mode + byte cm = ds.readByte(); + _cm = (RMColorMode)cm; + + // Flag for the presence of custom palette differences + _bPal = ds.readByte(); + + if (_cm == CM_256) { + // If there is a palette, read it in + if (_bPal) + _pal.readFromStream(ds); + } + + // MPAL data + if (!bLOX) + ds.skip(20); + + _FX = ds.readByte(); + _FXparm = ds.readByte(); + + if (!bLOX) + ds.skip(106); + + // Create sub-classes + if (_nSprites > 0) + _sprites = new RMSprite[_nSprites]; + if (_nSfx > 0) + _sfx = new RMSfx[_nSfx]; + _patterns = new RMPattern[_nPatterns + 1]; + + int dimx, dimy; + // Read in class data + if (!ds.err()) { + for (int i = 0; i < _nSprites && !ds.err(); i++) { + // Download the sprites + if (bLOX) { + _sprites[i].LOXGetSizeFromStream(ds, &dimx, &dimy); + _sprites[i].init(newItemSpriteBuffer(dimx, dimy, true)); + _sprites[i].readFromStream(ds, true); + } else { + _sprites[i].getSizeFromStream(ds, &dimx, &dimy); + _sprites[i].init(newItemSpriteBuffer(dimx, dimy, false)); + _sprites[i].readFromStream(ds, false); + } + + if (_cm == CM_256 && _bPal) + _sprites[i].setPalette(_pal._data); + } + } + + if (!ds.err()) { + for (int i = 0; i < _nSfx && !ds.err(); i++) { + if (bLOX) + _sfx[i].readFromStream(ds, true); + else + _sfx[i].readFromStream(ds, false); + } + } + + // Read the pattern from pattern 1 + if (!ds.err()) { + for (int i = 1; i <= _nPatterns && !ds.err(); i++) { + if (bLOX) + _patterns[i].readFromStream(ds, true); + else + _patterns[i].readFromStream(ds, false); + } + } + + // Initialize the current pattern + if (_bInitCurPattern) + setPattern(mpalQueryItemPattern(_mpalCode)); + + // Initialize the current activation state + _bIsActive = mpalQueryItemIsActive(_mpalCode); +} + +RMGfxPrimitive *RMItem::newItemPrimitive() { + return new RMGfxPrimitive(this); +} + +void RMItem::setScrollPosition(const RMPoint &scroll) { + _curScroll = scroll; +} + +bool RMItem::doFrame(RMGfxTargetBuffer *bigBuf, bool bAddToList) { + int oldSprite = _nCurSprite; + + // Pattern 0 = Do not draw anything! + if (_nCurPattern == 0) + return false; + + // We do an update of the pattern, which also returns the current frame + if (_nCurPattern != 0) { + _nCurSprite = _patterns[_nCurPattern].update(_hEndPattern, _bCurFlag, _sfx); + + // WORKAROUND: Currently, m_nCurSprite = -1 is used to flag that an item should be removed. + // However, this seems to be done inside a process waiting on an event pulsed inside the pattern + // Update method. So the value of m_nCurSprite = -1 is being destroyed with the return value + // replacing it. It may be that the current coroutine PulseEvent implementation is wrong somehow. + // In any case, a special check here is done for items that have ended + if (_nCurPattern == 0) + _nCurSprite = -1; + } + + // If the function returned -1, it means that the pattern has finished + if (_nCurSprite == -1) { + // We have pattern 0, so leave. The class will self de-register from the OT list + _nCurPattern = 0; + return false; + } + + // If we are not in the OT list, add ourselves + if (!_nInList && bAddToList) + bigBuf->addPrim(newItemPrimitive()); + + return oldSprite != _nCurSprite; +} + +RMPoint RMItem::calculatePos() { + return _pos + _patterns[_nCurPattern].pos(); +} + +void RMItem::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + // If CurSprite == -1, then the pattern is finished + if (_nCurSprite == -1) + return; + + // Set the flag + prim->setFlag(_bCurFlag); + + // Offset direction for scrolling + prim->getDst().offset(-_curScroll); + + // We must offset the cordinates of the item inside the primitive + // It is estimated as nonno + (babbo + figlio) + prim->getDst().offset(calculatePos()); + + // No stretching, please + prim->setStretch(false); + + // Now we turn to the generic surface drawing routines + CORO_INVOKE_2(_sprites[_nCurSprite].draw, bigBuf, prim); + + CORO_END_CODE; +} + +/** + * Overloaded priority: it's based on Z ordering + */ +int RMItem::priority() { + return _z; +} + +/** + * Pattern number + */ +int RMItem::numPattern() { + return _nPatterns; +} + +void RMItem::removeThis(CORO_PARAM, bool &result) { + // Remove from the OT list if the current frame is -1 (pattern over) + result = (_nCurSprite == -1); +} + +void RMItem::setStatus(int nStatus) { + _bIsActive = (nStatus > 0); +} + +RMPoint RMItem::getHotspot() { + return _hot; +} + +int RMItem::mpalCode() { + return _mpalCode; +} + +void RMItem::setPattern(int nPattern, bool bPlayP0) { + assert(nPattern >= 0 && nPattern <= _nPatterns); + + if (_sfx) { + if (_nCurPattern > 0) + _patterns[_nCurPattern].stopSfx(_sfx); + } + + // Remember the current pattern + _nCurPattern = nPattern; + + // Start the pattern to start the animation + if (_nCurPattern != 0) + _nCurSprite = _patterns[_nCurPattern].init(_sfx, bPlayP0, &_bCurFlag); + else { + _nCurSprite = -1; + + // Look for the sound effect for pattern 0 + if (bPlayP0) { + for (int i = 0; i < _nSfx; i++) { + if (_sfx[i]._name == "p0") + _sfx[i].play(); + } + } + } +} + +bool RMItem::getName(Common::String &name) { + char buf[256]; + + mpalQueryItemName(_mpalCode, buf); + name = buf; + if (buf[0] == '\0') + return false; + return true; +} + +void RMItem::unload() { + if (_patterns != NULL) { + delete[] _patterns; + _patterns = NULL; + } + + if (_sprites != NULL) { + delete[] _sprites; + _sprites = NULL; + } + + if (_sfx != NULL) { + delete[] _sfx; + _sfx = NULL; + } +} + +RMItem::RMItem() { + _bCurFlag = 0; + _patterns = NULL; + _sprites = NULL; + _sfx = NULL; + _curScroll.set(0, 0); + _bInitCurPattern = true; + _nCurPattern = 0; + _z = 0; + _cm = CM_256; + _FX = 0; + _FXparm = 0; + _mpalCode = 0; + _nSprites = 0; + _nSfx = 0; + _nPatterns = 0; + _bPal = 0; + _nCurSprite = 0; + + _bIsActive = false; + memset(_pal._data, 0, sizeof(_pal._data)); + + _hEndPattern = CoroScheduler.createEvent(false, false); +} + +RMItem::~RMItem() { + unload(); + CoroScheduler.closeEvent(_hEndPattern); +} + +void RMItem::waitForEndPattern(CORO_PARAM, uint32 hCustomSkip) { + CORO_BEGIN_CONTEXT; + uint32 h[2]; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + if (_nCurPattern != 0) { + if (hCustomSkip == CORO_INVALID_PID_VALUE) + CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _hEndPattern, CORO_INFINITE); + else { + _ctx->h[0] = hCustomSkip; + _ctx->h[1] = _hEndPattern; + CORO_INVOKE_4(CoroScheduler.waitForMultipleObjects, 2, &_ctx->h[0], false, CORO_INFINITE); + } + } + + CORO_END_CODE; +} + +void RMItem::changeHotspot(const RMPoint &pt) { + _hot = pt; +} + +void RMItem::setInitCurPattern(bool status) { + _bInitCurPattern = status; +} + +void RMItem::playSfx(int nSfx) { + if (nSfx < _nSfx) + _sfx[nSfx].play(); +} + +void RMItem::pauseSound(bool bPause) { + for (int i = 0; i < _nSfx; i++) + _sfx[i].pause(bPause); +} + +/****************************************************************************\ +* RMWipe Methods +\****************************************************************************/ + +RMWipe::RMWipe() { + _hUnregistered = CoroScheduler.createEvent(false, false); + _hEndOfFade = CoroScheduler.createEvent(false, false); + + _bMustRegister = false; + _bUnregister = false; + _bEndFade = false; + _bFading = false; + _nFadeStep = 0; +} + +RMWipe::~RMWipe() { + CoroScheduler.closeEvent(_hUnregistered); + CoroScheduler.closeEvent(_hEndOfFade); +} + +int RMWipe::priority() { + return 200; +} + +void RMWipe::unregister() { + RMGfxTask::unregister(); + assert(_nInList == 0); + CoroScheduler.setEvent(_hUnregistered); +} + +void RMWipe::removeThis(CORO_PARAM, bool &result) { + result = _bUnregister; +} + +void RMWipe::waitForFadeEnd(CORO_PARAM) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _hEndOfFade, CORO_INFINITE); + + _bEndFade = true; + _bFading = false; + + CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE); + CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE); + + CORO_END_CODE; +} + +void RMWipe::closeFade() { + _wip0r.unload(); +} + +void RMWipe::initFade(int type) { + // Activate the fade + _bUnregister = false; + _bEndFade = false; + + _nFadeStep = 0; + + _bMustRegister = true; + + RMRes res(RES_W_CIRCLE); + Common::SeekableReadStream *ds = res.getReadStream(); + _wip0r.readFromStream(*ds); + delete ds; + + _wip0r.setPattern(1); + + _bFading = true; +} + +void RMWipe::doFrame(RMGfxTargetBuffer &bigBuf) { + if (_bMustRegister) { + bigBuf.addPrim(new RMGfxPrimitive(this)); + _bMustRegister = false; + } + + if (_bFading) { + _wip0r.doFrame(&bigBuf, false); + + _nFadeStep++; + + if (_nFadeStep == 10) { + CoroScheduler.setEvent(_hEndOfFade); + } + } +} + +void RMWipe::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + if (_bFading) { + CORO_INVOKE_2(_wip0r.draw, bigBuf, prim); + } + + if (_bEndFade) + Common::fill((byte *)bigBuf, (byte *)bigBuf + bigBuf.getDimx() * bigBuf.getDimy() * 2, 0x0); + + CORO_END_CODE; +} + +/****************************************************************************\ +* RMCharacter Methods +\****************************************************************************/ + +/****************************************************************************/ +/* Find the shortest path between two nodes of the graph connecting the BOX */ +/* Returns path along the vector path path[] */ +/****************************************************************************/ + +short RMCharacter::findPath(short source, short destination) { + static RMBox box[MAXBOXES]; // Matrix of adjacent boxes + static short nodeCost[MAXBOXES]; // Cost per node + static short valid[MAXBOXES]; // 0:Invalid 1:Valid 2:Saturated + static short nextNode[MAXBOXES]; // Next node + short minCost, error = 0; + RMBoxLoc *cur; + + g_system->lockMutex(_csMove); + + if (source == -1 || destination == -1) { + g_system->unlockMutex(_csMove); + return 0; + } + + // Get the boxes + cur = _theBoxes->getBoxes(_curLocation); + + // Make a backup copy to work on + for (int i = 0; i < cur->_numbBox; i++) + memcpy(&box[i], &cur->_boxes[i], sizeof(RMBox)); + + // Invalidate all nodes + for (int i = 0; i < cur->_numbBox; i++) + valid[i] = 0; + + // Prepare source and variables for the procedure + nodeCost[source] = 0; + valid[source] = 1; + bool finish = false; + + // Find the shortest path + while (!finish) { + minCost = 32000; // Reset the minimum cost + error = 1; // Possible error + + // 1st cycle: explore possible new nodes + for (int i = 0; i < cur->_numbBox; i++) { + if (valid[i] == 1) { + error = 0; // Failure de-bunked + int j = 0; + while (((box[i]._adj[j]) != 1) && (j < cur->_numbBox)) + j++; + + if (j >= cur->_numbBox) + valid[i] = 2; // nodo saturated? + else { + nextNode[i] = j; + if (nodeCost[i] + 1 < minCost) + minCost = nodeCost[i] + 1; + } + } + } + + if (error) + finish = true; // All nodes saturated + + // 2nd cycle: adding new nodes that were found, saturate old nodes + for (int i = 0; i < cur->_numbBox; i++) { + if ((valid[i] == 1) && ((nodeCost[i] + 1) == minCost)) { + box[i]._adj[nextNode[i]] = 2; + nodeCost[nextNode[i]] = minCost; + valid[nextNode[i]] = 1; + for (int j = 0; j < cur->_numbBox; j++) + if (box[j]._adj[nextNode[i]] == 1) + box[j]._adj[nextNode[i]] = 0; + + if (nextNode[i] == destination) + finish = true; + } + } + } + + // Remove the path from the adjacent modified matrixes + if (!error) { + _pathLength = nodeCost[destination]; + short k = _pathLength; + _path[k] = destination; + + while (_path[k] != source) { + int i = 0; + while (box[i]._adj[_path[k]] != 2) + i++; + k--; + _path[k] = i; + } + + _pathLength++; + } + + g_system->unlockMutex(_csMove); + + return !error; +} + +void RMCharacter::goTo(CORO_PARAM, RMPoint destcoord, bool bReversed) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + if (_pos == destcoord) { + if (_minPath == 0) { + CORO_INVOKE_0(stop); + CoroScheduler.pulseEvent(_hEndOfPath); + return; + } + } + + _status = WALK; + _lineStart = _pos; + _lineEnd = destcoord; + _dx = _lineStart._x - _lineEnd._x; + _dy = _lineStart._y - _lineEnd._y; + _fx = _dx; + _fy = _dy; + _dx = ABS(_dx); + _dy = ABS(_dy); + _walkSpeed = _curSpeed; + _walkCount = 0; + + if (bReversed) { + while (0) ; + } + + int nPatt = getCurPattern(); + + if (_dx > _dy) { + _slope = _fy / _fx; + if (_lineEnd._x < _lineStart._x) + _walkSpeed = -_walkSpeed; + _walkStatus = 1; + + // Change the pattern for the new direction + _bNeedToStop = true; + if ((_walkSpeed < 0 && !bReversed) || (_walkSpeed >= 0 && bReversed)) { + if (nPatt != PAT_WALKLEFT) + setPattern(PAT_WALKLEFT); + } else { + if (nPatt != PAT_WALKRIGHT) + setPattern(PAT_WALKRIGHT); + } + } else { + _slope = _fx / _fy; + if (_lineEnd._y < _lineStart._y) + _walkSpeed = -_walkSpeed; + _walkStatus = 0; + + _bNeedToStop = true; + if ((_walkSpeed < 0 && !bReversed) || (_walkSpeed >= 0 && bReversed)) { + if (nPatt != PAT_WALKUP) + setPattern(PAT_WALKUP); + } else { + if (nPatt != PAT_WALKDOWN) + setPattern(PAT_WALKDOWN); + } + } + + _olddx = _dx; + _olddy = _dy; + + CORO_END_CODE; +} + +RMPoint RMCharacter::searching(char UP, char DOWN, char RIGHT, char LEFT, RMPoint point) { + short steps; + RMPoint newPt, foundPt; + short minStep = 32000; + + if (UP) { + newPt = point; + steps = 0; + while ((inWhichBox(newPt) == -1) && (newPt._y >= 0)) { + newPt._y--; + steps++; + } + if ((inWhichBox(newPt) != -1) && (steps < minStep) && + findPath(inWhichBox(_pos), inWhichBox(newPt))) { + minStep = steps; + newPt._y--; // to avoid error? + foundPt = newPt; + } + } + + if (DOWN) { + newPt = point; + steps = 0; + while ((inWhichBox(newPt) == -1) && (newPt._y < 480)) { + newPt._y++; + steps++; + } + if ((inWhichBox(newPt) != -1) && (steps < minStep) && + findPath(inWhichBox(_pos), inWhichBox(newPt))) { + minStep = steps; + newPt._y++; // to avoid error? + foundPt = newPt; + } + } + + if (RIGHT) { + newPt = point; + steps = 0; + while ((inWhichBox(newPt) == -1) && (newPt._x < 640)) { + newPt._x++; + steps++; + } + if ((inWhichBox(newPt) != -1) && (steps < minStep) && + findPath(inWhichBox(_pos), inWhichBox(newPt))) { + minStep = steps; + newPt._x++; // to avoid error? + foundPt = newPt; + } + } + + if (LEFT) { + newPt = point; + steps = 0; + while ((inWhichBox(newPt) == -1) && (newPt._x >= 0)) { + newPt._x--; + steps++; + } + if ((inWhichBox(newPt) != -1) && (steps < minStep) && + findPath(inWhichBox(_pos), inWhichBox(newPt))) { + minStep = steps; + newPt._x--; // to avoid error? + foundPt = newPt; + } + } + + if (minStep == 32000) + foundPt = point; + + return foundPt; +} + +RMPoint RMCharacter::nearestPoint(const RMPoint &point) { + return searching(1, 1, 1, 1, point); +} + +short RMCharacter::scanLine(const RMPoint &point) { + int Ldx, Ldy, Lcount; + float Lfx, Lfy, Lslope; + RMPoint Lstart, Lend, Lscan; + signed char Lspeed, Lstatus; + + Lstart = _pos; + Lend = point; + Ldx = Lstart._x - Lend._x; + Ldy = Lstart._y - Lend._y; + Lfx = Ldx; + Lfy = Ldy; + Ldx = ABS(Ldx); + Ldy = ABS(Ldy); + Lspeed = 1; + Lcount = 0; + + if (Ldx > Ldy) { + Lslope = Lfy / Lfx; + if (Lend._x < Lstart._x) + Lspeed = -Lspeed; + Lstatus = 1; + } else { + Lslope = Lfx / Lfy; + if (Lend._y < Lstart._y) + Lspeed = - Lspeed; + Lstatus = 0; + } + + Lscan = Lstart; // Start scanning + while (inWhichBox(Lscan) != -1) { + Lcount++; + if (Lstatus) { + Ldx = Lspeed * Lcount; + Ldy = (int)(Lslope * Ldx); + } else { + Ldy = Lspeed * Lcount; + Ldx = (int)(Lslope * Ldy); + } + + Lscan._x = Lstart._x + Ldx; + Lscan._y = Lstart._y + Ldy; + + if ((ABS(Lscan._x - Lend._x) <= 1) && (ABS(Lscan._y - Lend._y) <= 1)) + return 1; + } + + return 0; +} + +/** + * Calculates intersections between the straight line and the closest BBOX + */ +RMPoint RMCharacter::invScanLine(const RMPoint &point) { + RMPoint lStart = point; // Exchange! + RMPoint lEnd = _pos; // :-) + int lDx = lStart._x - lEnd._x; + int lDy = lStart._y - lEnd._y; + float lFx = lDx; + float lFy = lDy; + lDx = ABS(lDx); + lDy = ABS(lDy); + signed char lSpeed = 1; + int lCount = 0; + + signed char lStatus; + float lSlope; + + if (lDx > lDy) { + lSlope = lFy / lFx; + if (lEnd._x < lStart._x) + lSpeed = -lSpeed; + lStatus = 1; + } else { + lSlope = lFx / lFy; + if (lEnd._y < lStart._y) + lSpeed = -lSpeed; + lStatus = 0; + } + + RMPoint lScan = lStart; + signed char lBox = -1; + + for (;;) { + if (inWhichBox(lScan) != -1) { + if (inWhichBox(lScan) != lBox) { + if (inWhichBox(_pos) == inWhichBox(lScan) || findPath(inWhichBox(_pos), inWhichBox(lScan))) + return lScan; + else + lBox = inWhichBox(lScan); + } + } + + lCount++; + if (lStatus) { + lDx = lSpeed * lCount; + lDy = (int)(lSlope * lDx); + } else { + lDy = lSpeed * lCount; + lDx = (int)(lSlope * lDy); + } + lScan._x = lStart._x + lDx; + lScan._y = lStart._y + lDy; + + // WORKAROUND: Handles cases where the points never fall inside a bounding box + if (lScan._x < -100 || lScan._y < -100 || lScan._x >= 1000 || lScan._y >= 1000) + return point; + } +} + +/** + * Returns the HotSpot coordinate closest to the player + */ + +RMPoint RMCharacter::nearestHotSpot(int sourcebox, int destbox) { + RMPoint hotspot; + int x, y; + int minDist = 10000000; + RMBoxLoc *cur = _theBoxes->getBoxes(_curLocation); + + for (short cc = 0; cc < cur->_boxes[sourcebox]._numHotspot; cc++) + if ((cur->_boxes[sourcebox]._hotspot[cc]._destination) == destbox) { + x = ABS(cur->_boxes[sourcebox]._hotspot[cc]._hotx - _pos._x); + y = ABS(cur->_boxes[sourcebox]._hotspot[cc]._hoty - _pos._y); + + if ((x * x + y * y) < minDist) { + minDist = x * x + y * y; + hotspot._x = cur->_boxes[sourcebox]._hotspot[cc]._hotx; + hotspot._y = cur->_boxes[sourcebox]._hotspot[cc]._hoty; + } + } + + return hotspot; +} + +void RMCharacter::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + if (_bDrawNow) { + prim->getDst() += _fixedScroll; + + CORO_INVOKE_2(RMItem::draw, bigBuf, prim); + } + + CORO_END_CODE; +} + +void RMCharacter::newBoxEntered(int nBox) { + RMBoxLoc *cur; + + // Recall on ExitBox + mpalQueryDoAction(3, _curLocation, _curBox); + + cur = _theBoxes->getBoxes(_curLocation); + bool bOldReverse = cur->_boxes[_curBox]._bReversed; + _curBox = nBox; + + // If Z is changed, we must remove it from the OT + if (cur->_boxes[_curBox]._destZ != _z) { + _bRemoveFromOT = true; + _z = cur->_boxes[_curBox]._destZ; + } + + // Movement management is reversed, only if we are not in the shortest path. If we are in the shortest + // path, directly do the DoFrame + if (_bMovingWithoutMinpath) { + if ((cur->_boxes[_curBox]._bReversed && !bOldReverse) || (!cur->_boxes[_curBox]._bReversed && bOldReverse)) { + switch (getCurPattern()) { + case PAT_WALKUP: + setPattern(PAT_WALKDOWN); + break; + case PAT_WALKDOWN: + setPattern(PAT_WALKUP); + break; + case PAT_WALKRIGHT: + setPattern(PAT_WALKLEFT); + break; + case PAT_WALKLEFT: + setPattern(PAT_WALKRIGHT); + break; + } + } + } + + // Recall On EnterBox + mpalQueryDoAction(2, _curLocation, _curBox); +} + +void RMCharacter::doFrame(CORO_PARAM, RMGfxTargetBuffer *bigBuf, int loc) { + CORO_BEGIN_CONTEXT; + bool bEndNow; + RMBoxLoc *cur; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + _ctx->bEndNow = false; + _bEndOfPath = false; + _bDrawNow = (_curLocation == loc); + + g_system->lockMutex(_csMove); + + // If we're walking.. + if (_status != STAND) { + // If we are going horizontally + if (_walkStatus == 1) { + _dx = _walkSpeed * _walkCount; + _dy = (int)(_slope * _dx); + _pos._x = _lineStart._x + _dx; + _pos._y = _lineStart._y + _dy; + + // Right + if (((_walkSpeed > 0) && (_pos._x > _lineEnd._x)) || ((_walkSpeed < 0) && (_pos._x < _lineEnd._x))) { + _pos = _lineEnd; + _status = STAND; + _ctx->bEndNow = true; + } + } + + // If we are going vertical + if (_walkStatus == 0) { + _dy = _walkSpeed * _walkCount; + _dx = (int)(_slope * _dy); + _pos._x = _lineStart._x + _dx; + _pos._y = _lineStart._y + _dy; + + // Down + if (((_walkSpeed > 0) && (_pos._y > _lineEnd._y)) || ((_walkSpeed < 0) && (_pos._y < _lineEnd._y))) { + _pos = _lineEnd; + _status = STAND; + _ctx->bEndNow = true; + } + } + + // Check if the character came out of the BOX in error, in which case he returns immediately + if (inWhichBox(_pos) == -1) { + _pos._x = _lineStart._x + _olddx; + _pos._y = _lineStart._y + _olddy; + } + + // If we have just moved to a temporary location, and is over the shortest path, we stop permanently + if (_ctx->bEndNow && _minPath == 0) { + if (!_bEndOfPath) + CORO_INVOKE_0(stop); + _bEndOfPath = true; + CoroScheduler.pulseEvent(_hEndOfPath); + } + + _walkCount++; + + // Update the character Z. @@@ Should remove only if the Z was changed + + // Check if the box was changed + if (!_theBoxes->isInBox(_curLocation, _curBox, _pos)) + newBoxEntered(inWhichBox(_pos)); + + // Update the old coordinates + _olddx = _dx; + _olddy = _dy; + } + + // If we stop + if (_status == STAND) { + // Check if there is still the shortest path to calculate + if (_minPath == 1) { + _ctx->cur = _theBoxes->getBoxes(_curLocation); + + // If we still have to go through a box + if (_pathCount < _pathLength) { + // Check if the box we're going into is active + if (_ctx->cur->_boxes[_path[_pathCount - 1]]._bActive) { + // Move in a straight line towards the nearest hotspot, taking into account the reversing + // NEWBOX = path[pathcount-1] + CORO_INVOKE_2(goTo, nearestHotSpot(_path[_pathCount - 1], _path[_pathCount]), _ctx->cur->_boxes[_path[_pathCount - 1]]._bReversed); + _pathCount++; + } else { + // If the box is off, we can only block all + // @@@ Whilst this should not happen, because have improved + // the search for the minimum path + _minPath = 0; + if (!_bEndOfPath) + CORO_INVOKE_0(stop); + _bEndOfPath = true; + CoroScheduler.pulseEvent(_hEndOfPath); + } + } else { + // If we have already entered the last box, we just have to move in a straight line towards the + // point of arrival + // NEWBOX = InWhichBox(pathend) + _minPath = 0; + CORO_INVOKE_2(goTo, _pathEnd, _ctx->cur->_boxes[inWhichBox(_pathEnd)]._bReversed); + } + } + } + + g_system->unlockMutex(_csMove); + + // Invoke the DoFrame of the item + RMItem::doFrame(bigBuf); + + CORO_END_CODE; +} + +bool RMCharacter::endOfPath() { + return _bEndOfPath; +} + +void RMCharacter::stop(CORO_PARAM) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + _bMoving = false; + + // You never know.. + _status = STAND; + _minPath = 0; + + if (!_bNeedToStop) + return; + + _bNeedToStop = false; + + switch (getCurPattern()) { + case PAT_WALKUP: + setPattern(PAT_STANDUP); + break; + + case PAT_WALKDOWN: + setPattern(PAT_STANDDOWN); + break; + + case PAT_WALKLEFT: + setPattern(PAT_STANDLEFT); + break; + + case PAT_WALKRIGHT: + setPattern(PAT_STANDRIGHT); + break; + + default: + setPattern(PAT_STANDDOWN); + break; + } + + CORO_END_CODE; +} + +/** + * Check if the character is moving + */ +bool RMCharacter::isMoving() { + return _bMoving; +} + +inline int RMCharacter::inWhichBox(const RMPoint &pt) { + return _theBoxes->whichBox(_curLocation, pt); +} + +void RMCharacter::move(CORO_PARAM, RMPoint pt, bool *result) { + CORO_BEGIN_CONTEXT; + RMPoint dest; + int numbox; + RMBoxLoc *cur; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + _bMoving = true; + + // 0, 0 does not do anything, just stops the character + if (pt._x == 0 && pt._y == 0) { + _minPath = 0; + _status = STAND; + CORO_INVOKE_0(stop); + if (result) + *result = true; + return; + } + + // If clicked outside the box + _ctx->numbox = inWhichBox(pt); + if (_ctx->numbox == -1) { + // Find neareste point inside the box + _ctx->dest = nearestPoint(pt); + + // ???!?? + if (_ctx->dest == pt) + _ctx->dest = invScanLine(pt); + + pt = _ctx->dest; + _ctx->numbox = inWhichBox(pt); + } + + _ctx->cur = _theBoxes->getBoxes(_curLocation); + + _minPath = 0; + _status = STAND; + _bMovingWithoutMinpath = true; + if (scanLine(pt)) + CORO_INVOKE_2(goTo, pt, _ctx->cur->_boxes[_ctx->numbox]._bReversed); + else if (findPath(inWhichBox(_pos), inWhichBox(pt))) { + _bMovingWithoutMinpath = false; + _minPath = 1; + _pathCount = 1; + _pathEnd = pt; + } else { + // @@@ This case is whether a hotspot is inside a box, but there is + // a path to get there. We use the InvScanLine to search around a point + _ctx->dest = invScanLine(pt); + pt = _ctx->dest; + + if (scanLine(pt)) + CORO_INVOKE_2(goTo, pt, _ctx->cur->_boxes[_ctx->numbox]._bReversed); + else if (findPath(inWhichBox(_pos), inWhichBox(pt))) { + _bMovingWithoutMinpath = false; + _minPath = 1; + _pathCount = 1; + _pathEnd = pt; + if (result) + *result = true; + } else { + if (result) + *result = false; + } + + return; + } + + if (result) + *result = true; + + CORO_END_CODE; +} + +void RMCharacter::setPosition(const RMPoint &pt, int newloc) { + RMBoxLoc *box; + + _minPath = 0; + _status = STAND; + _pos = pt; + + if (newloc != -1) + _curLocation = newloc; + + // Update the character's Z value + box = _theBoxes->getBoxes(_curLocation); + _curBox = inWhichBox(_pos); + assert(_curBox != -1); + _z = box->_boxes[_curBox]._destZ; + _bRemoveFromOT = true; +} + +void RMCharacter::waitForEndMovement(CORO_PARAM) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + if (_bMoving) + CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _hEndOfPath, CORO_INFINITE); + + CORO_END_CODE; +} + +void RMCharacter::setFixedScroll(const RMPoint &fix) { + _fixedScroll = fix; +} + +void RMCharacter::setSpeed(int speed) { + _curSpeed = speed; +} + +void RMCharacter::removeThis(CORO_PARAM, bool &result) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + if (_bRemoveFromOT) + result = true; + else + CORO_INVOKE_1(RMItem::removeThis, result); + + CORO_END_CODE; +} + +RMCharacter::RMCharacter() { + _csMove = g_system->createMutex(); + _hEndOfPath = CoroScheduler.createEvent(false, false); + _minPath = 0; + _curSpeed = 3; + _bRemoveFromOT = false; + _bMoving = false; + _curLocation = 0; + _curBox = 0; + _dx = _dy = 0; + _olddx = _olddy = 0; + _fx = _fy = _slope = 0; + _walkSpeed = _walkStatus = 0; + _nextBox = 0; + _pathLength = _pathCount = 0; + _status = STAND; + _theBoxes = NULL; + _walkCount = 0; + _bEndOfPath = false; + _bMovingWithoutMinpath = false; + _bDrawNow = false; + _bNeedToStop = false; + + memset(_path, 0, sizeof(_path)); + + _pos.set(0, 0); +} + +RMCharacter::~RMCharacter() { + g_system->deleteMutex(_csMove); + CoroScheduler.closeEvent(_hEndOfPath); +} + +void RMCharacter::linkToBoxes(RMGameBoxes *boxes) { + _theBoxes = boxes; +} + +/****************************************************************************\ +* RMBox Methods +\****************************************************************************/ + +void RMBox::readFromStream(Common::ReadStream &ds) { + // Bbox + _left = ds.readSint32LE(); + _top = ds.readSint32LE(); + _right = ds.readSint32LE(); + _bottom = ds.readSint32LE(); + + // Adjacency + for (int i = 0; i < MAXBOXES; i++) { + _adj[i] = ds.readSint32LE(); + } + + // Misc + _numHotspot = ds.readSint32LE(); + _destZ = ds.readByte(); + byte b = ds.readByte(); + _bActive = b; + b = ds.readByte(); + _bReversed = b; + + // Reversed expansion space + for (int i = 0; i < 30; i++) + ds.readByte(); + + uint16 w; + // Hotspots + for (int i = 0; i < _numHotspot; i++) { + w = ds.readUint16LE(); + _hotspot[i]._hotx = w; + w = ds.readUint16LE(); + _hotspot[i]._hoty = w; + w = ds.readUint16LE(); + _hotspot[i]._destination = w; + } +} + +/****************************************************************************\ +* RMBoxLoc Methods +\****************************************************************************/ + +RMBoxLoc::RMBoxLoc() { + _boxes = NULL; + _numbBox = 0; +} + +RMBoxLoc::~RMBoxLoc() { + delete[] _boxes; +} + +void RMBoxLoc::readFromStream(Common::ReadStream &ds) { + char buf[2]; + + // ID and version + buf[0] = ds.readByte(); + buf[1] = ds.readByte(); + byte ver = ds.readByte(); + assert(buf[0] == 'B' && buf[1] == 'X'); + assert(ver == 3); + + // Number of boxes + _numbBox = ds.readSint32LE(); + + // Allocate memory for the boxes + _boxes = new RMBox[_numbBox]; + + // Read in boxes + for (int i = 0; i < _numbBox; i++) + _boxes[i].readFromStream(ds); +} + +void RMBoxLoc::recalcAllAdj() { + for (int i = 0; i < _numbBox; i++) { + Common::fill(_boxes[i]._adj, _boxes[i]._adj + MAXBOXES, 0); + + for (int j = 0; j < _boxes[i]._numHotspot; j++) { + if (_boxes[_boxes[i]._hotspot[j]._destination]._bActive) + _boxes[i]._adj[_boxes[i]._hotspot[j]._destination] = 1; + } + } +} + +/****************************************************************************\ +* RMGameBoxes methods +\****************************************************************************/ + +RMGameBoxes::RMGameBoxes() { + _nLocBoxes = 0; + Common::fill(_allBoxes, _allBoxes + GAME_BOXES_SIZE, (RMBoxLoc *)NULL); +} + +RMGameBoxes::~RMGameBoxes() { + for (int i = 1; i <= _nLocBoxes; ++i) + delete _allBoxes[i]; +} + +void RMGameBoxes::init() { + // Load boxes from disk + _nLocBoxes = 130; + for (int i = 1; i <= _nLocBoxes; i++) { + RMRes res(10000 + i); + + Common::SeekableReadStream *ds = res.getReadStream(); + + _allBoxes[i] = new RMBoxLoc(); + _allBoxes[i]->readFromStream(*ds); + + _allBoxes[i]->recalcAllAdj(); + + delete ds; + } +} + +void RMGameBoxes::close() { +} + +RMBoxLoc *RMGameBoxes::getBoxes(int nLoc) { + return _allBoxes[nLoc]; +} + +int RMGameBoxes::getLocBoxesCount() const { + return _nLocBoxes; +} + +bool RMGameBoxes::isInBox(int nLoc, int nBox, const RMPoint &pt) { + RMBoxLoc *cur = getBoxes(nLoc); + + if ((pt._x >= cur->_boxes[nBox]._left) && (pt._x <= cur->_boxes[nBox]._right) && + (pt._y >= cur->_boxes[nBox]._top) && (pt._y <= cur->_boxes[nBox]._bottom)) + return true; + else + return false; +} + +int RMGameBoxes::whichBox(int nLoc, const RMPoint &punto) { + RMBoxLoc *cur = getBoxes(nLoc); + + if (!cur) + return -1; + + for (int i = 0; i < cur->_numbBox; i++) { + if (cur->_boxes[i]._bActive) { + if ((punto._x >= cur->_boxes[i]._left) && (punto._x <= cur->_boxes[i]._right) && + (punto._y >= cur->_boxes[i]._top) && (punto._y <= cur->_boxes[i]._bottom)) + return i; + } + } + + return -1; +} + +void RMGameBoxes::changeBoxStatus(int nLoc, int nBox, int status) { + _allBoxes[nLoc]->_boxes[nBox]._bActive = status; + _allBoxes[nLoc]->recalcAllAdj(); +} + +int RMGameBoxes::getSaveStateSize() { + int size = 4; + + for (int i = 1; i <= _nLocBoxes; i++) { + size += 4; + size += _allBoxes[i]->_numbBox; + } + + return size; +} + +void RMGameBoxes::saveState(byte *state) { + // Save the number of locations with boxes + WRITE_LE_UINT32(state, _nLocBoxes); + state += 4; + + // For each location, write out the number of boxes and their status + for (int i = 1; i <= _nLocBoxes; i++) { + WRITE_LE_UINT32(state, _allBoxes[i]->_numbBox); + state += 4; + + for (int j = 0; j < _allBoxes[i]->_numbBox; j++) + *state++ = _allBoxes[i]->_boxes[j]._bActive; + } +} + +void RMGameBoxes::loadState(byte *state) { + // Load number of items + int nloc = READ_LE_UINT32(state); + state += 4; + + assert(nloc <= _nLocBoxes); + + int nbox; + // For each location, read the number of boxes and their status + for (int i = 1; i <= nloc; i++) { + nbox = READ_LE_UINT32(state); + state += 4; + + for (int j = 0; j < nbox ; j++) { + if (j < _allBoxes[i]->_numbBox) + _allBoxes[i]->_boxes[j]._bActive = *state; + + state++; + } + + _allBoxes[i]->recalcAllAdj(); + } +} + +/****************************************************************************\ +* RMLocation Methods +\****************************************************************************/ + +/** + * Standard constructor + */ +RMLocation::RMLocation() { + _nItems = 0; + _items = NULL; + _buf = NULL; + TEMPNumLoc = 0; + _cmode = CM_256; +} + +RMPoint RMLocation::TEMPGetTonyStart() { + return TEMPTonyStart; +} + +int RMLocation::TEMPGetNumLoc() { + return TEMPNumLoc; +} + +/** + * Load a location (.LOC) from a given data stream + * + * @param ds Data stream + * @returns True if succeeded OK, false in case of error. + */ +bool RMLocation::load(Common::SeekableReadStream &ds) { + char id[3]; + + // Reset dirty rectangling + _prevScroll.set(-1, -1); + _prevFixedScroll.set(-1, -1); + + // Check the ID + ds.read(id, 3); + + // Check if we are in a LOX + if (id[0] == 'L' && id[1] == 'O' && id[2] == 'X') + return loadLOX(ds); + + // Otherwise, check that it is a normal LOC + if (id[0] != 'L' || id[1] != 'O' || id[2] != 'C') + return false; + + // Version + byte ver = ds.readByte(); + assert(ver == 6); + + // Location name + _name = readString(ds); + + // Skip the MPAL bailouts (64 bytes) + TEMPNumLoc = ds.readSint32LE(); + TEMPTonyStart._x = ds.readSint32LE(); + TEMPTonyStart._y = ds.readSint32LE(); + ds.skip(64 - 4 * 3); + + // Skip flag associated with the background (?) + ds.skip(1); + + // Location dimensions + int dimx = ds.readSint32LE(); + int dimy = ds.readSint32LE(); + _curScroll.set(0, 0); + + // Read the color mode + byte cm = ds.readByte(); + _cmode = (RMColorMode)cm; + + // Initialize the source buffer and read the location + switch (_cmode) { + case CM_256: + _buf = new RMGfxSourceBuffer8; + break; + + case CM_65K: + _buf = new RMGfxSourceBuffer16; + break; + + default: + assert(0); + break; + }; + + // Initialize the surface, loading the palette if necessary + _buf->init(ds, dimx, dimy, true); + + // Check the size of the location + //assert(dimy!=512); + + // Number of objects + _nItems = ds.readSint32LE(); + + // Create and read in the objects + if (_nItems > 0) + _items = new RMItem[_nItems]; + + + g_vm->freezeTime(); + for (int i = 0; i < _nItems && !ds.err(); i++) + _items[i].readFromStream(ds); + g_vm->unfreezeTime(); + + return ds.err(); +} + +bool RMLocation::loadLOX(Common::SeekableReadStream &ds) { + // Version + byte ver = ds.readByte(); + assert(ver == 1); + + // Location name + _name = readString(ds); + + // Location number + TEMPNumLoc = ds.readSint32LE(); + TEMPTonyStart._x = ds.readSint32LE(); + TEMPTonyStart._y = ds.readSint32LE(); + + // Dimensions + int dimx = ds.readSint32LE(); + int dimy = ds.readSint32LE(); + _curScroll.set(0, 0); + + // It's always 65K (16-bit) mode + _cmode = CM_65K; + _buf = new RMGfxSourceBuffer16; + + // Initialize the surface, loading in the palette if necessary + _buf->init(ds, dimx, dimy, true); + + // Number of items + _nItems = ds.readSint32LE(); + + // Create and read objects + if (_nItems > 0) + _items = new RMItem[_nItems]; + + for (int i = 0; i < _nItems && !ds.err(); i++) + _items[i].readFromStream(ds, true); + + return ds.err(); +} + +/** + * Draw method overloaded from RMGfxSourceBUffer8 + */ +void RMLocation::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) { + CORO_BEGIN_CONTEXT; + bool priorTracking; + bool hasChanges; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + // Set the position of the source scrolling + if (_buf->getDimy() > RM_SY || _buf->getDimx() > RM_SX) { + prim->setSrc(RMRect(_curScroll, _curScroll + RMPoint(640, 480))); + } + + prim->setDst(_fixedScroll); + + // Check whether dirty rects are being tracked, and if there are changes, leave tracking + // turned on so a dirty rect will be added for the entire background + _ctx->priorTracking = bigBuf.getTrackDirtyRects(); + _ctx->hasChanges = (_prevScroll != _curScroll) || (_prevFixedScroll != _fixedScroll); + bigBuf.setTrackDirtyRects(_ctx->priorTracking && _ctx->hasChanges); + + // Invoke the drawing method fo the image class, which will draw the location background + CORO_INVOKE_2(_buf->draw, bigBuf, prim); + + if (_ctx->hasChanges) { + _prevScroll = _curScroll; + _prevFixedScroll = _fixedScroll; + } + bigBuf.setTrackDirtyRects(_ctx->priorTracking); + + CORO_END_CODE; +} + +/** + * Prepare a frame, adding the location to the OT list, and all the items that have changed animation frame. + */ +void RMLocation::doFrame(RMGfxTargetBuffer *bigBuf) { + // If the location is not in the OT list, add it in + if (!_nInList) + bigBuf->addPrim(new RMGfxPrimitive(this)); + + // Process all the location items + for (int i = 0; i < _nItems; i++) + _items[i].doFrame(bigBuf); +} + +RMItem *RMLocation::getItemFromCode(uint32 dwCode) { + for (int i = 0; i < _nItems; i++) { + if (_items[i].mpalCode() == (int)dwCode) + return &_items[i]; + } + + return NULL; +} + +RMItem *RMLocation::whichItemIsIn(const RMPoint &pt) { + int found = -1; + int foundSize = 0; + int size; + + for (int i = 0; i < _nItems; i++) { + size = 0; + if (_items[i].isIn(pt, &size)) { + if (found == -1 || size < foundSize) { + foundSize = size; + found = i; + } + } + } + + if (found == -1) + return NULL; + else + return &_items[found]; +} + +RMLocation::~RMLocation() { + unload(); +} + +void RMLocation::unload() { + // Clear memory + if (_items) { + delete[] _items; + _items = NULL; + } + + // Destroy the buffer + if (_buf) { + delete _buf; + _buf = NULL; + } +} + +void RMLocation::updateScrolling(const RMPoint &ptShowThis) { + RMPoint oldScroll = _curScroll; + + if (_curScroll._x + 250 > ptShowThis._x) { + _curScroll._x = ptShowThis._x - 250; + } else if (_curScroll._x + RM_SX - 250 < ptShowThis._x) { + _curScroll._x = ptShowThis._x + 250 - RM_SX; + } else if (ABS(_curScroll._x + RM_SX / 2 - ptShowThis._x) > 32 && _buf->getDimx() > RM_SX) { + if (_curScroll._x + RM_SX / 2 < ptShowThis._x) + _curScroll._x++; + else + _curScroll._x--; + } + + if (_curScroll._y + 180 > ptShowThis._y) { + _curScroll._y = ptShowThis._y - 180; + } else if (_curScroll._y + RM_SY - 180 < ptShowThis._y) { + _curScroll._y = ptShowThis._y + 180 - RM_SY; + } else if (ABS(_curScroll._y + RM_SY / 2 - ptShowThis._y) > 16 && _buf->getDimy() > RM_SY) { + if (_curScroll._y + RM_SY / 2 < ptShowThis._y) + _curScroll._y++; + else + _curScroll._y--; + } + + if (_curScroll._x < 0) + _curScroll._x = 0; + if (_curScroll._y < 0) + _curScroll._y = 0; + if (_curScroll._x + RM_SX > _buf->getDimx()) + _curScroll._x = _buf->getDimx() - RM_SX; + if (_curScroll._y + RM_SY > _buf->getDimy()) + _curScroll._y = _buf->getDimy() - RM_SY; + + if (oldScroll != _curScroll) { + for (int i = 0; i < _nItems; i++) + _items[i].setScrollPosition(_curScroll); + } +} + +void RMLocation::setFixedScroll(const RMPoint &scroll) { + _fixedScroll = scroll; + + for (int i = 0; i < _nItems; i++) + _items[i].setScrollPosition(_curScroll - _fixedScroll); +} + +void RMLocation::setScrollPosition(const RMPoint &scroll) { + RMPoint pt = scroll; + if (pt._x < 0) + pt._x = 0; + if (pt._y < 0) + pt._y = 0; + if (pt._x + RM_SX > _buf->getDimx()) + pt._x = _buf->getDimx() - RM_SX; + if (pt._y + RM_SY > _buf->getDimy()) + pt._y = _buf->getDimy() - RM_SY; + + _curScroll = pt; + + for (int i = 0; i < _nItems; i++) + _items[i].setScrollPosition(_curScroll); +} + + +void RMLocation::pauseSound(bool bPause) { + for (int i = 0; i < _nItems; i++) + _items[i].pauseSound(bPause); +} + +/** + * Read the current scroll position + */ +RMPoint RMLocation::scrollPosition() { + return _curScroll; +} + +/****************************************************************************\ +* RMMessage Methods +\****************************************************************************/ + +RMMessage::RMMessage(uint32 dwId) { + load(dwId); +} + +RMMessage::RMMessage() { + _lpMessage = NULL; + _nPeriods = 0; + for (int i = 0; i < 256; i++) + _lpPeriods[i] = 0; +} + +RMMessage::~RMMessage() { + if (_lpMessage) + globalDestroy(_lpMessage); +} + +void RMMessage::load(uint32 dwId) { + _lpMessage = mpalQueryMessage(dwId); + assert(_lpMessage != NULL); + + if (_lpMessage) + parseMessage(); +} + +void RMMessage::parseMessage() { + char *p; + + assert(_lpMessage != NULL); + + _nPeriods = 1; + p = _lpPeriods[0] = _lpMessage; + + for (;;) { + // Find the end of the current period + while (*p != '\0') + p++; + + // If there is another '0' at the end of the string, the end has been found + p++; + if (*p == '\0') + break; + + // Otherwise there is another line, and remember it's start + _lpPeriods[_nPeriods++] = p; + } +} + +bool RMMessage::isValid() { + return _lpMessage != NULL; +} + +int RMMessage::numPeriods() { + return _nPeriods; +} + +char *RMMessage::period(int num) { + return _lpPeriods[num]; +} + +char *RMMessage::operator[](int num) { + return _lpPeriods[num]; +} + +} // End of namespace Tony diff --git a/engines/tony/loc.h b/engines/tony/loc.h new file mode 100644 index 0000000000..1306316136 --- /dev/null +++ b/engines/tony/loc.h @@ -0,0 +1,572 @@ +/* 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. + * + */ + +/* + * This code is based on original Tony Tough source code + * + * Copyright (c) 1997-2003 Nayma Software + */ + +#ifndef TONY_LOC_H +#define TONY_LOC_H + +#include "common/scummsys.h" +#include "common/system.h" +#include "common/file.h" +#include "tony/sound.h" +#include "tony/utils.h" + +namespace Tony { + +/****************************************************************************\ +* Various defines +\****************************************************************************/ + +/** + * Valid color modes + */ +typedef enum { + CM_256, + CM_65K +} RMColorMode; + +/****************************************************************************\ +* Class declarations +\****************************************************************************/ + +/** + * Generic palette + */ +class RMPalette { +public: + byte _data[1024]; + +public: + void readFromStream(Common::ReadStream &ds); +}; + +/** + * Sound effect of an object + */ +class RMSfx { +public: + Common::String _name; + FPSfx *_fx; + bool _bPlayingLoop; + +public: + RMSfx(); + virtual ~RMSfx(); + + void play(bool bLoop = false); + void setVolume(int vol); + void pause(bool bPause); + void stop(); + + void readFromStream(Common::ReadStream &ds, bool bLOX = false); +}; + +/** + * Object pattern + */ +class RMPattern { +public: + // Type of slot + enum RMSlotType { + DUMMY1 = 0, + DUMMY2, + SPRITE, + SOUND, + COMMAND, + SPECIAL + }; + + // Class slot + class RMSlot { + private: + RMPoint _pos; // Child co-ordinates + + public: + RMSlotType _type; + int _data; + byte _flag; + + public: + RMPoint pos() { + return _pos; + } + + void readFromStream(Common::ReadStream &ds, bool bLOX = false); + }; + +public: + Common::String _name; + +private: + int _speed; + RMPoint _pos; // Parent coordinates + RMPoint _curPos; // Parent + child coordinates + int _bLoop; + int _nSlots; + int _nCurSlot; + int _nCurSprite; + + RMSlot *_slots; + + uint32 _nStartTime; + +public: + RMPattern(); + virtual ~RMPattern(); + + // A warning that the pattern now and the current + int init(RMSfx *sfx, bool bPlayP0 = false, byte *bFlag = NULL); + + // Update the pattern, checking to see if it's time to change slot and executing + // any associated commands + int update(uint32 hEndPattern, byte &bFlag, RMSfx *sfx); + + // Stop a sound effect + void stopSfx(RMSfx *sfx); + + // Reads the position of the pattern + RMPoint pos(); + + void readFromStream(Common::ReadStream &ds, bool bLOX = false); + +private: + void updateCoord(); +}; + +/** + * Sprite (frame) animation of an item + */ +class RMSprite : public RMGfxTask { +public: + Common::String _name; + RMRect _rcBox; + +protected: + RMGfxSourceBuffer *_buf; + +public: + RMSprite(); + virtual ~RMSprite(); + + void init(RMGfxSourceBuffer *buf); + virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim); + void setPalette(byte *lpBuf); + void getSizeFromStream(Common::SeekableReadStream &ds, int *dimx, int *dimy); + void LOXGetSizeFromStream(Common::SeekableReadStream &ds, int *dimx, int *dimy); + + void readFromStream(Common::SeekableReadStream &ds, bool bLOX = false); +}; + +/** + * Data on an item + */ +class RMItem : public RMGfxTask { +public: + Common::String _name; + +protected: + int _z; + RMPoint _pos; // Coordinate ancestor + RMColorMode _cm; + RMPoint _curScroll; + + byte _FX; + byte _FXparm; + + virtual int getCurPattern(); + +private: + int _nCurPattern; + int _mpalCode; + RMPoint _hot; + RMRect _rcBox; + int _nSprites, _nSfx, _nPatterns; + byte _bPal; + RMPalette _pal; + + RMSprite *_sprites; + RMSfx *_sfx; + RMPattern *_patterns; + + byte _bCurFlag; + int _nCurSprite; + bool _bIsActive; + uint32 _hEndPattern; + bool _bInitCurPattern; + +public: + RMPoint calculatePos(); + +public: + RMItem(); + virtual ~RMItem(); + + // Process to make the object move on any animations. + // Returns TRUE if it should be redrawn on the next frame + bool doFrame(RMGfxTargetBuffer *bigBuf, bool bAddToList = true); + + // Sets the current scrolling position + void setScrollPosition(const RMPoint &scroll); + + // Overloading of check whether to remove from active list + virtual void removeThis(CORO_PARAM, bool &result); + + // Overloaded Draw + virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim); + + // Overloaded priority: it's based on Z ordering + virtual int priority(); + + // Pattern number + int numPattern(); + + // Set anew animation pattern, changing abruptly from the current + virtual void setPattern(int nPattern, bool bPlayP0 = false); + + // Set a new status + void setStatus(int nStatus); + + bool isIn(const RMPoint &pt, int *size = NULL); + RMPoint getHotspot(); + bool getName(Common::String &name); + int mpalCode(); + + // Unload + void unload(); + + // Wait for the end of the current pattern + void waitForEndPattern(CORO_PARAM, uint32 hCustomSkip = CORO_INVALID_PID_VALUE); + + // Sets a new hotspot fro the object + void changeHotspot(const RMPoint &pt); + + void setInitCurPattern(bool status); + + void playSfx(int nSfx); + + void readFromStream(Common::SeekableReadStream &ds, bool bLOX = false); + + void pauseSound(bool bPause); + +protected: + // Create a primitive that has as it's task this item + virtual RMGfxPrimitive *newItemPrimitive(); + + // Allocate memory for the sprites + virtual RMGfxSourceBuffer *newItemSpriteBuffer(int dimx, int dimy, bool bPreRLE); +}; + +#define MAXBOXES 50 // Maximum number of allowed boxes +#define MAXHOTSPOT 20 // Maximum nimber of allowed hotspots + +class RMBox { +public: + struct Hotspot { + int _hotx, _hoty; // Hotspot coordinates + int _destination; // Hotspot destination + }; + +public: + int _left, _top, _right, _bottom; // Vertici bounding boxes + int _adj[MAXBOXES]; // List of adjacent bounding boxes + int _numHotspot; // Hotspot number + uint8 _destZ; // Z value for the bounding box + Hotspot _hotspot[MAXHOTSPOT]; // List of hotspots + + bool _bActive; + bool _bReversed; + + void readFromStream(Common::ReadStream &ds); +}; + +class RMBoxLoc { +public: + int _numbBox; + RMBox *_boxes; + + void readFromStream(Common::ReadStream &ds); + +public: + RMBoxLoc(); + virtual ~RMBoxLoc(); + + void recalcAllAdj(); +}; + +#define GAME_BOXES_SIZE 200 + +class RMGameBoxes { +protected: + RMBoxLoc *_allBoxes[GAME_BOXES_SIZE]; + int _nLocBoxes; + +public: + RMGameBoxes(); + ~RMGameBoxes(); + + void init(); + void close(); + + // Get binding boxes for a given location + RMBoxLoc *getBoxes(int nLoc); + int getLocBoxesCount() const; + + // Return the box which contains a given point + int whichBox(int nLoc, const RMPoint &pt); + + // Check whether a point is inside a given box + bool isInBox(int nLoc, int nBox, const RMPoint &pt); + + // Change the status of a box + void changeBoxStatus(int nLoc, int nBox, int status); + + // Save state handling + int getSaveStateSize(); + void saveState(byte *buf); + void loadState(byte *buf); +}; + +class RMCharacter : protected RMItem { +public: + enum Patterns { + PAT_STANDUP = 1, + PAT_STANDDOWN, + PAT_STANDLEFT, + PAT_STANDRIGHT, + PAT_WALKUP, + PAT_WALKDOWN, + PAT_WALKLEFT, + PAT_WALKRIGHT + }; + +private: + enum CharacterStatus { + STAND, + WALK + }; + + signed short _walkCount; + int _dx, _dy, _olddx, _olddy; + float _fx, _fy, _slope; + RMPoint _lineStart, _lineEnd, _pathEnd; + signed char _walkSpeed, _walkStatus; + char _minPath; + short _nextBox; + short _path[MAXBOXES]; + short _pathLength, _pathCount; + int _curBox; + + CharacterStatus _status; + int _curSpeed; + bool _bEndOfPath; + uint32 _hEndOfPath; + OSystem::MutexRef _csMove; + int _curLocation; + bool _bRemoveFromOT; + bool _bMovingWithoutMinpath; + RMGameBoxes *_theBoxes; + + RMPoint _fixedScroll; + +private: + int inWhichBox(const RMPoint &pt); + + short findPath(short source, short destination); + RMPoint searching(char UP, char DOWN, char RIGHT, char LEFT, RMPoint point); + RMPoint nearestPoint(const RMPoint &punto); + + void goTo(CORO_PARAM, RMPoint destcoord, bool bReversed = false); + short scanLine(const RMPoint &point); + RMPoint invScanLine(const RMPoint &point); + RMPoint nearestHotSpot(int sourcebox, int destbox); + + void newBoxEntered(int nBox); + +protected: + bool _bMoving; + bool _bDrawNow; + bool _bNeedToStop; + +public: + RMCharacter(); + virtual ~RMCharacter(); + + void linkToBoxes(RMGameBoxes *theBoxes); + + virtual void removeThis(CORO_PARAM, bool &result); + + // Update the position of a character + void doFrame(CORO_PARAM, RMGfxTargetBuffer *bigBuf, int loc); + + // Overloaded draw + virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim); + + // TRUE if you just stopped + bool endOfPath(); + + // Change the pattern of a character to STOP + virtual void stop(CORO_PARAM); + + // Check if the character is moving + bool isMoving(); + + // Move the character to a certain position + void move(CORO_PARAM, RMPoint pt, bool *result = NULL); + + // Place the character in a certain position WITHOUT moving + void setPosition(const RMPoint &pt, int newloc = -1); + + // Wait for the end of movement + void waitForEndMovement(CORO_PARAM); + + void setFixedScroll(const RMPoint &fix); + void setSpeed(int speed); +}; + +class RMWipe : public RMGfxTask { +private: + bool _bFading; + bool _bEndFade; + bool _bUnregister; + uint32 _hUnregistered; + int _nFadeStep; + uint32 _hEndOfFade; + bool _bMustRegister; + + RMItem _wip0r; + +public: + RMWipe(); + virtual ~RMWipe(); + + void doFrame(RMGfxTargetBuffer &bigBuf); + virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim); + + void initFade(int type); + void closeFade(); + void waitForFadeEnd(CORO_PARAM); + + virtual void unregister(); + virtual void removeThis(CORO_PARAM, bool &result); + virtual int priority(); +}; + +/** + * Location + */ +class RMLocation : public RMGfxTaskSetPrior { +public: + Common::String _name; // Name + +private: + RMColorMode _cmode; // Color mode + RMGfxSourceBuffer *_buf; // Location picture + + int _nItems; // Number of objects + RMItem *_items; // Objects + + RMPoint _curScroll; // Current scroll position + RMPoint _fixedScroll; + + RMPoint _prevScroll; // Previous scroll position + RMPoint _prevFixedScroll; + +public: + // @@@@@@@@@@@@@@@@@@@@@@@ + + RMPoint TEMPTonyStart; + RMPoint TEMPGetTonyStart(); + + int TEMPNumLoc; + int TEMPGetNumLoc(); + +public: + RMLocation(); + virtual ~RMLocation(); + + // Load variations + bool load(Common::SeekableReadStream &ds); + bool loadLOX(Common::SeekableReadStream &ds); + + // Unload + void unload(); + + // Overloaded draw + virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim); + + // Prepare a frame by drawing the location and all it's items + void doFrame(RMGfxTargetBuffer *bigBuf); + + // Return the item at a given point + RMItem *whichItemIsIn(const RMPoint &pt); + + // Return the item based on it's MPAL code + RMItem *getItemFromCode(uint32 dwCode); + + // Set the current scroll position + void setScrollPosition(const RMPoint &scroll); + + // Sets an additinal offset for scrolling + void setFixedScroll(const RMPoint &scroll); + + // Update the scrolling coordinates to display the specified point + void updateScrolling(const RMPoint &ptShowThis); + + // Read the current scroll position + RMPoint scrollPosition(); + + // Pause sound + void pauseSound(bool bPause); +}; + +/** + * MPAL message, composed of more ASCIIZ + */ +class RMMessage { +private: + char *_lpMessage; + char *_lpPeriods[256]; + int _nPeriods; + +private: + void parseMessage(); + +public: + RMMessage(); + RMMessage(uint32 dwId); + virtual ~RMMessage(); + + void load(uint32 dwId); + bool isValid(); + int numPeriods(); + char *period(int num); + char *operator[](int num); +}; + +} // End of namespace Tony + +#endif /* TONY_H */ diff --git a/engines/tony/module.mk b/engines/tony/module.mk new file mode 100644 index 0000000000..d66cf6f065 --- /dev/null +++ b/engines/tony/module.mk @@ -0,0 +1,33 @@ +MODULE := engines/tony + +MODULE_OBJS := \ + custom.o \ + debugger.o \ + detection.o \ + font.o \ + game.o \ + gfxcore.o \ + gfxengine.o \ + globals.o \ + input.o \ + inventory.o \ + loc.o \ + sound.o \ + tony.o \ + tonychar.o \ + utils.o \ + window.o \ + mpal/expr.o \ + mpal/loadmpc.o \ + mpal/memory.o \ + mpal/mpal.o \ + mpal/mpalutils.o \ + mpal/lzo.o + +# This module can be built as a plugin +ifeq ($(ENABLE_TONY), DYNAMIC_PLUGIN) +PLUGIN := 1 +endif + +# Include common rules +include $(srcdir)/rules.mk diff --git a/engines/tony/mpal/expr.cpp b/engines/tony/mpal/expr.cpp new file mode 100644 index 0000000000..7dc640ba7c --- /dev/null +++ b/engines/tony/mpal/expr.cpp @@ -0,0 +1,361 @@ +/* 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. + * + * + */ +/* + * This code is based on original Tony Tough source code + * + * Copyright (c) 1997-2003 Nayma Software + */ + +#include "tony/mpal/mpal.h" +#include "tony/mpal/memory.h" +#include "tony/mpal/mpaldll.h" +#include "tony/tony.h" + +namespace Tony { + +namespace MPAL { + +/** + * Duplicate a mathematical expression. + * + * @param h Handle to the original expression + * @retruns Pointer to the cloned expression + */ +static byte *duplicateExpression(MpalHandle h) { + byte *orig, *clone; + + orig = (byte *)globalLock(h); + + int num = *(byte *)orig; + LpExpression one = (LpExpression)(orig+1); + + clone = (byte *)globalAlloc(GMEM_FIXED, sizeof(Expression) * num + 1); + LpExpression two = (LpExpression)(clone + 1); + + memcpy(clone, orig, sizeof(Expression) * num + 1); + + for (int i = 0; i < num; i++) { + if (one->_type == ELT_PARENTH) { + two->_type = ELT_PARENTH2; + two->_val._pson = duplicateExpression(two->_val._son); + } + + ++one; + ++two; + } + + globalUnlock(h); + return clone; +} + +static int Compute(int a, int b, byte symbol) { + switch (symbol) { + case OP_MUL: + return a * b; + case OP_DIV: + return a / b; + case OP_MODULE: + return a % b; + case OP_ADD: + return a + b; + case OP_SUB: + return a - b; + case OP_SHL: + return a << b; + case OP_SHR: + return a >> b; + case OP_MINOR: + return a < b; + case OP_MAJOR: + return a > b; + case OP_MINEQ: + return a <= b; + case OP_MAJEQ: + return a >= b; + case OP_EQUAL: + return a == b; + case OP_NOEQUAL: + return a != b; + case OP_BITAND: + return a & b; + case OP_BITXOR: + return a ^ b; + case OP_BITOR: + return a | b; + case OP_AND: + return a && b; + case OP_OR: + return a || b; + default: + GLOBALS._mpalError = 1; + break; + } + + return 0; +} + +static void solve(LpExpression one, int num) { + LpExpression two, three; + + while (num > 1) { + two = one + 1; + if ((two->_symbol == 0) || (one->_symbol & 0xF0) <= (two->_symbol & 0xF0)) { + two->_val._num = Compute(one->_val._num, two->_val._num, one->_symbol); + memmove(one, two, (num - 1) * sizeof(Expression)); + --num; + } else { + int j = 1; + three = two + 1; + while ((three->_symbol != 0) && (two->_symbol & 0xF0) > (three->_symbol & 0xF0)) { + ++two; + ++three; + ++j; + } + + three->_val._num = Compute(two->_val._num, three->_val._num, two->_symbol); + memmove(two, three, (num - j - 1) * sizeof(Expression)); + --num; + } + } +} + +/** + * Calculates the result of a mathematical expression, replacing the current + * value of any variable. + * + * @param expr Pointer to an expression duplicated by DuplicateExpression + * @returns Value + */ +static int evaluateAndFreeExpression(byte *expr) { + int num = *expr; + LpExpression one = (LpExpression)(expr + 1); + + // 1) Substitutions of variables + LpExpression cur = one; + for (int i = 0; i < num; i++, cur++) { + if (cur->_type == ELT_VAR) { + cur->_type = ELT_NUMBER; + cur->_val._num = varGetValue(cur->_val._name); + } + } + + // 2) Replacement of brackets (using recursive calls) + cur = one; + for (int i = 0; i < num; i++, cur++) { + if (cur->_type == ELT_PARENTH2) { + cur->_type = ELT_NUMBER; + cur->_val._num = evaluateAndFreeExpression(cur->_val._pson); + } + } + + // 3) algebraic resolution + solve(one, num); + int val = one->_val._num; + globalDestroy(expr); + + return val; +} + +/** + * Parses a mathematical expression from the MPC file + * + * @param buf Buffer containing the expression to evaluate + * @param h Pointer to a handle that, at the end of execution, + * will point to the area of memory containing the parsed expression + * @returns Pointer to the buffer immediately after the expression, or NULL if error. + */ +const byte *parseExpression(const byte *lpBuf, MpalHandle *h) { + byte *start; + + uint32 num = *lpBuf; + lpBuf++; + + if (num == 0) + return NULL; + + *h = globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, num * sizeof(Expression) + 1); + if (*h == NULL) + return NULL; + + start = (byte *)globalLock(*h); + *start = (byte)num; + + LpExpression cur = (LpExpression)(start + 1); + + for (uint32 i = 0;i < num; i++) { + cur->_type = *(lpBuf); + + // *(lpBuf + 1) contains the unary operator, unused => skipped + lpBuf += 2; + + switch (cur->_type) { + case ELT_NUMBER: + cur->_val._num = (int32)READ_LE_UINT32(lpBuf); + lpBuf += 4; + break; + + case ELT_VAR: + cur->_val._name = (char *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, (*lpBuf) + 1); + if (cur->_val._name == NULL) + return NULL; + memcpy(cur->_val._name, lpBuf + 1, *lpBuf); + lpBuf += *lpBuf + 1; + break; + + case ELT_PARENTH: + lpBuf = parseExpression(lpBuf, &cur->_val._son); + if (lpBuf == NULL) + return NULL; + break; + + default: + return NULL; + } + + cur->_symbol = *lpBuf; + lpBuf++; + + cur++; + } + + if (*lpBuf != 0) + return NULL; + + lpBuf++; + + return lpBuf; +} + +/** + * Calculate the value of a mathamatical expression + * + * @param h Handle to the expression + * @returns Numeric value + */ +int evaluateExpression(MpalHandle h) { + lockVar(); + int ret = evaluateAndFreeExpression(duplicateExpression(h)); + unlockVar(); + + return ret; +} + +/** + * Compare two mathematical expressions together + * + * @param h1 Expression to be compared + * @param h2 Expression to be compared + */ +bool compareExpressions(MpalHandle h1, MpalHandle h2) { + byte *e1, *e2; + + e1 = (byte *)globalLock(h1); + e2 = (byte *)globalLock(h2); + + int num1 = *(byte *)e1; + int num2 = *(byte *)e2; + + if (num1 != num2) { + globalUnlock(h1); + globalUnlock(h2); + return false; + } + + LpExpression one = (LpExpression)(e1 + 1); + LpExpression two = (LpExpression)(e2 + 1); + + for (int i = 0; i < num1; i++) { + if (one->_type != two->_type || (i != num1 - 1 && one->_symbol != two->_symbol)) { + globalUnlock(h1); + globalUnlock(h2); + return false; + } + + switch (one->_type) { + case ELT_NUMBER: + if (one->_val._num != two->_val._num) { + globalUnlock(h1); + globalUnlock(h2); + return false; + } + break; + + case ELT_VAR: + if (strcmp(one->_val._name, two->_val._name) != 0) { + globalUnlock(h1); + globalUnlock(h2); + return false; + } + break; + + case ELT_PARENTH: + if (!compareExpressions(one->_val._son, two->_val._son)) { + globalUnlock(h1); + globalUnlock(h2); + return false; + } + break; + } + + ++one; + ++two; + } + + globalUnlock(h1); + globalUnlock(h2); + + return true; +} + +/** + * Frees an expression that was previously parsed + * + * @param h Handle for the expression + */ +void freeExpression(MpalHandle h) { + byte *data = (byte *)globalLock(h); + int num = *data; + LpExpression cur = (LpExpression)(data + 1); + + for (int i = 0; i < num; ++i, ++cur) { + switch (cur->_type) { + case ELT_VAR: + globalDestroy(cur->_val._name); + break; + + case ELT_PARENTH: + freeExpression(cur->_val._son); + break; + + default: + break; + } + } + + globalUnlock(h); + globalFree(h); +} + +} // end of namespace MPAL + +} // end of namespace Tony diff --git a/engines/tony/mpal/expr.h b/engines/tony/mpal/expr.h new file mode 100644 index 0000000000..256d09bb9b --- /dev/null +++ b/engines/tony/mpal/expr.h @@ -0,0 +1,140 @@ +/* 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. + * + * + */ +/* + * This code is based on original Tony Tough source code + * + * Copyright (c) 1997-2003 Nayma Software + */ + +#ifndef MPAL_EXPR_H +#define MPAL_EXPR_H + +#include "tony/mpal/memory.h" + +namespace Tony { + +namespace MPAL { + +/** + * @defgroup Mathamatical operations + */ +//@{ + +#define OP_MUL ((1 << 4) | 0) +#define OP_DIV ((1 << 4) | 1) +#define OP_MODULE ((1 << 4) | 2) +#define OP_ADD ((2 << 4) | 0) +#define OP_SUB ((2 << 4) | 1) +#define OP_SHL ((3 << 4) | 0) +#define OP_SHR ((3 << 4) | 1) +#define OP_MINOR ((4 << 4) | 0) +#define OP_MAJOR ((4 << 4) | 1) +#define OP_MINEQ ((4 << 4) | 2) +#define OP_MAJEQ ((4 << 4) | 3) +#define OP_EQUAL ((5 << 4) | 0) +#define OP_NOEQUAL ((5 << 4) | 1) +#define OP_BITAND ((6 << 4) | 0) +#define OP_BITXOR ((7 << 4) | 0) +#define OP_BITOR ((8 << 4) | 0) +#define OP_AND ((9 << 4) | 0) +#define OP_OR ((10 << 4) | 0) + +//@} + +/** + * @defgroup Structures + */ + +//@{ +/** + * Mathamatical framework to manage operations + */ +typedef struct { + byte _type; // Object Type (see enum ExprListTypes) + + union { + int _num; // Identifier (if type == ELT_NUMBER) + char *_name; // Variable name (if type == ELT_VAR) + MpalHandle _son; // Handle expressions (if type == ELT_PARENTH) + byte *_pson; // Handle lockato (if type == ELT_PARENTH2) + } _val; + + byte _symbol; // Mathematic symbols (see #define OP_*) + +} Expression; +typedef Expression *LpExpression; + +//@} + +/** + * Object types that can be contained in an EXPRESSION structure + */ +enum ExprListTypes { + ELT_NUMBER = 1, + ELT_VAR = 2, + ELT_PARENTH = 3, + ELT_PARENTH2 = 4 +}; + +/****************************************************************************\ +* Function Prototypes +\****************************************************************************/ + +/** + * Parses a mathematical expression from the MPC file + * + * @param buf Buffer containing the expression to evaluate + * @param h Pointer to a handle that, at the end of execution, + * will point to the area of memory containing the parsed expression + * @returns Pointer to the buffer immediately after the expression, or NULL if error. + */ +const byte *parseExpression(const byte *lpBuf, MpalHandle *h); + +/** + * Calculate the value of a mathamatical expression + * + * @param h Handle to the expression + * @returns Numeric value + */ +int evaluateExpression(MpalHandle h); + +/** + * Compare two mathematical expressions together + * + * @param h1 Expression to be compared + * @param h2 Expression to be compared + */ +bool compareExpressions(MpalHandle h1, MpalHandle h2); + +/** + * Frees an expression that was previously parsed + * + * @param h Handle for the expression + */ +void freeExpression(MpalHandle h); + +} // end of namespace MPAL + +} // end of namespace Tony + +#endif diff --git a/engines/tony/mpal/loadmpc.cpp b/engines/tony/mpal/loadmpc.cpp new file mode 100644 index 0000000000..4eb84d1406 --- /dev/null +++ b/engines/tony/mpal/loadmpc.cpp @@ -0,0 +1,787 @@ +/* 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. + * + * + */ +/* + * This code is based on original Tony Tough source code + * + * Copyright (c) 1997-2003 Nayma Software + */ + +#include "mpal.h" +#include "mpaldll.h" +#include "memory.h" +#include "tony/tony.h" + +namespace Tony { + +namespace MPAL { + +/****************************************************************************\ +* Static functions +\****************************************************************************/ + +static bool compareCommands(struct Command *cmd1, struct Command *cmd2) { + if (cmd1->_type == 2 && cmd2->_type == 2) { + if (strcmp(cmd1->_lpszVarName, cmd2->_lpszVarName) == 0 && + compareExpressions(cmd1->_expr, cmd2->_expr)) + return true; + else + return false; + } else + return (memcmp(cmd1, cmd2, sizeof(struct Command)) == 0); +} + +/** + * Parses a script from the MPC file, and inserts its data into a structure + * + * @param lpBuf Buffer containing the compiled script. + * @param lpmsScript Pointer to a structure that will be filled with the + * data of the script. + * @returns Pointer to the buffer after the item, or NULL on failure. + */ +static const byte *ParseScript(const byte *lpBuf, LpMpalScript lpmsScript) { + lpmsScript->_nObj = (int32)READ_LE_UINT32(lpBuf); + lpBuf += 4; + + lpmsScript->_nMoments = READ_LE_UINT16(lpBuf); + lpBuf += 2; + + int curCmd = 0; + + for (uint i = 0; i < lpmsScript->_nMoments; i++) { + lpmsScript->_moment[i]._dwTime = (int32)READ_LE_UINT32(lpBuf); + lpBuf += 4; + lpmsScript->_moment[i]._nCmds = *lpBuf; + lpBuf++; + + for (int j = 0; j < lpmsScript->_moment[i]._nCmds; j++) { + lpmsScript->_command[curCmd]._type = *lpBuf; + lpBuf++; + switch (lpmsScript->_command[curCmd]._type) { + case 1: + lpmsScript->_command[curCmd]._nCf = READ_LE_UINT16(lpBuf); + lpBuf += 2; + lpmsScript->_command[curCmd]._arg1 = (int32)READ_LE_UINT32(lpBuf); + lpBuf += 4; + lpmsScript->_command[curCmd]._arg2 = (int32)READ_LE_UINT32(lpBuf); + lpBuf += 4; + lpmsScript->_command[curCmd]._arg3 = (int32)READ_LE_UINT32(lpBuf); + lpBuf += 4; + lpmsScript->_command[curCmd]._arg4 = (int32)READ_LE_UINT32(lpBuf); + lpBuf += 4; + break; + + case 2: { // Variable assign + int len = *lpBuf; + lpBuf++; + lpmsScript->_command[curCmd]._lpszVarName = (char *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, len + 1); + if (lpmsScript->_command[curCmd]._lpszVarName == NULL) + return NULL; + memcpy(lpmsScript->_command[curCmd]._lpszVarName, lpBuf, len); + lpBuf += len; + + lpBuf = parseExpression(lpBuf, &lpmsScript->_command[curCmd]._expr); + if (lpBuf == NULL) + return NULL; + break; + } + default: + return NULL; + } + + lpmsScript->_moment[i]._cmdNum[j] = curCmd; + curCmd++; + } + } + return lpBuf; +} + +/** + * Frees a script allocated via a previous call to ParseScript + * + * @param lpmsScript Pointer to a script structure + */ +static void FreeScript(LpMpalScript lpmsScript) { + for (int i = 0; i < MAX_COMMANDS_PER_SCRIPT && (lpmsScript->_command[i]._type); ++i, ++lpmsScript) { + if (lpmsScript->_command[i]._type == 2) { + // Variable Assign + globalDestroy(lpmsScript->_command[i]._lpszVarName); + freeExpression(lpmsScript->_command[i]._expr); + } + } +} + +/** + * Parses a dialog from the MPC file, and inserts its data into a structure + * + * @param lpBuf Buffer containing the compiled dialog. + * @param lpmdDialog Pointer to a structure that will be filled with the + * data of the dialog. + * @returns Pointer to the buffer after the item, or NULL on failure. + */ +static const byte *parseDialog(const byte *lpBuf, LpMpalDialog lpmdDialog) { + byte *lpLock; + + lpmdDialog->_nObj = READ_LE_UINT32(lpBuf); + lpBuf += 4; + + // Periods + uint32 num = READ_LE_UINT16(lpBuf); + lpBuf += 2; + + if (num >= MAX_PERIODS_PER_DIALOG - 1) + error("Too much periods in dialog #%d", lpmdDialog->_nObj); + + uint32 i; + for (i = 0; i < num; i++) { + lpmdDialog->_periodNums[i] = READ_LE_UINT16(lpBuf); + lpBuf += 2; + lpmdDialog->_periods[i] = globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, *lpBuf + 1); + lpLock = (byte *)globalLock(lpmdDialog->_periods[i]); + Common::copy(lpBuf + 1, lpBuf + 1 + *lpBuf, lpLock); + globalUnlock(lpmdDialog->_periods[i]); + lpBuf += (*lpBuf) + 1; + } + + lpmdDialog->_periodNums[i] = 0; + lpmdDialog->_periods[i] = NULL; + + // Groups + num = READ_LE_UINT16(lpBuf); + lpBuf += 2; + uint32 curCmd = 0; + + if (num >= MAX_GROUPS_PER_DIALOG) + error("Too much groups in dialog #%d", lpmdDialog->_nObj); + + for (i = 0; i < num; i++) { + lpmdDialog->_group[i]._num = READ_LE_UINT16(lpBuf); + lpBuf += 2; + lpmdDialog->_group[i]._nCmds = *lpBuf; lpBuf++; + + if (lpmdDialog->_group[i]._nCmds >= MAX_COMMANDS_PER_GROUP) + error("Too much commands in group #%d in dialog #%d", lpmdDialog->_group[i]._num, lpmdDialog->_nObj); + + for (uint32 j = 0; j < lpmdDialog->_group[i]._nCmds; j++) { + lpmdDialog->_command[curCmd]._type = *lpBuf; + lpBuf++; + + switch (lpmdDialog->_command[curCmd]._type) { + // Call custom function + case 1: + lpmdDialog->_command[curCmd]._nCf = READ_LE_UINT16(lpBuf); + lpBuf += 2; + lpmdDialog->_command[curCmd]._arg1 = READ_LE_UINT32(lpBuf); + lpBuf += 4; + lpmdDialog->_command[curCmd]._arg2 = READ_LE_UINT32(lpBuf); + lpBuf += 4; + lpmdDialog->_command[curCmd]._arg3 = READ_LE_UINT32(lpBuf); + lpBuf += 4; + lpmdDialog->_command[curCmd]._arg4 = READ_LE_UINT32(lpBuf); + lpBuf += 4; + break; + + // Variable assign + case 2: { + uint32 len = *lpBuf; + lpBuf++; + lpmdDialog->_command[curCmd]._lpszVarName = (char *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, len + 1); + if (lpmdDialog->_command[curCmd]._lpszVarName == NULL) + return NULL; + + Common::copy(lpBuf, lpBuf + len, lpmdDialog->_command[curCmd]._lpszVarName); + lpBuf += len; + + lpBuf = parseExpression(lpBuf, &lpmdDialog->_command[curCmd]._expr); + if (lpBuf == NULL) + return NULL; + break; + } + + // Do Choice + case 3: + lpmdDialog->_command[curCmd]._nChoice = READ_LE_UINT16(lpBuf); + lpBuf += 2; + break; + + default: + return NULL; + } + + uint32 kk; + for (kk = 0;kk < curCmd; kk++) { + if (compareCommands(&lpmdDialog->_command[kk], &lpmdDialog->_command[curCmd])) { + lpmdDialog->_group[i]._cmdNum[j] = kk; + + // Free any data allocated for the duplictaed command + if (lpmdDialog->_command[curCmd]._type == 2) { + globalDestroy(lpmdDialog->_command[curCmd]._lpszVarName); + freeExpression(lpmdDialog->_command[curCmd]._expr); + + lpmdDialog->_command[curCmd]._lpszVarName = NULL; + lpmdDialog->_command[curCmd]._expr = 0; + lpmdDialog->_command[curCmd]._type = 0; + } + break; + } + } + + if (kk == curCmd) { + lpmdDialog->_group[i]._cmdNum[j] = curCmd; + curCmd++; + } + } + } + + if (curCmd >= MAX_COMMANDS_PER_DIALOG) + error("Too much commands in dialog #%d", lpmdDialog->_nObj); + + // Choices + num = READ_LE_UINT16(lpBuf); + lpBuf += 2; + + if (num >= MAX_CHOICES_PER_DIALOG) + error("Too much choices in dialog #%d", lpmdDialog->_nObj); + + for (i = 0; i < num; i++) { + lpmdDialog->_choice[i]._nChoice = READ_LE_UINT16(lpBuf); + lpBuf += 2; + + uint32 num2 = *lpBuf++; + + if (num2 >= MAX_SELECTS_PER_CHOICE) + error("Too much selects in choice #%d in dialog #%d", lpmdDialog->_choice[i]._nChoice, lpmdDialog->_nObj); + + for (uint32 j = 0; j < num2; j++) { + // When + switch (*lpBuf++) { + case 0: + lpmdDialog->_choice[i]._select[j]._when = NULL; + break; + + case 1: + lpBuf = parseExpression(lpBuf, &lpmdDialog->_choice[i]._select[j]._when); + if (lpBuf == NULL) + return NULL; + break; + + case 2: + return NULL; + } + + // Attrib + lpmdDialog->_choice[i]._select[j]._attr = *lpBuf++; + + // Data + lpmdDialog->_choice[i]._select[j]._dwData = READ_LE_UINT32(lpBuf); + lpBuf += 4; + + // PlayGroup + uint32 num3 = *lpBuf++; + + if (num3 >= MAX_PLAYGROUPS_PER_SELECT) + error("Too much playgroups in select #%d in choice #%d in dialog #%d", j, lpmdDialog->_choice[i]._nChoice, lpmdDialog->_nObj); + + for (uint32 z = 0; z < num3; z++) { + lpmdDialog->_choice[i]._select[j]._wPlayGroup[z] = READ_LE_UINT16(lpBuf); + lpBuf += 2; + } + + lpmdDialog->_choice[i]._select[j]._wPlayGroup[num3] = 0; + } + + // Mark the last selection + lpmdDialog->_choice[i]._select[num2]._dwData = 0; + } + + lpmdDialog->_choice[num]._nChoice = 0; + + return lpBuf; +} + +/** + * Parses an item from the MPC file, and inserts its data into a structure + * + * @param lpBuf Buffer containing the compiled dialog. + * @param lpmiItem Pointer to a structure that will be filled with the + * data of the item. + * @returns Pointer to the buffer after the item, or NULL on failure. + * @remarks It's necessary that the structure that is passed has been + * completely initialized to 0 beforehand. + */ +static const byte *parseItem(const byte *lpBuf, LpMpalItem lpmiItem) { + lpmiItem->_nObj = (int32)READ_LE_UINT32(lpBuf); + lpBuf += 4; + + byte len = *lpBuf; + lpBuf++; + memcpy(lpmiItem->_lpszDescribe, lpBuf, MIN((byte)127, len)); + lpBuf += len; + + if (len >= MAX_DESCRIBE_SIZE) + error("Describe too long in item #%d", lpmiItem->_nObj); + + lpmiItem->_nActions=*lpBuf; + lpBuf++; + + // Allocation action + if (lpmiItem->_nActions > 0) + lpmiItem->_action = (ItemAction *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(struct ItemAction) * (int)lpmiItem->_nActions); + + uint32 curCmd = 0; + + for (uint32 i = 0; i < lpmiItem->_nActions; i++) { + lpmiItem->_action[i]._num = *lpBuf; + lpBuf++; + + lpmiItem->_action[i]._wParm = READ_LE_UINT16(lpBuf); + lpBuf += 2; + + if (lpmiItem->_action[i]._num == 0xFF) { + lpmiItem->_action[i]._wTime = READ_LE_UINT16(lpBuf); + lpBuf += 2; + + lpmiItem->_action[i]._perc = *lpBuf; + lpBuf++; + } + + if (*lpBuf == 0) { + lpBuf++; + lpmiItem->_action[i]._when = NULL; + } else { + lpBuf++; + lpBuf = parseExpression(lpBuf,&lpmiItem->_action[i]._when); + if (lpBuf == NULL) + return NULL; + } + + lpmiItem->_action[i]._nCmds=*lpBuf; + lpBuf++; + + if (lpmiItem->_action[i]._nCmds >= MAX_COMMANDS_PER_ACTION) + error("Too much commands in action #%d in item #%d", lpmiItem->_action[i]._num, lpmiItem->_nObj); + + for (uint32 j = 0; j < lpmiItem->_action[i]._nCmds; j++) { + lpmiItem->_command[curCmd]._type = *lpBuf; + lpBuf++; + switch (lpmiItem->_command[curCmd]._type) { + case 1: // Call custom function + lpmiItem->_command[curCmd]._nCf = READ_LE_UINT16(lpBuf); + lpBuf += 2; + lpmiItem->_command[curCmd]._arg1 = (int32)READ_LE_UINT32(lpBuf); + lpBuf += 4; + lpmiItem->_command[curCmd]._arg2 = (int32)READ_LE_UINT32(lpBuf); + lpBuf += 4; + lpmiItem->_command[curCmd]._arg3 = (int32)READ_LE_UINT32(lpBuf); + lpBuf += 4; + lpmiItem->_command[curCmd]._arg4 = (int32)READ_LE_UINT32(lpBuf); + lpBuf += 4; + break; + + case 2: // Variable assign + len = *lpBuf; + lpBuf++; + lpmiItem->_command[curCmd]._lpszVarName = (char *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, len + 1); + if (lpmiItem->_command[curCmd]._lpszVarName == NULL) + return NULL; + memcpy(lpmiItem->_command[curCmd]._lpszVarName, lpBuf, len); + lpBuf += len; + + lpBuf = parseExpression(lpBuf, &lpmiItem->_command[curCmd]._expr); + if (lpBuf == NULL) + return NULL; + break; + + default: + return NULL; + } + + uint32 kk; + for (kk = 0; kk < curCmd; kk++) { + if (compareCommands(&lpmiItem->_command[kk], &lpmiItem->_command[curCmd])) { + lpmiItem->_action[i]._cmdNum[j] = kk; + + // Free any data allocated for the duplictaed command + if (lpmiItem->_command[curCmd]._type == 2) { + globalDestroy(lpmiItem->_command[curCmd]._lpszVarName); + freeExpression(lpmiItem->_command[curCmd]._expr); + + lpmiItem->_command[curCmd]._lpszVarName = NULL; + lpmiItem->_command[curCmd]._expr = 0; + lpmiItem->_command[curCmd]._type = 0; + } + break; + } + } + + if (kk == curCmd) { + lpmiItem->_action[i]._cmdNum[j] = curCmd; + curCmd++; + + if (curCmd >= MAX_COMMANDS_PER_ITEM) { + error("Too much commands in item #%d", lpmiItem->_nObj); + //curCmd=0; + } + } + } + } + + lpmiItem->_dwRes = READ_LE_UINT32(lpBuf); + lpBuf += 4; + + return lpBuf; +} + +/** + * Frees an item parsed from a prior call to ParseItem + * + * @param lpmiItem Pointer to an item structure + */ +static void freeItem(LpMpalItem lpmiItem) { + // Free the actions + if (lpmiItem->_action) { + for (int i = 0; i < lpmiItem->_nActions; ++i) { + if (lpmiItem->_action[i]._when != 0) + freeExpression(lpmiItem->_action[i]._when); + } + + globalDestroy(lpmiItem->_action); + } + + // Free the commands + for (int i = 0; i < MAX_COMMANDS_PER_ITEM && (lpmiItem->_command[i]._type); ++i) { + if (lpmiItem->_command[i]._type == 2) { + // Variable Assign + globalDestroy(lpmiItem->_command[i]._lpszVarName); + freeExpression(lpmiItem->_command[i]._expr); + } + } +} + +/** + * Parses a location from the MPC file, and inserts its data into a structure + * + * @param lpBuf Buffer containing the compiled location. + * @param lpmiLocation Pointer to a structure that will be filled with the + * data of the location. + * @returns Pointer to the buffer after the location, or NULL on failure. + */ +static const byte *ParseLocation(const byte *lpBuf, LpMpalLocation lpmlLocation) { + lpmlLocation->_nObj = (int32)READ_LE_UINT32(lpBuf); + lpBuf += 4; + lpmlLocation->_dwXlen = READ_LE_UINT16(lpBuf); + lpBuf += 2; + lpmlLocation->_dwYlen = READ_LE_UINT16(lpBuf); + lpBuf += 2; + lpmlLocation->_dwPicRes = READ_LE_UINT32(lpBuf); + lpBuf += 4; + + return lpBuf; +} + +/****************************************************************************\ +* Exported functions +\****************************************************************************/ +/** + * @defgroup Exported functions + */ +//@{ + +/** + * Reads and interprets the MPC file, and create structures for various directives + * in the global variables + * + * @param lpBuf Buffer containing the MPC file data, excluding the header. + * @returns True if succeeded OK, false if failure. + */ +bool parseMpc(const byte *lpBuf) { + byte *lpTemp; + + // 1. Variables + if (lpBuf[0] != 'V' || lpBuf[1] != 'A' || lpBuf[2] != 'R' || lpBuf[3] != 'S') + return false; + + lpBuf += 4; + GLOBALS._nVars = READ_LE_UINT16(lpBuf); + lpBuf += 2; + + GLOBALS._hVars = globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, sizeof(MpalVar) * (uint32)GLOBALS._nVars); + if (GLOBALS._hVars == NULL) + return false; + + GLOBALS._lpmvVars = (LpMpalVar)globalLock(GLOBALS._hVars); + + for (uint16 i = 0; i < GLOBALS._nVars; i++) { + uint16 wLen = *(const byte *)lpBuf; + lpBuf++; + memcpy(GLOBALS._lpmvVars->_lpszVarName, lpBuf, MIN(wLen, (uint16)32)); + lpBuf += wLen; + GLOBALS._lpmvVars->_dwVal = READ_LE_UINT32(lpBuf); + lpBuf += 4; + + lpBuf++; // Skip 'ext' + GLOBALS._lpmvVars++; + } + + globalUnlock(GLOBALS._hVars); + + // 2. Messages + if (lpBuf[0] != 'M' || lpBuf[1] != 'S' || lpBuf[2] != 'G' || lpBuf[3] != 'S') + return false; + + lpBuf += 4; + GLOBALS._nMsgs = READ_LE_UINT16(lpBuf); + lpBuf += 2; + +#ifdef NEED_LOCK_MSGS + GLOBALS._hMsgs = globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, sizeof(MpalMsg) * (uint32)GLOBALS._nMsgs); + if (GLOBALS._hMsgs == NULL) + return false; + + GLOBALS._lpmmMsgs = (LpMpalMsg)globalLock(GLOBALS._hMsgs); +#else + GLOBALS._lpmmMsgs=(LPMPALMSG)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(MPALMSG) * (uint32)GLOBALS._nMsgs); + if (GLOBALS._lpmmMsgs==NULL) + return false; +#endif + + for (uint16 i = 0; i < GLOBALS._nMsgs; i++) { + GLOBALS._lpmmMsgs->_wNum = READ_LE_UINT16(lpBuf); + lpBuf += 2; + + uint16 j; + for (j = 0; lpBuf[j] != 0;) + j += lpBuf[j] + 1; + + GLOBALS._lpmmMsgs->_hText = globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, j + 1); + lpTemp = (byte *)globalLock(GLOBALS._lpmmMsgs->_hText); + + for (j = 0; lpBuf[j] != 0;) { + memcpy(lpTemp, &lpBuf[j + 1], lpBuf[j]); + lpTemp += lpBuf[j]; + *lpTemp ++= '\0'; + j += lpBuf[j] + 1; + } + + lpBuf += j + 1; + *lpTemp = '\0'; + + globalUnlock(GLOBALS._lpmmMsgs->_hText); + GLOBALS._lpmmMsgs++; + } + +#ifdef NEED_LOCK_MSGS + globalUnlock(GLOBALS._hMsgs); +#endif + + // 3. Objects + if (lpBuf[0] != 'O' || lpBuf[1] != 'B' || lpBuf[2] != 'J' || lpBuf[3] != 'S') + return false; + + lpBuf += 4; + GLOBALS._nObjs = READ_LE_UINT16(lpBuf); + lpBuf += 2; + + // Check out the dialogs + GLOBALS._nDialogs = 0; + GLOBALS._hDialogs = GLOBALS._lpmdDialogs = NULL; + if (*((const byte *)lpBuf + 2) == 6 && strncmp((const char *)lpBuf + 3, "Dialog", 6) == 0) { + GLOBALS._nDialogs = READ_LE_UINT16(lpBuf); + lpBuf += 2; + + GLOBALS._hDialogs = globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, (uint32)GLOBALS._nDialogs * sizeof(MpalDialog)); + if (GLOBALS._hDialogs == NULL) + return false; + + GLOBALS._lpmdDialogs = (LpMpalDialog)globalLock(GLOBALS._hDialogs); + + for (uint16 i = 0; i < GLOBALS._nDialogs; i++) { + if ((lpBuf = parseDialog(lpBuf + 7, &GLOBALS._lpmdDialogs[i])) == NULL) + return false; + } + + globalUnlock(GLOBALS._hDialogs); + } + + // Check the items + GLOBALS._nItems = 0; + GLOBALS._hItems = GLOBALS._lpmiItems = NULL; + if (*(lpBuf + 2) == 4 && strncmp((const char *)lpBuf + 3, "Item", 4) == 0) { + GLOBALS._nItems = READ_LE_UINT16(lpBuf); + lpBuf += 2; + + // Allocate memory and read them in + GLOBALS._hItems = globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, (uint32)GLOBALS._nItems * sizeof(MpalItem)); + if (GLOBALS._hItems == NULL) + return false; + + GLOBALS._lpmiItems = (LpMpalItem)globalLock(GLOBALS._hItems); + + for (uint16 i = 0; i < GLOBALS._nItems; i++) { + if ((lpBuf = parseItem(lpBuf + 5, &GLOBALS._lpmiItems[i])) == NULL) + return false; + } + + globalUnlock(GLOBALS._hItems); + } + + // Check the locations + GLOBALS._nLocations = 0; + GLOBALS._hLocations = GLOBALS._lpmlLocations = NULL; + if (*(lpBuf + 2) == 8 && strncmp((const char *)lpBuf + 3, "Location", 8) == 0) { + GLOBALS._nLocations = READ_LE_UINT16(lpBuf); + lpBuf += 2; + + // Allocate memory and read them in + GLOBALS._hLocations = globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, (uint32)GLOBALS._nLocations * sizeof(MpalLocation)); + if (GLOBALS._hLocations == NULL) + return false; + + GLOBALS._lpmlLocations = (LpMpalLocation)globalLock(GLOBALS._hLocations); + + for (uint16 i = 0; i < GLOBALS._nLocations; i++) { + if ((lpBuf = ParseLocation(lpBuf + 9, &GLOBALS._lpmlLocations[i])) == NULL) + return false; + } + + globalUnlock(GLOBALS._hLocations); + } + + // Check the scripts + GLOBALS._nScripts = 0; + GLOBALS._hScripts = GLOBALS._lpmsScripts = NULL; + if (*(lpBuf + 2) == 6 && strncmp((const char *)lpBuf + 3, "Script", 6) == 0) { + GLOBALS._nScripts = READ_LE_UINT16(lpBuf); + lpBuf += 2; + + // Allocate memory + GLOBALS._hScripts = globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, (uint32)GLOBALS._nScripts * sizeof(MpalScript)); + if (GLOBALS._hScripts == NULL) + return false; + + GLOBALS._lpmsScripts = (LpMpalScript)globalLock(GLOBALS._hScripts); + + for (uint16 i = 0; i < GLOBALS._nScripts; i++) { + if ((lpBuf = ParseScript(lpBuf + 7, &GLOBALS._lpmsScripts[i])) == NULL) + return false; + + // Sort the various moments of the script + //qsort( + //GLOBALS.lpmsScripts[i].Moment, + //GLOBALS.lpmsScripts[i].nMoments, + //sizeof(GLOBALS.lpmsScripts[i].Moment[0]), + //(int (*)(const void *, const void *))CompareMoments + //); + } + + globalUnlock(GLOBALS._hScripts); + } + + if (lpBuf[0] != 'E' || lpBuf[1] != 'N' || lpBuf[2] != 'D' || lpBuf[3] != '0') + return false; + + return true; +} + +/** + * Free the given dialog + */ +static void freeDialog(LpMpalDialog lpmdDialog) { + // Free the periods + for (int i = 0; i < MAX_PERIODS_PER_DIALOG && (lpmdDialog->_periods[i]); ++i) + globalFree(lpmdDialog->_periods[i]); + + for (int i = 0; i < MAX_COMMANDS_PER_DIALOG && (lpmdDialog->_command[i]._type); i++) { + if (lpmdDialog->_command[i]._type == 2) { + // Variable assign + globalDestroy(lpmdDialog->_command[i]._lpszVarName); + freeExpression(lpmdDialog->_command[i]._expr); + } + } + + // Free the choices + for (int i = 0; i < MAX_CHOICES_PER_DIALOG; ++i) { + for (int j = 0; j < MAX_SELECTS_PER_CHOICE; j++) { + if (lpmdDialog->_choice[i]._select[j]._when) + freeExpression(lpmdDialog->_choice[i]._select[j]._when); + } + } +} + +/** + * Frees any data allocated from the parsing of the MPC file + */ +void freeMpc() { + // Free variables + globalFree(GLOBALS._hVars); + + // Free messages + LpMpalMsg lpmmMsgs = (LpMpalMsg)globalLock(GLOBALS._hMsgs); + for (int i = 0; i < GLOBALS._nMsgs; i++, ++lpmmMsgs) + globalFree(lpmmMsgs->_hText); + + globalUnlock(GLOBALS._hMsgs); + globalFree(GLOBALS._hMsgs); + + // Free objects + if (GLOBALS._hDialogs) { + LpMpalDialog lpmdDialogs = (LpMpalDialog)globalLock(GLOBALS._hDialogs); + + for (int i = 0; i < GLOBALS._nDialogs; i++, ++lpmdDialogs) + freeDialog(lpmdDialogs); + + globalFree(GLOBALS._hDialogs); + } + + // Free items + if (GLOBALS._hItems) { + LpMpalItem lpmiItems = (LpMpalItem)globalLock(GLOBALS._hItems); + + for (int i = 0; i < GLOBALS._nItems; ++i, ++lpmiItems) + freeItem(lpmiItems); + + globalUnlock(GLOBALS._hItems); + globalFree(GLOBALS._hItems); + } + + // Free the locations + if (GLOBALS._hLocations) { + globalFree(GLOBALS._hLocations); + } + + // Free the scripts + if (GLOBALS._hScripts) { + LpMpalScript lpmsScripts = (LpMpalScript)globalLock(GLOBALS._hScripts); + + for (int i = 0; i < GLOBALS._nScripts; ++i, ++lpmsScripts) { + FreeScript(lpmsScripts); + } + + globalUnlock(GLOBALS._hScripts); + } +} + +//@} + +} // end of namespace MPAL + +} // end of namespace Tony diff --git a/engines/tony/mpal/loadmpc.h b/engines/tony/mpal/loadmpc.h new file mode 100644 index 0000000000..20956288aa --- /dev/null +++ b/engines/tony/mpal/loadmpc.h @@ -0,0 +1,59 @@ +/* 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. + * + * + */ +/* + * This code is based on original Tony Tough source code + * + * Copyright (c) 1997-2003 Nayma Software + */ + +#ifndef __LOADMPC_H +#define __LOADMPC_H + +namespace Tony { + +namespace MPAL { + +/****************************************************************************\ +* Function prototypes +\****************************************************************************/ + +/** + * Reads and interprets the MPC file, and create structures for various directives + * in the global variables + * + * @param lpBuf Buffer containing the MPC file data, excluding the header. + * @returns True if succeeded OK, false if failure. + */ +bool parseMpc(const byte *lpBuf); + +/** + * Frees any data allocated from the parsing of the MPC file + */ +void freeMpc(); + +} // end of namespace MPAL + +} // end of namespace Tony + +#endif + diff --git a/engines/tony/mpal/lzo.cpp b/engines/tony/mpal/lzo.cpp new file mode 100644 index 0000000000..a04a769528 --- /dev/null +++ b/engines/tony/mpal/lzo.cpp @@ -0,0 +1,511 @@ +/* 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. + * + * + */ +/* minilzo.c -- mini subset of the LZO real-time data compression library + + This file is part of the LZO real-time data compression library. + + Copyright (C) 2011 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2010 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2009 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2008 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2007 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2006 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2005 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2004 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2003 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2002 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2001 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1999 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1998 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1997 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer + All Rights Reserved. + + The LZO library 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. + + The LZO library 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 the LZO library; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + Markus F.X.J. Oberhumer + <markus@oberhumer.com> + http://www.oberhumer.com/opensource/lzo/ + */ + +#include "lzo.h" +#include "common/textconsole.h" + +namespace Tony { + +namespace MPAL { + +#define pd(a, b) ((uint32) ((a) - (b))) + +#define TEST_IP (ip < ip_end) +#define TEST_OP 1 +#define NEED_IP(x) ((void) 0) +#define NEED_OP(x) ((void) 0) +#define TEST_LB(m_pos) ((void) 0) + +#define M2_MAX_OFFSET 0x0800 +#define LZO1X + +/** + * Decompresses an LZO compressed resource + */ +int lzo1x_decompress(const byte *in, uint32 in_len, byte *out, uint32 *out_len) { + register byte *op; + register const byte *ip; + register uint32 t = 0; +#if defined(COPY_DICT) + uint32 m_off; + const byte *dict_end; +#else + register const byte *m_pos; +#endif + + const byte * const ip_end = in + in_len; +#if defined(HAVE_ANY_OP) + byte * const op_end = out + *out_len; +#endif +#if defined(LZO1Z) + uint32 last_m_off = 0; +#endif + +#if defined(COPY_DICT) + if (dict) + { + if (dict_len > M4_MAX_OFFSET) + { + dict += dict_len - M4_MAX_OFFSET; + dict_len = M4_MAX_OFFSET; + } + dict_end = dict + dict_len; + } + else + { + dict_len = 0; + dict_end = NULL; + } +#endif + + *out_len = 0; + + op = out; + ip = in; + + if (*ip > 17) + { + t = *ip++ - 17; + if (t < 4) + goto match_next; + assert(t > 0); NEED_OP(t); NEED_IP(t+1); + do *op++ = *ip++; while (--t > 0); + goto first_literal_run; + } + + while (TEST_IP && TEST_OP) + { + t = *ip++; + if (t >= 16) + goto match; + if (t == 0) + { + NEED_IP(1); + while (*ip == 0) + { + t += 255; + ip++; + NEED_IP(1); + } + t += 15 + *ip++; + } + assert(t > 0); NEED_OP(t+3); NEED_IP(t+4); +#if defined(LZO_UNALIGNED_OK_8) && defined(LZO_UNALIGNED_OK_4) + t += 3; + if (t >= 8) do + { + UA_COPY64(op, ip); + op += 8; ip += 8; t -= 8; + } while (t >= 8); + if (t >= 4) + { + UA_COPY32(op, ip); + op += 4; ip += 4; t -= 4; + } + if (t > 0) + { + *op++ = *ip++; + if (t > 1) { *op++ = *ip++; if (t > 2) { *op++ = *ip++; } } + } +#elif defined(LZO_UNALIGNED_OK_4) || defined(LZO_ALIGNED_OK_4) +#if !defined(LZO_UNALIGNED_OK_4) + if (PTR_ALIGNED2_4(op, ip)) + { +#endif + UA_COPY32(op, ip); + op += 4; ip += 4; + if (--t > 0) + { + if (t >= 4) + { + do { + UA_COPY32(op, ip); + op += 4; ip += 4; t -= 4; + } while (t >= 4); + if (t > 0) do *op++ = *ip++; while (--t > 0); + } + else + do *op++ = *ip++; while (--t > 0); + } +#if !defined(LZO_UNALIGNED_OK_4) + } + else +#endif +#endif +#if !defined(LZO_UNALIGNED_OK_4) && !defined(LZO_UNALIGNED_OK_8) + { + *op++ = *ip++; *op++ = *ip++; *op++ = *ip++; + do *op++ = *ip++; while (--t > 0); + } +#endif + +first_literal_run: + + t = *ip++; + if (t >= 16) + goto match; +#if defined(COPY_DICT) +#if defined(LZO1Z) + m_off = (1 + M2_MAX_OFFSET) + (t << 6) + (*ip++ >> 2); + last_m_off = m_off; +#else + m_off = (1 + M2_MAX_OFFSET) + (t >> 2) + (*ip++ << 2); +#endif + NEED_OP(3); + t = 3; COPY_DICT(t, m_off) +#else +#if defined(LZO1Z) + t = (1 + M2_MAX_OFFSET) + (t << 6) + (*ip++ >> 2); + m_pos = op - t; + last_m_off = t; +#else + m_pos = op - (1 + M2_MAX_OFFSET); + m_pos -= t >> 2; + m_pos -= *ip++ << 2; +#endif + TEST_LB(m_pos); NEED_OP(3); + *op++ = *m_pos++; *op++ = *m_pos++; *op++ = *m_pos; +#endif + goto match_done; + + do { +match: + if (t >= 64) + { +#if defined(COPY_DICT) +#if defined(LZO1X) + m_off = 1 + ((t >> 2) & 7) + (*ip++ << 3); + t = (t >> 5) - 1; +#elif defined(LZO1Y) + m_off = 1 + ((t >> 2) & 3) + (*ip++ << 2); + t = (t >> 4) - 3; +#elif defined(LZO1Z) + m_off = t & 0x1f; + if (m_off >= 0x1c) + m_off = last_m_off; + else + { + m_off = 1 + (m_off << 6) + (*ip++ >> 2); + last_m_off = m_off; + } + t = (t >> 5) - 1; +#endif +#else +#if defined(LZO1X) + m_pos = op - 1; + m_pos -= (t >> 2) & 7; + m_pos -= *ip++ << 3; + t = (t >> 5) - 1; +#elif defined(LZO1Y) + m_pos = op - 1; + m_pos -= (t >> 2) & 3; + m_pos -= *ip++ << 2; + t = (t >> 4) - 3; +#elif defined(LZO1Z) + { + uint32 off = t & 0x1f; + m_pos = op; + if (off >= 0x1c) + { + assert(last_m_off > 0); + m_pos -= last_m_off; + } + else + { + off = 1 + (off << 6) + (*ip++ >> 2); + m_pos -= off; + last_m_off = off; + } + } + t = (t >> 5) - 1; +#endif + TEST_LB(m_pos); assert(t > 0); NEED_OP(t+3-1); + goto copy_match; +#endif + } + else if (t >= 32) + { + t &= 31; + if (t == 0) + { + NEED_IP(1); + while (*ip == 0) + { + t += 255; + ip++; + NEED_IP(1); + } + t += 31 + *ip++; + } +#if defined(COPY_DICT) +#if defined(LZO1Z) + m_off = 1 + (ip[0] << 6) + (ip[1] >> 2); + last_m_off = m_off; +#else + m_off = 1 + (ip[0] >> 2) + (ip[1] << 6); +#endif +#else +#if defined(LZO1Z) + { + uint32 off = 1 + (ip[0] << 6) + (ip[1] >> 2); + m_pos = op - off; + last_m_off = off; + } +#elif defined(LZO_UNALIGNED_OK_2) && defined(LZO_ABI_LITTLE_ENDIAN) + m_pos = op - 1; + m_pos -= UA_GET16(ip) >> 2; +#else + m_pos = op - 1; + m_pos -= (ip[0] >> 2) + (ip[1] << 6); +#endif +#endif + ip += 2; + } + else if (t >= 16) + { +#if defined(COPY_DICT) + m_off = (t & 8) << 11; +#else + m_pos = op; + m_pos -= (t & 8) << 11; +#endif + t &= 7; + if (t == 0) + { + NEED_IP(1); + while (*ip == 0) + { + t += 255; + ip++; + NEED_IP(1); + } + t += 7 + *ip++; + } +#if defined(COPY_DICT) +#if defined(LZO1Z) + m_off += (ip[0] << 6) + (ip[1] >> 2); +#else + m_off += (ip[0] >> 2) + (ip[1] << 6); +#endif + ip += 2; + if (m_off == 0) + goto eof_found; + m_off += 0x4000; +#if defined(LZO1Z) + last_m_off = m_off; +#endif +#else +#if defined(LZO1Z) + m_pos -= (ip[0] << 6) + (ip[1] >> 2); +#elif defined(LZO_UNALIGNED_OK_2) && defined(LZO_ABI_LITTLE_ENDIAN) + m_pos -= UA_GET16(ip) >> 2; +#else + m_pos -= (ip[0] >> 2) + (ip[1] << 6); +#endif + ip += 2; + if (m_pos == op) + goto eof_found; + m_pos -= 0x4000; +#if defined(LZO1Z) + last_m_off = pd((const byte *)op, m_pos); +#endif +#endif + } + else + { +#if defined(COPY_DICT) +#if defined(LZO1Z) + m_off = 1 + (t << 6) + (*ip++ >> 2); + last_m_off = m_off; +#else + m_off = 1 + (t >> 2) + (*ip++ << 2); +#endif + NEED_OP(2); + t = 2; COPY_DICT(t, m_off) +#else +#if defined(LZO1Z) + t = 1 + (t << 6) + (*ip++ >> 2); + m_pos = op - t; + last_m_off = t; +#else + m_pos = op - 1; + m_pos -= t >> 2; + m_pos -= *ip++ << 2; +#endif + TEST_LB(m_pos); NEED_OP(2); + *op++ = *m_pos++; *op++ = *m_pos; +#endif + goto match_done; + } + +#if defined(COPY_DICT) + + NEED_OP(t+3-1); + t += 3-1; COPY_DICT(t, m_off) + +#else + + TEST_LB(m_pos); assert(t > 0); NEED_OP(t+3-1); +#if defined(LZO_UNALIGNED_OK_8) && defined(LZO_UNALIGNED_OK_4) + if (op - m_pos >= 8) + { + t += (3 - 1); + if (t >= 8) do + { + UA_COPY64(op, m_pos); + op += 8; m_pos += 8; t -= 8; + } while (t >= 8); + if (t >= 4) + { + UA_COPY32(op, m_pos); + op += 4; m_pos += 4; t -= 4; + } + if (t > 0) + { + *op++ = m_pos[0]; + if (t > 1) { *op++ = m_pos[1]; if (t > 2) { *op++ = m_pos[2]; } } + } + } + else +#elif defined(LZO_UNALIGNED_OK_4) || defined(LZO_ALIGNED_OK_4) +#if !defined(LZO_UNALIGNED_OK_4) + if (t >= 2 * 4 - (3 - 1) && PTR_ALIGNED2_4(op, m_pos)) + { + assert((op - m_pos) >= 4); +#else + if (t >= 2 * 4 - (3 - 1) && (op - m_pos) >= 4) + { +#endif + UA_COPY32(op, m_pos); + op += 4; m_pos += 4; t -= 4 - (3 - 1); + do { + UA_COPY32(op, m_pos); + op += 4; m_pos += 4; t -= 4; + } while (t >= 4); + if (t > 0) do *op++ = *m_pos++; while (--t > 0); + } + else +#endif + { +copy_match: + *op++ = *m_pos++; *op++ = *m_pos++; + do *op++ = *m_pos++; while (--t > 0); + } + +#endif + +match_done: +#if defined(LZO1Z) + t = ip[-1] & 3; +#else + t = ip[-2] & 3; +#endif + if (t == 0) + break; + +match_next: + assert(t > 0); assert(t < 4); NEED_OP(t); NEED_IP(t+1); +#if 0 + do *op++ = *ip++; while (--t > 0); +#else + *op++ = *ip++; + if (t > 1) { *op++ = *ip++; if (t > 2) { *op++ = *ip++; } } +#endif + t = *ip++; + } while (TEST_IP && TEST_OP); + } + +#if defined(HAVE_TEST_IP) || defined(HAVE_TEST_OP) + *out_len = pd(op, out); + return LZO_E_EOF_NOT_FOUND; +#endif + +eof_found: + assert(t == 1); + *out_len = pd(op, out); + return (ip == ip_end ? LZO_E_OK : + (ip < ip_end ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN)); + +#if defined(HAVE_NEED_IP) +input_overrun: + *out_len = pd(op, out); + return LZO_E_INPUT_OVERRUN; +#endif + +#if defined(HAVE_NEED_OP) +output_overrun: + *out_len = pd(op, out); + return LZO_E_OUTPUT_OVERRUN; +#endif + +#if defined(LZO_TEST_OVERRUN_LOOKBEHIND) +lookbehind_overrun: + *out_len = pd(op, out); + return LZO_E_LOOKBEHIND_OVERRUN; +#endif +} + +} // end of namespace MPAL + +} // end of namespace Tony diff --git a/engines/tony/mpal/lzo.h b/engines/tony/mpal/lzo.h new file mode 100644 index 0000000000..ebb1c4b516 --- /dev/null +++ b/engines/tony/mpal/lzo.h @@ -0,0 +1,111 @@ +/* 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. + * + * + */ +/* minilzo.c -- mini subset of the LZO real-time data compression library + + This file is part of the LZO real-time data compression library. + + Copyright (C) 2011 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2010 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2009 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2008 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2007 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2006 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2005 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2004 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2003 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2002 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2001 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1999 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1998 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1997 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer + All Rights Reserved. + + The LZO library 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. + + The LZO library 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 the LZO library; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + Markus F.X.J. Oberhumer + <markus@oberhumer.com> + http://www.oberhumer.com/opensource/lzo/ + */ + +#ifndef TONY_MPAL_LZO_H +#define TONY_MPAL_LZO_H + +#include "common/scummsys.h" + +namespace Tony { + +namespace MPAL { + +/* Error codes for the compression/decompression functions. Negative + * values are errors, positive values will be used for special but + * normal events. + */ +#define LZO_E_OK 0 +#define LZO_E_ERROR (-1) +#define LZO_E_OUT_OF_MEMORY (-2) /* [lzo_alloc_func_t failure] */ +#define LZO_E_NOT_COMPRESSIBLE (-3) /* [not used right now] */ +#define LZO_E_INPUT_OVERRUN (-4) +#define LZO_E_OUTPUT_OVERRUN (-5) +#define LZO_E_LOOKBEHIND_OVERRUN (-6) +#define LZO_E_EOF_NOT_FOUND (-7) +#define LZO_E_INPUT_NOT_CONSUMED (-8) +#define LZO_E_NOT_YET_IMPLEMENTED (-9) /* [not used right now] */ +#define LZO_E_INVALID_ARGUMENT (-10) + +#define LZO1X_999_MEM_COMPRESS ((uint32) (14 * 16384L * sizeof(uint16))) + +/** + * Decompresses an LZO compressed resource + */ +int lzo1x_decompress(const byte *src, uint32 src_len, byte *dst, uint32 *dst_len); + +/** + * Comrpess a data block into an LZO stream + */ +int lzo1x_1_compress(const byte *src, uint32 src_len, byte *dst, uint32 *dst_len, void *wrkmem); + +/** + * better compression ratio at the cost of more memory and time + */ +int lzo1x_999_compress(const byte *src, uint32 src_len, byte *dst, uint32 *dst_len, void *wrkmem); + +} // end of namespace MPAL + +} // end of namespace Tony + +#endif /* already included */ diff --git a/engines/tony/mpal/memory.cpp b/engines/tony/mpal/memory.cpp new file mode 100644 index 0000000000..9737fe0abf --- /dev/null +++ b/engines/tony/mpal/memory.cpp @@ -0,0 +1,126 @@ +/* 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 "common/algorithm.h" +#include "common/textconsole.h" +#include "tony/mpal/memory.h" + +namespace Tony { + +namespace MPAL { + +/****************************************************************************\ +* MemoryManager methods +\****************************************************************************/ + +/** + * Allocates a new memory block + * @return Returns a MemoryItem instance for the new block + */ +MpalHandle MemoryManager::allocate(uint32 size, uint flags) { + MemoryItem *newItem = (MemoryItem *)malloc(sizeof(MemoryItem) + size); + newItem->_id = BLOCK_ID; + newItem->_size = size; + newItem->_lockCount = 0; + + // If requested, clear the allocated data block + if ((flags & GMEM_ZEROINIT) != 0) { + byte *dataP = newItem->_data; + Common::fill(dataP, dataP + size, 0); + } + + return (MpalHandle)newItem; +} + +/** + * Allocates a new memory block and returns its data pointer + * @return Data pointer to allocated block + */ +void *MemoryManager::alloc(uint32 size, uint flags) { + MemoryItem *item = (MemoryItem *)allocate(size, flags); + ++item->_lockCount; + return &item->_data[0]; +} + +#define OFFSETOF(type, field) ((size_t) &(((type *) 0)->field)) + +/** + * Returns a reference to the MemoryItem for a gien byte pointer + * @param block Byte pointer + */ +MemoryItem *MemoryManager::getItem(MpalHandle handle) { + MemoryItem *rec = (MemoryItem *)((byte *)handle - OFFSETOF(MemoryItem, _data)); + assert(rec->_id == BLOCK_ID); + return rec; +} + +/** + * Returns a size of a memory block given its pointer + */ +uint32 MemoryManager::getSize(MpalHandle handle) { + MemoryItem *item = (MemoryItem *)handle; + assert(item->_id == BLOCK_ID); + return item->_size; +} + +/** + * Erases a given item + */ +void MemoryManager::freeBlock(MpalHandle handle) { + MemoryItem *item = (MemoryItem *)handle; + assert(item->_id == BLOCK_ID); + free(item); +} + +/** + * Erases a given item + */ +void MemoryManager::destroyItem(MpalHandle handle) { + MemoryItem *item = getItem(handle); + assert(item->_id == BLOCK_ID); + free(item); +} + +/** + * Locks an item for access + */ +byte *MemoryManager::lockItem(MpalHandle handle) { + MemoryItem *item = (MemoryItem *)handle; + assert(item->_id == BLOCK_ID); + ++item->_lockCount; + return &item->_data[0]; +} + +/** + * Unlocks a locked item + */ +void MemoryManager::unlockItem(MpalHandle handle) { + MemoryItem *item = (MemoryItem *)handle; + assert(item->_id == BLOCK_ID); + assert(item->_lockCount > 0); + --item->_lockCount; +} + +} // end of namespace MPAL + +} // end of namespace Tony diff --git a/engines/tony/mpal/memory.h b/engines/tony/mpal/memory.h new file mode 100644 index 0000000000..9c21cc20e6 --- /dev/null +++ b/engines/tony/mpal/memory.h @@ -0,0 +1,78 @@ +/* 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 TONY_MPAL_MEMORY +#define TONY_MPAL_MEMORY + +#include "common/scummsys.h" +#include "common/list.h" + +namespace Tony { + +namespace MPAL { + +typedef void *MpalHandle; + +struct MemoryItem { + uint32 _id; + uint32 _size; + int _lockCount; + byte _data[1]; + + // Casting for access to data + operator void *() { return &_data[0]; } +}; + +class MemoryManager { +private: + static MemoryItem *getItem(MpalHandle handle); +public: + static MpalHandle allocate(uint32 size, uint flags); + static void *alloc(uint32 size, uint flags); + static void freeBlock(MpalHandle handle); + static void destroyItem(MpalHandle handle); + static uint32 getSize(MpalHandle handle); + static byte *lockItem(MpalHandle handle); + static void unlockItem(MpalHandle handle); +}; + +// defines +#define globalAlloc(flags, size) MemoryManager::alloc(size, flags) +#define globalAllocate(flags, size) MemoryManager::allocate(size, flags) +#define globalFree(handle) MemoryManager::freeBlock(handle) +#define globalDestroy(handle) MemoryManager::destroyItem(handle) +#define globalLock(handle) MemoryManager::lockItem(handle) +#define globalUnlock(handle) MemoryManager::unlockItem(handle) +#define globalSize(handle) MemoryManager::getSize(handle) + +#define GMEM_FIXED 1 +#define GMEM_MOVEABLE 2 +#define GMEM_ZEROINIT 4 + +const uint32 BLOCK_ID = 0x12345678; + +} // end of namespace MPAL + +} // end of namespace Tony + +#endif diff --git a/engines/tony/mpal/mpal.cpp b/engines/tony/mpal/mpal.cpp new file mode 100644 index 0000000000..1a24c5a576 --- /dev/null +++ b/engines/tony/mpal/mpal.cpp @@ -0,0 +1,2068 @@ +/* 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. + * + * + */ +/* + * This code is based on original Tony Tough source code + * + * Copyright (c) 1997-2003 Nayma Software + */ + +#include "common/scummsys.h" +#include "common/file.h" +#include "common/savefile.h" +#include "common/system.h" +#include "tony/tony.h" +#include "tony/mpal/lzo.h" +#include "tony/mpal/mpal.h" +#include "tony/mpal/mpaldll.h" + +namespace Tony { + +namespace MPAL { + +/****************************************************************************\ +* Internal functions +\****************************************************************************/ + +/** + * Locks the variables for access + */ +void lockVar() { + GLOBALS._lpmvVars = (LpMpalVar)globalLock(GLOBALS._hVars); +} + +/** + * Unlocks variables after use + */ +void unlockVar() { + globalUnlock(GLOBALS._hVars); +} + +/** + * Locks the messages for access + */ +static void LockMsg() { +#ifdef NEED_LOCK_MSGS + GLOBALS._lpmmMsgs = (LpMpalMsg)globalLock(GLOBALS._hMsgs); +#endif +} + +/** + * Unlocks the messages after use + */ +static void UnlockMsg() { +#ifdef NEED_LOCK_MSGS + globalUnlock(GLOBALS._hMsgs); +#endif +} + +/** + * Locks the dialogs for access + */ +static void lockDialogs() { + GLOBALS._lpmdDialogs = (LpMpalDialog)globalLock(GLOBALS._hDialogs); +} + +/** + * Unlocks the dialogs after use + */ +static void unlockDialogs() { + globalUnlock(GLOBALS._hDialogs); +} + +/** + * Locks the location data structures for access + */ +static void lockLocations() { + GLOBALS._lpmlLocations = (LpMpalLocation)globalLock(GLOBALS._hLocations); +} + +/** + * Unlocks the location structures after use + */ +static void unlockLocations() { + globalUnlock(GLOBALS._hLocations); +} + +/** + * Locks the items structures for use + */ +static void lockItems() { + GLOBALS._lpmiItems = (LpMpalItem)globalLock(GLOBALS._hItems); +} + +/** + * Unlocks the items structures after use + */ +static void unlockItems() { + globalUnlock(GLOBALS._hItems); +} + +/** + * Locks the script data structures for use + */ +static void LockScripts() { + GLOBALS._lpmsScripts = (LpMpalScript)globalLock(GLOBALS._hScripts); +} + +/** + * Unlocks the script data structures after use + */ +static void unlockScripts() { + globalUnlock(GLOBALS._hScripts); +} + +/** + * Returns the current value of a global variable + * + * @param lpszVarName Name of the variable + * @returns Current value + * @remarks Before using this method, you must call lockVar() to + * lock the global variablves for use. Then afterwards, you will + * need to remember to call UnlockVar() + */ +int32 varGetValue(const char *lpszVarName) { + LpMpalVar v = GLOBALS._lpmvVars; + + for (int i = 0; i < GLOBALS._nVars; v++, i++) + if (strcmp(lpszVarName, v->_lpszVarName) == 0) + return v->_dwVal; + + GLOBALS._mpalError = 1; + return 0; +} + +/** + * Sets the value of a MPAL global variable + * @param lpszVarName Name of the variable + * @param val Value to set + */ +void varSetValue(const char *lpszVarName, int32 val) { + LpMpalVar v = GLOBALS._lpmvVars; + + for (uint i = 0; i < GLOBALS._nVars; v++, i++) + if (strcmp(lpszVarName, v->_lpszVarName) == 0) { + v->_dwVal = val; + if (GLOBALS._lpiifCustom != NULL && strncmp(v->_lpszVarName, "Pattern.", 8) == 0) { + i = 0; + sscanf(v->_lpszVarName, "Pattern.%u", &i); + GLOBALS._lpiifCustom(i, val, -1); + } else if (GLOBALS._lpiifCustom != NULL && strncmp(v->_lpszVarName, "Status.", 7) == 0) { + i = 0; + sscanf(v->_lpszVarName,"Status.%u", &i); + GLOBALS._lpiifCustom(i, -1, val); + } + return; + } + + GLOBALS._mpalError = 1; + return; +} + +/** + * Find the index of a location within the location array. Remember to call LockLoc() beforehand. + * + * @param nLoc Location number to search for + * @returns Index, or -1 if the location is not present + * @remarks This function requires the location list to have + * first been locked with a call to LockLoc(). + */ +static int locGetOrderFromNum(uint32 nLoc) { + LpMpalLocation loc = GLOBALS._lpmlLocations; + + for (int i = 0; i < GLOBALS._nLocations; i++, loc++) + if (loc->_nObj == nLoc) + return i; + + return -1; +} + +/** + * Find the index of a message within the messages array + * @param nMsg Message number to search for + * @returns Index, or -1 if the message is not present + * @remarks This function requires the message list to have + * first been locked with a call to LockMsg() + */ +static int msgGetOrderFromNum(uint32 nMsg) { + LpMpalMsg msg = GLOBALS._lpmmMsgs; + + for (int i = 0; i < GLOBALS._nMsgs; i++, msg++) { + if (msg->_wNum == nMsg) + return i; + } + + return -1; +} + +/** + * Find the index of an item within the items array + * @param nItem Item number to search for + * @returns Index, or -1 if the item is not present + * @remarks This function requires the item list to have + * first been locked with a call to LockItems() + */ +static int itemGetOrderFromNum(uint32 nItem) { + LpMpalItem item = GLOBALS._lpmiItems; + + for (int i = 0; i < GLOBALS._nItems; i++, item++) { + if (item->_nObj == nItem) + return i; + } + + return -1; +} + +/** + * Find the index of a script within the scripts array + * @param nScript Script number to search for + * @returns Index, or -1 if the script is not present + * @remarks This function requires the script list to have + * first been locked with a call to LockScripts() + */ +static int scriptGetOrderFromNum(uint32 nScript) { + LpMpalScript script = GLOBALS._lpmsScripts; + + for (int i = 0; i < GLOBALS._nScripts; i++, script++) { + if (script->_nObj == nScript) + return i; + } + + return -1; +} + +/** + * Find the index of a dialog within the dialogs array + * @param nDialog Dialog number to search for + * @returns Index, or -1 if the dialog is not present + * @remarks This function requires the dialog list to have + * first been locked with a call to LockDialogs() + */ +static int dialogGetOrderFromNum(uint32 nDialog) { + LpMpalDialog dialog = GLOBALS._lpmdDialogs; + + for (int i = 0; i < GLOBALS._nDialogs; i++, dialog++) { + if (dialog->_nObj == nDialog) + return i; + } + + return -1; +} + +/** + * Duplicates a message + * @param nMsgOrd Index of the message inside the messages array + * @returns Pointer to the duplicated message. + * @remarks Remember to free the duplicated message when done with it. + */ +static char *DuplicateMessage(uint32 nMsgOrd) { + const char *origmsg; + char *clonemsg; + + if (nMsgOrd == (uint32)-1) + return NULL; + + origmsg = (const char *)globalLock(GLOBALS._lpmmMsgs[nMsgOrd]._hText); + + int j = 0; + while (origmsg[j] != '\0' || origmsg[j + 1] != '\0') + j++; + j += 2; + + clonemsg = (char *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, j); + if (clonemsg == NULL) + return NULL; + + memcpy(clonemsg, origmsg, j); + globalUnlock(GLOBALS._lpmmMsgs[nMsgOrd]._hText); + + return clonemsg; +} + +/** + * Duplicate a sentence of a dialog + * @param nDlgOrd Index of the dialog in the dialogs array + * @param nPeriod Sentence number to be duplicated. + * @returns Pointer to the duplicated phrase. Remember to free it + * when done with it. + */ +static char *duplicateDialogPeriod(uint32 nPeriod) { + const char *origmsg; + char *clonemsg; + LpMpalDialog dialog = GLOBALS._lpmdDialogs + GLOBALS._nExecutingDialog; + + for (int j = 0; dialog->_periods[j] != NULL; j++) { + if (dialog->_periodNums[j] == nPeriod) { + // Found the phrase, it should be duplicated + origmsg = (const char *)globalLock(dialog->_periods[j]); + + // Calculate the length and allocate memory + int i = 0; + while (origmsg[i] != '\0') + i++; + + clonemsg = (char *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, i + 1); + if (clonemsg == NULL) + return NULL; + + memcpy(clonemsg, origmsg, i); + + globalUnlock(dialog->_periods[j]); + + return clonemsg; + } + } + + return NULL; +} + +/** + * Load a resource from the MPR file + * + * @param dwId ID of the resource to load + * @returns Handle to the loaded resource + */ +MpalHandle resLoad(uint32 dwId) { + MpalHandle h; + char head[4]; + byte *temp, *buf; + + for (int i = 0; i < GLOBALS._nResources; i++) + if (GLOBALS._lpResources[i * 2] == dwId) { + GLOBALS._hMpr.seek(GLOBALS._lpResources[i * 2 + 1]); + uint32 nBytesRead = GLOBALS._hMpr.read(head, 4); + if (nBytesRead != 4) + return NULL; + if (head[0] != 'R' || head[1] != 'E' || head[2] != 'S' || head[3] != 'D') + return NULL; + + uint32 nSizeDecomp = GLOBALS._hMpr.readUint32LE(); + if (GLOBALS._hMpr.err()) + return NULL; + + uint32 nSizeComp = GLOBALS._hMpr.readUint32LE(); + if (GLOBALS._hMpr.err()) + return NULL; + + h = globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, nSizeDecomp + (nSizeDecomp / 1024) * 16); + buf = (byte *)globalLock(h); + temp = (byte *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, nSizeComp); + + nBytesRead = GLOBALS._hMpr.read(temp, nSizeComp); + if (nBytesRead != nSizeComp) + return NULL; + + lzo1x_decompress(temp, nSizeComp, buf, &nBytesRead); + if (nBytesRead != nSizeDecomp) + return NULL; + + globalDestroy(temp); + globalUnlock(h); + return h; + } + + return NULL; +} + +static uint32 *getSelectList(uint32 i) { + uint32 *sl; + LpMpalDialog dialog = GLOBALS._lpmdDialogs + GLOBALS._nExecutingDialog; + + // Count how many are active selects + int num = 0; + for (int j = 0; dialog->_choice[i]._select[j]._dwData != 0; j++) { + if (dialog->_choice[i]._select[j]._curActive) + num++; + } + + // If there are 0, it's a mistake + if (num == 0) + return NULL; + + sl = (uint32 *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(uint32) * (num + 1)); + if (sl == NULL) + return NULL; + + // Copy all the data inside the active select list + int k = 0; + for (int j = 0; dialog->_choice[i]._select[j]._dwData != 0; j++) { + if (dialog->_choice[i]._select[j]._curActive) + sl[k++] = dialog->_choice[i]._select[j]._dwData; + } + + sl[k] = (uint32)NULL; + return sl; +} + +static uint32 *GetItemList(uint32 nLoc) { + uint32 *il; + LpMpalVar v = GLOBALS._lpmvVars; + + uint32 num = 0; + for (uint32 i = 0; i < GLOBALS._nVars; i++, v++) { + if (strncmp(v->_lpszVarName, "Location", 8) == 0 && v->_dwVal == nLoc) + num++; + } + + il = (uint32 *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(uint32) * (num + 1)); + if (il == NULL) + return NULL; + + v = GLOBALS._lpmvVars; + uint32 j = 0; + for (uint32 i = 0; i < GLOBALS._nVars; i++, v++) { + if (strncmp(v->_lpszVarName, "Location", 8) == 0 && v->_dwVal == nLoc) { + sscanf(v->_lpszVarName, "Location.%u", &il[j]); + j++; + } + } + + il[j] = (uint32)NULL; + return il; +} + +static LpItem getItemData(uint32 nOrdItem) { + LpMpalItem curitem = GLOBALS._lpmiItems + nOrdItem; + char *dat; + char *patlength; + + // Zeroing out the allocated memory is required!!! + LpItem ret = (LpItem)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(Item)); + if (ret == NULL) + return NULL; + ret->_speed = 150; + + MpalHandle hDat = resLoad(curitem->_dwRes); + dat = (char *)globalLock(hDat); + + if (dat[0] == 'D' && dat[1] == 'A' && dat[2] == 'T') { + int i = dat[3]; // For version 1.0!! + dat += 4; + + if (i >= 0x10) { // From 1.0, there's a destination point for each object + ret->_destX = (int16)READ_LE_UINT16(dat); + ret->_destY = (int16)READ_LE_UINT16(dat + 2); + dat += 4; + } + + if (i >= 0x11) { // From 1.1, there's animation speed + ret->_speed = READ_LE_UINT16(dat); + dat += 2; + } else + ret->_speed = 150; + } + + ret->_numframe = *dat++; + ret->_numpattern = *dat++; + ret->_destZ = *dat++; + + // Upload the left & top co-ordinates of each frame + for (int i = 0; i < ret->_numframe; i++) { + ret->_frameslocations[i].left = (int16)READ_LE_UINT16(dat); + ret->_frameslocations[i].top = (int16)READ_LE_UINT16(dat + 2); + dat += 4; + } + + // Upload the size of each frame and calculate the right & bottom + for (int i = 0; i < ret->_numframe; i++) { + ret->_frameslocations[i].right = (int16)READ_LE_UINT16(dat) + ret->_frameslocations[i].left; + ret->_frameslocations[i].bottom = (int16)READ_LE_UINT16(dat + 2) + ret->_frameslocations[i].top; + dat += 4; + } + + // Upload the bounding boxes of each frame + for (int i = 0; i < ret->_numframe; i++) { + ret->_bbox[i].left = (int16)READ_LE_UINT16(dat); + ret->_bbox[i].top = (int16)READ_LE_UINT16(dat + 2); + ret->_bbox[i].right = (int16)READ_LE_UINT16(dat + 4); + ret->_bbox[i].bottom = (int16)READ_LE_UINT16(dat + 6); + dat += 8; + } + + // Load the animation pattern + patlength = dat; + dat += ret->_numpattern; + + for (int i = 1; i < ret->_numpattern; i++) { + for (int j = 0; j < patlength[i]; j++) + ret->_pattern[i][j] = dat[j]; + ret->_pattern[i][(int)patlength[i]] = 255; // Terminate pattern + dat += patlength[i]; + } + + // Upload the individual frames of animations + for (int i = 1; i < ret->_numframe; i++) { + uint32 dim = (uint32)(ret->_frameslocations[i].right - ret->_frameslocations[i].left) * + (uint32)(ret->_frameslocations[i].bottom - ret->_frameslocations[i].top); + ret->_frames[i] = (char *)globalAlloc(GMEM_FIXED, dim); + + if (ret->_frames[i] == NULL) + return NULL; + memcpy(ret->_frames[i], dat, dim); + dat += dim; + } + + // Check if we've got to the end of the file + int i = READ_LE_UINT16(dat); + if (i != 0xABCD) + return NULL; + + globalUnlock(hDat); + globalFree(hDat); + + return ret; +} + +/** + * Thread that calls a custom function. It is used in scripts, so that each script + * function is executed without delaying the others. + * + * @param param pointer to a pointer to the structure that defines the call. + * @remarks The passed structure is freed when the process finishes. + */ +void CustomThread(CORO_PARAM, const void *param) { + CORO_BEGIN_CONTEXT; + LpCfCall p; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + _ctx->p = *(const LpCfCall *)param; + + CORO_INVOKE_4(GLOBALS._lplpFunctions[_ctx->p->_nCf], _ctx->p->_arg1, _ctx->p->_arg2, _ctx->p->_arg3, _ctx->p->_arg4); + + globalFree(_ctx->p); + + CORO_END_CODE; +} + +/** + * Main process for running a script. + * + * @param param Pointer to a pointer to a structure containing the script data. + * @remarks The passed structure is freed when the process finishes. + */ +void ScriptThread(CORO_PARAM, const void *param) { + CORO_BEGIN_CONTEXT; + uint i, j, k; + uint32 dwStartTime; + uint32 dwCurTime; + uint32 dwId; + int numHandles; + LpCfCall p; + CORO_END_CONTEXT(_ctx); + + static uint32 cfHandles[MAX_COMMANDS_PER_MOMENT]; + LpMpalScript s = *(const LpMpalScript *)param; + + CORO_BEGIN_CODE(_ctx); + + _ctx->dwStartTime = g_vm->getTime(); + _ctx->numHandles = 0; + + //debugC(DEBUG_BASIC, kTonyDebugMPAL, "PlayScript(): Moments: %u\n", s->_nMoments); + for (_ctx->i = 0; _ctx->i < s->_nMoments; _ctx->i++) { + // Sleep for the required time + if (s->_moment[_ctx->i]._dwTime == -1) { + CORO_INVOKE_4(CoroScheduler.waitForMultipleObjects, _ctx->numHandles, cfHandles, true, CORO_INFINITE); + _ctx->dwStartTime = g_vm->getTime(); + } else { + _ctx->dwCurTime = g_vm->getTime(); + if (_ctx->dwCurTime < _ctx->dwStartTime + (s->_moment[_ctx->i]._dwTime * 100)) { + //debugC(DEBUG_BASIC, kTonyDebugMPAL, "PlayScript(): Sleeping %lums\n",_ctx->dwStartTime + (s->_moment[_ctx->i]._dwTime*100) - _ctx->dwCurTime); + CORO_INVOKE_1(CoroScheduler.sleep, _ctx->dwStartTime + (s->_moment[_ctx->i]._dwTime * 100) - _ctx->dwCurTime); + } + } + + _ctx->numHandles = 0; + for (_ctx->j = 0; _ctx->j < s->_moment[_ctx->i]._nCmds; _ctx->j++) { + _ctx->k = s->_moment[_ctx->i]._cmdNum[_ctx->j]; + + if (s->_command[_ctx->k]._type == 1) { + _ctx->p = (LpCfCall)globalAlloc(GMEM_FIXED, sizeof(CfCall)); + if (_ctx->p == NULL) { + GLOBALS._mpalError = 1; + + CORO_KILL_SELF(); + return; + } + + _ctx->p->_nCf = s->_command[_ctx->k]._nCf; + _ctx->p->_arg1 = s->_command[_ctx->k]._arg1; + _ctx->p->_arg2 = s->_command[_ctx->k]._arg2; + _ctx->p->_arg3 = s->_command[_ctx->k]._arg3; + _ctx->p->_arg4 = s->_command[_ctx->k]._arg4; + + // !!! New process management + if ((cfHandles[_ctx->numHandles++] = CoroScheduler.createProcess(CustomThread, &_ctx->p, sizeof(LpCfCall))) == 0) { + GLOBALS._mpalError = 1; + + CORO_KILL_SELF(); + return; + } + } else if (s->_command[_ctx->k]._type == 2) { + lockVar(); + varSetValue( + s->_command[_ctx->k]._lpszVarName, + evaluateExpression(s->_command[_ctx->k]._expr) + ); + unlockVar(); + + } else { + GLOBALS._mpalError = 1; + globalFree(s); + + CORO_KILL_SELF(); + return; + } + + // WORKAROUND: Wait for events to pulse. + CORO_SLEEP(1); + } + } + + globalFree(s); + + CORO_KILL_SELF(); + + CORO_END_CODE; +} + +/** + * Thread that performs an action on an item. the thread always executes the action, + * so it should create a new item in which the action is the one required. + * Furthermore, the expression is not checked, but it is always performed the action. + * + * @param param Pointer to a pointer to a structure containing the action. + */ +void ActionThread(CORO_PARAM, const void *param) { + // COROUTINE + CORO_BEGIN_CONTEXT; + int j, k; + LpMpalItem item; + + ~CoroContextTag() { + if (item) + globalDestroy(item); + } + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + // The ActionThread owns the data block pointed to, so we need to make sure it's + // freed when the process exits + _ctx->item = *(const LpMpalItem *)param; + + GLOBALS._mpalError = 0; + for (_ctx->j = 0; _ctx->j < _ctx->item->_action[_ctx->item->_dwRes]._nCmds; _ctx->j++) { + _ctx->k = _ctx->item->_action[_ctx->item->_dwRes]._cmdNum[_ctx->j]; + + if (_ctx->item->_command[_ctx->k]._type == 1) { + // Custom function + debugC(DEBUG_DETAILED, kTonyDebugActions, "Action Process %d Call=%s params=%d,%d,%d,%d", + CoroScheduler.getCurrentPID(), GLOBALS._lplpFunctionStrings[_ctx->item->_command[_ctx->k]._nCf].c_str(), + _ctx->item->_command[_ctx->k]._arg1, _ctx->item->_command[_ctx->k]._arg2, + _ctx->item->_command[_ctx->k]._arg3, _ctx->item->_command[_ctx->k]._arg4 + ); + + CORO_INVOKE_4(GLOBALS._lplpFunctions[_ctx->item->_command[_ctx->k]._nCf], + _ctx->item->_command[_ctx->k]._arg1, + _ctx->item->_command[_ctx->k]._arg2, + _ctx->item->_command[_ctx->k]._arg3, + _ctx->item->_command[_ctx->k]._arg4 + + ); + } else if (_ctx->item->_command[_ctx->k]._type == 2) { + // Variable assign + debugC(DEBUG_DETAILED, kTonyDebugActions, "Action Process %d Variable=%s", + CoroScheduler.getCurrentPID(), _ctx->item->_command[_ctx->k]._lpszVarName); + + lockVar(); + varSetValue(_ctx->item->_command[_ctx->k]._lpszVarName, evaluateExpression(_ctx->item->_command[_ctx->k]._expr)); + unlockVar(); + + } else { + GLOBALS._mpalError = 1; + break; + } + + // WORKAROUND: Wait for events to pulse. + CORO_SLEEP(1); + } + + globalDestroy(_ctx->item); + _ctx->item = NULL; + + debugC(DEBUG_DETAILED, kTonyDebugActions, "Action Process %d ended", CoroScheduler.getCurrentPID()); + + CORO_END_CODE; +} + +/** + * This thread monitors a created action to detect when it ends. + * @remarks Since actions can spawn sub-actions, this needs to be a + * separate thread to determine when the outer action is done + */ +void ShutUpActionThread(CORO_PARAM, const void *param) { + // COROUTINE + CORO_BEGIN_CONTEXT; + int slotNumber; + CORO_END_CONTEXT(_ctx); + + uint32 pid = *(const uint32 *)param; + + CORO_BEGIN_CODE(_ctx); + + CORO_INVOKE_2(CoroScheduler.waitForSingleObject, pid, CORO_INFINITE); + + GLOBALS._bExecutingAction = false; + + if (g_vm->_initialLoadSlotNumber != -1) { + _ctx->slotNumber = g_vm->_initialLoadSlotNumber; + g_vm->_initialLoadSlotNumber = -1; + + CORO_INVOKE_1(g_vm->loadState, _ctx->slotNumber); + } + + CORO_END_CODE; +} + +/** + * Polls one location (starting point of a process) + * + * @param param Pointer to an index in the array of polling locations. + */ +void LocationPollThread(CORO_PARAM, const void *param) { + typedef struct { + uint32 _nItem, _nAction; + + uint16 _wTime; + byte _perc; + MpalHandle _when; + byte _nCmds; + uint16 _cmdNum[MAX_COMMANDS_PER_ACTION]; + uint32 _dwLastTime; + } MYACTION; + + typedef struct { + uint32 _nItem; + uint32 _hThread; + } MYTHREAD; + + CORO_BEGIN_CONTEXT; + uint32 *il; + int i, j, k; + int numitems; + int nRealItems; + LpMpalItem curItem, newItem; + int nIdleActions; + uint32 curTime; + uint32 dwSleepTime; + uint32 dwId; + int ord; + bool delayExpired; + bool expired; + + MYACTION *myActions; + MYTHREAD *myThreads; + + ~CoroContextTag() { + // Free data blocks + if (myThreads) + globalDestroy(myThreads); + if (myActions) + globalDestroy(myActions); + } + CORO_END_CONTEXT(_ctx); + + uint32 id = *((const uint32 *)param); + + CORO_BEGIN_CODE(_ctx); + + // Initialize data pointers + _ctx->myActions = NULL; + _ctx->myThreads = NULL; + + // To begin with, we need to request the item list from the location + _ctx->il = mpalQueryItemList(GLOBALS._nPollingLocations[id]); + + // Count the items + for (_ctx->numitems = 0; _ctx->il[_ctx->numitems] != 0; _ctx->numitems++) + ; + + // We look for items without idle actions, and eliminate them from the list + lockItems(); + _ctx->nIdleActions = 0; + _ctx->nRealItems = 0; + for (_ctx->i = 0; _ctx->i < _ctx->numitems; _ctx->i++) { + _ctx->ord = itemGetOrderFromNum(_ctx->il[_ctx->i]); + + if (_ctx->ord == -1) + continue; + + _ctx->curItem = GLOBALS._lpmiItems + _ctx->ord; + + _ctx->k = 0; + for (_ctx->j = 0; _ctx->j < _ctx->curItem->_nActions; _ctx->j++) { + if (_ctx->curItem->_action[_ctx->j]._num == 0xFF) + _ctx->k++; + } + + _ctx->nIdleActions += _ctx->k; + + if (_ctx->k == 0) + // We can remove this item from the list + _ctx->il[_ctx->i] = (uint32)NULL; + else + _ctx->nRealItems++; + } + unlockItems(); + + // If there is nothing left, we can exit + if (_ctx->nRealItems == 0) { + globalDestroy(_ctx->il); + CORO_KILL_SELF(); + return; + } + + _ctx->myThreads = (MYTHREAD *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, _ctx->nRealItems * sizeof(MYTHREAD)); + if (_ctx->myThreads == NULL) { + globalDestroy(_ctx->il); + CORO_KILL_SELF(); + return; + } + + // We have established that there is at least one item that contains idle actions. + // Now we created the mirrored copies of the idle actions. + _ctx->myActions = (MYACTION *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, _ctx->nIdleActions * sizeof(MYACTION)); + if (_ctx->myActions == NULL) { + globalDestroy(_ctx->myThreads); + globalDestroy(_ctx->il); + CORO_KILL_SELF(); + return; + } + + lockItems(); + _ctx->k = 0; + + for (_ctx->i = 0; _ctx->i < _ctx->numitems; _ctx->i++) { + if (_ctx->il[_ctx->i] == 0) + continue; + + _ctx->curItem = GLOBALS._lpmiItems + itemGetOrderFromNum(_ctx->il[_ctx->i]); + + for (_ctx->j = 0; _ctx->j < _ctx->curItem->_nActions; _ctx->j++) { + if (_ctx->curItem->_action[_ctx->j]._num == 0xFF) { + _ctx->myActions[_ctx->k]._nItem = _ctx->il[_ctx->i]; + _ctx->myActions[_ctx->k]._nAction = _ctx->j; + + _ctx->myActions[_ctx->k]._wTime = _ctx->curItem->_action[_ctx->j]._wTime; + _ctx->myActions[_ctx->k]._perc = _ctx->curItem->_action[_ctx->j]._perc; + _ctx->myActions[_ctx->k]._when = _ctx->curItem->_action[_ctx->j]._when; + _ctx->myActions[_ctx->k]._nCmds = _ctx->curItem->_action[_ctx->j]._nCmds; + memcpy(_ctx->myActions[_ctx->k]._cmdNum, _ctx->curItem->_action[_ctx->j]._cmdNum, + MAX_COMMANDS_PER_ACTION * sizeof(uint16)); + + _ctx->myActions[_ctx->k]._dwLastTime = g_vm->getTime(); + _ctx->k++; + } + } + } + + unlockItems(); + + // We don't need the item list anymore + globalDestroy(_ctx->il); + + // Here's the main loop + while (1) { + // Searching for idle actions requiring time to execute + _ctx->curTime = g_vm->getTime(); + _ctx->dwSleepTime = (uint32)-1L; + + for (_ctx->k = 0;_ctx->k<_ctx->nIdleActions;_ctx->k++) { + if (_ctx->curTime >= _ctx->myActions[_ctx->k]._dwLastTime + _ctx->myActions[_ctx->k]._wTime) { + _ctx->dwSleepTime = 0; + break; + } else + _ctx->dwSleepTime = MIN(_ctx->dwSleepTime, _ctx->myActions[_ctx->k]._dwLastTime + _ctx->myActions[_ctx->k]._wTime - _ctx->curTime); + } + + // We fall alseep, but always checking that the event is set when prompted for closure + CORO_INVOKE_3(CoroScheduler.waitForSingleObject, GLOBALS._hEndPollingLocations[id], _ctx->dwSleepTime, &_ctx->expired); + + //if (_ctx->k == WAIT_OBJECT_0) + if (!_ctx->expired) + break; + + for (_ctx->i = 0; _ctx->i < _ctx->nRealItems; _ctx->i++) { + if (_ctx->myThreads[_ctx->i]._nItem != 0) { + CORO_INVOKE_3(CoroScheduler.waitForSingleObject, _ctx->myThreads[_ctx->i]._hThread, 0, &_ctx->delayExpired); + + // if result == WAIT_OBJECT_0) + if (!_ctx->delayExpired) + _ctx->myThreads[_ctx->i]._nItem = 0; + } + } + + _ctx->curTime = g_vm->getTime(); + + // Loop through all the necessary idle actions + for (_ctx->k = 0; _ctx->k < _ctx->nIdleActions; _ctx->k++) { + if (_ctx->curTime >= _ctx->myActions[_ctx->k]._dwLastTime + _ctx->myActions[_ctx->k]._wTime) { + _ctx->myActions[_ctx->k]._dwLastTime += _ctx->myActions[_ctx->k]._wTime; + + // It's time to check to see if fortune is on the side of the idle action + byte randomVal = (byte)g_vm->_randomSource.getRandomNumber(99); + if (randomVal < _ctx->myActions[_ctx->k]._perc) { + // Check if there is an action running on the item + if ((GLOBALS._bExecutingAction) && (GLOBALS._nExecutingAction == _ctx->myActions[_ctx->k]._nItem)) + continue; + + // Check to see if there already another idle funning running on the item + for (_ctx->i = 0; _ctx->i < _ctx->nRealItems; _ctx->i++) { + if (_ctx->myThreads[_ctx->i]._nItem == _ctx->myActions[_ctx->k]._nItem) + break; + } + + if (_ctx->i < _ctx->nRealItems) + continue; + + // Ok, we are the only ones :) + lockItems(); + _ctx->curItem = GLOBALS._lpmiItems + itemGetOrderFromNum(_ctx->myActions[_ctx->k]._nItem); + + // Check if there is a WhenExecute expression + _ctx->j=_ctx->myActions[_ctx->k]._nAction; + if (_ctx->curItem->_action[_ctx->j]._when != NULL) { + if (!evaluateExpression(_ctx->curItem->_action[_ctx->j]._when)) { + unlockItems(); + continue; + } + } + + // Ok, we can perform the action. For convenience, we do it in a new process + _ctx->newItem = (LpMpalItem)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(MpalItem)); + if (_ctx->newItem == false) { + globalDestroy(_ctx->myThreads); + globalDestroy(_ctx->myActions); + + CORO_KILL_SELF(); + return; + } + + memcpy(_ctx->newItem,_ctx->curItem, sizeof(MpalItem)); + unlockItems(); + + // We copy the action in #0 + //_ctx->newItem->Action[0].nCmds = _ctx->curItem->Action[_ctx->j].nCmds; + //memcpy(_ctx->newItem->Action[0].CmdNum,_ctx->curItem->Action[_ctx->j].CmdNum,_ctx->newItem->Action[0].nCmds*sizeof(_ctx->newItem->Action[0].CmdNum[0])); + _ctx->newItem->_dwRes = _ctx->j; + + // We will create an action, and will provide the necessary details + for (_ctx->i = 0; _ctx->i < _ctx->nRealItems; _ctx->i++) { + if (_ctx->myThreads[_ctx->i]._nItem == 0) + break; + } + + _ctx->myThreads[_ctx->i]._nItem = _ctx->myActions[_ctx->k]._nItem; + + // Create the process + if ((_ctx->myThreads[_ctx->i]._hThread = CoroScheduler.createProcess(ActionThread, &_ctx->newItem, sizeof(LpMpalItem))) == CORO_INVALID_PID_VALUE) { + //if ((_ctx->myThreads[_ctx->i]._hThread = (void*)_beginthread(ActionThread, 10240, (void *)_ctx->newItem)) == (void*)-1) + globalDestroy(_ctx->newItem); + globalDestroy(_ctx->myThreads); + globalDestroy(_ctx->myActions); + + CORO_KILL_SELF(); + return; + } + + // Skip all idle actions of the same item + } + } + } + } + + // Set idle skip on + CORO_INVOKE_4(GLOBALS._lplpFunctions[200], 0, 0, 0, 0); + + for (_ctx->i = 0; _ctx->i < _ctx->nRealItems; _ctx->i++) { + if (_ctx->myThreads[_ctx->i]._nItem != 0) { + CORO_INVOKE_3(CoroScheduler.waitForSingleObject, _ctx->myThreads[_ctx->i]._hThread, 5000, &_ctx->delayExpired); + + //if (result != WAIT_OBJECT_0) + //if (_ctx->delayExpired) + // TerminateThread(_ctx->MyThreads[_ctx->i].hThread, 0); + + CoroScheduler.killMatchingProcess(_ctx->myThreads[_ctx->i]._hThread); + } + } + + // Set idle skip off + CORO_INVOKE_4(GLOBALS._lplpFunctions[201], 0, 0, 0, 0); + + CORO_END_CODE; +} + +/** + * Wait for the end of the dialog execution thread, and then restore global + * variables indicating that the dialogue has finished. + * + * @param param Pointer to a handle to the dialog + * @remarks This additional process is used, instead of clearing variables + * within the same dialog thread, because due to the recursive nature of a dialog, + * it would be difficult to know within it when the dialog is actually ending. + */ +void ShutUpDialogThread(CORO_PARAM, const void *param) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + uint32 pid = *(const uint32 *)param; + + CORO_BEGIN_CODE(_ctx); + + CORO_INVOKE_2(CoroScheduler.waitForSingleObject, pid, CORO_INFINITE); + + GLOBALS._bExecutingDialog = false; + GLOBALS._nExecutingDialog = 0; + GLOBALS._nExecutingChoice = 0; + + CoroScheduler.setEvent(GLOBALS._hAskChoice); + + CORO_KILL_SELF(); + + CORO_END_CODE; +} + +void doChoice(CORO_PARAM, uint32 nChoice); + +/** + * Executes a group of the current dialog. Can 'be the Starting point of a process. + * @parm nGroup Number of the group to perform + */ +void GroupThread(CORO_PARAM, const void *param) { + CORO_BEGIN_CONTEXT; + LpMpalDialog dialog; + int i, j, k; + int type; + CORO_END_CONTEXT(_ctx); + + uint32 nGroup = *(const uint32 *)param; + + CORO_BEGIN_CODE(_ctx); + + // Lock the _ctx->dialog + lockDialogs(); + + // Find the pointer to the current _ctx->dialog + _ctx->dialog = GLOBALS._lpmdDialogs + GLOBALS._nExecutingDialog; + + // Search inside the group requesting the _ctx->dialog + for (_ctx->i = 0; _ctx->dialog->_group[_ctx->i]._num != 0; _ctx->i++) { + if (_ctx->dialog->_group[_ctx->i]._num == nGroup) { + // Cycle through executing the commands of the group + for (_ctx->j = 0; _ctx->j < _ctx->dialog->_group[_ctx->i]._nCmds; _ctx->j++) { + _ctx->k = _ctx->dialog->_group[_ctx->i]._cmdNum[_ctx->j]; + + _ctx->type = _ctx->dialog->_command[_ctx->k]._type; + if (_ctx->type == 1) { + // Call custom function + CORO_INVOKE_4(GLOBALS._lplpFunctions[_ctx->dialog->_command[_ctx->k]._nCf], + _ctx->dialog->_command[_ctx->k]._arg1, + _ctx->dialog->_command[_ctx->k]._arg2, + _ctx->dialog->_command[_ctx->k]._arg3, + _ctx->dialog->_command[_ctx->k]._arg4 + ); + + } else if (_ctx->type == 2) { + // Set a variable + lockVar(); + varSetValue(_ctx->dialog->_command[_ctx->k]._lpszVarName, evaluateExpression(_ctx->dialog->_command[_ctx->k]._expr)); + unlockVar(); + + } else if (_ctx->type == 3) { + // DoChoice: call the chosen function + CORO_INVOKE_1(doChoice, (uint32)_ctx->dialog->_command[_ctx->k]._nChoice); + + } else { + GLOBALS._mpalError = 1; + unlockDialogs(); + + CORO_KILL_SELF(); + return; + } + + // WORKAROUND: Wait for events to pulse. + CORO_SLEEP(1); + } + + // The gruop is finished, so we can return to the calling function. + // If the group was the first called, then the process will automatically + // end. Otherwise it returns to the caller method + + return; + } + } + + // If we are here, it means that we have not found the requested group + GLOBALS._mpalError = 1; + unlockDialogs(); + + CORO_KILL_SELF(); + + CORO_END_CODE; +} + +/** + * Make a choice in the current dialog. + * + * @param nChoice Number of choice to perform + */ +void doChoice(CORO_PARAM, uint32 nChoice) { + CORO_BEGIN_CONTEXT; + LpMpalDialog dialog; + int i, j, k; + uint32 nGroup; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + // Lock the dialogs + lockDialogs(); + + // Get a pointer to the current dialog + _ctx->dialog = GLOBALS._lpmdDialogs + GLOBALS._nExecutingDialog; + + // Search the choice between those required in the dialog + for (_ctx->i = 0; _ctx->dialog->_choice[_ctx->i]._nChoice != 0; _ctx->i++) { + if (_ctx->dialog->_choice[_ctx->i]._nChoice == nChoice) + break; + } + + // If nothing has been found, exit with an error + if (_ctx->dialog->_choice[_ctx->i]._nChoice == 0) { + // If we're here, we did not find the required choice + GLOBALS._mpalError = 1; + unlockDialogs(); + + CORO_KILL_SELF(); + return; + } + + // We've found the requested choice. Remember what in global variables + GLOBALS._nExecutingChoice = _ctx->i; + + while (1) { + GLOBALS._nExecutingChoice = _ctx->i; + + _ctx->k = 0; + // Calculate the expression of each selection, to see if they're active or inactive + for (_ctx->j = 0; _ctx->dialog->_choice[_ctx->i]._select[_ctx->j]._dwData != 0; _ctx->j++) { + if (_ctx->dialog->_choice[_ctx->i]._select[_ctx->j]._when == NULL) { + _ctx->dialog->_choice[_ctx->i]._select[_ctx->j]._curActive = 1; + _ctx->k++; + } else if (evaluateExpression(_ctx->dialog->_choice[_ctx->i]._select[_ctx->j]._when)) { + _ctx->dialog->_choice[_ctx->i]._select[_ctx->j]._curActive = 1; + _ctx->k++; + } else + _ctx->dialog->_choice[_ctx->i]._select[_ctx->j]._curActive = 0; + } + + // If there are no choices activated, then the dialog is finished. + if (_ctx->k == 0) { + unlockDialogs(); + break; + } + + // There are choices available to the user, so wait for them to make one + CoroScheduler.resetEvent(GLOBALS._hDoneChoice); + CoroScheduler.setEvent(GLOBALS._hAskChoice); + CORO_INVOKE_2(CoroScheduler.waitForSingleObject, GLOBALS._hDoneChoice, CORO_INFINITE); + + // Now that the choice has been made, we can run the groups associated with the choice tbontbtitq + _ctx->j = GLOBALS._nSelectedChoice; + for (_ctx->k = 0; _ctx->dialog->_choice[_ctx->i]._select[_ctx->j]._wPlayGroup[_ctx->k] != 0; _ctx->k++) { + _ctx->nGroup = _ctx->dialog->_choice[_ctx->i]._select[_ctx->j]._wPlayGroup[_ctx->k]; + CORO_INVOKE_1(GroupThread, &_ctx->nGroup); + } + + // Control attribute + if (_ctx->dialog->_choice[_ctx->i]._select[_ctx->j]._attr & (1 << 0)) { + // Bit 0 set: the end of the choice + unlockDialogs(); + break; + } + + if (_ctx->dialog->_choice[_ctx->i]._select[_ctx->j]._attr & (1 << 1)) { + // Bit 1 set: the end of the dialog + unlockDialogs(); + + CORO_KILL_SELF(); + return; + } + + // End of choic ewithout attributes. We must do it again + } + + // If we're here, we found an end choice. Return to the caller group + return; + + CORO_END_CODE; +} + +/** + * Perform an action on a certain item. + * + * @param nAction Action number + * @param ordItem Index of the item in the items list + * @param dwParam Any parameter for the action. + * @returns Id of the process that was launched to perform the action, or + * CORO_INVALID_PID_VALUE if the action was not defined, or the item was inactive. + * @remarks You can get the index of an item from its number by using + * the itemGetOrderFromNum() function. The items list must first be locked + * by calling LockItem(). + */ +static uint32 doAction(uint32 nAction, uint32 ordItem, uint32 dwParam) { + LpMpalItem item = GLOBALS._lpmiItems; + LpMpalItem newitem; + + item+=ordItem; + Common::String buf = Common::String::format("Status.%u", item->_nObj); + if (varGetValue(buf.c_str()) <= 0) + return CORO_INVALID_PID_VALUE; + + for (int i = 0; i < item->_nActions; i++) { + if (item->_action[i]._num != nAction) + continue; + + if (item->_action[i]._wParm != dwParam) + continue; + + if (item->_action[i]._when != NULL) { + if (!evaluateExpression(item->_action[i]._when)) + continue; + } + + // Now we find the right action to be performed + // Duplicate the item and copy the current action in #i into #0 + newitem = (LpMpalItem)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(MpalItem)); + if (newitem == NULL) + return CORO_INVALID_PID_VALUE; + + // In the new version number of the action in writing dwRes + Common::copy((byte *)item, (byte *)item + sizeof(MpalItem), (byte *)newitem); + + //newitem->_action[0]._nCmds=item->_action[i]._nCmds; + //memcpy(newitem->_action[0]._cmdNum, item->_action[i]._cmdNum, newitem->Action[0].nCmds * sizeof(newitem->_action[0]._cmdNum[0])); + + newitem->_dwRes = i; + + // And finally we can laucnh the process that will execute the action, + // and a second process to free up the memory when the action is finished. + + // !!! New process management + uint32 h; + if ((h = CoroScheduler.createProcess(ActionThread, &newitem, sizeof(LpMpalItem))) == CORO_INVALID_PID_VALUE) + return CORO_INVALID_PID_VALUE; + + if (CoroScheduler.createProcess(ShutUpActionThread, &h, sizeof(uint32)) == CORO_INVALID_PID_VALUE) + return CORO_INVALID_PID_VALUE; + + GLOBALS._nExecutingAction = item->_nObj; + GLOBALS._bExecutingAction = true; + + return h; + } + + return CORO_INVALID_PID_VALUE; +} + +/** + * Shows a dialog in a separate process. + * + * @param nDlgOrd The index of the dialog in the dialog list + * @param nGroup Number of the group to perform + * @returns The process Id of the process running the dialog + * or CORO_INVALID_PID_VALUE on error + * @remarks The dialogue runs in a thread created on purpose, + * so that must inform through an event and when 'necessary to you make a choice. + * The data on the choices may be obtained through various queries. + */ +static uint32 doDialog(uint32 nDlgOrd, uint32 nGroup) { + // Store the running dialog in a global variable + GLOBALS._nExecutingDialog = nDlgOrd; + + // Enables the flag to indicate that there is' a running dialogue + GLOBALS._bExecutingDialog = true; + + CoroScheduler.resetEvent(GLOBALS._hAskChoice); + CoroScheduler.resetEvent(GLOBALS._hDoneChoice); + + // Create a thread that performs the dialogue group + + // Create the process + uint32 h; + if ((h = CoroScheduler.createProcess(GroupThread, &nGroup, sizeof(uint32))) == CORO_INVALID_PID_VALUE) + return CORO_INVALID_PID_VALUE; + + // Create a thread that waits until the end of the dialog process, and will restore the global variables + if (CoroScheduler.createProcess(ShutUpDialogThread, &h, sizeof(uint32)) == CORO_INVALID_PID_VALUE) { + // Something went wrong, so kill the previously started dialog process + CoroScheduler.killMatchingProcess(h); + return CORO_INVALID_PID_VALUE; + } + + return h; +} + +/** + * Takes note of the selection chosen by the user, and warns the process that was running + * the box that it can continue. + * + * @param nChoice Number of choice that was in progress + * @param dwData Since combined with select selection + * @returns True if everything is OK, false on failure + */ +bool doSelection(uint32 i, uint32 dwData) { + LpMpalDialog dialog = GLOBALS._lpmdDialogs + GLOBALS._nExecutingDialog; + int j; + + for (j = 0; dialog->_choice[i]._select[j]._dwData != 0; j++) { + if (dialog->_choice[i]._select[j]._dwData == dwData && dialog->_choice[i]._select[j]._curActive != 0) + break; + } + + if (dialog->_choice[i]._select[j]._dwData == 0) + return false; + + GLOBALS._nSelectedChoice = j; + CoroScheduler.setEvent(GLOBALS._hDoneChoice); + return true; +} + +/** + * @defgroup Exported functions + */ +//@{ + +/** + * Initializes the MPAL library and opens the .MPC file, which will be used for all queries. + * + * @param lpszMpcFileName Name of the MPC file + * @param lpszMprFileName Name of the MPR file + * @param lplpcfArray Array of pointers to custom functions. + * @returns True if everything is OK, false on failure + */ +bool mpalInit(const char *lpszMpcFileName, const char *lpszMprFileName, + LPLPCUSTOMFUNCTION lplpcfArray, Common::String *lpcfStrings) { + byte buf[5]; + byte *cmpbuf; + + // Save the array of custom functions + GLOBALS._lplpFunctions = lplpcfArray; + GLOBALS._lplpFunctionStrings = lpcfStrings; + + // OPen the MPC file for reading + Common::File hMpc; + if (!hMpc.open(lpszMpcFileName)) + return false; + + // Read and check the header + uint32 nBytesRead = hMpc.read(buf, 5); + if (nBytesRead != 5) + return false; + + if (buf[0] != 'M' || buf[1] != 'P' || buf[2] != 'C' || buf[3] != 0x20) + return false; + + bool bCompress = buf[4]; + + // Reads the size of the uncompressed file, and allocate memory + uint32 dwSizeDecomp = hMpc.readUint32LE(); + if (hMpc.err()) + return false; + + byte *lpMpcImage = (byte *)globalAlloc(GMEM_FIXED, dwSizeDecomp + 16); + if (lpMpcImage == NULL) + return false; + + if (bCompress) { + // Get the compressed size and read the data in + uint32 dwSizeComp = hMpc.readUint32LE(); + if (hMpc.err()) + return false; + + cmpbuf = (byte *)globalAlloc(GMEM_FIXED, dwSizeComp); + if (cmpbuf == NULL) + return false; + + nBytesRead = hMpc.read(cmpbuf, dwSizeComp); + if (nBytesRead != dwSizeComp) + return false; + + // Decompress the data + lzo1x_decompress(cmpbuf, dwSizeComp, lpMpcImage, &nBytesRead); + if (nBytesRead != dwSizeDecomp) + return false; + + globalDestroy(cmpbuf); + } else { + // If the file is not compressed, we directly read in the data + nBytesRead = hMpc.read(lpMpcImage, dwSizeDecomp); + if (nBytesRead != dwSizeDecomp) + return false; + } + + // Close the file + hMpc.close(); + + // Process the data + if (parseMpc(lpMpcImage) == false) + return false; + + globalDestroy(lpMpcImage); + + // Open the MPR file + if (!GLOBALS._hMpr.open(lpszMprFileName)) + return false; + + // Seek to the end of the file to read overall information + GLOBALS._hMpr.seek(-12, SEEK_END); + + uint32 dwSizeComp = GLOBALS._hMpr.readUint32LE(); + if (GLOBALS._hMpr.err()) + return false; + + GLOBALS._nResources = GLOBALS._hMpr.readUint32LE(); + if (GLOBALS._hMpr.err()) + return false; + + nBytesRead = GLOBALS._hMpr.read(buf, 4); + if (GLOBALS._hMpr.err()) + return false; + + if (buf[0] !='E' || buf[1] != 'N' || buf[2] != 'D' || buf[3] != '0') + return false; + + // Move to the start of the resources header + GLOBALS._hMpr.seek(-(12 + (int)dwSizeComp), SEEK_END); + + GLOBALS._lpResources = (uint32 *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, GLOBALS._nResources * 8); + if (GLOBALS._lpResources == NULL) + return false; + + cmpbuf = (byte *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, dwSizeComp); + if (cmpbuf == NULL) + return false; + + nBytesRead = GLOBALS._hMpr.read(cmpbuf, dwSizeComp); + if (nBytesRead != dwSizeComp) + return false; + + lzo1x_decompress((const byte *)cmpbuf, dwSizeComp, (byte *)GLOBALS._lpResources, (uint32 *)&nBytesRead); + if (nBytesRead != (uint32)GLOBALS._nResources * 8) + return false; + for (int i = 0; i < 2*GLOBALS._nResources; ++i) + GLOBALS._lpResources[i] = FROM_LE_32(GLOBALS._lpResources[i]); + + globalDestroy(cmpbuf); + + // Reset back to the start of the file, leaving it open + GLOBALS._hMpr.seek(0, SEEK_SET); + + // There is no action or dialog running by default + GLOBALS._bExecutingAction = false; + GLOBALS._bExecutingDialog = false; + + // There's no polling location + Common::fill(GLOBALS._nPollingLocations, GLOBALS._nPollingLocations + MAXPOLLINGLOCATIONS, 0); + + // Create the event that will be used to co-ordinate making choices and choices finishing + GLOBALS._hAskChoice = CoroScheduler.createEvent(true, false); + GLOBALS._hDoneChoice = CoroScheduler.createEvent(true, false); + + return true; +} + +/** + * Frees resources allocated by the MPAL subsystem + */ +void mpalFree() { + // Free the resource list + globalDestroy(GLOBALS._lpResources); +} + +/** + * This is a general function to communicate with the library, to request information + * about what is in the .MPC file + * + * @param wQueryType Type of query. The list is in the QueryTypes enum. + * @returns 4 bytes depending on the type of query + * @remarks This is the specialised version of the original single mpalQuery + * method that returns numeric results. + */ +uint32 mpalQueryDWORD(uint16 wQueryType, ...) { + Common::String buf; + uint32 dwRet = 0; + char *n; + + va_list v; + va_start(v, wQueryType); + + GLOBALS._mpalError = OK; + + if (wQueryType == MPQ_VERSION) { + + /* + * uint32 mpalQuery(MPQ_VERSION); + */ + dwRet = HEX_VERSION; + + } else if (wQueryType == MPQ_GLOBAL_VAR) { + /* + * uint32 mpalQuery(MPQ_GLOBAL_VAR, char * lpszVarName); + */ + lockVar(); + dwRet = (uint32)varGetValue(GETARG(char *)); + unlockVar(); + + } else if (wQueryType == MPQ_MESSAGE) { + /* + * char * mpalQuery(MPQ_MESSAGE, uint32 nMsg); + */ + error("mpalQuery(MPQ_MESSAGE, uint32 nMsg) used incorrect method variant"); + + + } else if (wQueryType == MPQ_ITEM_PATTERN) { + /* + * uint32 mpalQuery(MPQ_ITEM_PATTERN, uint32 nItem); + */ + lockVar(); + buf = Common::String::format("Pattern.%u", GETARG(uint32)); + dwRet = (uint32)varGetValue(buf.c_str()); + unlockVar(); + + } else if (wQueryType == MPQ_LOCATION_SIZE) { + /* + * uint32 mpalQuery(MPQ_LOCATION_SIZE, uint32 nLoc, uint32 dwCoord); + */ + lockLocations(); + int x = locGetOrderFromNum(GETARG(uint32)); + int y = GETARG(uint32); + if (x != -1) { + if (y == MPQ_X) + dwRet = GLOBALS._lpmlLocations[x]._dwXlen; + else if (y == MPQ_Y) + dwRet = GLOBALS._lpmlLocations[x]._dwYlen; + else + GLOBALS._mpalError = 1; + } else + GLOBALS._mpalError = 1; + + unlockLocations(); + + } else if (wQueryType == MPQ_LOCATION_IMAGE) { + /* + * HGLOBAL mpalQuery(MPQ_LOCATION_IMAGE, uint32 nLoc); + */ + error("mpalQuery(MPQ_LOCATION_IMAGE, uint32 nLoc) used incorrect variant"); + + } else if (wQueryType == MPQ_RESOURCE) { + /* + * HGLOBAL mpalQuery(MPQ_RESOURCE, uint32 dwRes); + */ + error("mpalQuery(MPQ_RESOURCE, uint32 dwRes) used incorrect variant"); + + } else if (wQueryType == MPQ_ITEM_LIST) { + /* + * uint32 mpalQuery(MPQ_ITEM_LIST, uint32 nLoc); + */ + error("mpalQuery(MPQ_ITEM_LIST, uint32 nLoc) used incorrect variant"); + + } else if (wQueryType == MPQ_ITEM_DATA) { + /* + * LpItem mpalQuery(MPQ_ITEM_DATA, uint32 nItem); + */ + error("mpalQuery(MPQ_ITEM_DATA, uint32 nItem) used incorrect variant"); + + } else if (wQueryType == MPQ_ITEM_IS_ACTIVE) { + /* + * bool mpalQuery(MPQ_ITEM_IS_ACTIVE, uint32 nItem); + */ + lockVar(); + int x = GETARG(uint32); + buf = Common::String::format("Status.%u", x); + if (varGetValue(buf.c_str()) <= 0) + dwRet = (uint32)false; + else + dwRet = (uint32)true; + + unlockVar(); + + } else if (wQueryType == MPQ_ITEM_NAME) { + /* + * uint32 mpalQuery(MPQ_ITEM_NAME, uint32 nItem, char * lpszName); + */ + lockVar(); + int x = GETARG(uint32); + n = GETARG(char *); + buf = Common::String::format("Status.%u", x); + if (varGetValue(buf.c_str()) <= 0) + n[0]='\0'; + else { + lockItems(); + int y = itemGetOrderFromNum(x); + memcpy(n, (char *)(GLOBALS._lpmiItems + y)->_lpszDescribe, MAX_DESCRIBE_SIZE); + unlockItems(); + } + + unlockVar(); + + } else if (wQueryType == MPQ_DIALOG_PERIOD) { + /* + * char *mpalQuery(MPQ_DIALOG_PERIOD, uint32 nDialog, uint32 nPeriod); + */ + error("mpalQuery(MPQ_DIALOG_PERIOD, uint32 nDialog, uint32 nPeriod) used incorrect variant"); + + } else if (wQueryType == MPQ_DIALOG_WAITFORCHOICE) { + /* + * void mpalQuery(MPQ_DIALOG_WAITFORCHOICE); + */ + error("mpalQuery(MPQ_DIALOG_WAITFORCHOICE) used incorrect variant"); + + } else if (wQueryType == MPQ_DIALOG_SELECTLIST) { + /* + * uint32 *mpalQuery(MPQ_DIALOG_SELECTLIST, uint32 nChoice); + */ + error("mpalQuery(MPQ_DIALOG_SELECTLIST, uint32 nChoice) used incorrect variant"); + + } else if (wQueryType == MPQ_DIALOG_SELECTION) { + /* + * bool mpalQuery(MPQ_DIALOG_SELECTION, uint32 nChoice, uint32 dwData); + */ + lockDialogs(); + int x = GETARG(uint32); + int y = GETARG(uint32); + dwRet = (uint32)doSelection(x, y); + + unlockDialogs(); + + } else if (wQueryType == MPQ_DO_ACTION) { + /* + * int mpalQuery(MPQ_DO_ACTION, uint32 nAction, uint32 nItem, uint32 dwParam); + */ + lockItems(); + lockVar(); + int x = GETARG(uint32); + int z = GETARG(uint32); + int y = itemGetOrderFromNum(z); + if (y != -1) { + dwRet = doAction(x, y, GETARG(uint32)); + } else { + dwRet = CORO_INVALID_PID_VALUE; + GLOBALS._mpalError = 1; + } + + unlockVar(); + unlockItems(); + + } else if (wQueryType == MPQ_DO_DIALOG) { + /* + * int mpalQuery(MPQ_DO_DIALOG, uint32 nDialog, uint32 nGroup); + */ + if (!GLOBALS._bExecutingDialog) { + lockDialogs(); + + int x = dialogGetOrderFromNum(GETARG(uint32)); + int y = GETARG(uint32); + dwRet = doDialog(x, y); + unlockDialogs(); + } + } else { + /* + * DEFAULT -> ERROR + */ + GLOBALS._mpalError = 1; + } + + va_end(v); + return dwRet; +} + +/** + * This is a general function to communicate with the library, to request information + * about what is in the .MPC file + * + * @param wQueryType Type of query. The list is in the QueryTypes enum. + * @returns 4 bytes depending on the type of query + * @remarks This is the specialised version of the original single mpalQuery + * method that returns a pointer or handle. + */ +MpalHandle mpalQueryHANDLE(uint16 wQueryType, ...) { + char *n; + Common::String buf; + va_list v; + va_start(v, wQueryType); + void *hRet = NULL; + + GLOBALS._mpalError = OK; + + if (wQueryType == MPQ_VERSION) { + /* + * uint32 mpalQuery(MPQ_VERSION); + */ + error("mpalQuery(MPQ_VERSION) used incorrect variant"); + + } else if (wQueryType == MPQ_GLOBAL_VAR) { + /* + * uint32 mpalQuery(MPQ_GLOBAL_VAR, char * lpszVarName); + */ + error("mpalQuery(MPQ_GLOBAL_VAR, char * lpszVarName) used incorrect variant"); + + } else if (wQueryType == MPQ_MESSAGE) { + /* + * char * mpalQuery(MPQ_MESSAGE, uint32 nMsg); + */ + LockMsg(); + hRet = DuplicateMessage(msgGetOrderFromNum(GETARG(uint32))); + UnlockMsg(); + + } else if (wQueryType == MPQ_ITEM_PATTERN) { + /* + * uint32 mpalQuery(MPQ_ITEM_PATTERN, uint32 nItem); + */ + error("mpalQuery(MPQ_ITEM_PATTERN, uint32 nItem) used incorrect variant"); + + } else if (wQueryType == MPQ_LOCATION_SIZE) { + /* + * uint32 mpalQuery(MPQ_LOCATION_SIZE, uint32 nLoc, uint32 dwCoord); + */ + error("mpalQuery(MPQ_LOCATION_SIZE, uint32 nLoc, uint32 dwCoord) used incorrect variant"); + + } else if (wQueryType == MPQ_LOCATION_IMAGE) { + /* + * HGLOBAL mpalQuery(MPQ_LOCATION_IMAGE, uint32 nLoc); + */ + lockLocations(); + int x = locGetOrderFromNum(GETARG(uint32)); + hRet = resLoad(GLOBALS._lpmlLocations[x]._dwPicRes); + unlockLocations(); + + } else if (wQueryType == MPQ_RESOURCE) { + /* + * HGLOBAL mpalQuery(MPQ_RESOURCE, uint32 dwRes); + */ + hRet = resLoad(GETARG(uint32)); + + } else if (wQueryType == MPQ_ITEM_LIST) { + /* + * uint32 mpalQuery(MPQ_ITEM_LIST, uint32 nLoc); + */ + lockVar(); + hRet = GetItemList(GETARG(uint32)); + lockVar(); + + } else if (wQueryType == MPQ_ITEM_DATA) { + /* + * LpItem mpalQuery(MPQ_ITEM_DATA, uint32 nItem); + */ + lockItems(); + hRet = getItemData(itemGetOrderFromNum(GETARG(uint32))); + unlockItems(); + + } else if (wQueryType == MPQ_ITEM_IS_ACTIVE) { + /* + * bool mpalQuery(MPQ_ITEM_IS_ACTIVE, uint32 nItem); + */ + error("mpalQuery(MPQ_ITEM_IS_ACTIVE, uint32 nItem) used incorrect variant"); + + } else if (wQueryType == MPQ_ITEM_NAME) { + /* + * uint32 mpalQuery(MPQ_ITEM_NAME, uint32 nItem, char *lpszName); + */ + lockVar(); + int x = GETARG(uint32); + n = GETARG(char *); + buf = Common::String::format("Status.%u", x); + if (varGetValue(buf.c_str()) <= 0) + n[0] = '\0'; + else { + lockItems(); + int y = itemGetOrderFromNum(x); + memcpy(n, (char *)(GLOBALS._lpmiItems + y)->_lpszDescribe, MAX_DESCRIBE_SIZE); + unlockItems(); + } + + unlockVar(); + + } else if (wQueryType == MPQ_DIALOG_PERIOD) { + /* + * char * mpalQuery(MPQ_DIALOG_PERIOD, uint32 nDialog, uint32 nPeriod); + */ + lockDialogs(); + int y = GETARG(uint32); + hRet = duplicateDialogPeriod(y); + unlockDialogs(); + + } else if (wQueryType == MPQ_DIALOG_WAITFORCHOICE) { + /* + * void mpalQuery(MPQ_DIALOG_WAITFORCHOICE); + */ + error("mpalQuery(MPQ_DIALOG_WAITFORCHOICE) used incorrect variant"); + + } else if (wQueryType == MPQ_DIALOG_SELECTLIST) { + /* + * uint32 *mpalQuery(MPQ_DIALOG_SELECTLIST, uint32 nChoice); + */ + lockDialogs(); + hRet = getSelectList(GETARG(uint32)); + unlockDialogs(); + + } else if (wQueryType == MPQ_DIALOG_SELECTION) { + /* + * bool mpalQuery(MPQ_DIALOG_SELECTION, uint32 nChoice, uint32 dwData); + */ + error("mpalQuery(MPQ_DIALOG_SELECTION, uint32 nChoice, uint32 dwData) used incorrect variant"); + + } else if (wQueryType == MPQ_DO_ACTION) { + /* + * int mpalQuery(MPQ_DO_ACTION, uint32 nAction, uint32 nItem, uint32 dwParam); + */ + error("mpalQuery(MPQ_DO_ACTION, uint32 nAction, uint32 nItem, uint32 dwParam) used incorrect variant"); + + } else if (wQueryType == MPQ_DO_DIALOG) { + /* + * int mpalQuery(MPQ_DO_DIALOG, uint32 nDialog, uint32 nGroup); + */ + error("mpalQuery(MPQ_DO_DIALOG, uint32 nDialog, uint32 nGroup) used incorrect variant"); + } else { + /* + * DEFAULT -> ERROR + */ + GLOBALS._mpalError = 1; + } + + va_end(v); + return hRet; +} + +/** + * This is a general function to communicate with the library, to request information + * about what is in the .MPC file + * + * @param wQueryType Type of query. The list is in the QueryTypes enum. + * @returns 4 bytes depending on the type of query + * @remarks This is the specialised version of the original single mpalQuery + * method that needs to run within a co-routine context. + */ +void mpalQueryCORO(CORO_PARAM, uint16 wQueryType, uint32 *dwRet, ...) { + CORO_BEGIN_CONTEXT; + uint32 dwRet; + CORO_END_CONTEXT(_ctx); + + va_list v; + va_start(v, dwRet); + + CORO_BEGIN_CODE(_ctx); + + if (wQueryType == MPQ_DIALOG_WAITFORCHOICE) { + /* + * void mpalQuery(MPQ_DIALOG_WAITFORCHOICE); + */ + CORO_INVOKE_2(CoroScheduler.waitForSingleObject, GLOBALS._hAskChoice, CORO_INFINITE); + + // WORKAROUND: Introduce a single frame delay so that if there are multiple actions running, + // they all have time to be signalled before resetting the event. This fixes a problem where + // if you try to use the 'shrimp' on the parrot a second time after trying to first use it + // whilst the parrot was talking, the cursor wouldn't be re-enabled afterwards + CORO_SLEEP(1); + + CoroScheduler.resetEvent(GLOBALS._hAskChoice); + + if (GLOBALS._bExecutingDialog) + *dwRet = (uint32)GLOBALS._nExecutingChoice; + else + *dwRet = (uint32)((int)-1); + } else { + error("mpalQueryCORO called with unsupported query type"); + } + + CORO_END_CODE; + + va_end(v); +} + +/** + * Returns the current MPAL error code + * + * @returns Error code + */ +uint32 mpalGetError() { + return GLOBALS._mpalError; +} + +/** + * Execute a script. The script runs on multitasking by a thread. + * + * @param nScript Script number to run + * @returns TRUE if the script 'was launched, FALSE on failure + */ +bool mpalExecuteScript(int nScript) { + LockScripts(); + int n = scriptGetOrderFromNum(nScript); + LpMpalScript s = (LpMpalScript)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(MpalScript)); + if (s == NULL) + return false; + + memcpy(s, GLOBALS._lpmsScripts + n, sizeof(MpalScript)); + unlockScripts(); + + // !!! New process management + if (CoroScheduler.createProcess(ScriptThread, &s, sizeof(LpMpalScript)) == CORO_INVALID_PID_VALUE) + return false; + + return true; +} + +/** + * Install a custom routine That will be called by MPAL every time the pattern + * of an item has been changed. + * + * @param lpiifCustom Custom function to install + */ +void mpalInstallItemIrq(LPITEMIRQFUNCTION lpiifCus) { + GLOBALS._lpiifCustom = lpiifCus; +} + +/** + * Process the idle actions of the items on one location. + * + * @param nLoc Number of the location whose items must be processed + * for idle actions. + * @returns TRUE if all OK, and FALSE if it exceeded the maximum limit. + * @remarks The maximum number of locations that can be polled + * simultaneously is defined defined by MAXPOLLINGFUNCIONS + */ +bool mpalStartIdlePoll(int nLoc) { + for (uint32 i = 0; i < MAXPOLLINGLOCATIONS; i++) { + if (GLOBALS._nPollingLocations[i] == (uint32)nLoc) + return false; + } + + for (uint32 i = 0; i < MAXPOLLINGLOCATIONS; i++) { + if (GLOBALS._nPollingLocations[i] == 0) { + GLOBALS._nPollingLocations[i] = nLoc; + + GLOBALS._hEndPollingLocations[i] = CoroScheduler.createEvent(true, false); +// !!! New process management + if ((GLOBALS._pollingThreads[i] = CoroScheduler.createProcess(LocationPollThread, &i, sizeof(uint32))) == CORO_INVALID_PID_VALUE) +// if ((GLOBALS.hEndPollingLocations[i] = (void*)_beginthread(LocationPollThread, 10240, (void *)i))= = (void*)-1) + return false; + + return true; + } + } + + return false; +} + +/** + * Stop processing the idle actions of the items on one location. + * + * @param nLo Number of the location + * @returns TRUE if all OK, FALSE if the specified location was not + * in the process of polling + */ +void mpalEndIdlePoll(CORO_PARAM, int nLoc, bool *result) { + CORO_BEGIN_CONTEXT; + int i; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + for (_ctx->i = 0; _ctx->i < MAXPOLLINGLOCATIONS; _ctx->i++) { + if (GLOBALS._nPollingLocations[_ctx->i] == (uint32)nLoc) { + CoroScheduler.setEvent(GLOBALS._hEndPollingLocations[_ctx->i]); + + CORO_INVOKE_2(CoroScheduler.waitForSingleObject, GLOBALS._pollingThreads[_ctx->i], CORO_INFINITE); + + CoroScheduler.closeEvent(GLOBALS._hEndPollingLocations[_ctx->i]); + GLOBALS._nPollingLocations[_ctx->i] = 0; + + if (result) + *result = true; + return; + } + } + + if (result) + *result = false; + + CORO_END_CODE; +} + +/** + * Retrieve the length of a save state + * + * @returns Length in bytes + */ +int mpalGetSaveStateSize() { + return GLOBALS._nVars * sizeof(MpalVar) + 4; +} + +/** + * Store the save state into a buffer. The buffer must be + * length at least the size specified with mpalGetSaveStateSize + * + * @param buf Buffer where to store the state + */ +void mpalSaveState(byte *buf) { + lockVar(); + WRITE_LE_UINT32(buf, GLOBALS._nVars); + memcpy(buf + 4, (byte *)GLOBALS._lpmvVars, GLOBALS._nVars * sizeof(MpalVar)); + unlockVar(); +} + +/** + * Load a save state from a buffer. + * + * @param buf Buffer where to store the state + * @returns Length of the state buffer in bytes + */ +int mpalLoadState(byte *buf) { + // We must destroy and recreate all the variables + globalFree(GLOBALS._hVars); + + GLOBALS._nVars = READ_LE_UINT32(buf); + + GLOBALS._hVars = globalAllocate(GMEM_ZEROINIT | GMEM_MOVEABLE, GLOBALS._nVars * sizeof(MpalVar)); + lockVar(); + memcpy((byte *)GLOBALS._lpmvVars, buf + 4, GLOBALS._nVars * sizeof(MpalVar)); + unlockVar(); + + return GLOBALS._nVars * sizeof(MpalVar) + 4; +} + +} // end of namespace MPAL + +} // end of namespace Tony diff --git a/engines/tony/mpal/mpal.h b/engines/tony/mpal/mpal.h new file mode 100644 index 0000000000..779bdd6188 --- /dev/null +++ b/engines/tony/mpal/mpal.h @@ -0,0 +1,503 @@ +/* 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. + * + * + */ +/* + * This code is based on original Tony Tough source code + * + * Copyright (c) 1997-2003 Nayma Software + */ + +/****************************************************************************\ +* General Introduction +\****************************************************************************/ + +/* + * MPAL (MultiPurpose Adventure Language) is a high level language + * for the definition of adventure. Through the use of MPAL you can describe + * storyboard the adventure, and then use it with any user interface. + * In fact, unlike many other similar products, MPAL is not programmed through + * the whole adventure, but are defined only the locations, objects, as they may + * interact with each other, etc.. thus making MPAL useful for any type of adventure. + */ + +/****************************************************************************\ +* Structure +\****************************************************************************/ + +/* + * MPAL consists of two main files: MPAL.DLL and MPAL.H + * The first is the DLL that contains the code to interface with MPAL + * adventures, the second is the header that defines the prototypes + * functions. MPAL is compiled for Win32, and it can therefore be used with + * any compiler that supports Win32 DLL (Watcom C++, Visual C++, + * Delphi, etc.), and therefore compatible with both Windows 95 and Windows NT. + * + * To use the DLL, and 'obviously need to create a library for symbols to export. + * + */ + +/****************************************************************************\ +* Custom Functions +\****************************************************************************/ + +/* + * A custom function and a function specified by the program that uses the + * library, to perform the particular code. The custom functions are + * retrieved from the library as specified in the source MPAL, and in particular + * in defining the behavior of an item with some action. + * + * To use the custom functions, you need to prepare an array of + * pointers to functions (such as using the type casting LPCUSTOMFUNCTION, + * (defined below), and pass it as second parameter to mpalInit (). Note you + * must specify the size of the array, as elements of pointers and which do not + * contain the same: the library will call it only those functions specified in + * the source MPAL. It can be useful, for debugging reasons, do not bet + * the shares of arrays used to debugging function, to avoid unpleasant crash, + * if it has been made an error in source and / or some oversight in the code. + * + */ + +#ifndef TONY_MPAL_H +#define TONY_MPAL_H + +#include "common/scummsys.h" +#include "common/coroutines.h" +#include "common/rect.h" +#include "common/str.h" +#include "tony/mpal/memory.h" + +namespace Tony { + +namespace MPAL { + +/****************************************************************************\ +* Macro definitions and structures +\****************************************************************************/ + +// OK value for the error codes +#define OK 0 + +#define MAXFRAMES 400 // frame animation of an object +#define MAXPATTERN 40 // pattern of animation of an object +#define MAXPOLLINGLOCATIONS 64 + +#define GETARG(type) va_arg(v, type) + +/** + * Macro for use with queries that may refer to X and Y co-ordinates + */ +enum QueryCoordinates { + MPQ_X, + MPQ_Y +}; + +/** + * Query can be used with mpalQuery methods. In practice corresponds all claims + * that can do at the library + */ +enum QueryTypes { + // General Query + MPQ_VERSION = 10, + + MPQ_GLOBAL_VAR = 50, + MPQ_RESOURCE, + MPQ_MESSAGE, + + // Query on leases + MPQ_LOCATION_IMAGE = 100, + MPQ_LOCATION_SIZE, + + // Queries about items + MPQ_ITEM_LIST = 200, + MPQ_ITEM_DATA, + MPQ_ITEM_PATTERN, + MPQ_ITEM_NAME, + MPQ_ITEM_IS_ACTIVE, + + // Query dialog + MPQ_DIALOG_PERIOD = 300, + MPQ_DIALOG_WAITFORCHOICE, + MPQ_DIALOG_SELECTLIST, + MPQ_DIALOG_SELECTION, + + // Query execution + MPQ_DO_ACTION = 400, + MPQ_DO_DIALOG +}; + +/** + * Framework to manage the animation of an item + */ +typedef struct { + char *_frames[MAXFRAMES]; + Common::Rect _frameslocations[MAXFRAMES]; + Common::Rect _bbox[MAXFRAMES]; + short _pattern[MAXPATTERN][MAXFRAMES]; + short _speed; + char _numframe; + char _numpattern; + char _curframe; + char _curpattern; + short _destX, _destY; + signed char _destZ; + short _objectID; +} Item; +typedef Item *LpItem; + +/** + * Define a custom function, to use the language MPAL to perform various controls as a result of an action + */ +typedef void (*LPCUSTOMFUNCTION)(CORO_PARAM, uint32, uint32, uint32, uint32); +typedef LPCUSTOMFUNCTION *LPLPCUSTOMFUNCTION; + +/** + * + * Define an IRQ of an item that is called when the pattern changes or the status of an item + */ +typedef void (*LPITEMIRQFUNCTION)(uint32, int, int); +typedef LPITEMIRQFUNCTION* LPLPITEMIRQFUNCTION; + +/** + * @defgroup Macrofunctions query + * + * The following are defines used for simplifying calling the mpalQuery variants + */ +//@{ + +/** + * Gets the current version of MPAL + * + * @returns Version number (0x1232 = 1.2.3b) + */ +#define mpalQueryVersion() \ + (uint16)mpalQueryDWORD(MPQ_VERSION) + +/** + * Gets the numerical value of a global variable + * + * @param lpszVarName Variable name (ASCIIZ) + * @returns Global variable value + * @remarks This query was implemented for debugging. The program, + * if well designed, should not need to access variables from + * within the library. + */ +#define mpalQueryGlobalVar(lpszVarName) \ + mpalQueryDWORD(MPQ_GLOBAL_VAR, (const char *)(lpszVarName)) + +/** + * Provides access to a resource inside the .MPC file + * + * @param dwResId Resource Id + * @returns Handle to a memory area containing the resource, ready for use. + */ +#define mpalQueryResource(dwResId) \ + mpalQueryHANDLE(MPQ_RESOURCE, (uint32)(dwResId)) + +/** + * Returns a message. + * + * @param nMsg Message number + * @returns ASCIIZ message + * @remarks The returned pointer must be freed via the memory manager +* after use. The message will be in ASCIIZ format. +*/ +#define mpalQueryMessage(nMsg) \ + (char *)mpalQueryHANDLE(MPQ_MESSAGE, (uint32)(nMsg)) + +/** + * Provides a location image + * @return Returns a picture handle + */ +#define mpalQueryLocationImage(nLoc) \ + mpalQueryHANDLE(MPQ_LOCATION_IMAGE, (uint32)(nLoc)) + +/** + * Request the x or y size of a location in pixels + * + * @param nLoc Location number + * @param dwCoord MPQ_X or MPQ_Y coordinate to retrieve + * @returns Size + */ +#define mpalQueryLocationSize(nLoc, dwCoord) \ + mpalQueryDWORD(MPQ_LOCATION_SIZE, (uint32)(nLoc), (uint32)(dwCoord)) + +/** + * Provides the list of objects in a location. + * + * @param nLoc Location number + * @returns List of objects (accessible by Item [0], Item [1], etc.) + */ +// TODO: Determine if this is endian safe +#define mpalQueryItemList(nLoc) \ + (uint32 *)mpalQueryHANDLE(MPQ_ITEM_LIST, (uint32)(nLoc)) + +/** + * Provides information on an item + * + * @param nItem Item number + * @returns Structure filled with requested information + */ +#define mpalQueryItemData(nItem) \ + (LpItem)mpalQueryHANDLE(MPQ_ITEM_DATA, (uint32)(nItem)) + +/** + * Provides the current pattern of an item + * + * @param nItem Item number + * @returns Number of animation patterns to be executed. + * @remarks By default, the pattern of 0 indicates that we should do nothing. + */ +#define mpalQueryItemPattern(nItem) \ + mpalQueryDWORD(MPQ_ITEM_PATTERN, (uint32)(nItem)) + +/** + * Returns true if an item is active + * + * @param nItem Item number + * @returns TRUE if the item is active, FALSE otherwise + */ +#define mpalQueryItemIsActive(nItem) \ + (bool)mpalQueryDWORD(MPQ_ITEM_IS_ACTIVE, (uint32)(nItem)) + +/** + * Returns the name of an item + * + * @param nItem Item number + * @param lpszName Pointer to a buffer of at least 33 bytes + * that will be filled with the name + * @remarks If the item is not active (ie. if its status or number + * is less than or equal to 0), the string will be empty. + */ +#define mpalQueryItemName(nItem, lpszName) \ + mpalQueryHANDLE(MPQ_ITEM_NAME, (uint32)(nItem), (char *)(lpszName)) + +/** + * Returns a sentence of dialog. + * + * @param nDialog Dialog number + * @param nPeriod Number of words + * @returns A pointer to the string of words, or NULL on failure. + * @remarks The string must be freed after use using the memory manager. + * Unlike normal messages, the sentences of dialogue are formed by a single + * string terminated with 0. + */ +#define mpalQueryDialogPeriod(nPeriod) \ + (char *)mpalQueryHANDLE(MPQ_DIALOG_PERIOD, (uint32)(nPeriod)) + +/** + * Wait until the moment in which the need is signaled to make a choice by the user. + * @returns Number of choice to be made, or -1 if the dialogue is finished. + */ +#define mpalQueryDialogWaitForChoice(dwRet) \ + CORO_INVOKE_2(mpalQueryCORO, MPQ_DIALOG_WAITFORCHOICE, dwRet) + +/** + * Requires a list of various options for some choice within the current dialog. + * + * @param nChoice Choice number + * @returns A pointer to an array containing the data matched to each option. + * @remarks The figure 'a uint32 specified in the source to which MPAL + * You can assign meaning that the more' suits. + * The pointer msut be freed after use using the memory memory. + */ +#define mpalQueryDialogSelectList(nChoice) \ + (uint32 *)mpalQueryHANDLE(MPQ_DIALOG_SELECTLIST, (uint32)(nChoice)) + +/** + * Warns the library that the user has selected, in a certain choice of the current dialog, + * corresponding option at a certain given. + * + * @param nChoice Choice number of the choice that was in progress + * @param dwData Option that was selected by the user. + * @returns TRUE if all OK, FALSE on failure. + * @remarks After execution of this query, MPAL continue + * Groups according to the execution of the dialogue. And necessary so the game + * remains on hold again for another chosen by mpalQueryDialogWaitForChoice (). + */ +#define mpalQueryDialogSelection(nChoice, dwData) \ + (bool)mpalQueryDWORD(MPQ_DIALOG_SELECTION, (uint32)(nChoice), (uint32)(dwData)) + +#define mpalQueryDialogSelectionDWORD(nChoice, dwData) \ + mpalQueryDWORD(MPQ_DIALOG_SELECTION, (uint32)(nChoice), (uint32)(dwData)) + +/** + * Warns the library an action was performed on a Object. + * The library will call custom functions, if necessary. + * + * @param nAction Action number + * @param nItem Item number + * @param dwParam Action parameter + * @returns Handle to the thread that is performing the action, or CORO_INVALID_PID_VALUE + * if the action is not defined for the item, or the item is inactive. + * @remarks The parameter is used primarily to implement actions + * as "U.S." involving two objects together. The action will be executed only + * if the item is active, ie if its status is a positive number greater than 0. + */ +#define mpalQueryDoAction(nAction, nItem, dwParam) \ + mpalQueryDWORD(MPQ_DO_ACTION, (uint32)(nAction), (uint32)(nItem), (uint32)(dwParam)) + +/** + * Warns the library a dialogue was required. + * + * @param nDialog Dialog number + * @param nGroup Group number to use + * @returns Handle to the thread that is running the box, or + * CORO_INVALID_PID_VALUE if the dialogue does not exist. + */ +#define mpalQueryDoDialog(nDialog, nGroup) \ + mpalQueryDWORD(MPQ_DO_DIALOG, (uint32)(nDialog), (uint32)(nGroup)) + +/** + * @defgroup Functions exported to the main game + */ +//@{ + +/** + * Initializes the MPAL library, and opens an .MPC file, which will be 'used for all queries + * @param lpszMpcFileName Name of the .MPC file, including extension + * @param lpszMprFileName Name of the .MPR file, including extension + * @param lplpcfArray Array of pointers to custom functions + * @returns TRUE if all OK, FALSE on failure + */ +bool mpalInit(const char *lpszFileName, const char *lpszMprFileName, + LPLPCUSTOMFUNCTION lplpcfArray, Common::String *lpcfStrings); + +/** + * Frees resources allocated by the MPAL subsystem + */ +void mpalFree(); + +/** + * This is a general function to communicate with the library, to request information + * about what is in the .MPC file + * + * @param wQueryType Type of query. The list is in the QueryTypes enum. + * @returns 4 bytes depending on the type of query + * @remarks This is the specialised version of the original single mpalQuery + * method that returns numeric results. + */ +uint32 mpalQueryDWORD(uint16 wQueryType, ...); + +/** + * This is a general function to communicate with the library, to request information + * about what is in the .MPC file + * + * @param wQueryType Type of query. The list is in the QueryTypes enum. + * @returns 4 bytes depending on the type of query + * @remarks This is the specialised version of the original single mpalQuery + * method that returns a pointer or handle. + */ +MpalHandle mpalQueryHANDLE(uint16 wQueryType, ...); + +/** + * This is a general function to communicate with the library, to request information + * about what is in the .MPC file + * + * @param wQueryType Type of query. The list is in the QueryTypes enum. + * @returns 4 bytes depending on the type of query + * @remarks This is the specialised version of the original single mpalQuery + * method that needs to run within a co-routine context. + */ +void mpalQueryCORO(CORO_PARAM, uint16 wQueryType, uint32 *dwRet, ...); + +/** + * Execute a script. The script runs on multitasking by a thread. + * + * @param nScript Script number to run + * @returns TRUE if the script 'was launched, FALSE on failure + */ +bool mpalExecuteScript(int nScript); + +/** + * Returns the current MPAL error code + * + * @returns Error code + */ +uint32 mpalGetError(); + +/** + * Install a custom routine That will be called by MPAL every time the pattern + * of an item has been changed. + * + * @param lpiifCustom Custom function to install + */ +void mpalInstallItemIrq(LPITEMIRQFUNCTION lpiifCustom); + +/** + * Process the idle actions of the items on one location. + * + * @param nLoc Number of the location whose items must be processed + * for idle actions. + * @returns TRUE if all OK, and FALSE if it exceeded the maximum limit. + * @remarks The maximum number of locations that can be polled + * simultaneously is defined defined by MAXPOLLINGFUNCIONS + */ +bool mpalStartIdlePoll(int nLoc); + +/** + * Stop processing the idle actions of the items on one location. + * + * @param nLo Number of the location + * @returns TRUE if all OK, FALSE if the specified location was not + * in the process of polling + */ +void mpalEndIdlePoll(CORO_PARAM, int nLoc, bool *result); + +/** + * Load a save state from a buffer. + * + * @param buf Buffer where to store the state + * @returns Length of the state buffer in bytes + */ +int mpalLoadState(byte *buf); + +/** + * Store the save state into a buffer. The buffer must be + * length at least the size specified with mpalGetSaveStateSize + * + * @param buf Buffer where to store the state + */ +void mpalSaveState(byte *buf); + +/** + * Retrieve the length of a save state + * + * @returns Length in bytes + */ +int mpalGetSaveStateSize(); + +/** + * Locks the variables for access + */ +void lockVar(); + +/** + * Unlocks variables after use + */ +void unlockVar(); + +} // end of namespace MPAL + +} // end of namespace Tony + +#endif + diff --git a/engines/tony/mpal/mpaldll.h b/engines/tony/mpal/mpaldll.h new file mode 100644 index 0000000000..92ddf8fc5a --- /dev/null +++ b/engines/tony/mpal/mpaldll.h @@ -0,0 +1,248 @@ +/* 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. + * + * + */ +/* + * This code is based on original Tony Tough source code + * + * Copyright (c) 1997-2003 Nayma Software + */ + +#ifndef __MPALDLL_H +#define __MPALDLL_H + +#include "common/file.h" +#include "tony/mpal/memory.h" +#include "tony/mpal/loadmpc.h" +#include "tony/mpal/expr.h" + +namespace Tony { + +namespace MPAL { + +/****************************************************************************\ +* Defines +\****************************************************************************/ + +#define HEX_VERSION 0x0170 + +#define MAX_ACTIONS_PER_ITEM 40 +#define MAX_COMMANDS_PER_ITEM 128 +#define MAX_COMMANDS_PER_ACTION 128 +#define MAX_DESCRIBE_SIZE 64 + +#define MAX_MOMENTS_PER_SCRIPT 256 +#define MAX_COMMANDS_PER_SCRIPT 256 +#define MAX_COMMANDS_PER_MOMENT 32 + +#define MAX_GROUPS_PER_DIALOG 128 +#define MAX_COMMANDS_PER_DIALOG 480 +#define MAX_COMMANDS_PER_GROUP 64 +#define MAX_CHOICES_PER_DIALOG 64 +#define MAX_SELECTS_PER_CHOICE 64 +#define MAX_PLAYGROUPS_PER_SELECT 9 +#define MAX_PERIODS_PER_DIALOG 400 + +#define NEED_LOCK_MSGS + +/****************************************************************************\ +* Structures +\****************************************************************************/ + +#include "common/pack-start.h" + +/** + * MPAL global variables + */ +struct MpalVar { + uint32 _dwVal; // Variable value + char _lpszVarName[33]; // Variable name +} PACKED_STRUCT; +typedef MpalVar *LpMpalVar; + +/** + * MPAL Messages + */ +struct MpalMsg { + MpalHandle _hText; // Handle to the message text + uint16 _wNum; // Message number +} PACKED_STRUCT; +typedef MpalMsg *LpMpalMsg; + +/** + * MPAL Locations + */ +struct MpalLocation { + uint32 _nObj; // Location number + uint32 _dwXlen, _dwYlen; // Dimensions + uint32 _dwPicRes; // Resource that contains the image +} PACKED_STRUCT; +typedef MpalLocation *LpMpalLocation; + +/** + * All the data for a command, ie. tags used by OnAction in the item, the time + * in the script, and in the group dialog. + */ +struct Command { + /* + * Types of commands that are recognized + * + * #1 -> Custom function call (ITEM, SCRIPT, DIALOG) + * #2 -> Variable assignment (ITEM, SCRIPT, DIALOG) + * #3 -> Making a choice (DIALOG) + * + */ + byte _type; // Type of control + + union { + int32 _nCf; // Custom function call [#1] + char *_lpszVarName; // Variable name [#2] + int32 _nChoice; // Number of choice you make [#3] + }; + + union { + int32 _arg1; // Argument for custom function [#1] + MpalHandle _expr; // Expression to assign to a variable [#2] + }; + + int32 _arg2, _arg3, _arg4; // Arguments for custom function [#1] +} PACKED_STRUCT; + +/** + * MPAL dialog + */ +struct MpalDialog { + uint32 _nObj; // Dialog number + + struct Command _command[MAX_COMMANDS_PER_DIALOG]; + + struct { + uint16 _num; + byte _nCmds; + uint16 _cmdNum[MAX_COMMANDS_PER_GROUP]; + + } _group[MAX_GROUPS_PER_DIALOG]; + + struct { + // The last choice has nChoice == 0 + uint16 _nChoice; + + // The select number (we're pretty stingy with RAM). The last select has dwData == 0 + struct { + MpalHandle _when; + uint32 _dwData; + uint16 _wPlayGroup[MAX_PLAYGROUPS_PER_SELECT]; + + // Bit 0=endchoice Bit 1=enddialog + byte _attr; + + // Modified at run-time: 0 if the select is currently disabled, + // and 1 if currently active + byte _curActive; + } _select[MAX_SELECTS_PER_CHOICE]; + + } _choice[MAX_CHOICES_PER_DIALOG]; + + uint16 _periodNums[MAX_PERIODS_PER_DIALOG]; + MpalHandle _periods[MAX_PERIODS_PER_DIALOG]; + +} PACKED_STRUCT; +typedef MpalDialog *LpMpalDialog; + +/** + * MPAL Item + */ +struct ItemAction { + byte _num; // Action number + uint16 _wTime; // If idle, the time which must pass + byte _perc; // Percentage of the idle run + MpalHandle _when; // Expression to compute. If != 0, then action can be done + uint16 _wParm; // Parameter for action + + byte _nCmds; // Number of commands to be executed + uint32 _cmdNum[MAX_COMMANDS_PER_ACTION]; // Commands to execute +} PACKED_STRUCT; + +struct MpalItem { + uint32 _nObj; // Item number + + byte _lpszDescribe[MAX_DESCRIBE_SIZE]; // Name + byte _nActions; // Number of managed actions + uint32 _dwRes; // Resource that contains frames and patterns + + struct Command _command[MAX_COMMANDS_PER_ITEM]; + + // Pointer to array of structures containing various managed activities. In practice, of + // every action we know what commands to run, including those defined in structures above + struct ItemAction *_action; + +} PACKED_STRUCT; +typedef MpalItem *LpMpalItem; + +/** + * MPAL Script + */ +struct MpalScript { + uint32 _nObj; + uint32 _nMoments; + + struct Command _command[MAX_COMMANDS_PER_SCRIPT]; + + struct { + int32 _dwTime; + byte _nCmds; + uint32 _cmdNum[MAX_COMMANDS_PER_MOMENT]; + + } _moment[MAX_MOMENTS_PER_SCRIPT]; + +} PACKED_STRUCT; +typedef MpalScript *LpMpalScript; + +#include "common/pack-end.h" + +/****************************************************************************\ +* Function prototypes +\****************************************************************************/ + +/** + * Returns the current value of a global variable + * + * @param lpszVarName Name of the variable + * @returns Current value + * @remarks Before using this method, you must call LockVar() to + * lock the global variablves for use. Then afterwards, you will + * need to remember to call UnlockVar() + */ +extern int32 varGetValue(const char *lpszVarName); + +/** + * Sets the value of a MPAL global variable + * @param lpszVarName Name of the variable + * @param val Value to set + */ +extern void varSetValue(const char *lpszVarName, int32 val); + +} // end of namespace MPAL + +} // end of namespace Tony + +#endif + diff --git a/engines/tony/mpal/mpalutils.cpp b/engines/tony/mpal/mpalutils.cpp new file mode 100644 index 0000000000..0919aed5ac --- /dev/null +++ b/engines/tony/mpal/mpalutils.cpp @@ -0,0 +1,115 @@ +/* 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 "tony/mpal/mpalutils.h" +#include "tony/tony.h" +#include "common/memstream.h" + +namespace Tony { + +namespace MPAL { + +/****************************************************************************\ +* RMRes methods +\****************************************************************************/ + +/** + * Constructor + * @param resId MPAL resource to open + */ +RMRes::RMRes(uint32 resID) { + _h = g_vm->_resUpdate.queryResource(resID); + if (_h == NULL) + _h = mpalQueryResource(resID); + if (_h != NULL) + _buf = (byte *)globalLock(_h); +} + +/** + * Destructor + */ +RMRes::~RMRes() { + if (_h != NULL) { + globalUnlock(_h); + globalFree(_h); + } +} + +/** + * Returns a pointer to the resource + */ +const byte *RMRes::dataPointer() { + return _buf; +} + +/** + * Returns a pointer to the resource + */ +RMRes::operator const byte *() { + return dataPointer(); +} + +/** + * Returns the size of the resource + */ +unsigned int RMRes::size() { + return globalSize(_h); +} + +Common::SeekableReadStream *RMRes::getReadStream() { + return new Common::MemoryReadStream(_buf, size()); +} + +bool RMRes::isValid() { + return _h != NULL; +} + +/****************************************************************************\ +* RMResRaw methods +\****************************************************************************/ + +RMResRaw::RMResRaw(uint32 resID) : RMRes(resID) { +} + +RMResRaw::~RMResRaw() { +} + +const byte *RMResRaw::dataPointer() { + return _buf + 8; +} + +RMResRaw::operator const byte *() { + return dataPointer(); +} + +int RMResRaw::width() { + return READ_LE_UINT16(_buf + 4); +} + +int RMResRaw::height() { + return READ_LE_UINT16(_buf + 6); +} + +} // end of namespace MPAL + +} // end of namespace Tony diff --git a/engines/tony/mpal/mpalutils.h b/engines/tony/mpal/mpalutils.h new file mode 100644 index 0000000000..f351f22196 --- /dev/null +++ b/engines/tony/mpal/mpalutils.h @@ -0,0 +1,74 @@ +/* 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 TONY_MPAL_MPALUTILS +#define TONY_MPAL_MPALUTILS + +#include "common/scummsys.h" +#include "tony/mpal/memory.h" + +namespace Common { + class SeekableReadStream; +} + +namespace Tony { + +namespace MPAL { + +class RMRes { +protected: + MpalHandle _h; + byte *_buf; + +public: + RMRes(uint32 resID); + virtual ~RMRes(); + + // Attributes + unsigned int size(); + const byte *dataPointer(); + bool isValid(); + + // Casting for access to data + operator const byte*(); + + Common::SeekableReadStream *getReadStream(); +}; + +class RMResRaw : public RMRes { +public: + RMResRaw(uint32 resID); + virtual ~RMResRaw(); + + const byte *dataPointer(); + operator const byte*(); + + int width(); + int height(); +}; + +} // end of namespace MPAL + +} // end of namespace Tony + +#endif diff --git a/engines/tony/resid.h b/engines/tony/resid.h new file mode 100644 index 0000000000..0d601b7dd6 --- /dev/null +++ b/engines/tony/resid.h @@ -0,0 +1,70 @@ +/* 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. + * + */ + +/* + * This code is based on original Tony Tough source code + * + * Copyright (c) 1997-2003 Nayma Software + */ + +// From 10500 onwards there are .OGG for inventory and scrap +#ifndef TONY_RESID_H +#define TONY_RESID_H + +#define RES_I_INTERFACE 10300 +#define RES_I_INTERPAL 10301 +#define RES_I_INTERPPAL 10302 +#define RES_I_INTERP1 10303 +#define RES_I_INTERP2 10304 +#define RES_I_INTERP3 10305 +#define RES_I_INTERP4 10306 +#define RES_I_INTERP5 10307 + +#define RES_I_DLGTEXT 10350 +#define RES_I_DLGTEXTLINE 10351 +#define RES_I_DLGTEXTPAL 10352 + +#define RES_I_MINIINTER 10360 + +#define RES_P_PAL 10410 +#define RES_P_GO 10400 +#define RES_P_TAKE 10401 +#define RES_P_USE 10402 +#define RES_P_EXAM 10403 +#define RES_P_TALK 10404 + +#define RES_P_PAP1 10420 +#define RES_P_PAP2 10421 +#define RES_P_PAP3 10422 +#define RES_P_PAP4 10423 +#define RES_P_FRMAP 10424 + +#define RES_F_PAL 10700 +#define RES_F_PARL 10701 +#define RES_F_OBJ 10702 +#define RES_F_MACC 10703 +#define RES_F_CREDITS 10704 +#define RES_F_CPAL 10705 + +#define RES_W_CIRCLE 10800 + +#endif diff --git a/engines/tony/sound.cpp b/engines/tony/sound.cpp new file mode 100644 index 0000000000..90ae241db0 --- /dev/null +++ b/engines/tony/sound.cpp @@ -0,0 +1,685 @@ +/* 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. + * + */ + +/* + * This code is based on original Tony Tough source code + * + * Copyright (c) 1997-2003 Nayma Software + */ + +#include "audio/audiostream.h" +#include "audio/decoders/adpcm.h" +#include "audio/decoders/raw.h" +#include "audio/decoders/wave.h" +#include "common/textconsole.h" +#include "tony/game.h" +#include "tony/tony.h" + +namespace Tony { + +/* + * Tony uses a [0,63] volume scale (where 0 is silent and 63 is loudest). + * The original game engine linearly mapped this scale into DirectSound's + * [-10000, 0] scale (where -10000 is silent), which is a logarithmic scale. + * + * This means that Tony's scale is logarithmic as well, and must be converted + * to the linear scale used by the mixer. + */ +static int remapVolume(int volume) { + double dsvol = (double)(63 - volume) * -10000.0 / 63.0; + return (int)((double)Audio::Mixer::kMaxChannelVolume * pow(10.0, dsvol / 2000.0) + 0.5); +} + +/****************************************************************************\ +* FPSOUND Methods +\****************************************************************************/ + +/** + * Default constructor. Initializes the attributes. + * + */ +FPSound::FPSound() { + _soundSupported = false; +} + +/** + * Initializes the object, and prepare everything you need to create streams and sound effects. + * + * @returns True is everything is OK, False otherwise + */ +bool FPSound::init() { + _soundSupported = g_system->getMixer()->isReady(); + return _soundSupported; +} + +/** + * Destroy the object and free the memory + * + */ +FPSound::~FPSound() { +} + +/** + * Allocates an object of type FPStream, and return its pointer + * + * @param streamPtr Will contain a pointer to the object you just created. + * + * @returns True is everything is OK, False otherwise + */ +bool FPSound::createStream(FPStream **streamPtr) { + (*streamPtr) = new FPStream(_soundSupported); + + return (*streamPtr != NULL); +} + +/** + * Allocates an object of type FpSfx, and return its pointer + * + * @param soundPtr Will contain a pointer to the object you just created. + * + * @returns True is everything is OK, False otherwise + */ +bool FPSound::createSfx(FPSfx **sfxPtr) { + (*sfxPtr) = new FPSfx(_soundSupported); + + return (*sfxPtr != NULL); +} + +/** + * Set the general volume + * + * @param volume Volume to set (0-63) + */ +void FPSound::setMasterVolume(int volume) { + if (!_soundSupported) + return; + + // WORKAROUND: We don't use remapVolume() here, so that the main option screen exposes + // a linear scale to the user. This is an improvement over the original game + // where the user had to deal with a logarithmic volume scale. + g_system->getMixer()->setVolumeForSoundType(Audio::Mixer::kPlainSoundType, CLIP<int>(volume, 0, 63) * Audio::Mixer::kMaxChannelVolume / 63); +} + +/** + * Get the general volume + * + * @param volumePtr Variable that will contain the volume (0-63) + */ +void FPSound::getMasterVolume(int *volumePtr) { + if (!_soundSupported) + return; + + *volumePtr = g_system->getMixer()->getVolumeForSoundType(Audio::Mixer::kPlainSoundType) * 63 / Audio::Mixer::kMaxChannelVolume; +} + +/** + * Default constructor. + * + * @remarks Do *NOT* declare an object directly, but rather + * create it using FPSound::CreateSfx() + * + */ +FPSfx::FPSfx(bool soundOn) { + _soundSupported = soundOn; + _fileLoaded = false; + _lastVolume = 63; + _hEndOfBuffer = CoroScheduler.createEvent(true, false); + _isVoice = false; + _loopStream = 0; + _rewindableStream = 0; + _paused = false; + + g_vm->_activeSfx.push_back(this); +} + +/** + * Default Destructor. + * + * @remarks It is also stops the sound effect that may be + * currently played, and free the memory it uses. + * + */ +FPSfx::~FPSfx() { + if (!_soundSupported) + return; + + g_system->getMixer()->stopHandle(_handle); + g_vm->_activeSfx.remove(this); + + if (_loopStream) + delete _loopStream; // _rewindableStream is deleted by deleting _loopStream + else + delete _rewindableStream; + + // Free the buffer end event + CoroScheduler.closeEvent(_hEndOfBuffer); +} + +/** + * Releases the memory used by the object. + * + * @remarks Must be called when the object is no longer used and + * **ONLY** if the object was created by + * FPSound::CreateStream(). + * Object pointers are no longer valid after this call. + */ +void FPSfx::release() { + delete this; +} + +bool FPSfx::loadWave(Common::SeekableReadStream *stream) { + if (!stream) + return false; + + _rewindableStream = Audio::makeWAVStream(stream, DisposeAfterUse::YES); + + if (!_rewindableStream) + return false; + + _fileLoaded = true; + setVolume(_lastVolume); + return true; +} + +bool FPSfx::loadVoiceFromVDB(Common::File &vdbFP) { + if (!_soundSupported) + return true; + + uint32 size = vdbFP.readUint32LE(); + uint32 rate = vdbFP.readUint32LE(); + _isVoice = true; + + _rewindableStream = Audio::makeADPCMStream(vdbFP.readStream(size), DisposeAfterUse::YES, 0, Audio::kADPCMDVI, rate, 1); + + _fileLoaded = true; + setVolume(62); + return true; +} + +/** + * Opens a file and loads a sound effect. + * + * @param fileName Sfx filename + * @param codec CODEC used to uncompress the samples + * + * @returns True is everything is OK, False otherwise + */ +bool FPSfx::loadFile(const char *fileName, uint32 codec) { + if (!_soundSupported) + return true; + + Common::File file; + if (!file.open(fileName)) { + warning("FPSfx::LoadFile(): Cannot open sfx file!"); + return false; + } + + if (file.readUint32BE() != MKTAG('A', 'D', 'P', 0x10)) { + warning("FPSfx::LoadFile(): Invalid ADP header!"); + return false; + } + + uint32 rate = file.readUint32LE(); + uint32 channels = file.readUint32LE(); + + Common::SeekableReadStream *buffer = file.readStream(file.size() - file.pos()); + + if (codec == FPCODEC_ADPCM) { + _rewindableStream = Audio::makeADPCMStream(buffer, DisposeAfterUse::YES, 0, Audio::kADPCMDVI, rate, channels); + } else { + byte flags = Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN; + + if (channels == 2) + flags |= Audio::FLAG_STEREO; + + _rewindableStream = Audio::makeRawStream(buffer, rate, flags, DisposeAfterUse::YES); + } + + _fileLoaded = true; + return true; +} + +/** + * Play the Sfx in memory. + * + * @returns True is everything is OK, False otherwise + */ +bool FPSfx::play() { + stop(); // sanity check + + if (_fileLoaded) { + CoroScheduler.resetEvent(_hEndOfBuffer); + + _rewindableStream->rewind(); + + Audio::AudioStream *stream = _rewindableStream; + + if (_loop) { + if (!_loopStream) + _loopStream = Audio::makeLoopingAudioStream(_rewindableStream, 0); + + stream = _loopStream; + } + + g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &_handle, stream, -1, + Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO); + + setVolume(_lastVolume); + + if (_paused) + g_system->getMixer()->pauseHandle(_handle, true); + } + + return true; +} + +/** + * Stops a Sfx. + * + * @returns True is everything is OK, False otherwise + */ +bool FPSfx::stop() { + if (_fileLoaded) { + g_system->getMixer()->stopHandle(_handle); + _paused = false; + } + + return true; +} + +/** + * Enables or disables the Sfx loop. + * + * @param loop True to enable the loop, False to disable + * + * @remarks The loop must be activated BEFORE the sfx starts + * playing. Any changes made during the play will have + * no effect until the sfx is stopped then played again. + */ +void FPSfx::setLoop(bool loop) { + _loop = loop; +} + +/** + * Pauses a Sfx. + * + */ +void FPSfx::setPause(bool pause) { + if (_fileLoaded) { + if (g_system->getMixer()->isSoundHandleActive(_handle) && (pause ^ _paused)) + g_system->getMixer()->pauseHandle(_handle, pause); + + _paused = pause; + } +} + +/** + * Change the volume of Sfx + * + * @param volume Volume to be set (0-63) + * + */ +void FPSfx::setVolume(int volume) { + if (volume > 63) + volume = 63; + + if (volume < 0) + volume = 0; + + _lastVolume = volume; + + if (_isVoice) { + if (!GLOBALS._bCfgDubbing) + volume = 0; + else { + volume -= (10 - GLOBALS._nCfgDubbingVolume) * 2; + if (volume < 0) + volume = 0; + } + } else { + if (!GLOBALS._bCfgSFX) + volume = 0; + else { + volume -= (10 - GLOBALS._nCfgSFXVolume) * 2; + if (volume < 0) + volume = 0; + } + } + + if (g_system->getMixer()->isSoundHandleActive(_handle)) + g_system->getMixer()->setChannelVolume(_handle, remapVolume(volume)); +} + +/** + * Gets the Sfx volume + * + * @param volumePtr Will contain the current Sfx volume + * + */ +void FPSfx::getVolume(int *volumePtr) { + if (g_system->getMixer()->isSoundHandleActive(_handle)) + *volumePtr = _lastVolume; + else + *volumePtr = 0; +} + +/** + * Returns true if the underlying sound has ended + */ +bool FPSfx::endOfBuffer() const { + return !g_system->getMixer()->isSoundHandleActive(_handle) && (!_rewindableStream || _rewindableStream->endOfData()); +} + +/** + * Continually checks to see if active sounds have finished playing + * Sets the event signalling the sound has ended + */ +void FPSfx::soundCheckProcess(CORO_PARAM, const void *param) { + CORO_BEGIN_CONTEXT; + Common::List<FPSfx *>::iterator i; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + for (;;) { + // Check each active sound + for (_ctx->i = g_vm->_activeSfx.begin(); _ctx->i != g_vm->_activeSfx.end(); ++_ctx->i) { + FPSfx *sfx = *_ctx->i; + if (sfx->endOfBuffer()) + CoroScheduler.setEvent(sfx->_hEndOfBuffer); + } + + // Delay until the next check is done + CORO_INVOKE_1(CoroScheduler.sleep, 50); + } + + CORO_END_CODE; +} + +/** + * Default constructor. + * + * @remarks Do *NOT* declare an object directly, but rather + * create it using FPSound::CreateStream() + */ +FPStream::FPStream(bool soundOn) { + _soundSupported = soundOn; + _fileLoaded = false; + _paused = false; + _loop = false; + _doFadeOut = false; + _syncExit = false; + _bufferSize = _size = 0; + _lastVolume = 0; + _syncToPlay = NULL; + _loopStream = NULL; + _rewindableStream = NULL; +} + +/** + * Default destructor. + * + * @remarks It calls CloseFile() if needed. + */ +FPStream::~FPStream() { + if (!_soundSupported) + return; + + if (g_system->getMixer()->isSoundHandleActive(_handle)) + stop(); + + if (_fileLoaded) + unloadFile(); + + _syncToPlay = NULL; +} + +/** + * Releases the memory object. + * + * @remarks Must be called when the object is no longer used + * and **ONLY** if the object was created by + * FPSound::CreateStream(). + * Object pointers are no longer valid after this call. + */ +void FPStream::release() { + delete this; +} + +/** + * Opens a file stream + * + * @param fileName Filename to be opened + * @param codec CODEC to be used to uncompress samples + * + * @returns True is everything is OK, False otherwise + */ +bool FPStream::loadFile(const Common::String &fileName, uint32 codec, int bufSize) { + if (!_soundSupported) + return true; + + if (_fileLoaded) + unloadFile(); + + // Save the codec type + _codec = codec; + + // Open the file stream for reading + if (!_file.open(fileName)) { + // Fallback: try with an extra '0' prefix + if (!_file.open("0" + fileName)) + return false; + warning("FPStream::loadFile(): Fallback from %s to %s", fileName.c_str(), _file.getName()); + } + + // Save the size of the stream + _size = _file.size(); + + switch (_codec) { + case FPCODEC_RAW: + _rewindableStream = Audio::makeRawStream(&_file, 44100, Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN | Audio::FLAG_STEREO, DisposeAfterUse::NO); + break; + + case FPCODEC_ADPCM: + _rewindableStream = Audio::makeADPCMStream(&_file, DisposeAfterUse::NO, 0, Audio::kADPCMDVI, 44100, 2); + break; + + default: + _file.close(); + return false; + } + + // All done + _fileLoaded = true; + _paused = false; + + setVolume(63); + + return true; +} + +/** + * Closes a file stream (opened or not). + * + * @returns For safety, the destructor calls unloadFile() if it has not + * been mentioned explicitly. + * + * @remarks It is necessary to call this function to free the + * memory used by the stream. + */ +bool FPStream::unloadFile() { + if (!_soundSupported || !_fileLoaded) + return true; + + assert(!g_system->getMixer()->isSoundHandleActive(_handle)); + + // Closes the file handle stream + delete _loopStream; + delete _rewindableStream; + _loopStream = NULL; + _rewindableStream = NULL; + _file.close(); + + // Flag that the file is no longer in memory + _fileLoaded = false; + + return true; +} + +/** + * Play the stream. + * + * @returns True is everything is OK, False otherwise + */ + +bool FPStream::play() { + if (!_soundSupported || !_fileLoaded) + return false; + + stop(); + + _rewindableStream->rewind(); + + Audio::AudioStream *stream = _rewindableStream; + + if (_loop) { + if (!_loopStream) + _loopStream = new Audio::LoopingAudioStream(_rewindableStream, 0, DisposeAfterUse::NO); + + stream = _loopStream; + } + + // FIXME: Should this be kMusicSoundType or KPlainSoundType? + g_system->getMixer()->playStream(Audio::Mixer::kMusicSoundType, &_handle, stream, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO); + setVolume(_lastVolume); + _paused = false; + + return true; +} + +/** + * Closes the stream. + * + * @returns True is everything is OK, False otherwise + * + */ +bool FPStream::stop() { + if (!_soundSupported) + return true; + + if (!_fileLoaded) + return false; + + if (!g_system->getMixer()->isSoundHandleActive(_handle)) + return false; + + g_system->getMixer()->stopHandle(_handle); + + _paused = false; + + return true; +} + +void FPStream::waitForSync(FPStream *toPlay) { + // FIXME: The idea here is that you wait for this stream to reach + // a buffer which is a multiple of nBufSize/nSync, and then the + // thread stops it and immediately starts the 'toplay' stream. + + stop(); + toPlay->play(); +} + +/** + * Unables or disables stream loop. + * + * @param loop True enable loop, False disables it + * + * @remarks The loop must be activated BEFORE the stream starts + * playing. Any changes made during the play will have no + * effect until the stream is stopped then played again. + */ +void FPStream::setLoop(bool loop) { + _loop = loop; +} + +/** + * Pause sound effect + * + * @param pause True enables pause, False disables it + */ +void FPStream::setPause(bool pause) { + if (!_fileLoaded) + return; + + if (pause == _paused) + return; + + if (g_system->getMixer()->isSoundHandleActive(_handle)) + g_system->getMixer()->pauseHandle(_handle, pause); + + _paused = pause; + + // Trick to reset the volume after a possible new sound configuration + setVolume(_lastVolume); +} + +/** + * Change the volume of the stream + * + * @param volume Volume to be set (0-63) + * + */ +void FPStream::setVolume(int volume) { + if (volume > 63) + volume = 63; + + if (volume < 0) + volume = 0; + + _lastVolume = volume; + + if (!GLOBALS._bCfgMusic) + volume = 0; + else { + volume -= (10 - GLOBALS._nCfgMusicVolume) * 2; + if (volume < 0) + volume = 0; + } + + if (g_system->getMixer()->isSoundHandleActive(_handle)) + g_system->getMixer()->setChannelVolume(_handle, remapVolume(volume)); +} + +/** + * Gets the volume of the stream + * + * @param volumePtr Variable that will contain the current volume + * + */ +void FPStream::getVolume(int *volumePtr) { + if (g_system->getMixer()->isSoundHandleActive(_handle)) + *volumePtr = _lastVolume; + else + *volumePtr = 0; +} + +} // End of namespace Tony diff --git a/engines/tony/sound.h b/engines/tony/sound.h new file mode 100644 index 0000000000..7422de02b3 --- /dev/null +++ b/engines/tony/sound.h @@ -0,0 +1,377 @@ +/* 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. + * + */ + +/* + * This code is based on original Tony Tough source code + * + * Copyright (c) 1997-2003 Nayma Software + */ + +#ifndef TONY_SOUND_H +#define TONY_SOUND_H + +#include "audio/mixer.h" +#include "common/file.h" +#include "tony/gfxcore.h" +#include "tony/loc.h" +#include "tony/utils.h" + +namespace Audio { +class RewindableAudioStream; +} + +namespace Tony { + +class FPStream; +class FPSfx; + +enum SoundCodecs { + FPCODEC_RAW, + FPCODEC_ADPCM +}; + +/** + * Sound driver For Tony Tough + */ + +class FPSound { +private: + bool _soundSupported; + +public: + /** + * Default constructor. Initializes the attributes. + * + */ + + FPSound(); + + /** + * Destroy the object and free the memory + * + */ + + ~FPSound(); + + /** + * Initializes the object, and prepare everything you need to create streams and sound effects. + * + * @returns True is everything is OK, False otherwise + */ + + bool init(); + + /** + * Allocates an object of type FPStream, and return its pointer + * + * @param streamPtr Will contain a pointer to the object you just created. + * + * @returns True is everything is OK, False otherwise + */ + + bool createStream(FPStream **streamPtr); + + /** + * Allocates an object of type FpSfx, and return its pointer + * + * @param sfxPtr Will contain a pointer to the object you just created. + * + * @returns True is everything is OK, False otherwise + */ + + bool createSfx(FPSfx **sfxPtr); + + /** + * Set the general volume + * + * @param volume Volume to set (0-63) + */ + + void setMasterVolume(int volume); + + /** + * Get the general volume + * + * @param volume Variable that will contain the volume (0-63) + */ + + void getMasterVolume(int *volume); +}; + +class FPSfx { +private: + bool _soundSupported; // True if the sound is active + bool _fileLoaded; // True is a file is opened + bool _loop; // True is sound effect should loop + int _lastVolume; + + bool _isVoice; + bool _paused; + + Audio::AudioStream *_loopStream; + Audio::RewindableAudioStream *_rewindableStream; + Audio::SoundHandle _handle; + +public: + uint32 _hEndOfBuffer; + + /** + * Check process for whether sounds have finished playing + */ + static void soundCheckProcess(CORO_PARAM, const void *param); + + /** + * Default constructor. + * + * @remarks Do *NOT* declare an object directly, but rather + * create it using FPSound::CreateSfx() + * + */ + + FPSfx(bool soundOn); + + /** + * Default Destructor. + * + * @remarks It is also stops the sound effect that may be + * currently played, and free the memory it uses. + * + */ + + ~FPSfx(); + + /** + * Releases the memory used by the object. + * + * @remarks Must be called when the object is no longer used and + * **ONLY** if the object was created by + * FPSound::CreateStream(). + * Object pointers are no longer valid after this call. + */ + + void release(); + + /** + * Opens a file and loads a sound effect. + * + * @param fileName Sfx filename + * @param codec CODEC used to uncompress the samples + * + * @returns True is everything is OK, False otherwise + */ + + bool loadFile(const char *fileName, uint32 codec = FPCODEC_RAW); + bool loadWave(Common::SeekableReadStream *stream); + bool loadVoiceFromVDB(Common::File &vdbFP); + + /** + * Play the Sfx in memory. + * + * @returns True is everything is OK, False otherwise + */ + + bool play(); + + /** + * Stops a Sfx. + * + * @returns True is everything is OK, False otherwise + */ + + bool stop(); + + /** + * Pauses a Sfx. + * + */ + + void setPause(bool pause); + + /** + * Enables or disables the Sfx loop. + * + * @param loop True to enable the loop, False to disable + * + * @remarks The loop must be activated BEFORE the sfx starts + * playing. Any changes made during the play will have + * no effect until the sfx is stopped then played again. + */ + + void setLoop(bool loop); + + /** + * Change the volume of Sfx + * + * @param volume Volume to be set (0-63) + * + */ + + void setVolume(int volume); + + /** + * Gets the Sfx volume + * + * @param volumePtr Will contain the current Sfx volume + * + */ + + void getVolume(int *volumePtr); + + /** + * Returns true if the underlying sound has ended + */ + bool endOfBuffer() const; +}; + +class FPStream { +private: + uint32 _bufferSize; // Buffer size (bytes) + uint32 _size; // Stream size (bytes) + uint32 _codec; // CODEC used + + Common::File _file; // File handle used for the stream + + bool _soundSupported; // True if the sound is active + bool _fileLoaded; // True if the file is open + bool _loop; // True if the stream should loop + bool _doFadeOut; // True if fade out is required + bool _syncExit; + bool _paused; + int _lastVolume; + FPStream *_syncToPlay; + + Audio::AudioStream *_loopStream; + Audio::RewindableAudioStream *_rewindableStream; + Audio::SoundHandle _handle; + +public: + + /** + * Default constructor. + * + * @remarks Do *NOT* declare an object directly, but rather + * create it using FPSound::CreateStream() + */ + + FPStream(bool soundOn); + + /** + * Default destructor. + * + * @remarks It calls CloseFile() if needed. + */ + + ~FPStream(); + + /** + * Releases the memory object. + * + * @remarks Must be called when the object is no longer used + * and **ONLY** if the object was created by + * FPSound::CreateStream(). + * Object pointers are no longer valid after this call. + */ + + void release(); + + /** + * Opens a file stream + * + * @param fileName Filename to be opened + * @param codec CODEC to be used to uncompress samples + * + * @returns True is everything is OK, False otherwise + */ + + bool loadFile(const Common::String &fileName, uint32 codec = FPCODEC_RAW, int sync = 2000); + + /** + * Closes a file stream (opened or not). + * + * @returns For safety, the destructor calls unloadFile() if it has not + * been mentioned explicitly. + * + * @remarks It is necessary to call this function to free the + * memory used by the stream. + */ + + bool unloadFile(); + + /** + * Play the stream. + * + * @returns True is everything is OK, False otherwise + */ + + bool play(); + void playFast(); + + /** + * Closes the stream. + * + * @returns True is everything is OK, False otherwise + */ + + bool stop(); + void waitForSync(FPStream *toPlay); + + /** + * Pause sound effect + * + * @param pause True enables pause, False disables it + */ + + void setPause(bool pause); + + /** + * Unables or disables stream loop. + * + * @param loop True enable loop, False disables it + * + * @remarks The loop must be activated BEFORE the stream starts + * playing. Any changes made during the play will have no + * effect until the stream is stopped then played again. + */ + + void setLoop(bool loop); + + /** + * Change the volume of the stream + * + * @param volume Volume to be set (0-63) + */ + + void setVolume(int volume); + + /** + * Gets the volume of the stream + * + * @param volumePtr Variable that will contain the current volume + * + */ + + void getVolume(int *volumePtr); +}; + +} // End of namespace Tony + +#endif diff --git a/engines/tony/tony.cpp b/engines/tony/tony.cpp new file mode 100644 index 0000000000..43a2f639d9 --- /dev/null +++ b/engines/tony/tony.cpp @@ -0,0 +1,793 @@ +/* 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 "common/scummsys.h" +#include "common/algorithm.h" +#include "common/config-manager.h" +#include "common/debug-channels.h" +#include "common/events.h" +#include "common/file.h" +#include "common/installshield_cab.h" +#include "tony/tony.h" +#include "tony/custom.h" +#include "tony/debugger.h" +#include "tony/game.h" +#include "tony/mpal/mpal.h" + +namespace Tony { + +TonyEngine *g_vm; + +TonyEngine::TonyEngine(OSystem *syst, const TonyGameDescription *gameDesc) : Engine(syst), + _gameDescription(gameDesc), _randomSource("tony") { + g_vm = this; + _loadSlotNumber = -1; + + // Set the up the debugger + _debugger = new Debugger(); + DebugMan.addDebugChannel(kTonyDebugAnimations, "animations", "Animations debugging"); + DebugMan.addDebugChannel(kTonyDebugActions, "actions", "Actions debugging"); + DebugMan.addDebugChannel(kTonyDebugSound, "sound", "Sound debugging"); + DebugMan.addDebugChannel(kTonyDebugMusic, "music", "Music debugging"); + + // Add folders to the search directory list + const Common::FSNode gameDataDir(ConfMan.get("path")); + SearchMan.addSubDirectoryMatching(gameDataDir, "Voices"); + SearchMan.addSubDirectoryMatching(gameDataDir, "Roasted"); + SearchMan.addSubDirectoryMatching(gameDataDir, "Music"); + SearchMan.addSubDirectoryMatching(gameDataDir, "Music/utilsfx"); + SearchMan.addSubDirectoryMatching(gameDataDir, "Music/Layer"); + + // Set up load slot number + _initialLoadSlotNumber = -1; + if (ConfMan.hasKey("save_slot")) { + int slotNumber = ConfMan.getInt("save_slot"); + if (slotNumber >= 0 && slotNumber <= 99) + _initialLoadSlotNumber = slotNumber; + } + + // Load the ScummVM sound settings + syncSoundSettings(); + + _hEndOfFrame = 0; + for (int i = 0; i < 6; i++) + _stream[i] = NULL; + for (int i = 0; i < MAX_SFX_CHANNELS; i++) { + _sfx[i] = NULL; + _utilSfx[i] = NULL; + } + _bPaused = false; + _bDrawLocation = false; + _startTime = 0; + _curThumbnail = NULL; + _bQuitNow = false; + _bTimeFreezed = false; + _nTimeFreezed = 0; +} + +TonyEngine::~TonyEngine() { + // Close the voice database + closeVoiceDatabase(); + + // Reset the coroutine scheduler + CoroScheduler.reset(); + CoroScheduler.setResourceCallback(NULL); + + delete _debugger; +} + +/** + * Run the game + */ +Common::Error TonyEngine::run() { + Common::ErrorCode result = init(); + if (result != Common::kNoError) + return result; + + play(); + close(); + + return Common::kNoError; +} + +/** + * Initialize the game + */ +Common::ErrorCode TonyEngine::init() { + // Load DAT file (used by font manager) + if (!loadTonyDat()) + return Common::kUnknownError; + + if (isCompressed()) { + Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember("data1.cab"); + if (!stream) + error("Failed to open data1.cab"); + + Common::Archive *cabinet = Common::makeInstallShieldArchive(stream); + if (!cabinet) + error("Failed to parse data1.cab"); + + SearchMan.add("data1.cab", cabinet); + } + + _hEndOfFrame = CoroScheduler.createEvent(false, false); + + _bPaused = false; + _bDrawLocation = true; + _startTime = g_system->getMillis(); + + // Init static class fields + RMText::initStatics(); + RMTony::initStatics(); + + // Reset the scheduler + CoroScheduler.reset(); + + // Initialize the graphics window + _window.init(); + + // Initialize the function list + Common::fill(_funcList, _funcList + 300, (LPCUSTOMFUNCTION)NULL); + initCustomFunctionMap(); + + // Initializes MPAL system, passing the custom functions list + Common::File f; + if (!f.open("ROASTED.MPC")) + return Common::kReadingFailed; + f.close(); + + if (!mpalInit("ROASTED.MPC", "ROASTED.MPR", _funcList, _funcListStrings)) + return Common::kUnknownError; + + // Initialize the update resources + _resUpdate.init("ROASTED.MPU"); + + // Initialize the music + initMusic(); + + // Initialize the voices database + if (!openVoiceDatabase()) + return Common::kReadingFailed; + + // Initialize the boxes + _theBoxes.init(); + + // Link to the custom graphics engine + _theEngine.initCustomDll(); + _theEngine.init(); + + // Allocate space for thumbnails when saving the game + _curThumbnail = new uint16[160 * 120]; + + _bQuitNow = false; + + return Common::kNoError; +} + +bool TonyEngine::loadTonyDat() { + Common::String msg; + Common::File in; + + in.open("tony.dat"); + + if (!in.isOpen()) { + msg = "You're missing the 'tony.dat' file. Get it from the ScummVM website"; + GUIErrorMessage(msg); + warning("%s", msg.c_str()); + return false; + } + + // Read header + char buf[4+1]; + in.read(buf, 4); + buf[4] = '\0'; + + if (strcmp(buf, "TONY")) { + msg = "File 'tony.dat' is corrupt. Get it from the ScummVM website"; + GUIErrorMessage(msg); + warning("%s", msg.c_str()); + return false; + } + + int majVer = in.readByte(); + int minVer = in.readByte(); + + if ((majVer != TONY_DAT_VER_MAJ) || (minVer != TONY_DAT_VER_MIN)) { + msg = Common::String::format("File 'tony.dat' is wrong version. Expected %d.%d but got %d.%d. Get it from the ScummVM website", TONY_DAT_VER_MAJ, TONY_DAT_VER_MIN, majVer, minVer); + GUIErrorMessage(msg); + warning("%s", msg.c_str()); + + return false; + } + + int expectedLangVariant = -1; + switch (g_vm->getLanguage()) { + case Common::IT_ITA: + case Common::EN_ANY: + expectedLangVariant = 0; + break; + case Common::PL_POL: + expectedLangVariant = 1; + break; + case Common::RU_RUS: + expectedLangVariant = 2; + break; + case Common::CZ_CZE: + expectedLangVariant = 3; + break; + case Common::FR_FRA: + expectedLangVariant = 4; + break; + case Common::DE_DEU: + expectedLangVariant = 5; + break; + default: + warning("Unhandled language, falling back to English/Italian fonts."); + expectedLangVariant = 0; + break; + } + + int numVariant = in.readUint16BE(); + if (expectedLangVariant > numVariant - 1) { + msg = Common::String::format("Font variant not present in 'tony.dat'. Get it from the ScummVM website"); + GUIErrorMessage(msg); + warning("%s", msg.c_str()); + + return false; + } + + in.seek(in.pos() + (2 * 256 * 8 * expectedLangVariant)); + for (int i = 0; i < 256; i++) { + _cTableDialog[i] = in.readSint16BE(); + _lTableDialog[i] = in.readSint16BE(); + _cTableMacc[i] = in.readSint16BE(); + _lTableMacc[i] = in.readSint16BE(); + _cTableCred[i] = in.readSint16BE(); + _lTableCred[i] = in.readSint16BE(); + _cTableObj[i] = in.readSint16BE(); + _lTableObj[i] = in.readSint16BE(); + } + + return true; +} + +void TonyEngine::initCustomFunctionMap() { + INIT_CUSTOM_FUNCTION(_funcList, _funcListStrings); +} + +/** + * Display an error message + */ +void TonyEngine::GUIError(const Common::String &msg) { + GUIErrorMessage(msg); +} + +void TonyEngine::playMusic(int nChannel, const Common::String &fname, int nFX, bool bLoop, int nSync) { + if (nChannel < 4) { + if (GLOBALS._flipflop) + nChannel = nChannel + 1; + } + + switch (nFX) { + case 0: + case 1: + case 2: + _stream[nChannel]->stop(); + _stream[nChannel]->unloadFile(); + break; + + case 22: + break; + } + + if (nFX == 22) { // Sync a tempo + GLOBALS._curChannel = nChannel; + GLOBALS._nextLoop = bLoop; + GLOBALS._nextSync = nSync; + GLOBALS._nextMusic = fname; + + if (GLOBALS._flipflop) + GLOBALS._nextChannel = nChannel - 1; + else + GLOBALS._nextChannel = nChannel + 1; + + uint32 hThread = CoroScheduler.createProcess(doNextMusic, NULL, 0); + assert(hThread != CORO_INVALID_PID_VALUE); + + } else if (nFX == 44) { // Change the channel and let the first finish + if (GLOBALS._flipflop) + GLOBALS._nextChannel = nChannel - 1; + else + GLOBALS._nextChannel = nChannel + 1; + + _stream[GLOBALS._nextChannel]->stop(); + _stream[GLOBALS._nextChannel]->unloadFile(); + + if (!getIsDemo()) { + if (!_stream[GLOBALS._nextChannel]->loadFile(fname, FPCODEC_ADPCM, nSync)) + error("failed to open music file '%s'", fname.c_str()); + } else { + _stream[GLOBALS._nextChannel]->loadFile(fname, FPCODEC_ADPCM, nSync); + } + + _stream[GLOBALS._nextChannel]->setLoop(bLoop); + _stream[GLOBALS._nextChannel]->play(); + + GLOBALS._flipflop = 1 - GLOBALS._flipflop; + } else { + if (!getIsDemo()) { + if (!_stream[nChannel]->loadFile(fname, FPCODEC_ADPCM, nSync)) + error("failed to open music file '%s'", fname.c_str()); + } else { + _stream[nChannel]->loadFile(fname, FPCODEC_ADPCM, nSync); + } + + _stream[nChannel]->setLoop(bLoop); + _stream[nChannel]->play(); + } +} + +void TonyEngine::doNextMusic(CORO_PARAM, const void *param) { + CORO_BEGIN_CONTEXT; + Common::String fn; + CORO_END_CONTEXT(_ctx); + + FPStream **streams = g_vm->_stream; + + CORO_BEGIN_CODE(_ctx); + + if (!g_vm->getIsDemo()) { + if (!streams[GLOBALS._nextChannel]->loadFile(GLOBALS._nextMusic, FPCODEC_ADPCM, GLOBALS._nextSync)) + error("failed to open next music file '%s'", GLOBALS._nextMusic.c_str()); + } else { + streams[GLOBALS._nextChannel]->loadFile(GLOBALS._nextMusic, FPCODEC_ADPCM, GLOBALS._nextSync); + } + + streams[GLOBALS._nextChannel]->setLoop(GLOBALS._nextLoop); + //streams[GLOBALS._nextChannel]->prefetch(); + + streams[GLOBALS._curChannel]->waitForSync(streams[GLOBALS._nextChannel]); + + streams[GLOBALS._curChannel]->unloadFile(); + + GLOBALS._flipflop = 1 - GLOBALS._flipflop; + + CORO_END_CODE; +} + +void TonyEngine::playSFX(int nChannel, int nFX) { + if (_sfx[nChannel] == NULL) + return; + + switch (nFX) { + case 0: + _sfx[nChannel]->setLoop(false); + break; + + case 1: + _sfx[nChannel]->setLoop(true); + break; + } + + _sfx[nChannel]->play(); +} + +void TonyEngine::stopMusic(int nChannel) { + if (nChannel < 4) + _stream[nChannel + GLOBALS._flipflop]->stop(); + else + _stream[nChannel]->stop(); +} + +void TonyEngine::stopSFX(int nChannel) { + _sfx[nChannel]->stop(); +} + +void TonyEngine::playUtilSFX(int nChannel, int nFX) { + if (_utilSfx[nChannel] == NULL) + return; + + switch (nFX) { + case 0: + _utilSfx[nChannel]->setLoop(false); + break; + + case 1: + _utilSfx[nChannel]->setLoop(true); + break; + } + + _utilSfx[nChannel]->setVolume(52); + _utilSfx[nChannel]->play(); +} + +void TonyEngine::stopUtilSFX(int nChannel) { + _utilSfx[nChannel]->stop(); +} + +void TonyEngine::preloadSFX(int nChannel, const char *fn) { + if (_sfx[nChannel] != NULL) { + _sfx[nChannel]->stop(); + _sfx[nChannel]->release(); + _sfx[nChannel] = NULL; + } + + _theSound.createSfx(&_sfx[nChannel]); + + _sfx[nChannel]->loadFile(fn, FPCODEC_ADPCM); +} + +FPSfx *TonyEngine::createSFX(Common::SeekableReadStream *stream) { + FPSfx *sfx; + + _theSound.createSfx(&sfx); + sfx->loadWave(stream); + return sfx; +} + +void TonyEngine::preloadUtilSFX(int nChannel, const char *fn) { + if (_utilSfx[nChannel] != NULL) { + _utilSfx[nChannel]->stop(); + _utilSfx[nChannel]->release(); + _utilSfx[nChannel] = NULL; + } + + _theSound.createSfx(&_utilSfx[nChannel]); + + _utilSfx[nChannel]->loadFile(fn, FPCODEC_ADPCM); + _utilSfx[nChannel]->setVolume(63); +} + +void TonyEngine::unloadAllSFX() { + for (int i = 0; i < MAX_SFX_CHANNELS; i++) { + if (_sfx[i] != NULL) { + _sfx[i]->stop(); + _sfx[i]->release(); + _sfx[i] = NULL; + } + } +} + +void TonyEngine::unloadAllUtilSFX() { + for (int i = 0; i < MAX_SFX_CHANNELS; i++) { + if (_utilSfx[i] != NULL) { + _utilSfx[i]->stop(); + _utilSfx[i]->release(); + _utilSfx[i] = NULL; + } + } +} + +void TonyEngine::initMusic() { + int i; + + _theSound.init(); + _theSound.setMasterVolume(63); + + for (i = 0; i < 6; i++) + _theSound.createStream(&_stream[i]); + + for (i = 0; i < MAX_SFX_CHANNELS; i++) { + _sfx[i] = _utilSfx[i] = NULL; + } + + // Preload sound effects + preloadUtilSFX(0, "U01.ADP"); // Reversed!! + preloadUtilSFX(1, "U02.ADP"); + + // Start check processes for sound + CoroScheduler.createProcess(FPSfx::soundCheckProcess, NULL); +} + +void TonyEngine::closeMusic() { + for (int i = 0; i < 6; i++) { + _stream[i]->stop(); + _stream[i]->unloadFile(); + _stream[i]->release(); + } + + unloadAllSFX(); + unloadAllUtilSFX(); +} + +void TonyEngine::pauseSound(bool bPause) { + _theEngine.pauseSound(bPause); + + for (uint i = 0; i < 6; i++) + if (_stream[i]) + _stream[i]->setPause(bPause); + + for (uint i = 0; i < MAX_SFX_CHANNELS; i++) { + if (_sfx[i]) + _sfx[i]->setPause(bPause); + if (_utilSfx[i]) + _utilSfx[i]->setPause(bPause); + } +} + +void TonyEngine::setMusicVolume(int nChannel, int volume) { + _stream[nChannel + GLOBALS._flipflop]->setVolume(volume); +} + +int TonyEngine::getMusicVolume(int nChannel) { + int volume; + _stream[nChannel + GLOBALS._flipflop]->getVolume(&volume); + return volume; +} + +Common::String TonyEngine::getSaveStateFileName(int n) { + return Common::String::format("tony.%03d", n); +} + +void TonyEngine::autoSave(CORO_PARAM) { + CORO_BEGIN_CONTEXT; + Common::String buf; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + grabThumbnail(); + CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE); + CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE); + _ctx->buf = getSaveStateFileName(0); + _theEngine.saveState(_ctx->buf, (byte *)_curThumbnail, "Autosave"); + + CORO_END_CODE; +} + +void TonyEngine::saveState(int n, const char *name) { + Common::String buf = getSaveStateFileName(n); + _theEngine.saveState(buf.c_str(), (byte *)_curThumbnail, name); +} + +void TonyEngine::loadState(CORO_PARAM, int n) { + CORO_BEGIN_CONTEXT; + Common::String buf; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + _ctx->buf = getSaveStateFileName(n); + CORO_INVOKE_1(_theEngine.loadState, _ctx->buf.c_str()); + + CORO_END_CODE; +} + +bool TonyEngine::openVoiceDatabase() { + char id[4]; + uint32 numfiles; + + // Open the voices database + if (!_vdbFP.open("voices.vdb")) + return false; + + _vdbFP.seek(-8, SEEK_END); + numfiles = _vdbFP.readUint32LE(); + _vdbFP.read(id, 4); + + if (id[0] != 'V' || id[1] != 'D' || id[2] != 'B' || id[3] != '1') { + _vdbFP.close(); + return false; + } + + // Read in the index + _vdbFP.seek(-8 - (numfiles * VOICE_HEADER_SIZE), SEEK_END); + + for (uint32 i = 0; i < numfiles; ++i) { + VoiceHeader vh; + vh._offset = _vdbFP.readUint32LE(); + vh._code = _vdbFP.readUint32LE(); + vh._parts = _vdbFP.readUint32LE(); + + _voices.push_back(vh); + } + + return true; +} + +void TonyEngine::closeVoiceDatabase() { + if (_vdbFP.isOpen()) + _vdbFP.close(); + + if (_voices.size() > 0) + _voices.clear(); +} + +void TonyEngine::grabThumbnail() { + _window.grabThumbnail(_curThumbnail); +} + +uint16 *TonyEngine::getThumbnail() { + return _curThumbnail; +} + +void TonyEngine::quitGame() { + _bQuitNow = true; +} + +void TonyEngine::openInitLoadMenu(CORO_PARAM) { + _theEngine.openOptionScreen(coroParam, 1); +} + +void TonyEngine::openInitOptions(CORO_PARAM) { + _theEngine.openOptionScreen(coroParam, 2); +} + +/** + * Main process for playing the game. + * + * @remarks This needs to be in a separate process, since there are some things that can briefly + * block the execution of process. For now, all ScummVm event handling is dispatched to within the context of this + * process. If it ever proves a problem, we may have to look into whether it's feasible to have it still remain + * in the outer 'main' process. + */ +void TonyEngine::playProcess(CORO_PARAM, const void *param) { + CORO_BEGIN_CONTEXT; + Common::String fn; + CORO_END_CONTEXT(_ctx); + + + CORO_BEGIN_CODE(_ctx); + + // Game loop. We rely on the outer main process to detect if a shutdown is required, + // and kill the scheudler and all the processes, including this one + for (;;) { + // If a savegame needs to be loaded, then do so + if (g_vm->_loadSlotNumber != -1 && GLOBALS._gfxEngine != NULL) { + _ctx->fn = getSaveStateFileName(g_vm->_loadSlotNumber); + CORO_INVOKE_1(GLOBALS._gfxEngine->loadState, _ctx->fn); + g_vm->_loadSlotNumber = -1; + } + + // Wait for the next frame + CORO_INVOKE_1(CoroScheduler.sleep, 50); + + // Call the engine to handle the next frame + CORO_INVOKE_1(g_vm->_theEngine.doFrame, g_vm->_bDrawLocation); + + // Warns that a frame is finished + CoroScheduler.pulseEvent(g_vm->_hEndOfFrame); + + // Handle drawing the frame + if (!g_vm->_bPaused) { + if (!g_vm->_theEngine._bWiping) + g_vm->_window.getNewFrame(g_vm->_theEngine, NULL); + else + g_vm->_window.getNewFrame(g_vm->_theEngine, &g_vm->_theEngine._rcWipeEllipse); + } + + // Paint the frame onto the screen + g_vm->_window.repaint(); + + // Signal the ScummVM debugger + g_vm->_debugger->onFrame(); + } + + CORO_END_CODE; +} + +/** + * Play the game + */ +void TonyEngine::play() { + // Create the game player process + CoroScheduler.createProcess(playProcess, NULL); + + // Loop through calling the scheduler until it's time for the game to quit + while (!shouldQuit() && !_bQuitNow) { + // Delay for a brief amount + g_system->delayMillis(10); + + // Call any scheduled processes + CoroScheduler.schedule(); + } +} + +void TonyEngine::close() { + closeMusic(); + CoroScheduler.closeEvent(_hEndOfFrame); + _theBoxes.close(); + _theEngine.close(); + _window.close(); + mpalFree(); + freeMpc(); + delete[] _curThumbnail; +} + +void TonyEngine::freezeTime() { + _bTimeFreezed = true; + _nTimeFreezed = getTime() - _startTime; +} + +void TonyEngine::unfreezeTime() { + _bTimeFreezed = false; +} + +/** + * Returns the millisecond timer + */ +uint32 TonyEngine::getTime() { + return g_system->getMillis(); +} + +bool TonyEngine::canLoadGameStateCurrently() { + return GLOBALS._gfxEngine != NULL && GLOBALS._gfxEngine->canLoadSave(); +} + +bool TonyEngine::canSaveGameStateCurrently() { + return GLOBALS._gfxEngine != NULL && GLOBALS._gfxEngine->canLoadSave(); +} + +Common::Error TonyEngine::loadGameState(int slot) { + _loadSlotNumber = slot; + return Common::kNoError; +} + +Common::Error TonyEngine::saveGameState(int slot, const Common::String &desc) { + if (!GLOBALS._gfxEngine) + return Common::kUnknownError; + + RMGfxTargetBuffer &bigBuf = *GLOBALS._gfxEngine; + RMSnapshot s; + s.grabScreenshot(bigBuf, 4, _curThumbnail); + + GLOBALS._gfxEngine->saveState(getSaveStateFileName(slot), (byte *)_curThumbnail, desc); + return Common::kNoError; +} + +void TonyEngine::syncSoundSettings() { + Engine::syncSoundSettings(); + + GLOBALS._bCfgDubbing = !ConfMan.getBool("mute") && !ConfMan.getBool("speech_mute"); + GLOBALS._bCfgSFX = !ConfMan.getBool("mute") && !ConfMan.getBool("sfx_mute"); + GLOBALS._bCfgMusic = !ConfMan.getBool("mute") && !ConfMan.getBool("music_mute"); + + GLOBALS._nCfgDubbingVolume = ConfMan.getInt("speech_volume") * 10 / 256; + GLOBALS._nCfgSFXVolume = ConfMan.getInt("sfx_volume") * 10 / 256; + GLOBALS._nCfgMusicVolume = ConfMan.getInt("music_volume") * 10 / 256; + + GLOBALS._bShowSubtitles = ConfMan.getBool("subtitles"); + GLOBALS._nCfgTextSpeed = ConfMan.getInt("talkspeed") * 10 / 256; +} + +void TonyEngine::saveSoundSettings() { + ConfMan.setBool("speech_mute", !GLOBALS._bCfgDubbing); + ConfMan.setBool("sfx_mute", !GLOBALS._bCfgSFX); + ConfMan.setBool("music_mute", !GLOBALS._bCfgMusic); + + ConfMan.setInt("speech_volume", GLOBALS._nCfgDubbingVolume * 256 / 10); + ConfMan.setInt("sfx_volume", GLOBALS._nCfgSFXVolume * 256 / 10); + ConfMan.setInt("music_volume", GLOBALS._nCfgMusicVolume * 256 / 10); + + ConfMan.setBool("subtitles", GLOBALS._bShowSubtitles); + ConfMan.setInt("talkspeed", GLOBALS._nCfgTextSpeed * 256 / 10); +} + +void TonyEngine::showLocation() { + _bDrawLocation = true; +} + +void TonyEngine::hideLocation() { + _bDrawLocation = false; +} + +} // End of namespace Tony diff --git a/engines/tony/tony.h b/engines/tony/tony.h new file mode 100644 index 0000000000..750673061d --- /dev/null +++ b/engines/tony/tony.h @@ -0,0 +1,242 @@ +/* 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 TONY_H +#define TONY_H + +#include "common/scummsys.h" +#include "common/system.h" +#include "common/array.h" +#include "common/coroutines.h" +#include "common/error.h" +#include "common/random.h" +#include "common/util.h" +#include "engines/engine.h" + +#include "tony/mpal/mpal.h" +#include "tony/mpal/memory.h" +#include "tony/debugger.h" +#include "tony/gfxengine.h" +#include "tony/loc.h" +#include "tony/utils.h" +#include "tony/window.h" +#include "tony/globals.h" + +/** + * This is the namespace of the Tony engine. + * + * Status of this engine: In Development + * + * Games using this engine: + * - Tony Tough + */ +namespace Tony { + +using namespace MPAL; + +class Globals; + +enum { + kTonyDebugAnimations = 1 << 0, + kTonyDebugActions = 1 << 1, + kTonyDebugSound = 1 << 2, + kTonyDebugMusic = 1 << 3, + kTonyDebugMPAL = 1 << 4 +}; + +#define DEBUG_BASIC 1 +#define DEBUG_INTERMEDIATE 2 +#define DEBUG_DETAILED 3 + +struct TonyGameDescription; + +#define MAX_SFX_CHANNELS 32 +#define TONY_DAT_VER_MAJ 0 +#define TONY_DAT_VER_MIN 3 + +struct VoiceHeader { + int _offset; + int _code; + int _parts; +}; +#define VOICE_HEADER_SIZE 12 + +class TonyEngine : public Engine { +private: + Common::ErrorCode init(); + bool loadTonyDat(); + void initMusic(); + void closeMusic(); + bool openVoiceDatabase(); + void closeVoiceDatabase(); + void initCustomFunctionMap(); + static void playProcess(CORO_PARAM, const void *param); + static void doNextMusic(CORO_PARAM, const void *param); + +protected: + // Engine APIs + virtual Common::Error run(); + virtual bool hasFeature(EngineFeature f) const; +public: + LPCUSTOMFUNCTION _funcList[300]; + Common::String _funcListStrings[300]; + Common::RandomSource _randomSource; + RMResUpdate _resUpdate; + uint32 _hEndOfFrame; + Common::File _vdbFP; + Common::Array<VoiceHeader> _voices; + FPSound _theSound; + Common::List<FPSfx *> _activeSfx; + Globals _globals; + Debugger *_debugger; + + int16 _cTableDialog[256]; + int16 _lTableDialog[256]; + int16 _cTableMacc[256]; + int16 _lTableMacc[256]; + int16 _cTableCred[256]; + int16 _lTableCred[256]; + int16 _cTableObj[256]; + int16 _lTableObj[256]; + + enum DataDir { + DD_BASE = 1, + DD_SAVE, + DD_SHOTS, + DD_MUSIC, + DD_LAYER, + DD_UTILSFX, + DD_VOICES, + DD_BASE2 + }; + + FPStream *_stream[6]; + FPSfx *_sfx[MAX_SFX_CHANNELS]; + FPSfx *_utilSfx[MAX_SFX_CHANNELS]; + bool _bPaused; + bool _bDrawLocation; + int _startTime; + uint16 *_curThumbnail; + int _initialLoadSlotNumber; + int _loadSlotNumber; + + // Bounding box list manager + RMGameBoxes _theBoxes; + RMWindow _window; + RMGfxEngine _theEngine; + + bool _bQuitNow; + bool _bTimeFreezed; + int _nTimeFreezed; +public: + TonyEngine(OSystem *syst, const TonyGameDescription *gameDesc); + virtual ~TonyEngine(); + + const TonyGameDescription *_gameDescription; + uint32 getFeatures() const; + Common::Language getLanguage() const; + uint16 getVersion() const; + bool getIsDemo() const; + bool isCompressed() const; + RMGfxEngine *getEngine() { + return &_theEngine; + } + void GUIError(const Common::String &msg); + + virtual bool canLoadGameStateCurrently(); + virtual bool canSaveGameStateCurrently(); + Common::Error loadGameState(int slot); + Common::Error saveGameState(int slot, const Common::String &desc); + + void play(); + void close(); + + void getDataDirectory(DataDir dir, char *path); + + void showLocation(); + void hideLocation(); + + /** + * Reads the time + */ + uint32 getTime(); + void freezeTime(); + void unfreezeTime(); + + // Music + // ****** + void playMusic(int nChannel, const Common::String &fn, int nFX, bool bLoop, int nSync); + void stopMusic(int nChannel); + + void playSFX(int nSfx, int nFX = 0); + void stopSFX(int nSfx); + + void playUtilSFX(int nSfx, int nFX = 0); + void stopUtilSFX(int nSfx); + + FPSfx *createSFX(Common::SeekableReadStream *stream); + + void preloadSFX(int nSfx, const char *fn); + void unloadAllSFX(); + + void preloadUtilSFX(int nSfx, const char *fn); + void unloadAllUtilSFX(); + + /** + * Stop all the audio + */ + void pauseSound(bool bPause); + + void setMusicVolume(int nChannel, int volume); + int getMusicVolume(int nChannel); + + /** + * Handle saving + */ + void autoSave(CORO_PARAM); + void saveState(int n, const char *name); + void loadState(CORO_PARAM, int n); + static Common::String getSaveStateFileName(int n); + + /** + * Get a thumbnail + */ + void grabThumbnail(); + uint16 *getThumbnail(); + + void quitGame(); + + void openInitLoadMenu(CORO_PARAM); + void openInitOptions(CORO_PARAM); + + virtual void syncSoundSettings(); + void saveSoundSettings(); +}; + +// Global reference to the TonyEngine object +extern TonyEngine *g_vm; + +#define GLOBALS g_vm->_globals + +} // End of namespace Tony + +#endif /* TONY_H */ diff --git a/engines/tony/tonychar.cpp b/engines/tony/tonychar.cpp new file mode 100644 index 0000000000..46c018728e --- /dev/null +++ b/engines/tony/tonychar.cpp @@ -0,0 +1,1937 @@ +/* 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. + * + */ + +/* + * This code is based on original Tony Tough source code + * + * Copyright (c) 1997-2003 Nayma Software + */ + +#include "tony/mpal/memory.h" +#include "tony/mpal/mpalutils.h" +#include "tony/game.h" +#include "tony/tonychar.h" +#include "tony/tony.h" + +namespace Tony { + +bool RMTony::_bAction = false; + +void RMTony::initStatics() { + _bAction = false; +} + +RMTony::RMTony() { + _bShow = false; + _bShowShadow = false; + _bBodyFront = false; + _bActionPending = false; + _actionItem = NULL; + _action = 0; + _actionParm = 0; + _bShepherdess = false; + _bIsStaticTalk = false; + _bIsTalking = false; + _nPatB4Talking = 0; + _nTalkType = TALK_NORMAL; + _talkDirection = UP; + _nTimeLastStep = 0; + _hActionThread = CORO_INVALID_PID_VALUE; +} + +void RMTony::waitEndOfAction(CORO_PARAM, const void *param) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + uint32 pid = *(const uint32 *)param; + + CORO_BEGIN_CODE(_ctx); + + CORO_INVOKE_2(CoroScheduler.waitForSingleObject, pid, CORO_INFINITE); + + _bAction = false; + + CORO_END_CODE; +} + +RMGfxSourceBuffer *RMTony::newItemSpriteBuffer(int dimx, int dimy, bool bPreRLE) { + RMGfxSourceBuffer8RLE *spr; + + assert(_cm == CM_256); + spr = new RMGfxSourceBuffer8RLEByteAA; + spr->setAlphaBlendColor(1); + if (bPreRLE) + spr->setAlreadyCompressed(); + return spr; +} + +void RMTony::init() { + RMRes tony(0); + RMRes body(9999); + + // Tony is shown by default + _bShow = _bShowShadow = true; + + // No action pending + _bActionPending = false; + _bAction = false; + + _bShepherdess = false; + _bIsTalking = false; + _bIsStaticTalk = false; + + // Opens the buffer + Common::SeekableReadStream *ds = tony.getReadStream(); + + // Reads his details from the stream + readFromStream(*ds, true); + + // Closes the buffer + delete ds; + + // Reads Tony's body + ds = body.getReadStream(); + _body.readFromStream(*ds, true); + delete ds; + _body.setPattern(0); + + _nTimeLastStep = g_vm->getTime(); +} + + +void RMTony::close() { + // Deallocation of missing item + //_shadow.destroy(); +} + +void RMTony::doFrame(CORO_PARAM, RMGfxTargetBuffer *bigBuf, int curLoc) { + CORO_BEGIN_CONTEXT; + int time; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + if (!_nInList && _bShow) + bigBuf->addPrim(new RMGfxPrimitive(this)); + + setSpeed(GLOBALS._nCfgTonySpeed); + + // Runs the normal character movement + _ctx->time = g_vm->getTime(); + + do { + _nTimeLastStep += (1000 / 40); + CORO_INVOKE_2(RMCharacter::doFrame, bigBuf, curLoc); + } while (_ctx->time > _nTimeLastStep + (1000 / 40)); + + // Check if we are at the end of a path + if (endOfPath() && _bActionPending) { + // Must perform the action on which we clicked + _bActionPending = false; + } + + if (_bIsTalking || _bIsStaticTalk) + _body.doFrame(bigBuf, false); + + CORO_END_CODE; +} + +void RMTony::show() { + _bShow = true; + _bShowShadow = true; +} + +void RMTony::hide(bool bShowShadow) { + _bShow = false; + _bShowShadow = bShowShadow; +} + +void RMTony::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + // Call the Draw() of the parent class if Tony is visible + if (_bShow && _bDrawNow) { + if (_bBodyFront) { + prim->getDst().setEmpty(); + prim->getDst().offset(-44, -134); + if (_bShepherdess) + prim->getDst().offset(1, 4); + CORO_INVOKE_2(RMCharacter::draw, bigBuf, prim); + } + + if (_bIsTalking || _bIsStaticTalk) { + // Offest direction from scrolling + prim->getDst().setEmpty(); + prim->getDst().offset(-_curScroll); + prim->getDst().offset(_pos); + prim->getDst().offset(-44, -134); + prim->getDst() += _nBodyOffset; + CORO_INVOKE_2(_body.draw, bigBuf, prim); + } + + if (!_bBodyFront) { + prim->getDst().setEmpty(); + prim->getDst().offset(-44, -134); + if (_bShepherdess) + prim->getDst().offset(0, 3); + CORO_INVOKE_2(RMCharacter::draw, bigBuf, prim); + } + } + + CORO_END_CODE; +} + +void RMTony::moveAndDoAction(CORO_PARAM, RMPoint dst, RMItem *item, int nAction, int nActionParm) { + CORO_BEGIN_CONTEXT; + bool result; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + // Makes normal movement, but remember if you must then perform an action + if (item == NULL) { + _bActionPending = false; + _actionItem = NULL; + } else { + _actionItem = item; + _action = nAction; + _actionParm = nActionParm; + _bActionPending = true; + } + + CORO_INVOKE_2(RMCharacter::move, dst, &_ctx->result); + if (!_ctx->result) { + _bActionPending = false; + _actionItem = NULL; + } + + CORO_END_CODE; +} + +void RMTony::executeAction(int nAction, int nActionItem, int nParm) { + uint32 pid; + + if (nAction == TA_COMBINE) { + pid = mpalQueryDoAction(TA_COMBINE, nParm, nActionItem); + + // If you failed the combine, we have RECEIVECOMBINE as a fallback + if (pid == CORO_INVALID_PID_VALUE) { + pid = mpalQueryDoAction(TA_RECEIVECOMBINE, nActionItem, nParm); + + // If you failed with that, go with the generic + // @@@ CombineGive! + if (pid == CORO_INVALID_PID_VALUE) { + pid = mpalQueryDoAction(TA_COMBINE, nParm, 0); + + if (pid == CORO_INVALID_PID_VALUE) { + pid = mpalQueryDoAction(TA_RECEIVECOMBINE, nActionItem, 0); + } + } + } + } else { + // Perform the action + pid = mpalQueryDoAction(nAction, nActionItem, 0); + } + + if (pid != CORO_INVALID_PID_VALUE) { + _bAction = true; + CoroScheduler.createProcess(waitEndOfAction, &pid, sizeof(uint32)); + _hActionThread = pid; + } else if (nAction != TA_GOTO) { + if (nAction == TA_TALK) { + pid = mpalQueryDoAction(6, 1, 0); + _bAction = true; + CoroScheduler.createProcess(waitEndOfAction, &pid, sizeof(uint32)); + _hActionThread = pid; + } else if (nAction == TA_PERORATE) { + pid = mpalQueryDoAction(7, 1, 0); + _bAction = true; + CoroScheduler.createProcess(waitEndOfAction, &pid, sizeof(uint32)); + _hActionThread = pid; + } else { + pid = mpalQueryDoAction(5, 1, 0); + _bAction = true; + CoroScheduler.createProcess(waitEndOfAction, &pid, sizeof(uint32)); + _hActionThread = pid; + } + } +} + +void RMTony::stopNoAction(CORO_PARAM) { + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + if (_bAction) + CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _hActionThread, CORO_INFINITE); + + _bActionPending = false; + _actionItem = NULL; + CORO_INVOKE_0(stop); + + CORO_END_CODE; +} + +void RMTony::stop(CORO_PARAM) { + CORO_BEGIN_CONTEXT; + uint32 pid; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + if (_actionItem != NULL) { + // Call MPAL to choose the direction + _ctx->pid = mpalQueryDoAction(21, _actionItem->mpalCode(), 0); + + if (_ctx->pid == CORO_INVALID_PID_VALUE) + CORO_INVOKE_0(RMCharacter::stop); + else { + _bNeedToStop = false; // If we make the OnWhichDirection, we don't need at least after the Stop(). + _bMoving = false; + CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _ctx->pid, CORO_INFINITE); // @@@ Put an assert after 10 seconds + } + } else { + CORO_INVOKE_0(RMCharacter::stop); + } + + if (!_bActionPending) + return; + + _bActionPending = false; + + executeAction(_action, _actionItem->mpalCode(), _actionParm); + + _actionItem = NULL; + + CORO_END_CODE; +} + +int RMTony::getCurPattern() { + int nPatt = RMCharacter::getCurPattern(); + + if (!_bShepherdess) + return nPatt; + + switch (nPatt) { + case PAT_PAST_STANDUP: + return PAT_STANDUP; + case PAT_PAST_STANDDOWN: + return PAT_STANDDOWN; + case PAT_PAST_STANDLEFT: + return PAT_STANDLEFT; + case PAT_PAST_STANDRIGHT: + return PAT_STANDRIGHT; + case PAT_PAST_WALKUP: + return PAT_WALKUP; + case PAT_PAST_WALKDOWN: + return PAT_WALKDOWN; + case PAT_PAST_WALKLEFT: + return PAT_WALKLEFT; + case PAT_PAST_WALKRIGHT: + return PAT_WALKRIGHT; + } + + return nPatt; +} + +void RMTony::setPattern(int nPatt, bool bPlayP0) { + if (_bShepherdess) { + switch (nPatt) { + case PAT_STANDUP: + nPatt = PAT_PAST_STANDUP; + break; + case PAT_STANDDOWN: + nPatt = PAT_PAST_STANDDOWN; + break; + case PAT_STANDLEFT: + nPatt = PAT_PAST_STANDLEFT; + break; + case PAT_STANDRIGHT: + nPatt = PAT_PAST_STANDRIGHT; + break; + case PAT_WALKUP: + nPatt = PAT_PAST_WALKUP; + break; + case PAT_WALKDOWN: + nPatt = PAT_PAST_WALKDOWN; + break; + case PAT_WALKLEFT: + nPatt = PAT_PAST_WALKLEFT; + break; + case PAT_WALKRIGHT: + nPatt = PAT_PAST_WALKRIGHT; + break; + } + } + + RMCharacter::setPattern(nPatt, bPlayP0); +} + +void RMTony::take(int nWhere, int nPart) { + if (nPart == 0) { + switch (getCurPattern()) { + case PAT_STANDDOWN: + assert(0); // Not while you're doing a StandDown + break; + + case PAT_STANDUP: + switch (nWhere) { + case 0: + setPattern(PAT_TAKEUP_UP1); + break; + case 1: + setPattern(PAT_TAKEUP_MID1); + break; + case 2: + setPattern(PAT_TAKEUP_DOWN1); + break; + } + break; + + case PAT_STANDRIGHT: + switch (nWhere) { + case 0: + setPattern(PAT_TAKERIGHT_UP1); + break; + case 1: + setPattern(PAT_TAKERIGHT_MID1); + break; + case 2: + setPattern(PAT_TAKERIGHT_DOWN1); + break; + } + break; + + case PAT_STANDLEFT: + switch (nWhere) { + case 0: + setPattern(PAT_TAKELEFT_UP1); + break; + case 1: + setPattern(PAT_TAKELEFT_MID1); + break; + case 2: + setPattern(PAT_TAKELEFT_DOWN1); + break; + } + break; + } + } else if (nPart == 1) { + setPattern(getCurPattern() + 1); + } else if (nPart == 2) { + switch (getCurPattern()) { + case PAT_TAKEUP_UP2: + case PAT_TAKEUP_MID2: + case PAT_TAKEUP_DOWN2: + setPattern(PAT_STANDUP); + break; + + case PAT_TAKELEFT_UP2: + case PAT_TAKELEFT_MID2: + case PAT_TAKELEFT_DOWN2: + setPattern(PAT_STANDLEFT); + break; + + case PAT_TAKERIGHT_UP2: + case PAT_TAKERIGHT_MID2: + case PAT_TAKERIGHT_DOWN2: + setPattern(PAT_STANDRIGHT); + break; + } + } +} + +void RMTony::put(int nWhere, int nPart) { + if (nPart == 0) { + switch (getCurPattern()) { + case PAT_STANDDOWN: + break; + + case PAT_STANDUP: + switch (nWhere) { + case 0: + setPattern(PAT_PUTUP_UP1); + break; + case 1: + setPattern(PAT_PUTUP_MID1); + break; + case 2: + setPattern(PAT_PUTUP_DOWN1); + break; + } + break; + + case PAT_STANDRIGHT: + switch (nWhere) { + case 0: + setPattern(PAT_PUTRIGHT_UP1); + break; + case 1: + setPattern(PAT_PUTRIGHT_MID1); + break; + case 2: + setPattern(PAT_PUTRIGHT_DOWN1); + break; + } + break; + + case PAT_STANDLEFT: + switch (nWhere) { + case 0: + setPattern(PAT_PUTLEFT_UP1); + break; + case 1: + setPattern(PAT_PUTLEFT_MID1); + break; + case 2: + setPattern(PAT_PUTLEFT_DOWN1); + break; + } + break; + } + } else if (nPart == 1) { + setPattern(getCurPattern() + 1); + } else if (nPart == 2) { + switch (getCurPattern()) { + case PAT_PUTUP_UP2: + case PAT_PUTUP_MID2: + case PAT_PUTUP_DOWN2: + setPattern(PAT_STANDUP); + break; + + case PAT_PUTLEFT_UP2: + case PAT_PUTLEFT_MID2: + case PAT_PUTLEFT_DOWN2: + setPattern(PAT_STANDLEFT); + break; + + case PAT_PUTRIGHT_UP2: + case PAT_PUTRIGHT_MID2: + case PAT_PUTRIGHT_DOWN2: + setPattern(PAT_STANDRIGHT); + break; + } + } +} + +bool RMTony::startTalkCalculate(CharacterTalkType nTalkType, int &headStartPat, int &bodyStartPat, + int &headLoopPat, int &bodyLoopPat) { + assert(!_bIsTalking); + + _bIsTalking = true; + _nPatB4Talking = getCurPattern(); + _nTalkType = nTalkType; + + // Set the direction of speech ONLY if we are not in a static animation (since it would have already been done) + if (!_bIsStaticTalk) { + switch (_nPatB4Talking) { + case PAT_STANDDOWN: + _talkDirection = DOWN; + break; + + case PAT_TAKELEFT_UP2: + case PAT_TAKELEFT_MID2: + case PAT_TAKELEFT_DOWN2: + case PAT_GETUPLEFT: + case PAT_STANDLEFT: + _talkDirection = LEFT; + break; + + case PAT_TAKERIGHT_UP2: + case PAT_TAKERIGHT_MID2: + case PAT_TAKERIGHT_DOWN2: + case PAT_GETUPRIGHT: + case PAT_STANDRIGHT: + _talkDirection = RIGHT; + break; + + case PAT_TAKEUP_UP2: + case PAT_TAKEUP_MID2: + case PAT_TAKEUP_DOWN2: + case PAT_STANDUP: + _talkDirection = UP; + break; + } + + // Puts the body in front by default + _bBodyFront = true; + } + + if (_bShepherdess) { + // Talking whilst a shepherdess + switch (_talkDirection) { + case UP: + setPattern(PAT_PAST_TALKUP); + break; + + case DOWN: + setPattern(PAT_PAST_TALKDOWN); + break; + + case LEFT: + setPattern(PAT_PAST_TALKLEFT); + break; + + case RIGHT: + setPattern(PAT_PAST_TALKRIGHT); + break; + } + return false; + } + + headStartPat = bodyStartPat = 0; + bodyLoopPat = 0; + + switch (nTalkType) { + case TALK_NORMAL: + _bBodyFront = false; + headStartPat = 0; + bodyStartPat = 0; + + switch (_talkDirection) { + case DOWN: + headLoopPat = PAT_TALK_DOWN; + bodyLoopPat = BPAT_STANDDOWN; + _nBodyOffset.set(4, 53); + break; + + case LEFT: + headLoopPat = PAT_TALK_LEFT; + bodyLoopPat = BPAT_STANDLEFT; + _nBodyOffset.set(6, 56); + break; + + case RIGHT: + headLoopPat = PAT_TALK_RIGHT; + bodyLoopPat = BPAT_STANDRIGHT; + _nBodyOffset.set(6, 56); + break; + + case UP: + headLoopPat = PAT_TALK_UP; + bodyLoopPat = BPAT_STANDUP; + _nBodyOffset.set(6, 53); + break; + } + break; + + case TALK_HIPS: + _bBodyFront = false; + switch (_talkDirection) { + case UP: + _nBodyOffset.set(2, 42); + headStartPat = PAT_HEAD_UP; + bodyStartPat = BPAT_HIPSUP_START; + headLoopPat = PAT_TALK_UP; + bodyLoopPat = BPAT_HIPSUP_LOOP; + break; + + case DOWN: + _nBodyOffset.set(2, 48); + headStartPat = PAT_HEAD_DOWN; + bodyStartPat = BPAT_HIPSDOWN_START; + headLoopPat = PAT_TALK_DOWN; + bodyLoopPat = BPAT_HIPSDOWN_LOOP; + break; + + case LEFT: + _nBodyOffset.set(-3, 53); + headStartPat = PAT_HEAD_LEFT; + bodyStartPat = BPAT_HIPSLEFT_START; + headLoopPat = PAT_TALK_LEFT; + bodyLoopPat = BPAT_HIPSLEFT_LOOP; + break; + + case RIGHT: + _nBodyOffset.set(2, 53); + headStartPat = PAT_HEAD_RIGHT; + bodyStartPat = BPAT_HIPSRIGHT_START; + headLoopPat = PAT_TALK_RIGHT; + bodyLoopPat = BPAT_HIPSRIGHT_LOOP; + break; + } + break; + + case TALK_SING: + _nBodyOffset.set(-10, 25); + headStartPat = PAT_HEAD_LEFT; + bodyStartPat = BPAT_SINGLEFT_START; + headLoopPat = PAT_TALK_LEFT; + bodyLoopPat = BPAT_SINGLEFT_LOOP; + break; + + case TALK_LAUGH: + _bBodyFront = false; + switch (_talkDirection) { + case UP: + case DOWN: + case LEFT: + _nBodyOffset.set(6, 56); + headStartPat = PAT_LAUGHLEFT_START; + bodyStartPat = BPAT_STANDLEFT; + headLoopPat = PAT_LAUGHLEFT_LOOP; + bodyLoopPat = BPAT_LAUGHLEFT; + break; + + case RIGHT: + _nBodyOffset.set(6, 56); + headStartPat = PAT_LAUGHRIGHT_START; + bodyStartPat = BPAT_STANDRIGHT; + headLoopPat = PAT_LAUGHRIGHT_LOOP; + bodyLoopPat = BPAT_LAUGHRIGHT; + break; + } + break; + + case TALK_LAUGH2: + _bBodyFront = false; + switch (_talkDirection) { + case UP: + case DOWN: + case LEFT: + _nBodyOffset.set(6, 56); + headStartPat = PAT_LAUGHLEFT_START; + bodyStartPat = BPAT_STANDLEFT; + headLoopPat = PAT_LAUGHLEFT_LOOP; + break; + + case RIGHT: + _nBodyOffset.set(6, 56); + headStartPat = PAT_LAUGHRIGHT_START; + bodyStartPat = BPAT_STANDRIGHT; + headLoopPat = PAT_LAUGHRIGHT_LOOP; + bodyLoopPat = BPAT_LAUGHRIGHT; + break; + } + break; + + case TALK_INDICATE: + switch (_talkDirection) { + case UP: + case DOWN: + case LEFT: + _nBodyOffset.set(-4, 40); + headLoopPat = PAT_TALK_LEFT; + bodyLoopPat = BPAT_INDICATELEFT; + break; + + case RIGHT: + _nBodyOffset.set(5, 40); + headLoopPat = PAT_TALK_RIGHT; + bodyLoopPat = BPAT_INDICATERIGHT; + break; + } + break; + + case TALK_SCARED: + switch (_talkDirection) { + case UP: + _nBodyOffset.set(-4, -11); + headStartPat = PAT_HEAD_UP; + bodyStartPat = BPAT_SCAREDUP_START; + headLoopPat = PAT_TALK_UP; + bodyLoopPat = BPAT_SCAREDUP_LOOP; + break; + + case DOWN: + _nBodyOffset.set(-5, 45); + headStartPat = PAT_SCAREDDOWN_START; + bodyStartPat = BPAT_SCAREDDOWN_START; + headLoopPat = PAT_SCAREDDOWN_LOOP; + bodyLoopPat = BPAT_SCAREDDOWN_LOOP; + break; + + case RIGHT: + _nBodyOffset.set(-4, 41); + headStartPat = PAT_SCAREDRIGHT_START; + bodyStartPat = BPAT_SCAREDRIGHT_START; + headLoopPat = PAT_SCAREDRIGHT_LOOP; + bodyLoopPat = BPAT_SCAREDRIGHT_LOOP; + break; + + case LEFT: + _nBodyOffset.set(-10, 41); + headStartPat = PAT_SCAREDLEFT_START; + bodyStartPat = BPAT_SCAREDLEFT_START; + headLoopPat = PAT_SCAREDLEFT_LOOP; + bodyLoopPat = BPAT_SCAREDLEFT_LOOP; + break; + } + break; + + case TALK_SCARED2: + _bBodyFront = false; + switch (_talkDirection) { + case UP: + bodyStartPat = BPAT_STANDUP; + bodyLoopPat = BPAT_STANDUP; + _nBodyOffset.set(6, 53); + + headStartPat = PAT_HEAD_UP; + headLoopPat = PAT_TALK_UP; + break; + + case DOWN: + bodyStartPat = BPAT_STANDDOWN; + bodyLoopPat = BPAT_STANDDOWN; + _nBodyOffset.set(4, 53); + + headStartPat = PAT_SCAREDDOWN_START; + headLoopPat = PAT_SCAREDDOWN_LOOP; + break; + + case RIGHT: + bodyStartPat = BPAT_STANDRIGHT; + bodyLoopPat = BPAT_STANDRIGHT; + _nBodyOffset.set(6, 56); + + headStartPat = PAT_SCAREDRIGHT_START; + headLoopPat = PAT_SCAREDRIGHT_LOOP; + break; + + case LEFT: + bodyStartPat = BPAT_STANDLEFT; + bodyLoopPat = BPAT_STANDLEFT; + _nBodyOffset.set(6, 56); + + headStartPat = PAT_SCAREDLEFT_START; + headLoopPat = PAT_SCAREDLEFT_LOOP; + break; + } + break; + + case TALK_WITHGLASSES: + _nBodyOffset.set(4, 53); + headLoopPat = PAT_TALK_DOWN; + bodyLoopPat = BPAT_GLASS; + break; + case TALK_WITHWORM: + _nBodyOffset.set(9, 56); + headLoopPat = PAT_TALK_RIGHT; + bodyLoopPat = BPAT_WORM; + break; + case TALK_WITHHAMMER: + _nBodyOffset.set(6, 56); + headLoopPat = PAT_TALK_LEFT; + bodyLoopPat = BPAT_HAMMER; + break; + case TALK_WITHROPE: + _nBodyOffset.set(-3, 38); + headLoopPat = PAT_TALK_RIGHT; + bodyLoopPat = BPAT_ROPE; + break; + case TALK_WITHSECRETARY: + _nBodyOffset.set(-17, 12); + headLoopPat = PAT_TALK_RIGHT; + bodyLoopPat = BPAT_WITHSECRETARY; + break; + + case TALK_WITHRABBIT: + switch (_talkDirection) { + case LEFT: + case UP: + _nBodyOffset.set(-21, -5); + bodyStartPat = BPAT_WITHRABBITLEFT_START; + headLoopPat = PAT_TALK_LEFT; + bodyLoopPat = BPAT_WITHRABBITLEFT_LOOP; + break; + + case DOWN: + case RIGHT: + _nBodyOffset.set(-4, -5); + bodyStartPat = BPAT_WITHRABBITRIGHT_START; + headLoopPat = PAT_TALK_RIGHT; + bodyLoopPat = BPAT_WITHRABBITRIGHT_LOOP; + break; + } + break; + + case TALK_WITHRECIPE: + switch (_talkDirection) { + case LEFT: + case UP: + _nBodyOffset.set(-61, -7); + bodyStartPat = BPAT_WITHRECIPELEFT_START; + headLoopPat = PAT_TALK_LEFT; + bodyLoopPat = BPAT_WITHRECIPELEFT_LOOP; + break; + + case DOWN: + case RIGHT: + _nBodyOffset.set(-5, -7); + bodyStartPat = BPAT_WITHRECIPERIGHT_START; + headLoopPat = PAT_TALK_RIGHT; + bodyLoopPat = BPAT_WITHRECIPERIGHT_LOOP; + break; + } + break; + + case TALK_WITHCARDS: + switch (_talkDirection) { + case LEFT: + case UP: + _nBodyOffset.set(-34, -2); + bodyStartPat = BPAT_WITHCARDSLEFT_START; + headLoopPat = PAT_TALK_LEFT; + bodyLoopPat = BPAT_WITHCARDSLEFT_LOOP; + break; + + case DOWN: + case RIGHT: + _nBodyOffset.set(-4, -2); + bodyStartPat = BPAT_WITHCARDSRIGHT_START; + headLoopPat = PAT_TALK_RIGHT; + bodyLoopPat = BPAT_WITHCARDSRIGHT_LOOP; + break; + } + break; + + case TALK_WITHSNOWMAN: + switch (_talkDirection) { + case LEFT: + case UP: + _nBodyOffset.set(-35, 2); + bodyStartPat = BPAT_WITHSNOWMANLEFT_START; + headLoopPat = PAT_TALK_LEFT; + bodyLoopPat = BPAT_WITHSNOWMANLEFT_LOOP; + break; + + case DOWN: + case RIGHT: + _nBodyOffset.set(-14, 2); + bodyStartPat = BPAT_WITHSNOWMANRIGHT_START; + headLoopPat = PAT_TALK_RIGHT; + bodyLoopPat = BPAT_WITHSNOWMANRIGHT_LOOP; + break; + } + break; + + case TALK_WITHSNOWMANSTATIC: + case TALK_WITHRECIPESTATIC: + case TALK_WITHRABBITSTATIC: + case TALK_WITHCARDSSTATIC: + case TALK_WITH_NOTEBOOK: + case TALK_WITHMEGAPHONESTATIC: + switch (_talkDirection) { + case LEFT: + case UP: + headLoopPat = PAT_TALK_LEFT; + break; + + case DOWN: + case RIGHT: + headLoopPat = PAT_TALK_RIGHT; + break; + } + break; + + // The beard is the only case in which the head is animated separately while the body is the standard + case TALK_WITHBEARDSTATIC: + switch (_talkDirection) { + case LEFT: + case UP: + headLoopPat = PAT_TALKBEARD_LEFT; + bodyLoopPat = BPAT_STANDLEFT; + _nBodyOffset.set(6, 56); + break; + + case DOWN: + case RIGHT: + headLoopPat = PAT_TALKBEARD_RIGHT; + bodyLoopPat = BPAT_STANDRIGHT; + _nBodyOffset.set(6, 56); + break; + } + break; + + case TALK_DISGUSTED: + switch (_talkDirection) { + case LEFT: + case UP: + _nBodyOffset.set(6, 56); + headStartPat = PAT_DISGUSTEDLEFT_START; + bodyStartPat = BPAT_STANDLEFT; + headLoopPat = PAT_DISGUSTEDLEFT_LOOP; + break; + + case DOWN: + case RIGHT: + _nBodyOffset.set(6, 56); + headStartPat = PAT_DISGUSTEDRIGHT_START; + bodyStartPat = BPAT_STANDRIGHT; + headLoopPat = PAT_DISGUSTEDRIGHT_LOOP; + break; + } + break; + + case TALK_SARCASTIC: + switch (_talkDirection) { + case LEFT: + case UP: + _nBodyOffset.set(6, 56); + headStartPat = PAT_SARCASTICLEFT_START; + bodyStartPat = BPAT_STANDLEFT; + headLoopPat = PAT_SARCASTICLEFT_LOOP; + break; + + case DOWN: + case RIGHT: + _nBodyOffset.set(6, 56); + headStartPat = PAT_SARCASTICRIGHT_START; + bodyStartPat = BPAT_STANDRIGHT; + headLoopPat = PAT_SARCASTICRIGHT_LOOP; + break; + } + break; + + case TALK_MACBETH1: + _nBodyOffset.set(-33, -1); + headLoopPat = PAT_TALK_LEFT; + bodyLoopPat = BPAT_MACBETH1; + break; + case TALK_MACBETH2: + _nBodyOffset.set(-33, -1); + headLoopPat = PAT_TALK_LEFT; + bodyLoopPat = BPAT_MACBETH2; + break; + case TALK_MACBETH3: + _nBodyOffset.set(-33, -1); + headLoopPat = PAT_TALK_LEFT; + bodyLoopPat = BPAT_MACBETH3; + break; + case TALK_MACBETH4: + _nBodyOffset.set(-33, -1); + headLoopPat = PAT_TALK_LEFT; + bodyLoopPat = BPAT_MACBETH4; + break; + case TALK_MACBETH5: + _nBodyOffset.set(-33, -1); + headLoopPat = PAT_TALK_LEFT; + bodyLoopPat = BPAT_MACBETH5; + break; + case TALK_MACBETH6: + _nBodyOffset.set(-33, -1); + headLoopPat = PAT_TALK_LEFT; + bodyLoopPat = BPAT_MACBETH6; + break; + case TALK_MACBETH7: + _nBodyOffset.set(-33, -1); + headLoopPat = PAT_TALK_LEFT; + bodyLoopPat = BPAT_MACBETH7; + break; + case TALK_MACBETH8: + _nBodyOffset.set(-33, -1); + headLoopPat = PAT_TALK_LEFT; + bodyLoopPat = BPAT_MACBETH8; + break; + case TALK_MACBETH9: + _nBodyOffset.set(-33, -1); + headLoopPat = PAT_TALK_LEFT; + bodyLoopPat = BPAT_MACBETH9; + break; + + case TALK_SCAREDSTATIC: + _bBodyFront = false; + switch (_talkDirection) { + case DOWN: + bodyStartPat = BPAT_STANDDOWN; + bodyLoopPat = BPAT_STANDDOWN; + _nBodyOffset.set(4, 53); + + headStartPat = PAT_SCAREDDOWN_STAND; + headLoopPat = PAT_SCAREDDOWN_LOOP; + break; + + case RIGHT: + bodyStartPat = BPAT_STANDRIGHT; + bodyLoopPat = BPAT_STANDRIGHT; + _nBodyOffset.set(6, 56); + + headStartPat = PAT_SCAREDRIGHT_STAND; + headLoopPat = PAT_SCAREDRIGHT_LOOP; + break; + + case LEFT: + bodyStartPat = BPAT_STANDLEFT; + bodyLoopPat = BPAT_STANDLEFT; + _nBodyOffset.set(6, 56); + + headStartPat = PAT_SCAREDLEFT_STAND; + headLoopPat = PAT_SCAREDLEFT_LOOP; + break; + + default: + break; + } + break; + } + + return true; +} + +void RMTony::startTalk(CORO_PARAM, CharacterTalkType nTalkType) { + CORO_BEGIN_CONTEXT; + int headStartPat, bodyStartPat; + int headLoopPat, bodyLoopPat; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + _ctx->headStartPat = _ctx->bodyStartPat = 0; + _ctx->headLoopPat = _ctx->bodyLoopPat = 0; + + if (!startTalkCalculate(nTalkType, _ctx->headStartPat, _ctx->bodyStartPat, + _ctx->headLoopPat, _ctx->bodyLoopPat)) + return; + + // Perform the set pattern + if (_ctx->headStartPat != 0 || _ctx->bodyStartPat != 0) { + setPattern(_ctx->headStartPat); + _body.setPattern(_ctx->bodyStartPat); + + if (_ctx->bodyStartPat != 0) + CORO_INVOKE_0(_body.waitForEndPattern); + if (_ctx->headStartPat != 0) + CORO_INVOKE_0(waitForEndPattern); + } + + setPattern(_ctx->headLoopPat); + if (_ctx->bodyLoopPat) + _body.setPattern(_ctx->bodyLoopPat); + + CORO_END_CODE; +} + +bool RMTony::endTalkCalculate(int &headStandPat, int &headEndPat, int &bodyEndPat, int &finalPat, bool &bStatic) { + bodyEndPat = 0; + headEndPat = 0; + + switch (_talkDirection) { + case UP: + finalPat = PAT_STANDUP; + headStandPat = PAT_HEAD_UP; + break; + + case DOWN: + finalPat = PAT_STANDDOWN; + headStandPat = PAT_HEAD_DOWN; + break; + + case LEFT: + finalPat = PAT_STANDLEFT; + headStandPat = PAT_HEAD_LEFT; + break; + + case RIGHT: + finalPat = PAT_STANDRIGHT; + headStandPat = PAT_HEAD_RIGHT; + break; + } + + if (_bShepherdess) { + setPattern(finalPat); + _bIsTalking = false; + return false; + } + + bStatic = false; + switch (_nTalkType) { + case TALK_NORMAL: + bodyEndPat = 0; + break; + + case TALK_HIPS: + switch (_talkDirection) { + case UP: + bodyEndPat = BPAT_HIPSUP_END; + break; + + case DOWN: + bodyEndPat = BPAT_HIPSDOWN_END; + break; + + case LEFT: + bodyEndPat = BPAT_HIPSLEFT_END; + break; + + case RIGHT: + bodyEndPat = BPAT_HIPSRIGHT_END; + break; + } + break; + + case TALK_SING: + bodyEndPat = BPAT_SINGLEFT_END; + break; + + case TALK_LAUGH: + case TALK_LAUGH2: + if (_talkDirection == LEFT) + headEndPat = PAT_LAUGHLEFT_END; + else if (_talkDirection == RIGHT) + headEndPat = PAT_LAUGHRIGHT_END; + + bodyEndPat = 0; + break; + + case TALK_DISGUSTED: + switch (_talkDirection) { + case UP: + case LEFT: + headEndPat = PAT_DISGUSTEDLEFT_END; + break; + + case DOWN: + case RIGHT: + headEndPat = PAT_DISGUSTEDRIGHT_END; + break; + } + + bodyEndPat = 0; + break; + + case TALK_SARCASTIC: + switch (_talkDirection) { + case UP: + case LEFT: + headEndPat = PAT_SARCASTICLEFT_END; + break; + + case DOWN: + case RIGHT: + headEndPat = PAT_SARCASTICRIGHT_END; + break; + } + + bodyEndPat = 0; + break; + + case TALK_INDICATE: + break; + + case TALK_SCARED: + switch (_talkDirection) { + case UP: + bodyEndPat = BPAT_SCAREDUP_END; + break; + + case DOWN: + headEndPat = PAT_SCAREDDOWN_END; + bodyEndPat = BPAT_SCAREDDOWN_END; + break; + + case RIGHT: + headEndPat = PAT_SCAREDRIGHT_END; + bodyEndPat = BPAT_SCAREDRIGHT_END; + break; + + case LEFT: + headEndPat = PAT_SCAREDLEFT_END; + bodyEndPat = BPAT_SCAREDLEFT_END; + break; + } + break; + + case TALK_SCARED2: + switch (_talkDirection) { + case UP: + bodyEndPat = 0; + break; + + case DOWN: + headEndPat = PAT_SCAREDDOWN_END; + bodyEndPat = 0; + break; + + case RIGHT: + headEndPat = PAT_SCAREDRIGHT_END; + bodyEndPat = 0; + break; + + case LEFT: + headEndPat = PAT_SCAREDLEFT_END; + bodyEndPat = 0; + break; + } + break; + + case TALK_WITHRABBIT: + switch (_talkDirection) { + case UP: + case LEFT: + finalPat = PAT_STANDLEFT; + bodyEndPat = BPAT_WITHRABBITLEFT_END; + break; + + case RIGHT: + case DOWN: + finalPat = PAT_STANDRIGHT; + bodyEndPat = BPAT_WITHRABBITRIGHT_END; + break; + } + break; + + case TALK_WITHRECIPE: + switch (_talkDirection) { + case UP: + case LEFT: + finalPat = PAT_STANDLEFT; + bodyEndPat = BPAT_WITHRECIPELEFT_END; + break; + + case RIGHT: + case DOWN: + finalPat = PAT_STANDRIGHT; + bodyEndPat = BPAT_WITHRECIPERIGHT_END; + break; + } + break; + + case TALK_WITHCARDS: + switch (_talkDirection) { + case UP: + case LEFT: + finalPat = PAT_STANDLEFT; + bodyEndPat = BPAT_WITHCARDSLEFT_END; + break; + + case RIGHT: + case DOWN: + finalPat = PAT_STANDRIGHT; + bodyEndPat = BPAT_WITHCARDSRIGHT_END; + break; + } + break; + + case TALK_WITHSNOWMAN: + switch (_talkDirection) { + case UP: + case LEFT: + finalPat = PAT_STANDLEFT; + bodyEndPat = BPAT_WITHSNOWMANLEFT_END; + break; + + case RIGHT: + case DOWN: + finalPat = PAT_STANDRIGHT; + bodyEndPat = BPAT_WITHSNOWMANRIGHT_END; + break; + } + break; + + case TALK_WITHWORM: + finalPat = PAT_WITHWORM; + break; + case TALK_WITHROPE: + finalPat = PAT_WITHROPE; + break; + case TALK_WITHSECRETARY: + finalPat = PAT_WITHSECRETARY; + break; + case TALK_WITHHAMMER: + finalPat = PAT_WITHHAMMER; + break; + case TALK_WITHGLASSES: + finalPat = PAT_WITHGLASSES; + break; + + case TALK_MACBETH1: + case TALK_MACBETH2: + case TALK_MACBETH3: + case TALK_MACBETH4: + case TALK_MACBETH5: + case TALK_MACBETH6: + case TALK_MACBETH7: + case TALK_MACBETH8: + finalPat = 0; + break; + + case TALK_SCAREDSTATIC: + switch (_talkDirection) { + case DOWN: + headStandPat = PAT_SCAREDDOWN_STAND; + bodyEndPat = 0; + break; + + case RIGHT: + headStandPat = PAT_SCAREDRIGHT_STAND; + bodyEndPat = 0; + break; + + case LEFT: + headStandPat = PAT_SCAREDLEFT_STAND; + bodyEndPat = 0; + break; + + + default: + break; + } + break; + + default: + break; + } + + return true; +} + +void RMTony::endTalk(CORO_PARAM) { + CORO_BEGIN_CONTEXT; + int headStandPat, headEndPat; + int bodyEndPat, finalPat; + bool bStatic; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + _ctx->headStandPat = _ctx->headEndPat = 0; + _ctx->bodyEndPat = _ctx->finalPat = 0; + _ctx->bStatic = false; + + _ctx->bodyEndPat = 0; + _ctx->headEndPat = 0; + + if (!endTalkCalculate(_ctx->headStandPat, _ctx->headEndPat, _ctx->bodyEndPat, _ctx->finalPat, _ctx->bStatic)) + return; + + // Handles the end of an animated and static, leaving everything unchanged + if (_bIsStaticTalk) { + if (_nTalkType == TALK_WITHBEARDSTATIC) { + setPattern(0); + if (_talkDirection == UP || _talkDirection == LEFT) { + _body.setPattern(BPAT_WITHBEARDLEFT_STATIC); + _nBodyOffset.set(-41, -14); + } else if (_talkDirection == DOWN || _talkDirection == RIGHT) { + _body.setPattern(BPAT_WITHBEARDRIGHT_STATIC); + _nBodyOffset.set(-26, -14); + } + } else { + setPattern(_ctx->headStandPat); + + CORO_INVOKE_0(_body.waitForEndPattern); + } + + _bIsTalking = false; + return; + } + + // Set the pattern + if (_ctx->headEndPat != 0 && _ctx->bodyEndPat != 0) { + setPattern(_ctx->headEndPat); + + CORO_INVOKE_0(_body.waitForEndPattern); + + _body.setPattern(_ctx->bodyEndPat); + + CORO_INVOKE_0(waitForEndPattern); + CORO_INVOKE_0(_body.waitForEndPattern); + } else if (_ctx->bodyEndPat != 0) { + setPattern(_ctx->headStandPat); + + CORO_INVOKE_0(_body.waitForEndPattern); + + _body.setPattern(_ctx->bodyEndPat); + + CORO_INVOKE_0(_body.waitForEndPattern); + } else if (_ctx->headEndPat != 0) { + CORO_INVOKE_0(_body.waitForEndPattern); + + setPattern(_ctx->headEndPat); + + CORO_INVOKE_0(waitForEndPattern); + } else { + CORO_INVOKE_0(_body.waitForEndPattern); + } + + if (_ctx->finalPat != 0) { + _body.setPattern(0); + setPattern(_ctx->finalPat); + } + + _bIsTalking = false; + + CORO_END_CODE; +} + +void RMTony::startStaticCalculate(CharacterTalkType nTalk, int &headPat, int &headLoopPat, + int &bodyStartPat, int &bodyLoopPat) { + int nPat = getCurPattern(); + + headLoopPat = -1; + + switch (nPat) { + case PAT_STANDDOWN: + _talkDirection = DOWN; + headPat = PAT_HEAD_RIGHT; + break; + + case PAT_TAKELEFT_UP2: + case PAT_TAKELEFT_MID2: + case PAT_TAKELEFT_DOWN2: + case PAT_GETUPLEFT: + case PAT_STANDLEFT: + _talkDirection = LEFT; + headPat = PAT_HEAD_LEFT; + break; + + case PAT_TAKERIGHT_UP2: + case PAT_TAKERIGHT_MID2: + case PAT_TAKERIGHT_DOWN2: + case PAT_GETUPRIGHT: + case PAT_STANDRIGHT: + _talkDirection = RIGHT; + headPat = PAT_HEAD_RIGHT; + break; + + case PAT_TAKEUP_UP2: + case PAT_TAKEUP_MID2: + case PAT_TAKEUP_DOWN2: + case PAT_STANDUP: + _talkDirection = UP; + headPat = PAT_HEAD_LEFT; + break; + } + + _bBodyFront = true; + + switch (nTalk) { + case TALK_WITHRABBITSTATIC: + switch (_talkDirection) { + case UP: + case LEFT: + _nBodyOffset.set(-21, -5); + bodyStartPat = BPAT_WITHRABBITLEFT_START; + bodyLoopPat = BPAT_WITHRABBITLEFT_LOOP; + break; + + case DOWN: + case RIGHT: + _nBodyOffset.set(-4, -5); + bodyStartPat = BPAT_WITHRABBITRIGHT_START; + bodyLoopPat = BPAT_WITHRABBITRIGHT_LOOP; + break; + } + break; + + case TALK_WITHCARDSSTATIC: + switch (_talkDirection) { + case UP: + case LEFT: + _nBodyOffset.set(-34, -2); + bodyStartPat = BPAT_WITHCARDSLEFT_START; + bodyLoopPat = BPAT_WITHCARDSLEFT_LOOP; + break; + + case DOWN: + case RIGHT: + _nBodyOffset.set(-4, -2); + bodyStartPat = BPAT_WITHCARDSRIGHT_START; + bodyLoopPat = BPAT_WITHCARDSRIGHT_LOOP; + break; + } + break; + + case TALK_WITHRECIPESTATIC: + switch (_talkDirection) { + case UP: + case LEFT: + _nBodyOffset.set(-61, -7); + bodyStartPat = BPAT_WITHRECIPELEFT_START; + bodyLoopPat = BPAT_WITHRECIPELEFT_LOOP; + break; + + case DOWN: + case RIGHT: + _nBodyOffset.set(-5, -7); + bodyStartPat = BPAT_WITHRECIPERIGHT_START; + bodyLoopPat = BPAT_WITHRECIPERIGHT_LOOP; + break; + } + break; + + case TALK_WITHSNOWMANSTATIC: + switch (_talkDirection) { + case UP: + case LEFT: + _nBodyOffset.set(-35, 2); + bodyStartPat = BPAT_WITHSNOWMANLEFT_START; + bodyLoopPat = BPAT_WITHSNOWMANLEFT_LOOP; + break; + + case DOWN: + case RIGHT: + _nBodyOffset.set(-14, 2); + bodyStartPat = BPAT_WITHSNOWMANRIGHT_START; + bodyLoopPat = BPAT_WITHSNOWMANRIGHT_LOOP; + break; + } + break; + + case TALK_WITH_NOTEBOOK: + switch (_talkDirection) { + case UP: + case LEFT: + _nBodyOffset.set(-16, -9); + bodyStartPat = BPAT_WITHNOTEBOOKLEFT_START; + bodyLoopPat = BPAT_WITHNOTEBOOKLEFT_LOOP; + break; + + case DOWN: + case RIGHT: + _nBodyOffset.set(-6, -9); + bodyStartPat = BPAT_WITHNOTEBOOKRIGHT_START; + bodyLoopPat = BPAT_WITHNOTEBOOKRIGHT_LOOP; + break; + } + break; + + case TALK_WITHMEGAPHONESTATIC: + switch (_talkDirection) { + case UP: + case LEFT: + _nBodyOffset.set(-41, -8); + bodyStartPat = BPAT_WITHMEGAPHONELEFT_START; + bodyLoopPat = BPAT_WITHMEGAPHONELEFT_LOOP; + break; + + case DOWN: + case RIGHT: + _nBodyOffset.set(-14, -8); + bodyStartPat = BPAT_WITHMEGAPHONERIGHT_START; + bodyLoopPat = BPAT_WITHMEGAPHONERIGHT_LOOP; + break; + } + break; + + case TALK_WITHBEARDSTATIC: + switch (_talkDirection) { + case UP: + case LEFT: + _nBodyOffset.set(-41, -14); + bodyStartPat = BPAT_WITHBEARDLEFT_START; + bodyLoopPat = BPAT_STANDLEFT; + headLoopPat = PAT_TALKBEARD_LEFT; + headPat = 0; + break; + + case DOWN: + case RIGHT: + _nBodyOffset.set(-26, -14); + bodyStartPat = BPAT_WITHBEARDRIGHT_START; + bodyLoopPat = BPAT_STANDRIGHT; + headLoopPat = PAT_TALKBEARD_RIGHT; + headPat = 0; + break; + } + break; + + case TALK_SCAREDSTATIC: + switch (_talkDirection) { + case DOWN: + headPat = PAT_SCAREDDOWN_START; + bodyLoopPat = BPAT_STANDDOWN; + bodyStartPat = BPAT_STANDDOWN; + headLoopPat = PAT_SCAREDDOWN_STAND; + _nBodyOffset.set(4, 53); + break; + + case LEFT: + headPat = PAT_SCAREDLEFT_START; + bodyLoopPat = BPAT_STANDLEFT; + bodyStartPat = BPAT_STANDLEFT; + headLoopPat = PAT_SCAREDLEFT_STAND; + _nBodyOffset.set(6, 56); + break; + + case RIGHT: + headPat = PAT_SCAREDRIGHT_START; + bodyLoopPat = BPAT_STANDRIGHT; + bodyStartPat = BPAT_STANDRIGHT; + headLoopPat = PAT_SCAREDRIGHT_STAND; + _nBodyOffset.set(6, 56); + break; + + default: + break; + } + + default: + break; + } +} + +void RMTony::startStatic(CORO_PARAM, CharacterTalkType nTalk) { + CORO_BEGIN_CONTEXT; + int headPat, headLoopPat; + int bodyStartPat, bodyLoopPat; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + _ctx->headPat = _ctx->headLoopPat = 0; + _ctx->bodyStartPat = _ctx->bodyLoopPat = 0; + + startStaticCalculate(nTalk, _ctx->headPat, _ctx->headLoopPat, + _ctx->bodyStartPat, _ctx->bodyLoopPat); + + // e vai con i pattern + _bIsStaticTalk = true; + + setPattern(_ctx->headPat); + _body.setPattern(_ctx->bodyStartPat); + + CORO_INVOKE_0(_body.waitForEndPattern); + CORO_INVOKE_0(waitForEndPattern); + + if (_ctx->headLoopPat != -1) + setPattern(_ctx->headLoopPat); + _body.setPattern(_ctx->bodyLoopPat); + + CORO_END_CODE; +} + +void RMTony::endStaticCalculate(CharacterTalkType nTalk, int &bodyEndPat, int &finalPat, int &headEndPat) { + switch (_talkDirection) { + case UP: + case LEFT: + finalPat = PAT_STANDLEFT; + break; + + case RIGHT: + case DOWN: + finalPat = PAT_STANDRIGHT; + break; + } + + switch (nTalk) { + case TALK_WITHSNOWMANSTATIC: + switch (_talkDirection) { + case UP: + case LEFT: + bodyEndPat = BPAT_WITHSNOWMANLEFT_END; + break; + + case DOWN: + case RIGHT: + bodyEndPat = BPAT_WITHSNOWMANRIGHT_END; + break; + } + break; + + case TALK_WITHRECIPESTATIC: + switch (_talkDirection) { + case UP: + case LEFT: + bodyEndPat = BPAT_WITHRECIPELEFT_END; + break; + + case DOWN: + case RIGHT: + bodyEndPat = BPAT_WITHRECIPERIGHT_END; + break; + } + break; + + case TALK_WITHRABBITSTATIC: + switch (_talkDirection) { + case UP: + case LEFT: + bodyEndPat = BPAT_WITHRABBITLEFT_END; + break; + + case DOWN: + case RIGHT: + bodyEndPat = BPAT_WITHRABBITRIGHT_END; + break; + } + break; + + case TALK_WITHCARDSSTATIC: + switch (_talkDirection) { + case UP: + case LEFT: + bodyEndPat = BPAT_WITHCARDSLEFT_END; + break; + + case DOWN: + case RIGHT: + bodyEndPat = BPAT_WITHCARDSRIGHT_END; + break; + } + break; + + case TALK_WITH_NOTEBOOK: + switch (_talkDirection) { + case UP: + case LEFT: + bodyEndPat = BPAT_WITHNOTEBOOKLEFT_END; + break; + + case DOWN: + case RIGHT: + bodyEndPat = BPAT_WITHNOTEBOOKRIGHT_END; + break; + } + break; + + case TALK_WITHMEGAPHONESTATIC: + switch (_talkDirection) { + case UP: + case LEFT: + bodyEndPat = BPAT_WITHMEGAPHONELEFT_END; + break; + + case DOWN: + case RIGHT: + bodyEndPat = BPAT_WITHMEGAPHONERIGHT_END; + break; + } + break; + + case TALK_WITHBEARDSTATIC: + switch (_talkDirection) { + case UP: + case LEFT: + bodyEndPat = BPAT_WITHBEARDLEFT_END; + break; + + case DOWN: + case RIGHT: + bodyEndPat = BPAT_WITHBEARDRIGHT_END; + break; + } + break; + + case TALK_SCAREDSTATIC: + switch (_talkDirection) { + case LEFT: + headEndPat = PAT_SCAREDLEFT_END; + break; + + case DOWN: + headEndPat = PAT_SCAREDDOWN_END; + break; + + case RIGHT: + headEndPat = PAT_SCAREDRIGHT_END; + break; + + default: + break; + } + break; + + default: + break; + } +} + +void RMTony::endStatic(CORO_PARAM, CharacterTalkType nTalk) { + CORO_BEGIN_CONTEXT; + int bodyEndPat; + int finalPat; + int headEndPat; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + _ctx->bodyEndPat = 0; + _ctx->finalPat = 0; + _ctx->headEndPat = 0; + + endStaticCalculate(nTalk, _ctx->bodyEndPat, _ctx->finalPat, _ctx->headEndPat); + + if (_ctx->headEndPat != 0) { + setPattern(_ctx->headEndPat); + + CORO_INVOKE_0(waitForEndPattern); + } else { + // Play please + _body.setPattern(_ctx->bodyEndPat); + + CORO_INVOKE_0(_body.waitForEndPattern); + } + + setPattern(_ctx->finalPat); + _body.setPattern(0); + + _bIsStaticTalk = false; + + CORO_END_CODE; +} + +/** + * Waits until the end of a pattern + */ +void RMTony::waitForEndPattern(CORO_PARAM, uint32 hCustomSkip) { + RMCharacter::waitForEndPattern(coroParam, hCustomSkip); +} + +/** + * Check if currently in an action + */ +bool RMTony::inAction() { + return (_bActionPending && _action != 0) | _bAction; +} + +/** + * Check if there needs to be an update for scrolling movement + */ +bool RMTony::mustUpdateScrolling() { + return ((!inAction()) || (isMoving())); +} + +/** + * Returns Tony's position + */ +RMPoint RMTony::position() { + return _pos; +} + +/** + * Set the scrolling position + */ +void RMTony::setScrollPosition(const RMPoint &pt) { + RMCharacter::setScrollPosition(pt); +} + +/** + * Tony disguises himself! + */ +void RMTony::setShepherdess(bool bIsPast) { + _bShepherdess = bIsPast; +} + +int RMTony::getShepherdess() { + return _bShepherdess; +} + +void RMTony::playSfx(int nSfx) { + RMItem::playSfx(nSfx); +} + +} // End of namespace Tony diff --git a/engines/tony/tonychar.h b/engines/tony/tonychar.h new file mode 100644 index 0000000000..d9f18f61ec --- /dev/null +++ b/engines/tony/tonychar.h @@ -0,0 +1,482 @@ +/* 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. + * + */ + +/* + * This code is based on original Tony Tough source code + * + * Copyright (c) 1997-2003 Nayma Software + */ + +#ifndef TONY_TONYCHAR_H +#define TONY_TONYCHAR_H + +#include "common/coroutines.h" +#include "tony/loc.h" + +namespace Tony { + +class RMTony : public RMCharacter { +private: + enum CharacterDirection { + UP, DOWN, LEFT, RIGHT + }; + +public: + enum CharacterTalkType { + TALK_NORMAL, + TALK_HIPS, + TALK_SING, + TALK_LAUGH, + TALK_INDICATE, + TALK_SCARED, + TALK_SCARED2, + TALK_WITHGLASSES, + TALK_WITHHAMMER, + TALK_WITHWORM, + TALK_WITHROPE, + TALK_WITHRABBIT, + TALK_WITHRECIPE, + TALK_WITHCARDS, + TALK_WITHSNOWMAN, + TALK_WITHSNOWMANSTATIC, + TALK_WITHRABBITSTATIC, + TALK_WITHRECIPESTATIC, + TALK_WITHCARDSSTATIC, + TALK_WITH_NOTEBOOK, + TALK_WITHMEGAPHONESTATIC, + TALK_WITHBEARDSTATIC, + TALK_LAUGH2, + TALK_DISGUSTED, + TALK_SARCASTIC, + TALK_MACBETH1, + TALK_MACBETH2, + TALK_MACBETH3, + TALK_MACBETH4, + TALK_MACBETH5, + TALK_MACBETH6, + TALK_MACBETH7, + TALK_MACBETH8, + TALK_MACBETH9, + TALK_SCAREDSTATIC, + TALK_WITHSECRETARY + }; + +private: + bool _bShow; + bool _bShowShadow; + bool _bBodyFront; + // Useless variable? + // RMGfxSourceBuffer8AB _shadow; + bool _bActionPending; + RMItem *_actionItem; + int _action; + int _actionParm; + static bool _bAction; + + bool _bShepherdess; + + bool _bIsStaticTalk; + bool _bIsTalking; + int _nPatB4Talking; + CharacterTalkType _nTalkType; + CharacterDirection _talkDirection; + RMPoint _nBodyOffset; + + int _nTimeLastStep; + + RMItem _body; + uint32 _hActionThread; + +protected: + /** + * Overload of the allocation allocation of sprites + */ + virtual RMGfxSourceBuffer *newItemSpriteBuffer(int dimx, int dimy, bool bPreRLE); + + /** + * Watch thread which waits for the end of an action + */ + static void waitEndOfAction(CORO_PARAM, const void *param); + +public: + enum CharacterPatterns { + PAT_TAKEUP_UP1 = 9, + PAT_TAKEUP_UP2, + PAT_TAKEUP_MID1, + PAT_TAKEUP_MID2, + PAT_TAKEUP_DOWN1, + PAT_TAKEUP_DOWN2, + + PAT_TAKELEFT_UP1, + PAT_TAKELEFT_UP2, + PAT_TAKELEFT_MID1, + PAT_TAKELEFT_MID2, + PAT_TAKELEFT_DOWN1, + PAT_TAKELEFT_DOWN2, + + PAT_TAKERIGHT_UP1, + PAT_TAKERIGHT_UP2, + PAT_TAKERIGHT_MID1, + PAT_TAKERIGHT_MID2, + PAT_TAKERIGHT_DOWN1, + PAT_TAKERIGHT_DOWN2, + + PAT_GETUPLEFT, + PAT_ONTHEFLOORLEFT, + PAT_GETUPRIGHT, + PAT_ONTHEFLOORRIGHT, + + // Sheperdess! + PAT_PAST_WALKUP, + PAT_PAST_WALKDOWN, + PAT_PAST_WALKLEFT, + PAT_PAST_WALKRIGHT, + + PAT_PAST_STANDUP, + PAT_PAST_STANDDOWN, + PAT_PAST_STANDLEFT, + PAT_PAST_STANDRIGHT, + + // Speech + PAT_TALK_UP, + PAT_TALK_DOWN, + PAT_TALK_LEFT, + PAT_TALK_RIGHT, + + // Static head + PAT_HEAD_UP, + PAT_HEAD_DOWN, + PAT_HEAD_LEFT, + PAT_HEAD_RIGHT, + + // Laugh + PAT_LAUGHLEFT_START, + PAT_LAUGHLEFT_LOOP, + PAT_LAUGHLEFT_END, + PAT_LAUGHRIGHT_START, + PAT_LAUGHRIGHT_LOOP, + PAT_LAUGHRIGHT_END, + + // Speaking as a shepherdess + PAT_PAST_TALKUP, + PAT_PAST_TALKDOWN, + PAT_PAST_TALKLEFT, + PAT_PAST_TALKRIGHT, + + // Fear + PAT_SCAREDLEFT_START, + PAT_SCAREDLEFT_LOOP, + PAT_SCAREDLEFT_END, + PAT_SCAREDRIGHT_START, + PAT_SCAREDRIGHT_LOOP, + PAT_SCAREDRIGHT_END, + PAT_SCAREDDOWN_START, + PAT_SCAREDDOWN_LOOP, + PAT_SCAREDDOWN_END, + + // With objects: full body + PAT_WITHGLASSES, + PAT_WITHROPE, + PAT_WITHWORM, + PAT_WITHHAMMER, + + // Sound the whistle + PAT_WHISTLERIGHT, + + // Head with beard + PAT_TALKBEARD_LEFT, + PAT_TALKBEARD_RIGHT, + + // Sniff + PAT_SNIFF_LEFT, + PAT_SNIFF_RIGHT, + + // Disgusted + PAT_DISGUSTEDLEFT_START, + PAT_DISGUSTEDLEFT_LOOP, + PAT_DISGUSTEDLEFT_END, + PAT_DISGUSTEDRIGHT_START, + PAT_DISGUSTEDRIGHT_LOOP, + PAT_DISGUSTEDRIGHT_END, + PAT_SARCASTICLEFT_START, + PAT_SARCASTICLEFT_LOOP, + PAT_SARCASTICLEFT_END, + PAT_SARCASTICRIGHT_START, + PAT_SARCASTICRIGHT_LOOP, + PAT_SARCASTICRIGHT_END, + + // Stand scared + PAT_SCAREDLEFT_STAND, + PAT_SCAREDRIGHT_STAND, + PAT_SCAREDDOWN_STAND, + + PAT_PUTLEFT_UP1, + PAT_PUTLEFT_UP2, + PAT_PUTRIGHT_UP1, + PAT_PUTRIGHT_UP2, + PAT_PUTLEFT_MID1, + PAT_PUTLEFT_MID2, + PAT_PUTRIGHT_MID1, + PAT_PUTRIGHT_MID2, + PAT_PUTLEFT_DOWN1, + PAT_PUTLEFT_DOWN2, + PAT_PUTRIGHT_DOWN1, + PAT_PUTRIGHT_DOWN2, + PAT_PUTUP_UP1, + PAT_PUTUP_UP2, + PAT_PUTUP_MID1, + PAT_PUTUP_MID2, + PAT_PUTUP_DOWN1, + PAT_PUTUP_DOWN2, + + PAT_WITHSECRETARY + }; + + enum CharacterBodyPatterns { + BPAT_STANDUP = 1, + BPAT_STANDDOWN, + BPAT_STANDLEFT, + BPAT_STANDRIGHT, + + BPAT_HAMMER, + BPAT_SNOWMAN, + BPAT_WORM, + BPAT_GLASS, + + BPAT_SINGLEFT_START, + BPAT_SINGLEFT_LOOP, + BPAT_SINGLEFT_END, + + BPAT_HIPSLEFT_START, + BPAT_HIPSLEFT_LOOP, + BPAT_HIPSLEFT_END, + BPAT_HIPSRIGHT_START, + BPAT_HIPSRIGHT_LOOP, + BPAT_HIPSRIGHT_END, + BPAT_HIPSUP_START, + BPAT_HIPSUP_LOOP, + BPAT_HIPSUP_END, + BPAT_HIPSDOWN_START, + BPAT_HIPSDOWN_LOOP, + BPAT_HIPSDOWN_END, + + BPAT_LAUGHLEFT, + BPAT_LAUGHRIGHT, + + BPAT_INDICATELEFT, + BPAT_INDICATERIGHT, + + BPAT_SCAREDDOWN_START, + BPAT_SCAREDDOWN_LOOP, + BPAT_SCAREDDOWN_END, + BPAT_SCAREDLEFT_START, + BPAT_SCAREDLEFT_LOOP, + BPAT_SCAREDLEFT_END, + BPAT_SCAREDRIGHT_START, + BPAT_SCAREDRIGHT_LOOP, + BPAT_SCAREDRIGHT_END, + BPAT_SCAREDUP_START, + BPAT_SCAREDUP_LOOP, + BPAT_SCAREDUP_END, + + BPAT_ROPE, + + BPAT_WITHRABBITLEFT_START, + BPAT_WITHRABBITLEFT_LOOP, + BPAT_WITHRABBITLEFT_END, + BPAT_WITHRABBITRIGHT_START, + BPAT_WITHRABBITRIGHT_LOOP, + BPAT_WITHRABBITRIGHT_END, + + BPAT_WITHRECIPELEFT_START, + BPAT_WITHRECIPELEFT_LOOP, + BPAT_WITHRECIPELEFT_END, + BPAT_WITHRECIPERIGHT_START, + BPAT_WITHRECIPERIGHT_LOOP, + BPAT_WITHRECIPERIGHT_END, + + BPAT_WITHCARDSLEFT_START, + BPAT_WITHCARDSLEFT_LOOP, + BPAT_WITHCARDSLEFT_END, + BPAT_WITHCARDSRIGHT_START, + BPAT_WITHCARDSRIGHT_LOOP, + BPAT_WITHCARDSRIGHT_END, + + BPAT_WITHSNOWMANLEFT_START, + BPAT_WITHSNOWMANLEFT_LOOP, + BPAT_WITHSNOWMANLEFT_END, + BPAT_WITHSNOWMANRIGHT_START, + BPAT_WITHSNOWMANRIGHT_LOOP, + BPAT_WITHSNOWMANRIGHT_END, + + BPAT_WITHNOTEBOOKLEFT_START, + BPAT_WITHNOTEBOOKLEFT_LOOP, + BPAT_WITHNOTEBOOKLEFT_END, + BPAT_WITHNOTEBOOKRIGHT_START, + BPAT_WITHNOTEBOOKRIGHT_LOOP, + BPAT_WITHNOTEBOOKRIGHT_END, + + BPAT_WITHMEGAPHONELEFT_START, + BPAT_WITHMEGAPHONELEFT_LOOP, + BPAT_WITHMEGAPHONELEFT_END, + BPAT_WITHMEGAPHONERIGHT_START, + BPAT_WITHMEGAPHONERIGHT_LOOP, + BPAT_WITHMEGAPHONERIGHT_END, + + BPAT_WITHBEARDLEFT_START, + BPAT_WITHBEARDLEFT_END, + BPAT_WITHBEARDRIGHT_START, + BPAT_WITHBEARDRIGHT_END, + BPAT_WITHBEARDLEFT_STATIC, + BPAT_WITHBEARDRIGHT_STATIC, + + BPAT_MACBETH1, + BPAT_MACBETH2, + BPAT_MACBETH3, + BPAT_MACBETH4, + BPAT_MACBETH5, + BPAT_MACBETH6, + BPAT_MACBETH7, + BPAT_MACBETH8, + BPAT_MACBETH9, + + BPAT_WITHSECRETARY + }; + +public: + static void initStatics(); + RMTony(); + + /** + * Initialize Tony + */ + void init(); + + /** + * Free all memory + */ + void close(); + + /** + * Tony makes a frame, updating the movement, etc. + */ + void doFrame(CORO_PARAM, RMGfxTargetBuffer *bigBuf, int curLoc); + + /** + * Draw method, which controls chararacter display + */ + virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim); + + /** + * Show or hide + */ + void show(); + void hide(bool bShowShadow = false); + + /** + * Move and make an action, if necessary + */ + void moveAndDoAction(CORO_PARAM, RMPoint dst, RMItem *item, int nAction, int nActionParm = 0); + + /** + * Tony stops (on the right side with respect to any subject) + */ + virtual void stop(CORO_PARAM); + void stopNoAction(CORO_PARAM); + + /** + * Set a pattern + */ + void setPattern(int npatt, bool bPlayP0 = false); + + /** + * Reads the current pattern + */ + int getCurPattern(); + + /** + * Waits until the end of a pattern + */ + void waitForEndPattern(CORO_PARAM, uint32 hCustomSkip = CORO_INVALID_PID_VALUE); + + /** + * Check if currently in an action + */ + bool inAction(); + + /** + * Check if there needs to be an update for scrolling movement + */ + bool mustUpdateScrolling(); + + /** + * Returns Tony's position + */ + RMPoint position(); + + /** + * Set the scrolling position + */ + void setScrollPosition(const RMPoint &pt); + + /** + * Set the take animation + */ + void take(int nWhere, int nPart); + void put(int nWhere, int nPart); + + /** + * Start or End Talk + */ + bool startTalkCalculate(CharacterTalkType nTalkType, int &headStartPat, int &bodyStartPat, + int &headLoopPat, int &bodyLoopPat); + void startTalk(CORO_PARAM, CharacterTalkType nTalkType); + bool endTalkCalculate(int &headStandPat, int &headEndPat, int &bodyEndPat, int &finalPat, bool &bStatic); + void endTalk(CORO_PARAM); + + /** + * Start or End Static + */ + void startStaticCalculate(CharacterTalkType nTalk, int &headPat, int &headLoopPat, + int &bodyStartPat, int &bodyLoopPat); + void startStatic(CORO_PARAM, CharacterTalkType nTalkType); + void endStaticCalculate(CharacterTalkType nTalk, int &bodyEndPat, int &finalPat, int &headEndPat); + void endStatic(CORO_PARAM, CharacterTalkType nTalkType); + + /** + * Tony disguises himself! + */ + void setShepherdess(bool bIsPast); + + int getShepherdess(); + + /** + * Perform an action + */ + void executeAction(int nAction, int nActionItem, int nParm); + + void playSfx(int nSfx); +}; + +} // End of namespace Tony + +#endif diff --git a/engines/tony/utils.cpp b/engines/tony/utils.cpp new file mode 100644 index 0000000000..81060146b7 --- /dev/null +++ b/engines/tony/utils.cpp @@ -0,0 +1,448 @@ +/* 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. + * + */ + +/* + * This code is based on original Tony Tough source code + * + * Copyright (c) 1997-2003 Nayma Software + */ + +#include "tony/utils.h" +#include "tony/tony.h" +#include "tony/mpal/lzo.h" + +namespace Tony { + +/** + * Extracts a string from a data stream + * @param df data stream + */ +Common::String readString(Common::ReadStream &df) { + Common::String var; + uint8 len = df.readByte(); + + for (int i = 0; i < len; i++) { + char c; + c = df.readByte(); + var += c; + } + + return var; +} + +/****************************************************************************\ +* RMPoint methods +\****************************************************************************/ + +/** + * Constructor + */ +RMPoint::RMPoint() { + _x = _y = 0; +} + +/** + * Copy constructor + */ +RMPoint::RMPoint(const RMPoint &p) { + _x = p._x; + _y = p._y; +} + +/** + * Constructor with integer parameters + */ +RMPoint::RMPoint(int x1, int y1) { + _x = x1; + _y = y1; +} + +/** + * Copy operator + */ +RMPoint &RMPoint::operator=(RMPoint p) { + _x = p._x; + _y = p._y; + + return *this; +} + +/** + * Set a point + */ +void RMPoint::set(int x1, int y1) { + _x = x1; + _y = y1; +} + +/** + * Offsets the point by another point + */ +void RMPoint::offset(const RMPoint &p) { + _x += p._x; + _y += p._y; +} + +/** + * Offsets the point by a specified offset + */ +void RMPoint::offset(int xOff, int yOff) { + _x += xOff; + _y += yOff; +} + +/** + * Sums together two points + */ +RMPoint operator+(RMPoint p1, RMPoint p2) { + RMPoint p(p1); + + return (p += p2); +} + +/** + * Subtracts two points + */ +RMPoint operator-(RMPoint p1, RMPoint p2) { + RMPoint p(p1); + + return (p -= p2); +} + +/** + * Sum (offset) of a point + */ +RMPoint &RMPoint::operator+=(RMPoint p) { + offset(p); + return *this; +} + +/** + * Subtract (offset) of a point + */ +RMPoint &RMPoint::operator-=(RMPoint p) { + offset(-p); + return *this; +} + +/** + * Inverts a point + */ +RMPoint RMPoint::operator-() { + RMPoint p; + + p._x = -_x; + p._y = -_y; + + return p; +} + +/** + * Equality operator + */ +bool RMPoint::operator==(RMPoint p) { + return ((_x == p._x) && (_y == p._y)); +} + +/** + * Not equal operator + */ +bool RMPoint::operator!=(RMPoint p) { + return ((_x != p._x) || (_y != p._y)); +} + +/** + * Reads a point from a stream + */ +void RMPoint::readFromStream(Common::ReadStream &ds) { + _x = ds.readSint32LE(); + _y = ds.readSint32LE(); +} + +/****************************************************************************\ +* RMPointReference methods +\****************************************************************************/ + +RMPointReference::RMPointReference(int &x, int &y): _x(x), _y(y) { +} + +RMPointReference &RMPointReference::operator=(const RMPoint &p) { + _x = p._x; _y = p._y; + return *this; +} + +RMPointReference &RMPointReference::operator-=(const RMPoint &p) { + _x -= p._x; _y -= p._y; + return *this; +} + +RMPointReference::operator RMPoint() const { + return RMPoint(_x, _y); +} + +/****************************************************************************\ +* RMRect methods +\****************************************************************************/ + +RMRect::RMRect(): _topLeft(_x1, _y1), _bottomRight(_x2, _y2) { + setEmpty(); +} + +void RMRect::setEmpty() { + _x1 = _y1 = _x2 = _y2 = 0; +} + +RMRect::RMRect(const RMPoint &p1, const RMPoint &p2): _topLeft(_x1, _y1), _bottomRight(_x2, _y2) { + setRect(p1, p2); +} + +RMRect::RMRect(int X1, int Y1, int X2, int Y2): _topLeft(_x1, _y1), _bottomRight(_x2, _y2) { + setRect(X1, Y1, X2, Y2); +} + +RMRect::RMRect(const RMRect &rc): _topLeft(_x1, _y1), _bottomRight(_x2, _y2) { + copyRect(rc); +} + +void RMRect::setRect(const RMPoint &p1, const RMPoint &p2) { + _x1 = p1._x; + _y1 = p1._y; + _x2 = p2._x; + _y2 = p2._y; +} + +void RMRect::setRect(int X1, int Y1, int X2, int Y2) { + _x1 = X1; + _y1 = Y1; + _x2 = X2; + _y2 = Y2; +} + +void RMRect::setRect(const RMRect &rc) { + copyRect(rc); +} + +void RMRect::copyRect(const RMRect &rc) { + _x1 = rc._x1; + _y1 = rc._y1; + _x2 = rc._x2; + _y2 = rc._y2; +} + +RMPointReference &RMRect::topLeft() { + return _topLeft; +} + +RMPointReference &RMRect::bottomRight() { + return _bottomRight; +} + +RMPoint RMRect::center() { + return RMPoint((_x2 - _x1) / 2, (_y2 - _y1) / 2); +} + +int RMRect::width() const { + return _x2 - _x1; +} + +int RMRect::height() const { + return _y2 - _y1; +} + +int RMRect::size() const { + return width() * height(); +} + +RMRect::operator Common::Rect() const { + return Common::Rect(_x1, _y1, _x2, _y2); +} + +bool RMRect::isEmpty() const { + return (_x1 == 0 && _y1 == 0 && _x2 == 0 && _y2 == 0); +} + +const RMRect &RMRect::operator=(const RMRect &rc) { + copyRect(rc); + return *this; +} + +void RMRect::offset(int xOff, int yOff) { + _x1 += xOff; + _y1 += yOff; + _x2 += xOff; + _y2 += yOff; +} + +void RMRect::offset(const RMPoint &p) { + _x1 += p._x; + _y1 += p._y; + _x2 += p._x; + _y2 += p._y; +} + +const RMRect &RMRect::operator+=(RMPoint p) { + offset(p); + return *this; +} + +const RMRect &RMRect::operator-=(RMPoint p) { + offset(-p); + return *this; +} + +RMRect operator+(const RMRect &rc, RMPoint p) { + RMRect r(rc); + return (r += p); +} + +RMRect operator-(const RMRect &rc, RMPoint p) { + RMRect r(rc); + + return (r -= p); +} + +RMRect operator+(RMPoint p, const RMRect &rc) { + RMRect r(rc); + + return (r += p); +} + +RMRect operator-(RMPoint p, const RMRect &rc) { + RMRect r(rc); + + return (r += p); +} + +bool RMRect::operator==(const RMRect &rc) { + return ((_x1 == rc._x1) && (_y1 == rc._y1) && (_x2 == rc._x2) && (_y2 == rc._y2)); +} + +bool RMRect::operator!=(const RMRect &rc) { + return ((_x1 != rc._x1) || (_y1 != rc._y1) || (_x2 != rc._x2) || (_y2 != rc._y2)); +} + +void RMRect::normalizeRect() { + setRect(MIN(_x1, _x2), MIN(_y1, _y2), MAX(_x1, _x2), MAX(_y1, _y2)); +} + +void RMRect::readFromStream(Common::ReadStream &ds) { + _x1 = ds.readSint32LE(); + _y1 = ds.readSint32LE(); + _x2 = ds.readSint32LE(); + _y2 = ds.readSint32LE(); +} + +/** + * Check if RMPoint is in RMRect + */ +bool RMRect::ptInRect(const RMPoint &pt) { + return (pt._x >= _x1 && pt._x <= _x2 && pt._y >= _y1 && pt._y <= _y2); +} + +/****************************************************************************\ +* Resource Update +\****************************************************************************/ + +RMResUpdate::RMResUpdate() { + _infos = NULL; + _numUpd = 0; +} + +RMResUpdate::~RMResUpdate() { + if (_infos) { + delete[] _infos; + _infos = NULL; + } + + if (_hFile.isOpen()) + _hFile.close(); +} + +void RMResUpdate::init(const Common::String &fileName) { + // Open the resource update file + if (!_hFile.open(fileName)) + // It doesn't exist, so exit immediately + return; + + _hFile.readByte(); // Version, unused + + _numUpd = _hFile.readUint32LE(); + + _infos = new ResUpdInfo[_numUpd]; + + // Load the index of the resources in the file + for (uint32 i = 0; i < _numUpd; ++i) { + ResUpdInfo &info = _infos[i]; + + info._dwRes = _hFile.readUint32LE(); + info._offset = _hFile.readUint32LE(); + info._size = _hFile.readUint32LE(); + info._cmpSize = _hFile.readUint32LE(); + } +} + +MpalHandle RMResUpdate::queryResource(uint32 dwRes) { + // If there isn't an update file, return NULL + if (!_hFile.isOpen()) + return NULL; + + uint32 i; + for (i = 0; i < _numUpd; ++i) + if (_infos[i]._dwRes == dwRes) + // Found the index + break; + + if (i == _numUpd) + // Couldn't find a matching resource, so return NULL + return NULL; + + const ResUpdInfo &info = _infos[i]; + byte *cmpBuf = new byte[info._cmpSize]; + uint32 dwRead; + + // Move to the correct offset and read in the compressed data + _hFile.seek(info._offset); + dwRead = _hFile.read(cmpBuf, info._cmpSize); + + if (info._cmpSize > dwRead) { + // Error occurred reading data, so return NULL + delete[] cmpBuf; + return NULL; + } + + // Allocate space for the output resource + MpalHandle destBuf = globalAllocate(0, info._size); + byte *lpDestBuf = (byte *)globalLock(destBuf); + uint32 dwSize; + + // Decompress the data + lzo1x_decompress(cmpBuf, info._cmpSize, lpDestBuf, &dwSize); + + // Delete buffer for compressed data + delete [] cmpBuf; + + // Return the resource + globalUnlock(destBuf); + return destBuf; +} + +} // End of namespace Tony diff --git a/engines/tony/utils.h b/engines/tony/utils.h new file mode 100644 index 0000000000..9f13e5f19b --- /dev/null +++ b/engines/tony/utils.h @@ -0,0 +1,176 @@ +/* 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. + * + */ + +/* + * This code is based on original Tony Tough source code + * + * Copyright (c) 1997-2003 Nayma Software + */ + +#ifndef TONY_UTILS_H +#define TONY_UTILS_H + +#include "common/scummsys.h" +#include "common/file.h" +#include "common/rect.h" +#include "common/str.h" +#include "tony/mpal/memory.h" + +namespace Tony { + +using namespace ::Tony::MPAL; + +Common::String readString(Common::ReadStream &ds); + +/** + * Point class + */ +class RMPoint { +public: + int _x, _y; + +public: + // Constructor + RMPoint(); + RMPoint(const RMPoint &p); + RMPoint(int x1, int y1); + + // Copy + RMPoint &operator=(RMPoint p); + + // Set + void set(int x1, int y1); + + // Offset + void offset(int xOff, int yOff); + void offset(const RMPoint &p); + friend RMPoint operator+(RMPoint p1, RMPoint p2); + friend RMPoint operator-(RMPoint p1, RMPoint p2); + RMPoint &operator+=(RMPoint p); + RMPoint &operator-=(RMPoint p); + RMPoint operator-(); + + // Comparison + bool operator==(RMPoint p); + bool operator!=(RMPoint p); + + // Casting a POINT + operator Common::Point() const; + + // Extraction from data streams + void readFromStream(Common::ReadStream &ds); +}; + +class RMPointReference { +public: + int &_x; + int &_y; + + RMPointReference(int &x, int &y); + RMPointReference &operator=(const RMPoint &p); + RMPointReference &operator-=(const RMPoint &p); + operator RMPoint() const; +}; + +class RMRect { +public: + int _x1, _y1; + int _x2, _y2; + RMPointReference _topLeft; + RMPointReference _bottomRight; + +public: + RMRect(); + RMRect(int x1, int y1, int x2, int y2); + RMRect(const RMPoint &p1, const RMPoint &p2); + RMRect(const RMRect &rc); + + // Attributes + RMPointReference &topLeft(); + RMPointReference &bottomRight(); + RMPoint center(); + int width() const; + int height() const; + bool isEmpty() const; + int size() const; + operator Common::Rect() const; + + // Set + void setRect(int x1, int y1, int x2, int y2); + void setRect(const RMPoint &p1, const RMPoint &p2); + void setEmpty(); + + // Copiers + void setRect(const RMRect &rc); + void copyRect(const RMRect &rc); + const RMRect &operator=(const RMRect &rc); + + // Offset + void offset(int xOff, int yOff); + void offset(const RMPoint &p); + friend RMRect operator+(const RMRect &rc, RMPoint p); + friend RMRect operator-(const RMRect &rc, RMPoint p); + friend RMRect operator+(RMPoint p, const RMRect &rc); + friend RMRect operator-(RMPoint p, const RMRect &rc); + const RMRect &operator+=(RMPoint p); + const RMRect &operator-=(RMPoint p); + + // Comparison + bool operator==(const RMRect &rc); + bool operator!=(const RMRect &rc); + + // Normalize + void normalizeRect(); + + // Point in rect + bool ptInRect(const RMPoint &pt); + + // Extract from data stream + void readFromStream(Common::ReadStream &ds); +}; + +/** + * Resource update manager + */ +class RMResUpdate { + struct ResUpdInfo { + uint32 _dwRes; + uint32 _offset; + uint32 _size; + uint32 _cmpSize; + }; + + uint32 _numUpd; + ResUpdInfo *_infos; + Common::File _hFile; + +public: + RMResUpdate(); + ~RMResUpdate(); + + void init(const Common::String &fileName); + MpalHandle queryResource(uint32 dwRes); +}; + +} // End of namespace Tony + +#endif /* TONY_H */ diff --git a/engines/tony/window.cpp b/engines/tony/window.cpp new file mode 100644 index 0000000000..61497a8066 --- /dev/null +++ b/engines/tony/window.cpp @@ -0,0 +1,335 @@ +/* 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. + * + */ + +/* + * This code is based on original Tony Tough source code + * + * Copyright (c) 1997-2003 Nayma Software + */ + +#include "common/scummsys.h" +#include "graphics/surface.h" +#include "util.h" +#include "tony/window.h" +#include "tony/game.h" +#include "tony/tony.h" + +namespace Tony { + +/****************************************************************************\ +* RMWindow Methods +\****************************************************************************/ + +RMWindow::RMWindow() { + _showDirtyRects = false; +} + +RMWindow::~RMWindow() { + close(); + RMText::unload(); + RMGfxTargetBuffer::freeBWPrecalcTable(); +} + +/** + * Initializes the graphics window + */ +void RMWindow::init() { + Graphics::PixelFormat pixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0); + initGraphics(RM_SX, RM_SY, true, &pixelFormat); + + _bGrabScreenshot = false; + _bGrabThumbnail = false; + _bGrabMovie = false; + _wiping = false; +} + +void RMWindow::copyRectToScreen(const byte *buf, int pitch, int x, int y, int w, int h) { + if (GLOBALS._bCfgAnni30) { + if (!RMGfxTargetBuffer::_precalcTable) { + RMGfxTargetBuffer::createBWPrecalcTable(); + g_vm->getEngine()->getPointer().updateCursor(); + } + Graphics::Surface *screen = g_system->lockScreen(); + const uint16 *src = (const uint16 *)buf; + for (int i = 0; i < h; i++) { + uint16 *dst = (uint16 *)screen->getBasePtr(x, y + i); + for (int j = 0; j < w; j++) { + dst[j] = RMGfxTargetBuffer::_precalcTable[src[j] & 0x7FFF]; + } + src += (pitch / 2); + } + g_system->unlockScreen(); + } else { + if (RMGfxTargetBuffer::_precalcTable) { + RMGfxTargetBuffer::freeBWPrecalcTable(); + g_vm->getEngine()->getPointer().updateCursor(); + } + g_system->copyRectToScreen(buf, pitch, x, y, w, h); + } + } + +/** + * Close the window + */ +void RMWindow::close() { +} + +void RMWindow::grabThumbnail(uint16 *thumbmem) { + _bGrabThumbnail = true; + _wThumbBuf = thumbmem; +} + +/** + * Repaint the screen + */ +void RMWindow::repaint() { + g_system->updateScreen(); +} + +/** + * Wipes an area of the screen + */ +void RMWindow::wipeEffect(Common::Rect &rcBoundEllipse) { + if ((rcBoundEllipse.left == 0) && (rcBoundEllipse.top == 0) && + (rcBoundEllipse.right == RM_SX) && (rcBoundEllipse.bottom == RM_SY)) { + // Full screen clear wanted, so use shortcut method + g_system->fillScreen(0); + } else { + // Clear the designated area a line at a time + uint16 line[RM_SX]; + Common::fill(line, line + RM_SX, 0); + + // Loop through each line + for (int yp = rcBoundEllipse.top; yp < rcBoundEllipse.bottom; ++yp) { + copyRectToScreen((const byte *)&line[0], RM_SX * 2, rcBoundEllipse.left, yp, rcBoundEllipse.width(), 1); + } + } +} + +void RMWindow::getNewFrame(RMGfxTargetBuffer &bigBuf, Common::Rect *rcBoundEllipse) { + // Get a pointer to the bytes of the source buffer + byte *lpBuf = bigBuf; + + if (rcBoundEllipse != NULL) { + // Circular wipe effect + getNewFrameWipe(lpBuf, *rcBoundEllipse); + _wiping = true; + } else if (_wiping) { + // Just finished a wiping effect, so copy the full screen + copyRectToScreen(lpBuf, RM_SX * 2, 0, 0, RM_SX, RM_SY); + _wiping = false; + + } else { + // Standard screen copy - iterate through the dirty rects + Common::List<Common::Rect> dirtyRects = bigBuf.getDirtyRects(); + Common::List<Common::Rect>::iterator i; + + // If showing dirty rects, copy the entire screen background and set up a surface pointer + Graphics::Surface *s = NULL; + if (_showDirtyRects) { + copyRectToScreen(lpBuf, RM_SX * 2, 0, 0, RM_SX, RM_SY); + s = g_system->lockScreen(); + } + + for (i = dirtyRects.begin(); i != dirtyRects.end(); ++i) { + Common::Rect &r = *i; + const byte *lpSrc = lpBuf + (RM_SX * 2) * r.top + (r.left * 2); + copyRectToScreen(lpSrc, RM_SX * 2, r.left, r.top, r.width(), r.height()); + } + + if (_showDirtyRects) { + for (i = dirtyRects.begin(); i != dirtyRects.end(); ++i) { + // Frame the copied area with a rectangle + s->frameRect(*i, 0xffffff); + } + + g_system->unlockScreen(); + } + } + + if (_bGrabThumbnail) { + // Need to generate a thumbnail + RMSnapshot s; + + s.grabScreenshot(lpBuf, 4, _wThumbBuf); + _bGrabThumbnail = false; + } + + // Clear the dirty rect list + bigBuf.clearDirtyRects(); +} + +/** + * Copies a section of the game frame in a circle bounded by the specified rectangle + */ +void RMWindow::getNewFrameWipe(byte *lpBuf, Common::Rect &rcBoundEllipse) { + // Clear the screen + g_system->fillScreen(0); + + if (!rcBoundEllipse.isValidRect()) + return; + + Common::Point center(rcBoundEllipse.left + rcBoundEllipse.width() / 2, + rcBoundEllipse.top + rcBoundEllipse.height() / 2); + + // The rectangle technically defines the area inside the ellipse, with the corners touching + // the ellipse boundary. Since we're currently simulating the ellipse using a plain circle, + // we need to calculate a necessary width using the hypotenuse of X/2 & Y/2 + int x2y2 = (rcBoundEllipse.width() / 2) * (rcBoundEllipse.width() / 2) + + (rcBoundEllipse.height() / 2) * (rcBoundEllipse.height() / 2); + int radius = 0; + while ((radius * radius) < x2y2) + ++radius; + + // Proceed copying a circular area of the frame with the calculated radius onto the screen + int error = -radius; + int x = radius; + int y = 0; + + while (x >= y) { + plotSplices(lpBuf, center, x, y); + + error += y; + ++y; + error += y; + + if (error >= 0) { + error -= x; + --x; + error -= x; + } + } +} + +/** + * Handles drawing the line splices for the circle of viewable area + */ +void RMWindow::plotSplices(const byte *lpBuf, const Common::Point ¢er, int x, int y) { + plotLines(lpBuf, center, x, y); + if (x != y) + plotLines(lpBuf, center, y, x); +} + +/** + * Handles drawing the line splices for the circle of viewable area + */ +void RMWindow::plotLines(const byte *lpBuf, const Common::Point ¢er, int x, int y) { + // Skips lines that have no width (i.e. at the top of the circle) + if ((x == 0) || (y > center.y)) + return; + + const byte *pSrc; + int xs = MAX(center.x - x, 0); + int width = MIN(RM_SX - xs, x * 2); + + if ((center.y - y) >= 0) { + // Draw line in top half of circle + pSrc = lpBuf + ((center.y - y) * RM_SX * 2) + xs * 2; + copyRectToScreen(pSrc, RM_SX * 2, xs, center.y - y, width, 1); + } + + if ((center.y + y) < RM_SY) { + // Draw line in bottom half of circle + pSrc = lpBuf + ((center.y + y) * RM_SX * 2) + xs * 2; + copyRectToScreen(pSrc, RM_SX * 2, xs, center.y + y, width, 1); + } +} + +void RMWindow::showDirtyRects(bool v) { + _showDirtyRects = v; +} + +/****************************************************************************\ +* RMSnapshot Methods +\****************************************************************************/ + +void RMSnapshot::grabScreenshot(byte *lpBuf, int dezoom, uint16 *lpDestBuf) { + uint16 *src = (uint16 *)lpBuf; + + int dimx = RM_SX / dezoom; + int dimy = RM_SY / dezoom; + + uint16 *cursrc; + + if (lpDestBuf == NULL) + src += (RM_SY - 1) * RM_BBX; + + if (dezoom == 1 && 0) { + byte *curOut = _rgb; + + for (int y = 0; y < dimy; y++) { + for (int x = 0; x < dimx; x++) { + cursrc = &src[RM_SKIPX + x]; + + *curOut++ = ((*cursrc) & 0x1F) << 3; + *curOut++ = (((*cursrc) >> 5) & 0x1F) << 3; + *curOut++ = (((*cursrc) >> 10) & 0x1F) << 3; + + if (lpDestBuf) + *lpDestBuf++ = *cursrc; + } + + if (lpDestBuf == NULL) + src -= RM_BBX; + else + src += RM_BBX; + } + } else { + uint32 k = 0; + for (int y = 0; y < dimy; y++) { + for (int x = 0; x < dimx; x++) { + cursrc = &src[RM_SKIPX + x * dezoom]; + int sommar, sommab, sommag, curv; + sommar = sommab = sommag = 0; + + for (int v = 0; v < dezoom; v++) { + for (int u = 0; u < dezoom; u++) { + if (lpDestBuf == NULL) + curv = -v; + else + curv = v; + + sommab += cursrc[curv * RM_BBX + u] & 0x1F; + sommag += (cursrc[curv * RM_BBX + u] >> 5) & 0x1F; + sommar += (cursrc[curv * RM_BBX + u] >> 10) & 0x1F; + } + } + _rgb[k + 0] = (byte)(sommab * 8 / (dezoom * dezoom)); + _rgb[k + 1] = (byte)(sommag * 8 / (dezoom * dezoom)); + _rgb[k + 2] = (byte)(sommar * 8 / (dezoom * dezoom)); + + if (lpDestBuf != NULL) + lpDestBuf[k / 3] = ((int)_rgb[k + 0] >> 3) | (((int)_rgb[k + 1] >> 3) << 5) | + (((int)_rgb[k + 2] >> 3) << 10); + + k += 3; + } + + if (lpDestBuf == NULL) + src -= RM_BBX * dezoom; + else + src += RM_BBX * dezoom; + } + } +} + +} // End of namespace Tony diff --git a/engines/tony/window.h b/engines/tony/window.h new file mode 100644 index 0000000000..3874652f64 --- /dev/null +++ b/engines/tony/window.h @@ -0,0 +1,97 @@ +/* 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. + * + */ + +/* + * This code is based on original Tony Tough source code + * + * Copyright (c) 1997-2003 Nayma Software + */ + +#ifndef TONY_WINDOW_H +#define TONY_WINDOW_H + +#include "common/scummsys.h" +#include "common/rect.h" +#include "tony/game.h" + +namespace Tony { + +class RMSnapshot { +private: + // Buffer used to convert to RGB + byte _rgb[RM_SX *RM_SY * 3]; +public: + /** + * Take a screenshot + */ + void grabScreenshot(byte *lpBuf, int dezoom = 1, uint16 *lpDestBuf = NULL); +}; + +class RMWindow { +private: + void plotSplices(const byte *lpBuf, const Common::Point ¢er, int x, int y); + void plotLines(const byte *lpBuf, const Common::Point ¢er, int x, int y); + +protected: + bool _wiping; + bool _showDirtyRects; + + bool _bGrabScreenshot; + bool _bGrabThumbnail; + bool _bGrabMovie; + uint16 *_wThumbBuf; + + void copyRectToScreen(const byte *buf, int pitch, int x, int y, int w, int h); + void wipeEffect(Common::Rect &rcBoundEllipse); + void getNewFrameWipe(byte *lpBuf, Common::Rect &rcBoundEllipse); + +public: + RMWindow(); + ~RMWindow(); + + /** + * Initialization + */ + void init(); + void close(); + + /** + * Drawing + */ + void repaint(); + + /** + * Reads the next frame + */ + void getNewFrame(RMGfxTargetBuffer &lpBuf, Common::Rect *rcBoundEllipse); + + /** + * Request a thumbnail be grabbed during the next frame + */ + void grabThumbnail(uint16 *buf); + + void showDirtyRects(bool v); +}; + +} // End of namespace Tony + +#endif /* TONY_WINDOW_H */ diff --git a/engines/toon/audio.cpp b/engines/toon/audio.cpp index 77822ab078..bc0e051057 100644 --- a/engines/toon/audio.cpp +++ b/engines/toon/audio.cpp @@ -326,7 +326,7 @@ bool AudioStreamInstance::readPacket() { } if (numDecompressedBytes > _bufferMaxSize) { - delete [] _buffer; + delete[] _buffer; _bufferMaxSize = numDecompressedBytes; _buffer = new int16[numDecompressedBytes]; } diff --git a/engines/toon/detection.cpp b/engines/toon/detection.cpp index 0cafee1475..38b1f4f6e1 100644 --- a/engines/toon/detection.cpp +++ b/engines/toon/detection.cpp @@ -84,7 +84,7 @@ static const ADGameDescription gameDescriptions[] = { {"study.svl", 0, "d4aff126ee27be3c3d25e2996369d7cb", 2324368}, }, Common::RU_RUS, Common::kPlatformPC, ADGF_NO_FLAGS, GUIO0() - }, + }, { "toon", "", { @@ -133,7 +133,7 @@ public: } virtual const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const { - return detectGameFilebased(allFiles, Toon::fileBasedFallback); + return detectGameFilebased(allFiles, fslist, Toon::fileBasedFallback); } virtual const char *getName() const { diff --git a/engines/toon/movie.cpp b/engines/toon/movie.cpp index 93e41adf57..8c85e20f7c 100644 --- a/engines/toon/movie.cpp +++ b/engines/toon/movie.cpp @@ -25,6 +25,7 @@ #include "common/keyboard.h" #include "common/stream.h" #include "common/system.h" +#include "graphics/palette.h" #include "graphics/surface.h" #include "toon/audio.h" @@ -33,6 +34,10 @@ namespace Toon { +ToonstruckSmackerDecoder::ToonstruckSmackerDecoder() : Video::SmackerDecoder() { + _lowRes = false; +} + void ToonstruckSmackerDecoder::handleAudioTrack(byte track, uint32 chunkSize, uint32 unpackedSize) { debugC(6, kDebugMovie, "handleAudioTrack(%d, %d, %d)", track, chunkSize, unpackedSize); @@ -40,33 +45,21 @@ void ToonstruckSmackerDecoder::handleAudioTrack(byte track, uint32 chunkSize, ui /* uint16 width = */ _fileStream->readUint16LE(); uint16 height = _fileStream->readUint16LE(); _lowRes = (height == getHeight() / 2); - } else + } else { Video::SmackerDecoder::handleAudioTrack(track, chunkSize, unpackedSize); + } } -bool ToonstruckSmackerDecoder::loadFile(const Common::String &filename) { - debugC(1, kDebugMovie, "loadFile(%s)", filename.c_str()); +bool ToonstruckSmackerDecoder::loadStream(Common::SeekableReadStream *stream) { + if (!Video::SmackerDecoder::loadStream(stream)) + return false; _lowRes = false; - - if (Video::SmackerDecoder::loadFile(filename)) { - if (_surface->h == 200) { - if (_surface) { - _surface->free(); - delete _surface; - } - _surface = new Graphics::Surface(); - _surface->create(640, 400, Graphics::PixelFormat::createFormatCLUT8()); - _header.flags = 4; - } - - return true; - } - return false; + return true; } -ToonstruckSmackerDecoder::ToonstruckSmackerDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundType) : Video::SmackerDecoder(mixer, soundType) { - _lowRes = false; +Video::SmackerDecoder::SmackerVideoTrack *ToonstruckSmackerDecoder::createVideoTrack(uint32 width, uint32 height, uint32 frameCount, const Common::Rational &frameRate, uint32 flags, uint32 signature) const { + return Video::SmackerDecoder::createVideoTrack(width, height, frameCount, frameRate, (height == 200) ? 4 : flags, signature); } // decoder is deallocated with Movie destruction i.e. new ToonstruckSmackerDecoder is needed @@ -103,6 +96,9 @@ void Movie::play(const Common::String &video, int32 flags) { bool Movie::playVideo(bool isFirstIntroVideo) { debugC(1, kDebugMovie, "playVideo(isFirstIntroVideo: %d)", isFirstIntroVideo); + + _decoder->start(); + while (!_vm->shouldQuit() && !_decoder->endOfVideo()) { if (_decoder->needsUpdate()) { const Graphics::Surface *frame = _decoder->decodeNextFrame(); @@ -131,7 +127,7 @@ bool Movie::playVideo(bool isFirstIntroVideo) { } } } - _decoder->setSystemPalette(); + _vm->_system->getPaletteManager()->setPalette(_decoder->getPalette(), 0, 256); _vm->_system->updateScreen(); } diff --git a/engines/toon/movie.h b/engines/toon/movie.h index 2cd33302f2..4dd6583bf6 100644 --- a/engines/toon/movie.h +++ b/engines/toon/movie.h @@ -30,12 +30,16 @@ namespace Toon { class ToonstruckSmackerDecoder : public Video::SmackerDecoder { public: - ToonstruckSmackerDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundType = Audio::Mixer::kSFXSoundType); - virtual ~ToonstruckSmackerDecoder() {} - void handleAudioTrack(byte track, uint32 chunkSize, uint32 unpackedSize); - bool loadFile(const Common::String &filename); + ToonstruckSmackerDecoder(); + + bool loadStream(Common::SeekableReadStream *stream); bool isLowRes() { return _lowRes; } + protected: + void handleAudioTrack(byte track, uint32 chunkSize, uint32 unpackedSize); + SmackerVideoTrack *createVideoTrack(uint32 width, uint32 height, uint32 frameCount, const Common::Rational &frameRate, uint32 flags, uint32 signature) const; + +private: bool _lowRes; }; diff --git a/engines/toon/picture.cpp b/engines/toon/picture.cpp index 204b0fe576..f59cdca064 100644 --- a/engines/toon/picture.cpp +++ b/engines/toon/picture.cpp @@ -71,7 +71,7 @@ bool Picture::loadPicture(const Common::String &file) { _data = new uint8[decSize + 100]; _paletteEntries = READ_LE_UINT16(fileData + 14) / 3; _useFullPalette = (_paletteEntries == 256); - + if (_paletteEntries) { _palette = new uint8[_paletteEntries * 3]; memcpy(_palette, fileData + 16, _paletteEntries * 3); diff --git a/engines/toon/toon.cpp b/engines/toon/toon.cpp index ee427652d8..9fd8415676 100644 --- a/engines/toon/toon.cpp +++ b/engines/toon/toon.cpp @@ -51,7 +51,7 @@ void ToonEngine::init() { _currentScriptRegion = 0; _resources = new Resources(this); _animationManager = new AnimationManager(this); - _moviePlayer = new Movie(this, new ToonstruckSmackerDecoder(_mixer)); + _moviePlayer = new Movie(this, new ToonstruckSmackerDecoder()); _hotspots = new Hotspots(this); _mainSurface = new Graphics::Surface(); diff --git a/engines/toon/toon.h b/engines/toon/toon.h index d40c489011..0ff351804f 100644 --- a/engines/toon/toon.h +++ b/engines/toon/toon.h @@ -316,12 +316,11 @@ public: } Common::Error saveGameState(int slot, const Common::String &desc) { - - return (saveGame(slot, desc) ? Common::kWritingFailed : Common::kNoError); + return (saveGame(slot, desc) ? Common::kNoError : Common::kWritingFailed); } Common::Error loadGameState(int slot) { - return (loadGame(slot) ? Common::kReadingFailed : Common::kNoError); + return (loadGame(slot) ? Common::kNoError : Common::kReadingFailed); } bool hasFeature(EngineFeature f) const { diff --git a/engines/touche/console.cpp b/engines/touche/console.cpp index 51ef5fc639..2c4c6a0da1 100644 --- a/engines/touche/console.cpp +++ b/engines/touche/console.cpp @@ -26,9 +26,28 @@ namespace Touche { ToucheConsole::ToucheConsole(ToucheEngine *vm) : GUI::Debugger(), _vm(vm) { + DCmd_Register("startMusic", WRAP_METHOD(ToucheConsole, Cmd_StartMusic)); + DCmd_Register("stopMusic", WRAP_METHOD(ToucheConsole, Cmd_StopMusic)); } ToucheConsole::~ToucheConsole() { } +bool ToucheConsole::Cmd_StartMusic(int argc, const char **argv) { + if (argc != 2) { + DebugPrintf("Usage: startMusic <num>\n"); + return true; + } + + int num = atoi(argv[1]); + + _vm->startMusic(num); + return false; +} + +bool ToucheConsole::Cmd_StopMusic(int argc, const char **argv) { + _vm->stopMusic(); + return false; +} + } // End of namespace Touche diff --git a/engines/touche/console.h b/engines/touche/console.h index e3cdc9d48b..43a303ad77 100644 --- a/engines/touche/console.h +++ b/engines/touche/console.h @@ -36,6 +36,9 @@ public: private: ToucheEngine *_vm; + + bool Cmd_StartMusic(int argc, const char **argv); + bool Cmd_StopMusic(int argc, const char **argv); }; } // End of namespace Touche diff --git a/engines/touche/detection.cpp b/engines/touche/detection.cpp index 35dd54776f..e4bbe0c4c1 100644 --- a/engines/touche/detection.cpp +++ b/engines/touche/detection.cpp @@ -135,19 +135,13 @@ public: } virtual const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const { - const ADGameDescription *matchedDesc = detectGameFilebased(allFiles, Touche::fileBasedFallback); - - if (matchedDesc) { // We got a match - Common::String report = Common::String::format(_("Your game version has been detected using " - "filename matching as a variant of %s."), matchedDesc->gameid); - report += "\n"; - report += _("If this is an original and unmodified version, please report any"); - report += "\n"; - report += _("information previously printed by ScummVM to the team."); - report += "\n"; - g_system->logMessage(LogMessageType::kInfo, report.c_str()); - } + ADFilePropertiesMap filesProps; + + const ADGameDescription *matchedDesc = detectGameFilebased(allFiles, fslist, Touche::fileBasedFallback, &filesProps); + if (!matchedDesc) + return 0; + reportUnknown(fslist.begin()->getParent(), filesProps); return matchedDesc; } diff --git a/engines/touche/menu.cpp b/engines/touche/menu.cpp index c58e2f1a33..85ca519f05 100644 --- a/engines/touche/menu.cpp +++ b/engines/touche/menu.cpp @@ -103,7 +103,7 @@ struct MenuData { void addCharToDescription(int slot, char chr) { char *description = saveLoadDescriptionsTable[slot]; int descriptionLen = strlen(description); - if (descriptionLen < 32 && isprint(static_cast<unsigned char>(chr))) { + if (descriptionLen < 32 && Common::isPrint(chr)) { description[descriptionLen] = chr; description[descriptionLen + 1] = 0; } @@ -260,7 +260,7 @@ void ToucheEngine::redrawMenu(void *menu) { Graphics::drawRect(_offscreenBuffer, kScreenWidth, 106, 118, 340, 164, 0xF9, 0xF7); switch (menuData->mode) { case kMenuSettingsMode: - drawVolumeSlideBar(_offscreenBuffer, kScreenWidth, _midiPlayer->getVolume()); + drawVolumeSlideBar(_offscreenBuffer, kScreenWidth, getMusicVolume()); menuData->buttonsTable[5].data = 0; menuData->buttonsTable[6].data = 0; menuData->buttonsTable[7].data = 0; @@ -307,10 +307,10 @@ void ToucheEngine::handleMenuAction(void *menu, int actionId) { _talkTextMode = kTalkModeVoiceAndText; break; case kActionLowerVolume: - _midiPlayer->adjustVolume(-16); + adjustMusicVolume(-16); break; case kActionUpperVolume: - _midiPlayer->adjustVolume(+16); + adjustMusicVolume(+16); break; case kActionScrollUpSaves: --_saveLoadCurrentPage; diff --git a/engines/touche/resource.cpp b/engines/touche/resource.cpp index 0790d726b7..9625224316 100644 --- a/engines/touche/resource.cpp +++ b/engines/touche/resource.cpp @@ -610,10 +610,7 @@ void ToucheEngine::res_stopSound() { void ToucheEngine::res_loadMusic(int num) { debugC(9, kDebugResource, "ToucheEngine::res_loadMusic() num=%d", num); - uint32 size; - const uint32 offs = res_getDataOffset(kResourceTypeMusic, num, &size); - _fData.seek(offs); - _midiPlayer->play(_fData, size, true); + startMusic(num); } void ToucheEngine::res_loadSpeech(int num) { diff --git a/engines/touche/staticres.cpp b/engines/touche/staticres.cpp index c18a947358..23b76558e4 100644 --- a/engines/touche/staticres.cpp +++ b/engines/touche/staticres.cpp @@ -471,7 +471,10 @@ const uint16 Graphics::_freGerFontOffs[] = { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x1920 + 0x0000, 0x0000, 0x0000, 0x1920, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000 }; const int Graphics::_freGerFontSize = ARRAYSIZE(Graphics::_freGerFontOffs); diff --git a/engines/touche/touche.cpp b/engines/touche/touche.cpp index 4b989963f6..5c133ccbc6 100644 --- a/engines/touche/touche.cpp +++ b/engines/touche/touche.cpp @@ -32,6 +32,8 @@ #include "common/keyboard.h" #include "common/textconsole.h" +#include "audio/mixer.h" + #include "engines/util.h" #include "graphics/cursorman.h" #include "graphics/palette.h" @@ -58,6 +60,8 @@ ToucheEngine::ToucheEngine(OSystem *system, Common::Language language) _playSoundCounter = 0; + _musicVolume = 0; + _processRandomPaletteCounter = 0; _fastWalkMode = false; @@ -90,6 +94,7 @@ ToucheEngine::~ToucheEngine() { DebugMan.clearAllDebugChannels(); delete _console; + stopMusic(); delete _midiPlayer; } @@ -100,7 +105,7 @@ Common::Error ToucheEngine::run() { setupOpcodes(); - _midiPlayer = new MidiPlayer; + initMusic(); // Setup mixer syncSoundSettings(); @@ -120,7 +125,7 @@ Common::Error ToucheEngine::run() { } void ToucheEngine::restart() { - _midiPlayer->stop(); + stopMusic(); _gameState = kGameStateGameLoop; _displayQuitDialog = false; @@ -216,7 +221,7 @@ void ToucheEngine::readConfigurationSettings() { _talkTextMode = kTalkModeVoiceOnly; } } - _midiPlayer->setVolume(ConfMan.getInt("music_volume")); + setMusicVolume(ConfMan.getInt("music_volume")); } void ToucheEngine::writeConfigurationSettings() { @@ -234,7 +239,7 @@ void ToucheEngine::writeConfigurationSettings() { ConfMan.setBool("subtitles", true); break; } - ConfMan.setInt("music_volume", _midiPlayer->getVolume()); + ConfMan.setInt("music_volume", getMusicVolume()); ConfMan.flushToDisk(); } @@ -3307,4 +3312,80 @@ bool ToucheEngine::canSaveGameStateCurrently() { return _gameState == kGameStateGameLoop && _flagsTable[618] == 0 && !_hideInventoryTexts; } +void ToucheEngine::initMusic() { + // Detect External Music Files + bool extMusic = true; + for (int num = 0; num < 26 && extMusic; num++) { + Common::String extMusicFilename = Common::String::format("track%02d", num+1); + Audio::SeekableAudioStream *musicStream = Audio::SeekableAudioStream::openStreamFile(extMusicFilename); + if (!musicStream) + extMusic = false; + delete musicStream; + } + + if (!extMusic) { + _midiPlayer = new MidiPlayer; + debug(1, "initMusic(): Using midi music!"); + } else + debug(1, "initMusic(): Using external digital music!"); +} + +void ToucheEngine::startMusic(int num) { + debug(1, "startMusic(%d)", num); + uint32 size; + + stopMusic(); + + if (_midiPlayer) { + const uint32 offs = res_getDataOffset(kResourceTypeMusic, num, &size); + _fData.seek(offs); + _midiPlayer->play(_fData, size, true); + } else { + Common::String extMusicFilename = Common::String::format("track%02d", num); + Audio::SeekableAudioStream *extMusicFileStream = Audio::SeekableAudioStream::openStreamFile(extMusicFilename); + if (!extMusicFileStream) { + error("Unable to open %s for reading", extMusicFilename.c_str()); + } + Audio::LoopingAudioStream *loopStream = new Audio::LoopingAudioStream(extMusicFileStream, 0); + _mixer->playStream(Audio::Mixer::kMusicSoundType, &_musicHandle, loopStream, -1, _musicVolume); + } +} + +void ToucheEngine::stopMusic() { + debug(1, "stopMusic()"); + if (_midiPlayer) + _midiPlayer->stop(); + else { + _mixer->stopHandle(_musicHandle); + } +} + +int ToucheEngine::getMusicVolume() { + if (_midiPlayer) + _musicVolume = _midiPlayer->getVolume(); + return _musicVolume; +} + +void ToucheEngine::setMusicVolume(int volume) { + debug(1, "setMusicVolume(%d)", volume); + _musicVolume = CLIP(volume, 0, 255); + + if (_midiPlayer) + _midiPlayer->setVolume(_musicVolume); + else { + _mixer->setChannelVolume(_musicHandle, _musicVolume); + } +} + +void ToucheEngine::adjustMusicVolume(int diff) { + debug(1, "adjustMusicVolume(%d)", diff); + _musicVolume = CLIP(_musicVolume + diff, 0, 255); + + if (_midiPlayer) + _midiPlayer->adjustVolume(diff); + else { + _mixer->setChannelVolume(_musicHandle, _musicVolume); + } +} + } // namespace Touche diff --git a/engines/touche/touche.h b/engines/touche/touche.h index 949727b665..6ac43e7dfe 100644 --- a/engines/touche/touche.h +++ b/engines/touche/touche.h @@ -31,6 +31,7 @@ #include "common/util.h" #include "audio/mixer.h" +#include "audio/audiostream.h" #include "engines/engine.h" @@ -646,6 +647,18 @@ protected: MidiPlayer *_midiPlayer; + int _musicVolume; + Audio::SoundHandle _musicHandle; + + void initMusic(); +public: // To allow access from console + void startMusic(int num); + void stopMusic(); +protected: + int getMusicVolume(); + void setMusicVolume(int volume); + void adjustMusicVolume(int diff); + Common::Language _language; Common::RandomSource _rnd; diff --git a/engines/tsage/blue_force/blueforce_dialogs.cpp b/engines/tsage/blue_force/blueforce_dialogs.cpp index a76d5839a9..23701c9e5b 100644 --- a/engines/tsage/blue_force/blueforce_dialogs.cpp +++ b/engines/tsage/blue_force/blueforce_dialogs.cpp @@ -88,7 +88,7 @@ RightClickDialog::~RightClickDialog() { void RightClickDialog::draw() { // Save the covered background area - _savedArea = Surface_getArea(g_globals->_gfxManagerInstance.getSurface(), _bounds); + _savedArea = surfaceGetArea(g_globals->_gfxManagerInstance.getSurface(), _bounds); // Draw the dialog image g_globals->gfxManager().copyFrom(_surface, _bounds.left, _bounds.top); @@ -323,7 +323,7 @@ void AmmoBeltDialog::draw() { if (!_savedArea) { // Save the covered background area - _savedArea = Surface_getArea(g_globals->_gfxManagerInstance.getSurface(), _bounds); + _savedArea = surfaceGetArea(g_globals->_gfxManagerInstance.getSurface(), _bounds); } else { bounds.moveTo(0, 0); } diff --git a/engines/tsage/blue_force/blueforce_scenes3.cpp b/engines/tsage/blue_force/blueforce_scenes3.cpp index 22c831f531..81e4af6e97 100644 --- a/engines/tsage/blue_force/blueforce_scenes3.cpp +++ b/engines/tsage/blue_force/blueforce_scenes3.cpp @@ -346,6 +346,14 @@ void Scene300::postInit(SceneObjectList *OwnerList) { break; } + if (BF_GLOBALS.getFlag(onBike) && !BF_GLOBALS.getFlag(onDuty)) { + BF_GLOBALS._sound1.play(30); + } else if ((BF_GLOBALS._dayNumber == 2) && (BF_GLOBALS._bookmark < bEndDayOne)) { + BF_GLOBALS._sound1.changeSound(49); + } else if (BF_GLOBALS._sceneManager._previousScene != 190) { + BF_GLOBALS._sound1.changeSound(33); + } + _item10.setDetails(4, 300, 7, 13, 16, 1); _item11.setDetails(2, 300, 9, 13, 18, 1); _item12.setDetails(5, 300, 10, 13, 19, 1); diff --git a/engines/tsage/blue_force/blueforce_scenes7.cpp b/engines/tsage/blue_force/blueforce_scenes7.cpp index bb29ad1f34..4cdd2f3f15 100644 --- a/engines/tsage/blue_force/blueforce_scenes7.cpp +++ b/engines/tsage/blue_force/blueforce_scenes7.cpp @@ -159,7 +159,7 @@ void Scene710::postInit(SceneObjectList *OwnerList) { _stripManager.addSpeaker(&_skipSpeaker); _stripManager.addSpeaker(&_lauraSpeaker); _stripManager.addSpeaker(&_gameTextSpeaker); - + _kid.postInit(); _kid._moveDiff = Common::Point(4, 2); _laura.postInit(); diff --git a/engines/tsage/blue_force/blueforce_speakers.cpp b/engines/tsage/blue_force/blueforce_speakers.cpp index 8af18b43b8..2a57616640 100644 --- a/engines/tsage/blue_force/blueforce_speakers.cpp +++ b/engines/tsage/blue_force/blueforce_speakers.cpp @@ -809,7 +809,7 @@ void SpeakerGiggles::setText(const Common::String &msg) { SpeakerFBI::SpeakerFBI(): VisualSpeaker() { _color1 = 27; _color2 = 89; - + _speakerName = "FBI"; } @@ -832,7 +832,7 @@ void SpeakerFBI::setText(const Common::String &msg) { SpeakerNico::SpeakerNico(): VisualSpeaker() { _color1 = 105; _color2 = 102; - + _speakerName = "NICO"; } @@ -845,7 +845,7 @@ void SpeakerNico::setText(const Common::String &msg) { _object1.fixPriority(254); _object1.setPosition(Common::Point(BF_GLOBALS._sceneManager._scene->_sceneBounds.left + 262, BF_GLOBALS._sceneManager._scene->_sceneBounds.top + 166)); - + _object2.postInit(); _object2.setVisage(905); _object2.setStrip2(1); @@ -862,7 +862,7 @@ void SpeakerNico::setText(const Common::String &msg) { SpeakerDA::SpeakerDA(): VisualSpeaker() { _color1 = 82; _color2 = 80; - + _speakerName = "DA"; } @@ -875,7 +875,7 @@ void SpeakerDA::setText(const Common::String &msg) { _object1.fixPriority(254); _object1.setPosition(Common::Point(BF_GLOBALS._sceneManager._scene->_sceneBounds.left + 84, BF_GLOBALS._sceneManager._scene->_sceneBounds.top + 166)); - + _object2.postInit(); _object2.setVisage(915); _object2.setStrip2(1); @@ -892,7 +892,7 @@ void SpeakerDA::setText(const Common::String &msg) { SpeakerGrandma::SpeakerGrandma(): VisualSpeaker() { _color1 = 20; _color2 = 23; - + _speakerName = "GRANDMA"; } @@ -905,7 +905,7 @@ void SpeakerGrandma::setText(const Common::String &msg) { _object1.fixPriority(254); _object1.setPosition(Common::Point(BF_GLOBALS._sceneManager._scene->_sceneBounds.left + 43, BF_GLOBALS._sceneManager._scene->_sceneBounds.top + 166)); - + _object2.postInit(); _object2.setVisage(274); _object2.setStrip2(3); @@ -922,7 +922,7 @@ void SpeakerGrandma::setText(const Common::String &msg) { SpeakerLyle::SpeakerLyle(): VisualSpeaker() { _color1 = 29; _color2 = 89; - + _speakerName = "LYLE"; } @@ -935,7 +935,7 @@ void SpeakerLyle::setText(const Common::String &msg) { _object1.fixPriority(254); _object1.setPosition(Common::Point(BF_GLOBALS._sceneManager._scene->_sceneBounds.left + 75, BF_GLOBALS._sceneManager._scene->_sceneBounds.top + 166)); - + _object2.postInit(); _object2.setVisage(278); _object2.setStrip2(1); @@ -952,7 +952,7 @@ void SpeakerLyle::setText(const Common::String &msg) { SpeakerGranText::SpeakerGranText(): VisualSpeaker() { _color1 = 20; _color2 = 23; - + _speakerName = "GRANTEXT"; } @@ -961,7 +961,7 @@ SpeakerGranText::SpeakerGranText(): VisualSpeaker() { SpeakerLyleText::SpeakerLyleText(): VisualSpeaker() { _color1 = 29; _color2 = 89; - + _speakerName = "LYLETEXT"; } @@ -969,7 +969,7 @@ SpeakerLyleText::SpeakerLyleText(): VisualSpeaker() { SpeakerKate::SpeakerKate(): VisualSpeaker() { _color1 = 108; - + _speakerName = "KATE"; } @@ -982,7 +982,7 @@ void SpeakerKate::setText(const Common::String &msg) { _object1.fixPriority(254); _object1.setPosition(Common::Point(BF_GLOBALS._sceneManager._scene->_sceneBounds.left + 270, BF_GLOBALS._sceneManager._scene->_sceneBounds.top + 166)); - + _object2.postInit(); _object2.setVisage(122); _object2.setStrip2(1); @@ -1000,7 +1000,7 @@ void SpeakerKate::setText(const Common::String &msg) { SpeakerTony::SpeakerTony(): VisualSpeaker() { _color1 = 108; _color2 = 8; - + _speakerName = "TONY"; } diff --git a/engines/tsage/blue_force/blueforce_speakers.h b/engines/tsage/blue_force/blueforce_speakers.h index 508279a929..e406a50fbe 100644 --- a/engines/tsage/blue_force/blueforce_speakers.h +++ b/engines/tsage/blue_force/blueforce_speakers.h @@ -290,7 +290,7 @@ public: virtual Common::String getClassName() { return "FBI"; } virtual void setText(const Common::String &msg); }; - + class SpeakerNico: public VisualSpeaker { public: SpeakerNico(); @@ -340,7 +340,7 @@ public: class SpeakerKate: public VisualSpeaker { public: SpeakerKate(); - + virtual Common::String getClassName() { return "SpeakerKate"; } virtual void setText(const Common::String &msg); }; @@ -348,7 +348,7 @@ public: class SpeakerTony: public VisualSpeaker { public: SpeakerTony(); - + virtual Common::String getClassName() { return "SpeakerTony"; } virtual void setText(const Common::String &msg); }; diff --git a/engines/tsage/converse.cpp b/engines/tsage/converse.cpp index 06fbffb751..ba27db9104 100644 --- a/engines/tsage/converse.cpp +++ b/engines/tsage/converse.cpp @@ -505,7 +505,7 @@ void ConversationChoiceDialog::draw() { // Make a backup copy of the area the dialog will occupy Rect tempRect = _bounds; tempRect.collapse(-10, -10); - _savedArea = Surface_getArea(g_globals->_gfxManagerInstance.getSurface(), tempRect); + _savedArea = surfaceGetArea(g_globals->_gfxManagerInstance.getSurface(), tempRect); // Fill in the contents of the entire dialog _gfxManager._bounds = Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); diff --git a/engines/tsage/detection.cpp b/engines/tsage/detection.cpp index bcadfdc201..a35d663b93 100644 --- a/engines/tsage/detection.cpp +++ b/engines/tsage/detection.cpp @@ -156,7 +156,7 @@ public: SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const { Common::InSaveFile *f = g_system->getSavefileManager()->openForLoading( generateGameStateFileName(target, slot)); - + if (f) { TsAGE::tSageSavegameHeader header; TsAGE::Saver::readSavegameHeader(f, header); diff --git a/engines/tsage/detection_tables.h b/engines/tsage/detection_tables.h index d538cbacbf..a84ee5662f 100644 --- a/engines/tsage/detection_tables.h +++ b/engines/tsage/detection_tables.h @@ -105,7 +105,7 @@ static const tSageGameDescription gameDescriptions[] = { AD_ENTRY1s("blue.rlb", "17c3993415e8a2cf93040eef7e88ec93", 1156508), Common::EN_ANY, Common::kPlatformPC, - ADGF_TESTING, + ADGF_UNSTABLE, GUIO2(GUIO_NOSPEECH, GUIO_NOSFX) }, GType_BlueForce, @@ -120,7 +120,7 @@ static const tSageGameDescription gameDescriptions[] = { AD_ENTRY1s("blue.rlb", "17eabb456cb1546c66baf1aff387ba6a", 10032614), Common::EN_ANY, Common::kPlatformPC, - ADGF_TESTING, + ADGF_NO_FLAGS, GUIO2(GUIO_NOSPEECH, GUIO_NOSFX) }, GType_BlueForce, @@ -134,7 +134,7 @@ static const tSageGameDescription gameDescriptions[] = { AD_ENTRY1s("blue.rlb", "99983f48cb218f1f3760cf2f9a7ef11d", 63863322), Common::EN_ANY, Common::kPlatformPC, - ADGF_CD | ADGF_TESTING, + ADGF_CD, GUIO2(GUIO_NOSPEECH, GUIO_NOSFX) }, GType_BlueForce, @@ -150,7 +150,7 @@ static const tSageGameDescription gameDescriptions[] = { AD_ENTRY1s("blue.rlb", "5b2b35c51b62e82d82b0791540bfae2d", 10082565), Common::ES_ESP, Common::kPlatformPC, - ADGF_CD | ADGF_TESTING, + ADGF_CD | ADGF_UNSTABLE, GUIO2(GUIO_NOSPEECH, GUIO_NOSFX) }, GType_BlueForce, diff --git a/engines/tsage/dialogs.cpp b/engines/tsage/dialogs.cpp index 972d591c34..77ac0a25d7 100644 --- a/engines/tsage/dialogs.cpp +++ b/engines/tsage/dialogs.cpp @@ -116,7 +116,7 @@ void ModalDialog::draw() { // Make a backup copy of the area the dialog will occupy Rect tempRect = _bounds; tempRect.collapse(-10, -10); - _savedArea = Surface_getArea(g_globals->_gfxManagerInstance.getSurface(), tempRect); + _savedArea = surfaceGetArea(g_globals->_gfxManagerInstance.getSurface(), tempRect); _gfxManager.activate(); diff --git a/engines/tsage/events.h b/engines/tsage/events.h index 475db47315..a1e9da3477 100644 --- a/engines/tsage/events.h +++ b/engines/tsage/events.h @@ -32,7 +32,7 @@ namespace TsAGE { enum EventType {EVENT_NONE = 0, EVENT_BUTTON_DOWN = 1, EVENT_BUTTON_UP = 2, EVENT_KEYPRESS = 4, - EVENT_MOUSE_MOVE = 8}; + EVENT_MOUSE_MOVE = 8, EVENT_UNK27 = 27}; enum ButtonShiftFlags {BTNSHIFT_LEFT = 0, BTNSHIFT_RIGHT = 3, BTNSHIFT_MIDDLE = 4}; diff --git a/engines/tsage/globals.cpp b/engines/tsage/globals.cpp index de9463268b..4589a926c9 100644 --- a/engines/tsage/globals.cpp +++ b/engines/tsage/globals.cpp @@ -205,7 +205,7 @@ void Globals::dispatchSounds() { void TsAGE2Globals::reset() { Globals::reset(); - + // Reset the inventory T2_GLOBALS._uiElements.updateInventory(); T2_GLOBALS._uiElements._scoreValue = 0; @@ -277,7 +277,7 @@ void BlueForceGlobals::synchronize(Serializer &s) { void BlueForceGlobals::reset() { TsAGE2Globals::reset(); _scenePalette.clearListeners(); - + _scrollFollower = &_player; _bookmark = bNone; @@ -368,7 +368,7 @@ namespace Ringworld2 { void Ringworld2Globals::reset() { Globals::reset(); - + // Reset the inventory R2_INVENTORY.reset(); T2_GLOBALS._uiElements.updateInventory(); @@ -526,7 +526,7 @@ void Ringworld2Globals::synchronize(Serializer &s) { for (i = 0; i < MAX_CHARACTERS; ++i) s.syncAsByte(_v565F1[i]); - + s.syncAsByte(_v565AE); s.syncAsByte(_v566A4); s.syncAsByte(_v566A5); diff --git a/engines/tsage/graphics.cpp b/engines/tsage/graphics.cpp index 0781ae4544..fb0b0b0cbb 100644 --- a/engines/tsage/graphics.cpp +++ b/engines/tsage/graphics.cpp @@ -38,7 +38,7 @@ namespace TsAGE { * @src Source surface * @bounds Area to backup */ -GfxSurface *Surface_getArea(GfxSurface &src, const Rect &bounds) { +GfxSurface *surfaceGetArea(GfxSurface &src, const Rect &bounds) { assert(bounds.isValidRect()); GfxSurface *dest = new GfxSurface(); dest->create(bounds.width(), bounds.height()); @@ -437,7 +437,7 @@ bool GfxSurface::displayText(const Common::String &msg, const Common::Point &pt) // Make a backup copy of the area the text will occupy Rect saveRect = textRect; saveRect.collapse(-20, -8); - GfxSurface *savedArea = Surface_getArea(gfxManager.getSurface(), saveRect); + GfxSurface *savedArea = surfaceGetArea(gfxManager.getSurface(), saveRect); // Display the text gfxManager._font.writeLines(msg.c_str(), textRect, ALIGN_LEFT); @@ -1073,7 +1073,7 @@ void GfxDialog::draw() { Rect tempRect(_bounds); // Make a backup copy of the area the dialog will occupy - _savedArea = Surface_getArea(g_globals->_gfxManagerInstance.getSurface(), _bounds); + _savedArea = surfaceGetArea(g_globals->_gfxManagerInstance.getSurface(), _bounds); // Set the palette for use in the dialog setPalette(); diff --git a/engines/tsage/graphics.h b/engines/tsage/graphics.h index 9c6f13e407..9175b1050a 100644 --- a/engines/tsage/graphics.h +++ b/engines/tsage/graphics.h @@ -339,7 +339,7 @@ public: static void setPalette(); }; -GfxSurface *Surface_getArea(GfxSurface &src, const Rect &bounds); +GfxSurface *surfaceGetArea(GfxSurface &src, const Rect &bounds); GfxSurface surfaceFromRes(const byte *imgData); GfxSurface surfaceFromRes(int resNum, int rlbNum, int subNum); diff --git a/engines/tsage/ringworld/ringworld_dialogs.cpp b/engines/tsage/ringworld/ringworld_dialogs.cpp index 0e451b8429..4728e66cd9 100644 --- a/engines/tsage/ringworld/ringworld_dialogs.cpp +++ b/engines/tsage/ringworld/ringworld_dialogs.cpp @@ -59,7 +59,7 @@ void RightClickButton::highlight() { _savedButton = NULL; } else { // Highlight button by getting the needed highlighted image resource - _savedButton = Surface_getArea(g_globals->gfxManager().getSurface(), _bounds); + _savedButton = surfaceGetArea(g_globals->gfxManager().getSurface(), _bounds); uint size; byte *imgData = g_resourceManager->getSubResource(7, 2, _buttonIndex, &size); @@ -122,7 +122,7 @@ RightClickButton *RightClickDialog::findButton(const Common::Point &pt) { void RightClickDialog::draw() { // Save the covered background area - _savedArea = Surface_getArea(g_globals->_gfxManagerInstance.getSurface(), _bounds); + _savedArea = surfaceGetArea(g_globals->_gfxManagerInstance.getSurface(), _bounds); // Draw the dialog image g_globals->gfxManager().copyFrom(_surface, _bounds.left, _bounds.top); diff --git a/engines/tsage/ringworld/ringworld_logic.cpp b/engines/tsage/ringworld/ringworld_logic.cpp index 00c219f2ee..0584570ac2 100644 --- a/engines/tsage/ringworld/ringworld_logic.cpp +++ b/engines/tsage/ringworld/ringworld_logic.cpp @@ -295,7 +295,7 @@ void SceneArea::display() { _bounds.setWidth(_surface.getBounds().width()); _bounds.setHeight(_surface.getBounds().height()); - _savedArea = Surface_getArea(g_globals->_gfxManagerInstance.getSurface(), _bounds); + _savedArea = surfaceGetArea(g_globals->_gfxManagerInstance.getSurface(), _bounds); draw2(); } @@ -610,7 +610,7 @@ void NamedHotspot::doAction(int action) { case CURSOR_USE: if (_useLineNum == -1) break; - + SceneItem::display(_resNum, _useLineNum, SET_Y, 20, SET_WIDTH, 200, SET_EXT_BGCOLOR, 7, LIST_END); return; case CURSOR_TALK: diff --git a/engines/tsage/ringworld/ringworld_scenes5.cpp b/engines/tsage/ringworld/ringworld_scenes5.cpp index 3b415bdb6a..004ccbbb6d 100644 --- a/engines/tsage/ringworld/ringworld_scenes5.cpp +++ b/engines/tsage/ringworld/ringworld_scenes5.cpp @@ -1893,7 +1893,7 @@ void Scene4045::postInit(SceneObjectList *OwnerList) { _olloFace.setStrip(4); _olloFace.fixPriority(152); - if(g_globals->_sceneManager._previousScene == 4050) { + if (g_globals->_sceneManager._previousScene == 4050) { g_globals->_soundHandler.play(155); g_globals->_player.setPosition(Common::Point(72, 128)); g_globals->_player.enableControl(); diff --git a/engines/tsage/ringworld2/ringworld2_dialogs.cpp b/engines/tsage/ringworld2/ringworld2_dialogs.cpp index 30ae6be7b1..478fdcf5a5 100644 --- a/engines/tsage/ringworld2/ringworld2_dialogs.cpp +++ b/engines/tsage/ringworld2/ringworld2_dialogs.cpp @@ -85,7 +85,7 @@ RightClickDialog::~RightClickDialog() { void RightClickDialog::draw() { // Save the covered background area - _savedArea = Surface_getArea(g_globals->_gfxManagerInstance.getSurface(), _bounds); + _savedArea = surfaceGetArea(g_globals->_gfxManagerInstance.getSurface(), _bounds); // Draw the dialog image g_globals->gfxManager().copyFrom(_surface, _bounds.left, _bounds.top); diff --git a/engines/tsage/ringworld2/ringworld2_logic.cpp b/engines/tsage/ringworld2/ringworld2_logic.cpp index a06899fe5a..97042cb621 100644 --- a/engines/tsage/ringworld2/ringworld2_logic.cpp +++ b/engines/tsage/ringworld2/ringworld2_logic.cpp @@ -1695,7 +1695,7 @@ bool AnimationPlayer::load(int animId, Action *endAction) { _playbackTickPrior = -1; _playbackTick = 0; - // The final multiplication is used to deliberately slow down playback, since the original + // The final multiplication is used to deliberately slow down playback, since the original // was slowed down by the amount of time spent to decode and display the frames _frameDelay = (60 / _subData._frameRate) * 8; _gameFrame = R2_GLOBALS._events.getFrameNumber(); @@ -1706,7 +1706,7 @@ bool AnimationPlayer::load(int animId, Action *endAction) { int v = (_subData._sliceSize + 2) * _subData._ySlices * _subData._framesPerSlices; _dataNeeded = (_subData._field16 / _subData._framesPerSlices) + v + 96; } - + debugC(1, ktSageDebugGraphics, "Data needed %d", _dataNeeded); // Set up animation data objects @@ -1760,7 +1760,7 @@ bool AnimationPlayer::load(int animId, Action *endAction) { byte r = _subData._palData[idx * 3]; byte g = _subData._palData[idx * 3 + 1]; byte b = _subData._palData[idx * 3 + 2]; - + int palIndex = R2_GLOBALS._scenePalette.indexOf(r, g, b); _palIndexes[idx] = palIndex; } diff --git a/engines/tsage/ringworld2/ringworld2_scenes0.cpp b/engines/tsage/ringworld2/ringworld2_scenes0.cpp index 4c98fcf00a..3b7d283e44 100644 --- a/engines/tsage/ringworld2/ringworld2_scenes0.cpp +++ b/engines/tsage/ringworld2/ringworld2_scenes0.cpp @@ -61,14 +61,10 @@ void Scene50::postInit(SceneObjectList *OwnerList) { } void Scene50::process(Event &event) { - if ((event.eventType != EVENT_BUTTON_DOWN) && (event.eventType != EVENT_KEYPRESS) && (event.eventType == 27)) { + if ((event.eventType != EVENT_BUTTON_DOWN) && (event.eventType != EVENT_KEYPRESS) && (event.eventType == EVENT_UNK27)) { event.handled = true; - warning("TODO: incomplete Scene50::process()"); - // CursorType _oldCursorId = _cursorId; g_globals->_events.setCursor(CURSOR_ARROW); - // _cursorManager.sub_1D474(2, 0); - // sub_5566A(1); - // _cursorManager._fieldE = _oldCursorId; + HelpDialog::show(); R2_GLOBALS._sceneManager.changeScene(100); } } diff --git a/engines/tsage/scenes.h b/engines/tsage/scenes.h index 2daa71ba98..d5ac88c692 100644 --- a/engines/tsage/scenes.h +++ b/engines/tsage/scenes.h @@ -67,7 +67,7 @@ public: void setZoomPercents(int yStart, int minPercent, int yEnd, int maxPercent); void loadBackground(int xAmount, int yAmount); - + void loadSceneData(int sceneNum); }; diff --git a/engines/tsage/sound.cpp b/engines/tsage/sound.cpp index 9df5a6666b..69a9975ef4 100644 --- a/engines/tsage/sound.cpp +++ b/engines/tsage/sound.cpp @@ -66,7 +66,7 @@ SoundManager::~SoundManager() { ++i; delete driver; } - _sfTerminate(); + sfTerminate(); // g_system->getTimerManager()->removeTimerProc(_sfUpdateCallback); } @@ -132,7 +132,7 @@ void SoundManager::syncSounds() { } void SoundManager::update() { - _sfSoundServer(); + sfSoundServer(); } Common::List<SoundDriverEntry> &SoundManager::buildDriverList(bool detectFlag) { @@ -144,22 +144,22 @@ Common::List<SoundDriverEntry> &SoundManager::buildDriverList(bool detectFlag) { // Adlib driver SoundDriverEntry sd; - sd.driverNum = ADLIB_DRIVER_NUM; - sd.status = detectFlag ? SNDSTATUS_DETECTED : SNDSTATUS_SKIPPED; - sd.field2 = 0; - sd.field6 = 15000; - sd.shortDescription = "Adlib or SoundBlaster"; - sd.longDescription = "3812fm"; + sd._driverNum = ADLIB_DRIVER_NUM; + sd._status = detectFlag ? SNDSTATUS_DETECTED : SNDSTATUS_SKIPPED; + sd._field2 = 0; + sd._field6 = 15000; + sd._shortDescription = "Adlib or SoundBlaster"; + sd._longDescription = "3812fm"; _availableDrivers.push_back(sd); // SoundBlaster entry SoundDriverEntry sdFx; - sdFx.driverNum = SBLASTER_DRIVER_NUM; - sdFx.status = detectFlag ? SNDSTATUS_DETECTED : SNDSTATUS_SKIPPED; - sdFx.field2 = 0; - sdFx.field6 = 15000; - sdFx.shortDescription = "SndBlast"; - sdFx.longDescription = "SoundBlaster"; + sdFx._driverNum = SBLASTER_DRIVER_NUM; + sdFx._status = detectFlag ? SNDSTATUS_DETECTED : SNDSTATUS_SKIPPED; + sdFx._field2 = 0; + sdFx._field6 = 15000; + sdFx._shortDescription = "SndBlast"; + sdFx._longDescription = "SoundBlaster"; _availableDrivers.push_back(sdFx); _driversDetected = true; @@ -204,7 +204,7 @@ void SoundManager::installDriver(int driverNum) { (*i)->mute(true); // Install the driver - if (!_sfInstallDriver(driver)) + if (!sfInstallDriver(driver)) error("Sound driver initialization failed"); switch (driverNum) { @@ -214,11 +214,11 @@ void SoundManager::installDriver(int driverNum) { byte *bankData = g_resourceManager->getResource(RES_BANK, driverNum, 0, true); if (bankData) { // Install the patch bank data - _sfInstallPatchBank(driver, bankData); + sfInstallPatchBank(driver, bankData); DEALLOCATE(bankData); } else { // Could not locate patch bank data, so unload the driver - _sfUnInstallDriver(driver); + sfUnInstallDriver(driver); // Unmute currently active sounds for (Common::List<Sound *>::iterator i = _playList.begin(); i != _playList.end(); ++i) @@ -260,7 +260,7 @@ void SoundManager::unInstallDriver(int driverNum) { (*j)->mute(true); // Uninstall the driver - _sfUnInstallDriver(*i); + sfUnInstallDriver(*i); // Re-orient all the loaded sounds for (j = _soundList.begin(); j != _soundList.end(); ++j) @@ -303,7 +303,7 @@ void SoundManager::unloadSound(int soundNum) { } int SoundManager::determineGroup(const byte *soundData) { - return _sfDetermineGroup(soundData); + return sfDetermineGroup(soundData); } void SoundManager::checkResVersion(const byte *soundData) { @@ -325,7 +325,7 @@ int SoundManager::extractLoop(const byte *soundData) { } void SoundManager::extractTrackInfo(trackInfoStruct *trackInfo, const byte *soundData, int groupNum) { - _sfExtractTrackInfo(trackInfo, soundData, groupNum); + sfExtractTrackInfo(trackInfo, soundData, groupNum); } void SoundManager::addToSoundList(Sound *sound) { @@ -338,46 +338,46 @@ void SoundManager::removeFromSoundList(Sound *sound) { } void SoundManager::addToPlayList(Sound *sound) { - _sfAddToPlayList(sound); + sfAddToPlayList(sound); } void SoundManager::removeFromPlayList(Sound *sound) { if (_soundManager) - _sfRemoveFromPlayList(sound); + sfRemoveFromPlayList(sound); } bool SoundManager::isOnPlayList(Sound *sound) { - return _sfIsOnPlayList(sound); + return sfIsOnPlayList(sound); } void SoundManager::updateSoundVol(Sound *sound) { - _sfUpdateVolume(sound); + sfUpdateVolume(sound); } void SoundManager::updateSoundPri(Sound *sound) { - _sfUpdatePriority(sound); + sfUpdatePriority(sound); } void SoundManager::updateSoundLoop(Sound *sound) { - _sfUpdateLoop(sound); + sfUpdateLoop(sound); } void SoundManager::rethinkVoiceTypes() { Common::StackLock slock(sfManager()._serverSuspendedMutex); - _sfRethinkVoiceTypes(); + sfRethinkVoiceTypes(); } -void SoundManager::_sfSoundServer() { +void SoundManager::sfSoundServer() { if (sfManager()._needToRethink) { - _sfRethinkVoiceTypes(); + sfRethinkVoiceTypes(); sfManager()._needToRethink = false; } else { - _sfDereferenceAll(); + sfDereferenceAll(); } // If the master volume has changed, update it if (sfManager()._newVolume != sfManager()._masterVol) - _sfSetMasterVol(sfManager()._newVolume); + sfSetMasterVol(sfManager()._newVolume); // If a time index has been set for any sound, fast forward to it SynchronizedList<Sound *>::iterator i; @@ -385,14 +385,14 @@ void SoundManager::_sfSoundServer() { Sound *s = *i; if (s->_newTimeIndex != 0) { s->mute(true); - s->_soSetTimeIndex(s->_newTimeIndex); + s->soSetTimeIndex(s->_newTimeIndex); s->mute(false); s->_newTimeIndex = 0; } } // Handle any fading if necessary - _sfProcessFading(); + sfProcessFading(); // Poll all sound drivers in case they need it for (Common::List<SoundDriver *>::iterator j = sfManager()._installedDrivers.begin(); @@ -401,7 +401,7 @@ void SoundManager::_sfSoundServer() { } } -void SoundManager::_sfProcessFading() { +void SoundManager::sfProcessFading() { // Loop through processing active sounds bool removeFlag = false; Common::List<Sound *>::iterator i = sfManager()._playList.begin(); @@ -410,9 +410,9 @@ void SoundManager::_sfProcessFading() { ++i; if (!s->_pausedCount) - removeFlag = s->_soServiceTracks(); + removeFlag = s->soServiceTracks(); if (removeFlag) { - _sfDoRemoveFromPlayList(s); + sfDoRemoveFromPlayList(s); s->_stoppedAsynchronously = true; sfManager()._needToRethink = true; } @@ -429,13 +429,13 @@ void SoundManager::_sfProcessFading() { s->_volume + s->_fadeSteps : s->_fadeDest; } - _sfDoUpdateVolume(s); + sfDoUpdateVolume(s); if (s->_volume != s->_fadeDest) s->_fadeCounter = s->_fadeTicks; else { s->_fadeDest = -1; if (s->_stopAfterFadeFlag) { - _sfDoRemoveFromPlayList(s); + sfDoRemoveFromPlayList(s); s->_stoppedAsynchronously = true; sfManager()._needToRethink = true; } @@ -475,7 +475,7 @@ bool SoundManager::isFading() { return false; } -void SoundManager::_sfUpdateVoiceStructs() { +void SoundManager::sfUpdateVoiceStructs() { for (int voiceIndex = 0; voiceIndex < SOUND_ARR_SIZE; ++voiceIndex) { VoiceTypeStruct *vs = sfManager()._voiceTypeStructPtrs[voiceIndex]; if (!vs) @@ -504,7 +504,7 @@ void SoundManager::_sfUpdateVoiceStructs() { } } -void SoundManager::_sfUpdateVoiceStructs2() { +void SoundManager::sfUpdateVoiceStructs2() { for (int voiceIndex = 0; voiceIndex < SOUND_ARR_SIZE; ++voiceIndex) { VoiceTypeStruct *vtStruct = sfManager()._voiceTypeStructPtrs[voiceIndex]; if (!vtStruct) @@ -528,7 +528,7 @@ void SoundManager::_sfUpdateVoiceStructs2() { } } -void SoundManager::_sfUpdateCallback(void *ref) { +void SoundManager::sfUpdateCallback(void *ref) { ((SoundManager *)ref)->update(); } @@ -587,7 +587,7 @@ SoundManager &SoundManager::sfManager() { return *_soundManager; } -int SoundManager::_sfDetermineGroup(const byte *soundData) { +int SoundManager::sfDetermineGroup(const byte *soundData) { const byte *p = soundData + READ_LE_UINT16(soundData + 8); uint32 v; while ((v = READ_LE_UINT32(p)) != 0) { @@ -600,22 +600,22 @@ int SoundManager::_sfDetermineGroup(const byte *soundData) { return 0; } -void SoundManager::_sfAddToPlayList(Sound *sound) { +void SoundManager::sfAddToPlayList(Sound *sound) { Common::StackLock slock(sfManager()._serverSuspendedMutex); - _sfDoAddToPlayList(sound); + sfDoAddToPlayList(sound); sound->_stoppedAsynchronously = false; - _sfRethinkVoiceTypes(); + sfRethinkVoiceTypes(); } -void SoundManager::_sfRemoveFromPlayList(Sound *sound) { +void SoundManager::sfRemoveFromPlayList(Sound *sound) { Common::StackLock slock(sfManager()._serverSuspendedMutex); - if (_sfDoRemoveFromPlayList(sound)) - _sfRethinkVoiceTypes(); + if (sfDoRemoveFromPlayList(sound)) + sfRethinkVoiceTypes(); } -bool SoundManager::_sfIsOnPlayList(Sound *sound) { +bool SoundManager::sfIsOnPlayList(Sound *sound) { Common::StackLock slock(sfManager()._serverSuspendedMutex); bool result = contains(_soundManager->_playList, sound); @@ -623,7 +623,7 @@ bool SoundManager::_sfIsOnPlayList(Sound *sound) { return result; } -void SoundManager::_sfRethinkSoundDrivers() { +void SoundManager::sfRethinkSoundDrivers() { // Free any existing entries int idx; @@ -643,7 +643,7 @@ void SoundManager::_sfRethinkSoundDrivers() { i != sfManager()._installedDrivers.end(); ++i) { // Process the group data for each sound driver SoundDriver *driver = *i; - const byte *groupData = driver->_groupOffset->pData; + const byte *groupData = driver->_groupOffset->_pData; while (*groupData != 0xff) { byte byteVal = *groupData++; @@ -690,7 +690,7 @@ void SoundManager::_sfRethinkSoundDrivers() { i != sfManager()._installedDrivers.end(); ++i) { // Process the group data for each sound driver SoundDriver *driver = *i; - const byte *groupData = driver->_groupOffset->pData; + const byte *groupData = driver->_groupOffset->_pData; while (*groupData != 0xff) { byte byteVal = *groupData++; @@ -746,8 +746,8 @@ void SoundManager::_sfRethinkSoundDrivers() { } } -void SoundManager::_sfRethinkVoiceTypes() { - _sfDereferenceAll(); +void SoundManager::sfRethinkVoiceTypes() { + sfDereferenceAll(); // Pre-processing for (int voiceIndex = 0; voiceIndex < SOUND_ARR_SIZE; ++voiceIndex) { @@ -797,7 +797,7 @@ void SoundManager::_sfRethinkVoiceTypes() { if ((sound->_mutedCount != 0) || (sound->_pausedCount != 0)) continue; - _sfUpdateVoiceStructs(); + sfUpdateVoiceStructs(); Common::fill(sound->_chWork, sound->_chWork + SOUND_ARR_SIZE, false); for (;;) { @@ -831,7 +831,7 @@ void SoundManager::_sfRethinkVoiceTypes() { if (foundPriority) continue; - _sfUpdateVoiceStructs2(); + sfUpdateVoiceStructs2(); break; } @@ -860,7 +860,7 @@ void SoundManager::_sfRethinkVoiceTypes() { maxPriority = MAX(maxPriority, vtStruct->_entries[idx]._type1._priority2); if (!maxPriority) { - _sfUpdateVoiceStructs2(); + sfUpdateVoiceStructs2(); break; } @@ -944,7 +944,7 @@ void SoundManager::_sfRethinkVoiceTypes() { continue; } - _sfUpdateVoiceStructs2(); + sfUpdateVoiceStructs2(); break; } else { // Channel mode 1 handling (loc_23FAC) @@ -973,7 +973,7 @@ void SoundManager::_sfRethinkVoiceTypes() { if (foundPriority) continue; if (entryIndex == -1) { - _sfUpdateVoiceStructs2(); + sfUpdateVoiceStructs2(); break; } } @@ -1008,7 +1008,7 @@ void SoundManager::_sfRethinkVoiceTypes() { continue; } - _sfUpdateVoiceStructs2(); + sfUpdateVoiceStructs2(); break; } @@ -1051,7 +1051,7 @@ void SoundManager::_sfRethinkVoiceTypes() { if (!foundPriority) continue; if (priorityIndex == -1) { - _sfUpdateVoiceStructs2(); + sfUpdateVoiceStructs2(); break; } @@ -1271,38 +1271,38 @@ void SoundManager::_sfRethinkVoiceTypes() { } } -void SoundManager::_sfUpdateVolume(Sound *sound) { - _sfDereferenceAll(); - _sfDoUpdateVolume(sound); +void SoundManager::sfUpdateVolume(Sound *sound) { + sfDereferenceAll(); + sfDoUpdateVolume(sound); } -void SoundManager::_sfDereferenceAll() { +void SoundManager::sfDereferenceAll() { // Orignal used handles for both the driver list and voiceTypeStructPtrs list. This method then refreshed // pointer lists based on the handles. Since in ScummVM we're just using pointers directly, this // method doesn't need any implementation } -void SoundManager::_sfUpdatePriority(Sound *sound) { +void SoundManager::sfUpdatePriority(Sound *sound) { Common::StackLock slock(sfManager()._serverSuspendedMutex); int tempPriority = (sound->_fixedPriority == 255) ? sound->_sndResPriority : sound->_priority; if (sound->_priority != tempPriority) { sound->_priority = tempPriority; - if (_sfDoRemoveFromPlayList(sound)) { - _sfDoAddToPlayList(sound); - _sfRethinkVoiceTypes(); + if (sfDoRemoveFromPlayList(sound)) { + sfDoAddToPlayList(sound); + sfRethinkVoiceTypes(); } } } -void SoundManager::_sfUpdateLoop(Sound *sound) { +void SoundManager::sfUpdateLoop(Sound *sound) { if (sound->_fixedLoop) sound->_loop = sound->_sndResLoop; else sound->_loop = sound->_fixedLoop; } -void SoundManager::_sfSetMasterVol(int volume) { +void SoundManager::sfSetMasterVol(int volume) { if (volume > 127) volume = 127; @@ -1316,7 +1316,7 @@ void SoundManager::_sfSetMasterVol(int volume) { } } -void SoundManager::_sfExtractTrackInfo(trackInfoStruct *trackInfo, const byte *soundData, int groupNum) { +void SoundManager::sfExtractTrackInfo(trackInfoStruct *trackInfo, const byte *soundData, int groupNum) { trackInfo->_numTracks = 0; const byte *p = soundData + READ_LE_UINT16(soundData + 8); @@ -1345,11 +1345,11 @@ void SoundManager::_sfExtractTrackInfo(trackInfoStruct *trackInfo, const byte *s } } -void SoundManager::_sfTerminate() { +void SoundManager::sfTerminate() { } -void SoundManager::_sfExtractGroupMask() { +void SoundManager::sfExtractGroupMask() { uint32 mask = 0; for (Common::List<SoundDriver *>::iterator i = sfManager()._installedDrivers.begin(); @@ -1359,37 +1359,37 @@ void SoundManager::_sfExtractGroupMask() { _soundManager->_groupsAvail = mask; } -bool SoundManager::_sfInstallDriver(SoundDriver *driver) { +bool SoundManager::sfInstallDriver(SoundDriver *driver) { if (!driver->open()) return false; sfManager()._installedDrivers.push_back(driver); driver->_groupOffset = driver->getGroupData(); - driver->_groupMask = driver->_groupOffset->groupMask; + driver->_groupMask = driver->_groupOffset->_groupMask; - _sfExtractGroupMask(); - _sfRethinkSoundDrivers(); + sfExtractGroupMask(); + sfRethinkSoundDrivers(); driver->setMasterVolume(sfManager()._masterVol); return true; } -void SoundManager::_sfUnInstallDriver(SoundDriver *driver) { +void SoundManager::sfUnInstallDriver(SoundDriver *driver) { sfManager()._installedDrivers.remove(driver); delete driver; - _sfExtractGroupMask(); - _sfRethinkSoundDrivers(); + sfExtractGroupMask(); + sfRethinkSoundDrivers(); } -void SoundManager::_sfInstallPatchBank(SoundDriver *driver, const byte *bankData) { +void SoundManager::sfInstallPatchBank(SoundDriver *driver, const byte *bankData) { driver->installPatch(bankData, g_vm->_memoryManager.getSize(bankData)); } /** * Adds the specified sound in the playing sound list, inserting in order of priority */ -void SoundManager::_sfDoAddToPlayList(Sound *sound) { +void SoundManager::sfDoAddToPlayList(Sound *sound) { Common::StackLock slock2(sfManager()._serverSuspendedMutex); Common::List<Sound *>::iterator i = sfManager()._playList.begin(); @@ -1402,7 +1402,7 @@ void SoundManager::_sfDoAddToPlayList(Sound *sound) { /** * Removes the specified sound from the play list */ -bool SoundManager::_sfDoRemoveFromPlayList(Sound *sound) { +bool SoundManager::sfDoRemoveFromPlayList(Sound *sound) { Common::StackLock slock(sfManager()._serverSuspendedMutex); bool result = false; @@ -1417,7 +1417,7 @@ bool SoundManager::_sfDoRemoveFromPlayList(Sound *sound) { return result; } -void SoundManager::_sfDoUpdateVolume(Sound *sound) { +void SoundManager::sfDoUpdateVolume(Sound *sound) { Common::StackLock slock(sfManager()._serverSuspendedMutex); for (int voiceIndex = 0; voiceIndex < SOUND_ARR_SIZE; ++voiceIndex) { @@ -1583,7 +1583,7 @@ void Sound::_prime(int soundResID, bool dontQueue) { _remoteReceiver = ALLOCATE(200); } - _soPrimeSound(dontQueue); + soPrimeSound(dontQueue); if (!dontQueue) _soundManager->addToSoundList(this); @@ -1767,7 +1767,7 @@ void Sound::release() { _hold = -1; } -void Sound::_soPrimeSound(bool dontQueue) { +void Sound::soPrimeSound(bool dontQueue) { if (!dontQueue) { _priority = (_fixedPriority != -1) ? _fixedPriority : _sndResPriority; _loop = !_fixedLoop ? _fixedLoop : _sndResLoop; @@ -1785,21 +1785,21 @@ void Sound::_soPrimeSound(bool dontQueue) { _timer = 0; _newTimeIndex = 0; _loopTimer = 0; - _soPrimeChannelData(); + soPrimeChannelData(); } -void Sound::_soSetTimeIndex(uint timeIndex) { +void Sound::soSetTimeIndex(uint timeIndex) { Common::StackLock slock(g_globals->_soundManager._serverSuspendedMutex); if (timeIndex != _timer) { _soundManager->_soTimeIndexFlag = true; _timer = 0; _loopTimer = 0; - _soPrimeChannelData(); + soPrimeChannelData(); while (timeIndex > 0) { - if (_soServiceTracks()) { - SoundManager::_sfDoRemoveFromPlayList(this); + if (soServiceTracks()) { + SoundManager::sfDoRemoveFromPlayList(this); _stoppedAsynchronously = true; _soundManager->_needToRethink = true; break; @@ -1812,9 +1812,9 @@ void Sound::_soSetTimeIndex(uint timeIndex) { } } -bool Sound::_soServiceTracks() { +bool Sound::soServiceTracks() { if (_isEmpty) { - _soRemoteReceive(); + soRemoteReceive(); return false; } @@ -1823,9 +1823,9 @@ bool Sound::_soServiceTracks() { int mode = *_channelData[trackCtr]; if (mode == 0) { - _soServiceTrackType0(trackCtr, _channelData[trackCtr]); + soServiceTrackType0(trackCtr, _channelData[trackCtr]); } else if (mode == 1) { - _soServiceTrackType1(trackCtr, _channelData[trackCtr]); + soServiceTrackType1(trackCtr, _channelData[trackCtr]); } else { error("Unknown sound mode encountered"); } @@ -1851,7 +1851,7 @@ bool Sound::_soServiceTracks() { } } -void Sound::_soPrimeChannelData() { +void Sound::soPrimeChannelData() { if (_isEmpty) { for (int idx = 0; idx < 16; ++idx) { _chProgram[idx] = 0; @@ -1917,11 +1917,11 @@ void Sound::_soPrimeChannelData() { } } -void Sound::_soRemoteReceive() { - error("_soRemoteReceive not implemented"); +void Sound::soRemoteReceive() { + error("soRemoteReceive not implemented"); } -void Sound::_soServiceTrackType0(int trackIndex, const byte *channelData) { +void Sound::soServiceTrackType0(int trackIndex, const byte *channelData) { if (_trkRest[trackIndex]) { --_trkRest[trackIndex]; return; @@ -1970,7 +1970,7 @@ void Sound::_soServiceTrackType0(int trackIndex, const byte *channelData) { // Only do processing if fast forwarding to a given time index if (channelNum != -1) { if (voiceType == VOICETYPE_1) { - _soUpdateDamper(vtStruct, channelNum, chVoiceType, v); + soUpdateDamper(vtStruct, channelNum, chVoiceType, v); } else if (voiceNum != -1) { assert(driver); driver->proc18(voiceNum, chVoiceType); @@ -1991,9 +1991,9 @@ void Sound::_soServiceTrackType0(int trackIndex, const byte *channelData) { if (channelNum != -1) { if (voiceType != VOICETYPE_0) { if (chFlags & 0x10) - _soPlaySound2(vtStruct, channelData, channelNum, chVoiceType, v); + soPlaySound2(vtStruct, channelData, channelNum, chVoiceType, v); else - _soPlaySound(vtStruct, channelData, channelNum, chVoiceType, v, b); + soPlaySound(vtStruct, channelData, channelNum, chVoiceType, v, b); } else if (voiceNum != -1) { assert(driver); driver->proc20(voiceNum, chVoiceType); @@ -2030,17 +2030,17 @@ void Sound::_soServiceTrackType0(int trackIndex, const byte *channelData) { int cmdVal = cmdList[v]; if (channelNum == -1) { - if (_soDoUpdateTracks(cmdVal, b)) + if (soDoUpdateTracks(cmdVal, b)) return; } else { - _soDoTrackCommand(_trkChannel[trackIndex], cmdVal, b); + soDoTrackCommand(_trkChannel[trackIndex], cmdVal, b); if (!_soundManager->_soTimeIndexFlag) { if (cmdVal == 7) b = static_cast<byte>(_volume * (int)b / 127); if (voiceType != VOICETYPE_0) { - _soProc38(vtStruct, channelNum, chVoiceType, cmdVal, b); + soProc38(vtStruct, channelNum, chVoiceType, cmdVal, b); } else if (voiceNum != -1) { assert(driver); driver->proc24(voiceNum, chVoiceType, this, cmdVal, b); @@ -2067,17 +2067,17 @@ void Sound::_soServiceTrackType0(int trackIndex, const byte *channelData) { int value = *pData++; if (channelNum != -1) { - _soDoTrackCommand(_trkChannel[trackIndex], cmd, value); + soDoTrackCommand(_trkChannel[trackIndex], cmd, value); if (!_soundManager->_soTimeIndexFlag) { if (voiceType != VOICETYPE_0) { - _soProc38(vtStruct, channelNum, chVoiceType, cmd, value); + soProc38(vtStruct, channelNum, chVoiceType, cmd, value); } else if (voiceNum != -1) { assert(driver); driver->proc24(voiceNum, chVoiceType, this, cmd, value); } } - } else if (_soDoUpdateTracks(cmd, value)) { + } else if (soDoUpdateTracks(cmd, value)) { return; } } else if (!(v & 0x2)) { @@ -2091,7 +2091,7 @@ void Sound::_soServiceTrackType0(int trackIndex, const byte *channelData) { _chPitchBlend[channel] = pitchBlend; if (voiceType != VOICETYPE_0) { - _soProc40(vtStruct, channelNum, pitchBlend); + soProc40(vtStruct, channelNum, pitchBlend); } else if (voiceNum != -1) { assert(driver); driver->setPitchBlend(channel, pitchBlend); @@ -2115,7 +2115,7 @@ void Sound::_soServiceTrackType0(int trackIndex, const byte *channelData) { } } } else { - _soSetTrackPos(trackIndex, pData - channelData, program); + soSetTrackPos(trackIndex, pData - channelData, program); } } else { @@ -2139,7 +2139,7 @@ void Sound::_soServiceTrackType0(int trackIndex, const byte *channelData) { } } -void Sound::_soUpdateDamper(VoiceTypeStruct *voiceType, int channelNum, VoiceType mode, int v0) { +void Sound::soUpdateDamper(VoiceTypeStruct *voiceType, int channelNum, VoiceType mode, int v0) { bool hasDamper = _chDamper[channelNum] != 0; for (uint idx = 0; idx < voiceType->_entries.size(); ++idx) { @@ -2161,8 +2161,8 @@ void Sound::_soUpdateDamper(VoiceTypeStruct *voiceType, int channelNum, VoiceTyp } } -void Sound::_soPlaySound(VoiceTypeStruct *vtStruct, const byte *channelData, int channelNum, VoiceType voiceType, int v0, int v1) { - int entryIndex = _soFindSound(vtStruct, channelNum); +void Sound::soPlaySound(VoiceTypeStruct *vtStruct, const byte *channelData, int channelNum, VoiceType voiceType, int v0, int v1) { + int entryIndex = soFindSound(vtStruct, channelNum); if (entryIndex != -1) { SoundDriver *driver = vtStruct->_entries[entryIndex]._driver; assert(driver); @@ -2175,11 +2175,11 @@ void Sound::_soPlaySound(VoiceTypeStruct *vtStruct, const byte *channelData, int } } -void Sound::_soPlaySound2(VoiceTypeStruct *vtStruct, const byte *channelData, int channelNum, VoiceType voiceType, int v0) { +void Sound::soPlaySound2(VoiceTypeStruct *vtStruct, const byte *channelData, int channelNum, VoiceType voiceType, int v0) { for (int trackCtr = 0; trackCtr < _trackInfo._numTracks; ++trackCtr) { const byte *instrument = _channelData[trackCtr]; if ((*(instrument + 13) == v0) && (*instrument == 1)) { - int entryIndex = _soFindSound(vtStruct, channelNum); + int entryIndex = soFindSound(vtStruct, channelNum); if (entryIndex != -1) { SoundDriver *driver = vtStruct->_entries[entryIndex]._driver; @@ -2199,7 +2199,7 @@ void Sound::_soPlaySound2(VoiceTypeStruct *vtStruct, const byte *channelData, in } } -void Sound::_soProc38(VoiceTypeStruct *vtStruct, int channelNum, VoiceType voiceType, int cmd, int value) { +void Sound::soProc38(VoiceTypeStruct *vtStruct, int channelNum, VoiceType voiceType, int cmd, int value) { if (cmd == 64) { if (value == 0) { for (uint entryIndex = 0; entryIndex < vtStruct->_entries.size(); ++entryIndex) { @@ -2231,7 +2231,7 @@ void Sound::_soProc38(VoiceTypeStruct *vtStruct, int channelNum, VoiceType voice } } -void Sound::_soProc40(VoiceTypeStruct *vtStruct, int channelNum, int pitchBlend) { +void Sound::soProc40(VoiceTypeStruct *vtStruct, int channelNum, int pitchBlend) { for (uint entryIndex = 0; entryIndex < vtStruct->_entries.size(); ++entryIndex) { VoiceStructEntryType1 &vte = vtStruct->_entries[entryIndex]._type1; @@ -2244,7 +2244,7 @@ void Sound::_soProc40(VoiceTypeStruct *vtStruct, int channelNum, int pitchBlend) } } -void Sound::_soDoTrackCommand(int channelNum, int command, int value) { +void Sound::soDoTrackCommand(int channelNum, int command, int value) { switch (command) { case 1: _chModulation[channelNum] = value; @@ -2264,7 +2264,7 @@ void Sound::_soDoTrackCommand(int channelNum, int command, int value) { } } -bool Sound::_soDoUpdateTracks(int command, int value) { +bool Sound::soDoUpdateTracks(int command, int value) { if ((command == 76) || (_hold != value)) return false; @@ -2278,7 +2278,7 @@ bool Sound::_soDoUpdateTracks(int command, int value) { return true; } -void Sound::_soSetTrackPos(int trackIndex, int trackPos, int cueValue) { +void Sound::soSetTrackPos(int trackIndex, int trackPos, int cueValue) { _trkIndex[trackIndex] = trackPos; if (cueValue == 127) { if (!_soundManager->_soTimeIndexFlag) @@ -2294,7 +2294,7 @@ void Sound::_soSetTrackPos(int trackIndex, int trackPos, int cueValue) { } } -void Sound::_soServiceTrackType1(int trackIndex, const byte *channelData) { +void Sound::soServiceTrackType1(int trackIndex, const byte *channelData) { if (_soundManager->_soTimeIndexFlag || !_trkState[trackIndex]) return; @@ -2310,7 +2310,7 @@ void Sound::_soServiceTrackType1(int trackIndex, const byte *channelData) { else { if (vtStruct->_voiceType != VOICETYPE_0) { if (_trkState[trackIndex] == 1) { - int entryIndex = _soFindSound(vtStruct, *(channelData + 1)); + int entryIndex = soFindSound(vtStruct, *(channelData + 1)); if (entryIndex != -1) { SoundDriver *driver = vtStruct->_entries[entryIndex]._driver; assert(driver); @@ -2352,7 +2352,7 @@ void Sound::_soServiceTrackType1(int trackIndex, const byte *channelData) { } } -int Sound::_soFindSound(VoiceTypeStruct *vtStruct, int channelNum) { +int Sound::soFindSound(VoiceTypeStruct *vtStruct, int channelNum) { int entryIndex = -1, entry2Index = -1; int v6 = 0, v8 = 0; @@ -2539,10 +2539,10 @@ AdlibSoundDriver::AdlibSoundDriver(): SoundDriver() { _maxVersion = 0x10A; _masterVolume = 0; - _groupData.groupMask = 9; - _groupData.v1 = 0x46; - _groupData.v2 = 0; - _groupData.pData = &adlib_group_data[0]; + _groupData._groupMask = 9; + _groupData._v1 = 0x46; + _groupData._v2 = 0; + _groupData._pData = &adlib_group_data[0]; _mixer = g_vm->_mixer; _sampleRate = _mixer->getOutputRate(); @@ -2823,7 +2823,7 @@ int AdlibSoundDriver::readBuffer(int16 *buffer, const int numSamples) { memset(buffer, 0, sizeof(int16) * numSamples); while (samplesLeft) { if (!_samplesTillCallback) { - SoundManager::_sfUpdateCallback(NULL); + SoundManager::sfUpdateCallback(NULL); flush(); _samplesTillCallback = _samplesPerCallback; @@ -2852,11 +2852,11 @@ SoundBlasterDriver::SoundBlasterDriver(): SoundDriver() { _maxVersion = 0x10A; _masterVolume = 0; - _groupData.groupMask = 1; - _groupData.v1 = 0x3E; - _groupData.v2 = 0; + _groupData._groupMask = 1; + _groupData._v1 = 0x3E; + _groupData._v2 = 0; static byte const group_data[] = { 3, 1, 1, 0, 0xff }; - _groupData.pData = group_data; + _groupData._pData = group_data; _mixer = g_vm->_mixer; _sampleRate = _mixer->getOutputRate(); diff --git a/engines/tsage/sound.h b/engines/tsage/sound.h index 77d1f3d3ac..2f59afb49b 100644 --- a/engines/tsage/sound.h +++ b/engines/tsage/sound.h @@ -54,18 +54,18 @@ enum VoiceType {VOICETYPE_0 = 0, VOICETYPE_1 = 1}; class SoundDriverEntry { public: - int driverNum; - SoundDriverStatus status; - int field2, field6; - Common::String shortDescription; - Common::String longDescription; + int _driverNum; + SoundDriverStatus _status; + int _field2, _field6; + Common::String _shortDescription; + Common::String _longDescription; }; struct GroupData { - uint32 groupMask; - byte v1; - byte v2; - const byte *pData; + uint32 _groupMask; + byte _v1; + byte _v2; + const byte *_pData; }; struct RegisterValue { @@ -229,31 +229,31 @@ public: // _sf methods static SoundManager &sfManager(); - static void _sfTerminate(); - static int _sfDetermineGroup(const byte *soundData); - static void _sfAddToPlayList(Sound *sound); - static void _sfRemoveFromPlayList(Sound *sound); - static bool _sfIsOnPlayList(Sound *sound); - static void _sfRethinkSoundDrivers(); - static void _sfRethinkVoiceTypes(); - static void _sfUpdateVolume(Sound *sound); - static void _sfDereferenceAll(); - static void _sfUpdatePriority(Sound *sound); - static void _sfUpdateLoop(Sound *sound); - static void _sfSetMasterVol(int volume); - static void _sfExtractTrackInfo(trackInfoStruct *trackInfo, const byte *soundData, int groupNum); - static void _sfExtractGroupMask(); - static bool _sfInstallDriver(SoundDriver *driver); - static void _sfUnInstallDriver(SoundDriver *driver); - static void _sfInstallPatchBank(SoundDriver *driver, const byte *bankData); - static void _sfDoAddToPlayList(Sound *sound); - static bool _sfDoRemoveFromPlayList(Sound *sound); - static void _sfDoUpdateVolume(Sound *sound); - static void _sfSoundServer(); - static void _sfProcessFading(); - static void _sfUpdateVoiceStructs(); - static void _sfUpdateVoiceStructs2(); - static void _sfUpdateCallback(void *ref); + static void sfTerminate(); + static int sfDetermineGroup(const byte *soundData); + static void sfAddToPlayList(Sound *sound); + static void sfRemoveFromPlayList(Sound *sound); + static bool sfIsOnPlayList(Sound *sound); + static void sfRethinkSoundDrivers(); + static void sfRethinkVoiceTypes(); + static void sfUpdateVolume(Sound *sound); + static void sfDereferenceAll(); + static void sfUpdatePriority(Sound *sound); + static void sfUpdateLoop(Sound *sound); + static void sfSetMasterVol(int volume); + static void sfExtractTrackInfo(trackInfoStruct *trackInfo, const byte *soundData, int groupNum); + static void sfExtractGroupMask(); + static bool sfInstallDriver(SoundDriver *driver); + static void sfUnInstallDriver(SoundDriver *driver); + static void sfInstallPatchBank(SoundDriver *driver, const byte *bankData); + static void sfDoAddToPlayList(Sound *sound); + static bool sfDoRemoveFromPlayList(Sound *sound); + static void sfDoUpdateVolume(Sound *sound); + static void sfSoundServer(); + static void sfProcessFading(); + static void sfUpdateVoiceStructs(); + static void sfUpdateVoiceStructs2(); + static void sfUpdateCallback(void *ref); }; class Sound: public EventHandler { @@ -343,23 +343,23 @@ public: void orientAfterDriverChange(); // _so methods - void _soPrimeSound(bool dontQueue); - void _soSetTimeIndex(uint timeIndex); - bool _soServiceTracks(); - void _soPrimeChannelData(); - void _soRemoteReceive(); - void _soServiceTrackType0(int trackIndex, const byte *channelData); - void _soUpdateDamper(VoiceTypeStruct *voiceType, int channelNum, VoiceType mode, int v0); - void _soPlaySound(VoiceTypeStruct *vtStruct, const byte *channelData, int channelNum, VoiceType voiceType, int v0, int v1); - void _soPlaySound2(VoiceTypeStruct *vtStruct, const byte *channelData, int channelNum, VoiceType voiceType, int v0); - void _soProc38(VoiceTypeStruct *vtStruct, int channelNum, VoiceType voiceType, int cmd, int value); - void _soProc40(VoiceTypeStruct *vtStruct, int channelNum, int pitchBlend); - void _soDoTrackCommand(int channelNum, int command, int value); - bool _soDoUpdateTracks(int command, int value); - void _soSetTrackPos(int trackIndex, int trackPos, int cueValue); - - void _soServiceTrackType1(int trackIndex, const byte *channelData); - int _soFindSound(VoiceTypeStruct *vtStruct, int channelNum); + void soPrimeSound(bool dontQueue); + void soSetTimeIndex(uint timeIndex); + bool soServiceTracks(); + void soPrimeChannelData(); + void soRemoteReceive(); + void soServiceTrackType0(int trackIndex, const byte *channelData); + void soUpdateDamper(VoiceTypeStruct *voiceType, int channelNum, VoiceType mode, int v0); + void soPlaySound(VoiceTypeStruct *vtStruct, const byte *channelData, int channelNum, VoiceType voiceType, int v0, int v1); + void soPlaySound2(VoiceTypeStruct *vtStruct, const byte *channelData, int channelNum, VoiceType voiceType, int v0); + void soProc38(VoiceTypeStruct *vtStruct, int channelNum, VoiceType voiceType, int cmd, int value); + void soProc40(VoiceTypeStruct *vtStruct, int channelNum, int pitchBlend); + void soDoTrackCommand(int channelNum, int command, int value); + bool soDoUpdateTracks(int command, int value); + void soSetTrackPos(int trackIndex, int trackPos, int cueValue); + + void soServiceTrackType1(int trackIndex, const byte *channelData); + int soFindSound(VoiceTypeStruct *vtStruct, int channelNum); }; class ASound: public EventHandler { diff --git a/engines/tsage/tsage.cpp b/engines/tsage/tsage.cpp index 40f4dfcfd2..87697f950b 100644 --- a/engines/tsage/tsage.cpp +++ b/engines/tsage/tsage.cpp @@ -45,7 +45,7 @@ TSageEngine::TSageEngine(OSystem *system, const tSageGameDescription *gameDesc) else if (g_vm->getGameID() == GType_BlueForce) _debugger = new BlueForceDebugger(); else if (g_vm->getGameID() == GType_Ringworld2) - _debugger = new Ringworld2Debugger(); + _debugger = new Ringworld2Debugger(); } Common::Error TSageEngine::init() { @@ -92,7 +92,7 @@ void TSageEngine::initialize() { g_resourceManager->addLib("TSAGE.RLB"); } g_globals = new BlueForce::BlueForceGlobals(); - + // Setup the user interface T2_GLOBALS._uiElements.setup(Common::Point(0, UI_INTERFACE_Y - 2)); @@ -107,7 +107,7 @@ void TSageEngine::initialize() { // Reset all global variables R2_GLOBALS.reset(); - } + } g_globals->gfxManager().setDefaults(); diff --git a/engines/tsage/user_interface.cpp b/engines/tsage/user_interface.cpp index 10cb6961dc..4bd9e49875 100644 --- a/engines/tsage/user_interface.cpp +++ b/engines/tsage/user_interface.cpp @@ -112,7 +112,7 @@ void UIQuestion::showItem(int resNum, int rlbNum, int frameNum) { imgRect.center(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2); // Save the area behind where the image will be displayed - GfxSurface *savedArea = Surface_getArea(GLOBALS.gfxManager().getSurface(), imgRect); + GfxSurface *savedArea = surfaceGetArea(GLOBALS.gfxManager().getSurface(), imgRect); // Draw the image GLOBALS.gfxManager().copyFrom(objImage, imgRect); diff --git a/engines/tucker/resource.cpp b/engines/tucker/resource.cpp index bee09f7391..1b04f3fae9 100644 --- a/engines/tucker/resource.cpp +++ b/engines/tucker/resource.cpp @@ -29,6 +29,9 @@ #include "audio/decoders/vorbis.h" #include "audio/decoders/wave.h" +#include "graphics/surface.h" +#include "graphics/decoders/pcx.h" + #include "tucker/tucker.h" #include "tucker/graphics.h" @@ -298,23 +301,21 @@ void TuckerEngine::loadImage(const char *fname, uint8 *dst, int type) { return; } } - f.seek(128, SEEK_SET); - int size = 0; - while (size < 64000) { - int code = f.readByte(); - if (code >= 0xC0) { - const int sz = code - 0xC0; - code = f.readByte(); - memset(dst + size, code, sz); - size += sz; - } else { - dst[size++] = code; - } - } + + ::Graphics::PCXDecoder pcx; + if (!pcx.loadStream(f)) + error("Error while reading PCX image"); + + const ::Graphics::Surface *pcxSurface = pcx.getSurface(); + if (pcxSurface->format.bytesPerPixel != 1) + error("Invalid bytes per pixel in PCX surface (%d)", pcxSurface->format.bytesPerPixel); + if (pcxSurface->w != 320 || pcxSurface->h != 200) + error("Invalid PCX surface size (%d x %d)", pcxSurface->w, pcxSurface->h); + for (uint16 y = 0; y < pcxSurface->h; y++) + memcpy(dst + y * 320, pcxSurface->getBasePtr(0, y), pcxSurface->w); + if (type != 0) { - if (f.readByte() != 12) - return; - f.read(_currentPalette, 768); + memcpy(_currentPalette, pcx.getPalette(), 3 * 256); setBlackPalette(); } } diff --git a/engines/tucker/sequences.cpp b/engines/tucker/sequences.cpp index 775fd6f1a0..16c4f4f6f0 100644 --- a/engines/tucker/sequences.cpp +++ b/engines/tucker/sequences.cpp @@ -28,6 +28,7 @@ #include "audio/decoders/wave.h" #include "graphics/palette.h" +#include "graphics/surface.h" #include "tucker/tucker.h" #include "tucker/graphics.h" @@ -749,6 +750,7 @@ void AnimationSequencePlayer::openAnimation(int index, const char *fileName) { _seqNum = 1; return; } + _flicPlayer[index].start(); _flicPlayer[index].decodeNextFrame(); if (index == 0) { getRGBPalette(index); @@ -801,7 +803,7 @@ void AnimationSequencePlayer::playIntroSeq19_20() { if (_flicPlayer[0].getCurFrame() >= 115) { surface = _flicPlayer[1].decodeNextFrame(); if (_flicPlayer[1].endOfVideo()) - _flicPlayer[1].reset(); + _flicPlayer[1].rewind(); } bool framesLeft = decodeNextAnimationFrame(0, false); diff --git a/engines/wintermute/ad/ad_actor.cpp b/engines/wintermute/ad/ad_actor.cpp new file mode 100644 index 0000000000..74fb4a5ae2 --- /dev/null +++ b/engines/wintermute/ad/ad_actor.cpp @@ -0,0 +1,1460 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_actor.h" +#include "engines/wintermute/ad/ad_game.h" +#include "engines/wintermute/ad/ad_scene.h" +#include "engines/wintermute/ad/ad_entity.h" +#include "engines/wintermute/ad/ad_sprite_set.h" +#include "engines/wintermute/ad/ad_waypoint_group.h" +#include "engines/wintermute/ad/ad_path.h" +#include "engines/wintermute/ad/ad_sentence.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/sound/base_sound.h" +#include "engines/wintermute/base/base_region.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/base/particles/part_emitter.h" +#include "engines/wintermute/base/base_engine.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdActor, false) + + +////////////////////////////////////////////////////////////////////////// +AdActor::AdActor(BaseGame *inGame) : AdTalkHolder(inGame) { + _path = new AdPath(_gameRef); + + _type = OBJECT_ACTOR; + _dir = DI_LEFT; + + _walkSprite = NULL; + _standSprite = NULL; + _turnLeftSprite = NULL; + _turnRightSprite = NULL; + + _targetPoint = new BasePoint; + _afterWalkDir = DI_NONE; + + _animSprite2 = NULL; + + setDefaultAnimNames(); +} + +////////////////////////////////////////////////////////////////////////// +bool AdActor::setDefaultAnimNames() { + _talkAnimName = "talk"; + _idleAnimName = "idle"; + _walkAnimName = "walk"; + _turnLeftAnimName = "turnleft"; + _turnRightAnimName = "turnright"; + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +AdActor::~AdActor() { + delete _path; + delete _targetPoint; + _path = NULL; + _targetPoint = NULL; + + delete _walkSprite; + delete _standSprite; + delete _turnLeftSprite; + delete _turnRightSprite; + _walkSprite = NULL; + _standSprite = NULL; + _turnLeftSprite = NULL; + _turnRightSprite = NULL; + + _animSprite2 = NULL; // ref only + + for (uint32 i = 0; i < _talkSprites.size(); i++) { + delete _talkSprites[i]; + } + _talkSprites.clear(); + + for (uint32 i = 0; i < _talkSpritesEx.size(); i++) { + delete _talkSpritesEx[i]; + } + _talkSpritesEx.clear(); + + for (uint32 i = 0; i < _anims.size(); i++) { + delete _anims[i]; + _anims[i] = NULL; + } + _anims.clear(); + +} + + +////////////////////////////////////////////////////////////////////////// +bool AdActor::loadFile(const char *filename) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "AdActor::LoadFile failed for file '%s'", filename); + return STATUS_FAILED; + } + + bool ret; + + setFilename(filename); + + if (DID_FAIL(ret = loadBuffer(buffer, true))) { + _gameRef->LOG(0, "Error parsing ACTOR file '%s'", filename); + } + + + delete[] buffer; + + return ret; +} + + +TOKEN_DEF_START +TOKEN_DEF(ACTOR) +TOKEN_DEF(X) +TOKEN_DEF(Y) +TOKEN_DEF(TEMPLATE) +TOKEN_DEF(NAME) +TOKEN_DEF(SCALABLE) +TOKEN_DEF(REGISTRABLE) +TOKEN_DEF(INTERACTIVE) +TOKEN_DEF(SHADOWABLE) +TOKEN_DEF(COLORABLE) +TOKEN_DEF(ACTIVE) +TOKEN_DEF(WALK) +TOKEN_DEF(STAND) +TOKEN_DEF(TALK_SPECIAL) +TOKEN_DEF(TALK) +TOKEN_DEF(TURN_LEFT) +TOKEN_DEF(TURN_RIGHT) +TOKEN_DEF(EVENTS) +TOKEN_DEF(FONT) +TOKEN_DEF(CURSOR) +TOKEN_DEF(SCRIPT) +TOKEN_DEF(SOUND_VOLUME) +TOKEN_DEF(SOUND_PANNING) +TOKEN_DEF(CAPTION) +TOKEN_DEF(PROPERTY) +TOKEN_DEF(BLOCKED_REGION) +TOKEN_DEF(WAYPOINTS) +TOKEN_DEF(IGNORE_ITEMS) +TOKEN_DEF(ROTABLE) +TOKEN_DEF(ROTATABLE) +TOKEN_DEF(ALPHA_COLOR) +TOKEN_DEF(SCALE) +TOKEN_DEF(RELATIVE_SCALE) +TOKEN_DEF(ALPHA) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF(ANIMATION) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool AdActor::loadBuffer(byte *buffer, bool complete) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(ACTOR) + TOKEN_TABLE(X) + TOKEN_TABLE(Y) + TOKEN_TABLE(TEMPLATE) + TOKEN_TABLE(NAME) + TOKEN_TABLE(SCALABLE) + TOKEN_TABLE(REGISTRABLE) + TOKEN_TABLE(INTERACTIVE) + TOKEN_TABLE(SHADOWABLE) + TOKEN_TABLE(COLORABLE) + TOKEN_TABLE(ACTIVE) + TOKEN_TABLE(WALK) + TOKEN_TABLE(STAND) + TOKEN_TABLE(TALK_SPECIAL) + TOKEN_TABLE(TALK) + TOKEN_TABLE(TURN_LEFT) + TOKEN_TABLE(TURN_RIGHT) + TOKEN_TABLE(EVENTS) + TOKEN_TABLE(FONT) + TOKEN_TABLE(CURSOR) + TOKEN_TABLE(SCRIPT) + TOKEN_TABLE(SOUND_VOLUME) + TOKEN_TABLE(SOUND_PANNING) + TOKEN_TABLE(CAPTION) + TOKEN_TABLE(PROPERTY) + TOKEN_TABLE(BLOCKED_REGION) + TOKEN_TABLE(WAYPOINTS) + TOKEN_TABLE(IGNORE_ITEMS) + TOKEN_TABLE(ROTABLE) + TOKEN_TABLE(ROTATABLE) + TOKEN_TABLE(ALPHA_COLOR) + TOKEN_TABLE(SCALE) + TOKEN_TABLE(RELATIVE_SCALE) + TOKEN_TABLE(ALPHA) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE(ANIMATION) + TOKEN_TABLE_END + + byte *params; + int cmd; + BaseParser parser; + + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_ACTOR) { + _gameRef->LOG(0, "'ACTOR' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + AdGame *adGame = (AdGame *)_gameRef; + AdSpriteSet *spr = NULL; + int ar = 0, ag = 0, ab = 0, alpha = 0; + while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_TEMPLATE: + if (DID_FAIL(loadFile((char *)params))) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_X: + parser.scanStr((char *)params, "%d", &_posX); + break; + + case TOKEN_Y: + parser.scanStr((char *)params, "%d", &_posY); + break; + + case TOKEN_NAME: + setName((char *)params); + break; + + case TOKEN_CAPTION: + setCaption((char *)params); + break; + + case TOKEN_FONT: + setFont((char *)params); + break; + + case TOKEN_SCALABLE: + parser.scanStr((char *)params, "%b", &_zoomable); + break; + + case TOKEN_ROTABLE: + case TOKEN_ROTATABLE: + parser.scanStr((char *)params, "%b", &_rotatable); + break; + + case TOKEN_REGISTRABLE: + case TOKEN_INTERACTIVE: + parser.scanStr((char *)params, "%b", &_registrable); + break; + + case TOKEN_SHADOWABLE: + case TOKEN_COLORABLE: + parser.scanStr((char *)params, "%b", &_shadowable); + break; + + case TOKEN_ACTIVE: + parser.scanStr((char *)params, "%b", &_active); + break; + + case TOKEN_WALK: + delete _walkSprite; + _walkSprite = NULL; + spr = new AdSpriteSet(_gameRef, this); + if (!spr || DID_FAIL(spr->loadBuffer(params, true, adGame->_texWalkLifeTime, CACHE_HALF))) { + cmd = PARSERR_GENERIC; + } else { + _walkSprite = spr; + } + break; + + case TOKEN_TALK: + spr = new AdSpriteSet(_gameRef, this); + if (!spr || DID_FAIL(spr->loadBuffer(params, true, adGame->_texTalkLifeTime))) { + cmd = PARSERR_GENERIC; + } else { + _talkSprites.add(spr); + } + break; + + case TOKEN_TALK_SPECIAL: + spr = new AdSpriteSet(_gameRef, this); + if (!spr || DID_FAIL(spr->loadBuffer(params, true, adGame->_texTalkLifeTime))) { + cmd = PARSERR_GENERIC; + } else { + _talkSpritesEx.add(spr); + } + break; + + case TOKEN_STAND: + delete _standSprite; + _standSprite = NULL; + spr = new AdSpriteSet(_gameRef, this); + if (!spr || DID_FAIL(spr->loadBuffer(params, true, adGame->_texStandLifeTime))) { + cmd = PARSERR_GENERIC; + } else { + _standSprite = spr; + } + break; + + case TOKEN_TURN_LEFT: + delete _turnLeftSprite; + _turnLeftSprite = NULL; + spr = new AdSpriteSet(_gameRef, this); + if (!spr || DID_FAIL(spr->loadBuffer(params, true))) { + cmd = PARSERR_GENERIC; + } else { + _turnLeftSprite = spr; + } + break; + + case TOKEN_TURN_RIGHT: + delete _turnRightSprite; + _turnRightSprite = NULL; + spr = new AdSpriteSet(_gameRef, this); + if (!spr || DID_FAIL(spr->loadBuffer(params, true))) { + cmd = PARSERR_GENERIC; + } else { + _turnRightSprite = spr; + } + break; + + case TOKEN_SCRIPT: + addScript((char *)params); + break; + + case TOKEN_CURSOR: + delete _cursor; + _cursor = new BaseSprite(_gameRef); + if (!_cursor || DID_FAIL(_cursor->loadFile((char *)params))) { + delete _cursor; + _cursor = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_SOUND_VOLUME: + parser.scanStr((char *)params, "%d", &_sFXVolume); + break; + + case TOKEN_SCALE: { + int s; + parser.scanStr((char *)params, "%d", &s); + _scale = (float)s; + + } + break; + + case TOKEN_RELATIVE_SCALE: { + int s; + parser.scanStr((char *)params, "%d", &s); + _relativeScale = (float)s; + + } + break; + + case TOKEN_SOUND_PANNING: + parser.scanStr((char *)params, "%b", &_autoSoundPanning); + break; + + case TOKEN_PROPERTY: + parseProperty(params, false); + break; + + case TOKEN_BLOCKED_REGION: { + delete _blockRegion; + delete _currentBlockRegion; + _blockRegion = NULL; + _currentBlockRegion = NULL; + BaseRegion *rgn = new BaseRegion(_gameRef); + BaseRegion *crgn = new BaseRegion(_gameRef); + if (!rgn || !crgn || DID_FAIL(rgn->loadBuffer(params, false))) { + delete _blockRegion; + delete _currentBlockRegion; + _blockRegion = NULL; + _currentBlockRegion = NULL; + cmd = PARSERR_GENERIC; + } else { + _blockRegion = rgn; + _currentBlockRegion = crgn; + _currentBlockRegion->mimic(_blockRegion); + } + } + break; + + case TOKEN_WAYPOINTS: { + delete _wptGroup; + delete _currentWptGroup; + _wptGroup = NULL; + _currentWptGroup = NULL; + AdWaypointGroup *wpt = new AdWaypointGroup(_gameRef); + AdWaypointGroup *cwpt = new AdWaypointGroup(_gameRef); + if (!wpt || !cwpt || DID_FAIL(wpt->loadBuffer(params, false))) { + delete _wptGroup; + delete _currentWptGroup; + _wptGroup = NULL; + _currentWptGroup = NULL; + cmd = PARSERR_GENERIC; + } else { + _wptGroup = wpt; + _currentWptGroup = cwpt; + _currentWptGroup->mimic(_wptGroup); + } + } + break; + + case TOKEN_IGNORE_ITEMS: + parser.scanStr((char *)params, "%b", &_ignoreItems); + break; + + case TOKEN_ALPHA_COLOR: + parser.scanStr((char *)params, "%d,%d,%d", &ar, &ag, &ab); + break; + + case TOKEN_ALPHA: + parser.scanStr((char *)params, "%d", &alpha); + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty(params, false); + break; + + case TOKEN_ANIMATION: { + AdSpriteSet *anim = new AdSpriteSet(_gameRef, this); + if (!anim || DID_FAIL(anim->loadBuffer(params, false))) { + cmd = PARSERR_GENERIC; + } else { + _anims.add(anim); + } + } + break; + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in ACTOR definition"); + return STATUS_FAILED; + } + if (cmd == PARSERR_GENERIC) { + if (spr) { + delete spr; + } + _gameRef->LOG(0, "Error loading ACTOR definition"); + return STATUS_FAILED; + } + + if (alpha != 0 && ar == 0 && ag == 0 && ab == 0) { + ar = ag = ab = 255; + } + _alphaColor = BYTETORGBA(ar, ag, ab, alpha); + _state = _nextState = STATE_READY; + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +void AdActor::turnTo(TDirection dir) { + int delta1, delta2, delta3, delta; + + delta1 = dir - _dir; + delta2 = dir + NUM_DIRECTIONS - _dir; + delta3 = dir - NUM_DIRECTIONS - _dir; + + delta1 = (abs(delta1) <= abs(delta2)) ? delta1 : delta2; + delta = (abs(delta1) <= abs(delta3)) ? delta1 : delta3; + + // already there? + if (abs(delta) < 2) { + _dir = dir; + _state = _nextState; + _nextState = STATE_READY; + return; + } + + _targetDir = dir; + _state = delta < 0 ? STATE_TURNING_LEFT : STATE_TURNING_RIGHT; + + _tempSprite2 = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +void AdActor::goTo(int x, int y, TDirection afterWalkDir) { + _afterWalkDir = afterWalkDir; + if (x == _targetPoint->x && y == _targetPoint->y && _state == STATE_FOLLOWING_PATH) { + return; + } + + _path->reset(); + _path->setReady(false); + + _targetPoint->x = x; + _targetPoint->y = y; + + ((AdGame *)_gameRef)->_scene->correctTargetPoint(_posX, _posY, &_targetPoint->x, &_targetPoint->y, true, this); + + _state = STATE_SEARCHING_PATH; + +} + + +////////////////////////////////////////////////////////////////////////// +bool AdActor::display() { + if (_active) { + updateSounds(); + } + + uint32 alpha; + if (_alphaColor != 0) { + alpha = _alphaColor; + } else { + alpha = _shadowable ? ((AdGame *)_gameRef)->_scene->getAlphaAt(_posX, _posY, true) : 0xFFFFFFFF; + } + + float scaleX, scaleY; + getScale(&scaleX, &scaleY); + + + float rotate; + if (_rotatable) { + if (_rotateValid) { + rotate = _rotate; + } else { + rotate = ((AdGame *)_gameRef)->_scene->getRotationAt(_posX, _posY) + _relativeRotate; + } + } else { + rotate = 0.0f; + } + + if (_active) { + displaySpriteAttachments(true); + } + + if (_currentSprite && _active) { + bool reg = _registrable; + if (_ignoreItems && ((AdGame *)_gameRef)->_selectedItem) { + reg = false; + } + + _currentSprite->display(_posX, + _posY, + reg ? _registerAlias : NULL, + scaleX, + scaleY, + alpha, + rotate, + _blendMode); + + } + + if (_active) { + displaySpriteAttachments(false); + } + if (_active && _partEmitter) { + _partEmitter->display(); + } + + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdActor::update() { + _currentSprite = NULL; + + if (_state == STATE_READY) { + if (_animSprite) { + delete _animSprite; + _animSprite = NULL; + } + if (_animSprite2) { + _animSprite2 = NULL; + } + } + + // finished playing animation? + if (_state == STATE_PLAYING_ANIM && _animSprite != NULL && _animSprite->isFinished()) { + _state = _nextState; + _nextState = STATE_READY; + _currentSprite = _animSprite; + } + + if (_state == STATE_PLAYING_ANIM_SET && _animSprite2 != NULL && _animSprite2->isFinished()) { + _state = _nextState; + _nextState = STATE_READY; + _currentSprite = _animSprite2; + } + + if (_sentence && _state != STATE_TALKING) { + _sentence->finish(); + } + + // default: stand animation + if (!_currentSprite) { + if (_sprite) { + _currentSprite = _sprite; + } else { + if (_standSprite) { + _currentSprite = _standSprite->getSprite(_dir); + } else { + AdSpriteSet *anim = getAnimByName(_idleAnimName); + if (anim) { + _currentSprite = anim->getSprite(_dir); + } + } + } + } + + bool already_moved = false; + + switch (_state) { + ////////////////////////////////////////////////////////////////////////// + case STATE_PLAYING_ANIM: + _currentSprite = _animSprite; + break; + + ////////////////////////////////////////////////////////////////////////// + case STATE_PLAYING_ANIM_SET: + _currentSprite = _animSprite2; + break; + + ////////////////////////////////////////////////////////////////////////// + case STATE_TURNING_LEFT: + if (_tempSprite2 == NULL || _tempSprite2->isFinished()) { + if (_dir > 0) { + _dir = (TDirection)(_dir - 1); + } else { + _dir = (TDirection)(NUM_DIRECTIONS - 1); + } + + if (_dir == _targetDir) { + _tempSprite2 = NULL; + _state = _nextState; + _nextState = STATE_READY; + } else { + if (_turnLeftSprite) { + _tempSprite2 = _turnLeftSprite->getSprite(_dir); + } else { + AdSpriteSet *anim = getAnimByName(_turnLeftAnimName); + if (anim) { + _tempSprite2 = anim->getSprite(_dir); + } + } + + if (_tempSprite2) { + _tempSprite2->reset(); + if (_tempSprite2->_looping) { + _tempSprite2->_looping = false; + } + } + _currentSprite = _tempSprite2; + } + } else { + _currentSprite = _tempSprite2; + } + break; + + + ////////////////////////////////////////////////////////////////////////// + case STATE_TURNING_RIGHT: + if (_tempSprite2 == NULL || _tempSprite2->isFinished()) { + _dir = (TDirection)(_dir + 1); + + if ((int)_dir >= (int)NUM_DIRECTIONS) { + _dir = (TDirection)(0); + } + + if (_dir == _targetDir) { + _tempSprite2 = NULL; + _state = _nextState; + _nextState = STATE_READY; + } else { + if (_turnRightSprite) { + _tempSprite2 = _turnRightSprite->getSprite(_dir); + } else { + AdSpriteSet *anim = getAnimByName(_turnRightAnimName); + if (anim) { + _tempSprite2 = anim->getSprite(_dir); + } + } + + if (_tempSprite2) { + _tempSprite2->reset(); + if (_tempSprite2->_looping) { + _tempSprite2->_looping = false; + } + } + _currentSprite = _tempSprite2; + } + } else { + _currentSprite = _tempSprite2; + } + break; + + + ////////////////////////////////////////////////////////////////////////// + case STATE_SEARCHING_PATH: + // keep asking scene for the path + if (((AdGame *)_gameRef)->_scene->getPath(BasePoint(_posX, _posY), *_targetPoint, _path, this)) { + _state = STATE_WAITING_PATH; + } + break; + + + ////////////////////////////////////////////////////////////////////////// + case STATE_WAITING_PATH: + // wait until the scene finished the path + if (_path->_ready) { + followPath(); + } + break; + + + ////////////////////////////////////////////////////////////////////////// + case STATE_FOLLOWING_PATH: + getNextStep(); + already_moved = true; + break; + + ////////////////////////////////////////////////////////////////////////// + case STATE_TALKING: { + _sentence->update(_dir); + if (_sentence->_currentSprite) { + _tempSprite2 = _sentence->_currentSprite; + } + + bool timeIsUp = (_sentence->_sound && _sentence->_soundStarted && (!_sentence->_sound->isPlaying() && !_sentence->_sound->isPaused())) || (!_sentence->_sound && _sentence->_duration <= _gameRef->_timer - _sentence->_startTime); + if (_tempSprite2 == NULL || _tempSprite2->isFinished() || (/*_tempSprite2->_looping &&*/ timeIsUp)) { + if (timeIsUp) { + _sentence->finish(); + _tempSprite2 = NULL; + _state = _nextState; + _nextState = STATE_READY; + } else { + _tempSprite2 = getTalkStance(_sentence->getNextStance()); + if (_tempSprite2) { + _tempSprite2->reset(); + _currentSprite = _tempSprite2; + ((AdGame *)_gameRef)->addSentence(_sentence); + } + } + } else { + _currentSprite = _tempSprite2; + ((AdGame *)_gameRef)->addSentence(_sentence); + } + } + break; + + ////////////////////////////////////////////////////////////////////////// + case STATE_READY: + if (!_animSprite && !_animSprite2) { + if (_sprite) { + _currentSprite = _sprite; + } else { + if (_standSprite) { + _currentSprite = _standSprite->getSprite(_dir); + } else { + AdSpriteSet *anim = getAnimByName(_idleAnimName); + if (anim) { + _currentSprite = anim->getSprite(_dir); + } + } + } + } + break; + default: + error("AdActor::Update - Unhandled enum"); + } + + + if (_currentSprite && !already_moved) { + _currentSprite->getCurrentFrame(_zoomable ? ((AdGame *)_gameRef)->_scene->getZoomAt(_posX, _posY) : 100, _zoomable ? ((AdGame *)_gameRef)->_scene->getZoomAt(_posX, _posY) : 100); + if (_currentSprite->isChanged()) { + _posX += _currentSprite->_moveX; + _posY += _currentSprite->_moveY; + afterMove(); + } + } + + //_gameRef->QuickMessageForm("%s", _currentSprite->_filename); + + updateBlockRegion(); + _ready = (_state == STATE_READY); + + updatePartEmitter(); + updateSpriteAttachments(); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +void AdActor::followPath() { + // skip current position + _path->getFirst(); + while (_path->getCurrent() != NULL) { + if (_path->getCurrent()->x != _posX || _path->getCurrent()->y != _posY) { + break; + } + _path->getNext(); + } + + // are there points to follow? + if (_path->getCurrent() != NULL) { + _state = STATE_FOLLOWING_PATH; + initLine(BasePoint(_posX, _posY), *_path->getCurrent()); + } else { + if (_afterWalkDir != DI_NONE) { + turnTo(_afterWalkDir); + } else { + _state = STATE_READY; + } + } +} + + +////////////////////////////////////////////////////////////////////////// +void AdActor::getNextStep() { + if (_walkSprite) { + _currentSprite = _walkSprite->getSprite(_dir); + } else { + AdSpriteSet *anim = getAnimByName(_walkAnimName); + if (anim) { + _currentSprite = anim->getSprite(_dir); + } + } + + if (!_currentSprite) { + return; + } + + _currentSprite->getCurrentFrame(_zoomable ? ((AdGame *)_gameRef)->_scene->getZoomAt(_posX, _posY) : 100, _zoomable ? ((AdGame *)_gameRef)->_scene->getZoomAt(_posX, _posY) : 100); + if (!_currentSprite->isChanged()) { + return; + } + + + int maxStepX, maxStepY; + maxStepX = abs(_currentSprite->_moveX); + maxStepY = abs(_currentSprite->_moveY); + + maxStepX = MAX(maxStepX, maxStepY); + maxStepX = MAX(maxStepX, 1); + + while (_pFCount > 0 && maxStepX >= 0) { + _pFX += _pFStepX; + _pFY += _pFStepY; + + _pFCount--; + maxStepX--; + } + + if (((AdGame *)_gameRef)->_scene->isBlockedAt((int)_pFX, (int) _pFY, true, this)) { + if (_pFCount == 0) { + _state = _nextState; + _nextState = STATE_READY; + return; + } + goTo(_targetPoint->x, _targetPoint->y); + return; + } + + + _posX = (int)_pFX; + _posY = (int)_pFY; + + afterMove(); + + + if (_pFCount == 0) { + if (_path->getNext() == NULL) { + _posX = _targetPoint->x; + _posY = _targetPoint->y; + + _path->reset(); + if (_afterWalkDir != DI_NONE) { + turnTo(_afterWalkDir); + } else { + _state = _nextState; + _nextState = STATE_READY; + } + } else { + initLine(BasePoint(_posX, _posY), *_path->getCurrent()); + } + } +} + + +////////////////////////////////////////////////////////////////////////// +void AdActor::initLine(BasePoint startPt, BasePoint endPt) { + _pFCount = MAX((abs(endPt.x - startPt.x)) , (abs(endPt.y - startPt.y))); + + _pFStepX = (double)(endPt.x - startPt.x) / _pFCount; + _pFStepY = (double)(endPt.y - startPt.y) / _pFCount; + + _pFX = startPt.x; + _pFY = startPt.y; + + int angle = (int)(atan2((double)(endPt.y - startPt.y), (double)(endPt.x - startPt.x)) * (180 / 3.14)); + + _nextState = STATE_FOLLOWING_PATH; + + turnTo(angleToDirection(angle)); +} + + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool AdActor::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // GoTo / GoToAsync + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "GoTo") == 0 || strcmp(name, "GoToAsync") == 0) { + stack->correctParams(2); + int x = stack->pop()->getInt(); + int y = stack->pop()->getInt(); + goTo(x, y); + if (strcmp(name, "GoToAsync") != 0) { + script->waitForExclusive(this); + } + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GoToObject / GoToObjectAsync + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GoToObject") == 0 || strcmp(name, "GoToObjectAsync") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + if (!val->isNative()) { + script->runtimeError("actor.%s method accepts an entity refrence only", name); + stack->pushNULL(); + return STATUS_OK; + } + AdObject *obj = (AdObject *)val->getNative(); + if (!obj || obj->getType() != OBJECT_ENTITY) { + script->runtimeError("actor.%s method accepts an entity refrence only", name); + stack->pushNULL(); + return STATUS_OK; + } + AdEntity *ent = (AdEntity *)obj; + if (ent->getWalkToX() == 0 && ent->getWalkToY() == 0) { + goTo(ent->_posX, ent->_posY); + } else { + goTo(ent->getWalkToX(), ent->getWalkToY(), ent->getWalkToDir()); + } + if (strcmp(name, "GoToObjectAsync") != 0) { + script->waitForExclusive(this); + } + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // TurnTo / TurnToAsync + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "TurnTo") == 0 || strcmp(name, "TurnToAsync") == 0) { + stack->correctParams(1); + int dir; + ScValue *val = stack->pop(); + + // turn to object? + if (val->isNative() && _gameRef->validObject((BaseObject *)val->getNative())) { + BaseObject *obj = (BaseObject *)val->getNative(); + int angle = (int)(atan2((double)(obj->_posY - _posY), (double)(obj->_posX - _posX)) * (180 / 3.14)); + dir = (int)angleToDirection(angle); + } + // otherwise turn to direction + else { + dir = val->getInt(); + } + + if (dir >= 0 && dir < NUM_DIRECTIONS) { + turnTo((TDirection)dir); + if (strcmp(name, "TurnToAsync") != 0) { + script->waitForExclusive(this); + } + } + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // IsWalking + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "IsWalking") == 0) { + stack->correctParams(0); + stack->pushBool(_state == STATE_FOLLOWING_PATH); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // MergeAnims + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "MergeAnims") == 0) { + stack->correctParams(1); + stack->pushBool(DID_SUCCEED(mergeAnims(stack->pop()->getString()))); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // UnloadAnim + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "UnloadAnim") == 0) { + stack->correctParams(1); + const char *animName = stack->pop()->getString(); + + bool found = false; + for (uint32 i = 0; i < _anims.size(); i++) { + if (scumm_stricmp(_anims[i]->getName(), animName) == 0) { + // invalidate sprites in use + if (_anims[i]->containsSprite(_tempSprite2)) { + _tempSprite2 = NULL; + } + if (_anims[i]->containsSprite(_currentSprite)) { + _currentSprite = NULL; + } + if (_anims[i]->containsSprite(_animSprite2)) { + _animSprite2 = NULL; + } + + delete _anims[i]; + _anims[i] = NULL; + _anims.remove_at(i); + i--; + found = true; + } + } + stack->pushBool(found); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // HasAnim + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "HasAnim") == 0) { + stack->correctParams(1); + const char *animName = stack->pop()->getString(); + stack->pushBool(getAnimByName(animName) != NULL); + return STATUS_OK; + } else { + return AdTalkHolder::scCallMethod(script, stack, thisStack, name); + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *AdActor::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Direction + ////////////////////////////////////////////////////////////////////////// + if (name == "Direction") { + _scValue->setInt(_dir); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // Type + ////////////////////////////////////////////////////////////////////////// + else if (name == "Type") { + _scValue->setString("actor"); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // TalkAnimName + ////////////////////////////////////////////////////////////////////////// + else if (name == "TalkAnimName") { + _scValue->setString(_talkAnimName); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // WalkAnimName + ////////////////////////////////////////////////////////////////////////// + else if (name == "WalkAnimName") { + _scValue->setString(_walkAnimName); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // IdleAnimName + ////////////////////////////////////////////////////////////////////////// + else if (name == "IdleAnimName") { + _scValue->setString(_idleAnimName); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // TurnLeftAnimName + ////////////////////////////////////////////////////////////////////////// + else if (name == "TurnLeftAnimName") { + _scValue->setString(_turnLeftAnimName); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // TurnRightAnimName + ////////////////////////////////////////////////////////////////////////// + else if (name == "TurnRightAnimName") { + _scValue->setString(_turnRightAnimName); + return _scValue; + } else { + return AdTalkHolder::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool AdActor::scSetProperty(const char *name, ScValue *value) { + ////////////////////////////////////////////////////////////////////////// + // Direction + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Direction") == 0) { + int dir = value->getInt(); + if (dir >= 0 && dir < NUM_DIRECTIONS) { + _dir = (TDirection)dir; + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // TalkAnimName + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "TalkAnimName") == 0) { + if (value->isNULL()) { + _talkAnimName = "talk"; + } else { + _talkAnimName = value->getString(); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // WalkAnimName + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "WalkAnimName") == 0) { + if (value->isNULL()) { + _walkAnimName = "walk"; + } else { + _walkAnimName = value->getString(); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // IdleAnimName + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "IdleAnimName") == 0) { + if (value->isNULL()) { + _idleAnimName = "idle"; + } else { + _idleAnimName = value->getString(); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // TurnLeftAnimName + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "TurnLeftAnimName") == 0) { + if (value->isNULL()) { + _turnLeftAnimName = "turnleft"; + } else { + _turnLeftAnimName = value->getString(); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // TurnRightAnimName + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "TurnRightAnimName") == 0) { + if (value->isNULL()) { + _turnRightAnimName = "turnright"; + } else { + _turnRightAnimName = value->getString(); + } + return STATUS_OK; + } else { + return AdTalkHolder::scSetProperty(name, value); + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *AdActor::scToString() { + return "[actor object]"; +} + + +////////////////////////////////////////////////////////////////////////// +BaseSprite *AdActor::getTalkStance(const char *stance) { + // forced stance? + if (_forcedTalkAnimName && !_forcedTalkAnimUsed) { + _forcedTalkAnimUsed = true; + delete _animSprite; + _animSprite = new BaseSprite(_gameRef, this); + if (_animSprite) { + bool res = _animSprite->loadFile(_forcedTalkAnimName); + if (DID_FAIL(res)) { + _gameRef->LOG(res, "AdActor::GetTalkStance: error loading talk sprite (object:\"%s\" sprite:\"%s\")", getName(), _forcedTalkAnimName); + delete _animSprite; + _animSprite = NULL; + } else { + return _animSprite; + } + } + } + + // old way + if (_talkSprites.size() > 0 || _talkSpritesEx.size() > 0) { + return getTalkStanceOld(stance); + } + + // new way + BaseSprite *ret = NULL; + + // do we have an animation with this name? + AdSpriteSet *anim = getAnimByName(stance); + if (anim) { + ret = anim->getSprite(_dir); + } + + // not - get a random talk + if (!ret) { + BaseArray<AdSpriteSet *> talkAnims; + for (uint32 i = 0; i < _anims.size(); i++) { + if (_talkAnimName.compareToIgnoreCase(_anims[i]->getName()) == 0) { + talkAnims.add(_anims[i]); + } + } + + if (talkAnims.size() > 0) { + int rnd = BaseEngine::instance().randInt(0, talkAnims.size() - 1); + ret = talkAnims[rnd]->getSprite(_dir); + } else { + if (_standSprite) { + ret = _standSprite->getSprite(_dir); + } else { + anim = getAnimByName(_idleAnimName); + if (anim) { + ret = anim->getSprite(_dir); + } + } + } + } + return ret; +} + +////////////////////////////////////////////////////////////////////////// +BaseSprite *AdActor::getTalkStanceOld(const char *stance) { + BaseSprite *ret = NULL; + + if (stance != NULL) { + // search special stances + for (uint32 i = 0; i < _talkSpritesEx.size(); i++) { + if (scumm_stricmp(_talkSpritesEx[i]->getName(), stance) == 0) { + ret = _talkSpritesEx[i]->getSprite(_dir); + break; + } + } + if (ret == NULL) { + // search generic stances + for (uint32 i = 0; i < _talkSprites.size(); i++) { + if (scumm_stricmp(_talkSprites[i]->getName(), stance) == 0) { + ret = _talkSprites[i]->getSprite(_dir); + break; + } + } + } + } + + // not a valid stance? get a random one + if (ret == NULL) { + if (_talkSprites.size() < 1) { + ret = _standSprite->getSprite(_dir); + } else { + // TODO: remember last + int rnd = BaseEngine::instance().randInt(0, _talkSprites.size() - 1); + ret = _talkSprites[rnd]->getSprite(_dir); + } + } + + return ret; +} + +////////////////////////////////////////////////////////////////////////// +bool AdActor::persist(BasePersistenceManager *persistMgr) { + AdTalkHolder::persist(persistMgr); + + persistMgr->transfer(TMEMBER_INT(_dir)); + persistMgr->transfer(TMEMBER(_path)); + persistMgr->transfer(TMEMBER(_pFCount)); + persistMgr->transfer(TMEMBER(_pFStepX)); + persistMgr->transfer(TMEMBER(_pFStepY)); + persistMgr->transfer(TMEMBER(_pFX)); + persistMgr->transfer(TMEMBER(_pFY)); + persistMgr->transfer(TMEMBER(_standSprite)); + _talkSprites.persist(persistMgr); + _talkSpritesEx.persist(persistMgr); + persistMgr->transfer(TMEMBER_INT(_targetDir)); + persistMgr->transfer(TMEMBER_INT(_afterWalkDir)); + persistMgr->transfer(TMEMBER(_targetPoint)); + persistMgr->transfer(TMEMBER(_turnLeftSprite)); + persistMgr->transfer(TMEMBER(_turnRightSprite)); + persistMgr->transfer(TMEMBER(_walkSprite)); + + persistMgr->transfer(TMEMBER(_animSprite2)); + persistMgr->transfer(TMEMBER(_talkAnimName)); + persistMgr->transfer(TMEMBER(_idleAnimName)); + persistMgr->transfer(TMEMBER(_walkAnimName)); + persistMgr->transfer(TMEMBER(_turnLeftAnimName)); + persistMgr->transfer(TMEMBER(_turnRightAnimName)); + + _anims.persist(persistMgr); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +TDirection AdActor::angleToDirection(int angle) { + TDirection ret = DI_DOWN; + + if (angle > -112 && angle <= -67) { + ret = DI_UP; + } else if (angle > -67 && angle <= -22) { + ret = DI_UPRIGHT; + } else if (angle > -22 && angle <= 22) { + ret = DI_RIGHT; + } else if (angle > 22 && angle <= 67) { + ret = DI_DOWNRIGHT; + } else if (angle > 67 && angle <= 112) { + ret = DI_DOWN; + } else if (angle > 112 && angle <= 157) { + ret = DI_DOWNLEFT; + } else if ((angle > 157 && angle <= 180) || (angle >= -180 && angle <= -157)) { + ret = DI_LEFT; + } else if (angle > -157 && angle <= -112) { + ret = DI_UPLEFT; + } + + return ret; +} + + +////////////////////////////////////////////////////////////////////////// +int AdActor::getHeight() { + // if no current sprite is set, set some + if (_currentSprite == NULL) { + if (_standSprite) { + _currentSprite = _standSprite->getSprite(_dir); + } else { + AdSpriteSet *anim = getAnimByName(_idleAnimName); + if (anim) { + _currentSprite = anim->getSprite(_dir); + } + } + } + // and get height + return AdTalkHolder::getHeight(); +} + + +////////////////////////////////////////////////////////////////////////// +AdSpriteSet *AdActor::getAnimByName(const Common::String &animName) { + for (uint32 i = 0; i < _anims.size(); i++) { + if (animName.compareToIgnoreCase(_anims[i]->getName()) == 0) { + return _anims[i]; + } + } + return NULL; +} + +////////////////////////////////////////////////////////////////////////// +bool AdActor::mergeAnims(const char *animsFilename) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(ANIMATION) + TOKEN_TABLE_END + + + byte *fileBuffer = BaseFileManager::getEngineInstance()->readWholeFile(animsFilename); + if (fileBuffer == NULL) { + _gameRef->LOG(0, "AdActor::MergeAnims failed for file '%s'", animsFilename); + return STATUS_FAILED; + } + + byte *buffer = fileBuffer; + byte *params; + int cmd; + BaseParser parser; + + bool ret = STATUS_OK; + + while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_ANIMATION: { + AdSpriteSet *anim = new AdSpriteSet(_gameRef, this); + if (!anim || DID_FAIL(anim->loadBuffer(params, false))) { + cmd = PARSERR_GENERIC; + ret = STATUS_FAILED; + } else { + _anims.add(anim); + } + } + break; + } + } + delete[] fileBuffer; + return ret; +} + +////////////////////////////////////////////////////////////////////////// +bool AdActor::playAnim(const char *filename) { + // if we have an anim with this name, use it + AdSpriteSet *anim = getAnimByName(filename); + if (anim) { + _animSprite2 = anim->getSprite(_dir); + if (_animSprite2) { + _animSprite2->reset(); + _state = STATE_PLAYING_ANIM_SET; + return STATUS_OK; + } + } + // otherwise call the standard handler + return AdTalkHolder::playAnim(filename); +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_actor.h b/engines/wintermute/ad/ad_actor.h new file mode 100644 index 0000000000..543c9d063a --- /dev/null +++ b/engines/wintermute/ad/ad_actor.h @@ -0,0 +1,108 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADACTOR_H +#define WINTERMUTE_ADACTOR_H + + +#include "engines/wintermute/dctypes.h" // Added by ClassView +#include "engines/wintermute/ad/ad_types.h" // Added by ClassView +#include "engines/wintermute/ad/ad_talk_holder.h" +#include "engines/wintermute/coll_templ.h" +#include "engines/wintermute/base/base_point.h" // Added by ClassView +#include "engines/wintermute/persistent.h" +#include "common/str.h" + +namespace Wintermute { + +class AdSpriteSet; +class AdPath; +class BaseSprite; +class AdActor : public AdTalkHolder { +public: + TDirection angleToDirection(int angle); + DECLARE_PERSISTENT(AdActor, AdTalkHolder) + virtual int getHeight(); + BaseSprite *getTalkStance(const char *stance); + virtual void goTo(int x, int y, TDirection afterWalkDir = DI_NONE); + BasePoint *_targetPoint; + virtual bool update(); + virtual bool display(); + virtual void turnTo(TDirection dir); + AdActor(BaseGame *inGame/*=NULL*/); + virtual ~AdActor(); + bool loadFile(const char *filename); + bool loadBuffer(byte *buffer, bool complete = true); + + +private: + TDirection _targetDir; + TDirection _afterWalkDir; + + AdPath *_path; + AdSpriteSet *_walkSprite; + AdSpriteSet *_standSprite; + AdSpriteSet *_turnLeftSprite; + AdSpriteSet *_turnRightSprite; + BaseArray<AdSpriteSet *> _talkSprites; + BaseArray<AdSpriteSet *> _talkSpritesEx; + TDirection _dir; + // new anim system + Common::String _talkAnimName; + Common::String _idleAnimName; + Common::String _walkAnimName; + Common::String _turnLeftAnimName; + Common::String _turnRightAnimName; + BaseArray<AdSpriteSet *> _anims; + virtual bool playAnim(const char *filename); + AdSpriteSet *getAnimByName(const Common::String &animName); + + // scripting interface + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + virtual const char *scToString(); + + bool setDefaultAnimNames(); + BaseSprite *getTalkStanceOld(const char *stance); + bool mergeAnims(const char *animsFilename); + BaseSprite *_animSprite2; + + void initLine(BasePoint startPt, BasePoint endPt); + void getNextStep(); + void followPath(); + double _pFStepX; + double _pFStepY; + double _pFX; + double _pFY; + int _pFCount; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_entity.cpp b/engines/wintermute/ad/ad_entity.cpp new file mode 100644 index 0000000000..2c331de964 --- /dev/null +++ b/engines/wintermute/ad/ad_entity.cpp @@ -0,0 +1,1137 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + + +#include "engines/wintermute/ad/ad_entity.h" +#include "engines/wintermute/ad/ad_game.h" +#include "engines/wintermute/ad/ad_scene.h" +#include "engines/wintermute/ad/ad_waypoint_group.h" +#include "engines/wintermute/ad/ad_sentence.h" +#include "engines/wintermute/base/base_active_rect.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_region.h" +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/base/base_surface_storage.h" +#include "engines/wintermute/base/font/base_font_storage.h" +#include "engines/wintermute/base/font/base_font.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/base/particles/part_emitter.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/base/sound/base_sound.h" +#include "engines/wintermute/video/video_theora_player.h" +#include "engines/wintermute/utils/utils.h" +#include "engines/wintermute/platform_osystem.h" +#include "common/str.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdEntity, false) + +////////////////////////////////////////////////////////////////////////// +AdEntity::AdEntity(BaseGame *inGame) : AdTalkHolder(inGame) { + _type = OBJECT_ENTITY; + _subtype = ENTITY_NORMAL; + _region = NULL; + _item = NULL; + + _walkToX = _walkToY = 0; + _walkToDir = DI_NONE; + + _theora = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +AdEntity::~AdEntity() { + _gameRef->unregisterObject(_region); + + delete _theora; + _theora = NULL; + + delete[] _item; + _item = NULL; +} + +int32 AdEntity::getWalkToX() const { + return _walkToX; +} + +int32 AdEntity::getWalkToY() const { + return _walkToY; +} + +TDirection AdEntity::getWalkToDir() const { + return _walkToDir; +} + +const char *AdEntity::getItemName() const { + return _item; +} + +////////////////////////////////////////////////////////////////////////// +bool AdEntity::loadFile(const char *filename) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "AdEntity::LoadFile failed for file '%s'", filename); + return STATUS_FAILED; + } + + bool ret; + + setFilename(filename); + + if (DID_FAIL(ret = loadBuffer(buffer, true))) { + _gameRef->LOG(0, "Error parsing ENTITY file '%s'", filename); + } + + + delete[] buffer; + + return ret; +} + + +TOKEN_DEF_START +TOKEN_DEF(ENTITY) +TOKEN_DEF(SPRITE) +TOKEN_DEF(X) +TOKEN_DEF(Y) +TOKEN_DEF(TEMPLATE) +TOKEN_DEF(NAME) +TOKEN_DEF(SCALABLE) +TOKEN_DEF(REGISTRABLE) +TOKEN_DEF(INTERACTIVE) +TOKEN_DEF(SHADOWABLE) +TOKEN_DEF(COLORABLE) +TOKEN_DEF(ACTIVE) +TOKEN_DEF(EVENTS) +TOKEN_DEF(FONT) +TOKEN_DEF(TALK_SPECIAL) +TOKEN_DEF(TALK) +TOKEN_DEF(CURSOR) +TOKEN_DEF(REGION) +TOKEN_DEF(BLOCKED_REGION) +TOKEN_DEF(EDITOR_SELECTED) +TOKEN_DEF(SCRIPT) +TOKEN_DEF(SOUND_START_TIME) +TOKEN_DEF(SOUND_VOLUME) +TOKEN_DEF(SOUND_PANNING) +TOKEN_DEF(SOUND) +TOKEN_DEF(SUBTYPE) +TOKEN_DEF(CAPTION) +TOKEN_DEF(PROPERTY) +TOKEN_DEF(WAYPOINTS) +TOKEN_DEF(IGNORE_ITEMS) +TOKEN_DEF(ROTABLE) +TOKEN_DEF(ROTATABLE) +TOKEN_DEF(ALPHA_COLOR) +TOKEN_DEF(SCALE) +TOKEN_DEF(RELATIVE_SCALE) +TOKEN_DEF(ALPHA) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF(ITEM) +TOKEN_DEF(WALK_TO_X) +TOKEN_DEF(WALK_TO_Y) +TOKEN_DEF(WALK_TO_DIR) +TOKEN_DEF(SAVE_STATE) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool AdEntity::loadBuffer(byte *buffer, bool complete) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(ENTITY) + TOKEN_TABLE(SPRITE) + TOKEN_TABLE(X) + TOKEN_TABLE(Y) + TOKEN_TABLE(TEMPLATE) + TOKEN_TABLE(NAME) + TOKEN_TABLE(SCALABLE) + TOKEN_TABLE(REGISTRABLE) + TOKEN_TABLE(INTERACTIVE) + TOKEN_TABLE(SHADOWABLE) + TOKEN_TABLE(COLORABLE) + TOKEN_TABLE(ACTIVE) + TOKEN_TABLE(EVENTS) + TOKEN_TABLE(FONT) + TOKEN_TABLE(TALK_SPECIAL) + TOKEN_TABLE(TALK) + TOKEN_TABLE(CURSOR) + TOKEN_TABLE(REGION) + TOKEN_TABLE(BLOCKED_REGION) + TOKEN_TABLE(EDITOR_SELECTED) + TOKEN_TABLE(SCRIPT) + TOKEN_TABLE(SOUND_START_TIME) + TOKEN_TABLE(SOUND_VOLUME) + TOKEN_TABLE(SOUND_PANNING) + TOKEN_TABLE(SOUND) + TOKEN_TABLE(SUBTYPE) + TOKEN_TABLE(CAPTION) + TOKEN_TABLE(PROPERTY) + TOKEN_TABLE(WAYPOINTS) + TOKEN_TABLE(IGNORE_ITEMS) + TOKEN_TABLE(ROTABLE) + TOKEN_TABLE(ROTATABLE) + TOKEN_TABLE(ALPHA_COLOR) + TOKEN_TABLE(SCALE) + TOKEN_TABLE(RELATIVE_SCALE) + TOKEN_TABLE(ALPHA) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE(ITEM) + TOKEN_TABLE(WALK_TO_X) + TOKEN_TABLE(WALK_TO_Y) + TOKEN_TABLE(WALK_TO_DIR) + TOKEN_TABLE(SAVE_STATE) + TOKEN_TABLE_END + + byte *params; + int cmd; + BaseParser parser; + + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_ENTITY) { + _gameRef->LOG(0, "'ENTITY' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + AdGame *adGame = (AdGame *)_gameRef; + BaseSprite *spr = NULL; + int ar = 0, ag = 0, ab = 0, alpha = 0; + while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_TEMPLATE: + if (DID_FAIL(loadFile((char *)params))) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_X: + parser.scanStr((char *)params, "%d", &_posX); + break; + + case TOKEN_Y: + parser.scanStr((char *)params, "%d", &_posY); + break; + + case TOKEN_SPRITE: { + delete _sprite; + _sprite = NULL; + spr = new BaseSprite(_gameRef, this); + if (!spr || DID_FAIL(spr->loadFile((char *)params))) { + cmd = PARSERR_GENERIC; + } else { + _sprite = spr; + } + } + break; + + case TOKEN_TALK: { + spr = new BaseSprite(_gameRef, this); + if (!spr || DID_FAIL(spr->loadFile((char *)params, adGame->_texTalkLifeTime))) { + cmd = PARSERR_GENERIC; + } else { + _talkSprites.add(spr); + } + } + break; + + case TOKEN_TALK_SPECIAL: { + spr = new BaseSprite(_gameRef, this); + if (!spr || DID_FAIL(spr->loadFile((char *)params, adGame->_texTalkLifeTime))) { + cmd = PARSERR_GENERIC; + } else { + _talkSpritesEx.add(spr); + } + } + break; + + case TOKEN_NAME: + setName((char *)params); + break; + + case TOKEN_ITEM: + setItem((char *)params); + break; + + case TOKEN_CAPTION: + setCaption((char *)params); + break; + + case TOKEN_FONT: + setFont((char *)params); + break; + + case TOKEN_SCALABLE: + parser.scanStr((char *)params, "%b", &_zoomable); + break; + + case TOKEN_SCALE: { + int s; + parser.scanStr((char *)params, "%d", &s); + _scale = (float)s; + + } + break; + + case TOKEN_RELATIVE_SCALE: { + int s; + parser.scanStr((char *)params, "%d", &s); + _relativeScale = (float)s; + + } + break; + + case TOKEN_ROTABLE: + case TOKEN_ROTATABLE: + parser.scanStr((char *)params, "%b", &_rotatable); + break; + + case TOKEN_REGISTRABLE: + case TOKEN_INTERACTIVE: + parser.scanStr((char *)params, "%b", &_registrable); + break; + + case TOKEN_SHADOWABLE: + case TOKEN_COLORABLE: + parser.scanStr((char *)params, "%b", &_shadowable); + break; + + case TOKEN_ACTIVE: + parser.scanStr((char *)params, "%b", &_active); + break; + + case TOKEN_CURSOR: + delete _cursor; + _cursor = new BaseSprite(_gameRef); + if (!_cursor || DID_FAIL(_cursor->loadFile((char *)params))) { + delete _cursor; + _cursor = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_EDITOR_SELECTED: + parser.scanStr((char *)params, "%b", &_editorSelected); + break; + + case TOKEN_REGION: { + if (_region) { + _gameRef->unregisterObject(_region); + } + _region = NULL; + BaseRegion *rgn = new BaseRegion(_gameRef); + if (!rgn || DID_FAIL(rgn->loadBuffer(params, false))) { + cmd = PARSERR_GENERIC; + } else { + _region = rgn; + _gameRef->registerObject(_region); + } + } + break; + + case TOKEN_BLOCKED_REGION: { + delete _blockRegion; + _blockRegion = NULL; + delete _currentBlockRegion; + _currentBlockRegion = NULL; + BaseRegion *rgn = new BaseRegion(_gameRef); + BaseRegion *crgn = new BaseRegion(_gameRef); + if (!rgn || !crgn || DID_FAIL(rgn->loadBuffer(params, false))) { + delete _blockRegion; + _blockRegion = NULL; + delete _currentBlockRegion; + _currentBlockRegion = NULL; + cmd = PARSERR_GENERIC; + } else { + _blockRegion = rgn; + _currentBlockRegion = crgn; + _currentBlockRegion->mimic(_blockRegion); + } + } + break; + + case TOKEN_WAYPOINTS: { + delete _wptGroup; + _wptGroup = NULL; + delete _currentWptGroup; + _currentWptGroup = NULL; + AdWaypointGroup *wpt = new AdWaypointGroup(_gameRef); + AdWaypointGroup *cwpt = new AdWaypointGroup(_gameRef); + if (!wpt || !cwpt || DID_FAIL(wpt->loadBuffer(params, false))) { + delete _wptGroup; + _wptGroup = NULL; + delete _currentWptGroup; + _currentWptGroup = NULL; + cmd = PARSERR_GENERIC; + } else { + _wptGroup = wpt; + _currentWptGroup = cwpt; + _currentWptGroup->mimic(_wptGroup); + } + } + break; + + case TOKEN_SCRIPT: + addScript((char *)params); + break; + + case TOKEN_SUBTYPE: { + if (scumm_stricmp((char *)params, "sound") == 0) { + delete _sprite; + _sprite = NULL; + if (_gameRef->_editorMode) { + spr = new BaseSprite(_gameRef, this); + if (!spr || DID_FAIL(spr->loadFile("entity_sound.sprite"))) { + cmd = PARSERR_GENERIC; + } else { + _sprite = spr; + } + } + if (_gameRef->_editorMode) { + _editorOnly = true; + } + _zoomable = false; + _rotatable = false; + _registrable = _gameRef->_editorMode; + _shadowable = false; + _subtype = ENTITY_SOUND; + } + } + break; + + case TOKEN_SOUND: + playSFX((char *)params, false, false); + break; + + case TOKEN_SOUND_START_TIME: + parser.scanStr((char *)params, "%d", &_sFXStart); + break; + + case TOKEN_SOUND_VOLUME: + parser.scanStr((char *)params, "%d", &_sFXVolume); + break; + + case TOKEN_SOUND_PANNING: + parser.scanStr((char *)params, "%b", &_autoSoundPanning); + break; + + case TOKEN_SAVE_STATE: + parser.scanStr((char *)params, "%b", &_saveState); + break; + + case TOKEN_PROPERTY: + parseProperty(params, false); + break; + + case TOKEN_IGNORE_ITEMS: + parser.scanStr((char *)params, "%b", &_ignoreItems); + break; + + case TOKEN_ALPHA_COLOR: + parser.scanStr((char *)params, "%d,%d,%d", &ar, &ag, &ab); + break; + + case TOKEN_ALPHA: + parser.scanStr((char *)params, "%d", &alpha); + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty(params, false); + break; + + case TOKEN_WALK_TO_X: + parser.scanStr((char *)params, "%d", &_walkToX); + break; + + case TOKEN_WALK_TO_Y: + parser.scanStr((char *)params, "%d", &_walkToY); + break; + + case TOKEN_WALK_TO_DIR: { + int i; + parser.scanStr((char *)params, "%d", &i); + if (i < 0) { + i = 0; + } + if (i >= NUM_DIRECTIONS) { + i = DI_NONE; + } + _walkToDir = (TDirection)i; + } + break; + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in ENTITY definition"); + return STATUS_FAILED; + } + if (cmd == PARSERR_GENERIC) { + _gameRef->LOG(0, "Error loading ENTITY definition"); + if (spr) { + delete spr; + } + return STATUS_FAILED; + } + + if (_region && _sprite) { + _gameRef->LOG(0, "Warning: Entity '%s' has both sprite and region.", getName()); + } + + updatePosition(); + + if (alpha != 0 && ar == 0 && ag == 0 && ab == 0) { + ar = ag = ab = 255; + } + _alphaColor = BYTETORGBA(ar, ag, ab, alpha); + _state = STATE_READY; + + if (_item && ((AdGame *)_gameRef)->isItemTaken(_item)) { + _active = false; + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdEntity::display() { + if (_active) { + updateSounds(); + + uint32 alpha; + if (_alphaColor != 0) { + alpha = _alphaColor; + } else { + alpha = _shadowable ? ((AdGame *)_gameRef)->_scene->getAlphaAt(_posX, _posY) : 0xFFFFFFFF; + } + + float scaleX, scaleY; + getScale(&scaleX, &scaleY); + + float rotate; + if (_rotatable) { + if (_rotateValid) { + rotate = _rotate; + } else { + rotate = ((AdGame *)_gameRef)->_scene->getRotationAt(_posX, _posY) + _relativeRotate; + } + } else { + rotate = 0.0f; + } + + + bool reg = _registrable; + if (_ignoreItems && ((AdGame *)_gameRef)->_selectedItem) { + reg = false; + } + + if (_region && (reg || _editorAlwaysRegister)) { + _gameRef->_renderer->addRectToList(new BaseActiveRect(_gameRef, _registerAlias, _region, _gameRef->_offsetX, _gameRef->_offsetY)); + } + + displaySpriteAttachments(true); + if (_theora && (_theora->isPlaying() || _theora->isPaused())) { + _theora->display(alpha); + } else if (_currentSprite) { + _currentSprite->display(_posX, + _posY, + (reg || _editorAlwaysRegister) ? _registerAlias : NULL, + scaleX, + scaleY, + alpha, + rotate, + _blendMode); + } + displaySpriteAttachments(false); + + if (_partEmitter) { + _partEmitter->display(_region); + } + + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdEntity::update() { + _currentSprite = NULL; + + if (_state == STATE_READY && _animSprite) { + delete _animSprite; + _animSprite = NULL; + } + + // finished playing animation? + if (_state == STATE_PLAYING_ANIM && _animSprite != NULL && _animSprite->isFinished()) { + _state = STATE_READY; + _currentSprite = _animSprite; + } + + if (_sentence && _state != STATE_TALKING) { + _sentence->finish(); + } + + // default: stand animation + if (!_currentSprite) { + _currentSprite = _sprite; + } + + switch (_state) { + ////////////////////////////////////////////////////////////////////////// + case STATE_PLAYING_ANIM: + _currentSprite = _animSprite; + break; + + ////////////////////////////////////////////////////////////////////////// + case STATE_READY: + if (!_animSprite) { + _currentSprite = _sprite; + } + break; + + ////////////////////////////////////////////////////////////////////////// + case STATE_TALKING: { + _sentence->update(); + if (_sentence->_currentSprite) { + _tempSprite2 = _sentence->_currentSprite; + } + + bool timeIsUp = (_sentence->_sound && _sentence->_soundStarted && (!_sentence->_sound->isPlaying() && !_sentence->_sound->isPaused())) || (!_sentence->_sound && _sentence->_duration <= _gameRef->_timer - _sentence->_startTime); + if (_tempSprite2 == NULL || _tempSprite2->isFinished() || (/*_tempSprite2->_looping &&*/ timeIsUp)) { + if (timeIsUp) { + _sentence->finish(); + _tempSprite2 = NULL; + _state = STATE_READY; + } else { + _tempSprite2 = getTalkStance(_sentence->getNextStance()); + if (_tempSprite2) { + _tempSprite2->reset(); + _currentSprite = _tempSprite2; + } + ((AdGame *)_gameRef)->addSentence(_sentence); + } + } else { + _currentSprite = _tempSprite2; + ((AdGame *)_gameRef)->addSentence(_sentence); + } + } + break; + default: // Silence unhandled enum-warning + break; + } + + + if (_currentSprite) { + _currentSprite->getCurrentFrame(_zoomable ? ((AdGame *)_gameRef)->_scene->getZoomAt(_posX, _posY) : 100); + if (_currentSprite->isChanged()) { + _posX += _currentSprite->_moveX; + _posY += _currentSprite->_moveY; + } + } + + updateBlockRegion(); + _ready = (_state == STATE_READY); + + if (_theora) { + int offsetX, offsetY; + _gameRef->getOffset(&offsetX, &offsetY); + _theora->_posX = _posX - offsetX; + _theora->_posY = _posY - offsetY; + + _theora->update(); + if (_theora->isFinished()) { + _theora->stop(); + delete _theora; + _theora = NULL; + } + } + + updatePartEmitter(); + updateSpriteAttachments(); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool AdEntity::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // StopSound + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "StopSound") == 0 && _subtype == ENTITY_SOUND) { + stack->correctParams(0); + + if (DID_FAIL(stopSFX(false))) { + stack->pushBool(false); + } else { + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // PlayTheora + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "PlayTheora") == 0) { + stack->correctParams(4); + const char *filename = stack->pop()->getString(); + bool looping = stack->pop()->getBool(false); + ScValue *valAlpha = stack->pop(); + int startTime = stack->pop()->getInt(); + + delete _theora; + _theora = new VideoTheoraPlayer(_gameRef); + if (_theora && DID_SUCCEED(_theora->initialize(filename))) { + if (!valAlpha->isNULL()) { + _theora->setAlphaImage(valAlpha->getString()); + } + _theora->play(VID_PLAY_POS, 0, 0, false, false, looping, startTime, _scale >= 0.0f ? _scale : -1.0f, _sFXVolume); + //if (_scale>=0) _theora->_playZoom = _scale; + stack->pushBool(true); + } else { + script->runtimeError("Entity.PlayTheora - error playing video '%s'", filename); + stack->pushBool(false); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // StopTheora + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "StopTheora") == 0) { + stack->correctParams(0); + if (_theora) { + _theora->stop(); + delete _theora; + _theora = NULL; + stack->pushBool(true); + } else { + stack->pushBool(false); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // IsTheoraPlaying + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "IsTheoraPlaying") == 0) { + stack->correctParams(0); + if (_theora && _theora->isPlaying()) { + stack->pushBool(true); + } else { + stack->pushBool(false); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // PauseTheora + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "PauseTheora") == 0) { + stack->correctParams(0); + if (_theora && _theora->isPlaying()) { + _theora->pause(); + stack->pushBool(true); + } else { + stack->pushBool(false); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ResumeTheora + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ResumeTheora") == 0) { + stack->correctParams(0); + if (_theora && _theora->isPaused()) { + _theora->resume(); + stack->pushBool(true); + } else { + stack->pushBool(false); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // IsTheoraPaused + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "IsTheoraPaused") == 0) { + stack->correctParams(0); + if (_theora && _theora->isPaused()) { + stack->pushBool(true); + } else { + stack->pushBool(false); + } + + return STATUS_OK; + } + + + ////////////////////////////////////////////////////////////////////////// + // CreateRegion + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "CreateRegion") == 0) { + stack->correctParams(0); + if (!_region) { + _region = new BaseRegion(_gameRef); + _gameRef->registerObject(_region); + } + if (_region) { + stack->pushNative(_region, true); + } else { + stack->pushNULL(); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // DeleteRegion + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "DeleteRegion") == 0) { + stack->correctParams(0); + if (_region) { + _gameRef->unregisterObject(_region); + _region = NULL; + stack->pushBool(true); + } else { + stack->pushBool(false); + } + + return STATUS_OK; + } else { + return AdTalkHolder::scCallMethod(script, stack, thisStack, name); + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *AdEntity::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type (RO) + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("entity"); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Item + ////////////////////////////////////////////////////////////////////////// + else if (name == "Item") { + if (_item) { + _scValue->setString(_item); + } else { + _scValue->setNULL(); + } + + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Subtype (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "Subtype") { + if (_subtype == ENTITY_SOUND) { + _scValue->setString("sound"); + } else { + _scValue->setString("normal"); + } + + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // WalkToX + ////////////////////////////////////////////////////////////////////////// + else if (name == "WalkToX") { + _scValue->setInt(_walkToX); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // WalkToY + ////////////////////////////////////////////////////////////////////////// + else if (name == "WalkToY") { + _scValue->setInt(_walkToY); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // WalkToDirection + ////////////////////////////////////////////////////////////////////////// + else if (name == "WalkToDirection") { + _scValue->setInt((int)_walkToDir); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Region (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "Region") { + if (_region) { + _scValue->setNative(_region, true); + } else { + _scValue->setNULL(); + } + return _scValue; + } else { + return AdTalkHolder::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool AdEntity::scSetProperty(const char *name, ScValue *value) { + + ////////////////////////////////////////////////////////////////////////// + // Item + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Item") == 0) { + setItem(value->getString()); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // WalkToX + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "WalkToX") == 0) { + _walkToX = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // WalkToY + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "WalkToY") == 0) { + _walkToY = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // WalkToDirection + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "WalkToDirection") == 0) { + int dir = value->getInt(); + if (dir >= 0 && dir < NUM_DIRECTIONS) { + _walkToDir = (TDirection)dir; + } + return STATUS_OK; + } else { + return AdTalkHolder::scSetProperty(name, value); + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *AdEntity::scToString() { + return "[entity object]"; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdEntity::saveAsText(BaseDynamicBuffer *buffer, int indent) { + buffer->putTextIndent(indent, "ENTITY {\n"); + buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", getName()); + if (_subtype == ENTITY_SOUND) { + buffer->putTextIndent(indent + 2, "SUBTYPE=\"SOUND\"\n"); + } + buffer->putTextIndent(indent + 2, "CAPTION=\"%s\"\n", getCaption()); + buffer->putTextIndent(indent + 2, "ACTIVE=%s\n", _active ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "X=%d\n", _posX); + buffer->putTextIndent(indent + 2, "Y=%d\n", _posY); + buffer->putTextIndent(indent + 2, "SCALABLE=%s\n", _zoomable ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "INTERACTIVE=%s\n", _registrable ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "COLORABLE=%s\n", _shadowable ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "EDITOR_SELECTED=%s\n", _editorSelected ? "TRUE" : "FALSE"); + if (_ignoreItems) { + buffer->putTextIndent(indent + 2, "IGNORE_ITEMS=%s\n", _ignoreItems ? "TRUE" : "FALSE"); + } + if (_rotatable) { + buffer->putTextIndent(indent + 2, "ROTATABLE=%s\n", _rotatable ? "TRUE" : "FALSE"); + } + + if (!_autoSoundPanning) { + buffer->putTextIndent(indent + 2, "SOUND_PANNING=%s\n", _autoSoundPanning ? "TRUE" : "FALSE"); + } + + if (!_saveState) { + buffer->putTextIndent(indent + 2, "SAVE_STATE=%s\n", _saveState ? "TRUE" : "FALSE"); + } + + if (_item && _item[0] != '\0') { + buffer->putTextIndent(indent + 2, "ITEM=\"%s\"\n", _item); + } + + buffer->putTextIndent(indent + 2, "WALK_TO_X=%d\n", _walkToX); + buffer->putTextIndent(indent + 2, "WALK_TO_Y=%d\n", _walkToY); + if (_walkToDir != DI_NONE) { + buffer->putTextIndent(indent + 2, "WALK_TO_DIR=%d\n", (int)_walkToDir); + } + + for (uint32 i = 0; i < _scripts.size(); i++) { + buffer->putTextIndent(indent + 2, "SCRIPT=\"%s\"\n", _scripts[i]->_filename); + } + + if (_subtype == ENTITY_NORMAL && _sprite && _sprite->getFilename()) { + buffer->putTextIndent(indent + 2, "SPRITE=\"%s\"\n", _sprite->getFilename()); + } + + if (_subtype == ENTITY_SOUND && _sFX && _sFX->getFilename()) { + buffer->putTextIndent(indent + 2, "SOUND=\"%s\"\n", _sFX->getFilename()); + buffer->putTextIndent(indent + 2, "SOUND_START_TIME=%d\n", _sFXStart); + buffer->putTextIndent(indent + 2, "SOUND_VOLUME=%d\n", _sFXVolume); + } + + + if (RGBCOLGetR(_alphaColor) != 0 || RGBCOLGetG(_alphaColor) != 0 || RGBCOLGetB(_alphaColor) != 0) { + buffer->putTextIndent(indent + 2, "ALPHA_COLOR { %d,%d,%d }\n", RGBCOLGetR(_alphaColor), RGBCOLGetG(_alphaColor), RGBCOLGetB(_alphaColor)); + } + + if (RGBCOLGetA(_alphaColor) != 0) { + buffer->putTextIndent(indent + 2, "ALPHA = %d\n", RGBCOLGetA(_alphaColor)); + } + + if (_scale >= 0) { + buffer->putTextIndent(indent + 2, "SCALE = %d\n", (int)_scale); + } + + if (_relativeScale != 0) { + buffer->putTextIndent(indent + 2, "RELATIVE_SCALE = %d\n", (int)_relativeScale); + } + + if (_font && _font->getFilename()) { + buffer->putTextIndent(indent + 2, "FONT=\"%s\"\n", _font->getFilename()); + } + + if (_cursor && _cursor->getFilename()) { + buffer->putTextIndent(indent + 2, "CURSOR=\"%s\"\n", _cursor->getFilename()); + } + + AdTalkHolder::saveAsText(buffer, indent + 2); + + if (_region) { + _region->saveAsText(buffer, indent + 2); + } + + if (_scProp) { + _scProp->saveAsText(buffer, indent + 2); + } + + AdObject::saveAsText(buffer, indent + 2); + + buffer->putTextIndent(indent, "}\n\n"); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +int AdEntity::getHeight() { + if (_region && !_sprite) { + return _region->_rect.bottom - _region->_rect.top; + } else { + if (_currentSprite == NULL) { + _currentSprite = _sprite; + } + return AdObject::getHeight(); + } +} + + +////////////////////////////////////////////////////////////////////////// +void AdEntity::updatePosition() { + if (_region && !_sprite) { + _posX = _region->_rect.left + (_region->_rect.right - _region->_rect.left) / 2; + _posY = _region->_rect.bottom; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool AdEntity::persist(BasePersistenceManager *persistMgr) { + AdTalkHolder::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_item)); + persistMgr->transfer(TMEMBER(_region)); + //persistMgr->transfer(TMEMBER(_sprite)); + persistMgr->transfer(TMEMBER_INT(_subtype)); + _talkSprites.persist(persistMgr); + _talkSpritesEx.persist(persistMgr); + + persistMgr->transfer(TMEMBER(_walkToX)); + persistMgr->transfer(TMEMBER(_walkToY)); + persistMgr->transfer(TMEMBER_INT(_walkToDir)); + + persistMgr->transfer(TMEMBER(_theora)); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +void AdEntity::setItem(const char *itemName) { + BaseUtils::setString(&_item, itemName); +} + +////////////////////////////////////////////////////////////////////////// +bool AdEntity::setSprite(const char *filename) { + if (_currentSprite == _sprite) { + _currentSprite = NULL; + } + + delete _sprite; + _sprite = NULL; + BaseSprite *spr = new BaseSprite(_gameRef, this); + if (!spr || DID_FAIL(spr->loadFile(filename))) { + delete _sprite; + _sprite = NULL; + return STATUS_FAILED; + } else { + _sprite = spr; + _currentSprite = _sprite; + return STATUS_OK; + } +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_entity.h b/engines/wintermute/ad/ad_entity.h new file mode 100644 index 0000000000..94921aaa27 --- /dev/null +++ b/engines/wintermute/ad/ad_entity.h @@ -0,0 +1,73 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADENTITY_H +#define WINTERMUTE_ADENTITY_H + +#include "engines/wintermute/ad/ad_talk_holder.h" + +namespace Wintermute { +class VideoTheoraPlayer; +class AdEntity : public AdTalkHolder { +public: + VideoTheoraPlayer *_theora; + bool setSprite(const char *filename); + void setItem(const char *itemName); + DECLARE_PERSISTENT(AdEntity, AdTalkHolder) + void updatePosition(); + virtual int getHeight(); + BaseRegion *_region; + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent); + virtual bool update(); + virtual bool display(); + AdEntity(BaseGame *inGame); + virtual ~AdEntity(); + bool loadFile(const char *filename); + bool loadBuffer(byte *buffer, bool complete = true); + + int32 getWalkToX() const; + int32 getWalkToY() const; + TDirection getWalkToDir() const; + const char* getItemName() const; + + // scripting interface + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + virtual const char *scToString(); +private: + int32 _walkToX; + int32 _walkToY; + TDirection _walkToDir; + char *_item; + TEntityType _subtype; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_game.cpp b/engines/wintermute/ad/ad_game.cpp new file mode 100644 index 0000000000..29e932a586 --- /dev/null +++ b/engines/wintermute/ad/ad_game.cpp @@ -0,0 +1,2281 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_actor.h" +#include "engines/wintermute/ad/ad_game.h" +#include "engines/wintermute/ad/ad_entity.h" +#include "engines/wintermute/ad/ad_inventory.h" +#include "engines/wintermute/ad/ad_inventory_box.h" +#include "engines/wintermute/ad/ad_item.h" +#include "engines/wintermute/ad/ad_response.h" +#include "engines/wintermute/ad/ad_response_box.h" +#include "engines/wintermute/ad/ad_response_context.h" +#include "engines/wintermute/ad/ad_scene.h" +#include "engines/wintermute/ad/ad_scene_state.h" +#include "engines/wintermute/ad/ad_sentence.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/font/base_font.h" +#include "engines/wintermute/base/base_object.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/sound/base_sound.h" +#include "engines/wintermute/base/base_string_table.h" +#include "engines/wintermute/base/base_surface_storage.h" +#include "engines/wintermute/base/base_transition_manager.h" +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/base/base_viewport.h" +#include "engines/wintermute/base/particles/part_emitter.h" +#include "engines/wintermute/base/saveload.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/base/scriptables/script_engine.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/ui/ui_entity.h" +#include "engines/wintermute/ui/ui_window.h" +#include "engines/wintermute/utils/utils.h" +#include "engines/wintermute/video/video_player.h" +#include "engines/wintermute/video/video_theora_player.h" +#include "engines/wintermute/platform_osystem.h" +#include "common/str.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdGame, true) + +////////////////////////////////////////////////////////////////////////// +AdGame::AdGame(const Common::String &gameId) : BaseGame(gameId) { + _responseBox = NULL; + _inventoryBox = NULL; + + _scene = new AdScene(_gameRef); + _scene->setName(""); + registerObject(_scene); + + _prevSceneName = NULL; + _prevSceneFilename = NULL; + _scheduledScene = NULL; + _scheduledFadeIn = false; + + + _stateEx = GAME_NORMAL; + + _selectedItem = NULL; + + + _texItemLifeTime = 10000; + _texWalkLifeTime = 10000; + _texStandLifeTime = 10000; + _texTalkLifeTime = 10000; + + _talkSkipButton = TALK_SKIP_LEFT; + + _sceneViewport = NULL; + + _initialScene = true; + _debugStartupScene = NULL; + _startupScene = NULL; + + _invObject = new AdObject(this); + _inventoryOwner = _invObject; + + _tempDisableSaveState = false; + _itemsFile = NULL; + + _smartItemCursor = false; + + addSpeechDir("speech"); +} + + +////////////////////////////////////////////////////////////////////////// +AdGame::~AdGame() { + cleanup(); +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::cleanup() { + for (uint32 i = 0; i < _objects.size(); i++) { + unregisterObject(_objects[i]); + _objects[i] = NULL; + } + _objects.clear(); + + + for (uint32 i = 0; i < _dlgPendingBranches.size(); i++) { + delete[] _dlgPendingBranches[i]; + } + _dlgPendingBranches.clear(); + + for (uint32 i = 0; i < _speechDirs.size(); i++) { + delete[] _speechDirs[i]; + } + _speechDirs.clear(); + + + unregisterObject(_scene); + _scene = NULL; + + // remove items + for (uint32 i = 0; i < _items.size(); i++) { + _gameRef->unregisterObject(_items[i]); + } + _items.clear(); + + + // clear remaining inventories + delete _invObject; + _invObject = NULL; + + for (uint32 i = 0; i < _inventories.size(); i++) { + delete _inventories[i]; + } + _inventories.clear(); + + + if (_responseBox) { + _gameRef->unregisterObject(_responseBox); + _responseBox = NULL; + } + + if (_inventoryBox) { + _gameRef->unregisterObject(_inventoryBox); + _inventoryBox = NULL; + } + + delete[] _prevSceneName; + delete[] _prevSceneFilename; + delete[] _scheduledScene; + delete[] _debugStartupScene; + delete[] _itemsFile; + _prevSceneName = NULL; + _prevSceneFilename = NULL; + _scheduledScene = NULL; + _debugStartupScene = NULL; + _startupScene = NULL; + _itemsFile = NULL; + + delete _sceneViewport; + _sceneViewport = NULL; + + for (uint32 i = 0; i < _sceneStates.size(); i++) { + delete _sceneStates[i]; + } + _sceneStates.clear(); + + for (uint32 i = 0; i < _responsesBranch.size(); i++) { + delete _responsesBranch[i]; + } + _responsesBranch.clear(); + + for (uint32 i = 0; i < _responsesGame.size(); i++) { + delete _responsesGame[i]; + } + _responsesGame.clear(); + + return BaseGame::cleanup(); +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::initLoop() { + if (_scheduledScene && _transMgr->isReady()) { + changeScene(_scheduledScene, _scheduledFadeIn); + delete[] _scheduledScene; + _scheduledScene = NULL; + + _gameRef->_activeObject = NULL; + } + + + bool res; + res = BaseGame::initLoop(); + if (DID_FAIL(res)) { + return res; + } + + if (_scene) { + res = _scene->initLoop(); + } + + _sentences.clear(); + + return res; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::addObject(AdObject *object) { + _objects.add(object); + return registerObject(object); +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::removeObject(AdObject *object) { + // in case the user called Scene.CreateXXX() and Game.DeleteXXX() + if (_scene) { + bool res = _scene->removeObject(object); + if (DID_SUCCEED(res)) { + return res; + } + } + + for (uint32 i = 0; i < _objects.size(); i++) { + if (_objects[i] == object) { + _objects.remove_at(i); + break; + } + } + return unregisterObject(object); +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::changeScene(const char *filename, bool fadeIn) { + if (_scene == NULL) { + _scene = new AdScene(_gameRef); + registerObject(_scene); + } else { + _scene->applyEvent("SceneShutdown", true); + + setPrevSceneName(_scene->getName()); + setPrevSceneFilename(_scene->getFilename()); + + if (!_tempDisableSaveState) { + _scene->saveState(); + } + _tempDisableSaveState = false; + } + + if (_scene) { + // reset objects + for (uint32 i = 0; i < _objects.size(); i++) { + _objects[i]->reset(); + } + + // reset scene properties + _scene->_sFXVolume = 100; + if (_scene->_scProp) { + _scene->_scProp->cleanup(); + } + + bool ret; + if (_initialScene && _debugDebugMode && _debugStartupScene) { + _initialScene = false; + ret = _scene->loadFile(_debugStartupScene); + } else { + ret = _scene->loadFile(filename); + } + + if (DID_SUCCEED(ret)) { + // invalidate references to the original scene + for (uint32 i = 0; i < _objects.size(); i++) { + _objects[i]->invalidateCurrRegions(); + _objects[i]->_stickRegion = NULL; + } + + _scene->loadState(); + } + if (fadeIn) { + _gameRef->_transMgr->start(TRANSITION_FADE_IN); + } + return ret; + } else { + return STATUS_FAILED; + } +} + + +////////////////////////////////////////////////////////////////////////// +void AdGame::addSentence(AdSentence *sentence) { + _sentences.add(sentence); +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::displaySentences(bool frozen) { + for (uint32 i = 0; i < _sentences.size(); i++) { + if (frozen && _sentences[i]->_freezable) { + continue; + } else { + _sentences[i]->display(); + } + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +void AdGame::finishSentences() { + for (uint32 i = 0; i < _sentences.size(); i++) { + if (_sentences[i]->canSkip()) { + _sentences[i]->_duration = 0; + if (_sentences[i]->_sound) { + _sentences[i]->_sound->stop(); + } + } + } +} + + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool AdGame::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // ChangeScene + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "ChangeScene") == 0) { + stack->correctParams(3); + const char *filename = stack->pop()->getString(); + ScValue *valFadeOut = stack->pop(); + ScValue *valFadeIn = stack->pop(); + + bool transOut = valFadeOut->isNULL() ? true : valFadeOut->getBool(); + bool transIn = valFadeIn->isNULL() ? true : valFadeIn->getBool(); + + scheduleChangeScene(filename, transIn); + if (transOut) { + _transMgr->start(TRANSITION_FADE_OUT, true); + } + stack->pushNULL(); + + + //bool ret = ChangeScene(stack->pop()->getString()); + //if (DID_FAIL(ret)) stack->pushBool(false); + //else stack->pushBool(true); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // LoadActor + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "LoadActor") == 0) { + stack->correctParams(1); + AdActor *act = new AdActor(_gameRef); + if (act && DID_SUCCEED(act->loadFile(stack->pop()->getString()))) { + addObject(act); + stack->pushNative(act, true); + } else { + delete act; + act = NULL; + stack->pushNULL(); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // LoadEntity + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "LoadEntity") == 0) { + stack->correctParams(1); + AdEntity *ent = new AdEntity(_gameRef); + if (ent && DID_SUCCEED(ent->loadFile(stack->pop()->getString()))) { + addObject(ent); + stack->pushNative(ent, true); + } else { + delete ent; + ent = NULL; + stack->pushNULL(); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // UnloadObject / UnloadActor / UnloadEntity / DeleteEntity + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "UnloadObject") == 0 || strcmp(name, "UnloadActor") == 0 || strcmp(name, "UnloadEntity") == 0 || strcmp(name, "DeleteEntity") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + AdObject *obj = (AdObject *)val->getNative(); + removeObject(obj); + if (val->getType() == VAL_VARIABLE_REF) { + val->setNULL(); + } + + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // CreateEntity + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "CreateEntity") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + AdEntity *ent = new AdEntity(_gameRef); + addObject(ent); + if (!val->isNULL()) { + ent->setName(val->getString()); + } + stack->pushNative(ent, true); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // CreateItem + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "CreateItem") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + AdItem *item = new AdItem(_gameRef); + addItem(item); + if (!val->isNULL()) { + item->setName(val->getString()); + } + stack->pushNative(item, true); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // DeleteItem + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "DeleteItem") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + AdItem *item = NULL; + if (val->isNative()) { + item = (AdItem *)val->getNative(); + } else { + item = getItemByName(val->getString()); + } + + if (item) { + deleteItem(item); + } + + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // QueryItem + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "QueryItem") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + AdItem *item = NULL; + if (val->isInt()) { + int index = val->getInt(); + if (index >= 0 && index < (int32)_items.size()) { + item = _items[index]; + } + } else { + item = getItemByName(val->getString()); + } + + if (item) { + stack->pushNative(item, true); + } else { + stack->pushNULL(); + } + + return STATUS_OK; + } + + + ////////////////////////////////////////////////////////////////////////// + // AddResponse/AddResponseOnce/AddResponseOnceGame + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AddResponse") == 0 || strcmp(name, "AddResponseOnce") == 0 || strcmp(name, "AddResponseOnceGame") == 0) { + stack->correctParams(6); + int id = stack->pop()->getInt(); + const char *text = stack->pop()->getString(); + ScValue *val1 = stack->pop(); + ScValue *val2 = stack->pop(); + ScValue *val3 = stack->pop(); + ScValue *val4 = stack->pop(); + + if (_responseBox) { + AdResponse *res = new AdResponse(_gameRef); + if (res) { + res->_iD = id; + res->setText(text); + _stringTable->expand(&res->_text); + if (!val1->isNULL()) { + res->setIcon(val1->getString()); + } + if (!val2->isNULL()) { + res->setIconHover(val2->getString()); + } + if (!val3->isNULL()) { + res->setIconPressed(val3->getString()); + } + if (!val4->isNULL()) { + res->setFont(val4->getString()); + } + + if (strcmp(name, "AddResponseOnce") == 0) { + res->_responseType = RESPONSE_ONCE; + } else if (strcmp(name, "AddResponseOnceGame") == 0) { + res->_responseType = RESPONSE_ONCE_GAME; + } + + _responseBox->addResponse(res); + } + } else { + script->runtimeError("Game.AddResponse: response box is not defined"); + } + stack->pushNULL(); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ResetResponse + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ResetResponse") == 0) { + stack->correctParams(1); + int id = stack->pop()->getInt(-1); + resetResponse(id); + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ClearResponses + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ClearResponses") == 0) { + stack->correctParams(0); + _responseBox->clearResponses(); + _responseBox->clearButtons(); + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetResponse + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetResponse") == 0) { + stack->correctParams(1); + bool autoSelectLast = stack->pop()->getBool(); + + if (_responseBox) { + _responseBox->weedResponses(); + + if (_responseBox->getNumResponses() == 0) { + stack->pushNULL(); + return STATUS_OK; + } + + + if (_responseBox->getNumResponses() == 1 && autoSelectLast) { + stack->pushInt(_responseBox->getIdForResponseNum(0)); + _responseBox->handleResponseNum(0); + _responseBox->clearResponses(); + return STATUS_OK; + } + + _responseBox->createButtons(); + _responseBox->_waitingScript = script; + script->waitForExclusive(_responseBox); + _state = GAME_SEMI_FROZEN; + _stateEx = GAME_WAITING_RESPONSE; + } else { + script->runtimeError("Game.GetResponse: response box is not defined"); + stack->pushNULL(); + } + return STATUS_OK; + } + + + ////////////////////////////////////////////////////////////////////////// + // GetNumResponses + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetNumResponses") == 0) { + stack->correctParams(0); + if (_responseBox) { + _responseBox->weedResponses(); + stack->pushInt(_responseBox->getNumResponses()); + } else { + script->runtimeError("Game.GetNumResponses: response box is not defined"); + stack->pushNULL(); + } + return STATUS_OK; + } + + + ////////////////////////////////////////////////////////////////////////// + // StartDlgBranch + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "StartDlgBranch") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + Common::String branchName; + if (val->isNULL()) { + branchName.format("line%d", script->_currentLine); + } else { + branchName = val->getString(); + } + + startDlgBranch(branchName.c_str(), script->_filename == NULL ? "" : script->_filename, script->_threadEvent == NULL ? "" : script->_threadEvent); + stack->pushNULL(); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // EndDlgBranch + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "EndDlgBranch") == 0) { + stack->correctParams(1); + + const char *branchName = NULL; + ScValue *val = stack->pop(); + if (!val->isNULL()) { + branchName = val->getString(); + } + endDlgBranch(branchName, script->_filename == NULL ? "" : script->_filename, script->_threadEvent == NULL ? "" : script->_threadEvent); + + stack->pushNULL(); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetCurrentDlgBranch + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetCurrentDlgBranch") == 0) { + stack->correctParams(0); + + if (_dlgPendingBranches.size() > 0) { + stack->pushString(_dlgPendingBranches[_dlgPendingBranches.size() - 1]); + } else { + stack->pushNULL(); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // TakeItem + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "TakeItem") == 0) { + return _invObject->scCallMethod(script, stack, thisStack, name); + } + + ////////////////////////////////////////////////////////////////////////// + // DropItem + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "DropItem") == 0) { + return _invObject->scCallMethod(script, stack, thisStack, name); + } + + ////////////////////////////////////////////////////////////////////////// + // GetItem + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetItem") == 0) { + return _invObject->scCallMethod(script, stack, thisStack, name); + } + + ////////////////////////////////////////////////////////////////////////// + // HasItem + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "HasItem") == 0) { + return _invObject->scCallMethod(script, stack, thisStack, name); + } + + ////////////////////////////////////////////////////////////////////////// + // IsItemTaken + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "IsItemTaken") == 0) { + stack->correctParams(1); + + ScValue *val = stack->pop(); + if (!val->isNULL()) { + for (uint32 i = 0; i < _inventories.size(); i++) { + AdInventory *inv = _inventories[i]; + + for (uint32 j = 0; j < inv->_takenItems.size(); j++) { + if (val->getNative() == inv->_takenItems[j]) { + stack->pushBool(true); + return STATUS_OK; + } else if (scumm_stricmp(val->getString(), inv->_takenItems[j]->getName()) == 0) { + stack->pushBool(true); + return STATUS_OK; + } + } + } + } else { + script->runtimeError("Game.IsItemTaken: item name expected"); + } + + stack->pushBool(false); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetInventoryWindow + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetInventoryWindow") == 0) { + stack->correctParams(0); + if (_inventoryBox && _inventoryBox->_window) { + stack->pushNative(_inventoryBox->_window, true); + } else { + stack->pushNULL(); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetResponsesWindow + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetResponsesWindow") == 0 || strcmp(name, "GetResponseWindow") == 0) { + stack->correctParams(0); + if (_responseBox && _responseBox->getResponseWindow()) { + stack->pushNative(_responseBox->getResponseWindow(), true); + } else { + stack->pushNULL(); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // LoadResponseBox + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "LoadResponseBox") == 0) { + stack->correctParams(1); + const char *filename = stack->pop()->getString(); + + _gameRef->unregisterObject(_responseBox); + _responseBox = new AdResponseBox(_gameRef); + if (_responseBox && !DID_FAIL(_responseBox->loadFile(filename))) { + registerObject(_responseBox); + stack->pushBool(true); + } else { + delete _responseBox; + _responseBox = NULL; + stack->pushBool(false); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // LoadInventoryBox + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "LoadInventoryBox") == 0) { + stack->correctParams(1); + const char *filename = stack->pop()->getString(); + + _gameRef->unregisterObject(_inventoryBox); + _inventoryBox = new AdInventoryBox(_gameRef); + if (_inventoryBox && !DID_FAIL(_inventoryBox->loadFile(filename))) { + registerObject(_inventoryBox); + stack->pushBool(true); + } else { + delete _inventoryBox; + _inventoryBox = NULL; + stack->pushBool(false); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // LoadItems + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "LoadItems") == 0) { + stack->correctParams(2); + const char *filename = stack->pop()->getString(); + bool merge = stack->pop()->getBool(false); + + bool ret = loadItemsFile(filename, merge); + stack->pushBool(DID_SUCCEED(ret)); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // AddSpeechDir + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AddSpeechDir") == 0) { + stack->correctParams(1); + const char *dir = stack->pop()->getString(); + stack->pushBool(DID_SUCCEED(addSpeechDir(dir))); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // RemoveSpeechDir + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "RemoveSpeechDir") == 0) { + stack->correctParams(1); + const char *dir = stack->pop()->getString(); + stack->pushBool(DID_SUCCEED(removeSpeechDir(dir))); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetSceneViewport + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetSceneViewport") == 0) { + stack->correctParams(4); + int x = stack->pop()->getInt(); + int y = stack->pop()->getInt(); + int width = stack->pop()->getInt(); + int height = stack->pop()->getInt(); + + if (width <= 0) { + width = _renderer->_width; + } + if (height <= 0) { + height = _renderer->_height; + } + + if (!_sceneViewport) { + _sceneViewport = new BaseViewport(_gameRef); + } + if (_sceneViewport) { + _sceneViewport->setRect(x, y, x + width, y + height); + } + + stack->pushBool(true); + + return STATUS_OK; + } + + + else { + return BaseGame::scCallMethod(script, stack, thisStack, name); + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *AdGame::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("game"); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // Scene + ////////////////////////////////////////////////////////////////////////// + else if (name == "Scene") { + if (_scene) { + _scValue->setNative(_scene, true); + } else { + _scValue->setNULL(); + } + + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // SelectedItem + ////////////////////////////////////////////////////////////////////////// + else if (name == "SelectedItem") { + //if (_selectedItem) _scValue->setString(_selectedItem->_name); + if (_selectedItem) { + _scValue->setNative(_selectedItem, true); + } else { + _scValue->setNULL(); + } + + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // NumItems + ////////////////////////////////////////////////////////////////////////// + else if (name == "NumItems") { + return _invObject->scGetProperty(name); + } + + ////////////////////////////////////////////////////////////////////////// + // SmartItemCursor + ////////////////////////////////////////////////////////////////////////// + else if (name == "SmartItemCursor") { + _scValue->setBool(_smartItemCursor); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // InventoryVisible + ////////////////////////////////////////////////////////////////////////// + else if (name == "InventoryVisible") { + _scValue->setBool(_inventoryBox && _inventoryBox->_visible); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // InventoryScrollOffset + ////////////////////////////////////////////////////////////////////////// + else if (name == "InventoryScrollOffset") { + if (_inventoryBox) { + _scValue->setInt(_inventoryBox->_scrollOffset); + } else { + _scValue->setInt(0); + } + + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // ResponsesVisible (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "ResponsesVisible") { + _scValue->setBool(_stateEx == GAME_WAITING_RESPONSE); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // PrevScene / PreviousScene (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "PrevScene" || name == "PreviousScene") { + if (!_prevSceneName) { + _scValue->setString(""); + } else { + _scValue->setString(_prevSceneName); + } + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // PrevSceneFilename / PreviousSceneFilename (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "PrevSceneFilename" || name == "PreviousSceneFilename") { + if (!_prevSceneFilename) { + _scValue->setString(""); + } else { + _scValue->setString(_prevSceneFilename); + } + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // LastResponse (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "LastResponse") { + if (!_responseBox || !_responseBox->getLastResponseText()) { + _scValue->setString(""); + } else { + _scValue->setString(_responseBox->getLastResponseText()); + } + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // LastResponseOrig (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "LastResponseOrig") { + if (!_responseBox || !_responseBox->getLastResponseTextOrig()) { + _scValue->setString(""); + } else { + _scValue->setString(_responseBox->getLastResponseTextOrig()); + } + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // InventoryObject + ////////////////////////////////////////////////////////////////////////// + else if (name == "InventoryObject") { + if (_inventoryOwner == _invObject) { + _scValue->setNative(this, true); + } else { + _scValue->setNative(_inventoryOwner, true); + } + + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // TotalNumItems + ////////////////////////////////////////////////////////////////////////// + else if (name == "TotalNumItems") { + _scValue->setInt(_items.size()); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // TalkSkipButton + ////////////////////////////////////////////////////////////////////////// + else if (name == "TalkSkipButton") { + _scValue->setInt(_talkSkipButton); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // ChangingScene + ////////////////////////////////////////////////////////////////////////// + else if (name == "ChangingScene") { + _scValue->setBool(_scheduledScene != NULL); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // StartupScene + ////////////////////////////////////////////////////////////////////////// + else if (name == "StartupScene") { + if (!_startupScene) { + _scValue->setNULL(); + } else { + _scValue->setString(_startupScene); + } + return _scValue; + } + + else { + return BaseGame::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::scSetProperty(const char *name, ScValue *value) { + + ////////////////////////////////////////////////////////////////////////// + // SelectedItem + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "SelectedItem") == 0) { + if (value->isNULL()) { + _selectedItem = NULL; + } else { + if (value->isNative()) { + _selectedItem = NULL; + for (uint32 i = 0; i < _items.size(); i++) { + if (_items[i] == value->getNative()) { + _selectedItem = (AdItem *)value->getNative(); + break; + } + } + } else { + // try to get by name + _selectedItem = getItemByName(value->getString()); + } + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SmartItemCursor + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SmartItemCursor") == 0) { + _smartItemCursor = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // InventoryVisible + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "InventoryVisible") == 0) { + if (_inventoryBox) { + _inventoryBox->_visible = value->getBool(); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // InventoryObject + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "InventoryObject") == 0) { + if (_inventoryOwner && _inventoryBox) { + _inventoryOwner->getInventory()->_scrollOffset = _inventoryBox->_scrollOffset; + } + + if (value->isNULL()) { + _inventoryOwner = _invObject; + } else { + BaseObject *obj = (BaseObject *)value->getNative(); + if (obj == this) { + _inventoryOwner = _invObject; + } else if (_gameRef->validObject(obj)) { + _inventoryOwner = (AdObject *)obj; + } + } + + if (_inventoryOwner && _inventoryBox) { + _inventoryBox->_scrollOffset = _inventoryOwner->getInventory()->_scrollOffset; + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // InventoryScrollOffset + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "InventoryScrollOffset") == 0) { + if (_inventoryBox) { + _inventoryBox->_scrollOffset = value->getInt(); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // TalkSkipButton + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "TalkSkipButton") == 0) { + int val = value->getInt(); + if (val < 0) { + val = 0; + } + if (val > TALK_SKIP_NONE) { + val = TALK_SKIP_NONE; + } + _talkSkipButton = (TTalkSkipButton)val; + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // StartupScene + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "StartupScene") == 0) { + if (value == NULL) { + delete[] _startupScene; + _startupScene = NULL; + } else { + BaseUtils::setString(&_startupScene, value->getString()); + } + + return STATUS_OK; + } + + else { + return BaseGame::scSetProperty(name, value); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::externalCall(ScScript *script, ScStack *stack, ScStack *thisStack, char *name) { + ScValue *thisObj; + + ////////////////////////////////////////////////////////////////////////// + // Actor + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Actor") == 0) { + stack->correctParams(0); + thisObj = thisStack->getTop(); + + thisObj->setNative(new AdActor(_gameRef)); + stack->pushNULL(); + } + + ////////////////////////////////////////////////////////////////////////// + // Entity + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Entity") == 0) { + stack->correctParams(0); + thisObj = thisStack->getTop(); + + thisObj->setNative(new AdEntity(_gameRef)); + stack->pushNULL(); + } + + + ////////////////////////////////////////////////////////////////////////// + // call parent + else { + return BaseGame::externalCall(script, stack, thisStack, name); + } + + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::showCursor() { + if (_cursorHidden) { + return STATUS_OK; + } + + if (_selectedItem && _gameRef->_state == GAME_RUNNING && _stateEx == GAME_NORMAL && _interactive) { + if (_selectedItem->_cursorCombined) { + BaseSprite *origLastCursor = _lastCursor; + BaseGame::showCursor(); + _lastCursor = origLastCursor; + } + if (_activeObject && _selectedItem->_cursorHover && _activeObject->getExtendedFlag("usable")) { + if (!_smartItemCursor || _activeObject->canHandleEvent(_selectedItem->getName())) { + return drawCursor(_selectedItem->_cursorHover); + } else { + return drawCursor(_selectedItem->_cursorNormal); + } + } else { + return drawCursor(_selectedItem->_cursorNormal); + } + } else { + return BaseGame::showCursor(); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::loadFile(const char *filename) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "AdGame::LoadFile failed for file '%s'", filename); + return STATUS_FAILED; + } + + bool ret; + + setFilename(filename); + + if (DID_FAIL(ret = loadBuffer(buffer, true))) { + _gameRef->LOG(0, "Error parsing GAME file '%s'", filename); + } + + + delete[] buffer; + + return ret; +} + + +TOKEN_DEF_START +TOKEN_DEF(GAME) +TOKEN_DEF(AD_GAME) +TOKEN_DEF(RESPONSE_BOX) +TOKEN_DEF(INVENTORY_BOX) +TOKEN_DEF(ITEMS) +TOKEN_DEF(ITEM) +TOKEN_DEF(TALK_SKIP_BUTTON) +TOKEN_DEF(SCENE_VIEWPORT) +TOKEN_DEF(ENTITY_CONTAINER) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF(STARTUP_SCENE) +TOKEN_DEF(DEBUG_STARTUP_SCENE) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool AdGame::loadBuffer(byte *buffer, bool complete) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(GAME) + TOKEN_TABLE(AD_GAME) + TOKEN_TABLE(RESPONSE_BOX) + TOKEN_TABLE(INVENTORY_BOX) + TOKEN_TABLE(ITEMS) + TOKEN_TABLE(TALK_SKIP_BUTTON) + TOKEN_TABLE(SCENE_VIEWPORT) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE(STARTUP_SCENE) + TOKEN_TABLE(DEBUG_STARTUP_SCENE) + TOKEN_TABLE_END + + byte *params; + byte *params2; + int cmd = 1; + BaseParser parser; + + bool itemFound = false, itemsFound = false; + + while (cmd > 0 && (cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_GAME: + if (DID_FAIL(BaseGame::loadBuffer(params, false))) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_AD_GAME: + while (cmd > 0 && (cmd = parser.getCommand((char **)¶ms, commands, (char **)¶ms2)) > 0) { + switch (cmd) { + case TOKEN_RESPONSE_BOX: + delete _responseBox; + _responseBox = new AdResponseBox(_gameRef); + if (_responseBox && !DID_FAIL(_responseBox->loadFile((char *)params2))) { + registerObject(_responseBox); + } else { + delete _responseBox; + _responseBox = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_INVENTORY_BOX: + delete _inventoryBox; + _inventoryBox = new AdInventoryBox(_gameRef); + if (_inventoryBox && !DID_FAIL(_inventoryBox->loadFile((char *)params2))) { + registerObject(_inventoryBox); + } else { + delete _inventoryBox; + _inventoryBox = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_ITEMS: + itemsFound = true; + BaseUtils::setString(&_itemsFile, (char *)params2); + if (DID_FAIL(loadItemsFile(_itemsFile))) { + delete[] _itemsFile; + _itemsFile = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_TALK_SKIP_BUTTON: + if (scumm_stricmp((char *)params2, "right") == 0) { + _talkSkipButton = TALK_SKIP_RIGHT; + } else if (scumm_stricmp((char *)params2, "both") == 0) { + _talkSkipButton = TALK_SKIP_BOTH; + } else { + _talkSkipButton = TALK_SKIP_LEFT; + } + break; + + case TOKEN_SCENE_VIEWPORT: { + Rect32 rc; + parser.scanStr((char *)params2, "%d,%d,%d,%d", &rc.left, &rc.top, &rc.right, &rc.bottom); + if (!_sceneViewport) { + _sceneViewport = new BaseViewport(_gameRef); + } + if (_sceneViewport) { + _sceneViewport->setRect(rc.left, rc.top, rc.right, rc.bottom); + } + } + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty(params2, false); + break; + + case TOKEN_STARTUP_SCENE: + BaseUtils::setString(&_startupScene, (char *)params2); + break; + + case TOKEN_DEBUG_STARTUP_SCENE: + BaseUtils::setString(&_debugStartupScene, (char *)params2); + break; + } + } + break; + } + } + + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in GAME definition"); + return STATUS_FAILED; + } + if (cmd == PARSERR_GENERIC) { + _gameRef->LOG(0, "Error loading GAME definition"); + return STATUS_FAILED; + } + + if (itemFound && !itemsFound) { + _gameRef->LOG(0, "**Warning** Please put the items definition to a separate file."); + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::persist(BasePersistenceManager *persistMgr) { + if (!persistMgr->getIsSaving()) { + cleanup(); + } + BaseGame::persist(persistMgr); + + _dlgPendingBranches.persist(persistMgr); + + _inventories.persist(persistMgr); + persistMgr->transfer(TMEMBER(_inventoryBox)); + + _objects.persist(persistMgr); + + persistMgr->transfer(TMEMBER(_prevSceneName)); + persistMgr->transfer(TMEMBER(_prevSceneFilename)); + + persistMgr->transfer(TMEMBER(_responseBox)); + _responsesBranch.persist(persistMgr); + _responsesGame.persist(persistMgr); + persistMgr->transfer(TMEMBER(_scene)); + _sceneStates.persist(persistMgr); + persistMgr->transfer(TMEMBER(_scheduledFadeIn)); + persistMgr->transfer(TMEMBER(_scheduledScene)); + persistMgr->transfer(TMEMBER(_selectedItem)); + persistMgr->transfer(TMEMBER_INT(_talkSkipButton)); + + _sentences.persist(persistMgr); + + persistMgr->transfer(TMEMBER(_sceneViewport)); + persistMgr->transfer(TMEMBER_INT(_stateEx)); + persistMgr->transfer(TMEMBER(_initialScene)); + persistMgr->transfer(TMEMBER(_debugStartupScene)); + + persistMgr->transfer(TMEMBER(_invObject)); + persistMgr->transfer(TMEMBER(_inventoryOwner)); + persistMgr->transfer(TMEMBER(_tempDisableSaveState)); + _items.persist(persistMgr); + + persistMgr->transfer(TMEMBER(_itemsFile)); + + _speechDirs.persist(persistMgr); + persistMgr->transfer(TMEMBER(_smartItemCursor)); + + if (!persistMgr->getIsSaving()) { + _initialScene = false; + } + + persistMgr->transfer(TMEMBER(_startupScene)); + + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +void AdGame::setPrevSceneName(const char *name) { + delete[] _prevSceneName; + _prevSceneName = NULL; + if (name) { + _prevSceneName = new char[strlen(name) + 1]; + if (_prevSceneName) { + strcpy(_prevSceneName, name); + } + } +} + + +////////////////////////////////////////////////////////////////////////// +void AdGame::setPrevSceneFilename(const char *name) { + delete[] _prevSceneFilename; + _prevSceneFilename = NULL; + if (name) { + _prevSceneFilename = new char[strlen(name) + 1]; + if (_prevSceneFilename) { + strcpy(_prevSceneFilename, name); + } + } +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::scheduleChangeScene(const char *filename, bool fadeIn) { + delete[] _scheduledScene; + _scheduledScene = NULL; + + if (_scene && !_scene->_initialized) { + return changeScene(filename, fadeIn); + } else { + _scheduledScene = new char [strlen(filename) + 1]; + strcpy(_scheduledScene, filename); + + _scheduledFadeIn = fadeIn; + + return STATUS_OK; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::getVersion(byte *verMajor, byte *verMinor, byte *extMajor, byte *extMinor) { + BaseGame::getVersion(verMajor, verMinor, NULL, NULL); + + if (extMajor) { + *extMajor = 0; + } + if (extMinor) { + *extMinor = 0; + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::loadItemsFile(const char *filename, bool merge) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "AdGame::LoadItemsFile failed for file '%s'", filename); + return STATUS_FAILED; + } + + bool ret; + + //_filename = new char [strlen(filename)+1]; + //strcpy(_filename, filename); + + if (DID_FAIL(ret = loadItemsBuffer(buffer, merge))) { + _gameRef->LOG(0, "Error parsing ITEMS file '%s'", filename); + } + + + delete[] buffer; + + return ret; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::loadItemsBuffer(byte *buffer, bool merge) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(ITEM) + TOKEN_TABLE_END + + byte *params; + int cmd; + BaseParser parser; + + if (!merge) { + while (_items.size() > 0) { + deleteItem(_items[0]); + } + } + + while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_ITEM: { + AdItem *item = new AdItem(_gameRef); + if (item && !DID_FAIL(item->loadBuffer(params, false))) { + // delete item with the same name, if exists + if (merge) { + AdItem *prevItem = getItemByName(item->getName()); + if (prevItem) { + deleteItem(prevItem); + } + } + addItem(item); + } else { + delete item; + item = NULL; + cmd = PARSERR_GENERIC; + } + } + break; + } + } + + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in ITEMS definition"); + return STATUS_FAILED; + } + if (cmd == PARSERR_GENERIC) { + _gameRef->LOG(0, "Error loading ITEMS definition"); + return STATUS_FAILED; + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +AdSceneState *AdGame::getSceneState(const char *filename, bool saving) { + char *filenameCor = new char[strlen(filename) + 1]; + strcpy(filenameCor, filename); + for (uint32 i = 0; i < strlen(filenameCor); i++) { + if (filenameCor[i] == '/') { + filenameCor[i] = '\\'; + } + } + + for (uint32 i = 0; i < _sceneStates.size(); i++) { + if (scumm_stricmp(_sceneStates[i]->getFilename(), filenameCor) == 0) { + delete[] filenameCor; + return _sceneStates[i]; + } + } + + if (saving) { + AdSceneState *ret = new AdSceneState(_gameRef); + ret->setFilename(filenameCor); + + _sceneStates.add(ret); + + delete[] filenameCor; + return ret; + } else { + delete[] filenameCor; + return NULL; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::windowLoadHook(UIWindow *win, char **buffer, char **params) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(ENTITY_CONTAINER) + TOKEN_TABLE_END + + int cmd = PARSERR_GENERIC; + BaseParser parser; + + cmd = parser.getCommand(buffer, commands, params); + switch (cmd) { + case TOKEN_ENTITY_CONTAINER: { + UIEntity *ent = new UIEntity(_gameRef); + if (!ent || DID_FAIL(ent->loadBuffer((byte *)*params, false))) { + delete ent; + ent = NULL; + cmd = PARSERR_GENERIC; + } else { + ent->_parent = win; + win->_widgets.add(ent); + } + } + break; + } + + if (cmd == PARSERR_TOKENNOTFOUND || cmd == PARSERR_GENERIC) { + return STATUS_FAILED; + } + + return STATUS_OK; + +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::windowScriptMethodHook(UIWindow *win, ScScript *script, ScStack *stack, const char *name) { + if (strcmp(name, "CreateEntityContainer") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + UIEntity *ent = new UIEntity(_gameRef); + if (!val->isNULL()) { + ent->setName(val->getString()); + } + stack->pushNative(ent, true); + + ent->_parent = win; + win->_widgets.add(ent); + + return STATUS_OK; + } else { + return STATUS_FAILED; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::startDlgBranch(const char *branchName, const char *scriptName, const char *eventName) { + char *name = new char[strlen(branchName) + 1 + strlen(scriptName) + 1 + strlen(eventName) + 1]; + if (name) { + sprintf(name, "%s.%s.%s", branchName, scriptName, eventName); + _dlgPendingBranches.add(name); + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::endDlgBranch(const char *branchName, const char *scriptName, const char *eventName) { + char *name = NULL; + bool deleteName = false; + if (branchName == NULL && _dlgPendingBranches.size() > 0) { + name = _dlgPendingBranches[_dlgPendingBranches.size() - 1]; + } else { + if (branchName != NULL) { + name = new char[strlen(branchName) + 1 + strlen(scriptName) + 1 + strlen(eventName) + 1]; + if (name) { + sprintf(name, "%s.%s.%s", branchName, scriptName, eventName); + deleteName = true; + } + } + } + + if (name == NULL) { + return STATUS_OK; + } + + + int startIndex = -1; + for (int i = _dlgPendingBranches.size() - 1; i >= 0; i--) { + if (scumm_stricmp(name, _dlgPendingBranches[i]) == 0) { + startIndex = i; + break; + } + } + if (startIndex >= 0) { + for (uint32 i = startIndex; i < _dlgPendingBranches.size(); i++) { + //ClearBranchResponses(_dlgPendingBranches[i]); + delete[] _dlgPendingBranches[i]; + _dlgPendingBranches[i] = NULL; + } + _dlgPendingBranches.remove_at(startIndex, _dlgPendingBranches.size() - startIndex); + } + + // dialogue is over, forget selected responses + if (_dlgPendingBranches.size() == 0) { + for (uint32 i = 0; i < _responsesBranch.size(); i++) { + delete _responsesBranch[i]; + } + _responsesBranch.clear(); + } + + if (deleteName) { + delete[] name; + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::clearBranchResponses(char *name) { + for (uint32 i = 0; i < _responsesBranch.size(); i++) { + if (scumm_stricmp(name, _responsesBranch[i]->_context) == 0) { + delete _responsesBranch[i]; + _responsesBranch.remove_at(i); + i--; + } + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::addBranchResponse(int id) { + if (branchResponseUsed(id)) { + return STATUS_OK; + } + AdResponseContext *r = new AdResponseContext(_gameRef); + r->_id = id; + r->setContext(_dlgPendingBranches.size() > 0 ? _dlgPendingBranches[_dlgPendingBranches.size() - 1] : NULL); + _responsesBranch.add(r); + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::branchResponseUsed(int id) { + char *context = _dlgPendingBranches.size() > 0 ? _dlgPendingBranches[_dlgPendingBranches.size() - 1] : NULL; + for (uint32 i = 0; i < _responsesBranch.size(); i++) { + if (_responsesBranch[i]->_id == id) { + if ((context == NULL && _responsesBranch[i]->_context == NULL) || scumm_stricmp(context, _responsesBranch[i]->_context) == 0) { + return true; + } + } + } + return false; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::addGameResponse(int id) { + if (gameResponseUsed(id)) { + return STATUS_OK; + } + AdResponseContext *r = new AdResponseContext(_gameRef); + r->_id = id; + r->setContext(_dlgPendingBranches.size() > 0 ? _dlgPendingBranches[_dlgPendingBranches.size() - 1] : NULL); + _responsesGame.add(r); + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::gameResponseUsed(int id) { + char *context = _dlgPendingBranches.size() > 0 ? _dlgPendingBranches[_dlgPendingBranches.size() - 1] : NULL; + for (uint32 i = 0; i < _responsesGame.size(); i++) { + AdResponseContext *respContext = _responsesGame[i]; + if (respContext->_id == id) { + if ((context == NULL && respContext->_context == NULL) || ((context != NULL && respContext->_context != NULL) && scumm_stricmp(context, respContext->_context) == 0)) { + return true; + } + } + } + return false; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::resetResponse(int id) { + char *context = _dlgPendingBranches.size() > 0 ? _dlgPendingBranches[_dlgPendingBranches.size() - 1] : NULL; + + for (uint32 i = 0; i < _responsesGame.size(); i++) { + if (_responsesGame[i]->_id == id) { + if ((context == NULL && _responsesGame[i]->_context == NULL) || scumm_stricmp(context, _responsesGame[i]->_context) == 0) { + delete _responsesGame[i]; + _responsesGame.remove_at(i); + break; + } + } + } + + for (uint32 i = 0; i < _responsesBranch.size(); i++) { + if (_responsesBranch[i]->_id == id) { + if ((context == NULL && _responsesBranch[i]->_context == NULL) || scumm_stricmp(context, _responsesBranch[i]->_context) == 0) { + delete _responsesBranch[i]; + _responsesBranch.remove_at(i); + break; + } + } + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::displayContent(bool doUpdate, bool displayAll) { + // init + if (doUpdate) { + initLoop(); + } + + // fill black + _renderer->fill(0, 0, 0); + if (!_editorMode) { + _renderer->setScreenViewport(); + } + + // playing exclusive video? + if (_videoPlayer->isPlaying()) { + if (doUpdate) { + _videoPlayer->update(); + } + _videoPlayer->display(); + } else if (_theoraPlayer) { + if (_theoraPlayer->isPlaying()) { + if (doUpdate) { + _theoraPlayer->update(); + } + _theoraPlayer->display(); + } + if (_theoraPlayer->isFinished()) { + delete _theoraPlayer; + _theoraPlayer = NULL; + } + } else { + + // process scripts + if (doUpdate) { + _scEngine->tick(); + } + + Point32 p; + getMousePos(&p); + + _scene->update(); + _scene->display(); + + + // display in-game windows + displayWindows(true); + if (_inventoryBox) { + _inventoryBox->display(); + } + if (_stateEx == GAME_WAITING_RESPONSE) { + _responseBox->display(); + } + _renderer->displayIndicator(); + + + if (doUpdate || displayAll) { + // display normal windows + displayWindows(false); + + setActiveObject(_gameRef->_renderer->getObjectAt(p.x, p.y)); + + // textual info + displaySentences(_state == GAME_FROZEN); + + showCursor(); + + if (_fader) { + _fader->display(); + } + _transMgr->update(); + } + + } + if (_loadingIcon) { + _loadingIcon->display(_loadingIconX, _loadingIconY); + if (!_loadingIconPersistent) { + delete _loadingIcon; + _loadingIcon = NULL; + } + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool AdGame::registerInventory(AdInventory *inv) { + for (uint32 i = 0; i < _inventories.size(); i++) { + if (_inventories[i] == inv) { + return STATUS_OK; + } + } + registerObject(inv); + _inventories.add(inv); + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool AdGame::unregisterInventory(AdInventory *inv) { + for (uint32 i = 0; i < _inventories.size(); i++) { + if (_inventories[i] == inv) { + unregisterObject(_inventories[i]); + _inventories.remove_at(i); + return STATUS_OK; + } + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool AdGame::isItemTaken(char *itemName) { + for (uint32 i = 0; i < _inventories.size(); i++) { + AdInventory *inv = _inventories[i]; + + for (uint32 j = 0; j < inv->_takenItems.size(); j++) { + if (scumm_stricmp(itemName, inv->_takenItems[j]->getName()) == 0) { + return true; + } + } + } + return false; +} + +////////////////////////////////////////////////////////////////////////// +AdItem *AdGame::getItemByName(const char *name) { + for (uint32 i = 0; i < _items.size(); i++) { + if (scumm_stricmp(_items[i]->getName(), name) == 0) { + return _items[i]; + } + } + return NULL; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::addItem(AdItem *item) { + _items.add(item); + return _gameRef->registerObject(item); +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::resetContent() { + // clear pending dialogs + for (uint32 i = 0; i < _dlgPendingBranches.size(); i++) { + delete[] _dlgPendingBranches[i]; + } + _dlgPendingBranches.clear(); + + + // clear inventories + for (uint32 i = 0; i < _inventories.size(); i++) { + _inventories[i]->_takenItems.clear(); + } + + // clear scene states + for (uint32 i = 0; i < _sceneStates.size(); i++) { + delete _sceneStates[i]; + } + _sceneStates.clear(); + + // clear once responses + for (uint32 i = 0; i < _responsesBranch.size(); i++) { + delete _responsesBranch[i]; + } + _responsesBranch.clear(); + + // clear once game responses + for (uint32 i = 0; i < _responsesGame.size(); i++) { + delete _responsesGame[i]; + } + _responsesGame.clear(); + + // reload inventory items + if (_itemsFile) { + loadItemsFile(_itemsFile); + } + + _tempDisableSaveState = true; + + return BaseGame::resetContent(); +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::deleteItem(AdItem *item) { + if (!item) { + return STATUS_FAILED; + } + + if (_selectedItem == item) { + _selectedItem = NULL; + } + _scene->handleItemAssociations(item->getName(), false); + + // remove from all inventories + for (uint32 i = 0; i < _inventories.size(); i++) { + _inventories[i]->removeItem(item); + } + + // remove object + for (uint32 i = 0; i < _items.size(); i++) { + if (_items[i] == item) { + unregisterObject(_items[i]); + _items.remove_at(i); + break; + } + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::addSpeechDir(const char *dir) { + if (!dir || dir[0] == '\0') { + return STATUS_FAILED; + } + + char *temp = new char[strlen(dir) + 2]; + strcpy(temp, dir); + if (temp[strlen(temp) - 1] != '\\' && temp[strlen(temp) - 1] != '/') { + strcat(temp, "\\"); + } + + for (uint32 i = 0; i < _speechDirs.size(); i++) { + if (scumm_stricmp(_speechDirs[i], temp) == 0) { + delete[] temp; + return STATUS_OK; + } + } + _speechDirs.add(temp); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::removeSpeechDir(const char *dir) { + if (!dir || dir[0] == '\0') { + return STATUS_FAILED; + } + + char *temp = new char[strlen(dir) + 2]; + strcpy(temp, dir); + if (temp[strlen(temp) - 1] != '\\' && temp[strlen(temp) - 1] != '/') { + strcat(temp, "\\"); + } + + bool found = false; + for (uint32 i = 0; i < _speechDirs.size(); i++) { + if (scumm_stricmp(_speechDirs[i], temp) == 0) { + delete[] _speechDirs[i]; + _speechDirs.remove_at(i); + found = true; + break; + } + } + delete[] temp; + + return found; +} + + +////////////////////////////////////////////////////////////////////////// +char *AdGame::findSpeechFile(char *stringID) { + char *ret = new char[MAX_PATH_LENGTH]; + + for (uint32 i = 0; i < _speechDirs.size(); i++) { + sprintf(ret, "%s%s.ogg", _speechDirs[i], stringID); + if (BaseFileManager::getEngineInstance()->hasFile(ret)) { + return ret; + } + + sprintf(ret, "%s%s.wav", _speechDirs[i], stringID); + if (BaseFileManager::getEngineInstance()->hasFile(ret)) { + return ret; + } + } + delete[] ret; + return NULL; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::validMouse() { + Point32 pos; + BasePlatform::getCursorPos(&pos); + + return _renderer->pointInViewport(&pos); +} + +////////////////////////////////////////////////////////////////////////// +bool AdGame::onMouseLeftDown() { + if (!validMouse()) { + return STATUS_OK; + } + if (_state == GAME_RUNNING && !_interactive) { + if (_talkSkipButton == TALK_SKIP_LEFT || _talkSkipButton == TALK_SKIP_BOTH) { + finishSentences(); + } + return STATUS_OK; + } + + if (_activeObject) { + _activeObject->handleMouse(MOUSE_CLICK, MOUSE_BUTTON_LEFT); + } + + bool handled = _state == GAME_RUNNING && DID_SUCCEED(applyEvent("LeftClick")); + if (!handled) { + if (_activeObject != NULL) { + _activeObject->applyEvent("LeftClick"); + } else if (_state == GAME_RUNNING && _scene && _scene->pointInViewport(_mousePos.x, _mousePos.y)) { + _scene->applyEvent("LeftClick"); + } + } + + if (_activeObject != NULL) { + _gameRef->_capturedObject = _gameRef->_activeObject; + } + _mouseLeftDown = true; + BasePlatform::setCapture(/*_renderer->_window*/); + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool AdGame::onMouseLeftUp() { + if (_activeObject) { + _activeObject->handleMouse(MOUSE_RELEASE, MOUSE_BUTTON_LEFT); + } + + BasePlatform::releaseCapture(); + _capturedObject = NULL; + _mouseLeftDown = false; + + bool handled = /*_state==GAME_RUNNING &&*/ DID_SUCCEED(applyEvent("LeftRelease")); + if (!handled) { + if (_activeObject != NULL) { + _activeObject->applyEvent("LeftRelease"); + } else if (_state == GAME_RUNNING && _scene && _scene->pointInViewport(_mousePos.x, _mousePos.y)) { + _scene->applyEvent("LeftRelease"); + } + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool AdGame::onMouseLeftDblClick() { + if (!validMouse()) { + return STATUS_OK; + } + + if (_state == GAME_RUNNING && !_interactive) { + return STATUS_OK; + } + + if (_activeObject) { + _activeObject->handleMouse(MOUSE_DBLCLICK, MOUSE_BUTTON_LEFT); + } + + bool handled = _state == GAME_RUNNING && DID_SUCCEED(applyEvent("LeftDoubleClick")); + if (!handled) { + if (_activeObject != NULL) { + _activeObject->applyEvent("LeftDoubleClick"); + } else if (_state == GAME_RUNNING && _scene && _scene->pointInViewport(_mousePos.x, _mousePos.y)) { + _scene->applyEvent("LeftDoubleClick"); + } + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool AdGame::onMouseRightDown() { + if (!validMouse()) { + return STATUS_OK; + } + if (_state == GAME_RUNNING && !_interactive) { + if (_talkSkipButton == TALK_SKIP_RIGHT || _talkSkipButton == TALK_SKIP_BOTH) { + finishSentences(); + } + return STATUS_OK; + } + + if ((_state == GAME_RUNNING && !_interactive) || _stateEx == GAME_WAITING_RESPONSE) { + return STATUS_OK; + } + + if (_activeObject) { + _activeObject->handleMouse(MOUSE_CLICK, MOUSE_BUTTON_RIGHT); + } + + bool handled = _state == GAME_RUNNING && DID_SUCCEED(applyEvent("RightClick")); + if (!handled) { + if (_activeObject != NULL) { + _activeObject->applyEvent("RightClick"); + } else if (_state == GAME_RUNNING && _scene && _scene->pointInViewport(_mousePos.x, _mousePos.y)) { + _scene->applyEvent("RightClick"); + } + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool AdGame::onMouseRightUp() { + if (_activeObject) { + _activeObject->handleMouse(MOUSE_RELEASE, MOUSE_BUTTON_RIGHT); + } + + bool handled = _state == GAME_RUNNING && DID_SUCCEED(applyEvent("RightRelease")); + if (!handled) { + if (_activeObject != NULL) { + _activeObject->applyEvent("RightRelease"); + } else if (_state == GAME_RUNNING && _scene && _scene->pointInViewport(_mousePos.x, _mousePos.y)) { + _scene->applyEvent("RightRelease"); + } + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool AdGame::displayDebugInfo() { + char str[100]; + if (_gameRef->_debugDebugMode) { + sprintf(str, "Mouse: %d, %d (scene: %d, %d)", _mousePos.x, _mousePos.y, _mousePos.x + _scene->getOffsetLeft(), _mousePos.y + _scene->getOffsetTop()); + _systemFont->drawText((byte *)str, 0, 90, _renderer->_width, TAL_RIGHT); + + sprintf(str, "Scene: %s (prev: %s)", (_scene && _scene->getName()) ? _scene->getName() : "???", _prevSceneName ? _prevSceneName : "???"); + _systemFont->drawText((byte *)str, 0, 110, _renderer->_width, TAL_RIGHT); + } + return BaseGame::displayDebugInfo(); +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::onScriptShutdown(ScScript *script) { + if (_responseBox && _responseBox->_waitingScript == script) { + _responseBox->_waitingScript = NULL; + } + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_game.h b/engines/wintermute/ad/ad_game.h new file mode 100644 index 0000000000..81c79a3da8 --- /dev/null +++ b/engines/wintermute/ad/ad_game.h @@ -0,0 +1,163 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ +#ifndef WINTERMUTE_ADGAME_H +#define WINTERMUTE_ADGAME_H + +#include "engines/wintermute/ad/ad_types.h" +#include "engines/wintermute/base/base_game.h" + +namespace Wintermute { +class AdItem; +class AdInventory; +class AdSceneState; +class AdScene; +class AdItem; +class AdObject; +class AdSentence; +class AdInventoryBox; +class AdResponseContext; +class AdResponseBox; +class AdGame : public BaseGame { +public: + virtual bool onScriptShutdown(ScScript *script); + + virtual bool onMouseLeftDown(); + virtual bool onMouseLeftUp(); + virtual bool onMouseLeftDblClick(); + virtual bool onMouseRightDown(); + virtual bool onMouseRightUp(); + + virtual bool displayDebugInfo(); + + bool addSpeechDir(const char *dir); + bool removeSpeechDir(const char *dir); + char *findSpeechFile(char *StringID); + + bool deleteItem(AdItem *Item); + char *_itemsFile; + bool _tempDisableSaveState; + virtual bool resetContent(); + bool addItem(AdItem *item); + AdItem *getItemByName(const char *name); + + AdObject *_inventoryOwner; + bool isItemTaken(char *itemName); + bool registerInventory(AdInventory *inv); + bool unregisterInventory(AdInventory *inv); + virtual bool displayContent(bool update = true, bool displayAll = false); + + bool gameResponseUsed(int ID); + bool addGameResponse(int ID); + bool resetResponse(int ID); + + bool branchResponseUsed(int ID); + bool addBranchResponse(int ID); + bool clearBranchResponses(char *name); + bool startDlgBranch(const char *branchName, const char *scriptName, const char *eventName); + bool endDlgBranch(const char *branchName, const char *scriptName, const char *eventName); + virtual bool windowLoadHook(UIWindow *win, char **buf, char **params); + virtual bool windowScriptMethodHook(UIWindow *win, ScScript *script, ScStack *stack, const char *name); + + AdSceneState *getSceneState(const char *filename, bool saving); + BaseViewport *_sceneViewport; + + int _texItemLifeTime; + int _texWalkLifeTime; + int _texStandLifeTime; + int _texTalkLifeTime; + + TTalkSkipButton _talkSkipButton; + + virtual bool getVersion(byte *verMajor, byte *verMinor, byte *extMajor, byte *extMinor); + bool scheduleChangeScene(const char *filename, bool fadeIn); + void setPrevSceneName(const char *name); + void setPrevSceneFilename(const char *name); + + AdItem *_selectedItem; + bool cleanup(); + DECLARE_PERSISTENT(AdGame, BaseGame) + + void finishSentences(); + bool showCursor(); + + TGameStateEx _stateEx; + + bool displaySentences(bool frozen); + void addSentence(AdSentence *sentence); + bool changeScene(const char *filename, bool fadeIn); + bool removeObject(AdObject *object); + bool addObject(AdObject *object); + AdScene *_scene; + bool initLoop(); + AdGame(const Common::String &gameId); + virtual ~AdGame(); + + BaseArray<AdObject *> _objects; + + virtual bool loadFile(const char *filename); + virtual bool loadBuffer(byte *buffer, bool complete = true); + + bool loadItemsFile(const char *filename, bool merge = false); + bool loadItemsBuffer(byte *buffer, bool merge = false); + + // scripting interface + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + bool validMouse(); +private: + virtual bool externalCall(ScScript *script, ScStack *stack, ScStack *thisStack, char *name); + + AdObject *_invObject; + BaseArray<AdInventory *> _inventories; + char *_scheduledScene; + bool _scheduledFadeIn; + char *_prevSceneName; + char *_prevSceneFilename; + char *_debugStartupScene; + char *_startupScene; + bool _initialScene; + bool _smartItemCursor; + BaseArray<char *> _speechDirs; + BaseArray<AdItem *> _items; + + BaseArray<AdSentence *> _sentences; + + BaseArray<AdSceneState *> _sceneStates; + BaseArray<char *> _dlgPendingBranches; + + BaseArray<AdResponseContext *> _responsesBranch; + BaseArray<AdResponseContext *> _responsesGame; + + AdResponseBox *_responseBox; + AdInventoryBox *_inventoryBox; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_inventory.cpp b/engines/wintermute/ad/ad_inventory.cpp new file mode 100644 index 0000000000..72f8fa0fb4 --- /dev/null +++ b/engines/wintermute/ad/ad_inventory.cpp @@ -0,0 +1,136 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_inventory.h" +#include "engines/wintermute/ad/ad_game.h" +#include "engines/wintermute/ad/ad_item.h" +#include "engines/wintermute/platform_osystem.h" +#include "common/str.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdInventory, false) + +////////////////////////////////////////////////////////////////////////// +AdInventory::AdInventory(BaseGame *inGame) : BaseObject(inGame) { + _scrollOffset = 0; +} + + +////////////////////////////////////////////////////////////////////////// +AdInventory::~AdInventory() { + _takenItems.clear(); // ref only +} + + +////////////////////////////////////////////////////////////////////////// +bool AdInventory::insertItem(const char *name, const char *insertAfter) { + if (name == NULL) { + return STATUS_FAILED; + } + + AdItem *item = ((AdGame *)_gameRef)->getItemByName(name); + if (item == NULL) { + return STATUS_FAILED; + } + + int insertIndex = -1; + for (uint32 i = 0; i < _takenItems.size(); i++) { + if (scumm_stricmp(_takenItems[i]->getName(), name) == 0) { + _takenItems.remove_at(i); + i--; + continue; + } + if (insertAfter && scumm_stricmp(_takenItems[i]->getName(), insertAfter) == 0) { + insertIndex = i + 1; + } + } + + + if (insertIndex == -1) { + _takenItems.add(item); + } else { + _takenItems.insert_at(insertIndex, item); + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdInventory::removeItem(const char *name) { + if (name == NULL) { + return STATUS_FAILED; + } + + for (uint32 i = 0; i < _takenItems.size(); i++) { + if (scumm_stricmp(_takenItems[i]->getName(), name) == 0) { + if (((AdGame *)_gameRef)->_selectedItem == _takenItems[i]) { + ((AdGame *)_gameRef)->_selectedItem = NULL; + } + _takenItems.remove_at(i); + return STATUS_OK; + } + } + + return STATUS_FAILED; +} + + + +////////////////////////////////////////////////////////////////////////// +bool AdInventory::removeItem(AdItem *item) { + if (item == NULL) { + return STATUS_FAILED; + } + + for (uint32 i = 0; i < _takenItems.size(); i++) { + if (_takenItems[i] == item) { + if (((AdGame *)_gameRef)->_selectedItem == _takenItems[i]) { + ((AdGame *)_gameRef)->_selectedItem = NULL; + } + _takenItems.remove_at(i); + return STATUS_OK; + } + } + + return STATUS_FAILED; +} + +////////////////////////////////////////////////////////////////////////// +bool AdInventory::persist(BasePersistenceManager *persistMgr) { + + BaseObject::persist(persistMgr); + + _takenItems.persist(persistMgr); + persistMgr->transfer(TMEMBER(_scrollOffset)); + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_inventory.h b/engines/wintermute/ad/ad_inventory.h new file mode 100644 index 0000000000..4017d914bc --- /dev/null +++ b/engines/wintermute/ad/ad_inventory.h @@ -0,0 +1,52 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADINVENTORY_H +#define WINTERMUTE_ADINVENTORY_H + +#include "engines/wintermute/base/base_object.h" + +namespace Wintermute { + +class AdItem; + +class AdInventory : public BaseObject { +public: + DECLARE_PERSISTENT(AdInventory, BaseObject) + bool removeItem(const char *name); + bool removeItem(AdItem *Item); + bool insertItem(const char *name, const char *insertAfter = NULL); + AdInventory(BaseGame *inGame); + virtual ~AdInventory(); + BaseArray<AdItem *> _takenItems; + int _scrollOffset; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_inventory_box.cpp b/engines/wintermute/ad/ad_inventory_box.cpp new file mode 100644 index 0000000000..7ae8ff8d69 --- /dev/null +++ b/engines/wintermute/ad/ad_inventory_box.cpp @@ -0,0 +1,389 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_game.h" +#include "engines/wintermute/ad/ad_inventory_box.h" +#include "engines/wintermute/ad/ad_inventory.h" +#include "engines/wintermute/ad/ad_item.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/base_viewport.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/ui/ui_button.h" +#include "engines/wintermute/ui/ui_window.h" +#include "engines/wintermute/platform_osystem.h" +#include "common/str.h" +#include "common/rect.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdInventoryBox, false) + +////////////////////////////////////////////////////////////////////////// +AdInventoryBox::AdInventoryBox(BaseGame *inGame) : BaseObject(inGame) { + _itemsArea.setEmpty(); + _scrollOffset = 0; + _spacing = 0; + _itemWidth = _itemHeight = 50; + _scrollBy = 1; + + _window = NULL; + _closeButton = NULL; + + _hideSelected = false; + + _visible = false; + _exclusive = false; +} + + +////////////////////////////////////////////////////////////////////////// +AdInventoryBox::~AdInventoryBox() { + _gameRef->unregisterObject(_window); + _window = NULL; + + delete _closeButton; + _closeButton = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdInventoryBox::listen(BaseScriptHolder *param1, uint32 param2) { + UIObject *obj = (UIObject *)param1; + + switch (obj->_type) { + case UI_BUTTON: + if (scumm_stricmp(obj->getName(), "close") == 0) { + _visible = false; + } else if (scumm_stricmp(obj->getName(), "prev") == 0) { + _scrollOffset -= _scrollBy; + _scrollOffset = MAX(_scrollOffset, 0); + } else if (scumm_stricmp(obj->getName(), "next") == 0) { + _scrollOffset += _scrollBy; + } else { + return BaseObject::listen(param1, param2); + } + break; + default: + error("AdInventoryBox::Listen - Unhandled enum"); + break; + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdInventoryBox::display() { + AdGame *adGame = (AdGame *)_gameRef; + + if (!_visible) { + return STATUS_OK; + } + + int itemsX, itemsY; + itemsX = (int)floor((float)((_itemsArea.right - _itemsArea.left + _spacing) / (_itemWidth + _spacing))); + itemsY = (int)floor((float)((_itemsArea.bottom - _itemsArea.top + _spacing) / (_itemHeight + _spacing))); + + if (_window) { + _window->enableWidget("prev", _scrollOffset > 0); + _window->enableWidget("next", _scrollOffset + itemsX * itemsY < (int32)adGame->_inventoryOwner->getInventory()->_takenItems.size()); + } + + + if (_closeButton) { + _closeButton->_posX = _closeButton->_posY = 0; + _closeButton->_width = _gameRef->_renderer->_width; + _closeButton->_height = _gameRef->_renderer->_height; + + _closeButton->display(); + } + + + // display window + Rect32 rect = _itemsArea; + if (_window) { + rect.offsetRect(_window->_posX, _window->_posY); + _window->display(); + } + + // display items + if (_window && _window->_alphaColor != 0) { + _gameRef->_renderer->_forceAlphaColor = _window->_alphaColor; + } + int yyy = rect.top; + for (int j = 0; j < itemsY; j++) { + int xxx = rect.left; + for (int i = 0; i < itemsX; i++) { + int itemIndex = _scrollOffset + j * itemsX + i; + if (itemIndex >= 0 && itemIndex < (int32)adGame->_inventoryOwner->getInventory()->_takenItems.size()) { + AdItem *item = adGame->_inventoryOwner->getInventory()->_takenItems[itemIndex]; + if (item != ((AdGame *)_gameRef)->_selectedItem || !_hideSelected) { + item->update(); + item->display(xxx, yyy); + } + } + + xxx += (_itemWidth + _spacing); + } + yyy += (_itemHeight + _spacing); + } + if (_window && _window->_alphaColor != 0) { + _gameRef->_renderer->_forceAlphaColor = 0; + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdInventoryBox::loadFile(const char *filename) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "AdInventoryBox::LoadFile failed for file '%s'", filename); + return STATUS_FAILED; + } + + bool ret; + + setFilename(filename); + + if (DID_FAIL(ret = loadBuffer(buffer, true))) { + _gameRef->LOG(0, "Error parsing INVENTORY_BOX file '%s'", filename); + } + + + delete[] buffer; + + return ret; +} + + +TOKEN_DEF_START +TOKEN_DEF(INVENTORY_BOX) +TOKEN_DEF(TEMPLATE) +TOKEN_DEF(WINDOW) +TOKEN_DEF(EXCLUSIVE) +TOKEN_DEF(ALWAYS_VISIBLE) +TOKEN_DEF(AREA) +TOKEN_DEF(SPACING) +TOKEN_DEF(ITEM_WIDTH) +TOKEN_DEF(ITEM_HEIGHT) +TOKEN_DEF(SCROLL_BY) +TOKEN_DEF(NAME) +TOKEN_DEF(CAPTION) +TOKEN_DEF(HIDE_SELECTED) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool AdInventoryBox::loadBuffer(byte *buffer, bool complete) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(INVENTORY_BOX) + TOKEN_TABLE(TEMPLATE) + TOKEN_TABLE(WINDOW) + TOKEN_TABLE(EXCLUSIVE) + TOKEN_TABLE(ALWAYS_VISIBLE) + TOKEN_TABLE(AREA) + TOKEN_TABLE(SPACING) + TOKEN_TABLE(ITEM_WIDTH) + TOKEN_TABLE(ITEM_HEIGHT) + TOKEN_TABLE(SCROLL_BY) + TOKEN_TABLE(NAME) + TOKEN_TABLE(CAPTION) + TOKEN_TABLE(HIDE_SELECTED) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE_END + + byte *params; + int cmd = 2; + BaseParser parser; + bool alwaysVisible = false; + + _exclusive = false; + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_INVENTORY_BOX) { + _gameRef->LOG(0, "'INVENTORY_BOX' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + while (cmd > 0 && (cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_TEMPLATE: + if (DID_FAIL(loadFile((char *)params))) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_NAME: + setName((char *)params); + break; + + case TOKEN_CAPTION: + setCaption((char *)params); + break; + + case TOKEN_WINDOW: + delete _window; + _window = new UIWindow(_gameRef); + if (!_window || DID_FAIL(_window->loadBuffer(params, false))) { + delete _window; + _window = NULL; + cmd = PARSERR_GENERIC; + } else { + _gameRef->registerObject(_window); + } + break; + + case TOKEN_AREA: + parser.scanStr((char *)params, "%d,%d,%d,%d", &_itemsArea.left, &_itemsArea.top, &_itemsArea.right, &_itemsArea.bottom); + break; + + case TOKEN_EXCLUSIVE: + parser.scanStr((char *)params, "%b", &_exclusive); + break; + + case TOKEN_HIDE_SELECTED: + parser.scanStr((char *)params, "%b", &_hideSelected); + break; + + case TOKEN_ALWAYS_VISIBLE: + parser.scanStr((char *)params, "%b", &alwaysVisible); + break; + + case TOKEN_SPACING: + parser.scanStr((char *)params, "%d", &_spacing); + break; + + case TOKEN_ITEM_WIDTH: + parser.scanStr((char *)params, "%d", &_itemWidth); + break; + + case TOKEN_ITEM_HEIGHT: + parser.scanStr((char *)params, "%d", &_itemHeight); + break; + + case TOKEN_SCROLL_BY: + parser.scanStr((char *)params, "%d", &_scrollBy); + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty(params, false); + break; + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in INVENTORY_BOX definition"); + return STATUS_FAILED; + } + if (cmd == PARSERR_GENERIC) { + _gameRef->LOG(0, "Error loading INVENTORY_BOX definition"); + return STATUS_FAILED; + } + + if (_exclusive) { + delete _closeButton; + _closeButton = new UIButton(_gameRef); + if (_closeButton) { + _closeButton->setName("close"); + _closeButton->setListener(this, _closeButton, 0); + _closeButton->_parent = _window; + } + } + + _visible = alwaysVisible; + + if (_window) { + for (uint32 i = 0; i < _window->_widgets.size(); i++) { + if (!_window->_widgets[i]->_listenerObject) { + _window->_widgets[i]->setListener(this, _window->_widgets[i], 0); + } + } + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool AdInventoryBox::saveAsText(BaseDynamicBuffer *buffer, int indent) { + buffer->putTextIndent(indent, "INVENTORY_BOX\n"); + buffer->putTextIndent(indent, "{\n"); + + buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", getName()); + buffer->putTextIndent(indent + 2, "CAPTION=\"%s\"\n", getCaption()); + + buffer->putTextIndent(indent + 2, "AREA { %d, %d, %d, %d }\n", _itemsArea.left, _itemsArea.top, _itemsArea.right, _itemsArea.bottom); + + buffer->putTextIndent(indent + 2, "EXCLUSIVE=%s\n", _exclusive ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "HIDE_SELECTED=%s\n", _hideSelected ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "ALWAYS_VISIBLE=%s\n", _visible ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "SPACING=%d\n", _spacing); + buffer->putTextIndent(indent + 2, "ITEM_WIDTH=%d\n", _itemWidth); + buffer->putTextIndent(indent + 2, "ITEM_HEIGHT=%d\n", _itemHeight); + buffer->putTextIndent(indent + 2, "SCROLL_BY=%d\n", _scrollBy); + + buffer->putTextIndent(indent + 2, "\n"); + + // window + if (_window) { + _window->saveAsText(buffer, indent + 2); + } + + buffer->putTextIndent(indent + 2, "\n"); + + // editor properties + BaseClass::saveAsText(buffer, indent + 2); + + buffer->putTextIndent(indent, "}\n"); + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdInventoryBox::persist(BasePersistenceManager *persistMgr) { + BaseObject::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_closeButton)); + persistMgr->transfer(TMEMBER(_hideSelected)); + persistMgr->transfer(TMEMBER(_itemHeight)); + persistMgr->transfer(TMEMBER(_itemsArea)); + persistMgr->transfer(TMEMBER(_itemWidth)); + persistMgr->transfer(TMEMBER(_scrollBy)); + persistMgr->transfer(TMEMBER(_scrollOffset)); + persistMgr->transfer(TMEMBER(_spacing)); + persistMgr->transfer(TMEMBER(_visible)); + persistMgr->transfer(TMEMBER(_window)); + persistMgr->transfer(TMEMBER(_exclusive)); + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_inventory_box.h b/engines/wintermute/ad/ad_inventory_box.h new file mode 100644 index 0000000000..cb6d084562 --- /dev/null +++ b/engines/wintermute/ad/ad_inventory_box.h @@ -0,0 +1,65 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADINVENTORYBOX_H +#define WINTERMUTE_ADINVENTORYBOX_H + +#include "engines/wintermute/base/base_object.h" +#include "common/rect.h" + +namespace Wintermute { +class UIButton; +class UIWindow; + +class AdInventoryBox : public BaseObject { +public: + bool _hideSelected; + DECLARE_PERSISTENT(AdInventoryBox, BaseObject) + bool _visible; + virtual bool display(); + UIButton *_closeButton; + int _spacing; + int _scrollOffset; + Rect32 _itemsArea; + bool listen(BaseScriptHolder *param1, uint32 param2); + UIWindow *_window; + AdInventoryBox(BaseGame *inGame); + virtual ~AdInventoryBox(); + bool loadFile(const char *filename); + bool loadBuffer(byte *buffer, bool complete = true); + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent); +private: + bool _exclusive; + int _scrollBy; + int _itemHeight; + int _itemWidth; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_item.cpp b/engines/wintermute/ad/ad_item.cpp new file mode 100644 index 0000000000..427b1c7db4 --- /dev/null +++ b/engines/wintermute/ad/ad_item.cpp @@ -0,0 +1,813 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_item.h" +#include "engines/wintermute/ad/ad_game.h" +#include "engines/wintermute/ad/ad_sentence.h" +#include "engines/wintermute/base/font/base_font_storage.h" +#include "engines/wintermute/base/font/base_font.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/sound/base_sound.h" +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/utils/utils.h" +#include "engines/wintermute/platform_osystem.h" +#include "common/str.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdItem, false) + +////////////////////////////////////////////////////////////////////////// +AdItem::AdItem(BaseGame *inGame) : AdTalkHolder(inGame) { + _spriteHover = NULL; + _cursorNormal = _cursorHover = NULL; + + _cursorCombined = true; + _inInventory = false; + + _displayAmount = false; + _amount = 0; + _amountOffsetX = 0; + _amountOffsetY = 0; + _amountAlign = TAL_RIGHT; + _amountString = NULL; + + _state = STATE_READY; + + _movable = false; +} + + +////////////////////////////////////////////////////////////////////////// +AdItem::~AdItem() { + delete _spriteHover; + delete _cursorNormal; + delete _cursorHover; + _spriteHover = NULL; + _cursorNormal = NULL; + _cursorHover = NULL; + + delete[] _amountString; + _amountString = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdItem::loadFile(const char *filename) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "AdItem::LoadFile failed for file '%s'", filename); + return STATUS_FAILED; + } + + bool ret; + + setFilename(filename); + + if (DID_FAIL(ret = loadBuffer(buffer, true))) { + _gameRef->LOG(0, "Error parsing ITEM file '%s'", filename); + } + + + delete[] buffer; + + return ret; +} + + +TOKEN_DEF_START +TOKEN_DEF(ITEM) +TOKEN_DEF(TEMPLATE) +TOKEN_DEF(CURSOR_HOVER) +TOKEN_DEF(CURSOR_COMBINED) +TOKEN_DEF(CURSOR) +TOKEN_DEF(NAME) +TOKEN_DEF(IMAGE_HOVER) +TOKEN_DEF(IMAGE) +TOKEN_DEF(EVENTS) +TOKEN_DEF(SCRIPT) +TOKEN_DEF(CAPTION) +TOKEN_DEF(PROPERTY) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF(FONT) +TOKEN_DEF(ALPHA_COLOR) +TOKEN_DEF(ALPHA) +TOKEN_DEF(TALK_SPECIAL) +TOKEN_DEF(TALK) +TOKEN_DEF(SPRITE_HOVER) +TOKEN_DEF(SPRITE) +TOKEN_DEF(DISPLAY_AMOUNT) +TOKEN_DEF(AMOUNT_OFFSET_X) +TOKEN_DEF(AMOUNT_OFFSET_Y) +TOKEN_DEF(AMOUNT_ALIGN) +TOKEN_DEF(AMOUNT_STRING) +TOKEN_DEF(AMOUNT) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool AdItem::loadBuffer(byte *buffer, bool complete) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(ITEM) + TOKEN_TABLE(TEMPLATE) + TOKEN_TABLE(CURSOR_HOVER) + TOKEN_TABLE(CURSOR_COMBINED) + TOKEN_TABLE(CURSOR) + TOKEN_TABLE(NAME) + TOKEN_TABLE(IMAGE_HOVER) + TOKEN_TABLE(IMAGE) + TOKEN_TABLE(EVENTS) + TOKEN_TABLE(SCRIPT) + TOKEN_TABLE(CAPTION) + TOKEN_TABLE(PROPERTY) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE(FONT) + TOKEN_TABLE(ALPHA_COLOR) + TOKEN_TABLE(ALPHA) + TOKEN_TABLE(TALK_SPECIAL) + TOKEN_TABLE(TALK) + TOKEN_TABLE(SPRITE_HOVER) + TOKEN_TABLE(SPRITE) + TOKEN_TABLE(DISPLAY_AMOUNT) + TOKEN_TABLE(AMOUNT_OFFSET_X) + TOKEN_TABLE(AMOUNT_OFFSET_Y) + TOKEN_TABLE(AMOUNT_ALIGN) + TOKEN_TABLE(AMOUNT_STRING) + TOKEN_TABLE(AMOUNT) + TOKEN_TABLE_END + + byte *params; + int cmd = 2; + BaseParser parser; + + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_ITEM) { + _gameRef->LOG(0, "'ITEM' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + int ar = 0, ag = 0, ab = 0, alpha = 255; + while (cmd > 0 && (cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_TEMPLATE: + if (DID_FAIL(loadFile((char *)params))) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_NAME: + setName((char *)params); + break; + + case TOKEN_FONT: + setFont((char *)params); + break; + + case TOKEN_CAPTION: + setCaption((char *)params); + break; + + case TOKEN_IMAGE: + case TOKEN_SPRITE: + delete _sprite; + _sprite = new BaseSprite(_gameRef, this); + if (!_sprite || DID_FAIL(_sprite->loadFile((char *)params, ((AdGame *)_gameRef)->_texItemLifeTime))) { + delete _sprite; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_IMAGE_HOVER: + case TOKEN_SPRITE_HOVER: + delete _spriteHover; + _spriteHover = new BaseSprite(_gameRef, this); + if (!_spriteHover || DID_FAIL(_spriteHover->loadFile((char *)params, ((AdGame *)_gameRef)->_texItemLifeTime))) { + delete _spriteHover; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_AMOUNT: + parser.scanStr((char *)params, "%d", &_amount); + break; + + case TOKEN_DISPLAY_AMOUNT: + parser.scanStr((char *)params, "%b", &_displayAmount); + break; + + case TOKEN_AMOUNT_OFFSET_X: + parser.scanStr((char *)params, "%d", &_amountOffsetX); + break; + + case TOKEN_AMOUNT_OFFSET_Y: + parser.scanStr((char *)params, "%d", &_amountOffsetY); + break; + + case TOKEN_AMOUNT_ALIGN: + if (scumm_stricmp((char *)params, "left") == 0) { + _amountAlign = TAL_LEFT; + } else if (scumm_stricmp((char *)params, "right") == 0) { + _amountAlign = TAL_RIGHT; + } else { + _amountAlign = TAL_CENTER; + } + break; + + case TOKEN_AMOUNT_STRING: + BaseUtils::setString(&_amountString, (char *)params); + break; + + case TOKEN_TALK: { + BaseSprite *spr = new BaseSprite(_gameRef, this); + if (!spr || DID_FAIL(spr->loadFile((char *)params, ((AdGame *)_gameRef)->_texTalkLifeTime))) { + cmd = PARSERR_GENERIC; + } else { + _talkSprites.add(spr); + } + } + break; + + case TOKEN_TALK_SPECIAL: { + BaseSprite *spr = new BaseSprite(_gameRef, this); + if (!spr || DID_FAIL(spr->loadFile((char *)params, ((AdGame *)_gameRef)->_texTalkLifeTime))) { + cmd = PARSERR_GENERIC; + } else { + _talkSpritesEx.add(spr); + } + } + break; + + case TOKEN_CURSOR: + delete _cursorNormal; + _cursorNormal = new BaseSprite(_gameRef); + if (!_cursorNormal || DID_FAIL(_cursorNormal->loadFile((char *)params, ((AdGame *)_gameRef)->_texItemLifeTime))) { + delete _cursorNormal; + _cursorNormal = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_CURSOR_HOVER: + delete _cursorHover; + _cursorHover = new BaseSprite(_gameRef); + if (!_cursorHover || DID_FAIL(_cursorHover->loadFile((char *)params, ((AdGame *)_gameRef)->_texItemLifeTime))) { + delete _cursorHover; + _cursorHover = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_CURSOR_COMBINED: + parser.scanStr((char *)params, "%b", &_cursorCombined); + break; + + case TOKEN_SCRIPT: + addScript((char *)params); + break; + + case TOKEN_PROPERTY: + parseProperty(params, false); + break; + + case TOKEN_ALPHA_COLOR: + parser.scanStr((char *)params, "%d,%d,%d", &ar, &ag, &ab); + break; + + case TOKEN_ALPHA: + parser.scanStr((char *)params, "%d", &alpha); + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty(params, false); + break; + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in ITEM definition"); + return STATUS_FAILED; + } + if (cmd == PARSERR_GENERIC) { + _gameRef->LOG(0, "Error loading ITEM definition"); + return STATUS_FAILED; + } + + if (alpha != 0 && ar == 0 && ag == 0 && ab == 0) { + ar = ag = ab = 255; + } + _alphaColor = BYTETORGBA(ar, ag, ab, alpha); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdItem::update() { + _currentSprite = NULL; + + if (_state == STATE_READY && _animSprite) { + delete _animSprite; + _animSprite = NULL; + } + + // finished playing animation? + if (_state == STATE_PLAYING_ANIM && _animSprite != NULL && _animSprite->isFinished()) { + _state = STATE_READY; + _currentSprite = _animSprite; + } + + if (_sentence && _state != STATE_TALKING) { + _sentence->finish(); + } + + // default: stand animation + if (!_currentSprite) { + _currentSprite = _sprite; + } + + switch (_state) { + ////////////////////////////////////////////////////////////////////////// + case STATE_PLAYING_ANIM: + _currentSprite = _animSprite; + break; + + ////////////////////////////////////////////////////////////////////////// + case STATE_READY: + if (!_animSprite) { + if (_gameRef->_activeObject == this && _spriteHover) { + _currentSprite = _spriteHover; + } else { + _currentSprite = _sprite; + } + } + break; + + ////////////////////////////////////////////////////////////////////////// + case STATE_TALKING: { + _sentence->update(); + if (_sentence->_currentSprite) { + _tempSprite2 = _sentence->_currentSprite; + } + + bool timeIsUp = (_sentence->_sound && _sentence->_soundStarted && (!_sentence->_sound->isPlaying() && !_sentence->_sound->isPaused())) || (!_sentence->_sound && _sentence->_duration <= _gameRef->_timer - _sentence->_startTime); + if (_tempSprite2 == NULL || _tempSprite2->isFinished() || (/*_tempSprite2->_looping &&*/ timeIsUp)) { + if (timeIsUp) { + _sentence->finish(); + _tempSprite2 = NULL; + _state = STATE_READY; + } else { + _tempSprite2 = getTalkStance(_sentence->getNextStance()); + if (_tempSprite2) { + _tempSprite2->reset(); + _currentSprite = _tempSprite2; + } + ((AdGame *)_gameRef)->addSentence(_sentence); + } + } else { + _currentSprite = _tempSprite2; + ((AdGame *)_gameRef)->addSentence(_sentence); + } + } + default: + break; + } + _ready = (_state == STATE_READY); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdItem::display(int x, int y) { + int width = 0; + if (_currentSprite) { + Rect32 rc; + _currentSprite->getBoundingRect(&rc, 0, 0); + width = rc.width(); + } + + _posX = x + width / 2; + _posY = y; + + bool ret; + if (_currentSprite) { + ret = _currentSprite->draw(x, y, this, 100, 100, _alphaColor); + } else { + ret = STATUS_OK; + } + + if (_displayAmount) { + int amountX = x; + int amountY = y + _amountOffsetY; + + if (_amountAlign == TAL_RIGHT) { + width -= _amountOffsetX; + amountX -= _amountOffsetX; + } + amountX += _amountOffsetX; + + BaseFont *font = _font ? _font : _gameRef->_systemFont; + if (font) { + if (_amountString) { + font->drawText((byte *)_amountString, amountX, amountY, width, _amountAlign); + } else { + char str[256]; + sprintf(str, "%d", _amount); + font->drawText((byte *)str, amountX, amountY, width, _amountAlign); + } + } + } + + return ret; +} + + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool AdItem::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // SetHoverSprite + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "SetHoverSprite") == 0) { + stack->correctParams(1); + + bool setCurrent = false; + if (_currentSprite && _currentSprite == _spriteHover) { + setCurrent = true; + } + + const char *filename = stack->pop()->getString(); + + delete _spriteHover; + _spriteHover = NULL; + BaseSprite *spr = new BaseSprite(_gameRef, this); + if (!spr || DID_FAIL(spr->loadFile(filename))) { + stack->pushBool(false); + script->runtimeError("Item.SetHoverSprite failed for file '%s'", filename); + } else { + _spriteHover = spr; + if (setCurrent) { + _currentSprite = _spriteHover; + } + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetHoverSprite + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetHoverSprite") == 0) { + stack->correctParams(0); + + if (!_spriteHover || !_spriteHover->getFilename()) { + stack->pushNULL(); + } else { + stack->pushString(_spriteHover->getFilename()); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetHoverSpriteObject + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetHoverSpriteObject") == 0) { + stack->correctParams(0); + if (!_spriteHover) { + stack->pushNULL(); + } else { + stack->pushNative(_spriteHover, true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetNormalCursor + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "SetNormalCursor") == 0) { + stack->correctParams(1); + + const char *filename = stack->pop()->getString(); + + delete _cursorNormal; + _cursorNormal = NULL; + BaseSprite *spr = new BaseSprite(_gameRef); + if (!spr || DID_FAIL(spr->loadFile(filename))) { + stack->pushBool(false); + script->runtimeError("Item.SetNormalCursor failed for file '%s'", filename); + } else { + _cursorNormal = spr; + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetNormalCursor + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetNormalCursor") == 0) { + stack->correctParams(0); + + if (!_cursorNormal || !_cursorNormal->getFilename()) { + stack->pushNULL(); + } else { + stack->pushString(_cursorNormal->getFilename()); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetNormalCursorObject + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetNormalCursorObject") == 0) { + stack->correctParams(0); + + if (!_cursorNormal) { + stack->pushNULL(); + } else { + stack->pushNative(_cursorNormal, true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetHoverCursor + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "SetHoverCursor") == 0) { + stack->correctParams(1); + + const char *filename = stack->pop()->getString(); + + delete _cursorHover; + _cursorHover = NULL; + BaseSprite *spr = new BaseSprite(_gameRef); + if (!spr || DID_FAIL(spr->loadFile(filename))) { + stack->pushBool(false); + script->runtimeError("Item.SetHoverCursor failed for file '%s'", filename); + } else { + _cursorHover = spr; + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetHoverCursor + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetHoverCursor") == 0) { + stack->correctParams(0); + + if (!_cursorHover || !_cursorHover->getFilename()) { + stack->pushNULL(); + } else { + stack->pushString(_cursorHover->getFilename()); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetHoverCursorObject + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetHoverCursorObject") == 0) { + stack->correctParams(0); + + if (!_cursorHover) { + stack->pushNULL(); + } else { + stack->pushNative(_cursorHover, true); + } + return STATUS_OK; + } else { + return AdTalkHolder::scCallMethod(script, stack, thisStack, name); + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *AdItem::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("item"); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Name + ////////////////////////////////////////////////////////////////////////// + else if (name == "Name") { + _scValue->setString(getName()); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // DisplayAmount + ////////////////////////////////////////////////////////////////////////// + else if (name == "DisplayAmount") { + _scValue->setBool(_displayAmount); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Amount + ////////////////////////////////////////////////////////////////////////// + else if (name == "Amount") { + _scValue->setInt(_amount); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // AmountOffsetX + ////////////////////////////////////////////////////////////////////////// + else if (name == "AmountOffsetX") { + _scValue->setInt(_amountOffsetX); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // AmountOffsetY + ////////////////////////////////////////////////////////////////////////// + else if (name == "AmountOffsetY") { + _scValue->setInt(_amountOffsetY); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // AmountAlign + ////////////////////////////////////////////////////////////////////////// + else if (name == "AmountAlign") { + _scValue->setInt(_amountAlign); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // AmountString + ////////////////////////////////////////////////////////////////////////// + else if (name == "AmountString") { + if (!_amountString) { + _scValue->setNULL(); + } else { + _scValue->setString(_amountString); + } + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // CursorCombined + ////////////////////////////////////////////////////////////////////////// + else if (name == "CursorCombined") { + _scValue->setBool(_cursorCombined); + return _scValue; + } else { + return AdTalkHolder::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool AdItem::scSetProperty(const char *name, ScValue *value) { + ////////////////////////////////////////////////////////////////////////// + // Name + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Name") == 0) { + setName(value->getString()); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // DisplayAmount + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "DisplayAmount") == 0) { + _displayAmount = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Amount + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Amount") == 0) { + _amount = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // AmountOffsetX + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AmountOffsetX") == 0) { + _amountOffsetX = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // AmountOffsetY + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AmountOffsetY") == 0) { + _amountOffsetY = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // AmountAlign + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AmountAlign") == 0) { + _amountAlign = (TTextAlign)value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // AmountString + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AmountString") == 0) { + if (value->isNULL()) { + delete[] _amountString; + _amountString = NULL; + } else { + BaseUtils::setString(&_amountString, value->getString()); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // CursorCombined + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "CursorCombined") == 0) { + _cursorCombined = value->getBool(); + return STATUS_OK; + } else { + return AdTalkHolder::scSetProperty(name, value); + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *AdItem::scToString() { + return "[item]"; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdItem::persist(BasePersistenceManager *persistMgr) { + + AdTalkHolder::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_cursorCombined)); + persistMgr->transfer(TMEMBER(_cursorHover)); + persistMgr->transfer(TMEMBER(_cursorNormal)); + persistMgr->transfer(TMEMBER(_spriteHover)); + persistMgr->transfer(TMEMBER(_inInventory)); + persistMgr->transfer(TMEMBER(_displayAmount)); + persistMgr->transfer(TMEMBER(_amount)); + persistMgr->transfer(TMEMBER(_amountOffsetX)); + persistMgr->transfer(TMEMBER(_amountOffsetY)); + persistMgr->transfer(TMEMBER_INT(_amountAlign)); + persistMgr->transfer(TMEMBER(_amountString)); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdItem::getExtendedFlag(const char *flagName) { + if (!flagName) { + return false; + } else if (strcmp(flagName, "usable") == 0) { + return true; + } else { + return AdObject::getExtendedFlag(flagName); + } +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_item.h b/engines/wintermute/ad/ad_item.h new file mode 100644 index 0000000000..79978f9f72 --- /dev/null +++ b/engines/wintermute/ad/ad_item.h @@ -0,0 +1,69 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADITEM_H +#define WINTERMUTE_ADITEM_H + + +#include "engines/wintermute/ad/ad_talk_holder.h" + +namespace Wintermute { + +class AdItem : public AdTalkHolder { +public: + bool update(); + DECLARE_PERSISTENT(AdItem, AdTalkHolder) + bool display(int x, int y); + bool getExtendedFlag(const char *flagName); + bool _inInventory; + bool _cursorCombined; + BaseSprite *_spriteHover; + BaseSprite *_cursorNormal; + BaseSprite *_cursorHover; + AdItem(BaseGame *inGame); + virtual ~AdItem(); + bool loadFile(const char *filename); + bool loadBuffer(byte *buffer, bool complete = true); + + // scripting interface + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + virtual const char *scToString(); +private: + bool _displayAmount; + int _amount; + int _amountOffsetX; + int _amountOffsetY; + TTextAlign _amountAlign; + char *_amountString; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_layer.cpp b/engines/wintermute/ad/ad_layer.cpp new file mode 100644 index 0000000000..209c12b7a2 --- /dev/null +++ b/engines/wintermute/ad/ad_layer.cpp @@ -0,0 +1,564 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/ad/ad_layer.h" +#include "engines/wintermute/ad/ad_scene_node.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/platform_osystem.h" +#include "common/str.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdLayer, false) + +////////////////////////////////////////////////////////////////////////// +AdLayer::AdLayer(BaseGame *inGame) : BaseObject(inGame) { + _main = false; + _width = _height = 0; + _active = true; + _closeUp = false; +} + + +////////////////////////////////////////////////////////////////////////// +AdLayer::~AdLayer() { + for (uint32 i = 0; i < _nodes.size(); i++) { + delete _nodes[i]; + } + _nodes.clear(); +} + + +////////////////////////////////////////////////////////////////////////// +bool AdLayer::loadFile(const char *filename) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "AdLayer::LoadFile failed for file '%s'", filename); + return STATUS_FAILED; + } + + bool ret; + + setFilename(filename); + + if (DID_FAIL(ret = loadBuffer(buffer, true))) { + _gameRef->LOG(0, "Error parsing LAYER file '%s'", filename); + } + + delete[] buffer; + + return ret; +} + + +TOKEN_DEF_START +TOKEN_DEF(LAYER) +TOKEN_DEF(TEMPLATE) +TOKEN_DEF(NAME) +TOKEN_DEF(WIDTH) +TOKEN_DEF(HEIGHT) +TOKEN_DEF(MAIN) +TOKEN_DEF(ENTITY) +TOKEN_DEF(REGION) +TOKEN_DEF(ACTIVE) +TOKEN_DEF(EDITOR_SELECTED) +TOKEN_DEF(SCRIPT) +TOKEN_DEF(CAPTION) +TOKEN_DEF(PROPERTY) +TOKEN_DEF(CLOSE_UP) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool AdLayer::loadBuffer(byte *buffer, bool complete) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(LAYER) + TOKEN_TABLE(TEMPLATE) + TOKEN_TABLE(NAME) + TOKEN_TABLE(WIDTH) + TOKEN_TABLE(HEIGHT) + TOKEN_TABLE(MAIN) + TOKEN_TABLE(ENTITY) + TOKEN_TABLE(REGION) + TOKEN_TABLE(ACTIVE) + TOKEN_TABLE(EDITOR_SELECTED) + TOKEN_TABLE(SCRIPT) + TOKEN_TABLE(CAPTION) + TOKEN_TABLE(PROPERTY) + TOKEN_TABLE(CLOSE_UP) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE_END + + byte *params; + int cmd; + BaseParser parser; + + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_LAYER) { + _gameRef->LOG(0, "'LAYER' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_TEMPLATE: + if (DID_FAIL(loadFile((char *)params))) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_NAME: + setName((char *)params); + break; + + case TOKEN_CAPTION: + setCaption((char *)params); + break; + + case TOKEN_MAIN: + parser.scanStr((char *)params, "%b", &_main); + break; + + case TOKEN_CLOSE_UP: + parser.scanStr((char *)params, "%b", &_closeUp); + break; + + case TOKEN_WIDTH: + parser.scanStr((char *)params, "%d", &_width); + break; + + case TOKEN_HEIGHT: + parser.scanStr((char *)params, "%d", &_height); + break; + + case TOKEN_ACTIVE: + parser.scanStr((char *)params, "%b", &_active); + break; + + case TOKEN_REGION: { + AdRegion *region = new AdRegion(_gameRef); + AdSceneNode *node = new AdSceneNode(_gameRef); + if (!region || !node || DID_FAIL(region->loadBuffer(params, false))) { + cmd = PARSERR_GENERIC; + delete region; + delete node; + region = NULL; + node = NULL; + } else { + node->setRegion(region); + _nodes.add(node); + } + } + break; + + case TOKEN_ENTITY: { + AdEntity *entity = new AdEntity(_gameRef); + AdSceneNode *node = new AdSceneNode(_gameRef); + if (entity) { + entity->_zoomable = false; // scene entites default to NOT zoom + } + if (!entity || !node || DID_FAIL(entity->loadBuffer(params, false))) { + cmd = PARSERR_GENERIC; + delete entity; + delete node; + entity = NULL; + node = NULL; + } else { + node->setEntity(entity); + _nodes.add(node); + } + } + break; + + case TOKEN_EDITOR_SELECTED: + parser.scanStr((char *)params, "%b", &_editorSelected); + break; + + case TOKEN_SCRIPT: + addScript((char *)params); + break; + + case TOKEN_PROPERTY: + parseProperty(params, false); + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty(params, false); + break; + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in LAYER definition"); + return STATUS_FAILED; + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool AdLayer::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // GetNode + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "GetNode") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + int node = -1; + + if (val->_type == VAL_INT) { + node = val->getInt(); + } else { // get by name + for (uint32 i = 0; i < _nodes.size(); i++) { + if ((_nodes[i]->_type == OBJECT_ENTITY && scumm_stricmp(_nodes[i]->_entity->getName(), val->getString()) == 0) || + (_nodes[i]->_type == OBJECT_REGION && scumm_stricmp(_nodes[i]->_region->getName(), val->getString()) == 0)) { + node = i; + break; + } + } + } + + if (node < 0 || node >= (int32)_nodes.size()) { + stack->pushNULL(); + } else { + switch (_nodes[node]->_type) { + case OBJECT_ENTITY: + stack->pushNative(_nodes[node]->_entity, true); + break; + case OBJECT_REGION: + stack->pushNative(_nodes[node]->_region, true); + break; + default: + stack->pushNULL(); + } + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // AddRegion / AddEntity + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AddRegion") == 0 || strcmp(name, "AddEntity") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + AdSceneNode *node = new AdSceneNode(_gameRef); + if (strcmp(name, "AddRegion") == 0) { + AdRegion *region = new AdRegion(_gameRef); + if (!val->isNULL()) { + region->setName(val->getString()); + } + node->setRegion(region); + stack->pushNative(region, true); + } else { + AdEntity *entity = new AdEntity(_gameRef); + if (!val->isNULL()) { + entity->setName(val->getString()); + } + node->setEntity(entity); + stack->pushNative(entity, true); + } + _nodes.add(node); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // InsertRegion / InsertEntity + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "InsertRegion") == 0 || strcmp(name, "InsertEntity") == 0) { + stack->correctParams(2); + int index = stack->pop()->getInt(); + ScValue *val = stack->pop(); + + AdSceneNode *node = new AdSceneNode(_gameRef); + if (strcmp(name, "InsertRegion") == 0) { + AdRegion *region = new AdRegion(_gameRef); + if (!val->isNULL()) { + region->setName(val->getString()); + } + node->setRegion(region); + stack->pushNative(region, true); + } else { + AdEntity *entity = new AdEntity(_gameRef); + if (!val->isNULL()) { + entity->setName(val->getString()); + } + node->setEntity(entity); + stack->pushNative(entity, true); + } + if (index < 0) { + index = 0; + } + if (index <= (int32)_nodes.size() - 1) { + _nodes.insert_at(index, node); + } else { + _nodes.add(node); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // DeleteNode + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "DeleteNode") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + AdSceneNode *toDelete = NULL; + if (val->isNative()) { + BaseScriptable *temp = val->getNative(); + for (uint32 i = 0; i < _nodes.size(); i++) { + if (_nodes[i]->_region == temp || _nodes[i]->_entity == temp) { + toDelete = _nodes[i]; + break; + } + } + } else { + int index = val->getInt(); + if (index >= 0 && index < (int32)_nodes.size()) { + toDelete = _nodes[index]; + } + } + if (toDelete == NULL) { + stack->pushBool(false); + return STATUS_OK; + } + + for (uint32 i = 0; i < _nodes.size(); i++) { + if (_nodes[i] == toDelete) { + delete _nodes[i]; + _nodes[i] = NULL; + _nodes.remove_at(i); + break; + } + } + stack->pushBool(true); + return STATUS_OK; + } else { + return BaseObject::scCallMethod(script, stack, thisStack, name); + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *AdLayer::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("layer"); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // NumNodes (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "NumNodes") { + _scValue->setInt(_nodes.size()); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Width + ////////////////////////////////////////////////////////////////////////// + else if (name == "Width") { + _scValue->setInt(_width); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Height + ////////////////////////////////////////////////////////////////////////// + else if (name == "Height") { + _scValue->setInt(_height); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Main (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "Main") { + _scValue->setBool(_main); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // CloseUp + ////////////////////////////////////////////////////////////////////////// + else if (name == "CloseUp") { + _scValue->setBool(_closeUp); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Active + ////////////////////////////////////////////////////////////////////////// + else if (name == "Active") { + _scValue->setBool(_active); + return _scValue; + } else { + return BaseObject::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool AdLayer::scSetProperty(const char *name, ScValue *value) { + ////////////////////////////////////////////////////////////////////////// + // Name + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Name") == 0) { + setName(value->getString()); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // CloseUp + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "CloseUp") == 0) { + _closeUp = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Width + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Width") == 0) { + _width = value->getInt(); + if (_width < 0) { + _width = 0; + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Height + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Height") == 0) { + _height = value->getInt(); + if (_height < 0) { + _height = 0; + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Active + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Active") == 0) { + bool b = value->getBool(); + if (b == false && _main) { + _gameRef->LOG(0, "Warning: cannot deactivate scene's main layer"); + } else { + _active = b; + } + return STATUS_OK; + } else { + return BaseObject::scSetProperty(name, value); + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *AdLayer::scToString() { + return "[layer]"; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdLayer::saveAsText(BaseDynamicBuffer *buffer, int indent) { + buffer->putTextIndent(indent, "LAYER {\n"); + buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", getName()); + buffer->putTextIndent(indent + 2, "CAPTION=\"%s\"\n", getCaption()); + buffer->putTextIndent(indent + 2, "MAIN=%s\n", _main ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "WIDTH=%d\n", _width); + buffer->putTextIndent(indent + 2, "HEIGHT=%d\n", _height); + buffer->putTextIndent(indent + 2, "ACTIVE=%s\n", _active ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "EDITOR_SELECTED=%s\n", _editorSelected ? "TRUE" : "FALSE"); + if (_closeUp) { + buffer->putTextIndent(indent + 2, "CLOSE_UP=%s\n", _closeUp ? "TRUE" : "FALSE"); + } + + for (uint32 i = 0; i < _scripts.size(); i++) { + buffer->putTextIndent(indent + 2, "SCRIPT=\"%s\"\n", _scripts[i]->_filename); + } + + if (_scProp) { + _scProp->saveAsText(buffer, indent + 2); + } + + for (uint32 i = 0; i < _nodes.size(); i++) { + switch (_nodes[i]->_type) { + case OBJECT_ENTITY: + _nodes[i]->_entity->saveAsText(buffer, indent + 2); + break; + case OBJECT_REGION: + _nodes[i]->_region->saveAsText(buffer, indent + 2); + break; + default: + error("AdLayer::SaveAsText - Unhandled enum"); + break; + } + } + + BaseClass::saveAsText(buffer, indent + 2); + + buffer->putTextIndent(indent, "}\n\n"); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdLayer::persist(BasePersistenceManager *persistMgr) { + + BaseObject::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_active)); + persistMgr->transfer(TMEMBER(_closeUp)); + persistMgr->transfer(TMEMBER(_height)); + persistMgr->transfer(TMEMBER(_main)); + _nodes.persist(persistMgr); + persistMgr->transfer(TMEMBER(_width)); + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_layer.h b/engines/wintermute/ad/ad_layer.h new file mode 100644 index 0000000000..de65e2822f --- /dev/null +++ b/engines/wintermute/ad/ad_layer.h @@ -0,0 +1,58 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADLAYER_H +#define WINTERMUTE_ADLAYER_H + +namespace Wintermute { +class AdSceneNode; +class AdLayer : public BaseObject { +public: + bool _closeUp; + DECLARE_PERSISTENT(AdLayer, BaseObject) + bool _active; + int _height; + int _width; + bool _main; + AdLayer(BaseGame *inGame); + virtual ~AdLayer(); + BaseArray<AdSceneNode *> _nodes; + bool loadFile(const char *filename); + bool loadBuffer(byte *buffer, bool complete = true); + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent); + + // scripting interface + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + virtual const char *scToString(); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_node_state.cpp b/engines/wintermute/ad/ad_node_state.cpp new file mode 100644 index 0000000000..493156c750 --- /dev/null +++ b/engines/wintermute/ad/ad_node_state.cpp @@ -0,0 +1,196 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/ad/ad_node_state.h" +#include "engines/wintermute/ad/ad_entity.h" +#include "engines/wintermute/base/base_string_table.h" +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/utils/utils.h" +#include "engines/wintermute/platform_osystem.h" +#include "common/str.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdNodeState, false) + + +////////////////////////////////////////////////////////////////////////// +AdNodeState::AdNodeState(BaseGame *inGame) : BaseClass(inGame) { + _name = NULL; + _active = false; + for (int i = 0; i < 7; i++) { + _caption[i] = NULL; + } + _alphaColor = 0; + _filename = NULL; + _cursor = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +AdNodeState::~AdNodeState() { + delete[] _name; + delete[] _filename; + delete[] _cursor; + _name = NULL; + _filename = NULL; + _cursor = NULL; + for (int i = 0; i < 7; i++) { + delete[] _caption[i]; + _caption[i] = NULL; + } +} + + +////////////////////////////////////////////////////////////////////////// +void AdNodeState::setName(const char *name) { + delete[] _name; + _name = NULL; + BaseUtils::setString(&_name, name); +} + + +////////////////////////////////////////////////////////////////////////// +void AdNodeState::setFilename(const char *filename) { + delete[] _filename; + _filename = NULL; + BaseUtils::setString(&_filename, filename); +} + + +////////////////////////////////////////////////////////////////////////// +void AdNodeState::setCursor(const char *filename) { + delete[] _cursor; + _cursor = NULL; + BaseUtils::setString(&_cursor, filename); +} + + +////////////////////////////////////////////////////////////////////////// +bool AdNodeState::persist(BasePersistenceManager *persistMgr) { + persistMgr->transfer(TMEMBER(_gameRef)); + + persistMgr->transfer(TMEMBER(_active)); + persistMgr->transfer(TMEMBER(_name)); + persistMgr->transfer(TMEMBER(_filename)); + persistMgr->transfer(TMEMBER(_cursor)); + persistMgr->transfer(TMEMBER(_alphaColor)); + for (int i = 0; i < 7; i++) { + persistMgr->transfer(TMEMBER(_caption[i])); + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +void AdNodeState::setCaption(const char *caption, int caseVal) { + if (caseVal == 0) { + caseVal = 1; + } + if (caseVal < 1 || caseVal > 7) { + return; + } + + delete[] _caption[caseVal - 1]; + _caption[caseVal - 1] = new char[strlen(caption) + 1]; + if (_caption[caseVal - 1]) { + strcpy(_caption[caseVal - 1], caption); + _gameRef->_stringTable->expand(&_caption[caseVal - 1]); + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *AdNodeState::getCaption(int caseVal) { + if (caseVal == 0) { + caseVal = 1; + } + if (caseVal < 1 || caseVal > 7 || _caption[caseVal - 1] == NULL) { + return ""; + } else { + return _caption[caseVal - 1]; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool AdNodeState::transferEntity(AdEntity *entity, bool includingSprites, bool saving) { + if (!entity) { + return STATUS_FAILED; + } + + // HACK! + if (this->_gameRef != entity->_gameRef) { + this->_gameRef = entity->_gameRef; + } + + if (saving) { + for (int i = 0; i < 7; i++) { + if (entity->_caption[i]) { + setCaption(entity->_caption[i], i); + } + } + if (!entity->_region && entity->_sprite && entity->_sprite->getFilename()) { + if (includingSprites) { + setFilename(entity->_sprite->getFilename()); + } else { + setFilename(""); + } + } + if (entity->_cursor && entity->_cursor->getFilename()) { + setCursor(entity->_cursor->getFilename()); + } + _alphaColor = entity->_alphaColor; + _active = entity->_active; + } else { + for (int i = 0; i < 7; i++) { + if (_caption[i]) { + entity->setCaption(_caption[i], i); + } + } + if (_filename && !entity->_region && includingSprites && strcmp(_filename, "") != 0) { + if (!entity->_sprite || !entity->_sprite->getFilename() || scumm_stricmp(entity->_sprite->getFilename(), _filename) != 0) { + entity->setSprite(_filename); + } + } + if (_cursor) { + if (!entity->_cursor || !entity->_cursor->getFilename() || scumm_stricmp(entity->_cursor->getFilename(), _cursor) != 0) { + entity->setCursor(_cursor); + } + } + + entity->_active = _active; + entity->_alphaColor = _alphaColor; + } + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_node_state.h b/engines/wintermute/ad/ad_node_state.h new file mode 100644 index 0000000000..e2050815a7 --- /dev/null +++ b/engines/wintermute/ad/ad_node_state.h @@ -0,0 +1,60 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADNODESTATE_H +#define WINTERMUTE_ADNODESTATE_H + +namespace Wintermute { + +class AdEntity; + +class AdNodeState : public BaseClass { +public: + bool _active; + bool transferEntity(AdEntity *entity, bool includingSprites, bool saving); + void setName(const char *name); + void setFilename(const char *filename); + void setCursor(const char *filename); + DECLARE_PERSISTENT(AdNodeState, BaseClass) + AdNodeState(BaseGame *inGame); + virtual ~AdNodeState(); + const char *getName() const { return _name; } +private: + char *_name; + char *_caption[7]; + void setCaption(const char *caption, int caseVal); + const char *getCaption(int caseVal); + uint32 _alphaColor; + char *_filename; + char *_cursor; + +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_object.cpp b/engines/wintermute/ad/ad_object.cpp new file mode 100644 index 0000000000..a940b33805 --- /dev/null +++ b/engines/wintermute/ad/ad_object.cpp @@ -0,0 +1,1303 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_game.h" +#include "engines/wintermute/ad/ad_item.h" +#include "engines/wintermute/ad/ad_object.h" +#include "engines/wintermute/ad/ad_inventory.h" +#include "engines/wintermute/ad/ad_layer.h" +#include "engines/wintermute/ad/ad_scene.h" +#include "engines/wintermute/ad/ad_scene_node.h" +#include "engines/wintermute/ad/ad_sentence.h" +#include "engines/wintermute/ad/ad_waypoint_group.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_frame.h" +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/base/base_string_table.h" +#include "engines/wintermute/base/base_sub_frame.h" +#include "engines/wintermute/base/base_surface_storage.h" +#include "engines/wintermute/base/font/base_font.h" +#include "engines/wintermute/base/font/base_font_storage.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/base/particles/part_emitter.h" +#include "engines/wintermute/base/scriptables/script_engine.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/sound/base_sound.h" +#include "common/str.h" +#include "common/util.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdObject, false) + +////////////////////////////////////////////////////////////////////////// +AdObject::AdObject(BaseGame *inGame) : BaseObject(inGame) { + _type = OBJECT_NONE; + _state = _nextState = STATE_NONE; + + _active = true; + _drawn = false; + + _currentSprite = NULL; + _animSprite = NULL; + _tempSprite2 = NULL; + + _font = NULL; + + _sentence = NULL; + + _forcedTalkAnimName = NULL; + _forcedTalkAnimUsed = false; + + _blockRegion = NULL; + _wptGroup = NULL; + + _currentBlockRegion = NULL; + _currentWptGroup = NULL; + + _ignoreItems = false; + _sceneIndependent = false; + + _stickRegion = NULL; + + _subtitlesModRelative = true; + _subtitlesModX = 0; + _subtitlesModY = 0; + _subtitlesWidth = 0; + _subtitlesModXCenter = true; + + _inventory = NULL; + + for (int i = 0; i < MAX_NUM_REGIONS; i++) { + _currentRegions[i] = NULL; + } + + _partEmitter = NULL; + _partFollowParent = false; + _partOffsetX = _partOffsetY = 0; + + _registerAlias = this; +} + + +////////////////////////////////////////////////////////////////////////// +AdObject::~AdObject() { + _currentSprite = NULL; // reference only, don't delete + delete _animSprite; + _animSprite = NULL; + delete _sentence; + _sentence = NULL; + delete[] _forcedTalkAnimName; + _forcedTalkAnimName = NULL; + + delete _blockRegion; + _blockRegion = NULL; + delete _wptGroup; + _wptGroup = NULL; + + delete _currentBlockRegion; + _currentBlockRegion = NULL; + delete _currentWptGroup; + _currentWptGroup = NULL; + + _tempSprite2 = NULL; // reference only + _stickRegion = NULL; + + if (_font) { + _gameRef->_fontStorage->removeFont(_font); + } + + if (_inventory) { + ((AdGame *)_gameRef)->unregisterInventory(_inventory); + _inventory = NULL; + } + + if (_partEmitter) { + _gameRef->unregisterObject(_partEmitter); + } + + + for (uint32 i = 0; i < _attachmentsPre.size(); i++) { + _gameRef->unregisterObject(_attachmentsPre[i]); + } + _attachmentsPre.clear(); + + for (uint32 i = 0; i < _attachmentsPost.size(); i++) { + _gameRef->unregisterObject(_attachmentsPost[i]); + } + _attachmentsPost.clear(); +} + + +////////////////////////////////////////////////////////////////////////// +bool AdObject::playAnim(const char *filename) { + delete _animSprite; + _animSprite = NULL; + _animSprite = new BaseSprite(_gameRef, this); + if (!_animSprite) { + _gameRef->LOG(0, "AdObject::PlayAnim: error creating temp sprite (object:\"%s\" sprite:\"%s\")", getName(), filename); + return STATUS_FAILED; + } + bool res = _animSprite->loadFile(filename); + if (DID_FAIL(res)) { + _gameRef->LOG(res, "AdObject::PlayAnim: error loading temp sprite (object:\"%s\" sprite:\"%s\")", getName(), filename); + delete _animSprite; + _animSprite = NULL; + return res; + } + _state = STATE_PLAYING_ANIM; + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdObject::display() { + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdObject::update() { + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool AdObject::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + + ////////////////////////////////////////////////////////////////////////// + // PlayAnim / PlayAnimAsync + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "PlayAnim") == 0 || strcmp(name, "PlayAnimAsync") == 0) { + stack->correctParams(1); + if (DID_FAIL(playAnim(stack->pop()->getString()))) { + stack->pushBool(false); + } else { + if (strcmp(name, "PlayAnimAsync") != 0) { + script->waitFor(this); + } + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Reset + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Reset") == 0) { + stack->correctParams(0); + reset(); + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // IsTalking + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "IsTalking") == 0) { + stack->correctParams(0); + stack->pushBool(_state == STATE_TALKING); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // StopTalk / StopTalking + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "StopTalk") == 0 || strcmp(name, "StopTalking") == 0) { + stack->correctParams(0); + if (_sentence) { + _sentence->finish(); + } + if (_state == STATE_TALKING) { + _state = _nextState; + _nextState = STATE_READY; + stack->pushBool(true); + } else { + stack->pushBool(false); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ForceTalkAnim + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ForceTalkAnim") == 0) { + stack->correctParams(1); + const char *animName = stack->pop()->getString(); + delete[] _forcedTalkAnimName; + _forcedTalkAnimName = new char[strlen(animName) + 1]; + strcpy(_forcedTalkAnimName, animName); + _forcedTalkAnimUsed = false; + stack->pushBool(true); + return STATUS_OK; + } + + + ////////////////////////////////////////////////////////////////////////// + // Talk / TalkAsync + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Talk") == 0 || strcmp(name, "TalkAsync") == 0) { + stack->correctParams(5); + + const char *text = stack->pop()->getString(); + ScValue *soundVal = stack->pop(); + int duration = stack->pop()->getInt(); + ScValue *valStances = stack->pop(); + + const char *stances = valStances->isNULL() ? NULL : valStances->getString(); + + int align = 0; + ScValue *val = stack->pop(); + if (val->isNULL()) { + align = TAL_CENTER; + } else { + align = val->getInt(); + } + + align = MIN(MAX(0, align), NUM_TEXT_ALIGN - 1); + + const char *sound = soundVal->isNULL() ? NULL : soundVal->getString(); + + talk(text, sound, duration, stances, (TTextAlign)align); + if (strcmp(name, "TalkAsync") != 0) { + script->waitForExclusive(this); + } + + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // StickToRegion + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "StickToRegion") == 0) { + stack->correctParams(1); + + AdLayer *main = ((AdGame *)_gameRef)->_scene->_mainLayer; + bool regFound = false; + + uint32 i; + ScValue *val = stack->pop(); + if (val->isNULL() || !main) { + _stickRegion = NULL; + regFound = true; + } else if (val->isString()) { + const char *regionName = val->getString(); + for (i = 0; i < main->_nodes.size(); i++) { + if (main->_nodes[i]->_type == OBJECT_REGION && main->_nodes[i]->_region->getName() && scumm_stricmp(main->_nodes[i]->_region->getName(), regionName) == 0) { + _stickRegion = main->_nodes[i]->_region; + regFound = true; + break; + } + } + } else if (val->isNative()) { + BaseScriptable *obj = val->getNative(); + + for (i = 0; i < main->_nodes.size(); i++) { + if (main->_nodes[i]->_type == OBJECT_REGION && main->_nodes[i]->_region == obj) { + _stickRegion = main->_nodes[i]->_region; + regFound = true; + break; + } + } + + } + + if (!regFound) { + _stickRegion = NULL; + } + stack->pushBool(regFound); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetFont + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetFont") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + if (val->isNULL()) { + setFont(NULL); + } else { + setFont(val->getString()); + } + + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetFont + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetFont") == 0) { + stack->correctParams(0); + if (_font && _font->getFilename()) { + stack->pushString(_font->getFilename()); + } else { + stack->pushNULL(); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // TakeItem + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "TakeItem") == 0) { + stack->correctParams(2); + + if (!_inventory) { + _inventory = new AdInventory(_gameRef); + ((AdGame *)_gameRef)->registerInventory(_inventory); + } + + ScValue *val = stack->pop(); + if (!val->isNULL()) { + const char *itemName = val->getString(); + val = stack->pop(); + const char *insertAfter = val->isNULL() ? NULL : val->getString(); + if (DID_FAIL(_inventory->insertItem(itemName, insertAfter))) { + script->runtimeError("Cannot add item '%s' to inventory", itemName); + } else { + // hide associated entities + ((AdGame *)_gameRef)->_scene->handleItemAssociations(itemName, false); + } + + } else { + script->runtimeError("TakeItem: item name expected"); + } + + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // DropItem + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "DropItem") == 0) { + stack->correctParams(1); + + if (!_inventory) { + _inventory = new AdInventory(_gameRef); + ((AdGame *)_gameRef)->registerInventory(_inventory); + } + + ScValue *val = stack->pop(); + if (!val->isNULL()) { + if (DID_FAIL(_inventory->removeItem(val->getString()))) { + script->runtimeError("Cannot remove item '%s' from inventory", val->getString()); + } else { + // show associated entities + ((AdGame *)_gameRef)->_scene->handleItemAssociations(val->getString(), true); + } + } else { + script->runtimeError("DropItem: item name expected"); + } + + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetItem + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetItem") == 0) { + stack->correctParams(1); + + if (!_inventory) { + _inventory = new AdInventory(_gameRef); + ((AdGame *)_gameRef)->registerInventory(_inventory); + } + + ScValue *val = stack->pop(); + if (val->_type == VAL_STRING) { + AdItem *item = ((AdGame *)_gameRef)->getItemByName(val->getString()); + if (item) { + stack->pushNative(item, true); + } else { + stack->pushNULL(); + } + } else if (val->isNULL() || val->getInt() < 0 || val->getInt() >= (int32)_inventory->_takenItems.size()) { + stack->pushNULL(); + } else { + stack->pushNative(_inventory->_takenItems[val->getInt()], true); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // HasItem + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "HasItem") == 0) { + stack->correctParams(1); + + if (!_inventory) { + _inventory = new AdInventory(_gameRef); + ((AdGame *)_gameRef)->registerInventory(_inventory); + } + + ScValue *val = stack->pop(); + if (!val->isNULL()) { + for (uint32 i = 0; i < _inventory->_takenItems.size(); i++) { + if (val->getNative() == _inventory->_takenItems[i]) { + stack->pushBool(true); + return STATUS_OK; + } else if (scumm_stricmp(val->getString(), _inventory->_takenItems[i]->getName()) == 0) { + stack->pushBool(true); + return STATUS_OK; + } + } + } else { + script->runtimeError("HasItem: item name expected"); + } + + stack->pushBool(false); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // CreateParticleEmitter + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "CreateParticleEmitter") == 0) { + stack->correctParams(3); + bool followParent = stack->pop()->getBool(); + int offsetX = stack->pop()->getInt(); + int offsetY = stack->pop()->getInt(); + + PartEmitter *emitter = createParticleEmitter(followParent, offsetX, offsetY); + if (emitter) { + stack->pushNative(_partEmitter, true); + } else { + stack->pushNULL(); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // DeleteParticleEmitter + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "DeleteParticleEmitter") == 0) { + stack->correctParams(0); + if (_partEmitter) { + _gameRef->unregisterObject(_partEmitter); + _partEmitter = NULL; + } + stack->pushNULL(); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // AddAttachment + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AddAttachment") == 0) { + stack->correctParams(4); + const char *filename = stack->pop()->getString(); + bool preDisplay = stack->pop()->getBool(true); + int offsetX = stack->pop()->getInt(); + int offsetY = stack->pop()->getInt(); + + bool res; + AdEntity *ent = new AdEntity(_gameRef); + if (DID_FAIL(res = ent->loadFile(filename))) { + delete ent; + ent = NULL; + script->runtimeError("AddAttachment() failed loading entity '%s'", filename); + stack->pushBool(false); + } else { + _gameRef->registerObject(ent); + + ent->_posX = offsetX; + ent->_posY = offsetY; + ent->_active = true; + + if (preDisplay) { + _attachmentsPre.add(ent); + } else { + _attachmentsPost.add(ent); + } + + stack->pushBool(true); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // RemoveAttachment + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "RemoveAttachment") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + bool found = false; + if (val->isNative()) { + BaseScriptable *obj = val->getNative(); + for (uint32 i = 0; i < _attachmentsPre.size(); i++) { + if (_attachmentsPre[i] == obj) { + found = true; + _gameRef->unregisterObject(_attachmentsPre[i]); + _attachmentsPre.remove_at(i); + i--; + } + } + for (uint32 i = 0; i < _attachmentsPost.size(); i++) { + if (_attachmentsPost[i] == obj) { + found = true; + _gameRef->unregisterObject(_attachmentsPost[i]); + _attachmentsPost.remove_at(i); + i--; + } + } + } else { + const char *attachmentName = val->getString(); + for (uint32 i = 0; i < _attachmentsPre.size(); i++) { + if (_attachmentsPre[i]->getName() && scumm_stricmp(_attachmentsPre[i]->getName(), attachmentName) == 0) { + found = true; + _gameRef->unregisterObject(_attachmentsPre[i]); + _attachmentsPre.remove_at(i); + i--; + } + } + for (uint32 i = 0; i < _attachmentsPost.size(); i++) { + if (_attachmentsPost[i]->getName() && scumm_stricmp(_attachmentsPost[i]->getName(), attachmentName) == 0) { + found = true; + _gameRef->unregisterObject(_attachmentsPost[i]); + _attachmentsPost.remove_at(i); + i--; + } + } + } + stack->pushBool(found); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetAttachment + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetAttachment") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + AdObject *ret = NULL; + if (val->isInt()) { + int index = val->getInt(); + int currIndex = 0; + for (uint32 i = 0; i < _attachmentsPre.size(); i++) { + if (currIndex == index) { + ret = _attachmentsPre[i]; + } + currIndex++; + } + for (uint32 i = 0; i < _attachmentsPost.size(); i++) { + if (currIndex == index) { + ret = _attachmentsPost[i]; + } + currIndex++; + } + } else { + const char *attachmentName = val->getString(); + for (uint32 i = 0; i < _attachmentsPre.size(); i++) { + if (_attachmentsPre[i]->getName() && scumm_stricmp(_attachmentsPre[i]->getName(), attachmentName) == 0) { + ret = _attachmentsPre[i]; + break; + } + } + if (!ret) { + for (uint32 i = 0; i < _attachmentsPost.size(); i++) { + if (_attachmentsPost[i]->getName() && scumm_stricmp(_attachmentsPost[i]->getName(), attachmentName) == 0) { + ret = _attachmentsPre[i]; + break; + } + } + } + } + + if (ret != NULL) { + stack->pushNative(ret, true); + } else { + stack->pushNULL(); + } + + return STATUS_OK; + } else { + return BaseObject::scCallMethod(script, stack, thisStack, name); + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *AdObject::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("object"); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Active + ////////////////////////////////////////////////////////////////////////// + else if (name == "Active") { + _scValue->setBool(_active); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // IgnoreItems + ////////////////////////////////////////////////////////////////////////// + else if (name == "IgnoreItems") { + _scValue->setBool(_ignoreItems); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // SceneIndependent + ////////////////////////////////////////////////////////////////////////// + else if (name == "SceneIndependent") { + _scValue->setBool(_sceneIndependent); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // SubtitlesWidth + ////////////////////////////////////////////////////////////////////////// + else if (name == "SubtitlesWidth") { + _scValue->setInt(_subtitlesWidth); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // SubtitlesPosRelative + ////////////////////////////////////////////////////////////////////////// + else if (name == "SubtitlesPosRelative") { + _scValue->setBool(_subtitlesModRelative); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // SubtitlesPosX + ////////////////////////////////////////////////////////////////////////// + else if (name == "SubtitlesPosX") { + _scValue->setInt(_subtitlesModX); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // SubtitlesPosY + ////////////////////////////////////////////////////////////////////////// + else if (name == "SubtitlesPosY") { + _scValue->setInt(_subtitlesModY); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // SubtitlesPosXCenter + ////////////////////////////////////////////////////////////////////////// + else if (name == "SubtitlesPosXCenter") { + _scValue->setBool(_subtitlesModXCenter); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // NumItems (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "NumItems") { + _scValue->setInt(getInventory()->_takenItems.size()); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // ParticleEmitter (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "ParticleEmitter") { + if (_partEmitter) { + _scValue->setNative(_partEmitter, true); + } else { + _scValue->setNULL(); + } + + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // NumAttachments (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "NumAttachments") { + _scValue->setInt(_attachmentsPre.size() + _attachmentsPost.size()); + return _scValue; + } else { + return BaseObject::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool AdObject::scSetProperty(const char *name, ScValue *value) { + + ////////////////////////////////////////////////////////////////////////// + // Active + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Active") == 0) { + _active = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // IgnoreItems + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "IgnoreItems") == 0) { + _ignoreItems = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SceneIndependent + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SceneIndependent") == 0) { + _sceneIndependent = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SubtitlesWidth + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SubtitlesWidth") == 0) { + _subtitlesWidth = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SubtitlesPosRelative + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SubtitlesPosRelative") == 0) { + _subtitlesModRelative = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SubtitlesPosX + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SubtitlesPosX") == 0) { + _subtitlesModX = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SubtitlesPosY + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SubtitlesPosY") == 0) { + _subtitlesModY = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SubtitlesPosXCenter + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SubtitlesPosXCenter") == 0) { + _subtitlesModXCenter = value->getBool(); + return STATUS_OK; + } else { + return BaseObject::scSetProperty(name, value); + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *AdObject::scToString() { + return "[ad object]"; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdObject::setFont(const char *filename) { + if (_font) { + _gameRef->_fontStorage->removeFont(_font); + } + if (filename) { + _font = _gameRef->_fontStorage->addFont(filename); + return _font == NULL ? STATUS_FAILED : STATUS_OK; + } else { + _font = NULL; + return STATUS_OK; + } +} + + +////////////////////////////////////////////////////////////////////////// +int AdObject::getHeight() { + if (!_currentSprite) { + return 0; + } else { + BaseFrame *frame = _currentSprite->_frames[_currentSprite->_currentFrame]; + int ret = 0; + for (uint32 i = 0; i < frame->_subframes.size(); i++) { + ret = MAX(ret, frame->_subframes[i]->_hotspotY); + } + + if (_zoomable) { + float zoom = ((AdGame *)_gameRef)->_scene->getZoomAt(_posX, _posY); + ret = (int)(ret * zoom / 100); + } + return ret; + } +} + +TObjectType AdObject::getType() const { + return _type; +} + +////////////////////////////////////////////////////////////////////////// +void AdObject::talk(const char *text, const char *sound, uint32 duration, const char *stances, TTextAlign Align) { + if (!_sentence) { + _sentence = new AdSentence(_gameRef); + } + if (!_sentence) { + return; + } + + if (_forcedTalkAnimName && _forcedTalkAnimUsed) { + delete[] _forcedTalkAnimName; + _forcedTalkAnimName = NULL; + _forcedTalkAnimUsed = false; + } + + delete(_sentence->_sound); + _sentence->_sound = NULL; + + _sentence->setText(text); + _gameRef->_stringTable->expand(&_sentence->_text); + _sentence->setStances(stances); + _sentence->_duration = duration; + _sentence->_align = Align; + _sentence->_startTime = _gameRef->_timer; + _sentence->_currentStance = -1; + _sentence->_font = _font == NULL ? _gameRef->_systemFont : _font; + _sentence->_freezable = _freezable; + + // try to locate speech file automatically + bool deleteSound = false; + if (!sound) { + char *key = _gameRef->_stringTable->getKey(text); + if (key) { + sound = ((AdGame *)_gameRef)->findSpeechFile(key); + delete[] key; + + if (sound) { + deleteSound = true; + } + } + } + + // load sound and set duration appropriately + if (sound) { + BaseSound *snd = new BaseSound(_gameRef); + if (snd && DID_SUCCEED(snd->setSound(sound, Audio::Mixer::kSpeechSoundType, true))) { + _sentence->setSound(snd); + if (_sentence->_duration <= 0) { + uint32 length = snd->getLength(); + if (length != 0) { + _sentence->_duration = length; + } + } + } else { + delete snd; + } + } + + // set duration by text length + if (_sentence->_duration <= 0) {// TODO: Avoid longs. + _sentence->_duration = MAX((size_t)1000, _gameRef->_subtitlesSpeed * strlen(_sentence->_text)); + } + + + int x, y, width, height; + + x = _posX; + y = _posY; + + if (!_sceneIndependent && _subtitlesModRelative) { + x -= ((AdGame *)_gameRef)->_scene->getOffsetLeft(); + y -= ((AdGame *)_gameRef)->_scene->getOffsetTop(); + } + + + if (_subtitlesWidth > 0) { + width = _subtitlesWidth; + } else { + if ((x < _gameRef->_renderer->_width / 4 || x > _gameRef->_renderer->_width * 0.75) && !_gameRef->_touchInterface) { + width = MAX(_gameRef->_renderer->_width / 4, MIN(x * 2, (_gameRef->_renderer->_width - x) * 2)); + } else { + width = _gameRef->_renderer->_width / 2; + } + } + + height = _sentence->_font->getTextHeight((byte *)_sentence->_text, width); + + y = y - height - getHeight() - 5; + if (_subtitlesModRelative) { + x += _subtitlesModX; + y += _subtitlesModY; + } else { + x = _subtitlesModX; + y = _subtitlesModY; + } + if (_subtitlesModXCenter) { + x = x - width / 2; + } + + + x = MIN(MAX(0, x), _gameRef->_renderer->_width - width); + y = MIN(MAX(0, y), _gameRef->_renderer->_height - height); + + _sentence->_width = width; + + + _sentence->_pos.x = x; + _sentence->_pos.y = y; + + + if (_subtitlesModRelative) { + _sentence->_pos.x += ((AdGame *)_gameRef)->_scene->getOffsetLeft(); + _sentence->_pos.y += ((AdGame *)_gameRef)->_scene->getOffsetTop(); + } + + _sentence->_fixedPos = !_subtitlesModRelative; + + + _sentence->setupTalkFile(sound); + + _state = STATE_TALKING; + + if (deleteSound) { + delete[] sound; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool AdObject::reset() { + if (_state == STATE_PLAYING_ANIM && _animSprite != NULL) { + delete _animSprite; + _animSprite = NULL; + } else if (_state == STATE_TALKING && _sentence) { + _sentence->finish(); + } + + _state = _nextState = STATE_READY; + + _gameRef->_scEngine->resetObject(this); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdObject::persist(BasePersistenceManager *persistMgr) { + BaseObject::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_active)); + persistMgr->transfer(TMEMBER(_blockRegion)); + persistMgr->transfer(TMEMBER(_currentBlockRegion)); + persistMgr->transfer(TMEMBER(_currentWptGroup)); + persistMgr->transfer(TMEMBER(_currentSprite)); + persistMgr->transfer(TMEMBER(_drawn)); + persistMgr->transfer(TMEMBER(_font)); + persistMgr->transfer(TMEMBER(_ignoreItems)); + persistMgr->transfer(TMEMBER_INT(_nextState)); + persistMgr->transfer(TMEMBER(_sentence)); + persistMgr->transfer(TMEMBER_INT(_state)); + persistMgr->transfer(TMEMBER(_animSprite)); + persistMgr->transfer(TMEMBER(_sceneIndependent)); + persistMgr->transfer(TMEMBER(_forcedTalkAnimName)); + persistMgr->transfer(TMEMBER(_forcedTalkAnimUsed)); + persistMgr->transfer(TMEMBER(_tempSprite2)); + persistMgr->transfer(TMEMBER_INT(_type)); + persistMgr->transfer(TMEMBER(_wptGroup)); + persistMgr->transfer(TMEMBER(_stickRegion)); + persistMgr->transfer(TMEMBER(_subtitlesModRelative)); + persistMgr->transfer(TMEMBER(_subtitlesModX)); + persistMgr->transfer(TMEMBER(_subtitlesModY)); + persistMgr->transfer(TMEMBER(_subtitlesModXCenter)); + persistMgr->transfer(TMEMBER(_subtitlesWidth)); + persistMgr->transfer(TMEMBER(_inventory)); + persistMgr->transfer(TMEMBER(_partEmitter)); + + for (int i = 0; i < MAX_NUM_REGIONS; i++) { + persistMgr->transfer(TMEMBER(_currentRegions[i])); + } + + _attachmentsPre.persist(persistMgr); + _attachmentsPost.persist(persistMgr); + persistMgr->transfer(TMEMBER(_registerAlias)); + + persistMgr->transfer(TMEMBER(_partFollowParent)); + persistMgr->transfer(TMEMBER(_partOffsetX)); + persistMgr->transfer(TMEMBER(_partOffsetY)); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdObject::updateSounds() { + if (_sentence && _sentence->_sound) { + updateOneSound(_sentence->_sound); + } + + return BaseObject::updateSounds(); +} + + +////////////////////////////////////////////////////////////////////////// +bool AdObject::resetSoundPan() { + if (_sentence && _sentence->_sound) { + _sentence->_sound->setPan(0.0f); + } + return BaseObject::resetSoundPan(); +} + + +////////////////////////////////////////////////////////////////////////// +bool AdObject::getExtendedFlag(const char *flagName) { + if (!flagName) { + return false; + } else if (strcmp(flagName, "usable") == 0) { + return true; + } else { + return BaseObject::getExtendedFlag(flagName); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool AdObject::saveAsText(BaseDynamicBuffer *buffer, int indent) { + if (_blockRegion) { + _blockRegion->saveAsText(buffer, indent + 2, "BLOCKED_REGION"); + } + if (_wptGroup) { + _wptGroup->saveAsText(buffer, indent + 2); + } + + BaseClass::saveAsText(buffer, indent + 2); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdObject::updateBlockRegion() { + AdGame *adGame = (AdGame *)_gameRef; + if (adGame->_scene) { + if (_blockRegion && _currentBlockRegion) { + _currentBlockRegion->mimic(_blockRegion, _zoomable ? adGame->_scene->getScaleAt(_posY) : 100.0f, _posX, _posY); + } + + if (_wptGroup && _currentWptGroup) { + _currentWptGroup->mimic(_wptGroup, _zoomable ? adGame->_scene->getScaleAt(_posY) : 100.0f, _posX, _posY); + } + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +AdInventory *AdObject::getInventory() { + if (!_inventory) { + _inventory = new AdInventory(_gameRef); + ((AdGame *)_gameRef)->registerInventory(_inventory); + } + return _inventory; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdObject::afterMove() { + AdRegion *newRegions[MAX_NUM_REGIONS]; + + ((AdGame *)_gameRef)->_scene->getRegionsAt(_posX, _posY, newRegions, MAX_NUM_REGIONS); + for (int i = 0; i < MAX_NUM_REGIONS; i++) { + if (!newRegions[i]) { + break; + } + bool regFound = false; + for (int j = 0; j < MAX_NUM_REGIONS; j++) { + if (_currentRegions[j] == newRegions[i]) { + _currentRegions[j] = NULL; + regFound = true; + break; + } + } + if (!regFound) { + newRegions[i]->applyEvent("ActorEntry"); + } + } + + for (int i = 0; i < MAX_NUM_REGIONS; i++) { + if (_currentRegions[i] && _gameRef->validObject(_currentRegions[i])) { + _currentRegions[i]->applyEvent("ActorLeave"); + } + _currentRegions[i] = newRegions[i]; + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool AdObject::invalidateCurrRegions() { + for (int i = 0; i < MAX_NUM_REGIONS; i++) { + _currentRegions[i] = NULL; + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdObject::getScale(float *scaleX, float *scaleY) { + if (_zoomable) { + if (_scaleX >= 0 || _scaleY >= 0) { + *scaleX = _scaleX < 0 ? 100 : _scaleX; + *scaleY = _scaleY < 0 ? 100 : _scaleY; + } else if (_scale >= 0) { + *scaleX = *scaleY = _scale; + } else { + *scaleX = *scaleY = ((AdGame *)_gameRef)->_scene->getZoomAt(_posX, _posY) + _relativeScale; + } + } else { + *scaleX = *scaleY = 100; + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool AdObject::updateSpriteAttachments() { + for (uint32 i = 0; i < _attachmentsPre.size(); i++) { + _attachmentsPre[i]->update(); + } + for (uint32 i = 0; i < _attachmentsPost.size(); i++) { + _attachmentsPost[i]->update(); + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool AdObject::displaySpriteAttachments(bool preDisplay) { + if (preDisplay) { + for (uint32 i = 0; i < _attachmentsPre.size(); i++) { + displaySpriteAttachment(_attachmentsPre[i]); + } + } else { + for (uint32 i = 0; i < _attachmentsPost.size(); i++) { + displaySpriteAttachment(_attachmentsPost[i]); + } + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool AdObject::displaySpriteAttachment(AdObject *attachment) { + if (!attachment->_active) { + return STATUS_OK; + } + + float scaleX, scaleY; + getScale(&scaleX, &scaleY); + + int origX = attachment->_posX; + int origY = attachment->_posY; + + // inherit position from owner + attachment->_posX = (int)(this->_posX + attachment->_posX * scaleX / 100.0f); + attachment->_posY = (int)(this->_posY + attachment->_posY * scaleY / 100.0f); + + // inherit other props + attachment->_alphaColor = this->_alphaColor; + attachment->_blendMode = this->_blendMode; + + attachment->_scale = this->_scale; + attachment->_relativeScale = this->_relativeScale; + attachment->_scaleX = this->_scaleX; + attachment->_scaleY = this->_scaleY; + + attachment->_rotate = this->_rotate; + attachment->_relativeRotate = this->_relativeRotate; + attachment->_rotateValid = this->_rotateValid; + + attachment->_registerAlias = this; + attachment->_registrable = this->_registrable; + + bool ret = attachment->display(); + + attachment->_posX = origX; + attachment->_posY = origY; + + return ret; +} + +////////////////////////////////////////////////////////////////////////// +PartEmitter *AdObject::createParticleEmitter(bool followParent, int offsetX, int offsetY) { + _partFollowParent = followParent; + _partOffsetX = offsetX; + _partOffsetY = offsetY; + + if (!_partEmitter) { + _partEmitter = new PartEmitter(_gameRef, this); + if (_partEmitter) { + _gameRef->registerObject(_partEmitter); + } + } + updatePartEmitter(); + return _partEmitter; +} + +////////////////////////////////////////////////////////////////////////// +bool AdObject::updatePartEmitter() { + if (!_partEmitter) { + return STATUS_FAILED; + } + + if (_partFollowParent) { + float scaleX, scaleY; + getScale(&scaleX, &scaleY); + + _partEmitter->_posX = (int)(_posX + (scaleX / 100.0f) * _partOffsetX); + _partEmitter->_posY = (int)(_posY + (scaleY / 100.0f) * _partOffsetY); + } + return _partEmitter->update(); +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_object.h b/engines/wintermute/ad/ad_object.h new file mode 100644 index 0000000000..2a437c7e09 --- /dev/null +++ b/engines/wintermute/ad/ad_object.h @@ -0,0 +1,128 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADOBJECT_H +#define WINTERMUTE_ADOBJECT_H + +#include "engines/wintermute/ad/ad_types.h" +#include "engines/wintermute/base/base_object.h" + +namespace Wintermute { + +class AdWaypointGroup; +class AdRegion; +class AdSentence; +class BaseFont; +class BaseRegion; +class AdInventory; +class PartEmitter; + +#define MAX_NUM_REGIONS 10 + +class AdObject : public BaseObject { +public: + virtual PartEmitter *createParticleEmitter(bool followParent = false, int offsetX = 0, int offsetY = 0); + virtual bool updatePartEmitter(); + + bool invalidateCurrRegions(); + AdRegion *_stickRegion; + bool _sceneIndependent; + + bool updateBlockRegion(); + + virtual bool getExtendedFlag(const char *flagName); + virtual bool resetSoundPan(); + virtual bool updateSounds(); + bool reset(); + DECLARE_PERSISTENT(AdObject, BaseObject) + virtual void talk(const char *text, const char *sound = NULL, uint32 duration = 0, const char *stances = NULL, TTextAlign align = TAL_CENTER); + virtual int getHeight(); + + bool setFont(const char *filename); + virtual bool update(); + virtual bool display(); + + bool _drawn; + bool _active; + virtual bool playAnim(const char *filename); + + TObjectType getType() const; + AdObject(BaseGame *inGame); + virtual ~AdObject(); + + BaseRegion *_currentBlockRegion; + AdWaypointGroup *_currentWptGroup; + AdInventory *getInventory(); + + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent); + virtual bool afterMove(); + + // scripting interface + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + virtual const char *scToString(); + bool updateSpriteAttachments(); + bool displaySpriteAttachments(bool preDisplay); + +protected: + PartEmitter *_partEmitter; + bool _ignoreItems; + bool _forcedTalkAnimUsed; + char *_forcedTalkAnimName; + BaseSprite *_animSprite; + BaseSprite *_currentSprite; + AdSentence *_sentence; + TObjectState _state; + TObjectState _nextState; + TObjectType _type; + BaseFont *_font; + BaseSprite *_tempSprite2; + BaseRegion *_blockRegion; + AdWaypointGroup *_wptGroup; + AdObject *_registerAlias; + bool getScale(float *scaleX, float *scaleY); +private: + bool _partFollowParent; + int _partOffsetX; + int _partOffsetY; + bool _subtitlesModRelative; + bool _subtitlesModXCenter; + int _subtitlesModX; + int _subtitlesModY; + int _subtitlesWidth; + AdRegion *_currentRegions[MAX_NUM_REGIONS]; + BaseArray<AdObject *> _attachmentsPre; + BaseArray<AdObject *> _attachmentsPost; + bool displaySpriteAttachment(AdObject *attachment); + AdInventory *_inventory; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_path.cpp b/engines/wintermute/ad/ad_path.cpp new file mode 100644 index 0000000000..c931213456 --- /dev/null +++ b/engines/wintermute/ad/ad_path.cpp @@ -0,0 +1,120 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_path.h" +#include "engines/wintermute/base/base_point.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdPath, false) + +////////////////////////////////////////////////////////////////////////// +AdPath::AdPath(BaseGame *inGame) : BaseClass(inGame) { + _currIndex = -1; + _ready = false; +} + + +////////////////////////////////////////////////////////////////////////// +AdPath::~AdPath() { + reset(); +} + + +////////////////////////////////////////////////////////////////////////// +void AdPath::reset() { + for (uint32 i = 0; i < _points.size(); i++) { + delete _points[i]; + } + + _points.clear(); + _currIndex = -1; + _ready = false; +} + + +////////////////////////////////////////////////////////////////////////// +BasePoint *AdPath::getFirst() { + if (_points.size() > 0) { + _currIndex = 0; + return _points[_currIndex]; + } else { + return NULL; + } +} + + +////////////////////////////////////////////////////////////////////////// +BasePoint *AdPath::getNext() { + _currIndex++; + if (_currIndex < (int32)_points.size()) { + return _points[_currIndex]; + } else { + return NULL; + } +} + + +////////////////////////////////////////////////////////////////////////// +BasePoint *AdPath::getCurrent() { + if (_currIndex >= 0 && _currIndex < (int32)_points.size()) { + return _points[_currIndex]; + } else { + return NULL; + } +} + + +////////////////////////////////////////////////////////////////////////// +void AdPath::addPoint(BasePoint *point) { + _points.add(point); +} + + +////////////////////////////////////////////////////////////////////////// +bool AdPath::setReady(bool ready) { + bool orig = _ready; + _ready = ready; + + return orig; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdPath::persist(BasePersistenceManager *persistMgr) { + + persistMgr->transfer(TMEMBER(_gameRef)); + + persistMgr->transfer(TMEMBER(_currIndex)); + _points.persist(persistMgr); + persistMgr->transfer(TMEMBER(_ready)); + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_path.h b/engines/wintermute/ad/ad_path.h new file mode 100644 index 0000000000..6b043197aa --- /dev/null +++ b/engines/wintermute/ad/ad_path.h @@ -0,0 +1,56 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADPATH_H +#define WINTERMUTE_ADPATH_H + +#include "engines/wintermute/persistent.h" +#include "engines/wintermute/coll_templ.h" +#include "engines/wintermute/base/base.h" + +namespace Wintermute { +class BasePoint; +class AdPath : public BaseClass { +public: + DECLARE_PERSISTENT(AdPath, BaseClass) + BasePoint *getCurrent(); + bool setReady(bool ready = true); + void addPoint(BasePoint *point); + BasePoint *getNext(); + BasePoint *getFirst(); + void reset(); + AdPath(BaseGame *inGame); + virtual ~AdPath(); + BaseArray<BasePoint *> _points; + int _currIndex; + bool _ready; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_path_point.cpp b/engines/wintermute/ad/ad_path_point.cpp new file mode 100644 index 0000000000..a36648eb69 --- /dev/null +++ b/engines/wintermute/ad/ad_path_point.cpp @@ -0,0 +1,75 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_path_point.h" +#include "engines/wintermute/base/base_persistence_manager.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdPathPoint, false) + +////////////////////////////////////////////////////////////////////////// +AdPathPoint::AdPathPoint() { + x = y = 0; + _distance = 0; + + _marked = false; + _origin = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +AdPathPoint::AdPathPoint(int initX, int initY, int initDistance) { + x = initX; + y = initY; + _distance = initDistance; + + _marked = false; + _origin = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +AdPathPoint::~AdPathPoint() { + _origin = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdPathPoint::persist(BasePersistenceManager *persistMgr) { + + BasePoint::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_distance)); + persistMgr->transfer(TMEMBER(_marked)); + persistMgr->transfer(TMEMBER(_origin)); + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_path_point.h b/engines/wintermute/ad/ad_path_point.h new file mode 100644 index 0000000000..58457976c8 --- /dev/null +++ b/engines/wintermute/ad/ad_path_point.h @@ -0,0 +1,50 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADPATHPOINT_H +#define WINTERMUTE_ADPATHPOINT_H + +#include "engines/wintermute/persistent.h" +#include "engines/wintermute/base/base_point.h" + +namespace Wintermute { + +class AdPathPoint : public BasePoint { +public: + DECLARE_PERSISTENT(AdPathPoint, BasePoint) + AdPathPoint(int initX, int initY, int initDistance); + AdPathPoint(); + virtual ~AdPathPoint(); + AdPathPoint *_origin; + bool _marked; + int _distance; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_region.cpp b/engines/wintermute/ad/ad_region.cpp new file mode 100644 index 0000000000..daf0c89606 --- /dev/null +++ b/engines/wintermute/ad/ad_region.cpp @@ -0,0 +1,412 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_region.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdRegion, false) + +////////////////////////////////////////////////////////////////////////// +AdRegion::AdRegion(BaseGame *inGame) : BaseRegion(inGame) { + _blocked = false; + _decoration = false; + _zoom = 0; + _alpha = 0xFFFFFFFF; +} + + +////////////////////////////////////////////////////////////////////////// +AdRegion::~AdRegion() { +} + +uint32 AdRegion::getAlpha() const { + return _alpha; +} + +float AdRegion::getZoom() const { + return _zoom; +} + +bool AdRegion::isBlocked() const { + return _blocked; +} + +bool AdRegion::hasDecoration() const { + return _decoration; +} + +////////////////////////////////////////////////////////////////////////// +bool AdRegion::loadFile(const char *filename) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "AdRegion::LoadFile failed for file '%s'", filename); + return STATUS_FAILED; + } + + bool ret; + + setFilename(filename); + + if (DID_FAIL(ret = loadBuffer(buffer, true))) { + _gameRef->LOG(0, "Error parsing REGION file '%s'", filename); + } + + + delete[] buffer; + + return ret; +} + + +TOKEN_DEF_START +TOKEN_DEF(REGION) +TOKEN_DEF(TEMPLATE) +TOKEN_DEF(NAME) +TOKEN_DEF(ACTIVE) +TOKEN_DEF(ZOOM) +TOKEN_DEF(SCALE) +TOKEN_DEF(BLOCKED) +TOKEN_DEF(DECORATION) +TOKEN_DEF(POINT) +TOKEN_DEF(ALPHA_COLOR) +TOKEN_DEF(ALPHA) +TOKEN_DEF(EDITOR_SELECTED_POINT) +TOKEN_DEF(EDITOR_SELECTED) +TOKEN_DEF(SCRIPT) +TOKEN_DEF(CAPTION) +TOKEN_DEF(PROPERTY) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool AdRegion::loadBuffer(byte *buffer, bool complete) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(REGION) + TOKEN_TABLE(TEMPLATE) + TOKEN_TABLE(NAME) + TOKEN_TABLE(ACTIVE) + TOKEN_TABLE(ZOOM) + TOKEN_TABLE(SCALE) + TOKEN_TABLE(BLOCKED) + TOKEN_TABLE(DECORATION) + TOKEN_TABLE(POINT) + TOKEN_TABLE(ALPHA_COLOR) + TOKEN_TABLE(ALPHA) + TOKEN_TABLE(EDITOR_SELECTED_POINT) + TOKEN_TABLE(EDITOR_SELECTED) + TOKEN_TABLE(SCRIPT) + TOKEN_TABLE(CAPTION) + TOKEN_TABLE(PROPERTY) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE_END + + byte *params; + int cmd; + BaseParser parser; + + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_REGION) { + _gameRef->LOG(0, "'REGION' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + for (uint32 i = 0; i < _points.size(); i++) { + delete _points[i]; + } + _points.clear(); + + int ar = 255, ag = 255, ab = 255, alpha = 255; + + while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_TEMPLATE: + if (DID_FAIL(loadFile((char *)params))) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_NAME: + setName((char *)params); + break; + + case TOKEN_CAPTION: + setCaption((char *)params); + break; + + case TOKEN_ACTIVE: + parser.scanStr((char *)params, "%b", &_active); + break; + + case TOKEN_BLOCKED: + parser.scanStr((char *)params, "%b", &_blocked); + break; + + case TOKEN_DECORATION: + parser.scanStr((char *)params, "%b", &_decoration); + break; + + case TOKEN_ZOOM: + case TOKEN_SCALE: { + int j; + parser.scanStr((char *)params, "%d", &j); + _zoom = (float)j; + } + break; + + case TOKEN_POINT: { + int x, y; + parser.scanStr((char *)params, "%d,%d", &x, &y); + _points.add(new BasePoint(x, y)); + } + break; + + case TOKEN_ALPHA_COLOR: + parser.scanStr((char *)params, "%d,%d,%d", &ar, &ag, &ab); + break; + + case TOKEN_ALPHA: + parser.scanStr((char *)params, "%d", &alpha); + break; + + case TOKEN_EDITOR_SELECTED: + parser.scanStr((char *)params, "%b", &_editorSelected); + break; + + case TOKEN_EDITOR_SELECTED_POINT: + parser.scanStr((char *)params, "%d", &_editorSelectedPoint); + break; + + case TOKEN_SCRIPT: + addScript((char *)params); + break; + + case TOKEN_PROPERTY: + parseProperty(params, false); + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty(params, false); + break; + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in REGION definition"); + return STATUS_FAILED; + } + + createRegion(); + + _alpha = BYTETORGBA(ar, ag, ab, alpha); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool AdRegion::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + /* + ////////////////////////////////////////////////////////////////////////// + // SkipTo + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "SkipTo")==0) { + stack->correctParams(2); + _posX = stack->pop()->getInt(); + _posY = stack->pop()->getInt(); + stack->pushNULL(); + + return STATUS_OK; + } + + else*/ return BaseRegion::scCallMethod(script, stack, thisStack, name); +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *AdRegion::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("ad region"); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Name + ////////////////////////////////////////////////////////////////////////// + else if (name == "Name") { + _scValue->setString(getName()); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Blocked + ////////////////////////////////////////////////////////////////////////// + else if (name == "Blocked") { + _scValue->setBool(_blocked); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Decoration + ////////////////////////////////////////////////////////////////////////// + else if (name == "Decoration") { + _scValue->setBool(_decoration); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Scale + ////////////////////////////////////////////////////////////////////////// + else if (name == "Scale") { + _scValue->setFloat(_zoom); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // AlphaColor + ////////////////////////////////////////////////////////////////////////// + else if (name == "AlphaColor") { + _scValue->setInt((int)_alpha); + return _scValue; + } else { + return BaseRegion::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool AdRegion::scSetProperty(const char *name, ScValue *value) { + ////////////////////////////////////////////////////////////////////////// + // Name + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Name") == 0) { + setName(value->getString()); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Blocked + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Blocked") == 0) { + _blocked = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Decoration + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Decoration") == 0) { + _decoration = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Scale + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Scale") == 0) { + _zoom = value->getFloat(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // AlphaColor + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AlphaColor") == 0) { + _alpha = (uint32)value->getInt(); + return STATUS_OK; + } else { + return BaseRegion::scSetProperty(name, value); + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *AdRegion::scToString() { + return "[ad region]"; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdRegion::saveAsText(BaseDynamicBuffer *buffer, int indent) { + buffer->putTextIndent(indent, "REGION {\n"); + buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", getName()); + buffer->putTextIndent(indent + 2, "CAPTION=\"%s\"\n", getCaption()); + buffer->putTextIndent(indent + 2, "BLOCKED=%s\n", _blocked ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "DECORATION=%s\n", _decoration ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "ACTIVE=%s\n", _active ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "SCALE=%d\n", (int)_zoom); + buffer->putTextIndent(indent + 2, "ALPHA_COLOR { %d,%d,%d }\n", RGBCOLGetR(_alpha), RGBCOLGetG(_alpha), RGBCOLGetB(_alpha)); + buffer->putTextIndent(indent + 2, "ALPHA = %d\n", RGBCOLGetA(_alpha)); + buffer->putTextIndent(indent + 2, "EDITOR_SELECTED=%s\n", _editorSelected ? "TRUE" : "FALSE"); + + for (uint32 i = 0; i < _scripts.size(); i++) { + buffer->putTextIndent(indent + 2, "SCRIPT=\"%s\"\n", _scripts[i]->_filename); + } + + if (_scProp) { + _scProp->saveAsText(buffer, indent + 2); + } + + for (uint32 i = 0; i < _points.size(); i++) { + buffer->putTextIndent(indent + 2, "POINT {%d,%d}\n", _points[i]->x, _points[i]->y); + } + + BaseClass::saveAsText(buffer, indent + 2); + + buffer->putTextIndent(indent, "}\n\n"); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdRegion::persist(BasePersistenceManager *persistMgr) { + BaseRegion::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_alpha)); + persistMgr->transfer(TMEMBER(_blocked)); + persistMgr->transfer(TMEMBER(_decoration)); + persistMgr->transfer(TMEMBER(_zoom)); + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_region.h b/engines/wintermute/ad/ad_region.h new file mode 100644 index 0000000000..960dde5f15 --- /dev/null +++ b/engines/wintermute/ad/ad_region.h @@ -0,0 +1,64 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADREGION_H +#define WINTERMUTE_ADREGION_H + +#include "engines/wintermute/base/base_region.h" + +namespace Wintermute { + +class AdRegion : public BaseRegion { +public: + DECLARE_PERSISTENT(AdRegion, BaseRegion) + + AdRegion(BaseGame *inGame); + virtual ~AdRegion(); + bool loadFile(const char *filename); + bool loadBuffer(byte *buffer, bool complete = true); + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent); + + bool hasDecoration() const; + bool isBlocked() const; + uint32 getAlpha() const; + float getZoom() const; + // scripting interface + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + virtual const char *scToString(); +private: + uint32 _alpha; + float _zoom; + bool _blocked; + bool _decoration; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_response.cpp b/engines/wintermute/ad/ad_response.cpp new file mode 100644 index 0000000000..a2225f2632 --- /dev/null +++ b/engines/wintermute/ad/ad_response.cpp @@ -0,0 +1,146 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_response.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/base/font/base_font_storage.h" +#include "engines/wintermute/utils/utils.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdResponse, false) + +////////////////////////////////////////////////////////////////////////// +AdResponse::AdResponse(BaseGame *inGame) : BaseObject(inGame) { + _text = NULL; + _textOrig = NULL; + _icon = _iconHover = _iconPressed = NULL; + _font = NULL; + _iD = 0; + _responseType = RESPONSE_ALWAYS; +} + + +////////////////////////////////////////////////////////////////////////// +AdResponse::~AdResponse() { + delete[] _text; + delete[] _textOrig; + delete _icon; + delete _iconHover; + delete _iconPressed; + _text = NULL; + _textOrig = NULL; + _icon = NULL; + _iconHover = NULL; + _iconPressed = NULL; + if (_font) { + _gameRef->_fontStorage->removeFont(_font); + } +} + + +////////////////////////////////////////////////////////////////////////// +void AdResponse::setText(const char *text) { + BaseUtils::setString(&_text, text); + BaseUtils::setString(&_textOrig, text); +} + + +////////////////////////////////////////////////////////////////////////// +bool AdResponse::setIcon(const char *filename) { + delete _icon; + _icon = new BaseSprite(_gameRef); + if (!_icon || DID_FAIL(_icon->loadFile(filename))) { + _gameRef->LOG(0, "AdResponse::setIcon failed for file '%s'", filename); + delete _icon; + _icon = NULL; + return STATUS_FAILED; + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool AdResponse::setFont(const char *filename) { + if (_font) { + _gameRef->_fontStorage->removeFont(_font); + } + _font = _gameRef->_fontStorage->addFont(filename); + if (!_font) { + _gameRef->LOG(0, "AdResponse::setFont failed for file '%s'", filename); + return STATUS_FAILED; + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool AdResponse::setIconHover(const char *filename) { + delete _iconHover; + _iconHover = new BaseSprite(_gameRef); + if (!_iconHover || DID_FAIL(_iconHover->loadFile(filename))) { + _gameRef->LOG(0, "AdResponse::setIconHover failed for file '%s'", filename); + delete _iconHover; + _iconHover = NULL; + return STATUS_FAILED; + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdResponse::setIconPressed(const char *filename) { + delete _iconPressed; + _iconPressed = new BaseSprite(_gameRef); + if (!_iconPressed || DID_FAIL(_iconPressed->loadFile(filename))) { + _gameRef->LOG(0, "AdResponse::setIconPressed failed for file '%s'", filename); + delete _iconPressed; + _iconPressed = NULL; + return STATUS_FAILED; + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdResponse::persist(BasePersistenceManager *persistMgr) { + + BaseObject::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_icon)); + persistMgr->transfer(TMEMBER(_iconHover)); + persistMgr->transfer(TMEMBER(_iconPressed)); + persistMgr->transfer(TMEMBER(_iD)); + persistMgr->transfer(TMEMBER(_text)); + persistMgr->transfer(TMEMBER(_textOrig)); + persistMgr->transfer(TMEMBER_INT(_responseType)); + persistMgr->transfer(TMEMBER(_font)); + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_response.h b/engines/wintermute/ad/ad_response.h new file mode 100644 index 0000000000..2119067d3e --- /dev/null +++ b/engines/wintermute/ad/ad_response.h @@ -0,0 +1,61 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADRESPONSE_H +#define WINTERMUTE_ADRESPONSE_H + + +#include "engines/wintermute/base/base_object.h" +#include "engines/wintermute/ad/ad_types.h" + +namespace Wintermute { +class BaseFont; +class AdResponse : public BaseObject { +public: + DECLARE_PERSISTENT(AdResponse, BaseObject) + bool setIcon(const char *filename); + bool setFont(const char *filename); + bool setIconHover(const char *filename); + bool setIconPressed(const char *filename); + void setText(const char *text); + int32 _iD; + BaseSprite *_icon; + BaseSprite *_iconHover; + BaseSprite *_iconPressed; + BaseFont *_font; + char *_text; + char *_textOrig; + AdResponse(BaseGame *inGame); + virtual ~AdResponse(); + TResponseType _responseType; + +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_response_box.cpp b/engines/wintermute/ad/ad_response_box.cpp new file mode 100644 index 0000000000..45fd33d222 --- /dev/null +++ b/engines/wintermute/ad/ad_response_box.cpp @@ -0,0 +1,740 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_game.h" +#include "engines/wintermute/ad/ad_response.h" +#include "engines/wintermute/ad/ad_response_box.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/base/base_surface_storage.h" +#include "engines/wintermute/base/font/base_font_storage.h" +#include "engines/wintermute/base/font/base_font.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/ui/ui_button.h" +#include "engines/wintermute/ui/ui_window.h" +#include "engines/wintermute/utils/utils.h" +#include "engines/wintermute/platform_osystem.h" +#include "engines/wintermute/wintermute.h" +#include "common/str.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdResponseBox, false) + +////////////////////////////////////////////////////////////////////////// +AdResponseBox::AdResponseBox(BaseGame *inGame) : BaseObject(inGame) { + _font = _fontHover = NULL; + + _window = NULL; + _shieldWindow = new UIWindow(_gameRef); + + _horizontal = false; + BasePlatform::setRectEmpty(&_responseArea); + _scrollOffset = 0; + _spacing = 0; + + _waitingScript = NULL; + _lastResponseText = NULL; + _lastResponseTextOrig = NULL; + + _verticalAlign = VAL_BOTTOM; + _align = TAL_LEFT; +} + + +////////////////////////////////////////////////////////////////////////// +AdResponseBox::~AdResponseBox() { + + delete _window; + _window = NULL; + delete _shieldWindow; + _shieldWindow = NULL; + delete[] _lastResponseText; + _lastResponseText = NULL; + delete[] _lastResponseTextOrig; + _lastResponseTextOrig = NULL; + + if (_font) { + _gameRef->_fontStorage->removeFont(_font); + } + if (_fontHover) { + _gameRef->_fontStorage->removeFont(_fontHover); + } + + clearResponses(); + clearButtons(); + + _waitingScript = NULL; +} + +uint32 AdResponseBox::getNumResponses() const { + return _responses.size(); +} + +////////////////////////////////////////////////////////////////////////// +void AdResponseBox::clearResponses() { + for (uint32 i = 0; i < _responses.size(); i++) { + delete _responses[i]; + } + _responses.clear(); +} + + +////////////////////////////////////////////////////////////////////////// +void AdResponseBox::clearButtons() { + for (uint32 i = 0; i < _respButtons.size(); i++) { + delete _respButtons[i]; + } + _respButtons.clear(); +} + + +////////////////////////////////////////////////////////////////////////// +bool AdResponseBox::invalidateButtons() { + for (uint32 i = 0; i < _respButtons.size(); i++) { + _respButtons[i]->_image = NULL; + _respButtons[i]->_cursor = NULL; + _respButtons[i]->_font = NULL; + _respButtons[i]->_fontHover = NULL; + _respButtons[i]->_fontPress = NULL; + _respButtons[i]->setText(""); + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdResponseBox::createButtons() { + clearButtons(); + + _scrollOffset = 0; + for (uint32 i = 0; i < _responses.size(); i++) { + UIButton *btn = new UIButton(_gameRef); + if (btn) { + btn->_parent = _window; + btn->_sharedFonts = btn->_sharedImages = true; + btn->_sharedCursors = true; + // iconic + if (_responses[i]->_icon) { + btn->_image = _responses[i]->_icon; + if (_responses[i]->_iconHover) { + btn->_imageHover = _responses[i]->_iconHover; + } + if (_responses[i]->_iconPressed) { + btn->_imagePress = _responses[i]->_iconPressed; + } + + btn->setCaption(_responses[i]->_text); + if (_cursor) { + btn->_cursor = _cursor; + } else if (_gameRef->_activeCursor) { + btn->_cursor = _gameRef->_activeCursor; + } + } + // textual + else { + btn->setText(_responses[i]->_text); + btn->_font = (_font == NULL) ? _gameRef->_systemFont : _font; + btn->_fontHover = (_fontHover == NULL) ? _gameRef->_systemFont : _fontHover; + btn->_fontPress = btn->_fontHover; + btn->_align = _align; + + if (_gameRef->_touchInterface) { + btn->_fontHover = btn->_font; + } + + + if (_responses[i]->_font) { + btn->_font = _responses[i]->_font; + } + + btn->_width = _responseArea.right - _responseArea.left; + if (btn->_width <= 0) { + btn->_width = _gameRef->_renderer->_width; + } + } + btn->setName("response"); + btn->correctSize(); + + // make the responses touchable + if (_gameRef->_touchInterface) { + btn->_height = MAX(btn->_height, 50); + } + + //btn->SetListener(this, btn, _responses[i]->_iD); + btn->setListener(this, btn, i); + btn->_visible = false; + _respButtons.add(btn); + + if (_responseArea.bottom - _responseArea.top < btn->_height) { + _gameRef->LOG(0, "Warning: Response '%s' is too high to be displayed within response box. Correcting.", _responses[i]->_text); + _responseArea.bottom += (btn->_height - (_responseArea.bottom - _responseArea.top)); + } + } + } + _ready = false; + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdResponseBox::loadFile(const char *filename) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "AdResponseBox::LoadFile failed for file '%s'", filename); + return STATUS_FAILED; + } + + bool ret; + + setFilename(filename); + + if (DID_FAIL(ret = loadBuffer(buffer, true))) { + _gameRef->LOG(0, "Error parsing RESPONSE_BOX file '%s'", filename); + } + + + delete[] buffer; + + return ret; +} + + +TOKEN_DEF_START +TOKEN_DEF(RESPONSE_BOX) +TOKEN_DEF(TEMPLATE) +TOKEN_DEF(FONT_HOVER) +TOKEN_DEF(FONT) +TOKEN_DEF(AREA) +TOKEN_DEF(HORIZONTAL) +TOKEN_DEF(SPACING) +TOKEN_DEF(WINDOW) +TOKEN_DEF(CURSOR) +TOKEN_DEF(TEXT_ALIGN) +TOKEN_DEF(VERTICAL_ALIGN) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool AdResponseBox::loadBuffer(byte *buffer, bool complete) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(RESPONSE_BOX) + TOKEN_TABLE(TEMPLATE) + TOKEN_TABLE(FONT_HOVER) + TOKEN_TABLE(FONT) + TOKEN_TABLE(AREA) + TOKEN_TABLE(HORIZONTAL) + TOKEN_TABLE(SPACING) + TOKEN_TABLE(WINDOW) + TOKEN_TABLE(CURSOR) + TOKEN_TABLE(TEXT_ALIGN) + TOKEN_TABLE(VERTICAL_ALIGN) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE_END + + + byte *params; + int cmd; + BaseParser parser; + + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_RESPONSE_BOX) { + _gameRef->LOG(0, "'RESPONSE_BOX' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_TEMPLATE: + if (DID_FAIL(loadFile((char *)params))) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_WINDOW: + delete _window; + _window = new UIWindow(_gameRef); + if (!_window || DID_FAIL(_window->loadBuffer(params, false))) { + delete _window; + _window = NULL; + cmd = PARSERR_GENERIC; + } else if (_shieldWindow) { + _shieldWindow->_parent = _window; + } + break; + + case TOKEN_FONT: + if (_font) { + _gameRef->_fontStorage->removeFont(_font); + } + _font = _gameRef->_fontStorage->addFont((char *)params); + if (!_font) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_FONT_HOVER: + if (_fontHover) { + _gameRef->_fontStorage->removeFont(_fontHover); + } + _fontHover = _gameRef->_fontStorage->addFont((char *)params); + if (!_fontHover) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_AREA: + parser.scanStr((char *)params, "%d,%d,%d,%d", &_responseArea.left, &_responseArea.top, &_responseArea.right, &_responseArea.bottom); + break; + + case TOKEN_HORIZONTAL: + parser.scanStr((char *)params, "%b", &_horizontal); + break; + + case TOKEN_TEXT_ALIGN: + if (scumm_stricmp((char *)params, "center") == 0) { + _align = TAL_CENTER; + } else if (scumm_stricmp((char *)params, "right") == 0) { + _align = TAL_RIGHT; + } else { + _align = TAL_LEFT; + } + break; + + case TOKEN_VERTICAL_ALIGN: + if (scumm_stricmp((char *)params, "top") == 0) { + _verticalAlign = VAL_TOP; + } else if (scumm_stricmp((char *)params, "center") == 0) { + _verticalAlign = VAL_CENTER; + } else { + _verticalAlign = VAL_BOTTOM; + } + break; + + case TOKEN_SPACING: + parser.scanStr((char *)params, "%d", &_spacing); + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty(params, false); + break; + + case TOKEN_CURSOR: + delete _cursor; + _cursor = new BaseSprite(_gameRef); + if (!_cursor || DID_FAIL(_cursor->loadFile((char *)params))) { + delete _cursor; + _cursor = NULL; + cmd = PARSERR_GENERIC; + } + break; + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in RESPONSE_BOX definition"); + return STATUS_FAILED; + } + + if (_window) { + for (uint32 i = 0; i < _window->_widgets.size(); i++) { + if (!_window->_widgets[i]->_listenerObject) { + _window->_widgets[i]->setListener(this, _window->_widgets[i], 0); + } + } + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool AdResponseBox::saveAsText(BaseDynamicBuffer *buffer, int indent) { + buffer->putTextIndent(indent, "RESPONSE_BOX\n"); + buffer->putTextIndent(indent, "{\n"); + + buffer->putTextIndent(indent + 2, "AREA { %d, %d, %d, %d }\n", _responseArea.left, _responseArea.top, _responseArea.right, _responseArea.bottom); + + if (_font && _font->getFilename()) { + buffer->putTextIndent(indent + 2, "FONT=\"%s\"\n", _font->getFilename()); + } + if (_fontHover && _fontHover->getFilename()) { + buffer->putTextIndent(indent + 2, "FONT_HOVER=\"%s\"\n", _fontHover->getFilename()); + } + + if (_cursor && _cursor->getFilename()) { + buffer->putTextIndent(indent + 2, "CURSOR=\"%s\"\n", _cursor->getFilename()); + } + + buffer->putTextIndent(indent + 2, "HORIZONTAL=%s\n", _horizontal ? "TRUE" : "FALSE"); + + switch (_align) { + case TAL_LEFT: + buffer->putTextIndent(indent + 2, "TEXT_ALIGN=\"%s\"\n", "left"); + break; + case TAL_RIGHT: + buffer->putTextIndent(indent + 2, "TEXT_ALIGN=\"%s\"\n", "right"); + break; + case TAL_CENTER: + buffer->putTextIndent(indent + 2, "TEXT_ALIGN=\"%s\"\n", "center"); + break; + default: + error("AdResponseBox::SaveAsText - Unhandled enum"); + break; + } + + switch (_verticalAlign) { + case VAL_TOP: + buffer->putTextIndent(indent + 2, "VERTICAL_ALIGN=\"%s\"\n", "top"); + break; + case VAL_BOTTOM: + buffer->putTextIndent(indent + 2, "VERTICAL_ALIGN=\"%s\"\n", "bottom"); + break; + case VAL_CENTER: + buffer->putTextIndent(indent + 2, "VERTICAL_ALIGN=\"%s\"\n", "center"); + break; + } + + buffer->putTextIndent(indent + 2, "SPACING=%d\n", _spacing); + + buffer->putTextIndent(indent + 2, "\n"); + + // window + if (_window) { + _window->saveAsText(buffer, indent + 2); + } + + buffer->putTextIndent(indent + 2, "\n"); + + // editor properties + BaseClass::saveAsText(buffer, indent + 2); + + buffer->putTextIndent(indent, "}\n"); + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdResponseBox::display() { + Rect32 rect = _responseArea; + if (_window) { + rect.offsetRect(_window->_posX, _window->_posY); + //_window->display(); + } + + int xxx, yyy; + uint32 i; + + xxx = rect.left; + yyy = rect.top; + + // shift down if needed + if (!_horizontal) { + int totalHeight = 0; + for (i = 0; i < _respButtons.size(); i++) { + totalHeight += (_respButtons[i]->_height + _spacing); + } + totalHeight -= _spacing; + + switch (_verticalAlign) { + case VAL_BOTTOM: + if (yyy + totalHeight < rect.bottom) { + yyy = rect.bottom - totalHeight; + } + break; + + case VAL_CENTER: + if (yyy + totalHeight < rect.bottom) { + yyy += ((rect.bottom - rect.top) - totalHeight) / 2; + } + break; + + case VAL_TOP: + // do nothing + break; + } + } + + // prepare response buttons + bool scrollNeeded = false; + for (i = _scrollOffset; i < _respButtons.size(); i++) { + if ((_horizontal && xxx + _respButtons[i]->_width > rect.right) + || (!_horizontal && yyy + _respButtons[i]->_height > rect.bottom)) { + + scrollNeeded = true; + _respButtons[i]->_visible = false; + break; + } + + _respButtons[i]->_visible = true; + _respButtons[i]->_posX = xxx; + _respButtons[i]->_posY = yyy; + + if (_horizontal) { + xxx += (_respButtons[i]->_width + _spacing); + } else { + yyy += (_respButtons[i]->_height + _spacing); + } + } + + // show appropriate scroll buttons + if (_window) { + _window->showWidget("prev", _scrollOffset > 0); + _window->showWidget("next", scrollNeeded); + } + + // go exclusive + if (_shieldWindow) { + _shieldWindow->_posX = _shieldWindow->_posY = 0; + _shieldWindow->_width = _gameRef->_renderer->_width; + _shieldWindow->_height = _gameRef->_renderer->_height; + + _shieldWindow->display(); + } + + // display window + if (_window) { + _window->display(); + } + + + // display response buttons + for (i = _scrollOffset; i < _respButtons.size(); i++) { + _respButtons[i]->display(); + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdResponseBox::listen(BaseScriptHolder *param1, uint32 param2) { + UIObject *obj = (UIObject *)param1; + + switch (obj->_type) { + case UI_BUTTON: + if (scumm_stricmp(obj->getName(), "prev") == 0) { + _scrollOffset--; + } else if (scumm_stricmp(obj->getName(), "next") == 0) { + _scrollOffset++; + } else if (scumm_stricmp(obj->getName(), "response") == 0) { + if (_waitingScript) { + _waitingScript->_stack->pushInt(_responses[param2]->_iD); + } + handleResponse(_responses[param2]); + _waitingScript = NULL; + _gameRef->_state = GAME_RUNNING; + ((AdGame *)_gameRef)->_stateEx = GAME_NORMAL; + _ready = true; + invalidateButtons(); + clearResponses(); + } else { + return BaseObject::listen(param1, param2); + } + break; + default: + error("AdResponseBox::Listen - Unhandled enum"); + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdResponseBox::persist(BasePersistenceManager *persistMgr) { + BaseObject::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_font)); + persistMgr->transfer(TMEMBER(_fontHover)); + persistMgr->transfer(TMEMBER(_horizontal)); + persistMgr->transfer(TMEMBER(_lastResponseText)); + persistMgr->transfer(TMEMBER(_lastResponseTextOrig)); + _respButtons.persist(persistMgr); + persistMgr->transfer(TMEMBER(_responseArea)); + _responses.persist(persistMgr); + persistMgr->transfer(TMEMBER(_scrollOffset)); + persistMgr->transfer(TMEMBER(_shieldWindow)); + persistMgr->transfer(TMEMBER(_spacing)); + persistMgr->transfer(TMEMBER(_waitingScript)); + persistMgr->transfer(TMEMBER(_window)); + + persistMgr->transfer(TMEMBER_INT(_verticalAlign)); + persistMgr->transfer(TMEMBER_INT(_align)); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdResponseBox::weedResponses() { + AdGame *adGame = (AdGame *)_gameRef; + + for (uint32 i = 0; i < _responses.size(); i++) { + switch (_responses[i]->_responseType) { + case RESPONSE_ONCE: + if (adGame->branchResponseUsed(_responses[i]->_iD)) { + delete _responses[i]; + _responses.remove_at(i); + i--; + } + break; + + case RESPONSE_ONCE_GAME: + if (adGame->gameResponseUsed(_responses[i]->_iD)) { + delete _responses[i]; + _responses.remove_at(i); + i--; + } + break; + default: + debugC(kWintermuteDebugGeneral, "AdResponseBox::WeedResponses - Unhandled enum"); + break; + } + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +void AdResponseBox::setLastResponseText(const char *text, const char *textOrig) { + BaseUtils::setString(&_lastResponseText, text); + BaseUtils::setString(&_lastResponseTextOrig, textOrig); +} + +const char *AdResponseBox::getLastResponseText() const { + return _lastResponseText; +} + +const char *AdResponseBox::getLastResponseTextOrig() const { + return _lastResponseTextOrig; +} + +UIWindow *AdResponseBox::getResponseWindow() { + return _window; +} + +void AdResponseBox::addResponse(AdResponse *response) { + _responses.add(response); +} + +int32 AdResponseBox::getIdForResponseNum(uint32 num) const { + assert(num < _responses.size()); + return _responses[num]->_iD; +} + +bool AdResponseBox::handleResponseNum(uint32 num) { + return handleResponse(_responses[num]); +} + +////////////////////////////////////////////////////////////////////////// +bool AdResponseBox::handleResponse(AdResponse *response) { + setLastResponseText(response->_text, response->_textOrig); + + AdGame *adGame = (AdGame *)_gameRef; + + switch (response->_responseType) { + case RESPONSE_ONCE: + adGame->addBranchResponse(response->_iD); + break; + + case RESPONSE_ONCE_GAME: + adGame->addGameResponse(response->_iD); + break; + default: + debugC(kWintermuteDebugGeneral, "AdResponseBox::HandleResponse - Unhandled enum"); + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +BaseObject *AdResponseBox::getNextAccessObject(BaseObject *currObject) { + BaseArray<UIObject *> objects; + getObjects(objects, true); + + if (objects.size() == 0) { + return NULL; + } else { + if (currObject != NULL) { + for (uint32 i = 0; i < objects.size(); i++) { + if (objects[i] == currObject) { + if (i < objects.size() - 1) { + return objects[i + 1]; + } else { + break; + } + } + } + } + return objects[0]; + } + return NULL; +} + +////////////////////////////////////////////////////////////////////////// +BaseObject *AdResponseBox::getPrevAccessObject(BaseObject *currObject) { + BaseArray<UIObject *> objects; + getObjects(objects, true); + + if (objects.size() == 0) { + return NULL; + } else { + if (currObject != NULL) { + for (int i = objects.size() - 1; i >= 0; i--) { + if (objects[i] == currObject) { + if (i > 0) { + return objects[i - 1]; + } else { + break; + } + } + } + } + return objects[objects.size() - 1]; + } + return NULL; +} + +////////////////////////////////////////////////////////////////////////// +bool AdResponseBox::getObjects(BaseArray<UIObject *> &objects, bool interactiveOnly) { + for (uint32 i = 0; i < _respButtons.size(); i++) { + objects.add(_respButtons[i]); + } + if (_window) { + _window->getWindowObjects(objects, interactiveOnly); + } + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_response_box.h b/engines/wintermute/ad/ad_response_box.h new file mode 100644 index 0000000000..3b180062b6 --- /dev/null +++ b/engines/wintermute/ad/ad_response_box.h @@ -0,0 +1,99 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADRESPONSEBOX_H +#define WINTERMUTE_ADRESPONSEBOX_H + + +#include "engines/wintermute/base/base_object.h" + +namespace Wintermute { + +class UIButton; +class UIWindow; +class UIObject; +class AdResponse; +class AdResponseBox : public BaseObject { +public: + BaseObject *getNextAccessObject(BaseObject *CurrObject); + BaseObject *getPrevAccessObject(BaseObject *CurrObject); + bool getObjects(BaseArray<UIObject *> &objects, bool interactiveOnly); + + void addResponse(AdResponse* response); + bool handleResponse(AdResponse *response); + bool handleResponseNum(uint32 num); + int32 getIdForResponseNum(uint32 num) const; + void setLastResponseText(const char *text, const char *textOrig); + const char *getLastResponseText() const; + const char *getLastResponseTextOrig() const; + + DECLARE_PERSISTENT(AdResponseBox, BaseObject) + ScScript *_waitingScript; + virtual bool listen(BaseScriptHolder *param1, uint32 param2); + typedef enum { + EVENT_PREV, + EVENT_NEXT, + EVENT_RESPONSE + } TResponseEvent; + + bool weedResponses(); + bool display(); + + bool createButtons(); + bool invalidateButtons(); + void clearButtons(); + void clearResponses(); + AdResponseBox(BaseGame *inGame); + virtual ~AdResponseBox(); + + bool loadFile(const char *filename); + bool loadBuffer(byte *buffer, bool complete = true); + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent); + + UIWindow *getResponseWindow(); + uint32 getNumResponses() const; +private: + int _spacing; + int _scrollOffset; + BaseFont *_fontHover; + BaseFont *_font; + bool _horizontal; + Rect32 _responseArea; + int _verticalAlign; + TTextAlign _align; + BaseArray<UIButton *> _respButtons; + BaseArray<AdResponse *> _responses; + UIWindow *_shieldWindow; + char *_lastResponseText; + char *_lastResponseTextOrig; + UIWindow *_window; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_response_context.cpp b/engines/wintermute/ad/ad_response_context.cpp new file mode 100644 index 0000000000..ebfa03feea --- /dev/null +++ b/engines/wintermute/ad/ad_response_context.cpp @@ -0,0 +1,71 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_response_context.h" +#include "engines/wintermute/base/base_persistence_manager.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdResponseContext, false) + +////////////////////////////////////////////////////////////////////////// +AdResponseContext::AdResponseContext(BaseGame *inGame) : BaseClass(inGame) { + _id = 0; + _context = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +AdResponseContext::~AdResponseContext() { + delete[] _context; + _context = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdResponseContext::persist(BasePersistenceManager *persistMgr) { + persistMgr->transfer(TMEMBER(_gameRef)); + persistMgr->transfer(TMEMBER(_context)); + persistMgr->transfer(TMEMBER(_id)); + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +void AdResponseContext::setContext(const char *context) { + delete[] _context; + _context = NULL; + if (context) { + _context = new char [strlen(context) + 1]; + if (_context) { + strcpy(_context, context); + } + } +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_response_context.h b/engines/wintermute/ad/ad_response_context.h new file mode 100644 index 0000000000..14bc1abd93 --- /dev/null +++ b/engines/wintermute/ad/ad_response_context.h @@ -0,0 +1,50 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADRESPONSECONTEXT_H +#define WINTERMUTE_ADRESPONSECONTEXT_H + +#include "engines/wintermute/persistent.h" +#include "engines/wintermute/base/base.h" + +namespace Wintermute { + +class AdResponseContext : public BaseClass { +public: + void setContext(const char *context); + int _id; + char *_context; + DECLARE_PERSISTENT(AdResponseContext, BaseClass) + AdResponseContext(BaseGame *inGame); + virtual ~AdResponseContext(); + +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_rot_level.cpp b/engines/wintermute/ad/ad_rot_level.cpp new file mode 100644 index 0000000000..fb9a4a47b9 --- /dev/null +++ b/engines/wintermute/ad/ad_rot_level.cpp @@ -0,0 +1,161 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_rot_level.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_sprite.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdRotLevel, false) + + +////////////////////////////////////////////////////////////////////////// +AdRotLevel::AdRotLevel(BaseGame *inGame) : BaseObject(inGame) { + _posX = 0; + _rotation = 0.0f; +} + + +////////////////////////////////////////////////////////////////////////// +AdRotLevel::~AdRotLevel() { + +} + + +////////////////////////////////////////////////////////////////////////// +bool AdRotLevel::loadFile(const char *filename) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "AdRotLevel::LoadFile failed for file '%s'", filename); + return STATUS_FAILED; + } + + bool ret; + + setFilename(filename); + + if (DID_FAIL(ret = loadBuffer(buffer, true))) { + _gameRef->LOG(0, "Error parsing ROTATION_LEVEL file '%s'", filename); + } + + + delete[] buffer; + + return ret; +} + + +TOKEN_DEF_START +TOKEN_DEF(ROTATION_LEVEL) +TOKEN_DEF(TEMPLATE) +TOKEN_DEF(X) +TOKEN_DEF(ROTATION) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool AdRotLevel::loadBuffer(byte *buffer, bool complete) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(ROTATION_LEVEL) + TOKEN_TABLE(TEMPLATE) + TOKEN_TABLE(X) + TOKEN_TABLE(ROTATION) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE_END + + byte *params; + int cmd; + BaseParser parser; + + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_ROTATION_LEVEL) { + _gameRef->LOG(0, "'ROTATION_LEVEL' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_TEMPLATE: + if (DID_FAIL(loadFile((char *)params))) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_X: + parser.scanStr((char *)params, "%d", &_posX); + break; + + case TOKEN_ROTATION: { + int i; + parser.scanStr((char *)params, "%d", &i); + _rotation = (float)i; + } + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty(params, false); + break; + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in ROTATION_LEVEL definition"); + return STATUS_FAILED; + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdRotLevel::saveAsText(BaseDynamicBuffer *buffer, int indent) { + buffer->putTextIndent(indent, "ROTATION_LEVEL {\n"); + buffer->putTextIndent(indent + 2, "X=%d\n", _posX); + buffer->putTextIndent(indent + 2, "ROTATION=%d\n", (int)_rotation); + BaseClass::saveAsText(buffer, indent + 2); + buffer->putTextIndent(indent, "}\n"); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdRotLevel::persist(BasePersistenceManager *persistMgr) { + + BaseObject::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_rotation)); + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_rot_level.h b/engines/wintermute/ad/ad_rot_level.h new file mode 100644 index 0000000000..d7f5f8edf0 --- /dev/null +++ b/engines/wintermute/ad/ad_rot_level.h @@ -0,0 +1,49 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADROTLEVEL_H +#define WINTERMUTE_ADROTLEVEL_H + +#include "engines/wintermute/base/base_object.h" + +namespace Wintermute { + +class AdRotLevel : public BaseObject { +public: + DECLARE_PERSISTENT(AdRotLevel, BaseObject) + AdRotLevel(BaseGame *inGame); + virtual ~AdRotLevel(); + float _rotation; + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent); + bool loadFile(const char *filename); + bool loadBuffer(byte *buffer, bool complete = true); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_scale_level.cpp b/engines/wintermute/ad/ad_scale_level.cpp new file mode 100644 index 0000000000..cfec8d7cb9 --- /dev/null +++ b/engines/wintermute/ad/ad_scale_level.cpp @@ -0,0 +1,162 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_scale_level.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdScaleLevel, false) + +////////////////////////////////////////////////////////////////////////// +AdScaleLevel::AdScaleLevel(BaseGame *inGame) : BaseObject(inGame) { + _posY = 0; + _scale = 100; +} + + +////////////////////////////////////////////////////////////////////////// +AdScaleLevel::~AdScaleLevel() { + +} + +float AdScaleLevel::getScale() const { + return _scale; +} + +////////////////////////////////////////////////////////////////////////// +bool AdScaleLevel::loadFile(const char *filename) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "AdScaleLevel::LoadFile failed for file '%s'", filename); + return STATUS_FAILED; + } + + bool ret; + + setFilename(filename); + + if (DID_FAIL(ret = loadBuffer(buffer, true))) { + _gameRef->LOG(0, "Error parsing SCALE_LEVEL file '%s'", filename); + } + + + delete[] buffer; + + return ret; +} + + +TOKEN_DEF_START +TOKEN_DEF(SCALE_LEVEL) +TOKEN_DEF(TEMPLATE) +TOKEN_DEF(Y) +TOKEN_DEF(SCALE) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool AdScaleLevel::loadBuffer(byte *buffer, bool complete) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(SCALE_LEVEL) + TOKEN_TABLE(TEMPLATE) + TOKEN_TABLE(Y) + TOKEN_TABLE(SCALE) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE_END + + byte *params; + int cmd; + BaseParser parser; + + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_SCALE_LEVEL) { + _gameRef->LOG(0, "'SCALE_LEVEL' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_TEMPLATE: + if (DID_FAIL(loadFile((char *)params))) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_Y: + parser.scanStr((char *)params, "%d", &_posY); + break; + + case TOKEN_SCALE: { + int i; + parser.scanStr((char *)params, "%d", &i); + _scale = (float)i; + } + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty(params, false); + break; + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in SCALE_LEVEL definition"); + return STATUS_FAILED; + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScaleLevel::saveAsText(BaseDynamicBuffer *buffer, int indent) { + buffer->putTextIndent(indent, "SCALE_LEVEL {\n"); + buffer->putTextIndent(indent + 2, "Y=%d\n", _posY); + buffer->putTextIndent(indent + 2, "SCALE=%d\n", (int)_scale); + BaseClass::saveAsText(buffer, indent + 2); + buffer->putTextIndent(indent, "}\n"); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScaleLevel::persist(BasePersistenceManager *persistMgr) { + + BaseObject::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_scale)); + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_scale_level.h b/engines/wintermute/ad/ad_scale_level.h new file mode 100644 index 0000000000..5c206423cf --- /dev/null +++ b/engines/wintermute/ad/ad_scale_level.h @@ -0,0 +1,52 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADSCALELEVEL_H +#define WINTERMUTE_ADSCALELEVEL_H + + +#include "engines/wintermute/base/base_object.h" + +namespace Wintermute { + +class AdScaleLevel : public BaseObject { +public: + DECLARE_PERSISTENT(AdScaleLevel, BaseObject) + AdScaleLevel(BaseGame *inGame); + virtual ~AdScaleLevel(); + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent); + bool loadFile(const char *filename); + bool loadBuffer(byte *buffer, bool complete = true); + float getScale() const; +private: + float _scale; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_scene.cpp b/engines/wintermute/ad/ad_scene.cpp new file mode 100644 index 0000000000..526e0802cb --- /dev/null +++ b/engines/wintermute/ad/ad_scene.cpp @@ -0,0 +1,2992 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_scene.h" +#include "engines/wintermute/ad/ad_actor.h" +#include "engines/wintermute/ad/ad_entity.h" +#include "engines/wintermute/ad/ad_game.h" +#include "engines/wintermute/ad/ad_layer.h" +#include "engines/wintermute/ad/ad_node_state.h" +#include "engines/wintermute/ad/ad_object.h" +#include "engines/wintermute/ad/ad_path.h" +#include "engines/wintermute/ad/ad_path_point.h" +#include "engines/wintermute/ad/ad_rot_level.h" +#include "engines/wintermute/ad/ad_scale_level.h" +#include "engines/wintermute/ad/ad_scene_node.h" +#include "engines/wintermute/ad/ad_scene_state.h" +#include "engines/wintermute/ad/ad_sentence.h" +#include "engines/wintermute/ad/ad_waypoint_group.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/font/base_font.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_object.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_point.h" +#include "engines/wintermute/base/base_region.h" +#include "engines/wintermute/base/base_scriptable.h" +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/base/base_viewport.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/ui/ui_window.h" +#include "engines/wintermute/utils/utils.h" +#include "engines/wintermute/wintermute.h" +#include <limits.h> + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdScene, false) + +////////////////////////////////////////////////////////////////////////// +AdScene::AdScene(BaseGame *inGame) : BaseObject(inGame) { + _pfTarget = new BasePoint; + setDefaults(); +} + + +////////////////////////////////////////////////////////////////////////// +AdScene::~AdScene() { + cleanup(); + _gameRef->unregisterObject(_fader); + delete _pfTarget; + _pfTarget = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +void AdScene::setDefaults() { + _initialized = false; + _pfReady = true; + _pfTargetPath = NULL; + _pfRequester = NULL; + _mainLayer = NULL; + + _pfPointsNum = 0; + _persistentState = false; + _persistentStateSprites = true; + + _autoScroll = true; + _offsetLeft = _offsetTop = 0; + _targetOffsetLeft = _targetOffsetTop = 0; + + _lastTimeH = _lastTimeV = 0; + _scrollTimeH = _scrollTimeV = 10; + _scrollPixelsH = _scrollPixelsV = 1; + + _pfMaxTime = 15; + + _paralaxScrolling = true; + + // editor settings + _editorMarginH = _editorMarginV = 100; + + _editorColFrame = 0xE0888888; + _editorColEntity = 0xFF008000; + _editorColRegion = 0xFF0000FF; + _editorColBlocked = 0xFF800080; + _editorColWaypoints = 0xFF0000FF; + _editorColEntitySel = 0xFFFF0000; + _editorColRegionSel = 0xFFFF0000; + _editorColBlockedSel = 0xFFFF0000; + _editorColWaypointsSel = 0xFFFF0000; + _editorColScale = 0xFF00FF00; + _editorColDecor = 0xFF00FFFF; + _editorColDecorSel = 0xFFFF0000; + + _editorShowRegions = true; + _editorShowBlocked = true; + _editorShowDecor = true; + _editorShowEntities = true; + _editorShowScale = true; + + _shieldWindow = NULL; + + _fader = new BaseFader(_gameRef); + _gameRef->registerObject(_fader); + + _viewport = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +void AdScene::cleanup() { + BaseObject::cleanup(); + + _mainLayer = NULL; // reference only + + delete _shieldWindow; + _shieldWindow = NULL; + + _gameRef->unregisterObject(_fader); + _fader = NULL; + + for (uint32 i = 0; i < _layers.size(); i++) { + _gameRef->unregisterObject(_layers[i]); + } + _layers.clear(); + + + for (uint32 i = 0; i < _waypointGroups.size(); i++) { + _gameRef->unregisterObject(_waypointGroups[i]); + } + _waypointGroups.clear(); + + for (uint32 i = 0; i < _scaleLevels.size(); i++) { + _gameRef->unregisterObject(_scaleLevels[i]); + } + _scaleLevels.clear(); + + for (uint32 i = 0; i < _rotLevels.size(); i++) { + _gameRef->unregisterObject(_rotLevels[i]); + } + _rotLevels.clear(); + + + for (uint32 i = 0; i < _pfPath.size(); i++) { + delete _pfPath[i]; + } + _pfPath.clear(); + _pfPointsNum = 0; + + for (uint32 i = 0; i < _objects.size(); i++) { + _gameRef->unregisterObject(_objects[i]); + } + _objects.clear(); + + delete _viewport; + _viewport = NULL; + + setDefaults(); +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::getPath(BasePoint source, BasePoint target, AdPath *path, BaseObject *requester) { + if (!_pfReady) { + return false; + } else { + _pfReady = false; + *_pfTarget = target; + _pfTargetPath = path; + _pfRequester = requester; + + _pfTargetPath->reset(); + _pfTargetPath->setReady(false); + + // prepare working path + pfPointsStart(); + + // first point + //_pfPath.add(new AdPathPoint(source.x, source.y, 0)); + + // if we're one pixel stuck, get unstuck + int startX = source.x; + int startY = source.y; + int bestDistance = 1000; + if (isBlockedAt(startX, startY, true, requester)) { + int tolerance = 2; + for (int xxx = startX - tolerance; xxx <= startX + tolerance; xxx++) { + for (int yyy = startY - tolerance; yyy <= startY + tolerance; yyy++) { + if (isWalkableAt(xxx, yyy, true, requester)) { + int distance = abs(xxx - source.x) + abs(yyy - source.y); + if (distance < bestDistance) { + startX = xxx; + startY = yyy; + + bestDistance = distance; + } + } + } + } + } + + pfPointsAdd(startX, startY, 0); + + //CorrectTargetPoint(&target.x, &target.y); + + // last point + //_pfPath.add(new AdPathPoint(target.x, target.y, INT_MAX)); + pfPointsAdd(target.x, target.y, INT_MAX); + + // active waypoints + for (uint32 i = 0; i < _waypointGroups.size(); i++) { + if (_waypointGroups[i]->_active) { + pfAddWaypointGroup(_waypointGroups[i], requester); + } + } + + + // free waypoints + for (uint32 i = 0; i < _objects.size(); i++) { + if (_objects[i]->_active && _objects[i] != requester && _objects[i]->_currentWptGroup) { + pfAddWaypointGroup(_objects[i]->_currentWptGroup, requester); + } + } + AdGame *adGame = (AdGame *)_gameRef; + for (uint32 i = 0; i < adGame->_objects.size(); i++) { + if (adGame->_objects[i]->_active && adGame->_objects[i] != requester && adGame->_objects[i]->_currentWptGroup) { + pfAddWaypointGroup(adGame->_objects[i]->_currentWptGroup, requester); + } + } + + return true; + } +} + + +////////////////////////////////////////////////////////////////////////// +void AdScene::pfAddWaypointGroup(AdWaypointGroup *wpt, BaseObject *requester) { + if (!wpt->_active) { + return; + } + + for (uint32 i = 0; i < wpt->_points.size(); i++) { + if (isBlockedAt(wpt->_points[i]->x, wpt->_points[i]->y, true, requester)) { + continue; + } + + //_pfPath.add(new AdPathPoint(Wpt->_points[i]->x, Wpt->_points[i]->y, INT_MAX)); + pfPointsAdd(wpt->_points[i]->x, wpt->_points[i]->y, INT_MAX); + } +} + + +////////////////////////////////////////////////////////////////////////// +float AdScene::getZoomAt(int x, int y) { + float ret = 100; + + bool found = false; + if (_mainLayer) { + for (int i = _mainLayer->_nodes.size() - 1; i >= 0; i--) { + AdSceneNode *node = _mainLayer->_nodes[i]; + if (node->_type == OBJECT_REGION && node->_region->_active && !node->_region->isBlocked() && node->_region->pointInRegion(x, y)) { + if (node->_region->getZoom() != 0) { + ret = node->_region->getZoom(); + found = true; + break; + } + } + } + } + if (!found) { + ret = getScaleAt(y); + } + + return ret; +} + + +////////////////////////////////////////////////////////////////////////// +uint32 AdScene::getAlphaAt(int x, int y, bool colorCheck) { + if (!_gameRef->_debugDebugMode) { + colorCheck = false; + } + + uint32 ret; + if (colorCheck) { + ret = 0xFFFF0000; + } else { + ret = 0xFFFFFFFF; + } + + if (_mainLayer) { + for (int i = _mainLayer->_nodes.size() - 1; i >= 0; i--) { + AdSceneNode *node = _mainLayer->_nodes[i]; + if (node->_type == OBJECT_REGION && node->_region->_active && (colorCheck || !node->_region->isBlocked()) && node->_region->pointInRegion(x, y)) { + if (!node->_region->isBlocked()) { + ret = node->_region->getAlpha(); + } + break; + } + } + } + return ret; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::isBlockedAt(int x, int y, bool checkFreeObjects, BaseObject *requester) { + bool ret = true; + + if (checkFreeObjects) { + for (uint32 i = 0; i < _objects.size(); i++) { + if (_objects[i]->_active && _objects[i] != requester && _objects[i]->_currentBlockRegion) { + if (_objects[i]->_currentBlockRegion->pointInRegion(x, y)) { + return true; + } + } + } + AdGame *adGame = (AdGame *)_gameRef; + for (uint32 i = 0; i < adGame->_objects.size(); i++) { + if (adGame->_objects[i]->_active && adGame->_objects[i] != requester && adGame->_objects[i]->_currentBlockRegion) { + if (adGame->_objects[i]->_currentBlockRegion->pointInRegion(x, y)) { + return true; + } + } + } + } + + + if (_mainLayer) { + for (uint32 i = 0; i < _mainLayer->_nodes.size(); i++) { + AdSceneNode *node = _mainLayer->_nodes[i]; + /* + if (Node->_type == OBJECT_REGION && Node->_region->_active && Node->_region->_blocked && Node->_region->PointInRegion(X, Y)) + { + ret = true; + break; + } + */ + if (node->_type == OBJECT_REGION && node->_region->_active && !node->_region->hasDecoration() && node->_region->pointInRegion(x, y)) { + if (node->_region->isBlocked()) { + ret = true; + break; + } else { + ret = false; + } + } + } + } + return ret; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::isWalkableAt(int x, int y, bool checkFreeObjects, BaseObject *requester) { + bool ret = false; + + if (checkFreeObjects) { + for (uint32 i = 0; i < _objects.size(); i++) { + if (_objects[i]->_active && _objects[i] != requester && _objects[i]->_currentBlockRegion) { + if (_objects[i]->_currentBlockRegion->pointInRegion(x, y)) { + return false; + } + } + } + AdGame *adGame = (AdGame *)_gameRef; + for (uint32 i = 0; i < adGame->_objects.size(); i++) { + if (adGame->_objects[i]->_active && adGame->_objects[i] != requester && adGame->_objects[i]->_currentBlockRegion) { + if (adGame->_objects[i]->_currentBlockRegion->pointInRegion(x, y)) { + return false; + } + } + } + } + + + if (_mainLayer) { + for (uint32 i = 0; i < _mainLayer->_nodes.size(); i++) { + AdSceneNode *node = _mainLayer->_nodes[i]; + if (node->_type == OBJECT_REGION && node->_region->_active && !node->_region->hasDecoration() && node->_region->pointInRegion(x, y)) { + if (node->_region->isBlocked()) { + ret = false; + break; + } else { + ret = true; + } + } + } + } + return ret; +} + + +////////////////////////////////////////////////////////////////////////// +int AdScene::getPointsDist(BasePoint p1, BasePoint p2, BaseObject *requester) { + double xStep, yStep, x, y; + int xLength, yLength, xCount, yCount; + int x1, y1, x2, y2; + + x1 = p1.x; + y1 = p1.y; + x2 = p2.x; + y2 = p2.y; + + xLength = abs(x2 - x1); + yLength = abs(y2 - y1); + + if (xLength > yLength) { + if (x1 > x2) { + BaseUtils::swap(&x1, &x2); + BaseUtils::swap(&y1, &y2); + } + + yStep = (double)(y2 - y1) / (double)(x2 - x1); + y = y1; + + for (xCount = x1; xCount < x2; xCount++) { + if (isBlockedAt(xCount, (int)y, true, requester)) { + return -1; + } + y += yStep; + } + } else { + if (y1 > y2) { + BaseUtils::swap(&x1, &x2); + BaseUtils::swap(&y1, &y2); + } + + xStep = (double)(x2 - x1) / (double)(y2 - y1); + x = x1; + + for (yCount = y1; yCount < y2; yCount++) { + if (isBlockedAt((int)x, yCount, true, requester)) { + return -1; + } + x += xStep; + } + } + return MAX(xLength, yLength); +} + + +////////////////////////////////////////////////////////////////////////// +void AdScene::pathFinderStep() { + int i; + // get lowest unmarked + int lowestDist = INT_MAX; + AdPathPoint *lowestPt = NULL; + + for (i = 0; i < _pfPointsNum; i++) + if (!_pfPath[i]->_marked && _pfPath[i]->_distance < lowestDist) { + lowestDist = _pfPath[i]->_distance; + lowestPt = _pfPath[i]; + } + + if (lowestPt == NULL) { // no path -> terminate PathFinder + _pfReady = true; + _pfTargetPath->setReady(true); + return; + } + + lowestPt->_marked = true; + + // target point marked, generate path and terminate + if (lowestPt->x == _pfTarget->x && lowestPt->y == _pfTarget->y) { + while (lowestPt != NULL) { + _pfTargetPath->_points.insert_at(0, new BasePoint(lowestPt->x, lowestPt->y)); + lowestPt = lowestPt->_origin; + } + + _pfReady = true; + _pfTargetPath->setReady(true); + return; + } + + // otherwise keep on searching + for (i = 0; i < _pfPointsNum; i++) + if (!_pfPath[i]->_marked) { + int j = getPointsDist(*lowestPt, *_pfPath[i], _pfRequester); + if (j != -1 && lowestPt->_distance + j < _pfPath[i]->_distance) { + _pfPath[i]->_distance = lowestPt->_distance + j; + _pfPath[i]->_origin = lowestPt; + } + } +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::initLoop() { +#ifdef _DEBUGxxxx + int nu_steps = 0; + uint32 start = _gameRef->_currentTime; + while (!_pfReady && g_system->getMillis() - start <= _pfMaxTime) { + PathFinderStep(); + nu_steps++; + } + if (nu_steps > 0) { + _gameRef->LOG(0, "STAT: PathFinder iterations in one loop: %d (%s) _pfMaxTime=%d", nu_steps, _pfReady ? "finished" : "not yet done", _pfMaxTime); + } +#else + uint32 start = _gameRef->_currentTime; + while (!_pfReady && g_system->getMillis() - start <= _pfMaxTime) { + pathFinderStep(); + } +#endif + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::loadFile(const char *filename) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "AdScene::LoadFile failed for file '%s'", filename); + return STATUS_FAILED; + } + + bool ret; + + setFilename(filename); + + if (DID_FAIL(ret = loadBuffer(buffer, true))) { + _gameRef->LOG(0, "Error parsing SCENE file '%s'", filename); + } + + setFilename(filename); + + delete[] buffer; + + return ret; +} + + +TOKEN_DEF_START +TOKEN_DEF(SCENE) +TOKEN_DEF(TEMPLATE) +TOKEN_DEF(NAME) +TOKEN_DEF(LAYER) +TOKEN_DEF(WAYPOINTS) +TOKEN_DEF(EVENTS) +TOKEN_DEF(CURSOR) +TOKEN_DEF(CAMERA) +TOKEN_DEF(ENTITY) +TOKEN_DEF(SCALE_LEVEL) +TOKEN_DEF(ROTATION_LEVEL) +TOKEN_DEF(EDITOR_MARGIN_H) +TOKEN_DEF(EDITOR_MARGIN_V) +TOKEN_DEF(EDITOR_COLOR_FRAME) +TOKEN_DEF(EDITOR_COLOR_ENTITY_SEL) +TOKEN_DEF(EDITOR_COLOR_REGION_SEL) +TOKEN_DEF(EDITOR_COLOR_DECORATION_SEL) +TOKEN_DEF(EDITOR_COLOR_BLOCKED_SEL) +TOKEN_DEF(EDITOR_COLOR_WAYPOINTS_SEL) +TOKEN_DEF(EDITOR_COLOR_REGION) +TOKEN_DEF(EDITOR_COLOR_DECORATION) +TOKEN_DEF(EDITOR_COLOR_BLOCKED) +TOKEN_DEF(EDITOR_COLOR_ENTITY) +TOKEN_DEF(EDITOR_COLOR_WAYPOINTS) +TOKEN_DEF(EDITOR_COLOR_SCALE) +TOKEN_DEF(EDITOR_SHOW_REGIONS) +TOKEN_DEF(EDITOR_SHOW_BLOCKED) +TOKEN_DEF(EDITOR_SHOW_DECORATION) +TOKEN_DEF(EDITOR_SHOW_ENTITIES) +TOKEN_DEF(EDITOR_SHOW_SCALE) +TOKEN_DEF(SCRIPT) +TOKEN_DEF(CAPTION) +TOKEN_DEF(PROPERTY) +TOKEN_DEF(VIEWPORT) +TOKEN_DEF(PERSISTENT_STATE_SPRITES) +TOKEN_DEF(PERSISTENT_STATE) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool AdScene::loadBuffer(byte *buffer, bool complete) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(SCENE) + TOKEN_TABLE(TEMPLATE) + TOKEN_TABLE(NAME) + TOKEN_TABLE(LAYER) + TOKEN_TABLE(WAYPOINTS) + TOKEN_TABLE(EVENTS) + TOKEN_TABLE(CURSOR) + TOKEN_TABLE(CAMERA) + TOKEN_TABLE(ENTITY) + TOKEN_TABLE(SCALE_LEVEL) + TOKEN_TABLE(ROTATION_LEVEL) + TOKEN_TABLE(EDITOR_MARGIN_H) + TOKEN_TABLE(EDITOR_MARGIN_V) + TOKEN_TABLE(EDITOR_COLOR_FRAME) + TOKEN_TABLE(EDITOR_COLOR_ENTITY_SEL) + TOKEN_TABLE(EDITOR_COLOR_REGION_SEL) + TOKEN_TABLE(EDITOR_COLOR_DECORATION_SEL) + TOKEN_TABLE(EDITOR_COLOR_BLOCKED_SEL) + TOKEN_TABLE(EDITOR_COLOR_WAYPOINTS_SEL) + TOKEN_TABLE(EDITOR_COLOR_REGION) + TOKEN_TABLE(EDITOR_COLOR_DECORATION) + TOKEN_TABLE(EDITOR_COLOR_BLOCKED) + TOKEN_TABLE(EDITOR_COLOR_ENTITY) + TOKEN_TABLE(EDITOR_COLOR_WAYPOINTS) + TOKEN_TABLE(EDITOR_COLOR_SCALE) + TOKEN_TABLE(EDITOR_SHOW_REGIONS) + TOKEN_TABLE(EDITOR_SHOW_DECORATION) + TOKEN_TABLE(EDITOR_SHOW_BLOCKED) + TOKEN_TABLE(EDITOR_SHOW_ENTITIES) + TOKEN_TABLE(EDITOR_SHOW_SCALE) + TOKEN_TABLE(SCRIPT) + TOKEN_TABLE(CAPTION) + TOKEN_TABLE(PROPERTY) + TOKEN_TABLE(VIEWPORT) + TOKEN_TABLE(PERSISTENT_STATE_SPRITES) + TOKEN_TABLE(PERSISTENT_STATE) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE_END + + cleanup(); + + byte *params; + int cmd; + BaseParser parser; + + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_SCENE) { + _gameRef->LOG(0, "'SCENE' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + int ar, ag, ab, aa; + char camera[MAX_PATH_LENGTH] = ""; + /* float waypointHeight = -1.0f; */ + + while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_TEMPLATE: + if (DID_FAIL(loadFile((char *)params))) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_NAME: + setName((char *)params); + break; + + case TOKEN_CAPTION: + setCaption((char *)params); + break; + + case TOKEN_LAYER: { + AdLayer *layer = new AdLayer(_gameRef); + if (!layer || DID_FAIL(layer->loadBuffer(params, false))) { + cmd = PARSERR_GENERIC; + delete layer; + layer = NULL; + } else { + _gameRef->registerObject(layer); + _layers.add(layer); + if (layer->_main) { + _mainLayer = layer; + _width = layer->_width; + _height = layer->_height; + } + } + } + break; + + case TOKEN_WAYPOINTS: { + AdWaypointGroup *wpt = new AdWaypointGroup(_gameRef); + if (!wpt || DID_FAIL(wpt->loadBuffer(params, false))) { + cmd = PARSERR_GENERIC; + delete wpt; + wpt = NULL; + } else { + _gameRef->registerObject(wpt); + _waypointGroups.add(wpt); + } + } + break; + + case TOKEN_SCALE_LEVEL: { + AdScaleLevel *sl = new AdScaleLevel(_gameRef); + if (!sl || DID_FAIL(sl->loadBuffer(params, false))) { + cmd = PARSERR_GENERIC; + delete sl; + sl = NULL; + } else { + _gameRef->registerObject(sl); + _scaleLevels.add(sl); + } + } + break; + + case TOKEN_ROTATION_LEVEL: { + AdRotLevel *rl = new AdRotLevel(_gameRef); + if (!rl || DID_FAIL(rl->loadBuffer(params, false))) { + cmd = PARSERR_GENERIC; + delete rl; + rl = NULL; + } else { + _gameRef->registerObject(rl); + _rotLevels.add(rl); + } + } + break; + + case TOKEN_ENTITY: { + AdEntity *entity = new AdEntity(_gameRef); + if (!entity || DID_FAIL(entity->loadBuffer(params, false))) { + cmd = PARSERR_GENERIC; + delete entity; + entity = NULL; + } else { + addObject(entity); + } + } + break; + + case TOKEN_CURSOR: + delete _cursor; + _cursor = new BaseSprite(_gameRef); + if (!_cursor || DID_FAIL(_cursor->loadFile((char *)params))) { + delete _cursor; + _cursor = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_CAMERA: + strcpy(camera, (char *)params); + break; + + case TOKEN_EDITOR_MARGIN_H: + parser.scanStr((char *)params, "%d", &_editorMarginH); + break; + + case TOKEN_EDITOR_MARGIN_V: + parser.scanStr((char *)params, "%d", &_editorMarginV); + break; + + case TOKEN_EDITOR_COLOR_FRAME: + parser.scanStr((char *)params, "%d,%d,%d,%d", &ar, &ag, &ab, &aa); + _editorColFrame = BYTETORGBA(ar, ag, ab, aa); + break; + + case TOKEN_EDITOR_COLOR_ENTITY: + parser.scanStr((char *)params, "%d,%d,%d,%d", &ar, &ag, &ab, &aa); + _editorColEntity = BYTETORGBA(ar, ag, ab, aa); + break; + + case TOKEN_EDITOR_COLOR_ENTITY_SEL: + parser.scanStr((char *)params, "%d,%d,%d,%d", &ar, &ag, &ab, &aa); + _editorColEntitySel = BYTETORGBA(ar, ag, ab, aa); + break; + + case TOKEN_EDITOR_COLOR_REGION_SEL: + parser.scanStr((char *)params, "%d,%d,%d,%d", &ar, &ag, &ab, &aa); + _editorColRegionSel = BYTETORGBA(ar, ag, ab, aa); + break; + + case TOKEN_EDITOR_COLOR_DECORATION_SEL: + parser.scanStr((char *)params, "%d,%d,%d,%d", &ar, &ag, &ab, &aa); + _editorColDecorSel = BYTETORGBA(ar, ag, ab, aa); + break; + + case TOKEN_EDITOR_COLOR_BLOCKED_SEL: + parser.scanStr((char *)params, "%d,%d,%d,%d", &ar, &ag, &ab, &aa); + _editorColBlockedSel = BYTETORGBA(ar, ag, ab, aa); + break; + + case TOKEN_EDITOR_COLOR_WAYPOINTS_SEL: + parser.scanStr((char *)params, "%d,%d,%d,%d", &ar, &ag, &ab, &aa); + _editorColWaypointsSel = BYTETORGBA(ar, ag, ab, aa); + break; + + case TOKEN_EDITOR_COLOR_REGION: + parser.scanStr((char *)params, "%d,%d,%d,%d", &ar, &ag, &ab, &aa); + _editorColRegion = BYTETORGBA(ar, ag, ab, aa); + break; + + case TOKEN_EDITOR_COLOR_DECORATION: + parser.scanStr((char *)params, "%d,%d,%d,%d", &ar, &ag, &ab, &aa); + _editorColDecor = BYTETORGBA(ar, ag, ab, aa); + break; + + case TOKEN_EDITOR_COLOR_BLOCKED: + parser.scanStr((char *)params, "%d,%d,%d,%d", &ar, &ag, &ab, &aa); + _editorColBlocked = BYTETORGBA(ar, ag, ab, aa); + break; + + case TOKEN_EDITOR_COLOR_WAYPOINTS: + parser.scanStr((char *)params, "%d,%d,%d,%d", &ar, &ag, &ab, &aa); + _editorColWaypoints = BYTETORGBA(ar, ag, ab, aa); + break; + + case TOKEN_EDITOR_COLOR_SCALE: + parser.scanStr((char *)params, "%d,%d,%d,%d", &ar, &ag, &ab, &aa); + _editorColScale = BYTETORGBA(ar, ag, ab, aa); + break; + + case TOKEN_EDITOR_SHOW_REGIONS: + parser.scanStr((char *)params, "%b", &_editorShowRegions); + break; + + case TOKEN_EDITOR_SHOW_BLOCKED: + parser.scanStr((char *)params, "%b", &_editorShowBlocked); + break; + + case TOKEN_EDITOR_SHOW_DECORATION: + parser.scanStr((char *)params, "%b", &_editorShowDecor); + break; + + case TOKEN_EDITOR_SHOW_ENTITIES: + parser.scanStr((char *)params, "%b", &_editorShowEntities); + break; + + case TOKEN_EDITOR_SHOW_SCALE: + parser.scanStr((char *)params, "%b", &_editorShowScale); + break; + + case TOKEN_SCRIPT: + addScript((char *)params); + break; + + case TOKEN_PROPERTY: + parseProperty(params, false); + break; + + case TOKEN_VIEWPORT: { + Rect32 rc; + parser.scanStr((char *)params, "%d,%d,%d,%d", &rc.left, &rc.top, &rc.right, &rc.bottom); + if (!_viewport) { + _viewport = new BaseViewport(_gameRef); + } + if (_viewport) { + _viewport->setRect(rc.left, rc.top, rc.right, rc.bottom, true); + } + } + + case TOKEN_PERSISTENT_STATE: + parser.scanStr((char *)params, "%b", &_persistentState); + break; + + case TOKEN_PERSISTENT_STATE_SPRITES: + parser.scanStr((char *)params, "%b", &_persistentStateSprites); + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty(params, false); + break; + + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in SCENE definition"); + return STATUS_FAILED; + } + + if (_mainLayer == NULL) { + _gameRef->LOG(0, "Warning: scene '%s' has no main layer.", getFilename()); + } + + + sortScaleLevels(); + sortRotLevels(); + + _initialized = true; + + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool AdScene::traverseNodes(bool doUpdate) { + if (!_initialized) { + return STATUS_OK; + } + + AdGame *adGame = (AdGame *)_gameRef; + + + ////////////////////////////////////////////////////////////////////////// + // prepare viewport + bool popViewport = false; + if (_viewport && !_gameRef->_editorMode) { + _gameRef->pushViewport(_viewport); + popViewport = true; + } else if (adGame->_sceneViewport && !_gameRef->_editorMode) { + _gameRef->pushViewport(adGame->_sceneViewport); + popViewport = true; + } + + + ////////////////////////////////////////////////////////////////////////// + // *** adjust scroll offset + if (doUpdate) { + /* + if (_autoScroll && _gameRef->_mainObject != NULL) + { + ScrollToObject(_gameRef->_mainObject); + } + */ + + if (_autoScroll) { + // adjust horizontal scroll + if (_gameRef->_timer - _lastTimeH >= _scrollTimeH) { + int timesMissed = (_gameRef->_timer - _lastTimeH) / _scrollTimeH; + // Cap the amount of catch-up to avoid jittery characters. + if (timesMissed > 2) { + timesMissed = 2; + } + _lastTimeH = _gameRef->_timer; + if (_offsetLeft < _targetOffsetLeft) { + _offsetLeft += _scrollPixelsH * timesMissed; + _offsetLeft = MIN(_offsetLeft, _targetOffsetLeft); + } else if (_offsetLeft > _targetOffsetLeft) { + _offsetLeft -= _scrollPixelsH * timesMissed; + _offsetLeft = MAX(_offsetLeft, _targetOffsetLeft); + } + } + + // adjust vertical scroll + if (_gameRef->_timer - _lastTimeV >= _scrollTimeV) { + int timesMissed = (_gameRef->_timer - _lastTimeV) / _scrollTimeV; + // Cap the amount of catch-up to avoid jittery characters. + if (timesMissed > 2) { + timesMissed = 2; + } + _lastTimeV = _gameRef->_timer; + if (_offsetTop < _targetOffsetTop) { + _offsetTop += _scrollPixelsV * timesMissed; + _offsetTop = MIN(_offsetTop, _targetOffsetTop); + } else if (_offsetTop > _targetOffsetTop) { + _offsetTop -= _scrollPixelsV * timesMissed; + _offsetTop = MAX(_offsetTop, _targetOffsetTop); + } + } + + if (_offsetTop == _targetOffsetTop && _offsetLeft == _targetOffsetLeft) { + _ready = true; + } + } else { + _ready = true; // not scrolling, i.e. always ready + } + } + + + + + ////////////////////////////////////////////////////////////////////////// + int viewportWidth, viewportHeight; + getViewportSize(&viewportWidth, &viewportHeight); + + int viewportX, viewportY; + getViewportOffset(&viewportX, &viewportY); + + int scrollableX = _width - viewportWidth; + int scrollableY = _height - viewportHeight; + + double widthRatio = scrollableX <= 0 ? 0 : ((double)(_offsetLeft) / (double)scrollableX); + double heightRatio = scrollableY <= 0 ? 0 : ((double)(_offsetTop) / (double)scrollableY); + + int origX, origY; + _gameRef->getOffset(&origX, &origY); + + + + ////////////////////////////////////////////////////////////////////////// + // *** display/update everything + _gameRef->_renderer->setup2D(); + + // for each layer + /* int mainOffsetX = 0; */ + /* int mainOffsetY = 0; */ + + for (uint32 j = 0; j < _layers.size(); j++) { + if (!_layers[j]->_active) { + continue; + } + + // make layer exclusive + if (!doUpdate) { + if (_layers[j]->_closeUp && !_gameRef->_editorMode) { + if (!_shieldWindow) { + _shieldWindow = new UIWindow(_gameRef); + } + if (_shieldWindow) { + _shieldWindow->_posX = _shieldWindow->_posY = 0; + _shieldWindow->_width = _gameRef->_renderer->_width; + _shieldWindow->_height = _gameRef->_renderer->_height; + _shieldWindow->display(); + } + } + } + + if (_paralaxScrolling) { + int offsetX = (int)(widthRatio * (_layers[j]->_width - viewportWidth) - viewportX); + int offsetY = (int)(heightRatio * (_layers[j]->_height - viewportHeight) - viewportY); + _gameRef->setOffset(offsetX, offsetY); + + _gameRef->_offsetPercentX = (float)offsetX / ((float)_layers[j]->_width - viewportWidth) * 100.0f; + _gameRef->_offsetPercentY = (float)offsetY / ((float)_layers[j]->_height - viewportHeight) * 100.0f; + + //_gameRef->QuickMessageForm("%d %f", OffsetX+ViewportX, _gameRef->_offsetPercentX); + } else { + _gameRef->setOffset(_offsetLeft - viewportX, _offsetTop - viewportY); + + _gameRef->_offsetPercentX = (float)(_offsetLeft - viewportX) / ((float)_layers[j]->_width - viewportWidth) * 100.0f; + _gameRef->_offsetPercentY = (float)(_offsetTop - viewportY) / ((float)_layers[j]->_height - viewportHeight) * 100.0f; + } + + + // for each node + for (uint32 k = 0; k < _layers[j]->_nodes.size(); k++) { + AdSceneNode *node = _layers[j]->_nodes[k]; + switch (node->_type) { + case OBJECT_ENTITY: + if (node->_entity->_active && (_gameRef->_editorMode || !node->_entity->_editorOnly)) { + _gameRef->_renderer->setup2D(); + + if (doUpdate) { + node->_entity->update(); + } else { + node->_entity->display(); + } + } + break; + + case OBJECT_REGION: { + if (node->_region->isBlocked()) { + break; + } + if (node->_region->hasDecoration()) { + break; + } + + if (!doUpdate) { + displayRegionContent(node->_region); + } + } + break; + default: + error("AdScene::TraverseNodes - Unhandled enum"); + break; + } // switch + } // each node + + // display/update all objects which are off-regions + if (_layers[j]->_main) { + if (doUpdate) { + updateFreeObjects(); + } else { + displayRegionContent(NULL); + } + } + } // each layer + + + // restore state + _gameRef->setOffset(origX, origY); + _gameRef->_renderer->setup2D(); + + // display/update fader + if (_fader) { + if (doUpdate) { + _fader->update(); + } else { + _fader->display(); + } + } + + if (popViewport) { + _gameRef->popViewport(); + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::display() { + return traverseNodes(false); +} + +////////////////////////////////////////////////////////////////////////// +bool AdScene::updateFreeObjects() { + AdGame *adGame = (AdGame *)_gameRef; + // 3D-code removed + // bool is3DSet; + + // *** update all active objects + // is3DSet = false; + for (uint32 i = 0; i < adGame->_objects.size(); i++) { + if (!adGame->_objects[i]->_active) { + continue; + } + // 3D-code removed + adGame->_objects[i]->update(); + adGame->_objects[i]->_drawn = false; + } + + + for (uint32 i = 0; i < _objects.size(); i++) { + if (!_objects[i]->_active) { + continue; + } + + _objects[i]->update(); + _objects[i]->_drawn = false; + } + + + if (_autoScroll && _gameRef->_mainObject != NULL) { + scrollToObject(_gameRef->_mainObject); + } + + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::displayRegionContent(AdRegion *region, bool display3DOnly) { + AdGame *adGame = (AdGame *)_gameRef; + Common::Array<AdObject *> objects; + AdObject *obj; + + // global objects + for (uint32 i = 0; i < adGame->_objects.size(); i++) { + obj = adGame->_objects[i]; + if (obj->_active && !obj->_drawn && (obj->_stickRegion == region || region == NULL || (obj->_stickRegion == NULL && region->pointInRegion(obj->_posX, obj->_posY)))) { + objects.push_back(obj); + } + } + + // scene objects + for (uint32 i = 0; i < _objects.size(); i++) { + obj = _objects[i]; + if (obj->_active && !obj->_editorOnly && !obj->_drawn && (obj->_stickRegion == region || region == NULL || (obj->_stickRegion == NULL && region->pointInRegion(obj->_posX, obj->_posY)))) { + objects.push_back(obj); + } + } + + // sort by _posY + Common::sort(objects.begin(), objects.end(), AdScene::compareObjs); + + // display them + for (uint32 i = 0; i < objects.size(); i++) { + obj = objects[i]; + + if (display3DOnly && !obj->_is3D) { + continue; + } + + _gameRef->_renderer->setup2D(); + + if (_gameRef->_editorMode || !obj->_editorOnly) { + obj->display(); + } + obj->_drawn = true; + } + + + // display design only objects + if (!display3DOnly) { + if (_gameRef->_editorMode && region == NULL) { + for (uint32 i = 0; i < _objects.size(); i++) { + if (_objects[i]->_active && _objects[i]->_editorOnly) { + _objects[i]->display(); + _objects[i]->_drawn = true; + } + } + } + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool AdScene::compareObjs(const AdObject *obj1, const AdObject *obj2) { + if (obj1->_posY < obj2->_posY) { + return true; + } else { + return false; + } +} + +////////////////////////////////////////////////////////////////////////// +bool AdScene::displayRegionContentOld(AdRegion *region) { + AdGame *adGame = (AdGame *)_gameRef; + AdObject *obj; + + // display all objects in region sorted by _posY + do { + obj = NULL; + int minY = INT_MAX; + + // global objects + for (uint32 i = 0; i < adGame->_objects.size(); i++) { + if (adGame->_objects[i]->_active && !adGame->_objects[i]->_drawn && adGame->_objects[i]->_posY < minY && (adGame->_objects[i]->_stickRegion == region || region == NULL || (adGame->_objects[i]->_stickRegion == NULL && region->pointInRegion(adGame->_objects[i]->_posX, adGame->_objects[i]->_posY)))) { + obj = adGame->_objects[i]; + minY = adGame->_objects[i]->_posY; + } + } + + // scene objects + for (uint32 i = 0; i < _objects.size(); i++) { + if (_objects[i]->_active && !_objects[i]->_editorOnly && !_objects[i]->_drawn && _objects[i]->_posY < minY && (_objects[i]->_stickRegion == region || region == NULL || (_objects[i]->_stickRegion == NULL && region->pointInRegion(_objects[i]->_posX, _objects[i]->_posY)))) { + obj = _objects[i]; + minY = _objects[i]->_posY; + } + } + + + if (obj != NULL) { + _gameRef->_renderer->setup2D(); + + if (_gameRef->_editorMode || !obj->_editorOnly) { + obj->display(); + } + obj->_drawn = true; + } + } while (obj != NULL); + + + // design only objects + if (_gameRef->_editorMode && region == NULL) { + for (uint32 i = 0; i < _objects.size(); i++) { + if (_objects[i]->_active && _objects[i]->_editorOnly) { + _objects[i]->display(); + _objects[i]->_drawn = true; + } + } + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::update() { + return traverseNodes(true); +} + +////////////////////////////////////////////////////////////////////////// +void AdScene::scrollTo(int offsetX, int offsetY) { + int viewportWidth, viewportHeight; + getViewportSize(&viewportWidth, &viewportHeight); + + int origOffsetLeft = _targetOffsetLeft; + int origOffsetTop = _targetOffsetTop; + + _targetOffsetLeft = MAX(0, offsetX - viewportWidth / 2); + _targetOffsetLeft = MIN(_targetOffsetLeft, _width - viewportWidth); + + _targetOffsetTop = MAX(0, offsetY - viewportHeight / 2); + _targetOffsetTop = MIN(_targetOffsetTop, _height - viewportHeight); + + + if (_gameRef->_mainObject && _gameRef->_mainObject->_is3D) { + if (abs(origOffsetLeft - _targetOffsetLeft) < 5) { + _targetOffsetLeft = origOffsetLeft; + } + if (abs(origOffsetTop - _targetOffsetTop) < 5) { + _targetOffsetTop = origOffsetTop; + } + //_targetOffsetTop = 0; + } + + _ready = false; +} + + +////////////////////////////////////////////////////////////////////////// +void AdScene::scrollToObject(BaseObject *object) { + if (object) { + scrollTo(object->_posX, object->_posY - object->getHeight() / 2); + } +} + + +////////////////////////////////////////////////////////////////////////// +void AdScene::skipToObject(BaseObject *object) { + if (object) { + skipTo(object->_posX, object->_posY - object->getHeight() / 2); + } +} + + +////////////////////////////////////////////////////////////////////////// +void AdScene::skipTo(int offsetX, int offsetY) { + int viewportWidth, viewportHeight; + getViewportSize(&viewportWidth, &viewportHeight); + + _offsetLeft = MAX(0, offsetX - viewportWidth / 2); + _offsetLeft = MIN(_offsetLeft, _width - viewportWidth); + + _offsetTop = MAX(0, offsetY - viewportHeight / 2); + _offsetTop = MIN(_offsetTop, _height - viewportHeight); + + _targetOffsetLeft = _offsetLeft; + _targetOffsetTop = _offsetTop; +} + + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool AdScene::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // LoadActor + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "LoadActor") == 0) { + stack->correctParams(1); + AdActor *act = new AdActor(_gameRef); + if (act && DID_SUCCEED(act->loadFile(stack->pop()->getString()))) { + addObject(act); + stack->pushNative(act, true); + } else { + delete act; + act = NULL; + stack->pushNULL(); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // LoadEntity + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "LoadEntity") == 0) { + stack->correctParams(1); + AdEntity *ent = new AdEntity(_gameRef); + if (ent && DID_SUCCEED(ent->loadFile(stack->pop()->getString()))) { + addObject(ent); + stack->pushNative(ent, true); + } else { + delete ent; + ent = NULL; + stack->pushNULL(); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // CreateEntity + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "CreateEntity") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + AdEntity *ent = new AdEntity(_gameRef); + addObject(ent); + if (!val->isNULL()) { + ent->setName(val->getString()); + } + stack->pushNative(ent, true); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // UnloadObject / UnloadActor / UnloadEntity / UnloadActor3D / DeleteEntity + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "UnloadObject") == 0 || strcmp(name, "UnloadActor") == 0 || strcmp(name, "UnloadEntity") == 0 || strcmp(name, "UnloadActor3D") == 0 || strcmp(name, "DeleteEntity") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + AdObject *obj = (AdObject *)val->getNative(); + removeObject(obj); + if (val->getType() == VAL_VARIABLE_REF) { + val->setNULL(); + } + + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SkipTo + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SkipTo") == 0) { + stack->correctParams(2); + ScValue *val1 = stack->pop(); + ScValue *val2 = stack->pop(); + if (val1->isNative()) { + skipToObject((BaseObject *)val1->getNative()); + } else { + skipTo(val1->getInt(), val2->getInt()); + } + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ScrollTo / ScrollToAsync + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ScrollTo") == 0 || strcmp(name, "ScrollToAsync") == 0) { + stack->correctParams(2); + ScValue *val1 = stack->pop(); + ScValue *val2 = stack->pop(); + if (val1->isNative()) { + scrollToObject((BaseObject *)val1->getNative()); + } else { + scrollTo(val1->getInt(), val2->getInt()); + } + if (strcmp(name, "ScrollTo") == 0) { + script->waitForExclusive(this); + } + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetLayer + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetLayer") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + if (val->isInt()) { + int layer = val->getInt(); + if (layer < 0 || layer >= (int32)_layers.size()) { + stack->pushNULL(); + } else { + stack->pushNative(_layers[layer], true); + } + } else { + const char *layerName = val->getString(); + bool layerFound = false; + for (uint32 i = 0; i < _layers.size(); i++) { + if (scumm_stricmp(layerName, _layers[i]->getName()) == 0) { + stack->pushNative(_layers[i], true); + layerFound = true; + break; + } + } + if (!layerFound) { + stack->pushNULL(); + } + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetWaypointGroup + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetWaypointGroup") == 0) { + stack->correctParams(1); + int group = stack->pop()->getInt(); + if (group < 0 || group >= (int32)_waypointGroups.size()) { + stack->pushNULL(); + } else { + stack->pushNative(_waypointGroups[group], true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetNode + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetNode") == 0) { + stack->correctParams(1); + const char *nodeName = stack->pop()->getString(); + + BaseObject *node = getNodeByName(nodeName); + if (node) { + stack->pushNative((BaseScriptable *)node, true); + } else { + stack->pushNULL(); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetFreeNode + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetFreeNode") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + AdObject *ret = NULL; + if (val->isInt()) { + int index = val->getInt(); + if (index >= 0 && index < (int32)_objects.size()) { + ret = _objects[index]; + } + } else { + const char *nodeName = val->getString(); + for (uint32 i = 0; i < _objects.size(); i++) { + if (_objects[i] && _objects[i]->getName() && scumm_stricmp(_objects[i]->getName(), nodeName) == 0) { + ret = _objects[i]; + break; + } + } + } + if (ret) { + stack->pushNative(ret, true); + } else { + stack->pushNULL(); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetRegionAt + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetRegionAt") == 0) { + stack->correctParams(3); + int x = stack->pop()->getInt(); + int y = stack->pop()->getInt(); + ScValue *val = stack->pop(); + + bool includeDecors = false; + if (!val->isNULL()) { + includeDecors = val->getBool(); + } + + if (_mainLayer) { + for (int i = _mainLayer->_nodes.size() - 1; i >= 0; i--) { + AdSceneNode *node = _mainLayer->_nodes[i]; + if (node->_type == OBJECT_REGION && node->_region->_active && node->_region->pointInRegion(x, y)) { + if (node->_region->hasDecoration() && !includeDecors) { + continue; + } + + stack->pushNative(node->_region, true); + return STATUS_OK; + } + } + } + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // IsBlockedAt + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "IsBlockedAt") == 0) { + stack->correctParams(2); + int x = stack->pop()->getInt(); + int y = stack->pop()->getInt(); + + stack->pushBool(isBlockedAt(x, y)); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // IsWalkableAt + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "IsWalkableAt") == 0) { + stack->correctParams(2); + int x = stack->pop()->getInt(); + int y = stack->pop()->getInt(); + + stack->pushBool(isWalkableAt(x, y)); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetScaleAt + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetScaleAt") == 0) { + stack->correctParams(2); + int x = stack->pop()->getInt(); + int y = stack->pop()->getInt(); + + stack->pushFloat(getZoomAt(x, y)); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetRotationAt + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetRotationAt") == 0) { + stack->correctParams(2); + int x = stack->pop()->getInt(); + int y = stack->pop()->getInt(); + + stack->pushFloat(getRotationAt(x, y)); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // IsScrolling + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "IsScrolling") == 0) { + stack->correctParams(0); + bool ret = false; + if (_autoScroll) { + if (_targetOffsetLeft != _offsetLeft || _targetOffsetTop != _offsetTop) { + ret = true; + } + } + + stack->pushBool(ret); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // FadeOut / FadeOutAsync + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "FadeOut") == 0 || strcmp(name, "FadeOutAsync") == 0) { + stack->correctParams(5); + uint32 duration = stack->pop()->getInt(500); + byte red = stack->pop()->getInt(0); + byte green = stack->pop()->getInt(0); + byte blue = stack->pop()->getInt(0); + byte alpha = stack->pop()->getInt(0xFF); + + _fader->fadeOut(BYTETORGBA(red, green, blue, alpha), duration); + if (strcmp(name, "FadeOutAsync") != 0) { + script->waitFor(_fader); + } + + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // FadeIn / FadeInAsync + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "FadeIn") == 0 || strcmp(name, "FadeInAsync") == 0) { + stack->correctParams(5); + uint32 duration = stack->pop()->getInt(500); + byte red = stack->pop()->getInt(0); + byte green = stack->pop()->getInt(0); + byte blue = stack->pop()->getInt(0); + byte alpha = stack->pop()->getInt(0xFF); + + _fader->fadeIn(BYTETORGBA(red, green, blue, alpha), duration); + if (strcmp(name, "FadeInAsync") != 0) { + script->waitFor(_fader); + } + + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetFadeColor + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetFadeColor") == 0) { + stack->correctParams(0); + stack->pushInt(_fader->getCurrentColor()); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // IsPointInViewport + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "IsPointInViewport") == 0) { + stack->correctParams(2); + int x = stack->pop()->getInt(); + int y = stack->pop()->getInt(); + stack->pushBool(pointInViewport(x, y)); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetViewport + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetViewport") == 0) { + stack->correctParams(4); + int x = stack->pop()->getInt(); + int y = stack->pop()->getInt(); + int width = stack->pop()->getInt(); + int height = stack->pop()->getInt(); + + if (width <= 0) { + width = _gameRef->_renderer->_width; + } + if (height <= 0) { + height = _gameRef->_renderer->_height; + } + + if (!_viewport) { + _viewport = new BaseViewport(_gameRef); + } + if (_viewport) { + _viewport->setRect(x, y, x + width, y + height); + } + + stack->pushBool(true); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // AddLayer + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AddLayer") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + AdLayer *layer = new AdLayer(_gameRef); + if (!val->isNULL()) { + layer->setName(val->getString()); + } + if (_mainLayer) { + layer->_width = _mainLayer->_width; + layer->_height = _mainLayer->_height; + } + _layers.add(layer); + _gameRef->registerObject(layer); + + stack->pushNative(layer, true); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // InsertLayer + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "InsertLayer") == 0) { + stack->correctParams(2); + int index = stack->pop()->getInt(); + ScValue *val = stack->pop(); + + AdLayer *layer = new AdLayer(_gameRef); + if (!val->isNULL()) { + layer->setName(val->getString()); + } + if (_mainLayer) { + layer->_width = _mainLayer->_width; + layer->_height = _mainLayer->_height; + } + if (index < 0) { + index = 0; + } + if (index <= (int32)_layers.size() - 1) { + _layers.insert_at(index, layer); + } else { + _layers.add(layer); + } + + _gameRef->registerObject(layer); + + stack->pushNative(layer, true); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // DeleteLayer + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "DeleteLayer") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + AdLayer *toDelete = NULL; + if (val->isNative()) { + BaseScriptable *temp = val->getNative(); + for (uint32 i = 0; i < _layers.size(); i++) { + if (_layers[i] == temp) { + toDelete = _layers[i]; + break; + } + } + } else { + int index = val->getInt(); + if (index >= 0 && index < (int32)_layers.size()) { + toDelete = _layers[index]; + } + } + if (toDelete == NULL) { + stack->pushBool(false); + return STATUS_OK; + } + + if (toDelete->_main) { + script->runtimeError("Scene.DeleteLayer - cannot delete main scene layer"); + stack->pushBool(false); + return STATUS_OK; + } + + for (uint32 i = 0; i < _layers.size(); i++) { + if (_layers[i] == toDelete) { + _layers.remove_at(i); + _gameRef->unregisterObject(toDelete); + break; + } + } + stack->pushBool(true); + return STATUS_OK; + } else { + return BaseObject::scCallMethod(script, stack, thisStack, name); + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *AdScene::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("scene"); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // NumLayers (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "NumLayers") { + _scValue->setInt(_layers.size()); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // NumWaypointGroups (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "NumWaypointGroups") { + _scValue->setInt(_waypointGroups.size()); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // MainLayer (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "MainLayer") { + if (_mainLayer) { + _scValue->setNative(_mainLayer, true); + } else { + _scValue->setNULL(); + } + + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // NumFreeNodes (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "NumFreeNodes") { + _scValue->setInt(_objects.size()); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // MouseX (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "MouseX") { + int viewportX; + getViewportOffset(&viewportX); + + _scValue->setInt(_gameRef->_mousePos.x + _offsetLeft - viewportX); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // MouseY (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "MouseY") { + int viewportY; + getViewportOffset(NULL, &viewportY); + + _scValue->setInt(_gameRef->_mousePos.y + _offsetTop - viewportY); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // AutoScroll + ////////////////////////////////////////////////////////////////////////// + else if (name == "AutoScroll") { + _scValue->setBool(_autoScroll); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // PersistentState + ////////////////////////////////////////////////////////////////////////// + else if (name == "PersistentState") { + _scValue->setBool(_persistentState); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // PersistentStateSprites + ////////////////////////////////////////////////////////////////////////// + else if (name == "PersistentStateSprites") { + _scValue->setBool(_persistentStateSprites); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // ScrollPixelsX + ////////////////////////////////////////////////////////////////////////// + else if (name == "ScrollPixelsX") { + _scValue->setInt(_scrollPixelsH); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // ScrollPixelsY + ////////////////////////////////////////////////////////////////////////// + else if (name == "ScrollPixelsY") { + _scValue->setInt(_scrollPixelsV); + return _scValue; + } + + + ////////////////////////////////////////////////////////////////////////// + // ScrollSpeedX + ////////////////////////////////////////////////////////////////////////// + else if (name == "ScrollSpeedX") { + _scValue->setInt(_scrollTimeH); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // ScrollSpeedY + ////////////////////////////////////////////////////////////////////////// + else if (name == "ScrollSpeedY") { + _scValue->setInt(_scrollTimeV); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // OffsetX + ////////////////////////////////////////////////////////////////////////// + else if (name == "OffsetX") { + _scValue->setInt(_offsetLeft); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // OffsetY + ////////////////////////////////////////////////////////////////////////// + else if (name == "OffsetY") { + _scValue->setInt(_offsetTop); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Width (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "Width") { + if (_mainLayer) { + _scValue->setInt(_mainLayer->_width); + } else { + _scValue->setInt(0); + } + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Height (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "Height") { + if (_mainLayer) { + _scValue->setInt(_mainLayer->_height); + } else { + _scValue->setInt(0); + } + return _scValue; + } else { + return BaseObject::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::scSetProperty(const char *name, ScValue *value) { + ////////////////////////////////////////////////////////////////////////// + // Name + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Name") == 0) { + setName(value->getString()); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // AutoScroll + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AutoScroll") == 0) { + _autoScroll = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // PersistentState + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "PersistentState") == 0) { + _persistentState = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // PersistentStateSprites + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "PersistentStateSprites") == 0) { + _persistentStateSprites = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ScrollPixelsX + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ScrollPixelsX") == 0) { + _scrollPixelsH = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ScrollPixelsY + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ScrollPixelsY") == 0) { + _scrollPixelsV = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ScrollSpeedX + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ScrollSpeedX") == 0) { + _scrollTimeH = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ScrollSpeedY + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ScrollSpeedY") == 0) { + _scrollTimeV = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // OffsetX + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "OffsetX") == 0) { + _offsetLeft = value->getInt(); + + int viewportWidth, viewportHeight; + getViewportSize(&viewportWidth, &viewportHeight); + + _offsetLeft = MAX(0, _offsetLeft - viewportWidth / 2); + _offsetLeft = MIN(_offsetLeft, _width - viewportWidth); + _targetOffsetLeft = _offsetLeft; + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // OffsetY + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "OffsetY") == 0) { + _offsetTop = value->getInt(); + + int viewportWidth, viewportHeight; + getViewportSize(&viewportWidth, &viewportHeight); + + _offsetTop = MAX(0, _offsetTop - viewportHeight / 2); + _offsetTop = MIN(_offsetTop, _height - viewportHeight); + _targetOffsetTop = _offsetTop; + + return STATUS_OK; + } else { + return BaseObject::scSetProperty(name, value); + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *AdScene::scToString() { + return "[scene object]"; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::addObject(AdObject *object) { + _objects.add(object); + return _gameRef->registerObject(object); +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::removeObject(AdObject *object) { + for (uint32 i = 0; i < _objects.size(); i++) { + if (_objects[i] == object) { + _objects.remove_at(i); + return _gameRef->unregisterObject(object); + } + } + return STATUS_FAILED; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::saveAsText(BaseDynamicBuffer *buffer, int indent) { + buffer->putTextIndent(indent, "SCENE {\n"); + + buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", getName()); + buffer->putTextIndent(indent + 2, "CAPTION=\"%s\"\n", getCaption()); + + if (_persistentState) { + buffer->putTextIndent(indent + 2, "PERSISTENT_STATE=%s\n", _persistentState ? "TRUE" : "FALSE"); + } + + if (!_persistentStateSprites) { + buffer->putTextIndent(indent + 2, "PERSISTENT_STATE_SPRITES=%s\n", _persistentStateSprites ? "TRUE" : "FALSE"); + } + + + // scripts + for (uint32 i = 0; i < _scripts.size(); i++) { + buffer->putTextIndent(indent + 2, "SCRIPT=\"%s\"\n", _scripts[i]->_filename); + } + + buffer->putTextIndent(indent + 2, "\n"); + + // properties + if (_scProp) { + _scProp->saveAsText(buffer, indent + 2); + } + + // viewport + if (_viewport) { + Rect32 *rc = _viewport->getRect(); + buffer->putTextIndent(indent + 2, "VIEWPORT { %d, %d, %d, %d }\n", rc->left, rc->top, rc->right, rc->bottom); + } + + + + // editor settings + buffer->putTextIndent(indent + 2, "; ----- editor settings\n"); + buffer->putTextIndent(indent + 2, "EDITOR_MARGIN_H=%d\n", _editorMarginH); + buffer->putTextIndent(indent + 2, "EDITOR_MARGIN_V=%d\n", _editorMarginV); + buffer->putTextIndent(indent + 2, "EDITOR_COLOR_FRAME { %d,%d,%d,%d }\n", RGBCOLGetR(_editorColFrame), RGBCOLGetG(_editorColFrame), RGBCOLGetB(_editorColFrame), RGBCOLGetA(_editorColFrame)); + buffer->putTextIndent(indent + 2, "EDITOR_COLOR_ENTITY_SEL { %d,%d,%d,%d }\n", RGBCOLGetR(_editorColEntitySel), RGBCOLGetG(_editorColEntitySel), RGBCOLGetB(_editorColEntitySel), RGBCOLGetA(_editorColEntitySel)); + buffer->putTextIndent(indent + 2, "EDITOR_COLOR_REGION_SEL { %d,%d,%d,%d }\n", RGBCOLGetR(_editorColRegionSel), RGBCOLGetG(_editorColRegionSel), RGBCOLGetB(_editorColRegionSel), RGBCOLGetA(_editorColRegionSel)); + buffer->putTextIndent(indent + 2, "EDITOR_COLOR_BLOCKED_SEL { %d,%d,%d,%d }\n", RGBCOLGetR(_editorColBlockedSel), RGBCOLGetG(_editorColBlockedSel), RGBCOLGetB(_editorColBlockedSel), RGBCOLGetA(_editorColBlockedSel)); + buffer->putTextIndent(indent + 2, "EDITOR_COLOR_DECORATION_SEL { %d,%d,%d,%d }\n", RGBCOLGetR(_editorColDecorSel), RGBCOLGetG(_editorColDecorSel), RGBCOLGetB(_editorColDecorSel), RGBCOLGetA(_editorColDecorSel)); + buffer->putTextIndent(indent + 2, "EDITOR_COLOR_WAYPOINTS_SEL { %d,%d,%d,%d }\n", RGBCOLGetR(_editorColWaypointsSel), RGBCOLGetG(_editorColWaypointsSel), RGBCOLGetB(_editorColWaypointsSel), RGBCOLGetA(_editorColWaypointsSel)); + buffer->putTextIndent(indent + 2, "EDITOR_COLOR_ENTITY { %d,%d,%d,%d }\n", RGBCOLGetR(_editorColEntity), RGBCOLGetG(_editorColEntity), RGBCOLGetB(_editorColEntity), RGBCOLGetA(_editorColEntity)); + buffer->putTextIndent(indent + 2, "EDITOR_COLOR_REGION { %d,%d,%d,%d }\n", RGBCOLGetR(_editorColRegion), RGBCOLGetG(_editorColRegion), RGBCOLGetB(_editorColRegion), RGBCOLGetA(_editorColRegion)); + buffer->putTextIndent(indent + 2, "EDITOR_COLOR_DECORATION { %d,%d,%d,%d }\n", RGBCOLGetR(_editorColDecor), RGBCOLGetG(_editorColDecor), RGBCOLGetB(_editorColDecor), RGBCOLGetA(_editorColDecor)); + buffer->putTextIndent(indent + 2, "EDITOR_COLOR_BLOCKED { %d,%d,%d,%d }\n", RGBCOLGetR(_editorColBlocked), RGBCOLGetG(_editorColBlocked), RGBCOLGetB(_editorColBlocked), RGBCOLGetA(_editorColBlocked)); + buffer->putTextIndent(indent + 2, "EDITOR_COLOR_WAYPOINTS { %d,%d,%d,%d }\n", RGBCOLGetR(_editorColWaypoints), RGBCOLGetG(_editorColWaypoints), RGBCOLGetB(_editorColWaypoints), RGBCOLGetA(_editorColWaypoints)); + buffer->putTextIndent(indent + 2, "EDITOR_COLOR_SCALE { %d,%d,%d,%d }\n", RGBCOLGetR(_editorColScale), RGBCOLGetG(_editorColScale), RGBCOLGetB(_editorColScale), RGBCOLGetA(_editorColScale)); + + buffer->putTextIndent(indent + 2, "EDITOR_SHOW_REGIONS=%s\n", _editorShowRegions ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "EDITOR_SHOW_BLOCKED=%s\n", _editorShowBlocked ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "EDITOR_SHOW_DECORATION=%s\n", _editorShowDecor ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "EDITOR_SHOW_ENTITIES=%s\n", _editorShowEntities ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "EDITOR_SHOW_SCALE=%s\n", _editorShowScale ? "TRUE" : "FALSE"); + + buffer->putTextIndent(indent + 2, "\n"); + + BaseClass::saveAsText(buffer, indent + 2); + + // waypoints + buffer->putTextIndent(indent + 2, "; ----- waypoints\n"); + for (uint32 i = 0; i < _waypointGroups.size(); i++) { + _waypointGroups[i]->saveAsText(buffer, indent + 2); + } + + buffer->putTextIndent(indent + 2, "\n"); + + // layers + buffer->putTextIndent(indent + 2, "; ----- layers\n"); + for (uint32 i = 0; i < _layers.size(); i++) { + _layers[i]->saveAsText(buffer, indent + 2); + } + + // scale levels + buffer->putTextIndent(indent + 2, "; ----- scale levels\n"); + for (uint32 i = 0; i < _scaleLevels.size(); i++) { + _scaleLevels[i]->saveAsText(buffer, indent + 2); + } + + // rotation levels + buffer->putTextIndent(indent + 2, "; ----- rotation levels\n"); + for (uint32 i = 0; i < _rotLevels.size(); i++) { + _rotLevels[i]->saveAsText(buffer, indent + 2); + } + + + buffer->putTextIndent(indent + 2, "\n"); + + // free entities + buffer->putTextIndent(indent + 2, "; ----- free entities\n"); + for (uint32 i = 0; i < _objects.size(); i++) { + if (_objects[i]->getType() == OBJECT_ENTITY) { + _objects[i]->saveAsText(buffer, indent + 2); + + } + } + + buffer->putTextIndent(indent, "}\n"); + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::sortScaleLevels() { + if (_scaleLevels.size() == 0) { + return STATUS_OK; + } + bool changed; + do { + changed = false; + for (uint32 i = 0; i < _scaleLevels.size() - 1; i++) { + if (_scaleLevels[i]->_posY > _scaleLevels[i + 1]->_posY) { + AdScaleLevel *sl = _scaleLevels[i]; + _scaleLevels[i] = _scaleLevels[i + 1]; + _scaleLevels[i + 1] = sl; + + changed = true; + } + } + + } while (changed); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::sortRotLevels() { + if (_rotLevels.size() == 0) { + return STATUS_OK; + } + bool changed; + do { + changed = false; + for (uint32 i = 0; i < _rotLevels.size() - 1; i++) { + if (_rotLevels[i]->_posX > _rotLevels[i + 1]->_posX) { + AdRotLevel *rl = _rotLevels[i]; + _rotLevels[i] = _rotLevels[i + 1]; + _rotLevels[i + 1] = rl; + + changed = true; + } + } + + } while (changed); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +float AdScene::getScaleAt(int Y) { + AdScaleLevel *prev = NULL; + AdScaleLevel *next = NULL; + + for (uint32 i = 0; i < _scaleLevels.size(); i++) { + /* AdScaleLevel *xxx = _scaleLevels[i];*/ + /* int j = _scaleLevels.size(); */ + if (_scaleLevels[i]->_posY < Y) { + prev = _scaleLevels[i]; + } else { + next = _scaleLevels[i]; + break; + } + } + + if (prev == NULL || next == NULL) { + return 100; + } + + int delta_y = next->_posY - prev->_posY; + float delta_scale = next->getScale() - prev->getScale(); + Y -= prev->_posY; + + float percent = (float)Y / ((float)delta_y / 100.0f); + return prev->getScale() + delta_scale / 100 * percent; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::persist(BasePersistenceManager *persistMgr) { + BaseObject::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_autoScroll)); + persistMgr->transfer(TMEMBER(_editorColBlocked)); + persistMgr->transfer(TMEMBER(_editorColBlockedSel)); + persistMgr->transfer(TMEMBER(_editorColDecor)); + persistMgr->transfer(TMEMBER(_editorColDecorSel)); + persistMgr->transfer(TMEMBER(_editorColEntity)); + persistMgr->transfer(TMEMBER(_editorColEntitySel)); + persistMgr->transfer(TMEMBER(_editorColFrame)); + persistMgr->transfer(TMEMBER(_editorColRegion)); + persistMgr->transfer(TMEMBER(_editorColRegionSel)); + persistMgr->transfer(TMEMBER(_editorColScale)); + persistMgr->transfer(TMEMBER(_editorColWaypoints)); + persistMgr->transfer(TMEMBER(_editorColWaypointsSel)); + persistMgr->transfer(TMEMBER(_editorMarginH)); + persistMgr->transfer(TMEMBER(_editorMarginV)); + persistMgr->transfer(TMEMBER(_editorShowBlocked)); + persistMgr->transfer(TMEMBER(_editorShowDecor)); + persistMgr->transfer(TMEMBER(_editorShowEntities)); + persistMgr->transfer(TMEMBER(_editorShowRegions)); + persistMgr->transfer(TMEMBER(_editorShowScale)); + persistMgr->transfer(TMEMBER(_fader)); + persistMgr->transfer(TMEMBER(_height)); + persistMgr->transfer(TMEMBER(_initialized)); + persistMgr->transfer(TMEMBER(_lastTimeH)); + persistMgr->transfer(TMEMBER(_lastTimeV)); + _layers.persist(persistMgr); + persistMgr->transfer(TMEMBER(_mainLayer)); + _objects.persist(persistMgr); + persistMgr->transfer(TMEMBER(_offsetLeft)); + persistMgr->transfer(TMEMBER(_offsetTop)); + persistMgr->transfer(TMEMBER(_paralaxScrolling)); + persistMgr->transfer(TMEMBER(_persistentState)); + persistMgr->transfer(TMEMBER(_persistentStateSprites)); + persistMgr->transfer(TMEMBER(_pfMaxTime)); + _pfPath.persist(persistMgr); + persistMgr->transfer(TMEMBER(_pfPointsNum)); + persistMgr->transfer(TMEMBER(_pfReady)); + persistMgr->transfer(TMEMBER(_pfRequester)); + persistMgr->transfer(TMEMBER(_pfTarget)); + persistMgr->transfer(TMEMBER(_pfTargetPath)); + _rotLevels.persist(persistMgr); + _scaleLevels.persist(persistMgr); + persistMgr->transfer(TMEMBER(_scrollPixelsH)); + persistMgr->transfer(TMEMBER(_scrollPixelsV)); + persistMgr->transfer(TMEMBER(_scrollTimeH)); + persistMgr->transfer(TMEMBER(_scrollTimeV)); + persistMgr->transfer(TMEMBER(_shieldWindow)); + persistMgr->transfer(TMEMBER(_targetOffsetLeft)); + persistMgr->transfer(TMEMBER(_targetOffsetTop)); + _waypointGroups.persist(persistMgr); + persistMgr->transfer(TMEMBER(_viewport)); + persistMgr->transfer(TMEMBER(_width)); + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool AdScene::afterLoad() { + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool AdScene::correctTargetPoint2(int startX, int startY, int *targetX, int *targetY, bool checkFreeObjects, BaseObject *requester) { + double xStep, yStep, x, y; + int xLength, yLength, xCount, yCount; + int x1, y1, x2, y2; + + x1 = *targetX; + y1 = *targetY; + x2 = startX; + y2 = startY; + + + xLength = abs(x2 - x1); + yLength = abs(y2 - y1); + + if (xLength > yLength) { + + yStep = fabs((double)(y2 - y1) / (double)(x2 - x1)); + y = y1; + + for (xCount = x1; xCount < x2; xCount++) { + if (isWalkableAt(xCount, (int)y, checkFreeObjects, requester)) { + *targetX = xCount; + *targetY = (int)y; + return STATUS_OK; + } + y += yStep; + } + } else { + + xStep = fabs((double)(x2 - x1) / (double)(y2 - y1)); + x = x1; + + for (yCount = y1; yCount < y2; yCount++) { + if (isWalkableAt((int)x, yCount, checkFreeObjects, requester)) { + *targetX = (int)x; + *targetY = yCount; + return STATUS_OK; + } + x += xStep; + } + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool AdScene::correctTargetPoint(int startX, int startY, int *argX, int *argY, bool checkFreeObjects, BaseObject *requester) { + int x = *argX; + int y = *argY; + + if (isWalkableAt(x, y, checkFreeObjects, requester) || !_mainLayer) { + return STATUS_OK; + } + + // right + int lengthRight = 0; + bool foundRight = false; + for (x = *argX, y = *argY; x < _mainLayer->_width; x++, lengthRight++) { + if (isWalkableAt(x, y, checkFreeObjects, requester) && isWalkableAt(x - 5, y, checkFreeObjects, requester)) { + foundRight = true; + break; + } + } + + // left + int lengthLeft = 0; + bool foundLeft = false; + for (x = *argX, y = *argY; x >= 0; x--, lengthLeft--) { + if (isWalkableAt(x, y, checkFreeObjects, requester) && isWalkableAt(x + 5, y, checkFreeObjects, requester)) { + foundLeft = true; + break; + } + } + + // up + int lengthUp = 0; + bool foundUp = false; + for (x = *argX, y = *argY; y >= 0; y--, lengthUp--) { + if (isWalkableAt(x, y, checkFreeObjects, requester) && isWalkableAt(x, y + 5, checkFreeObjects, requester)) { + foundUp = true; + break; + } + } + + // down + int lengthDown = 0; + bool foundDown = false; + for (x = *argX, y = *argY; y < _mainLayer->_height; y++, lengthDown++) { + if (isWalkableAt(x, y, checkFreeObjects, requester) && isWalkableAt(x, y - 5, checkFreeObjects, requester)) { + foundDown = true; + break; + } + } + + if (!foundLeft && !foundRight && !foundUp && !foundDown) { + return STATUS_OK; + } + + int offsetX = INT_MAX, offsetY = INT_MAX; + + if (foundLeft && foundRight) { + if (abs(lengthLeft) < abs(lengthRight)) { + offsetX = lengthLeft; + } else { + offsetX = lengthRight; + } + } else if (foundLeft) { + offsetX = lengthLeft; + } else if (foundRight) { + offsetX = lengthRight; + } + + if (foundUp && foundDown) { + if (abs(lengthUp) < abs(lengthDown)) { + offsetY = lengthUp; + } else { + offsetY = lengthDown; + } + } else if (foundUp) { + offsetY = lengthUp; + } else if (foundDown) { + offsetY = lengthDown; + } + + if (abs(offsetX) < abs(offsetY)) { + *argX = *argX + offsetX; + } else { + *argY = *argY + offsetY; + } + + if (!isWalkableAt(*argX, *argY)) { + return correctTargetPoint2(startX, startY, argX, argY, checkFreeObjects, requester); + } else { + return STATUS_OK; + } +} + + +////////////////////////////////////////////////////////////////////////// +void AdScene::pfPointsStart() { + _pfPointsNum = 0; +} + + +////////////////////////////////////////////////////////////////////////// +void AdScene::pfPointsAdd(int x, int y, int distance) { + if (_pfPointsNum >= (int32)_pfPath.size()) { + _pfPath.add(new AdPathPoint(x, y, distance)); + } else { + _pfPath[_pfPointsNum]->x = x; + _pfPath[_pfPointsNum]->y = y; + _pfPath[_pfPointsNum]->_distance = distance; + _pfPath[_pfPointsNum]->_marked = false; + _pfPath[_pfPointsNum]->_origin = NULL; + } + + _pfPointsNum++; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::getViewportOffset(int *offsetX, int *offsetY) { + AdGame *adGame = (AdGame *)_gameRef; + if (_viewport && !_gameRef->_editorMode) { + if (offsetX) { + *offsetX = _viewport->_offsetX; + } + if (offsetY) { + *offsetY = _viewport->_offsetY; + } + } else if (adGame->_sceneViewport && !_gameRef->_editorMode) { + if (offsetX) { + *offsetX = adGame->_sceneViewport->_offsetX; + } + if (offsetY) { + *offsetY = adGame->_sceneViewport->_offsetY; + } + } else { + if (offsetX) { + *offsetX = 0; + } + if (offsetY) { + *offsetY = 0; + } + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::getViewportSize(int *width, int *height) { + AdGame *adGame = (AdGame *)_gameRef; + if (_viewport && !_gameRef->_editorMode) { + if (width) { + *width = _viewport->getWidth(); + } + if (height) { + *height = _viewport->getHeight(); + } + } else if (adGame->_sceneViewport && !_gameRef->_editorMode) { + if (width) { + *width = adGame->_sceneViewport->getWidth(); + } + if (height) { + *height = adGame->_sceneViewport->getHeight(); + } + } else { + if (width) { + *width = _gameRef->_renderer->_width; + } + if (height) { + *height = _gameRef->_renderer->_height; + } + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +int AdScene::getOffsetLeft() { + int viewportX; + getViewportOffset(&viewportX); + + return _offsetLeft - viewportX; +} + + +////////////////////////////////////////////////////////////////////////// +int AdScene::getOffsetTop() { + int viewportY; + getViewportOffset(NULL, &viewportY); + + return _offsetTop - viewportY; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::pointInViewport(int x, int y) { + int left, top, width, height; + + getViewportOffset(&left, &top); + getViewportSize(&width, &height); + + return x >= left && x <= left + width && y >= top && y <= top + height; +} + + +////////////////////////////////////////////////////////////////////////// +void AdScene::setOffset(int offsetLeft, int offsetTop) { + _offsetLeft = offsetLeft; + _offsetTop = offsetTop; +} + + +////////////////////////////////////////////////////////////////////////// +BaseObject *AdScene::getNodeByName(const char *name) { + BaseObject *ret = NULL; + + // dependent objects + for (uint32 i = 0; i < _layers.size(); i++) { + AdLayer *layer = _layers[i]; + for (uint32 j = 0; j < layer->_nodes.size(); j++) { + AdSceneNode *node = layer->_nodes[j]; + if ((node->_type == OBJECT_ENTITY && !scumm_stricmp(name, node->_entity->getName())) || + (node->_type == OBJECT_REGION && !scumm_stricmp(name, node->_region->getName()))) { + switch (node->_type) { + case OBJECT_ENTITY: + ret = node->_entity; + break; + case OBJECT_REGION: + ret = node->_region; + break; + default: + ret = NULL; + } + return ret; + } + } + } + + // free entities + for (uint32 i = 0; i < _objects.size(); i++) { + if (_objects[i]->getType() == OBJECT_ENTITY && !scumm_stricmp(name, _objects[i]->getName())) { + return _objects[i]; + } + } + + // waypoint groups + for (uint32 i = 0; i < _waypointGroups.size(); i++) { + if (!scumm_stricmp(name, _waypointGroups[i]->getName())) { + return _waypointGroups[i]; + } + } + + return NULL; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::saveState() { + return persistState(true); +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::loadState() { + return persistState(false); +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::persistState(bool saving) { + if (!_persistentState) { + return STATUS_OK; + } + + AdGame *adGame = (AdGame *)_gameRef; + AdSceneState *state = adGame->getSceneState(getFilename(), saving); + if (!state) { + return STATUS_OK; + } + + AdNodeState *nodeState; + + // dependent objects + for (uint32 i = 0; i < _layers.size(); i++) { + AdLayer *layer = _layers[i]; + for (uint32 j = 0; j < layer->_nodes.size(); j++) { + AdSceneNode *node = layer->_nodes[j]; + switch (node->_type) { + case OBJECT_ENTITY: + if (!node->_entity->_saveState) { + continue; + } + nodeState = state->getNodeState(node->_entity->getName(), saving); + if (nodeState) { + nodeState->transferEntity(node->_entity, _persistentStateSprites, saving); + //if (Saving) NodeState->_active = node->_entity->_active; + //else node->_entity->_active = NodeState->_active; + } + break; + case OBJECT_REGION: + if (!node->_region->_saveState) { + continue; + } + nodeState = state->getNodeState(node->_region->getName(), saving); + if (nodeState) { + if (saving) { + nodeState->_active = node->_region->_active; + } else { + node->_region->_active = nodeState->_active; + } + } + break; + default: + warning("AdScene::PersistState - unhandled enum"); + break; + } + } + } + + // free entities + for (uint32 i = 0; i < _objects.size(); i++) { + if (!_objects[i]->_saveState) { + continue; + } + if (_objects[i]->getType() == OBJECT_ENTITY) { + nodeState = state->getNodeState(_objects[i]->getName(), saving); + if (nodeState) { + nodeState->transferEntity((AdEntity *)_objects[i], _persistentStateSprites, saving); + //if (Saving) NodeState->_active = _objects[i]->_active; + //else _objects[i]->_active = NodeState->_active; + } + } + } + + // waypoint groups + for (uint32 i = 0; i < _waypointGroups.size(); i++) { + nodeState = state->getNodeState(_waypointGroups[i]->getName(), saving); + if (nodeState) { + if (saving) { + nodeState->_active = _waypointGroups[i]->_active; + } else { + _waypointGroups[i]->_active = nodeState->_active; + } + } + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +float AdScene::getRotationAt(int x, int y) { + AdRotLevel *prev = NULL; + AdRotLevel *next = NULL; + + for (uint32 i = 0; i < _rotLevels.size(); i++) { + /* AdRotLevel *xxx = _rotLevels[i]; + int j = _rotLevels.size();*/ + if (_rotLevels[i]->_posX < x) { + prev = _rotLevels[i]; + } else { + next = _rotLevels[i]; + break; + } + } + + if (prev == NULL || next == NULL) { + return 0; + } + + int delta_x = next->_posX - prev->_posX; + float delta_rot = next->_rotation - prev->_rotation; + x -= prev->_posX; + + float percent = (float)x / ((float)delta_x / 100.0f); + return prev->_rotation + delta_rot / 100 * percent; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::handleItemAssociations(const char *itemName, bool show) { + for (uint32 i = 0; i < _layers.size(); i++) { + AdLayer *layer = _layers[i]; + for (uint32 j = 0; j < layer->_nodes.size(); j++) { + if (layer->_nodes[j]->_type == OBJECT_ENTITY) { + AdEntity *ent = layer->_nodes[j]->_entity; + + if (ent->getItemName() && strcmp(ent->getItemName(), itemName) == 0) { + ent->_active = show; + } + } + } + } + + for (uint32 i = 0; i < _objects.size(); i++) { + if (_objects[i]->getType() == OBJECT_ENTITY) { + AdEntity *ent = (AdEntity *)_objects[i]; + if (ent->getItemName() && strcmp(ent->getItemName(), itemName) == 0) { + ent->_active = show; + } + } + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::getRegionsAt(int x, int y, AdRegion **regionList, int numRegions) { + int numUsed = 0; + if (_mainLayer) { + for (int i = _mainLayer->_nodes.size() - 1; i >= 0; i--) { + AdSceneNode *node = _mainLayer->_nodes[i]; + if (node->_type == OBJECT_REGION && node->_region->_active && node->_region->pointInRegion(x, y)) { + if (numUsed < numRegions - 1) { + regionList[numUsed] = node->_region; + numUsed++; + } else { + break; + } + } + } + } + for (int i = numUsed; i < numRegions; i++) { + regionList[i] = NULL; + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool AdScene::restoreDeviceObjects() { + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +BaseObject *AdScene::getNextAccessObject(BaseObject *currObject) { + BaseArray<AdObject *> objects; + getSceneObjects(objects, true); + + if (objects.size() == 0) { + return NULL; + } else { + if (currObject != NULL) { + for (uint32 i = 0; i < objects.size(); i++) { + if (objects[i] == currObject) { + if (i < objects.size() - 1) { + return objects[i + 1]; + } else { + break; + } + } + } + } + return objects[0]; + } + return NULL; +} + +////////////////////////////////////////////////////////////////////////// +BaseObject *AdScene::getPrevAccessObject(BaseObject *currObject) { + BaseArray<AdObject *> objects; + getSceneObjects(objects, true); + + if (objects.size() == 0) { + return NULL; + } else { + if (currObject != NULL) { + for (int i = objects.size() - 1; i >= 0; i--) { + if (objects[i] == currObject) { + if (i > 0) { + return objects[i - 1]; + } else { + break; + } + } + } + } + return objects[objects.size() - 1]; + } + return NULL; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::getSceneObjects(BaseArray<AdObject *> &objects, bool interactiveOnly) { + for (uint32 i = 0; i < _layers.size(); i++) { + // close-up layer -> remove everything below it + if (interactiveOnly && _layers[i]->_closeUp) { + objects.clear(); + } + + + for (uint32 j = 0; j < _layers[i]->_nodes.size(); j++) { + AdSceneNode *node = _layers[i]->_nodes[j]; + switch (node->_type) { + case OBJECT_ENTITY: { + AdEntity *ent = node->_entity; + if (ent->_active && (ent->_registrable || !interactiveOnly)) { + objects.add(ent); + } + } + break; + + case OBJECT_REGION: { + BaseArray<AdObject *> regionObj; + getRegionObjects(node->_region, regionObj, interactiveOnly); + for (uint32 newIndex = 0; newIndex < regionObj.size(); newIndex++) { + bool found = false; + for (uint32 old = 0; old < objects.size(); old++) { + if (objects[old] == regionObj[newIndex]) { + found = true; + break; + } + } + if (!found) { + objects.add(regionObj[newIndex]); + } + } + //if (regionObj.size() > 0) Objects.Append(RegionObj); + } + break; + default: + debugC(kWintermuteDebugGeneral, "AdScene::GetSceneObjects - Unhandled enum"); + break; + } + } + } + + // objects outside any region + BaseArray<AdObject *> regionObj; + getRegionObjects(NULL, regionObj, interactiveOnly); + for (uint32 newIndex = 0; newIndex < regionObj.size(); newIndex++) { + bool found = false; + for (uint32 old = 0; old < objects.size(); old++) { + if (objects[old] == regionObj[newIndex]) { + found = true; + break; + } + } + if (!found) { + objects.add(regionObj[newIndex]); + } + } + + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::getRegionObjects(AdRegion *region, BaseArray<AdObject *> &objects, bool interactiveOnly) { + AdGame *adGame = (AdGame *)_gameRef; + AdObject *obj; + + // global objects + for (uint32 i = 0; i < adGame->_objects.size(); i++) { + obj = adGame->_objects[i]; + if (obj->_active && (obj->_stickRegion == region || region == NULL || (obj->_stickRegion == NULL && region->pointInRegion(obj->_posX, obj->_posY)))) { + if (interactiveOnly && !obj->_registrable) { + continue; + } + + objects.add(obj); + } + } + + // scene objects + for (uint32 i = 0; i < _objects.size(); i++) { + obj = _objects[i]; + if (obj->_active && !obj->_editorOnly && (obj->_stickRegion == region || region == NULL || (obj->_stickRegion == NULL && region->pointInRegion(obj->_posX, obj->_posY)))) { + if (interactiveOnly && !obj->_registrable) { + continue; + } + + objects.add(obj); + } + } + + // sort by _posY + Common::sort(objects.begin(), objects.end(), AdScene::compareObjs); + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_scene.h b/engines/wintermute/ad/ad_scene.h new file mode 100644 index 0000000000..230cb77f5b --- /dev/null +++ b/engines/wintermute/ad/ad_scene.h @@ -0,0 +1,181 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADSCENE_H +#define WINTERMUTE_ADSCENE_H + +#include "engines/wintermute/base/base_fader.h" + +namespace Wintermute { + +class UIWindow; +class AdObject; +class AdRegion; +class BaseViewport; +class AdLayer; +class BasePoint; +class AdWaypointGroup; +class AdPath; +class AdScaleLevel; +class AdRotLevel; +class AdPathPoint; +class AdScene : public BaseObject { +public: + + BaseObject *getNextAccessObject(BaseObject *currObject); + BaseObject *getPrevAccessObject(BaseObject *currObject); + bool getSceneObjects(BaseArray<AdObject *> &objects, bool interactiveOnly); + bool getRegionObjects(AdRegion *region, BaseArray<AdObject *> &objects, bool interactiveOnly); + + bool afterLoad(); + + bool getRegionsAt(int x, int y, AdRegion **regionList, int numRegions); + bool handleItemAssociations(const char *itemName, bool show); + UIWindow *_shieldWindow; + float getRotationAt(int x, int y); + bool loadState(); + bool saveState(); + bool _persistentState; + bool _persistentStateSprites; + BaseObject *getNodeByName(const char *name); + void setOffset(int offsetLeft, int offsetTop); + bool pointInViewport(int x, int y); + int getOffsetTop(); + int getOffsetLeft(); + bool getViewportSize(int *width = NULL, int *height = NULL); + bool getViewportOffset(int *offsetX = NULL, int *offsetY = NULL); + BaseViewport *_viewport; + BaseFader *_fader; + int _pfPointsNum; + void pfPointsAdd(int x, int y, int distance); + void pfPointsStart(); + bool _initialized; + bool correctTargetPoint(int startX, int startY, int *x, int *y, bool checkFreeObjects = false, BaseObject *requester = NULL); + bool correctTargetPoint2(int startX, int startY, int *targetX, int *targetY, bool checkFreeObjects, BaseObject *requester); + DECLARE_PERSISTENT(AdScene, BaseObject) + bool displayRegionContent(AdRegion *region = NULL, bool display3DOnly = false); + bool displayRegionContentOld(AdRegion *region = NULL); + static bool compareObjs(const AdObject *obj1, const AdObject *obj2); + + bool updateFreeObjects(); + bool traverseNodes(bool update = false); + float getScaleAt(int y); + bool sortScaleLevels(); + bool sortRotLevels(); + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent); + uint32 getAlphaAt(int x, int y, bool colorCheck = false); + bool _paralaxScrolling; + void skipTo(int offsetX, int offsetY); + void setDefaults(); + void cleanup(); + void skipToObject(BaseObject *object); + void scrollToObject(BaseObject *object); + void scrollTo(int offsetX, int offsetY); + virtual bool update(); + bool _autoScroll; + int _targetOffsetTop; + int _targetOffsetLeft; + + int _scrollPixelsV; + uint32 _scrollTimeV; + uint32 _lastTimeV; + + int _scrollPixelsH; + uint32 _scrollTimeH; + uint32 _lastTimeH; + + virtual bool display(); + uint32 _pfMaxTime; + bool initLoop(); + void pathFinderStep(); + bool isBlockedAt(int x, int y, bool checkFreeObjects = false, BaseObject *requester = NULL); + bool isWalkableAt(int x, int y, bool checkFreeObjects = false, BaseObject *requester = NULL); + AdLayer *_mainLayer; + float getZoomAt(int x, int y); + bool getPath(BasePoint source, BasePoint target, AdPath *path, BaseObject *requester = NULL); + AdScene(BaseGame *inGame); + virtual ~AdScene(); + BaseArray<AdLayer *> _layers; + BaseArray<AdObject *> _objects; + BaseArray<AdWaypointGroup *> _waypointGroups; + bool loadFile(const char *filename); + bool loadBuffer(byte *buffer, bool complete = true); + int _width; + int _height; + bool addObject(AdObject *Object); + bool removeObject(AdObject *Object); + int _editorMarginH; + int _editorMarginV; + uint32 _editorColFrame; + uint32 _editorColEntity; + uint32 _editorColRegion; + uint32 _editorColBlocked; + uint32 _editorColWaypoints; + uint32 _editorColEntitySel; + uint32 _editorColRegionSel; + uint32 _editorColBlockedSel; + uint32 _editorColWaypointsSel; + uint32 _editorColScale; + uint32 _editorColDecor; + uint32 _editorColDecorSel; + + bool _editorShowRegions; + bool _editorShowBlocked; + bool _editorShowDecor; + bool _editorShowEntities; + bool _editorShowScale; + BaseArray<AdScaleLevel *> _scaleLevels; + BaseArray<AdRotLevel *> _rotLevels; + + virtual bool restoreDeviceObjects(); + int getPointsDist(BasePoint p1, BasePoint p2, BaseObject *requester = NULL); + + // scripting interface + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + virtual const char *scToString(); + + +private: + bool persistState(bool saving = true); + void pfAddWaypointGroup(AdWaypointGroup *Wpt, BaseObject *requester = NULL); + bool _pfReady; + BasePoint *_pfTarget; + AdPath *_pfTargetPath; + BaseObject *_pfRequester; + BaseArray<AdPathPoint *> _pfPath; + + int _offsetTop; + int _offsetLeft; + +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_scene_node.cpp b/engines/wintermute/ad/ad_scene_node.cpp new file mode 100644 index 0000000000..d0202236fd --- /dev/null +++ b/engines/wintermute/ad/ad_scene_node.cpp @@ -0,0 +1,82 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_scene_node.h" +#include "engines/wintermute/base/base_game.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdSceneNode, false) + +////////////////////////////////////////////////////////////////////////// +AdSceneNode::AdSceneNode(BaseGame *inGame) : BaseObject(inGame) { + _type = OBJECT_NONE; + _region = NULL; + _entity = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +AdSceneNode::~AdSceneNode() { + _gameRef->unregisterObject(_region); + _region = NULL; + + _gameRef->unregisterObject(_entity); + _entity = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdSceneNode::setEntity(AdEntity *entity) { + _type = OBJECT_ENTITY; + _entity = entity; + return _gameRef->registerObject(entity); +} + + +////////////////////////////////////////////////////////////////////////// +bool AdSceneNode::setRegion(AdRegion *region) { + _type = OBJECT_REGION; + _region = region; + return _gameRef->registerObject(region); +} + + +////////////////////////////////////////////////////////////////////////// +bool AdSceneNode::persist(BasePersistenceManager *persistMgr) { + + BaseObject::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_entity)); + persistMgr->transfer(TMEMBER(_region)); + persistMgr->transfer(TMEMBER_INT(_type)); + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_scene_node.h b/engines/wintermute/ad/ad_scene_node.h new file mode 100644 index 0000000000..5bb1606d0e --- /dev/null +++ b/engines/wintermute/ad/ad_scene_node.h @@ -0,0 +1,54 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADSCENENODE_H +#define WINTERMUTE_ADSCENENODE_H + + +#include "engines/wintermute/ad/ad_types.h" // Added by ClassView +#include "engines/wintermute/ad/ad_region.h" // Added by ClassView +#include "engines/wintermute/ad/ad_entity.h" + +namespace Wintermute { + +class AdSceneNode : public BaseObject { +public: + DECLARE_PERSISTENT(AdSceneNode, BaseObject) + bool setRegion(AdRegion *region); + bool setEntity(AdEntity *entity); + AdEntity *_entity; + AdRegion *_region; + TObjectType _type; + AdSceneNode(BaseGame *inGame); + virtual ~AdSceneNode(); + +}; + +} + +#endif diff --git a/engines/wintermute/ad/ad_scene_state.cpp b/engines/wintermute/ad/ad_scene_state.cpp new file mode 100644 index 0000000000..e6dd08a5fe --- /dev/null +++ b/engines/wintermute/ad/ad_scene_state.cpp @@ -0,0 +1,98 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_scene_state.h" +#include "engines/wintermute/ad/ad_node_state.h" +#include "engines/wintermute/persistent.h" +#include "engines/wintermute/platform_osystem.h" +#include "common/str.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdSceneState, false) + +////////////////////////////////////////////////////////////////////////// +AdSceneState::AdSceneState(BaseGame *inGame) : BaseClass(inGame) { + _filename = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +AdSceneState::~AdSceneState() { + delete[] _filename; + _filename = NULL; + + for (uint32 i = 0; i < _nodeStates.size(); i++) { + delete _nodeStates[i]; + } + _nodeStates.clear(); +} + + +////////////////////////////////////////////////////////////////////////// +bool AdSceneState::persist(BasePersistenceManager *persistMgr) { + persistMgr->transfer(TMEMBER(_filename)); + _nodeStates.persist(persistMgr); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +void AdSceneState::setFilename(const char *filename) { + delete[] _filename; + _filename = new char [strlen(filename) + 1]; + if (_filename) { + strcpy(_filename, filename); + } +} + +const char *AdSceneState::getFilename() const { + return _filename; +} + +////////////////////////////////////////////////////////////////////////// +AdNodeState *AdSceneState::getNodeState(const char *name, bool saving) { + for (uint32 i = 0; i < _nodeStates.size(); i++) { + if (scumm_stricmp(_nodeStates[i]->getName(), name) == 0) { + return _nodeStates[i]; + } + } + + if (saving) { + AdNodeState *ret = new AdNodeState(_gameRef); + ret->setName(name); + _nodeStates.add(ret); + + return ret; + } else { + return NULL; + } +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_scene_state.h b/engines/wintermute/ad/ad_scene_state.h new file mode 100644 index 0000000000..600aa4b581 --- /dev/null +++ b/engines/wintermute/ad/ad_scene_state.h @@ -0,0 +1,53 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADSCENESTATE_H +#define WINTERMUTE_ADSCENESTATE_H + +#include "engines/wintermute/persistent.h" +#include "engines/wintermute/base/base.h" +#include "engines/wintermute/coll_templ.h" + +namespace Wintermute { +class AdNodeState; +class AdSceneState : public BaseClass { +public: + AdNodeState *getNodeState(const char *name, bool saving); + void setFilename(const char *filename); + const char *getFilename() const; + DECLARE_PERSISTENT(AdSceneState, BaseClass) + AdSceneState(BaseGame *inGame); + virtual ~AdSceneState(); +private: + char *_filename; + BaseArray<AdNodeState *> _nodeStates; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_sentence.cpp b/engines/wintermute/ad/ad_sentence.cpp new file mode 100644 index 0000000000..cfe4191b07 --- /dev/null +++ b/engines/wintermute/ad/ad_sentence.cpp @@ -0,0 +1,361 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_game.h" +#include "engines/wintermute/ad/ad_scene.h" +#include "engines/wintermute/ad/ad_sentence.h" +#include "engines/wintermute/ad/ad_talk_def.h" +#include "engines/wintermute/ad/ad_talk_node.h" +#include "engines/wintermute/utils/path_util.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/font/base_font.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/base/sound/base_sound.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdSentence, false) + +////////////////////////////////////////////////////////////////////////// +AdSentence::AdSentence(BaseGame *inGame) : BaseClass(inGame) { + _text = NULL; + _stances = NULL; + _tempStance = NULL; + + _duration = 0; + _startTime = 0; + _currentStance = 0; + + _font = NULL; + + _pos.x = _pos.y = 0; + _width = _gameRef->_renderer->_width; + + _align = (TTextAlign)TAL_CENTER; + + _sound = NULL; + _soundStarted = false; + + _talkDef = NULL; + _currentSprite = NULL; + _currentSkelAnim = NULL; + _fixedPos = false; + _freezable = true; +} + + +////////////////////////////////////////////////////////////////////////// +AdSentence::~AdSentence() { + delete _sound; + delete[] _text; + delete[] _stances; + delete[] _tempStance; + delete _talkDef; + _sound = NULL; + _text = NULL; + _stances = NULL; + _tempStance = NULL; + _talkDef = NULL; + + _currentSprite = NULL; // ref only + _currentSkelAnim = NULL; + _font = NULL; // ref only +} + + +////////////////////////////////////////////////////////////////////////// +void AdSentence::setText(const char *text) { + if (_text) { + delete[] _text; + } + _text = new char[strlen(text) + 1]; + if (_text) { + strcpy(_text, text); + } +} + + +////////////////////////////////////////////////////////////////////////// +void AdSentence::setStances(const char *stances) { + if (_stances) { + delete[] _stances; + } + if (stances) { + _stances = new char[strlen(stances) + 1]; + if (_stances) { + strcpy(_stances, stances); + } + } else { + _stances = NULL; + } +} + + +////////////////////////////////////////////////////////////////////////// +char *AdSentence::getCurrentStance() { + return getStance(_currentStance); +} + + +////////////////////////////////////////////////////////////////////////// +char *AdSentence::getNextStance() { + _currentStance++; + return getStance(_currentStance); +} + + +////////////////////////////////////////////////////////////////////////// +char *AdSentence::getStance(int stance) { + if (_stances == NULL) { + return NULL; + } + + if (_tempStance) { + delete[] _tempStance; + } + _tempStance = NULL; + + char *start; + char *curr; + int pos; + + if (stance == 0) { + start = _stances; + } else { + pos = 0; + start = NULL; + curr = _stances; + while (pos < stance) { + if (*curr == '\0') { + break; + } + if (*curr == ',') { + pos++; + } + curr++; + } + if (pos == stance) { + start = curr; + } + } + + if (start == NULL) { + return NULL; + } + + while (*start == ' ' && *start != ',' && *start != '\0') { + start++; + } + + curr = start; + while (*curr != '\0' && *curr != ',') { + curr++; + } + + while (curr > start && *(curr - 1) == ' ') { + curr--; + } + + _tempStance = new char [curr - start + 1]; + if (_tempStance) { + Common::strlcpy(_tempStance, start, curr - start + 1); + } + + return _tempStance; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdSentence::display() { + if (!_font || !_text) { + return STATUS_FAILED; + } + + if (_sound && !_soundStarted) { + _sound->play(); + _soundStarted = true; + } + + if (_gameRef->_subtitles) { + int x = _pos.x; + int y = _pos.y; + + if (!_fixedPos) { + x = x - ((AdGame *)_gameRef)->_scene->getOffsetLeft(); + y = y - ((AdGame *)_gameRef)->_scene->getOffsetTop(); + } + + + x = MAX(x, 0); + x = MIN(x, _gameRef->_renderer->_width - _width); + y = MAX(y, 0); + + _font->drawText((byte *)_text, x, y, _width, _align); + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +void AdSentence::setSound(BaseSound *sound) { + if (!sound) { + return; + } + delete _sound; + _sound = sound; + _soundStarted = false; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdSentence::finish() { + if (_sound) { + _sound->stop(); + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdSentence::persist(BasePersistenceManager *persistMgr) { + + persistMgr->transfer(TMEMBER(_gameRef)); + + persistMgr->transfer(TMEMBER_INT(_align)); + persistMgr->transfer(TMEMBER(_currentStance)); + persistMgr->transfer(TMEMBER(_currentSprite)); + persistMgr->transfer(TMEMBER(_currentSkelAnim)); + persistMgr->transfer(TMEMBER(_duration)); + persistMgr->transfer(TMEMBER(_font)); + persistMgr->transfer(TMEMBER(_pos)); + persistMgr->transfer(TMEMBER(_sound)); + persistMgr->transfer(TMEMBER(_soundStarted)); + persistMgr->transfer(TMEMBER(_stances)); + persistMgr->transfer(TMEMBER(_startTime)); + persistMgr->transfer(TMEMBER(_talkDef)); + persistMgr->transfer(TMEMBER(_tempStance)); + persistMgr->transfer(TMEMBER(_text)); + persistMgr->transfer(TMEMBER(_width)); + persistMgr->transfer(TMEMBER(_fixedPos)); + persistMgr->transfer(TMEMBER(_freezable)); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdSentence::setupTalkFile(const char *soundFilename) { + delete _talkDef; + _talkDef = NULL; + _currentSprite = NULL; + + if (!soundFilename) { + return STATUS_OK; + } + + + AnsiString path = PathUtil::getDirectoryName(soundFilename); + AnsiString name = PathUtil::getFileNameWithoutExtension(soundFilename); + + AnsiString talkDefFileName = PathUtil::combine(path, name + ".talk"); + + if (!BaseFileManager::getEngineInstance()->hasFile(talkDefFileName)) { + return STATUS_OK; // no talk def file found + } + + _talkDef = new AdTalkDef(_gameRef); + if (!_talkDef || DID_FAIL(_talkDef->loadFile(talkDefFileName.c_str()))) { + delete _talkDef; + _talkDef = NULL; + return STATUS_FAILED; + } + //_gameRef->LOG(0, "Using .talk file: %s", TalkDefFile); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdSentence::update(TDirection dir) { + if (!_talkDef) { + return STATUS_OK; + } + + uint32 currentTime; + // if sound is available, synchronize with sound, otherwise use timer + + /* + if (_sound) CurrentTime = _sound->GetPositionTime(); + else CurrentTime = _gameRef->_timer - _startTime; + */ + currentTime = _gameRef->_timer - _startTime; + + bool talkNodeFound = false; + for (uint32 i = 0; i < _talkDef->_nodes.size(); i++) { + if (_talkDef->_nodes[i]->isInTimeInterval(currentTime, dir)) { + talkNodeFound = true; + + BaseSprite *newSprite = _talkDef->_nodes[i]->getSprite(dir); + if (newSprite != _currentSprite) { + newSprite->reset(); + } + _currentSprite = newSprite; + + if (!_talkDef->_nodes[i]->_playToEnd) { + break; + } + } + } + + + // no talk node, try to use default sprite instead (if any) + if (!talkNodeFound) { + BaseSprite *newSprite = _talkDef->getDefaultSprite(dir); + if (newSprite) { + if (newSprite != _currentSprite) { + newSprite->reset(); + } + _currentSprite = newSprite; + } else { + _currentSprite = NULL; + } + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool AdSentence::canSkip() { + // prevent accidental sentence skipping (TODO make configurable) + return (_gameRef->_timer - _startTime) > 300; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_sentence.h b/engines/wintermute/ad/ad_sentence.h new file mode 100644 index 0000000000..e7c94030b9 --- /dev/null +++ b/engines/wintermute/ad/ad_sentence.h @@ -0,0 +1,85 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADSENTENCE_H +#define WINTERMUTE_ADSENTENCE_H + + +#include "engines/wintermute/base/base.h" +#include "engines/wintermute/persistent.h" +#include "engines/wintermute/math/rect32.h" +#include "engines/wintermute/dctypes.h" // Added by ClassView +#include "common/rect.h" + +namespace Wintermute { +class AdTalkDef; +class BaseFont; +class BaseSprite; +class BaseSound; +class AdSentence : public BaseClass { +public: + bool _freezable; + bool _fixedPos; + BaseSprite *_currentSprite; + char *_currentSkelAnim; + bool update(TDirection dir = DI_DOWN); + bool setupTalkFile(const char *soundFilename); + DECLARE_PERSISTENT(AdSentence, BaseClass) + bool finish(); + void setSound(BaseSound *Sound); + bool _soundStarted; + BaseSound *_sound; + TTextAlign _align; + bool display(); + int _width; + Point32 _pos; + BaseFont *_font; + char *getNextStance(); + char *getCurrentStance(); + void setStances(const char *stances); + void setText(const char *text); + int _currentStance; + uint32 _startTime; + char *_stances; + char *_text; + uint32 _duration; + AdSentence(BaseGame *inGame); + virtual ~AdSentence(); + AdTalkDef *_talkDef; + + bool canSkip(); + +private: + char *_tempStance; + char *getStance(int stance); + +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_sprite_set.cpp b/engines/wintermute/ad/ad_sprite_set.cpp new file mode 100644 index 0000000000..345b483a8f --- /dev/null +++ b/engines/wintermute/ad/ad_sprite_set.cpp @@ -0,0 +1,356 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_sprite_set.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_sprite.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdSpriteSet, false) + +////////////////////////////////////////////////////////////////////////// +AdSpriteSet::AdSpriteSet(BaseGame *inGame, BaseObject *owner) : BaseObject(inGame) { + _owner = owner; + + for (int i = 0; i < NUM_DIRECTIONS; i++) { + _sprites[i] = NULL; + } +} + + +////////////////////////////////////////////////////////////////////////// +AdSpriteSet::~AdSpriteSet() { + for (int i = 0; i < NUM_DIRECTIONS; i++) { + delete _sprites[i]; + _sprites[i] = NULL; + } + + _owner = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdSpriteSet::loadFile(const char *filename, int lifeTime, TSpriteCacheType cacheType) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "AdSpriteSet::LoadFile failed for file '%s'", filename); + return STATUS_FAILED; + } + + bool ret; + + if (DID_FAIL(ret = loadBuffer(buffer, true))) { + _gameRef->LOG(0, "Error parsing SPRITESET file '%s'", filename); + } + + delete[] buffer; + + return ret; +} + + +TOKEN_DEF_START +TOKEN_DEF(SPRITESET) +TOKEN_DEF(NAME) +TOKEN_DEF(UP_LEFT) +TOKEN_DEF(DOWN_LEFT) +TOKEN_DEF(LEFT) +TOKEN_DEF(UP_RIGHT) +TOKEN_DEF(DOWN_RIGHT) +TOKEN_DEF(RIGHT) +TOKEN_DEF(UP) +TOKEN_DEF(DOWN) +TOKEN_DEF(TEMPLATE) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool AdSpriteSet::loadBuffer(byte *buffer, bool complete, int lifeTime, TSpriteCacheType cacheType) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(SPRITESET) + TOKEN_TABLE(NAME) + TOKEN_TABLE(UP_LEFT) + TOKEN_TABLE(DOWN_LEFT) + TOKEN_TABLE(LEFT) + TOKEN_TABLE(UP_RIGHT) + TOKEN_TABLE(DOWN_RIGHT) + TOKEN_TABLE(RIGHT) + TOKEN_TABLE(UP) + TOKEN_TABLE(DOWN) + TOKEN_TABLE(TEMPLATE) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE_END + + byte *params; + int cmd; + BaseParser parser; + + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_SPRITESET) { + _gameRef->LOG(0, "'SPRITESET' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + BaseSprite *spr = NULL; + while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_TEMPLATE: + if (DID_FAIL(loadFile((char *)params, lifeTime, cacheType))) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_NAME: + setName((char *)params); + break; + + case TOKEN_LEFT: + delete _sprites[DI_LEFT]; + _sprites[DI_LEFT] = NULL; + spr = new BaseSprite(_gameRef, _owner); + if (!spr || DID_FAIL(spr->loadFile((char *)params, lifeTime, cacheType))) { + cmd = PARSERR_GENERIC; + } else { + _sprites[DI_LEFT] = spr; + } + break; + + case TOKEN_RIGHT: + delete _sprites[DI_RIGHT]; + _sprites[DI_RIGHT] = NULL; + spr = new BaseSprite(_gameRef, _owner); + if (!spr || DID_FAIL(spr->loadFile((char *)params, lifeTime, cacheType))) { + cmd = PARSERR_GENERIC; + } else { + _sprites[DI_RIGHT] = spr; + } + break; + + case TOKEN_UP: + delete _sprites[DI_UP]; + _sprites[DI_UP] = NULL; + spr = new BaseSprite(_gameRef, _owner); + if (!spr || DID_FAIL(spr->loadFile((char *)params, lifeTime, cacheType))) { + cmd = PARSERR_GENERIC; + } else { + _sprites[DI_UP] = spr; + } + break; + + case TOKEN_DOWN: + delete _sprites[DI_DOWN]; + _sprites[DI_DOWN] = NULL; + spr = new BaseSprite(_gameRef, _owner); + if (!spr || DID_FAIL(spr->loadFile((char *)params, lifeTime, cacheType))) { + cmd = PARSERR_GENERIC; + } else { + _sprites[DI_DOWN] = spr; + } + break; + + case TOKEN_UP_LEFT: + delete _sprites[DI_UPLEFT]; + _sprites[DI_UPLEFT] = NULL; + spr = new BaseSprite(_gameRef, _owner); + if (!spr || DID_FAIL(spr->loadFile((char *)params, lifeTime, cacheType))) { + cmd = PARSERR_GENERIC; + } else { + _sprites[DI_UPLEFT] = spr; + } + break; + + case TOKEN_UP_RIGHT: + delete _sprites[DI_UPRIGHT]; + _sprites[DI_UPRIGHT] = NULL; + spr = new BaseSprite(_gameRef, _owner); + if (!spr || DID_FAIL(spr->loadFile((char *)params, lifeTime, cacheType))) { + cmd = PARSERR_GENERIC; + } else { + _sprites[DI_UPRIGHT] = spr; + } + break; + + case TOKEN_DOWN_LEFT: + delete _sprites[DI_DOWNLEFT]; + _sprites[DI_DOWNLEFT] = NULL; + spr = new BaseSprite(_gameRef, _owner); + if (!spr || DID_FAIL(spr->loadFile((char *)params, lifeTime, cacheType))) { + cmd = PARSERR_GENERIC; + } else { + _sprites[DI_DOWNLEFT] = spr; + } + break; + + case TOKEN_DOWN_RIGHT: + delete _sprites[DI_DOWNRIGHT]; + _sprites[DI_DOWNRIGHT] = NULL; + spr = new BaseSprite(_gameRef, _owner); + if (!spr || DID_FAIL(spr->loadFile((char *)params, lifeTime, cacheType))) { + cmd = PARSERR_GENERIC; + } else { + _sprites[DI_DOWNRIGHT] = spr; + } + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty(params, false); + break; + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in SPRITESET definition"); + return STATUS_FAILED; + } + + if (cmd == PARSERR_GENERIC) { + _gameRef->LOG(0, "Error loading SPRITESET definition"); + if (spr) { + delete spr; + } + return STATUS_FAILED; + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdSpriteSet::persist(BasePersistenceManager *persistMgr) { + + BaseObject::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_owner)); + for (int i = 0; i < NUM_DIRECTIONS; i++) { + persistMgr->transfer("", &_sprites[i]); + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +BaseSprite *AdSpriteSet::getSprite(TDirection direction) { + int dir = (int)direction; + if (dir < 0) { + dir = 0; + } + if (dir >= NUM_DIRECTIONS) { + dir = NUM_DIRECTIONS - 1; + } + + BaseSprite *ret = NULL; + + // find nearest set sprite + int numSteps = 0; + for (int i = dir; i >= 0; i--) { + if (_sprites[i] != NULL) { + ret = _sprites[i]; + numSteps = dir - i; + break; + } + } + + for (int i = dir; i < NUM_DIRECTIONS; i++) { + if (_sprites[i] != NULL) { + if (ret == NULL || numSteps > i - dir) { + return _sprites[i]; + } else { + return ret; + } + } + } + + return ret; +} + + + +////////////////////////////////////////////////////////////////////////// +bool AdSpriteSet::saveAsText(BaseDynamicBuffer *buffer, int indent) { + buffer->putTextIndent(indent, "SPRITESET {\n"); + if (getName()) { + buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", getName()); + } + for (int i = 0; i < NUM_DIRECTIONS; i++) { + if (_sprites[i]) { + switch (i) { + case DI_UP: + buffer->putTextIndent(indent + 2, "UP=\"%s\"\n", _sprites[i]->getFilename()); + break; + case DI_UPRIGHT: + buffer->putTextIndent(indent + 2, "UP_RIGHT=\"%s\"\n", _sprites[i]->getFilename()); + break; + case DI_RIGHT: + buffer->putTextIndent(indent + 2, "RIGHT=\"%s\"\n", _sprites[i]->getFilename()); + break; + case DI_DOWNRIGHT: + buffer->putTextIndent(indent + 2, "DOWN_RIGHT=\"%s\"\n", _sprites[i]->getFilename()); + break; + case DI_DOWN: + buffer->putTextIndent(indent + 2, "DOWN=\"%s\"\n", _sprites[i]->getFilename()); + break; + case DI_DOWNLEFT: + buffer->putTextIndent(indent + 2, "DOWN_LEFT=\"%s\"\n", _sprites[i]->getFilename()); + break; + case DI_LEFT: + buffer->putTextIndent(indent + 2, "LEFT=\"%s\"\n", _sprites[i]->getFilename()); + break; + case DI_UPLEFT: + buffer->putTextIndent(indent + 2, "UP_LEFT=\"%s\"\n", _sprites[i]->getFilename()); + break; + } + } + } + + BaseClass::saveAsText(buffer, indent + 2); + + buffer->putTextIndent(indent, "}\n"); + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool AdSpriteSet::containsSprite(BaseSprite *sprite) { + if (!sprite) { + return false; + } + + for (int i = 0; i < NUM_DIRECTIONS; i++) { + if (_sprites[i] == sprite) { + return true; + } + } + return false; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_sprite_set.h b/engines/wintermute/ad/ad_sprite_set.h new file mode 100644 index 0000000000..ba5da0ff2e --- /dev/null +++ b/engines/wintermute/ad/ad_sprite_set.h @@ -0,0 +1,53 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADSPRITESET_H +#define WINTERMUTE_ADSPRITESET_H + + +#include "engines/wintermute/base/base_object.h" + +namespace Wintermute { +class BaseSprite; +class AdSpriteSet : public BaseObject { +public: + bool containsSprite(BaseSprite *sprite); + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent = 0); + BaseSprite *getSprite(TDirection direction); + DECLARE_PERSISTENT(AdSpriteSet, BaseObject) + BaseObject *_owner; + AdSpriteSet(BaseGame *inGame, BaseObject *owner = NULL); + virtual ~AdSpriteSet(); + bool loadFile(const char *filename, int lifeTime = -1, TSpriteCacheType cacheType = CACHE_ALL); + bool loadBuffer(byte *buffer, bool complete = true, int lifeTime = -1, TSpriteCacheType cacheType = CACHE_ALL); + BaseSprite *_sprites[NUM_DIRECTIONS]; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_talk_def.cpp b/engines/wintermute/ad/ad_talk_def.cpp new file mode 100644 index 0000000000..a85cd7f986 --- /dev/null +++ b/engines/wintermute/ad/ad_talk_def.cpp @@ -0,0 +1,285 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_sprite_set.h" +#include "engines/wintermute/ad/ad_talk_def.h" +#include "engines/wintermute/ad/ad_talk_node.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/utils/utils.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdTalkDef, false) + +////////////////////////////////////////////////////////////////////////// +AdTalkDef::AdTalkDef(BaseGame *inGame) : BaseObject(inGame) { + _defaultSpriteFilename = NULL; + _defaultSprite = NULL; + + _defaultSpriteSetFilename = NULL; + _defaultSpriteSet = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +AdTalkDef::~AdTalkDef() { + for (uint32 i = 0; i < _nodes.size(); i++) { + delete _nodes[i]; + } + _nodes.clear(); + + delete[] _defaultSpriteFilename; + delete _defaultSprite; + _defaultSpriteFilename = NULL; + _defaultSprite = NULL; + + delete[] _defaultSpriteSetFilename; + delete _defaultSpriteSet; + _defaultSpriteSetFilename = NULL; + _defaultSpriteSet = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdTalkDef::loadFile(const char *filename) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "AdTalkDef::LoadFile failed for file '%s'", filename); + return STATUS_FAILED; + } + + bool ret; + + setFilename(filename); + + if (DID_FAIL(ret = loadBuffer(buffer, true))) { + _gameRef->LOG(0, "Error parsing TALK file '%s'", filename); + } + + delete[] buffer; + + return ret; +} + + +TOKEN_DEF_START +TOKEN_DEF(TALK) +TOKEN_DEF(TEMPLATE) +TOKEN_DEF(ACTION) +TOKEN_DEF(DEFAULT_SPRITESET_FILE) +TOKEN_DEF(DEFAULT_SPRITESET) +TOKEN_DEF(DEFAULT_SPRITE) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool AdTalkDef::loadBuffer(byte *buffer, bool complete) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(TALK) + TOKEN_TABLE(TEMPLATE) + TOKEN_TABLE(ACTION) + TOKEN_TABLE(DEFAULT_SPRITESET_FILE) + TOKEN_TABLE(DEFAULT_SPRITESET) + TOKEN_TABLE(DEFAULT_SPRITE) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE_END + + byte *params; + int cmd; + BaseParser parser; + + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_TALK) { + _gameRef->LOG(0, "'TALK' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_TEMPLATE: + if (DID_FAIL(loadFile((char *)params))) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_ACTION: { + AdTalkNode *node = new AdTalkNode(_gameRef); + if (node && DID_SUCCEED(node->loadBuffer(params, false))) { + _nodes.add(node); + } else { + delete node; + node = NULL; + cmd = PARSERR_GENERIC; + } + } + break; + + case TOKEN_DEFAULT_SPRITE: + BaseUtils::setString(&_defaultSpriteFilename, (char *)params); + break; + + case TOKEN_DEFAULT_SPRITESET_FILE: + BaseUtils::setString(&_defaultSpriteSetFilename, (char *)params); + break; + + case TOKEN_DEFAULT_SPRITESET: { + delete _defaultSpriteSet; + _defaultSpriteSet = new AdSpriteSet(_gameRef); + if (!_defaultSpriteSet || DID_FAIL(_defaultSpriteSet->loadBuffer(params, false))) { + delete _defaultSpriteSet; + _defaultSpriteSet = NULL; + cmd = PARSERR_GENERIC; + } + } + break; + + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty(params, false); + break; + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in TALK definition"); + return STATUS_FAILED; + } + + if (cmd == PARSERR_GENERIC) { + _gameRef->LOG(0, "Error loading TALK definition"); + return STATUS_FAILED; + } + + delete _defaultSprite; + delete _defaultSpriteSet; + _defaultSprite = NULL; + _defaultSpriteSet = NULL; + + if (_defaultSpriteFilename) { + _defaultSprite = new BaseSprite(_gameRef); + if (!_defaultSprite || DID_FAIL(_defaultSprite->loadFile(_defaultSpriteFilename))) { + return STATUS_FAILED; + } + } + + if (_defaultSpriteSetFilename) { + _defaultSpriteSet = new AdSpriteSet(_gameRef); + if (!_defaultSpriteSet || DID_FAIL(_defaultSpriteSet->loadFile(_defaultSpriteSetFilename))) { + return STATUS_FAILED; + } + } + + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdTalkDef::persist(BasePersistenceManager *persistMgr) { + + BaseObject::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_defaultSprite)); + persistMgr->transfer(TMEMBER(_defaultSpriteFilename)); + persistMgr->transfer(TMEMBER(_defaultSpriteSet)); + persistMgr->transfer(TMEMBER(_defaultSpriteSetFilename)); + + _nodes.persist(persistMgr); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdTalkDef::saveAsText(BaseDynamicBuffer *buffer, int indent) { + buffer->putTextIndent(indent, "TALK {\n"); + if (_defaultSpriteFilename) { + buffer->putTextIndent(indent + 2, "DEFAULT_SPRITE=\"%s\"\n", _defaultSpriteFilename); + } + + if (_defaultSpriteSetFilename) { + buffer->putTextIndent(indent + 2, "DEFAULT_SPRITESET_FILE=\"%s\"\n", _defaultSpriteSetFilename); + } else if (_defaultSpriteSet) { + _defaultSpriteSet->saveAsText(buffer, indent + 2); + } + + for (uint32 i = 0; i < _nodes.size(); i++) { + _nodes[i]->saveAsText(buffer, indent + 2); + buffer->putTextIndent(indent, "\n"); + } + BaseClass::saveAsText(buffer, indent + 2); + + buffer->putTextIndent(indent, "}\n"); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdTalkDef::loadDefaultSprite() { + if (_defaultSpriteFilename && !_defaultSprite) { + _defaultSprite = new BaseSprite(_gameRef); + if (!_defaultSprite || DID_FAIL(_defaultSprite->loadFile(_defaultSpriteFilename))) { + delete _defaultSprite; + _defaultSprite = NULL; + return STATUS_FAILED; + } else { + return STATUS_OK; + } + } else if (_defaultSpriteSetFilename && !_defaultSpriteSet) { + _defaultSpriteSet = new AdSpriteSet(_gameRef); + if (!_defaultSpriteSet || DID_FAIL(_defaultSpriteSet->loadFile(_defaultSpriteSetFilename))) { + delete _defaultSpriteSet; + _defaultSpriteSet = NULL; + return STATUS_FAILED; + } else { + return STATUS_OK; + } + } else { + return STATUS_OK; + } +} + + +////////////////////////////////////////////////////////////////////////// +BaseSprite *AdTalkDef::getDefaultSprite(TDirection dir) { + loadDefaultSprite(); + if (_defaultSprite) { + return _defaultSprite; + } else if (_defaultSpriteSet) { + return _defaultSpriteSet->getSprite(dir); + } else { + return NULL; + } +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_talk_def.h b/engines/wintermute/ad/ad_talk_def.h new file mode 100644 index 0000000000..d147212775 --- /dev/null +++ b/engines/wintermute/ad/ad_talk_def.h @@ -0,0 +1,58 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADTALKDEF_H +#define WINTERMUTE_ADTALKDEF_H + +#include "engines/wintermute/coll_templ.h" +#include "engines/wintermute/base/base_object.h" + +namespace Wintermute { +class AdTalkNode; +class AdSpriteSet; +class AdTalkDef : public BaseObject { +public: + char *_defaultSpriteSetFilename; + AdSpriteSet *_defaultSpriteSet; + BaseSprite *getDefaultSprite(TDirection Dir); + bool loadDefaultSprite(); + DECLARE_PERSISTENT(AdTalkDef, BaseObject) + + AdTalkDef(BaseGame *inGame); + virtual ~AdTalkDef(); + bool loadFile(const char *filename); + bool loadBuffer(byte *buffer, bool complete = true); + BaseArray<AdTalkNode *> _nodes; + char *_defaultSpriteFilename; + BaseSprite *_defaultSprite; + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent = 0); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_talk_holder.cpp b/engines/wintermute/ad/ad_talk_holder.cpp new file mode 100644 index 0000000000..937a4b333d --- /dev/null +++ b/engines/wintermute/ad/ad_talk_holder.cpp @@ -0,0 +1,402 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_talk_holder.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/base_engine.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/platform_osystem.h" +#include "common/str.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdTalkHolder, false) + +////////////////////////////////////////////////////////////////////////// +AdTalkHolder::AdTalkHolder(BaseGame *inGame) : AdObject(inGame) { + _sprite = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +AdTalkHolder::~AdTalkHolder() { + delete _sprite; + _sprite = NULL; + + for (uint32 i = 0; i < _talkSprites.size(); i++) { + delete _talkSprites[i]; + } + _talkSprites.clear(); + + for (uint32 i = 0; i < _talkSpritesEx.size(); i++) { + delete _talkSpritesEx[i]; + } + _talkSpritesEx.clear(); +} + +////////////////////////////////////////////////////////////////////////// +BaseSprite *AdTalkHolder::getTalkStance(const char *stance) { + BaseSprite *ret = NULL; + + + // forced stance? + if (_forcedTalkAnimName && !_forcedTalkAnimUsed) { + _forcedTalkAnimUsed = true; + delete _animSprite; + _animSprite = new BaseSprite(_gameRef, this); + if (_animSprite) { + bool res = _animSprite->loadFile(_forcedTalkAnimName); + if (DID_FAIL(res)) { + _gameRef->LOG(res, "AdTalkHolder::GetTalkStance: error loading talk sprite (object:\"%s\" sprite:\"%s\")", getName(), _forcedTalkAnimName); + delete _animSprite; + _animSprite = NULL; + } else { + return _animSprite; + } + } + } + + + if (stance != NULL) { + // search special talk stances + for (uint32 i = 0; i < _talkSpritesEx.size(); i++) { + if (scumm_stricmp(_talkSpritesEx[i]->getName(), stance) == 0) { + ret = _talkSpritesEx[i]; + break; + } + } + if (ret == NULL) { + // serach generic talk stances + for (uint32 i = 0; i < _talkSprites.size(); i++) { + if (scumm_stricmp(_talkSprites[i]->getName(), stance) == 0) { + ret = _talkSprites[i]; + break; + } + } + } + } + + // not a valid stance? get a random one + if (ret == NULL) { + if (_talkSprites.size() < 1) { + ret = _sprite; + } else { + // TODO: remember last + int rnd = BaseEngine::instance().randInt(0, _talkSprites.size() - 1); + ret = _talkSprites[rnd]; + } + } + + return ret; +} + + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool AdTalkHolder::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // SetSprite + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "SetSprite") == 0) { + stack->correctParams(1); + + ScValue *val = stack->pop(); + + bool setCurrent = false; + if (_currentSprite && _currentSprite == _sprite) { + setCurrent = true; + } + + delete _sprite; + _sprite = NULL; + + if (val->isNULL()) { + _sprite = NULL; + if (setCurrent) { + _currentSprite = NULL; + } + stack->pushBool(true); + } else { + const char *filename = val->getString(); + BaseSprite *spr = new BaseSprite(_gameRef, this); + if (!spr || DID_FAIL(spr->loadFile(filename))) { + script->runtimeError("SetSprite method failed for file '%s'", filename); + stack->pushBool(false); + } else { + _sprite = spr; + if (setCurrent) { + _currentSprite = _sprite; + } + stack->pushBool(true); + } + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetSprite + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetSprite") == 0) { + stack->correctParams(0); + + if (!_sprite || !_sprite->getFilename()) { + stack->pushNULL(); + } else { + stack->pushString(_sprite->getFilename()); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetSpriteObject + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetSpriteObject") == 0) { + stack->correctParams(0); + + if (!_sprite) { + stack->pushNULL(); + } else { + stack->pushNative(_sprite, true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // AddTalkSprite + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AddTalkSprite") == 0) { + stack->correctParams(2); + + const char *filename = stack->pop()->getString(); + bool ex = stack->pop()->getBool(); + + BaseSprite *spr = new BaseSprite(_gameRef, this); + if (!spr || DID_FAIL(spr->loadFile(filename))) { + stack->pushBool(false); + script->runtimeError("AddTalkSprite method failed for file '%s'", filename); + } else { + if (ex) { + _talkSpritesEx.add(spr); + } else { + _talkSprites.add(spr); + } + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // RemoveTalkSprite + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "RemoveTalkSprite") == 0) { + stack->correctParams(2); + + const char *filename = stack->pop()->getString(); + bool ex = stack->pop()->getBool(); + + bool setCurrent = false; + bool setTemp2 = false; + + if (ex) { + for (uint32 i = 0; i < _talkSpritesEx.size(); i++) { + if (scumm_stricmp(_talkSpritesEx[i]->getFilename(), filename) == 0) { + if (_currentSprite == _talkSpritesEx[i]) { + setCurrent = true; + } + if (_tempSprite2 == _talkSpritesEx[i]) { + setTemp2 = true; + } + delete _talkSpritesEx[i]; + _talkSpritesEx.remove_at(i); + break; + } + } + } else { + for (uint32 i = 0; i < _talkSprites.size(); i++) { + if (scumm_stricmp(_talkSprites[i]->getFilename(), filename) == 0) { + if (_currentSprite == _talkSprites[i]) { + setCurrent = true; + } + if (_tempSprite2 == _talkSprites[i]) { + setTemp2 = true; + } + delete _talkSprites[i]; + _talkSprites.remove_at(i); + break; + } + } + + } + + stack->pushBool(true); + if (setCurrent) { + _currentSprite = _sprite; + } + if (setTemp2) { + _tempSprite2 = _sprite; + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetTalkSprite + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetTalkSprite") == 0) { + stack->correctParams(2); + + const char *filename = stack->pop()->getString(); + bool ex = stack->pop()->getBool(); + bool setCurrent = false; + bool setTemp2 = false; + + BaseSprite *spr = new BaseSprite(_gameRef, this); + if (!spr || DID_FAIL(spr->loadFile(filename))) { + stack->pushBool(false); + script->runtimeError("SetTalkSprite method failed for file '%s'", filename); + } else { + + // delete current + if (ex) { + for (uint32 i = 0; i < _talkSpritesEx.size(); i++) { + if (_talkSpritesEx[i] == _currentSprite) { + setCurrent = true; + } + if (_talkSpritesEx[i] == _tempSprite2) { + setTemp2 = true; + } + delete _talkSpritesEx[i]; + } + _talkSpritesEx.clear(); + } else { + for (uint32 i = 0; i < _talkSprites.size(); i++) { + if (_talkSprites[i] == _currentSprite) { + setCurrent = true; + } + if (_talkSprites[i] == _tempSprite2) { + setTemp2 = true; + } + delete _talkSprites[i]; + } + _talkSprites.clear(); + } + + // set new + if (ex) { + _talkSpritesEx.add(spr); + } else { + _talkSprites.add(spr); + } + stack->pushBool(true); + + if (setCurrent) { + _currentSprite = spr; + } + if (setTemp2) { + _tempSprite2 = spr; + } + } + return STATUS_OK; + } else { + return AdObject::scCallMethod(script, stack, thisStack, name); + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *AdTalkHolder::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type (RO) + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("talk-holder"); + return _scValue; + } else { + return AdObject::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool AdTalkHolder::scSetProperty(const char *name, ScValue *value) { + /* + ////////////////////////////////////////////////////////////////////////// + // Item + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Item")==0) { + SetItem(value->getString()); + return STATUS_OK; + } + + else*/ return AdObject::scSetProperty(name, value); +} + + +////////////////////////////////////////////////////////////////////////// +const char *AdTalkHolder::scToString() { + return "[talk-holder object]"; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdTalkHolder::saveAsText(BaseDynamicBuffer *buffer, int indent) { + for (uint32 i = 0; i < _talkSprites.size(); i++) { + if (_talkSprites[i]->getFilename()) { + buffer->putTextIndent(indent + 2, "TALK=\"%s\"\n", _talkSprites[i]->getFilename()); + } + } + + for (uint32 i = 0; i < _talkSpritesEx.size(); i++) { + if (_talkSpritesEx[i]->getFilename()) { + buffer->putTextIndent(indent + 2, "TALK_SPECIAL=\"%s\"\n", _talkSpritesEx[i]->getFilename()); + } + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdTalkHolder::persist(BasePersistenceManager *persistMgr) { + AdObject::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_sprite)); + _talkSprites.persist(persistMgr); + _talkSpritesEx.persist(persistMgr); + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_talk_holder.h b/engines/wintermute/ad/ad_talk_holder.h new file mode 100644 index 0000000000..d52ebf63c0 --- /dev/null +++ b/engines/wintermute/ad/ad_talk_holder.h @@ -0,0 +1,57 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADTALKHOLDER_H +#define WINTERMUTE_ADTALKHOLDER_H + +#include "engines/wintermute/ad/ad_object.h" + +namespace Wintermute { + +class AdTalkHolder : public AdObject { +public: + DECLARE_PERSISTENT(AdTalkHolder, AdObject) + virtual BaseSprite *getTalkStance(const char *stance); + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent); + BaseSprite *_sprite; + BaseArray<BaseSprite *> _talkSprites; + BaseArray<BaseSprite *> _talkSpritesEx; + AdTalkHolder(BaseGame *inGame); + virtual ~AdTalkHolder(); + + // scripting interface + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + virtual const char *scToString(); + +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_talk_node.cpp b/engines/wintermute/ad/ad_talk_node.cpp new file mode 100644 index 0000000000..c909ee27ff --- /dev/null +++ b/engines/wintermute/ad/ad_talk_node.cpp @@ -0,0 +1,295 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_sprite_set.h" +#include "engines/wintermute/ad/ad_talk_node.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/utils/utils.h" +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdTalkNode, false) + +////////////////////////////////////////////////////////////////////////// +AdTalkNode::AdTalkNode(BaseGame *inGame) : BaseClass(inGame) { + _sprite = NULL; + _spriteFilename = NULL; + _spriteSet = NULL; + _spriteSetFilename = NULL; + _comment = NULL; + + _startTime = _endTime = 0; + _playToEnd = false; + _preCache = false; +} + + +////////////////////////////////////////////////////////////////////////// +AdTalkNode::~AdTalkNode() { + delete[] _spriteFilename; + delete _sprite; + delete[] _spriteSetFilename; + delete _spriteSet; + delete _comment; + _spriteFilename = NULL; + _sprite = NULL; + _spriteSetFilename = NULL; + _spriteSet = NULL; + _comment = NULL; +} + + + +TOKEN_DEF_START +TOKEN_DEF(ACTION) +TOKEN_DEF(SPRITESET_FILE) +TOKEN_DEF(SPRITESET) +TOKEN_DEF(SPRITE) +TOKEN_DEF(START_TIME) +TOKEN_DEF(END_TIME) +TOKEN_DEF(COMMENT) +TOKEN_DEF(PRECACHE) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool AdTalkNode::loadBuffer(byte *buffer, bool complete) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(ACTION) + TOKEN_TABLE(SPRITESET_FILE) + TOKEN_TABLE(SPRITESET) + TOKEN_TABLE(SPRITE) + TOKEN_TABLE(START_TIME) + TOKEN_TABLE(END_TIME) + TOKEN_TABLE(COMMENT) + TOKEN_TABLE(PRECACHE) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE_END + + byte *params; + int cmd; + BaseParser parser; + + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_ACTION) { + _gameRef->LOG(0, "'ACTION' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + _endTime = 0; + _playToEnd = false; + _preCache = false; + + while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_SPRITE: + BaseUtils::setString(&_spriteFilename, (char *)params); + break; + + case TOKEN_SPRITESET_FILE: + BaseUtils::setString(&_spriteSetFilename, (char *)params); + break; + + case TOKEN_SPRITESET: { + delete _spriteSet; + _spriteSet = new AdSpriteSet(_gameRef); + if (!_spriteSet || DID_FAIL(_spriteSet->loadBuffer(params, false))) { + delete _spriteSet; + _spriteSet = NULL; + cmd = PARSERR_GENERIC; + } + } + break; + + case TOKEN_START_TIME: + parser.scanStr((char *)params, "%d", &_startTime); + break; + + case TOKEN_END_TIME: + parser.scanStr((char *)params, "%d", &_endTime); + break; + + case TOKEN_PRECACHE: + parser.scanStr((char *)params, "%b", &_preCache); + break; + + case TOKEN_COMMENT: + if (_gameRef->_editorMode) { + BaseUtils::setString(&_comment, (char *)params); + } + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty(params, false); + break; + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in ACTION definition"); + return STATUS_FAILED; + } + + if (cmd == PARSERR_GENERIC) { + _gameRef->LOG(0, "Error loading ACTION definition"); + return STATUS_FAILED; + } + + if (_endTime == 0) { + _playToEnd = true; + } else { + _playToEnd = false; + } + + if (_preCache && _spriteFilename) { + delete _sprite; + _sprite = new BaseSprite(_gameRef); + if (!_sprite || DID_FAIL(_sprite->loadFile(_spriteFilename))) { + return STATUS_FAILED; + } + } + + if (_preCache && _spriteSetFilename) { + delete _spriteSet; + _spriteSet = new AdSpriteSet(_gameRef); + if (!_spriteSet || DID_FAIL(_spriteSet->loadFile(_spriteSetFilename))) { + return STATUS_FAILED; + } + } + + return STATUS_OK; +} + + + +////////////////////////////////////////////////////////////////////////// +bool AdTalkNode::persist(BasePersistenceManager *persistMgr) { + persistMgr->transfer(TMEMBER(_comment)); + persistMgr->transfer(TMEMBER(_startTime)); + persistMgr->transfer(TMEMBER(_endTime)); + persistMgr->transfer(TMEMBER(_playToEnd)); + persistMgr->transfer(TMEMBER(_sprite)); + persistMgr->transfer(TMEMBER(_spriteFilename)); + persistMgr->transfer(TMEMBER(_spriteSet)); + persistMgr->transfer(TMEMBER(_spriteSetFilename)); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdTalkNode::saveAsText(BaseDynamicBuffer *buffer, int indent) { + buffer->putTextIndent(indent, "ACTION {\n"); + if (_comment) { + buffer->putTextIndent(indent + 2, "COMMENT=\"%s\"\n", _comment); + } + buffer->putTextIndent(indent + 2, "START_TIME=%d\n", _startTime); + if (!_playToEnd) { + buffer->putTextIndent(indent + 2, "END_TIME=%d\n", _endTime); + } + if (_spriteFilename) { + buffer->putTextIndent(indent + 2, "SPRITE=\"%s\"\n", _spriteFilename); + } + if (_spriteSetFilename) { + buffer->putTextIndent(indent + 2, "SPRITESET_FILE=\"%s\"\n", _spriteSetFilename); + } else if (_spriteSet) { + _spriteSet->saveAsText(buffer, indent + 2); + } + if (_preCache) { + buffer->putTextIndent(indent + 2, "PRECACHE=\"%s\"\n", _preCache ? "TRUE" : "FALSE"); + } + + BaseClass::saveAsText(buffer, indent + 2); + + buffer->putTextIndent(indent, "}\n"); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdTalkNode::loadSprite() { + if (_spriteFilename && !_sprite) { + _sprite = new BaseSprite(_gameRef); + if (!_sprite || DID_FAIL(_sprite->loadFile(_spriteFilename))) { + delete _sprite; + _sprite = NULL; + return STATUS_FAILED; + } else { + return STATUS_OK; + } + } else if (_spriteSetFilename && !_spriteSet) { + _spriteSet = new AdSpriteSet(_gameRef); + if (!_spriteSet || DID_FAIL(_spriteSet->loadFile(_spriteSetFilename))) { + delete _spriteSet; + _spriteSet = NULL; + return STATUS_FAILED; + } else { + return STATUS_OK; + } + } else { + return STATUS_OK; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool AdTalkNode::isInTimeInterval(uint32 time, TDirection dir) { + if (time >= _startTime) { + if (_playToEnd) { + if ((_spriteFilename && _sprite == NULL) || (_sprite && _sprite->isFinished() == false)) { + return true; + } else if ((_spriteSetFilename && _spriteSet == NULL) || (_spriteSet && _spriteSet->getSprite(dir) && _spriteSet->getSprite(dir)->isFinished() == false)) { + return true; + } else { + return false; + } + } else { + return _endTime >= time; + } + } else { + return false; + } +} + + +////////////////////////////////////////////////////////////////////////// +BaseSprite *AdTalkNode::getSprite(TDirection dir) { + loadSprite(); + if (_sprite) { + return _sprite; + } else if (_spriteSet) { + return _spriteSet->getSprite(dir); + } else { + return NULL; + } +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_talk_node.h b/engines/wintermute/ad/ad_talk_node.h new file mode 100644 index 0000000000..7dfd861f85 --- /dev/null +++ b/engines/wintermute/ad/ad_talk_node.h @@ -0,0 +1,63 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADTALKNODE_H +#define WINTERMUTE_ADTALKNODE_H + +#include "engines/wintermute/persistent.h" +#include "engines/wintermute/base/base.h" + +namespace Wintermute { +class AdSpriteSet; +class BaseSprite; +class AdTalkNode : public BaseClass { +public: + char *_spriteSetFilename; + AdSpriteSet *_spriteSet; + BaseSprite *getSprite(TDirection dir); + bool isInTimeInterval(uint32 time, TDirection dir); + bool loadSprite(); + DECLARE_PERSISTENT(AdTalkNode, BaseClass) + + AdTalkNode(BaseGame *inGame); + virtual ~AdTalkNode(); + bool loadBuffer(byte *buffer, bool complete = true); + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent = 0); + char *_spriteFilename; + BaseSprite *_sprite; + uint32 _startTime; + uint32 _endTime; + bool _playToEnd; + bool _preCache; + char *_comment; + +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_types.h b/engines/wintermute/ad/ad_types.h new file mode 100644 index 0000000000..ae5882f4ee --- /dev/null +++ b/engines/wintermute/ad/ad_types.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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADTYPES_H +#define WINTERMUTE_ADTYPES_H + +namespace Wintermute { + +typedef enum { + GAME_NORMAL, + GAME_WAITING_RESPONSE +} TGameStateEx; + +typedef enum { + OBJECT_ENTITY, + OBJECT_REGION, + OBJECT_ACTOR, + OBJECT_NONE +} TObjectType; + +typedef enum { + ENTITY_NORMAL, + ENTITY_SOUND +} TEntityType; + +typedef enum { + STATE_NONE, + STATE_IDLE, + STATE_PLAYING_ANIM, + STATE_READY, + STATE_FOLLOWING_PATH, + STATE_SEARCHING_PATH, + STATE_WAITING_PATH, + STATE_TURNING_LEFT, + STATE_TURNING_RIGHT, + STATE_TURNING, + STATE_TALKING, + STATE_DIRECT_CONTROL, + STATE_PLAYING_ANIM_SET +} TObjectState; + +typedef enum { + DIRECT_WALK_NONE, + DIRECT_WALK_FW, + DIRECT_WALK_BK +} TDirectWalkMode; + +typedef enum { + DIRECT_TURN_NONE, + DIRECT_TURN_CW, + DIRECT_TURN_CCW +} TDirectTurnMode; + +typedef enum { + RESPONSE_TEXT, + RESPONSE_ICON +} TResponseStyle; + +typedef enum { + RESPONSE_ALWAYS, + RESPONSE_ONCE, + RESPONSE_ONCE_GAME +} TResponseType; + + +typedef enum { + TALK_SKIP_LEFT = 0, + TALK_SKIP_RIGHT = 1, + TALK_SKIP_BOTH = 2, + TALK_SKIP_NONE = 3 +} TTalkSkipButton; + +typedef enum { + GEOM_WAYPOINT, + GEOM_WALKPLANE, + GEOM_BLOCKED, + GEOM_GENERIC +} TGeomNodeType; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_waypoint_group.cpp b/engines/wintermute/ad/ad_waypoint_group.cpp new file mode 100644 index 0000000000..81493ce769 --- /dev/null +++ b/engines/wintermute/ad/ad_waypoint_group.cpp @@ -0,0 +1,270 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_waypoint_group.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_region.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include <limits.h> + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdWaypointGroup, false) + +////////////////////////////////////////////////////////////////////////// +AdWaypointGroup::AdWaypointGroup(BaseGame *inGame) : BaseObject(inGame) { + _active = true; + _editorSelectedPoint = -1; + _lastMimicScale = -1; + _lastMimicX = _lastMimicY = INT_MIN; +} + + +////////////////////////////////////////////////////////////////////////// +AdWaypointGroup::~AdWaypointGroup() { + cleanup(); +} + + +////////////////////////////////////////////////////////////////////////// +void AdWaypointGroup::cleanup() { + for (uint32 i = 0; i < _points.size(); i++) { + delete _points[i]; + } + _points.clear(); + _editorSelectedPoint = -1; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdWaypointGroup::loadFile(const char *filename) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "AdWaypointGroup::LoadFile failed for file '%s'", filename); + return STATUS_FAILED; + } + + bool ret; + + setFilename(filename); + + if (DID_FAIL(ret = loadBuffer(buffer, true))) { + _gameRef->LOG(0, "Error parsing WAYPOINTS file '%s'", filename); + } + + + delete[] buffer; + + return ret; +} + + +TOKEN_DEF_START +TOKEN_DEF(WAYPOINTS) +TOKEN_DEF(TEMPLATE) +TOKEN_DEF(NAME) +TOKEN_DEF(POINT) +TOKEN_DEF(EDITOR_SELECTED_POINT) +TOKEN_DEF(EDITOR_SELECTED) +TOKEN_DEF(PROPERTY) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool AdWaypointGroup::loadBuffer(byte *buffer, bool complete) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(WAYPOINTS) + TOKEN_TABLE(TEMPLATE) + TOKEN_TABLE(NAME) + TOKEN_TABLE(POINT) + TOKEN_TABLE(EDITOR_SELECTED_POINT) + TOKEN_TABLE(EDITOR_SELECTED) + TOKEN_TABLE(PROPERTY) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE_END + + byte *params; + int cmd; + BaseParser parser; + + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_WAYPOINTS) { + _gameRef->LOG(0, "'WAYPOINTS' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_TEMPLATE: + if (DID_FAIL(loadFile((char *)params))) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_NAME: + setName((char *)params); + break; + + case TOKEN_POINT: { + int x, y; + parser.scanStr((char *)params, "%d,%d", &x, &y); + _points.add(new BasePoint(x, y)); + } + break; + + case TOKEN_EDITOR_SELECTED: + parser.scanStr((char *)params, "%b", &_editorSelected); + break; + + case TOKEN_EDITOR_SELECTED_POINT: + parser.scanStr((char *)params, "%d", &_editorSelectedPoint); + break; + + case TOKEN_PROPERTY: + parseProperty(params, false); + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty(params, false); + break; + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in WAYPOINTS definition"); + return STATUS_FAILED; + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdWaypointGroup::saveAsText(BaseDynamicBuffer *buffer, int indent) { + buffer->putTextIndent(indent, "WAYPOINTS {\n"); + buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", getName()); + buffer->putTextIndent(indent + 2, "EDITOR_SELECTED=%s\n", _editorSelected ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "EDITOR_SELECTED_POINT=%d\n", _editorSelectedPoint); + + if (_scProp) { + _scProp->saveAsText(buffer, indent + 2); + } + BaseClass::saveAsText(buffer, indent + 2); + + for (uint32 i = 0; i < _points.size(); i++) { + buffer->putTextIndent(indent + 2, "POINT {%d,%d}\n", _points[i]->x, _points[i]->y); + } + + buffer->putTextIndent(indent, "}\n"); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdWaypointGroup::persist(BasePersistenceManager *persistMgr) { + + BaseObject::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_active)); + persistMgr->transfer(TMEMBER(_editorSelectedPoint)); + persistMgr->transfer(TMEMBER(_lastMimicScale)); + persistMgr->transfer(TMEMBER(_lastMimicX)); + persistMgr->transfer(TMEMBER(_lastMimicY)); + _points.persist(persistMgr); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *AdWaypointGroup::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("waypoint-group"); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Active + ////////////////////////////////////////////////////////////////////////// + else if (name == "Active") { + _scValue->setBool(_active); + return _scValue; + } else { + return BaseObject::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool AdWaypointGroup::scSetProperty(const char *name, ScValue *value) { + ////////////////////////////////////////////////////////////////////////// + // Active + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Active") == 0) { + _active = value->getBool(); + return STATUS_OK; + } + + else { + return BaseObject::scSetProperty(name, value); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool AdWaypointGroup::mimic(AdWaypointGroup *wpt, float scale, int argX, int argY) { + if (scale == _lastMimicScale && argX == _lastMimicX && argY == _lastMimicY) { + return STATUS_OK; + } + + cleanup(); + + for (uint32 i = 0; i < wpt->_points.size(); i++) { + int x = (int)((float)wpt->_points[i]->x * scale / 100.0f); + int y = (int)((float)wpt->_points[i]->y * scale / 100.0f); + + _points.add(new BasePoint(x + argX, y + argY)); + } + + _lastMimicScale = scale; + _lastMimicX = argX; + _lastMimicY = argY; + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_waypoint_group.h b/engines/wintermute/ad/ad_waypoint_group.h new file mode 100644 index 0000000000..7e427313e6 --- /dev/null +++ b/engines/wintermute/ad/ad_waypoint_group.h @@ -0,0 +1,61 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADWAYPOINTGROUP_H +#define WINTERMUTE_ADWAYPOINTGROUP_H + +#include "engines/wintermute/base/base_object.h" + +namespace Wintermute { +class BasePoint; +class AdWaypointGroup : public BaseObject { +public: + void cleanup(); + bool mimic(AdWaypointGroup *wpt, float scale = 100.0f, int x = 0, int y = 0); + DECLARE_PERSISTENT(AdWaypointGroup, BaseObject) + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent); + AdWaypointGroup(BaseGame *inGame); + bool loadFile(const char *filename); + bool loadBuffer(byte *buffer, bool complete = true); + virtual ~AdWaypointGroup(); + + bool _active; + BaseArray<BasePoint *> _points; + + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); +private: + int _editorSelectedPoint; + float _lastMimicScale; + int _lastMimicX; + int _lastMimicY; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base.cpp b/engines/wintermute/base/base.cpp new file mode 100644 index 0000000000..e351792e61 --- /dev/null +++ b/engines/wintermute/base/base.cpp @@ -0,0 +1,185 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////// +BaseClass::BaseClass(BaseGame *gameOwner) { + _gameRef = gameOwner; + _persistable = true; +} + + +////////////////////////////////////////////////////////////////////////// +BaseClass::BaseClass() { + _gameRef = NULL; + _persistable = true; +} + + +////////////////////////////////////////////////////////////////////// +BaseClass::~BaseClass() { + _editorProps.clear(); +} + + +////////////////////////////////////////////////////////////////////////// +Common::String BaseClass::getEditorProp(const Common::String &propName, const Common::String &initVal) { + _editorPropsIter = _editorProps.find(propName); + if (_editorPropsIter != _editorProps.end()) { + return _editorPropsIter->_value.c_str(); + } else { + return initVal; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseClass::setEditorProp(const Common::String &propName, const Common::String &propValue) { + if (propName.size() == 0) { + return STATUS_FAILED; + } + + if (propValue.size() == 0) { + _editorProps.erase(propName); + } else { + _editorProps[propName] = propValue; + } + return STATUS_OK; +} + + + +TOKEN_DEF_START +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF(NAME) +TOKEN_DEF(VALUE) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool BaseClass::parseEditorProperty(byte *buffer, bool complete) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE(NAME) + TOKEN_TABLE(VALUE) + TOKEN_TABLE_END + + + if (!_gameRef->_editorMode) { + return STATUS_OK; + } + + + byte *params; + int cmd; + BaseParser parser; + + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_EDITOR_PROPERTY) { + _gameRef->LOG(0, "'EDITOR_PROPERTY' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + char *propName = NULL; + char *propValue = NULL; + + while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_NAME: + delete[] propName; + propName = new char[strlen((char *)params) + 1]; + if (propName) { + strcpy(propName, (char *)params); + } else { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_VALUE: + delete[] propValue; + propValue = new char[strlen((char *)params) + 1]; + if (propValue) { + strcpy(propValue, (char *)params); + } else { + cmd = PARSERR_GENERIC; + } + break; + } + + } + if (cmd == PARSERR_TOKENNOTFOUND) { + delete[] propName; + delete[] propValue; + propName = NULL; + propValue = NULL; + _gameRef->LOG(0, "Syntax error in EDITOR_PROPERTY definition"); + return STATUS_FAILED; + } + if (cmd == PARSERR_GENERIC || propName == NULL || propValue == NULL) { + delete[] propName; + delete[] propValue; + propName = NULL; + propValue = NULL; + _gameRef->LOG(0, "Error loading EDITOR_PROPERTY definition"); + return STATUS_FAILED; + } + + + setEditorProp(propName, propValue); + + delete[] propName; + delete[] propValue; + propName = NULL; + propValue = NULL; + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseClass::saveAsText(BaseDynamicBuffer *buffer, int indent) { + _editorPropsIter = _editorProps.begin(); + while (_editorPropsIter != _editorProps.end()) { + buffer->putTextIndent(indent, "EDITOR_PROPERTY\n"); + buffer->putTextIndent(indent, "{\n"); + buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", _editorPropsIter->_key.c_str()); + buffer->putTextIndent(indent + 2, "VALUE=\"%s\"\n", _editorPropsIter->_value.c_str()); + buffer->putTextIndent(indent, "}\n\n"); + + _editorPropsIter++; + } + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base.h b/engines/wintermute/base/base.h new file mode 100644 index 0000000000..24820c748a --- /dev/null +++ b/engines/wintermute/base/base.h @@ -0,0 +1,62 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_BASE_H +#define WINTERMUTE_BASE_BASE_H + +#include "engines/wintermute/wintypes.h" +#include "engines/wintermute/dctypes.h" +#include "common/str.h" +#include "common/hashmap.h" +#include "common/hash-str.h" + +namespace Wintermute { + +class BaseGame; +class BaseDynamicBuffer; + +class BaseClass { +public: + bool _persistable; + bool setEditorProp(const Common::String &propName, const Common::String &propValue); + Common::String getEditorProp(const Common::String &propName, const Common::String &initVal = NULL); + BaseClass(TDynamicConstructor, TDynamicConstructor) {} + bool parseEditorProperty(byte *buffer, bool complete = true); + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent = 0); + BaseClass(); + BaseClass(BaseGame *GameOwner); + virtual ~BaseClass(); + BaseGame *_gameRef; +protected: + Common::HashMap<Common::String, Common::String> _editorProps; + Common::HashMap<Common::String, Common::String>::iterator _editorPropsIter; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_active_rect.cpp b/engines/wintermute/base/base_active_rect.cpp new file mode 100644 index 0000000000..4addf15be8 --- /dev/null +++ b/engines/wintermute/base/base_active_rect.cpp @@ -0,0 +1,111 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_active_rect.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_region.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/platform_osystem.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////// +BaseActiveRect::BaseActiveRect(BaseGame *inGame) : BaseClass(inGame) { + BasePlatform::setRectEmpty(&_rect); + _owner = NULL; + _frame = NULL; + _region = NULL; + _zoomX = 100; + _zoomY = 100; + _offsetX = _offsetY = 0; + clipRect(); +} + + +////////////////////////////////////////////////////////////////////// +BaseActiveRect::BaseActiveRect(BaseGame *inGame, BaseObject *owner, BaseSubFrame *frame, int x, int y, int width, int height, float zoomX, float zoomY, bool precise) : BaseClass(inGame) { + _owner = owner; + _frame = frame; + BasePlatform::setRect(&_rect, x, y, x + width, y + height); + _zoomX = zoomX; + _zoomY = zoomY; + _precise = precise; + _region = NULL; + _offsetX = _offsetY = 0; + clipRect(); +} + +////////////////////////////////////////////////////////////////////// +BaseActiveRect::BaseActiveRect(BaseGame *inGame, BaseObject *owner, BaseRegion *region, int offsetX, int offsetY) : BaseClass(inGame) { + _owner = owner; + _region = region; + BasePlatform::copyRect(&_rect, ®ion->_rect); + _rect.offsetRect(-offsetX, -offsetY); + _zoomX = 100; + _zoomY = 100; + _precise = true; + _frame = NULL; + clipRect(); + _offsetX = offsetX; + _offsetY = offsetY; +} + + +////////////////////////////////////////////////////////////////////// +BaseActiveRect::~BaseActiveRect() { + _owner = NULL; + _frame = NULL; + _region = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +void BaseActiveRect::clipRect() { + Rect32 rc; + bool customViewport; + _gameRef->getCurrentViewportRect(&rc, &customViewport); + BaseRenderer *Rend = _gameRef->_renderer; + + if (!customViewport) { + rc.left -= Rend->_drawOffsetX; + rc.right -= Rend->_drawOffsetX; + rc.top -= Rend->_drawOffsetY; + rc.bottom -= Rend->_drawOffsetY; + } + + if (rc.left > _rect.left) { + _offsetX = rc.left - _rect.left; + } + if (rc.top > _rect.top) { + _offsetY = rc.top - _rect.top; + } + + BasePlatform::intersectRect(&_rect, &_rect, &rc); +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_active_rect.h b/engines/wintermute/base/base_active_rect.h new file mode 100644 index 0000000000..fcd2619b03 --- /dev/null +++ b/engines/wintermute/base/base_active_rect.h @@ -0,0 +1,60 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_ACTIVE_RECT_H +#define WINTERMUTE_BASE_ACTIVE_RECT_H + +#include "engines/wintermute/math/rect32.h" +#include "engines/wintermute/base/base.h" + +namespace Wintermute { +class BaseRegion; +class BaseSubFrame; +class BaseObject; +class BaseActiveRect: BaseClass { +public: + void clipRect(); + bool _precise; + float _zoomX; + float _zoomY; + BaseSubFrame *_frame; + BaseObject *_owner; + BaseRegion *_region; + int _offsetX; + int _offsetY; + Rect32 _rect; + BaseActiveRect(BaseGame *inGameOwner = NULL); + BaseActiveRect(BaseGame *inGameOwner, BaseObject *owner, BaseSubFrame *frame, int x, int y, int width, int height, float zoomX = 100, float zoomY = 100, bool precise = true); + BaseActiveRect(BaseGame *inGame, BaseObject *owner, BaseRegion *region, int offsetX, int offsetY); + virtual ~BaseActiveRect(); + +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_dynamic_buffer.cpp b/engines/wintermute/base/base_dynamic_buffer.cpp new file mode 100644 index 0000000000..dc14a4e052 --- /dev/null +++ b/engines/wintermute/base/base_dynamic_buffer.cpp @@ -0,0 +1,204 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_engine.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////////// +BaseDynamicBuffer::BaseDynamicBuffer(BaseGame *inGame, uint32 initSize, uint32 growBy) { + _buffer = NULL; + _size = 0; + _realSize = 0; + + _offset = 0; + _initSize = initSize; + _growBy = growBy; + + _initialized = false; +} + + +////////////////////////////////////////////////////////////////////////// +BaseDynamicBuffer::~BaseDynamicBuffer() { + cleanup(); +} + + +////////////////////////////////////////////////////////////////////////// +void BaseDynamicBuffer::cleanup() { + if (_buffer) { + free(_buffer); + } + _buffer = NULL; + _size = 0; + _realSize = 0; + _offset = 0; + _initialized = false; +} + + +////////////////////////////////////////////////////////////////////////// +uint32 BaseDynamicBuffer::getSize() const { + return _size; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseDynamicBuffer::init(uint32 initSize) { + cleanup(); + + if (initSize == 0) { + initSize = _initSize; + } + + _buffer = (byte *)malloc(initSize); + if (!_buffer) { + BaseEngine::LOG(0, "BaseDynamicBuffer::Init - Error allocating %d bytes", initSize); + return STATUS_FAILED; + } + + _realSize = initSize; + _initialized = true; + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseDynamicBuffer::putBytes(const byte *buffer, uint32 size) { + if (!_initialized) { + init(); + } + + while (_offset + size > _realSize) { + _realSize += _growBy; + _buffer = (byte *)realloc(_buffer, _realSize); + if (!_buffer) { + BaseEngine::LOG(0, "BaseDynamicBuffer::PutBytes - Error reallocating buffer to %d bytes", _realSize); + return STATUS_FAILED; + } + } + + memcpy(_buffer + _offset, buffer, size); + _offset += size; + _size += size; + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseDynamicBuffer::getBytes(byte *buffer, uint32 size) { + if (!_initialized) { + init(); + } + + if (_offset + size > _size) { + BaseEngine::LOG(0, "BaseDynamicBuffer::GetBytes - Buffer underflow"); + return STATUS_FAILED; + } + + memcpy(buffer, _buffer + _offset, size); + _offset += size; + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +void BaseDynamicBuffer::putDWORD(uint32 val) { + putBytes((byte *)&val, sizeof(uint32)); +} + + +////////////////////////////////////////////////////////////////////////// +uint32 BaseDynamicBuffer::getDWORD() { + uint32 ret; + getBytes((byte *)&ret, sizeof(uint32)); + return ret; +} + + +////////////////////////////////////////////////////////////////////////// +void BaseDynamicBuffer::putString(const char *val) { + if (!val) { + putString("(null)"); + } else { + putDWORD(strlen(val) + 1); + putBytes((const byte *)val, strlen(val) + 1); + } +} + + +////////////////////////////////////////////////////////////////////////// +char *BaseDynamicBuffer::getString() { + uint32 len = getDWORD(); + char *ret = (char *)(_buffer + _offset); + _offset += len; + + if (!strcmp(ret, "(null)")) { + return NULL; + } else { + return ret; + } +} + + +////////////////////////////////////////////////////////////////////////// +void BaseDynamicBuffer::putText(const char *fmt, ...) { + va_list va; + + va_start(va, fmt); + putTextForm(fmt, va); + va_end(va); + +} + + +////////////////////////////////////////////////////////////////////////// +void BaseDynamicBuffer::putTextIndent(int indent, const char *fmt, ...) { + va_list va; + + putText("%*s", indent, ""); + + va_start(va, fmt); + putTextForm(fmt, va); + va_end(va); +} + + +////////////////////////////////////////////////////////////////////////// +void BaseDynamicBuffer::putTextForm(const char *format, va_list argptr) { + char buff[32768]; + vsprintf(buff, format, argptr); + putBytes((byte *)buff, strlen(buff)); +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_dynamic_buffer.h b/engines/wintermute/base/base_dynamic_buffer.h new file mode 100644 index 0000000000..ad78ebad00 --- /dev/null +++ b/engines/wintermute/base/base_dynamic_buffer.h @@ -0,0 +1,65 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_DYNAMIC_BUFFER_H +#define WINTERMUTE_BASE_DYNAMIC_BUFFER_H + +#include "engines/wintermute/base/base.h" + +namespace Wintermute { + +class BaseDynamicBuffer { +public: + void putText(const char *fmt, ...); + void putTextIndent(int indent, const char *fmt, ...); + uint32 getDWORD(); + void putDWORD(uint32 val); + char *getString(); + void putString(const char *val); + bool getBytes(byte *buffer, uint32 size); + bool putBytes(const byte *buffer, uint32 size); + uint32 getSize() const; + bool init(uint32 initSize = 0); + void cleanup(); + BaseDynamicBuffer(BaseGame *inGame, uint32 initSize = 1000, uint32 growBy = 1000); + virtual ~BaseDynamicBuffer(); + +private: + uint32 _size; + byte *_buffer; + bool _initialized; + uint32 _realSize; + uint32 _growBy; + uint32 _initSize; + uint32 _offset; + void putTextForm(const char *format, va_list argptr); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_engine.cpp b/engines/wintermute/base/base_engine.cpp new file mode 100644 index 0000000000..8146d14beb --- /dev/null +++ b/engines/wintermute/base/base_engine.cpp @@ -0,0 +1,93 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_engine.h" +#include "engines/wintermute/wintermute.h" +#include "engines/wintermute/system/sys_class_registry.h" +#include "common/system.h" +namespace Common { +DECLARE_SINGLETON(Wintermute::BaseEngine); +} + +namespace Wintermute { + +BaseEngine::BaseEngine() { + _fileManager = NULL; + _gameRef = NULL; + _classReg = NULL; + _rnd = NULL; + _gameId = ""; +} + +void BaseEngine::init(Common::Language lang) { + _fileManager = new BaseFileManager(lang); + // Don't forget to register your random source + _rnd = new Common::RandomSource("Wintermute"); + _classReg = new SystemClassRegistry(); + _classReg->registerClasses(); +} + +BaseEngine::~BaseEngine() { + delete _fileManager; + delete _rnd; + delete _classReg; +} + +void BaseEngine::createInstance(const Common::String &gameid, Common::Language lang) { + instance()._gameId = gameid; + instance().init(lang); +} + +void BaseEngine::LOG(bool res, const char *fmt, ...) { + uint32 secs = g_system->getMillis() / 1000; + uint32 hours = secs / 3600; + secs = secs % 3600; + uint32 mins = secs / 60; + secs = secs % 60; + + char buff[512]; + va_list va; + + va_start(va, fmt); + vsprintf(buff, fmt, va); + va_end(va); + + if (instance()._gameRef) { + instance()._gameRef->LOG("%s", buff); + } else { + debugCN(kWintermuteDebugLog, "%02d:%02d:%02d: %s\n", hours, mins, secs, buff); + } +} + +uint32 BaseEngine::randInt(int from, int to) { + return _rnd->getRandomNumberRng(from, to); +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_engine.h b/engines/wintermute/base/base_engine.h new file mode 100644 index 0000000000..f04c594699 --- /dev/null +++ b/engines/wintermute/base/base_engine.h @@ -0,0 +1,69 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_ENGINE_H +#define WINTERMUTE_BASE_ENGINE_H + +#include "common/str.h" +#include "common/singleton.h" +#include "common/random.h" +#include "common/language.h" + +namespace Wintermute { + +class BaseFileManager; +class BaseRegistry; +class BaseGame; +class SystemClassRegistry; +class BaseEngine : public Common::Singleton<Wintermute::BaseEngine> { + void init(Common::Language lang); + BaseFileManager *_fileManager; + Common::String _gameId; + BaseGame *_gameRef; + // We need random numbers + Common::RandomSource *_rnd; + SystemClassRegistry *_classReg; +public: + BaseEngine(); + ~BaseEngine(); + static void createInstance(const Common::String &gameid, Common::Language lang); + void setGameRef(BaseGame *gameRef) { _gameRef = gameRef; } + + Common::RandomSource *getRandomSource() { return _rnd; } + uint32 randInt(int from, int to); + + SystemClassRegistry *getClassRegistry() { return _classReg; } + BaseGame *getGameRef() { return _gameRef; } + BaseFileManager *getFileManager() { return _fileManager; } + static void LOG(bool res, const char *fmt, ...); + const char *getGameId() { return _gameId.c_str(); } +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_fader.cpp b/engines/wintermute/base/base_fader.cpp new file mode 100644 index 0000000000..8f74d71caf --- /dev/null +++ b/engines/wintermute/base/base_fader.cpp @@ -0,0 +1,195 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_fader.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "common/util.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +IMPLEMENT_PERSISTENT(BaseFader, false) + +////////////////////////////////////////////////////////////////////////// +BaseFader::BaseFader(BaseGame *inGame) : BaseObject(inGame) { + _active = false; + _red = _green = _blue = 0; + _currentAlpha = 0x00; + _sourceAlpha = 0; + _targetAlpha = 0; + _duration = 1000; + _startTime = 0; + _system = false; +} + + +////////////////////////////////////////////////////////////////////////// +BaseFader::~BaseFader() { + +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseFader::update() { + if (!_active) { + return STATUS_OK; + } + + int alphaDelta = _targetAlpha - _sourceAlpha; + + uint32 time; + + if (_system) { + time = g_system->getMillis() - _startTime; + } else { + time = _gameRef->_timer - _startTime; + } + + if (time >= _duration) { + _currentAlpha = _targetAlpha; + } else { + _currentAlpha = (byte)(_sourceAlpha + (float)time / (float)_duration * alphaDelta); + } + _currentAlpha = MIN((unsigned char)255, MAX(_currentAlpha, (byte)0)); // TODO: clean + + _ready = time >= _duration; + if (_ready && _currentAlpha == 0x00) { + _active = false; + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseFader::display() { + if (!_active) { + return STATUS_OK; + } + + if (_currentAlpha > 0x00) { + _gameRef->_renderer->fadeToColor(_red, _green, _blue, _currentAlpha); + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseFader::deactivate() { + _active = false; + _ready = true; + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseFader::fadeIn(uint32 sourceColor, uint32 duration, bool system) { + _ready = false; + _active = true; + + _red = RGBCOLGetR(sourceColor); + _green = RGBCOLGetG(sourceColor); + _blue = RGBCOLGetB(sourceColor); + + _sourceAlpha = RGBCOLGetA(sourceColor); + _targetAlpha = 0; + + _duration = duration; + _system = system; + + if (_system) { + _startTime = g_system->getMillis(); + } else { + _startTime = _gameRef->_timer; + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseFader::fadeOut(uint32 targetColor, uint32 duration, bool system) { + _ready = false; + _active = true; + + _red = RGBCOLGetR(targetColor); + _green = RGBCOLGetG(targetColor); + _blue = RGBCOLGetB(targetColor); + + //_sourceAlpha = 0; + _sourceAlpha = _currentAlpha; + _targetAlpha = RGBCOLGetA(targetColor); + + _duration = duration; + _system = system; + + if (_system) { + _startTime = g_system->getMillis(); + } else { + _startTime = _gameRef->_timer; + } + + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +uint32 BaseFader::getCurrentColor() const { + return BYTETORGBA(_red, _green, _blue, _currentAlpha); +} + + + +////////////////////////////////////////////////////////////////////////// +bool BaseFader::persist(BasePersistenceManager *persistMgr) { + BaseObject::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_active)); + persistMgr->transfer(TMEMBER(_blue)); + persistMgr->transfer(TMEMBER(_currentAlpha)); + persistMgr->transfer(TMEMBER(_duration)); + persistMgr->transfer(TMEMBER(_green)); + persistMgr->transfer(TMEMBER(_red)); + persistMgr->transfer(TMEMBER(_sourceAlpha)); + persistMgr->transfer(TMEMBER(_startTime)); + persistMgr->transfer(TMEMBER(_targetAlpha)); + persistMgr->transfer(TMEMBER(_system)); + + if (_system && !persistMgr->getIsSaving()) { + _startTime = 0; + } + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_fader.h b/engines/wintermute/base/base_fader.h new file mode 100644 index 0000000000..845ce2f244 --- /dev/null +++ b/engines/wintermute/base/base_fader.h @@ -0,0 +1,63 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_FADER_H +#define WINTERMUTE_BASE_FADER_H + + +#include "engines/wintermute/base/base_object.h" + +namespace Wintermute { + +class BaseFader : public BaseObject { +public: + uint32 getCurrentColor() const; + bool fadeOut(uint32 targetColor, uint32 duration, bool system = false); + bool fadeIn(uint32 sourceColor, uint32 duration, bool system = false); + bool deactivate(); + bool display(); + bool update(); + DECLARE_PERSISTENT(BaseFader, BaseObject) + BaseFader(BaseGame *inGame); + virtual ~BaseFader(); +private: + bool _system; + bool _active; + byte _red; + byte _green; + byte _blue; + byte _currentAlpha; + byte _targetAlpha; + byte _sourceAlpha; + uint32 _duration; + uint32 _startTime; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_file_manager.cpp b/engines/wintermute/base/base_file_manager.cpp new file mode 100644 index 0000000000..b726c0c66f --- /dev/null +++ b/engines/wintermute/base/base_file_manager.cpp @@ -0,0 +1,340 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/base_persistence_manager.h" +#include "engines/wintermute/base/file/base_disk_file.h" +#include "engines/wintermute/base/file/base_save_thumb_file.h" +#include "engines/wintermute/base/file/base_package.h" +#include "engines/wintermute/base/file/base_resources.h" +#include "engines/wintermute/base/base_engine.h" +#include "engines/wintermute/wintermute.h" +#include "common/debug.h" +#include "common/str.h" +#include "common/tokenizer.h" +#include "common/textconsole.h" +#include "common/util.h" +#include "common/config-manager.h" +#include "common/system.h" +#include "common/fs.h" +#include "common/file.h" +#include "common/savefile.h" +#include "common/fs.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////// +BaseFileManager::BaseFileManager(Common::Language lang) { + _language = lang; + initPaths(); + registerPackages(); +} + +////////////////////////////////////////////////////////////////////// +BaseFileManager::~BaseFileManager() { + cleanup(); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseFileManager::cleanup() { + // delete registered paths + _packagePaths.clear(); + + // close open files + for (uint32 i = 0; i < _openFiles.size(); i++) { + delete _openFiles[i]; + } + _openFiles.clear(); + + // delete packages + _packages.clear(); + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////// +byte *BaseFileManager::readWholeFile(const Common::String &filename, uint32 *size, bool mustExist) { + byte *buffer = NULL; + + Common::SeekableReadStream *file = openFile(filename); + if (!file) { + if (mustExist) { + debugC(kWintermuteDebugFileAccess | kWintermuteDebugLog, "Error opening file '%s'", filename.c_str()); + } + return NULL; + } + + buffer = new byte[file->size() + 1]; + if (buffer == NULL) { + debugC(kWintermuteDebugFileAccess | kWintermuteDebugLog, "Error allocating buffer for file '%s' (%d bytes)", filename.c_str(), file->size() + 1); + closeFile(file); + return NULL; + } + + if (file->read(buffer, (uint32)file->size()) != (uint32)file->size()) { + debugC(kWintermuteDebugFileAccess | kWintermuteDebugLog, "Error reading file '%s'", filename.c_str()); + closeFile(file); + delete[] buffer; + return NULL; + }; + + buffer[file->size()] = '\0'; + if (size != NULL) { + *size = file->size(); + } + closeFile(file); + + return buffer; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseFileManager::addPath(TPathType type, const Common::FSNode &path) { + if (!path.exists()) { + return STATUS_FAILED; + } + + switch (type) { + case PATH_SINGLE: + // _singlePaths.push_back(path); + error("TODO: Allow adding single-paths"); + break; + case PATH_PACKAGE: + _packagePaths.push_back(path); + break; + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseFileManager::reloadPaths() { + // delete registered paths + //_singlePaths.clear(); + _packagePaths.clear(); + + return initPaths(); +} + +////////////////////////////////////////////////////////////////////////// +bool BaseFileManager::initPaths() { + // Removed: Config-based file-path choice. + + // package files paths + const Common::FSNode gameData(ConfMan.get("path")); + addPath(PATH_PACKAGE, gameData); + + Common::FSNode dataSubFolder = gameData.getChild("data"); + if (dataSubFolder.exists()) { + addPath(PATH_PACKAGE, dataSubFolder); + } + Common::FSNode languageSubFolder = gameData.getChild("language"); + if (languageSubFolder.exists()) { + addPath(PATH_PACKAGE, languageSubFolder); + } + return STATUS_OK; +} + +bool BaseFileManager::registerPackages(const Common::FSList &fslist) { + for (Common::FSList::const_iterator it = fslist.begin(); it != fslist.end(); ++it) { + debugC(kWintermuteDebugFileAccess, "Adding %s", (*it).getName().c_str()); + if ((*it).getName().contains(".dcp")) { + if (registerPackage((*it))) { + addPath(PATH_PACKAGE, (*it)); + } + } + } + return true; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseFileManager::registerPackages() { + debugC(kWintermuteDebugFileAccess | kWintermuteDebugLog, "Scanning packages"); + + // Register without using SearchMan, as otherwise the FSNode-based lookup in openPackage will fail + // and that has to be like that to support the detection-scheme. + Common::FSList files; + for (Common::FSList::iterator it = _packagePaths.begin(); it != _packagePaths.end(); ++it) { + debugC(kWintermuteDebugFileAccess, "Should register folder: %s %s", (*it).getPath().c_str(), (*it).getName().c_str()); + (*it).getChildren(files, Common::FSNode::kListFilesOnly); + for (Common::FSList::iterator fileIt = files.begin(); fileIt != files.end(); ++fileIt) { + if (!fileIt->getName().hasSuffix(".dcp")) { + continue; + } + // Avoid registering all the language files + // TODO: Select based on the gameDesc. + if (_language != Common::UNK_LANG && fileIt->getParent().getName() == "language") { + Common::String parentName = fileIt->getParent().getName(); + Common::String dcpName = fileIt->getName(); + if (_language == Common::EN_ANY && fileIt->getName() != "english.dcp") { + continue; + } else if (_language == Common::CZ_CZE && fileIt->getName() != "czech.dcp") { + continue; + } else if (_language == Common::IT_ITA && fileIt->getName() != "italian.dcp") { + continue; + } else if (_language == Common::PL_POL && fileIt->getName() != "polish.dcp") { + continue; + } else if (_language == Common::RU_RUS && fileIt->getName() != "russian.dcp") { + continue; + } + } + debugC(kWintermuteDebugFileAccess, "Registering %s %s", (*fileIt).getPath().c_str(), (*fileIt).getName().c_str()); + registerPackage((*fileIt)); + } + } + +// debugC(kWintermuteDebugFileAccess | kWintermuteDebugLog, " Registered %d files in %d package(s)", _files.size(), _packages.size()); + + return STATUS_OK; +} + +bool BaseFileManager::registerPackage(Common::FSNode file, const Common::String &filename, bool searchSignature) { + PackageSet *pack = new PackageSet(file, filename, searchSignature); + _packages.add(file.getName(), pack, pack->getPriority() , true); + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +Common::SeekableReadStream *BaseFileManager::openPkgFile(const Common::String &filename) { + Common::String upcName = filename; + upcName.toUppercase(); + Common::SeekableReadStream *file = NULL; + char fileName[MAX_PATH_LENGTH]; + strcpy(fileName, upcName.c_str()); + + // correct slashes + for (uint32 i = 0; i < upcName.size(); i++) { + if (upcName[(int32)i] == '/') { + upcName.setChar('\\', (uint32)i); + } + } + Common::ArchiveMemberPtr entry = _packages.getMember(upcName); + if (!entry) { + return NULL; + } + file = entry->createReadStream(); + return file; +} + +bool BaseFileManager::hasFile(const Common::String &filename) { + if (scumm_strnicmp(filename.c_str(), "savegame:", 9) == 0) { + BasePersistenceManager pm(BaseEngine::instance().getGameId()); + if (filename.size() <= 9) { + return false; + } + int slot = atoi(filename.c_str() + 9); + return pm.getSaveExists(slot); + } + if (diskFileExists(filename)) { + return true; + } + if (_packages.hasFile(filename)) { + return true; // We don't bother checking if the file can actually be opened, something bigger is wrong if that is the case. + } + if (BaseResources::hasFile(filename)) { + return true; + } + return false; +} + +////////////////////////////////////////////////////////////////////////// +Common::SeekableReadStream *BaseFileManager::openFile(const Common::String &filename, bool absPathWarning, bool keepTrackOf) { + if (strcmp(filename.c_str(), "") == 0) { + return NULL; + } + debugC(kWintermuteDebugFileAccess, "Open file %s", filename.c_str()); + + Common::SeekableReadStream *file = openFileRaw(filename); + if (file && keepTrackOf) { + _openFiles.push_back(file); + } + return file; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseFileManager::closeFile(Common::SeekableReadStream *File) { + for (uint32 i = 0; i < _openFiles.size(); i++) { + if (_openFiles[i] == File) { + delete _openFiles[i]; + _openFiles.remove_at(i); + return STATUS_OK; + } + } + return STATUS_FAILED; +} + + +////////////////////////////////////////////////////////////////////////// +Common::SeekableReadStream *BaseFileManager::openFileRaw(const Common::String &filename) { + Common::SeekableReadStream *ret = NULL; + + if (scumm_strnicmp(filename.c_str(), "savegame:", 9) == 0) { + if (!BaseEngine::instance().getGameRef()) { + error("Attempt to load filename: %s without BaseEngine-object, this is unsupported", filename.c_str()); + } + BaseSaveThumbFile *saveThumbFile = new BaseSaveThumbFile(); + if (DID_SUCCEED(saveThumbFile->open(filename))) { + ret = saveThumbFile->getMemStream(); + } + delete saveThumbFile; + return ret; + } + + ret = openDiskFile(filename); + if (ret) { + return ret; + } + + ret = openPkgFile(filename); + if (ret) { + return ret; + } + + ret = BaseResources::getFile(filename); + if (ret) { + return ret; + } + + debugC(kWintermuteDebugFileAccess ,"BFileManager::OpenFileRaw - Failed to open %s", filename.c_str()); + return NULL; +} + +BaseFileManager *BaseFileManager::getEngineInstance() { + if (BaseEngine::instance().getFileManager()) { + return BaseEngine::instance().getFileManager(); + } + return NULL; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_file_manager.h b/engines/wintermute/base/base_file_manager.h new file mode 100644 index 0000000000..70aff49bbb --- /dev/null +++ b/engines/wintermute/base/base_file_manager.h @@ -0,0 +1,76 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_FILE_MANAGER_H +#define WINTERMUTE_BASE_FILE_MANAGER_H + +#include "common/archive.h" +#include "common/str.h" +#include "common/fs.h" +#include "common/file.h" +#include "common/language.h" + +namespace Wintermute { +class BaseFileManager { +public: + bool cleanup(); + + bool closeFile(Common::SeekableReadStream *File); + bool hasFile(const Common::String &filename); + Common::SeekableReadStream *openFile(const Common::String &filename, bool absPathWarning = true, bool keepTrackOf = true); + byte *readWholeFile(const Common::String &filename, uint32 *size = NULL, bool mustExist = true); + + BaseFileManager(Common::Language lang); + virtual ~BaseFileManager(); + // Used only for detection + bool registerPackages(const Common::FSList &fslist); + static BaseFileManager *getEngineInstance(); +private: + typedef enum { + PATH_PACKAGE, + PATH_SINGLE + } TPathType; + bool reloadPaths(); + bool initPaths(); + bool addPath(TPathType type, const Common::FSNode &path); + bool registerPackages(); + Common::SeekableReadStream *openFileRaw(const Common::String &filename); + Common::SeekableReadStream *openPkgFile(const Common::String &filename); + Common::FSList _packagePaths; + bool findPackageSignature(Common::SeekableReadStream *f, uint32 *offset); + bool registerPackage(Common::FSNode package, const Common::String &filename = "", bool searchSignature = false); + Common::SearchSet _packages; + Common::Array<Common::SeekableReadStream *> _openFiles; + Common::Language _language; + // This class is intentionally not a subclass of Base, as it needs to be used by + // the detector too, without launching the entire engine: +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_frame.cpp b/engines/wintermute/base/base_frame.cpp new file mode 100644 index 0000000000..7c64144480 --- /dev/null +++ b/engines/wintermute/base/base_frame.cpp @@ -0,0 +1,766 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_frame.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/sound/base_sound_manager.h" +#include "engines/wintermute/base/sound/base_sound.h" +#include "engines/wintermute/base/base_sub_frame.h" +#include "engines/wintermute/platform_osystem.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "common/str.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(BaseFrame, false) + +////////////////////////////////////////////////////////////////////// +BaseFrame::BaseFrame(BaseGame *inGame) : BaseScriptable(inGame, true) { + _delay = 0; + _moveX = _moveY = 0; + + _sound = NULL; + _killSound = false; + + _editorExpanded = false; + _keyframe = false; +} + + +////////////////////////////////////////////////////////////////////// +BaseFrame::~BaseFrame() { + delete _sound; + _sound = NULL; + + for (uint32 i = 0; i < _subframes.size(); i++) { + delete _subframes[i]; + } + _subframes.clear(); + + for (uint32 i = 0; i < _applyEvent.size(); i++) { + delete[] _applyEvent[i]; + _applyEvent[i] = NULL; + } + _applyEvent.clear(); +} + + +////////////////////////////////////////////////////////////////////// +bool BaseFrame::draw(int x, int y, BaseObject *registerOwner, float zoomX, float zoomY, bool precise, uint32 alpha, bool allFrames, float rotate, TSpriteBlendMode blendMode) { + bool res; + + for (uint32 i = 0; i < _subframes.size(); i++) { + res = _subframes[i]->draw(x, y, registerOwner, zoomX, zoomY, precise, alpha, rotate, blendMode); + if (DID_FAIL(res)) { + return res; + } + } + return STATUS_OK; +} + +void BaseFrame::stopSound() { + if (_sound) { + _sound->stop(); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseFrame::oneTimeDisplay(BaseObject *owner, bool muted) { + if (_sound && !muted) { + if (owner) { + owner->updateOneSound(_sound); + } + _sound->play(); + /* + if (_gameRef->_state == GAME_FROZEN) { + _sound->Pause(true); + } + */ + } + if (owner) { + for (uint32 i = 0; i < _applyEvent.size(); i++) { + owner->applyEvent(_applyEvent[i]); + } + } + return STATUS_OK; +} + + + +TOKEN_DEF_START +TOKEN_DEF(DELAY) +TOKEN_DEF(IMAGE) +TOKEN_DEF(TRANSPARENT) +TOKEN_DEF(RECT) +TOKEN_DEF(HOTSPOT) +TOKEN_DEF(2D_ONLY) +TOKEN_DEF(3D_ONLY) +TOKEN_DEF(MIRROR_X) +TOKEN_DEF(MIRROR_Y) +TOKEN_DEF(MOVE) +TOKEN_DEF(ALPHA_COLOR) +TOKEN_DEF(ALPHA) +TOKEN_DEF(SUBFRAME) +TOKEN_DEF(SOUND) +TOKEN_DEF(KEYFRAME) +TOKEN_DEF(DECORATION) +TOKEN_DEF(APPLY_EVENT) +TOKEN_DEF(EDITOR_SELECTED) +TOKEN_DEF(EDITOR_EXPANDED) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF(KILL_SOUND) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////// +bool BaseFrame::loadBuffer(byte *buffer, int lifeTime, bool keepLoaded) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(DELAY) + TOKEN_TABLE(IMAGE) + TOKEN_TABLE(TRANSPARENT) + TOKEN_TABLE(RECT) + TOKEN_TABLE(HOTSPOT) + TOKEN_TABLE(2D_ONLY) + TOKEN_TABLE(3D_ONLY) + TOKEN_TABLE(MIRROR_X) + TOKEN_TABLE(MIRROR_Y) + TOKEN_TABLE(MOVE) + TOKEN_TABLE(ALPHA_COLOR) + TOKEN_TABLE(ALPHA) + TOKEN_TABLE(SUBFRAME) + TOKEN_TABLE(SOUND) + TOKEN_TABLE(KEYFRAME) + TOKEN_TABLE(DECORATION) + TOKEN_TABLE(APPLY_EVENT) + TOKEN_TABLE(EDITOR_SELECTED) + TOKEN_TABLE(EDITOR_EXPANDED) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE(KILL_SOUND) + TOKEN_TABLE_END + + char *params; + int cmd; + BaseParser parser; + Rect32 rect; + int r = 255, g = 255, b = 255; + int ar = 255, ag = 255, ab = 255, alpha = 255; + int hotspotX = 0, hotspotY = 0; + bool custoTrans = false; + bool editorSelected = false; + bool is2DOnly = false; + bool is3DOnly = false; + bool decoration = false; + bool mirrorX = false; + bool mirrorY = false; + BasePlatform::setRectEmpty(&rect); + char *surface_file = NULL; + + while ((cmd = parser.getCommand((char **)&buffer, commands, ¶ms)) > 0) { + switch (cmd) { + case TOKEN_DELAY: + parser.scanStr(params, "%d", &_delay); + break; + + case TOKEN_IMAGE: + surface_file = params; + break; + + case TOKEN_TRANSPARENT: + parser.scanStr(params, "%d,%d,%d", &r, &g, &b); + custoTrans = true; + break; + + case TOKEN_RECT: + parser.scanStr(params, "%d,%d,%d,%d", &rect.left, &rect.top, &rect.right, &rect.bottom); + break; + + case TOKEN_HOTSPOT: + parser.scanStr(params, "%d,%d", &hotspotX, &hotspotY); + break; + + case TOKEN_MOVE: + parser.scanStr(params, "%d,%d", &_moveX, &_moveY); + break; + + case TOKEN_2D_ONLY: + parser.scanStr(params, "%b", &is2DOnly); + break; + + case TOKEN_3D_ONLY: + parser.scanStr(params, "%b", &is3DOnly); + break; + + case TOKEN_MIRROR_X: + parser.scanStr(params, "%b", &mirrorX); + break; + + case TOKEN_MIRROR_Y: + parser.scanStr(params, "%b", &mirrorY); + break; + + case TOKEN_ALPHA_COLOR: + parser.scanStr(params, "%d,%d,%d", &ar, &ag, &ab); + break; + + case TOKEN_ALPHA: + parser.scanStr(params, "%d", &alpha); + break; + + case TOKEN_EDITOR_SELECTED: + parser.scanStr(params, "%b", &editorSelected); + break; + + case TOKEN_EDITOR_EXPANDED: + parser.scanStr(params, "%b", &_editorExpanded); + break; + + case TOKEN_KILL_SOUND: + parser.scanStr(params, "%b", &_killSound); + break; + + case TOKEN_SUBFRAME: { + BaseSubFrame *subframe = new BaseSubFrame(_gameRef); + if (!subframe || DID_FAIL(subframe->loadBuffer((byte *)params, lifeTime, keepLoaded))) { + delete subframe; + cmd = PARSERR_GENERIC; + } else { + _subframes.add(subframe); + } + } + break; + + case TOKEN_SOUND: { + if (_sound) { + delete _sound; + _sound = NULL; + } + _sound = new BaseSound(_gameRef); + if (!_sound || DID_FAIL(_sound->setSound(params, Audio::Mixer::kSFXSoundType, false))) { + if (_gameRef->_soundMgr->_soundAvailable) { + _gameRef->LOG(0, "Error loading sound '%s'.", params); + } + delete _sound; + _sound = NULL; + } + } + break; + + case TOKEN_APPLY_EVENT: { + char *event = new char[strlen(params) + 1]; + strcpy(event, params); + _applyEvent.add(event); + } + break; + + case TOKEN_KEYFRAME: + parser.scanStr(params, "%b", &_keyframe); + break; + + case TOKEN_DECORATION: + parser.scanStr(params, "%b", &decoration); + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty((byte *)params, false); + break; + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in FRAME definition"); + return STATUS_FAILED; + } + + if (cmd == PARSERR_GENERIC) { + _gameRef->LOG(0, "Error loading FRAME definition"); + return STATUS_FAILED; + } + + + BaseSubFrame *sub = new BaseSubFrame(_gameRef); + if (surface_file != NULL) { + if (custoTrans) { + sub->setSurface(surface_file, false, r, g, b, lifeTime, keepLoaded); + } else { + sub->setSurface(surface_file, true, 0, 0, 0, lifeTime, keepLoaded); + } + + if (!sub->_surface) { + delete sub; + _gameRef->LOG(0, "Error loading SUBFRAME"); + return STATUS_FAILED; + } + + sub->_alpha = BYTETORGBA(ar, ag, ab, alpha); + if (custoTrans) { + sub->_transparent = BYTETORGBA(r, g, b, 0xFF); + } + } + + if (BasePlatform::isRectEmpty(&rect)) { + sub->setDefaultRect(); + } else { + sub->setRect(rect); + } + + sub->_hotspotX = hotspotX; + sub->_hotspotY = hotspotY; + sub->_2DOnly = is2DOnly; + sub->_3DOnly = is3DOnly; + sub->_decoration = decoration; + sub->_mirrorX = mirrorX; + sub->_mirrorY = mirrorY; + + + sub->_editorSelected = editorSelected; + _subframes.insert_at(0, sub); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseFrame::getBoundingRect(Rect32 *rect, int x, int y, float scaleX, float scaleY) { + if (!rect) { + return false; + } + BasePlatform::setRectEmpty(rect); + + Rect32 subRect; + + for (uint32 i = 0; i < _subframes.size(); i++) { + _subframes[i]->getBoundingRect(&subRect, x, y, scaleX, scaleY); + BasePlatform::unionRect(rect, rect, &subRect); + } + return true; +} + + + +////////////////////////////////////////////////////////////////////////// +bool BaseFrame::saveAsText(BaseDynamicBuffer *buffer, int indent) { + buffer->putTextIndent(indent, "FRAME {\n"); + buffer->putTextIndent(indent + 2, "DELAY = %d\n", _delay); + + if (_moveX != 0 || _moveY != 0) { + buffer->putTextIndent(indent + 2, "MOVE {%d, %d}\n", _moveX, _moveY); + } + + if (_sound && _sound->getFilename()) { + buffer->putTextIndent(indent + 2, "SOUND=\"%s\"\n", _sound->getFilename()); + } + + buffer->putTextIndent(indent + 2, "KEYFRAME=%s\n", _keyframe ? "TRUE" : "FALSE"); + + if (_killSound) { + buffer->putTextIndent(indent + 2, "KILL_SOUND=%s\n", _killSound ? "TRUE" : "FALSE"); + } + + if (_editorExpanded) { + buffer->putTextIndent(indent + 2, "EDITOR_EXPANDED=%s\n", _editorExpanded ? "TRUE" : "FALSE"); + } + + if (_subframes.size() > 0) { + _subframes[0]->saveAsText(buffer, indent, false); + } + + for (uint32 i = 1; i < _subframes.size(); i++) { + _subframes[i]->saveAsText(buffer, indent + 2); + } + + for (uint32 i = 0; i < _applyEvent.size(); i++) { + buffer->putTextIndent(indent + 2, "APPLY_EVENT=\"%s\"\n", _applyEvent[i]); + } + + BaseClass::saveAsText(buffer, indent + 2); + + + buffer->putTextIndent(indent, "}\n\n"); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseFrame::persist(BasePersistenceManager *persistMgr) { + BaseScriptable::persist(persistMgr); + + _applyEvent.persist(persistMgr); + persistMgr->transfer(TMEMBER(_delay)); + persistMgr->transfer(TMEMBER(_editorExpanded)); + persistMgr->transfer(TMEMBER(_keyframe)); + persistMgr->transfer(TMEMBER(_killSound)); + persistMgr->transfer(TMEMBER(_moveX)); + persistMgr->transfer(TMEMBER(_moveY)); + persistMgr->transfer(TMEMBER(_sound)); + _subframes.persist(persistMgr); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool BaseFrame::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + + ////////////////////////////////////////////////////////////////////////// + // GetSound + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "GetSound") == 0) { + stack->correctParams(0); + + if (_sound && _sound->getFilename()) { + stack->pushString(_sound->getFilename()); + } else { + stack->pushNULL(); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetSound + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "SetSound") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + delete _sound; + _sound = NULL; + + if (!val->isNULL()) { + _sound = new BaseSound(_gameRef); + if (!_sound || DID_FAIL(_sound->setSound(val->getString(), Audio::Mixer::kSFXSoundType, false))) { + stack->pushBool(false); + delete _sound; + _sound = NULL; + } else { + stack->pushBool(true); + } + } else { + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetSubframe + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "GetSubframe") == 0) { + stack->correctParams(1); + int index = stack->pop()->getInt(-1); + if (index < 0 || index >= (int32)_subframes.size()) { + script->runtimeError("Frame.GetSubframe: Subframe index %d is out of range.", index); + stack->pushNULL(); + } else { + stack->pushNative(_subframes[index], true); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // DeleteSubframe + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "DeleteSubframe") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + if (val->isInt()) { + int index = val->getInt(-1); + if (index < 0 || index >= (int32)_subframes.size()) { + script->runtimeError("Frame.DeleteSubframe: Subframe index %d is out of range.", index); + } + } else { + BaseSubFrame *sub = (BaseSubFrame *)val->getNative(); + for (uint32 i = 0; i < _subframes.size(); i++) { + if (_subframes[i] == sub) { + delete _subframes[i]; + _subframes.remove_at(i); + break; + } + } + } + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // AddSubframe + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AddSubframe") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + const char *filename = NULL; + if (!val->isNULL()) { + filename = val->getString(); + } + + BaseSubFrame *sub = new BaseSubFrame(_gameRef); + if (filename != NULL) { + sub->setSurface(filename); + sub->setDefaultRect(); + } + _subframes.add(sub); + + stack->pushNative(sub, true); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // InsertSubframe + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "InsertSubframe") == 0) { + stack->correctParams(2); + int index = stack->pop()->getInt(); + if (index < 0) { + index = 0; + } + + ScValue *val = stack->pop(); + const char *filename = NULL; + if (!val->isNULL()) { + filename = val->getString(); + } + + BaseSubFrame *sub = new BaseSubFrame(_gameRef); + if (filename != NULL) { + sub->setSurface(filename); + } + + if (index >= (int32)_subframes.size()) { + _subframes.add(sub); + } else { + _subframes.insert_at(index, sub); + } + + stack->pushNative(sub, true); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetEvent + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetSubframe") == 0) { + stack->correctParams(1); + int index = stack->pop()->getInt(-1); + if (index < 0 || index >= (int32)_applyEvent.size()) { + script->runtimeError("Frame.GetEvent: Event index %d is out of range.", index); + stack->pushNULL(); + } else { + stack->pushString(_applyEvent[index]); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // AddEvent + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AddEvent") == 0) { + stack->correctParams(1); + const char *event = stack->pop()->getString(); + for (uint32 i = 0; i < _applyEvent.size(); i++) { + if (scumm_stricmp(_applyEvent[i], event) == 0) { + stack->pushNULL(); + return STATUS_OK; + } + } + _applyEvent.add(event); + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // DeleteEvent + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "DeleteEvent") == 0) { + stack->correctParams(1); + const char *event = stack->pop()->getString(); + for (uint32 i = 0; i < _applyEvent.size(); i++) { + if (scumm_stricmp(_applyEvent[i], event) == 0) { + delete[] _applyEvent[i]; + _applyEvent.remove_at(i); + break; + } + } + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + else { + if (_subframes.size() == 1) { + return _subframes[0]->scCallMethod(script, stack, thisStack, name); + } else { + return BaseScriptable::scCallMethod(script, stack, thisStack, name); + } + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *BaseFrame::scGetProperty(const Common::String &name) { + if (!_scValue) { + _scValue = new ScValue(_gameRef); + } + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type (RO) + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("frame"); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Delay + ////////////////////////////////////////////////////////////////////////// + else if (name == "Delay") { + _scValue->setInt(_delay); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Keyframe + ////////////////////////////////////////////////////////////////////////// + else if (name == "Keyframe") { + _scValue->setBool(_keyframe); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // KillSounds + ////////////////////////////////////////////////////////////////////////// + else if (name == "KillSounds") { + _scValue->setBool(_killSound); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // MoveX + ////////////////////////////////////////////////////////////////////////// + else if (name == "MoveX") { + _scValue->setInt(_moveX); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // MoveY + ////////////////////////////////////////////////////////////////////////// + else if (name == "MoveY") { + _scValue->setInt(_moveY); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // NumSubframes (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "NumSubframes") { + _scValue->setInt(_subframes.size()); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // NumEvents (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "NumEvents") { + _scValue->setInt(_applyEvent.size()); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + else { + if (_subframes.size() == 1) { + return _subframes[0]->scGetProperty(name); + } else { + return BaseScriptable::scGetProperty(name); + } + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseFrame::scSetProperty(const char *name, ScValue *value) { + ////////////////////////////////////////////////////////////////////////// + // Delay + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Delay") == 0) { + _delay = MAX(0, value->getInt()); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Keyframe + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Keyframe") == 0) { + _keyframe = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // KillSounds + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "KillSounds") == 0) { + _killSound = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // MoveX + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "MoveX") == 0) { + _moveX = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // MoveY + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "MoveY") == 0) { + _moveY = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + else { + if (_subframes.size() == 1) { + return _subframes[0]->scSetProperty(name, value); + } else { + return BaseScriptable::scSetProperty(name, value); + } + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *BaseFrame::scToString() { + return "[frame]"; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_frame.h b/engines/wintermute/base/base_frame.h new file mode 100644 index 0000000000..7c5d893e70 --- /dev/null +++ b/engines/wintermute/base/base_frame.h @@ -0,0 +1,75 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_FRAME_H +#define WINTERMUTE_BASE_FRAME_H + +#include "engines/wintermute/base/base_scriptable.h" +#include "engines/wintermute/coll_templ.h" + +namespace Wintermute { +class BaseSound; +class BaseSubFrame; +class BaseObject; +class ScScript; +class ScStack; +class BaseFrame: public BaseScriptable { +public: + bool _killSound; + void stopSound(); + bool oneTimeDisplay(BaseObject *owner, bool muted = false); + DECLARE_PERSISTENT(BaseFrame, BaseScriptable) + + bool getBoundingRect(Rect32 *rect, int x, int y, float scaleX = 100, float scaleY = 100); + bool saveAsText(BaseDynamicBuffer *buffer, int indent); + int _moveY; + int _moveX; + uint32 _delay; + BaseArray<BaseSubFrame *> _subframes; + bool draw(int x, int y, BaseObject *registerOwner = NULL, float zoomX = 100, float zoomY = 100, bool precise = true, uint32 alpha = 0xFFFFFFFF, bool allFrames = false, float rotate = 0.0f, TSpriteBlendMode blendMode = BLEND_NORMAL); + bool loadBuffer(byte *buffer, int lifeTime, bool keepLoaded); + + BaseFrame(BaseGame *inGame); + virtual ~BaseFrame(); + + BaseArray<const char *> _applyEvent; + + // scripting interface + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + virtual const char *scToString(); +private: + bool _keyframe; + bool _editorExpanded; + BaseSound *_sound; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_game.cpp b/engines/wintermute/base/base_game.cpp new file mode 100644 index 0000000000..46acd8cd98 --- /dev/null +++ b/engines/wintermute/base/base_game.cpp @@ -0,0 +1,4035 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/dcgf.h" +#include "engines/wintermute/base/base_engine.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_game_music.h" +#include "engines/wintermute/base/base_fader.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/font/base_font.h" +#include "engines/wintermute/base/font/base_font_storage.h" +#include "engines/wintermute/base/gfx/base_image.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/base/base_keyboard_state.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_quick_msg.h" +#include "engines/wintermute/base/sound/base_sound_manager.h" +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/base/base_sub_frame.h" +#include "engines/wintermute/base/base_transition_manager.h" +#include "engines/wintermute/base/base_viewport.h" +#include "engines/wintermute/base/base_string_table.h" +#include "engines/wintermute/base/base_region.h" +#include "engines/wintermute/base/base_save_thumb_helper.h" +#include "engines/wintermute/base/base_surface_storage.h" +#include "engines/wintermute/base/saveload.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script_engine.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/base/sound/base_sound.h" +#include "engines/wintermute/video/video_player.h" +#include "engines/wintermute/video/video_theora_player.h" +#include "engines/wintermute/utils/utils.h" +#include "engines/wintermute/utils/crc.h" +#include "engines/wintermute/utils/path_util.h" +#include "engines/wintermute/utils/string_util.h" +#include "engines/wintermute/ui/ui_window.h" +#include "engines/wintermute/wintermute.h" +#include "engines/wintermute/platform_osystem.h" +#include "base/version.h" +#include "common/config-manager.h" +#include "common/savefile.h" +#include "common/textconsole.h" +#include "common/util.h" +#include "common/keyboard.h" +#include "common/system.h" +#include "common/file.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +IMPLEMENT_PERSISTENT(BaseGame, true) + + +////////////////////////////////////////////////////////////////////// +BaseGame::BaseGame(const Common::String &gameId) : BaseObject(this), _gameId(gameId) { + _shuttingDown = false; + + _state = GAME_RUNNING; + _origState = GAME_RUNNING; + _freezeLevel = 0; + + _interactive = true; + _origInteractive = false; + + _surfaceStorage = NULL; + _fontStorage = NULL; + _renderer = NULL; + _soundMgr = NULL; + _transMgr = NULL; + _scEngine = NULL; + _keyboardState = NULL; + + _mathClass = NULL; + + _debugLogFile = NULL; + _debugDebugMode = false; + _debugShowFPS = false; + + _systemFont = NULL; + _videoFont = NULL; + + _videoPlayer = NULL; + _theoraPlayer = NULL; + + _mainObject = NULL; + _activeObject = NULL; + + _fader = NULL; + + _offsetX = _offsetY = 0; + _offsetPercentX = _offsetPercentY = 0.0f; + + _subtitles = true; + _videoSubtitles = true; + + _timer = 0; + _timerDelta = 0; + _timerLast = 0; + + _liveTimer = 0; + _liveTimerDelta = 0; + _liveTimerLast = 0; + + _sequence = 0; + + _mousePos.x = _mousePos.y = 0; + _mouseLeftDown = _mouseRightDown = _mouseMidlleDown = false; + _capturedObject = NULL; + + // FPS counters + _lastTime = _fpsTime = _deltaTime = _framesRendered = _fps = 0; + + _cursorNoninteractive = NULL; + + _useD3D = false; + + _stringTable = new BaseStringTable(this); + + _musicSystem = new BaseGameMusic(this); + + _settingsResWidth = 800; + _settingsResHeight = 600; + _settingsRequireAcceleration = false; + _settingsRequireSound = false; + _settingsTLMode = 0; + _settingsAllowWindowed = true; + _settingsGameFile = NULL; + _settingsAllowAdvanced = false; + _settingsAllowAccessTab = true; + _settingsAllowAboutTab = true; + _settingsAllowDesktopRes = false; + + _editorForceScripts = false; + _editorAlwaysRegister = false; + + _focusedWindow = NULL; + + _loadInProgress = false; + + _quitting = false; + _loading = false; + _scheduledLoadSlot = -1; + + _personalizedSave = false; + _compressedSavegames = true; + + _editorMode = false; + //_doNotExpandStrings = false; + + _engineLogCallback = NULL; + _engineLogCallbackData = NULL; + + _smartCache = false; + _surfaceGCCycleTime = 10000; + + _reportTextureFormat = false; + + _viewportSP = -1; + + _subtitlesSpeed = 70; + + _forceNonStreamedSounds = false; + + _thumbnailWidth = _thumbnailHeight = 0; + + _richSavedGames = false; + _savedGameExt = "dsv"; + _localSaveDir = "saves"; + + _saveDirChecked = false; + + _loadingIcon = NULL; + _loadingIconX = _loadingIconY = 0; + _loadingIconPersistent = false; + + _textEncoding = TEXT_ANSI; + _textRTL = false; + + _soundBufferSizeSec = 3; + _suspendedRendering = false; + + _lastCursor = NULL; + + BasePlatform::setRectEmpty(&_mouseLockRect); + + _suppressScriptErrors = false; + _lastMiniUpdate = 0; + _miniUpdateEnabled = false; + + _cachedThumbnail = NULL; + + _autorunDisabled = false; + + // compatibility bits + _compatKillMethodThreads = false; + + _usedMem = 0; + + + _autoSaveOnExit = true; + _autoSaveSlot = 999; + _cursorHidden = false; + + // Block kept as a reminder that the engine CAN run in constrained/touch-mode + /*#ifdef __IPHONEOS__ + _touchInterface = true; + _constrainedMemory = true; // TODO differentiate old and new iOS devices + #else*/ + _touchInterface = false; + _constrainedMemory = false; +//#endif + +} + + +////////////////////////////////////////////////////////////////////// +BaseGame::~BaseGame() { + _shuttingDown = true; + + LOG(0, ""); + LOG(0, "Shutting down..."); + + ConfMan.setBool("last_run", true); + + cleanup(); + + delete[] _settingsGameFile; + + delete _cachedThumbnail; + + delete _mathClass; + + delete _transMgr; + delete _scEngine; + delete _fontStorage; + delete _surfaceStorage; + delete _videoPlayer; + delete _theoraPlayer; + delete _soundMgr; + //SAFE_DELETE(_keyboardState); + + delete _renderer; + delete _stringTable; + delete _musicSystem; + + _settingsGameFile = NULL; + + _cachedThumbnail = NULL; + + _mathClass = NULL; + + _transMgr = NULL; + _scEngine = NULL; + _fontStorage = NULL; + _surfaceStorage = NULL; + _videoPlayer = NULL; + _theoraPlayer = NULL; + _soundMgr = NULL; + + _renderer = NULL; + _stringTable = NULL; + _musicSystem = NULL; + + DEBUG_DebugDisable(); + debugC(kWintermuteDebugLog, "--- shutting down normally ---\n"); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::cleanup() { + delete _loadingIcon; + _loadingIcon = NULL; + + _engineLogCallback = NULL; + _engineLogCallbackData = NULL; + + _musicSystem->cleanup(); + + unregisterObject(_fader); + _fader = NULL; + + for (uint32 i = 0; i < _regObjects.size(); i++) { + delete _regObjects[i]; + _regObjects[i] = NULL; + } + _regObjects.clear(); + + _windows.clear(); // refs only + _focusedWindow = NULL; // ref only + + delete _cursorNoninteractive; + delete _cursor; + delete _activeCursor; + _cursorNoninteractive = NULL; + _cursor = NULL; + _activeCursor = NULL; + + delete _scValue; + delete _sFX; + _scValue = NULL; + _sFX = NULL; + + for (uint32 i = 0; i < _scripts.size(); i++) { + _scripts[i]->_owner = NULL; + _scripts[i]->finish(); + } + _scripts.clear(); + + _fontStorage->removeFont(_systemFont); + _systemFont = NULL; + + _fontStorage->removeFont(_videoFont); + _videoFont = NULL; + + for (uint32 i = 0; i < _quickMessages.size(); i++) { + delete _quickMessages[i]; + } + _quickMessages.clear(); + + _viewportStack.clear(); + _viewportSP = -1; + + setName(NULL); + setFilename(NULL); + for (int i = 0; i < 7; i++) { + delete[] _caption[i]; + _caption[i] = NULL; + } + + _lastCursor = NULL; + + delete _keyboardState; + _keyboardState = NULL; + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////// +bool BaseGame::initialize1() { + bool loaded = false; // Not really a loop, but a goto-replacement. + while (!loaded) { + _surfaceStorage = new BaseSurfaceStorage(this); + if (_surfaceStorage == NULL) { + break; + } + + _fontStorage = new BaseFontStorage(this); + if (_fontStorage == NULL) { + break; + } + + _soundMgr = new BaseSoundMgr(this); + if (_soundMgr == NULL) { + break; + } + + _mathClass = makeSXMath(this); + if (_mathClass == NULL) { + break; + } + + _scEngine = new ScEngine(this); + if (_scEngine == NULL) { + break; + } + + _videoPlayer = new VideoPlayer(this); + if (_videoPlayer == NULL) { + break; + } + + _transMgr = new BaseTransitionMgr(this); + if (_transMgr == NULL) { + break; + } + + _keyboardState = new BaseKeyboardState(this); + if (_keyboardState == NULL) { + break; + } + + _fader = new BaseFader(this); + if (_fader == NULL) { + break; + } + registerObject(_fader); + + loaded = true; + } + if (loaded == true) { + return STATUS_OK; + } else { + delete _mathClass; + delete _keyboardState; + delete _transMgr; + delete _surfaceStorage; + delete _fontStorage; + delete _soundMgr; + delete _scEngine; + delete _videoPlayer; + return STATUS_FAILED; + } +} + + +////////////////////////////////////////////////////////////////////// +bool BaseGame::initialize2() { // we know whether we are going to be accelerated + _renderer = makeOSystemRenderer(this); + if (_renderer == NULL) { + return STATUS_FAILED; + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////// +bool BaseGame::initialize3() { // renderer is initialized + _posX = _renderer->_width / 2; + _posY = _renderer->_height / 2; + _renderer->initIndicator(); + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////// +void BaseGame::DEBUG_DebugEnable(const char *filename) { + _debugDebugMode = true; + + int secs = g_system->getMillis() / 1000; + int hours = secs / 3600; + secs = secs % 3600; + int mins = secs / 60; + secs = secs % 60; + +#ifdef _DEBUG + LOG(0, "********** DEBUG LOG OPENED %02d-%02d-%02d (Debug Build) *******************", hours, mins, secs); +#else + LOG(0, "********** DEBUG LOG OPENED %02d-%02d-%02d (Release Build) *****************", hours, mins, secs); +#endif + + LOG(0, "%s - %s ver %d.%d.%d%s ", gScummVMFullVersion, DCGF_NAME, DCGF_VER_MAJOR, DCGF_VER_MINOR, DCGF_VER_BUILD, DCGF_VER_SUFFIX); + + AnsiString platform = BasePlatform::getPlatformName(); + LOG(0, "Platform: %s", platform.c_str()); + LOG(0, ""); +} + + +////////////////////////////////////////////////////////////////////// +void BaseGame::DEBUG_DebugDisable() { + if (_debugLogFile != NULL) { + LOG(0, "********** DEBUG LOG CLOSED ********************************************"); + //fclose((FILE *)_debugLogFile); + _debugLogFile = NULL; + } + _debugDebugMode = false; +} + + +////////////////////////////////////////////////////////////////////// +void BaseGame::LOG(bool res, const char *fmt, ...) { + uint32 secs = g_system->getMillis() / 1000; + uint32 hours = secs / 3600; + secs = secs % 3600; + uint32 mins = secs / 60; + secs = secs % 60; + + char buff[512]; + va_list va; + + va_start(va, fmt); + vsprintf(buff, fmt, va); + va_end(va); + + // redirect to an engine's own callback + if (_engineLogCallback) { + _engineLogCallback(buff, res, _engineLogCallbackData); + } + + debugCN(kWintermuteDebugLog, "%02d:%02d:%02d: %s\n", hours, mins, secs, buff); + + //fprintf((FILE *)_debugLogFile, "%02d:%02d:%02d: %s\n", hours, mins, secs, buff); + //fflush((FILE *)_debugLogFile); + + //QuickMessage(buff); +} + + +////////////////////////////////////////////////////////////////////////// +void BaseGame::setEngineLogCallback(ENGINE_LOG_CALLBACK callback, void *data) { + _engineLogCallback = callback; + _engineLogCallbackData = data; +} + + +////////////////////////////////////////////////////////////////////// +bool BaseGame::initLoop() { + _viewportSP = -1; + + _currentTime = g_system->getMillis(); + + _renderer->initLoop(); + _musicSystem->updateMusicCrossfade(); + + _surfaceStorage->initLoop(); + _fontStorage->initLoop(); + + + //_activeObject = NULL; + + // count FPS + _deltaTime = _currentTime - _lastTime; + _lastTime = _currentTime; + _fpsTime += _deltaTime; + + _liveTimerDelta = _liveTimer - _liveTimerLast; + _liveTimerLast = _liveTimer; + _liveTimer += MIN((uint32)1000, _deltaTime); + + if (_state != GAME_FROZEN) { + _timerDelta = _timer - _timerLast; + _timerLast = _timer; + _timer += MIN((uint32)1000, _deltaTime); + } else { + _timerDelta = 0; + } + + _framesRendered++; + if (_fpsTime > 1000) { + _fps = _framesRendered; + _framesRendered = 0; + _fpsTime = 0; + } + //_gameRef->LOG(0, "%d", _fps); + + getMousePos(&_mousePos); + + _focusedWindow = NULL; + for (int i = _windows.size() - 1; i >= 0; i--) { + if (_windows[i]->_visible) { + _focusedWindow = _windows[i]; + break; + } + } + + updateSounds(); + + if (_fader) { + _fader->update(); + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////// +bool BaseGame::initInput() { + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +int BaseGame::getSequence() { + return ++_sequence; +} + + +////////////////////////////////////////////////////////////////////////// +void BaseGame::setOffset(int offsetX, int offsetY) { + _offsetX = offsetX; + _offsetY = offsetY; +} + +////////////////////////////////////////////////////////////////////////// +void BaseGame::getOffset(int *offsetX, int *offsetY) { + if (offsetX != NULL) { + *offsetX = _offsetX; + } + if (offsetY != NULL) { + *offsetY = _offsetY; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::loadFile(const char *filename) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "BaseGame::LoadFile failed for file '%s'", filename); + return STATUS_FAILED; + } + + bool ret; + + setFilename(filename); + + if (DID_FAIL(ret = loadBuffer(buffer, true))) { + _gameRef->LOG(0, "Error parsing GAME file '%s'", filename); + } + + delete[] buffer; + + return ret; +} + + +TOKEN_DEF_START +TOKEN_DEF(GAME) +TOKEN_DEF(TEMPLATE) +TOKEN_DEF(NAME) +TOKEN_DEF(SYSTEM_FONT) +TOKEN_DEF(VIDEO_FONT) +TOKEN_DEF(EVENTS) +TOKEN_DEF(CURSOR) +TOKEN_DEF(ACTIVE_CURSOR) +TOKEN_DEF(NONINTERACTIVE_CURSOR) +TOKEN_DEF(STRING_TABLE) +TOKEN_DEF(RESOLUTION) +TOKEN_DEF(SETTINGS) +TOKEN_DEF(REQUIRE_3D_ACCELERATION) +TOKEN_DEF(REQUIRE_SOUND) +TOKEN_DEF(HWTL_MODE) +TOKEN_DEF(ALLOW_WINDOWED_MODE) +TOKEN_DEF(ALLOW_ACCESSIBILITY_TAB) +TOKEN_DEF(ALLOW_ABOUT_TAB) +TOKEN_DEF(ALLOW_ADVANCED) +TOKEN_DEF(ALLOW_DESKTOP_RES) +TOKEN_DEF(REGISTRY_PATH) +TOKEN_DEF(PERSONAL_SAVEGAMES) +TOKEN_DEF(SCRIPT) +TOKEN_DEF(CAPTION) +TOKEN_DEF(PROPERTY) +TOKEN_DEF(SUBTITLES_SPEED) +TOKEN_DEF(SUBTITLES) +TOKEN_DEF(VIDEO_SUBTITLES) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF(THUMBNAIL_WIDTH) +TOKEN_DEF(THUMBNAIL_HEIGHT) +TOKEN_DEF(INDICATOR_X) +TOKEN_DEF(INDICATOR_Y) +TOKEN_DEF(INDICATOR_WIDTH) +TOKEN_DEF(INDICATOR_HEIGHT) +TOKEN_DEF(INDICATOR_COLOR) +TOKEN_DEF(SAVE_IMAGE_X) +TOKEN_DEF(SAVE_IMAGE_Y) +TOKEN_DEF(SAVE_IMAGE) +TOKEN_DEF(LOAD_IMAGE_X) +TOKEN_DEF(LOAD_IMAGE_Y) +TOKEN_DEF(LOAD_IMAGE) +TOKEN_DEF(LOCAL_SAVE_DIR) +TOKEN_DEF(RICH_SAVED_GAMES) +TOKEN_DEF(SAVED_GAME_EXT) +TOKEN_DEF(GUID) +TOKEN_DEF(COMPAT_KILL_METHOD_THREADS) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool BaseGame::loadBuffer(byte *buffer, bool complete) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(GAME) + TOKEN_TABLE(TEMPLATE) + TOKEN_TABLE(NAME) + TOKEN_TABLE(SYSTEM_FONT) + TOKEN_TABLE(VIDEO_FONT) + TOKEN_TABLE(EVENTS) + TOKEN_TABLE(CURSOR) + TOKEN_TABLE(ACTIVE_CURSOR) + TOKEN_TABLE(NONINTERACTIVE_CURSOR) + TOKEN_TABLE(PERSONAL_SAVEGAMES) + TOKEN_TABLE(SCRIPT) + TOKEN_TABLE(CAPTION) + TOKEN_TABLE(PROPERTY) + TOKEN_TABLE(SUBTITLES_SPEED) + TOKEN_TABLE(SUBTITLES) + TOKEN_TABLE(VIDEO_SUBTITLES) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE(THUMBNAIL_WIDTH) + TOKEN_TABLE(THUMBNAIL_HEIGHT) + TOKEN_TABLE(INDICATOR_X) + TOKEN_TABLE(INDICATOR_Y) + TOKEN_TABLE(INDICATOR_WIDTH) + TOKEN_TABLE(INDICATOR_HEIGHT) + TOKEN_TABLE(INDICATOR_COLOR) + TOKEN_TABLE(SAVE_IMAGE_X) + TOKEN_TABLE(SAVE_IMAGE_Y) + TOKEN_TABLE(SAVE_IMAGE) + TOKEN_TABLE(LOAD_IMAGE_X) + TOKEN_TABLE(LOAD_IMAGE_Y) + TOKEN_TABLE(LOAD_IMAGE) + TOKEN_TABLE(LOCAL_SAVE_DIR) + TOKEN_TABLE(COMPAT_KILL_METHOD_THREADS) + TOKEN_TABLE_END + + // Declare a few variables necessary for moving data from these settings over to the renderer: + // The values are the same as the defaults set in BaseRenderer. + int loadImageX = 0; + int loadImageY = 0; + int saveImageX = 0; + int saveImageY = 0; + int indicatorX = -1; + int indicatorY = -1; + int indicatorWidth = -1; + int indicatorHeight = 8; + uint32 indicatorColor = BYTETORGBA(255, 0, 0, 128); + Common::String loadImageName = ""; + Common::String saveImageName = ""; + + byte *params; + int cmd; + BaseParser parser; + + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_GAME) { + _gameRef->LOG(0, "'GAME' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_TEMPLATE: + if (DID_FAIL(loadFile((char *)params))) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_NAME: + setName((char *)params); + break; + + case TOKEN_CAPTION: + setCaption((char *)params); + break; + + case TOKEN_SYSTEM_FONT: + if (_systemFont) { + _fontStorage->removeFont(_systemFont); + } + _systemFont = NULL; + + _systemFont = _gameRef->_fontStorage->addFont((char *)params); + break; + + case TOKEN_VIDEO_FONT: + if (_videoFont) { + _fontStorage->removeFont(_videoFont); + } + _videoFont = NULL; + + _videoFont = _gameRef->_fontStorage->addFont((char *)params); + break; + + + case TOKEN_CURSOR: + delete _cursor; + _cursor = new BaseSprite(_gameRef); + if (!_cursor || DID_FAIL(_cursor->loadFile((char *)params))) { + delete _cursor; + _cursor = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_ACTIVE_CURSOR: + delete _activeCursor; + _activeCursor = NULL; + _activeCursor = new BaseSprite(_gameRef); + if (!_activeCursor || DID_FAIL(_activeCursor->loadFile((char *)params))) { + delete _activeCursor; + _activeCursor = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_NONINTERACTIVE_CURSOR: + delete _cursorNoninteractive; + _cursorNoninteractive = new BaseSprite(_gameRef); + if (!_cursorNoninteractive || DID_FAIL(_cursorNoninteractive->loadFile((char *)params))) { + delete _cursorNoninteractive; + _cursorNoninteractive = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_SCRIPT: + addScript((char *)params); + break; + + case TOKEN_PERSONAL_SAVEGAMES: + parser.scanStr((char *)params, "%b", &_personalizedSave); + break; + + case TOKEN_SUBTITLES: + parser.scanStr((char *)params, "%b", &_subtitles); + break; + + case TOKEN_SUBTITLES_SPEED: + parser.scanStr((char *)params, "%d", &_subtitlesSpeed); + break; + + case TOKEN_VIDEO_SUBTITLES: + parser.scanStr((char *)params, "%b", &_videoSubtitles); + break; + + case TOKEN_PROPERTY: + parseProperty(params, false); + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty(params, false); + break; + + case TOKEN_THUMBNAIL_WIDTH: + parser.scanStr((char *)params, "%d", &_thumbnailWidth); + break; + + case TOKEN_THUMBNAIL_HEIGHT: + parser.scanStr((char *)params, "%d", &_thumbnailHeight); + break; + + case TOKEN_INDICATOR_X: + parser.scanStr((char *)params, "%d", &indicatorX); + break; + + case TOKEN_INDICATOR_Y: + parser.scanStr((char *)params, "%d", &indicatorY); + break; + + case TOKEN_INDICATOR_COLOR: { + int r, g, b, a; + parser.scanStr((char *)params, "%d,%d,%d,%d", &r, &g, &b, &a); + indicatorColor = BYTETORGBA(r, g, b, a); + } + break; + + case TOKEN_INDICATOR_WIDTH: + parser.scanStr((char *)params, "%d", &indicatorWidth); + break; + + case TOKEN_INDICATOR_HEIGHT: + parser.scanStr((char *)params, "%d", &indicatorHeight); + break; + + case TOKEN_SAVE_IMAGE: + saveImageName = (char *) params; + break; + + case TOKEN_SAVE_IMAGE_X: + parser.scanStr((char *)params, "%d", &saveImageX); + break; + + case TOKEN_SAVE_IMAGE_Y: + parser.scanStr((char *)params, "%d", &saveImageY); + break; + + case TOKEN_LOAD_IMAGE: + loadImageName = (char *) params; + break; + + case TOKEN_LOAD_IMAGE_X: + parser.scanStr((char *)params, "%d", &loadImageX); + break; + + case TOKEN_LOAD_IMAGE_Y: + parser.scanStr((char *)params, "%d", &loadImageY); + break; + + case TOKEN_LOCAL_SAVE_DIR: + _localSaveDir = (char *)params; + break; + + case TOKEN_COMPAT_KILL_METHOD_THREADS: + parser.scanStr((char *)params, "%b", &_compatKillMethodThreads); + break; + } + } + + _renderer->setIndicator(indicatorWidth, indicatorHeight, indicatorX, indicatorY, indicatorColor); + _renderer->initIndicator(); // In case we just reset the values. + _renderer->setSaveImage(saveImageName.c_str(), saveImageX, saveImageY); + _renderer->setLoadingScreen(loadImageName.c_str(), loadImageX, loadImageY); + + if (!_systemFont) { + _systemFont = _gameRef->_fontStorage->addFont("system_font.fnt"); + } + + + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in GAME definition"); + return STATUS_FAILED; + } + if (cmd == PARSERR_GENERIC) { + _gameRef->LOG(0, "Error loading GAME definition"); + return STATUS_FAILED; + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool BaseGame::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // LOG + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "LOG") == 0) { + stack->correctParams(1); + LOG(0, stack->pop()->getString()); + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Caption + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Caption") == 0) { + bool res = BaseObject::scCallMethod(script, stack, thisStack, name); + setWindowTitle(); + return res; + } + + ////////////////////////////////////////////////////////////////////////// + // Msg + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Msg") == 0) { + stack->correctParams(1); + quickMessage(stack->pop()->getString()); + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // RunScript + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "RunScript") == 0) { + _gameRef->LOG(0, "**Warning** The 'RunScript' method is now obsolete. Use 'AttachScript' instead (same syntax)"); + stack->correctParams(1); + if (DID_FAIL(addScript(stack->pop()->getString()))) { + stack->pushBool(false); + } else { + stack->pushBool(true); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // LoadStringTable + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "LoadStringTable") == 0) { + stack->correctParams(2); + const char *filename = stack->pop()->getString(); + ScValue *val = stack->pop(); + + bool clearOld; + if (val->isNULL()) { + clearOld = true; + } else { + clearOld = val->getBool(); + } + + if (DID_FAIL(_stringTable->loadFile(filename, clearOld))) { + stack->pushBool(false); + } else { + stack->pushBool(true); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ValidObject + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ValidObject") == 0) { + stack->correctParams(1); + BaseScriptable *obj = stack->pop()->getNative(); + if (validObject((BaseObject *) obj)) { + stack->pushBool(true); + } else { + stack->pushBool(false); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Reset + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Reset") == 0) { + stack->correctParams(0); + resetContent(); + stack->pushNULL(); + + return STATUS_OK; + } + + + ////////////////////////////////////////////////////////////////////////// + // UnloadObject + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "UnloadObject") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + BaseObject *obj = (BaseObject *)val->getNative(); + unregisterObject(obj); + if (val->getType() == VAL_VARIABLE_REF) { + val->setNULL(); + } + + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // LoadWindow + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "LoadWindow") == 0) { + stack->correctParams(1); + UIWindow *win = new UIWindow(_gameRef); + if (win && DID_SUCCEED(win->loadFile(stack->pop()->getString()))) { + _windows.add(win); + registerObject(win); + stack->pushNative(win, true); + } else { + delete win; + win = NULL; + stack->pushNULL(); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ExpandString + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ExpandString") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + char *str = new char[strlen(val->getString()) + 1]; + strcpy(str, val->getString()); + _stringTable->expand(&str); + stack->pushString(str); + delete[] str; + return STATUS_OK; + } + + else if (_musicSystem->scCallMethod(script, stack, thisStack, name) == STATUS_OK) { + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetMousePos + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetMousePos") == 0) { + stack->correctParams(2); + int x = stack->pop()->getInt(); + int y = stack->pop()->getInt(); + x = MAX(x, 0); + x = MIN(x, _renderer->_width); + y = MAX(y, 0); + y = MIN(y, _renderer->_height); + Point32 p; + p.x = x + _renderer->_drawOffsetX; + p.y = y + _renderer->_drawOffsetY; + + BasePlatform::setCursorPos(p.x, p.y); + + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // LockMouseRect + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "LockMouseRect") == 0) { + stack->correctParams(4); + int left = stack->pop()->getInt(); + int top = stack->pop()->getInt(); + int right = stack->pop()->getInt(); + int bottom = stack->pop()->getInt(); + + if (right < left) { + BaseUtils::swap(&left, &right); + } + if (bottom < top) { + BaseUtils::swap(&top, &bottom); + } + + BasePlatform::setRect(&_mouseLockRect, left, top, right, bottom); + + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // PlayVideo + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "PlayVideo") == 0) { + _gameRef->LOG(0, "Warning: Game.PlayVideo() is now deprecated. Use Game.PlayTheora() instead."); + + stack->correctParams(6); + const char *filename = stack->pop()->getString(); + warning("PlayVideo: %s - not implemented yet", filename); + ScValue *valType = stack->pop(); + int type; + if (valType->isNULL()) { + type = (int)VID_PLAY_STRETCH; + } else { + type = valType->getInt(); + } + + int xVal = stack->pop()->getInt(); + int yVal = stack->pop()->getInt(); + bool freezeMusic = stack->pop()->getBool(true); + + ScValue *valSub = stack->pop(); + const char *subtitleFile = valSub->isNULL() ? NULL : valSub->getString(); + + if (type < (int)VID_PLAY_POS || type > (int)VID_PLAY_CENTER) { + type = (int)VID_PLAY_STRETCH; + } + + if (DID_SUCCEED(_gameRef->_videoPlayer->initialize(filename, subtitleFile))) { + if (DID_SUCCEED(_gameRef->_videoPlayer->play((TVideoPlayback)type, xVal, yVal, freezeMusic))) { + stack->pushBool(true); + script->sleep(0); + } else { + stack->pushBool(false); + } + } else { + stack->pushBool(false); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // PlayTheora + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "PlayTheora") == 0) { + stack->correctParams(7); + const char *filename = stack->pop()->getString(); + ScValue *valType = stack->pop(); + int type; + if (valType->isNULL()) { + type = (int)VID_PLAY_STRETCH; + } else { + type = valType->getInt(); + } + + int xVal = stack->pop()->getInt(); + int yVal = stack->pop()->getInt(); + bool freezeMusic = stack->pop()->getBool(true); + bool dropFrames = stack->pop()->getBool(true); + + ScValue *valSub = stack->pop(); + const char *subtitleFile = valSub->isNULL() ? NULL : valSub->getString(); + + if (type < (int)VID_PLAY_POS || type > (int)VID_PLAY_CENTER) { + type = (int)VID_PLAY_STRETCH; + } + + delete _theoraPlayer; + _theoraPlayer = new VideoTheoraPlayer(this); + if (_theoraPlayer && DID_SUCCEED(_theoraPlayer->initialize(filename, subtitleFile))) { + _theoraPlayer->_dontDropFrames = !dropFrames; + if (DID_SUCCEED(_theoraPlayer->play((TVideoPlayback)type, xVal, yVal, true, freezeMusic))) { + stack->pushBool(true); + script->sleep(0); + } else { + stack->pushBool(false); + } + } else { + stack->pushBool(false); + delete _theoraPlayer; + _theoraPlayer = NULL; + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // QuitGame + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "QuitGame") == 0) { + stack->correctParams(0); + stack->pushNULL(); + _quitting = true; + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // RegWriteNumber + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "RegWriteNumber") == 0) { + stack->correctParams(2); + const char *key = stack->pop()->getString(); + int val = stack->pop()->getInt(); + Common::String privKey = "priv_" + StringUtil::encodeSetting(key); + ConfMan.setInt(privKey, val); + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // RegReadNumber + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "RegReadNumber") == 0) { + stack->correctParams(2); + const char *key = stack->pop()->getString(); + int initVal = stack->pop()->getInt(); + Common::String privKey = "priv_" + StringUtil::encodeSetting(key); + int result = initVal; + if (ConfMan.hasKey(privKey)) { + result = ConfMan.getInt(privKey); + } + stack->pushInt(result); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // RegWriteString + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "RegWriteString") == 0) { + stack->correctParams(2); + const char *key = stack->pop()->getString(); + const char *val = stack->pop()->getString(); + Common::String privKey = "wme_" + StringUtil::encodeSetting(key); + Common::String privVal = StringUtil::encodeSetting(val); + ConfMan.set(privKey, privVal); + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // RegReadString + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "RegReadString") == 0) { + stack->correctParams(2); + const char *key = stack->pop()->getString(); + const char *initVal = stack->pop()->getString(); + Common::String privKey = "wme_" + StringUtil::encodeSetting(key); + Common::String result = initVal; + if (ConfMan.hasKey(privKey)) { + result = StringUtil::decodeSetting(ConfMan.get(key)); + } + stack->pushString(result.c_str()); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SaveGame + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SaveGame") == 0) { + stack->correctParams(3); + int slot = stack->pop()->getInt(); + const char *xdesc = stack->pop()->getString(); + bool quick = stack->pop()->getBool(false); + + char *desc = new char[strlen(xdesc) + 1]; + strcpy(desc, xdesc); + stack->pushBool(true); + if (DID_FAIL(saveGame(slot, desc, quick))) { + stack->pop(); + stack->pushBool(false); + } + delete[] desc; + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // LoadGame + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "LoadGame") == 0) { + stack->correctParams(1); + _scheduledLoadSlot = stack->pop()->getInt(); + _loading = true; + stack->pushBool(false); + script->sleep(0); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // IsSaveSlotUsed + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "IsSaveSlotUsed") == 0) { + stack->correctParams(1); + int slot = stack->pop()->getInt(); + stack->pushBool(SaveLoad::isSaveSlotUsed(slot)); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetSaveSlotDescription + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetSaveSlotDescription") == 0) { + stack->correctParams(1); + int slot = stack->pop()->getInt(); + char desc[512]; + desc[0] = '\0'; + SaveLoad::getSaveSlotDescription(slot, desc); + stack->pushString(desc); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // EmptySaveSlot + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "EmptySaveSlot") == 0) { + stack->correctParams(1); + int slot = stack->pop()->getInt(); + SaveLoad::emptySaveSlot(slot); + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetGlobalSFXVolume + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetGlobalSFXVolume") == 0) { + stack->correctParams(1); + _gameRef->_soundMgr->setVolumePercent(Audio::Mixer::kSFXSoundType, (byte)stack->pop()->getInt()); + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetGlobalSpeechVolume + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetGlobalSpeechVolume") == 0) { + stack->correctParams(1); + _gameRef->_soundMgr->setVolumePercent(Audio::Mixer::kSpeechSoundType, (byte)stack->pop()->getInt()); + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetGlobalMusicVolume + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetGlobalMusicVolume") == 0) { + stack->correctParams(1); + _gameRef->_soundMgr->setVolumePercent(Audio::Mixer::kMusicSoundType, (byte)stack->pop()->getInt()); + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetGlobalMasterVolume + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetGlobalMasterVolume") == 0) { + stack->correctParams(1); + _gameRef->_soundMgr->setMasterVolumePercent((byte)stack->pop()->getInt()); + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetGlobalSFXVolume + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetGlobalSFXVolume") == 0) { + stack->correctParams(0); + stack->pushInt(_soundMgr->getVolumePercent(Audio::Mixer::kSFXSoundType)); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetGlobalSpeechVolume + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetGlobalSpeechVolume") == 0) { + stack->correctParams(0); + stack->pushInt(_soundMgr->getVolumePercent(Audio::Mixer::kSpeechSoundType)); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetGlobalMusicVolume + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetGlobalMusicVolume") == 0) { + stack->correctParams(0); + stack->pushInt(_soundMgr->getVolumePercent(Audio::Mixer::kMusicSoundType)); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetGlobalMasterVolume + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetGlobalMasterVolume") == 0) { + stack->correctParams(0); + stack->pushInt(_soundMgr->getMasterVolumePercent()); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetActiveCursor + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetActiveCursor") == 0) { + stack->correctParams(1); + if (DID_SUCCEED(setActiveCursor(stack->pop()->getString()))) { + stack->pushBool(true); + } else { + stack->pushBool(false); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetActiveCursor + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetActiveCursor") == 0) { + stack->correctParams(0); + if (!_activeCursor || !_activeCursor->getFilename()) { + stack->pushNULL(); + } else { + stack->pushString(_activeCursor->getFilename()); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetActiveCursorObject + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetActiveCursorObject") == 0) { + stack->correctParams(0); + if (!_activeCursor) { + stack->pushNULL(); + } else { + stack->pushNative(_activeCursor, true); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // RemoveActiveCursor + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "RemoveActiveCursor") == 0) { + stack->correctParams(0); + delete _activeCursor; + _activeCursor = NULL; + stack->pushNULL(); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // HasActiveCursor + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "HasActiveCursor") == 0) { + stack->correctParams(0); + + if (_activeCursor) { + stack->pushBool(true); + } else { + stack->pushBool(false); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // FileExists + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "FileExists") == 0) { + stack->correctParams(1); + const char *filename = stack->pop()->getString(); + + bool exists = BaseFileManager::getEngineInstance()->hasFile(filename); // Had absPathWarning = false + stack->pushBool(exists); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // FadeOut / FadeOutAsync / SystemFadeOut / SystemFadeOutAsync + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "FadeOut") == 0 || strcmp(name, "FadeOutAsync") == 0 || strcmp(name, "SystemFadeOut") == 0 || strcmp(name, "SystemFadeOutAsync") == 0) { + stack->correctParams(5); + uint32 duration = stack->pop()->getInt(500); + byte red = stack->pop()->getInt(0); + byte green = stack->pop()->getInt(0); + byte blue = stack->pop()->getInt(0); + byte alpha = stack->pop()->getInt(0xFF); + + bool system = (strcmp(name, "SystemFadeOut") == 0 || strcmp(name, "SystemFadeOutAsync") == 0); + + _fader->fadeOut(BYTETORGBA(red, green, blue, alpha), duration, system); + if (strcmp(name, "FadeOutAsync") != 0 && strcmp(name, "SystemFadeOutAsync") != 0) { + script->waitFor(_fader); + } + + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // FadeIn / FadeInAsync / SystemFadeIn / SystemFadeInAsync + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "FadeIn") == 0 || strcmp(name, "FadeInAsync") == 0 || strcmp(name, "SystemFadeIn") == 0 || strcmp(name, "SystemFadeInAsync") == 0) { + stack->correctParams(5); + uint32 duration = stack->pop()->getInt(500); + byte red = stack->pop()->getInt(0); + byte green = stack->pop()->getInt(0); + byte blue = stack->pop()->getInt(0); + byte alpha = stack->pop()->getInt(0xFF); + + bool system = (strcmp(name, "SystemFadeIn") == 0 || strcmp(name, "SystemFadeInAsync") == 0); + + _fader->fadeIn(BYTETORGBA(red, green, blue, alpha), duration, system); + if (strcmp(name, "FadeInAsync") != 0 && strcmp(name, "SystemFadeInAsync") != 0) { + script->waitFor(_fader); + } + + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetFadeColor + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetFadeColor") == 0) { + stack->correctParams(0); + stack->pushInt(_fader->getCurrentColor()); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Screenshot + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Screenshot") == 0) { + stack->correctParams(1); + char filename[MAX_PATH_LENGTH]; + + ScValue *val = stack->pop(); + + warning("BGame::ScCallMethod - Screenshot not reimplemented"); //TODO + int fileNum = 0; + + while (true) { + sprintf(filename, "%s%03d.bmp", val->isNULL() ? getName() : val->getString(), fileNum); + if (!Common::File::exists(filename)) { + break; + } + fileNum++; + } + + bool ret = false; + BaseImage *image = _gameRef->_renderer->takeScreenshot(); + if (image) { + ret = DID_SUCCEED(image->saveBMPFile(filename)); + delete image; + } else { + ret = false; + } + + stack->pushBool(ret); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ScreenshotEx + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ScreenshotEx") == 0) { + stack->correctParams(3); + const char *filename = stack->pop()->getString(); + int sizeX = stack->pop()->getInt(_renderer->_width); + int sizeY = stack->pop()->getInt(_renderer->_height); + + bool ret = false; + BaseImage *image = _gameRef->_renderer->takeScreenshot(); + if (image) { + ret = DID_SUCCEED(image->resize(sizeX, sizeY)); + if (ret) { + ret = DID_SUCCEED(image->saveBMPFile(filename)); + } + delete image; + } else { + ret = false; + } + + stack->pushBool(ret); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // CreateWindow + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "CreateWindow") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + UIWindow *win = new UIWindow(_gameRef); + _windows.add(win); + registerObject(win); + if (!val->isNULL()) { + win->setName(val->getString()); + } + stack->pushNative(win, true); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // DeleteWindow + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "DeleteWindow") == 0) { + stack->correctParams(1); + BaseObject *obj = (BaseObject *)stack->pop()->getNative(); + for (uint32 i = 0; i < _windows.size(); i++) { + if (_windows[i] == obj) { + unregisterObject(_windows[i]); + stack->pushBool(true); + return STATUS_OK; + } + } + stack->pushBool(false); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // OpenDocument + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "OpenDocument") == 0) { + stack->correctParams(0); + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // DEBUG_DumpClassRegistry + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "DEBUG_DumpClassRegistry") == 0) { + stack->correctParams(0); + DEBUG_DumpClassRegistry(); + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetLoadingScreen + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetLoadingScreen") == 0) { + stack->correctParams(3); + ScValue *val = stack->pop(); + int loadImageX = stack->pop()->getInt(); + int loadImageY = stack->pop()->getInt(); + + if (val->isNULL()) { + _renderer->setLoadingScreen(NULL, loadImageX, loadImageY); + } else { + _renderer->setLoadingScreen(val->getString(), loadImageX, loadImageY); + } + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetSavingScreen + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetSavingScreen") == 0) { + stack->correctParams(3); + ScValue *val = stack->pop(); + int saveImageX = stack->pop()->getInt(); + int saveImageY = stack->pop()->getInt(); + + if (val->isNULL()) { + _renderer->setSaveImage(NULL, saveImageX, saveImageY); + } else { + _renderer->setSaveImage(NULL, saveImageX, saveImageY); + } + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetWaitCursor + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetWaitCursor") == 0) { + stack->correctParams(1); + if (DID_SUCCEED(setWaitCursor(stack->pop()->getString()))) { + stack->pushBool(true); + } else { + stack->pushBool(false); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // RemoveWaitCursor + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "RemoveWaitCursor") == 0) { + stack->correctParams(0); + delete _cursorNoninteractive; + _cursorNoninteractive = NULL; + + stack->pushNULL(); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetWaitCursor + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetWaitCursor") == 0) { + stack->correctParams(0); + if (!_cursorNoninteractive || !_cursorNoninteractive->getFilename()) { + stack->pushNULL(); + } else { + stack->pushString(_cursorNoninteractive->getFilename()); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetWaitCursorObject + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetWaitCursorObject") == 0) { + stack->correctParams(0); + if (!_cursorNoninteractive) { + stack->pushNULL(); + } else { + stack->pushNative(_cursorNoninteractive, true); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ClearScriptCache + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ClearScriptCache") == 0) { + stack->correctParams(0); + stack->pushBool(DID_SUCCEED(_scEngine->emptyScriptCache())); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // DisplayLoadingIcon + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "DisplayLoadingIcon") == 0) { + stack->correctParams(4); + + const char *filename = stack->pop()->getString(); + _loadingIconX = stack->pop()->getInt(); + _loadingIconY = stack->pop()->getInt(); + _loadingIconPersistent = stack->pop()->getBool(); + + delete _loadingIcon; + _loadingIcon = new BaseSprite(this); + if (!_loadingIcon || DID_FAIL(_loadingIcon->loadFile(filename))) { + delete _loadingIcon; + _loadingIcon = NULL; + } else { + displayContent(false, true); + _gameRef->_renderer->flip(); + _gameRef->_renderer->initLoop(); + } + stack->pushNULL(); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // HideLoadingIcon + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "HideLoadingIcon") == 0) { + stack->correctParams(0); + delete _loadingIcon; + _loadingIcon = NULL; + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // DumpTextureStats + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "DumpTextureStats") == 0) { + stack->correctParams(1); + const char *filename = stack->pop()->getString(); + + _renderer->dumpData(filename); + + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // AccOutputText + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AccOutputText") == 0) { + stack->correctParams(2); + /* const char *str = */ stack->pop()->getString(); + /* int type = */ stack->pop()->getInt(); + // do nothing + stack->pushNULL(); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // StoreSaveThumbnail + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "StoreSaveThumbnail") == 0) { + stack->correctParams(0); + delete _cachedThumbnail; + _cachedThumbnail = new BaseSaveThumbHelper(this); + if (DID_FAIL(_cachedThumbnail->storeThumbnail())) { + delete _cachedThumbnail; + _cachedThumbnail = NULL; + stack->pushBool(false); + } else { + stack->pushBool(true); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // DeleteSaveThumbnail + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "DeleteSaveThumbnail") == 0) { + stack->correctParams(0); + delete _cachedThumbnail; + _cachedThumbnail = NULL; + stack->pushNULL(); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetFileChecksum + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetFileChecksum") == 0) { + stack->correctParams(2); + const char *filename = stack->pop()->getString(); + bool asHex = stack->pop()->getBool(false); + + Common::SeekableReadStream *file = BaseFileManager::getEngineInstance()->openFile(filename, false); + if (file) { + crc remainder = crc_initialize(); + byte buf[1024]; + int bytesRead = 0; + + while (bytesRead < file->size()) { + int bufSize = MIN((uint32)1024, (uint32)(file->size() - bytesRead)); + bytesRead += file->read(buf, bufSize); + + for (int i = 0; i < bufSize; i++) { + remainder = crc_process_byte(buf[i], remainder); + } + } + crc checksum = crc_finalize(remainder); + + if (asHex) { + char hex[100]; + sprintf(hex, "%x", checksum); + stack->pushString(hex); + } else { + stack->pushInt(checksum); + } + + BaseFileManager::getEngineInstance()->closeFile(file); + file = NULL; + } else { + stack->pushNULL(); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // EnableScriptProfiling + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "EnableScriptProfiling") == 0) { + stack->correctParams(0); + _scEngine->enableProfiling(); + stack->pushNULL(); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // DisableScriptProfiling + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "DisableScriptProfiling") == 0) { + stack->correctParams(0); + _scEngine->disableProfiling(); + stack->pushNULL(); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ShowStatusLine + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ShowStatusLine") == 0) { + stack->correctParams(0); + // Block kept to show intention of opcode. + /*#ifdef __IPHONEOS__ + IOS_ShowStatusLine(TRUE); + #endif*/ + stack->pushNULL(); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // HideStatusLine + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "HideStatusLine") == 0) { + stack->correctParams(0); + // Block kept to show intention of opcode. + /*#ifdef __IPHONEOS__ + IOS_ShowStatusLine(FALSE); + #endif*/ + stack->pushNULL(); + + return STATUS_OK; + } else { + return BaseObject::scCallMethod(script, stack, thisStack, name); + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *BaseGame::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("game"); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // Name + ////////////////////////////////////////////////////////////////////////// + else if (name == "Name") { + _scValue->setString(getName()); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // Hwnd (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "Hwnd") { + _scValue->setInt((int)_renderer->_window); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // CurrentTime (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "CurrentTime") { + _scValue->setInt((int)_timer); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // WindowsTime (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "WindowsTime") { + _scValue->setInt((int)g_system->getMillis()); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // WindowedMode (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "WindowedMode") { + _scValue->setBool(_renderer->_windowed); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // MouseX + ////////////////////////////////////////////////////////////////////////// + else if (name == "MouseX") { + _scValue->setInt(_mousePos.x); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // MouseY + ////////////////////////////////////////////////////////////////////////// + else if (name == "MouseY") { + _scValue->setInt(_mousePos.y); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // MainObject + ////////////////////////////////////////////////////////////////////////// + else if (name == "MainObject") { + _scValue->setNative(_mainObject, true); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // ActiveObject (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "ActiveObject") { + _scValue->setNative(_activeObject, true); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // ScreenWidth (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "ScreenWidth") { + _scValue->setInt(_renderer->_width); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // ScreenHeight (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "ScreenHeight") { + _scValue->setInt(_renderer->_height); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Interactive + ////////////////////////////////////////////////////////////////////////// + else if (name == "Interactive") { + _scValue->setBool(_interactive); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // DebugMode (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "DebugMode") { + _scValue->setBool(_debugDebugMode); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // SoundAvailable (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "SoundAvailable") { + _scValue->setBool(_soundMgr->_soundAvailable); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // SFXVolume + ////////////////////////////////////////////////////////////////////////// + else if (name == "SFXVolume") { + _gameRef->LOG(0, "**Warning** The SFXVolume attribute is obsolete"); + _scValue->setInt(_soundMgr->getVolumePercent(Audio::Mixer::kSFXSoundType)); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // SpeechVolume + ////////////////////////////////////////////////////////////////////////// + else if (name == "SpeechVolume") { + _gameRef->LOG(0, "**Warning** The SpeechVolume attribute is obsolete"); + _scValue->setInt(_soundMgr->getVolumePercent(Audio::Mixer::kSpeechSoundType)); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // MusicVolume + ////////////////////////////////////////////////////////////////////////// + else if (name == "MusicVolume") { + _gameRef->LOG(0, "**Warning** The MusicVolume attribute is obsolete"); + _scValue->setInt(_soundMgr->getVolumePercent(Audio::Mixer::kMusicSoundType)); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // MasterVolume + ////////////////////////////////////////////////////////////////////////// + else if (name == "MasterVolume") { + _gameRef->LOG(0, "**Warning** The MasterVolume attribute is obsolete"); + _scValue->setInt(_soundMgr->getMasterVolumePercent()); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Keyboard (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "Keyboard") { + if (_keyboardState) { + _scValue->setNative(_keyboardState, true); + } else { + _scValue->setNULL(); + } + + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Subtitles + ////////////////////////////////////////////////////////////////////////// + else if (name == "Subtitles") { + _scValue->setBool(_subtitles); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // SubtitlesSpeed + ////////////////////////////////////////////////////////////////////////// + else if (name == "SubtitlesSpeed") { + _scValue->setInt(_subtitlesSpeed); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // VideoSubtitles + ////////////////////////////////////////////////////////////////////////// + else if (name == "VideoSubtitles") { + _scValue->setBool(_videoSubtitles); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // FPS (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "FPS") { + _scValue->setInt(_fps); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // AcceleratedMode / Accelerated (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "AcceleratedMode" || name == "Accelerated") { + _scValue->setBool(_useD3D); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // TextEncoding + ////////////////////////////////////////////////////////////////////////// + else if (name == "TextEncoding") { + _scValue->setInt(_textEncoding); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // TextRTL + ////////////////////////////////////////////////////////////////////////// + else if (name == "TextRTL") { + _scValue->setBool(_textRTL); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // SoundBufferSize + ////////////////////////////////////////////////////////////////////////// + else if (name == "SoundBufferSize") { + _scValue->setInt(_soundBufferSizeSec); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // SuspendedRendering + ////////////////////////////////////////////////////////////////////////// + else if (name == "SuspendedRendering") { + _scValue->setBool(_suspendedRendering); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // SuppressScriptErrors + ////////////////////////////////////////////////////////////////////////// + else if (name == "SuppressScriptErrors") { + _scValue->setBool(_suppressScriptErrors); + return _scValue; + } + + + ////////////////////////////////////////////////////////////////////////// + // Frozen + ////////////////////////////////////////////////////////////////////////// + else if (name == "Frozen") { + _scValue->setBool(_state == GAME_FROZEN); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // AccTTSEnabled + ////////////////////////////////////////////////////////////////////////// + else if (name == "AccTTSEnabled") { + _scValue->setBool(false); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // AccTTSTalk + ////////////////////////////////////////////////////////////////////////// + else if (name == "AccTTSTalk") { + _scValue->setBool(false); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // AccTTSCaptions + ////////////////////////////////////////////////////////////////////////// + else if (name == "AccTTSCaptions") { + _scValue->setBool(false); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // AccTTSKeypress + ////////////////////////////////////////////////////////////////////////// + else if (name == "AccTTSKeypress") { + _scValue->setBool(false); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // AccKeyboardEnabled + ////////////////////////////////////////////////////////////////////////// + else if (name == "AccKeyboardEnabled") { + _scValue->setBool(false); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // AccKeyboardCursorSkip + ////////////////////////////////////////////////////////////////////////// + else if (name == "AccKeyboardCursorSkip") { + _scValue->setBool(false); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // AccKeyboardPause + ////////////////////////////////////////////////////////////////////////// + else if (name == "AccKeyboardPause") { + _scValue->setBool(false); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // AutorunDisabled + ////////////////////////////////////////////////////////////////////////// + else if (name == "AutorunDisabled") { + _scValue->setBool(_autorunDisabled); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // SaveDirectory (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "SaveDirectory") { + AnsiString dataDir = "saves/"; // TODO: This is just to avoid telling the engine actual paths. + _scValue->setString(dataDir.c_str()); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // AutoSaveOnExit + ////////////////////////////////////////////////////////////////////////// + else if (name == "AutoSaveOnExit") { + _scValue->setBool(_autoSaveOnExit); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // AutoSaveSlot + ////////////////////////////////////////////////////////////////////////// + else if (name == "AutoSaveSlot") { + _scValue->setInt(_autoSaveSlot); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // CursorHidden + ////////////////////////////////////////////////////////////////////////// + else if (name == "CursorHidden") { + _scValue->setBool(_cursorHidden); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Platform (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "Platform") { + _scValue->setString(BasePlatform::getPlatformName().c_str()); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // DeviceType (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "DeviceType") { + _scValue->setString(getDeviceType().c_str()); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // MostRecentSaveSlot (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "MostRecentSaveSlot") { + if (!ConfMan.hasKey("most_recent_saveslot")) { + _scValue->setInt(-1); + } else { + _scValue->setInt(ConfMan.getInt("most_recent_saveslot")); + } + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Store (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "Store") { + _scValue->setNULL(); + error("Request for a SXStore-object, which is not supported by ScummVM"); + + return _scValue; + } else { + return BaseObject::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::scSetProperty(const char *name, ScValue *value) { + ////////////////////////////////////////////////////////////////////////// + // Name + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Name") == 0) { + setName(value->getString()); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // MouseX + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "MouseX") == 0) { + _mousePos.x = value->getInt(); + resetMousePos(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // MouseY + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "MouseY") == 0) { + _mousePos.y = value->getInt(); + resetMousePos(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Caption + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Name") == 0) { + bool res = BaseObject::scSetProperty(name, value); + setWindowTitle(); + return res; + } + + ////////////////////////////////////////////////////////////////////////// + // MainObject + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "MainObject") == 0) { + BaseScriptable *obj = value->getNative(); + if (obj == NULL || validObject((BaseObject *)obj)) { + _mainObject = (BaseObject *)obj; + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Interactive + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Interactive") == 0) { + setInteractive(value->getBool()); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SFXVolume + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SFXVolume") == 0) { + _gameRef->LOG(0, "**Warning** The SFXVolume attribute is obsolete"); + _gameRef->_soundMgr->setVolumePercent(Audio::Mixer::kSFXSoundType, (byte)value->getInt()); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SpeechVolume + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SpeechVolume") == 0) { + _gameRef->LOG(0, "**Warning** The SpeechVolume attribute is obsolete"); + _gameRef->_soundMgr->setVolumePercent(Audio::Mixer::kSpeechSoundType, (byte)value->getInt()); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // MusicVolume + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "MusicVolume") == 0) { + _gameRef->LOG(0, "**Warning** The MusicVolume attribute is obsolete"); + _gameRef->_soundMgr->setVolumePercent(Audio::Mixer::kMusicSoundType, (byte)value->getInt()); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // MasterVolume + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "MasterVolume") == 0) { + _gameRef->LOG(0, "**Warning** The MasterVolume attribute is obsolete"); + _gameRef->_soundMgr->setMasterVolumePercent((byte)value->getInt()); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Subtitles + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Subtitles") == 0) { + _subtitles = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SubtitlesSpeed + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SubtitlesSpeed") == 0) { + _subtitlesSpeed = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // VideoSubtitles + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "VideoSubtitles") == 0) { + _videoSubtitles = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // TextEncoding + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "TextEncoding") == 0) { + int enc = value->getInt(); + if (enc < 0) { + enc = 0; + } + if (enc >= NUM_TEXT_ENCODINGS) { + enc = NUM_TEXT_ENCODINGS - 1; + } + _textEncoding = (TTextEncoding)enc; + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // TextRTL + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "TextRTL") == 0) { + _textRTL = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SoundBufferSize + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SoundBufferSize") == 0) { + _soundBufferSizeSec = value->getInt(); + _soundBufferSizeSec = MAX(3, _soundBufferSizeSec); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SuspendedRendering + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SuspendedRendering") == 0) { + _suspendedRendering = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SuppressScriptErrors + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SuppressScriptErrors") == 0) { + _suppressScriptErrors = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // AutorunDisabled + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AutorunDisabled") == 0) { + _autorunDisabled = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // AutoSaveOnExit + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AutoSaveOnExit") == 0) { + _autoSaveOnExit = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // AutoSaveSlot + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AutoSaveSlot") == 0) { + _autoSaveSlot = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // CursorHidden + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "CursorHidden") == 0) { + _cursorHidden = value->getBool(); + return STATUS_OK; + } else { + return BaseObject::scSetProperty(name, value); + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *BaseGame::scToString() { + return "[game object]"; +} + + + +#define QUICK_MSG_DURATION 3000 +////////////////////////////////////////////////////////////////////////// +bool BaseGame::displayQuickMsg() { + if (_quickMessages.size() == 0 || !_systemFont) { + return STATUS_OK; + } + + // update + for (uint32 i = 0; i < _quickMessages.size(); i++) { + if (_currentTime - _quickMessages[i]->_startTime >= QUICK_MSG_DURATION) { + delete _quickMessages[i]; + _quickMessages.remove_at(i); + i--; + } + } + + int posY = 20; + + // display + for (uint32 i = 0; i < _quickMessages.size(); i++) { + _systemFont->drawText((byte *)_quickMessages[i]->getText(), 0, posY, _renderer->_width); + posY += _systemFont->getTextHeight((byte *)_quickMessages[i]->getText(), _renderer->_width); + } + return STATUS_OK; +} + + +#define MAX_QUICK_MSG 5 +////////////////////////////////////////////////////////////////////////// +void BaseGame::quickMessage(const char *text) { + if (_quickMessages.size() >= MAX_QUICK_MSG) { + delete _quickMessages[0]; + _quickMessages.remove_at(0); + } + _quickMessages.add(new BaseQuickMsg(_gameRef, text)); +} + + +////////////////////////////////////////////////////////////////////////// +void BaseGame::quickMessageForm(char *fmt, ...) { + char buff[256]; + va_list va; + + va_start(va, fmt); + vsprintf(buff, fmt, va); + va_end(va); + + quickMessage(buff); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::registerObject(BaseObject *object) { + _regObjects.add(object); + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::unregisterObject(BaseObject *object) { + if (!object) { + return STATUS_OK; + } + + // is it a window? + for (uint32 i = 0; i < _windows.size(); i++) { + if ((BaseObject *)_windows[i] == object) { + _windows.remove_at(i); + + // get new focused window + if (_focusedWindow == object) { + _focusedWindow = NULL; + } + + break; + } + } + + // is it active object? + if (_activeObject == object) { + _activeObject = NULL; + } + + // is it main object? + if (_mainObject == object) { + _mainObject = NULL; + } + + // destroy object + for (uint32 i = 0; i < _regObjects.size(); i++) { + if (_regObjects[i] == object) { + _regObjects.remove_at(i); + if (!_loadInProgress) { + SystemClassRegistry::getInstance()->enumInstances(invalidateValues, "ScValue", (void *)object); + } + delete object; + return STATUS_OK; + } + } + + return STATUS_FAILED; +} + + +////////////////////////////////////////////////////////////////////////// +void BaseGame::invalidateValues(void *value, void *data) { + ScValue *val = (ScValue *)value; + if (val->isNative() && val->getNative() == data) { + if (!val->_persistent && ((BaseScriptable *)data)->_refCount == 1) { + ((BaseScriptable *)data)->_refCount++; + } + val->setNative(NULL); + val->setNULL(); + } +} + + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::validObject(BaseObject *object) { + if (!object) { + return false; + } + if (object == this) { + return true; + } + + for (uint32 i = 0; i < _regObjects.size(); i++) { + if (_regObjects[i] == object) { + return true; + } + } + return false; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::externalCall(ScScript *script, ScStack *stack, ScStack *thisStack, char *name) { + ScValue *thisObj; + + ////////////////////////////////////////////////////////////////////////// + // LOG + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "LOG") == 0) { + stack->correctParams(1); + _gameRef->LOG(0, "sc: %s", stack->pop()->getString()); + stack->pushNULL(); + } + + ////////////////////////////////////////////////////////////////////////// + // String + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "String") == 0) { + thisObj = thisStack->getTop(); + + thisObj->setNative(makeSXString(_gameRef, stack)); + stack->pushNULL(); + } + + ////////////////////////////////////////////////////////////////////////// + // MemBuffer + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "MemBuffer") == 0) { + thisObj = thisStack->getTop(); + + thisObj->setNative(makeSXMemBuffer(_gameRef, stack)); + stack->pushNULL(); + } + + ////////////////////////////////////////////////////////////////////////// + // File + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "File") == 0) { + thisObj = thisStack->getTop(); + + thisObj->setNative(makeSXFile(_gameRef, stack)); + stack->pushNULL(); + } + + ////////////////////////////////////////////////////////////////////////// + // Date + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Date") == 0) { + thisObj = thisStack->getTop(); + + thisObj->setNative(makeSXDate(_gameRef, stack)); + stack->pushNULL(); + } + + ////////////////////////////////////////////////////////////////////////// + // Array + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Array") == 0) { + thisObj = thisStack->getTop(); + + thisObj->setNative(makeSXArray(_gameRef, stack)); + stack->pushNULL(); + } + + ////////////////////////////////////////////////////////////////////////// + // Object + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Object") == 0) { + thisObj = thisStack->getTop(); + + thisObj->setNative(makeSXObject(_gameRef, stack)); + stack->pushNULL(); + } + + ////////////////////////////////////////////////////////////////////////// + // Sleep + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Sleep") == 0) { + stack->correctParams(1); + + script->sleep((uint32)stack->pop()->getInt()); + stack->pushNULL(); + } + + ////////////////////////////////////////////////////////////////////////// + // WaitFor + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "WaitFor") == 0) { + stack->correctParams(1); + + BaseScriptable *obj = stack->pop()->getNative(); + if (validObject((BaseObject *)obj)) { + script->waitForExclusive((BaseObject *)obj); + } + stack->pushNULL(); + } + + ////////////////////////////////////////////////////////////////////////// + // Random + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Random") == 0) { + stack->correctParams(2); + + int from = stack->pop()->getInt(); + int to = stack->pop()->getInt(); + + stack->pushInt(BaseUtils::randomInt(from, to)); + } + + ////////////////////////////////////////////////////////////////////////// + // SetScriptTimeSlice + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetScriptTimeSlice") == 0) { + stack->correctParams(1); + + script->_timeSlice = (uint32)stack->pop()->getInt(); + stack->pushNULL(); + } + + ////////////////////////////////////////////////////////////////////////// + // MakeRGBA / MakeRGB / RGB + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "MakeRGBA") == 0 || strcmp(name, "MakeRGB") == 0 || strcmp(name, "RGB") == 0) { + stack->correctParams(4); + int r = stack->pop()->getInt(); + int g = stack->pop()->getInt(); + int b = stack->pop()->getInt(); + int a; + ScValue *val = stack->pop(); + if (val->isNULL()) { + a = 255; + } else { + a = val->getInt(); + } + + stack->pushInt(BYTETORGBA(r, g, b, a)); + } + + ////////////////////////////////////////////////////////////////////////// + // MakeHSL + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "MakeHSL") == 0) { + stack->correctParams(3); + int h = stack->pop()->getInt(); + int s = stack->pop()->getInt(); + int l = stack->pop()->getInt(); + + stack->pushInt(BaseUtils::HSLtoRGB(h, s, l)); + } + + ////////////////////////////////////////////////////////////////////////// + // GetRValue + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetRValue") == 0) { + stack->correctParams(1); + + uint32 rgba = (uint32)stack->pop()->getInt(); + stack->pushInt(RGBCOLGetR(rgba)); + } + + ////////////////////////////////////////////////////////////////////////// + // GetGValue + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetGValue") == 0) { + stack->correctParams(1); + + uint32 rgba = (uint32)stack->pop()->getInt(); + stack->pushInt(RGBCOLGetG(rgba)); + } + + ////////////////////////////////////////////////////////////////////////// + // GetBValue + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetBValue") == 0) { + stack->correctParams(1); + + uint32 rgba = (uint32)stack->pop()->getInt(); + stack->pushInt(RGBCOLGetB(rgba)); + } + + ////////////////////////////////////////////////////////////////////////// + // GetAValue + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetAValue") == 0) { + stack->correctParams(1); + + uint32 rgba = (uint32)stack->pop()->getInt(); + stack->pushInt(RGBCOLGetA(rgba)); + } + + ////////////////////////////////////////////////////////////////////////// + // GetHValue + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetHValue") == 0) { + stack->correctParams(1); + uint32 rgb = (uint32)stack->pop()->getInt(); + + byte H, S, L; + BaseUtils::RGBtoHSL(rgb, &H, &S, &L); + stack->pushInt(H); + } + + ////////////////////////////////////////////////////////////////////////// + // GetSValue + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetSValue") == 0) { + stack->correctParams(1); + uint32 rgb = (uint32)stack->pop()->getInt(); + + byte H, S, L; + BaseUtils::RGBtoHSL(rgb, &H, &S, &L); + stack->pushInt(S); + } + + ////////////////////////////////////////////////////////////////////////// + // GetLValue + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetLValue") == 0) { + stack->correctParams(1); + uint32 rgb = (uint32)stack->pop()->getInt(); + + byte H, S, L; + BaseUtils::RGBtoHSL(rgb, &H, &S, &L); + stack->pushInt(L); + } + + ////////////////////////////////////////////////////////////////////////// + // Debug + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Debug") == 0) { + stack->correctParams(0); + stack->pushNULL(); + } + + ////////////////////////////////////////////////////////////////////////// + // ToString + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ToString") == 0) { + stack->correctParams(1); + const char *str = stack->pop()->getString(); + char *str2 = new char[strlen(str) + 1]; + strcpy(str2, str); + stack->pushString(str2); + delete[] str2; + } + + ////////////////////////////////////////////////////////////////////////// + // ToInt + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ToInt") == 0) { + stack->correctParams(1); + int val = stack->pop()->getInt(); + stack->pushInt(val); + } + + ////////////////////////////////////////////////////////////////////////// + // ToFloat + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ToFloat") == 0) { + stack->correctParams(1); + double val = stack->pop()->getFloat(); + stack->pushFloat(val); + } + + ////////////////////////////////////////////////////////////////////////// + // ToBool + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ToBool") == 0) { + stack->correctParams(1); + bool val = stack->pop()->getBool(); + stack->pushBool(val); + } + + ////////////////////////////////////////////////////////////////////////// + // failure + else { + script->runtimeError("Call to undefined function '%s'. Ignored.", name); + stack->correctParams(0); + stack->pushNULL(); + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::showCursor() { + if (_cursorHidden) { + return STATUS_OK; + } + + if (!_interactive && _gameRef->_state == GAME_RUNNING) { + if (_cursorNoninteractive) { + return drawCursor(_cursorNoninteractive); + } + } else { + if (_activeObject && !DID_FAIL(_activeObject->showCursor())) { + return STATUS_OK; + } else { + if (_activeObject && _activeCursor && _activeObject->getExtendedFlag("usable")) { + return drawCursor(_activeCursor); + } else if (_cursor) { + return drawCursor(_cursor); + } + } + } + return STATUS_FAILED; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::saveGame(int slot, const char *desc, bool quickSave) { + return SaveLoad::saveGame(slot, desc, quickSave, _gameRef); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::loadGame(int slot) { + //_gameRef->LOG(0, "Load start %d", BaseUtils::GetUsedMemMB()); + + _loading = false; + _scheduledLoadSlot = -1; + + Common::String filename = SaveLoad::getSaveSlotFilename(slot); + + return loadGame(filename.c_str()); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::loadGame(const char *filename) { + return SaveLoad::loadGame(filename, _gameRef); +} + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::displayWindows(bool inGame) { + bool res; + + // did we lose focus? focus topmost window + if (_focusedWindow == NULL || !_focusedWindow->_visible || _focusedWindow->_disable) { + _focusedWindow = NULL; + for (int i = _windows.size() - 1; i >= 0; i--) { + if (_windows[i]->_visible && !_windows[i]->_disable) { + _focusedWindow = _windows[i]; + break; + } + } + } + + // display all windows + for (uint32 i = 0; i < _windows.size(); i++) { + if (_windows[i]->_visible && _windows[i]->_inGame == inGame) { + + res = _windows[i]->display(); + if (DID_FAIL(res)) { + return res; + } + } + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::loadSettings(const char *filename) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(SETTINGS) + TOKEN_TABLE(GAME) + TOKEN_TABLE(STRING_TABLE) + TOKEN_TABLE(RESOLUTION) + TOKEN_TABLE(REQUIRE_3D_ACCELERATION) + TOKEN_TABLE(REQUIRE_SOUND) + TOKEN_TABLE(HWTL_MODE) + TOKEN_TABLE(ALLOW_WINDOWED_MODE) + TOKEN_TABLE(ALLOW_ACCESSIBILITY_TAB) + TOKEN_TABLE(ALLOW_ABOUT_TAB) + TOKEN_TABLE(ALLOW_ADVANCED) + TOKEN_TABLE(ALLOW_DESKTOP_RES) + TOKEN_TABLE(REGISTRY_PATH) + TOKEN_TABLE(RICH_SAVED_GAMES) + TOKEN_TABLE(SAVED_GAME_EXT) + TOKEN_TABLE(GUID) + TOKEN_TABLE_END + + + byte *origBuffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (origBuffer == NULL) { + _gameRef->LOG(0, "BaseGame::LoadSettings failed for file '%s'", filename); + return STATUS_FAILED; + } + + bool ret = STATUS_OK; + + byte *buffer = origBuffer; + byte *params; + int cmd; + BaseParser parser; + + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_SETTINGS) { + _gameRef->LOG(0, "'SETTINGS' keyword expected in game settings file."); + return STATUS_FAILED; + } + buffer = params; + while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_GAME: + delete[] _settingsGameFile; + _settingsGameFile = new char[strlen((char *)params) + 1]; + if (_settingsGameFile) { + strcpy(_settingsGameFile, (char *)params); + } + break; + + case TOKEN_STRING_TABLE: + if (DID_FAIL(_stringTable->loadFile((char *)params))) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_RESOLUTION: + parser.scanStr((char *)params, "%d,%d", &_settingsResWidth, &_settingsResHeight); + break; + + case TOKEN_REQUIRE_3D_ACCELERATION: + parser.scanStr((char *)params, "%b", &_settingsRequireAcceleration); + break; + + case TOKEN_REQUIRE_SOUND: + parser.scanStr((char *)params, "%b", &_settingsRequireSound); + break; + + case TOKEN_HWTL_MODE: + parser.scanStr((char *)params, "%d", &_settingsTLMode); + break; + + case TOKEN_ALLOW_WINDOWED_MODE: + parser.scanStr((char *)params, "%b", &_settingsAllowWindowed); + break; + + case TOKEN_ALLOW_DESKTOP_RES: + parser.scanStr((char *)params, "%b", &_settingsAllowDesktopRes); + break; + + case TOKEN_ALLOW_ADVANCED: + parser.scanStr((char *)params, "%b", &_settingsAllowAdvanced); + break; + + case TOKEN_ALLOW_ACCESSIBILITY_TAB: + parser.scanStr((char *)params, "%b", &_settingsAllowAccessTab); + break; + + case TOKEN_ALLOW_ABOUT_TAB: + parser.scanStr((char *)params, "%b", &_settingsAllowAboutTab); + break; + + case TOKEN_REGISTRY_PATH: + //BaseEngine::instance().getRegistry()->setBasePath((char *)params); + break; + + case TOKEN_RICH_SAVED_GAMES: + parser.scanStr((char *)params, "%b", &_richSavedGames); + break; + + case TOKEN_SAVED_GAME_EXT: + _savedGameExt = (char *)params; + break; + + case TOKEN_GUID: + break; + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in game settings '%s'", filename); + ret = STATUS_FAILED; + } + if (cmd == PARSERR_GENERIC) { + _gameRef->LOG(0, "Error loading game settings '%s'", filename); + ret = STATUS_FAILED; + } + + _settingsAllowWindowed = true; // TODO: These two settings should probably be cleaned out altogether. + _compressedSavegames = true; + + delete[] origBuffer; + + return ret; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::persist(BasePersistenceManager *persistMgr) { + if (!persistMgr->getIsSaving()) { + cleanup(); + } + + BaseObject::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_activeObject)); + persistMgr->transfer(TMEMBER(_capturedObject)); + persistMgr->transfer(TMEMBER(_cursorNoninteractive)); + persistMgr->transfer(TMEMBER(_editorMode)); + persistMgr->transfer(TMEMBER(_fader)); + persistMgr->transfer(TMEMBER(_freezeLevel)); + persistMgr->transfer(TMEMBER(_focusedWindow)); + persistMgr->transfer(TMEMBER(_fontStorage)); + persistMgr->transfer(TMEMBER(_interactive)); + persistMgr->transfer(TMEMBER(_keyboardState)); + persistMgr->transfer(TMEMBER(_lastTime)); + persistMgr->transfer(TMEMBER(_mainObject)); + _musicSystem->persistChannels(persistMgr); + + persistMgr->transfer(TMEMBER(_offsetX)); + persistMgr->transfer(TMEMBER(_offsetY)); + persistMgr->transfer(TMEMBER(_offsetPercentX)); + persistMgr->transfer(TMEMBER(_offsetPercentY)); + + persistMgr->transfer(TMEMBER(_origInteractive)); + persistMgr->transfer(TMEMBER_INT(_origState)); + persistMgr->transfer(TMEMBER(_personalizedSave)); + persistMgr->transfer(TMEMBER(_quitting)); + + _regObjects.persist(persistMgr); + + persistMgr->transfer(TMEMBER(_scEngine)); + //persistMgr->transfer(TMEMBER(_soundMgr)); + persistMgr->transfer(TMEMBER_INT(_state)); + //persistMgr->transfer(TMEMBER(_surfaceStorage)); + persistMgr->transfer(TMEMBER(_subtitles)); + persistMgr->transfer(TMEMBER(_subtitlesSpeed)); + persistMgr->transfer(TMEMBER(_systemFont)); + persistMgr->transfer(TMEMBER(_videoFont)); + persistMgr->transfer(TMEMBER(_videoSubtitles)); + + persistMgr->transfer(TMEMBER(_timer)); + persistMgr->transfer(TMEMBER(_timerDelta)); + persistMgr->transfer(TMEMBER(_timerLast)); + + persistMgr->transfer(TMEMBER(_liveTimer)); + persistMgr->transfer(TMEMBER(_liveTimerDelta)); + persistMgr->transfer(TMEMBER(_liveTimerLast)); + + _musicSystem->persistCrossfadeSettings(persistMgr); + + _renderer->persistSaveLoadImages(persistMgr); + + persistMgr->transfer(TMEMBER_INT(_textEncoding)); + persistMgr->transfer(TMEMBER(_textRTL)); + + persistMgr->transfer(TMEMBER(_soundBufferSizeSec)); + persistMgr->transfer(TMEMBER(_suspendedRendering)); + + persistMgr->transfer(TMEMBER(_mouseLockRect)); + + _windows.persist(persistMgr); + + persistMgr->transfer(TMEMBER(_suppressScriptErrors)); + persistMgr->transfer(TMEMBER(_autorunDisabled)); + + persistMgr->transfer(TMEMBER(_autoSaveOnExit)); + persistMgr->transfer(TMEMBER(_autoSaveSlot)); + persistMgr->transfer(TMEMBER(_cursorHidden)); + + if (!persistMgr->getIsSaving()) { + _quitting = false; + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::focusWindow(UIWindow *window) { + UIWindow *prev = _focusedWindow; + + for (uint32 i = 0; i < _windows.size(); i++) { + if (_windows[i] == window) { + if (i < _windows.size() - 1) { + _windows.remove_at(i); + _windows.add(window); + + _gameRef->_focusedWindow = window; + } + + if (window->_mode == WINDOW_NORMAL && prev != window && _gameRef->validObject(prev) && (prev->_mode == WINDOW_EXCLUSIVE || prev->_mode == WINDOW_SYSTEM_EXCLUSIVE)) { + return focusWindow(prev); + } else { + return STATUS_OK; + } + } + } + return STATUS_FAILED; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::freeze(bool includingMusic) { + if (_freezeLevel == 0) { + _scEngine->pauseAll(); + _soundMgr->pauseAll(includingMusic); + _origState = _state; + _origInteractive = _interactive; + _interactive = true; + } + _state = GAME_FROZEN; + _freezeLevel++; + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::unfreeze() { + if (_freezeLevel == 0) { + return STATUS_OK; + } + + _freezeLevel--; + if (_freezeLevel == 0) { + _state = _origState; + _interactive = _origInteractive; + _scEngine->resumeAll(); + _soundMgr->resumeAll(); + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::handleKeypress(Common::Event *event, bool printable) { + if (isVideoPlaying()) { + if (event->kbd.keycode == Common::KEYCODE_ESCAPE) { + stopVideo(); + } + return true; + } + + if (event->type == Common::EVENT_QUIT) { + onWindowClose(); + return true; + } + + _keyboardState->handleKeyPress(event); + _keyboardState->readKey(event); +// TODO + + if (_focusedWindow) { + if (!_gameRef->_focusedWindow->handleKeypress(event, _keyboardState->isCurrentPrintable())) { + /*if (event->type != SDL_TEXTINPUT) {*/ + if (_gameRef->_focusedWindow->canHandleEvent("Keypress")) { + _gameRef->_focusedWindow->applyEvent("Keypress"); + } else { + applyEvent("Keypress"); + } + /*}*/ + } + return true; + } else { /*if (event->type != SDL_TEXTINPUT)*/ + applyEvent("Keypress"); + return true; + } + + return false; +} + +void BaseGame::handleKeyRelease(Common::Event *event) { + _keyboardState->handleKeyRelease(event); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::handleMouseWheel(int delta) { + bool handled = false; + if (_focusedWindow) { + handled = _gameRef->_focusedWindow->handleMouseWheel(delta); + + if (!handled) { + if (delta < 0 && _gameRef->_focusedWindow->canHandleEvent("MouseWheelDown")) { + _gameRef->_focusedWindow->applyEvent("MouseWheelDown"); + handled = true; + } else if (_gameRef->_focusedWindow->canHandleEvent("MouseWheelUp")) { + _gameRef->_focusedWindow->applyEvent("MouseWheelUp"); + handled = true; + } + + } + } + + if (!handled) { + if (delta < 0) { + applyEvent("MouseWheelDown"); + } else { + applyEvent("MouseWheelUp"); + } + } + + return true; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::getVersion(byte *verMajor, byte *verMinor, byte *extMajor, byte *extMinor) { + if (verMajor) { + *verMajor = DCGF_VER_MAJOR; + } + if (verMinor) { + *verMinor = DCGF_VER_MINOR; + } + + if (extMajor) { + *extMajor = 0; + } + if (extMinor) { + *extMinor = 0; + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +void BaseGame::setWindowTitle() { + if (_renderer) { + char title[512]; + strcpy(title, _caption[0]); + if (title[0] != '\0') { + strcat(title, " - "); + } + strcat(title, "WME Lite"); + + + Utf8String utf8Title; + if (_textEncoding == TEXT_UTF8) { + utf8Title = Utf8String(title); + } else { + warning("BaseGame::SetWindowTitle - Ignoring textencoding"); + utf8Title = Utf8String(title); + /* WideString wstr = StringUtil::AnsiToWide(Title); + title = StringUtil::WideToUtf8(wstr);*/ + } + warning("BaseGame::SetWindowTitle: Ignoring value: %s", utf8Title.c_str()); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::setActiveObject(BaseObject *obj) { + // not-active when game is frozen + if (obj && !_gameRef->_interactive && !obj->_nonIntMouseEvents) { + obj = NULL; + } + + if (obj == _activeObject) { + return STATUS_OK; + } + + if (_activeObject) { + _activeObject->applyEvent("MouseLeave"); + } + //if (ValidObject(_activeObject)) _activeObject->applyEvent("MouseLeave"); + _activeObject = obj; + if (_activeObject) { + _activeObject->applyEvent("MouseEntry"); + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::pushViewport(BaseViewport *viewport) { + _viewportSP++; + if (_viewportSP >= (int32)_viewportStack.size()) { + _viewportStack.add(viewport); + } else { + _viewportStack[_viewportSP] = viewport; + } + + _renderer->setViewport(viewport->getRect()); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::popViewport() { + _viewportSP--; + if (_viewportSP < -1) { + _gameRef->LOG(0, "Fatal: Viewport stack underflow!"); + } + + if (_viewportSP >= 0 && _viewportSP < (int32)_viewportStack.size()) { + _renderer->setViewport(_viewportStack[_viewportSP]->getRect()); + } else _renderer->setViewport(_renderer->_drawOffsetX, + _renderer->_drawOffsetY, + _renderer->_width + _renderer->_drawOffsetX, + _renderer->_height + _renderer->_drawOffsetY); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::getCurrentViewportRect(Rect32 *rect, bool *custom) { + if (rect == NULL) { + return STATUS_FAILED; + } else { + if (_viewportSP >= 0) { + BasePlatform::copyRect(rect, _viewportStack[_viewportSP]->getRect()); + if (custom) { + *custom = true; + } + } else { + BasePlatform::setRect(rect, _renderer->_drawOffsetX, + _renderer->_drawOffsetY, + _renderer->_width + _renderer->_drawOffsetX, + _renderer->_height + _renderer->_drawOffsetY); + if (custom) { + *custom = false; + } + } + + return STATUS_OK; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::getCurrentViewportOffset(int *offsetX, int *offsetY) { + if (_viewportSP >= 0) { + if (offsetX) { + *offsetX = _viewportStack[_viewportSP]->_offsetX; + } + if (offsetY) { + *offsetY = _viewportStack[_viewportSP]->_offsetY; + } + } else { + if (offsetX) { + *offsetX = 0; + } + if (offsetY) { + *offsetY = 0; + } + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::windowLoadHook(UIWindow *win, char **buf, char **params) { + return STATUS_FAILED; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::windowScriptMethodHook(UIWindow *win, ScScript *script, ScStack *stack, const char *name) { + return STATUS_FAILED; +} + + +////////////////////////////////////////////////////////////////////////// +void BaseGame::setInteractive(bool state) { + _interactive = state; + if (_transMgr) { + _transMgr->_origInteractive = state; + } +} + + +////////////////////////////////////////////////////////////////////////// +void BaseGame::resetMousePos() { + Common::Point p; + p.x = _mousePos.x + _renderer->_drawOffsetX; + p.y = _mousePos.y + _renderer->_drawOffsetY; + + BasePlatform::setCursorPos(p.x, p.y); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::displayContent(bool doUpdate, bool displayAll) { + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::displayContentSimple() { + // fill black + _renderer->fill(0, 0, 0); + _renderer->displayIndicator(); + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::resetContent() { + _scEngine->clearGlobals(); + //_timer = 0; + //_liveTimer = 0; + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +void BaseGame::DEBUG_DumpClassRegistry() { + warning("DEBUG_DumpClassRegistry - untested"); + Common::DumpFile *f = new Common::DumpFile; + f->open("zz_class_reg_dump.log"); + + SystemClassRegistry::getInstance()->dumpClasses(f); + + f->close(); + delete f; + _gameRef->quickMessage("Classes dump completed."); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::invalidateDeviceObjects() { + for (uint32 i = 0; i < _regObjects.size(); i++) { + _regObjects[i]->invalidateDeviceObjects(); + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::restoreDeviceObjects() { + for (uint32 i = 0; i < _regObjects.size(); i++) { + _regObjects[i]->restoreDeviceObjects(); + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::setWaitCursor(const char *filename) { + delete _cursorNoninteractive; + _cursorNoninteractive = NULL; + + _cursorNoninteractive = new BaseSprite(_gameRef); + if (!_cursorNoninteractive || DID_FAIL(_cursorNoninteractive->loadFile(filename))) { + delete _cursorNoninteractive; + _cursorNoninteractive = NULL; + return STATUS_FAILED; + } else { + return STATUS_OK; + } +} + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::isVideoPlaying() { + if (_videoPlayer->isPlaying()) { + return true; + } + if (_theoraPlayer && _theoraPlayer->isPlaying()) { + return true; + } + return false; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::stopVideo() { + if (_videoPlayer->isPlaying()) { + _videoPlayer->stop(); + } + if (_theoraPlayer && _theoraPlayer->isPlaying()) { + _theoraPlayer->stop(); + delete _theoraPlayer; + _theoraPlayer = NULL; + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::drawCursor(BaseSprite *cursor) { + if (!cursor) { + return STATUS_FAILED; + } + if (cursor != _lastCursor) { + cursor->reset(); + _lastCursor = cursor; + } + return cursor->draw(_mousePos.x, _mousePos.y); +} + + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// +bool BaseGame::onActivate(bool activate, bool refreshMouse) { + if (_shuttingDown || !_renderer) { + return STATUS_OK; + } + + _renderer->_active = activate; + + if (refreshMouse) { + Point32 p; + getMousePos(&p); + setActiveObject(_renderer->getObjectAt(p.x, p.y)); + } + + if (activate) { + _soundMgr->resumeAll(); + } else { + _soundMgr->pauseAll(); + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::onMouseLeftDown() { + if (_activeObject) { + _activeObject->handleMouse(MOUSE_CLICK, MOUSE_BUTTON_LEFT); + } + + bool handled = _state == GAME_RUNNING && DID_SUCCEED(applyEvent("LeftClick")); + if (!handled) { + if (_activeObject != NULL) { + _activeObject->applyEvent("LeftClick"); + } + } + + if (_activeObject != NULL) { + _capturedObject = _activeObject; + } + _mouseLeftDown = true; + BasePlatform::setCapture(/*_renderer->_window*/); + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::onMouseLeftUp() { + if (_activeObject) { + _activeObject->handleMouse(MOUSE_RELEASE, MOUSE_BUTTON_LEFT); + } + + BasePlatform::releaseCapture(); + _capturedObject = NULL; + _mouseLeftDown = false; + + bool handled = _state == GAME_RUNNING && DID_SUCCEED(applyEvent("LeftRelease")); + if (!handled) { + if (_activeObject != NULL) { + _activeObject->applyEvent("LeftRelease"); + } + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::onMouseLeftDblClick() { + if (_state == GAME_RUNNING && !_interactive) { + return STATUS_OK; + } + + if (_activeObject) { + _activeObject->handleMouse(MOUSE_DBLCLICK, MOUSE_BUTTON_LEFT); + } + + bool handled = _state == GAME_RUNNING && DID_SUCCEED(applyEvent("LeftDoubleClick")); + if (!handled) { + if (_activeObject != NULL) { + _activeObject->applyEvent("LeftDoubleClick"); + } + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::onMouseRightDblClick() { + if (_state == GAME_RUNNING && !_interactive) { + return STATUS_OK; + } + + if (_activeObject) { + _activeObject->handleMouse(MOUSE_DBLCLICK, MOUSE_BUTTON_RIGHT); + } + + bool handled = _state == GAME_RUNNING && DID_SUCCEED(applyEvent("RightDoubleClick")); + if (!handled) { + if (_activeObject != NULL) { + _activeObject->applyEvent("RightDoubleClick"); + } + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::onMouseRightDown() { + if (_activeObject) { + _activeObject->handleMouse(MOUSE_CLICK, MOUSE_BUTTON_RIGHT); + } + + bool handled = _state == GAME_RUNNING && DID_SUCCEED(applyEvent("RightClick")); + if (!handled) { + if (_activeObject != NULL) { + _activeObject->applyEvent("RightClick"); + } + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::onMouseRightUp() { + if (_activeObject) { + _activeObject->handleMouse(MOUSE_RELEASE, MOUSE_BUTTON_RIGHT); + } + + bool handled = _state == GAME_RUNNING && DID_SUCCEED(applyEvent("RightRelease")); + if (!handled) { + if (_activeObject != NULL) { + _activeObject->applyEvent("RightRelease"); + } + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::onMouseMiddleDown() { + if (_state == GAME_RUNNING && !_interactive) { + return STATUS_OK; + } + + if (_activeObject) { + _activeObject->handleMouse(MOUSE_CLICK, MOUSE_BUTTON_MIDDLE); + } + + bool handled = _state == GAME_RUNNING && DID_SUCCEED(applyEvent("MiddleClick")); + if (!handled) { + if (_activeObject != NULL) { + _activeObject->applyEvent("MiddleClick"); + } + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::onMouseMiddleUp() { + if (_activeObject) { + _activeObject->handleMouse(MOUSE_RELEASE, MOUSE_BUTTON_MIDDLE); + } + + bool handled = _state == GAME_RUNNING && DID_SUCCEED(applyEvent("MiddleRelease")); + if (!handled) { + if (_activeObject != NULL) { + _activeObject->applyEvent("MiddleRelease"); + } + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::onPaint() { + if (_renderer && _renderer->_windowed && _renderer->_ready) { + _renderer->initLoop(); + displayContent(false, true); + displayDebugInfo(); + _renderer->windowedBlt(); + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::onWindowClose() { + if (canHandleEvent("QuitGame")) { + if (_state != GAME_FROZEN) { + _gameRef->applyEvent("QuitGame"); + } + return STATUS_OK; + } else { + return STATUS_FAILED; + } +} + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::displayDebugInfo() { + char str[100]; + + if (_debugShowFPS) { + sprintf(str, "FPS: %d", _gameRef->_fps); + _systemFont->drawText((byte *)str, 0, 0, 100, TAL_LEFT); + } + + if (_gameRef->_debugDebugMode) { + if (!_gameRef->_renderer->_windowed) { + sprintf(str, "Mode: %dx%dx%d", _renderer->_width, _renderer->_height, _renderer->_bPP); + } else { + sprintf(str, "Mode: %dx%d windowed", _renderer->_width, _renderer->_height); + } + + strcat(str, " ("); + strcat(str, _renderer->getName().c_str()); + strcat(str, ")"); + _systemFont->drawText((byte *)str, 0, 0, _renderer->_width, TAL_RIGHT); + + _renderer->displayDebugInfo(); + + int scrTotal, scrRunning, scrWaiting, scrPersistent; + scrTotal = _scEngine->getNumScripts(&scrRunning, &scrWaiting, &scrPersistent); + sprintf(str, "Running scripts: %d (r:%d w:%d p:%d)", scrTotal, scrRunning, scrWaiting, scrPersistent); + _systemFont->drawText((byte *)str, 0, 70, _renderer->_width, TAL_RIGHT); + + + sprintf(str, "Timer: %d", _timer); + _gameRef->_systemFont->drawText((byte *)str, 0, 130, _renderer->_width, TAL_RIGHT); + + if (_activeObject != NULL) { + _systemFont->drawText((const byte *)_activeObject->getName(), 0, 150, _renderer->_width, TAL_RIGHT); + } + + sprintf(str, "GfxMem: %dMB", _usedMem / (1024 * 1024)); + _systemFont->drawText((byte *)str, 0, 170, _renderer->_width, TAL_RIGHT); + + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +void BaseGame::getMousePos(Point32 *pos) { + BasePlatform::getCursorPos(pos); + + pos->x -= _renderer->_drawOffsetX; + pos->y -= _renderer->_drawOffsetY; + + /* + // Windows can squish maximized window if it's larger than desktop + // so we need to modify mouse position appropriately (tnx mRax) + if (_renderer->_windowed && ::IsZoomed(_renderer->_window)) { + Common::Rect rc; + ::GetClientRect(_renderer->_window, &rc); + Pos->x *= _gameRef->_renderer->_realWidth; + Pos->x /= (rc.right - rc.left); + Pos->y *= _gameRef->_renderer->_realHeight; + Pos->y /= (rc.bottom - rc.top); + } + */ + + if (_mouseLockRect.left != 0 && _mouseLockRect.right != 0 && _mouseLockRect.top != 0 && _mouseLockRect.bottom != 0) { + if (!BasePlatform::ptInRect(&_mouseLockRect, *pos)) { + pos->x = MAX(_mouseLockRect.left, pos->x); + pos->y = MAX(_mouseLockRect.top, pos->y); + + pos->x = MIN(_mouseLockRect.right, pos->x); + pos->y = MIN(_mouseLockRect.bottom, pos->y); + + Point32 newPos = *pos; + + newPos.x += _renderer->_drawOffsetX; + newPos.y += _renderer->_drawOffsetY; + + BasePlatform::setCursorPos(newPos.x, newPos.y); + } + } +} + +////////////////////////////////////////////////////////////////////////// +void BaseGame::miniUpdate() { // TODO: Is this really necessary, it used to update sound, but the mixer does that now. + if (!_miniUpdateEnabled) { + return; + } + + if (g_system->getMillis() - _lastMiniUpdate > 200) { + _lastMiniUpdate = g_system->getMillis(); + } +} + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::onScriptShutdown(ScScript *script) { + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::isLeftDoubleClick() { + return isDoubleClick(0); +} + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::isRightDoubleClick() { + return isDoubleClick(1); +} + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::isDoubleClick(int buttonIndex) { + uint32 maxDoubleCLickTime = 500; + int maxMoveX = 4; + int maxMoveY = 4; + + Point32 pos; + BasePlatform::getCursorPos(&pos); + + int moveX = abs(pos.x - _lastClick[buttonIndex].posX); + int moveY = abs(pos.y - _lastClick[buttonIndex].posY); + + + if (_lastClick[buttonIndex].time == 0 || g_system->getMillis() - _lastClick[buttonIndex].time > maxDoubleCLickTime || moveX > maxMoveX || moveY > maxMoveY) { + _lastClick[buttonIndex].time = g_system->getMillis(); + _lastClick[buttonIndex].posX = pos.x; + _lastClick[buttonIndex].posY = pos.y; + return false; + } else { + _lastClick[buttonIndex].time = 0; + return true; + } +} + +////////////////////////////////////////////////////////////////////////// +void BaseGame::autoSaveOnExit() { + _soundMgr->saveSettings(); + ConfMan.flushToDisk(); + + if (!_autoSaveOnExit) { + return; + } + if (_state == GAME_FROZEN) { + return; + } + + saveGame(_autoSaveSlot, "autosave", true); +} + +////////////////////////////////////////////////////////////////////////// +void BaseGame::addMem(int bytes) { + _usedMem += bytes; +} + +////////////////////////////////////////////////////////////////////////// +AnsiString BaseGame::getDeviceType() const { + return "computer"; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_game.h b/engines/wintermute/base/base_game.h new file mode 100644 index 0000000000..75cf3fd832 --- /dev/null +++ b/engines/wintermute/base/base_game.h @@ -0,0 +1,347 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_GAME_H +#define WINTERMUTE_BASE_GAME_H + +#include "engines/wintermute/base/base_object.h" +#include "engines/wintermute/persistent.h" +#include "engines/wintermute/coll_templ.h" +#include "engines/wintermute/math/rect32.h" +#include "common/events.h" + +namespace Wintermute { + +typedef void (*ENGINE_LOG_CALLBACK)(char *text, bool result, void *data); + +class BaseSoundMgr; +class BaseFader; +class BaseFont; +class BaseFileManager; +class BaseTransitionMgr; +class ScEngine; +class BaseFontStorage; +class BaseGameMusic; +class BaseStringTable; +class BaseQuickMsg; +class UIWindow; +class BaseViewport; +class BaseRenderer; +class BaseRegistry; +class BaseSaveThumbHelper; +class BaseSurfaceStorage; +class SXMath; +class BaseKeyboardState; +class VideoPlayer; +class VideoTheoraPlayer; + +class BaseGame: public BaseObject { +public: + DECLARE_PERSISTENT(BaseGame, BaseObject) + + virtual bool onScriptShutdown(ScScript *script); + + virtual bool onActivate(bool activate, bool refreshMouse); + virtual bool onMouseLeftDown(); + virtual bool onMouseLeftUp(); + virtual bool onMouseLeftDblClick(); + virtual bool onMouseRightDblClick(); + virtual bool onMouseRightDown(); + virtual bool onMouseRightUp(); + virtual bool onMouseMiddleDown(); + virtual bool onMouseMiddleUp(); + virtual bool onPaint(); + virtual bool onWindowClose(); + + bool isLeftDoubleClick(); + bool isRightDoubleClick(); + + bool _autorunDisabled; + + uint32 _lastMiniUpdate; + bool _miniUpdateEnabled; + + virtual void miniUpdate(); + + void getMousePos(Point32 *Pos); + Rect32 _mouseLockRect; + + bool _shuttingDown; + + virtual bool displayDebugInfo(); + bool _debugShowFPS; + + bool _suspendedRendering; + int _soundBufferSizeSec; + + TTextEncoding _textEncoding; + bool _textRTL; + + virtual bool resetContent(); + + void DEBUG_DumpClassRegistry(); + bool setWaitCursor(const char *filename); + + int _thumbnailWidth; + int _thumbnailHeight; + + bool _editorMode; + void getOffset(int *offsetX, int *offsetY); + void setOffset(int offsetX, int offsetY); + int getSequence(); + + int _offsetY; + int _offsetX; + float _offsetPercentX; + float _offsetPercentY; + BaseObject *_mainObject; + + bool initInput(); + bool initLoop(); + uint32 _currentTime; + uint32 _deltaTime; + BaseFont *_systemFont; + BaseFont *_videoFont; + bool initialize1(); + bool initialize2(); + bool initialize3(); + BaseTransitionMgr *_transMgr; + + void LOG(bool res, const char *fmt, ...); + + BaseRenderer *_renderer; + BaseSoundMgr *_soundMgr; + ScEngine *_scEngine; + BaseScriptable *_mathClass; + BaseSurfaceStorage *_surfaceStorage; + BaseFontStorage *_fontStorage; + BaseGame(const Common::String &gameId); + virtual ~BaseGame(); + + void DEBUG_DebugDisable(); + void DEBUG_DebugEnable(const char *filename = NULL); + bool _debugDebugMode; + + void *_debugLogFile; + int _sequence; + virtual bool loadFile(const char *filename); + virtual bool loadBuffer(byte *buffer, bool complete = true); + + int _viewportSP; + + BaseStringTable *_stringTable; + int _settingsResWidth; + int _settingsResHeight; + char *_settingsGameFile; + bool _suppressScriptErrors; + bool _mouseLeftDown; // TODO: Hide + + virtual bool externalCall(ScScript *script, ScStack *stack, ScStack *thisStack, char *name); + // scripting interface + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + virtual const char *scToString(); + // compatibility bits + bool _compatKillMethodThreads; + + const char* getGameId() { return _gameId.c_str(); } + void setGameId(const Common::String& gameId) { _gameId = gameId; } + uint32 _surfaceGCCycleTime; + bool _smartCache; // RO + bool _subtitles; // RO + + int _scheduledLoadSlot; + bool _loading; + + virtual bool handleMouseWheel(int delta); + bool _quitting; + virtual bool getVersion(byte *verMajor, byte *verMinor, byte *extMajor, byte *extMinor); + + virtual bool handleKeypress(Common::Event *event, bool printable = false); + virtual void handleKeyRelease(Common::Event *event); + + bool unfreeze(); + bool freeze(bool includingMusic = true); + bool focusWindow(UIWindow *window); + bool _loadInProgress; + UIWindow *_focusedWindow; + bool _editorForceScripts; + + static void invalidateValues(void *value, void *data); + + bool loadSettings(const char *filename); + + bool displayWindows(bool inGame = false); + bool _useD3D; + virtual bool cleanup(); + bool loadGame(int slot); + bool loadGame(const char *filename); + bool saveGame(int slot, const char *desc, bool quickSave = false); + virtual bool showCursor(); + + BaseObject *_activeObject; + + bool _interactive; + TGameState _state; + TGameState _origState; + bool _origInteractive; + uint32 _timer; + uint32 _timerDelta; + uint32 _timerLast; + + uint32 _liveTimer; + uint32 _liveTimerDelta; + uint32 _liveTimerLast; + + BaseObject *_capturedObject; + Point32 _mousePos; + bool validObject(BaseObject *object); + bool unregisterObject(BaseObject *object); + bool registerObject(BaseObject *object); + void quickMessage(const char *text); + void quickMessageForm(char *fmt, ...); + bool displayQuickMsg(); + + virtual bool displayContent(bool update = true, bool displayAll = false); + virtual bool displayContentSimple(); + bool _forceNonStreamedSounds; + void resetMousePos(); + int _subtitlesSpeed; + void setInteractive(bool state); + virtual bool windowLoadHook(UIWindow *win, char **buf, char **params); + virtual bool windowScriptMethodHook(UIWindow *win, ScScript *script, ScStack *stack, const char *name); + bool getCurrentViewportOffset(int *offsetX = NULL, int *offsetY = NULL); + bool getCurrentViewportRect(Rect32 *rect, bool *custom = NULL); + bool popViewport(); + bool pushViewport(BaseViewport *Viewport); + bool setActiveObject(BaseObject *Obj); + BaseSprite *_lastCursor; + bool drawCursor(BaseSprite *Cursor); + + BaseSaveThumbHelper *_cachedThumbnail; + void addMem(int bytes); + bool _touchInterface; + bool _constrainedMemory; +protected: + BaseSprite *_loadingIcon; + int _loadingIconX; + int _loadingIconY; + int _loadingIconPersistent; + + BaseFader *_fader; + + int _freezeLevel; + VideoPlayer *_videoPlayer; + VideoTheoraPlayer *_theoraPlayer; +private: + bool _mouseRightDown; + bool _mouseMidlleDown; + bool _settingsRequireAcceleration; + bool _settingsAllowWindowed; + bool _settingsAllowAdvanced; + bool _settingsAllowAccessTab; + bool _settingsAllowAboutTab; + bool _settingsRequireSound; + bool _settingsAllowDesktopRes; + int _settingsTLMode; + virtual bool invalidateDeviceObjects(); + virtual bool restoreDeviceObjects(); + + // TODO: This can probably be removed completely: + bool _saveDirChecked; + bool _richSavedGames; + Common::String _localSaveDir; + Common::String _savedGameExt; + + bool _reportTextureFormat; + + // FPS stuff + uint32 _lastTime; + uint32 _fpsTime; + uint32 _framesRendered; + Common::String _gameId; + + void setEngineLogCallback(ENGINE_LOG_CALLBACK callback = NULL, void *data = NULL); + ENGINE_LOG_CALLBACK _engineLogCallback; + void *_engineLogCallbackData; + + bool _videoSubtitles; + bool _compressedSavegames; + + bool _personalizedSave; + + void setWindowTitle(); + + BaseSprite *_cursorNoninteractive; + BaseKeyboardState *_keyboardState; + + uint32 _fps; + BaseGameMusic *_musicSystem; + + bool isVideoPlaying(); + bool stopVideo(); + + BaseArray<BaseQuickMsg *> _quickMessages; + BaseArray<UIWindow *> _windows; + BaseArray<BaseViewport *> _viewportStack; + BaseArray<BaseObject *> _regObjects; + + AnsiString getDeviceType() const; + + struct LastClickInfo { + LastClickInfo() { + posX = posY = 0; + time = 0; + } + + int posX; + int posY; + uint32 time; + }; + + LastClickInfo _lastClick[2]; + bool isDoubleClick(int buttonIndex); + uint32 _usedMem; + + + +protected: + // WME Lite specific + bool _autoSaveOnExit; + int _autoSaveSlot; + bool _cursorHidden; + +public: + void autoSaveOnExit(); + +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_game_music.cpp b/engines/wintermute/base/base_game_music.cpp new file mode 100644 index 0000000000..365fa6e212 --- /dev/null +++ b/engines/wintermute/base/base_game_music.cpp @@ -0,0 +1,506 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_game_music.h" +#include "engines/wintermute/base/base_engine.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_persistence_manager.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/base/sound/base_sound.h" + +namespace Wintermute { + +BaseGameMusic::BaseGameMusic(BaseGame *gameRef) : _gameRef(gameRef) { + for (int i = 0; i < NUM_MUSIC_CHANNELS; i++) { + _music[i] = NULL; + _musicStartTime[i] = 0; + } + + _musicCrossfadeRunning = false; + _musicCrossfadeStartTime = 0; + _musicCrossfadeLength = 0; + _musicCrossfadeChannel1 = -1; + _musicCrossfadeChannel2 = -1; + _musicCrossfadeSwap = false; +} + +void BaseGameMusic::cleanup() { + for (int i = 0; i < NUM_MUSIC_CHANNELS; i++) { + delete _music[i]; + _music[i] = NULL; + _musicStartTime[i] = 0; + } +} + +////////////////////////////////////////////////////////////////////////// +bool BaseGameMusic::playMusic(int channel, const char *filename, bool looping, uint32 loopStart) { + if (channel >= NUM_MUSIC_CHANNELS) { + BaseEngine::LOG(0, "**Error** Attempting to use music channel %d (max num channels: %d)", channel, NUM_MUSIC_CHANNELS); + return STATUS_FAILED; + } + + delete _music[channel]; + _music[channel] = NULL; + + _music[channel] = new BaseSound(_gameRef); + if (_music[channel] && DID_SUCCEED(_music[channel]->setSound(filename, Audio::Mixer::kMusicSoundType, true))) { + if (_musicStartTime[channel]) { + _music[channel]->setPositionTime(_musicStartTime[channel]); + _musicStartTime[channel] = 0; + } + if (loopStart) { + _music[channel]->setLoopStart(loopStart); + } + return _music[channel]->play(looping); + } else { + delete _music[channel]; + _music[channel] = NULL; + return STATUS_FAILED; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGameMusic::stopMusic(int channel) { + if (channel >= NUM_MUSIC_CHANNELS) { + BaseEngine::LOG(0, "**Error** Attempting to use music channel %d (max num channels: %d)", channel, NUM_MUSIC_CHANNELS); + return STATUS_FAILED; + } + + if (_music[channel]) { + _music[channel]->stop(); + delete _music[channel]; + _music[channel] = NULL; + return STATUS_OK; + } else { + return STATUS_FAILED; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGameMusic::pauseMusic(int channel) { + if (channel >= NUM_MUSIC_CHANNELS) { + BaseEngine::LOG(0, "**Error** Attempting to use music channel %d (max num channels: %d)", channel, NUM_MUSIC_CHANNELS); + return STATUS_FAILED; + } + + if (_music[channel]) { + return _music[channel]->pause(); + } else { + return STATUS_FAILED; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGameMusic::resumeMusic(int channel) { + if (channel >= NUM_MUSIC_CHANNELS) { + BaseEngine::LOG(0, "**Error** Attempting to use music channel %d (max num channels: %d)", channel, NUM_MUSIC_CHANNELS); + return STATUS_FAILED; + } + + if (_music[channel]) { + return _music[channel]->resume(); + } else { + return STATUS_FAILED; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGameMusic::setMusicStartTime(int channel, uint32 time) { + if (channel >= NUM_MUSIC_CHANNELS) { + BaseEngine::LOG(0, "**Error** Attempting to use music channel %d (max num channels: %d)", channel, NUM_MUSIC_CHANNELS); + return STATUS_FAILED; + } + + _musicStartTime[channel] = time; + if (_music[channel] && _music[channel]->isPlaying()) { + return _music[channel]->setPositionTime(time); + } else { + return STATUS_OK; + } +} + +////////////////////////////////////////////////////////////////////////// +bool BaseGameMusic::updateMusicCrossfade() { + /* byte globMusicVol = _soundMgr->getVolumePercent(SOUND_MUSIC); */ + + if (!_musicCrossfadeRunning) { + return STATUS_OK; + } + if (_gameRef->_state == GAME_FROZEN) { + return STATUS_OK; + } + + if (_musicCrossfadeChannel1 < 0 || _musicCrossfadeChannel1 >= NUM_MUSIC_CHANNELS || !_music[_musicCrossfadeChannel1]) { + _musicCrossfadeRunning = false; + return STATUS_OK; + } + if (_musicCrossfadeChannel2 < 0 || _musicCrossfadeChannel2 >= NUM_MUSIC_CHANNELS || !_music[_musicCrossfadeChannel2]) { + _musicCrossfadeRunning = false; + return STATUS_OK; + } + + if (!_music[_musicCrossfadeChannel1]->isPlaying()) { + _music[_musicCrossfadeChannel1]->play(); + } + if (!_music[_musicCrossfadeChannel2]->isPlaying()) { + _music[_musicCrossfadeChannel2]->play(); + } + + uint32 currentTime = _gameRef->_liveTimer - _musicCrossfadeStartTime; + + if (currentTime >= _musicCrossfadeLength) { + _musicCrossfadeRunning = false; + //_music[_musicCrossfadeChannel2]->setVolume(GlobMusicVol); + _music[_musicCrossfadeChannel2]->setVolumePercent(100); + + _music[_musicCrossfadeChannel1]->stop(); + //_music[_musicCrossfadeChannel1]->setVolume(GlobMusicVol); + _music[_musicCrossfadeChannel1]->setVolumePercent(100); + + + if (_musicCrossfadeSwap) { + // swap channels + BaseSound *dummy = _music[_musicCrossfadeChannel1]; + int dummyInt = _musicStartTime[_musicCrossfadeChannel1]; + + _music[_musicCrossfadeChannel1] = _music[_musicCrossfadeChannel2]; + _musicStartTime[_musicCrossfadeChannel1] = _musicStartTime[_musicCrossfadeChannel2]; + + _music[_musicCrossfadeChannel2] = dummy; + _musicStartTime[_musicCrossfadeChannel2] = dummyInt; + } + } else { + //_music[_musicCrossfadeChannel1]->setVolume(GlobMusicVol - (float)CurrentTime / (float)_musicCrossfadeLength * GlobMusicVol); + //_music[_musicCrossfadeChannel2]->setVolume((float)CurrentTime / (float)_musicCrossfadeLength * GlobMusicVol); + _music[_musicCrossfadeChannel1]->setVolumePercent((int)(100.0f - (float)currentTime / (float)_musicCrossfadeLength * 100.0f)); + _music[_musicCrossfadeChannel2]->setVolumePercent((int)((float)currentTime / (float)_musicCrossfadeLength * 100.0f)); + + //_gameRef->QuickMessageForm("%d %d", _music[_musicCrossfadeChannel1]->GetVolume(), _music[_musicCrossfadeChannel2]->GetVolume()); + } + + return STATUS_OK; +} + +bool BaseGameMusic::persistChannels(BasePersistenceManager *persistMgr) { + for (int i = 0; i < NUM_MUSIC_CHANNELS; i++) { + persistMgr->transfer(TMEMBER(_music[i])); + persistMgr->transfer(TMEMBER(_musicStartTime[i])); + } + return true; +} + +bool BaseGameMusic::persistCrossfadeSettings(BasePersistenceManager *persistMgr) { + persistMgr->transfer(TMEMBER(_musicCrossfadeRunning)); + persistMgr->transfer(TMEMBER(_musicCrossfadeStartTime)); + persistMgr->transfer(TMEMBER(_musicCrossfadeLength)); + persistMgr->transfer(TMEMBER(_musicCrossfadeChannel1)); + persistMgr->transfer(TMEMBER(_musicCrossfadeChannel2)); + persistMgr->transfer(TMEMBER(_musicCrossfadeSwap)); + return true; +} + +bool BaseGameMusic::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // PlayMusic / PlayMusicChannel + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "PlayMusic") == 0 || strcmp(name, "PlayMusicChannel") == 0) { + int channel = 0; + if (strcmp(name, "PlayMusic") == 0) { + stack->correctParams(3); + } else { + stack->correctParams(4); + channel = stack->pop()->getInt(); + } + + const char *filename = stack->pop()->getString(); + ScValue *valLooping = stack->pop(); + bool looping = valLooping->isNULL() ? true : valLooping->getBool(); + + ScValue *valLoopStart = stack->pop(); + uint32 loopStart = (uint32)(valLoopStart->isNULL() ? 0 : valLoopStart->getInt()); + + + if (DID_FAIL(playMusic(channel, filename, looping, loopStart))) { + stack->pushBool(false); + } else { + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // StopMusic / StopMusicChannel + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "StopMusic") == 0 || strcmp(name, "StopMusicChannel") == 0) { + int channel = 0; + + if (strcmp(name, "StopMusic") == 0) { + stack->correctParams(0); + } else { + stack->correctParams(1); + channel = stack->pop()->getInt(); + } + + if (DID_FAIL(stopMusic(channel))) { + stack->pushBool(false); + } else { + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // PauseMusic / PauseMusicChannel + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "PauseMusic") == 0 || strcmp(name, "PauseMusicChannel") == 0) { + int channel = 0; + + if (strcmp(name, "PauseMusic") == 0) { + stack->correctParams(0); + } else { + stack->correctParams(1); + channel = stack->pop()->getInt(); + } + + if (DID_FAIL(pauseMusic(channel))) { + stack->pushBool(false); + } else { + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ResumeMusic / ResumeMusicChannel + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ResumeMusic") == 0 || strcmp(name, "ResumeMusicChannel") == 0) { + int channel = 0; + if (strcmp(name, "ResumeMusic") == 0) { + stack->correctParams(0); + } else { + stack->correctParams(1); + channel = stack->pop()->getInt(); + } + + if (DID_FAIL(resumeMusic(channel))) { + stack->pushBool(false); + } else { + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetMusic / GetMusicChannel + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetMusic") == 0 || strcmp(name, "GetMusicChannel") == 0) { + int channel = 0; + if (strcmp(name, "GetMusic") == 0) { + stack->correctParams(0); + } else { + stack->correctParams(1); + channel = stack->pop()->getInt(); + } + if (channel < 0 || channel >= NUM_MUSIC_CHANNELS) { + stack->pushNULL(); + } else { + if (!_music[channel] || !_music[channel]->getFilename()) { + stack->pushNULL(); + } else { + stack->pushString(_music[channel]->getFilename()); + } + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetMusicPosition / SetMusicChannelPosition + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetMusicPosition") == 0 || strcmp(name, "SetMusicChannelPosition") == 0 || strcmp(name, "SetMusicPositionChannel") == 0) { + int channel = 0; + if (strcmp(name, "SetMusicPosition") == 0) { + stack->correctParams(1); + } else { + stack->correctParams(2); + channel = stack->pop()->getInt(); + } + + uint32 time = stack->pop()->getInt(); + + if (DID_FAIL(setMusicStartTime(channel, time))) { + stack->pushBool(false); + } else { + stack->pushBool(true); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetMusicPosition / GetMusicChannelPosition + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetMusicPosition") == 0 || strcmp(name, "GetMusicChannelPosition") == 0) { + int channel = 0; + if (strcmp(name, "GetMusicPosition") == 0) { + stack->correctParams(0); + } else { + stack->correctParams(1); + channel = stack->pop()->getInt(); + } + + if (channel < 0 || channel >= NUM_MUSIC_CHANNELS || !_music[channel]) { + stack->pushInt(0); + } else { + stack->pushInt(_music[channel]->getPositionTime()); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // IsMusicPlaying / IsMusicChannelPlaying + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "IsMusicPlaying") == 0 || strcmp(name, "IsMusicChannelPlaying") == 0) { + int channel = 0; + if (strcmp(name, "IsMusicPlaying") == 0) { + stack->correctParams(0); + } else { + stack->correctParams(1); + channel = stack->pop()->getInt(); + } + + if (channel < 0 || channel >= NUM_MUSIC_CHANNELS || !_music[channel]) { + stack->pushBool(false); + } else { + stack->pushBool(_music[channel]->isPlaying()); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetMusicVolume / SetMusicChannelVolume + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetMusicVolume") == 0 || strcmp(name, "SetMusicChannelVolume") == 0) { + int channel = 0; + if (strcmp(name, "SetMusicVolume") == 0) { + stack->correctParams(1); + } else { + stack->correctParams(2); + channel = stack->pop()->getInt(); + } + + int volume = stack->pop()->getInt(); + if (channel < 0 || channel >= NUM_MUSIC_CHANNELS || !_music[channel]) { + stack->pushBool(false); + } else { + if (DID_FAIL(_music[channel]->setVolumePercent(volume))) { + stack->pushBool(false); + } else { + stack->pushBool(true); + } + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetMusicVolume / GetMusicChannelVolume + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetMusicVolume") == 0 || strcmp(name, "GetMusicChannelVolume") == 0) { + int channel = 0; + if (strcmp(name, "GetMusicVolume") == 0) { + stack->correctParams(0); + } else { + stack->correctParams(1); + channel = stack->pop()->getInt(); + } + + if (channel < 0 || channel >= NUM_MUSIC_CHANNELS || !_music[channel]) { + stack->pushInt(0); + } else { + stack->pushInt(_music[channel]->getVolumePercent()); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // MusicCrossfade + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "MusicCrossfade") == 0) { + stack->correctParams(4); + int channel1 = stack->pop()->getInt(0); + int channel2 = stack->pop()->getInt(0); + uint32 fadeLength = (uint32)stack->pop()->getInt(0); + bool swap = stack->pop()->getBool(true); + + if (_musicCrossfadeRunning) { + script->runtimeError("Game.MusicCrossfade: Music crossfade is already in progress."); + stack->pushBool(false); + return STATUS_OK; + } + + _musicCrossfadeStartTime = _gameRef->_liveTimer; + _musicCrossfadeChannel1 = channel1; + _musicCrossfadeChannel2 = channel2; + _musicCrossfadeLength = fadeLength; + _musicCrossfadeSwap = swap; + + _musicCrossfadeRunning = true; + + stack->pushBool(true); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetSoundLength + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetSoundLength") == 0) { + stack->correctParams(1); + + int length = 0; + const char *filename = stack->pop()->getString(); + + BaseSound *sound = new BaseSound(_gameRef); + if (sound && DID_SUCCEED(sound->setSound(filename, Audio::Mixer::kMusicSoundType, true))) { + length = sound->getLength(); + delete sound; + sound = NULL; + } + stack->pushInt(length); + return STATUS_OK; + } else { + return STATUS_FAILED; + } +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_game_music.h b/engines/wintermute/base/base_game_music.h new file mode 100644 index 0000000000..058a8cadef --- /dev/null +++ b/engines/wintermute/base/base_game_music.h @@ -0,0 +1,73 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_GAME_MUSIC_H +#define WINTERMUTE_BASE_GAME_MUSIC_H + +#include "common/scummsys.h" + + +namespace Wintermute { + +#define NUM_MUSIC_CHANNELS 5 +class BasePersistenceManager; +class BaseSound; +class ScStack; +class ScScript; +class BaseGame; +class BaseGameMusic { +public: + BaseGameMusic(BaseGame *gameRef); + void cleanup(); + + bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + + bool resumeMusic(int channel); + bool setMusicStartTime(int channel, uint32 time); + bool pauseMusic(int channel); + bool stopMusic(int channel); + bool playMusic(int channel, const char *filename, bool looping = true, uint32 loopStart = 0); + bool updateMusicCrossfade(); + + bool persistChannels(BasePersistenceManager *persistMgr); + bool persistCrossfadeSettings(BasePersistenceManager *persistMgr); +private: + BaseGame *_gameRef; + BaseSound *_music[NUM_MUSIC_CHANNELS]; + uint32 _musicStartTime[NUM_MUSIC_CHANNELS]; + bool _musicCrossfadeRunning; + bool _musicCrossfadeSwap; + uint32 _musicCrossfadeStartTime; + uint32 _musicCrossfadeLength; + int _musicCrossfadeChannel1; + int _musicCrossfadeChannel2; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_keyboard_state.cpp b/engines/wintermute/base/base_keyboard_state.cpp new file mode 100644 index 0000000000..072a1bb71b --- /dev/null +++ b/engines/wintermute/base/base_keyboard_state.cpp @@ -0,0 +1,313 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_keyboard_state.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "common/system.h" +#include "common/keyboard.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(BaseKeyboardState, false) + +////////////////////////////////////////////////////////////////////////// +BaseKeyboardState::BaseKeyboardState(BaseGame *inGame) : BaseScriptable(inGame) { + _currentPrintable = false; + _currentCharCode = 0; + _currentKeyData = 0; + + _currentShift = false; + _currentAlt = false; + _currentControl = false; + + _keyStates = new uint8[323]; // Hardcoded size for the common/keyboard.h enum + for (int i = 0; i < 323; i++) { + _keyStates[i] = false; + } +} + +////////////////////////////////////////////////////////////////////////// +BaseKeyboardState::~BaseKeyboardState() { + delete[] _keyStates; +} + +void BaseKeyboardState::handleKeyPress(Common::Event *event) { + if (event->type == Common::EVENT_KEYDOWN) { + _keyStates[event->kbd.keycode] = true; + } +} + +void BaseKeyboardState::handleKeyRelease(Common::Event *event) { + if (event->type == Common::EVENT_KEYUP) { + _keyStates[event->kbd.keycode] = false; + } +} + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool BaseKeyboardState::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // IsKeyDown + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "IsKeyDown") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + int vKey; + + if (val->_type == VAL_STRING && strlen(val->getString()) > 0) { + const char *str = val->getString(); + char temp = str[0]; + if (temp >= 'A' && temp <= 'Z') { + temp += ('a' - 'A'); + } + vKey = (int)temp; + } else { + vKey = val->getInt(); + } + + bool isDown = _keyStates[vKeyToKeyCode(vKey)]; + + stack->pushBool(isDown); + return STATUS_OK; + } else { + return BaseScriptable::scCallMethod(script, stack, thisStack, name); + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *BaseKeyboardState::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("keyboard"); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Key + ////////////////////////////////////////////////////////////////////////// + else if (name == "Key") { + if (_currentPrintable) { + char key[2]; + key[0] = (char)_currentCharCode; + key[1] = '\0'; + _scValue->setString(key); + } else { + _scValue->setString(""); + } + + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Printable + ////////////////////////////////////////////////////////////////////////// + else if (name == "Printable") { + _scValue->setBool(_currentPrintable); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // KeyCode + ////////////////////////////////////////////////////////////////////////// + else if (name == "KeyCode") { + _scValue->setInt(_currentCharCode); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // IsShift + ////////////////////////////////////////////////////////////////////////// + else if (name == "IsShift") { + _scValue->setBool(_currentShift); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // IsAlt + ////////////////////////////////////////////////////////////////////////// + else if (name == "IsAlt") { + _scValue->setBool(_currentAlt); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // IsControl + ////////////////////////////////////////////////////////////////////////// + else if (name == "IsControl") { + _scValue->setBool(_currentControl); + return _scValue; + } else { + return BaseScriptable::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseKeyboardState::scSetProperty(const char *name, ScValue *value) { + /* + ////////////////////////////////////////////////////////////////////////// + // Name + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Name") == 0) { + setName(value->getString()); + if (_renderer) SetWindowText(_renderer->_window, _name); + return STATUS_OK; + } + + else*/ return BaseScriptable::scSetProperty(name, value); +} + + +////////////////////////////////////////////////////////////////////////// +const char *BaseKeyboardState::scToString() { + return "[keyboard state]"; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseKeyboardState::readKey(Common::Event *event) { + //_currentPrintable = (event->type == SDL_TEXTINPUT); // TODO + _currentCharCode = keyCodeToVKey(event); + // Verify that this is a printable ISO-8859-character (including the upper charset) + if ((_currentCharCode <= 0x7E && _currentCharCode >= 0x20) || (_currentCharCode <= 0xFF && _currentCharCode >= 0xA0)) { + _currentPrintable = true; + } else { + _currentPrintable = false; + } + //_currentKeyData = KeyData; + + _currentControl = isControlDown(); + _currentAlt = isAltDown(); + _currentShift = isShiftDown(); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseKeyboardState::persist(BasePersistenceManager *persistMgr) { + //if (!persistMgr->getIsSaving()) cleanup(); + BaseScriptable::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_currentAlt)); + persistMgr->transfer(TMEMBER(_currentCharCode)); + persistMgr->transfer(TMEMBER(_currentControl)); + persistMgr->transfer(TMEMBER(_currentKeyData)); + persistMgr->transfer(TMEMBER(_currentPrintable)); + persistMgr->transfer(TMEMBER(_currentShift)); + + if (!persistMgr->getIsSaving()) { + _keyStates = new uint8[323]; // Hardcoded size for the common/keyboard.h enum + for (int i = 0; i < 323; i++) { + _keyStates[i] = false; + } + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseKeyboardState::isShiftDown() { + int mod = g_system->getEventManager()->getModifierState(); + return (mod & Common::KBD_SHIFT); +} + +////////////////////////////////////////////////////////////////////////// +bool BaseKeyboardState::isControlDown() { + int mod = g_system->getEventManager()->getModifierState(); + return (mod & Common::KBD_CTRL); +} + +////////////////////////////////////////////////////////////////////////// +bool BaseKeyboardState::isAltDown() { + int mod = g_system->getEventManager()->getModifierState(); + return (mod & Common::KBD_ALT); +} + +////////////////////////////////////////////////////////////////////////// +bool BaseKeyboardState::isCurrentPrintable() const { + return _currentPrintable; +} + +////////////////////////////////////////////////////////////////////////// +uint32 BaseKeyboardState::keyCodeToVKey(Common::Event *event) { + if (event->type != Common::EVENT_KEYDOWN) { + return 0; + } + + switch (event->kbd.keycode) { + case Common::KEYCODE_KP_ENTER: + return Common::KEYCODE_RETURN; + default: + return (uint32)event->kbd.ascii; + } +} + +enum VKeyCodes { + kVkSpace = 32, + kVkLeft = 37, + kVkUp = 38, + kVkRight = 39, + kVkDown = 40 +}; + +////////////////////////////////////////////////////////////////////////// +Common::KeyCode BaseKeyboardState::vKeyToKeyCode(uint32 vkey) { + // todo + switch (vkey) { + case kVkSpace: + return Common::KEYCODE_SPACE; + break; + case kVkLeft: + return Common::KEYCODE_LEFT; + break; + case kVkRight: + return Common::KEYCODE_RIGHT; + break; + case kVkUp: + return Common::KEYCODE_UP; + break; + case kVkDown: + return Common::KEYCODE_DOWN; + break; + default: + warning("Unknown VKEY: %d", vkey); + return (Common::KeyCode)vkey; + break; + } + +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_keyboard_state.h b/engines/wintermute/base/base_keyboard_state.h new file mode 100644 index 0000000000..b62ece02b7 --- /dev/null +++ b/engines/wintermute/base/base_keyboard_state.h @@ -0,0 +1,76 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_KEYBOARD_STATE_H +#define WINTERMUTE_BASE_KEYBOARD_STATE_H + + +#include "engines/wintermute/base/base.h" +#include "engines/wintermute/base/base_scriptable.h" +#include "common/keyboard.h" +#include "common/events.h" + +namespace Wintermute { + +class BaseKeyboardState : public BaseScriptable { +public: + DECLARE_PERSISTENT(BaseKeyboardState, BaseScriptable) + BaseKeyboardState(BaseGame *inGame); + virtual ~BaseKeyboardState(); + bool readKey(Common::Event *event); + + void handleKeyPress(Common::Event *event); + void handleKeyRelease(Common::Event *event); + static bool isShiftDown(); + static bool isControlDown(); + static bool isAltDown(); + bool isCurrentPrintable() const; + + // scripting interface + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + virtual const char *scToString(); + +private: + bool _currentPrintable; + uint32 _currentKeyData; + uint32 _currentCharCode; + + bool _currentShift; + bool _currentAlt; + bool _currentControl; + + uint8 *_keyStates; + uint32 keyCodeToVKey(Common::Event *event); + Common::KeyCode vKeyToKeyCode(uint32 vkey); //TODO, reimplement using ScummVM-backend +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_named_object.cpp b/engines/wintermute/base/base_named_object.cpp new file mode 100644 index 0000000000..915bf24d7f --- /dev/null +++ b/engines/wintermute/base/base_named_object.cpp @@ -0,0 +1,71 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_named_object.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////////// +BaseNamedObject::BaseNamedObject(BaseGame *inGame) : BaseClass(inGame) { + _name = NULL; +} + +////////////////////////////////////////////////////////////////////////// +BaseNamedObject::BaseNamedObject() : BaseClass() { + _name = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +BaseNamedObject::BaseNamedObject(TDynamicConstructor, TDynamicConstructor) { + _name = NULL; +} + +////////////////////////////////////////////////////////////////////////// +BaseNamedObject::~BaseNamedObject(void) { + delete[] _name; + _name = NULL; +} + + +////////////////////////////////////////////////////////////////////// +void BaseNamedObject::setName(const char *name) { + delete[] _name; + _name = NULL; + + if (name == NULL) { + return; + } + + _name = new char [strlen(name) + 1]; + if (_name != NULL) { + strcpy(_name, name); + } +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_named_object.h b/engines/wintermute/base/base_named_object.h new file mode 100644 index 0000000000..77a00cee45 --- /dev/null +++ b/engines/wintermute/base/base_named_object.h @@ -0,0 +1,51 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_NAMED_OBJECT_H +#define WINTERMUTE_BASE_NAMED_OBJECT_H + + +#include "engines/wintermute/base/base.h" + +namespace Wintermute { + +class BaseNamedObject : public BaseClass { + char *_name; +public: + BaseNamedObject(BaseGame *inGame); + BaseNamedObject(); + virtual ~BaseNamedObject(void); + BaseNamedObject(TDynamicConstructor, TDynamicConstructor); + + const char *getName() { return _name; } + void setName(const char *name); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_object.cpp b/engines/wintermute/base/base_object.cpp new file mode 100644 index 0000000000..eba8416485 --- /dev/null +++ b/engines/wintermute/base/base_object.cpp @@ -0,0 +1,1246 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_object.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/base/sound/base_sound.h" +#include "engines/wintermute/base/sound/base_sound_manager.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_string_table.h" +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/platform_osystem.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(BaseObject, false) + +////////////////////////////////////////////////////////////////////// +BaseObject::BaseObject(BaseGame *inGame) : BaseScriptHolder(inGame) { + _posX = _posY = 0; + _movable = true; + _zoomable = true; + _registrable = true; + _shadowable = true; + _rotatable = false; + _is3D = false; + + _alphaColor = 0; + _scale = -1; + _relativeScale = 0; + + _scaleX = -1; + _scaleY = -1; + + _ready = true; + + _soundEvent = NULL; + + _iD = _gameRef->getSequence(); + + BasePlatform::setRectEmpty(&_rect); + _rectSet = false; + + _cursor = NULL; + _activeCursor = NULL; + _sharedCursors = false; + + _sFX = NULL; + _sFXStart = 0; + _sFXVolume = 100; + _autoSoundPanning = true; + + _editorAlwaysRegister = false; + _editorSelected = false; + + _editorOnly = false; + + _rotate = 0.0f; + _rotateValid = false; + _relativeRotate = 0.0f; + + for (int i = 0; i < 7; i++) { + _caption[i] = NULL; + } + _saveState = true; + + _nonIntMouseEvents = false; + + // sound FX + _sFXType = SFX_NONE; + _sFXParam1 = _sFXParam2 = _sFXParam3 = _sFXParam4 = 0; + + _blendMode = BLEND_NORMAL; +} + + +////////////////////////////////////////////////////////////////////// +BaseObject::~BaseObject() { + cleanup(); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseObject::cleanup() { + if (_gameRef && _gameRef->_activeObject == this) { + _gameRef->_activeObject = NULL; + } + + BaseScriptHolder::cleanup(); + delete[] _soundEvent; + _soundEvent = NULL; + + if (!_sharedCursors) { + delete _cursor; + delete _activeCursor; + _cursor = NULL; + _activeCursor = NULL; + } + delete _sFX; + _sFX = NULL; + + for (int i = 0; i < 7; i++) { + delete[] _caption[i]; + _caption[i] = NULL; + } + + _sFXType = SFX_NONE; + _sFXParam1 = _sFXParam2 = _sFXParam3 = _sFXParam4 = 0; + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +void BaseObject::setCaption(const char *caption, int caseVal) { + if (caseVal == 0) { + caseVal = 1; + } + if (caseVal < 1 || caseVal > 7) { + return; + } + + delete[] _caption[caseVal - 1]; + _caption[caseVal - 1] = new char[strlen(caption) + 1]; + if (_caption[caseVal - 1]) { + strcpy(_caption[caseVal - 1], caption); + _gameRef->_stringTable->expand(&_caption[caseVal - 1]); + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *BaseObject::getCaption(int caseVal) { + if (caseVal == 0) { + caseVal = 1; + } + if (caseVal < 1 || caseVal > 7 || _caption[caseVal - 1] == NULL) { + return ""; + } else { + return _caption[caseVal - 1]; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseObject::listen(BaseScriptHolder *param1, uint32 param2) { + return STATUS_FAILED; +} + + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool BaseObject::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + + ////////////////////////////////////////////////////////////////////////// + // SkipTo + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "SkipTo") == 0) { + stack->correctParams(2); + _posX = stack->pop()->getInt(); + _posY = stack->pop()->getInt(); + afterMove(); + stack->pushNULL(); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Caption + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Caption") == 0) { + stack->correctParams(1); + stack->pushString(getCaption(stack->pop()->getInt())); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetCursor + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetCursor") == 0) { + stack->correctParams(1); + if (DID_SUCCEED(setCursor(stack->pop()->getString()))) { + stack->pushBool(true); + } else { + stack->pushBool(false); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // RemoveCursor + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "RemoveCursor") == 0) { + stack->correctParams(0); + if (!_sharedCursors) { + delete _cursor; + _cursor = NULL; + } else { + _cursor = NULL; + + } + stack->pushNULL(); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetCursor + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetCursor") == 0) { + stack->correctParams(0); + if (!_cursor || !_cursor->getFilename()) { + stack->pushNULL(); + } else { + stack->pushString(_cursor->getFilename()); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetCursorObject + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetCursorObject") == 0) { + stack->correctParams(0); + if (!_cursor) { + stack->pushNULL(); + } else { + stack->pushNative(_cursor, true); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // HasCursor + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "HasCursor") == 0) { + stack->correctParams(0); + + if (_cursor) { + stack->pushBool(true); + } else { + stack->pushBool(false); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetCaption + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetCaption") == 0) { + stack->correctParams(2); + setCaption(stack->pop()->getString(), stack->pop()->getInt()); + stack->pushNULL(); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // LoadSound + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "LoadSound") == 0) { + stack->correctParams(1); + const char *filename = stack->pop()->getString(); + if (DID_SUCCEED(playSFX(filename, false, false))) { + stack->pushBool(true); + } else { + stack->pushBool(false); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // PlaySound + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "PlaySound") == 0) { + stack->correctParams(3); + + const char *filename; + bool looping; + uint32 loopStart; + + ScValue *val1 = stack->pop(); + ScValue *val2 = stack->pop(); + ScValue *val3 = stack->pop(); + + if (val1->_type == VAL_BOOL) { + filename = NULL; + looping = val1->getBool(); + loopStart = val2->getInt(); + } else { + if (val1->isNULL()) { + filename = NULL; + } else { + filename = val1->getString(); + } + looping = val2->isNULL() ? false : val2->getBool(); + loopStart = val3->getInt(); + } + + if (DID_FAIL(playSFX(filename, looping, true, NULL, loopStart))) { + stack->pushBool(false); + } else { + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // PlaySoundEvent + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "PlaySoundEvent") == 0) { + stack->correctParams(2); + + const char *filename; + const char *eventName; + + ScValue *val1 = stack->pop(); + ScValue *val2 = stack->pop(); + + if (val2->isNULL()) { + filename = NULL; + eventName = val1->getString(); + } else { + filename = val1->getString(); + eventName = val2->getString(); + } + + if (DID_FAIL(playSFX(filename, false, true, eventName))) { + stack->pushBool(false); + } else { + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // StopSound + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "StopSound") == 0) { + stack->correctParams(0); + + if (DID_FAIL(stopSFX())) { + stack->pushBool(false); + } else { + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // PauseSound + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "PauseSound") == 0) { + stack->correctParams(0); + + if (DID_FAIL(pauseSFX())) { + stack->pushBool(false); + } else { + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ResumeSound + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ResumeSound") == 0) { + stack->correctParams(0); + + if (DID_FAIL(resumeSFX())) { + stack->pushBool(false); + } else { + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // IsSoundPlaying + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "IsSoundPlaying") == 0) { + stack->correctParams(0); + + if (_sFX && _sFX->isPlaying()) { + stack->pushBool(true); + } else { + stack->pushBool(false); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetSoundPosition + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetSoundPosition") == 0) { + stack->correctParams(1); + + uint32 time = stack->pop()->getInt(); + if (DID_FAIL(setSFXTime(time))) { + stack->pushBool(false); + } else { + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetSoundPosition + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetSoundPosition") == 0) { + stack->correctParams(0); + + if (!_sFX) { + stack->pushInt(0); + } else { + stack->pushInt(_sFX->getPositionTime()); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetSoundVolume + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetSoundVolume") == 0) { + stack->correctParams(1); + + int volume = stack->pop()->getInt(); + if (DID_FAIL(setSFXVolume(volume))) { + stack->pushBool(false); + } else { + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetSoundVolume + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetSoundVolume") == 0) { + stack->correctParams(0); + + if (!_sFX) { + stack->pushInt(_sFXVolume); + } else { + stack->pushInt(_sFX->getVolumePercent()); + } + return STATUS_OK; + } + + + ////////////////////////////////////////////////////////////////////////// + // SoundFXNone + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SoundFXNone") == 0) { + stack->correctParams(0); + _sFXType = SFX_NONE; + _sFXParam1 = 0; + _sFXParam2 = 0; + _sFXParam3 = 0; + _sFXParam4 = 0; + stack->pushNULL(); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SoundFXEcho + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SoundFXEcho") == 0) { + stack->correctParams(4); + _sFXType = SFX_ECHO; + _sFXParam1 = (float)stack->pop()->getFloat(0); // Wet/Dry Mix [%] (0-100) + _sFXParam2 = (float)stack->pop()->getFloat(0); // Feedback [%] (0-100) + _sFXParam3 = (float)stack->pop()->getFloat(333.0f); // Left Delay [ms] (1-2000) + _sFXParam4 = (float)stack->pop()->getFloat(333.0f); // Right Delay [ms] (1-2000) + stack->pushNULL(); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SoundFXReverb + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SoundFXReverb") == 0) { + stack->correctParams(4); + _sFXType = SFX_REVERB; + _sFXParam1 = (float)stack->pop()->getFloat(0); // In Gain [dB] (-96 - 0) + _sFXParam2 = (float)stack->pop()->getFloat(0); // Reverb Mix [dB] (-96 - 0) + _sFXParam3 = (float)stack->pop()->getFloat(1000.0f); // Reverb Time [ms] (0.001 - 3000) + _sFXParam4 = (float)stack->pop()->getFloat(0.001f); // HighFreq RT Ratio (0.001 - 0.999) + stack->pushNULL(); + + return STATUS_OK; + } else { + return BaseScriptHolder::scCallMethod(script, stack, thisStack, name); + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *BaseObject::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("object"); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Caption + ////////////////////////////////////////////////////////////////////////// + else if (name == "Caption") { + _scValue->setString(getCaption(1)); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // X + ////////////////////////////////////////////////////////////////////////// + else if (name == "X") { + _scValue->setInt(_posX); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Y + ////////////////////////////////////////////////////////////////////////// + else if (name == "Y") { + _scValue->setInt(_posY); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Height (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "Height") { + _scValue->setInt(getHeight()); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Ready (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "Ready") { + _scValue->setBool(_ready); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Movable + ////////////////////////////////////////////////////////////////////////// + else if (name == "Movable") { + _scValue->setBool(_movable); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Registrable/Interactive + ////////////////////////////////////////////////////////////////////////// + else if (name == "Registrable" || name == "Interactive") { + _scValue->setBool(_registrable); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Zoomable/Scalable + ////////////////////////////////////////////////////////////////////////// + else if (name == "Zoomable" || name == "Scalable") { + _scValue->setBool(_zoomable); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // Rotatable + ////////////////////////////////////////////////////////////////////////// + else if (name == "Rotatable") { + _scValue->setBool(_rotatable); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // AlphaColor + ////////////////////////////////////////////////////////////////////////// + else if (name == "AlphaColor") { + _scValue->setInt((int)_alphaColor); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // BlendMode + ////////////////////////////////////////////////////////////////////////// + else if (name == "BlendMode") { + _scValue->setInt((int)_blendMode); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Scale + ////////////////////////////////////////////////////////////////////////// + else if (name == "Scale") { + if (_scale < 0) { + _scValue->setNULL(); + } else { + _scValue->setFloat((double)_scale); + } + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // ScaleX + ////////////////////////////////////////////////////////////////////////// + else if (name == "ScaleX") { + if (_scaleX < 0) { + _scValue->setNULL(); + } else { + _scValue->setFloat((double)_scaleX); + } + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // ScaleY + ////////////////////////////////////////////////////////////////////////// + else if (name == "ScaleY") { + if (_scaleY < 0) { + _scValue->setNULL(); + } else { + _scValue->setFloat((double)_scaleY); + } + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // RelativeScale + ////////////////////////////////////////////////////////////////////////// + else if (name == "RelativeScale") { + _scValue->setFloat((double)_relativeScale); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Rotate + ////////////////////////////////////////////////////////////////////////// + else if (name == "Rotate") { + if (!_rotateValid) { + _scValue->setNULL(); + } else { + _scValue->setFloat((double)_rotate); + } + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // RelativeRotate + ////////////////////////////////////////////////////////////////////////// + else if (name == "RelativeRotate") { + _scValue->setFloat((double)_relativeRotate); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Colorable + ////////////////////////////////////////////////////////////////////////// + else if (name == "Colorable") { + _scValue->setBool(_shadowable); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // SoundPanning + ////////////////////////////////////////////////////////////////////////// + else if (name == "SoundPanning") { + _scValue->setBool(_autoSoundPanning); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // SaveState + ////////////////////////////////////////////////////////////////////////// + else if (name == "SaveState") { + _scValue->setBool(_saveState); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // NonIntMouseEvents + ////////////////////////////////////////////////////////////////////////// + else if (name == "NonIntMouseEvents") { + _scValue->setBool(_nonIntMouseEvents); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // AccCaption + ////////////////////////////////////////////////////////////////////////// + else if (name == "AccCaption") { + _scValue->setNULL(); + return _scValue; + } else { + return BaseScriptHolder::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseObject::scSetProperty(const char *name, ScValue *value) { + ////////////////////////////////////////////////////////////////////////// + // Caption + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Caption") == 0) { + setCaption(value->getString()); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // X + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "X") == 0) { + _posX = value->getInt(); + afterMove(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Y + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Y") == 0) { + _posY = value->getInt(); + afterMove(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Movable + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Movable") == 0) { + _movable = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Registrable/Interactive + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Registrable") == 0 || strcmp(name, "Interactive") == 0) { + _registrable = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Zoomable/Scalable + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Zoomable") == 0 || strcmp(name, "Scalable") == 0) { + _zoomable = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Rotatable + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Rotatable") == 0) { + _rotatable = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // AlphaColor + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AlphaColor") == 0) { + _alphaColor = (uint32)value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // BlendMode + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "BlendMode") == 0) { + int i = value->getInt(); + if (i < BLEND_NORMAL || i >= NUM_BLEND_MODES) { + i = BLEND_NORMAL; + } + _blendMode = (TSpriteBlendMode)i; + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Scale + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Scale") == 0) { + if (value->isNULL()) { + _scale = -1; + } else { + _scale = (float)value->getFloat(); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ScaleX + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ScaleX") == 0) { + if (value->isNULL()) { + _scaleX = -1; + } else { + _scaleX = (float)value->getFloat(); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ScaleY + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ScaleY") == 0) { + if (value->isNULL()) { + _scaleY = -1; + } else { + _scaleY = (float)value->getFloat(); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // RelativeScale + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "RelativeScale") == 0) { + _relativeScale = (float)value->getFloat(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Rotate + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Rotate") == 0) { + if (value->isNULL()) { + _rotate = 0.0f; + _rotateValid = false; + } else { + _rotate = (float)value->getFloat(); + _rotateValid = true; + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // RelativeRotate + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "RelativeRotate") == 0) { + _relativeRotate = (float)value->getFloat(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Colorable + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Colorable") == 0) { + _shadowable = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SoundPanning + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SoundPanning") == 0) { + _autoSoundPanning = value->getBool(); + if (!_autoSoundPanning) { + resetSoundPan(); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SaveState + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SaveState") == 0) { + _saveState = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // NonIntMouseEvents + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "NonIntMouseEvents") == 0) { + _nonIntMouseEvents = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // AccCaption + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AccCaption") == 0) { + return STATUS_OK; + } else { + return BaseScriptHolder::scSetProperty(name, value); + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *BaseObject::scToString() { + return "[object]"; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseObject::showCursor() { + if (_cursor) { + return _gameRef->drawCursor(_cursor); + } else { + return STATUS_FAILED; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseObject::saveAsText(BaseDynamicBuffer *buffer, int indent) { + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseObject::persist(BasePersistenceManager *persistMgr) { + BaseScriptHolder::persist(persistMgr); + + for (int i = 0; i < 7; i++) { + persistMgr->transfer(TMEMBER(_caption[i])); + } + persistMgr->transfer(TMEMBER(_activeCursor)); + persistMgr->transfer(TMEMBER(_alphaColor)); + persistMgr->transfer(TMEMBER(_autoSoundPanning)); + persistMgr->transfer(TMEMBER(_cursor)); + persistMgr->transfer(TMEMBER(_sharedCursors)); + persistMgr->transfer(TMEMBER(_editorAlwaysRegister)); + persistMgr->transfer(TMEMBER(_editorOnly)); + persistMgr->transfer(TMEMBER(_editorSelected)); + persistMgr->transfer(TMEMBER(_iD)); + persistMgr->transfer(TMEMBER(_is3D)); + persistMgr->transfer(TMEMBER(_movable)); + persistMgr->transfer(TMEMBER(_posX)); + persistMgr->transfer(TMEMBER(_posY)); + persistMgr->transfer(TMEMBER(_relativeScale)); + persistMgr->transfer(TMEMBER(_rotatable)); + persistMgr->transfer(TMEMBER(_scale)); + persistMgr->transfer(TMEMBER(_sFX)); + persistMgr->transfer(TMEMBER(_sFXStart)); + persistMgr->transfer(TMEMBER(_sFXVolume)); + persistMgr->transfer(TMEMBER(_ready)); + persistMgr->transfer(TMEMBER(_rect)); + persistMgr->transfer(TMEMBER(_rectSet)); + persistMgr->transfer(TMEMBER(_registrable)); + persistMgr->transfer(TMEMBER(_shadowable)); + persistMgr->transfer(TMEMBER(_soundEvent)); + persistMgr->transfer(TMEMBER(_zoomable)); + + persistMgr->transfer(TMEMBER(_scaleX)); + persistMgr->transfer(TMEMBER(_scaleY)); + + persistMgr->transfer(TMEMBER(_rotate)); + persistMgr->transfer(TMEMBER(_rotateValid)); + persistMgr->transfer(TMEMBER(_relativeRotate)); + + persistMgr->transfer(TMEMBER(_saveState)); + persistMgr->transfer(TMEMBER(_nonIntMouseEvents)); + + persistMgr->transfer(TMEMBER_INT(_sFXType)); + persistMgr->transfer(TMEMBER(_sFXParam1)); + persistMgr->transfer(TMEMBER(_sFXParam2)); + persistMgr->transfer(TMEMBER(_sFXParam3)); + persistMgr->transfer(TMEMBER(_sFXParam4)); + + + persistMgr->transfer(TMEMBER_INT(_blendMode)); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseObject::setCursor(const char *filename) { + if (!_sharedCursors) { + delete _cursor; + _cursor = NULL; + } + + _sharedCursors = false; + _cursor = new BaseSprite(_gameRef); + if (!_cursor || DID_FAIL(_cursor->loadFile(filename))) { + delete _cursor; + _cursor = NULL; + return STATUS_FAILED; + } else { + return STATUS_OK; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseObject::setActiveCursor(const char *filename) { + delete _activeCursor; + _activeCursor = new BaseSprite(_gameRef); + if (!_activeCursor || DID_FAIL(_activeCursor->loadFile(filename))) { + delete _activeCursor; + _activeCursor = NULL; + return STATUS_FAILED; + } else { + return STATUS_OK; + } +} + + +////////////////////////////////////////////////////////////////////////// +int BaseObject::getHeight() { + return 0; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseObject::handleMouse(TMouseEvent event, TMouseButton button) { + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseObject::handleKeypress(Common::Event *event, bool printable) { + return false; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseObject::handleMouseWheel(int delta) { + return false; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseObject::playSFX(const char *filename, bool looping, bool playNow, const char *eventName, uint32 loopStart) { + // just play loaded sound + if (filename == NULL && _sFX) { + if (_gameRef->_editorMode || _sFXStart) { + _sFX->setVolumePercent(_sFXVolume); + _sFX->setPositionTime(_sFXStart); + if (!_gameRef->_editorMode) { + _sFXStart = 0; + } + } + if (playNow) { + setSoundEvent(eventName); + if (loopStart) { + _sFX->setLoopStart(loopStart); + } + return _sFX->play(looping); + } else { + return STATUS_OK; + } + } + + if (filename == NULL) { + return STATUS_FAILED; + } + + // create new sound + delete _sFX; + + _sFX = new BaseSound(_gameRef); + if (_sFX && DID_SUCCEED(_sFX->setSound(filename, Audio::Mixer::kSFXSoundType, true))) { + _sFX->setVolumePercent(_sFXVolume); + if (_sFXStart) { + _sFX->setPositionTime(_sFXStart); + _sFXStart = 0; + } + _sFX->applyFX(_sFXType, _sFXParam1, _sFXParam2, _sFXParam3, _sFXParam4); + if (playNow) { + setSoundEvent(eventName); + if (loopStart) { + _sFX->setLoopStart(loopStart); + } + return _sFX->play(looping); + } else { + return STATUS_OK; + } + } else { + delete _sFX; + _sFX = NULL; + return STATUS_FAILED; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseObject::stopSFX(bool deleteSound) { + if (_sFX) { + _sFX->stop(); + if (deleteSound) { + delete _sFX; + _sFX = NULL; + } + return STATUS_OK; + } else { + return STATUS_FAILED; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseObject::pauseSFX() { + if (_sFX) { + return _sFX->pause(); + } else { + return STATUS_FAILED; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseObject::resumeSFX() { + if (_sFX) { + return _sFX->resume(); + } else { + return STATUS_FAILED; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseObject::setSFXTime(uint32 time) { + _sFXStart = time; + if (_sFX && _sFX->isPlaying()) { + return _sFX->setPositionTime(time); + } else { + return STATUS_OK; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseObject::setSFXVolume(int volume) { + _sFXVolume = volume; + if (_sFX) { + return _sFX->setVolumePercent(volume); + } else { + return STATUS_OK; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseObject::updateSounds() { + if (_soundEvent) { + if (_sFX && !_sFX->isPlaying()) { + applyEvent(_soundEvent); + setSoundEvent(NULL); + } + } + + if (_sFX) { + updateOneSound(_sFX); + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseObject::updateOneSound(BaseSound *sound) { + bool ret = STATUS_OK; + + if (sound) { + if (_autoSoundPanning) { + ret = sound->setPan(_gameRef->_soundMgr->posToPan(_posX - _gameRef->_offsetX, _posY - _gameRef->_offsetY)); + } + + ret = sound->applyFX(_sFXType, _sFXParam1, _sFXParam2, _sFXParam3, _sFXParam4); + } + return ret; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseObject::resetSoundPan() { + if (!_sFX) { + return STATUS_OK; + } else { + return _sFX->setPan(0.0f); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseObject::getExtendedFlag(const char *flagName) { + return false; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseObject::isReady() { + return _ready; +} + + +////////////////////////////////////////////////////////////////////////// +void BaseObject::setSoundEvent(const char *eventName) { + delete[] _soundEvent; + _soundEvent = NULL; + if (eventName) { + _soundEvent = new char[strlen(eventName) + 1]; + if (_soundEvent) { + strcpy(_soundEvent, eventName); + } + } +} + +////////////////////////////////////////////////////////////////////////// +bool BaseObject::afterMove() { + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_object.h b/engines/wintermute/base/base_object.h new file mode 100644 index 0000000000..96fed2b847 --- /dev/null +++ b/engines/wintermute/base/base_object.h @@ -0,0 +1,147 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_OBJECT_H +#define WINTERMUTE_BASE_OBJECT_H + + +#include "engines/wintermute/base/base_script_holder.h" +#include "engines/wintermute/persistent.h" +#include "common/events.h" + +namespace Wintermute { + +class BaseSprite; +class BaseSound; +class BaseSurface; +class BaseScriptHolder; +class ScValue; +class ScStack; +class ScScript; +class BaseObject : public BaseScriptHolder { +protected: + bool _autoSoundPanning; + uint32 _sFXStart; + bool setSFXTime(uint32 time); + bool setSFXVolume(int volume); + bool resumeSFX(); + bool pauseSFX(); + bool stopSFX(bool deleteSound = true); + bool playSFX(const char *filename, bool looping = false, bool playNow = true, const char *eventName = NULL, uint32 loopStart = 0); + BaseSound *_sFX; + TSFXType _sFXType; + float _sFXParam1; + float _sFXParam2; + float _sFXParam3; + float _sFXParam4; + float _relativeRotate; + bool _rotateValid; + float _rotate; + void setSoundEvent(const char *eventName); + bool _rotatable; + float _scaleX; + float _scaleY; + float _relativeScale; + bool _editorSelected; + bool _editorAlwaysRegister; + bool _ready; + Rect32 _rect; + bool _rectSet; + int _iD; + char *_soundEvent; +public: + TSpriteBlendMode _blendMode; + virtual bool afterMove(); + float _scale; + uint32 _alphaColor; + virtual bool isReady(); + virtual bool getExtendedFlag(const char *flagName); + virtual bool resetSoundPan(); + virtual bool updateSounds(); + bool updateOneSound(BaseSound *sound); + int _sFXVolume; + + virtual bool handleMouseWheel(int delta); + virtual bool handleMouse(TMouseEvent event, TMouseButton button); + virtual bool handleKeypress(Common::Event *event, bool printable = false); + virtual int getHeight(); + bool setCursor(const char *filename); + bool setActiveCursor(const char *filename); + bool cleanup(); + const char *getCaption(int caseVal = 1); + void setCaption(const char *caption, int caseVal = 1); + + bool _editorOnly; + bool _is3D; + + DECLARE_PERSISTENT(BaseObject, BaseScriptHolder) + virtual bool showCursor(); + BaseSprite *_cursor; + bool _sharedCursors; + BaseSprite *_activeCursor; + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent); + virtual bool listen(BaseScriptHolder *param1, uint32 param2); + + bool _movable; + bool _zoomable; + bool _shadowable; + int32 _posY; + int32 _posX; + bool _registrable; + char *_caption[7]; + bool _saveState; + + BaseObject(BaseGame *inGame); + virtual ~BaseObject(); + // base + virtual bool update() { + return STATUS_FAILED; + }; + virtual bool display() { + return STATUS_FAILED; + }; + virtual bool invalidateDeviceObjects() { + return STATUS_OK; + }; + virtual bool restoreDeviceObjects() { + return STATUS_OK; + }; + bool _nonIntMouseEvents; + + +public: + // scripting interface + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + virtual const char *scToString(); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_parser.cpp b/engines/wintermute/base/base_parser.cpp new file mode 100644 index 0000000000..9a0e9e3ad9 --- /dev/null +++ b/engines/wintermute/base/base_parser.cpp @@ -0,0 +1,467 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_engine.h" +#include "engines/wintermute/platform_osystem.h" +#include "common/str.h" +#include "common/util.h" + +#define WHITESPACE " \t\n\r" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////// +BaseParser::BaseParser() { + _whiteSpace = new char [strlen(WHITESPACE) + 1]; + strcpy(_whiteSpace, WHITESPACE); +} + + +////////////////////////////////////////////////////////////////////// +BaseParser::~BaseParser() { + if (_whiteSpace != NULL) { + delete[] _whiteSpace; + } +} + + +////////////////////////////////////////////////////////////////////// +char *BaseParser::getLastOffender() { + return _lastOffender; +} + + +////////////////////////////////////////////////////////////////////// +int32 BaseParser::getObject(char **buf, const TokenDesc *tokens, char **name, char **data) { + skipCharacters(buf, _whiteSpace); + + // skip comment lines. + while (**buf == ';') { + *buf = strchr(*buf, '\n'); + _parserLine++; + skipCharacters(buf, _whiteSpace); + } + + if (! **buf) { // at end of file + return PARSERR_EOF; + } + + // find the token. + // TODO: for now just use brute force. Improve later. + while (tokens->id != 0) { + if (!scumm_strnicmp(tokens->token, *buf, strlen(tokens->token))) { + // here we could be matching PART of a string + // we could detect this here or the token list + // could just have the longer tokens first in the list + break; + } + ++tokens; + } + if (tokens->id == 0) { + char *p = strchr(*buf, '\n'); + if (p && p > *buf) { + strncpy(_lastOffender, *buf, MIN((uint32)255, (uint32)(p - *buf))); // TODO, clean + } else { + strcpy(_lastOffender, ""); + } + + return PARSERR_TOKENNOTFOUND; + } + // skip the token + *buf += strlen(tokens->token); + skipCharacters(buf, _whiteSpace); + + // get optional name + *name = getSubText(buf, '\'', '\''); // single quotes + skipCharacters(buf, _whiteSpace); + + // get optional data + if (**buf == '=') { // An assignment rather than a command/object. + *data = getAssignmentText(buf); + } else { + *data = getSubText(buf, '{', '}'); + } + + return tokens->id; +} + + +////////////////////////////////////////////////////////////////////// +int32 BaseParser::getCommand(char **buf, const TokenDesc *tokens, char **params) { + if (!*buf) { + return PARSERR_TOKENNOTFOUND; + } + BaseEngine::instance().getGameRef()->miniUpdate(); + char *name; + return getObject(buf, tokens, &name, params); +} + + +////////////////////////////////////////////////////////////////////// +void BaseParser::skipCharacters(char **buf, const char *toSkip) { + char ch; + while ((ch = **buf) != 0) { + if (ch == '\n') { + _parserLine++; + } + if (strchr(toSkip, ch) == NULL) { + return; + } + ++*buf; // skip this character + } + // we must be at the end of the buffer if we get here +} + + +////////////////////////////////////////////////////////////////////// +char *BaseParser::getSubText(char **buf, char open, char close) { + if (**buf == 0 || **buf != open) { + return 0; + } + ++*buf; // skip opening delimiter + char *result = *buf; + + // now find the closing delimiter + char theChar; + long skip = 1; + + if (open == close) { // we cant nest identical delimiters + open = 0; + } + while ((theChar = **buf) != 0) { + if (theChar == open) { + ++skip; + } + if (theChar == close) { + if (--skip == 0) { + **buf = 0; // null terminate the result string + ++*buf; // move past the closing delimiter + break; + } + } + ++*buf; // try next character + } + return result; +} + + +////////////////////////////////////////////////////////////////////// +char *BaseParser::getAssignmentText(char **buf) { + ++*buf; // skip the '=' + skipCharacters(buf, _whiteSpace); + char *result = *buf; + + + if (*result == '"') { + result = getSubText(buf, '"', '"'); + } else { + // now, we need to find the next whitespace to end the data + char theChar; + + while ((theChar = **buf) != 0) { + if (theChar <= 0x20) { // space and control chars + break; + } + ++*buf; + } + **buf = 0; // null terminate it + if (theChar) { // skip the terminator + ++*buf; + } + } + + return result; +} + +////////////////////////////////////////////////////////////////////// +Common::String BaseParser::getToken(char **buf) { + char token[100]; // TODO: Remove static + char *b = *buf, *t = token; + while (true) { + while (*b && (*b == ' ' || *b == '\n' || *b == 13 || *b == 10 || *b == '\t')) { + b++; + } + if (*b == ';') + while (*b && *b != '\n' && *b != 13 && *b != 10) { + b++; + } + else { + break; + } + } + + if (*b == '\'') { + b++; + while (*b && *b != '\'') { + *t++ = *b++; + } + *t++ = 0; + if (*b == '\'') { + b++; + } + } else if (*b == '(' || *b == ')' || *b == '=' || *b == ',' || *b == '[' || *b == ']' || + *b == '%' || *b == ':' || *b == '{' || *b == '}') { + *t++ = *b++; + *t++ = 0; + } else if (*b == '.' && (*(b + 1) < '0' || *(b + 1) > '9')) { + *t++ = *b++; + *t++ = 0; + } else if ((*b >= '0' && *b <= '9') || *b == '.' || *b == '-') { + while (*b && ((*b >= '0' && *b <= '9') || *b == '.' || *b == '-')) { + *t++ = *b++; + } + *t++ = 0; + } else if ((*b >= 'A' && *b <= 'Z') || (*b >= 'a' && *b <= 'z') || *b == '_') { + while (*b && ((*b >= 'A' && *b <= 'Z') || (*b >= 'a' && *b <= 'z') || *b == '_')) { + *t++ = *b++; + } + *t++ = 0; + } else if (*b == 0) { + *buf = b; + return NULL; + } else { + // Error. + return NULL; + } + + *buf = b; + return token; +} + + +////////////////////////////////////////////////////////////////////// +float BaseParser::getTokenFloat(char **buf) { + Common::String token = getToken(buf); + const char *t = token.c_str(); + if (!((*t >= '0' && *t <= '9') || *t == '-' || *t == '.')) { + // Error situation. We handle this by return 0. + return 0.; + } + float rc = (float)atof(t); + return rc; +} + + +////////////////////////////////////////////////////////////////////// +int BaseParser::getTokenInt(char **buf) { + Common::String token = getToken(buf); + const char *t = token.c_str(); + if (!((*t >= '0' && *t <= '9') || *t == '-')) { + // Error situation. We handle this by return 0. + return 0; + } + int rc = atoi(t); + return rc; +} + + +////////////////////////////////////////////////////////////////////// +void BaseParser::skipToken(char **buf, char *tok, char * /*msg*/) { + Common::String token = getToken(buf); + const char *t = token.c_str(); + if (strcmp(t, tok)) { + return; // Error + } +} + + +////////////////////////////////////////////////////////////////////// +int BaseParser::scanStr(const char *in, const char *format, ...) { + va_list arg; + va_start(arg, format); + + int num = 0; + in += strspn(in, " \t\n\f"); + + while (*format && *in) { + if (*format == '%') { + format++; + switch (*format) { + case 'd': { + int *a = va_arg(arg, int *); + in += strspn(in, " \t\n\f"); + *a = atoi(in); + in += strspn(in, "0123456789+- \t\n\f"); + num++; + break; + } + case 'D': { + int i; + int *list = va_arg(arg, int *); + int *nr = va_arg(arg, int *); + in += strspn(in, " \t\n\f"); + i = 0; + while ((*in >= '0' && *in <= '9') || *in == '+' || *in == '-') { + list[i++] = atoi(in); + in += strspn(in, "0123456789+-"); + in += strspn(in, " \t\n\f"); + if (*in != ',') { + break; + } + in++; + in += strspn(in, " \t\n\f"); + } + *nr = i; + num++; + break; + } + case 'b': { + bool *a = va_arg(arg, bool *); + in += strspn(in, " \t\n\f"); + const char *in2 = in + strspn(in, "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); + int l = (int)(in2 - in); + + *a = (bool)(!scumm_strnicmp(in, "yes", l) || !scumm_strnicmp(in, "true", l) || !scumm_strnicmp(in, "on", l) || + !scumm_strnicmp(in, "1", l)); + + + in = in2 + strspn(in2, " \t\n\f"); + num++; + break; + } + case 'f': { + float *a = va_arg(arg, float *); + in += strspn(in, " \t\n\f"); + *a = (float)atof(in); + in += strspn(in, "0123456789.eE+- \t\n\f"); + num++; + break; + } + case 'F': { + int i; + float *list = va_arg(arg, float *); + int *nr = va_arg(arg, int *); + in += strspn(in, " \t\n\f"); + i = 0; + while ((*in >= '0' && *in <= '9') || *in == '.' || *in == '+' || *in == '-' || *in == 'e' || *in == 'E') { + list[i++] = (float)atof(in); + in += strspn(in, "0123456789.eE+-"); + in += strspn(in, " \t\n\f"); + if (*in != ',') { + break; + } + in++; + in += strspn(in, " \t\n\f"); + } + *nr = i; + num++; + break; + } + case 's': { + char *a = va_arg(arg, char *); + in += strspn(in, " \t\n\f"); + if (*in == '\'') { + in++; + const char *in2 = strchr(in, '\''); + if (in2) { + Common::strlcpy(a, in, (int)(in2 - in) + 1); + in = in2 + 1; + } else { + strcpy(a, in); + in = strchr(in, 0); + } + } else { + const char *in2 = in + strspn(in, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789."); + Common::strlcpy(a, in, (int)(in2 - in) + 1); + in = in2; + } + in += strspn(in, " \t\n\f"); + num++; + break; + } + case 'S': { + char *a = va_arg(arg, char *); + in += strspn(in, " \t\n\f"); + if (*in == '\"') { + in++; + while (*in != '\"') { + if (*in == '\\') { + in++; + switch (*in) { + case '\\': + *a++ = '\\'; + break; + case 'n': + *a++ = '\n'; + break; + case 'r': + *a++ = '\r'; + break; + case 't': + *a++ = '\t'; + break; + case '"': + *a++ = '"'; + break; + default: + *a++ = '\\'; + *a++ = *in; + break; + } //switch + in++; + } else { + *a++ = *in++; + } + } //while in string + in++; + num++; + } //if string started + + //terminate string + *a = '\0'; + break; + } + } + if (*format) { + format++; + } + } else if (*format == ' ') { + format++; + in += strspn(in, " \t\n\f"); + } else if (*in == *format) { + in++; + format++; + } else { + num = -1; + break; + } + } + + va_end(arg); + + return num; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_parser.h b/engines/wintermute/base/base_parser.h new file mode 100644 index 0000000000..76ca8ea856 --- /dev/null +++ b/engines/wintermute/base/base_parser.h @@ -0,0 +1,88 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_PARSER_H +#define WINTERMUTE_BASE_PARSER_H + + +#define TOKEN_DEF_START \ + enum \ + { \ + TOKEN_NONE = 0, +#define TOKEN_DEF(name) \ + TOKEN_ ## name, +#define TOKEN_DEF_END \ + TOKEN_TOTAL_COUNT \ + }; +#define TOKEN_TABLE_START(name) \ + static const BaseParser::TokenDesc name [] = \ + { +#define TOKEN_TABLE(name) \ + { TOKEN_ ## name, #name }, +#define TOKEN_TABLE_END \ + { 0, 0 } \ + }; + +#define PARSERR_GENERIC -3 +#define PARSERR_EOF -2 +#define PARSERR_TOKENNOTFOUND -1 + +#include "engines/wintermute/coll_templ.h" + +namespace Wintermute { + +class BaseParser { +public: + struct TokenDesc { + int32 id; + const char *token; + }; + +public: + int scanStr(const char *in, const char *format, ...); + int32 getCommand(char **buf, const TokenDesc *tokens, char **params); + BaseParser(); + virtual ~BaseParser(); +private: + char *getLastOffender(); + void skipToken(char **buf, char *tok, char *msg = NULL); + int getTokenInt(char **buf); + float getTokenFloat(char **buf); + Common::String getToken(char **buf); + char *getAssignmentText(char **buf); + char *getSubText(char **buf, char open, char close); + void skipCharacters(char **buf, const char *toSkip); + int32 getObject(char **buf, const TokenDesc *tokens, char **name, char **data); + int _parserLine; + char _lastOffender[255]; + char *_whiteSpace; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_persistence_manager.cpp b/engines/wintermute/base/base_persistence_manager.cpp new file mode 100644 index 0000000000..accb0a8fd3 --- /dev/null +++ b/engines/wintermute/base/base_persistence_manager.cpp @@ -0,0 +1,882 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/dcgf.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_engine.h" +#include "engines/wintermute/base/base_persistence_manager.h" +#include "engines/wintermute/base/base_save_thumb_helper.h" +#include "engines/wintermute/platform_osystem.h" +#include "engines/wintermute/math/vector2.h" +#include "engines/wintermute/base/gfx/base_image.h" +#include "engines/wintermute/base/sound/base_sound.h" +#include "engines/wintermute/graphics/transparent_surface.h" +#include "engines/wintermute/wintermute.h" +#include "graphics/decoders/bmp.h" +#include "graphics/scaler.h" +#include "common/memstream.h" +#include "common/str.h" +#include "common/system.h" +#include "common/savefile.h" + +namespace Wintermute { + +#define SAVE_BUFFER_INIT_SIZE 100000 +#define SAVE_BUFFER_GROW_BY 50000 + +#define SAVE_MAGIC 0x45564153 +#define SAVE_MAGIC_2 0x32564153 + +////////////////////////////////////////////////////////////////////////// +BasePersistenceManager::BasePersistenceManager(const char *savePrefix, bool deleteSingleton) { + _saving = false; +// _buffer = NULL; +// _bufferSize = 0; + _offset = 0; + _saveStream = NULL; + _loadStream = NULL; + _deleteSingleton = deleteSingleton; + if (BaseEngine::instance().getGameRef()) { + _gameRef = BaseEngine::instance().getGameRef(); + } else { + _gameRef = NULL; + } + + _richBuffer = NULL; + _richBufferSize = 0; + + _scummVMThumbnailData = NULL; + _scummVMThumbSize = 0; + + _savedDescription = NULL; +// _savedTimestamp = 0; + _savedVerMajor = _savedVerMinor = _savedVerBuild = 0; + _savedExtMajor = _savedExtMinor = 0; + + _thumbnailDataSize = 0; + _thumbnailData = NULL; + if (savePrefix) { + _savePrefix = savePrefix; + } else if (_gameRef) { + _savePrefix = _gameRef->getGameId(); + } else { + _savePrefix = "wmesav"; + } +} + + +////////////////////////////////////////////////////////////////////////// +BasePersistenceManager::~BasePersistenceManager() { + cleanup(); + if (_deleteSingleton && BaseEngine::instance().getGameRef() == NULL) + BaseEngine::destroy(); +} + + +////////////////////////////////////////////////////////////////////////// +void BasePersistenceManager::cleanup() { + /* if (_buffer) { + if (_saving) free(_buffer); + else delete[] _buffer; // allocated by file manager + } + _buffer = NULL; + + _bufferSize = 0;*/ + _offset = 0; + + delete[] _richBuffer; + _richBuffer = NULL; + _richBufferSize = 0; + + delete[] _savedDescription; + _savedDescription = NULL; // ref to buffer +// _savedTimestamp = 0; + _savedVerMajor = _savedVerMinor = _savedVerBuild = 0; + _savedExtMajor = _savedExtMinor = 0; + + _thumbnailDataSize = 0; + if (_thumbnailData) { + delete[] _thumbnailData; + _thumbnailData = NULL; + } + + _scummVMThumbSize = 0; + if (_scummVMThumbnailData) { + delete[] _scummVMThumbnailData; + _scummVMThumbnailData = NULL; + } + + delete _loadStream; + delete _saveStream; + _loadStream = NULL; + _saveStream = NULL; +} + +Common::String BasePersistenceManager::getFilenameForSlot(int slot) const { + // 3 Digits, to allow for one save-slot for autosave + slot 1 - 100 (which will be numbered 0-99 filename-wise) + return Common::String::format("%s.%03d", _savePrefix.c_str(), slot); +} + +void BasePersistenceManager::getSaveStateDesc(int slot, SaveStateDescriptor &desc) { + Common::String filename = getFilenameForSlot(slot); + debugC(kWintermuteDebugSaveGame, "Trying to list savegame %s in slot %d", filename.c_str(), slot); + if (DID_FAIL(readHeader(filename))) { + warning("getSavedDesc(%d) - Failed for %s", slot, filename.c_str()); + return; + } + desc.setSaveSlot(slot); + desc.setDescription(_savedDescription); + desc.setDeletableFlag(true); + desc.setWriteProtectedFlag(false); + + int thumbSize = 0; + byte *thumbData = NULL; + if (_scummVMThumbSize > 0) { + thumbSize = _scummVMThumbSize; + thumbData = _scummVMThumbnailData; + } else if (_thumbnailDataSize > 0) { + thumbSize = _thumbnailDataSize; + thumbData = _thumbnailData; + } + + if (thumbSize > 0) { + Common::MemoryReadStream thumbStream(thumbData, thumbSize, DisposeAfterUse::NO); + Graphics::BitmapDecoder bmpDecoder; + if (bmpDecoder.loadStream(thumbStream)) { + const Graphics::Surface *bmpSurface = bmpDecoder.getSurface(); + TransparentSurface *scaleableSurface = new TransparentSurface(*bmpSurface, false); + Graphics::Surface *scaled = scaleableSurface->scale(kThumbnailWidth, kThumbnailHeight2); + Graphics::Surface *thumb = scaled->convertTo(g_system->getOverlayFormat()); + desc.setThumbnail(thumb); + delete scaleableSurface; + scaled->free(); + delete scaled; + } + } + + desc.setSaveDate(_savedTimestamp.tm_year, _savedTimestamp.tm_mon, _savedTimestamp.tm_mday); + desc.setSaveTime(_savedTimestamp.tm_hour, _savedTimestamp.tm_min); + desc.setPlayTime(0); +} + +void BasePersistenceManager::deleteSaveSlot(int slot) { + Common::String filename = getFilenameForSlot(slot); + g_system->getSavefileManager()->removeSavefile(filename); +} + +uint32 BasePersistenceManager::getMaxUsedSlot() { + Common::String saveMask = Common::String::format("%s.???", _savePrefix.c_str()); + Common::StringArray saves = g_system->getSavefileManager()->listSavefiles(saveMask); + Common::StringArray::iterator it = saves.begin(); + int ret = -1; + for (; it != saves.end(); ++it) { + int num = -1; + sscanf(it->c_str(), ".%d", &num); + ret = MAX(ret, num); + } + return ret; +} + +bool BasePersistenceManager::getSaveExists(int slot) { + Common::String filename = getFilenameForSlot(slot); + if (DID_FAIL(readHeader(filename))) { + return false; + } + return true; +} + +////////////////////////////////////////////////////////////////////////// +bool BasePersistenceManager::initSave(const char *desc) { + if (!desc) { + return STATUS_FAILED; + } + + cleanup(); + _saving = true; + + _saveStream = new Common::MemoryWriteStreamDynamic(DisposeAfterUse::YES); + + if (_saveStream) { + // get thumbnails + if (!_gameRef->_cachedThumbnail) { + _gameRef->_cachedThumbnail = new BaseSaveThumbHelper(_gameRef); + if (DID_FAIL(_gameRef->_cachedThumbnail->storeThumbnail(true))) { + delete _gameRef->_cachedThumbnail; + _gameRef->_cachedThumbnail = NULL; + } + } + + uint32 magic = DCGF_MAGIC; + putDWORD(magic); + + magic = SAVE_MAGIC_2; + putDWORD(magic); + + byte verMajor, verMinor, extMajor, extMinor; + _gameRef->getVersion(&verMajor, &verMinor, &extMajor, &extMinor); + //uint32 version = MAKELONG(MAKEWORD(VerMajor, VerMinor), MAKEWORD(ExtMajor, ExtMinor)); + _saveStream->writeByte(verMajor); + _saveStream->writeByte(verMinor); + _saveStream->writeByte(extMajor); + _saveStream->writeByte(extMinor); + + // new in ver 2 + putDWORD((uint32)DCGF_VER_BUILD); + putString(_gameRef->getName()); + + // thumbnail data size + bool thumbnailOK = false; + + if (_gameRef->_cachedThumbnail) { + if (_gameRef->_cachedThumbnail->_thumbnail) { + Common::MemoryWriteStreamDynamic thumbStream(DisposeAfterUse::YES); + if (_gameRef->_cachedThumbnail->_thumbnail->writeBMPToStream(&thumbStream)) { + _saveStream->writeUint32LE(thumbStream.size()); + _saveStream->write(thumbStream.getData(), thumbStream.size()); + } else { + _saveStream->writeUint32LE(0); + } + + thumbnailOK = true; + } + } + if (!thumbnailOK) { + putDWORD(0); + } + thumbnailOK = false; + // Again for the ScummVM-thumb: + if (_gameRef->_cachedThumbnail) { + if (_gameRef->_cachedThumbnail->_scummVMThumb) { + Common::MemoryWriteStreamDynamic scummVMthumbStream(DisposeAfterUse::YES); + if (_gameRef->_cachedThumbnail->_scummVMThumb->writeBMPToStream(&scummVMthumbStream)) { + _saveStream->writeUint32LE(scummVMthumbStream.size()); + _saveStream->write(scummVMthumbStream.getData(), scummVMthumbStream.size()); + } else { + _saveStream->writeUint32LE(0); + } + + thumbnailOK = true; + } + } + if (!thumbnailOK) { + putDWORD(0); + } + + + // in any case, destroy the cached thumbnail once used + delete _gameRef->_cachedThumbnail; + _gameRef->_cachedThumbnail = NULL; + + uint32 dataOffset = _offset + + sizeof(uint32) + // data offset + sizeof(uint32) + strlen(desc) + 1 + // description + sizeof(uint32); // timestamp + + putDWORD(dataOffset); + putString(desc); + + g_system->getTimeAndDate(_savedTimestamp); + putTimeDate(_savedTimestamp); + _savedPlayTime = g_system->getMillis(); + _saveStream->writeUint32LE(_savedPlayTime); + } + return STATUS_OK; +} + +bool BasePersistenceManager::readHeader(const Common::String &filename) { + cleanup(); + + _saving = false; + + _loadStream = g_system->getSavefileManager()->openForLoading(filename); + //_buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename, &_bufferSize); + if (_loadStream) { + uint32 magic; + magic = getDWORD(); + + if (magic != DCGF_MAGIC) { + cleanup(); + return STATUS_FAILED; + } + + magic = getDWORD(); + + if (magic == SAVE_MAGIC || magic == SAVE_MAGIC_2) { + _savedVerMajor = _loadStream->readByte(); + _savedVerMinor = _loadStream->readByte(); + _savedExtMajor = _loadStream->readByte(); + _savedExtMinor = _loadStream->readByte(); + + if (magic == SAVE_MAGIC_2) { + _savedVerBuild = (byte)getDWORD(); + _savedName = getStringObj(); + + // load thumbnail + _thumbnailDataSize = getDWORD(); + if (_thumbnailDataSize > 0) { + _thumbnailData = new byte[_thumbnailDataSize]; + if (_thumbnailData) { + getBytes(_thumbnailData, _thumbnailDataSize); + } else { + _thumbnailDataSize = 0; + } + } + if (_savedVerMajor >= 1 && _savedVerMinor >= 2) { + _scummVMThumbSize = getDWORD(); + _scummVMThumbnailData = new byte[_scummVMThumbSize]; + if (_scummVMThumbnailData) { + getBytes(_scummVMThumbnailData, _scummVMThumbSize); + } else { + _scummVMThumbSize = 0; + } + } + } else { + _savedVerBuild = 35; // last build with ver1 savegames + } + + uint32 dataOffset = getDWORD(); + + _savedDescription = getString(); + _savedTimestamp = getTimeDate(); + _savedPlayTime = _loadStream->readUint32LE(); + + _offset = dataOffset; + + return STATUS_OK; + } + } + + cleanup(); + return STATUS_FAILED; +} + +////////////////////////////////////////////////////////////////////////// +bool BasePersistenceManager::initLoad(const Common::String &filename) { + if (DID_FAIL(readHeader(filename))) { + cleanup(); + return STATUS_FAILED; + } + _saving = false; + + if (_savedName == "" || scumm_stricmp(_savedName.c_str(), _gameRef->getName()) != 0) { + debugC(kWintermuteDebugSaveGame, "ERROR: Saved game name doesn't match current game"); + cleanup(); + return STATUS_FAILED; + } + + // if save is newer version than we are, fail + if (_savedVerMajor > DCGF_VER_MAJOR || + (_savedVerMajor == DCGF_VER_MAJOR && _savedVerMinor > DCGF_VER_MINOR) || + (_savedVerMajor == DCGF_VER_MAJOR && _savedVerMinor == DCGF_VER_MINOR && _savedVerBuild > DCGF_VER_BUILD) + ) { + + debugC(kWintermuteDebugSaveGame, "ERROR: Saved game version is newer than current game"); + debugC(kWintermuteDebugSaveGame, "ERROR: Expected %d.%d.%d got %d.%d.%d", DCGF_VER_MAJOR, DCGF_VER_MINOR, DCGF_VER_BUILD, _savedVerMajor, _savedVerMinor, _savedVerBuild); + cleanup(); + return STATUS_FAILED; + } + + // if save is older than the minimal version we support + if (_savedVerMajor < SAVEGAME_VER_MAJOR || + (_savedVerMajor == SAVEGAME_VER_MAJOR && _savedVerMinor < SAVEGAME_VER_MINOR) || + (_savedVerMajor == SAVEGAME_VER_MAJOR && _savedVerMinor == SAVEGAME_VER_MINOR && _savedVerBuild < SAVEGAME_VER_BUILD) + ) { + debugC(kWintermuteDebugSaveGame, "ERROR: Saved game is too old and cannot be used by this version of game engine"); + debugC(kWintermuteDebugSaveGame, "ERROR: Expected %d.%d.%d got %d.%d.%d", DCGF_VER_MAJOR, DCGF_VER_MINOR, DCGF_VER_BUILD, _savedVerMajor, _savedVerMinor, _savedVerBuild); + cleanup(); + return STATUS_FAILED; + + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BasePersistenceManager::saveFile(const Common::String &filename) { + byte *prefixBuffer = _richBuffer; + uint32 prefixSize = _richBufferSize; + byte *buffer = ((Common::MemoryWriteStreamDynamic *)_saveStream)->getData(); + uint32 bufferSize = ((Common::MemoryWriteStreamDynamic *)_saveStream)->size(); + + Common::SaveFileManager *saveMan = ((WintermuteEngine *)g_engine)->getSaveFileMan(); + Common::OutSaveFile *file = saveMan->openForSaving(filename); + file->write(prefixBuffer, prefixSize); + file->write(buffer, bufferSize); + bool retVal = !file->err(); + file->finalize(); + delete file; + return retVal; +} + + +////////////////////////////////////////////////////////////////////////// +bool BasePersistenceManager::putBytes(byte *buffer, uint32 size) { + _saveStream->write(buffer, size); + if (_saveStream->err()) { + return STATUS_FAILED; + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BasePersistenceManager::getBytes(byte *buffer, uint32 size) { + _loadStream->read(buffer, size); + if (_loadStream->err()) { + return STATUS_FAILED; + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +void BasePersistenceManager::putDWORD(uint32 val) { + _saveStream->writeUint32LE(val); +} + + +////////////////////////////////////////////////////////////////////////// +uint32 BasePersistenceManager::getDWORD() { + uint32 ret = _loadStream->readUint32LE(); + return ret; +} + + +////////////////////////////////////////////////////////////////////////// +void BasePersistenceManager::putString(const Common::String &val) { + if (!val.size()) { + putString("(null)"); + } else { + _saveStream->writeUint32LE(val.size()); + _saveStream->writeString(val); + } +} + +Common::String BasePersistenceManager::getStringObj() { + uint32 len = _loadStream->readUint32LE(); + char *ret = new char[len + 1]; + _loadStream->read(ret, len); + ret[len] = '\0'; + + Common::String retString = ret; + delete[] ret; + + if (retString == "(null)") { + retString = ""; + } + + return retString; +} + +////////////////////////////////////////////////////////////////////////// +char *BasePersistenceManager::getString() { + uint32 len = _loadStream->readUint32LE(); + char *ret = new char[len + 1]; + _loadStream->read(ret, len); + ret[len] = '\0'; + + if (!strcmp(ret, "(null)")) { + delete[] ret; + return NULL; + } else { + return ret; + } +} + +bool BasePersistenceManager::putTimeDate(const TimeDate &t) { + _saveStream->writeSint32LE(t.tm_sec); + _saveStream->writeSint32LE(t.tm_min); + _saveStream->writeSint32LE(t.tm_hour); + _saveStream->writeSint32LE(t.tm_mday); + _saveStream->writeSint32LE(t.tm_mon); + _saveStream->writeSint32LE(t.tm_year); + // _saveStream->writeSint32LE(t.tm_wday); //TODO: Add this in when merging next + + if (_saveStream->err()) { + return STATUS_FAILED; + } + return STATUS_OK; +} + +TimeDate BasePersistenceManager::getTimeDate() { + TimeDate t; + t.tm_sec = _loadStream->readSint32LE(); + t.tm_min = _loadStream->readSint32LE(); + t.tm_hour = _loadStream->readSint32LE(); + t.tm_mday = _loadStream->readSint32LE(); + t.tm_mon = _loadStream->readSint32LE(); + t.tm_year = _loadStream->readSint32LE(); + // t.tm_wday = _loadStream->readSint32LE(); //TODO: Add this in when merging next + return t; +} + +void BasePersistenceManager::putFloat(float val) { + Common::String str = Common::String::format("F%f", val); + _saveStream->writeUint32LE(str.size()); + _saveStream->writeString(str); +} + +float BasePersistenceManager::getFloat() { + char *str = getString(); + float value = 0.0f; + int ret = sscanf(str, "F%f", &value); + if (ret != 1) { + warning("%s not parsed as float", str); + } + delete[] str; + return value; +} + +void BasePersistenceManager::putDouble(double val) { + Common::String str = Common::String::format("D%f", val); + str.format("D%f", val); + _saveStream->writeUint32LE(str.size()); + _saveStream->writeString(str); +} + +double BasePersistenceManager::getDouble() { + char *str = getString(); + float value = 0.0f; // TODO: Do we ever really need to carry a full double-precision number? + int ret = sscanf(str, "D%f", &value); + if (ret != 1) { + warning("%s not parsed as double", str); + } + delete[] str; + return value; +} + +////////////////////////////////////////////////////////////////////////// +// bool +bool BasePersistenceManager::transfer(const char *name, bool *val) { + if (_saving) { + _saveStream->writeByte(*val); + if (_saveStream->err()) { + return STATUS_FAILED; + } + return STATUS_OK; + } else { + *val = _loadStream->readByte(); + if (_loadStream->err()) { + return STATUS_FAILED; + } + return STATUS_OK; + } +} + + +////////////////////////////////////////////////////////////////////////// +// int +bool BasePersistenceManager::transfer(const char *name, int *val) { + if (_saving) { + _saveStream->writeSint32LE(*val); + if (_saveStream->err()) { + return STATUS_FAILED; + } + return STATUS_OK; + } else { + *val = _loadStream->readSint32LE(); + if (_loadStream->err()) { + return STATUS_FAILED; + } + return STATUS_OK; + } +} + + +////////////////////////////////////////////////////////////////////////// +// DWORD +bool BasePersistenceManager::transfer(const char *name, uint32 *val) { + if (_saving) { + _saveStream->writeUint32LE(*val); + if (_saveStream->err()) { + return STATUS_FAILED; + } + return STATUS_OK; + } else { + *val = _loadStream->readUint32LE(); + if (_loadStream->err()) { + return STATUS_FAILED; + } + return STATUS_OK; + } +} + + +////////////////////////////////////////////////////////////////////////// +// float +bool BasePersistenceManager::transfer(const char *name, float *val) { + if (_saving) { + putFloat(*val); + if (_saveStream->err()) { + return STATUS_FAILED; + } + return STATUS_OK; + } else { + *val = getFloat(); + if (_loadStream->err()) { + return STATUS_FAILED; + } + return STATUS_OK; + } +} + + +////////////////////////////////////////////////////////////////////////// +// double +bool BasePersistenceManager::transfer(const char *name, double *val) { + if (_saving) { + putDouble(*val); + if (_saveStream->err()) { + return STATUS_FAILED; + } + return STATUS_OK; + } else { + *val = getDouble(); + if (_loadStream->err()) { + return STATUS_FAILED; + } + return STATUS_OK; + } +} + + +////////////////////////////////////////////////////////////////////////// +// char* +bool BasePersistenceManager::transfer(const char *name, char **val) { + if (_saving) { + putString(*val); + return STATUS_OK; + } else { + char *str = getString(); + if (_loadStream->err()) { + delete[] str; + return STATUS_FAILED; + } + *val = str; + return STATUS_OK; + } +} + +////////////////////////////////////////////////////////////////////////// +// const char* +bool BasePersistenceManager::transfer(const char *name, const char **val) { + if (_saving) { + putString(*val); + return STATUS_OK; + } else { + char *str = getString(); + if (_loadStream->err()) { + delete[] str; + return STATUS_FAILED; + } + *val = str; + return STATUS_OK; + } +} + +////////////////////////////////////////////////////////////////////////// +// Common::String +bool BasePersistenceManager::transfer(const char *name, Common::String *val) { + if (_saving) { + putString(*val); + return STATUS_OK; + } else { + char *str = getString(); + if (_loadStream->err()) { + delete[] str; + return STATUS_FAILED; + } + if (str) { + *val = str; + delete[] str; + } else { + *val = ""; + } + return STATUS_OK; + } +} + +////////////////////////////////////////////////////////////////////////// +bool BasePersistenceManager::transfer(const char *name, AnsiStringArray &val) { + size_t size; + + if (_saving) { + size = val.size(); + _saveStream->writeUint32LE(size); + + for (AnsiStringArray::iterator it = val.begin(); it != val.end(); ++it) { + putString((*it).c_str()); + } + } else { + val.clear(); + size = _loadStream->readUint32LE(); + + for (size_t i = 0; i < size; i++) { + char *str = getString(); + if (_loadStream->err()) { + delete[] str; + return STATUS_FAILED; + } + if (str) { + val.push_back(str); + } + delete[] str; + } + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +// BYTE +bool BasePersistenceManager::transfer(const char *name, byte *val) { + if (_saving) { + _saveStream->writeByte(*val); + if (_saveStream->err()) { + return STATUS_FAILED; + } + return STATUS_OK; + } else { + *val = _loadStream->readByte(); + if (_loadStream->err()) { + return STATUS_FAILED; + } + return STATUS_OK; + } +} + + +////////////////////////////////////////////////////////////////////////// +// RECT +bool BasePersistenceManager::transfer(const char *name, Rect32 *val) { + if (_saving) { + _saveStream->writeSint32LE(val->left); + _saveStream->writeSint32LE(val->top); + _saveStream->writeSint32LE(val->right); + _saveStream->writeSint32LE(val->bottom); + if (_saveStream->err()) { + return STATUS_FAILED; + } + return STATUS_OK; + } else { + val->left = _loadStream->readSint32LE(); + val->top = _loadStream->readSint32LE(); + val->right = _loadStream->readSint32LE(); + val->bottom = _loadStream->readSint32LE(); + if (_loadStream->err()) { + return STATUS_FAILED; + } + return STATUS_OK; + } +} + + +////////////////////////////////////////////////////////////////////////// +// POINT +bool BasePersistenceManager::transfer(const char *name, Point32 *val) { + if (_saving) { + _saveStream->writeSint32LE(val->x); + _saveStream->writeSint32LE(val->y); + if (_saveStream->err()) { + return STATUS_FAILED; + } + return STATUS_OK; + } else { + val->x = _loadStream->readSint32LE(); + val->y = _loadStream->readSint32LE(); + if (_loadStream->err()) { + return STATUS_FAILED; + } + return STATUS_OK; + } +} + + +////////////////////////////////////////////////////////////////////////// +// Vector2 +bool BasePersistenceManager::transfer(const char *name, Vector2 *val) { + if (_saving) { + putFloat(val->x); + putFloat(val->y); + if (_saveStream->err()) { + return STATUS_FAILED; + } + return STATUS_OK; + } else { + val->x = getFloat(); + val->y = getFloat(); + if (_loadStream->err()) { + return STATUS_FAILED; + } + return STATUS_OK; + } +} + + +////////////////////////////////////////////////////////////////////////// +// generic pointer +bool BasePersistenceManager::transfer(const char *name, void *val) { + int classID = -1, instanceID = -1; + + if (_saving) { + SystemClassRegistry::getInstance()->getPointerID(*(void **)val, &classID, &instanceID); + if (*(void **)val != NULL && (classID == -1 || instanceID == -1)) { + debugC(kWintermuteDebugSaveGame, "Warning: invalid instance '%s'", name); + } + + _saveStream->writeUint32LE(classID); + _saveStream->writeUint32LE(instanceID); + } else { + classID = _loadStream->readUint32LE(); + instanceID = _loadStream->readUint32LE(); + + *(void **)val = SystemClassRegistry::getInstance()->idToPointer(classID, instanceID); + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BasePersistenceManager::checkVersion(byte verMajor, byte verMinor, byte verBuild) { + if (_saving) { + return true; + } + + // it's ok if we are same or newer than the saved game + if (verMajor > _savedVerMajor || + (verMajor == _savedVerMajor && verMinor > _savedVerMinor) || + (verMajor == _savedVerMajor && verMinor == _savedVerMinor && verBuild > _savedVerBuild) + ) { + return false; + } + + return true; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_persistence_manager.h b/engines/wintermute/base/base_persistence_manager.h new file mode 100644 index 0000000000..114f6e066f --- /dev/null +++ b/engines/wintermute/base/base_persistence_manager.h @@ -0,0 +1,120 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_PERSISTENCE_MANAGER_H +#define WINTERMUTE_BASE_PERSISTENCE_MANAGER_H + + +#include "engines/wintermute/dctypes.h" +#include "engines/wintermute/math/rect32.h" +#include "engines/savestate.h" +#include "common/stream.h" +#include "common/str.h" +#include "common/system.h" +#include "common/rect.h" + +namespace Wintermute { + +class Vector2; +class BaseGame; +class BasePersistenceManager { +public: + char *_savedDescription; + Common::String _savePrefix; + Common::String _savedName; + bool saveFile(const Common::String &filename); + uint32 getDWORD(); + void putDWORD(uint32 val); + char *getString(); + Common::String getStringObj(); + void putString(const Common::String &val); + float getFloat(); + void putFloat(float val); + double getDouble(); + void putDouble(double val); + void cleanup(); + void getSaveStateDesc(int slot, SaveStateDescriptor &desc); + void deleteSaveSlot(int slot); + uint32 getMaxUsedSlot(); + bool getSaveExists(int slot); + bool initLoad(const Common::String &filename); + bool initSave(const char *desc); + bool getBytes(byte *buffer, uint32 size); + bool putBytes(byte *buffer, uint32 size); + uint32 _offset; + + bool getIsSaving() { return _saving; } + + uint32 _richBufferSize; + byte *_richBuffer; + + bool transfer(const char *name, void *val); + bool transfer(const char *name, int *val); + bool transfer(const char *name, uint32 *val); + bool transfer(const char *name, float *val); + bool transfer(const char *name, double *val); + bool transfer(const char *name, bool *val); + bool transfer(const char *name, byte *val); + bool transfer(const char *name, Rect32 *val); + bool transfer(const char *name, Point32 *val); + bool transfer(const char *name, const char **val); + bool transfer(const char *name, char **val); + bool transfer(const char *name, Common::String *val); + bool transfer(const char *name, Vector2 *val); + bool transfer(const char *name, AnsiStringArray &Val); + BasePersistenceManager(const char *savePrefix = NULL, bool deleteSingleton = false); + virtual ~BasePersistenceManager(); + bool checkVersion(byte verMajor, byte verMinor, byte verBuild); + + uint32 _thumbnailDataSize; + byte *_thumbnailData; + uint32 _scummVMThumbSize; + byte *_scummVMThumbnailData; + Common::String getFilenameForSlot(int slot) const; +private: + bool _deleteSingleton; + bool readHeader(const Common::String &filename); + TimeDate getTimeDate(); + bool putTimeDate(const TimeDate &t); + Common::WriteStream *_saveStream; + Common::SeekableReadStream *_loadStream; + TimeDate _savedTimestamp; + uint32 _savedPlayTime; + byte _savedVerMajor; + byte _savedVerMinor; + byte _savedVerBuild; + byte _savedExtMajor; + byte _savedExtMinor; + bool _saving; + // Separate from Base, as this class can do SOME operations without a _gameRef. + BaseGame *_gameRef; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_point.cpp b/engines/wintermute/base/base_point.cpp new file mode 100644 index 0000000000..fbd8960894 --- /dev/null +++ b/engines/wintermute/base/base_point.cpp @@ -0,0 +1,63 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_point.h" +#include "engines/wintermute/base/base_persistence_manager.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(BasePoint, false) + +////////////////////////////////////////////////////////////////////////// +BasePoint::BasePoint() { + x = y = 0; +} + + +////////////////////////////////////////////////////////////////////////// +BasePoint::~BasePoint() { + +} + + +////////////////////////////////////////////////////////////////////////// +BasePoint::BasePoint(int initX, int initY) { + x = initX; + y = initY; +} + +////////////////////////////////////////////////////////////////////////// +bool BasePoint::persist(BasePersistenceManager *persistMgr) { + + persistMgr->transfer(TMEMBER(x)); + persistMgr->transfer(TMEMBER(y)); + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_point.h b/engines/wintermute/base/base_point.h new file mode 100644 index 0000000000..c0bbd3102e --- /dev/null +++ b/engines/wintermute/base/base_point.h @@ -0,0 +1,50 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_POINT_H +#define WINTERMUTE_BASE_POINT_H + +#include "engines/wintermute/persistent.h" +#include "engines/wintermute/base/base.h" + +namespace Wintermute { + +class BasePoint: public BaseClass { +public: + DECLARE_PERSISTENT(BasePoint, BaseClass) + BasePoint(); + BasePoint(int initX, int initY); + int y; + int x; + virtual ~BasePoint(); + +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_quick_msg.cpp b/engines/wintermute/base/base_quick_msg.cpp new file mode 100644 index 0000000000..0a9907ac6b --- /dev/null +++ b/engines/wintermute/base/base_quick_msg.cpp @@ -0,0 +1,57 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_quick_msg.h" +#include "engines/wintermute/base/base_game.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////////// +BaseQuickMsg::BaseQuickMsg(BaseGame *inGame, const char *text) : BaseClass(inGame) { + _text = new char [strlen(text) + 1]; + if (_text) { + strcpy(_text, text); + } + _startTime = _gameRef->_currentTime; +} + + +////////////////////////////////////////////////////////////////////////// +BaseQuickMsg::~BaseQuickMsg() { + if (_text) { + delete[] _text; + } +} + + +////////////////////////////////////////////////////////////////////////// +char *BaseQuickMsg::getText() { + return _text; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_quick_msg.h b/engines/wintermute/base/base_quick_msg.h new file mode 100644 index 0000000000..67f9613461 --- /dev/null +++ b/engines/wintermute/base/base_quick_msg.h @@ -0,0 +1,48 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_QUICKMSG_H +#define WINTERMUTE_BASE_QUICKMSG_H + +#include "engines/wintermute/base/base.h" + +namespace Wintermute { + +class BaseQuickMsg : public BaseClass { +public: + char *getText(); + uint32 _startTime; + BaseQuickMsg(BaseGame *inGame, const char *text); + virtual ~BaseQuickMsg(); +private: + char *_text; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_region.cpp b/engines/wintermute/base/base_region.cpp new file mode 100644 index 0000000000..0bc5975e51 --- /dev/null +++ b/engines/wintermute/base/base_region.cpp @@ -0,0 +1,535 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_region.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/platform_osystem.h" +#include <limits.h> + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(BaseRegion, false) + +////////////////////////////////////////////////////////////////////////// +BaseRegion::BaseRegion(BaseGame *inGame) : BaseObject(inGame) { + _active = true; + _editorSelectedPoint = -1; + _lastMimicScale = -1; + _lastMimicX = _lastMimicY = INT_MIN; + + BasePlatform::setRectEmpty(&_rect); +} + + +////////////////////////////////////////////////////////////////////////// +BaseRegion::~BaseRegion() { + cleanup(); +} + + +////////////////////////////////////////////////////////////////////////// +void BaseRegion::cleanup() { + for (uint32 i = 0; i < _points.size(); i++) { + delete _points[i]; + } + _points.clear(); + + BasePlatform::setRectEmpty(&_rect); + _editorSelectedPoint = -1; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseRegion::createRegion() { + return DID_SUCCEED(getBoundingRect(&_rect)); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseRegion::pointInRegion(int x, int y) { + if (_points.size() < 3) { + return false; + } + + Point32 pt; + pt.x = x; + pt.y = y; + + Rect32 rect; + rect.left = x - 1; + rect.right = x + 2; + rect.top = y - 1; + rect.bottom = y + 2; + + if (BasePlatform::ptInRect(&_rect, pt)) { + return ptInPolygon(x, y); + } else { + return false; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseRegion::loadFile(const char *filename) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "BaseRegion::LoadFile failed for file '%s'", filename); + return STATUS_FAILED; + } + + bool ret; + + setFilename(filename); + + if (DID_FAIL(ret = loadBuffer(buffer, true))) { + _gameRef->LOG(0, "Error parsing REGION file '%s'", filename); + } + + + delete[] buffer; + + return ret; +} + + +TOKEN_DEF_START +TOKEN_DEF(REGION) +TOKEN_DEF(TEMPLATE) +TOKEN_DEF(NAME) +TOKEN_DEF(ACTIVE) +TOKEN_DEF(POINT) +TOKEN_DEF(CAPTION) +TOKEN_DEF(SCRIPT) +TOKEN_DEF(EDITOR_SELECTED_POINT) +TOKEN_DEF(PROPERTY) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool BaseRegion::loadBuffer(byte *buffer, bool complete) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(REGION) + TOKEN_TABLE(TEMPLATE) + TOKEN_TABLE(NAME) + TOKEN_TABLE(ACTIVE) + TOKEN_TABLE(POINT) + TOKEN_TABLE(CAPTION) + TOKEN_TABLE(SCRIPT) + TOKEN_TABLE(EDITOR_SELECTED_POINT) + TOKEN_TABLE(PROPERTY) + TOKEN_TABLE_END + + byte *params; + int cmd; + BaseParser parser; + + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_REGION) { + _gameRef->LOG(0, "'REGION' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + for (uint32 i = 0; i < _points.size(); i++) { + delete _points[i]; + } + _points.clear(); + + while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_TEMPLATE: + if (DID_FAIL(loadFile((char *)params))) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_NAME: + setName((char *)params); + break; + + case TOKEN_CAPTION: + setCaption((char *)params); + break; + + case TOKEN_ACTIVE: + parser.scanStr((char *)params, "%b", &_active); + break; + + case TOKEN_POINT: { + int x, y; + parser.scanStr((char *)params, "%d,%d", &x, &y); + _points.add(new BasePoint(x, y)); + } + break; + + case TOKEN_SCRIPT: + addScript((char *)params); + break; + + case TOKEN_EDITOR_SELECTED_POINT: + parser.scanStr((char *)params, "%d", &_editorSelectedPoint); + break; + + case TOKEN_PROPERTY: + parseProperty(params, false); + break; + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in REGION definition"); + return STATUS_FAILED; + } + + createRegion(); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool BaseRegion::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + + ////////////////////////////////////////////////////////////////////////// + // AddPoint + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "AddPoint") == 0) { + stack->correctParams(2); + int x = stack->pop()->getInt(); + int y = stack->pop()->getInt(); + + _points.add(new BasePoint(x, y)); + createRegion(); + + stack->pushBool(true); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // InsertPoint + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "InsertPoint") == 0) { + stack->correctParams(3); + int index = stack->pop()->getInt(); + int x = stack->pop()->getInt(); + int y = stack->pop()->getInt(); + + if (index >= 0 && index < (int32)_points.size()) { + _points.insert_at(index, new BasePoint(x, y)); + createRegion(); + + stack->pushBool(true); + } else { + stack->pushBool(false); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetPoint + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetPoint") == 0) { + stack->correctParams(3); + int index = stack->pop()->getInt(); + int x = stack->pop()->getInt(); + int y = stack->pop()->getInt(); + + if (index >= 0 && index < (int32)_points.size()) { + _points[index]->x = x; + _points[index]->y = y; + createRegion(); + + stack->pushBool(true); + } else { + stack->pushBool(false); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // RemovePoint + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "RemovePoint") == 0) { + stack->correctParams(1); + int index = stack->pop()->getInt(); + + if (index >= 0 && index < (int32)_points.size()) { + delete _points[index]; + _points[index] = NULL; + + _points.remove_at(index); + createRegion(); + + stack->pushBool(true); + } else { + stack->pushBool(false); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetPoint + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetPoint") == 0) { + stack->correctParams(1); + int index = stack->pop()->getInt(); + + if (index >= 0 && index < (int32)_points.size()) { + ScValue *val = stack->getPushValue(); + if (val) { + val->setProperty("X", _points[index]->x); + val->setProperty("Y", _points[index]->y); + } + } else { + stack->pushNULL(); + } + + return STATUS_OK; + } else { + return BaseObject::scCallMethod(script, stack, thisStack, name); + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *BaseRegion::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("region"); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Name + ////////////////////////////////////////////////////////////////////////// + else if (name == "Name") { + _scValue->setString(getName()); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Active + ////////////////////////////////////////////////////////////////////////// + else if (name == "Active") { + _scValue->setBool(_active); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // NumPoints + ////////////////////////////////////////////////////////////////////////// + else if (name == "NumPoints") { + _scValue->setInt(_points.size()); + return _scValue; + } else { + return BaseObject::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseRegion::scSetProperty(const char *name, ScValue *value) { + ////////////////////////////////////////////////////////////////////////// + // Name + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Name") == 0) { + setName(value->getString()); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Active + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Active") == 0) { + _active = value->getBool(); + return STATUS_OK; + } else { + return BaseObject::scSetProperty(name, value); + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *BaseRegion::scToString() { + return "[region]"; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseRegion::saveAsText(BaseDynamicBuffer *buffer, int indent, const char *nameOverride) { + if (!nameOverride) { + buffer->putTextIndent(indent, "REGION {\n"); + } else { + buffer->putTextIndent(indent, "%s {\n", nameOverride); + } + + buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", getName()); + buffer->putTextIndent(indent + 2, "CAPTION=\"%s\"\n", getCaption()); + buffer->putTextIndent(indent + 2, "ACTIVE=%s\n", _active ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "EDITOR_SELECTED_POINT=%d\n", _editorSelectedPoint); + + for (uint32 i = 0; i < _scripts.size(); i++) { + buffer->putTextIndent(indent + 2, "SCRIPT=\"%s\"\n", _scripts[i]->_filename); + } + + for (uint32 i = 0; i < _points.size(); i++) { + buffer->putTextIndent(indent + 2, "POINT {%d,%d}\n", _points[i]->x, _points[i]->y); + } + + if (_scProp) { + _scProp->saveAsText(buffer, indent + 2); + } + + buffer->putTextIndent(indent, "}\n\n"); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseRegion::persist(BasePersistenceManager *persistMgr) { + + BaseObject::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_active)); + persistMgr->transfer(TMEMBER(_editorSelectedPoint)); + persistMgr->transfer(TMEMBER(_lastMimicScale)); + persistMgr->transfer(TMEMBER(_lastMimicX)); + persistMgr->transfer(TMEMBER(_lastMimicY)); + _points.persist(persistMgr); + + return STATUS_OK; +} + + +typedef struct { + double x, y; +} dPoint; + +////////////////////////////////////////////////////////////////////////// +bool BaseRegion::ptInPolygon(int x, int y) { + if (_points.size() < 3) { + return false; + } + + int counter = 0; + double xinters; + dPoint p, p1, p2; + + p.x = (double)x; + p.y = (double)y; + + p1.x = (double)_points[0]->x; + p1.y = (double)_points[0]->y; + + for (uint32 i = 1; i <= _points.size(); i++) { + p2.x = (double)_points[i % _points.size()]->x; + p2.y = (double)_points[i % _points.size()]->y; + + if (p.y > MIN(p1.y, p2.y)) { + if (p.y <= MAX(p1.y, p2.y)) { + if (p.x <= MAX(p1.x, p2.x)) { + if (p1.y != p2.y) { + xinters = (p.y - p1.y) * (p2.x - p1.x) / (p2.y - p1.y) + p1.x; + if (p1.x == p2.x || p.x <= xinters) { + counter++; + } + } + } + } + } + p1 = p2; + } + + if (counter % 2 == 0) { + return false; + } else { + return true; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseRegion::getBoundingRect(Rect32 *rect) { + if (_points.size() == 0) { + BasePlatform::setRectEmpty(rect); + } else { + int minX = INT_MAX, minY = INT_MAX, maxX = INT_MIN, maxY = INT_MIN; + + for (uint32 i = 0; i < _points.size(); i++) { + minX = MIN(minX, _points[i]->x); + minY = MIN(minY, _points[i]->y); + + maxX = MAX(maxX, _points[i]->x); + maxY = MAX(maxY, _points[i]->y); + } + BasePlatform::setRect(rect, minX, minY, maxX, maxY); + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseRegion::mimic(BaseRegion *region, float scale, int x, int y) { + if (scale == _lastMimicScale && x == _lastMimicX && y == _lastMimicY) { + return STATUS_OK; + } + + cleanup(); + + for (uint32 i = 0; i < region->_points.size(); i++) { + int xVal, yVal; + + xVal = (int)((float)region->_points[i]->x * scale / 100.0f); + yVal = (int)((float)region->_points[i]->y * scale / 100.0f); + + _points.add(new BasePoint(xVal + x, yVal + y)); + } + + _lastMimicScale = scale; + _lastMimicX = x; + _lastMimicY = y; + + return createRegion() ? STATUS_OK : STATUS_FAILED; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_region.h b/engines/wintermute/base/base_region.h new file mode 100644 index 0000000000..464f25be2f --- /dev/null +++ b/engines/wintermute/base/base_region.h @@ -0,0 +1,70 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_REGION_H +#define WINTERMUTE_BASE_REGION_H + +#include "engines/wintermute/base/base_point.h" +#include "engines/wintermute/base/base_object.h" + +namespace Wintermute { + +class BaseRegion : public BaseObject { +public: + void cleanup(); + bool mimic(BaseRegion *region, float scale = 100.0f, int x = 0, int y = 0); + bool getBoundingRect(Rect32 *rect); + bool ptInPolygon(int x, int y); + DECLARE_PERSISTENT(BaseRegion, BaseObject) + bool _active; + int _editorSelectedPoint; + BaseRegion(BaseGame *inGame); + virtual ~BaseRegion(); + bool pointInRegion(int x, int y); + bool createRegion(); + bool loadFile(const char *filename); + bool loadBuffer(byte *buffer, bool complete = true); + Rect32 _rect; + BaseArray<BasePoint *> _points; + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent) { return saveAsText(buffer, indent, NULL); } + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent, const char *nameOverride); + + // scripting interface + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + virtual const char *scToString(); +private: + float _lastMimicScale; + int _lastMimicX; + int _lastMimicY; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_save_thumb_helper.cpp b/engines/wintermute/base/base_save_thumb_helper.cpp new file mode 100644 index 0000000000..3899ece59b --- /dev/null +++ b/engines/wintermute/base/base_save_thumb_helper.cpp @@ -0,0 +1,110 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_save_thumb_helper.h" +#include "engines/wintermute/base/gfx/base_image.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/base/base_game.h" +#include "graphics/scaler.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////////// +BaseSaveThumbHelper::BaseSaveThumbHelper(BaseGame *inGame) : BaseClass(inGame) { + _thumbnail = NULL; + _scummVMThumb = NULL; +} + +////////////////////////////////////////////////////////////////////////// +BaseSaveThumbHelper::~BaseSaveThumbHelper(void) { + delete _thumbnail; + _thumbnail = NULL; + delete _scummVMThumb; + _scummVMThumb = NULL; +} + +BaseImage *BaseSaveThumbHelper::storeThumb(bool doFlip, int width, int height) { + BaseImage *thumbnail = NULL; + if (_gameRef->_thumbnailWidth > 0 && _gameRef->_thumbnailHeight > 0) { + if (doFlip) { + // when using opengl on windows it seems to be necessary to do this twice + // works normally for direct3d + _gameRef->displayContent(false); + _gameRef->_renderer->flip(); + + _gameRef->displayContent(false); + _gameRef->_renderer->flip(); + } + + BaseImage *screenshot = _gameRef->_renderer->takeScreenshot(); + if (!screenshot) { + return NULL; + } + + // normal thumbnail + if (_gameRef->_thumbnailWidth > 0 && _gameRef->_thumbnailHeight > 0) { + thumbnail = new BaseImage(); + thumbnail->copyFrom(screenshot, width, height); + } + + + delete screenshot; + screenshot = NULL; + } + return thumbnail; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSaveThumbHelper::storeThumbnail(bool doFlip) { + delete _thumbnail; + _thumbnail = NULL; + + if (_gameRef->_thumbnailWidth > 0 && _gameRef->_thumbnailHeight > 0) { + + _thumbnail = storeThumb(doFlip, _gameRef->_thumbnailWidth, _gameRef->_thumbnailHeight); + if (!_thumbnail) { + return STATUS_FAILED; + } + } + storeScummVMThumbNail(); + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSaveThumbHelper::storeScummVMThumbNail(bool doFlip) { + delete _scummVMThumb; + _scummVMThumb = NULL; + + _scummVMThumb = storeThumb(doFlip, kThumbnailWidth, kThumbnailHeight2); + if (!_scummVMThumb) { + return STATUS_FAILED; + } + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_save_thumb_helper.h b/engines/wintermute/base/base_save_thumb_helper.h new file mode 100644 index 0000000000..8e8a7183c2 --- /dev/null +++ b/engines/wintermute/base/base_save_thumb_helper.h @@ -0,0 +1,54 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ +#ifndef WINTERMUTE_BASE_SAVE_THUMB_HELPER_H +#define WINTERMUTE_BASE_SAVE_THUMB_HELPER_H + + +#include "engines/wintermute/base/base.h" + +namespace Wintermute { + +class BaseImage; + +class BaseSaveThumbHelper : public BaseClass { +public: + BaseSaveThumbHelper(BaseGame *inGame); + virtual ~BaseSaveThumbHelper(void); + bool storeThumbnail(bool doFlip = false); + bool storeScummVMThumbNail(bool doFlip = false); + + BaseImage *_thumbnail; + BaseImage *_scummVMThumb; +private: + BaseImage *storeThumb(bool doFlip, int width, int height); + BaseImage *_richThumbnail; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_script_holder.cpp b/engines/wintermute/base/base_script_holder.cpp new file mode 100644 index 0000000000..abe5501b8d --- /dev/null +++ b/engines/wintermute/base/base_script_holder.cpp @@ -0,0 +1,502 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_game.h" +#include "engines/wintermute/base/base_script_holder.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script_engine.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/base/scriptables/script_stack.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(BaseScriptHolder, false) + +////////////////////////////////////////////////////////////////////// +BaseScriptHolder::BaseScriptHolder(BaseGame *inGame) : BaseScriptable(inGame) { + setName("<unnamed>"); + + _freezable = true; + _filename = NULL; +} + + +////////////////////////////////////////////////////////////////////// +BaseScriptHolder::~BaseScriptHolder() { + cleanup(); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseScriptHolder::cleanup() { + delete[] _filename; + _filename = NULL; + + for (uint32 i = 0; i < _scripts.size(); i++) { + _scripts[i]->finish(true); + _scripts[i]->_owner = NULL; + } + _scripts.clear(); + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////// +void BaseScriptHolder::setFilename(const char *filename) { + if (_filename != NULL) { + delete[] _filename; + _filename = NULL; + } + if (filename == NULL) { + return; + } + _filename = new char [strlen(filename) + 1]; + if (_filename != NULL) { + strcpy(_filename, filename); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseScriptHolder::applyEvent(const char *eventName, bool unbreakable) { + int numHandlers = 0; + + bool ret = STATUS_FAILED; + for (uint32 i = 0; i < _scripts.size(); i++) { + if (!_scripts[i]->_thread) { + ScScript *handler = _scripts[i]->invokeEventHandler(eventName, unbreakable); + if (handler) { + //_scripts.add(handler); + numHandlers++; + ret = STATUS_OK; + } + } + } + if (numHandlers > 0 && unbreakable) { + _gameRef->_scEngine->tickUnbreakable(); + } + + return ret; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseScriptHolder::listen(BaseScriptHolder *param1, uint32 param2) { + return STATUS_FAILED; +} + + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool BaseScriptHolder::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // DEBUG_CrashMe + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "DEBUG_CrashMe") == 0) { + stack->correctParams(0); + byte *p = 0; + *p = 10; + stack->pushNULL(); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ApplyEvent + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ApplyEvent") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + bool ret; + ret = applyEvent(val->getString()); + + if (DID_SUCCEED(ret)) { + stack->pushBool(true); + } else { + stack->pushBool(false); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // CanHandleEvent + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "CanHandleEvent") == 0) { + stack->correctParams(1); + stack->pushBool(canHandleEvent(stack->pop()->getString())); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // CanHandleMethod + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "CanHandleMethod") == 0) { + stack->correctParams(1); + stack->pushBool(canHandleMethod(stack->pop()->getString())); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // AttachScript + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AttachScript") == 0) { + stack->correctParams(1); + stack->pushBool(DID_SUCCEED(addScript(stack->pop()->getString()))); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // DetachScript + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "DetachScript") == 0) { + stack->correctParams(2); + const char *filename = stack->pop()->getString(); + bool killThreads = stack->pop()->getBool(false); + bool ret = false; + for (uint32 i = 0; i < _scripts.size(); i++) { + if (scumm_stricmp(_scripts[i]->_filename, filename) == 0) { + _scripts[i]->finish(killThreads); + ret = true; + break; + } + } + stack->pushBool(ret); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // IsScriptRunning + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "IsScriptRunning") == 0) { + stack->correctParams(1); + const char *filename = stack->pop()->getString(); + bool ret = false; + for (uint32 i = 0; i < _scripts.size(); i++) { + if (scumm_stricmp(_scripts[i]->_filename, filename) == 0 && _scripts[i]->_state != SCRIPT_FINISHED && _scripts[i]->_state != SCRIPT_ERROR) { + ret = true; + break; + } + } + stack->pushBool(ret); + + return STATUS_OK; + } else { + return BaseScriptable::scCallMethod(script, stack, thisStack, name); + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *BaseScriptHolder::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("script_holder"); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Name + ////////////////////////////////////////////////////////////////////////// + else if (name == "Name") { + _scValue->setString(getName()); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Filename (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "Filename") { + _scValue->setString(_filename); + return _scValue; + } else { + return BaseScriptable::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseScriptHolder::scSetProperty(const char *name, ScValue *value) { + ////////////////////////////////////////////////////////////////////////// + // Name + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Name") == 0) { + setName(value->getString()); + return STATUS_OK; + } else { + return BaseScriptable::scSetProperty(name, value); + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *BaseScriptHolder::scToString() { + return "[script_holder]"; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseScriptHolder::saveAsText(BaseDynamicBuffer *buffer, int indent) { + return BaseClass::saveAsText(buffer, indent); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseScriptHolder::persist(BasePersistenceManager *persistMgr) { + BaseScriptable::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_filename)); + persistMgr->transfer(TMEMBER(_freezable)); + if (persistMgr->getIsSaving()) { + const char *name = getName(); + persistMgr->transfer(TMEMBER(name)); + } else { + char *name; + persistMgr->transfer(TMEMBER(name)); + setName(name); + delete[] name; + } + _scripts.persist(persistMgr); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseScriptHolder::addScript(const char *filename) { + for (uint32 i = 0; i < _scripts.size(); i++) { + if (scumm_stricmp(_scripts[i]->_filename, filename) == 0) { + if (_scripts[i]->_state != SCRIPT_FINISHED) { + _gameRef->LOG(0, "BaseScriptHolder::AddScript - trying to add script '%s' mutiple times (obj: '%s')", filename, getName()); + return STATUS_OK; + } + } + } + + ScScript *scr = _gameRef->_scEngine->runScript(filename, this); + if (!scr) { + if (_gameRef->_editorForceScripts) { + // editor hack + scr = new ScScript(_gameRef, _gameRef->_scEngine); + scr->_filename = new char[strlen(filename) + 1]; + strcpy(scr->_filename, filename); + scr->_state = SCRIPT_ERROR; + scr->_owner = this; + _scripts.add(scr); + _gameRef->_scEngine->_scripts.add(scr); + + return STATUS_OK; + } + return STATUS_FAILED; + } else { + scr->_freezable = _freezable; + _scripts.add(scr); + return STATUS_OK; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseScriptHolder::removeScript(ScScript *script) { + for (uint32 i = 0; i < _scripts.size(); i++) { + if (_scripts[i] == script) { + _scripts.remove_at(i); + break; + } + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseScriptHolder::canHandleEvent(const char *EventName) const { + for (uint32 i = 0; i < _scripts.size(); i++) { + if (!_scripts[i]->_thread && _scripts[i]->canHandleEvent(EventName)) { + return true; + } + } + return false; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseScriptHolder::canHandleMethod(const char *MethodName) const { + for (uint32 i = 0; i < _scripts.size(); i++) { + if (!_scripts[i]->_thread && _scripts[i]->canHandleMethod(MethodName)) { + return true; + } + } + return false; +} + + +TOKEN_DEF_START +TOKEN_DEF(PROPERTY) +TOKEN_DEF(NAME) +TOKEN_DEF(VALUE) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool BaseScriptHolder::parseProperty(byte *buffer, bool complete) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(PROPERTY) + TOKEN_TABLE(NAME) + TOKEN_TABLE(VALUE) + TOKEN_TABLE_END + + byte *params; + int cmd; + BaseParser parser; + + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_PROPERTY) { + _gameRef->LOG(0, "'PROPERTY' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + char *propName = NULL; + char *propValue = NULL; + + while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_NAME: + delete[] propName; + propName = new char[strlen((char *)params) + 1]; + if (propName) { + strcpy(propName, (char *)params); + } else { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_VALUE: + delete[] propValue; + propValue = new char[strlen((char *)params) + 1]; + if (propValue) { + strcpy(propValue, (char *)params); + } else { + cmd = PARSERR_GENERIC; + } + break; + } + + } + if (cmd == PARSERR_TOKENNOTFOUND) { + delete[] propName; + delete[] propValue; + propName = NULL; + propValue = NULL; + _gameRef->LOG(0, "Syntax error in PROPERTY definition"); + return STATUS_FAILED; + } + if (cmd == PARSERR_GENERIC || propName == NULL || propValue == NULL) { + delete[] propName; + delete[] propValue; + propName = NULL; + propValue = NULL; + _gameRef->LOG(0, "Error loading PROPERTY definition"); + return STATUS_FAILED; + } + + + ScValue *val = new ScValue(_gameRef); + val->setString(propValue); + scSetProperty(propName, val); + + delete val; + delete[] propName; + delete[] propValue; + propName = NULL; + propValue = NULL; + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +void BaseScriptHolder::makeFreezable(bool freezable) { + _freezable = freezable; + for (uint32 i = 0; i < _scripts.size(); i++) { + _scripts[i]->_freezable = freezable; + } + +} + + +////////////////////////////////////////////////////////////////////////// +ScScript *BaseScriptHolder::invokeMethodThread(const char *methodName) { + for (int i = _scripts.size() - 1; i >= 0; i--) { + if (_scripts[i]->canHandleMethod(methodName)) { + + ScScript *thread = new ScScript(_gameRef, _scripts[i]->_engine); + if (thread) { + bool ret = thread->createMethodThread(_scripts[i], methodName); + if (DID_SUCCEED(ret)) { + _scripts[i]->_engine->_scripts.add(thread); + return thread; + } else { + delete thread; + } + } + } + } + return NULL; +} + + +////////////////////////////////////////////////////////////////////////// +void BaseScriptHolder::scDebuggerDesc(char *buf, int bufSize) { + strcpy(buf, scToString()); + if (getName() && strcmp(getName(), "<unnamed>") != 0) { + strcat(buf, " Name: "); + strcat(buf, getName()); + } + if (_filename) { + strcat(buf, " File: "); + strcat(buf, _filename); + } +} + + +////////////////////////////////////////////////////////////////////////// +// IWmeObject +////////////////////////////////////////////////////////////////////////// +bool BaseScriptHolder::sendEvent(const char *eventName) { + return DID_SUCCEED(applyEvent(eventName)); +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_script_holder.h b/engines/wintermute/base/base_script_holder.h new file mode 100644 index 0000000000..320f0fb07a --- /dev/null +++ b/engines/wintermute/base/base_script_holder.h @@ -0,0 +1,76 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_SCRIPTHOLDER_H +#define WINTERMUTE_BASE_SCRIPTHOLDER_H + +#include "engines/wintermute/coll_templ.h" +#include "engines/wintermute/persistent.h" +#include "engines/wintermute/base/base_scriptable.h" + +namespace Wintermute { + +class BaseScriptHolder : public BaseScriptable { +public: + DECLARE_PERSISTENT(BaseScriptHolder, BaseScriptable) + + BaseScriptHolder(BaseGame *inGame); + virtual ~BaseScriptHolder(); + virtual ScScript *invokeMethodThread(const char *methodName); + virtual void makeFreezable(bool freezable); + bool canHandleEvent(const char *eventName) const; + virtual bool canHandleMethod(const char *eventMethod) const; + bool cleanup(); + bool removeScript(ScScript *script); + bool addScript(const char *filename); + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent); + virtual bool listen(BaseScriptHolder *param1, uint32 param2); + bool applyEvent(const char *eventName, bool unbreakable = false); + void setFilename(const char *filename); + const char *getFilename() { return _filename; } + bool parseProperty(byte *buffer, bool complete = true); + bool _freezable; + bool _ready; + + BaseArray<ScScript *> _scripts; + // scripting interface + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + virtual const char *scToString(); + virtual void scDebuggerDesc(char *buf, int bufSize); + // IWmeObject +private: + char *_filename; +public: + virtual bool sendEvent(const char *eventName); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_scriptable.cpp b/engines/wintermute/base/base_scriptable.cpp new file mode 100644 index 0000000000..8c5ef7e45a --- /dev/null +++ b/engines/wintermute/base/base_scriptable.cpp @@ -0,0 +1,191 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_scriptable.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/base_persistence_manager.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(BaseScriptable, false) + +////////////////////////////////////////////////////////////////////////// +BaseScriptable::BaseScriptable(BaseGame *inGame, bool noValue, bool persistable) : BaseNamedObject(inGame) { + _refCount = 0; + + if (noValue) { + _scValue = NULL; + } else { + _scValue = new ScValue(_gameRef); + } + + _persistable = persistable; + + _scProp = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +BaseScriptable::~BaseScriptable() { + //if (_refCount>0) _gameRef->LOG(0, "Warning: Destroying object, _refCount=%d", _refCount); + delete _scValue; + delete _scProp; + _scValue = NULL; + _scProp = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool BaseScriptable::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + /* + stack->correctParams(0); + stack->pushNULL(); + script->runtimeError("Call to undefined method '%s'.", name); + + return STATUS_OK; + */ + return STATUS_FAILED; +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *BaseScriptable::scGetProperty(const Common::String &name) { + if (!_scProp) { + _scProp = new ScValue(_gameRef); + } + if (_scProp) { + return _scProp->getProp(name.c_str()); // TODO: Change to Common::String + } else { + return NULL; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseScriptable::scSetProperty(const char *name, ScValue *value) { + if (!_scProp) { + _scProp = new ScValue(_gameRef); + } + if (_scProp) { + return _scProp->setProp(name, value); + } else { + return STATUS_FAILED; + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *BaseScriptable::scToString() { + return "[native object]"; +} + +////////////////////////////////////////////////////////////////////////// +void *BaseScriptable::scToMemBuffer() { + return (void *)NULL; +} + + +////////////////////////////////////////////////////////////////////////// +int BaseScriptable::scToInt() { + return 0; +} + + +////////////////////////////////////////////////////////////////////////// +double BaseScriptable::scToFloat() { + return 0.0f; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseScriptable::scToBool() { + return false; +} + + +////////////////////////////////////////////////////////////////////////// +void BaseScriptable::scSetString(const char *val) { +} + + +////////////////////////////////////////////////////////////////////////// +void BaseScriptable::scSetInt(int val) { +} + + +////////////////////////////////////////////////////////////////////////// +void BaseScriptable::scSetFloat(double val) { +} + + +////////////////////////////////////////////////////////////////////////// +void BaseScriptable::scSetBool(bool val) { +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseScriptable::persist(BasePersistenceManager *persistMgr) { + persistMgr->transfer(TMEMBER(_gameRef)); + persistMgr->transfer(TMEMBER(_refCount)); + persistMgr->transfer(TMEMBER(_scProp)); + persistMgr->transfer(TMEMBER(_scValue)); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +int BaseScriptable::scCompare(BaseScriptable *val) { + if (this < val) { + return -1; + } else if (this > val) { + return 1; + } else { + return 0; + } +} + +////////////////////////////////////////////////////////////////////////// +void BaseScriptable::scDebuggerDesc(char *buf, int bufSize) { + strcpy(buf, scToString()); +} + +////////////////////////////////////////////////////////////////////////// +bool BaseScriptable::canHandleMethod(const char *eventMethod) const { + return false; +} + + +////////////////////////////////////////////////////////////////////////// +ScScript *BaseScriptable::invokeMethodThread(const char *methodName) { + return NULL; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_scriptable.h b/engines/wintermute/base/base_scriptable.h new file mode 100644 index 0000000000..b0b4e77ed2 --- /dev/null +++ b/engines/wintermute/base/base_scriptable.h @@ -0,0 +1,83 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_SCRIPTABLE_H +#define WINTERMUTE_BASE_SCRIPTABLE_H + + +#include "engines/wintermute/base/base_named_object.h" +#include "engines/wintermute/persistent.h" + +namespace Wintermute { + +class ScValue; +class ScStack; +class ScScript; + +class BaseScriptable : public BaseNamedObject { +public: + virtual ScScript *invokeMethodThread(const char *methodName); + DECLARE_PERSISTENT(BaseScriptable, BaseNamedObject) + + BaseScriptable(BaseGame *inGame, bool noValue = false, bool persistable = true); + virtual ~BaseScriptable(); + + // high level scripting interface + virtual bool canHandleMethod(const char *eventMethod) const; + virtual bool scSetProperty(const char *name, ScValue *value); + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + virtual const char *scToString(); + virtual void *scToMemBuffer(); + virtual int scToInt(); + virtual double scToFloat(); + virtual bool scToBool(); + virtual void scSetString(const char *val); + virtual void scSetInt(int val); + virtual void scSetFloat(double val); + virtual void scSetBool(bool val); + virtual int scCompare(BaseScriptable *val); + virtual void scDebuggerDesc(char *buf, int bufSize); + int _refCount; + ScValue *_scValue; + ScValue *_scProp; +}; + +// Implemented in their respective .cpp-files +BaseScriptable *makeSXArray(BaseGame *inGame, ScStack *stack); +BaseScriptable *makeSXDate(BaseGame *inGame, ScStack *stack); +BaseScriptable *makeSXFile(BaseGame *inGame, ScStack *stack); +BaseScriptable *makeSXMath(BaseGame *inGame); +BaseScriptable *makeSXMemBuffer(BaseGame *inGame, ScStack *stack); +BaseScriptable *makeSXObject(BaseGame *inGame, ScStack *stack); +BaseScriptable *makeSXStore(BaseGame *inGame); +BaseScriptable *makeSXString(BaseGame *inGame, ScStack *stack); + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_sprite.cpp b/engines/wintermute/base/base_sprite.cpp new file mode 100644 index 0000000000..468af1bd75 --- /dev/null +++ b/engines/wintermute/base/base_sprite.cpp @@ -0,0 +1,819 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/utils/path_util.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/gfx/base_surface.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_frame.h" +#include "engines/wintermute/base/sound/base_sound.h" +#include "engines/wintermute/base/base_sub_frame.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/platform_osystem.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/base/scriptables/script_stack.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(BaseSprite, false) + +////////////////////////////////////////////////////////////////////// +BaseSprite::BaseSprite(BaseGame *inGame, BaseObject *Owner) : BaseScriptHolder(inGame) { + _editorAllFrames = false; + _owner = Owner; + setDefaults(); +} + + +////////////////////////////////////////////////////////////////////// +BaseSprite::~BaseSprite() { + cleanup(); +} + + +////////////////////////////////////////////////////////////////////////// +void BaseSprite::setDefaults() { + _currentFrame = -1; + _looping = false; + _lastFrameTime = 0; + setFilename(NULL); + _finished = false; + _changed = false; + _paused = false; + _continuous = false; + _moveX = _moveY = 0; + + _editorMuted = false; + _editorBgFile = NULL; + _editorBgOffsetX = _editorBgOffsetY = 0; + _editorBgAlpha = 0xFF; + _streamed = false; + _streamedKeepLoaded = false; + + setName(""); + + _precise = true; +} + + +////////////////////////////////////////////////////////////////////////// +void BaseSprite::cleanup() { + BaseScriptHolder::cleanup(); + + for (uint32 i = 0; i < _frames.size(); i++) { + delete _frames[i]; + } + _frames.clear(); + + delete[] _editorBgFile; + _editorBgFile = NULL; + + setDefaults(); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSprite::draw(int x, int y, BaseObject *registerOwner, float zoomX, float zoomY, uint32 alpha) { + getCurrentFrame(zoomX, zoomY); + if (_currentFrame < 0 || _currentFrame >= (int32)_frames.size()) { + return STATUS_OK; + } + + // move owner if allowed to + if (_changed && _owner && _owner->_movable) { + _owner->_posX += _moveX; + _owner->_posY += _moveY; + _owner->afterMove(); + + x = _owner->_posX; + y = _owner->_posY; + } + + // draw frame + return display(x, y, registerOwner, zoomX, zoomY, alpha); +} + +bool BaseSprite::isChanged() { + return _changed; +} + +bool BaseSprite::isFinished() { + return _finished; +} + +////////////////////////////////////////////////////////////////////// +bool BaseSprite::loadFile(const Common::String &filename, int lifeTime, TSpriteCacheType cacheType) { + Common::SeekableReadStream *file = BaseFileManager::getEngineInstance()->openFile(filename); + if (!file) { + _gameRef->LOG(0, "BaseSprite::LoadFile failed for file '%s'", filename.c_str()); + if (_gameRef->_debugDebugMode) { + return loadFile("invalid_debug.bmp", lifeTime, cacheType); + } else { + return loadFile("invalid.bmp", lifeTime, cacheType); + } + } else { + BaseFileManager::getEngineInstance()->closeFile(file); + file = NULL; + } + + bool ret = STATUS_FAILED; + + AnsiString filePrefix = filename; + AnsiString ext = PathUtil::getExtension(filename); + ext.toLowercase(); + filePrefix.toLowercase(); + if (filePrefix.hasPrefix("savegame:") || (ext == "bmp") || (ext == "tga") || (ext == "png") || (ext == "jpg")) { + BaseFrame *frame = new BaseFrame(_gameRef); + BaseSubFrame *subframe = new BaseSubFrame(_gameRef); + subframe->setSurface(filename, true, 0, 0, 0, lifeTime, true); + if (subframe->_surface == NULL) { + _gameRef->LOG(0, "Error loading simple sprite '%s'", filename.c_str()); + ret = STATUS_FAILED; + delete frame; + delete subframe; + } else { + subframe->setDefaultRect(); + frame->_subframes.add(subframe); + _frames.add(frame); + _currentFrame = 0; + ret = STATUS_OK; + } + } else { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer) { + if (DID_FAIL(ret = loadBuffer(buffer, true, lifeTime, cacheType))) { + _gameRef->LOG(0, "Error parsing SPRITE file '%s'", filename.c_str()); + } else { + ret = STATUS_OK; + } + delete[] buffer; + } + } + + setFilename(filename.c_str()); + + return ret; +} + + + +TOKEN_DEF_START +TOKEN_DEF(CONTINUOUS) +TOKEN_DEF(SPRITE) +TOKEN_DEF(LOOPING) +TOKEN_DEF(FRAME) +TOKEN_DEF(NAME) +TOKEN_DEF(PRECISE) +TOKEN_DEF(EDITOR_MUTED) +TOKEN_DEF(STREAMED_KEEP_LOADED) +TOKEN_DEF(STREAMED) +TOKEN_DEF(SCRIPT) +TOKEN_DEF(EDITOR_BG_FILE) +TOKEN_DEF(EDITOR_BG_OFFSET_X) +TOKEN_DEF(EDITOR_BG_OFFSET_Y) +TOKEN_DEF(EDITOR_BG_ALPHA) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////// +bool BaseSprite::loadBuffer(byte *buffer, bool complete, int lifeTime, TSpriteCacheType cacheType) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(CONTINUOUS) + TOKEN_TABLE(SPRITE) + TOKEN_TABLE(LOOPING) + TOKEN_TABLE(FRAME) + TOKEN_TABLE(NAME) + TOKEN_TABLE(PRECISE) + TOKEN_TABLE(EDITOR_MUTED) + TOKEN_TABLE(STREAMED_KEEP_LOADED) + TOKEN_TABLE(STREAMED) + TOKEN_TABLE(SCRIPT) + TOKEN_TABLE(EDITOR_BG_FILE) + TOKEN_TABLE(EDITOR_BG_OFFSET_X) + TOKEN_TABLE(EDITOR_BG_OFFSET_Y) + TOKEN_TABLE(EDITOR_BG_ALPHA) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE_END + + byte *params; + int cmd; + BaseParser parser; + + cleanup(); + + + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_SPRITE) { + _gameRef->LOG(0, "'SPRITE' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + int frameCount = 1; + BaseFrame *frame; + while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_CONTINUOUS: + parser.scanStr((char *)params, "%b", &_continuous); + break; + + case TOKEN_EDITOR_MUTED: + parser.scanStr((char *)params, "%b", &_editorMuted); + break; + + case TOKEN_SCRIPT: + addScript((char *)params); + break; + + case TOKEN_LOOPING: + parser.scanStr((char *)params, "%b", &_looping); + break; + + case TOKEN_PRECISE: + parser.scanStr((char *)params, "%b", &_precise); + break; + + case TOKEN_STREAMED: + parser.scanStr((char *)params, "%b", &_streamed); + if (_streamed && lifeTime == -1) { + lifeTime = 500; + cacheType = CACHE_ALL; + } + break; + + case TOKEN_STREAMED_KEEP_LOADED: + parser.scanStr((char *)params, "%b", &_streamedKeepLoaded); + break; + + case TOKEN_NAME: + setName((char *)params); + break; + + case TOKEN_EDITOR_BG_FILE: + if (_gameRef->_editorMode) { + delete[] _editorBgFile; + _editorBgFile = new char[strlen((char *)params) + 1]; + if (_editorBgFile) { + strcpy(_editorBgFile, (char *)params); + } + } + break; + + case TOKEN_EDITOR_BG_OFFSET_X: + parser.scanStr((char *)params, "%d", &_editorBgOffsetX); + break; + + case TOKEN_EDITOR_BG_OFFSET_Y: + parser.scanStr((char *)params, "%d", &_editorBgOffsetY); + break; + + case TOKEN_EDITOR_BG_ALPHA: + parser.scanStr((char *)params, "%d", &_editorBgAlpha); + _editorBgAlpha = MIN(_editorBgAlpha, 255); + _editorBgAlpha = MAX(_editorBgAlpha, 0); + break; + + case TOKEN_FRAME: { + int frameLifeTime = lifeTime; + if (cacheType == CACHE_HALF && frameCount % 2 != 1) { + frameLifeTime = -1; + } + + frame = new BaseFrame(_gameRef); + + if (DID_FAIL(frame->loadBuffer(params, frameLifeTime, _streamedKeepLoaded))) { + delete frame; + _gameRef->LOG(0, "Error parsing frame %d", frameCount); + return STATUS_FAILED; + } + + _frames.add(frame); + frameCount++; + if (_currentFrame == -1) { + _currentFrame = 0; + } + } + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty(params, false); + break; + } + } + + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in SPRITE definition"); + return STATUS_FAILED; + } + _canBreak = !_continuous; + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////// +void BaseSprite::reset() { + if (_frames.size() > 0) { + _currentFrame = 0; + } else { + _currentFrame = -1; + } + + killAllSounds(); + + _lastFrameTime = 0; + _finished = false; + _moveX = _moveY = 0; +} + + +////////////////////////////////////////////////////////////////////// +bool BaseSprite::getCurrentFrame(float zoomX, float zoomY) { + //if (_owner && _owner->_freezable && _gameRef->_state == GAME_FROZEN) return true; + + if (_currentFrame == -1) { + return false; + } + + uint32 timer; + if (_owner && _owner->_freezable) { + timer = _gameRef->_timer; + } else { + timer = _gameRef->_liveTimer; + } + + int lastFrame = _currentFrame; + + // get current frame + if (!_paused && !_finished && timer >= _lastFrameTime + _frames[_currentFrame]->_delay && _lastFrameTime != 0) { + if (_currentFrame < (int32)_frames.size() - 1) { + _currentFrame++; + if (_continuous) { + _canBreak = (_currentFrame == (int32)_frames.size() - 1); + } + } else { + if (_looping) { + _currentFrame = 0; + _canBreak = true; + } else { + _finished = true; + _canBreak = true; + } + } + + _lastFrameTime = timer; + } + + _changed = (lastFrame != _currentFrame || (_looping && (int32)_frames.size() == 1)); + + if (_lastFrameTime == 0) { + _lastFrameTime = timer; + _changed = true; + if (_continuous) { + _canBreak = (_currentFrame == (int32)_frames.size() - 1); + } + } + + if (_changed) { + _moveX = _frames[_currentFrame]->_moveX; + _moveY = _frames[_currentFrame]->_moveY; + + if (zoomX != 100 || zoomY != 100) { + _moveX = (int)((float)_moveX * (float)(zoomX / 100.0f)); + _moveY = (int)((float)_moveY * (float)(zoomY / 100.0f)); + } + } + + return _changed; +} + + +////////////////////////////////////////////////////////////////////// +bool BaseSprite::display(int x, int y, BaseObject *registerVal, float zoomX, float zoomY, uint32 alpha, float rotate, TSpriteBlendMode blendMode) { + if (_currentFrame < 0 || _currentFrame >= (int32)_frames.size()) { + return STATUS_OK; + } + + // on change... + if (_changed) { + if (_frames[_currentFrame]->_killSound) { + killAllSounds(); + } + applyEvent("FrameChanged"); + _frames[_currentFrame]->oneTimeDisplay(_owner, _gameRef->_editorMode && _editorMuted); + } + + // draw frame + return _frames[_currentFrame]->draw(x - _gameRef->_offsetX, y - _gameRef->_offsetY, registerVal, zoomX, zoomY, _precise, alpha, _editorAllFrames, rotate, blendMode); +} + + +////////////////////////////////////////////////////////////////////////// +BaseSurface *BaseSprite::getSurface() { + // only used for animated textures for 3D models + if (_currentFrame < 0 || _currentFrame >= (int32)_frames.size()) { + return NULL; + } + BaseFrame *frame = _frames[_currentFrame]; + if (frame && frame->_subframes.size() > 0) { + BaseSubFrame *subframe = frame->_subframes[0]; + if (subframe) { + return subframe->_surface; + } else { + return NULL; + } + } else { + return NULL; + } +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSprite::getBoundingRect(Rect32 *rect, int x, int y, float scaleX, float scaleY) { + if (!rect) { + return false; + } + + BasePlatform::setRectEmpty(rect); + for (uint32 i = 0; i < _frames.size(); i++) { + Rect32 frame; + Rect32 temp; + BasePlatform::copyRect(&temp, rect); + _frames[i]->getBoundingRect(&frame, x, y, scaleX, scaleY); + BasePlatform::unionRect(rect, &temp, &frame); + } + return true; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSprite::saveAsText(BaseDynamicBuffer *buffer, int indent) { + buffer->putTextIndent(indent, "SPRITE {\n"); + buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", getName()); + buffer->putTextIndent(indent + 2, "LOOPING=%s\n", _looping ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "CONTINUOUS=%s\n", _continuous ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "PRECISE=%s\n", _precise ? "TRUE" : "FALSE"); + if (_streamed) { + buffer->putTextIndent(indent + 2, "STREAMED=%s\n", _streamed ? "TRUE" : "FALSE"); + + if (_streamedKeepLoaded) { + buffer->putTextIndent(indent + 2, "STREAMED_KEEP_LOADED=%s\n", _streamedKeepLoaded ? "TRUE" : "FALSE"); + } + } + + if (_editorMuted) { + buffer->putTextIndent(indent + 2, "EDITOR_MUTED=%s\n", _editorMuted ? "TRUE" : "FALSE"); + } + + if (_editorBgFile) { + buffer->putTextIndent(indent + 2, "EDITOR_BG_FILE=\"%s\"\n", _editorBgFile); + buffer->putTextIndent(indent + 2, "EDITOR_BG_OFFSET_X=%d\n", _editorBgOffsetX); + buffer->putTextIndent(indent + 2, "EDITOR_BG_OFFSET_Y=%d\n", _editorBgOffsetY); + buffer->putTextIndent(indent + 2, "EDITOR_BG_ALPHA=%d\n", _editorBgAlpha); + } + + BaseScriptHolder::saveAsText(buffer, indent + 2); + + // scripts + for (uint32 i = 0; i < _scripts.size(); i++) { + buffer->putTextIndent(indent + 2, "SCRIPT=\"%s\"\n", _scripts[i]->_filename); + } + + for (uint32 i = 0; i < _frames.size(); i++) { + _frames[i]->saveAsText(buffer, indent + 2); + } + + buffer->putTextIndent(indent, "}\n\n"); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSprite::persist(BasePersistenceManager *persistMgr) { + BaseScriptHolder::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_canBreak)); + persistMgr->transfer(TMEMBER(_changed)); + persistMgr->transfer(TMEMBER(_paused)); + persistMgr->transfer(TMEMBER(_continuous)); + persistMgr->transfer(TMEMBER(_currentFrame)); + persistMgr->transfer(TMEMBER(_editorAllFrames)); + persistMgr->transfer(TMEMBER(_editorBgAlpha)); + persistMgr->transfer(TMEMBER(_editorBgFile)); + persistMgr->transfer(TMEMBER(_editorBgOffsetX)); + persistMgr->transfer(TMEMBER(_editorBgOffsetY)); + persistMgr->transfer(TMEMBER(_editorMuted)); + persistMgr->transfer(TMEMBER(_finished)); + + _frames.persist(persistMgr); + + persistMgr->transfer(TMEMBER(_lastFrameTime)); + persistMgr->transfer(TMEMBER(_looping)); + persistMgr->transfer(TMEMBER(_moveX)); + persistMgr->transfer(TMEMBER(_moveY)); + persistMgr->transfer(TMEMBER(_owner)); + persistMgr->transfer(TMEMBER(_precise)); + persistMgr->transfer(TMEMBER(_streamed)); + persistMgr->transfer(TMEMBER(_streamedKeepLoaded)); + + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool BaseSprite::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // GetFrame + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "GetFrame") == 0) { + stack->correctParams(1); + int index = stack->pop()->getInt(-1); + if (index < 0 || index >= (int32)_frames.size()) { + script->runtimeError("Sprite.GetFrame: Frame index %d is out of range.", index); + stack->pushNULL(); + } else { + stack->pushNative(_frames[index], true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // DeleteFrame + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "DeleteFrame") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + if (val->isInt()) { + int index = val->getInt(-1); + if (index < 0 || index >= (int32)_frames.size()) { + script->runtimeError("Sprite.DeleteFrame: Frame index %d is out of range.", index); + } + } else { + BaseFrame *frame = (BaseFrame *)val->getNative(); + for (uint32 i = 0; i < _frames.size(); i++) { + if (_frames[i] == frame) { + if (i == (uint32)_currentFrame) { + _lastFrameTime = 0; + } + delete _frames[i]; + _frames.remove_at(i); + break; + } + } + } + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Reset + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Reset") == 0) { + stack->correctParams(0); + reset(); + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // AddFrame + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AddFrame") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + const char *filename = NULL; + if (!val->isNULL()) { + filename = val->getString(); + } + + BaseFrame *frame = new BaseFrame(_gameRef); + if (filename != NULL) { + BaseSubFrame *sub = new BaseSubFrame(_gameRef); + if (DID_SUCCEED(sub->setSurface(filename))) { + sub->setDefaultRect(); + frame->_subframes.add(sub); + } else { + delete sub; + } + } + _frames.add(frame); + + stack->pushNative(frame, true); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // InsertFrame + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "InsertFrame") == 0) { + stack->correctParams(2); + int index = stack->pop()->getInt(); + if (index < 0) { + index = 0; + } + + ScValue *val = stack->pop(); + const char *filename = NULL; + if (!val->isNULL()) { + filename = val->getString(); + } + + BaseFrame *frame = new BaseFrame(_gameRef); + if (filename != NULL) { + BaseSubFrame *sub = new BaseSubFrame(_gameRef); + if (DID_SUCCEED(sub->setSurface(filename))) { + frame->_subframes.add(sub); + } else { + delete sub; + } + } + + if (index >= (int32)_frames.size()) { + _frames.add(frame); + } else { + _frames.insert_at(index, frame); + } + + stack->pushNative(frame, true); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Pause + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Pause") == 0) { + stack->correctParams(0); + _paused = true; + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Play + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Play") == 0) { + stack->correctParams(0); + _paused = false; + stack->pushNULL(); + return STATUS_OK; + } else { + return BaseScriptHolder::scCallMethod(script, stack, thisStack, name); + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *BaseSprite::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("sprite"); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // NumFrames (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "NumFrames") { + _scValue->setInt(_frames.size()); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // CurrentFrame + ////////////////////////////////////////////////////////////////////////// + else if (name == "CurrentFrame") { + _scValue->setInt(_currentFrame); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // PixelPerfect + ////////////////////////////////////////////////////////////////////////// + else if (name == "PixelPerfect") { + _scValue->setBool(_precise); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Looping + ////////////////////////////////////////////////////////////////////////// + else if (name == "Looping") { + _scValue->setBool(_looping); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Owner (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "Owner") { + if (_owner == NULL) { + _scValue->setNULL(); + } else { + _scValue->setNative(_owner, true); + } + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Finished (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "Finished") { + _scValue->setBool(_finished); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Paused (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "Paused") { + _scValue->setBool(_paused); + return _scValue; + } else { + return BaseScriptHolder::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSprite::scSetProperty(const char *name, ScValue *value) { + ////////////////////////////////////////////////////////////////////////// + // CurrentFrame + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "CurrentFrame") == 0) { + _currentFrame = value->getInt(0); + if (_currentFrame >= (int32)_frames.size() || _currentFrame < 0) { + _currentFrame = -1; + } + _lastFrameTime = 0; + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // PixelPerfect + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "PixelPerfect") == 0) { + _precise = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Looping + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Looping") == 0) { + _looping = value->getBool(); + return STATUS_OK; + } else { + return BaseScriptHolder::scSetProperty(name, value); + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *BaseSprite::scToString() { + return "[sprite]"; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSprite::killAllSounds() { + for (uint32 i = 0; i < _frames.size(); i++) { + _frames[i]->stopSound(); + } + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_sprite.h b/engines/wintermute/base/base_sprite.h new file mode 100644 index 0000000000..1d244c3a52 --- /dev/null +++ b/engines/wintermute/base/base_sprite.h @@ -0,0 +1,93 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_SPRITE_H +#define WINTERMUTE_BASE_SPRITE_H + + +#include "engines/wintermute/coll_templ.h" +#include "engines/wintermute/base/base_script_holder.h" + +namespace Wintermute { +class BaseFrame; +class BaseSurface; +class BaseObject; +class BaseSprite: public BaseScriptHolder { +public: + BaseSurface *getSurface(); + void cleanup(); + void setDefaults(); + DECLARE_PERSISTENT(BaseSprite, BaseScriptHolder) + + bool getBoundingRect(Rect32 *rect, int x, int y, float scaleX = 100, float scaleY = 100); + int _moveY; + int _moveX; + bool display(int x, int y, BaseObject *registerOwner = NULL, float zoomX = 100, float zoomY = 100, uint32 alpha = 0xFFFFFFFF, float rotate = 0.0f, TSpriteBlendMode blendMode = BLEND_NORMAL); + bool getCurrentFrame(float zoomX = 100, float zoomY = 100); + void reset(); + bool isChanged(); + bool isFinished(); + bool loadBuffer(byte *buffer, bool compete = true, int lifeTime = -1, TSpriteCacheType cacheType = CACHE_ALL); + bool loadFile(const Common::String &filename, int lifeTime = -1, TSpriteCacheType cacheType = CACHE_ALL); + bool draw(int x, int y, BaseObject *Register = NULL, float zoomX = 100, float zoomY = 100, uint32 alpha = 0xFFFFFFFF); + bool _looping; + int _currentFrame; + bool addFrame(const char *filename, uint32 delay = 0, int hotspotX = 0, int hotspotY = 0, Rect32 *rect = NULL); + BaseSprite(BaseGame *inGame, BaseObject *owner = NULL); + virtual ~BaseSprite(); + BaseArray<BaseFrame *> _frames; + bool saveAsText(BaseDynamicBuffer *buffer, int indent); + + // scripting interface + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + virtual const char *scToString(); +private: + BaseObject *_owner; + bool _canBreak; + bool _changed; + bool _editorAllFrames; + char *_editorBgFile; + int _editorBgOffsetX; + int _editorBgOffsetY; + int _editorBgAlpha; + bool _editorMuted; + bool _finished; + bool _continuous; + uint32 _lastFrameTime; + bool _precise; + bool _paused; + bool _streamed; + bool _streamedKeepLoaded; + bool killAllSounds(); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_string_table.cpp b/engines/wintermute/base/base_string_table.cpp new file mode 100644 index 0000000000..2f890beea1 --- /dev/null +++ b/engines/wintermute/base/base_string_table.cpp @@ -0,0 +1,255 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/platform_osystem.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_string_table.h" +#include "common/str.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////////// +BaseStringTable::BaseStringTable(BaseGame *inGame) : BaseClass(inGame) { + +} + + +////////////////////////////////////////////////////////////////////////// +BaseStringTable::~BaseStringTable() { + // delete strings + _strings.clear(); + +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseStringTable::addString(const char *key, const char *val, bool reportDuplicities) { + if (key == NULL || val == NULL) { + return STATUS_FAILED; + } + + if (scumm_stricmp(key, "@right-to-left") == 0) { + _gameRef->_textRTL = true; + return STATUS_OK; + } + + Common::String finalKey = key; + finalKey.toLowercase(); + + StringsIter it = _strings.find(finalKey); + if (it != _strings.end() && reportDuplicities) { + _gameRef->LOG(0, " Warning: Duplicate definition of string '%s'.", finalKey.c_str()); + } + + _strings[finalKey] = val; + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +char *BaseStringTable::getKey(const char *str) const { + if (str == NULL || str[0] != '/') { + return NULL; + } + + const char *value = strchr(str + 1, '/'); + if (value == NULL) { + return NULL; + } + + char *key = new char[value - str]; + Common::strlcpy(key, str + 1, (size_t)(value - str)); + + BasePlatform::strlwr(key); + + char *newStr; + + StringsIter it = _strings.find(key); + if (it != _strings.end()) { + newStr = new char[it->_value.size() + 1]; + strcpy(newStr, it->_value.c_str()); + if (strlen(newStr) > 0 && newStr[0] == '/' && strchr(newStr + 1, '/')) { + delete[] key; + char *ret = getKey(newStr); + delete[] newStr; + return ret; + } else { + delete[] newStr; + return key; + } + } else { + return key; + } +} + +////////////////////////////////////////////////////////////////////////// +void BaseStringTable::expand(char **str) const { + if (str == NULL || *str == NULL || *str[0] != '/') { + return; + } + + char *value = strchr(*str + 1, '/'); + if (value == NULL) { + return; + } + + char *key = new char[value - *str]; + Common::strlcpy(key, *str + 1, (size_t)(value - *str)); + + BasePlatform::strlwr(key); + + value++; + + char *newStr; + + StringsIter it = _strings.find(key); + if (it != _strings.end()) { + newStr = new char[it->_value.size() + 1]; + strcpy(newStr, it->_value.c_str()); + } else { + newStr = new char[strlen(value) + 1]; + strcpy(newStr, value); + } + + delete[] key; + delete[] *str; + *str = newStr; + + if (strlen(*str) > 0 && *str[0] == '/') { + expand(str); + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *BaseStringTable::expandStatic(const char *string) const { + if (string == NULL || string[0] == '\0' || string[0] != '/') { + return string; + } + + const char *value = strchr(string + 1, '/'); + if (value == NULL) { + return string; + } + + char *key = new char[value - string]; + Common::strlcpy(key, string + 1, (size_t)(value - string - 1)); + BasePlatform::strlwr(key); + + value++; + + const char *newStr; + + StringsIter it = _strings.find(key); + if (it != _strings.end()) { + newStr = it->_value.c_str(); + } else { + newStr = value; + } + + delete[] key; + + if (strlen(newStr) > 0 && newStr[0] == '/') { + return expandStatic(newStr); + } else { + return newStr; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseStringTable::loadFile(const char *filename, bool clearOld) { + _gameRef->LOG(0, "Loading string table..."); + + if (clearOld) { + _strings.clear(); + } + + uint32 size; + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename, &size); + if (buffer == NULL) { + _gameRef->LOG(0, "BaseStringTable::LoadFile failed for file '%s'", filename); + return STATUS_FAILED; + } + + uint32 pos = 0; + + if (size > 3 && buffer[0] == 0xEF && buffer[1] == 0xBB && buffer[2] == 0xBF) { + pos += 3; + if (_gameRef->_textEncoding != TEXT_UTF8) { + _gameRef->_textEncoding = TEXT_UTF8; + //_gameRef->_textEncoding = TEXT_ANSI; + _gameRef->LOG(0, " UTF8 file detected, switching to UTF8 text encoding"); + } + } else { + _gameRef->_textEncoding = TEXT_ANSI; + } + + uint32 lineLength = 0; + while (pos < size) { + lineLength = 0; + while (pos + lineLength < size && buffer[pos + lineLength] != '\n' && buffer[pos + lineLength] != '\0') { + lineLength++; + } + + uint32 realLength = lineLength - (pos + lineLength >= size ? 0 : 1); + char *line = new char[realLength + 1]; + Common::strlcpy(line, (char *)&buffer[pos], realLength + 1); + char *value = strchr(line, '\t'); + if (value == NULL) { + value = strchr(line, ' '); + } + + if (line[0] != ';') { + if (value != NULL) { + value[0] = '\0'; + value++; + for (uint32 i = 0; i < strlen(value); i++) { + if (value[i] == '|') { + value[i] = '\n'; + } + } + addString(line, value, clearOld); + } else if (line[0] != '\0') { + addString(line, "", clearOld); + } + } + + delete[] line; + pos += lineLength + 1; + } + + delete[] buffer; + + _gameRef->LOG(0, " %d strings loaded", _strings.size()); + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_string_table.h b/engines/wintermute/base/base_string_table.h new file mode 100644 index 0000000000..128807bd1a --- /dev/null +++ b/engines/wintermute/base/base_string_table.h @@ -0,0 +1,55 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_STRING_TABLE_H +#define WINTERMUTE_BASE_STRING_TABLE_H + + +#include "common/hashmap.h" +#include "engines/wintermute/base/base.h" + +namespace Wintermute { + +class BaseStringTable : public BaseClass { +public: + bool loadFile(const char *filename, bool deleteAll = true); + void expand(char **str) const; + const char *expandStatic(const char *string) const; + bool addString(const char *key, const char *val, bool reportDuplicities = true); + BaseStringTable(BaseGame *inGame); + virtual ~BaseStringTable(); + char *getKey(const char *str) const; +private: + Common::HashMap<Common::String, Common::String> _strings; + typedef Common::HashMap<Common::String, Common::String>::const_iterator StringsIter; + +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_sub_frame.cpp b/engines/wintermute/base/base_sub_frame.cpp new file mode 100644 index 0000000000..77cc522ae7 --- /dev/null +++ b/engines/wintermute/base/base_sub_frame.cpp @@ -0,0 +1,659 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_sub_frame.h" +#include "engines/wintermute/base/base_active_rect.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/gfx/base_surface.h" +#include "engines/wintermute/base/base_surface_storage.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/platform_osystem.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script_stack.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(BaseSubFrame, false) + +////////////////////////////////////////////////////////////////////////// +BaseSubFrame::BaseSubFrame(BaseGame *inGame) : BaseScriptable(inGame, true) { + _surface = NULL; + _hotspotX = _hotspotY = 0; + _alpha = 0xFFFFFFFF; + _transparent = 0xFFFF00FF; + + _wantsDefaultRect = false; + BasePlatform::setRectEmpty(&_rect); + + _editorSelected = false; + + _surfaceFilename = NULL; + _cKDefault = true; + _cKRed = _cKBlue = _cKGreen = 0; + _lifeTime = -1; + _keepLoaded = false; + + _2DOnly = _3DOnly = false; + _decoration = false; + + _mirrorX = _mirrorY = false; +} + + +////////////////////////////////////////////////////////////////////////// +BaseSubFrame::~BaseSubFrame() { + if (_surface) { + _gameRef->_surfaceStorage->removeSurface(_surface); + } + delete[] _surfaceFilename; + _surfaceFilename = NULL; +} + + +TOKEN_DEF_START +TOKEN_DEF(IMAGE) +TOKEN_DEF(TRANSPARENT) +TOKEN_DEF(RECT) +TOKEN_DEF(HOTSPOT) +TOKEN_DEF(2D_ONLY) +TOKEN_DEF(3D_ONLY) +TOKEN_DEF(DECORATION) +TOKEN_DEF(ALPHA_COLOR) +TOKEN_DEF(ALPHA) +TOKEN_DEF(MIRROR_X) +TOKEN_DEF(MIRROR_Y) +TOKEN_DEF(EDITOR_SELECTED) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////// +bool BaseSubFrame::loadBuffer(byte *buffer, int lifeTime, bool keepLoaded) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(IMAGE) + TOKEN_TABLE(TRANSPARENT) + TOKEN_TABLE(RECT) + TOKEN_TABLE(HOTSPOT) + TOKEN_TABLE(2D_ONLY) + TOKEN_TABLE(3D_ONLY) + TOKEN_TABLE(DECORATION) + TOKEN_TABLE(ALPHA_COLOR) + TOKEN_TABLE(ALPHA) + TOKEN_TABLE(MIRROR_X) + TOKEN_TABLE(MIRROR_Y) + TOKEN_TABLE(EDITOR_SELECTED) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE_END + + char *params; + int cmd; + BaseParser parser; + Rect32 rect; + int r = 255, g = 255, b = 255; + int ar = 255, ag = 255, ab = 255, alpha = 255; + bool custoTrans = false; + BasePlatform::setRectEmpty(&rect); + char *surfaceFile = NULL; + + delete _surface; + _surface = NULL; + + while ((cmd = parser.getCommand((char **)&buffer, commands, ¶ms)) > 0) { + switch (cmd) { + case TOKEN_IMAGE: + surfaceFile = params; + break; + + case TOKEN_TRANSPARENT: + parser.scanStr(params, "%d,%d,%d", &r, &g, &b); + custoTrans = true; + break; + + case TOKEN_RECT: + parser.scanStr(params, "%d,%d,%d,%d", &rect.left, &rect.top, &rect.right, &rect.bottom); + break; + + case TOKEN_HOTSPOT: + parser.scanStr(params, "%d,%d", &_hotspotX, &_hotspotY); + break; + + case TOKEN_2D_ONLY: + parser.scanStr(params, "%b", &_2DOnly); + break; + + case TOKEN_3D_ONLY: + parser.scanStr(params, "%b", &_3DOnly); + break; + + case TOKEN_MIRROR_X: + parser.scanStr(params, "%b", &_mirrorX); + break; + + case TOKEN_MIRROR_Y: + parser.scanStr(params, "%b", &_mirrorY); + break; + + case TOKEN_DECORATION: + parser.scanStr(params, "%b", &_decoration); + break; + + case TOKEN_ALPHA_COLOR: + parser.scanStr(params, "%d,%d,%d", &ar, &ag, &ab); + break; + + case TOKEN_ALPHA: + parser.scanStr(params, "%d", &alpha); + break; + + case TOKEN_EDITOR_SELECTED: + parser.scanStr(params, "%b", &_editorSelected); + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty((byte *)params, false); + break; + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in SUBFRAME definition"); + return STATUS_FAILED; + } + + if (surfaceFile != NULL) { + if (custoTrans) { + setSurface(surfaceFile, false, r, g, b, lifeTime, keepLoaded); + } else { + setSurface(surfaceFile, true, 0, 0, 0, lifeTime, keepLoaded); + } + } + + _alpha = BYTETORGBA(ar, ag, ab, alpha); + if (custoTrans) { + _transparent = BYTETORGBA(r, g, b, 0xFF); + } + + /* + if (_surface == NULL) + { + _gameRef->LOG(0, "Error parsing sub-frame. Image not set."); + return STATUS_FAILED; + } + */ + if (BasePlatform::isRectEmpty(&rect)) { + setDefaultRect(); + } else { + setRect(rect); + } + + return STATUS_OK; +} + +Rect32 BaseSubFrame::getRect() { + if (_wantsDefaultRect && _surface) { + BasePlatform::setRect(&_rect, 0, 0, _surface->getWidth(), _surface->getHeight()); + _wantsDefaultRect = false; + } + return _rect; +} + +void BaseSubFrame::setRect(Rect32 rect) { + _wantsDefaultRect = false; + _rect = rect; +} + +const char* BaseSubFrame::getSurfaceFilename() { + return _surfaceFilename; +} + +////////////////////////////////////////////////////////////////////// +bool BaseSubFrame::draw(int x, int y, BaseObject *registerOwner, float zoomX, float zoomY, bool precise, uint32 alpha, float rotate, TSpriteBlendMode blendMode) { + if (!_surface) { + return STATUS_OK; + } + + if (registerOwner != NULL && !_decoration) { + if (zoomX == 100 && zoomY == 100) { + _gameRef->_renderer->addRectToList(new BaseActiveRect(_gameRef, registerOwner, this, x - _hotspotX + getRect().left, y - _hotspotY + getRect().top, getRect().right - getRect().left, getRect().bottom - getRect().top, zoomX, zoomY, precise)); + } else { + _gameRef->_renderer->addRectToList(new BaseActiveRect(_gameRef, registerOwner, this, (int)(x - (_hotspotX + getRect().left) * (zoomX / 100)), (int)(y - (_hotspotY + getRect().top) * (zoomY / 100)), (int)((getRect().right - getRect().left) * (zoomX / 100)), (int)((getRect().bottom - getRect().top) * (zoomY / 100)), zoomX, zoomY, precise)); + } + } + if (_gameRef->_suspendedRendering) { + return STATUS_OK; + } + + bool res; + + //if (Alpha==0xFFFFFFFF) Alpha = _alpha; // TODO: better (combine owner's and self alpha) + if (_alpha != 0xFFFFFFFF) { + alpha = _alpha; + } + + if (rotate != 0.0f) { + res = _surface->displayTransform((int)(x - _hotspotX * (zoomX / 100)), (int)(y - _hotspotY * (zoomY / 100)), _hotspotX, _hotspotY, getRect(), zoomX, zoomY, alpha, rotate, blendMode, _mirrorX, _mirrorY); + } else { + if (zoomX == 100 && zoomY == 100) { + res = _surface->displayTrans(x - _hotspotX, y - _hotspotY, getRect(), alpha, blendMode, _mirrorX, _mirrorY); + } else { + res = _surface->displayTransZoom((int)(x - _hotspotX * (zoomX / 100)), (int)(y - _hotspotY * (zoomY / 100)), getRect(), zoomX, zoomY, alpha, blendMode, _mirrorX, _mirrorY); + } + } + + return res; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSubFrame::getBoundingRect(Rect32 *rect, int x, int y, float scaleX, float scaleY) { + if (!rect) { + return false; + } + + float ratioX = scaleX / 100.0f; + float ratioY = scaleY / 100.0f; + + BasePlatform::setRect(rect, + (int)(x - _hotspotX * ratioX), + (int)(y - _hotspotY * ratioY), + (int)(x - _hotspotX * ratioX + (getRect().right - getRect().left) * ratioX), + (int)(y - _hotspotY * ratioY + (getRect().bottom - getRect().top) * ratioY)); + return true; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSubFrame::saveAsText(BaseDynamicBuffer *buffer, int indent, bool complete) { + if (complete) { + buffer->putTextIndent(indent, "SUBFRAME {\n"); + } + + if (_surface && _surface->getFileNameStr() != "") { + buffer->putTextIndent(indent + 2, "IMAGE = \"%s\"\n", _surface->getFileName()); + } + + if (_transparent != 0xFFFF00FF) { + buffer->putTextIndent(indent + 2, "TRANSPARENT { %d,%d,%d }\n", RGBCOLGetR(_transparent), RGBCOLGetG(_transparent), RGBCOLGetB(_transparent)); + } + + Rect32 rect; + BasePlatform::setRectEmpty(&rect); + if (_surface) { + BasePlatform::setRect(&rect, 0, 0, _surface->getWidth(), _surface->getHeight()); + } + if (!(rect == getRect())) { + buffer->putTextIndent(indent + 2, "RECT { %d,%d,%d,%d }\n", getRect().left, getRect().top, getRect().right, getRect().bottom); + } + + if (_hotspotX != 0 || _hotspotY != 0) { + buffer->putTextIndent(indent + 2, "HOTSPOT {%d, %d}\n", _hotspotX, _hotspotY); + } + + if (_alpha != 0xFFFFFFFF) { + buffer->putTextIndent(indent + 2, "ALPHA_COLOR { %d,%d,%d }\n", RGBCOLGetR(_alpha), RGBCOLGetG(_alpha), RGBCOLGetB(_alpha)); + buffer->putTextIndent(indent + 2, "ALPHA = %d\n", RGBCOLGetA(_alpha)); + } + + if (_mirrorX) { + buffer->putTextIndent(indent + 2, "MIRROR_X=%s\n", _mirrorX ? "TRUE" : "FALSE"); + } + + if (_mirrorY) { + buffer->putTextIndent(indent + 2, "MIRROR_Y=%s\n", _mirrorY ? "TRUE" : "FALSE"); + } + + if (_2DOnly) { + buffer->putTextIndent(indent + 2, "2D_ONLY=%s\n", _2DOnly ? "TRUE" : "FALSE"); + } + + if (_3DOnly) { + buffer->putTextIndent(indent + 2, "3D_ONLY=%s\n", _3DOnly ? "TRUE" : "FALSE"); + } + + if (_decoration) { + buffer->putTextIndent(indent + 2, "DECORATION=%s\n", _decoration ? "TRUE" : "FALSE"); + } + + if (_editorSelected) { + buffer->putTextIndent(indent + 2, "EDITOR_SELECTED=%s\n", _editorSelected ? "TRUE" : "FALSE"); + } + + BaseClass::saveAsText(buffer, indent + 2); + + + if (complete) { + buffer->putTextIndent(indent, "}\n\n"); + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +void BaseSubFrame::setDefaultRect() { + if (_surface) { + _wantsDefaultRect = true; + } else { + _wantsDefaultRect = false; + BasePlatform::setRectEmpty(&_rect); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSubFrame::persist(BasePersistenceManager *persistMgr) { + + BaseScriptable::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_2DOnly)); + persistMgr->transfer(TMEMBER(_3DOnly)); + persistMgr->transfer(TMEMBER(_alpha)); + persistMgr->transfer(TMEMBER(_decoration)); + persistMgr->transfer(TMEMBER(_editorSelected)); + persistMgr->transfer(TMEMBER(_hotspotX)); + persistMgr->transfer(TMEMBER(_hotspotY)); + persistMgr->transfer(TMEMBER(_rect)); + persistMgr->transfer(TMEMBER(_wantsDefaultRect)); + + persistMgr->transfer(TMEMBER(_surfaceFilename)); + persistMgr->transfer(TMEMBER(_cKDefault)); + persistMgr->transfer(TMEMBER(_cKRed)); + persistMgr->transfer(TMEMBER(_cKGreen)); + persistMgr->transfer(TMEMBER(_cKBlue)); + persistMgr->transfer(TMEMBER(_lifeTime)); + + persistMgr->transfer(TMEMBER(_keepLoaded)); + persistMgr->transfer(TMEMBER(_mirrorX)); + persistMgr->transfer(TMEMBER(_mirrorY)); + persistMgr->transfer(TMEMBER(_transparent)); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool BaseSubFrame::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + + ////////////////////////////////////////////////////////////////////////// + // GetImage + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "GetImage") == 0) { + stack->correctParams(0); + + if (!_surfaceFilename) { + stack->pushNULL(); + } else { + stack->pushString(_surfaceFilename); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetImage + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetImage") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + if (val->isNULL()) { + if (_surface) { + _gameRef->_surfaceStorage->removeSurface(_surface); + } + delete[] _surfaceFilename; + _surfaceFilename = NULL; + stack->pushBool(true); + } else { + const char *filename = val->getString(); + if (DID_SUCCEED(setSurface(filename))) { + setDefaultRect(); + stack->pushBool(true); + } else { + stack->pushBool(false); + } + } + + return STATUS_OK; + } else { + return BaseScriptable::scCallMethod(script, stack, thisStack, name); + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *BaseSubFrame::scGetProperty(const Common::String &name) { + if (!_scValue) { + _scValue = new ScValue(_gameRef); + } + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type (RO) + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("subframe"); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // AlphaColor + ////////////////////////////////////////////////////////////////////////// + else if (name == "AlphaColor") { + + _scValue->setInt((int)_alpha); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // TransparentColor (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "TransparentColor") { + _scValue->setInt((int)_transparent); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Is2DOnly + ////////////////////////////////////////////////////////////////////////// + else if (name == "Is2DOnly") { + _scValue->setBool(_2DOnly); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Is3DOnly + ////////////////////////////////////////////////////////////////////////// + else if (name == "Is3DOnly") { + _scValue->setBool(_3DOnly); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // MirrorX + ////////////////////////////////////////////////////////////////////////// + else if (name == "MirrorX") { + _scValue->setBool(_mirrorX); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // MirrorY + ////////////////////////////////////////////////////////////////////////// + else if (name == "MirrorY") { + _scValue->setBool(_mirrorY); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Decoration + ////////////////////////////////////////////////////////////////////////// + else if (name == "Decoration") { + _scValue->setBool(_decoration); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // HotspotX + ////////////////////////////////////////////////////////////////////////// + else if (name == "HotspotX") { + _scValue->setInt(_hotspotX); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // HotspotY + ////////////////////////////////////////////////////////////////////////// + else if (name == "HotspotY") { + _scValue->setInt(_hotspotY); + return _scValue; + } else { + return BaseScriptable::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSubFrame::scSetProperty(const char *name, ScValue *value) { + ////////////////////////////////////////////////////////////////////////// + // AlphaColor + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "AlphaColor") == 0) { + _alpha = (uint32)value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Is2DOnly + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Is2DOnly") == 0) { + _2DOnly = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Is3DOnly + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Is3DOnly") == 0) { + _3DOnly = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // MirrorX + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "MirrorX") == 0) { + _mirrorX = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // MirrorY + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "MirrorY") == 0) { + _mirrorY = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Decoration + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Decoration") == 0) { + _decoration = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // HotspotX + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "HotspotX") == 0) { + _hotspotX = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // HotspotY + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "HotspotY") == 0) { + _hotspotY = value->getInt(); + return STATUS_OK; + } else { + return BaseScriptable::scSetProperty(name, value); + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *BaseSubFrame::scToString() { + return "[subframe]"; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSubFrame::setSurface(const Common::String &filename, bool defaultCK, byte ckRed, byte ckGreen, byte ckBlue, int lifeTime, bool keepLoaded) { + if (_surface) { + _gameRef->_surfaceStorage->removeSurface(_surface); + _surface = NULL; + } + + delete[] _surfaceFilename; + _surfaceFilename = NULL; + + _surface = _gameRef->_surfaceStorage->addSurface(filename, defaultCK, ckRed, ckGreen, ckBlue, lifeTime, keepLoaded); + if (_surface) { + _surfaceFilename = new char[filename.size() + 1]; + strcpy(_surfaceFilename, filename.c_str()); + + _cKDefault = defaultCK; + _cKRed = ckRed; + _cKGreen = ckGreen; + _cKBlue = ckBlue; + _lifeTime = lifeTime; + _keepLoaded = keepLoaded; + + return STATUS_OK; + } else { + return STATUS_FAILED; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSubFrame::setSurfaceSimple() { + if (!_surfaceFilename) { + _surface = NULL; + return STATUS_OK; + } + _surface = _gameRef->_surfaceStorage->addSurface(_surfaceFilename, _cKDefault, _cKRed, _cKGreen, _cKBlue, _lifeTime, _keepLoaded); + if (_surface) { + return STATUS_OK; + } else { + return STATUS_FAILED; + } +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_sub_frame.h b/engines/wintermute/base/base_sub_frame.h new file mode 100644 index 0000000000..c173ae69d1 --- /dev/null +++ b/engines/wintermute/base/base_sub_frame.h @@ -0,0 +1,93 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_SUBFRAME_H +#define WINTERMUTE_BASE_SUBFRAME_H + + +#include "engines/wintermute/base/base.h" +#include "engines/wintermute/base/base_scriptable.h" + +namespace Wintermute { +class BaseObject; +class BaseSurface; +class BaseSubFrame : public BaseScriptable { +public: + bool _mirrorX; + bool _mirrorY; + bool _decoration; + bool setSurface(const Common::String &filename, bool defaultCK = true, byte ckRed = 0, byte ckGreen = 0, byte ckBlue = 0, int lifeTime = -1, bool keepLoaded = false); + bool setSurfaceSimple(); + DECLARE_PERSISTENT(BaseSubFrame, BaseScriptable) + void setDefaultRect(); + uint32 _transparent; + bool saveAsText(BaseDynamicBuffer *buffer, int indent) { return saveAsText(buffer, indent, true); } + bool saveAsText(BaseDynamicBuffer *buffer, int indent, bool complete); + bool _editorSelected; + BaseSubFrame(BaseGame *inGame); + virtual ~BaseSubFrame(); + bool loadBuffer(byte *buffer, int lifeTime, bool keepLoaded); + bool draw(int x, int y, BaseObject *registerOwner = NULL, float zoomX = 100, float zoomY = 100, bool precise = true, uint32 alpha = 0xFFFFFFFF, float rotate = 0.0f, TSpriteBlendMode blendMode = BLEND_NORMAL); + bool getBoundingRect(Rect32 *rect, int x, int y, float scaleX = 100, float scaleY = 100); + const char* getSurfaceFilename(); + + int _hotspotX; + int _hotspotY; + uint32 _alpha; + // These two setters and getters are rather usefull, as they allow _rect to be lazily defined + // Thus we don't need to load the actual graphics before the rect is actually needed. + Rect32 getRect(); + void setRect(Rect32 rect); +private: + bool _wantsDefaultRect; + Rect32 _rect; + char *_surfaceFilename; +public: + bool _cKDefault; + byte _cKRed; + byte _cKGreen; + byte _cKBlue; + int _lifeTime; + bool _keepLoaded; + + bool _2DOnly; + bool _3DOnly; + + BaseSurface *_surface; + + // scripting interface + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + virtual const char *scToString(); + +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_surface_storage.cpp b/engines/wintermute/base/base_surface_storage.cpp new file mode 100644 index 0000000000..2205e3e096 --- /dev/null +++ b/engines/wintermute/base/base_surface_storage.cpp @@ -0,0 +1,205 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_surface_storage.h" +#include "engines/wintermute/base/gfx/base_surface.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/platform_osystem.h" +#include "common/str.h" + +namespace Wintermute { + +//IMPLEMENT_PERSISTENT(BaseSurfaceStorage, true); + +////////////////////////////////////////////////////////////////////// +BaseSurfaceStorage::BaseSurfaceStorage(BaseGame *inGame) : BaseClass(inGame) { + _lastCleanupTime = 0; +} + + +////////////////////////////////////////////////////////////////////// +BaseSurfaceStorage::~BaseSurfaceStorage() { + cleanup(true); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSurfaceStorage::cleanup(bool warn) { + for (uint32 i = 0; i < _surfaces.size(); i++) { + if (warn) { + _gameRef->LOG(0, "BaseSurfaceStorage warning: purging surface '%s', usage:%d", _surfaces[i]->getFileName(), _surfaces[i]->_referenceCount); + } + delete _surfaces[i]; + } + _surfaces.clear(); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSurfaceStorage::initLoop() { + if (_gameRef->_smartCache && _gameRef->_liveTimer - _lastCleanupTime >= _gameRef->_surfaceGCCycleTime) { + _lastCleanupTime = _gameRef->_liveTimer; + sortSurfaces(); + for (uint32 i = 0; i < _surfaces.size(); i++) { + if (_surfaces[i]->_lifeTime <= 0) { + break; + } + + if (_surfaces[i]->_lifeTime > 0 && _surfaces[i]->_valid && (int)(_gameRef->_liveTimer - _surfaces[i]->_lastUsedTime) >= _surfaces[i]->_lifeTime) { + //_gameRef->QuickMessageForm("Invalidating: %s", _surfaces[i]->_filename); + _surfaces[i]->invalidate(); + } + } + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////// +bool BaseSurfaceStorage::removeSurface(BaseSurface *surface) { + for (uint32 i = 0; i < _surfaces.size(); i++) { + if (_surfaces[i] == surface) { + _surfaces[i]->_referenceCount--; + if (_surfaces[i]->_referenceCount <= 0) { + delete _surfaces[i]; + _surfaces.remove_at(i); + } + break; + } + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////// +BaseSurface *BaseSurfaceStorage::addSurface(const Common::String &filename, bool defaultCK, byte ckRed, byte ckGreen, byte ckBlue, int lifeTime, bool keepLoaded) { + for (uint32 i = 0; i < _surfaces.size(); i++) { + if (scumm_stricmp(_surfaces[i]->getFileName(), filename.c_str()) == 0) { + _surfaces[i]->_referenceCount++; + return _surfaces[i]; + } + } + + if (!BaseFileManager::getEngineInstance()->hasFile(filename)) { + if (filename.size()) { + _gameRef->LOG(0, "Missing image: '%s'", filename.c_str()); + } + if (_gameRef->_debugDebugMode) { + return addSurface("invalid_debug.bmp", defaultCK, ckRed, ckGreen, ckBlue, lifeTime, keepLoaded); + } else { + return addSurface("invalid.bmp", defaultCK, ckRed, ckGreen, ckBlue, lifeTime, keepLoaded); + } + } + + BaseSurface *surface; + surface = _gameRef->_renderer->createSurface(); + + if (!surface) { + return NULL; + } + + if (DID_FAIL(surface->create(filename, defaultCK, ckRed, ckGreen, ckBlue, lifeTime, keepLoaded))) { + delete surface; + return NULL; + } else { + surface->_referenceCount = 1; + _surfaces.push_back(surface); + return surface; + } +} + + +////////////////////////////////////////////////////////////////////// +bool BaseSurfaceStorage::restoreAll() { + bool ret; + for (uint32 i = 0; i < _surfaces.size(); i++) { + ret = _surfaces[i]->restore(); + if (ret != STATUS_OK) { + _gameRef->LOG(0, "BaseSurfaceStorage::RestoreAll failed"); + return ret; + } + } + return STATUS_OK; +} + + +/* +////////////////////////////////////////////////////////////////////////// +bool BaseSurfaceStorage::persist(BasePersistenceManager *persistMgr) +{ + + if (!persistMgr->getIsSaving()) cleanup(false); + + persistMgr->transfer(TMEMBER(_gameRef)); + + //_surfaces.persist(persistMgr); + + return STATUS_OK; +} +*/ + + +////////////////////////////////////////////////////////////////////////// +bool BaseSurfaceStorage::sortSurfaces() { + Common::sort(_surfaces.begin(), _surfaces.end(), surfaceSortCB); + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSurfaceStorage::surfaceSortCB(const BaseSurface *s1, const BaseSurface *s2) { + // sort by life time + if (s1->_lifeTime <= 0 && s2->_lifeTime > 0) { + return false; + } else if (s1->_lifeTime > 0 && s2->_lifeTime <= 0) { + return true; + } + + + // sort by validity + if (s1->_valid && !s2->_valid) { + return true; + } else if (!s1->_valid && s2->_valid) { + return false; + } + + // sort by time + else if (s1->_lastUsedTime > s2->_lastUsedTime) { + return false; + } else if (s1->_lastUsedTime < s2->_lastUsedTime) { + return true; + } else { + return false; + } +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_surface_storage.h b/engines/wintermute/base/base_surface_storage.h new file mode 100644 index 0000000000..61738e69a2 --- /dev/null +++ b/engines/wintermute/base/base_surface_storage.h @@ -0,0 +1,57 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_SURFACE_STORAGE_H +#define WINTERMUTE_BASE_SURFACE_STORAGE_H + +#include "engines/wintermute/base/base.h" +#include "common/array.h" + +namespace Wintermute { +class BaseSurface; +class BaseSurfaceStorage : public BaseClass { +public: + uint32 _lastCleanupTime; + bool initLoop(); + bool sortSurfaces(); + static bool surfaceSortCB(const BaseSurface *arg1, const BaseSurface *arg2); + bool cleanup(bool warn = false); + //DECLARE_PERSISTENT(BaseSurfaceStorage, BaseClass); + + bool restoreAll(); + BaseSurface *addSurface(const Common::String &filename, bool defaultCK = true, byte ckRed = 0, byte ckGreen = 0, byte ckBlue = 0, int lifeTime = -1, bool keepLoaded = false); + bool removeSurface(BaseSurface *surface); + BaseSurfaceStorage(BaseGame *inGame); + virtual ~BaseSurfaceStorage(); + + Common::Array<BaseSurface *> _surfaces; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_transition_manager.cpp b/engines/wintermute/base/base_transition_manager.cpp new file mode 100644 index 0000000000..6ae51d8e83 --- /dev/null +++ b/engines/wintermute/base/base_transition_manager.cpp @@ -0,0 +1,137 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_transition_manager.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/gfx/base_renderer.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////////// +BaseTransitionMgr::BaseTransitionMgr(BaseGame *inGame) : BaseClass(inGame) { + _state = TRANS_MGR_READY; + _type = TRANSITION_NONE; + _origInteractive = false; + _preserveInteractive = false; + _lastTime = 0; + _started = false; +} + + + +////////////////////////////////////////////////////////////////////////// +BaseTransitionMgr::~BaseTransitionMgr() { + +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseTransitionMgr::isReady() const { + return (_state == TRANS_MGR_READY); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseTransitionMgr::start(TTransitionType type, bool nonInteractive) { + if (_state != TRANS_MGR_READY) { + return STATUS_OK; + } + + if (type == TRANSITION_NONE || type >= NUM_TRANSITION_TYPES) { + _state = TRANS_MGR_READY; + return STATUS_OK; + } + + if (nonInteractive) { + _preserveInteractive = true; + _origInteractive = _gameRef->_interactive; + _gameRef->_interactive = false; + } /*else _preserveInteractive */; + + + _type = type; + _state = TRANS_MGR_RUNNING; + _started = false; + + return STATUS_OK; +} + +#define FADE_DURATION 200 + +////////////////////////////////////////////////////////////////////////// +bool BaseTransitionMgr::update() { + if (isReady()) { + return STATUS_OK; + } + + if (!_started) { + _started = true; + _lastTime = g_system->getMillis(); + } + + switch (_type) { + case TRANSITION_NONE: + _state = TRANS_MGR_READY; + break; + + case TRANSITION_FADE_OUT: { + uint32 time = g_system->getMillis() - _lastTime; + int alpha = (int)(255 - (float)time / (float)FADE_DURATION * 255); + alpha = MIN(255, MAX(alpha, 0)); + _gameRef->_renderer->fade((uint16)alpha); + + if (time > FADE_DURATION) { + _state = TRANS_MGR_READY; + } + } + break; + + case TRANSITION_FADE_IN: { + uint32 time = g_system->getMillis() - _lastTime; + int alpha = (int)((float)time / (float)FADE_DURATION * 255); + alpha = MIN(255, MAX(alpha, 0)); + _gameRef->_renderer->fade((uint16)alpha); + + if (time > FADE_DURATION) { + _state = TRANS_MGR_READY; + } + } + break; + default: + error("BaseTransitionMgr::Update - unhandled enum NUM_TRANSITION_TYPES"); + } + + if (isReady()) { + if (_preserveInteractive) { + _gameRef->_interactive = _origInteractive; + } + } + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_transition_manager.h b/engines/wintermute/base/base_transition_manager.h new file mode 100644 index 0000000000..edb3045a58 --- /dev/null +++ b/engines/wintermute/base/base_transition_manager.h @@ -0,0 +1,54 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_TRANSITION_MANAGER_H +#define WINTERMUTE_BASE_TRANSITION_MANAGER_H + +#include "engines/wintermute/base/base.h" + +namespace Wintermute { + +class BaseTransitionMgr : public BaseClass { +public: + bool _started; + uint32 _lastTime; + bool _origInteractive; + bool _preserveInteractive; + bool update(); + bool start(TTransitionType type, bool nonInteractive = false); + bool isReady() const; + TTransMgrState _state; + BaseTransitionMgr(BaseGame *inGame); + virtual ~BaseTransitionMgr(); + TTransitionType _type; + +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_viewport.cpp b/engines/wintermute/base/base_viewport.cpp new file mode 100644 index 0000000000..b3c2cfa6c3 --- /dev/null +++ b/engines/wintermute/base/base_viewport.cpp @@ -0,0 +1,99 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/platform_osystem.h" +#include "engines/wintermute/base/base_viewport.h" +#include "engines/wintermute/base/gfx/base_renderer.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(BaseViewport, false) + +////////////////////////////////////////////////////////////////////////// +BaseViewport::BaseViewport(BaseGame *inGame) : BaseClass(inGame) { + BasePlatform::setRectEmpty(&_rect); + _mainObject = NULL; + _offsetX = _offsetY = 0; +} + + +////////////////////////////////////////////////////////////////////////// +BaseViewport::~BaseViewport() { + +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseViewport::persist(BasePersistenceManager *persistMgr) { + + persistMgr->transfer(TMEMBER(_gameRef)); + + persistMgr->transfer(TMEMBER(_mainObject)); + persistMgr->transfer(TMEMBER(_offsetX)); + persistMgr->transfer(TMEMBER(_offsetY)); + persistMgr->transfer(TMEMBER(_rect)); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseViewport::setRect(int left, int top, int right, int bottom, bool noCheck) { + if (!noCheck) { + left = MAX(left, 0); + top = MAX(top, 0); + right = MIN(right, _gameRef->_renderer->_width); + bottom = MIN(bottom, _gameRef->_renderer->_height); + } + + BasePlatform::setRect(&_rect, left, top, right, bottom); + _offsetX = left; + _offsetY = top; + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +Rect32 *BaseViewport::getRect() { + return &_rect; +} + + +////////////////////////////////////////////////////////////////////////// +int BaseViewport::getWidth() const { + return _rect.right - _rect.left; +} + + +////////////////////////////////////////////////////////////////////////// +int BaseViewport::getHeight() const { + return _rect.bottom - _rect.top; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_viewport.h b/engines/wintermute/base/base_viewport.h new file mode 100644 index 0000000000..0182565a67 --- /dev/null +++ b/engines/wintermute/base/base_viewport.h @@ -0,0 +1,55 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_VIEWPORT_H +#define WINTERMUTE_BASE_VIEWPORT_H + + +#include "engines/wintermute/base/base.h" + +namespace Wintermute { +class BaseObject; +class BaseViewport : public BaseClass { +public: + int getHeight() const; + int getWidth() const; + Rect32 *getRect(); + bool setRect(int left, int top, int right, int bottom, bool noCheck = false); + DECLARE_PERSISTENT(BaseViewport, BaseClass) + int _offsetY; + int _offsetX; + BaseObject *_mainObject; + BaseViewport(BaseGame *inGame = NULL); + virtual ~BaseViewport(); +private: + Rect32 _rect; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/file/base_disk_file.cpp b/engines/wintermute/base/file/base_disk_file.cpp new file mode 100644 index 0000000000..25be3dad2d --- /dev/null +++ b/engines/wintermute/base/file/base_disk_file.cpp @@ -0,0 +1,194 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/dcgf.h" +#include "engines/wintermute/base/file/base_disk_file.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "common/stream.h" +#include "common/memstream.h" +#include "common/file.h" +#include "common/zlib.h" +#include "common/archive.h" +#include "common/tokenizer.h" +#include "common/config-manager.h" + +namespace Wintermute { + +void correctSlashes(char *fileName) { + for (size_t i = 0; i < strlen(fileName); i++) { + if (fileName[i] == '\\') { + fileName[i] = '/'; + } + } +} + +// Parse a relative path in the game-folder, and if it exists, return a FSNode to it. +static Common::FSNode getNodeForRelativePath(const Common::String &filename) { + // The filename can be an explicit path, thus we need to chop it up, expecting the path the game + // specifies to follow the Windows-convention of folder\subfolder\file (absolute paths should not happen) + + // Absolute path: These should have been handled in openDiskFile. + if (filename.contains(':')) { + // So just return an invalid node. + return Common::FSNode(); + } + + // Relative path: + if (filename.contains('\\')) { + Common::StringTokenizer path(filename, "\\"); + + // Start traversing relative to the game-data-dir + const Common::FSNode gameDataDir(ConfMan.get("path")); + Common::FSNode curNode = gameDataDir; + + // Parse all path-elements + while (!path.empty()) { + // Get the next path-component by slicing on '\\' + Common::String pathPart = path.nextToken(); + // Get the next FSNode in the chain, if it exists as a child from the previous. + curNode = curNode.getChild(pathPart); + if (!curNode.isReadable()) { + // Return an invalid FSNode. + return Common::FSNode(); + } + // Following the comments in common/fs.h, anything not a directory is a file. + if (!curNode.isDirectory()) { + if (!path.empty()) { + error("Relative path %s reached a file before the end of the path", filename.c_str()); + } + return curNode; + } + } + } + // Return an invalid FSNode to mark that we didn't find the requested file. + return Common::FSNode(); +} + +bool diskFileExists(const Common::String &filename) { + // Try directly from SearchMan first + Common::ArchiveMemberList files; + SearchMan.listMatchingMembers(files, filename); + + for (Common::ArchiveMemberList::iterator it = files.begin(); it != files.end(); ++it) { + if ((*it)->getName() == filename) { + return true; + } + } + // File wasn't found in SearchMan, try to parse the path as a relative path. + Common::FSNode searchNode = getNodeForRelativePath(filename); + if (searchNode.exists() && !searchNode.isDirectory() && searchNode.isReadable()) { + return true; + } + return false; +} + +Common::SeekableReadStream *openDiskFile(const Common::String &filename) { + uint32 prefixSize = 0; + Common::SeekableReadStream *file = NULL; + Common::String fixedFilename = filename; + + // Absolute path: TODO: Add specific fallbacks here. + if (filename.contains(':')) { + if (filename.hasPrefix("c:\\windows\\fonts\\")) { // East Side Story refers to "c:\windows\fonts\framd.ttf" + fixedFilename = filename.c_str() + 17; + } else { + error("openDiskFile::Absolute path or invalid filename used in %s", filename.c_str()); + } + } + // Try directly from SearchMan first + Common::ArchiveMemberList files; + SearchMan.listMatchingMembers(files, fixedFilename); + + for (Common::ArchiveMemberList::iterator it = files.begin(); it != files.end(); ++it) { + if ((*it)->getName() == filename) { + file = (*it)->createReadStream(); + break; + } + } + // File wasn't found in SearchMan, try to parse the path as a relative path. + if (!file) { + Common::FSNode searchNode = getNodeForRelativePath(filename); + if (searchNode.exists() && !searchNode.isDirectory() && searchNode.isReadable()) { + file = searchNode.createReadStream(); + } + } + if (file) { + uint32 magic1, magic2; + magic1 = file->readUint32LE(); + magic2 = file->readUint32LE(); + + bool compressed = false; + if (magic1 == DCGF_MAGIC && magic2 == COMPRESSED_FILE_MAGIC) { + compressed = true; + } + + if (compressed) { + uint32 dataOffset, compSize, uncompSize; + dataOffset = file->readUint32LE(); + compSize = file->readUint32LE(); + uncompSize = file->readUint32LE(); + + byte *compBuffer = new byte[compSize]; + if (!compBuffer) { + error("Error allocating memory for compressed file '%s'", filename.c_str()); + delete file; + return NULL; + } + + byte *data = new byte[uncompSize]; + if (!data) { + error("Error allocating buffer for file '%s'", filename.c_str()); + delete[] compBuffer; + delete file; + return NULL; + } + file->seek(dataOffset + prefixSize, SEEK_SET); + file->read(compBuffer, compSize); + + if (Common::uncompress(data, (unsigned long *)&uncompSize, compBuffer, compSize) != true) { + error("Error uncompressing file '%s'", filename.c_str()); + delete[] compBuffer; + delete file; + return NULL; + } + + delete[] compBuffer; + delete file; + return new Common::MemoryReadStream(data, uncompSize, DisposeAfterUse::YES); + } else { + file->seek(0, SEEK_SET); + return file; + } + + return file; + + } + return NULL; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/file/base_disk_file.h b/engines/wintermute/base/file/base_disk_file.h new file mode 100644 index 0000000000..c9f93b80d9 --- /dev/null +++ b/engines/wintermute/base/file/base_disk_file.h @@ -0,0 +1,41 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_DISKFILE_H +#define WINTERMUTE_BASE_DISKFILE_H + +#include "common/stream.h" + +namespace Wintermute { + +Common::SeekableReadStream *openDiskFile(const Common::String &filename); +bool diskFileExists(const Common::String &filename); + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/file/base_file.cpp b/engines/wintermute/base/file/base_file.cpp new file mode 100644 index 0000000000..f52a13211e --- /dev/null +++ b/engines/wintermute/base/file/base_file.cpp @@ -0,0 +1,68 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/file/base_file.h" +#include "common/memstream.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////// +BaseFile::BaseFile() { + _pos = 0; + _size = 0; +} + + +////////////////////////////////////////////////////////////////////////// +BaseFile::~BaseFile() { + +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseFile::isEOF() { + return _pos == _size; +} + +Common::SeekableReadStream *BaseFile::getMemStream() { + uint32 oldPos = getPos(); + seek(0); + byte *data = new byte[getSize()]; + read(data, getSize()); + seek(oldPos); + Common::MemoryReadStream *memStream = new Common::MemoryReadStream(data, getSize(), DisposeAfterUse::YES); + return memStream; +} + + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/file/base_file.h b/engines/wintermute/base/file/base_file.h new file mode 100644 index 0000000000..8eda6d51d9 --- /dev/null +++ b/engines/wintermute/base/file/base_file.h @@ -0,0 +1,67 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_FILE_H +#define WINTERMUTE_BASE_FILE_H + + +#include "engines/wintermute/base/base.h" +#include "common/str.h" +#include "common/stream.h" + +namespace Common { +class SeekableReadStream; +} + +namespace Wintermute { + +class BaseFile { +protected: + uint32 _pos; + uint32 _size; +public: + virtual uint32 getSize() const { + return _size; + }; + virtual uint32 getPos() const { + return _pos; + }; + virtual bool seek(uint32 pos, int whence = SEEK_SET) = 0; + virtual bool read(void *buffer, uint32 size) = 0; + virtual bool close() = 0; + virtual bool open(const Common::String &filename) = 0; + virtual bool isEOF(); + BaseFile(); + virtual ~BaseFile(); + // Temporary solution to allow usage in ScummVM-code: + virtual Common::SeekableReadStream *getMemStream(); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/file/base_file_entry.cpp b/engines/wintermute/base/file/base_file_entry.cpp new file mode 100644 index 0000000000..b9805d78dd --- /dev/null +++ b/engines/wintermute/base/file/base_file_entry.cpp @@ -0,0 +1,73 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/file/base_file_entry.h" +#include "engines/wintermute/base/file/base_package.h" +#include "common/stream.h" +#include "common/substream.h" +#include "common/zlib.h" + +namespace Wintermute { + +Common::SeekableReadStream *BaseFileEntry::createReadStream() const { + Common::SeekableReadStream *file = _package->getFilePointer(); + if (!file) { + return NULL; + } + + bool compressed = (_compressedLength != 0); + + if (compressed) { + file = Common::wrapCompressedReadStream(new Common::SeekableSubReadStream(file, _offset, _offset + _length, DisposeAfterUse::YES), _length); // + } else { + file = new Common::SeekableSubReadStream(file, _offset, _offset + _length, DisposeAfterUse::YES); + } + + file->seek(0); + + return file; +} + +////////////////////////////////////////////////////////////////////////// +BaseFileEntry::BaseFileEntry() { + _package = NULL; + _length = _compressedLength = _offset = _flags = 0; + _filename = ""; + + _timeDate1 = _timeDate2 = 0; + + _journalTime = 0; +} + + +////////////////////////////////////////////////////////////////////////// +BaseFileEntry::~BaseFileEntry() { + _package = NULL; // ref only +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/file/base_file_entry.h b/engines/wintermute/base/file/base_file_entry.h new file mode 100644 index 0000000000..6e4823d994 --- /dev/null +++ b/engines/wintermute/base/file/base_file_entry.h @@ -0,0 +1,60 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_FILEENTRY_H +#define WINTERMUTE_BASE_FILEENTRY_H + +#include "common/archive.h" +#include "common/str.h" +#include "common/stream.h" + +namespace Wintermute { + +class BasePackage; + +class BaseFileEntry : public Common::ArchiveMember { +public: + virtual Common::SeekableReadStream *createReadStream() const; + virtual Common::String getName() const { return _filename; } + uint32 _timeDate2; + uint32 _timeDate1; + uint32 _flags; + uint32 _journalTime; + Common::String _filename; + uint32 _compressedLength; + uint32 _length; + uint32 _offset; + BasePackage *_package; + BaseFileEntry(); + virtual ~BaseFileEntry(); + +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/file/base_package.cpp b/engines/wintermute/base/file/base_package.cpp new file mode 100644 index 0000000000..51a1558a7c --- /dev/null +++ b/engines/wintermute/base/file/base_package.cpp @@ -0,0 +1,276 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/file/base_package.h" +#include "engines/wintermute/base/file/base_file_entry.h" +#include "engines/wintermute/base/file/dcpackage.h" +#include "engines/wintermute/wintermute.h" +#include "common/file.h" +#include "common/stream.h" +#include "common/debug.h" + +namespace Wintermute { + +BasePackage::BasePackage() { + _name = ""; + _cd = 0; + _priority = 0; + _boundToExe = false; +} + +Common::SeekableReadStream *BasePackage::getFilePointer() { + Common::SeekableReadStream *stream = _fsnode.createReadStream(); + + return stream; +} + +static bool findPackageSignature(Common::SeekableReadStream *f, uint32 *offset) { + byte buf[32768]; + + byte signature[8]; + ((uint32 *)signature)[0] = PACKAGE_MAGIC_1; + ((uint32 *)signature)[1] = PACKAGE_MAGIC_2; + + uint32 fileSize = (uint32)f->size(); + uint32 startPos = 1024 * 1024; + uint32 bytesRead = startPos; + + while (bytesRead < fileSize - 16) { + uint32 toRead = MIN((unsigned int)32768, fileSize - bytesRead); + f->seek((int32)startPos, SEEK_SET); + uint32 actuallyRead = f->read(buf, toRead); + if (actuallyRead != toRead) { + return false; + } + + for (uint32 i = 0; i < toRead - 8; i++) + if (!memcmp(buf + i, signature, 8)) { + *offset = startPos + i; + return true; + } + + bytesRead = bytesRead + toRead - 16; + startPos = startPos + toRead - 16; + + } + return false; + +} + +void TPackageHeader::readFromStream(Common::ReadStream *stream) { + _magic1 = stream->readUint32LE(); + _magic2 = stream->readUint32LE(); + _packageVersion = stream->readUint32LE(); + + _gameVersion = stream->readUint32LE(); + + _priority = stream->readByte(); + _cd = stream->readByte(); + _masterIndex = stream->readByte(); + stream->readByte(); // To align the next byte... + + _creationTime = stream->readUint32LE(); + + stream->read(_desc, 100); + _numDirs = stream->readUint32LE(); +} + +PackageSet::PackageSet(Common::FSNode file, const Common::String &filename, bool searchSignature) { + uint32 absoluteOffset = 0; + _priority = 0; + bool boundToExe = false; + Common::SeekableReadStream *stream = file.createReadStream(); + if (!stream) { + return; + } + if (searchSignature) { + uint32 offset; + if (!findPackageSignature(stream, &offset)) { + delete stream; + return; + } else { + stream->seek(offset, SEEK_SET); + absoluteOffset = offset; + boundToExe = true; + } + } + + TPackageHeader hdr; + hdr.readFromStream(stream); + if (hdr._magic1 != PACKAGE_MAGIC_1 || hdr._magic2 != PACKAGE_MAGIC_2 || hdr._packageVersion > PACKAGE_VERSION) { + debugC(kWintermuteDebugFileAccess | kWintermuteDebugLog, " Invalid header in package file '%s'. Ignoring.", filename.c_str()); + delete stream; + return; + } + + if (hdr._packageVersion != PACKAGE_VERSION) { + debugC(kWintermuteDebugFileAccess | kWintermuteDebugLog, " Warning: package file '%s' is outdated.", filename.c_str()); + } + _priority = hdr._priority; + // new in v2 + if (hdr._packageVersion == PACKAGE_VERSION) { + uint32 dirOffset; + dirOffset = stream->readUint32LE(); + dirOffset += absoluteOffset; + stream->seek(dirOffset, SEEK_SET); + } + assert(hdr._numDirs == 1); + for (uint32 i = 0; i < hdr._numDirs; i++) { + BasePackage *pkg = new BasePackage(); + if (!pkg) { + return; + } + pkg->_fsnode = file; + + pkg->_boundToExe = boundToExe; + + // read package info + byte nameLength = stream->readByte(); + char *pkgName = new char[nameLength]; + stream->read(pkgName, nameLength); + pkg->_name = pkgName; + pkg->_cd = stream->readByte(); + pkg->_priority = hdr._priority; + delete[] pkgName; + pkgName = NULL; + + if (!hdr._masterIndex) { + pkg->_cd = 0; // override CD to fixed disk + } + _packages.push_back(pkg); + + // read file entries + uint32 numFiles = stream->readUint32LE(); + + for (uint32 j = 0; j < numFiles; j++) { + char *name; + uint32 offset, length, compLength, flags;/*, timeDate1, timeDate2;*/ + + nameLength = stream->readByte(); + name = new char[nameLength]; + stream->read(name, nameLength); + + // v2 - xor name + if (hdr._packageVersion == PACKAGE_VERSION) { + for (int k = 0; k < nameLength; k++) { + ((byte *)name)[k] ^= 'D'; + } + } + debugC(kWintermuteDebugFileAccess, "Package contains %s", name); + + Common::String upcName = name; + upcName.toUppercase(); + delete[] name; + name = NULL; + + offset = stream->readUint32LE(); + offset += absoluteOffset; + length = stream->readUint32LE(); + compLength = stream->readUint32LE(); + flags = stream->readUint32LE(); + + if (hdr._packageVersion == PACKAGE_VERSION) { + /* timeDate1 = */ stream->readUint32LE(); + /* timeDate2 = */ stream->readUint32LE(); + } + _filesIter = _files.find(upcName); + if (_filesIter == _files.end()) { + BaseFileEntry *fileEntry = new BaseFileEntry(); + fileEntry->_package = pkg; + fileEntry->_offset = offset; + fileEntry->_length = length; + fileEntry->_compressedLength = compLength; + fileEntry->_flags = flags; + + _files[upcName] = Common::ArchiveMemberPtr(fileEntry); + } else { + // current package has higher priority than the registered + // TODO: This cast might be a bit ugly. + BaseFileEntry *filePtr = (BaseFileEntry *) &*(_filesIter->_value); + if (pkg->_priority > filePtr->_package->_priority) { + filePtr->_package = pkg; + filePtr->_offset = offset; + filePtr->_length = length; + filePtr->_compressedLength = compLength; + filePtr->_flags = flags; + } + } + } + } + debugC(kWintermuteDebugFileAccess, " Registered %d files in %d package(s)", _files.size(), _packages.size()); + + delete stream; +} + +PackageSet::~PackageSet() { + for (Common::Array<BasePackage *>::iterator it = _packages.begin(); it != _packages.end(); ++it) { + delete *it; + } + _packages.clear(); +} + +bool PackageSet::hasFile(const Common::String &name) const { + Common::String upcName = name; + upcName.toUppercase(); + Common::HashMap<Common::String, Common::ArchiveMemberPtr>::const_iterator it; + it = _files.find(upcName.c_str()); + return (it != _files.end()); +} + +int PackageSet::listMembers(Common::ArchiveMemberList &list) const { + Common::HashMap<Common::String, Common::ArchiveMemberPtr>::const_iterator it = _files.begin(); + Common::HashMap<Common::String, Common::ArchiveMemberPtr>::const_iterator end = _files.end(); + int count = 0; + for (; it != end; ++it) { + const Common::ArchiveMemberPtr ptr(it->_value); + list.push_back(ptr); + count++; + } + return count; +} + +const Common::ArchiveMemberPtr PackageSet::getMember(const Common::String &name) const { + Common::String upcName = name; + upcName.toUppercase(); + Common::HashMap<Common::String, Common::ArchiveMemberPtr>::const_iterator it; + it = _files.find(upcName.c_str()); + return Common::ArchiveMemberPtr(it->_value); +} + +Common::SeekableReadStream *PackageSet::createReadStreamForMember(const Common::String &name) const { + Common::String upcName = name; + upcName.toUppercase(); + Common::HashMap<Common::String, Common::ArchiveMemberPtr>::const_iterator it; + it = _files.find(upcName.c_str()); + if (it != _files.end()) { + return it->_value->createReadStream(); + } + return NULL; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/file/base_package.h b/engines/wintermute/base/file/base_package.h new file mode 100644 index 0000000000..2882eb03b7 --- /dev/null +++ b/engines/wintermute/base/file/base_package.h @@ -0,0 +1,90 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_PACKAGE_H +#define WINTERMUTE_BASE_PACKAGE_H + +#include "common/archive.h" +#include "common/stream.h" +#include "common/fs.h" + +namespace Wintermute { +class BasePackage { +public: + Common::SeekableReadStream *getFilePointer(); + Common::FSNode _fsnode; + bool _boundToExe; + byte _priority; + Common::String _name; + int _cd; + BasePackage(); +}; + +class PackageSet : public Common::Archive { +public: + virtual ~PackageSet(); + + PackageSet(Common::FSNode package, const Common::String &filename = "", bool searchSignature = false); + /** + * 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; + + /** + * 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; + + /** + * Returns a ArchiveMember representation of the given file. + */ + virtual const Common::ArchiveMemberPtr getMember(const Common::String &name) const; + + /** + * 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; + + int getPriority() const { return _priority; } +private: + byte _priority; + Common::Array<BasePackage *> _packages; + Common::HashMap<Common::String, Common::ArchiveMemberPtr> _files; + Common::HashMap<Common::String, Common::ArchiveMemberPtr>::iterator _filesIter; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/file/base_resources.cpp b/engines/wintermute/base/file/base_resources.cpp new file mode 100644 index 0000000000..0b32cb0c4f --- /dev/null +++ b/engines/wintermute/base/file/base_resources.cpp @@ -0,0 +1,2830 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/file/base_resources.h" +#include "common/str.h" +#include "common/memstream.h" + +namespace Wintermute { + +unsigned char invalid[] = { + 0x42, 0x4d, 0x36, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x04, 0x00, 0x00, 0x28, 0x00, + 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x20, 0x4e, 0x00, 0x00, 0x20, 0x4e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +} ; + +unsigned char invaliddebug[] = { + 0x42, 0x4d, 0x36, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x04, 0x00, 0x00, 0x28, 0x00, + 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, + 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x80, + 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0x00, 0xc0, 0xdc, 0xc0, 0x00, 0xf0, 0xca, 0xa6, 0x00, 0x00, 0x20, + 0x40, 0x00, 0x00, 0x20, 0x60, 0x00, 0x00, 0x20, 0x80, 0x00, 0x00, 0x20, 0xa0, 0x00, 0x00, 0x20, + 0xc0, 0x00, 0x00, 0x20, 0xe0, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x20, 0x00, 0x00, 0x40, + 0x40, 0x00, 0x00, 0x40, 0x60, 0x00, 0x00, 0x40, 0x80, 0x00, 0x00, 0x40, 0xa0, 0x00, 0x00, 0x40, + 0xc0, 0x00, 0x00, 0x40, 0xe0, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x20, 0x00, 0x00, 0x60, + 0x40, 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x60, 0x80, 0x00, 0x00, 0x60, 0xa0, 0x00, 0x00, 0x60, + 0xc0, 0x00, 0x00, 0x60, 0xe0, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x20, 0x00, 0x00, 0x80, + 0x40, 0x00, 0x00, 0x80, 0x60, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0xa0, 0x00, 0x00, 0x80, + 0xc0, 0x00, 0x00, 0x80, 0xe0, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0xa0, 0x20, 0x00, 0x00, 0xa0, + 0x40, 0x00, 0x00, 0xa0, 0x60, 0x00, 0x00, 0xa0, 0x80, 0x00, 0x00, 0xa0, 0xa0, 0x00, 0x00, 0xa0, + 0xc0, 0x00, 0x00, 0xa0, 0xe0, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x20, 0x00, 0x00, 0xc0, + 0x40, 0x00, 0x00, 0xc0, 0x60, 0x00, 0x00, 0xc0, 0x80, 0x00, 0x00, 0xc0, 0xa0, 0x00, 0x00, 0xc0, + 0xc0, 0x00, 0x00, 0xc0, 0xe0, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0xe0, 0x20, 0x00, 0x00, 0xe0, + 0x40, 0x00, 0x00, 0xe0, 0x60, 0x00, 0x00, 0xe0, 0x80, 0x00, 0x00, 0xe0, 0xa0, 0x00, 0x00, 0xe0, + 0xc0, 0x00, 0x00, 0xe0, 0xe0, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x20, 0x00, 0x40, 0x00, + 0x40, 0x00, 0x40, 0x00, 0x60, 0x00, 0x40, 0x00, 0x80, 0x00, 0x40, 0x00, 0xa0, 0x00, 0x40, 0x00, + 0xc0, 0x00, 0x40, 0x00, 0xe0, 0x00, 0x40, 0x20, 0x00, 0x00, 0x40, 0x20, 0x20, 0x00, 0x40, 0x20, + 0x40, 0x00, 0x40, 0x20, 0x60, 0x00, 0x40, 0x20, 0x80, 0x00, 0x40, 0x20, 0xa0, 0x00, 0x40, 0x20, + 0xc0, 0x00, 0x40, 0x20, 0xe0, 0x00, 0x40, 0x40, 0x00, 0x00, 0x40, 0x40, 0x20, 0x00, 0x40, 0x40, + 0x40, 0x00, 0x40, 0x40, 0x60, 0x00, 0x40, 0x40, 0x80, 0x00, 0x40, 0x40, 0xa0, 0x00, 0x40, 0x40, + 0xc0, 0x00, 0x40, 0x40, 0xe0, 0x00, 0x40, 0x60, 0x00, 0x00, 0x40, 0x60, 0x20, 0x00, 0x40, 0x60, + 0x40, 0x00, 0x40, 0x60, 0x60, 0x00, 0x40, 0x60, 0x80, 0x00, 0x40, 0x60, 0xa0, 0x00, 0x40, 0x60, + 0xc0, 0x00, 0x40, 0x60, 0xe0, 0x00, 0x40, 0x80, 0x00, 0x00, 0x40, 0x80, 0x20, 0x00, 0x40, 0x80, + 0x40, 0x00, 0x40, 0x80, 0x60, 0x00, 0x40, 0x80, 0x80, 0x00, 0x40, 0x80, 0xa0, 0x00, 0x40, 0x80, + 0xc0, 0x00, 0x40, 0x80, 0xe0, 0x00, 0x40, 0xa0, 0x00, 0x00, 0x40, 0xa0, 0x20, 0x00, 0x40, 0xa0, + 0x40, 0x00, 0x40, 0xa0, 0x60, 0x00, 0x40, 0xa0, 0x80, 0x00, 0x40, 0xa0, 0xa0, 0x00, 0x40, 0xa0, + 0xc0, 0x00, 0x40, 0xa0, 0xe0, 0x00, 0x40, 0xc0, 0x00, 0x00, 0x40, 0xc0, 0x20, 0x00, 0x40, 0xc0, + 0x40, 0x00, 0x40, 0xc0, 0x60, 0x00, 0x40, 0xc0, 0x80, 0x00, 0x40, 0xc0, 0xa0, 0x00, 0x40, 0xc0, + 0xc0, 0x00, 0x40, 0xc0, 0xe0, 0x00, 0x40, 0xe0, 0x00, 0x00, 0x40, 0xe0, 0x20, 0x00, 0x40, 0xe0, + 0x40, 0x00, 0x40, 0xe0, 0x60, 0x00, 0x40, 0xe0, 0x80, 0x00, 0x40, 0xe0, 0xa0, 0x00, 0x40, 0xe0, + 0xc0, 0x00, 0x40, 0xe0, 0xe0, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x20, 0x00, 0x80, 0x00, + 0x40, 0x00, 0x80, 0x00, 0x60, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0xa0, 0x00, 0x80, 0x00, + 0xc0, 0x00, 0x80, 0x00, 0xe0, 0x00, 0x80, 0x20, 0x00, 0x00, 0x80, 0x20, 0x20, 0x00, 0x80, 0x20, + 0x40, 0x00, 0x80, 0x20, 0x60, 0x00, 0x80, 0x20, 0x80, 0x00, 0x80, 0x20, 0xa0, 0x00, 0x80, 0x20, + 0xc0, 0x00, 0x80, 0x20, 0xe0, 0x00, 0x80, 0x40, 0x00, 0x00, 0x80, 0x40, 0x20, 0x00, 0x80, 0x40, + 0x40, 0x00, 0x80, 0x40, 0x60, 0x00, 0x80, 0x40, 0x80, 0x00, 0x80, 0x40, 0xa0, 0x00, 0x80, 0x40, + 0xc0, 0x00, 0x80, 0x40, 0xe0, 0x00, 0x80, 0x60, 0x00, 0x00, 0x80, 0x60, 0x20, 0x00, 0x80, 0x60, + 0x40, 0x00, 0x80, 0x60, 0x60, 0x00, 0x80, 0x60, 0x80, 0x00, 0x80, 0x60, 0xa0, 0x00, 0x80, 0x60, + 0xc0, 0x00, 0x80, 0x60, 0xe0, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x20, 0x00, 0x80, 0x80, + 0x40, 0x00, 0x80, 0x80, 0x60, 0x00, 0x80, 0x80, 0x80, 0x00, 0x80, 0x80, 0xa0, 0x00, 0x80, 0x80, + 0xc0, 0x00, 0x80, 0x80, 0xe0, 0x00, 0x80, 0xa0, 0x00, 0x00, 0x80, 0xa0, 0x20, 0x00, 0x80, 0xa0, + 0x40, 0x00, 0x80, 0xa0, 0x60, 0x00, 0x80, 0xa0, 0x80, 0x00, 0x80, 0xa0, 0xa0, 0x00, 0x80, 0xa0, + 0xc0, 0x00, 0x80, 0xa0, 0xe0, 0x00, 0x80, 0xc0, 0x00, 0x00, 0x80, 0xc0, 0x20, 0x00, 0x80, 0xc0, + 0x40, 0x00, 0x80, 0xc0, 0x60, 0x00, 0x80, 0xc0, 0x80, 0x00, 0x80, 0xc0, 0xa0, 0x00, 0x80, 0xc0, + 0xc0, 0x00, 0x80, 0xc0, 0xe0, 0x00, 0x80, 0xe0, 0x00, 0x00, 0x80, 0xe0, 0x20, 0x00, 0x80, 0xe0, + 0x40, 0x00, 0x80, 0xe0, 0x60, 0x00, 0x80, 0xe0, 0x80, 0x00, 0x80, 0xe0, 0xa0, 0x00, 0x80, 0xe0, + 0xc0, 0x00, 0x80, 0xe0, 0xe0, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x20, 0x00, 0xc0, 0x00, + 0x40, 0x00, 0xc0, 0x00, 0x60, 0x00, 0xc0, 0x00, 0x80, 0x00, 0xc0, 0x00, 0xa0, 0x00, 0xc0, 0x00, + 0xc0, 0x00, 0xc0, 0x00, 0xe0, 0x00, 0xc0, 0x20, 0x00, 0x00, 0xc0, 0x20, 0x20, 0x00, 0xc0, 0x20, + 0x40, 0x00, 0xc0, 0x20, 0x60, 0x00, 0xc0, 0x20, 0x80, 0x00, 0xc0, 0x20, 0xa0, 0x00, 0xc0, 0x20, + 0xc0, 0x00, 0xc0, 0x20, 0xe0, 0x00, 0xc0, 0x40, 0x00, 0x00, 0xc0, 0x40, 0x20, 0x00, 0xc0, 0x40, + 0x40, 0x00, 0xc0, 0x40, 0x60, 0x00, 0xc0, 0x40, 0x80, 0x00, 0xc0, 0x40, 0xa0, 0x00, 0xc0, 0x40, + 0xc0, 0x00, 0xc0, 0x40, 0xe0, 0x00, 0xc0, 0x60, 0x00, 0x00, 0xc0, 0x60, 0x20, 0x00, 0xc0, 0x60, + 0x40, 0x00, 0xc0, 0x60, 0x60, 0x00, 0xc0, 0x60, 0x80, 0x00, 0xc0, 0x60, 0xa0, 0x00, 0xc0, 0x60, + 0xc0, 0x00, 0xc0, 0x60, 0xe0, 0x00, 0xc0, 0x80, 0x00, 0x00, 0xc0, 0x80, 0x20, 0x00, 0xc0, 0x80, + 0x40, 0x00, 0xc0, 0x80, 0x60, 0x00, 0xc0, 0x80, 0x80, 0x00, 0xc0, 0x80, 0xa0, 0x00, 0xc0, 0x80, + 0xc0, 0x00, 0xc0, 0x80, 0xe0, 0x00, 0xc0, 0xa0, 0x00, 0x00, 0xc0, 0xa0, 0x20, 0x00, 0xc0, 0xa0, + 0x40, 0x00, 0xc0, 0xa0, 0x60, 0x00, 0xc0, 0xa0, 0x80, 0x00, 0xc0, 0xa0, 0xa0, 0x00, 0xc0, 0xa0, + 0xc0, 0x00, 0xc0, 0xa0, 0xe0, 0x00, 0xc0, 0xc0, 0x00, 0x00, 0xc0, 0xc0, 0x20, 0x00, 0xc0, 0xc0, + 0x40, 0x00, 0xc0, 0xc0, 0x60, 0x00, 0xc0, 0xc0, 0x80, 0x00, 0xc0, 0xc0, 0xa0, 0x00, 0xf0, 0xfb, + 0xff, 0x00, 0xa4, 0xa0, 0xa0, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0xf9, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, + 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0xf9, + 0x00, 0x00, 0x00, 0xf9, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, + 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0xf9, + 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, + 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, + 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, + 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0xf9, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, + 0x00, 0x00, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, + 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, + 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0xf9, 0xf9, 0x00, + 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0xf9, + 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0xf9, + 0xf9, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0xf9, + 0x00, 0x00, 0x00, 0xf9, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, + 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, + 0x00, 0x00, 0x00, 0xf9, 0xf9, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, + 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, + 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, + 0xf9, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, + 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, + 0xf9, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0xf9, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, + 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, + 0xf9, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, + 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0xf9, + 0x00, 0x00, 0x00, 0xf9, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, + 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0xf9, + 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, + 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9 +} ; + +unsigned char systemfont[] = { + 0x42, 0x4d, 0x36, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x04, 0x00, 0x00, 0x28, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x70, 0x0b, 0x00, 0x00, 0x70, 0x0b, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x80, 0x80, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, + 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, + 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, + 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, + 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x00, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, + 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x02, 0x01, 0x00, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, + 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, + 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x00, 0x00, 0x01, 0x01, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, + 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, + 0x00, 0x01, 0x02, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, + 0x00, 0x02, 0x02, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, + 0x02, 0x02, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, + 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, + 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, + 0x01, 0x00, 0x00, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x01, 0x00, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x01, 0x01, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, + 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, + 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x02, 0x02, + 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, + 0x01, 0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x00, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x02, 0x02, + 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x00, + 0x01, 0x02, 0x02, 0x01, 0x00, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, + 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x00, 0x02, 0x00, 0x00, 0x02, 0x01, 0x02, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x00, + 0x02, 0x00, 0x01, 0x02, 0x01, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x02, 0x02, 0x01, 0x00, 0x02, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, + 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x00, + 0x02, 0x00, 0x01, 0x01, 0x00, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, 0x00, 0x02, 0x00, 0x01, 0x02, 0x00, 0x02, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, + 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x02, 0x02, + 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x00, + 0x02, 0x01, 0x00, 0x02, 0x01, 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x00, 0x02, 0x00, 0x00, 0x02, 0x01, 0x02, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x01, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, + 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x00, + 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x00, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, + 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, + 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x00, 0x01, 0x02, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, + 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x02, 0x00, + 0x00, 0x01, 0x02, 0x01, 0x02, 0x01, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, + 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x02, 0x02, + 0x00, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x01, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x00, 0x01, 0x02, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x00, 0x01, 0x02, 0x00, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, + 0x02, 0x02, 0x01, 0x01, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x02, 0x01, + 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x01, 0x00, 0x02, 0x01, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, + 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, + 0x00, 0x01, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, + 0x00, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, + 0x00, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, + 0x00, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, + 0x00, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, + 0x00, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, + 0x00, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x01, 0x00, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, + 0x00, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, + 0x00, 0x02, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, + 0x00, 0x02, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, + 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, + 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, + 0x00, 0x01, 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, + 0x00, 0x02, 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, + 0x00, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, + 0x00, 0x02, 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, + 0x00, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, + 0x00, 0x02, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, + 0x00, 0x02, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, + 0x02, 0x01, 0x02, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, + 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, + 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, + 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x02, 0x02, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x02, 0x02, + 0x00, 0x02, 0x02, 0x00, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, + 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 +} ; + +Common::SeekableReadStream *BaseResources::getFile(const Common::String &filename) { + if (scumm_stricmp(filename.c_str(), "invalid.bmp") == 0) { + return new Common::MemoryReadStream(invalid, sizeof(invalid), DisposeAfterUse::NO); + } else if (scumm_stricmp(filename.c_str(), "invalid_debug.bmp") == 0) { + return new Common::MemoryReadStream(invaliddebug, sizeof(invalid), DisposeAfterUse::NO); + } else if (scumm_stricmp(filename.c_str(), "syste_font.bmp") == 0) { + return new Common::MemoryReadStream(systemfont, sizeof(invalid), DisposeAfterUse::NO); + } + return NULL; +} + +bool BaseResources::hasFile(const Common::String &filename) { + if (scumm_stricmp(filename.c_str(), "invalid.bmp") == 0) { + return true; + } else if (scumm_stricmp(filename.c_str(), "invalid_debug.bmp") == 0) { + return true; + } else if (scumm_stricmp(filename.c_str(), "syste_font.bmp") == 0) { + return true; + } + return false; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/file/base_resources.h b/engines/wintermute/base/file/base_resources.h new file mode 100644 index 0000000000..91c30bcfa7 --- /dev/null +++ b/engines/wintermute/base/file/base_resources.h @@ -0,0 +1,45 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_RESOURCES_H +#define WINTERMUTE_BASE_RESOURCES_H + +#include "common/stream.h" +#include "common/str.h" + +namespace Wintermute { + +class BaseResources { +public: + static Common::SeekableReadStream *getFile(const Common::String &filename); + static bool hasFile(const Common::String &filename); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/file/base_save_thumb_file.cpp b/engines/wintermute/base/file/base_save_thumb_file.cpp new file mode 100644 index 0000000000..94d3e5a94e --- /dev/null +++ b/engines/wintermute/base/file/base_save_thumb_file.cpp @@ -0,0 +1,153 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_persistence_manager.h" +#include "engines/wintermute/base/file/base_save_thumb_file.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////// +BaseSaveThumbFile::BaseSaveThumbFile() { + _data = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +BaseSaveThumbFile::~BaseSaveThumbFile() { + close(); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSaveThumbFile::open(const Common::String &filename) { + close(); + + if (scumm_strnicmp(filename.c_str(), "savegame:", 9) != 0) { + return STATUS_FAILED; + } + + char *tempFilename = new char[strlen(filename.c_str()) - 8]; + strcpy(tempFilename, filename.c_str() + 9); + for (uint32 i = 0; i < strlen(tempFilename); i++) { + if (tempFilename[i] < '0' || tempFilename[i] > '9') { + tempFilename[i] = '\0'; + break; + } + } + + // get slot number from name + int slot = atoi(tempFilename); + delete[] tempFilename; + + BasePersistenceManager *pm = new BasePersistenceManager(); + Common::String slotFilename = pm->getFilenameForSlot(slot); + if (!pm) { + return STATUS_FAILED; + } + + if (DID_FAIL(pm->initLoad(slotFilename))) { + delete pm; + return STATUS_FAILED; + } + + bool res; + + if (pm->_thumbnailDataSize != 0) { + _data = new byte[pm->_thumbnailDataSize]; + memcpy(_data, pm->_thumbnailData, pm->_thumbnailDataSize); + _size = pm->_thumbnailDataSize; + res = STATUS_OK; + } else { + res = STATUS_FAILED; + } + delete pm; + + return res; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSaveThumbFile::close() { + delete[] _data; + _data = NULL; + + _pos = 0; + _size = 0; + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSaveThumbFile::read(void *buffer, uint32 size) { + if (!_data || _pos + size > _size) { + return STATUS_FAILED; + } + + memcpy(buffer, (byte *)_data + _pos, size); + _pos += size; + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSaveThumbFile::seek(uint32 pos, int whence) { + if (!_data) { + return STATUS_FAILED; + } + + uint32 newPos = 0; + + switch (whence) { + case SEEK_SET: + newPos = pos; + break; + case SEEK_END: + newPos = _size + pos; + break; + case SEEK_CUR: + newPos = _pos + pos; + break; + } + + if (newPos > _size) { + return STATUS_FAILED; + } else { + _pos = newPos; + } + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/file/base_save_thumb_file.h b/engines/wintermute/base/file/base_save_thumb_file.h new file mode 100644 index 0000000000..3b217525fd --- /dev/null +++ b/engines/wintermute/base/file/base_save_thumb_file.h @@ -0,0 +1,52 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_SAVETHUMBFILE_H +#define WINTERMUTE_BASE_SAVETHUMBFILE_H + + +#include "engines/wintermute/base/file/base_file.h" + +namespace Wintermute { + +//TODO: Get rid of this +class BaseSaveThumbFile : public BaseFile { +public: + BaseSaveThumbFile(); + virtual ~BaseSaveThumbFile(); + virtual bool seek(uint32 pos, int whence = SEEK_SET); + virtual bool read(void *buffer, uint32 size); + virtual bool close(); + virtual bool open(const Common::String &filename); +private: + byte *_data; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/file/dcpackage.h b/engines/wintermute/base/file/dcpackage.h new file mode 100644 index 0000000000..2234139408 --- /dev/null +++ b/engines/wintermute/base/file/dcpackage.h @@ -0,0 +1,80 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef _DCPACKAGE_H_ +#define _DCPACKAGE_H_ + + +#define PACKAGE_MAGIC_1 0xDEC0ADDE +#define PACKAGE_MAGIC_2 0x4B4E554A // "JUNK" +#define PACKAGE_VERSION 0x00000200 +#define PACKAGE_EXTENSION "dcp" + +#include "common/stream.h" + +namespace Wintermute { + +struct TPackageHeader { + uint32 _magic1; + uint32 _magic2; + uint32 _packageVersion; + uint32 _gameVersion; + byte _priority; + byte _cd; + bool _masterIndex; + uint32 _creationTime; + char _desc[100]; + uint32 _numDirs; + // base_package.cpp: + void readFromStream(Common::ReadStream *stream); +}; + +/* +v2: uint32 DirOffset + + +Dir: byte NameLength + char Name [NameLength] + byte CD; + uint32 NumEntries + + +Entry: byte NameLength + char Name [NameLength] + uint32 Offset + uint32 Length + uint32 CompLength + uint32 Flags +v2: uint32 TimeDate1 + uint32 TimeDate2 // not used + +*/ + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/font/base_font.cpp b/engines/wintermute/base/font/base_font.cpp new file mode 100644 index 0000000000..87dd3da5a3 --- /dev/null +++ b/engines/wintermute/base/font/base_font.cpp @@ -0,0 +1,140 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/font/base_font.h" +#include "engines/wintermute/base/font/base_font_bitmap.h" +#include "engines/wintermute/base/font/base_font_truetype.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_file_manager.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +IMPLEMENT_PERSISTENT(BaseFont, false) + +////////////////////////////////////////////////////////////////////// +BaseFont::BaseFont(BaseGame *inGame) : BaseObject(inGame) { + +} + + +////////////////////////////////////////////////////////////////////// +BaseFont::~BaseFont() { +} + + +////////////////////////////////////////////////////////////////////// +void BaseFont::drawText(const byte *text, int x, int y, int width, TTextAlign align, int maxHeight, int maxLength) { +} + + +////////////////////////////////////////////////////////////////////// +int BaseFont::getTextHeight(byte *text, int width) { + return 0; +} + + +////////////////////////////////////////////////////////////////////// +int BaseFont::getTextWidth(byte *text, int maxLength) { + return 0; +} + + +////////////////////////////////////////////////////////////////////////// +int BaseFont::getLetterHeight() { + return 0; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseFont::persist(BasePersistenceManager *persistMgr) { + + BaseObject::persist(persistMgr); + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +BaseFont *BaseFont::createFromFile(BaseGame *gameRef, const Common::String &filename) { + if (isTrueType(gameRef, filename)) { + BaseFontTT *font = new BaseFontTT(gameRef); + if (font) { + if (DID_FAIL(font->loadFile(filename))) { + delete font; + return NULL; + } + } + return font; + } else { + BaseFontBitmap *font = new BaseFontBitmap(gameRef); + if (font) { + if (DID_FAIL(font->loadFile(filename))) { + delete font; + return NULL; + } + } + return font; + } +} + + +TOKEN_DEF_START +TOKEN_DEF(FONT) +TOKEN_DEF(TTFONT) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool BaseFont::isTrueType(BaseGame *gameRef, const Common::String &filename) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(FONT) + TOKEN_TABLE(TTFONT) + TOKEN_TABLE_END + + + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + return false; + } + + byte *workBuffer = buffer; + + char *params; + BaseParser parser; + + bool ret = false; + if (parser.getCommand((char **)&workBuffer, commands, (char **)¶ms) == TOKEN_TTFONT) { + ret = true; + } + + delete[] buffer; + return ret; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/font/base_font.h b/engines/wintermute/base/font/base_font.h new file mode 100644 index 0000000000..0abe62ab98 --- /dev/null +++ b/engines/wintermute/base/font/base_font.h @@ -0,0 +1,61 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_FONT_H +#define WINTERMUTE_BASE_FONT_H + +#include "engines/wintermute/base/base_object.h" + +#define NUM_CHARACTERS 256 + +namespace Wintermute { + +class BaseFont: public BaseObject { +public: + DECLARE_PERSISTENT(BaseFont, BaseObject) + virtual int getTextWidth(byte *text, int maxLength = -1); + virtual int getTextHeight(byte *text, int width); + virtual void drawText(const byte *text, int x, int y, int width, TTextAlign align = TAL_LEFT, int max_height = -1, int maxLength = -1); + virtual int getLetterHeight(); + + virtual void initLoop() {} + virtual void afterLoad() {} + BaseFont(BaseGame *inGame); + virtual ~BaseFont(); + + static BaseFont *createFromFile(BaseGame *game, const Common::String &filename); + +private: + //bool loadBuffer(byte * Buffer); + //bool loadFile(const char* Filename); + static bool isTrueType(BaseGame *game, const Common::String &filename); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/font/base_font_bitmap.cpp b/engines/wintermute/base/font/base_font_bitmap.cpp new file mode 100644 index 0000000000..55f46c476b --- /dev/null +++ b/engines/wintermute/base/font/base_font_bitmap.cpp @@ -0,0 +1,590 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/font/base_font_bitmap.h" +#include "engines/wintermute/utils/string_util.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_frame.h" +#include "engines/wintermute/base/gfx/base_surface.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_sub_frame.h" +#include "engines/wintermute/base/base_frame.h" +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/platform_osystem.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +IMPLEMENT_PERSISTENT(BaseFontBitmap, false) + +////////////////////////////////////////////////////////////////////// +BaseFontBitmap::BaseFontBitmap(BaseGame *inGame) : BaseFont(inGame) { + _subframe = NULL; + _sprite = NULL; + _widthsFrame = 0; + memset(_widths, 0, NUM_CHARACTERS); + _tileWidth = _tileHeight = _numColumns = 0; + _fontextFix = false; + _freezable = false; + _wholeCell = false; +} + + +////////////////////////////////////////////////////////////////////// +BaseFontBitmap::~BaseFontBitmap() { + delete _subframe; + delete _sprite; + _subframe = NULL; + _sprite = NULL; +} + + +////////////////////////////////////////////////////////////////////// +void BaseFontBitmap::drawText(const byte *text, int x, int y, int width, TTextAlign align, int maxHeight, int maxLength) { + textHeightDraw(text, x, y, width, align, true, maxHeight, maxLength); +} + + +////////////////////////////////////////////////////////////////////// +int BaseFontBitmap::getTextHeight(byte *text, int width) { + return textHeightDraw(text, 0, 0, width, TAL_LEFT, false); +} + + +////////////////////////////////////////////////////////////////////// +int BaseFontBitmap::getTextWidth(byte *text, int maxLength) { + AnsiString str; + + if (_gameRef->_textEncoding == TEXT_UTF8) { + WideString wstr = StringUtil::utf8ToWide(Utf8String((char *)text)); + str = StringUtil::wideToAnsi(wstr); + } else { + str = AnsiString((char *)text); + } + + if (maxLength >= 0 && str.size() > (uint32)maxLength) { + str = Common::String(str.c_str(), (uint32)maxLength); + } + //str.substr(0, maxLength); // TODO: Remove + + int textWidth = 0; + for (int i = 0; (uint32)i < str.size(); i++) { + textWidth += getCharWidth((byte)str[i]); + } + + return textWidth; +} + + +////////////////////////////////////////////////////////////////////// +int BaseFontBitmap::textHeightDraw(const byte *text, int x, int y, int width, TTextAlign align, bool draw, int maxHeight, int maxLength) { + if (maxLength == 0) { + return 0; + } + + if (text == NULL || text[0] == '\0') { + return _tileHeight; + } + + AnsiString str; + + if (_gameRef->_textEncoding == TEXT_UTF8) { + WideString wstr = StringUtil::utf8ToWide(Utf8String((const char *)text)); + str = StringUtil::wideToAnsi(wstr); + } else { + str = AnsiString((const char *)text); + } + if (str.empty()) { + return 0; + } + + int lineLength = 0; + int realLength = 0; + int numLines = 0; + + int i; + + int index = -1; + int start = 0; + int end = 0; + int last_end = 0; + + bool done = false; + bool newLine = false; + bool longLine = false; + + if (draw) { + _gameRef->_renderer->startSpriteBatch(); + } + + while (!done) { + if (maxHeight > 0 && (numLines + 1)*_tileHeight > maxHeight) { + if (draw) { + _gameRef->_renderer->endSpriteBatch(); + } + return numLines * _tileHeight; + } + + index++; + + if (str[index] == ' ' && (maxHeight < 0 || maxHeight / _tileHeight > 1)) { + end = index - 1; + realLength = lineLength; + } + + if (str[index] == '\n') { + end = index - 1; + realLength = lineLength; + newLine = true; + } + + if (lineLength + getCharWidth(str[index]) > width && last_end == end) { + end = index - 1; + realLength = lineLength; + newLine = true; + longLine = true; + } + + if ((int)str.size() == (index + 1) || (maxLength >= 0 && index == maxLength - 1)) { + done = true; + if (!newLine) { + end = index; + lineLength += getCharWidth(str[index]); + realLength = lineLength; + } + } else { + lineLength += getCharWidth(str[index]); + } + + if ((lineLength > width) || done || newLine) { + if (end < 0) { + done = true; + } + int startX; + switch (align) { + case TAL_CENTER: + startX = x + (width - realLength) / 2; + break; + case TAL_RIGHT: + startX = x + width - realLength; + break; + case TAL_LEFT: + startX = x; + break; + default: + error("BaseFontBitmap::TextHeightDraw - Unhandled enum"); + break; + } + for (i = start; i < end + 1; i++) { + if (draw) { + drawChar(str[i], startX, y); + } + startX += getCharWidth(str[i]); + } + y += _tileHeight; + last_end = end; + if (longLine) { + end--; + } + start = end + 2; + index = end + 1; + lineLength = 0; + newLine = false; + longLine = false; + numLines++; + } + } + + if (draw) { + _gameRef->_renderer->endSpriteBatch(); + } + + return numLines * _tileHeight; +} + + +////////////////////////////////////////////////////////////////////// +void BaseFontBitmap::drawChar(byte c, int x, int y) { + if (_fontextFix) { + c--; + } + + int row, col; + + row = c / _numColumns; + col = c % _numColumns; + + Rect32 rect; + /* l t r b */ + int tileWidth; + if (_wholeCell) { + tileWidth = _tileWidth; + } else { + tileWidth = _widths[c]; + } + + BasePlatform::setRect(&rect, col * _tileWidth, row * _tileHeight, col * _tileWidth + tileWidth, (row + 1)*_tileHeight); + bool handled = false; + if (_sprite) { + _sprite->getCurrentFrame(); + if (_sprite->_currentFrame >= 0 && _sprite->_currentFrame < (int32)_sprite->_frames.size() && _sprite->_frames[_sprite->_currentFrame]) { + if (_sprite->_frames[_sprite->_currentFrame]->_subframes.size() > 0) { + _sprite->_frames[_sprite->_currentFrame]->_subframes[0]->_surface->displayTrans(x, y, rect); + } + handled = true; + } + } + if (!handled && _subframe) { + _subframe->_surface->displayTrans(x, y, rect); + } +} + + +////////////////////////////////////////////////////////////////////// +bool BaseFontBitmap::loadFile(const Common::String &filename) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "BaseFontBitmap::LoadFile failed for file '%s'", filename.c_str()); + return STATUS_FAILED; + } + + bool ret; + + setFilename(filename.c_str()); + + if (DID_FAIL(ret = loadBuffer(buffer))) { + _gameRef->LOG(0, "Error parsing FONT file '%s'", filename.c_str()); + } + + delete[] buffer; + + return ret; +} + + +TOKEN_DEF_START +TOKEN_DEF(FONTEXT_FIX) +TOKEN_DEF(FONT) +TOKEN_DEF(IMAGE) +TOKEN_DEF(TRANSPARENT) +TOKEN_DEF(COLUMNS) +TOKEN_DEF(TILE_WIDTH) +TOKEN_DEF(TILE_HEIGHT) +TOKEN_DEF(DEFAULT_WIDTH) +TOKEN_DEF(WIDTHS) +TOKEN_DEF(AUTO_WIDTH) +TOKEN_DEF(SPACE_WIDTH) +TOKEN_DEF(EXPAND_WIDTH) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF(SPRITE) +TOKEN_DEF(WIDTHS_FRAME) +TOKEN_DEF(PAINT_WHOLE_CELL) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////// +bool BaseFontBitmap::loadBuffer(byte *buffer) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(FONTEXT_FIX) + TOKEN_TABLE(FONT) + TOKEN_TABLE(IMAGE) + TOKEN_TABLE(TRANSPARENT) + TOKEN_TABLE(COLUMNS) + TOKEN_TABLE(TILE_WIDTH) + TOKEN_TABLE(TILE_HEIGHT) + TOKEN_TABLE(DEFAULT_WIDTH) + TOKEN_TABLE(WIDTHS) + TOKEN_TABLE(AUTO_WIDTH) + TOKEN_TABLE(SPACE_WIDTH) + TOKEN_TABLE(EXPAND_WIDTH) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE(SPRITE) + TOKEN_TABLE(WIDTHS_FRAME) + TOKEN_TABLE(PAINT_WHOLE_CELL) + TOKEN_TABLE_END + + char *params; + int cmd; + BaseParser parser; + + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_FONT) { + _gameRef->LOG(0, "'FONT' keyword expected."); + return STATUS_FAILED; + } + buffer = (byte *)params; + + int widths[300]; + int num = 0, defaultWidth = 8; + int lastWidth = 0; + int i; + int r = 255, g = 255, b = 255; + bool custoTrans = false; + char *surfaceFile = NULL; + char *spriteFile = NULL; + + bool autoWidth = false; + int spaceWidth = 0; + int expandWidth = 0; + + while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + + switch (cmd) { + case TOKEN_IMAGE: + surfaceFile = (char *)params; + break; + + case TOKEN_SPRITE: + spriteFile = (char *)params; + break; + + case TOKEN_TRANSPARENT: + parser.scanStr(params, "%d,%d,%d", &r, &g, &b); + custoTrans = true; + break; + + case TOKEN_WIDTHS: + parser.scanStr(params, "%D", widths, &num); + for (i = 0; lastWidth < NUM_CHARACTERS && num > 0; lastWidth++, num--, i++) { + _widths[lastWidth] = (byte)widths[i]; + } + break; + + case TOKEN_DEFAULT_WIDTH: + parser.scanStr(params, "%d", &defaultWidth); + break; + + case TOKEN_WIDTHS_FRAME: + parser.scanStr(params, "%d", &_widthsFrame); + break; + + case TOKEN_COLUMNS: + parser.scanStr(params, "%d", &_numColumns); + break; + + case TOKEN_TILE_WIDTH: + parser.scanStr(params, "%d", &_tileWidth); + break; + + case TOKEN_TILE_HEIGHT: + parser.scanStr(params, "%d", &_tileHeight); + break; + + case TOKEN_AUTO_WIDTH: + parser.scanStr(params, "%b", &autoWidth); + break; + + case TOKEN_FONTEXT_FIX: + parser.scanStr(params, "%b", &_fontextFix); + break; + + case TOKEN_PAINT_WHOLE_CELL: + parser.scanStr(params, "%b", &_wholeCell); + break; + + case TOKEN_SPACE_WIDTH: + parser.scanStr(params, "%d", &spaceWidth); + break; + + case TOKEN_EXPAND_WIDTH: + parser.scanStr(params, "%d", &expandWidth); + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty((byte *)params, false); + break; + } + + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in FONT definition"); + return STATUS_FAILED; + } + + if (spriteFile != NULL) { + delete _sprite; + _sprite = new BaseSprite(_gameRef, this); + if (!_sprite || DID_FAIL(_sprite->loadFile(spriteFile))) { + delete _sprite; + _sprite = NULL; + } + } + + if (surfaceFile != NULL && !_sprite) { + _subframe = new BaseSubFrame(_gameRef); + if (custoTrans) { + _subframe->setSurface(surfaceFile, false, r, g, b); + } else { + _subframe->setSurface(surfaceFile); + } + } + + + if (((_subframe == NULL || _subframe->_surface == NULL) && _sprite == NULL) || _numColumns == 0 || _tileWidth == 0 || _tileHeight == 0) { + _gameRef->LOG(0, "Incomplete font definition"); + return STATUS_FAILED; + } + + if (autoWidth) { + // calculate characters width + getWidths(); + + // do we need to modify widths? + if (expandWidth != 0) { + for (i = 0; i < NUM_CHARACTERS; i++) { + int newWidth = (int)_widths[i] + expandWidth; + if (newWidth < 0) { + newWidth = 0; + } + + _widths[i] = (byte)newWidth; + } + } + + // handle space character + uint32 spaceChar = ' '; + if (_fontextFix) { + spaceChar--; + } + + if (spaceWidth != 0) { + _widths[spaceChar] = spaceWidth; + } else { + if (_widths[spaceChar] == expandWidth || _widths[spaceChar] == 0) { + _widths[spaceChar] = (_widths['m'] + _widths['i']) / 2; + } + } + } else { + for (i = lastWidth; i < NUM_CHARACTERS; i++) { + _widths[i] = defaultWidth; + } + } + + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseFontBitmap::persist(BasePersistenceManager *persistMgr) { + + BaseFont::persist(persistMgr); + persistMgr->transfer(TMEMBER(_numColumns)); + + persistMgr->transfer(TMEMBER(_subframe)); + persistMgr->transfer(TMEMBER(_tileHeight)); + persistMgr->transfer(TMEMBER(_tileWidth)); + persistMgr->transfer(TMEMBER(_sprite)); + persistMgr->transfer(TMEMBER(_widthsFrame)); + + if (persistMgr->getIsSaving()) { + persistMgr->putBytes(_widths, sizeof(_widths)); + } else { + persistMgr->getBytes(_widths, sizeof(_widths)); + } + + + persistMgr->transfer(TMEMBER(_fontextFix)); + persistMgr->transfer(TMEMBER(_wholeCell)); + + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +int BaseFontBitmap::getCharWidth(byte index) { + if (_fontextFix) { + index--; + } + return _widths[index]; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseFontBitmap::getWidths() { + BaseSurface *surf = NULL; + + if (_sprite) { + if (_widthsFrame >= 0 && _widthsFrame < (int32)_sprite->_frames.size()) { + if (_sprite->_frames[_widthsFrame] && (int32)_sprite->_frames[_widthsFrame]->_subframes.size() > 0) { + surf = _sprite->_frames[_widthsFrame]->_subframes[0]->_surface; + } + } + } + if (surf == NULL && _subframe) { + surf = _subframe->_surface; + } + if (!surf || DID_FAIL(surf->startPixelOp())) { + return STATUS_FAILED; + } + + + for (int i = 0; i < NUM_CHARACTERS; i++) { + int xxx = (i % _numColumns) * _tileWidth; + int yyy = (i / _numColumns) * _tileHeight; + + + int minCol = -1; + for (int row = 0; row < _tileHeight; row++) { + for (int col = _tileWidth - 1; col >= minCol + 1; col--) { + if (xxx + col < 0 || xxx + col >= surf->getWidth() || yyy + row < 0 || yyy + row >= surf->getHeight()) { + continue; + } + if (!surf->isTransparentAtLite(xxx + col, yyy + row)) { + //min_col = col; + minCol = MAX(col, minCol); + break; + } + } + if (minCol == _tileWidth - 1) { + break; + } + } + + _widths[i] = minCol + 1; + } + surf->endPixelOp(); + /* + _gameRef->LOG(0, "----- %s ------", _filename); + for(int j=0; j<16; j++) + { + _gameRef->LOG(0, "%02d %02d %02d %02d %02d %02d %02d %02d %02d %02d %02d %02d %02d %02d %02d %02d", _widths[j*16+0], _widths[j*16+1], _widths[j*16+2], _widths[j*16+3], _widths[j*16+4], _widths[j*16+5], _widths[j*16+6], _widths[j*16+7], _widths[j*16+8], _widths[j*16+9], _widths[j*16+10], _widths[j*16+11], _widths[j*16+12], _widths[j*16+13], _widths[j*16+14], _widths[j*16+15]); + } + */ + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +int BaseFontBitmap::getLetterHeight() { + return _tileHeight; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/font/base_font_bitmap.h b/engines/wintermute/base/font/base_font_bitmap.h new file mode 100644 index 0000000000..2f3a69d097 --- /dev/null +++ b/engines/wintermute/base/font/base_font_bitmap.h @@ -0,0 +1,71 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_FONTBITMAP_H +#define WINTERMUTE_BASE_FONTBITMAP_H + + +#include "engines/wintermute/base/font/base_font.h" + +namespace Wintermute { +class BaseSubFrame; +class BaseFontBitmap : public BaseFont { +public: + DECLARE_PERSISTENT(BaseFontBitmap, BaseFont) + bool loadBuffer(byte *Buffer); + bool loadFile(const Common::String &filename); + virtual int getTextWidth(byte *text, int maxLength = -1); + virtual int getTextHeight(byte *text, int width); + virtual void drawText(const byte *text, int x, int y, int width, TTextAlign align = TAL_LEFT, int max_height = -1, int maxLength = -1); + virtual int getLetterHeight(); + + BaseFontBitmap(BaseGame *inGame); + virtual ~BaseFontBitmap(); + +private: + bool getWidths(); + BaseSprite *_sprite; + int _widthsFrame; + bool _fontextFix; + int _numColumns; + int _tileHeight; + int _tileWidth; + byte _widths[NUM_CHARACTERS]; + BaseSubFrame *_subframe; + bool _wholeCell; + + int getCharWidth(byte index); + void drawChar(byte c, int x, int y); + + int textHeightDraw(const byte *text, int x, int y, int width, TTextAlign align, bool draw, int max_height = -1, int maxLength = -1); + +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/font/base_font_storage.cpp b/engines/wintermute/base/font/base_font_storage.cpp new file mode 100644 index 0000000000..8128ffe897 --- /dev/null +++ b/engines/wintermute/base/font/base_font_storage.cpp @@ -0,0 +1,141 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/font/base_font_storage.h" +#include "engines/wintermute/base/font/base_font.h" +#include "engines/wintermute/base/base_game.h" +#include "common/str.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +IMPLEMENT_PERSISTENT(BaseFontStorage, true) + +////////////////////////////////////////////////////////////////////////// +BaseFontStorage::BaseFontStorage(BaseGame *inGame) : BaseClass(inGame) { +} + +////////////////////////////////////////////////////////////////////////// +BaseFontStorage::~BaseFontStorage() { + cleanup(true); +} + +////////////////////////////////////////////////////////////////////////// +bool BaseFontStorage::cleanup(bool warn) { + for (uint32 i = 0; i < _fonts.size(); i++) { + if (warn) { + _gameRef->LOG(0, "Removing orphan font '%s'", _fonts[i]->getFilename()); + } + delete _fonts[i]; + } + _fonts.clear(); + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseFontStorage::initLoop() { + for (uint32 i = 0; i < _fonts.size(); i++) { + _fonts[i]->initLoop(); + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +BaseFont *BaseFontStorage::addFont(const Common::String &filename) { + if (!filename.size()) { + return NULL; + } + + for (uint32 i = 0; i < _fonts.size(); i++) { + if (scumm_stricmp(_fonts[i]->getFilename(), filename.c_str()) == 0) { + _fonts[i]->_refCount++; + return _fonts[i]; + } + } + + /* + BaseFont* font = new BaseFont(_gameRef); + if (!font) return NULL; + + if (DID_FAIL(font->loadFile(filename))) { + delete font; + return NULL; + } + else { + font->_refCount = 1; + _fonts.add(font); + return font; + } + */ + BaseFont *font = BaseFont::createFromFile(_gameRef, filename); + if (font) { + font->_refCount = 1; + _fonts.add(font); + } + return font; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseFontStorage::removeFont(BaseFont *font) { + if (!font) { + return STATUS_FAILED; + } + + for (uint32 i = 0; i < _fonts.size(); i++) { + if (_fonts[i] == font) { + _fonts[i]->_refCount--; + if (_fonts[i]->_refCount <= 0) { + delete _fonts[i]; + _fonts.remove_at(i); + } + break; + } + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseFontStorage::persist(BasePersistenceManager *persistMgr) { + + if (!persistMgr->getIsSaving()) { + cleanup(false); + } + + persistMgr->transfer(TMEMBER(_gameRef)); + _fonts.persist(persistMgr); + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/font/base_font_storage.h b/engines/wintermute/base/font/base_font_storage.h new file mode 100644 index 0000000000..60874167e7 --- /dev/null +++ b/engines/wintermute/base/font/base_font_storage.h @@ -0,0 +1,55 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_FONTSTORAGE_H +#define WINTERMUTE_BASE_FONTSTORAGE_H + + +#include "engines/wintermute/base/base.h" +#include "engines/wintermute/persistent.h" +#include "engines/wintermute/coll_templ.h" + +namespace Wintermute { + +class BaseFont; + +class BaseFontStorage : public BaseClass { +public: + DECLARE_PERSISTENT(BaseFontStorage, BaseClass) + bool cleanup(bool warn = false); + bool removeFont(BaseFont *font); + BaseFont *addFont(const Common::String &filename); + BaseFontStorage(BaseGame *inGame); + virtual ~BaseFontStorage(); + BaseArray<BaseFont *> _fonts; + bool initLoop(); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/font/base_font_truetype.cpp b/engines/wintermute/base/font/base_font_truetype.cpp new file mode 100644 index 0000000000..83f0a35f53 --- /dev/null +++ b/engines/wintermute/base/font/base_font_truetype.cpp @@ -0,0 +1,634 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/font/base_font_truetype.h" +#include "engines/wintermute/utils/string_util.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/base/gfx/base_surface.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/utils/utils.h" +#include "engines/wintermute/platform_osystem.h" +#include "engines/wintermute/wintermute.h" +#include "graphics/fonts/ttf.h" +#include "graphics/fontman.h" +#include "common/unzip.h" +#include <limits.h> + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(BaseFontTT, false) + +////////////////////////////////////////////////////////////////////////// +BaseFontTT::BaseFontTT(BaseGame *inGame) : BaseFont(inGame) { + _fontHeight = 12; + _isBold = _isItalic = _isUnderline = _isStriked = false; + + _fontFile = NULL; + _font = NULL; + _fallbackFont = NULL; + _deletableFont = NULL; + + for (int i = 0; i < NUM_CACHED_TEXTS; i++) { + _cachedTexts[i] = NULL; + } + + _lineHeight = 0; + _maxCharWidth = _maxCharHeight = 0; +} + +////////////////////////////////////////////////////////////////////////// +BaseFontTT::~BaseFontTT(void) { + clearCache(); + + for (uint32 i = 0; i < _layers.size(); i++) { + delete _layers[i]; + } + _layers.clear(); + + delete[] _fontFile; + _fontFile = NULL; + + delete _deletableFont; + _font = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +void BaseFontTT::clearCache() { + for (int i = 0; i < NUM_CACHED_TEXTS; i++) { + if (_cachedTexts[i]) { + delete _cachedTexts[i]; + } + _cachedTexts[i] = NULL; + } +} + +////////////////////////////////////////////////////////////////////////// +void BaseFontTT::initLoop() { + // we need more aggressive cache management on iOS not to waste too much memory on fonts + if (_gameRef->_constrainedMemory) { + // purge all cached images not used in the last frame + for (int i = 0; i < NUM_CACHED_TEXTS; i++) { + if (_cachedTexts[i] == NULL) { + continue; + } + + if (!_cachedTexts[i]->_marked) { + delete _cachedTexts[i]; + _cachedTexts[i] = NULL; + } else { + _cachedTexts[i]->_marked = false; + } + } + } +} + +////////////////////////////////////////////////////////////////////////// +int BaseFontTT::getTextWidth(byte *text, int maxLength) { + WideString textStr; + + if (_gameRef->_textEncoding == TEXT_UTF8) { + textStr = StringUtil::utf8ToWide((char *)text); + } else { + textStr = StringUtil::ansiToWide((char *)text); + } + + if (maxLength >= 0 && textStr.size() > (uint32)maxLength) { + textStr = Common::String(textStr.c_str(), (uint32)maxLength); + } + //text = text.substr(0, MaxLength); // TODO: Remove + + int textWidth, textHeight; + measureText(textStr, -1, -1, textWidth, textHeight); + + return textWidth; +} + +////////////////////////////////////////////////////////////////////////// +int BaseFontTT::getTextHeight(byte *text, int width) { + WideString textStr; + + if (_gameRef->_textEncoding == TEXT_UTF8) { + textStr = StringUtil::utf8ToWide((char *)text); + } else { + textStr = StringUtil::ansiToWide((char *)text); + } + + + int textWidth, textHeight; + measureText(textStr, width, -1, textWidth, textHeight); + + return textHeight; +} + + +////////////////////////////////////////////////////////////////////////// +void BaseFontTT::drawText(const byte *text, int x, int y, int width, TTextAlign align, int maxHeight, int maxLength) { + if (text == NULL || strcmp((const char *)text, "") == 0) { + return; + } + + WideString textStr = (const char *)text; + + // TODO: Why do we still insist on Widestrings everywhere? + /* if (_gameRef->_textEncoding == TEXT_UTF8) text = StringUtil::Utf8ToWide((char *)Text); + else text = StringUtil::AnsiToWide((char *)Text);*/ + // HACK: J.U.L.I.A. uses CP1252, we need to fix that, + // And we still don't have any UTF8-support. + if (_gameRef->_textEncoding != TEXT_UTF8) { + textStr = StringUtil::ansiToWide((const char *)text); + } + + if (maxLength >= 0 && textStr.size() > (uint32)maxLength) { + textStr = Common::String(textStr.c_str(), (uint32)maxLength); + } + //text = text.substr(0, MaxLength); // TODO: Remove + + BaseRenderer *renderer = _gameRef->_renderer; + + // find cached surface, if exists + uint32 minUseTime = UINT_MAX; + int minIndex = -1; + BaseSurface *surface = NULL; + int textOffset = 0; + + for (int i = 0; i < NUM_CACHED_TEXTS; i++) { + if (_cachedTexts[i] == NULL) { + minUseTime = 0; + minIndex = i; + } else { + if (_cachedTexts[i]->_text == textStr && _cachedTexts[i]->_align == align && _cachedTexts[i]->_width == width && _cachedTexts[i]->_maxHeight == maxHeight && _cachedTexts[i]->_maxLength == maxLength) { + surface = _cachedTexts[i]->_surface; + textOffset = _cachedTexts[i]->_textOffset; + _cachedTexts[i]->_marked = true; + _cachedTexts[i]->_lastUsed = g_system->getMillis(); + break; + } else { + if (_cachedTexts[i]->_lastUsed < minUseTime) { + minUseTime = _cachedTexts[i]->_lastUsed; + minIndex = i; + } + } + } + } + + // not found, create one + if (!surface) { + debugC(kWintermuteDebugFont, "Draw text: %s", text); + surface = renderTextToTexture(textStr, width, align, maxHeight, textOffset); + if (surface) { + // write surface to cache + if (_cachedTexts[minIndex] != NULL) { + delete _cachedTexts[minIndex]; + } + _cachedTexts[minIndex] = new BaseCachedTTFontText; + + _cachedTexts[minIndex]->_surface = surface; + _cachedTexts[minIndex]->_align = align; + _cachedTexts[minIndex]->_width = width; + _cachedTexts[minIndex]->_maxHeight = maxHeight; + _cachedTexts[minIndex]->_maxLength = maxLength; + _cachedTexts[minIndex]->_text = textStr; + _cachedTexts[minIndex]->_textOffset = textOffset; + _cachedTexts[minIndex]->_marked = true; + _cachedTexts[minIndex]->_lastUsed = g_system->getMillis(); + } + } + + + // and paint it + if (surface) { + Rect32 rc; + BasePlatform::setRect(&rc, 0, 0, surface->getWidth(), surface->getHeight()); + for (uint32 i = 0; i < _layers.size(); i++) { + uint32 color = _layers[i]->_color; + uint32 origForceAlpha = renderer->_forceAlphaColor; + if (renderer->_forceAlphaColor != 0) { + color = BYTETORGBA(RGBCOLGetR(color), RGBCOLGetG(color), RGBCOLGetB(color), RGBCOLGetA(renderer->_forceAlphaColor)); + renderer->_forceAlphaColor = 0; + } + surface->displayTransOffset(x, y - textOffset, rc, color, BLEND_NORMAL, false, false, _layers[i]->_offsetX, _layers[i]->_offsetY); + + renderer->_forceAlphaColor = origForceAlpha; + } + } + + +} + +////////////////////////////////////////////////////////////////////////// +BaseSurface *BaseFontTT::renderTextToTexture(const WideString &text, int width, TTextAlign align, int maxHeight, int &textOffset) { + //TextLineList lines; + // TODO: Use WideString-conversion here. + //WrapText(text, width, maxHeight, lines); + Common::Array<Common::String> lines; + _font->wordWrapText(text, width, lines); + + while (maxHeight > 0 && lines.size() * _lineHeight > maxHeight) { + lines.pop_back(); + } + if (lines.size() == 0) { + return NULL; + } + + Graphics::TextAlign alignment = Graphics::kTextAlignInvalid; + if (align == TAL_LEFT) { + alignment = Graphics::kTextAlignLeft; + } else if (align == TAL_CENTER) { + alignment = Graphics::kTextAlignCenter; + } else if (align == TAL_RIGHT) { + alignment = Graphics::kTextAlignRight; + } + + debugC(kWintermuteDebugFont, "%s %d %d %d %d", text.c_str(), RGBCOLGetR(_layers[0]->_color), RGBCOLGetG(_layers[0]->_color), RGBCOLGetB(_layers[0]->_color), RGBCOLGetA(_layers[0]->_color)); +// void drawString(Surface *dst, const Common::String &str, int x, int y, int w, uint32 color, TextAlign align = kTextAlignLeft, int deltax = 0, bool useEllipsis = true) const; + Graphics::Surface *surface = new Graphics::Surface(); + if (_deletableFont) { // We actually have a TTF + surface->create((uint16)width, (uint16)(_lineHeight * lines.size()), Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24)); + } else { // We are using a fallback, they can't do 32bpp + surface->create((uint16)width, (uint16)(_lineHeight * lines.size()), Graphics::PixelFormat(2, 5, 5, 5, 1, 11, 6, 1, 0)); + } + uint32 useColor = 0xffffffff; + Common::Array<Common::String>::iterator it; + int heightOffset = 0; + for (it = lines.begin(); it != lines.end(); ++it) { + _font->drawString(surface, *it, 0, heightOffset, width, useColor, alignment); + heightOffset += (int)_lineHeight; + } + + BaseSurface *retSurface = _gameRef->_renderer->createSurface(); + Graphics::Surface *convertedSurface = surface->convertTo(Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24)); + retSurface->putSurface(*convertedSurface, true); + convertedSurface->free(); + surface->free(); + delete surface; + delete convertedSurface; + return retSurface; + // TODO: _isUnderline, _isBold, _isItalic, _isStriked +} + + +////////////////////////////////////////////////////////////////////////// +int BaseFontTT::getLetterHeight() { + return (int)getLineHeight(); +} + + +////////////////////////////////////////////////////////////////////// +bool BaseFontTT::loadFile(const Common::String &filename) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "BaseFontTT::LoadFile failed for file '%s'", filename.c_str()); + return STATUS_FAILED; + } + + bool ret; + + setFilename(filename.c_str()); + + if (DID_FAIL(ret = loadBuffer(buffer))) { + _gameRef->LOG(0, "Error parsing TTFONT file '%s'", filename.c_str()); + } + + delete[] buffer; + + return ret; +} + + +TOKEN_DEF_START +TOKEN_DEF(TTFONT) +TOKEN_DEF(SIZE) +TOKEN_DEF(FACE) +TOKEN_DEF(FILENAME) +TOKEN_DEF(BOLD) +TOKEN_DEF(ITALIC) +TOKEN_DEF(UNDERLINE) +TOKEN_DEF(STRIKE) +TOKEN_DEF(CHARSET) +TOKEN_DEF(COLOR) +TOKEN_DEF(ALPHA) +TOKEN_DEF(LAYER) +TOKEN_DEF(OFFSET_X) +TOKEN_DEF(OFFSET_Y) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////// +bool BaseFontTT::loadBuffer(byte *buffer) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(TTFONT) + TOKEN_TABLE(SIZE) + TOKEN_TABLE(FACE) + TOKEN_TABLE(FILENAME) + TOKEN_TABLE(BOLD) + TOKEN_TABLE(ITALIC) + TOKEN_TABLE(UNDERLINE) + TOKEN_TABLE(STRIKE) + TOKEN_TABLE(CHARSET) + TOKEN_TABLE(COLOR) + TOKEN_TABLE(ALPHA) + TOKEN_TABLE(LAYER) + TOKEN_TABLE_END + + char *params; + int cmd; + BaseParser parser; + + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_TTFONT) { + _gameRef->LOG(0, "'TTFONT' keyword expected."); + return STATUS_FAILED; + } + buffer = (byte *)params; + + uint32 baseColor = 0x00000000; + + while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_SIZE: + parser.scanStr(params, "%d", &_fontHeight); + break; + + case TOKEN_FACE: + // we don't need this anymore + break; + + case TOKEN_FILENAME: + BaseUtils::setString(&_fontFile, params); + break; + + case TOKEN_BOLD: + parser.scanStr(params, "%b", &_isBold); + break; + + case TOKEN_ITALIC: + parser.scanStr(params, "%b", &_isItalic); + break; + + case TOKEN_UNDERLINE: + parser.scanStr(params, "%b", &_isUnderline); + break; + + case TOKEN_STRIKE: + parser.scanStr(params, "%b", &_isStriked); + break; + + case TOKEN_CHARSET: + // we don't need this anymore + break; + + case TOKEN_COLOR: { + int r, g, b; + parser.scanStr(params, "%d,%d,%d", &r, &g, &b); + baseColor = BYTETORGBA(r, g, b, RGBCOLGetA(baseColor)); + } + break; + + case TOKEN_ALPHA: { + int a; + parser.scanStr(params, "%d", &a); + baseColor = BYTETORGBA(RGBCOLGetR(baseColor), RGBCOLGetG(baseColor), RGBCOLGetB(baseColor), a); + } + break; + + case TOKEN_LAYER: { + BaseTTFontLayer *layer = new BaseTTFontLayer; + if (layer && DID_SUCCEED(parseLayer(layer, (byte *)params))) { + _layers.add(layer); + } else { + delete layer; + layer = NULL; + cmd = PARSERR_TOKENNOTFOUND; + } + } + break; + + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in TTFONT definition"); + return STATUS_FAILED; + } + + // create at least one layer + if (_layers.size() == 0) { + BaseTTFontLayer *layer = new BaseTTFontLayer; + layer->_color = baseColor; + _layers.add(layer); + } + + if (!_fontFile) { + BaseUtils::setString(&_fontFile, "arial.ttf"); + } + + return initFont(); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseFontTT::parseLayer(BaseTTFontLayer *layer, byte *buffer) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(OFFSET_X) + TOKEN_TABLE(OFFSET_Y) + TOKEN_TABLE(COLOR) + TOKEN_TABLE(ALPHA) + TOKEN_TABLE_END + + char *params; + int cmd; + BaseParser parser; + + while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_OFFSET_X: + parser.scanStr(params, "%d", &layer->_offsetX); + break; + + case TOKEN_OFFSET_Y: + parser.scanStr(params, "%d", &layer->_offsetY); + break; + + case TOKEN_COLOR: { + int r, g, b; + parser.scanStr(params, "%d,%d,%d", &r, &g, &b); + layer->_color = BYTETORGBA(r, g, b, RGBCOLGetA(layer->_color)); + } + break; + + case TOKEN_ALPHA: { + int a; + parser.scanStr(params, "%d", &a); + layer->_color = BYTETORGBA(RGBCOLGetR(layer->_color), RGBCOLGetG(layer->_color), RGBCOLGetB(layer->_color), a); + } + break; + } + } + if (cmd != PARSERR_EOF) { + return STATUS_FAILED; + } else { + return STATUS_OK; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseFontTT::persist(BasePersistenceManager *persistMgr) { + BaseFont::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_isBold)); + persistMgr->transfer(TMEMBER(_isItalic)); + persistMgr->transfer(TMEMBER(_isUnderline)); + persistMgr->transfer(TMEMBER(_isStriked)); + persistMgr->transfer(TMEMBER(_fontHeight)); + persistMgr->transfer(TMEMBER(_fontFile)); + + + // persist layers + int numLayers; + if (persistMgr->getIsSaving()) { + numLayers = _layers.size(); + persistMgr->transfer(TMEMBER(numLayers)); + for (int i = 0; i < numLayers; i++) { + _layers[i]->persist(persistMgr); + } + } else { + numLayers = _layers.size(); + persistMgr->transfer(TMEMBER(numLayers)); + for (int i = 0; i < numLayers; i++) { + BaseTTFontLayer *layer = new BaseTTFontLayer; + layer->persist(persistMgr); + _layers.add(layer); + } + } + + if (!persistMgr->getIsSaving()) { + for (int i = 0; i < NUM_CACHED_TEXTS; i++) { + _cachedTexts[i] = NULL; + } + _fallbackFont = _font = _deletableFont = NULL; + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +void BaseFontTT::afterLoad() { + initFont(); +} + +////////////////////////////////////////////////////////////////////////// +bool BaseFontTT::initFont() { + if (!_fontFile) { + return STATUS_FAILED; + } +#ifdef USE_FREETYPE2 + Common::SeekableReadStream *file = BaseFileManager::getEngineInstance()->openFile(_fontFile); + if (!file) { + if (Common::String(_fontFile) != "arial.ttf") { + warning("%s has no replacement font yet, using FreeSans for now (if available)", _fontFile); + } + // Fallback1: Try to find FreeSans.ttf + file = SearchMan.createReadStreamForMember("FreeSans.ttf"); + } + + if (file) { + _deletableFont = Graphics::loadTTFFont(*file, 96, _fontHeight); // Use the same dpi as WME (96 vs 72). + _font = _deletableFont; + BaseFileManager::getEngineInstance()->closeFile(file); + file = NULL; + } + + // Fallback2: Try to find ScummModern.zip, and get the font from there: + if (!_font) { + Common::SeekableReadStream *themeFile = SearchMan.createReadStreamForMember("scummmodern.zip"); + if (themeFile) { + Common::Archive *themeArchive = Common::makeZipArchive(themeFile); + if (themeArchive->hasFile("FreeSans.ttf")) { + file = NULL; + file = themeArchive->createReadStreamForMember("FreeSans.ttf"); + _deletableFont = Graphics::loadTTFFont(*file, 96, _fontHeight); // Use the same dpi as WME (96 vs 72). + _font = _deletableFont; + } + // We're not using BaseFileManager, so clean up after ourselves: + delete file; + file = NULL; + delete themeArchive; + themeArchive = NULL; + } + } + + // Fallback3: Try to ask FontMan for the FreeSans.ttf ScummModern.zip uses: + if (!_font) { + // Really not desireable, as we will get a font with dpi-72 then + Common::String fontName = Common::String::format("%s-%s@%d", "FreeSans.ttf", "ASCII", _fontHeight); + warning("Looking for %s", fontName.c_str()); + _font = FontMan.getFontByName(fontName); + } +#endif // USE_FREETYPE2 + + // Fallback4: Just use the Big GUI-font. (REALLY undesireable) + if (!_font) { + _font = _fallbackFont = FontMan.getFontByUsage(Graphics::FontManager::kBigGUIFont); + warning("BaseFontTT::InitFont - Couldn't load font: %s", _fontFile); + } + _lineHeight = _font->getFontHeight(); + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +void BaseFontTT::measureText(const WideString &text, int maxWidth, int maxHeight, int &textWidth, int &textHeight) { + //TextLineList lines; + + if (maxWidth >= 0) { + Common::Array<Common::String> lines; + _font->wordWrapText(text, maxWidth, lines); + Common::Array<Common::String>::iterator it; + textWidth = 0; + for (it = lines.begin(); it != lines.end(); ++it) { + textWidth = MAX(textWidth, _font->getStringWidth(*it)); + } + + //WrapText(text, maxWidth, maxHeight, lines); + + textHeight = (int)(lines.size() * getLineHeight()); + } else { + textWidth = _font->getStringWidth(text); + textHeight = _fontHeight; + } + /* + TextLineList::iterator it; + for (it = lines.begin(); it != lines.end(); ++it) { + TextLine *line = (*it); + textWidth = MAX(textWidth, line->GetWidth()); + delete line; + line = NULL; + }*/ +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/font/base_font_truetype.h b/engines/wintermute/base/font/base_font_truetype.h new file mode 100644 index 0000000000..2b69d1655d --- /dev/null +++ b/engines/wintermute/base/font/base_font_truetype.h @@ -0,0 +1,153 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_FONTTT_H +#define WINTERMUTE_BASE_FONTTT_H + +#include "engines/wintermute/base/font/base_font_storage.h" +#include "engines/wintermute/base/font/base_font.h" +#include "engines/wintermute/base/gfx/base_surface.h" +#include "common/rect.h" +#include "graphics/surface.h" +#include "graphics/font.h" + +#define NUM_CACHED_TEXTS 30 + +namespace Wintermute { + +class BaseFontTT : public BaseFont { +private: + ////////////////////////////////////////////////////////////////////////// + class BaseCachedTTFontText { + public: + WideString _text; + int _width; + TTextAlign _align; + int _maxHeight; + int _maxLength; + BaseSurface *_surface; + int _priority; + int _textOffset; + bool _marked; + uint32 _lastUsed; + + BaseCachedTTFontText() { + //_text = L""; + _text = ""; + _width = _maxHeight = _maxLength = -1; + _align = TAL_LEFT; + _surface = NULL; + _textOffset = 0; + _lastUsed = 0; + _marked = false; + } + + virtual ~BaseCachedTTFontText() { + if (_surface) { + delete _surface; + } + } + }; + +public: + ////////////////////////////////////////////////////////////////////////// + class BaseTTFontLayer { + public: + BaseTTFontLayer() { + _offsetX = _offsetY = 0; + _color = 0x00000000; + } + + bool persist(BasePersistenceManager *persistMgr) { + persistMgr->transfer(TMEMBER(_offsetX)); + persistMgr->transfer(TMEMBER(_offsetY)); + persistMgr->transfer(TMEMBER(_color)); + return STATUS_OK; + } + + int _offsetX; + int _offsetY; + uint32 _color; + }; + +public: + DECLARE_PERSISTENT(BaseFontTT, BaseFont) + BaseFontTT(BaseGame *inGame); + virtual ~BaseFontTT(void); + + virtual int getTextWidth(byte *text, int maxLength = -1); + virtual int getTextHeight(byte *text, int width); + virtual void drawText(const byte *text, int x, int y, int width, TTextAlign align = TAL_LEFT, int max_height = -1, int maxLength = -1); + virtual int getLetterHeight(); + + bool loadBuffer(byte *buffer); + bool loadFile(const Common::String &filename); + + float getLineHeight() const { + return _lineHeight; + } + + void afterLoad(); + void initLoop(); + +private: + bool parseLayer(BaseTTFontLayer *layer, byte *buffer); + + void measureText(const WideString &text, int maxWidth, int maxHeight, int &textWidth, int &textHeight); + + BaseSurface *renderTextToTexture(const WideString &text, int width, TTextAlign align, int maxHeight, int &textOffset); + + BaseCachedTTFontText *_cachedTexts[NUM_CACHED_TEXTS]; + + bool initFont(); + + Graphics::Font *_deletableFont; + const Graphics::Font *_font; + const Graphics::Font *_fallbackFont; + + float _lineHeight; + + size_t _maxCharWidth; + size_t _maxCharHeight; + +public: + bool _isBold; + bool _isItalic; + bool _isUnderline; + bool _isStriked; + int _fontHeight; + char *_fontFile; + + BaseArray<BaseTTFontLayer *> _layers; + void clearCache(); + +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/gfx/base_image.cpp b/engines/wintermute/base/gfx/base_image.cpp new file mode 100644 index 0000000000..4b15d563ed --- /dev/null +++ b/engines/wintermute/base/gfx/base_image.cpp @@ -0,0 +1,231 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/gfx/base_image.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/graphics/transparent_surface.h" +#include "graphics/decoders/png.h" +#include "graphics/decoders/jpeg.h" +#include "graphics/decoders/bmp.h" +#include "graphics/decoders/tga.h" +#include "graphics/surface.h" +#include "common/textconsole.h" +#include "common/stream.h" +#include "common/system.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////// +BaseImage::BaseImage() { + _fileManager = BaseFileManager::getEngineInstance(); + _palette = NULL; + _surface = NULL; + _decoder = NULL; + _deletableSurface = NULL; +} + + +////////////////////////////////////////////////////////////////////// +BaseImage::~BaseImage() { + delete _decoder; + if (_deletableSurface) { + _deletableSurface->free(); + } + delete _deletableSurface; +} + +bool BaseImage::loadFile(const Common::String &filename) { + _filename = filename; + _filename.toLowercase(); + if (filename.hasPrefix("savegame:")) { + _decoder = new Graphics::BitmapDecoder(); + } else if (_filename.hasSuffix(".png")) { + _decoder = new Graphics::PNGDecoder(); + } else if (_filename.hasSuffix(".bmp")) { + _decoder = new Graphics::BitmapDecoder(); + } else if (_filename.hasSuffix(".tga")) { + _decoder = new Graphics::TGADecoder(); + } else if (_filename.hasSuffix(".jpg")) { + _decoder = new Graphics::JPEGDecoder(); + } else { + error("BaseImage::loadFile : Unsupported fileformat %s", filename.c_str()); + } + _filename = filename; + Common::SeekableReadStream *file = _fileManager->openFile(filename.c_str()); + if (!file) { + return false; + } + + _decoder->loadStream(*file); + _surface = _decoder->getSurface(); + _palette = _decoder->getPalette(); + _fileManager->closeFile(file); + + return true; +} + +byte BaseImage::getAlphaAt(int x, int y) const { + if (!_surface) { + return 0xFF; + } + uint32 color = *(const uint32 *)_surface->getBasePtr(x, y); + byte r, g, b, a; + _surface->format.colorToARGB(color, a, r, g, b); + return a; +} + +void BaseImage::copyFrom(const Graphics::Surface *surface) { + _surface = _deletableSurface = new Graphics::Surface(); + _deletableSurface->copyFrom(*surface); +} + +////////////////////////////////////////////////////////////////////////// +bool BaseImage::saveBMPFile(const char *filename) const { + warning("BaseImage::saveBMPFile - stubbed"); // TODO + return false; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseImage::resize(int newWidth, int newHeight) { + // WME Lite used FILTER_BILINEAR with FreeImage_Rescale here. + TransparentSurface temp(*_surface, true); + if (_deletableSurface) { + _deletableSurface->free(); + delete _deletableSurface; + _deletableSurface = NULL; + } + _surface = _deletableSurface = temp.scale((uint16)newWidth, (uint16)newHeight); + temp.free(); + return true; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseImage::writeBMPToStream(Common::WriteStream *stream) const { + if (!_surface) { + return false; + } + + /* The following is just copied over and inverted to write-ops from the BMP-decoder */ + stream->writeByte('B'); + stream->writeByte('M'); + + /* Since we don't care during reads, we don't care during writes: */ + /* uint32 fileSize = */ + stream->writeUint32LE(54 + _surface->h * _surface->pitch); + /* uint16 res1 = */ + stream->writeUint16LE(0); + /* uint16 res2 = */ + stream->writeUint16LE(0); + const uint32 imageOffset = 54; + stream->writeUint32LE(imageOffset); + + const uint32 infoSize = 40; /* Windows v3 BMP */ + stream->writeUint32LE(infoSize); + + uint32 width = _surface->w; + int32 height = _surface->h; + stream->writeUint32LE(width); + stream->writeUint32LE((uint32)height); + + if (width == 0 || height == 0) { + return false; + } + + if (height < 0) { + warning("Right-side up bitmaps not supported"); + return false; + } + + /* uint16 planes = */ stream->writeUint16LE(1); + const uint16 bitsPerPixel = 24; + stream->writeUint16LE(bitsPerPixel); + + const uint32 compression = 0; + stream->writeUint32LE(compression); + + /* uint32 imageSize = */ + stream->writeUint32LE(_surface->h * _surface->pitch); + /* uint32 pixelsPerMeterX = */ + stream->writeUint32LE(0); + /* uint32 pixelsPerMeterY = */ + stream->writeUint32LE(0); + const uint32 paletteColorCount = 0; + stream->writeUint32LE(paletteColorCount); + /* uint32 colorsImportant = */ + stream->writeUint32LE(0); + + // Start us at the beginning of the image (54 bytes in) + Graphics::PixelFormat format = Graphics::PixelFormat::createFormatCLUT8(); + + // BGRA for 24bpp + if (bitsPerPixel == 24) { + format = Graphics::PixelFormat(4, 8, 8, 8, 8, 8, 16, 24, 0); + } + + Graphics::Surface *surface = _surface->convertTo(format); + + int srcPitch = width * (bitsPerPixel >> 3); + const int extraDataLength = (srcPitch % 4) ? 4 - (srcPitch % 4) : 0; + + for (int32 i = height - 1; i >= 0; i--) { + for (uint32 j = 0; j < width; j++) { + byte b, g, r; + uint32 color = *(uint32 *)surface->getBasePtr(j, i); + surface->format.colorToRGB(color, r, g, b); + stream->writeByte(b); + stream->writeByte(g); + stream->writeByte(r); + } + + for (int k = 0; k < extraDataLength; k++) { + stream->writeByte(0); + } + } + surface->free(); + delete surface; + return true; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseImage::copyFrom(BaseImage *origImage, int newWidth, int newHeight) { + // WME Lite used FILTER_BILINEAR with FreeImage_Rescale here. + + TransparentSurface temp(*origImage->_surface, false); + if (_deletableSurface) { + _deletableSurface->free(); + delete _deletableSurface; + _deletableSurface = NULL; + } + _surface = _deletableSurface = temp.scale((uint16)newWidth, (uint16)newHeight); + return true; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/gfx/base_image.h b/engines/wintermute/base/gfx/base_image.h new file mode 100644 index 0000000000..6d01b84184 --- /dev/null +++ b/engines/wintermute/base/gfx/base_image.h @@ -0,0 +1,72 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_IMAGE_H +#define WINTERMUTE_BASE_IMAGE_H + +#include "graphics/surface.h" +#include "graphics/pixelformat.h" +#include "graphics/decoders/image_decoder.h" +#include "common/endian.h" +#include "common/str.h" +#include "common/stream.h" + +namespace Wintermute { +class BaseSurface; +class BaseFileManager; +class BaseImage { + +public: + BaseImage(); + ~BaseImage(); + + bool loadFile(const Common::String &filename); + const Graphics::Surface *getSurface() const { + return _surface; + }; + const byte *getPalette() const { + return _palette; + } + byte getAlphaAt(int x, int y) const; + bool writeBMPToStream(Common::WriteStream *stream) const; + bool resize(int newWidth, int newHeight); + bool saveBMPFile(const char *filename) const; + bool copyFrom(BaseImage *origImage, int newWidth = 0, int newHeight = 0); + void copyFrom(const Graphics::Surface *surface); +private: + Common::String _filename; + Graphics::ImageDecoder *_decoder; + const Graphics::Surface *_surface; + Graphics::Surface *_deletableSurface; + const byte *_palette; + BaseFileManager *_fileManager; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/gfx/base_renderer.cpp b/engines/wintermute/base/gfx/base_renderer.cpp new file mode 100644 index 0000000000..e7ffc14c25 --- /dev/null +++ b/engines/wintermute/base/gfx/base_renderer.cpp @@ -0,0 +1,381 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_active_rect.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/base/gfx/base_surface.h" +#include "engines/wintermute/base/base_sub_frame.h" +#include "engines/wintermute/base/base_region.h" +#include "engines/wintermute/platform_osystem.h" +#include "engines/wintermute/base/base_persistence_manager.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////// +BaseRenderer::BaseRenderer(BaseGame *inGame) : BaseClass(inGame) { + _window = 0; + _clipperWindow = 0; + _active = false; + _ready = false; + _windowed = true; + _forceAlphaColor = 0x00; + + _indicatorDisplay = false; + _indicatorColor = BYTETORGBA(255, 0, 0, 128); + _indicatorProgress = 0; + _indicatorX = -1; + _indicatorY = -1; + _indicatorWidth = -1; + _indicatorHeight = 8; + _indicatorWidthDrawn = 0; + + _loadImageName = ""; + _saveImageName = ""; + _saveLoadImage = NULL; + _loadInProgress = false; + _hasDrawnSaveLoadImage = false; + + _saveImageX = _saveImageY = 0; + _loadImageX = _loadImageY = 0; + + _width = _height = _bPP = 0; + BasePlatform::setRectEmpty(&_monitorRect); + + _realWidth = _realHeight = 0; + _drawOffsetX = _drawOffsetY = 0; +} + + +////////////////////////////////////////////////////////////////////// +BaseRenderer::~BaseRenderer() { + deleteRectList(); + unclipCursor(); + delete _saveLoadImage; +} + + +////////////////////////////////////////////////////////////////////// +void BaseRenderer::initLoop() { + deleteRectList(); +} + +void BaseRenderer::initIndicator() { + if (_indicatorY == -1) { + _indicatorY = _height - _indicatorHeight; + } + if (_indicatorX == -1) { + _indicatorX = 0; + } + if (_indicatorWidth == -1) { + _indicatorWidth = _width; + } +} + +void BaseRenderer::setIndicator(int width, int height, int x, int y, uint32 color) { + _indicatorWidth = width; + _indicatorHeight = height; + _indicatorX = x; + _indicatorY = y; + _indicatorColor = color; +} + +void BaseRenderer::setIndicatorVal(int value) { + bool redisplay = (_indicatorProgress != value); + _indicatorProgress = value; + if (redisplay) + displayIndicator(); +} + +void BaseRenderer::setLoadingScreen(const char *filename, int x, int y) { + // TODO: Handle NULL + _loadImageName = filename; + _loadImageX = x; + _loadImageY = y; +} + +void BaseRenderer::setSaveImage(const char *filename, int x, int y) { + // TODO: Handle NULL + _saveImageName = filename; + _saveImageX = x; + _saveImageY = y; +} + +void BaseRenderer::initSaveLoad(bool isSaving, bool quickSave) { + _indicatorDisplay = true; + _indicatorProgress = 0; + _hasDrawnSaveLoadImage = false; + + if (isSaving && !quickSave) { + delete _saveLoadImage; + _saveLoadImage = NULL; + if (_saveImageName.size()) { + _saveLoadImage = createSurface(); + + if (!_saveLoadImage || DID_FAIL(_saveLoadImage->create(_saveImageName, true, 0, 0, 0))) { + delete _saveLoadImage; + _saveLoadImage = NULL; + } + } + } else { + delete _saveLoadImage; + _saveLoadImage = NULL; + if (_loadImageName.size()) { + _saveLoadImage = createSurface(); + + if (!_saveLoadImage || DID_FAIL(_saveLoadImage->create(_loadImageName, true, 0, 0, 0))) { + delete _saveLoadImage; + _saveLoadImage = NULL; + } + } + _loadInProgress = true; + } +} + +void BaseRenderer::endSaveLoad() { + _loadInProgress = false; + _indicatorDisplay = false; + _indicatorWidthDrawn = 0; + + delete _saveLoadImage; + _saveLoadImage = NULL; +} + +void BaseRenderer::persistSaveLoadImages(BasePersistenceManager *persistMgr) { + persistMgr->transfer(TMEMBER(_loadImageName)); + persistMgr->transfer(TMEMBER(_saveImageName)); + persistMgr->transfer(TMEMBER(_saveImageX)); + persistMgr->transfer(TMEMBER(_saveImageY)); + persistMgr->transfer(TMEMBER(_loadImageX)); + persistMgr->transfer(TMEMBER(_loadImageY)); +} + +////////////////////////////////////////////////////////////////////// +BaseObject *BaseRenderer::getObjectAt(int x, int y) { + Point32 point; + point.x = x; + point.y = y; + + for (int i = _rectList.size() - 1; i >= 0; i--) { + if (BasePlatform::ptInRect(&_rectList[i]->_rect, point)) { + if (_rectList[i]->_precise) { + // frame + if (_rectList[i]->_frame) { + int xx = (int)((_rectList[i]->_frame->getRect().left + x - _rectList[i]->_rect.left + _rectList[i]->_offsetX) / (float)((float)_rectList[i]->_zoomX / (float)100)); + int yy = (int)((_rectList[i]->_frame->getRect().top + y - _rectList[i]->_rect.top + _rectList[i]->_offsetY) / (float)((float)_rectList[i]->_zoomY / (float)100)); + + if (_rectList[i]->_frame->_mirrorX) { + int width = _rectList[i]->_frame->getRect().right - _rectList[i]->_frame->getRect().left; + xx = width - xx; + } + + if (_rectList[i]->_frame->_mirrorY) { + int height = _rectList[i]->_frame->getRect().bottom - _rectList[i]->_frame->getRect().top; + yy = height - yy; + } + + if (!_rectList[i]->_frame->_surface->isTransparentAt(xx, yy)) { + return _rectList[i]->_owner; + } + } + // region + else if (_rectList[i]->_region) { + if (_rectList[i]->_region->pointInRegion(x + _rectList[i]->_offsetX, y + _rectList[i]->_offsetY)) { + return _rectList[i]->_owner; + } + } + } else { + return _rectList[i]->_owner; + } + } + } + + return (BaseObject *)NULL; +} + + +////////////////////////////////////////////////////////////////////////// +void BaseRenderer::deleteRectList() { + for (uint32 i = 0; i < _rectList.size(); i++) { + delete _rectList[i]; + } + _rectList.clear(); +} + +////////////////////////////////////////////////////////////////////// +bool BaseRenderer::initRenderer(int width, int height, bool windowed) { + return STATUS_FAILED; +} + + +////////////////////////////////////////////////////////////////////// +void BaseRenderer::onWindowChange() { +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseRenderer::windowedBlt() { + return STATUS_FAILED; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseRenderer::setup2D(bool Force) { + return STATUS_FAILED; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseRenderer::setupLines() { + return STATUS_FAILED; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseRenderer::drawLine(int x1, int y1, int x2, int y2, uint32 color) { + return STATUS_FAILED; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseRenderer::drawRect(int x1, int y1, int x2, int y2, uint32 color, int width) { + for (int i = 0; i < width; i++) { + drawLine(x1 + i, y1 + i, x2 - i, y1 + i, color); // up + drawLine(x1 + i, y2 - i, x2 - i + 1, y2 - i, color); // down + + drawLine(x1 + i, y1 + i, x1 + i, y2 - i, color); // left + drawLine(x2 - i, y1 + i, x2 - i, y2 - i + 1, color); // right + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseRenderer::setViewport(int left, int top, int right, int bottom) { + return STATUS_FAILED; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseRenderer::setScreenViewport() { + return setViewport(_drawOffsetX, _drawOffsetY, _width + _drawOffsetX, _height + _drawOffsetY); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseRenderer::setViewport(Rect32 *rect) { + return setViewport(rect->left + _drawOffsetX, + rect->top + _drawOffsetY, + rect->right + _drawOffsetX, + rect->bottom + _drawOffsetY); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseRenderer::clipCursor() { + // TODO: Reimplement this. (Currently aspect-indpendence isn't quite finished) + /* + if (!_windowed) { + Rect32 rc; + GetWindowRect(_window, &rc); + + // if "maintain aspect ratio" is in effect, lock mouse to visible area + rc.left = _drawOffsetX; + rc.top = _drawOffsetY; + rc.right = rc.left + _width; + rc.bottom = rc.top + _height; + + ::ClipCursor(&rc); + } + */ + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseRenderer::unclipCursor() { + /* + if (!_windowed) ::ClipCursor(NULL); + */ + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseRenderer::pointInViewport(Point32 *p) { + if (p->x < _drawOffsetX) { + return false; + } + if (p->y < _drawOffsetY) { + return false; + } + if (p->x > _drawOffsetX + _width) { + return false; + } + if (p->y > _drawOffsetY + _height) { + return false; + } + + return true; +} + +void BaseRenderer::addRectToList(BaseActiveRect *rect) { + _rectList.push_back(rect); +} + +////////////////////////////////////////////////////////////////////////// +bool BaseRenderer::displayIndicator() { + if (!_indicatorDisplay || !_indicatorProgress) { + return STATUS_OK; + } + if (_saveLoadImage && !_hasDrawnSaveLoadImage) { + Rect32 rc; + BasePlatform::setRect(&rc, 0, 0, _saveLoadImage->getWidth(), _saveLoadImage->getHeight()); + if (_loadInProgress) { + _saveLoadImage->displayTrans(_loadImageX, _loadImageY, rc); + } else { + _saveLoadImage->displayTrans(_saveImageX, _saveImageY, rc); + } + flip(); + _hasDrawnSaveLoadImage = true; + } + + if ((!_indicatorDisplay && _indicatorWidth <= 0) || _indicatorHeight <= 0) { + return STATUS_OK; + } + setupLines(); + int curWidth = (int)(_indicatorWidth * (float)((float)_indicatorProgress / 100.0f)); + for (int i = 0; i < _indicatorHeight; i++) { + drawLine(_indicatorX, _indicatorY + i, _indicatorX + curWidth, _indicatorY + i, _indicatorColor); + } + + setup2D(); + _indicatorWidthDrawn = curWidth; + if (_indicatorWidthDrawn) { + indicatorFlip(); + } + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/gfx/base_renderer.h b/engines/wintermute/base/gfx/base_renderer.h new file mode 100644 index 0000000000..796dee8cd1 --- /dev/null +++ b/engines/wintermute/base/gfx/base_renderer.h @@ -0,0 +1,222 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_RENDERER_H +#define WINTERMUTE_BASE_RENDERER_H + +#include "engines/wintermute/math/rect32.h" +#include "engines/wintermute/base/base.h" +#include "common/rect.h" +#include "common/array.h" + +namespace Wintermute { + +class BaseImage; +class BaseActiveRect; +class BaseObject; +class BaseSurface; +class BasePersistenceManager; + +/** + * @class BaseRenderer a common interface for the rendering portion of WME + * this interface is mainly intended to wrap away any differencies between + * software-rendering/hardware-rendering. + */ +class BaseRenderer: public BaseClass { +public: + int _realWidth; + int _realHeight; + int _drawOffsetX; + int _drawOffsetY; + + virtual void dumpData(const char *filename) {} + /** + * Take a screenshot of the current screenstate + * + * @return a BaseImage containing the current screen-buffer. + */ + virtual BaseImage *takeScreenshot() = 0; + virtual bool setViewport(int left, int top, int right, int bottom); + virtual bool setViewport(Rect32 *rect); + virtual Rect32 getViewPort() = 0; + virtual bool setScreenViewport(); + + virtual Graphics::PixelFormat getPixelFormat() const = 0; + /** + * Fade the screen to black + * + * @param alpha amount to fade by (alpha value of black) + * @return + */ + virtual void fade(uint16 alpha) = 0; + /** + * Fade a portion of the screen to a specific color + * + * @param r the red component to fade too. + * @param g the green component to fade too. + * @param b the blue component to fade too. + * @param a the alpha component to fade too. + * @param rect the portion of the screen to fade (if NULL, the entire screen will be faded). + */ + virtual void fadeToColor(byte r, byte g, byte b, byte a, Common::Rect *rect = NULL) = 0; + + virtual bool drawLine(int x1, int y1, int x2, int y2, uint32 color); // Unused outside indicator-display + virtual bool drawRect(int x1, int y1, int x2, int y2, uint32 color, int width = 1); // Unused outside indicator-display + BaseRenderer(BaseGame *inGame = NULL); + virtual ~BaseRenderer(); + virtual bool setProjection() { + return STATUS_OK; + }; + + virtual bool windowedBlt(); + /** + * Fill a portion of the screen with a specified color + * + * @param r the red component to fill with. + * @param g the green component to fill with. + * @param b the blue component to fill with. + */ + virtual bool fill(byte r, byte g, byte b, Common::Rect *rect = NULL) = 0; + virtual void onWindowChange(); + virtual bool initRenderer(int width, int height, bool windowed); + /** + * Flip the backbuffer onto the screen-buffer + * The screen will NOT be updated before calling this function. + * + * @return true if successfull, false on error. + */ + virtual bool flip() = 0; + /** + * Special flip for the indicator drawn during save/load + * essentially, just copies the region defined by the _indicator-variables. + */ + virtual bool indicatorFlip() = 0; + virtual void initLoop(); + virtual bool setup2D(bool force = false); + virtual bool setupLines(); + + /** + * Get the name of the current renderer + * + * @return the name of the renderer. + */ + virtual Common::String getName() const = 0; + virtual bool displayDebugInfo() { + return STATUS_FAILED; + }; + virtual bool drawShaderQuad() { + return STATUS_FAILED; + } + + virtual float getScaleRatioX() const { + return 1.0f; + } + virtual float getScaleRatioY() const { + return 1.0f; + } + + /** + * Create a Surface fit for use with the renderer. + * As diverse implementations of BaseRenderer might have different solutions for storing surfaces + * this allows for a common interface for creating surface-handles. (Mostly usefull to ease future + * implementation of hw-accelerated rendering, or readding 3D-support at some point). + * + * @return a surface that can be used with this renderer + */ + virtual BaseSurface *createSurface() = 0; + + bool clipCursor(); + bool unclipCursor(); + + BaseObject *getObjectAt(int x, int y); + void deleteRectList(); + + virtual bool startSpriteBatch() { + return STATUS_OK; + }; + virtual bool endSpriteBatch() { + return STATUS_OK; + }; + bool pointInViewport(Point32 *P); + bool _active; + bool _ready; + bool _windowed; + int _bPP; + int _height; + int _width; + uint32 _window; + uint32 _forceAlphaColor; + + void addRectToList(BaseActiveRect *rect); + + // Indicator & Save/Load-related functions + void initIndicator(); + void setIndicatorVal(int value); + void setIndicator(int width, int height, int x, int y, uint32 color); + void persistSaveLoadImages(BasePersistenceManager *persistMgr); + void initSaveLoad(bool isSaving, bool quickSave = false); + virtual void endSaveLoad(); + void setLoadingScreen(const char *filename, int x, int y); + void setSaveImage(const char *filename, int x, int y); + + bool displayIndicator(); +protected: + Common::String _loadImageName; + Common::String _saveImageName; + int _saveImageX; + int _saveImageY; + int _loadImageX; + int _loadImageY; + BaseSurface *_saveLoadImage; + bool _hasDrawnSaveLoadImage; + + int _indicatorWidthDrawn; + uint32 _indicatorColor; + int _indicatorX; + int _indicatorY; + int _indicatorWidth; + int _indicatorHeight; + bool _loadInProgress; + bool _indicatorDisplay; + int _indicatorProgress; +protected: + uint32 _clipperWindow; + + Rect32 _windowRect; + Rect32 _viewportRect; + Rect32 _screenRect; + Rect32 _monitorRect; +private: + Common::Array<BaseActiveRect *> _rectList; +}; + +BaseRenderer *makeOSystemRenderer(BaseGame *inGame); // Implemented in BRenderSDL.cpp + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/gfx/base_surface.cpp b/engines/wintermute/base/gfx/base_surface.cpp new file mode 100644 index 0000000000..d882adf5ad --- /dev/null +++ b/engines/wintermute/base/gfx/base_surface.cpp @@ -0,0 +1,149 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/wintypes.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/gfx/base_surface.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////// +BaseSurface::BaseSurface(BaseGame *inGame) : BaseClass(inGame) { + _referenceCount = 0; + + _width = _height = 0; + + _filename = ""; + + _pixelOpReady = false; + + _ckDefault = true; + _ckRed = _ckGreen = _ckBlue = 0; + _lifeTime = 0; + _keepLoaded = false; + + _lastUsedTime = 0; + _valid = false; +} + + +////////////////////////////////////////////////////////////////////// +BaseSurface::~BaseSurface() { + if (_pixelOpReady) { + endPixelOp(); + } +} + +////////////////////////////////////////////////////////////////////// +bool BaseSurface::restore() { + return STATUS_FAILED; +} + +////////////////////////////////////////////////////////////////////// +bool BaseSurface::isTransparentAt(int x, int y) { + return false; +} + +////////////////////////////////////////////////////////////////////// +bool BaseSurface::displayHalfTrans(int x, int y, Rect32 rect) { + return STATUS_FAILED; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSurface::displayTransform(int x, int y, int hotX, int hotY, Rect32 rect, float zoomX, float zoomY, uint32 alpha, float rotate, TSpriteBlendMode blendMode, bool mirrorX, bool mirrorY) { + return displayTransZoom(x, y, rect, zoomX, zoomY, alpha, blendMode, mirrorX, mirrorY); +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSurface::create(int width, int height) { + return STATUS_FAILED; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSurface::startPixelOp() { + return STATUS_FAILED; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSurface::endPixelOp() { + return STATUS_FAILED; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSurface::getPixel(int x, int y, byte *r, byte *g, byte *b, byte *a) { + return STATUS_FAILED; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSurface::putPixel(int x, int y, byte r, byte g, byte b, int a) { + return STATUS_FAILED; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSurface::comparePixel(int x, int y, byte r, byte g, byte b, int a) { + return false; +} + + +////////////////////////////////////////////////////////////////////// +bool BaseSurface::isTransparentAtLite(int x, int y) { + return false; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSurface::invalidate() { + return STATUS_FAILED; +} + + + +////////////////////////////////////////////////////////////////////////// +bool BaseSurface::prepareToDraw() { + _lastUsedTime = _gameRef->_liveTimer; + + if (!_valid) { + //_gameRef->LOG(0, "Reviving: %s", _filename); + return create(_filename.c_str(), _ckDefault, _ckRed, _ckGreen, _ckBlue, _lifeTime, _keepLoaded); + } else { + return STATUS_OK; + } +} + + +////////////////////////////////////////////////////////////////////////// +void BaseSurface::setSize(int width, int height) { + _width = width; + _height = height; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/gfx/base_surface.h b/engines/wintermute/base/gfx/base_surface.h new file mode 100644 index 0000000000..012be95aac --- /dev/null +++ b/engines/wintermute/base/gfx/base_surface.h @@ -0,0 +1,100 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_SURFACE_H +#define WINTERMUTE_BASE_SURFACE_H + +#include "engines/wintermute/base/base.h" +#include "engines/wintermute/math/rect32.h" +#include "graphics/surface.h" + +namespace Wintermute { + +class BaseSurface: public BaseClass { +public: + virtual bool invalidate(); + virtual bool prepareToDraw(); + uint32 _lastUsedTime; + bool _valid; + int _lifeTime; + + bool _pixelOpReady; + BaseSurface(BaseGame *inGame); + virtual ~BaseSurface(); + + virtual bool displayHalfTrans(int x, int y, Rect32 rect); + virtual bool isTransparentAt(int x, int y); + virtual bool displayTransZoom(int x, int y, Rect32 rect, float zoomX, float zoomY, uint32 alpha = 0xFFFFFFFF, TSpriteBlendMode blendMode = BLEND_NORMAL, bool mirrorX = false, bool mirrorY = false) = 0; + virtual bool displayTrans(int x, int y, Rect32 rect, uint32 alpha = 0xFFFFFFFF, TSpriteBlendMode blendMode = BLEND_NORMAL, bool mirrorX = false, bool mirrorY = false) = 0; + virtual bool displayTransOffset(int x, int y, Rect32 rect, uint32 alpha = 0xFFFFFFFF, TSpriteBlendMode blendMode = BLEND_NORMAL, bool mirrorX = false, bool mirrorY = false, int offsetX = 0, int offsetY = 0) = 0; + virtual bool display(int x, int y, Rect32 rect, TSpriteBlendMode blendMode = BLEND_NORMAL, bool mirrorX = false, bool mirrorY = false) = 0; + virtual bool displayZoom(int x, int y, Rect32 rect, float zoomX, float zoomY, uint32 alpha = 0xFFFFFFFF, bool transparent = false, TSpriteBlendMode blendMode = BLEND_NORMAL, bool mirrorX = false, bool mirrorY = false) = 0; + virtual bool displayTransform(int x, int y, int hotX, int hotY, Rect32 rect, float zoomX, float zoomY, uint32 alpha, float rotate, TSpriteBlendMode blendMode = BLEND_NORMAL, bool mirrorX = false, bool mirrorY = false) = 0; + virtual bool repeatLastDisplayOp(int offsetX, int offsetY, int numTimesX, int numTimesY) = 0; + virtual bool restore(); + virtual bool create(const Common::String &filename, bool defaultCK, byte ckRed, byte ckGreen, byte ckBlue, int lifeTime = -1, bool keepLoaded = false) = 0; + virtual bool create(int width, int height); + virtual bool putSurface(const Graphics::Surface &surface, bool hasAlpha = false) { + return STATUS_FAILED; + } + virtual bool putPixel(int x, int y, byte r, byte g, byte b, int a = -1); + virtual bool getPixel(int x, int y, byte *r, byte *g, byte *b, byte *a = NULL); + virtual bool comparePixel(int x, int y, byte r, byte g, byte b, int a = -1); + virtual bool startPixelOp(); + virtual bool endPixelOp(); + virtual bool isTransparentAtLite(int x, int y); + void setSize(int width, int height); + + int _referenceCount; + + virtual int getWidth() { + return _width; + } + virtual int getHeight() { + return _height; + } + Common::String getFileNameStr() { return _filename; } + const char* getFileName() { return _filename.c_str(); } + //void SetWidth(int Width) { _width = Width; } + //void SetHeight(int Height){ _height = Height; } +protected: + bool _ckDefault; + byte _ckRed; + byte _ckGreen; + byte _ckBlue; + + bool _keepLoaded; + Common::String _filename; + int _height; + int _width; + +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/gfx/osystem/base_render_osystem.cpp b/engines/wintermute/base/gfx/osystem/base_render_osystem.cpp new file mode 100644 index 0000000000..a5b251cea6 --- /dev/null +++ b/engines/wintermute/base/gfx/osystem/base_render_osystem.cpp @@ -0,0 +1,667 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/gfx/osystem/base_render_osystem.h" +#include "engines/wintermute/base/gfx/osystem/base_surface_osystem.h" +#include "engines/wintermute/base/gfx/osystem/render_ticket.h" +#include "engines/wintermute/base/base_surface_storage.h" +#include "engines/wintermute/base/gfx/base_image.h" +#include "engines/wintermute/math/math_util.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_sprite.h" +#include "common/system.h" +#include "engines/wintermute/graphics/transparent_surface.h" +#include "common/queue.h" +#include "common/config-manager.h" + +namespace Wintermute { + +BaseRenderer *makeOSystemRenderer(BaseGame *inGame) { + return new BaseRenderOSystem(inGame); +} + +////////////////////////////////////////////////////////////////////////// +BaseRenderOSystem::BaseRenderOSystem(BaseGame *inGame) : BaseRenderer(inGame) { + _renderSurface = new Graphics::Surface(); + _blankSurface = new Graphics::Surface(); + _drawNum = 1; + _needsFlip = true; + _spriteBatch = false; + _batchNum = 0; + _skipThisFrame = false; + _previousTicket = NULL; + + _borderLeft = _borderRight = _borderTop = _borderBottom = 0; + _ratioX = _ratioY = 1.0f; + setAlphaMod(255); + setColorMod(255, 255, 255); + _dirtyRect = NULL; + _disableDirtyRects = false; + if (ConfMan.hasKey("dirty_rects")) { + _disableDirtyRects = !ConfMan.getBool("dirty_rects"); + } +} + +////////////////////////////////////////////////////////////////////////// +BaseRenderOSystem::~BaseRenderOSystem() { + RenderQueueIterator it = _renderQueue.begin(); + while (it != _renderQueue.end()) { + RenderTicket *ticket = *it; + it = _renderQueue.erase(it); + delete ticket; + } + + delete _dirtyRect; + + _renderSurface->free(); + delete _renderSurface; + _blankSurface->free(); + delete _blankSurface; + TransparentSurface::destroyLookup(); +} + +////////////////////////////////////////////////////////////////////////// +bool BaseRenderOSystem::initRenderer(int width, int height, bool windowed) { + _width = width; + _height = height; + _renderRect.setWidth(_width); + _renderRect.setHeight(_height); + + _realWidth = width; + _realHeight = height; + + //TODO: Tiny resolution-displays might want to do some resolution-selection logic here + + //_realWidth = BaseEngine::instance().getRegistry()->readInt("Debug", "ForceResWidth", _width); + //_realHeight = BaseEngine::instance().getRegistry()->readInt("Debug", "ForceResHeight", _height); + + float origAspect = (float)_width / (float)_height; + float realAspect = (float)_realWidth / (float)_realHeight; + + float ratio; + if (origAspect < realAspect) { + // normal to wide + ratio = (float)_realHeight / (float)_height; + } else { + // wide to normal + ratio = (float)_realWidth / (float)_width; + } + + _borderLeft = (int)((_realWidth - (_width * ratio)) / 2); + _borderRight = (int)(_realWidth - (_width * ratio) - _borderLeft); + + _borderTop = (int)((_realHeight - (_height * ratio)) / 2); + _borderBottom = (int)(_realHeight - (_height * ratio) - _borderTop); + + + + _ratioX = (float)(_realWidth - _borderLeft - _borderRight) / (float)_width; + _ratioY = (float)(_realHeight - _borderTop - _borderBottom) / (float)_height; + + _windowed = !ConfMan.getBool("fullscreen"); + + Graphics::PixelFormat format(4, 8, 8, 8, 8, 16, 8, 0, 24); + g_system->beginGFXTransaction(); + g_system->initSize(_width, _height, &format); + OSystem::TransactionError gfxError = g_system->endGFXTransaction(); + + if (gfxError != OSystem::kTransactionSuccess) { + warning("Couldn't setup GFX-backend for %dx%dx%d", _width, _height, format.bytesPerPixel * 8); + return STATUS_FAILED; + } + + g_system->showMouse(false); + + _renderSurface->create(g_system->getWidth(), g_system->getHeight(), g_system->getScreenFormat()); + _blankSurface->create(g_system->getWidth(), g_system->getHeight(), g_system->getScreenFormat()); + _blankSurface->fillRect(Common::Rect(0, 0, _blankSurface->h, _blankSurface->w), _blankSurface->format.ARGBToColor(255, 0, 0, 0)); + _active = true; + + _clearColor = _renderSurface->format.ARGBToColor(255, 0, 0, 0); + + return STATUS_OK; +} + +void BaseRenderOSystem::setAlphaMod(byte alpha) { + byte r = RGBCOLGetR(_colorMod); + byte g = RGBCOLGetB(_colorMod); + byte b = RGBCOLGetB(_colorMod); + _colorMod = BS_ARGB(alpha, r, g, b); +} + +void BaseRenderOSystem::setColorMod(byte r, byte g, byte b) { + byte alpha = RGBCOLGetA(_colorMod); + _colorMod = BS_ARGB(alpha, r, g, b); +} + +bool BaseRenderOSystem::indicatorFlip() { + g_system->copyRectToScreen((byte *)_renderSurface->getBasePtr(_indicatorX, _indicatorY), _renderSurface->pitch, _indicatorX, _indicatorY, _indicatorWidthDrawn, _indicatorHeight); + g_system->updateScreen(); + return STATUS_OK; +} + +bool BaseRenderOSystem::flip() { + if (_skipThisFrame) { + _skipThisFrame = false; + delete _dirtyRect; + _dirtyRect = NULL; + g_system->updateScreen(); + _needsFlip = false; + return true; + } + if (!_disableDirtyRects) { + drawTickets(); + } else { + // Clear the scale-buffered tickets that wasn't reused. + RenderQueueIterator it = _renderQueue.begin(); + while (it != _renderQueue.end()) { + if ((*it)->_wantsDraw == false) { + RenderTicket *ticket = *it; + it = _renderQueue.erase(it); + delete ticket; + } else { + (*it)->_wantsDraw = false; + ++it; + } + } + } + if (_needsFlip || _disableDirtyRects) { + if (_disableDirtyRects) { + g_system->copyRectToScreen((byte *)_renderSurface->pixels, _renderSurface->pitch, 0, 0, _renderSurface->w, _renderSurface->h); + } + // g_system->copyRectToScreen((byte *)_renderSurface->pixels, _renderSurface->pitch, _dirtyRect->left, _dirtyRect->top, _dirtyRect->width(), _dirtyRect->height()); + delete _dirtyRect; + _dirtyRect = NULL; + g_system->updateScreen(); + _needsFlip = false; + } + _drawNum = 1; + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseRenderOSystem::fill(byte r, byte g, byte b, Common::Rect *rect) { + _clearColor = _renderSurface->format.ARGBToColor(0xFF, r, g, b); + if (!_disableDirtyRects) { + return STATUS_OK; + } + if (!rect) { +// TODO: This should speed things up, but for some reason it misses the size by quite a bit. +/* if (r == 0 && g == 0 && b == 0) { + // Simply memcpy from the buffered black-surface, way faster than Surface::fillRect. + memcpy(_renderSurface->pixels, _blankSurface->pixels, _renderSurface->pitch * _renderSurface->h); + return STATUS_OK; + }*/ + rect = &_renderRect; + } + // TODO: This doesn't work with dirty rects + _renderSurface->fillRect(*rect, _clearColor); + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +void BaseRenderOSystem::fade(uint16 alpha) { + byte dwAlpha = (byte)(255 - alpha); + return fadeToColor(0, 0, 0, dwAlpha); +} + + +////////////////////////////////////////////////////////////////////////// +void BaseRenderOSystem::fadeToColor(byte r, byte g, byte b, byte a, Common::Rect *rect) { + Common::Rect fillRect; + + if (rect) { + fillRect.left = rect->left; + fillRect.top = rect->top; + fillRect.setWidth(rect->width()); + fillRect.setHeight(rect->height()); + } else { + Rect32 rc; + _gameRef->getCurrentViewportRect(&rc); + fillRect.left = (int16)rc.left; + fillRect.top = (int16)rc.top; + fillRect.setWidth((int16)(rc.right - rc.left)); + fillRect.setHeight((int16)(rc.bottom - rc.top)); + } + modTargetRect(&fillRect); + + //TODO: This is only here until I'm sure about the final pixelformat + uint32 col = _renderSurface->format.ARGBToColor(a, r, g, b); + + setAlphaMod(255); + setColorMod(255, 255, 255); + Graphics::Surface surf; + surf.create((uint16)fillRect.width(), (uint16)fillRect.height(), _renderSurface->format); + Common::Rect sizeRect(fillRect); + sizeRect.translate(-fillRect.top, -fillRect.left); + surf.fillRect(fillRect, col); + drawSurface(NULL, &surf, &sizeRect, &fillRect, false, false); + surf.free(); + + //SDL_SetRenderDrawColor(_renderer, r, g, b, a); + //SDL_SetRenderDrawBlendMode(_renderer, SDL_BLENDMODE_BLEND); + //SDL_RenderFillRect(_renderer, &fillRect); +} + +Graphics::PixelFormat BaseRenderOSystem::getPixelFormat() const { + return _renderSurface->format; +} + +void BaseRenderOSystem::drawSurface(BaseSurfaceOSystem *owner, const Graphics::Surface *surf, Common::Rect *srcRect, Common::Rect *dstRect, bool mirrorX, bool mirrorY, bool disableAlpha) { + // Start searching from the beginning for the first and second items (since it's empty the first time around + // then keep incrementing the start-position, to avoid comparing against already used tickets. + if (_drawNum == 0 || _drawNum == 1) { + _lastAddedTicket = _renderQueue.begin(); + } + + // Skip rects that are completely outside the screen: + if ((dstRect->left < 0 && dstRect->right < 0) || (dstRect->top < 0 && dstRect->bottom < 0)) { + return; + } + + if (owner) { // Fade-tickets are owner-less + RenderTicket compare(owner, NULL, srcRect, dstRect, mirrorX, mirrorY, disableAlpha); + compare._batchNum = _batchNum; + if (_spriteBatch) { + _batchNum++; + } + compare._colorMod = _colorMod; + RenderQueueIterator it; + // Avoid calling end() and operator* every time, when potentially going through + // LOTS of tickets. + RenderQueueIterator endIterator = _renderQueue.end(); + RenderTicket *compareTicket = NULL; + for (it = _lastAddedTicket; it != endIterator; ++it) { + compareTicket = *it; + if (*(compareTicket) == compare && compareTicket->_isValid) { + compareTicket->_colorMod = _colorMod; + if (_disableDirtyRects) { + drawFromSurface(compareTicket); + } else { + drawFromTicket(compareTicket); + _previousTicket = compareTicket; + } + return; + } + } + } + RenderTicket *ticket = new RenderTicket(owner, surf, srcRect, dstRect, mirrorX, mirrorY, disableAlpha); + ticket->_colorMod = _colorMod; + if (!_disableDirtyRects) { + drawFromTicket(ticket); + _previousTicket = ticket; + } else { + ticket->_wantsDraw = true; + _renderQueue.push_back(ticket); + } +} + +void BaseRenderOSystem::repeatLastDraw(int offsetX, int offsetY, int numTimesX, int numTimesY) { + if (_previousTicket && _lastAddedTicket != _renderQueue.end()) { + RenderTicket *origTicket = _previousTicket; + + // Make sure drawSurface WILL start from the correct _lastAddedTicket + if (*_lastAddedTicket != origTicket) { + RenderQueueIterator it; + RenderQueueIterator endIterator = _renderQueue.end(); + for (it = _renderQueue.begin(); it != endIterator; ++it) { + if ((*it) == _previousTicket) { + _lastAddedTicket = it; + break; + } + } + } + Common::Rect srcRect(0, 0, 0, 0); + srcRect.setWidth(origTicket->getSrcRect()->width()); + srcRect.setHeight(origTicket->getSrcRect()->height()); + + Common::Rect dstRect = origTicket->_dstRect; + int initLeft = dstRect.left; + int initRight = dstRect.right; + + for (int i = 0; i < numTimesY; i++) { + if (i == 0) { + dstRect.translate(offsetX, 0); + } + for (int j = (i == 0 ? 1 : 0); j < numTimesX; j++) { + drawSurface(origTicket->_owner, origTicket->getSurface(), &srcRect, &dstRect, false, false); + dstRect.translate(offsetX, 0); + } + dstRect.left = initLeft; + dstRect.right = initRight; + dstRect.translate(0, offsetY); + } + } else { + error("Repeat-draw failed (did you forget to draw something before this?)"); + } +} + +void BaseRenderOSystem::invalidateTicket(RenderTicket *renderTicket) { + addDirtyRect(renderTicket->_dstRect); + renderTicket->_isValid = false; +// renderTicket->_canDelete = true; // TODO: Maybe readd this, to avoid even more duplicates. +} + +void BaseRenderOSystem::invalidateTicketsFromSurface(BaseSurfaceOSystem *surf) { + RenderQueueIterator it; + for (it = _renderQueue.begin(); it != _renderQueue.end(); ++it) { + if ((*it)->_owner == surf) { + invalidateTicket(*it); + } + } +} + +void BaseRenderOSystem::drawFromTicket(RenderTicket *renderTicket) { + renderTicket->_wantsDraw = true; + // A new item always has _drawNum == 0 + if (renderTicket->_drawNum == 0) { + // In-order + if (_renderQueue.empty() || _drawNum > (_renderQueue.back())->_drawNum) { + renderTicket->_drawNum = _drawNum++; + _renderQueue.push_back(renderTicket); + addDirtyRect(renderTicket->_dstRect); + ++_lastAddedTicket; + } else { + // Before something + RenderQueueIterator pos; + for (pos = _renderQueue.begin(); pos != _renderQueue.end(); pos++) { + if ((*pos)->_drawNum >= _drawNum) { + break; + } + } + _renderQueue.insert(pos, renderTicket); + renderTicket->_drawNum = _drawNum++; + // Increment the following tickets, so they still are in line + RenderQueueIterator it; + for (it = pos; it != _renderQueue.end(); ++it) { + (*it)->_drawNum++; + (*it)->_wantsDraw = false; + } + addDirtyRect(renderTicket->_dstRect); + _lastAddedTicket = pos; + } + } else { + // Was drawn last round, still in the same order + if (_drawNum == renderTicket->_drawNum) { + _drawNum++; + ++_lastAddedTicket; + } else { + // Remove the ticket from the list + RenderQueueIterator it = _renderQueue.begin(); + while (it != _renderQueue.end()) { + if ((*it) == renderTicket) { + it = _renderQueue.erase(it); + break; + } else { + ++it; + } + } + if (it != _renderQueue.end()) { + // Decreement the following tickets. + for (; it != _renderQueue.end(); ++it) { + (*it)->_drawNum--; + } + } + // Is not in order, so readd it as if it was a new ticket + renderTicket->_drawNum = 0; + drawFromTicket(renderTicket); + } + } +} + +void BaseRenderOSystem::addDirtyRect(const Common::Rect &rect) { + if (!_dirtyRect) { + _dirtyRect = new Common::Rect(rect); + } else { + _dirtyRect->extend(rect); + } + _dirtyRect->clip(_renderRect); +} + +void BaseRenderOSystem::drawTickets() { + RenderQueueIterator it = _renderQueue.begin(); + // Clean out the old tickets + // Note: We draw invalid tickets too, otherwise we wouldn't be honouring + // the draw request they obviously made BEFORE becoming invalid, either way + // we have a copy of their data, so their invalidness won't affect us. + uint32 decrement = 0; + while (it != _renderQueue.end()) { + if ((*it)->_wantsDraw == false) { + RenderTicket *ticket = *it; + addDirtyRect((*it)->_dstRect); + it = _renderQueue.erase(it); + delete ticket; + decrement++; + } else { + (*it)->_drawNum -= decrement; + ++it; + } + } + if (!_dirtyRect || _dirtyRect->width() == 0 || _dirtyRect->height() == 0) { + it = _renderQueue.begin(); + while (it != _renderQueue.end()) { + RenderTicket *ticket = *it; + ticket->_wantsDraw = false; + ++it; + } + return; + } + // The color-mods are stored in the RenderTickets on add, since we set that state again during + // draw, we need to keep track of what it was prior to draw. + uint32 oldColorMod = _colorMod; + + // Apply the clear-color to the dirty rect. + _renderSurface->fillRect(*_dirtyRect, _clearColor); + _drawNum = 1; + for (it = _renderQueue.begin(); it != _renderQueue.end(); ++it) { + RenderTicket *ticket = *it; + assert(ticket->_drawNum == _drawNum++); + if (ticket->_dstRect.intersects(*_dirtyRect)) { + // dstClip is the area we want redrawn. + Common::Rect dstClip(ticket->_dstRect); + // reduce it to the dirty rect + dstClip.clip(*_dirtyRect); + // we need to keep track of the position to redraw the dirty rect + Common::Rect pos(dstClip); + int16 offsetX = ticket->_dstRect.left; + int16 offsetY = ticket->_dstRect.top; + // convert from screen-coords to surface-coords. + dstClip.translate(-offsetX, -offsetY); + + _colorMod = ticket->_colorMod; + drawFromSurface(ticket, &pos, &dstClip); + _needsFlip = true; + } + // Some tickets want redraw but don't actually clip the dirty area (typically the ones that shouldnt become clear-color) + ticket->_wantsDraw = false; + } + g_system->copyRectToScreen((byte *)_renderSurface->getBasePtr(_dirtyRect->left, _dirtyRect->top), _renderSurface->pitch, _dirtyRect->left, _dirtyRect->top, _dirtyRect->width(), _dirtyRect->height()); + + // Revert the colorMod-state. + _colorMod = oldColorMod; + + it = _renderQueue.begin(); + // Clean out the old tickets + decrement = 0; + while (it != _renderQueue.end()) { + if ((*it)->_isValid == false) { + RenderTicket *ticket = *it; + addDirtyRect((*it)->_dstRect); + it = _renderQueue.erase(it); + delete ticket; + decrement++; + } else { + (*it)->_drawNum -= decrement; + ++it; + } + } + +} + +// Replacement for SDL2's SDL_RenderCopy +void BaseRenderOSystem::drawFromSurface(RenderTicket *ticket) { + ticket->drawToSurface(_renderSurface); +} + +void BaseRenderOSystem::drawFromSurface(RenderTicket *ticket, Common::Rect *dstRect, Common::Rect *clipRect) { + ticket->drawToSurface(_renderSurface, dstRect, clipRect); +} + +////////////////////////////////////////////////////////////////////////// +bool BaseRenderOSystem::drawLine(int x1, int y1, int x2, int y2, uint32 color) { + // This function isn't used outside of indicator-displaying, and thus quite unused in + // BaseRenderOSystem when dirty-rects are enabled. + if (!_disableDirtyRects && !_indicatorDisplay) { + error("BaseRenderOSystem::DrawLine - doesn't work for dirty rects yet"); + } + + byte r = RGBCOLGetR(color); + byte g = RGBCOLGetG(color); + byte b = RGBCOLGetB(color); + byte a = RGBCOLGetA(color); + + //SDL_SetRenderDrawColor(_renderer, r, g, b, a); + //SDL_SetRenderDrawBlendMode(_renderer, SDL_BLENDMODE_BLEND); + + Point32 point1, point2; + point1.x = x1; + point1.y = y1; + pointToScreen(&point1); + + point2.x = x2; + point2.y = y2; + pointToScreen(&point2); + + // TODO: This thing is mostly here until I'm sure about the final color-format. + uint32 colorVal = _renderSurface->format.ARGBToColor(a, r, g, b); + _renderSurface->drawLine(point1.x, point1.y, point2.x, point2.y, colorVal); + //SDL_RenderDrawLine(_renderer, point1.x, point1.y, point2.x, point2.y); + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +BaseImage *BaseRenderOSystem::takeScreenshot() { +// TODO: Clip by viewport. + BaseImage *screenshot = new BaseImage(); + screenshot->copyFrom(_renderSurface); + return screenshot; +} + +////////////////////////////////////////////////////////////////////////// +Common::String BaseRenderOSystem::getName() const { + return "ScummVM-OSystem-renderer"; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseRenderOSystem::setViewport(int left, int top, int right, int bottom) { + Common::Rect rect; + // TODO: Hopefully this is the same logic that ScummVM uses. + rect.left = (int16)(left + _borderLeft); + rect.top = (int16)(top + _borderTop); + rect.right = (int16)((right - left) * _ratioX); + rect.bottom = (int16)((bottom - top) * _ratioY); + + _renderRect = rect; + return STATUS_OK; +} + +Rect32 BaseRenderOSystem::getViewPort() { + Rect32 ret; + ret.top = _renderRect.top; + ret.bottom = _renderRect.bottom; + ret.left = _renderRect.left; + ret.right = _renderRect.right; + return ret; +} + +////////////////////////////////////////////////////////////////////////// +void BaseRenderOSystem::modTargetRect(Common::Rect *rect) { + // FIXME: This is wrong in quite a few ways right now, and ends up + // breaking the notebook in Dirty Split, so we disable the correction + // for now, this will need fixing when a game with odd aspect-ratios + // show up. + return; + rect->left = (int16)MathUtil::round(rect->left * _ratioX + _borderLeft - _renderRect.left); + rect->top = (int16)MathUtil::round(rect->top * _ratioY + _borderTop - _renderRect.top); + rect->setWidth((int16)MathUtil::roundUp(rect->width() * _ratioX)); + rect->setHeight((int16)MathUtil::roundUp(rect->height() * _ratioY)); +} + +////////////////////////////////////////////////////////////////////////// +void BaseRenderOSystem::pointFromScreen(Point32 *point) { + point->x = (int16)(point->x / _ratioX - _borderLeft / _ratioX + _renderRect.left); + point->y = (int16)(point->y / _ratioY - _borderTop / _ratioY + _renderRect.top); +} + + +////////////////////////////////////////////////////////////////////////// +void BaseRenderOSystem::pointToScreen(Point32 *point) { + point->x = (int16)MathUtil::roundUp(point->x * _ratioX) + _borderLeft - _renderRect.left; + point->y = (int16)MathUtil::roundUp(point->y * _ratioY) + _borderTop - _renderRect.top; +} + +////////////////////////////////////////////////////////////////////////// +void BaseRenderOSystem::dumpData(const char *filename) { + warning("BaseRenderOSystem::DumpData(%s) - stubbed", filename); // TODO +} + +BaseSurface *BaseRenderOSystem::createSurface() { + return new BaseSurfaceOSystem(_gameRef); +} + +void BaseRenderOSystem::endSaveLoad() { + BaseRenderer::endSaveLoad(); + + // Clear the scale-buffered tickets as we just loaded. + RenderQueueIterator it = _renderQueue.begin(); + while (it != _renderQueue.end()) { + RenderTicket *ticket = *it; + it = _renderQueue.erase(it); + delete ticket; + } + // HACK: After a save the buffer will be drawn before the scripts get to update it, + // so just skip this single frame. + _skipThisFrame = true; + _drawNum = 1; +} + +bool BaseRenderOSystem::startSpriteBatch() { + _spriteBatch = true; + _batchNum = 1; + return STATUS_OK; +} + +bool BaseRenderOSystem::endSpriteBatch() { + _spriteBatch = false; + _batchNum = 0; + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/gfx/osystem/base_render_osystem.h b/engines/wintermute/base/gfx/osystem/base_render_osystem.h new file mode 100644 index 0000000000..2c89037183 --- /dev/null +++ b/engines/wintermute/base/gfx/osystem/base_render_osystem.h @@ -0,0 +1,123 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_RENDERER_SDL_H +#define WINTERMUTE_BASE_RENDERER_SDL_H + +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "common/rect.h" +#include "graphics/surface.h" +#include "common/list.h" + +namespace Wintermute { +class BaseSurfaceOSystem; +class RenderTicket; +class BaseRenderOSystem : public BaseRenderer { +public: + BaseRenderOSystem(BaseGame *inGame); + ~BaseRenderOSystem(); + + Common::String getName() const; + + bool initRenderer(int width, int height, bool windowed); + bool flip(); + virtual bool indicatorFlip(); + bool fill(byte r, byte g, byte b, Common::Rect *rect = NULL); + Graphics::PixelFormat getPixelFormat() const; + void fade(uint16 alpha); + void fadeToColor(byte r, byte g, byte b, byte a, Common::Rect *rect = NULL); + + bool drawLine(int x1, int y1, int x2, int y2, uint32 color); + + BaseImage *takeScreenshot(); + + void setAlphaMod(byte alpha); + void setColorMod(byte r, byte g, byte b); + void invalidateTicket(RenderTicket *renderTicket); + void invalidateTicketsFromSurface(BaseSurfaceOSystem *surf); + void drawFromTicket(RenderTicket *renderTicket); + + bool setViewport(int left, int top, int right, int bottom); + bool setViewport(Rect32 *rect) { return BaseRenderer::setViewport(rect); } + Rect32 getViewPort(); + void modTargetRect(Common::Rect *rect); + void pointFromScreen(Point32 *point); + void pointToScreen(Point32 *point); + + void dumpData(const char *filename); + + float getScaleRatioX() const { + return _ratioX; + } + float getScaleRatioY() const { + return _ratioY; + } + virtual bool startSpriteBatch(); + virtual bool endSpriteBatch(); + void endSaveLoad(); + void drawSurface(BaseSurfaceOSystem *owner, const Graphics::Surface *surf, Common::Rect *srcRect, Common::Rect *dstRect, bool mirrorX, bool mirrorY, bool disableAlpha = false); + void repeatLastDraw(int offsetX, int offsetY, int numTimesX, int numTimesY); + BaseSurface *createSurface(); +private: + void addDirtyRect(const Common::Rect &rect); + void drawTickets(); + // Non-dirty-rects: + void drawFromSurface(RenderTicket *ticket); + // Dirty-rects: + void drawFromSurface(RenderTicket *ticket, Common::Rect *dstRect, Common::Rect *clipRect); + typedef Common::List<RenderTicket *>::iterator RenderQueueIterator; + Common::Rect *_dirtyRect; + Common::List<RenderTicket *> _renderQueue; + RenderQueueIterator _lastAddedTicket; + RenderTicket *_previousTicket; + + bool _needsFlip; + uint32 _drawNum; + Common::Rect _renderRect; + Graphics::Surface *_renderSurface; + Graphics::Surface *_blankSurface; + + int _borderLeft; + int _borderTop; + int _borderRight; + int _borderBottom; + + bool _disableDirtyRects; + bool _spriteBatch; + uint32 _batchNum; + float _ratioX; + float _ratioY; + uint32 _colorMod; + uint32 _clearColor; + + bool _skipThisFrame; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/gfx/osystem/base_surface_osystem.cpp b/engines/wintermute/base/gfx/osystem/base_surface_osystem.cpp new file mode 100644 index 0000000000..e2bc3967e2 --- /dev/null +++ b/engines/wintermute/base/gfx/osystem/base_surface_osystem.cpp @@ -0,0 +1,434 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/gfx/osystem/base_surface_osystem.h" +#include "engines/wintermute/base/gfx/osystem/base_render_osystem.h" +#include "engines/wintermute/base/gfx/base_image.h" +#include "engines/wintermute/platform_osystem.h" +#include "graphics/decoders/png.h" +#include "graphics/decoders/bmp.h" +#include "graphics/decoders/jpeg.h" +#include "graphics/decoders/tga.h" +#include "engines/wintermute/graphics/transparent_surface.h" +#include "graphics/pixelformat.h" +#include "graphics/surface.h" +#include "common/stream.h" +#include "common/system.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////////// +BaseSurfaceOSystem::BaseSurfaceOSystem(BaseGame *inGame) : BaseSurface(inGame) { + _surface = new Graphics::Surface(); + _alphaMask = NULL; + _hasAlpha = true; + _lockPixels = NULL; + _lockPitch = 0; + _loaded = false; +} + +////////////////////////////////////////////////////////////////////////// +BaseSurfaceOSystem::~BaseSurfaceOSystem() { + if (_surface) { + _surface->free(); + delete _surface; + _surface = NULL; + } + + delete[] _alphaMask; + _alphaMask = NULL; + + _gameRef->addMem(-_width * _height * 4); + BaseRenderOSystem *renderer = static_cast<BaseRenderOSystem *>(_gameRef->_renderer); + renderer->invalidateTicketsFromSurface(this); +} + +bool hasTransparency(Graphics::Surface *surf) { + if (surf->format.bytesPerPixel != 4) { + warning("hasTransparency:: non 32 bpp surface passed as argument"); + return false; + } + uint8 r, g, b, a; + for (int i = 0; i < surf->h; i++) { + for (int j = 0; j < surf->w; j++) { + uint32 pix = *(uint32 *)surf->getBasePtr(j, i); + surf->format.colorToARGB(pix, a, r, g, b); + if (a != 255) { + return true; + } + } + } + return false; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSurfaceOSystem::create(const Common::String &filename, bool defaultCK, byte ckRed, byte ckGreen, byte ckBlue, int lifeTime, bool keepLoaded) { + /* BaseRenderOSystem *renderer = static_cast<BaseRenderOSystem *>(_gameRef->_renderer); */ + _filename = filename; +// const Graphics::Surface *surface = image->getSurface(); + + if (defaultCK) { + ckRed = 255; + ckGreen = 0; + ckBlue = 255; + } + + _ckDefault = defaultCK; + _ckRed = ckRed; + _ckGreen = ckGreen; + _ckBlue = ckBlue; + + if (_lifeTime == 0 || lifeTime == -1 || lifeTime > _lifeTime) { + _lifeTime = lifeTime; + } + + _keepLoaded = keepLoaded; + if (_keepLoaded) { + _lifeTime = -1; + } + + return STATUS_OK; +} + +bool BaseSurfaceOSystem::finishLoad() { + BaseImage *image = new BaseImage(); + if (!image->loadFile(_filename)) { + return false; + } + + _width = image->getSurface()->w; + _height = image->getSurface()->h; + + bool isSaveGameGrayscale = scumm_strnicmp(_filename.c_str(), "savegame:", 9) == 0 && (_filename.c_str()[_filename.size() - 1] == 'g' || _filename.c_str()[_filename.size() - 1] == 'G'); + if (isSaveGameGrayscale) { + warning("grayscaleConversion not yet implemented"); + // FIBITMAP *newImg = FreeImage_ConvertToGreyscale(img); TODO + } + + // no alpha, set color key + /* if (surface->format.bytesPerPixel != 4) + SDL_SetColorKey(surf, SDL_TRUE, SDL_MapRGB(surf->format, ck_red, ck_green, ck_blue));*/ + + // convert 32-bit BMPs to 24-bit or they appear totally transparent (does any app actually write alpha in BMP properly?) + // Well, actually, we don't convert via 24-bit as the color-key application overwrites the Alpha-channel anyhow. + _surface->free(); + delete _surface; + if (_filename.hasSuffix(".bmp") && image->getSurface()->format.bytesPerPixel == 4) { + _surface = image->getSurface()->convertTo(g_system->getScreenFormat(), image->getPalette()); + TransparentSurface trans(*_surface); + trans.applyColorKey(_ckRed, _ckGreen, _ckBlue); + } else if (image->getSurface()->format.bytesPerPixel == 1 && image->getPalette()) { + _surface = image->getSurface()->convertTo(g_system->getScreenFormat(), image->getPalette()); + TransparentSurface trans(*_surface); + trans.applyColorKey(_ckRed, _ckGreen, _ckBlue, true); + } else if (image->getSurface()->format.bytesPerPixel >= 3 && image->getSurface()->format != g_system->getScreenFormat()) { + _surface = image->getSurface()->convertTo(g_system->getScreenFormat()); + if (image->getSurface()->format.bytesPerPixel == 3) { + TransparentSurface trans(*_surface); + trans.applyColorKey(_ckRed, _ckGreen, _ckBlue, true); + } + } else { + _surface = new Graphics::Surface(); + _surface->copyFrom(*image->getSurface()); + } + + _hasAlpha = hasTransparency(_surface); + _valid = true; + + _gameRef->addMem(_width * _height * 4); + + delete image; + + _loaded = true; + + return true; +} + +////////////////////////////////////////////////////////////////////////// +void BaseSurfaceOSystem::genAlphaMask(Graphics::Surface *surface) { + warning("BaseSurfaceOSystem::GenAlphaMask - Not ported yet"); + return; + // TODO: Reimplement this + delete[] _alphaMask; + _alphaMask = NULL; + if (!surface) { + return; + } + + bool hasColorKey; + /* uint32 colorKey; */ + uint8 ckRed, ckGreen, ckBlue; + /* if (SDL_GetColorKey(surface, &colorKey) == 0) { + hasColorKey = true; + SDL_GetRGB(colorKey, surface->format, &ckRed, &ckGreen, &ckBlue); + } else hasColorKey = false; + */ + _alphaMask = new byte[surface->w * surface->h]; + + bool hasTransparency = false; + for (int y = 0; y < surface->h; y++) { + for (int x = 0; x < surface->w; x++) { + uint32 pixel = getPixelAt(surface, x, y); + + uint8 r, g, b, a; + surface->format.colorToARGB(pixel, a, r, g, b); + //SDL_GetRGBA(pixel, surface->format, &r, &g, &b, &a); + + if (hasColorKey && r == ckRed && g == ckGreen && b == ckBlue) { + a = 0; + } + + _alphaMask[y * surface->w + x] = a; + if (a < 255) { + hasTransparency = true; + } + } + } + + if (!hasTransparency) { + delete[] _alphaMask; + _alphaMask = NULL; + } +} + +////////////////////////////////////////////////////////////////////////// +uint32 BaseSurfaceOSystem::getPixelAt(Graphics::Surface *surface, int x, int y) { + warning("BaseSurfaceOSystem::GetPixel - Not ported yet"); + int bpp = surface->format.bytesPerPixel; + /* Here p is the address to the pixel we want to retrieve */ + uint8 *p = (uint8 *)surface->pixels + y * surface->pitch + x * bpp; + + switch (bpp) { + case 1: + return *p; + break; + + case 2: + return *(uint16 *)p; + break; + + case 3: +#ifdef SCUMM_BIG_ENDIAN + // if (SDL_BYTEORDER == SDL_BIG_ENDIAN) + return p[0] << 16 | p[1] << 8 | p[2]; +#else + //else + return p[0] | p[1] << 8 | p[2] << 16; +#endif + break; + + case 4: + return *(uint32 *)p; + break; + + default: + return 0; /* shouldn't happen, but avoids warnings */ + } + return 0; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSurfaceOSystem::create(int width, int height) { + _width = width; + _height = height; + + _gameRef->addMem(_width * _height * 4); + + _valid = true; + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSurfaceOSystem::isTransparentAt(int x, int y) { + return isTransparentAtLite(x, y); +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSurfaceOSystem::isTransparentAtLite(int x, int y) { + if (x < 0 || x >= _surface->w || y < 0 || y >= _surface->h) { + return true; + } + + if (_surface->format.bytesPerPixel == 4) { + uint32 pixel = *(uint32 *)_surface->getBasePtr(x, y); + uint8 r, g, b, a; + _surface->format.colorToARGB(pixel, a, r, g, b); + if (a <= 128) { + return true; + } else { + return false; + } + } + + return false; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSurfaceOSystem::startPixelOp() { + //SDL_LockTexture(_texture, NULL, &_lockPixels, &_lockPitch); + // Any pixel-op makes the caching useless: + BaseRenderOSystem *renderer = static_cast<BaseRenderOSystem *>(_gameRef->_renderer); + renderer->invalidateTicketsFromSurface(this); + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSurfaceOSystem::endPixelOp() { + //SDL_UnlockTexture(_texture); + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSurfaceOSystem::display(int x, int y, Rect32 rect, TSpriteBlendMode blendMode, bool mirrorX, bool mirrorY) { + return drawSprite(x, y, &rect, 100, 100, 0xFFFFFFFF, true, blendMode, mirrorX, mirrorY); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSurfaceOSystem::displayTrans(int x, int y, Rect32 rect, uint32 alpha, TSpriteBlendMode blendMode, bool mirrorX, bool mirrorY) { + return drawSprite(x, y, &rect, 100, 100, alpha, false, blendMode, mirrorX, mirrorY); +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSurfaceOSystem::displayTransOffset(int x, int y, Rect32 rect, uint32 alpha, TSpriteBlendMode blendMode, bool mirrorX, bool mirrorY, int offsetX, int offsetY) { + return drawSprite(x, y, &rect, 100, 100, alpha, false, blendMode, mirrorX, mirrorY, offsetX, offsetY); +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSurfaceOSystem::displayTransZoom(int x, int y, Rect32 rect, float zoomX, float zoomY, uint32 alpha, TSpriteBlendMode blendMode, bool mirrorX, bool mirrorY) { + return drawSprite(x, y, &rect, zoomX, zoomY, alpha, false, blendMode, mirrorX, mirrorY); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSurfaceOSystem::displayZoom(int x, int y, Rect32 rect, float zoomX, float zoomY, uint32 alpha, bool transparent, TSpriteBlendMode blendMode, bool mirrorX, bool mirrorY) { + return drawSprite(x, y, &rect, zoomX, zoomY, alpha, !transparent, blendMode, mirrorX, mirrorY); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSurfaceOSystem::displayTransform(int x, int y, int hotX, int hotY, Rect32 rect, float zoomX, float zoomY, uint32 alpha, float rotate, TSpriteBlendMode blendMode, bool mirrorX, bool mirrorY) { + return drawSprite(x, y, &rect, zoomX, zoomY, alpha, false, blendMode, mirrorX, mirrorY); +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSurfaceOSystem::drawSprite(int x, int y, Rect32 *rect, float zoomX, float zoomY, uint32 alpha, bool alphaDisable, TSpriteBlendMode blendMode, bool mirrorX, bool mirrorY, int offsetX, int offsetY) { + BaseRenderOSystem *renderer = static_cast<BaseRenderOSystem *>(_gameRef->_renderer); + + if (!_loaded) { + finishLoad(); + } + + if (renderer->_forceAlphaColor != 0) { + alpha = renderer->_forceAlphaColor; + } + + byte r = RGBCOLGetR(alpha); + byte g = RGBCOLGetG(alpha); + byte b = RGBCOLGetB(alpha); + byte a = RGBCOLGetA(alpha); + + renderer->setAlphaMod(a); + renderer->setColorMod(r, g, b); + +#if 0 // These are kept for reference if BlendMode is reimplemented at some point. + if (alphaDisable) { + SDL_SetTextureBlendMode(_texture, SDL_BLENDMODE_NONE); + } else { + SDL_SetTextureBlendMode(_texture, SDL_BLENDMODE_BLEND); + } +#endif + // TODO: This _might_ miss the intended behaviour by 1 in each direction + // But I think it fits the model used in Wintermute. + Common::Rect srcRect; + srcRect.left = rect->left; + srcRect.top = rect->top; + srcRect.setWidth(rect->right - rect->left); + srcRect.setHeight(rect->bottom - rect->top); + + Common::Rect position; + position.left = x + offsetX; + position.top = y + offsetY; + + // Crop off-by-ones: + if (position.left == -1) { + position.left = 0; // TODO: Something is wrong + } + if (position.top == -1) { + position.top = 0; // TODO: Something is wrong + } + + position.setWidth((int16)((float)srcRect.width() * zoomX / 100.f)); + position.setHeight((int16)((float)srcRect.height() * zoomX / 100.f)); + + renderer->modTargetRect(&position); + + /* position.left += offsetX; + position.top += offsetY;*/ + + // TODO: This actually requires us to have the SAME source-offsets every time, + // But no checking is in place for that yet. + + // TODO: Optimize by not doing alpha-blits if we lack or disable alpha + bool hasAlpha; + if (_hasAlpha && !alphaDisable) { + hasAlpha = true; + } else { + hasAlpha = false; + } + if (alphaDisable) { + warning("BaseSurfaceOSystem::drawSprite - AlphaDisable ignored"); + } + + renderer->drawSurface(this, _surface, &srcRect, &position, mirrorX, mirrorY, !hasAlpha); + + return STATUS_OK; +} + +bool BaseSurfaceOSystem::repeatLastDisplayOp(int offsetX, int offsetY, int numTimesX, int numTimesY) { + BaseRenderOSystem *renderer = static_cast<BaseRenderOSystem *>(_gameRef->_renderer); + renderer->repeatLastDraw(offsetX, offsetY, numTimesX, numTimesY); + return STATUS_OK; +} + +bool BaseSurfaceOSystem::putSurface(const Graphics::Surface &surface, bool hasAlpha) { + _loaded = true; + _surface->free(); + _surface->copyFrom(surface); + _hasAlpha = hasAlpha; + BaseRenderOSystem *renderer = static_cast<BaseRenderOSystem *>(_gameRef->_renderer); + renderer->invalidateTicketsFromSurface(this); + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/gfx/osystem/base_surface_osystem.h b/engines/wintermute/base/gfx/osystem/base_surface_osystem.h new file mode 100644 index 0000000000..5bbfa27179 --- /dev/null +++ b/engines/wintermute/base/gfx/osystem/base_surface_osystem.h @@ -0,0 +1,100 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_SURFACESDL_H +#define WINTERMUTE_BASE_SURFACESDL_H + +#include "graphics/surface.h" +#include "engines/wintermute/base/gfx/base_surface.h" +#include "common/list.h" + +namespace Wintermute { +struct TransparentSurface; +class BaseImage; +class BaseSurfaceOSystem : public BaseSurface { +public: + BaseSurfaceOSystem(BaseGame *inGame); + ~BaseSurfaceOSystem(); + + bool create(const Common::String &filename, bool defaultCK, byte ckRed, byte ckGreen, byte ckBlue, int lifeTime = -1, bool keepLoaded = false); + bool create(int width, int height); + + bool isTransparentAt(int x, int y); + bool isTransparentAtLite(int x, int y); + + bool startPixelOp(); + bool endPixelOp(); + + + bool displayTransZoom(int x, int y, Rect32 rect, float zoomX, float zoomY, uint32 alpha = 0xFFFFFFFF, TSpriteBlendMode blendMode = BLEND_NORMAL, bool mirrorX = false, bool mirrorY = false); + bool displayTrans(int x, int y, Rect32 rect, uint32 alpha = 0xFFFFFFFF, TSpriteBlendMode blendMode = BLEND_NORMAL, bool mirrorX = false, bool mirrorY = false); + bool displayTransOffset(int x, int y, Rect32 rect, uint32 alpha = 0xFFFFFFFF, TSpriteBlendMode blendMode = BLEND_NORMAL, bool mirrorX = false, bool mirrorY = false, int offsetX = 0, int offsetY = 0); + bool display(int x, int y, Rect32 rect, TSpriteBlendMode blendMode = BLEND_NORMAL, bool mirrorX = false, bool mirrorY = false); + bool displayZoom(int x, int y, Rect32 rect, float zoomX, float zoomY, uint32 alpha = 0xFFFFFFFF, bool transparent = false, TSpriteBlendMode blendMode = BLEND_NORMAL, bool mirrorX = false, bool mirrorY = false); + bool displayTransform(int x, int y, int hotX, int hotY, Rect32 Rect, float zoomX, float zoomY, uint32 alpha, float rotate, TSpriteBlendMode blendMode = BLEND_NORMAL, bool mirrorX = false, bool mirrorY = false); + bool repeatLastDisplayOp(int offsetX, int offsetY, int numTimesX, int numTimesY); + virtual bool putSurface(const Graphics::Surface &surface, bool hasAlpha = false); + /* static unsigned DLL_CALLCONV ReadProc(void *buffer, unsigned size, unsigned count, fi_handle handle); + static int DLL_CALLCONV SeekProc(fi_handle handle, long offset, int origin); + static long DLL_CALLCONV TellProc(fi_handle handle);*/ + virtual int getWidth() { + if (!_loaded) { + finishLoad(); + } + if (_surface) { + return _surface->w; + } + return _width; + } + virtual int getHeight() { + if (!_loaded) { + finishLoad(); + } + if (_surface) { + return _surface->h; + } + return _height; + } + +private: + Graphics::Surface *_surface; + bool _loaded; + bool finishLoad(); + bool drawSprite(int x, int y, Rect32 *rect, float zoomX, float zoomY, uint32 alpha, bool alphaDisable, TSpriteBlendMode blendMode, bool mirrorX, bool mirrorY, int offsetX = 0, int offsetY = 0); + void genAlphaMask(Graphics::Surface *surface); + uint32 getPixelAt(Graphics::Surface *surface, int x, int y); + + bool _hasAlpha; + void *_lockPixels; + int _lockPitch; + byte *_alphaMask; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/gfx/osystem/render_ticket.cpp b/engines/wintermute/base/gfx/osystem/render_ticket.cpp new file mode 100644 index 0000000000..174f54e315 --- /dev/null +++ b/engines/wintermute/base/gfx/osystem/render_ticket.cpp @@ -0,0 +1,115 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/graphics/transparent_surface.h" +#include "engines/wintermute/base/gfx/osystem/render_ticket.h" + +namespace Wintermute { + +RenderTicket::RenderTicket(BaseSurfaceOSystem *owner, const Graphics::Surface *surf, Common::Rect *srcRect, Common::Rect *dstRect, bool mirrorX, bool mirrorY, bool disableAlpha) : _owner(owner), +_srcRect(*srcRect), _dstRect(*dstRect), _drawNum(0), _isValid(true), _wantsDraw(true), _hasAlpha(!disableAlpha) { + _colorMod = 0; + _batchNum = 0; + _mirror = TransparentSurface::FLIP_NONE; + if (mirrorX) { + _mirror |= TransparentSurface::FLIP_V; + } + if (mirrorY) { + _mirror |= TransparentSurface::FLIP_H; + } + if (surf) { + _surface = new Graphics::Surface(); + _surface->create((uint16)srcRect->width(), (uint16)srcRect->height(), surf->format); + assert(_surface->format.bytesPerPixel == 4); + // Get a clipped copy of the surface + for (int i = 0; i < _surface->h; i++) { + memcpy(_surface->getBasePtr(0, i), surf->getBasePtr(srcRect->left, srcRect->top + i), srcRect->width() * _surface->format.bytesPerPixel); + } + // Then scale it if necessary + if (dstRect->width() != srcRect->width() || dstRect->height() != srcRect->height()) { + TransparentSurface src(*_surface, false); + Graphics::Surface *temp = src.scale(dstRect->width(), dstRect->height()); + _surface->free(); + delete _surface; + _surface = temp; + } + } else { + _surface = NULL; + } +} + +RenderTicket::~RenderTicket() { + if (_surface) { + _surface->free(); + delete _surface; + } +} + +bool RenderTicket::operator==(RenderTicket &t) { + if ((t._owner != _owner) || + (t._batchNum != t._batchNum) || + (t._hasAlpha != _hasAlpha) || + (t._mirror != _mirror) || + (t._colorMod != _colorMod) || + (t._dstRect != _dstRect) || + (t._srcRect != _srcRect)) { + return false; + } + return true; +} + +// Replacement for SDL2's SDL_RenderCopy +void RenderTicket::drawToSurface(Graphics::Surface *_targetSurface) { + TransparentSurface src(*getSurface(), false); + + Common::Rect clipRect; + clipRect.setWidth(getSurface()->w); + clipRect.setHeight(getSurface()->h); + + src._enableAlphaBlit = _hasAlpha; + src.blit(*_targetSurface, _dstRect.left, _dstRect.top, _mirror, &clipRect, _colorMod, clipRect.width(), clipRect.height()); +} + +void RenderTicket::drawToSurface(Graphics::Surface *_targetSurface, Common::Rect *dstRect, Common::Rect *clipRect) { + TransparentSurface src(*getSurface(), false); + bool doDelete = false; + if (!clipRect) { + doDelete = true; + clipRect = new Common::Rect(); + clipRect->setWidth(getSurface()->w); + clipRect->setHeight(getSurface()->h); + } + + src._enableAlphaBlit = _hasAlpha; + src.blit(*_targetSurface, dstRect->left, dstRect->top, _mirror, clipRect, _colorMod, clipRect->width(), clipRect->height()); + if (doDelete) { + delete clipRect; + } +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/gfx/osystem/render_ticket.h b/engines/wintermute/base/gfx/osystem/render_ticket.h new file mode 100644 index 0000000000..968b42b5e1 --- /dev/null +++ b/engines/wintermute/base/gfx/osystem/render_ticket.h @@ -0,0 +1,69 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_RENDER_TICKET_H +#define WINTERMUTE_RENDER_TICKET_H + +#include "graphics/surface.h" +#include "common/rect.h" + +namespace Wintermute { + +class BaseSurfaceOSystem; +class RenderTicket { +public: + RenderTicket(BaseSurfaceOSystem *owner, const Graphics::Surface *surf, Common::Rect *srcRect, Common::Rect *dstRest, bool mirrorX = false, bool mirrorY = false, bool disableAlpha = false); + RenderTicket() : _isValid(true), _wantsDraw(false), _drawNum(0) {} + ~RenderTicket(); + const Graphics::Surface *getSurface() { return _surface; } + // Non-dirty-rects: + void drawToSurface(Graphics::Surface *_targetSurface); + // Dirty-rects: + void drawToSurface(Graphics::Surface *_targetSurface, Common::Rect *dstRect, Common::Rect *clipRect); + + Common::Rect _dstRect; + uint32 _batchNum; + + bool _isValid; + bool _wantsDraw; + uint32 _drawNum; + uint32 _colorMod; + + BaseSurfaceOSystem *_owner; + bool operator==(RenderTicket &a); + const Common::Rect *getSrcRect() { return &_srcRect; } +private: + Graphics::Surface *_surface; + Common::Rect _srcRect; + bool _hasAlpha; + uint32 _mirror; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/particles/part_emitter.cpp b/engines/wintermute/base/particles/part_emitter.cpp new file mode 100644 index 0000000000..e1bc659fdd --- /dev/null +++ b/engines/wintermute/base/particles/part_emitter.cpp @@ -0,0 +1,1254 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/particles/part_emitter.h" +#include "engines/wintermute/base/particles/part_particle.h" +#include "engines/wintermute/math/vector2.h" +#include "engines/wintermute/math/matrix4.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_region.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/utils/utils.h" +#include "engines/wintermute/platform_osystem.h" +#include "common/str.h" +#include "common/math.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(PartEmitter, false) + +////////////////////////////////////////////////////////////////////////// +PartEmitter::PartEmitter(BaseGame *inGame, BaseScriptHolder *owner) : BaseObject(inGame) { + _width = _height = 0; + + BasePlatform::setRectEmpty(&_border); + _borderThicknessLeft = _borderThicknessRight = _borderThicknessTop = _borderThicknessBottom = 0; + + _angle1 = _angle2 = 0; + + _velocity1 = _velocity2 = 0.0f; + _velocityZBased = false; + + _scale1 = _scale2 = 100.0f; + _scaleZBased = false; + + _maxParticles = 100; + + _lifeTime1 = _lifeTime2 = 1000; + _lifeTimeZBased = false; + + _lastGenTime = 0; + _genInterval = 0; + _genAmount = 1; + + _overheadTime = 0; + _running = false; + + _maxBatches = 0; + _batchesGenerated = 0; + + _fadeInTime = _fadeOutTime = 0; + + _alpha1 = _alpha2 = 255; + _alphaTimeBased = false; + + _rotation1 = _rotation2 = 0.0f; + _angVelocity1 = _angVelocity2 = 0.0f; + + _growthRate1 = _growthRate2 = 0.0f; + _exponentialGrowth = false; + + _useRegion = false; + + _emitEvent = NULL; + _owner = owner; +} + + +////////////////////////////////////////////////////////////////////////// +PartEmitter::~PartEmitter(void) { + for (uint32 i = 0; i < _particles.size(); i++) { + delete _particles[i]; + } + _particles.clear(); + + for (uint32 i = 0; i < _forces.size(); i++) { + delete _forces[i]; + } + _forces.clear(); + + + for (uint32 i = 0; i < _sprites.size(); i++) { + delete[] _sprites[i]; + } + _sprites.clear(); + + delete[] _emitEvent; + _emitEvent = NULL; +} + +////////////////////////////////////////////////////////////////////////// +bool PartEmitter::addSprite(const char *filename) { + if (!filename) { + return STATUS_FAILED; + } + + // do we already have the file? + for (uint32 i = 0; i < _sprites.size(); i++) { + if (scumm_stricmp(filename, _sprites[i]) == 0) { + return STATUS_OK; + } + } + + // check if file exists + Common::SeekableReadStream *File = BaseFileManager::getEngineInstance()->openFile(filename); + if (!File) { + _gameRef->LOG(0, "Sprite '%s' not found", filename); + return STATUS_FAILED; + } else { + BaseFileManager::getEngineInstance()->closeFile(File); + } + + char *str = new char[strlen(filename) + 1]; + strcpy(str, filename); + _sprites.add(str); + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool PartEmitter::removeSprite(const char *filename) { + for (uint32 i = 0; i < _sprites.size(); i++) { + if (scumm_stricmp(filename, _sprites[i]) == 0) { + delete[] _sprites[i]; + _sprites.remove_at(i); + return STATUS_OK; + } + } + return STATUS_FAILED; +} + +////////////////////////////////////////////////////////////////////////// +bool PartEmitter::initParticle(PartParticle *particle, uint32 currentTime, uint32 timerDelta) { + if (!particle) { + return STATUS_FAILED; + } + if (_sprites.size() == 0) { + return STATUS_FAILED; + } + + int posX = BaseUtils::randomInt(_posX, _posX + _width); + int posY = BaseUtils::randomInt(_posY, _posY + _height); + float posZ = BaseUtils::randomFloat(0.0f, 100.0f); + + float velocity; + if (_velocityZBased) { + velocity = _velocity1 + posZ * (_velocity2 - _velocity1) / 100; + } else { + velocity = BaseUtils::randomFloat(_velocity1, _velocity2); + } + + float scale; + if (_scaleZBased) { + scale = _scale1 + posZ * (_scale2 - _scale1) / 100; + } else { + scale = BaseUtils::randomFloat(_scale1, _scale2); + } + + int lifeTime; + if (_lifeTimeZBased) { + lifeTime = (int)(_lifeTime2 - posZ * (_lifeTime2 - _lifeTime1) / 100); + } else { + lifeTime = BaseUtils::randomInt(_lifeTime1, _lifeTime2); + } + + float angle = BaseUtils::randomAngle(_angle1, _angle2); + int spriteIndex = BaseUtils::randomInt(0, _sprites.size() - 1); + + float rotation = BaseUtils::randomAngle(_rotation1, _rotation2); + float angVelocity = BaseUtils::randomFloat(_angVelocity1, _angVelocity2); + float growthRate = BaseUtils::randomFloat(_growthRate1, _growthRate2); + + if (!BasePlatform::isRectEmpty(&_border)) { + int thicknessLeft = (int)(_borderThicknessLeft - (float)_borderThicknessLeft * posZ / 100.0f); + int thicknessRight = (int)(_borderThicknessRight - (float)_borderThicknessRight * posZ / 100.0f); + int thicknessTop = (int)(_borderThicknessTop - (float)_borderThicknessTop * posZ / 100.0f); + int thicknessBottom = (int)(_borderThicknessBottom - (float)_borderThicknessBottom * posZ / 100.0f); + + particle->_border = _border; + particle->_border.left += thicknessLeft; + particle->_border.right -= thicknessRight; + particle->_border.top += thicknessTop; + particle->_border.bottom -= thicknessBottom; + } + + Vector2 vecPos((float)posX, (float)posY); + Vector2 vecVel(0, velocity); + + Matrix4 matRot; + matRot.rotationZ(Common::deg2rad(BaseUtils::normalizeAngle(angle - 180))); + matRot.transformVector2(vecVel); + + if (_alphaTimeBased) { + particle->_alpha1 = _alpha1; + particle->_alpha2 = _alpha2; + } else { + int alpha = BaseUtils::randomInt(_alpha1, _alpha2); + particle->_alpha1 = alpha; + particle->_alpha2 = alpha; + } + + particle->_creationTime = currentTime; + particle->_pos = vecPos; + particle->_posZ = posZ; + particle->_velocity = vecVel; + particle->_scale = scale; + particle->_lifeTime = lifeTime; + particle->_rotation = rotation; + particle->_angVelocity = angVelocity; + particle->_growthRate = growthRate; + particle->_exponentialGrowth = _exponentialGrowth; + particle->_isDead = DID_FAIL(particle->setSprite(_sprites[spriteIndex])); + particle->fadeIn(currentTime, _fadeInTime); + + + if (particle->_isDead) { + return STATUS_FAILED; + } else { + return STATUS_OK; + } +} + +////////////////////////////////////////////////////////////////////////// +bool PartEmitter::update() { + if (!_running) { + return STATUS_OK; + } else { + return updateInternal(_gameRef->_timer, _gameRef->_timerDelta); + } +} + +////////////////////////////////////////////////////////////////////////// +bool PartEmitter::updateInternal(uint32 currentTime, uint32 timerDelta) { + int numLive = 0; + + for (uint32 i = 0; i < _particles.size(); i++) { + _particles[i]->update(this, currentTime, timerDelta); + + if (!_particles[i]->_isDead) { + numLive++; + } + } + + + // we're understaffed + if (numLive < _maxParticles) { + bool needsSort = false; + if ((int)(currentTime - _lastGenTime) > _genInterval) { + _lastGenTime = currentTime; + _batchesGenerated++; + + if (_maxBatches > 0 && _batchesGenerated > _maxBatches) { + return STATUS_OK; + } + + int toGen = MIN(_genAmount, _maxParticles - numLive); + while (toGen > 0) { + int firstDeadIndex = -1; + for (uint32 i = 0; i < _particles.size(); i++) { + if (_particles[i]->_isDead) { + firstDeadIndex = i; + break; + } + } + + PartParticle *particle; + if (firstDeadIndex >= 0) { + particle = _particles[firstDeadIndex]; + } else { + particle = new PartParticle(_gameRef); + _particles.add(particle); + } + initParticle(particle, currentTime, timerDelta); + needsSort = true; + + toGen--; + } + } + if (needsSort && (_scaleZBased || _velocityZBased || _lifeTimeZBased)) { + sortParticlesByZ(); + } + + // we actually generated some particles and we're not in fast-forward mode + if (needsSort && _overheadTime == 0) { + if (_owner && _emitEvent) { + _owner->applyEvent(_emitEvent); + } + } + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool PartEmitter::display(BaseRegion *region) { + if (_sprites.size() <= 1) { + _gameRef->_renderer->startSpriteBatch(); + } + + for (uint32 i = 0; i < _particles.size(); i++) { + if (region != NULL && _useRegion) { + if (!region->pointInRegion((int)_particles[i]->_pos.x, (int)_particles[i]->_pos.y)) { + continue; + } + } + + _particles[i]->display(this); + } + + if (_sprites.size() <= 1) { + _gameRef->_renderer->endSpriteBatch(); + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool PartEmitter::start() { + for (uint32 i = 0; i < _particles.size(); i++) { + _particles[i]->_isDead = true; + } + _running = true; + _batchesGenerated = 0; + + + if (_overheadTime > 0) { + uint32 delta = 500; + int steps = _overheadTime / delta; + uint32 currentTime = _gameRef->_timer - _overheadTime; + + for (int i = 0; i < steps; i++) { + updateInternal(currentTime, delta); + currentTime += delta; + } + _overheadTime = 0; + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool PartEmitter::sortParticlesByZ() { + // sort particles by _posY + Common::sort(_particles.begin(), _particles.end(), PartEmitter::compareZ); + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool PartEmitter::compareZ(const PartParticle *p1, const PartParticle *p2) { + if (p1->_posZ < p2->_posZ) { + return true; + } else if (p1->_posZ > p2->_posZ) { + return false; + } else { + return false; + } +} + +////////////////////////////////////////////////////////////////////////// +bool PartEmitter::setBorder(int x, int y, int width, int height) { + BasePlatform::setRect(&_border, x, y, x + width, y + height); + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool PartEmitter::setBorderThickness(int thicknessLeft, int thicknessRight, int thicknessTop, int thicknessBottom) { + _borderThicknessLeft = thicknessLeft; + _borderThicknessRight = thicknessRight; + _borderThicknessTop = thicknessTop; + _borderThicknessBottom = thicknessBottom; + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +PartForce *PartEmitter::addForceByName(const Common::String &name) { + PartForce *force = NULL; + + for (uint32 i = 0; i < _forces.size(); i++) { + if (scumm_stricmp(name.c_str(), _forces[i]->getName()) == 0) { + force = _forces[i]; + break; + } + } + if (!force) { + force = new PartForce(_gameRef); + if (force) { + force->setName(name.c_str()); + _forces.add(force); + } + } + return force; +} + + +////////////////////////////////////////////////////////////////////////// +bool PartEmitter::addForce(const Common::String &name, PartForce::TForceType type, int posX, int posY, float angle, float strength) { + PartForce *force = addForceByName(name); + if (!force) { + return STATUS_FAILED; + } + + force->_type = type; + force->_pos = Vector2(posX, posY); + + force->_direction = Vector2(0, strength); + Matrix4 matRot; + matRot.rotationZ(Common::deg2rad(BaseUtils::normalizeAngle(angle - 180))); + matRot.transformVector2(force->_direction); + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool PartEmitter::removeForce(const Common::String &name) { + for (uint32 i = 0; i < _forces.size(); i++) { + if (scumm_stricmp(name.c_str(), _forces[i]->getName()) == 0) { + delete _forces[i]; + _forces.remove_at(i); + return STATUS_OK; + } + } + return STATUS_FAILED; +} + + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool PartEmitter::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // SetBorder + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "SetBorder") == 0) { + stack->correctParams(4); + int borderX = stack->pop()->getInt(); + int borderY = stack->pop()->getInt(); + int borderWidth = stack->pop()->getInt(); + int borderHeight = stack->pop()->getInt(); + + stack->pushBool(DID_SUCCEED(setBorder(borderX, borderY, borderWidth, borderHeight))); + + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // SetBorderThickness + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetBorderThickness") == 0) { + stack->correctParams(4); + int left = stack->pop()->getInt(); + int right = stack->pop()->getInt(); + int top = stack->pop()->getInt(); + int bottom = stack->pop()->getInt(); + + stack->pushBool(DID_SUCCEED(setBorderThickness(left, right, top, bottom))); + + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // AddSprite + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AddSprite") == 0) { + stack->correctParams(1); + const char *spriteFile = stack->pop()->getString(); + stack->pushBool(DID_SUCCEED(addSprite(spriteFile))); + + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // RemoveSprite + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "RemoveSprite") == 0) { + stack->correctParams(1); + const char *spriteFile = stack->pop()->getString(); + stack->pushBool(DID_SUCCEED(removeSprite(spriteFile))); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Start + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Start") == 0) { + stack->correctParams(1); + _overheadTime = stack->pop()->getInt(); + stack->pushBool(DID_SUCCEED(start())); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Stop + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Stop") == 0) { + stack->correctParams(0); + + for (uint32 i = 0; i < _particles.size(); i++) { + delete _particles[i]; + } + _particles.clear(); + + _running = false; + stack->pushBool(true); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Pause + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Pause") == 0) { + stack->correctParams(0); + _running = false; + stack->pushBool(true); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Resume + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Resume") == 0) { + stack->correctParams(0); + _running = true; + stack->pushBool(true); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // AddGlobalForce + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AddGlobalForce") == 0) { + stack->correctParams(3); + const char *forceName = stack->pop()->getString(); + float angle = stack->pop()->getFloat(); + float strength = stack->pop()->getFloat(); + + stack->pushBool(DID_SUCCEED(addForce(forceName, PartForce::FORCE_GLOBAL, 0, 0, angle, strength))); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // AddPointForce + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AddPointForce") == 0) { + stack->correctParams(5); + const char *forceName = stack->pop()->getString(); + int posX = stack->pop()->getInt(); + int posY = stack->pop()->getInt(); + float angle = stack->pop()->getFloat(); + float strength = stack->pop()->getFloat(); + + stack->pushBool(DID_SUCCEED(addForce(forceName, PartForce::FORCE_GLOBAL, posX, posY, angle, strength))); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // RemoveForce + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "RemoveForce") == 0) { + stack->correctParams(1); + const char *forceName = stack->pop()->getString(); + + stack->pushBool(DID_SUCCEED(removeForce(forceName))); + + return STATUS_OK; + } else { + return BaseObject::scCallMethod(script, stack, thisStack, name); + } +} + +////////////////////////////////////////////////////////////////////////// +ScValue *PartEmitter::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("particle-emitter"); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // X + ////////////////////////////////////////////////////////////////////////// + else if (name == "X") { + _scValue->setInt(_posX); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // Y + ////////////////////////////////////////////////////////////////////////// + else if (name == "Y") { + _scValue->setInt(_posY); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // Width + ////////////////////////////////////////////////////////////////////////// + else if (name == "Width") { + _scValue->setInt(_width); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // Height + ////////////////////////////////////////////////////////////////////////// + else if (name == "Height") { + _scValue->setInt(_height); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Scale1 + ////////////////////////////////////////////////////////////////////////// + else if (name == "Scale1") { + _scValue->setFloat(_scale1); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // Scale2 + ////////////////////////////////////////////////////////////////////////// + else if (name == "Scale2") { + _scValue->setFloat(_scale2); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // ScaleZBased + ////////////////////////////////////////////////////////////////////////// + else if (name == "ScaleZBased") { + _scValue->setBool(_scaleZBased); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Velocity1 + ////////////////////////////////////////////////////////////////////////// + else if (name == "Velocity1") { + _scValue->setFloat(_velocity1); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // Velocity2 + ////////////////////////////////////////////////////////////////////////// + else if (name == "Velocity2") { + _scValue->setFloat(_velocity2); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // VelocityZBased + ////////////////////////////////////////////////////////////////////////// + else if (name == "VelocityZBased") { + _scValue->setBool(_velocityZBased); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // LifeTime1 + ////////////////////////////////////////////////////////////////////////// + else if (name == "LifeTime1") { + _scValue->setInt(_lifeTime1); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // LifeTime2 + ////////////////////////////////////////////////////////////////////////// + else if (name == "LifeTime2") { + _scValue->setInt(_lifeTime2); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // LifeTimeZBased + ////////////////////////////////////////////////////////////////////////// + else if (name == "LifeTimeZBased") { + _scValue->setBool(_lifeTimeZBased); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Angle1 + ////////////////////////////////////////////////////////////////////////// + else if (name == "Angle1") { + _scValue->setInt(_angle1); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // Angle2 + ////////////////////////////////////////////////////////////////////////// + else if (name == "Angle2") { + _scValue->setInt(_angle2); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // AngVelocity1 + ////////////////////////////////////////////////////////////////////////// + else if (name == "AngVelocity1") { + _scValue->setFloat(_angVelocity1); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // AngVelocity2 + ////////////////////////////////////////////////////////////////////////// + else if (name == "AngVelocity2") { + _scValue->setFloat(_angVelocity2); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Rotation1 + ////////////////////////////////////////////////////////////////////////// + else if (name == "Rotation1") { + _scValue->setFloat(_rotation1); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // Rotation2 + ////////////////////////////////////////////////////////////////////////// + else if (name == "Rotation2") { + _scValue->setFloat(_rotation2); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Alpha1 + ////////////////////////////////////////////////////////////////////////// + else if (name == "Alpha1") { + _scValue->setInt(_alpha1); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // Alpha2 + ////////////////////////////////////////////////////////////////////////// + else if (name == "Alpha2") { + _scValue->setInt(_alpha2); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // AlphaTimeBased + ////////////////////////////////////////////////////////////////////////// + else if (name == "AlphaTimeBased") { + _scValue->setBool(_alphaTimeBased); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // MaxParticles + ////////////////////////////////////////////////////////////////////////// + else if (name == "MaxParticles") { + _scValue->setInt(_maxParticles); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // NumLiveParticles (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "NumLiveParticles") { + int numAlive = 0; + for (uint32 i = 0; i < _particles.size(); i++) { + if (_particles[i] && !_particles[i]->_isDead) { + numAlive++; + } + } + _scValue->setInt(numAlive); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // GenerationInterval + ////////////////////////////////////////////////////////////////////////// + else if (name == "GenerationInterval") { + _scValue->setInt(_genInterval); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // GenerationAmount + ////////////////////////////////////////////////////////////////////////// + else if (name == "GenerationAmount") { + _scValue->setInt(_genAmount); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // MaxBatches + ////////////////////////////////////////////////////////////////////////// + else if (name == "MaxBatches") { + _scValue->setInt(_maxBatches); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // FadeInTime + ////////////////////////////////////////////////////////////////////////// + else if (name == "FadeInTime") { + _scValue->setInt(_fadeInTime); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // FadeOutTime + ////////////////////////////////////////////////////////////////////////// + else if (name == "FadeOutTime") { + _scValue->setInt(_fadeOutTime); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // GrowthRate1 + ////////////////////////////////////////////////////////////////////////// + else if (name == "GrowthRate1") { + _scValue->setFloat(_growthRate1); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // GrowthRate2 + ////////////////////////////////////////////////////////////////////////// + else if (name == "GrowthRate2") { + _scValue->setFloat(_growthRate2); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // ExponentialGrowth + ////////////////////////////////////////////////////////////////////////// + else if (name == "ExponentialGrowth") { + _scValue->setBool(_exponentialGrowth); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // UseRegion + ////////////////////////////////////////////////////////////////////////// + else if (name == "UseRegion") { + _scValue->setBool(_useRegion); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // EmitEvent + ////////////////////////////////////////////////////////////////////////// + else if (name == "EmitEvent") { + if (!_emitEvent) { + _scValue->setNULL(); + } else { + _scValue->setString(_emitEvent); + } + return _scValue; + } else { + return BaseObject::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool PartEmitter::scSetProperty(const char *name, ScValue *value) { + ////////////////////////////////////////////////////////////////////////// + // X + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "X") == 0) { + _posX = value->getInt(); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // Y + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Y") == 0) { + _posY = value->getInt(); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // Width + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Width") == 0) { + _width = value->getInt(); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // Height + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Height") == 0) { + _height = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Scale1 + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Scale1") == 0) { + _scale1 = value->getFloat(); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // Scale2 + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Scale2") == 0) { + _scale2 = value->getFloat(); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // ScaleZBased + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ScaleZBased") == 0) { + _scaleZBased = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Velocity1 + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Velocity1") == 0) { + _velocity1 = value->getFloat(); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // Velocity2 + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Velocity2") == 0) { + _velocity2 = value->getFloat(); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // VelocityZBased + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "VelocityZBased") == 0) { + _velocityZBased = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // LifeTime1 + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "LifeTime1") == 0) { + _lifeTime1 = value->getInt(); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // LifeTime2 + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "LifeTime2") == 0) { + _lifeTime2 = value->getInt(); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // LifeTimeZBased + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "LifeTimeZBased") == 0) { + _lifeTimeZBased = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Angle1 + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Angle1") == 0) { + _angle1 = value->getInt(); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // Angle2 + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Angle2") == 0) { + _angle2 = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // AngVelocity1 + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AngVelocity1") == 0) { + _angVelocity1 = value->getFloat(); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // AngVelocity2 + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AngVelocity2") == 0) { + _angVelocity2 = value->getFloat(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Rotation1 + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Rotation1") == 0) { + _rotation1 = value->getFloat(); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // Rotation2 + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Rotation2") == 0) { + _rotation2 = value->getFloat(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Alpha1 + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Alpha1") == 0) { + _alpha1 = value->getInt(); + if (_alpha1 < 0) { + _alpha1 = 0; + } + if (_alpha1 > 255) { + _alpha1 = 255; + } + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // Alpha2 + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Alpha2") == 0) { + _alpha2 = value->getInt(); + if (_alpha2 < 0) { + _alpha2 = 0; + } + if (_alpha2 > 255) { + _alpha2 = 255; + } + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // AlphaTimeBased + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AlphaTimeBased") == 0) { + _alphaTimeBased = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // MaxParticles + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "MaxParticles") == 0) { + _maxParticles = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GenerationInterval + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GenerationInterval") == 0) { + _genInterval = value->getInt(); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // GenerationAmount + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GenerationAmount") == 0) { + _genAmount = value->getInt(); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // MaxBatches + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "MaxBatches") == 0) { + _maxBatches = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // FadeInTime + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "FadeInTime") == 0) { + _fadeInTime = value->getInt(); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // FadeOutTime + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "FadeOutTime") == 0) { + _fadeOutTime = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GrowthRate1 + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GrowthRate1") == 0) { + _growthRate1 = value->getFloat(); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // GrowthRate2 + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GrowthRate2") == 0) { + _growthRate2 = value->getFloat(); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // ExponentialGrowth + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ExponentialGrowth") == 0) { + _exponentialGrowth = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // UseRegion + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "UseRegion") == 0) { + _useRegion = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // EmitEvent + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "EmitEvent") == 0) { + delete[] _emitEvent; + _emitEvent = NULL; + if (!value->isNULL()) { + BaseUtils::setString(&_emitEvent, value->getString()); + } + return STATUS_OK; + } else { + return BaseObject::scSetProperty(name, value); + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *PartEmitter::scToString() { + return "[particle emitter]"; +} + + + + +////////////////////////////////////////////////////////////////////////// +bool PartEmitter::persist(BasePersistenceManager *persistMgr) { + BaseObject::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_width)); + persistMgr->transfer(TMEMBER(_height)); + + persistMgr->transfer(TMEMBER(_angle1)); + persistMgr->transfer(TMEMBER(_angle2)); + + persistMgr->transfer(TMEMBER(_velocity1)); + persistMgr->transfer(TMEMBER(_velocity2)); + persistMgr->transfer(TMEMBER(_velocityZBased)); + + persistMgr->transfer(TMEMBER(_scale1)); + persistMgr->transfer(TMEMBER(_scale2)); + persistMgr->transfer(TMEMBER(_scaleZBased)); + + persistMgr->transfer(TMEMBER(_maxParticles)); + + persistMgr->transfer(TMEMBER(_lifeTime1)); + persistMgr->transfer(TMEMBER(_lifeTime2)); + persistMgr->transfer(TMEMBER(_lifeTimeZBased)); + + persistMgr->transfer(TMEMBER(_genInterval)); + persistMgr->transfer(TMEMBER(_genAmount)); + + persistMgr->transfer(TMEMBER(_running)); + persistMgr->transfer(TMEMBER(_overheadTime)); + + persistMgr->transfer(TMEMBER(_border)); + persistMgr->transfer(TMEMBER(_borderThicknessLeft)); + persistMgr->transfer(TMEMBER(_borderThicknessRight)); + persistMgr->transfer(TMEMBER(_borderThicknessTop)); + persistMgr->transfer(TMEMBER(_borderThicknessBottom)); + + persistMgr->transfer(TMEMBER(_fadeInTime)); + persistMgr->transfer(TMEMBER(_fadeOutTime)); + + persistMgr->transfer(TMEMBER(_alpha1)); + persistMgr->transfer(TMEMBER(_alpha2)); + persistMgr->transfer(TMEMBER(_alphaTimeBased)); + + persistMgr->transfer(TMEMBER(_angVelocity1)); + persistMgr->transfer(TMEMBER(_angVelocity2)); + + persistMgr->transfer(TMEMBER(_rotation1)); + persistMgr->transfer(TMEMBER(_rotation2)); + + persistMgr->transfer(TMEMBER(_growthRate1)); + persistMgr->transfer(TMEMBER(_growthRate2)); + persistMgr->transfer(TMEMBER(_exponentialGrowth)); + + persistMgr->transfer(TMEMBER(_useRegion)); + + persistMgr->transfer(TMEMBER_INT(_maxBatches)); + persistMgr->transfer(TMEMBER_INT(_batchesGenerated)); + + persistMgr->transfer(TMEMBER(_emitEvent)); + persistMgr->transfer(TMEMBER(_owner)); + + + _sprites.persist(persistMgr); + + uint32 numForces; + if (persistMgr->getIsSaving()) { + numForces = _forces.size(); + persistMgr->transfer(TMEMBER(numForces)); + for (uint32 i = 0; i < _forces.size(); i++) { + _forces[i]->persist(persistMgr); + } + } else { + persistMgr->transfer(TMEMBER(numForces)); + for (uint32 i = 0; i < numForces; i++) { + PartForce *force = new PartForce(_gameRef); + force->persist(persistMgr); + _forces.add(force); + } + } + + uint32 numParticles; + if (persistMgr->getIsSaving()) { + numParticles = _particles.size(); + persistMgr->transfer(TMEMBER(numParticles)); + for (uint32 i = 0; i < _particles.size(); i++) { + _particles[i]->persist(persistMgr); + } + } else { + persistMgr->transfer(TMEMBER(numParticles)); + for (uint32 i = 0; i < numParticles; i++) { + PartParticle *particle = new PartParticle(_gameRef); + particle->persist(persistMgr); + _particles.add(particle); + } + } + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/particles/part_emitter.h b/engines/wintermute/base/particles/part_emitter.h new file mode 100644 index 0000000000..3aa55e1ac8 --- /dev/null +++ b/engines/wintermute/base/particles/part_emitter.h @@ -0,0 +1,140 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_PARTEMITTER_H +#define WINTERMUTE_PARTEMITTER_H + + +#include "engines/wintermute/base/base_object.h" +#include "engines/wintermute/base/particles/part_force.h" + +namespace Wintermute { +class BaseRegion; +class PartParticle; +class PartEmitter : public BaseObject { +public: + DECLARE_PERSISTENT(PartEmitter, BaseObject) + + PartEmitter(BaseGame *inGame, BaseScriptHolder *Owner); + virtual ~PartEmitter(void); + + int _fadeOutTime; + + bool start(); + + bool update(); + bool display() { return display(NULL); } // To avoid shadowing the inherited display-function. + bool display(BaseRegion *region); + + bool sortParticlesByZ(); + bool addSprite(const char *filename); + bool removeSprite(const char *filename); + bool setBorder(int x, int y, int width, int height); + bool setBorderThickness(int thicknessLeft, int thicknessRight, int thicknessTop, int thicknessBottom); + + bool addForce(const Common::String &name, PartForce::TForceType type, int posX, int posY, float angle, float strength); + bool removeForce(const Common::String &name); + + BaseArray<PartForce *> _forces; + + // scripting interface + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + virtual const char *scToString(); + + +private: + int _width; + int _height; + + int _angle1; + int _angle2; + + float _rotation1; + float _rotation2; + + float _angVelocity1; + float _angVelocity2; + + float _growthRate1; + float _growthRate2; + bool _exponentialGrowth; + + float _velocity1; + float _velocity2; + bool _velocityZBased; + + float _scale1; + float _scale2; + bool _scaleZBased; + + int _maxParticles; + + int _lifeTime1; + int _lifeTime2; + bool _lifeTimeZBased; + + int _genInterval; + int _genAmount; + + bool _running; + int _overheadTime; + + int _maxBatches; + int _batchesGenerated; + + Rect32 _border; + int _borderThicknessLeft; + int _borderThicknessRight; + int _borderThicknessTop; + int _borderThicknessBottom; + + int _fadeInTime; + + int _alpha1; + int _alpha2; + bool _alphaTimeBased; + + bool _useRegion; + + char *_emitEvent; + BaseScriptHolder *_owner; + + PartForce *addForceByName(const Common::String &name); + bool static compareZ(const PartParticle *p1, const PartParticle *p2); + bool initParticle(PartParticle *particle, uint32 currentTime, uint32 timerDelta); + bool updateInternal(uint32 currentTime, uint32 timerDelta); + uint32 _lastGenTime; + BaseArray<PartParticle *> _particles; + BaseArray<char *> _sprites; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/particles/part_force.cpp b/engines/wintermute/base/particles/part_force.cpp new file mode 100644 index 0000000000..df84162504 --- /dev/null +++ b/engines/wintermute/base/particles/part_force.cpp @@ -0,0 +1,65 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/persistent.h" +#include "engines/wintermute/base/particles/part_force.h" +#include "engines/wintermute/base/base_persistence_manager.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////////// +PartForce::PartForce(BaseGame *inGame) : BaseNamedObject(inGame) { + _pos = Vector2(0.0f, 0.0f); + _direction = Vector2(0.0f, 0.0f); + _type = FORCE_POINT; +} + + +////////////////////////////////////////////////////////////////////////// +PartForce::~PartForce(void) { +} + + +////////////////////////////////////////////////////////////////////////// +bool PartForce::persist(BasePersistenceManager *persistMgr) { + if (persistMgr->getIsSaving()) { + const char *name = getName(); + persistMgr->transfer(TMEMBER(name)); + } else { + const char *name; + persistMgr->transfer(TMEMBER(name)); + setName(name); + } + persistMgr->transfer(TMEMBER(_pos)); + persistMgr->transfer(TMEMBER(_direction)); + persistMgr->transfer(TMEMBER_INT(_type)); + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/particles/part_force.h b/engines/wintermute/base/particles/part_force.h new file mode 100644 index 0000000000..27f4cb7d90 --- /dev/null +++ b/engines/wintermute/base/particles/part_force.h @@ -0,0 +1,57 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_PARTFORCE_H +#define WINTERMUTE_PARTFORCE_H + + +#include "engines/wintermute/base/base.h" +#include "engines/wintermute/base/base_named_object.h" +#include "engines/wintermute/math/vector2.h" + +namespace Wintermute { + +class PartForce : public BaseNamedObject { +public: + enum TForceType { + FORCE_POINT, FORCE_GLOBAL + }; + + PartForce(BaseGame *inGame); + virtual ~PartForce(void); + + Vector2 _pos; + Vector2 _direction; + TForceType _type; + + bool persist(BasePersistenceManager *PersistMgr); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/particles/part_particle.cpp b/engines/wintermute/base/particles/part_particle.cpp new file mode 100644 index 0000000000..0b850d9618 --- /dev/null +++ b/engines/wintermute/base/particles/part_particle.cpp @@ -0,0 +1,269 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/particles/part_particle.h" +#include "engines/wintermute/base/particles/part_emitter.h" +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/utils/utils.h" +#include "engines/wintermute/platform_osystem.h" +#include "common/str.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////////// +PartParticle::PartParticle(BaseGame *inGame) : BaseClass(inGame) { + _pos = Vector2(0.0f, 0.0f); + _posZ = 0.0f; + _velocity = Vector2(0.0f, 0.0f); + _scale = 100.0f; + _sprite = NULL; + _creationTime = 0; + _lifeTime = 0; + _isDead = true; + BasePlatform::setRectEmpty(&_border); + + _state = PARTICLE_NORMAL; + _fadeStart = 0; + _fadeTime = 0; + _currentAlpha = 255; + + _alpha1 = _alpha2 = 255; + + _rotation = 0.0f; + _angVelocity = 0.0f; + + _growthRate = 0.0f; + _exponentialGrowth = false; +} + + +////////////////////////////////////////////////////////////////////////// +PartParticle::~PartParticle(void) { + delete _sprite; + _sprite = NULL; +} + +////////////////////////////////////////////////////////////////////////// +bool PartParticle::setSprite(const Common::String &filename) { + if (_sprite && _sprite->getFilename() && scumm_stricmp(filename.c_str(), _sprite->getFilename()) == 0) { + _sprite->reset(); + return STATUS_OK; + } + + delete _sprite; + _sprite = NULL; + + SystemClassRegistry::getInstance()->_disabled = true; + _sprite = new BaseSprite(_gameRef, (BaseObject*)_gameRef); + if (_sprite && DID_SUCCEED(_sprite->loadFile(filename))) { + SystemClassRegistry::getInstance()->_disabled = false; + return STATUS_OK; + } else { + delete _sprite; + _sprite = NULL; + SystemClassRegistry::getInstance()->_disabled = false; + return STATUS_FAILED; + } + +} + +////////////////////////////////////////////////////////////////////////// +bool PartParticle::update(PartEmitter *emitter, uint32 currentTime, uint32 timerDelta) { + if (_state == PARTICLE_FADEIN) { + if (currentTime - _fadeStart >= (uint32)_fadeTime) { + _state = PARTICLE_NORMAL; + _currentAlpha = _alpha1; + } else { + _currentAlpha = (int)(((float)currentTime - (float)_fadeStart) / (float)_fadeTime * _alpha1); + } + + return STATUS_OK; + } else if (_state == PARTICLE_FADEOUT) { + if (currentTime - _fadeStart >= (uint32)_fadeTime) { + _isDead = true; + return STATUS_OK; + } else { + _currentAlpha = _fadeStartAlpha - (int)(((float)currentTime - (float)_fadeStart) / (float)_fadeTime * _fadeStartAlpha); + } + + return STATUS_OK; + } else { + // time is up + if (_lifeTime > 0) { + if (currentTime - _creationTime >= (uint32)_lifeTime) { + if (emitter->_fadeOutTime > 0) { + fadeOut(currentTime, emitter->_fadeOutTime); + } else { + _isDead = true; + } + } + } + + // particle hit the border + if (!_isDead && !BasePlatform::isRectEmpty(&_border)) { + Point32 p; + p.x = (int32)_pos.x; + p.y = (int32)_pos.y; + if (!BasePlatform::ptInRect(&_border, p)) { + fadeOut(currentTime, emitter->_fadeOutTime); + } + } + if (_state != PARTICLE_NORMAL) { + return STATUS_OK; + } + + // update alpha + if (_lifeTime > 0) { + int age = (int)(currentTime - _creationTime); + int alphaDelta = (int)(_alpha2 - _alpha1); + + _currentAlpha = _alpha1 + (int)(((float)alphaDelta / (float)_lifeTime * (float)age)); + } + + // update position + float elapsedTime = (float)timerDelta / 1000.f; + + for (uint32 i = 0; i < emitter->_forces.size(); i++) { + PartForce *force = emitter->_forces[i]; + switch (force->_type) { + case PartForce::FORCE_GLOBAL: + _velocity += force->_direction * elapsedTime; + break; + + case PartForce::FORCE_POINT: { + Vector2 vecDist = force->_pos - _pos; + float dist = fabs(vecDist.length()); + + dist = 100.0f / dist; + + _velocity += force->_direction * dist * elapsedTime; + } + break; + } + } + _pos += _velocity * elapsedTime; + + // update rotation + _rotation += _angVelocity * elapsedTime; + _rotation = BaseUtils::normalizeAngle(_rotation); + + // update scale + if (_exponentialGrowth) { + _scale += _scale / 100.0f * _growthRate * elapsedTime; + } else { + _scale += _growthRate * elapsedTime; + } + + if (_scale <= 0.0f) { + _isDead = true; + } + + + return STATUS_OK; + } +} + +////////////////////////////////////////////////////////////////////////// +bool PartParticle::display(PartEmitter *emitter) { + if (!_sprite) { + return STATUS_FAILED; + } + if (_isDead) { + return STATUS_OK; + } + + _sprite->getCurrentFrame(); + return _sprite->display((int)_pos.x, (int)_pos.y, + NULL, + _scale, _scale, + BYTETORGBA(255, 255, 255, _currentAlpha), + _rotation, + emitter->_blendMode); +} + + +////////////////////////////////////////////////////////////////////////// +bool PartParticle::fadeIn(uint32 currentTime, int fadeTime) { + _currentAlpha = 0; + _fadeStart = currentTime; + _fadeTime = fadeTime; + _state = PARTICLE_FADEIN; + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool PartParticle::fadeOut(uint32 currentTime, int fadeTime) { + //_currentAlpha = 255; + _fadeStartAlpha = _currentAlpha; + _fadeStart = currentTime; + _fadeTime = fadeTime; + _state = PARTICLE_FADEOUT; + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool PartParticle::persist(BasePersistenceManager *persistMgr) { + persistMgr->transfer(TMEMBER(_alpha1)); + persistMgr->transfer(TMEMBER(_alpha2)); + persistMgr->transfer(TMEMBER(_border)); + persistMgr->transfer(TMEMBER(_pos)); + persistMgr->transfer(TMEMBER(_posZ)); + persistMgr->transfer(TMEMBER(_velocity)); + persistMgr->transfer(TMEMBER(_scale)); + persistMgr->transfer(TMEMBER(_creationTime)); + persistMgr->transfer(TMEMBER(_lifeTime)); + persistMgr->transfer(TMEMBER(_isDead)); + persistMgr->transfer(TMEMBER_INT(_state)); + persistMgr->transfer(TMEMBER(_fadeStart)); + persistMgr->transfer(TMEMBER(_fadeTime)); + persistMgr->transfer(TMEMBER(_currentAlpha)); + persistMgr->transfer(TMEMBER(_angVelocity)); + persistMgr->transfer(TMEMBER(_rotation)); + persistMgr->transfer(TMEMBER(_growthRate)); + persistMgr->transfer(TMEMBER(_exponentialGrowth)); + persistMgr->transfer(TMEMBER(_fadeStartAlpha)); + + if (persistMgr->getIsSaving()) { + const char *filename = _sprite->getFilename(); + persistMgr->transfer(TMEMBER(filename)); + } else { + char *filename; + persistMgr->transfer(TMEMBER(filename)); + SystemClassRegistry::getInstance()->_disabled = true; + setSprite(filename); + SystemClassRegistry::getInstance()->_disabled = false; + delete[] filename; + filename = NULL; + } + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/particles/part_particle.h b/engines/wintermute/base/particles/part_particle.h new file mode 100644 index 0000000000..4b8c2f131e --- /dev/null +++ b/engines/wintermute/base/particles/part_particle.h @@ -0,0 +1,90 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_PARTPARTICLE_H +#define WINTERMUTE_PARTPARTICLE_H + + +#include "engines/wintermute/base/base.h" +#include "engines/wintermute/math/rect32.h" +#include "engines/wintermute/math/vector2.h" + +namespace Wintermute { + +class PartEmitter; +class BaseSprite; +class BasePersistenceManager; + +class PartParticle : public BaseClass { +public: + enum TParticleState { + PARTICLE_NORMAL, PARTICLE_FADEIN, PARTICLE_FADEOUT + }; + + PartParticle(BaseGame *inGame); + virtual ~PartParticle(void); + + float _growthRate; + bool _exponentialGrowth; + + float _rotation; + float _angVelocity; + + int _alpha1; + int _alpha2; + + Rect32 _border; + Vector2 _pos; + float _posZ; + Vector2 _velocity; + float _scale; + BaseSprite *_sprite; + uint32 _creationTime; + int _lifeTime; + bool _isDead; + TParticleState _state; + + bool update(PartEmitter *emitter, uint32 currentTime, uint32 timerDelta); + bool display(PartEmitter *emitter); + + bool setSprite(const Common::String &filename); + + bool fadeIn(uint32 currentTime, int fadeTime); + bool fadeOut(uint32 currentTime, int fadeTime); + + bool persist(BasePersistenceManager *PersistMgr); +private: + uint32 _fadeStart; + int _fadeTime; + int _currentAlpha; + int _fadeStartAlpha; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/saveload.cpp b/engines/wintermute/base/saveload.cpp new file mode 100644 index 0000000000..12204e1b35 --- /dev/null +++ b/engines/wintermute/base/saveload.cpp @@ -0,0 +1,204 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_persistence_manager.h" +#include "engines/wintermute/wintermute.h" +#include "engines/wintermute/base/saveload.h" +#include "engines/wintermute/ad/ad_scene.h" +#include "engines/wintermute/base/base_engine.h" +#include "engines/wintermute/base/base_game.h" // Temporary +#include "engines/wintermute/base/base_region.h" +#include "engines/wintermute/base/base_sub_frame.h" +#include "engines/wintermute/base/font/base_font.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/base/sound/base_sound.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "common/savefile.h" +#include "common/config-manager.h" + +namespace Wintermute { + +bool SaveLoad::loadGame(const Common::String &filename, BaseGame *gameRef) { + gameRef->LOG(0, "Loading game '%s'...", filename.c_str()); + + bool ret; + + gameRef->_renderer->initSaveLoad(false); + + gameRef->_loadInProgress = true; + BasePersistenceManager *pm = new BasePersistenceManager(); + if (DID_SUCCEED(ret = pm->initLoad(filename))) { + //if (DID_SUCCEED(ret = cleanup())) { + if (DID_SUCCEED(ret = SystemClassRegistry::getInstance()->loadTable(gameRef, pm))) { + if (DID_SUCCEED(ret = SystemClassRegistry::getInstance()->loadInstances(gameRef, pm))) { + // Restore random-seed: + BaseEngine::instance().getRandomSource()->setSeed(pm->getDWORD()); + + // data initialization after load + SaveLoad::initAfterLoad(); + + gameRef->applyEvent("AfterLoad", true); + + gameRef->displayContent(true, false); + //_renderer->flip(); + } + } + } + + delete pm; + gameRef->_loadInProgress = false; + + gameRef->_renderer->endSaveLoad(); + + //_gameRef->LOG(0, "Load end %d", BaseUtils::GetUsedMemMB()); + // AdGame: + if (DID_SUCCEED(ret)) { + SystemClassRegistry::getInstance()->enumInstances(SaveLoad::afterLoadRegion, "AdRegion", NULL); + } + return ret; +} + +bool SaveLoad::saveGame(int slot, const char *desc, bool quickSave, BaseGame *gameRef) { + Common::String filename = SaveLoad::getSaveSlotFilename(slot); + + gameRef->LOG(0, "Saving game '%s'...", filename.c_str()); + + gameRef->applyEvent("BeforeSave", true); + + bool ret; + + BasePersistenceManager *pm = new BasePersistenceManager(); + if (DID_SUCCEED(ret = pm->initSave(desc))) { + gameRef->_renderer->initSaveLoad(true, quickSave); // TODO: The original code inited the indicator before the conditionals + if (DID_SUCCEED(ret = SystemClassRegistry::getInstance()->saveTable(gameRef, pm, quickSave))) { + if (DID_SUCCEED(ret = SystemClassRegistry::getInstance()->saveInstances(gameRef, pm, quickSave))) { + pm->putDWORD(BaseEngine::instance().getRandomSource()->getSeed()); + if (DID_SUCCEED(ret = pm->saveFile(filename))) { + ConfMan.setInt("most_recent_saveslot", slot); + } + } + } + } + + delete pm; + + gameRef->_renderer->endSaveLoad(); + + return ret; +} + +////////////////////////////////////////////////////////////////////////// +bool SaveLoad::initAfterLoad() { + SystemClassRegistry::getInstance()->enumInstances(afterLoadRegion, "BaseRegion", NULL); + SystemClassRegistry::getInstance()->enumInstances(afterLoadSubFrame, "BaseSubFrame", NULL); + SystemClassRegistry::getInstance()->enumInstances(afterLoadSound, "BaseSound", NULL); + SystemClassRegistry::getInstance()->enumInstances(afterLoadFont, "BaseFontTT", NULL); + SystemClassRegistry::getInstance()->enumInstances(afterLoadScript, "ScScript", NULL); + // AdGame: + SystemClassRegistry::getInstance()->enumInstances(afterLoadScene, "AdScene", NULL); + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +void SaveLoad::afterLoadScene(void *scene, void *data) { + ((AdScene *)scene)->afterLoad(); +} + +////////////////////////////////////////////////////////////////////////// +void SaveLoad::afterLoadRegion(void *region, void *data) { + ((BaseRegion *)region)->createRegion(); +} + + +////////////////////////////////////////////////////////////////////////// +void SaveLoad::afterLoadSubFrame(void *subframe, void *data) { + ((BaseSubFrame *)subframe)->setSurfaceSimple(); +} + + +////////////////////////////////////////////////////////////////////////// +void SaveLoad::afterLoadSound(void *sound, void *data) { + ((BaseSound *)sound)->setSoundSimple(); +} + +////////////////////////////////////////////////////////////////////////// +void SaveLoad::afterLoadFont(void *font, void *data) { + ((BaseFont *)font)->afterLoad(); +} + +////////////////////////////////////////////////////////////////////////// +void SaveLoad::afterLoadScript(void *script, void *data) { + ((ScScript *)script)->afterLoad(); +} + +Common::String SaveLoad::getSaveSlotFilename(int slot) { + BasePersistenceManager *pm = new BasePersistenceManager(); + Common::String filename = pm->getFilenameForSlot(slot); + delete pm; + debugC(kWintermuteDebugSaveGame, "getSaveSlotFileName(%d) = %s", slot, filename.c_str()); + return filename; +} + +bool SaveLoad::getSaveSlotDescription(int slot, char *buffer) { + buffer[0] = '\0'; + + Common::String filename = getSaveSlotFilename(slot); + BasePersistenceManager *pm = new BasePersistenceManager(); + if (!pm) { + return false; + } + + if (!(pm->initLoad(filename))) { + delete pm; + return false; + } + + strcpy(buffer, pm->_savedDescription); + delete pm; + + return true; +} + +bool SaveLoad::isSaveSlotUsed(int slot) { + Common::String filename = getSaveSlotFilename(slot); + BasePersistenceManager *pm = new BasePersistenceManager(); + bool ret = pm->getSaveExists(slot); + delete pm; + return ret; +} + +bool SaveLoad::emptySaveSlot(int slot) { + Common::String filename = getSaveSlotFilename(slot); + BasePersistenceManager *pm = new BasePersistenceManager(); + ((WintermuteEngine *)g_engine)->getSaveFileMan()->removeSavefile(pm->getFilenameForSlot(slot)); + delete pm; + return true; +} + + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/saveload.h b/engines/wintermute/base/saveload.h new file mode 100644 index 0000000000..722f7a89b6 --- /dev/null +++ b/engines/wintermute/base/saveload.h @@ -0,0 +1,57 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_SAVEGAME_H +#define WINTERMUTE_SAVEGAME_H + +#include "common/str.h" + +namespace Wintermute { +class BaseGame; +class SaveLoad { +public: + static bool emptySaveSlot(int slot); + static bool isSaveSlotUsed(int slot); + static bool getSaveSlotDescription(int slot, char *buffer); + static Common::String getSaveSlotFilename(int slot); + + static bool loadGame(const Common::String &filename, BaseGame *gameRef); + static bool saveGame(int slot, const char *desc, bool quickSave, BaseGame *gameRef); + static bool initAfterLoad(); + static void afterLoadScene(void *scene, void *data); + static void afterLoadRegion(void *region, void *data); +private: + static void afterLoadSubFrame(void *subframe, void *data); + static void afterLoadSound(void *sound, void *data); + static void afterLoadFont(void *font, void *data); + static void afterLoadScript(void *script, void *data); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/scriptables/dcscript.h b/engines/wintermute/base/scriptables/dcscript.h new file mode 100644 index 0000000000..4aae897dc2 --- /dev/null +++ b/engines/wintermute/base/scriptables/dcscript.h @@ -0,0 +1,141 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_DCSCRIPT_H +#define WINTERMUTE_DCSCRIPT_H + +namespace Wintermute { + +#define SCRIPT_MAGIC 0xDEC0ADDE +#define SCRIPT_VERSION 0x0102 + +// value types +typedef enum { + VAL_NULL, + VAL_STRING, + VAL_INT, + VAL_BOOL, + VAL_FLOAT, + VAL_OBJECT, + VAL_NATIVE, + VAL_VARIABLE_REF +} TValType; + + +// script states +typedef enum { + SCRIPT_RUNNING, + SCRIPT_WAITING, + SCRIPT_SLEEPING, + SCRIPT_FINISHED, + SCRIPT_PERSISTENT, + SCRIPT_ERROR, + SCRIPT_PAUSED, + SCRIPT_WAITING_SCRIPT, + SCRIPT_THREAD_FINISHED +} TScriptState; + +// opcodes +typedef enum { + II_DEF_VAR = 0, + II_DEF_GLOB_VAR, + II_RET, + II_RET_EVENT, + II_CALL, + II_CALL_BY_EXP, + II_EXTERNAL_CALL, + II_SCOPE, + II_CORRECT_STACK, + II_CREATE_OBJECT, + II_POP_EMPTY, + II_PUSH_VAR, + II_PUSH_VAR_REF, + II_POP_VAR, + II_PUSH_VAR_THIS, // push current this on stack + II_PUSH_INT, + II_PUSH_BOOL, + II_PUSH_FLOAT, + II_PUSH_STRING, + II_PUSH_NULL, + II_PUSH_THIS_FROM_STACK, + II_PUSH_THIS, + II_POP_THIS, + II_PUSH_BY_EXP, + II_POP_BY_EXP, + II_JMP, + II_JMP_FALSE, + II_ADD, + II_SUB, + II_MUL, + II_DIV, + II_MODULO, + II_NOT, + II_AND, + II_OR, + II_CMP_EQ, + II_CMP_NE, + II_CMP_L, + II_CMP_G, + II_CMP_LE, + II_CMP_GE, + II_CMP_STRICT_EQ, + II_CMP_STRICT_NE, + II_DBG_LINE, + II_POP_REG1, + II_PUSH_REG1, + II_DEF_CONST_VAR +} TInstruction; + +// external data types +typedef enum { + TYPE_VOID = 0, + TYPE_BOOL, + TYPE_LONG, + TYPE_BYTE, + TYPE_STRING, + TYPE_FLOAT, + TYPE_DOUBLE, + TYPE_MEMBUFFER +} TExternalType; + + +// call types +typedef enum { + CALL_STDCALL = 0, + CALL_CDECL, + CALL_THISCALL +} TCallType; + +// element types +typedef enum { + ELEMENT_STRING = 0 +} TElementType; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/scriptables/script.cpp b/engines/wintermute/base/scriptables/script.cpp new file mode 100644 index 0000000000..9bb7fedf38 --- /dev/null +++ b/engines/wintermute/base/scriptables/script.cpp @@ -0,0 +1,1467 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/scriptables/script_engine.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "common/memstream.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(ScScript, false) + +////////////////////////////////////////////////////////////////////////// +ScScript::ScScript(BaseGame *inGame, ScEngine *engine) : BaseClass(inGame) { + _buffer = NULL; + _bufferSize = _iP = 0; + _scriptStream = NULL; + _filename = NULL; + _currentLine = 0; + + _symbols = NULL; + _numSymbols = 0; + + _engine = engine; + + _globals = NULL; + + _scopeStack = NULL; + _callStack = NULL; + _thisStack = NULL; + _stack = NULL; + + _operand = NULL; + _reg1 = NULL; + + _functions = NULL; + _numFunctions = 0; + + _methods = NULL; + _numMethods = 0; + + _events = NULL; + _numEvents = 0; + + _externals = NULL; + _numExternals = 0; + + _state = SCRIPT_FINISHED; + _origState = SCRIPT_FINISHED; + + _waitObject = NULL; + _waitTime = 0; + _waitFrozen = false; + _waitScript = NULL; + + _timeSlice = 0; + + _thread = false; + _methodThread = false; + _threadEvent = NULL; + + _freezable = true; + _owner = NULL; + + _unbreakable = false; + _parentScript = NULL; + + _tracingMode = false; +} + + +////////////////////////////////////////////////////////////////////////// +ScScript::~ScScript() { + cleanup(); +} + +void ScScript::readHeader() { + uint32 oldPos = _scriptStream->pos(); + _scriptStream->seek(0); + _header.magic = _scriptStream->readUint32LE(); + _header.version = _scriptStream->readUint32LE(); + _header.codeStart = _scriptStream->readUint32LE(); + _header.funcTable = _scriptStream->readUint32LE(); + _header.symbolTable = _scriptStream->readUint32LE(); + _header.eventTable = _scriptStream->readUint32LE(); + _header.externalsTable = _scriptStream->readUint32LE(); + _header.methodTable = _scriptStream->readUint32LE(); + _scriptStream->seek(oldPos); +} + + +////////////////////////////////////////////////////////////////////////// +bool ScScript::initScript() { + if (!_scriptStream) { + _scriptStream = new Common::MemoryReadStream(_buffer, _bufferSize); + } + readHeader(); + + if (_header.magic != SCRIPT_MAGIC) { + _gameRef->LOG(0, "File '%s' is not a valid compiled script", _filename); + cleanup(); + return STATUS_FAILED; + } + + if (_header.version > SCRIPT_VERSION) { + _gameRef->LOG(0, "Script '%s' has a wrong version %d.%d (expected %d.%d)", _filename, _header.version / 256, _header.version % 256, SCRIPT_VERSION / 256, SCRIPT_VERSION % 256); + cleanup(); + return STATUS_FAILED; + } + + initTables(); + + // init stacks + _scopeStack = new ScStack(_gameRef); + _callStack = new ScStack(_gameRef); + _thisStack = new ScStack(_gameRef); + _stack = new ScStack(_gameRef); + + _operand = new ScValue(_gameRef); + _reg1 = new ScValue(_gameRef); + + + // skip to the beginning + _iP = _header.codeStart; + _scriptStream->seek(_iP); + _currentLine = 0; + + // ready to rumble... + _state = SCRIPT_RUNNING; + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScScript::initTables() { + uint32 origIP = _iP; + + readHeader(); + // load symbol table + _iP = _header.symbolTable; + + _numSymbols = getDWORD(); + _symbols = new char*[_numSymbols]; + for (uint32 i = 0; i < _numSymbols; i++) { + uint32 index = getDWORD(); + _symbols[index] = getString(); + } + + // load functions table + _iP = _header.funcTable; + + _numFunctions = getDWORD(); + _functions = new TFunctionPos[_numFunctions]; + for (uint32 i = 0; i < _numFunctions; i++) { + _functions[i].pos = getDWORD(); + _functions[i].name = getString(); + } + + + // load events table + _iP = _header.eventTable; + + _numEvents = getDWORD(); + _events = new TEventPos[_numEvents]; + for (uint32 i = 0; i < _numEvents; i++) { + _events[i].pos = getDWORD(); + _events[i].name = getString(); + } + + + // load externals + if (_header.version >= 0x0101) { + _iP = _header.externalsTable; + + _numExternals = getDWORD(); + _externals = new TExternalFunction[_numExternals]; + for (uint32 i = 0; i < _numExternals; i++) { + _externals[i].dll_name = getString(); + _externals[i].name = getString(); + _externals[i].call_type = (TCallType)getDWORD(); + _externals[i].returns = (TExternalType)getDWORD(); + _externals[i].nu_params = getDWORD(); + if (_externals[i].nu_params > 0) { + _externals[i].params = new TExternalType[_externals[i].nu_params]; + for (int j = 0; j < _externals[i].nu_params; j++) { + _externals[i].params[j] = (TExternalType)getDWORD(); + } + } + } + } + + // load method table + _iP = _header.methodTable; + + _numMethods = getDWORD(); + _methods = new TMethodPos[_numMethods]; + for (uint32 i = 0; i < _numMethods; i++) { + _methods[i].pos = getDWORD(); + _methods[i].name = getString(); + } + + + _iP = origIP; + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScScript::create(const char *filename, byte *buffer, uint32 size, BaseScriptHolder *owner) { + cleanup(); + + _thread = false; + _methodThread = false; + + delete[] _threadEvent; + _threadEvent = NULL; + + _filename = new char[strlen(filename) + 1]; + if (_filename) { + strcpy(_filename, filename); + } + + _buffer = new byte [size]; + if (!_buffer) { + return STATUS_FAILED; + } + + memcpy(_buffer, buffer, size); + + _bufferSize = size; + + bool res = initScript(); + if (DID_FAIL(res)) { + return res; + } + + // establish global variables table + _globals = new ScValue(_gameRef); + + _owner = owner; + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScScript::createThread(ScScript *original, uint32 initIP, const Common::String &eventName) { + cleanup(); + + _thread = true; + _methodThread = false; + _threadEvent = new char[eventName.size() + 1]; + if (_threadEvent) { + strcpy(_threadEvent, eventName.c_str()); + } + + // copy filename + _filename = new char[strlen(original->_filename) + 1]; + if (_filename) { + strcpy(_filename, original->_filename); + } + + // copy buffer + _buffer = new byte [original->_bufferSize]; + if (!_buffer) { + return STATUS_FAILED; + } + + memcpy(_buffer, original->_buffer, original->_bufferSize); + _bufferSize = original->_bufferSize; + + // initialize + bool res = initScript(); + if (DID_FAIL(res)) { + return res; + } + + // copy globals + _globals = original->_globals; + + // skip to the beginning of the event + _iP = initIP; + _scriptStream->seek(_iP); + + _timeSlice = original->_timeSlice; + _freezable = original->_freezable; + _owner = original->_owner; + + _engine = original->_engine; + _parentScript = original; + + return STATUS_OK; +} + + + + +////////////////////////////////////////////////////////////////////////// +bool ScScript::createMethodThread(ScScript *original, const Common::String &methodName) { + uint32 ip = original->getMethodPos(methodName); + if (ip == 0) { + return STATUS_FAILED; + } + + cleanup(); + + _thread = true; + _methodThread = true; + _threadEvent = new char[methodName.size() + 1]; + if (_threadEvent) { + strcpy(_threadEvent, methodName.c_str()); + } + + // copy filename + _filename = new char[strlen(original->_filename) + 1]; + if (_filename) { + strcpy(_filename, original->_filename); + } + + // copy buffer + _buffer = new byte [original->_bufferSize]; + if (!_buffer) { + return STATUS_FAILED; + } + + memcpy(_buffer, original->_buffer, original->_bufferSize); + _bufferSize = original->_bufferSize; + + // initialize + bool res = initScript(); + if (DID_FAIL(res)) { + return res; + } + + // copy globals + _globals = original->_globals; + + // skip to the beginning of the event + _iP = ip; + + _timeSlice = original->_timeSlice; + _freezable = original->_freezable; + _owner = original->_owner; + + _engine = original->_engine; + _parentScript = original; + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +void ScScript::cleanup() { + if (_buffer) { + delete[] _buffer; + } + _buffer = NULL; + + if (_filename) { + delete[] _filename; + } + _filename = NULL; + + if (_symbols) { + delete[] _symbols; + } + _symbols = NULL; + _numSymbols = 0; + + if (_globals && !_thread) { + delete _globals; + } + _globals = NULL; + + delete _scopeStack; + _scopeStack = NULL; + + delete _callStack; + _callStack = NULL; + + delete _thisStack; + _thisStack = NULL; + + delete _stack; + _stack = NULL; + + if (_functions) { + delete[] _functions; + } + _functions = NULL; + _numFunctions = 0; + + if (_methods) { + delete[] _methods; + } + _methods = NULL; + _numMethods = 0; + + if (_events) { + delete[] _events; + } + _events = NULL; + _numEvents = 0; + + + if (_externals) { + for (uint32 i = 0; i < _numExternals; i++) { + if (_externals[i].nu_params > 0) { + delete[] _externals[i].params; + } + } + delete[] _externals; + } + _externals = NULL; + _numExternals = 0; + + delete _operand; + delete _reg1; + _operand = NULL; + _reg1 = NULL; + + delete[] _threadEvent; + _threadEvent = NULL; + + _state = SCRIPT_FINISHED; + + _waitObject = NULL; + _waitTime = 0; + _waitFrozen = false; + _waitScript = NULL; + + _parentScript = NULL; // ref only + + delete _scriptStream; +} + + +////////////////////////////////////////////////////////////////////////// +uint32 ScScript::getDWORD() { + _scriptStream->seek((int32)_iP); + uint32 ret = _scriptStream->readUint32LE(); + _iP += sizeof(uint32); +// assert(oldRet == ret); + return ret; +} + +////////////////////////////////////////////////////////////////////////// +double ScScript::getFloat() { + _scriptStream->seek((int32)_iP); + byte buffer[8]; + _scriptStream->read(buffer, 8); + +#ifdef SCUMM_BIG_ENDIAN + // TODO: For lack of a READ_LE_UINT64 + SWAP(buffer[0], buffer[7]); + SWAP(buffer[1], buffer[6]); + SWAP(buffer[2], buffer[5]); + SWAP(buffer[3], buffer[4]); +#endif + + double ret = *(double *)(buffer); + _iP += 8; // Hardcode the double-size used originally. + return ret; +} + + +////////////////////////////////////////////////////////////////////////// +char *ScScript::getString() { + char *ret = (char *)(_buffer + _iP); + while (*(char *)(_buffer + _iP) != '\0') { + _iP++; + } + _iP++; // string terminator + _scriptStream->seek(_iP); + + return ret; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScScript::executeInstruction() { + bool ret = STATUS_OK; + + uint32 dw; + const char *str = NULL; + + //ScValue* op = new ScValue(_gameRef); + _operand->cleanup(); + + ScValue *op1; + ScValue *op2; + + uint32 inst = getDWORD(); + switch (inst) { + + case II_DEF_VAR: + _operand->setNULL(); + dw = getDWORD(); + if (_scopeStack->_sP < 0) { + _globals->setProp(_symbols[dw], _operand); + } else { + _scopeStack->getTop()->setProp(_symbols[dw], _operand); + } + + break; + + case II_DEF_GLOB_VAR: + case II_DEF_CONST_VAR: { + dw = getDWORD(); + /* char *temp = _symbols[dw]; // TODO delete */ + // only create global var if it doesn't exist + if (!_engine->_globals->propExists(_symbols[dw])) { + _operand->setNULL(); + _engine->_globals->setProp(_symbols[dw], _operand, false, inst == II_DEF_CONST_VAR); + } + break; + } + + case II_RET: + if (_scopeStack->_sP >= 0 && _callStack->_sP >= 0) { + _scopeStack->pop(); + _iP = (uint32)_callStack->pop()->getInt(); + } else { + if (_thread) { + _state = SCRIPT_THREAD_FINISHED; + } else { + if (_numEvents == 0 && _numMethods == 0) { + _state = SCRIPT_FINISHED; + } else { + _state = SCRIPT_PERSISTENT; + } + } + } + + break; + + case II_RET_EVENT: + _state = SCRIPT_FINISHED; + break; + + + case II_CALL: + dw = getDWORD(); + + _operand->setInt(_iP); + _callStack->push(_operand); + + _iP = dw; + + break; + + case II_CALL_BY_EXP: { + // push var + // push string + str = _stack->pop()->getString(); + char *methodName = new char[strlen(str) + 1]; + strcpy(methodName, str); + + ScValue *var = _stack->pop(); + if (var->_type == VAL_VARIABLE_REF) { + var = var->_valRef; + } + + bool res = STATUS_FAILED; + bool triedNative = false; + + // we are already calling this method, try native + if (_thread && _methodThread && strcmp(methodName, _threadEvent) == 0 && var->_type == VAL_NATIVE && _owner == var->getNative()) { + triedNative = true; + res = var->_valNative->scCallMethod(this, _stack, _thisStack, methodName); + } + + if (DID_FAIL(res)) { + if (var->isNative() && var->getNative()->canHandleMethod(methodName)) { + if (!_unbreakable) { + _waitScript = var->getNative()->invokeMethodThread(methodName); + if (!_waitScript) { + _stack->correctParams(0); + runtimeError("Error invoking method '%s'.", methodName); + _stack->pushNULL(); + } else { + _state = SCRIPT_WAITING_SCRIPT; + _waitScript->copyParameters(_stack); + } + } else { + // can call methods in unbreakable mode + _stack->correctParams(0); + runtimeError("Cannot call method '%s'. Ignored.", methodName); + _stack->pushNULL(); + } + delete[] methodName; + break; + } + /* + ScValue* val = var->getProp(MethodName); + if (val) { + dw = GetFuncPos(val->getString()); + if (dw==0) { + TExternalFunction* f = GetExternal(val->getString()); + if (f) { + ExternalCall(_stack, _thisStack, f); + } + else{ + // not an internal nor external, try for native function + _gameRef->ExternalCall(this, _stack, _thisStack, val->getString()); + } + } + else{ + _operand->setInt(_iP); + _callStack->Push(_operand); + _iP = dw; + } + } + */ + else { + res = STATUS_FAILED; + if (var->_type == VAL_NATIVE && !triedNative) { + res = var->_valNative->scCallMethod(this, _stack, _thisStack, methodName); + } + + if (DID_FAIL(res)) { + _stack->correctParams(0); + runtimeError("Call to undefined method '%s'. Ignored.", methodName); + _stack->pushNULL(); + } + } + } + delete[] methodName; + } + break; + + case II_EXTERNAL_CALL: { + uint32 symbolIndex = getDWORD(); + + TExternalFunction *f = getExternal(_symbols[symbolIndex]); + if (f) { + externalCall(_stack, _thisStack, f); + } else { + _gameRef->externalCall(this, _stack, _thisStack, _symbols[symbolIndex]); + } + + break; + } + case II_SCOPE: + _operand->setNULL(); + _scopeStack->push(_operand); + break; + + case II_CORRECT_STACK: + dw = getDWORD(); // params expected + _stack->correctParams(dw); + break; + + case II_CREATE_OBJECT: + _operand->setObject(); + _stack->push(_operand); + break; + + case II_POP_EMPTY: + _stack->pop(); + break; + + case II_PUSH_VAR: { + ScValue *var = getVar(_symbols[getDWORD()]); + if (false && /*var->_type==VAL_OBJECT ||*/ var->_type == VAL_NATIVE) { + _operand->setReference(var); + _stack->push(_operand); + } else { + _stack->push(var); + } + break; + } + + case II_PUSH_VAR_REF: { + ScValue *var = getVar(_symbols[getDWORD()]); + _operand->setReference(var); + _stack->push(_operand); + break; + } + + case II_POP_VAR: { + char *varName = _symbols[getDWORD()]; + ScValue *var = getVar(varName); + if (var) { + ScValue *val = _stack->pop(); + if (!val) { + runtimeError("Script stack corruption detected. Please report this script at WME bug reports forum."); + var->setNULL(); + } else { + if (val->getType() == VAL_VARIABLE_REF) { + val = val->_valRef; + } + if (val->_type == VAL_NATIVE) { + var->setValue(val); + } else { + var->copy(val); + } + } + } + + break; + } + + case II_PUSH_VAR_THIS: + _stack->push(_thisStack->getTop()); + break; + + case II_PUSH_INT: + _stack->pushInt((int)getDWORD()); + break; + + case II_PUSH_FLOAT: + _stack->pushFloat(getFloat()); + break; + + + case II_PUSH_BOOL: + _stack->pushBool(getDWORD() != 0); + + break; + + case II_PUSH_STRING: + _stack->pushString(getString()); + break; + + case II_PUSH_NULL: + _stack->pushNULL(); + break; + + case II_PUSH_THIS_FROM_STACK: + _operand->setReference(_stack->getTop()); + _thisStack->push(_operand); + break; + + case II_PUSH_THIS: + _operand->setReference(getVar(_symbols[getDWORD()])); + _thisStack->push(_operand); + break; + + case II_POP_THIS: + _thisStack->pop(); + break; + + case II_PUSH_BY_EXP: { + str = _stack->pop()->getString(); + ScValue *val = _stack->pop()->getProp(str); + if (val) { + _stack->push(val); + } else { + _stack->pushNULL(); + } + + break; + } + + case II_POP_BY_EXP: { + str = _stack->pop()->getString(); + ScValue *var = _stack->pop(); + ScValue *val = _stack->pop(); + + if (val == NULL) { + runtimeError("Script stack corruption detected. Please report this script at WME bug reports forum."); + var->setNULL(); + } else { + var->setProp(str, val); + } + + break; + } + + case II_PUSH_REG1: + _stack->push(_reg1); + break; + + case II_POP_REG1: + _reg1->copy(_stack->pop()); + break; + + case II_JMP: + _iP = getDWORD(); + break; + + case II_JMP_FALSE: { + dw = getDWORD(); + //if (!_stack->pop()->getBool()) _iP = dw; + ScValue *val = _stack->pop(); + if (!val) { + runtimeError("Script corruption detected. Did you use '=' instead of '==' for comparison?"); + } else { + if (!val->getBool()) { + _iP = dw; + } + } + break; + } + + case II_ADD: + op2 = _stack->pop(); + op1 = _stack->pop(); + + if (op1->isNULL() || op2->isNULL()) { + _operand->setNULL(); + } else if (op1->getType() == VAL_STRING || op2->getType() == VAL_STRING) { + char *tempStr = new char [strlen(op1->getString()) + strlen(op2->getString()) + 1]; + strcpy(tempStr, op1->getString()); + strcat(tempStr, op2->getString()); + _operand->setString(tempStr); + delete[] tempStr; + } else if (op1->getType() == VAL_INT && op2->getType() == VAL_INT) { + _operand->setInt(op1->getInt() + op2->getInt()); + } else { + _operand->setFloat(op1->getFloat() + op2->getFloat()); + } + + _stack->push(_operand); + + break; + + case II_SUB: + op2 = _stack->pop(); + op1 = _stack->pop(); + + if (op1->isNULL() || op2->isNULL()) { + _operand->setNULL(); + } else if (op1->getType() == VAL_INT && op2->getType() == VAL_INT) { + _operand->setInt(op1->getInt() - op2->getInt()); + } else { + _operand->setFloat(op1->getFloat() - op2->getFloat()); + } + + _stack->push(_operand); + + break; + + case II_MUL: + op2 = _stack->pop(); + op1 = _stack->pop(); + + if (op1->isNULL() || op2->isNULL()) { + _operand->setNULL(); + } else if (op1->getType() == VAL_INT && op2->getType() == VAL_INT) { + _operand->setInt(op1->getInt() * op2->getInt()); + } else { + _operand->setFloat(op1->getFloat() * op2->getFloat()); + } + + _stack->push(_operand); + + break; + + case II_DIV: + op2 = _stack->pop(); + op1 = _stack->pop(); + + if (op2->getFloat() == 0.0f) { + runtimeError("Division by zero."); + } + + if (op1->isNULL() || op2->isNULL() || op2->getFloat() == 0.0f) { + _operand->setNULL(); + } else { + _operand->setFloat(op1->getFloat() / op2->getFloat()); + } + + _stack->push(_operand); + + break; + + case II_MODULO: + op2 = _stack->pop(); + op1 = _stack->pop(); + + if (op2->getInt() == 0) { + runtimeError("Division by zero."); + } + + if (op1->isNULL() || op2->isNULL() || op2->getInt() == 0) { + _operand->setNULL(); + } else { + _operand->setInt(op1->getInt() % op2->getInt()); + } + + _stack->push(_operand); + + break; + + case II_NOT: + op1 = _stack->pop(); + //if (op1->isNULL()) _operand->setNULL(); + if (op1->isNULL()) { + _operand->setBool(true); + } else { + _operand->setBool(!op1->getBool()); + } + _stack->push(_operand); + + break; + + case II_AND: + op2 = _stack->pop(); + op1 = _stack->pop(); + if (op1 == NULL || op2 == NULL) { + runtimeError("Script corruption detected. Did you use '=' instead of '==' for comparison?"); + _operand->setBool(false); + } else { + _operand->setBool(op1->getBool() && op2->getBool()); + } + _stack->push(_operand); + break; + + case II_OR: + op2 = _stack->pop(); + op1 = _stack->pop(); + if (op1 == NULL || op2 == NULL) { + runtimeError("Script corruption detected. Did you use '=' instead of '==' for comparison?"); + _operand->setBool(false); + } else { + _operand->setBool(op1->getBool() || op2->getBool()); + } + _stack->push(_operand); + break; + + case II_CMP_EQ: + op2 = _stack->pop(); + op1 = _stack->pop(); + + /* + if ((op1->isNULL() && !op2->isNULL()) || (!op1->isNULL() && op2->isNULL())) _operand->setBool(false); + else if (op1->isNative() && op2->isNative()) { + _operand->setBool(op1->getNative() == op2->getNative()); + } + else if (op1->getType()==VAL_STRING || op2->getType()==VAL_STRING) { + _operand->setBool(scumm_stricmp(op1->getString(), op2->getString())==0); + } + else if (op1->getType()==VAL_FLOAT && op2->getType()==VAL_FLOAT) { + _operand->setBool(op1->getFloat() == op2->getFloat()); + } + else{ + _operand->setBool(op1->getInt() == op2->getInt()); + } + */ + + _operand->setBool(ScValue::compare(op1, op2) == 0); + _stack->push(_operand); + break; + + case II_CMP_NE: + op2 = _stack->pop(); + op1 = _stack->pop(); + + /* + if ((op1->isNULL() && !op2->isNULL()) || (!op1->isNULL() && op2->isNULL())) _operand->setBool(true); + else if (op1->isNative() && op2->isNative()) { + _operand->setBool(op1->getNative() != op2->getNative()); + } + else if (op1->getType()==VAL_STRING || op2->getType()==VAL_STRING) { + _operand->setBool(scumm_stricmp(op1->getString(), op2->getString())!=0); + } + else if (op1->getType()==VAL_FLOAT && op2->getType()==VAL_FLOAT) { + _operand->setBool(op1->getFloat() != op2->getFloat()); + } + else{ + _operand->setBool(op1->getInt() != op2->getInt()); + } + */ + + _operand->setBool(ScValue::compare(op1, op2) != 0); + _stack->push(_operand); + break; + + case II_CMP_L: + op2 = _stack->pop(); + op1 = _stack->pop(); + + /* + if (op1->getType()==VAL_FLOAT && op2->getType()==VAL_FLOAT) { + _operand->setBool(op1->getFloat() < op2->getFloat()); + } + else _operand->setBool(op1->getInt() < op2->getInt()); + */ + + _operand->setBool(ScValue::compare(op1, op2) < 0); + _stack->push(_operand); + break; + + case II_CMP_G: + op2 = _stack->pop(); + op1 = _stack->pop(); + + /* + if (op1->getType()==VAL_FLOAT && op2->getType()==VAL_FLOAT) { + _operand->setBool(op1->getFloat() > op2->getFloat()); + } + else _operand->setBool(op1->getInt() > op2->getInt()); + */ + + _operand->setBool(ScValue::compare(op1, op2) > 0); + _stack->push(_operand); + break; + + case II_CMP_LE: + op2 = _stack->pop(); + op1 = _stack->pop(); + + /* + if (op1->getType()==VAL_FLOAT && op2->getType()==VAL_FLOAT) { + _operand->setBool(op1->getFloat() <= op2->getFloat()); + } + else _operand->setBool(op1->getInt() <= op2->getInt()); + */ + + _operand->setBool(ScValue::compare(op1, op2) <= 0); + _stack->push(_operand); + break; + + case II_CMP_GE: + op2 = _stack->pop(); + op1 = _stack->pop(); + + /* + if (op1->getType()==VAL_FLOAT && op2->getType()==VAL_FLOAT) { + _operand->setBool(op1->getFloat() >= op2->getFloat()); + } + else _operand->setBool(op1->getInt() >= op2->getInt()); + */ + + _operand->setBool(ScValue::compare(op1, op2) >= 0); + _stack->push(_operand); + break; + + case II_CMP_STRICT_EQ: + op2 = _stack->pop(); + op1 = _stack->pop(); + + //_operand->setBool(op1->getType()==op2->getType() && op1->getFloat()==op2->getFloat()); + _operand->setBool(ScValue::compareStrict(op1, op2) == 0); + _stack->push(_operand); + + break; + + case II_CMP_STRICT_NE: + op2 = _stack->pop(); + op1 = _stack->pop(); + + //_operand->setBool(op1->getType()!=op2->getType() || op1->getFloat()!=op2->getFloat()); + _operand->setBool(ScValue::compareStrict(op1, op2) != 0); + _stack->push(_operand); + break; + + case II_DBG_LINE: { + int newLine = getDWORD(); + if (newLine != _currentLine) { + _currentLine = newLine; + } + break; + + } + default: + _gameRef->LOG(0, "Fatal: Invalid instruction %d ('%s', line %d, IP:0x%x)\n", inst, _filename, _currentLine, _iP - sizeof(uint32)); + _state = SCRIPT_FINISHED; + ret = STATUS_FAILED; + } // switch(instruction) + + //delete op; + + return ret; +} + + +////////////////////////////////////////////////////////////////////////// +uint32 ScScript::getFuncPos(const Common::String &name) { + for (uint32 i = 0; i < _numFunctions; i++) { + if (name == _functions[i].name) { + return _functions[i].pos; + } + } + return 0; +} + + +////////////////////////////////////////////////////////////////////////// +uint32 ScScript::getMethodPos(const Common::String &name) const { + for (uint32 i = 0; i < _numMethods; i++) { + if (name == _methods[i].name) { + return _methods[i].pos; + } + } + return 0; +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *ScScript::getVar(char *name) { + ScValue *ret = NULL; + + // scope locals + if (_scopeStack->_sP >= 0) { + if (_scopeStack->getTop()->propExists(name)) { + ret = _scopeStack->getTop()->getProp(name); + } + } + + // script globals + if (ret == NULL) { + if (_globals->propExists(name)) { + ret = _globals->getProp(name); + } + } + + // engine globals + if (ret == NULL) { + if (_engine->_globals->propExists(name)) { + ret = _engine->_globals->getProp(name); + } + } + + if (ret == NULL) { + //RuntimeError("Variable '%s' is inaccessible in the current block. Consider changing the script.", name); + _gameRef->LOG(0, "Warning: variable '%s' is inaccessible in the current block. Consider changing the script (script:%s, line:%d)", name, _filename, _currentLine); + ScValue *val = new ScValue(_gameRef); + ScValue *scope = _scopeStack->getTop(); + if (scope) { + scope->setProp(name, val); + ret = _scopeStack->getTop()->getProp(name); + } else { + _globals->setProp(name, val); + ret = _globals->getProp(name); + } + delete val; + } + + return ret; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScScript::waitFor(BaseObject *object) { + if (_unbreakable) { + runtimeError("Script cannot be interrupted."); + return STATUS_OK; + } + + _state = SCRIPT_WAITING; + _waitObject = object; + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScScript::waitForExclusive(BaseObject *object) { + _engine->resetObject(object); + return waitFor(object); +} + + +////////////////////////////////////////////////////////////////////////// +bool ScScript::sleep(uint32 duration) { + if (_unbreakable) { + runtimeError("Script cannot be interrupted."); + return STATUS_OK; + } + + _state = SCRIPT_SLEEPING; + if (_gameRef->_state == GAME_FROZEN) { + _waitTime = g_system->getMillis() + duration; + _waitFrozen = true; + } else { + _waitTime = _gameRef->_timer + duration; + _waitFrozen = false; + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScScript::finish(bool includingThreads) { + if (_state != SCRIPT_FINISHED && includingThreads) { + _state = SCRIPT_FINISHED; + finishThreads(); + } else { + _state = SCRIPT_FINISHED; + } + + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScScript::run() { + _state = SCRIPT_RUNNING; + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////// +void ScScript::runtimeError(const char *fmt, ...) { + char buff[256]; + va_list va; + + va_start(va, fmt); + vsprintf(buff, fmt, va); + va_end(va); + + _gameRef->LOG(0, "Runtime error. Script '%s', line %d", _filename, _currentLine); + _gameRef->LOG(0, " %s", buff); + + if (!_gameRef->_suppressScriptErrors) { + _gameRef->quickMessage("Script runtime error. View log for details."); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool ScScript::persist(BasePersistenceManager *persistMgr) { + + persistMgr->transfer(TMEMBER(_gameRef)); + + // buffer + if (persistMgr->getIsSaving()) { + if (_state != SCRIPT_PERSISTENT && _state != SCRIPT_FINISHED && _state != SCRIPT_THREAD_FINISHED) { + persistMgr->transfer(TMEMBER(_bufferSize)); + persistMgr->putBytes(_buffer, _bufferSize); + } else { + // don't save idle/finished scripts + int bufferSize = 0; + persistMgr->transfer(TMEMBER(bufferSize)); + } + } else { + persistMgr->transfer(TMEMBER(_bufferSize)); + if (_bufferSize > 0) { + _buffer = new byte[_bufferSize]; + persistMgr->getBytes(_buffer, _bufferSize); + _scriptStream = new Common::MemoryReadStream(_buffer, _bufferSize); + initTables(); + } else { + _buffer = NULL; + _scriptStream = NULL; + } + } + + persistMgr->transfer(TMEMBER(_callStack)); + persistMgr->transfer(TMEMBER(_currentLine)); + persistMgr->transfer(TMEMBER(_engine)); + persistMgr->transfer(TMEMBER(_filename)); + persistMgr->transfer(TMEMBER(_freezable)); + persistMgr->transfer(TMEMBER(_globals)); + persistMgr->transfer(TMEMBER(_iP)); + persistMgr->transfer(TMEMBER(_scopeStack)); + persistMgr->transfer(TMEMBER(_stack)); + persistMgr->transfer(TMEMBER_INT(_state)); + persistMgr->transfer(TMEMBER(_operand)); + persistMgr->transfer(TMEMBER_INT(_origState)); + persistMgr->transfer(TMEMBER(_owner)); + persistMgr->transfer(TMEMBER(_reg1)); + persistMgr->transfer(TMEMBER(_thread)); + persistMgr->transfer(TMEMBER(_threadEvent)); + persistMgr->transfer(TMEMBER(_thisStack)); + persistMgr->transfer(TMEMBER(_timeSlice)); + persistMgr->transfer(TMEMBER(_waitObject)); + persistMgr->transfer(TMEMBER(_waitScript)); + persistMgr->transfer(TMEMBER(_waitTime)); + persistMgr->transfer(TMEMBER(_waitFrozen)); + + persistMgr->transfer(TMEMBER(_methodThread)); + persistMgr->transfer(TMEMBER(_methodThread)); + persistMgr->transfer(TMEMBER(_unbreakable)); + persistMgr->transfer(TMEMBER(_parentScript)); + + if (!persistMgr->getIsSaving()) { + _tracingMode = false; + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +ScScript *ScScript::invokeEventHandler(const Common::String &eventName, bool unbreakable) { + //if (_state!=SCRIPT_PERSISTENT) return NULL; + + uint32 pos = getEventPos(eventName); + if (!pos) { + return NULL; + } + + ScScript *thread = new ScScript(_gameRef, _engine); + if (thread) { + bool ret = thread->createThread(this, pos, eventName); + if (DID_SUCCEED(ret)) { + thread->_unbreakable = unbreakable; + _engine->_scripts.add(thread); + return thread; + } else { + delete thread; + return NULL; + } + } else { + return NULL; + } + +} + + +////////////////////////////////////////////////////////////////////////// +uint32 ScScript::getEventPos(const Common::String &name) const { + for (int i = _numEvents - 1; i >= 0; i--) { + if (scumm_stricmp(name.c_str(), _events[i].name) == 0) { + return _events[i].pos; + } + } + return 0; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScScript::canHandleEvent(const Common::String &eventName) const { + return getEventPos(eventName) != 0; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScScript::canHandleMethod(const Common::String &methodName) const { + return getMethodPos(methodName) != 0; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScScript::pause() { + if (_state == SCRIPT_PAUSED) { + _gameRef->LOG(0, "Attempting to pause a paused script ('%s', line %d)", _filename, _currentLine); + return STATUS_FAILED; + } + + if (!_freezable || _state == SCRIPT_PERSISTENT) { + return STATUS_OK; + } + + _origState = _state; + _state = SCRIPT_PAUSED; + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScScript::resume() { + if (_state != SCRIPT_PAUSED) { + return STATUS_OK; + } + + _state = _origState; + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +ScScript::TExternalFunction *ScScript::getExternal(char *name) { + for (uint32 i = 0; i < _numExternals; i++) { + if (strcmp(name, _externals[i].name) == 0) { + return &_externals[i]; + } + } + return NULL; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScScript::externalCall(ScStack *stack, ScStack *thisStack, ScScript::TExternalFunction *function) { + + _gameRef->LOG(0, "External functions are not supported on this platform."); + stack->correctParams(0); + stack->pushNULL(); + return STATUS_FAILED; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScScript::copyParameters(ScStack *stack) { + int i; + int numParams = stack->pop()->getInt(); + for (i = numParams - 1; i >= 0; i--) { + _stack->push(stack->getAt(i)); + } + _stack->pushInt(numParams); + + for (i = 0; i < numParams; i++) { + stack->pop(); + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScScript::finishThreads() { + for (uint32 i = 0; i < _engine->_scripts.size(); i++) { + ScScript *scr = _engine->_scripts[i]; + if (scr->_thread && scr->_state != SCRIPT_FINISHED && scr->_owner == _owner && scumm_stricmp(scr->_filename, _filename) == 0) { + scr->finish(true); + } + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +// IWmeDebugScript interface implementation +int ScScript::dbgGetLine() { + return _currentLine; +} + +////////////////////////////////////////////////////////////////////////// +const char *ScScript::dbgGetFilename() { + return _filename; +} + +////////////////////////////////////////////////////////////////////////// +void ScScript::afterLoad() { + if (_buffer == NULL) { + byte *buffer = _engine->getCompiledScript(_filename, &_bufferSize); + if (!buffer) { + _gameRef->LOG(0, "Error reinitializing script '%s' after load. Script will be terminated.", _filename); + _state = SCRIPT_ERROR; + return; + } + + _buffer = new byte [_bufferSize]; + memcpy(_buffer, buffer, _bufferSize); + + delete _scriptStream; + _scriptStream = new Common::MemoryReadStream(_buffer, _bufferSize); + + initTables(); + } +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/scriptables/script.h b/engines/wintermute/base/scriptables/script.h new file mode 100644 index 0000000000..428eb9a17e --- /dev/null +++ b/engines/wintermute/base/scriptables/script.h @@ -0,0 +1,174 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_SCSCRIPT_H +#define WINTERMUTE_SCSCRIPT_H + + +#include "engines/wintermute/base/base.h" +#include "engines/wintermute/base/scriptables/dcscript.h" // Added by ClassView +#include "engines/wintermute/coll_templ.h" + +namespace Wintermute { +class BaseScriptHolder; +class BaseObject; +class ScEngine; +class ScStack; +class ScScript : public BaseClass { +public: + BaseArray<int> _breakpoints; + bool _tracingMode; + + ScScript *_parentScript; + bool _unbreakable; + bool finishThreads(); + bool copyParameters(ScStack *stack); + + void afterLoad(); +private: + ScValue *_operand; + ScValue *_reg1; +public: + bool _freezable; + bool resume(); + bool pause(); + bool canHandleEvent(const Common::String &eventName) const; + bool canHandleMethod(const Common::String &methodName) const; + bool createThread(ScScript *original, uint32 initIP, const Common::String &eventName); + bool createMethodThread(ScScript *original, const Common::String &methodName); + ScScript *invokeEventHandler(const Common::String &eventName, bool unbreakable = false); + uint32 _timeSlice; + DECLARE_PERSISTENT(ScScript, BaseClass) + void runtimeError(const char *fmt, ...); + bool run(); + bool finish(bool includingThreads = false); + bool sleep(uint32 duration); + bool waitForExclusive(BaseObject *object); + bool waitFor(BaseObject *object); + uint32 _waitTime; + bool _waitFrozen; + BaseObject *_waitObject; + ScScript *_waitScript; + TScriptState _state; + TScriptState _origState; + ScValue *getVar(char *name); + uint32 getFuncPos(const Common::String &name); + uint32 getEventPos(const Common::String &name) const; + uint32 getMethodPos(const Common::String &name) const; + typedef struct { + uint32 magic; + uint32 version; + uint32 codeStart; + uint32 funcTable; + uint32 symbolTable; + uint32 eventTable; + uint32 externalsTable; + uint32 methodTable; + } TScriptHeader; + + TScriptHeader _header; + + typedef struct { + char *name; + uint32 pos; + } TFunctionPos; + + typedef struct { + char *name; + uint32 pos; + } TMethodPos; + + typedef struct { + char *name; + uint32 pos; + } TEventPos; + + typedef struct { + char *name; + char *dll_name; + TCallType call_type; + TExternalType returns; + int nu_params; + TExternalType *params; + } TExternalFunction; + + + ScStack *_callStack; + ScStack *_thisStack; + ScStack *_scopeStack; + ScStack *_stack; + ScValue *_globals; + ScEngine *_engine; + int _currentLine; + bool executeInstruction(); + char *getString(); + uint32 getDWORD(); + double getFloat(); + void cleanup(); + bool create(const char *filename, byte *buffer, uint32 size, BaseScriptHolder *owner); + uint32 _iP; +private: + void readHeader(); + uint32 _bufferSize; + byte *_buffer; +public: + Common::SeekableReadStream *_scriptStream; + ScScript(BaseGame *inGame, ScEngine *engine); + virtual ~ScScript(); + char *_filename; + bool _thread; + bool _methodThread; + char *_threadEvent; + BaseScriptHolder *_owner; + ScScript::TExternalFunction *getExternal(char *name); + bool externalCall(ScStack *stack, ScStack *thisStack, ScScript::TExternalFunction *function); +private: + char **_symbols; + uint32 _numSymbols; + TFunctionPos *_functions; + TMethodPos *_methods; + TEventPos *_events; + uint32 _numExternals; + TExternalFunction *_externals; + uint32 _numFunctions; + uint32 _numMethods; + uint32 _numEvents; + + bool initScript(); + bool initTables(); + + +// IWmeDebugScript interface implementation +public: + virtual int dbgGetLine(); + virtual const char *dbgGetFilename(); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/scriptables/script_engine.cpp b/engines/wintermute/base/scriptables/script_engine.cpp new file mode 100644 index 0000000000..e8be0f0bd5 --- /dev/null +++ b/engines/wintermute/base/scriptables/script_engine.cpp @@ -0,0 +1,608 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/scriptables/script_engine.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/base/base_engine.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/utils/utils.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(ScEngine, true) + +#define COMPILER_DLL "dcscomp.dll" +////////////////////////////////////////////////////////////////////////// +ScEngine::ScEngine(BaseGame *inGame) : BaseClass(inGame) { + _gameRef->LOG(0, "Initializing scripting engine..."); + + if (_compilerAvailable) { + _gameRef->LOG(0, " Script compiler bound successfuly"); + } else { + _gameRef->LOG(0, " Script compiler is NOT available"); + } + + _globals = new ScValue(_gameRef); + + + // register 'Game' as global variable + if (!_globals->propExists("Game")) { + ScValue val(_gameRef); + val.setNative(_gameRef, true); + _globals->setProp("Game", &val); + } + + // register 'Math' as global variable + if (!_globals->propExists("Math")) { + ScValue val(_gameRef); + val.setNative(_gameRef->_mathClass, true); + _globals->setProp("Math", &val); + } + + // prepare script cache + for (int i = 0; i < MAX_CACHED_SCRIPTS; i++) { + _cachedScripts[i] = NULL; + } + + _currentScript = NULL; + + _isProfiling = false; + _profilingStartTime = 0; + + //EnableProfiling(); +} + + +////////////////////////////////////////////////////////////////////////// +ScEngine::~ScEngine() { + _gameRef->LOG(0, "Shutting down scripting engine"); + + disableProfiling(); + + cleanup(); +} + + +////////////////////////////////////////////////////////////////////////// +bool ScEngine::cleanup() { + for (uint32 i = 0; i < _scripts.size(); i++) { + if (!_scripts[i]->_thread && _scripts[i]->_owner) { + _scripts[i]->_owner->removeScript(_scripts[i]); + } + delete _scripts[i]; + _scripts.remove_at(i); + i--; + } + + _scripts.clear(); + + delete _globals; + _globals = NULL; + + emptyScriptCache(); + + _currentScript = NULL; // ref only + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +byte *ScEngine::loadFile(void *data, char *filename, uint32 *size) { + return BaseFileManager::getEngineInstance()->readWholeFile(filename, size); +} + + +////////////////////////////////////////////////////////////////////////// +void ScEngine::closeFile(void *data, byte *buffer) { + delete[] buffer; +} + + +////////////////////////////////////////////////////////////////////////// +void ScEngine::parseElement(void *data, int line, int type, void *elementData) { +} + + +////////////////////////////////////////////////////////////////////////// +ScScript *ScEngine::runScript(const char *filename, BaseScriptHolder *owner) { + byte *compBuffer; + uint32 compSize; + + // get script from cache + compBuffer = getCompiledScript(filename, &compSize); + if (!compBuffer) { + return NULL; + } + + // add new script + ScScript *script = new ScScript(_gameRef, this); + bool ret = script->create(filename, compBuffer, compSize, owner); + if (DID_FAIL(ret)) { + _gameRef->LOG(ret, "Error running script '%s'...", filename); + delete script; + return NULL; + } else { + // publish the "self" pseudo-variable + ScValue val(_gameRef); + if (owner) { + val.setNative(owner, true); + } else { + val.setNULL(); + } + + script->_globals->setProp("self", &val); + script->_globals->setProp("this", &val); + + _scripts.add(script); + + return script; + } +} + + +////////////////////////////////////////////////////////////////////////// +byte *ScEngine::getCompiledScript(const char *filename, uint32 *outSize, bool ignoreCache) { + // is script in cache? + if (!ignoreCache) { + for (int i = 0; i < MAX_CACHED_SCRIPTS; i++) { + if (_cachedScripts[i] && scumm_stricmp(_cachedScripts[i]->_filename.c_str(), filename) == 0) { + _cachedScripts[i]->_timestamp = g_system->getMillis(); + *outSize = _cachedScripts[i]->_size; + return _cachedScripts[i]->_buffer; + } + } + } + + // nope, load it + byte *compBuffer; + uint32 compSize; + + uint32 size; + + byte *buffer = BaseEngine::instance().getFileManager()->readWholeFile(filename, &size); + if (!buffer) { + _gameRef->LOG(0, "ScEngine::GetCompiledScript - error opening script '%s'", filename); + return NULL; + } + + // needs to be compiled? + if (FROM_LE_32(*(uint32 *)buffer) == SCRIPT_MAGIC) { + compBuffer = buffer; + compSize = size; + } else { + if (!_compilerAvailable) { + _gameRef->LOG(0, "ScEngine::GetCompiledScript - script '%s' needs to be compiled but compiler is not available", filename); + delete[] buffer; + return NULL; + } + // This code will never be called, since _compilerAvailable is const false. + // It's only here in the event someone would want to reinclude the compiler. + error("Script needs compilation, ScummVM does not contain a WME compiler"); + } + + byte *ret = NULL; + + // add script to cache + CScCachedScript *cachedScript = new CScCachedScript(filename, compBuffer, compSize); + if (cachedScript) { + int index = 0; + uint32 minTime = g_system->getMillis(); + for (int i = 0; i < MAX_CACHED_SCRIPTS; i++) { + if (_cachedScripts[i] == NULL) { + index = i; + break; + } else if (_cachedScripts[i]->_timestamp <= minTime) { + minTime = _cachedScripts[i]->_timestamp; + index = i; + } + } + + if (_cachedScripts[index] != NULL) { + delete _cachedScripts[index]; + } + _cachedScripts[index] = cachedScript; + + ret = cachedScript->_buffer; + *outSize = cachedScript->_size; + } + + + // cleanup + delete[] buffer; + + return ret; +} + + + +////////////////////////////////////////////////////////////////////////// +bool ScEngine::tick() { + if (_scripts.size() == 0) { + return STATUS_OK; + } + + + // resolve waiting scripts + for (uint32 i = 0; i < _scripts.size(); i++) { + + switch (_scripts[i]->_state) { + case SCRIPT_WAITING: { + /* + bool obj_found=false; + for(int j=0; j<_gameRef->_regObjects.size(); j++) + { + if (_gameRef->_regObjects[j] == _scripts[i]->_waitObject) + { + if (_gameRef->_regObjects[j]->IsReady()) _scripts[i]->Run(); + obj_found = true; + break; + } + } + if (!obj_found) _scripts[i]->finish(); // _waitObject no longer exists + */ + if (_gameRef->validObject(_scripts[i]->_waitObject)) { + if (_scripts[i]->_waitObject->isReady()) { + _scripts[i]->run(); + } + } else { + _scripts[i]->finish(); + } + break; + } + + case SCRIPT_SLEEPING: { + if (_scripts[i]->_waitFrozen) { + if (_scripts[i]->_waitTime <= g_system->getMillis()) { + _scripts[i]->run(); + } + } else { + if (_scripts[i]->_waitTime <= _gameRef->_timer) { + _scripts[i]->run(); + } + } + break; + } + + case SCRIPT_WAITING_SCRIPT: { + if (!isValidScript(_scripts[i]->_waitScript) || _scripts[i]->_waitScript->_state == SCRIPT_ERROR) { + // fake return value + _scripts[i]->_stack->pushNULL(); + _scripts[i]->_waitScript = NULL; + _scripts[i]->run(); + } else { + if (_scripts[i]->_waitScript->_state == SCRIPT_THREAD_FINISHED) { + // copy return value + _scripts[i]->_stack->push(_scripts[i]->_waitScript->_stack->pop()); + _scripts[i]->run(); + _scripts[i]->_waitScript->finish(); + _scripts[i]->_waitScript = NULL; + } + } + break; + } + default: + break; + } // switch + } // for each script + + + // execute scripts + for (uint32 i = 0; i < _scripts.size(); i++) { + + // skip paused scripts + if (_scripts[i]->_state == SCRIPT_PAUSED) { + continue; + } + + // time sliced script + if (_scripts[i]->_timeSlice > 0) { + uint32 startTime = g_system->getMillis(); + while (_scripts[i]->_state == SCRIPT_RUNNING && g_system->getMillis() - startTime < _scripts[i]->_timeSlice) { + _currentScript = _scripts[i]; + _scripts[i]->executeInstruction(); + } + if (_isProfiling && _scripts[i]->_filename) { + addScriptTime(_scripts[i]->_filename, g_system->getMillis() - startTime); + } + } + + // normal script + else { + uint32 startTime = 0; + bool isProfiling = _isProfiling; + if (isProfiling) { + startTime = g_system->getMillis(); + } + + while (_scripts[i]->_state == SCRIPT_RUNNING) { + _currentScript = _scripts[i]; + _scripts[i]->executeInstruction(); + } + if (isProfiling && _scripts[i]->_filename) { + addScriptTime(_scripts[i]->_filename, g_system->getMillis() - startTime); + } + } + _currentScript = NULL; + } + + removeFinishedScripts(); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScEngine::tickUnbreakable() { + // execute unbreakable scripts + for (uint32 i = 0; i < _scripts.size(); i++) { + if (!_scripts[i]->_unbreakable) { + continue; + } + + while (_scripts[i]->_state == SCRIPT_RUNNING) { + _currentScript = _scripts[i]; + _scripts[i]->executeInstruction(); + } + _scripts[i]->finish(); + _currentScript = NULL; + } + removeFinishedScripts(); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScEngine::removeFinishedScripts() { + // remove finished scripts + for (uint32 i = 0; i < _scripts.size(); i++) { + if (_scripts[i]->_state == SCRIPT_FINISHED || _scripts[i]->_state == SCRIPT_ERROR) { + if (!_scripts[i]->_thread && _scripts[i]->_owner) { + _scripts[i]->_owner->removeScript(_scripts[i]); + } + + delete _scripts[i]; + _scripts.remove_at(i); + i--; + } + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +int ScEngine::getNumScripts(int *running, int *waiting, int *persistent) { + int numRunning = 0, numWaiting = 0, numPersistent = 0, numTotal = 0; + + for (uint32 i = 0; i < _scripts.size(); i++) { + if (_scripts[i]->_state == SCRIPT_FINISHED) { + continue; + } + switch (_scripts[i]->_state) { + case SCRIPT_RUNNING: + case SCRIPT_SLEEPING: + case SCRIPT_PAUSED: + numRunning++; + break; + case SCRIPT_WAITING: + numWaiting++; + break; + case SCRIPT_PERSISTENT: + numPersistent++; + break; + default: + warning("ScEngine::GetNumScripts - unhandled enum"); + break; + } + numTotal++; + } + if (running) { + *running = numRunning; + } + if (waiting) { + *waiting = numWaiting; + } + if (persistent) { + *persistent = numPersistent; + } + + return numTotal; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScEngine::emptyScriptCache() { + for (int i = 0; i < MAX_CACHED_SCRIPTS; i++) { + if (_cachedScripts[i]) { + delete _cachedScripts[i]; + _cachedScripts[i] = NULL; + } + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScEngine::resetObject(BaseObject *Object) { + // terminate all scripts waiting for this object + for (uint32 i = 0; i < _scripts.size(); i++) { + if (_scripts[i]->_state == SCRIPT_WAITING && _scripts[i]->_waitObject == Object) { + if (!_gameRef->_compatKillMethodThreads) { + resetScript(_scripts[i]); + } + + bool isThread = _scripts[i]->_methodThread || _scripts[i]->_thread; + _scripts[i]->finish(!isThread); // 1.9b1 - top-level script kills its threads as well + } + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool ScEngine::resetScript(ScScript *script) { + // terminate all scripts waiting for this script + for (uint32 i = 0; i < _scripts.size(); i++) { + if (_scripts[i]->_state == SCRIPT_WAITING_SCRIPT && _scripts[i]->_waitScript == script) { + _scripts[i]->finish(); + } + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool ScEngine::persist(BasePersistenceManager *persistMgr) { + if (!persistMgr->getIsSaving()) { + cleanup(); + } + + persistMgr->transfer(TMEMBER(_gameRef)); + persistMgr->transfer(TMEMBER(_currentScript)); + persistMgr->transfer(TMEMBER(_globals)); + _scripts.persist(persistMgr); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +void ScEngine::editorCleanup() { + for (uint32 i = 0; i < _scripts.size(); i++) { + if (_scripts[i]->_owner == NULL && (_scripts[i]->_state == SCRIPT_FINISHED || _scripts[i]->_state == SCRIPT_ERROR)) { + delete _scripts[i]; + _scripts.remove_at(i); + i--; + } + } +} + + +////////////////////////////////////////////////////////////////////////// +bool ScEngine::pauseAll() { + for (uint32 i = 0; i < _scripts.size(); i++) { + if (_scripts[i] != _currentScript) { + _scripts[i]->pause(); + } + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScEngine::resumeAll() { + for (uint32 i = 0; i < _scripts.size(); i++) { + _scripts[i]->resume(); + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScEngine::isValidScript(ScScript *script) { + for (uint32 i = 0; i < _scripts.size(); i++) { + if (_scripts[i] == script) { + return true; + } + } + return false; +} + +////////////////////////////////////////////////////////////////////////// +bool ScEngine::clearGlobals(bool includingNatives) { + _globals->CleanProps(includingNatives); + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +void ScEngine::addScriptTime(const char *filename, uint32 time) { + if (!_isProfiling) { + return; + } + + AnsiString fileName = filename; + fileName.toLowercase(); + _scriptTimes[fileName] += time; +} + + +////////////////////////////////////////////////////////////////////////// +void ScEngine::enableProfiling() { + if (_isProfiling) { + return; + } + + // destroy old data, if any + _scriptTimes.clear(); + + _profilingStartTime = g_system->getMillis(); + _isProfiling = true; +} + + +////////////////////////////////////////////////////////////////////////// +void ScEngine::disableProfiling() { + if (!_isProfiling) { + return; + } + + dumpStats(); + _isProfiling = false; +} + + +////////////////////////////////////////////////////////////////////////// +void ScEngine::dumpStats() { + error("DumpStats not ported to ScummVM yet"); + /* uint32 totalTime = g_system->getMillis() - _profilingStartTime; + + typedef std::vector <std::pair<uint32, std::string> > TimeVector; + TimeVector times; + + ScriptTimes::iterator it; + for (it = _scriptTimes.begin(); it != _scriptTimes.end(); ++it) { + times.push_back(std::pair<uint32, std::string> (it->_value, it->_key)); + } + std::sort(times.begin(), times.end()); + + + TimeVector::reverse_iterator tit; + + _gameRef->LOG(0, "***** Script profiling information: *****"); + _gameRef->LOG(0, " %-40s %fs", "Total execution time", (float)totalTime / 1000); + + for (tit = times.rbegin(); tit != times.rend(); ++tit) { + _gameRef->LOG(0, " %-40s %fs (%f%%)", tit->second.c_str(), (float)tit->first / 1000, (float)tit->first / (float)totalTime * 100); + }*/ +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/scriptables/script_engine.h b/engines/wintermute/base/scriptables/script_engine.h new file mode 100644 index 0000000000..1a023326eb --- /dev/null +++ b/engines/wintermute/base/scriptables/script_engine.h @@ -0,0 +1,135 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_SCENGINE_H +#define WINTERMUTE_SCENGINE_H + +#include "engines/wintermute/persistent.h" +#include "engines/wintermute/coll_templ.h" +#include "engines/wintermute/base/base.h" + +namespace Wintermute { + +#define MAX_CACHED_SCRIPTS 20 +class ScScript; +class ScValue; +class BaseObject; +class BaseScriptHolder; +class ScEngine : public BaseClass { +public: + class CScCachedScript { + public: + CScCachedScript(const char *filename, byte *buffer, uint32 size) { + _timestamp = g_system->getMillis(); + _buffer = new byte[size]; + if (_buffer) { + memcpy(_buffer, buffer, size); + } + _size = size; + _filename = filename; + }; + + ~CScCachedScript() { + if (_buffer) { + delete[] _buffer; + } + }; + + uint32 _timestamp; + byte *_buffer; + uint32 _size; + Common::String _filename; + }; + + class CScBreakpoint { + public: + CScBreakpoint(const char *filename) { + _filename = filename; + } + + ~CScBreakpoint() { + _lines.clear(); + } + + Common::String _filename; + BaseArray<int> _lines; + }; + +public: + bool clearGlobals(bool includingNatives = false); + bool tickUnbreakable(); + bool removeFinishedScripts(); + bool isValidScript(ScScript *script); + + ScScript *_currentScript; + bool resumeAll(); + bool pauseAll(); + void editorCleanup(); + bool resetObject(BaseObject *Object); + bool resetScript(ScScript *script); + bool emptyScriptCache(); + byte *getCompiledScript(const char *filename, uint32 *outSize, bool ignoreCache = false); + DECLARE_PERSISTENT(ScEngine, BaseClass) + bool cleanup(); + int getNumScripts(int *running = NULL, int *waiting = NULL, int *persistent = NULL); + bool tick(); + ScValue *_globals; + ScScript *runScript(const char *filename, BaseScriptHolder *owner = NULL); + static const bool _compilerAvailable = false; + + ScEngine(BaseGame *inGame); + virtual ~ScEngine(); + static byte *loadFile(void *data, char *filename, uint32 *size); + static void closeFile(void *data, byte *buffer); + static void parseElement(void *data, int line, int type, void *elementData); + + BaseArray<ScScript *> _scripts; + + void enableProfiling(); + void disableProfiling(); + bool getIsProfiling() { + return _isProfiling; + } + + void addScriptTime(const char *filename, uint32 Time); + void dumpStats(); + +private: + + CScCachedScript *_cachedScripts[MAX_CACHED_SCRIPTS]; + bool _isProfiling; + uint32 _profilingStartTime; + + typedef Common::HashMap<Common::String, uint32> ScriptTimes; + ScriptTimes _scriptTimes; + +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/scriptables/script_ext_array.cpp b/engines/wintermute/base/scriptables/script_ext_array.cpp new file mode 100644 index 0000000000..613cbd0758 --- /dev/null +++ b/engines/wintermute/base/scriptables/script_ext_array.cpp @@ -0,0 +1,252 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/persistent.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/system/sys_instance.h" +#include "engines/wintermute/base/scriptables/script_ext_array.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(SXArray, false) + +BaseScriptable *makeSXArray(BaseGame *inGame, ScStack *stack) { + return new SXArray(inGame, stack); +} + +////////////////////////////////////////////////////////////////////////// +SXArray::SXArray(BaseGame *inGame, ScStack *stack) : BaseScriptable(inGame) { + _length = 0; + _values = new ScValue(_gameRef); + + int numParams = stack->pop()->getInt(0); + + if (numParams == 1) { + _length = stack->pop()->getInt(0); + } else if (numParams > 1) { + _length = numParams; + char paramName[20]; + for (int i = 0; i < numParams; i++) { + sprintf(paramName, "%d", i); + _values->setProp(paramName, stack->pop()); + } + } +} + +////////////////////////////////////////////////////////////////////////// +SXArray::SXArray(BaseGame *inGame) : BaseScriptable(inGame) { + _length = 0; + _values = new ScValue(_gameRef); +} + + +////////////////////////////////////////////////////////////////////////// +SXArray::~SXArray() { + delete _values; + _values = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +const char *SXArray::scToString() { + char dummy[32768]; + strcpy(dummy, ""); + char propName[20]; + for (int i = 0; i < _length; i++) { + sprintf(propName, "%d", i); + ScValue *val = _values->getProp(propName); + if (val) { + if (strlen(dummy) + strlen(val->getString()) < 32768) { + strcat(dummy, val->getString()); + } + } + + if (i < _length - 1 && strlen(dummy) + 1 < 32768) { + strcat(dummy, ","); + } + } + _strRep = dummy; + return _strRep.c_str(); +} + + +////////////////////////////////////////////////////////////////////////// +bool SXArray::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // Push + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Push") == 0) { + int numParams = stack->pop()->getInt(0); + char paramName[20]; + + for (int i = 0; i < numParams; i++) { + _length++; + sprintf(paramName, "%d", _length - 1); + _values->setProp(paramName, stack->pop(), true); + } + stack->pushInt(_length); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Pop + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Pop") == 0) { + + stack->correctParams(0); + + if (_length > 0) { + char paramName[20]; + sprintf(paramName, "%d", _length - 1); + stack->push(_values->getProp(paramName)); + _values->deleteProp(paramName); + _length--; + } else { + stack->pushNULL(); + } + + return STATUS_OK; + } else { + return STATUS_FAILED; + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *SXArray::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("array"); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Length + ////////////////////////////////////////////////////////////////////////// + else if (name == "Length") { + _scValue->setInt(_length); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // [number] + ////////////////////////////////////////////////////////////////////////// + else { + char paramName[20]; + if (validNumber(name.c_str(), paramName)) { // TODO: Change to Common::String + return _values->getProp(paramName); + } else { + return _scValue; + } + } +} + + +////////////////////////////////////////////////////////////////////////// +bool SXArray::scSetProperty(const char *name, ScValue *value) { + ////////////////////////////////////////////////////////////////////////// + // Length + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Length") == 0) { + int origLength = _length; + _length = MAX(value->getInt(0), 0); + + char propName[20]; + if (_length < origLength) { + for (int i = _length; i < origLength; i++) { + sprintf(propName, "%d", i); + _values->deleteProp(propName); + } + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // [number] + ////////////////////////////////////////////////////////////////////////// + else { + char paramName[20]; + if (validNumber(name, paramName)) { + int index = atoi(paramName); + if (index >= _length) { + _length = index + 1; + } + return _values->setProp(paramName, value); + } else { + return STATUS_FAILED; + } + } +} + + +////////////////////////////////////////////////////////////////////////// +bool SXArray::persist(BasePersistenceManager *persistMgr) { + BaseScriptable::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_length)); + persistMgr->transfer(TMEMBER(_values)); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool SXArray::validNumber(const char *origStr, char *outStr) { + bool isNumber = true; + for (uint32 i = 0; i < strlen(origStr); i++) { + if (!(origStr[i] >= '0' && origStr[i] <= '9')) { + isNumber = false; + break; + } + } + + if (isNumber) { + int index = atoi(origStr); + sprintf(outStr, "%d", index); + return true; + } else { + return false; + } +} + +////////////////////////////////////////////////////////////////////////// +bool SXArray::push(ScValue *val) { + char paramName[20]; + _length++; + sprintf(paramName, "%d", _length - 1); + _values->setProp(paramName, val, true); + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/scriptables/script_ext_array.h b/engines/wintermute/base/scriptables/script_ext_array.h new file mode 100644 index 0000000000..284c547a27 --- /dev/null +++ b/engines/wintermute/base/scriptables/script_ext_array.h @@ -0,0 +1,56 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_SXARRAY_H +#define WINTERMUTE_SXARRAY_H + +#include "engines/wintermute/base/base_scriptable.h" + +namespace Wintermute { + +class SXArray : public BaseScriptable { +public: + bool push(ScValue *val); + bool validNumber(const char *origStr, char *outStr); + DECLARE_PERSISTENT(SXArray, BaseScriptable) + SXArray(BaseGame *inGame, ScStack *stack); + SXArray(BaseGame *inGame); + virtual ~SXArray(); + ScValue *scGetProperty(const Common::String &name); + bool scSetProperty(const char *name, ScValue *value); + bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + const char *scToString(); +private: + int _length; + ScValue *_values; + Common::String _strRep; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/scriptables/script_ext_date.cpp b/engines/wintermute/base/scriptables/script_ext_date.cpp new file mode 100644 index 0000000000..53a1d36b81 --- /dev/null +++ b/engines/wintermute/base/scriptables/script_ext_date.cpp @@ -0,0 +1,293 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script_ext_date.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(SXDate, false) + +BaseScriptable *makeSXDate(BaseGame *inGame, ScStack *stack) { + return new SXDate(inGame, stack); +} + +////////////////////////////////////////////////////////////////////////// +SXDate::SXDate(BaseGame *inGame, ScStack *stack) : BaseScriptable(inGame) { + stack->correctParams(6); + + memset(&_tm, 0, sizeof(_tm)); + + ScValue *valYear = stack->pop(); + _tm.tm_year = valYear->getInt() - 1900; + _tm.tm_mon = stack->pop()->getInt() - 1; + _tm.tm_mday = stack->pop()->getInt(); + _tm.tm_hour = stack->pop()->getInt(); + _tm.tm_min = stack->pop()->getInt(); + _tm.tm_sec = stack->pop()->getInt(); + + if (valYear->isNULL()) { + g_system->getTimeAndDate(_tm); + } +} + + +////////////////////////////////////////////////////////////////////////// +SXDate::~SXDate() { + +} + +////////////////////////////////////////////////////////////////////////// +const char *SXDate::scToString() { + // TODO: Make this more stringy, and less ISO 8601-like + _strRep.format("%04d-%02d-%02d - %02d:%02d:%02d", _tm.tm_year, _tm.tm_mon, _tm.tm_mday, _tm.tm_hour, _tm.tm_min, _tm.tm_sec); + return _strRep.c_str(); + //return asctime(&_tm); +} + + +////////////////////////////////////////////////////////////////////////// +bool SXDate::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // GetYear + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "GetYear") == 0) { + stack->correctParams(0); + stack->pushInt(_tm.tm_year + 1900); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // GetMonth + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetMonth") == 0) { + stack->correctParams(0); + stack->pushInt(_tm.tm_mon + 1); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // GetDate + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetDate") == 0) { + stack->correctParams(0); + stack->pushInt(_tm.tm_mday); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // GetHours + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetHours") == 0) { + stack->correctParams(0); + stack->pushInt(_tm.tm_hour); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // GetMinutes + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetMinutes") == 0) { + stack->correctParams(0); + stack->pushInt(_tm.tm_min); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // GetSeconds + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetSeconds") == 0) { + stack->correctParams(0); + stack->pushInt(_tm.tm_sec); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // GetWeekday + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetWeekday") == 0) { + stack->correctParams(0); + stack->pushInt(_tm.tm_wday); + return STATUS_OK; + } + + + ////////////////////////////////////////////////////////////////////////// + // SetYear + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetYear") == 0) { + stack->correctParams(1); + _tm.tm_year = stack->pop()->getInt() - 1900; + stack->pushNULL(); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // SetMonth + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetMonth") == 0) { + stack->correctParams(1); + _tm.tm_mon = stack->pop()->getInt() - 1; + stack->pushNULL(); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // SetDate + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetDate") == 0) { + stack->correctParams(1); + _tm.tm_mday = stack->pop()->getInt(); + stack->pushNULL(); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // SetHours + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetHours") == 0) { + stack->correctParams(1); + _tm.tm_hour = stack->pop()->getInt(); + stack->pushNULL(); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // SetMinutes + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetMinutes") == 0) { + stack->correctParams(1); + _tm.tm_min = stack->pop()->getInt(); + stack->pushNULL(); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // SetSeconds + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetSeconds") == 0) { + stack->correctParams(1); + _tm.tm_sec = stack->pop()->getInt(); + stack->pushNULL(); + return STATUS_OK; + } + + + ////////////////////////////////////////////////////////////////////////// + // SetCurrentTime + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetCurrentTime") == 0) { + stack->correctParams(0); + g_system->getTimeAndDate(_tm); + stack->pushNULL(); + return STATUS_OK; + } else { + return STATUS_FAILED; + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *SXDate::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("date"); + return _scValue; + } else { + return _scValue; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool SXDate::scSetProperty(const char *name, ScValue *value) { + /* + ////////////////////////////////////////////////////////////////////////// + // Name + ////////////////////////////////////////////////////////////////////////// + if (name == "Name")==0) { + setName(value->getString()); + return STATUS_OK; + } + + else*/ return STATUS_FAILED; +} + + +////////////////////////////////////////////////////////////////////////// +bool SXDate::persist(BasePersistenceManager *persistMgr) { + + BaseScriptable::persist(persistMgr); + persistMgr->transfer(TMEMBER(_tm.tm_year)); + persistMgr->transfer(TMEMBER(_tm.tm_mon)); + persistMgr->transfer(TMEMBER(_tm.tm_mday)); + persistMgr->transfer(TMEMBER(_tm.tm_hour)); + persistMgr->transfer(TMEMBER(_tm.tm_min)); + persistMgr->transfer(TMEMBER(_tm.tm_sec)); + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +int SXDate::scCompare(BaseScriptable *Value) { + TimeDate time1 = _tm; + TimeDate time2 = ((SXDate *)Value)->_tm; + + if (time1.tm_year < time2.tm_year) { + return -1; + } else if (time1.tm_year == time2.tm_year) { + if (time1.tm_mon < time2.tm_mon) { + return -1; + } else if (time1.tm_mon == time2.tm_mon) { + if (time1.tm_mday < time2.tm_mday) { + return -1; + } else if (time1.tm_mday == time2.tm_mday) { + if (time1.tm_hour < time2.tm_hour) { + return -1; + } else if (time1.tm_hour == time2.tm_hour) { + if (time1.tm_min < time2.tm_min) { + return -1; + } else if (time1.tm_min == time2.tm_min) { + if (time1.tm_sec < time2.tm_sec) { + return -1; + } else if (time1.tm_sec == time2.tm_sec) { + return 0; // Equal + } else { + return 1; // Sec + } + } else { + return 1; // Minute + } + } else { + return 1; // Hour + } + } else { + return 1; // Day + } + } else { + return 1; // Month + } + } else { + return 1; // Year + } +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/scriptables/script_ext_date.h b/engines/wintermute/base/scriptables/script_ext_date.h new file mode 100644 index 0000000000..062b7c55c7 --- /dev/null +++ b/engines/wintermute/base/scriptables/script_ext_date.h @@ -0,0 +1,54 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_SXDATE_H +#define WINTERMUTE_SXDATE_H + +#include "common/system.h" +#include "engines/wintermute/base/base_scriptable.h" + +namespace Wintermute { + +class SXDate : public BaseScriptable { +public: + int scCompare(BaseScriptable *Value); + DECLARE_PERSISTENT(SXDate, BaseScriptable) + SXDate(BaseGame *inGame, ScStack *Stack); + virtual ~SXDate(); + ScValue *scGetProperty(const Common::String &name); + bool scSetProperty(const char *name, ScValue *value); + bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + const char *scToString(); +private: + TimeDate _tm; + Common::String _strRep; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/scriptables/script_ext_file.cpp b/engines/wintermute/base/scriptables/script_ext_file.cpp new file mode 100644 index 0000000000..08ecc8d7db --- /dev/null +++ b/engines/wintermute/base/scriptables/script_ext_file.cpp @@ -0,0 +1,828 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/system/sys_class_registry.h" +#include "engines/wintermute/system/sys_class.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/utils/utils.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/platform_osystem.h" +#include "engines/wintermute/base/scriptables/script_ext_file.h" + +// Note: This code is completely untested, as I have yet to find a game that uses SXFile. + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(SXFile, false) + +BaseScriptable *makeSXFile(BaseGame *inGame, ScStack *stack) { + return new SXFile(inGame, stack); +} + +////////////////////////////////////////////////////////////////////////// +SXFile::SXFile(BaseGame *inGame, ScStack *stack) : BaseScriptable(inGame) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + _filename = NULL; + if (!val->isNULL()) { + BaseUtils::setString(&_filename, val->getString()); + } + + _readFile = NULL; + _writeFile = NULL; + + _mode = 0; + _textMode = false; +} + + +////////////////////////////////////////////////////////////////////////// +SXFile::~SXFile() { + cleanup(); +} + +////////////////////////////////////////////////////////////////////////// +void SXFile::cleanup() { + delete[] _filename; + _filename = NULL; + close(); +} + + +////////////////////////////////////////////////////////////////////////// +void SXFile::close() { + if (_readFile) { + BaseFileManager::getEngineInstance()->closeFile(_readFile); + _readFile = NULL; + } + if (_writeFile) { + _writeFile->finalize(); + delete _writeFile; + _writeFile = NULL; + } + _mode = 0; + _textMode = false; +} + +////////////////////////////////////////////////////////////////////////// +const char *SXFile::scToString() { + if (_filename) { + return _filename; + } else { + return "[file object]"; + } +} + +#define FILE_BUFFER_SIZE 32768 +////////////////////////////////////////////////////////////////////////// +bool SXFile::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // SetFilename + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "SetFilename") == 0) { + stack->correctParams(1); + const char *filename = stack->pop()->getString(); + cleanup(); + BaseUtils::setString(&_filename, filename); + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // OpenAsText / OpenAsBinary + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "OpenAsText") == 0 || strcmp(name, "OpenAsBinary") == 0) { + stack->correctParams(1); + close(); + _mode = stack->pop()->getInt(1); + if (_mode < 1 || _mode > 3) { + script->runtimeError("File.%s: invalid access mode. Setting read mode.", name); + _mode = 1; + } + if (_mode == 1) { + _readFile = BaseFileManager::getEngineInstance()->openFile(_filename); + if (!_readFile) { + //script->runtimeError("File.%s: Error opening file '%s' for reading.", Name, _filename); + close(); + } else { + _textMode = strcmp(name, "OpenAsText") == 0; + } + } else { + if (strcmp(name, "OpenAsText") == 0) { + if (_mode == 2) { + _writeFile = openForWrite(_filename, false); + } else { + _writeFile = openForAppend(_filename, false); + } + } else { + if (_mode == 2) { + _writeFile = openForWrite(_filename, true); + } else { + _writeFile = openForAppend(_filename, true); + } + } + + if (!_writeFile) { + //script->runtimeError("File.%s: Error opening file '%s' for writing.", Name, _filename); + close(); + } else { + _textMode = strcmp(name, "OpenAsText") == 0; + } + } + + if (_readFile || _writeFile) { + stack->pushBool(true); + } else { + stack->pushBool(false); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Close + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Close") == 0) { + stack->correctParams(0); + close(); + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetPosition + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetPosition") == 0) { + stack->correctParams(1); + if (_mode == 0) { + script->runtimeError("File.%s: File is not open", name); + stack->pushBool(false); + } else { + int pos = stack->pop()->getInt(); + stack->pushBool(setPos(pos)); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Delete + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Delete") == 0) { + stack->correctParams(0); + close(); + error("SXFile-Method: \"Delete\" not supported"); + //stack->pushBool(BasePlatform::deleteFile(_filename) != false); + stack->pushBool(false); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Copy + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Copy") == 0) { + stack->correctParams(2); + /* const char *dest = */ stack->pop()->getString(); + /* bool overwrite = */ stack->pop()->getBool(true); + + close(); + error("SXFile-Method: Copy not supported"); + //stack->pushBool(BasePlatform::copyFile(_filename, Dest, !Overwrite) != false); + stack->pushBool(false); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ReadLine + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ReadLine") == 0) { + stack->correctParams(0); + if (!_textMode || !_readFile) { + script->runtimeError("File.%s: File must be open in text mode.", name); + stack->pushNULL(); + return STATUS_OK; + } + uint32 bufSize = FILE_BUFFER_SIZE; + byte *buf = (byte *)malloc(bufSize); + uint32 counter = 0; + byte b; + bool foundNewLine = false; + bool ret = STATUS_FAILED; + do { + ret = _readFile->read(&b, 1); + if (ret != 1) { + break; + } + + if (counter > bufSize) { + buf = (byte *)realloc(buf, bufSize + FILE_BUFFER_SIZE); + bufSize += FILE_BUFFER_SIZE; + } + if (b == '\n') { + buf[counter] = '\0'; + foundNewLine = true; + break; + } else if (b == 0x0D) { + continue; + } else { + buf[counter] = b; + counter++; + } + } while (DID_SUCCEED(ret)); + + if (counter > bufSize) { + buf = (byte *)realloc(buf, bufSize + FILE_BUFFER_SIZE); + bufSize += FILE_BUFFER_SIZE; + } + buf[counter] = '\0'; + + if (!foundNewLine && counter == 0) { + stack->pushNULL(); + } else { + stack->pushString((char *)buf); + } + + free(buf); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ReadText + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ReadText") == 0) { + stack->correctParams(1); + int textLen = stack->pop()->getInt(); + + if (!_textMode || !_readFile) { + script->runtimeError("File.%s: File must be open in text mode.", name); + stack->pushNULL(); + return STATUS_OK; + } + uint32 bufSize = FILE_BUFFER_SIZE; + byte *buf = (byte *)malloc(bufSize); + uint32 counter = 0; + byte b; + + bool ret = STATUS_FAILED; + while (counter < (uint32)textLen) { + ret = _readFile->read(&b, 1); + if (ret != 1) { + break; + } + + if (counter > bufSize) { + buf = (byte *)realloc(buf, bufSize + FILE_BUFFER_SIZE); + bufSize += FILE_BUFFER_SIZE; + } + if (b == 0x0D) { + continue; + } else { + buf[counter] = b; + counter++; + } + } + + if (counter > bufSize) { + buf = (byte *)realloc(buf, bufSize + FILE_BUFFER_SIZE); + bufSize += FILE_BUFFER_SIZE; + } + buf[counter] = '\0'; + + if (textLen > 0 && counter == 0) { + stack->pushNULL(); + } else { + stack->pushString((char *)buf); + } + + free(buf); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // WriteLine / WriteText + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "WriteLine") == 0 || strcmp(name, "WriteText") == 0) { + stack->correctParams(1); + const char *line = stack->pop()->getString(); + if (!_textMode || !_writeFile) { + script->runtimeError("File.%s: File must be open for writing in text mode.", name); + stack->pushBool(false); + return STATUS_OK; + } + Common::String writeLine; + if (strcmp(name, "WriteLine") == 0) { + writeLine = Common::String::format("%s\n", line); + } else { + writeLine = Common::String::format("%s", line); + } + _writeFile->writeString(writeLine); + _writeFile->writeByte(0); + stack->pushBool(true); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// + // ReadBool + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ReadBool") == 0) { + stack->correctParams(0); + if (_textMode || !_readFile) { + script->runtimeError("File.%s: File must be open for reading in binary mode.", name); + stack->pushNULL(); + return STATUS_OK; + } + bool val; + if (_readFile->read(&val, sizeof(bool)) == sizeof(bool)) { + stack->pushBool(val); + } else { + stack->pushNULL(); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ReadByte + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ReadByte") == 0) { + stack->correctParams(0); + if (_textMode || !_readFile) { + script->runtimeError("File.%s: File must be open for reading in binary mode.", name); + stack->pushNULL(); + return STATUS_OK; + } + byte val = _readFile->readByte(); + if (!_readFile->err()) { + stack->pushInt(val); + } else { + stack->pushNULL(); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ReadShort + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ReadShort") == 0) { + stack->correctParams(0); + if (_textMode || !_readFile) { + script->runtimeError("File.%s: File must be open for reading in binary mode.", name); + stack->pushNULL(); + return STATUS_OK; + } + int16 val = _readFile->readSint16LE(); + if (!_readFile->err()) { + stack->pushInt(65536 + val); + } else { + stack->pushNULL(); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ReadInt / ReadLong + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ReadInt") == 0 || strcmp(name, "ReadLong") == 0) { + stack->correctParams(0); + if (_textMode || !_readFile) { + script->runtimeError("File.%s: File must be open for reading in binary mode.", name); + stack->pushNULL(); + return STATUS_OK; + } + int32 val = _readFile->readSint32LE(); + if (!_readFile->err()) { + stack->pushInt(val); + } else { + stack->pushNULL(); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ReadFloat + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ReadFloat") == 0) { + stack->correctParams(0); + if (_textMode || !_readFile) { + script->runtimeError("File.%s: File must be open for reading in binary mode.", name); + stack->pushNULL(); + return STATUS_OK; + } + float val; + (*(uint32 *)&val) = _readFile->readUint32LE(); + if (!_readFile->err()) { + stack->pushFloat(val); + } else { + stack->pushNULL(); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ReadDouble + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ReadDouble") == 0) { // TODO: Solve reading a 8 byte double. + error("SXFile::ReadDouble - Not endian safe yet"); + stack->correctParams(0); + if (_textMode || !_readFile) { + script->runtimeError("File.%s: File must be open for reading in binary mode.", name); + stack->pushNULL(); + return STATUS_OK; + } + double val; + if (_readFile->read(&val, sizeof(double)) == sizeof(double)) { + stack->pushFloat(val); + } else { + stack->pushNULL(); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ReadString + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ReadString") == 0) { + stack->correctParams(0); + if (_textMode || !_readFile) { + script->runtimeError("File.%s: File must be open for reading in binary mode.", name); + stack->pushNULL(); + return STATUS_OK; + } + uint32 size = _readFile->readUint32LE(); + if (!_readFile->err()) { + byte *str = new byte[size + 1]; + if (str) { + if (_readFile->read(str, size) == size) { + str[size] = '\0'; + stack->pushString((char *)str); + } + delete[] str; + } else { + stack->pushNULL(); + } + } else { + stack->pushNULL(); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // WriteBool + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "WriteBool") == 0) { + stack->correctParams(1); + bool val = stack->pop()->getBool(); + + if (_textMode || !_writeFile) { + script->runtimeError("File.%s: File must be open for writing in binary mode.", name); + stack->pushBool(false); + return STATUS_OK; + } + _writeFile->writeByte(val); + stack->pushBool(true); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // WriteByte + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "WriteByte") == 0) { + stack->correctParams(1); + byte val = stack->pop()->getInt(); + + if (_textMode || !_writeFile) { + script->runtimeError("File.%s: File must be open for writing in binary mode.", name); + stack->pushBool(false); + return STATUS_OK; + } + _writeFile->writeByte(val); + stack->pushBool(true); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // WriteShort + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "WriteShort") == 0) { + stack->correctParams(1); + int16 val = stack->pop()->getInt(); + + if (_textMode || !_writeFile) { + script->runtimeError("File.%s: File must be open for writing in binary mode.", name); + stack->pushBool(false); + return STATUS_OK; + } + _writeFile->writeSint16LE(val); + stack->pushBool(true); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // WriteInt / WriteLong + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "WriteInt") == 0 || strcmp(name, "WriteLong") == 0) { + stack->correctParams(1); + int32 val = stack->pop()->getInt(); + + if (_textMode || !_writeFile) { + script->runtimeError("File.%s: File must be open for writing in binary mode.", name); + stack->pushBool(false); + return STATUS_OK; + } + _writeFile->writeSint32LE(val); + stack->pushBool(true); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // WriteFloat + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "WriteFloat") == 0) { + stack->correctParams(1); + float val = stack->pop()->getFloat(); + + if (_textMode || !_writeFile) { + script->runtimeError("File.%s: File must be open for writing in binary mode.", name); + stack->pushBool(false); + return STATUS_OK; + } + uint32 *ptr = (uint32 *)&val; + _writeFile->writeUint32LE(*ptr); + stack->pushBool(true); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // WriteDouble + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "WriteDouble") == 0) { + error("SXFile::WriteDouble - Not endian safe yet"); + stack->correctParams(1); + /* double val = */ stack->pop()->getFloat(); + + if (_textMode || !_writeFile) { + script->runtimeError("File.%s: File must be open for writing in binary mode.", name); + stack->pushBool(false); + return STATUS_OK; + } + //fwrite(&val, sizeof(val), 1, (FILE *)_writeFile); + stack->pushBool(true); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // WriteString + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "WriteString") == 0) { + stack->correctParams(1); + const char *val = stack->pop()->getString(); + + if (_textMode || !_writeFile) { + script->runtimeError("File.%s: File must be open for writing in binary mode.", name); + stack->pushBool(false); + return STATUS_OK; + } + + uint32 size = strlen(val); + _writeFile->writeUint32LE(size); + _writeFile->writeString(val); + + stack->pushBool(true); + + return STATUS_OK; + } else { + return BaseScriptable::scCallMethod(script, stack, thisStack, name); + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *SXFile::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type (RO) + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("file"); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Filename (RO) + ////////////////////////////////////////////////////////////////////////// + if (name == "Filename") { + _scValue->setString(_filename); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Position (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "Position") { + _scValue->setInt(getPos()); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Length (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "Length") { + _scValue->setInt(getLength()); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // TextMode (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "TextMode") { + _scValue->setBool(_textMode); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // AccessMode (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "AccessMode") { + _scValue->setInt(_mode); + return _scValue; + } else { + return BaseScriptable::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool SXFile::scSetProperty(const char *name, ScValue *value) { + /* + ////////////////////////////////////////////////////////////////////////// + // Length + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Length")==0) { + int origLength = _length; + _length = max(value->getInt(0), 0); + + char propName[20]; + if (_length < OrigLength) { + for(int i=_length; i<OrigLength; i++) { + sprintf(PropName, "%d", i); + _values->DeleteProp(PropName); + } + } + return STATUS_OK; + } + else*/ return BaseScriptable::scSetProperty(name, value); +} + +////////////////////////////////////////////////////////////////////////// +uint32 SXFile::getPos() { + if (_mode == 1 && _readFile) { + return _readFile->pos(); + } else if ((_mode == 2 || _mode == 3) && _writeFile) { + error("SXFile - getPos for WriteFile not supported"); + return 0; +// return ftell((FILE *)_writeFile); + } else { + return 0; + } +} + +////////////////////////////////////////////////////////////////////////// +bool SXFile::setPos(uint32 pos, int whence) { + if (_mode == 1 && _readFile) { + return _readFile->seek(pos, whence); + } else if ((_mode == 2 || _mode == 3) && _writeFile) { + error("SXFile - seeking in WriteFile not supported"); + return false; +// return fseek((FILE *)_writeFile, pos, (int)origin) == 0; + } else { + return false; + } +} + +////////////////////////////////////////////////////////////////////////// +uint32 SXFile::getLength() { + if (_mode == 1 && _readFile) { + return _readFile->size(); + } else if ((_mode == 2 || _mode == 3) && _writeFile) { + error("SXFile - reading length for WriteFile not supported"); + return 0; + /* + uint32 currentPos = ftell((FILE *)_writeFile); + fseek((FILE *)_writeFile, 0, SEEK_END); + int ret = ftell((FILE *)_writeFile); + fseek((FILE *)_writeFile, CurrentPos, SEEK_SET); + return Ret;*/ + } else { + return 0; + } +} + +////////////////////////////////////////////////////////////////////////// +bool SXFile::persist(BasePersistenceManager *persistMgr) { + + BaseScriptable::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_filename)); + persistMgr->transfer(TMEMBER(_mode)); + persistMgr->transfer(TMEMBER(_textMode)); + + uint32 pos = 0; + if (persistMgr->getIsSaving()) { + pos = getPos(); + persistMgr->transfer(TMEMBER(pos)); + } else { + persistMgr->transfer(TMEMBER(pos)); + + // try to re-open file if needed + _writeFile = NULL; + _readFile = NULL; + + if (_mode != 0) { + // open for reading + if (_mode == 1) { + _readFile = BaseFileManager::getEngineInstance()->openFile(_filename); + if (!_readFile) { + close(); + } + } + // open for writing / appending + else { + if (_textMode) { + if (_mode == 2) { + _writeFile = openForWrite(_filename, false); + } else { + _writeFile = openForAppend(_filename, false); + } + } else { + if (_mode == 2) { + _writeFile = openForWrite(_filename, true); + } else { + _writeFile = openForAppend(_filename, true); + } + } + if (_writeFile) { + close(); + } + } + setPos(pos); + } + } + + return STATUS_OK; +} + +// Should replace fopen(..., "wb+") and fopen(..., "w+") +Common::WriteStream *SXFile::openForWrite(const Common::String &filename, bool binary) { + error("SXFile::openForWrite - WriteFiles not supported"); +} + +// Should replace fopen(..., "ab+") and fopen(..., "a+") +Common::WriteStream *SXFile::openForAppend(const Common::String &filename, bool binary) { + error("SXFile::openForAppend - WriteFiles not supported"); +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/scriptables/script_ext_file.h b/engines/wintermute/base/scriptables/script_ext_file.h new file mode 100644 index 0000000000..f7c72fcfb3 --- /dev/null +++ b/engines/wintermute/base/scriptables/script_ext_file.h @@ -0,0 +1,66 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTES_SXFILE_H +#define WINTERMUTES_SXFILE_H + + +#include "engines/wintermute/base/base_scriptable.h" +#include "common/stream.h" + +namespace Wintermute { + +class BaseFile; + +class SXFile : public BaseScriptable { +public: + DECLARE_PERSISTENT(SXFile, BaseScriptable) + ScValue *scGetProperty(const Common::String &name); + bool scSetProperty(const char *name, ScValue *value); + bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + const char *scToString(); + SXFile(BaseGame *inGame, ScStack *Stack); + virtual ~SXFile(); +private: + Common::SeekableReadStream *_readFile; + Common::WriteStream *_writeFile; + int _mode; // 0..none, 1..read, 2..write, 3..append + bool _textMode; + void close(); + void cleanup(); + uint32 getPos(); + uint32 getLength(); + bool setPos(uint32 pos, int whence = SEEK_SET); + char *_filename; + Common::WriteStream *openForWrite(const Common::String &filename, bool binary); + Common::WriteStream *openForAppend(const Common::String &filename, bool binary); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/scriptables/script_ext_math.cpp b/engines/wintermute/base/scriptables/script_ext_math.cpp new file mode 100644 index 0000000000..d816fbec65 --- /dev/null +++ b/engines/wintermute/base/scriptables/script_ext_math.cpp @@ -0,0 +1,295 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/scriptables/script_ext_math.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/persistent.h" +#include "common/math.h" +#include <math.h> + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + + +IMPLEMENT_PERSISTENT(SXMath, true) + +BaseScriptable *makeSXMath(BaseGame *inGame) { + return new SXMath(inGame); +} + +////////////////////////////////////////////////////////////////////////// +SXMath::SXMath(BaseGame *inGame) : BaseScriptable(inGame) { + +} + + +////////////////////////////////////////////////////////////////////////// +SXMath::~SXMath() { + +} + + +////////////////////////////////////////////////////////////////////////// +bool SXMath::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // Abs + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Abs") == 0) { + stack->correctParams(1); + stack->pushFloat(fabs(stack->pop()->getFloat())); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Acos + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Acos") == 0) { + stack->correctParams(1); + stack->pushFloat(acos(stack->pop()->getFloat())); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Asin + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Asin") == 0) { + stack->correctParams(1); + stack->pushFloat(asin(stack->pop()->getFloat())); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Atan + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Atan") == 0) { + stack->correctParams(1); + stack->pushFloat(atan(stack->pop()->getFloat())); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Atan2 + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Atan2") == 0) { + stack->correctParams(2); + double y = stack->pop()->getFloat(); + double x = stack->pop()->getFloat(); + stack->pushFloat(atan2(y, x)); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Ceil + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Ceil") == 0) { + stack->correctParams(1); + stack->pushFloat(ceil(stack->pop()->getFloat())); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Cos + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Cos") == 0) { + stack->correctParams(1); + stack->pushFloat(cos(degreeToRadian(stack->pop()->getFloat()))); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Cosh + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Cosh") == 0) { + stack->correctParams(1); + stack->pushFloat(cosh(degreeToRadian(stack->pop()->getFloat()))); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Exp + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Exp") == 0) { + stack->correctParams(1); + stack->pushFloat(exp(stack->pop()->getFloat())); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Floor + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Floor") == 0) { + stack->correctParams(1); + stack->pushFloat(floor(stack->pop()->getFloat())); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Log + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Log") == 0) { + stack->correctParams(1); + stack->pushFloat(log(stack->pop()->getFloat())); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Log10 + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Log10") == 0) { + stack->correctParams(1); + stack->pushFloat(log10(stack->pop()->getFloat())); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Pow + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Pow") == 0) { + stack->correctParams(2); + double x = stack->pop()->getFloat(); + double y = stack->pop()->getFloat(); + + stack->pushFloat(pow(x, y)); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Sin + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Sin") == 0) { + stack->correctParams(1); + stack->pushFloat(sin(degreeToRadian(stack->pop()->getFloat()))); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Sinh + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Sinh") == 0) { + stack->correctParams(1); + stack->pushFloat(sinh(degreeToRadian(stack->pop()->getFloat()))); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Tan + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Tan") == 0) { + stack->correctParams(1); + stack->pushFloat(tan(degreeToRadian(stack->pop()->getFloat()))); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Tanh + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Tanh") == 0) { + stack->correctParams(1); + stack->pushFloat(tanh(degreeToRadian(stack->pop()->getFloat()))); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Sqrt + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Sqrt") == 0) { + stack->correctParams(1); + stack->pushFloat(sqrt(stack->pop()->getFloat())); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // DegToRad + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "DegToRad") == 0) { + stack->correctParams(1); + stack->pushFloat(degreeToRadian(stack->pop()->getFloat())); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // RadToDeg + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "RadToDeg") == 0) { + stack->correctParams(1); + stack->pushFloat(radianToDegree(stack->pop()->getFloat())); + return STATUS_OK; + } else { + return STATUS_FAILED; + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *SXMath::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("math"); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // PI + ////////////////////////////////////////////////////////////////////////// + else if (name == "PI") { + _scValue->setFloat(M_PI); + return _scValue; + } else { + return _scValue; + } +} + + +////////////////////////////////////////////////////////////////////////// +double SXMath::degreeToRadian(double value) { + return value * (M_PI / 180.0f); +} + + +////////////////////////////////////////////////////////////////////////// +double SXMath::radianToDegree(double value) { + return value * (180.0f / M_PI); +} + + +////////////////////////////////////////////////////////////////////////// +bool SXMath::persist(BasePersistenceManager *persistMgr) { + + BaseScriptable::persist(persistMgr); + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/scriptables/script_ext_math.h b/engines/wintermute/base/scriptables/script_ext_math.h new file mode 100644 index 0000000000..48c43ea7e8 --- /dev/null +++ b/engines/wintermute/base/scriptables/script_ext_math.h @@ -0,0 +1,53 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_SXMATH_H +#define WINTERMUTE_SXMATH_H + + +#include "engines/wintermute/base/base_scriptable.h" + +namespace Wintermute { + +class SXMath : public BaseScriptable { +public: + DECLARE_PERSISTENT(SXMath, BaseScriptable) + SXMath(BaseGame *inGame); + virtual ~SXMath(); + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + +private: + double degreeToRadian(double value); + double radianToDegree(double value); + +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/scriptables/script_ext_mem_buffer.cpp b/engines/wintermute/base/scriptables/script_ext_mem_buffer.cpp new file mode 100644 index 0000000000..42c5cfb20e --- /dev/null +++ b/engines/wintermute/base/scriptables/script_ext_mem_buffer.cpp @@ -0,0 +1,529 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_scriptable.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script_ext_mem_buffer.h" +#include "common/file.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(SXMemBuffer, false) + +BaseScriptable *makeSXMemBuffer(BaseGame *inGame, ScStack *stack) { + return new SXMemBuffer(inGame, stack); +} + +////////////////////////////////////////////////////////////////////////// +SXMemBuffer::SXMemBuffer(BaseGame *inGame, ScStack *stack) : BaseScriptable(inGame) { + stack->correctParams(1); + _buffer = NULL; + _size = 0; + + int newSize = stack->pop()->getInt(); + resize(MAX(0, newSize)); +} + +////////////////////////////////////////////////////////////////////////// +SXMemBuffer::SXMemBuffer(BaseGame *inGame, void *buffer) : BaseScriptable(inGame) { + _size = 0; + _buffer = buffer; +} + + +////////////////////////////////////////////////////////////////////////// +SXMemBuffer::~SXMemBuffer() { + cleanup(); +} + +////////////////////////////////////////////////////////////////////////// +void *SXMemBuffer::scToMemBuffer() { + return _buffer; +} + +////////////////////////////////////////////////////////////////////////// +void SXMemBuffer::cleanup() { + if (_size) { + free(_buffer); + } + _buffer = NULL; + _size = 0; +} + +////////////////////////////////////////////////////////////////////////// +bool SXMemBuffer::resize(int newSize) { + int oldSize = _size; + + if (_size == 0) { + _buffer = malloc(newSize); + if (_buffer) { + _size = newSize; + } + } else { + void *newBuf = realloc(_buffer, newSize); + if (!newBuf) { + if (newSize == 0) { + _buffer = newBuf; + _size = newSize; + } else { + return STATUS_FAILED; + } + } else { + _buffer = newBuf; + _size = newSize; + } + } + + if (_buffer && _size > oldSize) { + memset((byte *)_buffer + oldSize, 0, _size - oldSize); + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool SXMemBuffer::checkBounds(ScScript *script, int start, int length) { + if (_buffer == NULL) { + script->runtimeError("Cannot use Set/Get methods on an uninitialized memory buffer"); + return false; + } + if (_size == 0) { + return true; + } + + if (start < 0 || length == 0 || start + length > _size) { + script->runtimeError("Set/Get method call is out of bounds"); + return false; + } else { + return true; + } +} + +////////////////////////////////////////////////////////////////////////// +const char *SXMemBuffer::scToString() { + return "[membuffer object]"; +} + + +////////////////////////////////////////////////////////////////////////// +bool SXMemBuffer::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // SetSize + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "SetSize") == 0) { + stack->correctParams(1); + int newSize = stack->pop()->getInt(); + newSize = MAX(0, newSize); + if (DID_SUCCEED(resize(newSize))) { + stack->pushBool(true); + } else { + stack->pushBool(false); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetBool + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetBool") == 0) { + stack->correctParams(1); + int start = stack->pop()->getInt(); + if (!checkBounds(script, start, sizeof(bool))) { + stack->pushNULL(); + } else { + stack->pushBool(*(bool *)((byte *)_buffer + start)); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetByte + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetByte") == 0) { + stack->correctParams(1); + int start = stack->pop()->getInt(); + if (!checkBounds(script, start, sizeof(byte))) { + stack->pushNULL(); + } else { + stack->pushInt(*(byte *)((byte *)_buffer + start)); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetShort + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetShort") == 0) { + stack->correctParams(1); + int start = stack->pop()->getInt(); + if (!checkBounds(script, start, sizeof(short))) { + stack->pushNULL(); + } else { + stack->pushInt(65536 + * (short *)((byte *)_buffer + start)); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetInt / GetLong + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetInt") == 0 || strcmp(name, "GetLong") == 0) { + stack->correctParams(1); + int start = stack->pop()->getInt(); + if (!checkBounds(script, start, sizeof(int))) { + stack->pushNULL(); + } else { + stack->pushInt(*(int *)((byte *)_buffer + start)); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetFloat + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetFloat") == 0) { + stack->correctParams(1); + int start = stack->pop()->getInt(); + if (!checkBounds(script, start, sizeof(float))) { + stack->pushNULL(); + } else { + stack->pushFloat(*(float *)((byte *)_buffer + start)); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetDouble + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetDouble") == 0) { + stack->correctParams(1); + int start = stack->pop()->getInt(); + if (!checkBounds(script, start, sizeof(double))) { + stack->pushNULL(); + } else { + stack->pushFloat(*(double *)((byte *)_buffer + start)); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetString + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetString") == 0) { + stack->correctParams(2); + int start = stack->pop()->getInt(); + int length = stack->pop()->getInt(); + + // find end of string + if (length == 0 && start >= 0 && start < _size) { + for (int i = start; i < _size; i++) { + if (((char *)_buffer)[i] == '\0') { + length = i - start; + break; + } + } + } + + if (!checkBounds(script, start, length)) { + stack->pushNULL(); + } else { + char *str = new char[length + 1]; + Common::strlcpy(str, (const char *)_buffer + start, length + 1); + stack->pushString(str); + delete[] str; + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetPointer + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetPointer") == 0) { + stack->correctParams(1); + int start = stack->pop()->getInt(); + if (!checkBounds(script, start, sizeof(void *))) { + stack->pushNULL(); + } else { + void *pointer = *(void **)((byte *)_buffer + start); + SXMemBuffer *buf = new SXMemBuffer(_gameRef, pointer); + stack->pushNative(buf, false); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetBool + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetBool") == 0) { + stack->correctParams(2); + int start = stack->pop()->getInt(); + bool val = stack->pop()->getBool(); + + if (!checkBounds(script, start, sizeof(bool))) { + stack->pushBool(false); + } else { + *(bool *)((byte *)_buffer + start) = val; + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetByte + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetByte") == 0) { + stack->correctParams(2); + int start = stack->pop()->getInt(); + byte val = (byte)stack->pop()->getInt(); + + if (!checkBounds(script, start, sizeof(byte))) { + stack->pushBool(false); + } else { + *(byte *)((byte *)_buffer + start) = val; + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetShort + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetShort") == 0) { + stack->correctParams(2); + int start = stack->pop()->getInt(); + short val = (short)stack->pop()->getInt(); + + if (!checkBounds(script, start, sizeof(short))) { + stack->pushBool(false); + } else { + *(short *)((byte *)_buffer + start) = val; + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetInt / SetLong + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetInt") == 0 || strcmp(name, "SetLong") == 0) { + stack->correctParams(2); + int start = stack->pop()->getInt(); + int val = stack->pop()->getInt(); + + if (!checkBounds(script, start, sizeof(int))) { + stack->pushBool(false); + } else { + *(int *)((byte *)_buffer + start) = val; + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetFloat + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetFloat") == 0) { + stack->correctParams(2); + int start = stack->pop()->getInt(); + float val = (float)stack->pop()->getFloat(); + + if (!checkBounds(script, start, sizeof(float))) { + stack->pushBool(false); + } else { + *(float *)((byte *)_buffer + start) = val; + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetDouble + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetDouble") == 0) { + stack->correctParams(2); + int start = stack->pop()->getInt(); + double val = stack->pop()->getFloat(); + + if (!checkBounds(script, start, sizeof(double))) { + stack->pushBool(false); + } else { + *(double *)((byte *)_buffer + start) = val; + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetString + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetString") == 0) { + stack->correctParams(2); + int start = stack->pop()->getInt(); + const char *val = stack->pop()->getString(); + + if (!checkBounds(script, start, strlen(val) + 1)) { + stack->pushBool(false); + } else { + memcpy((byte *)_buffer + start, val, strlen(val) + 1); + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetPointer + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetPointer") == 0) { + stack->correctParams(2); + int start = stack->pop()->getInt(); + /* ScValue *val = */ stack->pop(); + + if (!checkBounds(script, start, sizeof(void *))) { + stack->pushBool(false); + } else { + /* + int pointer = (int)Val->getMemBuffer(); + memcpy((byte *)_buffer+Start, &Pointer, sizeof(void*)); + stack->pushBool(true); + */ + // TODO fix + stack->pushBool(false); + + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // DEBUG_Dump + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "DEBUG_Dump") == 0) { + stack->correctParams(0); + if (_buffer && _size) { + warning("SXMemBuffer::ScCallMethod - DEBUG_Dump"); + Common::DumpFile f; + f.open("buffer.bin"); + f.write(_buffer, _size); + f.close(); + } + stack->pushNULL(); + return STATUS_OK; + } else { + return STATUS_FAILED; + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *SXMemBuffer::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type (RO) + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("membuffer"); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Size (RO) + ////////////////////////////////////////////////////////////////////////// + if (name == "Size") { + _scValue->setInt(_size); + return _scValue; + } else { + return BaseScriptable::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool SXMemBuffer::scSetProperty(const char *name, ScValue *value) { + /* + ////////////////////////////////////////////////////////////////////////// + // Length + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Length")==0) { + int origLength = _length; + _length = max(value->getInt(0), 0); + + char propName[20]; + if (_length < origLength) { + for(int i=_length; i < origLength; i++) { + sprintf(propName, "%d", i); + _values->DeleteProp(propName); + } + } + return STATUS_OK; + } + else*/ return BaseScriptable::scSetProperty(name, value); +} + + +////////////////////////////////////////////////////////////////////////// +bool SXMemBuffer::persist(BasePersistenceManager *persistMgr) { + + BaseScriptable::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_size)); + + if (persistMgr->getIsSaving()) { + if (_size > 0) { + persistMgr->putBytes((byte *)_buffer, _size); + } + } else { + if (_size > 0) { + _buffer = malloc(_size); + persistMgr->getBytes((byte *)_buffer, _size); + } else { + _buffer = NULL; + } + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +int SXMemBuffer::scCompare(BaseScriptable *val) { + if (_buffer == val->scToMemBuffer()) { + return 0; + } else { + return 1; + } +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/scriptables/script_ext_mem_buffer.h b/engines/wintermute/base/scriptables/script_ext_mem_buffer.h new file mode 100644 index 0000000000..1527a323dc --- /dev/null +++ b/engines/wintermute/base/scriptables/script_ext_mem_buffer.h @@ -0,0 +1,60 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_SXMEMBUFFER_H +#define WINTERMUTE_SXMEMBUFFER_H + + +#include "engines/wintermute/base/base_scriptable.h" + +namespace Wintermute { + +class SXMemBuffer : public BaseScriptable { +public: + virtual int scCompare(BaseScriptable *Val); + DECLARE_PERSISTENT(SXMemBuffer, BaseScriptable) + ScValue *scGetProperty(const Common::String &name); + bool scSetProperty(const char *name, ScValue *value); + bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + const char *scToString(); + SXMemBuffer(BaseGame *inGame, ScStack *stack); + SXMemBuffer(BaseGame *inGame, void *buffer); + virtual ~SXMemBuffer(); + virtual void *scToMemBuffer(); +private: + int _size; + + bool resize(int newSize); + void *_buffer; + void cleanup(); + bool checkBounds(ScScript *script, int start, int length); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/scriptables/script_ext_object.cpp b/engines/wintermute/base/scriptables/script_ext_object.cpp new file mode 100644 index 0000000000..b87aac81f9 --- /dev/null +++ b/engines/wintermute/base/scriptables/script_ext_object.cpp @@ -0,0 +1,67 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/scriptables/script_ext_object.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script_stack.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +IMPLEMENT_PERSISTENT(SXObject, false) + +BaseScriptable *makeSXObject(BaseGame *inGame, ScStack *stack) { + return new SXObject(inGame, stack); +} + +////////////////////////////////////////////////////////////////////////// +SXObject::SXObject(BaseGame *inGame, ScStack *stack) : BaseObject(inGame) { + int numParams = stack->pop()->getInt(0); + for (int i = 0; i < numParams; i++) { + addScript(stack->pop()->getString()); + } +} + + +////////////////////////////////////////////////////////////////////////// +SXObject::~SXObject() { + +} + + +////////////////////////////////////////////////////////////////////////// +bool SXObject::persist(BasePersistenceManager *persistMgr) { + BaseObject::persist(persistMgr); + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/scriptables/script_ext_object.h b/engines/wintermute/base/scriptables/script_ext_object.h new file mode 100644 index 0000000000..c85d16d44e --- /dev/null +++ b/engines/wintermute/base/scriptables/script_ext_object.h @@ -0,0 +1,46 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_SXOBJECT_H +#define WINTERMUTE_SXOBJECT_H + + +#include "engines/wintermute/base/base_object.h" + +namespace Wintermute { + +class SXObject : public BaseObject { +public: + DECLARE_PERSISTENT(SXObject, BaseObject) + SXObject(BaseGame *inGame, ScStack *Stack); + virtual ~SXObject(); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/scriptables/script_ext_string.cpp b/engines/wintermute/base/scriptables/script_ext_string.cpp new file mode 100644 index 0000000000..5f7da1c2dd --- /dev/null +++ b/engines/wintermute/base/scriptables/script_ext_string.cpp @@ -0,0 +1,436 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/utils/utils.h" +#include "engines/wintermute/base/scriptables/script_ext_string.h" +#include "engines/wintermute/base/scriptables/script_ext_array.h" +#include "engines/wintermute/utils/string_util.h" +#include "common/tokenizer.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(SXString, false) + +BaseScriptable *makeSXString(BaseGame *inGame, ScStack *stack) { + return new SXString(inGame, stack); +} + +////////////////////////////////////////////////////////////////////////// +SXString::SXString(BaseGame *inGame, ScStack *stack) : BaseScriptable(inGame) { + _string = NULL; + _capacity = 0; + + stack->correctParams(1); + ScValue *val = stack->pop(); + + if (val->isInt()) { + _capacity = MAX(0, val->getInt()); + if (_capacity > 0) { + _string = new char[_capacity]; + memset(_string, 0, _capacity); + } + } else { + setStringVal(val->getString()); + } + + if (_capacity == 0) { + setStringVal(""); + } +} + + +////////////////////////////////////////////////////////////////////////// +SXString::~SXString() { + if (_string) { + delete[] _string; + } +} + + +////////////////////////////////////////////////////////////////////////// +void SXString::setStringVal(const char *val) { + int len = strlen(val); + if (len >= _capacity) { + _capacity = len + 1; + delete[] _string; + _string = NULL; + _string = new char[_capacity]; + memset(_string, 0, _capacity); + } + strcpy(_string, val); +} + + +////////////////////////////////////////////////////////////////////////// +const char *SXString::scToString() { + if (_string) { + return _string; + } else { + return "[null string]"; + } +} + + +////////////////////////////////////////////////////////////////////////// +void SXString::scSetString(const char *val) { + setStringVal(val); +} + + +////////////////////////////////////////////////////////////////////////// +bool SXString::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // Substring + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Substring") == 0) { + stack->correctParams(2); + int start = stack->pop()->getInt(); + int end = stack->pop()->getInt(); + + if (end < start) { + BaseUtils::swap(&start, &end); + } + + //try { + WideString str; + if (_gameRef->_textEncoding == TEXT_UTF8) { + str = StringUtil::utf8ToWide(_string); + } else { + str = StringUtil::ansiToWide(_string); + } + + //WideString subStr = str.substr(start, end - start + 1); + WideString subStr(str.c_str() + start, end - start + 1); + + if (_gameRef->_textEncoding == TEXT_UTF8) { + stack->pushString(StringUtil::wideToUtf8(subStr).c_str()); + } else { + stack->pushString(StringUtil::wideToAnsi(subStr).c_str()); + } + // } catch (std::exception &) { + // stack->pushNULL(); + // } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Substr + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Substr") == 0) { + stack->correctParams(2); + int start = stack->pop()->getInt(); + + ScValue *val = stack->pop(); + int len = val->getInt(); + + if (!val->isNULL() && len <= 0) { + stack->pushString(""); + return STATUS_OK; + } + + if (val->isNULL()) { + len = strlen(_string) - start; + } + +// try { + WideString str; + if (_gameRef->_textEncoding == TEXT_UTF8) { + str = StringUtil::utf8ToWide(_string); + } else { + str = StringUtil::ansiToWide(_string); + } + +// WideString subStr = str.substr(start, len); + WideString subStr(str.c_str() + start, len); + + if (_gameRef->_textEncoding == TEXT_UTF8) { + stack->pushString(StringUtil::wideToUtf8(subStr).c_str()); + } else { + stack->pushString(StringUtil::wideToAnsi(subStr).c_str()); + } +// } catch (std::exception &) { +// stack->pushNULL(); +// } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ToUpperCase + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ToUpperCase") == 0) { + stack->correctParams(0); + + WideString str; + if (_gameRef->_textEncoding == TEXT_UTF8) { + str = StringUtil::utf8ToWide(_string); + } else { + str = StringUtil::ansiToWide(_string); + } + + str.toUppercase(); + + if (_gameRef->_textEncoding == TEXT_UTF8) { + stack->pushString(StringUtil::wideToUtf8(str).c_str()); + } else { + stack->pushString(StringUtil::wideToAnsi(str).c_str()); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ToLowerCase + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ToLowerCase") == 0) { + stack->correctParams(0); + + WideString str; + if (_gameRef->_textEncoding == TEXT_UTF8) { + str = StringUtil::utf8ToWide(_string); + } else { + str = StringUtil::ansiToWide(_string); + } + + str.toLowercase(); + + if (_gameRef->_textEncoding == TEXT_UTF8) { + stack->pushString(StringUtil::wideToUtf8(str).c_str()); + } else { + stack->pushString(StringUtil::wideToAnsi(str).c_str()); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // IndexOf + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "IndexOf") == 0) { + stack->correctParams(2); + + const char *strToFind = stack->pop()->getString(); + int index = stack->pop()->getInt(); + + WideString str; + if (_gameRef->_textEncoding == TEXT_UTF8) { + str = StringUtil::utf8ToWide(_string); + } else { + str = StringUtil::ansiToWide(_string); + } + + WideString toFind; + if (_gameRef->_textEncoding == TEXT_UTF8) { + toFind = StringUtil::utf8ToWide(strToFind); + } else { + toFind = StringUtil::ansiToWide(strToFind); + } + + int indexOf = StringUtil::indexOf(str, toFind, index); + stack->pushInt(indexOf); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Split + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Split") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + char separators[MAX_PATH_LENGTH] = ","; + if (!val->isNULL()) { + strcpy(separators, val->getString()); + } + + SXArray *array = new SXArray(_gameRef); + if (!array) { + stack->pushNULL(); + return STATUS_OK; + } + + + WideString str; + if (_gameRef->_textEncoding == TEXT_UTF8) { + str = StringUtil::utf8ToWide(_string); + } else { + str = StringUtil::ansiToWide(_string); + } + + WideString delims; + if (_gameRef->_textEncoding == TEXT_UTF8) { + delims = StringUtil::utf8ToWide(separators); + } else { + delims = StringUtil::ansiToWide(separators); + } + + Common::Array<WideString> parts; + + + + Common::StringTokenizer tokenizer(str, delims); + while (!tokenizer.empty()) { + Common::String str2 = tokenizer.nextToken(); + parts.push_back(str2); + } + // TODO: Clean this up + /*do { + pos = StringUtil::IndexOf(Common::String(str.c_str() + start), delims, start); + //pos = str.find_first_of(delims, start); + if (pos == start) { + start = pos + 1; + } else if (pos == str.size()) { + parts.push_back(Common::String(str.c_str() + start)); + break; + } else { + parts.push_back(Common::String(str.c_str() + start, pos - start)); + start = pos + 1; + } + //start = str.find_first_not_of(delims, start); + start = StringUtil::LastIndexOf(Common::String(str.c_str() + start), delims, start) + 1; + + } while (pos != str.size());*/ + + for (Common::Array<WideString>::iterator it = parts.begin(); it != parts.end(); ++it) { + WideString &part = (*it); + + if (_gameRef->_textEncoding == TEXT_UTF8) { + val = new ScValue(_gameRef, StringUtil::wideToUtf8(part).c_str()); + } else { + val = new ScValue(_gameRef, StringUtil::wideToAnsi(part).c_str()); + } + + array->push(val); + delete val; + val = NULL; + } + + stack->pushNative(array, false); + return STATUS_OK; + } else { + return STATUS_FAILED; + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *SXString::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type (RO) + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("string"); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // Length (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "Length") { + if (_gameRef->_textEncoding == TEXT_UTF8) { + WideString wstr = StringUtil::utf8ToWide(_string); + _scValue->setInt(wstr.size()); + } else { + _scValue->setInt(strlen(_string)); + } + + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // Capacity + ////////////////////////////////////////////////////////////////////////// + else if (name == "Capacity") { + _scValue->setInt(_capacity); + return _scValue; + } else { + return _scValue; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool SXString::scSetProperty(const char *name, ScValue *value) { + ////////////////////////////////////////////////////////////////////////// + // Capacity + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Capacity") == 0) { + int32 newCap = (uint32)value->getInt(); + if (newCap < (int32)(strlen(_string) + 1)) { + _gameRef->LOG(0, "Warning: cannot lower string capacity"); + } else if (newCap != _capacity) { + char *newStr = new char[newCap]; + if (newStr) { + memset(newStr, 0, newCap); + strcpy(newStr, _string); + delete[] _string; + _string = newStr; + _capacity = newCap; + } + } + return STATUS_OK; + } else { + return STATUS_FAILED; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool SXString::persist(BasePersistenceManager *persistMgr) { + + BaseScriptable::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_capacity)); + + if (persistMgr->getIsSaving()) { + if (_capacity > 0) { + persistMgr->putBytes((byte *)_string, _capacity); + } + } else { + if (_capacity > 0) { + _string = new char[_capacity]; + persistMgr->getBytes((byte *)_string, _capacity); + } else { + _string = NULL; + } + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +int SXString::scCompare(BaseScriptable *val) { + return strcmp(_string, ((SXString *)val)->_string); +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/scriptables/script_ext_string.h b/engines/wintermute/base/scriptables/script_ext_string.h new file mode 100644 index 0000000000..00bffab3a9 --- /dev/null +++ b/engines/wintermute/base/scriptables/script_ext_string.h @@ -0,0 +1,58 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_SXSTRING_H +#define WINTERMUTE_SXSTRING_H + + +#include "engines/wintermute/base/base_scriptable.h" + +namespace Wintermute { + +class SXString : public BaseScriptable { +public: + virtual int scCompare(BaseScriptable *Val); + DECLARE_PERSISTENT(SXString, BaseScriptable) + ScValue *scGetProperty(const Common::String &name); + bool scSetProperty(const char *name, ScValue *value); + bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + void scSetString(const char *val); + const char *scToString(); + void setStringVal(const char *val); + + SXString(BaseGame *inGame, ScStack *Stack); + virtual ~SXString(); + +private: + char *_string; + int _capacity; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/scriptables/script_stack.cpp b/engines/wintermute/base/scriptables/script_stack.cpp new file mode 100644 index 0000000000..194c5f4f35 --- /dev/null +++ b/engines/wintermute/base/scriptables/script_stack.cpp @@ -0,0 +1,195 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/base_game.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(ScStack, false) + +////////////////////////////////////////////////////////////////////////// +ScStack::ScStack(BaseGame *inGame) : BaseClass(inGame) { + _sP = -1; +} + + +////////////////////////////////////////////////////////////////////////// +ScStack::~ScStack() { + +#if _DEBUG + //_gameRef->LOG(0, "STAT: Stack size: %d, SP=%d", _values.size(), _sP); +#endif + + for (uint32 i = 0; i < _values.size(); i++) { + delete _values[i]; + } + _values.clear(); +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *ScStack::pop() { + if (_sP < 0) { + _gameRef->LOG(0, "Fatal: Stack underflow"); + return NULL; + } + + return _values[_sP--]; +} + + +////////////////////////////////////////////////////////////////////////// +void ScStack::push(ScValue *val) { + _sP++; + + if (_sP < (int32)_values.size()) { + _values[_sP]->cleanup(); + _values[_sP]->copy(val); + } else { + ScValue *copyVal = new ScValue(_gameRef); + copyVal->copy(val); + _values.add(copyVal); + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *ScStack::getPushValue() { + _sP++; + + if (_sP >= (int32)_values.size()) { + ScValue *val = new ScValue(_gameRef); + _values.add(val); + } + _values[_sP]->cleanup(); + return _values[_sP]; +} + + + +////////////////////////////////////////////////////////////////////////// +ScValue *ScStack::getTop() { + if (_sP < 0 || _sP >= (int32)_values.size()) { + return NULL; + } else { + return _values[_sP]; + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *ScStack::getAt(int index) { + index = _sP - index; + if (index < 0 || index >= (int32)_values.size()) { + return NULL; + } else { + return _values[index]; + } +} + + +////////////////////////////////////////////////////////////////////////// +void ScStack::correctParams(uint32 expectedParams) { + uint32 nuParams = (uint32)pop()->getInt(); + + if (expectedParams < nuParams) { // too many params + while (expectedParams < nuParams) { + //Pop(); + delete _values[_sP - expectedParams]; + _values.remove_at(_sP - expectedParams); + nuParams--; + _sP--; + } + } else if (expectedParams > nuParams) { // need more params + while (expectedParams > nuParams) { + //Push(null_val); + ScValue *nullVal = new ScValue(_gameRef); + nullVal->setNULL(); + _values.insert_at(_sP - nuParams + 1, nullVal); + nuParams++; + _sP++; + + if ((int32)_values.size() > _sP + 1) { + delete _values[_values.size() - 1]; + _values.remove_at(_values.size() - 1); + } + } + } +} + + +////////////////////////////////////////////////////////////////////////// +void ScStack::pushNULL() { + getPushValue()->setNULL(); +} + + +////////////////////////////////////////////////////////////////////////// +void ScStack::pushInt(int val) { + getPushValue()->setInt(val); +} + + +////////////////////////////////////////////////////////////////////////// +void ScStack::pushFloat(double val) { + getPushValue()->setFloat(val); +} + + +////////////////////////////////////////////////////////////////////////// +void ScStack::pushBool(bool val) { + getPushValue()->setBool(val); +} + + +////////////////////////////////////////////////////////////////////////// +void ScStack::pushString(const char *val) { + getPushValue()->setString(val); +} + + +////////////////////////////////////////////////////////////////////////// +void ScStack::pushNative(BaseScriptable *val, bool persistent) { + getPushValue()->setNative(val, persistent); +} + + +////////////////////////////////////////////////////////////////////////// +bool ScStack::persist(BasePersistenceManager *persistMgr) { + + persistMgr->transfer(TMEMBER(_gameRef)); + + persistMgr->transfer(TMEMBER(_sP)); + _values.persist(persistMgr); + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/scriptables/script_stack.h b/engines/wintermute/base/scriptables/script_stack.h new file mode 100644 index 0000000000..86d246cf34 --- /dev/null +++ b/engines/wintermute/base/scriptables/script_stack.h @@ -0,0 +1,66 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_SCSTACK_H +#define WINTERMUTE_SCSTACK_H + + +#include "engines/wintermute/base/base.h" +#include "engines/wintermute/coll_templ.h" +#include "engines/wintermute/persistent.h" + +namespace Wintermute { + +class ScValue; +class BaseScriptable; + +class ScStack : public BaseClass { +public: + ScValue *getAt(int Index); + ScValue *getPushValue(); + DECLARE_PERSISTENT(ScStack, BaseClass) + void pushNative(BaseScriptable *val, bool persistent); + void pushString(const char *val); + void pushBool(bool val); + void pushInt(int val); + void pushFloat(double val); + void pushNULL(); + void correctParams(uint32 expectedParams); + ScValue *getTop(); + void push(ScValue *val); + ScValue *pop(); + ScStack(BaseGame *inGame); + virtual ~ScStack(); + BaseArray<ScValue *> _values; + int _sP; + +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/scriptables/script_value.cpp b/engines/wintermute/base/scriptables/script_value.cpp new file mode 100644 index 0000000000..ad8ab26e09 --- /dev/null +++ b/engines/wintermute/base/scriptables/script_value.cpp @@ -0,0 +1,995 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/platform_osystem.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/utils/string_util.h" +#include "engines/wintermute/base/base_scriptable.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +IMPLEMENT_PERSISTENT(ScValue, false) + +////////////////////////////////////////////////////////////////////////// +ScValue::ScValue(BaseGame *inGame) : BaseClass(inGame) { + _type = VAL_NULL; + + _valBool = false; + _valInt = 0; + _valFloat = 0.0f; + _valNative = NULL; + _valString = NULL; + _valRef = NULL; + _persistent = false; + _isConstVar = false; +} + + +////////////////////////////////////////////////////////////////////////// +ScValue::ScValue(BaseGame *inGame, bool val) : BaseClass(inGame) { + _type = VAL_BOOL; + _valBool = val; + + _valInt = 0; + _valFloat = 0.0f; + _valNative = NULL; + _valString = NULL; + _valRef = NULL; + _persistent = false; + _isConstVar = false; +} + + +////////////////////////////////////////////////////////////////////////// +ScValue::ScValue(BaseGame *inGame, int val) : BaseClass(inGame) { + _type = VAL_INT; + _valInt = val; + + _valFloat = 0.0f; + _valBool = false; + _valNative = NULL; + _valString = NULL; + _valRef = NULL; + _persistent = false; + _isConstVar = false; +} + + +////////////////////////////////////////////////////////////////////////// +ScValue::ScValue(BaseGame *inGame, double val) : BaseClass(inGame) { + _type = VAL_FLOAT; + _valFloat = val; + + _valInt = 0; + _valBool = false; + _valNative = NULL; + _valString = NULL; + _valRef = NULL; + _persistent = false; + _isConstVar = false; +} + + +////////////////////////////////////////////////////////////////////////// +ScValue::ScValue(BaseGame *inGame, const char *val) : BaseClass(inGame) { + _type = VAL_STRING; + _valString = NULL; + setStringVal(val); + + _valBool = false; + _valInt = 0; + _valFloat = 0.0f; + _valNative = NULL; + _valRef = NULL; + _persistent = false; + _isConstVar = false; +} + + +////////////////////////////////////////////////////////////////////////// +void ScValue::cleanup(bool ignoreNatives) { + deleteProps(); + + if (_valString) { + delete[] _valString; + } + + if (!ignoreNatives) { + if (_valNative && !_persistent) { + _valNative->_refCount--; + if (_valNative->_refCount <= 0) { + delete _valNative; + _valNative = NULL; + } + } + } + + + _type = VAL_NULL; + + _valBool = false; + _valInt = 0; + _valFloat = 0.0f; + _valNative = NULL; + _valString = NULL; + _valRef = NULL; + _persistent = false; + _isConstVar = false; +} + + + +////////////////////////////////////////////////////////////////////////// +ScValue::~ScValue() { + cleanup(); +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *ScValue::getProp(const char *name) { + if (_type == VAL_VARIABLE_REF) { + return _valRef->getProp(name); + } + + if (_type == VAL_STRING && strcmp(name, "Length") == 0) { + _gameRef->_scValue->_type = VAL_INT; + + if (_gameRef->_textEncoding == TEXT_ANSI) { + _gameRef->_scValue->setInt(strlen(_valString)); + } else { + WideString wstr = StringUtil::utf8ToWide(_valString); + _gameRef->_scValue->setInt(wstr.size()); + } + + return _gameRef->_scValue; + } + + ScValue *ret = NULL; + + if (_type == VAL_NATIVE && _valNative) { + ret = _valNative->scGetProperty(name); + } + + if (ret == NULL) { + _valIter = _valObject.find(name); + if (_valIter != _valObject.end()) { + ret = _valIter->_value; + } + } + return ret; +} + +////////////////////////////////////////////////////////////////////////// +bool ScValue::deleteProp(const char *name) { + if (_type == VAL_VARIABLE_REF) { + return _valRef->deleteProp(name); + } + + _valIter = _valObject.find(name); + if (_valIter != _valObject.end()) { + delete _valIter->_value; + _valIter->_value = NULL; + } + + return STATUS_OK; +} + + + +////////////////////////////////////////////////////////////////////////// +bool ScValue::setProp(const char *name, ScValue *val, bool copyWhole, bool setAsConst) { + if (_type == VAL_VARIABLE_REF) { + return _valRef->setProp(name, val); + } + + bool ret = STATUS_FAILED; + if (_type == VAL_NATIVE && _valNative) { + ret = _valNative->scSetProperty(name, val); + } + + if (DID_FAIL(ret)) { + ScValue *newVal = NULL; + + _valIter = _valObject.find(name); + if (_valIter != _valObject.end()) { + newVal = _valIter->_value; + } + if (!newVal) { + newVal = new ScValue(_gameRef); + } else { + newVal->cleanup(); + } + + newVal->copy(val, copyWhole); + newVal->_isConstVar = setAsConst; + _valObject[name] = newVal; + + if (_type != VAL_NATIVE) { + _type = VAL_OBJECT; + } + + /* + _valIter = _valObject.find(Name); + if (_valIter != _valObject.end()) { + delete _valIter->_value; + _valIter->_value = NULL; + } + ScValue* val = new ScValue(_gameRef); + val->Copy(Val, CopyWhole); + val->_isConstVar = SetAsConst; + _valObject[Name] = val; + + if (_type!=VAL_NATIVE) _type = VAL_OBJECT; + */ + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScValue::propExists(const char *name) { + if (_type == VAL_VARIABLE_REF) { + return _valRef->propExists(name); + } + _valIter = _valObject.find(name); + + return (_valIter != _valObject.end()); +} + + +////////////////////////////////////////////////////////////////////////// +void ScValue::deleteProps() { + _valIter = _valObject.begin(); + while (_valIter != _valObject.end()) { + delete(ScValue *)_valIter->_value; + _valIter++; + } + _valObject.clear(); +} + + +////////////////////////////////////////////////////////////////////////// +void ScValue::CleanProps(bool includingNatives) { + _valIter = _valObject.begin(); + while (_valIter != _valObject.end()) { + if (!_valIter->_value->_isConstVar && (!_valIter->_value->isNative() || includingNatives)) { + _valIter->_value->setNULL(); + } + _valIter++; + } +} + +////////////////////////////////////////////////////////////////////////// +bool ScValue::isNULL() { + if (_type == VAL_VARIABLE_REF) { + return _valRef->isNULL(); + } + + return (_type == VAL_NULL); +} + + +////////////////////////////////////////////////////////////////////////// +bool ScValue::isNative() { + if (_type == VAL_VARIABLE_REF) { + return _valRef->isNative(); + } + + return (_type == VAL_NATIVE); +} + + +////////////////////////////////////////////////////////////////////////// +bool ScValue::isString() { + if (_type == VAL_VARIABLE_REF) { + return _valRef->isString(); + } + + return (_type == VAL_STRING); +} + + +////////////////////////////////////////////////////////////////////////// +bool ScValue::isFloat() { + if (_type == VAL_VARIABLE_REF) { + return _valRef->isFloat(); + } + + return (_type == VAL_FLOAT); +} + + +////////////////////////////////////////////////////////////////////////// +bool ScValue::isInt() { + if (_type == VAL_VARIABLE_REF) { + return _valRef->isInt(); + } + + return (_type == VAL_INT); +} + + +////////////////////////////////////////////////////////////////////////// +bool ScValue::isBool() { + if (_type == VAL_VARIABLE_REF) { + return _valRef->isBool(); + } + + return (_type == VAL_BOOL); +} + + +////////////////////////////////////////////////////////////////////////// +bool ScValue::isObject() { + if (_type == VAL_VARIABLE_REF) { + return _valRef->isObject(); + } + + return (_type == VAL_OBJECT); +} + + +////////////////////////////////////////////////////////////////////////// +TValType ScValue::getTypeTolerant() { + if (_type == VAL_VARIABLE_REF) { + return _valRef->getType(); + } + + return _type; +} + + +////////////////////////////////////////////////////////////////////////// +void ScValue::setBool(bool val) { + if (_type == VAL_VARIABLE_REF) { + _valRef->setBool(val); + return; + } + + if (_type == VAL_NATIVE) { + _valNative->scSetBool(val); + return; + } + + _valBool = val; + _type = VAL_BOOL; +} + + +////////////////////////////////////////////////////////////////////////// +void ScValue::setInt(int val) { + if (_type == VAL_VARIABLE_REF) { + _valRef->setInt(val); + return; + } + + if (_type == VAL_NATIVE) { + _valNative->scSetInt(val); + return; + } + + _valInt = val; + _type = VAL_INT; +} + + +////////////////////////////////////////////////////////////////////////// +void ScValue::setFloat(double val) { + if (_type == VAL_VARIABLE_REF) { + _valRef->setFloat(val); + return; + } + + if (_type == VAL_NATIVE) { + _valNative->scSetFloat(val); + return; + } + + _valFloat = val; + _type = VAL_FLOAT; +} + + +////////////////////////////////////////////////////////////////////////// +void ScValue::setString(const char *val) { + if (_type == VAL_VARIABLE_REF) { + _valRef->setString(val); + return; + } + + if (_type == VAL_NATIVE) { + _valNative->scSetString(val); + return; + } + + setStringVal(val); + if (_valString) { + _type = VAL_STRING; + } else { + _type = VAL_NULL; + } +} + +void ScValue::setString(const Common::String &val) { + setString(val.c_str()); +} + +////////////////////////////////////////////////////////////////////////// +void ScValue::setStringVal(const char *val) { + if (_valString) { + delete[] _valString; + _valString = NULL; + } + + if (val == NULL) { + _valString = NULL; + return; + } + + _valString = new char [strlen(val) + 1]; + if (_valString) { + strcpy(_valString, val); + } +} + + +////////////////////////////////////////////////////////////////////////// +void ScValue::setNULL() { + if (_type == VAL_VARIABLE_REF) { + _valRef->setNULL(); + return; + } + + if (_valNative && !_persistent) { + _valNative->_refCount--; + if (_valNative->_refCount <= 0) { + delete _valNative; + } + } + _valNative = NULL; + deleteProps(); + + _type = VAL_NULL; +} + + +////////////////////////////////////////////////////////////////////////// +void ScValue::setNative(BaseScriptable *val, bool persistent) { + if (_type == VAL_VARIABLE_REF) { + _valRef->setNative(val, persistent); + return; + } + + if (val == NULL) { + setNULL(); + } else { + if (_valNative && !_persistent) { + _valNative->_refCount--; + if (_valNative->_refCount <= 0) { + if (_valNative != val) { + delete _valNative; + } + _valNative = NULL; + } + } + + _type = VAL_NATIVE; + _persistent = persistent; + + _valNative = val; + if (_valNative && !_persistent) { + _valNative->_refCount++; + } + } +} + + +////////////////////////////////////////////////////////////////////////// +void ScValue::setObject() { + if (_type == VAL_VARIABLE_REF) { + _valRef->setObject(); + return; + } + + deleteProps(); + _type = VAL_OBJECT; +} + + +////////////////////////////////////////////////////////////////////////// +void ScValue::setReference(ScValue *val) { + _valRef = val; + _type = VAL_VARIABLE_REF; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScValue::getBool(bool defaultVal) { + if (_type == VAL_VARIABLE_REF) { + return _valRef->getBool(); + } + + switch (_type) { + case VAL_BOOL: + return _valBool; + + case VAL_NATIVE: + return _valNative->scToBool(); + + case VAL_INT: + return (_valInt != 0); + + case VAL_FLOAT: + return (_valFloat != 0.0f); + + case VAL_STRING: + return (scumm_stricmp(_valString, "1") == 0 || scumm_stricmp(_valString, "yes") == 0 || scumm_stricmp(_valString, "true") == 0); + + default: + return defaultVal; + } +} + + +////////////////////////////////////////////////////////////////////////// +int ScValue::getInt(int defaultVal) { + if (_type == VAL_VARIABLE_REF) { + return _valRef->getInt(); + } + + switch (_type) { + case VAL_BOOL: + return _valBool ? 1 : 0; + + case VAL_NATIVE: + return _valNative->scToInt(); + + case VAL_INT: + return _valInt; + + case VAL_FLOAT: + return (int)_valFloat; + + case VAL_STRING: + return atoi(_valString); + + default: + return defaultVal; + } +} + + +////////////////////////////////////////////////////////////////////////// +double ScValue::getFloat(double defaultVal) { + if (_type == VAL_VARIABLE_REF) { + return _valRef->getFloat(); + } + + switch (_type) { + case VAL_BOOL: + return _valBool ? 1.0f : 0.0f; + + case VAL_NATIVE: + return _valNative->scToFloat(); + + case VAL_INT: + return (double)_valInt; + + case VAL_FLOAT: + return _valFloat; + + case VAL_STRING: + return atof(_valString); + + default: + return defaultVal; + } +} + +////////////////////////////////////////////////////////////////////////// +void *ScValue::getMemBuffer() { + if (_type == VAL_VARIABLE_REF) { + return _valRef->getMemBuffer(); + } + + if (_type == VAL_NATIVE) { + return _valNative->scToMemBuffer(); + } else { + return (void *)NULL; + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *ScValue::getString() { + if (_type == VAL_VARIABLE_REF) { + return _valRef->getString(); + } + + switch (_type) { + case VAL_OBJECT: + setStringVal("[object]"); + break; + + case VAL_NULL: + setStringVal("[null]"); + break; + + case VAL_NATIVE: { + const char *strVal = _valNative->scToString(); + setStringVal(strVal); + return strVal; + break; + } + + case VAL_BOOL: + setStringVal(_valBool ? "yes" : "no"); + break; + + case VAL_INT: { + char dummy[50]; + sprintf(dummy, "%d", _valInt); + setStringVal(dummy); + break; + } + + case VAL_FLOAT: { + char dummy[50]; + sprintf(dummy, "%f", _valFloat); + setStringVal(dummy); + break; + } + + case VAL_STRING: + break; + + default: + setStringVal(""); + } + + return _valString; +} + + +////////////////////////////////////////////////////////////////////////// +BaseScriptable *ScValue::getNative() { + if (_type == VAL_VARIABLE_REF) { + return _valRef->getNative(); + } + + if (_type == VAL_NATIVE) { + return _valNative; + } else { + return NULL; + } +} + + +////////////////////////////////////////////////////////////////////////// +TValType ScValue::getType() { + return _type; +} + + +////////////////////////////////////////////////////////////////////////// +void ScValue::copy(ScValue *orig, bool copyWhole) { + _gameRef = orig->_gameRef; + + if (_valNative && !_persistent) { + _valNative->_refCount--; + if (_valNative->_refCount <= 0) { + if (_valNative != orig->_valNative) { + delete _valNative; + } + _valNative = NULL; + } + } + + if (orig->_type == VAL_VARIABLE_REF && orig->_valRef && copyWhole) { + orig = orig->_valRef; + } + + cleanup(true); + + _type = orig->_type; + _valBool = orig->_valBool; + _valInt = orig->_valInt; + _valFloat = orig->_valFloat; + setStringVal(orig->_valString); + + _valRef = orig->_valRef; + _persistent = orig->_persistent; + + _valNative = orig->_valNative; + if (_valNative && !_persistent) { + _valNative->_refCount++; + } +//!!!! ref->native++ + + // copy properties + if (orig->_type == VAL_OBJECT && orig->_valObject.size() > 0) { + orig->_valIter = orig->_valObject.begin(); + while (orig->_valIter != orig->_valObject.end()) { + _valObject[orig->_valIter->_key] = new ScValue(_gameRef); + _valObject[orig->_valIter->_key]->copy(orig->_valIter->_value); + orig->_valIter++; + } + } else { + _valObject.clear(); + } +} + + +////////////////////////////////////////////////////////////////////////// +void ScValue::setValue(ScValue *val) { + if (val->_type == VAL_VARIABLE_REF) { + setValue(val->_valRef); + return; + } + + // if being assigned a simple type, preserve native state + if (_type == VAL_NATIVE && (val->_type == VAL_INT || val->_type == VAL_STRING || val->_type == VAL_BOOL)) { + switch (val->_type) { + case VAL_INT: + _valNative->scSetInt(val->getInt()); + break; + case VAL_FLOAT: + _valNative->scSetFloat(val->getFloat()); + break; + case VAL_BOOL: + _valNative->scSetBool(val->getBool()); + break; + case VAL_STRING: + _valNative->scSetString(val->getString()); + break; + default: + warning("ScValue::setValue - unhandled enum"); + break; + } + } + // otherwise just copy everything + else { + copy(val); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool ScValue::persist(BasePersistenceManager *persistMgr) { + persistMgr->transfer(TMEMBER(_gameRef)); + + persistMgr->transfer(TMEMBER(_persistent)); + persistMgr->transfer(TMEMBER(_isConstVar)); + persistMgr->transfer(TMEMBER_INT(_type)); + persistMgr->transfer(TMEMBER(_valBool)); + persistMgr->transfer(TMEMBER(_valFloat)); + persistMgr->transfer(TMEMBER(_valInt)); + persistMgr->transfer(TMEMBER(_valNative)); + + int size; + const char *str; + if (persistMgr->getIsSaving()) { + size = _valObject.size(); + persistMgr->transfer("", &size); + _valIter = _valObject.begin(); + while (_valIter != _valObject.end()) { + str = _valIter->_key.c_str(); + persistMgr->transfer("", &str); + persistMgr->transfer("", &_valIter->_value); + + _valIter++; + } + } else { + ScValue *val; + persistMgr->transfer("", &size); + for (int i = 0; i < size; i++) { + persistMgr->transfer("", &str); + persistMgr->transfer("", &val); + + _valObject[str] = val; + delete[] str; + } + } + + persistMgr->transfer(TMEMBER(_valRef)); + persistMgr->transfer(TMEMBER(_valString)); + + /* + FILE* f = fopen("c:\\val.log", "a+"); + switch(_type) + { + case VAL_STRING: + fprintf(f, "str %s\n", _valString); + break; + + case VAL_INT: + fprintf(f, "int %d\n", _valInt); + break; + + case VAL_BOOL: + fprintf(f, "bool %d\n", _valBool); + break; + + case VAL_NULL: + fprintf(f, "null\n"); + break; + + case VAL_NATIVE: + fprintf(f, "native\n"); + break; + + case VAL_VARIABLE_REF: + fprintf(f, "ref\n"); + break; + + case VAL_OBJECT: + fprintf(f, "obj\n"); + break; + + case VAL_FLOAT: + fprintf(f, "float\n"); + break; + + } + fclose(f); + */ + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScValue::saveAsText(BaseDynamicBuffer *buffer, int indent) { + _valIter = _valObject.begin(); + while (_valIter != _valObject.end()) { + buffer->putTextIndent(indent, "PROPERTY {\n"); + buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", _valIter->_key.c_str()); + buffer->putTextIndent(indent + 2, "VALUE=\"%s\"\n", _valIter->_value->getString()); + buffer->putTextIndent(indent, "}\n\n"); + + _valIter++; + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +// -1 ... left is less, 0 ... equals, 1 ... left is greater +int ScValue::compare(ScValue *val1, ScValue *val2) { + // both natives? + if (val1->isNative() && val2->isNative()) { + // same class? + if (strcmp(val1->getNative()->getClassName(), val2->getNative()->getClassName()) == 0) { + return val1->getNative()->scCompare(val2->getNative()); + } else { + return strcmp(val1->getString(), val2->getString()); + } + } + + // both objects? + if (val1->isObject() && val2->isObject()) { + return -1; + } + + + // null states + if (val1->isNULL() && !val2->isNULL()) { + return -1; + } else if (!val1->isNULL() && val2->isNULL()) { + return 1; + } else if (val1->isNULL() && val2->isNULL()) { + return 0; + } + + // one of them is string? convert both to string + if (val1->isString() || val2->isString()) { + return strcmp(val1->getString(), val2->getString()); + } + + // one of them is float? + if (val1->isFloat() || val2->isFloat()) { + if (val1->getFloat() < val2->getFloat()) { + return -1; + } else if (val1->getFloat() > val2->getFloat()) { + return 1; + } else { + return 0; + } + } + + // otherwise compare as int's + if (val1->getInt() < val2->getInt()) { + return -1; + } else if (val1->getInt() > val2->getInt()) { + return 1; + } else { + return 0; + } +} + + +////////////////////////////////////////////////////////////////////////// +int ScValue::compareStrict(ScValue *val1, ScValue *val2) { + if (val1->getTypeTolerant() != val2->getTypeTolerant()) { + return -1; + } else { + return ScValue::compare(val1, val2); + } +} + +////////////////////////////////////////////////////////////////////////// +bool ScValue::setProperty(const char *propName, int value) { + ScValue *val = new ScValue(_gameRef, value); + bool ret = DID_SUCCEED(setProp(propName, val)); + delete val; + return ret; +} + +////////////////////////////////////////////////////////////////////////// +bool ScValue::setProperty(const char *propName, const char *value) { + ScValue *val = new ScValue(_gameRef, value); + bool ret = DID_SUCCEED(setProp(propName, val)); + delete val; + return ret; +} + +////////////////////////////////////////////////////////////////////////// +bool ScValue::setProperty(const char *propName, double value) { + ScValue *val = new ScValue(_gameRef, value); + bool ret = DID_SUCCEED(setProp(propName, val)); + delete val; + return ret; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScValue::setProperty(const char *propName, bool value) { + ScValue *val = new ScValue(_gameRef, value); + bool ret = DID_SUCCEED(setProp(propName, val)); + delete val; + return ret; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScValue::setProperty(const char *propName) { + ScValue *val = new ScValue(_gameRef); + bool ret = DID_SUCCEED(setProp(propName, val)); + delete val; + return ret; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/scriptables/script_value.h b/engines/wintermute/base/scriptables/script_value.h new file mode 100644 index 0000000000..bf7d9cd8a1 --- /dev/null +++ b/engines/wintermute/base/scriptables/script_value.h @@ -0,0 +1,113 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_SCVALUE_H +#define WINTERMUTE_SCVALUE_H + + +#include "engines/wintermute/base/base.h" +#include "engines/wintermute/persistent.h" +#include "engines/wintermute/base/scriptables/dcscript.h" // Added by ClassView +#include "common/str.h" + +namespace Wintermute { + +class ScScript; +class BaseScriptable; + +class ScValue : public BaseClass { +public: + static int compare(ScValue *val1, ScValue *val2); + static int compareStrict(ScValue *val1, ScValue *val2); + TValType getTypeTolerant(); + void cleanup(bool ignoreNatives = false); + DECLARE_PERSISTENT(ScValue, BaseClass) + + bool _isConstVar; + bool saveAsText(BaseDynamicBuffer *buffer, int indent); + void setValue(ScValue *val); + bool _persistent; + bool propExists(const char *name); + void copy(ScValue *orig, bool copyWhole = false); + void setStringVal(const char *val); + TValType getType(); + bool getBool(bool defaultVal = false); + int getInt(int defaultVal = 0); + double getFloat(double defaultVal = 0.0f); + const char *getString(); + void *getMemBuffer(); + BaseScriptable *getNative(); + bool deleteProp(const char *name); + void deleteProps(); + void CleanProps(bool includingNatives); + void setBool(bool val); + void setInt(int val); + void setFloat(double val); + void setString(const char *val); + void setString(const Common::String &val); + void setNULL(); + void setNative(BaseScriptable *val, bool persistent = false); + void setObject(); + void setReference(ScValue *val); + bool isNULL(); + bool isNative(); + bool isString(); + bool isBool(); + bool isFloat(); + bool isInt(); + bool isObject(); + bool setProp(const char *name, ScValue *val, bool copyWhole = false, bool setAsConst = false); + ScValue *getProp(const char *name); + BaseScriptable *_valNative; + ScValue *_valRef; +private: + bool _valBool; + int _valInt; + double _valFloat; + char *_valString; +public: + TValType _type; + ScValue(BaseGame *inGame); + ScValue(BaseGame *inGame, bool Val); + ScValue(BaseGame *inGame, int Val); + ScValue(BaseGame *inGame, double Val); + ScValue(BaseGame *inGame, const char *Val); + virtual ~ScValue(); + Common::HashMap<Common::String, ScValue *> _valObject; + Common::HashMap<Common::String, ScValue *>::iterator _valIter; + + bool setProperty(const char *propName, int value); + bool setProperty(const char *propName, const char *value); + bool setProperty(const char *propName, double value); + bool setProperty(const char *propName, bool value); + bool setProperty(const char *propName); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/sound/base_sound.cpp b/engines/wintermute/base/sound/base_sound.cpp new file mode 100644 index 0000000000..00d07cd3c2 --- /dev/null +++ b/engines/wintermute/base/sound/base_sound.cpp @@ -0,0 +1,292 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/sound/base_sound.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/sound/base_sound_manager.h" +#include "engines/wintermute/base/sound/base_sound_buffer.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(BaseSound, false) + +BaseSound::BaseSound(BaseGame *inGame) : BaseClass(inGame) { + _sound = NULL; + _soundFilename = ""; + + _soundType = Audio::Mixer::kSFXSoundType; + _soundStreamed = false; + _soundLooping = false; + _soundPlaying = false; + _soundPaused = false; + _soundFreezePaused = false; + _soundPosition = 0; + _soundPrivateVolume = 0; + _soundLoopStart = 0; + + _sFXType = SFX_NONE; + _sFXParam1 = _sFXParam2 = _sFXParam3 = _sFXParam4 = 0; +} + +BaseSound::~BaseSound() { + if (_sound) { + _gameRef->_soundMgr->removeSound(_sound); + } + _sound = NULL; +} + +bool BaseSound::setSound(const Common::String &filename, Audio::Mixer::SoundType type, bool streamed) { + if (_sound) { + _gameRef->_soundMgr->removeSound(_sound); + _sound = NULL; + } + _soundFilename = Common::String(); // Set empty + + _sound = _gameRef->_soundMgr->addSound(filename, type, streamed); + if (_sound) { + _soundFilename = filename; + + _soundType = type; + _soundStreamed = streamed; + + return STATUS_OK; + } else { + return STATUS_FAILED; + } +} + +bool BaseSound::setSoundSimple() { + _sound = _gameRef->_soundMgr->addSound(_soundFilename, _soundType, _soundStreamed); + if (_sound) { + if (_soundPosition) { + _sound->setPosition(_soundPosition); + } + _sound->setLooping(_soundLooping); + _sound->setPrivateVolume(_soundPrivateVolume); + _sound->setLoopStart(_soundLoopStart); + _sound->_freezePaused = _soundFreezePaused; + if (_soundPlaying) { + return _sound->resume(); + } else { + return STATUS_OK; + } + } else { + return STATUS_FAILED; + } +} + +uint32 BaseSound::getLength() { + if (_sound) { + return _sound->getLength(); + } else { + return 0; + } +} + +bool BaseSound::play(bool looping) { + if (_sound) { + _soundPaused = false; + return _sound->play(looping, _soundPosition); + } else { + return STATUS_FAILED; + } +} + +bool BaseSound::stop() { + if (_sound) { + _soundPaused = false; + return _sound->stop(); + } else { + return STATUS_FAILED; + } +} + +bool BaseSound::pause(bool freezePaused) { + if (_sound) { + _soundPaused = true; + if (freezePaused) { + _sound->_freezePaused = true; + } + return _sound->pause(); + } else { + return STATUS_FAILED; + } +} + +bool BaseSound::resume() { + if (_sound && _soundPaused) { + _soundPaused = false; + return _sound->resume(); + } else { + return STATUS_FAILED; + } +} + +bool BaseSound::persist(BasePersistenceManager *persistMgr) { + if (persistMgr->getIsSaving() && _sound) { + _soundPlaying = _sound->isPlaying(); + _soundLooping = _sound->_looping; + _soundPrivateVolume = _sound->_privateVolume; + if (_soundPlaying) { + _soundPosition = _sound->getPosition(); + } + _soundLoopStart = _sound->_loopStart; + _soundFreezePaused = _sound->_freezePaused; + } + + if (persistMgr->getIsSaving()) { + _sFXType = SFX_NONE; + _sFXParam1 = _sFXParam2 = _sFXParam3 = _sFXParam4 = 0; + } + + persistMgr->transfer(TMEMBER(_gameRef)); + + persistMgr->transfer(TMEMBER(_soundFilename)); + persistMgr->transfer(TMEMBER(_soundLooping)); + persistMgr->transfer(TMEMBER(_soundPaused)); + persistMgr->transfer(TMEMBER(_soundFreezePaused)); + persistMgr->transfer(TMEMBER(_soundPlaying)); + persistMgr->transfer(TMEMBER(_soundPosition)); + persistMgr->transfer(TMEMBER(_soundPrivateVolume)); + persistMgr->transfer(TMEMBER(_soundStreamed)); + persistMgr->transfer(TMEMBER_INT(_soundType)); + persistMgr->transfer(TMEMBER(_soundLoopStart)); + + return STATUS_OK; +} + +bool BaseSound::isPlaying() { + return _sound && _sound->isPlaying(); +} + +bool BaseSound::isPaused() { + return _sound && _soundPaused; +} + +bool BaseSound::setPositionTime(uint32 time) { + if (!_sound) { + return STATUS_FAILED; + } + _soundPosition = time; + bool ret = _sound->setPosition(_soundPosition); + if (_sound->isPlaying()) { + _soundPosition = 0; + } + return ret; +} + +uint32 BaseSound::getPositionTime() { + if (!_sound) { + return 0; + } + + if (!_sound->isPlaying()) { + return 0; + } else { + return _sound->getPosition(); + } +} + +bool BaseSound::setVolumePercent(int percent) { + if (!_sound) { + return STATUS_FAILED; + } else { + return _sound->setPrivateVolume(percent * 255 / 100); + } +} + +bool BaseSound::setVolume(int volume) { + if (!_sound) { + return STATUS_FAILED; + } else { + return _sound->setPrivateVolume(volume); + } +} + +bool BaseSound::setPrivateVolume(int volume) { + if (!_sound) { + return STATUS_FAILED; + } else { + _sound->_privateVolume = volume; + return STATUS_OK; + } +} + +int BaseSound::getVolumePercent() { + if (!_sound) { + return 0; + } else { + return _sound->_privateVolume * 100 / 255; + } +} + +int BaseSound::getVolume() { + if (!_sound) { + return 0; + } else { + return _sound->_privateVolume; + } +} + +bool BaseSound::setLoopStart(uint32 pos) { + if (!_sound) { + return STATUS_FAILED; + } else { + _sound->setLoopStart(pos); + return STATUS_OK; + } +} + +bool BaseSound::setPan(float pan) { + if (_sound) { + return _sound->setPan(pan); + } else { + return STATUS_FAILED; + } +} + +bool BaseSound::applyFX(TSFXType type, float param1, float param2, float param3, float param4) { + if (!_sound) { + return STATUS_OK; + } + + if (type != _sFXType || param1 != _sFXParam1 || param2 != _sFXParam2 || param3 != _sFXParam3 || param4 != _sFXParam4) { + bool ret = _sound->applyFX(type, param1, param2, param3, param4); + + _sFXType = type; + _sFXParam1 = param1; + _sFXParam2 = param2; + _sFXParam3 = param3; + _sFXParam4 = param4; + + return ret; + } + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/sound/base_sound.h b/engines/wintermute/base/sound/base_sound.h new file mode 100644 index 0000000000..637061b7cc --- /dev/null +++ b/engines/wintermute/base/sound/base_sound.h @@ -0,0 +1,87 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_SOUND_H +#define WINTERMUTE_BASE_SOUND_H + +#include "engines/wintermute/base/base.h" +#include "engines/wintermute/dctypes.h" // Added by ClassView +#include "engines/wintermute/persistent.h" +#include "audio/mixer.h" + +namespace Wintermute { + +class BaseSoundBuffer; +class BaseSound : public BaseClass { +public: + bool setPan(float pan); + int getVolume(); + int getVolumePercent(); + bool setVolumePercent(int percent); + bool setVolume(int volume); + bool setPrivateVolume(int volume); + bool setLoopStart(uint32 pos); + uint32 getPositionTime(); + bool setPositionTime(uint32 time); + bool isPlaying(); + bool isPaused(); + DECLARE_PERSISTENT(BaseSound, BaseClass) + bool resume(); + bool pause(bool freezePaused = false); + bool stop(); + bool play(bool looping = false); + uint32 getLength(); + const char *getFilename() { return _soundFilename.c_str(); } + bool setSoundSimple(); + bool setSound(const Common::String &filename, Audio::Mixer::SoundType type = Audio::Mixer::kSFXSoundType, bool streamed = false); + BaseSound(BaseGame *inGame); + virtual ~BaseSound(); + + bool applyFX(TSFXType type = SFX_NONE, float param1 = 0, float param2 = 0, float param3 = 0, float param4 = 0); +private: + Common::String _soundFilename; + bool _soundStreamed; + Audio::Mixer::SoundType _soundType; + int _soundPrivateVolume; + uint32 _soundLoopStart; + uint32 _soundPosition; + bool _soundPlaying; + bool _soundLooping; + bool _soundPaused; + bool _soundFreezePaused; + TSFXType _sFXType; + float _sFXParam1; + float _sFXParam2; + float _sFXParam3; + float _sFXParam4; + BaseSoundBuffer *_sound; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/sound/base_sound_buffer.cpp b/engines/wintermute/base/sound/base_sound_buffer.cpp new file mode 100644 index 0000000000..d2b265a254 --- /dev/null +++ b/engines/wintermute/base/sound/base_sound_buffer.cpp @@ -0,0 +1,295 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/sound/base_sound_manager.h" +#include "engines/wintermute/base/sound/base_sound_buffer.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/wintermute.h" +#include "audio/audiostream.h" +#include "audio/mixer.h" +#include "audio/decoders/vorbis.h" +#include "audio/decoders/wave.h" +#include "audio/decoders/raw.h" +#include "common/system.h" +#include "common/substream.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +#define MAX_NONSTREAMED_FILE_SIZE 1024*1024 + +////////////////////////////////////////////////////////////////////////// +BaseSoundBuffer::BaseSoundBuffer(BaseGame *inGame) : BaseClass(inGame) { + _stream = NULL; + _handle = NULL; +// _sync = NULL; + + _streamed = false; + _filename = ""; + _file = NULL; + _privateVolume = 255; + _volume = 255; + + _looping = false; + _loopStart = 0; + _startPos = 0; + + _type = Audio::Mixer::kSFXSoundType; + + _freezePaused = false; +} + + +////////////////////////////////////////////////////////////////////////// +BaseSoundBuffer::~BaseSoundBuffer() { + stop(); + + if (_handle) { + g_system->getMixer()->stopHandle(*_handle); + delete _handle; + _handle = NULL; + } + delete _stream; + _stream = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +void BaseSoundBuffer::setStreaming(bool streamed, uint32 numBlocks, uint32 blockSize) { + _streamed = streamed; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSoundBuffer::loadFromFile(const Common::String &filename, bool forceReload) { + debugC(kWintermuteDebugAudio, "BSoundBuffer::LoadFromFile(%s,%d)", filename.c_str(), forceReload); + + // Load a file, but avoid having the File-manager handle the disposal of it. + _file = BaseFileManager::getEngineInstance()->openFile(filename, true, false); + if (!_file) { + _gameRef->LOG(0, "Error opening sound file '%s'", filename.c_str()); + return STATUS_FAILED; + } + Common::String strFilename(filename); + strFilename.toLowercase(); + if (strFilename.hasSuffix(".ogg")) { + _stream = Audio::makeVorbisStream(_file, DisposeAfterUse::YES); + } else if (strFilename.hasSuffix(".wav")) { + int waveSize, waveRate; + byte waveFlags; + uint16 waveType; + + if (Audio::loadWAVFromStream(*_file, waveSize, waveRate, waveFlags, &waveType)) { + if (waveType == 1) { + // We need to wrap the file in a substream to make sure the size is right. + _file = new Common::SeekableSubReadStream(_file, _file->pos(), waveSize + _file->pos(), DisposeAfterUse::YES); + _stream = Audio::makeRawStream(_file, waveRate, waveFlags, DisposeAfterUse::YES); + } else { + error("BSoundBuffer::LoadFromFile - WAVE not supported yet for %s with type %d", filename.c_str(), waveType); + } + } + } else { + error("BSoundBuffer::LoadFromFile - Unknown filetype for %s", filename.c_str()); + } + if (!_stream) { + return STATUS_FAILED; + } + _filename = filename; + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSoundBuffer::play(bool looping, uint32 startSample) { + if (_handle) { + g_system->getMixer()->stopHandle(*_handle); + delete _handle; + _handle = NULL; + } + // Store the loop-value for save-games. + setLooping(looping); + if (_stream) { + _stream->seek(startSample); + _handle = new Audio::SoundHandle; + if (_looping) { + Audio::AudioStream *loopStream = new Audio::LoopingAudioStream(_stream, 0, DisposeAfterUse::NO); + g_system->getMixer()->playStream(_type, _handle, loopStream, -1, _volume, 0, DisposeAfterUse::YES); + } else { + g_system->getMixer()->playStream(_type, _handle, _stream, -1, _volume, 0, DisposeAfterUse::NO); + } + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +void BaseSoundBuffer::setLooping(bool looping) { + if (isPlaying()) { + // This warning is here, to see if this is ever the case. + warning("BSoundBuffer::SetLooping(%d) - won't change a playing sound", looping); // TODO + } + _looping = looping; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSoundBuffer::resume() { + // If the sound was paused while active: + if (_stream && _handle) { + g_system->getMixer()->pauseHandle(*_handle, false); + } else if (_stream) { // Otherwise we come from a savegame, and thus have no handle + play(_looping, _startPos); + } else { + warning("BaseSoundBuffer::resume - Called without a handle or a stream"); + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSoundBuffer::stop() { + if (_stream && _handle) { + g_system->getMixer()->stopHandle(*_handle); + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSoundBuffer::pause() { + if (_stream && _handle) { + g_system->getMixer()->pauseHandle(*_handle, true); + } + return STATUS_OK; + +} + +////////////////////////////////////////////////////////////////////////// +uint32 BaseSoundBuffer::getLength() { + if (_stream) { + uint32 len = _stream->getLength().msecs(); + return len * 1000; + } + return 0; +} + + +////////////////////////////////////////////////////////////////////////// +void BaseSoundBuffer::setType(Audio::Mixer::SoundType type) { + _type = type; +} + +////////////////////////////////////////////////////////////////////////// +void BaseSoundBuffer::updateVolume() { + setVolume(_privateVolume); +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSoundBuffer::setVolume(int volume) { + _volume = volume * _gameRef->_soundMgr->getMasterVolume() / 255; + if (_stream && _handle) { + byte vol = (byte)(_volume); + g_system->getMixer()->setChannelVolume(*_handle, vol); + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSoundBuffer::setPrivateVolume(int volume) { + _privateVolume = volume; + return setVolume(_privateVolume); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSoundBuffer::isPlaying() { + if (_stream && _handle) { + return _freezePaused || g_system->getMixer()->isSoundHandleActive(*_handle); + } else { + return false; + } +} + + +////////////////////////////////////////////////////////////////////////// +uint32 BaseSoundBuffer::getPosition() { + if (_stream && _handle) { + uint32 pos = g_system->getMixer()->getSoundElapsedTime(*_handle); + return pos; + } + return 0; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSoundBuffer::setPosition(uint32 pos) { + if (isPlaying()) { + warning("BaseSoundBuffer::SetPosition - not implemented for playing sounds yet."); + } + _startPos = pos; + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSoundBuffer::setLoopStart(uint32 pos) { + _loopStart = pos; + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSoundBuffer::setPan(float pan) { + if (_handle) { + g_system->getMixer()->setChannelBalance(*_handle, (int8)(pan * 127)); + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSoundBuffer::applyFX(TSFXType type, float param1, float param2, float param3, float param4) { + // This function was already stubbed out in WME Lite, and thus isn't reimplemented here either. + switch (type) { + case SFX_ECHO: + //warning("BaseSoundBuffer::ApplyFX(SFX_ECHO, %f, %f, %f, %f) - not implemented yet", param1, param2, param3, param4); + break; + + case SFX_REVERB: + //warning("BaseSoundBuffer::ApplyFX(SFX_REVERB, %f, %f, %f, %f) - not implemented yet", param1, param2, param3, param4); + break; + + default: + break; + } + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/sound/base_sound_buffer.h b/engines/wintermute/base/sound/base_sound_buffer.h new file mode 100644 index 0000000000..9c39f4c34b --- /dev/null +++ b/engines/wintermute/base/sound/base_sound_buffer.h @@ -0,0 +1,100 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_SOUNDBUFFER_H +#define WINTERMUTE_BASE_SOUNDBUFFER_H + + +#include "engines/wintermute/base/base.h" +#include "audio/mixer.h" +#include "common/stream.h" + +namespace Audio { +class SeekableAudioStream; +class SoundHandle; +} + +namespace Wintermute { + +class BaseFile; +class BaseSoundBuffer : public BaseClass { +public: + + BaseSoundBuffer(BaseGame *inGame); + virtual ~BaseSoundBuffer(); + + bool pause(); + bool play(bool looping = false, uint32 startSample = 0); + bool resume(); + bool stop(); + bool isPlaying(); + + void setLooping(bool looping); + + uint32 getPosition(); + bool setPosition(uint32 pos); + uint32 getLength(); + + bool setLoopStart(uint32 pos); + uint32 getLoopStart() const { + return _loopStart; + } + + bool setPan(float pan); + bool setPrivateVolume(int colume); + bool setVolume(int colume); + void updateVolume(); + + void setType(Audio::Mixer::SoundType Type); + + bool loadFromFile(const Common::String &filename, bool forceReload = false); + void setStreaming(bool streamed, uint32 numBlocks = 0, uint32 blockSize = 0); + bool applyFX(TSFXType type, float param1, float param2, float param3, float param4); + + //HSTREAM _stream; + //HSYNC _sync; + Audio::SeekableAudioStream *_stream; + Audio::SoundHandle *_handle; + + bool _freezePaused; + uint32 _loopStart; + Audio::Mixer::SoundType _type; + bool _looping; + + int _privateVolume; +private: + uint32 _startPos; + Common::String _filename; + bool _streamed; + Common::SeekableReadStream *_file; + int _volume; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/sound/base_sound_manager.cpp b/engines/wintermute/base/sound/base_sound_manager.cpp new file mode 100644 index 0000000000..7e4a70afb2 --- /dev/null +++ b/engines/wintermute/base/sound/base_sound_manager.cpp @@ -0,0 +1,291 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/sound/base_sound_manager.h" +#include "engines/wintermute/base/base_engine.h" +#include "engines/wintermute/utils/path_util.h" +#include "engines/wintermute/utils/string_util.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/base/sound/base_sound_buffer.h" +#include "engines/wintermute/wintermute.h" +#include "common/config-manager.h" +#include "audio/mixer.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +//IMPLEMENT_PERSISTENT(BaseSoundMgr, true); + +////////////////////////////////////////////////////////////////////////// +BaseSoundMgr::BaseSoundMgr(BaseGame *inGame) : BaseClass(inGame) { + _soundAvailable = false; + _volumeMaster = 255; + _volumeMasterPercent = 100; +} + + +////////////////////////////////////////////////////////////////////////// +BaseSoundMgr::~BaseSoundMgr() { + saveSettings(); + cleanup(); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSoundMgr::cleanup() { + for (uint32 i = 0; i < _sounds.size(); i++) { + delete _sounds[i]; + } + _sounds.clear(); + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +void BaseSoundMgr::saveSettings() { + if (_soundAvailable) { + ConfMan.setInt("master_volume_percent", _volumeMasterPercent); + } +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSoundMgr::initialize() { + _soundAvailable = false; + + if (!g_system->getMixer()->isReady()) { + return STATUS_FAILED; + } + byte volumeMasterPercent = (ConfMan.hasKey("master_volume_percent") ? ConfMan.getInt("master_volume_percent") : 100); + setMasterVolumePercent(volumeMasterPercent); + _soundAvailable = true; + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +BaseSoundBuffer *BaseSoundMgr::addSound(const Common::String &filename, Audio::Mixer::SoundType type, bool streamed) { + if (!_soundAvailable) { + return NULL; + } + + BaseSoundBuffer *sound; + + Common::String useFilename = filename; + // try to switch WAV to OGG file (if available) + AnsiString ext = PathUtil::getExtension(filename); + if (StringUtil::compareNoCase(ext, "wav")) { + AnsiString path = PathUtil::getDirectoryName(filename); + AnsiString name = PathUtil::getFileNameWithoutExtension(filename); + + AnsiString newFile = PathUtil::combine(path, name + "ogg"); + if (BaseFileManager::getEngineInstance()->hasFile(newFile)) { + useFilename = newFile; + } + } + + sound = new BaseSoundBuffer(_gameRef); + if (!sound) { + return NULL; + } + + sound->setStreaming(streamed); + sound->setType(type); + + + bool res = sound->loadFromFile(useFilename); + if (DID_FAIL(res)) { + _gameRef->LOG(res, "Error loading sound '%s'", useFilename.c_str()); + delete sound; + return NULL; + } + + // Make sure the master-volume is applied to the sound. + sound->updateVolume(); + + // register sound + _sounds.push_back(sound); + + return sound; + + return NULL; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSoundMgr::addSound(BaseSoundBuffer *sound, Audio::Mixer::SoundType type) { + if (!sound) { + return STATUS_FAILED; + } + + // Make sure the master-volume is applied to the sound. + sound->updateVolume(); + + // register sound + _sounds.push_back(sound); + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSoundMgr::removeSound(BaseSoundBuffer *sound) { + for (uint32 i = 0; i < _sounds.size(); i++) { + if (_sounds[i] == sound) { + delete _sounds[i]; + _sounds.remove_at(i); + return STATUS_OK; + } + } + + return STATUS_FAILED; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSoundMgr::setVolume(Audio::Mixer::SoundType type, int volume) { + if (!_soundAvailable) { + return STATUS_OK; + } + + switch (type) { + case Audio::Mixer::kSFXSoundType: + ConfMan.setInt("sfx_volume", volume); + break; + case Audio::Mixer::kSpeechSoundType: + ConfMan.setInt("speech_volume", volume); + break; + case Audio::Mixer::kMusicSoundType: + ConfMan.setInt("music_volume", volume); + break; + case Audio::Mixer::kPlainSoundType: + error("Plain sound type shouldn't be used in WME"); + } + g_engine->syncSoundSettings(); + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSoundMgr::setVolumePercent(Audio::Mixer::SoundType type, byte percent) { + return setVolume(type, percent * 255 / 100); +} + + +////////////////////////////////////////////////////////////////////////// +byte BaseSoundMgr::getVolumePercent(Audio::Mixer::SoundType type) { + int volume = 0; + + switch (type) { + case Audio::Mixer::kSFXSoundType: + case Audio::Mixer::kSpeechSoundType: + case Audio::Mixer::kMusicSoundType: + volume = g_system->getMixer()->getVolumeForSoundType(type); + break; + default: + error("Sound-type not set"); + break; + } + + return (byte)(volume * 100 / 255); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSoundMgr::setMasterVolume(byte value) { + // This function intentionally doesn't touch _volumeMasterPercent, + // as that variable keeps track of what the game actually wanted, + // and this gives a close approximation, while letting the game + // be none the wiser about round-off-errors. This function should thus + // ONLY be called by setMasterVolumePercent. + _volumeMaster = value; + for (uint32 i = 0; i < _sounds.size(); i++) { + _sounds[i]->updateVolume(); + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSoundMgr::setMasterVolumePercent(byte percent) { + _volumeMasterPercent = percent; + setMasterVolume((int)ceil(percent * 255.0 / 100.0)); + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +byte BaseSoundMgr::getMasterVolumePercent() { + return _volumeMasterPercent; +} + +////////////////////////////////////////////////////////////////////////// +byte BaseSoundMgr::getMasterVolume() { + return (byte)_volumeMaster; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSoundMgr::pauseAll(bool includingMusic) { + + for (uint32 i = 0; i < _sounds.size(); i++) { + if (_sounds[i]->isPlaying() && (_sounds[i]->_type != Audio::Mixer::kMusicSoundType || includingMusic)) { + _sounds[i]->pause(); + _sounds[i]->_freezePaused = true; + } + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSoundMgr::resumeAll() { + + for (uint32 i = 0; i < _sounds.size(); i++) { + if (_sounds[i]->_freezePaused) { + _sounds[i]->resume(); + _sounds[i]->_freezePaused = false; + } + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +float BaseSoundMgr::posToPan(int x, int y) { + float relPos = (float)x / ((float)_gameRef->_renderer->_width); + + float minPan = -0.7f; + float maxPan = 0.7f; + + return minPan + relPos * (maxPan - minPan); +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/sound/base_sound_manager.h b/engines/wintermute/base/sound/base_sound_manager.h new file mode 100644 index 0000000000..1ee3c13fdb --- /dev/null +++ b/engines/wintermute/base/sound/base_sound_manager.h @@ -0,0 +1,70 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_SOUNDMGR_H +#define WINTERMUTE_BASE_SOUNDMGR_H + +#include "engines/wintermute/coll_templ.h" +#include "engines/wintermute/base/base.h" +#include "audio/mixer.h" +#include "common/array.h" + +namespace Wintermute { +class BaseSoundBuffer; +class BaseSoundMgr : public BaseClass { +public: + float posToPan(int x, int y); + bool resumeAll(); + bool pauseAll(bool includingMusic = true); + bool cleanup(); + //DECLARE_PERSISTENT(BaseSoundMgr, BaseClass); + byte getMasterVolumePercent(); + byte getMasterVolume(); + bool setMasterVolumePercent(byte percent); + byte getVolumePercent(Audio::Mixer::SoundType type); + bool setVolumePercent(Audio::Mixer::SoundType type, byte percent); + bool setVolume(Audio::Mixer::SoundType type, int volume); + uint32 _volumeOriginal; + int _volumeMaster; + bool removeSound(BaseSoundBuffer *sound); + BaseSoundBuffer *addSound(const Common::String &filename, Audio::Mixer::SoundType type = Audio::Mixer::kSFXSoundType, bool streamed = false); + bool addSound(BaseSoundBuffer *sound, Audio::Mixer::SoundType type = Audio::Mixer::kSFXSoundType); + bool initialize(); + bool _soundAvailable; + BaseSoundMgr(BaseGame *inGame); + virtual ~BaseSoundMgr(); + Common::Array<BaseSoundBuffer *> _sounds; + void saveSettings(); +private: + int _volumeMasterPercent; // Necessary to avoid round-offs. + bool setMasterVolume(byte percent); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/coll_templ.h b/engines/wintermute/coll_templ.h new file mode 100644 index 0000000000..493ea07015 --- /dev/null +++ b/engines/wintermute/coll_templ.h @@ -0,0 +1,86 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_COLL_TEMPL_H +#define WINTERMUTE_COLL_TEMPL_H + +#include "common/array.h" +#include "engines/wintermute/base/base_persistence_manager.h" + +namespace Wintermute { + +// Basically Common::Array with peristence-support. +template<typename TYPE> +class BaseArray : public Common::Array<TYPE> { +public: +// TODO: Might want to make sure that destructors are called when replacing/deleting/getting destructed + bool persist(BasePersistenceManager *persistMgr) { + int j; + if (persistMgr->getIsSaving()) { + j = Common::Array<TYPE>::size(); + persistMgr->transfer("ArraySize", &j); + typename Common::Array<TYPE>::const_iterator it = Common::Array<TYPE>::begin(); + for (; it != Common::Array<TYPE>::end(); ++it) { + TYPE obj = *it; + persistMgr->transfer("", &obj); + } + } else { + Common::Array<TYPE>::clear(); + persistMgr->transfer("ArraySize", &j); + for (int i = 0; i < j; i++) { + TYPE obj; + persistMgr->transfer("", &obj); + add(obj); + } + } + return true; + } + int add(TYPE newElement) { + Common::Array<TYPE>::push_back(newElement); + return Common::Array<TYPE>::size() - 1; + } + void remove_at(uint32 idx) { + Common::Array<TYPE>::remove_at(idx); + } + void remove_at(uint32 idx, uint32 num) { + while (num) { + if (idx >= Common::Array<TYPE>::size()) { + break; + } + Common::Array<TYPE>::remove_at(idx); + } + } + template<typename T2> + void copy(const BaseArray<T2> &src) { + Common::Array<TYPE>::insert_at(0, src); + } +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/dcgf.h b/engines/wintermute/dcgf.h new file mode 100644 index 0000000000..0fbb1c6b29 --- /dev/null +++ b/engines/wintermute/dcgf.h @@ -0,0 +1,51 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_DCGF_H +#define WINTERMUTE_DCGF_H + + +////////////////////////////////////////////////////////////////////////// +#define DCGF_VER_MAJOR 1 +#define DCGF_VER_MINOR 2 +#define DCGF_VER_BUILD 1 +#define DCGF_VER_SUFFIX "beta" +#define DCGF_VER_BETA true + +#define DCGF_NAME "WME Lite" +#define DCGF_MAGIC 0xDEC0ADDE + +// minimal saved game version we support +#define SAVEGAME_VER_MAJOR 1 +#define SAVEGAME_VER_MINOR 1 +#define SAVEGAME_VER_BUILD 1 +////////////////////////////////////////////////////////////////////////// + +#define COMPRESSED_FILE_MAGIC 0x504D435A // ZCMP + +#endif diff --git a/engines/wintermute/dctypes.h b/engines/wintermute/dctypes.h new file mode 100644 index 0000000000..bd4966eb6b --- /dev/null +++ b/engines/wintermute/dctypes.h @@ -0,0 +1,225 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_DCTYPES_H +#define WINTERMUTE_DCTYPES_H + + +#include "common/str.h" +#include "common/list.h" +#include "common/array.h" + +namespace Wintermute { + +//typedef std::string AnsiString; +//typedef std::string Utf8String; +//typedef std::wstring WideString; +typedef Common::String AnsiString; +typedef Common::String Utf8String; +typedef Common::String WideString; // NB: Not actually true I presume. + +typedef Common::List<WideString> WideStringList; +typedef Common::List<AnsiString> AnsiStringList; + +typedef Common::Array<WideString> WideStringArray; +typedef Common::Array<AnsiString> AnsiStringArray; + + +enum TGameState { + GAME_RUNNING, + GAME_FROZEN, + GAME_SEMI_FROZEN +}; + + +enum TImageType { + IMG_PALETTED8, + IMG_TRUECOLOR +}; + + +enum TTextAlign { + TAL_LEFT = 0, + TAL_RIGHT, + TAL_CENTER, + NUM_TEXT_ALIGN +}; + + +enum TVerticalAlign { + VAL_TOP = 0, + VAL_CENTER, + VAL_BOTTOM, + NUM_VERTICAL_ALIGN +}; + + +enum TDirection { + DI_UP = 0, + DI_UPRIGHT = 1, + DI_RIGHT = 2, + DI_DOWNRIGHT = 3, + DI_DOWN = 4, + DI_DOWNLEFT = 5, + DI_LEFT = 6, + DI_UPLEFT = 7, + NUM_DIRECTIONS = 8, + DI_NONE = 9 +}; + +enum TEventType { + EVENT_NONE = 0, + EVENT_INIT = 1, + EVENT_SHUTDOWN = 2, + EVENT_LEFT_CLICK = 3, + EVENT_RIGHT_CLICK = 4, + EVENT_MIDDLE_CLICK = 5, + EVENT_LEFT_DBLCLICK = 6, + EVENT_PRESS = 7, + EVENT_IDLE = 8, + EVENT_MOUSE_OVER = 9, + EVENT_LEFT_RELEASE = 10, + EVENT_RIGHT_RELEASE = 11, + EVENT_MIDDLE_RELEASE = 12, + NUM_EVENTS +}; + +enum TUIObjectType { + UI_UNKNOWN, + UI_BUTTON, + UI_WINDOW, + UI_STATIC, + UI_EDIT, + UI_HTML, + UI_CUSTOM +}; + + +enum TRendererState { + RSTATE_3D, + RSTATE_2D, + RSTATE_LINES, + RSTATE_NONE +}; + + +enum TDynamicConstructor { + DYNAMIC_CONSTRUCTOR +}; + + +enum TVideoMode { + VIDEO_WINDOW, + VIDEO_FULLSCREEN, + VIDEO_ANY +}; + + +enum TVideoPlayback { + VID_PLAY_POS = 0, + VID_PLAY_STRETCH = 1, + VID_PLAY_CENTER = 2 +}; + + +enum TMouseEvent { + MOUSE_CLICK, + MOUSE_RELEASE, + MOUSE_DBLCLICK +}; + + +enum TMouseButton { + MOUSE_BUTTON_LEFT, + MOUSE_BUTTON_RIGHT, + MOUSE_BUTTON_MIDDLE +}; + + +enum TTransMgrState { + TRANS_MGR_RUNNING, + TRANS_MGR_READY +}; + + +enum TTransitionType { + TRANSITION_NONE = 0, + TRANSITION_FADE_OUT = 1, + TRANSITION_FADE_IN = 2, + NUM_TRANSITION_TYPES +}; + + +enum TWindowMode { + WINDOW_NORMAL, + WINDOW_EXCLUSIVE, + WINDOW_SYSTEM_EXCLUSIVE +}; + +enum TSFXType { + SFX_NONE, + SFX_ECHO, + SFX_REVERB +}; + + +enum TSpriteCacheType { + CACHE_ALL, + CACHE_HALF +}; + +enum TTextEncoding { + TEXT_ANSI = 0, + TEXT_UTF8 = 1, + NUM_TEXT_ENCODINGS +}; + +enum TSpriteBlendMode { + BLEND_UNKNOWN = -1, + BLEND_NORMAL = 0, + BLEND_ADDITIVE = 1, + BLEND_SUBTRACTIVE = 2, + NUM_BLEND_MODES +}; + +enum TTTSType { + TTS_CAPTION = 0, + TTS_TALK, + TTS_KEYPRESS +}; + +enum TShadowType { + SHADOW_NONE = 0, + SHADOW_SIMPLE = 1, + SHADOW_FLAT = 2, + SHADOW_STENCIL = 3 +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/debugger.cpp b/engines/wintermute/debugger.cpp new file mode 100644 index 0000000000..1160a16d37 --- /dev/null +++ b/engines/wintermute/debugger.cpp @@ -0,0 +1,48 @@ +/* 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 "engines/wintermute/debugger.h" +#include "engines/wintermute/wintermute.h" +#include "engines/wintermute/base/base_game.h" + +namespace Wintermute { + +Console::Console(WintermuteEngine *vm) : GUI::Debugger(), _engineRef(vm) { + DCmd_Register("show_fps", WRAP_METHOD(Console, Cmd_ShowFps)); +} + +Console::~Console(void) { + +} + +bool Console::Cmd_ShowFps(int argc, const char **argv) { + if (argc > 1) { + if (Common::String(argv[1]) == "true") { + _engineRef->_game->_debugShowFPS = true; + } else if (Common::String(argv[1]) == "false") { + _engineRef->_game->_debugShowFPS = false; + } + } + return true; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/debugger.h b/engines/wintermute/debugger.h new file mode 100644 index 0000000000..069980385e --- /dev/null +++ b/engines/wintermute/debugger.h @@ -0,0 +1,43 @@ +/* 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 WINTERMUTE_DEBUGGER_H +#define WINTERMUTE_DEBUGGER_H + +#include "gui/debugger.h" + +namespace Wintermute { + +class WintermuteEngine; +class Console : public GUI::Debugger { +public: + Console(WintermuteEngine *vm); + virtual ~Console(); + + bool Cmd_ShowFps(int argc, const char **argv); +private: + WintermuteEngine *_engineRef; +}; + +} + +#endif // WINTERMUTE_DEBUGGER_H diff --git a/engines/wintermute/detection.cpp b/engines/wintermute/detection.cpp new file mode 100644 index 0000000000..1bf2c76a50 --- /dev/null +++ b/engines/wintermute/detection.cpp @@ -0,0 +1,190 @@ +/* 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 "engines/advancedDetector.h" +#include "engines/wintermute/wintermute.h" +#include "engines/wintermute/base/base_persistence_manager.h" + +#include "common/config-manager.h" +#include "common/error.h" +#include "common/fs.h" +#include "common/util.h" +#include "common/translation.h" + +#include "engines/metaengine.h" + +#include "engines/wintermute/detection_tables.h" + +namespace Wintermute { + +/** + * The fallback game descriptor used by the Wintermute engine's fallbackDetector. + * Contents of this struct are overwritten by the fallbackDetector. (logic copied partially + * from the SCI-engine). + */ +static ADGameDescription s_fallbackDesc = { + "", + "", + AD_ENTRY1(0, 0), // This should always be AD_ENTRY1(0, 0) in the fallback descriptor + Common::UNK_LANG, + Common::kPlatformWindows, + ADGF_UNSTABLE, + GUIO0() +}; + +static const ADExtraGuiOptionsMap gameGuiOptions[] = { + { + GAMEOPTION_SHOW_FPS, + { + _s("Show FPS-counter"), + _s("Show the current number of frames per second in the upper left corner"), + "show_fps", + false + } + }, + AD_EXTRA_GUI_OPTIONS_TERMINATOR +}; + +static char s_fallbackGameIdBuf[256]; + +static const char *directoryGlobs[] = { + "language", // To detect the various languages + 0 +}; + +class WintermuteMetaEngine : public AdvancedMetaEngine { +public: + WintermuteMetaEngine() : AdvancedMetaEngine(Wintermute::gameDescriptions, sizeof(ADGameDescription), Wintermute::wintermuteGames, gameGuiOptions) { + _singleid = "wintermute"; + _guioptions = GUIO2(GUIO_NOMIDI, GAMEOPTION_SHOW_FPS); + _maxScanDepth = 2; + _directoryGlobs = directoryGlobs; + } + virtual const char *getName() const { + return "Wintermute"; + } + + virtual const char *getOriginalCopyright() const { + return "Copyright (c) 2011 Jan Nedoma"; + } + + virtual const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const { + // Set some defaults + s_fallbackDesc.extra = ""; + s_fallbackDesc.language = Common::UNK_LANG; + s_fallbackDesc.flags = ADGF_UNSTABLE; + s_fallbackDesc.platform = Common::kPlatformWindows; // default to Windows + s_fallbackDesc.gameid = "wintermute"; + s_fallbackDesc.guioptions = GUIO0(); + + if (allFiles.contains("data.dcp")) { + Common::String name, caption; + if (WintermuteEngine::getGameInfo(fslist, name, caption)) { + for (uint32 i = 0; i < name.size(); i++) { + // Replace spaces (and other non-alphanumerics) with underscores + if (!Common::isAlnum(name[(int32)i])) { + name.setChar('_', (uint32)i); + } + } + // Prefix to avoid collisions with actually known games + name = "wmefan-" + name; + Common::strlcpy(s_fallbackGameIdBuf, name.c_str(), sizeof(s_fallbackGameIdBuf) - 1); + s_fallbackDesc.gameid = s_fallbackGameIdBuf; + if (caption != name) { + caption += " (fangame) "; + char *offset = s_fallbackGameIdBuf + name.size() + 1; + uint32 remainingLength = (sizeof(s_fallbackGameIdBuf) - 1) - (name.size() + 1); + Common::strlcpy(offset, caption.c_str(), remainingLength); + s_fallbackDesc.extra = offset; + s_fallbackDesc.flags |= ADGF_USEEXTRAASTITLE; + } + return &s_fallbackDesc; + } // Fall through to return 0; + } + return 0; + } + + virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const { + assert(syst); + assert(engine); + + *engine = new Wintermute::WintermuteEngine(syst, desc); + return true; + } + + bool hasFeature(MetaEngineFeature f) const { + switch (f) { + case MetaEngine::kSupportsListSaves: + return true; + case MetaEngine::kSupportsLoadingDuringStartup: + return true; + case MetaEngine::kSupportsDeleteSave: + return true; + case MetaEngine::kSavesSupportCreationDate: + return true; + case MetaEngine::kSavesSupportMetaInfo: + return true; + case MetaEngine::kSavesSupportThumbnail: + return true; + default: + return false; + } + } + + SaveStateList listSaves(const char *target) const { + SaveStateList saves; + Wintermute::BasePersistenceManager pm(target, true); + for (int i = 0; i < getMaximumSaveSlot(); i++) { + if (pm.getSaveExists(i)) { + SaveStateDescriptor desc; + pm.getSaveStateDesc(i, desc); + saves.push_back(desc); + } + } + return saves; + } + + int getMaximumSaveSlot() const { + return 100; + } + + void removeSaveState(const char *target, int slot) const { + Wintermute::BasePersistenceManager pm(target, true); + pm.deleteSaveSlot(slot); + } + + virtual SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const { + Wintermute::BasePersistenceManager pm(target, true); + SaveStateDescriptor retVal; + retVal.setDescription("Invalid savegame"); + pm.getSaveStateDesc(slot, retVal); + return retVal; + } +}; + +} // end of namespace Wintermute + +#if PLUGIN_ENABLED_DYNAMIC(WINTERMUTE) +REGISTER_PLUGIN_DYNAMIC(WINTERMUTE, PLUGIN_TYPE_ENGINE, Wintermute::WintermuteMetaEngine); +#else +REGISTER_PLUGIN_STATIC(WINTERMUTE, PLUGIN_TYPE_ENGINE, Wintermute::WintermuteMetaEngine); +#endif diff --git a/engines/wintermute/detection_tables.h b/engines/wintermute/detection_tables.h new file mode 100644 index 0000000000..6b6dec635d --- /dev/null +++ b/engines/wintermute/detection_tables.h @@ -0,0 +1,277 @@ +/* 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. + * + */ + +namespace Wintermute { + +#define GAMEOPTION_SHOW_FPS GUIO_GAMEOPTIONS1 + +static const PlainGameDescriptor wintermuteGames[] = { + {"5ld", "Five Lethal Demons"}, + {"5ma", "Five Magical Amulets"}, + {"actualdest", "Actual Destination"}, + {"chivalry", "Chivalry is Not Dead"}, + {"deadcity", "Dead City"}, + {"dirtysplit", "Dirty Split"}, + {"eastside", "East Side Story"}, + {"ghostsheet", "Ghost in the Sheet"}, + {"hamlet", "Hamlet or the last game without MMORPS features, shaders and product placement"}, + {"julia", "J.U.L.I.A."}, + {"mirage", "Mirage"}, + {"pigeons", "Pigeons in the Park"}, + {"reversion", "Reversion"}, + {"rosemary", "Rosemary"}, + {"thebox", "The Box"}, + {"twc", "the white chamber"}, + {"wintermute", "Wintermute engine game"}, + {0, 0} +}; + +static const ADGameDescription gameDescriptions[] = { + // Five Lethal Demons + { + "5ld", + "", + AD_ENTRY1s("data.dcp", "1037a77cbd001e0644898addc022322c", 15407750), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_UNSTABLE, + GUIO0() + }, + // Five Magical Amulets + { + "5ma", + "", + AD_ENTRY1s("data.dcp", "0134e92bcd5fd2837df3971087e96067", 163316498), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_UNSTABLE, + GUIO0() + }, + // Actual Destination + { + "actualdest", + "", + AD_ENTRY1s("data.dcp", "6926f44b26f21ceb1d840eaab9aeb510", 9081740), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_UNSTABLE, + GUIO0() + }, + // Chivalry is Not Dead + { + "chivalry", + "", + AD_ENTRY1s("data.dcp", "ebd0915d9a12df5224be22f53bb23eb6", 7278306), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_UNSTABLE, + GUIO0() + }, + // Chivalry is Not Dead (Version from deirdrakai.com) + { + "chivalry", + "", + AD_ENTRY1s("data.dcp", "ae6d91b9517f4d2851a8ad94c96951c8", 7278302), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_UNSTABLE, + GUIO0() + }, + // Dead City (English) + { + "deadcity", + "", + { + {"english.dcp", 0, "c591046d6de7e381d76f70e0787b2b1f", 415935}, + {"data.dcp", 0, "7ebfd50d1a22370ed7b079bcaa631d62", 9070205}, + AD_LISTEND + }, + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_UNSTABLE, + GUIO0() + }, + // Dead City (Italian) + { + "deadcity", + "", + { + {"italian.dcp", 0, "92d8efb94436bec7bd1b7fe0b548192e", 454037}, + {"data.dcp", 0, "7ebfd50d1a22370ed7b079bcaa631d62", 9070205}, + AD_LISTEND + }, + Common::IT_ITA, + Common::kPlatformWindows, + ADGF_UNSTABLE, + GUIO0() + }, + // Dead City (Russian) + { + "deadcity", + "", + { + {"russian.dcp", 0, "a0ae71e9e1185596fffb07ad2c951eb9", 653317}, + {"data.dcp", 0, "7ebfd50d1a22370ed7b079bcaa631d62", 9070205}, + AD_LISTEND + }, + Common::RU_RUS, + Common::kPlatformWindows, + ADGF_UNSTABLE, + GUIO0() + }, + // Dirty Split (English) + { + "dirtysplit", + "", + AD_ENTRY1s("data.dcp", "8f3dae199361ece0f59fb20cfff6eed3", 88577621), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_UNSTABLE, + GUIO0() + }, + // Dirty Split (German) + { + "dirtysplit", + "", + AD_ENTRY1s("data.dcp", "139d8a25579e969f8b37d20e6e3de5f9", 92668291), + Common::DE_DEU, + Common::kPlatformWindows, + ADGF_UNSTABLE, + GUIO0() + }, + // East Side Story (Demo) + { + "eastside", + "Demo", + AD_ENTRY1s("data.dcp", "b3f8b09bb4b05ee3e9d14697525257f9", 59296246), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_UNSTABLE | + ADGF_DEMO, + GUIO0() + }, + // Ghosts in the Sheet + { + "ghostsheet", + "Demo", + AD_ENTRY1s("data.dcp", "dc1f6595f412ac25a52eaf47dad4ab81", 169083), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_UNSTABLE | + ADGF_DEMO, + GUIO0() + }, + // Hamlet or the last game without MMORPS features, shaders and product placement + { + "hamlet", + "", + AD_ENTRY1s("data.dcp", "f624add957a77c9930529fb28cc2450f", 88183022), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_UNSTABLE, + GUIO0() + }, + // J.U.L.I.A. (English) + { + "julia", + "", + AD_ENTRY1s("data.dcp", "c2264b4f8fcd132d2913ff5b6076a24f", 10109741), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_UNSTABLE, + GUIO0() + }, + // J.U.L.I.A. (English) (Demo) + { + "julia", + "Demo", + AD_ENTRY1s("data.dcp", "f0bbc3394555a9811f6050dae428cab6", 7655237), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_UNSTABLE | + ADGF_DEMO, + GUIO0() + }, + // Mirage + { + "mirage", + "", + AD_ENTRY1s("data.dcp", "d230b0b99c0aa77b9ecd094d8ee5573b", 17844056), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_UNSTABLE, + GUIO0() + }, + // Pigeons in the Park + { + "pigeons", + "", + AD_ENTRY1s("data.dcp", "9143a5b6ff8206aefe3c4c643add3ec7", 2611100), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_UNSTABLE, + GUIO0() + }, + // Reversion + { + "reversion", + "", + AD_ENTRY1s("data.dcp", "cd616f98ebfd047e0c540b50b4b70761", 254384531), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_UNSTABLE, + GUIO0() + }, + // Rosemary + { + "rosemary", + "", + AD_ENTRY1s("data.dcp", "4f2631138bd4d27587d9043f8aeff3df", 29483643), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_UNSTABLE, + GUIO0() + }, + // The Box + { + "thebox", + "", + AD_ENTRY1s("data.dcp", "ec5f0c7e8174e307701447b53afe7e2f", 108372483), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_UNSTABLE, + GUIO0() + }, + // the white chamber (multi-language) + { + "twc", + "", + AD_ENTRY1s("data.dcp", "0011d01142547c61e51ba24dc42b579e", 186451273), + Common::UNK_LANG, + Common::kPlatformWindows, + ADGF_UNSTABLE, + GUIO0() + }, + AD_TABLE_END_MARKER +}; + +} // End of namespace Wintermute diff --git a/engines/wintermute/graphics/transparent_surface.cpp b/engines/wintermute/graphics/transparent_surface.cpp new file mode 100644 index 0000000000..0f2279c40e --- /dev/null +++ b/engines/wintermute/graphics/transparent_surface.cpp @@ -0,0 +1,442 @@ +/* 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 "common/algorithm.h" +#include "common/endian.h" +#include "common/util.h" +#include "common/rect.h" +#include "common/textconsole.h" +#include "graphics/primitives.h" +#include "engines/wintermute/graphics/transparent_surface.h" + +namespace Wintermute { + +byte *TransparentSurface::_lookup = NULL; + +void TransparentSurface::destroyLookup() { + delete[] _lookup; + _lookup = NULL; +} + +TransparentSurface::TransparentSurface() : Surface(), _enableAlphaBlit(true) {} + +TransparentSurface::TransparentSurface(const Surface &surf, bool copyData) : Surface(), _enableAlphaBlit(true) { + if (copyData) { + copyFrom(surf); + } else { + w = surf.w; + h = surf.h; + pitch = surf.pitch; + format = surf.format; + pixels = surf.pixels; + } +} + +void doBlitOpaque(byte *ino, byte* outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep) { + byte *in, *out; + +#ifdef SCUMM_LITTLE_ENDIAN + const int aIndex = 3; +#else + const int aIndex = 0; +#endif + + for (uint32 i = 0; i < height; i++) { + out = outo; + in = ino; + memcpy(out, in, width * 4); + for (uint32 j = 0; j < width; j++) { + out[aIndex] = 0xFF; + out += 4; + } + outo += pitch; + ino += inoStep; + } +} + +void TransparentSurface::generateLookup() { + _lookup = new byte[256 * 256]; + for (int i = 0; i < 256; i++) { + for (int j = 0; j < 256; j++) { + _lookup[(i << 8) + j] = (i * j) >> 8; + } + } +} + +void TransparentSurface::doBlitAlpha(byte *ino, byte* outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep) { + byte *in, *out; + + if (!_lookup) { + generateLookup(); + } + +#ifdef SCUMM_LITTLE_ENDIAN + const int aIndex = 3; + const int bIndex = 0; + const int gIndex = 1; + const int rIndex = 2; +#else + const int aIndex = 0; + const int bIndex = 3; + const int gIndex = 2; + const int rIndex = 1; +#endif + + const int bShift = 0;//img->format.bShift; + const int gShift = 8;//img->format.gShift; + const int rShift = 16;//img->format.rShift; + const int aShift = 24;//img->format.aShift; + + const int bShiftTarget = 0;//target.format.bShift; + const int gShiftTarget = 8;//target.format.gShift; + const int rShiftTarget = 16;//target.format.rShift; + + for (uint32 i = 0; i < height; i++) { + out = outo; + in = ino; + for (uint32 j = 0; j < width; j++) { + uint32 pix = *(uint32 *)in; + uint32 oPix = *(uint32 *) out; + int b = (pix >> bShift) & 0xff; + int g = (pix >> gShift) & 0xff; + int r = (pix >> rShift) & 0xff; + int a = (pix >> aShift) & 0xff; + int outb, outg, outr, outa; + in += inStep; + + switch (a) { + case 0: // Full transparency + out += 4; + break; + case 255: // Full opacity + outb = b; + outg = g; + outr = r; + outa = a; + + out[aIndex] = outa; + out[bIndex] = outb; + out[gIndex] = outg; + out[rIndex] = outr; + out += 4; + break; + + default: // alpha blending + outa = 255; + + outb = _lookup[(((oPix >> bShiftTarget) & 0xff)) + ((255 - a) << 8)]; + outg = _lookup[(((oPix >> gShiftTarget) & 0xff)) + ((255 - a) << 8)]; + outr = _lookup[(((oPix >> rShiftTarget) & 0xff)) + ((255 - a) << 8)]; + outb += _lookup[b + (a << 8)]; + outg += _lookup[g + (a << 8)]; + outr += _lookup[r + (a << 8)]; + + out[aIndex] = outa; + out[bIndex] = outb; + out[gIndex] = outg; + out[rIndex] = outr; + out += 4; + } + } + outo += pitch; + ino += inoStep; + } +} + + +Common::Rect TransparentSurface::blit(Graphics::Surface &target, int posX, int posY, int flipping, Common::Rect *pPartRect, uint color, int width, int height) { + int ca = (color >> 24) & 0xff; + + Common::Rect retSize; + retSize.top = 0; + retSize.left = 0; + retSize.setWidth(0); + retSize.setHeight(0); + // Check if we need to draw anything at all + if (ca == 0) + return retSize; + + int cr = (color >> 16) & 0xff; + int cg = (color >> 8) & 0xff; + int cb = (color >> 0) & 0xff; + + // Compensate for transparency. Since we're coming + // down to 255 alpha, we just compensate for the colors here + if (ca != 255) { + cr = cr * ca >> 8; + cg = cg * ca >> 8; + cb = cb * ca >> 8; + } + + // Create an encapsulating surface for the data + TransparentSurface srcImage(*this, false); + // TODO: Is the data really in the screen format? + if (format.bytesPerPixel != 4) { + warning("TransparentSurface can only blit 32 bpp images"); + return retSize; + } + + if (pPartRect) { + srcImage.pixels = &((char *)pixels)[pPartRect->top * srcImage.pitch + pPartRect->left * 4]; + srcImage.w = pPartRect->width(); + srcImage.h = pPartRect->height(); + + debug(6, "Blit(%d, %d, %d, [%d, %d, %d, %d], %08x, %d, %d)", posX, posY, flipping, + pPartRect->left, pPartRect->top, pPartRect->width(), pPartRect->height(), color, width, height); + } else { + + debug(6, "Blit(%d, %d, %d, [%d, %d, %d, %d], %08x, %d, %d)", posX, posY, flipping, 0, 0, + srcImage.w, srcImage.h, color, width, height); + } + + if (width == -1) + width = srcImage.w; + if (height == -1) + height = srcImage.h; + +#ifdef SCALING_TESTING + // Hardcode scaling to 66% to test scaling + width = width * 2 / 3; + height = height * 2 / 3; +#endif + + Graphics::Surface *img; + Graphics::Surface *imgScaled = NULL; + byte *savedPixels = NULL; + if ((width != srcImage.w) || (height != srcImage.h)) { + // Scale the image + img = imgScaled = srcImage.scale(width, height); + savedPixels = (byte *)img->pixels; + } else { + img = &srcImage; + } + + // Handle off-screen clipping + if (posY < 0) { + img->h = MAX(0, (int)img->h - -posY); + img->pixels = (byte *)img->pixels + img->pitch * -posY; + posY = 0; + } + + if (posX < 0) { + img->w = MAX(0, (int)img->w - -posX); + img->pixels = (byte *)img->pixels + (-posX * 4); + posX = 0; + } + + img->w = CLIP((int)img->w, 0, (int)MAX((int)target.w - posX, 0)); + img->h = CLIP((int)img->h, 0, (int)MAX((int)target.h - posY, 0)); + + if ((img->w > 0) && (img->h > 0)) { + int xp = 0, yp = 0; + + int inStep = 4; + int inoStep = img->pitch; + if (flipping & TransparentSurface::FLIP_V) { + inStep = -inStep; + xp = img->w - 1; + } + + if (flipping & TransparentSurface::FLIP_H) { + inoStep = -inoStep; + yp = img->h - 1; + } + + byte *ino = (byte *)img->getBasePtr(xp, yp); + byte *outo = (byte *)target.getBasePtr(posX, posY); + byte *in, *out; + +#ifdef SCUMM_LITTLE_ENDIAN + const int aIndex = 3; + const int bIndex = 0; + const int gIndex = 1; + const int rIndex = 2; +#else + const int aIndex = 0; + const int bIndex = 3; + const int gIndex = 2; + const int rIndex = 1; +#endif + const int bShift = 0;//img->format.bShift; + const int gShift = 8;//img->format.gShift; + const int rShift = 16;//img->format.rShift; + const int aShift = 24;//img->format.aShift; + + const int bShiftTarget = 0;//target.format.bShift; + const int gShiftTarget = 8;//target.format.gShift; + const int rShiftTarget = 16;//target.format.rShift; + + if (ca == 255 && cb == 255 && cg == 255 && cr == 255) { + if (_enableAlphaBlit) { + doBlitAlpha(ino, outo, img->w, img->h, target.pitch, inStep, inoStep); + } else { + doBlitOpaque(ino, outo, img->w, img->h, target.pitch, inStep, inoStep); + } + } else { + for (int i = 0; i < img->h; i++) { + out = outo; + in = ino; + for (int j = 0; j < img->w; j++) { + uint32 pix = *(uint32 *)in; + uint32 o_pix = *(uint32 *) out; + int b = (pix >> bShift) & 0xff; + int g = (pix >> gShift) & 0xff; + int r = (pix >> rShift) & 0xff; + int a = (pix >> aShift) & 0xff; + int outb, outg, outr, outa; + in += inStep; + + if (ca != 255) { + a = a * ca >> 8; + } + + switch (a) { + case 0: // Full transparency + out += 4; + break; + case 255: // Full opacity + if (cb != 255) + outb = (b * cb) >> 8; + else + outb = b; + + if (cg != 255) + outg = (g * cg) >> 8; + else + outg = g; + + if (cr != 255) + outr = (r * cr) >> 8; + else + outr = r; + outa = a; + out[aIndex] = outa; + out[bIndex] = outb; + out[gIndex] = outg; + out[rIndex] = outr; + out += 4; + break; + + default: // alpha blending + outa = 255; + outb = (o_pix >> bShiftTarget) & 0xff; + outg = (o_pix >> gShiftTarget) & 0xff; + outr = (o_pix >> rShiftTarget) & 0xff; + if (cb == 0) + outb = 0; + else if (cb != 255) + outb += ((b - outb) * a * cb) >> 16; + else + outb += ((b - outb) * a) >> 8; + if (cg == 0) + outg = 0; + else if (cg != 255) + outg += ((g - outg) * a * cg) >> 16; + else + outg += ((g - outg) * a) >> 8; + if (cr == 0) + outr = 0; + else if (cr != 255) + outr += ((r - outr) * a * cr) >> 16; + else + outr += ((r - outr) * a) >> 8; + out[aIndex] = outa; + out[bIndex] = outb; + out[gIndex] = outg; + out[rIndex] = outr; + out += 4; + } + } + outo += target.pitch; + ino += inoStep; + } + } + } + + if (imgScaled) { + imgScaled->pixels = savedPixels; + imgScaled->free(); + delete imgScaled; + } + + retSize.setWidth(img->w); + retSize.setHeight(img->h); + return retSize; +} + +TransparentSurface *TransparentSurface::scale(uint16 newWidth, uint16 newHeight) const { + Common::Rect srcRect(0, 0, (int16)w, (int16)h); + Common::Rect dstRect(0, 0, (int16)newWidth, (int16)newHeight); + return scale(srcRect, dstRect); +} + +// Copied from clone2727's https://github.com/clone2727/scummvm/blob/pegasus/engines/pegasus/surface.cpp#L247 +TransparentSurface *TransparentSurface::scale(const Common::Rect &srcRect, const Common::Rect &dstRect) const { + // I'm doing simple linear scaling here + // dstRect(x, y) = srcRect(x * srcW / dstW, y * srcH / dstH); + TransparentSurface *target = new TransparentSurface(); + + assert(format.bytesPerPixel == 4); + + int srcW = srcRect.width(); + int srcH = srcRect.height(); + int dstW = dstRect.width(); + int dstH = dstRect.height(); + + target->create((uint16)dstW, (uint16)dstH, this->format); + + for (int y = 0; y < dstH; y++) { + for (int x = 0; x < dstW; x++) { + uint32 color = READ_UINT32((const byte *)getBasePtr(x * srcW / dstW + srcRect.left, + y * srcH / dstH + srcRect.top)); + WRITE_UINT32((byte *)target->getBasePtr(x + dstRect.left, y + dstRect.top), color); + } + } + return target; + +} + +/** + * Writes a color key to the alpha channel of the surface + * @param rKey the red component of the color key + * @param gKey the green component of the color key + * @param bKey the blue component of the color key + * @param overwriteAlpha if true, all other alpha will be set fully opaque + */ +void TransparentSurface::applyColorKey(uint8 rKey, uint8 gKey, uint8 bKey, bool overwriteAlpha) { + assert(format.bytesPerPixel == 4); + for (int i = 0; i < h; i++) { + for (int j = 0; j < w; j++) { + uint32 pix = ((uint32 *)pixels)[i * w + j]; + uint8 r, g, b, a; + format.colorToARGB(pix, a, r, g, b); + if (r == rKey && g == gKey && b == bKey) { + a = 0; + ((uint32 *)pixels)[i * w + j] = format.ARGBToColor(a, r, g, b); + } else if (overwriteAlpha) { + a = 255; + ((uint32 *)pixels)[i * w + j] = format.ARGBToColor(a, r, g, b); + } + } + } +} + +} // End of namespace Graphics diff --git a/engines/wintermute/graphics/transparent_surface.h b/engines/wintermute/graphics/transparent_surface.h new file mode 100644 index 0000000000..8b00dccbd9 --- /dev/null +++ b/engines/wintermute/graphics/transparent_surface.h @@ -0,0 +1,131 @@ +/* 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 GRAPHICS_TRANSPARENTSURFACE_H +#define GRAPHICS_TRANSPARENTSURFACE_H + +#include "graphics/surface.h" + +/* + * This code is based on Broken Sword 2.5 engine + * + * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer + * + * Licensed under GNU GPL v2 + * + */ + +// TODO: Find a better solution for this. +#define BS_RGB(R,G,B) (0xFF000000 | ((R) << 16) | ((G) << 8) | (B)) +#define BS_ARGB(A,R,G,B) (((A) << 24) | ((R) << 16) | ((G) << 8) | (B)) + +namespace Wintermute { + +/** + * A transparent graphics surface, which implements alpha blitting. + */ +struct TransparentSurface : public Graphics::Surface { + TransparentSurface(); + TransparentSurface(const Graphics::Surface &surf, bool copyData = false); + + void setColorKey(char r, char g, char b); + void disableColorKey(); + + // Enums + /** + @brief The possible flipping parameters for the blit methode. + */ + enum FLIP_FLAGS { + /// The image will not be flipped. + FLIP_NONE = 0, + /// The image will be flipped at the horizontal axis. + FLIP_H = 1, + /// The image will be flipped at the vertical axis. + FLIP_V = 2, + /// The image will be flipped at the horizontal and vertical axis. + FLIP_HV = FLIP_H | FLIP_V, + /// The image will be flipped at the horizontal and vertical axis. + FLIP_VH = FLIP_H | FLIP_V + }; + + bool _enableAlphaBlit; + + /** + @brief renders the surface to another surface + @param pDest a pointer to the target image. In most cases this is the framebuffer. + @param PosX the position on the X-axis in the target image in pixels where the image is supposed to be rendered.<br> + The default value is 0. + @param PosY the position on the Y-axis in the target image in pixels where the image is supposed to be rendered.<br> + The default value is 0. + @param Flipping how the the image should be flipped.<br> + The default value is BS_Image::FLIP_NONE (no flipping) + @param pSrcPartRect Pointer on Common::Rect which specifies the section to be rendered. If the whole image has to be rendered the Pointer is NULL.<br> + This referes to the unflipped and unscaled image.<br> + The default value is NULL. + @param Color an ARGB color value, which determines the parameters for the color modulation und alpha blending.<br> + The alpha component of the color determines the alpha blending parameter (0 = no covering, 255 = full covering).<br> + The color components determines the color for color modulation.<br> + The default value is BS_ARGB(255, 255, 255, 255) (full covering, no color modulation). + The macros BS_RGB and BS_ARGB can be used for the creation of the color value. + @param Width the output width of the screen section. + The images will be scaled if the output width of the screen section differs from the image section.<br> + The value -1 determines that the image should not be scaled.<br> + The default value is -1. + @param Width the output height of the screen section. + The images will be scaled if the output width of the screen section differs from the image section.<br> + The value -1 determines that the image should not be scaled.<br> + The default value is -1. + @return returns false if the rendering failed. + */ + + Common::Rect blit(Graphics::Surface &target, int posX = 0, int posY = 0, + int flipping = FLIP_NONE, + Common::Rect *pPartRect = NULL, + uint color = BS_ARGB(255, 255, 255, 255), + int width = -1, int height = -1); + void applyColorKey(uint8 r, uint8 g, uint8 b, bool overwriteAlpha = false); + // The following scale-code supports arbitrary scaling (i.e. no repeats of column 0 at the end of lines) + TransparentSurface *scale(uint16 newWidth, uint16 newHeight) const; + TransparentSurface *scale(const Common::Rect &srcRect, const Common::Rect &dstRect) const; + static byte *_lookup; + static void destroyLookup(); +private: + static void doBlitAlpha(byte *ino, byte* outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep); + static void generateLookup(); +}; + +/** + * A deleter for Surface objects which can be used with SharedPtr. + * + * This deleter assures Surface::free is called on deletion. + */ +/*struct SharedPtrTransparentSurfaceDeleter { + void operator()(TransparentSurface *ptr) { + ptr->free(); + delete ptr; + } +};*/ + + +} // End of namespace Graphics + + +#endif diff --git a/engines/wintermute/math/math_util.cpp b/engines/wintermute/math/math_util.cpp new file mode 100644 index 0000000000..31af77538a --- /dev/null +++ b/engines/wintermute/math/math_util.cpp @@ -0,0 +1,52 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/math/math_util.h" +#include <math.h> + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////////// +float MathUtil::round(float val) { + float result = floor(val); + if (val - result >= 0.5f) { + result += 1.0; + } + return result; +} + +////////////////////////////////////////////////////////////////////////// +float MathUtil::roundUp(float val) { + float result = floor(val); + if (val - result > 0) { + result += 1.0; + } + return result; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/math/math_util.h b/engines/wintermute/math/math_util.h new file mode 100644 index 0000000000..38b6d9abf9 --- /dev/null +++ b/engines/wintermute/math/math_util.h @@ -0,0 +1,42 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_MATHUTIL_H +#define WINTERMUTE_MATHUTIL_H + +namespace Wintermute { + +class MathUtil { +public: + static float round(float val); + static float roundUp(float val); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/math/matrix4.cpp b/engines/wintermute/math/matrix4.cpp new file mode 100644 index 0000000000..ca1eae8813 --- /dev/null +++ b/engines/wintermute/math/matrix4.cpp @@ -0,0 +1,86 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/math/matrix4.h" +#include "engines/wintermute/math/vector2.h" +#include <math.h> + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////////// +Matrix4::Matrix4() { + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + m[i][j] = 0.0f; + } + } +} + +////////////////////////////////////////////////////////////////////////// +Matrix4::~Matrix4() { +} + + +////////////////////////////////////////////////////////////////////////// +void Matrix4::identity() { + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + m[i][j] = 0.0f; + } + } + m[0][0] = 1.0f; + m[1][1] = 1.0f; + m[2][2] = 1.0f; + m[3][3] = 1.0f; + +} + +////////////////////////////////////////////////////////////////////////// +void Matrix4::rotationZ(float angle) { + identity(); + + m[0][0] = cos(angle); + m[1][1] = cos(angle); + m[0][1] = sin(angle); + m[1][0] = -sin(angle); +} + +////////////////////////////////////////////////////////////////////////// +void Matrix4::transformVector2(Vector2 &vec) { + float norm; + + norm = m[0][3] * vec.x + m[1][3] * vec.y + m[3][3]; + + float x = (m[0][0] * vec.x + m[1][0] * vec.y + m[3][0]) / norm; + float y = (m[0][1] * vec.x + m[1][1] * vec.y + m[3][1]) / norm; + + vec.x = x; + vec.y = y; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/math/matrix4.h b/engines/wintermute/math/matrix4.h new file mode 100644 index 0000000000..273633f723 --- /dev/null +++ b/engines/wintermute/math/matrix4.h @@ -0,0 +1,50 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_MATRIX4_H +#define WINTERMUTE_MATRIX4_H + +namespace Wintermute { + +class Vector2; + +class Matrix4 { +public: + Matrix4(); + ~Matrix4(); + + void identity(); + void rotationZ(float angle); + void transformVector2(Vector2 &vec); + + float m[4][4]; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/math/rect32.h b/engines/wintermute/math/rect32.h new file mode 100644 index 0000000000..190c1135cf --- /dev/null +++ b/engines/wintermute/math/rect32.h @@ -0,0 +1,94 @@ +/* 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 WINTERMUTE_RECT32_H +#define WINTERMUTE_RECT32_H + +#include "common/system.h" + +namespace Wintermute { + +struct Point32 { + int32 x; + int32 y; +}; + +struct Rect32 { + int32 top, left; ///< The point at the top left of the rectangle (part of the rect). + int32 bottom, right; ///< The point at the bottom right of the rectangle (not part of the rect). + + Rect32() : top(0), left(0), bottom(0), right(0) {} + Rect32(int32 w, int32 h) : top(0), left(0), bottom(h), right(w) {} + Rect32(int32 x1, int32 y1, int32 x2, int32 y2) : top(y1), left(x1), bottom(y2), right(x2) { + assert(isValidRect()); + } + bool operator==(const Rect32 &rhs) const { + return equals(rhs); + } + bool operator!=(const Rect32 &rhs) const { + return !equals(rhs); + } + + int32 width() const { + return right - left; + } + int32 height() const { + return bottom - top; + } + + void setWidth(int32 aWidth) { + right = left + aWidth; + } + + void setHeight(int32 aHeight) { + bottom = top + aHeight; + } + + void setEmpty() { + left = right = top = bottom = 0; + } + + void offsetRect(int dx, int dy) { + left += dx; + top += dy; + right += dx; + bottom += dy; + } + /** + * Check if the given rect is equal to this one. + * + * @param r The rectangle to check + * + * @return true if the given rect is equal, false otherwise + */ + bool equals(const Rect32 &r) const { + return (left == r.left) && (right == r.right) && (top == r.top) && (bottom == r.bottom); + } + + bool isValidRect() const { + return (left <= right && top <= bottom); + } +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/math/vector2.cpp b/engines/wintermute/math/vector2.cpp new file mode 100644 index 0000000000..74c5586d62 --- /dev/null +++ b/engines/wintermute/math/vector2.cpp @@ -0,0 +1,55 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/math/vector2.h" +#include <math.h> + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////////// +Vector2::Vector2() { + x = y = 0.0f; +} + +////////////////////////////////////////////////////////////////////////// +Vector2::Vector2(float xVal, float yVal) { + this->x = xVal; + this->y = yVal; +} + +////////////////////////////////////////////////////////////////////////// +Vector2::~Vector2() { +} + + +////////////////////////////////////////////////////////////////////////// +float Vector2::length() const { + return (float)sqrt(x * x + y * y); +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/math/vector2.h b/engines/wintermute/math/vector2.h new file mode 100644 index 0000000000..31f31daaa0 --- /dev/null +++ b/engines/wintermute/math/vector2.h @@ -0,0 +1,75 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_VECTOR2_H +#define WINTERMUTE_VECTOR2_H + +namespace Wintermute { + +class Vector2 { +public: + Vector2(); + Vector2(float x, float y); + ~Vector2(); + + float length() const; + + inline Vector2 &operator= (const Vector2 &other) { + x = other.x; + y = other.y; + + return *this; + } + + inline Vector2 operator+ (const Vector2 &other) const { + return Vector2(x + other.x, y + other.y); + } + + inline Vector2 operator- (const Vector2 &other) const { + return Vector2(x - other.x, y - other.y); + } + + inline Vector2 operator* (const float scalar) const { + return Vector2(x * scalar, y * scalar); + } + + inline Vector2 &operator+= (const Vector2 &other) { + x += other.x; + y += other.y; + + return *this; + } + + + float x; + float y; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/module.mk b/engines/wintermute/module.mk new file mode 100644 index 0000000000..f61e61f6db --- /dev/null +++ b/engines/wintermute/module.mk @@ -0,0 +1,126 @@ +MODULE := engines/wintermute + +MODULE_OBJS := \ + ad/ad_actor.o \ + ad/ad_entity.o \ + ad/ad_game.o \ + ad/ad_inventory.o \ + ad/ad_inventory_box.o \ + ad/ad_item.o \ + ad/ad_layer.o \ + ad/ad_node_state.o \ + ad/ad_object.o \ + ad/ad_path.o \ + ad/ad_path_point.o \ + ad/ad_region.o \ + ad/ad_response.o \ + ad/ad_response_box.o \ + ad/ad_response_context.o \ + ad/ad_rot_level.o \ + ad/ad_scale_level.o \ + ad/ad_scene.o \ + ad/ad_scene_node.o \ + ad/ad_scene_state.o \ + ad/ad_sentence.o \ + ad/ad_sprite_set.o \ + ad/ad_talk_def.o \ + ad/ad_talk_holder.o \ + ad/ad_talk_node.o \ + ad/ad_waypoint_group.o \ + base/scriptables/script.o \ + base/scriptables/script_engine.o \ + base/scriptables/script_stack.o \ + base/scriptables/script_value.o \ + base/scriptables/script_ext_array.o \ + base/scriptables/script_ext_date.o \ + base/scriptables/script_ext_file.o \ + base/scriptables/script_ext_math.o \ + base/scriptables/script_ext_object.o \ + base/scriptables/script_ext_mem_buffer.o \ + base/scriptables/script_ext_string.o \ + base/file/base_disk_file.o \ + base/file/base_file.o \ + base/file/base_file_entry.o \ + base/file/base_package.o \ + base/file/base_resources.o \ + base/file/base_save_thumb_file.o \ + base/font/base_font_bitmap.o \ + base/font/base_font_truetype.o \ + base/font/base_font.o \ + base/font/base_font_storage.o \ + base/gfx/base_image.o \ + base/gfx/base_renderer.o \ + base/gfx/base_surface.o \ + base/gfx/osystem/base_surface_osystem.o \ + base/gfx/osystem/base_render_osystem.o \ + base/gfx/osystem/render_ticket.o \ + base/particles/part_particle.o \ + base/particles/part_emitter.o \ + base/particles/part_force.o \ + base/sound/base_sound.o \ + base/sound/base_sound_buffer.o \ + base/sound/base_sound_manager.o \ + base/base_active_rect.o \ + base/base.o \ + base/base_dynamic_buffer.o \ + base/base_engine.o \ + base/base_fader.o \ + base/base_file_manager.o \ + base/base_frame.o \ + base/base_game.o \ + base/base_game_music.o \ + base/base_keyboard_state.o \ + base/base_named_object.o \ + base/base_object.o \ + base/base_parser.o \ + base/base_persistence_manager.o \ + base/base_point.o \ + base/base_quick_msg.o \ + base/base_region.o \ + base/base_save_thumb_helper.o \ + base/base_scriptable.o \ + base/base_script_holder.o \ + base/base_sprite.o \ + base/base_string_table.o \ + base/base_sub_frame.o \ + base/base_surface_storage.o \ + base/base_transition_manager.o \ + base/base_viewport.o \ + base/saveload.o \ + detection.o \ + graphics/transparent_surface.o \ + math/math_util.o \ + math/matrix4.o \ + math/vector2.o \ + platform_osystem.o \ + system/sys_class.o \ + system/sys_class_registry.o \ + system/sys_instance.o \ + ui/ui_button.o \ + ui/ui_edit.o \ + ui/ui_entity.o \ + ui/ui_object.o \ + ui/ui_text.o \ + ui/ui_tiled_image.o \ + ui/ui_window.o \ + utils/convert_utf.o \ + utils/crc.o \ + utils/path_util.o \ + utils/string_util.o \ + utils/utils.o \ + video/video_player.o \ + video/video_theora_player.o \ + debugger.o \ + wintermute.o \ + persistent.o + +MODULE_DIRS += \ + engines/wintermute + +# This module can be built as a plugin +ifeq ($(ENABLE_WINTERMUTE), DYNAMIC_PLUGIN) +PLUGIN := 1 +endif + +# Include common rules +include $(srcdir)/rules.mk diff --git a/engines/wintermute/persistent.cpp b/engines/wintermute/persistent.cpp new file mode 100644 index 0000000000..514fd61d34 --- /dev/null +++ b/engines/wintermute/persistent.cpp @@ -0,0 +1,168 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_actor.h" +#include "engines/wintermute/ad/ad_entity.h" +#include "engines/wintermute/ad/ad_game.h" +#include "engines/wintermute/ad/ad_inventory.h" +#include "engines/wintermute/ad/ad_inventory_box.h" +#include "engines/wintermute/ad/ad_item.h" +#include "engines/wintermute/ad/ad_layer.h" +#include "engines/wintermute/ad/ad_node_state.h" +#include "engines/wintermute/ad/ad_object.h" +#include "engines/wintermute/ad/ad_path.h" +#include "engines/wintermute/ad/ad_path_point.h" +#include "engines/wintermute/ad/ad_region.h" +#include "engines/wintermute/ad/ad_response.h" +#include "engines/wintermute/ad/ad_response_box.h" +#include "engines/wintermute/ad/ad_response_context.h" +#include "engines/wintermute/ad/ad_rot_level.h" +#include "engines/wintermute/ad/ad_scale_level.h" +#include "engines/wintermute/ad/ad_scene.h" +#include "engines/wintermute/ad/ad_scene_node.h" +#include "engines/wintermute/ad/ad_scene_state.h" +#include "engines/wintermute/ad/ad_sentence.h" +#include "engines/wintermute/ad/ad_sprite_set.h" +#include "engines/wintermute/ad/ad_talk_def.h" +#include "engines/wintermute/ad/ad_talk_holder.h" +#include "engines/wintermute/ad/ad_talk_node.h" +#include "engines/wintermute/ad/ad_waypoint_group.h" +#include "engines/wintermute/base/base_fader.h" +#include "engines/wintermute/base/font/base_font_bitmap.h" +#include "engines/wintermute/base/font/base_font_storage.h" +#include "engines/wintermute/base/font/base_font_truetype.h" +#include "engines/wintermute/base/base_frame.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_keyboard_state.h" +#include "engines/wintermute/base/base_object.h" +#include "engines/wintermute/base/base_point.h" +#include "engines/wintermute/base/base_region.h" +#include "engines/wintermute/base/base_scriptable.h" +#include "engines/wintermute/base/base_script_holder.h" +#include "engines/wintermute/base/sound/base_sound.h" +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/base/base_sub_frame.h" +#include "engines/wintermute/base/base_viewport.h" +#include "engines/wintermute/base/particles/part_emitter.h" +#include "engines/wintermute/base/scriptables/script_engine.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script_ext_array.h" +#include "engines/wintermute/base/scriptables/script_ext_date.h" +#include "engines/wintermute/base/scriptables/script_ext_file.h" +#include "engines/wintermute/base/scriptables/script_ext_math.h" +#include "engines/wintermute/base/scriptables/script_ext_mem_buffer.h" +#include "engines/wintermute/base/scriptables/script_ext_object.h" +#include "engines/wintermute/base/scriptables/script_ext_string.h" +#include "engines/wintermute/ui/ui_button.h" +#include "engines/wintermute/ui/ui_edit.h" +#include "engines/wintermute/ui/ui_entity.h" +#include "engines/wintermute/ui/ui_text.h" +#include "engines/wintermute/ui/ui_tiled_image.h" +#include "engines/wintermute/ui/ui_window.h" +#include "engines/wintermute/video/video_theora_player.h" +#include "engines/wintermute/system/sys_class.h" + +// SystemClass adds these objects to the registry, thus they aren't as leaked as they look +#define REGISTER_CLASS(class_name, persistent_class)\ + new Wintermute::SystemClass(class_name::_className, class_name::persistBuild, class_name::persistLoad, persistent_class); + +namespace Wintermute { + +// This is done in a separate file, to avoid including the kitchensink in SystemClassRegistry. +void SystemClassRegistry::registerClasses() { + REGISTER_CLASS(AdActor, false) + REGISTER_CLASS(AdEntity, false) + REGISTER_CLASS(AdGame, true) + REGISTER_CLASS(AdInventory, false) + REGISTER_CLASS(AdInventoryBox, false) + REGISTER_CLASS(AdItem, false) + REGISTER_CLASS(AdLayer, false) + REGISTER_CLASS(AdNodeState, false) + REGISTER_CLASS(AdObject, false) + REGISTER_CLASS(AdPath, false) + REGISTER_CLASS(AdPathPoint, false) + REGISTER_CLASS(AdRegion, false) + REGISTER_CLASS(AdResponse, false) + REGISTER_CLASS(AdResponseBox, false) + REGISTER_CLASS(AdResponseContext, false) + REGISTER_CLASS(AdRotLevel, false) + REGISTER_CLASS(AdScaleLevel, false) + REGISTER_CLASS(AdScene, false) + REGISTER_CLASS(AdSceneNode, false) + REGISTER_CLASS(AdSceneState, false) + REGISTER_CLASS(AdSentence, false) + REGISTER_CLASS(AdSpriteSet, false) + REGISTER_CLASS(AdTalkDef, false) + REGISTER_CLASS(AdTalkHolder, false) + REGISTER_CLASS(AdTalkNode, false) + REGISTER_CLASS(AdWaypointGroup, false) + + REGISTER_CLASS(BaseFader, false) + REGISTER_CLASS(BaseFont, false) + REGISTER_CLASS(BaseFontBitmap, false) + REGISTER_CLASS(BaseFontStorage, true) + REGISTER_CLASS(BaseFontTT, false) + REGISTER_CLASS(BaseFrame, false) + REGISTER_CLASS(BaseGame, true) + REGISTER_CLASS(BaseKeyboardState, false) + REGISTER_CLASS(BaseObject, false) + REGISTER_CLASS(BasePoint, false) + REGISTER_CLASS(BaseRegion, false) + REGISTER_CLASS(BaseScriptable, false) + REGISTER_CLASS(BaseScriptHolder, false) + REGISTER_CLASS(BaseSound, false) + REGISTER_CLASS(BaseSprite, false) + REGISTER_CLASS(BaseSubFrame, false) + + REGISTER_CLASS(BaseViewport, false) + REGISTER_CLASS(PartEmitter, false) + REGISTER_CLASS(ScEngine, true) + REGISTER_CLASS(ScScript, false) + REGISTER_CLASS(ScStack, false) + REGISTER_CLASS(ScValue, false) + REGISTER_CLASS(SXArray, false) + REGISTER_CLASS(SXDate, false) + REGISTER_CLASS(SXFile, false) + REGISTER_CLASS(SXMath, true) + REGISTER_CLASS(SXMemBuffer, false) + REGISTER_CLASS(SXObject, false) + REGISTER_CLASS(SXString, false) + + REGISTER_CLASS(UIButton, false) + REGISTER_CLASS(UIEdit, false) + REGISTER_CLASS(UIEntity, false) + REGISTER_CLASS(UIObject, false) + REGISTER_CLASS(UIText, false) + REGISTER_CLASS(UITiledImage, false) + REGISTER_CLASS(UIWindow, false) + REGISTER_CLASS(VideoTheoraPlayer, false) +} + +} diff --git a/engines/wintermute/persistent.h b/engines/wintermute/persistent.h new file mode 100644 index 0000000000..ca9281f798 --- /dev/null +++ b/engines/wintermute/persistent.h @@ -0,0 +1,89 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_PERSISTENT_H +#define WINTERMUTE_PERSISTENT_H + +namespace Wintermute { + +class BasePersistenceManager; + +// persistence support +typedef void *(*PERSISTBUILD)(void); +typedef bool(*PERSISTLOAD)(void *, BasePersistenceManager *); +typedef void(*SYS_INSTANCE_CALLBACK)(void *instance, void *data); +} // end of namespace Wintermute + +#include "engines/wintermute/system/sys_class_registry.h" +namespace Wintermute { + + +#define DECLARE_PERSISTENT(className, parentClass)\ + static const char _className[];\ + static void *persistBuild(void);\ + virtual const char *getClassName();\ + static bool persistLoad(void* Instance, BasePersistenceManager* PersistMgr);\ + className(TDynamicConstructor p1, TDynamicConstructor p2) : parentClass(p1, p2) { /*memset(this, 0, sizeof(class_name));*/ };\ + virtual bool persist(BasePersistenceManager *persistMgr);\ + void* operator new (size_t size);\ + void operator delete(void* p);\ + + +#define IMPLEMENT_PERSISTENT(className, persistentClass)\ + const char className::_className[] = #className;\ + void* className::persistBuild() {\ + return ::new className(DYNAMIC_CONSTRUCTOR, DYNAMIC_CONSTRUCTOR);\ + }\ + \ + bool className::persistLoad(void *instance, BasePersistenceManager *persistMgr) {\ + return ((className*)instance)->persist(persistMgr);\ + }\ + \ + const char *className::getClassName() {\ + return #className;\ + }\ + \ + /*SystemClass Register##class_name(class_name::_className, class_name::PersistBuild, class_name::PersistLoad, persistent_class);*/\ + \ + void* className::operator new(size_t size) {\ + void* ret = ::operator new(size);\ + SystemClassRegistry::getInstance()->registerInstance(#className, ret);\ + return ret;\ + }\ + \ + void className::operator delete(void *p) {\ + SystemClassRegistry::getInstance()->unregisterInstance(#className, p);\ + ::operator delete(p);\ + }\ + +#define TMEMBER(memberName) #memberName, &memberName +#define TMEMBER_INT(memberName) #memberName, (int*)&memberName + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/platform_osystem.cpp b/engines/wintermute/platform_osystem.cpp new file mode 100644 index 0000000000..f13bdd2a3c --- /dev/null +++ b/engines/wintermute/platform_osystem.cpp @@ -0,0 +1,275 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/wintermute.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/gfx/osystem/base_render_osystem.h" +#include "engines/wintermute/platform_osystem.h" +#include "common/str.h" +#include "common/textconsole.h" +#include "common/system.h" + +namespace Wintermute { + +BaseGame *BasePlatform::_gameRef = NULL; +WintermuteEngine *BasePlatform::_engineRef = NULL; + +#define CLASS_NAME "GF_FRAME" +int BasePlatform::initialize(WintermuteEngine *engineRef, BaseGame *inGame, int argc, char *argv[]) { + _gameRef = inGame; + _engineRef = engineRef; + return true; +} + +void BasePlatform::deinit() { + _gameRef = NULL; + _engineRef = NULL; +} + +////////////////////////////////////////////////////////////////////////// +void BasePlatform::handleEvent(Common::Event *event) { + switch (event->type) { + + case Common::EVENT_LBUTTONDOWN: + if (_gameRef) { + if (_gameRef->isLeftDoubleClick()) { + _gameRef->onMouseLeftDblClick(); + } else { + _gameRef->onMouseLeftDown(); + } + } + break; + case Common::EVENT_RBUTTONDOWN: + if (_gameRef) { + if (_gameRef->isRightDoubleClick()) { + _gameRef->onMouseRightDblClick(); + } else { + _gameRef->onMouseRightDown(); + } + } + break; + case Common::EVENT_MBUTTONDOWN: + if (_gameRef) { + _gameRef->onMouseMiddleDown(); + } + break; + case Common::EVENT_LBUTTONUP: + if (_gameRef) { + _gameRef->onMouseLeftUp(); + } + break; + case Common::EVENT_RBUTTONUP: + if (_gameRef) { + _gameRef->onMouseRightUp(); + } + break; + case Common::EVENT_MBUTTONUP: + if (_gameRef) { + _gameRef->onMouseMiddleUp(); + } + break; + case Common::EVENT_KEYDOWN: + if (event->kbd.flags & Common::KBD_CTRL) { + if (event->kbd.keycode == Common::KEYCODE_d) { + _engineRef->trigDebugger(); + } + } + if (_gameRef) { + _gameRef->handleKeypress(event); + } + break; + case Common::EVENT_KEYUP: + if (_gameRef) { + _gameRef->handleKeyRelease(event); + } + break; + case Common::EVENT_WHEELUP: + case Common::EVENT_WHEELDOWN: + if (_gameRef) { + _gameRef->handleMouseWheel(event->mouse.y); + } + break; +// Focus-events have been removed (_gameRef->onActivate originally) + case Common::EVENT_RTL: + _gameRef->_quitting = true; + break; + case Common::EVENT_QUIT: +// Block kept in case we want to support autoSaveOnExit. +// Originally this was the behaviour for WME Lite on iOS: +// if (_gameRef) { +// _gameRef->AutoSaveOnExit(); +// _gameRef->_quitting = true; +// } + if (_gameRef) { + _gameRef->onWindowClose(); + } + break; + default: + // TODO: Do we care about any other events? + break; + + } +} + + +////////////////////////////////////////////////////////////////////////// +// Win32 API bindings +////////////////////////////////////////////////////////////////////////// +bool BasePlatform::getCursorPos(Point32 *lpPoint) { + BaseRenderOSystem *renderer = static_cast<BaseRenderOSystem *>(_gameRef->_renderer); + + Common::Point p = g_system->getEventManager()->getMousePos(); + lpPoint->x = p.x; + lpPoint->y = p.y; + + renderer->pointFromScreen(lpPoint); + + return true; +} + +////////////////////////////////////////////////////////////////////////// +bool BasePlatform::setCursorPos(int x, int y) { + BaseRenderOSystem *renderer = static_cast<BaseRenderOSystem *>(_gameRef->_renderer); + + Point32 p; + p.x = x; + p.y = y; + renderer->pointToScreen(&p); + + g_system->warpMouse(x, y); + return true; +} + +////////////////////////////////////////////////////////////////////////// +bool BasePlatform::showWindow(int nCmdShow) { + return false; +} + +////////////////////////////////////////////////////////////////////////// +void BasePlatform::setCapture() { + return; +} + +////////////////////////////////////////////////////////////////////////// +bool BasePlatform::releaseCapture() { + return false; +} + +////////////////////////////////////////////////////////////////////////// +bool BasePlatform::setRectEmpty(Rect32 *lprc) { + lprc->left = lprc->right = lprc->top = lprc->bottom = 0; + return true; +} + +////////////////////////////////////////////////////////////////////////// +bool BasePlatform::isRectEmpty(const Rect32 *lprc) { + return (lprc->left >= lprc->right) || (lprc->top >= lprc->bottom); +} + +////////////////////////////////////////////////////////////////////////// +bool BasePlatform::ptInRect(Rect32 *lprc, Point32 p) { + return (p.x >= lprc->left) && (p.x < lprc->right) && (p.y >= lprc->top) && (p.y < lprc->bottom); +} + +////////////////////////////////////////////////////////////////////////// +bool BasePlatform::setRect(Rect32 *lprc, int left, int top, int right, int bottom) { + lprc->left = left; + lprc->top = top; + lprc->right = right; + lprc->bottom = bottom; + + return true; +} + +////////////////////////////////////////////////////////////////////////// +bool BasePlatform::intersectRect(Rect32 *lprcDst, const Rect32 *lprcSrc1, const Rect32 *lprcSrc2) { + if (isRectEmpty(lprcSrc1) || isRectEmpty(lprcSrc2) || + lprcSrc1->left >= lprcSrc2->right || lprcSrc2->left >= lprcSrc1->right || + lprcSrc1->top >= lprcSrc2->bottom || lprcSrc2->top >= lprcSrc1->bottom) { + setRectEmpty(lprcDst); + return false; + } + lprcDst->left = MAX(lprcSrc1->left, lprcSrc2->left); + lprcDst->right = MIN(lprcSrc1->right, lprcSrc2->right); + lprcDst->top = MAX(lprcSrc1->top, lprcSrc2->top); + lprcDst->bottom = MIN(lprcSrc1->bottom, lprcSrc2->bottom); + + return true; +} + +////////////////////////////////////////////////////////////////////////// +bool BasePlatform::unionRect(Rect32 *lprcDst, Rect32 *lprcSrc1, Rect32 *lprcSrc2) { + if (isRectEmpty(lprcSrc1)) { + if (isRectEmpty(lprcSrc2)) { + setRectEmpty(lprcDst); + return false; + } else { + *lprcDst = *lprcSrc2; + } + } else { + if (isRectEmpty(lprcSrc2)) { + *lprcDst = *lprcSrc1; + } else { + lprcDst->left = MIN(lprcSrc1->left, lprcSrc2->left); + lprcDst->top = MIN(lprcSrc1->top, lprcSrc2->top); + lprcDst->right = MAX(lprcSrc1->right, lprcSrc2->right); + lprcDst->bottom = MAX(lprcSrc1->bottom, lprcSrc2->bottom); + } + } + + return true; +} + +////////////////////////////////////////////////////////////////////////// +bool BasePlatform::copyRect(Rect32 *lprcDst, Rect32 *lprcSrc) { + if (lprcDst == NULL || lprcSrc == NULL) { + return false; + } + + *lprcDst = *lprcSrc; + return true; +} + +////////////////////////////////////////////////////////////////////////// +AnsiString BasePlatform::getPlatformName() { + // TODO: Should conform to the WME-spec. + //return AnsiString(SDL_GetPlatform()); + return AnsiString("ScummVM"); +} + +////////////////////////////////////////////////////////////////////////// +char *BasePlatform::strlwr(char *string) { + if (string) { + for (size_t i = 0; i < strlen(string); ++i) { + string[i] = tolower(string[i]); + } + } + return string; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/platform_osystem.h b/engines/wintermute/platform_osystem.h new file mode 100644 index 0000000000..8c39b29ea9 --- /dev/null +++ b/engines/wintermute/platform_osystem.h @@ -0,0 +1,75 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_PLATFORMSDL_H +#define WINTERMUTE_PLATFORMSDL_H + +#include "engines/wintermute/dctypes.h" +#include "engines/wintermute/math/rect32.h" +#include "common/events.h" + +namespace Wintermute { + +class BaseGame; +class WintermuteEngine; +////////////////////////////////////////////////////////////////////////// +class BasePlatform { +public: + static int initialize(WintermuteEngine *engineRef, BaseGame *inGame, int argc, char *argv[]); + static void deinit(); + static void handleEvent(Common::Event *event); + static AnsiString getPlatformName(); + + // Win32 API bindings + static bool getCursorPos(Point32 *lpPoint); + static bool setCursorPos(int x, int y); + static bool showWindow(int nCmdShow); + + static void setCapture(); + static bool releaseCapture(); + + static bool setRectEmpty(Rect32 *lprc); + static bool isRectEmpty(const Rect32 *lprc); + static bool ptInRect(Rect32 *lprc, Point32 p); + static bool setRect(Rect32 *lprc, int left, int top, int right, int bottom); + static bool intersectRect(Rect32 *lprcDst, const Rect32 *lprcSrc1, const Rect32 *lprcSrc2); + static bool unionRect(Rect32 *lprcDst, Rect32 *lprcSrc1, Rect32 *lprcSrc2); + static bool copyRect(Rect32 *lprcDst, Rect32 *lprcSrc); + + // string functions + static char *strlwr(char *string); + +private: + // Set by initialize on game-startup, the object referred to is also deleted by deinit in WintermuteEngine + static BaseGame *_gameRef; + static WintermuteEngine *_engineRef; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/system/sys_class.cpp b/engines/wintermute/system/sys_class.cpp new file mode 100644 index 0000000000..06b36b84de --- /dev/null +++ b/engines/wintermute/system/sys_class.cpp @@ -0,0 +1,220 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/persistent.h" +#include "engines/wintermute/system/sys_instance.h" +#include "engines/wintermute/system/sys_class.h" +#include "engines/wintermute/system/sys_class_registry.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_persistence_manager.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////////// +SystemClass::SystemClass(const AnsiString &name, PERSISTBUILD build, PERSISTLOAD load, bool persistentClass) { + _name = name; + + _build = build; + _load = load; + _next = NULL; + _savedID = -1; + _persistent = persistentClass; + _numInst = 0; + + SystemClassRegistry::getInstance()->registerClass(this); +} + + +////////////////////////////////////////////////////////////////////////// +SystemClass::~SystemClass() { + SystemClassRegistry::getInstance()->unregisterClass(this); + removeAllInstances(); +} + +////////////////////////////////////////////////////////////////////////// +bool SystemClass::removeAllInstances() { + Instances::iterator it; + for (it = _instances.begin(); it != _instances.end(); ++it) { + delete(it->_value); + } + _instances.clear(); + _instanceMap.clear(); + + return true; +} + +////////////////////////////////////////////////////////////////////////// +SystemInstance *SystemClass::addInstance(void *instance, int id, int savedId) { + SystemInstance *inst = new SystemInstance(instance, id, this); + inst->setSavedID(savedId); + _instances[inst] = (inst); + + _instanceMap[instance] = inst; + + SystemClassRegistry::getInstance()->addInstanceToTable(inst, instance); + + return inst; +} + + +////////////////////////////////////////////////////////////////////////// +bool SystemClass::removeInstance(void *instance) { + InstanceMap::iterator mapIt = _instanceMap.find(instance); + if (mapIt == _instanceMap.end()) { + return false; + } + + Instances::iterator it = _instances.find((mapIt->_value)); + if (it != _instances.end()) { + delete(it->_value); + _instances.erase(it); + } + + _instanceMap.erase(mapIt); + + return false; +} + +////////////////////////////////////////////////////////////////////////// +int SystemClass::getInstanceID(void *pointer) { + InstanceMap::iterator mapIt = _instanceMap.find(pointer); + if (mapIt == _instanceMap.end()) { + return -1; + } else { + return (mapIt->_value)->getID(); + } +} + +////////////////////////////////////////////////////////////////////////// +void *SystemClass::idToPointer(int savedID) { + //slow + Instances::iterator it; + for (it = _instances.begin(); it != _instances.end(); ++it) { + if ((it->_value)->getSavedID() == savedID) { + return (it->_value)->getInstance(); + } + } + return NULL; +} + +////////////////////////////////////////////////////////////////////////// +int SystemClass::getNumInstances() { + return _instances.size(); +} + +////////////////////////////////////////////////////////////////////////// +void SystemClass::dump(Common::WriteStream *stream) { + Common::String str; + str = Common::String::format("%03d %c %-20s instances: %d\n", _iD, _persistent ? 'p' : ' ', _name.c_str(), getNumInstances()); + stream->write(str.c_str(), str.size()); +} + +////////////////////////////////////////////////////////////////////////// +void SystemClass::saveTable(BaseGame *gameRef, BasePersistenceManager *persistMgr) { + persistMgr->putString(_name.c_str()); + persistMgr->putDWORD(_iD); + persistMgr->putDWORD(_instances.size()); + + Instances::iterator it; + for (it = _instances.begin(); it != _instances.end(); ++it) { + persistMgr->putDWORD((it->_value)->getID()); + } +} + +////////////////////////////////////////////////////////////////////////// +void SystemClass::loadTable(BaseGame *gameRef, BasePersistenceManager *persistMgr) { + _savedID = persistMgr->getDWORD(); + int numInstances = persistMgr->getDWORD(); + + for (int i = 0; i < numInstances; i++) { + int instID = persistMgr->getDWORD(); + if (_persistent) { + + if (i > 0) { + gameRef->LOG(0, "Warning: attempting to load multiple instances of persistent class %s (%d)", _name.c_str(), numInstances); + continue; + } + + Instances::iterator it = _instances.begin(); + if (it != _instances.end()) { + (it->_value)->setSavedID(instID); + SystemClassRegistry::getInstance()->addInstanceToTable((it->_value), (it->_value)->getInstance()); + } else { + gameRef->LOG(0, "Warning: instance %d of persistent class %s not found", i, _name.c_str()); + } + } + // normal instances, create empty objects + else { + void *emptyObject = _build(); + if (!emptyObject) { + warning("HALT"); + } + + addInstance(emptyObject, SystemClassRegistry::getInstance()->getNextID(), instID); + } + + } +} + +////////////////////////////////////////////////////////////////////////// +void SystemClass::saveInstances(BaseGame *Game, BasePersistenceManager *persistMgr) { + Instances::iterator it; + for (it = _instances.begin(); it != _instances.end(); ++it) { + // write instace header + persistMgr->putString("<INSTANCE_HEAD>"); + persistMgr->putDWORD(_iD); + persistMgr->putDWORD((it->_value)->getID()); + persistMgr->putString("</INSTANCE_HEAD>"); + _load((it->_value)->getInstance(), persistMgr); + persistMgr->putString("</INSTANCE>"); + } +} + +////////////////////////////////////////////////////////////////////////// +void SystemClass::loadInstance(void *instance, BasePersistenceManager *persistMgr) { + _load(instance, persistMgr); +} + + +////////////////////////////////////////////////////////////////////////// +void SystemClass::resetSavedIDs() { + Instances::iterator it; + for (it = _instances.begin(); it != _instances.end(); ++it) { + (it->_value)->setSavedID(-1); + } +} + +////////////////////////////////////////////////////////////////////////// +void SystemClass::instanceCallback(SYS_INSTANCE_CALLBACK lpCallback, void *lpData) { + Instances::iterator it; + for (it = _instances.begin(); it != _instances.end(); ++it) { + lpCallback((it->_value)->getInstance(), lpData); + } +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/system/sys_class.h b/engines/wintermute/system/sys_class.h new file mode 100644 index 0000000000..3f91723ed8 --- /dev/null +++ b/engines/wintermute/system/sys_class.h @@ -0,0 +1,130 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_SYSCLASS_H +#define WINTERMUTE_SYSCLASS_H + +#include "engines/wintermute/persistent.h" +#include "engines/wintermute/dctypes.h" +#include "common/hashmap.h" +#include "common/func.h" +#include "common/stream.h" + +namespace Wintermute { +class SystemInstance; +class BaseGame; +class BasePersistenceManager; +class SystemClass; + +} + +namespace Common { +template<typename T> struct Hash; + +template<> struct Hash<void *> : public UnaryFunction<void *, uint> { + uint operator()(void *val) const { + return (uint)((size_t)val); + } +}; + +template<> struct Hash<Wintermute::SystemInstance *> : public UnaryFunction<Wintermute::SystemInstance *, uint> { + uint operator()(Wintermute::SystemInstance *val) const { + return (uint)((size_t)val); + } +}; + + +} + +namespace Wintermute { + +class SystemClass { +public: + SystemClass(const AnsiString &name, PERSISTBUILD build, PERSISTLOAD load, bool persistentClass); + ~SystemClass(); + + int getNumInstances(); + bool removeInstance(void *instance); + SystemInstance *addInstance(void *instance, int id, int savedId = -1); + bool removeAllInstances(); + + int getInstanceID(void *pointer); + void *idToPointer(int savedID); + + void setID(int id) { + _iD = id; + } + int getID() const { + return _iD; + } + + int getSavedID() const { + return _savedID; + } + + bool isPersistent() const { + return _persistent; + } + + AnsiString getName() const { + return _name; + } + + void saveTable(BaseGame *Game, BasePersistenceManager *PersistMgr); + void loadTable(BaseGame *Game, BasePersistenceManager *PersistMgr); + + void saveInstances(BaseGame *Game, BasePersistenceManager *PersistMgr); + void loadInstance(void *instance, BasePersistenceManager *PersistMgr); + + void instanceCallback(SYS_INSTANCE_CALLBACK lpCallback, void *lpData); + + void resetSavedIDs(); + + void dump(Common::WriteStream *stream); + +private: + int _numInst; + bool _persistent; + SystemClass *_next; + int _iD; + int _savedID; + AnsiString _name; + PERSISTBUILD _build; + PERSISTLOAD _load; + + //typedef std::set<SystemInstance *> Instances; + typedef Common::HashMap<SystemInstance *, SystemInstance *> Instances; + Instances _instances; + + typedef Common::HashMap<void *, SystemInstance *> InstanceMap; + InstanceMap _instanceMap; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/system/sys_class_registry.cpp b/engines/wintermute/system/sys_class_registry.cpp new file mode 100644 index 0000000000..7c1911c2bf --- /dev/null +++ b/engines/wintermute/system/sys_class_registry.cpp @@ -0,0 +1,336 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/platform_osystem.h" +#include "engines/wintermute/base/base_engine.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/system/sys_instance.h" +#include "engines/wintermute/system/sys_class_registry.h" +#include "engines/wintermute/system/sys_class.h" +#include "engines/wintermute/wintermute.h" +#include "common/stream.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////////// +SystemClassRegistry::SystemClassRegistry() { + _count = 0; + _disabled = false; +} + + +////////////////////////////////////////////////////////////////////////// +SystemClassRegistry::~SystemClassRegistry() { + unregisterClasses(); +} + +////////////////////////////////////////////////////////////////////////// +SystemClassRegistry *SystemClassRegistry::getInstance() { + return BaseEngine::instance().getClassRegistry(); +} + +void SystemClassRegistry::unregisterClasses() { + // SystemClass calls UnregisterClass upon destruction. + while (_classes.size() > 0) { + delete _classes.begin()->_value; + } +} + +////////////////////////////////////////////////////////////////////////// +bool SystemClassRegistry::registerClass(SystemClass *classObj) { + classObj->setID(_count++); + //_classes.insert(classObj); + _classes[classObj] = classObj; + + _nameMap[classObj->getName()] = classObj; + _idMap[classObj->getID()] = classObj; + + return true; +} + + +////////////////////////////////////////////////////////////////////////// +bool SystemClassRegistry::unregisterClass(SystemClass *classObj) { + + Classes::iterator it = _classes.find(classObj); + if (it == _classes.end()) { + return false; + } + + if (classObj->getNumInstances() != 0) { + debugC(Wintermute::kWintermuteDebugSaveGame, "Memory leak@class %-20s: %d instance(s) left\n", classObj->getName().c_str(), classObj->getNumInstances()); + } + _classes.erase(it); + + NameMap::iterator mapIt = _nameMap.find(classObj->getName()); + if (mapIt != _nameMap.end()) { + _nameMap.erase(mapIt); + } + + IdMap::iterator idIt = _idMap.find(classObj->getID()); + if (idIt != _idMap.end()) { + _idMap.erase(idIt); + } + + + return true; +} + + +////////////////////////////////////////////////////////////////////////// +bool SystemClassRegistry::registerInstance(const char *className, void *instance) { + if (_disabled) { + return true; + } + + NameMap::iterator mapIt = _nameMap.find(className); + if (mapIt == _nameMap.end()) { + return false; + } + + SystemInstance *inst = (*mapIt)._value->addInstance(instance, _count++); + return (inst != NULL); +} + +////////////////////////////////////////////////////////////////////////// +void SystemClassRegistry::addInstanceToTable(SystemInstance *instance, void *pointer) { + _instanceMap[pointer] = instance; + + if (instance->getSavedID() >= 0) { + _savedInstanceMap[instance->getSavedID()] = instance; + } +} + +////////////////////////////////////////////////////////////////////////// +int SystemClassRegistry::getNextID() { + return _count++; +} + +////////////////////////////////////////////////////////////////////////// +bool SystemClassRegistry::unregisterInstance(const char *className, void *instance) { + NameMap::iterator mapIt = _nameMap.find(className); + if (mapIt == _nameMap.end()) { + return false; + } + (*mapIt)._value->removeInstance(instance); + + InstanceMap::iterator instIt = _instanceMap.find(instance); + if (instIt != _instanceMap.end()) { + _instanceMap.erase(instIt); + return true; + } else { + return false; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool SystemClassRegistry::getPointerID(void *pointer, int *classID, int *instanceID) { + if (pointer == NULL) { + return true; + } + + InstanceMap::iterator it = _instanceMap.find(pointer); + if (it == _instanceMap.end()) { + return false; + } + + + SystemInstance *inst = (*it)._value; + *instanceID = inst->getID(); + *classID = inst->getClass()->getID(); + + return true; +} + +////////////////////////////////////////////////////////////////////////// +void *SystemClassRegistry::idToPointer(int classID, int instanceID) { + SavedInstanceMap::iterator it = _savedInstanceMap.find(instanceID); + if (it == _savedInstanceMap.end()) { + return NULL; + } else { + return (*it)._value->getInstance(); + } +} + +bool checkHeader(const char *tag, BasePersistenceManager *pm) { + char *test = pm->getString(); + Common::String verify = test; + delete[] test; + bool retVal = (verify == tag); + if (!retVal) { + error("Expected %s in Save-file not found", tag); + } + return retVal; +} + +////////////////////////////////////////////////////////////////////////// +bool SystemClassRegistry::saveTable(BaseGame *gameRef, BasePersistenceManager *persistMgr, bool quickSave) { + persistMgr->putString("<CLASS_REGISTRY_TABLE>"); + persistMgr->putDWORD(_classes.size()); + + int counter = 0; + + Classes::iterator it; + for (it = _classes.begin(); it != _classes.end(); ++it) { + counter++; + + if (!quickSave) { + gameRef->_renderer->setIndicatorVal((int)(50.0f / (float)((float)_classes.size() / (float)counter))); + } + + (it->_value)->saveTable(gameRef, persistMgr); + } + persistMgr->putString("</CLASS_REGISTRY_TABLE>"); + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool SystemClassRegistry::loadTable(BaseGame *gameRef, BasePersistenceManager *persistMgr) { + checkHeader("<CLASS_REGISTRY_TABLE>", persistMgr); + + // reset SavedID of current instances + Classes::iterator it; + for (it = _classes.begin(); it != _classes.end(); ++it) { + (it->_value)->resetSavedIDs(); + } + + for (it = _classes.begin(); it != _classes.end(); ++it) { + if ((it->_value)->isPersistent()) { + continue; + } + (it->_value)->removeAllInstances(); + } + + _instanceMap.clear(); + + uint32 numClasses = persistMgr->getDWORD(); + + for (uint32 i = 0; i < numClasses; i++) { + gameRef->_renderer->setIndicatorVal((int)(50.0f / (float)((float)numClasses / (float)i))); + + Common::String className = persistMgr->getStringObj(); + NameMap::iterator mapIt = _nameMap.find(className); + if (mapIt != _nameMap.end()) { + (*mapIt)._value->loadTable(gameRef, persistMgr); + } + } + + checkHeader("</CLASS_REGISTRY_TABLE>", persistMgr); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool SystemClassRegistry::saveInstances(BaseGame *gameRef, BasePersistenceManager *persistMgr, bool quickSave) { + + Classes::iterator it; + + // count total instances + int numInstances = 0; + for (it = _classes.begin(); it != _classes.end(); ++it) { + numInstances += (it->_value)->getNumInstances(); + } + + persistMgr->putDWORD(numInstances); + + int counter = 0; + for (it = _classes.begin(); it != _classes.end(); ++it) { + counter++; + + if (!quickSave) { + if (counter % 20 == 0) { + gameRef->_renderer->setIndicatorVal((int)(50.0f + 50.0f / (float)((float)_classes.size() / (float)counter))); + } + } + gameRef->miniUpdate(); + + (it->_value)->saveInstances(gameRef, persistMgr); + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool SystemClassRegistry::loadInstances(BaseGame *gameRef, BasePersistenceManager *persistMgr) { + // get total instances + int numInstances = persistMgr->getDWORD(); + + for (int i = 0; i < numInstances; i++) { + if (i % 20 == 0) { + gameRef->_renderer->setIndicatorVal((int)(50.0f + 50.0f / (float)((float)numInstances / (float)i))); + } + + checkHeader("<INSTANCE_HEAD>", persistMgr); + + int classID = persistMgr->getDWORD(); + int instanceID = persistMgr->getDWORD(); + void *instance = idToPointer(classID, instanceID); + + checkHeader("</INSTANCE_HEAD>", persistMgr); + + Classes::iterator it; + for (it = _classes.begin(); it != _classes.end(); ++it) { + if ((it->_value)->getSavedID() == classID) { + (it->_value)->loadInstance(instance, persistMgr); + break; + } + } + checkHeader("</INSTANCE>", persistMgr); + } + + _savedInstanceMap.clear(); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool SystemClassRegistry::enumInstances(SYS_INSTANCE_CALLBACK lpCallback, const char *className, void *lpData) { + NameMap::iterator mapIt = _nameMap.find(className); + if (mapIt == _nameMap.end()) { + return STATUS_FAILED; + } + + (*mapIt)._value->instanceCallback(lpCallback, lpData); + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +void SystemClassRegistry::dumpClasses(Common::WriteStream *stream) { + Classes::iterator it; + for (it = _classes.begin(); it != _classes.end(); ++it) { + (it->_value)->dump(stream); + } +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/system/sys_class_registry.h b/engines/wintermute/system/sys_class_registry.h new file mode 100644 index 0000000000..ef7218c7c1 --- /dev/null +++ b/engines/wintermute/system/sys_class_registry.h @@ -0,0 +1,106 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_SYSCLASSREGISTRY_H +#define WINTERMUTE_SYSCLASSREGISTRY_H + +#include "engines/wintermute/wintypes.h" +#include "engines/wintermute/dctypes.h" +#include "engines/wintermute/system/sys_class.h" +#include "common/hashmap.h" +#include "common/hash-str.h" +#include "common/func.h" +#include "common/stream.h" + +namespace Wintermute { +class SystemClass; +} + +namespace Common { +template<typename T> struct Hash; +template<> struct Hash<Wintermute::SystemClass *> : public UnaryFunction<Wintermute::SystemClass *, uint> { + uint operator()(Wintermute::SystemClass *val) const { + return (uint)((size_t)val); + } +}; + +} + +namespace Wintermute { + +class BaseGame; +class BasePersistenceManager; +class SystemInstance; + +class SystemClassRegistry { + void unregisterClasses(); +public: + void registerClasses(); // persistent.cpp + static SystemClassRegistry *getInstance(); + + SystemClassRegistry(); + virtual ~SystemClassRegistry(); + + bool enumInstances(SYS_INSTANCE_CALLBACK lpCallback, const char *className, void *lpData); + bool loadTable(BaseGame *Game, BasePersistenceManager *PersistMgr); + bool saveTable(BaseGame *Game, BasePersistenceManager *PersistMgr, bool quickSave); + bool loadInstances(BaseGame *Game, BasePersistenceManager *PersistMgr); + bool saveInstances(BaseGame *Game, BasePersistenceManager *PersistMgr, bool quickSave); + void *idToPointer(int classID, int instanceID); + bool getPointerID(void *pointer, int *classID, int *instanceID); + bool registerClass(SystemClass *classObj); + bool unregisterClass(SystemClass *classObj); + bool registerInstance(const char *className, void *instance); + bool unregisterInstance(const char *className, void *instance); + void dumpClasses(Common::WriteStream *stream); + int getNextID(); + void addInstanceToTable(SystemInstance *instance, void *pointer); + + bool _disabled; + int _count; + + typedef Common::HashMap<SystemClass *, SystemClass *> Classes; + Classes _classes; + + typedef Common::HashMap<AnsiString, SystemClass *> NameMap; + NameMap _nameMap; + + typedef Common::HashMap<int, SystemClass *> IdMap; + IdMap _idMap; + + typedef Common::HashMap<void *, SystemInstance *> InstanceMap; + InstanceMap _instanceMap; + + typedef Common::HashMap<int, SystemInstance *> SavedInstanceMap; + SavedInstanceMap _savedInstanceMap; + +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/system/sys_instance.cpp b/engines/wintermute/system/sys_instance.cpp new file mode 100644 index 0000000000..d106119dba --- /dev/null +++ b/engines/wintermute/system/sys_instance.cpp @@ -0,0 +1,49 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/system/sys_instance.h" +#include "engines/wintermute/system/sys_class_registry.h" +#include "engines/wintermute/system/sys_class.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////////// +SystemInstance::SystemInstance(void *instance, int id, SystemClass *sysClass) { + _instance = instance; + _id = id; + _savedID = -1; + _class = sysClass; + + _used = false; +} + +////////////////////////////////////////////////////////////////////////// +SystemInstance::~SystemInstance() { +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/system/sys_instance.h b/engines/wintermute/system/sys_instance.h new file mode 100644 index 0000000000..215a6d1437 --- /dev/null +++ b/engines/wintermute/system/sys_instance.h @@ -0,0 +1,68 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_SYSINSTANCE_H +#define WINTERMUTE_SYSINSTANCE_H + +namespace Wintermute { + +class SystemClass; + +class SystemInstance { +public: + SystemInstance(void *instance, int id, SystemClass *sysClass); + virtual ~SystemInstance(); + + int getID() const { + return _id; + } + int getSavedID() const { + return _savedID; + } + void *getInstance() const { + return _instance; + } + SystemClass *getClass() const { + return _class; + } + + void setSavedID(int id) { + _savedID = id; + } + +private: + bool _used; + int _id; + int _savedID; + void *_instance; + SystemClass *_class; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ui/ui_button.cpp b/engines/wintermute/ui/ui_button.cpp new file mode 100644 index 0000000000..7967d566f9 --- /dev/null +++ b/engines/wintermute/ui/ui_button.cpp @@ -0,0 +1,1209 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/ui/ui_button.h" +#include "engines/wintermute/ui/ui_tiled_image.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_active_rect.h" +#include "engines/wintermute/base/font/base_font_storage.h" +#include "engines/wintermute/base/font/base_font.h" +#include "engines/wintermute/base/base_string_table.h" +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/base/scriptables/script_stack.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(UIButton, false) + +////////////////////////////////////////////////////////////////////////// +UIButton::UIButton(BaseGame *inGame) : UIObject(inGame) { + _backPress = _backHover = _backDisable = _backFocus = NULL; + + _fontHover = _fontPress = _fontDisable = _fontFocus = NULL; + + _imageDisable = _imagePress = _imageHover = _imageFocus = NULL; + + _align = TAL_CENTER; + + _hover = _press = false; + + _type = UI_BUTTON; + + _canFocus = false; + _stayPressed = false; + + _oneTimePress = false; + _centerImage = false; + + _pixelPerfect = false; +} + + +////////////////////////////////////////////////////////////////////////// +UIButton::~UIButton() { + delete _backPress; + delete _backHover; + delete _backDisable; + delete _backFocus; + + if (!_sharedFonts) { + if (_fontHover) { + _gameRef->_fontStorage->removeFont(_fontHover); + } + if (_fontPress) { + _gameRef->_fontStorage->removeFont(_fontPress); + } + if (_fontDisable) { + _gameRef->_fontStorage->removeFont(_fontDisable); + } + if (_fontFocus) { + _gameRef->_fontStorage->removeFont(_fontFocus); + } + } + + if (!_sharedImages) { + delete _imageHover; + delete _imagePress; + delete _imageDisable; + delete _imageFocus; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool UIButton::loadFile(const char *filename) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "UIButton::LoadFile failed for file '%s'", filename); + return STATUS_FAILED; + } + + bool ret; + + setFilename(filename); + + if (DID_FAIL(ret = loadBuffer(buffer, true))) { + _gameRef->LOG(0, "Error parsing BUTTON file '%s'", filename); + } + + delete[] buffer; + + return ret; +} + + +TOKEN_DEF_START +TOKEN_DEF(BUTTON) +TOKEN_DEF(TEMPLATE) +TOKEN_DEF(DISABLED) +TOKEN_DEF(VISIBLE) +TOKEN_DEF(FOCUSABLE) +TOKEN_DEF(BACK_HOVER) +TOKEN_DEF(BACK_PRESS) +TOKEN_DEF(BACK_DISABLE) +TOKEN_DEF(BACK_FOCUS) +TOKEN_DEF(BACK) +TOKEN_DEF(CENTER_IMAGE) +TOKEN_DEF(IMAGE_HOVER) +TOKEN_DEF(IMAGE_PRESS) +TOKEN_DEF(IMAGE_DISABLE) +TOKEN_DEF(IMAGE_FOCUS) +TOKEN_DEF(IMAGE) +TOKEN_DEF(FONT_HOVER) +TOKEN_DEF(FONT_PRESS) +TOKEN_DEF(FONT_DISABLE) +TOKEN_DEF(FONT_FOCUS) +TOKEN_DEF(FONT) +TOKEN_DEF(TEXT_ALIGN) +TOKEN_DEF(TEXT) +TOKEN_DEF(X) +TOKEN_DEF(Y) +TOKEN_DEF(WIDTH) +TOKEN_DEF(HEIGHT) +TOKEN_DEF(CURSOR) +TOKEN_DEF(NAME) +TOKEN_DEF(EVENTS) +TOKEN_DEF(SCRIPT) +TOKEN_DEF(CAPTION) +TOKEN_DEF(PARENT_NOTIFY) +TOKEN_DEF(PRESSED) +TOKEN_DEF(PIXEL_PERFECT) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool UIButton::loadBuffer(byte *buffer, bool complete) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(BUTTON) + TOKEN_TABLE(TEMPLATE) + TOKEN_TABLE(DISABLED) + TOKEN_TABLE(VISIBLE) + TOKEN_TABLE(FOCUSABLE) + TOKEN_TABLE(BACK_HOVER) + TOKEN_TABLE(BACK_PRESS) + TOKEN_TABLE(BACK_DISABLE) + TOKEN_TABLE(BACK_FOCUS) + TOKEN_TABLE(BACK) + TOKEN_TABLE(CENTER_IMAGE) + TOKEN_TABLE(IMAGE_HOVER) + TOKEN_TABLE(IMAGE_PRESS) + TOKEN_TABLE(IMAGE_DISABLE) + TOKEN_TABLE(IMAGE_FOCUS) + TOKEN_TABLE(IMAGE) + TOKEN_TABLE(FONT_HOVER) + TOKEN_TABLE(FONT_PRESS) + TOKEN_TABLE(FONT_DISABLE) + TOKEN_TABLE(FONT_FOCUS) + TOKEN_TABLE(FONT) + TOKEN_TABLE(TEXT_ALIGN) + TOKEN_TABLE(TEXT) + TOKEN_TABLE(X) + TOKEN_TABLE(Y) + TOKEN_TABLE(WIDTH) + TOKEN_TABLE(HEIGHT) + TOKEN_TABLE(CURSOR) + TOKEN_TABLE(NAME) + TOKEN_TABLE(EVENTS) + TOKEN_TABLE(SCRIPT) + TOKEN_TABLE(CAPTION) + TOKEN_TABLE(PARENT_NOTIFY) + TOKEN_TABLE(PRESSED) + TOKEN_TABLE(PIXEL_PERFECT) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE_END + + byte *params; + int cmd = 2; + BaseParser parser; + + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_BUTTON) { + _gameRef->LOG(0, "'BUTTON' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + while (cmd > 0 && (cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_TEMPLATE: + if (DID_FAIL(loadFile((char *)params))) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_NAME: + setName((char *)params); + break; + + case TOKEN_CAPTION: + setCaption((char *)params); + break; + + case TOKEN_BACK: + delete _back; + _back = new UITiledImage(_gameRef); + if (!_back || DID_FAIL(_back->loadFile((char *)params))) { + delete _back; + _back = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_BACK_HOVER: + delete _backHover; + _backHover = new UITiledImage(_gameRef); + if (!_backHover || DID_FAIL(_backHover->loadFile((char *)params))) { + delete _backHover; + _backHover = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_BACK_PRESS: + delete _backPress; + _backPress = new UITiledImage(_gameRef); + if (!_backPress || DID_FAIL(_backPress->loadFile((char *)params))) { + delete _backPress; + _backPress = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_BACK_DISABLE: + delete _backDisable; + _backDisable = new UITiledImage(_gameRef); + if (!_backDisable || DID_FAIL(_backDisable->loadFile((char *)params))) { + delete _backDisable; + _backDisable = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_BACK_FOCUS: + delete _backFocus; + _backFocus = new UITiledImage(_gameRef); + if (!_backFocus || DID_FAIL(_backFocus->loadFile((char *)params))) { + delete _backFocus; + _backFocus = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_IMAGE: + delete _image; + _image = new BaseSprite(_gameRef); + if (!_image || DID_FAIL(_image->loadFile((char *)params))) { + delete _image; + _image = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_IMAGE_HOVER: + delete _imageHover; + _imageHover = new BaseSprite(_gameRef); + if (!_imageHover || DID_FAIL(_imageHover->loadFile((char *)params))) { + delete _imageHover; + _imageHover = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_IMAGE_PRESS: + delete _imagePress; + _imagePress = new BaseSprite(_gameRef); + if (!_imagePress || DID_FAIL(_imagePress->loadFile((char *)params))) { + delete _imagePress; + _imagePress = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_IMAGE_DISABLE: + delete _imageDisable; + _imageDisable = new BaseSprite(_gameRef); + if (!_imageDisable || DID_FAIL(_imageDisable->loadFile((char *)params))) { + delete _imageDisable; + _imageDisable = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_IMAGE_FOCUS: + delete _imageFocus; + _imageFocus = new BaseSprite(_gameRef); + if (!_imageFocus || DID_FAIL(_imageFocus->loadFile((char *)params))) { + delete _imageFocus; + _imageFocus = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_FONT: + if (_font) { + _gameRef->_fontStorage->removeFont(_font); + } + _font = _gameRef->_fontStorage->addFont((char *)params); + if (!_font) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_FONT_HOVER: + if (_fontHover) { + _gameRef->_fontStorage->removeFont(_fontHover); + } + _fontHover = _gameRef->_fontStorage->addFont((char *)params); + if (!_fontHover) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_FONT_PRESS: + if (_fontPress) { + _gameRef->_fontStorage->removeFont(_fontPress); + } + _fontPress = _gameRef->_fontStorage->addFont((char *)params); + if (!_fontPress) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_FONT_DISABLE: + if (_fontDisable) { + _gameRef->_fontStorage->removeFont(_fontDisable); + } + _fontDisable = _gameRef->_fontStorage->addFont((char *)params); + if (!_fontDisable) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_FONT_FOCUS: + if (_fontFocus) { + _gameRef->_fontStorage->removeFont(_fontFocus); + } + _fontFocus = _gameRef->_fontStorage->addFont((char *)params); + if (!_fontFocus) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_TEXT: + setText((char *)params); + _gameRef->_stringTable->expand(&_text); + break; + + case TOKEN_TEXT_ALIGN: + if (scumm_stricmp((char *)params, "left") == 0) { + _align = TAL_LEFT; + } else if (scumm_stricmp((char *)params, "right") == 0) { + _align = TAL_RIGHT; + } else { + _align = TAL_CENTER; + } + break; + + case TOKEN_X: + parser.scanStr((char *)params, "%d", &_posX); + break; + + case TOKEN_Y: + parser.scanStr((char *)params, "%d", &_posY); + break; + + case TOKEN_WIDTH: + parser.scanStr((char *)params, "%d", &_width); + break; + + case TOKEN_HEIGHT: + parser.scanStr((char *)params, "%d", &_height); + break; + + case TOKEN_CURSOR: + delete _cursor; + _cursor = new BaseSprite(_gameRef); + if (!_cursor || DID_FAIL(_cursor->loadFile((char *)params))) { + delete _cursor; + _cursor = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_SCRIPT: + addScript((char *)params); + break; + + case TOKEN_PARENT_NOTIFY: + parser.scanStr((char *)params, "%b", &_parentNotify); + break; + + case TOKEN_DISABLED: + parser.scanStr((char *)params, "%b", &_disable); + break; + + case TOKEN_VISIBLE: + parser.scanStr((char *)params, "%b", &_visible); + break; + + case TOKEN_FOCUSABLE: + parser.scanStr((char *)params, "%b", &_canFocus); + break; + + case TOKEN_CENTER_IMAGE: + parser.scanStr((char *)params, "%b", &_centerImage); + break; + + case TOKEN_PRESSED: + parser.scanStr((char *)params, "%b", &_stayPressed); + break; + + case TOKEN_PIXEL_PERFECT: + parser.scanStr((char *)params, "%b", &_pixelPerfect); + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty(params, false); + break; + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in BUTTON definition"); + return STATUS_FAILED; + } + if (cmd == PARSERR_GENERIC) { + _gameRef->LOG(0, "Error loading BUTTON definition"); + return STATUS_FAILED; + } + + correctSize(); + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool UIButton::saveAsText(BaseDynamicBuffer *buffer, int indent) { + buffer->putTextIndent(indent, "BUTTON\n"); + buffer->putTextIndent(indent, "{\n"); + + buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", getName()); + buffer->putTextIndent(indent + 2, "CAPTION=\"%s\"\n", getCaption()); + + buffer->putTextIndent(indent + 2, "\n"); + + if (_back && _back->getFilename()) { + buffer->putTextIndent(indent + 2, "BACK=\"%s\"\n", _back->getFilename()); + } + if (_backHover && _backHover->getFilename()) { + buffer->putTextIndent(indent + 2, "BACK_HOVER=\"%s\"\n", _backHover->getFilename()); + } + if (_backPress && _backPress->getFilename()) { + buffer->putTextIndent(indent + 2, "BACK_PRESS=\"%s\"\n", _backPress->getFilename()); + } + if (_backDisable && _backDisable->getFilename()) { + buffer->putTextIndent(indent + 2, "BACK_DISABLE=\"%s\"\n", _backDisable->getFilename()); + } + if (_backFocus && _backFocus->getFilename()) { + buffer->putTextIndent(indent + 2, "BACK_FOCUS=\"%s\"\n", _backFocus->getFilename()); + } + + if (_image && _image->getFilename()) { + buffer->putTextIndent(indent + 2, "IMAGE=\"%s\"\n", _image->getFilename()); + } + if (_imageHover && _imageHover->getFilename()) { + buffer->putTextIndent(indent + 2, "IMAGE_HOVER=\"%s\"\n", _imageHover->getFilename()); + } + if (_imagePress && _imagePress->getFilename()) { + buffer->putTextIndent(indent + 2, "IMAGE_PRESS=\"%s\"\n", _imagePress->getFilename()); + } + if (_imageDisable && _imageDisable->getFilename()) { + buffer->putTextIndent(indent + 2, "IMAGE_DISABLE=\"%s\"\n", _imageDisable->getFilename()); + } + if (_imageFocus && _imageFocus->getFilename()) { + buffer->putTextIndent(indent + 2, "IMAGE_FOCUS=\"%s\"\n", _imageFocus->getFilename()); + } + + if (_font && _font->getFilename()) { + buffer->putTextIndent(indent + 2, "FONT=\"%s\"\n", _font->getFilename()); + } + if (_fontHover && _fontHover->getFilename()) { + buffer->putTextIndent(indent + 2, "FONT_HOVER=\"%s\"\n", _fontHover->getFilename()); + } + if (_fontPress && _fontPress->getFilename()) { + buffer->putTextIndent(indent + 2, "FONT_PRESS=\"%s\"\n", _fontPress->getFilename()); + } + if (_fontDisable && _fontDisable->getFilename()) { + buffer->putTextIndent(indent + 2, "FONT_DISABLE=\"%s\"\n", _fontDisable->getFilename()); + } + if (_fontFocus && _fontFocus->getFilename()) { + buffer->putTextIndent(indent + 2, "FONT_FOCUS=\"%s\"\n", _fontFocus->getFilename()); + } + + if (_cursor && _cursor->getFilename()) { + buffer->putTextIndent(indent + 2, "CURSOR=\"%s\"\n", _cursor->getFilename()); + } + + + buffer->putTextIndent(indent + 2, "\n"); + + if (_text) { + buffer->putTextIndent(indent + 2, "TEXT=\"%s\"\n", _text); + } + + switch (_align) { + case TAL_LEFT: + buffer->putTextIndent(indent + 2, "TEXT_ALIGN=\"%s\"\n", "left"); + break; + case TAL_RIGHT: + buffer->putTextIndent(indent + 2, "TEXT_ALIGN=\"%s\"\n", "right"); + break; + case TAL_CENTER: + buffer->putTextIndent(indent + 2, "TEXT_ALIGN=\"%s\"\n", "center"); + break; + default: + warning("UIButton::SaveAsText - unhandled enum"); + break; + } + + buffer->putTextIndent(indent + 2, "\n"); + + buffer->putTextIndent(indent + 2, "X=%d\n", _posX); + buffer->putTextIndent(indent + 2, "Y=%d\n", _posY); + buffer->putTextIndent(indent + 2, "WIDTH=%d\n", _width); + buffer->putTextIndent(indent + 2, "HEIGHT=%d\n", _height); + + + buffer->putTextIndent(indent + 2, "DISABLED=%s\n", _disable ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "VISIBLE=%s\n", _visible ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "PARENT_NOTIFY=%s\n", _parentNotify ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "FOCUSABLE=%s\n", _canFocus ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "CENTER_IMAGE=%s\n", _centerImage ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "PRESSED=%s\n", _stayPressed ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "PIXEL_PERFECT=%s\n", _pixelPerfect ? "TRUE" : "FALSE"); + + buffer->putTextIndent(indent + 2, "\n"); + + // scripts + for (uint32 i = 0; i < _scripts.size(); i++) { + buffer->putTextIndent(indent + 2, "SCRIPT=\"%s\"\n", _scripts[i]->_filename); + } + + buffer->putTextIndent(indent + 2, "\n"); + + // editor properties + BaseClass::saveAsText(buffer, indent + 2); + + buffer->putTextIndent(indent, "}\n"); + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +void UIButton::correctSize() { + Rect32 rect; + + BaseSprite *img = NULL; + if (_image) { + img = _image; + } else if (_imageDisable) { + img = _imageDisable; + } else if (_imageHover) { + img = _imageHover; + } else if (_imagePress) { + img = _imagePress; + } else if (_imageFocus) { + img = _imageFocus; + } + + if (_width <= 0) { + if (img) { + img->getBoundingRect(&rect, 0, 0); + _width = rect.right - rect.left; + } else { + _width = 100; + } + } + + if (_height <= 0) { + if (img) { + img->getBoundingRect(&rect, 0, 0); + _height = rect.bottom - rect.top; + } + } + + if (_text) { + int text_height; + if (_font) { + text_height = _font->getTextHeight((byte *)_text, _width); + } else { + text_height = _gameRef->_systemFont->getTextHeight((byte *)_text, _width); + } + + if (text_height > _height) { + _height = text_height; + } + } + + if (_height <= 0) { + _height = 100; + } + + if (_back) { + _back->correctSize(&_width, &_height); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool UIButton::display(int offsetX, int offsetY) { + if (!_visible) { + return STATUS_OK; + } + + UITiledImage *back = NULL; + BaseSprite *image = NULL; + BaseFont *font = 0; + + //RECT rect; + //BasePlatform::setRect(&rect, OffsetX + _posX, OffsetY + _posY, OffsetX+_posX+_width, OffsetY+_posY+_height); + //_hover = (!_disable && BasePlatform::ptInRect(&rect, _gameRef->_mousePos)!=FALSE); + _hover = (!_disable && _gameRef->_activeObject == this && (_gameRef->_interactive || _gameRef->_state == GAME_SEMI_FROZEN)); + + if ((_press && _hover && !_gameRef->_mouseLeftDown) || + (_oneTimePress && g_system->getMillis() - _oneTimePressTime >= 100)) { + press(); + } + + + if (_disable) { + if (_backDisable) { + back = _backDisable; + } + if (_imageDisable) { + image = _imageDisable; + } + if (_text && _fontDisable) { + font = _fontDisable; + } + } else if (_press || _oneTimePress || _stayPressed) { + if (_backPress) { + back = _backPress; + } + if (_imagePress) { + image = _imagePress; + } + if (_text && _fontPress) { + font = _fontPress; + } + } else if (_hover) { + if (_backHover) { + back = _backHover; + } + if (_imageHover) { + image = _imageHover; + } + if (_text && _fontHover) { + font = _fontHover; + } + } else if (_canFocus && isFocused()) { + if (_backFocus) { + back = _backFocus; + } + if (_imageFocus) { + image = _imageFocus; + } + if (_text && _fontFocus) { + font = _fontFocus; + } + } + + if (!back && _back) { + back = _back; + } + if (!image && _image) { + image = _image; + } + if (_text && !font) { + if (_font) { + font = _font; + } else { + font = _gameRef->_systemFont; + } + } + + int imageX = offsetX + _posX; + int imageY = offsetY + _posY; + + if (image && _centerImage) { + Rect32 rc; + image->getBoundingRect(&rc, 0, 0); + imageX += (_width - (rc.right - rc.left)) / 2; + imageY += (_height - (rc.bottom - rc.top)) / 2; + } + + if (back) { + back->display(offsetX + _posX, offsetY + _posY, _width, _height); + } + //if (image) image->Draw(ImageX +((_press||_oneTimePress)&&back?1:0), ImageY +((_press||_oneTimePress)&&back?1:0), NULL); + if (image) { + image->draw(imageX + ((_press || _oneTimePress) && back ? 1 : 0), imageY + ((_press || _oneTimePress) && back ? 1 : 0), _pixelPerfect ? this : NULL); + } + + if (font && _text) { + int text_offset = (_height - font->getTextHeight((byte *)_text, _width)) / 2; + font->drawText((byte *)_text, offsetX + _posX + ((_press || _oneTimePress) ? 1 : 0), offsetY + _posY + text_offset + ((_press || _oneTimePress) ? 1 : 0), _width, _align); + } + + if (!_pixelPerfect || !_image) { + _gameRef->_renderer->addRectToList(new BaseActiveRect(_gameRef, this, NULL, offsetX + _posX, offsetY + _posY, _width, _height, 100, 100, false)); + } + + // reset unused sprites + if (_image && _image != image) { + _image->reset(); + } + if (_imageDisable && _imageDisable != image) { + _imageDisable->reset(); + } + if (_imageFocus && _imageFocus != image) { + _imageFocus->reset(); + } + if (_imagePress && _imagePress != image) { + _imagePress->reset(); + } + if (_imageHover && _imageHover != image) { + _imageHover->reset(); + } + + _press = _hover && _gameRef->_mouseLeftDown && _gameRef->_capturedObject == this; + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +void UIButton::press() { + applyEvent("Press"); + if (_listenerObject) { + _listenerObject->listen(_listenerParamObject, _listenerParamDWORD); + } + if (_parentNotify && _parent) { + _parent->applyEvent(getName()); + } + + _oneTimePress = false; +} + + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool UIButton::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // SetDisabledFont + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "SetDisabledFont") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + if (_fontDisable) { + _gameRef->_fontStorage->removeFont(_fontDisable); + } + if (val->isNULL()) { + _fontDisable = NULL; + stack->pushBool(true); + } else { + _fontDisable = _gameRef->_fontStorage->addFont(val->getString()); + stack->pushBool(_fontDisable != NULL); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetHoverFont + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetHoverFont") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + if (_fontHover) { + _gameRef->_fontStorage->removeFont(_fontHover); + } + if (val->isNULL()) { + _fontHover = NULL; + stack->pushBool(true); + } else { + _fontHover = _gameRef->_fontStorage->addFont(val->getString()); + stack->pushBool(_fontHover != NULL); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetPressedFont + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetPressedFont") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + if (_fontPress) { + _gameRef->_fontStorage->removeFont(_fontPress); + } + if (val->isNULL()) { + _fontPress = NULL; + stack->pushBool(true); + } else { + _fontPress = _gameRef->_fontStorage->addFont(val->getString()); + stack->pushBool(_fontPress != NULL); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetFocusedFont + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetFocusedFont") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + if (_fontFocus) { + _gameRef->_fontStorage->removeFont(_fontFocus); + } + if (val->isNULL()) { + _fontFocus = NULL; + stack->pushBool(true); + } else { + _fontFocus = _gameRef->_fontStorage->addFont(val->getString()); + stack->pushBool(_fontFocus != NULL); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetDisabledImage + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetDisabledImage") == 0) { + stack->correctParams(1); + + delete _imageDisable; + _imageDisable = new BaseSprite(_gameRef); + const char *filename = stack->pop()->getString(); + if (!_imageDisable || DID_FAIL(_imageDisable->loadFile(filename))) { + delete _imageDisable; + _imageDisable = NULL; + stack->pushBool(false); + } else { + stack->pushBool(true); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetDisabledImage + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetDisabledImage") == 0) { + stack->correctParams(0); + if (!_imageDisable || !_imageDisable->getFilename()) { + stack->pushNULL(); + } else { + stack->pushString(_imageDisable->getFilename()); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetDisabledImageObject + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetDisabledImageObject") == 0) { + stack->correctParams(0); + if (!_imageDisable) { + stack->pushNULL(); + } else { + stack->pushNative(_imageDisable, true); + } + + return STATUS_OK; + } + + + ////////////////////////////////////////////////////////////////////////// + // SetHoverImage + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetHoverImage") == 0) { + stack->correctParams(1); + + delete _imageHover; + _imageHover = new BaseSprite(_gameRef); + const char *filename = stack->pop()->getString(); + if (!_imageHover || DID_FAIL(_imageHover->loadFile(filename))) { + delete _imageHover; + _imageHover = NULL; + stack->pushBool(false); + } else { + stack->pushBool(true); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetHoverImage + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetHoverImage") == 0) { + stack->correctParams(0); + if (!_imageHover || !_imageHover->getFilename()) { + stack->pushNULL(); + } else { + stack->pushString(_imageHover->getFilename()); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetHoverImageObject + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetHoverImageObject") == 0) { + stack->correctParams(0); + if (!_imageHover) { + stack->pushNULL(); + } else { + stack->pushNative(_imageHover, true); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetPressedImage + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetPressedImage") == 0) { + stack->correctParams(1); + + delete _imagePress; + _imagePress = new BaseSprite(_gameRef); + const char *filename = stack->pop()->getString(); + if (!_imagePress || DID_FAIL(_imagePress->loadFile(filename))) { + delete _imagePress; + _imagePress = NULL; + stack->pushBool(false); + } else { + stack->pushBool(true); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetPressedImage + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetPressedImage") == 0) { + stack->correctParams(0); + if (!_imagePress || !_imagePress->getFilename()) { + stack->pushNULL(); + } else { + stack->pushString(_imagePress->getFilename()); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetPressedImageObject + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetPressedImageObject") == 0) { + stack->correctParams(0); + if (!_imagePress) { + stack->pushNULL(); + } else { + stack->pushNative(_imagePress, true); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetFocusedImage + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetFocusedImage") == 0) { + stack->correctParams(1); + + delete _imageFocus; + _imageFocus = new BaseSprite(_gameRef); + const char *filename = stack->pop()->getString(); + if (!_imageFocus || DID_FAIL(_imageFocus->loadFile(filename))) { + delete _imageFocus; + _imageFocus = NULL; + stack->pushBool(false); + } else { + stack->pushBool(true); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetFocusedImage + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetFocusedImage") == 0) { + stack->correctParams(0); + if (!_imageFocus || !_imageFocus->getFilename()) { + stack->pushNULL(); + } else { + stack->pushString(_imageFocus->getFilename()); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetFocusedImageObject + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetFocusedImageObject") == 0) { + stack->correctParams(0); + if (!_imageFocus) { + stack->pushNULL(); + } else { + stack->pushNative(_imageFocus, true); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Press + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Press") == 0) { + stack->correctParams(0); + + if (_visible && !_disable) { + _oneTimePress = true; + _oneTimePressTime = g_system->getMillis(); + } + stack->pushNULL(); + + return STATUS_OK; + } else { + return UIObject::scCallMethod(script, stack, thisStack, name); + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *UIButton::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("button"); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // TextAlign + ////////////////////////////////////////////////////////////////////////// + else if (name == "TextAlign") { + _scValue->setInt(_align); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Focusable + ////////////////////////////////////////////////////////////////////////// + else if (name == "Focusable") { + _scValue->setBool(_canFocus); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // Pressed + ////////////////////////////////////////////////////////////////////////// + else if (name == "Pressed") { + _scValue->setBool(_stayPressed); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // PixelPerfect + ////////////////////////////////////////////////////////////////////////// + else if (name == "PixelPerfect") { + _scValue->setBool(_pixelPerfect); + return _scValue; + } else { + return UIObject::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool UIButton::scSetProperty(const char *name, ScValue *value) { + ////////////////////////////////////////////////////////////////////////// + // TextAlign + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "TextAlign") == 0) { + int i = value->getInt(); + if (i < 0 || i >= NUM_TEXT_ALIGN) { + i = 0; + } + _align = (TTextAlign)i; + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Focusable + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Focusable") == 0) { + _canFocus = value->getBool(); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // Pressed + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Pressed") == 0) { + _stayPressed = value->getBool(); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // PixelPerfect + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "PixelPerfect") == 0) { + _pixelPerfect = value->getBool(); + return STATUS_OK; + } else { + return UIObject::scSetProperty(name, value); + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *UIButton::scToString() { + return "[button]"; +} + + +////////////////////////////////////////////////////////////////////////// +bool UIButton::persist(BasePersistenceManager *persistMgr) { + + UIObject::persist(persistMgr); + + persistMgr->transfer(TMEMBER_INT(_align)); + persistMgr->transfer(TMEMBER(_backDisable)); + persistMgr->transfer(TMEMBER(_backFocus)); + persistMgr->transfer(TMEMBER(_backHover)); + persistMgr->transfer(TMEMBER(_backPress)); + persistMgr->transfer(TMEMBER(_centerImage)); + persistMgr->transfer(TMEMBER(_fontDisable)); + persistMgr->transfer(TMEMBER(_fontFocus)); + persistMgr->transfer(TMEMBER(_fontHover)); + persistMgr->transfer(TMEMBER(_fontPress)); + persistMgr->transfer(TMEMBER(_hover)); + persistMgr->transfer(TMEMBER(_image)); + persistMgr->transfer(TMEMBER(_imageDisable)); + persistMgr->transfer(TMEMBER(_imageFocus)); + persistMgr->transfer(TMEMBER(_imageHover)); + persistMgr->transfer(TMEMBER(_imagePress)); + persistMgr->transfer(TMEMBER(_pixelPerfect)); + persistMgr->transfer(TMEMBER(_press)); + persistMgr->transfer(TMEMBER(_stayPressed)); + + if (!persistMgr->getIsSaving()) { + _oneTimePress = false; + _oneTimePressTime = 0; + } + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ui/ui_button.h b/engines/wintermute/ui/ui_button.h new file mode 100644 index 0000000000..93333a2534 --- /dev/null +++ b/engines/wintermute/ui/ui_button.h @@ -0,0 +1,80 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_UIBUTTON_H +#define WINTERMUTE_UIBUTTON_H + + +#include "engines/wintermute/ui/ui_object.h" +#include "engines/wintermute/dctypes.h" // Added by ClassView + +namespace Wintermute { + +class UIButton : public UIObject { +public: + bool _pixelPerfect; + bool _stayPressed; + bool _centerImage; + bool _oneTimePress; + uint32 _oneTimePressTime; + DECLARE_PERSISTENT(UIButton, UIObject) + void press(); + virtual bool display() { return display(0, 0); } + virtual bool display(int offsetX, int offsetY); + bool _press; + bool _hover; + void correctSize(); + TTextAlign _align; + BaseSprite *_imageHover; + BaseSprite *_imagePress; + BaseSprite *_imageDisable; + BaseSprite *_imageFocus; + BaseFont *_fontDisable; + BaseFont *_fontPress; + BaseFont *_fontHover; + BaseFont *_fontFocus; + UITiledImage *_backPress; + UITiledImage *_backHover; + UITiledImage *_backDisable; + UITiledImage *_backFocus; + UIButton(BaseGame *inGame = NULL); + virtual ~UIButton(); + bool loadFile(const char *filename); + bool loadBuffer(byte *buffer, bool complete = true); + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent); + + // scripting interface + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + virtual const char *scToString(); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ui/ui_edit.cpp b/engines/wintermute/ui/ui_edit.cpp new file mode 100644 index 0000000000..a3283d5a01 --- /dev/null +++ b/engines/wintermute/ui/ui_edit.cpp @@ -0,0 +1,952 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ui/ui_edit.h" +#include "engines/wintermute/ui/ui_object.h" +#include "engines/wintermute/ui/ui_tiled_image.h" +#include "engines/wintermute/utils/string_util.h" +#include "engines/wintermute/base/base_active_rect.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/font/base_font.h" +#include "engines/wintermute/base/font/base_font_storage.h" +#include "engines/wintermute/base/base_keyboard_state.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/base/base_string_table.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/utils/utils.h" +#include "common/util.h" +#include "common/keyboard.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(UIEdit, false) + +////////////////////////////////////////////////////////////////////////// +UIEdit::UIEdit(BaseGame *inGame) : UIObject(inGame) { + _type = UI_EDIT; + + _fontSelected = NULL; + + _selStart = _selEnd = 10000; + _scrollOffset = 0; + + _cursorChar = NULL; + setCursorChar("|"); + + _cursorBlinkRate = 600; + + _frameWidth = 0; + + setText(""); + + _lastBlinkTime = 0; + _cursorVisible = true; + + _maxLength = -1; + + _canFocus = true; +} + + +////////////////////////////////////////////////////////////////////////// +UIEdit::~UIEdit() { + if (!_sharedFonts) { + if (_fontSelected) { + _gameRef->_fontStorage->removeFont(_fontSelected); + } + } + + delete[] _cursorChar; + _cursorChar = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +bool UIEdit::loadFile(const char *filename) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "UIEdit::LoadFile failed for file '%s'", filename); + return STATUS_FAILED; + } + + bool ret; + + setFilename(filename); + + if (DID_FAIL(ret = loadBuffer(buffer, true))) { + _gameRef->LOG(0, "Error parsing EDIT file '%s'", filename); + } + + delete[] buffer; + + return ret; +} + + +TOKEN_DEF_START +TOKEN_DEF(TEMPLATE) +TOKEN_DEF(DISABLED) +TOKEN_DEF(VISIBLE) +TOKEN_DEF(BACK) +TOKEN_DEF(IMAGE) +TOKEN_DEF(FONT_SELECTED) +TOKEN_DEF(FONT) +TOKEN_DEF(TEXT) +TOKEN_DEF(X) +TOKEN_DEF(Y) +TOKEN_DEF(WIDTH) +TOKEN_DEF(HEIGHT) +TOKEN_DEF(CURSOR_BLINK_RATE) +TOKEN_DEF(CURSOR) +TOKEN_DEF(FRAME_WIDTH) +TOKEN_DEF(NAME) +TOKEN_DEF(SCRIPT) +TOKEN_DEF(PARENT_NOTIFY) +TOKEN_DEF(MAX_LENGTH) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF(EDIT) +TOKEN_DEF(CAPTION) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool UIEdit::loadBuffer(byte *buffer, bool complete) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(TEMPLATE) + TOKEN_TABLE(DISABLED) + TOKEN_TABLE(VISIBLE) + TOKEN_TABLE(BACK) + TOKEN_TABLE(IMAGE) + TOKEN_TABLE(FONT_SELECTED) + TOKEN_TABLE(FONT) + TOKEN_TABLE(TEXT) + TOKEN_TABLE(X) + TOKEN_TABLE(Y) + TOKEN_TABLE(WIDTH) + TOKEN_TABLE(HEIGHT) + TOKEN_TABLE(CURSOR_BLINK_RATE) + TOKEN_TABLE(CURSOR) + TOKEN_TABLE(FRAME_WIDTH) + TOKEN_TABLE(NAME) + TOKEN_TABLE(SCRIPT) + TOKEN_TABLE(PARENT_NOTIFY) + TOKEN_TABLE(MAX_LENGTH) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE(EDIT) + TOKEN_TABLE(CAPTION) + TOKEN_TABLE_END + + byte *params; + int cmd = 2; + BaseParser parser; + + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_EDIT) { + _gameRef->LOG(0, "'EDIT' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + while (cmd > 0 && (cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_TEMPLATE: + if (DID_FAIL(loadFile((char *)params))) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_NAME: + setName((char *)params); + break; + + case TOKEN_BACK: + delete _back; + _back = new UITiledImage(_gameRef); + if (!_back || DID_FAIL(_back->loadFile((char *)params))) { + delete _back; + _back = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_IMAGE: + delete _image; + _image = new BaseSprite(_gameRef); + if (!_image || DID_FAIL(_image->loadFile((char *)params))) { + delete _image; + _image = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_FONT: + if (_font) { + _gameRef->_fontStorage->removeFont(_font); + } + _font = _gameRef->_fontStorage->addFont((char *)params); + if (!_font) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_FONT_SELECTED: + if (_fontSelected) { + _gameRef->_fontStorage->removeFont(_fontSelected); + } + _fontSelected = _gameRef->_fontStorage->addFont((char *)params); + if (!_fontSelected) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_TEXT: + setText((char *)params); + _gameRef->_stringTable->expand(&_text); + break; + + case TOKEN_X: + parser.scanStr((char *)params, "%d", &_posX); + break; + + case TOKEN_Y: + parser.scanStr((char *)params, "%d", &_posY); + break; + + case TOKEN_WIDTH: + parser.scanStr((char *)params, "%d", &_width); + break; + + case TOKEN_HEIGHT: + parser.scanStr((char *)params, "%d", &_height); + break; + + case TOKEN_MAX_LENGTH: + parser.scanStr((char *)params, "%d", &_maxLength); + break; + + case TOKEN_CAPTION: + setCaption((char *)params); + break; + + case TOKEN_CURSOR: + delete _cursor; + _cursor = new BaseSprite(_gameRef); + if (!_cursor || DID_FAIL(_cursor->loadFile((char *)params))) { + delete _cursor; + _cursor = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_CURSOR_BLINK_RATE: + parser.scanStr((char *)params, "%d", &_cursorBlinkRate); + break; + + case TOKEN_FRAME_WIDTH: + parser.scanStr((char *)params, "%d", &_frameWidth); + break; + + case TOKEN_SCRIPT: + addScript((char *)params); + break; + + case TOKEN_PARENT_NOTIFY: + parser.scanStr((char *)params, "%b", &_parentNotify); + break; + + case TOKEN_DISABLED: + parser.scanStr((char *)params, "%b", &_disable); + break; + + case TOKEN_VISIBLE: + parser.scanStr((char *)params, "%b", &_visible); + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty(params, false); + break; + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in EDIT definition"); + return STATUS_FAILED; + } + if (cmd == PARSERR_GENERIC) { + _gameRef->LOG(0, "Error loading EDIT definition"); + return STATUS_FAILED; + } + + correctSize(); + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool UIEdit::saveAsText(BaseDynamicBuffer *buffer, int indent) { + buffer->putTextIndent(indent, "EDIT\n"); + buffer->putTextIndent(indent, "{\n"); + + buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", getName()); + buffer->putTextIndent(indent + 2, "CAPTION=\"%s\"\n", getCaption()); + + buffer->putTextIndent(indent + 2, "\n"); + + if (_back && _back->getFilename()) { + buffer->putTextIndent(indent + 2, "BACK=\"%s\"\n", _back->getFilename()); + } + + if (_image && _image->getFilename()) { + buffer->putTextIndent(indent + 2, "IMAGE=\"%s\"\n", _image->getFilename()); + } + + if (_font && _font->getFilename()) { + buffer->putTextIndent(indent + 2, "FONT=\"%s\"\n", _font->getFilename()); + } + if (_fontSelected && _fontSelected->getFilename()) { + buffer->putTextIndent(indent + 2, "FONT_SELECTED=\"%s\"\n", _fontSelected->getFilename()); + } + + if (_cursor && _cursor->getFilename()) { + buffer->putTextIndent(indent + 2, "CURSOR=\"%s\"\n", _cursor->getFilename()); + } + + buffer->putTextIndent(indent + 2, "\n"); + + if (_text) { + buffer->putTextIndent(indent + 2, "TEXT=\"%s\"\n", _text); + } + + buffer->putTextIndent(indent + 2, "\n"); + + buffer->putTextIndent(indent + 2, "X=%d\n", _posX); + buffer->putTextIndent(indent + 2, "Y=%d\n", _posY); + buffer->putTextIndent(indent + 2, "WIDTH=%d\n", _width); + buffer->putTextIndent(indent + 2, "HEIGHT=%d\n", _height); + buffer->putTextIndent(indent + 2, "MAX_LENGTH=%d\n", _maxLength); + buffer->putTextIndent(indent + 2, "CURSOR_BLINK_RATE=%d\n", _cursorBlinkRate); + buffer->putTextIndent(indent + 2, "FRAME_WIDTH=%d\n", _frameWidth); + + buffer->putTextIndent(indent + 2, "DISABLED=%s\n", _disable ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "VISIBLE=%s\n", _visible ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "PARENT_NOTIFY=%s\n", _parentNotify ? "TRUE" : "FALSE"); + + // scripts + for (uint32 i = 0; i < _scripts.size(); i++) { + buffer->putTextIndent(indent + 2, "SCRIPT=\"%s\"\n", _scripts[i]->_filename); + } + + buffer->putTextIndent(indent + 2, "\n"); + + // editor properties + BaseClass::saveAsText(buffer, indent + 2); + + buffer->putTextIndent(indent, "}\n"); + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool UIEdit::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // SetSelectedFont + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "SetSelectedFont") == 0) { + stack->correctParams(1); + + if (_fontSelected) { + _gameRef->_fontStorage->removeFont(_fontSelected); + } + _fontSelected = _gameRef->_fontStorage->addFont(stack->pop()->getString()); + stack->pushBool(_fontSelected != NULL); + + return STATUS_OK; + } else { + return UIObject::scCallMethod(script, stack, thisStack, name); + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *UIEdit::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("editor"); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // SelStart + ////////////////////////////////////////////////////////////////////////// + else if (name == "SelStart") { + _scValue->setInt(_selStart); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // SelEnd + ////////////////////////////////////////////////////////////////////////// + else if (name == "SelEnd") { + _scValue->setInt(_selEnd); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // CursorBlinkRate + ////////////////////////////////////////////////////////////////////////// + else if (name == "CursorBlinkRate") { + _scValue->setInt(_cursorBlinkRate); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // CursorChar + ////////////////////////////////////////////////////////////////////////// + else if (name == "CursorChar") { + _scValue->setString(_cursorChar); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // FrameWidth + ////////////////////////////////////////////////////////////////////////// + else if (name == "FrameWidth") { + _scValue->setInt(_frameWidth); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // MaxLength + ////////////////////////////////////////////////////////////////////////// + else if (name == "MaxLength") { + _scValue->setInt(_maxLength); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Text + ////////////////////////////////////////////////////////////////////////// + else if (name == "Text") { + if (_gameRef->_textEncoding == TEXT_UTF8) { + WideString wstr = StringUtil::ansiToWide(_text); + _scValue->setString(StringUtil::wideToUtf8(wstr).c_str()); + } else { + _scValue->setString(_text); + } + return _scValue; + } else { + return UIObject::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool UIEdit::scSetProperty(const char *name, ScValue *value) { + ////////////////////////////////////////////////////////////////////////// + // SelStart + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "SelStart") == 0) { + _selStart = value->getInt(); + _selStart = MAX(_selStart, 0); + _selStart = (int)MIN((size_t)_selStart, strlen(_text)); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SelEnd + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SelEnd") == 0) { + _selEnd = value->getInt(); + _selEnd = MAX(_selEnd, 0); + _selEnd = (int)MIN((size_t)_selEnd, strlen(_text)); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // CursorBlinkRate + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "CursorBlinkRate") == 0) { + _cursorBlinkRate = (uint32)value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // CursorChar + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "CursorChar") == 0) { + setCursorChar(value->getString()); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // FrameWidth + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "FrameWidth") == 0) { + _frameWidth = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // MaxLength + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "MaxLength") == 0) { + _maxLength = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Text + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Text") == 0) { + if (_gameRef->_textEncoding == TEXT_UTF8) { + WideString wstr = StringUtil::utf8ToWide(value->getString()); + setText(StringUtil::wideToAnsi(wstr).c_str()); + } else { + setText(value->getString()); + } + return STATUS_OK; + } else { + return UIObject::scSetProperty(name, value); + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *UIEdit::scToString() { + return "[edit]"; +} + + +////////////////////////////////////////////////////////////////////////// +void UIEdit::setCursorChar(const char *character) { + if (!character) { + return; + } + delete[] _cursorChar; + _cursorChar = new char [strlen(character) + 1]; + if (_cursorChar) { + strcpy(_cursorChar, character); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool UIEdit::display(int offsetX, int offsetY) { + if (!_visible) { + return STATUS_OK; + } + + + // hack! + TTextEncoding OrigEncoding = _gameRef->_textEncoding; + _gameRef->_textEncoding = TEXT_ANSI; + + if (_back) { + _back->display(offsetX + _posX, offsetY + _posY, _width, _height); + } + if (_image) { + _image->draw(offsetX + _posX, offsetY + _posY, NULL); + } + + // prepare fonts + BaseFont *font; + BaseFont *sfont; + + if (_font) { + font = _font; + } else { + font = _gameRef->_systemFont; + } + + if (_fontSelected) { + sfont = _fontSelected; + } else { + sfont = font; + } + + bool focused = isFocused(); + + _selStart = MAX(_selStart, 0); + _selEnd = MAX(_selEnd, 0); + + _selStart = (int)MIN((size_t)_selStart, strlen(_text)); + _selEnd = (int)MIN((size_t)_selEnd, strlen(_text)); + + //int CursorWidth = font->GetCharWidth(_cursorChar[0]); + int cursorWidth = font->getTextWidth((byte *)_cursorChar); + + int s1, s2; + bool curFirst; + // modify scroll offset + if (_selStart >= _selEnd) { + while (font->getTextWidth((byte *)_text + _scrollOffset, MAX(0, _selEnd - _scrollOffset)) > _width - cursorWidth - 2 * _frameWidth) { + _scrollOffset++; + if (_scrollOffset >= (int)strlen(_text)) { + break; + } + } + + _scrollOffset = MIN(_scrollOffset, _selEnd); + + s1 = _selEnd; + s2 = _selStart; + curFirst = true; + } else { + while (font->getTextWidth((byte *)_text + _scrollOffset, MAX(0, _selStart - _scrollOffset)) + + sfont->getTextWidth((byte *)(_text + MAX(_scrollOffset, _selStart)), _selEnd - MAX(_scrollOffset, _selStart)) + + > _width - cursorWidth - 2 * _frameWidth) { + _scrollOffset++; + if (_scrollOffset >= (int)strlen(_text)) { + break; + } + } + + _scrollOffset = MIN(_scrollOffset, _selEnd); + + s1 = _selStart; + s2 = _selEnd; + curFirst = false; + } + + + int alignOffset = 0; + + for (int count = 0; count < 2; count++) { + // draw text + int xxx, yyy, width, height; + + xxx = _posX + _frameWidth + offsetX; + yyy = _posY + _frameWidth + offsetY; + + width = _posX + _width + offsetX - _frameWidth; + height = MAX(font->getLetterHeight(), sfont->getLetterHeight()); + + if (_gameRef->_textRTL) { + xxx += alignOffset; + } + + TTextAlign align = TAL_LEFT; + + + // unselected 1 + if (s1 > _scrollOffset) { + if (count) { + font->drawText((byte *)_text + _scrollOffset, xxx, yyy, width - xxx, align, height, s1 - _scrollOffset); + } + xxx += font->getTextWidth((byte *)_text + _scrollOffset, s1 - _scrollOffset); + alignOffset += font->getTextWidth((byte *)_text + _scrollOffset, s1 - _scrollOffset); + } + + // cursor + if (focused && curFirst) { + if (count) { + if (g_system->getMillis() - _lastBlinkTime >= _cursorBlinkRate) { + _lastBlinkTime = g_system->getMillis(); + _cursorVisible = !_cursorVisible; + } + if (_cursorVisible) { + font->drawText((byte *)_cursorChar, xxx, yyy, width - xxx, align, height, 1); + } + } + xxx += cursorWidth; + alignOffset += cursorWidth; + } + + // selected + int s3 = MAX(s1, _scrollOffset); + + if (s2 - s3 > 0) { + if (count) { + sfont->drawText((byte *)_text + s3, xxx, yyy, width - xxx, align, height, s2 - s3); + } + xxx += sfont->getTextWidth((byte *)_text + s3, s2 - s3); + alignOffset += sfont->getTextWidth((byte *)_text + s3, s2 - s3); + } + + // cursor + if (focused && !curFirst) { + if (count) { + if (g_system->getMillis() - _lastBlinkTime >= _cursorBlinkRate) { + _lastBlinkTime = g_system->getMillis(); + _cursorVisible = !_cursorVisible; + } + if (_cursorVisible) { + font->drawText((byte *)_cursorChar, xxx, yyy, width - xxx, align, height, 1); + } + } + xxx += cursorWidth; + alignOffset += cursorWidth; + } + + // unselected 2 + if (count) { + font->drawText((byte *)_text + s2, xxx, yyy, width - xxx, align, height); + } + alignOffset += font->getTextWidth((byte *)_text + s2); + + alignOffset = (_width - 2 * _frameWidth) - alignOffset; + if (alignOffset < 0) { + alignOffset = 0; + } + } + + + _gameRef->_renderer->addRectToList(new BaseActiveRect(_gameRef, this, NULL, offsetX + _posX, offsetY + _posY, _width, _height, 100, 100, false)); + + + _gameRef->_textEncoding = OrigEncoding; + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool UIEdit::handleKeypress(Common::Event *event, bool printable) { + bool handled = false; + + if (event->type == Common::EVENT_KEYDOWN && !printable) { + switch (event->kbd.keycode) { + case Common::KEYCODE_ESCAPE: + case Common::KEYCODE_TAB: + case Common::KEYCODE_RETURN: + return false; + + // ctrl+A + case Common::KEYCODE_a: + if (BaseKeyboardState::isControlDown()) { + _selStart = 0; + _selEnd = strlen(_text); + handled = true; + } + break; + + case Common::KEYCODE_BACKSPACE: + if (_selStart == _selEnd) { + if (_gameRef->_textRTL) { + deleteChars(_selStart, _selStart + 1); + } else { + deleteChars(_selStart - 1, _selStart); + } + } else { + deleteChars(_selStart, _selEnd); + } + if (_selEnd >= _selStart) { + _selEnd -= MAX(1, _selEnd - _selStart); + } + _selStart = _selEnd; + + handled = true; + break; + + case Common::KEYCODE_LEFT: + case Common::KEYCODE_UP: + _selEnd--; + if (!BaseKeyboardState::isShiftDown()) { + _selStart = _selEnd; + } + handled = true; + break; + + case Common::KEYCODE_RIGHT: + case Common::KEYCODE_DOWN: + _selEnd++; + if (!BaseKeyboardState::isShiftDown()) { + _selStart = _selEnd; + } + handled = true; + break; + + case Common::KEYCODE_HOME: + if (_gameRef->_textRTL) { + _selEnd = strlen(_text); + if (!BaseKeyboardState::isShiftDown()) { + _selStart = _selEnd; + } + } else { + _selEnd = 0; + if (!BaseKeyboardState::isShiftDown()) { + _selStart = _selEnd; + } + } + handled = true; + break; + + case Common::KEYCODE_END: + if (_gameRef->_textRTL) { + _selEnd = 0; + if (!BaseKeyboardState::isShiftDown()) { + _selStart = _selEnd; + } + } else { + _selEnd = strlen(_text); + if (!BaseKeyboardState::isShiftDown()) { + _selStart = _selEnd; + } + } + handled = true; + break; + + case Common::KEYCODE_DELETE: + if (_selStart == _selEnd) { + if (_gameRef->_textRTL) { + deleteChars(_selStart - 1, _selStart); + _selEnd--; + if (_selEnd < 0) { + _selEnd = 0; + } + } else { + deleteChars(_selStart, _selStart + 1); + } + } else { + deleteChars(_selStart, _selEnd); + } + if (_selEnd > _selStart) { + _selEnd -= (_selEnd - _selStart); + } + + _selStart = _selEnd; + handled = true; + break; + default: + break; + } + return handled; + } else if (event->type == Common::EVENT_KEYDOWN && printable) { + if (_selStart != _selEnd) { + deleteChars(_selStart, _selEnd); + } + + //WideString wstr = StringUtil::Utf8ToWide(event->kbd.ascii); + WideString wstr; + wstr += (char)event->kbd.ascii; + _selEnd += insertChars(_selEnd, (const byte *)StringUtil::wideToAnsi(wstr).c_str(), 1); + + if (_gameRef->_textRTL) { + _selEnd = _selStart; + } else { + _selStart = _selEnd; + } + + return true; + } + + return false; +} + + + +////////////////////////////////////////////////////////////////////////// +int UIEdit::deleteChars(int start, int end) { + if (start > end) { + BaseUtils::swap(&start, &end); + } + + start = MAX(start, (int)0); + end = MIN((size_t)end, strlen(_text)); + + char *str = new char[strlen(_text) - (end - start) + 1]; + if (str) { + if (start > 0) { + memcpy(str, _text, start); + } + memcpy(str + MAX(0, start), _text + end, strlen(_text) - end + 1); + + delete[] _text; + _text = str; + } + if (_parentNotify && _parent) { + _parent->applyEvent(getName()); + } + + return end - start; +} + + +////////////////////////////////////////////////////////////////////////// +int UIEdit::insertChars(int pos, const byte *chars, int num) { + if ((int)strlen(_text) + num > _maxLength) { + num -= (strlen(_text) + num - _maxLength); + } + + pos = MAX(pos, (int)0); + pos = MIN((size_t)pos, strlen(_text)); + + char *str = new char[strlen(_text) + num + 1]; + if (str) { + if (pos > 0) { + memcpy(str, _text, pos); + } + memcpy(str + pos + num, _text + pos, strlen(_text) - pos + 1); + + memcpy(str + pos, chars, num); + + delete[] _text; + _text = str; + } + if (_parentNotify && _parent) { + _parent->applyEvent(getName()); + } + + return num; +} + + + +////////////////////////////////////////////////////////////////////////// +bool UIEdit::persist(BasePersistenceManager *persistMgr) { + + UIObject::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_cursorBlinkRate)); + persistMgr->transfer(TMEMBER(_cursorChar)); + persistMgr->transfer(TMEMBER(_fontSelected)); + persistMgr->transfer(TMEMBER(_frameWidth)); + persistMgr->transfer(TMEMBER(_maxLength)); + persistMgr->transfer(TMEMBER(_scrollOffset)); + persistMgr->transfer(TMEMBER(_selEnd)); + persistMgr->transfer(TMEMBER(_selStart)); + + if (!persistMgr->getIsSaving()) { + _cursorVisible = false; + _lastBlinkTime = 0; + } + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ui/ui_edit.h b/engines/wintermute/ui/ui_edit.h new file mode 100644 index 0000000000..5bb31422b6 --- /dev/null +++ b/engines/wintermute/ui/ui_edit.h @@ -0,0 +1,72 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_UIEDIT_H +#define WINTERMUTE_UIEDIT_H + +#include "engines/wintermute/persistent.h" +#include "engines/wintermute/ui/ui_object.h" +#include "common/events.h" + +namespace Wintermute { +class BaseFont; +class UIEdit : public UIObject { +public: + DECLARE_PERSISTENT(UIEdit, UIObject) + int _maxLength; + int insertChars(int pos, const byte *chars, int num); + int deleteChars(int start, int end); + bool _cursorVisible; + uint32 _lastBlinkTime; + virtual bool display(int offsetX, int offsetY); + virtual bool handleKeypress(Common::Event *event, bool printable = false); + int _scrollOffset; + int _frameWidth; + uint32 _cursorBlinkRate; + void setCursorChar(const char *character); + char *_cursorChar; + int _selEnd; + int _selStart; + BaseFont *_fontSelected; + UIEdit(BaseGame *inGame); + virtual ~UIEdit(); + + bool loadFile(const char *filename); + bool loadBuffer(byte *buffer, bool complete = true); + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent); + + // scripting interface + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + virtual const char *scToString(); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ui/ui_entity.cpp b/engines/wintermute/ui/ui_entity.cpp new file mode 100644 index 0000000000..1cb4e0926b --- /dev/null +++ b/engines/wintermute/ui/ui_entity.cpp @@ -0,0 +1,366 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_entity.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/ui/ui_entity.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/base/scriptables/script_stack.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(UIEntity, false) + +////////////////////////////////////////////////////////////////////////// +UIEntity::UIEntity(BaseGame *inGame) : UIObject(inGame) { + _type = UI_CUSTOM; + _entity = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +UIEntity::~UIEntity() { + if (_entity) { + _gameRef->unregisterObject(_entity); + } + _entity = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +bool UIEntity::loadFile(const char *filename) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "UIEntity::LoadFile failed for file '%s'", filename); + return STATUS_FAILED; + } + + bool ret; + + setFilename(filename); + + if (DID_FAIL(ret = loadBuffer(buffer, true))) { + _gameRef->LOG(0, "Error parsing ENTITY container file '%s'", filename); + } + + + delete[] buffer; + + return ret; +} + + +TOKEN_DEF_START +TOKEN_DEF(ENTITY_CONTAINER) +TOKEN_DEF(TEMPLATE) +TOKEN_DEF(DISABLED) +TOKEN_DEF(VISIBLE) +TOKEN_DEF(X) +TOKEN_DEF(Y) +TOKEN_DEF(NAME) +TOKEN_DEF(ENTITY) +TOKEN_DEF(SCRIPT) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool UIEntity::loadBuffer(byte *buffer, bool complete) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(ENTITY_CONTAINER) + TOKEN_TABLE(TEMPLATE) + TOKEN_TABLE(DISABLED) + TOKEN_TABLE(VISIBLE) + TOKEN_TABLE(X) + TOKEN_TABLE(Y) + TOKEN_TABLE(NAME) + TOKEN_TABLE(ENTITY) + TOKEN_TABLE(SCRIPT) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE_END + + byte *params; + int cmd = 2; + BaseParser parser; + + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_ENTITY_CONTAINER) { + _gameRef->LOG(0, "'ENTITY_CONTAINER' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + while (cmd > 0 && (cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_TEMPLATE: + if (DID_FAIL(loadFile((char *)params))) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_NAME: + setName((char *)params); + break; + + case TOKEN_X: + parser.scanStr((char *)params, "%d", &_posX); + break; + + case TOKEN_Y: + parser.scanStr((char *)params, "%d", &_posY); + break; + + case TOKEN_DISABLED: + parser.scanStr((char *)params, "%b", &_disable); + break; + + case TOKEN_VISIBLE: + parser.scanStr((char *)params, "%b", &_visible); + break; + + case TOKEN_ENTITY: + if (DID_FAIL(setEntity((char *)params))) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_SCRIPT: + addScript((char *)params); + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty(params, false); + break; + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in ENTITY_CONTAINER definition"); + return STATUS_FAILED; + } + if (cmd == PARSERR_GENERIC) { + _gameRef->LOG(0, "Error loading ENTITY_CONTAINER definition"); + return STATUS_FAILED; + } + + correctSize(); + + if (_gameRef->_editorMode) { + _width = 50; + _height = 50; + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool UIEntity::saveAsText(BaseDynamicBuffer *buffer, int indent) { + buffer->putTextIndent(indent, "ENTITY_CONTAINER\n"); + buffer->putTextIndent(indent, "{\n"); + + buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", getName()); + + buffer->putTextIndent(indent + 2, "\n"); + + buffer->putTextIndent(indent + 2, "X=%d\n", _posX); + buffer->putTextIndent(indent + 2, "Y=%d\n", _posY); + + buffer->putTextIndent(indent + 2, "DISABLED=%s\n", _disable ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "VISIBLE=%s\n", _visible ? "TRUE" : "FALSE"); + + if (_entity && _entity->getFilename()) { + buffer->putTextIndent(indent + 2, "ENTITY=\"%s\"\n", _entity->getFilename()); + } + + buffer->putTextIndent(indent + 2, "\n"); + + // scripts + for (uint32 i = 0; i < _scripts.size(); i++) { + buffer->putTextIndent(indent + 2, "SCRIPT=\"%s\"\n", _scripts[i]->_filename); + } + + buffer->putTextIndent(indent + 2, "\n"); + + // editor properties + BaseClass::saveAsText(buffer, indent + 2); + + buffer->putTextIndent(indent, "}\n"); + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool UIEntity::setEntity(const char *filename) { + if (_entity) { + _gameRef->unregisterObject(_entity); + } + _entity = new AdEntity(_gameRef); + if (!_entity || DID_FAIL(_entity->loadFile(filename))) { + delete _entity; + _entity = NULL; + return STATUS_FAILED; + } else { + _entity->_nonIntMouseEvents = true; + _entity->_sceneIndependent = true; + _entity->makeFreezable(false); + _gameRef->registerObject(_entity); + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool UIEntity::display(int offsetX, int offsetY) { + if (!_visible) { + return STATUS_OK; + } + + if (_entity) { + _entity->_posX = offsetX + _posX; + _entity->_posY = offsetY + _posY; + if (_entity->_scale < 0) { + _entity->_zoomable = false; + } + _entity->_shadowable = false; + + _entity->update(); + + bool origReg = _entity->_registrable; + + if (_entity->_registrable && _disable) { + _entity->_registrable = false; + } + + _entity->display(); + _entity->_registrable = origReg; + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool UIEntity::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // GetEntity + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "GetEntity") == 0) { + stack->correctParams(0); + + if (_entity) { + stack->pushNative(_entity, true); + } else { + stack->pushNULL(); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetEntity + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetEntity") == 0) { + stack->correctParams(1); + + const char *filename = stack->pop()->getString(); + + if (DID_SUCCEED(setEntity(filename))) { + stack->pushBool(true); + } else { + stack->pushBool(false); + } + + return STATUS_OK; + } else { + return UIObject::scCallMethod(script, stack, thisStack, name); + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *UIEntity::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("entity container"); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Freezable + ////////////////////////////////////////////////////////////////////////// + else if (name == "Freezable") { + if (_entity) { + _scValue->setBool(_entity->_freezable); + } else { + _scValue->setBool(false); + } + return _scValue; + } else { + return UIObject::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool UIEntity::scSetProperty(const char *name, ScValue *value) { + ////////////////////////////////////////////////////////////////////////// + // Freezable + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Freezable") == 0) { + if (_entity) { + _entity->makeFreezable(value->getBool()); + } + return STATUS_OK; + } else { + return UIObject::scSetProperty(name, value); + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *UIEntity::scToString() { + return "[entity container]"; +} + + +////////////////////////////////////////////////////////////////////////// +bool UIEntity::persist(BasePersistenceManager *persistMgr) { + + UIObject::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_entity)); + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ui/ui_entity.h b/engines/wintermute/ui/ui_entity.h new file mode 100644 index 0000000000..b5f4450071 --- /dev/null +++ b/engines/wintermute/ui/ui_entity.h @@ -0,0 +1,59 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_UIENTITY_H +#define WINTERMUTE_UIENTITY_H + +#include "engines/wintermute/ui/ui_object.h" + +namespace Wintermute { +class AdEntity; +class UIEntity : public UIObject { +public: + DECLARE_PERSISTENT(UIEntity, UIObject) + UIEntity(BaseGame *inGame); + virtual ~UIEntity(); + bool loadFile(const char *filename); + bool loadBuffer(byte *buffer, bool complete); + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent); + + virtual bool display() { return display(0, 0); } + virtual bool display(int offsetX, int offsetY); + AdEntity *_entity; + bool setEntity(const char *filename); + + // scripting interface + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + virtual const char *scToString(); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ui/ui_object.cpp b/engines/wintermute/ui/ui_object.cpp new file mode 100644 index 0000000000..8e5bae993c --- /dev/null +++ b/engines/wintermute/ui/ui_object.cpp @@ -0,0 +1,651 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/ui/ui_object.h" +#include "engines/wintermute/ui/ui_tiled_image.h" +#include "engines/wintermute/ui/ui_window.h" +#include "engines/wintermute/platform_osystem.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/base/font/base_font_storage.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(UIObject, false) + +////////////////////////////////////////////////////////////////////////// +UIObject::UIObject(BaseGame *inGame) : BaseObject(inGame) { + _back = NULL; + _image = NULL; + _font = NULL; + _text = NULL; + _sharedFonts = _sharedImages = false; + + _width = _height = 0; + + _listenerObject = NULL; + _listenerParamObject = NULL; + _listenerParamDWORD = 0; + + _disable = false; + _visible = true; + + _type = UI_UNKNOWN; + _parent = NULL; + + _parentNotify = false; + + _focusedWidget = NULL; + + _canFocus = false; + _nonIntMouseEvents = true; +} + + +////////////////////////////////////////////////////////////////////////// +UIObject::~UIObject() { + if (!_gameRef->_loadInProgress) { + SystemClassRegistry::getInstance()->enumInstances(BaseGame::invalidateValues, "ScValue", (void *)this); + } + + if (_back) { + delete _back; + } + if (_font && !_sharedFonts) { + _gameRef->_fontStorage->removeFont(_font); + } + + if (_image && !_sharedImages) { + delete _image; + } + + if (_text) { + delete[] _text; + } + + _focusedWidget = NULL; // ref only +} + + +////////////////////////////////////////////////////////////////////////// +void UIObject::setText(const char *text) { + if (_text) { + delete[] _text; + } + _text = new char [strlen(text) + 1]; + if (_text) { + strcpy(_text, text); + for (uint32 i = 0; i < strlen(_text); i++) { + if (_text[i] == '|') { + _text[i] = '\n'; + } + } + } +} + + +////////////////////////////////////////////////////////////////////////// +bool UIObject::display(int offsetX, int offsetY) { + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +void UIObject::setListener(BaseScriptHolder *object, BaseScriptHolder *listenerObject, uint32 listenerParam) { + _listenerObject = object; + _listenerParamObject = listenerObject; + _listenerParamDWORD = listenerParam; +} + + +////////////////////////////////////////////////////////////////////////// +void UIObject::correctSize() { + Rect32 rect; + + if (_width <= 0) { + if (_image) { + _image->getBoundingRect(&rect, 0, 0); + _width = rect.right - rect.left; + } else { + _width = 100; + } + } + + if (_height <= 0) { + if (_image) { + _image->getBoundingRect(&rect, 0, 0); + _height = rect.bottom - rect.top; + } + } + + if (_back) { + _back->correctSize(&_width, &_height); + } +} + + + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool UIObject::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // SetFont + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "SetFont") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + if (_font) { + _gameRef->_fontStorage->removeFont(_font); + } + if (val->isNULL()) { + _font = NULL; + stack->pushBool(true); + } else { + _font = _gameRef->_fontStorage->addFont(val->getString()); + stack->pushBool(_font != NULL); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetImage + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetImage") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + /* const char *filename = */ val->getString(); + + delete _image; + _image = NULL; + if (val->isNULL()) { + stack->pushBool(true); + return STATUS_OK; + } + + _image = new BaseSprite(_gameRef); + if (!_image || DID_FAIL(_image->loadFile(val->getString()))) { + delete _image; + _image = NULL; + stack->pushBool(false); + } else { + stack->pushBool(true); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetImage + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetImage") == 0) { + stack->correctParams(0); + if (!_image || !_image->getFilename()) { + stack->pushNULL(); + } else { + stack->pushString(_image->getFilename()); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetImageObject + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetImageObject") == 0) { + stack->correctParams(0); + if (!_image) { + stack->pushNULL(); + } else { + stack->pushNative(_image, true); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Focus + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Focus") == 0) { + stack->correctParams(0); + focus(); + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // MoveAfter / MoveBefore + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "MoveAfter") == 0 || strcmp(name, "MoveBefore") == 0) { + stack->correctParams(1); + + if (_parent && _parent->_type == UI_WINDOW) { + UIWindow *win = (UIWindow *)_parent; + + uint32 i; + bool found = false; + ScValue *val = stack->pop(); + // find directly + if (val->isNative()) { + UIObject *widget = (UIObject *)val->getNative(); + for (i = 0; i < win->_widgets.size(); i++) { + if (win->_widgets[i] == widget) { + found = true; + break; + } + } + } + // find by name + else { + const char *findName = val->getString(); + for (i = 0; i < win->_widgets.size(); i++) { + if (scumm_stricmp(win->_widgets[i]->getName(), findName) == 0) { + found = true; + break; + } + } + } + + if (found) { + bool done = false; + for (uint32 j = 0; j < win->_widgets.size(); j++) { + if (win->_widgets[j] == this) { + if (strcmp(name, "MoveAfter") == 0) { + i++; + } + if (j >= i) { + j++; + } + + win->_widgets.insert_at(i, this); + win->_widgets.remove_at(j); + + done = true; + stack->pushBool(true); + break; + } + } + if (!done) { + stack->pushBool(false); + } + } else { + stack->pushBool(false); + } + + } else { + stack->pushBool(false); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // MoveToBottom + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "MoveToBottom") == 0) { + stack->correctParams(0); + + if (_parent && _parent->_type == UI_WINDOW) { + UIWindow *win = (UIWindow *)_parent; + for (uint32 i = 0; i < win->_widgets.size(); i++) { + if (win->_widgets[i] == this) { + win->_widgets.remove_at(i); + win->_widgets.insert_at(0, this); + break; + } + } + stack->pushBool(true); + } else { + stack->pushBool(false); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // MoveToTop + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "MoveToTop") == 0) { + stack->correctParams(0); + + if (_parent && _parent->_type == UI_WINDOW) { + UIWindow *win = (UIWindow *)_parent; + for (uint32 i = 0; i < win->_widgets.size(); i++) { + if (win->_widgets[i] == this) { + win->_widgets.remove_at(i); + win->_widgets.add(this); + break; + } + } + stack->pushBool(true); + } else { + stack->pushBool(false); + } + + return STATUS_OK; + } else { + return BaseObject::scCallMethod(script, stack, thisStack, name); + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *UIObject::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("ui_object"); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Name + ////////////////////////////////////////////////////////////////////////// + else if (name == "Name") { + _scValue->setString(getName()); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Parent (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "Parent") { + _scValue->setNative(_parent, true); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // ParentNotify + ////////////////////////////////////////////////////////////////////////// + else if (name == "ParentNotify") { + _scValue->setBool(_parentNotify); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Width + ////////////////////////////////////////////////////////////////////////// + else if (name == "Width") { + _scValue->setInt(_width); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Height + ////////////////////////////////////////////////////////////////////////// + else if (name == "Height") { + _scValue->setInt(_height); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Visible + ////////////////////////////////////////////////////////////////////////// + else if (name == "Visible") { + _scValue->setBool(_visible); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Disabled + ////////////////////////////////////////////////////////////////////////// + else if (name == "Disabled") { + _scValue->setBool(_disable); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Text + ////////////////////////////////////////////////////////////////////////// + else if (name == "Text") { + _scValue->setString(_text); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // NextSibling (RO) / PrevSibling (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "NextSibling" || name == "PrevSibling") { + _scValue->setNULL(); + if (_parent && _parent->_type == UI_WINDOW) { + UIWindow *win = (UIWindow *)_parent; + for (uint32 i = 0; i < win->_widgets.size(); i++) { + if (win->_widgets[i] == this) { + if (name == "NextSibling") { + if (i < win->_widgets.size() - 1) { + _scValue->setNative(win->_widgets[i + 1], true); + } + } else { + if (i > 0) { + _scValue->setNative(win->_widgets[i - 1], true); + } + } + break; + } + } + } + return _scValue; + } else { + return BaseObject::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool UIObject::scSetProperty(const char *name, ScValue *value) { + ////////////////////////////////////////////////////////////////////////// + // Name + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Name") == 0) { + setName(value->getString()); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ParentNotify + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ParentNotify") == 0) { + _parentNotify = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Width + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Width") == 0) { + _width = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Height + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Height") == 0) { + _height = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Visible + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Visible") == 0) { + _visible = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Disabled + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Disabled") == 0) { + _disable = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Text + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Text") == 0) { + setText(value->getString()); + return STATUS_OK; + } else { + return BaseObject::scSetProperty(name, value); + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *UIObject::scToString() { + return "[ui_object]"; +} + + +////////////////////////////////////////////////////////////////////////// +bool UIObject::isFocused() { + if (!_gameRef->_focusedWindow) { + return false; + } + if (_gameRef->_focusedWindow == this) { + return true; + } + + UIObject *obj = _gameRef->_focusedWindow; + while (obj) { + if (obj == this) { + return true; + } else { + obj = obj->_focusedWidget; + } + } + return false; +} + + +////////////////////////////////////////////////////////////////////////// +bool UIObject::handleMouse(TMouseEvent event, TMouseButton button) { + // handle focus change + if (event == MOUSE_CLICK && button == MOUSE_BUTTON_LEFT) { + focus(); + } + return BaseObject::handleMouse(event, button); +} + + +////////////////////////////////////////////////////////////////////////// +bool UIObject::focus() { + UIObject *obj = this; + bool disabled = false; + while (obj) { + if (obj->_disable && obj->_type == UI_WINDOW) { + disabled = true; + break; + } + obj = obj->_parent; + } + if (!disabled) { + obj = this; + while (obj) { + if (obj->_parent) { + if (!obj->_disable && obj->_canFocus) { + obj->_parent->_focusedWidget = obj; + } + } else { + if (obj->_type == UI_WINDOW) { + _gameRef->focusWindow((UIWindow *)obj); + } + } + + obj = obj->_parent; + } + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool UIObject::getTotalOffset(int *offsetX, int *offsetY) { + int offX = 0, offY = 0; + + UIObject *obj = _parent; + while (obj) { + offX += obj->_posX; + offY += obj->_posY; + + obj = obj->_parent; + } + if (offsetX) { + *offsetX = offX; + } + if (offsetY) { + *offsetY = offY; + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool UIObject::persist(BasePersistenceManager *persistMgr) { + + BaseObject::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_back)); + persistMgr->transfer(TMEMBER(_canFocus)); + persistMgr->transfer(TMEMBER(_disable)); + persistMgr->transfer(TMEMBER(_focusedWidget)); + persistMgr->transfer(TMEMBER(_font)); + persistMgr->transfer(TMEMBER(_height)); + persistMgr->transfer(TMEMBER(_image)); + persistMgr->transfer(TMEMBER(_listenerObject)); + persistMgr->transfer(TMEMBER(_listenerParamObject)); + persistMgr->transfer(TMEMBER(_listenerParamDWORD)); + persistMgr->transfer(TMEMBER(_parent)); + persistMgr->transfer(TMEMBER(_parentNotify)); + persistMgr->transfer(TMEMBER(_sharedFonts)); + persistMgr->transfer(TMEMBER(_sharedImages)); + persistMgr->transfer(TMEMBER(_text)); + persistMgr->transfer(TMEMBER_INT(_type)); + persistMgr->transfer(TMEMBER(_visible)); + persistMgr->transfer(TMEMBER(_width)); + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool UIObject::saveAsText(BaseDynamicBuffer *buffer, int indent) { + return STATUS_FAILED; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ui/ui_object.h b/engines/wintermute/ui/ui_object.h new file mode 100644 index 0000000000..ec2ea33de1 --- /dev/null +++ b/engines/wintermute/ui/ui_object.h @@ -0,0 +1,85 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_UIOBJECT_H +#define WINTERMUTE_UIOBJECT_H + + +#include "engines/wintermute/base/base_object.h" +#include "engines/wintermute/dctypes.h" // Added by ClassView + +namespace Wintermute { + +class UITiledImage; +class BaseFont; +class UIObject : public BaseObject { +public: + + bool getTotalOffset(int *offsetX, int *offsetY); + bool _canFocus; + bool focus(); + virtual bool handleMouse(TMouseEvent event, TMouseButton button); + bool isFocused(); + bool _parentNotify; + DECLARE_PERSISTENT(UIObject, BaseObject) + UIObject *_parent; + virtual bool display() { return display(0, 0); } + virtual bool display(int offsetX) { return display(offsetX, 0); } + virtual bool display(int offsetX, int offsetY); + virtual void correctSize(); + bool _sharedFonts; + bool _sharedImages; + void setText(const char *text); + char *_text; + BaseFont *_font; + bool _visible; + UITiledImage *_back; + bool _disable; + UIObject(BaseGame *inGame = NULL); + virtual ~UIObject(); + int _width; + int _height; + TUIObjectType _type; + BaseSprite *_image; + void setListener(BaseScriptHolder *object, BaseScriptHolder *listenerObject, uint32 listenerParam); + BaseScriptHolder *_listenerParamObject; + uint32 _listenerParamDWORD; + BaseScriptHolder *_listenerObject; + UIObject *_focusedWidget; + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent); + + // scripting interface + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + virtual const char *scToString(); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ui/ui_text.cpp b/engines/wintermute/ui/ui_text.cpp new file mode 100644 index 0000000000..2c10f176c7 --- /dev/null +++ b/engines/wintermute/ui/ui_text.cpp @@ -0,0 +1,522 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/ui/ui_text.h" +#include "engines/wintermute/ui/ui_tiled_image.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/font/base_font.h" +#include "engines/wintermute/base/font/base_font_storage.h" +#include "engines/wintermute/base/base_string_table.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/platform_osystem.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(UIText, false) + +////////////////////////////////////////////////////////////////////////// +UIText::UIText(BaseGame *inGame) : UIObject(inGame) { + _textAlign = TAL_LEFT; + _verticalAlign = VAL_CENTER; + _type = UI_STATIC; + _canFocus = false; +} + + +////////////////////////////////////////////////////////////////////////// +UIText::~UIText() { + +} + + +////////////////////////////////////////////////////////////////////////// +bool UIText::display(int offsetX, int offsetY) { + if (!_visible) { + return STATUS_OK; + } + + + BaseFont *font = _font; + if (!font) { + font = _gameRef->_systemFont; + } + + if (_back) { + _back->display(offsetX + _posX, offsetY + _posY, _width, _height); + } + if (_image) { + _image->draw(offsetX + _posX, offsetY + _posY, NULL); + } + + if (font && _text) { + int textOffset; + switch (_verticalAlign) { + case VAL_TOP: + textOffset = 0; + break; + case VAL_BOTTOM: + textOffset = _height - font->getTextHeight((byte *)_text, _width); + break; + default: + textOffset = (_height - font->getTextHeight((byte *)_text, _width)) / 2; + } + font->drawText((byte *)_text, offsetX + _posX, offsetY + _posY + textOffset, _width, _textAlign, _height); + } + + //_gameRef->_renderer->_rectList.add(new BaseActiveRect(_gameRef, this, NULL, OffsetX + _posX, OffsetY + _posY, _width, _height, 100, 100, false)); + + return STATUS_OK; +} + + + +////////////////////////////////////////////////////////////////////////// +bool UIText::loadFile(const char *filename) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "UIText::LoadFile failed for file '%s'", filename); + return STATUS_FAILED; + } + + bool ret; + + setFilename(filename); + + if (DID_FAIL(ret = loadBuffer(buffer, true))) { + _gameRef->LOG(0, "Error parsing STATIC file '%s'", filename); + } + + delete[] buffer; + + return ret; +} + + +TOKEN_DEF_START +TOKEN_DEF(STATIC) +TOKEN_DEF(TEMPLATE) +TOKEN_DEF(DISABLED) +TOKEN_DEF(VISIBLE) +TOKEN_DEF(BACK) +TOKEN_DEF(IMAGE) +TOKEN_DEF(FONT) +TOKEN_DEF(TEXT_ALIGN) +TOKEN_DEF(VERTICAL_ALIGN) +TOKEN_DEF(TEXT) +TOKEN_DEF(X) +TOKEN_DEF(Y) +TOKEN_DEF(WIDTH) +TOKEN_DEF(HEIGHT) +TOKEN_DEF(CURSOR) +TOKEN_DEF(NAME) +TOKEN_DEF(SCRIPT) +TOKEN_DEF(CAPTION) +TOKEN_DEF(PARENT_NOTIFY) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool UIText::loadBuffer(byte *buffer, bool complete) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(STATIC) + TOKEN_TABLE(TEMPLATE) + TOKEN_TABLE(DISABLED) + TOKEN_TABLE(VISIBLE) + TOKEN_TABLE(BACK) + TOKEN_TABLE(IMAGE) + TOKEN_TABLE(FONT) + TOKEN_TABLE(TEXT_ALIGN) + TOKEN_TABLE(VERTICAL_ALIGN) + TOKEN_TABLE(TEXT) + TOKEN_TABLE(X) + TOKEN_TABLE(Y) + TOKEN_TABLE(WIDTH) + TOKEN_TABLE(HEIGHT) + TOKEN_TABLE(CURSOR) + TOKEN_TABLE(NAME) + TOKEN_TABLE(SCRIPT) + TOKEN_TABLE(CAPTION) + TOKEN_TABLE(PARENT_NOTIFY) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE_END + + byte *params; + int cmd = 2; + BaseParser parser; + + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_STATIC) { + _gameRef->LOG(0, "'STATIC' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + while (cmd > 0 && (cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_TEMPLATE: + if (DID_FAIL(loadFile((char *)params))) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_NAME: + setName((char *)params); + break; + + case TOKEN_CAPTION: + setCaption((char *)params); + break; + + case TOKEN_BACK: + delete _back; + _back = new UITiledImage(_gameRef); + if (!_back || DID_FAIL(_back->loadFile((char *)params))) { + delete _back; + _back = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_IMAGE: + delete _image; + _image = new BaseSprite(_gameRef); + if (!_image || DID_FAIL(_image->loadFile((char *)params))) { + delete _image; + _image = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_FONT: + if (_font) { + _gameRef->_fontStorage->removeFont(_font); + } + _font = _gameRef->_fontStorage->addFont((char *)params); + if (!_font) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_TEXT: + setText((char *)params); + _gameRef->_stringTable->expand(&_text); + break; + + case TOKEN_TEXT_ALIGN: + if (scumm_stricmp((char *)params, "left") == 0) { + _textAlign = TAL_LEFT; + } else if (scumm_stricmp((char *)params, "right") == 0) { + _textAlign = TAL_RIGHT; + } else { + _textAlign = TAL_CENTER; + } + break; + + case TOKEN_VERTICAL_ALIGN: + if (scumm_stricmp((char *)params, "top") == 0) { + _verticalAlign = VAL_TOP; + } else if (scumm_stricmp((char *)params, "bottom") == 0) { + _verticalAlign = VAL_BOTTOM; + } else { + _verticalAlign = VAL_CENTER; + } + break; + + case TOKEN_X: + parser.scanStr((char *)params, "%d", &_posX); + break; + + case TOKEN_Y: + parser.scanStr((char *)params, "%d", &_posY); + break; + + case TOKEN_WIDTH: + parser.scanStr((char *)params, "%d", &_width); + break; + + case TOKEN_HEIGHT: + parser.scanStr((char *)params, "%d", &_height); + break; + + case TOKEN_CURSOR: + delete _cursor; + _cursor = new BaseSprite(_gameRef); + if (!_cursor || DID_FAIL(_cursor->loadFile((char *)params))) { + delete _cursor; + _cursor = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_SCRIPT: + addScript((char *)params); + break; + + case TOKEN_PARENT_NOTIFY: + parser.scanStr((char *)params, "%b", &_parentNotify); + break; + + case TOKEN_DISABLED: + parser.scanStr((char *)params, "%b", &_disable); + break; + + case TOKEN_VISIBLE: + parser.scanStr((char *)params, "%b", &_visible); + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty(params, false); + break; + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in STATIC definition"); + return STATUS_FAILED; + } + if (cmd == PARSERR_GENERIC) { + _gameRef->LOG(0, "Error loading STATIC definition"); + return STATUS_FAILED; + } + + correctSize(); + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool UIText::saveAsText(BaseDynamicBuffer *buffer, int indent) { + buffer->putTextIndent(indent, "STATIC\n"); + buffer->putTextIndent(indent, "{\n"); + + buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", getName()); + buffer->putTextIndent(indent + 2, "CAPTION=\"%s\"\n", getCaption()); + + buffer->putTextIndent(indent + 2, "\n"); + + if (_back && _back->getFilename()) { + buffer->putTextIndent(indent + 2, "BACK=\"%s\"\n", _back->getFilename()); + } + + if (_image && _image->getFilename()) { + buffer->putTextIndent(indent + 2, "IMAGE=\"%s\"\n", _image->getFilename()); + } + + if (_font && _font->getFilename()) { + buffer->putTextIndent(indent + 2, "FONT=\"%s\"\n", _font->getFilename()); + } + + if (_cursor && _cursor->getFilename()) { + buffer->putTextIndent(indent + 2, "CURSOR=\"%s\"\n", _cursor->getFilename()); + } + + if (_text) { + buffer->putTextIndent(indent + 2, "TEXT=\"%s\"\n", _text); + } + + switch (_textAlign) { + case TAL_LEFT: + buffer->putTextIndent(indent + 2, "TEXT_ALIGN=\"%s\"\n", "left"); + break; + case TAL_RIGHT: + buffer->putTextIndent(indent + 2, "TEXT_ALIGN=\"%s\"\n", "right"); + break; + case TAL_CENTER: + buffer->putTextIndent(indent + 2, "TEXT_ALIGN=\"%s\"\n", "center"); + break; + default: + error("UIText::SaveAsText - Unhandled enum"); + break; + } + + switch (_verticalAlign) { + case VAL_TOP: + buffer->putTextIndent(indent + 2, "VERTICAL_ALIGN=\"%s\"\n", "top"); + break; + case VAL_BOTTOM: + buffer->putTextIndent(indent + 2, "VERTICAL_ALIGN=\"%s\"\n", "bottom"); + break; + case VAL_CENTER: + buffer->putTextIndent(indent + 2, "VERTICAL_ALIGN=\"%s\"\n", "center"); + break; + default: + error("UIText::SaveAsText - Unhandled enum value: NUM_VERTICAL_ALIGN"); + } + + buffer->putTextIndent(indent + 2, "\n"); + + buffer->putTextIndent(indent + 2, "X=%d\n", _posX); + buffer->putTextIndent(indent + 2, "Y=%d\n", _posY); + buffer->putTextIndent(indent + 2, "WIDTH=%d\n", _width); + buffer->putTextIndent(indent + 2, "HEIGHT=%d\n", _height); + + buffer->putTextIndent(indent + 2, "DISABLED=%s\n", _disable ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "VISIBLE=%s\n", _visible ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "PARENT_NOTIFY=%s\n", _parentNotify ? "TRUE" : "FALSE"); + + buffer->putTextIndent(indent + 2, "\n"); + + // scripts + for (uint32 i = 0; i < _scripts.size(); i++) { + buffer->putTextIndent(indent + 2, "SCRIPT=\"%s\"\n", _scripts[i]->_filename); + } + + buffer->putTextIndent(indent + 2, "\n"); + + // editor properties + BaseClass::saveAsText(buffer, indent + 2); + + buffer->putTextIndent(indent, "}\n"); + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool UIText::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // SizeToFit + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "SizeToFit") == 0) { + stack->correctParams(0); + sizeToFit(); + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // HeightToFit + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "HeightToFit") == 0) { + stack->correctParams(0); + if (_font && _text) { + _height = _font->getTextHeight((byte *)_text, _width); + } + stack->pushNULL(); + return STATUS_OK; + } else { + return UIObject::scCallMethod(script, stack, thisStack, name); + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *UIText::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("static"); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // TextAlign + ////////////////////////////////////////////////////////////////////////// + else if (name == "TextAlign") { + _scValue->setInt(_textAlign); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // VerticalAlign + ////////////////////////////////////////////////////////////////////////// + else if (name == "VerticalAlign") { + _scValue->setInt(_verticalAlign); + return _scValue; + } else { + return UIObject::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool UIText::scSetProperty(const char *name, ScValue *value) { + ////////////////////////////////////////////////////////////////////////// + // TextAlign + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "TextAlign") == 0) { + int i = value->getInt(); + if (i < 0 || i >= NUM_TEXT_ALIGN) { + i = 0; + } + _textAlign = (TTextAlign)i; + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // VerticalAlign + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "VerticalAlign") == 0) { + int i = value->getInt(); + if (i < 0 || i >= NUM_VERTICAL_ALIGN) { + i = 0; + } + _verticalAlign = (TVerticalAlign)i; + return STATUS_OK; + } else { + return UIObject::scSetProperty(name, value); + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *UIText::scToString() { + return "[static]"; +} + + + +////////////////////////////////////////////////////////////////////////// +bool UIText::persist(BasePersistenceManager *persistMgr) { + + UIObject::persist(persistMgr); + persistMgr->transfer(TMEMBER_INT(_textAlign)); + persistMgr->transfer(TMEMBER_INT(_verticalAlign)); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool UIText::sizeToFit() { + if (_font && _text) { + _width = _font->getTextWidth((byte *)_text); + _height = _font->getTextHeight((byte *)_text, _width); + } + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ui/ui_text.h b/engines/wintermute/ui/ui_text.h new file mode 100644 index 0000000000..da4d113500 --- /dev/null +++ b/engines/wintermute/ui/ui_text.h @@ -0,0 +1,60 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_UITEXT_H +#define WINTERMUTE_UITEXT_H + + +#include "engines/wintermute/ui/ui_object.h" + +namespace Wintermute { + +class UIText : public UIObject { +private: + bool sizeToFit(); +public: + virtual bool display(int offsetX, int offsetY); + DECLARE_PERSISTENT(UIText, UIObject) + UIText(BaseGame *inGame = NULL); + virtual ~UIText(); + TTextAlign _textAlign; + TVerticalAlign _verticalAlign; + bool loadFile(const char *filename); + bool loadBuffer(byte *buffer, bool complete = true); + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent); + + // scripting interface + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + virtual const char *scToString(); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ui/ui_tiled_image.cpp b/engines/wintermute/ui/ui_tiled_image.cpp new file mode 100644 index 0000000000..03fef5ca05 --- /dev/null +++ b/engines/wintermute/ui/ui_tiled_image.cpp @@ -0,0 +1,391 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ui/ui_tiled_image.h" +#include "engines/wintermute/base/gfx/base_surface.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_sub_frame.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/platform_osystem.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(UITiledImage, false) + +////////////////////////////////////////////////////////////////////////// +UITiledImage::UITiledImage(BaseGame *inGame) : BaseObject(inGame) { + _image = NULL; + + BasePlatform::setRectEmpty(&_upLeft); + BasePlatform::setRectEmpty(&_upMiddle); + BasePlatform::setRectEmpty(&_upRight); + BasePlatform::setRectEmpty(&_middleLeft); + BasePlatform::setRectEmpty(&_middleMiddle); + BasePlatform::setRectEmpty(&_middleRight); + BasePlatform::setRectEmpty(&_downLeft); + BasePlatform::setRectEmpty(&_downMiddle); + BasePlatform::setRectEmpty(&_downRight); +} + + +////////////////////////////////////////////////////////////////////////// +UITiledImage::~UITiledImage() { + delete _image; + _image = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +bool UITiledImage::display(int x, int y, int width, int height) { + if (!_image) { + return STATUS_FAILED; + } + + int tileWidth = _middleMiddle.right - _middleMiddle.left; + int tileHeight = _middleMiddle.bottom - _middleMiddle.top; + + int nuColumns = (width - (_middleLeft.right - _middleLeft.left) - (_middleRight.right - _middleRight.left)) / tileWidth; + int nuRows = (height - (_upMiddle.bottom - _upMiddle.top) - (_downMiddle.bottom - _downMiddle.top)) / tileHeight; + + int col, row; + + _gameRef->_renderer->startSpriteBatch(); + + // top left/right + _image->_surface->displayTrans(x, y, _upLeft); + _image->_surface->displayTrans(x + (_upLeft.right - _upLeft.left) + nuColumns * tileWidth, y, _upRight); + + // bottom left/right + _image->_surface->displayTrans(x, y + (_upMiddle.bottom - _upMiddle.top) + nuRows * tileHeight, _downLeft); + _image->_surface->displayTrans(x + (_upLeft.right - _upLeft.left) + nuColumns * tileWidth, y + (_upMiddle.bottom - _upMiddle.top) + nuRows * tileHeight, _downRight); + + // left/right + int yyy = y + (_upMiddle.bottom - _upMiddle.top); + for (row = 0; row < nuRows; row++) { + _image->_surface->displayTrans(x, yyy, _middleLeft); + _image->_surface->displayTrans(x + (_middleLeft.right - _middleLeft.left) + nuColumns * tileWidth, yyy, _middleRight); + yyy += tileWidth; + } + + // top/bottom + int xxx = x + (_upLeft.right - _upLeft.left); + for (col = 0; col < nuColumns; col++) { + _image->_surface->displayTrans(xxx, y, _upMiddle); + _image->_surface->displayTrans(xxx, y + (_upMiddle.bottom - _upMiddle.top) + nuRows * tileHeight, _downMiddle); + xxx += tileWidth; + } + + // tiles + if (nuRows > 0 && nuColumns > 0) { + yyy = y + (_upMiddle.bottom - _upMiddle.top); + xxx = x + (_upLeft.right - _upLeft.left); + _image->_surface->displayTrans(xxx, yyy, _middleMiddle); + _image->_surface->repeatLastDisplayOp(tileWidth, tileWidth, nuColumns, nuRows); + } + + _gameRef->_renderer->endSpriteBatch(); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool UITiledImage::loadFile(const char *filename) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "UITiledImage::LoadFile failed for file '%s'", filename); + return STATUS_FAILED; + } + + bool ret; + + setFilename(filename); + + if (DID_FAIL(ret = loadBuffer(buffer, true))) { + _gameRef->LOG(0, "Error parsing TILED_IMAGE file '%s'", filename); + } + + + delete[] buffer; + + return ret; +} + + +TOKEN_DEF_START +TOKEN_DEF(TILED_IMAGE) +TOKEN_DEF(TEMPLATE) +TOKEN_DEF(IMAGE) +TOKEN_DEF(UP_LEFT) +TOKEN_DEF(UP_RIGHT) +TOKEN_DEF(UP_MIDDLE) +TOKEN_DEF(DOWN_LEFT) +TOKEN_DEF(DOWN_RIGHT) +TOKEN_DEF(DOWN_MIDDLE) +TOKEN_DEF(MIDDLE_LEFT) +TOKEN_DEF(MIDDLE_RIGHT) +TOKEN_DEF(MIDDLE_MIDDLE) +TOKEN_DEF(VERTICAL_TILES) +TOKEN_DEF(HORIZONTAL_TILES) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool UITiledImage::loadBuffer(byte *buffer, bool complete) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(TILED_IMAGE) + TOKEN_TABLE(TEMPLATE) + TOKEN_TABLE(IMAGE) + TOKEN_TABLE(UP_LEFT) + TOKEN_TABLE(UP_RIGHT) + TOKEN_TABLE(UP_MIDDLE) + TOKEN_TABLE(DOWN_LEFT) + TOKEN_TABLE(DOWN_RIGHT) + TOKEN_TABLE(DOWN_MIDDLE) + TOKEN_TABLE(MIDDLE_LEFT) + TOKEN_TABLE(MIDDLE_RIGHT) + TOKEN_TABLE(MIDDLE_MIDDLE) + TOKEN_TABLE(VERTICAL_TILES) + TOKEN_TABLE(HORIZONTAL_TILES) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE_END + + byte *params; + int cmd; + BaseParser parser; + bool hTiles = false, vTiles = false; + int h1 = 0, h2 = 0, h3 = 0; + int v1 = 0, v2 = 0, v3 = 0; + + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_TILED_IMAGE) { + _gameRef->LOG(0, "'TILED_IMAGE' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_TEMPLATE: + if (DID_FAIL(loadFile((char *)params))) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_IMAGE: + delete _image; + _image = new BaseSubFrame(_gameRef); + if (!_image || DID_FAIL(_image->setSurface((char *)params))) { + delete _image; + _image = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_UP_LEFT: + parser.scanStr((char *)params, "%d,%d,%d,%d", &_upLeft.left, &_upLeft.top, &_upLeft.right, &_upLeft.bottom); + break; + + case TOKEN_UP_RIGHT: + parser.scanStr((char *)params, "%d,%d,%d,%d", &_upRight.left, &_upRight.top, &_upRight.right, &_upRight.bottom); + break; + + case TOKEN_UP_MIDDLE: + parser.scanStr((char *)params, "%d,%d,%d,%d", &_upMiddle.left, &_upMiddle.top, &_upMiddle.right, &_upMiddle.bottom); + break; + + case TOKEN_DOWN_LEFT: + parser.scanStr((char *)params, "%d,%d,%d,%d", &_downLeft.left, &_downLeft.top, &_downLeft.right, &_downLeft.bottom); + break; + + case TOKEN_DOWN_RIGHT: + parser.scanStr((char *)params, "%d,%d,%d,%d", &_downRight.left, &_downRight.top, &_downRight.right, &_downRight.bottom); + break; + + case TOKEN_DOWN_MIDDLE: + parser.scanStr((char *)params, "%d,%d,%d,%d", &_downMiddle.left, &_downMiddle.top, &_downMiddle.right, &_downMiddle.bottom); + break; + + case TOKEN_MIDDLE_LEFT: + parser.scanStr((char *)params, "%d,%d,%d,%d", &_middleLeft.left, &_middleLeft.top, &_middleLeft.right, &_middleLeft.bottom); + break; + + case TOKEN_MIDDLE_RIGHT: + parser.scanStr((char *)params, "%d,%d,%d,%d", &_middleRight.left, &_middleRight.top, &_middleRight.right, &_middleRight.bottom); + break; + + case TOKEN_MIDDLE_MIDDLE: + parser.scanStr((char *)params, "%d,%d,%d,%d", &_middleMiddle.left, &_middleMiddle.top, &_middleMiddle.right, &_middleMiddle.bottom); + break; + + case TOKEN_HORIZONTAL_TILES: + parser.scanStr((char *)params, "%d,%d,%d", &h1, &h2, &h3); + hTiles = true; + break; + + case TOKEN_VERTICAL_TILES: + parser.scanStr((char *)params, "%d,%d,%d", &v1, &v2, &v3); + vTiles = true; + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty(params, false); + break; + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in TILED_IMAGE definition"); + return STATUS_FAILED; + } + if (cmd == PARSERR_GENERIC) { + _gameRef->LOG(0, "Error loading TILED_IMAGE definition"); + return STATUS_FAILED; + } + + if (vTiles && hTiles) { + // up row + BasePlatform::setRect(&_upLeft, 0, 0, h1, v1); + BasePlatform::setRect(&_upMiddle, h1, 0, h1 + h2, v1); + BasePlatform::setRect(&_upRight, h1 + h2, 0, h1 + h2 + h3, v1); + + // middle row + BasePlatform::setRect(&_middleLeft, 0, v1, h1, v1 + v2); + BasePlatform::setRect(&_middleMiddle, h1, v1, h1 + h2, v1 + v2); + BasePlatform::setRect(&_middleRight, h1 + h2, v1, h1 + h2 + h3, v1 + v2); + + // down row + BasePlatform::setRect(&_downLeft, 0, v1 + v2, h1, v1 + v2 + v3); + BasePlatform::setRect(&_downMiddle, h1, v1 + v2, h1 + h2, v1 + v2 + v3); + BasePlatform::setRect(&_downRight, h1 + h2, v1 + v2, h1 + h2 + h3, v1 + v2 + v3); + } + + // default + if (_image && _image->_surface) { + int width = _image->_surface->getWidth() / 3; + int height = _image->_surface->getHeight() / 3; + + if (BasePlatform::isRectEmpty(&_upLeft)) { + BasePlatform::setRect(&_upLeft, 0, 0, width, height); + } + if (BasePlatform::isRectEmpty(&_upMiddle)) { + BasePlatform::setRect(&_upMiddle, width, 0, 2 * width, height); + } + if (BasePlatform::isRectEmpty(&_upRight)) { + BasePlatform::setRect(&_upRight, 2 * width, 0, 3 * width, height); + } + + if (BasePlatform::isRectEmpty(&_middleLeft)) { + BasePlatform::setRect(&_middleLeft, 0, height, width, 2 * height); + } + if (BasePlatform::isRectEmpty(&_middleMiddle)) { + BasePlatform::setRect(&_middleMiddle, width, height, 2 * width, 2 * height); + } + if (BasePlatform::isRectEmpty(&_middleRight)) { + BasePlatform::setRect(&_middleRight, 2 * width, height, 3 * width, 2 * height); + } + + if (BasePlatform::isRectEmpty(&_downLeft)) { + BasePlatform::setRect(&_downLeft, 0, 2 * height, width, 3 * height); + } + if (BasePlatform::isRectEmpty(&_downMiddle)) { + BasePlatform::setRect(&_downMiddle, width, 2 * height, 2 * width, 3 * height); + } + if (BasePlatform::isRectEmpty(&_downRight)) { + BasePlatform::setRect(&_downRight, 2 * width, 2 * height, 3 * width, 3 * height); + } + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool UITiledImage::saveAsText(BaseDynamicBuffer *buffer, int indent) { + buffer->putTextIndent(indent, "TILED_IMAGE\n"); + buffer->putTextIndent(indent, "{\n"); + + if (_image && _image->getSurfaceFilename()) { + buffer->putTextIndent(indent + 2, "IMAGE=\"%s\"\n", _image->getSurfaceFilename()); + } + + int h1, h2, h3; + int v1, v2, v3; + + h1 = _upLeft.right; + h2 = _upMiddle.right - _upMiddle.left; + h3 = _upRight.right - _upRight.left; + + v1 = _upLeft.bottom; + v2 = _middleLeft.bottom - _middleLeft.top; + v3 = _downLeft.bottom - _downLeft.top; + + + buffer->putTextIndent(indent + 2, "VERTICAL_TILES { %d, %d, %d }\n", v1, v2, v3); + buffer->putTextIndent(indent + 2, "HORIZONTAL_TILES { %d, %d, %d }\n", h1, h2, h3); + + // editor properties + BaseClass::saveAsText(buffer, indent + 2); + + buffer->putTextIndent(indent, "}\n"); + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +void UITiledImage::correctSize(int *width, int *height) { + int tileWidth = _middleMiddle.right - _middleMiddle.left; + int tileHeight = _middleMiddle.bottom - _middleMiddle.top; + + int nuColumns = (*width - (_middleLeft.right - _middleLeft.left) - (_middleRight.right - _middleRight.left)) / tileWidth; + int nuRows = (*height - (_upMiddle.bottom - _upMiddle.top) - (_downMiddle.bottom - _downMiddle.top)) / tileHeight; + + *width = (_middleLeft.right - _middleLeft.left) + (_middleRight.right - _middleRight.left) + nuColumns * tileWidth; + *height = (_upMiddle.bottom - _upMiddle.top) + (_downMiddle.bottom - _downMiddle.top) + nuRows * tileHeight; +} + + +////////////////////////////////////////////////////////////////////////// +bool UITiledImage::persist(BasePersistenceManager *persistMgr) { + BaseObject::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_downLeft)); + persistMgr->transfer(TMEMBER(_downMiddle)); + persistMgr->transfer(TMEMBER(_downRight)); + persistMgr->transfer(TMEMBER(_image)); + persistMgr->transfer(TMEMBER(_middleLeft)); + persistMgr->transfer(TMEMBER(_middleMiddle)); + persistMgr->transfer(TMEMBER(_middleRight)); + persistMgr->transfer(TMEMBER(_upLeft)); + persistMgr->transfer(TMEMBER(_upMiddle)); + persistMgr->transfer(TMEMBER(_upRight)); + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ui/ui_tiled_image.h b/engines/wintermute/ui/ui_tiled_image.h new file mode 100644 index 0000000000..edea84f346 --- /dev/null +++ b/engines/wintermute/ui/ui_tiled_image.h @@ -0,0 +1,64 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_UITILEDIMAGE_H +#define WINTERMUTE_UITILEDIMAGE_H + + +#include "engines/wintermute/ui/ui_object.h" +#include "common/rect.h" + +namespace Wintermute { +class BaseSubFrame; +class UITiledImage : public BaseObject { +public: + DECLARE_PERSISTENT(UITiledImage, BaseObject) + void correctSize(int *width, int *height); + bool loadFile(const char *filename); + bool loadBuffer(byte *buffer, bool complete = true); + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent); + + bool display(int x, int y, int width, int height); + UITiledImage(BaseGame *inGame = NULL); + virtual ~UITiledImage(); +private: + BaseSubFrame *_image; + Rect32 _upLeft; + Rect32 _upMiddle; + Rect32 _upRight; + Rect32 _middleLeft; + Rect32 _middleMiddle; + Rect32 _middleRight; + Rect32 _downLeft; + Rect32 _downMiddle; + Rect32 _downRight; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ui/ui_window.cpp b/engines/wintermute/ui/ui_window.cpp new file mode 100644 index 0000000000..9606486efb --- /dev/null +++ b/engines/wintermute/ui/ui_window.cpp @@ -0,0 +1,1445 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_active_rect.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/base_keyboard_state.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/ui/ui_button.h" +#include "engines/wintermute/ui/ui_edit.h" +#include "engines/wintermute/ui/ui_text.h" +#include "engines/wintermute/ui/ui_tiled_image.h" +#include "engines/wintermute/ui/ui_window.h" +#include "engines/wintermute/base/base_viewport.h" +#include "engines/wintermute/base/font/base_font_storage.h" +#include "engines/wintermute/base/font/base_font.h" +#include "engines/wintermute/base/base_string_table.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/platform_osystem.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(UIWindow, false) + +////////////////////////////////////////////////////////////////////////// +UIWindow::UIWindow(BaseGame *inGame) : UIObject(inGame) { + BasePlatform::setRectEmpty(&_titleRect); + BasePlatform::setRectEmpty(&_dragRect); + _titleAlign = TAL_LEFT; + _transparent = false; + + _backInactive = NULL; + _fontInactive = NULL; + _imageInactive = NULL; + + _type = UI_WINDOW; + _canFocus = true; + + _dragging = false; + _dragFrom.x = _dragFrom.y = 0; + + _mode = WINDOW_NORMAL; + _shieldWindow = NULL; + _shieldButton = NULL; + + _fadeColor = 0x00000000; + _fadeBackground = false; + + _ready = true; + _isMenu = false; + _inGame = false; + + _clipContents = false; + _viewport = NULL; + + _pauseMusic = true; +} + + +////////////////////////////////////////////////////////////////////////// +UIWindow::~UIWindow() { + close(); + cleanup(); +} + + +////////////////////////////////////////////////////////////////////////// +void UIWindow::cleanup() { + delete _shieldWindow; + delete _shieldButton; + delete _viewport; + _shieldWindow = NULL; + _shieldButton = NULL; + _viewport = NULL; + + delete _backInactive; + if (!_sharedFonts && _fontInactive) { + _gameRef->_fontStorage->removeFont(_fontInactive); + } + if (!_sharedImages && _imageInactive) { + delete _imageInactive; + } + + for (uint32 i = 0; i < _widgets.size(); i++) { + delete _widgets[i]; + } + _widgets.clear(); +} + + +////////////////////////////////////////////////////////////////////////// +bool UIWindow::display(int offsetX, int offsetY) { + // go exclusive + if (_mode == WINDOW_EXCLUSIVE || _mode == WINDOW_SYSTEM_EXCLUSIVE) { + if (!_shieldWindow) { + _shieldWindow = new UIWindow(_gameRef); + } + if (_shieldWindow) { + _shieldWindow->_posX = _shieldWindow->_posY = 0; + _shieldWindow->_width = _gameRef->_renderer->_width; + _shieldWindow->_height = _gameRef->_renderer->_height; + + _shieldWindow->display(); + } + } else if (_isMenu) { + if (!_shieldButton) { + _shieldButton = new UIButton(_gameRef); + _shieldButton->setName("close"); + _shieldButton->setListener(this, _shieldButton, 0); + _shieldButton->_parent = this; + } + if (_shieldButton) { + _shieldButton->_posX = _shieldButton->_posY = 0; + _shieldButton->_width = _gameRef->_renderer->_width; + _shieldButton->_height = _gameRef->_renderer->_height; + + _shieldButton->display(); + } + } + + if (!_visible) { + return STATUS_OK; + } + + if (_fadeBackground) { + Graphics::PixelFormat format = _gameRef->_renderer->getPixelFormat(); + byte fadeR, fadeG, fadeB, fadeA; + // First convert from the internal format to the screen-format + uint32 fadeColor = format.ARGBToColor(RGBCOLGetA(_fadeColor), RGBCOLGetR(_fadeColor), RGBCOLGetG(_fadeColor), RGBCOLGetB(_fadeColor)); + // Then get components + format.colorToARGB(fadeColor, fadeA, fadeR, fadeG, fadeB); + _gameRef->_renderer->fadeToColor(fadeR, fadeG, fadeB, fadeA); + } + + if (_dragging) { + _posX += (_gameRef->_mousePos.x - _dragFrom.x); + _posY += (_gameRef->_mousePos.y - _dragFrom.y); + + _dragFrom.x = _gameRef->_mousePos.x; + _dragFrom.y = _gameRef->_mousePos.y; + } + + if (!_focusedWidget || (!_focusedWidget->_canFocus || _focusedWidget->_disable || !_focusedWidget->_visible)) { + moveFocus(); + } + + bool popViewport = false; + if (_clipContents) { + if (!_viewport) { + _viewport = new BaseViewport(_gameRef); + } + if (_viewport) { + _viewport->setRect(_posX + offsetX, _posY + offsetY, _posX + _width + offsetX, _posY + _height + offsetY); + _gameRef->pushViewport(_viewport); + popViewport = true; + } + } + + + UITiledImage *back = _back; + BaseSprite *image = _image; + BaseFont *font = _font; + + if (!isFocused()) { + if (_backInactive) { + back = _backInactive; + } + if (_imageInactive) { + image = _imageInactive; + } + if (_fontInactive) { + font = _fontInactive; + } + } + + if (_alphaColor != 0) { + _gameRef->_renderer->_forceAlphaColor = _alphaColor; + } + if (back) { + back->display(_posX + offsetX, _posY + offsetY, _width, _height); + } + if (image) { + image->draw(_posX + offsetX, _posY + offsetY, _transparent ? NULL : this); + } + + if (!BasePlatform::isRectEmpty(&_titleRect) && font && _text) { + font->drawText((byte *)_text, _posX + offsetX + _titleRect.left, _posY + offsetY + _titleRect.top, _titleRect.right - _titleRect.left, _titleAlign, _titleRect.bottom - _titleRect.top); + } + + if (!_transparent && !image) { + _gameRef->_renderer->addRectToList(new BaseActiveRect(_gameRef, this, NULL, _posX + offsetX, _posY + offsetY, _width, _height, 100, 100, false)); + } + + for (uint32 i = 0; i < _widgets.size(); i++) { + _widgets[i]->display(_posX + offsetX, _posY + offsetY); + } + + if (_alphaColor != 0) { + _gameRef->_renderer->_forceAlphaColor = 0; + } + + if (popViewport) { + _gameRef->popViewport(); + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool UIWindow::loadFile(const char *filename) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "UIWindow::LoadFile failed for file '%s'", filename); + return STATUS_FAILED; + } + + bool ret; + + setFilename(filename); + + if (DID_FAIL(ret = loadBuffer(buffer, true))) { + _gameRef->LOG(0, "Error parsing WINDOW file '%s'", filename); + } + + delete[] buffer; + + return ret; +} + + +TOKEN_DEF_START +TOKEN_DEF(WINDOW) +TOKEN_DEF(ALPHA_COLOR) +TOKEN_DEF(ALPHA) +TOKEN_DEF(TEMPLATE) +TOKEN_DEF(DISABLED) +TOKEN_DEF(VISIBLE) +TOKEN_DEF(BACK_INACTIVE) +TOKEN_DEF(BACK) +TOKEN_DEF(IMAGE_INACTIVE) +TOKEN_DEF(IMAGE) +TOKEN_DEF(FONT_INACTIVE) +TOKEN_DEF(FONT) +TOKEN_DEF(TITLE_ALIGN) +TOKEN_DEF(TITLE_RECT) +TOKEN_DEF(TITLE) +TOKEN_DEF(DRAG_RECT) +TOKEN_DEF(X) +TOKEN_DEF(Y) +TOKEN_DEF(WIDTH) +TOKEN_DEF(HEIGHT) +TOKEN_DEF(FADE_ALPHA) +TOKEN_DEF(FADE_COLOR) +TOKEN_DEF(CURSOR) +TOKEN_DEF(NAME) +TOKEN_DEF(BUTTON) +TOKEN_DEF(STATIC) +TOKEN_DEF(TRANSPARENT) +TOKEN_DEF(SCRIPT) +TOKEN_DEF(CAPTION) +TOKEN_DEF(PARENT_NOTIFY) +TOKEN_DEF(MENU) +TOKEN_DEF(IN_GAME) +TOKEN_DEF(CLIP_CONTENTS) +TOKEN_DEF(PAUSE_MUSIC) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF(EDIT) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool UIWindow::loadBuffer(byte *buffer, bool complete) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(WINDOW) + TOKEN_TABLE(ALPHA_COLOR) + TOKEN_TABLE(ALPHA) + TOKEN_TABLE(TEMPLATE) + TOKEN_TABLE(DISABLED) + TOKEN_TABLE(VISIBLE) + TOKEN_TABLE(BACK_INACTIVE) + TOKEN_TABLE(BACK) + TOKEN_TABLE(IMAGE_INACTIVE) + TOKEN_TABLE(IMAGE) + TOKEN_TABLE(FONT_INACTIVE) + TOKEN_TABLE(FONT) + TOKEN_TABLE(TITLE_ALIGN) + TOKEN_TABLE(TITLE_RECT) + TOKEN_TABLE(TITLE) + TOKEN_TABLE(DRAG_RECT) + TOKEN_TABLE(X) + TOKEN_TABLE(Y) + TOKEN_TABLE(WIDTH) + TOKEN_TABLE(HEIGHT) + TOKEN_TABLE(FADE_ALPHA) + TOKEN_TABLE(FADE_COLOR) + TOKEN_TABLE(CURSOR) + TOKEN_TABLE(NAME) + TOKEN_TABLE(BUTTON) + TOKEN_TABLE(STATIC) + TOKEN_TABLE(TRANSPARENT) + TOKEN_TABLE(SCRIPT) + TOKEN_TABLE(CAPTION) + TOKEN_TABLE(PARENT_NOTIFY) + TOKEN_TABLE(MENU) + TOKEN_TABLE(IN_GAME) + TOKEN_TABLE(CLIP_CONTENTS) + TOKEN_TABLE(PAUSE_MUSIC) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE(EDIT) + TOKEN_TABLE_END + + byte *params; + int cmd = 2; + BaseParser parser; + + int fadeR = 0, fadeG = 0, fadeB = 0, fadeA = 0; + int ar = 0, ag = 0, ab = 0, alpha = 0; + + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_WINDOW) { + _gameRef->LOG(0, "'WINDOW' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + while (cmd >= PARSERR_TOKENNOTFOUND && (cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) >= PARSERR_TOKENNOTFOUND) { + switch (cmd) { + case TOKEN_TEMPLATE: + if (DID_FAIL(loadFile((char *)params))) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_NAME: + setName((char *)params); + break; + + case TOKEN_CAPTION: + setCaption((char *)params); + break; + + case TOKEN_BACK: + delete _back; + _back = new UITiledImage(_gameRef); + if (!_back || DID_FAIL(_back->loadFile((char *)params))) { + delete _back; + _back = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_BACK_INACTIVE: + delete _backInactive; + _backInactive = new UITiledImage(_gameRef); + if (!_backInactive || DID_FAIL(_backInactive->loadFile((char *)params))) { + delete _backInactive; + _backInactive = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_IMAGE: + delete _image; + _image = new BaseSprite(_gameRef); + if (!_image || DID_FAIL(_image->loadFile((char *)params))) { + delete _image; + _image = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_IMAGE_INACTIVE: + delete _imageInactive, + _imageInactive = new BaseSprite(_gameRef); + if (!_imageInactive || DID_FAIL(_imageInactive->loadFile((char *)params))) { + delete _imageInactive; + _imageInactive = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_FONT: + if (_font) { + _gameRef->_fontStorage->removeFont(_font); + } + _font = _gameRef->_fontStorage->addFont((char *)params); + if (!_font) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_FONT_INACTIVE: + if (_fontInactive) { + _gameRef->_fontStorage->removeFont(_fontInactive); + } + _fontInactive = _gameRef->_fontStorage->addFont((char *)params); + if (!_fontInactive) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_TITLE: + setText((char *)params); + _gameRef->_stringTable->expand(&_text); + break; + + case TOKEN_TITLE_ALIGN: + if (scumm_stricmp((char *)params, "left") == 0) { + _titleAlign = TAL_LEFT; + } else if (scumm_stricmp((char *)params, "right") == 0) { + _titleAlign = TAL_RIGHT; + } else { + _titleAlign = TAL_CENTER; + } + break; + + case TOKEN_TITLE_RECT: + parser.scanStr((char *)params, "%d,%d,%d,%d", &_titleRect.left, &_titleRect.top, &_titleRect.right, &_titleRect.bottom); + break; + + case TOKEN_DRAG_RECT: + parser.scanStr((char *)params, "%d,%d,%d,%d", &_dragRect.left, &_dragRect.top, &_dragRect.right, &_dragRect.bottom); + break; + + case TOKEN_X: + parser.scanStr((char *)params, "%d", &_posX); + break; + + case TOKEN_Y: + parser.scanStr((char *)params, "%d", &_posY); + break; + + case TOKEN_WIDTH: + parser.scanStr((char *)params, "%d", &_width); + break; + + case TOKEN_HEIGHT: + parser.scanStr((char *)params, "%d", &_height); + break; + + case TOKEN_CURSOR: + delete _cursor; + _cursor = new BaseSprite(_gameRef); + if (!_cursor || DID_FAIL(_cursor->loadFile((char *)params))) { + delete _cursor; + _cursor = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_BUTTON: { + UIButton *btn = new UIButton(_gameRef); + if (!btn || DID_FAIL(btn->loadBuffer(params, false))) { + delete btn; + btn = NULL; + cmd = PARSERR_GENERIC; + } else { + btn->_parent = this; + _widgets.add(btn); + } + } + break; + + case TOKEN_STATIC: { + UIText *text = new UIText(_gameRef); + if (!text || DID_FAIL(text->loadBuffer(params, false))) { + delete text; + text = NULL; + cmd = PARSERR_GENERIC; + } else { + text->_parent = this; + _widgets.add(text); + } + } + break; + + case TOKEN_EDIT: { + UIEdit *edit = new UIEdit(_gameRef); + if (!edit || DID_FAIL(edit->loadBuffer(params, false))) { + delete edit; + edit = NULL; + cmd = PARSERR_GENERIC; + } else { + edit->_parent = this; + _widgets.add(edit); + } + } + break; + + case TOKEN_WINDOW: { + UIWindow *win = new UIWindow(_gameRef); + if (!win || DID_FAIL(win->loadBuffer(params, false))) { + delete win; + win = NULL; + cmd = PARSERR_GENERIC; + } else { + win->_parent = this; + _widgets.add(win); + } + } + break; + + + case TOKEN_TRANSPARENT: + parser.scanStr((char *)params, "%b", &_transparent); + break; + + case TOKEN_SCRIPT: + addScript((char *)params); + break; + + case TOKEN_PARENT_NOTIFY: + parser.scanStr((char *)params, "%b", &_parentNotify); + break; + + case TOKEN_PAUSE_MUSIC: + parser.scanStr((char *)params, "%b", &_pauseMusic); + break; + + case TOKEN_DISABLED: + parser.scanStr((char *)params, "%b", &_disable); + break; + + case TOKEN_VISIBLE: + parser.scanStr((char *)params, "%b", &_visible); + break; + + case TOKEN_MENU: + parser.scanStr((char *)params, "%b", &_isMenu); + break; + + case TOKEN_IN_GAME: + parser.scanStr((char *)params, "%b", &_inGame); + break; + + case TOKEN_CLIP_CONTENTS: + parser.scanStr((char *)params, "%b", &_clipContents); + break; + + case TOKEN_FADE_COLOR: + parser.scanStr((char *)params, "%d,%d,%d", &fadeR, &fadeG, &fadeB); + _fadeBackground = true; + break; + + case TOKEN_FADE_ALPHA: + parser.scanStr((char *)params, "%d", &fadeA); + _fadeBackground = true; + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty(params, false); + break; + + case TOKEN_ALPHA_COLOR: + parser.scanStr((char *)params, "%d,%d,%d", &ar, &ag, &ab); + break; + + case TOKEN_ALPHA: + parser.scanStr((char *)params, "%d", &alpha); + break; + + + default: + if (DID_FAIL(_gameRef->windowLoadHook(this, (char **)&buffer, (char **)params))) { + cmd = PARSERR_GENERIC; + } + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in WINDOW definition"); + return STATUS_FAILED; + } + if (cmd == PARSERR_GENERIC) { + _gameRef->LOG(0, "Error loading WINDOW definition"); + return STATUS_FAILED; + } + + correctSize(); + + if (alpha != 0 && ar == 0 && ag == 0 && ab == 0) { + ar = ag = ab = 255; + } + _alphaColor = BYTETORGBA(ar, ag, ab, alpha); + + if (_fadeBackground) { + _fadeColor = BYTETORGBA(fadeR, fadeG, fadeB, fadeA); + } + + _focusedWidget = NULL; + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool UIWindow::saveAsText(BaseDynamicBuffer *buffer, int indent) { + buffer->putTextIndent(indent, "WINDOW\n"); + buffer->putTextIndent(indent, "{\n"); + + buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", getName()); + buffer->putTextIndent(indent + 2, "CAPTION=\"%s\"\n", getCaption()); + + buffer->putTextIndent(indent + 2, "\n"); + + if (_back && _back->getFilename()) { + buffer->putTextIndent(indent + 2, "BACK=\"%s\"\n", _back->getFilename()); + } + if (_backInactive && _backInactive->getFilename()) { + buffer->putTextIndent(indent + 2, "BACK_INACTIVE=\"%s\"\n", _backInactive->getFilename()); + } + + if (_image && _image->getFilename()) { + buffer->putTextIndent(indent + 2, "IMAGE=\"%s\"\n", _image->getFilename()); + } + if (_imageInactive && _imageInactive->getFilename()) { + buffer->putTextIndent(indent + 2, "IMAGE_INACTIVE=\"%s\"\n", _imageInactive->getFilename()); + } + + if (_font && _font->getFilename()) { + buffer->putTextIndent(indent + 2, "FONT=\"%s\"\n", _font->getFilename()); + } + if (_fontInactive && _fontInactive->getFilename()) { + buffer->putTextIndent(indent + 2, "FONT_INACTIVE=\"%s\"\n", _fontInactive->getFilename()); + } + + if (_cursor && _cursor->getFilename()) { + buffer->putTextIndent(indent + 2, "CURSOR=\"%s\"\n", _cursor->getFilename()); + } + + buffer->putTextIndent(indent + 2, "\n"); + + if (_text) { + buffer->putTextIndent(indent + 2, "TITLE=\"%s\"\n", _text); + } + + switch (_titleAlign) { + case TAL_LEFT: + buffer->putTextIndent(indent + 2, "TITLE_ALIGN=\"%s\"\n", "left"); + break; + case TAL_RIGHT: + buffer->putTextIndent(indent + 2, "TITLE_ALIGN=\"%s\"\n", "right"); + break; + case TAL_CENTER: + buffer->putTextIndent(indent + 2, "TITLE_ALIGN=\"%s\"\n", "center"); + break; + default: + error("UIWindow::SaveAsText - Unhandled enum-value NUM_TEXT_ALIGN"); + } + + if (!BasePlatform::isRectEmpty(&_titleRect)) { + buffer->putTextIndent(indent + 2, "TITLE_RECT { %d, %d, %d, %d }\n", _titleRect.left, _titleRect.top, _titleRect.right, _titleRect.bottom); + } + + if (!BasePlatform::isRectEmpty(&_dragRect)) { + buffer->putTextIndent(indent + 2, "DRAG_RECT { %d, %d, %d, %d }\n", _dragRect.left, _dragRect.top, _dragRect.right, _dragRect.bottom); + } + + buffer->putTextIndent(indent + 2, "\n"); + + buffer->putTextIndent(indent + 2, "X=%d\n", _posX); + buffer->putTextIndent(indent + 2, "Y=%d\n", _posY); + buffer->putTextIndent(indent + 2, "WIDTH=%d\n", _width); + buffer->putTextIndent(indent + 2, "HEIGHT=%d\n", _height); + + buffer->putTextIndent(indent + 2, "DISABLED=%s\n", _disable ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "VISIBLE=%s\n", _visible ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "PARENT_NOTIFY=%s\n", _parentNotify ? "TRUE" : "FALSE"); + + buffer->putTextIndent(indent + 2, "TRANSPARENT=%s\n", _transparent ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "PAUSE_MUSIC=%s\n", _pauseMusic ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "MENU=%s\n", _isMenu ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "IN_GAME=%s\n", _inGame ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "CLIP_CONTENTS=%s\n", _clipContents ? "TRUE" : "FALSE"); + + buffer->putTextIndent(indent + 2, "\n"); + + if (_fadeBackground) { + buffer->putTextIndent(indent + 2, "FADE_COLOR { %d, %d, %d }\n", RGBCOLGetR(_fadeColor), RGBCOLGetG(_fadeColor), RGBCOLGetB(_fadeColor)); + buffer->putTextIndent(indent + 2, "FADE_ALPHA=%d\n", RGBCOLGetA(_fadeColor)); + } + + buffer->putTextIndent(indent + 2, "ALPHA_COLOR { %d, %d, %d }\n", RGBCOLGetR(_alphaColor), RGBCOLGetG(_alphaColor), RGBCOLGetB(_alphaColor)); + buffer->putTextIndent(indent + 2, "ALPHA=%d\n", RGBCOLGetA(_alphaColor)); + + buffer->putTextIndent(indent + 2, "\n"); + + // scripts + for (uint32 i = 0; i < _scripts.size(); i++) { + buffer->putTextIndent(indent + 2, "SCRIPT=\"%s\"\n", _scripts[i]->_filename); + } + + buffer->putTextIndent(indent + 2, "\n"); + + // editor properties + BaseClass::saveAsText(buffer, indent + 2); + + // controls + for (uint32 i = 0; i < _widgets.size(); i++) { + _widgets[i]->saveAsText(buffer, indent + 2); + } + + + buffer->putTextIndent(indent, "}\n"); + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool UIWindow::enableWidget(const char *name, bool enable) { + for (uint32 i = 0; i < _widgets.size(); i++) { + if (scumm_stricmp(_widgets[i]->getName(), name) == 0) { + _widgets[i]->_disable = !enable; + } + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool UIWindow::showWidget(const char *name, bool visible) { + for (uint32 i = 0; i < _widgets.size(); i++) { + if (scumm_stricmp(_widgets[i]->getName(), name) == 0) { + _widgets[i]->_visible = visible; + } + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool UIWindow::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // GetWidget / GetControl + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "GetWidget") == 0 || strcmp(name, "GetControl") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + if (val->getType() == VAL_INT) { + int widget = val->getInt(); + if (widget < 0 || widget >= (int32)_widgets.size()) { + stack->pushNULL(); + } else { + stack->pushNative(_widgets[widget], true); + } + } else { + for (uint32 i = 0; i < _widgets.size(); i++) { + if (scumm_stricmp(_widgets[i]->getName(), val->getString()) == 0) { + stack->pushNative(_widgets[i], true); + return STATUS_OK; + } + } + stack->pushNULL(); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetInactiveFont + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetInactiveFont") == 0) { + stack->correctParams(1); + + if (_fontInactive) { + _gameRef->_fontStorage->removeFont(_fontInactive); + } + _fontInactive = _gameRef->_fontStorage->addFont(stack->pop()->getString()); + stack->pushBool(_fontInactive != NULL); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetInactiveImage + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetInactiveImage") == 0) { + stack->correctParams(1); + + delete _imageInactive; + _imageInactive = new BaseSprite(_gameRef); + const char *filename = stack->pop()->getString(); + if (!_imageInactive || DID_FAIL(_imageInactive->loadFile(filename))) { + delete _imageInactive; + _imageInactive = NULL; + stack->pushBool(false); + } else { + stack->pushBool(true); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetInactiveImage + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetInactiveImage") == 0) { + stack->correctParams(0); + if (!_imageInactive || !_imageInactive->getFilename()) { + stack->pushNULL(); + } else { + stack->pushString(_imageInactive->getFilename()); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetInactiveImageObject + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetInactiveImageObject") == 0) { + stack->correctParams(0); + if (!_imageInactive) { + stack->pushNULL(); + } else { + stack->pushNative(_imageInactive, true); + } + + return STATUS_OK; + } + + + ////////////////////////////////////////////////////////////////////////// + // Close + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Close") == 0) { + stack->correctParams(0); + stack->pushBool(DID_SUCCEED(close())); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GoExclusive + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GoExclusive") == 0) { + stack->correctParams(0); + goExclusive(); + script->waitFor(this); + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GoSystemExclusive + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GoSystemExclusive") == 0) { + stack->correctParams(0); + goSystemExclusive(); + script->waitFor(this); + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Center + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Center") == 0) { + stack->correctParams(0); + _posX = (_gameRef->_renderer->_width - _width) / 2; + _posY = (_gameRef->_renderer->_height - _height) / 2; + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // LoadFromFile + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "LoadFromFile") == 0) { + stack->correctParams(1); + + ScValue *val = stack->pop(); + cleanup(); + if (!val->isNULL()) { + stack->pushBool(DID_SUCCEED(loadFile(val->getString()))); + } else { + stack->pushBool(true); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // CreateButton + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "CreateButton") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + UIButton *btn = new UIButton(_gameRef); + if (!val->isNULL()) { + btn->setName(val->getString()); + } + stack->pushNative(btn, true); + + btn->_parent = this; + _widgets.add(btn); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // CreateStatic + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "CreateStatic") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + UIText *sta = new UIText(_gameRef); + if (!val->isNULL()) { + sta->setName(val->getString()); + } + stack->pushNative(sta, true); + + sta->_parent = this; + _widgets.add(sta); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // CreateEditor + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "CreateEditor") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + UIEdit *edi = new UIEdit(_gameRef); + if (!val->isNULL()) { + edi->setName(val->getString()); + } + stack->pushNative(edi, true); + + edi->_parent = this; + _widgets.add(edi); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // CreateWindow + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "CreateWindow") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + UIWindow *win = new UIWindow(_gameRef); + if (!val->isNULL()) { + win->setName(val->getString()); + } + stack->pushNative(win, true); + + win->_parent = this; + _widgets.add(win); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // DeleteControl / DeleteButton / DeleteStatic / DeleteEditor / DeleteWindow + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "DeleteControl") == 0 || strcmp(name, "DeleteButton") == 0 || strcmp(name, "DeleteStatic") == 0 || strcmp(name, "DeleteEditor") == 0 || strcmp(name, "DeleteWindow") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + UIObject *obj = (UIObject *)val->getNative(); + + for (uint32 i = 0; i < _widgets.size(); i++) { + if (_widgets[i] == obj) { + delete _widgets[i]; + _widgets.remove_at(i); + if (val->getType() == VAL_VARIABLE_REF) { + val->setNULL(); + } + } + } + stack->pushNULL(); + return STATUS_OK; + } else if DID_SUCCEED(_gameRef->windowScriptMethodHook(this, script, stack, name)) { + return STATUS_OK; + } + + else { + return UIObject::scCallMethod(script, stack, thisStack, name); + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *UIWindow::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("window"); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // NumWidgets / NumControls (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "NumWidgets" || name == "NumControls") { + _scValue->setInt(_widgets.size()); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Exclusive + ////////////////////////////////////////////////////////////////////////// + else if (name == "Exclusive") { + _scValue->setBool(_mode == WINDOW_EXCLUSIVE); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // SystemExclusive + ////////////////////////////////////////////////////////////////////////// + else if (name == "SystemExclusive") { + _scValue->setBool(_mode == WINDOW_SYSTEM_EXCLUSIVE); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Menu + ////////////////////////////////////////////////////////////////////////// + else if (name == "Menu") { + _scValue->setBool(_isMenu); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // InGame + ////////////////////////////////////////////////////////////////////////// + else if (name == "InGame") { + _scValue->setBool(_inGame); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // PauseMusic + ////////////////////////////////////////////////////////////////////////// + else if (name == "PauseMusic") { + _scValue->setBool(_pauseMusic); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // ClipContents + ////////////////////////////////////////////////////////////////////////// + else if (name == "ClipContents") { + _scValue->setBool(_clipContents); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Transparent + ////////////////////////////////////////////////////////////////////////// + else if (name == "Transparent") { + _scValue->setBool(_transparent); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // FadeColor + ////////////////////////////////////////////////////////////////////////// + else if (name == "FadeColor") { + _scValue->setInt((int)_fadeColor); + return _scValue; + } else { + return UIObject::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool UIWindow::scSetProperty(const char *name, ScValue *value) { + ////////////////////////////////////////////////////////////////////////// + // Name + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Name") == 0) { + setName(value->getString()); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Menu + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Menu") == 0) { + _isMenu = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // InGame + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "InGame") == 0) { + _inGame = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // PauseMusic + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "PauseMusic") == 0) { + _pauseMusic = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ClipContents + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ClipContents") == 0) { + _clipContents = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Transparent + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Transparent") == 0) { + _transparent = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // FadeColor + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "FadeColor") == 0) { + _fadeColor = (uint32)value->getInt(); + _fadeBackground = (_fadeColor != 0); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Exclusive + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Exclusive") == 0) { + if (value->getBool()) { + goExclusive(); + } else { + close(); + _visible = true; + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SystemExclusive + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SystemExclusive") == 0) { + if (value->getBool()) { + goSystemExclusive(); + } else { + close(); + _visible = true; + } + return STATUS_OK; + } else { + return UIObject::scSetProperty(name, value); + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *UIWindow::scToString() { + return "[window]"; +} + + +////////////////////////////////////////////////////////////////////////// +bool UIWindow::handleKeypress(Common::Event *event, bool printable) { +//TODO + if (event->type == Common::EVENT_KEYDOWN && event->kbd.keycode == Common::KEYCODE_TAB) { + return DID_SUCCEED(moveFocus(!BaseKeyboardState::isShiftDown())); + } else { + if (_focusedWidget) { + return _focusedWidget->handleKeypress(event, printable); + } else { + return false; + } + } + return false; +} + + +////////////////////////////////////////////////////////////////////////// +bool UIWindow::handleMouseWheel(int Delta) { + if (_focusedWidget) { + return _focusedWidget->handleMouseWheel(Delta); + } else { + return false; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool UIWindow::handleMouse(TMouseEvent event, TMouseButton button) { + bool res = UIObject::handleMouse(event, button); + + // handle window dragging + if (!BasePlatform::isRectEmpty(&_dragRect)) { + // start drag + if (event == MOUSE_CLICK && button == MOUSE_BUTTON_LEFT) { + Rect32 dragRect = _dragRect; + int offsetX, offsetY; + getTotalOffset(&offsetX, &offsetY); + dragRect.offsetRect(_posX + offsetX, _posY + offsetY); + + if (BasePlatform::ptInRect(&dragRect, _gameRef->_mousePos)) { + _dragFrom.x = _gameRef->_mousePos.x; + _dragFrom.y = _gameRef->_mousePos.y; + _dragging = true; + } + } + // end drag + else if (_dragging && event == MOUSE_RELEASE && button == MOUSE_BUTTON_LEFT) { + _dragging = false; + } + } + + return res; +} + + + +////////////////////////////////////////////////////////////////////////// +bool UIWindow::persist(BasePersistenceManager *persistMgr) { + + UIObject::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_backInactive)); + persistMgr->transfer(TMEMBER(_clipContents)); + persistMgr->transfer(TMEMBER(_dragFrom)); + persistMgr->transfer(TMEMBER(_dragging)); + persistMgr->transfer(TMEMBER(_dragRect)); + persistMgr->transfer(TMEMBER(_fadeBackground)); + persistMgr->transfer(TMEMBER(_fadeColor)); + persistMgr->transfer(TMEMBER(_fontInactive)); + persistMgr->transfer(TMEMBER(_imageInactive)); + persistMgr->transfer(TMEMBER(_inGame)); + persistMgr->transfer(TMEMBER(_isMenu)); + persistMgr->transfer(TMEMBER_INT(_mode)); + persistMgr->transfer(TMEMBER(_shieldButton)); + persistMgr->transfer(TMEMBER(_shieldWindow)); + persistMgr->transfer(TMEMBER_INT(_titleAlign)); + persistMgr->transfer(TMEMBER(_titleRect)); + persistMgr->transfer(TMEMBER(_transparent)); + persistMgr->transfer(TMEMBER(_viewport)); + persistMgr->transfer(TMEMBER(_pauseMusic)); + + _widgets.persist(persistMgr); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool UIWindow::moveFocus(bool forward) { + int i; + bool found = false; + for (i = 0; i < (int32)_widgets.size(); i++) { + if (_widgets[i] == _focusedWidget) { + found = true; + break; + } + } + if (!found) { + _focusedWidget = NULL; + } + + if (!_focusedWidget) { + if (_widgets.size() > 0) { + i = 0; + } else { + return STATUS_OK; + } + } + + int numTries = 0; + bool done = false; + + while (numTries <= (int32)_widgets.size()) { + if (_widgets[i] != _focusedWidget && _widgets[i]->_canFocus && _widgets[i]->_visible && !_widgets[i]->_disable) { + _focusedWidget = _widgets[i]; + done = true; + break; + } + + if (forward) { + i++; + if (i >= (int32)_widgets.size()) { + i = 0; + } + } else { + i--; + if (i < 0) { + i = _widgets.size() - 1; + } + } + numTries++; + } + + return done ? STATUS_OK : STATUS_FAILED; +} + + +////////////////////////////////////////////////////////////////////////// +bool UIWindow::goExclusive() { + if (_mode == WINDOW_EXCLUSIVE) { + return STATUS_OK; + } + + if (_mode == WINDOW_NORMAL) { + _ready = false; + _mode = WINDOW_EXCLUSIVE; + _visible = true; + _disable = false; + _gameRef->focusWindow(this); + return STATUS_OK; + } else { + return STATUS_FAILED; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool UIWindow::goSystemExclusive() { + if (_mode == WINDOW_SYSTEM_EXCLUSIVE) { + return STATUS_OK; + } + + makeFreezable(false); + + _mode = WINDOW_SYSTEM_EXCLUSIVE; + _ready = false; + _visible = true; + _disable = false; + _gameRef->focusWindow(this); + + _gameRef->freeze(_pauseMusic); + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool UIWindow::close() { + if (_mode == WINDOW_SYSTEM_EXCLUSIVE) { + _gameRef->unfreeze(); + } + + _mode = WINDOW_NORMAL; + _visible = false; + _ready = true; + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool UIWindow::listen(BaseScriptHolder *param1, uint32 param2) { + UIObject *obj = (UIObject *)param1; + + switch (obj->_type) { + case UI_BUTTON: + if (scumm_stricmp(obj->getName(), "close") == 0) { + close(); + } else { + return BaseObject::listen(param1, param2); + } + break; + default: + return BaseObject::listen(param1, param2); + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +void UIWindow::makeFreezable(bool freezable) { + for (uint32 i = 0; i < _widgets.size(); i++) { + _widgets[i]->makeFreezable(freezable); + } + + BaseObject::makeFreezable(freezable); +} + + +////////////////////////////////////////////////////////////////////////// +bool UIWindow::getWindowObjects(BaseArray<UIObject *> &objects, bool interactiveOnly) { + for (uint32 i = 0; i < _widgets.size(); i++) { + UIObject *control = _widgets[i]; + if (control->_disable && interactiveOnly) { + continue; + } + + switch (control->_type) { + case UI_WINDOW: + ((UIWindow *)control)->getWindowObjects(objects, interactiveOnly); + break; + + case UI_BUTTON: + case UI_EDIT: + objects.add(control); + break; + + default: + if (!interactiveOnly) { + objects.add(control); + } + } + } + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ui/ui_window.h b/engines/wintermute/ui/ui_window.h new file mode 100644 index 0000000000..ae035c65c7 --- /dev/null +++ b/engines/wintermute/ui/ui_window.h @@ -0,0 +1,94 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_UIWINDOW_H +#define WINTERMUTE_UIWINDOW_H + + +#include "engines/wintermute/ui/ui_object.h" +#include "common/events.h" + +namespace Wintermute { + +class UIButton; +class BaseViewport; +class UIWindow : public UIObject { + uint32 _fadeColor; +public: + bool getWindowObjects(BaseArray<UIObject *> &Objects, bool InteractiveOnly); + + bool _pauseMusic; + void cleanup(); + virtual void makeFreezable(bool freezable); + BaseViewport *_viewport; + bool _clipContents; + bool _inGame; + bool _isMenu; + bool _fadeBackground; + + virtual bool handleMouseWheel(int delta); + UIWindow *_shieldWindow; + UIButton *_shieldButton; + bool close(); + bool goSystemExclusive(); + bool goExclusive(); + TWindowMode _mode; + bool moveFocus(bool forward = true); + virtual bool handleMouse(TMouseEvent Event, TMouseButton Button); + Point32 _dragFrom; + bool _dragging; + DECLARE_PERSISTENT(UIWindow, UIObject) + bool _transparent; + bool showWidget(const char *name, bool visible = true); + bool enableWidget(const char *name, bool enable = true); + Rect32 _titleRect; + Rect32 _dragRect; + virtual bool display(int offsetX = 0, int offsetY = 0); + UIWindow(BaseGame *inGame); + virtual ~UIWindow(); + virtual bool handleKeypress(Common::Event *event, bool printable = false); + BaseArray<UIObject *> _widgets; + TTextAlign _titleAlign; + bool loadFile(const char *filename); + bool loadBuffer(byte *buffer, bool complete = true); + UITiledImage *_backInactive; + BaseFont *_fontInactive; + BaseSprite *_imageInactive; + virtual bool listen(BaseScriptHolder *param1, uint32 param2); + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent); + + // scripting interface + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + virtual const char *scToString(); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/utils/convert_utf.cpp b/engines/wintermute/utils/convert_utf.cpp new file mode 100644 index 0000000000..7ebc011d01 --- /dev/null +++ b/engines/wintermute/utils/convert_utf.cpp @@ -0,0 +1,615 @@ +/* + * Copyright 2001-2004 Unicode, Inc. + * + * Disclaimer + * + * This source code is provided as is by Unicode, Inc. No claims are + * made as to fitness for any particular purpose. No warranties of any + * kind are expressed or implied. The recipient agrees to determine + * applicability of information provided. If this file has been + * purchased on magnetic or optical media from Unicode, Inc., the + * sole remedy for any claim will be exchange of defective media + * within 90 days of receipt. + * + * Limitations on Rights to Redistribute This Code + * + * Unicode, Inc. hereby grants the right to freely use the information + * supplied in this file in the creation of products supporting the + * Unicode Standard, and to make copies of this file in any form + * for internal or external distribution as long as this notice + * remains attached. + */ + +/* --------------------------------------------------------------------- + + Conversions between UTF32, UTF-16, and UTF-8. Source code file. + Author: Mark E. Davis, 1994. + Rev History: Rick McGowan, fixes & updates May 2001. + Sept 2001: fixed const & error conditions per + mods suggested by S. Parent & A. Lillich. + June 2002: Tim Dodd added detection and handling of incomplete + source sequences, enhanced error detection, added casts + to eliminate compiler warnings. + July 2003: slight mods to back out aggressive FFFE detection. + Jan 2004: updated switches in from-UTF8 conversions. + Oct 2004: updated to use UNI_MAX_LEGAL_UTF32 in UTF-32 conversions. + + See the header file "ConvertUTF.h" for complete documentation. + +------------------------------------------------------------------------ */ + + +#include "engines/wintermute/utils/convert_utf.h" +#ifdef CVTUTF_DEBUG +#include "common/textconsole.h" +#endif + +namespace Wintermute { + +static const int halfShift = 10; /* used for shifting by 10 bits */ + +static const UTF32 halfBase = 0x0010000UL; +static const UTF32 halfMask = 0x3FFUL; + +#define UNI_SUR_HIGH_START (UTF32)0xD800 +#define UNI_SUR_HIGH_END (UTF32)0xDBFF +#define UNI_SUR_LOW_START (UTF32)0xDC00 +#define UNI_SUR_LOW_END (UTF32)0xDFFF +#define false 0 +#define true 1 + +/* --------------------------------------------------------------------- */ + +ConversionResult ConvertUTF32toUTF16( + const UTF32 **sourceStart, const UTF32 *sourceEnd, + UTF16 **targetStart, UTF16 *targetEnd, ConversionFlags flags) { + ConversionResult result = conversionOK; + const UTF32 *source = *sourceStart; + UTF16 *target = *targetStart; + while (source < sourceEnd) { + UTF32 ch; + if (target >= targetEnd) { + result = targetExhausted; + break; + } + ch = *source++; + if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */ + /* UTF-16 surrogate values are illegal in UTF-32; 0xffff or 0xfffe are both reserved values */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { + if (flags == strictConversion) { + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } else { + *target++ = UNI_REPLACEMENT_CHAR; + } + } else { + *target++ = (UTF16)ch; /* normal case */ + } + } else if (ch > UNI_MAX_LEGAL_UTF32) { + if (flags == strictConversion) { + result = sourceIllegal; + } else { + *target++ = UNI_REPLACEMENT_CHAR; + } + } else { + /* target is a character in range 0xFFFF - 0x10FFFF. */ + if (target + 1 >= targetEnd) { + --source; /* Back up source pointer! */ + result = targetExhausted; + break; + } + ch -= halfBase; + *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START); + *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START); + } + } + *sourceStart = source; + *targetStart = target; + return result; +} + +/* --------------------------------------------------------------------- */ + +ConversionResult ConvertUTF16toUTF32( + const UTF16 **sourceStart, const UTF16 *sourceEnd, + UTF32 **targetStart, UTF32 *targetEnd, ConversionFlags flags) { + ConversionResult result = conversionOK; + const UTF16 *source = *sourceStart; + UTF32 *target = *targetStart; + UTF32 ch, ch2; + while (source < sourceEnd) { + const UTF16 *oldSource = source; /* In case we have to back up because of target overflow. */ + ch = *source++; + /* If we have a surrogate pair, convert to UTF32 first. */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) { + /* If the 16 bits following the high surrogate are in the source buffer... */ + if (source < sourceEnd) { + ch2 = *source; + /* If it's a low surrogate, convert to UTF32. */ + if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) { + ch = ((ch - UNI_SUR_HIGH_START) << halfShift) + + (ch2 - UNI_SUR_LOW_START) + halfBase; + ++source; + } else if (flags == strictConversion) { /* it's an unpaired high surrogate */ + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } + } else { /* We don't have the 16 bits following the high surrogate. */ + --source; /* return to the high surrogate */ + result = sourceExhausted; + break; + } + } else if (flags == strictConversion) { + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) { + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } + } + if (target >= targetEnd) { + source = oldSource; /* Back up source pointer! */ + result = targetExhausted; + break; + } + *target++ = ch; + } + *sourceStart = source; + *targetStart = target; +#ifdef CVTUTF_DEBUG + if (result == sourceIllegal) { + warning("ConvertUTF16toUTF32 illegal seq 0x%04x,%04x\n", ch, ch2); + } +#endif + return result; +} + +/* --------------------------------------------------------------------- */ + +/* + * Index into the table below with the first byte of a UTF-8 sequence to + * get the number of trailing bytes that are supposed to follow it. + * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is + * left as-is for anyone who may want to do such conversion, which was + * allowed in earlier algorithms. + */ +static const char trailingBytesForUTF8[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5 +}; + +/* + * Magic values subtracted from a buffer value during UTF8 conversion. + * This table contains as many values as there might be trailing bytes + * in a UTF-8 sequence. + */ +static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL, + 0x03C82080UL, 0xFA082080UL, 0x82082080UL + }; + +/* + * Once the bits are split out into bytes of UTF-8, this is a mask OR-ed + * into the first byte, depending on how many bytes follow. There are + * as many entries in this table as there are UTF-8 sequence types. + * (I.e., one byte sequence, two byte... etc.). Remember that sequencs + * for *legal* UTF-8 will be 4 or fewer bytes total. + */ +static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + +/* --------------------------------------------------------------------- */ + +/* The interface converts a whole buffer to avoid function-call overhead. + * Constants have been gathered. Loops & conditionals have been removed as + * much as possible for efficiency, in favor of drop-through switches. + * (See "Note A" at the bottom of the file for equivalent code.) + * If your compiler supports it, the "isLegalUTF8" call can be turned + * into an inline function. + */ + +/* --------------------------------------------------------------------- */ + +ConversionResult ConvertUTF16toUTF8( + const UTF16 **sourceStart, const UTF16 *sourceEnd, + UTF8 **targetStart, UTF8 *targetEnd, ConversionFlags flags) { + ConversionResult result = conversionOK; + const UTF16 *source = *sourceStart; + UTF8 *target = *targetStart; + while (source < sourceEnd) { + UTF32 ch; + unsigned short bytesToWrite = 0; + const UTF32 byteMask = 0xBF; + const UTF32 byteMark = 0x80; + const UTF16 *oldSource = source; /* In case we have to back up because of target overflow. */ + ch = *source++; + /* If we have a surrogate pair, convert to UTF32 first. */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) { + /* If the 16 bits following the high surrogate are in the source buffer... */ + if (source < sourceEnd) { + UTF32 ch2 = *source; + /* If it's a low surrogate, convert to UTF32. */ + if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) { + ch = ((ch - UNI_SUR_HIGH_START) << halfShift) + + (ch2 - UNI_SUR_LOW_START) + halfBase; + ++source; + } else if (flags == strictConversion) { /* it's an unpaired high surrogate */ + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } + } else { /* We don't have the 16 bits following the high surrogate. */ + --source; /* return to the high surrogate */ + result = sourceExhausted; + break; + } + } else if (flags == strictConversion) { + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) { + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } + } + /* Figure out how many bytes the result will require */ + if (ch < (UTF32)0x80) { + bytesToWrite = 1; + } else if (ch < (UTF32)0x800) { + bytesToWrite = 2; + } else if (ch < (UTF32)0x10000) { + bytesToWrite = 3; + } else if (ch < (UTF32)0x110000) { + bytesToWrite = 4; + } else { + bytesToWrite = 3; + ch = UNI_REPLACEMENT_CHAR; + } + + target += bytesToWrite; + if (target > targetEnd) { + source = oldSource; /* Back up source pointer! */ + target -= bytesToWrite; + result = targetExhausted; + break; + } + switch (bytesToWrite) { /* note: everything falls through. */ + case 4: + *--target = (UTF8)((ch | byteMark) & byteMask); + ch >>= 6; + case 3: + *--target = (UTF8)((ch | byteMark) & byteMask); + ch >>= 6; + case 2: + *--target = (UTF8)((ch | byteMark) & byteMask); + ch >>= 6; + case 1: + *--target = (UTF8)(ch | firstByteMark[bytesToWrite]); + } + target += bytesToWrite; + } + *sourceStart = source; + *targetStart = target; + return result; +} + +/* --------------------------------------------------------------------- */ + +/* + * Utility routine to tell whether a sequence of bytes is legal UTF-8. + * This must be called with the length pre-determined by the first byte. + * If not calling this from ConvertUTF8to*, then the length can be set by: + * length = trailingBytesForUTF8[*source]+1; + * and the sequence is illegal right away if there aren't that many bytes + * available. + * If presented with a length > 4, this returns false. The Unicode + * definition of UTF-8 goes up to 4-byte sequences. + */ + +static Boolean isLegalUTF8(const UTF8 *source, int length) { + UTF8 a; + const UTF8 *srcptr = source + length; + switch (length) { + default: + return false; + /* Everything else falls through when "true"... */ + case 4: + if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; + case 3: + if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; + case 2: + if ((a = (*--srcptr)) > 0xBF) return false; + + switch (*source) { + /* no fall-through in this inner switch */ + case 0xE0: + if (a < 0xA0) return false; + break; + case 0xED: + if (a > 0x9F) return false; + break; + case 0xF0: + if (a < 0x90) return false; + break; + case 0xF4: + if (a > 0x8F) return false; + break; + default: + if (a < 0x80) return false; + } + + case 1: + if (*source >= 0x80 && *source < 0xC2) return false; + } + if (*source > 0xF4) return false; + return true; +} + +/* --------------------------------------------------------------------- */ + +/* + * Exported function to return whether a UTF-8 sequence is legal or not. + * This is not used here; it's just exported. + */ +Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd) { + int length = trailingBytesForUTF8[*source] + 1; + if (source + length > sourceEnd) { + return false; + } + return isLegalUTF8(source, length); +} + +/* --------------------------------------------------------------------- */ + +ConversionResult ConvertUTF8toUTF16( + const UTF8 **sourceStart, const UTF8 *sourceEnd, + UTF16 **targetStart, UTF16 *targetEnd, ConversionFlags flags) { + ConversionResult result = conversionOK; + const UTF8 *source = *sourceStart; + UTF16 *target = *targetStart; + while (source < sourceEnd) { + UTF32 ch = 0; + unsigned short extraBytesToRead = trailingBytesForUTF8[*source]; + if (source + extraBytesToRead >= sourceEnd) { + result = sourceExhausted; + break; + } + /* Do this check whether lenient or strict */ + if (! isLegalUTF8(source, extraBytesToRead + 1)) { + result = sourceIllegal; + break; + } + /* + * The cases all fall through. See "Note A" below. + */ + switch (extraBytesToRead) { + case 5: + ch += *source++; + ch <<= 6; /* remember, illegal UTF-8 */ + case 4: + ch += *source++; + ch <<= 6; /* remember, illegal UTF-8 */ + case 3: + ch += *source++; + ch <<= 6; + case 2: + ch += *source++; + ch <<= 6; + case 1: + ch += *source++; + ch <<= 6; + case 0: + ch += *source++; + } + ch -= offsetsFromUTF8[extraBytesToRead]; + + if (target >= targetEnd) { + source -= (extraBytesToRead + 1); /* Back up source pointer! */ + result = targetExhausted; + break; + } + if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */ + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { + if (flags == strictConversion) { + source -= (extraBytesToRead + 1); /* return to the illegal value itself */ + result = sourceIllegal; + break; + } else { + *target++ = UNI_REPLACEMENT_CHAR; + } + } else { + *target++ = (UTF16)ch; /* normal case */ + } + } else if (ch > UNI_MAX_UTF16) { + if (flags == strictConversion) { + result = sourceIllegal; + source -= (extraBytesToRead + 1); /* return to the start */ + break; /* Bail out; shouldn't continue */ + } else { + *target++ = UNI_REPLACEMENT_CHAR; + } + } else { + /* target is a character in range 0xFFFF - 0x10FFFF. */ + if (target + 1 >= targetEnd) { + source -= (extraBytesToRead + 1); /* Back up source pointer! */ + result = targetExhausted; + break; + } + ch -= halfBase; + *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START); + *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START); + } + } + *sourceStart = source; + *targetStart = target; + return result; +} + +/* --------------------------------------------------------------------- */ + +ConversionResult ConvertUTF32toUTF8( + const UTF32 **sourceStart, const UTF32 *sourceEnd, + UTF8 **targetStart, UTF8 *targetEnd, ConversionFlags flags) { + ConversionResult result = conversionOK; + const UTF32 *source = *sourceStart; + UTF8 *target = *targetStart; + while (source < sourceEnd) { + UTF32 ch; + unsigned short bytesToWrite = 0; + const UTF32 byteMask = 0xBF; + const UTF32 byteMark = 0x80; + ch = *source++; + if (flags == strictConversion) { + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } + } + /* + * Figure out how many bytes the result will require. Turn any + * illegally large UTF32 things (> Plane 17) into replacement chars. + */ + if (ch < (UTF32)0x80) { + bytesToWrite = 1; + } else if (ch < (UTF32)0x800) { + bytesToWrite = 2; + } else if (ch < (UTF32)0x10000) { + bytesToWrite = 3; + } else if (ch <= UNI_MAX_LEGAL_UTF32) { + bytesToWrite = 4; + } else { + bytesToWrite = 3; + ch = UNI_REPLACEMENT_CHAR; + result = sourceIllegal; + } + + target += bytesToWrite; + if (target > targetEnd) { + --source; /* Back up source pointer! */ + target -= bytesToWrite; + result = targetExhausted; + break; + } + switch (bytesToWrite) { /* note: everything falls through. */ + case 4: + *--target = (UTF8)((ch | byteMark) & byteMask); + ch >>= 6; + case 3: + *--target = (UTF8)((ch | byteMark) & byteMask); + ch >>= 6; + case 2: + *--target = (UTF8)((ch | byteMark) & byteMask); + ch >>= 6; + case 1: + *--target = (UTF8)(ch | firstByteMark[bytesToWrite]); + } + target += bytesToWrite; + } + *sourceStart = source; + *targetStart = target; + return result; +} + +/* --------------------------------------------------------------------- */ + +ConversionResult ConvertUTF8toUTF32( + const UTF8 **sourceStart, const UTF8 *sourceEnd, + UTF32 **targetStart, UTF32 *targetEnd, ConversionFlags flags) { + ConversionResult result = conversionOK; + const UTF8 *source = *sourceStart; + UTF32 *target = *targetStart; + while (source < sourceEnd) { + UTF32 ch = 0; + unsigned short extraBytesToRead = trailingBytesForUTF8[*source]; + if (source + extraBytesToRead >= sourceEnd) { + result = sourceExhausted; + break; + } + /* Do this check whether lenient or strict */ + if (! isLegalUTF8(source, extraBytesToRead + 1)) { + result = sourceIllegal; + break; + } + /* + * The cases all fall through. See "Note A" below. + */ + switch (extraBytesToRead) { + case 5: + ch += *source++; + ch <<= 6; + case 4: + ch += *source++; + ch <<= 6; + case 3: + ch += *source++; + ch <<= 6; + case 2: + ch += *source++; + ch <<= 6; + case 1: + ch += *source++; + ch <<= 6; + case 0: + ch += *source++; + } + ch -= offsetsFromUTF8[extraBytesToRead]; + + if (target >= targetEnd) { + source -= (extraBytesToRead + 1); /* Back up the source pointer! */ + result = targetExhausted; + break; + } + if (ch <= UNI_MAX_LEGAL_UTF32) { + /* + * UTF-16 surrogate values are illegal in UTF-32, and anything + * over Plane 17 (> 0x10FFFF) is illegal. + */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { + if (flags == strictConversion) { + source -= (extraBytesToRead + 1); /* return to the illegal value itself */ + result = sourceIllegal; + break; + } else { + *target++ = UNI_REPLACEMENT_CHAR; + } + } else { + *target++ = ch; + } + } else { /* i.e., ch > UNI_MAX_LEGAL_UTF32 */ + result = sourceIllegal; + *target++ = UNI_REPLACEMENT_CHAR; + } + } + *sourceStart = source; + *targetStart = target; + return result; +} + +/* --------------------------------------------------------------------- + + Note A. + The fall-through switches in UTF-8 reading code save a + temp variable, some decrements & conditionals. The switches + are equivalent to the following loop: + { + int tmpBytesToRead = extraBytesToRead+1; + do { + ch += *source++; + --tmpBytesToRead; + if (tmpBytesToRead) ch <<= 6; + } while (tmpBytesToRead > 0); + } + In UTF-8 writing code, the switches on "bytesToWrite" are + similarly unrolled loops. + + --------------------------------------------------------------------- */ + +} // End of namespace Wintermute diff --git a/engines/wintermute/utils/convert_utf.h b/engines/wintermute/utils/convert_utf.h new file mode 100644 index 0000000000..a5f34456f5 --- /dev/null +++ b/engines/wintermute/utils/convert_utf.h @@ -0,0 +1,148 @@ +/* + * Copyright 2001-2004 Unicode, Inc. + * + * Disclaimer + * + * This source code is provided as is by Unicode, Inc. No claims are + * made as to fitness for any particular purpose. No warranties of any + * kind are expressed or implied. The recipient agrees to determine + * applicability of information provided. If this file has been + * purchased on magnetic or optical media from Unicode, Inc., the + * sole remedy for any claim will be exchange of defective media + * within 90 days of receipt. + * + * Limitations on Rights to Redistribute This Code + * + * Unicode, Inc. hereby grants the right to freely use the information + * supplied in this file in the creation of products supporting the + * Unicode Standard, and to make copies of this file in any form + * for internal or external distribution as long as this notice + * remains attached. + */ + +// NOTE: Modifications have been made to the code for inclusion +// into ScummVM. + +/* --------------------------------------------------------------------- + + Conversions between UTF32, UTF-16, and UTF-8. Header file. + + Several funtions are included here, forming a complete set of + conversions between the three formats. UTF-7 is not included + here, but is handled in a separate source file. + + Each of these routines takes pointers to input buffers and output + buffers. The input buffers are const. + + Each routine converts the text between *sourceStart and sourceEnd, + putting the result into the buffer between *targetStart and + targetEnd. Note: the end pointers are *after* the last item: e.g. + *(sourceEnd - 1) is the last item. + + The return result indicates whether the conversion was successful, + and if not, whether the problem was in the source or target buffers. + (Only the first encountered problem is indicated.) + + After the conversion, *sourceStart and *targetStart are both + updated to point to the end of last text successfully converted in + the respective buffers. + + Input parameters: + sourceStart - pointer to a pointer to the source buffer. + The contents of this are modified on return so that + it points at the next thing to be converted. + targetStart - similarly, pointer to pointer to the target buffer. + sourceEnd, targetEnd - respectively pointers to the ends of the + two buffers, for overflow checking only. + + These conversion functions take a ConversionFlags argument. When this + flag is set to strict, both irregular sequences and isolated surrogates + will cause an error. When the flag is set to lenient, both irregular + sequences and isolated surrogates are converted. + + Whether the flag is strict or lenient, all illegal sequences will cause + an error return. This includes sequences such as: <F4 90 80 80>, <C0 80>, + or <A0> in UTF-8, and values above 0x10FFFF in UTF-32. Conformant code + must check for illegal sequences. + + When the flag is set to lenient, characters over 0x10FFFF are converted + to the replacement character; otherwise (when the flag is set to strict) + they constitute an error. + + Output parameters: + The value "sourceIllegal" is returned from some routines if the input + sequence is malformed. When "sourceIllegal" is returned, the source + value will point to the illegal value that caused the problem. E.g., + in UTF-8 when a sequence is malformed, it points to the start of the + malformed sequence. + + Author: Mark E. Davis, 1994. + Rev History: Rick McGowan, fixes & updates May 2001. + Fixes & updates, Sept 2001. + +------------------------------------------------------------------------ */ + +/* --------------------------------------------------------------------- + The following 4 definitions are compiler-specific. + The C standard does not guarantee that wchar_t has at least + 16 bits, so wchar_t is no less portable than unsigned short! + All should be unsigned values to avoid sign extension during + bit mask & shift operations. +------------------------------------------------------------------------ */ +#include "common/system.h" + +namespace Wintermute { + +typedef uint32 UTF32; /* at least 32 bits */ +typedef uint16 UTF16; /* at least 16 bits */ +typedef uint8 UTF8; /* typically 8 bits */ +typedef uint8 Boolean; /* 0 or 1 */ + +/* Some fundamental constants */ +#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD +#define UNI_MAX_BMP (UTF32)0x0000FFFF +#define UNI_MAX_UTF16 (UTF32)0x0010FFFF +#define UNI_MAX_UTF32 (UTF32)0x7FFFFFFF +#define UNI_MAX_LEGAL_UTF32 (UTF32)0x0010FFFF + +typedef enum { + conversionOK, /* conversion successful */ + sourceExhausted, /* partial character in source, but hit end */ + targetExhausted, /* insuff. room in target for conversion */ + sourceIllegal /* source sequence is illegal/malformed */ +} ConversionResult; + +typedef enum { + strictConversion = 0, + lenientConversion +} ConversionFlags; + +ConversionResult ConvertUTF8toUTF16( + const UTF8 **sourceStart, const UTF8 *sourceEnd, + UTF16 **targetStart, UTF16 *targetEnd, ConversionFlags flags); + +ConversionResult ConvertUTF16toUTF8( + const UTF16 **sourceStart, const UTF16 *sourceEnd, + UTF8 **targetStart, UTF8 *targetEnd, ConversionFlags flags); + +ConversionResult ConvertUTF8toUTF32( + const UTF8 **sourceStart, const UTF8 *sourceEnd, + UTF32 **targetStart, UTF32 *targetEnd, ConversionFlags flags); + +ConversionResult ConvertUTF32toUTF8( + const UTF32 **sourceStart, const UTF32 *sourceEnd, + UTF8 **targetStart, UTF8 *targetEnd, ConversionFlags flags); + +ConversionResult ConvertUTF16toUTF32( + const UTF16 **sourceStart, const UTF16 *sourceEnd, + UTF32 **targetStart, UTF32 *targetEnd, ConversionFlags flags); + +ConversionResult ConvertUTF32toUTF16( + const UTF32 **sourceStart, const UTF32 *sourceEnd, + UTF16 **targetStart, UTF16 *targetEnd, ConversionFlags flags); + +Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd); + +} // End of namespace Wintermute + +/* --------------------------------------------------------------------- */ diff --git a/engines/wintermute/utils/crc.cpp b/engines/wintermute/utils/crc.cpp new file mode 100644 index 0000000000..e7ec45511b --- /dev/null +++ b/engines/wintermute/utils/crc.cpp @@ -0,0 +1,237 @@ +/********************************************************************** + * + * Filename: crc.c + * + * Description: Slow and fast implementations of the CRC standards. + * + * Notes: The parameters for each supported CRC standard are + * defined in the header file crc.h. The implementations + * here should stand up to further additions to that list. + * + * + * Copyright (c) 2000 by Michael Barr. This software is placed into + * the public domain and may be used for any purpose. However, this + * notice must not be changed or removed and no warranty is either + * expressed or implied by its publication or distribution. + **********************************************************************/ + +#include "engines/wintermute/utils/crc.h" + +namespace Wintermute { + +/* + * Derive parameters from the standard-specific parameters in crc.h. + */ +#define WIDTH (8 * sizeof(crc)) +#define TOPBIT (1 << (WIDTH - 1)) + +#if (REFLECT_DATA == TRUE) +#undef REFLECT_DATA +#define REFLECT_DATA(X) ((unsigned char) reflect((X), 8)) +#else +#undef REFLECT_DATA +#define REFLECT_DATA(X) (X) +#endif + +#if (REFLECT_REMAINDER == TRUE) +#undef REFLECT_REMAINDER +#define REFLECT_REMAINDER(X) ((crc) reflect((X), WIDTH)) +#else +#undef REFLECT_REMAINDER +#define REFLECT_REMAINDER(X) (X) +#endif + + +/********************************************************************* + * + * Function: reflect() + * + * Description: Reorder the bits of a binary sequence, by reflecting + * them about the middle position. + * + * Notes: No checking is done that nBits <= 32. + * + * Returns: The reflection of the original data. + * + *********************************************************************/ +static unsigned long +reflect(unsigned long data, unsigned char nBits) { + unsigned long reflection = 0x00000000; + unsigned char bit; + + /* + * Reflect the data about the center bit. + */ + for (bit = 0; bit < nBits; ++bit) { + /* + * If the LSB bit is set, set the reflection of it. + */ + if (data & 0x01) { + reflection |= (1 << ((nBits - 1) - bit)); + } + + data = (data >> 1); + } + + return (reflection); + +} /* reflect() */ + + +/********************************************************************* + * + * Function: crcSlow() + * + * Description: Compute the CRC of a given message. + * + * Notes: + * + * Returns: The CRC of the message. + * + *********************************************************************/ +crc +crcSlow(unsigned char const message[], int nBytes) { + crc remainder = INITIAL_REMAINDER; + int byte; + unsigned char bit; + + + /* + * Perform modulo-2 division, a byte at a time. + */ + for (byte = 0; byte < nBytes; ++byte) { + /* + * Bring the next byte into the remainder. + */ + remainder ^= (REFLECT_DATA(message[byte]) << (WIDTH - 8)); + + /* + * Perform modulo-2 division, a bit at a time. + */ + for (bit = 8; bit > 0; --bit) { + /* + * Try to divide the current data bit. + */ + if (remainder & TOPBIT) { + remainder = (remainder << 1) ^ POLYNOMIAL; + } else { + remainder = (remainder << 1); + } + } + } + + /* + * The final remainder is the CRC result. + */ + return (REFLECT_REMAINDER(remainder) ^ FINAL_XOR_VALUE); + +} /* crcSlow() */ + + +crc crcTable[256]; + + +/********************************************************************* + * + * Function: crcInit() + * + * Description: Populate the partial CRC lookup table. + * + * Notes: This function must be rerun any time the CRC standard + * is changed. If desired, it can be run "offline" and + * the table results stored in an embedded system's ROM. + * + * Returns: None defined. + * + *********************************************************************/ +void +crcInit(void) { + crc remainder; + int dividend; + unsigned char bit; + + + /* + * Compute the remainder of each possible dividend. + */ + for (dividend = 0; dividend < 256; ++dividend) { + /* + * Start with the dividend followed by zeros. + */ + remainder = dividend << (WIDTH - 8); + + /* + * Perform modulo-2 division, a bit at a time. + */ + for (bit = 8; bit > 0; --bit) { + /* + * Try to divide the current data bit. + */ + if (remainder & TOPBIT) { + remainder = (remainder << 1) ^ POLYNOMIAL; + } else { + remainder = (remainder << 1); + } + } + + /* + * Store the result into the table. + */ + crcTable[dividend] = remainder; + } + +} /* crcInit() */ + + +/********************************************************************* + * + * Function: crcFast() + * + * Description: Compute the CRC of a given message. + * + * Notes: crcInit() must be called first. + * + * Returns: The CRC of the message. + * + *********************************************************************/ +crc +crcFast(unsigned char const message[], int nBytes) { + crc remainder = INITIAL_REMAINDER; + unsigned char data; + int byte; + + + /* + * Divide the message by the polynomial, a byte at a time. + */ + for (byte = 0; byte < nBytes; ++byte) { + data = (unsigned char)(REFLECT_DATA(message[byte]) ^ (remainder >> (WIDTH - 8))); + remainder = crcTable[data] ^ (remainder << 8); + } + + /* + * The final remainder is the CRC. + */ + return (REFLECT_REMAINDER(remainder) ^ FINAL_XOR_VALUE); + +} /* crcFast() */ + + + +crc crc_initialize(void) { + crcInit(); + return INITIAL_REMAINDER; +} + +crc crc_process_byte(unsigned char byteVal, crc remainder) { + unsigned char data; + data = (unsigned char)(REFLECT_DATA(byteVal) ^ (remainder >> (WIDTH - 8))); + remainder = crcTable[data] ^ (remainder << 8); + return remainder; +} + +crc crc_finalize(crc remainder) { + return (REFLECT_REMAINDER(remainder) ^ FINAL_XOR_VALUE); +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/utils/crc.h b/engines/wintermute/utils/crc.h new file mode 100644 index 0000000000..77c2ea267f --- /dev/null +++ b/engines/wintermute/utils/crc.h @@ -0,0 +1,85 @@ +/********************************************************************** + * + * Filename: crc.h + * + * Description: A header file describing the various CRC standards. + * + * Notes: + * + * + * Copyright (c) 2000 by Michael Barr. This software is placed into + * the public domain and may be used for any purpose. However, this + * notice must not be changed or removed and no warranty is either + * expressed or implied by its publication or distribution. + **********************************************************************/ + +#ifndef _crc_h +#define _crc_h + +#include "common/system.h" // For types. + +namespace Wintermute { + +#ifndef TRUE +#define FALSE 0 +#define TRUE !FALSE +#endif + +/* + * Select the CRC standard from the list that follows. + */ +#define CRC32 + +#if defined(CRC_CCITT) + +typedef uint16 crc; + +#define CRC_NAME "CRC-CCITT" +#define POLYNOMIAL 0x1021 +#define INITIAL_REMAINDER 0xFFFF +#define FINAL_XOR_VALUE 0x0000 +#define REFLECT_DATA FALSE +#define REFLECT_REMAINDER FALSE +#define CHECK_VALUE 0x29B1 + +#elif defined(CRC16) + +typedef uint16 crc; + +#define CRC_NAME "CRC-16" +#define POLYNOMIAL 0x8005 +#define INITIAL_REMAINDER 0x0000 +#define FINAL_XOR_VALUE 0x0000 +#define REFLECT_DATA TRUE +#define REFLECT_REMAINDER TRUE +#define CHECK_VALUE 0xBB3D + +#elif defined(CRC32) + +typedef uint32 crc; + +#define CRC_NAME "CRC-32" +#define POLYNOMIAL 0x04C11DB7 +#define INITIAL_REMAINDER 0xFFFFFFFF +#define FINAL_XOR_VALUE 0xFFFFFFFF +#define REFLECT_DATA TRUE +#define REFLECT_REMAINDER TRUE +#define CHECK_VALUE 0xCBF43926 + +#else + +#error "One of CRC_CCITT, CRC16, or CRC32 must be #define'd." + +#endif + +void crcInit(void); +crc crcSlow(unsigned char const message[], int nBytes); +crc crcFast(unsigned char const message[], int nBytes); + +extern "C" crc crc_initialize(void); +extern "C" crc crc_process_byte(unsigned char byteVal, crc remainder); +extern "C" crc crc_finalize(crc remainder); + +} // End of namespace Wintermute + +#endif /* _crc_h */ diff --git a/engines/wintermute/utils/path_util.cpp b/engines/wintermute/utils/path_util.cpp new file mode 100644 index 0000000000..298f0c268f --- /dev/null +++ b/engines/wintermute/utils/path_util.cpp @@ -0,0 +1,101 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "common/file.h" +#include "engines/wintermute/utils/path_util.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////////// +AnsiString PathUtil::unifySeparators(const AnsiString &path) { + AnsiString newPath = path; + + for (uint32 i = 0; i < newPath.size(); i++) { + if (newPath[i] == '\\') { + newPath.setChar('/', i); + } + } + + return newPath; +} + +////////////////////////////////////////////////////////////////////////// +AnsiString PathUtil::normalizeFileName(const AnsiString &path) { + AnsiString newPath = unifySeparators(path); + newPath.toLowercase(); + return newPath; +} + +////////////////////////////////////////////////////////////////////////// +AnsiString PathUtil::combine(const AnsiString &path1, const AnsiString &path2) { + AnsiString newPath1 = unifySeparators(path1); + AnsiString newPath2 = unifySeparators(path2); + + if (!newPath1.hasSuffix("/") && !newPath2.hasPrefix("/")) { + newPath1 += "/"; + } + + return newPath1 + newPath2; +} + +////////////////////////////////////////////////////////////////////////// +AnsiString PathUtil::getDirectoryName(const AnsiString &path) { + AnsiString newPath = unifySeparators(path); + Common::String filename = getFileName(path); + return Common::String(path.c_str(), path.size() - filename.size()); +} + +////////////////////////////////////////////////////////////////////////// +AnsiString PathUtil::getFileName(const AnsiString &path) { + AnsiString newPath = unifySeparators(path); + Common::String lastPart = Common::lastPathComponent(newPath, '/'); + if (lastPart[lastPart.size() - 1 ] != '/') { + return lastPart; + } else { + return path; + } +} + +////////////////////////////////////////////////////////////////////////// +AnsiString PathUtil::getFileNameWithoutExtension(const AnsiString &path) { + AnsiString fileName = getFileName(path); + // TODO: Prettify this. + AnsiString extension = Common::lastPathComponent(fileName, '.'); + for (uint32 i = 0; i < extension.size() + 1; i++) { + fileName.deleteLastChar(); + } + return fileName; +} + +////////////////////////////////////////////////////////////////////////// +AnsiString PathUtil::getExtension(const AnsiString &path) { + AnsiString fileName = getFileName(path); + return Common::lastPathComponent(path, '.'); +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/utils/path_util.h b/engines/wintermute/utils/path_util.h new file mode 100644 index 0000000000..7358c2aba0 --- /dev/null +++ b/engines/wintermute/utils/path_util.h @@ -0,0 +1,49 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_PATHUTILS_H +#define WINTERMUTE_PATHUTILS_H + +#include "engines/wintermute/dctypes.h" + +namespace Wintermute { + +class PathUtil { +public: + static AnsiString unifySeparators(const AnsiString &path); + static AnsiString normalizeFileName(const AnsiString &path); + static AnsiString combine(const AnsiString &path1, const AnsiString &path2); + static AnsiString getDirectoryName(const AnsiString &path); + static AnsiString getFileName(const AnsiString &path); + static AnsiString getFileNameWithoutExtension(const AnsiString &path); + static AnsiString getExtension(const AnsiString &path); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/utils/string_util.cpp b/engines/wintermute/utils/string_util.cpp new file mode 100644 index 0000000000..a1053bbef6 --- /dev/null +++ b/engines/wintermute/utils/string_util.cpp @@ -0,0 +1,234 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "common/tokenizer.h" +#include "engines/wintermute/utils/string_util.h" +#include "engines/wintermute/utils/convert_utf.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////////// +bool StringUtil::compareNoCase(const AnsiString &str1, const AnsiString &str2) { + return (str1.compareToIgnoreCase(str2) == 0); +} + +////////////////////////////////////////////////////////////////////////// +/*bool StringUtil::CompareNoCase(const WideString &str1, const WideString &str2) { + WideString str1lc = str1; + WideString str2lc = str2; + + ToLowerCase(str1lc); + ToLowerCase(str2lc); + + return (str1lc == str2lc); +}*/ + +////////////////////////////////////////////////////////////////////////// +WideString StringUtil::utf8ToWide(const Utf8String &Utf8Str) { + error("StringUtil::Utf8ToWide - WideString not supported yet"); + /* size_t WideSize = Utf8Str.size(); + + if (sizeof(wchar_t) == 2) { + wchar_t *WideStringNative = new wchar_t[WideSize + 1]; + + const UTF8 *SourceStart = reinterpret_cast<const UTF8 *>(Utf8Str.c_str()); + const UTF8 *SourceEnd = SourceStart + WideSize; + + UTF16 *TargetStart = reinterpret_cast<UTF16 *>(WideStringNative); + UTF16 *TargetEnd = TargetStart + WideSize + 1; + + ConversionResult res = ConvertUTF8toUTF16(&SourceStart, SourceEnd, &TargetStart, TargetEnd, strictConversion); + if (res != conversionOK) { + delete[] WideStringNative; + return L""; + } + *TargetStart = 0; + WideString ResultString(WideStringNative); + delete[] WideStringNative; + + return ResultString; + } else if (sizeof(wchar_t) == 4) { + wchar_t *WideStringNative = new wchar_t[WideSize + 1]; + + const UTF8 *SourceStart = reinterpret_cast<const UTF8 *>(Utf8Str.c_str()); + const UTF8 *SourceEnd = SourceStart + WideSize; + + UTF32 *TargetStart = reinterpret_cast<UTF32 *>(WideStringNative); + UTF32 *TargetEnd = TargetStart + WideSize; + + ConversionResult res = ConvertUTF8toUTF32(&SourceStart, SourceEnd, &TargetStart, TargetEnd, strictConversion); + if (res != conversionOK) { + delete[] WideStringNative; + return L""; + } + *TargetStart = 0; + WideString ResultString(WideStringNative); + delete[] WideStringNative; + + return ResultString; + } else { + return L""; + }*/ + return ""; +} + +////////////////////////////////////////////////////////////////////////// +Utf8String StringUtil::wideToUtf8(const WideString &WideStr) { + error("StringUtil::wideToUtf8 - Widestring not supported yet"); + /* size_t WideSize = WideStr.length(); + + if (sizeof(wchar_t) == 2) { + size_t utf8Size = 3 * WideSize + 1; + char *utf8StringNative = new char[Utf8Size]; + + const UTF16 *SourceStart = reinterpret_cast<const UTF16 *>(WideStr.c_str()); + const UTF16 *SourceEnd = SourceStart + WideSize; + + UTF8 *TargetStart = reinterpret_cast<UTF8 *>(Utf8StringNative); + UTF8 *TargetEnd = TargetStart + Utf8Size; + + ConversionResult res = ConvertUTF16toUTF8(&SourceStart, SourceEnd, &TargetStart, TargetEnd, strictConversion); + if (res != conversionOK) { + delete[] Utf8StringNative; + return (Utf8String)""; + } + *TargetStart = 0; + Utf8String ResultString(Utf8StringNative); + delete[] Utf8StringNative; + return ResultString; + } else if (sizeof(wchar_t) == 4) { + size_t utf8Size = 4 * WideSize + 1; + char *utf8StringNative = new char[Utf8Size]; + + const UTF32 *SourceStart = reinterpret_cast<const UTF32 *>(WideStr.c_str()); + const UTF32 *SourceEnd = SourceStart + WideSize; + + UTF8 *TargetStart = reinterpret_cast<UTF8 *>(Utf8StringNative); + UTF8 *TargetEnd = TargetStart + Utf8Size; + + ConversionResult res = ConvertUTF32toUTF8(&SourceStart, SourceEnd, &TargetStart, TargetEnd, strictConversion); + if (res != conversionOK) { + delete[] Utf8StringNative; + return (Utf8String)""; + } + *TargetStart = 0; + Utf8String ResultString(Utf8StringNative); + delete[] Utf8StringNative; + return ResultString; + } else { + return (Utf8String)""; + }*/ + return ""; +} + +// Currently this only does Ansi->ISO 8859, and only for carets. +char simpleAnsiToWide(const AnsiString &str, uint32 &offset) { + byte c = str[offset]; + + if (c == 146) { + offset++; + return 39; // Replace right-quote with apostrophe + } else { + offset++; + return c; + } +} + +////////////////////////////////////////////////////////////////////////// +WideString StringUtil::ansiToWide(const AnsiString &str) { + // TODO: This function gets called a lot, so warnings like these drown out the usefull information + Common::String converted = ""; + uint32 index = 0; + while (index != str.size()) { + converted += simpleAnsiToWide(str, index); + } + // using default os locale! + + /* setlocale(LC_CTYPE, ""); + size_t wideSize = mbstowcs(NULL, str.c_str(), 0) + 1; + wchar_t *wstr = new wchar_t[WideSize]; + mbstowcs(wstr, str.c_str(), WideSize); + WideString ResultString(wstr); + delete[] wstr; + return ResultString;*/ + return WideString(converted); +} + +////////////////////////////////////////////////////////////////////////// +AnsiString StringUtil::wideToAnsi(const WideString &wstr) { + // using default os locale! + // TODO: This function gets called a lot, so warnings like these drown out the usefull information + /* setlocale(LC_CTYPE, ""); + size_t wideSize = wcstombs(NULL, wstr.c_str(), 0) + 1; + char *str = new char[WideSize]; + wcstombs(str, wstr.c_str(), WideSize); + AnsiString ResultString(str); + delete[] str; + return ResultString;*/ + return AnsiString(wstr); +} + +////////////////////////////////////////////////////////////////////////// +bool StringUtil::isUtf8BOM(const byte *buffer, uint32 bufferSize) { + if (bufferSize > 3 && buffer[0] == 0xEF && buffer[1] == 0xBB && buffer[2] == 0xBF) { + return true; + } else { + return false; + } +} + +////////////////////////////////////////////////////////////////////////// +int StringUtil::indexOf(const WideString &str, const WideString &toFind, size_t startFrom) { + const char *index = strstr(str.c_str(), toFind.c_str()); + if (index == NULL) { + return -1; + } else { + return index - str.c_str(); + } +} + +Common::String StringUtil::encodeSetting(const Common::String &str) { + for (uint32 i = 0; i < str.size(); i++) { + if ((str[i] < 33) || (str[i] == '=') || (str[i] > 126)) { + error("Setting contains illegal characters: %s", str.c_str()); + } + } + return str; +} + +Common::String StringUtil::decodeSetting(const Common::String &str) { + return str; +} + +////////////////////////////////////////////////////////////////////////// +AnsiString StringUtil::toString(int val) { + return Common::String::format("%d", val); +} + + +} // end of namespace Wintermute diff --git a/engines/wintermute/utils/string_util.h b/engines/wintermute/utils/string_util.h new file mode 100644 index 0000000000..e419e2bca8 --- /dev/null +++ b/engines/wintermute/utils/string_util.h @@ -0,0 +1,56 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_STRINGUTIL_H +#define WINTERMUTE_STRINGUTIL_H + +#include "engines/wintermute/dctypes.h" + +namespace Wintermute { + +class StringUtil { +public: + static bool compareNoCase(const AnsiString &str1, const AnsiString &str2); + //static bool compareNoCase(const WideString &str1, const WideString &str2); + static WideString utf8ToWide(const Utf8String &Utf8Str); + static Utf8String wideToUtf8(const WideString &WideStr); + static WideString ansiToWide(const AnsiString &str); + static AnsiString wideToAnsi(const WideString &str); + + static bool isUtf8BOM(const byte *buffer, uint32 bufferSize); + static int indexOf(const WideString &str, const WideString &toFind, size_t startFrom); + + static Common::String encodeSetting(const Common::String &str); + static Common::String decodeSetting(const Common::String &str); + + static AnsiString toString(int val); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/utils/utils.cpp b/engines/wintermute/utils/utils.cpp new file mode 100644 index 0000000000..824b16ccdb --- /dev/null +++ b/engines/wintermute/utils/utils.cpp @@ -0,0 +1,261 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/utils/utils.h" +#include "engines/wintermute/wintermute.h" +#include "engines/wintermute/base/base_engine.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////// +static inline unsigned Sqr(int x) { + return (x * x); +} + +////////////////////////////////////////////////////////////////////////////////// +// Swap - swaps two integers +////////////////////////////////////////////////////////////////////////////////// +void BaseUtils::swap(int *a, int *b) { + int temp = *a; + *a = *b; + *b = temp; +} + + +////////////////////////////////////////////////////////////////////////// +float BaseUtils::normalizeAngle(float angle) { + while (angle > 360) { + angle -= 360; + } + while (angle < 0) { + angle += 360; + } + + return angle; +} + + +//////////////////////////////////////////////////////////////////////////////// +void BaseUtils::createPath(const char *path, bool pathOnly) { + /* AnsiString pathStr; + + if (!pathOnly) pathStr = PathUtil::getDirectoryName(path); + else pathStr = path; + */ +// try { + warning("BaseUtils::CreatePath - not implemented: %s", path); +// boost::filesystem::create_directories(path); +// } catch (...) { + return; +// } +} + + +////////////////////////////////////////////////////////////////////////// +void BaseUtils::debugMessage(const char *text) { + //MessageBox(hWnd, Text, "WME", MB_OK|MB_ICONINFORMATION); +} + + +////////////////////////////////////////////////////////////////////////// +char *BaseUtils::setString(char **string, const char *value) { + delete[] *string; + *string = new char[strlen(value) + 1]; + if (*string) { + strcpy(*string, value); + } + return *string; +} + +////////////////////////////////////////////////////////////////////////// +char *BaseUtils::strEntry(int entry, const char *str, const char delim) { + int numEntries = 0; + + const char *start = NULL; + int len = 0; + + for (uint32 i = 0; i <= strlen(str); i++) { + if (numEntries == entry) { + if (!start) { + start = str + i; + } else { + len++; + } + } + if (str[i] == delim || str[i] == '\0') { + numEntries++; + if (start) { + char *ret = new char[len + 1]; + memset(ret, 0, len + 1); + Common::strlcpy(ret, start, len + 1); + return ret; + } + } + } + return NULL; +} + +////////////////////////////////////////////////////////////////////////// +int BaseUtils::randomInt(int from, int to) { + if (to < from) { + int i = to; + to = from; + from = i; + } + return BaseEngine::instance().randInt(from, to); +// return (rand() % (to - from + 1)) + from; +} + +////////////////////////////////////////////////////////////////////////// +float BaseUtils::randomFloat(float from, float to) { + const uint32 randMax = RAND_MAX; + float randNum = (float)BaseEngine::instance().randInt(0, randMax) / (float)randMax; + return from + (to - from) * randNum; +} + +////////////////////////////////////////////////////////////////////////// +float BaseUtils::randomAngle(float from, float to) { + while (to < from) { + to += 360; + } + return normalizeAngle(randomFloat(from, to)); +} + +////////////////////////////////////////////////////////////////////////// +void BaseUtils::RGBtoHSL(uint32 rgbColor, byte *outH, byte *outS, byte *outL) { + float varR = (RGBCOLGetR(rgbColor) / 255.0f); + float varG = (RGBCOLGetG(rgbColor) / 255.0f); + float varB = (RGBCOLGetB(rgbColor) / 255.0f); + + //Min. value of RGB + float varMin = MIN(varR, varG); + varMin = MIN(varMin, varB); + + //Max. value of RGB + float varMax = MAX(varR, varG); + varMax = MAX(varMax, varB); + + //Delta RGB value + float delMax = varMax - varMin; + + float H = 0.0f, S = 0.0f, L = 0.0f; + + L = (varMax + varMin) / 2.0f; + + //This is a gray, no chroma... + if (delMax == 0) { + H = 0; + S = 0; + } + //Chromatic data... + else { + if (L < 0.5f) { + S = delMax / (varMax + varMin); + } else { + S = delMax / (2.0f - varMax - varMin); + } + + float delR = (((varMax - varR) / 6.0f) + (delMax / 2.0f)) / delMax; + float delG = (((varMax - varG) / 6.0f) + (delMax / 2.0f)) / delMax; + float delB = (((varMax - varB) / 6.0f) + (delMax / 2.0f)) / delMax; + + if (varR == varMax) { + H = delB - delG; + } else if (varG == varMax) { + H = (1.0f / 3.0f) + delR - delB; + } else if (varB == varMax) { + H = (2.0f / 3.0f) + delG - delR; + } + + if (H < 0) { + H += 1; + } + if (H > 1) { + H -= 1; + } + } + + *outH = (byte)(H * 255); + *outS = (byte)(S * 255); + *outL = (byte)(L * 255); +} + + +////////////////////////////////////////////////////////////////////////// +uint32 BaseUtils::HSLtoRGB(byte InH, byte InS, byte InL) { + float H = InH / 255.0f; + float S = InS / 255.0f; + float L = InL / 255.0f; + + byte R, G, B; + + + if (S == 0) { + R = (byte)(L * 255); + G = (byte)(L * 255); + B = (byte)(L * 255); + } else { + float var1, var2; + + if (L < 0.5) { + var2 = L * (1.0 + S); + } else { + var2 = (L + S) - (S * L); + } + + var1 = 2.0f * L - var2; + + R = (byte)(255 * Hue2RGB(var1, var2, H + (1.0f / 3.0f))); + G = (byte)(255 * Hue2RGB(var1, var2, H)); + B = (byte)(255 * Hue2RGB(var1, var2, H - (1.0f / 3.0f))); + } + return BYTETORGBA(255, R, G, B); +} + + +////////////////////////////////////////////////////////////////////////// +float BaseUtils::Hue2RGB(float v1, float v2, float vH) { + if (vH < 0.0f) { + vH += 1.0f; + } + if (vH > 1.0f) { + vH -= 1.0f; + } + if ((6.0f * vH) < 1.0f) { + return (v1 + (v2 - v1) * 6.0f * vH); + } + if ((2.0f * vH) < 1.0f) { + return (v2); + } + if ((3.0f * vH) < 2.0f) { + return (v1 + (v2 - v1) * ((2.0f / 3.0f) - vH) * 6.0f); + } + return (v1); +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/utils/utils.h b/engines/wintermute/utils/utils.h new file mode 100644 index 0000000000..d6a603ec72 --- /dev/null +++ b/engines/wintermute/utils/utils.h @@ -0,0 +1,64 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_UTILS_H +#define WINTERMUTE_UTILS_H + +#include "engines/wintermute/wintypes.h" +#include "engines/wintermute/math/rect32.h" + +namespace Wintermute { + +class BaseGame; + +class BaseUtils { +public: + static void swap(int *a, int *b); + static float normalizeAngle(float angle); + + static void createPath(const char *path, bool pathOnly = false); + + static void debugMessage(const char *text); + static char *setString(char **string, const char *value); + + static char *strEntry(int entry, const char *str, const char delim = ','); + + static int randomInt(int from, int to); + static float randomFloat(float from, float to); + static float randomAngle(float from, float to); + + static void RGBtoHSL(uint32 rgbColor, byte *outH, byte *outS, byte *outL); + static uint32 HSLtoRGB(byte H, byte S, byte L); + +private: + static float Hue2RGB(float v1, float v2, float vH); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/video/video_player.cpp b/engines/wintermute/video/video_player.cpp new file mode 100644 index 0000000000..2577b8aedc --- /dev/null +++ b/engines/wintermute/video/video_player.cpp @@ -0,0 +1,109 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + + +#include "engines/wintermute/video/video_player.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////// +VideoPlayer::VideoPlayer(BaseGame *inGame) : BaseClass(inGame) { + setDefaults(); +} + +////////////////////////////////////////////////////////////////////////// +bool VideoPlayer::setDefaults() { + _playing = false; + _videoEndTime = 0; + _soundAvailable = false; + _startTime = 0; + _totalVideoTime = 0; + _playPosX = _playPosY = 0; + _playZoom = 0.0f; + + _filename = NULL; + + _slowRendering = false; + + _currentSubtitle = 0; + _showSubtitle = false; + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +VideoPlayer::~VideoPlayer() { + cleanup(); +} + +////////////////////////////////////////////////////////////////////////// +bool VideoPlayer::cleanup() { + return 0; +} + +////////////////////////////////////////////////////////////////////////// +bool VideoPlayer::initialize(const char *inFilename, const char *subtitleFile) { + warning("VideoPlayer: %s %s - Not implemented yet", inFilename, subtitleFile); + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool VideoPlayer::update() { + return 0; +} + +////////////////////////////////////////////////////////////////////////// +bool VideoPlayer::display() { + return 0; +} + +////////////////////////////////////////////////////////////////////////// +bool VideoPlayer::play(TVideoPlayback type, int x, int y, bool freezeMusic) { + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool VideoPlayer::stop() { + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool VideoPlayer::isPlaying() { + return _playing; +} + +////////////////////////////////////////////////////////////////////////// +bool VideoPlayer::loadSubtitles(const char *filename, const char *subtitleFile) { + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/video/video_player.h b/engines/wintermute/video/video_player.h new file mode 100644 index 0000000000..d5466da679 --- /dev/null +++ b/engines/wintermute/video/video_player.h @@ -0,0 +1,90 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_VIDPLAYER_H +#define WINTERMUTE_VIDPLAYER_H + +#include "engines/wintermute/dctypes.h" // Added by ClassView +#include "engines/wintermute/base/base.h" + +#define MAX_AUDIO_STREAMS 5 +#define MAX_VIDEO_STREAMS 5 + + +namespace Wintermute { + +// AVI-Video-player, currently fully stubbed +class VideoPlayer : public BaseClass { +public: + bool _showSubtitle; + int _currentSubtitle; + bool loadSubtitles(const char *filename, const char *subtitleFile); + bool _slowRendering; + bool isPlaying(); + char *_filename; + bool stop(); + bool play(TVideoPlayback Type = VID_PLAY_CENTER, int x = 0, int y = 0, bool freezeMusic = true); + uint32 _totalVideoTime; + uint32 _startTime; + //CVidRenderer *_vidRenderer; + //BaseSoundAVI *_sound; + bool _soundAvailable; + bool setDefaults(); + bool _playing; + bool display(); + bool update(); + bool initialize(const char *inFilename, const char *subtitleFile = NULL); + bool cleanup(); + VideoPlayer(BaseGame *inGame); + virtual ~VideoPlayer(); + + /*PAVIFILE _aviFile; + + LONG _lastSample; + + PAVISTREAM _audioStream; + PAVISTREAM _videoStream; + + LPWAVEFORMAT _audioFormat; + + LPBITMAPINFO _videoFormat; + PGETFRAME _videoPGF;*/ + uint32 _videoEndTime; + + int _playPosX; + int _playPosY; + float _playZoom; + + /* LPBITMAPV4HEADER _targetFormat; + + BaseArray<CVidSubtitle *, CVidSubtitle *> _subtitles;*/ +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/video/video_theora_player.cpp b/engines/wintermute/video/video_theora_player.cpp new file mode 100644 index 0000000000..d14c807e11 --- /dev/null +++ b/engines/wintermute/video/video_theora_player.cpp @@ -0,0 +1,532 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + + +#include "engines/wintermute/video/video_theora_player.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/gfx/osystem/base_surface_osystem.h" +#include "engines/wintermute/base/gfx/base_image.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/base/sound/base_sound_manager.h" +#include "engines/wintermute/platform_osystem.h" +#include "video/theora_decoder.h" +#include "engines/wintermute/wintermute.h" +#include "common/system.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(VideoTheoraPlayer, false) + +////////////////////////////////////////////////////////////////////////// +VideoTheoraPlayer::VideoTheoraPlayer(BaseGame *inGame) : BaseClass(inGame) { + SetDefaults(); +} + +////////////////////////////////////////////////////////////////////////// +void VideoTheoraPlayer::SetDefaults() { + + _file = NULL; + _filename = ""; + _startTime = 0; + _looping = false; + + _freezeGame = false; + _currentTime = 0; + + _state = THEORA_STATE_NONE; + + _videoFrameReady = false; + _audioFrameReady = false; + _videobufTime = 0; + + _playbackStarted = false; + _dontDropFrames = false; + + _texture = NULL; + _alphaImage = NULL; + _alphaFilename = ""; + + _frameRendered = false; + + _seekingKeyframe = false; + _timeOffset = 0.0f; + + _posX = _posY = 0; + _playbackType = VID_PLAY_CENTER; + _playZoom = 0.0f; + + _savedState = THEORA_STATE_NONE; + _savedPos = 0; + _volume = 100; + _theoraDecoder = NULL; + + // TODO: Add subtitles-support + //_subtitler = NULL; +} + +////////////////////////////////////////////////////////////////////////// +VideoTheoraPlayer::~VideoTheoraPlayer(void) { + cleanup(); +// SAFE_DELETE(_subtitler); +} + +////////////////////////////////////////////////////////////////////////// +void VideoTheoraPlayer::cleanup() { + if (_file) { + BaseFileManager::getEngineInstance()->closeFile(_file); + _file = NULL; + } + + _surface.free(); + if (_theoraDecoder) { + _theoraDecoder->close(); + } + delete _theoraDecoder; + _theoraDecoder = NULL; + delete _alphaImage; + _alphaImage = NULL; + delete _texture; + _texture = NULL; +} + +////////////////////////////////////////////////////////////////////////// +bool VideoTheoraPlayer::initialize(const Common::String &filename, const Common::String &subtitleFile) { + cleanup(); + + _filename = filename; + _file = BaseFileManager::getEngineInstance()->openFile(filename, true, false); + if (!_file) { + return STATUS_FAILED; + } + +#if defined (USE_THEORADEC) + _theoraDecoder = new Video::TheoraDecoder(); +#else + return STATUS_FAILED; +#endif + _theoraDecoder->loadStream(_file); + + if (!_theoraDecoder->isVideoLoaded()) { + return STATUS_FAILED; + } + + _state = THEORA_STATE_PAUSED; + + // Additional setup. + _surface.create(_theoraDecoder->getWidth(), _theoraDecoder->getHeight(), _theoraDecoder->getPixelFormat()); + _texture = new BaseSurfaceOSystem(_gameRef); + _texture->create(_theoraDecoder->getWidth(), _theoraDecoder->getHeight()); + _state = THEORA_STATE_PLAYING; + _playZoom = 100; + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool VideoTheoraPlayer::resetStream() { + warning("VidTheoraPlayer::resetStream - hacked"); + // HACK: Just reopen the same file again. + if (_theoraDecoder) { + _theoraDecoder->close(); + } + delete _theoraDecoder; + _theoraDecoder = NULL; + + _file = BaseFileManager::getEngineInstance()->openFile(_filename, true, false); + if (!_file) { + return STATUS_FAILED; + } + +#if defined (USE_THEORADEC) + _theoraDecoder = new Video::TheoraDecoder(); +#else + return STATUS_FAILED; +#endif + _theoraDecoder->loadStream(_file); + + if (!_theoraDecoder->isVideoLoaded()) { + return STATUS_FAILED; + } + + return play(_playbackType, _posX, _posY, false, false, _looping, 0, _playZoom); + // End of hack. +#if 0 // Stubbed for now, as theora isn't seekable + if (_sound) { + _sound->Stop(); + } + + m_TimeOffset = 0.0f; + Initialize(m_Filename); + Play(m_PlaybackType, m_PosX, m_PosY, false, false, m_Looping, 0, m_PlayZoom); +#endif + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool VideoTheoraPlayer::play(TVideoPlayback type, int x, int y, bool freezeGame, bool freezeMusic, bool looping, uint32 startTime, float forceZoom, int volume) { + if (forceZoom < 0.0f) { + forceZoom = 100.0f; + } + if (volume < 0) { + _volume = _gameRef->_soundMgr->getVolumePercent(Audio::Mixer::kSFXSoundType); + } else { + _volume = volume; + } + + _freezeGame = freezeGame; + + if (!_playbackStarted && _freezeGame) { + _gameRef->freeze(freezeMusic); + } + + _playbackStarted = false; + float width, height; + if (_theoraDecoder) { + _surface.free(); + _surface.copyFrom(*_theoraDecoder->decodeNextFrame()); + _state = THEORA_STATE_PLAYING; + _looping = looping; + _playbackType = type; + + _startTime = startTime; + _volume = volume; + _posX = x; + _posY = y; + _playZoom = forceZoom; + + width = (float)_theoraDecoder->getWidth(); + height = (float)_theoraDecoder->getHeight(); + } else { + width = (float)_gameRef->_renderer->_width; + height = (float)_gameRef->_renderer->_height; + } + + switch (type) { + case VID_PLAY_POS: + _playZoom = forceZoom; + _posX = x; + _posY = y; + break; + + case VID_PLAY_STRETCH: { + float zoomX = (float)((float)_gameRef->_renderer->_width / width * 100); + float zoomY = (float)((float)_gameRef->_renderer->_height / height * 100); + _playZoom = MIN(zoomX, zoomY); + _posX = (int)((_gameRef->_renderer->_width - width * (_playZoom / 100)) / 2); + _posY = (int)((_gameRef->_renderer->_height - height * (_playZoom / 100)) / 2); + } + break; + + case VID_PLAY_CENTER: + _playZoom = 100.0f; + _posX = (int)((_gameRef->_renderer->_width - width) / 2); + _posY = (int)((_gameRef->_renderer->_height - height) / 2); + break; + } + _theoraDecoder->start(); + + return STATUS_OK; +#if 0 // Stubbed for now as theora isn't seekable + if (StartTime) SeekToTime(StartTime); + + Update(); +#endif + return STATUS_FAILED; +} + +////////////////////////////////////////////////////////////////////////// +bool VideoTheoraPlayer::stop() { + _theoraDecoder->close(); + _state = THEORA_STATE_FINISHED; + if (_freezeGame) { + _gameRef->unfreeze(); + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool VideoTheoraPlayer::update() { + _currentTime = _freezeGame ? _gameRef->_liveTimer : _gameRef->_timer; + + if (!isPlaying()) { + return STATUS_OK; + } + + if (_playbackStarted /*&& m_Sound && !m_Sound->IsPlaying()*/) { + return STATUS_OK; + } + + if (_playbackStarted && !_freezeGame && _gameRef->_state == GAME_FROZEN) { + return STATUS_OK; + } + + if (_theoraDecoder) { + if (_theoraDecoder->endOfVideo() && _looping) { + warning("Should loop movie %s, hacked for now", _filename.c_str()); + _theoraDecoder->rewind(); + //HACK: Just reinitialize the same video again: + return resetStream(); + } else if (_theoraDecoder->endOfVideo() && !_looping) { + debugC(kWintermuteDebugLog, "Finished movie %s", _filename.c_str()); + _state = THEORA_STATE_FINISHED; + _playbackStarted = false; + if (_freezeGame) { + _gameRef->unfreeze(); + } + } + if (_state == THEORA_STATE_PLAYING) { + if (!_theoraDecoder->endOfVideo() && _theoraDecoder->getTimeToNextFrame() == 0) { + const Graphics::Surface *decodedFrame = _theoraDecoder->decodeNextFrame(); + if (decodedFrame) { + _surface.free(); + _surface.copyFrom(*decodedFrame); + if (_texture) { + writeVideo(); + } + } + } + return STATUS_OK; + } + } + // Skip the busy-loop? + if ((!_texture || !_videoFrameReady) && !_theoraDecoder->endOfVideo()) { + // end playback + if (!_looping) { + _state = THEORA_STATE_FINISHED; + if (_freezeGame) { + _gameRef->unfreeze(); + } + return STATUS_OK; + } else { + resetStream(); + return STATUS_OK; + } + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +uint32 VideoTheoraPlayer::getMovieTime() { + if (!_playbackStarted) { + return 0; + } else { + return _theoraDecoder->getTime(); + } +} + +////////////////////////////////////////////////////////////////////////// +bool VideoTheoraPlayer::writeVideo() { + if (!_texture) { + return STATUS_FAILED; + } + + _texture->startPixelOp(); + + writeAlpha(); + if (_alphaImage) { + _texture->putSurface(_surface, true); + } else { + _texture->putSurface(_surface, false); + } + + //RenderFrame(_texture, &yuv); + + _texture->endPixelOp(); + _videoFrameReady = true; + return STATUS_OK; +} + +void VideoTheoraPlayer::writeAlpha() { + if (_alphaImage && _surface.w == _alphaImage->getSurface()->w && _surface.h == _alphaImage->getSurface()->h) { + assert(_alphaImage->getSurface()->format.bytesPerPixel == 4); + assert(_surface.format.bytesPerPixel == 4); + const byte *alphaData = (const byte *)_alphaImage->getSurface()->getBasePtr(0, 0); +#ifdef SCUMM_LITTLE_ENDIAN + int alphaPlace = (_alphaImage->getSurface()->format.aShift / 8); +#else + int alphaPlace = 3 - (_alphaImage->getSurface()->format.aShift / 8); +#endif + alphaData += alphaPlace; + byte *imgData = (byte *)_surface.getBasePtr(0, 0); +#ifdef SCUMM_LITTLE_ENDIAN + imgData += (_surface.format.aShift / 8); +#else + imgData += 3 - (_surface.format.aShift / 8); +#endif + for (int i = 0; i < _surface.w * _surface.h; i++) { + *imgData = *alphaData; + alphaData += 4; + imgData += 4; + } + } +} + +////////////////////////////////////////////////////////////////////////// +bool VideoTheoraPlayer::display(uint32 alpha) { + Rect32 rc; + bool res; + + if (_texture && _videoFrameReady) { + BasePlatform::setRect(&rc, 0, 0, _texture->getWidth(), _texture->getHeight()); + if (_playZoom == 100.0f) { + res = _texture->displayTrans(_posX, _posY, rc, alpha); + } else { + res = _texture->displayTransZoom(_posX, _posY, rc, _playZoom, _playZoom, alpha); + } + } else { + res = STATUS_FAILED; + } + // TODO: Add subtitles-support +/* if (m_Subtitler && _gameRef->m_VideoSubtitles) { + m_Subtitler->display(); + }*/ + + return res; +} + +////////////////////////////////////////////////////////////////////////// +bool VideoTheoraPlayer::setAlphaImage(const Common::String &filename) { + delete _alphaImage; + _alphaImage = new BaseImage(); + if (!_alphaImage || DID_FAIL(_alphaImage->loadFile(filename))) { + delete _alphaImage; + _alphaImage = NULL; + _alphaFilename = ""; + return STATUS_FAILED; + } + + if (_alphaFilename != filename) { + _alphaFilename = filename; + } + //TODO: Conversion. + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +byte VideoTheoraPlayer::getAlphaAt(int x, int y) { + if (_alphaImage) { + return _alphaImage->getAlphaAt(x, y); + } else { + return 0xFF; + } +} + + +////////////////////////////////////////////////////////////////////////// +inline int intlog(int num) { + int r = 0; + while (num > 0) { + num = num / 2; + r = r + 1; + } + + return r; +} + +////////////////////////////////////////////////////////////////////////// +bool VideoTheoraPlayer::seekToTime(uint32 time) { + warning("VideoTheoraPlayer::SeekToTime(%d) - not supported", time); + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool VideoTheoraPlayer::pause() { + if (_state == THEORA_STATE_PLAYING) { + _state = THEORA_STATE_PAUSED; + _theoraDecoder->pauseVideo(true); + return STATUS_OK; + } else { + return STATUS_FAILED; + } +} + +////////////////////////////////////////////////////////////////////////// +bool VideoTheoraPlayer::resume() { + if (_state == THEORA_STATE_PAUSED) { + _state = THEORA_STATE_PLAYING; + _theoraDecoder->pauseVideo(false); + return STATUS_OK; + } else { + return STATUS_FAILED; + } +} + +////////////////////////////////////////////////////////////////////////// +bool VideoTheoraPlayer::persist(BasePersistenceManager *persistMgr) { + //BaseClass::persist(persistMgr); + + if (persistMgr->getIsSaving()) { + _savedPos = getMovieTime() * 1000; + _savedState = _state; + } else { + SetDefaults(); + } + + persistMgr->transfer(TMEMBER(_gameRef)); + persistMgr->transfer(TMEMBER(_savedPos)); + persistMgr->transfer(TMEMBER(_savedState)); + persistMgr->transfer(TMEMBER(_filename)); + persistMgr->transfer(TMEMBER(_alphaFilename)); + persistMgr->transfer(TMEMBER(_posX)); + persistMgr->transfer(TMEMBER(_posY)); + persistMgr->transfer(TMEMBER(_playZoom)); + persistMgr->transfer(TMEMBER_INT(_playbackType)); + persistMgr->transfer(TMEMBER(_looping)); + persistMgr->transfer(TMEMBER(_volume)); + + if (!persistMgr->getIsSaving() && (_savedState != THEORA_STATE_NONE)) { + initializeSimple(); + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool VideoTheoraPlayer::initializeSimple() { + if (DID_SUCCEED(initialize(_filename))) { + if (_alphaFilename != "") { + setAlphaImage(_alphaFilename); + } + play(_playbackType, _posX, _posY, false, false, _looping, _savedPos, _playZoom); + } else { + _state = THEORA_STATE_FINISHED; + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +BaseSurface *VideoTheoraPlayer::getTexture() { + return _texture; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/video/video_theora_player.h b/engines/wintermute/video/video_theora_player.h new file mode 100644 index 0000000000..593c1b9666 --- /dev/null +++ b/engines/wintermute/video/video_theora_player.h @@ -0,0 +1,148 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_VIDTHEORAPLAYER_H +#define WINTERMUTE_VIDTHEORAPLAYER_H + +#include "engines/wintermute/base/base.h" +#include "engines/wintermute/persistent.h" +#include "video/video_decoder.h" +#include "common/stream.h" +#include "graphics/surface.h" + +namespace Wintermute { +class BaseSurface; +class BaseImage; +class VideoTheoraPlayer : public BaseClass { +private: + enum { + THEORA_STATE_NONE = 0, + THEORA_STATE_PLAYING = 1, + THEORA_STATE_PAUSED = 2, + THEORA_STATE_FINISHED = 3 + }; + Video::VideoDecoder *_theoraDecoder; + Graphics::Surface _surface; +public: + DECLARE_PERSISTENT(VideoTheoraPlayer, BaseClass) + + VideoTheoraPlayer(BaseGame *inGame); + virtual ~VideoTheoraPlayer(void); + + // external objects + Common::SeekableReadStream *_file; + Common::String _filename; + + BaseSurface *_texture; + //CVidSubtitler *_subtitler; + + // control methods + bool initialize(const Common::String &filename, const Common::String &subtitleFile = NULL); + bool initializeSimple(); + bool update(); + bool play(TVideoPlayback type = VID_PLAY_CENTER, int x = 0, int y = 0, bool freezeGame = false, bool freezeMusic = true, bool looping = false, uint32 startTime = 0, float forceZoom = -1.0f, int volume = -1); + bool stop(); + bool display(uint32 alpha = 0xFFFFFFFF); + + bool pause(); + bool resume(); + + bool isPlaying() { + return _state == THEORA_STATE_PLAYING; + }; + bool isFinished() { + return _state == THEORA_STATE_FINISHED; + }; + bool isPaused() { + return _state == THEORA_STATE_PAUSED; + }; + + uint32 getMovieTime(); + + BaseSurface *getTexture(); + + int _state; + uint32 _startTime; + + int _savedState; + uint32 _savedPos; + + + // alpha related + BaseImage *_alphaImage; + Common::String _alphaFilename; + bool setAlphaImage(const Common::String &filename); + __inline byte getAlphaAt(int x, int y); + void writeAlpha(); + + bool seekToTime(uint32 Time); + + + void cleanup(); + bool resetStream(); + + // video properties + TVideoPlayback _playbackType; + int _posX; + int _posY; + float _playZoom; + int _volume; + + bool _looping; + bool _dontDropFrames; + bool _freezeGame; + uint32 _currentTime; + + +private: + // seeking support + bool _seekingKeyframe; + float _timeOffset; + + bool _frameRendered; + + bool getIsFrameReady() { + return _videoFrameReady; + } +private: + bool _audioFrameReady; + bool _videoFrameReady; + float _videobufTime; + + bool writeVideo(); + + bool _playbackStarted; + + // helpers + void SetDefaults(); + +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/wintermute.cpp b/engines/wintermute/wintermute.cpp new file mode 100644 index 0000000000..4d3adc95b2 --- /dev/null +++ b/engines/wintermute/wintermute.cpp @@ -0,0 +1,397 @@ +/* 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 "common/scummsys.h" + +#include "common/config-manager.h" +#include "common/debug.h" +#include "common/debug-channels.h" +#include "common/error.h" +#include "common/EventRecorder.h" +#include "common/file.h" +#include "common/fs.h" +#include "common/tokenizer.h" + +#include "engines/util.h" +#include "engines/wintermute/ad/ad_game.h" +#include "engines/wintermute/wintermute.h" +#include "engines/wintermute/debugger.h" +#include "engines/wintermute/platform_osystem.h" +#include "engines/wintermute/base/base_engine.h" + +#include "engines/wintermute/base/sound/base_sound_manager.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/base/scriptables/script_engine.h" + +namespace Wintermute { + +// Simple constructor for detection - we need to setup the persistence to avoid special-casing in-engine +// This might not be the prettiest solution +WintermuteEngine::WintermuteEngine() : Engine(g_system) { + _game = new AdGame(""); + _debugger = NULL; + _trigDebug = false; +} + +WintermuteEngine::WintermuteEngine(OSystem *syst, const ADGameDescription *desc) + : Engine(syst), _gameDescription(desc) { + // Put your engine in a sane state, but do nothing big yet; + // in particular, do not load data from files; rather, if you + // need to do such things, do them from init(). + ConfMan.registerDefault("show_fps","false"); + + // Do not initialize graphics here + + // However this is the place to specify all default directories + const Common::FSNode gameDataDir(ConfMan.get("path")); + //SearchMan.addSubDirectoryMatching(gameDataDir, "sound"); + + // Here is the right place to set up the engine specific debug channels + DebugMan.addDebugChannel(kWintermuteDebugLog, "enginelog", "Covers the same output as the log-file in WME"); + DebugMan.addDebugChannel(kWintermuteDebugSaveGame, "savegame", "Savegames"); + DebugMan.addDebugChannel(kWintermuteDebugFont, "font", "Text-drawing-related messages"); + DebugMan.addDebugChannel(kWintermuteDebugFileAccess, "file-access", "Non-critical problems like missing files"); + DebugMan.addDebugChannel(kWintermuteDebugAudio, "audio", "audio-playback-related issues"); + DebugMan.addDebugChannel(kWintermuteDebugGeneral, "general", "various issues not covered by any of the above"); + + _game = NULL; + _debugger = NULL; + _trigDebug = false; +} + +WintermuteEngine::~WintermuteEngine() { + // Dispose your resources here + deinit(); + delete _game; + delete _debugger; + + // Remove all of our debug levels here + DebugMan.clearAllDebugChannels(); +} + +bool WintermuteEngine::hasFeature(EngineFeature f) const { + switch (f) { + case kSupportsRTL: + return true; + case kSupportsLoadingDuringRuntime: + return true; + case kSupportsSavingDuringRuntime: + return true; + default: + return false; + } + return false; +} + +Common::Error WintermuteEngine::run() { + // Initialize graphics using following: + Graphics::PixelFormat format(4, 8, 8, 8, 8, 16, 8, 0, 24); + initGraphics(800, 600, true, &format); + if (g_system->getScreenFormat() != format) { + error("Wintermute currently REQUIRES 32bpp"); + } + + // Create debugger console. It requires GFX to be initialized + _debugger = new Console(this); + +// DebugMan.enableDebugChannel("enginelog"); + debugC(1, kWintermuteDebugLog, "Engine Debug-LOG enabled"); + debugC(2, kWintermuteDebugSaveGame , "Savegame debugging-enabled"); + + int ret = 1; + + // Additional setup. + debugC(kWintermuteDebugLog, "WintermuteEngine::init"); + ret = init(); + + debugC(kWintermuteDebugLog, "WintermuteEngine::messageLoop"); + if (ret == 0) { + ret = messageLoop(); + } + deinit(); + return Common::kNoError; +} + +int WintermuteEngine::init() { + BaseEngine::createInstance(_targetName, _gameDescription->language); + _game = new AdGame(_targetName); + if (!_game) { + return 1; + } + BaseEngine::instance().setGameRef(_game); + BasePlatform::initialize(this, _game, 0, NULL); + + bool windowedMode = !ConfMan.getBool("fullscreen"); + + if (ConfMan.hasKey("debug_mode")) { + if (ConfMan.getBool("debug_mode")) { + _game->DEBUG_DebugEnable("./wme.log"); + } + } + + if (ConfMan.hasKey("show_fps")) { + _game->_debugShowFPS = ConfMan.getBool("show_fps"); + } else { + _game->_debugShowFPS = false; + } + + if (ConfMan.hasKey("disable_smartcache")) { + _game->_smartCache = ConfMan.getBool("disable_smartcache"); + } else { + _game->_smartCache = true; + } + + if (!_game->_smartCache) { + _game->LOG(0, "Smart cache is DISABLED"); + } + + // load general game settings + _game->initialize1(); + + // set gameId, for savegame-naming: + _game->setGameId(_targetName); + + if (DID_FAIL(_game->loadSettings("startup.settings"))) { + _game->LOG(0, "Error loading game settings."); + delete _game; + _game = NULL; + + warning("Some of the essential files are missing. Please reinstall."); + return 2; + } + + _game->initialize2(); + + bool ret; + + // initialize the renderer + ret = _game->_renderer->initRenderer(_game->_settingsResWidth, _game->_settingsResHeight, windowedMode); + if (DID_FAIL(ret)) { + _game->LOG(ret, "Error initializing renderer. Exiting."); + + delete _game; + _game = NULL; + return 3; + } + + _game->initialize3(); + + // initialize sound manager (non-fatal if we fail) + ret = _game->_soundMgr->initialize(); + if (DID_FAIL(ret)) { + _game->LOG(ret, "Sound is NOT available."); + } + + + // load game + uint32 dataInitStart = g_system->getMillis(); + + if (DID_FAIL(_game->loadFile(_game->_settingsGameFile ? _game->_settingsGameFile : "default.game"))) { + _game->LOG(ret, "Error loading game file. Exiting."); + delete _game; + _game = NULL; + return false; + } + + _game->_renderer->_ready = true; + _game->_miniUpdateEnabled = true; + + _game->LOG(0, "Engine initialized in %d ms", g_system->getMillis() - dataInitStart); + _game->LOG(0, ""); + + if (ConfMan.hasKey("save_slot")) { + int slot = ConfMan.getInt("save_slot"); + _game->loadGame(slot); + } + + // all set, ready to go + return 0; +} + +int WintermuteEngine::messageLoop() { + bool done = false; + + uint32 prevTime = _system->getMillis(); + uint32 time = _system->getMillis(); + uint32 diff = 0; + + const uint32 maxFPS = 60; + const uint32 frameTime = 2 * (uint32)((1.0 / maxFPS) * 1000); + while (!done) { + _debugger->onFrame(); + + Common::Event event; + while (_system->getEventManager()->pollEvent(event)) { + BasePlatform::handleEvent(&event); + } + + if (_trigDebug) { + _debugger->attach(); + _trigDebug = false; + } + + if (_game && _game->_renderer->_active && _game->_renderer->_ready) { + _game->displayContent(); + _game->displayQuickMsg(); + + _game->displayDebugInfo(); + + time = _system->getMillis(); + diff = time - prevTime; + if (frameTime > diff) { // Avoid overflows + _system->delayMillis(frameTime - diff); + } + + // ***** flip + if (!_game->_suspendedRendering) { + _game->_renderer->flip(); + } + if (_game->_loading) { + _game->loadGame(_game->_scheduledLoadSlot); + } + prevTime = time; + } + if (_game->_quitting) { + break; + } + } + + if (_game) { + delete _game; + _game = NULL; + } + return 0; +} + +void WintermuteEngine::deinit() { + BaseEngine::destroy(); + BasePlatform::deinit(); +} + +Common::Error WintermuteEngine::loadGameState(int slot) { + BaseEngine::instance().getGameRef()->loadGame(slot); + return Common::kNoError; +} + +Common::Error WintermuteEngine::saveGameState(int slot, const Common::String &desc) { + BaseEngine::instance().getGameRef()->saveGame(slot, desc.c_str(), false); + return Common::kNoError; +} + +bool WintermuteEngine::canSaveGameStateCurrently() { + return true; +} + +bool WintermuteEngine::canLoadGameStateCurrently() { + return true; +} + +bool WintermuteEngine::getGameInfo(const Common::FSList &fslist, Common::String &name, Common::String &caption) { + bool retVal = false; + caption = name = "(invalid)"; + Common::SeekableReadStream *stream = NULL; + // Quick-fix, instead of possibly breaking the persistence-system, let's just roll with it + BaseFileManager *fileMan = new BaseFileManager(Common::UNK_LANG); + fileMan->registerPackages(fslist); + stream = fileMan->openFile("startup.settings", false, false); + + // The process is as follows: Check the "GAME=" tag in startup.settings, to decide where the + // game-settings are (usually "default.game"), then look into the game-settings to find + // the NAME = and CAPTION = tags, to use them to generate a gameid and extras-field + + Common::String settingsGameFile = "default.game"; + // If the stream-open failed, lets at least attempt to open the default game file afterwards + // so, we don't call it a failure yet. + if (stream) { + while (!stream->eos() && !stream->err()) { + Common::String line = stream->readLine(); + line.trim(); // Get rid of indentation + // Expect "SETTINGS {" or comment, or empty line + if (line.size() == 0 || line[0] == ';' || (line.contains("{"))) { + continue; + } else { + // We are looking for "GAME =" + Common::StringTokenizer token(line, "="); + Common::String key = token.nextToken(); + Common::String value = token.nextToken(); + if (value.size() == 0) { + continue; + } + if (value[0] == '\"') { + value.deleteChar(0); + } else { + continue; + } + if (value.lastChar() == '\"') { + value.deleteLastChar(); + } + if (key == "GAME") { + settingsGameFile = value; + break; + } + } + } + } + + delete stream; + stream = fileMan->openFile(settingsGameFile, false, false); + if (stream) { + // We do some manual parsing here, as the engine needs gfx to be initalized to do that. + while (!stream->eos() && !stream->err()) { + Common::String line = stream->readLine(); + line.trim(); // Get rid of indentation + // Expect "GAME {" or comment, or empty line + if (line.size() == 0 || line[0] == ';' || (line.contains("{"))) { + continue; + } else { + Common::StringTokenizer token(line, "="); + Common::String key = token.nextToken(); + Common::String value = token.nextToken(); + if (value.size() == 0) { + continue; + } + if (value[0] == '\"') { + value.deleteChar(0); + } else { + continue; // not a string + } + if (value.lastChar() == '\"') { + value.deleteLastChar(); + } + if (key == "NAME") { + retVal = true; + name = value; + } else if (key == "CAPTION") { + retVal = true; + caption = value; + } + } + } + delete stream; + } + delete fileMan; + BaseEngine::destroy(); + return retVal; +} + +} // End of namespace Wintermute diff --git a/engines/wintermute/wintermute.h b/engines/wintermute/wintermute.h new file mode 100644 index 0000000000..fcaa2840a9 --- /dev/null +++ b/engines/wintermute/wintermute.h @@ -0,0 +1,77 @@ +/* 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 WINTERMUTE_H +#define WINTERMUTE_H + +#include "engines/engine.h" +#include "engines/advancedDetector.h" +#include "gui/debugger.h" + +namespace Wintermute { + +class Console; +class BaseGame; +class SystemClassRegistry; +// our engine debug channels +enum { + kWintermuteDebugLog = 1 << 0, // The debug-logs from the original engine + kWintermuteDebugSaveGame = 1 << 1, + kWintermuteDebugFont = 1 << 2, // next new channel must be 1 << 2 (4) + kWintermuteDebugFileAccess = 1 << 3, // the current limitation is 32 debug channels (1 << 31 is the last one) + kWintermuteDebugAudio = 1 << 4, + kWintermuteDebugGeneral = 1 << 5 +}; + +class WintermuteEngine : public Engine { +public: + WintermuteEngine(OSystem *syst, const ADGameDescription *desc); + WintermuteEngine(); + ~WintermuteEngine(); + + virtual GUI::Debugger *getDebugger() { return _debugger; } + void trigDebugger() { _trigDebug = true; } + + virtual Common::Error run(); + virtual bool hasFeature(EngineFeature f) const; + Common::SaveFileManager *getSaveFileMan() { return _saveFileMan; } + virtual Common::Error loadGameState(int slot); + virtual bool canLoadGameStateCurrently(); + virtual Common::Error saveGameState(int slot, const Common::String &desc); + virtual bool canSaveGameStateCurrently(); + // For detection-purposes: + static bool getGameInfo(const Common::FSList &fslist, Common::String &name, Common::String &caption); +private: + bool _trigDebug; + int init(); + void deinit(); + int messageLoop(); + GUI::Debugger *_debugger; + BaseGame *_game; + const ADGameDescription *_gameDescription; + + friend class Console; +}; + +} // End of namespace Wintermute + +#endif diff --git a/engines/wintermute/wintypes.h b/engines/wintermute/wintypes.h new file mode 100644 index 0000000000..c7723808ea --- /dev/null +++ b/engines/wintermute/wintypes.h @@ -0,0 +1,53 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_WINTYPES_H +#define WINTERMUTE_WINTYPES_H + +#include "common/scummsys.h" + +namespace Wintermute { + +#define BYTETORGBA(r,g,b,a) ((uint32)((((a)&0xff)<<24)|(((r)&0xff)<<16)|(((g)&0xff)<<8)|((b)&0xff))) + +#define RGBCOLGetB(rgb) ((byte )(rgb)) +#define RGBCOLGetG(rgb) ((byte )(((uint16)(rgb)) >> 8)) +#define RGBCOLGetR(rgb) ((byte )((rgb)>>16)) +#define RGBCOLGetA(rgb) ((byte )((rgb)>>24)) + +#define DID_SUCCEED(hr) ((bool)(hr)) +#define DID_FAIL(hr) (!((bool)(hr))) + +#define STATUS_OK (true) +#define STATUS_FAILED (false) + +#define MAX_PATH_LENGTH 512 + +} // end of namespace Wintermute + +#endif diff --git a/graphics/VectorRenderer.cpp b/graphics/VectorRenderer.cpp index 30ef9eeeeb..f426dd8c41 100644 --- a/graphics/VectorRenderer.cpp +++ b/graphics/VectorRenderer.cpp @@ -80,7 +80,7 @@ void VectorRenderer::stepGetPositions(const DrawStep &step, const Common::Rect & case Graphics::DrawStep::kVectorAlignManual: if (step.x >= 0) in_x = area.left + step.x + step.padding.left; - else + else in_x = area.left + area.width() + step.x + step.padding.left; // value relative to the opposite corner. break; diff --git a/graphics/VectorRenderer.h b/graphics/VectorRenderer.h index e98f4aa761..0467cac946 100644 --- a/graphics/VectorRenderer.h +++ b/graphics/VectorRenderer.h @@ -55,7 +55,7 @@ struct DrawStep { bool autoWidth, autoHeight; int16 x, y, w, h; /**< width, height and position, if not measured automatically. negative values mean counting from the opposite direction */ - + Common::Rect padding; enum VectorAlignment { diff --git a/graphics/VectorRendererSpec.cpp b/graphics/VectorRendererSpec.cpp index 1ed5a3308a..6a3ee306a5 100644 --- a/graphics/VectorRendererSpec.cpp +++ b/graphics/VectorRendererSpec.cpp @@ -369,12 +369,12 @@ gradientFill(PixelType *ptr, int width, int x, int y) { int grad = (((y - _gradIndexes[curGrad]) % stripSize) << 2) / stripSize; // Dithering: - // +--+ +--+ +--+ +--+ - // | | | | | *| | *| - // | | | *| |* | |**| - // +--+ +--+ +--+ +--+ + // +--+ +--+ +--+ +--+ + // | | | | | *| | *| + // | | | *| |* | |**| + // +--+ +--+ +--+ +--+ // 0 1 2 3 - if (grad == 0 || + if (grad == 0 || _gradCache[curGrad] == _gradCache[curGrad + 1] || // no color change stripSize < 2) { // the stip is small colorFill<PixelType>(ptr, ptr + width, _gradCache[curGrad]); @@ -873,7 +873,7 @@ drawTriangle(int x, int y, int w, int h, TriangleOrientation orient) { case kTriangleDown: drawTriangleVertAlg(x, y, newW, newH, (orient == kTriangleDown), color, Base::_fillMode); break; - + case kTriangleLeft: case kTriangleRight: case kTriangleAuto: @@ -1206,14 +1206,14 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color pitch = -pitch; y1 += h; } - + PixelType *ptr_right = (PixelType *)_activeSurface->getBasePtr(x1, y1); PixelType *floor = ptr_right - 1; PixelType *ptr_left = (PixelType *)_activeSurface->getBasePtr(x1 + w, y1); int x2 = x1 + w / 2; int y2 = y1 + h; - + #if FIXED_POINT int dx = (x2 - x1) << 8; int dy = (y2 - y1) << 8; @@ -1227,7 +1227,7 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color #endif while (floor++ != ptr_left) blendPixelPtr(floor, color, 50); - + #if FIXED_POINT int gradient = (dy << 8) / dx; int intery = (y1 << 8) + gradient; @@ -1250,7 +1250,7 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color ptr_right += pitch; intery += gradient; - + switch (fill_m) { case kFillDisabled: *ptr_left = *ptr_right = color; @@ -1262,16 +1262,16 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color blendPixelPtr(ptr_left, color, rfpart(intery)); break; case kFillGradient: - colorFill<PixelType>(ptr_right, ptr_left, calcGradient(gradient_h++, h)); + colorFill<PixelType>(ptr_right, ptr_left, calcGradient(gradient_h++, h)); blendPixelPtr(ptr_right, color, rfpart(intery)); blendPixelPtr(ptr_left, color, rfpart(intery)); break; } } - + return; } - + #if FIXED_POINT if (abs(dx) < abs(dy)) { #else @@ -1280,7 +1280,7 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color ptr_left--; while (floor++ != ptr_left) blendPixelPtr(floor, color, 50); - + #if FIXED_POINT int gradient = (dx << 8) / (dy + 0x100); int interx = (x1 << 8) + gradient; @@ -1303,7 +1303,7 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color ptr_right += pitch; interx += gradient; - + switch (fill_m) { case kFillDisabled: *ptr_left = *ptr_right = color; @@ -1315,18 +1315,18 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color blendPixelPtr(ptr_left, color, rfpart(interx)); break; case kFillGradient: - colorFill<PixelType>(ptr_right, ptr_left, calcGradient(gradient_h++, h)); + colorFill<PixelType>(ptr_right, ptr_left, calcGradient(gradient_h++, h)); blendPixelPtr(ptr_right, color, rfpart(interx)); blendPixelPtr(ptr_left, color, rfpart(interx)); break; } } - + return; } - + ptr_left--; - + while (floor++ != ptr_left) blendPixelPtr(floor, color, 50); @@ -1341,12 +1341,12 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color for (int y = y1 + 1; y < y2; y++) { ptr_right++; ptr_left--; - + ptr_left += pitch; ptr_right += pitch; interx += gradient; - + switch (fill_m) { case kFillDisabled: *ptr_left = *ptr_right = color; @@ -1358,13 +1358,13 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color blendPixelPtr(ptr_left, color, rfpart(interx)); break; case kFillGradient: - colorFill<PixelType>(ptr_right, ptr_left, calcGradient(gradient_h++, h)); + colorFill<PixelType>(ptr_right, ptr_left, calcGradient(gradient_h++, h)); blendPixelPtr(ptr_right, color, rfpart(interx)); blendPixelPtr(ptr_left, color, rfpart(interx)); break; } } - + } /** VERTICAL TRIANGLE DRAWING - FAST VERSION FOR SQUARED TRIANGLES */ @@ -1372,12 +1372,12 @@ template<typename PixelType> void VectorRendererSpec<PixelType>:: drawTriangleFast(int x1, int y1, int size, bool inverted, PixelType color, VectorRenderer::FillMode fill_m) { int pitch = _activeSurface->pitch / _activeSurface->format.bytesPerPixel; - + if (!inverted) { pitch = -pitch; y1 += size; } - + int gradient_h = 0; PixelType *ptr_right = (PixelType *)_activeSurface->getBasePtr(x1, y1); PixelType *ptr_left = (PixelType *)_activeSurface->getBasePtr(x1 + size, y1); @@ -1388,9 +1388,9 @@ drawTriangleFast(int x1, int y1, int size, bool inverted, PixelType color, Vecto int signX = x1 < x2 ? 1 : -1; int signY = y1 < y2 ? 1 : -1; int error = deltaX - deltaY; - + colorFill<PixelType>(ptr_right, ptr_left, color); - + while (1) { switch (fill_m) { case kFillDisabled: @@ -1401,22 +1401,22 @@ drawTriangleFast(int x1, int y1, int size, bool inverted, PixelType color, Vecto colorFill<PixelType>(ptr_right, ptr_left, color); break; case kFillGradient: - colorFill<PixelType>(ptr_right, ptr_left, calcGradient(gradient_h++, size)); + colorFill<PixelType>(ptr_right, ptr_left, calcGradient(gradient_h++, size)); break; } - + if (x1 == x2 && y1 == y2) break; - + int error2 = error * 2; - + if (error2 > -deltaY) { error -= deltaY; x1 += signX; ptr_right += signX; ptr_left += -signX; } - + if (error2 < deltaX) { error += deltaX; y1 += signY; diff --git a/graphics/conversion.cpp b/graphics/conversion.cpp index 713a06ea74..2da8b6f0ce 100644 --- a/graphics/conversion.cpp +++ b/graphics/conversion.cpp @@ -22,123 +22,143 @@ #include "graphics/conversion.h" #include "graphics/pixelformat.h" +#include "common/endian.h" + namespace Graphics { // TODO: YUV to RGB conversion function +namespace { + +template<typename SrcColor, typename DstColor, bool backward> +inline void crossBlitLogic(byte *dst, const byte *src, const uint w, const uint h, + const PixelFormat &srcFmt, const PixelFormat &dstFmt, + const uint srcDelta, const uint dstDelta) { + for (uint y = 0; y < h; ++y) { + for (uint x = 0; x < w; ++x) { + const uint32 color = *(const SrcColor *)src; + byte a, r, g, b; + srcFmt.colorToARGB(color, a, r, g, b); + *(DstColor *)dst = dstFmt.ARGBToColor(a, r, g, b); + + if (backward) { + src -= sizeof(SrcColor); + dst -= sizeof(DstColor); + } else { + src += sizeof(SrcColor); + dst += sizeof(DstColor); + } + } + + if (backward) { + src -= srcDelta; + dst -= dstDelta; + } else { + src += srcDelta; + dst += dstDelta; + } + } +} + +template<typename DstColor, bool backward> +inline void crossBlitLogic3BppSource(byte *dst, const byte *src, const uint w, const uint h, + const PixelFormat &srcFmt, const PixelFormat &dstFmt, + const uint srcDelta, const uint dstDelta) { + uint32 color; + byte r, g, b, a; + uint8 *col = (uint8 *)&color; +#ifdef SCUMM_BIG_ENDIAN + col++; +#endif + for (uint y = 0; y < h; ++y) { + for (uint x = 0; x < w; ++x) { + memcpy(col, src, 3); + srcFmt.colorToARGB(color, a, r, g, b); + *(DstColor *)dst = dstFmt.ARGBToColor(a, r, g, b); + + if (backward) { + src -= 3; + dst -= sizeof(DstColor); + } else { + src += 3; + dst += sizeof(DstColor); + } + } + + if (backward) { + src -= srcDelta; + dst -= dstDelta; + } else { + src += srcDelta; + dst += dstDelta; + } + } +} + +} // End of anonymous namespace + // Function to blit a rect from one color format to another -bool crossBlit(byte *dst, const byte *src, int dstpitch, int srcpitch, - int w, int h, const Graphics::PixelFormat &dstFmt, const Graphics::PixelFormat &srcFmt) { +bool crossBlit(byte *dst, const byte *src, + const uint dstPitch, const uint srcPitch, + const uint w, const uint h, + const Graphics::PixelFormat &dstFmt, const Graphics::PixelFormat &srcFmt) { // Error out if conversion is impossible if ((srcFmt.bytesPerPixel == 1) || (dstFmt.bytesPerPixel == 1) - || (!srcFmt.bytesPerPixel) || (!dstFmt.bytesPerPixel) - || (srcFmt.bytesPerPixel > dstFmt.bytesPerPixel)) + || (dstFmt.bytesPerPixel == 3) + || (!srcFmt.bytesPerPixel) || (!dstFmt.bytesPerPixel)) return false; // Don't perform unnecessary conversion if (srcFmt == dstFmt) { - if (dst == src) - return true; - if (dstpitch == srcpitch && ((w * dstFmt.bytesPerPixel) == dstpitch)) { - memcpy(dst,src,dstpitch * h); - return true; - } else { - for (int i = 0; i < h; i++) { - memcpy(dst,src,w * dstFmt.bytesPerPixel); - dst += dstpitch; - src += srcpitch; + if (dst != src) { + if (dstPitch == srcPitch && ((w * dstFmt.bytesPerPixel) == dstPitch)) { + memcpy(dst, src, dstPitch * h); + } else { + for (uint i = 0; i < h; ++i) { + memcpy(dst, src, w * dstFmt.bytesPerPixel); + dst += dstPitch; + src += srcPitch; + } } - return true; } + + return true; } // Faster, but larger, to provide optimized handling for each case. - int srcDelta, dstDelta; - srcDelta = (srcpitch - w * srcFmt.bytesPerPixel); - dstDelta = (dstpitch - w * dstFmt.bytesPerPixel); + const uint srcDelta = (srcPitch - w * srcFmt.bytesPerPixel); + const uint dstDelta = (dstPitch - w * dstFmt.bytesPerPixel); // TODO: optimized cases for dstDelta of 0 - uint8 r, g, b, a; if (dstFmt.bytesPerPixel == 2) { - uint16 color; - for (int y = 0; y < h; y++) { - for (int x = 0; x < w; x++, src += 2, dst += 2) { - color = *(const uint16 *)src; - srcFmt.colorToARGB(color, a, r, g, b); - color = dstFmt.ARGBToColor(a, r, g, b); - *(uint16 *)dst = color; - } - src += srcDelta; - dst += dstDelta; - } - } else if (dstFmt.bytesPerPixel == 3) { - uint32 color; - uint8 *col = (uint8 *) &color; -#ifdef SCUMM_BIG_ENDIAN - col++; -#endif if (srcFmt.bytesPerPixel == 2) { - for (int y = 0; y < h; y++) { - for (int x = 0; x < w; x++, src += 2, dst += 3) { - color = *(const uint16 *)src; - srcFmt.colorToARGB(color, a, r, g, b); - color = dstFmt.ARGBToColor(a, r, g, b); - memcpy(dst, col, 3); - } - src += srcDelta; - dst += dstDelta; - } + crossBlitLogic<uint16, uint16, false>(dst, src, w, h, srcFmt, dstFmt, srcDelta, dstDelta); + } else if (srcFmt.bytesPerPixel == 3) { + crossBlitLogic3BppSource<uint16, false>(dst, src, w, h, srcFmt, dstFmt, srcDelta, dstDelta); } else { - for (int y = 0; y < h; y++) { - for (int x = 0; x < w; x++, src += 3, dst += 3) { - memcpy(col, src, 3); - srcFmt.colorToARGB(color, a, r, g, b); - color = dstFmt.ARGBToColor(a, r, g, b); - memcpy(dst, col, 3); - } - src += srcDelta; - dst += dstDelta; - } + crossBlitLogic<uint32, uint16, false>(dst, src, w, h, srcFmt, dstFmt, srcDelta, dstDelta); } } else if (dstFmt.bytesPerPixel == 4) { - uint32 color; if (srcFmt.bytesPerPixel == 2) { - for (int y = 0; y < h; y++) { - for (int x = 0; x < w; x++, src += 2, dst += 4) { - color = *(const uint16 *)src; - srcFmt.colorToARGB(color, a, r, g, b); - color = dstFmt.ARGBToColor(a, r, g, b); - *(uint32 *)dst = color; - } - src += srcDelta; - dst += dstDelta; - } + // We need to blit the surface from bottom right to top left here. + // This is neeeded, because when we convert to the same memory + // buffer copying the surface from top left to bottom right would + // overwrite the source, since we have more bits per destination + // color than per source color. + dst += h * dstPitch - dstDelta - dstFmt.bytesPerPixel; + src += h * srcPitch - srcDelta - srcFmt.bytesPerPixel; + crossBlitLogic<uint16, uint32, true>(dst, src, w, h, srcFmt, dstFmt, srcDelta, dstDelta); } else if (srcFmt.bytesPerPixel == 3) { - uint8 *col = (uint8 *)&color; -#ifdef SCUMM_BIG_ENDIAN - col++; -#endif - for (int y = 0; y < h; y++) { - for (int x = 0; x < w; x++, src += 2, dst += 4) { - memcpy(col, src, 3); - srcFmt.colorToARGB(color, a, r, g, b); - color = dstFmt.ARGBToColor(a, r, g, b); - *(uint32 *)dst = color; - } - src += srcDelta; - dst += dstDelta; - } + // We need to blit the surface from bottom right to top left here. + // This is neeeded, because when we convert to the same memory + // buffer copying the surface from top left to bottom right would + // overwrite the source, since we have more bits per destination + // color than per source color. + dst += h * dstPitch - dstDelta - dstFmt.bytesPerPixel; + src += h * srcPitch - srcDelta - srcFmt.bytesPerPixel; + crossBlitLogic3BppSource<uint32, true>(dst, src, w, h, srcFmt, dstFmt, srcDelta, dstDelta); } else { - for (int y = 0; y < h; y++) { - for (int x = 0; x < w; x++, src += 4, dst += 4) { - color = *(const uint32 *)src; - srcFmt.colorToARGB(color, a, r, g, b); - color = dstFmt.ARGBToColor(a, r, g, b); - *(uint32 *)dst = color; - } - src += srcDelta; - dst += dstDelta; - } + crossBlitLogic<uint32, uint32, false>(dst, src, w, h, srcFmt, dstFmt, srcDelta, dstDelta); } } else { return false; diff --git a/graphics/conversion.h b/graphics/conversion.h index 6babc763e2..28e64a94fb 100644 --- a/graphics/conversion.h +++ b/graphics/conversion.h @@ -59,15 +59,18 @@ inline static void RGB2YUV(byte r, byte g, byte b, byte &y, byte &u, byte &v) { * @return true if conversion completes successfully, * false if there is an error. * - * @note This implementation currently arbitrarily requires that the - * destination's format have at least as high a bytedepth as - * the source's. - * @note This can convert a rectangle in place, if the source and - * destination format have the same bytedepth. - * + * @note Blitting to a 3Bpp destination is not supported + * @note This can convert a surface in place, regardless of the + * source and destination format, as long as there is enough + * space for the destination. The dstPitch / srcPitch ratio + * must at least equal the dstBpp / srcBpp ratio for + * dstPitch >= srcPitch and at most dstBpp / srcBpp for + * dstPitch < srcPitch though. */ -bool crossBlit(byte *dst, const byte *src, int dstpitch, int srcpitch, - int w, int h, const Graphics::PixelFormat &dstFmt, const Graphics::PixelFormat &srcFmt); +bool crossBlit(byte *dst, const byte *src, + const uint dstPitch, const uint srcPitch, + const uint w, const uint h, + const Graphics::PixelFormat &dstFmt, const Graphics::PixelFormat &srcFmt); } // End of namespace Graphics diff --git a/graphics/decoders/bmp.cpp b/graphics/decoders/bmp.cpp index 5f764e1bd3..bcfd0abbda 100644 --- a/graphics/decoders/bmp.cpp +++ b/graphics/decoders/bmp.cpp @@ -82,7 +82,7 @@ bool BitmapDecoder::loadStream(Common::SeekableReadStream &stream) { /* uint16 planes = */ stream.readUint16LE(); uint16 bitsPerPixel = stream.readUint16LE(); - if (bitsPerPixel != 8 && bitsPerPixel != 24) { + if (bitsPerPixel != 8 && bitsPerPixel != 24 && bitsPerPixel != 32) { warning("%dbpp bitmaps not supported", bitsPerPixel); return false; } @@ -100,10 +100,10 @@ bool BitmapDecoder::loadStream(Common::SeekableReadStream &stream) { _paletteColorCount = stream.readUint32LE(); /* uint32 colorsImportant = */ stream.readUint32LE(); - if (_paletteColorCount == 0) - _paletteColorCount = 256; - if (bitsPerPixel == 8) { + if (_paletteColorCount == 0) + _paletteColorCount = 256; + // Read the palette _palette = new byte[_paletteColorCount * 3]; for (uint16 i = 0; i < _paletteColorCount; i++) { @@ -119,8 +119,8 @@ bool BitmapDecoder::loadStream(Common::SeekableReadStream &stream) { Graphics::PixelFormat format = Graphics::PixelFormat::createFormatCLUT8(); - // BGRA for 24bpp - if (bitsPerPixel == 24) + // BGRA for 24bpp and 32 bpp + if (bitsPerPixel == 24 || bitsPerPixel == 32) format = Graphics::PixelFormat(4, 8, 8, 8, 8, 8, 16, 24, 0); _surface = new Graphics::Surface(); @@ -136,7 +136,24 @@ bool BitmapDecoder::loadStream(Common::SeekableReadStream &stream) { stream.read(dst + (height - i - 1) * width, width); stream.skip(extraDataLength); } - } else { + } else if (bitsPerPixel == 24) { + byte *dst = (byte *)_surface->pixels + (height - 1) * _surface->pitch; + + for (int32 i = 0; i < height; i++) { + for (uint32 j = 0; j < width; j++) { + byte b = stream.readByte(); + byte g = stream.readByte(); + byte r = stream.readByte(); + uint32 color = format.RGBToColor(r, g, b); + + *((uint32 *)dst) = color; + dst += format.bytesPerPixel; + } + + stream.skip(extraDataLength); + dst -= _surface->pitch * 2; + } + } else { // 32 bpp byte *dst = (byte *)_surface->pixels + (height - 1) * _surface->pitch; for (int32 i = 0; i < height; i++) { @@ -144,6 +161,10 @@ bool BitmapDecoder::loadStream(Common::SeekableReadStream &stream) { byte b = stream.readByte(); byte g = stream.readByte(); byte r = stream.readByte(); + // Ignore the last byte, as in v3 it is unused + // and should thus NOT be used as alpha. + // ref: http://msdn.microsoft.com/en-us/library/windows/desktop/dd183376%28v=vs.85%29.aspx + stream.readByte(); uint32 color = format.RGBToColor(r, g, b); *((uint32 *)dst) = color; diff --git a/graphics/decoders/bmp.h b/graphics/decoders/bmp.h index 59da682e4d..779da352be 100644 --- a/graphics/decoders/bmp.h +++ b/graphics/decoders/bmp.h @@ -24,6 +24,7 @@ * Image decoder used in engines: * - hugo * - mohawk + * - wintermute */ #ifndef GRAPHICS_DECODERS_BMP_H diff --git a/graphics/decoders/iff.cpp b/graphics/decoders/iff.cpp new file mode 100644 index 0000000000..50c7b4f7de --- /dev/null +++ b/graphics/decoders/iff.cpp @@ -0,0 +1,241 @@ +/* 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 "common/iff_container.h" +#include "common/stream.h" +#include "common/util.h" + +#include "graphics/decoders/iff.h" + +namespace Graphics { + +IFFDecoder::IFFDecoder() { + _surface = 0; + _palette = 0; + + destroy(); +} + +IFFDecoder::~IFFDecoder() { + destroy(); +} + +void IFFDecoder::destroy() { + if (_surface) { + _surface->free(); + delete _surface; + _surface = 0; + } + + if (_palette) { + delete[] _palette; + _palette = 0; + } + + memset(&_header, 0, sizeof(Header)); + _paletteRanges.clear(); + _type = TYPE_UNKNOWN; + _paletteColorCount = 0; + _numRelevantPlanes = 8; + _pixelPacking = false; +} + +bool IFFDecoder::loadStream(Common::SeekableReadStream &stream) { + destroy(); + + const uint32 form = stream.readUint32BE(); + + if (form != ID_FORM) { + warning("Failed reading IFF-file"); + return false; + } + + stream.skip(4); + + const uint32 type = stream.readUint32BE(); + + switch (type) { + case ID_ILBM: + _type = TYPE_ILBM; + break; + case ID_PBM: + _type = TYPE_PBM; + break; + } + + if (type == TYPE_UNKNOWN) { + warning("Failed reading IFF-file"); + return false; + } + + while (1) { + const uint32 chunkType = stream.readUint32BE(); + const uint32 chunkSize = stream.readUint32BE(); + + if (stream.eos()) + break; + + switch (chunkType) { + case ID_BMHD: + loadHeader(stream); + break; + case ID_CMAP: + loadPalette(stream, chunkSize); + break; + case ID_CRNG: + loadPaletteRange(stream, chunkSize); + break; + case ID_BODY: + loadBitmap(stream); + break; + default: + stream.skip(chunkSize); + } + } + + return true; +} + +void IFFDecoder::loadHeader(Common::SeekableReadStream &stream) { + _header.width = stream.readUint16BE(); + _header.height = stream.readUint16BE(); + _header.x = stream.readUint16BE(); + _header.y = stream.readUint16BE(); + _header.numPlanes = stream.readByte(); + _header.masking = stream.readByte(); + _header.compression = stream.readByte(); + _header.flags = stream.readByte(); + _header.transparentColor = stream.readUint16BE(); + _header.xAspect = stream.readByte(); + _header.yAspect = stream.readByte(); + _header.pageWidth = stream.readUint16BE(); + _header.pageHeight = stream.readUint16BE(); + + assert(_header.width >= 1); + assert(_header.height >= 1); + assert(_header.numPlanes >= 1 && _header.numPlanes <= 8 && _header.numPlanes != 7); +} + +void IFFDecoder::loadPalette(Common::SeekableReadStream &stream, const uint32 size) { + _palette = new byte[size]; + stream.read(_palette, size); + _paletteColorCount = size / 3; +} + +void IFFDecoder::loadPaletteRange(Common::SeekableReadStream &stream, const uint32 size) { + PaletteRange range; + + range.timer = stream.readSint16BE(); + range.step = stream.readSint16BE(); + range.flags = stream.readSint16BE(); + range.first = stream.readByte(); + range.last = stream.readByte(); + + _paletteRanges.push_back(range); +} + +void IFFDecoder::loadBitmap(Common::SeekableReadStream &stream) { + _numRelevantPlanes = MIN(_numRelevantPlanes, _header.numPlanes); + + if (_numRelevantPlanes != 1 && _numRelevantPlanes != 2 && _numRelevantPlanes != 4) + _pixelPacking = false; + + uint16 outPitch = _header.width; + + if (_pixelPacking) + outPitch /= (8 / _numRelevantPlanes); + + // FIXME: CLUT8 is not a proper format for packed bitmaps but there is no way to tell it to use 1, 2 or 4 bits per pixel + _surface = new Graphics::Surface(); + _surface->create(outPitch, _header.height, Graphics::PixelFormat::createFormatCLUT8()); + + if (_type == TYPE_ILBM) { + uint32 scanlinePitch = ((_header.width + 15) >> 4) << 1; + byte *scanlines = new byte[scanlinePitch * _header.numPlanes]; + byte *data = (byte *)_surface->pixels; + + for (uint16 i = 0; i < _header.height; ++i) { + byte *scanline = scanlines; + + for (uint16 j = 0; j < _header.numPlanes; ++j) { + uint16 outSize = scanlinePitch; + + if (_header.compression) { + Common::PackBitsReadStream packStream(stream); + packStream.read(scanline, outSize); + } else { + stream.read(scanline, outSize); + } + + scanline += outSize; + } + + packPixels(scanlines, data, scanlinePitch, outPitch); + data += outPitch; + } + + delete[] scanlines; + } else if (_type == TYPE_PBM) { + byte *data = (byte *)_surface->pixels; + uint32 outSize = _header.width * _header.height; + + if (_header.compression) { + Common::PackBitsReadStream packStream(stream); + packStream.read(data, outSize); + } else { + stream.read(data, outSize); + } + } +} + +void IFFDecoder::packPixels(byte *scanlines, byte *data, const uint16 scanlinePitch, const uint16 outPitch) { + uint32 numPixels = _header.width; + + if (_pixelPacking) + numPixels = outPitch * (8 / _numRelevantPlanes); + + for (uint32 x = 0; x < numPixels; ++x) { + byte *scanline = scanlines; + byte pixel = 0; + byte offset = x >> 3; + byte bit = 0x80 >> (x & 7); + + // first build a pixel by scanning all the usable planes in the input + for (uint32 plane = 0; plane < _numRelevantPlanes; ++plane) { + if (scanline[offset] & bit) + pixel |= (1 << plane); + + scanline += scanlinePitch; + } + + // then output the pixel according to the requested packing + if (!_pixelPacking) + data[x] = pixel; + else if (_numRelevantPlanes == 1) + data[x / 8] |= (pixel << (x & 7)); + else if (_numRelevantPlanes == 2) + data[x / 4] |= (pixel << ((x & 3) << 1)); + else if (_numRelevantPlanes == 4) + data[x / 2] |= (pixel << ((x & 1) << 2)); + } +} + +} // End of namespace Graphics diff --git a/graphics/decoders/iff.h b/graphics/decoders/iff.h new file mode 100644 index 0000000000..beac62e519 --- /dev/null +++ b/graphics/decoders/iff.h @@ -0,0 +1,119 @@ +/* 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. + */ + +/** + * @file + * Image decoder used in engines: + * - gob + * - parallaction + * - queen + * - saga + */ + +#ifndef GRAPHICS_DECODERS_IFF_H +#define GRAPHICS_DECODERS_IFF_H + +#include "common/array.h" +#include "common/endian.h" + +#include "graphics/surface.h" +#include "graphics/decoders/image_decoder.h" + +namespace Common { +class SeekableReadStream; +} + +namespace Graphics { + +struct Surface; + +class IFFDecoder : public ImageDecoder { +public: + struct Header { + uint16 width, height; + uint16 x, y; + byte numPlanes; + byte masking; + byte compression; + byte flags; + uint16 transparentColor; + byte xAspect, yAspect; + uint16 pageWidth, pageHeight; + }; + + struct PaletteRange { + int16 timer, step, flags; + byte first, last; + }; + + enum Type { + TYPE_UNKNOWN = 0, + TYPE_ILBM, + TYPE_PBM + }; + + IFFDecoder(); + virtual ~IFFDecoder(); + + // ImageDecoder API + void destroy(); + bool loadStream(Common::SeekableReadStream &stream); + const Header *getHeader() const { return &_header; } + const Surface *getSurface() const { return _surface; } + const byte *getPalette() const { return _palette; } + const Common::Array<PaletteRange> &getPaletteRanges() const { return _paletteRanges; } + uint16 getPaletteColorCount() const { return _paletteColorCount; } + + /** + * The number of planes to decode, also determines the pixel packing if _packPixels is true. + * 8 == decode all planes, map 1 pixel in 1 byte. (default, no packing even if _packPixels is true) + */ + void setNumRelevantPlanes(const uint8 numRelevantPlanes) { _numRelevantPlanes = numRelevantPlanes; } + + /** + * Enables pixel packing, the amount of packing is determined by _numRelevantPlanes + * 1 == decode first plane, pack 8 pixels in 1 byte. This makes _surface->w 1/8th of _header.width + * 2 == decode first 2 planes, pack 4 pixels in 1 byte. This makes _surface->w 1/4th of _header.width + * 4 == decode first 4 planes, pack 2 pixels in 1 byte. This makes _surface->w half of _header.width + * Packed bitmaps won't have a proper surface format since there is no way to tell it to use 1, 2 or 4 bits per pixel + */ + void setPixelPacking(const bool pixelPacking) { _pixelPacking = pixelPacking; } +private: + + Header _header; + Surface *_surface; + byte *_palette; + Common::Array<PaletteRange> _paletteRanges; + Type _type; + uint16 _paletteColorCount; + uint8 _numRelevantPlanes; + bool _pixelPacking; + + void loadHeader(Common::SeekableReadStream &stream); + void loadPalette(Common::SeekableReadStream &stream, const uint32 size); + void loadPaletteRange(Common::SeekableReadStream &stream, const uint32 size); + void loadBitmap(Common::SeekableReadStream &stream); + void packPixels(byte *scanlines, byte *data, const uint16 scanlinePitch, const uint16 outPitch); +}; + +} // End of namespace Graphics + +#endif // GRAPHICS_DECODERS_IFF_H diff --git a/graphics/decoders/image_decoder.h b/graphics/decoders/image_decoder.h index 7fa00749ff..49e31c6e3a 100644 --- a/graphics/decoders/image_decoder.h +++ b/graphics/decoders/image_decoder.h @@ -75,10 +75,18 @@ public: * until destroy() or loadStream() is called, or until this ImageDecoder's * destructor is called. * - * @return the decoded palette, or 0 if no palette is present + * The palette's format is the same as PaletteManager's palette + * (interleaved RGB values). + * + * @return the decoded palette, or undefined if no palette is present */ virtual const byte *getPalette() const { return 0; } + /** + * Query if the decoded image has a palette. + */ + virtual bool hasPalette() const { return getPaletteColorCount() != 0; } + /** Return the starting index of the palette. */ virtual byte getPaletteStartIndex() const { return 0; } /** Return the number of colors in the palette. */ diff --git a/graphics/decoders/jpeg.cpp b/graphics/decoders/jpeg.cpp index a871377ca1..08bc1f7a3d 100644 --- a/graphics/decoders/jpeg.cpp +++ b/graphics/decoders/jpeg.cpp @@ -81,7 +81,7 @@ const Surface *JPEGDecoder::getSurface() const { const Graphics::Surface *uComponent = getComponent(2); const Graphics::Surface *vComponent = getComponent(3); - convertYUV444ToRGB(_rgbSurface, (byte *)yComponent->pixels, (byte *)uComponent->pixels, (byte *)vComponent->pixels, yComponent->w, yComponent->h, yComponent->pitch, uComponent->pitch); + YUVToRGBMan.convert444(_rgbSurface, Graphics::YUVToRGBManager::kScaleFull, (byte *)yComponent->pixels, (byte *)uComponent->pixels, (byte *)vComponent->pixels, yComponent->w, yComponent->h, yComponent->pitch, uComponent->pitch); return _rgbSurface; } @@ -452,7 +452,7 @@ bool JPEGDecoder::readSOS() { _bitsNumber = 0; for (byte i = 0; i < _numScanComp; i++) - _scanComp[i]->DCpredictor = 0; + _scanComp[i]->DCpredictor = 0; } } } diff --git a/graphics/decoders/jpeg.h b/graphics/decoders/jpeg.h index c74aa57ca1..d59b72adf4 100644 --- a/graphics/decoders/jpeg.h +++ b/graphics/decoders/jpeg.h @@ -25,6 +25,7 @@ * Image decoder used in engines: * - groovie * - mohawk + * - wintermute */ #ifndef GRAPHICS_JPEG_H diff --git a/graphics/decoders/pcx.cpp b/graphics/decoders/pcx.cpp new file mode 100644 index 0000000000..1250398c73 --- /dev/null +++ b/graphics/decoders/pcx.cpp @@ -0,0 +1,213 @@ +/* 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 "common/stream.h" +#include "common/textconsole.h" + +#include "graphics/pixelformat.h" +#include "graphics/surface.h" +#include "graphics/decoders/pcx.h" + +/** + * Based on the PCX specs: + * http://www.fileformat.info/format/pcx/spec/a10e75307b3a4cc49c3bbe6db4c41fa2/view.htm + * and the PCX decoder of FFmpeg (libavcodec/pcx.c): + * http://git.videolan.org/?p=ffmpeg.git;a=blob;f=libavcodec/pcx.c + */ + +namespace Graphics { + +PCXDecoder::PCXDecoder() { + _surface = 0; + _palette = 0; + _paletteColorCount = 0; +} + +PCXDecoder::~PCXDecoder() { + destroy(); +} + +void PCXDecoder::destroy() { + if (_surface) { + _surface->free(); + delete _surface; + _surface = 0; + } + + delete[] _palette; + _palette = 0; + _paletteColorCount = 0; +} + +bool PCXDecoder::loadStream(Common::SeekableReadStream &stream) { + destroy(); + + if (stream.readByte() != 0x0a) // ZSoft PCX + return false; + + byte version = stream.readByte(); // 0 - 5 + if (version > 5) + return false; + + bool compressed = stream.readByte(); // encoding, 1 = run length encoding + byte bitsPerPixel = stream.readByte(); // 1, 2, 4 or 8 + + // Window + uint16 xMin = stream.readUint16LE(); + uint16 yMin = stream.readUint16LE(); + uint16 xMax = stream.readUint16LE(); + uint16 yMax = stream.readUint16LE(); + + uint16 width = xMax - xMin + 1; + uint16 height = yMax - yMin + 1; + + if (xMax < xMin || yMax < yMin) { + warning("Invalid PCX image dimensions"); + return false; + } + + stream.skip(4); // HDpi, VDpi + + // Read the EGA palette (colormap) + _palette = new byte[16 * 3]; + for (uint16 i = 0; i < 16; i++) { + _palette[i * 3 + 0] = stream.readByte(); + _palette[i * 3 + 1] = stream.readByte(); + _palette[i * 3 + 2] = stream.readByte(); + } + + if (stream.readByte() != 0) // reserved, should be set to 0 + return false; + + byte nPlanes = stream.readByte(); + uint16 bytesPerLine = stream.readUint16LE(); + uint16 bytesPerscanLine = nPlanes * bytesPerLine; + + if (bytesPerscanLine < width * bitsPerPixel * nPlanes / 8) { + warning("PCX data is corrupted"); + return false; + } + + stream.skip(60); // PaletteInfo, HscreenSize, VscreenSize, Filler + + _surface = new Graphics::Surface(); + + byte *scanLine = new byte[bytesPerscanLine]; + byte *dst; + int x, y; + + if (nPlanes == 3 && bitsPerPixel == 8) { // 24bpp + Graphics::PixelFormat format = Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0); + _surface->create(width, height, format); + dst = (byte *)_surface->pixels; + _paletteColorCount = 0; + + for (y = 0; y < height; y++) { + decodeRLE(stream, scanLine, bytesPerscanLine, compressed); + + for (x = 0; x < width; x++) { + byte b = scanLine[x]; + byte g = scanLine[x + bytesPerLine]; + byte r = scanLine[x + (bytesPerLine << 1)]; + uint32 color = format.RGBToColor(r, g, b); + + *((uint32 *)dst) = color; + dst += format.bytesPerPixel; + } + } + } else if (nPlanes == 1 && bitsPerPixel == 8) { // 8bpp indexed + _surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8()); + dst = (byte *)_surface->pixels; + _paletteColorCount = 16; + + for (y = 0; y < height; y++, dst += _surface->pitch) { + decodeRLE(stream, scanLine, bytesPerscanLine, compressed); + memcpy(dst, scanLine, width); + } + + if (version == 5) { + if (stream.readByte() != 12) { + warning("Expected a palette after the PCX image data"); + delete[] scanLine; + return false; + } + + // Read the VGA palette + delete[] _palette; + _palette = new byte[256 * 3]; + for (uint16 i = 0; i < 256; i++) { + _palette[i * 3 + 0] = stream.readByte(); + _palette[i * 3 + 1] = stream.readByte(); + _palette[i * 3 + 2] = stream.readByte(); + } + + _paletteColorCount = 256; + } + } else if ((nPlanes == 2 || nPlanes == 3 || nPlanes == 4) && bitsPerPixel == 1) { // planar, 4, 8 or 16 colors + _surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8()); + dst = (byte *)_surface->pixels; + _paletteColorCount = 16; + + for (y = 0; y < height; y++, dst += _surface->pitch) { + decodeRLE(stream, scanLine, bytesPerscanLine, compressed); + + for (x = 0; x < width; x++) { + int m = 0x80 >> (x & 7), v = 0; + for (int i = nPlanes - 1; i >= 0; i--) { + v <<= 1; + v += (scanLine[i * bytesPerLine + (x >> 3)] & m) == 0 ? 0 : 1; + } + dst[x] = v; + } + } + } else { + // Known unsupported case: 1 plane and bpp < 8 (1, 2 or 4) + warning("Invalid PCX file (%d planes, %d bpp)", nPlanes, bitsPerPixel); + delete[] scanLine; + return false; + } + + delete[] scanLine; + + return true; +} + +void PCXDecoder::decodeRLE(Common::SeekableReadStream &stream, byte *dst, uint32 bytesPerscanLine, bool compressed) { + uint32 i = 0; + byte run, value; + + if (compressed) { + while (i < bytesPerscanLine) { + run = 1; + value = stream.readByte(); + if (value >= 0xc0) { + run = value & 0x3f; + value = stream.readByte(); + } + while (i < bytesPerscanLine && run--) + dst[i++] = value; + } + } else { + stream.read(dst, bytesPerscanLine); + } +} + +} // End of namespace Graphics diff --git a/graphics/decoders/pcx.h b/graphics/decoders/pcx.h new file mode 100644 index 0000000000..b25166b3d9 --- /dev/null +++ b/graphics/decoders/pcx.h @@ -0,0 +1,68 @@ +/* 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. + */ + +/** + * PCX decoder used in engines: + * - dreamweb + * - hugo + * - queen + * - tucker + */ + +#ifndef GRAPHICS_DECODERS_PCX_H +#define GRAPHICS_DECODERS_PCX_H + +#include "common/scummsys.h" +#include "common/str.h" +#include "graphics/decoders/image_decoder.h" + +namespace Common{ +class SeekableReadStream; +} + +namespace Graphics { + +struct PixelFormat; +struct Surface; + +class PCXDecoder : public ImageDecoder { +public: + PCXDecoder(); + virtual ~PCXDecoder(); + + // ImageDecoder API + void destroy(); + virtual bool loadStream(Common::SeekableReadStream &stream); + virtual const Surface *getSurface() const { return _surface; } + const byte *getPalette() const { return _palette; } + uint16 getPaletteColorCount() const { return _paletteColorCount; } + +private: + void decodeRLE(Common::SeekableReadStream &stream, byte *dst, uint32 bytesPerScanline, bool compressed); + + Surface *_surface; + byte *_palette; + uint16 _paletteColorCount; +}; + +} // End of namespace Graphics + +#endif diff --git a/graphics/decoders/pict.cpp b/graphics/decoders/pict.cpp index 7eddd3b893..b1d408ebc3 100644 --- a/graphics/decoders/pict.cpp +++ b/graphics/decoders/pict.cpp @@ -235,10 +235,13 @@ bool PICTDecoder::loadStream(Common::SeekableReadStream &stream) { // PICT v2 opcodes are two bytes uint16 opcode = stream.readUint16BE(); - if (opNum == 0 && opcode != 0x0011) - error("Cannot find PICT version opcode"); - else if (opNum == 1 && opcode != 0x0C00) - error("Cannot find PICT header opcode"); + if (opNum == 0 && opcode != 0x0011) { + warning("Cannot find PICT version opcode"); + return false; + } else if (opNum == 1 && opcode != 0x0C00) { + warning("Cannot find PICT header opcode"); + return false; + } // Since opcodes are word-aligned, we need to mark our starting // position here. @@ -292,12 +295,12 @@ struct PackBitsRectData { uint16 mode; }; -void PICTDecoder::unpackBitsRect(Common::SeekableReadStream &stream, bool hasPalette) { +void PICTDecoder::unpackBitsRect(Common::SeekableReadStream &stream, bool withPalette) { PackBitsRectData packBitsData; - packBitsData.pixMap = readPixMap(stream, !hasPalette); + packBitsData.pixMap = readPixMap(stream, !withPalette); // Read in the palette if there is one present - if (hasPalette) { + if (withPalette) { // See http://developer.apple.com/legacy/mac/library/documentation/mac/QuickDraw/QuickDraw-267.html stream.readUint32BE(); // seed stream.readUint16BE(); // flags @@ -469,10 +472,10 @@ void PICTDecoder::outputPixelBuffer(byte *&out, byte value, byte bitsPerPixel) { } } -void PICTDecoder::skipBitsRect(Common::SeekableReadStream &stream, bool hasPalette) { +void PICTDecoder::skipBitsRect(Common::SeekableReadStream &stream, bool withPalette) { // Step through a PackBitsRect/DirectBitsRect function - if (!hasPalette) + if (!withPalette) stream.readUint32BE(); uint16 rowBytes = stream.readUint16BE(); @@ -492,7 +495,7 @@ void PICTDecoder::skipBitsRect(Common::SeekableReadStream &stream, bool hasPalet stream.readUint16BE(); // pixelSize stream.skip(16); - if (hasPalette) { + if (withPalette) { stream.readUint32BE(); stream.readUint16BE(); stream.skip((stream.readUint16BE() + 1) * 8); @@ -543,7 +546,7 @@ void PICTDecoder::decodeCompressedQuickTime(Common::SeekableReadStream &stream) // Skip the matte and mask stream.skip(matteSize + maskSize); - + // Now we've reached the image descriptor, so read the relevant data from that uint32 idStart = stream.pos(); uint32 idSize = stream.readUint32BE(); diff --git a/graphics/decoders/pict.h b/graphics/decoders/pict.h index 417a7c5134..6f0d86c7a1 100644 --- a/graphics/decoders/pict.h +++ b/graphics/decoders/pict.h @@ -24,6 +24,7 @@ * @file * Image decoder used in engines: * - mohawk + * - pegasus * - sci */ @@ -87,9 +88,9 @@ private: bool _continueParsing; // Utility Functions - void unpackBitsRect(Common::SeekableReadStream &stream, bool hasPalette); + void unpackBitsRect(Common::SeekableReadStream &stream, bool withPalette); void unpackBitsLine(byte *out, uint32 length, Common::SeekableReadStream *stream, byte bitsPerPixel, byte bytesPerPixel); - void skipBitsRect(Common::SeekableReadStream &stream, bool hasPalette); + void skipBitsRect(Common::SeekableReadStream &stream, bool withPalette); void decodeCompressedQuickTime(Common::SeekableReadStream &stream); void outputPixelBuffer(byte *&out, byte value, byte bitsPerPixel); diff --git a/graphics/decoders/png.cpp b/graphics/decoders/png.cpp index 492c69779f..11e26162eb 100644 --- a/graphics/decoders/png.cpp +++ b/graphics/decoders/png.cpp @@ -20,86 +20,25 @@ * */ +// Since we need to work with libpng here, we need to allow all symbols +// to avoid compilation issues. +#define FORBIDDEN_SYMBOL_ALLOW_ALL +#include "common/scummsys.h" + +#ifdef USE_PNG +#include <png.h> +#endif + #include "graphics/decoders/png.h" #include "graphics/pixelformat.h" #include "graphics/surface.h" -#include "common/endian.h" -#include "common/memstream.h" #include "common/stream.h" -#include "common/types.h" -#include "common/util.h" -#include "common/zlib.h" - -// PNG decoder, based on the W3C specs: -// http://www.w3.org/TR/PNG/ -// Parts of the code have been adapted from LodePNG, by Lode Vandevenne: -// http://members.gamedev.net/lode/projects/LodePNG/ - -/* -LodePNG version 20101211 - -Copyright (c) 2005-2010 Lode Vandevenne - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - - 3. This notice may not be removed or altered from any source - distribution. -*/ namespace Graphics { -enum PNGChunks { - // == Critical chunks ===================================================== - kChunkIHDR = MKTAG('I','H','D','R'), // Image header - kChunkIDAT = MKTAG('I','D','A','T'), // Image data - kChunkPLTE = MKTAG('P','L','T','E'), // Palette - kChunkIEND = MKTAG('I','E','N','D'), // Image trailer - // == Ancillary chunks ==================================================== - kChunktRNS = MKTAG('t','R','N','S') // Transparency - // All of the other ancillary chunks are ignored. They're added here for - // reference only. - // cHRM - Primary chromacities and white point - // gAMA - Image gamma - // iCCP - Embedded ICC profile - // sBIT - Significant bits - // sRGB - Standard RGB color space - // tEXT - Textual data - // sTXt - Compressed textual data - // iTXt - International textual data - // bKGD - Background color - // hIST - Image histogram - // pHYs - Physical pixel dimensions - // sPLT - Suggested palette - // tIME - Image last-modification time -}; - -// Refer to http://www.w3.org/TR/PNG/#9Filters -enum PNGFilters { - kFilterNone = 0, - kFilterSub = 1, - kFilterUp = 2, - kFilterAverage = 3, - kFilterPaeth = 4 -}; - -PNGDecoder::PNGDecoder() : _compressedBuffer(0), _compressedBufferSize(0), - _transparentColorSpecified(false), _outputSurface(0), _paletteEntries(0) { +PNGDecoder::PNGDecoder() : _outputSurface(0), _palette(0), _paletteColorCount(0) { } PNGDecoder::~PNGDecoder() { @@ -112,14 +51,41 @@ void PNGDecoder::destroy() { delete _outputSurface; _outputSurface = 0; } + delete[] _palette; + _palette = NULL; +} + +#ifdef USE_PNG +// libpng-error-handling: +void pngError(png_structp pngptr, png_const_charp errorMsg) { + error("%s", errorMsg); +} - _paletteEntries = 0; +void pngWarning(png_structp pngptr, png_const_charp warningMsg) { + warning("%s", warningMsg); } +// libpng-I/O-helper: +void pngReadFromStream(png_structp pngPtr, png_bytep data, png_size_t length) { + void *readIOptr = png_get_io_ptr(pngPtr); + Common::SeekableReadStream *stream = (Common::SeekableReadStream *)readIOptr; + stream->read(data, length); +} +#endif + +/* + * This code is based on Broken Sword 2.5 engine + * + * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer + * + * Licensed under GNU GPL v2 + * + */ + bool PNGDecoder::loadStream(Common::SeekableReadStream &stream) { +#ifdef USE_PNG destroy(); - uint32 chunkLength = 0, chunkType = 0; _stream = &stream; // First, check the PNG signature @@ -132,374 +98,144 @@ bool PNGDecoder::loadStream(Common::SeekableReadStream &stream) { return false; } - // Start reading chunks till we reach an IEND chunk - while (chunkType != kChunkIEND) { - // The chunk length does not include the type or CRC bytes - chunkLength = _stream->readUint32BE(); - chunkType = _stream->readUint32BE(); - - switch (chunkType) { - case kChunkIHDR: - readHeaderChunk(); - break; - case kChunkIDAT: - if (_compressedBufferSize == 0) { - _compressedBufferSize += chunkLength; - _compressedBuffer = (byte *)malloc(_compressedBufferSize); - _stream->read(_compressedBuffer, chunkLength); - } else { - // Expand the buffer - uint32 prevSize = _compressedBufferSize; - _compressedBufferSize += chunkLength; - byte *tmp = new byte[prevSize]; - memcpy(tmp, _compressedBuffer, prevSize); - free(_compressedBuffer); - _compressedBuffer = (byte *)malloc(_compressedBufferSize); - memcpy(_compressedBuffer, tmp, prevSize); - delete[] tmp; - _stream->read(_compressedBuffer + prevSize, chunkLength); - } - break; - case kChunkPLTE: // only available in indexed PNGs - if (_header.colorType != kIndexed) - error("A palette chunk has been found in a non-indexed PNG file"); - if (chunkLength % 3 != 0) - error("Palette chunk not divisible by 3"); - - _paletteEntries = chunkLength / 3; - _stream->read(_palette, _paletteEntries * 3); - memset(_paletteTransparency, 0xff, sizeof(_paletteTransparency)); - break; - case kChunkIEND: - // End of stream - break; - case kChunktRNS: - readTransparencyChunk(chunkLength); - break; - default: - // Skip the chunk content - _stream->skip(chunkLength); - break; - } - - if (chunkType != kChunkIEND) - _stream->skip(4); // skip the chunk CRC checksum + // The following is based on the guide provided in: + //http://www.libpng.org/pub/png/libpng-1.2.5-manual.html#section-3 + //http://www.libpng.org/pub/png/libpng-1.4.0-manual.pdf + // along with the png-loading code used in the sword25-engine. + png_structp pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!pngPtr) { + delete _stream; + return false; + } + png_infop infoPtr = png_create_info_struct(pngPtr); + if (!infoPtr) { + png_destroy_read_struct(&pngPtr, NULL, NULL); + delete _stream; + return false; + } + png_infop endInfo = png_create_info_struct(pngPtr); + if (!endInfo) { + png_destroy_read_struct(&pngPtr, &infoPtr, NULL); + delete _stream; + return false; } - // We no longer need the file stream, thus close it here - _stream = 0; - - // Unpack the compressed buffer - Common::MemoryReadStream *compData = new Common::MemoryReadStream(_compressedBuffer, _compressedBufferSize, DisposeAfterUse::YES); - _imageData = Common::wrapCompressedReadStream(compData); + png_set_error_fn(pngPtr, NULL, pngError, pngWarning); + // TODO: The manual says errors should be handled via setjmp - // Construct the final image - constructImage(); + png_set_read_fn(pngPtr, _stream, pngReadFromStream); + png_set_crc_action(pngPtr, PNG_CRC_DEFAULT, PNG_CRC_WARN_USE); + // We already verified the PNG-header + png_set_sig_bytes(pngPtr, 8); - // Close the uncompressed stream, which will also delete the memory stream, - // and thus the original compressed buffer - delete _imageData; + // Read PNG header + png_read_info(pngPtr, infoPtr); - return true; -} + // No handling for unknown chunks yet. + int bitDepth, colorType, width, height, interlaceType; + png_uint_32 w, h; + png_get_IHDR(pngPtr, infoPtr, &w, &h, &bitDepth, &colorType, &interlaceType, NULL, NULL); + width = w; + height = h; -/** - * Paeth predictor, used by PNG filter type 4 - * The parameters are of signed 16-bit integers, but should come - * from unsigned chars. The integers are only needed to make - * the paeth calculation correct. - * - * Taken from lodePNG, with a slight patch: - * http://www.atalasoft.com/cs/blogs/stevehawley/archive/2010/02/23/libpng-you-re-doing-it-wrong.aspx - */ -byte PNGDecoder::paethPredictor(int16 a, int16 b, int16 c) { - int16 pa = ABS<int16>(b - c); - int16 pb = ABS<int16>(a - c); - int16 pc = ABS<int16>(a + b - c - c); - - if (pa <= MIN<int16>(pb, pc)) - return (byte)a; - else if (pb <= pc) - return (byte)b; - else - return (byte)c; -} + // Allocate memory for the final image data. + // To keep memory framentation low this happens before allocating memory for temporary image data. + _outputSurface = new Graphics::Surface(); -/** - * Unfilters a filtered PNG scan line. - * PNG filters are defined in: http://www.w3.org/TR/PNG/#9Filters - * Note that filters are always applied to bytes - * - * Taken from lodePNG - */ -void PNGDecoder::unfilterScanLine(byte *dest, const byte *scanLine, const byte *prevLine, uint16 byteWidth, byte filterType, uint16 length) { - uint16 i; - - switch (filterType) { - case kFilterNone: // no change - for (i = 0; i < length; i++) - dest[i] = scanLine[i]; - break; - case kFilterSub: // add the bytes to the left - for (i = 0; i < byteWidth; i++) - dest[i] = scanLine[i]; - for (i = byteWidth; i < length; i++) - dest[i] = scanLine[i] + dest[i - byteWidth]; - break; - case kFilterUp: // add the bytes of the above scanline - if (prevLine) { - for (i = 0; i < length; i++) - dest[i] = scanLine[i] + prevLine[i]; - } else { - for (i = 0; i < length; i++) - dest[i] = scanLine[i]; + // Images of all color formats except PNG_COLOR_TYPE_PALETTE + // will be transformed into ARGB images + if (colorType == PNG_COLOR_TYPE_PALETTE && !png_get_valid(pngPtr, infoPtr, PNG_INFO_tRNS)) { + int numPalette = 0; + png_colorp palette = NULL; + uint32 success = png_get_PLTE(pngPtr, infoPtr, &palette, &numPalette); + if (success != PNG_INFO_PLTE) { + png_destroy_read_struct(&pngPtr, &infoPtr, NULL); + return false; } - break; - case kFilterAverage: // average value of the left and top left - if (prevLine) { - for (i = 0; i < byteWidth; i++) - dest[i] = scanLine[i] + prevLine[i] / 2; - for (i = byteWidth; i < length; i++) - dest[i] = scanLine[i] + ((dest[i - byteWidth] + prevLine[i]) / 2); - } else { - for (i = 0; i < byteWidth; i++) - dest[i] = scanLine[i]; - for (i = byteWidth; i < length; i++) - dest[i] = scanLine[i] + dest[i - byteWidth] / 2; + _paletteColorCount = numPalette; + _palette = new byte[_paletteColorCount * 3]; + for (int i = 0; i < _paletteColorCount; i++) { + _palette[(i * 3)] = palette[i].red; + _palette[(i * 3) + 1] = palette[i].green; + _palette[(i * 3) + 2] = palette[i].blue; + } - break; - case kFilterPaeth: // Paeth filter: http://www.w3.org/TR/PNG/#9Filter-type-4-Paeth - if (prevLine) { - for(i = 0; i < byteWidth; i++) - dest[i] = (scanLine[i] + prevLine[i]); // paethPredictor(0, prevLine[i], 0) is always prevLine[i] - for(i = byteWidth; i < length; i++) - dest[i] = (scanLine[i] + paethPredictor(dest[i - byteWidth], prevLine[i], prevLine[i - byteWidth])); - } else { - for(i = 0; i < byteWidth; i++) - dest[i] = scanLine[i]; - for(i = byteWidth; i < length; i++) - dest[i] = (scanLine[i] + dest[i - byteWidth]); // paethPredictor(dest[i - byteWidth], 0, 0) is always dest[i - byteWidth] + _outputSurface->create(width, height, Graphics::PixelFormat::createFormatCLUT8()); + png_set_packing(pngPtr); + } else { + _outputSurface->create(width, height, Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0)); + if (!_outputSurface->pixels) { + error("Could not allocate memory for output image."); } - break; - default: - error("Unknown line filter"); - } + if (bitDepth == 16) + png_set_strip_16(pngPtr); + if (bitDepth < 8) + png_set_expand(pngPtr); + if (png_get_valid(pngPtr, infoPtr, PNG_INFO_tRNS)) + png_set_expand(pngPtr); + if (colorType == PNG_COLOR_TYPE_GRAY || + colorType == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb(pngPtr); + + // PNGs are Big-Endian: +#ifdef SCUMM_LITTLE_ENDIAN + png_set_bgr(pngPtr); + png_set_swap_alpha(pngPtr); + if (colorType != PNG_COLOR_TYPE_RGB_ALPHA) + png_set_filler(pngPtr, 0xff, PNG_FILLER_BEFORE); +#else + if (colorType != PNG_COLOR_TYPE_RGB_ALPHA) + png_set_filler(pngPtr, 0xff, PNG_FILLER_AFTER); +#endif -} + } -int PNGDecoder::getBytesPerPixel() const { - return (getNumColorChannels() * _header.bitDepth + 7) / 8; -} + // After the transformations have been registered, the image data is read again. + png_set_interlace_handling(pngPtr); + png_read_update_info(pngPtr, infoPtr); + png_get_IHDR(pngPtr, infoPtr, &w, &h, &bitDepth, &colorType, NULL, NULL, NULL); + width = w; + height = h; + + if (interlaceType == PNG_INTERLACE_NONE) { + // PNGs without interlacing can simply be read row by row. + for (int i = 0; i < height; i++) { + png_read_row(pngPtr, (png_bytep)_outputSurface->getBasePtr(0, i), NULL); + } + } else { + // PNGs with interlacing require us to allocate an auxillary + // buffer with pointers to all row starts. -void PNGDecoder::constructImage() { - assert (_header.bitDepth != 0); - - int bytesPerPixel = getBytesPerPixel(); - int pitch = bytesPerPixel * _header.width; - byte *unfilteredSurface = new byte[pitch * _header.height]; - byte *dest = unfilteredSurface; - uint16 scanLineWidth = (_header.width * getNumColorChannels() * _header.bitDepth + 7) / 8; - byte *scanLine = new byte[scanLineWidth]; - byte *prevLine = 0; - - switch(_header.interlaceType) { - case kNonInterlaced: - for (uint16 y = 0; y < _header.height; y++) { - byte filterType = _imageData->readByte(); - _imageData->read(scanLine, scanLineWidth); - unfilterScanLine(dest, scanLine, prevLine, bytesPerPixel, filterType, scanLineWidth); - prevLine = dest; - dest += pitch; + // Allocate row pointer buffer + png_bytep *rowPtr = new png_bytep[height]; + if (!rowPtr) { + error("Could not allocate memory for row pointers."); } - break; - case kInterlaced: - // Theoretically, this shouldn't be needed, as interlacing is only - // useful for web images. Interlaced PNG images require more complex - // handling, so unless having support for such images is needed, there - // is no reason to add support for them. - error("TODO: Support for interlaced PNG images"); - break; - } - delete[] scanLine; + // Initialize row pointers + for (int i = 0; i < height; i++) + rowPtr[i] = (png_bytep)_outputSurface->getBasePtr(0, i); - constructOutput(unfilteredSurface); - delete[] unfilteredSurface; -} + // Read image data + png_read_image(pngPtr, rowPtr); -Graphics::PixelFormat PNGDecoder::findPixelFormat() const { - // Try to find the best pixel format based on what we have here - // Which is basically 8bpp for paletted non-transparent - // and 32bpp for everything else - - switch (_header.colorType) { - case kIndexed: - if (!_transparentColorSpecified) - return Graphics::PixelFormat::createFormatCLUT8(); - // fall through - case kGrayScale: - case kTrueColor: - case kGrayScaleWithAlpha: - case kTrueColorWithAlpha: - // We'll go with standard RGBA 32-bit - return Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0); + // Free row pointer buffer + delete[] rowPtr; } - error("Unknown PNG color type"); - return Graphics::PixelFormat(); -} + // Read additional data at the end. + png_read_end(pngPtr, NULL); -void PNGDecoder::constructOutput(const byte *surface) { - _outputSurface = new Graphics::Surface(); - _outputSurface->create(_header.width, _header.height, findPixelFormat()); - - const byte *src = surface; - byte a = 0xFF; - int bytesPerPixel = getBytesPerPixel(); - - if (_header.colorType != kIndexed) { - if (_header.colorType == kTrueColor || - _header.colorType == kTrueColorWithAlpha) { - if (bytesPerPixel != 3 && bytesPerPixel != 4) - error("Unsupported truecolor PNG format"); - } else if (_header.colorType == kGrayScale || - _header.colorType == kGrayScaleWithAlpha) { - if (bytesPerPixel != 1 && bytesPerPixel != 2) - error("Unsupported grayscale PNG format"); - } - - for (uint16 i = 0; i < _outputSurface->h; i++) { - for (uint16 j = 0; j < _outputSurface->w; j++) { - uint32 result = 0; - - switch (bytesPerPixel) { - case 1: // Grayscale - if (_transparentColorSpecified) - a = (src[0] == _transparentColor[0]) ? 0 : 0xFF; - result = _outputSurface->format.ARGBToColor(a, src[0], src[0], src[0]); - break; - case 2: // Grayscale + alpha - result = _outputSurface->format.ARGBToColor(src[1], src[0], src[0], src[0]); - break; - case 3: // RGB - if (_transparentColorSpecified) { - bool isTransparentColor = (src[0] == _transparentColor[0] && - src[1] == _transparentColor[1] && - src[2] == _transparentColor[2]); - a = isTransparentColor ? 0 : 0xFF; - } - - result = _outputSurface->format.ARGBToColor(a, src[0], src[1], src[2]); - break; - case 4: // RGBA - result = _outputSurface->format.ARGBToColor(src[3], src[0], src[1], src[2]); - break; - } - - *((uint32 *)_outputSurface->getBasePtr(j, i)) = result; - src += bytesPerPixel; - } - } - } else { - uint32 mask = (0xff >> (8 - _header.bitDepth)) << (8 - _header.bitDepth); - - // Convert the indexed surface to the target pixel format - for (uint16 i = 0; i < _outputSurface->h; i++) { - int data = 0; - int bitCount = 8; - const byte *src1 = src; - - for (uint16 j = 0; j < _outputSurface->w; j++) { - if (bitCount == 8) { - data = *src; - src++; - } - - byte index = (data & mask) >> (8 - _header.bitDepth); - data = (data << _header.bitDepth) & 0xff; - bitCount -= _header.bitDepth; - - if (bitCount == 0) - bitCount = 8; - - if (_transparentColorSpecified) { - byte r = _palette[index * 3 + 0]; - byte g = _palette[index * 3 + 1]; - byte b = _palette[index * 3 + 2]; - a = _paletteTransparency[index]; - *((uint32 *)_outputSurface->getBasePtr(j, i)) = _outputSurface->format.ARGBToColor(a, r, g, b); - } else { - *((byte *)_outputSurface->getBasePtr(j, i)) = index; - } - } - - src = src1 + _outputSurface->w; - } - } -} + // Destroy libpng structures + png_destroy_read_struct(&pngPtr, &infoPtr, &endInfo); -void PNGDecoder::readHeaderChunk() { - _header.width = _stream->readUint32BE(); - _header.height = _stream->readUint32BE(); - _header.bitDepth = _stream->readByte(); - if (_header.bitDepth > 8) - error("Only PNGs with a bit depth of 1-8 bits are supported (i.e. PNG24)"); - _header.colorType = (PNGColorType)_stream->readByte(); - _header.compressionMethod = _stream->readByte(); - // Compression methods: http://www.w3.org/TR/PNG/#10Compression - // Only compression method 0 (deflate) is documented and supported - if (_header.compressionMethod != 0) - error("Unknown PNG compression method: %d", _header.compressionMethod); - _header.filterMethod = _stream->readByte(); - // Filter methods: http://www.w3.org/TR/PNG/#9Filters - // Only filter method 0 is documented and supported - if (_header.filterMethod != 0) - error("Unknown PNG filter method: %d", _header.filterMethod); - _header.interlaceType = (PNGInterlaceType)_stream->readByte(); -} - -byte PNGDecoder::getNumColorChannels() const { - switch (_header.colorType) { - case kGrayScale: - return 1; // Gray - case kTrueColor: - return 3; // RGB - case kIndexed: - return 1; // Indexed - case kGrayScaleWithAlpha: - return 2; // Gray + Alpha - case kTrueColorWithAlpha: - return 4; // RGBA - default: - error("Unknown color type"); - } -} + // We no longer need the file stream, thus close it here + _stream = 0; -void PNGDecoder::readTransparencyChunk(uint32 chunkLength) { - _transparentColorSpecified = true; - - switch(_header.colorType) { - case kGrayScale: - _transparentColor[0] = _stream->readUint16BE(); - _transparentColor[1] = _transparentColor[0]; - _transparentColor[2] = _transparentColor[0]; - break; - case kTrueColor: - _transparentColor[0] = _stream->readUint16BE(); - _transparentColor[1] = _stream->readUint16BE(); - _transparentColor[2] = _stream->readUint16BE(); - break; - case kIndexed: - _stream->read(_paletteTransparency, chunkLength); - - // A transparency chunk may have less entries - // than the palette entries. The remaining ones - // are unmodified (set to 255). Check here: - // http://www.w3.org/TR/PNG/#11tRNS - break; - default: - error("Transparency chunk found in a PNG that has a separate transparency channel"); - } + return true; +#else + return false; +#endif } } // End of Graphics namespace diff --git a/graphics/decoders/png.h b/graphics/decoders/png.h index ca204f6dd3..5e608eb7b1 100644 --- a/graphics/decoders/png.h +++ b/graphics/decoders/png.h @@ -23,34 +23,14 @@ /* * PNG decoder used in engines: * - sword25 + * - wintermute * Dependencies: - * - zlib + * - libpng */ #ifndef GRAPHICS_PNG_H #define GRAPHICS_PNG_H -// PNG decoder, based on the W3C specs: -// http://www.w3.org/TR/PNG/ -// Parts of the code have been adapted from LodePNG, by Lode Vandevenne: -// http://members.gamedev.net/lode/projects/LodePNG/ - -// All the numbers are BE: http://www.w3.org/TR/PNG/#7Integers-and-byte-order - -// Note: At the moment, this decoder only supports non-interlaced images, and -// does not support truecolor/grayscale images with 16bit depth. -// -// Theoretically, interlaced images shouldn't be needed for games, as -// interlacing is only useful for images in websites. -// -// PNG images with 16bit depth (i.e. 48bit images) are quite rare, and can -// theoretically contain more than 16.7 millions of colors (the so-called "deep -// color" representation). In essence, each of the R, G, B and A components in -// them is specified with 2 bytes, instead of 1. However, the PNG specification -// always refers to color components with 1 byte each, so this part of the spec -// is a bit unclear. For now, these won't be supported, until a suitable sample -// is found. - #include "common/scummsys.h" #include "common/textconsole.h" #include "graphics/decoders/image_decoder.h" @@ -73,62 +53,13 @@ public: void destroy(); const Graphics::Surface *getSurface() const { return _outputSurface; } const byte *getPalette() const { return _palette; } - uint16 getPaletteColorCount() const { return _paletteEntries; } - + uint16 getPaletteColorCount() const { return _paletteColorCount; } private: - enum PNGColorType { - kGrayScale = 0, // bit depths: 1, 2, 4, 8, 16 - kTrueColor = 2, // bit depths: 8, 16 - kIndexed = 3, // bit depths: 1, 2, 4, 8 - kGrayScaleWithAlpha = 4, // bit depths: 8, 16 - kTrueColorWithAlpha = 6 // bit depths: 8, 16 - }; - - enum PNGInterlaceType { - kNonInterlaced = 0, - kInterlaced = 1 - }; - - struct PNGHeader { - uint32 width; - uint32 height; - byte bitDepth; - PNGColorType colorType; - byte compressionMethod; - byte filterMethod; - PNGInterlaceType interlaceType; - }; - - void readHeaderChunk(); - byte getNumColorChannels() const; - - void readPaletteChunk(); - void readTransparencyChunk(uint32 chunkLength); - - void constructImage(); - void unfilterScanLine(byte *dest, const byte *scanLine, const byte *prevLine, uint16 byteWidth, byte filterType, uint16 length); - byte paethPredictor(int16 a, int16 b, int16 c); - - // The original file stream Common::SeekableReadStream *_stream; - // The unzipped image data stream - Common::SeekableReadStream *_imageData; - - PNGHeader _header; - - byte _palette[256 * 3]; // RGB - byte _paletteTransparency[256]; - uint16 _paletteEntries; - uint16 _transparentColor[3]; - bool _transparentColorSpecified; - - byte *_compressedBuffer; - uint32 _compressedBufferSize; + byte *_palette; + uint16 _paletteColorCount; Graphics::Surface *_outputSurface; - Graphics::PixelFormat findPixelFormat() const; - int getBytesPerPixel() const; - void constructOutput(const byte *surface); }; } // End of namespace Graphics diff --git a/graphics/decoders/tga.cpp b/graphics/decoders/tga.cpp new file mode 100644 index 0000000000..c3b9d84055 --- /dev/null +++ b/graphics/decoders/tga.cpp @@ -0,0 +1,427 @@ +/* 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. + */ + +/* Based on code from xoreos https://github.com/DrMcCoy/xoreos/ + * relicensed under GPLv2+ with permission from DrMcCoy and clone2727 + */ + +#include "common/util.h" +#include "common/stream.h" +#include "common/textconsole.h" +#include "common/error.h" + +#include "graphics/decoders/tga.h" + +namespace Graphics { + +TGADecoder::TGADecoder() { + _colorMapSize = 0; + _colorMapOrigin = 0; + _colorMapLength = 0; + _colorMapEntryLength = 0; + _colorMap = NULL; +} + +TGADecoder::~TGADecoder() { + destroy(); +} + +void TGADecoder::destroy() { + _surface.free(); + delete[] _colorMap; +} + +bool TGADecoder::loadStream(Common::SeekableReadStream &tga) { + byte imageType, pixelDepth; + bool success; + success = readHeader(tga, imageType, pixelDepth); + if (success) { + switch (imageType) { + case TYPE_BW: + case TYPE_TRUECOLOR: + success = readData(tga, imageType, pixelDepth); + break; + case TYPE_RLE_BW: + case TYPE_RLE_TRUECOLOR: + case TYPE_RLE_CMAP: + success = readDataRLE(tga, imageType, pixelDepth); + break; + case TYPE_CMAP: + success = readDataColorMapped(tga, imageType, pixelDepth); + break; + default: + success = false; + break; + } + } + if (tga.err() || !success) { + warning("Failed reading TGA-file"); + return false; + } + return success; +} + +bool TGADecoder::readHeader(Common::SeekableReadStream &tga, byte &imageType, byte &pixelDepth) { + if (!tga.seek(0)) { + warning("Failed reading TGA-file"); + return false; + } + + // TGAs have an optional "id" string in the header + uint32 idLength = tga.readByte(); + + // Number of colors in the color map / palette + int hasColorMap = tga.readByte(); + + // Image type. See header for numeric constants + imageType = tga.readByte(); + + switch (imageType) { + case TYPE_CMAP: + case TYPE_TRUECOLOR: + case TYPE_BW: + case TYPE_RLE_CMAP: + case TYPE_RLE_TRUECOLOR: + case TYPE_RLE_BW: + break; + default: + warning("Unsupported image type: %d", imageType); + return false; + } + + // Color map specifications + if (hasColorMap == 0) { + tga.skip(5); + } else { + _colorMapOrigin = tga.readUint16LE(); + _colorMapLength = tga.readUint16LE(); + _colorMapEntryLength = tga.readByte(); + } + // Origin-defintions + tga.skip(2 + 2); + + // Image dimensions + _surface.w = tga.readUint16LE(); + _surface.h = tga.readUint16LE(); + + // Bits per pixel + pixelDepth = tga.readByte(); + _surface.format.bytesPerPixel = pixelDepth / 8; + + // Image descriptor + byte imgDesc = tga.readByte(); + int attributeBits = imgDesc & 0x0F; + assert((imgDesc & 0x10) == 0); + _originTop = (imgDesc & 0x20); + + // Interleaving is not handled at this point + //int interleave = (imgDesc & 0xC); + if (imageType == TYPE_CMAP || imageType == TYPE_RLE_CMAP) { + if (pixelDepth == 8) { + _format = PixelFormat::createFormatCLUT8(); + } else { + warning("Unsupported index-depth: %d", pixelDepth); + return false; + } + } else if (imageType == TYPE_TRUECOLOR || imageType == TYPE_RLE_TRUECOLOR) { + if (pixelDepth == 24) { + _format = PixelFormat(3, 8, 8, 8, 0, 16, 8, 0, 0); + } else if (pixelDepth == 32) { + // HACK: According to the spec, attributeBits should determine the amount + // of alpha-bits, however, as the game files that use this decoder seems + // to ignore that fact, we force the amount to 8 for 32bpp files for now. + _format = PixelFormat(4, 8, 8, 8, /* attributeBits */ 8, 16, 8, 0, 24); + } else if (pixelDepth == 16 && imageType == TYPE_TRUECOLOR) { + // 16bpp TGA is ARGB1555 + _format = PixelFormat(2, 5, 5, 5, attributeBits, 10, 5, 0, 15); + } else { + warning("Unsupported pixel depth: %d, %d", imageType, pixelDepth); + return false; + } + } else if (imageType == TYPE_BW || TYPE_RLE_BW) { + if (pixelDepth == 8) { + _format = PixelFormat(4, 8, 8, 8, 0, 16, 8, 0, 0); + } else { + warning("Unsupported pixel depth: %d, %d", imageType, pixelDepth); + return false; + } + + } else { + warning("Unsupported image type: %d", imageType); + return false; + } + + // Skip the id string + tga.skip(idLength); + + if (hasColorMap) { + return readColorMap(tga, imageType, pixelDepth); + } + return true; +} + +bool TGADecoder::readColorMap(Common::SeekableReadStream &tga, byte imageType, byte pixelDepth) { + _colorMap = new byte[3 * _colorMapLength]; + for (int i = 0; i < _colorMapLength * 3; i += 3) { + byte r, g, b; + if (_colorMapEntryLength == 32) { + byte a; + PixelFormat format(4, 8, 8, 8, 0, 16, 8, 0, 24); + uint32 color = tga.readUint32LE(); + format.colorToARGB(color, a, r, g, b); + } else if (_colorMapEntryLength == 24) { + r = tga.readByte(); + g = tga.readByte(); + b = tga.readByte(); + } else if (_colorMapEntryLength == 16) { + byte a; + PixelFormat format(2, 5, 5, 5, 0, 10, 5, 0, 15); + uint16 color = tga.readUint16LE(); + format.colorToARGB(color, a, r, g, b); + } else { + warning("Unsupported image type: %d", imageType); + r = g = b = 0; + } +#ifdef SCUMM_LITTLE_ENDIAN + _colorMap[i] = r; + _colorMap[i + 1] = g; + _colorMap[i + 2] = b; +#else + _colorMap[i] = b; + _colorMap[i + 1] = g; + _colorMap[i + 2] = r; +#endif + } + return true; +} + +// Additional information found from http://paulbourke.net/dataformats/tga/ +// With some details from the link referenced in the header. +bool TGADecoder::readData(Common::SeekableReadStream &tga, byte imageType, byte pixelDepth) { + // TrueColor + if (imageType == TYPE_TRUECOLOR) { + _surface.create(_surface.w, _surface.h, _format); + + if (pixelDepth == 16) { + for (int i = 0; i < _surface.h; i++) { + uint16 *dst; + if (!_originTop) { + dst = (uint16 *)_surface.getBasePtr(0, _surface.h - i - 1); + } else { + dst = (uint16 *)_surface.getBasePtr(0, i); + } + for (int j = 0; j < _surface.w; j++) { + *dst++ = tga.readUint16LE(); + } + } + } else if (pixelDepth == 32) { + for (int i = 0; i < _surface.h; i++) { + uint32 *dst; + if (!_originTop) { + dst = (uint32 *)_surface.getBasePtr(0, _surface.h - i - 1); + } else { + dst = (uint32 *)_surface.getBasePtr(0, i); + } + for (int j = 0; j < _surface.w; j++) { + *dst++ = tga.readUint32LE(); + } + } + } else if (pixelDepth == 24) { + for (int i = 0; i < _surface.h; i++) { + byte *dst; + if (!_originTop) { + dst = (byte *)_surface.getBasePtr(0, _surface.h - i - 1); + } else { + dst = (byte *)_surface.getBasePtr(0, i); + } + for (int j = 0; j < _surface.w; j++) { + byte r = tga.readByte(); + byte g = tga.readByte(); + byte b = tga.readByte(); +#ifdef SCUMM_LITTLE_ENDIAN + *dst++ = r; + *dst++ = g; + *dst++ = b; +#else + *dst++ = b; + *dst++ = g; + *dst++ = r; +#endif + } + } + } + // Black/White + } else if (imageType == TYPE_BW) { + _surface.create(_surface.w, _surface.h, _format); + + byte *data = (byte *)_surface.pixels; + uint32 count = _surface.w * _surface.h; + + while (count-- > 0) { + byte g = tga.readByte(); + *data++ = g; + *data++ = g; + *data++ = g; + *data++ = g; + } + } + return true; +} + +bool TGADecoder::readDataColorMapped(Common::SeekableReadStream &tga, byte imageType, byte indexDepth) { + // Color-mapped + if (imageType == TYPE_CMAP) { + _surface.create(_surface.w, _surface.h, _format); + if (indexDepth == 8) { + for (int i = 0; i < _surface.h; i++) { + byte *dst; + if (!_originTop) { + dst = (byte *)_surface.getBasePtr(0, _surface.h - i - 1); + } else { + dst = (byte *)_surface.getBasePtr(0, i); + } + for (int j = 0; j < _surface.w; j++) { + byte index = tga.readByte(); + *dst++ = index; + } + } + } else if (indexDepth == 16) { + warning("16 bit indexes not supported"); + return false; + } + } else { + return false; + } + return true; +} + +bool TGADecoder::readDataRLE(Common::SeekableReadStream &tga, byte imageType, byte pixelDepth) { + // RLE-TrueColor / RLE-Black/White + if (imageType == TYPE_RLE_TRUECOLOR || imageType == TYPE_RLE_BW || imageType == TYPE_RLE_CMAP) { + _surface.create(_surface.w, _surface.h, _format); + uint32 count = _surface.w * _surface.h; + byte *data = (byte *)_surface.pixels; + + while (count > 0) { + uint32 header = tga.readByte(); + byte type = (header & 0x80) >> 7; + uint32 rleCount = (header & 0x7F) + 1; + + // RLE-packet + if (type == 1) { + if (pixelDepth == 32 && imageType == TYPE_RLE_TRUECOLOR) { + uint32 color = tga.readUint32LE(); + while (rleCount-- > 0) { + *((uint32 *)data) = color; + data += 4; + count--; + } + } else if (pixelDepth == 24 && imageType == TYPE_RLE_TRUECOLOR) { + byte r = tga.readByte(); + byte g = tga.readByte(); + byte b = tga.readByte(); + while (rleCount-- > 0) { +#ifdef SCUMM_LITTLE_ENDIAN + *data++ = r; + *data++ = g; + *data++ = b; +#else + *data++ = b; + *data++ = g; + *data++ = r; +#endif + count--; + } + } else if (pixelDepth == 8 && imageType == TYPE_RLE_BW) { + byte color = tga.readByte(); + while (rleCount-- > 0) { + *data++ = color; + *data++ = color; + *data++ = color; + *data++ = color; + count--; + } + } else if (pixelDepth == 8 && imageType == TYPE_RLE_CMAP) { + byte index = tga.readByte(); + while (rleCount-- > 0) { + *data++ = index; + count--; + } + } else { + warning("Unhandled pixel-depth for image-type 10"); + return false; + } + // Raw-packet + } else if (type == 0) { + if (pixelDepth == 32 && imageType == TYPE_RLE_TRUECOLOR) { + while (rleCount-- > 0) { + uint32 color = tga.readUint32LE(); + *((uint32 *)data) = color; + data += 4; + count--; + } + } else if (pixelDepth == 24 && imageType == TYPE_RLE_TRUECOLOR) { + while (rleCount-- > 0) { + byte r = tga.readByte(); + byte g = tga.readByte(); + byte b = tga.readByte(); +#ifdef SCUMM_LITTLE_ENDIAN + *data++ = r; + *data++ = g; + *data++ = b; +#else + *data++ = b; + *data++ = g; + *data++ = r; +#endif + count--; + } + } else if (pixelDepth == 8 && imageType == TYPE_RLE_BW) { + while (rleCount-- > 0) { + byte color = tga.readByte(); + *data++ = color; + *data++ = color; + *data++ = color; + *data++ = color; + count--; + } + } else if (pixelDepth == 8 && imageType == TYPE_RLE_CMAP) { + while (rleCount-- > 0) { + byte index = tga.readByte(); + *data++ = index; + count--; + } + } else { + warning("Unhandled pixel-depth for image-type 10"); + return false; + } + } else { + warning("Unknown header for RLE-packet %d", type); + return false; + } + } + } else { + return false; + } + return true; +} + +} // End of namespace Graphics diff --git a/graphics/decoders/tga.h b/graphics/decoders/tga.h new file mode 100644 index 0000000000..d8ccf8f766 --- /dev/null +++ b/graphics/decoders/tga.h @@ -0,0 +1,98 @@ +/* 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. + */ + +/* Based on code from eos https://github.com/DrMcCoy/xoreos/ + * relicensed under GPLv2+ with permission from DrMcCoy and clone2727 + */ + +/* + * TGA decoder used in engines: + * - wintermute + */ + +#ifndef GRAPHICS_DECODERS_TGA_H +#define GRAPHICS_DECODERS_TGA_H + +#include "graphics/surface.h" +#include "graphics/decoders/image_decoder.h" + +namespace Common { +class SeekableReadStream; +} + +namespace Graphics { + +/** TarGa image-decoder + * The following variations of TGA are supported: + * - Type 1 - Color-mapped images in 16/24/32 bpp with 8 bit indexes + * - Type 2 - 16/24/32 bpp Top AND Bottom origined. + * - Type 3 - Black/White images, 8bpp. + * - Type 9 - RLE-encoded color-mapped images. (8 bit indexes only) + * - Type 10 - RLE-encoded TrueColor, 24/32bpp. + * - Type 11 - RLE-encoded Black/White, 8bpp. + * + * No images are returned with a palette, instead they are converted + * to 16 bpp for Type 1, or 32 bpp for Black/White-images. + */ +class TGADecoder : public ImageDecoder { +public: + TGADecoder(); + virtual ~TGADecoder(); + virtual void destroy(); + virtual const Surface *getSurface() const { return &_surface; } + virtual const byte *getPalette() const { return _colorMap; } + virtual uint16 getPaletteColorCount() const { return _colorMapLength; } + virtual bool loadStream(Common::SeekableReadStream &stream); +private: + // Format-spec from: + //http://www.ludorg.net/amnesia/TGA_File_Format_Spec.html + enum { + TYPE_CMAP = 1, + TYPE_TRUECOLOR = 2, + TYPE_BW = 3, + TYPE_RLE_CMAP = 9, + TYPE_RLE_TRUECOLOR = 10, + TYPE_RLE_BW = 11 + }; + + // Color-map: + bool _colorMapSize; + byte *_colorMap; + int16 _colorMapOrigin; + int16 _colorMapLength; + byte _colorMapEntryLength; + + // Origin may be at the top, or bottom + bool _originTop; + + PixelFormat _format; + Surface _surface; + // Loading helpers + bool readHeader(Common::SeekableReadStream &tga, byte &imageType, byte &pixelDepth); + bool readData(Common::SeekableReadStream &tga, byte imageType, byte pixelDepth); + bool readDataColorMapped(Common::SeekableReadStream &tga, byte imageType, byte indexDepth); + bool readDataRLE(Common::SeekableReadStream &tga, byte imageType, byte pixelDepth); + bool readColorMap(Common::SeekableReadStream &tga, byte imageType, byte pixelDepth); +}; + +} // End of namespace Graphics + +#endif // GRAPHICS_DECODERS_TGA_H diff --git a/graphics/fonts/consolefont.cpp b/graphics/fonts/consolefont.cpp index 8244d75fc2..748aa08a5c 100644 --- a/graphics/fonts/consolefont.cpp +++ b/graphics/fonts/consolefont.cpp @@ -1,7 +1,7 @@ // Generated by convbdf on Fri Jan 6 14:32:21 2012 #include "graphics/fonts/bdf.h" -// Font information: +// Font information: // Name: -Misc-Fixed-Medium-R-Normal--8-80-75-75-C-50-ISO8859-1 // Size: 5x8 // Box: 5 8 0 -1 diff --git a/graphics/fonts/newfont.cpp b/graphics/fonts/newfont.cpp index 10af1efb0c..4922e24676 100644 --- a/graphics/fonts/newfont.cpp +++ b/graphics/fonts/newfont.cpp @@ -1,7 +1,7 @@ // Generated by convbdf on Fri Jan 6 14:33:07 2012 #include "graphics/fonts/bdf.h" -// Font information: +// Font information: // Name: -Schumacher-Clean-Medium-R-Normal--12-120-75-75-C-60-ISO8859-1 // Size: 6x12 // Box: 6 12 0 -3 diff --git a/graphics/fonts/newfont_big.cpp b/graphics/fonts/newfont_big.cpp index 0e61068ade..550d6dbfa9 100644 --- a/graphics/fonts/newfont_big.cpp +++ b/graphics/fonts/newfont_big.cpp @@ -1,7 +1,7 @@ // Generated by convbdf on Fri Jan 6 14:33:14 2012 #include "graphics/fonts/bdf.h" -// Font information: +// Font information: // Name: -Adobe-Helvetica-Bold-R-Normal--12-120-75-75-P-70-ISO8859-1 // Size: 13x14 // Box: 13 15 -1 -3 diff --git a/graphics/fonts/ttf.cpp b/graphics/fonts/ttf.cpp index 96241e923c..2b1dca1eae 100644 --- a/graphics/fonts/ttf.cpp +++ b/graphics/fonts/ttf.cpp @@ -101,7 +101,7 @@ public: TTFFont(); virtual ~TTFFont(); - bool load(Common::SeekableReadStream &stream, int size, bool monochrome, const uint32 *mapping); + bool load(Common::SeekableReadStream &stream, int size, uint dpi, bool monochrome, const uint32 *mapping); virtual int getFontHeight() const; @@ -157,7 +157,7 @@ TTFFont::~TTFFont() { } } -bool TTFFont::load(Common::SeekableReadStream &stream, int size, bool monochrome, const uint32 *mapping) { +bool TTFFont::load(Common::SeekableReadStream &stream, int size, uint dpi, bool monochrome, const uint32 *mapping) { if (!g_ttf.isInitialized()) return false; @@ -195,7 +195,7 @@ bool TTFFont::load(Common::SeekableReadStream &stream, int size, bool monochrome // Check whether we have kerning support _hasKerning = (FT_HAS_KERNING(_face) != 0); - if (FT_Set_Char_Size(_face, 0, size * 64, 0, 0)) { + if (FT_Set_Char_Size(_face, 0, size * 64, dpi, dpi)) { delete[] _ttfFile; _ttfFile = 0; @@ -462,10 +462,10 @@ bool TTFFont::cacheGlyph(Glyph &glyph, FT_UInt &slot, uint chr) { return true; } -Font *loadTTFFont(Common::SeekableReadStream &stream, int size, bool monochrome, const uint32 *mapping) { +Font *loadTTFFont(Common::SeekableReadStream &stream, int size, uint dpi, bool monochrome, const uint32 *mapping) { TTFFont *font = new TTFFont(); - if (!font->load(stream, size, monochrome, mapping)) { + if (!font->load(stream, size, dpi, monochrome, mapping)) { delete font; return 0; } diff --git a/graphics/fonts/ttf.h b/graphics/fonts/ttf.h index ec7dbe04ef..e1464b1f45 100644 --- a/graphics/fonts/ttf.h +++ b/graphics/fonts/ttf.h @@ -32,7 +32,7 @@ namespace Graphics { class Font; -Font *loadTTFFont(Common::SeekableReadStream &stream, int size, bool monochrome = false, const uint32 *mapping = 0); +Font *loadTTFFont(Common::SeekableReadStream &stream, int size, uint dpi = 0, bool monochrome = false, const uint32 *mapping = 0); void shutdownTTF(); diff --git a/graphics/iff.cpp b/graphics/iff.cpp deleted file mode 100644 index 7434a6bebc..0000000000 --- a/graphics/iff.cpp +++ /dev/null @@ -1,271 +0,0 @@ -/* 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 "graphics/iff.h" -#include "graphics/surface.h" - -#include "common/endian.h" -#include "common/func.h" -#include "common/iff_container.h" -#include "common/textconsole.h" -#include "common/util.h" - -namespace Graphics { - -void BMHD::load(Common::ReadStream *stream) { - assert(stream); - stream->read(this, sizeof(BMHD)); - width = FROM_BE_16(width); - height = FROM_BE_16(height); - x = FROM_BE_16(x); - y = FROM_BE_16(y); - transparentColor = FROM_BE_16(transparentColor); - pageWidth = FROM_BE_16(pageWidth); - pageHeight = FROM_BE_16(pageHeight); -} - - -void ILBMDecoder::loadHeader(Common::ReadStream *stream) { - _header.load(stream); -} - -void ILBMDecoder::loadBitmap(uint32 mode, byte *buffer, Common::ReadStream *stream) { - assert(stream); - uint32 numPlanes = MIN(mode & ILBM_UNPACK_PLANES, (uint32)_header.depth); - assert(numPlanes == 1 || numPlanes == 2 || numPlanes == 3 || numPlanes == 4 || numPlanes == 5 || numPlanes == 8); - - bool packPixels = (mode & ILBM_PACK_PLANES) != 0; - if (numPlanes != 1 && numPlanes != 2 && numPlanes != 4) { - packPixels = false; - } - - uint32 outPitch = _header.width; - if (packPixels) { - outPitch /= (8 / numPlanes); - } - byte *out = buffer; - - switch (_header.pack) { - case 1: { // PackBits compressed bitmap - Graphics::PackBitsReadStream packStream(*stream); - - // setup a buffer to hold enough data to build a line in the output - uint32 scanlineWidth = ((_header.width + 15)/16) << 1; - byte *scanline = new byte[scanlineWidth * _header.depth]; - - for (uint i = 0; i < _header.height; ++i) { - byte *s = scanline; - for (uint32 j = 0; j < _header.depth; ++j) { - packStream.read(s, scanlineWidth); - s += scanlineWidth; - } - - planarToChunky(out, outPitch, scanline, scanlineWidth, numPlanes, packPixels); - out += outPitch; - } - - delete []scanline; - break; - } - - default: - // implement other compression types here! - error("only RLE compressed ILBM files are supported"); - break; - } -} - -void ILBMDecoder::planarToChunky(byte *out, uint32 outPitch, byte *in, uint32 inWidth, uint32 nPlanes, bool packPlanes) { - byte pix, ofs, bit; - byte *s; - - uint32 pixels = outPitch; - if (packPlanes) { - pixels *= (8 / nPlanes); - } - - for (uint32 x = 0; x < pixels; ++x) { - - pix = 0; - ofs = x >> 3; - bit = 0x80 >> (x & 7); - - // first build a pixel by scanning all the usable planes in the input - s = in; - for (uint32 plane = 0; plane < nPlanes; ++plane) { - if (s[ofs] & bit) { - pix |= (1 << plane); - } - s += inWidth; - } - - - // then output the pixel according to the requested packing - if (!packPlanes) { - out[x] = pix; - } else - if (nPlanes == 1) { - out[x/8] |= (pix << (x & 7)); - } else - if (nPlanes == 2) { - out[x/4] |= (pix << ((x & 3) << 1)); - } else - if (nPlanes == 4) { - out[x/2] |= (pix << ((x & 1) << 2)); - } - } - -} - - -// handles PBM subtype of IFF FORM files -// -struct PBMDecoder { - /** - * PBM header data, necessary for loadBitmap() - */ - Graphics::BMHD _header; - - /** - * Fills the _header member from the given stream. - */ - void loadHeader(Common::ReadStream *stream); - - /** - * Loads and unpacks the PBM bitmap data from the stream into the buffer. - * The functions assumes the buffer is large enough to contain all data. - */ - void loadBitmap(byte *buffer, Common::ReadStream *stream); -}; - -void PBMDecoder::loadHeader(Common::ReadStream *stream) { - _header.load(stream); -} - - -void PBMDecoder::loadBitmap(byte *buffer, Common::ReadStream *stream) { - uint32 outSize = _header.width * _header.height; - - switch (_header.pack) { - case 0: - stream->read(buffer, outSize); - break; - - case 1: { - PackBitsReadStream packStream(*stream); - packStream.read(buffer, outSize); - break; - } - } -} - - -struct PBMLoader { - PBMDecoder _decoder; - Surface *_surface; - byte *_colors; - - void load(Common::ReadStream &input, Surface &surface, byte *&colors) { - _surface = &surface; - _colors = colors; - Common::IFFParser parser(&input); - Common::Functor1Mem< Common::IFFChunk&, bool, PBMLoader > c(this, &PBMLoader::callback); - parser.parse(c); - } - - bool callback(Common::IFFChunk &chunk) { - switch (chunk._type) { - case ID_BMHD: - _decoder.loadHeader(chunk._stream); - break; - - case ID_CMAP: - if (_colors) { - chunk._stream->read(_colors, chunk._size); - } - break; - - case ID_BODY: - if (_surface) { - _surface->create(_decoder._header.width, _decoder._header.height, PixelFormat::createFormatCLUT8()); - _decoder.loadBitmap((byte *)_surface->pixels, chunk._stream); - } - return true; // stop the parser - } - - return false; - } -}; - -void decodePBM(Common::ReadStream &input, Surface &surface, byte *colors) { - PBMLoader loader; - loader.load(input, surface, colors); -} - - - - -PackBitsReadStream::PackBitsReadStream(Common::ReadStream &input) : _input(&input) { -} - -PackBitsReadStream::~PackBitsReadStream() { -} - -bool PackBitsReadStream::eos() const { - return _input->eos(); -} - -uint32 PackBitsReadStream::read(void *dataPtr, uint32 dataSize) { - byte *out = (byte *)dataPtr; - uint32 left = dataSize; - - uint32 lenR = 0, lenW = 0; - while (left > 0 && !_input->eos()) { - lenR = _input->readByte(); - - if (lenR == 128) { - // no-op - lenW = 0; - } else if (lenR <= 127) { - // literal run - lenR++; - lenW = MIN(lenR, left); - for (uint32 j = 0; j < lenW; j++) { - *out++ = _input->readByte(); - } - for ( ; lenR > lenW; lenR--) { - _input->readByte(); - } - } else { // len > 128 - // expand run - lenW = MIN((256 - lenR) + 1, left); - byte val = _input->readByte(); - memset(out, val, lenW); - out += lenW; - } - - left -= lenW; - } - - return dataSize - left; -} - -} // End of namespace Graphics diff --git a/graphics/iff.h b/graphics/iff.h deleted file mode 100644 index 4d88148372..0000000000 --- a/graphics/iff.h +++ /dev/null @@ -1,137 +0,0 @@ -/* 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. - */ - -/* - * Bitmap decoder used in engines: - * - parallaction - * - saga - */ - -#ifndef GRAPHICS_IFF_H -#define GRAPHICS_IFF_H - -#include "common/stream.h" - -namespace Graphics { - -struct Surface; - - -struct BMHD { - uint16 width, height; - uint16 x, y; - byte depth; - byte masking; - byte pack; - byte flags; - uint16 transparentColor; - byte xAspect, yAspect; - uint16 pageWidth, pageHeight; - - BMHD() { - memset(this, 0, sizeof(*this)); - } - - void load(Common::ReadStream *stream); -}; - - -struct ILBMDecoder { - /** - * ILBM header data, necessary for loadBitmap() - */ - Graphics::BMHD _header; - - /** - * Available decoding modes for loadBitmap(). - */ - enum { - ILBM_UNPACK_PLANES = 0xFF, ///< Decode all bitplanes, and map 1 pixel to 1 byte. - ILBM_PACK_PLANES = 0x100, ///< Request unpacking, used as a mask with below options. - - ILBM_1_PLANES = 1, ///< Decode only the first bitplane, don't pack. - ILBM_1_PACK_PLANES = ILBM_1_PLANES | ILBM_PACK_PLANES, ///< Decode only the first bitplane, pack 8 pixels in 1 byte. - ILBM_2_PLANES = 2, ///< Decode first 2 bitplanes, don't pack. - ILBM_2_PACK_PLANES = ILBM_2_PLANES | ILBM_PACK_PLANES, ///< Decode first 2 bitplanes, pack 4 pixels in 1 byte. - ILBM_3_PLANES = 3, ///< Decode first 3 bitplanes, don't pack. - ILBM_4_PLANES = 4, ///< Decode first 4 bitplanes, don't pack. - ILBM_4_PACK_PLANES = ILBM_4_PLANES | ILBM_PACK_PLANES, ///< Decode first 4 bitplanes, pack 2 pixels in 1 byte. - ILBM_5_PLANES = 5, ///< Decode first 5 bitplanes, don't pack. - ILBM_8_PLANES = 8 ///< Decode all 8 bitplanes. - }; - - /** - * Fills the _header member from the given stream. - */ - void loadHeader(Common::ReadStream *stream); - - /** - * Loads and unpacks the ILBM bitmap data from the stream into the buffer. - * The functions assumes the buffer is large enough to contain all data. - * The caller controls how data should be packed by choosing mode from - * the enum above. - */ - void loadBitmap(uint32 mode, byte *buffer, Common::ReadStream *stream); - - /** - * Converts from bitplanar to chunky representation. Intended for internal - * usage, but you can be (ab)use it from client code if you know what you - * are doing. - */ - void planarToChunky(byte *out, uint32 width, byte *in, uint32 planeWidth, uint32 nPlanes, bool packPlanes); -}; - - - -/** - * Handles PBM subtype of IFF FORM files - */ -void decodePBM(Common::ReadStream &input, Surface &surface, byte *colors); - - -/** - * Decode a given PackBits encoded stream. - * - * PackBits is an RLE compression algorithm introduced by Apple. It is also - * used to encode ILBM and PBM subtypes of IFF files, and some flavors of - * TIFF. - * - * As there is no compression across row boundaries in the above formats, - * read() will extract a *new* line on each call, discarding any alignment - * or padding. - */ -class PackBitsReadStream : public Common::ReadStream { - -protected: - Common::ReadStream *_input; - -public: - PackBitsReadStream(Common::ReadStream &input); - ~PackBitsReadStream(); - - virtual bool eos() const; - - uint32 read(void *dataPtr, uint32 dataSize); -}; - -} // End of namespace Graphics - -#endif diff --git a/graphics/module.mk b/graphics/module.mk index 281f904b38..8b63435905 100644 --- a/graphics/module.mk +++ b/graphics/module.mk @@ -11,7 +11,6 @@ MODULE_OBJS := \ fonts/newfont.o \ fonts/ttf.o \ fonts/winfont.o \ - iff.o \ maccursor.o \ primitives.o \ scaler.o \ @@ -24,9 +23,12 @@ MODULE_OBJS := \ wincursor.o \ yuv_to_rgb.o \ decoders/bmp.o \ + decoders/iff.o \ decoders/jpeg.o \ + decoders/pcx.o \ decoders/pict.o \ - decoders/png.o + decoders/png.o \ + decoders/tga.o ifdef USE_SCALERS MODULE_OBJS += \ diff --git a/graphics/primitives.cpp b/graphics/primitives.cpp index 9834af65ba..b88db39f36 100644 --- a/graphics/primitives.cpp +++ b/graphics/primitives.cpp @@ -61,59 +61,21 @@ void drawLine(int x0, int y0, int x1, int y1, int color, void (*plotProc)(int, i } } +void drawThickLine(int x0, int y0, int x1, int y1, int penX, int penY, int color, void (*plotProc)(int, int, int, void *), void *data) { + assert(penX > 0 && penY > 0); -// FIXME: This is a limited version of thick line drawing -// it draws striped lines at some angles. Better algorithm could -// be found here: -// -// http://homepages.enterprise.net/murphy/thickline/index.html -// -// Feel free to replace it with better implementation -void drawThickLine(int x0, int y0, int x1, int y1, int thickness, int color, void (*plotProc)(int, int, int, void *), void *data) { - const bool steep = ABS(y1 - y0) > ABS(x1 - x0); - - if (steep) { - SWAP(x0, y0); - SWAP(x1, y1); - } - - float dx = x1 - x0; - float dy = y1 - y0; - float d = (float)sqrt(dx * dx + dy * dy); - - if (!d) + // Shortcut + if (penX == 1 && penY == 1) { + drawLine(x0, y0, x1, y1, color, plotProc, data); return; - - int thickX = (int)((float)thickness * dy / d / 2); - int thickY = (int)((float)thickness * dx / d / 2); - - const int delta_x = ABS(x1 - x0); - const int delta_y = ABS(y1 - y0); - const int delta_err = delta_y; - int x = x0; - int y = y0; - int err = 0; - - const int x_step = (x0 < x1) ? 1 : -1; - const int y_step = (y0 < y1) ? 1 : -1; - - if (steep) - drawLine(y - thickY, x + thickX, y + thickY, x - thickX, color, plotProc, data); - else - drawLine(x - thickX, y + thickY, x + thickX, y - thickY, color, plotProc, data); - - while (x != x1) { - x += x_step; - err += delta_err; - if (2 * err > delta_x) { - y += y_step; - err -= delta_x; - } - if (steep) - drawLine(y - thickY, x + thickX, y + thickY, x - thickX, color, plotProc, data); - else - drawLine(x - thickX, y + thickY, x + thickX, y - thickY, color, plotProc, data); } + + // TODO: Optimize this. It currently is a very naive way of handling + // thick lines since quite often it will be drawing to the same pixel + // multiple times. + for (int x = 0; x < penX; x++) + for (int y = 0; y < penY; y++) + drawLine(x0 + x, y0 + y, x1 + x, y1 + y, color, plotProc, data); } } // End of namespace Graphics diff --git a/graphics/primitives.h b/graphics/primitives.h index 0ab2dabcd8..f0780afc2e 100644 --- a/graphics/primitives.h +++ b/graphics/primitives.h @@ -25,7 +25,7 @@ namespace Graphics { void drawLine(int x0, int y0, int x1, int y1, int color, void (*plotProc)(int, int, int, void *), void *data); -void drawThickLine(int x0, int y0, int x1, int y1, int thickness, int color, void (*plotProc)(int, int, int, void *), void *data); +void drawThickLine(int x0, int y0, int x1, int y1, int penX, int penY, int color, void (*plotProc)(int, int, int, void *), void *data); } // End of namespace Graphics diff --git a/graphics/scaler.cpp b/graphics/scaler.cpp index b81e8937a8..3325fd4db2 100644 --- a/graphics/scaler.cpp +++ b/graphics/scaler.cpp @@ -152,7 +152,7 @@ void InitScalers(uint32 BitFormat) { g_dotmatrix[12] = g_dotmatrix[14] = format.RGBToColor(63, 63, 63); } -void DestroyScalers(){ +void DestroyScalers() { #ifdef USE_HQ_SCALERS free(RGBtoYUV); RGBtoYUV = 0; diff --git a/graphics/scaler/aspect.cpp b/graphics/scaler/aspect.cpp index f0ae732a40..92d6c5777e 100644 --- a/graphics/scaler/aspect.cpp +++ b/graphics/scaler/aspect.cpp @@ -23,6 +23,9 @@ #include "graphics/scaler/intern.h" #include "graphics/scaler/aspect.h" +#ifdef USE_ARM_NEON_ASPECT_CORRECTOR +#include <arm_neon.h> +#endif #define kSuperFastAndUglyAspectMode 0 // No interpolation at all, but super-fast #define kVeryFastAndGoodAspectMode 1 // Good quality with very good speed @@ -55,13 +58,66 @@ static inline void interpolate5Line(uint16 *dst, const uint16 *srcA, const uint1 #if ASPECT_MODE == kVeryFastAndGoodAspectMode +#ifdef USE_ARM_NEON_ASPECT_CORRECTOR + +template<typename ColorMask> +static void interpolate5LineNeon(uint16 *dst, const uint16 *srcA, const uint16 *srcB, int width, int k1, int k2) { + uint16x4_t kRedBlueMask_4 = vdup_n_u16(ColorMask::kRedBlueMask); + uint16x4_t kGreenMask_4 = vdup_n_u16(ColorMask::kGreenMask); + uint16x4_t k1_4 = vdup_n_u16(k1); + uint16x4_t k2_4 = vdup_n_u16(k2); + while (width >= 4) { + uint16x4_t srcA_4 = vld1_u16(srcA); + uint16x4_t srcB_4 = vld1_u16(srcB); + uint16x4_t p1_4 = srcB_4; + uint16x4_t p2_4 = srcA_4; + + uint16x4_t p1_rb_4 = vand_u16(p1_4, kRedBlueMask_4); + uint16x4_t p1_g_4 = vand_u16(p1_4, kGreenMask_4); + uint16x4_t p2_rb_4 = vand_u16(p2_4, kRedBlueMask_4); + uint16x4_t p2_g_4 = vand_u16(p2_4, kGreenMask_4); + + uint32x4_t tmp_rb_4 = vshrq_n_u32(vmlal_u16(vmull_u16(p2_rb_4, k2_4), p1_rb_4, k1_4), 3); + uint32x4_t tmp_g_4 = vshrq_n_u32(vmlal_u16(vmull_u16(p2_g_4, k2_4), p1_g_4, k1_4), 3); + uint16x4_t p_rb_4 = vmovn_u32(tmp_rb_4); + p_rb_4 = vand_u16(p_rb_4, kRedBlueMask_4); + uint16x4_t p_g_4 = vmovn_u32(tmp_g_4); + p_g_4 = vand_u16(p_g_4, kGreenMask_4); + + uint16x4_t result_4 = p_rb_4 | p_g_4; + vst1_u16(dst, result_4); + + dst += 4; + srcA += 4; + srcB += 4; + width -= 4; + } +} +#endif // USE_ARM_NEON_ASPECT_CORRECTOR + template<typename ColorMask, int scale> -static inline void interpolate5Line(uint16 *dst, const uint16 *srcA, const uint16 *srcB, int width) { +static void interpolate5Line(uint16 *dst, const uint16 *srcA, const uint16 *srcB, int width) { if (scale == 1) { +#ifdef USE_NEON_ASPECT_CORRECTOR + int width4 = width & ~3; + interpolate5LineNeon<ColorMask>(dst, srcA, srcB, width4, 7, 1); + srcA += width4; + srcB += width4; + dst += width4; + width -= width4; +#endif // USE_ARM_NEON_ASPECT_CORRECTOR while (width--) { *dst++ = interpolate16_7_1<ColorMask>(*srcB++, *srcA++); } } else { +#ifdef USE_ARM_NEON_ASPECT_CORRECTOR + int width4 = width & ~3; + interpolate5LineNeon<ColorMask>(dst, srcA, srcB, width4, 5, 3); + srcA += width4; + srcB += width4; + dst += width4; + width -= width4; +#endif // USE_ARM_NEON_ASPECT_CORRECTOR while (width--) { *dst++ = interpolate16_5_3<ColorMask>(*srcB++, *srcA++); } diff --git a/graphics/sjis.h b/graphics/sjis.h index 2d05005fc3..928332f712 100644 --- a/graphics/sjis.h +++ b/graphics/sjis.h @@ -169,7 +169,7 @@ protected: bool _flippedMode; int _fontWidth, _fontHeight; uint8 _bitPosNewLineMask; - + bool isASCII(uint16 ch) const; virtual const uint8 *getCharData(uint16 c) const = 0; diff --git a/graphics/surface.cpp b/graphics/surface.cpp index c0f1046eae..41ae8dcebb 100644 --- a/graphics/surface.cpp +++ b/graphics/surface.cpp @@ -26,6 +26,7 @@ #include "common/textconsole.h" #include "graphics/primitives.h" #include "graphics/surface.h" +#include "graphics/conversion.h" namespace Graphics { @@ -49,6 +50,17 @@ void Surface::drawLine(int x0, int y0, int x1, int y1, uint32 color) { error("Surface::drawLine: bytesPerPixel must be 1, 2, or 4"); } +void Surface::drawThickLine(int x0, int y0, int x1, int y1, int penX, int penY, uint32 color) { + if (format.bytesPerPixel == 1) + Graphics::drawThickLine(x0, y0, x1, y1, penX, penY, color, plotPoint<byte>, this); + else if (format.bytesPerPixel == 2) + Graphics::drawThickLine(x0, y0, x1, y1, penX, penY, color, plotPoint<uint16>, this); + else if (format.bytesPerPixel == 4) + Graphics::drawThickLine(x0, y0, x1, y1, penX, penY, color, plotPoint<uint32>, this); + else + error("Surface::drawThickLine: bytesPerPixel must be 1, 2, or 4"); +} + void Surface::create(uint16 width, uint16 height, const PixelFormat &f) { free(); @@ -271,6 +283,72 @@ void Surface::move(int dx, int dy, int height) { } } +void Surface::convertToInPlace(const PixelFormat &dstFormat, const byte *palette) { + // Do not convert to the same format and ignore empty surfaces. + if (format == dstFormat || pixels == 0) { + return; + } + + if (format.bytesPerPixel == 0 || format.bytesPerPixel > 4) + error("Surface::convertToInPlace(): Can only convert from 1Bpp, 2Bpp, 3Bpp, and 4Bpp"); + + if (dstFormat.bytesPerPixel != 2 && dstFormat.bytesPerPixel != 4) + error("Surface::convertToInPlace(): Can only convert to 2Bpp and 4Bpp"); + + // In case the surface data needs more space allocate it. + if (dstFormat.bytesPerPixel > format.bytesPerPixel) { + void *const newPixels = realloc(pixels, w * h * dstFormat.bytesPerPixel); + if (!newPixels) { + error("Surface::convertToInPlace(): Out of memory"); + } + pixels = newPixels; + } + + // We take advantage of the fact that pitch is always w * format.bytesPerPixel. + // This is assured by the logic of Surface::create. + + // We need to handle 1 Bpp surfaces special here. + if (format.bytesPerPixel == 1) { + assert(palette); + + for (int y = h; y > 0; --y) { + const byte *srcRow = (const byte *)pixels + y * pitch - 1; + byte *dstRow = (byte *)pixels + y * w * dstFormat.bytesPerPixel - dstFormat.bytesPerPixel; + + for (int x = 0; x < w; x++) { + byte index = *srcRow--; + byte r = palette[index * 3]; + byte g = palette[index * 3 + 1]; + byte b = palette[index * 3 + 2]; + + uint32 color = dstFormat.RGBToColor(r, g, b); + + if (dstFormat.bytesPerPixel == 2) + *((uint16 *)dstRow) = color; + else + *((uint32 *)dstRow) = color; + + dstRow -= dstFormat.bytesPerPixel; + } + } + } else { + crossBlit((byte *)pixels, (const byte *)pixels, w * dstFormat.bytesPerPixel, pitch, w, h, dstFormat, format); + } + + // In case the surface data got smaller, free up some memory. + if (dstFormat.bytesPerPixel < format.bytesPerPixel) { + void *const newPixels = realloc(pixels, w * h * dstFormat.bytesPerPixel); + if (!newPixels) { + error("Surface::convertToInPlace(): Freeing memory failed"); + } + pixels = newPixels; + } + + // Update the surface specific data. + format = dstFormat; + pitch = w * dstFormat.bytesPerPixel; +} + Graphics::Surface *Surface::convertTo(const PixelFormat &dstFormat, const byte *palette) const { assert(pixels); diff --git a/graphics/surface.h b/graphics/surface.h index eb8d1ac42e..6c9e464657 100644 --- a/graphics/surface.h +++ b/graphics/surface.h @@ -137,6 +137,20 @@ struct Surface { /** * Convert the data to another pixel format. * + * This works in-place. This means it will not create an additional buffer + * for the conversion process. The value of pixels might change though. + * + * Note that you should only use this, when you created the Surface data via + * create! Otherwise this function has undefined behavior. + * + * @param dstFormat The desired format + * @param palette The palette (in RGB888), if the source format has a Bpp of 1 + */ + void convertToInPlace(const PixelFormat &dstFormat, const byte *palette = 0); + + /** + * Convert the data to another pixel format. + * * The calling code must call free on the returned surface and then delete * it. * @@ -153,10 +167,26 @@ struct Surface { * @param x1 The x coordinate of the end point. * @param y1 The y coordinate of the end point. * @param color The color of the line. + * @note This is just a wrapper around Graphics::drawLine */ void drawLine(int x0, int y0, int x1, int y1, uint32 color); /** + * Draw a thick line. + * + * @param x0 The x coordinate of the start point. + * @param y0 The y coordiante of the start point. + * @param x1 The x coordinate of the end point. + * @param y1 The y coordinate of the end point. + * @param penX The width of the pen (thickness in the x direction) + * @param penY The height of the pen (thickness in the y direction) + * @param color The color of the line. + * @note This is just a wrapper around Graphics::drawThickLine + * @note The x/y coordinates of the start and end points are the upper-left most part of the pen + */ + void drawThickLine(int x0, int y0, int x1, int y1, int penX, int penY, uint32 color); + + /** * Draw a horizontal line. * * @param x The start x coordinate of the line. diff --git a/graphics/yuv_to_rgb.cpp b/graphics/yuv_to_rgb.cpp index 78903d0cd8..6043315a13 100644 --- a/graphics/yuv_to_rgb.cpp +++ b/graphics/yuv_to_rgb.cpp @@ -83,129 +83,127 @@ // BASIS, AND BROWN UNIVERSITY HAS NO OBLIGATION TO PROVIDE MAINTENANCE, // SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -#include "common/scummsys.h" -#include "common/singleton.h" - #include "graphics/surface.h" +#include "graphics/yuv_to_rgb.h" + +namespace Common { +DECLARE_SINGLETON(Graphics::YUVToRGBManager); +} namespace Graphics { class YUVToRGBLookup { public: - YUVToRGBLookup(Graphics::PixelFormat format); - ~YUVToRGBLookup(); - - int16 *_colorTab; - uint32 *_rgbToPix; -}; + YUVToRGBLookup(Graphics::PixelFormat format, YUVToRGBManager::LuminanceScale scale); -YUVToRGBLookup::YUVToRGBLookup(Graphics::PixelFormat format) { - _colorTab = new int16[4 * 256]; // 2048 bytes + Graphics::PixelFormat getFormat() const { return _format; } + YUVToRGBManager::LuminanceScale getScale() const { return _scale; } + const uint32 *getRGBToPix() const { return _rgbToPix; } - int16 *Cr_r_tab = &_colorTab[0 * 256]; - int16 *Cr_g_tab = &_colorTab[1 * 256]; - int16 *Cb_g_tab = &_colorTab[2 * 256]; - int16 *Cb_b_tab = &_colorTab[3 * 256]; +private: + Graphics::PixelFormat _format; + YUVToRGBManager::LuminanceScale _scale; + uint32 _rgbToPix[3 * 768]; // 9216 bytes +}; - _rgbToPix = new uint32[3 * 768]; // 9216 bytes +YUVToRGBLookup::YUVToRGBLookup(Graphics::PixelFormat format, YUVToRGBManager::LuminanceScale scale) { + _format = format; + _scale = scale; uint32 *r_2_pix_alloc = &_rgbToPix[0 * 768]; uint32 *g_2_pix_alloc = &_rgbToPix[1 * 768]; uint32 *b_2_pix_alloc = &_rgbToPix[2 * 768]; - int16 CR, CB; - int i; + if (scale == YUVToRGBManager::kScaleFull) { + // Set up entries 0-255 in rgb-to-pixel value tables. + for (int i = 0; i < 256; i++) { + r_2_pix_alloc[i + 256] = format.RGBToColor(i, 0, 0); + g_2_pix_alloc[i + 256] = format.RGBToColor(0, i, 0); + b_2_pix_alloc[i + 256] = format.RGBToColor(0, 0, i); + } + + // Spread out the values we have to the rest of the array so that we do + // not need to check for overflow. + for (int i = 0; i < 256; i++) { + r_2_pix_alloc[i] = r_2_pix_alloc[256]; + r_2_pix_alloc[i + 512] = r_2_pix_alloc[511]; + g_2_pix_alloc[i] = g_2_pix_alloc[256]; + g_2_pix_alloc[i + 512] = g_2_pix_alloc[511]; + b_2_pix_alloc[i] = b_2_pix_alloc[256]; + b_2_pix_alloc[i + 512] = b_2_pix_alloc[511]; + } + } else { + // Set up entries 16-235 in rgb-to-pixel value tables + for (int i = 16; i < 236; i++) { + int scaledValue = (i - 16) * 255 / 219; + r_2_pix_alloc[i + 256] = format.RGBToColor(scaledValue, 0, 0); + g_2_pix_alloc[i + 256] = format.RGBToColor(0, scaledValue, 0); + b_2_pix_alloc[i + 256] = format.RGBToColor(0, 0, scaledValue); + } + + // Spread out the values we have to the rest of the array so that we do + // not need to check for overflow. We have to do it here in two steps. + for (int i = 0; i < 256 + 16; i++) { + r_2_pix_alloc[i] = r_2_pix_alloc[256 + 16]; + g_2_pix_alloc[i] = g_2_pix_alloc[256 + 16]; + b_2_pix_alloc[i] = b_2_pix_alloc[256 + 16]; + } + + for (int i = 256 + 236; i < 768; i++) { + r_2_pix_alloc[i] = r_2_pix_alloc[256 + 236 - 1]; + g_2_pix_alloc[i] = g_2_pix_alloc[256 + 236 - 1]; + b_2_pix_alloc[i] = b_2_pix_alloc[256 + 236 - 1]; + } + } +} + +YUVToRGBManager::YUVToRGBManager() { + _lookup = 0; + + int16 *Cr_r_tab = &_colorTab[0 * 256]; + int16 *Cr_g_tab = &_colorTab[1 * 256]; + int16 *Cb_g_tab = &_colorTab[2 * 256]; + int16 *Cb_b_tab = &_colorTab[3 * 256]; // Generate the tables for the display surface - for (i = 0; i < 256; i++) { + for (int i = 0; i < 256; i++) { // Gamma correction (luminescence table) and chroma correction // would be done here. See the Berkeley mpeg_play sources. - CR = CB = (i - 128); + int16 CR = (i - 128), CB = CR; Cr_r_tab[i] = (int16) ( (0.419 / 0.299) * CR) + 0 * 768 + 256; Cr_g_tab[i] = (int16) (-(0.299 / 0.419) * CR) + 1 * 768 + 256; Cb_g_tab[i] = (int16) (-(0.114 / 0.331) * CB); Cb_b_tab[i] = (int16) ( (0.587 / 0.331) * CB) + 2 * 768 + 256; } - - // Set up entries 0-255 in rgb-to-pixel value tables. - for (i = 0; i < 256; i++) { - r_2_pix_alloc[i + 256] = format.RGBToColor(i, 0, 0); - g_2_pix_alloc[i + 256] = format.RGBToColor(0, i, 0); - b_2_pix_alloc[i + 256] = format.RGBToColor(0, 0, i); - } - - // Spread out the values we have to the rest of the array so that we do - // not need to check for overflow. - for (i = 0; i < 256; i++) { - r_2_pix_alloc[i] = r_2_pix_alloc[256]; - r_2_pix_alloc[i + 512] = r_2_pix_alloc[511]; - g_2_pix_alloc[i] = g_2_pix_alloc[256]; - g_2_pix_alloc[i + 512] = g_2_pix_alloc[511]; - b_2_pix_alloc[i] = b_2_pix_alloc[256]; - b_2_pix_alloc[i + 512] = b_2_pix_alloc[511]; - } -} - -YUVToRGBLookup::~YUVToRGBLookup() { - delete[] _rgbToPix; - delete[] _colorTab; -} - -class YUVToRGBManager : public Common::Singleton<YUVToRGBManager> { -public: - const YUVToRGBLookup *getLookup(Graphics::PixelFormat format); - -private: - friend class Common::Singleton<SingletonBaseType>; - YUVToRGBManager(); - ~YUVToRGBManager(); - - Graphics::PixelFormat _lastFormat; - YUVToRGBLookup *_lookup; -}; - -YUVToRGBManager::YUVToRGBManager() { - _lookup = 0; } YUVToRGBManager::~YUVToRGBManager() { delete _lookup; } -const YUVToRGBLookup *YUVToRGBManager::getLookup(Graphics::PixelFormat format) { - if (_lastFormat == format) +const YUVToRGBLookup *YUVToRGBManager::getLookup(Graphics::PixelFormat format, YUVToRGBManager::LuminanceScale scale) { + if (_lookup && _lookup->getFormat() == format && _lookup->getScale() == scale) return _lookup; delete _lookup; - _lookup = new YUVToRGBLookup(format); - _lastFormat = format; + _lookup = new YUVToRGBLookup(format, scale); return _lookup; } -} // End of namespace Graphics - -namespace Common { -DECLARE_SINGLETON(Graphics::YUVToRGBManager); -} - -#define YUVToRGBMan (Graphics::YUVToRGBManager::instance()) - -namespace Graphics { - #define PUT_PIXEL(s, d) \ L = &rgbToPix[(s)]; \ *((PixelInt *)(d)) = (L[cr_r] | L[crb_g] | L[cb_b]) template<typename PixelInt> -void convertYUV444ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) { +void convertYUV444ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup, int16 *colorTab, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) { // Keep the tables in pointers here to avoid a dereference on each pixel - const int16 *Cr_r_tab = lookup->_colorTab; + const int16 *Cr_r_tab = colorTab; const int16 *Cr_g_tab = Cr_r_tab + 256; const int16 *Cb_g_tab = Cr_g_tab + 256; const int16 *Cb_b_tab = Cb_g_tab + 256; - const uint32 *rgbToPix = lookup->_rgbToPix; + const uint32 *rgbToPix = lookup->getRGBToPix(); for (int h = 0; h < yHeight; h++) { for (int w = 0; w < yWidth; w++) { @@ -229,32 +227,32 @@ void convertYUV444ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup } } -void convertYUV444ToRGB(Graphics::Surface *dst, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) { +void YUVToRGBManager::convert444(Graphics::Surface *dst, YUVToRGBManager::LuminanceScale scale, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) { // Sanity checks assert(dst && dst->pixels); assert(dst->format.bytesPerPixel == 2 || dst->format.bytesPerPixel == 4); assert(ySrc && uSrc && vSrc); - const YUVToRGBLookup *lookup = YUVToRGBMan.getLookup(dst->format); + const YUVToRGBLookup *lookup = getLookup(dst->format, scale); // Use a templated function to avoid an if check on every pixel if (dst->format.bytesPerPixel == 2) - convertYUV444ToRGB<uint16>((byte *)dst->pixels, dst->pitch, lookup, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch); + convertYUV444ToRGB<uint16>((byte *)dst->pixels, dst->pitch, lookup, _colorTab, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch); else - convertYUV444ToRGB<uint32>((byte *)dst->pixels, dst->pitch, lookup, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch); + convertYUV444ToRGB<uint32>((byte *)dst->pixels, dst->pitch, lookup, _colorTab, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch); } template<typename PixelInt> -void convertYUV420ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) { +void convertYUV420ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup, int16 *colorTab, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) { int halfHeight = yHeight >> 1; int halfWidth = yWidth >> 1; // Keep the tables in pointers here to avoid a dereference on each pixel - const int16 *Cr_r_tab = lookup->_colorTab; + const int16 *Cr_r_tab = colorTab; const int16 *Cr_g_tab = Cr_r_tab + 256; const int16 *Cb_g_tab = Cr_g_tab + 256; const int16 *Cb_b_tab = Cb_g_tab + 256; - const uint32 *rgbToPix = lookup->_rgbToPix; + const uint32 *rgbToPix = lookup->getRGBToPix(); for (int h = 0; h < halfHeight; h++) { for (int w = 0; w < halfWidth; w++) { @@ -283,7 +281,7 @@ void convertYUV420ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup } } -void convertYUV420ToRGB(Graphics::Surface *dst, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) { +void YUVToRGBManager::convert420(Graphics::Surface *dst, YUVToRGBManager::LuminanceScale scale, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) { // Sanity checks assert(dst && dst->pixels); assert(dst->format.bytesPerPixel == 2 || dst->format.bytesPerPixel == 4); @@ -291,13 +289,13 @@ void convertYUV420ToRGB(Graphics::Surface *dst, const byte *ySrc, const byte *uS assert((yWidth & 1) == 0); assert((yHeight & 1) == 0); - const YUVToRGBLookup *lookup = YUVToRGBMan.getLookup(dst->format); + const YUVToRGBLookup *lookup = getLookup(dst->format, scale); // Use a templated function to avoid an if check on every pixel if (dst->format.bytesPerPixel == 2) - convertYUV420ToRGB<uint16>((byte *)dst->pixels, dst->pitch, lookup, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch); + convertYUV420ToRGB<uint16>((byte *)dst->pixels, dst->pitch, lookup, _colorTab, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch); else - convertYUV420ToRGB<uint32>((byte *)dst->pixels, dst->pitch, lookup, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch); + convertYUV420ToRGB<uint32>((byte *)dst->pixels, dst->pitch, lookup, _colorTab, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch); } #define READ_QUAD(ptr, prefix) \ @@ -325,13 +323,13 @@ void convertYUV420ToRGB(Graphics::Surface *dst, const byte *ySrc, const byte *uS xDiff++ template<typename PixelInt> -void convertYUV410ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) { +void convertYUV410ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup, int16 *colorTab, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) { // Keep the tables in pointers here to avoid a dereference on each pixel - const int16 *Cr_r_tab = lookup->_colorTab; + const int16 *Cr_r_tab = colorTab; const int16 *Cr_g_tab = Cr_r_tab + 256; const int16 *Cb_g_tab = Cr_g_tab + 256; const int16 *Cb_b_tab = Cb_g_tab + 256; - const uint32 *rgbToPix = lookup->_rgbToPix; + const uint32 *rgbToPix = lookup->getRGBToPix(); int quarterWidth = yWidth >> 2; @@ -368,7 +366,7 @@ void convertYUV410ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup #undef DO_INTERPOLATION #undef DO_YUV410_PIXEL -void convertYUV410ToRGB(Graphics::Surface *dst, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) { +void YUVToRGBManager::convert410(Graphics::Surface *dst, YUVToRGBManager::LuminanceScale scale, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) { // Sanity checks assert(dst && dst->pixels); assert(dst->format.bytesPerPixel == 2 || dst->format.bytesPerPixel == 4); @@ -376,13 +374,13 @@ void convertYUV410ToRGB(Graphics::Surface *dst, const byte *ySrc, const byte *uS assert((yWidth & 3) == 0); assert((yHeight & 3) == 0); - const YUVToRGBLookup *lookup = YUVToRGBMan.getLookup(dst->format); + const YUVToRGBLookup *lookup = getLookup(dst->format, scale); // Use a templated function to avoid an if check on every pixel if (dst->format.bytesPerPixel == 2) - convertYUV410ToRGB<uint16>((byte *)dst->pixels, dst->pitch, lookup, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch); + convertYUV410ToRGB<uint16>((byte *)dst->pixels, dst->pitch, lookup, _colorTab, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch); else - convertYUV410ToRGB<uint32>((byte *)dst->pixels, dst->pitch, lookup, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch); + convertYUV410ToRGB<uint32>((byte *)dst->pixels, dst->pitch, lookup, _colorTab, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch); } } // End of namespace Graphics diff --git a/graphics/yuv_to_rgb.h b/graphics/yuv_to_rgb.h index 73a2c69d7d..f785422c5a 100644 --- a/graphics/yuv_to_rgb.h +++ b/graphics/yuv_to_rgb.h @@ -32,57 +32,85 @@ #define GRAPHICS_YUV_TO_RGB_H #include "common/scummsys.h" +#include "common/singleton.h" #include "graphics/surface.h" namespace Graphics { -/** - * Convert a YUV444 image to an RGB surface - * - * @param dst the destination surface - * @param ySrc the source of the y component - * @param uSrc the source of the u component - * @param vSrc the source of the v component - * @param yWidth the width of the y surface - * @param yHeight the height of the y surface - * @param yPitch the pitch of the y surface - * @param uvPitch the pitch of the u and v surfaces - */ -void convertYUV444ToRGB(Graphics::Surface *dst, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch); +class YUVToRGBLookup; -/** - * Convert a YUV420 image to an RGB surface - * - * @param dst the destination surface - * @param ySrc the source of the y component - * @param uSrc the source of the u component - * @param vSrc the source of the v component - * @param yWidth the width of the y surface (must be divisible by 2) - * @param yHeight the height of the y surface (must be divisible by 2) - * @param yPitch the pitch of the y surface - * @param uvPitch the pitch of the u and v surfaces - */ -void convertYUV420ToRGB(Graphics::Surface *dst, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch); +class YUVToRGBManager : public Common::Singleton<YUVToRGBManager> { +public: + /** The scale of the luminance values */ + enum LuminanceScale { + kScaleFull, /** Luminance values range from [0, 255] */ + kScaleITU /** Luminance values range from [16, 235], the range from ITU-R BT.601 */ + }; -/** - * Convert a YUV410 image to an RGB surface - * - * Since the chroma has a very low resolution in 410, we perform bilinear scaling - * on the two chroma planes to produce the image. The chroma planes must have - * at least one extra row that can be read from in order to produce a proper - * image (filled with 0x80). This is required in order to speed up this function. - * - * @param dst the destination surface - * @param ySrc the source of the y component - * @param uSrc the source of the u component - * @param vSrc the source of the v component - * @param yWidth the width of the y surface (must be divisible by 4) - * @param yHeight the height of the y surface (must be divisible by 4) - * @param yPitch the pitch of the y surface - * @param uvPitch the pitch of the u and v surfaces - */ -void convertYUV410ToRGB(Graphics::Surface *dst, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch); + /** + * Convert a YUV444 image to an RGB surface + * + * @param dst the destination surface + * @param scale the scale of the luminance values + * @param ySrc the source of the y component + * @param uSrc the source of the u component + * @param vSrc the source of the v component + * @param yWidth the width of the y surface + * @param yHeight the height of the y surface + * @param yPitch the pitch of the y surface + * @param uvPitch the pitch of the u and v surfaces + */ + void convert444(Graphics::Surface *dst, LuminanceScale scale, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch); + + /** + * Convert a YUV420 image to an RGB surface + * + * @param dst the destination surface + * @param scale the scale of the luminance values + * @param ySrc the source of the y component + * @param uSrc the source of the u component + * @param vSrc the source of the v component + * @param yWidth the width of the y surface (must be divisible by 2) + * @param yHeight the height of the y surface (must be divisible by 2) + * @param yPitch the pitch of the y surface + * @param uvPitch the pitch of the u and v surfaces + */ + void convert420(Graphics::Surface *dst, LuminanceScale scale, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch); + + /** + * Convert a YUV410 image to an RGB surface + * + * Since the chroma has a very low resolution in 410, we perform bilinear scaling + * on the two chroma planes to produce the image. The chroma planes must have + * at least one extra row and one extra column that can be read from in order to + * produce a proper image. It is suggested that you fill these in with the previous + * row and column's data. This is required in order to speed up this function. + * + * @param dst the destination surface + * @param scale the scale of the luminance values + * @param ySrc the source of the y component + * @param uSrc the source of the u component + * @param vSrc the source of the v component + * @param yWidth the width of the y surface (must be divisible by 4) + * @param yHeight the height of the y surface (must be divisible by 4) + * @param yPitch the pitch of the y surface + * @param uvPitch the pitch of the u and v surfaces + */ + void convert410(Graphics::Surface *dst, LuminanceScale scale, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch); + +private: + friend class Common::Singleton<SingletonBaseType>; + YUVToRGBManager(); + ~YUVToRGBManager(); + + const YUVToRGBLookup *getLookup(Graphics::PixelFormat format, LuminanceScale scale); + + YUVToRGBLookup *_lookup; + int16 _colorTab[4 * 256]; // 2048 bytes +}; } // End of namespace Graphics +#define YUVToRGBMan (::Graphics::YUVToRGBManager::instance()) + #endif diff --git a/gui/ThemeEngine.cpp b/gui/ThemeEngine.cpp index e37022f5f1..e2fa2580f5 100644 --- a/gui/ThemeEngine.cpp +++ b/gui/ThemeEngine.cpp @@ -48,6 +48,8 @@ const char * const ThemeEngine::kImageLogoSmall = "logo_small.bmp"; const char * const ThemeEngine::kImageSearch = "search.bmp"; const char * const ThemeEngine::kImageEraser = "eraser.bmp"; const char * const ThemeEngine::kImageDelbtn = "delbtn.bmp"; +const char * const ThemeEngine::kImageList = "list.bmp"; +const char * const ThemeEngine::kImageGrid = "grid.bmp"; struct TextDrawData { const Graphics::Font *_fontPtr; @@ -1422,7 +1424,7 @@ const Graphics::Font *ThemeEngine::loadScalableFont(const Common::String &filena for (Common::ArchiveMemberList::const_iterator i = members.begin(), end = members.end(); i != end; ++i) { Common::SeekableReadStream *stream = (*i)->createReadStream(); if (stream) { - font = Graphics::loadTTFFont(*stream, pointsize, false, + font = Graphics::loadTTFFont(*stream, pointsize, 0, false, #ifdef USE_TRANSLATION TransMan.getCharsetMapping() #else diff --git a/gui/ThemeEngine.h b/gui/ThemeEngine.h index 21711e2955..dda9f2c2b5 100644 --- a/gui/ThemeEngine.h +++ b/gui/ThemeEngine.h @@ -35,7 +35,7 @@ #include "graphics/pixelformat.h" -#define SCUMMVM_THEME_VERSION_STR "SCUMMVM_STX0.8.13" +#define SCUMMVM_THEME_VERSION_STR "SCUMMVM_STX0.8.19" class OSystem; @@ -232,6 +232,8 @@ public: static const char *const kImageSearch; ///< Search tool image used in the launcher static const char *const kImageEraser; ///< Clear input image used in the launcher static const char *const kImageDelbtn; ///< Delete characters in the predictive dialog + static const char *const kImageList; ///< List image used in save/load chooser selection + static const char *const kImageGrid; ///< Grid image used in save/load chooser selection /** * Graphics mode enumeration. diff --git a/gui/ThemeParser.cpp b/gui/ThemeParser.cpp index 9a85399ed1..8285aff7ca 100644 --- a/gui/ThemeParser.cpp +++ b/gui/ThemeParser.cpp @@ -548,11 +548,11 @@ bool ThemeParser::parseDrawStep(ParserNode *stepNode, Graphics::DrawStep *drawst else return parserError("'" + stepNode->values["fill"] + "' is not a valid fill mode for a shape."); } - + if (stepNode->values.contains("padding")) { val = stepNode->values["padding"]; int pr, pt, pl, pb; - if (parseIntegerKey(val, 4, &pl, &pt, &pr, &pb)) + if (parseIntegerKey(val, 4, &pl, &pt, &pr, &pb)) drawstep->padding.left = pl, drawstep->padding.top = pt, drawstep->padding.right = pr, diff --git a/gui/ThemeParser.h b/gui/ThemeParser.h index 82f774b803..360e3da009 100644 --- a/gui/ThemeParser.h +++ b/gui/ThemeParser.h @@ -139,7 +139,7 @@ protected: XML_PROP(height, false) XML_PROP(xpos, false) XML_PROP(ypos, false) - XML_PROP(padding, false) + XML_PROP(padding, false) XML_PROP(orientation, false) XML_PROP(file, false) KEY_END() diff --git a/gui/Tooltip.cpp b/gui/Tooltip.cpp index 85e5856cff..88124e782b 100644 --- a/gui/Tooltip.cpp +++ b/gui/Tooltip.cpp @@ -37,7 +37,7 @@ Tooltip::Tooltip() : } void Tooltip::setup(Dialog *parent, Widget *widget, int x, int y) { - assert(widget->getTooltip()); + assert(widget->hasTooltip()); _maxWidth = g_gui.xmlEval()->getVar("Globals.Tooltip.MaxWidth", 100); _xdelta = g_gui.xmlEval()->getVar("Globals.Tooltip.XDelta", 0); diff --git a/gui/about.cpp b/gui/about.cpp index 03be1f8992..e2b7064279 100644 --- a/gui/about.cpp +++ b/gui/about.cpp @@ -54,7 +54,7 @@ enum { static const char *copyright_text[] = { "", -"C0""Copyright (C) 2001-2012 The ScummVM project", +"C0""Copyright (C) 2001-2013 The ScummVM project", "C0""http://www.scummvm.org", "", "C0""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 binary.", @@ -110,16 +110,16 @@ AboutDialog::AboutDialog() const EnginePlugin::List &plugins = EngineMan.getPlugins(); EnginePlugin::List::const_iterator iter = plugins.begin(); for (; iter != plugins.end(); ++iter) { - Common::String str; - str = "C0"; - str += (**iter).getName(); - addLine(str.c_str()); + Common::String str; + str = "C0"; + str += (**iter).getName(); + addLine(str.c_str()); - str = "C2"; - str += (**iter)->getOriginalCopyright(); - addLine(str.c_str()); + str = "C2"; + str += (**iter)->getOriginalCopyright(); + addLine(str.c_str()); - //addLine(""); + //addLine(""); } for (i = 0; i < ARRAYSIZE(gpl_text); i++) diff --git a/gui/browser.h b/gui/browser.h index e5cc12ad8e..5cf091fbf4 100644 --- a/gui/browser.h +++ b/gui/browser.h @@ -48,6 +48,7 @@ public: protected: #ifdef MACOSX const void *_titleRef; + const void *_chooseRef; #else ListWidget *_fileList; StaticTextWidget *_currentPath; diff --git a/gui/browser_osx.mm b/gui/browser_osx.mm index b8aa7c50ee..ecd60915f8 100644 --- a/gui/browser_osx.mm +++ b/gui/browser_osx.mm @@ -28,20 +28,39 @@ #include "common/config-manager.h" #include "common/system.h" #include "common/algorithm.h" +#include "common/translation.h" #include <AppKit/NSOpenPanel.h> #include <Foundation/NSString.h> +#include <Foundation/NSURL.h> namespace GUI { BrowserDialog::BrowserDialog(const char *title, bool dirBrowser) : Dialog("Browser") { - _titleRef = CFStringCreateWithCString(0, title, CFStringGetSystemEncoding()); + + // remember whether this is a file browser or a directory browser. _isDirBrowser = dirBrowser; + + // Get current encoding +#ifdef USE_TRANSLATION + CFStringRef encStr = CFStringCreateWithCString(NULL, TransMan.getCurrentCharset().c_str(), kCFStringEncodingASCII); + CFStringEncoding stringEncoding = CFStringConvertIANACharSetNameToEncoding(encStr); + CFRelease(encStr); +#else + CFStringEncoding stringEncoding = kCFStringEncodingASCII; +#endif + + // Convert title to NSString + _titleRef = CFStringCreateWithCString(0, title, stringEncoding); + + // Convert button text to NSString + _chooseRef = CFStringCreateWithCString(0, _("Choose"), stringEncoding); } BrowserDialog::~BrowserDialog() { CFRelease(_titleRef); + CFRelease(_chooseRef); } int BrowserDialog::runModal() { @@ -58,16 +77,20 @@ int BrowserDialog::runModal() { // Temporarily show the real mouse CGDisplayShowCursor(kCGDirectMainDisplay); - - NSOpenPanel * panel = [NSOpenPanel openPanel]; - [panel setCanChooseDirectories:YES]; - if ([panel runModalForTypes:nil] == NSOKButton) { - const char *filename = [[panel filename] UTF8String]; - _choice = Common::FSNode(filename); - choiceMade = true; + NSOpenPanel *panel = [NSOpenPanel openPanel]; + [panel setCanChooseFiles:!_isDirBrowser]; + [panel setCanChooseDirectories:_isDirBrowser]; + [panel setTitle:(NSString *)_titleRef]; + [panel setPrompt:(NSString *)_chooseRef]; + if ([panel runModal] == NSOKButton) { + NSURL *url = [panel URL]; + if ([url isFileURL]) { + const char *filename = [[url path] UTF8String]; + _choice = Common::FSNode(filename); + choiceMade = true; + } } - // If we were in fullscreen mode, switch back if (wasFullscreen) { g_system->beginGFXTransaction(); diff --git a/gui/credits.h b/gui/credits.h index 7226864543..237d97d56c 100644 --- a/gui/credits.h +++ b/gui/credits.h @@ -106,8 +106,10 @@ static const char *credits[] = { "C1""DreamWeb", "C0""Torbj\366rn Andersson", "C0""Bertrand Augereau", +"C0""Filippos Karapetis", "C0""Vladimir Menshakov", "C2""(retired)", +"C0""Willem Jan Palenstijn", "", "C1""Gob", "C0""Torbj\366rn Andersson", @@ -157,6 +159,9 @@ static const char *credits[] = { "C1""Parallaction", "C0""peres", "", +"C1""Pegasus", +"C0""Matthew Hoops", +"", "C1""Queen", "C0""David Eriksson", "C2""(retired)", @@ -232,6 +237,15 @@ static const char *credits[] = { "C0""Filippos Karapetis", "C0""Joost Peters", "", +"C1""Toltecs", +"C0""Benjamin Haisch", +"C0""Filippos Karapetis", +"", +"C1""Tony", +"C0""Arnaud Boutonn\351", +"C0""Paul Gilbert", +"C0""Alyssa Milburn", +"", "C1""Toon", "C0""Sylvain Dupont", "", @@ -247,6 +261,9 @@ static const char *credits[] = { "C0""Gregory Montoir", "C2""(retired)", "", +"C1""Wintermute", +"C0""Einar Johan T. S\370m\345en", +"", "", "C1""Backend Teams", "C1""Android", @@ -462,6 +479,9 @@ static const char *credits[] = { "C1""Basque", "C0""Mikel Iturbe Urretxa", "", +"C1""Belarusian", +"C0""Ivan Lukyanov", +"", "C1""Catalan", "C0""Jordi Vilalta Prat", "", @@ -471,12 +491,19 @@ static const char *credits[] = { "C1""Danish", "C0""Steffen Nyeland", "", +"C1""Finnish", +"C0""Toni Saarela", +"", "C1""French", "C0""Thierry Crozat", "", +"C1""Galician", +"C0""Santiago G. Sanz", +"", "C1""German", "C0""Simon Sawatzki", "C0""Lothar Serra Mari", +"C2""(retired)", "", "C1""Hungarian", "C0""Alex Bevilacqua", @@ -674,6 +701,8 @@ static const char *credits[] = { "C2""For generously providing hosting for our buildbot, SVN repository, planet and doxygen sites as well as tons of HD space", "C0""DOSBox Team", "C2""For their awesome OPL2 and OPL3 emulator", +"C0""Yusuke Kamiyamane", +"C2""For contributing some GUI icons ", "C0""Till Kresslein", "C2""For design of modern ScummVM GUI", "C0""Jezar", @@ -690,8 +719,6 @@ static const char *credits[] = { "C2""For additional work on the original MT-32 emulator", "C0""James Woodcock", "C2""Soundtrack enhancements", -"C0""Some icons by Yusuke Kamiyamane", -"C0""", "C0""Tony Warriner and everyone at Revolution Software Ltd. for sharing with us the source of some of their brilliant games, allowing us to release Beneath a Steel Sky as freeware... and generally being supportive above and beyond the call of duty.", "C0""", "C0""John Passfield and Steve Stamatiadis for sharing the source of their classic title, Flight of the Amazon Queen and also being incredibly supportive.", @@ -710,5 +737,13 @@ static const char *credits[] = { "C0""", "C0""Broken Sword 2.5 team for providing sources of their engine and their great support.", "C0""", +"C0""Neil Dodwell and David Dew from Creative Reality for providing the source of Dreamweb and for their tremendous support.", +"C0""", +"C0""Janusz Wisniewski and Miroslaw Liminowicz from Laboratorium Komputerowe Avalon for providing full source code for Soltys and letting us redistribute the game.", +"C0""", +"C0""Jan Nedoma for providing the sources to the Wintermute-engine, and for his support while porting the engine to ScummVM.", +"C0""", +"C0""Bob Bell, Michel Kripalani, Tommy Yune, from Presto Studios for providing the source code of The Journeyman Project: Pegasus Prime.", +"C0""", "", }; diff --git a/gui/fluidsynth-dialog.cpp b/gui/fluidsynth-dialog.cpp new file mode 100644 index 0000000000..662518b557 --- /dev/null +++ b/gui/fluidsynth-dialog.cpp @@ -0,0 +1,366 @@ +/* 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 "gui/fluidsynth-dialog.h" +#include "gui/message.h" +#include "gui/widgets/tab.h" +#include "gui/widgets/popup.h" + +#include "common/config-manager.h" +#include "common/translation.h" +#include "common/debug.h" + +namespace GUI { + +enum { + kActivateChorusCmd = 'acho', + kChorusVoiceCountChangedCmd = 'cvcc', + kChorusLevelChangedCmd = 'clec', + kChorusSpeedChangedCmd = 'cspc', + kChorusDepthChangedCmd = 'cdec', + + kActivateReverbCmd = 'arev', + kReverbRoomSizeChangedCmd = 'rrsc', + kReverbDampingChangedCmd = 'rdac', + kReverbWidthChangedCmd = 'rwic', + kReverbLevelChangedCmd = 'rlec', + + kResetSettingsCmd = 'rese' +}; + +enum { + kWaveFormTypeSine = 0, + kWaveFormTypeTriangle = 1 +}; + +enum { + kInterpolationNone = 0, + kInterpolationLinear = 1, + kInterpolation4thOrder = 2, + kInterpolation7thOrder = 3 +}; + +FluidSynthSettingsDialog::FluidSynthSettingsDialog() + : Dialog("FluidSynthSettings") { + _domain = Common::ConfigManager::kApplicationDomain; + + _tabWidget = new TabWidget(this, "FluidSynthSettings.TabWidget"); + + _tabWidget->addTab(_("Reverb")); + + _reverbActivate = new CheckboxWidget(_tabWidget, "FluidSynthSettings_Reverb.EnableTabCheckbox", _("Active"), 0, kActivateReverbCmd); + + _reverbRoomSizeDesc = new StaticTextWidget(_tabWidget, "FluidSynthSettings_Reverb.RoomSizeText", _("Room:")); + _reverbRoomSizeSlider = new SliderWidget(_tabWidget, "FluidSynthSettings_Reverb.RoomSizeSlider", 0, kReverbRoomSizeChangedCmd); + // 0.00 - 1.20, Default: 0.20 + _reverbRoomSizeSlider->setMinValue(0); + _reverbRoomSizeSlider->setMaxValue(120); + _reverbRoomSizeLabel = new StaticTextWidget(_tabWidget, "FluidSynthSettings_Reverb.RoomSizeLabel", "20"); + + _reverbDampingDesc = new StaticTextWidget(_tabWidget, "FluidSynthSettings_Reverb.DampingText", _("Damp:")); + _reverbDampingSlider = new SliderWidget(_tabWidget, "FluidSynthSettings_Reverb.DampingSlider", 0, kReverbDampingChangedCmd); + // 0.00 - 1.00, Default: 0.00 + _reverbDampingSlider->setMinValue(0); + _reverbDampingSlider->setMaxValue(100); + _reverbDampingLabel = new StaticTextWidget(_tabWidget, "FluidSynthSettings_Reverb.DampingLabel", "0"); + + _reverbWidthDesc = new StaticTextWidget(_tabWidget, "FluidSynthSettings_Reverb.WidthText", _("Width:")); + _reverbWidthSlider = new SliderWidget(_tabWidget, "FluidSynthSettings_Reverb.WidthSlider", 0, kReverbWidthChangedCmd); + // 0 - 100, Default: 1 + _reverbWidthSlider->setMinValue(0); + _reverbWidthSlider->setMaxValue(100); + _reverbWidthLabel = new StaticTextWidget(_tabWidget, "FluidSynthSettings_Reverb.WidthLabel", "1"); + + _reverbLevelDesc = new StaticTextWidget(_tabWidget, "FluidSynthSettings_Reverb.LevelText", _("Level:")); + _reverbLevelSlider = new SliderWidget(_tabWidget, "FluidSynthSettings_Reverb.LevelSlider", 0, kReverbLevelChangedCmd); + // 0.00 - 1.00, Default: 0.90 + _reverbLevelSlider->setMinValue(0); + _reverbLevelSlider->setMaxValue(100); + _reverbLevelLabel = new StaticTextWidget(_tabWidget, "FluidSynthSettings_Reverb.LevelLabel", "90"); + + _tabWidget->addTab(_("Chorus")); + + _chorusActivate = new CheckboxWidget(_tabWidget, "FluidSynthSettings_Chorus.EnableTabCheckbox", _("Active"), 0, kActivateChorusCmd); + + _chorusVoiceCountDesc = new StaticTextWidget(_tabWidget, "FluidSynthSettings_Chorus.VoiceCountText", _("N:")); + _chorusVoiceCountSlider = new SliderWidget(_tabWidget, "FluidSynthSettings_Chorus.VoiceCountSlider", 0, kChorusVoiceCountChangedCmd); + // 0-99, Default: 3 + _chorusVoiceCountSlider->setMinValue(0); + _chorusVoiceCountSlider->setMaxValue(99); + _chorusVoiceCountLabel = new StaticTextWidget(_tabWidget, "FluidSynthSettings_Chorus.VoiceCountLabel", "3"); + + _chorusLevelDesc = new StaticTextWidget(_tabWidget, "FluidSynthSettings_Chorus.LevelText", _("Level:")); + _chorusLevelSlider = new SliderWidget(_tabWidget, "FluidSynthSettings_Chorus.LevelSlider", 0, kChorusLevelChangedCmd); + // 0.00 - 1.00, Default: 1.00 + _chorusLevelSlider->setMinValue(0); + _chorusLevelSlider->setMaxValue(100); + _chorusLevelLabel = new StaticTextWidget(_tabWidget, "FluidSynthSettings_Chorus.LevelLabel", "100"); + + _chorusSpeedDesc = new StaticTextWidget(_tabWidget, "FluidSynthSettings_Chorus.SpeedText", _("Speed:")); + _chorusSpeedSlider = new SliderWidget(_tabWidget, "FluidSynthSettings_Chorus.SpeedSlider", 0, kChorusSpeedChangedCmd); + // 0.30 - 5.00, Default: 0.30 + _chorusSpeedSlider->setMinValue(30); + _chorusSpeedSlider->setMaxValue(500); + _chorusSpeedLabel = new StaticTextWidget(_tabWidget, "FluidSynthSettings_Chorus.SpeedLabel", "30"); + + _chorusDepthDesc = new StaticTextWidget(_tabWidget, "FluidSynthSettings_Chorus.DepthText", _("Depth:")); + _chorusDepthSlider = new SliderWidget(_tabWidget, "FluidSynthSettings_Chorus.DepthSlider", 0, kChorusDepthChangedCmd); + // 0.00 - 21.00, Default: 8.00 + _chorusDepthSlider->setMinValue(0); + _chorusDepthSlider->setMaxValue(210); + _chorusDepthLabel = new StaticTextWidget(_tabWidget, "FluidSynthSettings_Chorus.DepthLabel", "80"); + + _chorusWaveFormTypePopUpDesc = new StaticTextWidget(_tabWidget, "FluidSynthSettings_Chorus.WaveFormTypeText", _("Type:")); + _chorusWaveFormTypePopUp = new PopUpWidget(_tabWidget, "FluidSynthSettings_Chorus.WaveFormType"); + + _chorusWaveFormTypePopUp->appendEntry(_("Sine"), kWaveFormTypeSine); + _chorusWaveFormTypePopUp->appendEntry(_("Triangle"), kWaveFormTypeTriangle); + + _tabWidget->addTab(_("Misc")); + + _miscInterpolationPopUpDesc = new StaticTextWidget(_tabWidget, "FluidSynthSettings_Misc.InterpolationText", _("Interpolation:")); + _miscInterpolationPopUp = new PopUpWidget(_tabWidget, "FluidSynthSettings_Misc.Interpolation"); + + _miscInterpolationPopUp->appendEntry(_("None (fastest)"), kInterpolationNone); + _miscInterpolationPopUp->appendEntry(_("Linear"), kInterpolationLinear); + _miscInterpolationPopUp->appendEntry(_("Fourth-order"), kInterpolation4thOrder); + _miscInterpolationPopUp->appendEntry(_("Seventh-order"), kInterpolation7thOrder); + + _tabWidget->setActiveTab(0); + + new ButtonWidget(this, "FluidSynthSettings.ResetSettings", _("Reset"), _("Reset all FluidSynth settings to their default values."), kResetSettingsCmd); + + new ButtonWidget(this, "FluidSynthSettings.Cancel", _("Cancel"), 0, kCloseCmd); + new ButtonWidget(this, "FluidSynthSettings.Ok", _("OK"), 0, kOKCmd); +} + +FluidSynthSettingsDialog::~FluidSynthSettingsDialog() { +} + +void FluidSynthSettingsDialog::open() { + Dialog::open(); + + // Reset result value + setResult(0); + + readSettings(); +} + +void FluidSynthSettingsDialog::close() { + if (getResult()) { + writeSettings(); + } + + Dialog::close(); +} + +void FluidSynthSettingsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) { + switch (cmd) { + case kActivateChorusCmd: + setChorusSettingsState(data); + break; + case kChorusVoiceCountChangedCmd: + _chorusVoiceCountLabel->setLabel(Common::String::format("%d", _chorusVoiceCountSlider->getValue())); + _chorusVoiceCountLabel->draw(); + break; + case kChorusLevelChangedCmd: + _chorusLevelLabel->setLabel(Common::String::format("%d", _chorusLevelSlider->getValue())); + _chorusLevelLabel->draw(); + break; + case kChorusSpeedChangedCmd: + _chorusSpeedLabel->setLabel(Common::String::format("%d", _chorusSpeedSlider->getValue())); + _chorusSpeedLabel->draw(); + break; + case kChorusDepthChangedCmd: + _chorusDepthLabel->setLabel(Common::String::format("%d", _chorusDepthSlider->getValue())); + _chorusDepthLabel->draw(); + break; + case kActivateReverbCmd: + setReverbSettingsState(data); + break; + case kReverbRoomSizeChangedCmd: + _reverbRoomSizeLabel->setLabel(Common::String::format("%d", _reverbRoomSizeSlider->getValue())); + _reverbRoomSizeLabel->draw(); + break; + case kReverbDampingChangedCmd: + _reverbDampingLabel->setLabel(Common::String::format("%d", _reverbDampingSlider->getValue())); + _reverbDampingLabel->draw(); + break; + case kReverbWidthChangedCmd: + _reverbWidthLabel->setLabel(Common::String::format("%d", _reverbWidthSlider->getValue())); + _reverbWidthLabel->draw(); + break; + case kReverbLevelChangedCmd: + _reverbLevelLabel->setLabel(Common::String::format("%d", _reverbLevelSlider->getValue())); + _reverbLevelLabel->draw(); + break; + case kResetSettingsCmd: { + MessageDialog alert(_("Do you really want to reset all FluidSynth settings to their default values?"), _("Yes"), _("No")); + if (alert.runModal() == GUI::kMessageOK) { + resetSettings(); + readSettings(); + draw(); + } + break; + } + case kOKCmd: + setResult(1); + close(); + break; + default: + Dialog::handleCommand(sender, cmd, data); + break; + } +} + +void FluidSynthSettingsDialog::setChorusSettingsState(bool enabled) { + _chorusVoiceCountDesc->setEnabled(enabled); + _chorusVoiceCountSlider->setEnabled(enabled); + _chorusVoiceCountLabel->setEnabled(enabled); + _chorusLevelDesc->setEnabled(enabled); + _chorusLevelSlider->setEnabled(enabled); + _chorusLevelLabel->setEnabled(enabled); + _chorusSpeedDesc->setEnabled(enabled); + _chorusSpeedSlider->setEnabled(enabled); + _chorusSpeedLabel->setEnabled(enabled); + _chorusDepthDesc->setEnabled(enabled); + _chorusDepthSlider->setEnabled(enabled); + _chorusDepthLabel->setEnabled(enabled); + _chorusWaveFormTypePopUpDesc->setEnabled(enabled); + _chorusWaveFormTypePopUp->setEnabled(enabled); +} + +void FluidSynthSettingsDialog::setReverbSettingsState(bool enabled) { + _reverbRoomSizeDesc->setEnabled(enabled); + _reverbRoomSizeSlider->setEnabled(enabled); + _reverbRoomSizeLabel->setEnabled(enabled); + _reverbDampingDesc->setEnabled(enabled); + _reverbDampingSlider->setEnabled(enabled); + _reverbDampingLabel->setEnabled(enabled); + _reverbWidthDesc->setEnabled(enabled); + _reverbWidthSlider->setEnabled(enabled); + _reverbWidthLabel->setEnabled(enabled); + _reverbLevelDesc->setEnabled(enabled); + _reverbLevelSlider->setEnabled(enabled); + _reverbLevelLabel->setEnabled(enabled); +} + +void FluidSynthSettingsDialog::readSettings() { + _chorusVoiceCountSlider->setValue(ConfMan.getInt("fluidsynth_chorus_nr", _domain)); + _chorusVoiceCountLabel->setLabel(Common::String::format("%d", _chorusVoiceCountSlider->getValue())); + _chorusLevelSlider->setValue(ConfMan.getInt("fluidsynth_chorus_level", _domain)); + _chorusLevelLabel->setLabel(Common::String::format("%d", _chorusLevelSlider->getValue())); + _chorusSpeedSlider->setValue(ConfMan.getInt("fluidsynth_chorus_speed", _domain)); + _chorusSpeedLabel->setLabel(Common::String::format("%d", _chorusSpeedSlider->getValue())); + _chorusDepthSlider->setValue(ConfMan.getInt("fluidsynth_chorus_depth", _domain)); + _chorusDepthLabel->setLabel(Common::String::format("%d", _chorusDepthSlider->getValue())); + + Common::String waveForm = ConfMan.get("fluidsynth_chorus_waveform", _domain); + if (waveForm == "sine") { + _chorusWaveFormTypePopUp->setSelectedTag(kWaveFormTypeSine); + } else if (waveForm == "triangle") { + _chorusWaveFormTypePopUp->setSelectedTag(kWaveFormTypeTriangle); + } + + _reverbRoomSizeSlider->setValue(ConfMan.getInt("fluidsynth_reverb_roomsize", _domain)); + _reverbRoomSizeLabel->setLabel(Common::String::format("%d", _reverbRoomSizeSlider->getValue())); + _reverbDampingSlider->setValue(ConfMan.getInt("fluidsynth_reverb_damping", _domain)); + _reverbDampingLabel->setLabel(Common::String::format("%d", _reverbDampingSlider->getValue())); + _reverbWidthSlider->setValue(ConfMan.getInt("fluidsynth_reverb_width", _domain)); + _reverbWidthLabel->setLabel(Common::String::format("%d", _reverbWidthSlider->getValue())); + _reverbLevelSlider->setValue(ConfMan.getInt("fluidsynth_reverb_level", _domain)); + _reverbLevelLabel->setLabel(Common::String::format("%d", _reverbLevelSlider->getValue())); + + Common::String interpolation = ConfMan.get("fluidsynth_misc_interpolation", _domain); + if (interpolation == "none") { + _miscInterpolationPopUp->setSelectedTag(kInterpolationNone); + } else if (interpolation == "linear") { + _miscInterpolationPopUp->setSelectedTag(kInterpolationLinear); + } else if (interpolation == "4th") { + _miscInterpolationPopUp->setSelectedTag(kInterpolation4thOrder); + } else if (interpolation == "7th") { + _miscInterpolationPopUp->setSelectedTag(kInterpolation7thOrder); + } + + // This may trigger redrawing, so don't do it until all sliders have + // their proper values. Otherwise, the dialog may crash because of + // invalid slider values. + _chorusActivate->setState(ConfMan.getBool("fluidsynth_chorus_activate", _domain)); + _reverbActivate->setState(ConfMan.getBool("fluidsynth_reverb_activate", _domain)); +} + +void FluidSynthSettingsDialog::writeSettings() { + ConfMan.setBool("fluidsynth_chorus_activate", _chorusActivate->getState()); + ConfMan.setInt("fluidsynth_chorus_nr", _chorusVoiceCountSlider->getValue(), _domain); + ConfMan.setInt("fluidsynth_chorus_level", _chorusLevelSlider->getValue(), _domain); + ConfMan.setInt("fluidsynth_chorus_speed", _chorusSpeedSlider->getValue(), _domain); + ConfMan.setInt("fluidsynth_chorus_depth", _chorusDepthSlider->getValue(), _domain); + + uint32 waveForm = _chorusWaveFormTypePopUp->getSelectedTag(); + if (waveForm == kWaveFormTypeSine) { + ConfMan.set("fluidsynth_chorus_waveform", "sine", _domain); + } else if (waveForm == kWaveFormTypeTriangle) { + ConfMan.set("fluidsynth_chorus_waveform", "triangle", _domain); + } else { + ConfMan.removeKey("fluidsynth_chorus_waveform", _domain); + } + + ConfMan.setBool("fluidsynth_reverb_activate", _reverbActivate->getState()); + ConfMan.setInt("fluidsynth_reverb_roomsize", _reverbRoomSizeSlider->getValue(), _domain); + ConfMan.setInt("fluidsynth_reverb_damping", _reverbDampingSlider->getValue(), _domain); + ConfMan.setInt("fluidsynth_reverb_width", _reverbWidthSlider->getValue(), _domain); + ConfMan.setInt("fluidsynth_reverb_level", _reverbLevelSlider->getValue(), _domain); + + uint32 interpolation = _miscInterpolationPopUp->getSelectedTag(); + if (interpolation == kInterpolationNone) { + ConfMan.set("fluidsynth_misc_interpolation", "none", _domain); + } else if (interpolation == kInterpolationLinear) { + ConfMan.set("fluidsynth_misc_interpolation", "linear", _domain); + } else if (interpolation == kInterpolation4thOrder) { + ConfMan.set("fluidsynth_misc_interpolation", "4th", _domain); + } else if (interpolation == kInterpolation7thOrder) { + ConfMan.set("fluidsynth_misc_interpolation", "7th", _domain); + } else { + ConfMan.removeKey("fluidsynth_misc_interpolation", _domain); + } + + // The main options dialog is responsible for writing the config file. + // That's why we don't actually flush the settings to the file here. +} + +void FluidSynthSettingsDialog::resetSettings() { + ConfMan.removeKey("fluidsynth_chorus_activate", _domain); + ConfMan.removeKey("fluidsynth_chorus_nr", _domain); + ConfMan.removeKey("fluidsynth_chorus_level", _domain); + ConfMan.removeKey("fluidsynth_chorus_speed", _domain); + ConfMan.removeKey("fluidsynth_chorus_depth", _domain); + ConfMan.removeKey("fluidsynth_chorus_waveform", _domain); + + ConfMan.removeKey("fluidsynth_reverb_activate", _domain); + ConfMan.removeKey("fluidsynth_reverb_roomsize", _domain); + ConfMan.removeKey("fluidsynth_reverb_damping", _domain); + ConfMan.removeKey("fluidsynth_reverb_width", _domain); + ConfMan.removeKey("fluidsynth_reverb_level", _domain); + + ConfMan.removeKey("fluidsynth_misc_interpolation", _domain); +} + +} // End of namespace GUI diff --git a/gui/fluidsynth-dialog.h b/gui/fluidsynth-dialog.h new file mode 100644 index 0000000000..4d74c9f93e --- /dev/null +++ b/gui/fluidsynth-dialog.h @@ -0,0 +1,104 @@ +/* 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 FLUIDSYNTH_DIALOG_H +#define FLUIDSYNTH_DIALOG_H + +#include "common/str.h" +#include "gui/dialog.h" + +namespace GUI { + +class TabWidget; +class CheckboxWidget; +class SliderWidget; +class StaticTextWidget; +class PopUpWidget; + +class FluidSynthSettingsDialog : public Dialog { +public: + FluidSynthSettingsDialog(); + ~FluidSynthSettingsDialog(); + + void open(); + void close(); + void handleCommand(CommandSender *sender, uint32 cmd, uint32 data); + +protected: + void setChorusSettingsState(bool enabled); + void setReverbSettingsState(bool enabled); + + void readSettings(); + void writeSettings(); + + void resetSettings(); + +private: + Common::String _domain; + + TabWidget *_tabWidget; + + CheckboxWidget *_chorusActivate; + + StaticTextWidget *_chorusVoiceCountDesc; + SliderWidget *_chorusVoiceCountSlider; + StaticTextWidget *_chorusVoiceCountLabel; + + StaticTextWidget *_chorusLevelDesc; + SliderWidget *_chorusLevelSlider; + StaticTextWidget *_chorusLevelLabel; + + StaticTextWidget *_chorusSpeedDesc; + SliderWidget *_chorusSpeedSlider; + StaticTextWidget *_chorusSpeedLabel; + + StaticTextWidget *_chorusDepthDesc; + SliderWidget *_chorusDepthSlider; + StaticTextWidget *_chorusDepthLabel; + + StaticTextWidget *_chorusWaveFormTypePopUpDesc; + PopUpWidget *_chorusWaveFormTypePopUp; + + CheckboxWidget *_reverbActivate; + + StaticTextWidget *_reverbRoomSizeDesc; + SliderWidget *_reverbRoomSizeSlider; + StaticTextWidget *_reverbRoomSizeLabel; + + StaticTextWidget *_reverbDampingDesc; + SliderWidget *_reverbDampingSlider; + StaticTextWidget *_reverbDampingLabel; + + StaticTextWidget *_reverbWidthDesc; + SliderWidget *_reverbWidthSlider; + StaticTextWidget *_reverbWidthLabel; + + StaticTextWidget *_reverbLevelDesc; + SliderWidget *_reverbLevelSlider; + StaticTextWidget *_reverbLevelLabel; + + StaticTextWidget *_miscInterpolationPopUpDesc; + PopUpWidget *_miscInterpolationPopUp; +}; + +} // End of namespace GUI + +#endif diff --git a/gui/gui-manager.cpp b/gui/gui-manager.cpp index abd781e1a3..a0ef4216aa 100644 --- a/gui/gui-manager.cpp +++ b/gui/gui-manager.cpp @@ -381,7 +381,7 @@ void GuiManager::runLoop() { if (tooltipCheck && _lastMousePosition.time + kTooltipDelay < _system->getMillis()) { Widget *wdg = activeDialog->findWidget(_lastMousePosition.x, _lastMousePosition.y); - if (wdg && wdg->getTooltip() && !(wdg->getFlags() & WIDGET_PRESSED)) { + if (wdg && wdg->hasTooltip() && !(wdg->getFlags() & WIDGET_PRESSED)) { Tooltip *tooltip = new Tooltip(); tooltip->setup(activeDialog, wdg, _lastMousePosition.x, _lastMousePosition.y); tooltip->runModal(); diff --git a/gui/launcher.cpp b/gui/launcher.cpp index 26fafa5279..0f4867ced5 100644 --- a/gui/launcher.cpp +++ b/gui/launcher.cpp @@ -166,7 +166,7 @@ EditGameDialog::EditGameDialog(const String &domain, const String &desc) } else { warning("Plugin for target \"%s\" not found! Game specific settings might be missing", domain.c_str()); } - + // GAME: Path to game data (r/o), extra data (r/o), and save data (r/w) String gamePath(ConfMan.get("path", _domain)); String extraPath(ConfMan.get("extrapath", _domain)); diff --git a/gui/module.mk b/gui/module.mk index d272bb0313..bda3c88cd5 100644 --- a/gui/module.mk +++ b/gui/module.mk @@ -15,6 +15,7 @@ MODULE_OBJS := \ options.o \ predictivedialog.o \ saveload.o \ + saveload-dialog.o \ themebrowser.o \ ThemeEngine.o \ ThemeEval.o \ @@ -37,5 +38,10 @@ MODULE_OBJS += \ browser.o endif +ifdef USE_FLUIDSYNTH +MODULE_OBJS += \ + fluidsynth-dialog.o +endif + # Include common rules include $(srcdir)/rules.mk diff --git a/gui/options.cpp b/gui/options.cpp index 4fc37c93da..fb57c15d98 100644 --- a/gui/options.cpp +++ b/gui/options.cpp @@ -75,6 +75,12 @@ enum { }; #endif +#ifdef USE_FLUIDSYNTH +enum { + kFluidSynthSettingsCmd = 'flst' +}; +#endif + static const char *savePeriodLabels[] = { _s("Never"), _s("every 5 mins"), _s("every 10 mins"), _s("every 15 mins"), _s("every 30 mins"), 0 }; static const int savePeriodValues[] = { 0, 5 * 60, 10 * 60, 15 * 60, 30 * 60, -1 }; static const char *outputRateLabels[] = { _s("<default>"), _s("8 kHz"), _s("11kHz"), _s("22 kHz"), _s("44 kHz"), _s("48 kHz"), 0 }; @@ -863,6 +869,10 @@ void OptionsDialog::addMIDIControls(GuiObject *boss, const Common::String &prefi _midiGainSlider->setMaxValue(1000); _midiGainLabel = new StaticTextWidget(boss, prefix + "mcMidiGainLabel", "1.00"); +#ifdef USE_FLUIDSYNTH + new ButtonWidget(boss, prefix + "mcFluidSynthSettings", _("FluidSynth Settings"), 0, kFluidSynthSettingsCmd); +#endif + _enableMIDISettings = true; } @@ -877,7 +887,7 @@ void OptionsDialog::addMT32Controls(GuiObject *boss, const Common::String &prefi _mt32Checkbox = new CheckboxWidget(boss, prefix + "mcMt32Checkbox", _c("True Roland MT-32 (no GM emulation)", "lowres"), _("Check if you want to use your real hardware Roland-compatible sound device connected to your computer")); // GS Extensions setting - _enableGSCheckbox = new CheckboxWidget(boss, prefix + "mcGSCheckbox", _("Enable Roland GS Mode"), _("Turns off General MIDI mapping for games with Roland MT-32 soundtrack")); + _enableGSCheckbox = new CheckboxWidget(boss, prefix + "mcGSCheckbox", _("Roland GS Mode (disable GM mapping)"), _("Turns off General MIDI mapping for games with Roland MT-32 soundtrack")); const MusicPlugin::List p = MusicMan.getPlugins(); // Make sure the null device is the first one in the list to avoid undesired @@ -992,7 +1002,7 @@ void OptionsDialog::addEngineControls(GuiObject *boss, const Common::String &pre ExtraGuiOptions::const_iterator iter; for (iter = engineOptions.begin(); iter != engineOptions.end(); ++iter, ++i) { Common::String id = Common::String::format("%d", i); - _engineCheckboxes.push_back(new CheckboxWidget(boss, + _engineCheckboxes.push_back(new CheckboxWidget(boss, prefix + "customOption" + id + "Checkbox", _(iter->label), _(iter->tooltip))); } } @@ -1231,12 +1241,20 @@ GlobalOptionsDialog::GlobalOptionsDialog() #ifdef SMALL_SCREEN_DEVICE _keysDialog = new KeysDialog(); #endif + +#ifdef USE_FLUIDSYNTH + _fluidSynthSettingsDialog = new FluidSynthSettingsDialog(); +#endif } GlobalOptionsDialog::~GlobalOptionsDialog() { #ifdef SMALL_SCREEN_DEVICE delete _keysDialog; #endif + +#ifdef USE_FLUIDSYNTH + delete _fluidSynthSettingsDialog; +#endif } void GlobalOptionsDialog::open() { @@ -1466,6 +1484,11 @@ void GlobalOptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3 _keysDialog->runModal(); break; #endif +#ifdef USE_FLUIDSYNTH + case kFluidSynthSettingsCmd: + _fluidSynthSettingsDialog->runModal(); + break; +#endif default: OptionsDialog::handleCommand(sender, cmd, data); } diff --git a/gui/options.h b/gui/options.h index def56cfa35..081ef4fea5 100644 --- a/gui/options.h +++ b/gui/options.h @@ -32,6 +32,10 @@ #include "gui/KeysDialog.h" #endif +#ifdef USE_FLUIDSYNTH +#include "gui/fluidsynth-dialog.h" +#endif + namespace GUI { class CheckboxWidget; @@ -208,6 +212,9 @@ protected: #ifdef SMALL_SCREEN_DEVICE KeysDialog *_keysDialog; #endif +#ifdef USE_FLUIDSYNTH + FluidSynthSettingsDialog *_fluidSynthSettingsDialog; +#endif StaticTextWidget *_savePath; ButtonWidget *_savePathClearButton; StaticTextWidget *_themePath; diff --git a/gui/predictivedialog.cpp b/gui/predictivedialog.cpp index b827d49416..ed18847a40 100644 --- a/gui/predictivedialog.cpp +++ b/gui/predictivedialog.cpp @@ -71,7 +71,7 @@ PredictiveDialog::PredictiveDialog() : Dialog("Predictive") { _btns = (ButtonWidget **)calloc(1, sizeof(ButtonWidget *) * 16); - _btns[kCancelAct] = new ButtonWidget(this, "Predictive.Cancel", _("Cancel") , 0, kCancelCmd); + _btns[kCancelAct] = new ButtonWidget(this, "Predictive.Cancel", _("Cancel") , 0, kCancelCmd); _btns[kOkAct] = new ButtonWidget(this, "Predictive.OK", _("Ok") , 0, kOkCmd); _btns[kBtn1Act] = new ButtonWidget(this, "Predictive.Button1", "1 `-.&" , 0, kBut1Cmd); _btns[kBtn2Act] = new ButtonWidget(this, "Predictive.Button2", "2 abc" , 0, kBut2Cmd); @@ -84,10 +84,10 @@ PredictiveDialog::PredictiveDialog() : Dialog("Predictive") { _btns[kBtn9Act] = new ButtonWidget(this, "Predictive.Button9", "9 wxyz" , 0, kBut9Cmd); _btns[kBtn0Act] = new ButtonWidget(this, "Predictive.Button0", "0" , 0, kBut0Cmd); // I18N: You must leave "#" as is, only word 'next' is translatable - _btns[kNextAct] = new ButtonWidget(this, "Predictive.Next", _("# next") , 0, kNextCmd); + _btns[kNextAct] = new ButtonWidget(this, "Predictive.Next", _("# next") , 0, kNextCmd); _btns[kAddAct] = new ButtonWidget(this, "Predictive.Add", _("add") , 0, kAddCmd); _btns[kAddAct]->setEnabled(false); - + #ifndef DISABLE_FANCY_THEMES _btns[kDelAct] = new PicButtonWidget(this, "Predictive.Delete", _("Delete char"), kDelCmd); ((PicButtonWidget *)_btns[kDelAct])->useThemeTransparency(true); @@ -214,7 +214,7 @@ void PredictiveDialog::handleKeyDown(Common::KeyState state) { _navigationwithkeys = true; if (_lastbutton == kBtn1Act || _lastbutton == kBtn4Act || _lastbutton == kBtn7Act) _currBtn = ButtonId(_lastbutton + 2); - else if (_lastbutton == kDelAct) + else if (_lastbutton == kDelAct) _currBtn = kBtn1Act; else if (_lastbutton == kModeAct) _currBtn = kNextAct; @@ -227,7 +227,7 @@ void PredictiveDialog::handleKeyDown(Common::KeyState state) { else _currBtn = ButtonId(_lastbutton - 1); - + if (_mode != kModeAbc && _lastbutton == kCancelAct) _currBtn = kOkAct; @@ -237,7 +237,7 @@ void PredictiveDialog::handleKeyDown(Common::KeyState state) { _navigationwithkeys = true; if (_lastbutton == kBtn3Act || _lastbutton == kBtn6Act || _lastbutton == kBtn9Act || _lastbutton == kOkAct) _currBtn = ButtonId(_lastbutton - 2); - else if (_lastbutton == kDelAct) + else if (_lastbutton == kDelAct) _currBtn = kBtn3Act; else if (_lastbutton == kBtn0Act) _currBtn = kNextAct; @@ -249,7 +249,7 @@ void PredictiveDialog::handleKeyDown(Common::KeyState state) { _currBtn = kAddAct; else _currBtn = ButtonId(_lastbutton + 1); - + if (_mode != kModeAbc && _lastbutton == kOkAct) _currBtn = kCancelAct; _needRefresh = true; @@ -260,7 +260,7 @@ void PredictiveDialog::handleKeyDown(Common::KeyState state) { _currBtn = kDelAct; else if (_lastbutton == kDelAct) _currBtn = kOkAct; - else if (_lastbutton == kModeAct) + else if (_lastbutton == kModeAct) _currBtn = kBtn7Act; else if (_lastbutton == kBtn0Act) _currBtn = kBtn8Act; @@ -286,7 +286,7 @@ void PredictiveDialog::handleKeyDown(Common::KeyState state) { _currBtn = kBtn0Act; else if (_lastbutton == kBtn9Act) _currBtn = kNextAct; - else if (_lastbutton == kModeAct) + else if (_lastbutton == kModeAct) _currBtn = kAddAct; else if (_lastbutton == kBtn0Act) _currBtn = kCancelAct; diff --git a/gui/saveload-dialog.cpp b/gui/saveload-dialog.cpp new file mode 100644 index 0000000000..c7dd62b6c6 --- /dev/null +++ b/gui/saveload-dialog.cpp @@ -0,0 +1,954 @@ +/* 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 "gui/saveload-dialog.h" +#include "common/translation.h" +#include "common/config-manager.h" + +#include "gui/message.h" +#include "gui/gui-manager.h" +#include "gui/ThemeEval.h" +#include "gui/widgets/edittext.h" + +#include "graphics/scaler.h" + +namespace GUI { + +#ifndef DISABLE_SAVELOADCHOOSER_GRID +SaveLoadChooserType getRequestedSaveLoadDialog(const MetaEngine &metaEngine) { + const Common::String &userConfig = ConfMan.get("gui_saveload_chooser", Common::ConfigManager::kApplicationDomain); + + // Check (and update if necessary) the theme config here. This catches + // resolution changes, which happened after the GUI was closed. This + // should assure that the correct GUI width/height are returned below and + // prevent the logic from picking the grid dialog, even though it is not + // possible to use it. + g_gui.checkScreenChange(); + + if (g_gui.getWidth() >= 640 && g_gui.getHeight() >= 400 + && metaEngine.hasFeature(MetaEngine::kSavesSupportMetaInfo) + && metaEngine.hasFeature(MetaEngine::kSavesSupportThumbnail) + && userConfig.equalsIgnoreCase("grid")) { + // In case we are 640x400 or higher, this dialog is not in save mode, + // the user requested the grid dialog and the engines supports it we + // try to set it up. + return kSaveLoadDialogGrid; + } else { + // In all other cases we want to use the list dialog. + return kSaveLoadDialogList; + } +} + +enum { + kListSwitchCmd = 'LIST', + kGridSwitchCmd = 'GRID' +}; +#endif // !DISABLE_SAVELOADCHOOSER_GRID + +SaveLoadChooserDialog::SaveLoadChooserDialog(const Common::String &dialogName, const bool saveMode) + : Dialog(dialogName), _metaEngine(0), _delSupport(false), _metaInfoSupport(false), + _thumbnailSupport(false), _saveDateSupport(false), _playTimeSupport(false), _saveMode(saveMode) +#ifndef DISABLE_SAVELOADCHOOSER_GRID + , _listButton(0), _gridButton(0) +#endif // !DISABLE_SAVELOADCHOOSER_GRID + { +#ifndef DISABLE_SAVELOADCHOOSER_GRID + addChooserButtons(); +#endif // !DISABLE_SAVELOADCHOOSER_GRID +} + +SaveLoadChooserDialog::SaveLoadChooserDialog(int x, int y, int w, int h, const bool saveMode) + : Dialog(x, y, w, h), _metaEngine(0), _delSupport(false), _metaInfoSupport(false), + _thumbnailSupport(false), _saveDateSupport(false), _playTimeSupport(false), _saveMode(saveMode) +#ifndef DISABLE_SAVELOADCHOOSER_GRID + , _listButton(0), _gridButton(0) +#endif // !DISABLE_SAVELOADCHOOSER_GRID + { +#ifndef DISABLE_SAVELOADCHOOSER_GRID + addChooserButtons(); +#endif // !DISABLE_SAVELOADCHOOSER_GRID +} + +void SaveLoadChooserDialog::open() { + Dialog::open(); + + // So that quitting ScummVM will not cause the dialog result to say a + // savegame was selected. + setResult(-1); +} + +int SaveLoadChooserDialog::run(const Common::String &target, const MetaEngine *metaEngine) { + _metaEngine = metaEngine; + _target = target; + _delSupport = _metaEngine->hasFeature(MetaEngine::kSupportsDeleteSave); + _metaInfoSupport = _metaEngine->hasFeature(MetaEngine::kSavesSupportMetaInfo); + _thumbnailSupport = _metaInfoSupport && _metaEngine->hasFeature(MetaEngine::kSavesSupportThumbnail); + _saveDateSupport = _metaInfoSupport && _metaEngine->hasFeature(MetaEngine::kSavesSupportCreationDate); + _playTimeSupport = _metaInfoSupport && _metaEngine->hasFeature(MetaEngine::kSavesSupportPlayTime); + + return runIntern(); +} + +void SaveLoadChooserDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) { +#ifndef DISABLE_SAVELOADCHOOSER_GRID + switch (cmd) { + case kListSwitchCmd: + setResult(kSwitchSaveLoadDialog); + // We save the requested dialog type here to avoid the setting to be + // overwritten when our reflowLayout logic selects a different dialog + // type. + ConfMan.set("gui_saveload_chooser", "list", Common::ConfigManager::kApplicationDomain); + close(); + break; + + case kGridSwitchCmd: + setResult(kSwitchSaveLoadDialog); + // See above. + ConfMan.set("gui_saveload_chooser", "grid", Common::ConfigManager::kApplicationDomain); + close(); + break; + + default: + break; + } +#endif // !DISABLE_SAVELOADCHOOSER_GRID + + return Dialog::handleCommand(sender, cmd, data); +} + +void SaveLoadChooserDialog::reflowLayout() { +#ifndef DISABLE_SAVELOADCHOOSER_GRID + addChooserButtons(); + + const SaveLoadChooserType currentType = getType(); + const SaveLoadChooserType requestedType = getRequestedSaveLoadDialog(*_metaEngine); + + // Change the dialog type if there is any need for it. + if (requestedType != currentType) { + setResult(kSwitchSaveLoadDialog); + close(); + } +#endif // !DISABLE_SAVELOADCHOOSER_GRID + + Dialog::reflowLayout(); +} + +#ifndef DISABLE_SAVELOADCHOOSER_GRID +void SaveLoadChooserDialog::addChooserButtons() { + if (_listButton) { + removeWidget(_listButton); + delete _listButton; + } + + if (_gridButton) { + removeWidget(_gridButton); + delete _gridButton; + } + + _listButton = createSwitchButton("SaveLoadChooser.ListSwitch", "L", _("List view"), ThemeEngine::kImageList, kListSwitchCmd); + _gridButton = createSwitchButton("SaveLoadChooser.GridSwitch", "G", _("Grid view"), ThemeEngine::kImageGrid, kGridSwitchCmd); + if (!_metaInfoSupport || !_thumbnailSupport || !(g_gui.getWidth() >= 640 && g_gui.getHeight() >= 400)) { + _gridButton->setEnabled(false); + _listButton->setEnabled(false); + } +} + +ButtonWidget *SaveLoadChooserDialog::createSwitchButton(const Common::String &name, const char *desc, const char *tooltip, const char *image, uint32 cmd) { + ButtonWidget *button; + +#ifndef DISABLE_FANCY_THEMES + if (g_gui.xmlEval()->getVar("Globals.ShowChooserPics") == 1 && g_gui.theme()->supportsImages()) { + button = new PicButtonWidget(this, name, tooltip, cmd); + ((PicButtonWidget *)button)->useThemeTransparency(true); + ((PicButtonWidget *)button)->setGfx(g_gui.theme()->getImageSurface(image)); + } else +#endif + button = new ButtonWidget(this, name, desc, tooltip, cmd); + + return button; +} +#endif // !DISABLE_SAVELOADCHOOSER_GRID + +// SaveLoadChooserSimple implementation + +enum { + kChooseCmd = 'CHOS', + kDelCmd = 'DEL ' +}; + +SaveLoadChooserSimple::SaveLoadChooserSimple(const String &title, const String &buttonLabel, bool saveMode) + : SaveLoadChooserDialog("SaveLoadChooser", saveMode), _list(0), _chooseButton(0), _deleteButton(0), _gfxWidget(0) { + _backgroundType = ThemeEngine::kDialogBackgroundSpecial; + + new StaticTextWidget(this, "SaveLoadChooser.Title", title); + + // Add choice list + _list = new ListWidget(this, "SaveLoadChooser.List"); + _list->setNumberingMode(kListNumberingZero); + _list->setEditable(saveMode); + + _gfxWidget = new GraphicsWidget(this, 0, 0, 10, 10); + + _date = new StaticTextWidget(this, 0, 0, 10, 10, _("No date saved"), Graphics::kTextAlignCenter); + _time = new StaticTextWidget(this, 0, 0, 10, 10, _("No time saved"), Graphics::kTextAlignCenter); + _playtime = new StaticTextWidget(this, 0, 0, 10, 10, _("No playtime saved"), Graphics::kTextAlignCenter); + + // Buttons + new ButtonWidget(this, "SaveLoadChooser.Cancel", _("Cancel"), 0, kCloseCmd); + _chooseButton = new ButtonWidget(this, "SaveLoadChooser.Choose", buttonLabel, 0, kChooseCmd); + _chooseButton->setEnabled(false); + + _deleteButton = new ButtonWidget(this, "SaveLoadChooser.Delete", _("Delete"), 0, kDelCmd); + _deleteButton->setEnabled(false); + + _delSupport = _metaInfoSupport = _thumbnailSupport = false; + + _container = new ContainerWidget(this, 0, 0, 10, 10); +// _container->setHints(THEME_HINT_USE_SHADOW); +} + +int SaveLoadChooserSimple::runIntern() { + if (_gfxWidget) + _gfxWidget->setGfx(0); + + _resultString.clear(); + reflowLayout(); + updateSaveList(); + + return Dialog::runModal(); +} + +const Common::String &SaveLoadChooserSimple::getResultString() const { + int selItem = _list->getSelected(); + return (selItem >= 0) ? _list->getSelectedString() : _resultString; +} + +void SaveLoadChooserSimple::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) { + int selItem = _list->getSelected(); + + switch (cmd) { + case kListItemActivatedCmd: + case kListItemDoubleClickedCmd: + if (selItem >= 0 && _chooseButton->isEnabled()) { + if (_list->isEditable() || !_list->getSelectedString().empty()) { + _list->endEditMode(); + if (!_saveList.empty()) { + setResult(_saveList[selItem].getSaveSlot()); + _resultString = _list->getSelectedString(); + } + close(); + } + } + break; + case kChooseCmd: + _list->endEditMode(); + if (!_saveList.empty()) { + setResult(_saveList[selItem].getSaveSlot()); + _resultString = _list->getSelectedString(); + } + close(); + break; + case kListSelectionChangedCmd: + updateSelection(true); + break; + case kDelCmd: + if (selItem >= 0 && _delSupport) { + MessageDialog alert(_("Do you really want to delete this savegame?"), + _("Delete"), _("Cancel")); + if (alert.runModal() == kMessageOK) { + _metaEngine->removeSaveState(_target.c_str(), _saveList[selItem].getSaveSlot()); + + setResult(-1); + _list->setSelected(-1); + + updateSaveList(); + updateSelection(true); + } + } + break; + case kCloseCmd: + setResult(-1); + default: + SaveLoadChooserDialog::handleCommand(sender, cmd, data); + } +} + +void SaveLoadChooserSimple::reflowLayout() { + if (g_gui.xmlEval()->getVar("Globals.SaveLoadChooser.ExtInfo.Visible") == 1 && _thumbnailSupport) { + int16 x, y; + uint16 w, h; + + if (!g_gui.xmlEval()->getWidgetData("SaveLoadChooser.Thumbnail", x, y, w, h)) + error("Error when loading position data for Save/Load Thumbnails"); + + int thumbW = kThumbnailWidth; + int thumbH = kThumbnailHeight2; + int thumbX = x + (w >> 1) - (thumbW >> 1); + int thumbY = y + kLineHeight; + + int textLines = 0; + if (!_saveDateSupport) + textLines++; + if (!_playTimeSupport) + textLines++; + + _container->resize(x, y, w, h - (kLineHeight * textLines)); + _gfxWidget->resize(thumbX, thumbY, thumbW, thumbH); + + int height = thumbY + thumbH + kLineHeight; + + if (_saveDateSupport) { + _date->resize(thumbX, height, kThumbnailWidth, kLineHeight); + height += kLineHeight; + _time->resize(thumbX, height, kThumbnailWidth, kLineHeight); + height += kLineHeight; + } + + if (_playTimeSupport) + _playtime->resize(thumbX, height, kThumbnailWidth, kLineHeight); + + _container->setVisible(true); + _gfxWidget->setVisible(true); + + _date->setVisible(_saveDateSupport); + _time->setVisible(_saveDateSupport); + + _playtime->setVisible(_playTimeSupport); + + updateSelection(false); + } else { + _container->setVisible(false); + _gfxWidget->setVisible(false); + _date->setVisible(false); + _time->setVisible(false); + _playtime->setVisible(false); + } + + SaveLoadChooserDialog::reflowLayout(); +} + +void SaveLoadChooserSimple::updateSelection(bool redraw) { + int selItem = _list->getSelected(); + + bool isDeletable = _delSupport; + bool isWriteProtected = false; + bool startEditMode = _list->isEditable(); + + // We used to support letting the themes specify the fill color with our + // initial theme based GUI. But this support was dropped. + _gfxWidget->setGfx(-1, -1, 0, 0, 0); + _date->setLabel(_("No date saved")); + _time->setLabel(_("No time saved")); + _playtime->setLabel(_("No playtime saved")); + + if (selItem >= 0 && _metaInfoSupport) { + SaveStateDescriptor desc = _metaEngine->querySaveMetaInfos(_target.c_str(), _saveList[selItem].getSaveSlot()); + + isDeletable = desc.getDeletableFlag() && _delSupport; + isWriteProtected = desc.getWriteProtectedFlag(); + + // Don't allow the user to change the description of write protected games + if (isWriteProtected) + startEditMode = false; + + if (_thumbnailSupport) { + const Graphics::Surface *thumb = desc.getThumbnail(); + if (thumb) { + _gfxWidget->setGfx(thumb); + _gfxWidget->useAlpha(256); + } + } + + if (_saveDateSupport) { + const Common::String &saveDate = desc.getSaveDate(); + if (!saveDate.empty()) + _date->setLabel(_("Date: ") + saveDate); + + const Common::String &saveTime = desc.getSaveTime(); + if (!saveTime.empty()) + _time->setLabel(_("Time: ") + saveTime); + } + + if (_playTimeSupport) { + const Common::String &playTime = desc.getPlayTime(); + if (!playTime.empty()) + _playtime->setLabel(_("Playtime: ") + playTime); + } + } + + + if (_list->isEditable()) { + // Disable the save button if nothing is selected, or if the selected + // game is write protected + _chooseButton->setEnabled(selItem >= 0 && !isWriteProtected); + + if (startEditMode) { + _list->startEditMode(); + + if (_chooseButton->isEnabled() && _list->getSelectedString() == _("Untitled savestate") && + _list->getSelectionColor() == ThemeEngine::kFontColorAlternate) { + _list->setEditString(""); + _list->setEditColor(ThemeEngine::kFontColorNormal); + } + } + } else { + // Disable the load button if nothing is selected, or if an empty + // list item is selected. + _chooseButton->setEnabled(selItem >= 0 && !_list->getSelectedString().empty()); + } + + // Delete will always be disabled if the engine doesn't support it. + _deleteButton->setEnabled(isDeletable && (selItem >= 0) && (!_list->getSelectedString().empty())); + + if (redraw) { + _gfxWidget->draw(); + _date->draw(); + _time->draw(); + _playtime->draw(); + _chooseButton->draw(); + _deleteButton->draw(); + + draw(); + } +} + +void SaveLoadChooserSimple::open() { + SaveLoadChooserDialog::open(); + + // Scroll the list to the last used entry. + _list->scrollTo(ConfMan.getInt("gui_saveload_last_pos")); +} + +void SaveLoadChooserSimple::close() { + // Save the current scroll position/used entry. + const int result = getResult(); + if (result >= 0) { + ConfMan.setInt("gui_saveload_last_pos", result); + } else { + // Use the current scroll position here. + // TODO: This means we canceled the dialog (or switch to the grid). Do + // we want to save this position here? Does the user want that? + // TODO: Do we want to save the current scroll position or the + // currently selected item here? The scroll position is what the user + // currently sees and seems to make more sense. + ConfMan.setInt("gui_saveload_last_pos", _list->getCurrentScrollPos()); + } + + _metaEngine = 0; + _target.clear(); + _saveList.clear(); + _list->setList(StringArray()); + + SaveLoadChooserDialog::close(); +} + +void SaveLoadChooserSimple::updateSaveList() { + _saveList = _metaEngine->listSaves(_target.c_str()); + + int curSlot = 0; + int saveSlot = 0; + StringArray saveNames; + ListWidget::ColorList colors; + for (SaveStateList::const_iterator x = _saveList.begin(); x != _saveList.end(); ++x) { + // Handle gaps in the list of save games + saveSlot = x->getSaveSlot(); + if (curSlot < saveSlot) { + while (curSlot < saveSlot) { + SaveStateDescriptor dummySave(curSlot, ""); + _saveList.insert_at(curSlot, dummySave); + saveNames.push_back(dummySave.getDescription()); + colors.push_back(ThemeEngine::kFontColorNormal); + curSlot++; + } + + // Sync the save list iterator + for (x = _saveList.begin(); x != _saveList.end(); ++x) { + if (x->getSaveSlot() == saveSlot) + break; + } + } + + // Show "Untitled savestate" for empty/whitespace savegame descriptions + Common::String description = x->getDescription(); + Common::String trimmedDescription = description; + trimmedDescription.trim(); + if (trimmedDescription.empty()) { + description = _("Untitled savestate"); + colors.push_back(ThemeEngine::kFontColorAlternate); + } else { + colors.push_back(ThemeEngine::kFontColorNormal); + } + + saveNames.push_back(description); + curSlot++; + } + + // Fill the rest of the save slots with empty saves + + int maximumSaveSlots = _metaEngine->getMaximumSaveSlot(); + +#ifdef __DS__ + // Low memory on the DS means too many save slots are impractical, so limit + // the maximum here. + if (maximumSaveSlots > 99) { + maximumSaveSlots = 99; + } +#endif + + Common::String emptyDesc; + for (int i = curSlot; i <= maximumSaveSlots; i++) { + saveNames.push_back(emptyDesc); + SaveStateDescriptor dummySave(i, ""); + _saveList.push_back(dummySave); + colors.push_back(ThemeEngine::kFontColorNormal); + } + + _list->setList(saveNames, &colors); +} + +// SaveLoadChooserGrid implementation + +#ifndef DISABLE_SAVELOADCHOOSER_GRID + +enum { + kNextCmd = 'NEXT', + kPrevCmd = 'PREV', + kNewSaveCmd = 'SAVE' +}; + +SaveLoadChooserGrid::SaveLoadChooserGrid(const Common::String &title, bool saveMode) + : SaveLoadChooserDialog("SaveLoadChooser", saveMode), _lines(0), _columns(0), _entriesPerPage(0), + _curPage(0), _newSaveContainer(0), _nextFreeSaveSlot(0), _buttons() { + _backgroundType = ThemeEngine::kDialogBackgroundSpecial; + + new StaticTextWidget(this, "SaveLoadChooser.Title", title); + + // Buttons + new ButtonWidget(this, "SaveLoadChooser.Delete", _("Cancel"), 0, kCloseCmd); + _nextButton = new ButtonWidget(this, "SaveLoadChooser.Choose", _("Next"), 0, kNextCmd); + _nextButton->setEnabled(false); + + _prevButton = new ButtonWidget(this, "SaveLoadChooser.Cancel", _("Prev"), 0, kPrevCmd); + _prevButton->setEnabled(false); + + // Page display + _pageDisplay = new StaticTextWidget(this, "SaveLoadChooser.PageDisplay", Common::String()); + _pageDisplay->setAlign(Graphics::kTextAlignRight); +} + +SaveLoadChooserGrid::~SaveLoadChooserGrid() { + removeWidget(_pageDisplay); + delete _pageDisplay; +} + +const Common::String &SaveLoadChooserGrid::getResultString() const { + return _resultString; +} + +void SaveLoadChooserGrid::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) { + if (cmd <= _entriesPerPage && cmd + _curPage * _entriesPerPage <= _saveList.size()) { + const SaveStateDescriptor &desc = _saveList[cmd - 1 + _curPage * _entriesPerPage]; + + if (_saveMode) { + _resultString = desc.getDescription(); + } + + setResult(desc.getSaveSlot()); + close(); + } + + switch (cmd) { + case kNextCmd: + ++_curPage; + updateSaves(); + draw(); + break; + + case kPrevCmd: + --_curPage; + updateSaves(); + draw(); + break; + + case kNewSaveCmd: + setResult(_nextFreeSaveSlot); + close(); + break; + + case kCloseCmd: + setResult(-1); + default: + SaveLoadChooserDialog::handleCommand(sender, cmd, data); + } +} + +void SaveLoadChooserGrid::handleMouseWheel(int x, int y, int direction) { + if (direction > 0) { + if (_nextButton->isEnabled()) { + ++_curPage; + updateSaves(); + draw(); + } + } else { + if (_prevButton->isEnabled()) { + --_curPage; + updateSaves(); + draw(); + } + } +} + +void SaveLoadChooserGrid::open() { + SaveLoadChooserDialog::open(); + + _saveList = _metaEngine->listSaves(_target.c_str()); + _resultString.clear(); + + // Load information to restore the last page the user had open. + assert(_entriesPerPage != 0); + const uint lastPos = ConfMan.getInt("gui_saveload_last_pos"); + const uint listSize = _saveList.size(); + uint bestMatch = 0; + uint diff = 0xFFFFFFFF; + + // We look for the nearest available slot, since a slot might be missing + // due to the user deleting it via the list based chooser, by deleting + // it by hand, etc. + for (uint i = 0; i < listSize; ++i) { + uint curDiff = ABS(_saveList[i].getSaveSlot() - (int)lastPos); + if (curDiff < diff) { + diff = curDiff; + bestMatch = i; + } + } + + _curPage = bestMatch / _entriesPerPage; + + // Determine the next free save slot for save mode + if (_saveMode) { + int lastSlot = -1; + _nextFreeSaveSlot = -1; + for (SaveStateList::const_iterator x = _saveList.begin(); x != _saveList.end(); ++x) { + const int curSlot = x->getSaveSlot(); + + // In case there was a gap found use the slot. + if (lastSlot + 1 < curSlot) { + _nextFreeSaveSlot = lastSlot + 1; + break; + } + + lastSlot = curSlot; + } + + // Use the next available slot otherwise. + if (_nextFreeSaveSlot == -1 && lastSlot + 1 < _metaEngine->getMaximumSaveSlot()) { + _nextFreeSaveSlot = lastSlot + 1; + } + } + + updateSaves(); +} + +void SaveLoadChooserGrid::reflowLayout() { + // HACK: The page display is not available in low resolution layout. We + // remove and readd the widget here to avoid our GUI from erroring out. + removeWidget(_pageDisplay); + if (g_gui.xmlEval()->getVar("Globals.ShowChooserPageDisplay") == 1) { + _pageDisplay->init(); + } + + SaveLoadChooserDialog::reflowLayout(); + destroyButtons(); + + // HACK: The whole code below really works around the fact, that we have + // no easy way to dynamically layout widgets. + const uint16 availableWidth = getWidth() - 20; + uint16 availableHeight; + + int16 x, y; + uint16 w; + g_gui.xmlEval()->getWidgetData("SaveLoadChooser.List", x, y, w, availableHeight); + + const int16 buttonWidth = kThumbnailWidth + 6; + const int16 buttonHeight = kThumbnailHeight2 + 6; + + const int16 containerFrameWidthAdd = 10; + const int16 containerFrameHeightAdd = 0; + const int16 containerWidth = buttonWidth + containerFrameWidthAdd; + const int16 containerHeight = buttonHeight + kLineHeight + containerFrameHeightAdd; + + const int16 defaultSpacingHorizontal = 4; + const int16 defaultSpacingVertical = 8; + const int16 slotAreaWidth = containerWidth + defaultSpacingHorizontal; + const int16 slotAreaHeight = containerHeight + defaultSpacingVertical; + + const uint oldEntriesPerPage = _entriesPerPage; + _columns = MAX<uint>(1, availableWidth / slotAreaWidth); + _lines = MAX<uint>(1, availableHeight / slotAreaHeight); + _entriesPerPage = _columns * _lines; + + // In save mode the first button is always "New Save", thus we need to + // adjust the entries per page here. + if (_saveMode) { + --_entriesPerPage; + } + + // Recalculate the page number + if (!_saveList.empty() && oldEntriesPerPage != 0) { + if (_entriesPerPage != 0) { + _curPage = (_curPage * oldEntriesPerPage) / _entriesPerPage; + } else { + _curPage = 0; + } + } + + const uint addX = _columns > 1 ? (availableWidth % slotAreaWidth) / (_columns - 1) : 0; + //const uint addY = _lines > 1 ? (availableHeight % slotAreaHeight) / (_lines - 1) : 0; + + _buttons.reserve(_lines * _columns); + y += defaultSpacingVertical / 2; + for (uint curLine = 0; curLine < _lines; ++curLine, y += slotAreaHeight/* + addY*/) { + for (uint curColumn = 0, curX = x + defaultSpacingHorizontal / 2; curColumn < _columns; ++curColumn, curX += slotAreaWidth + addX) { + int dstY = containerFrameHeightAdd / 2; + int dstX = containerFrameWidthAdd / 2; + + // In the save mode we will always create a new save button as the first button. + if (_saveMode && curLine == 0 && curColumn == 0) { + _newSaveContainer = new ContainerWidget(this, curX, y, containerWidth, containerHeight); + ButtonWidget *newSave = new ButtonWidget(_newSaveContainer, dstX, dstY, buttonWidth, buttonHeight, _("New Save"), _("Create a new save game"), kNewSaveCmd); + // In case no more slots are free, we will disable the new save button + if (_nextFreeSaveSlot == -1) { + newSave->setEnabled(false); + } + continue; + } + + ContainerWidget *container = new ContainerWidget(this, curX, y, containerWidth, containerHeight); + container->setVisible(false); + + // Command 0 cannot be used, since it won't be send. Thus we will adjust + // command number here, if required. This is only the case for load mode + // since for save mode, the first button used is index 1 anyway. + uint buttonCmd = curLine * _columns + curColumn; + if (!_saveMode) { + buttonCmd += 1; + } + + PicButtonWidget *button = new PicButtonWidget(container, dstX, dstY, buttonWidth, buttonHeight, 0, buttonCmd); + dstY += buttonHeight; + + StaticTextWidget *description = new StaticTextWidget(container, dstX, dstY, buttonWidth, kLineHeight, Common::String(), Graphics::kTextAlignLeft); + + _buttons.push_back(SlotButton(container, button, description)); + } + } + + if (!_target.empty()) + updateSaves(); +} + +void SaveLoadChooserGrid::close() { + // Save the current page. + const int result = getResult(); + if (result >= 0 && result != _nextFreeSaveSlot) { + // If the user selected a slot we use that one. We ignore new slots + // here, since otherwise the dialog would reset to page 0 when the + // user cancels the savename dialog. + ConfMan.setInt("gui_saveload_last_pos", result); + } else { + // Otherwise save the first entry on the current page. + // This is less precise than the solution above, since the number of + // entries shown differs between save and load version of the dialog, + // thus it might wrap to a different page than expected. + // Similar things happen on resolution changes. + // TODO: Should we ignore this here? Is the user likely to be + // interested in having this page restored when he canceled? + ConfMan.setInt("gui_saveload_last_pos", !_saveList.empty() ? _saveList[_curPage * _entriesPerPage].getSaveSlot() : 0); + } + + SaveLoadChooserDialog::close(); + hideButtons(); +} + +int SaveLoadChooserGrid::runIntern() { + int slot; + do { + const SaveLoadChooserType currentType = getType(); + const SaveLoadChooserType requestedType = getRequestedSaveLoadDialog(*_metaEngine); + + // Catch resolution changes when the save name dialog was open. + if (currentType != requestedType) { + setResult(kSwitchSaveLoadDialog); + return kSwitchSaveLoadDialog; + } + + slot = runModal(); + } while (_saveMode && slot >= 0 && !selectDescription()); + + // Special case for new save games. We need to handle this here, since + // we cannot handle it in close() without problems. + if (slot == _nextFreeSaveSlot) { + ConfMan.setInt("gui_saveload_last_pos", slot); + } + + return slot; +} + +bool SaveLoadChooserGrid::selectDescription() { + _savenameDialog.setDescription(_resultString); + _savenameDialog.setTargetSlot(getResult()); + if (_savenameDialog.runModal() == 0) { + _resultString = _savenameDialog.getDescription(); + return true; + } else { + return false; + } +} + +void SaveLoadChooserGrid::destroyButtons() { + if (_newSaveContainer) { + removeWidget(_newSaveContainer); + delete _newSaveContainer; + _newSaveContainer = 0; + } + + for (ButtonArray::iterator i = _buttons.begin(), end = _buttons.end(); i != end; ++i) { + removeWidget(i->container); + delete i->container; + } + + _buttons.clear(); +} + +void SaveLoadChooserGrid::hideButtons() { + for (ButtonArray::iterator i = _buttons.begin(), end = _buttons.end(); i != end; ++i) { + i->button->setGfx(0); + i->setVisible(false); + } +} + +void SaveLoadChooserGrid::updateSaves() { + hideButtons(); + + for (uint i = _curPage * _entriesPerPage, curNum = 0; i < _saveList.size() && curNum < _entriesPerPage; ++i, ++curNum) { + const uint saveSlot = _saveList[i].getSaveSlot(); + + SaveStateDescriptor desc = _metaEngine->querySaveMetaInfos(_target.c_str(), saveSlot); + SlotButton &curButton = _buttons[curNum]; + curButton.setVisible(true); + const Graphics::Surface *thumbnail = desc.getThumbnail(); + if (thumbnail) { + curButton.button->setGfx(desc.getThumbnail()); + } else { + curButton.button->setGfx(kThumbnailWidth, kThumbnailHeight2, 0, 0, 0); + } + curButton.description->setLabel(Common::String::format("%d. %s", saveSlot, desc.getDescription().c_str())); + + Common::String tooltip(_("Name: ")); + tooltip += desc.getDescription(); + + if (_saveDateSupport) { + const Common::String &saveDate = desc.getSaveDate(); + if (!saveDate.empty()) { + tooltip += "\n"; + tooltip += _("Date: ") + saveDate; + } + + const Common::String &saveTime = desc.getSaveTime(); + if (!saveTime.empty()) { + tooltip += "\n"; + tooltip += _("Time: ") + saveTime; + } + } + + if (_playTimeSupport) { + const Common::String &playTime = desc.getPlayTime(); + if (!playTime.empty()) { + tooltip += "\n"; + tooltip += _("Playtime: ") + playTime; + } + } + + curButton.button->setTooltip(tooltip); + + // In save mode we disable the button, when it's write protected. + // TODO: Maybe we should not display it at all then? + if (_saveMode && desc.getWriteProtectedFlag()) { + curButton.button->setEnabled(false); + } else { + curButton.button->setEnabled(true); + } + } + + const uint numPages = (_entriesPerPage != 0 && !_saveList.empty()) ? ((_saveList.size() + _entriesPerPage - 1) / _entriesPerPage) : 1; + _pageDisplay->setLabel(Common::String::format("%u/%u", _curPage + 1, numPages)); + + if (_curPage > 0) + _prevButton->setEnabled(true); + else + _prevButton->setEnabled(false); + + if ((_curPage + 1) * _entriesPerPage < _saveList.size()) + _nextButton->setEnabled(true); + else + _nextButton->setEnabled(false); +} + +SavenameDialog::SavenameDialog() + : Dialog("SavenameDialog") { + _title = new StaticTextWidget(this, "SavenameDialog.DescriptionText", Common::String()); + + new ButtonWidget(this, "SavenameDialog.Cancel", _("Cancel"), 0, kCloseCmd); + new ButtonWidget(this, "SavenameDialog.Ok", _("OK"), 0, kOKCmd); + + _description = new EditTextWidget(this, "SavenameDialog.Description", Common::String(), 0, 0, kOKCmd); +} + +void SavenameDialog::setDescription(const Common::String &desc) { + _description->setEditString(desc); +} + +const Common::String &SavenameDialog::getDescription() { + return _description->getEditString(); +} + +void SavenameDialog::open() { + Dialog::open(); + setResult(-1); + + _title->setLabel(Common::String::format(_("Enter a description for slot %d:"), _targetSlot)); +} + +void SavenameDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) { + switch (cmd) { + case kOKCmd: + setResult(0); + close(); + break; + + default: + Dialog::handleCommand(sender, cmd, data); + } +} + +#endif // !DISABLE_SAVELOADCHOOSER_GRID + +} // End of namespace GUI diff --git a/gui/saveload-dialog.h b/gui/saveload-dialog.h new file mode 100644 index 0000000000..6f7d95f73f --- /dev/null +++ b/gui/saveload-dialog.h @@ -0,0 +1,209 @@ +/* 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 GUI_SAVELOAD_DIALOG_H +#define GUI_SAVELOAD_DIALOG_H + +#include "gui/dialog.h" +#include "gui/widgets/list.h" + +#include "engines/metaengine.h" + +namespace GUI { + +#define kSwitchSaveLoadDialog -2 + +// TODO: We might want to disable the grid based save/load chooser for more +// platforms, than those which define DISABLE_FANCY_THEMES. But those are +// probably not able to handle the grid chooser anyway, so disabling it +// for them is a good start. +#ifdef DISABLE_FANCY_THEMES +#define DISABLE_SAVELOADCHOOSER_GRID +#endif // DISABLE_FANCY_THEMES + +#ifndef DISABLE_SAVELOADCHOOSER_GRID +enum SaveLoadChooserType { + kSaveLoadDialogList = 0, + kSaveLoadDialogGrid = 1 +}; + +SaveLoadChooserType getRequestedSaveLoadDialog(const MetaEngine &metaEngine); +#endif // !DISABLE_SAVELOADCHOOSER_GRID + +class SaveLoadChooserDialog : protected Dialog { +public: + SaveLoadChooserDialog(const Common::String &dialogName, const bool saveMode); + SaveLoadChooserDialog(int x, int y, int w, int h, const bool saveMode); + + virtual void open(); + + virtual void reflowLayout(); + + virtual void handleCommand(CommandSender *sender, uint32 cmd, uint32 data); + +#ifndef DISABLE_SAVELOADCHOOSER_GRID + virtual SaveLoadChooserType getType() const = 0; +#endif // !DISABLE_SAVELOADCHOOSER_GRID + + int run(const Common::String &target, const MetaEngine *metaEngine); + virtual const Common::String &getResultString() const = 0; + +protected: + virtual int runIntern() = 0; + + const bool _saveMode; + const MetaEngine *_metaEngine; + bool _delSupport; + bool _metaInfoSupport; + bool _thumbnailSupport; + bool _saveDateSupport; + bool _playTimeSupport; + Common::String _target; + +#ifndef DISABLE_SAVELOADCHOOSER_GRID + ButtonWidget *_listButton; + ButtonWidget *_gridButton; + + void addChooserButtons(); + ButtonWidget *createSwitchButton(const Common::String &name, const char *desc, const char *tooltip, const char *image, uint32 cmd = 0); +#endif // !DISABLE_SAVELOADCHOOSER_GRID +}; + +class SaveLoadChooserSimple : public SaveLoadChooserDialog { + typedef Common::String String; + typedef Common::Array<Common::String> StringArray; +public: + SaveLoadChooserSimple(const String &title, const String &buttonLabel, bool saveMode); + + virtual void handleCommand(CommandSender *sender, uint32 cmd, uint32 data); + + virtual const Common::String &getResultString() const; + + virtual void reflowLayout(); + +#ifndef DISABLE_SAVELOADCHOOSER_GRID + virtual SaveLoadChooserType getType() const { return kSaveLoadDialogList; } +#endif // !DISABLE_SAVELOADCHOOSER_GRID + + virtual void open(); + virtual void close(); +private: + virtual int runIntern(); + + ListWidget *_list; + ButtonWidget *_chooseButton; + ButtonWidget *_deleteButton; + GraphicsWidget *_gfxWidget; + ContainerWidget *_container; + StaticTextWidget *_date; + StaticTextWidget *_time; + StaticTextWidget *_playtime; + + SaveStateList _saveList; + String _resultString; + + void updateSaveList(); + void updateSelection(bool redraw); +}; + +#ifndef DISABLE_SAVELOADCHOOSER_GRID + +class EditTextWidget; + +class SavenameDialog : public Dialog { +public: + SavenameDialog(); + + void setDescription(const Common::String &desc); + const Common::String &getDescription(); + + void setTargetSlot(int slot) { _targetSlot = slot; } + + virtual void open(); +protected: + virtual void handleCommand(CommandSender *sender, uint32 cmd, uint32 data); +private: + int _targetSlot; + StaticTextWidget *_title; + EditTextWidget *_description; +}; + +class SaveLoadChooserGrid : public SaveLoadChooserDialog { +public: + SaveLoadChooserGrid(const Common::String &title, bool saveMode); + ~SaveLoadChooserGrid(); + + virtual const Common::String &getResultString() const; + + virtual void open(); + + virtual void reflowLayout(); + + virtual SaveLoadChooserType getType() const { return kSaveLoadDialogGrid; } + + virtual void close(); +protected: + virtual void handleCommand(CommandSender *sender, uint32 cmd, uint32 data); + virtual void handleMouseWheel(int x, int y, int direction); +private: + virtual int runIntern(); + + uint _columns, _lines; + uint _entriesPerPage; + uint _curPage; + SaveStateList _saveList; + + ButtonWidget *_nextButton; + ButtonWidget *_prevButton; + + StaticTextWidget *_pageDisplay; + + ContainerWidget *_newSaveContainer; + int _nextFreeSaveSlot; + Common::String _resultString; + + SavenameDialog _savenameDialog; + bool selectDescription(); + + struct SlotButton { + SlotButton() : container(0), button(0), description(0) {} + SlotButton(ContainerWidget *c, PicButtonWidget *b, StaticTextWidget *d) : container(c), button(b), description(d) {} + + ContainerWidget *container; + PicButtonWidget *button; + StaticTextWidget *description; + + void setVisible(bool state) { + container->setVisible(state); + } + }; + typedef Common::Array<SlotButton> ButtonArray; + ButtonArray _buttons; + void destroyButtons(); + void hideButtons(); + void updateSaves(); +}; + +#endif // !DISABLE_SAVELOADCHOOSER_GRID + +} // End of namespace GUI + +#endif diff --git a/gui/saveload.cpp b/gui/saveload.cpp index 67d871e133..c2bbcd9bec 100644 --- a/gui/saveload.cpp +++ b/gui/saveload.cpp @@ -20,111 +20,45 @@ */ #include "common/config-manager.h" -#include "common/translation.h" #include "common/system.h" -#include "gui/widgets/list.h" -#include "gui/message.h" #include "gui/saveload.h" -#include "gui/ThemeEval.h" +#include "gui/saveload-dialog.h" #include "gui/gui-manager.h" -#include "graphics/scaler.h" - #include "engines/metaengine.h" namespace GUI { -enum { - kChooseCmd = 'CHOS', - kDelCmd = 'DEL ' - -}; - SaveLoadChooser::SaveLoadChooser(const String &title, const String &buttonLabel, bool saveMode) - : Dialog("SaveLoadChooser"), _delSupport(0), _list(0), _chooseButton(0), _deleteButton(0), _gfxWidget(0) { - _delSupport = _metaInfoSupport = _thumbnailSupport = _saveDateSupport = _playTimeSupport = false; - - _backgroundType = ThemeEngine::kDialogBackgroundSpecial; - - new StaticTextWidget(this, "SaveLoadChooser.Title", title); - - // Add choice list - _list = new GUI::ListWidget(this, "SaveLoadChooser.List"); - _list->setNumberingMode(GUI::kListNumberingZero); - _list->setEditable(saveMode); - - _gfxWidget = new GUI::GraphicsWidget(this, 0, 0, 10, 10); - - _date = new StaticTextWidget(this, 0, 0, 10, 10, _("No date saved"), Graphics::kTextAlignCenter); - _time = new StaticTextWidget(this, 0, 0, 10, 10, _("No time saved"), Graphics::kTextAlignCenter); - _playtime = new StaticTextWidget(this, 0, 0, 10, 10, _("No playtime saved"), Graphics::kTextAlignCenter); - - // Buttons - new GUI::ButtonWidget(this, "SaveLoadChooser.Cancel", _("Cancel"), 0, kCloseCmd); - _chooseButton = new GUI::ButtonWidget(this, "SaveLoadChooser.Choose", buttonLabel, 0, kChooseCmd); - _chooseButton->setEnabled(false); - - _deleteButton = new GUI::ButtonWidget(this, "SaveLoadChooser.Delete", _("Delete"), 0, kDelCmd); - _deleteButton->setEnabled(false); - - _delSupport = _metaInfoSupport = _thumbnailSupport = false; - - _container = new GUI::ContainerWidget(this, 0, 0, 10, 10); -// _container->setHints(GUI::THEME_HINT_USE_SHADOW); + : _impl(0), _title(title), _buttonLabel(buttonLabel), _saveMode(saveMode) { } SaveLoadChooser::~SaveLoadChooser() { + delete _impl; + _impl = 0; } -int SaveLoadChooser::runModalWithCurrentTarget() { - const Common::String gameId = ConfMan.get("gameid"); - - const EnginePlugin *plugin = 0; - EngineMan.findGame(gameId, &plugin); - - return runModalWithPluginAndTarget(plugin, ConfMan.getActiveDomainName()); -} - -int SaveLoadChooser::runModalWithPluginAndTarget(const EnginePlugin *plugin, const String &target) { - if (_gfxWidget) - _gfxWidget->setGfx(0); - - // Set up the game domain as newly active domain, so - // target specific savepath will be checked - String oldDomain = ConfMan.getActiveDomainName(); - ConfMan.setActiveDomain(target); - - _plugin = plugin; - _target = target; - _delSupport = (*_plugin)->hasFeature(MetaEngine::kSupportsDeleteSave); - _metaInfoSupport = (*_plugin)->hasFeature(MetaEngine::kSavesSupportMetaInfo); - _thumbnailSupport = _metaInfoSupport && (*_plugin)->hasFeature(MetaEngine::kSavesSupportThumbnail); - _saveDateSupport = _metaInfoSupport && (*_plugin)->hasFeature(MetaEngine::kSavesSupportCreationDate); - _playTimeSupport = _metaInfoSupport && (*_plugin)->hasFeature(MetaEngine::kSavesSupportPlayTime); - _resultString.clear(); - reflowLayout(); - updateSaveList(); - - int ret = Dialog::runModal(); - - // Revert to the old active domain - ConfMan.setActiveDomain(oldDomain); - - return ret; -} - -void SaveLoadChooser::open() { - Dialog::open(); - - // So that quitting ScummVM will not cause the dialog result to say a - // savegame was selected. - setResult(-1); -} - -const Common::String &SaveLoadChooser::getResultString() const { - int selItem = _list->getSelected(); - return (selItem >= 0) ? _list->getSelectedString() : _resultString; +void SaveLoadChooser::selectChooser(const MetaEngine &engine) { +#ifndef DISABLE_SAVELOADCHOOSER_GRID + const SaveLoadChooserType requestedType = getRequestedSaveLoadDialog(engine); + if (!_impl || _impl->getType() != requestedType) { + delete _impl; + _impl = 0; + + switch (requestedType) { + case kSaveLoadDialogGrid: + _impl = new SaveLoadChooserGrid(_title, _saveMode); + break; + + case kSaveLoadDialogList: +#endif // !DISABLE_SAVELOADCHOOSER_GRID + _impl = new SaveLoadChooserSimple(_title, _buttonLabel, _saveMode); +#ifndef DISABLE_SAVELOADCHOOSER_GRID + break; + } + } +#endif // !DISABLE_SAVELOADCHOOSER_GRID } Common::String SaveLoadChooser::createDefaultSaveDescription(const int slot) const { @@ -139,267 +73,44 @@ Common::String SaveLoadChooser::createDefaultSaveDescription(const int slot) con #endif } -void SaveLoadChooser::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) { - int selItem = _list->getSelected(); - - switch (cmd) { - case GUI::kListItemActivatedCmd: - case GUI::kListItemDoubleClickedCmd: - if (selItem >= 0 && _chooseButton->isEnabled()) { - if (_list->isEditable() || !_list->getSelectedString().empty()) { - _list->endEditMode(); - if (!_saveList.empty()) { - setResult(_saveList[selItem].getSaveSlot()); - _resultString = _list->getSelectedString(); - } - close(); - } - } - break; - case kChooseCmd: - _list->endEditMode(); - if (!_saveList.empty()) { - setResult(_saveList[selItem].getSaveSlot()); - _resultString = _list->getSelectedString(); - } - close(); - break; - case GUI::kListSelectionChangedCmd: - updateSelection(true); - break; - case kDelCmd: - if (selItem >= 0 && _delSupport) { - MessageDialog alert(_("Do you really want to delete this savegame?"), - _("Delete"), _("Cancel")); - if (alert.runModal() == GUI::kMessageOK) { - (*_plugin)->removeSaveState(_target.c_str(), _saveList[selItem].getSaveSlot()); - - setResult(-1); - _list->setSelected(-1); - - updateSaveList(); - updateSelection(true); - } - } - break; - case kCloseCmd: - setResult(-1); - default: - Dialog::handleCommand(sender, cmd, data); - } -} - -void SaveLoadChooser::reflowLayout() { - if (g_gui.xmlEval()->getVar("Globals.SaveLoadChooser.ExtInfo.Visible") == 1 && _thumbnailSupport) { - int16 x, y; - uint16 w, h; - - if (!g_gui.xmlEval()->getWidgetData("SaveLoadChooser.Thumbnail", x, y, w, h)) - error("Error when loading position data for Save/Load Thumbnails"); - - int thumbW = kThumbnailWidth; - int thumbH = kThumbnailHeight2; - int thumbX = x + (w >> 1) - (thumbW >> 1); - int thumbY = y + kLineHeight; - - int textLines = 0; - if (!_saveDateSupport) - textLines++; - if (!_playTimeSupport) - textLines++; - - _container->resize(x, y, w, h - (kLineHeight * textLines)); - _gfxWidget->resize(thumbX, thumbY, thumbW, thumbH); - - int height = thumbY + thumbH + kLineHeight; - - if (_saveDateSupport) { - _date->resize(thumbX, height, kThumbnailWidth, kLineHeight); - height += kLineHeight; - _time->resize(thumbX, height, kThumbnailWidth, kLineHeight); - height += kLineHeight; - } - - if (_playTimeSupport) - _playtime->resize(thumbX, height, kThumbnailWidth, kLineHeight); - - _container->setVisible(true); - _gfxWidget->setVisible(true); - - _date->setVisible(_saveDateSupport); - _time->setVisible(_saveDateSupport); - - _playtime->setVisible(_playTimeSupport); +int SaveLoadChooser::runModalWithCurrentTarget() { + const Common::String gameId = ConfMan.get("gameid"); - _fillR = 0; - _fillG = 0; - _fillB = 0; - updateSelection(false); - } else { - _container->setVisible(false); - _gfxWidget->setVisible(false); - _date->setVisible(false); - _time->setVisible(false); - _playtime->setVisible(false); - } + const EnginePlugin *plugin = 0; + EngineMan.findGame(gameId, &plugin); - Dialog::reflowLayout(); + return runModalWithPluginAndTarget(plugin, ConfMan.getActiveDomainName()); } -void SaveLoadChooser::updateSelection(bool redraw) { - int selItem = _list->getSelected(); - - bool isDeletable = _delSupport; - bool isWriteProtected = false; - bool startEditMode = _list->isEditable(); - - _gfxWidget->setGfx(-1, -1, _fillR, _fillG, _fillB); - _date->setLabel(_("No date saved")); - _time->setLabel(_("No time saved")); - _playtime->setLabel(_("No playtime saved")); - - if (selItem >= 0 && _metaInfoSupport) { - SaveStateDescriptor desc = (*_plugin)->querySaveMetaInfos(_target.c_str(), _saveList[selItem].getSaveSlot()); - - isDeletable = desc.getDeletableFlag() && _delSupport; - isWriteProtected = desc.getWriteProtectedFlag(); - - // Don't allow the user to change the description of write protected games - if (isWriteProtected) - startEditMode = false; - - if (_thumbnailSupport) { - const Graphics::Surface *thumb = desc.getThumbnail(); - if (thumb) { - _gfxWidget->setGfx(thumb); - _gfxWidget->useAlpha(256); - } - } - - if (_saveDateSupport) { - const Common::String &saveDate = desc.getSaveDate(); - if (!saveDate.empty()) - _date->setLabel(_("Date: ") + saveDate); - - const Common::String &saveTime = desc.getSaveTime(); - if (!saveTime.empty()) - _time->setLabel(_("Time: ") + saveTime); - } - - if (_playTimeSupport) { - const Common::String &playTime = desc.getPlayTime(); - if (!playTime.empty()) - _playtime->setLabel(_("Playtime: ") + playTime); - } - } - - - if (_list->isEditable()) { - // Disable the save button if nothing is selected, or if the selected - // game is write protected - _chooseButton->setEnabled(selItem >= 0 && !isWriteProtected); +int SaveLoadChooser::runModalWithPluginAndTarget(const EnginePlugin *plugin, const String &target) { + selectChooser(**plugin); + if (!_impl) + return -1; - if (startEditMode) { - _list->startEditMode(); + // Set up the game domain as newly active domain, so + // target specific savepath will be checked + String oldDomain = ConfMan.getActiveDomainName(); + ConfMan.setActiveDomain(target); - if (_chooseButton->isEnabled() && _list->getSelectedString() == _("Untitled savestate") && - _list->getSelectionColor() == ThemeEngine::kFontColorAlternate) { - _list->setEditString(""); - _list->setEditColor(ThemeEngine::kFontColorNormal); - } + int ret; + do { + ret = _impl->run(target, &(**plugin)); +#ifndef DISABLE_SAVELOADCHOOSER_GRID + if (ret == kSwitchSaveLoadDialog) { + selectChooser(**plugin); } - } else { - // Disable the load button if nothing is selected, or if an empty - // list item is selected. - _chooseButton->setEnabled(selItem >= 0 && !_list->getSelectedString().empty()); - } - - // Delete will always be disabled if the engine doesn't support it. - _deleteButton->setEnabled(isDeletable && (selItem >= 0) && (!_list->getSelectedString().empty())); - - if (redraw) { - _gfxWidget->draw(); - _date->draw(); - _time->draw(); - _playtime->draw(); - _chooseButton->draw(); - _deleteButton->draw(); - - draw(); - } -} +#endif // !DISABLE_SAVELOADCHOOSER_GRID + } while (ret < -1); -void SaveLoadChooser::close() { - _plugin = 0; - _target.clear(); - _saveList.clear(); - _list->setList(StringArray()); + // Revert to the old active domain + ConfMan.setActiveDomain(oldDomain); - Dialog::close(); + return ret; } -void SaveLoadChooser::updateSaveList() { - _saveList = (*_plugin)->listSaves(_target.c_str()); - - int curSlot = 0; - int saveSlot = 0; - StringArray saveNames; - ListWidget::ColorList colors; - for (SaveStateList::const_iterator x = _saveList.begin(); x != _saveList.end(); ++x) { - // Handle gaps in the list of save games - saveSlot = x->getSaveSlot(); - if (curSlot < saveSlot) { - while (curSlot < saveSlot) { - SaveStateDescriptor dummySave(curSlot, ""); - _saveList.insert_at(curSlot, dummySave); - saveNames.push_back(dummySave.getDescription()); - colors.push_back(ThemeEngine::kFontColorNormal); - curSlot++; - } - - // Sync the save list iterator - for (x = _saveList.begin(); x != _saveList.end(); ++x) { - if (x->getSaveSlot() == saveSlot) - break; - } - } - - // Show "Untitled savestate" for empty/whitespace savegame descriptions - Common::String description = x->getDescription(); - Common::String trimmedDescription = description; - trimmedDescription.trim(); - if (trimmedDescription.empty()) { - description = _("Untitled savestate"); - colors.push_back(ThemeEngine::kFontColorAlternate); - } else { - colors.push_back(ThemeEngine::kFontColorNormal); - } - - saveNames.push_back(description); - curSlot++; - } - - // Fill the rest of the save slots with empty saves - - int maximumSaveSlots = (*_plugin)->getMaximumSaveSlot(); - -#ifdef __DS__ - // Low memory on the DS means too many save slots are impractical, so limit - // the maximum here. - if (maximumSaveSlots > 99) { - maximumSaveSlots = 99; - } -#endif - - Common::String emptyDesc; - for (int i = curSlot; i <= maximumSaveSlots; i++) { - saveNames.push_back(emptyDesc); - SaveStateDescriptor dummySave(i, ""); - _saveList.push_back(dummySave); - colors.push_back(ThemeEngine::kFontColorNormal); - } - - _list->setList(saveNames, &colors); +const Common::String &SaveLoadChooser::getResultString() const { + assert(_impl); + return _impl->getResultString(); } } // End of namespace GUI diff --git a/gui/saveload.h b/gui/saveload.h index a19f5ab083..17fd99a31d 100644 --- a/gui/saveload.h +++ b/gui/saveload.h @@ -19,54 +19,30 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#ifndef GUI_SAVELOAD_DIALOG_H -#define GUI_SAVELOAD_DIALOG_H +#ifndef GUI_SAVELOAD_H +#define GUI_SAVELOAD_H #include "gui/dialog.h" #include "engines/metaengine.h" namespace GUI { -class ListWidget; -class GraphicsWidget; -class ButtonWidget; -class CommandSender; -class ContainerWidget; -class StaticTextWidget; +class SaveLoadChooserDialog; -class SaveLoadChooser : GUI::Dialog { +class SaveLoadChooser { typedef Common::String String; - typedef Common::Array<Common::String> StringArray; protected: - GUI::ListWidget *_list; - GUI::ButtonWidget *_chooseButton; - GUI::ButtonWidget *_deleteButton; - GUI::GraphicsWidget *_gfxWidget; - GUI::ContainerWidget *_container; - GUI::StaticTextWidget *_date; - GUI::StaticTextWidget *_time; - GUI::StaticTextWidget *_playtime; + SaveLoadChooserDialog *_impl; - const EnginePlugin *_plugin; - bool _delSupport; - bool _metaInfoSupport; - bool _thumbnailSupport; - bool _saveDateSupport; - bool _playTimeSupport; - String _target; - SaveStateList _saveList; - String _resultString; + const String _title; + const String _buttonLabel; + const bool _saveMode; - uint8 _fillR, _fillG, _fillB; - - void updateSaveList(); - void updateSelection(bool redraw); + void selectChooser(const MetaEngine &engine); public: SaveLoadChooser(const String &title, const String &buttonLabel, bool saveMode); ~SaveLoadChooser(); - virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data); - /** * Runs the save/load chooser with the currently active config manager * domain as target. @@ -75,7 +51,6 @@ public: */ int runModalWithCurrentTarget(); int runModalWithPluginAndTarget(const EnginePlugin *plugin, const String &target); - void open(); const Common::String &getResultString() const; @@ -93,10 +68,6 @@ public: * @return The slot description. */ Common::String createDefaultSaveDescription(const int slot) const; - - virtual void reflowLayout(); - - virtual void close(); }; } // End of namespace GUI diff --git a/gui/themes/default.inc b/gui/themes/default.inc index 86d0061e1b..78c04f14c6 100644 --- a/gui/themes/default.inc +++ b/gui/themes/default.inc @@ -619,6 +619,8 @@ "<def var='ShowLauncherLogo' value='0'/> " "<def var='ShowGlobalMenuLogo' value='0'/> " "<def var='ShowSearchPic' value='0'/> " +"<def var='ShowChooserPics' value='0'/> " +"<def var='ShowChooserPageDisplay' value='0'/> " "<def var='SaveLoadChooser.ExtInfo.Visible' value='0'/> " "<def var='KeyMapper.Spacing' value='5'/> " "<def var='KeyMapper.LabelWidth' value='80'/> " @@ -889,7 +891,7 @@ "</layout> " "</dialog> " "<dialog name='GlobalOptions_MIDI' overlays='Dialog.GlobalOptions.TabWidget'> " -"<layout type='vertical' padding='16,16,16,16' spacing='8'> " +"<layout type='vertical' padding='16,16,16,16' spacing='6'> " "<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " "<widget name='auPrefGmPopupDesc' " "type='OptionsLabel' " @@ -925,6 +927,10 @@ "height='Globals.Line.Height' " "/> " "</layout> " +"<widget name='mcFluidSynthSettings' " +"width='150' " +"height='Globals.Button.Height' " +"/> " "</layout> " "</dialog> " "<dialog name='GlobalOptions_MT32' overlays='Dialog.GlobalOptions.TabWidget'> " @@ -1357,11 +1363,166 @@ "</layout> " "</layout> " "</dialog> " +"<dialog name='FluidSynthSettings' overlays='GlobalOptions' shading='dim'> " +"<layout type='vertical' padding='0,0,0,0'> " +"<widget name='TabWidget'/> " +"<layout type='horizontal' padding='8,8,8,8'> " +"<space/> " +"<widget name='ResetSettings' " +"type='Button' " +"/> " +"<widget name='Cancel' " +"type='Button' " +"/> " +"<widget name='Ok' " +"type='Button' " +"/> " +"</layout> " +"</layout> " +"</dialog> " +"<dialog name='FluidSynthSettings_Chorus' overlays='Dialog.FluidSynthSettings.TabWidget'> " +"<layout type='vertical' padding='8,8,8,8' spacing='6'> " +"<widget name='EnableTabCheckbox' " +"type='Checkbox' " +"/> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<widget name='VoiceCountText' " +"type='OptionsLabel' " +"/> " +"<widget name='VoiceCountSlider' " +"type='Slider' " +"/> " +"<widget name='VoiceCountLabel' " +"width='32' " +"height='Globals.Line.Height' " +"/> " +"</layout> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<widget name='LevelText' " +"type='OptionsLabel' " +"/> " +"<widget name='LevelSlider' " +"type='Slider' " +"/> " +"<widget name='LevelLabel' " +"width='32' " +"height='Globals.Line.Height' " +"/> " +"</layout> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<widget name='SpeedText' " +"type='OptionsLabel' " +"/> " +"<widget name='SpeedSlider' " +"type='Slider' " +"/> " +"<widget name='SpeedLabel' " +"width='32' " +"height='Globals.Line.Height' " +"/> " +"</layout> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<widget name='DepthText' " +"type='OptionsLabel' " +"/> " +"<widget name='DepthSlider' " +"type='Slider' " +"/> " +"<widget name='DepthLabel' " +"width='32' " +"height='Globals.Line.Height' " +"/> " +"</layout> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<widget name='WaveFormTypeText' " +"type='OptionsLabel' " +"/> " +"<widget name='WaveFormType' " +"type='PopUp' " +"/> " +"</layout> " +"</layout> " +"</dialog> " +"<dialog name='FluidSynthSettings_Reverb' overlays='Dialog.FluidSynthSettings.TabWidget'> " +"<layout type='vertical' padding='8,8,8,8' spacing='6'> " +"<widget name='EnableTabCheckbox' " +"type='Checkbox' " +"/> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<widget name='RoomSizeText' " +"type='OptionsLabel' " +"/> " +"<widget name='RoomSizeSlider' " +"type='Slider' " +"/> " +"<widget name='RoomSizeLabel' " +"width='32' " +"height='Globals.Line.Height' " +"/> " +"</layout> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<widget name='DampingText' " +"type='OptionsLabel' " +"/> " +"<widget name='DampingSlider' " +"type='Slider' " +"/> " +"<widget name='DampingLabel' " +"width='32' " +"height='Globals.Line.Height' " +"/> " +"</layout> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<widget name='WidthText' " +"type='OptionsLabel' " +"/> " +"<widget name='WidthSlider' " +"type='Slider' " +"/> " +"<widget name='WidthLabel' " +"width='32' " +"height='Globals.Line.Height' " +"/> " +"</layout> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<widget name='LevelText' " +"type='OptionsLabel' " +"/> " +"<widget name='LevelSlider' " +"type='Slider' " +"/> " +"<widget name='LevelLabel' " +"width='32' " +"height='Globals.Line.Height' " +"/> " +"</layout> " +"</layout> " +"</dialog> " +"<dialog name='FluidSynthSettings_Misc' overlays='Dialog.FluidSynthSettings.TabWidget'> " +"<layout type='vertical' padding='8,8,8,8' spacing='6'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<widget name='InterpolationText' " +"type='OptionsLabel' " +"/> " +"<widget name='Interpolation' " +"type='PopUp' " +"/> " +"</layout> " +"</layout> " +"</dialog> " "<dialog name='SaveLoadChooser' overlays='screen' inset='8' shading='dim'> " "<layout type='vertical' padding='8,8,8,8' center='true'> " "<widget name='Title' height='Globals.Line.Height'/> " "<widget name='List' /> " "<layout type='horizontal' padding='0,0,16,0'> " +"<widget name='ListSwitch' " +"height='Globals.Line.Height' " +"width='Globals.Line.Height' " +"/> " +"<widget name='GridSwitch' " +"height='Globals.Line.Height' " +"width='Globals.Line.Height' " +"/> " "<space/> " "<widget name='Delete' " "type='Button' " @@ -1376,6 +1537,25 @@ "</layout> " "</layout> " "</dialog> " +"<dialog name='SavenameDialog' overlays='screen_center'> " +"<layout type='vertical' padding='8,8,8,8'> " +"<widget name='DescriptionText' " +"width='180' " +"height='Globals.Line.Height' " +"/> " +"<widget name='Description' " +"height='19' " +"/> " +"<layout type='horizontal' padding='0,0,16,0'> " +"<widget name='Cancel' " +"type='Button' " +"/> " +"<widget name='Ok' " +"type='Button' " +"/> " +"</layout> " +"</layout> " +"</dialog> " "<dialog name='ScummHelp' overlays='screen'> " "<layout type='vertical' padding='8,8,8,8'> " "<widget name='Title' " @@ -1564,6 +1744,8 @@ "<def var='ShowLauncherLogo' value='0'/> " "<def var='ShowGlobalMenuLogo' value='0'/> " "<def var='ShowSearchPic' value='0'/> " +"<def var='ShowChooserPics' value='0'/> " +"<def var='ShowChooserPageDisplay' value='1'/> " "<def var='SaveLoadChooser.ExtInfo.Visible' value='1'/> " "<def var='KeyMapper.Spacing' value='10'/> " "<def var='KeyMapper.LabelWidth' value='100'/> " @@ -1874,6 +2056,10 @@ "height='Globals.Line.Height' " "/> " "</layout> " +"<widget name='mcFluidSynthSettings' " +"width='200' " +"height='Globals.Button.Height' " +"/> " "</layout> " "</dialog> " "<dialog name='GlobalOptions_MT32' overlays='Dialog.GlobalOptions.TabWidget'> " @@ -2292,11 +2478,165 @@ "</layout> " "</layout> " "</dialog> " +"<dialog name='FluidSynthSettings' overlays='GlobalOptions' shading='dim'> " +"<layout type='vertical' padding='0,0,0,0'> " +"<widget name='TabWidget'/> " +"<layout type='horizontal' padding='16,16,16,16'> " +"<space/> " +"<widget name='ResetSettings' " +"type='Button' " +"/> " +"<widget name='Cancel' " +"type='Button' " +"/> " +"<widget name='Ok' " +"type='Button' " +"/> " +"</layout> " +"</layout> " +"</dialog> " +"<dialog name='FluidSynthSettings_Chorus' overlays='Dialog.FluidSynthSettings.TabWidget'> " +"<layout type='vertical' padding='16,16,16,16' spacing='8'> " +"<widget name='EnableTabCheckbox' " +"type='Checkbox' " +"/> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<widget name='VoiceCountText' " +"type='OptionsLabel' " +"/> " +"<widget name='VoiceCountSlider' " +"type='Slider' " +"/> " +"<widget name='VoiceCountLabel' " +"width='32' " +"height='Globals.Line.Height' " +"/> " +"</layout> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<widget name='LevelText' " +"type='OptionsLabel' " +"/> " +"<widget name='LevelSlider' " +"type='Slider' " +"/> " +"<widget name='LevelLabel' " +"width='32' " +"height='Globals.Line.Height' " +"/> " +"</layout> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<widget name='SpeedText' " +"type='OptionsLabel' " +"/> " +"<widget name='SpeedSlider' " +"type='Slider' " +"/> " +"<widget name='SpeedLabel' " +"width='32' " +"height='Globals.Line.Height' " +"/> " +"</layout> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<widget name='DepthText' " +"type='OptionsLabel' " +"/> " +"<widget name='DepthSlider' " +"type='Slider' " +"/> " +"<widget name='DepthLabel' " +"width='32' " +"height='Globals.Line.Height' " +"/> " +"</layout> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<widget name='WaveFormTypeText' " +"type='OptionsLabel' " +"/> " +"<widget name='WaveFormType' " +"type='PopUp' " +"/> " +"</layout> " +"</layout> " +"</dialog> " +"<dialog name='FluidSynthSettings_Reverb' overlays='Dialog.FluidSynthSettings.TabWidget'> " +"<layout type='vertical' padding='16,16,16,16' spacing='8'> " +"<widget name='EnableTabCheckbox' " +"type='Checkbox' " +"/> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<widget name='RoomSizeText' " +"type='OptionsLabel' " +"/> " +"<widget name='RoomSizeSlider' " +"type='Slider' " +"/> " +"<widget name='RoomSizeLabel' " +"width='32' " +"height='Globals.Line.Height' " +"/> " +"</layout> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<widget name='DampingText' " +"type='OptionsLabel' " +"/> " +"<widget name='DampingSlider' " +"type='Slider' " +"/> " +"<widget name='DampingLabel' " +"width='32' " +"height='Globals.Line.Height' " +"/> " +"</layout> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<widget name='WidthText' " +"type='OptionsLabel' " +"/> " +"<widget name='WidthSlider' " +"type='Slider' " +"/> " +"<widget name='WidthLabel' " +"width='32' " +"height='Globals.Line.Height' " +"/> " +"</layout> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<widget name='LevelText' " +"type='OptionsLabel' " +"/> " +"<widget name='LevelSlider' " +"type='Slider' " +"/> " +"<widget name='LevelLabel' " +"width='32' " +"height='Globals.Line.Height' " +"/> " +"</layout> " +"</layout> " +"</dialog> " +"<dialog name='FluidSynthSettings_Misc' overlays='Dialog.FluidSynthSettings.TabWidget'> " +"<layout type='vertical' padding='16,16,16,16' spacing='8'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<widget name='InterpolationText' " +"type='OptionsLabel' " +"/> " +"<widget name='Interpolation' " +"type='PopUp' " +"/> " +"</layout> " +"</layout> " +"</dialog> " "<dialog name='SaveLoadChooser' overlays='screen' inset='8' shading='dim'> " "<layout type='vertical' padding='8,8,8,32' center='true'> " +"<layout type='horizontal' padding='0,0,0,0'> " "<widget name='Title' " "height='Globals.Line.Height' " "/> " +"<space/> " +"<widget name='PageDisplay' " +"width='200' " +"height='Globals.Line.Height' " +"/> " +"</layout> " "<layout type='horizontal' padding='0,0,0,16' spacing='16'> " "<widget name='List' /> " "<widget name='Thumbnail' " @@ -2305,6 +2645,14 @@ "/> " "</layout> " "<layout type='horizontal' padding='0,0,0,0'> " +"<widget name='ListSwitch' " +"height='Globals.Line.Height' " +"width='Globals.Line.Height' " +"/> " +"<widget name='GridSwitch' " +"height='Globals.Line.Height' " +"width='Globals.Line.Height' " +"/> " "<space/> " "<widget name='Delete' " "type='Button' " @@ -2319,6 +2667,26 @@ "</layout> " "</layout> " "</dialog> " +"<dialog name='SavenameDialog' overlays='screen_center'> " +"<layout type='vertical' padding='8,8,8,8'> " +"<widget name='DescriptionText' " +"width='320' " +"height='Globals.Line.Height' " +"/> " +"<widget name='Description' " +"height='19' " +"/> " +"<layout type='horizontal' padding='0,0,16,0'> " +"<widget name='Cancel' " +"type='Button' " +"/> " +"<space size='96'/> " +"<widget name='Ok' " +"type='Button' " +"/> " +"</layout> " +"</layout> " +"</dialog> " "<dialog name='ScummHelp' overlays='screen_center'> " "<layout type='vertical' padding='8,8,8,8' center='true'> " "<widget name='Title' " diff --git a/gui/themes/scummclassic.zip b/gui/themes/scummclassic.zip Binary files differindex d126ed0774..3183417db9 100644 --- a/gui/themes/scummclassic.zip +++ b/gui/themes/scummclassic.zip diff --git a/gui/themes/scummclassic/THEMERC b/gui/themes/scummclassic/THEMERC index d4bed29cf8..36eaacd168 100644 --- a/gui/themes/scummclassic/THEMERC +++ b/gui/themes/scummclassic/THEMERC @@ -1 +1 @@ -[SCUMMVM_STX0.8.13:ScummVM Classic Theme:No Author] +[SCUMMVM_STX0.8.19:ScummVM Classic Theme:No Author] diff --git a/gui/themes/scummclassic/classic_layout.stx b/gui/themes/scummclassic/classic_layout.stx index 8717892995..312a90e78f 100644 --- a/gui/themes/scummclassic/classic_layout.stx +++ b/gui/themes/scummclassic/classic_layout.stx @@ -32,6 +32,9 @@ <def var = 'ShowGlobalMenuLogo' value = '0'/> <def var = 'ShowSearchPic' value = '0'/> + <def var = 'ShowChooserPics' value = '0'/> + <def var = 'ShowChooserPageDisplay' value = '1'/> + <def var = 'SaveLoadChooser.ExtInfo.Visible' value = '1'/> <def var = 'KeyMapper.Spacing' value = '10'/> @@ -356,6 +359,10 @@ height = 'Globals.Line.Height' /> </layout> + <widget name = 'mcFluidSynthSettings' + width = '200' + height = 'Globals.Button.Height' + /> </layout> </dialog> @@ -791,11 +798,169 @@ </layout> </dialog> - <dialog name = 'SaveLoadChooser' overlays = 'screen' inset = '8' shading = 'dim'> - <layout type = 'vertical' padding = '8, 8, 8, 32' center = 'true'> - <widget name = 'Title' + <dialog name = 'FluidSynthSettings' overlays = 'GlobalOptions' shading = 'dim'> + <layout type = 'vertical' padding = '0, 0, 0, 0'> + <widget name = 'TabWidget'/> + <layout type = 'horizontal' padding = '16, 16, 16, 16'> + <space/> + <widget name = 'ResetSettings' + type = 'Button' + /> + <widget name = 'Cancel' + type = 'Button' + /> + <widget name = 'Ok' + type = 'Button' + /> + </layout> + </layout> + </dialog> + + <dialog name = 'FluidSynthSettings_Chorus' overlays = 'Dialog.FluidSynthSettings.TabWidget'> + <layout type = 'vertical' padding = '16, 16, 16, 16' spacing = '8'> + <widget name = 'EnableTabCheckbox' + type = 'Checkbox' + /> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'VoiceCountText' + type = 'OptionsLabel' + /> + <widget name = 'VoiceCountSlider' + type = 'Slider' + /> + <widget name = 'VoiceCountLabel' + width = '32' + height = 'Globals.Line.Height' + /> + </layout> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'LevelText' + type = 'OptionsLabel' + /> + <widget name = 'LevelSlider' + type = 'Slider' + /> + <widget name = 'LevelLabel' + width = '32' + height = 'Globals.Line.Height' + /> + </layout> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'SpeedText' + type = 'OptionsLabel' + /> + <widget name = 'SpeedSlider' + type = 'Slider' + /> + <widget name = 'SpeedLabel' + width = '32' + height = 'Globals.Line.Height' + /> + </layout> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'DepthText' + type = 'OptionsLabel' + /> + <widget name = 'DepthSlider' + type = 'Slider' + /> + <widget name = 'DepthLabel' + width = '32' height = 'Globals.Line.Height' + /> + </layout> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'WaveFormTypeText' + type = 'OptionsLabel' + /> + <widget name = 'WaveFormType' + type = 'PopUp' + /> + </layout> + </layout> + </dialog> + + <dialog name = 'FluidSynthSettings_Reverb' overlays = 'Dialog.FluidSynthSettings.TabWidget'> + <layout type = 'vertical' padding = '16, 16, 16, 16' spacing = '8'> + <widget name = 'EnableTabCheckbox' + type = 'Checkbox' /> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'RoomSizeText' + type = 'OptionsLabel' + /> + <widget name = 'RoomSizeSlider' + type = 'Slider' + /> + <widget name = 'RoomSizeLabel' + width = '32' + height = 'Globals.Line.Height' + /> + </layout> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'DampingText' + type = 'OptionsLabel' + /> + <widget name = 'DampingSlider' + type = 'Slider' + /> + <widget name = 'DampingLabel' + width = '32' + height = 'Globals.Line.Height' + /> + </layout> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'WidthText' + type = 'OptionsLabel' + /> + <widget name = 'WidthSlider' + type = 'Slider' + /> + <widget name = 'WidthLabel' + width = '32' + height = 'Globals.Line.Height' + /> + </layout> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'LevelText' + type = 'OptionsLabel' + /> + <widget name = 'LevelSlider' + type = 'Slider' + /> + <widget name = 'LevelLabel' + width = '32' + height = 'Globals.Line.Height' + /> + </layout> + </layout> + </dialog> + + <dialog name = 'FluidSynthSettings_Misc' overlays = 'Dialog.FluidSynthSettings.TabWidget'> + <layout type = 'vertical' padding = '16, 16, 16, 16' spacing = '8'> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'InterpolationText' + type = 'OptionsLabel' + /> + <widget name = 'Interpolation' + type = 'PopUp' + /> + </layout> + </layout> + </dialog> + + <dialog name = 'SaveLoadChooser' overlays = 'screen' inset = '8' shading = 'dim'> + <layout type = 'vertical' padding = '8, 8, 8, 32' center = 'true'> + <layout type = 'horizontal' padding = '0, 0, 0, 0'> + <widget name = 'Title' + height = 'Globals.Line.Height' + /> + <space/> + <widget name = 'PageDisplay' + width = '200' + height = 'Globals.Line.Height' + /> + </layout> <layout type = 'horizontal' padding = '0, 0, 0, 16' spacing = '16'> <widget name = 'List' /> <widget name = 'Thumbnail' @@ -804,6 +969,14 @@ /> </layout> <layout type = 'horizontal' padding = '0, 0, 0, 0'> + <widget name = 'ListSwitch' + height = 'Globals.Line.Height' + width = 'Globals.Line.Height' + /> + <widget name = 'GridSwitch' + height = 'Globals.Line.Height' + width = 'Globals.Line.Height' + /> <space/> <widget name = 'Delete' type = 'Button' @@ -819,6 +992,27 @@ </layout> </dialog> + <dialog name = 'SavenameDialog' overlays = 'screen_center'> + <layout type = 'vertical' padding = '8, 8, 8, 8'> + <widget name = 'DescriptionText' + width = '320' + height = 'Globals.Line.Height' + /> + <widget name = 'Description' + height = '19' + /> + <layout type = 'horizontal' padding = '0, 0, 16, 0'> + <widget name = 'Cancel' + type = 'Button' + /> + <space size = '96'/> + <widget name = 'Ok' + type = 'Button' + /> + </layout> + </layout> + </dialog> + <dialog name = 'ScummHelp' overlays = 'screen_center'> <layout type = 'vertical' padding = '8, 8, 8, 8' center = 'true'> <widget name = 'Title' diff --git a/gui/themes/scummclassic/classic_layout_lowres.stx b/gui/themes/scummclassic/classic_layout_lowres.stx index 8f5db9d364..d42efb5aa4 100644 --- a/gui/themes/scummclassic/classic_layout_lowres.stx +++ b/gui/themes/scummclassic/classic_layout_lowres.stx @@ -33,6 +33,9 @@ <def var = 'ShowGlobalMenuLogo' value = '0'/> <def var = 'ShowSearchPic' value = '0'/> + <def var = 'ShowChooserPics' value = '0'/> + <def var = 'ShowChooserPageDisplay' value = '0'/> + <def var = 'SaveLoadChooser.ExtInfo.Visible' value = '0'/> <def var = 'KeyMapper.Spacing' value = '5'/> @@ -318,7 +321,7 @@ </dialog> <dialog name = 'GlobalOptions_MIDI' overlays = 'Dialog.GlobalOptions.TabWidget'> - <layout type = 'vertical' padding = '16, 16, 16, 16' spacing = '8'> + <layout type = 'vertical' padding = '16, 16, 16, 16' spacing = '6'> <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '6' center = 'true'> <widget name = 'auPrefGmPopupDesc' type = 'OptionsLabel' @@ -354,6 +357,10 @@ height = 'Globals.Line.Height' /> </layout> + <widget name = 'mcFluidSynthSettings' + width = '150' + height = 'Globals.Button.Height' + /> </layout> </dialog> @@ -802,11 +809,170 @@ </layout> </dialog> + <dialog name = 'FluidSynthSettings' overlays = 'GlobalOptions' shading = 'dim'> + <layout type = 'vertical' padding = '0, 0, 0, 0'> + <widget name = 'TabWidget'/> + <layout type = 'horizontal' padding = '8, 8, 8, 8'> + <space/> + <widget name = 'ResetSettings' + type = 'Button' + /> + <widget name = 'Cancel' + type = 'Button' + /> + <widget name = 'Ok' + type = 'Button' + /> + </layout> + </layout> + </dialog> + + <dialog name = 'FluidSynthSettings_Chorus' overlays = 'Dialog.FluidSynthSettings.TabWidget'> + <layout type = 'vertical' padding = '8, 8, 8, 8' spacing = '6'> + <widget name = 'EnableTabCheckbox' + type = 'Checkbox' + /> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'VoiceCountText' + type = 'OptionsLabel' + /> + <widget name = 'VoiceCountSlider' + type = 'Slider' + /> + <widget name = 'VoiceCountLabel' + width = '32' + height = 'Globals.Line.Height' + /> + </layout> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'LevelText' + type = 'OptionsLabel' + /> + <widget name = 'LevelSlider' + type = 'Slider' + /> + <widget name = 'LevelLabel' + width = '32' + height = 'Globals.Line.Height' + /> + </layout> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'SpeedText' + type = 'OptionsLabel' + /> + <widget name = 'SpeedSlider' + type = 'Slider' + /> + <widget name = 'SpeedLabel' + width = '32' + height = 'Globals.Line.Height' + /> + </layout> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'DepthText' + type = 'OptionsLabel' + /> + <widget name = 'DepthSlider' + type = 'Slider' + /> + <widget name = 'DepthLabel' + width = '32' + height = 'Globals.Line.Height' + /> + </layout> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'WaveFormTypeText' + type = 'OptionsLabel' + /> + <widget name = 'WaveFormType' + type = 'PopUp' + /> + </layout> + </layout> + </dialog> + + <dialog name = 'FluidSynthSettings_Reverb' overlays = 'Dialog.FluidSynthSettings.TabWidget'> + <layout type = 'vertical' padding = '8, 8, 8, 8' spacing = '6'> + <widget name = 'EnableTabCheckbox' + type = 'Checkbox' + /> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'RoomSizeText' + type = 'OptionsLabel' + /> + <widget name = 'RoomSizeSlider' + type = 'Slider' + /> + <widget name = 'RoomSizeLabel' + width = '32' + height = 'Globals.Line.Height' + /> + </layout> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'DampingText' + type = 'OptionsLabel' + /> + <widget name = 'DampingSlider' + type = 'Slider' + /> + <widget name = 'DampingLabel' + width = '32' + height = 'Globals.Line.Height' + /> + </layout> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'WidthText' + type = 'OptionsLabel' + /> + <widget name = 'WidthSlider' + type = 'Slider' + /> + <widget name = 'WidthLabel' + width = '32' + height = 'Globals.Line.Height' + /> + </layout> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'LevelText' + type = 'OptionsLabel' + /> + <widget name = 'LevelSlider' + type = 'Slider' + /> + <widget name = 'LevelLabel' + width = '32' + height = 'Globals.Line.Height' + /> + </layout> + </layout> + </dialog> + + <dialog name = 'FluidSynthSettings_Misc' overlays = 'Dialog.FluidSynthSettings.TabWidget'> + <layout type = 'vertical' padding = '8, 8, 8, 8' spacing = '6'> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'InterpolationText' + type = 'OptionsLabel' + /> + <widget name = 'Interpolation' + type = 'PopUp' + /> + </layout> + </layout> + </dialog> + <dialog name = 'SaveLoadChooser' overlays = 'screen' inset = '8' shading = 'dim'> <layout type = 'vertical' padding = '8, 8, 8, 8' center = 'true'> <widget name = 'Title' height = 'Globals.Line.Height'/> <widget name = 'List' /> <layout type = 'horizontal' padding = '0, 0, 16, 0'> + <widget name = 'ListSwitch' + height = 'Globals.Line.Height' + width = 'Globals.Line.Height' + /> + <widget name = 'GridSwitch' + height = 'Globals.Line.Height' + width = 'Globals.Line.Height' + /> <space/> <widget name = 'Delete' type = 'Button' @@ -822,6 +988,26 @@ </layout> </dialog> + <dialog name = 'SavenameDialog' overlays = 'screen_center'> + <layout type = 'vertical' padding = '8, 8, 8, 8'> + <widget name = 'DescriptionText' + width = '180' + height = 'Globals.Line.Height' + /> + <widget name = 'Description' + height = '19' + /> + <layout type = 'horizontal' padding = '0, 0, 16, 0'> + <widget name = 'Cancel' + type = 'Button' + /> + <widget name = 'Ok' + type = 'Button' + /> + </layout> + </layout> + </dialog> + <dialog name = 'ScummHelp' overlays = 'screen'> <layout type = 'vertical' padding = '8, 8, 8, 8'> <widget name = 'Title' diff --git a/gui/themes/scummmodern.zip b/gui/themes/scummmodern.zip Binary files differindex db116325e2..412ed6a96f 100644 --- a/gui/themes/scummmodern.zip +++ b/gui/themes/scummmodern.zip diff --git a/gui/themes/scummmodern/THEMERC b/gui/themes/scummmodern/THEMERC index 60744d386f..9e8776263b 100644 --- a/gui/themes/scummmodern/THEMERC +++ b/gui/themes/scummmodern/THEMERC @@ -1 +1 @@ -[SCUMMVM_STX0.8.13:ScummVM Modern Theme:No Author] +[SCUMMVM_STX0.8.19:ScummVM Modern Theme:No Author] diff --git a/gui/themes/scummmodern/grid.bmp b/gui/themes/scummmodern/grid.bmp Binary files differnew file mode 100644 index 0000000000..adeb209380 --- /dev/null +++ b/gui/themes/scummmodern/grid.bmp diff --git a/gui/themes/scummmodern/list.bmp b/gui/themes/scummmodern/list.bmp Binary files differnew file mode 100644 index 0000000000..2f54a40bcd --- /dev/null +++ b/gui/themes/scummmodern/list.bmp diff --git a/gui/themes/scummmodern/scummmodern_gfx.stx b/gui/themes/scummmodern/scummmodern_gfx.stx index 037c327235..4d449f50ec 100644 --- a/gui/themes/scummmodern/scummmodern_gfx.stx +++ b/gui/themes/scummmodern/scummmodern_gfx.stx @@ -101,6 +101,8 @@ <bitmap filename = 'search.bmp'/> <bitmap filename = 'eraser.bmp'/> <bitmap filename = 'delbtn.bmp'/> + <bitmap filename = 'list.bmp'/> + <bitmap filename = 'grid.bmp'/> </bitmaps> <fonts> @@ -153,7 +155,7 @@ /> <text_color id = 'color_normal_disabled' - color = '192, 192, 192' + color = '96, 96, 96' /> <text_color id = 'color_alternative' diff --git a/gui/themes/scummmodern/scummmodern_layout.stx b/gui/themes/scummmodern/scummmodern_layout.stx index 087a844a1b..fa57e62c54 100644 --- a/gui/themes/scummmodern/scummmodern_layout.stx +++ b/gui/themes/scummmodern/scummmodern_layout.stx @@ -39,6 +39,9 @@ <def var = 'ShowGlobalMenuLogo' value = '1'/> <def var = 'ShowSearchPic' value = '1'/> + <def var = 'ShowChooserPics' value = '1'/> + <def var = 'ShowChooserPageDisplay' value = '1'/> + <def var = 'SaveLoadChooser.ExtInfo.Visible' value = '1'/> <def var = 'KeyMapper.Spacing' value = '10'/> @@ -370,6 +373,10 @@ height = 'Globals.Line.Height' /> </layout> + <widget name = 'mcFluidSynthSettings' + width = '200' + height = 'Globals.Button.Height' + /> </layout> </dialog> @@ -805,11 +812,169 @@ </layout> </dialog> - <dialog name = 'SaveLoadChooser' overlays = 'screen' inset = '8' shading = 'dim'> - <layout type = 'vertical' padding = '8, 8, 8, 32' center = 'true'> - <widget name = 'Title' + <dialog name = 'FluidSynthSettings' overlays = 'GlobalOptions' shading = 'dim'> + <layout type = 'vertical' padding = '0, 0, 0, 0'> + <widget name = 'TabWidget'/> + <layout type = 'horizontal' padding = '16, 16, 16, 16'> + <space/> + <widget name = 'ResetSettings' + type = 'Button' + /> + <widget name = 'Cancel' + type = 'Button' + /> + <widget name = 'Ok' + type = 'Button' + /> + </layout> + </layout> + </dialog> + + <dialog name = 'FluidSynthSettings_Chorus' overlays = 'Dialog.FluidSynthSettings.TabWidget'> + <layout type = 'vertical' padding = '16, 16, 16, 16' spacing = '8'> + <widget name = 'EnableTabCheckbox' + type = 'Checkbox' + /> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'VoiceCountText' + type = 'OptionsLabel' + /> + <widget name = 'VoiceCountSlider' + type = 'Slider' + /> + <widget name = 'VoiceCountLabel' + width = '32' + height = 'Globals.Line.Height' + /> + </layout> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'LevelText' + type = 'OptionsLabel' + /> + <widget name = 'LevelSlider' + type = 'Slider' + /> + <widget name = 'LevelLabel' + width = '32' + height = 'Globals.Line.Height' + /> + </layout> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'SpeedText' + type = 'OptionsLabel' + /> + <widget name = 'SpeedSlider' + type = 'Slider' + /> + <widget name = 'SpeedLabel' + width = '32' + height = 'Globals.Line.Height' + /> + </layout> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'DepthText' + type = 'OptionsLabel' + /> + <widget name = 'DepthSlider' + type = 'Slider' + /> + <widget name = 'DepthLabel' + width = '32' height = 'Globals.Line.Height' + /> + </layout> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'WaveFormTypeText' + type = 'OptionsLabel' + /> + <widget name = 'WaveFormType' + type = 'PopUp' + /> + </layout> + </layout> + </dialog> + + <dialog name = 'FluidSynthSettings_Reverb' overlays = 'Dialog.FluidSynthSettings.TabWidget'> + <layout type = 'vertical' padding = '16, 16, 16, 16' spacing = '8'> + <widget name = 'EnableTabCheckbox' + type = 'Checkbox' /> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'RoomSizeText' + type = 'OptionsLabel' + /> + <widget name = 'RoomSizeSlider' + type = 'Slider' + /> + <widget name = 'RoomSizeLabel' + width = '32' + height = 'Globals.Line.Height' + /> + </layout> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'DampingText' + type = 'OptionsLabel' + /> + <widget name = 'DampingSlider' + type = 'Slider' + /> + <widget name = 'DampingLabel' + width = '32' + height = 'Globals.Line.Height' + /> + </layout> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'WidthText' + type = 'OptionsLabel' + /> + <widget name = 'WidthSlider' + type = 'Slider' + /> + <widget name = 'WidthLabel' + width = '32' + height = 'Globals.Line.Height' + /> + </layout> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'LevelText' + type = 'OptionsLabel' + /> + <widget name = 'LevelSlider' + type = 'Slider' + /> + <widget name = 'LevelLabel' + width = '32' + height = 'Globals.Line.Height' + /> + </layout> + </layout> + </dialog> + + <dialog name = 'FluidSynthSettings_Misc' overlays = 'Dialog.FluidSynthSettings.TabWidget'> + <layout type = 'vertical' padding = '16, 16, 16, 16' spacing = '8'> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'InterpolationText' + type = 'OptionsLabel' + /> + <widget name = 'Interpolation' + type = 'PopUp' + /> + </layout> + </layout> + </dialog> + + <dialog name = 'SaveLoadChooser' overlays = 'screen' inset = '8' shading = 'dim'> + <layout type = 'vertical' padding = '8, 8, 8, 32' center = 'true'> + <layout type = 'horizontal' padding = '0, 0, 0, 0'> + <widget name = 'Title' + height = 'Globals.Line.Height' + /> + <space/> + <widget name = 'PageDisplay' + width = '200' + height = 'Globals.Line.Height' + /> + </layout> <layout type = 'horizontal' padding = '0, 0, 0, 16' spacing = '16'> <widget name = 'List' /> <widget name = 'Thumbnail' @@ -818,6 +983,14 @@ /> </layout> <layout type = 'horizontal' padding = '0, 0, 0, 0'> + <widget name = 'ListSwitch' + height = '20' + width = '20' + /> + <widget name = 'GridSwitch' + height = '20' + width = '20' + /> <space/> <widget name = 'Delete' type = 'Button' @@ -833,6 +1006,27 @@ </layout> </dialog> + <dialog name = 'SavenameDialog' overlays = 'screen_center'> + <layout type = 'vertical' padding = '8, 8, 8, 8'> + <widget name = 'DescriptionText' + width = '320' + height = 'Globals.Line.Height' + /> + <widget name = 'Description' + height = '19' + /> + <layout type = 'horizontal' padding = '0, 0, 16, 0'> + <widget name = 'Cancel' + type = 'Button' + /> + <space size = '96'/> + <widget name = 'Ok' + type = 'Button' + /> + </layout> + </layout> + </dialog> + <dialog name = 'ScummHelp' overlays = 'screen_center'> <layout type = 'vertical' padding = '8, 8, 8, 8' center = 'true'> <widget name = 'Title' diff --git a/gui/themes/scummmodern/scummmodern_layout_lowres.stx b/gui/themes/scummmodern/scummmodern_layout_lowres.stx index 987fee800a..5c3cc8357e 100644 --- a/gui/themes/scummmodern/scummmodern_layout_lowres.stx +++ b/gui/themes/scummmodern/scummmodern_layout_lowres.stx @@ -31,6 +31,9 @@ <def var = 'ShowGlobalMenuLogo' value = '0'/> <def var = 'ShowSearchPic' value = '0'/> + <def var = 'ShowChooserPics' value = '0'/> + <def var = 'ShowChooserPageDisplay' value = '0'/> + <def var = 'SaveLoadChooser.ExtInfo.Visible' value = '0'/> <def var = 'Predictive.Button.Width' value = '45' /> @@ -316,7 +319,7 @@ </dialog> <dialog name = 'GlobalOptions_MIDI' overlays = 'Dialog.GlobalOptions.TabWidget'> - <layout type = 'vertical' padding = '16, 16, 16, 16' spacing = '8'> + <layout type = 'vertical' padding = '16, 16, 16, 16' spacing = '7'> <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '6' center = 'true'> <widget name = 'auPrefGmPopupDesc' type = 'OptionsLabel' @@ -352,6 +355,10 @@ height = 'Globals.Line.Height' /> </layout> + <widget name = 'mcFluidSynthSettings' + width = '150' + height = 'Globals.Button.Height' + /> </layout> </dialog> @@ -801,11 +808,170 @@ </layout> </dialog> + <dialog name = 'FluidSynthSettings' overlays = 'GlobalOptions' shading = 'dim'> + <layout type = 'vertical' padding = '0, 0, 0, 0'> + <widget name = 'TabWidget'/> + <layout type = 'horizontal' padding = '8, 8, 8, 8'> + <space/> + <widget name = 'ResetSettings' + type = 'Button' + /> + <widget name = 'Cancel' + type = 'Button' + /> + <widget name = 'Ok' + type = 'Button' + /> + </layout> + </layout> + </dialog> + + <dialog name = 'FluidSynthSettings_Chorus' overlays = 'Dialog.FluidSynthSettings.TabWidget'> + <layout type = 'vertical' padding = '8, 8, 8, 8' spacing = '6'> + <widget name = 'EnableTabCheckbox' + type = 'Checkbox' + /> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'VoiceCountText' + type = 'OptionsLabel' + /> + <widget name = 'VoiceCountSlider' + type = 'Slider' + /> + <widget name = 'VoiceCountLabel' + width = '32' + height = 'Globals.Line.Height' + /> + </layout> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'LevelText' + type = 'OptionsLabel' + /> + <widget name = 'LevelSlider' + type = 'Slider' + /> + <widget name = 'LevelLabel' + width = '32' + height = 'Globals.Line.Height' + /> + </layout> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'SpeedText' + type = 'OptionsLabel' + /> + <widget name = 'SpeedSlider' + type = 'Slider' + /> + <widget name = 'SpeedLabel' + width = '32' + height = 'Globals.Line.Height' + /> + </layout> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'DepthText' + type = 'OptionsLabel' + /> + <widget name = 'DepthSlider' + type = 'Slider' + /> + <widget name = 'DepthLabel' + width = '32' + height = 'Globals.Line.Height' + /> + </layout> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'WaveFormTypeText' + type = 'OptionsLabel' + /> + <widget name = 'WaveFormType' + type = 'PopUp' + /> + </layout> + </layout> + </dialog> + + <dialog name = 'FluidSynthSettings_Reverb' overlays = 'Dialog.FluidSynthSettings.TabWidget'> + <layout type = 'vertical' padding = '8, 8, 8, 8' spacing = '6'> + <widget name = 'EnableTabCheckbox' + type = 'Checkbox' + /> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'RoomSizeText' + type = 'OptionsLabel' + /> + <widget name = 'RoomSizeSlider' + type = 'Slider' + /> + <widget name = 'RoomSizeLabel' + width = '32' + height = 'Globals.Line.Height' + /> + </layout> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'DampingText' + type = 'OptionsLabel' + /> + <widget name = 'DampingSlider' + type = 'Slider' + /> + <widget name = 'DampingLabel' + width = '32' + height = 'Globals.Line.Height' + /> + </layout> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'WidthText' + type = 'OptionsLabel' + /> + <widget name = 'WidthSlider' + type = 'Slider' + /> + <widget name = 'WidthLabel' + width = '32' + height = 'Globals.Line.Height' + /> + </layout> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'LevelText' + type = 'OptionsLabel' + /> + <widget name = 'LevelSlider' + type = 'Slider' + /> + <widget name = 'LevelLabel' + width = '32' + height = 'Globals.Line.Height' + /> + </layout> + </layout> + </dialog> + + <dialog name = 'FluidSynthSettings_Misc' overlays = 'Dialog.FluidSynthSettings.TabWidget'> + <layout type = 'vertical' padding = '8, 8, 8, 8' spacing = '6'> + <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'> + <widget name = 'InterpolationText' + type = 'OptionsLabel' + /> + <widget name = 'Interpolation' + type = 'PopUp' + /> + </layout> + </layout> + </dialog> + <dialog name = 'SaveLoadChooser' overlays = 'screen' inset = '8' shading = 'dim'> <layout type = 'vertical' padding = '8, 8, 8, 8' center = 'true'> <widget name = 'Title' height = 'Globals.Line.Height'/> <widget name = 'List' /> <layout type = 'horizontal' padding = '0, 0, 16, 0'> + <widget name = 'ListSwitch' + height = 'Globals.Line.Height' + width = 'Globals.Line.Height' + /> + <widget name = 'GridSwitch' + height = 'Globals.Line.Height' + width = 'Globals.Line.Height' + /> <space/> <widget name = 'Delete' type = 'Button' @@ -821,6 +987,26 @@ </layout> </dialog> + <dialog name = 'SavenameDialog' overlays = 'screen_center'> + <layout type = 'vertical' padding = '8, 8, 8, 8'> + <widget name = 'DescriptionText' + width = '180' + height = 'Globals.Line.Height' + /> + <widget name = 'Description' + height = '19' + /> + <layout type = 'horizontal' padding = '0, 0, 16, 0'> + <widget name = 'Cancel' + type = 'Button' + /> + <widget name = 'Ok' + type = 'Button' + /> + </layout> + </layout> + </dialog> + <dialog name = 'ScummHelp' overlays = 'screen' inset = '8'> <layout type = 'vertical' padding = '8, 8, 8, 8'> <widget name = 'Title' diff --git a/gui/themes/translations.dat b/gui/themes/translations.dat Binary files differindex 0e32008bd9..9b9b7bbea6 100644 --- a/gui/themes/translations.dat +++ b/gui/themes/translations.dat diff --git a/gui/widget.cpp b/gui/widget.cpp index 1b68e36ea8..c3f10a861f 100644 --- a/gui/widget.cpp +++ b/gui/widget.cpp @@ -296,8 +296,8 @@ ButtonWidget::ButtonWidget(GuiObject *boss, const Common::String &name, const Co void ButtonWidget::handleMouseUp(int x, int y, int button, int clickCount) { if (isEnabled() && x >= 0 && x < _w && y >= 0 && y < _h) { - sendCommand(_cmd, 0); startAnimatePressedState(); + sendCommand(_cmd, 0); } } @@ -366,7 +366,7 @@ void ButtonWidget::startAnimatePressedState() { } void ButtonWidget::wantTickle(bool tickled) { - if (tickled) + if (tickled) ((GUI::Dialog *)_boss)->setTickleWidget(this); else ((GUI::Dialog *)_boss)->unSetTickleWidget(); @@ -376,7 +376,7 @@ void ButtonWidget::wantTickle(bool tickled) { PicButtonWidget::PicButtonWidget(GuiObject *boss, int x, int y, int w, int h, const char *tooltip, uint32 cmd, uint8 hotkey) : ButtonWidget(boss, x, y, w, h, "", tooltip, cmd, hotkey), - _gfx(new Graphics::Surface()), _alpha(256), _transparency(false) { + _gfx(), _alpha(256), _transparency(false) { setFlags(WIDGET_ENABLED/* | WIDGET_BORDER*/ | WIDGET_CLEARBG); _type = kButtonWidget; @@ -384,18 +384,17 @@ PicButtonWidget::PicButtonWidget(GuiObject *boss, int x, int y, int w, int h, co PicButtonWidget::PicButtonWidget(GuiObject *boss, const Common::String &name, const char *tooltip, uint32 cmd, uint8 hotkey) : ButtonWidget(boss, name, "", tooltip, cmd, hotkey), - _gfx(new Graphics::Surface()), _alpha(256), _transparency(false) { + _gfx(), _alpha(256), _transparency(false) { setFlags(WIDGET_ENABLED/* | WIDGET_BORDER*/ | WIDGET_CLEARBG); _type = kButtonWidget; } PicButtonWidget::~PicButtonWidget() { - _gfx->free(); - delete _gfx; + _gfx.free(); } void PicButtonWidget::setGfx(const Graphics::Surface *gfx) { - _gfx->free(); + _gfx.free(); if (!gfx || !gfx->pixels) return; @@ -411,27 +410,37 @@ void PicButtonWidget::setGfx(const Graphics::Surface *gfx) { return; } - _gfx->copyFrom(*gfx); + _gfx.copyFrom(*gfx); +} + +void PicButtonWidget::setGfx(int w, int h, int r, int g, int b) { + if (w == -1) + w = _w; + if (h == -1) + h = _h; + + const Graphics::PixelFormat &requiredFormat = g_gui.theme()->getPixelFormat(); + + _gfx.free(); + _gfx.create(w, h, requiredFormat); + _gfx.fillRect(Common::Rect(0, 0, w, h), _gfx.format.RGBToColor(r, g, b)); } void PicButtonWidget::drawWidget() { g_gui.theme()->drawButton(Common::Rect(_x, _y, _x+_w, _y+_h), "", _state, getFlags()); - if (_gfx->pixels) { + if (_gfx.pixels) { // Check whether the set up surface needs to be converted to the GUI // color format. const Graphics::PixelFormat &requiredFormat = g_gui.theme()->getPixelFormat(); - if (_gfx->format != requiredFormat) { - Graphics::Surface *converted = _gfx->convertTo(requiredFormat); - _gfx->free(); - delete _gfx; - _gfx = converted; + if (_gfx.format != requiredFormat) { + _gfx.convertToInPlace(requiredFormat); } - const int x = _x + (_w - _gfx->w) / 2; - const int y = _y + (_h - _gfx->h) / 2; + const int x = _x + (_w - _gfx.w) / 2; + const int y = _y + (_h - _gfx.h) / 2; - g_gui.theme()->drawSurface(Common::Rect(x, y, x + _gfx->w, y + _gfx->h), *_gfx, _state, _alpha, _transparency); + g_gui.theme()->drawSurface(Common::Rect(x, y, x + _gfx.w, y + _gfx.h), _gfx, _state, _alpha, _transparency); } } @@ -584,8 +593,8 @@ void SliderWidget::handleMouseUp(int x, int y, int button, int clickCount) { void SliderWidget::handleMouseWheel(int x, int y, int direction) { if (isEnabled() && !_isDragging) { - // Increment or decrement one position - int newValue = posToValue(valueToPos(_value) - 1 * direction); + // Increment or decrement by one + int newValue = _value - direction; if (newValue < _valueMin) newValue = _valueMin; @@ -619,24 +628,23 @@ int SliderWidget::posToValue(int pos) { #pragma mark - GraphicsWidget::GraphicsWidget(GuiObject *boss, int x, int y, int w, int h, const char *tooltip) - : Widget(boss, x, y, w, h, tooltip), _gfx(new Graphics::Surface()), _alpha(256), _transparency(false) { + : Widget(boss, x, y, w, h, tooltip), _gfx(), _alpha(256), _transparency(false) { setFlags(WIDGET_ENABLED | WIDGET_CLEARBG); _type = kGraphicsWidget; } GraphicsWidget::GraphicsWidget(GuiObject *boss, const Common::String &name, const char *tooltip) - : Widget(boss, name, tooltip), _gfx(new Graphics::Surface()), _alpha(256), _transparency(false) { + : Widget(boss, name, tooltip), _gfx(), _alpha(256), _transparency(false) { setFlags(WIDGET_ENABLED | WIDGET_CLEARBG); _type = kGraphicsWidget; } GraphicsWidget::~GraphicsWidget() { - _gfx->free(); - delete _gfx; + _gfx.free(); } void GraphicsWidget::setGfx(const Graphics::Surface *gfx) { - _gfx->free(); + _gfx.free(); if (!gfx || !gfx->pixels) return; @@ -651,7 +659,7 @@ void GraphicsWidget::setGfx(const Graphics::Surface *gfx) { return; } - _gfx->copyFrom(*gfx); + _gfx.copyFrom(*gfx); } void GraphicsWidget::setGfx(int w, int h, int r, int g, int b) { @@ -662,27 +670,24 @@ void GraphicsWidget::setGfx(int w, int h, int r, int g, int b) { const Graphics::PixelFormat &requiredFormat = g_gui.theme()->getPixelFormat(); - _gfx->free(); - _gfx->create(w, h, requiredFormat); - _gfx->fillRect(Common::Rect(0, 0, w, h), _gfx->format.RGBToColor(r, g, b)); + _gfx.free(); + _gfx.create(w, h, requiredFormat); + _gfx.fillRect(Common::Rect(0, 0, w, h), _gfx.format.RGBToColor(r, g, b)); } void GraphicsWidget::drawWidget() { - if (_gfx->pixels) { + if (_gfx.pixels) { // Check whether the set up surface needs to be converted to the GUI // color format. const Graphics::PixelFormat &requiredFormat = g_gui.theme()->getPixelFormat(); - if (_gfx->format != requiredFormat) { - Graphics::Surface *converted = _gfx->convertTo(requiredFormat); - _gfx->free(); - delete _gfx; - _gfx = converted; + if (_gfx.format != requiredFormat) { + _gfx.convertToInPlace(requiredFormat); } - const int x = _x + (_w - _gfx->w) / 2; - const int y = _y + (_h - _gfx->h) / 2; + const int x = _x + (_w - _gfx.w) / 2; + const int y = _y + (_h - _gfx.h) / 2; - g_gui.theme()->drawSurface(Common::Rect(x, y, x + _gfx->w, y + _gfx->h), *_gfx, _state, _alpha, _transparency); + g_gui.theme()->drawSurface(Common::Rect(x, y, x + _gfx.w, y + _gfx.h), _gfx, _state, _alpha, _transparency); } } @@ -698,6 +703,26 @@ ContainerWidget::ContainerWidget(GuiObject *boss, const Common::String &name) : _type = kContainerWidget; } +ContainerWidget::~ContainerWidget() { + // We also remove the widget from the boss to avoid segfaults, when the + // deleted widget is an active widget in the boss. + for (Widget *w = _firstWidget; w; w = w->next()) { + _boss->removeWidget(w); + } +} + +Widget *ContainerWidget::findWidget(int x, int y) { + return findWidgetInChain(_firstWidget, x, y); +} + +void ContainerWidget::removeWidget(Widget *widget) { + // We also remove the widget from the boss to avoid a reference to a + // widget not in the widget chain anymore. + _boss->removeWidget(widget); + + Widget::removeWidget(widget); +} + void ContainerWidget::drawWidget() { g_gui.theme()->drawWidgetBackground(Common::Rect(_x, _y, _x + _w, _y + _h), 0, ThemeEngine::kWidgetBackgroundBorder); } diff --git a/gui/widget.h b/gui/widget.h index 6de56862c3..e3f712564f 100644 --- a/gui/widget.h +++ b/gui/widget.h @@ -88,7 +88,7 @@ protected: uint16 _id; bool _hasFocus; ThemeEngine::WidgetStateInfo _state; - const char *_tooltip; + Common::String _tooltip; private: uint16 _flags; @@ -142,7 +142,9 @@ public: uint8 parseHotkey(const Common::String &label); Common::String cleanupHotkey(const Common::String &label); - const char *getTooltip() const { return _tooltip; } + bool hasTooltip() const { return !_tooltip.empty(); } + const Common::String &getTooltip() const { return _tooltip; } + void setTooltip(const Common::String &tooltip) { _tooltip = tooltip; } protected: void updateState(int oldFlags, int newFlags); @@ -220,6 +222,7 @@ public: ~PicButtonWidget(); void setGfx(const Graphics::Surface *gfx); + void setGfx(int w, int h, int r, int g, int b); void useAlpha(int alpha) { _alpha = alpha; } void useThemeTransparency(bool enable) { _transparency = enable; } @@ -227,7 +230,7 @@ public: protected: void drawWidget(); - Graphics::Surface *_gfx; + Graphics::Surface _gfx; int _alpha; bool _transparency; }; @@ -355,7 +358,7 @@ public: protected: void drawWidget(); - Graphics::Surface *_gfx; + Graphics::Surface _gfx; int _alpha; bool _transparency; }; @@ -365,7 +368,10 @@ class ContainerWidget : public Widget { public: ContainerWidget(GuiObject *boss, int x, int y, int w, int h); ContainerWidget(GuiObject *boss, const Common::String &name); + ~ContainerWidget(); + virtual Widget *findWidget(int x, int y); + virtual void removeWidget(Widget *widget); protected: void drawWidget(); }; diff --git a/gui/widgets/editable.h b/gui/widgets/editable.h index 7e453c1204..4a18d5e689 100644 --- a/gui/widgets/editable.h +++ b/gui/widgets/editable.h @@ -77,10 +77,10 @@ public: protected: virtual void startEditMode() = 0; virtual void endEditMode() = 0; - virtual void abortEditMode() = 0; + virtual void abortEditMode() = 0; virtual Common::Rect getEditRect() const = 0; virtual int getCaretOffset() const; - void drawCaret(bool erase); + void drawCaret(bool erase); bool adjustOffset(); void makeCaretVisible(); diff --git a/gui/widgets/list.cpp b/gui/widgets/list.cpp index 13784ddf7f..95d39c4f24 100644 --- a/gui/widgets/list.cpp +++ b/gui/widgets/list.cpp @@ -284,7 +284,7 @@ bool ListWidget::handleKeyDown(Common::KeyState state) { bool dirty = false; int oldSelectedItem = _selectedItem; - if (!_editMode && state.keycode <= Common::KEYCODE_z && isprint((unsigned char)state.ascii)) { + if (!_editMode && state.keycode <= Common::KEYCODE_z && Common::isPrint(state.ascii)) { // Quick selection mode: Go to first list item starting with this key // (or a substring accumulated from the last couple key presses). // Only works in a useful fashion if the list entries are sorted. diff --git a/gui/widgets/list.h b/gui/widgets/list.h index 41fae37a71..47613b79f3 100644 --- a/gui/widgets/list.h +++ b/gui/widgets/list.h @@ -105,6 +105,7 @@ public: void scrollTo(int item); void scrollToEnd(); + int getCurrentScrollPos() const { return _currentPos; } void enableQuickSelect(bool enable) { _quickSelect = enable; } String getQuickSelectString() const { return _quickSelectStr; } diff --git a/gui/widgets/popup.cpp b/gui/widgets/popup.cpp index 1a552e97c0..829a49c53e 100644 --- a/gui/widgets/popup.cpp +++ b/gui/widgets/popup.cpp @@ -388,24 +388,28 @@ void PopUpWidget::handleMouseDown(int x, int y, int button, int clickCount) { if (newSel != -1 && _selectedItem != newSel) { _selectedItem = newSel; sendCommand(kPopUpItemSelectedCmd, _entries[_selectedItem].tag); + draw(); } } } void PopUpWidget::handleMouseWheel(int x, int y, int direction) { - int newSelection = _selectedItem + direction; + if (isEnabled()) { + int newSelection = _selectedItem + direction; - // Skip separator entries - while ((newSelection >= 0) && (newSelection < (int)_entries.size()) && - _entries[newSelection].name.equals("")) { - newSelection += direction; - } + // Skip separator entries + while ((newSelection >= 0) && (newSelection < (int)_entries.size()) && + _entries[newSelection].name.equals("")) { + newSelection += direction; + } - // Just update the selected item when we're in range - if ((newSelection >= 0) && (newSelection < (int)_entries.size()) && - (newSelection != _selectedItem)) { - _selectedItem = newSelection; - draw(); + // Just update the selected item when we're in range + if ((newSelection >= 0) && (newSelection < (int)_entries.size()) && + (newSelection != _selectedItem)) { + _selectedItem = newSelection; + sendCommand(kPopUpItemSelectedCmd, _entries[_selectedItem].tag); + draw(); + } } } diff --git a/po/POTFILES b/po/POTFILES index 8b74115939..3d9b993d47 100644 --- a/po/POTFILES +++ b/po/POTFILES @@ -10,7 +10,7 @@ gui/KeysDialog.cpp gui/launcher.cpp gui/massadd.cpp gui/options.cpp -gui/saveload.cpp +gui/saveload-dialog.cpp gui/themebrowser.cpp gui/ThemeEngine.cpp gui/widget.cpp @@ -25,6 +25,8 @@ engines/dialogs.cpp engines/engine.cpp engines/agi/detection.cpp engines/agi/saveload.cpp +engines/drascula/detection.cpp +engines/drascula/saveload.cpp engines/dreamweb/detection.cpp engines/sci/detection.cpp engines/scumm/dialogs.cpp @@ -40,6 +42,7 @@ engines/agos/animation.cpp engines/gob/inter_playtoons.cpp engines/gob/inter_v2.cpp engines/gob/inter_v5.cpp +engines/groovie/detection.cpp engines/groovie/script.cpp engines/kyra/detection.cpp engines/kyra/lol.cpp @@ -53,7 +56,10 @@ engines/sword1/logic.cpp engines/sword1/sword1.cpp engines/sword2/animation.cpp engines/sword2/sword2.cpp +engines/teenagent/resources.cpp engines/tinsel/saveload.cpp +engines/toltecs/detection.cpp +engines/toltecs/menu.cpp engines/parallaction/saveload.cpp audio/fmopl.cpp diff --git a/po/be_BY.po b/po/be_BY.po new file mode 100644 index 0000000000..3d64a3f55f --- /dev/null +++ b/po/be_BY.po @@ -0,0 +1,3140 @@ +# Belarusian translation for ScummVM. +# Copyright (C) 2010-2013 ScummVM Team +# This file is distributed under the same license as the ScummVM package. +# Ivan Lukyanov <greencis@mail.ru>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: ScummVM 1.6.0git\n" +"Report-Msgid-Bugs-To: scummvm-devel@lists.sf.net\n" +"POT-Creation-Date: 2012-08-12 14:57+0200\n" +"PO-Revision-Date: 2012-12-12 22:02+0300\n" +"Last-Translator: Ivan Lukyanov <greencis@mail.ru>\n" +"Language-Team: Ivan Lukyanov <greencis@mail.ru>\n" +"Language: Belarusian\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=iso-8859-5\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n" +"%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" +"X-Generator: Poedit 1.5.4\n" + +#: gui/about.cpp:91 +#, c-format +msgid "(built on %s)" +msgstr "(áÐÑàÐÝë %s)" + +#: gui/about.cpp:98 +msgid "Features compiled in:" +msgstr "ÃÚÛîçÐÝëï þ ÑöÛÔ Þßæëö:" + +#: gui/about.cpp:107 +msgid "Available engines:" +msgstr "´ÐáâãßÝëï àãåÐÒöçÚö:" + +#: gui/browser.cpp:66 +msgid "Go up" +msgstr "ÃÒÕàå" + +#: gui/browser.cpp:66 gui/browser.cpp:68 +msgid "Go to previous directory level" +msgstr "¿ÕàÐÙáæö ÝÐ ÔëàíÚâÞàëî þ×àÞþÝÕÜ ÒëèíÙ" + +#: gui/browser.cpp:68 +msgctxt "lowres" +msgid "Go up" +msgstr "ÃÒÕàå" + +#: gui/browser.cpp:69 gui/chooser.cpp:45 gui/KeysDialog.cpp:43 +#: gui/launcher.cpp:345 gui/massadd.cpp:94 gui/options.cpp:1228 +#: gui/saveload-dialog.cpp:207 gui/saveload-dialog.cpp:267 +#: gui/saveload-dialog.cpp:516 gui/saveload-dialog.cpp:843 +#: gui/themebrowser.cpp:54 engines/engine.cpp:442 +#: engines/scumm/dialogs.cpp:190 engines/sword1/control.cpp:865 +#: engines/parallaction/saveload.cpp:274 backends/platform/wii/options.cpp:48 +#: backends/events/default/default-events.cpp:191 +#: backends/events/default/default-events.cpp:213 +msgid "Cancel" +msgstr "°ÔÜÕÝÐ" + +#: gui/browser.cpp:70 gui/chooser.cpp:46 gui/themebrowser.cpp:55 +msgid "Choose" +msgstr "°ÑàÐæì" + +#: gui/gui-manager.cpp:115 engines/scumm/help.cpp:125 +#: engines/scumm/help.cpp:140 engines/scumm/help.cpp:165 +#: engines/scumm/help.cpp:191 engines/scumm/help.cpp:209 +#: backends/keymapper/remap-dialog.cpp:52 +msgid "Close" +msgstr "·ÐÚàëæì" + +#: gui/gui-manager.cpp:118 +msgid "Mouse click" +msgstr "ºÛöÚ Üëèèã" + +#: gui/gui-manager.cpp:122 base/main.cpp:300 +msgid "Display keyboard" +msgstr "¿ÐÚÐ×Ðæì ÚÛÐÒöïâãàã" + +#: gui/gui-manager.cpp:126 base/main.cpp:304 +msgid "Remap keys" +msgstr "¿ÕàÐßàë×ÝÐçëæì ÚÛÐÒöèë" + +#: gui/gui-manager.cpp:129 base/main.cpp:307 +msgid "Toggle FullScreen" +msgstr "¿ÕàÐÚÛîçíÝÝÕ ÝÐ þÒÕáì íÚàÐÝ" + +#: gui/KeysDialog.h:36 gui/KeysDialog.cpp:145 +msgid "Choose an action to map" +msgstr "°ÑïàëæÕ Ô×ÕïÝÝÕ ÔÛï ßàë×ÝÐçíÝÝï" + +#: gui/KeysDialog.cpp:41 +msgid "Map" +msgstr "¿àë×ÝÐçëæì" + +#: gui/KeysDialog.cpp:42 gui/launcher.cpp:346 gui/launcher.cpp:1001 +#: gui/launcher.cpp:1005 gui/massadd.cpp:91 gui/options.cpp:1229 +#: gui/saveload-dialog.cpp:844 engines/engine.cpp:361 engines/engine.cpp:372 +#: engines/scumm/dialogs.cpp:192 engines/scumm/scumm.cpp:1775 +#: engines/agos/animation.cpp:561 engines/groovie/script.cpp:420 +#: engines/sky/compact.cpp:131 engines/sky/compact.cpp:141 +#: engines/sword1/animation.cpp:539 engines/sword1/animation.cpp:560 +#: engines/sword1/animation.cpp:570 engines/sword1/animation.cpp:577 +#: engines/sword1/control.cpp:865 engines/sword1/logic.cpp:1633 +#: engines/sword2/animation.cpp:435 engines/sword2/animation.cpp:455 +#: engines/sword2/animation.cpp:465 engines/sword2/animation.cpp:474 +#: engines/parallaction/saveload.cpp:274 backends/platform/wii/options.cpp:47 +#: backends/platform/wince/CELauncherDialog.cpp:54 +msgid "OK" +msgstr "OK" + +#: gui/KeysDialog.cpp:49 +msgid "Select an action and click 'Map'" +msgstr "°ÑïàëæÕ Ô×ÕïÝÝÕ ö ÚÛöÚÝöæÕ '¿àë×ÝÐçëæì'" + +#: gui/KeysDialog.cpp:80 gui/KeysDialog.cpp:102 gui/KeysDialog.cpp:141 +#, c-format +msgid "Associated key : %s" +msgstr "¿àë×ÝÐçÐÝÐï ÚÛÐÒöèÐ : %s" + +#: gui/KeysDialog.cpp:82 gui/KeysDialog.cpp:104 gui/KeysDialog.cpp:143 +#, c-format +msgid "Associated key : none" +msgstr "¿àë×ÝÐçÐÝÐï ÚÛÐÒöèÐ : ÝïÜÐ" + +#: gui/KeysDialog.cpp:90 +msgid "Please select an action" +msgstr "ºÐÛö ÛÐáÚÐ, ÐÑïàëæÕ Ô×ÕïÝÝÕ" + +#: gui/KeysDialog.cpp:106 +msgid "Press the key to associate" +msgstr "½ÐæöáÝöæÕ ÚÛÐÒöèã ÔÛï ßàë×ÝÐçíÝÝï" + +#: gui/launcher.cpp:187 +msgid "Game" +msgstr "³ãÛìÝï" + +#: gui/launcher.cpp:191 +msgid "ID:" +msgstr "ID:" + +#: gui/launcher.cpp:191 gui/launcher.cpp:193 gui/launcher.cpp:194 +msgid "" +"Short game identifier used for referring to savegames and running the game " +"from the command line" +msgstr "" +"ºÐàÞâÚö öÔíÝâëäöÚÐâÐà, ÒëÚÐàëáâÞþÒÐÝë ÔÛï öÜñÝÐþ ×ÐåÐÒÐÝÝïþ ÓãÛìÝïþ ö ÔÛï " +"×ÐßãáÚã × ÚÐÜÐÝÔÝÐÓÐ àÐÔÚÐ" + +#: gui/launcher.cpp:193 +msgctxt "lowres" +msgid "ID:" +msgstr "ID:" + +#: gui/launcher.cpp:198 +msgid "Name:" +msgstr "½Ð×ÒÐ:" + +#: gui/launcher.cpp:198 gui/launcher.cpp:200 gui/launcher.cpp:201 +msgid "Full title of the game" +msgstr "¿ÞþÝÐï ÝÐ×ÒÐ ÓãÛìÝö" + +#: gui/launcher.cpp:200 +msgctxt "lowres" +msgid "Name:" +msgstr "½Ð×Ò:" + +#: gui/launcher.cpp:204 +msgid "Language:" +msgstr "¼ÞÒÐ:" + +#: gui/launcher.cpp:204 gui/launcher.cpp:205 +msgid "" +"Language of the game. This will not turn your Spanish game version into " +"English" +msgstr "" +"¼ÞÒÐ ÓãÛìÝö. ·ÜÕÝÐ ÓíâÐÙ ÝÐÛÐÔë ÝÕ ßÕàÐâÒÞàëæì ÐÝÓÕÛìáÚãî ÓãÛìÝî þ àãáÚãî" + +#: gui/launcher.cpp:206 gui/launcher.cpp:220 gui/options.cpp:80 +#: gui/options.cpp:730 gui/options.cpp:743 gui/options.cpp:1199 +#: audio/null.cpp:40 +msgid "<default>" +msgstr "<ßÐ ×ÜÐþçÐÝÝö>" + +#: gui/launcher.cpp:216 +msgid "Platform:" +msgstr "¿ÛÐâäÞàÜÐ:" + +#: gui/launcher.cpp:216 gui/launcher.cpp:218 gui/launcher.cpp:219 +msgid "Platform the game was originally designed for" +msgstr "¿ÛÐâäÞàÜÐ, ÔÛï ïÚÞÙ ÓãÛìÝï ÑëÛÐ ßÕàèÐßÐçÐâÚÞÒÐ àÐáßàÐæÐÒÐÝÐ" + +#: gui/launcher.cpp:218 +msgctxt "lowres" +msgid "Platform:" +msgstr "¿ÛÐâäÞàÜÐ:" + +#: gui/launcher.cpp:231 +msgid "Engine" +msgstr "ÀãåÐÒöçÞÚ" + +#: gui/launcher.cpp:239 gui/options.cpp:1062 gui/options.cpp:1079 +msgid "Graphics" +msgstr "³àÐäöÚÐ" + +#: gui/launcher.cpp:239 gui/options.cpp:1062 gui/options.cpp:1079 +msgid "GFX" +msgstr "³àä" + +#: gui/launcher.cpp:242 +msgid "Override global graphic settings" +msgstr "¿ÕàÐÚàëæì ÓÛÐÑÐÛìÝëï ÝÐÛÐÔë ÓàÐäöÚö" + +#: gui/launcher.cpp:244 +msgctxt "lowres" +msgid "Override global graphic settings" +msgstr "¿ÕàÐÚàëæì ÓÛÐÑÐÛìÝëï ÝÐÛÐÔë ÓàÐäöÚö" + +#: gui/launcher.cpp:251 gui/options.cpp:1085 +msgid "Audio" +msgstr "°þÔëñ" + +#: gui/launcher.cpp:254 +msgid "Override global audio settings" +msgstr "¿ÕàÐÚàëæì ÓÛÐÑÐÛìÝëï ÝÐÛÐÔë ÐþÔëñ" + +#: gui/launcher.cpp:256 +msgctxt "lowres" +msgid "Override global audio settings" +msgstr "¿ÕàÐÚàëæì ÓÛÐÑÐÛìÝëï ÝÐÛÐÔë ÐþÔëñ" + +#: gui/launcher.cpp:265 gui/options.cpp:1090 +msgid "Volume" +msgstr "³ãçÝÐáæì" + +#: gui/launcher.cpp:267 gui/options.cpp:1092 +msgctxt "lowres" +msgid "Volume" +msgstr "³ãçÝ" + +#: gui/launcher.cpp:270 +msgid "Override global volume settings" +msgstr "¿ÕàÐÚàëæì ÓÛÐÑÐÛìÝëï ÝÐÛÐÔë ÓãçÝÐáæö" + +#: gui/launcher.cpp:272 +msgctxt "lowres" +msgid "Override global volume settings" +msgstr "¿ÕàÐÚàëæì ÓÛÐÑÐÛìÝëï ÝÐÛÐÔë ÓãçÝÐáæö" + +#: gui/launcher.cpp:280 gui/options.cpp:1100 +msgid "MIDI" +msgstr "MIDI" + +#: gui/launcher.cpp:283 +msgid "Override global MIDI settings" +msgstr "¿ÕàÐÚàëæì ÓÛÐÑÐÛìÝëï ÝÐÛÐÔë MIDI" + +#: gui/launcher.cpp:285 +msgctxt "lowres" +msgid "Override global MIDI settings" +msgstr "¿ÕàÐÚàëæì ÓÛÐÑÐÛìÝëï ÝÐÛÐÔë MIDI" + +#: gui/launcher.cpp:294 gui/options.cpp:1106 +msgid "MT-32" +msgstr "MT-32" + +#: gui/launcher.cpp:297 +msgid "Override global MT-32 settings" +msgstr "¿ÕàÐÚàëæì ÓÛÐÑÐÛìÝëï ÝÐÛÐÔë MT-32" + +#: gui/launcher.cpp:299 +msgctxt "lowres" +msgid "Override global MT-32 settings" +msgstr "¿ÕàÐÚàëæì ÓÛÐÑÐÛìÝëï ÝÐÛÐÔë MT-32" + +#: gui/launcher.cpp:308 gui/options.cpp:1113 +msgid "Paths" +msgstr "ÈÛïåö" + +#: gui/launcher.cpp:310 gui/options.cpp:1115 +msgctxt "lowres" +msgid "Paths" +msgstr "ÈÛïåö" + +#: gui/launcher.cpp:317 +msgid "Game Path:" +msgstr "ÈÛïå ÔÐ ÓãÛìÝö:" + +#: gui/launcher.cpp:319 +msgctxt "lowres" +msgid "Game Path:" +msgstr "´×Õ ÓãÛìÝï:" + +#: gui/launcher.cpp:324 gui/options.cpp:1139 +msgid "Extra Path:" +msgstr "´ÐÔ. èÛïå:" + +#: gui/launcher.cpp:324 gui/launcher.cpp:326 gui/launcher.cpp:327 +msgid "Specifies path to additional data used the game" +msgstr "¿ÐÚÐ×ÒÐÕ èÛïå ÔÐ ÔÐÔÐâÚÞÒëå äÐÙÛÐþ, ÔÐÔ×ÕÝëå ÔÛï ÓãÛìÝö" + +#: gui/launcher.cpp:326 gui/options.cpp:1141 +msgctxt "lowres" +msgid "Extra Path:" +msgstr "´ÐÔ. èÛïå:" + +#: gui/launcher.cpp:333 gui/options.cpp:1123 +msgid "Save Path:" +msgstr "·ÐåÐÒÐÝÝö ÓãÛìÝïþ:" + +#: gui/launcher.cpp:333 gui/launcher.cpp:335 gui/launcher.cpp:336 +#: gui/options.cpp:1123 gui/options.cpp:1125 gui/options.cpp:1126 +msgid "Specifies where your savegames are put" +msgstr "¿ÐÚÐ×ÒÐÕ èÛïå ÔÐ ×ÐåÐÒÐÝÝïþ ÓãÛìÝö" + +#: gui/launcher.cpp:335 gui/options.cpp:1125 +msgctxt "lowres" +msgid "Save Path:" +msgstr "ÈÛïå ×Ðå:" + +#: gui/launcher.cpp:354 gui/launcher.cpp:453 gui/launcher.cpp:511 +#: gui/launcher.cpp:565 gui/options.cpp:1134 gui/options.cpp:1142 +#: gui/options.cpp:1151 gui/options.cpp:1258 gui/options.cpp:1264 +#: gui/options.cpp:1272 gui/options.cpp:1302 gui/options.cpp:1308 +#: gui/options.cpp:1315 gui/options.cpp:1408 gui/options.cpp:1411 +#: gui/options.cpp:1423 +msgctxt "path" +msgid "None" +msgstr "½Õ ×ÐÔÐÔ×ÕÝë" + +#: gui/launcher.cpp:359 gui/launcher.cpp:459 gui/launcher.cpp:569 +#: gui/options.cpp:1252 gui/options.cpp:1296 gui/options.cpp:1414 +#: backends/platform/wii/options.cpp:56 +msgid "Default" +msgstr "¿Ð ×ÜÐþçÐÝÝö" + +#: gui/launcher.cpp:504 gui/options.cpp:1417 +msgid "Select SoundFont" +msgstr "°ÑïàëæÕ SoundFont" + +#: gui/launcher.cpp:523 gui/launcher.cpp:677 +msgid "Select directory with game data" +msgstr "°ÑïàëæÕ ÔëàíÚâÞàëî × äÐÙÛÐÜö ÓãÛìÝö" + +#: gui/launcher.cpp:541 +msgid "Select additional game directory" +msgstr "°ÑïàëæÕ ÔÐÔÐâÚÞÒãî ÔëàíÚâÞàëî ÓãÛìÝö" + +#: gui/launcher.cpp:553 +msgid "Select directory for saved games" +msgstr "°ÑïàëæÕ ÔëàíÚâÞàëî ÔÛï ×ÐåÐÒÐÝÝïþ" + +#: gui/launcher.cpp:580 +msgid "This game ID is already taken. Please choose another one." +msgstr "³íâë ID ÓãÛìÝö þÖÞ ÒëÚÐàëáâÞþÒÐÕææÐ. ºÐÛö ÛÐáÚÐ, ÐÑïàëæÕ öÝèë." + +#: gui/launcher.cpp:621 engines/dialogs.cpp:110 +msgid "~Q~uit" +msgstr "~²~ëåÐÔ" + +#: gui/launcher.cpp:621 backends/platform/sdl/macosx/appmenu_osx.mm:96 +msgid "Quit ScummVM" +msgstr "·ÐÒïàèëæì ScummVM" + +#: gui/launcher.cpp:622 +msgid "A~b~out..." +msgstr "¿àÐ ß~à~ÐÓàÐÜã..." + +#: gui/launcher.cpp:622 backends/platform/sdl/macosx/appmenu_osx.mm:70 +msgid "About ScummVM" +msgstr "¿àÐ ßàÐÓàÐÜã ScummVM" + +#: gui/launcher.cpp:623 +msgid "~O~ptions..." +msgstr "~½~ÐÛÐÔë..." + +#: gui/launcher.cpp:623 +msgid "Change global ScummVM options" +msgstr "·ÜïÝöæì ÓÛÐÑÐÛìÝëï ÝÐÛÐÔë ScummVM" + +#: gui/launcher.cpp:625 +msgid "~S~tart" +msgstr "¿~ã~áÚ" + +#: gui/launcher.cpp:625 +msgid "Start selected game" +msgstr "·Ðßãáæöæì ÐÑàÐÝãî ÓãÛìÝî" + +#: gui/launcher.cpp:628 +msgid "~L~oad..." +msgstr "~·~ÐÓàã×öæì..." + +#: gui/launcher.cpp:628 +msgid "Load savegame for selected game" +msgstr "·ÐÓàã×öæì ×ÐåÐÒÐÝÝÕ ÔÛï ÐÑàÐÝÐÙ ÓãÛìÝö" + +#: gui/launcher.cpp:633 gui/launcher.cpp:1120 +msgid "~A~dd Game..." +msgstr "~´~ÐÔÐæì ÓãÛìÝî..." + +#: gui/launcher.cpp:633 gui/launcher.cpp:640 +msgid "Hold Shift for Mass Add" +msgstr "ÃâàëÜÛöÒÐÙæÕ ÚÛÐÒöèã Shift ÔÛï âÐÓÞ, ÚÐÑ ÔÐÔÐæì ÝÕÚÐÛìÚö ÓãÛìÝïþ" + +#: gui/launcher.cpp:635 +msgid "~E~dit Game..." +msgstr "½~Ð~ÛÐÔë ÓãÛìÝö..." + +#: gui/launcher.cpp:635 gui/launcher.cpp:642 +msgid "Change game options" +msgstr "·ÜïÝöæì ÝÐÛÐÔë ÓãÛìÝö" + +#: gui/launcher.cpp:637 +msgid "~R~emove Game" +msgstr "²~ë~ÔÐÛöæì ÓãÛìÝî" + +#: gui/launcher.cpp:637 gui/launcher.cpp:644 +msgid "Remove game from the list. The game data files stay intact" +msgstr "²ëÔÐÛöæì ÓãÛìÝî áÐ áßöáã. ½Õ ÒëÔÐÛïÕ ÓãÛìÝî × ÖÞàáâÚÐÓÐ ÔëáÚÐ" + +#: gui/launcher.cpp:640 gui/launcher.cpp:1120 +msgctxt "lowres" +msgid "~A~dd Game..." +msgstr "~´~ÐÔ. ÓãÛìÝî..." + +#: gui/launcher.cpp:642 +msgctxt "lowres" +msgid "~E~dit Game..." +msgstr "½~Ð~Û. ÓãÛìÝö..." + +#: gui/launcher.cpp:644 +msgctxt "lowres" +msgid "~R~emove Game" +msgstr "²~ë~ÔÐÛöæì ÓãÛìÝî" + +#: gui/launcher.cpp:652 +msgid "Search in game list" +msgstr "¿ÞèãÚ ã áßöáÕ ÓãÛìÝïþ" + +#: gui/launcher.cpp:656 gui/launcher.cpp:1167 +msgid "Search:" +msgstr "¿ÞèãÚ:" + +#: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/mohawk/myst.cpp:245 +#: engines/mohawk/riven.cpp:716 engines/cruise/menu.cpp:214 +msgid "Load game:" +msgstr "·ÐÓàã×öæì ÓãÛìÝî:" + +#: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/scumm/dialogs.cpp:188 +#: engines/mohawk/myst.cpp:245 engines/mohawk/riven.cpp:716 +#: engines/cruise/menu.cpp:214 backends/platform/wince/CEActionsPocket.cpp:267 +#: backends/platform/wince/CEActionsSmartphone.cpp:231 +msgid "Load" +msgstr "·ÐÓàã×öæì" + +#: gui/launcher.cpp:788 +msgid "" +"Do you really want to run the mass game detector? This could potentially add " +"a huge number of games." +msgstr "" +"²ë áÐßàÐþÔë ÖÐÔÐÕæÕ ×Ðßãáæöæì ÔíâíÚâÐà ãáöå ÓãÛìÝïþ? ³íâÐ ßÐâíÝæëïÛìÝÐ ÜÞÖÐ " +"ÔÐÔÐæì ÒïÛöÚãî ÚÞÛìÚÐáæì ÓãÛìÝïþ." + +#: gui/launcher.cpp:789 gui/launcher.cpp:937 +#: backends/events/symbiansdl/symbiansdl-events.cpp:184 +#: backends/platform/wince/CEActionsPocket.cpp:326 +#: backends/platform/wince/CEActionsSmartphone.cpp:287 +#: backends/platform/wince/CELauncherDialog.cpp:83 +msgid "Yes" +msgstr "ÂÐÚ" + +#: gui/launcher.cpp:789 gui/launcher.cpp:937 +#: backends/events/symbiansdl/symbiansdl-events.cpp:184 +#: backends/platform/wince/CEActionsPocket.cpp:326 +#: backends/platform/wince/CEActionsSmartphone.cpp:287 +#: backends/platform/wince/CELauncherDialog.cpp:83 +msgid "No" +msgstr "½Õ" + +#: gui/launcher.cpp:837 +msgid "ScummVM couldn't open the specified directory!" +msgstr "ScummVM ÝÕ ÜÞÖÐ ÐÔÚàëæì Ð×ÝÐçÐÝãî ÔëàíÚâÞàëî!" + +#: gui/launcher.cpp:849 +msgid "ScummVM could not find any game in the specified directory!" +msgstr "ScummVM ÝÕ ÜÞÖÐ ×ÝÐÙáæö ÓãÛìÝî þ Ð×ÝÐçÐÝÐÙ ÔëàíÚâÞàëö!" + +#: gui/launcher.cpp:863 +msgid "Pick the game:" +msgstr "°ÑïàëæÕ ÓãÛìÝî:" + +#: gui/launcher.cpp:937 +msgid "Do you really want to remove this game configuration?" +msgstr "²ë áÐßàÐþÔë ÖÐÔÐÕæÕ ÒëÔÐÛöæì ÝÐÛÐÔë ÔÛï ÓíâÐÙ ÓãÛìÝö?" + +#: gui/launcher.cpp:1001 +msgid "This game does not support loading games from the launcher." +msgstr "³íâÐï ÓãÛìÝï ÝÕ ßÐÔâàëÜÛöÒÐÕ ×ÐÓàã×Úã ×ÐåÐÒÐÝÝïþ ßàÐ× ÓÐÛÞþÝÐÕ ÜÕÝî." + +#: gui/launcher.cpp:1005 +msgid "ScummVM could not find any engine capable of running the selected game!" +msgstr "ScummVM ÝÕ ×ÜÞÓ ×ÝÐÙáæö àãåÐÒöçÞÚ ÔÛï ×ÐßãáÚã ÐÑàÐÝÐÙ ÓãÛìÝö!" + +#: gui/launcher.cpp:1119 +msgctxt "lowres" +msgid "Mass Add..." +msgstr "ÈÜÐâ ÓãÛìÝïþ..." + +#: gui/launcher.cpp:1119 +msgid "Mass Add..." +msgstr "ÈÜÐâ ÓãÛìÝïþ..." + +#: gui/massadd.cpp:78 gui/massadd.cpp:81 +msgid "... progress ..." +msgstr "... èãÚÐî ..." + +#: gui/massadd.cpp:258 +msgid "Scan complete!" +msgstr "¿ÞèãÚ áÚÞÝçÐÝë!" + +#: gui/massadd.cpp:261 +#, c-format +msgid "Discovered %d new games, ignored %d previously added games." +msgstr "·ÝÞÙÔ×ÕÝÐ %d ÝÞÒëå ÓãÛìÝïþ, ßàÐßãèçÐÝÐ %d àÐÝÕÙ ÔÐÔÐÔ×ÕÝëå ÓãÛìÝïþ." + +#: gui/massadd.cpp:265 +#, c-format +msgid "Scanned %d directories ..." +msgstr "¿àÐÓÛÕÔÖÐÝÐ %d ÔëàíÚâÞàëÙ ..." + +#: gui/massadd.cpp:268 +#, c-format +msgid "Discovered %d new games, ignored %d previously added games ..." +msgstr "·ÝÞÙÔ×ÕÝÐ %d ÝÞÒëå ÓãÛìÝïþ, ßàÐßãèçÐÝÐ %d àÐÝÕÙ ÔÐÔÐÔ×ÕÝëå ÓãÛìÝïþ ..." + +#: gui/options.cpp:78 +msgid "Never" +msgstr "½öÚÞÛö" + +#: gui/options.cpp:78 +msgid "every 5 mins" +msgstr "ÚÞÖÝëï 5 åÒöÛöÝ" + +#: gui/options.cpp:78 +msgid "every 10 mins" +msgstr "ÚÞÖÝëï 10 åÒöÛöÝ" + +#: gui/options.cpp:78 +msgid "every 15 mins" +msgstr "ÚÞÖÝëï 15 åÒöÛöÝ" + +#: gui/options.cpp:78 +msgid "every 30 mins" +msgstr "ÚÞÖÝëï 30 åÒöÛöÝ" + +#: gui/options.cpp:80 +msgid "8 kHz" +msgstr "8 Ú³æ" + +#: gui/options.cpp:80 +msgid "11kHz" +msgstr "11 Ú³æ" + +#: gui/options.cpp:80 +msgid "22 kHz" +msgstr "22 Ú³æ" + +#: gui/options.cpp:80 +msgid "44 kHz" +msgstr "44 Ú³æ" + +#: gui/options.cpp:80 +msgid "48 kHz" +msgstr "48 Ú³æ" + +#: gui/options.cpp:248 gui/options.cpp:474 gui/options.cpp:575 +#: gui/options.cpp:644 gui/options.cpp:852 +msgctxt "soundfont" +msgid "None" +msgstr "½Õ ×ÐÔÐÔ×ÕÝë" + +#: gui/options.cpp:382 +msgid "Failed to apply some of the graphic options changes:" +msgstr "½Õ ÐâàëÜÐÛÐáï þÖëæì ×ÜÕÝë ÝÕÚÐâÞàëå ÓàÐäöçÝëå ÝÐÛÐÔ:" + +#: gui/options.cpp:394 +msgid "the video mode could not be changed." +msgstr "ÒöÔíÐàíÖëÜ ÝÕ ÜÞÖÐ Ñëæì ×ÜÕÝÕÝë." + +#: gui/options.cpp:400 +msgid "the fullscreen setting could not be changed" +msgstr "ßÞþÝÐíÚàÐÝÝë àíÖëÜ ÝÕ ÜÞÖÐ Ñëæì ×ÜÕÝÕÝë" + +#: gui/options.cpp:406 +msgid "the aspect ratio setting could not be changed" +msgstr "àíÖëÜ ÚÐàíÚâëàÞþÚö áãÐÔÝÞáöÝ ÑÐÚÞþ ÝÕ ÜÞÖÐ Ñëæì ×ÜÕÝÕÝë" + +#: gui/options.cpp:727 +msgid "Graphics mode:" +msgstr "³àÐä. àíÖëÜ:" + +#: gui/options.cpp:741 +msgid "Render mode:" +msgstr "ÀíÖëÜ àÐáâàã:" + +#: gui/options.cpp:741 gui/options.cpp:742 +msgid "Special dithering modes supported by some games" +msgstr "ÁßÕæëïÛìÝëï àíÖëÜë àíÝÔíàëÝÓã, ßÐÔâàëÜÞþÒÐÝëï ÝÕÚÐâÞàëÜö ÓãÛìÝïÜö" + +#: gui/options.cpp:753 +#: backends/graphics/surfacesdl/surfacesdl-graphics.cpp:2236 +#: backends/graphics/openglsdl/openglsdl-graphics.cpp:472 +msgid "Fullscreen mode" +msgstr "¿ÞþÝÐíÚàÐÝÝë àíÖëÜ" + +#: gui/options.cpp:756 +msgid "Aspect ratio correction" +msgstr "ºÐàíÚæëï áãÐÔÝÞáöÝ ÑÐÚÞþ" + +#: gui/options.cpp:756 +msgid "Correct aspect ratio for 320x200 games" +msgstr "ºÐàíÚâÐÒÐæì áãÐÔÝÞáöÝë ÑÐÚÞþ ÔÛï ÓãÛìÝïþ × àí×ÐÛîæëïÙ 320x200" + +#: gui/options.cpp:764 +msgid "Preferred Device:" +msgstr "ÃßÐÔÐÑÐÝÐÕ:" + +#: gui/options.cpp:764 +msgid "Music Device:" +msgstr "³ãÚÐÒÐï ßàëÛÐÔÐ:" + +#: gui/options.cpp:764 gui/options.cpp:766 +msgid "Specifies preferred sound device or sound card emulator" +msgstr "·Ð×ÝÐçÐÕ þßÐÔÐÑÐÝãî ÓãÚÐÒãî ßàëÛÐÔã æö íÜãÛïâÐà ÓãÚÐÒÞÙ ÚÐàâë" + +#: gui/options.cpp:764 gui/options.cpp:766 gui/options.cpp:767 +msgid "Specifies output sound device or sound card emulator" +msgstr "·Ð×ÝÐçÐÕ ÒëåÞÔÝãî ÓãÚÐÒãî ßàëÛÐÔã æö íÜãÛïâÐà ÓãÚÐÒÞÙ ÚÐàâë" + +#: gui/options.cpp:766 +msgctxt "lowres" +msgid "Preferred Dev.:" +msgstr "ÃßÐÔÐÑÐÝÐÕ:" + +#: gui/options.cpp:766 +msgctxt "lowres" +msgid "Music Device:" +msgstr "³ãÚ. ßàëÛÐÔÐ:" + +#: gui/options.cpp:793 +msgid "AdLib emulator:" +msgstr "ÍÜãÛïâÐà AdLib:" + +#: gui/options.cpp:793 gui/options.cpp:794 +msgid "AdLib is used for music in many games" +msgstr "³ãÚÐÒÐï ÚÐàâÐ AdLib ÒëÚÐàëáâÞþÒÐÕææÐ ÜÝÞÓöÜö ÓãÛìÝïÜö" + +#: gui/options.cpp:804 +msgid "Output rate:" +msgstr "ÇÐèçëÝï ÓãÚã:" + +#: gui/options.cpp:804 gui/options.cpp:805 +msgid "" +"Higher value specifies better sound quality but may be not supported by your " +"soundcard" +msgstr "" +"±ÞÛìèëï ×ÝÐçíÝÝö ×ÐÔÐîæì ÛÕßèãî ïÚÐáæì ÓãÚã, ÐÔÝÐÚ ïÝë ÜÞÓãæì ÝÕ " +"ßÐÔâàëÜÛöÒÐææÐ ÒÐèÐÙ ÓãÚÐÒÞÙ ÚÐàâÐÙ" + +#: gui/options.cpp:815 +msgid "GM Device:" +msgstr "¿àëÛÐÔÐ GM:" + +#: gui/options.cpp:815 +msgid "Specifies default sound device for General MIDI output" +msgstr "·Ð×ÝÐçÐÕ ÒëåÞÔÝãî ÓãÚÐÒãî ßàëÛÐÔã ÔÛï MIDI" + +#: gui/options.cpp:826 +msgid "Don't use General MIDI music" +msgstr "½Õ ÒëÚÐàëáâÞþÒÐæì Üã×ëÚã ÔÛï General MIDI" + +#: gui/options.cpp:837 gui/options.cpp:899 +msgid "Use first available device" +msgstr "²ëÚÐàëáâÞþÒÐæì ßÕàèãî ÔÐáâãßÝãî ßàëÛÐÔã" + +#: gui/options.cpp:849 +msgid "SoundFont:" +msgstr "SoundFont:" + +#: gui/options.cpp:849 gui/options.cpp:851 gui/options.cpp:852 +msgid "SoundFont is supported by some audio cards, Fluidsynth and Timidity" +msgstr "" +"SoundFont'ë ßÐÔâàëÜÛöÒÐîææÐ ÝÕÚÐâÞàëÜö ÓãÚÐÒëÜö ÚÐàâÐÜö, Fluidsynth Ôë " +"Timidity" + +#: gui/options.cpp:851 +msgctxt "lowres" +msgid "SoundFont:" +msgstr "SoundFont:" + +#: gui/options.cpp:857 +msgid "Mixed AdLib/MIDI mode" +msgstr "·ÜÕèÐÝë àíÖëÜ AdLib/MIDI" + +#: gui/options.cpp:857 +msgid "Use both MIDI and AdLib sound generation" +msgstr "²ëÚÐàëáâÞþÒÐæì ö MIDI, ö AdLib ÔÛï ÓÕÝÕàÐæëö ÓãÚã" + +#: gui/options.cpp:860 +msgid "MIDI gain:" +msgstr "Ã×ÜÐæÝÕÝÝÕ MIDI:" + +#: gui/options.cpp:870 +msgid "MT-32 Device:" +msgstr "½ÐÛ. MT-32:" + +#: gui/options.cpp:870 +msgid "Specifies default sound device for Roland MT-32/LAPC1/CM32l/CM64 output" +msgstr "" +"¿ÐÚÐ×ÒÐÕ ÓãÚÐÒãî ßàëÛÐÔã ßÐ ×ÜÐþçÐÝÝö ÔÛï ÒëÒÐÔã ÝÐ Roland MT-32/LAPC1/CM32l/" +"CM64" + +#: gui/options.cpp:875 +msgid "True Roland MT-32 (disable GM emulation)" +msgstr "ÁÐßàÐþÔÝë Roland MT-32 (×ÐÑÐàÐÝöæì íÜãÛïæëî GM)" + +#: gui/options.cpp:875 gui/options.cpp:877 +msgid "" +"Check if you want to use your real hardware Roland-compatible sound device " +"connected to your computer" +msgstr "" +"°Ô×ÝÐçæÕ, ÚÐÛö þ ÒÐá ßÐÔÚÛîçÐÝÐ Roland-áãÜïèçÐÛìÝÐï ÓãÚÐÒÐï ßàëÛÐÔÐ ö Òë " +"ÖÐÔÐÕæÕ ïÕ ÒëÚÐàëáâÞþÒÐæì" + +#: gui/options.cpp:877 +msgctxt "lowres" +msgid "True Roland MT-32 (no GM emulation)" +msgstr "ÁÐßàÐþÔÝë Roland MT-32 (×ÐÑÐàÐÝöæì GM)" + +#: gui/options.cpp:880 +msgid "Enable Roland GS Mode" +msgstr "ÃÚÛîçëæì àíÖëÜ Roland GS" + +#: gui/options.cpp:880 +msgid "Turns off General MIDI mapping for games with Roland MT-32 soundtrack" +msgstr "" +"²ëÚÛîçÐÕ áãßÐáâÐþÛÕÝÝÕ General MIDI ÔÛï ÓãÛìÝïþ × ÓãÚÐÒÞÙ ÔÐàÞÖÚÐÙ ÔÛï " +"Roland MT-32" + +#: gui/options.cpp:889 +msgid "Don't use Roland MT-32 music" +msgstr "½Õ ÒëÚÐàëáâÞþÒÐæì Üã×ëÚã ÔÛï MT-32" + +#: gui/options.cpp:916 +msgid "Text and Speech:" +msgstr "ÂíÚáâ ö ÐÓãçÚÐ:" + +#: gui/options.cpp:920 gui/options.cpp:930 +msgid "Speech" +msgstr "°ÓãçÚÐ" + +#: gui/options.cpp:921 gui/options.cpp:931 +msgid "Subtitles" +msgstr "ÁãÑâëâàë" + +#: gui/options.cpp:922 +msgid "Both" +msgstr "°ÑÞÕ" + +#: gui/options.cpp:924 +msgid "Subtitle speed:" +msgstr "ÅãâÚÐáæì âëâàÐþ:" + +#: gui/options.cpp:926 +msgctxt "lowres" +msgid "Text and Speech:" +msgstr "ÂíÚáâ ö ÐÓãçÚÐ:" + +#: gui/options.cpp:930 +msgid "Spch" +msgstr "°Óãç" + +#: gui/options.cpp:931 +msgid "Subs" +msgstr "ÁãÑ" + +#: gui/options.cpp:932 +msgctxt "lowres" +msgid "Both" +msgstr "°ÑÞÕ" + +#: gui/options.cpp:932 +msgid "Show subtitles and play speech" +msgstr "¿ÐÚÐ×ÒÐæì áãÑâëâàë ö ßàÐÙÓàÐÒÐæì ÓÐÒÞàÚã" + +#: gui/options.cpp:934 +msgctxt "lowres" +msgid "Subtitle speed:" +msgstr "ÅãâÚÐáæì âëâàÐþ:" + +#: gui/options.cpp:950 +msgid "Music volume:" +msgstr "³ãçÝ. Üã×ëÚö:" + +#: gui/options.cpp:952 +msgctxt "lowres" +msgid "Music volume:" +msgstr "³ãçÝ. Üã×ëÚö:" + +#: gui/options.cpp:959 +msgid "Mute All" +msgstr "²ëÚÛ. ãáñ" + +#: gui/options.cpp:962 +msgid "SFX volume:" +msgstr "³ãçÝÐáæì SFX:" + +#: gui/options.cpp:962 gui/options.cpp:964 gui/options.cpp:965 +msgid "Special sound effects volume" +msgstr "³ãçÝÐáæì áßÕæëïÛìÝëå ÓãÚÐÒëå íäÕÚâÐþ" + +#: gui/options.cpp:964 +msgctxt "lowres" +msgid "SFX volume:" +msgstr "³ãçÝ. SFX:" + +#: gui/options.cpp:972 +msgid "Speech volume:" +msgstr "³ãçÝ. ÐÓãçÚö:" + +#: gui/options.cpp:974 +msgctxt "lowres" +msgid "Speech volume:" +msgstr "³ãçÝ. ÐÓãçÚö:" + +#: gui/options.cpp:1131 +msgid "Theme Path:" +msgstr "ÈÛïå ÔÐ âíÜ:" + +#: gui/options.cpp:1133 +msgctxt "lowres" +msgid "Theme Path:" +msgstr "´×Õ âíÜë:" + +#: gui/options.cpp:1139 gui/options.cpp:1141 gui/options.cpp:1142 +msgid "Specifies path to additional data used by all games or ScummVM" +msgstr "" +"¿ÐÚÐ×ÒÐÕ èÛïå ÔÐ ÔÐÔÐâÚÞÒëå äÐÙÛÐþ ÔÐÔ×ÕÝëå, ÒëÚÐàëáâÞþÒÐÝëå ãáöÜö ÓãÛìÝïÜö, " +"ÐÑÞ ScummVM" + +#: gui/options.cpp:1148 +msgid "Plugins Path:" +msgstr "ÈÛïå ÔÐ ßÛÐÓöÝÐþ:" + +#: gui/options.cpp:1150 +msgctxt "lowres" +msgid "Plugins Path:" +msgstr "ÈÛïå ÔÐ ßÛÐÓöÝÐþ:" + +#: gui/options.cpp:1159 +msgid "Misc" +msgstr "ÀÞ×ÝÐÕ" + +#: gui/options.cpp:1161 +msgctxt "lowres" +msgid "Misc" +msgstr "ÀÞ×ÝÐÕ" + +#: gui/options.cpp:1163 +msgid "Theme:" +msgstr "ÂíÜÐ" + +#: gui/options.cpp:1167 +msgid "GUI Renderer:" +msgstr "¼ÐÛïÒÐÛÚÐ GUI:" + +#: gui/options.cpp:1179 +msgid "Autosave:" +msgstr "°þâÐ×ÐåÐÒÐÝÝÕ:" + +#: gui/options.cpp:1181 +msgctxt "lowres" +msgid "Autosave:" +msgstr "°þâÐ×Ðå.:" + +#: gui/options.cpp:1189 +msgid "Keys" +msgstr "ºÛÐÒöèë" + +#: gui/options.cpp:1196 +msgid "GUI Language:" +msgstr "¼ÞÒÐ GUI:" + +#: gui/options.cpp:1196 +msgid "Language of ScummVM GUI" +msgstr "¼ÞÒÐ ÓàÐäöçÝÐÓÐ öÝâíàäÕÙáã ScummVM" + +#: gui/options.cpp:1347 +msgid "You have to restart ScummVM before your changes will take effect." +msgstr "²ë ßÐÒöÝÝë ßÕàÐ×Ðßãáæöæì ScummVM, ÚÐÑ ãÖëæì ×ÜÕÝë." + +#: gui/options.cpp:1360 +msgid "Select directory for savegames" +msgstr "°ÑïàëæÕ ÔëàíÚâÞàëî ÔÛï ×ÐåÐÒÐÝÝïþ" + +#: gui/options.cpp:1367 +msgid "The chosen directory cannot be written to. Please select another one." +msgstr "½Õ ÜÐÓã ßöáÐæì ã ÐÑàÐÝãî ÔëàíÚâÞàëî. ºÐÛö ÛÐáÚÐ, Ð×ÝÐçæÕ öÝèãî." + +#: gui/options.cpp:1376 +msgid "Select directory for GUI themes" +msgstr "°ÑïàëæÕ ÔëàíÚâÞàëî ÔÛï âíÜ GUI" + +#: gui/options.cpp:1386 +msgid "Select directory for extra files" +msgstr "°ÑïàëæÕ ÔëàíÚâÞàëî × ÔÐÔÐâÚÞÒëÜö äÐÙÛÐÜö" + +#: gui/options.cpp:1397 +msgid "Select directory for plugins" +msgstr "°ÑïàëæÕ ÔëàíÚâÞàëî × ßÛÐÓöÝÐÜö" + +#: gui/options.cpp:1450 +msgid "" +"The theme you selected does not support your current language. If you want " +"to use this theme you need to switch to another language first." +msgstr "" +"ÂíÜÐ, ÐÑàÐÝÐï ÒÐÜö, ÝÕ ßÐÔâàëÜÛöÒÐÕ ÑïÓãçãî ÜÞÒã. ºÐÛö Òë ÖÐÔÐÕæÕ " +"ÒëÚÐàëáâÞþÒÐæì Óíâãî âíÜã, ÒÐÜ ÝÕÐÑåÞÔÝÐ áßÐçÐâÚã ßÕàÐÚÛîçëææÐ ÝÐ öÝèãî ÜÞÒã." + +#: gui/saveload-dialog.cpp:158 +msgid "List view" +msgstr "²ëÓÛïÔ áßöáã" + +#: gui/saveload-dialog.cpp:159 +msgid "Grid view" +msgstr "²ëÓÛïÔ áÕâÚö" + +#: gui/saveload-dialog.cpp:202 gui/saveload-dialog.cpp:350 +msgid "No date saved" +msgstr "´ÐâÐ ÝÕ ×ÐßöáÐÝÐ" + +#: gui/saveload-dialog.cpp:203 gui/saveload-dialog.cpp:351 +msgid "No time saved" +msgstr "ÇÐá ÝÕ ×ÐßöáÐÝë" + +#: gui/saveload-dialog.cpp:204 gui/saveload-dialog.cpp:352 +msgid "No playtime saved" +msgstr "ÇÐá ÓãÛìÝö ÝÕ ×ÐßöáÐÝë" + +#: gui/saveload-dialog.cpp:211 gui/saveload-dialog.cpp:267 +msgid "Delete" +msgstr "²ëÔÐÛöæì" + +#: gui/saveload-dialog.cpp:266 +msgid "Do you really want to delete this savegame?" +msgstr "²ë áÐßàÐþÔë ÖÐÔÐÕæÕ ÒëÔÐÛöæì ÓíâÐ ×ÐåÐÒÐÝÝÕ?" + +#: gui/saveload-dialog.cpp:375 gui/saveload-dialog.cpp:796 +msgid "Date: " +msgstr "´ÐâÐ: " + +#: gui/saveload-dialog.cpp:379 gui/saveload-dialog.cpp:802 +msgid "Time: " +msgstr "ÇÐá: " + +#: gui/saveload-dialog.cpp:385 gui/saveload-dialog.cpp:810 +msgid "Playtime: " +msgstr "ÇÐá ÓãÛìÝö: " + +#: gui/saveload-dialog.cpp:398 gui/saveload-dialog.cpp:465 +msgid "Untitled savestate" +msgstr "·ÐåÐÒÐÝÝÕ ÑÕ× öÜï" + +#: gui/saveload-dialog.cpp:517 +msgid "Next" +msgstr "½ÐáâãßÝë" + +#: gui/saveload-dialog.cpp:520 +msgid "Prev" +msgstr "¿ÐßïàíÔÝö" + +#: gui/saveload-dialog.cpp:684 +msgid "New Save" +msgstr "½ÞÒÐÕ ×ÐåÐÒÐÝÝÕ" + +#: gui/saveload-dialog.cpp:684 +msgid "Create a new save game" +msgstr "ÁâÒÐàëæì ÝÞÒë ×Ðßöá ÓãÛìÝö" + +#: gui/saveload-dialog.cpp:789 +msgid "Name: " +msgstr "½Ð×ÒÐ: " + +#: gui/saveload-dialog.cpp:861 +#, c-format +msgid "Enter a description for slot %d:" +msgstr "ÃÒïÔ×öæÕ ÐßöáÐÝÝÕ áÛÞâÐ %d:" + +#: gui/themebrowser.cpp:44 +msgid "Select a Theme" +msgstr "°ÑïàëæÕ âíÜã" + +#: gui/ThemeEngine.cpp:337 +msgid "Disabled GFX" +msgstr "±Õ× ÓàÐäöÚö" + +#: gui/ThemeEngine.cpp:337 +msgctxt "lowres" +msgid "Disabled GFX" +msgstr "±Õ× ÓàÐäöÚö" + +#: gui/ThemeEngine.cpp:338 +msgid "Standard Renderer (16bpp)" +msgstr "ÁâÐÝÔÐàâÝë àÐáâÐàë×ÐâÐà (16bpp)" + +#: gui/ThemeEngine.cpp:338 +msgid "Standard (16bpp)" +msgstr "ÁâÐÝÔÐàâÝë àÐáâÐàë×ÐâÐà (16bpp)" + +#: gui/ThemeEngine.cpp:340 +msgid "Antialiased Renderer (16bpp)" +msgstr "ÀÐáâÐàë×ÐâÐà áÐ ×ÓÛÐÔÖÒÐÝÝÕÜ (16bpp)" + +#: gui/ThemeEngine.cpp:340 +msgid "Antialiased (16bpp)" +msgstr "ÀÐáâÐàë×ÐâÐà áÐ ×ÓÛÐÔÖÒÐÝÝÕÜ (16bpp)" + +#: gui/widget.cpp:322 gui/widget.cpp:324 gui/widget.cpp:330 gui/widget.cpp:332 +msgid "Clear value" +msgstr "°çëáæöæì ×ÝÐçíÝÝÕ" + +#: base/main.cpp:209 +#, c-format +msgid "Engine does not support debug level '%s'" +msgstr "ÀãåÐÒöçÞÚ ÝÕ ßÐÔâàëÜÛöÒÐÕ þ×àÞÒÕÝì ÐÔÛÐÔÚö '%s'" + +#: base/main.cpp:287 +msgid "Menu" +msgstr "¼ÕÝî" + +#: base/main.cpp:290 backends/platform/symbian/src/SymbianActions.cpp:45 +#: backends/platform/wince/CEActionsPocket.cpp:45 +#: backends/platform/wince/CEActionsSmartphone.cpp:46 +msgid "Skip" +msgstr "¿àÐßãáæöæì" + +#: base/main.cpp:293 backends/platform/symbian/src/SymbianActions.cpp:50 +#: backends/platform/wince/CEActionsPocket.cpp:42 +msgid "Pause" +msgstr "¿Ðþ×Ð" + +#: base/main.cpp:296 +msgid "Skip line" +msgstr "¿àÐßãáæöæì àÐÔÞÚ" + +#: base/main.cpp:467 +msgid "Error running game:" +msgstr "¿ÐÜëÛÚÐ ×ÐßãáÚã ÓãÛìÝö:" + +#: base/main.cpp:491 +msgid "Could not find any engine capable of running the selected game" +msgstr "½Õ ÜÐÓã ×ÝÐÙáæö àãåÐÒöçÞÚ ÔÛï ×ÐßãáÚã ÐÑàÐÝÐÙ ÓãÛìÝö" + +#: common/error.cpp:38 +msgid "No error" +msgstr "½ïÜÐ ßÐÜëÛÚö" + +#: common/error.cpp:40 +msgid "Game data not found" +msgstr "½ïÜÐ äÐÙÛÐþ ÓãÛìÝö" + +#: common/error.cpp:42 +msgid "Game id not supported" +msgstr "Game id ÝÕ ßÐÔâàëÜÛöÒÐÕææÐ" + +#: common/error.cpp:44 +msgid "Unsupported color mode" +msgstr "½ÕßÐÔâàëÜÞþÒÐÝë àíÖëÜ ÚÞÛÕàã" + +#: common/error.cpp:47 +msgid "Read permission denied" +msgstr "½ÕÔÐáâÐâÚÞÒÐ ßàÐÒÞþ ÔÛï çëâÐÝÝï" + +#: common/error.cpp:49 +msgid "Write permission denied" +msgstr "½ÕÔÐáâÐâÚÞÒÐ ßàÐÒÞþ ÔÛï ×Ðßöáã" + +#: common/error.cpp:52 +msgid "Path does not exist" +msgstr "ÈÛïå ÝÕ ×ÝÞÙÔ×ÕÝë" + +#: common/error.cpp:54 +msgid "Path not a directory" +msgstr "ÈÛïå ÝÕ ×'ïþÛïÕææÐ ÔëàíÚâÞàëïÙ" + +#: common/error.cpp:56 +msgid "Path not a file" +msgstr "ÈÛïå ÝÕ ×'ïþÛïÕææÐ äÐÙÛÐÜ" + +#: common/error.cpp:59 +msgid "Cannot create file" +msgstr "½Õ ÜÐÓã áâÒÐàëæì äÐÙÛ" + +#: common/error.cpp:61 +msgid "Reading data failed" +msgstr "¿ÐÜëÛÚÐ çëâÐÝÝï ÔÐÔ×ÕÝëå" + +#: common/error.cpp:63 +msgid "Writing data failed" +msgstr "¿ÐÜëÛÚÐ ×Ðßöáã ÔÐÔ×ÕÝëå" + +#: common/error.cpp:66 +msgid "Could not find suitable engine plugin" +msgstr "½Õ ÜÐÓã ×ÝÐÙáæö ßÐÔëåÞÔÝë ßÛÐÓöÝ ÔÛï àãåÐÒöçÚÐ" + +#: common/error.cpp:68 +msgid "Engine plugin does not support save states" +msgstr "ÀãåÐÒöçÞÚ ÝÕ ßÐÔâàëÜÛöÒÐÕ ×ÐåÐÒÐÝÝö" + +#: common/error.cpp:71 +msgid "User canceled" +msgstr "¿ÕàÐßëÝÕÝÐ ÚÐàëáâÐçÞÜ" + +#: common/error.cpp:75 +msgid "Unknown error" +msgstr "½ÕÒïÔÞÜÐï ßÐÜëÛÚÐ" + +#: engines/advancedDetector.cpp:316 +#, c-format +msgid "The game in '%s' seems to be unknown." +msgstr "·ÔÐÕææÐ, èâÞ ÓãÛìÝï '%s' ïèçí ÝÕÒïÔÞÜÐ." + +#: engines/advancedDetector.cpp:317 +msgid "Please, report the following data to the ScummVM team along with name" +msgstr "" +"ºÐÛö ÛÐáÚÐ, ßÕàÐÔÐÙæÕ ÝÐáâãßÝëï ÔÐÔ×ÕÝëï ÚÐÜÐÝÔ×Õ ScummVM àÐ×ÐÜ × ÝÐ×ÒÐÙ" + +#: engines/advancedDetector.cpp:319 +msgid "of the game you tried to add and its version/language/etc.:" +msgstr "ÓãÛìÝö, ïÚãî Òë áßàÐÑãÕæÕ ÔÐÔÐæì, ö Ð×ÝÐçæÕ ïÕ ÒÕàáöî, ÜÞÒã ö Ó.Ô." + +#: engines/dialogs.cpp:84 +msgid "~R~esume" +msgstr "¿àÐæïÓ~Ý~ãæì" + +#: engines/dialogs.cpp:86 +msgid "~L~oad" +msgstr "~·~ÐÓàã×öæì" + +#: engines/dialogs.cpp:90 +msgid "~S~ave" +msgstr "~·~ÐßöáÐæì" + +#: engines/dialogs.cpp:94 +msgid "~O~ptions" +msgstr "~¾~ßæëö" + +#: engines/dialogs.cpp:99 +msgid "~H~elp" +msgstr "~´~ÐßÐÜÞÓÐ" + +#: engines/dialogs.cpp:101 +msgid "~A~bout" +msgstr "¿àÐ ßàÐ~Ó~àÐÜã" + +#: engines/dialogs.cpp:104 engines/dialogs.cpp:180 +msgid "~R~eturn to Launcher" +msgstr "~²~ëÙáæö þ ÓÐÛÞþÝÐÕ ÜÕÝî" + +#: engines/dialogs.cpp:106 engines/dialogs.cpp:182 +msgctxt "lowres" +msgid "~R~eturn to Launcher" +msgstr "~Ã~ ÓÐÛÞþÝÐÕ ÜÕÝî" + +#: engines/dialogs.cpp:115 engines/agi/saveload.cpp:803 +#: engines/cruise/menu.cpp:212 engines/sci/engine/kfile.cpp:742 +msgid "Save game:" +msgstr "·ÐåÐÒÐæì ÓãÛìÝî:" + +#: engines/dialogs.cpp:115 engines/agi/saveload.cpp:803 +#: engines/scumm/dialogs.cpp:187 engines/cruise/menu.cpp:212 +#: engines/sci/engine/kfile.cpp:742 +#: backends/platform/symbian/src/SymbianActions.cpp:44 +#: backends/platform/wince/CEActionsPocket.cpp:43 +#: backends/platform/wince/CEActionsPocket.cpp:267 +#: backends/platform/wince/CEActionsSmartphone.cpp:45 +#: backends/platform/wince/CEActionsSmartphone.cpp:231 +msgid "Save" +msgstr "·ÐåÐÒÐæì" + +#: engines/dialogs.cpp:144 +msgid "" +"Sorry, this engine does not currently provide in-game help. Please consult " +"the README for basic information, and for instructions on how to obtain " +"further assistance." +msgstr "" +"¿àÐÑÐçæÕ, ÐÛÕ Óíâë àãåÐÒöçÞÚ ßÐÚãÛì ÝÕ ßÐÔÐÕ ÔÐßÐÜÞÓö þ ÓãÛìÝö. ºÐÛö ÛÐáÚÐ, " +"×ÒïàÝöæÕáï þ äÐÙÛ README ×Ð ÑÐ×ÐÒÐÙ öÝäÐàÜÐæëïÙ, Ð âÐÚáÐÜÐ öÝáâàãÚæëïÜö ßàÐ " +"âÞÕ, ïÚ ÐâàëÜÐæì ÔÐÛÕÙèãî ÔÐßÐÜÞÓã." + +#: engines/dialogs.cpp:228 +#, c-format +msgid "" +"Gamestate save failed (%s)! Please consult the README for basic information, " +"and for instructions on how to obtain further assistance." +msgstr "" +"½Õ ÐâàëÜÐÛÐáï ×ÐåÐÒÐæì ÓãÛìÝî (%s)! ºÐÛö ÛÐáÚÐ, ×ÒïàÝöæÕáï þ äÐÙÛ README ×Ð " +"ÑÐ×ÐÒÐÙ öÝäÐàÜÐæëïÙ, Ð âÐÚáÐÜÐ öÝáâàãÚæëïÜö ßàÐ âÞÕ, ïÚ ÐâàëÜÐæì ÔÐÛÕÙèãî " +"ÔÐßÐÜÞÓã." + +#: engines/dialogs.cpp:301 engines/mohawk/dialogs.cpp:109 +#: engines/mohawk/dialogs.cpp:174 +msgid "~O~K" +msgstr "~¾~º" + +#: engines/dialogs.cpp:302 engines/mohawk/dialogs.cpp:110 +#: engines/mohawk/dialogs.cpp:175 +msgid "~C~ancel" +msgstr "~°~ÔÜÕÝÐ" + +#: engines/dialogs.cpp:305 +msgid "~K~eys" +msgstr "~º~ÛÐÒöèë" + +#: engines/engine.cpp:235 +msgid "Could not initialize color format." +msgstr "½Õ ÜÐÓã öÝöæëïÛö×ÐÒÐæì äÐàÜÐâ ÚÞÛÕàã." + +#: engines/engine.cpp:243 +msgid "Could not switch to video mode: '" +msgstr "½Õ ÐâàëÜÐÛÐáï ßÕàÐÚÛîçëæì ÒöÔíÐàíÖëÜ: '" + +#: engines/engine.cpp:252 +msgid "Could not apply aspect ratio setting." +msgstr "½Õ ÐâàëÜÐÛÐáï ÒëÚÐàëáâÐæì ÚÐàíÚæëî áãÐÔÝÞáöÝ ÑÐÚÞþ." + +#: engines/engine.cpp:257 +msgid "Could not apply fullscreen setting." +msgstr "½Õ ÜÐÓã þÖëæì ßÞþÝÐíÚàÐÝÝë àíÖëÜ." + +#: engines/engine.cpp:357 +msgid "" +"You appear to be playing this game directly\n" +"from the CD. This is known to cause problems,\n" +"and it is therefore recommended that you copy\n" +"the data files to your hard disk instead.\n" +"See the README file for details." +msgstr "" +"·ÔÐÕææÐ, Òë áßàÐÑãÕæÕ ×Ðßãáæöæì Óíâã ÓãÛìÝî ßàÐÜÐ\n" +"× CD. ³íâÐ ×ÒëçÐÙÝÐ ÒëÚÛöÚÐÕ ßàÐÑÛÕÜë, ö âÐÜã\n" +"Üë àíÚÐÜÕÝÔãÕÜ áÚÐßöïÒÐæì äÐÙÛë ÔÐÔ×ÕÝëå ÓãÛìÝö\n" +"ÝÐ ÖÞàáâÚö ÔëáÚ. ¿ÐÔàÐÑï×ÝÐáæö ÜÞÖÝÐ ×ÝÐÙáæö þ\n" +"äÐÙÛÕ README." + +#: engines/engine.cpp:368 +msgid "" +"This game has audio tracks in its disk. These\n" +"tracks need to be ripped from the disk using\n" +"an appropriate CD audio extracting tool in\n" +"order to listen to the game's music.\n" +"See the README file for details." +msgstr "" +"´ëáÚ ÓíâÐÙ ÓãÛìÝö þâàëÜÞþÒÐÕ ÓãÚÐÒëï ÔÐàÞÖÚö. ¦å\n" +"ÝÕÐÑåÞÔÝÐ ßÕàÐßöáÐæì × ÔëáÚÐ × ÔÐßÐÜÞÓÐÙ\n" +"ÐÔßÐÒÕÔÝÐÙ ßàÐÓàÐÜë ÔÛï ÚÐßöïÒÐÝÝï\n" +"ÐþÔëñÔëáÚÐþ, ö âÞÛìÚö ßÐáÛï ÓíâÐÓÐ þ ÓãÛìÝö\n" +"×'ïÒöææÐ Üã×ëÚÐ. ¿ÐÔàÐÑï×ÝÐáæö ÜÞÖÝÐ ×ÝÐÙáæö þ\n" +"äÐÙÛÕ README." + +#: engines/engine.cpp:426 +#, c-format +msgid "" +"Gamestate load failed (%s)! Please consult the README for basic information, " +"and for instructions on how to obtain further assistance." +msgstr "" +"½Õ ÐâàëÜÐÛÐáï ßàÐçëâÐæì ×ÐåÐÒÐÝÝÕ ÓãÛìÝö (%s)! ºÐÛö ÛÐáÚÐ, ×ÒïàÝöæÕáï þ äÐÙÛ " +"README ×Ð ÑÐ×ÐÒÐÙ öÝäÐàÜÐæëïÙ, Ð âÐÚáÐÜÐ öÝáâàãÚæëïÜö ßàÐ âÞÕ, ïÚ ÐâàëÜÐæì " +"ÔÐÛÕÙèãî ÔÐßÐÜÞÓã." + +#: engines/engine.cpp:439 +msgid "" +"WARNING: The game you are about to start is not yet fully supported by " +"ScummVM. As such, it is likely to be unstable, and any saves you make might " +"not work in future versions of ScummVM." +msgstr "" +"¿°¿ÏÀÍ´¶°½½µ: ³ãÛìÝï, ïÚãî Òë ×ÑöàÐÕæÕáï ×Ðßãáæöæì, ïèçí ÝÕ ßÐÔâàëÜÛöÒÐÕææÐ " +"ScummVM æÐÛÚÐÜ. ÏÝÐ, åãâçíÙ ×Ð þáñ, ÝÕ ÑãÔ×Õ ßàÐæÐÒÐæì áâÐÑöÛìÝÐ, ö " +"×ÐåÐÒÐÝÝö ÓãÛìÝïþ ÜÞÓãæì ÝÕ ßàÐæÐÒÐæì ã ÑãÔãçëå ÒÕàáöïå ScummVM." + +#: engines/engine.cpp:442 +msgid "Start anyway" +msgstr "Ãáñ ÐÔÝÞ ×Ðßãáæöæì" + +#: engines/agi/detection.cpp:145 engines/dreamweb/detection.cpp:47 +#: engines/sci/detection.cpp:390 +msgid "Use original save/load screens" +msgstr "²ëÚÐàëáâÞþÒÐæì ÐàëÓöÝÐÛìÝëï íÚàÐÝë ×Ðßöáã/çëâÐÝÝö ÓãÛìÝö" + +#: engines/agi/detection.cpp:146 engines/dreamweb/detection.cpp:48 +#: engines/sci/detection.cpp:391 +msgid "Use the original save/load screens, instead of the ScummVM ones" +msgstr "" +"²ëÚÐàëáâÞþÒÐæì ÐàëÓöÝÐÛìÝëï íÚàÐÝë ×Ðßöáã ö ×ÐåÐÒÐÝÝö ÓãÛìÝö ×ÐÜÕáâ " +"×àÞÑÛÕÝëå ã ScummVM" + +#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:838 +msgid "Restore game:" +msgstr "Ã×ÝÐÒöæì ÓãÛìÝî:" + +#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:838 +msgid "Restore" +msgstr "Ã×ÝÐÒöæì" + +#: engines/dreamweb/detection.cpp:57 +msgid "Use bright palette mode" +msgstr "²ëÚÐàëáâÞþÒÐæì àíÖëÜ ïàÚÐÙ ßÐÛöâàë" + +#: engines/dreamweb/detection.cpp:58 +msgid "Display graphics using the game's bright palette" +msgstr "¼ÐÛîÕ ÓàÐäöÚã × ÒëÚÐàëáâÐÝÝÕÜ ïàÚÐÙ ßÐÛöâàë ÓãÛìÝö" + +#: engines/sci/detection.cpp:370 +msgid "EGA undithering" +msgstr "EGA ÑÕ× àÐáâàã" + +#: engines/sci/detection.cpp:371 +msgid "Enable undithering in EGA games" +msgstr "ÃÚÛîçÐÕ àíÖëÜ ÑÕ× àÐáâàÐÒÐÝÝï þ EGA ÓãÛìÝïå" + +#: engines/sci/detection.cpp:380 +msgid "Prefer digital sound effects" +msgstr "°ÔÔÐÒÐæì ßÕàÐÒÐÓã ÛöçÑÐÒëÜ ÓãÚÐÒëÜ íäÕÚâÐÜ" + +#: engines/sci/detection.cpp:381 +msgid "Prefer digital sound effects instead of synthesized ones" +msgstr "°ÔÔÐÒÐæì ßÕàÐÒÐÓã ÛöçÑÐÒëÜ ÓãÚÐÒëÜ íäÕÚâÐÜ ×ÐÜÕáâ áöÝâí×ÐÒÐÝëå" + +#: engines/sci/detection.cpp:400 +msgid "Use IMF/Yamaha FB-01 for MIDI output" +msgstr "²ëÚÐàëáâÞþÒÐæì IMF/Yamaha FB-01 ÔÛï ÒëÒÐÔã MIDI" + +#: engines/sci/detection.cpp:401 +msgid "" +"Use an IBM Music Feature card or a Yamaha FB-01 FM synth module for MIDI " +"output" +msgstr "" +"²ëÚÐàëáâÞþÒÐæì ÓãÚÐÒãî ÚÐàâã IBM Music Feature æö ÜÞÔãÛì áöÝâí×ã Yamaha " +"FB-01 FM ÔÛï MIDI" + +#: engines/sci/detection.cpp:411 +msgid "Use CD audio" +msgstr "²ëÚÐàëáâÞþÒÐæì CD ÐþÔëñ" + +#: engines/sci/detection.cpp:412 +msgid "Use CD audio instead of in-game audio, if available" +msgstr "" +"²ëÚÐàëáâÞþÒÐæì ÓãÚÐÒëï ÔÐàÞÖÚö × CD ×ÐÜÕáâ Üã×ëÚö × äÐÙÛÐþ ÓãÛìÝö (ÚÐÛö " +"ÔÐáâãßÝÐ)" + +#: engines/sci/detection.cpp:422 +msgid "Use Windows cursors" +msgstr "²ëÚÐàëáâÞþÒÐæì ÚãàáÞàë Windows" + +#: engines/sci/detection.cpp:423 +msgid "" +"Use the Windows cursors (smaller and monochrome) instead of the DOS ones" +msgstr "" +"²ëÚÐàëáâÞþÒÐæì ÚãàáÞàë Windows (ÜÕÝèëï ßÐ ßÐÜÕàë ö ÐÔÝÐÚÐÛïàÞÒëï) ×ÐÜÕáâ " +"ÚãàáÞàÐþ DOS" + +#: engines/sci/detection.cpp:433 +msgid "Use silver cursors" +msgstr "²ëÚÐàëáâÞþÒÐæì áàíÑÝëï ÚãàáÞàë" + +#: engines/sci/detection.cpp:434 +msgid "" +"Use the alternate set of silver cursors, instead of the normal golden ones" +msgstr "" +"²ëÚÐàëáâÞþÒÐæì ÐÛìâíàÝÐâëþÝë ÝÐÑÞà áàíÑÝëå ÚãàáÞàÐþ ×ÐÜÕáâ ×ÒëçÐÙÝëå ×ÐÛÐâëå" + +#: engines/scumm/dialogs.cpp:175 +#, c-format +msgid "Insert Disk %c and Press Button to Continue." +msgstr "ÃáâÐþæÕ ÔëáÚ %c ö ÝÐæöáÝöæÕ ÚÛÐÒöèã, ÚÐÑ ßàÐæïÓÝãæì." + +#: engines/scumm/dialogs.cpp:176 +#, c-format +msgid "Unable to Find %s, (%c%d) Press Button." +msgstr "½Õ ÐâàëÜÐÛÐáï ×ÝÐÙáæö %s, (%c%d) ½ÐæöáÝöæÕ ÚÛÐÒöèã." + +#: engines/scumm/dialogs.cpp:177 +#, c-format +msgid "Error reading disk %c, (%c%d) Press Button." +msgstr "¿ÐÜëÛÚÐ çëâÐÝÝï ÔëáÚÐ %c, (%c%d) ½ÐæöáÝöæÕ ÚÛÐÒöèã." + +#: engines/scumm/dialogs.cpp:178 +msgid "Game Paused. Press SPACE to Continue." +msgstr "³ãÛìÝï áßëÝÕÝÐ. ½ÐæöáÝöæÕ ßàÐÑÕÛ, ÚÐÑ ßàÐæïÓÝãæì." + +#. I18N: You may specify 'Yes' symbol at the end of the line, like this: +#. "Moechten Sie wirklich neu starten? (J/N)J" +#. Will react to J as 'Yes' +#: engines/scumm/dialogs.cpp:182 +msgid "Are you sure you want to restart? (Y/N)" +msgstr "²ë þßíþÝÕÝë, èâÞ ÖÐÔÐÕæÕ ßÐçÐæì ö×ÝÞþ? (Y/N)" + +#. I18N: you may specify 'Yes' symbol at the end of the line. See previous comment +#: engines/scumm/dialogs.cpp:184 +msgid "Are you sure you want to quit? (Y/N)" +msgstr "²ë þßíþÝÕÝë, èâÞ ÖÐÔÐÕæÕ ÒëÙáæö? (Y/N)" + +#: engines/scumm/dialogs.cpp:189 +msgid "Play" +msgstr "³ãÛïæì" + +#: engines/scumm/dialogs.cpp:191 engines/scumm/help.cpp:82 +#: engines/scumm/help.cpp:84 +#: backends/platform/symbian/src/SymbianActions.cpp:52 +#: backends/platform/wince/CEActionsPocket.cpp:44 +#: backends/platform/wince/CEActionsSmartphone.cpp:52 +#: backends/events/default/default-events.cpp:213 +msgid "Quit" +msgstr "²ëåÐÔ" + +#: engines/scumm/dialogs.cpp:193 +msgid "Insert save/load game disk" +msgstr "ÃáâÐþæÕ ÔëáÚ × ×ÐåÐÒÐÝÝïÜö" + +#: engines/scumm/dialogs.cpp:194 +msgid "You must enter a name" +msgstr "²ë ßÐÒöÝÝë þÒÕáæö öÜï" + +#: engines/scumm/dialogs.cpp:195 +msgid "The game was NOT saved (disk full?)" +msgstr "³ãÛìÝï ½µ ±Ë»° ×ÐßöáÐÝÐ (ÔëáÚ ßÞþÝë?)" + +#: engines/scumm/dialogs.cpp:196 +msgid "The game was NOT loaded" +msgstr "³ãÛìÝï ½µ ±Ë»° ×ÐÓàãÖÐÝÐ" + +#: engines/scumm/dialogs.cpp:197 +#, c-format +msgid "Saving '%s'" +msgstr "·ÐåÞþÒÐî '%s'" + +#: engines/scumm/dialogs.cpp:198 +#, c-format +msgid "Loading '%s'" +msgstr "·ÐÓàãÖÐî '%s'" + +#: engines/scumm/dialogs.cpp:199 +msgid "Name your SAVE game" +msgstr "½Ð×ÐÒöæÕ ×ÐåÐÒÐÝÝÕ ÓãÛìÝö" + +#: engines/scumm/dialogs.cpp:200 +msgid "Select a game to LOAD" +msgstr "°ÑïàëæÕ ÓãÛìÝî ÔÛï ×ÐÓàã×Úö" + +#: engines/scumm/dialogs.cpp:201 +msgid "Game title)" +msgstr "½Ð×ÒÐ ÓãÛìÝö)" + +#. I18N: Previous page button +#: engines/scumm/dialogs.cpp:287 +msgid "~P~revious" +msgstr "~¿~Ðßïà" + +#. I18N: Next page button +#: engines/scumm/dialogs.cpp:289 +msgid "~N~ext" +msgstr "~½~Ðáâ" + +#: engines/scumm/dialogs.cpp:290 +#: backends/platform/ds/arm9/source/dsoptions.cpp:56 +msgid "~C~lose" +msgstr "~·~ÐÚàëæì" + +#: engines/scumm/dialogs.cpp:597 +msgid "Speech Only" +msgstr "ÂÞÛìÚö ÐÓãçÚÐ" + +#: engines/scumm/dialogs.cpp:598 +msgid "Speech and Subtitles" +msgstr "°ÓãçÚÐ ö áãÑâëâàë" + +#: engines/scumm/dialogs.cpp:599 +msgid "Subtitles Only" +msgstr "ÂÞÛìÚö áãÑâëâàë" + +#: engines/scumm/dialogs.cpp:607 +msgctxt "lowres" +msgid "Speech & Subs" +msgstr "°ÓãçÚÐ ö âíÚáâ" + +#: engines/scumm/dialogs.cpp:653 +msgid "Select a Proficiency Level." +msgstr "°ÑïàëæÕ þ×àÞÒÕÝì áÚÛÐÔÐÝÐáæö." + +#: engines/scumm/dialogs.cpp:655 +msgid "Refer to your Loom(TM) manual for help." +msgstr "·Ð ÔÐßÐÜÞÓÐÙ ×ÒïàÝöæÕáï ÔÐ öÝáâàãÚæëö Loom(TM)." + +#: engines/scumm/dialogs.cpp:658 +msgid "Standard" +msgstr "ÁâÐÝÔÐàâÝë" + +#: engines/scumm/dialogs.cpp:659 +msgid "Practice" +msgstr "¿àÐÚâëÚÐÝâ" + +#: engines/scumm/dialogs.cpp:660 +msgid "Expert" +msgstr "ÍÚáßÕàâ" + +#: engines/scumm/help.cpp:73 +msgid "Common keyboard commands:" +msgstr "°ÓãÛìÝëï ÚÛÐÒöïâãàÝëï ÚÐÜÐÝÔë:" + +#: engines/scumm/help.cpp:74 +msgid "Save / Load dialog" +msgstr "´ëïÛÞÓ ×Ðßöáã / çëâÐÝÝï" + +#: engines/scumm/help.cpp:76 +msgid "Skip line of text" +msgstr "¿àÐßãáæöæì àÐÔÞÚ" + +#: engines/scumm/help.cpp:77 +msgid "Esc" +msgstr "Esc" + +#: engines/scumm/help.cpp:77 +msgid "Skip cutscene" +msgstr "¿àÐßãáæöæì ×ÐáâÐþÚã" + +#: engines/scumm/help.cpp:78 +msgid "Space" +msgstr "¿àÐÑÕÛ" + +#: engines/scumm/help.cpp:78 +msgid "Pause game" +msgstr "¿Ðþ×Ð ÓãÛìÝö" + +#: engines/scumm/help.cpp:79 engines/scumm/help.cpp:84 +#: engines/scumm/help.cpp:95 engines/scumm/help.cpp:96 +#: engines/scumm/help.cpp:97 engines/scumm/help.cpp:98 +#: engines/scumm/help.cpp:99 engines/scumm/help.cpp:100 +#: engines/scumm/help.cpp:101 engines/scumm/help.cpp:102 +msgid "Ctrl" +msgstr "Ctrl" + +#: engines/scumm/help.cpp:79 +msgid "Load game state 1-10" +msgstr "·ÐÓàã×öæì ÓãÛìÝî 1-10" + +#: engines/scumm/help.cpp:80 engines/scumm/help.cpp:84 +#: engines/scumm/help.cpp:86 engines/scumm/help.cpp:100 +#: engines/scumm/help.cpp:101 engines/scumm/help.cpp:102 +msgid "Alt" +msgstr "Alt" + +#: engines/scumm/help.cpp:80 +msgid "Save game state 1-10" +msgstr "·ÐåÐÒÐæì ÓãÛìÝî 1-10" + +#: engines/scumm/help.cpp:86 engines/scumm/help.cpp:89 +msgid "Enter" +msgstr "ÃÒÞÔ" + +#: engines/scumm/help.cpp:86 +msgid "Toggle fullscreen" +msgstr "¿ÕàÐÚÛîçëæì ÝÐ þÒÕáì íÚàÐÝ" + +#: engines/scumm/help.cpp:87 +msgid "Music volume up / down" +msgstr "³ãçÝÐáæì Üã×ëÚö ßÐÒïÛöçëæì / ßÐÜÕÝèëæì" + +#: engines/scumm/help.cpp:88 +msgid "Text speed slower / faster" +msgstr "ÅãâÚÐáæì âíÚáâã åãâçíÙ / ßÐÒÞÛìÝÕÙ" + +#: engines/scumm/help.cpp:89 +msgid "Simulate left mouse button" +msgstr "ÍÜãÛïæëï ÛÕÒÐÙ ÚÛÐÒöèë Üëèë" + +#: engines/scumm/help.cpp:90 +msgid "Tab" +msgstr "Tab" + +#: engines/scumm/help.cpp:90 +msgid "Simulate right mouse button" +msgstr "ÍÜãÛïæëï ßàÐÒÐÙ ÚÛÐÒöèë Üëèë" + +#: engines/scumm/help.cpp:93 +msgid "Special keyboard commands:" +msgstr "ÁßÕæëïÛìÝëï ÚÛÐÒöïâãàÝëï ÚÐÜÐÝÔë:" + +#: engines/scumm/help.cpp:94 +msgid "Show / Hide console" +msgstr "¿ÐÚÐ×Ðæì / ¿àëÑàÐæì ÚÐÝáÞÛì" + +#: engines/scumm/help.cpp:95 +msgid "Start the debugger" +msgstr "·ÐßãáÚ ÐÔÛÐÔçëÚÐ" + +#: engines/scumm/help.cpp:96 +msgid "Show memory consumption" +msgstr "¿ÐÚÐ×Ðæì áßÐÖëÒÐÝÝÕ ßÐÜïæö" + +#: engines/scumm/help.cpp:97 +msgid "Run in fast mode (*)" +msgstr "·Ðßãáæöæì åãâÚö àíÖëÜ (*)" + +#: engines/scumm/help.cpp:98 +msgid "Run in really fast mode (*)" +msgstr "·Ðßãáæöæì ÒÕÛìÜö åãâÚö àíÖëÜ (*)" + +#: engines/scumm/help.cpp:99 +msgid "Toggle mouse capture" +msgstr "¿ÕàÐÚÛîçíÝÝÕ ßÕàÐåÞßã Üëèë" + +#: engines/scumm/help.cpp:100 +msgid "Switch between graphics filters" +msgstr "¿ÕàÐÚÛîçíÝÝÕ ßÐÜöÖ ÓàÐäöçÝëÜö äöÛìâàÐÜö" + +#: engines/scumm/help.cpp:101 +msgid "Increase / Decrease scale factor" +msgstr "¿ÐÒïÛöçëæì/ßÐÜÕÝèëæì ÜÐèâÐÑ" + +#: engines/scumm/help.cpp:102 +msgid "Toggle aspect-ratio correction" +msgstr "¿ÕàÐÚÛîçíÝÝÕ ÚÐàíÚæëö áãÐÔÝÞáöÝ ÑÐÚÞþ" + +#: engines/scumm/help.cpp:107 +msgid "* Note that using ctrl-f and" +msgstr "* ²ëÚÐàëáâÐÝÝÕ ctrl-f ö" + +#: engines/scumm/help.cpp:108 +msgid " ctrl-g are not recommended" +msgstr " ctrl-g ÝÕ àíÚÐÜÕÝÔãÕææÐ," + +#: engines/scumm/help.cpp:109 +msgid " since they may cause crashes" +msgstr " ÑÞ ïÝë ÜÞÓãæì ßàëÒÕáæö ÔÐ" + +#: engines/scumm/help.cpp:110 +msgid " or incorrect game behavior." +msgstr " ÝïßàÐÒöÛìÝÐÙ àÐÑÞâë ÓãÛìÝö." + +#: engines/scumm/help.cpp:114 +msgid "Spinning drafts on the keyboard:" +msgstr "·ÜïÝïÝëï çÐàÝÐÒöÚö ÝÐ ÚÛÐÒöïâãàë:" + +#: engines/scumm/help.cpp:116 +msgid "Main game controls:" +msgstr "°áÝÞþÝÐÕ ÚöàÐÒÐÝÝÕ ÓãÛìÝñÙ:" + +#: engines/scumm/help.cpp:121 engines/scumm/help.cpp:136 +#: engines/scumm/help.cpp:161 +msgid "Push" +msgstr "¿åÐæì" + +#: engines/scumm/help.cpp:122 engines/scumm/help.cpp:137 +#: engines/scumm/help.cpp:162 +msgid "Pull" +msgstr "ÆïÓÝãæì" + +#: engines/scumm/help.cpp:123 engines/scumm/help.cpp:138 +#: engines/scumm/help.cpp:163 engines/scumm/help.cpp:197 +#: engines/scumm/help.cpp:207 +msgid "Give" +msgstr "´Ðæì" + +#: engines/scumm/help.cpp:124 engines/scumm/help.cpp:139 +#: engines/scumm/help.cpp:164 engines/scumm/help.cpp:190 +#: engines/scumm/help.cpp:208 +msgid "Open" +msgstr "°ÔÚàëæì" + +#: engines/scumm/help.cpp:126 +msgid "Go to" +msgstr "¦áæö" + +#: engines/scumm/help.cpp:127 +msgid "Get" +msgstr "Ã×ïæì" + +#: engines/scumm/help.cpp:128 engines/scumm/help.cpp:152 +#: engines/scumm/help.cpp:170 engines/scumm/help.cpp:198 +#: engines/scumm/help.cpp:213 engines/scumm/help.cpp:224 +#: engines/scumm/help.cpp:250 +msgid "Use" +msgstr "²ëÚÐàëáâÐæì" + +#: engines/scumm/help.cpp:129 engines/scumm/help.cpp:141 +msgid "Read" +msgstr "ÇëâÐæì" + +#: engines/scumm/help.cpp:130 engines/scumm/help.cpp:147 +msgid "New kid" +msgstr "½ÞÒë ßÕàá" + +#: engines/scumm/help.cpp:131 engines/scumm/help.cpp:153 +#: engines/scumm/help.cpp:171 +msgid "Turn on" +msgstr "ÃÚÛîçëæì" + +#: engines/scumm/help.cpp:132 engines/scumm/help.cpp:154 +#: engines/scumm/help.cpp:172 +msgid "Turn off" +msgstr "²ëÚÛîçëæì" + +#: engines/scumm/help.cpp:142 engines/scumm/help.cpp:167 +#: engines/scumm/help.cpp:194 +msgid "Walk to" +msgstr "¦áæö ÔÐ" + +#: engines/scumm/help.cpp:143 engines/scumm/help.cpp:168 +#: engines/scumm/help.cpp:195 engines/scumm/help.cpp:210 +#: engines/scumm/help.cpp:227 +msgid "Pick up" +msgstr "¿ÐÔÝïæì" + +#: engines/scumm/help.cpp:144 engines/scumm/help.cpp:169 +msgid "What is" +msgstr "ÈâÞ âÐÚÞÕ" + +#: engines/scumm/help.cpp:146 +msgid "Unlock" +msgstr "°ÔÚàëæì" + +#: engines/scumm/help.cpp:149 +msgid "Put on" +msgstr "¿ÐÚÛÐáæö" + +#: engines/scumm/help.cpp:150 +msgid "Take off" +msgstr "¿ÐÔÝïæì" + +#: engines/scumm/help.cpp:156 +msgid "Fix" +msgstr "²ëßàÐÒöæì" + +#: engines/scumm/help.cpp:158 +msgid "Switch" +msgstr "¿ÕàÐÚÛîçëæì" + +#: engines/scumm/help.cpp:166 engines/scumm/help.cpp:228 +msgid "Look" +msgstr "³ÛïÔ×Õæì" + +#: engines/scumm/help.cpp:173 engines/scumm/help.cpp:223 +msgid "Talk" +msgstr "³ÐÒÐàëæì" + +#: engines/scumm/help.cpp:174 +msgid "Travel" +msgstr "¿ÐÔÐàÞÖÝöçÐæì" + +#: engines/scumm/help.cpp:175 +msgid "To Henry / To Indy" +msgstr "³ÕÝàë/¦ÝÔë" + +#. I18N: These are different musical notes +#: engines/scumm/help.cpp:179 +msgid "play C minor on distaff" +msgstr "öÓàÐæì ÔÞ ÜöÝÞà ÝÐ ÚÐÛÐþàÞæÕ" + +#: engines/scumm/help.cpp:180 +msgid "play D on distaff" +msgstr "öÓàÐæì àí ÝÐ ÚÐÛÐþàÞæÕ" + +#: engines/scumm/help.cpp:181 +msgid "play E on distaff" +msgstr "öÓàÐæì Üö ÝÐ ÚÐÛÐþàÞæÕ" + +#: engines/scumm/help.cpp:182 +msgid "play F on distaff" +msgstr "öÓàÐæì äÐ ÝÐ ÚÐÛÐþàÞæÕ" + +#: engines/scumm/help.cpp:183 +msgid "play G on distaff" +msgstr "öÓàÐæì áÞÛì ÝÐ ÚÐÛÐþàÞæÕ" + +#: engines/scumm/help.cpp:184 +msgid "play A on distaff" +msgstr "öÓàÐæì Ûï ÝÐ ÚÐÛÐþàÞæÕ" + +#: engines/scumm/help.cpp:185 +msgid "play B on distaff" +msgstr "öÓàÐæì áö ÝÐ ÚÐÛÐþàÞæÕ" + +#: engines/scumm/help.cpp:186 +msgid "play C major on distaff" +msgstr "öÓàÐæì ÔÞ ÜÐÖÞà ÝÐ ÚÐÛÐþàÞæÕ" + +#: engines/scumm/help.cpp:192 engines/scumm/help.cpp:214 +msgid "puSh" +msgstr "ßåÐæì" + +#: engines/scumm/help.cpp:193 engines/scumm/help.cpp:215 +msgid "pull (Yank)" +msgstr "æïÓÝãæì (çÐßÛïæì)" + +#: engines/scumm/help.cpp:196 engines/scumm/help.cpp:212 +#: engines/scumm/help.cpp:248 +msgid "Talk to" +msgstr "³ÐÒÐàëæì ×" + +#: engines/scumm/help.cpp:199 engines/scumm/help.cpp:211 +msgid "Look at" +msgstr "³ÛïÔ×Õæì ÝÐ" + +#: engines/scumm/help.cpp:200 +msgid "turn oN" +msgstr "ãÚÛîçëæì" + +#: engines/scumm/help.cpp:201 +msgid "turn oFf" +msgstr "ÒëÚÛîçëæì" + +#: engines/scumm/help.cpp:217 +msgid "KeyUp" +msgstr "ÃÒÕàå" + +#: engines/scumm/help.cpp:217 +msgid "Highlight prev dialogue" +msgstr "¿ÐÔáÒïâÛöæì ßÐßïàíÔÝö ÔëïÛÞÓ" + +#: engines/scumm/help.cpp:218 +msgid "KeyDown" +msgstr "ÃÝö×" + +#: engines/scumm/help.cpp:218 +msgid "Highlight next dialogue" +msgstr "¿ÐÔáÒïâÛöæì ÝÐáâãßÝë ÔëïÛÞÓ" + +#: engines/scumm/help.cpp:222 +msgid "Walk" +msgstr "¦áæö" + +#: engines/scumm/help.cpp:225 engines/scumm/help.cpp:234 +#: engines/scumm/help.cpp:241 engines/scumm/help.cpp:249 +msgid "Inventory" +msgstr "¦ÝÒÕÝâÐà" + +#: engines/scumm/help.cpp:226 +msgid "Object" +msgstr "°Ñ'ÕÚâ" + +#: engines/scumm/help.cpp:229 +msgid "Black and White / Color" +msgstr "ÇÞàÝÐ-ÑÕÛë / ºÐÛïàÞÒë" + +#: engines/scumm/help.cpp:232 +msgid "Eyes" +msgstr "²Þçë" + +#: engines/scumm/help.cpp:233 +msgid "Tongue" +msgstr "Ï×ëÚ" + +#: engines/scumm/help.cpp:235 +msgid "Punch" +msgstr "ÃÔÐà" + +#: engines/scumm/help.cpp:236 +msgid "Kick" +msgstr "½ÐÓÞÙ" + +#: engines/scumm/help.cpp:239 engines/scumm/help.cpp:247 +msgid "Examine" +msgstr "¿àÐÒÕàëæì" + +#: engines/scumm/help.cpp:240 +msgid "Regular cursor" +msgstr "·ÒëçÐÙÝë ÚãàáÞà" + +#. I18N: Comm is a communication device +#: engines/scumm/help.cpp:243 +msgid "Comm" +msgstr "ºÐÜ" + +#: engines/scumm/help.cpp:246 +msgid "Save / Load / Options" +msgstr "·ÐÓàã×öæì / ·ÐåÐÒÐæì / ½ÐÛÐÔë" + +#: engines/scumm/help.cpp:255 +msgid "Other game controls:" +msgstr "°áâÐâÝïÕ ÚöàÐÒÐÝÝÕ ÓãÛìÝñÙ:" + +#: engines/scumm/help.cpp:257 engines/scumm/help.cpp:267 +msgid "Inventory:" +msgstr "¦ÝÒÕÝâÐà:" + +#: engines/scumm/help.cpp:258 engines/scumm/help.cpp:274 +msgid "Scroll list up" +msgstr "¿àÐÚàãæöæì áßöá ãÒÕàå" + +#: engines/scumm/help.cpp:259 engines/scumm/help.cpp:275 +msgid "Scroll list down" +msgstr "¿àÐÚàãæöæì áßöá ãÝö×" + +#: engines/scumm/help.cpp:260 engines/scumm/help.cpp:268 +msgid "Upper left item" +msgstr "²ÕàåÝö ÛÕÒë ßàÐÔÜÕâ" + +#: engines/scumm/help.cpp:261 engines/scumm/help.cpp:270 +msgid "Lower left item" +msgstr "½öÖÝö ÛÕÒë ßàÐÔÜÕâ" + +#: engines/scumm/help.cpp:262 engines/scumm/help.cpp:271 +msgid "Upper right item" +msgstr "²ÕàåÝö ßàÐÒë ßàÐÔÜÕâ" + +#: engines/scumm/help.cpp:263 engines/scumm/help.cpp:273 +msgid "Lower right item" +msgstr "½öÖÝö ßàÐÒë ßàÐÔÜÕâ" + +#: engines/scumm/help.cpp:269 +msgid "Middle left item" +msgstr "ÁïàíÔÝö ÛÕÒë ßàÐÔÜÕâ" + +#: engines/scumm/help.cpp:272 +msgid "Middle right item" +msgstr "ÁïàíÔÝö ßàÐÒë ßàÐÔÜÕâ" + +#: engines/scumm/help.cpp:279 engines/scumm/help.cpp:284 +msgid "Switching characters:" +msgstr "·ÜÕÝÐ ÓÕàÞï:" + +#: engines/scumm/help.cpp:281 +msgid "Second kid" +msgstr "´àãÓö ÓÕàÞÙ" + +#: engines/scumm/help.cpp:282 +msgid "Third kid" +msgstr "Âàíæö ÓÕàÞÙ" + +#: engines/scumm/help.cpp:294 +msgid "Fighting controls (numpad):" +msgstr "ºöàÐÒÐÝÝÕ ÑÞÕÜ (ÛöçÑÐÒëï ÚÛÐÒöèë)" + +#: engines/scumm/help.cpp:295 engines/scumm/help.cpp:296 +#: engines/scumm/help.cpp:297 +msgid "Step back" +msgstr "ºàÞÚ ÝÐ×ÐÔ" + +#: engines/scumm/help.cpp:298 +msgid "Block high" +msgstr "°ÑÐàÞÝÐ ×ÒÕàåã" + +#: engines/scumm/help.cpp:299 +msgid "Block middle" +msgstr "°ÑÐàÞÝÐ ßÐáïàíÔ×öÝÕ" + +#: engines/scumm/help.cpp:300 +msgid "Block low" +msgstr "°ÑÐàÞÝÐ ×Ýö×ã" + +#: engines/scumm/help.cpp:301 +msgid "Punch high" +msgstr "ÃÔÐà ×ÒÕàåã" + +#: engines/scumm/help.cpp:302 +msgid "Punch middle" +msgstr "ÃÔÐà ßÐáïàíÔ×öÝÕ" + +#: engines/scumm/help.cpp:303 +msgid "Punch low" +msgstr "ÃÔÐà ×Ýö×ã" + +#: engines/scumm/help.cpp:306 +msgid "These are for Indy on left." +msgstr "³íâÐ ÚÐÛö ¦ÝÔë ×ÛÕÒÐ." + +#: engines/scumm/help.cpp:307 +msgid "When Indy is on the right," +msgstr "ºÐÛö ¦ÝÔë áßàÐÒÐ," + +#: engines/scumm/help.cpp:308 +msgid "7, 4, and 1 are switched with" +msgstr "7, 4 ö 1 ×ÜïÝïîææÐ ×" + +#: engines/scumm/help.cpp:309 +msgid "9, 6, and 3, respectively." +msgstr "9, 6 ö 3 ÐÔßÐÒÕÔÝÐ." + +#: engines/scumm/help.cpp:316 +msgid "Biplane controls (numpad):" +msgstr "ºöàÐÒÐÝÝÕ áÐÜÐÛñâÐÜ (ÛöçÑÐÒëï ÚÛÐÒöèë)" + +#: engines/scumm/help.cpp:317 +msgid "Fly to upper left" +msgstr "»ïæÕæì ÝÐÛÕÒÐ-þÒÕàå" + +#: engines/scumm/help.cpp:318 +msgid "Fly to left" +msgstr "»ïæÕæì ÝÐÛÕÒÐ" + +#: engines/scumm/help.cpp:319 +msgid "Fly to lower left" +msgstr "»ïæÕæì ÝÐÛÕÒÐ-þÝö×" + +#: engines/scumm/help.cpp:320 +msgid "Fly upwards" +msgstr "»ïæÕæì ãÒÕàå" + +#: engines/scumm/help.cpp:321 +msgid "Fly straight" +msgstr "»ïæÕæì ßàÐÜÐ" + +#: engines/scumm/help.cpp:322 +msgid "Fly down" +msgstr "»ïæÕæì ãÝö×" + +#: engines/scumm/help.cpp:323 +msgid "Fly to upper right" +msgstr "»ïæÕæì ÝÐßàÐÒÐ-þÒÕàå" + +#: engines/scumm/help.cpp:324 +msgid "Fly to right" +msgstr "»ïæÕæì ÝÐßàÐÒÐ" + +#: engines/scumm/help.cpp:325 +msgid "Fly to lower right" +msgstr "»ïæÕæì ÝÐßàÐÒÐ-þÝö×" + +#: engines/scumm/scumm.cpp:1773 +#, c-format +msgid "" +"Native MIDI support requires the Roland Upgrade from LucasArts,\n" +"but %s is missing. Using AdLib instead." +msgstr "" +"ÀíÖëÜ \"àÞÔÝÐÓÐ\" MIDI ßÐâàÐÑãÕ ÐÑÝÐþÛÕÝÝÕ Roland Upgrade ÐÔ\n" +"LucasArts, ÐÛÕ ÝÕ åÐßÐÕ %s. ¿ÕàÐÚÛîçÐîáï ÝÐ AdLib." + +#: engines/scumm/scumm.cpp:2278 engines/agos/saveload.cpp:220 +#, c-format +msgid "" +"Failed to save game state to file:\n" +"\n" +"%s" +msgstr "" +"½Õ ÐâàëÜÐÛÐáï ×ÐßöáÐæì ÓãÛìÝî þ äÐÙÛ:\n" +"\n" +"%s" + +#: engines/scumm/scumm.cpp:2285 engines/agos/saveload.cpp:185 +#, c-format +msgid "" +"Failed to load game state from file:\n" +"\n" +"%s" +msgstr "" +"½Õ ÐâàëÜÐÛÐáï ×ÐÓàã×öæì ÓãÛìÝî × äÐÙÛÐ:\n" +"\n" +"%s" + +#: engines/scumm/scumm.cpp:2297 engines/agos/saveload.cpp:228 +#, c-format +msgid "" +"Successfully saved game state in file:\n" +"\n" +"%s" +msgstr "" +"³ãÛìÝï ßÐáßïåÞÒÐ ×ÐåÐÒÐÝÐ þ äÐÙÛ:\n" +"\n" +"%s" + +#: engines/scumm/scumm.cpp:2512 +msgid "" +"Usually, Maniac Mansion would start now. But ScummVM doesn't do that yet. To " +"play it, go to 'Add Game' in the ScummVM start menu and select the 'Maniac' " +"directory inside the Tentacle game directory." +msgstr "" +"·ÐàÐ× ßÐÒöÝÝÐ ×ÐßãáæöææÐ ÓãÛìÝï Maniac Mansion. °ÛÕ ScummVM ßÐÚãÛì ÓíâÐÓÐ ÝÕ " +"þÜÕÕ. ºÐÑ ×ÓãÛïæì, ÝÐæöáÝöæÕ '½ÞÒÐï ÓãÛìÝï' ã áâÐàâÐÒëÜ ÜÕÝî ScummVM, Ð " +"×ÐâëÜ ÐÑïàëæÕ ÔëàíÚâÞàëî Maniac ã ÔëàíÚâÞàëö × ÓãÛìÝñÙ Tentacle." + +#. I18N: Option for fast scene switching +#: engines/mohawk/dialogs.cpp:92 engines/mohawk/dialogs.cpp:171 +msgid "~Z~ip Mode Activated" +msgstr "ÀíÖëÜ åãâÚÐÓÐ ßÕàÐåÞÔã ÐÚâëÒÐÒÐÝë" + +#: engines/mohawk/dialogs.cpp:93 +msgid "~T~ransitions Enabled" +msgstr "¿ÕàÐåÞÔë ÐÚâëÒÐÒÐÝë" + +#. I18N: Drop book page +#: engines/mohawk/dialogs.cpp:95 +msgid "~D~rop Page" +msgstr "²ëÚöÝãæì áâÐàÞÝÚã" + +#: engines/mohawk/dialogs.cpp:99 +msgid "~S~how Map" +msgstr "¿ÞÚÐ×Ðâì ÚÐàâã" + +#: engines/mohawk/dialogs.cpp:105 +msgid "~M~ain Menu" +msgstr "³ÐÛÞþÝÐÕ ÜÕÝî" + +#: engines/mohawk/dialogs.cpp:172 +msgid "~W~ater Effect Enabled" +msgstr "ÍäÕÚâë ÒÐÔë þÚÛîçÐÝë" + +#: engines/agos/animation.cpp:560 +#, c-format +msgid "Cutscene file '%s' not found!" +msgstr "ÄÐÙÛ ×ÐáâÐþÚö '%s' ÝÕ ×ÝÞÙÔ×ÕÝë!" + +#: engines/gob/inter_playtoons.cpp:256 engines/gob/inter_v2.cpp:1287 +#: engines/tinsel/saveload.cpp:532 +msgid "Failed to load game state from file." +msgstr "½Õ ÐâàëÜÐÛÐáï ×ÐÓàã×öæì ×ÐåÐÒÐÝãî ÓãÛìÝî × äÐÙÛÐ." + +#: engines/gob/inter_v2.cpp:1357 engines/tinsel/saveload.cpp:545 +msgid "Failed to save game state to file." +msgstr "½Õ ÐâàëÜÐÛÐáï ×ÐåÐÒÐæì ÓãÛìÝî þ äÐÙÛ." + +#: engines/gob/inter_v5.cpp:107 +msgid "Failed to delete file." +msgstr "½Õ ÐâàëÜÐÛÐáï ÒëÔÐÛöæì äÐÙÛ." + +#: engines/groovie/script.cpp:420 +msgid "Failed to save game" +msgstr "½Õ ÐâàëÜÐÛÐáï ×ÐåÐÒÐæì ÓãÛìÝî" + +#. I18N: Studio audience adds an applause and cheering sounds whenever +#. Malcolm makes a joke. +#: engines/kyra/detection.cpp:62 +msgid "Studio audience" +msgstr "ÁâãÔëÙÝÐï ÐþÔëâÞàëï" + +#: engines/kyra/detection.cpp:63 +msgid "Enable studio audience" +msgstr "ÃÚÛîçëæì ÓãÚö ÐþÔëâÞàëö þ áâãÔëö" + +#. I18N: This option allows the user to skip text and cutscenes. +#: engines/kyra/detection.cpp:73 +msgid "Skip support" +msgstr "¿ÐÔâàëÜÚÐ ßàÞßãáÚÐþ" + +#: engines/kyra/detection.cpp:74 +msgid "Allow text and cutscenes to be skipped" +msgstr "ÃÚÛîçÐÕ ÜÐÓçëÜÐáæì ßàÐßãáÚÐæì âíÚáâë ö ×ÐáâÐþÚö" + +#. I18N: Helium mode makes people sound like they've inhaled Helium. +#: engines/kyra/detection.cpp:84 +msgid "Helium mode" +msgstr "ÀíÖëÜ ÓÕÛöï" + +#: engines/kyra/detection.cpp:85 +msgid "Enable helium mode" +msgstr "ÃÚÛîçëæì àíÖëÜ ÓÕÛöï" + +#. I18N: When enabled, this option makes scrolling smoother when +#. changing from one screen to another. +#: engines/kyra/detection.cpp:99 +msgid "Smooth scrolling" +msgstr "¿ÛëþÝÐï ßàÐÓÞàâÚÐ" + +#: engines/kyra/detection.cpp:100 +msgid "Enable smooth scrolling when walking" +msgstr "ÃÚÛîçëæì ßÛëþÝãî ßàÐÓÞàâÚã ßÐÔçÐá åÐÔë" + +#. I18N: When enabled, this option changes the cursor when it floats to the +#. edge of the screen to a directional arrow. The player can then click to +#. walk towards that direction. +#: engines/kyra/detection.cpp:112 +msgid "Floating cursors" +msgstr "¿ÛëÒãçëï ÚãàáÞàë" + +#: engines/kyra/detection.cpp:113 +msgid "Enable floating cursors" +msgstr "ÃÚÛîçëæì ßÛëÒãçëï ÚãàáÞàë" + +#. I18N: HP stands for Hit Points +#: engines/kyra/detection.cpp:127 +msgid "HP bar graphs" +msgstr "¿ÐÛÞáÚö ×ÔÐàÞþï" + +#: engines/kyra/detection.cpp:128 +msgid "Enable hit point bar graphs" +msgstr "ÃÚÛîçëæì ÐÔÛîáâàÐÒÐÝÝÕ ßÐÛÞáÐÚ ×ÔÐàÞþï" + +#: engines/kyra/lol.cpp:478 +msgid "Attack 1" +msgstr "°âÐÚÐ 1" + +#: engines/kyra/lol.cpp:479 +msgid "Attack 2" +msgstr "°âÐÚÐ 2" + +#: engines/kyra/lol.cpp:480 +msgid "Attack 3" +msgstr "°âÐÚÐ 3" + +#: engines/kyra/lol.cpp:481 +msgid "Move Forward" +msgstr "¦áæö ÝÐßÕàÐÔ" + +#: engines/kyra/lol.cpp:482 +msgid "Move Back" +msgstr "¦áæö ÝÐ×ÐÔ" + +#: engines/kyra/lol.cpp:483 +msgid "Slide Left" +msgstr "ÁÛö×ÓÐæì ÝÐÛÕÒÐ" + +#: engines/kyra/lol.cpp:484 +msgid "Slide Right" +msgstr "ÁÛö×ÓÐæì ÝÐßàÐÒÐ" + +#: engines/kyra/lol.cpp:485 +msgid "Turn Left" +msgstr "¿ÐÒÐàÞâ ÝÐÛÕÒÐ" + +#: engines/kyra/lol.cpp:486 +msgid "Turn Right" +msgstr "¿ÐÒÐàÞâ ÝÐßàÐÒÐ" + +#: engines/kyra/lol.cpp:487 +msgid "Rest" +msgstr "°ÔßÐçëæì" + +#: engines/kyra/lol.cpp:488 +msgid "Options" +msgstr "¾ßæëö" + +#: engines/kyra/lol.cpp:489 +msgid "Choose Spell" +msgstr "°ÑàÐæì ×ÐÓÐÒÞà" + +#: engines/kyra/sound_midi.cpp:475 +msgid "" +"You appear to be using a General MIDI device,\n" +"but your game only supports Roland MT32 MIDI.\n" +"We try to map the Roland MT32 instruments to\n" +"General MIDI ones. It is still possible that\n" +"some tracks sound incorrect." +msgstr "" +"·ÔÐÕææÐ, Òë áßàÐÑãÕæÕ ÒëÚÐàëáâÞþÒÐæì ßàëÛÐÔã\n" +"General MIDI, ÐÛÕ ÓíâÐï ÓãÛìÝï ßÐÔâàëÜÛöÒÐÕ âÞÛìÚö\n" +"Roland MT32 MIDI. ¼ë ßÐáßàÐÑãÕÜ ßÐÔÐÑàÐæì General\n" +"MIDI ßàëÛÐÔë, ßÐÔÞÑÝëï ÝÐ Roland MT32, ÐÛÕ\n" +"ÜÞÖÐ âÐÚ ÐâàëÜÐææÐ, èâÞ ÝÕÚÐâÞàëï âàíÚö ÑãÔãæì\n" +"áëÓàÐÝë ÝïßàÐÒöÛìÝÐ." + +#: engines/queen/queen.cpp:59 +msgid "Alternative intro" +msgstr "°ÛìâíàÝÐâëþÝë þáâãß" + +#: engines/queen/queen.cpp:60 +msgid "Use an alternative game intro (CD version only)" +msgstr "²ëÚÐàëáâÞþÒÐæì ÐÛìâíàÝÐâëþÝë þáâãß (âÞÛìÚö ÔÛï CD ÒÕàáöö ÓãÛìÝö)" + +#: engines/sky/compact.cpp:130 +msgid "" +"Unable to find \"sky.cpt\" file!\n" +"Please download it from www.scummvm.org" +msgstr "" +"°ÔáãâÝöçÐÕ äÐÙÛ sky.cpt!\n" +"ºÐÛö ÛÐáÚÐ, ×ÐßÐÜßãÙæÕ ïÓÞ × www.scummvm.org" + +#: engines/sky/compact.cpp:141 +msgid "" +"The \"sky.cpt\" file has an incorrect size.\n" +"Please (re)download it from www.scummvm.org" +msgstr "" +"ÄÐÙÛ sky.cpt ÜÐÕ ÝïßàÐÒöÛìÝë ßÐÜÕà.\n" +"ºÐÛö ÛÐáÚÐ, ×ÐßÐÜßãÙæÕ ïÓÞ ÝÐÝÞÒÐ × www.scummvm.org" + +#: engines/sky/detection.cpp:44 +msgid "Floppy intro" +msgstr "Ãáâãß × ÔëáÚÕâ" + +#: engines/sky/detection.cpp:45 +msgid "Use the floppy version's intro (CD version only)" +msgstr "²ëÚÐàëáâÞþÒÐæì ãáâãß × ÓÝãâÚöå ÔëáÚÐþ (âÞÛìÚö ÔÛï CD ÒÕàáöö ÓãÛìÝö)" + +#: engines/sword1/animation.cpp:539 +#, c-format +msgid "PSX stream cutscene '%s' cannot be played in paletted mode" +msgstr "·ÐáâÐþÚÐ PSX '%s' ÝÕ ÜÞÖÐ Ñëæì ßàÐÙÓàÐÝÐ þ àíÖëÜÕ × ßÐÛöâàÐÙ" + +#: engines/sword1/animation.cpp:560 engines/sword2/animation.cpp:455 +msgid "DXA cutscenes found but ScummVM has been built without zlib support" +msgstr "" +"·ÝÞÙÔ×ÕÝë ×ÐáâÐþÚö þ äÐàÜÐæÕ DXA, ÐÛÕ ScummVM Ñëþ áÐÑàÐÝë ÑÕ× ßÐÔâàëÜÚö zlib" + +#: engines/sword1/animation.cpp:570 engines/sword2/animation.cpp:465 +msgid "MPEG2 cutscenes are no longer supported" +msgstr "·ÐáâÐþÚö þ äÐàÜÐæÕ MPEG2 ÑÞÛìè ÝÕ ßÐÔâàëÜÛöÒÐîææÐ" + +#: engines/sword1/animation.cpp:576 engines/sword2/animation.cpp:473 +#, c-format +msgid "Cutscene '%s' not found" +msgstr "·ÐáâÐþÚÐ '%s' ÝÕ ×ÝÞÙÔ×ÕÝÐ" + +#: engines/sword1/control.cpp:863 +msgid "" +"ScummVM found that you have old savefiles for Broken Sword 1 that should be " +"converted.\n" +"The old save game format is no longer supported, so you will not be able to " +"load your games if you don't convert them.\n" +"\n" +"Press OK to convert them now, otherwise you will be asked again the next " +"time you start the game.\n" +msgstr "" +"ScummVM ÒëïÒöþ ã ÒÐá ×ÐåÐÒÐÝÝö ÓãÛìÝö Broken Sword 1 ã áâÐàëÜ äÐàÜÐæÕ.\n" +"ÁâÐàë äÐàÜÐâ ÑÞÛìè ÝÕ ßÐÔâàëÜÛöÒÐÕææÐ, ö, ÚÐÑ ×ÐÓàã×öæì ×ÐåÐÒÐÝÝö, ïÝë " +"ßÐÒöÝÝë Ñëæì ßÕàÐÒÕÔ×ÕÝë þ ÝÞÒë äÐàÜÐâ.\n" +"\n" +"½ÐæöáÝöæÕ ¾º, ÚÐÑ ßÕàÐÒÕáæö öå ã ÝÞÒë äÐàÜÐâ ×ÐàÐ×, ã ÐÔÒÐàÞâÝëÜ ÒëßÐÔÚã " +"ÓíâÐ ßÐÒÕÔÐÜÛÕÝÝÕ ×'ïÒöææÐ ×ÝÞþ ßàë ÝÐáâãßÝëÜ ×ÐßãáÚã ÓãÛìÝö.\n" + +#: engines/sword1/control.cpp:1232 +#, c-format +msgid "" +"Target new save game already exists!\n" +"Would you like to keep the old save game (%s) or the new one (%s)?\n" +msgstr "" +"·ÐåÐÒÐÝÝÕ ÓãÛìÝö × âÐÚöÜ öÜÕÜ ãÖÞ öáÝãÕ!\n" +"²ë ÖÐÔÐÕæÕ ßÐÚöÝãæì áâÐàãî ÝÐ×Òã (%s) æö ×àÐÑöæì ÝÞÒãî (%s)?\n" + +#: engines/sword1/control.cpp:1235 +msgid "Keep the old one" +msgstr "¿ÐÚöÝãæì áâÐàÞÕ" + +#: engines/sword1/control.cpp:1235 +msgid "Keep the new one" +msgstr "·àÐÑöæì ÝÞÒÐÕ" + +#: engines/sword1/logic.cpp:1633 +msgid "This is the end of the Broken Sword 1 Demo" +msgstr "³íâÐ ×ÐÒïàèíÝÝÕ ÔíÜÐ Broken Sword 1" + +#: engines/sword2/animation.cpp:435 +msgid "" +"PSX cutscenes found but ScummVM has been built without RGB color support" +msgstr "" +"·ÝÞÙÔ×ÕÝë ×ÐáâÐþÚö þ äÐàÜÐæÕ PSX, ÐÛÕ ScummVM Ñëþ áÐÑàÐÝë ÑÕ× ßÐÔâàëÜÚö RGB " +"ÚÞÛÕàÐþ" + +#: engines/sword2/sword2.cpp:79 +msgid "Show object labels" +msgstr "¿ÐÚÐ×ÒÐæì ÝÐ×Òë ÐÑ'ÕÚâÐþ" + +#: engines/sword2/sword2.cpp:80 +msgid "Show labels for objects on mouse hover" +msgstr "¿ÐÚÐ×ÒÐÕ ÝÐ×Òë ÐÑ'ÕÚâÐþ ßàë ÝÐÒïÔ×ÕÝÝö ÚãàáÞàÐ Üëèë" + +#: engines/teenagent/resources.cpp:68 +msgid "" +"You're missing the 'teenagent.dat' file. Get it from the ScummVM website" +msgstr "" +"à ÒÐá ÐÔáãâÝöçÐÕ äÐÙÛ 'teenagent.dat'. ·ÐßÐÜßãÙæÕ ïÓÞ × ÒíÑ-áÐÙâÐ ScummVM" + +#: engines/teenagent/resources.cpp:89 +msgid "" +"The teenagent.dat file is compressed and zlib hasn't been included in this " +"executable. Please decompress it" +msgstr "" +"ÄÐÙÛ teenagent.dat ×ÖÐâë, ÐÛÕ zlib ÝÕ þÚÛîçÐÝÐ þ Óíâã ßàÐÓàÐÜã. ºÐÛö ÛÐáÚÐ, " +"àÐáßÐÚãÙæÕ ïÓÞ" + +#: engines/parallaction/saveload.cpp:133 +#, c-format +msgid "" +"Can't save game in slot %i\n" +"\n" +msgstr "" +"½Õ ÜÐÓã ×ÐåÐÒÐæì ÓãÛìÝî þ ßÐ×öæëî %i\n" +"\n" + +#: engines/parallaction/saveload.cpp:204 +msgid "Loading game..." +msgstr "·ÐÓàãÖÐî ÓãÛìÝî..." + +#: engines/parallaction/saveload.cpp:219 +msgid "Saving game..." +msgstr "·ÐåÞþÒÐî ÓãÛìÝî..." + +#: engines/parallaction/saveload.cpp:272 +msgid "" +"ScummVM found that you have old savefiles for Nippon Safes that should be " +"renamed.\n" +"The old names are no longer supported, so you will not be able to load your " +"games if you don't convert them.\n" +"\n" +"Press OK to convert them now, otherwise you will be asked next time.\n" +msgstr "" +"ScummVM ×ÝÐÙèÞþ ã ÒÐá áâÐàëï ×ÐåÐÒÐÝÝö ÓãÛìÝö Nippon Safes, ïÚöï ÝÕÐÑåÞÔÝÐ " +"ßÕàÐÝÐ×ÒÐæì. ÁâÐàëï ÝÐ×Òë ÑÞÛìè ÝÕ ßÐÔâàëÜÛöÒÐîææÐ, ö âÐÜã Òë ÝÕ ×ÜÞÖÐæÕ " +"×ÐÓàã×öæì ×ÐåÐÒÐÝÝö, ÚÐÛö ÝÕ ßÕàÐÝÐ×ÐÒÕæÕ öå.\n" +"\n" +"½ÐæöáÝöæÕ ¾º, ÚÐÑ ßÕàÐÝÐ×ÒÐæì öå ×ÐàÐ×, ã ÐÔÒÐàÞâÝëÜ ÒëßÐÔÚã ÓíâÐ Ö " +"ßÐÒÕÔÐÜÛÕÝÝÕ ×'ïÒöææÐ ßàë ÝÐáâãßÝëÜ ×ÐßãáÚã ÓãÛìÝö.\n" + +#: engines/parallaction/saveload.cpp:319 +msgid "ScummVM successfully converted all your savefiles." +msgstr "ScummVM ßÐáßïåÞÒÐ ßÕàÐþâÒÐàëþ ãáÕ ÒÐèë ×ÐåÐÒÐÝÝö ÓãÛìÝïþ." + +#: engines/parallaction/saveload.cpp:321 +msgid "" +"ScummVM printed some warnings in your console window and can't guarantee all " +"your files have been converted.\n" +"\n" +"Please report to the team." +msgstr "" +"ScummVM ÝÐßöáÐþ ÝÕÚÐÛìÚö ßÐßïàíÔÖÐÝÝïþ ã ÐÚÝÞ ÚÐÝáÞÛö ö ÝÕ ×ÜÞÓ ßÕàÐþâÒÐàëæì " +"ãáÕ äÐÙÛë.\n" +"\n" +"ºÐÛö ÛÐáÚÐ, ßÐÒÕÔÐÜöæÕ ßàÐ ÓíâÐ ÚÐÜÐÝÔ×Õ ScummVM." + +#: audio/fmopl.cpp:49 +msgid "MAME OPL emulator" +msgstr "ÍÜãÛïâÐà MAME OPL" + +#: audio/fmopl.cpp:51 +msgid "DOSBox OPL emulator" +msgstr "ÍÜãÛïâÐà DOSBox OPL" + +#: audio/mididrv.cpp:209 +#, c-format +msgid "" +"The selected audio device '%s' was not found (e.g. might be turned off or " +"disconnected)." +msgstr "" +"°ÑàÐÝÐï ÓãÚÐÒÐï ßàëÛÐÔÐ '%s' ÝÕ ÑëÛÐ ×ÝÞÙÔ×ÕÝÐ (ÜÐÓçëÜÐ, ïÝÐ ÒëÚÛîçÐÝÐ æö ÝÕ " +"ßÐÔÚÛîçÐÝÐ)." + +#: audio/mididrv.cpp:209 audio/mididrv.cpp:221 audio/mididrv.cpp:257 +#: audio/mididrv.cpp:272 +msgid "Attempting to fall back to the next available device..." +msgstr "ÁßàÐÑãî ÒëÚÐàëáâÐæì öÝèãî ÔÐáâãßÝãî ßàëÛÐÔã..." + +#: audio/mididrv.cpp:221 +#, c-format +msgid "" +"The selected audio device '%s' cannot be used. See log file for more " +"information." +msgstr "" +"°ÑàÐÝÐï ÓãÚÐÒÐï ßàëÛÐÔÐ '%s' ÝÕ ÜÞÖÐ Ñëæì áÚÐàëáâÐÝÐ. ³ÛïÔ×öæÕ äÐÙÛ " +"ßàÐâÐÚÞÛã ÔÛï ÑÞÛìè ßÐÔàÐÑï×ÝÐÙ öÝäÐàÜÐæëö." + +#: audio/mididrv.cpp:257 +#, c-format +msgid "" +"The preferred audio device '%s' was not found (e.g. might be turned off or " +"disconnected)." +msgstr "" +"¿ÕàÐÒÐÖÝÐï ÓãÚÐÒÐï ßàëÛÐÔÐ '%s' ÝÕ ÑëÛÐ ×ÝÞÙÔ×ÕÝÐ (ÜÐÓçëÜÐ, ïÝÐ ÒëÚÛîçÐÝÐ æö " +"ÝÕ ßÐÔÚÛîçÐÝÐ)." + +#: audio/mididrv.cpp:272 +#, c-format +msgid "" +"The preferred audio device '%s' cannot be used. See log file for more " +"information." +msgstr "" +"¿ÕàÐÒÐÖÝÐï ÓãÚÐÒÐï ßàëÛÐÔÐ '%s' ÝÕ ÜÞÖÐ Ñëæì áÚÐàëáâÐÝÐ. ³ÛïÔ×öæÕ äÐÙÛ " +"ßàÐâÐÚÞÛã ÔÛï ÑÞÛìè ßÐÔàÐÑï×ÝÐÙ öÝäÐàÜÐæëö." + +#: audio/null.h:43 +msgid "No music" +msgstr "±Õ× Üã×ëÚö" + +#: audio/mods/paula.cpp:189 +msgid "Amiga Audio Emulator" +msgstr "ÍÜãÛïâÐà ÓãÚã Amiga" + +#: audio/softsynth/adlib.cpp:1593 +msgid "AdLib Emulator" +msgstr "ÍÜãÛïâÐà AdLib" + +#: audio/softsynth/appleiigs.cpp:33 +msgid "Apple II GS Emulator (NOT IMPLEMENTED)" +msgstr "ÍÜãÛïâÐà Apple II GS (ÐÔáãâÝöçÐÕ)" + +#: audio/softsynth/sid.cpp:1430 +msgid "C64 Audio Emulator" +msgstr "ÍÜãÛïâÐà ÓãÚã C64" + +#: audio/softsynth/mt32.cpp:293 +msgid "Initializing MT-32 Emulator" +msgstr "½ÐÛÐÔÖÒÐî íÜãÛïâÐà MT-32" + +#: audio/softsynth/mt32.cpp:512 +msgid "MT-32 Emulator" +msgstr "ÍÜãÛïâÐà MT-32" + +#: audio/softsynth/pcspk.cpp:139 +msgid "PC Speaker Emulator" +msgstr "ÍÜãÛïâÐà PC áßöÚÕàÐ" + +#: audio/softsynth/pcspk.cpp:158 +msgid "IBM PCjr Emulator" +msgstr "ÍÜãÛïâÐà IBM PCjr" + +#: backends/keymapper/remap-dialog.cpp:47 +msgid "Keymap:" +msgstr "ÂÐÑÛöæÐ ÚÛÐÒöè:" + +#: backends/keymapper/remap-dialog.cpp:66 +msgid " (Effective)" +msgstr " (´×ÕÙáÝÐï)" + +#: backends/keymapper/remap-dialog.cpp:106 +msgid " (Active)" +msgstr " (°ÚâëþÝÐï)" + +#: backends/keymapper/remap-dialog.cpp:106 +msgid " (Blocked)" +msgstr " (·ÐÑÛÐÚÐÒÐÝÐ)" + +#: backends/keymapper/remap-dialog.cpp:119 +msgid " (Global)" +msgstr " (³ÛÐÑÐÛìÝÐï)" + +#: backends/keymapper/remap-dialog.cpp:127 +msgid " (Game)" +msgstr " (³ãÛìÝö)" + +#: backends/midi/windows.cpp:164 +msgid "Windows MIDI" +msgstr "Windows MIDI" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:57 +msgid "ScummVM Main Menu" +msgstr "³ÐÛÞþÝÐÕ ÜÕÝî ScummVM" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:63 +msgid "~L~eft handed mode" +msgstr "»ÕÒÐàãÚö àíÖëÜ" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:64 +msgid "~I~ndy fight controls" +msgstr "ºöàÐÒÐÝÝÕ ÑÐïÜö þ Indy" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:65 +msgid "Show mouse cursor" +msgstr "¿ÐÚÐ×ÒÐæì ÚãàáÞà Üëèë" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:66 +msgid "Snap to edges" +msgstr "¿àëÜÐæÐÒÐæì ÔÐ ÜÕÖ" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:68 +msgid "Touch X Offset" +msgstr "·àãèíÝÝÕ ÔÞâëÚÐþ ßÐ ÒÞáö X" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:75 +msgid "Touch Y Offset" +msgstr "·àãèíÝÝÕ ÔÞâëÚÐþ ßÐ ÒÞáö Y" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:87 +msgid "Use laptop trackpad-style cursor control" +msgstr "²ëÚÐàëáâÞþÒÐæì ÚöàÐÒÐÝÝÕ ÚãàáÞàÐÜ ïÚ ÝÐ âàíÚßÐÔ×Õ ÛíßâÞßÐþ" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:88 +msgid "Tap for left click, double tap right click" +msgstr "ÂÐß ÔÛï ÛÕÒÐÙ ßáâàëçÚö, ßÐÔÒÞÙÝë âÐß ÔÛï ßàÐÒÐÙ ßáâàëçÚö" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:90 +msgid "Sensitivity" +msgstr "°ÔçãÒÐÛìÝÐáæì" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:99 +msgid "Initial top screen scale:" +msgstr "¿ÐçÐâÚÞÒë ÜÐèâÐÑ ÒÕàåÝïÓÐ íÚàÐÝÐ:" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:105 +msgid "Main screen scaling:" +msgstr "¼ÐèâÐÑ ÓÐÛÞþÝÐÓÐ íÚàÐÝÐ:" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:107 +msgid "Hardware scale (fast, but low quality)" +msgstr "ÅÐàÔÒÐàÝÐÕ ÜÐèâÐÑÐÒÐÝÝÕ (åãâÚÐ, ÐÛÕ Ýö×ÚÐÙ ïÚÐáæö)" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:108 +msgid "Software scale (good quality, but slower)" +msgstr "¿àÐÓàÐÜÝÐÕ ÜÐèâÐÑÐÒÐÝÝÕ (ÔÞÑàÐï ïÚÐáæì, ÐÛÕ ÜÐàãÔÝÕÙ)" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:109 +msgid "Unscaled (you must scroll left and right)" +msgstr "±Õ× ÜÐèâÐÑÐÒÐÝÝï (âàíÑÐ ÑãÔ×Õ ßàÐÚàãçÒÐæì ÝÐÛÕÒÐ ö ÝÐßàÐÒÐ)" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:111 +msgid "Brightness:" +msgstr "ÏàÚÐáæì:" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:121 +msgid "High quality audio (slower) (reboot)" +msgstr "²ëáÞÚÐï ïÚÐáæì ÓãÚã (ÜÐàãÔÝÕÙ) (àíÑãâ)" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:122 +msgid "Disable power off" +msgstr "·ÐÑÐàÐÝöæì ÒëÚÛîçíÝÝÕ" + +#: backends/platform/iphone/osys_events.cpp:300 +msgid "Mouse-click-and-drag mode enabled." +msgstr "ÀíÖëÜ Üëèë ÝÐæöáÝãæì-ö-æïÓÝãæì ãÚÛîçÐÝë." + +#: backends/platform/iphone/osys_events.cpp:302 +msgid "Mouse-click-and-drag mode disabled." +msgstr "ÀíÖëÜ Üëèë ÝÐæöáÝãæì-ö-æïÓÝãæì ÒëÚÛîçÐÝë." + +#: backends/platform/iphone/osys_events.cpp:313 +msgid "Touchpad mode enabled." +msgstr "ÀíÖëÜ âÐçßÐÔÐ þÚÛîçÐÝë." + +#: backends/platform/iphone/osys_events.cpp:315 +msgid "Touchpad mode disabled." +msgstr "ÀíÖëÜ âÐçßÐÔÐ ÒëÚÛîçÐÝë." + +#: backends/platform/maemo/maemo.cpp:209 +msgid "Click Mode" +msgstr "ÀíÖëÜ ßáâàëçÚö" + +#: backends/platform/maemo/maemo.cpp:215 +#: backends/platform/symbian/src/SymbianActions.cpp:42 +#: backends/platform/wince/CEActionsPocket.cpp:60 +#: backends/platform/wince/CEActionsSmartphone.cpp:43 +#: backends/platform/bada/form.cpp:281 +msgid "Left Click" +msgstr "»ÕÒÐï ßáâàëçÚÐ" + +#: backends/platform/maemo/maemo.cpp:218 +msgid "Middle Click" +msgstr "ÁïàíÔÝïï ßáâàëçÚÐ" + +#: backends/platform/maemo/maemo.cpp:221 +#: backends/platform/symbian/src/SymbianActions.cpp:43 +#: backends/platform/wince/CEActionsSmartphone.cpp:44 +#: backends/platform/bada/form.cpp:273 +msgid "Right Click" +msgstr "¿àÐÒÐï ßáâàëçÚÐ" + +#: backends/platform/sdl/macosx/appmenu_osx.mm:78 +msgid "Hide ScummVM" +msgstr "ÁåÐÒÐæì ScummVM" + +#: backends/platform/sdl/macosx/appmenu_osx.mm:83 +msgid "Hide Others" +msgstr "ÁåÐÒÐæì ÐáâÐâÝöï" + +#: backends/platform/sdl/macosx/appmenu_osx.mm:88 +msgid "Show All" +msgstr "¿ÐÚÐ×Ðæì ãáñ" + +#: backends/platform/sdl/macosx/appmenu_osx.mm:110 +#: backends/platform/sdl/macosx/appmenu_osx.mm:121 +msgid "Window" +msgstr "°ÚÝÞ" + +#: backends/platform/sdl/macosx/appmenu_osx.mm:115 +msgid "Minimize" +msgstr "¿àëÑàÐæì ã Dock" + +#: backends/graphics/surfacesdl/surfacesdl-graphics.cpp:45 +msgid "Normal (no scaling)" +msgstr "±Õ× ßÐÒÕÛöçíÝÝï" + +#: backends/graphics/surfacesdl/surfacesdl-graphics.cpp:64 +msgctxt "lowres" +msgid "Normal (no scaling)" +msgstr "±Õ× ßÐÒÕÛöçíÝÝï" + +#: backends/graphics/surfacesdl/surfacesdl-graphics.cpp:2135 +#: backends/graphics/openglsdl/openglsdl-graphics.cpp:533 +msgid "Enabled aspect ratio correction" +msgstr "ºÐàíÚæëï áãÐÔÝÞáöÝ ÑÐÚÞþ ãÚÛîçÐÝÐ" + +#: backends/graphics/surfacesdl/surfacesdl-graphics.cpp:2141 +#: backends/graphics/openglsdl/openglsdl-graphics.cpp:538 +msgid "Disabled aspect ratio correction" +msgstr "ºÐàíÚæëï áãÐÔÝÞáöÝ ÑÐÚÞþ ÒëÚÛîçÐÝÐ" + +#: backends/graphics/surfacesdl/surfacesdl-graphics.cpp:2196 +msgid "Active graphics filter:" +msgstr "°ÚâëþÝë ÓàÐäöçÝë äöÛìâà:" + +#: backends/graphics/surfacesdl/surfacesdl-graphics.cpp:2238 +#: backends/graphics/openglsdl/openglsdl-graphics.cpp:477 +msgid "Windowed mode" +msgstr "°ÚÞÝÝë àíÖëÜ" + +#: backends/graphics/opengl/opengl-graphics.cpp:135 +msgid "OpenGL Normal" +msgstr "OpenGL ÑÕ× ßÐÒÕÛöçíÝÝï" + +#: backends/graphics/opengl/opengl-graphics.cpp:136 +msgid "OpenGL Conserve" +msgstr "OpenGL × ×ÐåÐÒÐÝÝÕÜ" + +#: backends/graphics/opengl/opengl-graphics.cpp:137 +msgid "OpenGL Original" +msgstr "OpenGL ßÕàèÐßÐçÐâÚÞÒë" + +#: backends/graphics/openglsdl/openglsdl-graphics.cpp:415 +msgid "Current display mode" +msgstr "±ïÓãçë ÒöÔíÐàíÖëÜ" + +#: backends/graphics/openglsdl/openglsdl-graphics.cpp:428 +msgid "Current scale" +msgstr "±ïÓãçë ÜÐèâÐÑ" + +#: backends/graphics/openglsdl/openglsdl-graphics.cpp:558 +msgid "Active filter mode: Linear" +msgstr "°ÚâëþÝë àíÖëÜ äöÛìâàÐ: »öÝÕÙÝë" + +#: backends/graphics/openglsdl/openglsdl-graphics.cpp:560 +msgid "Active filter mode: Nearest" +msgstr "°ÚâëþÝë àíÖëÜ äöÛìâàÐ: ½ÐÙÑÛö×Úö" + +#: backends/platform/symbian/src/SymbianActions.cpp:38 +#: backends/platform/wince/CEActionsSmartphone.cpp:39 +msgid "Up" +msgstr "ÃÒÕàå" + +#: backends/platform/symbian/src/SymbianActions.cpp:39 +#: backends/platform/wince/CEActionsSmartphone.cpp:40 +msgid "Down" +msgstr "ÃÝö×" + +#: backends/platform/symbian/src/SymbianActions.cpp:40 +#: backends/platform/wince/CEActionsSmartphone.cpp:41 +msgid "Left" +msgstr "½ÐÛÕÒÐ" + +#: backends/platform/symbian/src/SymbianActions.cpp:41 +#: backends/platform/wince/CEActionsSmartphone.cpp:42 +msgid "Right" +msgstr "½ÐßàÐÒÐ" + +#: backends/platform/symbian/src/SymbianActions.cpp:46 +#: backends/platform/wince/CEActionsSmartphone.cpp:47 +msgid "Zone" +msgstr "·ÞÝÐ" + +#: backends/platform/symbian/src/SymbianActions.cpp:47 +#: backends/platform/wince/CEActionsPocket.cpp:54 +#: backends/platform/wince/CEActionsSmartphone.cpp:48 +msgid "Multi Function" +msgstr "¼ãÛìâëäãÝÚæëï" + +#: backends/platform/symbian/src/SymbianActions.cpp:48 +msgid "Swap character" +msgstr "·ÜïÝöæì ÓÕàÞï" + +#: backends/platform/symbian/src/SymbianActions.cpp:49 +msgid "Skip text" +msgstr "¿àÐßãáæöæì âíÚáâ" + +#: backends/platform/symbian/src/SymbianActions.cpp:51 +msgid "Fast mode" +msgstr "ÅãâÚö àíÖëÜ" + +#: backends/platform/symbian/src/SymbianActions.cpp:53 +msgid "Debugger" +msgstr "°ÔÛÐÔçëÚ" + +#: backends/platform/symbian/src/SymbianActions.cpp:54 +msgid "Global menu" +msgstr "³ÛÐÑÐÛìÝÐÕ ÜÕÝî" + +#: backends/platform/symbian/src/SymbianActions.cpp:55 +msgid "Virtual keyboard" +msgstr "²öàâãÐÛìÝÐï ÚÛÐÒöïâãàÐ" + +#: backends/platform/symbian/src/SymbianActions.cpp:56 +msgid "Key mapper" +msgstr "¿àë×ÝÐçíÝÝÕ ÚÛÐÒöè" + +#: backends/events/symbiansdl/symbiansdl-events.cpp:184 +msgid "Do you want to quit ?" +msgstr "²ë áÐßàÐþÔë ÖÐÔÐÕæÕ ÒëÙáæö?" + +#: backends/platform/wii/options.cpp:51 +msgid "Video" +msgstr "²öÔíÐ" + +#: backends/platform/wii/options.cpp:54 +msgid "Current video mode:" +msgstr "±ïÓãçë ÒöÔíÐàíÖëÜ:" + +#: backends/platform/wii/options.cpp:56 +msgid "Double-strike" +msgstr "´ÒÐÙÝë þÔÐà" + +#: backends/platform/wii/options.cpp:60 +msgid "Horizontal underscan:" +msgstr "³Ðàë×ÐÝâÐÛìÝë underscan:" + +#: backends/platform/wii/options.cpp:66 +msgid "Vertical underscan:" +msgstr "²ÕàâëÚÐÛìÝë underscan:" + +#: backends/platform/wii/options.cpp:71 +msgid "Input" +msgstr "ÃÒÞÔ" + +#: backends/platform/wii/options.cpp:74 +msgid "GC Pad sensitivity:" +msgstr "°ÔçãÒÐÛìÝÐáæì GC ßÐÔÐ:" + +#: backends/platform/wii/options.cpp:80 +msgid "GC Pad acceleration:" +msgstr "¿ÐáÚÐàíÝÝÕ GC ßÐÔÐ:" + +#: backends/platform/wii/options.cpp:86 +msgid "DVD" +msgstr "DVD" + +#: backends/platform/wii/options.cpp:89 backends/platform/wii/options.cpp:101 +msgid "Status:" +msgstr "ÁâÐÝ:" + +#: backends/platform/wii/options.cpp:90 backends/platform/wii/options.cpp:102 +msgid "Unknown" +msgstr "½ÕÒïÔÞÜÐ" + +#: backends/platform/wii/options.cpp:93 +msgid "Mount DVD" +msgstr "¿ÐÔÚÛîçëæì DVD" + +#: backends/platform/wii/options.cpp:94 +msgid "Unmount DVD" +msgstr "°ÔÚÛîçëæì DVD" + +#: backends/platform/wii/options.cpp:98 +msgid "SMB" +msgstr "SMB" + +#: backends/platform/wii/options.cpp:106 +msgid "Server:" +msgstr "ÁÕàÒÕà:" + +#: backends/platform/wii/options.cpp:110 +msgid "Share:" +msgstr "ÁÕâÚÐÒÐï âíçÚÐ:" + +#: backends/platform/wii/options.cpp:114 +msgid "Username:" +msgstr "ºÐàëáâÐç:" + +#: backends/platform/wii/options.cpp:118 +msgid "Password:" +msgstr "¿ÐàÞÛì:" + +#: backends/platform/wii/options.cpp:121 +msgid "Init network" +msgstr "¦ÝöæëïÛö×Ðæëï áÕâÚö" + +#: backends/platform/wii/options.cpp:123 +msgid "Mount SMB" +msgstr "¿ÐÔÚÛîçëæì SMB" + +#: backends/platform/wii/options.cpp:124 +msgid "Unmount SMB" +msgstr "°ÔÚÛîçëæì SMB" + +#: backends/platform/wii/options.cpp:143 +msgid "DVD Mounted successfully" +msgstr "DVD ßÐÔÚÛîçÐÝë ßÐáßïåÞÒÐ" + +#: backends/platform/wii/options.cpp:146 +msgid "Error while mounting the DVD" +msgstr "¿ÐÜëÛÚÐ ßàë ßÐÔÚÛîçíÝÝö DVD" + +#: backends/platform/wii/options.cpp:148 +msgid "DVD not mounted" +msgstr "DVD ÝÕ ßÐÔÚÛîçÐÝë" + +#: backends/platform/wii/options.cpp:161 +msgid "Network up, share mounted" +msgstr "ÁÕâÚÐ ßàÐæãÕ, âíçÚÐ ßÐÔÚÛîçÐÝÐ" + +#: backends/platform/wii/options.cpp:163 +msgid "Network up" +msgstr "ÁÕâÚÐ ßàÐæãÕ" + +#: backends/platform/wii/options.cpp:166 +msgid ", error while mounting the share" +msgstr ", ßÐÜëÛÚÐ ßàë ßÐÔÚÛîçíÝÝö âíçÚö" + +#: backends/platform/wii/options.cpp:168 +msgid ", share not mounted" +msgstr ", âíçÚÐ ÝÕ ßÐÔÚÛîçÐÝÐ" + +#: backends/platform/wii/options.cpp:174 +msgid "Network down" +msgstr "ÁÕâÚÐ ÒëÚÛîçÐÝÐ" + +#: backends/platform/wii/options.cpp:178 +msgid "Initializing network" +msgstr "½ÐÛÐÔÖÒÐî áÕâÚã" + +#: backends/platform/wii/options.cpp:182 +msgid "Timeout while initializing network" +msgstr "ÇÐá ßÐÔÚÛîçíÝÝï ÔÐ áÕâÚö ÜöÝãþ" + +#: backends/platform/wii/options.cpp:186 +#, c-format +msgid "Network not initialized (%d)" +msgstr "ÁÕâÚÐ ÝÕ ÝÐÛÐÔ×öÛÐáï (%d)" + +#: backends/platform/wince/CEActionsPocket.cpp:46 +msgid "Hide Toolbar" +msgstr "ÁåÐÒÐæì ßÐÝíÛì öÝáâàãÜÕÝâÐþ" + +#: backends/platform/wince/CEActionsPocket.cpp:47 +msgid "Show Keyboard" +msgstr "¿ÐÚÐ×Ðæì ÚÛÐÒöïâãàã" + +#: backends/platform/wince/CEActionsPocket.cpp:48 +msgid "Sound on/off" +msgstr "³ãÚ ãÚÛ/ÒëÚÛ" + +#: backends/platform/wince/CEActionsPocket.cpp:49 +msgid "Right click" +msgstr "¿àÐÒÐï ßáâàëçÚÐ" + +#: backends/platform/wince/CEActionsPocket.cpp:50 +msgid "Show/Hide Cursor" +msgstr "¿ÐÚÐ×Ðæì/¿àëÑàÐæì ÚãàáÞà" + +#: backends/platform/wince/CEActionsPocket.cpp:51 +msgid "Free look" +msgstr "²ÞÛìÝë ÐÓÛïÔ" + +#: backends/platform/wince/CEActionsPocket.cpp:52 +msgid "Zoom up" +msgstr "¿ÐÒïÛ. ÜÐèâÐÑ" + +#: backends/platform/wince/CEActionsPocket.cpp:53 +msgid "Zoom down" +msgstr "¿ÐÜÕÝè. ÜÐèâÐÑ" + +#: backends/platform/wince/CEActionsPocket.cpp:55 +#: backends/platform/wince/CEActionsSmartphone.cpp:49 +msgid "Bind Keys" +msgstr "¿àë×ÝÐçëæì ÚÛÐÒöèë" + +#: backends/platform/wince/CEActionsPocket.cpp:56 +msgid "Cursor Up" +msgstr "ºãàáÞà ãÒÕàå" + +#: backends/platform/wince/CEActionsPocket.cpp:57 +msgid "Cursor Down" +msgstr "ºãàáÞà ãÝö×" + +#: backends/platform/wince/CEActionsPocket.cpp:58 +msgid "Cursor Left" +msgstr "ºãàáÞà ÝÐÛÕÒÐ" + +#: backends/platform/wince/CEActionsPocket.cpp:59 +msgid "Cursor Right" +msgstr "ºãàáÞà ÝÐßàÐÒÐ" + +#: backends/platform/wince/CEActionsPocket.cpp:267 +#: backends/platform/wince/CEActionsSmartphone.cpp:231 +msgid "Do you want to load or save the game?" +msgstr "²ë ÖÐÔÐÕæÕ ×ÐÓàã×öæì ÐÑÞ ×ÐåÐÒÐæì ÓãÛìÝî?" + +#: backends/platform/wince/CEActionsPocket.cpp:326 +#: backends/platform/wince/CEActionsSmartphone.cpp:287 +msgid " Are you sure you want to quit ? " +msgstr " ²ë þßíþÝÕÝë, èâÞ ÖÐÔÐÕæÕ ÒëÙáæö? " + +#: backends/platform/wince/CEActionsSmartphone.cpp:50 +msgid "Keyboard" +msgstr "ºÛÐÒöïâãàÐ" + +#: backends/platform/wince/CEActionsSmartphone.cpp:51 +msgid "Rotate" +msgstr "¿ÐÒïàÝãæì" + +#: backends/platform/wince/CELauncherDialog.cpp:56 +msgid "Using SDL driver " +msgstr "²ëÚÐàëáâÞþÒÐî ÔàÐÙÒÕà SDL " + +#: backends/platform/wince/CELauncherDialog.cpp:60 +msgid "Display " +msgstr "¿ÐÚÐ×Ðæì " + +#: backends/platform/wince/CELauncherDialog.cpp:83 +msgid "Do you want to perform an automatic scan ?" +msgstr "²ë ÖÐÔÐÕæÕ ×àÐÑöæì ÐþâÐÜÐâëçÝë ßÞèãÚ?" + +#: backends/platform/wince/wince-sdl.cpp:515 +msgid "Map right click action" +msgstr "¿àë×ÝÐçëæì Ô×ÕïÝÝÕ ßÐ ßàÐÒÐÙ ßáâàëçæë" + +#: backends/platform/wince/wince-sdl.cpp:519 +msgid "You must map a key to the 'Right Click' action to play this game" +msgstr "" +"²ë ßÐÒöÝÝë ßàë×ÝÐçëæì ÚÛÐÒöèã ÝÐ Ô×ÕïÝÝÕ 'Right Click' ÔÛï ÓíâÐÙ ÓãÛìÝö" + +#: backends/platform/wince/wince-sdl.cpp:528 +msgid "Map hide toolbar action" +msgstr "¿àë×ÝÐçëæì Ô×ÕïÝÝÕ 'áåÐÒÐæì ßÐÝíÛì öÝáâàãÜÕÝâÐþ'" + +#: backends/platform/wince/wince-sdl.cpp:532 +msgid "You must map a key to the 'Hide toolbar' action to play this game" +msgstr "" +"²ë ßÐÒöÝÝë ßàë×ÝÐçëæì ÚÛÐÒöèã ÝÐ Ô×ÕïÝÝÕ 'Hide toolbar' ÔÛï ÓíâÐÙ ÓãÛìÝö" + +#: backends/platform/wince/wince-sdl.cpp:541 +msgid "Map Zoom Up action (optional)" +msgstr "¿àë×ÝÐçëæì Ô×ÕïÝÝÕ ¿ÐÒïÛöçëæì ¼ÐèâÐÑ (ÝÕÐÑÐÒï×ÚÞÒÐ)" + +#: backends/platform/wince/wince-sdl.cpp:544 +msgid "Map Zoom Down action (optional)" +msgstr "¿àë×ÝÐçëæì Ô×ÕïÝÝÕ ¿ÐÜÕÝèëæì ¼ÐèâÐÑ (ÝÕÐÑÐÒï×ÚÞÒÐ)" + +#: backends/platform/wince/wince-sdl.cpp:552 +msgid "" +"Don't forget to map a key to 'Hide Toolbar' action to see the whole inventory" +msgstr "" +"½Õ ×ÐÑãÔ×ìæÕáï ßàë×ÝÐçëæì ÚÛÐÒöèã ÔÛï Ô×ÕïÝÝï 'Hide Toolbar', ÚÐÑ ãÑÐçëæì " +"ãÒÕáì öÝÒÕÝâÐà ã ÓãÛìÝö" + +#: backends/events/default/default-events.cpp:191 +msgid "Do you really want to return to the Launcher?" +msgstr "²ë áÐßàÐþÔë ÖÐÔÐÕæÕ ÒïàÝãææÐ þ ÓÐÛÞþÝÐÕ ÜÕÝî?" + +#: backends/events/default/default-events.cpp:191 +msgid "Launcher" +msgstr "³ÐÛÞþÝÐÕ ÜÕÝî" + +#: backends/events/default/default-events.cpp:213 +msgid "Do you really want to quit?" +msgstr "²ë áÐßàÐþÔë ÖÐÔÐÕæÕ ÒëÙáæö?" + +#: backends/events/gph/gph-events.cpp:386 +#: backends/events/gph/gph-events.cpp:429 +#: backends/events/openpandora/op-events.cpp:139 +msgid "Touchscreen 'Tap Mode' - Left Click" +msgstr "ÀíÖëÜ 'ÔÞâëÚÐþ' âÐçáÚàëÝÐ - »ÕÒë ÚÛöÚ" + +#: backends/events/gph/gph-events.cpp:388 +#: backends/events/gph/gph-events.cpp:431 +#: backends/events/openpandora/op-events.cpp:141 +msgid "Touchscreen 'Tap Mode' - Right Click" +msgstr "ÀíÖëÜ 'ÔÞâëÚÐþ' âÐçáÚàëÝÐ - ¿àÐÒë ÚÛöÚ" + +#: backends/events/gph/gph-events.cpp:390 +#: backends/events/gph/gph-events.cpp:433 +#: backends/events/openpandora/op-events.cpp:143 +msgid "Touchscreen 'Tap Mode' - Hover (No Click)" +msgstr "ÀíÖëÜ 'ÔÞâëÚÐþ' âÐçáÚàëÝÐ - ¿àÐÛñâ (ÑÕ× ÚÛöÚã)" + +#: backends/events/gph/gph-events.cpp:410 +msgid "Maximum Volume" +msgstr "¼ÐÚáöÜÐÛìÝÐï ÓãçÝÐáæì" + +#: backends/events/gph/gph-events.cpp:412 +msgid "Increasing Volume" +msgstr "¿ÐÒÕÛöçíÝÝÕ ÓãçÝÐáæö" + +#: backends/events/gph/gph-events.cpp:418 +msgid "Minimal Volume" +msgstr "¼öÝöÜÐÛìÝÐï ÓãçÝÐáæì" + +#: backends/events/gph/gph-events.cpp:420 +msgid "Decreasing Volume" +msgstr "¿ÐÜïÝèíÝÝÕ ÓãçÝÐáæö" + +#: backends/updates/macosx/macosx-updates.mm:65 +msgid "Check for Updates..." +msgstr "¿àÐÒïàÐî ÐÑÝÐþÛÕÝÝö..." + +#: backends/platform/bada/form.cpp:269 +msgid "Right Click Once" +msgstr "°ÔÝÐ ßàÐÒÐï ßáâàëçÚÐ" + +#: backends/platform/bada/form.cpp:277 +msgid "Move Only" +msgstr "ÂÞÛìÚö ßÕàÐÜïáæöæì" + +#: backends/platform/bada/form.cpp:291 +msgid "Escape Key" +msgstr "ºÛÐÒöèÐ ESC" + +#: backends/platform/bada/form.cpp:296 +msgid "Game Menu" +msgstr "¼ÕÝî ÓãÛìÝö" + +#: backends/platform/bada/form.cpp:301 +msgid "Show Keypad" +msgstr "¿ÐÚÐ×Ðæì ÚÛÐÒöïâãàã" + +#: backends/platform/bada/form.cpp:309 +msgid "Control Mouse" +msgstr "ºöàÐÒÐÝÝÕ Üëèèã" + +#: backends/events/maemosdl/maemosdl-events.cpp:192 +msgid "Clicking Enabled" +msgstr "¿áâàëçÚö þÚÛîçÐÝë" + +#: backends/events/maemosdl/maemosdl-events.cpp:192 +msgid "Clicking Disabled" +msgstr "¿áâàëçÚö ÒëÚÛîçÐÝë" + +#~ msgid "Hercules Green" +#~ msgstr "Hercules ·ÕÛñÝëÙ" + +#~ msgid "Hercules Amber" +#~ msgstr "Hercules ÏÝâÐàÝëÙ" + +#~ msgctxt "lowres" +#~ msgid "Hercules Green" +#~ msgstr "Hercules ·ÕÛñÝëÙ" + +#~ msgctxt "lowres" +#~ msgid "Hercules Amber" +#~ msgstr "Hercules ÏÝâÐàÝëÙ" + +#~ msgid "Save game failed!" +#~ msgstr "½Õ ãÔÐÛÞáì áÞåàÐÝØâì ØÓàã!" + +#~ msgctxt "lowres" +#~ msgid "Add Game..." +#~ msgstr "´ÞÑ. ØÓàã" + +#~ msgid "Add Game..." +#~ msgstr "´ÞÑÐÒØâì ØÓàã..." + +#~ msgid "Discovered %d new games." +#~ msgstr "½ÐÙÔÕÝÞ %d ÝÞÒëå ØÓà." + +#~ msgid "Command line argument not processed" +#~ msgstr "¿ÐàÐÜÕâàë ÚÞÜÐÝÔÝÞÙ áâàÞÚØ ÝÕ ÞÑàÐÑÞâÐÝë" + +#~ msgid "FM Towns Emulator" +#~ msgstr "ÍÜãÛïâÞà FM Towns" + +#~ msgid "Invalid Path" +#~ msgstr "½ÕÒÕàÝëÙ ßãâì" diff --git a/po/ca_ES.po b/po/ca_ES.po index 8a978bee0d..706eb64edb 100644 --- a/po/ca_ES.po +++ b/po/ca_ES.po @@ -1,20 +1,20 @@ # Catalan translation for ScummVM. -# Copyright (C) 2007-2012 ScummVM Team +# Copyright (C) 2007-2013 ScummVM Team # This file is distributed under the same license as the ScummVM package. # Jordi Vilalta Prat <jvprat@jvprat.com>, 2007-2011. # msgid "" msgstr "" -"Project-Id-Version: ScummVM 1.3.0svn\n" +"Project-Id-Version: ScummVM 1.6.0git\n" "Report-Msgid-Bugs-To: scummvm-devel@lists.sf.net\n" -"POT-Creation-Date: 2012-07-08 12:25+0100\n" -"PO-Revision-Date: 2011-10-04 20:51+0100\n" +"POT-Creation-Date: 2012-12-01 17:22+0000\n" +"PO-Revision-Date: 2012-08-26 20:32+0100\n" "Last-Translator: Jordi Vilalta Prat <jvprat@jvprat.com>\n" "Language-Team: Catalan <scummvm-devel@lists.sf.net>\n" +"Language: Catalan\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=iso-8859-1\n" "Content-Transfer-Encoding: 8bit\n" -"Language: Catalan\n" #: gui/about.cpp:91 #, c-format @@ -44,10 +44,11 @@ msgstr "Amunt" #: gui/browser.cpp:69 gui/chooser.cpp:45 gui/KeysDialog.cpp:43 #: gui/launcher.cpp:345 gui/massadd.cpp:94 gui/options.cpp:1228 -#: gui/saveload.cpp:64 gui/saveload.cpp:173 gui/themebrowser.cpp:54 -#: engines/engine.cpp:442 engines/scumm/dialogs.cpp:190 -#: engines/sword1/control.cpp:865 engines/parallaction/saveload.cpp:274 -#: backends/platform/wii/options.cpp:48 +#: gui/saveload-dialog.cpp:215 gui/saveload-dialog.cpp:275 +#: gui/saveload-dialog.cpp:545 gui/saveload-dialog.cpp:919 +#: gui/themebrowser.cpp:54 engines/engine.cpp:442 +#: engines/scumm/dialogs.cpp:190 engines/sword1/control.cpp:867 +#: engines/parallaction/saveload.cpp:274 backends/platform/wii/options.cpp:48 #: backends/events/default/default-events.cpp:191 #: backends/events/default/default-events.cpp:213 msgid "Cancel" @@ -68,16 +69,15 @@ msgstr "Tanca" msgid "Mouse click" msgstr "Clic del ratolí" -#: gui/gui-manager.cpp:122 base/main.cpp:300 +#: gui/gui-manager.cpp:122 base/main.cpp:301 msgid "Display keyboard" msgstr "Mostra el teclat" -#: gui/gui-manager.cpp:126 base/main.cpp:304 +#: gui/gui-manager.cpp:126 base/main.cpp:305 msgid "Remap keys" msgstr "Assigna les tecles" -#: gui/gui-manager.cpp:129 base/main.cpp:307 -#, fuzzy +#: gui/gui-manager.cpp:129 base/main.cpp:308 msgid "Toggle FullScreen" msgstr "Commuta la pantalla completa" @@ -91,16 +91,16 @@ msgstr "Assigna" #: gui/KeysDialog.cpp:42 gui/launcher.cpp:346 gui/launcher.cpp:1001 #: gui/launcher.cpp:1005 gui/massadd.cpp:91 gui/options.cpp:1229 -#: engines/engine.cpp:361 engines/engine.cpp:372 engines/scumm/dialogs.cpp:192 -#: engines/scumm/scumm.cpp:1775 engines/agos/animation.cpp:561 -#: engines/groovie/script.cpp:420 engines/sky/compact.cpp:131 -#: engines/sky/compact.cpp:141 engines/sword1/animation.cpp:539 -#: engines/sword1/animation.cpp:560 engines/sword1/animation.cpp:570 -#: engines/sword1/animation.cpp:577 engines/sword1/control.cpp:865 -#: engines/sword1/logic.cpp:1633 engines/sword2/animation.cpp:435 -#: engines/sword2/animation.cpp:455 engines/sword2/animation.cpp:465 -#: engines/sword2/animation.cpp:474 engines/parallaction/saveload.cpp:274 -#: backends/platform/wii/options.cpp:47 +#: gui/saveload-dialog.cpp:920 engines/engine.cpp:361 engines/engine.cpp:372 +#: engines/scumm/dialogs.cpp:192 engines/scumm/scumm.cpp:1776 +#: engines/agos/animation.cpp:558 engines/groovie/script.cpp:420 +#: engines/sky/compact.cpp:131 engines/sky/compact.cpp:141 +#: engines/sword1/animation.cpp:519 engines/sword1/animation.cpp:540 +#: engines/sword1/animation.cpp:550 engines/sword1/animation.cpp:557 +#: engines/sword1/control.cpp:867 engines/sword1/logic.cpp:1633 +#: engines/sword2/animation.cpp:419 engines/sword2/animation.cpp:439 +#: engines/sword2/animation.cpp:449 engines/sword2/animation.cpp:458 +#: engines/parallaction/saveload.cpp:274 backends/platform/wii/options.cpp:47 #: backends/platform/wince/CELauncherDialog.cpp:54 msgid "OK" msgstr "D'acord" @@ -193,9 +193,8 @@ msgid "Platform:" msgstr "Platafor.:" #: gui/launcher.cpp:231 -#, fuzzy msgid "Engine" -msgstr "Examina" +msgstr "Motor" #: gui/launcher.cpp:239 gui/options.cpp:1062 gui/options.cpp:1079 msgid "Graphics" @@ -357,7 +356,7 @@ msgstr "" msgid "~Q~uit" msgstr "~T~anca" -#: gui/launcher.cpp:621 backends/platform/sdl/macosx/appmenu_osx.mm:96 +#: gui/launcher.cpp:621 backends/platform/sdl/macosx/appmenu_osx.mm:95 msgid "Quit ScummVM" msgstr "Surt de ScummVM" @@ -365,7 +364,7 @@ msgstr "Surt de ScummVM" msgid "A~b~out..." msgstr "~Q~uant a..." -#: gui/launcher.cpp:622 backends/platform/sdl/macosx/appmenu_osx.mm:70 +#: gui/launcher.cpp:622 backends/platform/sdl/macosx/appmenu_osx.mm:69 msgid "About ScummVM" msgstr "Quant a ScummVM" @@ -442,13 +441,13 @@ msgstr "Cerca a la llista de jocs" msgid "Search:" msgstr "Cerca:" -#: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/mohawk/myst.cpp:255 +#: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/mohawk/myst.cpp:245 #: engines/mohawk/riven.cpp:716 engines/cruise/menu.cpp:214 msgid "Load game:" msgstr "Carrega partida:" #: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/scumm/dialogs.cpp:188 -#: engines/mohawk/myst.cpp:255 engines/mohawk/riven.cpp:716 +#: engines/mohawk/myst.cpp:245 engines/mohawk/riven.cpp:716 #: engines/cruise/menu.cpp:214 backends/platform/wince/CEActionsPocket.cpp:267 #: backends/platform/wince/CEActionsSmartphone.cpp:231 msgid "Load" @@ -931,68 +930,101 @@ msgstr "" "El tema que heu seleccionat no suporta l'idioma actual. Si voleu utilitzar " "aquest tema primer haureu de canviar a un altre idioma." -#: gui/saveload.cpp:59 gui/saveload.cpp:257 +#: gui/saveload-dialog.cpp:166 +msgid "List view" +msgstr "Vista de llistat" + +#: gui/saveload-dialog.cpp:167 +msgid "Grid view" +msgstr "Vista de quadrícula" + +#: gui/saveload-dialog.cpp:210 gui/saveload-dialog.cpp:358 msgid "No date saved" msgstr "No hi ha data desada" -#: gui/saveload.cpp:60 gui/saveload.cpp:258 +#: gui/saveload-dialog.cpp:211 gui/saveload-dialog.cpp:359 msgid "No time saved" msgstr "No hi ha hora desada" -#: gui/saveload.cpp:61 gui/saveload.cpp:259 +#: gui/saveload-dialog.cpp:212 gui/saveload-dialog.cpp:360 msgid "No playtime saved" msgstr "No hi ha temps de joc desat" -#: gui/saveload.cpp:68 gui/saveload.cpp:173 +#: gui/saveload-dialog.cpp:219 gui/saveload-dialog.cpp:275 msgid "Delete" msgstr "Suprimeix" -#: gui/saveload.cpp:172 +#: gui/saveload-dialog.cpp:274 msgid "Do you really want to delete this savegame?" msgstr "Realment voleu suprimir aquesta partida?" -#: gui/saveload.cpp:282 +#: gui/saveload-dialog.cpp:383 gui/saveload-dialog.cpp:872 msgid "Date: " msgstr "Data: " -#: gui/saveload.cpp:286 +#: gui/saveload-dialog.cpp:387 gui/saveload-dialog.cpp:878 msgid "Time: " msgstr "Hora: " -#: gui/saveload.cpp:292 +#: gui/saveload-dialog.cpp:393 gui/saveload-dialog.cpp:886 msgid "Playtime: " msgstr "Temps de joc: " -#: gui/saveload.cpp:305 gui/saveload.cpp:372 +#: gui/saveload-dialog.cpp:406 gui/saveload-dialog.cpp:494 msgid "Untitled savestate" msgstr "Partida sense títol" +#: gui/saveload-dialog.cpp:546 +msgid "Next" +msgstr "Següent" + +#: gui/saveload-dialog.cpp:549 +msgid "Prev" +msgstr "Anterior" + +#: gui/saveload-dialog.cpp:736 +msgid "New Save" +msgstr "Nova partida desada" + +#: gui/saveload-dialog.cpp:736 +msgid "Create a new save game" +msgstr "Crea una nova partida desada" + +#: gui/saveload-dialog.cpp:865 +msgid "Name: " +msgstr "Nom: " + +#: gui/saveload-dialog.cpp:937 +#, c-format +msgid "Enter a description for slot %d:" +msgstr "Entreu la descripció per l'espai %d:" + #: gui/themebrowser.cpp:44 msgid "Select a Theme" msgstr "Seleccioneu un Tema" -#: gui/ThemeEngine.cpp:335 +#: gui/ThemeEngine.cpp:337 msgid "Disabled GFX" msgstr "GFX desactivats" -#: gui/ThemeEngine.cpp:335 +#: gui/ThemeEngine.cpp:337 msgctxt "lowres" msgid "Disabled GFX" msgstr "GFX desactivats" -#: gui/ThemeEngine.cpp:336 +#: gui/ThemeEngine.cpp:338 msgid "Standard Renderer (16bpp)" msgstr "Pintat estàndard (16bpp)" -#: gui/ThemeEngine.cpp:336 +#: gui/ThemeEngine.cpp:338 msgid "Standard (16bpp)" msgstr "Estàndard (16bpp)" -#: gui/ThemeEngine.cpp:338 +#: gui/ThemeEngine.cpp:340 msgid "Antialiased Renderer (16bpp)" msgstr "Pintat amb antialias (16bpp)" -#: gui/ThemeEngine.cpp:338 +#: gui/ThemeEngine.cpp:340 msgid "Antialiased (16bpp)" msgstr "Amb antialias (16bpp)" @@ -1000,35 +1032,35 @@ msgstr "Amb antialias (16bpp)" msgid "Clear value" msgstr "Neteja el valor" -#: base/main.cpp:209 +#: base/main.cpp:210 #, c-format msgid "Engine does not support debug level '%s'" msgstr "El motor no suporta el nivell de depuració '%s'" -#: base/main.cpp:287 +#: base/main.cpp:288 msgid "Menu" msgstr "Menú" -#: base/main.cpp:290 backends/platform/symbian/src/SymbianActions.cpp:45 +#: base/main.cpp:291 backends/platform/symbian/src/SymbianActions.cpp:45 #: backends/platform/wince/CEActionsPocket.cpp:45 #: backends/platform/wince/CEActionsSmartphone.cpp:46 msgid "Skip" msgstr "Salta" -#: base/main.cpp:293 backends/platform/symbian/src/SymbianActions.cpp:50 +#: base/main.cpp:294 backends/platform/symbian/src/SymbianActions.cpp:50 #: backends/platform/wince/CEActionsPocket.cpp:42 msgid "Pause" msgstr "Pausa" -#: base/main.cpp:296 +#: base/main.cpp:297 msgid "Skip line" msgstr "Salta la línia" -#: base/main.cpp:467 +#: base/main.cpp:468 msgid "Error running game:" msgstr "Error al executar el joc:" -#: base/main.cpp:491 +#: base/main.cpp:492 msgid "Could not find any engine capable of running the selected game" msgstr "No s'ha pogut trobar cap motor capaç d'executar el joc seleccionat" @@ -1096,17 +1128,17 @@ msgstr "Cancel·lat per l'usuari" msgid "Unknown error" msgstr "Error desconegut" -#: engines/advancedDetector.cpp:324 +#: engines/advancedDetector.cpp:316 #, c-format msgid "The game in '%s' seems to be unknown." msgstr "El joc a '%s' sembla ser desconegut." -#: engines/advancedDetector.cpp:325 +#: engines/advancedDetector.cpp:317 msgid "Please, report the following data to the ScummVM team along with name" msgstr "" "Informeu de la següent informació a l'equip de ScummVM juntament amb el" -#: engines/advancedDetector.cpp:327 +#: engines/advancedDetector.cpp:319 msgid "of the game you tried to add and its version/language/etc.:" msgstr "nom del joc que heu provat d'afegir i la seva versió/llengua/etc.:" @@ -1144,13 +1176,13 @@ msgid "~R~eturn to Launcher" msgstr "~R~etorna al Llançador" #: engines/dialogs.cpp:115 engines/agi/saveload.cpp:803 -#: engines/cruise/menu.cpp:212 engines/sci/engine/kfile.cpp:735 +#: engines/cruise/menu.cpp:212 engines/sci/engine/kfile.cpp:742 msgid "Save game:" msgstr "Desa la partida:" #: engines/dialogs.cpp:115 engines/agi/saveload.cpp:803 #: engines/scumm/dialogs.cpp:187 engines/cruise/menu.cpp:212 -#: engines/sci/engine/kfile.cpp:735 +#: engines/sci/engine/kfile.cpp:742 #: backends/platform/symbian/src/SymbianActions.cpp:44 #: backends/platform/wince/CEActionsPocket.cpp:43 #: backends/platform/wince/CEActionsPocket.cpp:267 @@ -1169,13 +1201,13 @@ msgstr "" "la informació bàsica i les instruccions sobre com obtenir més assistència." #: engines/dialogs.cpp:228 -#, fuzzy, c-format +#, c-format msgid "" "Gamestate save failed (%s)! Please consult the README for basic information, " "and for instructions on how to obtain further assistance." msgstr "" -"Aquest motor no ofereix ajuda dins el joc. Consulteu el fitxer README per a " -"la informació bàsica i les instruccions sobre com obtenir més assistència." +"No s'ha pogut desar la partida (%s)! Consulteu el fitxer README per a la " +"informació bàsica i les instruccions sobre com obtenir més assistència." #: engines/dialogs.cpp:301 engines/mohawk/dialogs.cpp:109 #: engines/mohawk/dialogs.cpp:174 @@ -1236,13 +1268,13 @@ msgstr "" "Consulteu el fitxer README per a més detalls." #: engines/engine.cpp:426 -#, fuzzy, c-format +#, c-format msgid "" "Gamestate load failed (%s)! Please consult the README for basic information, " "and for instructions on how to obtain further assistance." msgstr "" -"Aquest motor no ofereix ajuda dins el joc. Consulteu el fitxer README per a " -"la informació bàsica i les instruccions sobre com obtenir més assistència." +"No s'ha pogut carregar la partida (%s)! Consulteu el fitxer README per a la " +"informació bàsica i les instruccions sobre com obtenir més assistència." #: engines/engine.cpp:439 msgid "" @@ -1258,87 +1290,91 @@ msgstr "" msgid "Start anyway" msgstr "Inicia de totes maneres" -#: engines/agi/detection.cpp:145 engines/dreamweb/detection.cpp:47 -#: engines/sci/detection.cpp:390 +#: engines/agi/detection.cpp:142 engines/dreamweb/detection.cpp:47 +#: engines/sci/detection.cpp:393 msgid "Use original save/load screens" -msgstr "" +msgstr "Utilitza les pantalles originals de desat/càrrega" -#: engines/agi/detection.cpp:146 engines/dreamweb/detection.cpp:48 -#: engines/sci/detection.cpp:391 +#: engines/agi/detection.cpp:143 engines/dreamweb/detection.cpp:48 +#: engines/sci/detection.cpp:394 msgid "Use the original save/load screens, instead of the ScummVM ones" msgstr "" +"Utilitza les pantalles originals de desat/càrrega, en lloc de les de ScummVM" -#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:831 +#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:838 msgid "Restore game:" msgstr "Recupera la partida:" -#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:831 +#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:838 msgid "Restore" msgstr "Restaura" #: engines/dreamweb/detection.cpp:57 -#, fuzzy msgid "Use bright palette mode" -msgstr "Element superior dret" +msgstr "Utilitza el mode de paleta brillant" #: engines/dreamweb/detection.cpp:58 msgid "Display graphics using the game's bright palette" -msgstr "" +msgstr "Mostra els gràfics utilitzant la paleta brillant del joc" -#: engines/sci/detection.cpp:370 +#: engines/sci/detection.cpp:373 msgid "EGA undithering" msgstr "Elimina el tramat d'EGA" -#: engines/sci/detection.cpp:371 -#, fuzzy +#: engines/sci/detection.cpp:374 msgid "Enable undithering in EGA games" -msgstr "Activa l'eliminació del tramat en els jocs EGA que ho suportin" +msgstr "Activa l'eliminació del tramat en els jocs EGA" -#: engines/sci/detection.cpp:380 -#, fuzzy +#: engines/sci/detection.cpp:383 msgid "Prefer digital sound effects" -msgstr "Volum dels sons d'efectes especials" +msgstr "Prefereix efectes de so digitals" -#: engines/sci/detection.cpp:381 +#: engines/sci/detection.cpp:384 msgid "Prefer digital sound effects instead of synthesized ones" -msgstr "" +msgstr "Prefereix els efectes de so digitals en lloc dels sintetitzats" -#: engines/sci/detection.cpp:400 +#: engines/sci/detection.cpp:403 msgid "Use IMF/Yamaha FB-01 for MIDI output" -msgstr "" +msgstr "Utilitza IMF/Yamaha FB-01 per la sortida MIDI" -#: engines/sci/detection.cpp:401 +#: engines/sci/detection.cpp:404 msgid "" "Use an IBM Music Feature card or a Yamaha FB-01 FM synth module for MIDI " "output" msgstr "" +"Utilitza una tarja IBM Music Feature o un mòdul sintetitzador Yamaha FB-01 " +"FM per la sortida MIDI" -#: engines/sci/detection.cpp:411 +#: engines/sci/detection.cpp:414 msgid "Use CD audio" -msgstr "" +msgstr "Utilitza l'àudio del CD" -#: engines/sci/detection.cpp:412 +#: engines/sci/detection.cpp:415 msgid "Use CD audio instead of in-game audio, if available" msgstr "" +"Utilitza l'àudio del CD en lloc de l'àudio intern del joc, si està disponible" -#: engines/sci/detection.cpp:422 +#: engines/sci/detection.cpp:425 msgid "Use Windows cursors" -msgstr "" +msgstr "Utilitza els cursors de Windows" -#: engines/sci/detection.cpp:423 +#: engines/sci/detection.cpp:426 msgid "" "Use the Windows cursors (smaller and monochrome) instead of the DOS ones" msgstr "" +"Utilitza els cursors de Windows (més petits i en blanc i negre) en lloc dels " +"de DOS" -#: engines/sci/detection.cpp:433 -#, fuzzy +#: engines/sci/detection.cpp:436 msgid "Use silver cursors" -msgstr "Cursor normal" +msgstr "Utilitza cursors platejats" -#: engines/sci/detection.cpp:434 +#: engines/sci/detection.cpp:437 msgid "" "Use the alternate set of silver cursors, instead of the normal golden ones" msgstr "" +"Utilitza el conjunt alternatiu de cursors platejats, en lloc dels normals " +"daurats" #: engines/scumm/dialogs.cpp:175 #, c-format @@ -1456,24 +1492,23 @@ msgstr "Veus i sub." #: engines/scumm/dialogs.cpp:653 msgid "Select a Proficiency Level." -msgstr "" +msgstr "Seleccioneu el nivell de competència." #: engines/scumm/dialogs.cpp:655 msgid "Refer to your Loom(TM) manual for help." -msgstr "" +msgstr "Consulteu el manual de Loom(TM) per ajuda." #: engines/scumm/dialogs.cpp:658 -#, fuzzy msgid "Standard" -msgstr "Estàndard (16bpp)" +msgstr "Estàndard" #: engines/scumm/dialogs.cpp:659 msgid "Practice" -msgstr "" +msgstr "Pràctica" #: engines/scumm/dialogs.cpp:660 msgid "Expert" -msgstr "" +msgstr "Expert" #: engines/scumm/help.cpp:73 msgid "Common keyboard commands:" @@ -1988,7 +2023,7 @@ msgstr "Vola a la dreta" msgid "Fly to lower right" msgstr "Vola avall i a la dreta" -#: engines/scumm/scumm.cpp:1773 +#: engines/scumm/scumm.cpp:1774 #, c-format msgid "" "Native MIDI support requires the Roland Upgrade from LucasArts,\n" @@ -1997,7 +2032,7 @@ msgstr "" "El suport de MIDI natiu requereix l'actualització Roland de LucasArts,\n" "però no s'ha trobat %s. S'utilitzarà AdLib." -#: engines/scumm/scumm.cpp:2278 engines/agos/saveload.cpp:202 +#: engines/scumm/scumm.cpp:2295 engines/agos/saveload.cpp:220 #, c-format msgid "" "Failed to save game state to file:\n" @@ -2008,7 +2043,7 @@ msgstr "" "\n" "%s" -#: engines/scumm/scumm.cpp:2285 engines/agos/saveload.cpp:167 +#: engines/scumm/scumm.cpp:2302 engines/agos/saveload.cpp:185 #, c-format msgid "" "Failed to load game state from file:\n" @@ -2019,7 +2054,7 @@ msgstr "" "\n" "%s" -#: engines/scumm/scumm.cpp:2297 engines/agos/saveload.cpp:210 +#: engines/scumm/scumm.cpp:2314 engines/agos/saveload.cpp:228 #, c-format msgid "" "Successfully saved game state in file:\n" @@ -2030,7 +2065,7 @@ msgstr "" "\n" "%s" -#: engines/scumm/scumm.cpp:2512 +#: engines/scumm/scumm.cpp:2529 msgid "" "Usually, Maniac Mansion would start now. But ScummVM doesn't do that yet. To " "play it, go to 'Add Game' in the ScummVM start menu and select the 'Maniac' " @@ -2066,17 +2101,17 @@ msgstr "~M~enú Principal" msgid "~W~ater Effect Enabled" msgstr "~E~fecte de l'aigua activat" -#: engines/agos/animation.cpp:560 +#: engines/agos/animation.cpp:557 #, c-format msgid "Cutscene file '%s' not found!" msgstr "No s'ha trobat el fitxer d'escena '%s'!" #: engines/gob/inter_playtoons.cpp:256 engines/gob/inter_v2.cpp:1287 -#: engines/tinsel/saveload.cpp:502 +#: engines/tinsel/saveload.cpp:532 msgid "Failed to load game state from file." msgstr "No s'ha pogut carregar l'estat del joc del fitxer." -#: engines/gob/inter_v2.cpp:1357 engines/tinsel/saveload.cpp:515 +#: engines/gob/inter_v2.cpp:1357 engines/tinsel/saveload.cpp:545 msgid "Failed to save game state to file." msgstr "No s'ha pogut desar l'estat del joc al fitxer." @@ -2092,118 +2127,109 @@ msgstr "No s'ha pogut desar l'estat del joc" #. Malcolm makes a joke. #: engines/kyra/detection.cpp:62 msgid "Studio audience" -msgstr "" +msgstr "Públic" #: engines/kyra/detection.cpp:63 msgid "Enable studio audience" -msgstr "" +msgstr "Activa el públic" #. I18N: This option allows the user to skip text and cutscenes. #: engines/kyra/detection.cpp:73 msgid "Skip support" -msgstr "" +msgstr "Suport per saltar text i escenes" #: engines/kyra/detection.cpp:74 msgid "Allow text and cutscenes to be skipped" -msgstr "" +msgstr "Permet que se saltin textos i escenes" #. I18N: Helium mode makes people sound like they've inhaled Helium. #: engines/kyra/detection.cpp:84 msgid "Helium mode" -msgstr "" +msgstr "Mode heli" #: engines/kyra/detection.cpp:85 -#, fuzzy msgid "Enable helium mode" -msgstr "Activa el Mode Roland GS" +msgstr "Activa el mode heli" #. I18N: When enabled, this option makes scrolling smoother when #. changing from one screen to another. #: engines/kyra/detection.cpp:99 msgid "Smooth scrolling" -msgstr "" +msgstr "Desplaçament suau" #: engines/kyra/detection.cpp:100 msgid "Enable smooth scrolling when walking" -msgstr "" +msgstr "Activa el desplaçament suau al caminar" #. I18N: When enabled, this option changes the cursor when it floats to the #. edge of the screen to a directional arrow. The player can then click to #. walk towards that direction. #: engines/kyra/detection.cpp:112 -#, fuzzy msgid "Floating cursors" -msgstr "Cursor normal" +msgstr "Cursor flotant" #: engines/kyra/detection.cpp:113 msgid "Enable floating cursors" -msgstr "" +msgstr "Activa els cursors flotants" #. I18N: HP stands for Hit Points #: engines/kyra/detection.cpp:127 msgid "HP bar graphs" -msgstr "" +msgstr "Barra gràfica de PI" #: engines/kyra/detection.cpp:128 msgid "Enable hit point bar graphs" -msgstr "" +msgstr "Activa la barra gràfica dels punts d'impacte" #: engines/kyra/lol.cpp:478 msgid "Attack 1" -msgstr "" +msgstr "Atac 1" #: engines/kyra/lol.cpp:479 msgid "Attack 2" -msgstr "" +msgstr "Atac 2" #: engines/kyra/lol.cpp:480 msgid "Attack 3" -msgstr "" +msgstr "Atac 3" #: engines/kyra/lol.cpp:481 msgid "Move Forward" -msgstr "" +msgstr "Mou endavant" #: engines/kyra/lol.cpp:482 msgid "Move Back" -msgstr "" +msgstr "Mou enrere" #: engines/kyra/lol.cpp:483 msgid "Slide Left" -msgstr "" +msgstr "Mou a l'esquerra" #: engines/kyra/lol.cpp:484 -#, fuzzy msgid "Slide Right" -msgstr "Dreta" +msgstr "Mou a la dreta" #: engines/kyra/lol.cpp:485 -#, fuzzy msgid "Turn Left" -msgstr "Apaga" +msgstr "Gira a l'esquerra" #: engines/kyra/lol.cpp:486 -#, fuzzy msgid "Turn Right" -msgstr "Cursor Dreta" +msgstr "Gira a la dreta" #: engines/kyra/lol.cpp:487 -#, fuzzy msgid "Rest" -msgstr "Restaura" +msgstr "Descansa" #: engines/kyra/lol.cpp:488 -#, fuzzy msgid "Options" -msgstr "~O~pcions" +msgstr "Opcions" #: engines/kyra/lol.cpp:489 -#, fuzzy msgid "Choose Spell" -msgstr "Escull" +msgstr "Escull l'encanteri" -#: engines/kyra/sound_midi.cpp:475 -#, fuzzy +#: engines/kyra/sound_midi.cpp:477 msgid "" "You appear to be using a General MIDI device,\n" "but your game only supports Roland MT32 MIDI.\n" @@ -2217,13 +2243,13 @@ msgstr "" "Roland MT32 als de General MIDI. És possible\n" "que algunes pistes no es reprodueixin correctament." -#: engines/queen/queen.cpp:59 engines/sky/detection.cpp:44 -msgid "Floppy intro" -msgstr "" +#: engines/queen/queen.cpp:59 +msgid "Alternative intro" +msgstr "Introducció alternativa" -#: engines/queen/queen.cpp:60 engines/sky/detection.cpp:45 -msgid "Use the floppy version's intro (CD version only)" -msgstr "" +#: engines/queen/queen.cpp:60 +msgid "Use an alternative game intro (CD version only)" +msgstr "Utilitza una introducció del joc alternativa (només per la versió CD)" #: engines/sky/compact.cpp:130 msgid "" @@ -2241,27 +2267,36 @@ msgstr "" "El fitxer \"sky.cpt\" té una mida incorrecta.\n" "Torneu a baixar-lo de www.scummvm.org" -#: engines/sword1/animation.cpp:539 +#: engines/sky/detection.cpp:44 +msgid "Floppy intro" +msgstr "Introducció de disquets" + +#: engines/sky/detection.cpp:45 +msgid "Use the floppy version's intro (CD version only)" +msgstr "" +"Utilitza la introducció de la versió de disquets (només per a la versió CD)" + +#: engines/sword1/animation.cpp:519 #, c-format msgid "PSX stream cutscene '%s' cannot be played in paletted mode" -msgstr "" +msgstr "L'escena '%s' de PSX no es pot reproduir en mode paleta" -#: engines/sword1/animation.cpp:560 engines/sword2/animation.cpp:455 +#: engines/sword1/animation.cpp:540 engines/sword2/animation.cpp:439 msgid "DXA cutscenes found but ScummVM has been built without zlib support" msgstr "" "S'han trobat escenes en DXA, però s'ha compilat el ScummVM sense suport de " "zlib" -#: engines/sword1/animation.cpp:570 engines/sword2/animation.cpp:465 +#: engines/sword1/animation.cpp:550 engines/sword2/animation.cpp:449 msgid "MPEG2 cutscenes are no longer supported" msgstr "Les escenes MPEG2 ja no estan suportades" -#: engines/sword1/animation.cpp:576 engines/sword2/animation.cpp:473 +#: engines/sword1/animation.cpp:556 engines/sword2/animation.cpp:457 #, c-format msgid "Cutscene '%s' not found" msgstr "No s'ha trobat l'escena '%s'" -#: engines/sword1/control.cpp:863 +#: engines/sword1/control.cpp:865 msgid "" "ScummVM found that you have old savefiles for Broken Sword 1 that should be " "converted.\n" @@ -2279,7 +2314,7 @@ msgstr "" "Premeu D'Acord per convertir-les ara, en cas contrari se us tornarà a " "demanar la propera vegada que engegueu el joc.\n" -#: engines/sword1/control.cpp:1232 +#: engines/sword1/control.cpp:1234 #, c-format msgid "" "Target new save game already exists!\n" @@ -2288,11 +2323,11 @@ msgstr "" "La nova partida guardada d'aquest joc ja existeix!\n" "Voleu conservar la partida guardada antiga (%s) o la nova (%s)?\n" -#: engines/sword1/control.cpp:1235 +#: engines/sword1/control.cpp:1237 msgid "Keep the old one" msgstr "Mantingues el vell" -#: engines/sword1/control.cpp:1235 +#: engines/sword1/control.cpp:1237 msgid "Keep the new one" msgstr "Mantingues el nou" @@ -2300,21 +2335,33 @@ msgstr "Mantingues el nou" msgid "This is the end of the Broken Sword 1 Demo" msgstr "Aquest és el final de la Demo del Broken Sword 1" -#: engines/sword2/animation.cpp:435 -#, fuzzy +#: engines/sword2/animation.cpp:419 msgid "" "PSX cutscenes found but ScummVM has been built without RGB color support" msgstr "" -"S'han trobat escenes en DXA, però s'ha compilat el ScummVM sense suport de " -"zlib" +"S'han trobat escenes de PSX, però s'ha compilat el ScummVM sense suport de " +"color RGB" #: engines/sword2/sword2.cpp:79 msgid "Show object labels" -msgstr "" +msgstr "Mostra les etiquetes dels objectes" #: engines/sword2/sword2.cpp:80 msgid "Show labels for objects on mouse hover" +msgstr "Mostra etiquetes al posar el ratolí sobre els objectes" + +#: engines/teenagent/resources.cpp:94 +msgid "" +"You're missing the 'teenagent.dat' file. Get it from the ScummVM website" +msgstr "Us falta el fitxer 'teenagent.dat'. Obteniu-lo a la pàgina de ScummVM" + +#: engines/teenagent/resources.cpp:115 +msgid "" +"The teenagent.dat file is compressed and zlib hasn't been included in this " +"executable. Please decompress it" msgstr "" +"El fitxer teenagent.dat està comprimit però aquest executable no conté zlib. " +"Descomprimiu-lo, si us plau." #: engines/parallaction/saveload.cpp:133 #, c-format @@ -2423,7 +2470,7 @@ msgstr "Sense música" msgid "Amiga Audio Emulator" msgstr "Emulador d'àudio Amiga" -#: audio/softsynth/adlib.cpp:1593 +#: audio/softsynth/adlib.cpp:2284 msgid "AdLib Emulator" msgstr "Emulador d'AdLib" @@ -2456,9 +2503,8 @@ msgid "Keymap:" msgstr "Assignacions de teclat:" #: backends/keymapper/remap-dialog.cpp:66 -#, fuzzy msgid " (Effective)" -msgstr " (Actiu)" +msgstr " (Efectiu)" #: backends/keymapper/remap-dialog.cpp:106 msgid " (Active)" @@ -2466,7 +2512,7 @@ msgstr " (Actiu)" #: backends/keymapper/remap-dialog.cpp:106 msgid " (Blocked)" -msgstr "" +msgstr " (Bloquejat)" #: backends/keymapper/remap-dialog.cpp:119 msgid " (Global)" @@ -2568,11 +2614,11 @@ msgstr "Mode Touchpad activat." msgid "Touchpad mode disabled." msgstr "Mode Touchpad desactivat." -#: backends/platform/maemo/maemo.cpp:205 +#: backends/platform/maemo/maemo.cpp:209 msgid "Click Mode" -msgstr "" +msgstr "Mode clic" -#: backends/platform/maemo/maemo.cpp:211 +#: backends/platform/maemo/maemo.cpp:215 #: backends/platform/symbian/src/SymbianActions.cpp:42 #: backends/platform/wince/CEActionsPocket.cpp:60 #: backends/platform/wince/CEActionsSmartphone.cpp:43 @@ -2580,36 +2626,35 @@ msgstr "" msgid "Left Click" msgstr "Clic esquerre" -#: backends/platform/maemo/maemo.cpp:214 -#, fuzzy +#: backends/platform/maemo/maemo.cpp:218 msgid "Middle Click" -msgstr "Element mig esquerre" +msgstr "Clic central" -#: backends/platform/maemo/maemo.cpp:217 +#: backends/platform/maemo/maemo.cpp:221 #: backends/platform/symbian/src/SymbianActions.cpp:43 #: backends/platform/wince/CEActionsSmartphone.cpp:44 #: backends/platform/bada/form.cpp:273 msgid "Right Click" msgstr "Clic dret" -#: backends/platform/sdl/macosx/appmenu_osx.mm:78 +#: backends/platform/sdl/macosx/appmenu_osx.mm:77 msgid "Hide ScummVM" msgstr "Amaga ScummVM" -#: backends/platform/sdl/macosx/appmenu_osx.mm:83 +#: backends/platform/sdl/macosx/appmenu_osx.mm:82 msgid "Hide Others" msgstr "Oculta els altres" -#: backends/platform/sdl/macosx/appmenu_osx.mm:88 +#: backends/platform/sdl/macosx/appmenu_osx.mm:87 msgid "Show All" msgstr "Mostra-ho tot" -#: backends/platform/sdl/macosx/appmenu_osx.mm:110 -#: backends/platform/sdl/macosx/appmenu_osx.mm:121 +#: backends/platform/sdl/macosx/appmenu_osx.mm:109 +#: backends/platform/sdl/macosx/appmenu_osx.mm:120 msgid "Window" msgstr "Finestra" -#: backends/platform/sdl/macosx/appmenu_osx.mm:115 +#: backends/platform/sdl/macosx/appmenu_osx.mm:114 msgid "Minimize" msgstr "Minimitza" @@ -2989,41 +3034,46 @@ msgstr "Llançador" msgid "Do you really want to quit?" msgstr "Estàs segur de voler sortir?" -#: backends/events/gph/gph-events.cpp:338 -#: backends/events/gph/gph-events.cpp:381 -#: backends/events/openpandora/op-events.cpp:139 +#: backends/events/gph/gph-events.cpp:386 +#: backends/events/gph/gph-events.cpp:429 +#: backends/events/openpandora/op-events.cpp:168 msgid "Touchscreen 'Tap Mode' - Left Click" msgstr "'Mode Toc' de pantalla tàctil - Clic esquerre" -#: backends/events/gph/gph-events.cpp:340 -#: backends/events/gph/gph-events.cpp:383 -#: backends/events/openpandora/op-events.cpp:141 +#: backends/events/gph/gph-events.cpp:388 +#: backends/events/gph/gph-events.cpp:431 +#: backends/events/openpandora/op-events.cpp:170 msgid "Touchscreen 'Tap Mode' - Right Click" msgstr "'Mode Toc' de pantalla tàctil - Clic dret" -#: backends/events/gph/gph-events.cpp:342 -#: backends/events/gph/gph-events.cpp:385 -#: backends/events/openpandora/op-events.cpp:143 +#: backends/events/gph/gph-events.cpp:390 +#: backends/events/gph/gph-events.cpp:433 +#: backends/events/openpandora/op-events.cpp:172 msgid "Touchscreen 'Tap Mode' - Hover (No Click)" msgstr "'Mode Toc' de pantalla tàctil - Flotant (sense clic)" -#: backends/events/gph/gph-events.cpp:362 +#: backends/events/gph/gph-events.cpp:410 msgid "Maximum Volume" msgstr "Volum màxim" -#: backends/events/gph/gph-events.cpp:364 +#: backends/events/gph/gph-events.cpp:412 msgid "Increasing Volume" msgstr "Pujant el volum" -#: backends/events/gph/gph-events.cpp:370 +#: backends/events/gph/gph-events.cpp:418 msgid "Minimal Volume" msgstr "Volum mínim" -#: backends/events/gph/gph-events.cpp:372 +#: backends/events/gph/gph-events.cpp:420 msgid "Decreasing Volume" msgstr "Baixant el volum" -#: backends/updates/macosx/macosx-updates.mm:65 +#: backends/events/openpandora/op-events.cpp:174 +#, fuzzy +msgid "Touchscreen 'Tap Mode' - Hover (DPad Clicks)" +msgstr "'Mode Toc' de pantalla tàctil - Flotant (sense clic)" + +#: backends/updates/macosx/macosx-updates.mm:67 msgid "Check for Updates..." msgstr "Comprova les actualitzacions..." @@ -3058,20 +3108,3 @@ msgstr "Clicat activat" #: backends/events/maemosdl/maemosdl-events.cpp:192 msgid "Clicking Disabled" msgstr "Clicat desactivat" - -#~ msgid "Hercules Green" -#~ msgstr "Hercules Verd" - -#~ msgid "Hercules Amber" -#~ msgstr "Hercules Àmbar" - -#~ msgctxt "lowres" -#~ msgid "Hercules Green" -#~ msgstr "Hercules Verd" - -#~ msgctxt "lowres" -#~ msgid "Hercules Amber" -#~ msgstr "Hercules Àmbar" - -#~ msgid "Save game failed!" -#~ msgstr "No s'ha pogut desar la partida!" diff --git a/po/cs_CZ.po b/po/cs_CZ.po index a2d640651c..5b1dc7ec52 100644 --- a/po/cs_CZ.po +++ b/po/cs_CZ.po @@ -1,5 +1,5 @@ # Czech translation for ScummVM. -# Copyright (C) 2001-2011 ScummVM Team +# Copyright (C) 2011-2013 ScummVM Team # This file is distributed under the same license as the ScummVM package. # Zbynìk Schwarz <zbynek.schwarz@gmail.com>, 2011. # @@ -7,14 +7,14 @@ msgid "" msgstr "" "Project-Id-Version: ScummVM 1.4.0git\n" "Report-Msgid-Bugs-To: scummvm-devel@lists.sf.net\n" -"POT-Creation-Date: 2012-07-08 12:25+0100\n" +"POT-Creation-Date: 2012-12-01 17:22+0000\n" "PO-Revision-Date: 2012-07-08 18:03+0100\n" "Last-Translator: Zbynìk Schwarz <zbynek.schwarz@gmail.com>\n" "Language-Team: \n" +"Language: Cesky\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=iso-8859-2\n" "Content-Transfer-Encoding: 8bit\n" -"Language: Cesky\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2\n" "X-Poedit-Language: Czech\n" "X-Poedit-Country: CZECH REPUBLIC\n" @@ -48,10 +48,11 @@ msgstr "Jít nahoru" #: gui/browser.cpp:69 gui/chooser.cpp:45 gui/KeysDialog.cpp:43 #: gui/launcher.cpp:345 gui/massadd.cpp:94 gui/options.cpp:1228 -#: gui/saveload.cpp:64 gui/saveload.cpp:173 gui/themebrowser.cpp:54 -#: engines/engine.cpp:442 engines/scumm/dialogs.cpp:190 -#: engines/sword1/control.cpp:865 engines/parallaction/saveload.cpp:274 -#: backends/platform/wii/options.cpp:48 +#: gui/saveload-dialog.cpp:215 gui/saveload-dialog.cpp:275 +#: gui/saveload-dialog.cpp:545 gui/saveload-dialog.cpp:919 +#: gui/themebrowser.cpp:54 engines/engine.cpp:442 +#: engines/scumm/dialogs.cpp:190 engines/sword1/control.cpp:867 +#: engines/parallaction/saveload.cpp:274 backends/platform/wii/options.cpp:48 #: backends/events/default/default-events.cpp:191 #: backends/events/default/default-events.cpp:213 msgid "Cancel" @@ -72,15 +73,15 @@ msgstr "Zavøít" msgid "Mouse click" msgstr "Kliknutí my¹í" -#: gui/gui-manager.cpp:122 base/main.cpp:300 +#: gui/gui-manager.cpp:122 base/main.cpp:301 msgid "Display keyboard" msgstr "Zobrazit klávesnici" -#: gui/gui-manager.cpp:126 base/main.cpp:304 +#: gui/gui-manager.cpp:126 base/main.cpp:305 msgid "Remap keys" msgstr "Pøemapovat klávesy" -#: gui/gui-manager.cpp:129 base/main.cpp:307 +#: gui/gui-manager.cpp:129 base/main.cpp:308 msgid "Toggle FullScreen" msgstr "Pøepnout celou obrazovku" @@ -94,16 +95,16 @@ msgstr "Mapovat" #: gui/KeysDialog.cpp:42 gui/launcher.cpp:346 gui/launcher.cpp:1001 #: gui/launcher.cpp:1005 gui/massadd.cpp:91 gui/options.cpp:1229 -#: engines/engine.cpp:361 engines/engine.cpp:372 engines/scumm/dialogs.cpp:192 -#: engines/scumm/scumm.cpp:1775 engines/agos/animation.cpp:561 -#: engines/groovie/script.cpp:420 engines/sky/compact.cpp:131 -#: engines/sky/compact.cpp:141 engines/sword1/animation.cpp:539 -#: engines/sword1/animation.cpp:560 engines/sword1/animation.cpp:570 -#: engines/sword1/animation.cpp:577 engines/sword1/control.cpp:865 -#: engines/sword1/logic.cpp:1633 engines/sword2/animation.cpp:435 -#: engines/sword2/animation.cpp:455 engines/sword2/animation.cpp:465 -#: engines/sword2/animation.cpp:474 engines/parallaction/saveload.cpp:274 -#: backends/platform/wii/options.cpp:47 +#: gui/saveload-dialog.cpp:920 engines/engine.cpp:361 engines/engine.cpp:372 +#: engines/scumm/dialogs.cpp:192 engines/scumm/scumm.cpp:1776 +#: engines/agos/animation.cpp:558 engines/groovie/script.cpp:420 +#: engines/sky/compact.cpp:131 engines/sky/compact.cpp:141 +#: engines/sword1/animation.cpp:519 engines/sword1/animation.cpp:540 +#: engines/sword1/animation.cpp:550 engines/sword1/animation.cpp:557 +#: engines/sword1/control.cpp:867 engines/sword1/logic.cpp:1633 +#: engines/sword2/animation.cpp:419 engines/sword2/animation.cpp:439 +#: engines/sword2/animation.cpp:449 engines/sword2/animation.cpp:458 +#: engines/parallaction/saveload.cpp:274 backends/platform/wii/options.cpp:47 #: backends/platform/wince/CELauncherDialog.cpp:54 msgid "OK" msgstr "OK" @@ -356,7 +357,7 @@ msgstr "Toto ID hry je u¾ zabrané. Vyberte si, prosím, jiné." msgid "~Q~uit" msgstr "~U~konèit" -#: gui/launcher.cpp:621 backends/platform/sdl/macosx/appmenu_osx.mm:96 +#: gui/launcher.cpp:621 backends/platform/sdl/macosx/appmenu_osx.mm:95 msgid "Quit ScummVM" msgstr "Ukonèit ScummVM" @@ -364,7 +365,7 @@ msgstr "Ukonèit ScummVM" msgid "A~b~out..." msgstr "~O~ Programu..." -#: gui/launcher.cpp:622 backends/platform/sdl/macosx/appmenu_osx.mm:70 +#: gui/launcher.cpp:622 backends/platform/sdl/macosx/appmenu_osx.mm:69 msgid "About ScummVM" msgstr "O ScummVM" @@ -439,13 +440,13 @@ msgstr "Hledat v seznamu her" msgid "Search:" msgstr "Hledat:" -#: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/mohawk/myst.cpp:255 +#: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/mohawk/myst.cpp:245 #: engines/mohawk/riven.cpp:716 engines/cruise/menu.cpp:214 msgid "Load game:" msgstr "Nahrát hru:" #: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/scumm/dialogs.cpp:188 -#: engines/mohawk/myst.cpp:255 engines/mohawk/riven.cpp:716 +#: engines/mohawk/myst.cpp:245 engines/mohawk/riven.cpp:716 #: engines/cruise/menu.cpp:214 backends/platform/wince/CEActionsPocket.cpp:267 #: backends/platform/wince/CEActionsSmartphone.cpp:231 msgid "Load" @@ -919,68 +920,104 @@ msgstr "" "Vzhled, který jste zvolili, nepodporuje Vá¹ souèasný jazyk. Pokud chcete " "tento vzhled pou¾ít, musíte nejdøíve pøepnout na jiný jazyk." -#: gui/saveload.cpp:59 gui/saveload.cpp:257 +#: gui/saveload-dialog.cpp:166 +msgid "List view" +msgstr "" + +#: gui/saveload-dialog.cpp:167 +msgid "Grid view" +msgstr "" + +#: gui/saveload-dialog.cpp:210 gui/saveload-dialog.cpp:358 msgid "No date saved" msgstr "Neulo¾ena ¾ádná data" -#: gui/saveload.cpp:60 gui/saveload.cpp:258 +#: gui/saveload-dialog.cpp:211 gui/saveload-dialog.cpp:359 msgid "No time saved" msgstr "®ádný ulo¾ený èas" -#: gui/saveload.cpp:61 gui/saveload.cpp:259 +#: gui/saveload-dialog.cpp:212 gui/saveload-dialog.cpp:360 msgid "No playtime saved" msgstr "®ádná ulo¾ená doba hraní" -#: gui/saveload.cpp:68 gui/saveload.cpp:173 +#: gui/saveload-dialog.cpp:219 gui/saveload-dialog.cpp:275 msgid "Delete" msgstr "Smazat" -#: gui/saveload.cpp:172 +#: gui/saveload-dialog.cpp:274 msgid "Do you really want to delete this savegame?" msgstr "Opravdu chcete tuto ulo¾enou hru vymazat" -#: gui/saveload.cpp:282 +#: gui/saveload-dialog.cpp:383 gui/saveload-dialog.cpp:872 msgid "Date: " msgstr "Datum:" -#: gui/saveload.cpp:286 +#: gui/saveload-dialog.cpp:387 gui/saveload-dialog.cpp:878 msgid "Time: " msgstr "Èas:" -#: gui/saveload.cpp:292 +#: gui/saveload-dialog.cpp:393 gui/saveload-dialog.cpp:886 msgid "Playtime: " msgstr "Doba hraní:" -#: gui/saveload.cpp:305 gui/saveload.cpp:372 +#: gui/saveload-dialog.cpp:406 gui/saveload-dialog.cpp:494 msgid "Untitled savestate" msgstr "Bezejmenný ulo¾ený stav" +#: gui/saveload-dialog.cpp:546 +msgid "Next" +msgstr "" + +#: gui/saveload-dialog.cpp:549 +msgid "Prev" +msgstr "" + +#: gui/saveload-dialog.cpp:736 +#, fuzzy +msgid "New Save" +msgstr "Ulo¾it" + +#: gui/saveload-dialog.cpp:736 +#, fuzzy +msgid "Create a new save game" +msgstr "Nelze ulo¾it hru." + +#: gui/saveload-dialog.cpp:865 +#, fuzzy +msgid "Name: " +msgstr "Jméno" + +#: gui/saveload-dialog.cpp:937 +#, c-format +msgid "Enter a description for slot %d:" +msgstr "" + #: gui/themebrowser.cpp:44 msgid "Select a Theme" msgstr "Vyberte Vzhled" -#: gui/ThemeEngine.cpp:335 +#: gui/ThemeEngine.cpp:337 msgid "Disabled GFX" msgstr "GFX zakázáno" -#: gui/ThemeEngine.cpp:335 +#: gui/ThemeEngine.cpp:337 msgctxt "lowres" msgid "Disabled GFX" msgstr "GFX zakázáno" -#: gui/ThemeEngine.cpp:336 +#: gui/ThemeEngine.cpp:338 msgid "Standard Renderer (16bpp)" msgstr "Standardní Vykreslovaè (16bpp)" -#: gui/ThemeEngine.cpp:336 +#: gui/ThemeEngine.cpp:338 msgid "Standard (16bpp)" msgstr "Standardní (16bpp)" -#: gui/ThemeEngine.cpp:338 +#: gui/ThemeEngine.cpp:340 msgid "Antialiased Renderer (16bpp)" msgstr "Vykreslovaè s vyhlazenými hranami (16bpp)" -#: gui/ThemeEngine.cpp:338 +#: gui/ThemeEngine.cpp:340 msgid "Antialiased (16bpp)" msgstr "S vyhlazenými hranami (16bpp)" @@ -988,35 +1025,35 @@ msgstr "S vyhlazenými hranami (16bpp)" msgid "Clear value" msgstr "Vyèistit hodnotu" -#: base/main.cpp:209 +#: base/main.cpp:210 #, c-format msgid "Engine does not support debug level '%s'" msgstr "Jádro nepodporuje úroveò ladìní '%s'" -#: base/main.cpp:287 +#: base/main.cpp:288 msgid "Menu" msgstr "Menu" -#: base/main.cpp:290 backends/platform/symbian/src/SymbianActions.cpp:45 +#: base/main.cpp:291 backends/platform/symbian/src/SymbianActions.cpp:45 #: backends/platform/wince/CEActionsPocket.cpp:45 #: backends/platform/wince/CEActionsSmartphone.cpp:46 msgid "Skip" msgstr "Pøeskoèit" -#: base/main.cpp:293 backends/platform/symbian/src/SymbianActions.cpp:50 +#: base/main.cpp:294 backends/platform/symbian/src/SymbianActions.cpp:50 #: backends/platform/wince/CEActionsPocket.cpp:42 msgid "Pause" msgstr "Pauza" -#: base/main.cpp:296 +#: base/main.cpp:297 msgid "Skip line" msgstr "Pøeskoèit øádek" -#: base/main.cpp:467 +#: base/main.cpp:468 msgid "Error running game:" msgstr "Chyba pøi spu¹tìní hry:" -#: base/main.cpp:491 +#: base/main.cpp:492 msgid "Could not find any engine capable of running the selected game" msgstr "Nelze nalézt ¾ádné jádro schopné vybranou hru spustit" @@ -1084,16 +1121,16 @@ msgstr "Zru¹eno u¾ivatelem" msgid "Unknown error" msgstr "Neznámá chyba" -#: engines/advancedDetector.cpp:324 +#: engines/advancedDetector.cpp:316 #, c-format msgid "The game in '%s' seems to be unknown." msgstr "Hra v '%s' se zdá být neznámá." -#: engines/advancedDetector.cpp:325 +#: engines/advancedDetector.cpp:317 msgid "Please, report the following data to the ScummVM team along with name" msgstr "Prosím nahlaste následující data týmu ScummVM spolu se jménem" -#: engines/advancedDetector.cpp:327 +#: engines/advancedDetector.cpp:319 msgid "of the game you tried to add and its version/language/etc.:" msgstr "hry, kterou jste se pokusili pøidat a její verzi/jazyk/atd.:" @@ -1131,13 +1168,13 @@ msgid "~R~eturn to Launcher" msgstr "~N~ávrat do Spou¹tìèe" #: engines/dialogs.cpp:115 engines/agi/saveload.cpp:803 -#: engines/cruise/menu.cpp:212 engines/sci/engine/kfile.cpp:735 +#: engines/cruise/menu.cpp:212 engines/sci/engine/kfile.cpp:742 msgid "Save game:" msgstr "Ulo¾it hru:" #: engines/dialogs.cpp:115 engines/agi/saveload.cpp:803 #: engines/scumm/dialogs.cpp:187 engines/cruise/menu.cpp:212 -#: engines/sci/engine/kfile.cpp:735 +#: engines/sci/engine/kfile.cpp:742 #: backends/platform/symbian/src/SymbianActions.cpp:44 #: backends/platform/wince/CEActionsPocket.cpp:43 #: backends/platform/wince/CEActionsPocket.cpp:267 @@ -1246,21 +1283,21 @@ msgstr "" msgid "Start anyway" msgstr "Pøesto spustit" -#: engines/agi/detection.cpp:145 engines/dreamweb/detection.cpp:47 -#: engines/sci/detection.cpp:390 +#: engines/agi/detection.cpp:142 engines/dreamweb/detection.cpp:47 +#: engines/sci/detection.cpp:393 msgid "Use original save/load screens" msgstr "Pou¾ít pùvodní obrazovky naètení/ulo¾ení" -#: engines/agi/detection.cpp:146 engines/dreamweb/detection.cpp:48 -#: engines/sci/detection.cpp:391 +#: engines/agi/detection.cpp:143 engines/dreamweb/detection.cpp:48 +#: engines/sci/detection.cpp:394 msgid "Use the original save/load screens, instead of the ScummVM ones" msgstr "Pou¾ít pùvodní obrazovky naètení/ulo¾ení místo ze ScummVM" -#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:831 +#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:838 msgid "Restore game:" msgstr "Obnovit hru" -#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:831 +#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:838 msgid "Restore" msgstr "Obnovit" @@ -1272,27 +1309,27 @@ msgstr "Pou¾ít re¾im jasné palety" msgid "Display graphics using the game's bright palette" msgstr "Zobrazit grafiku pomocí jasné palety hry" -#: engines/sci/detection.cpp:370 +#: engines/sci/detection.cpp:373 msgid "EGA undithering" msgstr "Nerozkládání EGA" -#: engines/sci/detection.cpp:371 +#: engines/sci/detection.cpp:374 msgid "Enable undithering in EGA games" msgstr "Povolit nerozkládání v EGA hrách" -#: engines/sci/detection.cpp:380 +#: engines/sci/detection.cpp:383 msgid "Prefer digital sound effects" msgstr "Upøednostòovat digitální zvukové efekty" -#: engines/sci/detection.cpp:381 +#: engines/sci/detection.cpp:384 msgid "Prefer digital sound effects instead of synthesized ones" msgstr "Upøednostòovat digitální zvukové efekty pøed syntetizovanými" -#: engines/sci/detection.cpp:400 +#: engines/sci/detection.cpp:403 msgid "Use IMF/Yamaha FB-01 for MIDI output" msgstr "Pou¾ít IMF/Yamaha FB-01 pro výstup MIDI" -#: engines/sci/detection.cpp:401 +#: engines/sci/detection.cpp:404 msgid "" "Use an IBM Music Feature card or a Yamaha FB-01 FM synth module for MIDI " "output" @@ -1300,28 +1337,28 @@ msgstr "" "Pou¾ít kartu IBM Music Feature nebo modul syntetizátoru Yamaha FB-01 FM pro " "výstup MIDI" -#: engines/sci/detection.cpp:411 +#: engines/sci/detection.cpp:414 msgid "Use CD audio" msgstr "Pou¾ít zvuky na CD" -#: engines/sci/detection.cpp:412 +#: engines/sci/detection.cpp:415 msgid "Use CD audio instead of in-game audio, if available" msgstr "Pou¾ít zvuky na CD místo ve høe, pokud je dostupné" -#: engines/sci/detection.cpp:422 +#: engines/sci/detection.cpp:425 msgid "Use Windows cursors" msgstr "Pou¾ít kurzory Windows" -#: engines/sci/detection.cpp:423 +#: engines/sci/detection.cpp:426 msgid "" "Use the Windows cursors (smaller and monochrome) instead of the DOS ones" msgstr "Pou¾ít kurzory Windows (men¹í a èernobílé) místo kurzorù z DOS" -#: engines/sci/detection.cpp:433 +#: engines/sci/detection.cpp:436 msgid "Use silver cursors" msgstr "Pou¾ít støíbrné kurzory" -#: engines/sci/detection.cpp:434 +#: engines/sci/detection.cpp:437 msgid "" "Use the alternate set of silver cursors, instead of the normal golden ones" msgstr "Pou¾ít alternativní sadu støíbrných kurzorù místo standardních zlatých" @@ -1973,7 +2010,7 @@ msgstr "Letìt doprava" msgid "Fly to lower right" msgstr "Letìt doprava dolù" -#: engines/scumm/scumm.cpp:1773 +#: engines/scumm/scumm.cpp:1774 #, c-format msgid "" "Native MIDI support requires the Roland Upgrade from LucasArts,\n" @@ -1982,7 +2019,7 @@ msgstr "" "Pøirozená podpora MIDI vy¾aduje Aktualizaci Roland od LucasArts,\n" "ale %s chybí. Místo toho je pou¾it AdLib." -#: engines/scumm/scumm.cpp:2278 engines/agos/saveload.cpp:202 +#: engines/scumm/scumm.cpp:2295 engines/agos/saveload.cpp:220 #, c-format msgid "" "Failed to save game state to file:\n" @@ -1993,7 +2030,7 @@ msgstr "" "\n" "%s" -#: engines/scumm/scumm.cpp:2285 engines/agos/saveload.cpp:167 +#: engines/scumm/scumm.cpp:2302 engines/agos/saveload.cpp:185 #, c-format msgid "" "Failed to load game state from file:\n" @@ -2004,7 +2041,7 @@ msgstr "" "\n" "%s" -#: engines/scumm/scumm.cpp:2297 engines/agos/saveload.cpp:210 +#: engines/scumm/scumm.cpp:2314 engines/agos/saveload.cpp:228 #, c-format msgid "" "Successfully saved game state in file:\n" @@ -2015,7 +2052,7 @@ msgstr "" "\n" "%s" -#: engines/scumm/scumm.cpp:2512 +#: engines/scumm/scumm.cpp:2529 msgid "" "Usually, Maniac Mansion would start now. But ScummVM doesn't do that yet. To " "play it, go to 'Add Game' in the ScummVM start menu and select the 'Maniac' " @@ -2051,17 +2088,17 @@ msgstr "~H~lavní Menu" msgid "~W~ater Effect Enabled" msgstr "~E~fekt Vody Zapnut" -#: engines/agos/animation.cpp:560 +#: engines/agos/animation.cpp:557 #, c-format msgid "Cutscene file '%s' not found!" msgstr "Soubor videa '%s' nenalezen'" #: engines/gob/inter_playtoons.cpp:256 engines/gob/inter_v2.cpp:1287 -#: engines/tinsel/saveload.cpp:502 +#: engines/tinsel/saveload.cpp:532 msgid "Failed to load game state from file." msgstr "Nelze naèíst stav hry ze souboru." -#: engines/gob/inter_v2.cpp:1357 engines/tinsel/saveload.cpp:515 +#: engines/gob/inter_v2.cpp:1357 engines/tinsel/saveload.cpp:545 msgid "Failed to save game state to file." msgstr "Nelze ulo¾it stav hry do souboru." @@ -2179,7 +2216,7 @@ msgstr "Volby" msgid "Choose Spell" msgstr "Zvolit Kouzlo" -#: engines/kyra/sound_midi.cpp:475 +#: engines/kyra/sound_midi.cpp:477 msgid "" "You appear to be using a General MIDI device,\n" "but your game only supports Roland MT32 MIDI.\n" @@ -2193,12 +2230,13 @@ msgstr "" "ty od General MIDI. Je stále mo¾né, ¾e\n" "nìkteré stopy nebudou znít správnì." -#: engines/queen/queen.cpp:59 engines/sky/detection.cpp:44 -msgid "Floppy intro" -msgstr "Úvod z diskety" +#: engines/queen/queen.cpp:59 +msgid "Alternative intro" +msgstr "" -#: engines/queen/queen.cpp:60 engines/sky/detection.cpp:45 -msgid "Use the floppy version's intro (CD version only)" +#: engines/queen/queen.cpp:60 +#, fuzzy +msgid "Use an alternative game intro (CD version only)" msgstr "Pou¾ít verzi úvodu z diskety (Pouze verze CD)" #: engines/sky/compact.cpp:130 @@ -2217,25 +2255,33 @@ msgstr "" "Soubor \"sky.cpt\" má nesprávnou velikost.\n" "Stáhnìte si ho, prosím, (znovu) z www.scummvm.org" -#: engines/sword1/animation.cpp:539 +#: engines/sky/detection.cpp:44 +msgid "Floppy intro" +msgstr "Úvod z diskety" + +#: engines/sky/detection.cpp:45 +msgid "Use the floppy version's intro (CD version only)" +msgstr "Pou¾ít verzi úvodu z diskety (Pouze verze CD)" + +#: engines/sword1/animation.cpp:519 #, c-format msgid "PSX stream cutscene '%s' cannot be played in paletted mode" msgstr "Proud videa PSX '%s' nemù¾e být pøehrán v re¾imu palety" -#: engines/sword1/animation.cpp:560 engines/sword2/animation.cpp:455 +#: engines/sword1/animation.cpp:540 engines/sword2/animation.cpp:439 msgid "DXA cutscenes found but ScummVM has been built without zlib support" msgstr "Videa DXA nalezena, ale ScummVM byl sestaven bez podpory zlib" -#: engines/sword1/animation.cpp:570 engines/sword2/animation.cpp:465 +#: engines/sword1/animation.cpp:550 engines/sword2/animation.cpp:449 msgid "MPEG2 cutscenes are no longer supported" msgstr "Videa MPGE2 ji¾ nejsou podporována" -#: engines/sword1/animation.cpp:576 engines/sword2/animation.cpp:473 +#: engines/sword1/animation.cpp:556 engines/sword2/animation.cpp:457 #, c-format msgid "Cutscene '%s' not found" msgstr "Video '%s' nenalezeno" -#: engines/sword1/control.cpp:863 +#: engines/sword1/control.cpp:865 msgid "" "ScummVM found that you have old savefiles for Broken Sword 1 that should be " "converted.\n" @@ -2253,7 +2299,7 @@ msgstr "" "Stisknìte OK, abyste je pøevedli teï, jinak budete po¾ádáni znovu, pøi " "spu¹tìní této hry.\n" -#: engines/sword1/control.cpp:1232 +#: engines/sword1/control.cpp:1234 #, c-format msgid "" "Target new save game already exists!\n" @@ -2262,11 +2308,11 @@ msgstr "" "Nová cílová ulo¾ená hra ji¾ existuje!\n" "Chtìli byste ponechat starou ulo¾enou hru (%s), nebo novou (%s)?\n" -#: engines/sword1/control.cpp:1235 +#: engines/sword1/control.cpp:1237 msgid "Keep the old one" msgstr "Ponechat starou" -#: engines/sword1/control.cpp:1235 +#: engines/sword1/control.cpp:1237 msgid "Keep the new one" msgstr "Ponechat novou" @@ -2274,7 +2320,7 @@ msgstr "Ponechat novou" msgid "This is the end of the Broken Sword 1 Demo" msgstr "Toto je konec Dema Broken Sword 1" -#: engines/sword2/animation.cpp:435 +#: engines/sword2/animation.cpp:419 msgid "" "PSX cutscenes found but ScummVM has been built without RGB color support" msgstr "Videa PSX nalezena, ale ScummVM byl sestaven bez podpory barev RGB" @@ -2287,6 +2333,17 @@ msgstr "Zobrazit jmenovky objektù" msgid "Show labels for objects on mouse hover" msgstr "Zobrazit jmenovky objektù pøi najetí my¹i" +#: engines/teenagent/resources.cpp:94 +msgid "" +"You're missing the 'teenagent.dat' file. Get it from the ScummVM website" +msgstr "" + +#: engines/teenagent/resources.cpp:115 +msgid "" +"The teenagent.dat file is compressed and zlib hasn't been included in this " +"executable. Please decompress it" +msgstr "" + #: engines/parallaction/saveload.cpp:133 #, c-format msgid "" @@ -2393,7 +2450,7 @@ msgstr "Bez hudby" msgid "Amiga Audio Emulator" msgstr "Emulátor zvuku Amiga" -#: audio/softsynth/adlib.cpp:1593 +#: audio/softsynth/adlib.cpp:2284 msgid "AdLib Emulator" msgstr "AdLib Emulátor" @@ -2537,11 +2594,11 @@ msgstr "Touchpad re¾im zapnut" msgid "Touchpad mode disabled." msgstr "Touchpad re¾im vypnut" -#: backends/platform/maemo/maemo.cpp:205 +#: backends/platform/maemo/maemo.cpp:209 msgid "Click Mode" msgstr "Re¾im kliknutí" -#: backends/platform/maemo/maemo.cpp:211 +#: backends/platform/maemo/maemo.cpp:215 #: backends/platform/symbian/src/SymbianActions.cpp:42 #: backends/platform/wince/CEActionsPocket.cpp:60 #: backends/platform/wince/CEActionsSmartphone.cpp:43 @@ -2549,35 +2606,35 @@ msgstr "Re¾im kliknutí" msgid "Left Click" msgstr "Levé Kliknutí" -#: backends/platform/maemo/maemo.cpp:214 +#: backends/platform/maemo/maemo.cpp:218 msgid "Middle Click" msgstr "Kliknutí prostøedním tlaèítkem" -#: backends/platform/maemo/maemo.cpp:217 +#: backends/platform/maemo/maemo.cpp:221 #: backends/platform/symbian/src/SymbianActions.cpp:43 #: backends/platform/wince/CEActionsSmartphone.cpp:44 #: backends/platform/bada/form.cpp:273 msgid "Right Click" msgstr "Pravé kliknutí" -#: backends/platform/sdl/macosx/appmenu_osx.mm:78 +#: backends/platform/sdl/macosx/appmenu_osx.mm:77 msgid "Hide ScummVM" msgstr "Skrýt ScummVM" -#: backends/platform/sdl/macosx/appmenu_osx.mm:83 +#: backends/platform/sdl/macosx/appmenu_osx.mm:82 msgid "Hide Others" msgstr "Skrýt Ostatní" -#: backends/platform/sdl/macosx/appmenu_osx.mm:88 +#: backends/platform/sdl/macosx/appmenu_osx.mm:87 msgid "Show All" msgstr "Zobrazit V¹e" -#: backends/platform/sdl/macosx/appmenu_osx.mm:110 -#: backends/platform/sdl/macosx/appmenu_osx.mm:121 +#: backends/platform/sdl/macosx/appmenu_osx.mm:109 +#: backends/platform/sdl/macosx/appmenu_osx.mm:120 msgid "Window" msgstr "Okno" -#: backends/platform/sdl/macosx/appmenu_osx.mm:115 +#: backends/platform/sdl/macosx/appmenu_osx.mm:114 msgid "Minimize" msgstr "Minimalizovat" @@ -2959,41 +3016,46 @@ msgstr "Spou¹tìè" msgid "Do you really want to quit?" msgstr "Opravdu chcete skonèit?" -#: backends/events/gph/gph-events.cpp:338 -#: backends/events/gph/gph-events.cpp:381 -#: backends/events/openpandora/op-events.cpp:139 +#: backends/events/gph/gph-events.cpp:386 +#: backends/events/gph/gph-events.cpp:429 +#: backends/events/openpandora/op-events.cpp:168 msgid "Touchscreen 'Tap Mode' - Left Click" msgstr "'Re¾im «uknutí' Dotykové Obrazovky - Levé Kliknutí" -#: backends/events/gph/gph-events.cpp:340 -#: backends/events/gph/gph-events.cpp:383 -#: backends/events/openpandora/op-events.cpp:141 +#: backends/events/gph/gph-events.cpp:388 +#: backends/events/gph/gph-events.cpp:431 +#: backends/events/openpandora/op-events.cpp:170 msgid "Touchscreen 'Tap Mode' - Right Click" msgstr "'Re¾im «uknutí' Dotykové Obrazovky - Pravé Kliknutí" -#: backends/events/gph/gph-events.cpp:342 -#: backends/events/gph/gph-events.cpp:385 -#: backends/events/openpandora/op-events.cpp:143 +#: backends/events/gph/gph-events.cpp:390 +#: backends/events/gph/gph-events.cpp:433 +#: backends/events/openpandora/op-events.cpp:172 msgid "Touchscreen 'Tap Mode' - Hover (No Click)" msgstr "'Re¾im «uknutí' Dotykové Obrazovky - Najetí (Bez Kliknutí)" -#: backends/events/gph/gph-events.cpp:362 +#: backends/events/gph/gph-events.cpp:410 msgid "Maximum Volume" msgstr "Maximální Hlasitost" -#: backends/events/gph/gph-events.cpp:364 +#: backends/events/gph/gph-events.cpp:412 msgid "Increasing Volume" msgstr "Zvy¹uji Hlasitost" -#: backends/events/gph/gph-events.cpp:370 +#: backends/events/gph/gph-events.cpp:418 msgid "Minimal Volume" msgstr "Minimální Hlasitost" -#: backends/events/gph/gph-events.cpp:372 +#: backends/events/gph/gph-events.cpp:420 msgid "Decreasing Volume" msgstr "Sni¾uji Hlasitost" -#: backends/updates/macosx/macosx-updates.mm:65 +#: backends/events/openpandora/op-events.cpp:174 +#, fuzzy +msgid "Touchscreen 'Tap Mode' - Hover (DPad Clicks)" +msgstr "'Re¾im «uknutí' Dotykové Obrazovky - Najetí (Bez Kliknutí)" + +#: backends/updates/macosx/macosx-updates.mm:67 msgid "Check for Updates..." msgstr "Zkontrolovat Aktualizace..." diff --git a/po/da_DA.po b/po/da_DA.po index c2d55f82cd..1e1a491990 100644 --- a/po/da_DA.po +++ b/po/da_DA.po @@ -1,4 +1,5 @@ -# Copyright (C) 2010-2012 ScummVM Team +# Dansk translation for ScummVM +# Copyright (C) 2010-2013 ScummVM Team # This file is distributed under the same license as the ScummVM package. # Steffen Nyeland <steffen@nyeland.dk>, 2010. # @@ -6,14 +7,14 @@ msgid "" msgstr "" "Project-Id-Version: ScummVM 1.3.0svn\n" "Report-Msgid-Bugs-To: scummvm-devel@lists.sf.net\n" -"POT-Creation-Date: 2012-07-08 12:25+0100\n" +"POT-Creation-Date: 2012-12-01 17:22+0000\n" "PO-Revision-Date: 2012-07-09 20:27+0100\n" "Last-Translator: Steffen Nyeland <steffen@nyeland.dk>\n" "Language-Team: Steffen Nyeland <steffen@nyeland.dk>\n" +"Language: Dansk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=iso-8859-1\n" "Content-Transfer-Encoding: 8bit\n" -"Language: Dansk\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Poedit-Language: Danish\n" "X-Poedit-Country: DENMARK\n" @@ -35,8 +36,7 @@ msgstr "Tilgængelige \"motorer\":" msgid "Go up" msgstr "Gå op" -#: gui/browser.cpp:66 -#: gui/browser.cpp:68 +#: gui/browser.cpp:66 gui/browser.cpp:68 msgid "Go to previous directory level" msgstr "Gå til forrige biblioteks niveau" @@ -45,37 +45,25 @@ msgctxt "lowres" msgid "Go up" msgstr "Gå op" -#: gui/browser.cpp:69 -#: gui/chooser.cpp:45 -#: gui/KeysDialog.cpp:43 -#: gui/launcher.cpp:345 -#: gui/massadd.cpp:94 -#: gui/options.cpp:1228 -#: gui/saveload.cpp:64 -#: gui/saveload.cpp:173 -#: gui/themebrowser.cpp:54 -#: engines/engine.cpp:442 -#: engines/scumm/dialogs.cpp:190 -#: engines/sword1/control.cpp:865 -#: engines/parallaction/saveload.cpp:274 -#: backends/platform/wii/options.cpp:48 +#: gui/browser.cpp:69 gui/chooser.cpp:45 gui/KeysDialog.cpp:43 +#: gui/launcher.cpp:345 gui/massadd.cpp:94 gui/options.cpp:1228 +#: gui/saveload-dialog.cpp:215 gui/saveload-dialog.cpp:275 +#: gui/saveload-dialog.cpp:545 gui/saveload-dialog.cpp:919 +#: gui/themebrowser.cpp:54 engines/engine.cpp:442 +#: engines/scumm/dialogs.cpp:190 engines/sword1/control.cpp:867 +#: engines/parallaction/saveload.cpp:274 backends/platform/wii/options.cpp:48 #: backends/events/default/default-events.cpp:191 #: backends/events/default/default-events.cpp:213 msgid "Cancel" msgstr "Fortryd" -#: gui/browser.cpp:70 -#: gui/chooser.cpp:46 -#: gui/themebrowser.cpp:55 +#: gui/browser.cpp:70 gui/chooser.cpp:46 gui/themebrowser.cpp:55 msgid "Choose" msgstr "Vælg" -#: gui/gui-manager.cpp:115 -#: engines/scumm/help.cpp:125 -#: engines/scumm/help.cpp:140 -#: engines/scumm/help.cpp:165 -#: engines/scumm/help.cpp:191 -#: engines/scumm/help.cpp:209 +#: gui/gui-manager.cpp:115 engines/scumm/help.cpp:125 +#: engines/scumm/help.cpp:140 engines/scumm/help.cpp:165 +#: engines/scumm/help.cpp:191 engines/scumm/help.cpp:209 #: backends/keymapper/remap-dialog.cpp:52 msgid "Close" msgstr "Luk" @@ -84,23 +72,19 @@ msgstr "Luk" msgid "Mouse click" msgstr "Muse klik" -#: gui/gui-manager.cpp:122 -#: base/main.cpp:300 +#: gui/gui-manager.cpp:122 base/main.cpp:301 msgid "Display keyboard" msgstr "Vis tastatur" -#: gui/gui-manager.cpp:126 -#: base/main.cpp:304 +#: gui/gui-manager.cpp:126 base/main.cpp:305 msgid "Remap keys" msgstr "Kortlæg taster" -#: gui/gui-manager.cpp:129 -#: base/main.cpp:307 +#: gui/gui-manager.cpp:129 base/main.cpp:308 msgid "Toggle FullScreen" msgstr "Skift fuldskærm" -#: gui/KeysDialog.h:36 -#: gui/KeysDialog.cpp:145 +#: gui/KeysDialog.h:36 gui/KeysDialog.cpp:145 msgid "Choose an action to map" msgstr "Vælg en handling at kortlægge" @@ -108,32 +92,18 @@ msgstr "Vælg en handling at kortlægge" msgid "Map" msgstr "Kortlæg" -#: gui/KeysDialog.cpp:42 -#: gui/launcher.cpp:346 -#: gui/launcher.cpp:1001 -#: gui/launcher.cpp:1005 -#: gui/massadd.cpp:91 -#: gui/options.cpp:1229 -#: engines/engine.cpp:361 -#: engines/engine.cpp:372 -#: engines/scumm/dialogs.cpp:192 -#: engines/scumm/scumm.cpp:1775 -#: engines/agos/animation.cpp:561 -#: engines/groovie/script.cpp:420 -#: engines/sky/compact.cpp:131 -#: engines/sky/compact.cpp:141 -#: engines/sword1/animation.cpp:539 -#: engines/sword1/animation.cpp:560 -#: engines/sword1/animation.cpp:570 -#: engines/sword1/animation.cpp:577 -#: engines/sword1/control.cpp:865 -#: engines/sword1/logic.cpp:1633 -#: engines/sword2/animation.cpp:435 -#: engines/sword2/animation.cpp:455 -#: engines/sword2/animation.cpp:465 -#: engines/sword2/animation.cpp:474 -#: engines/parallaction/saveload.cpp:274 -#: backends/platform/wii/options.cpp:47 +#: gui/KeysDialog.cpp:42 gui/launcher.cpp:346 gui/launcher.cpp:1001 +#: gui/launcher.cpp:1005 gui/massadd.cpp:91 gui/options.cpp:1229 +#: gui/saveload-dialog.cpp:920 engines/engine.cpp:361 engines/engine.cpp:372 +#: engines/scumm/dialogs.cpp:192 engines/scumm/scumm.cpp:1776 +#: engines/agos/animation.cpp:558 engines/groovie/script.cpp:420 +#: engines/sky/compact.cpp:131 engines/sky/compact.cpp:141 +#: engines/sword1/animation.cpp:519 engines/sword1/animation.cpp:540 +#: engines/sword1/animation.cpp:550 engines/sword1/animation.cpp:557 +#: engines/sword1/control.cpp:867 engines/sword1/logic.cpp:1633 +#: engines/sword2/animation.cpp:419 engines/sword2/animation.cpp:439 +#: engines/sword2/animation.cpp:449 engines/sword2/animation.cpp:458 +#: engines/parallaction/saveload.cpp:274 backends/platform/wii/options.cpp:47 #: backends/platform/wince/CELauncherDialog.cpp:54 msgid "OK" msgstr "OK" @@ -142,16 +112,12 @@ msgstr "OK" msgid "Select an action and click 'Map'" msgstr "Vælg en handling og klik 'Kortlæg'" -#: gui/KeysDialog.cpp:80 -#: gui/KeysDialog.cpp:102 -#: gui/KeysDialog.cpp:141 +#: gui/KeysDialog.cpp:80 gui/KeysDialog.cpp:102 gui/KeysDialog.cpp:141 #, c-format msgid "Associated key : %s" msgstr "Tilknyttet tast : %s" -#: gui/KeysDialog.cpp:82 -#: gui/KeysDialog.cpp:104 -#: gui/KeysDialog.cpp:143 +#: gui/KeysDialog.cpp:82 gui/KeysDialog.cpp:104 gui/KeysDialog.cpp:143 #, c-format msgid "Associated key : none" msgstr "Tilknyttet tast : ingen" @@ -172,11 +138,13 @@ msgstr "Spil" msgid "ID:" msgstr "ID:" -#: gui/launcher.cpp:191 -#: gui/launcher.cpp:193 -#: gui/launcher.cpp:194 -msgid "Short game identifier used for referring to savegames and running the game from the command line" -msgstr "Kort spil identifikator til brug for gemmer, og for at køre spillet fra kommandolinien" +#: gui/launcher.cpp:191 gui/launcher.cpp:193 gui/launcher.cpp:194 +msgid "" +"Short game identifier used for referring to savegames and running the game " +"from the command line" +msgstr "" +"Kort spil identifikator til brug for gemmer, og for at køre spillet fra " +"kommandolinien" #: gui/launcher.cpp:193 msgctxt "lowres" @@ -187,9 +155,7 @@ msgstr "ID:" msgid "Name:" msgstr "Navn:" -#: gui/launcher.cpp:198 -#: gui/launcher.cpp:200 -#: gui/launcher.cpp:201 +#: gui/launcher.cpp:198 gui/launcher.cpp:200 gui/launcher.cpp:201 msgid "Full title of the game" msgstr "Fuld titel på spillet" @@ -202,17 +168,16 @@ msgstr "Navn:" msgid "Language:" msgstr "Sprog:" -#: gui/launcher.cpp:204 -#: gui/launcher.cpp:205 -msgid "Language of the game. This will not turn your Spanish game version into English" -msgstr "Spillets sprog. Dette vil ikke ændre din spanske version af spillet til engelsk" +#: gui/launcher.cpp:204 gui/launcher.cpp:205 +msgid "" +"Language of the game. This will not turn your Spanish game version into " +"English" +msgstr "" +"Spillets sprog. Dette vil ikke ændre din spanske version af spillet til " +"engelsk" -#: gui/launcher.cpp:206 -#: gui/launcher.cpp:220 -#: gui/options.cpp:80 -#: gui/options.cpp:730 -#: gui/options.cpp:743 -#: gui/options.cpp:1199 +#: gui/launcher.cpp:206 gui/launcher.cpp:220 gui/options.cpp:80 +#: gui/options.cpp:730 gui/options.cpp:743 gui/options.cpp:1199 #: audio/null.cpp:40 msgid "<default>" msgstr "<standard>" @@ -221,9 +186,7 @@ msgstr "<standard>" msgid "Platform:" msgstr "Platform:" -#: gui/launcher.cpp:216 -#: gui/launcher.cpp:218 -#: gui/launcher.cpp:219 +#: gui/launcher.cpp:216 gui/launcher.cpp:218 gui/launcher.cpp:219 msgid "Platform the game was originally designed for" msgstr "Platform som spillet oprindeligt var designet til" @@ -236,15 +199,11 @@ msgstr "Platform:" msgid "Engine" msgstr "Motor" -#: gui/launcher.cpp:239 -#: gui/options.cpp:1062 -#: gui/options.cpp:1079 +#: gui/launcher.cpp:239 gui/options.cpp:1062 gui/options.cpp:1079 msgid "Graphics" msgstr "Grafik" -#: gui/launcher.cpp:239 -#: gui/options.cpp:1062 -#: gui/options.cpp:1079 +#: gui/launcher.cpp:239 gui/options.cpp:1062 gui/options.cpp:1079 msgid "GFX" msgstr "GFX" @@ -257,8 +216,7 @@ msgctxt "lowres" msgid "Override global graphic settings" msgstr "Overstyr globale grafik indstillinger" -#: gui/launcher.cpp:251 -#: gui/options.cpp:1085 +#: gui/launcher.cpp:251 gui/options.cpp:1085 msgid "Audio" msgstr "Lyd" @@ -271,13 +229,11 @@ msgctxt "lowres" msgid "Override global audio settings" msgstr "Overstyr globale lyd indstillinger" -#: gui/launcher.cpp:265 -#: gui/options.cpp:1090 +#: gui/launcher.cpp:265 gui/options.cpp:1090 msgid "Volume" msgstr "Lydstyrke" -#: gui/launcher.cpp:267 -#: gui/options.cpp:1092 +#: gui/launcher.cpp:267 gui/options.cpp:1092 msgctxt "lowres" msgid "Volume" msgstr "Lydstyrke" @@ -291,8 +247,7 @@ msgctxt "lowres" msgid "Override global volume settings" msgstr "Overstyr globale lydstyrke indstillinger" -#: gui/launcher.cpp:280 -#: gui/options.cpp:1100 +#: gui/launcher.cpp:280 gui/options.cpp:1100 msgid "MIDI" msgstr "MIDI" @@ -305,8 +260,7 @@ msgctxt "lowres" msgid "Override global MIDI settings" msgstr "Overstyr globale MIDI indstillinger" -#: gui/launcher.cpp:294 -#: gui/options.cpp:1106 +#: gui/launcher.cpp:294 gui/options.cpp:1106 msgid "MT-32" msgstr "MT-32" @@ -319,13 +273,11 @@ msgctxt "lowres" msgid "Override global MT-32 settings" msgstr "Overstyr globale MT-32 indstillinger" -#: gui/launcher.cpp:308 -#: gui/options.cpp:1113 +#: gui/launcher.cpp:308 gui/options.cpp:1113 msgid "Paths" msgstr "Stier" -#: gui/launcher.cpp:310 -#: gui/options.cpp:1115 +#: gui/launcher.cpp:310 gui/options.cpp:1115 msgctxt "lowres" msgid "Paths" msgstr "Stier" @@ -339,80 +291,54 @@ msgctxt "lowres" msgid "Game Path:" msgstr "Spil sti:" -#: gui/launcher.cpp:324 -#: gui/options.cpp:1139 +#: gui/launcher.cpp:324 gui/options.cpp:1139 msgid "Extra Path:" msgstr "Ekstra sti:" -#: gui/launcher.cpp:324 -#: gui/launcher.cpp:326 -#: gui/launcher.cpp:327 +#: gui/launcher.cpp:324 gui/launcher.cpp:326 gui/launcher.cpp:327 msgid "Specifies path to additional data used the game" msgstr "Angiver sti til ekstra data der bruges i spillet" -#: gui/launcher.cpp:326 -#: gui/options.cpp:1141 +#: gui/launcher.cpp:326 gui/options.cpp:1141 msgctxt "lowres" msgid "Extra Path:" msgstr "Ekstra sti:" -#: gui/launcher.cpp:333 -#: gui/options.cpp:1123 +#: gui/launcher.cpp:333 gui/options.cpp:1123 msgid "Save Path:" msgstr "Gemme sti:" -#: gui/launcher.cpp:333 -#: gui/launcher.cpp:335 -#: gui/launcher.cpp:336 -#: gui/options.cpp:1123 -#: gui/options.cpp:1125 -#: gui/options.cpp:1126 +#: gui/launcher.cpp:333 gui/launcher.cpp:335 gui/launcher.cpp:336 +#: gui/options.cpp:1123 gui/options.cpp:1125 gui/options.cpp:1126 msgid "Specifies where your savegames are put" msgstr "Angiver hvor dine gemmer bliver lagt" -#: gui/launcher.cpp:335 -#: gui/options.cpp:1125 +#: gui/launcher.cpp:335 gui/options.cpp:1125 msgctxt "lowres" msgid "Save Path:" msgstr "Gemme sti:" -#: gui/launcher.cpp:354 -#: gui/launcher.cpp:453 -#: gui/launcher.cpp:511 -#: gui/launcher.cpp:565 -#: gui/options.cpp:1134 -#: gui/options.cpp:1142 -#: gui/options.cpp:1151 -#: gui/options.cpp:1258 -#: gui/options.cpp:1264 -#: gui/options.cpp:1272 -#: gui/options.cpp:1302 -#: gui/options.cpp:1308 -#: gui/options.cpp:1315 -#: gui/options.cpp:1408 -#: gui/options.cpp:1411 +#: gui/launcher.cpp:354 gui/launcher.cpp:453 gui/launcher.cpp:511 +#: gui/launcher.cpp:565 gui/options.cpp:1134 gui/options.cpp:1142 +#: gui/options.cpp:1151 gui/options.cpp:1258 gui/options.cpp:1264 +#: gui/options.cpp:1272 gui/options.cpp:1302 gui/options.cpp:1308 +#: gui/options.cpp:1315 gui/options.cpp:1408 gui/options.cpp:1411 #: gui/options.cpp:1423 msgctxt "path" msgid "None" msgstr "Ingen" -#: gui/launcher.cpp:359 -#: gui/launcher.cpp:459 -#: gui/launcher.cpp:569 -#: gui/options.cpp:1252 -#: gui/options.cpp:1296 -#: gui/options.cpp:1414 +#: gui/launcher.cpp:359 gui/launcher.cpp:459 gui/launcher.cpp:569 +#: gui/options.cpp:1252 gui/options.cpp:1296 gui/options.cpp:1414 #: backends/platform/wii/options.cpp:56 msgid "Default" msgstr "Standard" -#: gui/launcher.cpp:504 -#: gui/options.cpp:1417 +#: gui/launcher.cpp:504 gui/options.cpp:1417 msgid "Select SoundFont" msgstr "Vælg SoundFont" -#: gui/launcher.cpp:523 -#: gui/launcher.cpp:677 +#: gui/launcher.cpp:523 gui/launcher.cpp:677 msgid "Select directory with game data" msgstr "Vælg bibliotek med spil data" @@ -428,13 +354,11 @@ msgstr "Vælg bibliotek til spil gemmer" msgid "This game ID is already taken. Please choose another one." msgstr "Dette spil ID er allerede i brug. Vælg venligst et andet." -#: gui/launcher.cpp:621 -#: engines/dialogs.cpp:110 +#: gui/launcher.cpp:621 engines/dialogs.cpp:110 msgid "~Q~uit" msgstr "~A~fslut" -#: gui/launcher.cpp:621 -#: backends/platform/sdl/macosx/appmenu_osx.mm:96 +#: gui/launcher.cpp:621 backends/platform/sdl/macosx/appmenu_osx.mm:95 msgid "Quit ScummVM" msgstr "Slut ScummVM" @@ -442,8 +366,7 @@ msgstr "Slut ScummVM" msgid "A~b~out..." msgstr "~O~m..." -#: gui/launcher.cpp:622 -#: backends/platform/sdl/macosx/appmenu_osx.mm:70 +#: gui/launcher.cpp:622 backends/platform/sdl/macosx/appmenu_osx.mm:69 msgid "About ScummVM" msgstr "Om ScummVM" @@ -471,13 +394,11 @@ msgstr "Ind~l~æs..." msgid "Load savegame for selected game" msgstr "Indlæs gemmer for det valgte spil" -#: gui/launcher.cpp:633 -#: gui/launcher.cpp:1120 +#: gui/launcher.cpp:633 gui/launcher.cpp:1120 msgid "~A~dd Game..." msgstr "~T~ilføj spil..." -#: gui/launcher.cpp:633 -#: gui/launcher.cpp:640 +#: gui/launcher.cpp:633 gui/launcher.cpp:640 msgid "Hold Shift for Mass Add" msgstr "Hold Skift for at tilføje flere" @@ -485,8 +406,7 @@ msgstr "Hold Skift for at tilføje flere" msgid "~E~dit Game..." msgstr "~R~ediger spil..." -#: gui/launcher.cpp:635 -#: gui/launcher.cpp:642 +#: gui/launcher.cpp:635 gui/launcher.cpp:642 msgid "Change game options" msgstr "Ændre spil indstillinger" @@ -494,13 +414,11 @@ msgstr "Ændre spil indstillinger" msgid "~R~emove Game" msgstr "~F~jern spil" -#: gui/launcher.cpp:637 -#: gui/launcher.cpp:644 +#: gui/launcher.cpp:637 gui/launcher.cpp:644 msgid "Remove game from the list. The game data files stay intact" msgstr "Fjerner spil fra listen. Spillets data filer forbliver uberørt" -#: gui/launcher.cpp:640 -#: gui/launcher.cpp:1120 +#: gui/launcher.cpp:640 gui/launcher.cpp:1120 msgctxt "lowres" msgid "~A~dd Game..." msgstr "~T~ilføj spil..." @@ -519,36 +437,31 @@ msgstr "~F~jern spil" msgid "Search in game list" msgstr "Søg i spil liste" -#: gui/launcher.cpp:656 -#: gui/launcher.cpp:1167 +#: gui/launcher.cpp:656 gui/launcher.cpp:1167 msgid "Search:" msgstr "Søg:" -#: gui/launcher.cpp:680 -#: engines/dialogs.cpp:114 -#: engines/mohawk/myst.cpp:255 -#: engines/mohawk/riven.cpp:716 -#: engines/cruise/menu.cpp:214 +#: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/mohawk/myst.cpp:245 +#: engines/mohawk/riven.cpp:716 engines/cruise/menu.cpp:214 msgid "Load game:" msgstr "Indlæs spil:" -#: gui/launcher.cpp:680 -#: engines/dialogs.cpp:114 -#: engines/scumm/dialogs.cpp:188 -#: engines/mohawk/myst.cpp:255 -#: engines/mohawk/riven.cpp:716 -#: engines/cruise/menu.cpp:214 -#: backends/platform/wince/CEActionsPocket.cpp:267 +#: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/scumm/dialogs.cpp:188 +#: engines/mohawk/myst.cpp:245 engines/mohawk/riven.cpp:716 +#: engines/cruise/menu.cpp:214 backends/platform/wince/CEActionsPocket.cpp:267 #: backends/platform/wince/CEActionsSmartphone.cpp:231 msgid "Load" msgstr "Indlæs" #: gui/launcher.cpp:788 -msgid "Do you really want to run the mass game detector? This could potentially add a huge number of games." -msgstr "Vil du virkelig køre fler spils detektoren? Dette kunne potentielt tilføje et stort antal spil." +msgid "" +"Do you really want to run the mass game detector? This could potentially add " +"a huge number of games." +msgstr "" +"Vil du virkelig køre fler spils detektoren? Dette kunne potentielt tilføje " +"et stort antal spil." -#: gui/launcher.cpp:789 -#: gui/launcher.cpp:937 +#: gui/launcher.cpp:789 gui/launcher.cpp:937 #: backends/events/symbiansdl/symbiansdl-events.cpp:184 #: backends/platform/wince/CEActionsPocket.cpp:326 #: backends/platform/wince/CEActionsSmartphone.cpp:287 @@ -556,8 +469,7 @@ msgstr "Vil du virkelig køre fler spils detektoren? Dette kunne potentielt tilfø msgid "Yes" msgstr "Ja" -#: gui/launcher.cpp:789 -#: gui/launcher.cpp:937 +#: gui/launcher.cpp:789 gui/launcher.cpp:937 #: backends/events/symbiansdl/symbiansdl-events.cpp:184 #: backends/platform/wince/CEActionsPocket.cpp:326 #: backends/platform/wince/CEActionsSmartphone.cpp:287 @@ -587,7 +499,8 @@ msgstr "Dette spil understøtter ikke indlæsning af spil fra spiloversigten." #: gui/launcher.cpp:1005 msgid "ScummVM could not find any engine capable of running the selected game!" -msgstr "ScummVM kunne ikke finde en motor, istand til at afvikle det valgte spil!" +msgstr "" +"ScummVM kunne ikke finde en motor, istand til at afvikle det valgte spil!" #: gui/launcher.cpp:1119 msgctxt "lowres" @@ -598,8 +511,7 @@ msgstr "Tilføj flere..." msgid "Mass Add..." msgstr "Tilføj flere..." -#: gui/massadd.cpp:78 -#: gui/massadd.cpp:81 +#: gui/massadd.cpp:78 gui/massadd.cpp:81 msgid "... progress ..." msgstr "... fremskridt ..." @@ -662,11 +574,8 @@ msgstr "44 kHz" msgid "48 kHz" msgstr "48 kHz" -#: gui/options.cpp:248 -#: gui/options.cpp:474 -#: gui/options.cpp:575 -#: gui/options.cpp:644 -#: gui/options.cpp:852 +#: gui/options.cpp:248 gui/options.cpp:474 gui/options.cpp:575 +#: gui/options.cpp:644 gui/options.cpp:852 msgctxt "soundfont" msgid "None" msgstr "Ingen" @@ -695,8 +604,7 @@ msgstr "Grafik tilstand:" msgid "Render mode:" msgstr "Rendere tilstand:" -#: gui/options.cpp:741 -#: gui/options.cpp:742 +#: gui/options.cpp:741 gui/options.cpp:742 msgid "Special dithering modes supported by some games" msgstr "Speciel farvereduceringstilstand understøttet a nogle spil" @@ -722,14 +630,11 @@ msgstr "Foretruk. enhed:" msgid "Music Device:" msgstr "Musik enhed:" -#: gui/options.cpp:764 -#: gui/options.cpp:766 +#: gui/options.cpp:764 gui/options.cpp:766 msgid "Specifies preferred sound device or sound card emulator" msgstr "Angiver foretukket lyd enhed eller lydkort emulator" -#: gui/options.cpp:764 -#: gui/options.cpp:766 -#: gui/options.cpp:767 +#: gui/options.cpp:764 gui/options.cpp:766 gui/options.cpp:767 msgid "Specifies output sound device or sound card emulator" msgstr "Angiver lyd udgangsenhed eller lydkorts emulator" @@ -747,8 +652,7 @@ msgstr "Musik enhed:" msgid "AdLib emulator:" msgstr "AdLib emulator:" -#: gui/options.cpp:793 -#: gui/options.cpp:794 +#: gui/options.cpp:793 gui/options.cpp:794 msgid "AdLib is used for music in many games" msgstr "AdLib bliver brugt til musik i mange spil" @@ -756,10 +660,13 @@ msgstr "AdLib bliver brugt til musik i mange spil" msgid "Output rate:" msgstr "Udgangsfrekvens:" -#: gui/options.cpp:804 -#: gui/options.cpp:805 -msgid "Higher value specifies better sound quality but may be not supported by your soundcard" -msgstr "Højere værdi angiver bedre lyd kvalitet, men understøttes måske ikke af dit lydkort" +#: gui/options.cpp:804 gui/options.cpp:805 +msgid "" +"Higher value specifies better sound quality but may be not supported by your " +"soundcard" +msgstr "" +"Højere værdi angiver bedre lyd kvalitet, men understøttes måske ikke af dit " +"lydkort" #: gui/options.cpp:815 msgid "GM Device:" @@ -773,8 +680,7 @@ msgstr "Angiver standard lyd enhed for Generel MIDI-udgang" msgid "Don't use General MIDI music" msgstr "Brug ikke Generel MIDI musik" -#: gui/options.cpp:837 -#: gui/options.cpp:899 +#: gui/options.cpp:837 gui/options.cpp:899 msgid "Use first available device" msgstr "Brug første tilgængelig enhed" @@ -782,9 +688,7 @@ msgstr "Brug første tilgængelig enhed" msgid "SoundFont:" msgstr "SoundFont:" -#: gui/options.cpp:849 -#: gui/options.cpp:851 -#: gui/options.cpp:852 +#: gui/options.cpp:849 gui/options.cpp:851 gui/options.cpp:852 msgid "SoundFont is supported by some audio cards, Fluidsynth and Timidity" msgstr "SoundFont er understøttet af nogle lydkort, Fluidsynth og Timidity" @@ -817,10 +721,13 @@ msgstr "Angiver standard lyd enhed for Roland MT-32/LAPC1/CM32I/CM64 udgang" msgid "True Roland MT-32 (disable GM emulation)" msgstr "Ægte Roland MT-32 (undlad GM emulering)" -#: gui/options.cpp:875 -#: gui/options.cpp:877 -msgid "Check if you want to use your real hardware Roland-compatible sound device connected to your computer" -msgstr "Kontroller om du vil bruge din rigtige hardware Roland-kompatible lyd enhed tilsluttet til din computer" +#: gui/options.cpp:875 gui/options.cpp:877 +msgid "" +"Check if you want to use your real hardware Roland-compatible sound device " +"connected to your computer" +msgstr "" +"Kontroller om du vil bruge din rigtige hardware Roland-kompatible lyd enhed " +"tilsluttet til din computer" #: gui/options.cpp:877 msgctxt "lowres" @@ -843,13 +750,11 @@ msgstr "Brug ikke Roland MT-32 musik" msgid "Text and Speech:" msgstr "Tekst og tale:" -#: gui/options.cpp:920 -#: gui/options.cpp:930 +#: gui/options.cpp:920 gui/options.cpp:930 msgid "Speech" msgstr "Tale" -#: gui/options.cpp:921 -#: gui/options.cpp:931 +#: gui/options.cpp:921 gui/options.cpp:931 msgid "Subtitles" msgstr "Undertekster" @@ -905,9 +810,7 @@ msgstr "Mute alle" msgid "SFX volume:" msgstr "SFX lydstyrke:" -#: gui/options.cpp:962 -#: gui/options.cpp:964 -#: gui/options.cpp:965 +#: gui/options.cpp:962 gui/options.cpp:964 gui/options.cpp:965 msgid "Special sound effects volume" msgstr "Lydstyrke for specielle lydeffekter" @@ -934,9 +837,7 @@ msgctxt "lowres" msgid "Theme Path:" msgstr "Tema sti:" -#: gui/options.cpp:1139 -#: gui/options.cpp:1141 -#: gui/options.cpp:1142 +#: gui/options.cpp:1139 gui/options.cpp:1141 gui/options.cpp:1142 msgid "Specifies path to additional data used by all games or ScummVM" msgstr "Angiver sti til ekstra data brugt af alle spil eller ScummVM" @@ -1012,117 +913,147 @@ msgid "Select directory for plugins" msgstr "Vælg bibliotek for plugins" #: gui/options.cpp:1450 -msgid "The theme you selected does not support your current language. If you want to use this theme you need to switch to another language first." -msgstr "Temaet du valgte understøtter ikke dit aktuelle sprog. Hvis du ønsker at bruge dette tema, skal du skifte til et andet sprog først." +msgid "" +"The theme you selected does not support your current language. If you want " +"to use this theme you need to switch to another language first." +msgstr "" +"Temaet du valgte understøtter ikke dit aktuelle sprog. Hvis du ønsker at " +"bruge dette tema, skal du skifte til et andet sprog først." + +#: gui/saveload-dialog.cpp:166 +msgid "List view" +msgstr "" + +#: gui/saveload-dialog.cpp:167 +msgid "Grid view" +msgstr "" -#: gui/saveload.cpp:59 -#: gui/saveload.cpp:257 +#: gui/saveload-dialog.cpp:210 gui/saveload-dialog.cpp:358 msgid "No date saved" msgstr "Ingen dato gemt" -#: gui/saveload.cpp:60 -#: gui/saveload.cpp:258 +#: gui/saveload-dialog.cpp:211 gui/saveload-dialog.cpp:359 msgid "No time saved" msgstr "Intet tidspunkt gemt" -#: gui/saveload.cpp:61 -#: gui/saveload.cpp:259 +#: gui/saveload-dialog.cpp:212 gui/saveload-dialog.cpp:360 msgid "No playtime saved" msgstr "Ingen spilletid gemt" -#: gui/saveload.cpp:68 -#: gui/saveload.cpp:173 +#: gui/saveload-dialog.cpp:219 gui/saveload-dialog.cpp:275 msgid "Delete" msgstr "Slet" -#: gui/saveload.cpp:172 +#: gui/saveload-dialog.cpp:274 msgid "Do you really want to delete this savegame?" msgstr "Vil du virkelig slette denne gemmer?" -#: gui/saveload.cpp:282 +#: gui/saveload-dialog.cpp:383 gui/saveload-dialog.cpp:872 msgid "Date: " msgstr "Dato:" -#: gui/saveload.cpp:286 +#: gui/saveload-dialog.cpp:387 gui/saveload-dialog.cpp:878 msgid "Time: " msgstr "Tid:" -#: gui/saveload.cpp:292 +#: gui/saveload-dialog.cpp:393 gui/saveload-dialog.cpp:886 msgid "Playtime: " msgstr "Spilletid:" -#: gui/saveload.cpp:305 -#: gui/saveload.cpp:372 +#: gui/saveload-dialog.cpp:406 gui/saveload-dialog.cpp:494 msgid "Untitled savestate" msgstr "Unavngivet gemmetilstand" +#: gui/saveload-dialog.cpp:546 +msgid "Next" +msgstr "" + +#: gui/saveload-dialog.cpp:549 +msgid "Prev" +msgstr "" + +#: gui/saveload-dialog.cpp:736 +#, fuzzy +msgid "New Save" +msgstr "Gem" + +#: gui/saveload-dialog.cpp:736 +#, fuzzy +msgid "Create a new save game" +msgstr "Mislykkedes at gemme spil" + +#: gui/saveload-dialog.cpp:865 +#, fuzzy +msgid "Name: " +msgstr "Navn:" + +#: gui/saveload-dialog.cpp:937 +#, c-format +msgid "Enter a description for slot %d:" +msgstr "" + #: gui/themebrowser.cpp:44 msgid "Select a Theme" msgstr "Vælg et tema" -#: gui/ThemeEngine.cpp:335 +#: gui/ThemeEngine.cpp:337 msgid "Disabled GFX" msgstr "Deaktiveret GFX" -#: gui/ThemeEngine.cpp:335 +#: gui/ThemeEngine.cpp:337 msgctxt "lowres" msgid "Disabled GFX" msgstr "Deaktiveret GFX" -#: gui/ThemeEngine.cpp:336 +#: gui/ThemeEngine.cpp:338 msgid "Standard Renderer (16bpp)" msgstr "Standard renderer (16bpp)" -#: gui/ThemeEngine.cpp:336 +#: gui/ThemeEngine.cpp:338 msgid "Standard (16bpp)" msgstr "Standard (16bpp)" -#: gui/ThemeEngine.cpp:338 +#: gui/ThemeEngine.cpp:340 msgid "Antialiased Renderer (16bpp)" msgstr "Antialias renderer (16bpp)" -#: gui/ThemeEngine.cpp:338 +#: gui/ThemeEngine.cpp:340 msgid "Antialiased (16bpp)" msgstr "Antialias (16bpp)" -#: gui/widget.cpp:322 -#: gui/widget.cpp:324 -#: gui/widget.cpp:330 -#: gui/widget.cpp:332 +#: gui/widget.cpp:322 gui/widget.cpp:324 gui/widget.cpp:330 gui/widget.cpp:332 msgid "Clear value" msgstr "Slet værdi" -#: base/main.cpp:209 +#: base/main.cpp:210 #, c-format msgid "Engine does not support debug level '%s'" msgstr "Motor understøtter ikke fejlfindingsniveau '%s'" -#: base/main.cpp:287 +#: base/main.cpp:288 msgid "Menu" msgstr "Menu" -#: base/main.cpp:290 -#: backends/platform/symbian/src/SymbianActions.cpp:45 +#: base/main.cpp:291 backends/platform/symbian/src/SymbianActions.cpp:45 #: backends/platform/wince/CEActionsPocket.cpp:45 #: backends/platform/wince/CEActionsSmartphone.cpp:46 msgid "Skip" msgstr "Spring over" -#: base/main.cpp:293 -#: backends/platform/symbian/src/SymbianActions.cpp:50 +#: base/main.cpp:294 backends/platform/symbian/src/SymbianActions.cpp:50 #: backends/platform/wince/CEActionsPocket.cpp:42 msgid "Pause" msgstr "Pause" -#: base/main.cpp:296 +#: base/main.cpp:297 msgid "Skip line" msgstr "Spring linje over" -#: base/main.cpp:467 +#: base/main.cpp:468 msgid "Error running game:" msgstr "Fejl ved kørsel af spil:" -#: base/main.cpp:491 +#: base/main.cpp:492 msgid "Could not find any engine capable of running the selected game" msgstr "Kunne ikke finde nogen motor istand til at afvikle det valgte spil" @@ -1190,16 +1121,17 @@ msgstr "Bruger annullerede" msgid "Unknown error" msgstr "Ukendt fejl" -#: engines/advancedDetector.cpp:324 +#: engines/advancedDetector.cpp:316 #, c-format msgid "The game in '%s' seems to be unknown." msgstr "Spillet i '%s' ser ud til at være ukendt." -#: engines/advancedDetector.cpp:325 +#: engines/advancedDetector.cpp:317 msgid "Please, report the following data to the ScummVM team along with name" -msgstr "Venligst, rapportere følgende data til ScummVM holdet sammen med navnet" +msgstr "" +"Venligst, rapportere følgende data til ScummVM holdet sammen med navnet" -#: engines/advancedDetector.cpp:327 +#: engines/advancedDetector.cpp:319 msgid "of the game you tried to add and its version/language/etc.:" msgstr "på det spil, du forsøgte at tilføje og dets version/sprog/ etc.:" @@ -1227,29 +1159,23 @@ msgstr "H~j~ælp" msgid "~A~bout" msgstr "~O~m" -#: engines/dialogs.cpp:104 -#: engines/dialogs.cpp:180 +#: engines/dialogs.cpp:104 engines/dialogs.cpp:180 msgid "~R~eturn to Launcher" msgstr "~R~etur til spiloversigt" -#: engines/dialogs.cpp:106 -#: engines/dialogs.cpp:182 +#: engines/dialogs.cpp:106 engines/dialogs.cpp:182 msgctxt "lowres" msgid "~R~eturn to Launcher" msgstr "~R~etur til oversigt" -#: engines/dialogs.cpp:115 -#: engines/agi/saveload.cpp:803 -#: engines/cruise/menu.cpp:212 -#: engines/sci/engine/kfile.cpp:735 +#: engines/dialogs.cpp:115 engines/agi/saveload.cpp:803 +#: engines/cruise/menu.cpp:212 engines/sci/engine/kfile.cpp:742 msgid "Save game:" msgstr "Gemmer:" -#: engines/dialogs.cpp:115 -#: engines/agi/saveload.cpp:803 -#: engines/scumm/dialogs.cpp:187 -#: engines/cruise/menu.cpp:212 -#: engines/sci/engine/kfile.cpp:735 +#: engines/dialogs.cpp:115 engines/agi/saveload.cpp:803 +#: engines/scumm/dialogs.cpp:187 engines/cruise/menu.cpp:212 +#: engines/sci/engine/kfile.cpp:742 #: backends/platform/symbian/src/SymbianActions.cpp:44 #: backends/platform/wince/CEActionsPocket.cpp:43 #: backends/platform/wince/CEActionsPocket.cpp:267 @@ -1259,22 +1185,30 @@ msgid "Save" msgstr "Gem" #: engines/dialogs.cpp:144 -msgid "Sorry, this engine does not currently provide in-game help. Please consult the README for basic information, and for instructions on how to obtain further assistance." -msgstr "Beklager, denne motor leverer i øjeblikket ikke spil hjælp. Se venligst README for grundlæggende oplysninger, og for at få instruktioner om, hvordan man får yderligere hjælp." +msgid "" +"Sorry, this engine does not currently provide in-game help. Please consult " +"the README for basic information, and for instructions on how to obtain " +"further assistance." +msgstr "" +"Beklager, denne motor leverer i øjeblikket ikke spil hjælp. Se venligst " +"README for grundlæggende oplysninger, og for at få instruktioner om, hvordan " +"man får yderligere hjælp." #: engines/dialogs.cpp:228 #, c-format -msgid "Gamestate save failed (%s)! Please consult the README for basic information, and for instructions on how to obtain further assistance." -msgstr "Gem af spiltilstand fejlede (%s)! Se venligst README for grundlæggende oplysninger, og for at få instruktioner om, hvordan man får yderligere hjælp." +msgid "" +"Gamestate save failed (%s)! Please consult the README for basic information, " +"and for instructions on how to obtain further assistance." +msgstr "" +"Gem af spiltilstand fejlede (%s)! Se venligst README for grundlæggende " +"oplysninger, og for at få instruktioner om, hvordan man får yderligere hjælp." -#: engines/dialogs.cpp:301 -#: engines/mohawk/dialogs.cpp:109 +#: engines/dialogs.cpp:301 engines/mohawk/dialogs.cpp:109 #: engines/mohawk/dialogs.cpp:174 msgid "~O~K" msgstr "~O~K" -#: engines/dialogs.cpp:302 -#: engines/mohawk/dialogs.cpp:110 +#: engines/dialogs.cpp:302 engines/mohawk/dialogs.cpp:110 #: engines/mohawk/dialogs.cpp:175 msgid "~C~ancel" msgstr "~F~ortryd" @@ -1329,36 +1263,43 @@ msgstr "" #: engines/engine.cpp:426 #, c-format -msgid "Gamestate load failed (%s)! Please consult the README for basic information, and for instructions on how to obtain further assistance." -msgstr "Indlæsning af spiltilstand fejlede (%s)! Se venligst README for grundlæggende oplysninger, og for at få instruktioner om, hvordan man får yderligere hjælp." +msgid "" +"Gamestate load failed (%s)! Please consult the README for basic information, " +"and for instructions on how to obtain further assistance." +msgstr "" +"Indlæsning af spiltilstand fejlede (%s)! Se venligst README for " +"grundlæggende oplysninger, og for at få instruktioner om, hvordan man får " +"yderligere hjælp." #: engines/engine.cpp:439 -msgid "WARNING: The game you are about to start is not yet fully supported by ScummVM. As such, it is likely to be unstable, and any saves you make might not work in future versions of ScummVM." -msgstr "ADVARSEL: Spillet du er ved at starte endnu ikke er fuldt understøttet af ScummVM. Således, er det sandsynligt, at det er ustabilt, og alle gemmer du foretager fungerer muligvis ikke i fremtidige versioner af ScummVM." +msgid "" +"WARNING: The game you are about to start is not yet fully supported by " +"ScummVM. As such, it is likely to be unstable, and any saves you make might " +"not work in future versions of ScummVM." +msgstr "" +"ADVARSEL: Spillet du er ved at starte endnu ikke er fuldt understøttet af " +"ScummVM. Således, er det sandsynligt, at det er ustabilt, og alle gemmer du " +"foretager fungerer muligvis ikke i fremtidige versioner af ScummVM." #: engines/engine.cpp:442 msgid "Start anyway" msgstr "Start alligevel" -#: engines/agi/detection.cpp:145 -#: engines/dreamweb/detection.cpp:47 -#: engines/sci/detection.cpp:390 +#: engines/agi/detection.cpp:142 engines/dreamweb/detection.cpp:47 +#: engines/sci/detection.cpp:393 msgid "Use original save/load screens" msgstr "Brug original gem/indlæs skærme" -#: engines/agi/detection.cpp:146 -#: engines/dreamweb/detection.cpp:48 -#: engines/sci/detection.cpp:391 +#: engines/agi/detection.cpp:143 engines/dreamweb/detection.cpp:48 +#: engines/sci/detection.cpp:394 msgid "Use the original save/load screens, instead of the ScummVM ones" msgstr "Brug de originale gem/indlæs skærme, istedet for dem fra ScummVM" -#: engines/agi/saveload.cpp:816 -#: engines/sci/engine/kfile.cpp:831 +#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:838 msgid "Restore game:" msgstr "Gendan spil:" -#: engines/agi/saveload.cpp:816 -#: engines/sci/engine/kfile.cpp:831 +#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:838 msgid "Restore" msgstr "Gendan" @@ -1370,53 +1311,60 @@ msgstr "Brug lys palet tilstand" msgid "Display graphics using the game's bright palette" msgstr "Vis grafik ved hjælp af spillets lyse palette" -#: engines/sci/detection.cpp:370 +#: engines/sci/detection.cpp:373 msgid "EGA undithering" msgstr "EGA farveforøgelse" -#: engines/sci/detection.cpp:371 +#: engines/sci/detection.cpp:374 msgid "Enable undithering in EGA games" msgstr "Aktiver farveforøgelse i EGA spil" -#: engines/sci/detection.cpp:380 +#: engines/sci/detection.cpp:383 msgid "Prefer digital sound effects" msgstr "Foretræk digitale lydeffekter" -#: engines/sci/detection.cpp:381 +#: engines/sci/detection.cpp:384 msgid "Prefer digital sound effects instead of synthesized ones" msgstr "Foretræk digitale lydeffekter i stedet for syntetiserede" -#: engines/sci/detection.cpp:400 +#: engines/sci/detection.cpp:403 msgid "Use IMF/Yamaha FB-01 for MIDI output" msgstr "Brug IMF/Yamaha FB-01 til MIDI-udgang" -#: engines/sci/detection.cpp:401 -msgid "Use an IBM Music Feature card or a Yamaha FB-01 FM synth module for MIDI output" -msgstr "Bruge et IBM Musik Feature-kort eller et Yamaha FB-01 FM synth modul til MIDI-udgang" +#: engines/sci/detection.cpp:404 +msgid "" +"Use an IBM Music Feature card or a Yamaha FB-01 FM synth module for MIDI " +"output" +msgstr "" +"Bruge et IBM Musik Feature-kort eller et Yamaha FB-01 FM synth modul til " +"MIDI-udgang" -#: engines/sci/detection.cpp:411 +#: engines/sci/detection.cpp:414 msgid "Use CD audio" msgstr "Brug CD lyd" -#: engines/sci/detection.cpp:412 +#: engines/sci/detection.cpp:415 msgid "Use CD audio instead of in-game audio, if available" msgstr "Brug cd-lyd i stedet for lyd fra spillet, hvis tilgængelige" -#: engines/sci/detection.cpp:422 +#: engines/sci/detection.cpp:425 msgid "Use Windows cursors" msgstr "Brug Windows markør" -#: engines/sci/detection.cpp:423 -msgid "Use the Windows cursors (smaller and monochrome) instead of the DOS ones" +#: engines/sci/detection.cpp:426 +msgid "" +"Use the Windows cursors (smaller and monochrome) instead of the DOS ones" msgstr "Brug Windows-markører (mindre og monokrome) i stedet for dem fra DOS" -#: engines/sci/detection.cpp:433 +#: engines/sci/detection.cpp:436 msgid "Use silver cursors" msgstr "Brug sølv markør" -#: engines/sci/detection.cpp:434 -msgid "Use the alternate set of silver cursors, instead of the normal golden ones" -msgstr "Brug det alternative sæt af sølv markører, i stedet for de normale gyldne" +#: engines/sci/detection.cpp:437 +msgid "" +"Use the alternate set of silver cursors, instead of the normal golden ones" +msgstr "" +"Brug det alternative sæt af sølv markører, i stedet for de normale gyldne" #: engines/scumm/dialogs.cpp:175 #, c-format @@ -1453,8 +1401,7 @@ msgstr "Er du sikker på at du vil afslutte? (J/N) " msgid "Play" msgstr "Spil" -#: engines/scumm/dialogs.cpp:191 -#: engines/scumm/help.cpp:82 +#: engines/scumm/dialogs.cpp:191 engines/scumm/help.cpp:82 #: engines/scumm/help.cpp:84 #: backends/platform/symbian/src/SymbianActions.cpp:52 #: backends/platform/wince/CEActionsPocket.cpp:44 @@ -1581,16 +1528,11 @@ msgstr "Mellemrum" msgid "Pause game" msgstr "Pause spil" -#: engines/scumm/help.cpp:79 -#: engines/scumm/help.cpp:84 -#: engines/scumm/help.cpp:95 -#: engines/scumm/help.cpp:96 -#: engines/scumm/help.cpp:97 -#: engines/scumm/help.cpp:98 -#: engines/scumm/help.cpp:99 -#: engines/scumm/help.cpp:100 -#: engines/scumm/help.cpp:101 -#: engines/scumm/help.cpp:102 +#: engines/scumm/help.cpp:79 engines/scumm/help.cpp:84 +#: engines/scumm/help.cpp:95 engines/scumm/help.cpp:96 +#: engines/scumm/help.cpp:97 engines/scumm/help.cpp:98 +#: engines/scumm/help.cpp:99 engines/scumm/help.cpp:100 +#: engines/scumm/help.cpp:101 engines/scumm/help.cpp:102 msgid "Ctrl" msgstr "Ctrl" @@ -1598,12 +1540,9 @@ msgstr "Ctrl" msgid "Load game state 1-10" msgstr "Indlæs spil tilstand 1-10" -#: engines/scumm/help.cpp:80 -#: engines/scumm/help.cpp:84 -#: engines/scumm/help.cpp:86 -#: engines/scumm/help.cpp:100 -#: engines/scumm/help.cpp:101 -#: engines/scumm/help.cpp:102 +#: engines/scumm/help.cpp:80 engines/scumm/help.cpp:84 +#: engines/scumm/help.cpp:86 engines/scumm/help.cpp:100 +#: engines/scumm/help.cpp:101 engines/scumm/help.cpp:102 msgid "Alt" msgstr "Alt" @@ -1611,8 +1550,7 @@ msgstr "Alt" msgid "Save game state 1-10" msgstr "Gem spil tilstand 1-10" -#: engines/scumm/help.cpp:86 -#: engines/scumm/help.cpp:89 +#: engines/scumm/help.cpp:86 engines/scumm/help.cpp:89 msgid "Enter" msgstr "Enter" @@ -1704,30 +1642,24 @@ msgstr "Spind ordspil på tastaturet:" msgid "Main game controls:" msgstr "Vigtigste spilstyring:" -#: engines/scumm/help.cpp:121 -#: engines/scumm/help.cpp:136 +#: engines/scumm/help.cpp:121 engines/scumm/help.cpp:136 #: engines/scumm/help.cpp:161 msgid "Push" msgstr "Skub" -#: engines/scumm/help.cpp:122 -#: engines/scumm/help.cpp:137 +#: engines/scumm/help.cpp:122 engines/scumm/help.cpp:137 #: engines/scumm/help.cpp:162 msgid "Pull" msgstr "Træk" -#: engines/scumm/help.cpp:123 -#: engines/scumm/help.cpp:138 -#: engines/scumm/help.cpp:163 -#: engines/scumm/help.cpp:197 +#: engines/scumm/help.cpp:123 engines/scumm/help.cpp:138 +#: engines/scumm/help.cpp:163 engines/scumm/help.cpp:197 #: engines/scumm/help.cpp:207 msgid "Give" msgstr "Giv" -#: engines/scumm/help.cpp:124 -#: engines/scumm/help.cpp:139 -#: engines/scumm/help.cpp:164 -#: engines/scumm/help.cpp:190 +#: engines/scumm/help.cpp:124 engines/scumm/help.cpp:139 +#: engines/scumm/help.cpp:164 engines/scumm/help.cpp:190 #: engines/scumm/help.cpp:208 msgid "Open" msgstr "Åbn" @@ -1740,54 +1672,43 @@ msgstr "Gå til" msgid "Get" msgstr "Tag" -#: engines/scumm/help.cpp:128 -#: engines/scumm/help.cpp:152 -#: engines/scumm/help.cpp:170 -#: engines/scumm/help.cpp:198 -#: engines/scumm/help.cpp:213 -#: engines/scumm/help.cpp:224 +#: engines/scumm/help.cpp:128 engines/scumm/help.cpp:152 +#: engines/scumm/help.cpp:170 engines/scumm/help.cpp:198 +#: engines/scumm/help.cpp:213 engines/scumm/help.cpp:224 #: engines/scumm/help.cpp:250 msgid "Use" msgstr "Brug" -#: engines/scumm/help.cpp:129 -#: engines/scumm/help.cpp:141 +#: engines/scumm/help.cpp:129 engines/scumm/help.cpp:141 msgid "Read" msgstr "Læs" -#: engines/scumm/help.cpp:130 -#: engines/scumm/help.cpp:147 +#: engines/scumm/help.cpp:130 engines/scumm/help.cpp:147 msgid "New kid" msgstr "Nyt barn" -#: engines/scumm/help.cpp:131 -#: engines/scumm/help.cpp:153 +#: engines/scumm/help.cpp:131 engines/scumm/help.cpp:153 #: engines/scumm/help.cpp:171 msgid "Turn on" msgstr "Tænd" -#: engines/scumm/help.cpp:132 -#: engines/scumm/help.cpp:154 +#: engines/scumm/help.cpp:132 engines/scumm/help.cpp:154 #: engines/scumm/help.cpp:172 msgid "Turn off" msgstr "Sluk" -#: engines/scumm/help.cpp:142 -#: engines/scumm/help.cpp:167 +#: engines/scumm/help.cpp:142 engines/scumm/help.cpp:167 #: engines/scumm/help.cpp:194 msgid "Walk to" msgstr "Gå til" -#: engines/scumm/help.cpp:143 -#: engines/scumm/help.cpp:168 -#: engines/scumm/help.cpp:195 -#: engines/scumm/help.cpp:210 +#: engines/scumm/help.cpp:143 engines/scumm/help.cpp:168 +#: engines/scumm/help.cpp:195 engines/scumm/help.cpp:210 #: engines/scumm/help.cpp:227 msgid "Pick up" msgstr "Tag op" -#: engines/scumm/help.cpp:144 -#: engines/scumm/help.cpp:169 +#: engines/scumm/help.cpp:144 engines/scumm/help.cpp:169 msgid "What is" msgstr "Hvad er" @@ -1811,13 +1732,11 @@ msgstr "Lav" msgid "Switch" msgstr "Skift" -#: engines/scumm/help.cpp:166 -#: engines/scumm/help.cpp:228 +#: engines/scumm/help.cpp:166 engines/scumm/help.cpp:228 msgid "Look" msgstr "Se" -#: engines/scumm/help.cpp:173 -#: engines/scumm/help.cpp:223 +#: engines/scumm/help.cpp:173 engines/scumm/help.cpp:223 msgid "Talk" msgstr "Tal" @@ -1862,24 +1781,20 @@ msgstr "spil H på rok" msgid "play C major on distaff" msgstr "spil C-dur på rok" -#: engines/scumm/help.cpp:192 -#: engines/scumm/help.cpp:214 +#: engines/scumm/help.cpp:192 engines/scumm/help.cpp:214 msgid "puSh" msgstr "Skub" -#: engines/scumm/help.cpp:193 -#: engines/scumm/help.cpp:215 +#: engines/scumm/help.cpp:193 engines/scumm/help.cpp:215 msgid "pull (Yank)" msgstr "træk (Y)" -#: engines/scumm/help.cpp:196 -#: engines/scumm/help.cpp:212 +#: engines/scumm/help.cpp:196 engines/scumm/help.cpp:212 #: engines/scumm/help.cpp:248 msgid "Talk to" msgstr "Tal til" -#: engines/scumm/help.cpp:199 -#: engines/scumm/help.cpp:211 +#: engines/scumm/help.cpp:199 engines/scumm/help.cpp:211 msgid "Look at" msgstr "Lur på" @@ -1911,10 +1826,8 @@ msgstr "Fremhæv næste dialog" msgid "Walk" msgstr "Gå" -#: engines/scumm/help.cpp:225 -#: engines/scumm/help.cpp:234 -#: engines/scumm/help.cpp:241 -#: engines/scumm/help.cpp:249 +#: engines/scumm/help.cpp:225 engines/scumm/help.cpp:234 +#: engines/scumm/help.cpp:241 engines/scumm/help.cpp:249 msgid "Inventory" msgstr "Oversigt" @@ -1942,8 +1855,7 @@ msgstr "Slag" msgid "Kick" msgstr "Spark" -#: engines/scumm/help.cpp:239 -#: engines/scumm/help.cpp:247 +#: engines/scumm/help.cpp:239 engines/scumm/help.cpp:247 msgid "Examine" msgstr "Undersøg" @@ -1964,38 +1876,31 @@ msgstr "Gem / Indlæs / Indstillinger" msgid "Other game controls:" msgstr "Andre spil kontroller" -#: engines/scumm/help.cpp:257 -#: engines/scumm/help.cpp:267 +#: engines/scumm/help.cpp:257 engines/scumm/help.cpp:267 msgid "Inventory:" msgstr "Oversigt:" -#: engines/scumm/help.cpp:258 -#: engines/scumm/help.cpp:274 +#: engines/scumm/help.cpp:258 engines/scumm/help.cpp:274 msgid "Scroll list up" msgstr "Rul liste op" -#: engines/scumm/help.cpp:259 -#: engines/scumm/help.cpp:275 +#: engines/scumm/help.cpp:259 engines/scumm/help.cpp:275 msgid "Scroll list down" msgstr "Rul liste ned" -#: engines/scumm/help.cpp:260 -#: engines/scumm/help.cpp:268 +#: engines/scumm/help.cpp:260 engines/scumm/help.cpp:268 msgid "Upper left item" msgstr "Øverste venstre punkt" -#: engines/scumm/help.cpp:261 -#: engines/scumm/help.cpp:270 +#: engines/scumm/help.cpp:261 engines/scumm/help.cpp:270 msgid "Lower left item" msgstr "Nederste højre punkt" -#: engines/scumm/help.cpp:262 -#: engines/scumm/help.cpp:271 +#: engines/scumm/help.cpp:262 engines/scumm/help.cpp:271 msgid "Upper right item" msgstr "Øverste højre punkt" -#: engines/scumm/help.cpp:263 -#: engines/scumm/help.cpp:273 +#: engines/scumm/help.cpp:263 engines/scumm/help.cpp:273 msgid "Lower right item" msgstr "Nederste venstre punkt" @@ -2007,8 +1912,7 @@ msgstr "Midterste højre punkt" msgid "Middle right item" msgstr "Midterste højre punkt" -#: engines/scumm/help.cpp:279 -#: engines/scumm/help.cpp:284 +#: engines/scumm/help.cpp:279 engines/scumm/help.cpp:284 msgid "Switching characters:" msgstr "Skift personer:" @@ -2024,8 +1928,7 @@ msgstr "Tredie barn" msgid "Fighting controls (numpad):" msgstr "Kamp kontroller (numtast):" -#: engines/scumm/help.cpp:295 -#: engines/scumm/help.cpp:296 +#: engines/scumm/help.cpp:295 engines/scumm/help.cpp:296 #: engines/scumm/help.cpp:297 msgid "Step back" msgstr "Skridt tilbage" @@ -2110,7 +2013,7 @@ msgstr "Flyv til højre" msgid "Fly to lower right" msgstr "Flyv nederst til højre" -#: engines/scumm/scumm.cpp:1773 +#: engines/scumm/scumm.cpp:1774 #, c-format msgid "" "Native MIDI support requires the Roland Upgrade from LucasArts,\n" @@ -2119,8 +2022,7 @@ msgstr "" "Indbygget MIDI understøttelse kræver Roland opgradering fra LucasArts,\n" "men %s mangler. Bruger AdLib i stedet." -#: engines/scumm/scumm.cpp:2278 -#: engines/agos/saveload.cpp:202 +#: engines/scumm/scumm.cpp:2295 engines/agos/saveload.cpp:220 #, c-format msgid "" "Failed to save game state to file:\n" @@ -2131,8 +2033,7 @@ msgstr "" "\n" "%s" -#: engines/scumm/scumm.cpp:2285 -#: engines/agos/saveload.cpp:167 +#: engines/scumm/scumm.cpp:2302 engines/agos/saveload.cpp:185 #, c-format msgid "" "Failed to load game state from file:\n" @@ -2143,8 +2044,7 @@ msgstr "" "\n" "%s" -#: engines/scumm/scumm.cpp:2297 -#: engines/agos/saveload.cpp:210 +#: engines/scumm/scumm.cpp:2314 engines/agos/saveload.cpp:228 #, c-format msgid "" "Successfully saved game state in file:\n" @@ -2155,13 +2055,18 @@ msgstr "" "\n" "%s" -#: engines/scumm/scumm.cpp:2512 -msgid "Usually, Maniac Mansion would start now. But ScummVM doesn't do that yet. To play it, go to 'Add Game' in the ScummVM start menu and select the 'Maniac' directory inside the Tentacle game directory." -msgstr "Normalt ville Maniac Mansion begynde nu. Men ScummVM kan ikke gøre det endnu. For at spille det, gå til 'Tilføj spil' i ScummVM start-menuen og vælg 'Maniac' mappen inde i Tentacle spillets mappe." +#: engines/scumm/scumm.cpp:2529 +msgid "" +"Usually, Maniac Mansion would start now. But ScummVM doesn't do that yet. To " +"play it, go to 'Add Game' in the ScummVM start menu and select the 'Maniac' " +"directory inside the Tentacle game directory." +msgstr "" +"Normalt ville Maniac Mansion begynde nu. Men ScummVM kan ikke gøre det " +"endnu. For at spille det, gå til 'Tilføj spil' i ScummVM start-menuen og " +"vælg 'Maniac' mappen inde i Tentacle spillets mappe." #. I18N: Option for fast scene switching -#: engines/mohawk/dialogs.cpp:92 -#: engines/mohawk/dialogs.cpp:171 +#: engines/mohawk/dialogs.cpp:92 engines/mohawk/dialogs.cpp:171 msgid "~Z~ip Mode Activated" msgstr "~Z~ip tilstand aktiveret" @@ -2186,19 +2091,17 @@ msgstr "Hoved~m~enu" msgid "~W~ater Effect Enabled" msgstr "~V~andeffekter aktiveret" -#: engines/agos/animation.cpp:560 +#: engines/agos/animation.cpp:557 #, c-format msgid "Cutscene file '%s' not found!" msgstr "Filmsekvens fil '%s' ikke fundet!" -#: engines/gob/inter_playtoons.cpp:256 -#: engines/gob/inter_v2.cpp:1287 -#: engines/tinsel/saveload.cpp:502 +#: engines/gob/inter_playtoons.cpp:256 engines/gob/inter_v2.cpp:1287 +#: engines/tinsel/saveload.cpp:532 msgid "Failed to load game state from file." msgstr "Mislykkedes at indlæse spil tilstand fra fil." -#: engines/gob/inter_v2.cpp:1357 -#: engines/tinsel/saveload.cpp:515 +#: engines/gob/inter_v2.cpp:1357 engines/tinsel/saveload.cpp:545 msgid "Failed to save game state to file." msgstr "Mislykkedes at gemme spil tilstand til fil." @@ -2316,7 +2219,7 @@ msgstr "Indstillinger" msgid "Choose Spell" msgstr "Vælg magi" -#: engines/kyra/sound_midi.cpp:475 +#: engines/kyra/sound_midi.cpp:477 msgid "" "You appear to be using a General MIDI device,\n" "but your game only supports Roland MT32 MIDI.\n" @@ -2331,13 +2234,12 @@ msgstr "" "at nogle stykker ikke lyder korrekt." #: engines/queen/queen.cpp:59 -#: engines/sky/detection.cpp:44 -msgid "Floppy intro" -msgstr "Diskette intro" +msgid "Alternative intro" +msgstr "" #: engines/queen/queen.cpp:60 -#: engines/sky/detection.cpp:45 -msgid "Use the floppy version's intro (CD version only)" +#, fuzzy +msgid "Use an alternative game intro (CD version only)" msgstr "Brug diskette versionens intro (kun CD version)" #: engines/sky/compact.cpp:130 @@ -2356,40 +2258,52 @@ msgstr "" "\"sky.cpt\" filen har en forkert størrelse.\n" "Venligst (gen)hent den fra www.scummvm.org" -#: engines/sword1/animation.cpp:539 +#: engines/sky/detection.cpp:44 +msgid "Floppy intro" +msgstr "Diskette intro" + +#: engines/sky/detection.cpp:45 +msgid "Use the floppy version's intro (CD version only)" +msgstr "Brug diskette versionens intro (kun CD version)" + +#: engines/sword1/animation.cpp:519 #, c-format msgid "PSX stream cutscene '%s' cannot be played in paletted mode" msgstr "PSX stream filmsekvens '%s' kan ikke afspilles i palette tilstand" -#: engines/sword1/animation.cpp:560 -#: engines/sword2/animation.cpp:455 +#: engines/sword1/animation.cpp:540 engines/sword2/animation.cpp:439 msgid "DXA cutscenes found but ScummVM has been built without zlib support" -msgstr "DXA filmsekvenser fundet, men ScummVM er bygget uden zlib understøttelse" +msgstr "" +"DXA filmsekvenser fundet, men ScummVM er bygget uden zlib understøttelse" -#: engines/sword1/animation.cpp:570 -#: engines/sword2/animation.cpp:465 +#: engines/sword1/animation.cpp:550 engines/sword2/animation.cpp:449 msgid "MPEG2 cutscenes are no longer supported" msgstr "MPEG2 filmsekvenser understøttes ikke længere" -#: engines/sword1/animation.cpp:576 -#: engines/sword2/animation.cpp:473 +#: engines/sword1/animation.cpp:556 engines/sword2/animation.cpp:457 #, c-format msgid "Cutscene '%s' not found" msgstr "Filmsekvens '%s' ikke fundet" -#: engines/sword1/control.cpp:863 +#: engines/sword1/control.cpp:865 msgid "" -"ScummVM found that you have old savefiles for Broken Sword 1 that should be converted.\n" -"The old save game format is no longer supported, so you will not be able to load your games if you don't convert them.\n" +"ScummVM found that you have old savefiles for Broken Sword 1 that should be " +"converted.\n" +"The old save game format is no longer supported, so you will not be able to " +"load your games if you don't convert them.\n" "\n" -"Press OK to convert them now, otherwise you will be asked again the next time you start the game.\n" +"Press OK to convert them now, otherwise you will be asked again the next " +"time you start the game.\n" msgstr "" -"ScummVM har konstateret, at du har gamle gemmer for Broken Sword 1, der skal konverteres.\n" -"Det gamle gemte spil format understøttes ikke længere, så vil du ikke være i stand til at indlæse dine spil, hvis du ikke konvertere dem.\n" +"ScummVM har konstateret, at du har gamle gemmer for Broken Sword 1, der skal " +"konverteres.\n" +"Det gamle gemte spil format understøttes ikke længere, så vil du ikke være i " +"stand til at indlæse dine spil, hvis du ikke konvertere dem.\n" "\n" -"Tryk på OK for at konvertere dem nu, ellers vil du blive spurgt igen, næste gang du starter spillet.\n" +"Tryk på OK for at konvertere dem nu, ellers vil du blive spurgt igen, næste " +"gang du starter spillet.\n" -#: engines/sword1/control.cpp:1232 +#: engines/sword1/control.cpp:1234 #, c-format msgid "" "Target new save game already exists!\n" @@ -2398,11 +2312,11 @@ msgstr "" "Nyt gemt spil findes allerede!\n" "Vil du gerne beholde det gamle gemte spil (%s) eller det nye (%s)?\n" -#: engines/sword1/control.cpp:1235 +#: engines/sword1/control.cpp:1237 msgid "Keep the old one" msgstr "Behold den gamle" -#: engines/sword1/control.cpp:1235 +#: engines/sword1/control.cpp:1237 msgid "Keep the new one" msgstr "Behold den nye" @@ -2410,9 +2324,11 @@ msgstr "Behold den nye" msgid "This is the end of the Broken Sword 1 Demo" msgstr "Dette er slutningen af Broken Sword 1 demoen" -#: engines/sword2/animation.cpp:435 -msgid "PSX cutscenes found but ScummVM has been built without RGB color support" -msgstr "PSX filmsekvenser fundet, men ScummVM er bygget uden RGB farve understøttelse" +#: engines/sword2/animation.cpp:419 +msgid "" +"PSX cutscenes found but ScummVM has been built without RGB color support" +msgstr "" +"PSX filmsekvenser fundet, men ScummVM er bygget uden RGB farve understøttelse" #: engines/sword2/sword2.cpp:79 msgid "Show object labels" @@ -2422,6 +2338,17 @@ msgstr "Vis labels på genstande" msgid "Show labels for objects on mouse hover" msgstr "Vis labels for genstande musen er henover" +#: engines/teenagent/resources.cpp:94 +msgid "" +"You're missing the 'teenagent.dat' file. Get it from the ScummVM website" +msgstr "" + +#: engines/teenagent/resources.cpp:115 +msgid "" +"The teenagent.dat file is compressed and zlib hasn't been included in this " +"executable. Please decompress it" +msgstr "" + #: engines/parallaction/saveload.cpp:133 #, c-format msgid "" @@ -2441,13 +2368,17 @@ msgstr "Gemmer spil..." #: engines/parallaction/saveload.cpp:272 msgid "" -"ScummVM found that you have old savefiles for Nippon Safes that should be renamed.\n" -"The old names are no longer supported, so you will not be able to load your games if you don't convert them.\n" +"ScummVM found that you have old savefiles for Nippon Safes that should be " +"renamed.\n" +"The old names are no longer supported, so you will not be able to load your " +"games if you don't convert them.\n" "\n" "Press OK to convert them now, otherwise you will be asked next time.\n" msgstr "" -"ScummVM har konstateret, at du har gamle gemmer for Nippon Safes, der skal omdøbes.\n" -"De gamle navne er ikke længere understøttet, så du vil ikke være i stand til at indlæse dine spil, hvis du ikke konvertere dem.\n" +"ScummVM har konstateret, at du har gamle gemmer for Nippon Safes, der skal " +"omdøbes.\n" +"De gamle navne er ikke længere understøttet, så du vil ikke være i stand til " +"at indlæse dine spil, hvis du ikke konvertere dem.\n" "\n" "Tryk på OK for at konvertere dem nu, ellers vil du blive spurgt næste gang.\n" @@ -2457,11 +2388,13 @@ msgstr "ScummVM konverterede med succes alle dine gemmer." #: engines/parallaction/saveload.cpp:321 msgid "" -"ScummVM printed some warnings in your console window and can't guarantee all your files have been converted.\n" +"ScummVM printed some warnings in your console window and can't guarantee all " +"your files have been converted.\n" "\n" "Please report to the team." msgstr "" -"ScummVM udskrev nogle advarsler i dit konsol vindue, og kan ikke garantere at alle dine filer er blevet konverteret.\n" +"ScummVM udskrev nogle advarsler i dit konsol vindue, og kan ikke garantere " +"at alle dine filer er blevet konverteret.\n" "\n" "Venligst rapportér til holdet." @@ -2475,30 +2408,43 @@ msgstr "DOSBox OPL emulator" #: audio/mididrv.cpp:209 #, c-format -msgid "The selected audio device '%s' was not found (e.g. might be turned off or disconnected)." -msgstr "Den valgte lydenhed '%s' blev ikke fundet (kan f.eks være slukket eller afbrudt)." +msgid "" +"The selected audio device '%s' was not found (e.g. might be turned off or " +"disconnected)." +msgstr "" +"Den valgte lydenhed '%s' blev ikke fundet (kan f.eks være slukket eller " +"afbrudt)." -#: audio/mididrv.cpp:209 -#: audio/mididrv.cpp:221 -#: audio/mididrv.cpp:257 +#: audio/mididrv.cpp:209 audio/mididrv.cpp:221 audio/mididrv.cpp:257 #: audio/mididrv.cpp:272 msgid "Attempting to fall back to the next available device..." msgstr "Forsøger at falde tilbage til den næste tilgængelig enhed..." #: audio/mididrv.cpp:221 #, c-format -msgid "The selected audio device '%s' cannot be used. See log file for more information." -msgstr "Den valgte lydenhed '%s' kan ikke bruges. Se log filen for mere information." +msgid "" +"The selected audio device '%s' cannot be used. See log file for more " +"information." +msgstr "" +"Den valgte lydenhed '%s' kan ikke bruges. Se log filen for mere information." #: audio/mididrv.cpp:257 #, c-format -msgid "The preferred audio device '%s' was not found (e.g. might be turned off or disconnected)." -msgstr "Den foretrukne lydenhed '%s' blev ikke fundet (kan f.eks være slukket eller afbrudt)." +msgid "" +"The preferred audio device '%s' was not found (e.g. might be turned off or " +"disconnected)." +msgstr "" +"Den foretrukne lydenhed '%s' blev ikke fundet (kan f.eks være slukket eller " +"afbrudt)." #: audio/mididrv.cpp:272 #, c-format -msgid "The preferred audio device '%s' cannot be used. See log file for more information." -msgstr "Den foretrukne lydenhed '%s' kan ikke bruges. Se log filen for mere information." +msgid "" +"The preferred audio device '%s' cannot be used. See log file for more " +"information." +msgstr "" +"Den foretrukne lydenhed '%s' kan ikke bruges. Se log filen for mere " +"information." #: audio/null.h:43 msgid "No music" @@ -2508,7 +2454,7 @@ msgstr "Ingen musik" msgid "Amiga Audio Emulator" msgstr "Amiga lyd emulator" -#: audio/softsynth/adlib.cpp:1593 +#: audio/softsynth/adlib.cpp:2284 msgid "AdLib Emulator" msgstr "AdLib emulator" @@ -2652,11 +2598,11 @@ msgstr "Pegeplade tilstand aktiveret." msgid "Touchpad mode disabled." msgstr "Pegeplade tilstand deaktiveret." -#: backends/platform/maemo/maemo.cpp:205 +#: backends/platform/maemo/maemo.cpp:209 msgid "Click Mode" msgstr "Klik tilstand" -#: backends/platform/maemo/maemo.cpp:211 +#: backends/platform/maemo/maemo.cpp:215 #: backends/platform/symbian/src/SymbianActions.cpp:42 #: backends/platform/wince/CEActionsPocket.cpp:60 #: backends/platform/wince/CEActionsSmartphone.cpp:43 @@ -2664,35 +2610,35 @@ msgstr "Klik tilstand" msgid "Left Click" msgstr "Venstre klik" -#: backends/platform/maemo/maemo.cpp:214 +#: backends/platform/maemo/maemo.cpp:218 msgid "Middle Click" msgstr "Miderste klik" -#: backends/platform/maemo/maemo.cpp:217 +#: backends/platform/maemo/maemo.cpp:221 #: backends/platform/symbian/src/SymbianActions.cpp:43 #: backends/platform/wince/CEActionsSmartphone.cpp:44 #: backends/platform/bada/form.cpp:273 msgid "Right Click" msgstr "Højre klik" -#: backends/platform/sdl/macosx/appmenu_osx.mm:78 +#: backends/platform/sdl/macosx/appmenu_osx.mm:77 msgid "Hide ScummVM" msgstr "Skjul ScummVM" -#: backends/platform/sdl/macosx/appmenu_osx.mm:83 +#: backends/platform/sdl/macosx/appmenu_osx.mm:82 msgid "Hide Others" msgstr "Skjul andre" -#: backends/platform/sdl/macosx/appmenu_osx.mm:88 +#: backends/platform/sdl/macosx/appmenu_osx.mm:87 msgid "Show All" msgstr "Vis alle" -#: backends/platform/sdl/macosx/appmenu_osx.mm:110 -#: backends/platform/sdl/macosx/appmenu_osx.mm:121 +#: backends/platform/sdl/macosx/appmenu_osx.mm:109 +#: backends/platform/sdl/macosx/appmenu_osx.mm:120 msgid "Window" msgstr "Vindue" -#: backends/platform/sdl/macosx/appmenu_osx.mm:115 +#: backends/platform/sdl/macosx/appmenu_osx.mm:114 msgid "Minimize" msgstr "Minimer" @@ -2851,13 +2797,11 @@ msgstr "GC Pad acceleration:" msgid "DVD" msgstr "DVD" -#: backends/platform/wii/options.cpp:89 -#: backends/platform/wii/options.cpp:101 +#: backends/platform/wii/options.cpp:89 backends/platform/wii/options.cpp:101 msgid "Status:" msgstr "Status:" -#: backends/platform/wii/options.cpp:90 -#: backends/platform/wii/options.cpp:102 +#: backends/platform/wii/options.cpp:90 backends/platform/wii/options.cpp:102 msgid "Unknown" msgstr "Ukendt" @@ -3035,7 +2979,8 @@ msgstr "Tildel højreklikshandling" #: backends/platform/wince/wince-sdl.cpp:519 msgid "You must map a key to the 'Right Click' action to play this game" -msgstr "Du skal tildele en tast til 'Højreklik' handlingen for at spille dette spil" +msgstr "" +"Du skal tildele en tast til 'Højreklik' handlingen for at spille dette spil" #: backends/platform/wince/wince-sdl.cpp:528 msgid "Map hide toolbar action" @@ -3043,7 +2988,9 @@ msgstr "Tildel \"skjul værktøjslinje\" handling" #: backends/platform/wince/wince-sdl.cpp:532 msgid "You must map a key to the 'Hide toolbar' action to play this game" -msgstr "Du skal tildele en tast til 'Skjul værktøjslinje' handlingen for at spille dette spil" +msgstr "" +"Du skal tildele en tast til 'Skjul værktøjslinje' handlingen for at spille " +"dette spil" #: backends/platform/wince/wince-sdl.cpp:541 msgid "Map Zoom Up action (optional)" @@ -3054,8 +3001,11 @@ msgid "Map Zoom Down action (optional)" msgstr "Tildel Forstør handling (valgfri)" #: backends/platform/wince/wince-sdl.cpp:552 -msgid "Don't forget to map a key to 'Hide Toolbar' action to see the whole inventory" -msgstr "Glem ikke at tildele en tast til 'Skjul værktøjslinje' handling for at se hele oversigten" +msgid "" +"Don't forget to map a key to 'Hide Toolbar' action to see the whole inventory" +msgstr "" +"Glem ikke at tildele en tast til 'Skjul værktøjslinje' handling for at se " +"hele oversigten" #: backends/events/default/default-events.cpp:191 msgid "Do you really want to return to the Launcher?" @@ -3069,41 +3019,46 @@ msgstr "Oversigt" msgid "Do you really want to quit?" msgstr "Vil du virkelig afslutte?" -#: backends/events/gph/gph-events.cpp:338 -#: backends/events/gph/gph-events.cpp:381 -#: backends/events/openpandora/op-events.cpp:139 +#: backends/events/gph/gph-events.cpp:386 +#: backends/events/gph/gph-events.cpp:429 +#: backends/events/openpandora/op-events.cpp:168 msgid "Touchscreen 'Tap Mode' - Left Click" msgstr "Touchscreen 'Tap Mode' - Venstre Klik" -#: backends/events/gph/gph-events.cpp:340 -#: backends/events/gph/gph-events.cpp:383 -#: backends/events/openpandora/op-events.cpp:141 +#: backends/events/gph/gph-events.cpp:388 +#: backends/events/gph/gph-events.cpp:431 +#: backends/events/openpandora/op-events.cpp:170 msgid "Touchscreen 'Tap Mode' - Right Click" msgstr "Touchscreen 'Tap Mode' - Højre Klik" -#: backends/events/gph/gph-events.cpp:342 -#: backends/events/gph/gph-events.cpp:385 -#: backends/events/openpandora/op-events.cpp:143 +#: backends/events/gph/gph-events.cpp:390 +#: backends/events/gph/gph-events.cpp:433 +#: backends/events/openpandora/op-events.cpp:172 msgid "Touchscreen 'Tap Mode' - Hover (No Click)" msgstr "Touchscreen 'Tap Mode' - Henover (Ingen Klik)" -#: backends/events/gph/gph-events.cpp:362 +#: backends/events/gph/gph-events.cpp:410 msgid "Maximum Volume" msgstr "Maximal lydstyrke" -#: backends/events/gph/gph-events.cpp:364 +#: backends/events/gph/gph-events.cpp:412 msgid "Increasing Volume" msgstr "Hæver lydstyrke" -#: backends/events/gph/gph-events.cpp:370 +#: backends/events/gph/gph-events.cpp:418 msgid "Minimal Volume" msgstr "Minimal lydstyrke" -#: backends/events/gph/gph-events.cpp:372 +#: backends/events/gph/gph-events.cpp:420 msgid "Decreasing Volume" msgstr "Sænker lydstyrke" -#: backends/updates/macosx/macosx-updates.mm:65 +#: backends/events/openpandora/op-events.cpp:174 +#, fuzzy +msgid "Touchscreen 'Tap Mode' - Hover (DPad Clicks)" +msgstr "Touchscreen 'Tap Mode' - Henover (Ingen Klik)" + +#: backends/updates/macosx/macosx-updates.mm:67 msgid "Check for Updates..." msgstr "Søg efter opdateringer..." @@ -3144,20 +3099,20 @@ msgstr "Klik deaktiveret" #~ msgid "Hercules Amber" #~ msgstr "Hercules brun" -#~ msgctxt "lowres" +#~ msgctxt "lowres" #~ msgid "Hercules Green" #~ msgstr "Hercules grøn" -#~ msgctxt "lowres" +#~ msgctxt "lowres" #~ msgid "Hercules Amber" #~ msgstr "Hercules brun" #, fuzzy #~ msgid "Save game failed!" #~ msgstr "Gemmer:" -#~ msgctxt "lowres" +#~ msgctxt "lowres" #~ msgid "Add Game..." #~ msgstr "Tilføj spil..." diff --git a/po/de_DE.po b/po/de_DE.po index c40e08034a..f164a9b76d 100644 --- a/po/de_DE.po +++ b/po/de_DE.po @@ -1,5 +1,5 @@ # German translation for ScummVM. -# Copyright (C) 2010-2012 ScummVM Team +# Copyright (C) 2010-2013 ScummVM Team # This file is distributed under the same license as the ScummVM package. # Simon Sawatzki <SimSaw@gmx.de>, Lothar Serra Mari <Lothar@Windowsbase.de>, 2012. # @@ -7,15 +7,15 @@ msgid "" msgstr "" "Project-Id-Version: ScummVM 1.5.0git\n" "Report-Msgid-Bugs-To: scummvm-devel@lists.sf.net\n" -"POT-Creation-Date: 2012-07-08 12:25+0100\n" -"PO-Revision-Date: 2012-01-29 21:11+0100\n" +"POT-Creation-Date: 2012-12-01 17:22+0000\n" +"PO-Revision-Date: 2012-07-14 22:49+0100\n" "Last-Translator: Simon Sawatzki <SimSaw@gmx.de>\n" "Language-Team: Simon Sawatzki <SimSaw@gmx.de> (Lead), Lothar Serra Mari " -"<Lothar@Windowsbase.de> (Contributor)\n" +"(Contributor)\n" +"Language: Deutsch\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=iso-8859-1\n" "Content-Transfer-Encoding: 8bit\n" -"Language: Deutsch\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" #: gui/about.cpp:91 @@ -46,10 +46,11 @@ msgstr "Pfad hoch" #: gui/browser.cpp:69 gui/chooser.cpp:45 gui/KeysDialog.cpp:43 #: gui/launcher.cpp:345 gui/massadd.cpp:94 gui/options.cpp:1228 -#: gui/saveload.cpp:64 gui/saveload.cpp:173 gui/themebrowser.cpp:54 -#: engines/engine.cpp:442 engines/scumm/dialogs.cpp:190 -#: engines/sword1/control.cpp:865 engines/parallaction/saveload.cpp:274 -#: backends/platform/wii/options.cpp:48 +#: gui/saveload-dialog.cpp:215 gui/saveload-dialog.cpp:275 +#: gui/saveload-dialog.cpp:545 gui/saveload-dialog.cpp:919 +#: gui/themebrowser.cpp:54 engines/engine.cpp:442 +#: engines/scumm/dialogs.cpp:190 engines/sword1/control.cpp:867 +#: engines/parallaction/saveload.cpp:274 backends/platform/wii/options.cpp:48 #: backends/events/default/default-events.cpp:191 #: backends/events/default/default-events.cpp:213 msgid "Cancel" @@ -70,15 +71,15 @@ msgstr "Schließen" msgid "Mouse click" msgstr "Mausklick" -#: gui/gui-manager.cpp:122 base/main.cpp:300 +#: gui/gui-manager.cpp:122 base/main.cpp:301 msgid "Display keyboard" msgstr "Tastatur anzeigen" -#: gui/gui-manager.cpp:126 base/main.cpp:304 +#: gui/gui-manager.cpp:126 base/main.cpp:305 msgid "Remap keys" msgstr "Tasten neu zuweisen" -#: gui/gui-manager.cpp:129 base/main.cpp:307 +#: gui/gui-manager.cpp:129 base/main.cpp:308 msgid "Toggle FullScreen" msgstr "Vollbild EIN/AUS" @@ -92,16 +93,16 @@ msgstr "Zuweisen" #: gui/KeysDialog.cpp:42 gui/launcher.cpp:346 gui/launcher.cpp:1001 #: gui/launcher.cpp:1005 gui/massadd.cpp:91 gui/options.cpp:1229 -#: engines/engine.cpp:361 engines/engine.cpp:372 engines/scumm/dialogs.cpp:192 -#: engines/scumm/scumm.cpp:1775 engines/agos/animation.cpp:561 -#: engines/groovie/script.cpp:420 engines/sky/compact.cpp:131 -#: engines/sky/compact.cpp:141 engines/sword1/animation.cpp:539 -#: engines/sword1/animation.cpp:560 engines/sword1/animation.cpp:570 -#: engines/sword1/animation.cpp:577 engines/sword1/control.cpp:865 -#: engines/sword1/logic.cpp:1633 engines/sword2/animation.cpp:435 -#: engines/sword2/animation.cpp:455 engines/sword2/animation.cpp:465 -#: engines/sword2/animation.cpp:474 engines/parallaction/saveload.cpp:274 -#: backends/platform/wii/options.cpp:47 +#: gui/saveload-dialog.cpp:920 engines/engine.cpp:361 engines/engine.cpp:372 +#: engines/scumm/dialogs.cpp:192 engines/scumm/scumm.cpp:1776 +#: engines/agos/animation.cpp:558 engines/groovie/script.cpp:420 +#: engines/sky/compact.cpp:131 engines/sky/compact.cpp:141 +#: engines/sword1/animation.cpp:519 engines/sword1/animation.cpp:540 +#: engines/sword1/animation.cpp:550 engines/sword1/animation.cpp:557 +#: engines/sword1/control.cpp:867 engines/sword1/logic.cpp:1633 +#: engines/sword2/animation.cpp:419 engines/sword2/animation.cpp:439 +#: engines/sword2/animation.cpp:449 engines/sword2/animation.cpp:458 +#: engines/parallaction/saveload.cpp:274 backends/platform/wii/options.cpp:47 #: backends/platform/wince/CELauncherDialog.cpp:54 msgid "OK" msgstr "OK" @@ -356,7 +357,7 @@ msgstr "Diese Spielkennung ist schon vergeben. Bitte eine andere wählen." msgid "~Q~uit" msgstr "~B~eenden" -#: gui/launcher.cpp:621 backends/platform/sdl/macosx/appmenu_osx.mm:96 +#: gui/launcher.cpp:621 backends/platform/sdl/macosx/appmenu_osx.mm:95 msgid "Quit ScummVM" msgstr "ScummVM beenden" @@ -364,7 +365,7 @@ msgstr "ScummVM beenden" msgid "A~b~out..." msgstr "Übe~r~" -#: gui/launcher.cpp:622 backends/platform/sdl/macosx/appmenu_osx.mm:70 +#: gui/launcher.cpp:622 backends/platform/sdl/macosx/appmenu_osx.mm:69 msgid "About ScummVM" msgstr "Über ScummVM" @@ -441,13 +442,13 @@ msgstr "In Spieleliste suchen" msgid "Search:" msgstr "Suchen:" -#: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/mohawk/myst.cpp:255 +#: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/mohawk/myst.cpp:245 #: engines/mohawk/riven.cpp:716 engines/cruise/menu.cpp:214 msgid "Load game:" msgstr "Spiel laden:" #: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/scumm/dialogs.cpp:188 -#: engines/mohawk/myst.cpp:255 engines/mohawk/riven.cpp:716 +#: engines/mohawk/myst.cpp:245 engines/mohawk/riven.cpp:716 #: engines/cruise/menu.cpp:214 backends/platform/wince/CEActionsPocket.cpp:267 #: backends/platform/wince/CEActionsSmartphone.cpp:231 msgid "Load" @@ -935,68 +936,104 @@ msgstr "" "dieses Thema benutzen wollen, müssen Sie erst zu einer anderen Sprache " "wechseln." -#: gui/saveload.cpp:59 gui/saveload.cpp:257 +#: gui/saveload-dialog.cpp:166 +msgid "List view" +msgstr "" + +#: gui/saveload-dialog.cpp:167 +msgid "Grid view" +msgstr "" + +#: gui/saveload-dialog.cpp:210 gui/saveload-dialog.cpp:358 msgid "No date saved" msgstr "Kein Datum gespeichert" -#: gui/saveload.cpp:60 gui/saveload.cpp:258 +#: gui/saveload-dialog.cpp:211 gui/saveload-dialog.cpp:359 msgid "No time saved" msgstr "Keine Zeit gespeichert" -#: gui/saveload.cpp:61 gui/saveload.cpp:259 +#: gui/saveload-dialog.cpp:212 gui/saveload-dialog.cpp:360 msgid "No playtime saved" msgstr "Keine Spielzeit gespeichert" -#: gui/saveload.cpp:68 gui/saveload.cpp:173 +#: gui/saveload-dialog.cpp:219 gui/saveload-dialog.cpp:275 msgid "Delete" msgstr "Löschen" -#: gui/saveload.cpp:172 +#: gui/saveload-dialog.cpp:274 msgid "Do you really want to delete this savegame?" msgstr "Diesen Spielstand wirklich löschen?" -#: gui/saveload.cpp:282 +#: gui/saveload-dialog.cpp:383 gui/saveload-dialog.cpp:872 msgid "Date: " msgstr "Datum: " -#: gui/saveload.cpp:286 +#: gui/saveload-dialog.cpp:387 gui/saveload-dialog.cpp:878 msgid "Time: " msgstr "Zeit: " -#: gui/saveload.cpp:292 +#: gui/saveload-dialog.cpp:393 gui/saveload-dialog.cpp:886 msgid "Playtime: " msgstr "Spieldauer: " -#: gui/saveload.cpp:305 gui/saveload.cpp:372 +#: gui/saveload-dialog.cpp:406 gui/saveload-dialog.cpp:494 msgid "Untitled savestate" msgstr "Unbenannt" +#: gui/saveload-dialog.cpp:546 +msgid "Next" +msgstr "" + +#: gui/saveload-dialog.cpp:549 +msgid "Prev" +msgstr "" + +#: gui/saveload-dialog.cpp:736 +#, fuzzy +msgid "New Save" +msgstr "Speichern" + +#: gui/saveload-dialog.cpp:736 +#, fuzzy +msgid "Create a new save game" +msgstr "Konnte Spielstand nicht speichern." + +#: gui/saveload-dialog.cpp:865 +#, fuzzy +msgid "Name: " +msgstr "Name:" + +#: gui/saveload-dialog.cpp:937 +#, c-format +msgid "Enter a description for slot %d:" +msgstr "" + #: gui/themebrowser.cpp:44 msgid "Select a Theme" msgstr "Thema auswählen" -#: gui/ThemeEngine.cpp:335 +#: gui/ThemeEngine.cpp:337 msgid "Disabled GFX" msgstr "GFX ausgeschaltet" -#: gui/ThemeEngine.cpp:335 +#: gui/ThemeEngine.cpp:337 msgctxt "lowres" msgid "Disabled GFX" msgstr "GFX ausgeschaltet" -#: gui/ThemeEngine.cpp:336 +#: gui/ThemeEngine.cpp:338 msgid "Standard Renderer (16bpp)" msgstr "Standard-Renderer (16bpp)" -#: gui/ThemeEngine.cpp:336 +#: gui/ThemeEngine.cpp:338 msgid "Standard (16bpp)" msgstr "Standard (16bpp)" -#: gui/ThemeEngine.cpp:338 +#: gui/ThemeEngine.cpp:340 msgid "Antialiased Renderer (16bpp)" msgstr "Kantenglättung (16bpp)" -#: gui/ThemeEngine.cpp:338 +#: gui/ThemeEngine.cpp:340 msgid "Antialiased (16bpp)" msgstr "Kantenglättung (16bpp)" @@ -1004,35 +1041,35 @@ msgstr "Kantenglättung (16bpp)" msgid "Clear value" msgstr "Wert löschen" -#: base/main.cpp:209 +#: base/main.cpp:210 #, c-format msgid "Engine does not support debug level '%s'" msgstr "Engine unterstützt den Debug-Level \"%s\" nicht." -#: base/main.cpp:287 +#: base/main.cpp:288 msgid "Menu" msgstr "Menü" -#: base/main.cpp:290 backends/platform/symbian/src/SymbianActions.cpp:45 +#: base/main.cpp:291 backends/platform/symbian/src/SymbianActions.cpp:45 #: backends/platform/wince/CEActionsPocket.cpp:45 #: backends/platform/wince/CEActionsSmartphone.cpp:46 msgid "Skip" msgstr "Überspringen" -#: base/main.cpp:293 backends/platform/symbian/src/SymbianActions.cpp:50 +#: base/main.cpp:294 backends/platform/symbian/src/SymbianActions.cpp:50 #: backends/platform/wince/CEActionsPocket.cpp:42 msgid "Pause" msgstr "Pause" -#: base/main.cpp:296 +#: base/main.cpp:297 msgid "Skip line" msgstr "Zeile überspringen" -#: base/main.cpp:467 +#: base/main.cpp:468 msgid "Error running game:" msgstr "Fehler beim Ausführen des Spiels:" -#: base/main.cpp:491 +#: base/main.cpp:492 msgid "Could not find any engine capable of running the selected game" msgstr "Konnte keine Spiel-Engine finden, die dieses Spiel starten kann." @@ -1100,18 +1137,18 @@ msgstr "Abbruch durch Benutzer" msgid "Unknown error" msgstr "Unbekannter Fehler" -#: engines/advancedDetector.cpp:324 +#: engines/advancedDetector.cpp:316 #, c-format msgid "The game in '%s' seems to be unknown." msgstr "Das Spiel im Verzeichnis \"%s\" scheint nicht bekannt zu sein." -#: engines/advancedDetector.cpp:325 +#: engines/advancedDetector.cpp:317 msgid "Please, report the following data to the ScummVM team along with name" msgstr "" "Bitte geben Sie die folgenden Daten auf Englisch an das ScummVM-Team weiter " "sowie" -#: engines/advancedDetector.cpp:327 +#: engines/advancedDetector.cpp:319 msgid "of the game you tried to add and its version/language/etc.:" msgstr "" "den Namen des Spiels, das Sie hinzufügen wollten, als auch die Version/" @@ -1151,13 +1188,13 @@ msgid "~R~eturn to Launcher" msgstr "Zur Spiele~l~iste" #: engines/dialogs.cpp:115 engines/agi/saveload.cpp:803 -#: engines/cruise/menu.cpp:212 engines/sci/engine/kfile.cpp:735 +#: engines/cruise/menu.cpp:212 engines/sci/engine/kfile.cpp:742 msgid "Save game:" msgstr "Speichern:" #: engines/dialogs.cpp:115 engines/agi/saveload.cpp:803 #: engines/scumm/dialogs.cpp:187 engines/cruise/menu.cpp:212 -#: engines/sci/engine/kfile.cpp:735 +#: engines/sci/engine/kfile.cpp:742 #: backends/platform/symbian/src/SymbianActions.cpp:44 #: backends/platform/wince/CEActionsPocket.cpp:43 #: backends/platform/wince/CEActionsPocket.cpp:267 @@ -1269,22 +1306,22 @@ msgstr "" msgid "Start anyway" msgstr "Trotzdem starten" -#: engines/agi/detection.cpp:145 engines/dreamweb/detection.cpp:47 -#: engines/sci/detection.cpp:390 +#: engines/agi/detection.cpp:142 engines/dreamweb/detection.cpp:47 +#: engines/sci/detection.cpp:393 msgid "Use original save/load screens" msgstr "Originale Spielstand-Menüs" -#: engines/agi/detection.cpp:146 engines/dreamweb/detection.cpp:48 -#: engines/sci/detection.cpp:391 +#: engines/agi/detection.cpp:143 engines/dreamweb/detection.cpp:48 +#: engines/sci/detection.cpp:394 msgid "Use the original save/load screens, instead of the ScummVM ones" msgstr "" "Verwendet die originalen Menüs zum Speichern und Laden statt der von ScummVM." -#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:831 +#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:838 msgid "Restore game:" msgstr "Spiel laden:" -#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:831 +#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:838 msgid "Restore" msgstr "Laden" @@ -1296,27 +1333,27 @@ msgstr "Modus für helle Palette verwenden" msgid "Display graphics using the game's bright palette" msgstr "Zeigt Grafiken über helle Spielpalette an." -#: engines/sci/detection.cpp:370 +#: engines/sci/detection.cpp:373 msgid "EGA undithering" msgstr "Antifehlerdiffusion für EGA" -#: engines/sci/detection.cpp:371 +#: engines/sci/detection.cpp:374 msgid "Enable undithering in EGA games" msgstr "Aktiviert die Aufhebung der Fehlerdiffusion in EGA-Spielen." -#: engines/sci/detection.cpp:380 +#: engines/sci/detection.cpp:383 msgid "Prefer digital sound effects" msgstr "Digitale Sound-Effekte bevorzugen" -#: engines/sci/detection.cpp:381 +#: engines/sci/detection.cpp:384 msgid "Prefer digital sound effects instead of synthesized ones" msgstr "Bevorzugt digitale Sound-Effekte statt synthethisierter." -#: engines/sci/detection.cpp:400 +#: engines/sci/detection.cpp:403 msgid "Use IMF/Yamaha FB-01 for MIDI output" msgstr "IMF/Yamaha FB-01 für MIDI-Ausgabe verwenden" -#: engines/sci/detection.cpp:401 +#: engines/sci/detection.cpp:404 msgid "" "Use an IBM Music Feature card or a Yamaha FB-01 FM synth module for MIDI " "output" @@ -1324,30 +1361,30 @@ msgstr "" "Verwendet eine Music-Feature-Karte von IBM oder ein Yamaha-FB-01-FM-" "Synthetisierungsmodul für die MIDI-Ausgabe." -#: engines/sci/detection.cpp:411 +#: engines/sci/detection.cpp:414 msgid "Use CD audio" msgstr "CD-Ton verwenden" -#: engines/sci/detection.cpp:412 +#: engines/sci/detection.cpp:415 msgid "Use CD audio instead of in-game audio, if available" msgstr "Verwendet CD-Ton anstatt des Tons im Spiel, sofern verfügbar." -#: engines/sci/detection.cpp:422 +#: engines/sci/detection.cpp:425 msgid "Use Windows cursors" msgstr "Windows-Mauszeiger verwenden" -#: engines/sci/detection.cpp:423 +#: engines/sci/detection.cpp:426 msgid "" "Use the Windows cursors (smaller and monochrome) instead of the DOS ones" msgstr "" "Verwendet die Windows-Mauszeiger (kleiner und schwarz-weiß) anstatt der von " "DOS." -#: engines/sci/detection.cpp:433 +#: engines/sci/detection.cpp:436 msgid "Use silver cursors" msgstr "Silberne Mauszeiger verwenden" -#: engines/sci/detection.cpp:434 +#: engines/sci/detection.cpp:437 msgid "" "Use the alternate set of silver cursors, instead of the normal golden ones" msgstr "" @@ -2001,7 +2038,7 @@ msgstr "Nach rechts fliegen" msgid "Fly to lower right" msgstr "Nach unten rechts fliegen" -#: engines/scumm/scumm.cpp:1773 +#: engines/scumm/scumm.cpp:1774 #, c-format msgid "" "Native MIDI support requires the Roland Upgrade from LucasArts,\n" @@ -2011,7 +2048,7 @@ msgstr "" "Roland-Upgrade von LucasArts, aber %s\n" "fehlt. Stattdessen wird AdLib verwendet." -#: engines/scumm/scumm.cpp:2278 engines/agos/saveload.cpp:202 +#: engines/scumm/scumm.cpp:2295 engines/agos/saveload.cpp:220 #, c-format msgid "" "Failed to save game state to file:\n" @@ -2022,7 +2059,7 @@ msgstr "" "\n" "%s" -#: engines/scumm/scumm.cpp:2285 engines/agos/saveload.cpp:167 +#: engines/scumm/scumm.cpp:2302 engines/agos/saveload.cpp:185 #, c-format msgid "" "Failed to load game state from file:\n" @@ -2033,7 +2070,7 @@ msgstr "" "\n" "%s" -#: engines/scumm/scumm.cpp:2297 engines/agos/saveload.cpp:210 +#: engines/scumm/scumm.cpp:2314 engines/agos/saveload.cpp:228 #, c-format msgid "" "Successfully saved game state in file:\n" @@ -2044,7 +2081,7 @@ msgstr "" "\n" "%s" -#: engines/scumm/scumm.cpp:2512 +#: engines/scumm/scumm.cpp:2529 msgid "" "Usually, Maniac Mansion would start now. But ScummVM doesn't do that yet. To " "play it, go to 'Add Game' in the ScummVM start menu and select the 'Maniac' " @@ -2081,17 +2118,17 @@ msgstr "Haupt~m~enü" msgid "~W~ater Effect Enabled" msgstr "~W~assereffekt aktiviert" -#: engines/agos/animation.cpp:560 +#: engines/agos/animation.cpp:557 #, c-format msgid "Cutscene file '%s' not found!" msgstr "Zwischensequenz \"%s\" nicht gefunden!" #: engines/gob/inter_playtoons.cpp:256 engines/gob/inter_v2.cpp:1287 -#: engines/tinsel/saveload.cpp:502 +#: engines/tinsel/saveload.cpp:532 msgid "Failed to load game state from file." msgstr "Konnte Spielstand aus Datei nicht laden." -#: engines/gob/inter_v2.cpp:1357 engines/tinsel/saveload.cpp:515 +#: engines/gob/inter_v2.cpp:1357 engines/tinsel/saveload.cpp:545 msgid "Failed to save game state to file." msgstr "Konnte Spielstand nicht in Datei speichern." @@ -2209,8 +2246,7 @@ msgstr "Optionen" msgid "Choose Spell" msgstr "Zauberspruch auswählen" -#: engines/kyra/sound_midi.cpp:475 -#, fuzzy +#: engines/kyra/sound_midi.cpp:477 msgid "" "You appear to be using a General MIDI device,\n" "but your game only supports Roland MT32 MIDI.\n" @@ -2220,18 +2256,19 @@ msgid "" msgstr "" "Sie scheinen ein General-MIDI-Gerät zu\n" "verwenden, aber das Spiel unterstützt nur\n" -"Roland MT-32 MIDI. Es wird versucht, die\n" -"Roland-MT-32-Instrumente denen von\n" -"General MIDI zuzuordnen. Es kann jedoch\n" -"vorkommen, dass ein paar Musikstücke nicht\n" +"Roland MT32 MIDI. Es wird versucht, die\n" +"Roland-MT32-Instrumente denen von\n" +"General MIDI zuzuordnen. Es ist dennoch\n" +"möglich, dass ein paar Musikstücke nicht\n" "richtig abgespielt werden." -#: engines/queen/queen.cpp:59 engines/sky/detection.cpp:44 -msgid "Floppy intro" -msgstr "Disketten-Vorspann" +#: engines/queen/queen.cpp:59 +msgid "Alternative intro" +msgstr "" -#: engines/queen/queen.cpp:60 engines/sky/detection.cpp:45 -msgid "Use the floppy version's intro (CD version only)" +#: engines/queen/queen.cpp:60 +#, fuzzy +msgid "Use an alternative game intro (CD version only)" msgstr "Verwendet den Vorspann der Diskettenversion (nur bei CD-Version)." #: engines/sky/compact.cpp:130 @@ -2252,28 +2289,36 @@ msgstr "" "Bitte laden Sie diese Datei (erneut) von\n" "www.scummvm.org herunter." -#: engines/sword1/animation.cpp:539 +#: engines/sky/detection.cpp:44 +msgid "Floppy intro" +msgstr "Disketten-Vorspann" + +#: engines/sky/detection.cpp:45 +msgid "Use the floppy version's intro (CD version only)" +msgstr "Verwendet den Vorspann der Diskettenversion (nur bei CD-Version)." + +#: engines/sword1/animation.cpp:519 #, c-format msgid "PSX stream cutscene '%s' cannot be played in paletted mode" msgstr "" "PSX-Zwischensequenz \"%s\" kann in Palettenmodus nicht wiedergegeben werden." -#: engines/sword1/animation.cpp:560 engines/sword2/animation.cpp:455 +#: engines/sword1/animation.cpp:540 engines/sword2/animation.cpp:439 msgid "DXA cutscenes found but ScummVM has been built without zlib support" msgstr "" "DXA-Zwischensequenzen gefunden, aber ScummVM wurde ohne Zlib-Unterstützung " "erstellt." -#: engines/sword1/animation.cpp:570 engines/sword2/animation.cpp:465 +#: engines/sword1/animation.cpp:550 engines/sword2/animation.cpp:449 msgid "MPEG2 cutscenes are no longer supported" msgstr "MPEG2-Zwischensequenzen werden nicht mehr unterstützt." -#: engines/sword1/animation.cpp:576 engines/sword2/animation.cpp:473 +#: engines/sword1/animation.cpp:556 engines/sword2/animation.cpp:457 #, c-format msgid "Cutscene '%s' not found" msgstr "Zwischensequenz \"%s\" gefunden" -#: engines/sword1/control.cpp:863 +#: engines/sword1/control.cpp:865 msgid "" "ScummVM found that you have old savefiles for Broken Sword 1 that should be " "converted.\n" @@ -2291,7 +2336,7 @@ msgstr "" "Klicken Sie auf OK, um diese jetzt umzuwandeln, sonst werden Sie erneut " "gefragt, wenn Sie nächstes Mal dieses Spiel starten.\n" -#: engines/sword1/control.cpp:1232 +#: engines/sword1/control.cpp:1234 #, c-format msgid "" "Target new save game already exists!\n" @@ -2300,11 +2345,11 @@ msgstr "" "Die für den neuen Spielstand vorgesehene Datei existiert bereits!\n" "Möchten Sie den alten Speicherstand (%s) oder den neuen (%s) behalten?\n" -#: engines/sword1/control.cpp:1235 +#: engines/sword1/control.cpp:1237 msgid "Keep the old one" msgstr "Den alten behalten" -#: engines/sword1/control.cpp:1235 +#: engines/sword1/control.cpp:1237 msgid "Keep the new one" msgstr "Den neuen behalten" @@ -2312,7 +2357,7 @@ msgstr "Den neuen behalten" msgid "This is the end of the Broken Sword 1 Demo" msgstr "Das ist das Ende der Demo von Broken Sword 1 (Baphomets Fluch 1)." -#: engines/sword2/animation.cpp:435 +#: engines/sword2/animation.cpp:419 msgid "" "PSX cutscenes found but ScummVM has been built without RGB color support" msgstr "" @@ -2327,6 +2372,17 @@ msgstr "Objektnamen zeigen" msgid "Show labels for objects on mouse hover" msgstr "Zeigt Objektbeschriftungen bei Mausberührung an." +#: engines/teenagent/resources.cpp:94 +msgid "" +"You're missing the 'teenagent.dat' file. Get it from the ScummVM website" +msgstr "" + +#: engines/teenagent/resources.cpp:115 +msgid "" +"The teenagent.dat file is compressed and zlib hasn't been included in this " +"executable. Please decompress it" +msgstr "" + #: engines/parallaction/saveload.cpp:133 #, c-format msgid "" @@ -2434,7 +2490,7 @@ msgstr "Keine Musik" msgid "Amiga Audio Emulator" msgstr "Amiga-Audio-Emulator" -#: audio/softsynth/adlib.cpp:1593 +#: audio/softsynth/adlib.cpp:2284 msgid "AdLib Emulator" msgstr "AdLib-Emulator" @@ -2578,11 +2634,11 @@ msgstr "Touchpad-Modus aktiviert." msgid "Touchpad mode disabled." msgstr "Touchpad-Modus ausgeschaltet." -#: backends/platform/maemo/maemo.cpp:205 +#: backends/platform/maemo/maemo.cpp:209 msgid "Click Mode" msgstr "Klickmodus" -#: backends/platform/maemo/maemo.cpp:211 +#: backends/platform/maemo/maemo.cpp:215 #: backends/platform/symbian/src/SymbianActions.cpp:42 #: backends/platform/wince/CEActionsPocket.cpp:60 #: backends/platform/wince/CEActionsSmartphone.cpp:43 @@ -2590,35 +2646,35 @@ msgstr "Klickmodus" msgid "Left Click" msgstr "Linksklick" -#: backends/platform/maemo/maemo.cpp:214 +#: backends/platform/maemo/maemo.cpp:218 msgid "Middle Click" msgstr "Mittelklick" -#: backends/platform/maemo/maemo.cpp:217 +#: backends/platform/maemo/maemo.cpp:221 #: backends/platform/symbian/src/SymbianActions.cpp:43 #: backends/platform/wince/CEActionsSmartphone.cpp:44 #: backends/platform/bada/form.cpp:273 msgid "Right Click" msgstr "Rechtsklick" -#: backends/platform/sdl/macosx/appmenu_osx.mm:78 +#: backends/platform/sdl/macosx/appmenu_osx.mm:77 msgid "Hide ScummVM" msgstr "ScummVM ausblenden" -#: backends/platform/sdl/macosx/appmenu_osx.mm:83 +#: backends/platform/sdl/macosx/appmenu_osx.mm:82 msgid "Hide Others" msgstr "Andere ausblenden" -#: backends/platform/sdl/macosx/appmenu_osx.mm:88 +#: backends/platform/sdl/macosx/appmenu_osx.mm:87 msgid "Show All" msgstr "Alle einblenden" -#: backends/platform/sdl/macosx/appmenu_osx.mm:110 -#: backends/platform/sdl/macosx/appmenu_osx.mm:121 +#: backends/platform/sdl/macosx/appmenu_osx.mm:109 +#: backends/platform/sdl/macosx/appmenu_osx.mm:120 msgid "Window" msgstr "Fenster" -#: backends/platform/sdl/macosx/appmenu_osx.mm:115 +#: backends/platform/sdl/macosx/appmenu_osx.mm:114 msgid "Minimize" msgstr "Im Dock ablegen" @@ -3000,41 +3056,46 @@ msgstr "Spieleliste" msgid "Do you really want to quit?" msgstr "Möchten Sie wirklich beenden?" -#: backends/events/gph/gph-events.cpp:338 -#: backends/events/gph/gph-events.cpp:381 -#: backends/events/openpandora/op-events.cpp:139 +#: backends/events/gph/gph-events.cpp:386 +#: backends/events/gph/gph-events.cpp:429 +#: backends/events/openpandora/op-events.cpp:168 msgid "Touchscreen 'Tap Mode' - Left Click" msgstr "Berührungsbildschirm-Tipp-Modus - Linksklick" -#: backends/events/gph/gph-events.cpp:340 -#: backends/events/gph/gph-events.cpp:383 -#: backends/events/openpandora/op-events.cpp:141 +#: backends/events/gph/gph-events.cpp:388 +#: backends/events/gph/gph-events.cpp:431 +#: backends/events/openpandora/op-events.cpp:170 msgid "Touchscreen 'Tap Mode' - Right Click" msgstr "Berührungsbildschirm-Tipp-Modus - Rechtsklick" -#: backends/events/gph/gph-events.cpp:342 -#: backends/events/gph/gph-events.cpp:385 -#: backends/events/openpandora/op-events.cpp:143 +#: backends/events/gph/gph-events.cpp:390 +#: backends/events/gph/gph-events.cpp:433 +#: backends/events/openpandora/op-events.cpp:172 msgid "Touchscreen 'Tap Mode' - Hover (No Click)" msgstr "Berührungsbildschirm-Tipp-Modus - schweben (kein Klick)" -#: backends/events/gph/gph-events.cpp:362 +#: backends/events/gph/gph-events.cpp:410 msgid "Maximum Volume" msgstr "Höchste Lautstärke" -#: backends/events/gph/gph-events.cpp:364 +#: backends/events/gph/gph-events.cpp:412 msgid "Increasing Volume" msgstr "Lautstärke höher" -#: backends/events/gph/gph-events.cpp:370 +#: backends/events/gph/gph-events.cpp:418 msgid "Minimal Volume" msgstr "Niedrigste Lautstärke" -#: backends/events/gph/gph-events.cpp:372 +#: backends/events/gph/gph-events.cpp:420 msgid "Decreasing Volume" msgstr "Lautstärke niedriger" -#: backends/updates/macosx/macosx-updates.mm:65 +#: backends/events/openpandora/op-events.cpp:174 +#, fuzzy +msgid "Touchscreen 'Tap Mode' - Hover (DPad Clicks)" +msgstr "Berührungsbildschirm-Tipp-Modus - schweben (kein Klick)" + +#: backends/updates/macosx/macosx-updates.mm:67 msgid "Check for Updates..." msgstr "Suche nach Aktualisierungen..." diff --git a/po/es_ES.po b/po/es_ES.po index 8e23894dcf..0d38044824 100644 --- a/po/es_ES.po +++ b/po/es_ES.po @@ -1,5 +1,5 @@ # Spanish translation for ScummVM. -# Copyright (C) 2010-2012 ScummVM Team +# Copyright (C) 2010-2013 ScummVM Team # This file is distributed under the same license as the ScummVM package. # Tomás Maidagan, 2011. # @@ -7,14 +7,14 @@ msgid "" msgstr "" "Project-Id-Version: ScummVM 1.4.0svn\n" "Report-Msgid-Bugs-To: scummvm-devel@lists.sf.net\n" -"POT-Creation-Date: 2012-07-08 12:25+0100\n" +"POT-Creation-Date: 2012-12-01 17:22+0000\n" "PO-Revision-Date: 2012-07-08 18:19+0100\n" "Last-Translator: Tomás Maidagan\n" "Language-Team: \n" +"Language: Espanol\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=iso-8859-1\n" "Content-Transfer-Encoding: 8bit\n" -"Language: Espanol\n" #: gui/about.cpp:91 #, c-format @@ -44,10 +44,11 @@ msgstr "Arriba" #: gui/browser.cpp:69 gui/chooser.cpp:45 gui/KeysDialog.cpp:43 #: gui/launcher.cpp:345 gui/massadd.cpp:94 gui/options.cpp:1228 -#: gui/saveload.cpp:64 gui/saveload.cpp:173 gui/themebrowser.cpp:54 -#: engines/engine.cpp:442 engines/scumm/dialogs.cpp:190 -#: engines/sword1/control.cpp:865 engines/parallaction/saveload.cpp:274 -#: backends/platform/wii/options.cpp:48 +#: gui/saveload-dialog.cpp:215 gui/saveload-dialog.cpp:275 +#: gui/saveload-dialog.cpp:545 gui/saveload-dialog.cpp:919 +#: gui/themebrowser.cpp:54 engines/engine.cpp:442 +#: engines/scumm/dialogs.cpp:190 engines/sword1/control.cpp:867 +#: engines/parallaction/saveload.cpp:274 backends/platform/wii/options.cpp:48 #: backends/events/default/default-events.cpp:191 #: backends/events/default/default-events.cpp:213 msgid "Cancel" @@ -68,15 +69,15 @@ msgstr "Cerrar" msgid "Mouse click" msgstr "Clic de ratón" -#: gui/gui-manager.cpp:122 base/main.cpp:300 +#: gui/gui-manager.cpp:122 base/main.cpp:301 msgid "Display keyboard" msgstr "Mostrar el teclado" -#: gui/gui-manager.cpp:126 base/main.cpp:304 +#: gui/gui-manager.cpp:126 base/main.cpp:305 msgid "Remap keys" msgstr "Asignar teclas" -#: gui/gui-manager.cpp:129 base/main.cpp:307 +#: gui/gui-manager.cpp:129 base/main.cpp:308 msgid "Toggle FullScreen" msgstr "Activar pantalla completa" @@ -90,16 +91,16 @@ msgstr "Asignar" #: gui/KeysDialog.cpp:42 gui/launcher.cpp:346 gui/launcher.cpp:1001 #: gui/launcher.cpp:1005 gui/massadd.cpp:91 gui/options.cpp:1229 -#: engines/engine.cpp:361 engines/engine.cpp:372 engines/scumm/dialogs.cpp:192 -#: engines/scumm/scumm.cpp:1775 engines/agos/animation.cpp:561 -#: engines/groovie/script.cpp:420 engines/sky/compact.cpp:131 -#: engines/sky/compact.cpp:141 engines/sword1/animation.cpp:539 -#: engines/sword1/animation.cpp:560 engines/sword1/animation.cpp:570 -#: engines/sword1/animation.cpp:577 engines/sword1/control.cpp:865 -#: engines/sword1/logic.cpp:1633 engines/sword2/animation.cpp:435 -#: engines/sword2/animation.cpp:455 engines/sword2/animation.cpp:465 -#: engines/sword2/animation.cpp:474 engines/parallaction/saveload.cpp:274 -#: backends/platform/wii/options.cpp:47 +#: gui/saveload-dialog.cpp:920 engines/engine.cpp:361 engines/engine.cpp:372 +#: engines/scumm/dialogs.cpp:192 engines/scumm/scumm.cpp:1776 +#: engines/agos/animation.cpp:558 engines/groovie/script.cpp:420 +#: engines/sky/compact.cpp:131 engines/sky/compact.cpp:141 +#: engines/sword1/animation.cpp:519 engines/sword1/animation.cpp:540 +#: engines/sword1/animation.cpp:550 engines/sword1/animation.cpp:557 +#: engines/sword1/control.cpp:867 engines/sword1/logic.cpp:1633 +#: engines/sword2/animation.cpp:419 engines/sword2/animation.cpp:439 +#: engines/sword2/animation.cpp:449 engines/sword2/animation.cpp:458 +#: engines/parallaction/saveload.cpp:274 backends/platform/wii/options.cpp:47 #: backends/platform/wince/CELauncherDialog.cpp:54 msgid "OK" msgstr "Aceptar" @@ -354,7 +355,7 @@ msgstr "Esta ID ya está siendo usada. Por favor, elige otra." msgid "~Q~uit" msgstr "~S~alir" -#: gui/launcher.cpp:621 backends/platform/sdl/macosx/appmenu_osx.mm:96 +#: gui/launcher.cpp:621 backends/platform/sdl/macosx/appmenu_osx.mm:95 msgid "Quit ScummVM" msgstr "Salir de ScummVM" @@ -362,7 +363,7 @@ msgstr "Salir de ScummVM" msgid "A~b~out..." msgstr "Acerca ~d~e" -#: gui/launcher.cpp:622 backends/platform/sdl/macosx/appmenu_osx.mm:70 +#: gui/launcher.cpp:622 backends/platform/sdl/macosx/appmenu_osx.mm:69 msgid "About ScummVM" msgstr "Acerca de ScummVM" @@ -437,13 +438,13 @@ msgstr "Buscar en la lista de juegos" msgid "Search:" msgstr "Buscar:" -#: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/mohawk/myst.cpp:255 +#: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/mohawk/myst.cpp:245 #: engines/mohawk/riven.cpp:716 engines/cruise/menu.cpp:214 msgid "Load game:" msgstr "Cargar juego:" #: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/scumm/dialogs.cpp:188 -#: engines/mohawk/myst.cpp:255 engines/mohawk/riven.cpp:716 +#: engines/mohawk/myst.cpp:245 engines/mohawk/riven.cpp:716 #: engines/cruise/menu.cpp:214 backends/platform/wince/CEActionsPocket.cpp:267 #: backends/platform/wince/CEActionsSmartphone.cpp:231 msgid "Load" @@ -924,68 +925,104 @@ msgstr "" "El tema seleccionado no es compatible con el idioma actual. Si quieres usar " "este tema debes cambiar a otro idioma primero." -#: gui/saveload.cpp:59 gui/saveload.cpp:257 +#: gui/saveload-dialog.cpp:166 +msgid "List view" +msgstr "" + +#: gui/saveload-dialog.cpp:167 +msgid "Grid view" +msgstr "" + +#: gui/saveload-dialog.cpp:210 gui/saveload-dialog.cpp:358 msgid "No date saved" msgstr "No hay fecha guardada" -#: gui/saveload.cpp:60 gui/saveload.cpp:258 +#: gui/saveload-dialog.cpp:211 gui/saveload-dialog.cpp:359 msgid "No time saved" msgstr "No hay hora guardada" -#: gui/saveload.cpp:61 gui/saveload.cpp:259 +#: gui/saveload-dialog.cpp:212 gui/saveload-dialog.cpp:360 msgid "No playtime saved" msgstr "No hay tiempo guardado" -#: gui/saveload.cpp:68 gui/saveload.cpp:173 +#: gui/saveload-dialog.cpp:219 gui/saveload-dialog.cpp:275 msgid "Delete" msgstr "Borrar" -#: gui/saveload.cpp:172 +#: gui/saveload-dialog.cpp:274 msgid "Do you really want to delete this savegame?" msgstr "¿Seguro que quieres borrar esta partida?" -#: gui/saveload.cpp:282 +#: gui/saveload-dialog.cpp:383 gui/saveload-dialog.cpp:872 msgid "Date: " msgstr "Fecha: " -#: gui/saveload.cpp:286 +#: gui/saveload-dialog.cpp:387 gui/saveload-dialog.cpp:878 msgid "Time: " msgstr "Hora: " -#: gui/saveload.cpp:292 +#: gui/saveload-dialog.cpp:393 gui/saveload-dialog.cpp:886 msgid "Playtime: " msgstr "Tiempo: " -#: gui/saveload.cpp:305 gui/saveload.cpp:372 +#: gui/saveload-dialog.cpp:406 gui/saveload-dialog.cpp:494 msgid "Untitled savestate" msgstr "Partida sin nombre" +#: gui/saveload-dialog.cpp:546 +msgid "Next" +msgstr "" + +#: gui/saveload-dialog.cpp:549 +msgid "Prev" +msgstr "" + +#: gui/saveload-dialog.cpp:736 +#, fuzzy +msgid "New Save" +msgstr "Guardar" + +#: gui/saveload-dialog.cpp:736 +#, fuzzy +msgid "Create a new save game" +msgstr "Fallo al guardar la partida" + +#: gui/saveload-dialog.cpp:865 +#, fuzzy +msgid "Name: " +msgstr "Nombre:" + +#: gui/saveload-dialog.cpp:937 +#, c-format +msgid "Enter a description for slot %d:" +msgstr "" + #: gui/themebrowser.cpp:44 msgid "Select a Theme" msgstr "Selecciona un tema" -#: gui/ThemeEngine.cpp:335 +#: gui/ThemeEngine.cpp:337 msgid "Disabled GFX" msgstr "GFX desactivados" -#: gui/ThemeEngine.cpp:335 +#: gui/ThemeEngine.cpp:337 msgctxt "lowres" msgid "Disabled GFX" msgstr "GFX desactivados" -#: gui/ThemeEngine.cpp:336 +#: gui/ThemeEngine.cpp:338 msgid "Standard Renderer (16bpp)" msgstr "Estándar (16bpp)" -#: gui/ThemeEngine.cpp:336 +#: gui/ThemeEngine.cpp:338 msgid "Standard (16bpp)" msgstr "Estándar (16bpp)" -#: gui/ThemeEngine.cpp:338 +#: gui/ThemeEngine.cpp:340 msgid "Antialiased Renderer (16bpp)" msgstr "Suavizado (16bpp)" -#: gui/ThemeEngine.cpp:338 +#: gui/ThemeEngine.cpp:340 msgid "Antialiased (16bpp)" msgstr "Suavizado (16bpp)" @@ -993,35 +1030,35 @@ msgstr "Suavizado (16bpp)" msgid "Clear value" msgstr "Eliminar valor" -#: base/main.cpp:209 +#: base/main.cpp:210 #, c-format msgid "Engine does not support debug level '%s'" msgstr "El motor no es compatible con el nivel de debug '%s'" -#: base/main.cpp:287 +#: base/main.cpp:288 msgid "Menu" msgstr "Menú" -#: base/main.cpp:290 backends/platform/symbian/src/SymbianActions.cpp:45 +#: base/main.cpp:291 backends/platform/symbian/src/SymbianActions.cpp:45 #: backends/platform/wince/CEActionsPocket.cpp:45 #: backends/platform/wince/CEActionsSmartphone.cpp:46 msgid "Skip" msgstr "Saltar" -#: base/main.cpp:293 backends/platform/symbian/src/SymbianActions.cpp:50 +#: base/main.cpp:294 backends/platform/symbian/src/SymbianActions.cpp:50 #: backends/platform/wince/CEActionsPocket.cpp:42 msgid "Pause" msgstr "Pausar" -#: base/main.cpp:296 +#: base/main.cpp:297 msgid "Skip line" msgstr "Saltar frase" -#: base/main.cpp:467 +#: base/main.cpp:468 msgid "Error running game:" msgstr "Error al ejecutar el juego:" -#: base/main.cpp:491 +#: base/main.cpp:492 msgid "Could not find any engine capable of running the selected game" msgstr "No se ha podido encontrar ningún motor capaz de ejecutar el juego" @@ -1089,16 +1126,16 @@ msgstr "Cancel·lat per l'usuari" msgid "Unknown error" msgstr "Error desconocido" -#: engines/advancedDetector.cpp:324 +#: engines/advancedDetector.cpp:316 #, c-format msgid "The game in '%s' seems to be unknown." msgstr "El juego en '%s' parece ser desconocido." -#: engines/advancedDetector.cpp:325 +#: engines/advancedDetector.cpp:317 msgid "Please, report the following data to the ScummVM team along with name" msgstr "Por favor, envía al equipo de ScummVM esta información junto al nombre" -#: engines/advancedDetector.cpp:327 +#: engines/advancedDetector.cpp:319 msgid "of the game you tried to add and its version/language/etc.:" msgstr "del juego que has intentado añadir y su versión/idioma/etc.:" @@ -1136,13 +1173,13 @@ msgid "~R~eturn to Launcher" msgstr "~V~olver al lanzador" #: engines/dialogs.cpp:115 engines/agi/saveload.cpp:803 -#: engines/cruise/menu.cpp:212 engines/sci/engine/kfile.cpp:735 +#: engines/cruise/menu.cpp:212 engines/sci/engine/kfile.cpp:742 msgid "Save game:" msgstr "Guardar partida" #: engines/dialogs.cpp:115 engines/agi/saveload.cpp:803 #: engines/scumm/dialogs.cpp:187 engines/cruise/menu.cpp:212 -#: engines/sci/engine/kfile.cpp:735 +#: engines/sci/engine/kfile.cpp:742 #: backends/platform/symbian/src/SymbianActions.cpp:44 #: backends/platform/wince/CEActionsPocket.cpp:43 #: backends/platform/wince/CEActionsPocket.cpp:267 @@ -1253,22 +1290,22 @@ msgstr "" msgid "Start anyway" msgstr "Jugar aun así" -#: engines/agi/detection.cpp:145 engines/dreamweb/detection.cpp:47 -#: engines/sci/detection.cpp:390 +#: engines/agi/detection.cpp:142 engines/dreamweb/detection.cpp:47 +#: engines/sci/detection.cpp:393 msgid "Use original save/load screens" msgstr "Usar pantallas de guardar/cargar originales" -#: engines/agi/detection.cpp:146 engines/dreamweb/detection.cpp:48 -#: engines/sci/detection.cpp:391 +#: engines/agi/detection.cpp:143 engines/dreamweb/detection.cpp:48 +#: engines/sci/detection.cpp:394 msgid "Use the original save/load screens, instead of the ScummVM ones" msgstr "" "Utilizar las pantallas de guardar/cargar originales, en vez de las de ScummVM" -#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:831 +#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:838 msgid "Restore game:" msgstr "Cargar partida:" -#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:831 +#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:838 msgid "Restore" msgstr "Cargar" @@ -1280,27 +1317,27 @@ msgstr "Usar paleta original" msgid "Display graphics using the game's bright palette" msgstr "Utilizar los niveles de brillo originales del juego" -#: engines/sci/detection.cpp:370 +#: engines/sci/detection.cpp:373 msgid "EGA undithering" msgstr "Difuminado EGA" -#: engines/sci/detection.cpp:371 +#: engines/sci/detection.cpp:374 msgid "Enable undithering in EGA games" msgstr "Activar difuminado en los juegos EGA" -#: engines/sci/detection.cpp:380 +#: engines/sci/detection.cpp:383 msgid "Prefer digital sound effects" msgstr "Preferir efectos de sonido digitales" -#: engines/sci/detection.cpp:381 +#: engines/sci/detection.cpp:384 msgid "Prefer digital sound effects instead of synthesized ones" msgstr "Preferir efectos de sonido digitales en vez de los sintetizados" -#: engines/sci/detection.cpp:400 +#: engines/sci/detection.cpp:403 msgid "Use IMF/Yamaha FB-01 for MIDI output" msgstr "Usar IMF/Yamaha FB-01 para la salida MIDI" -#: engines/sci/detection.cpp:401 +#: engines/sci/detection.cpp:404 msgid "" "Use an IBM Music Feature card or a Yamaha FB-01 FM synth module for MIDI " "output" @@ -1308,29 +1345,29 @@ msgstr "" "Usa una tarjeta IBM Music o un módulo sintetizador Yamaha FB-01 FM para la " "salida MIDI" -#: engines/sci/detection.cpp:411 +#: engines/sci/detection.cpp:414 msgid "Use CD audio" msgstr "Usar CD audio" -#: engines/sci/detection.cpp:412 +#: engines/sci/detection.cpp:415 msgid "Use CD audio instead of in-game audio, if available" msgstr "Usa CD audio en vez del sonido interno del juego, si está disponible" -#: engines/sci/detection.cpp:422 +#: engines/sci/detection.cpp:425 msgid "Use Windows cursors" msgstr "Usar cursores de Windows" -#: engines/sci/detection.cpp:423 +#: engines/sci/detection.cpp:426 msgid "" "Use the Windows cursors (smaller and monochrome) instead of the DOS ones" msgstr "" "Usar los cursores de Windows (más pequeños y monocromos) en vez de los de DOS" -#: engines/sci/detection.cpp:433 +#: engines/sci/detection.cpp:436 msgid "Use silver cursors" msgstr "Usar cursores plateados" -#: engines/sci/detection.cpp:434 +#: engines/sci/detection.cpp:437 msgid "" "Use the alternate set of silver cursors, instead of the normal golden ones" msgstr "" @@ -1983,7 +2020,7 @@ msgstr "Volar a la derecha" msgid "Fly to lower right" msgstr "Volar abajo y a la derecha" -#: engines/scumm/scumm.cpp:1773 +#: engines/scumm/scumm.cpp:1774 #, c-format msgid "" "Native MIDI support requires the Roland Upgrade from LucasArts,\n" @@ -1992,7 +2029,7 @@ msgstr "" "El soporte MIDI nativo requiere la actualización Roland de LucasArts,\n" "pero %s no está disponible. Se usará AdLib." -#: engines/scumm/scumm.cpp:2278 engines/agos/saveload.cpp:202 +#: engines/scumm/scumm.cpp:2295 engines/agos/saveload.cpp:220 #, c-format msgid "" "Failed to save game state to file:\n" @@ -2003,7 +2040,7 @@ msgstr "" "\n" "%s" -#: engines/scumm/scumm.cpp:2285 engines/agos/saveload.cpp:167 +#: engines/scumm/scumm.cpp:2302 engines/agos/saveload.cpp:185 #, c-format msgid "" "Failed to load game state from file:\n" @@ -2014,7 +2051,7 @@ msgstr "" "\n" "%s" -#: engines/scumm/scumm.cpp:2297 engines/agos/saveload.cpp:210 +#: engines/scumm/scumm.cpp:2314 engines/agos/saveload.cpp:228 #, c-format msgid "" "Successfully saved game state in file:\n" @@ -2025,7 +2062,7 @@ msgstr "" "\n" "%s" -#: engines/scumm/scumm.cpp:2512 +#: engines/scumm/scumm.cpp:2529 msgid "" "Usually, Maniac Mansion would start now. But ScummVM doesn't do that yet. To " "play it, go to 'Add Game' in the ScummVM start menu and select the 'Maniac' " @@ -2061,17 +2098,17 @@ msgstr "~M~enú principal" msgid "~W~ater Effect Enabled" msgstr "Efecto ag~u~a activado" -#: engines/agos/animation.cpp:560 +#: engines/agos/animation.cpp:557 #, c-format msgid "Cutscene file '%s' not found!" msgstr "No se ha encontrado el vídeo '%s'" #: engines/gob/inter_playtoons.cpp:256 engines/gob/inter_v2.cpp:1287 -#: engines/tinsel/saveload.cpp:502 +#: engines/tinsel/saveload.cpp:532 msgid "Failed to load game state from file." msgstr "Fallo al cargar el estado del juego desde el archivo." -#: engines/gob/inter_v2.cpp:1357 engines/tinsel/saveload.cpp:515 +#: engines/gob/inter_v2.cpp:1357 engines/tinsel/saveload.cpp:545 msgid "Failed to save game state to file." msgstr "Fallo al guardar el estado del juego en el archivo." @@ -2189,7 +2226,7 @@ msgstr "Opciones" msgid "Choose Spell" msgstr "Elegir hechizo" -#: engines/kyra/sound_midi.cpp:475 +#: engines/kyra/sound_midi.cpp:477 msgid "" "You appear to be using a General MIDI device,\n" "but your game only supports Roland MT32 MIDI.\n" @@ -2203,12 +2240,13 @@ msgstr "" "a los de General MIDI, pero es posible que algunas\n" "de las pistas no suenen correctamente." -#: engines/queen/queen.cpp:59 engines/sky/detection.cpp:44 -msgid "Floppy intro" -msgstr "Intro de disquete" +#: engines/queen/queen.cpp:59 +msgid "Alternative intro" +msgstr "" -#: engines/queen/queen.cpp:60 engines/sky/detection.cpp:45 -msgid "Use the floppy version's intro (CD version only)" +#: engines/queen/queen.cpp:60 +#, fuzzy +msgid "Use an alternative game intro (CD version only)" msgstr "" "Usa la introducción de la versión en disquete (solo para la versión CD)" @@ -2228,26 +2266,35 @@ msgstr "" "El archivo \"sky.cpt\" tiene un tamaño incorrecto.\n" "Por favor, vuelve a bajarlo de www.scummvm.org" -#: engines/sword1/animation.cpp:539 +#: engines/sky/detection.cpp:44 +msgid "Floppy intro" +msgstr "Intro de disquete" + +#: engines/sky/detection.cpp:45 +msgid "Use the floppy version's intro (CD version only)" +msgstr "" +"Usa la introducción de la versión en disquete (solo para la versión CD)" + +#: engines/sword1/animation.cpp:519 #, c-format msgid "PSX stream cutscene '%s' cannot be played in paletted mode" msgstr "El vídeo de PSX '%s' no se puede reproducir en modo paleta" -#: engines/sword1/animation.cpp:560 engines/sword2/animation.cpp:455 +#: engines/sword1/animation.cpp:540 engines/sword2/animation.cpp:439 msgid "DXA cutscenes found but ScummVM has been built without zlib support" msgstr "" "Se han encontrado vídeos DXA, pero se ha compilado ScummVM sin soporte zlib" -#: engines/sword1/animation.cpp:570 engines/sword2/animation.cpp:465 +#: engines/sword1/animation.cpp:550 engines/sword2/animation.cpp:449 msgid "MPEG2 cutscenes are no longer supported" msgstr "Los vídeos MPEG2 ya no son compatibles" -#: engines/sword1/animation.cpp:576 engines/sword2/animation.cpp:473 +#: engines/sword1/animation.cpp:556 engines/sword2/animation.cpp:457 #, c-format msgid "Cutscene '%s' not found" msgstr "No se ha encontrado el vídeo '%s'" -#: engines/sword1/control.cpp:863 +#: engines/sword1/control.cpp:865 msgid "" "ScummVM found that you have old savefiles for Broken Sword 1 that should be " "converted.\n" @@ -2265,7 +2312,7 @@ msgstr "" "Pulsa Aceptar para actualizarlas, si no lo haces este mensaje volverá a " "aparecer la próxima vez.\n" -#: engines/sword1/control.cpp:1232 +#: engines/sword1/control.cpp:1234 #, c-format msgid "" "Target new save game already exists!\n" @@ -2274,11 +2321,11 @@ msgstr "" "¡La partida guardada ya existe!\n" "¿Quieres conservar la partida guardada antigua (%s) o la nueva (%s)?\n" -#: engines/sword1/control.cpp:1235 +#: engines/sword1/control.cpp:1237 msgid "Keep the old one" msgstr "Conservar la antigua" -#: engines/sword1/control.cpp:1235 +#: engines/sword1/control.cpp:1237 msgid "Keep the new one" msgstr "Conservar la nueva" @@ -2286,7 +2333,7 @@ msgstr "Conservar la nueva" msgid "This is the end of the Broken Sword 1 Demo" msgstr "Este es el final de la demo de Broken Sword 1" -#: engines/sword2/animation.cpp:435 +#: engines/sword2/animation.cpp:419 msgid "" "PSX cutscenes found but ScummVM has been built without RGB color support" msgstr "" @@ -2301,6 +2348,17 @@ msgstr "Mostrar etiquetas de objetos" msgid "Show labels for objects on mouse hover" msgstr "Muestra las etiquetas de los objetos al pasar el ratón" +#: engines/teenagent/resources.cpp:94 +msgid "" +"You're missing the 'teenagent.dat' file. Get it from the ScummVM website" +msgstr "" + +#: engines/teenagent/resources.cpp:115 +msgid "" +"The teenagent.dat file is compressed and zlib hasn't been included in this " +"executable. Please decompress it" +msgstr "" + #: engines/parallaction/saveload.cpp:133 #, c-format msgid "" @@ -2408,7 +2466,7 @@ msgstr "Sin música" msgid "Amiga Audio Emulator" msgstr "Emulador de Amiga Audio" -#: audio/softsynth/adlib.cpp:1593 +#: audio/softsynth/adlib.cpp:2284 msgid "AdLib Emulator" msgstr "Emulador de AdLib" @@ -2552,11 +2610,11 @@ msgstr "Modo Touchpad activado." msgid "Touchpad mode disabled." msgstr "Modo Touchpad desactivado." -#: backends/platform/maemo/maemo.cpp:205 +#: backends/platform/maemo/maemo.cpp:209 msgid "Click Mode" msgstr "Modo clic" -#: backends/platform/maemo/maemo.cpp:211 +#: backends/platform/maemo/maemo.cpp:215 #: backends/platform/symbian/src/SymbianActions.cpp:42 #: backends/platform/wince/CEActionsPocket.cpp:60 #: backends/platform/wince/CEActionsSmartphone.cpp:43 @@ -2564,35 +2622,35 @@ msgstr "Modo clic" msgid "Left Click" msgstr "Clic izquierdo" -#: backends/platform/maemo/maemo.cpp:214 +#: backends/platform/maemo/maemo.cpp:218 msgid "Middle Click" msgstr "Clic central" -#: backends/platform/maemo/maemo.cpp:217 +#: backends/platform/maemo/maemo.cpp:221 #: backends/platform/symbian/src/SymbianActions.cpp:43 #: backends/platform/wince/CEActionsSmartphone.cpp:44 #: backends/platform/bada/form.cpp:273 msgid "Right Click" msgstr "Clic derecho" -#: backends/platform/sdl/macosx/appmenu_osx.mm:78 +#: backends/platform/sdl/macosx/appmenu_osx.mm:77 msgid "Hide ScummVM" msgstr "Ocultar ScummVM" -#: backends/platform/sdl/macosx/appmenu_osx.mm:83 +#: backends/platform/sdl/macosx/appmenu_osx.mm:82 msgid "Hide Others" msgstr "Ocultar otros" -#: backends/platform/sdl/macosx/appmenu_osx.mm:88 +#: backends/platform/sdl/macosx/appmenu_osx.mm:87 msgid "Show All" msgstr "Mostrar todo" -#: backends/platform/sdl/macosx/appmenu_osx.mm:110 -#: backends/platform/sdl/macosx/appmenu_osx.mm:121 +#: backends/platform/sdl/macosx/appmenu_osx.mm:109 +#: backends/platform/sdl/macosx/appmenu_osx.mm:120 msgid "Window" msgstr "Ventana" -#: backends/platform/sdl/macosx/appmenu_osx.mm:115 +#: backends/platform/sdl/macosx/appmenu_osx.mm:114 msgid "Minimize" msgstr "Minimizar" @@ -2973,41 +3031,46 @@ msgstr "Lanzador" msgid "Do you really want to quit?" msgstr "¿Realmente quieres salir?" -#: backends/events/gph/gph-events.cpp:338 -#: backends/events/gph/gph-events.cpp:381 -#: backends/events/openpandora/op-events.cpp:139 +#: backends/events/gph/gph-events.cpp:386 +#: backends/events/gph/gph-events.cpp:429 +#: backends/events/openpandora/op-events.cpp:168 msgid "Touchscreen 'Tap Mode' - Left Click" msgstr "'Modo toque' de pantalla táctil - Clic izquierdo" -#: backends/events/gph/gph-events.cpp:340 -#: backends/events/gph/gph-events.cpp:383 -#: backends/events/openpandora/op-events.cpp:141 +#: backends/events/gph/gph-events.cpp:388 +#: backends/events/gph/gph-events.cpp:431 +#: backends/events/openpandora/op-events.cpp:170 msgid "Touchscreen 'Tap Mode' - Right Click" msgstr "'Modo toque' de pantalla táctil - Clic derecho" -#: backends/events/gph/gph-events.cpp:342 -#: backends/events/gph/gph-events.cpp:385 -#: backends/events/openpandora/op-events.cpp:143 +#: backends/events/gph/gph-events.cpp:390 +#: backends/events/gph/gph-events.cpp:433 +#: backends/events/openpandora/op-events.cpp:172 msgid "Touchscreen 'Tap Mode' - Hover (No Click)" msgstr "'Modo toque' de pantalla táctil - Flotante (sin clic)" -#: backends/events/gph/gph-events.cpp:362 +#: backends/events/gph/gph-events.cpp:410 msgid "Maximum Volume" msgstr "Volumen máximo" -#: backends/events/gph/gph-events.cpp:364 +#: backends/events/gph/gph-events.cpp:412 msgid "Increasing Volume" msgstr "Aumentando el volumen" -#: backends/events/gph/gph-events.cpp:370 +#: backends/events/gph/gph-events.cpp:418 msgid "Minimal Volume" msgstr "Volumen mínimo" -#: backends/events/gph/gph-events.cpp:372 +#: backends/events/gph/gph-events.cpp:420 msgid "Decreasing Volume" msgstr "Bajando el volumen" -#: backends/updates/macosx/macosx-updates.mm:65 +#: backends/events/openpandora/op-events.cpp:174 +#, fuzzy +msgid "Touchscreen 'Tap Mode' - Hover (DPad Clicks)" +msgstr "'Modo toque' de pantalla táctil - Flotante (sin clic)" + +#: backends/updates/macosx/macosx-updates.mm:67 msgid "Check for Updates..." msgstr "Buscar actualizaciones..." @@ -1,5 +1,5 @@ # Basque translation for ScummVM. -# Copyright (C) 2011 ScummVM Team +# Copyright (C) 2012-2013 ScummVM Team # This file is distributed under the same license as the ScummVM package. # Mikel Iturbe Urretxa <mikel@hamahiru.org>, 2012. # @@ -7,14 +7,14 @@ msgid "" msgstr "" "Project-Id-Version: ScummVM 1.5.0git\n" "Report-Msgid-Bugs-To: scummvm-devel@lists.sf.net\n" -"POT-Creation-Date: 2012-07-08 12:25+0100\n" +"POT-Creation-Date: 2012-12-01 17:22+0000\n" "PO-Revision-Date: 2011-12-15 14:53+0100\n" "Last-Translator: Mikel Iturbe Urretxa <mikel@hamahiru.org>\n" "Language-Team: Librezale <librezale@librezale.org>\n" +"Language: Euskara\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=iso-8859-1\n" "Content-Transfer-Encoding: 8bit\n" -"Language: Euskara\n" #: gui/about.cpp:91 #, c-format @@ -44,10 +44,11 @@ msgstr "Joan gora" #: gui/browser.cpp:69 gui/chooser.cpp:45 gui/KeysDialog.cpp:43 #: gui/launcher.cpp:345 gui/massadd.cpp:94 gui/options.cpp:1228 -#: gui/saveload.cpp:64 gui/saveload.cpp:173 gui/themebrowser.cpp:54 -#: engines/engine.cpp:442 engines/scumm/dialogs.cpp:190 -#: engines/sword1/control.cpp:865 engines/parallaction/saveload.cpp:274 -#: backends/platform/wii/options.cpp:48 +#: gui/saveload-dialog.cpp:215 gui/saveload-dialog.cpp:275 +#: gui/saveload-dialog.cpp:545 gui/saveload-dialog.cpp:919 +#: gui/themebrowser.cpp:54 engines/engine.cpp:442 +#: engines/scumm/dialogs.cpp:190 engines/sword1/control.cpp:867 +#: engines/parallaction/saveload.cpp:274 backends/platform/wii/options.cpp:48 #: backends/events/default/default-events.cpp:191 #: backends/events/default/default-events.cpp:213 msgid "Cancel" @@ -68,15 +69,15 @@ msgstr "Itxi" msgid "Mouse click" msgstr "Sagu-klika" -#: gui/gui-manager.cpp:122 base/main.cpp:300 +#: gui/gui-manager.cpp:122 base/main.cpp:301 msgid "Display keyboard" msgstr "Teklatua erakutsi" -#: gui/gui-manager.cpp:126 base/main.cpp:304 +#: gui/gui-manager.cpp:126 base/main.cpp:305 msgid "Remap keys" msgstr "Teklak esleitu" -#: gui/gui-manager.cpp:129 base/main.cpp:307 +#: gui/gui-manager.cpp:129 base/main.cpp:308 msgid "Toggle FullScreen" msgstr "Txandakatu pantaila osoa" @@ -90,16 +91,16 @@ msgstr "Esleitu" #: gui/KeysDialog.cpp:42 gui/launcher.cpp:346 gui/launcher.cpp:1001 #: gui/launcher.cpp:1005 gui/massadd.cpp:91 gui/options.cpp:1229 -#: engines/engine.cpp:361 engines/engine.cpp:372 engines/scumm/dialogs.cpp:192 -#: engines/scumm/scumm.cpp:1775 engines/agos/animation.cpp:561 -#: engines/groovie/script.cpp:420 engines/sky/compact.cpp:131 -#: engines/sky/compact.cpp:141 engines/sword1/animation.cpp:539 -#: engines/sword1/animation.cpp:560 engines/sword1/animation.cpp:570 -#: engines/sword1/animation.cpp:577 engines/sword1/control.cpp:865 -#: engines/sword1/logic.cpp:1633 engines/sword2/animation.cpp:435 -#: engines/sword2/animation.cpp:455 engines/sword2/animation.cpp:465 -#: engines/sword2/animation.cpp:474 engines/parallaction/saveload.cpp:274 -#: backends/platform/wii/options.cpp:47 +#: gui/saveload-dialog.cpp:920 engines/engine.cpp:361 engines/engine.cpp:372 +#: engines/scumm/dialogs.cpp:192 engines/scumm/scumm.cpp:1776 +#: engines/agos/animation.cpp:558 engines/groovie/script.cpp:420 +#: engines/sky/compact.cpp:131 engines/sky/compact.cpp:141 +#: engines/sword1/animation.cpp:519 engines/sword1/animation.cpp:540 +#: engines/sword1/animation.cpp:550 engines/sword1/animation.cpp:557 +#: engines/sword1/control.cpp:867 engines/sword1/logic.cpp:1633 +#: engines/sword2/animation.cpp:419 engines/sword2/animation.cpp:439 +#: engines/sword2/animation.cpp:449 engines/sword2/animation.cpp:458 +#: engines/parallaction/saveload.cpp:274 backends/platform/wii/options.cpp:47 #: backends/platform/wince/CELauncherDialog.cpp:54 msgid "OK" msgstr "Ados" @@ -354,7 +355,7 @@ msgstr "ID hau jada erabilia izaten ari da. Mesedez, aukeratu beste bat." msgid "~Q~uit" msgstr "~I~rten" -#: gui/launcher.cpp:621 backends/platform/sdl/macosx/appmenu_osx.mm:96 +#: gui/launcher.cpp:621 backends/platform/sdl/macosx/appmenu_osx.mm:95 msgid "Quit ScummVM" msgstr "Irten ScummVM-tik" @@ -362,7 +363,7 @@ msgstr "Irten ScummVM-tik" msgid "A~b~out..." msgstr "Ho~n~i buruz..." -#: gui/launcher.cpp:622 backends/platform/sdl/macosx/appmenu_osx.mm:70 +#: gui/launcher.cpp:622 backends/platform/sdl/macosx/appmenu_osx.mm:69 msgid "About ScummVM" msgstr "ScummVM-i buruz" @@ -437,13 +438,13 @@ msgstr "Bilatu joko-zerrendan" msgid "Search:" msgstr "Bilatu:" -#: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/mohawk/myst.cpp:255 +#: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/mohawk/myst.cpp:245 #: engines/mohawk/riven.cpp:716 engines/cruise/menu.cpp:214 msgid "Load game:" msgstr "Jokoa kargatu:" #: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/scumm/dialogs.cpp:188 -#: engines/mohawk/myst.cpp:255 engines/mohawk/riven.cpp:716 +#: engines/mohawk/myst.cpp:245 engines/mohawk/riven.cpp:716 #: engines/cruise/menu.cpp:214 backends/platform/wince/CEActionsPocket.cpp:267 #: backends/platform/wince/CEActionsSmartphone.cpp:231 msgid "Load" @@ -925,68 +926,104 @@ msgstr "" "Aukeraturiko gaia ez da zure hizkuntzarekin bateragarria. Gai hau erabili " "nahi baduzu, aurretik beste hizkuntza batera pasa behar duzu." -#: gui/saveload.cpp:59 gui/saveload.cpp:257 +#: gui/saveload-dialog.cpp:166 +msgid "List view" +msgstr "" + +#: gui/saveload-dialog.cpp:167 +msgid "Grid view" +msgstr "" + +#: gui/saveload-dialog.cpp:210 gui/saveload-dialog.cpp:358 msgid "No date saved" msgstr "Ez dago datarik gordeta" -#: gui/saveload.cpp:60 gui/saveload.cpp:258 +#: gui/saveload-dialog.cpp:211 gui/saveload-dialog.cpp:359 msgid "No time saved" msgstr "Ez dago ordurik gordeta" -#: gui/saveload.cpp:61 gui/saveload.cpp:259 +#: gui/saveload-dialog.cpp:212 gui/saveload-dialog.cpp:360 msgid "No playtime saved" msgstr "Ez dago denborarik gordeta" -#: gui/saveload.cpp:68 gui/saveload.cpp:173 +#: gui/saveload-dialog.cpp:219 gui/saveload-dialog.cpp:275 msgid "Delete" msgstr "Ezabatu" -#: gui/saveload.cpp:172 +#: gui/saveload-dialog.cpp:274 msgid "Do you really want to delete this savegame?" msgstr "Ezabatu partida gorde hau?" -#: gui/saveload.cpp:282 +#: gui/saveload-dialog.cpp:383 gui/saveload-dialog.cpp:872 msgid "Date: " msgstr "Data:" -#: gui/saveload.cpp:286 +#: gui/saveload-dialog.cpp:387 gui/saveload-dialog.cpp:878 msgid "Time: " msgstr "Ordua" -#: gui/saveload.cpp:292 +#: gui/saveload-dialog.cpp:393 gui/saveload-dialog.cpp:886 msgid "Playtime: " msgstr "Denbora:" -#: gui/saveload.cpp:305 gui/saveload.cpp:372 +#: gui/saveload-dialog.cpp:406 gui/saveload-dialog.cpp:494 msgid "Untitled savestate" msgstr "Titulurik gabeko partida" +#: gui/saveload-dialog.cpp:546 +msgid "Next" +msgstr "" + +#: gui/saveload-dialog.cpp:549 +msgid "Prev" +msgstr "" + +#: gui/saveload-dialog.cpp:736 +#, fuzzy +msgid "New Save" +msgstr "Gorde" + +#: gui/saveload-dialog.cpp:736 +#, fuzzy +msgid "Create a new save game" +msgstr "Ezin izan da jokoa gorde" + +#: gui/saveload-dialog.cpp:865 +#, fuzzy +msgid "Name: " +msgstr "Izena:" + +#: gui/saveload-dialog.cpp:937 +#, c-format +msgid "Enter a description for slot %d:" +msgstr "" + #: gui/themebrowser.cpp:44 msgid "Select a Theme" msgstr "Gaia aukeratu" -#: gui/ThemeEngine.cpp:335 +#: gui/ThemeEngine.cpp:337 msgid "Disabled GFX" msgstr "GFX desgaituta" -#: gui/ThemeEngine.cpp:335 +#: gui/ThemeEngine.cpp:337 msgctxt "lowres" msgid "Disabled GFX" msgstr "GFX desgaituta" -#: gui/ThemeEngine.cpp:336 +#: gui/ThemeEngine.cpp:338 msgid "Standard Renderer (16bpp)" msgstr "Estandarra (16bpp)" -#: gui/ThemeEngine.cpp:336 +#: gui/ThemeEngine.cpp:338 msgid "Standard (16bpp)" msgstr "Estandarra (16bpp)" -#: gui/ThemeEngine.cpp:338 +#: gui/ThemeEngine.cpp:340 msgid "Antialiased Renderer (16bpp)" msgstr "Lausotua (16bpp)" -#: gui/ThemeEngine.cpp:338 +#: gui/ThemeEngine.cpp:340 msgid "Antialiased (16bpp)" msgstr "Lausotua (16bpp)" @@ -994,35 +1031,35 @@ msgstr "Lausotua (16bpp)" msgid "Clear value" msgstr "Balioa kendu:" -#: base/main.cpp:209 +#: base/main.cpp:210 #, c-format msgid "Engine does not support debug level '%s'" msgstr "Motoreak ez da '%s' debug mailarekin bateragarria" -#: base/main.cpp:287 +#: base/main.cpp:288 msgid "Menu" msgstr "Menua" -#: base/main.cpp:290 backends/platform/symbian/src/SymbianActions.cpp:45 +#: base/main.cpp:291 backends/platform/symbian/src/SymbianActions.cpp:45 #: backends/platform/wince/CEActionsPocket.cpp:45 #: backends/platform/wince/CEActionsSmartphone.cpp:46 msgid "Skip" msgstr "Saltatu" -#: base/main.cpp:293 backends/platform/symbian/src/SymbianActions.cpp:50 +#: base/main.cpp:294 backends/platform/symbian/src/SymbianActions.cpp:50 #: backends/platform/wince/CEActionsPocket.cpp:42 msgid "Pause" msgstr "Gelditu" -#: base/main.cpp:296 +#: base/main.cpp:297 msgid "Skip line" msgstr "Lerroa saltatu" -#: base/main.cpp:467 +#: base/main.cpp:468 msgid "Error running game:" msgstr "Jokoa exekutatzean errorea:" -#: base/main.cpp:491 +#: base/main.cpp:492 msgid "Could not find any engine capable of running the selected game" msgstr "Ezin izan da aukeraturiko jokoa exekutatzeko gai den motorerik aurkitu" @@ -1090,16 +1127,16 @@ msgstr "Erabiltzaileak utzia" msgid "Unknown error" msgstr "Errore ezezaguna" -#: engines/advancedDetector.cpp:324 +#: engines/advancedDetector.cpp:316 #, c-format msgid "The game in '%s' seems to be unknown." msgstr "'%s'-(e)ko jokoa ezezaguna dela dirudi" -#: engines/advancedDetector.cpp:325 +#: engines/advancedDetector.cpp:317 msgid "Please, report the following data to the ScummVM team along with name" msgstr "Mesedez, bidali hurrengo datuak ScummVM taldeari gehitzen saiatu zaren" -#: engines/advancedDetector.cpp:327 +#: engines/advancedDetector.cpp:319 msgid "of the game you tried to add and its version/language/etc.:" msgstr "jokoaren izen, bertsio/hizkuntza/e.a.-ekin batera:" @@ -1137,13 +1174,13 @@ msgid "~R~eturn to Launcher" msgstr "It~z~uli abiarazlera" #: engines/dialogs.cpp:115 engines/agi/saveload.cpp:803 -#: engines/cruise/menu.cpp:212 engines/sci/engine/kfile.cpp:735 +#: engines/cruise/menu.cpp:212 engines/sci/engine/kfile.cpp:742 msgid "Save game:" msgstr "Gorde jokoa:" #: engines/dialogs.cpp:115 engines/agi/saveload.cpp:803 #: engines/scumm/dialogs.cpp:187 engines/cruise/menu.cpp:212 -#: engines/sci/engine/kfile.cpp:735 +#: engines/sci/engine/kfile.cpp:742 #: backends/platform/symbian/src/SymbianActions.cpp:44 #: backends/platform/wince/CEActionsPocket.cpp:43 #: backends/platform/wince/CEActionsPocket.cpp:267 @@ -1251,21 +1288,21 @@ msgstr "" msgid "Start anyway" msgstr "Jolastu berdin-berdin" -#: engines/agi/detection.cpp:145 engines/dreamweb/detection.cpp:47 -#: engines/sci/detection.cpp:390 +#: engines/agi/detection.cpp:142 engines/dreamweb/detection.cpp:47 +#: engines/sci/detection.cpp:393 msgid "Use original save/load screens" msgstr "" -#: engines/agi/detection.cpp:146 engines/dreamweb/detection.cpp:48 -#: engines/sci/detection.cpp:391 +#: engines/agi/detection.cpp:143 engines/dreamweb/detection.cpp:48 +#: engines/sci/detection.cpp:394 msgid "Use the original save/load screens, instead of the ScummVM ones" msgstr "" -#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:831 +#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:838 msgid "Restore game:" msgstr "Jokoa kargatu:" -#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:831 +#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:838 msgid "Restore" msgstr "Kargatu" @@ -1278,57 +1315,57 @@ msgstr "Goiko eskuineko objektua" msgid "Display graphics using the game's bright palette" msgstr "" -#: engines/sci/detection.cpp:370 +#: engines/sci/detection.cpp:373 msgid "EGA undithering" msgstr "EGA lausotzea" -#: engines/sci/detection.cpp:371 +#: engines/sci/detection.cpp:374 #, fuzzy msgid "Enable undithering in EGA games" msgstr "EGA lausotzea gaitu joko bateragarrietan" -#: engines/sci/detection.cpp:380 +#: engines/sci/detection.cpp:383 #, fuzzy msgid "Prefer digital sound effects" msgstr "Soinu efektu berezien bolumena" -#: engines/sci/detection.cpp:381 +#: engines/sci/detection.cpp:384 msgid "Prefer digital sound effects instead of synthesized ones" msgstr "" -#: engines/sci/detection.cpp:400 +#: engines/sci/detection.cpp:403 msgid "Use IMF/Yamaha FB-01 for MIDI output" msgstr "" -#: engines/sci/detection.cpp:401 +#: engines/sci/detection.cpp:404 msgid "" "Use an IBM Music Feature card or a Yamaha FB-01 FM synth module for MIDI " "output" msgstr "" -#: engines/sci/detection.cpp:411 +#: engines/sci/detection.cpp:414 msgid "Use CD audio" msgstr "" -#: engines/sci/detection.cpp:412 +#: engines/sci/detection.cpp:415 msgid "Use CD audio instead of in-game audio, if available" msgstr "" -#: engines/sci/detection.cpp:422 +#: engines/sci/detection.cpp:425 msgid "Use Windows cursors" msgstr "" -#: engines/sci/detection.cpp:423 +#: engines/sci/detection.cpp:426 msgid "" "Use the Windows cursors (smaller and monochrome) instead of the DOS ones" msgstr "" -#: engines/sci/detection.cpp:433 +#: engines/sci/detection.cpp:436 #, fuzzy msgid "Use silver cursors" msgstr "Kurtsore normala" -#: engines/sci/detection.cpp:434 +#: engines/sci/detection.cpp:437 msgid "" "Use the alternate set of silver cursors, instead of the normal golden ones" msgstr "" @@ -1980,7 +2017,7 @@ msgstr "Eskuinera hegan egin" msgid "Fly to lower right" msgstr "Behera eta eskuinera hegan egin" -#: engines/scumm/scumm.cpp:1773 +#: engines/scumm/scumm.cpp:1774 #, c-format msgid "" "Native MIDI support requires the Roland Upgrade from LucasArts,\n" @@ -1989,7 +2026,7 @@ msgstr "" "MIDI euskarri natiboak LucasArts-en Roland eguneraketa behar du,\n" "baina %s ez dago eskuragarri. AdLib erabiliko da." -#: engines/scumm/scumm.cpp:2278 engines/agos/saveload.cpp:202 +#: engines/scumm/scumm.cpp:2295 engines/agos/saveload.cpp:220 #, c-format msgid "" "Failed to save game state to file:\n" @@ -2000,7 +2037,7 @@ msgstr "" "\n" "%s" -#: engines/scumm/scumm.cpp:2285 engines/agos/saveload.cpp:167 +#: engines/scumm/scumm.cpp:2302 engines/agos/saveload.cpp:185 #, c-format msgid "" "Failed to load game state from file:\n" @@ -2011,7 +2048,7 @@ msgstr "" "\n" "%s" -#: engines/scumm/scumm.cpp:2297 engines/agos/saveload.cpp:210 +#: engines/scumm/scumm.cpp:2314 engines/agos/saveload.cpp:228 #, c-format msgid "" "Successfully saved game state in file:\n" @@ -2022,7 +2059,7 @@ msgstr "" "\n" "%s" -#: engines/scumm/scumm.cpp:2512 +#: engines/scumm/scumm.cpp:2529 msgid "" "Usually, Maniac Mansion would start now. But ScummVM doesn't do that yet. To " "play it, go to 'Add Game' in the ScummVM start menu and select the 'Maniac' " @@ -2058,17 +2095,17 @@ msgstr "Menu ~n~agusia" msgid "~W~ater Effect Enabled" msgstr "~U~r-efektua gaituta" -#: engines/agos/animation.cpp:560 +#: engines/agos/animation.cpp:557 #, c-format msgid "Cutscene file '%s' not found!" msgstr "'%s' bideo fitxategia ez da aurkitu!" #: engines/gob/inter_playtoons.cpp:256 engines/gob/inter_v2.cpp:1287 -#: engines/tinsel/saveload.cpp:502 +#: engines/tinsel/saveload.cpp:532 msgid "Failed to load game state from file." msgstr "Ezin izan da fitxategitik jokoa kargatu." -#: engines/gob/inter_v2.cpp:1357 engines/tinsel/saveload.cpp:515 +#: engines/gob/inter_v2.cpp:1357 engines/tinsel/saveload.cpp:545 msgid "Failed to save game state to file." msgstr "Ezin izan da jokoa fitxategira gorde." @@ -2188,7 +2225,7 @@ msgstr "Aukerak" msgid "Choose Spell" msgstr "Sorginkeria aukeratu" -#: engines/kyra/sound_midi.cpp:475 +#: engines/kyra/sound_midi.cpp:477 #, fuzzy msgid "" "You appear to be using a General MIDI device,\n" @@ -2203,12 +2240,12 @@ msgstr "" "General MIDIkoetara egokitzen saiatuko gara,\n" "baina posible da pista batzuk egoki ez entzutea." -#: engines/queen/queen.cpp:59 engines/sky/detection.cpp:44 -msgid "Floppy intro" +#: engines/queen/queen.cpp:59 +msgid "Alternative intro" msgstr "" -#: engines/queen/queen.cpp:60 engines/sky/detection.cpp:45 -msgid "Use the floppy version's intro (CD version only)" +#: engines/queen/queen.cpp:60 +msgid "Use an alternative game intro (CD version only)" msgstr "" #: engines/sky/compact.cpp:130 @@ -2227,26 +2264,34 @@ msgstr "" "\"sky.cpt\" fitxategiak tamaina desegokia du.\n" "Mesdez, jaitsi ezazu (berriz) www.scummvm.org-etik" -#: engines/sword1/animation.cpp:539 +#: engines/sky/detection.cpp:44 +msgid "Floppy intro" +msgstr "" + +#: engines/sky/detection.cpp:45 +msgid "Use the floppy version's intro (CD version only)" +msgstr "" + +#: engines/sword1/animation.cpp:519 #, c-format msgid "PSX stream cutscene '%s' cannot be played in paletted mode" msgstr "" -#: engines/sword1/animation.cpp:560 engines/sword2/animation.cpp:455 +#: engines/sword1/animation.cpp:540 engines/sword2/animation.cpp:439 msgid "DXA cutscenes found but ScummVM has been built without zlib support" msgstr "" "DXA bideoak aurkitu dira, baina ScummVM zlib euskarri gabe konpilatu da" -#: engines/sword1/animation.cpp:570 engines/sword2/animation.cpp:465 +#: engines/sword1/animation.cpp:550 engines/sword2/animation.cpp:449 msgid "MPEG2 cutscenes are no longer supported" msgstr "MPEG2 bideoak ez dira bateragarriak jada" -#: engines/sword1/animation.cpp:576 engines/sword2/animation.cpp:473 +#: engines/sword1/animation.cpp:556 engines/sword2/animation.cpp:457 #, c-format msgid "Cutscene '%s' not found" msgstr "Ez da '%s' bideoa aurkitu" -#: engines/sword1/control.cpp:863 +#: engines/sword1/control.cpp:865 msgid "" "ScummVM found that you have old savefiles for Broken Sword 1 that should be " "converted.\n" @@ -2264,7 +2309,7 @@ msgstr "" "Sakatu Ados orain konbertitzeko, bestela berriz galdetuko dizut jokoa berriz " "martxan jartzen duzunean.\n" -#: engines/sword1/control.cpp:1232 +#: engines/sword1/control.cpp:1234 #, c-format msgid "" "Target new save game already exists!\n" @@ -2273,11 +2318,11 @@ msgstr "" "Gordetako partida jadanik existitzen da!\n" "Gordetako partida zaharra (%s) ala berria (%s) mantendu nahi zenuke?\n" -#: engines/sword1/control.cpp:1235 +#: engines/sword1/control.cpp:1237 msgid "Keep the old one" msgstr "Zaharra mantendu" -#: engines/sword1/control.cpp:1235 +#: engines/sword1/control.cpp:1237 msgid "Keep the new one" msgstr "Berria mantendu" @@ -2285,7 +2330,7 @@ msgstr "Berria mantendu" msgid "This is the end of the Broken Sword 1 Demo" msgstr "Hau Broken Sword 1 Demoaren amaiera da" -#: engines/sword2/animation.cpp:435 +#: engines/sword2/animation.cpp:419 msgid "" "PSX cutscenes found but ScummVM has been built without RGB color support" msgstr "" @@ -2300,6 +2345,17 @@ msgstr "" msgid "Show labels for objects on mouse hover" msgstr "" +#: engines/teenagent/resources.cpp:94 +msgid "" +"You're missing the 'teenagent.dat' file. Get it from the ScummVM website" +msgstr "" + +#: engines/teenagent/resources.cpp:115 +msgid "" +"The teenagent.dat file is compressed and zlib hasn't been included in this " +"executable. Please decompress it" +msgstr "" + #: engines/parallaction/saveload.cpp:133 #, c-format msgid "" @@ -2407,7 +2463,7 @@ msgstr "Musikarik ez" msgid "Amiga Audio Emulator" msgstr "Amiga Audio emuladorea" -#: audio/softsynth/adlib.cpp:1593 +#: audio/softsynth/adlib.cpp:2284 msgid "AdLib Emulator" msgstr "AdLib emuladorea" @@ -2551,11 +2607,11 @@ msgstr "Touchpad modua gaituta." msgid "Touchpad mode disabled." msgstr "Touchpad modua desgaituta." -#: backends/platform/maemo/maemo.cpp:205 +#: backends/platform/maemo/maemo.cpp:209 msgid "Click Mode" msgstr "Klikatzeko modua" -#: backends/platform/maemo/maemo.cpp:211 +#: backends/platform/maemo/maemo.cpp:215 #: backends/platform/symbian/src/SymbianActions.cpp:42 #: backends/platform/wince/CEActionsPocket.cpp:60 #: backends/platform/wince/CEActionsSmartphone.cpp:43 @@ -2563,35 +2619,35 @@ msgstr "Klikatzeko modua" msgid "Left Click" msgstr "Ezker-klika" -#: backends/platform/maemo/maemo.cpp:214 +#: backends/platform/maemo/maemo.cpp:218 msgid "Middle Click" msgstr "Erdiko klika" -#: backends/platform/maemo/maemo.cpp:217 +#: backends/platform/maemo/maemo.cpp:221 #: backends/platform/symbian/src/SymbianActions.cpp:43 #: backends/platform/wince/CEActionsSmartphone.cpp:44 #: backends/platform/bada/form.cpp:273 msgid "Right Click" msgstr "Eskuin-klika" -#: backends/platform/sdl/macosx/appmenu_osx.mm:78 +#: backends/platform/sdl/macosx/appmenu_osx.mm:77 msgid "Hide ScummVM" msgstr "ScummVM ezkutatu" -#: backends/platform/sdl/macosx/appmenu_osx.mm:83 +#: backends/platform/sdl/macosx/appmenu_osx.mm:82 msgid "Hide Others" msgstr "Besteak ezkutatu" -#: backends/platform/sdl/macosx/appmenu_osx.mm:88 +#: backends/platform/sdl/macosx/appmenu_osx.mm:87 msgid "Show All" msgstr "Denak erakutsi" -#: backends/platform/sdl/macosx/appmenu_osx.mm:110 -#: backends/platform/sdl/macosx/appmenu_osx.mm:121 +#: backends/platform/sdl/macosx/appmenu_osx.mm:109 +#: backends/platform/sdl/macosx/appmenu_osx.mm:120 msgid "Window" msgstr "Leihoa" -#: backends/platform/sdl/macosx/appmenu_osx.mm:115 +#: backends/platform/sdl/macosx/appmenu_osx.mm:114 msgid "Minimize" msgstr "Minimizatu" @@ -2972,41 +3028,46 @@ msgstr "Abiarazlea" msgid "Do you really want to quit?" msgstr "Benetan irten?" -#: backends/events/gph/gph-events.cpp:338 -#: backends/events/gph/gph-events.cpp:381 -#: backends/events/openpandora/op-events.cpp:139 +#: backends/events/gph/gph-events.cpp:386 +#: backends/events/gph/gph-events.cpp:429 +#: backends/events/openpandora/op-events.cpp:168 msgid "Touchscreen 'Tap Mode' - Left Click" msgstr "Ukimen-pantailako 'kolpetxo modua' - Ezker klika" -#: backends/events/gph/gph-events.cpp:340 -#: backends/events/gph/gph-events.cpp:383 -#: backends/events/openpandora/op-events.cpp:141 +#: backends/events/gph/gph-events.cpp:388 +#: backends/events/gph/gph-events.cpp:431 +#: backends/events/openpandora/op-events.cpp:170 msgid "Touchscreen 'Tap Mode' - Right Click" msgstr "Ukimen-pantailako 'kolpetxo modua' - Eskuin klika" -#: backends/events/gph/gph-events.cpp:342 -#: backends/events/gph/gph-events.cpp:385 -#: backends/events/openpandora/op-events.cpp:143 +#: backends/events/gph/gph-events.cpp:390 +#: backends/events/gph/gph-events.cpp:433 +#: backends/events/openpandora/op-events.cpp:172 msgid "Touchscreen 'Tap Mode' - Hover (No Click)" msgstr "Ukimen-pantailako 'kolpetxo modua' - Flotatu (klikik ez)" -#: backends/events/gph/gph-events.cpp:362 +#: backends/events/gph/gph-events.cpp:410 msgid "Maximum Volume" msgstr "Bolumen maximoa" -#: backends/events/gph/gph-events.cpp:364 +#: backends/events/gph/gph-events.cpp:412 msgid "Increasing Volume" msgstr "Bolumena igotzen" -#: backends/events/gph/gph-events.cpp:370 +#: backends/events/gph/gph-events.cpp:418 msgid "Minimal Volume" msgstr "Bolumen minimoa" -#: backends/events/gph/gph-events.cpp:372 +#: backends/events/gph/gph-events.cpp:420 msgid "Decreasing Volume" msgstr "Bolumena jaisten" -#: backends/updates/macosx/macosx-updates.mm:65 +#: backends/events/openpandora/op-events.cpp:174 +#, fuzzy +msgid "Touchscreen 'Tap Mode' - Hover (DPad Clicks)" +msgstr "Ukimen-pantailako 'kolpetxo modua' - Flotatu (klikik ez)" + +#: backends/updates/macosx/macosx-updates.mm:67 msgid "Check for Updates..." msgstr "Eguneraketak bilatzen..." diff --git a/po/fi_FI.po b/po/fi_FI.po new file mode 100644 index 0000000000..d2fda71b7d --- /dev/null +++ b/po/fi_FI.po @@ -0,0 +1,3096 @@ +# Finnish translation for ScummVM. +# Copyright (c) 2012-2013 ScummVM Team +# This file is distributed under the same license as the ScummVM package. +# Toni Saarela <saarela@gmail.com>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: ScummVM 1.6.0git\n" +"Report-Msgid-Bugs-To: scummvm-devel@lists.sf.net\n" +"POT-Creation-Date: 2012-12-01 17:22+0000\n" +"PO-Revision-Date: 2012-12-01 19:37+0200\n" +"Last-Translator: Toni Saarela <saarela@gmail.com>\n" +"Language-Team: Finnish\n" +"Language: Suomi\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=iso-8859-1\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 1.5.4\n" + +#: gui/about.cpp:91 +#, c-format +msgid "(built on %s)" +msgstr "(käännöksen päiväys: %s)" + +#: gui/about.cpp:98 +msgid "Features compiled in:" +msgstr "Tähän versioon käännetyt ominaisuudet:" + +#: gui/about.cpp:107 +msgid "Available engines:" +msgstr "Tuetut pelimoottorit:" + +#: gui/browser.cpp:66 +msgid "Go up" +msgstr "Siirry ylös" + +#: gui/browser.cpp:66 gui/browser.cpp:68 +msgid "Go to previous directory level" +msgstr "Palaa edelliselle hakemistotasolle" + +#: gui/browser.cpp:68 +msgctxt "lowres" +msgid "Go up" +msgstr "Siirry ylös" + +#: gui/browser.cpp:69 gui/chooser.cpp:45 gui/KeysDialog.cpp:43 +#: gui/launcher.cpp:345 gui/massadd.cpp:94 gui/options.cpp:1228 +#: gui/saveload-dialog.cpp:215 gui/saveload-dialog.cpp:275 +#: gui/saveload-dialog.cpp:545 gui/saveload-dialog.cpp:919 +#: gui/themebrowser.cpp:54 engines/engine.cpp:442 +#: engines/scumm/dialogs.cpp:190 engines/sword1/control.cpp:867 +#: engines/parallaction/saveload.cpp:274 backends/platform/wii/options.cpp:48 +#: backends/events/default/default-events.cpp:191 +#: backends/events/default/default-events.cpp:213 +msgid "Cancel" +msgstr "Peruuta" + +#: gui/browser.cpp:70 gui/chooser.cpp:46 gui/themebrowser.cpp:55 +msgid "Choose" +msgstr "Valitse" + +#: gui/gui-manager.cpp:115 engines/scumm/help.cpp:125 +#: engines/scumm/help.cpp:140 engines/scumm/help.cpp:165 +#: engines/scumm/help.cpp:191 engines/scumm/help.cpp:209 +#: backends/keymapper/remap-dialog.cpp:52 +msgid "Close" +msgstr "Sulje" + +#: gui/gui-manager.cpp:118 +msgid "Mouse click" +msgstr "Hiiren klikkaus" + +#: gui/gui-manager.cpp:122 base/main.cpp:301 +msgid "Display keyboard" +msgstr "Näytä näppäimistö" + +#: gui/gui-manager.cpp:126 base/main.cpp:305 +msgid "Remap keys" +msgstr "Määritä näppäimet uudelleen" + +#: gui/gui-manager.cpp:129 base/main.cpp:308 +msgid "Toggle FullScreen" +msgstr "Kokoruututilan vaihto" + +#: gui/KeysDialog.h:36 gui/KeysDialog.cpp:145 +msgid "Choose an action to map" +msgstr "Valitse toiminto" + +#: gui/KeysDialog.cpp:41 +msgid "Map" +msgstr "Näppäinkartta" + +#: gui/KeysDialog.cpp:42 gui/launcher.cpp:346 gui/launcher.cpp:1001 +#: gui/launcher.cpp:1005 gui/massadd.cpp:91 gui/options.cpp:1229 +#: gui/saveload-dialog.cpp:920 engines/engine.cpp:361 engines/engine.cpp:372 +#: engines/scumm/dialogs.cpp:192 engines/scumm/scumm.cpp:1776 +#: engines/agos/animation.cpp:558 engines/groovie/script.cpp:420 +#: engines/sky/compact.cpp:131 engines/sky/compact.cpp:141 +#: engines/sword1/animation.cpp:519 engines/sword1/animation.cpp:540 +#: engines/sword1/animation.cpp:550 engines/sword1/animation.cpp:557 +#: engines/sword1/control.cpp:867 engines/sword1/logic.cpp:1633 +#: engines/sword2/animation.cpp:419 engines/sword2/animation.cpp:439 +#: engines/sword2/animation.cpp:449 engines/sword2/animation.cpp:458 +#: engines/parallaction/saveload.cpp:274 backends/platform/wii/options.cpp:47 +#: backends/platform/wince/CELauncherDialog.cpp:54 +msgid "OK" +msgstr "Tallenna" + +#: gui/KeysDialog.cpp:49 +msgid "Select an action and click 'Map'" +msgstr "Valitse toiminto ja klikkaa 'Map'" + +#: gui/KeysDialog.cpp:80 gui/KeysDialog.cpp:102 gui/KeysDialog.cpp:141 +#, c-format +msgid "Associated key : %s" +msgstr "Nykyinen näppäin : %s" + +#: gui/KeysDialog.cpp:82 gui/KeysDialog.cpp:104 gui/KeysDialog.cpp:143 +#, c-format +msgid "Associated key : none" +msgstr "Nykyinen näppäin : ei mikään" + +#: gui/KeysDialog.cpp:90 +msgid "Please select an action" +msgstr "Valitse toiminto" + +#: gui/KeysDialog.cpp:106 +msgid "Press the key to associate" +msgstr "Paina haluamaasi nappia" + +#: gui/launcher.cpp:187 +msgid "Game" +msgstr "Peli" + +#: gui/launcher.cpp:191 +msgid "ID:" +msgstr "Tunniste:" + +#: gui/launcher.cpp:191 gui/launcher.cpp:193 gui/launcher.cpp:194 +msgid "" +"Short game identifier used for referring to savegames and running the game " +"from the command line" +msgstr "" +"Lyhyt pelitunniste, jota käytetään kun viitataan pelitallennuksiin ja kun " +"peli käynnistetään komentoriviltä" + +#: gui/launcher.cpp:193 +msgctxt "lowres" +msgid "ID:" +msgstr "Tunniste:" + +#: gui/launcher.cpp:198 +msgid "Name:" +msgstr "Nimi:" + +#: gui/launcher.cpp:198 gui/launcher.cpp:200 gui/launcher.cpp:201 +msgid "Full title of the game" +msgstr "Pelin koko nimi" + +#: gui/launcher.cpp:200 +msgctxt "lowres" +msgid "Name:" +msgstr "Nimi:" + +#: gui/launcher.cpp:204 +msgid "Language:" +msgstr "Kieli:" + +#: gui/launcher.cpp:204 gui/launcher.cpp:205 +msgid "" +"Language of the game. This will not turn your Spanish game version into " +"English" +msgstr "" +"Pelin kieli. Tämä ei muuta esimerkiksi espanjankielistä versiota pelistä " +"englanninkieliseksi." + +#: gui/launcher.cpp:206 gui/launcher.cpp:220 gui/options.cpp:80 +#: gui/options.cpp:730 gui/options.cpp:743 gui/options.cpp:1199 +#: audio/null.cpp:40 +msgid "<default>" +msgstr "<oletus>" + +#: gui/launcher.cpp:216 +msgid "Platform:" +msgstr "Alusta:" + +#: gui/launcher.cpp:216 gui/launcher.cpp:218 gui/launcher.cpp:219 +msgid "Platform the game was originally designed for" +msgstr "Alusta jolle peli alunperin suunniteltiin" + +#: gui/launcher.cpp:218 +msgctxt "lowres" +msgid "Platform:" +msgstr "Alusta:" + +#: gui/launcher.cpp:231 +msgid "Engine" +msgstr "Moottori" + +#: gui/launcher.cpp:239 gui/options.cpp:1062 gui/options.cpp:1079 +msgid "Graphics" +msgstr "Grafiikka" + +#: gui/launcher.cpp:239 gui/options.cpp:1062 gui/options.cpp:1079 +msgid "GFX" +msgstr "GFX" + +#: gui/launcher.cpp:242 +msgid "Override global graphic settings" +msgstr "Ohita globaalit grafiikka-asetukset" + +#: gui/launcher.cpp:244 +msgctxt "lowres" +msgid "Override global graphic settings" +msgstr "Ohita globaalit grafiikka-asetukset" + +#: gui/launcher.cpp:251 gui/options.cpp:1085 +msgid "Audio" +msgstr "Ääni" + +#: gui/launcher.cpp:254 +msgid "Override global audio settings" +msgstr "Ohita globaalit ääniasetukset" + +#: gui/launcher.cpp:256 +msgctxt "lowres" +msgid "Override global audio settings" +msgstr "Ohita globaalit ääniasetukset" + +#: gui/launcher.cpp:265 gui/options.cpp:1090 +msgid "Volume" +msgstr "Voimakkuus" + +#: gui/launcher.cpp:267 gui/options.cpp:1092 +msgctxt "lowres" +msgid "Volume" +msgstr "Voimakkuus" + +#: gui/launcher.cpp:270 +msgid "Override global volume settings" +msgstr "Ohita globaalit äänenvoimakkuusasetukset" + +#: gui/launcher.cpp:272 +msgctxt "lowres" +msgid "Override global volume settings" +msgstr "Ohita globaalit äänenvoimakkuusasetukset" + +#: gui/launcher.cpp:280 gui/options.cpp:1100 +msgid "MIDI" +msgstr "MIDI" + +#: gui/launcher.cpp:283 +msgid "Override global MIDI settings" +msgstr "Ohita globaalit MIDI-asetukset" + +#: gui/launcher.cpp:285 +msgctxt "lowres" +msgid "Override global MIDI settings" +msgstr "Ohita globaalit MIDI-asetukset" + +#: gui/launcher.cpp:294 gui/options.cpp:1106 +msgid "MT-32" +msgstr "MT-32" + +#: gui/launcher.cpp:297 +msgid "Override global MT-32 settings" +msgstr "Ohita globaalit MT-32 asetukset" + +#: gui/launcher.cpp:299 +msgctxt "lowres" +msgid "Override global MT-32 settings" +msgstr "Ohita globaalit MT-32 asetukset" + +#: gui/launcher.cpp:308 gui/options.cpp:1113 +msgid "Paths" +msgstr "Polut" + +#: gui/launcher.cpp:310 gui/options.cpp:1115 +msgctxt "lowres" +msgid "Paths" +msgstr "Polut" + +#: gui/launcher.cpp:317 +msgid "Game Path:" +msgstr "Pelin polku:" + +#: gui/launcher.cpp:319 +msgctxt "lowres" +msgid "Game Path:" +msgstr "Pelin polku:" + +#: gui/launcher.cpp:324 gui/options.cpp:1139 +msgid "Extra Path:" +msgstr "Lisäkansio:" + +#: gui/launcher.cpp:324 gui/launcher.cpp:326 gui/launcher.cpp:327 +msgid "Specifies path to additional data used the game" +msgstr "Määrittää polun lisätiedostoihin joita peli mahdollisesti käyttää" + +#: gui/launcher.cpp:326 gui/options.cpp:1141 +msgctxt "lowres" +msgid "Extra Path:" +msgstr "Lisäkansio:" + +#: gui/launcher.cpp:333 gui/options.cpp:1123 +msgid "Save Path:" +msgstr "Tallennuskansio:" + +#: gui/launcher.cpp:333 gui/launcher.cpp:335 gui/launcher.cpp:336 +#: gui/options.cpp:1123 gui/options.cpp:1125 gui/options.cpp:1126 +msgid "Specifies where your savegames are put" +msgstr "Määrittää polun pelitallennuksille" + +#: gui/launcher.cpp:335 gui/options.cpp:1125 +msgctxt "lowres" +msgid "Save Path:" +msgstr "Tallennuskansio:" + +#: gui/launcher.cpp:354 gui/launcher.cpp:453 gui/launcher.cpp:511 +#: gui/launcher.cpp:565 gui/options.cpp:1134 gui/options.cpp:1142 +#: gui/options.cpp:1151 gui/options.cpp:1258 gui/options.cpp:1264 +#: gui/options.cpp:1272 gui/options.cpp:1302 gui/options.cpp:1308 +#: gui/options.cpp:1315 gui/options.cpp:1408 gui/options.cpp:1411 +#: gui/options.cpp:1423 +msgctxt "path" +msgid "None" +msgstr "Ei määritelty" + +#: gui/launcher.cpp:359 gui/launcher.cpp:459 gui/launcher.cpp:569 +#: gui/options.cpp:1252 gui/options.cpp:1296 gui/options.cpp:1414 +#: backends/platform/wii/options.cpp:56 +msgid "Default" +msgstr "Oletus" + +#: gui/launcher.cpp:504 gui/options.cpp:1417 +msgid "Select SoundFont" +msgstr "Valitse äänifontti" + +#: gui/launcher.cpp:523 gui/launcher.cpp:677 +msgid "Select directory with game data" +msgstr "Valitse pelin kansio" + +#: gui/launcher.cpp:541 +msgid "Select additional game directory" +msgstr "Valitse lisäkansio pelille" + +#: gui/launcher.cpp:553 +msgid "Select directory for saved games" +msgstr "Valitse kansio pelitallennuksille" + +#: gui/launcher.cpp:580 +msgid "This game ID is already taken. Please choose another one." +msgstr "Pelin tunnus on jo käytössä. Valitse jokin muu." + +#: gui/launcher.cpp:621 engines/dialogs.cpp:110 +msgid "~Q~uit" +msgstr "~L~opeta" + +#: gui/launcher.cpp:621 backends/platform/sdl/macosx/appmenu_osx.mm:95 +msgid "Quit ScummVM" +msgstr "Lopeta ScummVM" + +#: gui/launcher.cpp:622 +msgid "A~b~out..." +msgstr "Tietoa..." + +#: gui/launcher.cpp:622 backends/platform/sdl/macosx/appmenu_osx.mm:69 +msgid "About ScummVM" +msgstr "Tietoa ScummVM:stä" + +#: gui/launcher.cpp:623 +msgid "~O~ptions..." +msgstr "~A~setukset" + +#: gui/launcher.cpp:623 +msgid "Change global ScummVM options" +msgstr "Muuta globaaleja ScummVM:n asetuksia" + +#: gui/launcher.cpp:625 +msgid "~S~tart" +msgstr "~P~elaa" + +#: gui/launcher.cpp:625 +msgid "Start selected game" +msgstr "Pelaa valittua peliä" + +#: gui/launcher.cpp:628 +msgid "~L~oad..." +msgstr "~L~ataa..." + +#: gui/launcher.cpp:628 +msgid "Load savegame for selected game" +msgstr "Lataa pelitallennus valitulle pelille" + +#: gui/launcher.cpp:633 gui/launcher.cpp:1120 +msgid "~A~dd Game..." +msgstr "~L~isää peli..." + +#: gui/launcher.cpp:633 gui/launcher.cpp:640 +msgid "Hold Shift for Mass Add" +msgstr "Pidä Shift-näppäintä pohjassa lisätäksesi useita pelejä kerralla" + +#: gui/launcher.cpp:635 +msgid "~E~dit Game..." +msgstr "Muokkaa peliä..." + +#: gui/launcher.cpp:635 gui/launcher.cpp:642 +msgid "Change game options" +msgstr "Muuta pelin asetuksia" + +#: gui/launcher.cpp:637 +msgid "~R~emove Game" +msgstr "Poista peli" + +#: gui/launcher.cpp:637 gui/launcher.cpp:644 +msgid "Remove game from the list. The game data files stay intact" +msgstr "Poista peli listasta. Pelin tiedostoja ei poisteta levyltä" + +#: gui/launcher.cpp:640 gui/launcher.cpp:1120 +msgctxt "lowres" +msgid "~A~dd Game..." +msgstr "Lisää peli..." + +#: gui/launcher.cpp:642 +msgctxt "lowres" +msgid "~E~dit Game..." +msgstr "Muokkaa peliä..." + +#: gui/launcher.cpp:644 +msgctxt "lowres" +msgid "~R~emove Game" +msgstr "Poista peli..." + +#: gui/launcher.cpp:652 +msgid "Search in game list" +msgstr "Etsi peliä listasta" + +#: gui/launcher.cpp:656 gui/launcher.cpp:1167 +msgid "Search:" +msgstr "Etsi:" + +#: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/mohawk/myst.cpp:245 +#: engines/mohawk/riven.cpp:716 engines/cruise/menu.cpp:214 +msgid "Load game:" +msgstr "Lataa peli:" + +#: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/scumm/dialogs.cpp:188 +#: engines/mohawk/myst.cpp:245 engines/mohawk/riven.cpp:716 +#: engines/cruise/menu.cpp:214 backends/platform/wince/CEActionsPocket.cpp:267 +#: backends/platform/wince/CEActionsSmartphone.cpp:231 +msgid "Load" +msgstr "Lataa" + +#: gui/launcher.cpp:788 +msgid "" +"Do you really want to run the mass game detector? This could potentially add " +"a huge number of games." +msgstr "" +"Haluatko varmasti lisätä pelejä alihakemistoineen? Tämä voi lisätä suuren " +"määrän pelejä." + +#: gui/launcher.cpp:789 gui/launcher.cpp:937 +#: backends/events/symbiansdl/symbiansdl-events.cpp:184 +#: backends/platform/wince/CEActionsPocket.cpp:326 +#: backends/platform/wince/CEActionsSmartphone.cpp:287 +#: backends/platform/wince/CELauncherDialog.cpp:83 +msgid "Yes" +msgstr "Kyllä" + +#: gui/launcher.cpp:789 gui/launcher.cpp:937 +#: backends/events/symbiansdl/symbiansdl-events.cpp:184 +#: backends/platform/wince/CEActionsPocket.cpp:326 +#: backends/platform/wince/CEActionsSmartphone.cpp:287 +#: backends/platform/wince/CELauncherDialog.cpp:83 +msgid "No" +msgstr "Ei" + +#: gui/launcher.cpp:837 +msgid "ScummVM couldn't open the specified directory!" +msgstr "ScummVM ei voi avata kyseistä hakemistoa!" + +#: gui/launcher.cpp:849 +msgid "ScummVM could not find any game in the specified directory!" +msgstr "ScummVM ei löytänyt yhtään peliä kyseisestä hakemistosta!" + +#: gui/launcher.cpp:863 +msgid "Pick the game:" +msgstr "Valitse peli:" + +#: gui/launcher.cpp:937 +msgid "Do you really want to remove this game configuration?" +msgstr "Haluatko varmasti poistaa pelin asetuksineen listalta?" + +#: gui/launcher.cpp:1001 +msgid "This game does not support loading games from the launcher." +msgstr "Tämä peli ei tue pelitallennuksien lataamista pelin ulkopuolelta." + +#: gui/launcher.cpp:1005 +msgid "ScummVM could not find any engine capable of running the selected game!" +msgstr "ScummVM ei löytänyt pelimoottoria joka tukee valittua peliä!" + +#: gui/launcher.cpp:1119 +msgctxt "lowres" +msgid "Mass Add..." +msgstr "Lisää monta..." + +#: gui/launcher.cpp:1119 +msgid "Mass Add..." +msgstr "Lisää monta..." + +#: gui/massadd.cpp:78 gui/massadd.cpp:81 +msgid "... progress ..." +msgstr "... skannaa ..." + +#: gui/massadd.cpp:258 +msgid "Scan complete!" +msgstr "Skannaus on valmis!" + +#: gui/massadd.cpp:261 +#, c-format +msgid "Discovered %d new games, ignored %d previously added games." +msgstr "" +"%d uutta peliä löytyi, jätettiin %d peliä huomiotta, koska ne oli jo lisätty " +"aiemmin." + +#: gui/massadd.cpp:265 +#, c-format +msgid "Scanned %d directories ..." +msgstr "Skannattiin %d hakemistoa ..." + +#: gui/massadd.cpp:268 +#, c-format +msgid "Discovered %d new games, ignored %d previously added games ..." +msgstr "" +"%d uutta peliä löytyi, jätettiin %d peliä huomiotta, koska ne oli jo lisätty " +"aiemmin." + +#: gui/options.cpp:78 +msgid "Never" +msgstr "Ei koskaan" + +#: gui/options.cpp:78 +msgid "every 5 mins" +msgstr "5 minuutin välein" + +#: gui/options.cpp:78 +msgid "every 10 mins" +msgstr "10 minuutin välein" + +#: gui/options.cpp:78 +msgid "every 15 mins" +msgstr "15 minuutin välein" + +#: gui/options.cpp:78 +msgid "every 30 mins" +msgstr "30 minuutin välein" + +#: gui/options.cpp:80 +msgid "8 kHz" +msgstr "8 kHz" + +#: gui/options.cpp:80 +msgid "11kHz" +msgstr "11 kHz" + +#: gui/options.cpp:80 +msgid "22 kHz" +msgstr "22 kHz" + +#: gui/options.cpp:80 +msgid "44 kHz" +msgstr "44 kHz" + +#: gui/options.cpp:80 +msgid "48 kHz" +msgstr "48 kHz" + +#: gui/options.cpp:248 gui/options.cpp:474 gui/options.cpp:575 +#: gui/options.cpp:644 gui/options.cpp:852 +msgctxt "soundfont" +msgid "None" +msgstr "Ei käytössä" + +#: gui/options.cpp:382 +msgid "Failed to apply some of the graphic options changes:" +msgstr "Joitain grafiikka-asetuksia ei saatu asetettua:" + +#: gui/options.cpp:394 +msgid "the video mode could not be changed." +msgstr "videotilaa ei voitu vaihtaa." + +#: gui/options.cpp:400 +msgid "the fullscreen setting could not be changed" +msgstr "kokoruututilaa ei voitu muuttaa" + +#: gui/options.cpp:406 +msgid "the aspect ratio setting could not be changed" +msgstr "kuvasuhdekorjausasetusta ei voitu muuttaa" + +#: gui/options.cpp:727 +msgid "Graphics mode:" +msgstr "Grafiikkatila:" + +#: gui/options.cpp:741 +msgid "Render mode:" +msgstr "Renderöintitila:" + +#: gui/options.cpp:741 gui/options.cpp:742 +msgid "Special dithering modes supported by some games" +msgstr "Erityiset dithering asetukset joita jotkut pelit tukevat" + +#: gui/options.cpp:753 +#: backends/graphics/surfacesdl/surfacesdl-graphics.cpp:2236 +#: backends/graphics/openglsdl/openglsdl-graphics.cpp:472 +msgid "Fullscreen mode" +msgstr "Kokoruututila" + +#: gui/options.cpp:756 +msgid "Aspect ratio correction" +msgstr "Kuvasuhteen korjaus" + +#: gui/options.cpp:756 +msgid "Correct aspect ratio for 320x200 games" +msgstr "Oikea kuvasuhde 320x200 peleille" + +#: gui/options.cpp:764 +msgid "Preferred Device:" +msgstr "Ensisijainen laite:" + +#: gui/options.cpp:764 +msgid "Music Device:" +msgstr "Musiikkilaite:" + +#: gui/options.cpp:764 gui/options.cpp:766 +msgid "Specifies preferred sound device or sound card emulator" +msgstr "" +"Määrittää äänilaitteen tai äänikorttiemulaattorin jota ensisijaisesti tulisi " +"käyttää" + +#: gui/options.cpp:764 gui/options.cpp:766 gui/options.cpp:767 +msgid "Specifies output sound device or sound card emulator" +msgstr "Määrittää äänikortin tai äänikorttia emuloivan ohjelmiston" + +#: gui/options.cpp:766 +msgctxt "lowres" +msgid "Preferred Dev.:" +msgstr "Ensisijainen:" + +#: gui/options.cpp:766 +msgctxt "lowres" +msgid "Music Device:" +msgstr "Musiikkilaite:" + +#: gui/options.cpp:793 +msgid "AdLib emulator:" +msgstr "AdLib emulaattori:" + +#: gui/options.cpp:793 gui/options.cpp:794 +msgid "AdLib is used for music in many games" +msgstr "AdLibiä käytetään monien pelien musiikeissa" + +#: gui/options.cpp:804 +msgid "Output rate:" +msgstr "Taajuus:" + +#: gui/options.cpp:804 gui/options.cpp:805 +msgid "" +"Higher value specifies better sound quality but may be not supported by your " +"soundcard" +msgstr "" +"Isommat taajuudet merkitsevät parempaa äänenlaatua, mutta äänikorttisi ei " +"ehkä tue niitä." + +#: gui/options.cpp:815 +msgid "GM Device:" +msgstr "GM laite:" + +#: gui/options.cpp:815 +msgid "Specifies default sound device for General MIDI output" +msgstr "Määrittää oletuksena käytettävän äänilaitteen General MIDIlle" + +#: gui/options.cpp:826 +msgid "Don't use General MIDI music" +msgstr "Älä käytä General MIDIä musiikissa" + +#: gui/options.cpp:837 gui/options.cpp:899 +msgid "Use first available device" +msgstr "Käytä ensimmäistä laitetta" + +#: gui/options.cpp:849 +msgid "SoundFont:" +msgstr "Äänifontti:" + +#: gui/options.cpp:849 gui/options.cpp:851 gui/options.cpp:852 +msgid "SoundFont is supported by some audio cards, Fluidsynth and Timidity" +msgstr "" +"Jotkut äänikortit tukevat äänifonttia (SoundFont), FluidSynth ja Timidity" + +#: gui/options.cpp:851 +msgctxt "lowres" +msgid "SoundFont:" +msgstr "Äänifontti:" + +#: gui/options.cpp:857 +msgid "Mixed AdLib/MIDI mode" +msgstr "Yhdistetty AdLib/MIDI tila" + +#: gui/options.cpp:857 +msgid "Use both MIDI and AdLib sound generation" +msgstr "Käytä sekä MIDIä että Adlibiä äänentuotantoon" + +#: gui/options.cpp:860 +msgid "MIDI gain:" +msgstr "MIDIn äänilisäys:" + +#: gui/options.cpp:870 +msgid "MT-32 Device:" +msgstr "MT-32 laite:" + +#: gui/options.cpp:870 +msgid "Specifies default sound device for Roland MT-32/LAPC1/CM32l/CM64 output" +msgstr "Määrittää oletusäänilaitteen Roland MT-32/LAPC1/CM32l/CM64:n käyttöön" + +#: gui/options.cpp:875 +msgid "True Roland MT-32 (disable GM emulation)" +msgstr "Aito Roland MT-32 (ei GM emulointia)" + +#: gui/options.cpp:875 gui/options.cpp:877 +msgid "" +"Check if you want to use your real hardware Roland-compatible sound device " +"connected to your computer" +msgstr "" +"Valitse jos haluat käyttää aitoa Roland-yhteensopivaa laittetta joka on " +"kytketty tietokoneeseesi" + +#: gui/options.cpp:877 +msgctxt "lowres" +msgid "True Roland MT-32 (no GM emulation)" +msgstr "Aito Roland MT-32 (ei GM emulointia)" + +#: gui/options.cpp:880 +msgid "Enable Roland GS Mode" +msgstr "Käytä Roland GS moodia" + +#: gui/options.cpp:880 +msgid "Turns off General MIDI mapping for games with Roland MT-32 soundtrack" +msgstr "Poistaa General MIDIn peleistä joissa on Roland MT-32 ääniraita" + +#: gui/options.cpp:889 +msgid "Don't use Roland MT-32 music" +msgstr "Älä käytä Roland MT-32 musiikkia" + +#: gui/options.cpp:916 +msgid "Text and Speech:" +msgstr "Tekstitys ja puhe:" + +#: gui/options.cpp:920 gui/options.cpp:930 +msgid "Speech" +msgstr "Puhe" + +#: gui/options.cpp:921 gui/options.cpp:931 +msgid "Subtitles" +msgstr "Tekstitys" + +#: gui/options.cpp:922 +msgid "Both" +msgstr "Molemmat" + +#: gui/options.cpp:924 +msgid "Subtitle speed:" +msgstr "Tekstin nopeus:" + +#: gui/options.cpp:926 +msgctxt "lowres" +msgid "Text and Speech:" +msgstr "Tekstitys ja puhe:" + +#: gui/options.cpp:930 +msgid "Spch" +msgstr "Puhe" + +#: gui/options.cpp:931 +msgid "Subs" +msgstr "Tekstit" + +#: gui/options.cpp:932 +msgctxt "lowres" +msgid "Both" +msgstr "Molemmat" + +#: gui/options.cpp:932 +msgid "Show subtitles and play speech" +msgstr "Näytä tekstitys ja käytä puhetta" + +#: gui/options.cpp:934 +msgctxt "lowres" +msgid "Subtitle speed:" +msgstr "Tekstin nopeus:" + +#: gui/options.cpp:950 +msgid "Music volume:" +msgstr "Musiikki:" + +#: gui/options.cpp:952 +msgctxt "lowres" +msgid "Music volume:" +msgstr "Musiikki:" + +#: gui/options.cpp:959 +msgid "Mute All" +msgstr "Vaimenna" + +#: gui/options.cpp:962 +msgid "SFX volume:" +msgstr "Ääniefektit:" + +#: gui/options.cpp:962 gui/options.cpp:964 gui/options.cpp:965 +msgid "Special sound effects volume" +msgstr "Erikoisefektit" + +#: gui/options.cpp:964 +msgctxt "lowres" +msgid "SFX volume:" +msgstr "Ääniefektit:" + +#: gui/options.cpp:972 +msgid "Speech volume:" +msgstr "Puhe:" + +#: gui/options.cpp:974 +msgctxt "lowres" +msgid "Speech volume:" +msgstr "Puhe:" + +#: gui/options.cpp:1131 +msgid "Theme Path:" +msgstr "Teemojen polku:" + +#: gui/options.cpp:1133 +msgctxt "lowres" +msgid "Theme Path:" +msgstr "Teemojen polku:" + +#: gui/options.cpp:1139 gui/options.cpp:1141 gui/options.cpp:1142 +msgid "Specifies path to additional data used by all games or ScummVM" +msgstr "" +"Määrittää polun, jossa on lisätiedostoja joita ScummVM tai kaikki pelit " +"käyttävät" + +#: gui/options.cpp:1148 +msgid "Plugins Path:" +msgstr "Pluginien sijainti:" + +#: gui/options.cpp:1150 +msgctxt "lowres" +msgid "Plugins Path:" +msgstr "Pluginien sijainti:" + +#: gui/options.cpp:1159 +msgid "Misc" +msgstr "Muut" + +#: gui/options.cpp:1161 +msgctxt "lowres" +msgid "Misc" +msgstr "Muut" + +#: gui/options.cpp:1163 +msgid "Theme:" +msgstr "Teema" + +#: gui/options.cpp:1167 +msgid "GUI Renderer:" +msgstr "GUI renderöijä:" + +#: gui/options.cpp:1179 +msgid "Autosave:" +msgstr "Autom. tallennus:" + +#: gui/options.cpp:1181 +msgctxt "lowres" +msgid "Autosave:" +msgstr "Autom. tallennus:" + +#: gui/options.cpp:1189 +msgid "Keys" +msgstr "Näppäimet" + +#: gui/options.cpp:1196 +msgid "GUI Language:" +msgstr "ScummVM:n kieli:" + +#: gui/options.cpp:1196 +msgid "Language of ScummVM GUI" +msgstr "ScummVM käyttöliittymän kieli" + +#: gui/options.cpp:1347 +msgid "You have to restart ScummVM before your changes will take effect." +msgstr "ScummVM pitää käynnistää uudelleen jotta muutokset tulevat voimaan." + +#: gui/options.cpp:1360 +msgid "Select directory for savegames" +msgstr "Valitse hakemisto pelitallennuksille." + +#: gui/options.cpp:1367 +msgid "The chosen directory cannot be written to. Please select another one." +msgstr "Valittuun hakemistoon ei voi kirjoittaa. Valitse toinen hakemisto." + +#: gui/options.cpp:1376 +msgid "Select directory for GUI themes" +msgstr "Valitse hakemisto käyttöliittymän teemoille" + +#: gui/options.cpp:1386 +msgid "Select directory for extra files" +msgstr "Valitse hakemisto lisätiedostoille" + +#: gui/options.cpp:1397 +msgid "Select directory for plugins" +msgstr "Valitse hakemisto plugineille" + +#: gui/options.cpp:1450 +msgid "" +"The theme you selected does not support your current language. If you want " +"to use this theme you need to switch to another language first." +msgstr "" +"Valitsemasi teema ei tue nykyistä valitsemaasi kieltä. Vaihda kieli ensin, " +"ja yritä sitten uudelleen." + +#: gui/saveload-dialog.cpp:166 +msgid "List view" +msgstr "Listanäkymä" + +#: gui/saveload-dialog.cpp:167 +msgid "Grid view" +msgstr "Ruudukkonäkymä" + +#: gui/saveload-dialog.cpp:210 gui/saveload-dialog.cpp:358 +msgid "No date saved" +msgstr "Päiväystä ei ole tallennettu" + +#: gui/saveload-dialog.cpp:211 gui/saveload-dialog.cpp:359 +msgid "No time saved" +msgstr "Aikaa ei ole tallennettu" + +#: gui/saveload-dialog.cpp:212 gui/saveload-dialog.cpp:360 +msgid "No playtime saved" +msgstr "Peliaikaa ei ole tallennettu" + +#: gui/saveload-dialog.cpp:219 gui/saveload-dialog.cpp:275 +msgid "Delete" +msgstr "Poista" + +#: gui/saveload-dialog.cpp:274 +msgid "Do you really want to delete this savegame?" +msgstr "Haluatko varmasti poistaa tämän pelitallennuksen?" + +#: gui/saveload-dialog.cpp:383 gui/saveload-dialog.cpp:872 +msgid "Date: " +msgstr "Päiväys: " + +#: gui/saveload-dialog.cpp:387 gui/saveload-dialog.cpp:878 +msgid "Time: " +msgstr "Aika: " + +#: gui/saveload-dialog.cpp:393 gui/saveload-dialog.cpp:886 +msgid "Playtime: " +msgstr "Peliaika: " + +#: gui/saveload-dialog.cpp:406 gui/saveload-dialog.cpp:494 +msgid "Untitled savestate" +msgstr "Nimetön pelitallennus" + +#: gui/saveload-dialog.cpp:546 +msgid "Next" +msgstr "Seuraava" + +#: gui/saveload-dialog.cpp:549 +msgid "Prev" +msgstr "Edellinen" + +#: gui/saveload-dialog.cpp:736 +msgid "New Save" +msgstr "Uusi pelitallennus" + +#: gui/saveload-dialog.cpp:736 +msgid "Create a new save game" +msgstr "Luo uusi pelitallennus" + +#: gui/saveload-dialog.cpp:865 +msgid "Name: " +msgstr "Nimi: " + +#: gui/saveload-dialog.cpp:937 +#, c-format +msgid "Enter a description for slot %d:" +msgstr "Anna kuvaus tallennukselle numero %d:" + +#: gui/themebrowser.cpp:44 +msgid "Select a Theme" +msgstr "Valitse teema" + +#: gui/ThemeEngine.cpp:337 +msgid "Disabled GFX" +msgstr "Disabloitu GFX" + +#: gui/ThemeEngine.cpp:337 +msgctxt "lowres" +msgid "Disabled GFX" +msgstr "Disabloitu GFX" + +#: gui/ThemeEngine.cpp:338 +msgid "Standard Renderer (16bpp)" +msgstr "Standardirenderöijä (16 bpp)" + +#: gui/ThemeEngine.cpp:338 +msgid "Standard (16bpp)" +msgstr "Standardi (16 bpp)" + +#: gui/ThemeEngine.cpp:340 +msgid "Antialiased Renderer (16bpp)" +msgstr "Antialiasoitu renderöijä (16 bpp)" + +#: gui/ThemeEngine.cpp:340 +msgid "Antialiased (16bpp)" +msgstr "Antialiasoitu (16 bpp)" + +#: gui/widget.cpp:322 gui/widget.cpp:324 gui/widget.cpp:330 gui/widget.cpp:332 +msgid "Clear value" +msgstr "Tyhjennä arvo" + +#: base/main.cpp:210 +#, c-format +msgid "Engine does not support debug level '%s'" +msgstr "Pelimoottori ei tue debug tasoa '%s'" + +#: base/main.cpp:288 +msgid "Menu" +msgstr "Valikko" + +#: base/main.cpp:291 backends/platform/symbian/src/SymbianActions.cpp:45 +#: backends/platform/wince/CEActionsPocket.cpp:45 +#: backends/platform/wince/CEActionsSmartphone.cpp:46 +msgid "Skip" +msgstr "Ohita" + +#: base/main.cpp:294 backends/platform/symbian/src/SymbianActions.cpp:50 +#: backends/platform/wince/CEActionsPocket.cpp:42 +msgid "Pause" +msgstr "Tauko" + +#: base/main.cpp:297 +msgid "Skip line" +msgstr "Ohita rivi" + +#: base/main.cpp:468 +msgid "Error running game:" +msgstr "Virhe ajettaessa peliä:" + +#: base/main.cpp:492 +msgid "Could not find any engine capable of running the selected game" +msgstr "Pelimoottoria joka tukisi valittua peliä ei löytynyt" + +#: common/error.cpp:38 +msgid "No error" +msgstr "Ei virhettä" + +#: common/error.cpp:40 +msgid "Game data not found" +msgstr "Pelin tietoja ei löytynyt" + +#: common/error.cpp:42 +msgid "Game id not supported" +msgstr "Pelin tunniste ei ole tuettu" + +#: common/error.cpp:44 +msgid "Unsupported color mode" +msgstr "Väritilaa ei tueta" + +#: common/error.cpp:47 +msgid "Read permission denied" +msgstr "Lukuoikeuksia ei saatu" + +#: common/error.cpp:49 +msgid "Write permission denied" +msgstr "Kirjoitusoikeuksia ei saatu" + +#: common/error.cpp:52 +msgid "Path does not exist" +msgstr "Polkua ei ole olemassa" + +#: common/error.cpp:54 +msgid "Path not a directory" +msgstr "Polku ei ole hakemisto" + +#: common/error.cpp:56 +msgid "Path not a file" +msgstr "Polku ei ole tiedosto" + +#: common/error.cpp:59 +msgid "Cannot create file" +msgstr "Tiedoston luonti ei onnistu" + +#: common/error.cpp:61 +msgid "Reading data failed" +msgstr "Tiedon lukeminen epäonnistui" + +#: common/error.cpp:63 +msgid "Writing data failed" +msgstr "Tiedon kirjoittaminen epäonnistui" + +#: common/error.cpp:66 +msgid "Could not find suitable engine plugin" +msgstr "Sopivaa pelimoottorin pluginia ei löytynyt" + +#: common/error.cpp:68 +msgid "Engine plugin does not support save states" +msgstr "Pelimoottori ei tue tallennustiloja" + +#: common/error.cpp:71 +msgid "User canceled" +msgstr "Käyttäjä peruutti" + +#: common/error.cpp:75 +msgid "Unknown error" +msgstr "Tuntematon virhe" + +#: engines/advancedDetector.cpp:316 +#, c-format +msgid "The game in '%s' seems to be unknown." +msgstr "Peli hakemistossa '%s' näyttäisi olevan tuntematon." + +#: engines/advancedDetector.cpp:317 +msgid "Please, report the following data to the ScummVM team along with name" +msgstr "" +"Ole hyvä ja ilmoita ScummVM:n kehittäjille seuraavat tiedot, lisäksi kerro" + +#: engines/advancedDetector.cpp:319 +msgid "of the game you tried to add and its version/language/etc.:" +msgstr "" +"mikä peli oli kyseessä, ja sen versio, kieli, ja muut vastaavat tiedot." + +#: engines/dialogs.cpp:84 +msgid "~R~esume" +msgstr "~J~atka" + +#: engines/dialogs.cpp:86 +msgid "~L~oad" +msgstr "~L~ataa" + +#: engines/dialogs.cpp:90 +msgid "~S~ave" +msgstr "~T~allenna" + +#: engines/dialogs.cpp:94 +msgid "~O~ptions" +msgstr "~A~setukset" + +#: engines/dialogs.cpp:99 +msgid "~H~elp" +msgstr "~O~hje" + +#: engines/dialogs.cpp:101 +msgid "~A~bout" +msgstr "~T~ietoa" + +#: engines/dialogs.cpp:104 engines/dialogs.cpp:180 +msgid "~R~eturn to Launcher" +msgstr "Palaa p~e~livalitsimeen" + +#: engines/dialogs.cpp:106 engines/dialogs.cpp:182 +msgctxt "lowres" +msgid "~R~eturn to Launcher" +msgstr "Palaa p~e~livalitsimeen" + +#: engines/dialogs.cpp:115 engines/agi/saveload.cpp:803 +#: engines/cruise/menu.cpp:212 engines/sci/engine/kfile.cpp:742 +msgid "Save game:" +msgstr "Tallenna peli:" + +#: engines/dialogs.cpp:115 engines/agi/saveload.cpp:803 +#: engines/scumm/dialogs.cpp:187 engines/cruise/menu.cpp:212 +#: engines/sci/engine/kfile.cpp:742 +#: backends/platform/symbian/src/SymbianActions.cpp:44 +#: backends/platform/wince/CEActionsPocket.cpp:43 +#: backends/platform/wince/CEActionsPocket.cpp:267 +#: backends/platform/wince/CEActionsSmartphone.cpp:45 +#: backends/platform/wince/CEActionsSmartphone.cpp:231 +msgid "Save" +msgstr "Tallenna" + +#: engines/dialogs.cpp:144 +msgid "" +"Sorry, this engine does not currently provide in-game help. Please consult " +"the README for basic information, and for instructions on how to obtain " +"further assistance." +msgstr "" +"Tämä pelimoottori ei toistaiseksi tue pelin sisäistä ohjetta. Avaa LUEMINUT " +"tiedosto saadaksesi lisätietoa." + +#: engines/dialogs.cpp:228 +#, c-format +msgid "" +"Gamestate save failed (%s)! Please consult the README for basic information, " +"and for instructions on how to obtain further assistance." +msgstr "" +"Pelitilan tallennus epäonnistui (%s)! Avaa LUEMINUT tiedosto saadaksesi " +"lisätietoa." + +#: engines/dialogs.cpp:301 engines/mohawk/dialogs.cpp:109 +#: engines/mohawk/dialogs.cpp:174 +msgid "~O~K" +msgstr "~H~yväksy" + +#: engines/dialogs.cpp:302 engines/mohawk/dialogs.cpp:110 +#: engines/mohawk/dialogs.cpp:175 +msgid "~C~ancel" +msgstr "~P~eruuta" + +#: engines/dialogs.cpp:305 +msgid "~K~eys" +msgstr "~N~äppäimet" + +#: engines/engine.cpp:235 +msgid "Could not initialize color format." +msgstr "Väriformaattia ei voitu alustaa" + +#: engines/engine.cpp:243 +msgid "Could not switch to video mode: '" +msgstr "Videotilan vaihto ei onnistunut:'" + +#: engines/engine.cpp:252 +msgid "Could not apply aspect ratio setting." +msgstr "Kuvasuhdeasetusta ei voitu asettaa." + +#: engines/engine.cpp:257 +msgid "Could not apply fullscreen setting." +msgstr "Kokoruututila-asetusta ei voi asettaa." + +#: engines/engine.cpp:357 +msgid "" +"You appear to be playing this game directly\n" +"from the CD. This is known to cause problems,\n" +"and it is therefore recommended that you copy\n" +"the data files to your hard disk instead.\n" +"See the README file for details." +msgstr "" +"Pelaat suoraan CD-levyltä. Tämä aiheuttaa\n" +"ongelmia, joten on suositeltavaa että kopioit\n" +"pelin tiedostot kovalevyllesi. Avaa LUEMINUT\n" +"tiedosto ohjeita varten." + +#: engines/engine.cpp:368 +msgid "" +"This game has audio tracks in its disk. These\n" +"tracks need to be ripped from the disk using\n" +"an appropriate CD audio extracting tool in\n" +"order to listen to the game's music.\n" +"See the README file for details." +msgstr "" +"Pelin musiikki on CD-levyllä ääniraitoina.\n" +"Raidat pitää ripata levyltä sopivaa\n" +"ohjelmistoa käyttäen, jotta musiikit\n" +"kuuluvat. Lue ohjeet LUEMINUT tiedostosta." + +#: engines/engine.cpp:426 +#, c-format +msgid "" +"Gamestate load failed (%s)! Please consult the README for basic information, " +"and for instructions on how to obtain further assistance." +msgstr "" +"Pelitilan lataus epäonnistui (%s)! Avaa LUEMINUT tiedosto saadaksesi " +"lisätietoa." + +#: engines/engine.cpp:439 +msgid "" +"WARNING: The game you are about to start is not yet fully supported by " +"ScummVM. As such, it is likely to be unstable, and any saves you make might " +"not work in future versions of ScummVM." +msgstr "" +"VAROITUS: ScummVM ei vielä tue täysin valitsemaasi peliä. Peli saattaa olla " +"epävakaa, eivätkä pelitallennukset välttämättä toimi tulevissa ScummVM:n " +"versioissa." + +#: engines/engine.cpp:442 +msgid "Start anyway" +msgstr "Pelaa silti" + +#: engines/agi/detection.cpp:142 engines/dreamweb/detection.cpp:47 +#: engines/sci/detection.cpp:393 +msgid "Use original save/load screens" +msgstr "Käytä alkuperäisiä tallenna/lataa valikkoja" + +#: engines/agi/detection.cpp:143 engines/dreamweb/detection.cpp:48 +#: engines/sci/detection.cpp:394 +msgid "Use the original save/load screens, instead of the ScummVM ones" +msgstr "Käytä alkuperäisiä tallenna/lataa valikkoja, ScummVM valikoiden sijaan" + +#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:838 +msgid "Restore game:" +msgstr "Lataa pelitallenne:" + +#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:838 +msgid "Restore" +msgstr "Lataa tallenne" + +#: engines/dreamweb/detection.cpp:57 +msgid "Use bright palette mode" +msgstr "Käytä kirkaspalettitilaa" + +#: engines/dreamweb/detection.cpp:58 +msgid "Display graphics using the game's bright palette" +msgstr "Näytä grafiikat käyttäen pelin kirkasta palettia" + +#: engines/sci/detection.cpp:373 +msgid "EGA undithering" +msgstr "EGA unditteröinti" + +#: engines/sci/detection.cpp:374 +msgid "Enable undithering in EGA games" +msgstr "Käytä unditteröintiä EGA peleissä" + +#: engines/sci/detection.cpp:383 +msgid "Prefer digital sound effects" +msgstr "Käytä mieluiten digitaalisia äänitehosteita." + +#: engines/sci/detection.cpp:384 +msgid "Prefer digital sound effects instead of synthesized ones" +msgstr "" +"Käytä mieluiten digitaalisia äänitehosteita synteettisten tehosteiden sijaan." + +#: engines/sci/detection.cpp:403 +msgid "Use IMF/Yamaha FB-01 for MIDI output" +msgstr "Käytä IMF/Yamaha FB-01:stä MIDI-musiikille" + +#: engines/sci/detection.cpp:404 +msgid "" +"Use an IBM Music Feature card or a Yamaha FB-01 FM synth module for MIDI " +"output" +msgstr "" +"Käytä IBM:n Music Feature korttia, tai Yamaha FB-01 FM moduulia MIDIlle" + +#: engines/sci/detection.cpp:414 +msgid "Use CD audio" +msgstr "Käytä CD:n ääntä" + +#: engines/sci/detection.cpp:415 +msgid "Use CD audio instead of in-game audio, if available" +msgstr "Käytä CD:n audiota pelin audion sijaan, jos mahdollista." + +#: engines/sci/detection.cpp:425 +msgid "Use Windows cursors" +msgstr "Käytä Windowsin kursoreita" + +#: engines/sci/detection.cpp:426 +msgid "" +"Use the Windows cursors (smaller and monochrome) instead of the DOS ones" +msgstr "" +"Käytä Windowsin kursoreita (pienempiä ja harmaasävyisiä) DOS kursorien sijaan" + +#: engines/sci/detection.cpp:436 +msgid "Use silver cursors" +msgstr "Käytä hopeisia kursoreita" + +#: engines/sci/detection.cpp:437 +msgid "" +"Use the alternate set of silver cursors, instead of the normal golden ones" +msgstr "Käytä vaihtoehtoisia hopeisia kursoreita normaalien kultaisten sijaan" + +#: engines/scumm/dialogs.cpp:175 +#, c-format +msgid "Insert Disk %c and Press Button to Continue." +msgstr "Lisää levyke %c ja paina jotain nappia jatkaaksesi." + +#: engines/scumm/dialogs.cpp:176 +#, c-format +msgid "Unable to Find %s, (%c%d) Press Button." +msgstr "Tiedosto %s, (%c%d) ei löydy. Paina nappia." + +#: engines/scumm/dialogs.cpp:177 +#, c-format +msgid "Error reading disk %c, (%c%d) Press Button." +msgstr "Virhe luettaessa levyä %c, (%c%d) Paina jotain nappia." + +#: engines/scumm/dialogs.cpp:178 +msgid "Game Paused. Press SPACE to Continue." +msgstr "Pause. Paina välilyöntiä jatkaaksesi." + +#. I18N: You may specify 'Yes' symbol at the end of the line, like this: +#. "Moechten Sie wirklich neu starten? (J/N)J" +#. Will react to J as 'Yes' +#: engines/scumm/dialogs.cpp:182 +msgid "Are you sure you want to restart? (Y/N)" +msgstr "Haluatko varmasti aloittaa pelin alusta? (K/E)K" + +#. I18N: you may specify 'Yes' symbol at the end of the line. See previous comment +#: engines/scumm/dialogs.cpp:184 +msgid "Are you sure you want to quit? (Y/N)" +msgstr "Haluatko varmati lopettaa?" + +#: engines/scumm/dialogs.cpp:189 +msgid "Play" +msgstr "Pelaa" + +#: engines/scumm/dialogs.cpp:191 engines/scumm/help.cpp:82 +#: engines/scumm/help.cpp:84 +#: backends/platform/symbian/src/SymbianActions.cpp:52 +#: backends/platform/wince/CEActionsPocket.cpp:44 +#: backends/platform/wince/CEActionsSmartphone.cpp:52 +#: backends/events/default/default-events.cpp:213 +msgid "Quit" +msgstr "Lopeta" + +#: engines/scumm/dialogs.cpp:193 +msgid "Insert save/load game disk" +msgstr "Laita tallennus/lataus levy asemaan" + +#: engines/scumm/dialogs.cpp:194 +msgid "You must enter a name" +msgstr "Nimi on pakko antaa" + +#: engines/scumm/dialogs.cpp:195 +msgid "The game was NOT saved (disk full?)" +msgstr "Peliä EI tallennettu (onko levy täysi?)" + +#: engines/scumm/dialogs.cpp:196 +msgid "The game was NOT loaded" +msgstr "Peliä EI ladattu" + +#: engines/scumm/dialogs.cpp:197 +#, c-format +msgid "Saving '%s'" +msgstr "Tallennetaan '%s'" + +#: engines/scumm/dialogs.cpp:198 +#, c-format +msgid "Loading '%s'" +msgstr "Ladataan '%s'" + +#: engines/scumm/dialogs.cpp:199 +msgid "Name your SAVE game" +msgstr "Nimeä pelitallenteesi" + +#: engines/scumm/dialogs.cpp:200 +msgid "Select a game to LOAD" +msgstr "Valitse ladattava peli" + +#: engines/scumm/dialogs.cpp:201 +msgid "Game title)" +msgstr "Pelin nimi" + +#. I18N: Previous page button +#: engines/scumm/dialogs.cpp:287 +msgid "~P~revious" +msgstr "E~d~ellinen" + +#. I18N: Next page button +#: engines/scumm/dialogs.cpp:289 +msgid "~N~ext" +msgstr "Se~u~raava" + +#: engines/scumm/dialogs.cpp:290 +#: backends/platform/ds/arm9/source/dsoptions.cpp:56 +msgid "~C~lose" +msgstr "~S~ulje" + +#: engines/scumm/dialogs.cpp:597 +msgid "Speech Only" +msgstr "Vain puhe" + +#: engines/scumm/dialogs.cpp:598 +msgid "Speech and Subtitles" +msgstr "Puhe ja Tekstitys" + +#: engines/scumm/dialogs.cpp:599 +msgid "Subtitles Only" +msgstr "Vain tekstitys" + +#: engines/scumm/dialogs.cpp:607 +msgctxt "lowres" +msgid "Speech & Subs" +msgstr "Puhe & teksti" + +#: engines/scumm/dialogs.cpp:653 +msgid "Select a Proficiency Level." +msgstr "Valitse taitotasosi." + +#: engines/scumm/dialogs.cpp:655 +msgid "Refer to your Loom(TM) manual for help." +msgstr "Lue Loom(TM) ohjekirjaa saadaksesi ohjeita." + +#: engines/scumm/dialogs.cpp:658 +msgid "Standard" +msgstr "Standardi" + +#: engines/scumm/dialogs.cpp:659 +msgid "Practice" +msgstr "Harjoitus" + +#: engines/scumm/dialogs.cpp:660 +msgid "Expert" +msgstr "Ekspertti" + +#: engines/scumm/help.cpp:73 +msgid "Common keyboard commands:" +msgstr "Yleisiä näppäimistökomentoja:" + +#: engines/scumm/help.cpp:74 +msgid "Save / Load dialog" +msgstr "Tallenna / Lataa peli" + +#: engines/scumm/help.cpp:76 +msgid "Skip line of text" +msgstr "Ohita rivi tekstiä" + +#: engines/scumm/help.cpp:77 +msgid "Esc" +msgstr "Esc" + +#: engines/scumm/help.cpp:77 +msgid "Skip cutscene" +msgstr "Ohita video" + +#: engines/scumm/help.cpp:78 +msgid "Space" +msgstr "Välilyönti" + +#: engines/scumm/help.cpp:78 +msgid "Pause game" +msgstr "Pause" + +#: engines/scumm/help.cpp:79 engines/scumm/help.cpp:84 +#: engines/scumm/help.cpp:95 engines/scumm/help.cpp:96 +#: engines/scumm/help.cpp:97 engines/scumm/help.cpp:98 +#: engines/scumm/help.cpp:99 engines/scumm/help.cpp:100 +#: engines/scumm/help.cpp:101 engines/scumm/help.cpp:102 +msgid "Ctrl" +msgstr "Ctrl" + +#: engines/scumm/help.cpp:79 +msgid "Load game state 1-10" +msgstr "Lataa pelitila 1-10" + +#: engines/scumm/help.cpp:80 engines/scumm/help.cpp:84 +#: engines/scumm/help.cpp:86 engines/scumm/help.cpp:100 +#: engines/scumm/help.cpp:101 engines/scumm/help.cpp:102 +msgid "Alt" +msgstr "Alt" + +#: engines/scumm/help.cpp:80 +msgid "Save game state 1-10" +msgstr "Tallenna pelitila 1-10" + +#: engines/scumm/help.cpp:86 engines/scumm/help.cpp:89 +msgid "Enter" +msgstr "Enter" + +#: engines/scumm/help.cpp:86 +msgid "Toggle fullscreen" +msgstr "Kytke kokoruututila päälle tai pois" + +#: engines/scumm/help.cpp:87 +msgid "Music volume up / down" +msgstr "Musiikin äänenvoimakkuus ylös / alas" + +#: engines/scumm/help.cpp:88 +msgid "Text speed slower / faster" +msgstr "Hidasta/nopeuta tekstiä" + +#: engines/scumm/help.cpp:89 +msgid "Simulate left mouse button" +msgstr "Simuloi hiiren vasenta näppäintä" + +#: engines/scumm/help.cpp:90 +msgid "Tab" +msgstr "Sarkain" + +#: engines/scumm/help.cpp:90 +msgid "Simulate right mouse button" +msgstr "Simuloi oikeaa hiiren nappia" + +#: engines/scumm/help.cpp:93 +msgid "Special keyboard commands:" +msgstr "Erityiskomennot:" + +#: engines/scumm/help.cpp:94 +msgid "Show / Hide console" +msgstr "Näytä / piilota konsoli" + +#: engines/scumm/help.cpp:95 +msgid "Start the debugger" +msgstr "Käynnistä debuggeri" + +#: engines/scumm/help.cpp:96 +msgid "Show memory consumption" +msgstr "Näytä muistinkulutus" + +#: engines/scumm/help.cpp:97 +msgid "Run in fast mode (*)" +msgstr "Aja nopeassa tilassa (*)" + +#: engines/scumm/help.cpp:98 +msgid "Run in really fast mode (*)" +msgstr "Aja erittäin nopeassa tilassa (*)" + +#: engines/scumm/help.cpp:99 +msgid "Toggle mouse capture" +msgstr "Kytke hiiren kaappaus päälle tai pois" + +#: engines/scumm/help.cpp:100 +msgid "Switch between graphics filters" +msgstr "Vaihda grafiikkafiltteriä" + +#: engines/scumm/help.cpp:101 +msgid "Increase / Decrease scale factor" +msgstr "Kasvata / vähennä skaalakerrointa" + +#: engines/scumm/help.cpp:102 +msgid "Toggle aspect-ratio correction" +msgstr "Kytke kuvasuhdekorjaus päälle tai pois" + +#: engines/scumm/help.cpp:107 +msgid "* Note that using ctrl-f and" +msgstr "* Huomaa että ctrl-f ja" + +#: engines/scumm/help.cpp:108 +msgid " ctrl-g are not recommended" +msgstr " ctrl-g ovat epävakaita eikä" + +#: engines/scumm/help.cpp:109 +msgid " since they may cause crashes" +msgstr " niiden käyttöä suositella" + +#: engines/scumm/help.cpp:110 +msgid " or incorrect game behavior." +msgstr " mahdollisten virheiden vuoksi." + +#: engines/scumm/help.cpp:114 +msgid "Spinning drafts on the keyboard:" +msgstr "" + +#: engines/scumm/help.cpp:116 +msgid "Main game controls:" +msgstr "Pelin tärkeimmät kontrollit:" + +#: engines/scumm/help.cpp:121 engines/scumm/help.cpp:136 +#: engines/scumm/help.cpp:161 +msgid "Push" +msgstr "Paina" + +#: engines/scumm/help.cpp:122 engines/scumm/help.cpp:137 +#: engines/scumm/help.cpp:162 +msgid "Pull" +msgstr "Vedä" + +#: engines/scumm/help.cpp:123 engines/scumm/help.cpp:138 +#: engines/scumm/help.cpp:163 engines/scumm/help.cpp:197 +#: engines/scumm/help.cpp:207 +msgid "Give" +msgstr "Anna" + +#: engines/scumm/help.cpp:124 engines/scumm/help.cpp:139 +#: engines/scumm/help.cpp:164 engines/scumm/help.cpp:190 +#: engines/scumm/help.cpp:208 +msgid "Open" +msgstr "Avaa" + +#: engines/scumm/help.cpp:126 +msgid "Go to" +msgstr "Mene" + +#: engines/scumm/help.cpp:127 +msgid "Get" +msgstr "Ota" + +#: engines/scumm/help.cpp:128 engines/scumm/help.cpp:152 +#: engines/scumm/help.cpp:170 engines/scumm/help.cpp:198 +#: engines/scumm/help.cpp:213 engines/scumm/help.cpp:224 +#: engines/scumm/help.cpp:250 +msgid "Use" +msgstr "Käytä" + +#: engines/scumm/help.cpp:129 engines/scumm/help.cpp:141 +msgid "Read" +msgstr "Lue" + +#: engines/scumm/help.cpp:130 engines/scumm/help.cpp:147 +msgid "New kid" +msgstr "Uusi lapsi" + +#: engines/scumm/help.cpp:131 engines/scumm/help.cpp:153 +#: engines/scumm/help.cpp:171 +msgid "Turn on" +msgstr "Käynnistä" + +#: engines/scumm/help.cpp:132 engines/scumm/help.cpp:154 +#: engines/scumm/help.cpp:172 +msgid "Turn off" +msgstr "Sammuta" + +#: engines/scumm/help.cpp:142 engines/scumm/help.cpp:167 +#: engines/scumm/help.cpp:194 +msgid "Walk to" +msgstr "Kävele" + +#: engines/scumm/help.cpp:143 engines/scumm/help.cpp:168 +#: engines/scumm/help.cpp:195 engines/scumm/help.cpp:210 +#: engines/scumm/help.cpp:227 +msgid "Pick up" +msgstr "Ota" + +#: engines/scumm/help.cpp:144 engines/scumm/help.cpp:169 +msgid "What is" +msgstr "Mitä on" + +#: engines/scumm/help.cpp:146 +msgid "Unlock" +msgstr "Avaa lukko" + +#: engines/scumm/help.cpp:149 +msgid "Put on" +msgstr "Pue ylle" + +#: engines/scumm/help.cpp:150 +msgid "Take off" +msgstr "Lähde matkaan" + +#: engines/scumm/help.cpp:156 +msgid "Fix" +msgstr "Korjaa" + +#: engines/scumm/help.cpp:158 +msgid "Switch" +msgstr "Vaihda" + +#: engines/scumm/help.cpp:166 engines/scumm/help.cpp:228 +msgid "Look" +msgstr "Katso" + +#: engines/scumm/help.cpp:173 engines/scumm/help.cpp:223 +msgid "Talk" +msgstr "Puhu" + +#: engines/scumm/help.cpp:174 +msgid "Travel" +msgstr "Matkusta" + +#: engines/scumm/help.cpp:175 +msgid "To Henry / To Indy" +msgstr "Henry / Indy" + +#. I18N: These are different musical notes +#: engines/scumm/help.cpp:179 +msgid "play C minor on distaff" +msgstr "soita C molli" + +#: engines/scumm/help.cpp:180 +msgid "play D on distaff" +msgstr "soita D" + +#: engines/scumm/help.cpp:181 +msgid "play E on distaff" +msgstr "soita E" + +#: engines/scumm/help.cpp:182 +msgid "play F on distaff" +msgstr "soita F" + +#: engines/scumm/help.cpp:183 +msgid "play G on distaff" +msgstr "soita G" + +#: engines/scumm/help.cpp:184 +msgid "play A on distaff" +msgstr "soita A" + +#: engines/scumm/help.cpp:185 +msgid "play B on distaff" +msgstr "soita B" + +#: engines/scumm/help.cpp:186 +msgid "play C major on distaff" +msgstr "soita C duuri" + +#: engines/scumm/help.cpp:192 engines/scumm/help.cpp:214 +msgid "puSh" +msgstr "Paina" + +#: engines/scumm/help.cpp:193 engines/scumm/help.cpp:215 +msgid "pull (Yank)" +msgstr "Vedä" + +#: engines/scumm/help.cpp:196 engines/scumm/help.cpp:212 +#: engines/scumm/help.cpp:248 +msgid "Talk to" +msgstr "Puhu" + +#: engines/scumm/help.cpp:199 engines/scumm/help.cpp:211 +msgid "Look at" +msgstr "Katso" + +#: engines/scumm/help.cpp:200 +msgid "turn oN" +msgstr "Kytke päälle" + +#: engines/scumm/help.cpp:201 +msgid "turn oFf" +msgstr "Kytke pois päältä" + +#: engines/scumm/help.cpp:217 +msgid "KeyUp" +msgstr "KeyUp" + +#: engines/scumm/help.cpp:217 +msgid "Highlight prev dialogue" +msgstr "Korosta edellistä dialogia" + +#: engines/scumm/help.cpp:218 +msgid "KeyDown" +msgstr "KeyDown" + +#: engines/scumm/help.cpp:218 +msgid "Highlight next dialogue" +msgstr "Korosta seuraavaa dialogia" + +#: engines/scumm/help.cpp:222 +msgid "Walk" +msgstr "Kävele" + +#: engines/scumm/help.cpp:225 engines/scumm/help.cpp:234 +#: engines/scumm/help.cpp:241 engines/scumm/help.cpp:249 +msgid "Inventory" +msgstr "Tavarat" + +#: engines/scumm/help.cpp:226 +msgid "Object" +msgstr "Esine" + +#: engines/scumm/help.cpp:229 +msgid "Black and White / Color" +msgstr "Mustavalko / Väri" + +#: engines/scumm/help.cpp:232 +msgid "Eyes" +msgstr "Silmät" + +#: engines/scumm/help.cpp:233 +msgid "Tongue" +msgstr "Kieli" + +#: engines/scumm/help.cpp:235 +msgid "Punch" +msgstr "Lyö" + +#: engines/scumm/help.cpp:236 +msgid "Kick" +msgstr "Potkaise" + +#: engines/scumm/help.cpp:239 engines/scumm/help.cpp:247 +msgid "Examine" +msgstr "Tutki" + +#: engines/scumm/help.cpp:240 +msgid "Regular cursor" +msgstr "Tavallinen kursori" + +#. I18N: Comm is a communication device +#: engines/scumm/help.cpp:243 +msgid "Comm" +msgstr "Kommunikointilaite" + +#: engines/scumm/help.cpp:246 +msgid "Save / Load / Options" +msgstr "Tallenna / Lataa / Asetukset" + +#: engines/scumm/help.cpp:255 +msgid "Other game controls:" +msgstr "Muut pelin ohjaimet:" + +#: engines/scumm/help.cpp:257 engines/scumm/help.cpp:267 +msgid "Inventory:" +msgstr "Tavarat:" + +#: engines/scumm/help.cpp:258 engines/scumm/help.cpp:274 +msgid "Scroll list up" +msgstr "Vieritä listaa ylös" + +#: engines/scumm/help.cpp:259 engines/scumm/help.cpp:275 +msgid "Scroll list down" +msgstr "Vieritä listaa alas" + +#: engines/scumm/help.cpp:260 engines/scumm/help.cpp:268 +msgid "Upper left item" +msgstr "" + +#: engines/scumm/help.cpp:261 engines/scumm/help.cpp:270 +msgid "Lower left item" +msgstr "" + +#: engines/scumm/help.cpp:262 engines/scumm/help.cpp:271 +msgid "Upper right item" +msgstr "" + +#: engines/scumm/help.cpp:263 engines/scumm/help.cpp:273 +msgid "Lower right item" +msgstr "" + +#: engines/scumm/help.cpp:269 +msgid "Middle left item" +msgstr "" + +#: engines/scumm/help.cpp:272 +msgid "Middle right item" +msgstr "" + +#: engines/scumm/help.cpp:279 engines/scumm/help.cpp:284 +msgid "Switching characters:" +msgstr "Vaihda hahmoa:" + +#: engines/scumm/help.cpp:281 +msgid "Second kid" +msgstr "Toinen lapsi" + +#: engines/scumm/help.cpp:282 +msgid "Third kid" +msgstr "Kolmas lapsi" + +#: engines/scumm/help.cpp:294 +msgid "Fighting controls (numpad):" +msgstr "Tappeluohjaimet (numpad)" + +#: engines/scumm/help.cpp:295 engines/scumm/help.cpp:296 +#: engines/scumm/help.cpp:297 +msgid "Step back" +msgstr "Astu taakse" + +#: engines/scumm/help.cpp:298 +msgid "Block high" +msgstr "Torju korkea" + +#: engines/scumm/help.cpp:299 +msgid "Block middle" +msgstr "Torju keskeltä" + +#: engines/scumm/help.cpp:300 +msgid "Block low" +msgstr "Torju alhaalta" + +#: engines/scumm/help.cpp:301 +msgid "Punch high" +msgstr "Lyö ylös" + +#: engines/scumm/help.cpp:302 +msgid "Punch middle" +msgstr "Lyö keskelle" + +#: engines/scumm/help.cpp:303 +msgid "Punch low" +msgstr "Lyö alas" + +#: engines/scumm/help.cpp:306 +msgid "These are for Indy on left." +msgstr "Nämä ovat Indylle vasemmalla." + +#: engines/scumm/help.cpp:307 +msgid "When Indy is on the right," +msgstr "Kun Indy on oikealla, " + +#: engines/scumm/help.cpp:308 +msgid "7, 4, and 1 are switched with" +msgstr "7, 4 ja 1 vaihdetaan näppäinten" + +#: engines/scumm/help.cpp:309 +msgid "9, 6, and 3, respectively." +msgstr "9, 6 ja 3 kanssa." + +#: engines/scumm/help.cpp:316 +msgid "Biplane controls (numpad):" +msgstr "Koneen ohjaimet (numpad):" + +#: engines/scumm/help.cpp:317 +msgid "Fly to upper left" +msgstr "Lennä ylös vasemmalle" + +#: engines/scumm/help.cpp:318 +msgid "Fly to left" +msgstr "Lennä vasemmalle" + +#: engines/scumm/help.cpp:319 +msgid "Fly to lower left" +msgstr "Lennä alas vasemmalle" + +#: engines/scumm/help.cpp:320 +msgid "Fly upwards" +msgstr "Lennä ylöspäin" + +#: engines/scumm/help.cpp:321 +msgid "Fly straight" +msgstr "Lennä suoraan" + +#: engines/scumm/help.cpp:322 +msgid "Fly down" +msgstr "Lennä alas" + +#: engines/scumm/help.cpp:323 +msgid "Fly to upper right" +msgstr "Lennä ylös oikealle" + +#: engines/scumm/help.cpp:324 +msgid "Fly to right" +msgstr "Lennä oikealle" + +#: engines/scumm/help.cpp:325 +msgid "Fly to lower right" +msgstr "Lennä alas oikealle" + +#: engines/scumm/scumm.cpp:1774 +#, c-format +msgid "" +"Native MIDI support requires the Roland Upgrade from LucasArts,\n" +"but %s is missing. Using AdLib instead." +msgstr "" +"Suora MIDI tuki vaatii Roland päivityksen LucasArtsilta, mutta\n" +"%s puuttuu. Käytetään AdLibia sen sijaan." + +#: engines/scumm/scumm.cpp:2295 engines/agos/saveload.cpp:220 +#, c-format +msgid "" +"Failed to save game state to file:\n" +"\n" +"%s" +msgstr "" +"Pelitilan tallennus ei onnistunut tiedostoon:\n" +"\n" +"%s" + +#: engines/scumm/scumm.cpp:2302 engines/agos/saveload.cpp:185 +#, c-format +msgid "" +"Failed to load game state from file:\n" +"\n" +"%s" +msgstr "" +"Pelitilan lataus ei onnistunut tiedostosta:\n" +"\n" +"%s" + +#: engines/scumm/scumm.cpp:2314 engines/agos/saveload.cpp:228 +#, c-format +msgid "" +"Successfully saved game state in file:\n" +"\n" +"%s" +msgstr "" +"Pelitilan tallennus onnistui tiedostoon:\n" +"\n" +"%s" + +#: engines/scumm/scumm.cpp:2529 +msgid "" +"Usually, Maniac Mansion would start now. But ScummVM doesn't do that yet. To " +"play it, go to 'Add Game' in the ScummVM start menu and select the 'Maniac' " +"directory inside the Tentacle game directory." +msgstr "" +"Maniac Mansionin pitäisi nyt käynnistyä, mutta ScummVM ei tue sitä vielä. " +"Pelataksesi Maniac Mansionia, mene ScummVM:n päävalikkoon ja paina 'Lisää " +"peli'. Valitse 'Maniac' hakemisto Tentacle hakemiston sisältä." + +#. I18N: Option for fast scene switching +#: engines/mohawk/dialogs.cpp:92 engines/mohawk/dialogs.cpp:171 +msgid "~Z~ip Mode Activated" +msgstr "~Z~ip moodi valittu" + +#: engines/mohawk/dialogs.cpp:93 +msgid "~T~ransitions Enabled" +msgstr "Siirtymät päällä" + +#. I18N: Drop book page +#: engines/mohawk/dialogs.cpp:95 +msgid "~D~rop Page" +msgstr "Pudota sivu" + +#: engines/mohawk/dialogs.cpp:99 +msgid "~S~how Map" +msgstr "Näytä kartta" + +#: engines/mohawk/dialogs.cpp:105 +msgid "~M~ain Menu" +msgstr "Päävalikko" + +#: engines/mohawk/dialogs.cpp:172 +msgid "~W~ater Effect Enabled" +msgstr "Vesiefekti päällä" + +#: engines/agos/animation.cpp:557 +#, c-format +msgid "Cutscene file '%s' not found!" +msgstr "Videotiedostoa '%s' ei löytynyt!" + +#: engines/gob/inter_playtoons.cpp:256 engines/gob/inter_v2.cpp:1287 +#: engines/tinsel/saveload.cpp:532 +msgid "Failed to load game state from file." +msgstr "Pelitallenteen lataaminen tiedostosta epäonnistui." + +#: engines/gob/inter_v2.cpp:1357 engines/tinsel/saveload.cpp:545 +msgid "Failed to save game state to file." +msgstr "Pelin tallentaminen tiedostoon epäonnistui." + +#: engines/gob/inter_v5.cpp:107 +msgid "Failed to delete file." +msgstr "Tiedoston tuhoaminen ei onnistunut." + +#: engines/groovie/script.cpp:420 +msgid "Failed to save game" +msgstr "Pelin tallentaminen epäonnistui." + +#. I18N: Studio audience adds an applause and cheering sounds whenever +#. Malcolm makes a joke. +#: engines/kyra/detection.cpp:62 +msgid "Studio audience" +msgstr "Studioyleisö" + +#: engines/kyra/detection.cpp:63 +msgid "Enable studio audience" +msgstr "Studioyleisö päälle" + +#. I18N: This option allows the user to skip text and cutscenes. +#: engines/kyra/detection.cpp:73 +msgid "Skip support" +msgstr "Ohita tuki" + +#: engines/kyra/detection.cpp:74 +msgid "Allow text and cutscenes to be skipped" +msgstr "Salli tekstin ja videoiden ohittaminen" + +#. I18N: Helium mode makes people sound like they've inhaled Helium. +#: engines/kyra/detection.cpp:84 +msgid "Helium mode" +msgstr "Helium moodi" + +#: engines/kyra/detection.cpp:85 +msgid "Enable helium mode" +msgstr "Käytä helium moodia" + +#. I18N: When enabled, this option makes scrolling smoother when +#. changing from one screen to another. +#: engines/kyra/detection.cpp:99 +msgid "Smooth scrolling" +msgstr "Pehmeä vieritys" + +#: engines/kyra/detection.cpp:100 +msgid "Enable smooth scrolling when walking" +msgstr "Käytä pehmeää vieritystä kävellessä" + +#. I18N: When enabled, this option changes the cursor when it floats to the +#. edge of the screen to a directional arrow. The player can then click to +#. walk towards that direction. +#: engines/kyra/detection.cpp:112 +msgid "Floating cursors" +msgstr "Leijuva kursori" + +#: engines/kyra/detection.cpp:113 +msgid "Enable floating cursors" +msgstr "Käytä leijuvia kursoreita" + +#. I18N: HP stands for Hit Points +#: engines/kyra/detection.cpp:127 +msgid "HP bar graphs" +msgstr "Kestopisteet (HP) graafisina palkkeina" + +#: engines/kyra/detection.cpp:128 +msgid "Enable hit point bar graphs" +msgstr "Käytä kestopisteissä värillisiä grafiikkapalkkeja numeroiden sijaan" + +#: engines/kyra/lol.cpp:478 +msgid "Attack 1" +msgstr "Hyökkäys 1" + +#: engines/kyra/lol.cpp:479 +msgid "Attack 2" +msgstr "Hyökkäys 2" + +#: engines/kyra/lol.cpp:480 +msgid "Attack 3" +msgstr "Hyökkäys 3" + +#: engines/kyra/lol.cpp:481 +msgid "Move Forward" +msgstr "Liiku eteenpäin" + +#: engines/kyra/lol.cpp:482 +msgid "Move Back" +msgstr "Liiku taaksepäin" + +#: engines/kyra/lol.cpp:483 +msgid "Slide Left" +msgstr "Liiku vasemmalle" + +#: engines/kyra/lol.cpp:484 +msgid "Slide Right" +msgstr "Liiku oikealle" + +#: engines/kyra/lol.cpp:485 +msgid "Turn Left" +msgstr "Käänny vasemmalle" + +#: engines/kyra/lol.cpp:486 +msgid "Turn Right" +msgstr "Käänny oikealle" + +#: engines/kyra/lol.cpp:487 +msgid "Rest" +msgstr "Lepää" + +#: engines/kyra/lol.cpp:488 +msgid "Options" +msgstr "Asetukset" + +#: engines/kyra/lol.cpp:489 +msgid "Choose Spell" +msgstr "Valitse loitsu" + +#: engines/kyra/sound_midi.cpp:477 +msgid "" +"You appear to be using a General MIDI device,\n" +"but your game only supports Roland MT32 MIDI.\n" +"We try to map the Roland MT32 instruments to\n" +"General MIDI ones. It is still possible that\n" +"some tracks sound incorrect." +msgstr "" +"Käytät General MIDI-äänilaitetta, mutta peli\n" +"tukee vain Roland MT-32:sta. Jotkut ääniraidat\n" +"eivät ehkä kuulosta siltä miltä niiden pitäisi." + +#: engines/queen/queen.cpp:59 +msgid "Alternative intro" +msgstr "Vaihtoehtoinen intro" + +#: engines/queen/queen.cpp:60 +msgid "Use an alternative game intro (CD version only)" +msgstr "Käytä vaihtoehtoista pelin introa (vain CD versiossa)" + +#: engines/sky/compact.cpp:130 +msgid "" +"Unable to find \"sky.cpt\" file!\n" +"Please download it from www.scummvm.org" +msgstr "" +"Tiedostoa \"sky.cpt\" ei löydy!\n" +"Hae se ScummVM:n verkkosivulta." + +#: engines/sky/compact.cpp:141 +msgid "" +"The \"sky.cpt\" file has an incorrect size.\n" +"Please (re)download it from www.scummvm.org" +msgstr "" +"Tiedosto \"sky.cpt\" on väärän kokoinen.\n" +"Hae uusi versio ScummVM:n verkkosivuilta." + +#: engines/sky/detection.cpp:44 +msgid "Floppy intro" +msgstr "Levykeversion intro" + +#: engines/sky/detection.cpp:45 +msgid "Use the floppy version's intro (CD version only)" +msgstr "Käytä levykeversion introa (vain CD versiossa)" + +#: engines/sword1/animation.cpp:519 +#, c-format +msgid "PSX stream cutscene '%s' cannot be played in paletted mode" +msgstr "PSX stream videota '%s' ei voi näyttää paletisoidussa tilassa" + +#: engines/sword1/animation.cpp:540 engines/sword2/animation.cpp:439 +msgid "DXA cutscenes found but ScummVM has been built without zlib support" +msgstr "" +"DXA videotiedostoja löydettiin mutta ScummVM on käännetty ilman zlib-tukea" + +#: engines/sword1/animation.cpp:550 engines/sword2/animation.cpp:449 +msgid "MPEG2 cutscenes are no longer supported" +msgstr "MPEG2 videotiedostoja ei enää tueta" + +#: engines/sword1/animation.cpp:556 engines/sword2/animation.cpp:457 +#, c-format +msgid "Cutscene '%s' not found" +msgstr "Videotiedosto '%s' ei löytynyt" + +#: engines/sword1/control.cpp:865 +msgid "" +"ScummVM found that you have old savefiles for Broken Sword 1 that should be " +"converted.\n" +"The old save game format is no longer supported, so you will not be able to " +"load your games if you don't convert them.\n" +"\n" +"Press OK to convert them now, otherwise you will be asked again the next " +"time you start the game.\n" +msgstr "" +"Broken Sword 1:n pelitallennukset ovat vanhassa formaatissa. Tallennukset " +"tulee muuntaa,\n" +"jotta niitä voi käyttää ScummVM:ssä. Paina 'Hyväksy' muuntaaksesi tiedostot. " +"Mikäli et halua muuntaa tiedostoja nyt\n" +"ScummVM kysyy asiaa seuraavan kerran kun käynnistät pelin.\n" + +#: engines/sword1/control.cpp:1234 +#, c-format +msgid "" +"Target new save game already exists!\n" +"Would you like to keep the old save game (%s) or the new one (%s)?\n" +msgstr "" +"Kohdetiedosto on jo olemassa!\n" +"Säilytetäänkö vanha pelitallennus (%s), vai uusi pelitallennus (%s)?\n" + +#: engines/sword1/control.cpp:1237 +msgid "Keep the old one" +msgstr "Säilytä vanha tallennus" + +#: engines/sword1/control.cpp:1237 +msgid "Keep the new one" +msgstr "Säilytä uusi tallennus" + +#: engines/sword1/logic.cpp:1633 +msgid "This is the end of the Broken Sword 1 Demo" +msgstr "Broken Sword 1:n demo päättyy tähän" + +#: engines/sword2/animation.cpp:419 +msgid "" +"PSX cutscenes found but ScummVM has been built without RGB color support" +msgstr "PSX videoita löydetty, mutta ScummVM on käännetty ilman RGB tukea" + +#: engines/sword2/sword2.cpp:79 +msgid "Show object labels" +msgstr "Näytä esineiden tiedot" + +#: engines/sword2/sword2.cpp:80 +msgid "Show labels for objects on mouse hover" +msgstr "Näytä esineiden kuvaus kohdistaessasi kursorin esineen ylle" + +#: engines/teenagent/resources.cpp:94 +msgid "" +"You're missing the 'teenagent.dat' file. Get it from the ScummVM website" +msgstr "" +"Asennuksestasi puuttuu 'teenagent.dat' tiedosto. Hae se ScummVM:n " +"nettisivuilta" + +#: engines/teenagent/resources.cpp:115 +msgid "" +"The teenagent.dat file is compressed and zlib hasn't been included in this " +"executable. Please decompress it" +msgstr "" +"Tiedosto teenagent.dat on pakattu, mutta zlib-kirjastoa ei ole käännetty " +"ScummVM:ään. Pura teenagent.dat." + +#: engines/parallaction/saveload.cpp:133 +#, c-format +msgid "" +"Can't save game in slot %i\n" +"\n" +msgstr "" +"Pelin tallennus kohtaan ei onnistunut kohtaan %i\n" +"\n" + +#: engines/parallaction/saveload.cpp:204 +msgid "Loading game..." +msgstr "Ladataan peliä..." + +#: engines/parallaction/saveload.cpp:219 +msgid "Saving game..." +msgstr "Tallennetaan peliä..." + +#: engines/parallaction/saveload.cpp:272 +msgid "" +"ScummVM found that you have old savefiles for Nippon Safes that should be " +"renamed.\n" +"The old names are no longer supported, so you will not be able to load your " +"games if you don't convert them.\n" +"\n" +"Press OK to convert them now, otherwise you will be asked next time.\n" +msgstr "" +"Pelin Nippon Safes tallennukset ovat vanhassa muodossa, joita ei enää tueta. " +"Tallennukset\n" +"tulee nimetä uudelleen ennen kuin ScummVM voi käyttää niitä. Paina 'Hyväksy' " +"muuntaaksesi tallennukset.\n" +"Jos et tee muunnosta nyt, ScummVM kysyy sinulta uudelleen seuraavalla " +"kerralla kun käynnistät pelin.\n" + +#: engines/parallaction/saveload.cpp:319 +msgid "ScummVM successfully converted all your savefiles." +msgstr "ScummVM muunsi kaikki pelitallenteet onnistuneesti" + +#: engines/parallaction/saveload.cpp:321 +msgid "" +"ScummVM printed some warnings in your console window and can't guarantee all " +"your files have been converted.\n" +"\n" +"Please report to the team." +msgstr "" +"Osa tiedostoista ei ehkä muuntunut oikein. Varoitukset tulostettiin konsoli-" +"ikkunaan. Pyydämme että ilmoittaisit\n" +"niistä ScummVM:n kehittäjille." + +#: audio/fmopl.cpp:49 +msgid "MAME OPL emulator" +msgstr "MAME OPL emulaattori" + +#: audio/fmopl.cpp:51 +msgid "DOSBox OPL emulator" +msgstr "DOSBox OPL emulaattori" + +#: audio/mididrv.cpp:209 +#, c-format +msgid "" +"The selected audio device '%s' was not found (e.g. might be turned off or " +"disconnected)." +msgstr "" +"Valittua äänilaitetta '%s' ei löytynyt. Se saattaa olla kytketty pois päältä " +"tai irrotettuna tietokoneesta." + +#: audio/mididrv.cpp:209 audio/mididrv.cpp:221 audio/mididrv.cpp:257 +#: audio/mididrv.cpp:272 +msgid "Attempting to fall back to the next available device..." +msgstr "Yritetään käyttää seuraavaa saatavilla olevaa laitetta..." + +#: audio/mididrv.cpp:221 +#, c-format +msgid "" +"The selected audio device '%s' cannot be used. See log file for more " +"information." +msgstr "" +"Valitsemaasi äänilaitetta '%s' ei voida käyttää. Avaa lokitiedosto " +"saadaksesi lisätietoja." + +#: audio/mididrv.cpp:257 +#, c-format +msgid "" +"The preferred audio device '%s' was not found (e.g. might be turned off or " +"disconnected)." +msgstr "" +"Ensisijaista äänilaitetta '%s' ei löytynyt. Se saattaa olla kytketty pois " +"päältä tai irrotettuna tietokoneesta." + +#: audio/mididrv.cpp:272 +#, c-format +msgid "" +"The preferred audio device '%s' cannot be used. See log file for more " +"information." +msgstr "" +"Ensisijaista äänilaitetta '%s' ei voida käyttää. Avaa lokitiedosto " +"saadaksesi lisätietoja." + +#: audio/null.h:43 +msgid "No music" +msgstr "Ei musiikkia" + +#: audio/mods/paula.cpp:189 +msgid "Amiga Audio Emulator" +msgstr "Amiga Audio emulaattori" + +#: audio/softsynth/adlib.cpp:2284 +msgid "AdLib Emulator" +msgstr "AdLib emulaattori" + +#: audio/softsynth/appleiigs.cpp:33 +msgid "Apple II GS Emulator (NOT IMPLEMENTED)" +msgstr "Apple II GS emulaattori (EI TOTEUTETTU)" + +#: audio/softsynth/sid.cpp:1430 +msgid "C64 Audio Emulator" +msgstr "C64 Audio emulaattori" + +#: audio/softsynth/mt32.cpp:293 +msgid "Initializing MT-32 Emulator" +msgstr "Alustetaan MT-32 emulaattoria" + +#: audio/softsynth/mt32.cpp:512 +msgid "MT-32 Emulator" +msgstr "MT-32 emulaattori" + +#: audio/softsynth/pcspk.cpp:139 +msgid "PC Speaker Emulator" +msgstr "PC kaiuttimen emulaattori" + +#: audio/softsynth/pcspk.cpp:158 +msgid "IBM PCjr Emulator" +msgstr "IBM PCjr emulaattori" + +#: backends/keymapper/remap-dialog.cpp:47 +msgid "Keymap:" +msgstr "Näppäinkartta:" + +#: backends/keymapper/remap-dialog.cpp:66 +msgid " (Effective)" +msgstr "" + +#: backends/keymapper/remap-dialog.cpp:106 +msgid " (Active)" +msgstr " (Aktiivinen)" + +#: backends/keymapper/remap-dialog.cpp:106 +msgid " (Blocked)" +msgstr " (Estetty)" + +#: backends/keymapper/remap-dialog.cpp:119 +msgid " (Global)" +msgstr " (Globaali)" + +#: backends/keymapper/remap-dialog.cpp:127 +msgid " (Game)" +msgstr " (Peli)" + +#: backends/midi/windows.cpp:164 +msgid "Windows MIDI" +msgstr "Windows MIDI" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:57 +msgid "ScummVM Main Menu" +msgstr "ScummVM päävalikko" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:63 +msgid "~L~eft handed mode" +msgstr "~V~asenkätinen tila" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:64 +msgid "~I~ndy fight controls" +msgstr "~I~ndyn tappelukontrollit" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:65 +msgid "Show mouse cursor" +msgstr "Näytä hiiren kursori" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:66 +msgid "Snap to edges" +msgstr "Tartu reunoihin" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:68 +msgid "Touch X Offset" +msgstr "Kosketuksen X siirros" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:75 +msgid "Touch Y Offset" +msgstr "Kosketuksen Y siirto" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:87 +msgid "Use laptop trackpad-style cursor control" +msgstr "Käytä kannettavan trackpad-tyylistä kursorinhallintaa" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:88 +msgid "Tap for left click, double tap right click" +msgstr "" +"Napauta kerran vasen klikkausta varten, napauta kahdesti oikeaa klikkausta " +"varten" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:90 +msgid "Sensitivity" +msgstr "Herkkyys" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:99 +msgid "Initial top screen scale:" +msgstr "" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:105 +msgid "Main screen scaling:" +msgstr "Pääruudun skaalaus" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:107 +msgid "Hardware scale (fast, but low quality)" +msgstr "Laitteistoskaalaus (nopea, mutta huono laatu)" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:108 +msgid "Software scale (good quality, but slower)" +msgstr "Ohjelmistoskaalaus (hyvä laatu, mutta hitaampi)" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:109 +msgid "Unscaled (you must scroll left and right)" +msgstr "Ei skaalattu (vierittä vasemmalle ja oikealle itse)" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:111 +msgid "Brightness:" +msgstr "Kirkkaus:" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:121 +msgid "High quality audio (slower) (reboot)" +msgstr "Korkealuokkainen ääni (hidas) (buuttaus)" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:122 +msgid "Disable power off" +msgstr "" + +#: backends/platform/iphone/osys_events.cpp:300 +msgid "Mouse-click-and-drag mode enabled." +msgstr "Hiiren vedä-ja-pudota tila käytössä." + +#: backends/platform/iphone/osys_events.cpp:302 +msgid "Mouse-click-and-drag mode disabled." +msgstr "Hiiren vedä-ja-pudota tila pois käytöstä." + +#: backends/platform/iphone/osys_events.cpp:313 +msgid "Touchpad mode enabled." +msgstr "Touchad tila päällä" + +#: backends/platform/iphone/osys_events.cpp:315 +msgid "Touchpad mode disabled." +msgstr "Touchpad tila pois päältä" + +#: backends/platform/maemo/maemo.cpp:209 +msgid "Click Mode" +msgstr "Klikkaus moodi" + +#: backends/platform/maemo/maemo.cpp:215 +#: backends/platform/symbian/src/SymbianActions.cpp:42 +#: backends/platform/wince/CEActionsPocket.cpp:60 +#: backends/platform/wince/CEActionsSmartphone.cpp:43 +#: backends/platform/bada/form.cpp:281 +msgid "Left Click" +msgstr "Vasen klikkaus" + +#: backends/platform/maemo/maemo.cpp:218 +msgid "Middle Click" +msgstr "Keskiklikkaus" + +#: backends/platform/maemo/maemo.cpp:221 +#: backends/platform/symbian/src/SymbianActions.cpp:43 +#: backends/platform/wince/CEActionsSmartphone.cpp:44 +#: backends/platform/bada/form.cpp:273 +msgid "Right Click" +msgstr "Oikea klikkaus" + +#: backends/platform/sdl/macosx/appmenu_osx.mm:77 +msgid "Hide ScummVM" +msgstr "Piilota ScummVM" + +#: backends/platform/sdl/macosx/appmenu_osx.mm:82 +msgid "Hide Others" +msgstr "Piilota muut" + +#: backends/platform/sdl/macosx/appmenu_osx.mm:87 +msgid "Show All" +msgstr "Näytä kaikki" + +#: backends/platform/sdl/macosx/appmenu_osx.mm:109 +#: backends/platform/sdl/macosx/appmenu_osx.mm:120 +msgid "Window" +msgstr "Ikkuna" + +#: backends/platform/sdl/macosx/appmenu_osx.mm:114 +msgid "Minimize" +msgstr "Minimoi" + +#: backends/graphics/surfacesdl/surfacesdl-graphics.cpp:45 +msgid "Normal (no scaling)" +msgstr "Normaali (ei skaalausta)" + +#: backends/graphics/surfacesdl/surfacesdl-graphics.cpp:64 +msgctxt "lowres" +msgid "Normal (no scaling)" +msgstr "Normaali (ei skaalausta)" + +#: backends/graphics/surfacesdl/surfacesdl-graphics.cpp:2135 +#: backends/graphics/openglsdl/openglsdl-graphics.cpp:533 +msgid "Enabled aspect ratio correction" +msgstr "Kuvasuhteen korjaus päällä" + +#: backends/graphics/surfacesdl/surfacesdl-graphics.cpp:2141 +#: backends/graphics/openglsdl/openglsdl-graphics.cpp:538 +msgid "Disabled aspect ratio correction" +msgstr "Kuvasuhteen korjaus pois päältä" + +#: backends/graphics/surfacesdl/surfacesdl-graphics.cpp:2196 +msgid "Active graphics filter:" +msgstr "Valittu grafiikkafiltteri:" + +#: backends/graphics/surfacesdl/surfacesdl-graphics.cpp:2238 +#: backends/graphics/openglsdl/openglsdl-graphics.cpp:477 +msgid "Windowed mode" +msgstr "Ikkunoitu tila" + +#: backends/graphics/opengl/opengl-graphics.cpp:135 +msgid "OpenGL Normal" +msgstr "OpenGL Normal" + +#: backends/graphics/opengl/opengl-graphics.cpp:136 +msgid "OpenGL Conserve" +msgstr "OpenGL Conserve" + +#: backends/graphics/opengl/opengl-graphics.cpp:137 +msgid "OpenGL Original" +msgstr "OpenGL Original" + +#: backends/graphics/openglsdl/openglsdl-graphics.cpp:415 +msgid "Current display mode" +msgstr "Nykyinen näyttötila" + +#: backends/graphics/openglsdl/openglsdl-graphics.cpp:428 +msgid "Current scale" +msgstr "Nykyinen skaalaus" + +#: backends/graphics/openglsdl/openglsdl-graphics.cpp:558 +msgid "Active filter mode: Linear" +msgstr "Valittu filtteritila: Linear" + +#: backends/graphics/openglsdl/openglsdl-graphics.cpp:560 +msgid "Active filter mode: Nearest" +msgstr "Valittu filtteritila: Nearest" + +#: backends/platform/symbian/src/SymbianActions.cpp:38 +#: backends/platform/wince/CEActionsSmartphone.cpp:39 +msgid "Up" +msgstr "Ylös" + +#: backends/platform/symbian/src/SymbianActions.cpp:39 +#: backends/platform/wince/CEActionsSmartphone.cpp:40 +msgid "Down" +msgstr "Alas" + +#: backends/platform/symbian/src/SymbianActions.cpp:40 +#: backends/platform/wince/CEActionsSmartphone.cpp:41 +msgid "Left" +msgstr "Vasen" + +#: backends/platform/symbian/src/SymbianActions.cpp:41 +#: backends/platform/wince/CEActionsSmartphone.cpp:42 +msgid "Right" +msgstr "Oikea" + +#: backends/platform/symbian/src/SymbianActions.cpp:46 +#: backends/platform/wince/CEActionsSmartphone.cpp:47 +msgid "Zone" +msgstr "" + +#: backends/platform/symbian/src/SymbianActions.cpp:47 +#: backends/platform/wince/CEActionsPocket.cpp:54 +#: backends/platform/wince/CEActionsSmartphone.cpp:48 +msgid "Multi Function" +msgstr "" + +#: backends/platform/symbian/src/SymbianActions.cpp:48 +msgid "Swap character" +msgstr "Vaihda hahmoa" + +#: backends/platform/symbian/src/SymbianActions.cpp:49 +msgid "Skip text" +msgstr "Ohita teksti" + +#: backends/platform/symbian/src/SymbianActions.cpp:51 +msgid "Fast mode" +msgstr "Nopea moodi" + +#: backends/platform/symbian/src/SymbianActions.cpp:53 +msgid "Debugger" +msgstr "Debuggeri" + +#: backends/platform/symbian/src/SymbianActions.cpp:54 +msgid "Global menu" +msgstr "Päävalikko" + +#: backends/platform/symbian/src/SymbianActions.cpp:55 +msgid "Virtual keyboard" +msgstr "Virtuaalinen näppäimistö" + +#: backends/platform/symbian/src/SymbianActions.cpp:56 +msgid "Key mapper" +msgstr "Näppäinmäärittelijä" + +#: backends/events/symbiansdl/symbiansdl-events.cpp:184 +msgid "Do you want to quit ?" +msgstr "Haluatko lopettaa?" + +#: backends/platform/wii/options.cpp:51 +msgid "Video" +msgstr "Video" + +#: backends/platform/wii/options.cpp:54 +msgid "Current video mode:" +msgstr "Nykyinen videotila:" + +#: backends/platform/wii/options.cpp:56 +msgid "Double-strike" +msgstr "" + +#: backends/platform/wii/options.cpp:60 +msgid "Horizontal underscan:" +msgstr "Horisontaalinenunderscan" + +#: backends/platform/wii/options.cpp:66 +msgid "Vertical underscan:" +msgstr "Vertikaalinen underscan" + +#: backends/platform/wii/options.cpp:71 +msgid "Input" +msgstr "Syöte" + +#: backends/platform/wii/options.cpp:74 +msgid "GC Pad sensitivity:" +msgstr "GC Padin herkkyys" + +#: backends/platform/wii/options.cpp:80 +msgid "GC Pad acceleration:" +msgstr "GC PAdin kiihtyvyys" + +#: backends/platform/wii/options.cpp:86 +msgid "DVD" +msgstr "DVD" + +#: backends/platform/wii/options.cpp:89 backends/platform/wii/options.cpp:101 +msgid "Status:" +msgstr "Tila:" + +#: backends/platform/wii/options.cpp:90 backends/platform/wii/options.cpp:102 +msgid "Unknown" +msgstr "Tuntematon" + +#: backends/platform/wii/options.cpp:93 +msgid "Mount DVD" +msgstr "Liitä DVD" + +#: backends/platform/wii/options.cpp:94 +msgid "Unmount DVD" +msgstr "Vapauta DVD" + +#: backends/platform/wii/options.cpp:98 +msgid "SMB" +msgstr "SMB" + +#: backends/platform/wii/options.cpp:106 +msgid "Server:" +msgstr "Palvelin:" + +#: backends/platform/wii/options.cpp:110 +msgid "Share:" +msgstr "Jako:" + +#: backends/platform/wii/options.cpp:114 +msgid "Username:" +msgstr "Käyttäjänimi:" + +#: backends/platform/wii/options.cpp:118 +msgid "Password:" +msgstr "Salasana:" + +#: backends/platform/wii/options.cpp:121 +msgid "Init network" +msgstr "Alusta verkko" + +#: backends/platform/wii/options.cpp:123 +msgid "Mount SMB" +msgstr "Mounttaa SMB" + +#: backends/platform/wii/options.cpp:124 +msgid "Unmount SMB" +msgstr "Vapauta SMB" + +#: backends/platform/wii/options.cpp:143 +msgid "DVD Mounted successfully" +msgstr "DVD mountattu onnistuneesti" + +#: backends/platform/wii/options.cpp:146 +msgid "Error while mounting the DVD" +msgstr "Virhe liitettäessä DVD:tä" + +#: backends/platform/wii/options.cpp:148 +msgid "DVD not mounted" +msgstr "DVD ei ole liitetty" + +#: backends/platform/wii/options.cpp:161 +msgid "Network up, share mounted" +msgstr "Verkko ylhäällä, jako liitetty" + +#: backends/platform/wii/options.cpp:163 +msgid "Network up" +msgstr "Verkko ylhäällä" + +#: backends/platform/wii/options.cpp:166 +msgid ", error while mounting the share" +msgstr ", virhe liitettäessä jakoa" + +#: backends/platform/wii/options.cpp:168 +msgid ", share not mounted" +msgstr ", jakoa ei liitetty" + +#: backends/platform/wii/options.cpp:174 +msgid "Network down" +msgstr "Verkko alhaalla" + +#: backends/platform/wii/options.cpp:178 +msgid "Initializing network" +msgstr "Alustetaan verkkoa" + +#: backends/platform/wii/options.cpp:182 +msgid "Timeout while initializing network" +msgstr "Aikakatkaisu kun verkkoa alustettiin" + +#: backends/platform/wii/options.cpp:186 +#, c-format +msgid "Network not initialized (%d)" +msgstr "Verkko ei ole alustettu (%d)" + +#: backends/platform/wince/CEActionsPocket.cpp:46 +msgid "Hide Toolbar" +msgstr "Piilota työkalupalkki" + +#: backends/platform/wince/CEActionsPocket.cpp:47 +msgid "Show Keyboard" +msgstr "Näytä näppäimistö" + +#: backends/platform/wince/CEActionsPocket.cpp:48 +msgid "Sound on/off" +msgstr "Äänet päällä/pois" + +#: backends/platform/wince/CEActionsPocket.cpp:49 +msgid "Right click" +msgstr "Oikea klikkaus" + +#: backends/platform/wince/CEActionsPocket.cpp:50 +msgid "Show/Hide Cursor" +msgstr "Näytä/Piilota kursori" + +#: backends/platform/wince/CEActionsPocket.cpp:51 +msgid "Free look" +msgstr "Vapaa katselu" + +#: backends/platform/wince/CEActionsPocket.cpp:52 +msgid "Zoom up" +msgstr "Zoomaa ylös" + +#: backends/platform/wince/CEActionsPocket.cpp:53 +msgid "Zoom down" +msgstr "Zoomaa alas" + +#: backends/platform/wince/CEActionsPocket.cpp:55 +#: backends/platform/wince/CEActionsSmartphone.cpp:49 +msgid "Bind Keys" +msgstr "Määritä näppäimet" + +#: backends/platform/wince/CEActionsPocket.cpp:56 +msgid "Cursor Up" +msgstr "Nuoli ylös" + +#: backends/platform/wince/CEActionsPocket.cpp:57 +msgid "Cursor Down" +msgstr "Nuoli alas" + +#: backends/platform/wince/CEActionsPocket.cpp:58 +msgid "Cursor Left" +msgstr "Nuoli vasemmalle" + +#: backends/platform/wince/CEActionsPocket.cpp:59 +msgid "Cursor Right" +msgstr "Nuoli oikealle" + +#: backends/platform/wince/CEActionsPocket.cpp:267 +#: backends/platform/wince/CEActionsSmartphone.cpp:231 +msgid "Do you want to load or save the game?" +msgstr "Haluatko tallentaa vai ladata pelin?" + +#: backends/platform/wince/CEActionsPocket.cpp:326 +#: backends/platform/wince/CEActionsSmartphone.cpp:287 +msgid " Are you sure you want to quit ? " +msgstr " Haluatko varmasti lopettaa?" + +#: backends/platform/wince/CEActionsSmartphone.cpp:50 +msgid "Keyboard" +msgstr "Näppäimistö" + +#: backends/platform/wince/CEActionsSmartphone.cpp:51 +msgid "Rotate" +msgstr "Käännä" + +#: backends/platform/wince/CELauncherDialog.cpp:56 +msgid "Using SDL driver " +msgstr "Käytetään SDL:n ajuria" + +#: backends/platform/wince/CELauncherDialog.cpp:60 +msgid "Display " +msgstr "Näyttö" + +#: backends/platform/wince/CELauncherDialog.cpp:83 +msgid "Do you want to perform an automatic scan ?" +msgstr "Haluatko suorittaa automaattisen skannauksen?" + +#: backends/platform/wince/wince-sdl.cpp:515 +msgid "Map right click action" +msgstr "Määritä oikeaklikkauksen toiminto" + +#: backends/platform/wince/wince-sdl.cpp:519 +msgid "You must map a key to the 'Right Click' action to play this game" +msgstr "" +"Hiiren oikealle klikkaukselle on määriteltävä näppäin, ennen kuin tätä peliä " +"voi pelata" + +#: backends/platform/wince/wince-sdl.cpp:528 +msgid "Map hide toolbar action" +msgstr "Määritä näppäin työkalupalkin piilottamiselle" + +#: backends/platform/wince/wince-sdl.cpp:532 +msgid "You must map a key to the 'Hide toolbar' action to play this game" +msgstr "" +"Työkalupalkin piilottamiselle on määriteltävä näppäin, ennen kuin tätä peliä " +"voi pelata" + +#: backends/platform/wince/wince-sdl.cpp:541 +msgid "Map Zoom Up action (optional)" +msgstr "Zoomaa karttaa ylös (valinnainen)" + +#: backends/platform/wince/wince-sdl.cpp:544 +msgid "Map Zoom Down action (optional)" +msgstr "Zoomaa karttaa alas (valinnainen)" + +#: backends/platform/wince/wince-sdl.cpp:552 +msgid "" +"Don't forget to map a key to 'Hide Toolbar' action to see the whole inventory" +msgstr "" +"Muista määritellä näppäin työkalupalkin piilottamiselle, jotta voit nähdä " +"koko tavaraluettelon" + +#: backends/events/default/default-events.cpp:191 +msgid "Do you really want to return to the Launcher?" +msgstr "Haluatko varmasti palata pelivalitsimeen?" + +#: backends/events/default/default-events.cpp:191 +msgid "Launcher" +msgstr "Pelivalitsin" + +#: backends/events/default/default-events.cpp:213 +msgid "Do you really want to quit?" +msgstr "Haluatko varmasti lopettaa?" + +#: backends/events/gph/gph-events.cpp:386 +#: backends/events/gph/gph-events.cpp:429 +#: backends/events/openpandora/op-events.cpp:168 +msgid "Touchscreen 'Tap Mode' - Left Click" +msgstr "Kosketusnäytön 'Tap moodi' - vasen klikkaus" + +#: backends/events/gph/gph-events.cpp:388 +#: backends/events/gph/gph-events.cpp:431 +#: backends/events/openpandora/op-events.cpp:170 +msgid "Touchscreen 'Tap Mode' - Right Click" +msgstr "Kosketusnäytön 'Tap moodi' - oikea klikkaus" + +#: backends/events/gph/gph-events.cpp:390 +#: backends/events/gph/gph-events.cpp:433 +#: backends/events/openpandora/op-events.cpp:172 +msgid "Touchscreen 'Tap Mode' - Hover (No Click)" +msgstr "Kosketusnäytön 'Tap moodi' - ei klikkausta" + +#: backends/events/gph/gph-events.cpp:410 +msgid "Maximum Volume" +msgstr "Maksimi äänenvoimakkuus" + +#: backends/events/gph/gph-events.cpp:412 +msgid "Increasing Volume" +msgstr "Nostetaan äänenvoimakkuutta" + +#: backends/events/gph/gph-events.cpp:418 +msgid "Minimal Volume" +msgstr "Minimi äänenvoimakkuus" + +#: backends/events/gph/gph-events.cpp:420 +msgid "Decreasing Volume" +msgstr "Lasketaan äänenvoimakkuutta" + +#: backends/events/openpandora/op-events.cpp:174 +#, fuzzy +msgid "Touchscreen 'Tap Mode' - Hover (DPad Clicks)" +msgstr "Kosketusnäytön 'Tap moodi' - ei klikkausta" + +#: backends/updates/macosx/macosx-updates.mm:67 +msgid "Check for Updates..." +msgstr "Tarkista päivitykset..." + +#: backends/platform/bada/form.cpp:269 +msgid "Right Click Once" +msgstr "Klikkaa oikealla kerran" + +#: backends/platform/bada/form.cpp:277 +msgid "Move Only" +msgstr "" + +#: backends/platform/bada/form.cpp:291 +msgid "Escape Key" +msgstr "Esc näppäin" + +#: backends/platform/bada/form.cpp:296 +msgid "Game Menu" +msgstr "Pelivalikko" + +#: backends/platform/bada/form.cpp:301 +msgid "Show Keypad" +msgstr "Näytä keypad" + +#: backends/platform/bada/form.cpp:309 +msgid "Control Mouse" +msgstr "Ohjaa hiirtä" + +#: backends/events/maemosdl/maemosdl-events.cpp:192 +msgid "Clicking Enabled" +msgstr "Klikkaus päällä" + +#: backends/events/maemosdl/maemosdl-events.cpp:192 +msgid "Clicking Disabled" +msgstr "Klikkaus pois päältä" diff --git a/po/fr_FR.po b/po/fr_FR.po index 4631d4c91a..ad1406ed18 100644 --- a/po/fr_FR.po +++ b/po/fr_FR.po @@ -1,5 +1,5 @@ # French translation for ScummVM. -# Copyright (C) 2010-2012 ScummVM Team +# Copyright (C) 2010-2013 ScummVM Team # This file is distributed under the same license as the ScummVM package. # Thierry Crozat <criezy@scummvm.org>, 2011. # @@ -7,14 +7,14 @@ msgid "" msgstr "" "Project-Id-Version: ScummVM 1.3.0svn\n" "Report-Msgid-Bugs-To: scummvm-devel@lists.sf.net\n" -"POT-Creation-Date: 2012-07-08 12:25+0100\n" +"POT-Creation-Date: 2012-12-01 17:22+0000\n" "PO-Revision-Date: 2012-07-08 12:24+0100\n" "Last-Translator: Thierry Crozat <criezy@scummvm.org>\n" "Language-Team: French <scummvm-devel@lists.sf.net>\n" +"Language: Francais\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=iso-8859-1\n" "Content-Transfer-Encoding: 8bit\n" -"Language: Francais\n" "Plural-Forms: nplurals=2; plural=n>1;\n" #: gui/about.cpp:91 @@ -45,10 +45,11 @@ msgstr "Remonter" #: gui/browser.cpp:69 gui/chooser.cpp:45 gui/KeysDialog.cpp:43 #: gui/launcher.cpp:345 gui/massadd.cpp:94 gui/options.cpp:1228 -#: gui/saveload.cpp:64 gui/saveload.cpp:173 gui/themebrowser.cpp:54 -#: engines/engine.cpp:442 engines/scumm/dialogs.cpp:190 -#: engines/sword1/control.cpp:865 engines/parallaction/saveload.cpp:274 -#: backends/platform/wii/options.cpp:48 +#: gui/saveload-dialog.cpp:215 gui/saveload-dialog.cpp:275 +#: gui/saveload-dialog.cpp:545 gui/saveload-dialog.cpp:919 +#: gui/themebrowser.cpp:54 engines/engine.cpp:442 +#: engines/scumm/dialogs.cpp:190 engines/sword1/control.cpp:867 +#: engines/parallaction/saveload.cpp:274 backends/platform/wii/options.cpp:48 #: backends/events/default/default-events.cpp:191 #: backends/events/default/default-events.cpp:213 msgid "Cancel" @@ -69,15 +70,15 @@ msgstr "Fermer" msgid "Mouse click" msgstr "Clic de souris" -#: gui/gui-manager.cpp:122 base/main.cpp:300 +#: gui/gui-manager.cpp:122 base/main.cpp:301 msgid "Display keyboard" msgstr "Afficher le clavier" -#: gui/gui-manager.cpp:126 base/main.cpp:304 +#: gui/gui-manager.cpp:126 base/main.cpp:305 msgid "Remap keys" msgstr "Changer l'affectation des touches" -#: gui/gui-manager.cpp:129 base/main.cpp:307 +#: gui/gui-manager.cpp:129 base/main.cpp:308 msgid "Toggle FullScreen" msgstr "Basculer en plein écran" @@ -91,16 +92,16 @@ msgstr "Affecter" #: gui/KeysDialog.cpp:42 gui/launcher.cpp:346 gui/launcher.cpp:1001 #: gui/launcher.cpp:1005 gui/massadd.cpp:91 gui/options.cpp:1229 -#: engines/engine.cpp:361 engines/engine.cpp:372 engines/scumm/dialogs.cpp:192 -#: engines/scumm/scumm.cpp:1775 engines/agos/animation.cpp:561 -#: engines/groovie/script.cpp:420 engines/sky/compact.cpp:131 -#: engines/sky/compact.cpp:141 engines/sword1/animation.cpp:539 -#: engines/sword1/animation.cpp:560 engines/sword1/animation.cpp:570 -#: engines/sword1/animation.cpp:577 engines/sword1/control.cpp:865 -#: engines/sword1/logic.cpp:1633 engines/sword2/animation.cpp:435 -#: engines/sword2/animation.cpp:455 engines/sword2/animation.cpp:465 -#: engines/sword2/animation.cpp:474 engines/parallaction/saveload.cpp:274 -#: backends/platform/wii/options.cpp:47 +#: gui/saveload-dialog.cpp:920 engines/engine.cpp:361 engines/engine.cpp:372 +#: engines/scumm/dialogs.cpp:192 engines/scumm/scumm.cpp:1776 +#: engines/agos/animation.cpp:558 engines/groovie/script.cpp:420 +#: engines/sky/compact.cpp:131 engines/sky/compact.cpp:141 +#: engines/sword1/animation.cpp:519 engines/sword1/animation.cpp:540 +#: engines/sword1/animation.cpp:550 engines/sword1/animation.cpp:557 +#: engines/sword1/control.cpp:867 engines/sword1/logic.cpp:1633 +#: engines/sword2/animation.cpp:419 engines/sword2/animation.cpp:439 +#: engines/sword2/animation.cpp:449 engines/sword2/animation.cpp:458 +#: engines/parallaction/saveload.cpp:274 backends/platform/wii/options.cpp:47 #: backends/platform/wince/CELauncherDialog.cpp:54 msgid "OK" msgstr "OK" @@ -355,7 +356,7 @@ msgstr "Cet ID est déjà utilisé par un autre jeu. Choisissez en un autre svp." msgid "~Q~uit" msgstr "~Q~uitter" -#: gui/launcher.cpp:621 backends/platform/sdl/macosx/appmenu_osx.mm:96 +#: gui/launcher.cpp:621 backends/platform/sdl/macosx/appmenu_osx.mm:95 msgid "Quit ScummVM" msgstr "Quitter ScummVM" @@ -363,7 +364,7 @@ msgstr "Quitter ScummVM" msgid "A~b~out..." msgstr "À ~P~ropos..." -#: gui/launcher.cpp:622 backends/platform/sdl/macosx/appmenu_osx.mm:70 +#: gui/launcher.cpp:622 backends/platform/sdl/macosx/appmenu_osx.mm:69 msgid "About ScummVM" msgstr "À propos de ScummVM" @@ -439,13 +440,13 @@ msgstr "Recherche dans la liste de jeux" msgid "Search:" msgstr "Filtre:" -#: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/mohawk/myst.cpp:255 +#: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/mohawk/myst.cpp:245 #: engines/mohawk/riven.cpp:716 engines/cruise/menu.cpp:214 msgid "Load game:" msgstr "Charger le jeu:" #: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/scumm/dialogs.cpp:188 -#: engines/mohawk/myst.cpp:255 engines/mohawk/riven.cpp:716 +#: engines/mohawk/myst.cpp:245 engines/mohawk/riven.cpp:716 #: engines/cruise/menu.cpp:214 backends/platform/wince/CEActionsPocket.cpp:267 #: backends/platform/wince/CEActionsSmartphone.cpp:231 msgid "Load" @@ -930,68 +931,104 @@ msgstr "" "Le thème que vous avez sélectioné ne support pas la langue française. Si " "vous voulez l'utiliser vous devez d'abord changer de langue." -#: gui/saveload.cpp:59 gui/saveload.cpp:257 +#: gui/saveload-dialog.cpp:166 +msgid "List view" +msgstr "" + +#: gui/saveload-dialog.cpp:167 +msgid "Grid view" +msgstr "" + +#: gui/saveload-dialog.cpp:210 gui/saveload-dialog.cpp:358 msgid "No date saved" msgstr "Date inconnue" -#: gui/saveload.cpp:60 gui/saveload.cpp:258 +#: gui/saveload-dialog.cpp:211 gui/saveload-dialog.cpp:359 msgid "No time saved" msgstr "Heure inconnue" -#: gui/saveload.cpp:61 gui/saveload.cpp:259 +#: gui/saveload-dialog.cpp:212 gui/saveload-dialog.cpp:360 msgid "No playtime saved" msgstr "Durée de jeu inconnue" -#: gui/saveload.cpp:68 gui/saveload.cpp:173 +#: gui/saveload-dialog.cpp:219 gui/saveload-dialog.cpp:275 msgid "Delete" msgstr "Supprimer" -#: gui/saveload.cpp:172 +#: gui/saveload-dialog.cpp:274 msgid "Do you really want to delete this savegame?" msgstr "Voulez-vous vraiment supprimer cette sauvegarde?" -#: gui/saveload.cpp:282 +#: gui/saveload-dialog.cpp:383 gui/saveload-dialog.cpp:872 msgid "Date: " msgstr "Date: " -#: gui/saveload.cpp:286 +#: gui/saveload-dialog.cpp:387 gui/saveload-dialog.cpp:878 msgid "Time: " msgstr "Heure: " -#: gui/saveload.cpp:292 +#: gui/saveload-dialog.cpp:393 gui/saveload-dialog.cpp:886 msgid "Playtime: " msgstr "Durée de jeu: " -#: gui/saveload.cpp:305 gui/saveload.cpp:372 +#: gui/saveload-dialog.cpp:406 gui/saveload-dialog.cpp:494 msgid "Untitled savestate" msgstr "Sauvegarde sans nom" +#: gui/saveload-dialog.cpp:546 +msgid "Next" +msgstr "" + +#: gui/saveload-dialog.cpp:549 +msgid "Prev" +msgstr "" + +#: gui/saveload-dialog.cpp:736 +#, fuzzy +msgid "New Save" +msgstr "Sauver" + +#: gui/saveload-dialog.cpp:736 +#, fuzzy +msgid "Create a new save game" +msgstr "Échec de la sauvegarde." + +#: gui/saveload-dialog.cpp:865 +#, fuzzy +msgid "Name: " +msgstr "Nom:" + +#: gui/saveload-dialog.cpp:937 +#, c-format +msgid "Enter a description for slot %d:" +msgstr "" + #: gui/themebrowser.cpp:44 msgid "Select a Theme" msgstr "Sélectionnez un Thème" -#: gui/ThemeEngine.cpp:335 +#: gui/ThemeEngine.cpp:337 msgid "Disabled GFX" msgstr "GFX désactivé" -#: gui/ThemeEngine.cpp:335 +#: gui/ThemeEngine.cpp:337 msgctxt "lowres" msgid "Disabled GFX" msgstr "GFX désactivé" -#: gui/ThemeEngine.cpp:336 +#: gui/ThemeEngine.cpp:338 msgid "Standard Renderer (16bpp)" msgstr "Rendu Standard (16bpp)" -#: gui/ThemeEngine.cpp:336 +#: gui/ThemeEngine.cpp:338 msgid "Standard (16bpp)" msgstr "Standard (16bpp)" -#: gui/ThemeEngine.cpp:338 +#: gui/ThemeEngine.cpp:340 msgid "Antialiased Renderer (16bpp)" msgstr "Rendu Anti-crénelé (16 bpp)" -#: gui/ThemeEngine.cpp:338 +#: gui/ThemeEngine.cpp:340 msgid "Antialiased (16bpp)" msgstr "Anti-crénelé (16 bpp)" @@ -999,35 +1036,35 @@ msgstr "Anti-crénelé (16 bpp)" msgid "Clear value" msgstr "Effacer la valeur" -#: base/main.cpp:209 +#: base/main.cpp:210 #, c-format msgid "Engine does not support debug level '%s'" msgstr "Le niveau de debug '%s' n'est pas supporté par ce moteur de jeu" -#: base/main.cpp:287 +#: base/main.cpp:288 msgid "Menu" msgstr "Menu" -#: base/main.cpp:290 backends/platform/symbian/src/SymbianActions.cpp:45 +#: base/main.cpp:291 backends/platform/symbian/src/SymbianActions.cpp:45 #: backends/platform/wince/CEActionsPocket.cpp:45 #: backends/platform/wince/CEActionsSmartphone.cpp:46 msgid "Skip" msgstr "Passer" -#: base/main.cpp:293 backends/platform/symbian/src/SymbianActions.cpp:50 +#: base/main.cpp:294 backends/platform/symbian/src/SymbianActions.cpp:50 #: backends/platform/wince/CEActionsPocket.cpp:42 msgid "Pause" msgstr "Mettre en pause" -#: base/main.cpp:296 +#: base/main.cpp:297 msgid "Skip line" msgstr "Passer la phrase" -#: base/main.cpp:467 +#: base/main.cpp:468 msgid "Error running game:" msgstr "Erreur lors de l'éxécution du jeu:" -#: base/main.cpp:491 +#: base/main.cpp:492 msgid "Could not find any engine capable of running the selected game" msgstr "Impossible de trouver un moteur pour exécuter le jeu sélectionné" @@ -1095,18 +1132,18 @@ msgstr "Annuler par l'utilisateur" msgid "Unknown error" msgstr "Erreur inconnue" -#: engines/advancedDetector.cpp:324 +#: engines/advancedDetector.cpp:316 #, c-format msgid "The game in '%s' seems to be unknown." msgstr "Le jeu dans '%s' n'est pas reconnu." -#: engines/advancedDetector.cpp:325 +#: engines/advancedDetector.cpp:317 msgid "Please, report the following data to the ScummVM team along with name" msgstr "" "Veuillez reporter les informations suivantes à l'équipe ScummVM ainsi que le " "nom" -#: engines/advancedDetector.cpp:327 +#: engines/advancedDetector.cpp:319 msgid "of the game you tried to add and its version/language/etc.:" msgstr "du jeu que vous avez essayé d'ajouter, sa version, le langage, etc..." @@ -1144,13 +1181,13 @@ msgid "~R~eturn to Launcher" msgstr "Retour au ~L~anceur" #: engines/dialogs.cpp:115 engines/agi/saveload.cpp:803 -#: engines/cruise/menu.cpp:212 engines/sci/engine/kfile.cpp:735 +#: engines/cruise/menu.cpp:212 engines/sci/engine/kfile.cpp:742 msgid "Save game:" msgstr "Sauvegarde:" #: engines/dialogs.cpp:115 engines/agi/saveload.cpp:803 #: engines/scumm/dialogs.cpp:187 engines/cruise/menu.cpp:212 -#: engines/sci/engine/kfile.cpp:735 +#: engines/sci/engine/kfile.cpp:742 #: backends/platform/symbian/src/SymbianActions.cpp:44 #: backends/platform/wince/CEActionsPocket.cpp:43 #: backends/platform/wince/CEActionsPocket.cpp:267 @@ -1259,23 +1296,23 @@ msgstr "" msgid "Start anyway" msgstr "Jouer quand même" -#: engines/agi/detection.cpp:145 engines/dreamweb/detection.cpp:47 -#: engines/sci/detection.cpp:390 +#: engines/agi/detection.cpp:142 engines/dreamweb/detection.cpp:47 +#: engines/sci/detection.cpp:393 msgid "Use original save/load screens" -msgstr "Utiliser les dialogues sauvegarde/chargement d'origine" +msgstr "Dialogues sauvegarde/chargement d'origine" -#: engines/agi/detection.cpp:146 engines/dreamweb/detection.cpp:48 -#: engines/sci/detection.cpp:391 +#: engines/agi/detection.cpp:143 engines/dreamweb/detection.cpp:48 +#: engines/sci/detection.cpp:394 msgid "Use the original save/load screens, instead of the ScummVM ones" msgstr "" "Utiliser les dialogues sauvegarde/chargement d'origine plutôt que ceux de " "ScummVM" -#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:831 +#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:838 msgid "Restore game:" msgstr "Charger le jeu:" -#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:831 +#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:838 msgid "Restore" msgstr "Charger" @@ -1287,27 +1324,27 @@ msgstr "Utiliser le mode palette lumineuse" msgid "Display graphics using the game's bright palette" msgstr "Utiliser la palette lumineuse du jeu pour l'affichage" -#: engines/sci/detection.cpp:370 +#: engines/sci/detection.cpp:373 msgid "EGA undithering" msgstr "Détramage EGA" -#: engines/sci/detection.cpp:371 +#: engines/sci/detection.cpp:374 msgid "Enable undithering in EGA games" msgstr "Activer le détramage dans les jeux EGA" -#: engines/sci/detection.cpp:380 +#: engines/sci/detection.cpp:383 msgid "Prefer digital sound effects" msgstr "Préférer les effets sonors digitals" -#: engines/sci/detection.cpp:381 +#: engines/sci/detection.cpp:384 msgid "Prefer digital sound effects instead of synthesized ones" msgstr "Préférer les effets sonores digitaux plutôt que ceux synthétisés" -#: engines/sci/detection.cpp:400 +#: engines/sci/detection.cpp:403 msgid "Use IMF/Yamaha FB-01 for MIDI output" msgstr "Utiliser IMF/Yamaha FB-01 pour la sortie MIDI" -#: engines/sci/detection.cpp:401 +#: engines/sci/detection.cpp:404 msgid "" "Use an IBM Music Feature card or a Yamaha FB-01 FM synth module for MIDI " "output" @@ -1315,32 +1352,32 @@ msgstr "" "Utiliser une carte IBM Music Feature ou un module Yamaha FB-01 FM pour la " "sortie MIDI" -#: engines/sci/detection.cpp:411 +#: engines/sci/detection.cpp:414 msgid "Use CD audio" msgstr "Utiliser la musique du CD" -#: engines/sci/detection.cpp:412 +#: engines/sci/detection.cpp:415 msgid "Use CD audio instead of in-game audio, if available" msgstr "" "Utiliser la musique du CD quand elle est disponible au lieu de la musique du " "jeu" -#: engines/sci/detection.cpp:422 +#: engines/sci/detection.cpp:425 msgid "Use Windows cursors" msgstr "Utiliser les curseurs Windows" -#: engines/sci/detection.cpp:423 +#: engines/sci/detection.cpp:426 msgid "" "Use the Windows cursors (smaller and monochrome) instead of the DOS ones" msgstr "" "Utiliser les curseurs Windows (plus petits et monochromes) au lieu des " "curseurs DOS" -#: engines/sci/detection.cpp:433 +#: engines/sci/detection.cpp:436 msgid "Use silver cursors" msgstr "Utiliser les curseurs argentés" -#: engines/sci/detection.cpp:434 +#: engines/sci/detection.cpp:437 msgid "" "Use the alternate set of silver cursors, instead of the normal golden ones" msgstr "Utiliser les curseurs argentés au lieu des curseurs normaux dorés" @@ -1992,7 +2029,7 @@ msgstr "Voler vers la droite" msgid "Fly to lower right" msgstr "Voler vers la bas à droite" -#: engines/scumm/scumm.cpp:1773 +#: engines/scumm/scumm.cpp:1774 #, c-format msgid "" "Native MIDI support requires the Roland Upgrade from LucasArts,\n" @@ -2001,7 +2038,7 @@ msgstr "" "Support MIDI natif requière la mise à jour Roland de LucasArt,\n" "mais %s manque. Utilise AdLib à la place." -#: engines/scumm/scumm.cpp:2278 engines/agos/saveload.cpp:202 +#: engines/scumm/scumm.cpp:2295 engines/agos/saveload.cpp:220 #, c-format msgid "" "Failed to save game state to file:\n" @@ -2012,7 +2049,7 @@ msgstr "" "\n" "%s" -#: engines/scumm/scumm.cpp:2285 engines/agos/saveload.cpp:167 +#: engines/scumm/scumm.cpp:2302 engines/agos/saveload.cpp:185 #, c-format msgid "" "Failed to load game state from file:\n" @@ -2023,7 +2060,7 @@ msgstr "" "\n" "%s" -#: engines/scumm/scumm.cpp:2297 engines/agos/saveload.cpp:210 +#: engines/scumm/scumm.cpp:2314 engines/agos/saveload.cpp:228 #, c-format msgid "" "Successfully saved game state in file:\n" @@ -2034,7 +2071,7 @@ msgstr "" "\n" "%s" -#: engines/scumm/scumm.cpp:2512 +#: engines/scumm/scumm.cpp:2529 msgid "" "Usually, Maniac Mansion would start now. But ScummVM doesn't do that yet. To " "play it, go to 'Add Game' in the ScummVM start menu and select the 'Maniac' " @@ -2071,17 +2108,17 @@ msgstr "~M~enu Principal" msgid "~W~ater Effect Enabled" msgstr "~E~ffets de l'Eau Activés" -#: engines/agos/animation.cpp:560 +#: engines/agos/animation.cpp:557 #, c-format msgid "Cutscene file '%s' not found!" msgstr "Fichier de séquence '%s' non trouvé!" #: engines/gob/inter_playtoons.cpp:256 engines/gob/inter_v2.cpp:1287 -#: engines/tinsel/saveload.cpp:502 +#: engines/tinsel/saveload.cpp:532 msgid "Failed to load game state from file." msgstr "Échec du chargement de l'état du jeu depuis le disque." -#: engines/gob/inter_v2.cpp:1357 engines/tinsel/saveload.cpp:515 +#: engines/gob/inter_v2.cpp:1357 engines/tinsel/saveload.cpp:545 msgid "Failed to save game state to file." msgstr "Échec de l'enregistrement de l'état du jeu sur le disque." @@ -2199,7 +2236,7 @@ msgstr "Options" msgid "Choose Spell" msgstr "Choisir un Sort" -#: engines/kyra/sound_midi.cpp:475 +#: engines/kyra/sound_midi.cpp:477 msgid "" "You appear to be using a General MIDI device,\n" "but your game only supports Roland MT32 MIDI.\n" @@ -2210,15 +2247,16 @@ msgstr "" "Il semble que vous utilisiez un périphérique General MIDI,\n" "mais ce jeu ne support que le MIDI Roland MT32. Nous essayons\n" "d'associer les instruments Roland MT32 aux instruments General\n" -"MIDI. Cependant il est possible que quelques pistes ne soient\n " -"pas jouées correctement." +"MIDI. Cependant il est possible que quelques pistes ne soient\n" +" pas jouées correctement." -#: engines/queen/queen.cpp:59 engines/sky/detection.cpp:44 -msgid "Floppy intro" -msgstr "Intro disquette" +#: engines/queen/queen.cpp:59 +msgid "Alternative intro" +msgstr "" -#: engines/queen/queen.cpp:60 engines/sky/detection.cpp:45 -msgid "Use the floppy version's intro (CD version only)" +#: engines/queen/queen.cpp:60 +#, fuzzy +msgid "Use an alternative game intro (CD version only)" msgstr "Utiliser l'intro de la version disquette (version CD uniquement)" #: engines/sky/compact.cpp:130 @@ -2237,28 +2275,36 @@ msgstr "" "Le fichier \"sky.cpt\" a une taille incorrecte.\n" "Vous pouvez le (re)télécharger sur www.scummvm.org" -#: engines/sword1/animation.cpp:539 +#: engines/sky/detection.cpp:44 +msgid "Floppy intro" +msgstr "Intro disquette" + +#: engines/sky/detection.cpp:45 +msgid "Use the floppy version's intro (CD version only)" +msgstr "Utiliser l'intro de la version disquette (version CD uniquement)" + +#: engines/sword1/animation.cpp:519 #, c-format msgid "PSX stream cutscene '%s' cannot be played in paletted mode" msgstr "" "La scène cinématique PSX '%s' ne peut pas être lu avec 256 couleurs ou moins" -#: engines/sword1/animation.cpp:560 engines/sword2/animation.cpp:455 +#: engines/sword1/animation.cpp:540 engines/sword2/animation.cpp:439 msgid "DXA cutscenes found but ScummVM has been built without zlib support" msgstr "" "Les séquences DXA sont présente mais ScummVM a été compilé sans le support " "zlib." -#: engines/sword1/animation.cpp:570 engines/sword2/animation.cpp:465 +#: engines/sword1/animation.cpp:550 engines/sword2/animation.cpp:449 msgid "MPEG2 cutscenes are no longer supported" msgstr "Les séquences MPEG2 ne sont plus supportées" -#: engines/sword1/animation.cpp:576 engines/sword2/animation.cpp:473 +#: engines/sword1/animation.cpp:556 engines/sword2/animation.cpp:457 #, c-format msgid "Cutscene '%s' not found" msgstr "Séquence '%s' non trouvé" -#: engines/sword1/control.cpp:863 +#: engines/sword1/control.cpp:865 msgid "" "ScummVM found that you have old savefiles for Broken Sword 1 that should be " "converted.\n" @@ -2276,7 +2322,7 @@ msgstr "" "Appuyer sur OK pour les convertir maintenant, sinon le même message " "s'affichera la prochaine fois que vous démarrerez le jeu.\n" -#: engines/sword1/control.cpp:1232 +#: engines/sword1/control.cpp:1234 #, c-format msgid "" "Target new save game already exists!\n" @@ -2285,11 +2331,11 @@ msgstr "" "La sauvegarde cible existe déjà!\n" "Voulez-vous conserver l'ancienne sauvegarde (%s) ou la nouvelle (%s)?\n" -#: engines/sword1/control.cpp:1235 +#: engines/sword1/control.cpp:1237 msgid "Keep the old one" msgstr "Garde l'ancienne" -#: engines/sword1/control.cpp:1235 +#: engines/sword1/control.cpp:1237 msgid "Keep the new one" msgstr "Garder la nouvelle" @@ -2297,7 +2343,7 @@ msgstr "Garder la nouvelle" msgid "This is the end of the Broken Sword 1 Demo" msgstr "C'est la fin de la démo des Chevaliers de Baphomet" -#: engines/sword2/animation.cpp:435 +#: engines/sword2/animation.cpp:419 msgid "" "PSX cutscenes found but ScummVM has been built without RGB color support" msgstr "" @@ -2312,6 +2358,17 @@ msgstr "Afficher la description des objets" msgid "Show labels for objects on mouse hover" msgstr "Afficher la description des objets lors de passage du pointeur" +#: engines/teenagent/resources.cpp:94 +msgid "" +"You're missing the 'teenagent.dat' file. Get it from the ScummVM website" +msgstr "" + +#: engines/teenagent/resources.cpp:115 +msgid "" +"The teenagent.dat file is compressed and zlib hasn't been included in this " +"executable. Please decompress it" +msgstr "" + #: engines/parallaction/saveload.cpp:133 #, c-format msgid "" @@ -2417,7 +2474,7 @@ msgstr "Pas de musique" msgid "Amiga Audio Emulator" msgstr "Émulateur Amiga Audio" -#: audio/softsynth/adlib.cpp:1593 +#: audio/softsynth/adlib.cpp:2284 msgid "AdLib Emulator" msgstr "Émulateur AdLib" @@ -2561,11 +2618,11 @@ msgstr "Mode touchpad activé" msgid "Touchpad mode disabled." msgstr "Mode touchpad désactivé" -#: backends/platform/maemo/maemo.cpp:205 +#: backends/platform/maemo/maemo.cpp:209 msgid "Click Mode" msgstr "Mode Clic" -#: backends/platform/maemo/maemo.cpp:211 +#: backends/platform/maemo/maemo.cpp:215 #: backends/platform/symbian/src/SymbianActions.cpp:42 #: backends/platform/wince/CEActionsPocket.cpp:60 #: backends/platform/wince/CEActionsSmartphone.cpp:43 @@ -2573,35 +2630,35 @@ msgstr "Mode Clic" msgid "Left Click" msgstr "Clic Gauche" -#: backends/platform/maemo/maemo.cpp:214 +#: backends/platform/maemo/maemo.cpp:218 msgid "Middle Click" msgstr "Clic Milieu" -#: backends/platform/maemo/maemo.cpp:217 +#: backends/platform/maemo/maemo.cpp:221 #: backends/platform/symbian/src/SymbianActions.cpp:43 #: backends/platform/wince/CEActionsSmartphone.cpp:44 #: backends/platform/bada/form.cpp:273 msgid "Right Click" msgstr "Clic Droit" -#: backends/platform/sdl/macosx/appmenu_osx.mm:78 +#: backends/platform/sdl/macosx/appmenu_osx.mm:77 msgid "Hide ScummVM" msgstr "Masquer ScummVM" -#: backends/platform/sdl/macosx/appmenu_osx.mm:83 +#: backends/platform/sdl/macosx/appmenu_osx.mm:82 msgid "Hide Others" msgstr "Masquer les autres" -#: backends/platform/sdl/macosx/appmenu_osx.mm:88 +#: backends/platform/sdl/macosx/appmenu_osx.mm:87 msgid "Show All" msgstr "Tout afficher" -#: backends/platform/sdl/macosx/appmenu_osx.mm:110 -#: backends/platform/sdl/macosx/appmenu_osx.mm:121 +#: backends/platform/sdl/macosx/appmenu_osx.mm:109 +#: backends/platform/sdl/macosx/appmenu_osx.mm:120 msgid "Window" msgstr "Fenêtre" -#: backends/platform/sdl/macosx/appmenu_osx.mm:115 +#: backends/platform/sdl/macosx/appmenu_osx.mm:114 msgid "Minimize" msgstr "Placer dans le Dock" @@ -2983,41 +3040,46 @@ msgstr "Lanceur" msgid "Do you really want to quit?" msgstr "Voulez-vous vraiment quitter?" -#: backends/events/gph/gph-events.cpp:338 -#: backends/events/gph/gph-events.cpp:381 -#: backends/events/openpandora/op-events.cpp:139 +#: backends/events/gph/gph-events.cpp:386 +#: backends/events/gph/gph-events.cpp:429 +#: backends/events/openpandora/op-events.cpp:168 msgid "Touchscreen 'Tap Mode' - Left Click" msgstr "Touchscreen 'Tap Mode' - Clic Gauche" -#: backends/events/gph/gph-events.cpp:340 -#: backends/events/gph/gph-events.cpp:383 -#: backends/events/openpandora/op-events.cpp:141 +#: backends/events/gph/gph-events.cpp:388 +#: backends/events/gph/gph-events.cpp:431 +#: backends/events/openpandora/op-events.cpp:170 msgid "Touchscreen 'Tap Mode' - Right Click" msgstr "Touchscreen 'Tap Mode' - Clic Droit" -#: backends/events/gph/gph-events.cpp:342 -#: backends/events/gph/gph-events.cpp:385 -#: backends/events/openpandora/op-events.cpp:143 +#: backends/events/gph/gph-events.cpp:390 +#: backends/events/gph/gph-events.cpp:433 +#: backends/events/openpandora/op-events.cpp:172 msgid "Touchscreen 'Tap Mode' - Hover (No Click)" msgstr "Touchscreen 'Tap Mode' - Déplacer sans cliquer" -#: backends/events/gph/gph-events.cpp:362 +#: backends/events/gph/gph-events.cpp:410 msgid "Maximum Volume" msgstr "Volume Maximum" -#: backends/events/gph/gph-events.cpp:364 +#: backends/events/gph/gph-events.cpp:412 msgid "Increasing Volume" msgstr "Augmentation Volume" -#: backends/events/gph/gph-events.cpp:370 +#: backends/events/gph/gph-events.cpp:418 msgid "Minimal Volume" msgstr "Volume Minimum" -#: backends/events/gph/gph-events.cpp:372 +#: backends/events/gph/gph-events.cpp:420 msgid "Decreasing Volume" msgstr "Diminution Volume" -#: backends/updates/macosx/macosx-updates.mm:65 +#: backends/events/openpandora/op-events.cpp:174 +#, fuzzy +msgid "Touchscreen 'Tap Mode' - Hover (DPad Clicks)" +msgstr "Touchscreen 'Tap Mode' - Déplacer sans cliquer" + +#: backends/updates/macosx/macosx-updates.mm:67 msgid "Check for Updates..." msgstr "Recherche des mises à jour..." diff --git a/po/gl_ES.po b/po/gl_ES.po new file mode 100644 index 0000000000..a650ff338d --- /dev/null +++ b/po/gl_ES.po @@ -0,0 +1,3097 @@ +# LANGUAGE translation for ScummVM. +# Copyright (C) 2010-2013 ScummVM Team +# This file is distributed under the same license as the ScummVM package. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: ScummVM 1.6.0git\n" +"Report-Msgid-Bugs-To: scummvm-devel@lists.sf.net\n" +"POT-Creation-Date: 2012-12-01 17:22+0000\n" +"PO-Revision-Date: 2012-08-15 13:33+0100\n" +"Last-Translator: Santiago G. Sanz <s.sanz@uvigo.es>\n" +"Language-Team: \n" +"Language: Galego\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=iso-8859-1\n" +"Content-Transfer-Encoding: 8bit\n" + +#: gui/about.cpp:91 +#, c-format +msgid "(built on %s)" +msgstr "(compilado o %s)" + +#: gui/about.cpp:98 +msgid "Features compiled in:" +msgstr "Funcionalidades compiladas:" + +#: gui/about.cpp:107 +msgid "Available engines:" +msgstr "Motores dispoñibles:" + +#: gui/browser.cpp:66 +msgid "Go up" +msgstr "Arriba" + +#: gui/browser.cpp:66 gui/browser.cpp:68 +msgid "Go to previous directory level" +msgstr "Ir ao directorio superior" + +#: gui/browser.cpp:68 +msgctxt "lowres" +msgid "Go up" +msgstr "Arriba" + +#: gui/browser.cpp:69 gui/chooser.cpp:45 gui/KeysDialog.cpp:43 +#: gui/launcher.cpp:345 gui/massadd.cpp:94 gui/options.cpp:1228 +#: gui/saveload-dialog.cpp:215 gui/saveload-dialog.cpp:275 +#: gui/saveload-dialog.cpp:545 gui/saveload-dialog.cpp:919 +#: gui/themebrowser.cpp:54 engines/engine.cpp:442 +#: engines/scumm/dialogs.cpp:190 engines/sword1/control.cpp:867 +#: engines/parallaction/saveload.cpp:274 backends/platform/wii/options.cpp:48 +#: backends/events/default/default-events.cpp:191 +#: backends/events/default/default-events.cpp:213 +msgid "Cancel" +msgstr "Cancelar" + +#: gui/browser.cpp:70 gui/chooser.cpp:46 gui/themebrowser.cpp:55 +msgid "Choose" +msgstr "Elexir" + +#: gui/gui-manager.cpp:115 engines/scumm/help.cpp:125 +#: engines/scumm/help.cpp:140 engines/scumm/help.cpp:165 +#: engines/scumm/help.cpp:191 engines/scumm/help.cpp:209 +#: backends/keymapper/remap-dialog.cpp:52 +msgid "Close" +msgstr "Pechar" + +#: gui/gui-manager.cpp:118 +msgid "Mouse click" +msgstr "Premer co rato" + +#: gui/gui-manager.cpp:122 base/main.cpp:301 +msgid "Display keyboard" +msgstr "Mostrar teclado" + +#: gui/gui-manager.cpp:126 base/main.cpp:305 +msgid "Remap keys" +msgstr "Asignar teclas" + +#: gui/gui-manager.cpp:129 base/main.cpp:308 +msgid "Toggle FullScreen" +msgstr "Activar/desactivar pantalla completa" + +#: gui/KeysDialog.h:36 gui/KeysDialog.cpp:145 +msgid "Choose an action to map" +msgstr "Elixe unha acción para asignala" + +#: gui/KeysDialog.cpp:41 +msgid "Map" +msgstr "Asignar" + +#: gui/KeysDialog.cpp:42 gui/launcher.cpp:346 gui/launcher.cpp:1001 +#: gui/launcher.cpp:1005 gui/massadd.cpp:91 gui/options.cpp:1229 +#: gui/saveload-dialog.cpp:920 engines/engine.cpp:361 engines/engine.cpp:372 +#: engines/scumm/dialogs.cpp:192 engines/scumm/scumm.cpp:1776 +#: engines/agos/animation.cpp:558 engines/groovie/script.cpp:420 +#: engines/sky/compact.cpp:131 engines/sky/compact.cpp:141 +#: engines/sword1/animation.cpp:519 engines/sword1/animation.cpp:540 +#: engines/sword1/animation.cpp:550 engines/sword1/animation.cpp:557 +#: engines/sword1/control.cpp:867 engines/sword1/logic.cpp:1633 +#: engines/sword2/animation.cpp:419 engines/sword2/animation.cpp:439 +#: engines/sword2/animation.cpp:449 engines/sword2/animation.cpp:458 +#: engines/parallaction/saveload.cpp:274 backends/platform/wii/options.cpp:47 +#: backends/platform/wince/CELauncherDialog.cpp:54 +msgid "OK" +msgstr "Aceptar" + +#: gui/KeysDialog.cpp:49 +msgid "Select an action and click 'Map'" +msgstr "Selecciona unha acción e preme en Asignar" + +#: gui/KeysDialog.cpp:80 gui/KeysDialog.cpp:102 gui/KeysDialog.cpp:141 +#, c-format +msgid "Associated key : %s" +msgstr "Tecla asociada: %s" + +#: gui/KeysDialog.cpp:82 gui/KeysDialog.cpp:104 gui/KeysDialog.cpp:143 +#, c-format +msgid "Associated key : none" +msgstr "Tecla asociada: ningunha" + +#: gui/KeysDialog.cpp:90 +msgid "Please select an action" +msgstr "Selecciona unha acción" + +#: gui/KeysDialog.cpp:106 +msgid "Press the key to associate" +msgstr "Preme a tecla para asociala" + +#: gui/launcher.cpp:187 +msgid "Game" +msgstr "Xogo" + +#: gui/launcher.cpp:191 +msgid "ID:" +msgstr "ID:" + +#: gui/launcher.cpp:191 gui/launcher.cpp:193 gui/launcher.cpp:194 +msgid "" +"Short game identifier used for referring to savegames and running the game " +"from the command line" +msgstr "" +"Identificador curto do xogo para os ficheiros de gardado e a execución do " +"xogo dende a liña de comandos" + +#: gui/launcher.cpp:193 +msgctxt "lowres" +msgid "ID:" +msgstr "ID:" + +#: gui/launcher.cpp:198 +msgid "Name:" +msgstr "Nome:" + +#: gui/launcher.cpp:198 gui/launcher.cpp:200 gui/launcher.cpp:201 +msgid "Full title of the game" +msgstr "Título completo do xogo" + +#: gui/launcher.cpp:200 +msgctxt "lowres" +msgid "Name:" +msgstr "Nome:" + +#: gui/launcher.cpp:204 +msgid "Language:" +msgstr "Idioma:" + +#: gui/launcher.cpp:204 gui/launcher.cpp:205 +msgid "" +"Language of the game. This will not turn your Spanish game version into " +"English" +msgstr "Idioma do xogo. Non converterá a versión galega do xogo en inglesa" + +#: gui/launcher.cpp:206 gui/launcher.cpp:220 gui/options.cpp:80 +#: gui/options.cpp:730 gui/options.cpp:743 gui/options.cpp:1199 +#: audio/null.cpp:40 +msgid "<default>" +msgstr "<por defecto>" + +#: gui/launcher.cpp:216 +msgid "Platform:" +msgstr "Plataforma:" + +#: gui/launcher.cpp:216 gui/launcher.cpp:218 gui/launcher.cpp:219 +msgid "Platform the game was originally designed for" +msgstr "Plataforma para a que se desenvolvera o xogo inicialmente" + +#: gui/launcher.cpp:218 +msgctxt "lowres" +msgid "Platform:" +msgstr "Plataforma:" + +#: gui/launcher.cpp:231 +msgid "Engine" +msgstr "Motor" + +#: gui/launcher.cpp:239 gui/options.cpp:1062 gui/options.cpp:1079 +msgid "Graphics" +msgstr "Gráficos" + +#: gui/launcher.cpp:239 gui/options.cpp:1062 gui/options.cpp:1079 +msgid "GFX" +msgstr "Efectos gráficos" + +#: gui/launcher.cpp:242 +msgid "Override global graphic settings" +msgstr "Anular a configuración dos gráficos" + +#: gui/launcher.cpp:244 +msgctxt "lowres" +msgid "Override global graphic settings" +msgstr "Anular a configuración dos gráficos" + +#: gui/launcher.cpp:251 gui/options.cpp:1085 +msgid "Audio" +msgstr "Son" + +#: gui/launcher.cpp:254 +msgid "Override global audio settings" +msgstr "Anular a configuración do son" + +#: gui/launcher.cpp:256 +msgctxt "lowres" +msgid "Override global audio settings" +msgstr "Anular a configuración do son" + +#: gui/launcher.cpp:265 gui/options.cpp:1090 +msgid "Volume" +msgstr "Volume" + +#: gui/launcher.cpp:267 gui/options.cpp:1092 +msgctxt "lowres" +msgid "Volume" +msgstr "Volume" + +#: gui/launcher.cpp:270 +msgid "Override global volume settings" +msgstr "Anular a configuración do volume" + +#: gui/launcher.cpp:272 +msgctxt "lowres" +msgid "Override global volume settings" +msgstr "Anular a configuración do volume" + +#: gui/launcher.cpp:280 gui/options.cpp:1100 +msgid "MIDI" +msgstr "MIDI" + +#: gui/launcher.cpp:283 +msgid "Override global MIDI settings" +msgstr "Anular a configuración de MIDI" + +#: gui/launcher.cpp:285 +msgctxt "lowres" +msgid "Override global MIDI settings" +msgstr "Anular a configuración de MIDI" + +#: gui/launcher.cpp:294 gui/options.cpp:1106 +msgid "MT-32" +msgstr "MT-32" + +#: gui/launcher.cpp:297 +msgid "Override global MT-32 settings" +msgstr "Anular a configuración de MT-32" + +#: gui/launcher.cpp:299 +msgctxt "lowres" +msgid "Override global MT-32 settings" +msgstr "Anular a configuración de MT-32" + +#: gui/launcher.cpp:308 gui/options.cpp:1113 +msgid "Paths" +msgstr "Camiños" + +#: gui/launcher.cpp:310 gui/options.cpp:1115 +msgctxt "lowres" +msgid "Paths" +msgstr "Camiños" + +#: gui/launcher.cpp:317 +msgid "Game Path:" +msgstr "Camiño do xogo:" + +#: gui/launcher.cpp:319 +msgctxt "lowres" +msgid "Game Path:" +msgstr "Camiño do xogo:" + +#: gui/launcher.cpp:324 gui/options.cpp:1139 +msgid "Extra Path:" +msgstr "Camiño adicional:" + +#: gui/launcher.cpp:324 gui/launcher.cpp:326 gui/launcher.cpp:327 +msgid "Specifies path to additional data used the game" +msgstr "Especifica o camiño dos datos adicionais usados no xogo" + +#: gui/launcher.cpp:326 gui/options.cpp:1141 +msgctxt "lowres" +msgid "Extra Path:" +msgstr "Camiño adicional:" + +#: gui/launcher.cpp:333 gui/options.cpp:1123 +msgid "Save Path:" +msgstr "Camiño de gardado:" + +#: gui/launcher.cpp:333 gui/launcher.cpp:335 gui/launcher.cpp:336 +#: gui/options.cpp:1123 gui/options.cpp:1125 gui/options.cpp:1126 +msgid "Specifies where your savegames are put" +msgstr "Especifica o lugar dos ficheiros de gardado" + +#: gui/launcher.cpp:335 gui/options.cpp:1125 +msgctxt "lowres" +msgid "Save Path:" +msgstr "Camiño de gardado:" + +#: gui/launcher.cpp:354 gui/launcher.cpp:453 gui/launcher.cpp:511 +#: gui/launcher.cpp:565 gui/options.cpp:1134 gui/options.cpp:1142 +#: gui/options.cpp:1151 gui/options.cpp:1258 gui/options.cpp:1264 +#: gui/options.cpp:1272 gui/options.cpp:1302 gui/options.cpp:1308 +#: gui/options.cpp:1315 gui/options.cpp:1408 gui/options.cpp:1411 +#: gui/options.cpp:1423 +msgctxt "path" +msgid "None" +msgstr "Ningún" + +#: gui/launcher.cpp:359 gui/launcher.cpp:459 gui/launcher.cpp:569 +#: gui/options.cpp:1252 gui/options.cpp:1296 gui/options.cpp:1414 +#: backends/platform/wii/options.cpp:56 +msgid "Default" +msgstr "Predefinido" + +#: gui/launcher.cpp:504 gui/options.cpp:1417 +msgid "Select SoundFont" +msgstr "Seleccionar SoundFont" + +#: gui/launcher.cpp:523 gui/launcher.cpp:677 +msgid "Select directory with game data" +msgstr "Selecciona un directorio con datos de xogo" + +#: gui/launcher.cpp:541 +msgid "Select additional game directory" +msgstr "Selecciona un directorio con datos adicionais" + +#: gui/launcher.cpp:553 +msgid "Select directory for saved games" +msgstr "Selecciona un directorio para ficheiros de gardado" + +#: gui/launcher.cpp:580 +msgid "This game ID is already taken. Please choose another one." +msgstr "Este ID de xogo xa está en uso. Selecciona outro." + +#: gui/launcher.cpp:621 engines/dialogs.cpp:110 +msgid "~Q~uit" +msgstr "~S~aír" + +#: gui/launcher.cpp:621 backends/platform/sdl/macosx/appmenu_osx.mm:95 +msgid "Quit ScummVM" +msgstr "Saír de ScummVM" + +#: gui/launcher.cpp:622 +msgid "A~b~out..." +msgstr "Ace~r~ca de..." + +#: gui/launcher.cpp:622 backends/platform/sdl/macosx/appmenu_osx.mm:69 +msgid "About ScummVM" +msgstr "Acerca de ScummVM" + +#: gui/launcher.cpp:623 +msgid "~O~ptions..." +msgstr "~O~pcións..." + +#: gui/launcher.cpp:623 +msgid "Change global ScummVM options" +msgstr "Cambiar as opcións de ScummVM" + +#: gui/launcher.cpp:625 +msgid "~S~tart" +msgstr "~I~niciar" + +#: gui/launcher.cpp:625 +msgid "Start selected game" +msgstr "Iniciar o xogo seleccionado" + +#: gui/launcher.cpp:628 +msgid "~L~oad..." +msgstr "~C~argar..." + +#: gui/launcher.cpp:628 +msgid "Load savegame for selected game" +msgstr "Cargar partida do xogo seleccionado" + +#: gui/launcher.cpp:633 gui/launcher.cpp:1120 +msgid "~A~dd Game..." +msgstr "Eng~a~dir xogo..." + +#: gui/launcher.cpp:633 gui/launcher.cpp:640 +msgid "Hold Shift for Mass Add" +msgstr "Manter premido MAIÚS para engadir en masa" + +#: gui/launcher.cpp:635 +msgid "~E~dit Game..." +msgstr "~E~ditar xogo..." + +#: gui/launcher.cpp:635 gui/launcher.cpp:642 +msgid "Change game options" +msgstr "Cambiar as opcións do xogo" + +#: gui/launcher.cpp:637 +msgid "~R~emove Game" +msgstr "Elimina~r~ xogo" + +#: gui/launcher.cpp:637 gui/launcher.cpp:644 +msgid "Remove game from the list. The game data files stay intact" +msgstr "Eliminar o xogo da lista. Os ficheiros de datos non se modifican" + +#: gui/launcher.cpp:640 gui/launcher.cpp:1120 +msgctxt "lowres" +msgid "~A~dd Game..." +msgstr "Eng~a~dir xogo..." + +#: gui/launcher.cpp:642 +msgctxt "lowres" +msgid "~E~dit Game..." +msgstr "~E~ditar xogo..." + +#: gui/launcher.cpp:644 +msgctxt "lowres" +msgid "~R~emove Game" +msgstr "Elimina~r~ xogo" + +#: gui/launcher.cpp:652 +msgid "Search in game list" +msgstr "Buscar na lista de xogos" + +#: gui/launcher.cpp:656 gui/launcher.cpp:1167 +msgid "Search:" +msgstr "Buscar:" + +#: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/mohawk/myst.cpp:245 +#: engines/mohawk/riven.cpp:716 engines/cruise/menu.cpp:214 +msgid "Load game:" +msgstr "Cargar partida:" + +#: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/scumm/dialogs.cpp:188 +#: engines/mohawk/myst.cpp:245 engines/mohawk/riven.cpp:716 +#: engines/cruise/menu.cpp:214 backends/platform/wince/CEActionsPocket.cpp:267 +#: backends/platform/wince/CEActionsSmartphone.cpp:231 +msgid "Load" +msgstr "Cargar" + +#: gui/launcher.cpp:788 +msgid "" +"Do you really want to run the mass game detector? This could potentially add " +"a huge number of games." +msgstr "" +"Queres executar o detector de xogos en masa? É posible que se engada un gran " +"número de xogos." + +#: gui/launcher.cpp:789 gui/launcher.cpp:937 +#: backends/events/symbiansdl/symbiansdl-events.cpp:184 +#: backends/platform/wince/CEActionsPocket.cpp:326 +#: backends/platform/wince/CEActionsSmartphone.cpp:287 +#: backends/platform/wince/CELauncherDialog.cpp:83 +msgid "Yes" +msgstr "Si" + +#: gui/launcher.cpp:789 gui/launcher.cpp:937 +#: backends/events/symbiansdl/symbiansdl-events.cpp:184 +#: backends/platform/wince/CEActionsPocket.cpp:326 +#: backends/platform/wince/CEActionsSmartphone.cpp:287 +#: backends/platform/wince/CELauncherDialog.cpp:83 +msgid "No" +msgstr "Non" + +#: gui/launcher.cpp:837 +msgid "ScummVM couldn't open the specified directory!" +msgstr "ScummVM non foi quen de abrir o directorio!" + +#: gui/launcher.cpp:849 +msgid "ScummVM could not find any game in the specified directory!" +msgstr "ScummVM non foi quen de atopar xogos no directorio!" + +#: gui/launcher.cpp:863 +msgid "Pick the game:" +msgstr "Elixe o xogo:" + +#: gui/launcher.cpp:937 +msgid "Do you really want to remove this game configuration?" +msgstr "Seguro que queres eliminar esta configuración de xogo?" + +#: gui/launcher.cpp:1001 +msgid "This game does not support loading games from the launcher." +msgstr "O xogo non permite cargar partidas dende o iniciador." + +#: gui/launcher.cpp:1005 +msgid "ScummVM could not find any engine capable of running the selected game!" +msgstr "ScummVM non foi quen de atopar un motor para executar o xogo!" + +#: gui/launcher.cpp:1119 +msgctxt "lowres" +msgid "Mass Add..." +msgstr "Engadir en masa..." + +#: gui/launcher.cpp:1119 +msgid "Mass Add..." +msgstr "Engadir en masa..." + +#: gui/massadd.cpp:78 gui/massadd.cpp:81 +msgid "... progress ..." +msgstr "...progreso..." + +#: gui/massadd.cpp:258 +msgid "Scan complete!" +msgstr "Análise finalizada!" + +#: gui/massadd.cpp:261 +#, c-format +msgid "Discovered %d new games, ignored %d previously added games." +msgstr "%d xogos novos atopados; %d xogos xa engadidos ignorados." + +#: gui/massadd.cpp:265 +#, c-format +msgid "Scanned %d directories ..." +msgstr "%d directorios analizados..." + +#: gui/massadd.cpp:268 +#, c-format +msgid "Discovered %d new games, ignored %d previously added games ..." +msgstr "%d xogos novos atopados; %d xogos xa engadidos ignorados..." + +#: gui/options.cpp:78 +msgid "Never" +msgstr "Nunca" + +#: gui/options.cpp:78 +msgid "every 5 mins" +msgstr "cada 5 min" + +#: gui/options.cpp:78 +msgid "every 10 mins" +msgstr "cada 10 min" + +#: gui/options.cpp:78 +msgid "every 15 mins" +msgstr "cada 15 min" + +#: gui/options.cpp:78 +msgid "every 30 mins" +msgstr "cada 30 min" + +#: gui/options.cpp:80 +msgid "8 kHz" +msgstr "8 kHz" + +#: gui/options.cpp:80 +msgid "11kHz" +msgstr "11 kHz" + +#: gui/options.cpp:80 +msgid "22 kHz" +msgstr "22 kHz" + +#: gui/options.cpp:80 +msgid "44 kHz" +msgstr "44 kHz" + +#: gui/options.cpp:80 +msgid "48 kHz" +msgstr "48 kHz" + +#: gui/options.cpp:248 gui/options.cpp:474 gui/options.cpp:575 +#: gui/options.cpp:644 gui/options.cpp:852 +msgctxt "soundfont" +msgid "None" +msgstr "Ningunha" + +#: gui/options.cpp:382 +msgid "Failed to apply some of the graphic options changes:" +msgstr "Erro ao aplicar os cambios na configuración dos gráficos:" + +#: gui/options.cpp:394 +msgid "the video mode could not be changed." +msgstr "non se puido cambiar o modo de vídeo." + +#: gui/options.cpp:400 +msgid "the fullscreen setting could not be changed" +msgstr "non se puido cambiar a configuración de pantalla completa." + +#: gui/options.cpp:406 +msgid "the aspect ratio setting could not be changed" +msgstr "non se puido cambiar a proporción." + +#: gui/options.cpp:727 +msgid "Graphics mode:" +msgstr "Modo de gráficos:" + +#: gui/options.cpp:741 +msgid "Render mode:" +msgstr "Modo de procesamento:" + +#: gui/options.cpp:741 gui/options.cpp:742 +msgid "Special dithering modes supported by some games" +msgstr "Modos de interpolación de cores compatibles con algúns xogos" + +#: gui/options.cpp:753 +#: backends/graphics/surfacesdl/surfacesdl-graphics.cpp:2236 +#: backends/graphics/openglsdl/openglsdl-graphics.cpp:472 +msgid "Fullscreen mode" +msgstr "Pantalla completa" + +#: gui/options.cpp:756 +msgid "Aspect ratio correction" +msgstr "Corrección de proporción" + +#: gui/options.cpp:756 +msgid "Correct aspect ratio for 320x200 games" +msgstr "Corrixir a proporción para os xogos en 320x200" + +#: gui/options.cpp:764 +msgid "Preferred Device:" +msgstr "Dispositivo preferido:" + +#: gui/options.cpp:764 +msgid "Music Device:" +msgstr "Dispositivo de música:" + +#: gui/options.cpp:764 gui/options.cpp:766 +msgid "Specifies preferred sound device or sound card emulator" +msgstr "Especifica o dispositivo ou emulador de tarxeta de son preferido" + +#: gui/options.cpp:764 gui/options.cpp:766 gui/options.cpp:767 +msgid "Specifies output sound device or sound card emulator" +msgstr "Especifica o dispositivo ou emulador de tarxeta de son de saída" + +#: gui/options.cpp:766 +msgctxt "lowres" +msgid "Preferred Dev.:" +msgstr "Disp. preferido:" + +#: gui/options.cpp:766 +msgctxt "lowres" +msgid "Music Device:" +msgstr "Disp. música:" + +#: gui/options.cpp:793 +msgid "AdLib emulator:" +msgstr "Emulador de AdLib:" + +#: gui/options.cpp:793 gui/options.cpp:794 +msgid "AdLib is used for music in many games" +msgstr "Moitos xogos empregan AdLib para a música" + +#: gui/options.cpp:804 +msgid "Output rate:" +msgstr "Taxa de saída:" + +#: gui/options.cpp:804 gui/options.cpp:805 +msgid "" +"Higher value specifies better sound quality but may be not supported by your " +"soundcard" +msgstr "" +"A maior valor, maior calidade do son, mais talvez non sexa compatible coa " +"tarxeta" + +#: gui/options.cpp:815 +msgid "GM Device:" +msgstr "Dispositivo de GM:" + +#: gui/options.cpp:815 +msgid "Specifies default sound device for General MIDI output" +msgstr "" +"Especifica o dispositivo de son por defecto para a saída de General MIDI" + +#: gui/options.cpp:826 +msgid "Don't use General MIDI music" +msgstr "Non empregar música en General MIDI" + +#: gui/options.cpp:837 gui/options.cpp:899 +msgid "Use first available device" +msgstr "Empregar o primeiro dispositivo dispoñible" + +#: gui/options.cpp:849 +msgid "SoundFont:" +msgstr "SoundFont:" + +#: gui/options.cpp:849 gui/options.cpp:851 gui/options.cpp:852 +msgid "SoundFont is supported by some audio cards, Fluidsynth and Timidity" +msgstr "" +"SoundFont é compatible con algunhas tarxetas de son, Fluidsynth e Timidity" + +#: gui/options.cpp:851 +msgctxt "lowres" +msgid "SoundFont:" +msgstr "SoundFont:" + +#: gui/options.cpp:857 +msgid "Mixed AdLib/MIDI mode" +msgstr "Modo AdLib/MIDI mixto" + +#: gui/options.cpp:857 +msgid "Use both MIDI and AdLib sound generation" +msgstr "Empregar xeración de son MIDI e máis AdLib" + +#: gui/options.cpp:860 +msgid "MIDI gain:" +msgstr "Ganancia de MIDI:" + +#: gui/options.cpp:870 +msgid "MT-32 Device:" +msgstr "Dispositivo de MT-32:" + +#: gui/options.cpp:870 +msgid "Specifies default sound device for Roland MT-32/LAPC1/CM32l/CM64 output" +msgstr "" +"Especifica o dispositivo por defecto para a saída de Roland MT-32/LAPC1/" +"CM32l/CM64" + +#: gui/options.cpp:875 +msgid "True Roland MT-32 (disable GM emulation)" +msgstr "Roland MT-32 verdadeiro (sen emulación de GM)" + +#: gui/options.cpp:875 gui/options.cpp:877 +msgid "" +"Check if you want to use your real hardware Roland-compatible sound device " +"connected to your computer" +msgstr "" +"Marcar para empregar o hardware compatible con Roland conectado ao sistema" + +#: gui/options.cpp:877 +msgctxt "lowres" +msgid "True Roland MT-32 (no GM emulation)" +msgstr "Roland MT-32 (sen emulación de GM)" + +#: gui/options.cpp:880 +msgid "Enable Roland GS Mode" +msgstr "Activar modo Roland GS" + +#: gui/options.cpp:880 +msgid "Turns off General MIDI mapping for games with Roland MT-32 soundtrack" +msgstr "Desactiva o General MIDI para os xogos con música en Roland MT-32" + +#: gui/options.cpp:889 +msgid "Don't use Roland MT-32 music" +msgstr "Non empregar música en Roland MT-32" + +#: gui/options.cpp:916 +msgid "Text and Speech:" +msgstr "Texto e voz:" + +#: gui/options.cpp:920 gui/options.cpp:930 +msgid "Speech" +msgstr "Voz" + +#: gui/options.cpp:921 gui/options.cpp:931 +msgid "Subtitles" +msgstr "Subtítulos" + +#: gui/options.cpp:922 +msgid "Both" +msgstr "Ambos" + +#: gui/options.cpp:924 +msgid "Subtitle speed:" +msgstr "Velocidade dos subtítulos:" + +#: gui/options.cpp:926 +msgctxt "lowres" +msgid "Text and Speech:" +msgstr "Texto e voz:" + +#: gui/options.cpp:930 +msgid "Spch" +msgstr "Voz" + +#: gui/options.cpp:931 +msgid "Subs" +msgstr "Subs" + +#: gui/options.cpp:932 +msgctxt "lowres" +msgid "Both" +msgstr "Ambos" + +#: gui/options.cpp:932 +msgid "Show subtitles and play speech" +msgstr "Mostrar os subtítulos e reproducir as voces" + +#: gui/options.cpp:934 +msgctxt "lowres" +msgid "Subtitle speed:" +msgstr "Velocidade subs:" + +#: gui/options.cpp:950 +msgid "Music volume:" +msgstr "Volume de música:" + +#: gui/options.cpp:952 +msgctxt "lowres" +msgid "Music volume:" +msgstr "Volume música:" + +#: gui/options.cpp:959 +msgid "Mute All" +msgstr "Silenciar todo" + +#: gui/options.cpp:962 +msgid "SFX volume:" +msgstr "Volume de efectos:" + +#: gui/options.cpp:962 gui/options.cpp:964 gui/options.cpp:965 +msgid "Special sound effects volume" +msgstr "Volume dos efectos de son" + +#: gui/options.cpp:964 +msgctxt "lowres" +msgid "SFX volume:" +msgstr "Volume efectos:" + +#: gui/options.cpp:972 +msgid "Speech volume:" +msgstr "Volume de voz:" + +#: gui/options.cpp:974 +msgctxt "lowres" +msgid "Speech volume:" +msgstr "Volume voz:" + +#: gui/options.cpp:1131 +msgid "Theme Path:" +msgstr "Camiño do tema:" + +#: gui/options.cpp:1133 +msgctxt "lowres" +msgid "Theme Path:" +msgstr "Camiño tema:" + +#: gui/options.cpp:1139 gui/options.cpp:1141 gui/options.cpp:1142 +msgid "Specifies path to additional data used by all games or ScummVM" +msgstr "" +"Especificar o camiño dos datos adicionais de todos os xogos ou de ScummVM" + +#: gui/options.cpp:1148 +msgid "Plugins Path:" +msgstr "Camiño dos complementos:" + +#: gui/options.cpp:1150 +msgctxt "lowres" +msgid "Plugins Path:" +msgstr "Camiño complementos:" + +#: gui/options.cpp:1159 +msgid "Misc" +msgstr "Misc." + +#: gui/options.cpp:1161 +msgctxt "lowres" +msgid "Misc" +msgstr "Misc." + +#: gui/options.cpp:1163 +msgid "Theme:" +msgstr "Tema:" + +#: gui/options.cpp:1167 +msgid "GUI Renderer:" +msgstr "Procesamento da interfaz:" + +#: gui/options.cpp:1179 +msgid "Autosave:" +msgstr "Autogardado:" + +#: gui/options.cpp:1181 +msgctxt "lowres" +msgid "Autosave:" +msgstr "Autogardado:" + +#: gui/options.cpp:1189 +msgid "Keys" +msgstr "Teclas" + +#: gui/options.cpp:1196 +msgid "GUI Language:" +msgstr "Idioma de interfaz:" + +#: gui/options.cpp:1196 +msgid "Language of ScummVM GUI" +msgstr "Idioma da interfaz de ScummVM" + +#: gui/options.cpp:1347 +msgid "You have to restart ScummVM before your changes will take effect." +msgstr "Debes reiniciar ScummVM para que os cambios teñan efecto." + +#: gui/options.cpp:1360 +msgid "Select directory for savegames" +msgstr "Seleccionar directorio para ficheiros de gardado" + +#: gui/options.cpp:1367 +msgid "The chosen directory cannot be written to. Please select another one." +msgstr "Non é posible escribir no directorio elixido. Selecciona outro." + +#: gui/options.cpp:1376 +msgid "Select directory for GUI themes" +msgstr "Seleccionar directorio para temas de interfaz" + +#: gui/options.cpp:1386 +msgid "Select directory for extra files" +msgstr "Seleccionar directorio para ficheiros adicionais" + +#: gui/options.cpp:1397 +msgid "Select directory for plugins" +msgstr "Seleccionar directorio para complementos" + +#: gui/options.cpp:1450 +msgid "" +"The theme you selected does not support your current language. If you want " +"to use this theme you need to switch to another language first." +msgstr "" +"O tema seleccionado non é compatible co idioma actual. Para empregar o tema, " +"deberás cambiar antes o idioma da interfaz." + +#: gui/saveload-dialog.cpp:166 +msgid "List view" +msgstr "Lista" + +#: gui/saveload-dialog.cpp:167 +msgid "Grid view" +msgstr "Grade" + +#: gui/saveload-dialog.cpp:210 gui/saveload-dialog.cpp:358 +msgid "No date saved" +msgstr "Non hai data gardada" + +#: gui/saveload-dialog.cpp:211 gui/saveload-dialog.cpp:359 +msgid "No time saved" +msgstr "Non hai hora gardada" + +#: gui/saveload-dialog.cpp:212 gui/saveload-dialog.cpp:360 +msgid "No playtime saved" +msgstr "Non hai tempo de xogo gardado" + +#: gui/saveload-dialog.cpp:219 gui/saveload-dialog.cpp:275 +msgid "Delete" +msgstr "Eliminar" + +#: gui/saveload-dialog.cpp:274 +msgid "Do you really want to delete this savegame?" +msgstr "Seguro que queres eliminar esta partida?" + +#: gui/saveload-dialog.cpp:383 gui/saveload-dialog.cpp:872 +msgid "Date: " +msgstr "Data:" + +#: gui/saveload-dialog.cpp:387 gui/saveload-dialog.cpp:878 +msgid "Time: " +msgstr "Hora:" + +#: gui/saveload-dialog.cpp:393 gui/saveload-dialog.cpp:886 +msgid "Playtime: " +msgstr "Tempo de xogo:" + +#: gui/saveload-dialog.cpp:406 gui/saveload-dialog.cpp:494 +msgid "Untitled savestate" +msgstr "Partida sen título" + +#: gui/saveload-dialog.cpp:546 +msgid "Next" +msgstr "Seg." + +#: gui/saveload-dialog.cpp:549 +msgid "Prev" +msgstr "Ant." + +#: gui/saveload-dialog.cpp:736 +msgid "New Save" +msgstr "Novo ficheiro" + +#: gui/saveload-dialog.cpp:736 +msgid "Create a new save game" +msgstr "Crea un novo ficheiro de gardado" + +#: gui/saveload-dialog.cpp:865 +msgid "Name: " +msgstr "Nome:" + +#: gui/saveload-dialog.cpp:937 +#, c-format +msgid "Enter a description for slot %d:" +msgstr "Introduce unha descrición para o espazo %d:" + +#: gui/themebrowser.cpp:44 +msgid "Select a Theme" +msgstr "Seleccionar tema" + +#: gui/ThemeEngine.cpp:337 +msgid "Disabled GFX" +msgstr "Efectos gráficos desactivados" + +#: gui/ThemeEngine.cpp:337 +msgctxt "lowres" +msgid "Disabled GFX" +msgstr "Efectos desactivados" + +#: gui/ThemeEngine.cpp:338 +msgid "Standard Renderer (16bpp)" +msgstr "Procesamento estándar (16 bpp)" + +#: gui/ThemeEngine.cpp:338 +msgid "Standard (16bpp)" +msgstr "Estándar (16 bpp)" + +#: gui/ThemeEngine.cpp:340 +msgid "Antialiased Renderer (16bpp)" +msgstr "Procesamento antidistorsión (16 bpp)" + +#: gui/ThemeEngine.cpp:340 +msgid "Antialiased (16bpp)" +msgstr "Antidistorsión (16 bpp)" + +#: gui/widget.cpp:322 gui/widget.cpp:324 gui/widget.cpp:330 gui/widget.cpp:332 +msgid "Clear value" +msgstr "Limpar valor" + +#: base/main.cpp:210 +#, c-format +msgid "Engine does not support debug level '%s'" +msgstr "O motor non é compatible co nivel de depuración %s" + +#: base/main.cpp:288 +msgid "Menu" +msgstr "Menú" + +#: base/main.cpp:291 backends/platform/symbian/src/SymbianActions.cpp:45 +#: backends/platform/wince/CEActionsPocket.cpp:45 +#: backends/platform/wince/CEActionsSmartphone.cpp:46 +msgid "Skip" +msgstr "Omitir" + +#: base/main.cpp:294 backends/platform/symbian/src/SymbianActions.cpp:50 +#: backends/platform/wince/CEActionsPocket.cpp:42 +msgid "Pause" +msgstr "Pausa" + +#: base/main.cpp:297 +msgid "Skip line" +msgstr "Omitir liña" + +#: base/main.cpp:468 +msgid "Error running game:" +msgstr "Erro de execución do xogo:" + +#: base/main.cpp:492 +msgid "Could not find any engine capable of running the selected game" +msgstr "Non se puido atopar un motor para executar o xogo seleccionado" + +#: common/error.cpp:38 +msgid "No error" +msgstr "Non hai erros" + +#: common/error.cpp:40 +msgid "Game data not found" +msgstr "Non se atoparon datos de xogo" + +#: common/error.cpp:42 +msgid "Game id not supported" +msgstr "ID de xogo non compatible" + +#: common/error.cpp:44 +msgid "Unsupported color mode" +msgstr "Modo de color non compatible" + +#: common/error.cpp:47 +msgid "Read permission denied" +msgstr "Permiso de lectura denegado" + +#: common/error.cpp:49 +msgid "Write permission denied" +msgstr "Permiso de escritura denegado" + +#: common/error.cpp:52 +msgid "Path does not exist" +msgstr "O camiño non existe" + +#: common/error.cpp:54 +msgid "Path not a directory" +msgstr "O camiño non é un directorio" + +#: common/error.cpp:56 +msgid "Path not a file" +msgstr "O camiño non é un ficheiro" + +#: common/error.cpp:59 +msgid "Cannot create file" +msgstr "Erro ao crear o ficheiro" + +#: common/error.cpp:61 +msgid "Reading data failed" +msgstr "Erro ao ler os datos" + +#: common/error.cpp:63 +msgid "Writing data failed" +msgstr "Erro ao escribir os datos" + +#: common/error.cpp:66 +msgid "Could not find suitable engine plugin" +msgstr "Non se atopou un complemento axeitado para o motor" + +#: common/error.cpp:68 +msgid "Engine plugin does not support save states" +msgstr "O complemento do motor non é compatible cos ficheiros de gardado" + +#: common/error.cpp:71 +msgid "User canceled" +msgstr "Usuario cancelado" + +#: common/error.cpp:75 +msgid "Unknown error" +msgstr "Erro descoñecido" + +#: engines/advancedDetector.cpp:316 +#, c-format +msgid "The game in '%s' seems to be unknown." +msgstr "O xogo de %s semella ser descoñecido." + +#: engines/advancedDetector.cpp:317 +msgid "Please, report the following data to the ScummVM team along with name" +msgstr "Facilita esta información ao equipo de ScummVM, xunto co nome" + +#: engines/advancedDetector.cpp:319 +msgid "of the game you tried to add and its version/language/etc.:" +msgstr "do xogo que tentaches engadir, xunto coa versión, lingua, etc.:" + +#: engines/dialogs.cpp:84 +msgid "~R~esume" +msgstr "~R~etomar" + +#: engines/dialogs.cpp:86 +msgid "~L~oad" +msgstr "~C~argar" + +#: engines/dialogs.cpp:90 +msgid "~S~ave" +msgstr "~G~ardar" + +#: engines/dialogs.cpp:94 +msgid "~O~ptions" +msgstr "~O~pcións" + +#: engines/dialogs.cpp:99 +msgid "~H~elp" +msgstr "~A~xuda" + +#: engines/dialogs.cpp:101 +msgid "~A~bout" +msgstr "Acerca ~d~e" + +#: engines/dialogs.cpp:104 engines/dialogs.cpp:180 +msgid "~R~eturn to Launcher" +msgstr "~V~olver ao Iniciador" + +#: engines/dialogs.cpp:106 engines/dialogs.cpp:182 +msgctxt "lowres" +msgid "~R~eturn to Launcher" +msgstr "~V~olver ao Iniciador" + +#: engines/dialogs.cpp:115 engines/agi/saveload.cpp:803 +#: engines/cruise/menu.cpp:212 engines/sci/engine/kfile.cpp:742 +msgid "Save game:" +msgstr "Gardar partida:" + +#: engines/dialogs.cpp:115 engines/agi/saveload.cpp:803 +#: engines/scumm/dialogs.cpp:187 engines/cruise/menu.cpp:212 +#: engines/sci/engine/kfile.cpp:742 +#: backends/platform/symbian/src/SymbianActions.cpp:44 +#: backends/platform/wince/CEActionsPocket.cpp:43 +#: backends/platform/wince/CEActionsPocket.cpp:267 +#: backends/platform/wince/CEActionsSmartphone.cpp:45 +#: backends/platform/wince/CEActionsSmartphone.cpp:231 +msgid "Save" +msgstr "Gardar" + +#: engines/dialogs.cpp:144 +msgid "" +"Sorry, this engine does not currently provide in-game help. Please consult " +"the README for basic information, and for instructions on how to obtain " +"further assistance." +msgstr "" +"Este motor non ofrece axuda no xogo actualmente. Consulta o ficheiro README " +"para obter información básica e máis instrucións para acadar asistencia " +"adicional." + +#: engines/dialogs.cpp:228 +#, c-format +msgid "" +"Gamestate save failed (%s)! Please consult the README for basic information, " +"and for instructions on how to obtain further assistance." +msgstr "" +"Erro ao gardar (%s)! Consulta o ficheiro README para obter información " +"básica e máis instrucións para acadar asistencia adicional." + +#: engines/dialogs.cpp:301 engines/mohawk/dialogs.cpp:109 +#: engines/mohawk/dialogs.cpp:174 +msgid "~O~K" +msgstr "~A~ceptar" + +#: engines/dialogs.cpp:302 engines/mohawk/dialogs.cpp:110 +#: engines/mohawk/dialogs.cpp:175 +msgid "~C~ancel" +msgstr "~C~ancelar" + +#: engines/dialogs.cpp:305 +msgid "~K~eys" +msgstr "~T~eclas" + +#: engines/engine.cpp:235 +msgid "Could not initialize color format." +msgstr "Non se puido iniciar o formato de cor." + +#: engines/engine.cpp:243 +msgid "Could not switch to video mode: '" +msgstr "Non se puido cambiar ao modo de vídeo: '" + +#: engines/engine.cpp:252 +msgid "Could not apply aspect ratio setting." +msgstr "Non se puido aplicar a configuración de proporción." + +#: engines/engine.cpp:257 +msgid "Could not apply fullscreen setting." +msgstr "Non se puido aplicar a configuración de pantalla completa." + +#: engines/engine.cpp:357 +msgid "" +"You appear to be playing this game directly\n" +"from the CD. This is known to cause problems,\n" +"and it is therefore recommended that you copy\n" +"the data files to your hard disk instead.\n" +"See the README file for details." +msgstr "" +"Semella que estás a xogar directamente\n" +"dende o CD. Temos constancia de que causa,\n" +"problemas. Por iso, recomendámosche copiar\n" +"os ficheiros de datos ao disco duro. Consulta\n" +"o ficheiro README para obter máis información." + +#: engines/engine.cpp:368 +msgid "" +"This game has audio tracks in its disk. These\n" +"tracks need to be ripped from the disk using\n" +"an appropriate CD audio extracting tool in\n" +"order to listen to the game's music.\n" +"See the README file for details." +msgstr "" +"O xogo ten pistas de son no disco. Debes\n" +"extraelas por medio dunha ferramenta\n" +"axeitada para poder escoitar a música\n" +"do xogo. Consulta o ficheiro README\n" +"para obter máis información." + +#: engines/engine.cpp:426 +#, c-format +msgid "" +"Gamestate load failed (%s)! Please consult the README for basic information, " +"and for instructions on how to obtain further assistance." +msgstr "" +"Erro ao cargar (%s)! Consulta o ficheiro README para obter información " +"básica e máis instrucións para acadar asistencia adicional." + +#: engines/engine.cpp:439 +msgid "" +"WARNING: The game you are about to start is not yet fully supported by " +"ScummVM. As such, it is likely to be unstable, and any saves you make might " +"not work in future versions of ScummVM." +msgstr "" +"Ollo: o xogo que vas iniciar non é aínda totalmente compatible con ScummVM. " +"Por iso, talvez sexa inestable e os ficheiros de gardado talvez non " +"funcionen en futuras versións de ScummVM." + +#: engines/engine.cpp:442 +msgid "Start anyway" +msgstr "Iniciar de todos os xeitos" + +#: engines/agi/detection.cpp:142 engines/dreamweb/detection.cpp:47 +#: engines/sci/detection.cpp:393 +msgid "Use original save/load screens" +msgstr "Empregar pantallas orixinais de gardado e carga" + +#: engines/agi/detection.cpp:143 engines/dreamweb/detection.cpp:48 +#: engines/sci/detection.cpp:394 +msgid "Use the original save/load screens, instead of the ScummVM ones" +msgstr "" +"Empregar as pantallas orixinais de gardado e carga, no canto das de ScummVM" + +#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:838 +msgid "Restore game:" +msgstr "Restaurar xogo:" + +#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:838 +msgid "Restore" +msgstr "Restaurar" + +#: engines/dreamweb/detection.cpp:57 +msgid "Use bright palette mode" +msgstr "Empregar modo de paleta intensa" + +#: engines/dreamweb/detection.cpp:58 +msgid "Display graphics using the game's bright palette" +msgstr "Mostrar os gráficos coa paletta intensa do xogo" + +#: engines/sci/detection.cpp:373 +msgid "EGA undithering" +msgstr "Non interpolación EGA" + +#: engines/sci/detection.cpp:374 +msgid "Enable undithering in EGA games" +msgstr "Activar a non interpolación nos xogos en EGA" + +#: engines/sci/detection.cpp:383 +msgid "Prefer digital sound effects" +msgstr "Preferir efectos de son dixitais" + +#: engines/sci/detection.cpp:384 +msgid "Prefer digital sound effects instead of synthesized ones" +msgstr "Dar preferencia aos efectos de son dixitais no canto dos sintéticos" + +#: engines/sci/detection.cpp:403 +msgid "Use IMF/Yamaha FB-01 for MIDI output" +msgstr "Empregar IMF/Yamaha FB-01 para a saída de MIDI" + +#: engines/sci/detection.cpp:404 +msgid "" +"Use an IBM Music Feature card or a Yamaha FB-01 FM synth module for MIDI " +"output" +msgstr "" +"Empregar unha tarxeta IBM Music Feature ou un módulo de sintetizador Yamaha " +"FB-01 FM para a saída de MIDI" + +#: engines/sci/detection.cpp:414 +msgid "Use CD audio" +msgstr "Empregar son de CD" + +#: engines/sci/detection.cpp:415 +msgid "Use CD audio instead of in-game audio, if available" +msgstr "Empregar son de CD no canto do do xogo, de ser o caso" + +#: engines/sci/detection.cpp:425 +msgid "Use Windows cursors" +msgstr "Empregar cursores de Windows" + +#: engines/sci/detection.cpp:426 +msgid "" +"Use the Windows cursors (smaller and monochrome) instead of the DOS ones" +msgstr "" +"Empregar os cursores de Windows (máis pequenos e monocromos) no canto dos de " +"DOS" + +#: engines/sci/detection.cpp:436 +msgid "Use silver cursors" +msgstr "Empregar cursores prateados" + +#: engines/sci/detection.cpp:437 +msgid "" +"Use the alternate set of silver cursors, instead of the normal golden ones" +msgstr "" +"Empregar o xogo de cursores prateados alternativo, no canto dos dourados " +"normais" + +#: engines/scumm/dialogs.cpp:175 +#, c-format +msgid "Insert Disk %c and Press Button to Continue." +msgstr "Insire o disco %c e preme o botón para continuar." + +#: engines/scumm/dialogs.cpp:176 +#, c-format +msgid "Unable to Find %s, (%c%d) Press Button." +msgstr "Non se puido atopar %s, (%c%d). Preme o botón." + +#: engines/scumm/dialogs.cpp:177 +#, c-format +msgid "Error reading disk %c, (%c%d) Press Button." +msgstr "Erro ao ler o disco %c, (%c%d). Preme o botón." + +#: engines/scumm/dialogs.cpp:178 +msgid "Game Paused. Press SPACE to Continue." +msgstr "Xogo en pausa. Pulsa a barra espazadora para continuar." + +#. I18N: You may specify 'Yes' symbol at the end of the line, like this: +#. "Moechten Sie wirklich neu starten? (J/N)J" +#. Will react to J as 'Yes' +#: engines/scumm/dialogs.cpp:182 +msgid "Are you sure you want to restart? (Y/N)" +msgstr "Seguro que queres reiniciar? (S/N)S" + +#. I18N: you may specify 'Yes' symbol at the end of the line. See previous comment +#: engines/scumm/dialogs.cpp:184 +msgid "Are you sure you want to quit? (Y/N)" +msgstr "Seguro que queres saír? (S/N)S" + +#: engines/scumm/dialogs.cpp:189 +msgid "Play" +msgstr "Xogar" + +#: engines/scumm/dialogs.cpp:191 engines/scumm/help.cpp:82 +#: engines/scumm/help.cpp:84 +#: backends/platform/symbian/src/SymbianActions.cpp:52 +#: backends/platform/wince/CEActionsPocket.cpp:44 +#: backends/platform/wince/CEActionsSmartphone.cpp:52 +#: backends/events/default/default-events.cpp:213 +msgid "Quit" +msgstr "Saír" + +#: engines/scumm/dialogs.cpp:193 +msgid "Insert save/load game disk" +msgstr "Inserir disco de gardado/carga" + +#: engines/scumm/dialogs.cpp:194 +msgid "You must enter a name" +msgstr "Debes introducir un nome" + +#: engines/scumm/dialogs.cpp:195 +msgid "The game was NOT saved (disk full?)" +msgstr "Non se puido gardar a partida (disco cheo?)" + +#: engines/scumm/dialogs.cpp:196 +msgid "The game was NOT loaded" +msgstr "Non se puido cargar a partida" + +#: engines/scumm/dialogs.cpp:197 +#, c-format +msgid "Saving '%s'" +msgstr "Gardando %s" + +#: engines/scumm/dialogs.cpp:198 +#, c-format +msgid "Loading '%s'" +msgstr "Cargando %s" + +#: engines/scumm/dialogs.cpp:199 +msgid "Name your SAVE game" +msgstr "Introduce un nome para a partida gardada" + +#: engines/scumm/dialogs.cpp:200 +msgid "Select a game to LOAD" +msgstr "Selecciona unha partida para cargala" + +#: engines/scumm/dialogs.cpp:201 +msgid "Game title)" +msgstr "Título)" + +#. I18N: Previous page button +#: engines/scumm/dialogs.cpp:287 +msgid "~P~revious" +msgstr "~A~nterior" + +#. I18N: Next page button +#: engines/scumm/dialogs.cpp:289 +msgid "~N~ext" +msgstr "~S~eguinte" + +#: engines/scumm/dialogs.cpp:290 +#: backends/platform/ds/arm9/source/dsoptions.cpp:56 +msgid "~C~lose" +msgstr "~P~echar" + +#: engines/scumm/dialogs.cpp:597 +msgid "Speech Only" +msgstr "Só voz" + +#: engines/scumm/dialogs.cpp:598 +msgid "Speech and Subtitles" +msgstr "Voz e subtítulos" + +#: engines/scumm/dialogs.cpp:599 +msgid "Subtitles Only" +msgstr "Só subtítulos" + +#: engines/scumm/dialogs.cpp:607 +msgctxt "lowres" +msgid "Speech & Subs" +msgstr "Voz e subs" + +#: engines/scumm/dialogs.cpp:653 +msgid "Select a Proficiency Level." +msgstr "Selecciona un nivel de habilidade." + +#: engines/scumm/dialogs.cpp:655 +msgid "Refer to your Loom(TM) manual for help." +msgstr "Consulta o manual de Loom(TM) para obter axuda." + +#: engines/scumm/dialogs.cpp:658 +msgid "Standard" +msgstr "Estándar" + +#: engines/scumm/dialogs.cpp:659 +msgid "Practice" +msgstr "Práctica" + +#: engines/scumm/dialogs.cpp:660 +msgid "Expert" +msgstr "Experto" + +#: engines/scumm/help.cpp:73 +msgid "Common keyboard commands:" +msgstr "Comandos de teclado comúns:" + +#: engines/scumm/help.cpp:74 +msgid "Save / Load dialog" +msgstr "Gardar/cargar diálogo" + +#: engines/scumm/help.cpp:76 +msgid "Skip line of text" +msgstr "Omitir liña de texto" + +#: engines/scumm/help.cpp:77 +msgid "Esc" +msgstr "ESC" + +#: engines/scumm/help.cpp:77 +msgid "Skip cutscene" +msgstr "Omitir secuencia" + +#: engines/scumm/help.cpp:78 +msgid "Space" +msgstr "Barra espazadora" + +#: engines/scumm/help.cpp:78 +msgid "Pause game" +msgstr "Pausar xogo" + +#: engines/scumm/help.cpp:79 engines/scumm/help.cpp:84 +#: engines/scumm/help.cpp:95 engines/scumm/help.cpp:96 +#: engines/scumm/help.cpp:97 engines/scumm/help.cpp:98 +#: engines/scumm/help.cpp:99 engines/scumm/help.cpp:100 +#: engines/scumm/help.cpp:101 engines/scumm/help.cpp:102 +msgid "Ctrl" +msgstr "CTRL" + +#: engines/scumm/help.cpp:79 +msgid "Load game state 1-10" +msgstr "Cargar partida 1-10" + +#: engines/scumm/help.cpp:80 engines/scumm/help.cpp:84 +#: engines/scumm/help.cpp:86 engines/scumm/help.cpp:100 +#: engines/scumm/help.cpp:101 engines/scumm/help.cpp:102 +msgid "Alt" +msgstr "ALT" + +#: engines/scumm/help.cpp:80 +msgid "Save game state 1-10" +msgstr "Gardar partida 1-10" + +#: engines/scumm/help.cpp:86 engines/scumm/help.cpp:89 +msgid "Enter" +msgstr "INTRO" + +#: engines/scumm/help.cpp:86 +msgid "Toggle fullscreen" +msgstr "Activar/desactivar pantalla completa" + +#: engines/scumm/help.cpp:87 +msgid "Music volume up / down" +msgstr "Subir/baixar volume de música" + +#: engines/scumm/help.cpp:88 +msgid "Text speed slower / faster" +msgstr "Acelerar/frear texto" + +#: engines/scumm/help.cpp:89 +msgid "Simulate left mouse button" +msgstr "Simular botón primario do rato" + +#: engines/scumm/help.cpp:90 +msgid "Tab" +msgstr "TAB" + +#: engines/scumm/help.cpp:90 +msgid "Simulate right mouse button" +msgstr "Simular botón secundario do rato" + +#: engines/scumm/help.cpp:93 +msgid "Special keyboard commands:" +msgstr "Comandos de teclado especiais:" + +#: engines/scumm/help.cpp:94 +msgid "Show / Hide console" +msgstr "Mostrar/ocultar consola" + +#: engines/scumm/help.cpp:95 +msgid "Start the debugger" +msgstr "Iniciar o depurador" + +#: engines/scumm/help.cpp:96 +msgid "Show memory consumption" +msgstr "Mostrar consumo de memoria" + +#: engines/scumm/help.cpp:97 +msgid "Run in fast mode (*)" +msgstr "Executar en modo rápido (*)" + +#: engines/scumm/help.cpp:98 +msgid "Run in really fast mode (*)" +msgstr "Executar en modo moi rápido (*)" + +#: engines/scumm/help.cpp:99 +msgid "Toggle mouse capture" +msgstr "Activar/desactivar captura de rato" + +#: engines/scumm/help.cpp:100 +msgid "Switch between graphics filters" +msgstr "Cambiar filtro de gráficos" + +#: engines/scumm/help.cpp:101 +msgid "Increase / Decrease scale factor" +msgstr "Aumentar/reducir factor de escala" + +#: engines/scumm/help.cpp:102 +msgid "Toggle aspect-ratio correction" +msgstr "Activar/desactivar corrección de proporción" + +#: engines/scumm/help.cpp:107 +msgid "* Note that using ctrl-f and" +msgstr "* Nota: non recomendamos" + +#: engines/scumm/help.cpp:108 +msgid " ctrl-g are not recommended" +msgstr " empregar CTRL-F nin CTRL-G," + +#: engines/scumm/help.cpp:109 +msgid " since they may cause crashes" +msgstr " xa que poden provocar bloqueos" + +#: engines/scumm/help.cpp:110 +msgid " or incorrect game behavior." +msgstr " ou outros erros no xogo." + +#: engines/scumm/help.cpp:114 +msgid "Spinning drafts on the keyboard:" +msgstr "Tecer feitizos co teclado:" + +#: engines/scumm/help.cpp:116 +msgid "Main game controls:" +msgstr "Controis principais de xogo:" + +#: engines/scumm/help.cpp:121 engines/scumm/help.cpp:136 +#: engines/scumm/help.cpp:161 +msgid "Push" +msgstr "Empuxar" + +#: engines/scumm/help.cpp:122 engines/scumm/help.cpp:137 +#: engines/scumm/help.cpp:162 +msgid "Pull" +msgstr "Tirar de" + +#: engines/scumm/help.cpp:123 engines/scumm/help.cpp:138 +#: engines/scumm/help.cpp:163 engines/scumm/help.cpp:197 +#: engines/scumm/help.cpp:207 +msgid "Give" +msgstr "Dar" + +#: engines/scumm/help.cpp:124 engines/scumm/help.cpp:139 +#: engines/scumm/help.cpp:164 engines/scumm/help.cpp:190 +#: engines/scumm/help.cpp:208 +msgid "Open" +msgstr "Abrir" + +#: engines/scumm/help.cpp:126 +msgid "Go to" +msgstr "Ir a" + +#: engines/scumm/help.cpp:127 +msgid "Get" +msgstr "Coller" + +#: engines/scumm/help.cpp:128 engines/scumm/help.cpp:152 +#: engines/scumm/help.cpp:170 engines/scumm/help.cpp:198 +#: engines/scumm/help.cpp:213 engines/scumm/help.cpp:224 +#: engines/scumm/help.cpp:250 +msgid "Use" +msgstr "Usar" + +#: engines/scumm/help.cpp:129 engines/scumm/help.cpp:141 +msgid "Read" +msgstr "Ler" + +#: engines/scumm/help.cpp:130 engines/scumm/help.cpp:147 +msgid "New kid" +msgstr "Rapaz" + +#: engines/scumm/help.cpp:131 engines/scumm/help.cpp:153 +#: engines/scumm/help.cpp:171 +msgid "Turn on" +msgstr "Acender" + +#: engines/scumm/help.cpp:132 engines/scumm/help.cpp:154 +#: engines/scumm/help.cpp:172 +msgid "Turn off" +msgstr "Apagar" + +#: engines/scumm/help.cpp:142 engines/scumm/help.cpp:167 +#: engines/scumm/help.cpp:194 +msgid "Walk to" +msgstr "Ir a" + +#: engines/scumm/help.cpp:143 engines/scumm/help.cpp:168 +#: engines/scumm/help.cpp:195 engines/scumm/help.cpp:210 +#: engines/scumm/help.cpp:227 +msgid "Pick up" +msgstr "Coller" + +#: engines/scumm/help.cpp:144 engines/scumm/help.cpp:169 +msgid "What is" +msgstr "Que é" + +#: engines/scumm/help.cpp:146 +msgid "Unlock" +msgstr "Despechar" + +#: engines/scumm/help.cpp:149 +msgid "Put on" +msgstr "Poñer" + +#: engines/scumm/help.cpp:150 +msgid "Take off" +msgstr "Quitar" + +#: engines/scumm/help.cpp:156 +msgid "Fix" +msgstr "Reparar" + +#: engines/scumm/help.cpp:158 +msgid "Switch" +msgstr "Cambiar" + +#: engines/scumm/help.cpp:166 engines/scumm/help.cpp:228 +msgid "Look" +msgstr "Mirar" + +#: engines/scumm/help.cpp:173 engines/scumm/help.cpp:223 +msgid "Talk" +msgstr "Falar" + +#: engines/scumm/help.cpp:174 +msgid "Travel" +msgstr "Viaxar" + +#: engines/scumm/help.cpp:175 +msgid "To Henry / To Indy" +msgstr "A Henry / A Indy" + +#. I18N: These are different musical notes +#: engines/scumm/help.cpp:179 +msgid "play C minor on distaff" +msgstr "tocar do menor no bastón" + +#: engines/scumm/help.cpp:180 +msgid "play D on distaff" +msgstr "tocar re no bastón" + +#: engines/scumm/help.cpp:181 +msgid "play E on distaff" +msgstr "tocar mi no bastón" + +#: engines/scumm/help.cpp:182 +msgid "play F on distaff" +msgstr "tocar fa no bastón" + +#: engines/scumm/help.cpp:183 +msgid "play G on distaff" +msgstr "tocar sol no bastón" + +#: engines/scumm/help.cpp:184 +msgid "play A on distaff" +msgstr "tocar la no bastón" + +#: engines/scumm/help.cpp:185 +msgid "play B on distaff" +msgstr "tocar si no bastón" + +#: engines/scumm/help.cpp:186 +msgid "play C major on distaff" +msgstr "tocar do maior no bastón" + +#: engines/scumm/help.cpp:192 engines/scumm/help.cpp:214 +msgid "puSh" +msgstr "Empurrar" + +#: engines/scumm/help.cpp:193 engines/scumm/help.cpp:215 +msgid "pull (Yank)" +msgstr "Tirar de" + +#: engines/scumm/help.cpp:196 engines/scumm/help.cpp:212 +#: engines/scumm/help.cpp:248 +msgid "Talk to" +msgstr "Falar con" + +#: engines/scumm/help.cpp:199 engines/scumm/help.cpp:211 +msgid "Look at" +msgstr "Mirar" + +#: engines/scumm/help.cpp:200 +msgid "turn oN" +msgstr "Acender" + +#: engines/scumm/help.cpp:201 +msgid "turn oFf" +msgstr "Apagar" + +#: engines/scumm/help.cpp:217 +msgid "KeyUp" +msgstr "Arriba" + +#: engines/scumm/help.cpp:217 +msgid "Highlight prev dialogue" +msgstr "Destacar diálogo anterior" + +#: engines/scumm/help.cpp:218 +msgid "KeyDown" +msgstr "Abaixo" + +#: engines/scumm/help.cpp:218 +msgid "Highlight next dialogue" +msgstr "Destacar diálogo seguinte" + +#: engines/scumm/help.cpp:222 +msgid "Walk" +msgstr "Ir a" + +#: engines/scumm/help.cpp:225 engines/scumm/help.cpp:234 +#: engines/scumm/help.cpp:241 engines/scumm/help.cpp:249 +msgid "Inventory" +msgstr "Inventario" + +#: engines/scumm/help.cpp:226 +msgid "Object" +msgstr "Obxecto" + +#: engines/scumm/help.cpp:229 +msgid "Black and White / Color" +msgstr "Branco e negro/cor" + +#: engines/scumm/help.cpp:232 +msgid "Eyes" +msgstr "Ollos" + +#: engines/scumm/help.cpp:233 +msgid "Tongue" +msgstr "Lingua" + +#: engines/scumm/help.cpp:235 +msgid "Punch" +msgstr "Bater a" + +#: engines/scumm/help.cpp:236 +msgid "Kick" +msgstr "Patear a" + +#: engines/scumm/help.cpp:239 engines/scumm/help.cpp:247 +msgid "Examine" +msgstr "Examinar" + +#: engines/scumm/help.cpp:240 +msgid "Regular cursor" +msgstr "Cursor normal" + +#. I18N: Comm is a communication device +#: engines/scumm/help.cpp:243 +msgid "Comm" +msgstr "Comunicador" + +#: engines/scumm/help.cpp:246 +msgid "Save / Load / Options" +msgstr "Gardar/cargar/opcións" + +#: engines/scumm/help.cpp:255 +msgid "Other game controls:" +msgstr "Outros controis de xogo:" + +#: engines/scumm/help.cpp:257 engines/scumm/help.cpp:267 +msgid "Inventory:" +msgstr "Inventario:" + +#: engines/scumm/help.cpp:258 engines/scumm/help.cpp:274 +msgid "Scroll list up" +msgstr "Subir lista" + +#: engines/scumm/help.cpp:259 engines/scumm/help.cpp:275 +msgid "Scroll list down" +msgstr "Baixar lista" + +#: engines/scumm/help.cpp:260 engines/scumm/help.cpp:268 +msgid "Upper left item" +msgstr "Obxecto esquerdo arriba" + +#: engines/scumm/help.cpp:261 engines/scumm/help.cpp:270 +msgid "Lower left item" +msgstr "Obxecto esquerdo abaixo" + +#: engines/scumm/help.cpp:262 engines/scumm/help.cpp:271 +msgid "Upper right item" +msgstr "Obxecto dereito arriba" + +#: engines/scumm/help.cpp:263 engines/scumm/help.cpp:273 +msgid "Lower right item" +msgstr "Obxecto dereito abaixo" + +#: engines/scumm/help.cpp:269 +msgid "Middle left item" +msgstr "Obxecto esquerdo medio" + +#: engines/scumm/help.cpp:272 +msgid "Middle right item" +msgstr "Obxecto dereito medio" + +#: engines/scumm/help.cpp:279 engines/scumm/help.cpp:284 +msgid "Switching characters:" +msgstr "Cambiar caracteres:" + +#: engines/scumm/help.cpp:281 +msgid "Second kid" +msgstr "Rapaz 2" + +#: engines/scumm/help.cpp:282 +msgid "Third kid" +msgstr "Rapaz 3" + +#: engines/scumm/help.cpp:294 +msgid "Fighting controls (numpad):" +msgstr "Controis de combate (teclado numérico):" + +#: engines/scumm/help.cpp:295 engines/scumm/help.cpp:296 +#: engines/scumm/help.cpp:297 +msgid "Step back" +msgstr "Paso atrás" + +#: engines/scumm/help.cpp:298 +msgid "Block high" +msgstr "Bloqueo alto" + +#: engines/scumm/help.cpp:299 +msgid "Block middle" +msgstr "Bloqueo medio" + +#: engines/scumm/help.cpp:300 +msgid "Block low" +msgstr "Bloqueo baixo" + +#: engines/scumm/help.cpp:301 +msgid "Punch high" +msgstr "Puñazo alto" + +#: engines/scumm/help.cpp:302 +msgid "Punch middle" +msgstr "Puñazo medio" + +#: engines/scumm/help.cpp:303 +msgid "Punch low" +msgstr "Puñazo baixo" + +#: engines/scumm/help.cpp:306 +msgid "These are for Indy on left." +msgstr "Son para Indy na esquerda." + +#: engines/scumm/help.cpp:307 +msgid "When Indy is on the right," +msgstr "Se está na dereita," + +#: engines/scumm/help.cpp:308 +msgid "7, 4, and 1 are switched with" +msgstr "7, 4 e 1 cámbianse por" + +#: engines/scumm/help.cpp:309 +msgid "9, 6, and 3, respectively." +msgstr "9, 6 e 3 respectivamente." + +#: engines/scumm/help.cpp:316 +msgid "Biplane controls (numpad):" +msgstr "Controis de biplano (teclado numérico):" + +#: engines/scumm/help.cpp:317 +msgid "Fly to upper left" +msgstr "Voar á esquerda arriba" + +#: engines/scumm/help.cpp:318 +msgid "Fly to left" +msgstr "Voar á esquerda" + +#: engines/scumm/help.cpp:319 +msgid "Fly to lower left" +msgstr "Voar á esquerda abaixo" + +#: engines/scumm/help.cpp:320 +msgid "Fly upwards" +msgstr "Voar arriba" + +#: engines/scumm/help.cpp:321 +msgid "Fly straight" +msgstr "Voar recto" + +#: engines/scumm/help.cpp:322 +msgid "Fly down" +msgstr "Voar abaixo" + +#: engines/scumm/help.cpp:323 +msgid "Fly to upper right" +msgstr "Voar á dereita arriba" + +#: engines/scumm/help.cpp:324 +msgid "Fly to right" +msgstr "Voar á dereita" + +#: engines/scumm/help.cpp:325 +msgid "Fly to lower right" +msgstr "Voar á dereita abaixo" + +#: engines/scumm/scumm.cpp:1774 +#, c-format +msgid "" +"Native MIDI support requires the Roland Upgrade from LucasArts,\n" +"but %s is missing. Using AdLib instead." +msgstr "" +"A compatibilidade nativa con MIDI precisa a actualización de Roland\n" +"de LucasArts, mais falla %s. Empregarase AdLib." + +#: engines/scumm/scumm.cpp:2295 engines/agos/saveload.cpp:220 +#, c-format +msgid "" +"Failed to save game state to file:\n" +"\n" +"%s" +msgstr "" +"Erro ao gardar a partida no ficheiro:\n" +"\n" +"%s" + +#: engines/scumm/scumm.cpp:2302 engines/agos/saveload.cpp:185 +#, c-format +msgid "" +"Failed to load game state from file:\n" +"\n" +"%s" +msgstr "" +"Erro ao cargar a partida do ficheiro:\n" +"\n" +"%s" + +#: engines/scumm/scumm.cpp:2314 engines/agos/saveload.cpp:228 +#, c-format +msgid "" +"Successfully saved game state in file:\n" +"\n" +"%s" +msgstr "" +"Partida gardada con éxito no ficheiro:\n" +"\n" +"%s" + +#: engines/scumm/scumm.cpp:2529 +msgid "" +"Usually, Maniac Mansion would start now. But ScummVM doesn't do that yet. To " +"play it, go to 'Add Game' in the ScummVM start menu and select the 'Maniac' " +"directory inside the Tentacle game directory." +msgstr "" +"Maniac Mansion tería que empezar agora. Porén, ScummVM aínda non é quen de " +"facelo. Para xogar, vai a Engadir xogo no menú de inicio de ScummVM e " +"selecciona o directorio Maniac que está dentro do directorio Tentacle." + +#. I18N: Option for fast scene switching +#: engines/mohawk/dialogs.cpp:92 engines/mohawk/dialogs.cpp:171 +msgid "~Z~ip Mode Activated" +msgstr "Modo ~c~omprimido activado" + +#: engines/mohawk/dialogs.cpp:93 +msgid "~T~ransitions Enabled" +msgstr "~T~ransicións activadas" + +#. I18N: Drop book page +#: engines/mohawk/dialogs.cpp:95 +msgid "~D~rop Page" +msgstr "~D~eixar folla" + +#: engines/mohawk/dialogs.cpp:99 +msgid "~S~how Map" +msgstr "Mo~s~trar mapa" + +#: engines/mohawk/dialogs.cpp:105 +msgid "~M~ain Menu" +msgstr "~M~enú principal" + +#: engines/mohawk/dialogs.cpp:172 +msgid "~W~ater Effect Enabled" +msgstr "Efecto de ~a~uga activado" + +#: engines/agos/animation.cpp:557 +#, c-format +msgid "Cutscene file '%s' not found!" +msgstr "Non se atopou o ficheiro de secuencia %s!" + +#: engines/gob/inter_playtoons.cpp:256 engines/gob/inter_v2.cpp:1287 +#: engines/tinsel/saveload.cpp:532 +msgid "Failed to load game state from file." +msgstr "Erro ao cargar a partida do ficheiro." + +#: engines/gob/inter_v2.cpp:1357 engines/tinsel/saveload.cpp:545 +msgid "Failed to save game state to file." +msgstr "Erro ao gardar a partida no ficheiro." + +#: engines/gob/inter_v5.cpp:107 +msgid "Failed to delete file." +msgstr "Erro ao eliminar o ficheiro." + +#: engines/groovie/script.cpp:420 +msgid "Failed to save game" +msgstr "Erro ao gardar a partida" + +#. I18N: Studio audience adds an applause and cheering sounds whenever +#. Malcolm makes a joke. +#: engines/kyra/detection.cpp:62 +msgid "Studio audience" +msgstr "Público do estudio" + +#: engines/kyra/detection.cpp:63 +msgid "Enable studio audience" +msgstr "Activar o público do estudio" + +#. I18N: This option allows the user to skip text and cutscenes. +#: engines/kyra/detection.cpp:73 +msgid "Skip support" +msgstr "Omisións" + +#: engines/kyra/detection.cpp:74 +msgid "Allow text and cutscenes to be skipped" +msgstr "Permitir a omisión do texto e das secuencias" + +#. I18N: Helium mode makes people sound like they've inhaled Helium. +#: engines/kyra/detection.cpp:84 +msgid "Helium mode" +msgstr "Modo helio" + +#: engines/kyra/detection.cpp:85 +msgid "Enable helium mode" +msgstr "Activar o modo helio" + +#. I18N: When enabled, this option makes scrolling smoother when +#. changing from one screen to another. +#: engines/kyra/detection.cpp:99 +msgid "Smooth scrolling" +msgstr "Desprazamento suave" + +#: engines/kyra/detection.cpp:100 +msgid "Enable smooth scrolling when walking" +msgstr "Activar o desprazamento suave ao camiñar" + +#. I18N: When enabled, this option changes the cursor when it floats to the +#. edge of the screen to a directional arrow. The player can then click to +#. walk towards that direction. +#: engines/kyra/detection.cpp:112 +msgid "Floating cursors" +msgstr "Cursores flotantes" + +#: engines/kyra/detection.cpp:113 +msgid "Enable floating cursors" +msgstr "Activar cursores flotantes" + +#. I18N: HP stands for Hit Points +#: engines/kyra/detection.cpp:127 +msgid "HP bar graphs" +msgstr "Barras de vida" + +#: engines/kyra/detection.cpp:128 +msgid "Enable hit point bar graphs" +msgstr "Activar barras de vida" + +#: engines/kyra/lol.cpp:478 +msgid "Attack 1" +msgstr "Ataque 1" + +#: engines/kyra/lol.cpp:479 +msgid "Attack 2" +msgstr "Ataque 2" + +#: engines/kyra/lol.cpp:480 +msgid "Attack 3" +msgstr "Ataque 3" + +#: engines/kyra/lol.cpp:481 +msgid "Move Forward" +msgstr "Mover cara diante" + +#: engines/kyra/lol.cpp:482 +msgid "Move Back" +msgstr "Mover cara atrás" + +#: engines/kyra/lol.cpp:483 +msgid "Slide Left" +msgstr "Esvarar á esquerda" + +#: engines/kyra/lol.cpp:484 +msgid "Slide Right" +msgstr "Esvarar á dereita" + +#: engines/kyra/lol.cpp:485 +msgid "Turn Left" +msgstr "Xirar á esquerda" + +#: engines/kyra/lol.cpp:486 +msgid "Turn Right" +msgstr "Xirar á dereita" + +#: engines/kyra/lol.cpp:487 +msgid "Rest" +msgstr "Parar" + +#: engines/kyra/lol.cpp:488 +msgid "Options" +msgstr "Opcións" + +#: engines/kyra/lol.cpp:489 +msgid "Choose Spell" +msgstr "Elixir feitizo" + +#: engines/kyra/sound_midi.cpp:477 +msgid "" +"You appear to be using a General MIDI device,\n" +"but your game only supports Roland MT32 MIDI.\n" +"We try to map the Roland MT32 instruments to\n" +"General MIDI ones. It is still possible that\n" +"some tracks sound incorrect." +msgstr "" +"Semella que estás a empregar un dispositivo,\n" +"de General MIDI, maix o xogo só e compatible con\n" +"Roland MT32 MIDI. Tentamos asignar os instrumentos\n" +"aos de General MIDI. No entanto, existe a posibilidade\n" +"de que algunhas pistas non soen correctamente." + +#: engines/queen/queen.cpp:59 +msgid "Alternative intro" +msgstr "Intro alternativa" + +#: engines/queen/queen.cpp:60 +msgid "Use an alternative game intro (CD version only)" +msgstr "Empregar unha introdución alternativa para o xogo (só versión en CD)" + +#: engines/sky/compact.cpp:130 +msgid "" +"Unable to find \"sky.cpt\" file!\n" +"Please download it from www.scummvm.org" +msgstr "" +"O ficheiro sky.cpt non se atopa!\n" +"Descárgao dende www.scummvm.org" + +#: engines/sky/compact.cpp:141 +msgid "" +"The \"sky.cpt\" file has an incorrect size.\n" +"Please (re)download it from www.scummvm.org" +msgstr "" +"O ficheiro sky.cpt ten un tamaño incorrecto.\n" +"Descárgao (de novo) dende www.scummvm.org" + +#: engines/sky/detection.cpp:44 +msgid "Floppy intro" +msgstr "Intro de disquete" + +#: engines/sky/detection.cpp:45 +msgid "Use the floppy version's intro (CD version only)" +msgstr "Empregar a introdución da versión en disquete (só versión en CD)" + +#: engines/sword1/animation.cpp:519 +#, c-format +msgid "PSX stream cutscene '%s' cannot be played in paletted mode" +msgstr "Non se pode reproducir a secuencia %s de PSX neste modo gráfico" + +#: engines/sword1/animation.cpp:540 engines/sword2/animation.cpp:439 +msgid "DXA cutscenes found but ScummVM has been built without zlib support" +msgstr "" +"Atopáronse secuencias de DXA. No entanto, esta versión de ScummVM non é " +"compatible con zlib" + +#: engines/sword1/animation.cpp:550 engines/sword2/animation.cpp:449 +msgid "MPEG2 cutscenes are no longer supported" +msgstr "Xa non hai compatibilidade coas secuencias en MPEG2" + +#: engines/sword1/animation.cpp:556 engines/sword2/animation.cpp:457 +#, c-format +msgid "Cutscene '%s' not found" +msgstr "Non se atopou a secuencia %s" + +#: engines/sword1/control.cpp:865 +msgid "" +"ScummVM found that you have old savefiles for Broken Sword 1 that should be " +"converted.\n" +"The old save game format is no longer supported, so you will not be able to " +"load your games if you don't convert them.\n" +"\n" +"Press OK to convert them now, otherwise you will be asked again the next " +"time you start the game.\n" +msgstr "" +"ScummVM atopou ficheiros de gardado vellos de Broken Sword 1 que deberían " +"ser convertidos.\n" +"O formato vello xa non é compatible, de xeito que non poderás cargar as " +"partidas se non os convertes.\n" +"\n" +"Preme Aceptar para convertilos. Se non, volverás ver esta mensaxe a próxima " +"vez que inicies o xogo.\n" + +#: engines/sword1/control.cpp:1234 +#, c-format +msgid "" +"Target new save game already exists!\n" +"Would you like to keep the old save game (%s) or the new one (%s)?\n" +msgstr "" +"Xa existe unha partida con ese nome!\n" +"Queres conservar a vella (%s) ou a nova (%s)?\n" + +#: engines/sword1/control.cpp:1237 +msgid "Keep the old one" +msgstr "Conservar a vella" + +#: engines/sword1/control.cpp:1237 +msgid "Keep the new one" +msgstr "Conservar a nova" + +#: engines/sword1/logic.cpp:1633 +msgid "This is the end of the Broken Sword 1 Demo" +msgstr "Aquí remata a demo de Broken Sword 1" + +#: engines/sword2/animation.cpp:419 +msgid "" +"PSX cutscenes found but ScummVM has been built without RGB color support" +msgstr "" +"Atopáronse secuencias de PSX. No entanto, a versión de ScummVM non é " +"compatible con cores RGB" + +#: engines/sword2/sword2.cpp:79 +msgid "Show object labels" +msgstr "Mostrar etiquetas" + +#: engines/sword2/sword2.cpp:80 +msgid "Show labels for objects on mouse hover" +msgstr "Mostrar as etiquetas dos obxectos ao apuntar co rato" + +#: engines/teenagent/resources.cpp:94 +msgid "" +"You're missing the 'teenagent.dat' file. Get it from the ScummVM website" +msgstr "" +"Falta o ficheiro teenagent.dat. Descárgao dende o sitio web de ScummVM." + +#: engines/teenagent/resources.cpp:115 +msgid "" +"The teenagent.dat file is compressed and zlib hasn't been included in this " +"executable. Please decompress it" +msgstr "" +"O ficheiro teenagent.dat está comprimido e zlib non foi incluído neste " +"executable. Descomprime o ficheiro" + +#: engines/parallaction/saveload.cpp:133 +#, c-format +msgid "" +"Can't save game in slot %i\n" +"\n" +msgstr "" +"Non se pode gardar a partida no espazo %i\n" +"\n" + +#: engines/parallaction/saveload.cpp:204 +msgid "Loading game..." +msgstr "Cargando..." + +#: engines/parallaction/saveload.cpp:219 +msgid "Saving game..." +msgstr "Gardando..." + +#: engines/parallaction/saveload.cpp:272 +msgid "" +"ScummVM found that you have old savefiles for Nippon Safes that should be " +"renamed.\n" +"The old names are no longer supported, so you will not be able to load your " +"games if you don't convert them.\n" +"\n" +"Press OK to convert them now, otherwise you will be asked next time.\n" +msgstr "" +"ScummVM atopou ficheiros de gardado vellos de Nippon Safes que deberían ser " +"renomeados.\n" +"Os nomes vellos xa non son compatibles, de xeito que non poderás cargar as " +"partidas se non os cambias.\n" +"\n" +"Preme Aceptar para cambialos. Se non, volverás ver esta mensaxe a próxima " +"vez que inicies o xogo.\n" + +#: engines/parallaction/saveload.cpp:319 +msgid "ScummVM successfully converted all your savefiles." +msgstr "ScummVM converteu correctamente todos os ficheiros de gardado." + +#: engines/parallaction/saveload.cpp:321 +msgid "" +"ScummVM printed some warnings in your console window and can't guarantee all " +"your files have been converted.\n" +"\n" +"Please report to the team." +msgstr "" +"ScummVM imprimiu avisos na ventá da consola. Non se pode garantir a " +"conversión de todos os ficheiros.\n" +"\n" +"Contacta co equipo de ScummVM." + +#: audio/fmopl.cpp:49 +msgid "MAME OPL emulator" +msgstr "Emulador de OPL de MAME" + +#: audio/fmopl.cpp:51 +msgid "DOSBox OPL emulator" +msgstr "Emulador de OPL de DOSBox" + +#: audio/mididrv.cpp:209 +#, c-format +msgid "" +"The selected audio device '%s' was not found (e.g. might be turned off or " +"disconnected)." +msgstr "" +"Non se atopou o dispositivo de son seleccionado (%s). Talvez estea apagado " +"ou desconectado." + +#: audio/mididrv.cpp:209 audio/mididrv.cpp:221 audio/mididrv.cpp:257 +#: audio/mididrv.cpp:272 +msgid "Attempting to fall back to the next available device..." +msgstr "Intentando pasar ao seguinte dispositivo dispoñible..." + +#: audio/mididrv.cpp:221 +#, c-format +msgid "" +"The selected audio device '%s' cannot be used. See log file for more " +"information." +msgstr "" +"Non se pode empregar o dispositivo de son seleccionado (%s). Consulta o " +"rexistro para obter máis información." + +#: audio/mididrv.cpp:257 +#, c-format +msgid "" +"The preferred audio device '%s' was not found (e.g. might be turned off or " +"disconnected)." +msgstr "" +"Non se atopou o dispositivo de son preferido (%s). Talvez estea apagado ou " +"desconectado." + +#: audio/mididrv.cpp:272 +#, c-format +msgid "" +"The preferred audio device '%s' cannot be used. See log file for more " +"information." +msgstr "" +"Non se pode empregar o dispositivo de son preferido (%s). Consulta o " +"rexistro para obter máis información." + +#: audio/null.h:43 +msgid "No music" +msgstr "Sen música" + +#: audio/mods/paula.cpp:189 +msgid "Amiga Audio Emulator" +msgstr "Emulador de Amiga Audio" + +#: audio/softsynth/adlib.cpp:2284 +msgid "AdLib Emulator" +msgstr "Emulador de AdLib" + +#: audio/softsynth/appleiigs.cpp:33 +msgid "Apple II GS Emulator (NOT IMPLEMENTED)" +msgstr "Emulador de Apple II GS (non implementado)" + +#: audio/softsynth/sid.cpp:1430 +msgid "C64 Audio Emulator" +msgstr "Emulador de C64 Audio" + +#: audio/softsynth/mt32.cpp:293 +msgid "Initializing MT-32 Emulator" +msgstr "Iniciando emulador de MT-32" + +#: audio/softsynth/mt32.cpp:512 +msgid "MT-32 Emulator" +msgstr "Emulador de MT-32" + +#: audio/softsynth/pcspk.cpp:139 +msgid "PC Speaker Emulator" +msgstr "Emulador de altofalante de PC" + +#: audio/softsynth/pcspk.cpp:158 +msgid "IBM PCjr Emulator" +msgstr "Emulador de IBM PCjr" + +#: backends/keymapper/remap-dialog.cpp:47 +msgid "Keymap:" +msgstr "Asignación de teclas:" + +#: backends/keymapper/remap-dialog.cpp:66 +msgid " (Effective)" +msgstr " (Efectiva)" + +#: backends/keymapper/remap-dialog.cpp:106 +msgid " (Active)" +msgstr " (Activa)" + +#: backends/keymapper/remap-dialog.cpp:106 +msgid " (Blocked)" +msgstr " (Bloqueada)" + +#: backends/keymapper/remap-dialog.cpp:119 +msgid " (Global)" +msgstr " (Global)" + +#: backends/keymapper/remap-dialog.cpp:127 +msgid " (Game)" +msgstr " (Xogo)" + +#: backends/midi/windows.cpp:164 +msgid "Windows MIDI" +msgstr "Windows MIDI" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:57 +msgid "ScummVM Main Menu" +msgstr "Menú principal de ScummVM" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:63 +msgid "~L~eft handed mode" +msgstr "Modo para ~z~urdos" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:64 +msgid "~I~ndy fight controls" +msgstr "Controis de combate de ~I~ndy" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:65 +msgid "Show mouse cursor" +msgstr "Mostrar cursor do rato" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:66 +msgid "Snap to edges" +msgstr "Axustar ás marxes" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:68 +msgid "Touch X Offset" +msgstr "Corrección táctil X" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:75 +msgid "Touch Y Offset" +msgstr "Corrección táctil Y" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:87 +msgid "Use laptop trackpad-style cursor control" +msgstr "Empregar control de cursor por trackpad" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:88 +msgid "Tap for left click, double tap right click" +msgstr "Tocar unha vez, premer co botón primario; dúas veces, botón secundario" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:90 +msgid "Sensitivity" +msgstr "Sensibilidade" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:99 +msgid "Initial top screen scale:" +msgstr "Escala da pantalla inicial:" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:105 +msgid "Main screen scaling:" +msgstr "Escala da pantalla principal:" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:107 +msgid "Hardware scale (fast, but low quality)" +msgstr "Escala por hardware (rápida, mais baixa calidade)" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:108 +msgid "Software scale (good quality, but slower)" +msgstr "Escala por software (boa calidade, mais lenta)" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:109 +msgid "Unscaled (you must scroll left and right)" +msgstr "Sen escala (deberás desprazar á esquerda e á dereita)" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:111 +msgid "Brightness:" +msgstr "Luminosidade:" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:121 +msgid "High quality audio (slower) (reboot)" +msgstr "Son de alta calidade (máis lento) (reiniciar)" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:122 +msgid "Disable power off" +msgstr "Desactivar apagado" + +#: backends/platform/iphone/osys_events.cpp:300 +msgid "Mouse-click-and-drag mode enabled." +msgstr "Modo premer e arrastrar activado." + +#: backends/platform/iphone/osys_events.cpp:302 +msgid "Mouse-click-and-drag mode disabled." +msgstr "Modo premer e arrastrar desactivado." + +#: backends/platform/iphone/osys_events.cpp:313 +msgid "Touchpad mode enabled." +msgstr "Modo panel táctil activado." + +#: backends/platform/iphone/osys_events.cpp:315 +msgid "Touchpad mode disabled." +msgstr "Modo panel táctil desactivado." + +#: backends/platform/maemo/maemo.cpp:209 +msgid "Click Mode" +msgstr "Modo rato" + +#: backends/platform/maemo/maemo.cpp:215 +#: backends/platform/symbian/src/SymbianActions.cpp:42 +#: backends/platform/wince/CEActionsPocket.cpp:60 +#: backends/platform/wince/CEActionsSmartphone.cpp:43 +#: backends/platform/bada/form.cpp:281 +msgid "Left Click" +msgstr "Botón primario" + +#: backends/platform/maemo/maemo.cpp:218 +msgid "Middle Click" +msgstr "Botón central" + +#: backends/platform/maemo/maemo.cpp:221 +#: backends/platform/symbian/src/SymbianActions.cpp:43 +#: backends/platform/wince/CEActionsSmartphone.cpp:44 +#: backends/platform/bada/form.cpp:273 +msgid "Right Click" +msgstr "Botón secundario" + +#: backends/platform/sdl/macosx/appmenu_osx.mm:77 +msgid "Hide ScummVM" +msgstr "Ocultar ScummVM" + +#: backends/platform/sdl/macosx/appmenu_osx.mm:82 +msgid "Hide Others" +msgstr "Ocultar outros" + +#: backends/platform/sdl/macosx/appmenu_osx.mm:87 +msgid "Show All" +msgstr "Mostrar todo" + +#: backends/platform/sdl/macosx/appmenu_osx.mm:109 +#: backends/platform/sdl/macosx/appmenu_osx.mm:120 +msgid "Window" +msgstr "Ventá" + +#: backends/platform/sdl/macosx/appmenu_osx.mm:114 +msgid "Minimize" +msgstr "Minimizar" + +#: backends/graphics/surfacesdl/surfacesdl-graphics.cpp:45 +msgid "Normal (no scaling)" +msgstr "Normal (sen escala)" + +#: backends/graphics/surfacesdl/surfacesdl-graphics.cpp:64 +msgctxt "lowres" +msgid "Normal (no scaling)" +msgstr "Normal (sen escala)" + +#: backends/graphics/surfacesdl/surfacesdl-graphics.cpp:2135 +#: backends/graphics/openglsdl/openglsdl-graphics.cpp:533 +msgid "Enabled aspect ratio correction" +msgstr "Corrección de proporción activada" + +#: backends/graphics/surfacesdl/surfacesdl-graphics.cpp:2141 +#: backends/graphics/openglsdl/openglsdl-graphics.cpp:538 +msgid "Disabled aspect ratio correction" +msgstr "Corrección de proporción desactivada" + +#: backends/graphics/surfacesdl/surfacesdl-graphics.cpp:2196 +msgid "Active graphics filter:" +msgstr "Filtro de gráficos activo:" + +#: backends/graphics/surfacesdl/surfacesdl-graphics.cpp:2238 +#: backends/graphics/openglsdl/openglsdl-graphics.cpp:477 +msgid "Windowed mode" +msgstr "Modo en ventá" + +#: backends/graphics/opengl/opengl-graphics.cpp:135 +msgid "OpenGL Normal" +msgstr "OpenGL Normal" + +#: backends/graphics/opengl/opengl-graphics.cpp:136 +msgid "OpenGL Conserve" +msgstr "OpenGL Conserve" + +#: backends/graphics/opengl/opengl-graphics.cpp:137 +msgid "OpenGL Original" +msgstr "OpenGL Original" + +#: backends/graphics/openglsdl/openglsdl-graphics.cpp:415 +msgid "Current display mode" +msgstr "Modo de visualización actual" + +#: backends/graphics/openglsdl/openglsdl-graphics.cpp:428 +msgid "Current scale" +msgstr "Escala actual" + +#: backends/graphics/openglsdl/openglsdl-graphics.cpp:558 +msgid "Active filter mode: Linear" +msgstr "Modo de filtro activo: lineal" + +#: backends/graphics/openglsdl/openglsdl-graphics.cpp:560 +msgid "Active filter mode: Nearest" +msgstr "Modo de filtro activo: máis próximo" + +#: backends/platform/symbian/src/SymbianActions.cpp:38 +#: backends/platform/wince/CEActionsSmartphone.cpp:39 +msgid "Up" +msgstr "Arriba" + +#: backends/platform/symbian/src/SymbianActions.cpp:39 +#: backends/platform/wince/CEActionsSmartphone.cpp:40 +msgid "Down" +msgstr "Abaixo" + +#: backends/platform/symbian/src/SymbianActions.cpp:40 +#: backends/platform/wince/CEActionsSmartphone.cpp:41 +msgid "Left" +msgstr "Esquerda" + +#: backends/platform/symbian/src/SymbianActions.cpp:41 +#: backends/platform/wince/CEActionsSmartphone.cpp:42 +msgid "Right" +msgstr "Dereita" + +#: backends/platform/symbian/src/SymbianActions.cpp:46 +#: backends/platform/wince/CEActionsSmartphone.cpp:47 +msgid "Zone" +msgstr "Zona" + +#: backends/platform/symbian/src/SymbianActions.cpp:47 +#: backends/platform/wince/CEActionsPocket.cpp:54 +#: backends/platform/wince/CEActionsSmartphone.cpp:48 +msgid "Multi Function" +msgstr "Multifunción" + +#: backends/platform/symbian/src/SymbianActions.cpp:48 +msgid "Swap character" +msgstr "Cambiar carácter" + +#: backends/platform/symbian/src/SymbianActions.cpp:49 +msgid "Skip text" +msgstr "Omitir texto" + +#: backends/platform/symbian/src/SymbianActions.cpp:51 +msgid "Fast mode" +msgstr "Modo rápido" + +#: backends/platform/symbian/src/SymbianActions.cpp:53 +msgid "Debugger" +msgstr "Depurador" + +#: backends/platform/symbian/src/SymbianActions.cpp:54 +msgid "Global menu" +msgstr "Menú global" + +#: backends/platform/symbian/src/SymbianActions.cpp:55 +msgid "Virtual keyboard" +msgstr "Teclado virtual" + +#: backends/platform/symbian/src/SymbianActions.cpp:56 +msgid "Key mapper" +msgstr "Asignador de teclas" + +#: backends/events/symbiansdl/symbiansdl-events.cpp:184 +msgid "Do you want to quit ?" +msgstr "Queres saír?" + +#: backends/platform/wii/options.cpp:51 +msgid "Video" +msgstr "Vídeo" + +#: backends/platform/wii/options.cpp:54 +msgid "Current video mode:" +msgstr "Modo de vídeo actual:" + +#: backends/platform/wii/options.cpp:56 +msgid "Double-strike" +msgstr "Dobre" + +#: backends/platform/wii/options.cpp:60 +msgid "Horizontal underscan:" +msgstr "Marxe horizontal:" + +#: backends/platform/wii/options.cpp:66 +msgid "Vertical underscan:" +msgstr "Marxe vertical:" + +#: backends/platform/wii/options.cpp:71 +msgid "Input" +msgstr "Entrada" + +#: backends/platform/wii/options.cpp:74 +msgid "GC Pad sensitivity:" +msgstr "Sensibilidade do mando de GC:" + +#: backends/platform/wii/options.cpp:80 +msgid "GC Pad acceleration:" +msgstr "Aceleración do mando de GC:" + +#: backends/platform/wii/options.cpp:86 +msgid "DVD" +msgstr "DVD" + +#: backends/platform/wii/options.cpp:89 backends/platform/wii/options.cpp:101 +msgid "Status:" +msgstr "Estado:" + +#: backends/platform/wii/options.cpp:90 backends/platform/wii/options.cpp:102 +msgid "Unknown" +msgstr "Descoñecido" + +#: backends/platform/wii/options.cpp:93 +msgid "Mount DVD" +msgstr "Montar DVD" + +#: backends/platform/wii/options.cpp:94 +msgid "Unmount DVD" +msgstr "Desmontar DVD" + +#: backends/platform/wii/options.cpp:98 +msgid "SMB" +msgstr "SMB" + +#: backends/platform/wii/options.cpp:106 +msgid "Server:" +msgstr "Servidor:" + +#: backends/platform/wii/options.cpp:110 +msgid "Share:" +msgstr "Disco compartido:" + +#: backends/platform/wii/options.cpp:114 +msgid "Username:" +msgstr "Nome de usuario:" + +#: backends/platform/wii/options.cpp:118 +msgid "Password:" +msgstr "Contrasinal:" + +#: backends/platform/wii/options.cpp:121 +msgid "Init network" +msgstr "Conectar á rede" + +#: backends/platform/wii/options.cpp:123 +msgid "Mount SMB" +msgstr "Montar SMB" + +#: backends/platform/wii/options.cpp:124 +msgid "Unmount SMB" +msgstr "Desmontar SMB" + +#: backends/platform/wii/options.cpp:143 +msgid "DVD Mounted successfully" +msgstr "DVD montado con éxito" + +#: backends/platform/wii/options.cpp:146 +msgid "Error while mounting the DVD" +msgstr "Erro ao montar o DVD" + +#: backends/platform/wii/options.cpp:148 +msgid "DVD not mounted" +msgstr "DVD non montado" + +#: backends/platform/wii/options.cpp:161 +msgid "Network up, share mounted" +msgstr "Conexión á rede, disco montado" + +#: backends/platform/wii/options.cpp:163 +msgid "Network up" +msgstr "Conexión á rede" + +#: backends/platform/wii/options.cpp:166 +msgid ", error while mounting the share" +msgstr ", erro ao montar o disco" + +#: backends/platform/wii/options.cpp:168 +msgid ", share not mounted" +msgstr ", disco non montado" + +#: backends/platform/wii/options.cpp:174 +msgid "Network down" +msgstr "Non hai conexión á rede" + +#: backends/platform/wii/options.cpp:178 +msgid "Initializing network" +msgstr "Conectando á rede" + +#: backends/platform/wii/options.cpp:182 +msgid "Timeout while initializing network" +msgstr "Tempo de espera esgotado" + +#: backends/platform/wii/options.cpp:186 +#, c-format +msgid "Network not initialized (%d)" +msgstr "Erro de conexión á rede (%d)" + +#: backends/platform/wince/CEActionsPocket.cpp:46 +msgid "Hide Toolbar" +msgstr "Ocultar barra de ferramentas" + +#: backends/platform/wince/CEActionsPocket.cpp:47 +msgid "Show Keyboard" +msgstr "Mostrar teclado" + +#: backends/platform/wince/CEActionsPocket.cpp:48 +msgid "Sound on/off" +msgstr "Son si/non" + +#: backends/platform/wince/CEActionsPocket.cpp:49 +msgid "Right click" +msgstr "Botón secundario" + +#: backends/platform/wince/CEActionsPocket.cpp:50 +msgid "Show/Hide Cursor" +msgstr "Mostrar/ocultar cursor" + +#: backends/platform/wince/CEActionsPocket.cpp:51 +msgid "Free look" +msgstr "Vista libre" + +#: backends/platform/wince/CEActionsPocket.cpp:52 +msgid "Zoom up" +msgstr "Ampliar" + +#: backends/platform/wince/CEActionsPocket.cpp:53 +msgid "Zoom down" +msgstr "Reducir" + +#: backends/platform/wince/CEActionsPocket.cpp:55 +#: backends/platform/wince/CEActionsSmartphone.cpp:49 +msgid "Bind Keys" +msgstr "Vincular teclas" + +#: backends/platform/wince/CEActionsPocket.cpp:56 +msgid "Cursor Up" +msgstr "Arriba" + +#: backends/platform/wince/CEActionsPocket.cpp:57 +msgid "Cursor Down" +msgstr "Abaixo" + +#: backends/platform/wince/CEActionsPocket.cpp:58 +msgid "Cursor Left" +msgstr "Esquerda" + +#: backends/platform/wince/CEActionsPocket.cpp:59 +msgid "Cursor Right" +msgstr "Dereita" + +#: backends/platform/wince/CEActionsPocket.cpp:267 +#: backends/platform/wince/CEActionsSmartphone.cpp:231 +msgid "Do you want to load or save the game?" +msgstr "Queres cargar ou gardar a partida?" + +#: backends/platform/wince/CEActionsPocket.cpp:326 +#: backends/platform/wince/CEActionsSmartphone.cpp:287 +msgid " Are you sure you want to quit ? " +msgstr " Seguro que queres saír?" + +#: backends/platform/wince/CEActionsSmartphone.cpp:50 +msgid "Keyboard" +msgstr "Teclado" + +#: backends/platform/wince/CEActionsSmartphone.cpp:51 +msgid "Rotate" +msgstr "Rotar" + +#: backends/platform/wince/CELauncherDialog.cpp:56 +msgid "Using SDL driver " +msgstr "Empregando driver de SDL" + +#: backends/platform/wince/CELauncherDialog.cpp:60 +msgid "Display " +msgstr "Pantalla" + +#: backends/platform/wince/CELauncherDialog.cpp:83 +msgid "Do you want to perform an automatic scan ?" +msgstr "Queres realizar unha análise automática?" + +#: backends/platform/wince/wince-sdl.cpp:515 +msgid "Map right click action" +msgstr "Asignar acción de botón secundario" + +#: backends/platform/wince/wince-sdl.cpp:519 +msgid "You must map a key to the 'Right Click' action to play this game" +msgstr "" +"Debes asignar unha tecla á acción do botón secundario do rato para xogar" + +#: backends/platform/wince/wince-sdl.cpp:528 +msgid "Map hide toolbar action" +msgstr "Asignar acción Ocultar barra de ferramentas" + +#: backends/platform/wince/wince-sdl.cpp:532 +msgid "You must map a key to the 'Hide toolbar' action to play this game" +msgstr "" +"Debes asignar unha tecla á acción Ocultar barra de ferramentas para xogar" + +#: backends/platform/wince/wince-sdl.cpp:541 +msgid "Map Zoom Up action (optional)" +msgstr "Asignar acción de Ampliar (opcional)" + +#: backends/platform/wince/wince-sdl.cpp:544 +msgid "Map Zoom Down action (optional)" +msgstr "Asignar acción de Reducir (opcional)" + +#: backends/platform/wince/wince-sdl.cpp:552 +msgid "" +"Don't forget to map a key to 'Hide Toolbar' action to see the whole inventory" +msgstr "" +"Non esquezas asignar unha tecla á acción Ocultar barra de ferramentas para " +"ver o inventario completo" + +#: backends/events/default/default-events.cpp:191 +msgid "Do you really want to return to the Launcher?" +msgstr "Seguro que queres volver ao Iniciador?" + +#: backends/events/default/default-events.cpp:191 +msgid "Launcher" +msgstr "Iniciador" + +#: backends/events/default/default-events.cpp:213 +msgid "Do you really want to quit?" +msgstr "Seguro que queres saír?" + +#: backends/events/gph/gph-events.cpp:386 +#: backends/events/gph/gph-events.cpp:429 +#: backends/events/openpandora/op-events.cpp:168 +msgid "Touchscreen 'Tap Mode' - Left Click" +msgstr "Modo pantalla táctil: premer botón primario" + +#: backends/events/gph/gph-events.cpp:388 +#: backends/events/gph/gph-events.cpp:431 +#: backends/events/openpandora/op-events.cpp:170 +msgid "Touchscreen 'Tap Mode' - Right Click" +msgstr "Modo pantalla táctil: premer botón secundario" + +#: backends/events/gph/gph-events.cpp:390 +#: backends/events/gph/gph-events.cpp:433 +#: backends/events/openpandora/op-events.cpp:172 +msgid "Touchscreen 'Tap Mode' - Hover (No Click)" +msgstr "Modo pantalla táctil: apuntar co rato" + +#: backends/events/gph/gph-events.cpp:410 +msgid "Maximum Volume" +msgstr "Volume máximo" + +#: backends/events/gph/gph-events.cpp:412 +msgid "Increasing Volume" +msgstr "Subindo volume" + +#: backends/events/gph/gph-events.cpp:418 +msgid "Minimal Volume" +msgstr "Volume mínimo" + +#: backends/events/gph/gph-events.cpp:420 +msgid "Decreasing Volume" +msgstr "Baixando volume" + +#: backends/events/openpandora/op-events.cpp:174 +#, fuzzy +msgid "Touchscreen 'Tap Mode' - Hover (DPad Clicks)" +msgstr "Modo pantalla táctil: apuntar co rato" + +#: backends/updates/macosx/macosx-updates.mm:67 +msgid "Check for Updates..." +msgstr "Buscar actualizacións..." + +#: backends/platform/bada/form.cpp:269 +msgid "Right Click Once" +msgstr "Botón secundario unha vez" + +#: backends/platform/bada/form.cpp:277 +msgid "Move Only" +msgstr "Mover unicamente" + +#: backends/platform/bada/form.cpp:291 +msgid "Escape Key" +msgstr "ESC" + +#: backends/platform/bada/form.cpp:296 +msgid "Game Menu" +msgstr "Menú do xogo" + +#: backends/platform/bada/form.cpp:301 +msgid "Show Keypad" +msgstr "Mostrar teclado numérico" + +#: backends/platform/bada/form.cpp:309 +msgid "Control Mouse" +msgstr "Rato" + +#: backends/events/maemosdl/maemosdl-events.cpp:192 +msgid "Clicking Enabled" +msgstr "Premer activado" + +#: backends/events/maemosdl/maemosdl-events.cpp:192 +msgid "Clicking Disabled" +msgstr "Premer desactivado" diff --git a/po/hu_HU.po b/po/hu_HU.po index 828659dea6..03b6c1a405 100644 --- a/po/hu_HU.po +++ b/po/hu_HU.po @@ -1,5 +1,5 @@ # Hungarian translation for ScummVM. -# Copyright (C) 2010-2011 ScummVM Team +# Copyright (C) 2010-2013 ScummVM Team # This file is distributed under the same license as the ScummVM package. # George Kormendi <grubycza@hotmail.com>, 2010. # @@ -7,18 +7,17 @@ msgid "" msgstr "" "Project-Id-Version: ScummVM 1.3.0svn\n" "Report-Msgid-Bugs-To: scummvm-devel@lists.sf.net\n" -"POT-Creation-Date: 2012-07-08 12:25+0100\n" -"PO-Revision-Date: 2012-07-09 05:58+0100\n" +"POT-Creation-Date: 2012-12-01 17:22+0000\n" +"PO-Revision-Date: 2012-12-22 09:15+0100\n" "Last-Translator: George Kormendi <grubycza@hotmail.com>\n" "Language-Team: Hungarian\n" +"Language: Magyar\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=iso-8859-2\n" "Content-Transfer-Encoding: 8bit\n" -"Language: Magyar\n" -"Plural-Forms: nplurals=2; plural=(n != 1)\n" -"X-Poedit-Language: Hungarian\n" -"X-Poedit-Country: HUNGARY\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Poedit-SourceCharset: iso-8859-1\n" +"X-Generator: Poedit 1.5.4\n" #: gui/about.cpp:91 #, c-format @@ -48,10 +47,11 @@ msgstr "Feljebb" #: gui/browser.cpp:69 gui/chooser.cpp:45 gui/KeysDialog.cpp:43 #: gui/launcher.cpp:345 gui/massadd.cpp:94 gui/options.cpp:1228 -#: gui/saveload.cpp:64 gui/saveload.cpp:173 gui/themebrowser.cpp:54 -#: engines/engine.cpp:442 engines/scumm/dialogs.cpp:190 -#: engines/sword1/control.cpp:865 engines/parallaction/saveload.cpp:274 -#: backends/platform/wii/options.cpp:48 +#: gui/saveload-dialog.cpp:215 gui/saveload-dialog.cpp:275 +#: gui/saveload-dialog.cpp:545 gui/saveload-dialog.cpp:919 +#: gui/themebrowser.cpp:54 engines/engine.cpp:442 +#: engines/scumm/dialogs.cpp:190 engines/sword1/control.cpp:867 +#: engines/parallaction/saveload.cpp:274 backends/platform/wii/options.cpp:48 #: backends/events/default/default-events.cpp:191 #: backends/events/default/default-events.cpp:213 msgid "Cancel" @@ -72,15 +72,15 @@ msgstr "Bezár" msgid "Mouse click" msgstr "Egérkattintás" -#: gui/gui-manager.cpp:122 base/main.cpp:300 +#: gui/gui-manager.cpp:122 base/main.cpp:301 msgid "Display keyboard" msgstr "Billentyûzet beállítások" -#: gui/gui-manager.cpp:126 base/main.cpp:304 +#: gui/gui-manager.cpp:126 base/main.cpp:305 msgid "Remap keys" msgstr "Billentyûk átállítása" -#: gui/gui-manager.cpp:129 base/main.cpp:307 +#: gui/gui-manager.cpp:129 base/main.cpp:308 msgid "Toggle FullScreen" msgstr "Teljesképernyõ kapcsoló" @@ -94,16 +94,16 @@ msgstr "Kiosztás" #: gui/KeysDialog.cpp:42 gui/launcher.cpp:346 gui/launcher.cpp:1001 #: gui/launcher.cpp:1005 gui/massadd.cpp:91 gui/options.cpp:1229 -#: engines/engine.cpp:361 engines/engine.cpp:372 engines/scumm/dialogs.cpp:192 -#: engines/scumm/scumm.cpp:1775 engines/agos/animation.cpp:561 -#: engines/groovie/script.cpp:420 engines/sky/compact.cpp:131 -#: engines/sky/compact.cpp:141 engines/sword1/animation.cpp:539 -#: engines/sword1/animation.cpp:560 engines/sword1/animation.cpp:570 -#: engines/sword1/animation.cpp:577 engines/sword1/control.cpp:865 -#: engines/sword1/logic.cpp:1633 engines/sword2/animation.cpp:435 -#: engines/sword2/animation.cpp:455 engines/sword2/animation.cpp:465 -#: engines/sword2/animation.cpp:474 engines/parallaction/saveload.cpp:274 -#: backends/platform/wii/options.cpp:47 +#: gui/saveload-dialog.cpp:920 engines/engine.cpp:361 engines/engine.cpp:372 +#: engines/scumm/dialogs.cpp:192 engines/scumm/scumm.cpp:1776 +#: engines/agos/animation.cpp:558 engines/groovie/script.cpp:420 +#: engines/sky/compact.cpp:131 engines/sky/compact.cpp:141 +#: engines/sword1/animation.cpp:519 engines/sword1/animation.cpp:540 +#: engines/sword1/animation.cpp:550 engines/sword1/animation.cpp:557 +#: engines/sword1/control.cpp:867 engines/sword1/logic.cpp:1633 +#: engines/sword2/animation.cpp:419 engines/sword2/animation.cpp:439 +#: engines/sword2/animation.cpp:449 engines/sword2/animation.cpp:458 +#: engines/parallaction/saveload.cpp:274 backends/platform/wii/options.cpp:47 #: backends/platform/wince/CELauncherDialog.cpp:54 msgid "OK" msgstr "OK" @@ -356,7 +356,7 @@ msgstr "Ez a játékazonosító ID már foglalt, Válassz egy másikat." msgid "~Q~uit" msgstr "Kilépés" -#: gui/launcher.cpp:621 backends/platform/sdl/macosx/appmenu_osx.mm:96 +#: gui/launcher.cpp:621 backends/platform/sdl/macosx/appmenu_osx.mm:95 msgid "Quit ScummVM" msgstr "ScummVM bezárása" @@ -364,7 +364,7 @@ msgstr "ScummVM bezárása" msgid "A~b~out..." msgstr "Névjegy" -#: gui/launcher.cpp:622 backends/platform/sdl/macosx/appmenu_osx.mm:70 +#: gui/launcher.cpp:622 backends/platform/sdl/macosx/appmenu_osx.mm:69 msgid "About ScummVM" msgstr "ScummVM névjegy" @@ -439,13 +439,13 @@ msgstr "Keresés a játéklistában" msgid "Search:" msgstr "Keresés:" -#: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/mohawk/myst.cpp:255 +#: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/mohawk/myst.cpp:245 #: engines/mohawk/riven.cpp:716 engines/cruise/menu.cpp:214 msgid "Load game:" msgstr "Játék betöltése:" #: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/scumm/dialogs.cpp:188 -#: engines/mohawk/myst.cpp:255 engines/mohawk/riven.cpp:716 +#: engines/mohawk/myst.cpp:245 engines/mohawk/riven.cpp:716 #: engines/cruise/menu.cpp:214 backends/platform/wince/CEActionsPocket.cpp:267 #: backends/platform/wince/CEActionsSmartphone.cpp:231 msgid "Load" @@ -918,68 +918,101 @@ msgstr "" "A kiválasztott téma nem támogatja a nyelvedet. Ha használni akarod ezt a " "témát, elõszõr válts át egy másik nyelvre." -#: gui/saveload.cpp:59 gui/saveload.cpp:257 +#: gui/saveload-dialog.cpp:166 +msgid "List view" +msgstr "Lista nézet" + +#: gui/saveload-dialog.cpp:167 +msgid "Grid view" +msgstr "Rács nézet" + +#: gui/saveload-dialog.cpp:210 gui/saveload-dialog.cpp:358 msgid "No date saved" msgstr "Dátum nincs mentve" -#: gui/saveload.cpp:60 gui/saveload.cpp:258 +#: gui/saveload-dialog.cpp:211 gui/saveload-dialog.cpp:359 msgid "No time saved" msgstr "Idõ nincs mentve" -#: gui/saveload.cpp:61 gui/saveload.cpp:259 +#: gui/saveload-dialog.cpp:212 gui/saveload-dialog.cpp:360 msgid "No playtime saved" msgstr "Játékidõ nincs mentve" -#: gui/saveload.cpp:68 gui/saveload.cpp:173 +#: gui/saveload-dialog.cpp:219 gui/saveload-dialog.cpp:275 msgid "Delete" msgstr "Töröl" -#: gui/saveload.cpp:172 +#: gui/saveload-dialog.cpp:274 msgid "Do you really want to delete this savegame?" msgstr "Biztos hogy törölni akarod ezt a játékállást?" -#: gui/saveload.cpp:282 +#: gui/saveload-dialog.cpp:383 gui/saveload-dialog.cpp:872 msgid "Date: " msgstr "Dátum:" -#: gui/saveload.cpp:286 +#: gui/saveload-dialog.cpp:387 gui/saveload-dialog.cpp:878 msgid "Time: " msgstr "Idõ:" -#: gui/saveload.cpp:292 +#: gui/saveload-dialog.cpp:393 gui/saveload-dialog.cpp:886 msgid "Playtime: " msgstr "Játékidõ:" -#: gui/saveload.cpp:305 gui/saveload.cpp:372 +#: gui/saveload-dialog.cpp:406 gui/saveload-dialog.cpp:494 msgid "Untitled savestate" msgstr "Névtelen játékállás" +#: gui/saveload-dialog.cpp:546 +msgid "Next" +msgstr "Következõ" + +#: gui/saveload-dialog.cpp:549 +msgid "Prev" +msgstr "Elõzõ" + +#: gui/saveload-dialog.cpp:736 +msgid "New Save" +msgstr "Új Mentés" + +#: gui/saveload-dialog.cpp:736 +msgid "Create a new save game" +msgstr "Új játékmentés készítése" + +#: gui/saveload-dialog.cpp:865 +msgid "Name: " +msgstr "Név:" + +#: gui/saveload-dialog.cpp:937 +#, c-format +msgid "Enter a description for slot %d:" +msgstr "Adj meg egy leírást a %d slothoz:" + #: gui/themebrowser.cpp:44 msgid "Select a Theme" msgstr "Válassz témát" -#: gui/ThemeEngine.cpp:335 +#: gui/ThemeEngine.cpp:337 msgid "Disabled GFX" msgstr "GFX letiltva" -#: gui/ThemeEngine.cpp:335 +#: gui/ThemeEngine.cpp:337 msgctxt "lowres" msgid "Disabled GFX" msgstr "GFX letiltva" -#: gui/ThemeEngine.cpp:336 +#: gui/ThemeEngine.cpp:338 msgid "Standard Renderer (16bpp)" msgstr "Standard leképezõ (16bpp)" -#: gui/ThemeEngine.cpp:336 +#: gui/ThemeEngine.cpp:338 msgid "Standard (16bpp)" msgstr "Standard (16bpp)" -#: gui/ThemeEngine.cpp:338 +#: gui/ThemeEngine.cpp:340 msgid "Antialiased Renderer (16bpp)" msgstr "Élsimításos leképezõ (16bpp)" -#: gui/ThemeEngine.cpp:338 +#: gui/ThemeEngine.cpp:340 msgid "Antialiased (16bpp)" msgstr "Élsimított (16bpp)" @@ -987,35 +1020,35 @@ msgstr "Élsimított (16bpp)" msgid "Clear value" msgstr "Érték törlése" -#: base/main.cpp:209 +#: base/main.cpp:210 #, c-format msgid "Engine does not support debug level '%s'" msgstr "A motor nem támogatja a '%s' debug szintet" -#: base/main.cpp:287 +#: base/main.cpp:288 msgid "Menu" msgstr "Menü" -#: base/main.cpp:290 backends/platform/symbian/src/SymbianActions.cpp:45 +#: base/main.cpp:291 backends/platform/symbian/src/SymbianActions.cpp:45 #: backends/platform/wince/CEActionsPocket.cpp:45 #: backends/platform/wince/CEActionsSmartphone.cpp:46 msgid "Skip" msgstr "Tovább" -#: base/main.cpp:293 backends/platform/symbian/src/SymbianActions.cpp:50 +#: base/main.cpp:294 backends/platform/symbian/src/SymbianActions.cpp:50 #: backends/platform/wince/CEActionsPocket.cpp:42 msgid "Pause" msgstr "Szünet" -#: base/main.cpp:296 +#: base/main.cpp:297 msgid "Skip line" msgstr "Sor átlépése" -#: base/main.cpp:467 +#: base/main.cpp:468 msgid "Error running game:" msgstr "Hiba a játék futtatásakor:" -#: base/main.cpp:491 +#: base/main.cpp:492 msgid "Could not find any engine capable of running the selected game" msgstr "Nem található olyan játékmotor ami a választott játékot támogatja" @@ -1083,16 +1116,16 @@ msgstr "Felhasználói megszakítás" msgid "Unknown error" msgstr "Ismeretlen hiba" -#: engines/advancedDetector.cpp:324 +#: engines/advancedDetector.cpp:316 #, c-format msgid "The game in '%s' seems to be unknown." msgstr "A '%s' játék ismeretlennek tûnik." -#: engines/advancedDetector.cpp:325 +#: engines/advancedDetector.cpp:317 msgid "Please, report the following data to the ScummVM team along with name" msgstr "Kérlek jelezd a ScummVM csapatnak a következõ adatokat, együtt a játék" -#: engines/advancedDetector.cpp:327 +#: engines/advancedDetector.cpp:319 msgid "of the game you tried to add and its version/language/etc.:" msgstr "címével és megbízható adataival játékverzió/nyelv(ek)/stb.:" @@ -1130,13 +1163,13 @@ msgid "~R~eturn to Launcher" msgstr "Visszatérés az indítóba" #: engines/dialogs.cpp:115 engines/agi/saveload.cpp:803 -#: engines/cruise/menu.cpp:212 engines/sci/engine/kfile.cpp:735 +#: engines/cruise/menu.cpp:212 engines/sci/engine/kfile.cpp:742 msgid "Save game:" msgstr "Játék mentése:" #: engines/dialogs.cpp:115 engines/agi/saveload.cpp:803 #: engines/scumm/dialogs.cpp:187 engines/cruise/menu.cpp:212 -#: engines/sci/engine/kfile.cpp:735 +#: engines/sci/engine/kfile.cpp:742 #: backends/platform/symbian/src/SymbianActions.cpp:44 #: backends/platform/wince/CEActionsPocket.cpp:43 #: backends/platform/wince/CEActionsPocket.cpp:267 @@ -1244,21 +1277,21 @@ msgstr "" msgid "Start anyway" msgstr "Indítás így is" -#: engines/agi/detection.cpp:145 engines/dreamweb/detection.cpp:47 -#: engines/sci/detection.cpp:390 +#: engines/agi/detection.cpp:142 engines/dreamweb/detection.cpp:47 +#: engines/sci/detection.cpp:393 msgid "Use original save/load screens" msgstr "Eredeti ment/tölt képernyõk használata" -#: engines/agi/detection.cpp:146 engines/dreamweb/detection.cpp:48 -#: engines/sci/detection.cpp:391 +#: engines/agi/detection.cpp:143 engines/dreamweb/detection.cpp:48 +#: engines/sci/detection.cpp:394 msgid "Use the original save/load screens, instead of the ScummVM ones" msgstr "Az eredeti mentés/betöltés képernyõ használata a ScummVM képek helyett" -#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:831 +#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:838 msgid "Restore game:" msgstr "Játékmenet visszaállítása:" -#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:831 +#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:838 msgid "Restore" msgstr "Visszaállítás" @@ -1270,27 +1303,27 @@ msgstr "Fényes paletta mód használata" msgid "Display graphics using the game's bright palette" msgstr "Grafikus megjelenítésre használja a játék fényes palettáját" -#: engines/sci/detection.cpp:370 +#: engines/sci/detection.cpp:373 msgid "EGA undithering" msgstr "EGA szinjavítás" -#: engines/sci/detection.cpp:371 +#: engines/sci/detection.cpp:374 msgid "Enable undithering in EGA games" msgstr "Undithering engedélyezése EGA játékokban" -#: engines/sci/detection.cpp:380 +#: engines/sci/detection.cpp:383 msgid "Prefer digital sound effects" msgstr "Digitális hangeffektusok elõnyben" -#: engines/sci/detection.cpp:381 +#: engines/sci/detection.cpp:384 msgid "Prefer digital sound effects instead of synthesized ones" msgstr "Digitális hanghatások elõnyben a szintetizáltakkal szemben" -#: engines/sci/detection.cpp:400 +#: engines/sci/detection.cpp:403 msgid "Use IMF/Yamaha FB-01 for MIDI output" msgstr "IMF/Yamaha FB-01 használata MIDI kimentre" -#: engines/sci/detection.cpp:401 +#: engines/sci/detection.cpp:404 msgid "" "Use an IBM Music Feature card or a Yamaha FB-01 FM synth module for MIDI " "output" @@ -1298,28 +1331,28 @@ msgstr "" "IBM Music Feature kártya vagy Yamaha FB-01 FM szintetizátor modul használata " "MIDI kimenetre" -#: engines/sci/detection.cpp:411 +#: engines/sci/detection.cpp:414 msgid "Use CD audio" msgstr "CD audió használata" -#: engines/sci/detection.cpp:412 +#: engines/sci/detection.cpp:415 msgid "Use CD audio instead of in-game audio, if available" msgstr "CD audió használata a játékban lévõvel szemben, ha elérhetõ" -#: engines/sci/detection.cpp:422 +#: engines/sci/detection.cpp:425 msgid "Use Windows cursors" msgstr "Windows kurzorok használata" -#: engines/sci/detection.cpp:423 +#: engines/sci/detection.cpp:426 msgid "" "Use the Windows cursors (smaller and monochrome) instead of the DOS ones" msgstr "Windows kurzorok használata (kisebb és monokróm) a DOS-osok helyett " -#: engines/sci/detection.cpp:433 +#: engines/sci/detection.cpp:436 msgid "Use silver cursors" msgstr "Ezüst kurzor használata" -#: engines/sci/detection.cpp:434 +#: engines/sci/detection.cpp:437 msgid "" "Use the alternate set of silver cursors, instead of the normal golden ones" msgstr "Alternatív ezüst kurzorszett használata, a normál arany helyett" @@ -1971,7 +2004,7 @@ msgstr "Jobbra repülés" msgid "Fly to lower right" msgstr "Jobbra le repülés" -#: engines/scumm/scumm.cpp:1773 +#: engines/scumm/scumm.cpp:1774 #, c-format msgid "" "Native MIDI support requires the Roland Upgrade from LucasArts,\n" @@ -1980,7 +2013,7 @@ msgstr "" "Native MIDI támogatáshoz kell a Roland Upgrade a LucasArts-tól,\n" "a %s hiányzik. AdLib-ot használok helyette." -#: engines/scumm/scumm.cpp:2278 engines/agos/saveload.cpp:202 +#: engines/scumm/scumm.cpp:2295 engines/agos/saveload.cpp:220 #, c-format msgid "" "Failed to save game state to file:\n" @@ -1991,7 +2024,7 @@ msgstr "" "\n" "%s fájlba nem sikerült" -#: engines/scumm/scumm.cpp:2285 engines/agos/saveload.cpp:167 +#: engines/scumm/scumm.cpp:2302 engines/agos/saveload.cpp:185 #, c-format msgid "" "Failed to load game state from file:\n" @@ -2002,7 +2035,7 @@ msgstr "" "\n" "%s fájlból nem sikerült" -#: engines/scumm/scumm.cpp:2297 engines/agos/saveload.cpp:210 +#: engines/scumm/scumm.cpp:2314 engines/agos/saveload.cpp:228 #, c-format msgid "" "Successfully saved game state in file:\n" @@ -2013,7 +2046,7 @@ msgstr "" "\n" "%s fájlba elkészült" -#: engines/scumm/scumm.cpp:2512 +#: engines/scumm/scumm.cpp:2529 msgid "" "Usually, Maniac Mansion would start now. But ScummVM doesn't do that yet. To " "play it, go to 'Add Game' in the ScummVM start menu and select the 'Maniac' " @@ -2049,17 +2082,17 @@ msgstr "Fõ~M~enü" msgid "~W~ater Effect Enabled" msgstr "Vízeffektus engedélyezve" -#: engines/agos/animation.cpp:560 +#: engines/agos/animation.cpp:557 #, c-format msgid "Cutscene file '%s' not found!" msgstr "'%s' átvezetõ fájl nem található" #: engines/gob/inter_playtoons.cpp:256 engines/gob/inter_v2.cpp:1287 -#: engines/tinsel/saveload.cpp:502 +#: engines/tinsel/saveload.cpp:532 msgid "Failed to load game state from file." msgstr "Játékállás betöltése fájlból nem sikerült." -#: engines/gob/inter_v2.cpp:1357 engines/tinsel/saveload.cpp:515 +#: engines/gob/inter_v2.cpp:1357 engines/tinsel/saveload.cpp:545 msgid "Failed to save game state to file." msgstr "Játékállás mentése fájlba nem sikerült." @@ -2177,7 +2210,7 @@ msgstr "Opciók" msgid "Choose Spell" msgstr "Válassz varázslatot" -#: engines/kyra/sound_midi.cpp:475 +#: engines/kyra/sound_midi.cpp:477 msgid "" "You appear to be using a General MIDI device,\n" "but your game only supports Roland MT32 MIDI.\n" @@ -2191,13 +2224,13 @@ msgstr "" "General MIDIre. Továbbra is lehetséges hogy\n" "néhány hangsáv helytelenül hangzik." -#: engines/queen/queen.cpp:59 engines/sky/detection.cpp:44 -msgid "Floppy intro" -msgstr "Floppy intro" +#: engines/queen/queen.cpp:59 +msgid "Alternative intro" +msgstr "Alternatív intro" -#: engines/queen/queen.cpp:60 engines/sky/detection.cpp:45 -msgid "Use the floppy version's intro (CD version only)" -msgstr "A floppy verzió intro használata (csak CD verziónál)" +#: engines/queen/queen.cpp:60 +msgid "Use an alternative game intro (CD version only)" +msgstr "Alternatív játékintro használata (csak CD verziónál)" #: engines/sky/compact.cpp:130 msgid "" @@ -2215,25 +2248,33 @@ msgstr "" "A \"sky.cpt\" fájl mérete nem megfelelõ.\n" "Töltsd le a www.scummvm.org oldaláról" -#: engines/sword1/animation.cpp:539 +#: engines/sky/detection.cpp:44 +msgid "Floppy intro" +msgstr "Floppy intro" + +#: engines/sky/detection.cpp:45 +msgid "Use the floppy version's intro (CD version only)" +msgstr "A floppy verzió intro használata (csak CD verziónál)" + +#: engines/sword1/animation.cpp:519 #, c-format msgid "PSX stream cutscene '%s' cannot be played in paletted mode" msgstr "'%s' PSX stream átvezetõ nem játszható le paletta módban" -#: engines/sword1/animation.cpp:560 engines/sword2/animation.cpp:455 +#: engines/sword1/animation.cpp:540 engines/sword2/animation.cpp:439 msgid "DXA cutscenes found but ScummVM has been built without zlib support" msgstr "DXA átvezetõ elérhetõ, de a ScummVM zlib támogatás nincs lefordítva" -#: engines/sword1/animation.cpp:570 engines/sword2/animation.cpp:465 +#: engines/sword1/animation.cpp:550 engines/sword2/animation.cpp:449 msgid "MPEG2 cutscenes are no longer supported" msgstr "MPEG2 átvezetõk már nem támogatottak" -#: engines/sword1/animation.cpp:576 engines/sword2/animation.cpp:473 +#: engines/sword1/animation.cpp:556 engines/sword2/animation.cpp:457 #, c-format msgid "Cutscene '%s' not found" msgstr "'%s' átvezetõ nem található" -#: engines/sword1/control.cpp:863 +#: engines/sword1/control.cpp:865 msgid "" "ScummVM found that you have old savefiles for Broken Sword 1 that should be " "converted.\n" @@ -2251,7 +2292,7 @@ msgstr "" "Nyomj OK-t az átalakításhoz, vagy rákérdezzek ha legközelebb elindítod a " "játékot.\n" -#: engines/sword1/control.cpp:1232 +#: engines/sword1/control.cpp:1234 #, c-format msgid "" "Target new save game already exists!\n" @@ -2260,11 +2301,11 @@ msgstr "" "A választott játékmentés már létezik!\n" "Megtartod a régi játékmentést (%s) vagy kicseréled az újra (%s)?\n" -#: engines/sword1/control.cpp:1235 +#: engines/sword1/control.cpp:1237 msgid "Keep the old one" msgstr "A régit megtartom" -#: engines/sword1/control.cpp:1235 +#: engines/sword1/control.cpp:1237 msgid "Keep the new one" msgstr "Az újat megtartom" @@ -2272,7 +2313,7 @@ msgstr "Az újat megtartom" msgid "This is the end of the Broken Sword 1 Demo" msgstr "Ez a Broken Sword 1 Demo vége" -#: engines/sword2/animation.cpp:435 +#: engines/sword2/animation.cpp:419 msgid "" "PSX cutscenes found but ScummVM has been built without RGB color support" msgstr "" @@ -2287,6 +2328,19 @@ msgstr "Tárgycimke látható" msgid "Show labels for objects on mouse hover" msgstr "Tárgycimke látható ha az egér felette van" +#: engines/teenagent/resources.cpp:94 +msgid "" +"You're missing the 'teenagent.dat' file. Get it from the ScummVM website" +msgstr "Hiányzik a 'teenagent.dat' fájl. Szerezd be a ScummVM website-ról" + +#: engines/teenagent/resources.cpp:115 +msgid "" +"The teenagent.dat file is compressed and zlib hasn't been included in this " +"executable. Please decompress it" +msgstr "" +"A teenagent.dat fájl tömörített és a zlib nem része ennek a futtatható " +"állománynak. Kérlek tömörítsd ki" + #: engines/parallaction/saveload.cpp:133 #, c-format msgid "" @@ -2391,7 +2445,7 @@ msgstr "Nincs zene" msgid "Amiga Audio Emulator" msgstr "Amiga Audió Emulátor" -#: audio/softsynth/adlib.cpp:1593 +#: audio/softsynth/adlib.cpp:2284 msgid "AdLib Emulator" msgstr "AdLib Emulátor" @@ -2535,11 +2589,11 @@ msgstr "Touchpad mód engedélyezve." msgid "Touchpad mode disabled." msgstr "Touchpad mód letiltva." -#: backends/platform/maemo/maemo.cpp:205 +#: backends/platform/maemo/maemo.cpp:209 msgid "Click Mode" msgstr "Kattintás Mód" -#: backends/platform/maemo/maemo.cpp:211 +#: backends/platform/maemo/maemo.cpp:215 #: backends/platform/symbian/src/SymbianActions.cpp:42 #: backends/platform/wince/CEActionsPocket.cpp:60 #: backends/platform/wince/CEActionsSmartphone.cpp:43 @@ -2547,35 +2601,35 @@ msgstr "Kattintás Mód" msgid "Left Click" msgstr "Bal katt" -#: backends/platform/maemo/maemo.cpp:214 +#: backends/platform/maemo/maemo.cpp:218 msgid "Middle Click" msgstr "Középsõ katt" -#: backends/platform/maemo/maemo.cpp:217 +#: backends/platform/maemo/maemo.cpp:221 #: backends/platform/symbian/src/SymbianActions.cpp:43 #: backends/platform/wince/CEActionsSmartphone.cpp:44 #: backends/platform/bada/form.cpp:273 msgid "Right Click" msgstr "Jobb katt" -#: backends/platform/sdl/macosx/appmenu_osx.mm:78 +#: backends/platform/sdl/macosx/appmenu_osx.mm:77 msgid "Hide ScummVM" msgstr "ScummVM elrejtése" -#: backends/platform/sdl/macosx/appmenu_osx.mm:83 +#: backends/platform/sdl/macosx/appmenu_osx.mm:82 msgid "Hide Others" msgstr "Többi elrejtése" -#: backends/platform/sdl/macosx/appmenu_osx.mm:88 +#: backends/platform/sdl/macosx/appmenu_osx.mm:87 msgid "Show All" msgstr "Mutasd mind" -#: backends/platform/sdl/macosx/appmenu_osx.mm:110 -#: backends/platform/sdl/macosx/appmenu_osx.mm:121 +#: backends/platform/sdl/macosx/appmenu_osx.mm:109 +#: backends/platform/sdl/macosx/appmenu_osx.mm:120 msgid "Window" msgstr "Ablak" -#: backends/platform/sdl/macosx/appmenu_osx.mm:115 +#: backends/platform/sdl/macosx/appmenu_osx.mm:114 msgid "Minimize" msgstr "Kis méret" @@ -2953,41 +3007,45 @@ msgstr "Indítópult" msgid "Do you really want to quit?" msgstr "Biztos hogy ki akarsz lépni ?" -#: backends/events/gph/gph-events.cpp:338 -#: backends/events/gph/gph-events.cpp:381 -#: backends/events/openpandora/op-events.cpp:139 +#: backends/events/gph/gph-events.cpp:386 +#: backends/events/gph/gph-events.cpp:429 +#: backends/events/openpandora/op-events.cpp:168 msgid "Touchscreen 'Tap Mode' - Left Click" msgstr "Érintõképernyõ 'Tap Mód' - Bal katt" -#: backends/events/gph/gph-events.cpp:340 -#: backends/events/gph/gph-events.cpp:383 -#: backends/events/openpandora/op-events.cpp:141 +#: backends/events/gph/gph-events.cpp:388 +#: backends/events/gph/gph-events.cpp:431 +#: backends/events/openpandora/op-events.cpp:170 msgid "Touchscreen 'Tap Mode' - Right Click" msgstr "Érintõképernyõ 'Tap Mód' - Jobb katt" -#: backends/events/gph/gph-events.cpp:342 -#: backends/events/gph/gph-events.cpp:385 -#: backends/events/openpandora/op-events.cpp:143 +#: backends/events/gph/gph-events.cpp:390 +#: backends/events/gph/gph-events.cpp:433 +#: backends/events/openpandora/op-events.cpp:172 msgid "Touchscreen 'Tap Mode' - Hover (No Click)" msgstr "Érintõképernyõ 'Tap Mód' - Lebegõ (Nincs katt)" -#: backends/events/gph/gph-events.cpp:362 +#: backends/events/gph/gph-events.cpp:410 msgid "Maximum Volume" msgstr "Maximum Hangerõ" -#: backends/events/gph/gph-events.cpp:364 +#: backends/events/gph/gph-events.cpp:412 msgid "Increasing Volume" msgstr "Hangerõ növelése" -#: backends/events/gph/gph-events.cpp:370 +#: backends/events/gph/gph-events.cpp:418 msgid "Minimal Volume" msgstr "Minimum Hangerõ" -#: backends/events/gph/gph-events.cpp:372 +#: backends/events/gph/gph-events.cpp:420 msgid "Decreasing Volume" msgstr "Hangerõ csökkentése" -#: backends/updates/macosx/macosx-updates.mm:65 +#: backends/events/openpandora/op-events.cpp:174 +msgid "Touchscreen 'Tap Mode' - Hover (DPad Clicks)" +msgstr "Érintõképernyõ 'Érintésmód' - Lebegõ (DPad katt)" + +#: backends/updates/macosx/macosx-updates.mm:67 msgid "Check for Updates..." msgstr "Frissítések keresése..." diff --git a/po/it_IT.po b/po/it_IT.po index 164171ce8f..220e18fce5 100644 --- a/po/it_IT.po +++ b/po/it_IT.po @@ -1,5 +1,5 @@ # Italian translation for ScummVM. -# Copyright (C) 2010-2012 ScummVM Team +# Copyright (C) 2010-2013 ScummVM Team # This file is distributed under the same license as the ScummVM package. # Matteo 'Maff' Angelino <matteo.maff at gmail dot com>, 2010. # @@ -7,14 +7,14 @@ msgid "" msgstr "" "Project-Id-Version: ScummVM 1.3.0svn\n" "Report-Msgid-Bugs-To: scummvm-devel@lists.sf.net\n" -"POT-Creation-Date: 2012-07-08 12:25+0100\n" +"POT-Creation-Date: 2012-12-01 17:22+0000\n" "PO-Revision-Date: 2012-07-09 09:30+0100\n" "Last-Translator: Matteo 'Maff' Angelino <matteo.maff at gmail dot com>\n" "Language-Team: Italian\n" +"Language: Italiano\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=iso-8859-1\n" "Content-Transfer-Encoding: 8bit\n" -"Language: Italiano\n" #: gui/about.cpp:91 #, c-format @@ -44,10 +44,11 @@ msgstr "Su" #: gui/browser.cpp:69 gui/chooser.cpp:45 gui/KeysDialog.cpp:43 #: gui/launcher.cpp:345 gui/massadd.cpp:94 gui/options.cpp:1228 -#: gui/saveload.cpp:64 gui/saveload.cpp:173 gui/themebrowser.cpp:54 -#: engines/engine.cpp:442 engines/scumm/dialogs.cpp:190 -#: engines/sword1/control.cpp:865 engines/parallaction/saveload.cpp:274 -#: backends/platform/wii/options.cpp:48 +#: gui/saveload-dialog.cpp:215 gui/saveload-dialog.cpp:275 +#: gui/saveload-dialog.cpp:545 gui/saveload-dialog.cpp:919 +#: gui/themebrowser.cpp:54 engines/engine.cpp:442 +#: engines/scumm/dialogs.cpp:190 engines/sword1/control.cpp:867 +#: engines/parallaction/saveload.cpp:274 backends/platform/wii/options.cpp:48 #: backends/events/default/default-events.cpp:191 #: backends/events/default/default-events.cpp:213 msgid "Cancel" @@ -68,15 +69,15 @@ msgstr "Chiudi" msgid "Mouse click" msgstr "Clic del mouse" -#: gui/gui-manager.cpp:122 base/main.cpp:300 +#: gui/gui-manager.cpp:122 base/main.cpp:301 msgid "Display keyboard" msgstr "Mostra tastiera" -#: gui/gui-manager.cpp:126 base/main.cpp:304 +#: gui/gui-manager.cpp:126 base/main.cpp:305 msgid "Remap keys" msgstr "Riprogramma tasti" -#: gui/gui-manager.cpp:129 base/main.cpp:307 +#: gui/gui-manager.cpp:129 base/main.cpp:308 msgid "Toggle FullScreen" msgstr "Attiva / disattiva schermo intero" @@ -90,16 +91,16 @@ msgstr "Mappa" #: gui/KeysDialog.cpp:42 gui/launcher.cpp:346 gui/launcher.cpp:1001 #: gui/launcher.cpp:1005 gui/massadd.cpp:91 gui/options.cpp:1229 -#: engines/engine.cpp:361 engines/engine.cpp:372 engines/scumm/dialogs.cpp:192 -#: engines/scumm/scumm.cpp:1775 engines/agos/animation.cpp:561 -#: engines/groovie/script.cpp:420 engines/sky/compact.cpp:131 -#: engines/sky/compact.cpp:141 engines/sword1/animation.cpp:539 -#: engines/sword1/animation.cpp:560 engines/sword1/animation.cpp:570 -#: engines/sword1/animation.cpp:577 engines/sword1/control.cpp:865 -#: engines/sword1/logic.cpp:1633 engines/sword2/animation.cpp:435 -#: engines/sword2/animation.cpp:455 engines/sword2/animation.cpp:465 -#: engines/sword2/animation.cpp:474 engines/parallaction/saveload.cpp:274 -#: backends/platform/wii/options.cpp:47 +#: gui/saveload-dialog.cpp:920 engines/engine.cpp:361 engines/engine.cpp:372 +#: engines/scumm/dialogs.cpp:192 engines/scumm/scumm.cpp:1776 +#: engines/agos/animation.cpp:558 engines/groovie/script.cpp:420 +#: engines/sky/compact.cpp:131 engines/sky/compact.cpp:141 +#: engines/sword1/animation.cpp:519 engines/sword1/animation.cpp:540 +#: engines/sword1/animation.cpp:550 engines/sword1/animation.cpp:557 +#: engines/sword1/control.cpp:867 engines/sword1/logic.cpp:1633 +#: engines/sword2/animation.cpp:419 engines/sword2/animation.cpp:439 +#: engines/sword2/animation.cpp:449 engines/sword2/animation.cpp:458 +#: engines/parallaction/saveload.cpp:274 backends/platform/wii/options.cpp:47 #: backends/platform/wince/CELauncherDialog.cpp:54 msgid "OK" msgstr "OK" @@ -353,7 +354,7 @@ msgstr "Questo ID di gioco è già in uso. Si prega di sceglierne un'altro." msgid "~Q~uit" msgstr "C~h~iudi" -#: gui/launcher.cpp:621 backends/platform/sdl/macosx/appmenu_osx.mm:96 +#: gui/launcher.cpp:621 backends/platform/sdl/macosx/appmenu_osx.mm:95 msgid "Quit ScummVM" msgstr "Esci da ScummVM" @@ -361,7 +362,7 @@ msgstr "Esci da ScummVM" msgid "A~b~out..." msgstr "~I~nfo..." -#: gui/launcher.cpp:622 backends/platform/sdl/macosx/appmenu_osx.mm:70 +#: gui/launcher.cpp:622 backends/platform/sdl/macosx/appmenu_osx.mm:69 msgid "About ScummVM" msgstr "Informazioni su ScummVM" @@ -436,13 +437,13 @@ msgstr "Cerca nella lista dei giochi" msgid "Search:" msgstr "Cerca:" -#: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/mohawk/myst.cpp:255 +#: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/mohawk/myst.cpp:245 #: engines/mohawk/riven.cpp:716 engines/cruise/menu.cpp:214 msgid "Load game:" msgstr "Carica gioco:" #: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/scumm/dialogs.cpp:188 -#: engines/mohawk/myst.cpp:255 engines/mohawk/riven.cpp:716 +#: engines/mohawk/myst.cpp:245 engines/mohawk/riven.cpp:716 #: engines/cruise/menu.cpp:214 backends/platform/wince/CEActionsPocket.cpp:267 #: backends/platform/wince/CEActionsSmartphone.cpp:231 msgid "Load" @@ -924,68 +925,104 @@ msgstr "" "Il tema che hai selezionato non supporta la lingua attuale. Se vuoi " "utilizzare questo tema devi prima cambiare la lingua." -#: gui/saveload.cpp:59 gui/saveload.cpp:257 +#: gui/saveload-dialog.cpp:166 +msgid "List view" +msgstr "" + +#: gui/saveload-dialog.cpp:167 +msgid "Grid view" +msgstr "" + +#: gui/saveload-dialog.cpp:210 gui/saveload-dialog.cpp:358 msgid "No date saved" msgstr "Nessuna data salvata" -#: gui/saveload.cpp:60 gui/saveload.cpp:258 +#: gui/saveload-dialog.cpp:211 gui/saveload-dialog.cpp:359 msgid "No time saved" msgstr "Nessun orario salvato" -#: gui/saveload.cpp:61 gui/saveload.cpp:259 +#: gui/saveload-dialog.cpp:212 gui/saveload-dialog.cpp:360 msgid "No playtime saved" msgstr "Nessun tempo salvato" -#: gui/saveload.cpp:68 gui/saveload.cpp:173 +#: gui/saveload-dialog.cpp:219 gui/saveload-dialog.cpp:275 msgid "Delete" msgstr "Elimina" -#: gui/saveload.cpp:172 +#: gui/saveload-dialog.cpp:274 msgid "Do you really want to delete this savegame?" msgstr "Sei sicuro di voler eliminare questo salvataggio?" -#: gui/saveload.cpp:282 +#: gui/saveload-dialog.cpp:383 gui/saveload-dialog.cpp:872 msgid "Date: " msgstr "Data: " -#: gui/saveload.cpp:286 +#: gui/saveload-dialog.cpp:387 gui/saveload-dialog.cpp:878 msgid "Time: " msgstr "Ora: " -#: gui/saveload.cpp:292 +#: gui/saveload-dialog.cpp:393 gui/saveload-dialog.cpp:886 msgid "Playtime: " msgstr "Tempo di gioco: " -#: gui/saveload.cpp:305 gui/saveload.cpp:372 +#: gui/saveload-dialog.cpp:406 gui/saveload-dialog.cpp:494 msgid "Untitled savestate" msgstr "Salvataggio senza titolo" +#: gui/saveload-dialog.cpp:546 +msgid "Next" +msgstr "" + +#: gui/saveload-dialog.cpp:549 +msgid "Prev" +msgstr "" + +#: gui/saveload-dialog.cpp:736 +#, fuzzy +msgid "New Save" +msgstr "Salva" + +#: gui/saveload-dialog.cpp:736 +#, fuzzy +msgid "Create a new save game" +msgstr "Impossibile salvare il gioco" + +#: gui/saveload-dialog.cpp:865 +#, fuzzy +msgid "Name: " +msgstr "Nome:" + +#: gui/saveload-dialog.cpp:937 +#, c-format +msgid "Enter a description for slot %d:" +msgstr "" + #: gui/themebrowser.cpp:44 msgid "Select a Theme" msgstr "Seleziona un tema" -#: gui/ThemeEngine.cpp:335 +#: gui/ThemeEngine.cpp:337 msgid "Disabled GFX" msgstr "Grafica disattivata" -#: gui/ThemeEngine.cpp:335 +#: gui/ThemeEngine.cpp:337 msgctxt "lowres" msgid "Disabled GFX" msgstr "Grafica disattivata" -#: gui/ThemeEngine.cpp:336 +#: gui/ThemeEngine.cpp:338 msgid "Standard Renderer (16bpp)" msgstr "Renderer standard (16bpp)" -#: gui/ThemeEngine.cpp:336 +#: gui/ThemeEngine.cpp:338 msgid "Standard (16bpp)" msgstr "Standard (16bpp)" -#: gui/ThemeEngine.cpp:338 +#: gui/ThemeEngine.cpp:340 msgid "Antialiased Renderer (16bpp)" msgstr "Renderer con antialiasing (16bpp)" -#: gui/ThemeEngine.cpp:338 +#: gui/ThemeEngine.cpp:340 msgid "Antialiased (16bpp)" msgstr "Con antialiasing (16bpp)" @@ -993,35 +1030,35 @@ msgstr "Con antialiasing (16bpp)" msgid "Clear value" msgstr "Cancella" -#: base/main.cpp:209 +#: base/main.cpp:210 #, c-format msgid "Engine does not support debug level '%s'" msgstr "Il motore non supporta il livello di debug '%s'" -#: base/main.cpp:287 +#: base/main.cpp:288 msgid "Menu" msgstr "Menu" -#: base/main.cpp:290 backends/platform/symbian/src/SymbianActions.cpp:45 +#: base/main.cpp:291 backends/platform/symbian/src/SymbianActions.cpp:45 #: backends/platform/wince/CEActionsPocket.cpp:45 #: backends/platform/wince/CEActionsSmartphone.cpp:46 msgid "Skip" msgstr "Salta" -#: base/main.cpp:293 backends/platform/symbian/src/SymbianActions.cpp:50 +#: base/main.cpp:294 backends/platform/symbian/src/SymbianActions.cpp:50 #: backends/platform/wince/CEActionsPocket.cpp:42 msgid "Pause" msgstr "Pausa" -#: base/main.cpp:296 +#: base/main.cpp:297 msgid "Skip line" msgstr "Salta battuta" -#: base/main.cpp:467 +#: base/main.cpp:468 msgid "Error running game:" msgstr "Errore nell'esecuzione del gioco:" -#: base/main.cpp:491 +#: base/main.cpp:492 msgid "Could not find any engine capable of running the selected game" msgstr "" "Impossibile trovare un motore in grado di eseguire il gioco selezionato" @@ -1090,16 +1127,16 @@ msgstr "Utente cancellato" msgid "Unknown error" msgstr "Errore sconosciuto" -#: engines/advancedDetector.cpp:324 +#: engines/advancedDetector.cpp:316 #, c-format msgid "The game in '%s' seems to be unknown." msgstr "Il gioco in '%s' sembra essere sconosciuto." -#: engines/advancedDetector.cpp:325 +#: engines/advancedDetector.cpp:317 msgid "Please, report the following data to the ScummVM team along with name" msgstr "Per favore, riporta i seguenti dati al team di ScummVM con il nome" -#: engines/advancedDetector.cpp:327 +#: engines/advancedDetector.cpp:319 msgid "of the game you tried to add and its version/language/etc.:" msgstr "del gioco che hai provato ad aggiungere e la sua versione/lingua/ecc.:" @@ -1137,13 +1174,13 @@ msgid "~R~eturn to Launcher" msgstr "~V~ai a elenco giochi" #: engines/dialogs.cpp:115 engines/agi/saveload.cpp:803 -#: engines/cruise/menu.cpp:212 engines/sci/engine/kfile.cpp:735 +#: engines/cruise/menu.cpp:212 engines/sci/engine/kfile.cpp:742 msgid "Save game:" msgstr "Salva gioco:" #: engines/dialogs.cpp:115 engines/agi/saveload.cpp:803 #: engines/scumm/dialogs.cpp:187 engines/cruise/menu.cpp:212 -#: engines/sci/engine/kfile.cpp:735 +#: engines/sci/engine/kfile.cpp:742 #: backends/platform/symbian/src/SymbianActions.cpp:44 #: backends/platform/wince/CEActionsPocket.cpp:43 #: backends/platform/wince/CEActionsPocket.cpp:267 @@ -1254,23 +1291,23 @@ msgstr "" msgid "Start anyway" msgstr "Avvia comunque" -#: engines/agi/detection.cpp:145 engines/dreamweb/detection.cpp:47 -#: engines/sci/detection.cpp:390 +#: engines/agi/detection.cpp:142 engines/dreamweb/detection.cpp:47 +#: engines/sci/detection.cpp:393 msgid "Use original save/load screens" msgstr "Usa schermate di salvataggio originali" -#: engines/agi/detection.cpp:146 engines/dreamweb/detection.cpp:48 -#: engines/sci/detection.cpp:391 +#: engines/agi/detection.cpp:143 engines/dreamweb/detection.cpp:48 +#: engines/sci/detection.cpp:394 msgid "Use the original save/load screens, instead of the ScummVM ones" msgstr "" "Usa le schermate originali di salvataggio e caricamento, al posto di quelle " "di ScummVM" -#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:831 +#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:838 msgid "Restore game:" msgstr "Ripristina gioco:" -#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:831 +#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:838 msgid "Restore" msgstr "Ripristina" @@ -1282,27 +1319,27 @@ msgstr "Usa modalità colori brillanti" msgid "Display graphics using the game's bright palette" msgstr "Visualizza la grafica con i colori brillanti del gioco" -#: engines/sci/detection.cpp:370 +#: engines/sci/detection.cpp:373 msgid "EGA undithering" msgstr "Undithering EGA" -#: engines/sci/detection.cpp:371 +#: engines/sci/detection.cpp:374 msgid "Enable undithering in EGA games" msgstr "Attiva undithering nei giochi EGA" -#: engines/sci/detection.cpp:380 +#: engines/sci/detection.cpp:383 msgid "Prefer digital sound effects" msgstr "Scegli effetti sonori digitali" -#: engines/sci/detection.cpp:381 +#: engines/sci/detection.cpp:384 msgid "Prefer digital sound effects instead of synthesized ones" msgstr "Scegli gli effetti sonori digitali al posto di quelli sintetizzati" -#: engines/sci/detection.cpp:400 +#: engines/sci/detection.cpp:403 msgid "Use IMF/Yamaha FB-01 for MIDI output" msgstr "Usa IMF/Yamaha FB-01 per output MIDI" -#: engines/sci/detection.cpp:401 +#: engines/sci/detection.cpp:404 msgid "" "Use an IBM Music Feature card or a Yamaha FB-01 FM synth module for MIDI " "output" @@ -1310,30 +1347,30 @@ msgstr "" "Usa una scheda IBM Music Feature o un modulo synth Yamaha FB-01 FM per " "l'output MIDI" -#: engines/sci/detection.cpp:411 +#: engines/sci/detection.cpp:414 msgid "Use CD audio" msgstr "Usa audio da CD" -#: engines/sci/detection.cpp:412 +#: engines/sci/detection.cpp:415 msgid "Use CD audio instead of in-game audio, if available" msgstr "" "Usa l'audio da CD al posto di quello incorporato nel gioco, se disponibile" -#: engines/sci/detection.cpp:422 +#: engines/sci/detection.cpp:425 msgid "Use Windows cursors" msgstr "Usa cursori di Windows" -#: engines/sci/detection.cpp:423 +#: engines/sci/detection.cpp:426 msgid "" "Use the Windows cursors (smaller and monochrome) instead of the DOS ones" msgstr "" "Usa i cursori di Windows (più piccoli e monocromatici) al posto di quelli DOS" -#: engines/sci/detection.cpp:433 +#: engines/sci/detection.cpp:436 msgid "Use silver cursors" msgstr "Usa cursori d'argento" -#: engines/sci/detection.cpp:434 +#: engines/sci/detection.cpp:437 msgid "" "Use the alternate set of silver cursors, instead of the normal golden ones" msgstr "" @@ -1986,7 +2023,7 @@ msgstr "Vola a destra" msgid "Fly to lower right" msgstr "Vola in basso a destra" -#: engines/scumm/scumm.cpp:1773 +#: engines/scumm/scumm.cpp:1774 #, c-format msgid "" "Native MIDI support requires the Roland Upgrade from LucasArts,\n" @@ -1995,7 +2032,7 @@ msgstr "" "Il supporto nativo MIDI richiede il Roland Upgrade della LucasArts,\n" "ma %s non è presente. Verrà usato AdLib." -#: engines/scumm/scumm.cpp:2278 engines/agos/saveload.cpp:202 +#: engines/scumm/scumm.cpp:2295 engines/agos/saveload.cpp:220 #, c-format msgid "" "Failed to save game state to file:\n" @@ -2006,7 +2043,7 @@ msgstr "" "\n" "%s" -#: engines/scumm/scumm.cpp:2285 engines/agos/saveload.cpp:167 +#: engines/scumm/scumm.cpp:2302 engines/agos/saveload.cpp:185 #, c-format msgid "" "Failed to load game state from file:\n" @@ -2017,7 +2054,7 @@ msgstr "" "\n" "%s" -#: engines/scumm/scumm.cpp:2297 engines/agos/saveload.cpp:210 +#: engines/scumm/scumm.cpp:2314 engines/agos/saveload.cpp:228 #, c-format msgid "" "Successfully saved game state in file:\n" @@ -2028,7 +2065,7 @@ msgstr "" "\n" "%s" -#: engines/scumm/scumm.cpp:2512 +#: engines/scumm/scumm.cpp:2529 msgid "" "Usually, Maniac Mansion would start now. But ScummVM doesn't do that yet. To " "play it, go to 'Add Game' in the ScummVM start menu and select the 'Maniac' " @@ -2065,17 +2102,17 @@ msgstr "~M~enu principale" msgid "~W~ater Effect Enabled" msgstr "~E~ffetto acqua attivo" -#: engines/agos/animation.cpp:560 +#: engines/agos/animation.cpp:557 #, c-format msgid "Cutscene file '%s' not found!" msgstr "File della scena di intermezzo '%s' non trovato!" #: engines/gob/inter_playtoons.cpp:256 engines/gob/inter_v2.cpp:1287 -#: engines/tinsel/saveload.cpp:502 +#: engines/tinsel/saveload.cpp:532 msgid "Failed to load game state from file." msgstr "Impossibile caricare il gioco dal file." -#: engines/gob/inter_v2.cpp:1357 engines/tinsel/saveload.cpp:515 +#: engines/gob/inter_v2.cpp:1357 engines/tinsel/saveload.cpp:545 msgid "Failed to save game state to file." msgstr "Impossibile salvare il gioco nel file." @@ -2193,7 +2230,7 @@ msgstr "Opzioni" msgid "Choose Spell" msgstr "Scegli incantesimo" -#: engines/kyra/sound_midi.cpp:475 +#: engines/kyra/sound_midi.cpp:477 msgid "" "You appear to be using a General MIDI device,\n" "but your game only supports Roland MT32 MIDI.\n" @@ -2207,12 +2244,13 @@ msgstr "" "Roland MT32 in quelli General MIDI. Alcune tracce\n" "potrebbero avere un suono non corretto." -#: engines/queen/queen.cpp:59 engines/sky/detection.cpp:44 -msgid "Floppy intro" -msgstr "Intro floppy" +#: engines/queen/queen.cpp:59 +msgid "Alternative intro" +msgstr "" -#: engines/queen/queen.cpp:60 engines/sky/detection.cpp:45 -msgid "Use the floppy version's intro (CD version only)" +#: engines/queen/queen.cpp:60 +#, fuzzy +msgid "Use an alternative game intro (CD version only)" msgstr "Usa la versione floppy dell'intro (solo versione CD)" #: engines/sky/compact.cpp:130 @@ -2231,28 +2269,36 @@ msgstr "" "Il file \"sky.cpt\" non ha una dimensione corretta.\n" "Si prega di (ri)scaricarlo da www.scummvm.org" -#: engines/sword1/animation.cpp:539 +#: engines/sky/detection.cpp:44 +msgid "Floppy intro" +msgstr "Intro floppy" + +#: engines/sky/detection.cpp:45 +msgid "Use the floppy version's intro (CD version only)" +msgstr "Usa la versione floppy dell'intro (solo versione CD)" + +#: engines/sword1/animation.cpp:519 #, c-format msgid "PSX stream cutscene '%s' cannot be played in paletted mode" msgstr "" "La scena PSX di intermezzo '%s' non può essere eseguita in modalità tavolozza" -#: engines/sword1/animation.cpp:560 engines/sword2/animation.cpp:455 +#: engines/sword1/animation.cpp:540 engines/sword2/animation.cpp:439 msgid "DXA cutscenes found but ScummVM has been built without zlib support" msgstr "" "Sono state trovare scene di intermezzo DXA ma ScummVM è stato compilato " "senza il supporto zlib" -#: engines/sword1/animation.cpp:570 engines/sword2/animation.cpp:465 +#: engines/sword1/animation.cpp:550 engines/sword2/animation.cpp:449 msgid "MPEG2 cutscenes are no longer supported" msgstr "Le scene di intermezzo MPEG2 non sono più supportate" -#: engines/sword1/animation.cpp:576 engines/sword2/animation.cpp:473 +#: engines/sword1/animation.cpp:556 engines/sword2/animation.cpp:457 #, c-format msgid "Cutscene '%s' not found" msgstr "Scena di intermezzo '%s' non trovata" -#: engines/sword1/control.cpp:863 +#: engines/sword1/control.cpp:865 msgid "" "ScummVM found that you have old savefiles for Broken Sword 1 that should be " "converted.\n" @@ -2270,7 +2316,7 @@ msgstr "" "Premi OK per convertirli adesso, altrimenti ti verrà richiesto al prossimo " "avvio del gioco.\n" -#: engines/sword1/control.cpp:1232 +#: engines/sword1/control.cpp:1234 #, c-format msgid "" "Target new save game already exists!\n" @@ -2279,11 +2325,11 @@ msgstr "" "La destinazione del nuovo salvataggio già esiste!\n" "Vuoi mantenere il vecchio salvataggio (%s) o quello nuovo (%s)?\n" -#: engines/sword1/control.cpp:1235 +#: engines/sword1/control.cpp:1237 msgid "Keep the old one" msgstr "Mantieni quello vecchio" -#: engines/sword1/control.cpp:1235 +#: engines/sword1/control.cpp:1237 msgid "Keep the new one" msgstr "Mantieni quello nuovo" @@ -2291,7 +2337,7 @@ msgstr "Mantieni quello nuovo" msgid "This is the end of the Broken Sword 1 Demo" msgstr "Questa è la fine della demo di Broken Sword 1" -#: engines/sword2/animation.cpp:435 +#: engines/sword2/animation.cpp:419 msgid "" "PSX cutscenes found but ScummVM has been built without RGB color support" msgstr "" @@ -2306,6 +2352,17 @@ msgstr "Mostra etichette oggetti" msgid "Show labels for objects on mouse hover" msgstr "Mostra etichette per gli oggetti al passaggio del mouse" +#: engines/teenagent/resources.cpp:94 +msgid "" +"You're missing the 'teenagent.dat' file. Get it from the ScummVM website" +msgstr "" + +#: engines/teenagent/resources.cpp:115 +msgid "" +"The teenagent.dat file is compressed and zlib hasn't been included in this " +"executable. Please decompress it" +msgstr "" + #: engines/parallaction/saveload.cpp:133 #, c-format msgid "" @@ -2413,7 +2470,7 @@ msgstr "Nessuna musica" msgid "Amiga Audio Emulator" msgstr "Emulatore audio Amiga" -#: audio/softsynth/adlib.cpp:1593 +#: audio/softsynth/adlib.cpp:2284 msgid "AdLib Emulator" msgstr "Emulatore AdLib" @@ -2557,11 +2614,11 @@ msgstr "Modalità touchpad attivata." msgid "Touchpad mode disabled." msgstr "Modalità touchpad disattivata." -#: backends/platform/maemo/maemo.cpp:205 +#: backends/platform/maemo/maemo.cpp:209 msgid "Click Mode" msgstr "Modalità clic" -#: backends/platform/maemo/maemo.cpp:211 +#: backends/platform/maemo/maemo.cpp:215 #: backends/platform/symbian/src/SymbianActions.cpp:42 #: backends/platform/wince/CEActionsPocket.cpp:60 #: backends/platform/wince/CEActionsSmartphone.cpp:43 @@ -2569,35 +2626,35 @@ msgstr "Modalità clic" msgid "Left Click" msgstr "Clic sinistro" -#: backends/platform/maemo/maemo.cpp:214 +#: backends/platform/maemo/maemo.cpp:218 msgid "Middle Click" msgstr "Clic centrale" -#: backends/platform/maemo/maemo.cpp:217 +#: backends/platform/maemo/maemo.cpp:221 #: backends/platform/symbian/src/SymbianActions.cpp:43 #: backends/platform/wince/CEActionsSmartphone.cpp:44 #: backends/platform/bada/form.cpp:273 msgid "Right Click" msgstr "Clic destro" -#: backends/platform/sdl/macosx/appmenu_osx.mm:78 +#: backends/platform/sdl/macosx/appmenu_osx.mm:77 msgid "Hide ScummVM" msgstr "Nascondi ScummVM" -#: backends/platform/sdl/macosx/appmenu_osx.mm:83 +#: backends/platform/sdl/macosx/appmenu_osx.mm:82 msgid "Hide Others" msgstr "Nascondi altre" -#: backends/platform/sdl/macosx/appmenu_osx.mm:88 +#: backends/platform/sdl/macosx/appmenu_osx.mm:87 msgid "Show All" msgstr "Mostra tutte" -#: backends/platform/sdl/macosx/appmenu_osx.mm:110 -#: backends/platform/sdl/macosx/appmenu_osx.mm:121 +#: backends/platform/sdl/macosx/appmenu_osx.mm:109 +#: backends/platform/sdl/macosx/appmenu_osx.mm:120 msgid "Window" msgstr "Finestra" -#: backends/platform/sdl/macosx/appmenu_osx.mm:115 +#: backends/platform/sdl/macosx/appmenu_osx.mm:114 msgid "Minimize" msgstr "Contrai" @@ -2977,41 +3034,46 @@ msgstr "Elenco giochi" msgid "Do you really want to quit?" msgstr "Sei sicuro di voler uscire?" -#: backends/events/gph/gph-events.cpp:338 -#: backends/events/gph/gph-events.cpp:381 -#: backends/events/openpandora/op-events.cpp:139 +#: backends/events/gph/gph-events.cpp:386 +#: backends/events/gph/gph-events.cpp:429 +#: backends/events/openpandora/op-events.cpp:168 msgid "Touchscreen 'Tap Mode' - Left Click" msgstr "Touchscreen 'Tap Mode' - Clic sinistro" -#: backends/events/gph/gph-events.cpp:340 -#: backends/events/gph/gph-events.cpp:383 -#: backends/events/openpandora/op-events.cpp:141 +#: backends/events/gph/gph-events.cpp:388 +#: backends/events/gph/gph-events.cpp:431 +#: backends/events/openpandora/op-events.cpp:170 msgid "Touchscreen 'Tap Mode' - Right Click" msgstr "Touchscreen 'Tap Mode' - Clic destro" -#: backends/events/gph/gph-events.cpp:342 -#: backends/events/gph/gph-events.cpp:385 -#: backends/events/openpandora/op-events.cpp:143 +#: backends/events/gph/gph-events.cpp:390 +#: backends/events/gph/gph-events.cpp:433 +#: backends/events/openpandora/op-events.cpp:172 msgid "Touchscreen 'Tap Mode' - Hover (No Click)" msgstr "Touchscreen 'Tap Mode' - Passaggio del cursore (nessun clic)" -#: backends/events/gph/gph-events.cpp:362 +#: backends/events/gph/gph-events.cpp:410 msgid "Maximum Volume" msgstr "Volume massimo" -#: backends/events/gph/gph-events.cpp:364 +#: backends/events/gph/gph-events.cpp:412 msgid "Increasing Volume" msgstr "Aumento volume" -#: backends/events/gph/gph-events.cpp:370 +#: backends/events/gph/gph-events.cpp:418 msgid "Minimal Volume" msgstr "Volume minimo" -#: backends/events/gph/gph-events.cpp:372 +#: backends/events/gph/gph-events.cpp:420 msgid "Decreasing Volume" msgstr "Diminuzione volume" -#: backends/updates/macosx/macosx-updates.mm:65 +#: backends/events/openpandora/op-events.cpp:174 +#, fuzzy +msgid "Touchscreen 'Tap Mode' - Hover (DPad Clicks)" +msgstr "Touchscreen 'Tap Mode' - Passaggio del cursore (nessun clic)" + +#: backends/updates/macosx/macosx-updates.mm:67 msgid "Check for Updates..." msgstr "Cerca aggiornamenti..." diff --git a/po/nb_NO.po b/po/nb_NO.po index bc7b1720fa..bf94822c95 100644 --- a/po/nb_NO.po +++ b/po/nb_NO.po @@ -1,5 +1,5 @@ # Norwegian (Bokmaal) translation for ScummVM. -# Copyright (C) 2010-2012 ScummVM Team +# Copyright (C) 2010-2013 ScummVM Team # This file is distributed under the same license as the ScummVM package. # Einar Johan T. Sømåen <einarjohants@gmail.com>, 2010. # @@ -7,14 +7,14 @@ msgid "" msgstr "" "Project-Id-Version: ScummVM 1.3.0svn\n" "Report-Msgid-Bugs-To: scummvm-devel@lists.sf.net\n" -"POT-Creation-Date: 2012-07-08 12:25+0100\n" +"POT-Creation-Date: 2012-12-01 17:22+0000\n" "PO-Revision-Date: 2012-07-04 02:19+0100\n" "Last-Translator: Einar Johan Sømåen <einarjohants@gmail.com>\n" "Language-Team: somaen <einarjohants@gmail.com>\n" +"Language: Norsk (bokmaal)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=iso-8859-1\n" "Content-Transfer-Encoding: 8bit\n" -"Language: Norsk (bokmaal)\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" "X-Poedit-Language: Norsk Bokmål\n" "X-Poedit-Country: NORWAY\n" @@ -48,10 +48,11 @@ msgstr "Gå tilbake" #: gui/browser.cpp:69 gui/chooser.cpp:45 gui/KeysDialog.cpp:43 #: gui/launcher.cpp:345 gui/massadd.cpp:94 gui/options.cpp:1228 -#: gui/saveload.cpp:64 gui/saveload.cpp:173 gui/themebrowser.cpp:54 -#: engines/engine.cpp:442 engines/scumm/dialogs.cpp:190 -#: engines/sword1/control.cpp:865 engines/parallaction/saveload.cpp:274 -#: backends/platform/wii/options.cpp:48 +#: gui/saveload-dialog.cpp:215 gui/saveload-dialog.cpp:275 +#: gui/saveload-dialog.cpp:545 gui/saveload-dialog.cpp:919 +#: gui/themebrowser.cpp:54 engines/engine.cpp:442 +#: engines/scumm/dialogs.cpp:190 engines/sword1/control.cpp:867 +#: engines/parallaction/saveload.cpp:274 backends/platform/wii/options.cpp:48 #: backends/events/default/default-events.cpp:191 #: backends/events/default/default-events.cpp:213 msgid "Cancel" @@ -72,15 +73,15 @@ msgstr "Lukk" msgid "Mouse click" msgstr "Musklikk" -#: gui/gui-manager.cpp:122 base/main.cpp:300 +#: gui/gui-manager.cpp:122 base/main.cpp:301 msgid "Display keyboard" msgstr "Vis tastatur" -#: gui/gui-manager.cpp:126 base/main.cpp:304 +#: gui/gui-manager.cpp:126 base/main.cpp:305 msgid "Remap keys" msgstr "Omkoble taster" -#: gui/gui-manager.cpp:129 base/main.cpp:307 +#: gui/gui-manager.cpp:129 base/main.cpp:308 msgid "Toggle FullScreen" msgstr "Veksle fullskjerm" @@ -94,16 +95,16 @@ msgstr "Koble" #: gui/KeysDialog.cpp:42 gui/launcher.cpp:346 gui/launcher.cpp:1001 #: gui/launcher.cpp:1005 gui/massadd.cpp:91 gui/options.cpp:1229 -#: engines/engine.cpp:361 engines/engine.cpp:372 engines/scumm/dialogs.cpp:192 -#: engines/scumm/scumm.cpp:1775 engines/agos/animation.cpp:561 -#: engines/groovie/script.cpp:420 engines/sky/compact.cpp:131 -#: engines/sky/compact.cpp:141 engines/sword1/animation.cpp:539 -#: engines/sword1/animation.cpp:560 engines/sword1/animation.cpp:570 -#: engines/sword1/animation.cpp:577 engines/sword1/control.cpp:865 -#: engines/sword1/logic.cpp:1633 engines/sword2/animation.cpp:435 -#: engines/sword2/animation.cpp:455 engines/sword2/animation.cpp:465 -#: engines/sword2/animation.cpp:474 engines/parallaction/saveload.cpp:274 -#: backends/platform/wii/options.cpp:47 +#: gui/saveload-dialog.cpp:920 engines/engine.cpp:361 engines/engine.cpp:372 +#: engines/scumm/dialogs.cpp:192 engines/scumm/scumm.cpp:1776 +#: engines/agos/animation.cpp:558 engines/groovie/script.cpp:420 +#: engines/sky/compact.cpp:131 engines/sky/compact.cpp:141 +#: engines/sword1/animation.cpp:519 engines/sword1/animation.cpp:540 +#: engines/sword1/animation.cpp:550 engines/sword1/animation.cpp:557 +#: engines/sword1/control.cpp:867 engines/sword1/logic.cpp:1633 +#: engines/sword2/animation.cpp:419 engines/sword2/animation.cpp:439 +#: engines/sword2/animation.cpp:449 engines/sword2/animation.cpp:458 +#: engines/parallaction/saveload.cpp:274 backends/platform/wii/options.cpp:47 #: backends/platform/wince/CELauncherDialog.cpp:54 msgid "OK" msgstr "OK" @@ -358,7 +359,7 @@ msgstr "Denne spill-IDen er allerede i bruk. Vennligst velg en annen." msgid "~Q~uit" msgstr "~A~vslutt" -#: gui/launcher.cpp:621 backends/platform/sdl/macosx/appmenu_osx.mm:96 +#: gui/launcher.cpp:621 backends/platform/sdl/macosx/appmenu_osx.mm:95 msgid "Quit ScummVM" msgstr "Avslutt ScummVM" @@ -366,7 +367,7 @@ msgstr "Avslutt ScummVM" msgid "A~b~out..." msgstr "~O~m..." -#: gui/launcher.cpp:622 backends/platform/sdl/macosx/appmenu_osx.mm:70 +#: gui/launcher.cpp:622 backends/platform/sdl/macosx/appmenu_osx.mm:69 msgid "About ScummVM" msgstr "Om ScummVM" @@ -441,13 +442,13 @@ msgstr "Søk i spilliste" msgid "Search:" msgstr "Søk:" -#: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/mohawk/myst.cpp:255 +#: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/mohawk/myst.cpp:245 #: engines/mohawk/riven.cpp:716 engines/cruise/menu.cpp:214 msgid "Load game:" msgstr "Åpne spill:" #: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/scumm/dialogs.cpp:188 -#: engines/mohawk/myst.cpp:255 engines/mohawk/riven.cpp:716 +#: engines/mohawk/myst.cpp:245 engines/mohawk/riven.cpp:716 #: engines/cruise/menu.cpp:214 backends/platform/wince/CEActionsPocket.cpp:267 #: backends/platform/wince/CEActionsSmartphone.cpp:231 msgid "Load" @@ -922,68 +923,104 @@ msgstr "" "Temaet du valgte støtter ikke det aktive språket. Hvis du vil bruke dette " "temaet, må du bytte til et annet språk først." -#: gui/saveload.cpp:59 gui/saveload.cpp:257 +#: gui/saveload-dialog.cpp:166 +msgid "List view" +msgstr "" + +#: gui/saveload-dialog.cpp:167 +msgid "Grid view" +msgstr "" + +#: gui/saveload-dialog.cpp:210 gui/saveload-dialog.cpp:358 msgid "No date saved" msgstr "Ingen dato lagret" -#: gui/saveload.cpp:60 gui/saveload.cpp:258 +#: gui/saveload-dialog.cpp:211 gui/saveload-dialog.cpp:359 msgid "No time saved" msgstr "Ingen tid lagret" -#: gui/saveload.cpp:61 gui/saveload.cpp:259 +#: gui/saveload-dialog.cpp:212 gui/saveload-dialog.cpp:360 msgid "No playtime saved" msgstr "Ingen spilltid lagret" -#: gui/saveload.cpp:68 gui/saveload.cpp:173 +#: gui/saveload-dialog.cpp:219 gui/saveload-dialog.cpp:275 msgid "Delete" msgstr "Slett" -#: gui/saveload.cpp:172 +#: gui/saveload-dialog.cpp:274 msgid "Do you really want to delete this savegame?" msgstr "Vil du virkelig slette dette lagrede spillet?" -#: gui/saveload.cpp:282 +#: gui/saveload-dialog.cpp:383 gui/saveload-dialog.cpp:872 msgid "Date: " msgstr "Dato: " -#: gui/saveload.cpp:286 +#: gui/saveload-dialog.cpp:387 gui/saveload-dialog.cpp:878 msgid "Time: " msgstr "Tid: " -#: gui/saveload.cpp:292 +#: gui/saveload-dialog.cpp:393 gui/saveload-dialog.cpp:886 msgid "Playtime: " msgstr "Spilltid: " -#: gui/saveload.cpp:305 gui/saveload.cpp:372 +#: gui/saveload-dialog.cpp:406 gui/saveload-dialog.cpp:494 msgid "Untitled savestate" msgstr "Ikke navngitt spilltilstand" +#: gui/saveload-dialog.cpp:546 +msgid "Next" +msgstr "" + +#: gui/saveload-dialog.cpp:549 +msgid "Prev" +msgstr "" + +#: gui/saveload-dialog.cpp:736 +#, fuzzy +msgid "New Save" +msgstr "Lagre" + +#: gui/saveload-dialog.cpp:736 +#, fuzzy +msgid "Create a new save game" +msgstr "Klarte ikke å lagre spill." + +#: gui/saveload-dialog.cpp:865 +#, fuzzy +msgid "Name: " +msgstr "Navn:" + +#: gui/saveload-dialog.cpp:937 +#, c-format +msgid "Enter a description for slot %d:" +msgstr "" + #: gui/themebrowser.cpp:44 msgid "Select a Theme" msgstr "Velg et tema" -#: gui/ThemeEngine.cpp:335 +#: gui/ThemeEngine.cpp:337 msgid "Disabled GFX" msgstr "Deaktivert GFX" -#: gui/ThemeEngine.cpp:335 +#: gui/ThemeEngine.cpp:337 msgctxt "lowres" msgid "Disabled GFX" msgstr "Deaktivert GFX" -#: gui/ThemeEngine.cpp:336 +#: gui/ThemeEngine.cpp:338 msgid "Standard Renderer (16bpp)" msgstr "Standard Tegner (16bpp)" -#: gui/ThemeEngine.cpp:336 +#: gui/ThemeEngine.cpp:338 msgid "Standard (16bpp)" msgstr "Standard (16bpp)" -#: gui/ThemeEngine.cpp:338 +#: gui/ThemeEngine.cpp:340 msgid "Antialiased Renderer (16bpp)" msgstr "Antialiased Tegner (16bpp)" -#: gui/ThemeEngine.cpp:338 +#: gui/ThemeEngine.cpp:340 msgid "Antialiased (16bpp)" msgstr "Antialiased (16bpp)" @@ -991,35 +1028,35 @@ msgstr "Antialiased (16bpp)" msgid "Clear value" msgstr "Tøm verdi" -#: base/main.cpp:209 +#: base/main.cpp:210 #, c-format msgid "Engine does not support debug level '%s'" msgstr "Motoren støtter ikke debug-nivå '%s'" -#: base/main.cpp:287 +#: base/main.cpp:288 msgid "Menu" msgstr "Meny" -#: base/main.cpp:290 backends/platform/symbian/src/SymbianActions.cpp:45 +#: base/main.cpp:291 backends/platform/symbian/src/SymbianActions.cpp:45 #: backends/platform/wince/CEActionsPocket.cpp:45 #: backends/platform/wince/CEActionsSmartphone.cpp:46 msgid "Skip" msgstr "Hopp over" -#: base/main.cpp:293 backends/platform/symbian/src/SymbianActions.cpp:50 +#: base/main.cpp:294 backends/platform/symbian/src/SymbianActions.cpp:50 #: backends/platform/wince/CEActionsPocket.cpp:42 msgid "Pause" msgstr "Pause" -#: base/main.cpp:296 +#: base/main.cpp:297 msgid "Skip line" msgstr "Hopp over linje" -#: base/main.cpp:467 +#: base/main.cpp:468 msgid "Error running game:" msgstr "Problem ved kjøring av spill:" -#: base/main.cpp:491 +#: base/main.cpp:492 msgid "Could not find any engine capable of running the selected game" msgstr "Kunne ikke finne noen motor som kunne kjøre det valgte spillet" @@ -1087,17 +1124,17 @@ msgstr "Brukeren avbrøt" msgid "Unknown error" msgstr "Ukjent feil" -#: engines/advancedDetector.cpp:324 +#: engines/advancedDetector.cpp:316 #, c-format msgid "The game in '%s' seems to be unknown." msgstr "Spillet i '%s' ser ut til å være ukjent." -#: engines/advancedDetector.cpp:325 +#: engines/advancedDetector.cpp:317 msgid "Please, report the following data to the ScummVM team along with name" msgstr "" "Vennligst rapporter de følgende dataene til ScummVM-teamet sammen med navnet" -#: engines/advancedDetector.cpp:327 +#: engines/advancedDetector.cpp:319 msgid "of the game you tried to add and its version/language/etc.:" msgstr "på spillet du forsøkte å legge til, og dets versjon/språk/etc.:" @@ -1135,13 +1172,13 @@ msgid "~R~eturn to Launcher" msgstr "~T~ilbake til oppstarter" #: engines/dialogs.cpp:115 engines/agi/saveload.cpp:803 -#: engines/cruise/menu.cpp:212 engines/sci/engine/kfile.cpp:735 +#: engines/cruise/menu.cpp:212 engines/sci/engine/kfile.cpp:742 msgid "Save game:" msgstr "Lagret spill:" #: engines/dialogs.cpp:115 engines/agi/saveload.cpp:803 #: engines/scumm/dialogs.cpp:187 engines/cruise/menu.cpp:212 -#: engines/sci/engine/kfile.cpp:735 +#: engines/sci/engine/kfile.cpp:742 #: backends/platform/symbian/src/SymbianActions.cpp:44 #: backends/platform/wince/CEActionsPocket.cpp:43 #: backends/platform/wince/CEActionsPocket.cpp:267 @@ -1243,21 +1280,21 @@ msgstr "" msgid "Start anyway" msgstr "Start allikevel" -#: engines/agi/detection.cpp:145 engines/dreamweb/detection.cpp:47 -#: engines/sci/detection.cpp:390 +#: engines/agi/detection.cpp:142 engines/dreamweb/detection.cpp:47 +#: engines/sci/detection.cpp:393 msgid "Use original save/load screens" msgstr "Bruk originale lagre/laste-skjermer" -#: engines/agi/detection.cpp:146 engines/dreamweb/detection.cpp:48 -#: engines/sci/detection.cpp:391 +#: engines/agi/detection.cpp:143 engines/dreamweb/detection.cpp:48 +#: engines/sci/detection.cpp:394 msgid "Use the original save/load screens, instead of the ScummVM ones" msgstr "Bruk de originale lagre/laste-skjermene, istedenfor ScummVM-variantene" -#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:831 +#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:838 msgid "Restore game:" msgstr "Gjennopprett spill:" -#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:831 +#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:838 msgid "Restore" msgstr "Gjenopprett" @@ -1269,54 +1306,54 @@ msgstr "Bruk lys palettmodus" msgid "Display graphics using the game's bright palette" msgstr "Vis grafikk med spillets lyse palett" -#: engines/sci/detection.cpp:370 +#: engines/sci/detection.cpp:373 msgid "EGA undithering" msgstr "EGA av-dithering" -#: engines/sci/detection.cpp:371 +#: engines/sci/detection.cpp:374 msgid "Enable undithering in EGA games" msgstr "Aktiver av-dithering i EGA-spill" -#: engines/sci/detection.cpp:380 +#: engines/sci/detection.cpp:383 msgid "Prefer digital sound effects" msgstr "Foretrekk digitale lydeffekter" -#: engines/sci/detection.cpp:381 +#: engines/sci/detection.cpp:384 msgid "Prefer digital sound effects instead of synthesized ones" msgstr "Foretrekk digitale lydeffekter fremfor syntetiske" -#: engines/sci/detection.cpp:400 +#: engines/sci/detection.cpp:403 msgid "Use IMF/Yamaha FB-01 for MIDI output" msgstr "Bruk IMF/Yamaha-FB-01 for MIDI-output" -#: engines/sci/detection.cpp:401 +#: engines/sci/detection.cpp:404 msgid "" "Use an IBM Music Feature card or a Yamaha FB-01 FM synth module for MIDI " "output" msgstr "" -#: engines/sci/detection.cpp:411 +#: engines/sci/detection.cpp:414 msgid "Use CD audio" msgstr "Bruk CD-lyd" -#: engines/sci/detection.cpp:412 +#: engines/sci/detection.cpp:415 msgid "Use CD audio instead of in-game audio, if available" msgstr "Bruk CD-lyd istedenfor spillets lyd, hvis tilgjengelig" -#: engines/sci/detection.cpp:422 +#: engines/sci/detection.cpp:425 msgid "Use Windows cursors" msgstr "Bruk Windows-muspekere" -#: engines/sci/detection.cpp:423 +#: engines/sci/detection.cpp:426 msgid "" "Use the Windows cursors (smaller and monochrome) instead of the DOS ones" msgstr "Bruk Windows-muspekerene (mindre, og monokrome) isteden" -#: engines/sci/detection.cpp:433 +#: engines/sci/detection.cpp:436 msgid "Use silver cursors" msgstr "Bruk sølvmuspekere" -#: engines/sci/detection.cpp:434 +#: engines/sci/detection.cpp:437 msgid "" "Use the alternate set of silver cursors, instead of the normal golden ones" msgstr "" @@ -1969,14 +2006,14 @@ msgstr "Fly til høyre" msgid "Fly to lower right" msgstr "Fly til nedre høyre" -#: engines/scumm/scumm.cpp:1773 +#: engines/scumm/scumm.cpp:1774 #, c-format msgid "" "Native MIDI support requires the Roland Upgrade from LucasArts,\n" "but %s is missing. Using AdLib instead." msgstr "" -#: engines/scumm/scumm.cpp:2278 engines/agos/saveload.cpp:202 +#: engines/scumm/scumm.cpp:2295 engines/agos/saveload.cpp:220 #, c-format msgid "" "Failed to save game state to file:\n" @@ -1987,7 +2024,7 @@ msgstr "" "\n" "%s" -#: engines/scumm/scumm.cpp:2285 engines/agos/saveload.cpp:167 +#: engines/scumm/scumm.cpp:2302 engines/agos/saveload.cpp:185 #, c-format msgid "" "Failed to load game state from file:\n" @@ -1998,7 +2035,7 @@ msgstr "" "\n" "%s" -#: engines/scumm/scumm.cpp:2297 engines/agos/saveload.cpp:210 +#: engines/scumm/scumm.cpp:2314 engines/agos/saveload.cpp:228 #, c-format msgid "" "Successfully saved game state in file:\n" @@ -2009,7 +2046,7 @@ msgstr "" "\n" "%s" -#: engines/scumm/scumm.cpp:2512 +#: engines/scumm/scumm.cpp:2529 msgid "" "Usually, Maniac Mansion would start now. But ScummVM doesn't do that yet. To " "play it, go to 'Add Game' in the ScummVM start menu and select the 'Maniac' " @@ -2045,17 +2082,17 @@ msgstr "Hoved~m~eny" msgid "~W~ater Effect Enabled" msgstr "~V~anneffekt aktivert" -#: engines/agos/animation.cpp:560 +#: engines/agos/animation.cpp:557 #, c-format msgid "Cutscene file '%s' not found!" msgstr "" #: engines/gob/inter_playtoons.cpp:256 engines/gob/inter_v2.cpp:1287 -#: engines/tinsel/saveload.cpp:502 +#: engines/tinsel/saveload.cpp:532 msgid "Failed to load game state from file." msgstr "Klarte ikke åpne spilltilstand fra fil." -#: engines/gob/inter_v2.cpp:1357 engines/tinsel/saveload.cpp:515 +#: engines/gob/inter_v2.cpp:1357 engines/tinsel/saveload.cpp:545 msgid "Failed to save game state to file." msgstr "Klarte ikke lagre spilltilstand fra fil." @@ -2174,7 +2211,7 @@ msgstr "Valg" msgid "Choose Spell" msgstr "Velg" -#: engines/kyra/sound_midi.cpp:475 +#: engines/kyra/sound_midi.cpp:477 #, fuzzy msgid "" "You appear to be using a General MIDI device,\n" @@ -2189,12 +2226,13 @@ msgstr "" "General MIDI-instrumentene. Allikevel, kan det\n" "skje at enkelte spor ikke vil spilles riktig." -#: engines/queen/queen.cpp:59 engines/sky/detection.cpp:44 -msgid "Floppy intro" -msgstr "Diskett-intro" +#: engines/queen/queen.cpp:59 +msgid "Alternative intro" +msgstr "" -#: engines/queen/queen.cpp:60 engines/sky/detection.cpp:45 -msgid "Use the floppy version's intro (CD version only)" +#: engines/queen/queen.cpp:60 +#, fuzzy +msgid "Use an alternative game intro (CD version only)" msgstr "Bruk diskettversjonens intro (Kun for CD-versjon)" #: engines/sky/compact.cpp:130 @@ -2211,25 +2249,33 @@ msgid "" "Please (re)download it from www.scummvm.org" msgstr "" -#: engines/sword1/animation.cpp:539 +#: engines/sky/detection.cpp:44 +msgid "Floppy intro" +msgstr "Diskett-intro" + +#: engines/sky/detection.cpp:45 +msgid "Use the floppy version's intro (CD version only)" +msgstr "Bruk diskettversjonens intro (Kun for CD-versjon)" + +#: engines/sword1/animation.cpp:519 #, c-format msgid "PSX stream cutscene '%s' cannot be played in paletted mode" msgstr "" -#: engines/sword1/animation.cpp:560 engines/sword2/animation.cpp:455 +#: engines/sword1/animation.cpp:540 engines/sword2/animation.cpp:439 msgid "DXA cutscenes found but ScummVM has been built without zlib support" msgstr "" -#: engines/sword1/animation.cpp:570 engines/sword2/animation.cpp:465 +#: engines/sword1/animation.cpp:550 engines/sword2/animation.cpp:449 msgid "MPEG2 cutscenes are no longer supported" msgstr "" -#: engines/sword1/animation.cpp:576 engines/sword2/animation.cpp:473 +#: engines/sword1/animation.cpp:556 engines/sword2/animation.cpp:457 #, c-format msgid "Cutscene '%s' not found" msgstr "" -#: engines/sword1/control.cpp:863 +#: engines/sword1/control.cpp:865 msgid "" "ScummVM found that you have old savefiles for Broken Sword 1 that should be " "converted.\n" @@ -2247,18 +2293,18 @@ msgstr "" "Trykk OK for å konvertere dem nå, ellers vil du bli spurt igjen neste gang " "du starter spillet." -#: engines/sword1/control.cpp:1232 +#: engines/sword1/control.cpp:1234 #, c-format msgid "" "Target new save game already exists!\n" "Would you like to keep the old save game (%s) or the new one (%s)?\n" msgstr "" -#: engines/sword1/control.cpp:1235 +#: engines/sword1/control.cpp:1237 msgid "Keep the old one" msgstr "Behold den gamle" -#: engines/sword1/control.cpp:1235 +#: engines/sword1/control.cpp:1237 msgid "Keep the new one" msgstr "Behold den nye" @@ -2266,7 +2312,7 @@ msgstr "Behold den nye" msgid "This is the end of the Broken Sword 1 Demo" msgstr "Dette er slutten på Broken Sword 1-demoen" -#: engines/sword2/animation.cpp:435 +#: engines/sword2/animation.cpp:419 msgid "" "PSX cutscenes found but ScummVM has been built without RGB color support" msgstr "" @@ -2279,6 +2325,17 @@ msgstr "" msgid "Show labels for objects on mouse hover" msgstr "" +#: engines/teenagent/resources.cpp:94 +msgid "" +"You're missing the 'teenagent.dat' file. Get it from the ScummVM website" +msgstr "" + +#: engines/teenagent/resources.cpp:115 +msgid "" +"The teenagent.dat file is compressed and zlib hasn't been included in this " +"executable. Please decompress it" +msgstr "" + #: engines/parallaction/saveload.cpp:133 #, c-format msgid "" @@ -2384,7 +2441,7 @@ msgstr "Ingen musikk" msgid "Amiga Audio Emulator" msgstr "Amiga Lydemulator" -#: audio/softsynth/adlib.cpp:1593 +#: audio/softsynth/adlib.cpp:2284 msgid "AdLib Emulator" msgstr "AdLib Emulator" @@ -2529,11 +2586,11 @@ msgstr "Touchpad-modus aktivert." msgid "Touchpad mode disabled." msgstr "Touchpad-modus deaktivert." -#: backends/platform/maemo/maemo.cpp:205 +#: backends/platform/maemo/maemo.cpp:209 msgid "Click Mode" msgstr "Klikkmodus" -#: backends/platform/maemo/maemo.cpp:211 +#: backends/platform/maemo/maemo.cpp:215 #: backends/platform/symbian/src/SymbianActions.cpp:42 #: backends/platform/wince/CEActionsPocket.cpp:60 #: backends/platform/wince/CEActionsSmartphone.cpp:43 @@ -2541,35 +2598,35 @@ msgstr "Klikkmodus" msgid "Left Click" msgstr "Venstreklikk" -#: backends/platform/maemo/maemo.cpp:214 +#: backends/platform/maemo/maemo.cpp:218 msgid "Middle Click" msgstr "Midtklikk" -#: backends/platform/maemo/maemo.cpp:217 +#: backends/platform/maemo/maemo.cpp:221 #: backends/platform/symbian/src/SymbianActions.cpp:43 #: backends/platform/wince/CEActionsSmartphone.cpp:44 #: backends/platform/bada/form.cpp:273 msgid "Right Click" msgstr "Høyreklikk" -#: backends/platform/sdl/macosx/appmenu_osx.mm:78 +#: backends/platform/sdl/macosx/appmenu_osx.mm:77 msgid "Hide ScummVM" msgstr "Skjul ScummVM" -#: backends/platform/sdl/macosx/appmenu_osx.mm:83 +#: backends/platform/sdl/macosx/appmenu_osx.mm:82 msgid "Hide Others" msgstr "Skjul andre" -#: backends/platform/sdl/macosx/appmenu_osx.mm:88 +#: backends/platform/sdl/macosx/appmenu_osx.mm:87 msgid "Show All" msgstr "Vis alle" -#: backends/platform/sdl/macosx/appmenu_osx.mm:110 -#: backends/platform/sdl/macosx/appmenu_osx.mm:121 +#: backends/platform/sdl/macosx/appmenu_osx.mm:109 +#: backends/platform/sdl/macosx/appmenu_osx.mm:120 msgid "Window" msgstr "Vindu" -#: backends/platform/sdl/macosx/appmenu_osx.mm:115 +#: backends/platform/sdl/macosx/appmenu_osx.mm:114 msgid "Minimize" msgstr "Minimer" @@ -2951,41 +3008,46 @@ msgstr "Oppstarter" msgid "Do you really want to quit?" msgstr "Vil du virkelig avslutte?" -#: backends/events/gph/gph-events.cpp:338 -#: backends/events/gph/gph-events.cpp:381 -#: backends/events/openpandora/op-events.cpp:139 +#: backends/events/gph/gph-events.cpp:386 +#: backends/events/gph/gph-events.cpp:429 +#: backends/events/openpandora/op-events.cpp:168 msgid "Touchscreen 'Tap Mode' - Left Click" msgstr "Touchskjerm 'Tapmodus' - Venstreklikk" -#: backends/events/gph/gph-events.cpp:340 -#: backends/events/gph/gph-events.cpp:383 -#: backends/events/openpandora/op-events.cpp:141 +#: backends/events/gph/gph-events.cpp:388 +#: backends/events/gph/gph-events.cpp:431 +#: backends/events/openpandora/op-events.cpp:170 msgid "Touchscreen 'Tap Mode' - Right Click" msgstr "Touchskjerm 'Tapmodus' - Høyreklikk" -#: backends/events/gph/gph-events.cpp:342 -#: backends/events/gph/gph-events.cpp:385 -#: backends/events/openpandora/op-events.cpp:143 +#: backends/events/gph/gph-events.cpp:390 +#: backends/events/gph/gph-events.cpp:433 +#: backends/events/openpandora/op-events.cpp:172 msgid "Touchscreen 'Tap Mode' - Hover (No Click)" msgstr "Touchskjerm 'Tapmodus' - Sveve (Ingen Klikk)" -#: backends/events/gph/gph-events.cpp:362 +#: backends/events/gph/gph-events.cpp:410 msgid "Maximum Volume" msgstr "Maksimalt Volum" -#: backends/events/gph/gph-events.cpp:364 +#: backends/events/gph/gph-events.cpp:412 msgid "Increasing Volume" msgstr "Øker volum" -#: backends/events/gph/gph-events.cpp:370 +#: backends/events/gph/gph-events.cpp:418 msgid "Minimal Volume" msgstr "Minimalt Volum" -#: backends/events/gph/gph-events.cpp:372 +#: backends/events/gph/gph-events.cpp:420 msgid "Decreasing Volume" msgstr "Senker volum" -#: backends/updates/macosx/macosx-updates.mm:65 +#: backends/events/openpandora/op-events.cpp:174 +#, fuzzy +msgid "Touchscreen 'Tap Mode' - Hover (DPad Clicks)" +msgstr "Touchskjerm 'Tapmodus' - Sveve (Ingen Klikk)" + +#: backends/updates/macosx/macosx-updates.mm:67 msgid "Check for Updates..." msgstr "Sjekk for oppdateringer..." diff --git a/po/nn_NO.po b/po/nn_NO.po index a88637b7c0..64c18effa8 100644 --- a/po/nn_NO.po +++ b/po/nn_NO.po @@ -1,5 +1,5 @@ # Norwegian (Nynorsk) translation for ScummVM. -# Copyright (C) 2010-2012 ScummVM Team +# Copyright (C) 2010-2013 ScummVM Team # This file is distributed under the same license as the ScummVM package. # Einar Johan T. Sømåen <einarjohants@gmail.com>, 2010. # @@ -7,14 +7,14 @@ msgid "" msgstr "" "Project-Id-Version: ScummVM 1.3.0svn\n" "Report-Msgid-Bugs-To: scummvm-devel@lists.sf.net\n" -"POT-Creation-Date: 2012-07-08 12:25+0100\n" +"POT-Creation-Date: 2012-12-01 17:22+0000\n" "PO-Revision-Date: 2011-04-25 23:07+0100\n" "Last-Translator: Einar Johan T. Sømåen <einarjohants@gmail.com>\n" "Language-Team: somaen <einarjohants@gmail.com>\n" +"Language: Norsk (nynorsk)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=iso-8859-1\n" "Content-Transfer-Encoding: 8bit\n" -"Language: Norsk (nynorsk)\n" "X-Poedit-Language: Norwegian Nynorsk\n" "X-Poedit-SourceCharset: iso-8859-1\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" @@ -48,10 +48,11 @@ msgstr "Gå tilbake" #: gui/browser.cpp:69 gui/chooser.cpp:45 gui/KeysDialog.cpp:43 #: gui/launcher.cpp:345 gui/massadd.cpp:94 gui/options.cpp:1228 -#: gui/saveload.cpp:64 gui/saveload.cpp:173 gui/themebrowser.cpp:54 -#: engines/engine.cpp:442 engines/scumm/dialogs.cpp:190 -#: engines/sword1/control.cpp:865 engines/parallaction/saveload.cpp:274 -#: backends/platform/wii/options.cpp:48 +#: gui/saveload-dialog.cpp:215 gui/saveload-dialog.cpp:275 +#: gui/saveload-dialog.cpp:545 gui/saveload-dialog.cpp:919 +#: gui/themebrowser.cpp:54 engines/engine.cpp:442 +#: engines/scumm/dialogs.cpp:190 engines/sword1/control.cpp:867 +#: engines/parallaction/saveload.cpp:274 backends/platform/wii/options.cpp:48 #: backends/events/default/default-events.cpp:191 #: backends/events/default/default-events.cpp:213 msgid "Cancel" @@ -72,15 +73,15 @@ msgstr "Steng" msgid "Mouse click" msgstr "Musklikk" -#: gui/gui-manager.cpp:122 base/main.cpp:300 +#: gui/gui-manager.cpp:122 base/main.cpp:301 msgid "Display keyboard" msgstr "Syn Tastatur" -#: gui/gui-manager.cpp:126 base/main.cpp:304 +#: gui/gui-manager.cpp:126 base/main.cpp:305 msgid "Remap keys" msgstr "Omkople tastar" -#: gui/gui-manager.cpp:129 base/main.cpp:307 +#: gui/gui-manager.cpp:129 base/main.cpp:308 #, fuzzy msgid "Toggle FullScreen" msgstr "Veksle fullskjerm" @@ -95,16 +96,16 @@ msgstr "Kople" #: gui/KeysDialog.cpp:42 gui/launcher.cpp:346 gui/launcher.cpp:1001 #: gui/launcher.cpp:1005 gui/massadd.cpp:91 gui/options.cpp:1229 -#: engines/engine.cpp:361 engines/engine.cpp:372 engines/scumm/dialogs.cpp:192 -#: engines/scumm/scumm.cpp:1775 engines/agos/animation.cpp:561 -#: engines/groovie/script.cpp:420 engines/sky/compact.cpp:131 -#: engines/sky/compact.cpp:141 engines/sword1/animation.cpp:539 -#: engines/sword1/animation.cpp:560 engines/sword1/animation.cpp:570 -#: engines/sword1/animation.cpp:577 engines/sword1/control.cpp:865 -#: engines/sword1/logic.cpp:1633 engines/sword2/animation.cpp:435 -#: engines/sword2/animation.cpp:455 engines/sword2/animation.cpp:465 -#: engines/sword2/animation.cpp:474 engines/parallaction/saveload.cpp:274 -#: backends/platform/wii/options.cpp:47 +#: gui/saveload-dialog.cpp:920 engines/engine.cpp:361 engines/engine.cpp:372 +#: engines/scumm/dialogs.cpp:192 engines/scumm/scumm.cpp:1776 +#: engines/agos/animation.cpp:558 engines/groovie/script.cpp:420 +#: engines/sky/compact.cpp:131 engines/sky/compact.cpp:141 +#: engines/sword1/animation.cpp:519 engines/sword1/animation.cpp:540 +#: engines/sword1/animation.cpp:550 engines/sword1/animation.cpp:557 +#: engines/sword1/control.cpp:867 engines/sword1/logic.cpp:1633 +#: engines/sword2/animation.cpp:419 engines/sword2/animation.cpp:439 +#: engines/sword2/animation.cpp:449 engines/sword2/animation.cpp:458 +#: engines/parallaction/saveload.cpp:274 backends/platform/wii/options.cpp:47 #: backends/platform/wince/CELauncherDialog.cpp:54 msgid "OK" msgstr "OK" @@ -360,7 +361,7 @@ msgstr "" msgid "~Q~uit" msgstr "~A~vslutt" -#: gui/launcher.cpp:621 backends/platform/sdl/macosx/appmenu_osx.mm:96 +#: gui/launcher.cpp:621 backends/platform/sdl/macosx/appmenu_osx.mm:95 msgid "Quit ScummVM" msgstr "Avslutt ScummVM" @@ -368,7 +369,7 @@ msgstr "Avslutt ScummVM" msgid "A~b~out..." msgstr "~O~m..." -#: gui/launcher.cpp:622 backends/platform/sdl/macosx/appmenu_osx.mm:70 +#: gui/launcher.cpp:622 backends/platform/sdl/macosx/appmenu_osx.mm:69 msgid "About ScummVM" msgstr "Om ScummVM" @@ -443,13 +444,13 @@ msgstr "Søk i spelliste" msgid "Search:" msgstr "Søk:" -#: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/mohawk/myst.cpp:255 +#: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/mohawk/myst.cpp:245 #: engines/mohawk/riven.cpp:716 engines/cruise/menu.cpp:214 msgid "Load game:" msgstr "Åpne spel:" #: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/scumm/dialogs.cpp:188 -#: engines/mohawk/myst.cpp:255 engines/mohawk/riven.cpp:716 +#: engines/mohawk/myst.cpp:245 engines/mohawk/riven.cpp:716 #: engines/cruise/menu.cpp:214 backends/platform/wince/CEActionsPocket.cpp:267 #: backends/platform/wince/CEActionsSmartphone.cpp:231 msgid "Load" @@ -920,68 +921,104 @@ msgstr "" "Temaet du har valt støttar ikkje det aktive språket. Om du vil nytte dette " "temaet må du bytte til eit anna språk først." -#: gui/saveload.cpp:59 gui/saveload.cpp:257 +#: gui/saveload-dialog.cpp:166 +msgid "List view" +msgstr "" + +#: gui/saveload-dialog.cpp:167 +msgid "Grid view" +msgstr "" + +#: gui/saveload-dialog.cpp:210 gui/saveload-dialog.cpp:358 msgid "No date saved" msgstr "Ingen dato lagra" -#: gui/saveload.cpp:60 gui/saveload.cpp:258 +#: gui/saveload-dialog.cpp:211 gui/saveload-dialog.cpp:359 msgid "No time saved" msgstr "Inga tid lagra" -#: gui/saveload.cpp:61 gui/saveload.cpp:259 +#: gui/saveload-dialog.cpp:212 gui/saveload-dialog.cpp:360 msgid "No playtime saved" msgstr "Inga speletid lagra" -#: gui/saveload.cpp:68 gui/saveload.cpp:173 +#: gui/saveload-dialog.cpp:219 gui/saveload-dialog.cpp:275 msgid "Delete" msgstr "Slett" -#: gui/saveload.cpp:172 +#: gui/saveload-dialog.cpp:274 msgid "Do you really want to delete this savegame?" msgstr "Vil du verkeleg slette det lagra spelet?" -#: gui/saveload.cpp:282 +#: gui/saveload-dialog.cpp:383 gui/saveload-dialog.cpp:872 msgid "Date: " msgstr "Dato: " -#: gui/saveload.cpp:286 +#: gui/saveload-dialog.cpp:387 gui/saveload-dialog.cpp:878 msgid "Time: " msgstr "Tid: " -#: gui/saveload.cpp:292 +#: gui/saveload-dialog.cpp:393 gui/saveload-dialog.cpp:886 msgid "Playtime: " msgstr "Speletid: " -#: gui/saveload.cpp:305 gui/saveload.cpp:372 +#: gui/saveload-dialog.cpp:406 gui/saveload-dialog.cpp:494 msgid "Untitled savestate" msgstr "Ikkje navngjeven speltilstand" +#: gui/saveload-dialog.cpp:546 +msgid "Next" +msgstr "" + +#: gui/saveload-dialog.cpp:549 +msgid "Prev" +msgstr "" + +#: gui/saveload-dialog.cpp:736 +#, fuzzy +msgid "New Save" +msgstr "Lagre" + +#: gui/saveload-dialog.cpp:736 +#, fuzzy +msgid "Create a new save game" +msgstr "Full speltittel:" + +#: gui/saveload-dialog.cpp:865 +#, fuzzy +msgid "Name: " +msgstr "Namn:" + +#: gui/saveload-dialog.cpp:937 +#, c-format +msgid "Enter a description for slot %d:" +msgstr "" + #: gui/themebrowser.cpp:44 msgid "Select a Theme" msgstr "Vel eit tema" -#: gui/ThemeEngine.cpp:335 +#: gui/ThemeEngine.cpp:337 msgid "Disabled GFX" msgstr "Deaktivert GFX" -#: gui/ThemeEngine.cpp:335 +#: gui/ThemeEngine.cpp:337 msgctxt "lowres" msgid "Disabled GFX" msgstr "Deaktivert GFX" -#: gui/ThemeEngine.cpp:336 +#: gui/ThemeEngine.cpp:338 msgid "Standard Renderer (16bpp)" msgstr "Standard Teiknar (16bpp)" -#: gui/ThemeEngine.cpp:336 +#: gui/ThemeEngine.cpp:338 msgid "Standard (16bpp)" msgstr "Standard (16bpp)" -#: gui/ThemeEngine.cpp:338 +#: gui/ThemeEngine.cpp:340 msgid "Antialiased Renderer (16bpp)" msgstr "Antialiased Teiknar (16bpp)" -#: gui/ThemeEngine.cpp:338 +#: gui/ThemeEngine.cpp:340 msgid "Antialiased (16bpp)" msgstr "Antialiased (16bpp)" @@ -989,35 +1026,35 @@ msgstr "Antialiased (16bpp)" msgid "Clear value" msgstr "Tøm verdi" -#: base/main.cpp:209 +#: base/main.cpp:210 #, c-format msgid "Engine does not support debug level '%s'" msgstr "Motoren støttar ikkje debug-nivå '%s'" -#: base/main.cpp:287 +#: base/main.cpp:288 msgid "Menu" msgstr "Meny" -#: base/main.cpp:290 backends/platform/symbian/src/SymbianActions.cpp:45 +#: base/main.cpp:291 backends/platform/symbian/src/SymbianActions.cpp:45 #: backends/platform/wince/CEActionsPocket.cpp:45 #: backends/platform/wince/CEActionsSmartphone.cpp:46 msgid "Skip" msgstr "Hopp over" -#: base/main.cpp:293 backends/platform/symbian/src/SymbianActions.cpp:50 +#: base/main.cpp:294 backends/platform/symbian/src/SymbianActions.cpp:50 #: backends/platform/wince/CEActionsPocket.cpp:42 msgid "Pause" msgstr "Pause" -#: base/main.cpp:296 +#: base/main.cpp:297 msgid "Skip line" msgstr "Hopp over linje" -#: base/main.cpp:467 +#: base/main.cpp:468 msgid "Error running game:" msgstr "Feil under køyring av spel:" -#: base/main.cpp:491 +#: base/main.cpp:492 msgid "Could not find any engine capable of running the selected game" msgstr "Kunne ikkje finne nokon motor som kunne køyre det velde spelet." @@ -1086,16 +1123,16 @@ msgstr "" msgid "Unknown error" msgstr "Ukjend feil" -#: engines/advancedDetector.cpp:324 +#: engines/advancedDetector.cpp:316 #, c-format msgid "The game in '%s' seems to be unknown." msgstr "" -#: engines/advancedDetector.cpp:325 +#: engines/advancedDetector.cpp:317 msgid "Please, report the following data to the ScummVM team along with name" msgstr "" -#: engines/advancedDetector.cpp:327 +#: engines/advancedDetector.cpp:319 msgid "of the game you tried to add and its version/language/etc.:" msgstr "" @@ -1135,13 +1172,13 @@ msgid "~R~eturn to Launcher" msgstr "~T~ilbake til oppstarter" #: engines/dialogs.cpp:115 engines/agi/saveload.cpp:803 -#: engines/cruise/menu.cpp:212 engines/sci/engine/kfile.cpp:735 +#: engines/cruise/menu.cpp:212 engines/sci/engine/kfile.cpp:742 msgid "Save game:" msgstr "Lagra spel:" #: engines/dialogs.cpp:115 engines/agi/saveload.cpp:803 #: engines/scumm/dialogs.cpp:187 engines/cruise/menu.cpp:212 -#: engines/sci/engine/kfile.cpp:735 +#: engines/sci/engine/kfile.cpp:742 #: backends/platform/symbian/src/SymbianActions.cpp:44 #: backends/platform/wince/CEActionsPocket.cpp:43 #: backends/platform/wince/CEActionsPocket.cpp:267 @@ -1232,21 +1269,21 @@ msgstr "" msgid "Start anyway" msgstr "" -#: engines/agi/detection.cpp:145 engines/dreamweb/detection.cpp:47 -#: engines/sci/detection.cpp:390 +#: engines/agi/detection.cpp:142 engines/dreamweb/detection.cpp:47 +#: engines/sci/detection.cpp:393 msgid "Use original save/load screens" msgstr "" -#: engines/agi/detection.cpp:146 engines/dreamweb/detection.cpp:48 -#: engines/sci/detection.cpp:391 +#: engines/agi/detection.cpp:143 engines/dreamweb/detection.cpp:48 +#: engines/sci/detection.cpp:394 msgid "Use the original save/load screens, instead of the ScummVM ones" msgstr "" -#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:831 +#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:838 msgid "Restore game:" msgstr "Gjenopprett spel:" -#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:831 +#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:838 msgid "Restore" msgstr "Gjenopprett" @@ -1259,55 +1296,55 @@ msgstr "Øvre høgre gjenstand" msgid "Display graphics using the game's bright palette" msgstr "" -#: engines/sci/detection.cpp:370 +#: engines/sci/detection.cpp:373 msgid "EGA undithering" msgstr "" -#: engines/sci/detection.cpp:371 +#: engines/sci/detection.cpp:374 msgid "Enable undithering in EGA games" msgstr "" -#: engines/sci/detection.cpp:380 +#: engines/sci/detection.cpp:383 msgid "Prefer digital sound effects" msgstr "" -#: engines/sci/detection.cpp:381 +#: engines/sci/detection.cpp:384 msgid "Prefer digital sound effects instead of synthesized ones" msgstr "" -#: engines/sci/detection.cpp:400 +#: engines/sci/detection.cpp:403 msgid "Use IMF/Yamaha FB-01 for MIDI output" msgstr "" -#: engines/sci/detection.cpp:401 +#: engines/sci/detection.cpp:404 msgid "" "Use an IBM Music Feature card or a Yamaha FB-01 FM synth module for MIDI " "output" msgstr "" -#: engines/sci/detection.cpp:411 +#: engines/sci/detection.cpp:414 msgid "Use CD audio" msgstr "" -#: engines/sci/detection.cpp:412 +#: engines/sci/detection.cpp:415 msgid "Use CD audio instead of in-game audio, if available" msgstr "" -#: engines/sci/detection.cpp:422 +#: engines/sci/detection.cpp:425 msgid "Use Windows cursors" msgstr "" -#: engines/sci/detection.cpp:423 +#: engines/sci/detection.cpp:426 msgid "" "Use the Windows cursors (smaller and monochrome) instead of the DOS ones" msgstr "" -#: engines/sci/detection.cpp:433 +#: engines/sci/detection.cpp:436 #, fuzzy msgid "Use silver cursors" msgstr "Vanleg peikar" -#: engines/sci/detection.cpp:434 +#: engines/sci/detection.cpp:437 msgid "" "Use the alternate set of silver cursors, instead of the normal golden ones" msgstr "" @@ -1968,14 +2005,14 @@ msgstr "Fly til høgre" msgid "Fly to lower right" msgstr "Fly til nedre høgre" -#: engines/scumm/scumm.cpp:1773 +#: engines/scumm/scumm.cpp:1774 #, c-format msgid "" "Native MIDI support requires the Roland Upgrade from LucasArts,\n" "but %s is missing. Using AdLib instead." msgstr "" -#: engines/scumm/scumm.cpp:2278 engines/agos/saveload.cpp:202 +#: engines/scumm/scumm.cpp:2295 engines/agos/saveload.cpp:220 #, c-format msgid "" "Failed to save game state to file:\n" @@ -1983,7 +2020,7 @@ msgid "" "%s" msgstr "" -#: engines/scumm/scumm.cpp:2285 engines/agos/saveload.cpp:167 +#: engines/scumm/scumm.cpp:2302 engines/agos/saveload.cpp:185 #, c-format msgid "" "Failed to load game state from file:\n" @@ -1991,7 +2028,7 @@ msgid "" "%s" msgstr "" -#: engines/scumm/scumm.cpp:2297 engines/agos/saveload.cpp:210 +#: engines/scumm/scumm.cpp:2314 engines/agos/saveload.cpp:228 #, c-format msgid "" "Successfully saved game state in file:\n" @@ -1999,7 +2036,7 @@ msgid "" "%s" msgstr "" -#: engines/scumm/scumm.cpp:2512 +#: engines/scumm/scumm.cpp:2529 msgid "" "Usually, Maniac Mansion would start now. But ScummVM doesn't do that yet. To " "play it, go to 'Add Game' in the ScummVM start menu and select the 'Maniac' " @@ -2036,17 +2073,17 @@ msgstr "ScummVM Hovudmeny" msgid "~W~ater Effect Enabled" msgstr "~V~anneffekt aktivert" -#: engines/agos/animation.cpp:560 +#: engines/agos/animation.cpp:557 #, c-format msgid "Cutscene file '%s' not found!" msgstr "" #: engines/gob/inter_playtoons.cpp:256 engines/gob/inter_v2.cpp:1287 -#: engines/tinsel/saveload.cpp:502 +#: engines/tinsel/saveload.cpp:532 msgid "Failed to load game state from file." msgstr "" -#: engines/gob/inter_v2.cpp:1357 engines/tinsel/saveload.cpp:515 +#: engines/gob/inter_v2.cpp:1357 engines/tinsel/saveload.cpp:545 msgid "Failed to save game state to file." msgstr "" @@ -2173,7 +2210,7 @@ msgstr "~V~al" msgid "Choose Spell" msgstr "Vel" -#: engines/kyra/sound_midi.cpp:475 +#: engines/kyra/sound_midi.cpp:477 msgid "" "You appear to be using a General MIDI device,\n" "but your game only supports Roland MT32 MIDI.\n" @@ -2182,12 +2219,12 @@ msgid "" "some tracks sound incorrect." msgstr "" -#: engines/queen/queen.cpp:59 engines/sky/detection.cpp:44 -msgid "Floppy intro" +#: engines/queen/queen.cpp:59 +msgid "Alternative intro" msgstr "" -#: engines/queen/queen.cpp:60 engines/sky/detection.cpp:45 -msgid "Use the floppy version's intro (CD version only)" +#: engines/queen/queen.cpp:60 +msgid "Use an alternative game intro (CD version only)" msgstr "" #: engines/sky/compact.cpp:130 @@ -2202,25 +2239,33 @@ msgid "" "Please (re)download it from www.scummvm.org" msgstr "" -#: engines/sword1/animation.cpp:539 +#: engines/sky/detection.cpp:44 +msgid "Floppy intro" +msgstr "" + +#: engines/sky/detection.cpp:45 +msgid "Use the floppy version's intro (CD version only)" +msgstr "" + +#: engines/sword1/animation.cpp:519 #, c-format msgid "PSX stream cutscene '%s' cannot be played in paletted mode" msgstr "" -#: engines/sword1/animation.cpp:560 engines/sword2/animation.cpp:455 +#: engines/sword1/animation.cpp:540 engines/sword2/animation.cpp:439 msgid "DXA cutscenes found but ScummVM has been built without zlib support" msgstr "" -#: engines/sword1/animation.cpp:570 engines/sword2/animation.cpp:465 +#: engines/sword1/animation.cpp:550 engines/sword2/animation.cpp:449 msgid "MPEG2 cutscenes are no longer supported" msgstr "" -#: engines/sword1/animation.cpp:576 engines/sword2/animation.cpp:473 +#: engines/sword1/animation.cpp:556 engines/sword2/animation.cpp:457 #, c-format msgid "Cutscene '%s' not found" msgstr "" -#: engines/sword1/control.cpp:863 +#: engines/sword1/control.cpp:865 msgid "" "ScummVM found that you have old savefiles for Broken Sword 1 that should be " "converted.\n" @@ -2231,18 +2276,18 @@ msgid "" "time you start the game.\n" msgstr "" -#: engines/sword1/control.cpp:1232 +#: engines/sword1/control.cpp:1234 #, c-format msgid "" "Target new save game already exists!\n" "Would you like to keep the old save game (%s) or the new one (%s)?\n" msgstr "" -#: engines/sword1/control.cpp:1235 +#: engines/sword1/control.cpp:1237 msgid "Keep the old one" msgstr "" -#: engines/sword1/control.cpp:1235 +#: engines/sword1/control.cpp:1237 msgid "Keep the new one" msgstr "" @@ -2250,7 +2295,7 @@ msgstr "" msgid "This is the end of the Broken Sword 1 Demo" msgstr "" -#: engines/sword2/animation.cpp:435 +#: engines/sword2/animation.cpp:419 msgid "" "PSX cutscenes found but ScummVM has been built without RGB color support" msgstr "" @@ -2263,6 +2308,17 @@ msgstr "" msgid "Show labels for objects on mouse hover" msgstr "" +#: engines/teenagent/resources.cpp:94 +msgid "" +"You're missing the 'teenagent.dat' file. Get it from the ScummVM website" +msgstr "" + +#: engines/teenagent/resources.cpp:115 +msgid "" +"The teenagent.dat file is compressed and zlib hasn't been included in this " +"executable. Please decompress it" +msgstr "" + #: engines/parallaction/saveload.cpp:133 #, c-format msgid "" @@ -2351,7 +2407,7 @@ msgstr "Ingen musikk" msgid "Amiga Audio Emulator" msgstr "Amiga Lydemulator" -#: audio/softsynth/adlib.cpp:1593 +#: audio/softsynth/adlib.cpp:2284 msgid "AdLib Emulator" msgstr "AdLib Emulator" @@ -2497,11 +2553,11 @@ msgstr "" msgid "Touchpad mode disabled." msgstr "" -#: backends/platform/maemo/maemo.cpp:205 +#: backends/platform/maemo/maemo.cpp:209 msgid "Click Mode" msgstr "" -#: backends/platform/maemo/maemo.cpp:211 +#: backends/platform/maemo/maemo.cpp:215 #: backends/platform/symbian/src/SymbianActions.cpp:42 #: backends/platform/wince/CEActionsPocket.cpp:60 #: backends/platform/wince/CEActionsSmartphone.cpp:43 @@ -2509,37 +2565,37 @@ msgstr "" msgid "Left Click" msgstr "Venstreklikk" -#: backends/platform/maemo/maemo.cpp:214 +#: backends/platform/maemo/maemo.cpp:218 #, fuzzy msgid "Middle Click" msgstr "Midtre venstre gjenstand" -#: backends/platform/maemo/maemo.cpp:217 +#: backends/platform/maemo/maemo.cpp:221 #: backends/platform/symbian/src/SymbianActions.cpp:43 #: backends/platform/wince/CEActionsSmartphone.cpp:44 #: backends/platform/bada/form.cpp:273 msgid "Right Click" msgstr "Høgreklikk" -#: backends/platform/sdl/macosx/appmenu_osx.mm:78 +#: backends/platform/sdl/macosx/appmenu_osx.mm:77 #, fuzzy msgid "Hide ScummVM" msgstr "Avslutt ScummVM" -#: backends/platform/sdl/macosx/appmenu_osx.mm:83 +#: backends/platform/sdl/macosx/appmenu_osx.mm:82 msgid "Hide Others" msgstr "" -#: backends/platform/sdl/macosx/appmenu_osx.mm:88 +#: backends/platform/sdl/macosx/appmenu_osx.mm:87 msgid "Show All" msgstr "" -#: backends/platform/sdl/macosx/appmenu_osx.mm:110 -#: backends/platform/sdl/macosx/appmenu_osx.mm:121 +#: backends/platform/sdl/macosx/appmenu_osx.mm:109 +#: backends/platform/sdl/macosx/appmenu_osx.mm:120 msgid "Window" msgstr "" -#: backends/platform/sdl/macosx/appmenu_osx.mm:115 +#: backends/platform/sdl/macosx/appmenu_osx.mm:114 msgid "Minimize" msgstr "" @@ -2928,43 +2984,47 @@ msgstr "Slå" msgid "Do you really want to quit?" msgstr "Vil du avslutte?" -#: backends/events/gph/gph-events.cpp:338 -#: backends/events/gph/gph-events.cpp:381 -#: backends/events/openpandora/op-events.cpp:139 +#: backends/events/gph/gph-events.cpp:386 +#: backends/events/gph/gph-events.cpp:429 +#: backends/events/openpandora/op-events.cpp:168 msgid "Touchscreen 'Tap Mode' - Left Click" msgstr "" -#: backends/events/gph/gph-events.cpp:340 -#: backends/events/gph/gph-events.cpp:383 -#: backends/events/openpandora/op-events.cpp:141 +#: backends/events/gph/gph-events.cpp:388 +#: backends/events/gph/gph-events.cpp:431 +#: backends/events/openpandora/op-events.cpp:170 msgid "Touchscreen 'Tap Mode' - Right Click" msgstr "" -#: backends/events/gph/gph-events.cpp:342 -#: backends/events/gph/gph-events.cpp:385 -#: backends/events/openpandora/op-events.cpp:143 +#: backends/events/gph/gph-events.cpp:390 +#: backends/events/gph/gph-events.cpp:433 +#: backends/events/openpandora/op-events.cpp:172 msgid "Touchscreen 'Tap Mode' - Hover (No Click)" msgstr "" -#: backends/events/gph/gph-events.cpp:362 +#: backends/events/gph/gph-events.cpp:410 #, fuzzy msgid "Maximum Volume" msgstr "Volum" -#: backends/events/gph/gph-events.cpp:364 +#: backends/events/gph/gph-events.cpp:412 msgid "Increasing Volume" msgstr "" -#: backends/events/gph/gph-events.cpp:370 +#: backends/events/gph/gph-events.cpp:418 #, fuzzy msgid "Minimal Volume" msgstr "Volum" -#: backends/events/gph/gph-events.cpp:372 +#: backends/events/gph/gph-events.cpp:420 msgid "Decreasing Volume" msgstr "" -#: backends/updates/macosx/macosx-updates.mm:65 +#: backends/events/openpandora/op-events.cpp:174 +msgid "Touchscreen 'Tap Mode' - Hover (DPad Clicks)" +msgstr "" + +#: backends/updates/macosx/macosx-updates.mm:67 msgid "Check for Updates..." msgstr "" diff --git a/po/pl_PL.po b/po/pl_PL.po index 5d3655f8b6..154d05a411 100644 --- a/po/pl_PL.po +++ b/po/pl_PL.po @@ -1,20 +1,20 @@ # Polish translation for ScummVM. -# Copyright (C) 2010-2012 ScummVM Team +# Copyright (C) 2010-2013 ScummVM Team # This file is distributed under the same license as the ScummVM package. -# Grajpopolsku.pl <grajpopolsku@gmail.com>, 2011. +# Grajpopolsku.pl <grajpopolsku@gmail.com>, 2011-2013. # msgid "" msgstr "" "Project-Id-Version: ScummVM 1.3.0\n" "Report-Msgid-Bugs-To: scummvm-devel@lists.sf.net\n" -"POT-Creation-Date: 2012-07-08 12:25+0100\n" -"PO-Revision-Date: 2011-10-24 21:14+0100\n" +"POT-Creation-Date: 2012-12-01 17:22+0000\n" +"PO-Revision-Date: 2012-07-29 15:49+0100\n" "Last-Translator: Micha³ Zi±bkowski <mziab@o2.pl>\n" "Language-Team: Grajpopolsku.pl <grajpopolsku@gmail.com>\n" +"Language: Polski\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=iso-8859-2\n" "Content-Transfer-Encoding: 8bit\n" -"Language: Polski\n" "X-Poedit-KeywordsList: _;gettext;gettext_noop\n" "X-Poedit-Basepath: .\n" "X-Poedit-Language: Polish\n" @@ -48,10 +48,11 @@ msgstr "W górê" #: gui/browser.cpp:69 gui/chooser.cpp:45 gui/KeysDialog.cpp:43 #: gui/launcher.cpp:345 gui/massadd.cpp:94 gui/options.cpp:1228 -#: gui/saveload.cpp:64 gui/saveload.cpp:173 gui/themebrowser.cpp:54 -#: engines/engine.cpp:442 engines/scumm/dialogs.cpp:190 -#: engines/sword1/control.cpp:865 engines/parallaction/saveload.cpp:274 -#: backends/platform/wii/options.cpp:48 +#: gui/saveload-dialog.cpp:215 gui/saveload-dialog.cpp:275 +#: gui/saveload-dialog.cpp:545 gui/saveload-dialog.cpp:919 +#: gui/themebrowser.cpp:54 engines/engine.cpp:442 +#: engines/scumm/dialogs.cpp:190 engines/sword1/control.cpp:867 +#: engines/parallaction/saveload.cpp:274 backends/platform/wii/options.cpp:48 #: backends/events/default/default-events.cpp:191 #: backends/events/default/default-events.cpp:213 msgid "Cancel" @@ -72,16 +73,15 @@ msgstr "Zamknij" msgid "Mouse click" msgstr "Klikniêcie" -#: gui/gui-manager.cpp:122 base/main.cpp:300 +#: gui/gui-manager.cpp:122 base/main.cpp:301 msgid "Display keyboard" msgstr "Wy¶wietl klawiaturê" -#: gui/gui-manager.cpp:126 base/main.cpp:304 +#: gui/gui-manager.cpp:126 base/main.cpp:305 msgid "Remap keys" msgstr "Dostosuj klawisze" -#: gui/gui-manager.cpp:129 base/main.cpp:307 -#, fuzzy +#: gui/gui-manager.cpp:129 base/main.cpp:308 msgid "Toggle FullScreen" msgstr "W³±cz/wy³±cz pe³ny ekran" @@ -95,16 +95,16 @@ msgstr "Przypisz" #: gui/KeysDialog.cpp:42 gui/launcher.cpp:346 gui/launcher.cpp:1001 #: gui/launcher.cpp:1005 gui/massadd.cpp:91 gui/options.cpp:1229 -#: engines/engine.cpp:361 engines/engine.cpp:372 engines/scumm/dialogs.cpp:192 -#: engines/scumm/scumm.cpp:1775 engines/agos/animation.cpp:561 -#: engines/groovie/script.cpp:420 engines/sky/compact.cpp:131 -#: engines/sky/compact.cpp:141 engines/sword1/animation.cpp:539 -#: engines/sword1/animation.cpp:560 engines/sword1/animation.cpp:570 -#: engines/sword1/animation.cpp:577 engines/sword1/control.cpp:865 -#: engines/sword1/logic.cpp:1633 engines/sword2/animation.cpp:435 -#: engines/sword2/animation.cpp:455 engines/sword2/animation.cpp:465 -#: engines/sword2/animation.cpp:474 engines/parallaction/saveload.cpp:274 -#: backends/platform/wii/options.cpp:47 +#: gui/saveload-dialog.cpp:920 engines/engine.cpp:361 engines/engine.cpp:372 +#: engines/scumm/dialogs.cpp:192 engines/scumm/scumm.cpp:1776 +#: engines/agos/animation.cpp:558 engines/groovie/script.cpp:420 +#: engines/sky/compact.cpp:131 engines/sky/compact.cpp:141 +#: engines/sword1/animation.cpp:519 engines/sword1/animation.cpp:540 +#: engines/sword1/animation.cpp:550 engines/sword1/animation.cpp:557 +#: engines/sword1/control.cpp:867 engines/sword1/logic.cpp:1633 +#: engines/sword2/animation.cpp:419 engines/sword2/animation.cpp:439 +#: engines/sword2/animation.cpp:449 engines/sword2/animation.cpp:458 +#: engines/parallaction/saveload.cpp:274 backends/platform/wii/options.cpp:47 #: backends/platform/wince/CELauncherDialog.cpp:54 msgid "OK" msgstr "OK" @@ -195,9 +195,8 @@ msgid "Platform:" msgstr "Platforma:" #: gui/launcher.cpp:231 -#, fuzzy msgid "Engine" -msgstr "Zbadaj" +msgstr "Silnik" #: gui/launcher.cpp:239 gui/options.cpp:1062 gui/options.cpp:1079 msgid "Graphics" @@ -358,7 +357,7 @@ msgstr "Identyfikator jest ju¿ zajêty. Wybierz inny." msgid "~Q~uit" msgstr "~Z~akoñcz" -#: gui/launcher.cpp:621 backends/platform/sdl/macosx/appmenu_osx.mm:96 +#: gui/launcher.cpp:621 backends/platform/sdl/macosx/appmenu_osx.mm:95 msgid "Quit ScummVM" msgstr "Zakoñcz ScummVM" @@ -366,7 +365,7 @@ msgstr "Zakoñcz ScummVM" msgid "A~b~out..." msgstr "I~n~formacje..." -#: gui/launcher.cpp:622 backends/platform/sdl/macosx/appmenu_osx.mm:70 +#: gui/launcher.cpp:622 backends/platform/sdl/macosx/appmenu_osx.mm:69 msgid "About ScummVM" msgstr "Ksi±¿ka ScummVM" @@ -441,13 +440,13 @@ msgstr "Wyszukaj grê na li¶cie" msgid "Search:" msgstr "Szukaj" -#: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/mohawk/myst.cpp:255 +#: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/mohawk/myst.cpp:245 #: engines/mohawk/riven.cpp:716 engines/cruise/menu.cpp:214 msgid "Load game:" msgstr "Wczytaj grê:" #: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/scumm/dialogs.cpp:188 -#: engines/mohawk/myst.cpp:255 engines/mohawk/riven.cpp:716 +#: engines/mohawk/myst.cpp:245 engines/mohawk/riven.cpp:716 #: engines/cruise/menu.cpp:214 backends/platform/wince/CEActionsPocket.cpp:267 #: backends/platform/wince/CEActionsSmartphone.cpp:231 msgid "Load" @@ -922,68 +921,104 @@ msgstr "" "Wybrany styl nie obs³uguje obecnego jêzyka. Je¶li chcesz go u¿ywaæ, zmieñ " "najpierw swój jêzyk." -#: gui/saveload.cpp:59 gui/saveload.cpp:257 +#: gui/saveload-dialog.cpp:166 +msgid "List view" +msgstr "" + +#: gui/saveload-dialog.cpp:167 +msgid "Grid view" +msgstr "" + +#: gui/saveload-dialog.cpp:210 gui/saveload-dialog.cpp:358 msgid "No date saved" msgstr "Brak daty" -#: gui/saveload.cpp:60 gui/saveload.cpp:258 +#: gui/saveload-dialog.cpp:211 gui/saveload-dialog.cpp:359 msgid "No time saved" msgstr "Brak godziny" -#: gui/saveload.cpp:61 gui/saveload.cpp:259 +#: gui/saveload-dialog.cpp:212 gui/saveload-dialog.cpp:360 msgid "No playtime saved" msgstr "Brak czasu gry" -#: gui/saveload.cpp:68 gui/saveload.cpp:173 +#: gui/saveload-dialog.cpp:219 gui/saveload-dialog.cpp:275 msgid "Delete" msgstr "Skasuj" -#: gui/saveload.cpp:172 +#: gui/saveload-dialog.cpp:274 msgid "Do you really want to delete this savegame?" msgstr "Na pewno chcesz skasowaæ ten zapis?" -#: gui/saveload.cpp:282 +#: gui/saveload-dialog.cpp:383 gui/saveload-dialog.cpp:872 msgid "Date: " msgstr "Data: " -#: gui/saveload.cpp:286 +#: gui/saveload-dialog.cpp:387 gui/saveload-dialog.cpp:878 msgid "Time: " msgstr "Czas: " -#: gui/saveload.cpp:292 +#: gui/saveload-dialog.cpp:393 gui/saveload-dialog.cpp:886 msgid "Playtime: " msgstr "Czas gry: " -#: gui/saveload.cpp:305 gui/saveload.cpp:372 +#: gui/saveload-dialog.cpp:406 gui/saveload-dialog.cpp:494 msgid "Untitled savestate" msgstr "Zapis bez nazwy" +#: gui/saveload-dialog.cpp:546 +msgid "Next" +msgstr "" + +#: gui/saveload-dialog.cpp:549 +msgid "Prev" +msgstr "" + +#: gui/saveload-dialog.cpp:736 +#, fuzzy +msgid "New Save" +msgstr "Zapisz" + +#: gui/saveload-dialog.cpp:736 +#, fuzzy +msgid "Create a new save game" +msgstr "Nie uda³o siê zapisaæ stanu gry" + +#: gui/saveload-dialog.cpp:865 +#, fuzzy +msgid "Name: " +msgstr "Nazwa:" + +#: gui/saveload-dialog.cpp:937 +#, c-format +msgid "Enter a description for slot %d:" +msgstr "" + #: gui/themebrowser.cpp:44 msgid "Select a Theme" msgstr "Wybierz styl" -#: gui/ThemeEngine.cpp:335 +#: gui/ThemeEngine.cpp:337 msgid "Disabled GFX" msgstr "Wy³±czona grafika" -#: gui/ThemeEngine.cpp:335 +#: gui/ThemeEngine.cpp:337 msgctxt "lowres" msgid "Disabled GFX" msgstr "Wy³±czona grafika" -#: gui/ThemeEngine.cpp:336 +#: gui/ThemeEngine.cpp:338 msgid "Standard Renderer (16bpp)" msgstr "Standardowy renderer (16bpp)" -#: gui/ThemeEngine.cpp:336 +#: gui/ThemeEngine.cpp:338 msgid "Standard (16bpp)" msgstr "Standardowy (16bpp)" -#: gui/ThemeEngine.cpp:338 +#: gui/ThemeEngine.cpp:340 msgid "Antialiased Renderer (16bpp)" msgstr "Wyg³adzany renderer (16bpp)" -#: gui/ThemeEngine.cpp:338 +#: gui/ThemeEngine.cpp:340 msgid "Antialiased (16bpp)" msgstr "Wyg³adzany (16bpp)" @@ -991,35 +1026,35 @@ msgstr "Wyg³adzany (16bpp)" msgid "Clear value" msgstr "Wyczy¶æ" -#: base/main.cpp:209 +#: base/main.cpp:210 #, c-format msgid "Engine does not support debug level '%s'" msgstr "Silnik nie wspiera poziomu debugowania '%s'" -#: base/main.cpp:287 +#: base/main.cpp:288 msgid "Menu" msgstr "Menu" -#: base/main.cpp:290 backends/platform/symbian/src/SymbianActions.cpp:45 +#: base/main.cpp:291 backends/platform/symbian/src/SymbianActions.cpp:45 #: backends/platform/wince/CEActionsPocket.cpp:45 #: backends/platform/wince/CEActionsSmartphone.cpp:46 msgid "Skip" msgstr "Pomiñ" -#: base/main.cpp:293 backends/platform/symbian/src/SymbianActions.cpp:50 +#: base/main.cpp:294 backends/platform/symbian/src/SymbianActions.cpp:50 #: backends/platform/wince/CEActionsPocket.cpp:42 msgid "Pause" msgstr "Wstrzymaj" -#: base/main.cpp:296 +#: base/main.cpp:297 msgid "Skip line" msgstr "Pomiñ liniê" -#: base/main.cpp:467 +#: base/main.cpp:468 msgid "Error running game:" msgstr "B³±d podczas uruchamiania gry:" -#: base/main.cpp:491 +#: base/main.cpp:492 msgid "Could not find any engine capable of running the selected game" msgstr "Nie uda³o siê znale¼æ silnika zdolnego do uruchomienia zaznaczonej gry" @@ -1087,16 +1122,16 @@ msgstr "Przerwane przez u¿ytkownika" msgid "Unknown error" msgstr "Nieznany b³±d" -#: engines/advancedDetector.cpp:324 +#: engines/advancedDetector.cpp:316 #, c-format msgid "The game in '%s' seems to be unknown." msgstr "Gra w '%s' wygl±da na nieznan±." -#: engines/advancedDetector.cpp:325 +#: engines/advancedDetector.cpp:317 msgid "Please, report the following data to the ScummVM team along with name" msgstr "Przeka¿ poni¿sze dane zespo³owi ScummVM razem z nazw±" -#: engines/advancedDetector.cpp:327 +#: engines/advancedDetector.cpp:319 msgid "of the game you tried to add and its version/language/etc.:" msgstr "gry, któr± próbowa³e¶ dodaæ oraz jej wersj±, jêzykiem itd.:" @@ -1134,13 +1169,13 @@ msgid "~R~eturn to Launcher" msgstr "~P~owrót do launchera" #: engines/dialogs.cpp:115 engines/agi/saveload.cpp:803 -#: engines/cruise/menu.cpp:212 engines/sci/engine/kfile.cpp:735 +#: engines/cruise/menu.cpp:212 engines/sci/engine/kfile.cpp:742 msgid "Save game:" msgstr "Zapis:" #: engines/dialogs.cpp:115 engines/agi/saveload.cpp:803 #: engines/scumm/dialogs.cpp:187 engines/cruise/menu.cpp:212 -#: engines/sci/engine/kfile.cpp:735 +#: engines/sci/engine/kfile.cpp:742 #: backends/platform/symbian/src/SymbianActions.cpp:44 #: backends/platform/wince/CEActionsPocket.cpp:43 #: backends/platform/wince/CEActionsPocket.cpp:267 @@ -1160,19 +1195,18 @@ msgstr "" "sprawd¼ plik README." #: engines/dialogs.cpp:228 -#, fuzzy, c-format +#, c-format msgid "" "Gamestate save failed (%s)! Please consult the README for basic information, " "and for instructions on how to obtain further assistance." msgstr "" -"Przepraszamy, ten silnik obecnie nie oferuje pomocy wewn±trz gry. Aby " -"uzyskaæ podstawowe informacje oraz dowiedzieæ jak szukaæ dalszej pomocy, " -"sprawd¼ plik README." +"Zapis stanu gry nie powiód³ siê (%s)! Aby uzyskaæ podstawowe informacje oraz " +"dowiedzieæ jak szukaæ dalszej pomocy, sprawd¼ plik README." #: engines/dialogs.cpp:301 engines/mohawk/dialogs.cpp:109 #: engines/mohawk/dialogs.cpp:174 msgid "~O~K" -msgstr "~O~K" +msgstr "" #: engines/dialogs.cpp:302 engines/mohawk/dialogs.cpp:110 #: engines/mohawk/dialogs.cpp:175 @@ -1224,14 +1258,13 @@ msgstr "" "Dalsze informacje s± dostêpne w pliku README." #: engines/engine.cpp:426 -#, fuzzy, c-format +#, c-format msgid "" "Gamestate load failed (%s)! Please consult the README for basic information, " "and for instructions on how to obtain further assistance." msgstr "" -"Przepraszamy, ten silnik obecnie nie oferuje pomocy wewn±trz gry. Aby " -"uzyskaæ podstawowe informacje oraz dowiedzieæ jak szukaæ dalszej pomocy, " -"sprawd¼ plik README." +"Odczyt stanu gry nie powiód³ siê (%s)! Aby uzyskaæ podstawowe informacje " +"oraz dowiedzieæ jak szukaæ dalszej pomocy, sprawd¼ plik README." #: engines/engine.cpp:439 msgid "" @@ -1247,87 +1280,87 @@ msgstr "" msgid "Start anyway" msgstr "W³±cz mimo tego" -#: engines/agi/detection.cpp:145 engines/dreamweb/detection.cpp:47 -#: engines/sci/detection.cpp:390 +#: engines/agi/detection.cpp:142 engines/dreamweb/detection.cpp:47 +#: engines/sci/detection.cpp:393 msgid "Use original save/load screens" -msgstr "" +msgstr "U¿yj oryginalnych ekranów odczytu/zapisu" -#: engines/agi/detection.cpp:146 engines/dreamweb/detection.cpp:48 -#: engines/sci/detection.cpp:391 +#: engines/agi/detection.cpp:143 engines/dreamweb/detection.cpp:48 +#: engines/sci/detection.cpp:394 msgid "Use the original save/load screens, instead of the ScummVM ones" -msgstr "" +msgstr "U¿yj oryginalnych ekranów odczytu/zapisu zamiast tych ze ScummVM" -#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:831 +#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:838 msgid "Restore game:" msgstr "Wznów grê:" -#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:831 +#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:838 msgid "Restore" msgstr "Wznów" #: engines/dreamweb/detection.cpp:57 -#, fuzzy msgid "Use bright palette mode" -msgstr "Przedmiot u góry, z prawej" +msgstr "U¿yj trybu jasnej palety" #: engines/dreamweb/detection.cpp:58 msgid "Display graphics using the game's bright palette" -msgstr "" +msgstr "Wy¶wietlaj grafikê za pomoc± jasnej palety gry" -#: engines/sci/detection.cpp:370 +#: engines/sci/detection.cpp:373 msgid "EGA undithering" msgstr "Anty-dithering EGA" -#: engines/sci/detection.cpp:371 -#, fuzzy +#: engines/sci/detection.cpp:374 msgid "Enable undithering in EGA games" msgstr "W³±cz anty-dithering we wspieranych grach EGA" -#: engines/sci/detection.cpp:380 -#, fuzzy +#: engines/sci/detection.cpp:383 msgid "Prefer digital sound effects" -msgstr "G³o¶no¶æ efektów d¼w." +msgstr "Preferuj cyfrowe efekty d¼wiêkowe" -#: engines/sci/detection.cpp:381 +#: engines/sci/detection.cpp:384 msgid "Prefer digital sound effects instead of synthesized ones" -msgstr "" +msgstr "Preferuj cyfrowe efekty d¼wiêkowe zamiast syntezowanych" -#: engines/sci/detection.cpp:400 +#: engines/sci/detection.cpp:403 msgid "Use IMF/Yamaha FB-01 for MIDI output" -msgstr "" +msgstr "U¿yj IMF/Yamaha FB-01 dla wyj¶cia MIDI" -#: engines/sci/detection.cpp:401 +#: engines/sci/detection.cpp:404 msgid "" "Use an IBM Music Feature card or a Yamaha FB-01 FM synth module for MIDI " "output" msgstr "" +"U¿yj karty IBM Music Feature lub modu³u syntezy FM Yamaha FB-01 dla wyj¶cia " +"MIDI" -#: engines/sci/detection.cpp:411 +#: engines/sci/detection.cpp:414 msgid "Use CD audio" -msgstr "" +msgstr "U¿yj CD audio" -#: engines/sci/detection.cpp:412 +#: engines/sci/detection.cpp:415 msgid "Use CD audio instead of in-game audio, if available" -msgstr "" +msgstr "U¿yj CD audio zamiast muzyki w grze, je¶li jest dostêpne" -#: engines/sci/detection.cpp:422 +#: engines/sci/detection.cpp:425 msgid "Use Windows cursors" -msgstr "" +msgstr "U¿yj windowsowych kursorów" -#: engines/sci/detection.cpp:423 +#: engines/sci/detection.cpp:426 msgid "" "Use the Windows cursors (smaller and monochrome) instead of the DOS ones" msgstr "" +"U¿yj windowsowych kursorów (mniejsze i monochromatyczne) zamiast DOS-owych" -#: engines/sci/detection.cpp:433 -#, fuzzy +#: engines/sci/detection.cpp:436 msgid "Use silver cursors" -msgstr "Zwyk³y kursor" +msgstr "U¿yj srebrnych kursorów" -#: engines/sci/detection.cpp:434 +#: engines/sci/detection.cpp:437 msgid "" "Use the alternate set of silver cursors, instead of the normal golden ones" msgstr "" +"U¿yj alternatywnego zestawu srebrnych kursorów zamiast zwyk³ych z³otych" #: engines/scumm/dialogs.cpp:175 #, c-format @@ -1445,24 +1478,23 @@ msgstr "Mowa i napisy" #: engines/scumm/dialogs.cpp:653 msgid "Select a Proficiency Level." -msgstr "" +msgstr "Wybierz poziom umiejêtno¶ci." #: engines/scumm/dialogs.cpp:655 msgid "Refer to your Loom(TM) manual for help." -msgstr "" +msgstr "Pomocy szukaj w instrukcji do³±czonej do Loom(TM)." #: engines/scumm/dialogs.cpp:658 -#, fuzzy msgid "Standard" -msgstr "Standardowy (16bpp)" +msgstr "Standardowy" #: engines/scumm/dialogs.cpp:659 msgid "Practice" -msgstr "" +msgstr "Trening" #: engines/scumm/dialogs.cpp:660 msgid "Expert" -msgstr "" +msgstr "Ekspert" #: engines/scumm/help.cpp:73 msgid "Common keyboard commands:" @@ -1977,7 +2009,7 @@ msgstr "Leæ w prawo" msgid "Fly to lower right" msgstr "Leæ w dó³, w prawo" -#: engines/scumm/scumm.cpp:1773 +#: engines/scumm/scumm.cpp:1774 #, c-format msgid "" "Native MIDI support requires the Roland Upgrade from LucasArts,\n" @@ -1986,7 +2018,7 @@ msgstr "" "Natywne wsparcie MIDI wymaga aktualizacji Rolanda od LucasArts,\n" "ale brakuje %s. Prze³±czam na tryb AdLib." -#: engines/scumm/scumm.cpp:2278 engines/agos/saveload.cpp:202 +#: engines/scumm/scumm.cpp:2295 engines/agos/saveload.cpp:220 #, c-format msgid "" "Failed to save game state to file:\n" @@ -1997,7 +2029,7 @@ msgstr "" "\n" "%s" -#: engines/scumm/scumm.cpp:2285 engines/agos/saveload.cpp:167 +#: engines/scumm/scumm.cpp:2302 engines/agos/saveload.cpp:185 #, c-format msgid "" "Failed to load game state from file:\n" @@ -2008,7 +2040,7 @@ msgstr "" "\n" "%s" -#: engines/scumm/scumm.cpp:2297 engines/agos/saveload.cpp:210 +#: engines/scumm/scumm.cpp:2314 engines/agos/saveload.cpp:228 #, c-format msgid "" "Successfully saved game state in file:\n" @@ -2019,7 +2051,7 @@ msgstr "" "\n" "%s" -#: engines/scumm/scumm.cpp:2512 +#: engines/scumm/scumm.cpp:2529 msgid "" "Usually, Maniac Mansion would start now. But ScummVM doesn't do that yet. To " "play it, go to 'Add Game' in the ScummVM start menu and select the 'Maniac' " @@ -2055,17 +2087,17 @@ msgstr "~M~enu g³ówne" msgid "~W~ater Effect Enabled" msgstr "~E~fekty wody w³±czone" -#: engines/agos/animation.cpp:560 +#: engines/agos/animation.cpp:557 #, c-format msgid "Cutscene file '%s' not found!" msgstr "Nie znaleziono pliku przerywnika '%s'!" #: engines/gob/inter_playtoons.cpp:256 engines/gob/inter_v2.cpp:1287 -#: engines/tinsel/saveload.cpp:502 +#: engines/tinsel/saveload.cpp:532 msgid "Failed to load game state from file." msgstr "Nie uda³o siê wczytaæ stanu gry z pliku." -#: engines/gob/inter_v2.cpp:1357 engines/tinsel/saveload.cpp:515 +#: engines/gob/inter_v2.cpp:1357 engines/tinsel/saveload.cpp:545 msgid "Failed to save game state to file." msgstr "Nie uda³o siê zapisaæ stanu gry do pliku." @@ -2081,118 +2113,109 @@ msgstr "Nie uda³o siê zapisaæ stanu gry" #. Malcolm makes a joke. #: engines/kyra/detection.cpp:62 msgid "Studio audience" -msgstr "" +msgstr "Publiczno¶æ studyjna" #: engines/kyra/detection.cpp:63 msgid "Enable studio audience" -msgstr "" +msgstr "W³±cz publiczno¶æ studyjn±" #. I18N: This option allows the user to skip text and cutscenes. #: engines/kyra/detection.cpp:73 msgid "Skip support" -msgstr "" +msgstr "Obs³uga pomijania" #: engines/kyra/detection.cpp:74 msgid "Allow text and cutscenes to be skipped" -msgstr "" +msgstr "Pozwól pomijaæ tekst i przerywniki" #. I18N: Helium mode makes people sound like they've inhaled Helium. #: engines/kyra/detection.cpp:84 msgid "Helium mode" -msgstr "" +msgstr "Tryb helowy" #: engines/kyra/detection.cpp:85 -#, fuzzy msgid "Enable helium mode" -msgstr "W³±cz tryb Roland GS" +msgstr "W³±cz tryb helowy" #. I18N: When enabled, this option makes scrolling smoother when #. changing from one screen to another. #: engines/kyra/detection.cpp:99 msgid "Smooth scrolling" -msgstr "" +msgstr "P³ynne przewijanie" #: engines/kyra/detection.cpp:100 msgid "Enable smooth scrolling when walking" -msgstr "" +msgstr "W³±cz p³ynne przewijanie przy chodzeniu" #. I18N: When enabled, this option changes the cursor when it floats to the #. edge of the screen to a directional arrow. The player can then click to #. walk towards that direction. #: engines/kyra/detection.cpp:112 -#, fuzzy msgid "Floating cursors" -msgstr "Zwyk³y kursor" +msgstr "P³ywaj±ce kursory" #: engines/kyra/detection.cpp:113 msgid "Enable floating cursors" -msgstr "" +msgstr "W³±cz p³ywaj±ce kursory" #. I18N: HP stands for Hit Points #: engines/kyra/detection.cpp:127 msgid "HP bar graphs" -msgstr "" +msgstr "Histogramy HP" #: engines/kyra/detection.cpp:128 msgid "Enable hit point bar graphs" -msgstr "" +msgstr "W³±cz histogramy punktów ¿ycia" #: engines/kyra/lol.cpp:478 msgid "Attack 1" -msgstr "" +msgstr "Atak 1" #: engines/kyra/lol.cpp:479 msgid "Attack 2" -msgstr "" +msgstr "Atak 2" #: engines/kyra/lol.cpp:480 msgid "Attack 3" -msgstr "" +msgstr "Atak 3" #: engines/kyra/lol.cpp:481 msgid "Move Forward" -msgstr "" +msgstr "Ruch naprzód" #: engines/kyra/lol.cpp:482 msgid "Move Back" -msgstr "" +msgstr "Ruch wstecz" #: engines/kyra/lol.cpp:483 msgid "Slide Left" -msgstr "" +msgstr "¦lizg w lewo" #: engines/kyra/lol.cpp:484 -#, fuzzy msgid "Slide Right" -msgstr "W prawo" +msgstr "¦lizg w prawo" #: engines/kyra/lol.cpp:485 -#, fuzzy msgid "Turn Left" -msgstr "Wy³±cz" +msgstr "Obrót w lewo" #: engines/kyra/lol.cpp:486 -#, fuzzy msgid "Turn Right" -msgstr "Kursor w prawo" +msgstr "Obrót w prawo" #: engines/kyra/lol.cpp:487 -#, fuzzy msgid "Rest" -msgstr "Wznów" +msgstr "Odpoczynek" #: engines/kyra/lol.cpp:488 -#, fuzzy msgid "Options" -msgstr "~O~pcje" +msgstr "Opcje" #: engines/kyra/lol.cpp:489 -#, fuzzy msgid "Choose Spell" -msgstr "Wybierz" +msgstr "Wybierz zaklêcie" -#: engines/kyra/sound_midi.cpp:475 -#, fuzzy +#: engines/kyra/sound_midi.cpp:477 msgid "" "You appear to be using a General MIDI device,\n" "but your game only supports Roland MT32 MIDI.\n" @@ -2205,13 +2228,14 @@ msgstr "" "Próbujemy przypisaæ instrumenty Rolanda MT32 do instrumentów General MIDI. " "Niektóre utwory mog± byæ ¼le odtwarzane." -#: engines/queen/queen.cpp:59 engines/sky/detection.cpp:44 -msgid "Floppy intro" +#: engines/queen/queen.cpp:59 +msgid "Alternative intro" msgstr "" -#: engines/queen/queen.cpp:60 engines/sky/detection.cpp:45 -msgid "Use the floppy version's intro (CD version only)" -msgstr "" +#: engines/queen/queen.cpp:60 +#, fuzzy +msgid "Use an alternative game intro (CD version only)" +msgstr "U¿yj intra z wersji dyskietkowej (tylko dla wersji CD)" #: engines/sky/compact.cpp:130 msgid "" @@ -2229,27 +2253,37 @@ msgstr "" "Plik \"sky.cpt\" ma nieprawid³owy rozmiar.\n" "Pobierz go (ponownie) ze strony www.scummvm.org" -#: engines/sword1/animation.cpp:539 +#: engines/sky/detection.cpp:44 +msgid "Floppy intro" +msgstr "Intro z wersji dyskietkowej" + +#: engines/sky/detection.cpp:45 +msgid "Use the floppy version's intro (CD version only)" +msgstr "U¿yj intra z wersji dyskietkowej (tylko dla wersji CD)" + +#: engines/sword1/animation.cpp:519 #, c-format msgid "PSX stream cutscene '%s' cannot be played in paletted mode" msgstr "" +"Przerywnik w formacie strumieniowym PSX '%s' nie mo¿e zostaæ odtworzony w " +"trybie indeksowanym" -#: engines/sword1/animation.cpp:560 engines/sword2/animation.cpp:455 +#: engines/sword1/animation.cpp:540 engines/sword2/animation.cpp:439 msgid "DXA cutscenes found but ScummVM has been built without zlib support" msgstr "" "Znaleziono przerywniki w formacie DXA, ale ScummVM jest skompilowany bez " "obs³ugi zlib" -#: engines/sword1/animation.cpp:570 engines/sword2/animation.cpp:465 +#: engines/sword1/animation.cpp:550 engines/sword2/animation.cpp:449 msgid "MPEG2 cutscenes are no longer supported" msgstr "Przerywniki w formacie MPEG2 nie s± ju¿ obs³ugiwane" -#: engines/sword1/animation.cpp:576 engines/sword2/animation.cpp:473 +#: engines/sword1/animation.cpp:556 engines/sword2/animation.cpp:457 #, c-format msgid "Cutscene '%s' not found" msgstr "Nie znaleziono przerywnika '%s'" -#: engines/sword1/control.cpp:863 +#: engines/sword1/control.cpp:865 msgid "" "ScummVM found that you have old savefiles for Broken Sword 1 that should be " "converted.\n" @@ -2267,7 +2301,7 @@ msgstr "" "Naci¶nij OK, ¿eby je teraz przekonwertowaæ. W przeciwnym wypadku zostaniesz " "zapytany ponownie przy nastêpnym w³±czeniu gry.\n" -#: engines/sword1/control.cpp:1232 +#: engines/sword1/control.cpp:1234 #, c-format msgid "" "Target new save game already exists!\n" @@ -2276,11 +2310,11 @@ msgstr "" "Docelowy plik nowego zapisu ju¿ istnieje!\n" "Chcesz zachowaæ stary zapis (%s) czy nowy (%s)?\n" -#: engines/sword1/control.cpp:1235 +#: engines/sword1/control.cpp:1237 msgid "Keep the old one" msgstr "Zachowaj stary" -#: engines/sword1/control.cpp:1235 +#: engines/sword1/control.cpp:1237 msgid "Keep the new one" msgstr "Zachowaj nowy" @@ -2288,20 +2322,30 @@ msgstr "Zachowaj nowy" msgid "This is the end of the Broken Sword 1 Demo" msgstr "To koniec dema Broken Sword 1" -#: engines/sword2/animation.cpp:435 -#, fuzzy +#: engines/sword2/animation.cpp:419 msgid "" "PSX cutscenes found but ScummVM has been built without RGB color support" msgstr "" -"Znaleziono przerywniki w formacie DXA, ale ScummVM jest skompilowany bez " -"obs³ugi zlib" +"Znaleziono przerywniki PSX, ale ScummVM jest skompilowany bez obs³ugi trybu " +"RGB" #: engines/sword2/sword2.cpp:79 msgid "Show object labels" -msgstr "" +msgstr "Poka¿ etykiety obiektów" #: engines/sword2/sword2.cpp:80 msgid "Show labels for objects on mouse hover" +msgstr "Poka¿ etykiety obiektów przy najechaniu myszk±" + +#: engines/teenagent/resources.cpp:94 +msgid "" +"You're missing the 'teenagent.dat' file. Get it from the ScummVM website" +msgstr "" + +#: engines/teenagent/resources.cpp:115 +msgid "" +"The teenagent.dat file is compressed and zlib hasn't been included in this " +"executable. Please decompress it" msgstr "" #: engines/parallaction/saveload.cpp:133 @@ -2410,7 +2454,7 @@ msgstr "Brak muzyki" msgid "Amiga Audio Emulator" msgstr "Emulator d¼wiêku Amigi" -#: audio/softsynth/adlib.cpp:1593 +#: audio/softsynth/adlib.cpp:2284 msgid "AdLib Emulator" msgstr "Emulator AdLib" @@ -2443,9 +2487,8 @@ msgid "Keymap:" msgstr "Klawisze:" #: backends/keymapper/remap-dialog.cpp:66 -#, fuzzy msgid " (Effective)" -msgstr " (Aktywny)" +msgstr " (Dzia³a)" #: backends/keymapper/remap-dialog.cpp:106 msgid " (Active)" @@ -2453,7 +2496,7 @@ msgstr " (Aktywny)" #: backends/keymapper/remap-dialog.cpp:106 msgid " (Blocked)" -msgstr "" +msgstr " (Zablokowany)" #: backends/keymapper/remap-dialog.cpp:119 msgid " (Global)" @@ -2555,11 +2598,11 @@ msgstr "Tryb touchpada w³±czony." msgid "Touchpad mode disabled." msgstr "Tryb touchpada wy³±czony." -#: backends/platform/maemo/maemo.cpp:205 +#: backends/platform/maemo/maemo.cpp:209 msgid "Click Mode" -msgstr "" +msgstr "Tryb klikania" -#: backends/platform/maemo/maemo.cpp:211 +#: backends/platform/maemo/maemo.cpp:215 #: backends/platform/symbian/src/SymbianActions.cpp:42 #: backends/platform/wince/CEActionsPocket.cpp:60 #: backends/platform/wince/CEActionsSmartphone.cpp:43 @@ -2567,36 +2610,35 @@ msgstr "" msgid "Left Click" msgstr "Klikniêcie LPM" -#: backends/platform/maemo/maemo.cpp:214 -#, fuzzy +#: backends/platform/maemo/maemo.cpp:218 msgid "Middle Click" -msgstr "Przedmiot na ¶rodku, z lewej" +msgstr "¦rodkowy przycisk" -#: backends/platform/maemo/maemo.cpp:217 +#: backends/platform/maemo/maemo.cpp:221 #: backends/platform/symbian/src/SymbianActions.cpp:43 #: backends/platform/wince/CEActionsSmartphone.cpp:44 #: backends/platform/bada/form.cpp:273 msgid "Right Click" msgstr "Klikniêcie PPM" -#: backends/platform/sdl/macosx/appmenu_osx.mm:78 +#: backends/platform/sdl/macosx/appmenu_osx.mm:77 msgid "Hide ScummVM" msgstr "Ukryj ScummVM" -#: backends/platform/sdl/macosx/appmenu_osx.mm:83 +#: backends/platform/sdl/macosx/appmenu_osx.mm:82 msgid "Hide Others" msgstr "Ukryj pozosta³e" -#: backends/platform/sdl/macosx/appmenu_osx.mm:88 +#: backends/platform/sdl/macosx/appmenu_osx.mm:87 msgid "Show All" msgstr "Poka¿ wszystkie" -#: backends/platform/sdl/macosx/appmenu_osx.mm:110 -#: backends/platform/sdl/macosx/appmenu_osx.mm:121 +#: backends/platform/sdl/macosx/appmenu_osx.mm:109 +#: backends/platform/sdl/macosx/appmenu_osx.mm:120 msgid "Window" msgstr "Okno" -#: backends/platform/sdl/macosx/appmenu_osx.mm:115 +#: backends/platform/sdl/macosx/appmenu_osx.mm:114 msgid "Minimize" msgstr "Miniaturka" @@ -2974,41 +3016,46 @@ msgstr "" msgid "Do you really want to quit?" msgstr "Na pewno chcesz wyj¶æ?" -#: backends/events/gph/gph-events.cpp:338 -#: backends/events/gph/gph-events.cpp:381 -#: backends/events/openpandora/op-events.cpp:139 +#: backends/events/gph/gph-events.cpp:386 +#: backends/events/gph/gph-events.cpp:429 +#: backends/events/openpandora/op-events.cpp:168 msgid "Touchscreen 'Tap Mode' - Left Click" msgstr "Dotkniêcie ekranu - klikniêcie LPM" -#: backends/events/gph/gph-events.cpp:340 -#: backends/events/gph/gph-events.cpp:383 -#: backends/events/openpandora/op-events.cpp:141 +#: backends/events/gph/gph-events.cpp:388 +#: backends/events/gph/gph-events.cpp:431 +#: backends/events/openpandora/op-events.cpp:170 msgid "Touchscreen 'Tap Mode' - Right Click" msgstr "Dotkniêcie ekranu - klikniêcie PPM" -#: backends/events/gph/gph-events.cpp:342 -#: backends/events/gph/gph-events.cpp:385 -#: backends/events/openpandora/op-events.cpp:143 +#: backends/events/gph/gph-events.cpp:390 +#: backends/events/gph/gph-events.cpp:433 +#: backends/events/openpandora/op-events.cpp:172 msgid "Touchscreen 'Tap Mode' - Hover (No Click)" msgstr "Dotkniêcie ekranu - brak klikniêcia" -#: backends/events/gph/gph-events.cpp:362 +#: backends/events/gph/gph-events.cpp:410 msgid "Maximum Volume" msgstr "Maksymalna g³o¶no¶æ" -#: backends/events/gph/gph-events.cpp:364 +#: backends/events/gph/gph-events.cpp:412 msgid "Increasing Volume" msgstr "Zwiêkszenie g³o¶no¶ci" -#: backends/events/gph/gph-events.cpp:370 +#: backends/events/gph/gph-events.cpp:418 msgid "Minimal Volume" msgstr "Minimalna g³o¶no¶æ" -#: backends/events/gph/gph-events.cpp:372 +#: backends/events/gph/gph-events.cpp:420 msgid "Decreasing Volume" msgstr "Zmniejszenie g³o¶no¶ci" -#: backends/updates/macosx/macosx-updates.mm:65 +#: backends/events/openpandora/op-events.cpp:174 +#, fuzzy +msgid "Touchscreen 'Tap Mode' - Hover (DPad Clicks)" +msgstr "Dotkniêcie ekranu - brak klikniêcia" + +#: backends/updates/macosx/macosx-updates.mm:67 msgid "Check for Updates..." msgstr "Sprawd¼ aktualizacjê..." diff --git a/po/pt_BR.po b/po/pt_BR.po index ac60a814e2..0225854de6 100644 --- a/po/pt_BR.po +++ b/po/pt_BR.po @@ -1,5 +1,5 @@ # Portuguese (Brazilian) translation for ScummVM. -# Copyright (C) 2010-2012 ScummVM Team +# Copyright (C) 2010-2013 ScummVM Team # This file is distributed under the same license as the ScummVM package. # Saulo Benigno <saulobenigno@gmail.com>, 2010. # @@ -7,14 +7,14 @@ msgid "" msgstr "" "Project-Id-Version: ScummVM 1.3.0svn\n" "Report-Msgid-Bugs-To: scummvm-devel@lists.sf.net\n" -"POT-Creation-Date: 2012-07-08 12:25+0100\n" +"POT-Creation-Date: 2012-12-01 17:22+0000\n" "PO-Revision-Date: 2011-10-21 21:30-0300\n" "Last-Translator: Saulo Benigno <saulobenigno@gmail.com>\n" "Language-Team: ScummBR (www.scummbr.com) <scummbr@yahoo.com.br>\n" +"Language: Portugues (Brasil)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=iso-8859-1\n" "Content-Transfer-Encoding: 8bit\n" -"Language: Portugues (Brasil)\n" "Plural-Forms: nplurals=2; plural=(n > 1)\n" "X-Poedit-Language: Portuguese\n" "X-Poedit-Country: BRAZIL\n" @@ -48,10 +48,11 @@ msgstr "Acima" #: gui/browser.cpp:69 gui/chooser.cpp:45 gui/KeysDialog.cpp:43 #: gui/launcher.cpp:345 gui/massadd.cpp:94 gui/options.cpp:1228 -#: gui/saveload.cpp:64 gui/saveload.cpp:173 gui/themebrowser.cpp:54 -#: engines/engine.cpp:442 engines/scumm/dialogs.cpp:190 -#: engines/sword1/control.cpp:865 engines/parallaction/saveload.cpp:274 -#: backends/platform/wii/options.cpp:48 +#: gui/saveload-dialog.cpp:215 gui/saveload-dialog.cpp:275 +#: gui/saveload-dialog.cpp:545 gui/saveload-dialog.cpp:919 +#: gui/themebrowser.cpp:54 engines/engine.cpp:442 +#: engines/scumm/dialogs.cpp:190 engines/sword1/control.cpp:867 +#: engines/parallaction/saveload.cpp:274 backends/platform/wii/options.cpp:48 #: backends/events/default/default-events.cpp:191 #: backends/events/default/default-events.cpp:213 msgid "Cancel" @@ -72,15 +73,15 @@ msgstr "Fechar" msgid "Mouse click" msgstr "Clique do mouse" -#: gui/gui-manager.cpp:122 base/main.cpp:300 +#: gui/gui-manager.cpp:122 base/main.cpp:301 msgid "Display keyboard" msgstr "Mostrar teclado" -#: gui/gui-manager.cpp:126 base/main.cpp:304 +#: gui/gui-manager.cpp:126 base/main.cpp:305 msgid "Remap keys" msgstr "Remapear teclas" -#: gui/gui-manager.cpp:129 base/main.cpp:307 +#: gui/gui-manager.cpp:129 base/main.cpp:308 #, fuzzy msgid "Toggle FullScreen" msgstr "Habilita Tela Cheia" @@ -95,16 +96,16 @@ msgstr "Mapear" #: gui/KeysDialog.cpp:42 gui/launcher.cpp:346 gui/launcher.cpp:1001 #: gui/launcher.cpp:1005 gui/massadd.cpp:91 gui/options.cpp:1229 -#: engines/engine.cpp:361 engines/engine.cpp:372 engines/scumm/dialogs.cpp:192 -#: engines/scumm/scumm.cpp:1775 engines/agos/animation.cpp:561 -#: engines/groovie/script.cpp:420 engines/sky/compact.cpp:131 -#: engines/sky/compact.cpp:141 engines/sword1/animation.cpp:539 -#: engines/sword1/animation.cpp:560 engines/sword1/animation.cpp:570 -#: engines/sword1/animation.cpp:577 engines/sword1/control.cpp:865 -#: engines/sword1/logic.cpp:1633 engines/sword2/animation.cpp:435 -#: engines/sword2/animation.cpp:455 engines/sword2/animation.cpp:465 -#: engines/sword2/animation.cpp:474 engines/parallaction/saveload.cpp:274 -#: backends/platform/wii/options.cpp:47 +#: gui/saveload-dialog.cpp:920 engines/engine.cpp:361 engines/engine.cpp:372 +#: engines/scumm/dialogs.cpp:192 engines/scumm/scumm.cpp:1776 +#: engines/agos/animation.cpp:558 engines/groovie/script.cpp:420 +#: engines/sky/compact.cpp:131 engines/sky/compact.cpp:141 +#: engines/sword1/animation.cpp:519 engines/sword1/animation.cpp:540 +#: engines/sword1/animation.cpp:550 engines/sword1/animation.cpp:557 +#: engines/sword1/control.cpp:867 engines/sword1/logic.cpp:1633 +#: engines/sword2/animation.cpp:419 engines/sword2/animation.cpp:439 +#: engines/sword2/animation.cpp:449 engines/sword2/animation.cpp:458 +#: engines/parallaction/saveload.cpp:274 backends/platform/wii/options.cpp:47 #: backends/platform/wince/CELauncherDialog.cpp:54 msgid "OK" msgstr "OK" @@ -358,7 +359,7 @@ msgstr "Este código já esta sendo utilizado. Por favor, escolha outro." msgid "~Q~uit" msgstr "~S~air" -#: gui/launcher.cpp:621 backends/platform/sdl/macosx/appmenu_osx.mm:96 +#: gui/launcher.cpp:621 backends/platform/sdl/macosx/appmenu_osx.mm:95 msgid "Quit ScummVM" msgstr "Sair do ScummVM" @@ -366,7 +367,7 @@ msgstr "Sair do ScummVM" msgid "A~b~out..." msgstr "So~b~re..." -#: gui/launcher.cpp:622 backends/platform/sdl/macosx/appmenu_osx.mm:70 +#: gui/launcher.cpp:622 backends/platform/sdl/macosx/appmenu_osx.mm:69 msgid "About ScummVM" msgstr "Sobre o ScumnmVM" @@ -442,13 +443,13 @@ msgstr "Pesquisar na lista de jogos" msgid "Search:" msgstr "Pesquisar:" -#: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/mohawk/myst.cpp:255 +#: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/mohawk/myst.cpp:245 #: engines/mohawk/riven.cpp:716 engines/cruise/menu.cpp:214 msgid "Load game:" msgstr "Carregar jogo:" #: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/scumm/dialogs.cpp:188 -#: engines/mohawk/myst.cpp:255 engines/mohawk/riven.cpp:716 +#: engines/mohawk/myst.cpp:245 engines/mohawk/riven.cpp:716 #: engines/cruise/menu.cpp:214 backends/platform/wince/CEActionsPocket.cpp:267 #: backends/platform/wince/CEActionsSmartphone.cpp:231 msgid "Load" @@ -931,68 +932,104 @@ msgstr "" "O tema que você selecionou não suporta seu idioma atual. Se você quiser usar " "este tema você precisa mudar para outro idioma." -#: gui/saveload.cpp:59 gui/saveload.cpp:257 +#: gui/saveload-dialog.cpp:166 +msgid "List view" +msgstr "" + +#: gui/saveload-dialog.cpp:167 +msgid "Grid view" +msgstr "" + +#: gui/saveload-dialog.cpp:210 gui/saveload-dialog.cpp:358 msgid "No date saved" msgstr "Sem data salva" -#: gui/saveload.cpp:60 gui/saveload.cpp:258 +#: gui/saveload-dialog.cpp:211 gui/saveload-dialog.cpp:359 msgid "No time saved" msgstr "Sem hora salva" -#: gui/saveload.cpp:61 gui/saveload.cpp:259 +#: gui/saveload-dialog.cpp:212 gui/saveload-dialog.cpp:360 msgid "No playtime saved" msgstr "Sem tempo de jogo salvo" -#: gui/saveload.cpp:68 gui/saveload.cpp:173 +#: gui/saveload-dialog.cpp:219 gui/saveload-dialog.cpp:275 msgid "Delete" msgstr "Excluir" -#: gui/saveload.cpp:172 +#: gui/saveload-dialog.cpp:274 msgid "Do you really want to delete this savegame?" msgstr "Você realmente quer excluir este jogo salvo?" -#: gui/saveload.cpp:282 +#: gui/saveload-dialog.cpp:383 gui/saveload-dialog.cpp:872 msgid "Date: " msgstr "Data:" -#: gui/saveload.cpp:286 +#: gui/saveload-dialog.cpp:387 gui/saveload-dialog.cpp:878 msgid "Time: " msgstr "Hora:" -#: gui/saveload.cpp:292 +#: gui/saveload-dialog.cpp:393 gui/saveload-dialog.cpp:886 msgid "Playtime: " msgstr "Tempo de jogo:" -#: gui/saveload.cpp:305 gui/saveload.cpp:372 +#: gui/saveload-dialog.cpp:406 gui/saveload-dialog.cpp:494 msgid "Untitled savestate" msgstr "Não-titulado arquivo de save" +#: gui/saveload-dialog.cpp:546 +msgid "Next" +msgstr "" + +#: gui/saveload-dialog.cpp:549 +msgid "Prev" +msgstr "" + +#: gui/saveload-dialog.cpp:736 +#, fuzzy +msgid "New Save" +msgstr "Salvar" + +#: gui/saveload-dialog.cpp:736 +#, fuzzy +msgid "Create a new save game" +msgstr "Falha ao salvar o jogo" + +#: gui/saveload-dialog.cpp:865 +#, fuzzy +msgid "Name: " +msgstr "Nome:" + +#: gui/saveload-dialog.cpp:937 +#, c-format +msgid "Enter a description for slot %d:" +msgstr "" + #: gui/themebrowser.cpp:44 msgid "Select a Theme" msgstr "Selecione um Tema" -#: gui/ThemeEngine.cpp:335 +#: gui/ThemeEngine.cpp:337 msgid "Disabled GFX" msgstr "GFX desabilitado" -#: gui/ThemeEngine.cpp:335 +#: gui/ThemeEngine.cpp:337 msgctxt "lowres" msgid "Disabled GFX" msgstr "GFX desabilitado" -#: gui/ThemeEngine.cpp:336 +#: gui/ThemeEngine.cpp:338 msgid "Standard Renderer (16bpp)" msgstr "Renderizador padrão (16bpp)" -#: gui/ThemeEngine.cpp:336 +#: gui/ThemeEngine.cpp:338 msgid "Standard (16bpp)" msgstr "Padrão (16bpp)" -#: gui/ThemeEngine.cpp:338 +#: gui/ThemeEngine.cpp:340 msgid "Antialiased Renderer (16bpp)" msgstr "Renderizador Anti-Serrilhamento (16bpp)" -#: gui/ThemeEngine.cpp:338 +#: gui/ThemeEngine.cpp:340 msgid "Antialiased (16bpp)" msgstr "Anti-Serrilhamento (16bpp)" @@ -1000,35 +1037,35 @@ msgstr "Anti-Serrilhamento (16bpp)" msgid "Clear value" msgstr "Limpar valor" -#: base/main.cpp:209 +#: base/main.cpp:210 #, c-format msgid "Engine does not support debug level '%s'" msgstr "Esse programa não suporta o nível de debug '%s'" -#: base/main.cpp:287 +#: base/main.cpp:288 msgid "Menu" msgstr "Menu" -#: base/main.cpp:290 backends/platform/symbian/src/SymbianActions.cpp:45 +#: base/main.cpp:291 backends/platform/symbian/src/SymbianActions.cpp:45 #: backends/platform/wince/CEActionsPocket.cpp:45 #: backends/platform/wince/CEActionsSmartphone.cpp:46 msgid "Skip" msgstr "Pular" -#: base/main.cpp:293 backends/platform/symbian/src/SymbianActions.cpp:50 +#: base/main.cpp:294 backends/platform/symbian/src/SymbianActions.cpp:50 #: backends/platform/wince/CEActionsPocket.cpp:42 msgid "Pause" msgstr "Pausar" -#: base/main.cpp:296 +#: base/main.cpp:297 msgid "Skip line" msgstr "Pula linha" -#: base/main.cpp:467 +#: base/main.cpp:468 msgid "Error running game:" msgstr "Erro ao executar o jogo:" -#: base/main.cpp:491 +#: base/main.cpp:492 msgid "Could not find any engine capable of running the selected game" msgstr "" "Não foi possível encontrar qualquer programa capaz de rodar o jogo " @@ -1098,17 +1135,17 @@ msgstr "Usuário cancelou" msgid "Unknown error" msgstr "Erro desconhecido" -#: engines/advancedDetector.cpp:324 +#: engines/advancedDetector.cpp:316 #, c-format msgid "The game in '%s' seems to be unknown." msgstr "O jogo em '% s' parece ser desconhecido." -#: engines/advancedDetector.cpp:325 +#: engines/advancedDetector.cpp:317 msgid "Please, report the following data to the ScummVM team along with name" msgstr "" "Por favor, informe os seguintes dados para a equipe ScummVM junto com o nome" -#: engines/advancedDetector.cpp:327 +#: engines/advancedDetector.cpp:319 msgid "of the game you tried to add and its version/language/etc.:" msgstr "do jogo que você tentou adicionar e sua versão/idioma/etc.:" @@ -1146,13 +1183,13 @@ msgid "~R~eturn to Launcher" msgstr "~V~oltar ao menu" #: engines/dialogs.cpp:115 engines/agi/saveload.cpp:803 -#: engines/cruise/menu.cpp:212 engines/sci/engine/kfile.cpp:735 +#: engines/cruise/menu.cpp:212 engines/sci/engine/kfile.cpp:742 msgid "Save game:" msgstr "Salvar jogo:" #: engines/dialogs.cpp:115 engines/agi/saveload.cpp:803 #: engines/scumm/dialogs.cpp:187 engines/cruise/menu.cpp:212 -#: engines/sci/engine/kfile.cpp:735 +#: engines/sci/engine/kfile.cpp:742 #: backends/platform/symbian/src/SymbianActions.cpp:44 #: backends/platform/wince/CEActionsPocket.cpp:43 #: backends/platform/wince/CEActionsPocket.cpp:267 @@ -1263,21 +1300,21 @@ msgstr "" msgid "Start anyway" msgstr "Iniciar de qualquer maneira" -#: engines/agi/detection.cpp:145 engines/dreamweb/detection.cpp:47 -#: engines/sci/detection.cpp:390 +#: engines/agi/detection.cpp:142 engines/dreamweb/detection.cpp:47 +#: engines/sci/detection.cpp:393 msgid "Use original save/load screens" msgstr "" -#: engines/agi/detection.cpp:146 engines/dreamweb/detection.cpp:48 -#: engines/sci/detection.cpp:391 +#: engines/agi/detection.cpp:143 engines/dreamweb/detection.cpp:48 +#: engines/sci/detection.cpp:394 msgid "Use the original save/load screens, instead of the ScummVM ones" msgstr "" -#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:831 +#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:838 msgid "Restore game:" msgstr "Restaurar jogo:" -#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:831 +#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:838 msgid "Restore" msgstr "Restaurar" @@ -1290,57 +1327,57 @@ msgstr "Item da direita superior" msgid "Display graphics using the game's bright palette" msgstr "" -#: engines/sci/detection.cpp:370 +#: engines/sci/detection.cpp:373 msgid "EGA undithering" msgstr "EGA sem dithering" -#: engines/sci/detection.cpp:371 +#: engines/sci/detection.cpp:374 #, fuzzy msgid "Enable undithering in EGA games" msgstr "Habilita EGA sem dithering em jogos com suporte" -#: engines/sci/detection.cpp:380 +#: engines/sci/detection.cpp:383 #, fuzzy msgid "Prefer digital sound effects" msgstr "Volume dos efeitos sonoros especiais" -#: engines/sci/detection.cpp:381 +#: engines/sci/detection.cpp:384 msgid "Prefer digital sound effects instead of synthesized ones" msgstr "" -#: engines/sci/detection.cpp:400 +#: engines/sci/detection.cpp:403 msgid "Use IMF/Yamaha FB-01 for MIDI output" msgstr "" -#: engines/sci/detection.cpp:401 +#: engines/sci/detection.cpp:404 msgid "" "Use an IBM Music Feature card or a Yamaha FB-01 FM synth module for MIDI " "output" msgstr "" -#: engines/sci/detection.cpp:411 +#: engines/sci/detection.cpp:414 msgid "Use CD audio" msgstr "" -#: engines/sci/detection.cpp:412 +#: engines/sci/detection.cpp:415 msgid "Use CD audio instead of in-game audio, if available" msgstr "" -#: engines/sci/detection.cpp:422 +#: engines/sci/detection.cpp:425 msgid "Use Windows cursors" msgstr "" -#: engines/sci/detection.cpp:423 +#: engines/sci/detection.cpp:426 msgid "" "Use the Windows cursors (smaller and monochrome) instead of the DOS ones" msgstr "" -#: engines/sci/detection.cpp:433 +#: engines/sci/detection.cpp:436 #, fuzzy msgid "Use silver cursors" msgstr "Cursor normal" -#: engines/sci/detection.cpp:434 +#: engines/sci/detection.cpp:437 msgid "" "Use the alternate set of silver cursors, instead of the normal golden ones" msgstr "" @@ -1993,7 +2030,7 @@ msgstr "Voar para direita" msgid "Fly to lower right" msgstr "Voar para direita inferior" -#: engines/scumm/scumm.cpp:1773 +#: engines/scumm/scumm.cpp:1774 #, c-format msgid "" "Native MIDI support requires the Roland Upgrade from LucasArts,\n" @@ -2003,7 +2040,7 @@ msgstr "" "LucasArts,\n" "mas %s está faltando. Utilizando AdLib ao invés." -#: engines/scumm/scumm.cpp:2278 engines/agos/saveload.cpp:202 +#: engines/scumm/scumm.cpp:2295 engines/agos/saveload.cpp:220 #, c-format msgid "" "Failed to save game state to file:\n" @@ -2014,7 +2051,7 @@ msgstr "" "\n" "%s" -#: engines/scumm/scumm.cpp:2285 engines/agos/saveload.cpp:167 +#: engines/scumm/scumm.cpp:2302 engines/agos/saveload.cpp:185 #, c-format msgid "" "Failed to load game state from file:\n" @@ -2025,7 +2062,7 @@ msgstr "" "\n" "%s" -#: engines/scumm/scumm.cpp:2297 engines/agos/saveload.cpp:210 +#: engines/scumm/scumm.cpp:2314 engines/agos/saveload.cpp:228 #, c-format msgid "" "Successfully saved game state in file:\n" @@ -2036,7 +2073,7 @@ msgstr "" "\n" "%s" -#: engines/scumm/scumm.cpp:2512 +#: engines/scumm/scumm.cpp:2529 msgid "" "Usually, Maniac Mansion would start now. But ScummVM doesn't do that yet. To " "play it, go to 'Add Game' in the ScummVM start menu and select the 'Maniac' " @@ -2073,20 +2110,20 @@ msgstr "~M~enu Principal ScummVM" msgid "~W~ater Effect Enabled" msgstr "Modo ~E~feitos de água ativado" -#: engines/agos/animation.cpp:560 +#: engines/agos/animation.cpp:557 #, c-format msgid "Cutscene file '%s' not found!" msgstr "Arquivo de vídeo '%s' não encontrado!" #: engines/gob/inter_playtoons.cpp:256 engines/gob/inter_v2.cpp:1287 -#: engines/tinsel/saveload.cpp:502 +#: engines/tinsel/saveload.cpp:532 msgid "Failed to load game state from file." msgstr "" "Falha ao carregar o estado do jogo a partir do arquivo:\n" "\n" "%s" -#: engines/gob/inter_v2.cpp:1357 engines/tinsel/saveload.cpp:515 +#: engines/gob/inter_v2.cpp:1357 engines/tinsel/saveload.cpp:545 msgid "Failed to save game state to file." msgstr "" "Falha ao salvar o estado do jogo para o arquivo:\n" @@ -2215,7 +2252,7 @@ msgstr "~O~pções" msgid "Choose Spell" msgstr "Escolher" -#: engines/kyra/sound_midi.cpp:475 +#: engines/kyra/sound_midi.cpp:477 #, fuzzy msgid "" "You appear to be using a General MIDI device,\n" @@ -2230,12 +2267,12 @@ msgstr "" "o modelo General MIDI. Talvez possa acontecer\n" "que algumas faixas não sejam corretamente tocadas." -#: engines/queen/queen.cpp:59 engines/sky/detection.cpp:44 -msgid "Floppy intro" +#: engines/queen/queen.cpp:59 +msgid "Alternative intro" msgstr "" -#: engines/queen/queen.cpp:60 engines/sky/detection.cpp:45 -msgid "Use the floppy version's intro (CD version only)" +#: engines/queen/queen.cpp:60 +msgid "Use an alternative game intro (CD version only)" msgstr "" #: engines/sky/compact.cpp:130 @@ -2254,27 +2291,35 @@ msgstr "" "O arquivo \"sky.cpt\" possui um tamanho incorreto.\n" "Por favor, refaça o download em www.scummvm.org" -#: engines/sword1/animation.cpp:539 +#: engines/sky/detection.cpp:44 +msgid "Floppy intro" +msgstr "" + +#: engines/sky/detection.cpp:45 +msgid "Use the floppy version's intro (CD version only)" +msgstr "" + +#: engines/sword1/animation.cpp:519 #, c-format msgid "PSX stream cutscene '%s' cannot be played in paletted mode" msgstr "" -#: engines/sword1/animation.cpp:560 engines/sword2/animation.cpp:455 +#: engines/sword1/animation.cpp:540 engines/sword2/animation.cpp:439 msgid "DXA cutscenes found but ScummVM has been built without zlib support" msgstr "" "Vídeos no formato DXA foram encontrados, mas o ScummVM foi compilado sem " "suporte a zlib" -#: engines/sword1/animation.cpp:570 engines/sword2/animation.cpp:465 +#: engines/sword1/animation.cpp:550 engines/sword2/animation.cpp:449 msgid "MPEG2 cutscenes are no longer supported" msgstr "Vídeos em MPEG2 não são mais suportados" -#: engines/sword1/animation.cpp:576 engines/sword2/animation.cpp:473 +#: engines/sword1/animation.cpp:556 engines/sword2/animation.cpp:457 #, c-format msgid "Cutscene '%s' not found" msgstr "Vídeo '%s' não encontrado" -#: engines/sword1/control.cpp:863 +#: engines/sword1/control.cpp:865 msgid "" "ScummVM found that you have old savefiles for Broken Sword 1 that should be " "converted.\n" @@ -2292,7 +2337,7 @@ msgstr "" "Pressione OK para convertê-los agora, caso contrário você será solicitado " "novamente na próxima vez que você iniciar o jogo.\n" -#: engines/sword1/control.cpp:1232 +#: engines/sword1/control.cpp:1234 #, c-format msgid "" "Target new save game already exists!\n" @@ -2301,11 +2346,11 @@ msgstr "" "Já existe um jogo salvo no destino!\n" "Você gostaria de manter o jogo salvo (%s) ou o novo (%s)?\n" -#: engines/sword1/control.cpp:1235 +#: engines/sword1/control.cpp:1237 msgid "Keep the old one" msgstr "Mantenha o antigo" -#: engines/sword1/control.cpp:1235 +#: engines/sword1/control.cpp:1237 msgid "Keep the new one" msgstr "Mantenha o novo" @@ -2313,7 +2358,7 @@ msgstr "Mantenha o novo" msgid "This is the end of the Broken Sword 1 Demo" msgstr "Este é o fim do demo de Broken Sword 1" -#: engines/sword2/animation.cpp:435 +#: engines/sword2/animation.cpp:419 #, fuzzy msgid "" "PSX cutscenes found but ScummVM has been built without RGB color support" @@ -2329,6 +2374,17 @@ msgstr "" msgid "Show labels for objects on mouse hover" msgstr "" +#: engines/teenagent/resources.cpp:94 +msgid "" +"You're missing the 'teenagent.dat' file. Get it from the ScummVM website" +msgstr "" + +#: engines/teenagent/resources.cpp:115 +msgid "" +"The teenagent.dat file is compressed and zlib hasn't been included in this " +"executable. Please decompress it" +msgstr "" + #: engines/parallaction/saveload.cpp:133 #, c-format msgid "" @@ -2436,7 +2492,7 @@ msgstr "Sem música" msgid "Amiga Audio Emulator" msgstr "Emulador Som Amiga" -#: audio/softsynth/adlib.cpp:1593 +#: audio/softsynth/adlib.cpp:2284 msgid "AdLib Emulator" msgstr "Emulador AdLib" @@ -2581,11 +2637,11 @@ msgstr "Modo Touchpad ligado." msgid "Touchpad mode disabled." msgstr "Modo Touchpad desligado." -#: backends/platform/maemo/maemo.cpp:205 +#: backends/platform/maemo/maemo.cpp:209 msgid "Click Mode" msgstr "" -#: backends/platform/maemo/maemo.cpp:211 +#: backends/platform/maemo/maemo.cpp:215 #: backends/platform/symbian/src/SymbianActions.cpp:42 #: backends/platform/wince/CEActionsPocket.cpp:60 #: backends/platform/wince/CEActionsSmartphone.cpp:43 @@ -2593,36 +2649,36 @@ msgstr "" msgid "Left Click" msgstr "Clique com o botão esquerdo" -#: backends/platform/maemo/maemo.cpp:214 +#: backends/platform/maemo/maemo.cpp:218 #, fuzzy msgid "Middle Click" msgstr "Item do meio na esquerda" -#: backends/platform/maemo/maemo.cpp:217 +#: backends/platform/maemo/maemo.cpp:221 #: backends/platform/symbian/src/SymbianActions.cpp:43 #: backends/platform/wince/CEActionsSmartphone.cpp:44 #: backends/platform/bada/form.cpp:273 msgid "Right Click" msgstr "Clique com o botão direito" -#: backends/platform/sdl/macosx/appmenu_osx.mm:78 +#: backends/platform/sdl/macosx/appmenu_osx.mm:77 msgid "Hide ScummVM" msgstr "Ocultar ScummVM" -#: backends/platform/sdl/macosx/appmenu_osx.mm:83 +#: backends/platform/sdl/macosx/appmenu_osx.mm:82 msgid "Hide Others" msgstr "Ocultar Outros" -#: backends/platform/sdl/macosx/appmenu_osx.mm:88 +#: backends/platform/sdl/macosx/appmenu_osx.mm:87 msgid "Show All" msgstr "Mostrar Tudo" -#: backends/platform/sdl/macosx/appmenu_osx.mm:110 -#: backends/platform/sdl/macosx/appmenu_osx.mm:121 +#: backends/platform/sdl/macosx/appmenu_osx.mm:109 +#: backends/platform/sdl/macosx/appmenu_osx.mm:120 msgid "Window" msgstr "Janela" -#: backends/platform/sdl/macosx/appmenu_osx.mm:115 +#: backends/platform/sdl/macosx/appmenu_osx.mm:114 msgid "Minimize" msgstr "Minimizar" @@ -3003,41 +3059,46 @@ msgstr "Menu principal" msgid "Do you really want to quit?" msgstr "Você realmente deseja sair?" -#: backends/events/gph/gph-events.cpp:338 -#: backends/events/gph/gph-events.cpp:381 -#: backends/events/openpandora/op-events.cpp:139 +#: backends/events/gph/gph-events.cpp:386 +#: backends/events/gph/gph-events.cpp:429 +#: backends/events/openpandora/op-events.cpp:168 msgid "Touchscreen 'Tap Mode' - Left Click" msgstr "Touchscreen 'Modo Toque' - Clique Esquerdo" -#: backends/events/gph/gph-events.cpp:340 -#: backends/events/gph/gph-events.cpp:383 -#: backends/events/openpandora/op-events.cpp:141 +#: backends/events/gph/gph-events.cpp:388 +#: backends/events/gph/gph-events.cpp:431 +#: backends/events/openpandora/op-events.cpp:170 msgid "Touchscreen 'Tap Mode' - Right Click" msgstr "Touchscreen 'Modo Toque' - Clique Direito" -#: backends/events/gph/gph-events.cpp:342 -#: backends/events/gph/gph-events.cpp:385 -#: backends/events/openpandora/op-events.cpp:143 +#: backends/events/gph/gph-events.cpp:390 +#: backends/events/gph/gph-events.cpp:433 +#: backends/events/openpandora/op-events.cpp:172 msgid "Touchscreen 'Tap Mode' - Hover (No Click)" msgstr "Touchscreen 'Modo Toque' - Acima (Sem Clicar)" -#: backends/events/gph/gph-events.cpp:362 +#: backends/events/gph/gph-events.cpp:410 msgid "Maximum Volume" msgstr "Volume máximo" -#: backends/events/gph/gph-events.cpp:364 +#: backends/events/gph/gph-events.cpp:412 msgid "Increasing Volume" msgstr "Aumentando Volume" -#: backends/events/gph/gph-events.cpp:370 +#: backends/events/gph/gph-events.cpp:418 msgid "Minimal Volume" msgstr "Volume mínimo" -#: backends/events/gph/gph-events.cpp:372 +#: backends/events/gph/gph-events.cpp:420 msgid "Decreasing Volume" msgstr "Diminuindo Volume" -#: backends/updates/macosx/macosx-updates.mm:65 +#: backends/events/openpandora/op-events.cpp:174 +#, fuzzy +msgid "Touchscreen 'Tap Mode' - Hover (DPad Clicks)" +msgstr "Touchscreen 'Modo Toque' - Acima (Sem Clicar)" + +#: backends/updates/macosx/macosx-updates.mm:67 msgid "Check for Updates..." msgstr "Procurar por Atualizações..." diff --git a/po/ru_RU.po b/po/ru_RU.po index 019acbddbc..3140b41f50 100644 --- a/po/ru_RU.po +++ b/po/ru_RU.po @@ -1,5 +1,5 @@ # Russian translation for ScummVM. -# Copyright (C) 2010-2012 ScummVM Team +# Copyright (C) 2010-2013 ScummVM Team # This file is distributed under the same license as the ScummVM package. # Eugene Sandulenko <sev@scummvm.org>, 2010. # @@ -7,16 +7,16 @@ msgid "" msgstr "" "Project-Id-Version: ScummVM 1.3.0svn\n" "Report-Msgid-Bugs-To: scummvm-devel@lists.sf.net\n" -"POT-Creation-Date: 2012-07-08 12:25+0100\n" +"POT-Creation-Date: 2012-12-01 17:22+0000\n" "PO-Revision-Date: 2012-07-08 22:00+0200+0200\n" "Last-Translator: Eugene Sandulenko <sev@scummvm.org>\n" "Language-Team: Russian\n" +"Language: Russian\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=iso-8859-5\n" "Content-Transfer-Encoding: 8bit\n" -"Language: Russian\n" -"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%" -"10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" +"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n" +"%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" #: gui/about.cpp:91 #, c-format @@ -46,10 +46,11 @@ msgstr "²ÒÕàå" #: gui/browser.cpp:69 gui/chooser.cpp:45 gui/KeysDialog.cpp:43 #: gui/launcher.cpp:345 gui/massadd.cpp:94 gui/options.cpp:1228 -#: gui/saveload.cpp:64 gui/saveload.cpp:173 gui/themebrowser.cpp:54 -#: engines/engine.cpp:442 engines/scumm/dialogs.cpp:190 -#: engines/sword1/control.cpp:865 engines/parallaction/saveload.cpp:274 -#: backends/platform/wii/options.cpp:48 +#: gui/saveload-dialog.cpp:215 gui/saveload-dialog.cpp:275 +#: gui/saveload-dialog.cpp:545 gui/saveload-dialog.cpp:919 +#: gui/themebrowser.cpp:54 engines/engine.cpp:442 +#: engines/scumm/dialogs.cpp:190 engines/sword1/control.cpp:867 +#: engines/parallaction/saveload.cpp:274 backends/platform/wii/options.cpp:48 #: backends/events/default/default-events.cpp:191 #: backends/events/default/default-events.cpp:213 msgid "Cancel" @@ -70,15 +71,15 @@ msgstr "·ÐÚàëâì" msgid "Mouse click" msgstr "ºÛØÚ Üëèìî" -#: gui/gui-manager.cpp:122 base/main.cpp:300 +#: gui/gui-manager.cpp:122 base/main.cpp:301 msgid "Display keyboard" msgstr "¿ÞÚÐ×Ðâì ÚÛÐÒØÐâãàã" -#: gui/gui-manager.cpp:126 base/main.cpp:304 +#: gui/gui-manager.cpp:126 base/main.cpp:305 msgid "Remap keys" msgstr "¿ÕàÕÝÐ×ÝÐçØâì ÚÛÐÒØèØ" -#: gui/gui-manager.cpp:129 base/main.cpp:307 +#: gui/gui-manager.cpp:129 base/main.cpp:308 msgid "Toggle FullScreen" msgstr "¿ÕàÕÚÛîçÕÝØÕ ÝÐ ÒÕáì íÚàÐÝ" @@ -92,16 +93,16 @@ msgstr "½Ð×ÝÐçØâì" #: gui/KeysDialog.cpp:42 gui/launcher.cpp:346 gui/launcher.cpp:1001 #: gui/launcher.cpp:1005 gui/massadd.cpp:91 gui/options.cpp:1229 -#: engines/engine.cpp:361 engines/engine.cpp:372 engines/scumm/dialogs.cpp:192 -#: engines/scumm/scumm.cpp:1775 engines/agos/animation.cpp:561 -#: engines/groovie/script.cpp:420 engines/sky/compact.cpp:131 -#: engines/sky/compact.cpp:141 engines/sword1/animation.cpp:539 -#: engines/sword1/animation.cpp:560 engines/sword1/animation.cpp:570 -#: engines/sword1/animation.cpp:577 engines/sword1/control.cpp:865 -#: engines/sword1/logic.cpp:1633 engines/sword2/animation.cpp:435 -#: engines/sword2/animation.cpp:455 engines/sword2/animation.cpp:465 -#: engines/sword2/animation.cpp:474 engines/parallaction/saveload.cpp:274 -#: backends/platform/wii/options.cpp:47 +#: gui/saveload-dialog.cpp:920 engines/engine.cpp:361 engines/engine.cpp:372 +#: engines/scumm/dialogs.cpp:192 engines/scumm/scumm.cpp:1776 +#: engines/agos/animation.cpp:558 engines/groovie/script.cpp:420 +#: engines/sky/compact.cpp:131 engines/sky/compact.cpp:141 +#: engines/sword1/animation.cpp:519 engines/sword1/animation.cpp:540 +#: engines/sword1/animation.cpp:550 engines/sword1/animation.cpp:557 +#: engines/sword1/control.cpp:867 engines/sword1/logic.cpp:1633 +#: engines/sword2/animation.cpp:419 engines/sword2/animation.cpp:439 +#: engines/sword2/animation.cpp:449 engines/sword2/animation.cpp:458 +#: engines/parallaction/saveload.cpp:274 backends/platform/wii/options.cpp:47 #: backends/platform/wince/CELauncherDialog.cpp:54 msgid "OK" msgstr "OK" @@ -355,7 +356,7 @@ msgstr "ÍâÞâ ID ØÓàë ãÖÕ ØáßÞÛì×ãÕâáï. ¿ÞÖÐÛãÙáâÐ, ÒëÑÕàØâÕ ÔàãÓÞÙ." msgid "~Q~uit" msgstr "~²~ëåÞÔ" -#: gui/launcher.cpp:621 backends/platform/sdl/macosx/appmenu_osx.mm:96 +#: gui/launcher.cpp:621 backends/platform/sdl/macosx/appmenu_osx.mm:95 msgid "Quit ScummVM" msgstr "·ÐÒÕàèØâì ScummVM" @@ -363,7 +364,7 @@ msgstr "·ÐÒÕàèØâì ScummVM" msgid "A~b~out..." msgstr "¾ ß~à~ÞÓàÐÜÜÕ..." -#: gui/launcher.cpp:622 backends/platform/sdl/macosx/appmenu_osx.mm:70 +#: gui/launcher.cpp:622 backends/platform/sdl/macosx/appmenu_osx.mm:69 msgid "About ScummVM" msgstr "¾ ßàÞÓàÐÜÜÕ ScummVM" @@ -438,13 +439,13 @@ msgstr "¿ÞØáÚ Ò áߨáÚÕ ØÓà" msgid "Search:" msgstr "¿ÞØáÚ:" -#: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/mohawk/myst.cpp:255 +#: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/mohawk/myst.cpp:245 #: engines/mohawk/riven.cpp:716 engines/cruise/menu.cpp:214 msgid "Load game:" msgstr "·ÐÓàãרâì ØÓàã:" #: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/scumm/dialogs.cpp:188 -#: engines/mohawk/myst.cpp:255 engines/mohawk/riven.cpp:716 +#: engines/mohawk/myst.cpp:245 engines/mohawk/riven.cpp:716 #: engines/cruise/menu.cpp:214 backends/platform/wince/CEActionsPocket.cpp:267 #: backends/platform/wince/CEActionsSmartphone.cpp:231 msgid "Load" @@ -688,8 +689,7 @@ msgstr "SoundFont:" #: gui/options.cpp:849 gui/options.cpp:851 gui/options.cpp:852 msgid "SoundFont is supported by some audio cards, Fluidsynth and Timidity" msgstr "" -"SoundFontë ßÞÔÔÕàÖØÒÐîâáï ÝÕÚÞâÞàëÜØ ×ÒãÚÞÒëÜØ ÚÐàâÐÜØ, Fluidsynth Ø " -"Timidity" +"SoundFontë ßÞÔÔÕàÖØÒÐîâáï ÝÕÚÞâÞàëÜØ ×ÒãÚÞÒëÜØ ÚÐàâÐÜØ, Fluidsynth Ø Timidity" #: gui/options.cpp:851 msgctxt "lowres" @@ -742,7 +742,8 @@ msgstr "²ÚÛîçØâì àÕÖØÜ Roland GS" #: gui/options.cpp:880 msgid "Turns off General MIDI mapping for games with Roland MT-32 soundtrack" msgstr "" -"²ëÚÛîçÐÕâ áÞßÞáâÐÒÛÕÝØÕ General MIDI ÔÛï ØÓà á ×ÒãÚÞÒÞÙ ÔÞàÞÖÚÞÙ ÔÛï Roland MT-32" +"²ëÚÛîçÐÕâ áÞßÞáâÐÒÛÕÝØÕ General MIDI ÔÛï ØÓà á ×ÒãÚÞÒÞÙ ÔÞàÞÖÚÞÙ ÔÛï Roland " +"MT-32" #: gui/options.cpp:889 msgid "Don't use Roland MT-32 music" @@ -924,68 +925,101 @@ msgstr "" "ÂÕÜÐ, ÒëÑàÐÝÝÐï ÒÐÜØ, ÝÕ ßÞÔÔÕàÖØÒÐÕâ âÕÚãéØÙ ï×ëÚ. µáÛØ Òë åÞâØâÕ " "ØáßÞÛì×ÞÒÐâì íâã âÕÜã, ÒÐÜ ÝÕÞÑåÞÔØÜÞ áÝÐçÐÛÐ ßÕàÕÚÛîçØâìáï ÝÐ ÔàãÓÞÙ ï×ëÚ." -#: gui/saveload.cpp:59 gui/saveload.cpp:257 +#: gui/saveload-dialog.cpp:166 +msgid "List view" +msgstr "²ØÔ áߨáÚÐ" + +#: gui/saveload-dialog.cpp:167 +msgid "Grid view" +msgstr "²ØÔ áÕâÚØ" + +#: gui/saveload-dialog.cpp:210 gui/saveload-dialog.cpp:358 msgid "No date saved" msgstr "´ÐâÐ ÝÕ ×ÐߨáÐÝÐ" -#: gui/saveload.cpp:60 gui/saveload.cpp:258 +#: gui/saveload-dialog.cpp:211 gui/saveload-dialog.cpp:359 msgid "No time saved" msgstr "²àÕÜï ÝÕ ×ÐߨáÐÝÞ" -#: gui/saveload.cpp:61 gui/saveload.cpp:259 +#: gui/saveload-dialog.cpp:212 gui/saveload-dialog.cpp:360 msgid "No playtime saved" msgstr "²àÕÜï ØÓàë ÝÕ ×ÐߨáÐÝÞ" -#: gui/saveload.cpp:68 gui/saveload.cpp:173 +#: gui/saveload-dialog.cpp:219 gui/saveload-dialog.cpp:275 msgid "Delete" msgstr "ÃÔÐÛØâì" -#: gui/saveload.cpp:172 +#: gui/saveload-dialog.cpp:274 msgid "Do you really want to delete this savegame?" msgstr "²ë ÔÕÙáâÒØâÕÛìÝÞ åÞâØâÕ ãÔÐÛØâì íâÞ áÞåàÐÝÕÝØÕ?" -#: gui/saveload.cpp:282 +#: gui/saveload-dialog.cpp:383 gui/saveload-dialog.cpp:872 msgid "Date: " msgstr "´ÐâÐ: " -#: gui/saveload.cpp:286 +#: gui/saveload-dialog.cpp:387 gui/saveload-dialog.cpp:878 msgid "Time: " msgstr "²àÕÜï: " -#: gui/saveload.cpp:292 +#: gui/saveload-dialog.cpp:393 gui/saveload-dialog.cpp:886 msgid "Playtime: " msgstr "²àÕÜï ØÓàë: " -#: gui/saveload.cpp:305 gui/saveload.cpp:372 +#: gui/saveload-dialog.cpp:406 gui/saveload-dialog.cpp:494 msgid "Untitled savestate" msgstr "ÁÞåàÐÝÕÝØÕ ÑÕ× ØÜÕÝØ" +#: gui/saveload-dialog.cpp:546 +msgid "Next" +msgstr "ÁÛÕÔãîéØÙ" + +#: gui/saveload-dialog.cpp:549 +msgid "Prev" +msgstr "¿àÕÔëÔãéØÙ" + +#: gui/saveload-dialog.cpp:736 +msgid "New Save" +msgstr "½ÞÒÞÕ áÞåàÐÝÕÝØÕ" + +#: gui/saveload-dialog.cpp:736 +msgid "Create a new save game" +msgstr "ÁÞ×ÔÐâì ÝÞÒãî ×Ðߨáì ØÓàë" + +#: gui/saveload-dialog.cpp:865 +msgid "Name: " +msgstr "½Ð×ÒÐÝØÕ: " + +#: gui/saveload-dialog.cpp:937 +#, c-format +msgid "Enter a description for slot %d:" +msgstr "²ÒÕÔØâÕ ÞߨáÐÝØÕ áÛÞâÐ %d:" + #: gui/themebrowser.cpp:44 msgid "Select a Theme" msgstr "²ëÑÕàØâÕ âÕÜã" -#: gui/ThemeEngine.cpp:335 +#: gui/ThemeEngine.cpp:337 msgid "Disabled GFX" msgstr "±Õ× ÓàÐäØÚØ" -#: gui/ThemeEngine.cpp:335 +#: gui/ThemeEngine.cpp:337 msgctxt "lowres" msgid "Disabled GFX" msgstr "±Õ× ÓàÐäØÚØ" -#: gui/ThemeEngine.cpp:336 +#: gui/ThemeEngine.cpp:338 msgid "Standard Renderer (16bpp)" msgstr "ÁâÐÝÔÐàâÝëÙ àÐáâÕàØ×ÐâÞà (16bpp)" -#: gui/ThemeEngine.cpp:336 +#: gui/ThemeEngine.cpp:338 msgid "Standard (16bpp)" msgstr "ÁâÐÝÔÐàâÝëÙ àÐáâÕàØ×ÐâÞà (16bpp)" -#: gui/ThemeEngine.cpp:338 +#: gui/ThemeEngine.cpp:340 msgid "Antialiased Renderer (16bpp)" msgstr "ÀÐáâÕàØ×ÐâÞà áÞ áÓÛÐÖØÒÐÝØÕÜ (16bpp)" -#: gui/ThemeEngine.cpp:338 +#: gui/ThemeEngine.cpp:340 msgid "Antialiased (16bpp)" msgstr "ÀÐáâÕàØ×ÐâÞà áÞ áÓÛÐÖØÒÐÝØÕÜ (16bpp)" @@ -993,35 +1027,35 @@ msgstr "ÀÐáâÕàØ×ÐâÞà áÞ áÓÛÐÖØÒÐÝØÕÜ (16bpp)" msgid "Clear value" msgstr "¾çØáâØâì ×ÝÐçÕÝØÕ" -#: base/main.cpp:209 +#: base/main.cpp:210 #, c-format msgid "Engine does not support debug level '%s'" msgstr "´ÒØÖÞÚ ÝÕ ßÞÔÔÕàÖØÒÐÕâ ãàÞÒÕÝì ÞâÛÐÔÚØ '%s'" -#: base/main.cpp:287 +#: base/main.cpp:288 msgid "Menu" msgstr "¼ÕÝî" -#: base/main.cpp:290 backends/platform/symbian/src/SymbianActions.cpp:45 +#: base/main.cpp:291 backends/platform/symbian/src/SymbianActions.cpp:45 #: backends/platform/wince/CEActionsPocket.cpp:45 #: backends/platform/wince/CEActionsSmartphone.cpp:46 msgid "Skip" msgstr "¿àÞßãáâØâì" -#: base/main.cpp:293 backends/platform/symbian/src/SymbianActions.cpp:50 +#: base/main.cpp:294 backends/platform/symbian/src/SymbianActions.cpp:50 #: backends/platform/wince/CEActionsPocket.cpp:42 msgid "Pause" msgstr "¿Ðã×Ð" -#: base/main.cpp:296 +#: base/main.cpp:297 msgid "Skip line" msgstr "¿àÞßãáâØâì áâàÞÚã" -#: base/main.cpp:467 +#: base/main.cpp:468 msgid "Error running game:" msgstr "¾èØÑÚÐ ×ÐßãáÚÐ ØÓàë:" -#: base/main.cpp:491 +#: base/main.cpp:492 msgid "Could not find any engine capable of running the selected game" msgstr "½Õ ÜÞÓã ÝÐÙâØ ÔÒØÖÞÚ ÔÛï ×ÐßãáÚÐ ÒëÑàÐÝÝÞÙ ØÓàë" @@ -1089,17 +1123,17 @@ msgstr "¿àÕàÒÐÝÞ ßÞÛì×ÞÒÐâÕÛÕÜ" msgid "Unknown error" msgstr "½ÕØ×ÒÕáâÝÐï ÞèØÑÚÐ" -#: engines/advancedDetector.cpp:324 +#: engines/advancedDetector.cpp:316 #, c-format msgid "The game in '%s' seems to be unknown." msgstr "ºÐÖÕâáï, çâÞ ØÓàÐ '%s' Õéñ ÝÕØ×ÒÕáâÝÐ." -#: engines/advancedDetector.cpp:325 +#: engines/advancedDetector.cpp:317 msgid "Please, report the following data to the ScummVM team along with name" msgstr "" "¿ÞÖÐÛãÙáâÐ, ßÕàÕÔÐÙâÕ áÛÕÔãîéØÕ ÔÐÝÝëÕ ÚÞÜÐÝÔÕ ScummVM ÒÜÕáâÕ á ÝÐ×ÒÐÝØÕÜ" -#: engines/advancedDetector.cpp:327 +#: engines/advancedDetector.cpp:319 msgid "of the game you tried to add and its version/language/etc.:" msgstr "ØÓàë, ÚÞâÞàãî Òë ßëâÐÕâÕáì ÔÞÑÐÒØâì, Ø ãÚÐÖØâÕ Õñ ÒÕàáØî, ï×ëÚ Ø â.Ô." @@ -1137,13 +1171,13 @@ msgid "~R~eturn to Launcher" msgstr "~²~ ÓÛÐÒÝÞÕ ÜÕÝî" #: engines/dialogs.cpp:115 engines/agi/saveload.cpp:803 -#: engines/cruise/menu.cpp:212 engines/sci/engine/kfile.cpp:735 +#: engines/cruise/menu.cpp:212 engines/sci/engine/kfile.cpp:742 msgid "Save game:" msgstr "ÁÞåàÐÝØâì ØÓàã:" #: engines/dialogs.cpp:115 engines/agi/saveload.cpp:803 #: engines/scumm/dialogs.cpp:187 engines/cruise/menu.cpp:212 -#: engines/sci/engine/kfile.cpp:735 +#: engines/sci/engine/kfile.cpp:742 #: backends/platform/symbian/src/SymbianActions.cpp:44 #: backends/platform/wince/CEActionsPocket.cpp:43 #: backends/platform/wince/CEActionsPocket.cpp:267 @@ -1168,9 +1202,9 @@ msgid "" "Gamestate save failed (%s)! Please consult the README for basic information, " "and for instructions on how to obtain further assistance." msgstr "" -"½Õ ãÔÐÛÞáì áÞåàÐÝØâì ØÓàã (%s)! " -"¿ÞÖÐÛãÙáâÐ, ÞÑàÐâØâÕáì Ò äÐÙÛ README ×Ð ÑÐ×ÞÒÞÙ ØÝäÞàÜÐæØÕÙ, Ð âÐÚÖÕ " -"ØÝáâàãÚæØïÜØ Þ âÞÜ, ÚÐÚ ßÞÛãçØâì ÔÐÛìÝÕÙèãî ßÞÜÞéì." +"½Õ ãÔÐÛÞáì áÞåàÐÝØâì ØÓàã (%s)! ¿ÞÖÐÛãÙáâÐ, ÞÑàÐâØâÕáì Ò äÐÙÛ README ×Ð " +"ÑÐ×ÞÒÞÙ ØÝäÞàÜÐæØÕÙ, Ð âÐÚÖÕ ØÝáâàãÚæØïÜØ Þ âÞÜ, ÚÐÚ ßÞÛãçØâì ÔÐÛìÝÕÙèãî " +"ßÞÜÞéì." #: engines/dialogs.cpp:301 engines/mohawk/dialogs.cpp:109 #: engines/mohawk/dialogs.cpp:174 @@ -1237,9 +1271,9 @@ msgid "" "Gamestate load failed (%s)! Please consult the README for basic information, " "and for instructions on how to obtain further assistance." msgstr "" -"½Õ ãÔÐÛÞáì ßàÞçØâÐâì áÞåàÐÝÕÝØÕ ØÓàë (%s)! " -"¿ÞÖÐÛãÙáâÐ, ÞÑàÐâØâÕáì Ò äÐÙÛ README ×Ð ÑÐ×ÞÒÞÙ ØÝäÞàÜÐæØÕÙ, Ð âÐÚÖÕ " -"ØÝáâàãÚæØïÜØ Þ âÞÜ, ÚÐÚ ßÞÛãçØâì ÔÐÛìÝÕÙèãî ßÞÜÞéì." +"½Õ ãÔÐÛÞáì ßàÞçØâÐâì áÞåàÐÝÕÝØÕ ØÓàë (%s)! ¿ÞÖÐÛãÙáâÐ, ÞÑàÐâØâÕáì Ò äÐÙÛ " +"README ×Ð ÑÐ×ÞÒÞÙ ØÝäÞàÜÐæØÕÙ, Ð âÐÚÖÕ ØÝáâàãÚæØïÜØ Þ âÞÜ, ÚÐÚ ßÞÛãçØâì " +"ÔÐÛìÝÕÙèãî ßÞÜÞéì." #: engines/engine.cpp:439 msgid "" @@ -1255,23 +1289,23 @@ msgstr "" msgid "Start anyway" msgstr "²áñ àÐÒÝÞ ×ÐßãáâØâì" -#: engines/agi/detection.cpp:145 engines/dreamweb/detection.cpp:47 -#: engines/sci/detection.cpp:390 +#: engines/agi/detection.cpp:142 engines/dreamweb/detection.cpp:47 +#: engines/sci/detection.cpp:393 msgid "Use original save/load screens" msgstr "¸áßÞÛì×ÞÒÐâì ÞàØÓØÝÐÛìÝëÕ íÚàÐÝë ×ÐߨáØ/çâÕÝØï ØÓàë" -#: engines/agi/detection.cpp:146 engines/dreamweb/detection.cpp:48 -#: engines/sci/detection.cpp:391 +#: engines/agi/detection.cpp:143 engines/dreamweb/detection.cpp:48 +#: engines/sci/detection.cpp:394 msgid "Use the original save/load screens, instead of the ScummVM ones" msgstr "" -"¸áßÞÛì×ÞÒÐâì ÞàØÓØÝÐÛìÝëÕ íÚàÐÝë ×ÐßØáØ Ø áÞåàÐÝÕÝØï ØÓàë ÒÜÕáâÞ " -"áÔÕÛÐÝÝëå Ò ScummVM" +"¸áßÞÛì×ÞÒÐâì ÞàØÓØÝÐÛìÝëÕ íÚàÐÝë ×ÐßØáØ Ø áÞåàÐÝÕÝØï ØÓàë ÒÜÕáâÞ áÔÕÛÐÝÝëå Ò " +"ScummVM" -#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:831 +#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:838 msgid "Restore game:" msgstr "²ÞááâÐÝÞÒØâì ØÓàã:" -#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:831 +#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:838 msgid "Restore" msgstr "²ÞááâÐÝÞÒØâì" @@ -1283,57 +1317,61 @@ msgstr "¸áßÞÛì×ÞÒÐâì àÕÖØÜ ïàÚÞÙ ßÐÛØâàë" msgid "Display graphics using the game's bright palette" msgstr "ÀØáãÕâ ÓàÐäØÚã á ØáßÞÛì×ÞÒÐÝØÕÜ ïàÚÞÙ ßÐÛØâàë ØÓàë" -#: engines/sci/detection.cpp:370 +#: engines/sci/detection.cpp:373 msgid "EGA undithering" msgstr "EGA ÑÕ× àÐáâàÐ" -#: engines/sci/detection.cpp:371 +#: engines/sci/detection.cpp:374 msgid "Enable undithering in EGA games" msgstr "²ÚÛîçÐÕâ àÕÖØÜ ÑÕ× àÐáâàØàÞÒÐÝØï Ò EGA ØÓàÐå" -#: engines/sci/detection.cpp:380 +#: engines/sci/detection.cpp:383 msgid "Prefer digital sound effects" msgstr "¿àÕÔßÞçØâÐâì æØäàÞÒëÕ ×ÒãÚÞÒëÕ íääÕÚâë" -#: engines/sci/detection.cpp:381 +#: engines/sci/detection.cpp:384 msgid "Prefer digital sound effects instead of synthesized ones" -msgstr "¾âÔÐÒÐâì ßàÕÔßÞçâÕÝØÕ æØäàÞÒëÜ ×ÒãÚÞÒëÜ íääÕÚâÐÜ ÒÜÕáâÞ áØÝâÕרàÞÒÐÝÝëå" +msgstr "" +"¾âÔÐÒÐâì ßàÕÔßÞçâÕÝØÕ æØäàÞÒëÜ ×ÒãÚÞÒëÜ íääÕÚâÐÜ ÒÜÕáâÞ áØÝâÕרàÞÒÐÝÝëå" -#: engines/sci/detection.cpp:400 +#: engines/sci/detection.cpp:403 msgid "Use IMF/Yamaha FB-01 for MIDI output" msgstr "¸áßÞÛì×ÞÒÐâì IMF/Yamaha FB-01 ÔÛï ÒëÒÞÔÐ MIDI" -#: engines/sci/detection.cpp:401 +#: engines/sci/detection.cpp:404 msgid "" "Use an IBM Music Feature card or a Yamaha FB-01 FM synth module for MIDI " "output" msgstr "" -"¸áßÞÛì×ÒÞÒÐâì ×ÒãÚÞÒãî ÚÐàâÚã IBM Music Feature ØÛØ ÜÞÔãÛì áØÝâÕ×Ð " -"Yamaha FB-01 FM ÔÛï MIDI" +"¸áßÞÛì×ÒÞÒÐâì ×ÒãÚÞÒãî ÚÐàâÚã IBM Music Feature ØÛØ ÜÞÔãÛì áØÝâÕ×Ð Yamaha " +"FB-01 FM ÔÛï MIDI" -#: engines/sci/detection.cpp:411 +#: engines/sci/detection.cpp:414 msgid "Use CD audio" msgstr "¸áßÞÛì×ÞÒÐâì CD ÐãÔØÞ" -#: engines/sci/detection.cpp:412 +#: engines/sci/detection.cpp:415 msgid "Use CD audio instead of in-game audio, if available" -msgstr "¸áßÞÛì×ÞÒÐâì ×ÒãÚÞÒëÕ ÔÞàÞÖÚØ á CD ÒÜÕáâÞ Üã×ëÚØ Ø× äÐÙÛÞÒ ØÓàë (ÕáÛØ ÔÞáâãßÝÞ)" +msgstr "" +"¸áßÞÛì×ÞÒÐâì ×ÒãÚÞÒëÕ ÔÞàÞÖÚØ á CD ÒÜÕáâÞ Üã×ëÚØ Ø× äÐÙÛÞÒ ØÓàë (ÕáÛØ " +"ÔÞáâãßÝÞ)" -#: engines/sci/detection.cpp:422 +#: engines/sci/detection.cpp:425 msgid "Use Windows cursors" msgstr "¸áßÞÛì×ÞÒÐâì ÚãàáÞàë Windows" -#: engines/sci/detection.cpp:423 +#: engines/sci/detection.cpp:426 msgid "" "Use the Windows cursors (smaller and monochrome) instead of the DOS ones" msgstr "" -"¸áßÞÛì×ÞÒÐâì ÚãàáÞàë Windows (ÜÕÝìèØÕ ßÞ àÐ×ÜÕàã Ø ÞÔÝÞæÒÕâÝëÕ) ÒÜÕáâÞ ÚãàáÞàÞÒ DOS" +"¸áßÞÛì×ÞÒÐâì ÚãàáÞàë Windows (ÜÕÝìèØÕ ßÞ àÐ×ÜÕàã Ø ÞÔÝÞæÒÕâÝëÕ) ÒÜÕáâÞ " +"ÚãàáÞàÞÒ DOS" -#: engines/sci/detection.cpp:433 +#: engines/sci/detection.cpp:436 msgid "Use silver cursors" msgstr "¸áßÞÛì×ÞÒÐâì áÕàÕÑàïÝÝëÕ ÚãàáÞàë" -#: engines/sci/detection.cpp:434 +#: engines/sci/detection.cpp:437 msgid "" "Use the alternate set of silver cursors, instead of the normal golden ones" msgstr "" @@ -1986,7 +2024,7 @@ msgstr "»ÕâÕâì ÒßàÐÒÞ" msgid "Fly to lower right" msgstr "»ÕâÕâì ÒßàÐÒÞ-ÒÝØ×" -#: engines/scumm/scumm.cpp:1773 +#: engines/scumm/scumm.cpp:1774 #, c-format msgid "" "Native MIDI support requires the Roland Upgrade from LucasArts,\n" @@ -1995,7 +2033,7 @@ msgstr "" "ÀÕÖØÜ \"àÞÔÝÞÓÞ\" MIDI âàÕÑãÕâ ÞÑÝÞÒÛÕÝØÕ Roland Upgrade Þâ\n" "LucasArts, ÝÞ ÝÕ åÒÐâÐÕâ %s. ¿ÕàÕÚÛîçÐîáì ÝÐ AdLib." -#: engines/scumm/scumm.cpp:2278 engines/agos/saveload.cpp:202 +#: engines/scumm/scumm.cpp:2295 engines/agos/saveload.cpp:220 #, c-format msgid "" "Failed to save game state to file:\n" @@ -2006,7 +2044,7 @@ msgstr "" "\n" "%s" -#: engines/scumm/scumm.cpp:2285 engines/agos/saveload.cpp:167 +#: engines/scumm/scumm.cpp:2302 engines/agos/saveload.cpp:185 #, c-format msgid "" "Failed to load game state from file:\n" @@ -2017,7 +2055,7 @@ msgstr "" "\n" "%s" -#: engines/scumm/scumm.cpp:2297 engines/agos/saveload.cpp:210 +#: engines/scumm/scumm.cpp:2314 engines/agos/saveload.cpp:228 #, c-format msgid "" "Successfully saved game state in file:\n" @@ -2028,7 +2066,7 @@ msgstr "" "\n" "%s" -#: engines/scumm/scumm.cpp:2512 +#: engines/scumm/scumm.cpp:2529 msgid "" "Usually, Maniac Mansion would start now. But ScummVM doesn't do that yet. To " "play it, go to 'Add Game' in the ScummVM start menu and select the 'Maniac' " @@ -2064,17 +2102,17 @@ msgstr "³ÛÐÒÝÞÕ ÜÕÝî" msgid "~W~ater Effect Enabled" msgstr "ÍääÕÚâë ÒÞÔë ÒÚÛîçÕÝë" -#: engines/agos/animation.cpp:560 +#: engines/agos/animation.cpp:557 #, c-format msgid "Cutscene file '%s' not found!" msgstr "ÄÐÙÛ ×ÐáâÐÒÚØ '%s' ÝÕ ÝÐÙÔÕÝ!" #: engines/gob/inter_playtoons.cpp:256 engines/gob/inter_v2.cpp:1287 -#: engines/tinsel/saveload.cpp:502 +#: engines/tinsel/saveload.cpp:532 msgid "Failed to load game state from file." msgstr "½Õ ãÔÐÛÞáì ×ÐÓàãרâì áÞåàÐÝñÝÝãî ØÓàã Ø× äÐÙÛÐ." -#: engines/gob/inter_v2.cpp:1357 engines/tinsel/saveload.cpp:515 +#: engines/gob/inter_v2.cpp:1357 engines/tinsel/saveload.cpp:545 msgid "Failed to save game state to file." msgstr "½Õ ãÔÐÛÞáì áÞåàÐÝØâì ØÓàã Ò äÐÙÛ." @@ -2192,7 +2230,7 @@ msgstr "¾ßæØØ" msgid "Choose Spell" msgstr "²ëÑàÐâì ×ÐÚÛØÝÐÝØÕ" -#: engines/kyra/sound_midi.cpp:475 +#: engines/kyra/sound_midi.cpp:477 msgid "" "You appear to be using a General MIDI device,\n" "but your game only supports Roland MT32 MIDI.\n" @@ -2207,13 +2245,13 @@ msgstr "" "ÜÞÖÕâ âÐÚ ßÞÛãçØâìáï, çâÞ ÝÕÚÞâÞàëÕ âàÕÚØ ÑãÔãâ\n" "áëÓàÐÝë ÝÕÒÕàÝÞ." -#: engines/queen/queen.cpp:59 engines/sky/detection.cpp:44 -msgid "Floppy intro" -msgstr "²áâãßÛÕÝØÕ á äÛÞßߨÚÞÒ" +#: engines/queen/queen.cpp:59 +msgid "Alternative intro" +msgstr "°ÛìâÕàÝÐâØÒÝÞÕ ÒáâãßÛÕÝØÕ" -#: engines/queen/queen.cpp:60 engines/sky/detection.cpp:45 -msgid "Use the floppy version's intro (CD version only)" -msgstr "¸áßÞÛì×ÞÒÐâì ÒáâãßÛÕÝØÕ á ÓØÑÚØå ÔØáÚÞÒ (âÞÛìÚÞ ÔÛï CD ÒÕàáØØ ØÓàë)" +#: engines/queen/queen.cpp:60 +msgid "Use an alternative game intro (CD version only)" +msgstr "¸áßÞÛì×ÞÒÐâì ÐÛìâÕàÝÐâØÒÝÞÕ ÒáâãßÛÕÝØÕ (âÞÛìÚÞ ÔÛï CD ÒÕàáØØ ØÓàë)" #: engines/sky/compact.cpp:130 msgid "" @@ -2231,26 +2269,34 @@ msgstr "" "ÄÐÙÛ sky.cpt ØÜÕÕâ ÝÕÒÕàÝëÙ àÐ×ÜÕà.\n" "¿ÞÖÐÛãÙáâÐ, áÚÐçÐÙâÕ ÕÓÞ ×ÐÝÞÒÞ á www.scummvm.org" -#: engines/sword1/animation.cpp:539 +#: engines/sky/detection.cpp:44 +msgid "Floppy intro" +msgstr "²áâãßÛÕÝØÕ á äÛÞßߨÚÞÒ" + +#: engines/sky/detection.cpp:45 +msgid "Use the floppy version's intro (CD version only)" +msgstr "¸áßÞÛì×ÞÒÐâì ÒáâãßÛÕÝØÕ á ÓØÑÚØå ÔØáÚÞÒ (âÞÛìÚÞ ÔÛï CD ÒÕàáØØ ØÓàë)" + +#: engines/sword1/animation.cpp:519 #, c-format msgid "PSX stream cutscene '%s' cannot be played in paletted mode" msgstr "·ÐáâÐÒÚÐ PSX '%s' ÝÕ ÜÞÖÕâ Ñëâì ßàÞØÓàÐÝÐ Ò àÕÖØÜÕ á ßÐÛØâàÞÙ" -#: engines/sword1/animation.cpp:560 engines/sword2/animation.cpp:455 +#: engines/sword1/animation.cpp:540 engines/sword2/animation.cpp:439 msgid "DXA cutscenes found but ScummVM has been built without zlib support" msgstr "" "½ÐÙÔÕÝë ×ÐáâÐÒÚØ Ò äÞàÜÐâÕ DXA, ÝÞ ScummVM ÑëÛ áÞÑàÐÝ ÑÕ× ßÞÔÔÕàÖÚØ zlib" -#: engines/sword1/animation.cpp:570 engines/sword2/animation.cpp:465 +#: engines/sword1/animation.cpp:550 engines/sword2/animation.cpp:449 msgid "MPEG2 cutscenes are no longer supported" msgstr "·ÐáâÐÒÚØ Ò äÞàÜÐâÕ MPEG2 ÑÞÛìèÕ ÝÕ ßÞÔÔÕàÖØÒÐîâáï" -#: engines/sword1/animation.cpp:576 engines/sword2/animation.cpp:473 +#: engines/sword1/animation.cpp:556 engines/sword2/animation.cpp:457 #, c-format msgid "Cutscene '%s' not found" msgstr "·ÐáâÐÒÚÐ '%s' ÝÕ ÝÐÙÔÕÝÐ" -#: engines/sword1/control.cpp:863 +#: engines/sword1/control.cpp:865 msgid "" "ScummVM found that you have old savefiles for Broken Sword 1 that should be " "converted.\n" @@ -2267,7 +2313,7 @@ msgstr "" "½ÐÖÜØâÕ ¾º, çâÞÑë ßÕàÕÒÕáâØ Øå Ò ÝÞÒëÙ äÞàÜÐâ áÕÙçÐá, Ò ßàÞâØÒÝÞÜ áÛãçÐÕ íâÞ " "áÞÞÑéÕÝØÕ ßÞïÒØâáï áÝÞÒÐ ßàØ áÛÕÔãîéÕÜ ×ÐßãáÚÕ ØÓàë.\n" -#: engines/sword1/control.cpp:1232 +#: engines/sword1/control.cpp:1234 #, c-format msgid "" "Target new save game already exists!\n" @@ -2276,11 +2322,11 @@ msgstr "" "ÁÞåàÐÝÕÝØÕ ØÓàë á âÐÚØÜ ØÜÕÝÕÜ ãÖÕ áãéÕáâÒãÕâ!\n" "²ë åÞâØâÕ ÞáâÐÒØâì áâÐàÞÕ ÝÐ×ÒÐÝØÕ (%s) ØÛØ áÔÕÛÐâì ÝÞÒÞÕ (%s)?\n" -#: engines/sword1/control.cpp:1235 +#: engines/sword1/control.cpp:1237 msgid "Keep the old one" msgstr "¾áâÐÒØâì áâÐàÞÕ" -#: engines/sword1/control.cpp:1235 +#: engines/sword1/control.cpp:1237 msgid "Keep the new one" msgstr "ÁÔÕÛÐâì ÝÞÒÞÕ" @@ -2288,11 +2334,12 @@ msgstr "ÁÔÕÛÐâì ÝÞÒÞÕ" msgid "This is the end of the Broken Sword 1 Demo" msgstr "ÍâÞ ×ÐÒÕàèÕÝØÕ ÔÕÜÞ ÁÛÞÜÐÝÝÞÓÞ ¼ÕçÐ 1" -#: engines/sword2/animation.cpp:435 +#: engines/sword2/animation.cpp:419 msgid "" "PSX cutscenes found but ScummVM has been built without RGB color support" msgstr "" -"½ÐÙÔÕÝë ×ÐáâÐÒÚØ Ò äÞàÜÐâÕ PSX, ÝÞ ScummVM ÑëÛ áÞÑàÐÝ ÑÕ× ßÞÔÔÕàÖÚØ RGB æÒÕâÞÒ" +"½ÐÙÔÕÝë ×ÐáâÐÒÚØ Ò äÞàÜÐâÕ PSX, ÝÞ ScummVM ÑëÛ áÞÑàÐÝ ÑÕ× ßÞÔÔÕàÖÚØ RGB " +"æÒÕâÞÒ" #: engines/sword2/sword2.cpp:79 msgid "Show object labels" @@ -2302,6 +2349,20 @@ msgstr "¿ÞÚÐ×ëÒÐâì ÝÐ×ÒÐÝØï ÞÑêÕÚâÞÒ" msgid "Show labels for objects on mouse hover" msgstr "¿ÞÚÐ×ëÒÐÕâ ÝÐ×ÒÐÝØï ÞÑêÕÚâÞÒ ßà ØÝÐÒÕÔÕÝØØ ÚãàáÞàÐ ÜëèØ" +#: engines/teenagent/resources.cpp:94 +msgid "" +"You're missing the 'teenagent.dat' file. Get it from the ScummVM website" +msgstr "" +"à ÒÐá ÞâáãâáâÒãÕâ äÐÙÛ 'teenagent.dat'. ÁÚÐçÐÙâÕ ÕÓÞ á ÒÕÑ-áÐÙâÐ ScummVM" + +#: engines/teenagent/resources.cpp:115 +msgid "" +"The teenagent.dat file is compressed and zlib hasn't been included in this " +"executable. Please decompress it" +msgstr "" +"ÄÐÙÛ teenagent.dat áÖÐâ, ÝÞ zlib ÝÕ ÒÚÛîçÕÝÞ Ò íâã ßàÞÓàÐÜÜã. ¿ÞÖÐÛãÙáâÐ," +"àÐáßÐÚãÙâÕ ÕÓÞ" + #: engines/parallaction/saveload.cpp:133 #, c-format msgid "" @@ -2329,8 +2390,8 @@ msgid "" "Press OK to convert them now, otherwise you will be asked next time.\n" msgstr "" "ScummVM ÞÑÝÐàãÖØÛ ã ÒÐá áâÐàëÕ áÞåàÐÝÕÝØï ØÓàë Nippon Safes, ÚÞâÞàëÕ " -"ÝÕÞÑåÞÔØÜÞ ßÕàÕØÜÕÝÞÒÐâì. ÁâÐàëÕ ÝÐ×ÒÐÝØï ÑÞÛìèÕ ÝÕ ßÞÔÔÕàÖØÒÐîâáï, Ø ßÞíâÞÜã " -"Òë ÝÕ áÜÞÖÕâÕ ×ÐÓàãרâì áÞåàÐÝÕÝØï, ÕáÛØ ÝÕ ßÕàÕØÜÕÝãÕâÕ Øå.\n" +"ÝÕÞÑåÞÔØÜÞ ßÕàÕØÜÕÝÞÒÐâì. ÁâÐàëÕ ÝÐ×ÒÐÝØï ÑÞÛìèÕ ÝÕ ßÞÔÔÕàÖØÒÐîâáï, Ø " +"ßÞíâÞÜã Òë ÝÕ áÜÞÖÕâÕ ×ÐÓàãרâì áÞåàÐÝÕÝØï, ÕáÛØ ÝÕ ßÕàÕØÜÕÝãÕâÕ Øå.\n" "\n" "½ÐÖÜØâÕ ¾º, çâÞÑë ßÕàÕØÜÕÝÞÒÐâì Øå áÕÙçÐá, Ò ßàÞâØÒÝÞÜ áÛãçÐÕ íâÞ ÖÕ " "áÞÞÑéÕÝØÕ ßÞïÒØâáï ßàØ áÛÕÔãîéÕÜ ×ÐßãáÚÕ ØÓàë.\n" @@ -2408,7 +2469,7 @@ msgstr "±Õ× Üã×ëÚØ" msgid "Amiga Audio Emulator" msgstr "ÍÜãÛïâÞà ×ÒãÚÐ Amiga" -#: audio/softsynth/adlib.cpp:1593 +#: audio/softsynth/adlib.cpp:2284 msgid "AdLib Emulator" msgstr "ÍÜãÛïâÞà AdLib" @@ -2552,11 +2613,11 @@ msgstr "ÀÕÖØÜ âÐçßÐÔÐ ÒÚÛîçÕÝ." msgid "Touchpad mode disabled." msgstr "ÀÕÖØÜ âÐçßÐÔÐ ÒëÚÛîçÕÝ." -#: backends/platform/maemo/maemo.cpp:205 +#: backends/platform/maemo/maemo.cpp:209 msgid "Click Mode" msgstr "ÀÕÖØÜ éÕÛçÚÐ" -#: backends/platform/maemo/maemo.cpp:211 +#: backends/platform/maemo/maemo.cpp:215 #: backends/platform/symbian/src/SymbianActions.cpp:42 #: backends/platform/wince/CEActionsPocket.cpp:60 #: backends/platform/wince/CEActionsSmartphone.cpp:43 @@ -2564,35 +2625,35 @@ msgstr "ÀÕÖØÜ éÕÛçÚÐ" msgid "Left Click" msgstr "»ÕÒëÙ éÕÛçÞÚ" -#: backends/platform/maemo/maemo.cpp:214 +#: backends/platform/maemo/maemo.cpp:218 msgid "Middle Click" msgstr "ÁàÕÔÝØÙ éÕÛçÞÚ" -#: backends/platform/maemo/maemo.cpp:217 +#: backends/platform/maemo/maemo.cpp:221 #: backends/platform/symbian/src/SymbianActions.cpp:43 #: backends/platform/wince/CEActionsSmartphone.cpp:44 #: backends/platform/bada/form.cpp:273 msgid "Right Click" msgstr "¿àÐÒëÙ éÕÛçÞÚ" -#: backends/platform/sdl/macosx/appmenu_osx.mm:78 +#: backends/platform/sdl/macosx/appmenu_osx.mm:77 msgid "Hide ScummVM" msgstr "ÁÚàëâì ScummVM" -#: backends/platform/sdl/macosx/appmenu_osx.mm:83 +#: backends/platform/sdl/macosx/appmenu_osx.mm:82 msgid "Hide Others" msgstr "ÁÚàëâì ÞáâÐÛìÝëÕ" -#: backends/platform/sdl/macosx/appmenu_osx.mm:88 +#: backends/platform/sdl/macosx/appmenu_osx.mm:87 msgid "Show All" msgstr "¿ÞÚÐ×Ðâì ÒáÕ" -#: backends/platform/sdl/macosx/appmenu_osx.mm:110 -#: backends/platform/sdl/macosx/appmenu_osx.mm:121 +#: backends/platform/sdl/macosx/appmenu_osx.mm:109 +#: backends/platform/sdl/macosx/appmenu_osx.mm:120 msgid "Window" msgstr "¾ÚÝÞ" -#: backends/platform/sdl/macosx/appmenu_osx.mm:115 +#: backends/platform/sdl/macosx/appmenu_osx.mm:114 msgid "Minimize" msgstr "ÃÑàÐâì Ò Dock" @@ -2970,41 +3031,46 @@ msgstr "³ÛÐÒÝÞÕ ÜÕÝî" msgid "Do you really want to quit?" msgstr "²ë ÔÕÙáâÒØâÕÛìÝÞ åÞâØâÕ ÒëÙâØ?" -#: backends/events/gph/gph-events.cpp:338 -#: backends/events/gph/gph-events.cpp:381 -#: backends/events/openpandora/op-events.cpp:139 +#: backends/events/gph/gph-events.cpp:386 +#: backends/events/gph/gph-events.cpp:429 +#: backends/events/openpandora/op-events.cpp:168 msgid "Touchscreen 'Tap Mode' - Left Click" msgstr "ÀÕÖØÜ 'ÚÐáÐÝØÙ' âÐçáÚàØÝÐ - »ÕÒëÙ ÚÛØÚ" -#: backends/events/gph/gph-events.cpp:340 -#: backends/events/gph/gph-events.cpp:383 -#: backends/events/openpandora/op-events.cpp:141 +#: backends/events/gph/gph-events.cpp:388 +#: backends/events/gph/gph-events.cpp:431 +#: backends/events/openpandora/op-events.cpp:170 msgid "Touchscreen 'Tap Mode' - Right Click" msgstr "ÀÕÖØÜ 'ÚÐáÐÝØÙ' âÐçáÚàØÝÐ - ¿àÐÒëÙ ÚÛØÚ" -#: backends/events/gph/gph-events.cpp:342 -#: backends/events/gph/gph-events.cpp:385 -#: backends/events/openpandora/op-events.cpp:143 +#: backends/events/gph/gph-events.cpp:390 +#: backends/events/gph/gph-events.cpp:433 +#: backends/events/openpandora/op-events.cpp:172 msgid "Touchscreen 'Tap Mode' - Hover (No Click)" msgstr "ÀÕÖØÜ 'ÚÐáÐÝØÙ' âÐçáÚàØÝÐ - ¿àÞÛñâ (ÑÕ× ÚÛØÚÐ)" -#: backends/events/gph/gph-events.cpp:362 +#: backends/events/gph/gph-events.cpp:410 msgid "Maximum Volume" msgstr "¼ÐÚáØÜÐÛìÝÐï ÓàÞÜÚÞáâì" -#: backends/events/gph/gph-events.cpp:364 +#: backends/events/gph/gph-events.cpp:412 msgid "Increasing Volume" msgstr "ÃÒÕÛØçÕÝØÕ ÓàÞÜÚÞáâØ" -#: backends/events/gph/gph-events.cpp:370 +#: backends/events/gph/gph-events.cpp:418 msgid "Minimal Volume" msgstr "¼ØÝØÜÐÛìÝÐï ÓàÞÜÚÞáâì" -#: backends/events/gph/gph-events.cpp:372 +#: backends/events/gph/gph-events.cpp:420 msgid "Decreasing Volume" msgstr "ÃÜÕÝìèÕÝØÕ ÓàÞÜÚÞáâØ" -#: backends/updates/macosx/macosx-updates.mm:65 +#: backends/events/openpandora/op-events.cpp:174 +#, fuzzy +msgid "Touchscreen 'Tap Mode' - Hover (DPad Clicks)" +msgstr "ÀÕÖØÜ 'ÚÐáÐÝØÙ' âÐçáÚàØÝÐ - ¿àÞÛñâ (ÑÕ× ÚÛØÚÐ)" + +#: backends/updates/macosx/macosx-updates.mm:67 msgid "Check for Updates..." msgstr "¿àÞÒÕàïî ÞÑÝÞÒÛÕÝØï..." diff --git a/po/scummvm.pot b/po/scummvm.pot index 8a68e4372b..fb56e89dce 100644 --- a/po/scummvm.pot +++ b/po/scummvm.pot @@ -6,12 +6,13 @@ #, fuzzy msgid "" msgstr "" -"Project-Id-Version: ScummVM 1.5.0git\n" +"Project-Id-Version: ScummVM 1.6.0git\n" "Report-Msgid-Bugs-To: scummvm-devel@lists.sf.net\n" -"POT-Creation-Date: 2012-07-08 12:25+0100\n" +"POT-Creation-Date: 2012-12-01 17:22+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" +"Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" @@ -44,10 +45,11 @@ msgstr "" #: gui/browser.cpp:69 gui/chooser.cpp:45 gui/KeysDialog.cpp:43 #: gui/launcher.cpp:345 gui/massadd.cpp:94 gui/options.cpp:1228 -#: gui/saveload.cpp:64 gui/saveload.cpp:173 gui/themebrowser.cpp:54 -#: engines/engine.cpp:442 engines/scumm/dialogs.cpp:190 -#: engines/sword1/control.cpp:865 engines/parallaction/saveload.cpp:274 -#: backends/platform/wii/options.cpp:48 +#: gui/saveload-dialog.cpp:215 gui/saveload-dialog.cpp:275 +#: gui/saveload-dialog.cpp:545 gui/saveload-dialog.cpp:919 +#: gui/themebrowser.cpp:54 engines/engine.cpp:442 +#: engines/scumm/dialogs.cpp:190 engines/sword1/control.cpp:867 +#: engines/parallaction/saveload.cpp:274 backends/platform/wii/options.cpp:48 #: backends/events/default/default-events.cpp:191 #: backends/events/default/default-events.cpp:213 msgid "Cancel" @@ -68,15 +70,15 @@ msgstr "" msgid "Mouse click" msgstr "" -#: gui/gui-manager.cpp:122 base/main.cpp:300 +#: gui/gui-manager.cpp:122 base/main.cpp:301 msgid "Display keyboard" msgstr "" -#: gui/gui-manager.cpp:126 base/main.cpp:304 +#: gui/gui-manager.cpp:126 base/main.cpp:305 msgid "Remap keys" msgstr "" -#: gui/gui-manager.cpp:129 base/main.cpp:307 +#: gui/gui-manager.cpp:129 base/main.cpp:308 msgid "Toggle FullScreen" msgstr "" @@ -90,16 +92,16 @@ msgstr "" #: gui/KeysDialog.cpp:42 gui/launcher.cpp:346 gui/launcher.cpp:1001 #: gui/launcher.cpp:1005 gui/massadd.cpp:91 gui/options.cpp:1229 -#: engines/engine.cpp:361 engines/engine.cpp:372 engines/scumm/dialogs.cpp:192 -#: engines/scumm/scumm.cpp:1775 engines/agos/animation.cpp:561 -#: engines/groovie/script.cpp:420 engines/sky/compact.cpp:131 -#: engines/sky/compact.cpp:141 engines/sword1/animation.cpp:539 -#: engines/sword1/animation.cpp:560 engines/sword1/animation.cpp:570 -#: engines/sword1/animation.cpp:577 engines/sword1/control.cpp:865 -#: engines/sword1/logic.cpp:1633 engines/sword2/animation.cpp:435 -#: engines/sword2/animation.cpp:455 engines/sword2/animation.cpp:465 -#: engines/sword2/animation.cpp:474 engines/parallaction/saveload.cpp:274 -#: backends/platform/wii/options.cpp:47 +#: gui/saveload-dialog.cpp:920 engines/engine.cpp:361 engines/engine.cpp:372 +#: engines/scumm/dialogs.cpp:192 engines/scumm/scumm.cpp:1776 +#: engines/agos/animation.cpp:558 engines/groovie/script.cpp:420 +#: engines/sky/compact.cpp:131 engines/sky/compact.cpp:141 +#: engines/sword1/animation.cpp:519 engines/sword1/animation.cpp:540 +#: engines/sword1/animation.cpp:550 engines/sword1/animation.cpp:557 +#: engines/sword1/control.cpp:867 engines/sword1/logic.cpp:1633 +#: engines/sword2/animation.cpp:419 engines/sword2/animation.cpp:439 +#: engines/sword2/animation.cpp:449 engines/sword2/animation.cpp:458 +#: engines/parallaction/saveload.cpp:274 backends/platform/wii/options.cpp:47 #: backends/platform/wince/CELauncherDialog.cpp:54 msgid "OK" msgstr "" @@ -350,7 +352,7 @@ msgstr "" msgid "~Q~uit" msgstr "" -#: gui/launcher.cpp:621 backends/platform/sdl/macosx/appmenu_osx.mm:96 +#: gui/launcher.cpp:621 backends/platform/sdl/macosx/appmenu_osx.mm:95 msgid "Quit ScummVM" msgstr "" @@ -358,7 +360,7 @@ msgstr "" msgid "A~b~out..." msgstr "" -#: gui/launcher.cpp:622 backends/platform/sdl/macosx/appmenu_osx.mm:70 +#: gui/launcher.cpp:622 backends/platform/sdl/macosx/appmenu_osx.mm:69 msgid "About ScummVM" msgstr "" @@ -433,13 +435,13 @@ msgstr "" msgid "Search:" msgstr "" -#: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/mohawk/myst.cpp:255 +#: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/mohawk/myst.cpp:245 #: engines/mohawk/riven.cpp:716 engines/cruise/menu.cpp:214 msgid "Load game:" msgstr "" #: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/scumm/dialogs.cpp:188 -#: engines/mohawk/myst.cpp:255 engines/mohawk/riven.cpp:716 +#: engines/mohawk/myst.cpp:245 engines/mohawk/riven.cpp:716 #: engines/cruise/menu.cpp:214 backends/platform/wince/CEActionsPocket.cpp:267 #: backends/platform/wince/CEActionsSmartphone.cpp:231 msgid "Load" @@ -903,68 +905,101 @@ msgid "" "to use this theme you need to switch to another language first." msgstr "" -#: gui/saveload.cpp:59 gui/saveload.cpp:257 +#: gui/saveload-dialog.cpp:166 +msgid "List view" +msgstr "" + +#: gui/saveload-dialog.cpp:167 +msgid "Grid view" +msgstr "" + +#: gui/saveload-dialog.cpp:210 gui/saveload-dialog.cpp:358 msgid "No date saved" msgstr "" -#: gui/saveload.cpp:60 gui/saveload.cpp:258 +#: gui/saveload-dialog.cpp:211 gui/saveload-dialog.cpp:359 msgid "No time saved" msgstr "" -#: gui/saveload.cpp:61 gui/saveload.cpp:259 +#: gui/saveload-dialog.cpp:212 gui/saveload-dialog.cpp:360 msgid "No playtime saved" msgstr "" -#: gui/saveload.cpp:68 gui/saveload.cpp:173 +#: gui/saveload-dialog.cpp:219 gui/saveload-dialog.cpp:275 msgid "Delete" msgstr "" -#: gui/saveload.cpp:172 +#: gui/saveload-dialog.cpp:274 msgid "Do you really want to delete this savegame?" msgstr "" -#: gui/saveload.cpp:282 +#: gui/saveload-dialog.cpp:383 gui/saveload-dialog.cpp:872 msgid "Date: " msgstr "" -#: gui/saveload.cpp:286 +#: gui/saveload-dialog.cpp:387 gui/saveload-dialog.cpp:878 msgid "Time: " msgstr "" -#: gui/saveload.cpp:292 +#: gui/saveload-dialog.cpp:393 gui/saveload-dialog.cpp:886 msgid "Playtime: " msgstr "" -#: gui/saveload.cpp:305 gui/saveload.cpp:372 +#: gui/saveload-dialog.cpp:406 gui/saveload-dialog.cpp:494 msgid "Untitled savestate" msgstr "" +#: gui/saveload-dialog.cpp:546 +msgid "Next" +msgstr "" + +#: gui/saveload-dialog.cpp:549 +msgid "Prev" +msgstr "" + +#: gui/saveload-dialog.cpp:736 +msgid "New Save" +msgstr "" + +#: gui/saveload-dialog.cpp:736 +msgid "Create a new save game" +msgstr "" + +#: gui/saveload-dialog.cpp:865 +msgid "Name: " +msgstr "" + +#: gui/saveload-dialog.cpp:937 +#, c-format +msgid "Enter a description for slot %d:" +msgstr "" + #: gui/themebrowser.cpp:44 msgid "Select a Theme" msgstr "" -#: gui/ThemeEngine.cpp:335 +#: gui/ThemeEngine.cpp:337 msgid "Disabled GFX" msgstr "" -#: gui/ThemeEngine.cpp:335 +#: gui/ThemeEngine.cpp:337 msgctxt "lowres" msgid "Disabled GFX" msgstr "" -#: gui/ThemeEngine.cpp:336 +#: gui/ThemeEngine.cpp:338 msgid "Standard Renderer (16bpp)" msgstr "" -#: gui/ThemeEngine.cpp:336 +#: gui/ThemeEngine.cpp:338 msgid "Standard (16bpp)" msgstr "" -#: gui/ThemeEngine.cpp:338 +#: gui/ThemeEngine.cpp:340 msgid "Antialiased Renderer (16bpp)" msgstr "" -#: gui/ThemeEngine.cpp:338 +#: gui/ThemeEngine.cpp:340 msgid "Antialiased (16bpp)" msgstr "" @@ -972,35 +1007,35 @@ msgstr "" msgid "Clear value" msgstr "" -#: base/main.cpp:209 +#: base/main.cpp:210 #, c-format msgid "Engine does not support debug level '%s'" msgstr "" -#: base/main.cpp:287 +#: base/main.cpp:288 msgid "Menu" msgstr "" -#: base/main.cpp:290 backends/platform/symbian/src/SymbianActions.cpp:45 +#: base/main.cpp:291 backends/platform/symbian/src/SymbianActions.cpp:45 #: backends/platform/wince/CEActionsPocket.cpp:45 #: backends/platform/wince/CEActionsSmartphone.cpp:46 msgid "Skip" msgstr "" -#: base/main.cpp:293 backends/platform/symbian/src/SymbianActions.cpp:50 +#: base/main.cpp:294 backends/platform/symbian/src/SymbianActions.cpp:50 #: backends/platform/wince/CEActionsPocket.cpp:42 msgid "Pause" msgstr "" -#: base/main.cpp:296 +#: base/main.cpp:297 msgid "Skip line" msgstr "" -#: base/main.cpp:467 +#: base/main.cpp:468 msgid "Error running game:" msgstr "" -#: base/main.cpp:491 +#: base/main.cpp:492 msgid "Could not find any engine capable of running the selected game" msgstr "" @@ -1068,16 +1103,16 @@ msgstr "" msgid "Unknown error" msgstr "" -#: engines/advancedDetector.cpp:324 +#: engines/advancedDetector.cpp:316 #, c-format msgid "The game in '%s' seems to be unknown." msgstr "" -#: engines/advancedDetector.cpp:325 +#: engines/advancedDetector.cpp:317 msgid "Please, report the following data to the ScummVM team along with name" msgstr "" -#: engines/advancedDetector.cpp:327 +#: engines/advancedDetector.cpp:319 msgid "of the game you tried to add and its version/language/etc.:" msgstr "" @@ -1115,13 +1150,13 @@ msgid "~R~eturn to Launcher" msgstr "" #: engines/dialogs.cpp:115 engines/agi/saveload.cpp:803 -#: engines/cruise/menu.cpp:212 engines/sci/engine/kfile.cpp:735 +#: engines/cruise/menu.cpp:212 engines/sci/engine/kfile.cpp:742 msgid "Save game:" msgstr "" #: engines/dialogs.cpp:115 engines/agi/saveload.cpp:803 #: engines/scumm/dialogs.cpp:187 engines/cruise/menu.cpp:212 -#: engines/sci/engine/kfile.cpp:735 +#: engines/sci/engine/kfile.cpp:742 #: backends/platform/symbian/src/SymbianActions.cpp:44 #: backends/platform/wince/CEActionsPocket.cpp:43 #: backends/platform/wince/CEActionsPocket.cpp:267 @@ -1210,21 +1245,21 @@ msgstr "" msgid "Start anyway" msgstr "" -#: engines/agi/detection.cpp:145 engines/dreamweb/detection.cpp:47 -#: engines/sci/detection.cpp:390 +#: engines/agi/detection.cpp:142 engines/dreamweb/detection.cpp:47 +#: engines/sci/detection.cpp:393 msgid "Use original save/load screens" msgstr "" -#: engines/agi/detection.cpp:146 engines/dreamweb/detection.cpp:48 -#: engines/sci/detection.cpp:391 +#: engines/agi/detection.cpp:143 engines/dreamweb/detection.cpp:48 +#: engines/sci/detection.cpp:394 msgid "Use the original save/load screens, instead of the ScummVM ones" msgstr "" -#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:831 +#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:838 msgid "Restore game:" msgstr "" -#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:831 +#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:838 msgid "Restore" msgstr "" @@ -1236,54 +1271,54 @@ msgstr "" msgid "Display graphics using the game's bright palette" msgstr "" -#: engines/sci/detection.cpp:370 +#: engines/sci/detection.cpp:373 msgid "EGA undithering" msgstr "" -#: engines/sci/detection.cpp:371 +#: engines/sci/detection.cpp:374 msgid "Enable undithering in EGA games" msgstr "" -#: engines/sci/detection.cpp:380 +#: engines/sci/detection.cpp:383 msgid "Prefer digital sound effects" msgstr "" -#: engines/sci/detection.cpp:381 +#: engines/sci/detection.cpp:384 msgid "Prefer digital sound effects instead of synthesized ones" msgstr "" -#: engines/sci/detection.cpp:400 +#: engines/sci/detection.cpp:403 msgid "Use IMF/Yamaha FB-01 for MIDI output" msgstr "" -#: engines/sci/detection.cpp:401 +#: engines/sci/detection.cpp:404 msgid "" "Use an IBM Music Feature card or a Yamaha FB-01 FM synth module for MIDI " "output" msgstr "" -#: engines/sci/detection.cpp:411 +#: engines/sci/detection.cpp:414 msgid "Use CD audio" msgstr "" -#: engines/sci/detection.cpp:412 +#: engines/sci/detection.cpp:415 msgid "Use CD audio instead of in-game audio, if available" msgstr "" -#: engines/sci/detection.cpp:422 +#: engines/sci/detection.cpp:425 msgid "Use Windows cursors" msgstr "" -#: engines/sci/detection.cpp:423 +#: engines/sci/detection.cpp:426 msgid "" "Use the Windows cursors (smaller and monochrome) instead of the DOS ones" msgstr "" -#: engines/sci/detection.cpp:433 +#: engines/sci/detection.cpp:436 msgid "Use silver cursors" msgstr "" -#: engines/sci/detection.cpp:434 +#: engines/sci/detection.cpp:437 msgid "" "Use the alternate set of silver cursors, instead of the normal golden ones" msgstr "" @@ -1935,14 +1970,14 @@ msgstr "" msgid "Fly to lower right" msgstr "" -#: engines/scumm/scumm.cpp:1773 +#: engines/scumm/scumm.cpp:1774 #, c-format msgid "" "Native MIDI support requires the Roland Upgrade from LucasArts,\n" "but %s is missing. Using AdLib instead." msgstr "" -#: engines/scumm/scumm.cpp:2278 engines/agos/saveload.cpp:202 +#: engines/scumm/scumm.cpp:2295 engines/agos/saveload.cpp:220 #, c-format msgid "" "Failed to save game state to file:\n" @@ -1950,7 +1985,7 @@ msgid "" "%s" msgstr "" -#: engines/scumm/scumm.cpp:2285 engines/agos/saveload.cpp:167 +#: engines/scumm/scumm.cpp:2302 engines/agos/saveload.cpp:185 #, c-format msgid "" "Failed to load game state from file:\n" @@ -1958,7 +1993,7 @@ msgid "" "%s" msgstr "" -#: engines/scumm/scumm.cpp:2297 engines/agos/saveload.cpp:210 +#: engines/scumm/scumm.cpp:2314 engines/agos/saveload.cpp:228 #, c-format msgid "" "Successfully saved game state in file:\n" @@ -1966,7 +2001,7 @@ msgid "" "%s" msgstr "" -#: engines/scumm/scumm.cpp:2512 +#: engines/scumm/scumm.cpp:2529 msgid "" "Usually, Maniac Mansion would start now. But ScummVM doesn't do that yet. To " "play it, go to 'Add Game' in the ScummVM start menu and select the 'Maniac' " @@ -1999,17 +2034,17 @@ msgstr "" msgid "~W~ater Effect Enabled" msgstr "" -#: engines/agos/animation.cpp:560 +#: engines/agos/animation.cpp:557 #, c-format msgid "Cutscene file '%s' not found!" msgstr "" #: engines/gob/inter_playtoons.cpp:256 engines/gob/inter_v2.cpp:1287 -#: engines/tinsel/saveload.cpp:502 +#: engines/tinsel/saveload.cpp:532 msgid "Failed to load game state from file." msgstr "" -#: engines/gob/inter_v2.cpp:1357 engines/tinsel/saveload.cpp:515 +#: engines/gob/inter_v2.cpp:1357 engines/tinsel/saveload.cpp:545 msgid "Failed to save game state to file." msgstr "" @@ -2127,7 +2162,7 @@ msgstr "" msgid "Choose Spell" msgstr "" -#: engines/kyra/sound_midi.cpp:475 +#: engines/kyra/sound_midi.cpp:477 msgid "" "You appear to be using a General MIDI device,\n" "but your game only supports Roland MT32 MIDI.\n" @@ -2136,12 +2171,12 @@ msgid "" "some tracks sound incorrect." msgstr "" -#: engines/queen/queen.cpp:59 engines/sky/detection.cpp:44 -msgid "Floppy intro" +#: engines/queen/queen.cpp:59 +msgid "Alternative intro" msgstr "" -#: engines/queen/queen.cpp:60 engines/sky/detection.cpp:45 -msgid "Use the floppy version's intro (CD version only)" +#: engines/queen/queen.cpp:60 +msgid "Use an alternative game intro (CD version only)" msgstr "" #: engines/sky/compact.cpp:130 @@ -2156,25 +2191,33 @@ msgid "" "Please (re)download it from www.scummvm.org" msgstr "" -#: engines/sword1/animation.cpp:539 +#: engines/sky/detection.cpp:44 +msgid "Floppy intro" +msgstr "" + +#: engines/sky/detection.cpp:45 +msgid "Use the floppy version's intro (CD version only)" +msgstr "" + +#: engines/sword1/animation.cpp:519 #, c-format msgid "PSX stream cutscene '%s' cannot be played in paletted mode" msgstr "" -#: engines/sword1/animation.cpp:560 engines/sword2/animation.cpp:455 +#: engines/sword1/animation.cpp:540 engines/sword2/animation.cpp:439 msgid "DXA cutscenes found but ScummVM has been built without zlib support" msgstr "" -#: engines/sword1/animation.cpp:570 engines/sword2/animation.cpp:465 +#: engines/sword1/animation.cpp:550 engines/sword2/animation.cpp:449 msgid "MPEG2 cutscenes are no longer supported" msgstr "" -#: engines/sword1/animation.cpp:576 engines/sword2/animation.cpp:473 +#: engines/sword1/animation.cpp:556 engines/sword2/animation.cpp:457 #, c-format msgid "Cutscene '%s' not found" msgstr "" -#: engines/sword1/control.cpp:863 +#: engines/sword1/control.cpp:865 msgid "" "ScummVM found that you have old savefiles for Broken Sword 1 that should be " "converted.\n" @@ -2185,18 +2228,18 @@ msgid "" "time you start the game.\n" msgstr "" -#: engines/sword1/control.cpp:1232 +#: engines/sword1/control.cpp:1234 #, c-format msgid "" "Target new save game already exists!\n" "Would you like to keep the old save game (%s) or the new one (%s)?\n" msgstr "" -#: engines/sword1/control.cpp:1235 +#: engines/sword1/control.cpp:1237 msgid "Keep the old one" msgstr "" -#: engines/sword1/control.cpp:1235 +#: engines/sword1/control.cpp:1237 msgid "Keep the new one" msgstr "" @@ -2204,7 +2247,7 @@ msgstr "" msgid "This is the end of the Broken Sword 1 Demo" msgstr "" -#: engines/sword2/animation.cpp:435 +#: engines/sword2/animation.cpp:419 msgid "" "PSX cutscenes found but ScummVM has been built without RGB color support" msgstr "" @@ -2217,6 +2260,17 @@ msgstr "" msgid "Show labels for objects on mouse hover" msgstr "" +#: engines/teenagent/resources.cpp:94 +msgid "" +"You're missing the 'teenagent.dat' file. Get it from the ScummVM website" +msgstr "" + +#: engines/teenagent/resources.cpp:115 +msgid "" +"The teenagent.dat file is compressed and zlib hasn't been included in this " +"executable. Please decompress it" +msgstr "" + #: engines/parallaction/saveload.cpp:133 #, c-format msgid "" @@ -2303,7 +2357,7 @@ msgstr "" msgid "Amiga Audio Emulator" msgstr "" -#: audio/softsynth/adlib.cpp:1593 +#: audio/softsynth/adlib.cpp:2284 msgid "AdLib Emulator" msgstr "" @@ -2447,11 +2501,11 @@ msgstr "" msgid "Touchpad mode disabled." msgstr "" -#: backends/platform/maemo/maemo.cpp:205 +#: backends/platform/maemo/maemo.cpp:209 msgid "Click Mode" msgstr "" -#: backends/platform/maemo/maemo.cpp:211 +#: backends/platform/maemo/maemo.cpp:215 #: backends/platform/symbian/src/SymbianActions.cpp:42 #: backends/platform/wince/CEActionsPocket.cpp:60 #: backends/platform/wince/CEActionsSmartphone.cpp:43 @@ -2459,35 +2513,35 @@ msgstr "" msgid "Left Click" msgstr "" -#: backends/platform/maemo/maemo.cpp:214 +#: backends/platform/maemo/maemo.cpp:218 msgid "Middle Click" msgstr "" -#: backends/platform/maemo/maemo.cpp:217 +#: backends/platform/maemo/maemo.cpp:221 #: backends/platform/symbian/src/SymbianActions.cpp:43 #: backends/platform/wince/CEActionsSmartphone.cpp:44 #: backends/platform/bada/form.cpp:273 msgid "Right Click" msgstr "" -#: backends/platform/sdl/macosx/appmenu_osx.mm:78 +#: backends/platform/sdl/macosx/appmenu_osx.mm:77 msgid "Hide ScummVM" msgstr "" -#: backends/platform/sdl/macosx/appmenu_osx.mm:83 +#: backends/platform/sdl/macosx/appmenu_osx.mm:82 msgid "Hide Others" msgstr "" -#: backends/platform/sdl/macosx/appmenu_osx.mm:88 +#: backends/platform/sdl/macosx/appmenu_osx.mm:87 msgid "Show All" msgstr "" -#: backends/platform/sdl/macosx/appmenu_osx.mm:110 -#: backends/platform/sdl/macosx/appmenu_osx.mm:121 +#: backends/platform/sdl/macosx/appmenu_osx.mm:109 +#: backends/platform/sdl/macosx/appmenu_osx.mm:120 msgid "Window" msgstr "" -#: backends/platform/sdl/macosx/appmenu_osx.mm:115 +#: backends/platform/sdl/macosx/appmenu_osx.mm:114 msgid "Minimize" msgstr "" @@ -2863,41 +2917,45 @@ msgstr "" msgid "Do you really want to quit?" msgstr "" -#: backends/events/gph/gph-events.cpp:338 -#: backends/events/gph/gph-events.cpp:381 -#: backends/events/openpandora/op-events.cpp:139 +#: backends/events/gph/gph-events.cpp:386 +#: backends/events/gph/gph-events.cpp:429 +#: backends/events/openpandora/op-events.cpp:168 msgid "Touchscreen 'Tap Mode' - Left Click" msgstr "" -#: backends/events/gph/gph-events.cpp:340 -#: backends/events/gph/gph-events.cpp:383 -#: backends/events/openpandora/op-events.cpp:141 +#: backends/events/gph/gph-events.cpp:388 +#: backends/events/gph/gph-events.cpp:431 +#: backends/events/openpandora/op-events.cpp:170 msgid "Touchscreen 'Tap Mode' - Right Click" msgstr "" -#: backends/events/gph/gph-events.cpp:342 -#: backends/events/gph/gph-events.cpp:385 -#: backends/events/openpandora/op-events.cpp:143 +#: backends/events/gph/gph-events.cpp:390 +#: backends/events/gph/gph-events.cpp:433 +#: backends/events/openpandora/op-events.cpp:172 msgid "Touchscreen 'Tap Mode' - Hover (No Click)" msgstr "" -#: backends/events/gph/gph-events.cpp:362 +#: backends/events/gph/gph-events.cpp:410 msgid "Maximum Volume" msgstr "" -#: backends/events/gph/gph-events.cpp:364 +#: backends/events/gph/gph-events.cpp:412 msgid "Increasing Volume" msgstr "" -#: backends/events/gph/gph-events.cpp:370 +#: backends/events/gph/gph-events.cpp:418 msgid "Minimal Volume" msgstr "" -#: backends/events/gph/gph-events.cpp:372 +#: backends/events/gph/gph-events.cpp:420 msgid "Decreasing Volume" msgstr "" -#: backends/updates/macosx/macosx-updates.mm:65 +#: backends/events/openpandora/op-events.cpp:174 +msgid "Touchscreen 'Tap Mode' - Hover (DPad Clicks)" +msgstr "" + +#: backends/updates/macosx/macosx-updates.mm:67 msgid "Check for Updates..." msgstr "" diff --git a/po/se_SE.po b/po/se_SE.po index 8388e55370..1eb6ffa4a3 100644 --- a/po/se_SE.po +++ b/po/se_SE.po @@ -1,5 +1,5 @@ # Swedish translation for ScummVM. -# Copyright (C) 2011-2012 ScummVM Team +# Copyright (C) 2011-2013 ScummVM Team # This file is distributed under the same license as the ScummVM package. # Hampus Flink <hampus.flink@gmail.com>, 2011. # @@ -7,14 +7,14 @@ msgid "" msgstr "" "Project-Id-Version: ScummVM 1.5.0svn\n" "Report-Msgid-Bugs-To: scummvm-devel@lists.sf.net\n" -"POT-Creation-Date: 2012-07-08 12:25+0100\n" +"POT-Creation-Date: 2012-12-01 17:22+0000\n" "PO-Revision-Date: 2012-07-08 18:03+0100\n" "Last-Translator: Hampus Flink <hampus.flink@gmail.com>\n" "Language-Team: \n" +"Language: Svenska\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=iso-8859-1\n" "Content-Transfer-Encoding: 8bit\n" -"Language: Svenska\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" "X-Poedit-Language: Swedish\n" "X-Poedit-Country: SWEDEN\n" @@ -48,10 +48,11 @@ msgstr "Uppåt" #: gui/browser.cpp:69 gui/chooser.cpp:45 gui/KeysDialog.cpp:43 #: gui/launcher.cpp:345 gui/massadd.cpp:94 gui/options.cpp:1228 -#: gui/saveload.cpp:64 gui/saveload.cpp:173 gui/themebrowser.cpp:54 -#: engines/engine.cpp:442 engines/scumm/dialogs.cpp:190 -#: engines/sword1/control.cpp:865 engines/parallaction/saveload.cpp:274 -#: backends/platform/wii/options.cpp:48 +#: gui/saveload-dialog.cpp:215 gui/saveload-dialog.cpp:275 +#: gui/saveload-dialog.cpp:545 gui/saveload-dialog.cpp:919 +#: gui/themebrowser.cpp:54 engines/engine.cpp:442 +#: engines/scumm/dialogs.cpp:190 engines/sword1/control.cpp:867 +#: engines/parallaction/saveload.cpp:274 backends/platform/wii/options.cpp:48 #: backends/events/default/default-events.cpp:191 #: backends/events/default/default-events.cpp:213 msgid "Cancel" @@ -72,15 +73,15 @@ msgstr "Stäng" msgid "Mouse click" msgstr "Musklick" -#: gui/gui-manager.cpp:122 base/main.cpp:300 +#: gui/gui-manager.cpp:122 base/main.cpp:301 msgid "Display keyboard" msgstr "Visa tangentbord" -#: gui/gui-manager.cpp:126 base/main.cpp:304 +#: gui/gui-manager.cpp:126 base/main.cpp:305 msgid "Remap keys" msgstr "Ställ in tangenter" -#: gui/gui-manager.cpp:129 base/main.cpp:307 +#: gui/gui-manager.cpp:129 base/main.cpp:308 msgid "Toggle FullScreen" msgstr "Fullskärmsläge" @@ -94,16 +95,16 @@ msgstr "Ställ in" #: gui/KeysDialog.cpp:42 gui/launcher.cpp:346 gui/launcher.cpp:1001 #: gui/launcher.cpp:1005 gui/massadd.cpp:91 gui/options.cpp:1229 -#: engines/engine.cpp:361 engines/engine.cpp:372 engines/scumm/dialogs.cpp:192 -#: engines/scumm/scumm.cpp:1775 engines/agos/animation.cpp:561 -#: engines/groovie/script.cpp:420 engines/sky/compact.cpp:131 -#: engines/sky/compact.cpp:141 engines/sword1/animation.cpp:539 -#: engines/sword1/animation.cpp:560 engines/sword1/animation.cpp:570 -#: engines/sword1/animation.cpp:577 engines/sword1/control.cpp:865 -#: engines/sword1/logic.cpp:1633 engines/sword2/animation.cpp:435 -#: engines/sword2/animation.cpp:455 engines/sword2/animation.cpp:465 -#: engines/sword2/animation.cpp:474 engines/parallaction/saveload.cpp:274 -#: backends/platform/wii/options.cpp:47 +#: gui/saveload-dialog.cpp:920 engines/engine.cpp:361 engines/engine.cpp:372 +#: engines/scumm/dialogs.cpp:192 engines/scumm/scumm.cpp:1776 +#: engines/agos/animation.cpp:558 engines/groovie/script.cpp:420 +#: engines/sky/compact.cpp:131 engines/sky/compact.cpp:141 +#: engines/sword1/animation.cpp:519 engines/sword1/animation.cpp:540 +#: engines/sword1/animation.cpp:550 engines/sword1/animation.cpp:557 +#: engines/sword1/control.cpp:867 engines/sword1/logic.cpp:1633 +#: engines/sword2/animation.cpp:419 engines/sword2/animation.cpp:439 +#: engines/sword2/animation.cpp:449 engines/sword2/animation.cpp:458 +#: engines/parallaction/saveload.cpp:274 backends/platform/wii/options.cpp:47 #: backends/platform/wince/CELauncherDialog.cpp:54 msgid "OK" msgstr "OK" @@ -358,7 +359,7 @@ msgstr "Detta ID-namn är upptaget. Var god välj ett annat." msgid "~Q~uit" msgstr "~A~vsluta" -#: gui/launcher.cpp:621 backends/platform/sdl/macosx/appmenu_osx.mm:96 +#: gui/launcher.cpp:621 backends/platform/sdl/macosx/appmenu_osx.mm:95 msgid "Quit ScummVM" msgstr "Avsluta ScummVM" @@ -366,7 +367,7 @@ msgstr "Avsluta ScummVM" msgid "A~b~out..." msgstr "O~m~..." -#: gui/launcher.cpp:622 backends/platform/sdl/macosx/appmenu_osx.mm:70 +#: gui/launcher.cpp:622 backends/platform/sdl/macosx/appmenu_osx.mm:69 msgid "About ScummVM" msgstr "Om ScummVM" @@ -441,13 +442,13 @@ msgstr "Sök i spellistan" msgid "Search:" msgstr "Sök:" -#: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/mohawk/myst.cpp:255 +#: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/mohawk/myst.cpp:245 #: engines/mohawk/riven.cpp:716 engines/cruise/menu.cpp:214 msgid "Load game:" msgstr "Ladda spel:" #: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/scumm/dialogs.cpp:188 -#: engines/mohawk/myst.cpp:255 engines/mohawk/riven.cpp:716 +#: engines/mohawk/myst.cpp:245 engines/mohawk/riven.cpp:716 #: engines/cruise/menu.cpp:214 backends/platform/wince/CEActionsPocket.cpp:267 #: backends/platform/wince/CEActionsSmartphone.cpp:231 msgid "Load" @@ -924,68 +925,104 @@ msgstr "" "Temat du valde stöder inte ditt språk. Om du vill använda det här temat " "måste först byta till ett annat språk." -#: gui/saveload.cpp:59 gui/saveload.cpp:257 +#: gui/saveload-dialog.cpp:166 +msgid "List view" +msgstr "" + +#: gui/saveload-dialog.cpp:167 +msgid "Grid view" +msgstr "" + +#: gui/saveload-dialog.cpp:210 gui/saveload-dialog.cpp:358 msgid "No date saved" msgstr "Inget datum sparat" -#: gui/saveload.cpp:60 gui/saveload.cpp:258 +#: gui/saveload-dialog.cpp:211 gui/saveload-dialog.cpp:359 msgid "No time saved" msgstr "Ingen tid sparad" -#: gui/saveload.cpp:61 gui/saveload.cpp:259 +#: gui/saveload-dialog.cpp:212 gui/saveload-dialog.cpp:360 msgid "No playtime saved" msgstr "Ingen speltid sparad" -#: gui/saveload.cpp:68 gui/saveload.cpp:173 +#: gui/saveload-dialog.cpp:219 gui/saveload-dialog.cpp:275 msgid "Delete" msgstr "Radera" -#: gui/saveload.cpp:172 +#: gui/saveload-dialog.cpp:274 msgid "Do you really want to delete this savegame?" msgstr "Vill du verkligen radera den här spardatan?" -#: gui/saveload.cpp:282 +#: gui/saveload-dialog.cpp:383 gui/saveload-dialog.cpp:872 msgid "Date: " msgstr "Datum:" -#: gui/saveload.cpp:286 +#: gui/saveload-dialog.cpp:387 gui/saveload-dialog.cpp:878 msgid "Time: " msgstr "Tid:" -#: gui/saveload.cpp:292 +#: gui/saveload-dialog.cpp:393 gui/saveload-dialog.cpp:886 msgid "Playtime: " msgstr "Speltid:" -#: gui/saveload.cpp:305 gui/saveload.cpp:372 +#: gui/saveload-dialog.cpp:406 gui/saveload-dialog.cpp:494 msgid "Untitled savestate" msgstr "Namnlös spardata" +#: gui/saveload-dialog.cpp:546 +msgid "Next" +msgstr "" + +#: gui/saveload-dialog.cpp:549 +msgid "Prev" +msgstr "" + +#: gui/saveload-dialog.cpp:736 +#, fuzzy +msgid "New Save" +msgstr "Spara" + +#: gui/saveload-dialog.cpp:736 +#, fuzzy +msgid "Create a new save game" +msgstr "Kunde inte spara spelet." + +#: gui/saveload-dialog.cpp:865 +#, fuzzy +msgid "Name: " +msgstr "Namn:" + +#: gui/saveload-dialog.cpp:937 +#, c-format +msgid "Enter a description for slot %d:" +msgstr "" + #: gui/themebrowser.cpp:44 msgid "Select a Theme" msgstr "Välj ett tema" -#: gui/ThemeEngine.cpp:335 +#: gui/ThemeEngine.cpp:337 msgid "Disabled GFX" msgstr "Inaktiverad GFX" -#: gui/ThemeEngine.cpp:335 +#: gui/ThemeEngine.cpp:337 msgctxt "lowres" msgid "Disabled GFX" msgstr "Inaktiverad GFX" -#: gui/ThemeEngine.cpp:336 +#: gui/ThemeEngine.cpp:338 msgid "Standard Renderer (16bpp)" msgstr "Standard rendering (16 bpp)" -#: gui/ThemeEngine.cpp:336 +#: gui/ThemeEngine.cpp:338 msgid "Standard (16bpp)" msgstr "Standard (16 bpp)" -#: gui/ThemeEngine.cpp:338 +#: gui/ThemeEngine.cpp:340 msgid "Antialiased Renderer (16bpp)" msgstr "Antialiserad rendering (16 bpp)" -#: gui/ThemeEngine.cpp:338 +#: gui/ThemeEngine.cpp:340 msgid "Antialiased (16bpp)" msgstr "Antialiserad (16 bpp)" @@ -993,35 +1030,35 @@ msgstr "Antialiserad (16 bpp)" msgid "Clear value" msgstr "Töm sökfältet" -#: base/main.cpp:209 +#: base/main.cpp:210 #, c-format msgid "Engine does not support debug level '%s'" msgstr "Motorn stöder inte debug-nivå '%s'" -#: base/main.cpp:287 +#: base/main.cpp:288 msgid "Menu" msgstr "Meny" -#: base/main.cpp:290 backends/platform/symbian/src/SymbianActions.cpp:45 +#: base/main.cpp:291 backends/platform/symbian/src/SymbianActions.cpp:45 #: backends/platform/wince/CEActionsPocket.cpp:45 #: backends/platform/wince/CEActionsSmartphone.cpp:46 msgid "Skip" msgstr "Skippa" -#: base/main.cpp:293 backends/platform/symbian/src/SymbianActions.cpp:50 +#: base/main.cpp:294 backends/platform/symbian/src/SymbianActions.cpp:50 #: backends/platform/wince/CEActionsPocket.cpp:42 msgid "Pause" msgstr "Paus" -#: base/main.cpp:296 +#: base/main.cpp:297 msgid "Skip line" msgstr "Skippa rad" -#: base/main.cpp:467 +#: base/main.cpp:468 msgid "Error running game:" msgstr "Fel under körning av spel:" -#: base/main.cpp:491 +#: base/main.cpp:492 msgid "Could not find any engine capable of running the selected game" msgstr "Kunde inte hitta en motor kapabel till att köra det valda spelet" @@ -1089,17 +1126,17 @@ msgstr "Avbrutit av användaren" msgid "Unknown error" msgstr "Okänt fel" -#: engines/advancedDetector.cpp:324 +#: engines/advancedDetector.cpp:316 #, c-format msgid "The game in '%s' seems to be unknown." msgstr "Spelet i '%s' verkar vara okänt." -#: engines/advancedDetector.cpp:325 +#: engines/advancedDetector.cpp:317 msgid "Please, report the following data to the ScummVM team along with name" msgstr "" "Var god rapportera följande data till ScummVM-teamet tillsammans med namnet" -#: engines/advancedDetector.cpp:327 +#: engines/advancedDetector.cpp:319 msgid "of the game you tried to add and its version/language/etc.:" msgstr "på spelet du försökte lägga till och dess version/språk/etc.:" @@ -1137,13 +1174,13 @@ msgid "~R~eturn to Launcher" msgstr "Åte~r~vänd till launcher" #: engines/dialogs.cpp:115 engines/agi/saveload.cpp:803 -#: engines/cruise/menu.cpp:212 engines/sci/engine/kfile.cpp:735 +#: engines/cruise/menu.cpp:212 engines/sci/engine/kfile.cpp:742 msgid "Save game:" msgstr "Spara spelet:" #: engines/dialogs.cpp:115 engines/agi/saveload.cpp:803 #: engines/scumm/dialogs.cpp:187 engines/cruise/menu.cpp:212 -#: engines/sci/engine/kfile.cpp:735 +#: engines/sci/engine/kfile.cpp:742 #: backends/platform/symbian/src/SymbianActions.cpp:44 #: backends/platform/wince/CEActionsPocket.cpp:43 #: backends/platform/wince/CEActionsPocket.cpp:267 @@ -1250,21 +1287,21 @@ msgstr "" msgid "Start anyway" msgstr "Starta ändå" -#: engines/agi/detection.cpp:145 engines/dreamweb/detection.cpp:47 -#: engines/sci/detection.cpp:390 +#: engines/agi/detection.cpp:142 engines/dreamweb/detection.cpp:47 +#: engines/sci/detection.cpp:393 msgid "Use original save/load screens" msgstr "Använd originalskärmar för spara/ladda" -#: engines/agi/detection.cpp:146 engines/dreamweb/detection.cpp:48 -#: engines/sci/detection.cpp:391 +#: engines/agi/detection.cpp:143 engines/dreamweb/detection.cpp:48 +#: engines/sci/detection.cpp:394 msgid "Use the original save/load screens, instead of the ScummVM ones" msgstr "Använder originalskärmarna för spara/ladda istället för ScummVM:s" -#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:831 +#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:838 msgid "Restore game:" msgstr "Återställ spel:" -#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:831 +#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:838 msgid "Restore" msgstr "Återställ" @@ -1276,27 +1313,27 @@ msgstr "Använd ljus palett-läge" msgid "Display graphics using the game's bright palette" msgstr "Visa grafik med spelets ljusa palett" -#: engines/sci/detection.cpp:370 +#: engines/sci/detection.cpp:373 msgid "EGA undithering" msgstr "EGA anti-gitter" -#: engines/sci/detection.cpp:371 +#: engines/sci/detection.cpp:374 msgid "Enable undithering in EGA games" msgstr "Aktivera anti-gitter i EGA-spel" -#: engines/sci/detection.cpp:380 +#: engines/sci/detection.cpp:383 msgid "Prefer digital sound effects" msgstr "Föredra digitala ljudeffekter" -#: engines/sci/detection.cpp:381 +#: engines/sci/detection.cpp:384 msgid "Prefer digital sound effects instead of synthesized ones" msgstr "Föredra digitala ljudeffekter istället för syntetiserade" -#: engines/sci/detection.cpp:400 +#: engines/sci/detection.cpp:403 msgid "Use IMF/Yamaha FB-01 for MIDI output" msgstr "Använd IMF/Yamaha FB-01 för MIDI-uppspelning" -#: engines/sci/detection.cpp:401 +#: engines/sci/detection.cpp:404 msgid "" "Use an IBM Music Feature card or a Yamaha FB-01 FM synth module for MIDI " "output" @@ -1304,29 +1341,29 @@ msgstr "" "Använd ett IMB Music Feature-kort eller en Yamaha FB-01 FM synthmodul för " "MIDI-uppspelning" -#: engines/sci/detection.cpp:411 +#: engines/sci/detection.cpp:414 msgid "Use CD audio" msgstr "Använd CD-ljud" -#: engines/sci/detection.cpp:412 +#: engines/sci/detection.cpp:415 msgid "Use CD audio instead of in-game audio, if available" msgstr "Använd CD-ljud istället för spelets ljud, om tillgängligt" -#: engines/sci/detection.cpp:422 +#: engines/sci/detection.cpp:425 msgid "Use Windows cursors" msgstr "Använd Windows muspekare" -#: engines/sci/detection.cpp:423 +#: engines/sci/detection.cpp:426 msgid "" "Use the Windows cursors (smaller and monochrome) instead of the DOS ones" msgstr "" "Använd Windows muspekare (mindre och svartvit) istället för DOS-pekaren" -#: engines/sci/detection.cpp:433 +#: engines/sci/detection.cpp:436 msgid "Use silver cursors" msgstr "Använd silverpekare" -#: engines/sci/detection.cpp:434 +#: engines/sci/detection.cpp:437 msgid "" "Use the alternate set of silver cursors, instead of the normal golden ones" msgstr "" @@ -1979,7 +2016,7 @@ msgstr "Flyg åt höger" msgid "Fly to lower right" msgstr "Flyg åt nedre höger" -#: engines/scumm/scumm.cpp:1773 +#: engines/scumm/scumm.cpp:1774 #, c-format msgid "" "Native MIDI support requires the Roland Upgrade from LucasArts,\n" @@ -1988,7 +2025,7 @@ msgstr "" "Stöd för Native MIDI kräver Roland-uppdateringen från LucasArts,\n" "men %s saknas. Använder AdLib istället." -#: engines/scumm/scumm.cpp:2278 engines/agos/saveload.cpp:202 +#: engines/scumm/scumm.cpp:2295 engines/agos/saveload.cpp:220 #, c-format msgid "" "Failed to save game state to file:\n" @@ -1999,7 +2036,7 @@ msgstr "" "\n" "%s" -#: engines/scumm/scumm.cpp:2285 engines/agos/saveload.cpp:167 +#: engines/scumm/scumm.cpp:2302 engines/agos/saveload.cpp:185 #, c-format msgid "" "Failed to load game state from file:\n" @@ -2010,7 +2047,7 @@ msgstr "" "\n" "%s" -#: engines/scumm/scumm.cpp:2297 engines/agos/saveload.cpp:210 +#: engines/scumm/scumm.cpp:2314 engines/agos/saveload.cpp:228 #, c-format msgid "" "Successfully saved game state in file:\n" @@ -2021,7 +2058,7 @@ msgstr "" "\n" "%s" -#: engines/scumm/scumm.cpp:2512 +#: engines/scumm/scumm.cpp:2529 msgid "" "Usually, Maniac Mansion would start now. But ScummVM doesn't do that yet. To " "play it, go to 'Add Game' in the ScummVM start menu and select the 'Maniac' " @@ -2057,17 +2094,17 @@ msgstr "Huvud~m~eny" msgid "~W~ater Effect Enabled" msgstr "~V~atteneffekt aktiverad" -#: engines/agos/animation.cpp:560 +#: engines/agos/animation.cpp:557 #, c-format msgid "Cutscene file '%s' not found!" msgstr "Filmscensfilen '%s' hittades ej!" #: engines/gob/inter_playtoons.cpp:256 engines/gob/inter_v2.cpp:1287 -#: engines/tinsel/saveload.cpp:502 +#: engines/tinsel/saveload.cpp:532 msgid "Failed to load game state from file." msgstr "Kunde inte läsa spardata från filen" -#: engines/gob/inter_v2.cpp:1357 engines/tinsel/saveload.cpp:515 +#: engines/gob/inter_v2.cpp:1357 engines/tinsel/saveload.cpp:545 msgid "Failed to save game state to file." msgstr "Kunde inte skriva spardata till filen." @@ -2185,7 +2222,7 @@ msgstr "Inställningar" msgid "Choose Spell" msgstr "Välj trollformel" -#: engines/kyra/sound_midi.cpp:475 +#: engines/kyra/sound_midi.cpp:477 msgid "" "You appear to be using a General MIDI device,\n" "but your game only supports Roland MT32 MIDI.\n" @@ -2199,12 +2236,13 @@ msgstr "" "General MIDI-instrument. Det kan trots allt hända\n" "att ett fåtal ljudspår inte spelas korrekt." -#: engines/queen/queen.cpp:59 engines/sky/detection.cpp:44 -msgid "Floppy intro" -msgstr "Diskettintro" +#: engines/queen/queen.cpp:59 +msgid "Alternative intro" +msgstr "" -#: engines/queen/queen.cpp:60 engines/sky/detection.cpp:45 -msgid "Use the floppy version's intro (CD version only)" +#: engines/queen/queen.cpp:60 +#, fuzzy +msgid "Use an alternative game intro (CD version only)" msgstr "Använd diskettversionens intro (endast CD-version)" #: engines/sky/compact.cpp:130 @@ -2223,25 +2261,33 @@ msgstr "" "Filen \"sky.cpt\" har inkorrekt filstorlek.\n" "Var god ladda hem den igen från www.scummvm.org" -#: engines/sword1/animation.cpp:539 +#: engines/sky/detection.cpp:44 +msgid "Floppy intro" +msgstr "Diskettintro" + +#: engines/sky/detection.cpp:45 +msgid "Use the floppy version's intro (CD version only)" +msgstr "Använd diskettversionens intro (endast CD-version)" + +#: engines/sword1/animation.cpp:519 #, c-format msgid "PSX stream cutscene '%s' cannot be played in paletted mode" msgstr "PSX-filmscenen '%s' kan inte visas i palettläget" -#: engines/sword1/animation.cpp:560 engines/sword2/animation.cpp:455 +#: engines/sword1/animation.cpp:540 engines/sword2/animation.cpp:439 msgid "DXA cutscenes found but ScummVM has been built without zlib support" msgstr "DXA filmscener hittades men ScummVM har byggts utan stöd för zlib" -#: engines/sword1/animation.cpp:570 engines/sword2/animation.cpp:465 +#: engines/sword1/animation.cpp:550 engines/sword2/animation.cpp:449 msgid "MPEG2 cutscenes are no longer supported" msgstr "MPEG2 filmscener stöds inte längre" -#: engines/sword1/animation.cpp:576 engines/sword2/animation.cpp:473 +#: engines/sword1/animation.cpp:556 engines/sword2/animation.cpp:457 #, c-format msgid "Cutscene '%s' not found" msgstr "Filmscenen '%s' hittades ej" -#: engines/sword1/control.cpp:863 +#: engines/sword1/control.cpp:865 msgid "" "ScummVM found that you have old savefiles for Broken Sword 1 that should be " "converted.\n" @@ -2259,7 +2305,7 @@ msgstr "" "Tryck \"OK\" för att konvertera dem nu, annars kommer du tillfrågas igen " "nästa gång du startar spelet.\n" -#: engines/sword1/control.cpp:1232 +#: engines/sword1/control.cpp:1234 #, c-format msgid "" "Target new save game already exists!\n" @@ -2268,11 +2314,11 @@ msgstr "" "Den valda spardatan existerar redan!\n" "Vill du behålla den gamla spardatan (%s) eller den nya (%s)?\n" -#: engines/sword1/control.cpp:1235 +#: engines/sword1/control.cpp:1237 msgid "Keep the old one" msgstr "Behåll den gamla" -#: engines/sword1/control.cpp:1235 +#: engines/sword1/control.cpp:1237 msgid "Keep the new one" msgstr "Behåll den nya" @@ -2280,7 +2326,7 @@ msgstr "Behåll den nya" msgid "This is the end of the Broken Sword 1 Demo" msgstr "Här slutar Broken Sword 1 demon" -#: engines/sword2/animation.cpp:435 +#: engines/sword2/animation.cpp:419 msgid "" "PSX cutscenes found but ScummVM has been built without RGB color support" msgstr "PSX-filmscener hittades men ScummVM har byggts utan stöd för RGB-färg" @@ -2293,6 +2339,17 @@ msgstr "Visa etiketter" msgid "Show labels for objects on mouse hover" msgstr "Visar etiketter för objekten som musen pekar på" +#: engines/teenagent/resources.cpp:94 +msgid "" +"You're missing the 'teenagent.dat' file. Get it from the ScummVM website" +msgstr "" + +#: engines/teenagent/resources.cpp:115 +msgid "" +"The teenagent.dat file is compressed and zlib hasn't been included in this " +"executable. Please decompress it" +msgstr "" + #: engines/parallaction/saveload.cpp:133 #, c-format msgid "" @@ -2400,7 +2457,7 @@ msgstr "Ingen musik" msgid "Amiga Audio Emulator" msgstr "Amiga ljudemulator" -#: audio/softsynth/adlib.cpp:1593 +#: audio/softsynth/adlib.cpp:2284 msgid "AdLib Emulator" msgstr "AdLib-emulator" @@ -2544,11 +2601,11 @@ msgstr "Touchpad-läge aktiverat." msgid "Touchpad mode disabled." msgstr "Touchpad-läge inaktiverat." -#: backends/platform/maemo/maemo.cpp:205 +#: backends/platform/maemo/maemo.cpp:209 msgid "Click Mode" msgstr "Klickläge" -#: backends/platform/maemo/maemo.cpp:211 +#: backends/platform/maemo/maemo.cpp:215 #: backends/platform/symbian/src/SymbianActions.cpp:42 #: backends/platform/wince/CEActionsPocket.cpp:60 #: backends/platform/wince/CEActionsSmartphone.cpp:43 @@ -2556,35 +2613,35 @@ msgstr "Klickläge" msgid "Left Click" msgstr "Vänsterklick" -#: backends/platform/maemo/maemo.cpp:214 +#: backends/platform/maemo/maemo.cpp:218 msgid "Middle Click" msgstr "Mittenklick" -#: backends/platform/maemo/maemo.cpp:217 +#: backends/platform/maemo/maemo.cpp:221 #: backends/platform/symbian/src/SymbianActions.cpp:43 #: backends/platform/wince/CEActionsSmartphone.cpp:44 #: backends/platform/bada/form.cpp:273 msgid "Right Click" msgstr "Högerklick" -#: backends/platform/sdl/macosx/appmenu_osx.mm:78 +#: backends/platform/sdl/macosx/appmenu_osx.mm:77 msgid "Hide ScummVM" msgstr "Göm ScummVM" -#: backends/platform/sdl/macosx/appmenu_osx.mm:83 +#: backends/platform/sdl/macosx/appmenu_osx.mm:82 msgid "Hide Others" msgstr "Göm övriga" -#: backends/platform/sdl/macosx/appmenu_osx.mm:88 +#: backends/platform/sdl/macosx/appmenu_osx.mm:87 msgid "Show All" msgstr "Visa alla" -#: backends/platform/sdl/macosx/appmenu_osx.mm:110 -#: backends/platform/sdl/macosx/appmenu_osx.mm:121 +#: backends/platform/sdl/macosx/appmenu_osx.mm:109 +#: backends/platform/sdl/macosx/appmenu_osx.mm:120 msgid "Window" msgstr "Fönster" -#: backends/platform/sdl/macosx/appmenu_osx.mm:115 +#: backends/platform/sdl/macosx/appmenu_osx.mm:114 msgid "Minimize" msgstr "Minimera" @@ -2965,41 +3022,46 @@ msgstr "Launcher" msgid "Do you really want to quit?" msgstr "Vill du verkligen avsluta?" -#: backends/events/gph/gph-events.cpp:338 -#: backends/events/gph/gph-events.cpp:381 -#: backends/events/openpandora/op-events.cpp:139 +#: backends/events/gph/gph-events.cpp:386 +#: backends/events/gph/gph-events.cpp:429 +#: backends/events/openpandora/op-events.cpp:168 msgid "Touchscreen 'Tap Mode' - Left Click" msgstr "Touchscreen \"Tap-läge\" - Vänsterklick" -#: backends/events/gph/gph-events.cpp:340 -#: backends/events/gph/gph-events.cpp:383 -#: backends/events/openpandora/op-events.cpp:141 +#: backends/events/gph/gph-events.cpp:388 +#: backends/events/gph/gph-events.cpp:431 +#: backends/events/openpandora/op-events.cpp:170 msgid "Touchscreen 'Tap Mode' - Right Click" msgstr "Touchscren \"Tap-läge\" - Högerklick" -#: backends/events/gph/gph-events.cpp:342 -#: backends/events/gph/gph-events.cpp:385 -#: backends/events/openpandora/op-events.cpp:143 +#: backends/events/gph/gph-events.cpp:390 +#: backends/events/gph/gph-events.cpp:433 +#: backends/events/openpandora/op-events.cpp:172 msgid "Touchscreen 'Tap Mode' - Hover (No Click)" msgstr "Touchscreen \"Tap-läge\" - Hover (utan klick)" -#: backends/events/gph/gph-events.cpp:362 +#: backends/events/gph/gph-events.cpp:410 msgid "Maximum Volume" msgstr "Max. volym" -#: backends/events/gph/gph-events.cpp:364 +#: backends/events/gph/gph-events.cpp:412 msgid "Increasing Volume" msgstr "Höja volymen" -#: backends/events/gph/gph-events.cpp:370 +#: backends/events/gph/gph-events.cpp:418 msgid "Minimal Volume" msgstr "Min. volym" -#: backends/events/gph/gph-events.cpp:372 +#: backends/events/gph/gph-events.cpp:420 msgid "Decreasing Volume" msgstr "Sänka volymen" -#: backends/updates/macosx/macosx-updates.mm:65 +#: backends/events/openpandora/op-events.cpp:174 +#, fuzzy +msgid "Touchscreen 'Tap Mode' - Hover (DPad Clicks)" +msgstr "Touchscreen \"Tap-läge\" - Hover (utan klick)" + +#: backends/updates/macosx/macosx-updates.mm:67 msgid "Check for Updates..." msgstr "Sök efter uppdateringar..." diff --git a/po/uk_UA.po b/po/uk_UA.po index 38855dcb11..3b6ccd266f 100644 --- a/po/uk_UA.po +++ b/po/uk_UA.po @@ -1,5 +1,5 @@ # Ukrainian translation for ScummVM. -# Copyright (C) 2010-2012 ScummVM Team +# Copyright (C) 2010-2013 ScummVM Team # This file is distributed under the same license as the ScummVM package. # Lubomyr Lisen, 2010. # @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: ScummVM 1.3.0svn\n" "Report-Msgid-Bugs-To: scummvm-devel@lists.sf.net\n" -"POT-Creation-Date: 2012-07-08 12:25+0100\n" +"POT-Creation-Date: 2012-12-01 17:22+0000\n" "PO-Revision-Date: 2012-06-29 20:19+0200\n" "Last-Translator: lubomyr <lubomyr31@gmail.com>\n" "Language-Team: Ukrainian\n" @@ -46,10 +46,11 @@ msgstr "²ÓÞàã" #: gui/browser.cpp:69 gui/chooser.cpp:45 gui/KeysDialog.cpp:43 #: gui/launcher.cpp:345 gui/massadd.cpp:94 gui/options.cpp:1228 -#: gui/saveload.cpp:64 gui/saveload.cpp:173 gui/themebrowser.cpp:54 -#: engines/engine.cpp:442 engines/scumm/dialogs.cpp:190 -#: engines/sword1/control.cpp:865 engines/parallaction/saveload.cpp:274 -#: backends/platform/wii/options.cpp:48 +#: gui/saveload-dialog.cpp:215 gui/saveload-dialog.cpp:275 +#: gui/saveload-dialog.cpp:545 gui/saveload-dialog.cpp:919 +#: gui/themebrowser.cpp:54 engines/engine.cpp:442 +#: engines/scumm/dialogs.cpp:190 engines/sword1/control.cpp:867 +#: engines/parallaction/saveload.cpp:274 backends/platform/wii/options.cpp:48 #: backends/events/default/default-events.cpp:191 #: backends/events/default/default-events.cpp:213 msgid "Cancel" @@ -70,15 +71,15 @@ msgstr "·ÐÚàØâØ" msgid "Mouse click" msgstr "ºÛöÚ ÜØèÚÞî" -#: gui/gui-manager.cpp:122 base/main.cpp:300 +#: gui/gui-manager.cpp:122 base/main.cpp:301 msgid "Display keyboard" msgstr "¿ÞÚÐ×ÐâØ ÚÛÐÒöÐâãàã" -#: gui/gui-manager.cpp:126 base/main.cpp:304 +#: gui/gui-manager.cpp:126 base/main.cpp:305 msgid "Remap keys" msgstr "¿ÕàÕßàØ×ÝÐçØâØ ÚÛÐÒöèö" -#: gui/gui-manager.cpp:129 base/main.cpp:307 +#: gui/gui-manager.cpp:129 base/main.cpp:308 msgid "Toggle FullScreen" msgstr "¿ÕàÕÜÚÝãâØ ßÞÒÝÞÕÚàÐÝÝØÙ àÕÖØÜ" @@ -92,16 +93,16 @@ msgstr "¿àØ×ÝÐçØâØ" #: gui/KeysDialog.cpp:42 gui/launcher.cpp:346 gui/launcher.cpp:1001 #: gui/launcher.cpp:1005 gui/massadd.cpp:91 gui/options.cpp:1229 -#: engines/engine.cpp:361 engines/engine.cpp:372 engines/scumm/dialogs.cpp:192 -#: engines/scumm/scumm.cpp:1775 engines/agos/animation.cpp:561 -#: engines/groovie/script.cpp:420 engines/sky/compact.cpp:131 -#: engines/sky/compact.cpp:141 engines/sword1/animation.cpp:539 -#: engines/sword1/animation.cpp:560 engines/sword1/animation.cpp:570 -#: engines/sword1/animation.cpp:577 engines/sword1/control.cpp:865 -#: engines/sword1/logic.cpp:1633 engines/sword2/animation.cpp:435 -#: engines/sword2/animation.cpp:455 engines/sword2/animation.cpp:465 -#: engines/sword2/animation.cpp:474 engines/parallaction/saveload.cpp:274 -#: backends/platform/wii/options.cpp:47 +#: gui/saveload-dialog.cpp:920 engines/engine.cpp:361 engines/engine.cpp:372 +#: engines/scumm/dialogs.cpp:192 engines/scumm/scumm.cpp:1776 +#: engines/agos/animation.cpp:558 engines/groovie/script.cpp:420 +#: engines/sky/compact.cpp:131 engines/sky/compact.cpp:141 +#: engines/sword1/animation.cpp:519 engines/sword1/animation.cpp:540 +#: engines/sword1/animation.cpp:550 engines/sword1/animation.cpp:557 +#: engines/sword1/control.cpp:867 engines/sword1/logic.cpp:1633 +#: engines/sword2/animation.cpp:419 engines/sword2/animation.cpp:439 +#: engines/sword2/animation.cpp:449 engines/sword2/animation.cpp:458 +#: engines/parallaction/saveload.cpp:274 backends/platform/wii/options.cpp:47 #: backends/platform/wince/CELauncherDialog.cpp:54 msgid "OK" msgstr "OK" @@ -356,7 +357,7 @@ msgstr "ÆÕÙ ID ÓàØ ÒÖÕ ÒØÚÞàØáâÞÒãôâìáï. ±ãÔì ÛÐáÚÐ, ÒØÑÕàöâì öÝèØÙ." msgid "~Q~uit" msgstr "~²~ØåöÔ" -#: gui/launcher.cpp:621 backends/platform/sdl/macosx/appmenu_osx.mm:96 +#: gui/launcher.cpp:621 backends/platform/sdl/macosx/appmenu_osx.mm:95 msgid "Quit ScummVM" msgstr "²ØåöÔ ×ö ScummVM" @@ -364,7 +365,7 @@ msgstr "²ØåöÔ ×ö ScummVM" msgid "A~b~out..." msgstr "¿àÞ ß~à~ÞÓàÐÜã..." -#: gui/launcher.cpp:622 backends/platform/sdl/macosx/appmenu_osx.mm:70 +#: gui/launcher.cpp:622 backends/platform/sdl/macosx/appmenu_osx.mm:69 msgid "About ScummVM" msgstr "¿àÞ ScummVM" @@ -439,13 +440,13 @@ msgstr "¿ÞèãÚ ã áߨáÚã öÓÞà" msgid "Search:" msgstr "¿ÞèãÚ:" -#: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/mohawk/myst.cpp:255 +#: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/mohawk/myst.cpp:245 #: engines/mohawk/riven.cpp:716 engines/cruise/menu.cpp:214 msgid "Load game:" msgstr "·ÐÒÐÝâÐÖØâØ Óàã:" #: gui/launcher.cpp:680 engines/dialogs.cpp:114 engines/scumm/dialogs.cpp:188 -#: engines/mohawk/myst.cpp:255 engines/mohawk/riven.cpp:716 +#: engines/mohawk/myst.cpp:245 engines/mohawk/riven.cpp:716 #: engines/cruise/menu.cpp:214 backends/platform/wince/CEActionsPocket.cpp:267 #: backends/platform/wince/CEActionsSmartphone.cpp:231 msgid "Load" @@ -923,68 +924,101 @@ msgstr "" "²ØÑàÐÝÐ âÕÜÐ ÝÕ ßöÔâàØÜãô ßÞâÞçÝã ÜÞÒã. ÏÚéÞ ÒØ åÞçÕâÕ ÒØÚÞàØáâÞÒãÒÐâØ æî " "âÕÜã, ßÞâàöÑÝÞ Ò ßÕàèã çÕàÓã ×ÜöÝØâØ ÜÞÒã." -#: gui/saveload.cpp:59 gui/saveload.cpp:257 +#: gui/saveload-dialog.cpp:166 +msgid "List view" +msgstr "²ØÓÛïÔ áߨáÚã" + +#: gui/saveload-dialog.cpp:167 +msgid "Grid view" +msgstr "²ØÓÛïÔ áöâÚØ" + +#: gui/saveload-dialog.cpp:210 gui/saveload-dialog.cpp:358 msgid "No date saved" msgstr "´Ðâã ÝÕ ×ÐߨáÐÝÞ" -#: gui/saveload.cpp:60 gui/saveload.cpp:258 +#: gui/saveload-dialog.cpp:211 gui/saveload-dialog.cpp:359 msgid "No time saved" msgstr "ÇÐá ÝÕ ×ÐߨáÐÝÞ" -#: gui/saveload.cpp:61 gui/saveload.cpp:259 +#: gui/saveload-dialog.cpp:212 gui/saveload-dialog.cpp:360 msgid "No playtime saved" msgstr "ÇÐá ÓàØ ÝÕ ×ÐߨáÐÝÞ" -#: gui/saveload.cpp:68 gui/saveload.cpp:173 +#: gui/saveload-dialog.cpp:219 gui/saveload-dialog.cpp:275 msgid "Delete" msgstr "²ØÔÐÛØâØ" -#: gui/saveload.cpp:172 +#: gui/saveload-dialog.cpp:274 msgid "Do you really want to delete this savegame?" msgstr "²Ø ÔöÙáÝÞ åÞçÕâÕ ÒØÔÐ󯉯 æÕ ×ÑÕàÕÖÕÝÝï?" -#: gui/saveload.cpp:282 +#: gui/saveload-dialog.cpp:383 gui/saveload-dialog.cpp:872 msgid "Date: " msgstr "´ÐâÐ: " -#: gui/saveload.cpp:286 +#: gui/saveload-dialog.cpp:387 gui/saveload-dialog.cpp:878 msgid "Time: " msgstr "ÇÐá: " -#: gui/saveload.cpp:292 +#: gui/saveload-dialog.cpp:393 gui/saveload-dialog.cpp:886 msgid "Playtime: " msgstr "ÇÐá ÓàØ: " -#: gui/saveload.cpp:305 gui/saveload.cpp:372 +#: gui/saveload-dialog.cpp:406 gui/saveload-dialog.cpp:494 msgid "Untitled savestate" msgstr "·ÑÕàÕÖÕÝÝï ÑÕ× öÜÕÝö" +#: gui/saveload-dialog.cpp:546 +msgid "Next" +msgstr "½ÐáãâßÝØÙ" + +#: gui/saveload-dialog.cpp:549 +msgid "Prev" +msgstr "¿ÞßÕàÕÔÝöÙ" + +#: gui/saveload-dialog.cpp:736 +msgid "New Save" +msgstr "½ÞÒÕ ×ÑÕàÕÖÕÝÝï" + +#: gui/saveload-dialog.cpp:736 +msgid "Create a new save game" +msgstr "ÁâÒÞàØâØ ÝÞÒØÙ ×Ðߨá ÓàØ" + +#: gui/saveload-dialog.cpp:865 +msgid "Name: " +msgstr "½Ð×ÒÐ: " + +#: gui/saveload-dialog.cpp:937 +#, c-format +msgid "Enter a description for slot %d:" +msgstr "²ÒÕÔöâì Þߨá ÔÛï áÛÞâã %d:" + #: gui/themebrowser.cpp:44 msgid "Select a Theme" msgstr "²ØÑÕàöâì âÕÜã" -#: gui/ThemeEngine.cpp:335 +#: gui/ThemeEngine.cpp:337 msgid "Disabled GFX" msgstr "±Õ× ÓàÐäöÚØ" -#: gui/ThemeEngine.cpp:335 +#: gui/ThemeEngine.cpp:337 msgctxt "lowres" msgid "Disabled GFX" msgstr "±Õ× ÓàÐäöÚØ" -#: gui/ThemeEngine.cpp:336 +#: gui/ThemeEngine.cpp:338 msgid "Standard Renderer (16bpp)" msgstr "ÁâÐÝÔÐàâÝØÙ àÐáâÕàØ×ÐâÞà (16bpp)" -#: gui/ThemeEngine.cpp:336 +#: gui/ThemeEngine.cpp:338 msgid "Standard (16bpp)" msgstr "ÁâÐÝÔÐàâÝØÙ àÐáâÕàØ×ÐâÞà (16bpp)" -#: gui/ThemeEngine.cpp:338 +#: gui/ThemeEngine.cpp:340 msgid "Antialiased Renderer (16bpp)" msgstr "ÀÐáâÕàØ×ÐâÞà ×ö ×ÓÛÐÔÖãÒÐÝÝïÜ (16bpp)" -#: gui/ThemeEngine.cpp:338 +#: gui/ThemeEngine.cpp:340 msgid "Antialiased (16bpp)" msgstr "ÀÐáâÕàØ×ÐâÞà ×ö ×ÓÛÐÔÖãÒÐÝÝïÜ (16bpp)" @@ -992,35 +1026,35 @@ msgstr "ÀÐáâÕàØ×ÐâÞà ×ö ×ÓÛÐÔÖãÒÐÝÝïÜ (16bpp)" msgid "Clear value" msgstr "¾çØáâØâØ ×ÝÐçÕÝÝï" -#: base/main.cpp:209 +#: base/main.cpp:210 #, c-format msgid "Engine does not support debug level '%s'" msgstr "´ÒØÖÞÚ ÝÕ ßöÔâàØÜãô àöÒÕÝì ÒöÔÛÐÔÚØ '%s'" -#: base/main.cpp:287 +#: base/main.cpp:288 msgid "Menu" msgstr "¼ÕÝî" -#: base/main.cpp:290 backends/platform/symbian/src/SymbianActions.cpp:45 +#: base/main.cpp:291 backends/platform/symbian/src/SymbianActions.cpp:45 #: backends/platform/wince/CEActionsPocket.cpp:45 #: backends/platform/wince/CEActionsSmartphone.cpp:46 msgid "Skip" msgstr "¿àÞßãáâØâØ" -#: base/main.cpp:293 backends/platform/symbian/src/SymbianActions.cpp:50 +#: base/main.cpp:294 backends/platform/symbian/src/SymbianActions.cpp:50 #: backends/platform/wince/CEActionsPocket.cpp:42 msgid "Pause" msgstr "¿Ðã×Ð" -#: base/main.cpp:296 +#: base/main.cpp:297 msgid "Skip line" msgstr "¿àÞßãáâØâØ àïÔÞÚ" -#: base/main.cpp:467 +#: base/main.cpp:468 msgid "Error running game:" msgstr "¿ÞÜØÛÚÐ ×ÐßãáÚã ÓàØ:" -#: base/main.cpp:491 +#: base/main.cpp:492 msgid "Could not find any engine capable of running the selected game" msgstr "½Õ ÜÞÖã ×ÝÐÙâØ ÔÒØÖÞÚ ÔÛï ×ÐßãáÚã ÒØÑàÐÝÞ÷ ÓàØ" @@ -1088,16 +1122,16 @@ msgstr "²öÔÜöÝÕÝÞ ÚÞàØáâãÒÐçÕÜ" msgid "Unknown error" msgstr "½ÕÒöÔÞÜÐ ßÞÜØÛÚÐ" -#: engines/advancedDetector.cpp:324 +#: engines/advancedDetector.cpp:316 #, c-format msgid "The game in '%s' seems to be unknown." msgstr "³àÐ ã '%s' ÝÕÒöÔÞÜÐ." -#: engines/advancedDetector.cpp:325 +#: engines/advancedDetector.cpp:317 msgid "Please, report the following data to the ScummVM team along with name" msgstr "±ãÔì ÛÐáÚÐ, ßÕàÕÔÐÙâÕ ÝØÖçÕÝÐÒÕÔÕÝã öÝäÞàÜÐæöî ÚÞÜÐÝÔö ScummVM àÐ×ÞÜ ×" -#: engines/advancedDetector.cpp:327 +#: engines/advancedDetector.cpp:319 msgid "of the game you tried to add and its version/language/etc.:" msgstr "ÝÐ×ÒÞî ÓàØ, ïÚã ÒØ ÝÐÜÐÓÐôâÕáì ÔÞÔÐâØ, Ð âÐÚÞÖ ÷÷ ÒÕàáöî/ÜÞÒã/âÐ öÝèÕ:" @@ -1135,13 +1169,13 @@ msgid "~R~eturn to Launcher" msgstr "~¿~ÞÒÕà.Ò ÓÞÛÞÒÝÕ ÜÕÝî" #: engines/dialogs.cpp:115 engines/agi/saveload.cpp:803 -#: engines/cruise/menu.cpp:212 engines/sci/engine/kfile.cpp:735 +#: engines/cruise/menu.cpp:212 engines/sci/engine/kfile.cpp:742 msgid "Save game:" msgstr "·ÑÕàÕÓâØ Óàã: " #: engines/dialogs.cpp:115 engines/agi/saveload.cpp:803 #: engines/scumm/dialogs.cpp:187 engines/cruise/menu.cpp:212 -#: engines/sci/engine/kfile.cpp:735 +#: engines/sci/engine/kfile.cpp:742 #: backends/platform/symbian/src/SymbianActions.cpp:44 #: backends/platform/wince/CEActionsPocket.cpp:43 #: backends/platform/wince/CEActionsPocket.cpp:267 @@ -1250,22 +1284,22 @@ msgstr "" msgid "Start anyway" msgstr "²áÕ ÞÔÝÞ ×ÐßãáâØâØ" -#: engines/agi/detection.cpp:145 engines/dreamweb/detection.cpp:47 -#: engines/sci/detection.cpp:390 +#: engines/agi/detection.cpp:142 engines/dreamweb/detection.cpp:47 +#: engines/sci/detection.cpp:393 msgid "Use original save/load screens" msgstr "²ØÚÞàØáâÞÒãÒÐâØ ÞàØÓ. ×ÑÕàÕÖÕÝÝï/×ÐÒÐÝâÐÖÕÝÝï ÕÚàÐÝØ" -#: engines/agi/detection.cpp:146 engines/dreamweb/detection.cpp:48 -#: engines/sci/detection.cpp:391 +#: engines/agi/detection.cpp:143 engines/dreamweb/detection.cpp:48 +#: engines/sci/detection.cpp:394 msgid "Use the original save/load screens, instead of the ScummVM ones" msgstr "" "²ØÚÞàØáâÞÒãÒÐâØ ÞàØÓöÝÐÛìÝö ×ÑÕàÕÖÕÝÝï/×ÐÒÐÝâÐÖÕÝÝï ÕÚàÐÝØ, ×ÐÜöáâì ScummVM" -#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:831 +#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:838 msgid "Restore game:" msgstr "²öÔÝÞ񯉯 Óàã:" -#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:831 +#: engines/agi/saveload.cpp:816 engines/sci/engine/kfile.cpp:838 msgid "Restore" msgstr "²öÔÝÞÒØâØ" @@ -1277,54 +1311,56 @@ msgstr "²ØÚÞàØáâÞÒãÒÐâØ ïáÚàÐÒØÙ àÕÖØÜ ßÐÛöâàØ" msgid "Display graphics using the game's bright palette" msgstr "²öÔÞÑàÐÖÕÝÝï ÓàÐäöÚØ × ÒØÚÞàØáâÐÝÝïÜ ïáÚàÐÒÞ÷ ßÐÛöâàØ öÓà" -#: engines/sci/detection.cpp:370 +#: engines/sci/detection.cpp:373 msgid "EGA undithering" msgstr "EGA ÑÕ× àÐáâàãÒÐÝÝï" -#: engines/sci/detection.cpp:371 +#: engines/sci/detection.cpp:374 msgid "Enable undithering in EGA games" msgstr "ÃÒöÜÚÝãâØ ÐÝâØ-×ÓÛÐÔÖãÒÐÝÝï Ò EGA öÓàÐå" -#: engines/sci/detection.cpp:380 +#: engines/sci/detection.cpp:383 msgid "Prefer digital sound effects" msgstr "½ÐÔÐÒÐâØ ßÕàÕÒÐÓã æØäàÞÒØÜ ×ÒãÚÞÒØÜ ÕäÕÚâÐÜ" -#: engines/sci/detection.cpp:381 +#: engines/sci/detection.cpp:384 msgid "Prefer digital sound effects instead of synthesized ones" msgstr "²öÔÔÐÒÐâØ ßÕàÕÒÐÓã æØäàÞÒØÜ ×ÒãÚÞÒØÜ ÕäÕÚâÐÜ, Ð ÝÕ áØÝâÕ×ÞÒÐÝØÜ" -#: engines/sci/detection.cpp:400 +#: engines/sci/detection.cpp:403 msgid "Use IMF/Yamaha FB-01 for MIDI output" msgstr "²ØÚÞàØáâÞÒãÒÐâØ IMF/Yahama FB-01 ÔÛï MIDI ÒØåÞÔã" -#: engines/sci/detection.cpp:401 +#: engines/sci/detection.cpp:404 msgid "" "Use an IBM Music Feature card or a Yamaha FB-01 FM synth module for MIDI " "output" msgstr "" +"²ØÚÞàØáâÞÒãÒÐâØ ÔÛïÒ ÒØÒÞÔã MIDI àÕÖØÜ ÚÐàâØ IBM Feature ÐÑÞ FM áöÝâÕ× " +"Yamaha FB-01" -#: engines/sci/detection.cpp:411 +#: engines/sci/detection.cpp:414 msgid "Use CD audio" msgstr "²ØÚÞàØáâÞÒãÒÐâØ CD ÐãÔöÞ" -#: engines/sci/detection.cpp:412 +#: engines/sci/detection.cpp:415 msgid "Use CD audio instead of in-game audio, if available" msgstr "²ØÚÞàØáâÞÒãÒÐâØ CD ÐãÔöÞ ×ÐÜöáâì ã-Óàö ÐãÔöÞ, ïÚéÞ âÐÚö ô" -#: engines/sci/detection.cpp:422 +#: engines/sci/detection.cpp:425 msgid "Use Windows cursors" msgstr "²ØÚÞàØáâÞÒãÒÐâØ Windows ÚãàáÞàØ" -#: engines/sci/detection.cpp:423 +#: engines/sci/detection.cpp:426 msgid "" "Use the Windows cursors (smaller and monochrome) instead of the DOS ones" msgstr "²ØÚÞàØáâÞÒãÒÐâØ Windows ÚãàáÞàØ (ÜÕÝèØå ö ÜÞÝÞåàÞÜÝØå), ×ÐÜöáâì DOS" -#: engines/sci/detection.cpp:433 +#: engines/sci/detection.cpp:436 msgid "Use silver cursors" msgstr "²ØÚÞàØáâÞÒãÒÐâØ áàöÑÝö ÚãàáÞàØ" -#: engines/sci/detection.cpp:434 +#: engines/sci/detection.cpp:437 msgid "" "Use the alternate set of silver cursors, instead of the normal golden ones" msgstr "" @@ -1978,7 +2014,7 @@ msgstr "»ÕâöâØ ÝÐßàÐÒÞ" msgid "Fly to lower right" msgstr "»ÕâöâØ ÔÞÝØ×ã ÝÐßàÐÒÞ" -#: engines/scumm/scumm.cpp:1773 +#: engines/scumm/scumm.cpp:1774 #, c-format msgid "" "Native MIDI support requires the Roland Upgrade from LucasArts,\n" @@ -1987,7 +2023,7 @@ msgstr "" "ÀÕÖØÜ \"àöÔÝÞÓÞ\" MIDI ßÞâàÕÑãô ßÞÝÞÒÛÕÝÝï Roland Upgrade ÒöÔ\n" "LucasArts, ßàÞâÕ %s ÒöÔáãâÝöÙ. ¿ÕàÕÜØÚÐîáì ÝÐ AdLib." -#: engines/scumm/scumm.cpp:2278 engines/agos/saveload.cpp:202 +#: engines/scumm/scumm.cpp:2295 engines/agos/saveload.cpp:220 #, c-format msgid "" "Failed to save game state to file:\n" @@ -1998,7 +2034,7 @@ msgstr "" "\n" "%s" -#: engines/scumm/scumm.cpp:2285 engines/agos/saveload.cpp:167 +#: engines/scumm/scumm.cpp:2302 engines/agos/saveload.cpp:185 #, c-format msgid "" "Failed to load game state from file:\n" @@ -2009,7 +2045,7 @@ msgstr "" "\n" "%s" -#: engines/scumm/scumm.cpp:2297 engines/agos/saveload.cpp:210 +#: engines/scumm/scumm.cpp:2314 engines/agos/saveload.cpp:228 #, c-format msgid "" "Successfully saved game state in file:\n" @@ -2020,7 +2056,7 @@ msgstr "" "\n" "%s" -#: engines/scumm/scumm.cpp:2512 +#: engines/scumm/scumm.cpp:2529 msgid "" "Usually, Maniac Mansion would start now. But ScummVM doesn't do that yet. To " "play it, go to 'Add Game' in the ScummVM start menu and select the 'Maniac' " @@ -2056,17 +2092,17 @@ msgstr "³ÞÛÞÒÝÕ ÜÕÝî" msgid "~W~ater Effect Enabled" msgstr "µäÕÚâØ ÒÞÔØ ãÒöÜÚÝÕÝÞ" -#: engines/agos/animation.cpp:560 +#: engines/agos/animation.cpp:557 #, c-format msgid "Cutscene file '%s' not found!" msgstr "ÄÐÙÛ ×ÐáâÐÒÚØ '%s' ÝÕ ×ÝÐÙÔÕÝÞ!" #: engines/gob/inter_playtoons.cpp:256 engines/gob/inter_v2.cpp:1287 -#: engines/tinsel/saveload.cpp:502 +#: engines/tinsel/saveload.cpp:532 msgid "Failed to load game state from file." msgstr "½Õ ÒÔÐÛÞáï ×ÐÒÐÝâÐÖØâØ áâÐÝ ÓàØ × äÐÙÛã." -#: engines/gob/inter_v2.cpp:1357 engines/tinsel/saveload.cpp:515 +#: engines/gob/inter_v2.cpp:1357 engines/tinsel/saveload.cpp:545 msgid "Failed to save game state to file." msgstr "½Õ ÒÔÐÛÞáï ×ÑÕàÕÓâØ áâÐÝ ÓàØ ã äÐÙÛ." @@ -2130,11 +2166,11 @@ msgstr "ÃÒöÜÚÝãâØ ßÛÐÒÐîçö ÚãàáÞàØ" #. I18N: HP stands for Hit Points #: engines/kyra/detection.cpp:127 msgid "HP bar graphs" -msgstr "" +msgstr "ÁâÞÒßçØÚØ ×ÔÞàÞÒ'ï" #: engines/kyra/detection.cpp:128 msgid "Enable hit point bar graphs" -msgstr "" +msgstr "ÃØöÜÚÝãâØ ÓàÐäöçÝö áâÞÒߨçÚØ ×ÔÞàÞÒ'ï" #: engines/kyra/lol.cpp:478 msgid "Attack 1" @@ -2184,7 +2220,7 @@ msgstr "½ÐÛÐèâãÒÐÝÝï" msgid "Choose Spell" msgstr "²ØÑàÐâØ ×ÐÚÛïââï" -#: engines/kyra/sound_midi.cpp:475 +#: engines/kyra/sound_midi.cpp:477 msgid "" "You appear to be using a General MIDI device,\n" "but your game only supports Roland MT32 MIDI.\n" @@ -2198,13 +2234,13 @@ msgstr "" "MT32 ÝÐ General MIDI. °ÛÕ Ò àÕ×ãÛìâÐâö ÜÞÖÕ\n" "áâÐâØáï, éÞ ÔÕïÚö âàÕÚØ ÑãÔãâì ÓàÐâØ ÝÕßàÐÒØÛìÝÞ." -#: engines/queen/queen.cpp:59 engines/sky/detection.cpp:44 -msgid "Floppy intro" -msgstr "´ØáÚÕâÝÕ ÒÒÕÔÕÝÝï" +#: engines/queen/queen.cpp:59 +msgid "Alternative intro" +msgstr "°ÛâÕàÝÐâØÒÝØÙ Òáâãß" -#: engines/queen/queen.cpp:60 engines/sky/detection.cpp:45 -msgid "Use the floppy version's intro (CD version only)" -msgstr "²ØÚÞàØáâÞÒãÒÐâØ ÔØáÚÕâÝö ÒÕàáö÷ ÒÒÕÔÕÝÝï (âöÛìÚØ CD ÒÕàáöï)" +#: engines/queen/queen.cpp:60 +msgid "Use an alternative game intro (CD version only)" +msgstr "²ØÚÞàØáâÞÒãÒÐâØ ÐÛìâÕàÝÐâØÒÝØÙ Òáâãß ÓàØ (âöÛìÚØ CD ÒÕàáöï)" #: engines/sky/compact.cpp:130 msgid "" @@ -2222,25 +2258,33 @@ msgstr "" "ÄÐÙÛ sky.cpt ÜÐô ÝÕÒöàÝØÙ àÞ×Üöà.\n" "±ãÔì ÛÐáÚÐ, (ßÕàÕ)×ÐÒÐÝâÐÖâÕ ÙÞÓÞ × www.scummvm.org" -#: engines/sword1/animation.cpp:539 +#: engines/sky/detection.cpp:44 +msgid "Floppy intro" +msgstr "´ØáÚÕâÝÕ ÒÒÕÔÕÝÝï" + +#: engines/sky/detection.cpp:45 +msgid "Use the floppy version's intro (CD version only)" +msgstr "²ØÚÞàØáâÞÒãÒÐâØ ÔØáÚÕâÝö ÒÕàáö÷ ÒÒÕÔÕÝÝï (âöÛìÚØ CD ÒÕàáöï)" + +#: engines/sword1/animation.cpp:519 #, c-format msgid "PSX stream cutscene '%s' cannot be played in paletted mode" msgstr " àÞÛØÚ PSX ßÞâÞÚã '%s' ÝÕ ÜÞÖãâì ÑãâØ ÒöÔâÒÞàÕÝö ã àÕÖØÜö ßÐÛöâàØ" -#: engines/sword1/animation.cpp:560 engines/sword2/animation.cpp:455 +#: engines/sword1/animation.cpp:540 engines/sword2/animation.cpp:439 msgid "DXA cutscenes found but ScummVM has been built without zlib support" msgstr "·ÝÐÙÔÕÝÞ ×ÐáâÐÒÚØ DXA, ÐÛÕ ScummVM ÑãÒ ßÞÑãÔÞÒÐÝØÙ ÑÕ× ßöÔâàØÜÚØ zlib" -#: engines/sword1/animation.cpp:570 engines/sword2/animation.cpp:465 +#: engines/sword1/animation.cpp:550 engines/sword2/animation.cpp:449 msgid "MPEG2 cutscenes are no longer supported" msgstr "·ÐáâÐÒÚØ MPEG2 ÑöÛìèÕ ÝÕ ßöÔâàØÜãîâìáï" -#: engines/sword1/animation.cpp:576 engines/sword2/animation.cpp:473 +#: engines/sword1/animation.cpp:556 engines/sword2/animation.cpp:457 #, c-format msgid "Cutscene '%s' not found" msgstr "·ÐáâÐÒÚã '%s' ÝÕ ×ÝÐÙÔÕÝÞ" -#: engines/sword1/control.cpp:863 +#: engines/sword1/control.cpp:865 msgid "" "ScummVM found that you have old savefiles for Broken Sword 1 that should be " "converted.\n" @@ -2257,7 +2301,7 @@ msgstr "" "½ÐâØáÝöâì ¾º, éÞÑ ßÕàÕÒÕáâØ ÷å ×ÐàÐ×, öÝÐÚèÕ ãÕ ßÞÒöÔÞÜÛÕÝÝï ×'ïÒØâìáï ßàØ " "ÝÐáâãßÝÞÜã ×ÐßãáÚã ÓàØ.\n" -#: engines/sword1/control.cpp:1232 +#: engines/sword1/control.cpp:1234 #, c-format msgid "" "Target new save game already exists!\n" @@ -2266,11 +2310,11 @@ msgstr "" "·ÑÕàÕÖÕÝÝï ÓàØ × âÐÚÞî ÝÐ×ÒÞî ÒÖÕ öáÝãô!\n" "ÇØ ÒØ åÞçÕâÕ ÛØèØâØ áâÐàÕ ×ÑÕàÕÖÕÝÝï (%s) ÐÑÞ ÝÞÒÕ (%s)?\n" -#: engines/sword1/control.cpp:1235 +#: engines/sword1/control.cpp:1237 msgid "Keep the old one" msgstr "»ØèØâØ áâÐàÕ" -#: engines/sword1/control.cpp:1235 +#: engines/sword1/control.cpp:1237 msgid "Keep the new one" msgstr "²×ïâØ ÝÞÒÕ" @@ -2278,7 +2322,7 @@ msgstr "²×ïâØ ÝÞÒÕ" msgid "This is the end of the Broken Sword 1 Demo" msgstr "½Ð æìÞÜã ×ÐÚöÝçãôâìáï ÔÕÜÞ Broken Sword 1" -#: engines/sword2/animation.cpp:435 +#: engines/sword2/animation.cpp:419 msgid "" "PSX cutscenes found but ScummVM has been built without RGB color support" msgstr "·ÝÐÙÔÕÝö PSX àÞÛØÚØ, ÐÛÕ ScummVM ÑãÒ ×öÑàÐÝØÙ ÑÕ× ßöÔâàØÜÚØ RGB ÚÞÛöàã" @@ -2291,6 +2335,20 @@ msgstr "¿ÞÚÐ×ãÒÐâØ ÜöâÚØ ÞÑ'ôÚâöÒ" msgid "Show labels for objects on mouse hover" msgstr "¿ÞÚÐ×ãÒÐâØ ÜöâÚØ ÔÛï ÞÑ'ôÚâöÒ ßàØ ÝÐÒÕÔÕÝÝö ÜØèö" +#: engines/teenagent/resources.cpp:94 +msgid "" +"You're missing the 'teenagent.dat' file. Get it from the ScummVM website" +msgstr "" +"à ÒÐá ÒöÔáâãÝöÙ äÐÙÛ 'teenagent.dat'. ²ö×ìÜöâì ÙÞÓÞ ÝÐ ÒÕÑáÐÙâö ScummVM" + +#: engines/teenagent/resources.cpp:115 +msgid "" +"The teenagent.dat file is compressed and zlib hasn't been included in this " +"executable. Please decompress it" +msgstr "" +"ÄÐÙÛ teenagent.dat ×ÐßÐÚÞÒÐÝÞ, ÐÛÕ zlib ÝÕ ÑãÛÞ ÒÚÛîçÕÝÞ Ò æî ßàÞÓàÐÜÜã.±ãÔì-" +"ÛÐáÚÐ àÞ×ßÐÚãÙâÕ ÙÞÓÞ" + #: engines/parallaction/saveload.cpp:133 #, c-format msgid "" @@ -2368,8 +2426,8 @@ msgid "" "The selected audio device '%s' cannot be used. See log file for more " "information." msgstr "" -"²ØÑàÐÝØÙ ×ÒãÚÞÒØÙ ßàØáâàöÙ '%s' ÝÕ ÜÞÖÕ ÑãâØ ÒØÚÞàØáâÐÝØÙ. ´ØÒöâìáï äÐÙÛ ÛÞÓã " -"ÔÛï ÔÞÔÐâÚÞÒÞ÷ öÝäÞàÜÐæö÷." +"²ØÑàÐÝØÙ ×ÒãÚÞÒØÙ ßàØáâàöÙ '%s' ÝÕ ÜÞÖÕ ÑãâØ ÒØÚÞàØáâÐÝØÙ. ´ØÒöâìáï äÐÙÛ " +"ÛÞÓã ÔÛï ÔÞÔÐâÚÞÒÞ÷ öÝäÞàÜÐæö÷." #: audio/mididrv.cpp:257 #, c-format @@ -2397,7 +2455,7 @@ msgstr "±Õ× Üã×ØÚØ" msgid "Amiga Audio Emulator" msgstr "°ÜöÓÐ °ãÔöÞ µÜãÛïâÞà" -#: audio/softsynth/adlib.cpp:1593 +#: audio/softsynth/adlib.cpp:2284 msgid "AdLib Emulator" msgstr "µÜãÛïâÞà AdLib" @@ -2541,11 +2599,11 @@ msgstr "ÀÕÖØÜ âÐçßÐÔã ãÒöÜÚÝÕÝÞ." msgid "Touchpad mode disabled." msgstr "ÀÕÖØÜ âÐçßÐÔã ÒØÜÚÝÕÝÞ." -#: backends/platform/maemo/maemo.cpp:205 +#: backends/platform/maemo/maemo.cpp:209 msgid "Click Mode" msgstr "ÀÕÖØÜ ÚÛöÚöÒ" -#: backends/platform/maemo/maemo.cpp:211 +#: backends/platform/maemo/maemo.cpp:215 #: backends/platform/symbian/src/SymbianActions.cpp:42 #: backends/platform/wince/CEActionsPocket.cpp:60 #: backends/platform/wince/CEActionsSmartphone.cpp:43 @@ -2553,35 +2611,35 @@ msgstr "ÀÕÖØÜ ÚÛöÚöÒ" msgid "Left Click" msgstr "»öÒØÙ ÚÛöÚ" -#: backends/platform/maemo/maemo.cpp:214 +#: backends/platform/maemo/maemo.cpp:218 msgid "Middle Click" msgstr "ÁÕàÕÔÝöÙ ÚÛöÚ" -#: backends/platform/maemo/maemo.cpp:217 +#: backends/platform/maemo/maemo.cpp:221 #: backends/platform/symbian/src/SymbianActions.cpp:43 #: backends/platform/wince/CEActionsSmartphone.cpp:44 #: backends/platform/bada/form.cpp:273 msgid "Right Click" msgstr "¿àÐÒØÙ ÚÛöÚ" -#: backends/platform/sdl/macosx/appmenu_osx.mm:78 +#: backends/platform/sdl/macosx/appmenu_osx.mm:77 msgid "Hide ScummVM" msgstr "ÁåÞÒÐâØ ScummVM" -#: backends/platform/sdl/macosx/appmenu_osx.mm:83 +#: backends/platform/sdl/macosx/appmenu_osx.mm:82 msgid "Hide Others" msgstr "ÁåÞÒÐâØ ¦Ýèö" -#: backends/platform/sdl/macosx/appmenu_osx.mm:88 +#: backends/platform/sdl/macosx/appmenu_osx.mm:87 msgid "Show All" msgstr "¿ÞÚÐ×ÐâØ ²áÕ" -#: backends/platform/sdl/macosx/appmenu_osx.mm:110 -#: backends/platform/sdl/macosx/appmenu_osx.mm:121 +#: backends/platform/sdl/macosx/appmenu_osx.mm:109 +#: backends/platform/sdl/macosx/appmenu_osx.mm:120 msgid "Window" msgstr "²öÚÝÞ" -#: backends/platform/sdl/macosx/appmenu_osx.mm:115 +#: backends/platform/sdl/macosx/appmenu_osx.mm:114 msgid "Minimize" msgstr "¼öÝöÜö×ãÒÐâØ" @@ -2961,41 +3019,46 @@ msgstr "³ÞÛÞÒÝÕ ÜÕÝî" msgid "Do you really want to quit?" msgstr "²Ø ÔöÙáÝÞ åÞçÕâÕ ÒØÙâØ?" -#: backends/events/gph/gph-events.cpp:338 -#: backends/events/gph/gph-events.cpp:381 -#: backends/events/openpandora/op-events.cpp:139 +#: backends/events/gph/gph-events.cpp:386 +#: backends/events/gph/gph-events.cpp:429 +#: backends/events/openpandora/op-events.cpp:168 msgid "Touchscreen 'Tap Mode' - Left Click" msgstr "ÀÕÖØÜ ÔÞâØÚã ã âÐçáÚàöÝö - »öÒØÙ ÚÛöÚ" -#: backends/events/gph/gph-events.cpp:340 -#: backends/events/gph/gph-events.cpp:383 -#: backends/events/openpandora/op-events.cpp:141 +#: backends/events/gph/gph-events.cpp:388 +#: backends/events/gph/gph-events.cpp:431 +#: backends/events/openpandora/op-events.cpp:170 msgid "Touchscreen 'Tap Mode' - Right Click" msgstr "ÀÕÖØÜ ÔÞâØÚã ã âÐçáÚàöÝö - ¿àÐÒØÙ ÚÛöÚ" -#: backends/events/gph/gph-events.cpp:342 -#: backends/events/gph/gph-events.cpp:385 -#: backends/events/openpandora/op-events.cpp:143 +#: backends/events/gph/gph-events.cpp:390 +#: backends/events/gph/gph-events.cpp:433 +#: backends/events/openpandora/op-events.cpp:172 msgid "Touchscreen 'Tap Mode' - Hover (No Click)" msgstr "ÀÕÖØÜ ÔÞâØÚã ã âÐçáÚàöÝö - ¿àÞÛöâ (ÑÕ× ÚÛöÚã)" -#: backends/events/gph/gph-events.cpp:362 +#: backends/events/gph/gph-events.cpp:410 msgid "Maximum Volume" msgstr "¼ÐÚáØÜÐÛìÝÐ ³ãçÝöáâì" -#: backends/events/gph/gph-events.cpp:364 +#: backends/events/gph/gph-events.cpp:412 msgid "Increasing Volume" msgstr "¿öÔÒØéÕÝÝï ÓãçÝÞáâö" -#: backends/events/gph/gph-events.cpp:370 +#: backends/events/gph/gph-events.cpp:418 msgid "Minimal Volume" msgstr "¼öÝöÜÐÛìÝÐ ³ãçÝöáâì" -#: backends/events/gph/gph-events.cpp:372 +#: backends/events/gph/gph-events.cpp:420 msgid "Decreasing Volume" msgstr "¿ÞÝØÖÕÝÝï ÓãçÝÞáâö" -#: backends/updates/macosx/macosx-updates.mm:65 +#: backends/events/openpandora/op-events.cpp:174 +#, fuzzy +msgid "Touchscreen 'Tap Mode' - Hover (DPad Clicks)" +msgstr "ÀÕÖØÜ ÔÞâØÚã ã âÐçáÚàöÝö - ¿àÞÛöâ (ÑÕ× ÚÛöÚã)" + +#: backends/updates/macosx/macosx-updates.mm:67 msgid "Check for Updates..." msgstr "¿ÕàÕÒöàïî ÞÝÞÒÛÕÝÝï..." @@ -2,12 +2,27 @@ # included by the default (main) Makefile. # - # # POSIX specific # install: $(INSTALL) -d "$(DESTDIR)$(bindir)" + $(INSTALL) -c -m 755 "./$(EXECUTABLE)" "$(DESTDIR)$(bindir)/$(EXECUTABLE)" + $(INSTALL) -d "$(DESTDIR)$(mandir)/man6/" + $(INSTALL) -c -m 644 "$(srcdir)/dists/scummvm.6" "$(DESTDIR)$(mandir)/man6/scummvm.6" + $(INSTALL) -d "$(DESTDIR)$(datarootdir)/pixmaps/" + $(INSTALL) -c -m 644 "$(srcdir)/icons/scummvm.xpm" "$(DESTDIR)$(datarootdir)/pixmaps/scummvm.xpm" + $(INSTALL) -d "$(DESTDIR)$(docdir)" + $(INSTALL) -c -m 644 $(DIST_FILES_DOCS) "$(DESTDIR)$(docdir)" + $(INSTALL) -d "$(DESTDIR)$(datadir)" + $(INSTALL) -c -m 644 $(DIST_FILES_THEMES) $(DIST_FILES_ENGINEDATA) "$(DESTDIR)$(datadir)/" +ifdef DYNAMIC_MODULES + $(INSTALL) -d "$(DESTDIR)$(libdir)/scummvm/" + $(INSTALL) -c -m 644 $(PLUGINS) "$(DESTDIR)$(libdir)/scummvm/" +endif + +install-strip: + $(INSTALL) -d "$(DESTDIR)$(bindir)" $(INSTALL) -c -s -m 755 "./$(EXECUTABLE)" "$(DESTDIR)$(bindir)/$(EXECUTABLE)" $(INSTALL) -d "$(DESTDIR)$(mandir)/man6/" $(INSTALL) -c -m 644 "$(srcdir)/dists/scummvm.6" "$(DESTDIR)$(mandir)/man6/scummvm.6" @@ -96,6 +111,12 @@ ifdef USE_FLAC OSX_STATIC_LIBS += $(STATICLIBPATH)/lib/libFLAC.a endif +ifdef USE_FLUIDSYNTH +OSX_STATIC_LIBS += \ + -framework CoreAudio \ + $(STATICLIBPATH)/lib/libfluidsynth.a +endif + ifdef USE_MAD OSX_STATIC_LIBS += $(STATICLIBPATH)/lib/libmad.a endif @@ -218,9 +239,6 @@ win32dist: $(EXECUTABLE) mkdir -p $(WIN32PATH)/doc/se $(STRIP) $(EXECUTABLE) -o $(WIN32PATH)/$(EXECUTABLE) cp $(DIST_FILES_THEMES) $(WIN32PATH) -ifdef DIST_FILES_ENGINEDATA - cp $(DIST_FILES_ENGINEDATA) $(WIN32PATH) -endif cp $(srcdir)/AUTHORS $(WIN32PATH)/AUTHORS.txt cp $(srcdir)/COPYING $(WIN32PATH)/COPYING.txt cp $(srcdir)/COPYING.BSD $(WIN32PATH)/COPYING.BSD.txt @@ -293,6 +311,8 @@ endif @cd $(srcdir)/dists/msvc9 && ../../devtools/create_project/create_project ../.. --msvc --msvc-version 9 >/dev/null && git add -f *.sln *.vcproj *.vsprops @echo Creating MSVC10 project files... @cd $(srcdir)/dists/msvc10 && ../../devtools/create_project/create_project ../.. --msvc --msvc-version 10 >/dev/null && git add -f *.sln *.vcxproj *.vcxproj.filters *.props + @echo Creating MSVC11 project files... + @cd $(srcdir)/dists/msvc11 && ../../devtools/create_project/create_project ../.. --msvc --msvc-version 11 >/dev/null && git add -f *.sln *.vcxproj *.vcxproj.filters *.props @echo @echo All is done. @echo Now run @@ -305,8 +325,10 @@ endif # Special target to create an AmigaOS snapshot installation aos4dist: $(EXECUTABLE) mkdir -p $(AOS4PATH) + mkdir -p $(AOS4PATH)/themes + mkdir -p $(AOS4PATH)/extras $(STRIP) $(EXECUTABLE) -o $(AOS4PATH)/$(EXECUTABLE) - cp icons/scummvm.info $(AOS4PATH)/$(EXECUTABLE).info + cp ${srcdir}/icons/scummvm.info $(AOS4PATH)/$(EXECUTABLE).info cp $(DIST_FILES_THEMES) $(AOS4PATH)/themes/ ifdef DIST_FILES_ENGINEDATA cp $(DIST_FILES_ENGINEDATA) $(AOS4PATH)/extras/ @@ -334,7 +356,6 @@ endif cp $(srcdir)/dists/ps3/PIC1.PNG ps3pkg/ sfo.py -f $(srcdir)/dists/ps3/sfo.xml ps3pkg/PARAM.SFO pkg.py --contentid UP0001-SCUM12000_00-0000000000000000 ps3pkg/ scummvm-ps3.pkg - package_finalize scummvm-ps3.pkg ps3run: $(EXECUTABLE) $(STRIP) $(EXECUTABLE) @@ -343,4 +364,4 @@ ps3run: $(EXECUTABLE) ps3load $(EXECUTABLE).self # Mark special targets as phony -.PHONY: deb bundle osxsnap win32dist install uninstall ps3pkg +.PHONY: deb bundle osxsnap win32dist install uninstall ps3pkg ps3run diff --git a/test/common/bitstream.h b/test/common/bitstream.h new file mode 100644 index 0000000000..3f6a15fcd8 --- /dev/null +++ b/test/common/bitstream.h @@ -0,0 +1,152 @@ +#include <cxxtest/TestSuite.h> + +#include "common/bitstream.h" +#include "common/memstream.h" + +class BitStreamTestSuite : public CxxTest::TestSuite +{ + public: + void test_get_bit() { + byte contents[] = { 'a' }; + + Common::MemoryReadStream ms(contents, sizeof(contents)); + + Common::BitStream8MSB bs(ms); + TS_ASSERT_EQUALS(bs.pos(), 0u); + TS_ASSERT_EQUALS(bs.getBit(), 0u); + TS_ASSERT_EQUALS(bs.getBit(), 1u); + TS_ASSERT_EQUALS(bs.getBit(), 1u); + TS_ASSERT_EQUALS(bs.pos(), 3u); + TS_ASSERT(!bs.eos()); + } + + void test_get_bits() { + byte contents[] = { 'a', 'b' }; + + Common::MemoryReadStream ms(contents, sizeof(contents)); + + Common::BitStream8MSB bs(ms); + TS_ASSERT_EQUALS(bs.pos(), 0u); + TS_ASSERT_EQUALS(bs.getBits(3), 3u); + TS_ASSERT_EQUALS(bs.pos(), 3u); + TS_ASSERT_EQUALS(bs.getBits(8), 11u); + TS_ASSERT_EQUALS(bs.pos(), 11u); + TS_ASSERT(!bs.eos()); + } + + void test_skip() { + byte contents[] = { 'a', 'b' }; + + Common::MemoryReadStream ms(contents, sizeof(contents)); + + Common::BitStream8MSB bs(ms); + TS_ASSERT_EQUALS(bs.pos(), 0u); + bs.skip(5); + TS_ASSERT_EQUALS(bs.pos(), 5u); + bs.skip(4); + TS_ASSERT_EQUALS(bs.pos(), 9u); + TS_ASSERT_EQUALS(bs.getBits(3), 6u); + TS_ASSERT(!bs.eos()); + } + + void test_rewind() { + byte contents[] = { 'a' }; + + Common::MemoryReadStream ms(contents, sizeof(contents)); + + Common::BitStream8MSB bs(ms); + TS_ASSERT_EQUALS(bs.pos(), 0u); + bs.skip(5); + TS_ASSERT_EQUALS(bs.pos(), 5u); + bs.rewind(); + TS_ASSERT_EQUALS(bs.pos(), 0u); + TS_ASSERT_EQUALS(bs.getBits(3), 3u); + TS_ASSERT(!bs.eos()); + + TS_ASSERT_EQUALS(bs.size(), 8u); + } + + void test_peek_bit() { + byte contents[] = { 'a' }; + + Common::MemoryReadStream ms(contents, sizeof(contents)); + + Common::BitStream8MSB bs(ms); + TS_ASSERT_EQUALS(bs.pos(), 0u); + TS_ASSERT_EQUALS(bs.peekBit(), 0u); + TS_ASSERT_EQUALS(bs.pos(), 0u); + TS_ASSERT_EQUALS(bs.getBit(), 0u); + TS_ASSERT_EQUALS(bs.pos(), 1u); + TS_ASSERT_EQUALS(bs.peekBit(), 1u); + TS_ASSERT_EQUALS(bs.pos(), 1u); + TS_ASSERT(!bs.eos()); + } + + void test_peek_bits() { + byte contents[] = { 'a', 'b' }; + + Common::MemoryReadStream ms(contents, sizeof(contents)); + + Common::BitStream8MSB bs(ms); + TS_ASSERT_EQUALS(bs.pos(), 0u); + TS_ASSERT_EQUALS(bs.peekBits(3), 3u); + TS_ASSERT_EQUALS(bs.pos(), 0u); + bs.skip(3); + TS_ASSERT_EQUALS(bs.pos(), 3u); + TS_ASSERT_EQUALS(bs.peekBits(8), 11u); + TS_ASSERT_EQUALS(bs.pos(), 3u); + bs.skip(8); + TS_ASSERT_EQUALS(bs.pos(), 11u); + TS_ASSERT_EQUALS(bs.peekBits(5), 2u); + TS_ASSERT(!bs.eos()); + } + + void test_eos() { + byte contents[] = { 'a', 'b' }; + + Common::MemoryReadStream ms(contents, sizeof(contents)); + + Common::BitStream8MSB bs(ms); + bs.skip(11); + TS_ASSERT_EQUALS(bs.pos(), 11u); + TS_ASSERT_EQUALS(bs.getBits(5), 2u); + TS_ASSERT(bs.eos()); + + bs.rewind(); + TS_ASSERT_EQUALS(bs.pos(), 0u); + TS_ASSERT(!bs.eos()); + } + + void test_get_bits_lsb() { + byte contents[] = { 'a', 'b' }; + + Common::MemoryReadStream ms(contents, sizeof(contents)); + + Common::BitStream8LSB bs(ms); + TS_ASSERT_EQUALS(bs.pos(), 0u); + TS_ASSERT_EQUALS(bs.getBits(3), 1u); + TS_ASSERT_EQUALS(bs.pos(), 3u); + TS_ASSERT_EQUALS(bs.getBits(8), 76u); + TS_ASSERT_EQUALS(bs.pos(), 11u); + TS_ASSERT(!bs.eos()); + } + + void test_peek_bits_lsb() { + byte contents[] = { 'a', 'b' }; + + Common::MemoryReadStream ms(contents, sizeof(contents)); + + Common::BitStream8LSB bs(ms); + TS_ASSERT_EQUALS(bs.pos(), 0u); + TS_ASSERT_EQUALS(bs.peekBits(3), 1u); + TS_ASSERT_EQUALS(bs.pos(), 0u); + bs.skip(3); + TS_ASSERT_EQUALS(bs.pos(), 3u); + TS_ASSERT_EQUALS(bs.peekBits(8), 76u); + TS_ASSERT_EQUALS(bs.pos(), 3u); + bs.skip(8); + TS_ASSERT_EQUALS(bs.pos(), 11u); + TS_ASSERT_EQUALS(bs.peekBits(5), 12u); + TS_ASSERT(!bs.eos()); + } +}; diff --git a/test/common/memorywritestream.h b/test/common/memorywritestream.h new file mode 100644 index 0000000000..43a137a9f3 --- /dev/null +++ b/test/common/memorywritestream.h @@ -0,0 +1,31 @@ +#include <cxxtest/TestSuite.h> + +#include "common/memstream.h" + +class MemoryWriteStreamTestSuite : public CxxTest::TestSuite { + public: + void test_err() { + byte temp = 0; + + Common::MemoryWriteStream stream(&temp, 0); + TS_ASSERT(!stream.err()); + + // Make sure the error indicator gets set + stream.write(&temp, 1); + TS_ASSERT(stream.err()); + + // Test whether the error indicator can be cleared + stream.clearErr(); + TS_ASSERT(!stream.err()); + } + + void test_write() { + byte buffer[7] = {}; + Common::MemoryWriteStream stream(buffer, sizeof(buffer)); + + const byte data[7] = { 7, 4, 3, 0, 10, 12, 1 }; + stream.write(data, sizeof(data)); + TS_ASSERT(memcmp(buffer, data, sizeof(data)) == 0); + TS_ASSERT(!stream.err()); + } +}; diff --git a/video/avi_decoder.cpp b/video/avi_decoder.cpp index 2ea7e8d90e..6fe9c773b8 100644 --- a/video/avi_decoder.cpp +++ b/video/avi_decoder.cpp @@ -42,106 +42,128 @@ namespace Video { -/* +#define UNKNOWN_HEADER(a) error("Unknown header found -- \'%s\'", tag2str(a)) + +// IDs used throughout the AVI files +// that will be handled by this player +#define ID_RIFF MKTAG('R','I','F','F') +#define ID_AVI MKTAG('A','V','I',' ') +#define ID_LIST MKTAG('L','I','S','T') +#define ID_HDRL MKTAG('h','d','r','l') +#define ID_AVIH MKTAG('a','v','i','h') +#define ID_STRL MKTAG('s','t','r','l') +#define ID_STRH MKTAG('s','t','r','h') +#define ID_VIDS MKTAG('v','i','d','s') +#define ID_AUDS MKTAG('a','u','d','s') +#define ID_MIDS MKTAG('m','i','d','s') +#define ID_TXTS MKTAG('t','x','t','s') +#define ID_JUNK MKTAG('J','U','N','K') +#define ID_STRF MKTAG('s','t','r','f') +#define ID_MOVI MKTAG('m','o','v','i') +#define ID_REC MKTAG('r','e','c',' ') +#define ID_VEDT MKTAG('v','e','d','t') +#define ID_IDX1 MKTAG('i','d','x','1') +#define ID_STRD MKTAG('s','t','r','d') +#define ID_00AM MKTAG('0','0','A','M') +//#define ID_INFO MKTAG('I','N','F','O') + +// Codec tags +#define ID_RLE MKTAG('R','L','E',' ') +#define ID_CRAM MKTAG('C','R','A','M') +#define ID_MSVC MKTAG('m','s','v','c') +#define ID_WHAM MKTAG('W','H','A','M') +#define ID_CVID MKTAG('c','v','i','d') +#define ID_IV32 MKTAG('i','v','3','2') +#define ID_DUCK MKTAG('D','U','C','K') + static byte char2num(char c) { - return (c >= 48 && c <= 57) ? c - 48 : 0; + c = tolower((byte)c); + return (c >= 'a' && c <= 'f') ? c - 'a' + 10 : c - '0'; } -static byte getStreamNum(uint32 tag) { - return char2num((char)(tag >> 24)) * 16 + char2num((char)(tag >> 16)); +static byte getStreamIndex(uint32 tag) { + return char2num((tag >> 24) & 0xFF) << 4 | char2num((tag >> 16) & 0xFF); } -*/ static uint16 getStreamType(uint32 tag) { return tag & 0xffff; } -AviDecoder::AviDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundType) : _mixer(mixer) { - _soundType = soundType; - - _videoCodec = NULL; +AVIDecoder::AVIDecoder(Audio::Mixer::SoundType soundType) : _soundType(soundType) { _decodedHeader = false; - _audStream = NULL; - _fileStream = NULL; - _audHandle = new Audio::SoundHandle(); - _dirtyPalette = false; - memset(_palette, 0, sizeof(_palette)); - memset(&_wvInfo, 0, sizeof(PCMWAVEFORMAT)); - memset(&_bmInfo, 0, sizeof(BITMAPINFOHEADER)); - memset(&_vidsHeader, 0, sizeof(AVIStreamHeader)); - memset(&_audsHeader, 0, sizeof(AVIStreamHeader)); - memset(&_ixInfo, 0, sizeof(AVIOLDINDEX)); + _fileStream = 0; + memset(&_ixInfo, 0, sizeof(_ixInfo)); + memset(&_header, 0, sizeof(_header)); } -AviDecoder::~AviDecoder() { +AVIDecoder::~AVIDecoder() { close(); - delete _audHandle; } -void AviDecoder::runHandle(uint32 tag) { - assert (_fileStream); +void AVIDecoder::runHandle(uint32 tag) { + assert(_fileStream); if (_fileStream->eos()) return; - debug (3, "Decoding tag %s", tag2str(tag)); + debug(3, "Decoding tag %s", tag2str(tag)); switch (tag) { - case ID_RIFF: - /*_filesize = */_fileStream->readUint32LE(); - if (_fileStream->readUint32BE() != ID_AVI) - error("RIFF file is not an AVI video"); - break; - case ID_LIST: - handleList(); - break; - case ID_AVIH: - _header.size = _fileStream->readUint32LE(); - _header.microSecondsPerFrame = _fileStream->readUint32LE(); - _header.maxBytesPerSecond = _fileStream->readUint32LE(); - _header.padding = _fileStream->readUint32LE(); - _header.flags = _fileStream->readUint32LE(); - _header.totalFrames = _fileStream->readUint32LE(); - _header.initialFrames = _fileStream->readUint32LE(); - _header.streams = _fileStream->readUint32LE(); - _header.bufferSize = _fileStream->readUint32LE(); - _header.width = _fileStream->readUint32LE(); - _header.height = _fileStream->readUint32LE(); - //Ignore 16 bytes of reserved data - _fileStream->skip(16); - break; - case ID_STRH: - handleStreamHeader(); - break; - case ID_STRD: // Extra stream info, safe to ignore - case ID_VEDT: // Unknown, safe to ignore - case ID_JUNK: // Alignment bytes, should be ignored - { - uint32 junkSize = _fileStream->readUint32LE(); - _fileStream->skip(junkSize + (junkSize & 1)); // Alignment - } break; - case ID_IDX1: - _ixInfo.size = _fileStream->readUint32LE(); - _ixInfo.indices = new AVIOLDINDEX::Index[_ixInfo.size / 16]; - debug (0, "%d Indices", (_ixInfo.size / 16)); - for (uint32 i = 0; i < (_ixInfo.size / 16); i++) { - _ixInfo.indices[i].id = _fileStream->readUint32BE(); - _ixInfo.indices[i].flags = _fileStream->readUint32LE(); - _ixInfo.indices[i].offset = _fileStream->readUint32LE(); - _ixInfo.indices[i].size = _fileStream->readUint32LE(); - debug (0, "Index %d == Tag \'%s\', Offset = %d, Size = %d", i, tag2str(_ixInfo.indices[i].id), _ixInfo.indices[i].offset, _ixInfo.indices[i].size); - } - break; - default: - error ("Unknown tag \'%s\' found", tag2str(tag)); + case ID_RIFF: + /*_filesize = */_fileStream->readUint32LE(); + if (_fileStream->readUint32BE() != ID_AVI) + error("RIFF file is not an AVI video"); + break; + case ID_LIST: + handleList(); + break; + case ID_AVIH: + _header.size = _fileStream->readUint32LE(); + _header.microSecondsPerFrame = _fileStream->readUint32LE(); + _header.maxBytesPerSecond = _fileStream->readUint32LE(); + _header.padding = _fileStream->readUint32LE(); + _header.flags = _fileStream->readUint32LE(); + _header.totalFrames = _fileStream->readUint32LE(); + _header.initialFrames = _fileStream->readUint32LE(); + _header.streams = _fileStream->readUint32LE(); + _header.bufferSize = _fileStream->readUint32LE(); + _header.width = _fileStream->readUint32LE(); + _header.height = _fileStream->readUint32LE(); + // Ignore 16 bytes of reserved data + _fileStream->skip(16); + break; + case ID_STRH: + handleStreamHeader(); + break; + case ID_STRD: // Extra stream info, safe to ignore + case ID_VEDT: // Unknown, safe to ignore + case ID_JUNK: // Alignment bytes, should be ignored + { + uint32 junkSize = _fileStream->readUint32LE(); + _fileStream->skip(junkSize + (junkSize & 1)); // Alignment + } break; + case ID_IDX1: + _ixInfo.size = _fileStream->readUint32LE(); + _ixInfo.indices = new OldIndex::Index[_ixInfo.size / 16]; + debug(0, "%d Indices", (_ixInfo.size / 16)); + for (uint32 i = 0; i < (_ixInfo.size / 16); i++) { + _ixInfo.indices[i].id = _fileStream->readUint32BE(); + _ixInfo.indices[i].flags = _fileStream->readUint32LE(); + _ixInfo.indices[i].offset = _fileStream->readUint32LE(); + _ixInfo.indices[i].size = _fileStream->readUint32LE(); + debug(0, "Index %d == Tag \'%s\', Offset = %d, Size = %d", i, tag2str(_ixInfo.indices[i].id), _ixInfo.indices[i].offset, _ixInfo.indices[i].size); + } + break; + default: + error("Unknown tag \'%s\' found", tag2str(tag)); } } -void AviDecoder::handleList() { +void AVIDecoder::handleList() { uint32 listSize = _fileStream->readUint32LE() - 4; // Subtract away listType's 4 bytes uint32 listType = _fileStream->readUint32BE(); uint32 curPos = _fileStream->pos(); - debug (0, "Found LIST of type %s", tag2str(listType)); + debug(0, "Found LIST of type %s", tag2str(listType)); while ((_fileStream->pos() - curPos) < listSize) runHandle(_fileStream->readUint32BE()); @@ -151,12 +173,14 @@ void AviDecoder::handleList() { _decodedHeader = true; } -void AviDecoder::handleStreamHeader() { +void AVIDecoder::handleStreamHeader() { AVIStreamHeader sHeader; sHeader.size = _fileStream->readUint32LE(); sHeader.streamType = _fileStream->readUint32BE(); + if (sHeader.streamType == ID_MIDS || sHeader.streamType == ID_TXTS) - error ("Unhandled MIDI/Text stream"); + error("Unhandled MIDI/Text stream"); + sHeader.streamHandler = _fileStream->readUint32BE(); sHeader.flags = _fileStream->readUint32LE(); sHeader.priority = _fileStream->readUint16LE(); @@ -174,63 +198,67 @@ void AviDecoder::handleStreamHeader() { if (_fileStream->readUint32BE() != ID_STRF) error("Could not find STRF tag"); + uint32 strfSize = _fileStream->readUint32LE(); uint32 startPos = _fileStream->pos(); if (sHeader.streamType == ID_VIDS) { - _vidsHeader = sHeader; - - _bmInfo.size = _fileStream->readUint32LE(); - _bmInfo.width = _fileStream->readUint32LE(); - assert (_header.width == _bmInfo.width); - _bmInfo.height = _fileStream->readUint32LE(); - assert (_header.height == _bmInfo.height); - _bmInfo.planes = _fileStream->readUint16LE(); - _bmInfo.bitCount = _fileStream->readUint16LE(); - _bmInfo.compression = _fileStream->readUint32BE(); - _bmInfo.sizeImage = _fileStream->readUint32LE(); - _bmInfo.xPelsPerMeter = _fileStream->readUint32LE(); - _bmInfo.yPelsPerMeter = _fileStream->readUint32LE(); - _bmInfo.clrUsed = _fileStream->readUint32LE(); - _bmInfo.clrImportant = _fileStream->readUint32LE(); - - if (_bmInfo.bitCount == 8) { - if (_bmInfo.clrUsed == 0) - _bmInfo.clrUsed = 256; - - for (uint32 i = 0; i < _bmInfo.clrUsed; i++) { - _palette[i * 3 + 2] = _fileStream->readByte(); - _palette[i * 3 + 1] = _fileStream->readByte(); - _palette[i * 3] = _fileStream->readByte(); + BitmapInfoHeader bmInfo; + bmInfo.size = _fileStream->readUint32LE(); + bmInfo.width = _fileStream->readUint32LE(); + bmInfo.height = _fileStream->readUint32LE(); + bmInfo.planes = _fileStream->readUint16LE(); + bmInfo.bitCount = _fileStream->readUint16LE(); + bmInfo.compression = _fileStream->readUint32BE(); + bmInfo.sizeImage = _fileStream->readUint32LE(); + bmInfo.xPelsPerMeter = _fileStream->readUint32LE(); + bmInfo.yPelsPerMeter = _fileStream->readUint32LE(); + bmInfo.clrUsed = _fileStream->readUint32LE(); + bmInfo.clrImportant = _fileStream->readUint32LE(); + + if (bmInfo.clrUsed == 0) + bmInfo.clrUsed = 256; + + if (sHeader.streamHandler == 0) + sHeader.streamHandler = bmInfo.compression; + + AVIVideoTrack *track = new AVIVideoTrack(_header.totalFrames, sHeader, bmInfo); + + if (bmInfo.bitCount == 8) { + byte *palette = const_cast<byte *>(track->getPalette()); + for (uint32 i = 0; i < bmInfo.clrUsed; i++) { + palette[i * 3 + 2] = _fileStream->readByte(); + palette[i * 3 + 1] = _fileStream->readByte(); + palette[i * 3] = _fileStream->readByte(); _fileStream->readByte(); } - _dirtyPalette = true; + track->markPaletteDirty(); } - if (!_vidsHeader.streamHandler) - _vidsHeader.streamHandler = _bmInfo.compression; + addTrack(track); } else if (sHeader.streamType == ID_AUDS) { - _audsHeader = sHeader; - - _wvInfo.tag = _fileStream->readUint16LE(); - _wvInfo.channels = _fileStream->readUint16LE(); - _wvInfo.samplesPerSec = _fileStream->readUint32LE(); - _wvInfo.avgBytesPerSec = _fileStream->readUint32LE(); - _wvInfo.blockAlign = _fileStream->readUint16LE(); - _wvInfo.size = _fileStream->readUint16LE(); + PCMWaveFormat wvInfo; + wvInfo.tag = _fileStream->readUint16LE(); + wvInfo.channels = _fileStream->readUint16LE(); + wvInfo.samplesPerSec = _fileStream->readUint32LE(); + wvInfo.avgBytesPerSec = _fileStream->readUint32LE(); + wvInfo.blockAlign = _fileStream->readUint16LE(); + wvInfo.size = _fileStream->readUint16LE(); // AVI seems to treat the sampleSize as including the second // channel as well, so divide for our sake. - if (_wvInfo.channels == 2) - _audsHeader.sampleSize /= 2; + if (wvInfo.channels == 2) + sHeader.sampleSize /= 2; + + addTrack(new AVIAudioTrack(sHeader, wvInfo, _soundType)); } // Ensure that we're at the end of the chunk _fileStream->seek(startPos + strfSize); } -bool AviDecoder::loadStream(Common::SeekableReadStream *stream) { +bool AVIDecoder::loadStream(Common::SeekableReadStream *stream) { close(); _fileStream = stream; @@ -252,74 +280,31 @@ bool AviDecoder::loadStream(Common::SeekableReadStream *stream) { if (nextTag == ID_LIST) { _fileStream->readUint32BE(); // Skip size if (_fileStream->readUint32BE() != ID_MOVI) - error ("Expected 'movi' LIST"); - } else - error ("Expected 'movi' LIST"); - - // Now, create the codec - _videoCodec = createCodec(); - - // Initialize the video stuff too - _audStream = createAudioStream(); - if (_audStream) - _mixer->playStream(_soundType, _audHandle, _audStream, -1, getVolume(), getBalance()); - - debug (0, "Frames = %d, Dimensions = %d x %d", _header.totalFrames, _header.width, _header.height); - debug (0, "Frame Rate = %d", _vidsHeader.rate / _vidsHeader.scale); - if (_wvInfo.samplesPerSec != 0) - debug (0, "Sound Rate = %d", _wvInfo.samplesPerSec); - debug (0, "Video Codec = \'%s\'", tag2str(_vidsHeader.streamHandler)); - - if (!_videoCodec) - return false; + error("Expected 'movi' LIST"); + } else { + error("Expected 'movi' LIST"); + } return true; } -void AviDecoder::close() { - if (!_fileStream) - return; +void AVIDecoder::close() { + VideoDecoder::close(); delete _fileStream; _fileStream = 0; - - // Deinitialize sound - _mixer->stopHandle(*_audHandle); - _audStream = 0; - _decodedHeader = false; - delete _videoCodec; - _videoCodec = 0; - delete[] _ixInfo.indices; - _ixInfo.indices = 0; - - memset(_palette, 0, sizeof(_palette)); - memset(&_wvInfo, 0, sizeof(PCMWAVEFORMAT)); - memset(&_bmInfo, 0, sizeof(BITMAPINFOHEADER)); - memset(&_vidsHeader, 0, sizeof(AVIStreamHeader)); - memset(&_audsHeader, 0, sizeof(AVIStreamHeader)); - memset(&_ixInfo, 0, sizeof(AVIOLDINDEX)); - - reset(); -} - -uint32 AviDecoder::getTime() const { - if (_audStream) - return _mixer->getSoundElapsedTime(*_audHandle); - - return FixedRateVideoDecoder::getTime(); + memset(&_ixInfo, 0, sizeof(_ixInfo)); + memset(&_header, 0, sizeof(_header)); } -const Graphics::Surface *AviDecoder::decodeNextFrame() { +void AVIDecoder::readNextPacket() { uint32 nextTag = _fileStream->readUint32BE(); if (_fileStream->eos()) - return NULL; - - if (_curFrame == -1) - _startTime = g_system->getMillis(); + return; if (nextTag == ID_LIST) { // A list of audio/video chunks @@ -327,138 +312,174 @@ const Graphics::Surface *AviDecoder::decodeNextFrame() { int32 startPos = _fileStream->pos(); if (_fileStream->readUint32BE() != ID_REC) - error ("Expected 'rec ' LIST"); - - // Decode chunks in the list and see if we get a frame - const Graphics::Surface *frame = NULL; - while (_fileStream->pos() < startPos + (int32)listSize) { - const Graphics::Surface *temp = decodeNextFrame(); - if (temp) - frame = temp; - } + error("Expected 'rec ' LIST"); - return frame; - } else if (getStreamType(nextTag) == 'wb') { - // Audio Chunk - uint32 chunkSize = _fileStream->readUint32LE(); - queueAudioBuffer(chunkSize); - _fileStream->skip(chunkSize & 1); // Alignment - } else if (getStreamType(nextTag) == 'dc' || getStreamType(nextTag) == 'id' || - getStreamType(nextTag) == 'AM' || getStreamType(nextTag) == '32' || - getStreamType(nextTag) == 'iv') { - // Compressed Frame - _curFrame++; - uint32 chunkSize = _fileStream->readUint32LE(); - - if (chunkSize == 0) // Keep last frame on screen - return NULL; - - Common::SeekableReadStream *frameData = _fileStream->readStream(chunkSize); - const Graphics::Surface *surface = _videoCodec->decodeImage(frameData); - delete frameData; - _fileStream->skip(chunkSize & 1); // Alignment - return surface; - } else if (getStreamType(nextTag) == 'pc') { - // Palette Change - _fileStream->readUint32LE(); // Chunk size, not needed here - byte firstEntry = _fileStream->readByte(); - uint16 numEntries = _fileStream->readByte(); - _fileStream->readUint16LE(); // Reserved - - // 0 entries means all colors are going to be changed - if (numEntries == 0) - numEntries = 256; - - for (uint16 i = firstEntry; i < numEntries + firstEntry; i++) { - _palette[i * 3] = _fileStream->readByte(); - _palette[i * 3 + 1] = _fileStream->readByte(); - _palette[i * 3 + 2] = _fileStream->readByte(); - _fileStream->readByte(); // Flags that don't serve us any purpose - } + // Decode chunks in the list + while (_fileStream->pos() < startPos + (int32)listSize) + readNextPacket(); - _dirtyPalette = true; + return; + } else if (nextTag == ID_JUNK || nextTag == ID_IDX1) { + runHandle(nextTag); + return; + } - // No alignment necessary. It's always even. - } else if (nextTag == ID_JUNK) { - runHandle(ID_JUNK); - } else if (nextTag == ID_IDX1) { - runHandle(ID_IDX1); - } else - error("Tag = \'%s\', %d", tag2str(nextTag), _fileStream->pos()); + Track *track = getTrack(getStreamIndex(nextTag)); - return NULL; -} + if (!track) + error("Cannot get track from tag '%s'", tag2str(nextTag)); -Codec *AviDecoder::createCodec() { - switch (_vidsHeader.streamHandler) { - case ID_CRAM: - case ID_MSVC: - case ID_WHAM: - return new MSVideo1Decoder(_bmInfo.width, _bmInfo.height, _bmInfo.bitCount); - case ID_RLE: - return new MSRLEDecoder(_bmInfo.width, _bmInfo.height, _bmInfo.bitCount); - case ID_CVID: - return new CinepakDecoder(_bmInfo.bitCount); - case ID_IV32: - return new Indeo3Decoder(_bmInfo.width, _bmInfo.height); -#ifdef VIDEO_CODECS_TRUEMOTION1_H - case ID_DUCK: - return new TrueMotion1Decoder(_bmInfo.width, _bmInfo.height); -#endif - default: - warning ("Unknown/Unhandled compression format \'%s\'", tag2str(_vidsHeader.streamHandler)); + uint32 chunkSize = _fileStream->readUint32LE(); + Common::SeekableReadStream *chunk = 0; + + if (chunkSize != 0) { + chunk = _fileStream->readStream(chunkSize); + _fileStream->skip(chunkSize & 1); } - return NULL; + if (track->getTrackType() == Track::kTrackTypeAudio) { + if (getStreamType(nextTag) != MKTAG16('w', 'b')) + error("Invalid audio track tag '%s'", tag2str(nextTag)); + + assert(chunk); + ((AVIAudioTrack *)track)->queueSound(chunk); + } else { + AVIVideoTrack *videoTrack = (AVIVideoTrack *)track; + + if (getStreamType(nextTag) == MKTAG16('p', 'c')) { + // Palette Change + assert(chunk); + byte firstEntry = chunk->readByte(); + uint16 numEntries = chunk->readByte(); + chunk->readUint16LE(); // Reserved + + // 0 entries means all colors are going to be changed + if (numEntries == 0) + numEntries = 256; + + byte *palette = const_cast<byte *>(videoTrack->getPalette()); + + for (uint16 i = firstEntry; i < numEntries + firstEntry; i++) { + palette[i * 3] = chunk->readByte(); + palette[i * 3 + 1] = chunk->readByte(); + palette[i * 3 + 2] = chunk->readByte(); + chunk->readByte(); // Flags that don't serve us any purpose + } + + delete chunk; + videoTrack->markPaletteDirty(); + } else if (getStreamType(nextTag) == MKTAG16('d', 'b')) { + // TODO: Check if this really is uncompressed. Many videos + // falsely put compressed data in here. + error("Uncompressed AVI frame found"); + } else { + // Otherwise, assume it's a compressed frame + videoTrack->decodeFrame(chunk); + } + } } -Graphics::PixelFormat AviDecoder::getPixelFormat() const { - assert(_videoCodec); - return _videoCodec->getPixelFormat(); +AVIDecoder::AVIVideoTrack::AVIVideoTrack(int frameCount, const AVIStreamHeader &streamHeader, const BitmapInfoHeader &bitmapInfoHeader) + : _frameCount(frameCount), _vidsHeader(streamHeader), _bmInfo(bitmapInfoHeader) { + memset(_palette, 0, sizeof(_palette)); + _videoCodec = createCodec(); + _dirtyPalette = false; + _lastFrame = 0; + _curFrame = -1; } -Audio::QueuingAudioStream *AviDecoder::createAudioStream() { - if (_wvInfo.tag == kWaveFormatPCM || _wvInfo.tag == kWaveFormatDK3) - return Audio::makeQueuingAudioStream(_wvInfo.samplesPerSec, _wvInfo.channels == 2); - else if (_wvInfo.tag != kWaveFormatNone) // No sound - warning("Unsupported AVI audio format %d", _wvInfo.tag); +AVIDecoder::AVIVideoTrack::~AVIVideoTrack() { + delete _videoCodec; +} - return NULL; +void AVIDecoder::AVIVideoTrack::decodeFrame(Common::SeekableReadStream *stream) { + if (stream) { + if (_videoCodec) + _lastFrame = _videoCodec->decodeImage(stream); + } else { + // Empty frame + _lastFrame = 0; + } + + delete stream; + _curFrame++; } -void AviDecoder::queueAudioBuffer(uint32 chunkSize) { - // Return if we haven't created the queue (unsupported audio format) - if (!_audStream) { - _fileStream->skip(chunkSize); - return; +Graphics::PixelFormat AVIDecoder::AVIVideoTrack::getPixelFormat() const { + if (_videoCodec) + return _videoCodec->getPixelFormat(); + + return Graphics::PixelFormat(); +} + +Codec *AVIDecoder::AVIVideoTrack::createCodec() { + switch (_vidsHeader.streamHandler) { + case ID_CRAM: + case ID_MSVC: + case ID_WHAM: + return new MSVideo1Decoder(_bmInfo.width, _bmInfo.height, _bmInfo.bitCount); + case ID_RLE: + return new MSRLEDecoder(_bmInfo.width, _bmInfo.height, _bmInfo.bitCount); + case ID_CVID: + return new CinepakDecoder(_bmInfo.bitCount); + case ID_IV32: + return new Indeo3Decoder(_bmInfo.width, _bmInfo.height); +#ifdef VIDEO_CODECS_TRUEMOTION1_H + case ID_DUCK: + return new TrueMotion1Decoder(_bmInfo.width, _bmInfo.height); +#endif + default: + warning("Unknown/Unhandled compression format \'%s\'", tag2str(_vidsHeader.streamHandler)); } - Common::SeekableReadStream *stream = _fileStream->readStream(chunkSize); + return 0; +} - if (_wvInfo.tag == kWaveFormatPCM) { - byte flags = 0; - if (_audsHeader.sampleSize == 2) - flags |= Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN; - else - flags |= Audio::FLAG_UNSIGNED; +AVIDecoder::AVIAudioTrack::AVIAudioTrack(const AVIStreamHeader &streamHeader, const PCMWaveFormat &waveFormat, Audio::Mixer::SoundType soundType) + : _audsHeader(streamHeader), _wvInfo(waveFormat), _soundType(soundType) { + _audStream = createAudioStream(); +} - if (_wvInfo.channels == 2) - flags |= Audio::FLAG_STEREO; +AVIDecoder::AVIAudioTrack::~AVIAudioTrack() { + delete _audStream; +} - _audStream->queueAudioStream(Audio::makeRawStream(stream, _wvInfo.samplesPerSec, flags, DisposeAfterUse::YES), DisposeAfterUse::YES); - } else if (_wvInfo.tag == kWaveFormatDK3) { - _audStream->queueAudioStream(Audio::makeADPCMStream(stream, DisposeAfterUse::YES, chunkSize, Audio::kADPCMDK3, _wvInfo.samplesPerSec, _wvInfo.channels, _wvInfo.blockAlign), DisposeAfterUse::YES); +void AVIDecoder::AVIAudioTrack::queueSound(Common::SeekableReadStream *stream) { + if (_audStream) { + if (_wvInfo.tag == kWaveFormatPCM) { + byte flags = 0; + if (_audsHeader.sampleSize == 2) + flags |= Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN; + else + flags |= Audio::FLAG_UNSIGNED; + + if (_wvInfo.channels == 2) + flags |= Audio::FLAG_STEREO; + + _audStream->queueAudioStream(Audio::makeRawStream(stream, _wvInfo.samplesPerSec, flags, DisposeAfterUse::YES), DisposeAfterUse::YES); + } else if (_wvInfo.tag == kWaveFormatMSADPCM) { + _audStream->queueAudioStream(Audio::makeADPCMStream(stream, DisposeAfterUse::YES, stream->size(), Audio::kADPCMMS, _wvInfo.samplesPerSec, _wvInfo.channels, _wvInfo.blockAlign), DisposeAfterUse::YES); + } else if (_wvInfo.tag == kWaveFormatMSIMAADPCM) { + _audStream->queueAudioStream(Audio::makeADPCMStream(stream, DisposeAfterUse::YES, stream->size(), Audio::kADPCMMSIma, _wvInfo.samplesPerSec, _wvInfo.channels, _wvInfo.blockAlign), DisposeAfterUse::YES); + } else if (_wvInfo.tag == kWaveFormatDK3) { + _audStream->queueAudioStream(Audio::makeADPCMStream(stream, DisposeAfterUse::YES, stream->size(), Audio::kADPCMDK3, _wvInfo.samplesPerSec, _wvInfo.channels, _wvInfo.blockAlign), DisposeAfterUse::YES); + } + } else { + delete stream; } } -void AviDecoder::updateVolume() { - if (g_system->getMixer()->isSoundHandleActive(*_audHandle)) - g_system->getMixer()->setChannelVolume(*_audHandle, getVolume()); +Audio::AudioStream *AVIDecoder::AVIAudioTrack::getAudioStream() const { + return _audStream; } -void AviDecoder::updateBalance() { - if (g_system->getMixer()->isSoundHandleActive(*_audHandle)) - g_system->getMixer()->setChannelBalance(*_audHandle, getBalance()); +Audio::QueuingAudioStream *AVIDecoder::AVIAudioTrack::createAudioStream() { + if (_wvInfo.tag == kWaveFormatPCM || _wvInfo.tag == kWaveFormatMSADPCM || _wvInfo.tag == kWaveFormatMSIMAADPCM || _wvInfo.tag == kWaveFormatDK3) + return Audio::makeQueuingAudioStream(_wvInfo.samplesPerSec, _wvInfo.channels == 2); + else if (_wvInfo.tag != kWaveFormatNone) // No sound + warning("Unsupported AVI audio format %d", _wvInfo.tag); + + return 0; } } // End of namespace Video diff --git a/video/avi_decoder.h b/video/avi_decoder.h index fb4dae6711..34a67f4c28 100644 --- a/video/avi_decoder.h +++ b/video/avi_decoder.h @@ -47,196 +47,185 @@ namespace Video { class Codec; -#define UNKNOWN_HEADER(a) error("Unknown header found -- \'%s\'", tag2str(a)) - -// IDs used throughout the AVI files -// that will be handled by this player -#define ID_RIFF MKTAG('R','I','F','F') -#define ID_AVI MKTAG('A','V','I',' ') -#define ID_LIST MKTAG('L','I','S','T') -#define ID_HDRL MKTAG('h','d','r','l') -#define ID_AVIH MKTAG('a','v','i','h') -#define ID_STRL MKTAG('s','t','r','l') -#define ID_STRH MKTAG('s','t','r','h') -#define ID_VIDS MKTAG('v','i','d','s') -#define ID_AUDS MKTAG('a','u','d','s') -#define ID_MIDS MKTAG('m','i','d','s') -#define ID_TXTS MKTAG('t','x','t','s') -#define ID_JUNK MKTAG('J','U','N','K') -#define ID_STRF MKTAG('s','t','r','f') -#define ID_MOVI MKTAG('m','o','v','i') -#define ID_REC MKTAG('r','e','c',' ') -#define ID_VEDT MKTAG('v','e','d','t') -#define ID_IDX1 MKTAG('i','d','x','1') -#define ID_STRD MKTAG('s','t','r','d') -#define ID_00AM MKTAG('0','0','A','M') -//#define ID_INFO MKTAG('I','N','F','O') - -// Codec tags -#define ID_RLE MKTAG('R','L','E',' ') -#define ID_CRAM MKTAG('C','R','A','M') -#define ID_MSVC MKTAG('m','s','v','c') -#define ID_WHAM MKTAG('W','H','A','M') -#define ID_CVID MKTAG('c','v','i','d') -#define ID_IV32 MKTAG('i','v','3','2') -#define ID_DUCK MKTAG('D','U','C','K') - -struct BITMAPINFOHEADER { - uint32 size; - uint32 width; - uint32 height; - uint16 planes; - uint16 bitCount; - uint32 compression; - uint32 sizeImage; - uint32 xPelsPerMeter; - uint32 yPelsPerMeter; - uint32 clrUsed; - uint32 clrImportant; -}; - -struct WAVEFORMAT { - uint16 tag; - uint16 channels; - uint32 samplesPerSec; - uint32 avgBytesPerSec; - uint16 blockAlign; -}; - -struct PCMWAVEFORMAT : public WAVEFORMAT { - uint16 size; -}; - -struct WAVEFORMATEX : public WAVEFORMAT { - uint16 bitsPerSample; - uint16 size; -}; - -struct AVIOLDINDEX { - uint32 size; - struct Index { - uint32 id; - uint32 flags; - uint32 offset; - uint32 size; - } *indices; -}; - -// Index Flags -enum IndexFlags { - AVIIF_INDEX = 0x10 -}; - -// Audio Codecs -enum { - kWaveFormatNone = 0, - kWaveFormatPCM = 1, - kWaveFormatDK3 = 98 -}; - -struct AVIHeader { - uint32 size; - uint32 microSecondsPerFrame; - uint32 maxBytesPerSecond; - uint32 padding; - uint32 flags; - uint32 totalFrames; - uint32 initialFrames; - uint32 streams; - uint32 bufferSize; - uint32 width; - uint32 height; -}; - -// Flags from the AVIHeader -enum AviFlags { - AVIF_HASINDEX = 0x00000010, - AVIF_MUSTUSEINDEX = 0x00000020, - AVIF_ISINTERLEAVED = 0x00000100, - AVIF_TRUSTCKTYPE = 0x00000800, - AVIF_WASCAPTUREFILE = 0x00010000, - AVIF_WASCOPYRIGHTED = 0x00020000 -}; - -struct AVIStreamHeader { - uint32 size; - uint32 streamType; - uint32 streamHandler; - uint32 flags; - uint16 priority; - uint16 language; - uint32 initialFrames; - uint32 scale; - uint32 rate; - uint32 start; - uint32 length; - uint32 bufferSize; - uint32 quality; - uint32 sampleSize; - Common::Rect frame; -}; - /** * Decoder for AVI videos. * * Video decoder used in engines: * - sci */ -class AviDecoder : public FixedRateVideoDecoder { +class AVIDecoder : public VideoDecoder { public: - AviDecoder(Audio::Mixer *mixer, - Audio::Mixer::SoundType soundType = Audio::Mixer::kPlainSoundType); - virtual ~AviDecoder(); + AVIDecoder(Audio::Mixer::SoundType soundType = Audio::Mixer::kPlainSoundType); + virtual ~AVIDecoder(); bool loadStream(Common::SeekableReadStream *stream); void close(); - - bool isVideoLoaded() const { return _fileStream != 0; } uint16 getWidth() const { return _header.width; } uint16 getHeight() const { return _header.height; } - uint32 getFrameCount() const { return _header.totalFrames; } - uint32 getTime() const; - const Graphics::Surface *decodeNextFrame(); - Graphics::PixelFormat getPixelFormat() const; - const byte *getPalette() { _dirtyPalette = false; return _palette; } - bool hasDirtyPalette() const { return _dirtyPalette; } protected: - // VideoDecoder API - void updateVolume(); - void updateBalance(); - - // FixedRateVideoDecoder API - Common::Rational getFrameRate() const { return Common::Rational(_vidsHeader.rate, _vidsHeader.scale); } + void readNextPacket(); private: - Audio::Mixer *_mixer; - BITMAPINFOHEADER _bmInfo; - PCMWAVEFORMAT _wvInfo; - AVIOLDINDEX _ixInfo; + struct BitmapInfoHeader { + uint32 size; + uint32 width; + uint32 height; + uint16 planes; + uint16 bitCount; + uint32 compression; + uint32 sizeImage; + uint32 xPelsPerMeter; + uint32 yPelsPerMeter; + uint32 clrUsed; + uint32 clrImportant; + }; + + struct WaveFormat { + uint16 tag; + uint16 channels; + uint32 samplesPerSec; + uint32 avgBytesPerSec; + uint16 blockAlign; + }; + + struct PCMWaveFormat : public WaveFormat { + uint16 size; + }; + + struct WaveFormatEX : public WaveFormat { + uint16 bitsPerSample; + uint16 size; + }; + + struct OldIndex { + uint32 size; + struct Index { + uint32 id; + uint32 flags; + uint32 offset; + uint32 size; + } *indices; + }; + + // Index Flags + enum IndexFlags { + AVIIF_INDEX = 0x10 + }; + + struct AVIHeader { + uint32 size; + uint32 microSecondsPerFrame; + uint32 maxBytesPerSecond; + uint32 padding; + uint32 flags; + uint32 totalFrames; + uint32 initialFrames; + uint32 streams; + uint32 bufferSize; + uint32 width; + uint32 height; + }; + + // Flags from the AVIHeader + enum AVIFlags { + AVIF_HASINDEX = 0x00000010, + AVIF_MUSTUSEINDEX = 0x00000020, + AVIF_ISINTERLEAVED = 0x00000100, + AVIF_TRUSTCKTYPE = 0x00000800, + AVIF_WASCAPTUREFILE = 0x00010000, + AVIF_WASCOPYRIGHTED = 0x00020000 + }; + + struct AVIStreamHeader { + uint32 size; + uint32 streamType; + uint32 streamHandler; + uint32 flags; + uint16 priority; + uint16 language; + uint32 initialFrames; + uint32 scale; + uint32 rate; + uint32 start; + uint32 length; + uint32 bufferSize; + uint32 quality; + uint32 sampleSize; + Common::Rect frame; + }; + + class AVIVideoTrack : public FixedRateVideoTrack { + public: + AVIVideoTrack(int frameCount, const AVIStreamHeader &streamHeader, const BitmapInfoHeader &bitmapInfoHeader); + ~AVIVideoTrack(); + + void decodeFrame(Common::SeekableReadStream *stream); + + uint16 getWidth() const { return _bmInfo.width; } + uint16 getHeight() const { return _bmInfo.height; } + Graphics::PixelFormat getPixelFormat() const; + int getCurFrame() const { return _curFrame; } + int getFrameCount() const { return _frameCount; } + const Graphics::Surface *decodeNextFrame() { return _lastFrame; } + const byte *getPalette() const { _dirtyPalette = false; return _palette; } + bool hasDirtyPalette() const { return _dirtyPalette; } + void markPaletteDirty() { _dirtyPalette = true; } + + protected: + Common::Rational getFrameRate() const { return Common::Rational(_vidsHeader.rate, _vidsHeader.scale); } + + private: + AVIStreamHeader _vidsHeader; + BitmapInfoHeader _bmInfo; + byte _palette[3 * 256]; + mutable bool _dirtyPalette; + int _frameCount, _curFrame; + + Codec *_videoCodec; + const Graphics::Surface *_lastFrame; + Codec *createCodec(); + }; + + class AVIAudioTrack : public AudioTrack { + public: + AVIAudioTrack(const AVIStreamHeader &streamHeader, const PCMWaveFormat &waveFormat, Audio::Mixer::SoundType soundType); + ~AVIAudioTrack(); + + void queueSound(Common::SeekableReadStream *stream); + Audio::Mixer::SoundType getSoundType() const { return _soundType; } + + protected: + Audio::AudioStream *getAudioStream() const; + + private: + // Audio Codecs + enum { + kWaveFormatNone = 0, + kWaveFormatPCM = 1, + kWaveFormatMSADPCM = 2, + kWaveFormatMSIMAADPCM = 17, + kWaveFormatDK3 = 98 // rogue format number + }; + + AVIStreamHeader _audsHeader; + PCMWaveFormat _wvInfo; + Audio::Mixer::SoundType _soundType; + Audio::QueuingAudioStream *_audStream; + Audio::QueuingAudioStream *createAudioStream(); + }; + + OldIndex _ixInfo; AVIHeader _header; - AVIStreamHeader _vidsHeader; - AVIStreamHeader _audsHeader; - byte _palette[3 * 256]; - bool _dirtyPalette; Common::SeekableReadStream *_fileStream; bool _decodedHeader; - Codec *_videoCodec; - Codec *createCodec(); - Audio::Mixer::SoundType _soundType; void runHandle(uint32 tag); void handleList(); void handleStreamHeader(); - void handlePalChange(); - - Audio::SoundHandle *_audHandle; - Audio::QueuingAudioStream *_audStream; - Audio::QueuingAudioStream *createAudioStream(); - void queueAudioBuffer(uint32 chunkSize); }; } // End of namespace Video diff --git a/video/bink_decoder.cpp b/video/bink_decoder.cpp index 538487f067..45dec0887b 100644 --- a/video/bink_decoder.cpp +++ b/video/bink_decoder.cpp @@ -24,6 +24,7 @@ // based quite heavily on the Bink decoder found in FFmpeg. // Many thanks to Kostya Shishkov for doing the hard work. +#include "audio/audiostream.h" #include "audio/decoders/raw.h" #include "common/util.h" @@ -60,139 +61,108 @@ static const uint32 kDCStartBits = 11; namespace Video { -BinkDecoder::VideoFrame::VideoFrame() : bits(0) { -} - -BinkDecoder::VideoFrame::~VideoFrame() { - delete bits; +BinkDecoder::BinkDecoder() { + _bink = 0; } - -BinkDecoder::AudioTrack::AudioTrack() : bits(0), bands(0), rdft(0), dct(0) { +BinkDecoder::~BinkDecoder() { + close(); } -BinkDecoder::AudioTrack::~AudioTrack() { - delete bits; - - delete[] bands; - - delete rdft; - delete dct; -} +bool BinkDecoder::loadStream(Common::SeekableReadStream *stream) { + close(); + uint32 id = stream->readUint32BE(); + if ((id != kBIKfID) && (id != kBIKgID) && (id != kBIKhID) && (id != kBIKiID)) + return false; -BinkDecoder::BinkDecoder() { - _bink = 0; - _audioTrack = 0; + uint32 fileSize = stream->readUint32LE() + 8; + uint32 frameCount = stream->readUint32LE(); + uint32 largestFrameSize = stream->readUint32LE(); - for (int i = 0; i < 16; i++) - _huffman[i] = 0; + if (largestFrameSize > fileSize) { + warning("Largest frame size greater than file size"); + return false; + } - for (int i = 0; i < kSourceMAX; i++) { - _bundles[i].countLength = 0; + stream->skip(4); - _bundles[i].huffman.index = 0; - for (int j = 0; j < 16; j++) - _bundles[i].huffman.symbols[j] = j; + uint32 width = stream->readUint32LE(); + uint32 height = stream->readUint32LE(); - _bundles[i].data = 0; - _bundles[i].dataEnd = 0; - _bundles[i].curDec = 0; - _bundles[i].curPtr = 0; + uint32 frameRateNum = stream->readUint32LE(); + uint32 frameRateDen = stream->readUint32LE(); + if (frameRateNum == 0 || frameRateDen == 0) { + warning("Invalid frame rate (%d/%d)", frameRateNum, frameRateDen); + return false; } - for (int i = 0; i < 16; i++) { - _colHighHuffman[i].index = 0; - for (int j = 0; j < 16; j++) - _colHighHuffman[i].symbols[j] = j; - } + _bink = stream; - for (int i = 0; i < 4; i++) { - _curPlanes[i] = 0; - _oldPlanes[i] = 0; - } + uint32 videoFlags = _bink->readUint32LE(); - _audioStream = 0; -} + // BIKh and BIKi swap the chroma planes + addTrack(new BinkVideoTrack(width, height, getDefaultHighColorFormat(), frameCount, + Common::Rational(frameRateNum, frameRateDen), (id == kBIKhID || id == kBIKiID), videoFlags & kVideoFlagAlpha, id)); -void BinkDecoder::startAudio() { - if (_audioTrack < _audioTracks.size()) { - const AudioTrack &audio = _audioTracks[_audioTrack]; + uint32 audioTrackCount = _bink->readUint32LE(); - _audioStream = Audio::makeQueuingAudioStream(audio.outSampleRate, audio.outChannels == 2); - g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &_audioHandle, _audioStream, -1, getVolume(), getBalance()); - } // else no audio -} + if (audioTrackCount > 0) { + _audioTracks.reserve(audioTrackCount); -void BinkDecoder::stopAudio() { - if (_audioStream) { - g_system->getMixer()->stopHandle(_audioHandle); - _audioStream = 0; - } -} + _bink->skip(4 * audioTrackCount); -BinkDecoder::~BinkDecoder() { - close(); -} + // Reading audio track properties + for (uint32 i = 0; i < audioTrackCount; i++) { + AudioInfo track; -void BinkDecoder::close() { - reset(); + track.sampleRate = _bink->readUint16LE(); + track.flags = _bink->readUint16LE(); - // Stop audio - stopAudio(); + _audioTracks.push_back(track); - for (int i = 0; i < 4; i++) { - delete[] _curPlanes[i]; _curPlanes[i] = 0; - delete[] _oldPlanes[i]; _oldPlanes[i] = 0; + initAudioTrack(_audioTracks[i]); + } + + _bink->skip(4 * audioTrackCount); } - deinitBundles(); + // Reading video frame properties + _frames.resize(frameCount); + for (uint32 i = 0; i < frameCount; i++) { + _frames[i].offset = _bink->readUint32LE(); + _frames[i].keyFrame = _frames[i].offset & 1; - for (int i = 0; i < 16; i++) { - delete _huffman[i]; - _huffman[i] = 0; - } + _frames[i].offset &= ~1; - delete _bink; _bink = 0; - _surface.free(); + if (i != 0) + _frames[i - 1].size = _frames[i].offset - _frames[i - 1].offset; - _audioTrack = 0; + _frames[i].bits = 0; + } - for (int i = 0; i < kSourceMAX; i++) { - _bundles[i].countLength = 0; + _frames[frameCount - 1].size = _bink->size() - _frames[frameCount - 1].offset; - _bundles[i].huffman.index = 0; - for (int j = 0; j < 16; j++) - _bundles[i].huffman.symbols[j] = j; + return true; +} - _bundles[i].data = 0; - _bundles[i].dataEnd = 0; - _bundles[i].curDec = 0; - _bundles[i].curPtr = 0; - } +void BinkDecoder::close() { + VideoDecoder::close(); - for (int i = 0; i < 16; i++) { - _colHighHuffman[i].index = 0; - for (int j = 0; j < 16; j++) - _colHighHuffman[i].symbols[j] = j; - } + delete _bink; + _bink = 0; _audioTracks.clear(); _frames.clear(); } -uint32 BinkDecoder::getTime() const { - if (_audioStream && g_system->getMixer()->isSoundHandleActive(_audioHandle)) - return g_system->getMixer()->getSoundElapsedTime(_audioHandle) + _audioStartOffset; - - return g_system->getMillis() - _startTime; -} +void BinkDecoder::readNextPacket() { + BinkVideoTrack *videoTrack = (BinkVideoTrack *)getTrack(0); -const Graphics::Surface *BinkDecoder::decodeNextFrame() { - if (endOfVideo()) - return 0; + if (videoTrack->endOfTrack()) + return; - VideoFrame &frame = _frames[_curFrame + 1]; + VideoFrame &frame = _frames[videoTrack->getCurFrame() + 1]; if (!_bink->seek(frame.offset)) error("Bad bink seek"); @@ -200,7 +170,7 @@ const Graphics::Surface *BinkDecoder::decodeNextFrame() { uint32 frameSize = frame.size; for (uint32 i = 0; i < _audioTracks.size(); i++) { - AudioTrack &audio = _audioTracks[i]; + AudioInfo &audio = _audioTracks[i]; uint32 audioPacketLength = _bink->readUint32LE(); @@ -210,24 +180,21 @@ const Graphics::Surface *BinkDecoder::decodeNextFrame() { error("Audio packet too big for the frame"); if (audioPacketLength >= 4) { + // Get our track - audio index plus one as the first track is video + BinkAudioTrack *audioTrack = (BinkAudioTrack *)getTrack(i + 1); uint32 audioPacketStart = _bink->pos(); uint32 audioPacketEnd = _bink->pos() + audioPacketLength; - if (i == _audioTrack) { - // Only play one audio track + // Number of samples in bytes + audio.sampleCount = _bink->readUint32LE() / (2 * audio.channels); - // Number of samples in bytes - audio.sampleCount = _bink->readUint32LE() / (2 * audio.channels); + audio.bits = new Common::BitStream32LELSB(new Common::SeekableSubReadStream(_bink, + audioPacketStart + 4, audioPacketEnd), true); - audio.bits = - new Common::BitStream32LELSB(new Common::SeekableSubReadStream(_bink, - audioPacketStart + 4, audioPacketEnd), true); + audioTrack->decodePacket(); - audioPacket(audio); - - delete audio.bits; - audio.bits = 0; - } + delete audio.bits; + audio.bits = 0; _bink->seek(audioPacketEnd); @@ -238,83 +205,160 @@ const Graphics::Surface *BinkDecoder::decodeNextFrame() { uint32 videoPacketStart = _bink->pos(); uint32 videoPacketEnd = _bink->pos() + frameSize; - frame.bits = - new Common::BitStream32LELSB(new Common::SeekableSubReadStream(_bink, - videoPacketStart, videoPacketEnd), true); + frame.bits = new Common::BitStream32LELSB(new Common::SeekableSubReadStream(_bink, + videoPacketStart, videoPacketEnd), true); - videoPacket(frame); + videoTrack->decodePacket(frame); delete frame.bits; frame.bits = 0; +} - _curFrame++; - if (_curFrame == 0) - _startTime = g_system->getMillis(); +BinkDecoder::VideoFrame::VideoFrame() : bits(0) { +} - return &_surface; +BinkDecoder::VideoFrame::~VideoFrame() { + delete bits; } -void BinkDecoder::audioPacket(AudioTrack &audio) { - if (!_audioStream) - return; - int outSize = audio.frameLen * audio.channels; - while (audio.bits->pos() < audio.bits->size()) { - int16 *out = (int16 *)malloc(outSize * 2); - memset(out, 0, outSize * 2); +BinkDecoder::AudioInfo::AudioInfo() : bits(0), bands(0), rdft(0), dct(0) { +} - audioBlock(audio, out); +BinkDecoder::AudioInfo::~AudioInfo() { + delete bits; - byte flags = Audio::FLAG_16BITS; - if (audio.outChannels == 2) - flags |= Audio::FLAG_STEREO; + delete[] bands; -#ifdef SCUMM_LITTLE_ENDIAN - flags |= Audio::FLAG_LITTLE_ENDIAN; -#endif + delete rdft; + delete dct; +} + +BinkDecoder::BinkVideoTrack::BinkVideoTrack(uint32 width, uint32 height, const Graphics::PixelFormat &format, uint32 frameCount, const Common::Rational &frameRate, bool swapPlanes, bool hasAlpha, uint32 id) : + _frameCount(frameCount), _frameRate(frameRate), _swapPlanes(swapPlanes), _hasAlpha(hasAlpha), _id(id) { + _curFrame = -1; + + for (int i = 0; i < 16; i++) + _huffman[i] = 0; - _audioStream->queueBuffer((byte *)out, audio.blockSize * 2, DisposeAfterUse::YES, flags); + for (int i = 0; i < kSourceMAX; i++) { + _bundles[i].countLength = 0; - if (audio.bits->pos() & 0x1F) // next data block starts at a 32-byte boundary - audio.bits->skip(32 - (audio.bits->pos() & 0x1F)); + _bundles[i].huffman.index = 0; + for (int j = 0; j < 16; j++) + _bundles[i].huffman.symbols[j] = j; + + _bundles[i].data = 0; + _bundles[i].dataEnd = 0; + _bundles[i].curDec = 0; + _bundles[i].curPtr = 0; } + + for (int i = 0; i < 16; i++) { + _colHighHuffman[i].index = 0; + for (int j = 0; j < 16; j++) + _colHighHuffman[i].symbols[j] = j; + } + + // Make the surface even-sized: + _surfaceHeight = height; + _surfaceWidth = width; + + if (height & 1) { + _surfaceHeight++; + } + if (width & 1) { + _surfaceWidth++; + } + + _surface.create(_surfaceWidth, _surfaceHeight, format); + // Since we over-allocate to make surfaces even-sized + // we need to set the actual VIDEO size back into the + // surface. + _surface.h = height; + _surface.w = width; + + // Give the planes a bit extra space + width = _surface.w + 32; + height = _surface.h + 32; + + _curPlanes[0] = new byte[ width * height ]; // Y + _curPlanes[1] = new byte[(width >> 1) * (height >> 1)]; // U, 1/4 resolution + _curPlanes[2] = new byte[(width >> 1) * (height >> 1)]; // V, 1/4 resolution + _curPlanes[3] = new byte[ width * height ]; // A + _oldPlanes[0] = new byte[ width * height ]; // Y + _oldPlanes[1] = new byte[(width >> 1) * (height >> 1)]; // U, 1/4 resolution + _oldPlanes[2] = new byte[(width >> 1) * (height >> 1)]; // V, 1/4 resolution + _oldPlanes[3] = new byte[ width * height ]; // A + + // Initialize the video with solid black + memset(_curPlanes[0], 0, width * height ); + memset(_curPlanes[1], 0, (width >> 1) * (height >> 1)); + memset(_curPlanes[2], 0, (width >> 1) * (height >> 1)); + memset(_curPlanes[3], 255, width * height ); + memset(_oldPlanes[0], 0, width * height ); + memset(_oldPlanes[1], 0, (width >> 1) * (height >> 1)); + memset(_oldPlanes[2], 0, (width >> 1) * (height >> 1)); + memset(_oldPlanes[3], 255, width * height ); + + initBundles(); + initHuffman(); } -void BinkDecoder::videoPacket(VideoFrame &video) { - assert(video.bits); +BinkDecoder::BinkVideoTrack::~BinkVideoTrack() { + for (int i = 0; i < 4; i++) { + delete[] _curPlanes[i]; _curPlanes[i] = 0; + delete[] _oldPlanes[i]; _oldPlanes[i] = 0; + } + + deinitBundles(); + + for (int i = 0; i < 16; i++) { + delete _huffman[i]; + _huffman[i] = 0; + } + + _surface.free(); +} + +void BinkDecoder::BinkVideoTrack::decodePacket(VideoFrame &frame) { + assert(frame.bits); if (_hasAlpha) { if (_id == kBIKiID) - video.bits->skip(32); + frame.bits->skip(32); - decodePlane(video, 3, false); + decodePlane(frame, 3, false); } if (_id == kBIKiID) - video.bits->skip(32); + frame.bits->skip(32); for (int i = 0; i < 3; i++) { int planeIdx = ((i == 0) || !_swapPlanes) ? i : (i ^ 3); - decodePlane(video, planeIdx, i != 0); + decodePlane(frame, planeIdx, i != 0); - if (video.bits->pos() >= video.bits->size()) + if (frame.bits->pos() >= frame.bits->size()) break; } // Convert the YUV data we have to our format // We're ignoring alpha for now + // The width used here is the surface-width, and not the video-width + // to allow for odd-sized videos. assert(_curPlanes[0] && _curPlanes[1] && _curPlanes[2]); - Graphics::convertYUV420ToRGB(&_surface, _curPlanes[0], _curPlanes[1], _curPlanes[2], - _surface.w, _surface.h, _surface.w, _surface.w >> 1); + YUVToRGBMan.convert420(&_surface, Graphics::YUVToRGBManager::kScaleITU, _curPlanes[0], _curPlanes[1], _curPlanes[2], + _surfaceWidth, _surfaceHeight, _surfaceWidth, _surfaceWidth >> 1); // And swap the planes with the reference planes for (int i = 0; i < 4; i++) SWAP(_curPlanes[i], _oldPlanes[i]); -} -void BinkDecoder::decodePlane(VideoFrame &video, int planeIdx, bool isChroma) { + _curFrame++; +} +void BinkDecoder::BinkVideoTrack::decodePlane(VideoFrame &video, int planeIdx, bool isChroma) { uint32 blockWidth = isChroma ? ((_surface.w + 15) >> 4) : ((_surface.w + 7) >> 3); uint32 blockHeight = isChroma ? ((_surface.h + 15) >> 4) : ((_surface.h + 7) >> 3); uint32 width = isChroma ? (_surface.w >> 1) : _surface.w; @@ -371,48 +415,38 @@ void BinkDecoder::decodePlane(VideoFrame &video, int planeIdx, bool isChroma) { } switch (blockType) { - case kBlockSkip: - blockSkip(ctx); - break; - - case kBlockScaled: - blockScaled(ctx); - break; - - case kBlockMotion: - blockMotion(ctx); - break; - - case kBlockRun: - blockRun(ctx); - break; - - case kBlockResidue: - blockResidue(ctx); - break; - - case kBlockIntra: - blockIntra(ctx); - break; - - case kBlockFill: - blockFill(ctx); - break; - - case kBlockInter: - blockInter(ctx); - break; - - case kBlockPattern: - blockPattern(ctx); - break; - - case kBlockRaw: - blockRaw(ctx); - break; - - default: - error("Unknown block type: %d", blockType); + case kBlockSkip: + blockSkip(ctx); + break; + case kBlockScaled: + blockScaled(ctx); + break; + case kBlockMotion: + blockMotion(ctx); + break; + case kBlockRun: + blockRun(ctx); + break; + case kBlockResidue: + blockResidue(ctx); + break; + case kBlockIntra: + blockIntra(ctx); + break; + case kBlockFill: + blockFill(ctx); + break; + case kBlockInter: + blockInter(ctx); + break; + case kBlockPattern: + blockPattern(ctx); + break; + case kBlockRaw: + blockRaw(ctx); + break; + default: + error("Unknown block type: %d", blockType); } } @@ -424,7 +458,7 @@ void BinkDecoder::decodePlane(VideoFrame &video, int planeIdx, bool isChroma) { } -void BinkDecoder::readBundle(VideoFrame &video, Source source) { +void BinkDecoder::BinkVideoTrack::readBundle(VideoFrame &video, Source source) { if (source == kSourceColors) { for (int i = 0; i < 16; i++) readHuffman(video, _colHighHuffman[i]); @@ -439,12 +473,11 @@ void BinkDecoder::readBundle(VideoFrame &video, Source source) { _bundles[source].curPtr = _bundles[source].data; } -void BinkDecoder::readHuffman(VideoFrame &video, Huffman &huffman) { +void BinkDecoder::BinkVideoTrack::readHuffman(VideoFrame &video, Huffman &huffman) { huffman.index = video.bits->getBits(4); if (huffman.index == 0) { // The first tree always gives raw nibbles - for (int i = 0; i < 16; i++) huffman.symbols[i] = i; @@ -455,7 +488,6 @@ void BinkDecoder::readHuffman(VideoFrame &video, Huffman &huffman) { if (video.bits->getBit()) { // Symbol selection - memset(hasSymbol, 0, 16); uint8 length = video.bits->getBits(3); @@ -493,9 +525,9 @@ void BinkDecoder::readHuffman(VideoFrame &video, Huffman &huffman) { memcpy(huffman.symbols, in, 16); } -void BinkDecoder::mergeHuffmanSymbols(VideoFrame &video, byte *dst, const byte *src, int size) { +void BinkDecoder::BinkVideoTrack::mergeHuffmanSymbols(VideoFrame &video, byte *dst, const byte *src, int size) { const byte *src2 = src + size; - int size2 = size; + int size2 = size; do { if (!video.bits->getBit()) { @@ -510,197 +542,12 @@ void BinkDecoder::mergeHuffmanSymbols(VideoFrame &video, byte *dst, const byte * while (size--) *dst++ = *src++; + while (size2--) *dst++ = *src2++; } -bool BinkDecoder::loadStream(Common::SeekableReadStream *stream) { - Graphics::PixelFormat format = g_system->getScreenFormat(); - return loadStream(stream, format); -} - -bool BinkDecoder::loadStream(Common::SeekableReadStream *stream, const Graphics::PixelFormat &format) { - close(); - - _id = stream->readUint32BE(); - if ((_id != kBIKfID) && (_id != kBIKgID) && (_id != kBIKhID) && (_id != kBIKiID)) - return false; - - uint32 fileSize = stream->readUint32LE() + 8; - uint32 frameCount = stream->readUint32LE(); - uint32 largestFrameSize = stream->readUint32LE(); - - if (largestFrameSize > fileSize) { - warning("Largest frame size greater than file size"); - return false; - } - - stream->skip(4); - - uint32 width = stream->readUint32LE(); - uint32 height = stream->readUint32LE(); - - uint32 frameRateNum = stream->readUint32LE(); - uint32 frameRateDen = stream->readUint32LE(); - if (frameRateNum == 0 || frameRateDen == 0) { - warning("Invalid frame rate (%d/%d)", frameRateNum, frameRateDen); - return false; - } - - _frameRate = Common::Rational(frameRateNum, frameRateDen); - _bink = stream; - - _videoFlags = _bink->readUint32LE(); - - uint32 audioTrackCount = _bink->readUint32LE(); - - if (audioTrackCount > 1) { - warning("More than one audio track found. Using the first one"); - - _audioTrack = 0; - } - - if (audioTrackCount > 0) { - _audioTracks.reserve(audioTrackCount); - - _bink->skip(4 * audioTrackCount); - - // Reading audio track properties - for (uint32 i = 0; i < audioTrackCount; i++) { - AudioTrack track; - - track.sampleRate = _bink->readUint16LE(); - track.flags = _bink->readUint16LE(); - - _audioTracks.push_back(track); - - initAudioTrack(_audioTracks[i]); - } - - _bink->skip(4 * audioTrackCount); - } - - // Reading video frame properties - _frames.resize(frameCount); - for (uint32 i = 0; i < frameCount; i++) { - _frames[i].offset = _bink->readUint32LE(); - _frames[i].keyFrame = _frames[i].offset & 1; - - _frames[i].offset &= ~1; - - if (i != 0) - _frames[i - 1].size = _frames[i].offset - _frames[i - 1].offset; - - _frames[i].bits = 0; - } - - _frames[frameCount - 1].size = _bink->size() - _frames[frameCount - 1].offset; - - _hasAlpha = _videoFlags & kVideoFlagAlpha; - _swapPlanes = (_id == kBIKhID) || (_id == kBIKiID); // BIKh and BIKi swap the chroma planes - - _surface.create(width, height, format); - - // Give the planes a bit extra space - width = _surface.w + 32; - height = _surface.h + 32; - - _curPlanes[0] = new byte[ width * height ]; // Y - _curPlanes[1] = new byte[(width >> 1) * (height >> 1)]; // U, 1/4 resolution - _curPlanes[2] = new byte[(width >> 1) * (height >> 1)]; // V, 1/4 resolution - _curPlanes[3] = new byte[ width * height ]; // A - _oldPlanes[0] = new byte[ width * height ]; // Y - _oldPlanes[1] = new byte[(width >> 1) * (height >> 1)]; // U, 1/4 resolution - _oldPlanes[2] = new byte[(width >> 1) * (height >> 1)]; // V, 1/4 resolution - _oldPlanes[3] = new byte[ width * height ]; // A - - // Initialize the video with solid black - memset(_curPlanes[0], 0, width * height ); - memset(_curPlanes[1], 0, (width >> 1) * (height >> 1)); - memset(_curPlanes[2], 0, (width >> 1) * (height >> 1)); - memset(_curPlanes[3], 255, width * height ); - memset(_oldPlanes[0], 0, width * height ); - memset(_oldPlanes[1], 0, (width >> 1) * (height >> 1)); - memset(_oldPlanes[2], 0, (width >> 1) * (height >> 1)); - memset(_oldPlanes[3], 255, width * height ); - - initBundles(); - initHuffman(); - - startAudio(); - _audioStartOffset = 0; - - return true; -} - -void BinkDecoder::initAudioTrack(AudioTrack &audio) { - audio.sampleCount = 0; - audio.bits = 0; - - audio.channels = ((audio.flags & kAudioFlagStereo) != 0) ? 2 : 1; - audio.codec = ((audio.flags & kAudioFlagDCT ) != 0) ? kAudioCodecDCT : kAudioCodecRDFT; - - if (audio.channels > kAudioChannelsMax) - error("Too many audio channels: %d", audio.channels); - - uint32 frameLenBits; - // Calculate frame length - if (audio.sampleRate < 22050) - frameLenBits = 9; - else if(audio.sampleRate < 44100) - frameLenBits = 10; - else - frameLenBits = 11; - - audio.frameLen = 1 << frameLenBits; - - audio.outSampleRate = audio.sampleRate; - audio.outChannels = audio.channels; - - if (audio.codec == kAudioCodecRDFT) { - // RDFT audio already interleaves the samples correctly - - if (audio.channels == 2) - frameLenBits++; - - audio.sampleRate *= audio.channels; - audio.frameLen *= audio.channels; - audio.channels = 1; - } - - audio.overlapLen = audio.frameLen / 16; - audio.blockSize = (audio.frameLen - audio.overlapLen) * audio.channels; - audio.root = 2.0 / sqrt((double)audio.frameLen); - - uint32 sampleRateHalf = (audio.sampleRate + 1) / 2; - - // Calculate number of bands - for (audio.bandCount = 1; audio.bandCount < 25; audio.bandCount++) - if (sampleRateHalf <= binkCriticalFreqs[audio.bandCount - 1]) - break; - - audio.bands = new uint32[audio.bandCount + 1]; - - // Populate bands - audio.bands[0] = 1; - for (uint32 i = 1; i < audio.bandCount; i++) - audio.bands[i] = binkCriticalFreqs[i - 1] * (audio.frameLen / 2) / sampleRateHalf; - audio.bands[audio.bandCount] = audio.frameLen / 2; - - audio.first = true; - - for (uint8 i = 0; i < audio.channels; i++) - audio.coeffsPtr[i] = audio.coeffs + i * audio.frameLen; - - audio.codec = ((audio.flags & kAudioFlagDCT) != 0) ? kAudioCodecDCT : kAudioCodecRDFT; - - if (audio.codec == kAudioCodecRDFT) - audio.rdft = new Common::RDFT(frameLenBits, Common::RDFT::DFT_C2R); - else if (audio.codec == kAudioCodecDCT) - audio.dct = new Common::DCT(frameLenBits, Common::DCT::DCT_III); -} - -void BinkDecoder::initBundles() { +void BinkDecoder::BinkVideoTrack::initBundles() { uint32 bw = (_surface.w + 7) >> 3; uint32 bh = (_surface.h + 7) >> 3; uint32 blocks = bw * bh; @@ -710,8 +557,8 @@ void BinkDecoder::initBundles() { _bundles[i].dataEnd = _bundles[i].data + blocks * 64; } - uint32 cbw[2] = { (_surface.w + 7) >> 3, (_surface.w + 15) >> 4 }; - uint32 cw [2] = { _surface.w , _surface.w >> 1 }; + uint32 cbw[2] = { (uint32)((_surface.w + 7) >> 3), (uint32)((_surface.w + 15) >> 4) }; + uint32 cw [2] = { (uint32)( _surface.w ), (uint32)( _surface.w >> 1) }; // Calculate the lengths of an element count in bits for (int i = 0; i < 2; i++) { @@ -729,21 +576,21 @@ void BinkDecoder::initBundles() { } } -void BinkDecoder::deinitBundles() { +void BinkDecoder::BinkVideoTrack::deinitBundles() { for (int i = 0; i < kSourceMAX; i++) delete[] _bundles[i].data; } -void BinkDecoder::initHuffman() { +void BinkDecoder::BinkVideoTrack::initHuffman() { for (int i = 0; i < 16; i++) _huffman[i] = new Common::Huffman(binkHuffmanLengths[i][15], 16, binkHuffmanCodes[i], binkHuffmanLengths[i]); } -byte BinkDecoder::getHuffmanSymbol(VideoFrame &video, Huffman &huffman) { +byte BinkDecoder::BinkVideoTrack::getHuffmanSymbol(VideoFrame &video, Huffman &huffman) { return huffman.symbols[_huffman[huffman.index]->getSymbol(*video.bits)]; } -int32 BinkDecoder::getBundleValue(Source source) { +int32 BinkDecoder::BinkVideoTrack::getBundleValue(Source source) { if ((source < kSourceXOff) || (source == kSourceRun)) return *_bundles[source].curPtr++; @@ -757,7 +604,7 @@ int32 BinkDecoder::getBundleValue(Source source) { return ret; } -uint32 BinkDecoder::readBundleCount(VideoFrame &video, Bundle &bundle) { +uint32 BinkDecoder::BinkVideoTrack::readBundleCount(VideoFrame &video, Bundle &bundle) { if (!bundle.curDec || (bundle.curDec > bundle.curPtr)) return 0; @@ -768,7 +615,7 @@ uint32 BinkDecoder::readBundleCount(VideoFrame &video, Bundle &bundle) { return n; } -void BinkDecoder::blockSkip(DecodeContext &ctx) { +void BinkDecoder::BinkVideoTrack::blockSkip(DecodeContext &ctx) { byte *dest = ctx.dest; byte *prev = ctx.prev; @@ -776,7 +623,7 @@ void BinkDecoder::blockSkip(DecodeContext &ctx) { memcpy(dest, prev, 8); } -void BinkDecoder::blockScaledSkip(DecodeContext &ctx) { +void BinkDecoder::BinkVideoTrack::blockScaledSkip(DecodeContext &ctx) { byte *dest = ctx.dest; byte *prev = ctx.prev; @@ -784,7 +631,7 @@ void BinkDecoder::blockScaledSkip(DecodeContext &ctx) { memcpy(dest, prev, 16); } -void BinkDecoder::blockScaledRun(DecodeContext &ctx) { +void BinkDecoder::BinkVideoTrack::blockScaledRun(DecodeContext &ctx) { const uint8 *scan = binkPatterns[ctx.video->bits->getBits(4)]; int i = 0; @@ -820,7 +667,7 @@ void BinkDecoder::blockScaledRun(DecodeContext &ctx) { ctx.dest[ctx.coordScaledMap4[*scan]] = getBundleValue(kSourceColors); } -void BinkDecoder::blockScaledIntra(DecodeContext &ctx) { +void BinkDecoder::BinkVideoTrack::blockScaledIntra(DecodeContext &ctx) { int16 block[64]; memset(block, 0, 64 * sizeof(int16)); @@ -841,7 +688,7 @@ void BinkDecoder::blockScaledIntra(DecodeContext &ctx) { } } -void BinkDecoder::blockScaledFill(DecodeContext &ctx) { +void BinkDecoder::BinkVideoTrack::blockScaledFill(DecodeContext &ctx) { byte v = getBundleValue(kSourceColors); byte *dest = ctx.dest; @@ -849,7 +696,7 @@ void BinkDecoder::blockScaledFill(DecodeContext &ctx) { memset(dest, v, 16); } -void BinkDecoder::blockScaledPattern(DecodeContext &ctx) { +void BinkDecoder::BinkVideoTrack::blockScaledPattern(DecodeContext &ctx) { byte col[2]; for (int i = 0; i < 2; i++) @@ -865,7 +712,7 @@ void BinkDecoder::blockScaledPattern(DecodeContext &ctx) { } } -void BinkDecoder::blockScaledRaw(DecodeContext &ctx) { +void BinkDecoder::BinkVideoTrack::blockScaledRaw(DecodeContext &ctx) { byte row[8]; byte *dest1 = ctx.dest; @@ -880,32 +727,27 @@ void BinkDecoder::blockScaledRaw(DecodeContext &ctx) { } } -void BinkDecoder::blockScaled(DecodeContext &ctx) { +void BinkDecoder::BinkVideoTrack::blockScaled(DecodeContext &ctx) { BlockType blockType = (BlockType) getBundleValue(kSourceSubBlockTypes); switch (blockType) { - case kBlockRun: - blockScaledRun(ctx); - break; - - case kBlockIntra: - blockScaledIntra(ctx); - break; - - case kBlockFill: - blockScaledFill(ctx); - break; - - case kBlockPattern: - blockScaledPattern(ctx); - break; - - case kBlockRaw: - blockScaledRaw(ctx); - break; - - default: - error("Invalid 16x16 block type: %d", blockType); + case kBlockRun: + blockScaledRun(ctx); + break; + case kBlockIntra: + blockScaledIntra(ctx); + break; + case kBlockFill: + blockScaledFill(ctx); + break; + case kBlockPattern: + blockScaledPattern(ctx); + break; + case kBlockRaw: + blockScaledRaw(ctx); + break; + default: + error("Invalid 16x16 block type: %d", blockType); } ctx.blockX += 1; @@ -913,7 +755,7 @@ void BinkDecoder::blockScaled(DecodeContext &ctx) { ctx.prev += 8; } -void BinkDecoder::blockMotion(DecodeContext &ctx) { +void BinkDecoder::BinkVideoTrack::blockMotion(DecodeContext &ctx) { int8 xOff = getBundleValue(kSourceXOff); int8 yOff = getBundleValue(kSourceYOff); @@ -926,7 +768,7 @@ void BinkDecoder::blockMotion(DecodeContext &ctx) { memcpy(dest, prev, 8); } -void BinkDecoder::blockRun(DecodeContext &ctx) { +void BinkDecoder::BinkVideoTrack::blockRun(DecodeContext &ctx) { const uint8 *scan = binkPatterns[ctx.video->bits->getBits(4)]; int i = 0; @@ -953,7 +795,7 @@ void BinkDecoder::blockRun(DecodeContext &ctx) { ctx.dest[ctx.coordMap[*scan++]] = getBundleValue(kSourceColors); } -void BinkDecoder::blockResidue(DecodeContext &ctx) { +void BinkDecoder::BinkVideoTrack::blockResidue(DecodeContext &ctx) { blockMotion(ctx); byte v = ctx.video->bits->getBits(7); @@ -970,7 +812,7 @@ void BinkDecoder::blockResidue(DecodeContext &ctx) { dst[j] += src[j]; } -void BinkDecoder::blockIntra(DecodeContext &ctx) { +void BinkDecoder::BinkVideoTrack::blockIntra(DecodeContext &ctx) { int16 block[64]; memset(block, 0, 64 * sizeof(int16)); @@ -981,7 +823,7 @@ void BinkDecoder::blockIntra(DecodeContext &ctx) { IDCTPut(ctx, block); } -void BinkDecoder::blockFill(DecodeContext &ctx) { +void BinkDecoder::BinkVideoTrack::blockFill(DecodeContext &ctx) { byte v = getBundleValue(kSourceColors); byte *dest = ctx.dest; @@ -989,7 +831,7 @@ void BinkDecoder::blockFill(DecodeContext &ctx) { memset(dest, v, 8); } -void BinkDecoder::blockInter(DecodeContext &ctx) { +void BinkDecoder::BinkVideoTrack::blockInter(DecodeContext &ctx) { blockMotion(ctx); int16 block[64]; @@ -1002,7 +844,7 @@ void BinkDecoder::blockInter(DecodeContext &ctx) { IDCTAdd(ctx, block); } -void BinkDecoder::blockPattern(DecodeContext &ctx) { +void BinkDecoder::BinkVideoTrack::blockPattern(DecodeContext &ctx) { byte col[2]; for (int i = 0; i < 2; i++) @@ -1017,7 +859,7 @@ void BinkDecoder::blockPattern(DecodeContext &ctx) { } } -void BinkDecoder::blockRaw(DecodeContext &ctx) { +void BinkDecoder::BinkVideoTrack::blockRaw(DecodeContext &ctx) { byte *dest = ctx.dest; byte *data = _bundles[kSourceColors].curPtr; for (int i = 0; i < 8; i++, dest += ctx.pitch, data += 8) @@ -1026,7 +868,7 @@ void BinkDecoder::blockRaw(DecodeContext &ctx) { _bundles[kSourceColors].curPtr += 64; } -void BinkDecoder::readRuns(VideoFrame &video, Bundle &bundle) { +void BinkDecoder::BinkVideoTrack::readRuns(VideoFrame &video, Bundle &bundle) { uint32 n = readBundleCount(video, bundle); if (n == 0) return; @@ -1046,7 +888,7 @@ void BinkDecoder::readRuns(VideoFrame &video, Bundle &bundle) { *bundle.curDec++ = getHuffmanSymbol(video, bundle.huffman); } -void BinkDecoder::readMotionValues(VideoFrame &video, Bundle &bundle) { +void BinkDecoder::BinkVideoTrack::readMotionValues(VideoFrame &video, Bundle &bundle) { uint32 n = readBundleCount(video, bundle); if (n == 0) return; @@ -1083,7 +925,7 @@ void BinkDecoder::readMotionValues(VideoFrame &video, Bundle &bundle) { } const uint8 rleLens[4] = { 4, 8, 12, 32 }; -void BinkDecoder::readBlockTypes(VideoFrame &video, Bundle &bundle) { +void BinkDecoder::BinkVideoTrack::readBlockTypes(VideoFrame &video, Bundle &bundle) { uint32 n = readBundleCount(video, bundle); if (n == 0) return; @@ -1120,7 +962,7 @@ void BinkDecoder::readBlockTypes(VideoFrame &video, Bundle &bundle) { } while (bundle.curDec < decEnd); } -void BinkDecoder::readPatterns(VideoFrame &video, Bundle &bundle) { +void BinkDecoder::BinkVideoTrack::readPatterns(VideoFrame &video, Bundle &bundle) { uint32 n = readBundleCount(video, bundle); if (n == 0) return; @@ -1138,7 +980,7 @@ void BinkDecoder::readPatterns(VideoFrame &video, Bundle &bundle) { } -void BinkDecoder::readColors(VideoFrame &video, Bundle &bundle) { +void BinkDecoder::BinkVideoTrack::readColors(VideoFrame &video, Bundle &bundle) { uint32 n = readBundleCount(video, bundle); if (n == 0) return; @@ -1182,7 +1024,7 @@ void BinkDecoder::readColors(VideoFrame &video, Bundle &bundle) { } } -void BinkDecoder::readDCS(VideoFrame &video, Bundle &bundle, int startBits, bool hasSign) { +void BinkDecoder::BinkVideoTrack::readDCS(VideoFrame &video, Bundle &bundle, int startBits, bool hasSign) { uint32 length = readBundleCount(video, bundle); if (length == 0) return; @@ -1228,7 +1070,7 @@ void BinkDecoder::readDCS(VideoFrame &video, Bundle &bundle, int startBits, bool } /** Reads 8x8 block of DCT coefficients. */ -void BinkDecoder::readDCTCoeffs(VideoFrame &video, int16 *block, bool isIntra) { +void BinkDecoder::BinkVideoTrack::readDCTCoeffs(VideoFrame &video, int16 *block, bool isIntra) { int coefCount = 0; int coefIdx[64]; @@ -1326,7 +1168,7 @@ void BinkDecoder::readDCTCoeffs(VideoFrame &video, int16 *block, bool isIntra) { } /** Reads 8x8 block with residue after motion compensation. */ -void BinkDecoder::readResidue(VideoFrame &video, int16 *block, int masksCount) { +void BinkDecoder::BinkVideoTrack::readResidue(VideoFrame &video, int16 *block, int masksCount) { int nzCoeff[64]; int nzCoeffCount = 0; @@ -1417,63 +1259,170 @@ void BinkDecoder::readResidue(VideoFrame &video, int16 *block, int masksCount) { } } -float BinkDecoder::getFloat(AudioTrack &audio) { - int power = audio.bits->getBits(5); +#define A1 2896 /* (1/sqrt(2))<<12 */ +#define A2 2217 +#define A3 3784 +#define A4 -5352 - float f = ldexp((float)audio.bits->getBits(23), power - 23); +#define IDCT_TRANSFORM(dest,s0,s1,s2,s3,s4,s5,s6,s7,d0,d1,d2,d3,d4,d5,d6,d7,munge,src) {\ + const int a0 = (src)[s0] + (src)[s4]; \ + const int a1 = (src)[s0] - (src)[s4]; \ + const int a2 = (src)[s2] + (src)[s6]; \ + const int a3 = (A1*((src)[s2] - (src)[s6])) >> 11; \ + const int a4 = (src)[s5] + (src)[s3]; \ + const int a5 = (src)[s5] - (src)[s3]; \ + const int a6 = (src)[s1] + (src)[s7]; \ + const int a7 = (src)[s1] - (src)[s7]; \ + const int b0 = a4 + a6; \ + const int b1 = (A3*(a5 + a7)) >> 11; \ + const int b2 = ((A4*a5) >> 11) - b0 + b1; \ + const int b3 = (A1*(a6 - a4) >> 11) - b2; \ + const int b4 = ((A2*a7) >> 11) + b3 - b1; \ + (dest)[d0] = munge(a0+a2 +b0); \ + (dest)[d1] = munge(a1+a3-a2+b2); \ + (dest)[d2] = munge(a1-a3+a2+b3); \ + (dest)[d3] = munge(a0-a2 -b4); \ + (dest)[d4] = munge(a0-a2 +b4); \ + (dest)[d5] = munge(a1-a3+a2-b3); \ + (dest)[d6] = munge(a1+a3-a2-b2); \ + (dest)[d7] = munge(a0+a2 -b0); \ +} +/* end IDCT_TRANSFORM macro */ - if (audio.bits->getBit()) - f = -f; +#define MUNGE_NONE(x) (x) +#define IDCT_COL(dest,src) IDCT_TRANSFORM(dest,0,8,16,24,32,40,48,56,0,8,16,24,32,40,48,56,MUNGE_NONE,src) - return f; +#define MUNGE_ROW(x) (((x) + 0x7F)>>8) +#define IDCT_ROW(dest,src) IDCT_TRANSFORM(dest,0,1,2,3,4,5,6,7,0,1,2,3,4,5,6,7,MUNGE_ROW,src) + +static inline void IDCTCol(int16 *dest, const int16 *src) { + if ((src[8] | src[16] | src[24] | src[32] | src[40] | src[48] | src[56]) == 0) { + dest[ 0] = + dest[ 8] = + dest[16] = + dest[24] = + dest[32] = + dest[40] = + dest[48] = + dest[56] = src[0]; + } else { + IDCT_COL(dest, src); + } } -void BinkDecoder::audioBlock(AudioTrack &audio, int16 *out) { - if (audio.codec == kAudioCodecDCT) - audioBlockDCT (audio); - else if (audio.codec == kAudioCodecRDFT) - audioBlockRDFT(audio); +void BinkDecoder::BinkVideoTrack::IDCT(int16 *block) { + int i; + int16 temp[64]; - floatToInt16Interleave(out, const_cast<const float **>(audio.coeffsPtr), audio.frameLen, audio.channels); + for (i = 0; i < 8; i++) + IDCTCol(&temp[i], &block[i]); + for (i = 0; i < 8; i++) { + IDCT_ROW( (&block[8*i]), (&temp[8*i]) ); + } +} - if (!audio.first) { - int count = audio.overlapLen * audio.channels; +void BinkDecoder::BinkVideoTrack::IDCTAdd(DecodeContext &ctx, int16 *block) { + int i, j; + + IDCT(block); + byte *dest = ctx.dest; + for (i = 0; i < 8; i++, dest += ctx.pitch, block += 8) + for (j = 0; j < 8; j++) + dest[j] += block[j]; +} + +void BinkDecoder::BinkVideoTrack::IDCTPut(DecodeContext &ctx, int16 *block) { + int i; + int16 temp[64]; + for (i = 0; i < 8; i++) + IDCTCol(&temp[i], &block[i]); + for (i = 0; i < 8; i++) { + IDCT_ROW( (&ctx.dest[i*ctx.pitch]), (&temp[8*i]) ); + } +} + +BinkDecoder::BinkAudioTrack::BinkAudioTrack(BinkDecoder::AudioInfo &audio) : _audioInfo(&audio) { + _audioStream = Audio::makeQueuingAudioStream(_audioInfo->outSampleRate, _audioInfo->outChannels == 2); +} + +BinkDecoder::BinkAudioTrack::~BinkAudioTrack() { + delete _audioStream; +} + +Audio::AudioStream *BinkDecoder::BinkAudioTrack::getAudioStream() const { + return _audioStream; +} + +void BinkDecoder::BinkAudioTrack::decodePacket() { + int outSize = _audioInfo->frameLen * _audioInfo->channels; + + while (_audioInfo->bits->pos() < _audioInfo->bits->size()) { + int16 *out = (int16 *)malloc(outSize * 2); + memset(out, 0, outSize * 2); + + audioBlock(out); + + byte flags = Audio::FLAG_16BITS; + if (_audioInfo->outChannels == 2) + flags |= Audio::FLAG_STEREO; + +#ifdef SCUMM_LITTLE_ENDIAN + flags |= Audio::FLAG_LITTLE_ENDIAN; +#endif + + _audioStream->queueBuffer((byte *)out, _audioInfo->blockSize * 2, DisposeAfterUse::YES, flags); + + if (_audioInfo->bits->pos() & 0x1F) // next data block starts at a 32-byte boundary + _audioInfo->bits->skip(32 - (_audioInfo->bits->pos() & 0x1F)); + } +} + +void BinkDecoder::BinkAudioTrack::audioBlock(int16 *out) { + if (_audioInfo->codec == kAudioCodecDCT) + audioBlockDCT (); + else if (_audioInfo->codec == kAudioCodecRDFT) + audioBlockRDFT(); + + floatToInt16Interleave(out, const_cast<const float **>(_audioInfo->coeffsPtr), _audioInfo->frameLen, _audioInfo->channels); + + if (!_audioInfo->first) { + int count = _audioInfo->overlapLen * _audioInfo->channels; int shift = Common::intLog2(count); for (int i = 0; i < count; i++) { - out[i] = (audio.prevCoeffs[i] * (count - i) + out[i] * i) >> shift; + out[i] = (_audioInfo->prevCoeffs[i] * (count - i) + out[i] * i) >> shift; } } - memcpy(audio.prevCoeffs, out + audio.blockSize, audio.overlapLen * audio.channels * sizeof(*out)); + memcpy(_audioInfo->prevCoeffs, out + _audioInfo->blockSize, _audioInfo->overlapLen * _audioInfo->channels * sizeof(*out)); - audio.first = false; + _audioInfo->first = false; } -void BinkDecoder::audioBlockDCT(AudioTrack &audio) { - audio.bits->skip(2); +void BinkDecoder::BinkAudioTrack::audioBlockDCT() { + _audioInfo->bits->skip(2); - for (uint8 i = 0; i < audio.channels; i++) { - float *coeffs = audio.coeffsPtr[i]; + for (uint8 i = 0; i < _audioInfo->channels; i++) { + float *coeffs = _audioInfo->coeffsPtr[i]; - readAudioCoeffs(audio, coeffs); + readAudioCoeffs(coeffs); coeffs[0] /= 0.5; - audio.dct->calc(coeffs); + _audioInfo->dct->calc(coeffs); - for (uint32 j = 0; j < audio.frameLen; j++) - coeffs[j] *= (audio.frameLen / 2.0); + for (uint32 j = 0; j < _audioInfo->frameLen; j++) + coeffs[j] *= (_audioInfo->frameLen / 2.0); } } -void BinkDecoder::audioBlockRDFT(AudioTrack &audio) { - for (uint8 i = 0; i < audio.channels; i++) { - float *coeffs = audio.coeffsPtr[i]; +void BinkDecoder::BinkAudioTrack::audioBlockRDFT() { + for (uint8 i = 0; i < _audioInfo->channels; i++) { + float *coeffs = _audioInfo->coeffsPtr[i]; - readAudioCoeffs(audio, coeffs); + readAudioCoeffs(coeffs); - audio.rdft->calc(coeffs); + _audioInfo->rdft->calc(coeffs); } } @@ -1481,56 +1430,56 @@ static const uint8 rleLengthTab[16] = { 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 32, 64 }; -void BinkDecoder::readAudioCoeffs(AudioTrack &audio, float *coeffs) { - coeffs[0] = getFloat(audio) * audio.root; - coeffs[1] = getFloat(audio) * audio.root; +void BinkDecoder::BinkAudioTrack::readAudioCoeffs(float *coeffs) { + coeffs[0] = getFloat() * _audioInfo->root; + coeffs[1] = getFloat() * _audioInfo->root; float quant[25]; - for (uint32 i = 0; i < audio.bandCount; i++) { - int value = audio.bits->getBits(8); + for (uint32 i = 0; i < _audioInfo->bandCount; i++) { + int value = _audioInfo->bits->getBits(8); // 0.066399999 / log10(M_E) - quant[i] = exp(MIN(value, 95) * 0.15289164787221953823f) * audio.root; + quant[i] = exp(MIN(value, 95) * 0.15289164787221953823f) * _audioInfo->root; } float q = 0.0; // Find band (k) int k; - for (k = 0; audio.bands[k] < 1; k++) + for (k = 0; _audioInfo->bands[k] < 1; k++) q = quant[k]; // Parse coefficients uint32 i = 2; - while (i < audio.frameLen) { + while (i < _audioInfo->frameLen) { uint32 j = 0; - if (audio.bits->getBit()) - j = i + rleLengthTab[audio.bits->getBits(4)] * 8; + if (_audioInfo->bits->getBit()) + j = i + rleLengthTab[_audioInfo->bits->getBits(4)] * 8; else j = i + 8; - j = MIN(j, audio.frameLen); + j = MIN(j, _audioInfo->frameLen); - int width = audio.bits->getBits(4); + int width = _audioInfo->bits->getBits(4); if (width == 0) { memset(coeffs + i, 0, (j - i) * sizeof(*coeffs)); i = j; - while (audio.bands[k] * 2 < i) + while (_audioInfo->bands[k] * 2 < i) q = quant[k++]; } else { while (i < j) { - if (audio.bands[k] * 2 == i) + if (_audioInfo->bands[k] * 2 == i) q = quant[k++]; - int coeff = audio.bits->getBits(width); + int coeff = _audioInfo->bits->getBits(width); if (coeff) { - if (audio.bits->getBit()) + if (_audioInfo->bits->getBit()) coeffs[i] = -q * coeff; else coeffs[i] = q * coeff; @@ -1548,10 +1497,10 @@ void BinkDecoder::readAudioCoeffs(AudioTrack &audio, float *coeffs) { } static inline int floatToInt16One(float src) { - return (int16) CLIP<int>((int) floor(src + 0.5), -32768, 32767); + return (int16)CLIP<int>((int)floor(src + 0.5), -32768, 32767); } -void BinkDecoder::floatToInt16Interleave(int16 *dst, const float **src, uint32 length, uint8 channels) { +void BinkDecoder::BinkAudioTrack::floatToInt16Interleave(int16 *dst, const float **src, uint32 length, uint8 channels) { if (channels == 2) { for (uint32 i = 0; i < length; i++) { dst[2 * i ] = floatToInt16One(src[0][i]); @@ -1564,97 +1513,84 @@ void BinkDecoder::floatToInt16Interleave(int16 *dst, const float **src, uint32 l } } -#define A1 2896 /* (1/sqrt(2))<<12 */ -#define A2 2217 -#define A3 3784 -#define A4 -5352 +float BinkDecoder::BinkAudioTrack::getFloat() { + int power = _audioInfo->bits->getBits(5); -#define IDCT_TRANSFORM(dest,s0,s1,s2,s3,s4,s5,s6,s7,d0,d1,d2,d3,d4,d5,d6,d7,munge,src) {\ - const int a0 = (src)[s0] + (src)[s4]; \ - const int a1 = (src)[s0] - (src)[s4]; \ - const int a2 = (src)[s2] + (src)[s6]; \ - const int a3 = (A1*((src)[s2] - (src)[s6])) >> 11; \ - const int a4 = (src)[s5] + (src)[s3]; \ - const int a5 = (src)[s5] - (src)[s3]; \ - const int a6 = (src)[s1] + (src)[s7]; \ - const int a7 = (src)[s1] - (src)[s7]; \ - const int b0 = a4 + a6; \ - const int b1 = (A3*(a5 + a7)) >> 11; \ - const int b2 = ((A4*a5) >> 11) - b0 + b1; \ - const int b3 = (A1*(a6 - a4) >> 11) - b2; \ - const int b4 = ((A2*a7) >> 11) + b3 - b1; \ - (dest)[d0] = munge(a0+a2 +b0); \ - (dest)[d1] = munge(a1+a3-a2+b2); \ - (dest)[d2] = munge(a1-a3+a2+b3); \ - (dest)[d3] = munge(a0-a2 -b4); \ - (dest)[d4] = munge(a0-a2 +b4); \ - (dest)[d5] = munge(a1-a3+a2-b3); \ - (dest)[d6] = munge(a1+a3-a2-b2); \ - (dest)[d7] = munge(a0+a2 -b0); \ + float f = ldexp((float)_audioInfo->bits->getBits(23), power - 23); + + if (_audioInfo->bits->getBit()) + f = -f; + + return f; } -/* end IDCT_TRANSFORM macro */ -#define MUNGE_NONE(x) (x) -#define IDCT_COL(dest,src) IDCT_TRANSFORM(dest,0,8,16,24,32,40,48,56,0,8,16,24,32,40,48,56,MUNGE_NONE,src) +void BinkDecoder::initAudioTrack(AudioInfo &audio) { + audio.sampleCount = 0; + audio.bits = 0; -#define MUNGE_ROW(x) (((x) + 0x7F)>>8) -#define IDCT_ROW(dest,src) IDCT_TRANSFORM(dest,0,1,2,3,4,5,6,7,0,1,2,3,4,5,6,7,MUNGE_ROW,src) + audio.channels = ((audio.flags & kAudioFlagStereo) != 0) ? 2 : 1; + audio.codec = ((audio.flags & kAudioFlagDCT ) != 0) ? kAudioCodecDCT : kAudioCodecRDFT; -static inline void IDCTCol(int16 *dest, const int16 *src) -{ - if ((src[8] | src[16] | src[24] | src[32] | src[40] | src[48] | src[56]) == 0) { - dest[ 0] = - dest[ 8] = - dest[16] = - dest[24] = - dest[32] = - dest[40] = - dest[48] = - dest[56] = src[0]; - } else { - IDCT_COL(dest, src); - } -} + if (audio.channels > kAudioChannelsMax) + error("Too many audio channels: %d", audio.channels); -void BinkDecoder::IDCT(int16 *block) { - int i; - int16 temp[64]; + uint32 frameLenBits; + // Calculate frame length + if (audio.sampleRate < 22050) + frameLenBits = 9; + else if(audio.sampleRate < 44100) + frameLenBits = 10; + else + frameLenBits = 11; - for (i = 0; i < 8; i++) - IDCTCol(&temp[i], &block[i]); - for (i = 0; i < 8; i++) { - IDCT_ROW( (&block[8*i]), (&temp[8*i]) ); - } -} + audio.frameLen = 1 << frameLenBits; -void BinkDecoder::IDCTAdd(DecodeContext &ctx, int16 *block) { - int i, j; + audio.outSampleRate = audio.sampleRate; + audio.outChannels = audio.channels; - IDCT(block); - byte *dest = ctx.dest; - for (i = 0; i < 8; i++, dest += ctx.pitch, block += 8) - for (j = 0; j < 8; j++) - dest[j] += block[j]; -} + if (audio.codec == kAudioCodecRDFT) { + // RDFT audio already interleaves the samples correctly -void BinkDecoder::IDCTPut(DecodeContext &ctx, int16 *block) { - int i; - int16 temp[64]; - for (i = 0; i < 8; i++) - IDCTCol(&temp[i], &block[i]); - for (i = 0; i < 8; i++) { - IDCT_ROW( (&ctx.dest[i*ctx.pitch]), (&temp[8*i]) ); + if (audio.channels == 2) + frameLenBits++; + + audio.sampleRate *= audio.channels; + audio.frameLen *= audio.channels; + audio.channels = 1; } -} -void BinkDecoder::updateVolume() { - if (g_system->getMixer()->isSoundHandleActive(_audioHandle)) - g_system->getMixer()->setChannelVolume(_audioHandle, getVolume()); -} + audio.overlapLen = audio.frameLen / 16; + audio.blockSize = (audio.frameLen - audio.overlapLen) * audio.channels; + audio.root = 2.0 / sqrt((double)audio.frameLen); + + uint32 sampleRateHalf = (audio.sampleRate + 1) / 2; + + // Calculate number of bands + for (audio.bandCount = 1; audio.bandCount < 25; audio.bandCount++) + if (sampleRateHalf <= binkCriticalFreqs[audio.bandCount - 1]) + break; + + audio.bands = new uint32[audio.bandCount + 1]; + + // Populate bands + audio.bands[0] = 1; + for (uint32 i = 1; i < audio.bandCount; i++) + audio.bands[i] = binkCriticalFreqs[i - 1] * (audio.frameLen / 2) / sampleRateHalf; + audio.bands[audio.bandCount] = audio.frameLen / 2; + + audio.first = true; + + for (uint8 i = 0; i < audio.channels; i++) + audio.coeffsPtr[i] = audio.coeffs + i * audio.frameLen; + + audio.codec = ((audio.flags & kAudioFlagDCT) != 0) ? kAudioCodecDCT : kAudioCodecRDFT; + + if (audio.codec == kAudioCodecRDFT) + audio.rdft = new Common::RDFT(frameLenBits, Common::RDFT::DFT_C2R); + else if (audio.codec == kAudioCodecDCT) + audio.dct = new Common::DCT(frameLenBits, Common::DCT::DCT_III); -void BinkDecoder::updateBalance() { - if (g_system->getMixer()->isSoundHandleActive(_audioHandle)) - g_system->getMixer()->setChannelBalance(_audioHandle, getBalance()); + addTrack(new BinkAudioTrack(audio)); } } // End of namespace Video diff --git a/video/bink_decoder.h b/video/bink_decoder.h index a5e1b10270..08800c2223 100644 --- a/video/bink_decoder.h +++ b/video/bink_decoder.h @@ -31,22 +31,29 @@ #ifndef VIDEO_BINK_DECODER_H #define VIDEO_BINK_DECODER_H -#include "audio/audiostream.h" -#include "audio/mixer.h" #include "common/array.h" #include "common/rational.h" +#include "video/video_decoder.h" + #include "graphics/surface.h" -#include "video/video_decoder.h" +namespace Audio { +class AudioStream; +class QueuingAudioStream; +} namespace Common { - class SeekableReadStream; - class BitStream; - class Huffman; +class SeekableReadStream; +class BitStream; +class Huffman; - class RDFT; - class DCT; +class RDFT; +class DCT; +} + +namespace Graphics { +struct Surface; } namespace Video { @@ -57,92 +64,28 @@ namespace Video { * Video decoder used in engines: * - scumm (he) */ -class BinkDecoder : public FixedRateVideoDecoder { +class BinkDecoder : public VideoDecoder { public: BinkDecoder(); ~BinkDecoder(); - // VideoDecoder API bool loadStream(Common::SeekableReadStream *stream); void close(); - bool isVideoLoaded() const { return _bink != 0; } - uint16 getWidth() const { return _surface.w; } - uint16 getHeight() const { return _surface.h; } - Graphics::PixelFormat getPixelFormat() const { return _surface.format; } - uint32 getFrameCount() const { return _frames.size(); } - uint32 getTime() const; - const Graphics::Surface *decodeNextFrame(); - - // FixedRateVideoDecoder - Common::Rational getFrameRate() const { return _frameRate; } - - // Bink specific - bool loadStream(Common::SeekableReadStream *stream, const Graphics::PixelFormat &format); protected: - // VideoDecoder API - void updateVolume(); - void updateBalance(); + void readNextPacket(); +private: static const int kAudioChannelsMax = 2; static const int kAudioBlockSizeMax = (kAudioChannelsMax << 11); - /** IDs for different data types used in Bink video codec. */ - enum Source { - kSourceBlockTypes = 0, ///< 8x8 block types. - kSourceSubBlockTypes , ///< 16x16 block types (a subset of 8x8 block types). - kSourceColors , ///< Pixel values used for different block types. - kSourcePattern , ///< 8-bit values for 2-color pattern fill. - kSourceXOff , ///< X components of motion value. - kSourceYOff , ///< Y components of motion value. - kSourceIntraDC , ///< DC values for intrablocks with DCT. - kSourceInterDC , ///< DC values for interblocks with DCT. - kSourceRun , ///< Run lengths for special fill block. - - kSourceMAX - }; - - /** Bink video block types. */ - enum BlockType { - kBlockSkip = 0, ///< Skipped block. - kBlockScaled , ///< Block has size 16x16. - kBlockMotion , ///< Block is copied from previous frame with some offset. - kBlockRun , ///< Block is composed from runs of colors with custom scan order. - kBlockResidue , ///< Motion block with some difference added. - kBlockIntra , ///< Intra DCT block. - kBlockFill , ///< Block is filled with single color. - kBlockInter , ///< Motion block with DCT applied to the difference. - kBlockPattern , ///< Block is filled with two colors following custom pattern. - kBlockRaw ///< Uncoded 8x8 block. - }; - - /** Data structure for decoding and tranlating Huffman'd data. */ - struct Huffman { - int index; ///< Index of the Huffman codebook to use. - byte symbols[16]; ///< Huffman symbol => Bink symbol tranlation list. - }; - - /** Data structure used for decoding a single Bink data type. */ - struct Bundle { - int countLengths[2]; ///< Lengths of number of entries to decode (in bits). - int countLength; ///< Length of number of entries to decode (in bits) for the current plane. - - Huffman huffman; ///< Huffman codebook. - - byte *data; ///< Buffer for decoded symbols. - byte *dataEnd; ///< Buffer end. - - byte *curDec; ///< Pointer to the data that wasn't yet decoded. - byte *curPtr; ///< Pointer to the data that wasn't yet read. - }; - enum AudioCodec { kAudioCodecDCT, kAudioCodecRDFT }; /** An audio track. */ - struct AudioTrack { + struct AudioInfo { uint16 flags; uint32 sampleRate; @@ -177,8 +120,8 @@ protected: Common::RDFT *rdft; Common::DCT *dct; - AudioTrack(); - ~AudioTrack(); + AudioInfo(); + ~AudioInfo(); }; /** A video frame. */ @@ -194,149 +137,220 @@ protected: ~VideoFrame(); }; - /** A decoder state. */ - struct DecodeContext { - VideoFrame *video; - - uint32 planeIdx; + class BinkVideoTrack : public FixedRateVideoTrack { + public: + BinkVideoTrack(uint32 width, uint32 height, const Graphics::PixelFormat &format, uint32 frameCount, const Common::Rational &frameRate, bool swapPlanes, bool hasAlpha, uint32 id); + ~BinkVideoTrack(); + + uint16 getWidth() const { return _surface.w; } + uint16 getHeight() const { return _surface.h; } + Graphics::PixelFormat getPixelFormat() const { return _surface.format; } + int getCurFrame() const { return _curFrame; } + int getFrameCount() const { return _frameCount; } + const Graphics::Surface *decodeNextFrame() { return &_surface; } + + /** Decode a video packet. */ + void decodePacket(VideoFrame &frame); + + protected: + Common::Rational getFrameRate() const { return _frameRate; } - uint32 blockX; - uint32 blockY; + private: + /** A decoder state. */ + struct DecodeContext { + VideoFrame *video; + + uint32 planeIdx; + + uint32 blockX; + uint32 blockY; + + byte *dest; + byte *prev; + + byte *destStart, *destEnd; + byte *prevStart, *prevEnd; - byte *dest; - byte *prev; - - byte *destStart, *destEnd; - byte *prevStart, *prevEnd; + uint32 pitch; + + int coordMap[64]; + int coordScaledMap1[64]; + int coordScaledMap2[64]; + int coordScaledMap3[64]; + int coordScaledMap4[64]; + }; + + /** IDs for different data types used in Bink video codec. */ + enum Source { + kSourceBlockTypes = 0, ///< 8x8 block types. + kSourceSubBlockTypes , ///< 16x16 block types (a subset of 8x8 block types). + kSourceColors , ///< Pixel values used for different block types. + kSourcePattern , ///< 8-bit values for 2-color pattern fill. + kSourceXOff , ///< X components of motion value. + kSourceYOff , ///< Y components of motion value. + kSourceIntraDC , ///< DC values for intrablocks with DCT. + kSourceInterDC , ///< DC values for interblocks with DCT. + kSourceRun , ///< Run lengths for special fill block. + + kSourceMAX + }; + + /** Bink video block types. */ + enum BlockType { + kBlockSkip = 0, ///< Skipped block. + kBlockScaled , ///< Block has size 16x16. + kBlockMotion , ///< Block is copied from previous frame with some offset. + kBlockRun , ///< Block is composed from runs of colors with custom scan order. + kBlockResidue , ///< Motion block with some difference added. + kBlockIntra , ///< Intra DCT block. + kBlockFill , ///< Block is filled with single color. + kBlockInter , ///< Motion block with DCT applied to the difference. + kBlockPattern , ///< Block is filled with two colors following custom pattern. + kBlockRaw ///< Uncoded 8x8 block. + }; + + /** Data structure for decoding and tranlating Huffman'd data. */ + struct Huffman { + int index; ///< Index of the Huffman codebook to use. + byte symbols[16]; ///< Huffman symbol => Bink symbol tranlation list. + }; + + /** Data structure used for decoding a single Bink data type. */ + struct Bundle { + int countLengths[2]; ///< Lengths of number of entries to decode (in bits). + int countLength; ///< Length of number of entries to decode (in bits) for the current plane. + + Huffman huffman; ///< Huffman codebook. + + byte *data; ///< Buffer for decoded symbols. + byte *dataEnd; ///< Buffer end. + + byte *curDec; ///< Pointer to the data that wasn't yet decoded. + byte *curPtr; ///< Pointer to the data that wasn't yet read. + }; + + int _curFrame; + int _frameCount; + + Graphics::Surface _surface; + int _surfaceWidth; ///< The actual surface width + int _surfaceHeight; ///< The actual surface height + + uint32 _id; ///< The BIK FourCC. + + bool _hasAlpha; ///< Do video frames have alpha? + bool _swapPlanes; ///< Are the planes ordered (A)YVU instead of (A)YUV? + + Common::Rational _frameRate; + + Bundle _bundles[kSourceMAX]; ///< Bundles for decoding all data types. + + Common::Huffman *_huffman[16]; ///< The 16 Huffman codebooks used in Bink decoding. + + /** Huffman codebooks to use for decoding high nibbles in color data types. */ + Huffman _colHighHuffman[16]; + /** Value of the last decoded high nibble in color data types. */ + int _colLastVal; + + byte *_curPlanes[4]; ///< The 4 color planes, YUVA, current frame. + byte *_oldPlanes[4]; ///< The 4 color planes, YUVA, last frame. + + /** Initialize the bundles. */ + void initBundles(); + /** Deinitialize the bundles. */ + void deinitBundles(); + + /** Initialize the Huffman decoders. */ + void initHuffman(); + + /** Decode a plane. */ + void decodePlane(VideoFrame &video, int planeIdx, bool isChroma); + + /** Read/Initialize a bundle for decoding a plane. */ + void readBundle(VideoFrame &video, Source source); + + /** Read the symbols for a Huffman code. */ + void readHuffman(VideoFrame &video, Huffman &huffman); + /** Merge two Huffman symbol lists. */ + void mergeHuffmanSymbols(VideoFrame &video, byte *dst, const byte *src, int size); + + /** Read and translate a symbol out of a Huffman code. */ + byte getHuffmanSymbol(VideoFrame &video, Huffman &huffman); + + /** Get a direct value out of a bundle. */ + int32 getBundleValue(Source source); + /** Read a count value out of a bundle. */ + uint32 readBundleCount(VideoFrame &video, Bundle &bundle); + + // Handle the block types + void blockSkip (DecodeContext &ctx); + void blockScaledSkip (DecodeContext &ctx); + void blockScaledRun (DecodeContext &ctx); + void blockScaledIntra (DecodeContext &ctx); + void blockScaledFill (DecodeContext &ctx); + void blockScaledPattern(DecodeContext &ctx); + void blockScaledRaw (DecodeContext &ctx); + void blockScaled (DecodeContext &ctx); + void blockMotion (DecodeContext &ctx); + void blockRun (DecodeContext &ctx); + void blockResidue (DecodeContext &ctx); + void blockIntra (DecodeContext &ctx); + void blockFill (DecodeContext &ctx); + void blockInter (DecodeContext &ctx); + void blockPattern (DecodeContext &ctx); + void blockRaw (DecodeContext &ctx); + + // Read the bundles + void readRuns (VideoFrame &video, Bundle &bundle); + void readMotionValues(VideoFrame &video, Bundle &bundle); + void readBlockTypes (VideoFrame &video, Bundle &bundle); + void readPatterns (VideoFrame &video, Bundle &bundle); + void readColors (VideoFrame &video, Bundle &bundle); + void readDCS (VideoFrame &video, Bundle &bundle, int startBits, bool hasSign); + void readDCTCoeffs (VideoFrame &video, int16 *block, bool isIntra); + void readResidue (VideoFrame &video, int16 *block, int masksCount); + + // Bink video IDCT + void IDCT(int16 *block); + void IDCTPut(DecodeContext &ctx, int16 *block); + void IDCTAdd(DecodeContext &ctx, int16 *block); + }; - uint32 pitch; + class BinkAudioTrack : public AudioTrack { + public: + BinkAudioTrack(AudioInfo &audio); + ~BinkAudioTrack(); - int coordMap[64]; - int coordScaledMap1[64]; - int coordScaledMap2[64]; - int coordScaledMap3[64]; - int coordScaledMap4[64]; - }; + /** Decode an audio packet. */ + void decodePacket(); - Common::SeekableReadStream *_bink; + protected: + Audio::AudioStream *getAudioStream() const; - uint32 _id; ///< The BIK FourCC. + private: + AudioInfo *_audioInfo; + Audio::QueuingAudioStream *_audioStream; - Common::Rational _frameRate; + float getFloat(); - Graphics::Surface _surface; + /** Decode an audio block. */ + void audioBlock(int16 *out); + /** Decode a DCT'd audio block. */ + void audioBlockDCT(); + /** Decode a RDFT'd audio block. */ + void audioBlockRDFT(); - Audio::SoundHandle _audioHandle; - Audio::QueuingAudioStream *_audioStream; - int32 _audioStartOffset; + void readAudioCoeffs(float *coeffs); - uint32 _videoFlags; ///< Video frame features. + static void floatToInt16Interleave(int16 *dst, const float **src, uint32 length, uint8 channels); + }; - bool _hasAlpha; ///< Do video frames have alpha? - bool _swapPlanes; ///< Are the planes ordered (A)YVU instead of (A)YUV? + Common::SeekableReadStream *_bink; - Common::Array<AudioTrack> _audioTracks; ///< All audio tracks. + Common::Array<AudioInfo> _audioTracks; ///< All audio tracks. Common::Array<VideoFrame> _frames; ///< All video frames. - uint32 _audioTrack; ///< Audio track to use. - - Common::Huffman *_huffman[16]; ///< The 16 Huffman codebooks used in Bink decoding. - - Bundle _bundles[kSourceMAX]; ///< Bundles for decoding all data types. - - /** Huffman codebooks to use for decoding high nibbles in color data types. */ - Huffman _colHighHuffman[16]; - /** Value of the last decoded high nibble in color data types. */ - int _colLastVal; - - byte *_curPlanes[4]; ///< The 4 color planes, YUVA, current frame. - byte *_oldPlanes[4]; ///< The 4 color planes, YUVA, last frame. - - - /** Initialize the bundles. */ - void initBundles(); - /** Deinitialize the bundles. */ - void deinitBundles(); - - /** Initialize the Huffman decoders. */ - void initHuffman(); - - /** Decode an audio packet. */ - void audioPacket(AudioTrack &audio); - /** Decode a video packet. */ - virtual void videoPacket(VideoFrame &video); - - /** Decode a plane. */ - void decodePlane(VideoFrame &video, int planeIdx, bool isChroma); - - /** Read/Initialize a bundle for decoding a plane. */ - void readBundle(VideoFrame &video, Source source); - - /** Read the symbols for a Huffman code. */ - void readHuffman(VideoFrame &video, Huffman &huffman); - /** Merge two Huffman symbol lists. */ - void mergeHuffmanSymbols(VideoFrame &video, byte *dst, const byte *src, int size); - - /** Read and translate a symbol out of a Huffman code. */ - byte getHuffmanSymbol(VideoFrame &video, Huffman &huffman); - - /** Get a direct value out of a bundle. */ - int32 getBundleValue(Source source); - /** Read a count value out of a bundle. */ - uint32 readBundleCount(VideoFrame &video, Bundle &bundle); - - // Handle the block types - void blockSkip (DecodeContext &ctx); - void blockScaledSkip (DecodeContext &ctx); - void blockScaledRun (DecodeContext &ctx); - void blockScaledIntra (DecodeContext &ctx); - void blockScaledFill (DecodeContext &ctx); - void blockScaledPattern(DecodeContext &ctx); - void blockScaledRaw (DecodeContext &ctx); - void blockScaled (DecodeContext &ctx); - void blockMotion (DecodeContext &ctx); - void blockRun (DecodeContext &ctx); - void blockResidue (DecodeContext &ctx); - void blockIntra (DecodeContext &ctx); - void blockFill (DecodeContext &ctx); - void blockInter (DecodeContext &ctx); - void blockPattern (DecodeContext &ctx); - void blockRaw (DecodeContext &ctx); - - // Read the bundles - void readRuns (VideoFrame &video, Bundle &bundle); - void readMotionValues(VideoFrame &video, Bundle &bundle); - void readBlockTypes (VideoFrame &video, Bundle &bundle); - void readPatterns (VideoFrame &video, Bundle &bundle); - void readColors (VideoFrame &video, Bundle &bundle); - void readDCS (VideoFrame &video, Bundle &bundle, int startBits, bool hasSign); - void readDCTCoeffs (VideoFrame &video, int16 *block, bool isIntra); - void readResidue (VideoFrame &video, int16 *block, int masksCount); - - void initAudioTrack(AudioTrack &audio); - - float getFloat(AudioTrack &audio); - - /** Decode an audio block. */ - void audioBlock (AudioTrack &audio, int16 *out); - /** Decode a DCT'd audio block. */ - void audioBlockDCT (AudioTrack &audio); - /** Decode a RDFT'd audio block. */ - void audioBlockRDFT(AudioTrack &audio); - - void readAudioCoeffs(AudioTrack &audio, float *coeffs); - - void floatToInt16Interleave(int16 *dst, const float **src, uint32 length, uint8 channels); - - // Bink video IDCT - void IDCT(int16 *block); - void IDCTPut(DecodeContext &ctx, int16 *block); - void IDCTAdd(DecodeContext &ctx, int16 *block); - - /** Start playing the audio track */ - void startAudio(); - /** Stop playing the audio track */ - void stopAudio(); + void initAudioTrack(AudioInfo &audio); }; } // End of namespace Video diff --git a/video/codecs/cdtoons.h b/video/codecs/cdtoons.h index 8f6d3acb6e..e6b7aab5f8 100644 --- a/video/codecs/cdtoons.h +++ b/video/codecs/cdtoons.h @@ -38,6 +38,12 @@ struct CDToonsBlock { byte *data; }; +/** + * Broderbund CDToons decoder. + * + * Used in video: + * - QuickTimeDecoder + */ class CDToonsDecoder : public Codec { public: CDToonsDecoder(uint16 width, uint16 height); diff --git a/video/codecs/cinepak.cpp b/video/codecs/cinepak.cpp index c197e0cc35..bcf0cf1180 100644 --- a/video/codecs/cinepak.cpp +++ b/video/codecs/cinepak.cpp @@ -34,16 +34,12 @@ namespace Video { -// Convert a color from YUV to RGB colorspace, Cinepak style. -inline static void CPYUV2RGB(byte y, byte u, byte v, byte &r, byte &g, byte &b) { - r = CLIP<int>(y + 2 * (v - 128), 0, 255); - g = CLIP<int>(y - (u - 128) / 2 - (v - 128), 0, 255); - b = CLIP<int>(y + 2 * (u - 128), 0, 255); -} - #define PUT_PIXEL(offset, lum, u, v) \ if (_pixelFormat.bytesPerPixel != 1) { \ - CPYUV2RGB(lum, u, v, r, g, b); \ + byte r = _clipTable[lum + (v << 1)]; \ + byte g = _clipTable[lum - (u >> 1) - v]; \ + byte b = _clipTable[lum + (u << 1)]; \ + \ if (_pixelFormat.bytesPerPixel == 2) \ *((uint16 *)_curFrame.surface->pixels + offset) = _pixelFormat.RGBToColor(r, g, b); \ else \ @@ -60,6 +56,21 @@ CinepakDecoder::CinepakDecoder(int bitsPerPixel) : Codec() { _pixelFormat = Graphics::PixelFormat::createFormatCLUT8(); else _pixelFormat = g_system->getScreenFormat(); + + // Create a lookup for the clip function + // This dramatically improves the performance of the color conversion + _clipTableBuf = new byte[1024]; + + for (uint i = 0; i < 1024; i++) { + if (i <= 512) + _clipTableBuf[i] = 0; + else if (i >= 768) + _clipTableBuf[i] = 255; + else + _clipTableBuf[i] = i - 512; + } + + _clipTable = _clipTableBuf + 512; } CinepakDecoder::~CinepakDecoder() { @@ -69,6 +80,7 @@ CinepakDecoder::~CinepakDecoder() { } delete[] _curFrame.strips; + delete[] _clipTableBuf; } const Graphics::Surface *CinepakDecoder::decodeImage(Common::SeekableReadStream *stream) { @@ -82,15 +94,14 @@ const Graphics::Surface *CinepakDecoder::decodeImage(Common::SeekableReadStream if (_curFrame.strips == NULL) _curFrame.strips = new CinepakStrip[_curFrame.stripCount]; - debug (4, "Cinepak Frame: Width = %d, Height = %d, Strip Count = %d", _curFrame.width, _curFrame.height, _curFrame.stripCount); + debug(4, "Cinepak Frame: Width = %d, Height = %d, Strip Count = %d", _curFrame.width, _curFrame.height, _curFrame.stripCount); // Borrowed from FFMPEG. This should cut out the extra data Cinepak for Sega has (which is useless). // The theory behind this is that this is here to confuse standard Cinepak decoders. But, we won't let that happen! ;) if (_curFrame.length != (uint32)stream->size()) { - uint16 temp = stream->readUint16BE(); - if (temp == 0xFE00) + if (stream->readUint16BE() == 0xFE00) stream->readUint32BE(); - else if (temp != _curFrame.width) + else if ((stream->size() % _curFrame.length) == 0) stream->seek(-2, SEEK_CUR); } @@ -191,14 +202,14 @@ void CinepakDecoder::loadCodebook(Common::SeekableReadStream *stream, uint16 str codebook[i].y[j] = stream->readByte(); if (n == 6) { - codebook[i].u = stream->readByte() + 128; - codebook[i].v = stream->readByte() + 128; + codebook[i].u = stream->readSByte(); + codebook[i].v = stream->readSByte(); } else { // This codebook type indicates either greyscale or // palettized video. For greyscale, default us to - // 128 for both u and v. - codebook[i].u = 128; - codebook[i].v = 128; + // 0 for both u and v. + codebook[i].u = 0; + codebook[i].v = 0; } } } @@ -208,7 +219,6 @@ void CinepakDecoder::decodeVectors(Common::SeekableReadStream *stream, uint16 st uint32 flag = 0, mask = 0; uint32 iy[4]; int32 startPos = stream->pos(); - byte r = 0, g = 0, b = 0; for (uint16 y = _curFrame.strips[strip].rect.top; y < _curFrame.strips[strip].rect.bottom; y += 4) { iy[0] = _curFrame.strips[strip].rect.left + y * _curFrame.width; diff --git a/video/codecs/cinepak.h b/video/codecs/cinepak.h index ca4552fae6..f4adfd50fe 100644 --- a/video/codecs/cinepak.h +++ b/video/codecs/cinepak.h @@ -36,8 +36,9 @@ class SeekableReadStream; namespace Video { struct CinepakCodebook { - byte y[4]; - byte u, v; + // These are not in the normal YUV colorspace, but in the Cinepak YUV colorspace instead. + byte y[4]; // [0, 255] + int8 u, v; // [-128, 127] }; struct CinepakStrip { @@ -58,6 +59,13 @@ struct CinepakFrame { Graphics::Surface *surface; }; +/** + * Cinepak decoder. + * + * Used in video: + * - AVIDecoder + * - QuickTimeDecoder + */ class CinepakDecoder : public Codec { public: CinepakDecoder(int bitsPerPixel = 24); @@ -70,6 +78,7 @@ private: CinepakFrame _curFrame; int32 _y; Graphics::PixelFormat _pixelFormat; + byte *_clipTable, *_clipTableBuf; void loadCodebook(Common::SeekableReadStream *stream, uint16 strip, byte codebookType, byte chunkID, uint32 chunkSize); void decodeVectors(Common::SeekableReadStream *stream, uint16 strip, byte chunkID, uint32 chunkSize); diff --git a/video/codecs/codec.h b/video/codecs/codec.h index 8e4691ca3c..c1194e461b 100644 --- a/video/codecs/codec.h +++ b/video/codecs/codec.h @@ -32,16 +32,48 @@ class SeekableReadStream; namespace Video { +/** + * An abstract representation of a video codec used for decoding + * video frames. + * + * Used in video: + * - AVIDecoder + * - QuickTimeDecoder + * - VMDDecoder + */ class Codec { public: Codec() {} virtual ~Codec() {} + /** + * Decode the frame for the given data and return a pointer to a surface + * containing the decoded frame. + * + * @return a pointer to the decoded frame + * @note stream is not deleted + */ virtual const Graphics::Surface *decodeImage(Common::SeekableReadStream *stream) = 0; + + /** + * Get the format that the surface returned from decodeImage() will + * be in. + */ virtual Graphics::PixelFormat getPixelFormat() const = 0; + /** + * Can this codec's frames contain a palette? + */ virtual bool containsPalette() const { return false; } + + /** + * Get the palette last decoded from decodeImage + */ virtual const byte *getPalette() { return 0; } + + /** + * Does the codec have a dirty palette? + */ virtual bool hasDirtyPalette() const { return false; } }; diff --git a/video/codecs/indeo3.h b/video/codecs/indeo3.h index a07d779dec..880901df13 100644 --- a/video/codecs/indeo3.h +++ b/video/codecs/indeo3.h @@ -36,6 +36,13 @@ namespace Video { +/** + * Intel Indeo 3 decoder. + * + * Used in video: + * - AVIDecoder + * - VMDDecoder + */ class Indeo3Decoder : public Codec { public: Indeo3Decoder(uint16 width, uint16 height); diff --git a/video/codecs/mjpeg.h b/video/codecs/mjpeg.h index 0c3b668a74..d71454799c 100644 --- a/video/codecs/mjpeg.h +++ b/video/codecs/mjpeg.h @@ -36,10 +36,12 @@ struct Surface; namespace Video { -// Motion JPEG Decoder -// Basically a wrapper around JPEG which converts to RGB and also functions -// as a Codec. - +/** + * Motion JPEG decoder. + * + * Used in video: + * - QuickTimeDecoder + */ class JPEGDecoder : public Codec { public: JPEGDecoder(); diff --git a/video/codecs/msrle.h b/video/codecs/msrle.h index 2aea66d113..64ebaaee51 100644 --- a/video/codecs/msrle.h +++ b/video/codecs/msrle.h @@ -27,6 +27,12 @@ namespace Video { +/** + * Microsoft Run-Length Encoding decoder. + * + * Used in video: + * - AVIDecoder + */ class MSRLEDecoder : public Codec { public: MSRLEDecoder(uint16 width, uint16 height, byte bitsPerPixel); diff --git a/video/codecs/msvideo1.h b/video/codecs/msvideo1.h index 767eece580..047d542743 100644 --- a/video/codecs/msvideo1.h +++ b/video/codecs/msvideo1.h @@ -27,6 +27,12 @@ namespace Video { +/** + * Microsoft Video 1 decoder. + * + * Used in video: + * - AVIDecoder + */ class MSVideo1Decoder : public Codec { public: MSVideo1Decoder(uint16 width, uint16 height, byte bitsPerPixel); diff --git a/video/codecs/qtrle.h b/video/codecs/qtrle.h index d9db58ab23..a1dd9c9188 100644 --- a/video/codecs/qtrle.h +++ b/video/codecs/qtrle.h @@ -28,6 +28,12 @@ namespace Video { +/** + * QuickTime Run-Length Encoding decoder. + * + * Used in video: + * - QuickTimeDecoder + */ class QTRLEDecoder : public Codec { public: QTRLEDecoder(uint16 width, uint16 height, byte bitsPerPixel); diff --git a/video/codecs/rpza.cpp b/video/codecs/rpza.cpp index acc1b7f358..0a9f87747e 100644 --- a/video/codecs/rpza.cpp +++ b/video/codecs/rpza.cpp @@ -163,7 +163,7 @@ const Graphics::Surface *RPZADecoder::decodeImage(Common::SeekableReadStream *st blockPtr = rowPtr + pixelPtr; for (byte pixel_y = 0; pixel_y < 4; pixel_y++) { byte index = stream->readByte(); - for (byte pixel_x = 0; pixel_x < 4; pixel_x++){ + for (byte pixel_x = 0; pixel_x < 4; pixel_x++) { byte idx = (index >> (2 * (3 - pixel_x))) & 0x03; PUT_PIXEL(color4[idx]); } @@ -177,7 +177,7 @@ const Graphics::Surface *RPZADecoder::decodeImage(Common::SeekableReadStream *st case 0x00: blockPtr = rowPtr + pixelPtr; for (byte pixel_y = 0; pixel_y < 4; pixel_y++) { - for (byte pixel_x = 0; pixel_x < 4; pixel_x++){ + for (byte pixel_x = 0; pixel_x < 4; pixel_x++) { // We already have color of upper left pixel if (pixel_y != 0 || pixel_x != 0) colorA = stream->readUint16BE(); diff --git a/video/codecs/rpza.h b/video/codecs/rpza.h index f082d25549..67e0699692 100644 --- a/video/codecs/rpza.h +++ b/video/codecs/rpza.h @@ -28,6 +28,12 @@ namespace Video { +/** + * Apple RPZA decoder. + * + * Used in video: + * - QuickTimeDecoder + */ class RPZADecoder : public Codec { public: RPZADecoder(uint16 width, uint16 height); diff --git a/video/codecs/smc.h b/video/codecs/smc.h index f2caca977a..4b9f57410a 100644 --- a/video/codecs/smc.h +++ b/video/codecs/smc.h @@ -34,6 +34,12 @@ enum { COLORS_PER_TABLE = 256 }; +/** + * Apple SMC decoder. + * + * Used in video: + * - QuickTimeDecoder + */ class SMCDecoder : public Codec { public: SMCDecoder(uint16 width, uint16 height); diff --git a/video/codecs/svq1.cpp b/video/codecs/svq1.cpp index eba0c90305..57e84968a3 100644 --- a/video/codecs/svq1.cpp +++ b/video/codecs/svq1.cpp @@ -180,28 +180,29 @@ const Graphics::Surface *SVQ1Decoder::decodeImage(Common::SeekableReadStream *st frameData.skip(8); } - int yWidth = ALIGN(_frameWidth, 16); - int yHeight = ALIGN(_frameHeight, 16); - int uvWidth = ALIGN(yWidth / 4, 16); - int uvHeight = ALIGN(yHeight / 4, 16); + uint yWidth = ALIGN(_frameWidth, 16); + uint yHeight = ALIGN(_frameHeight, 16); + uint uvWidth = ALIGN(yWidth / 4, 16); + uint uvHeight = ALIGN(yHeight / 4, 16); + uint uvPitch = uvWidth + 4; // we need at least one extra column and pitch must be divisible by 4 byte *current[3]; // Decode Y, U and V component planes for (int i = 0; i < 3; i++) { - int width, height; + uint width, height, pitch; if (i == 0) { width = yWidth; height = yHeight; + pitch = width; current[i] = new byte[width * height]; } else { width = uvWidth; height = uvHeight; + pitch = uvPitch; - // Add an extra row's worth of data to not go out-of-bounds in the - // color conversion. Then fill that with "empty" data. - current[i] = new byte[width * (height + 1)]; - memset(current[i] + width * height, 0x80, width); + // Add an extra row here. See below for more information. + current[i] = new byte[pitch * (height + 1)]; } if (frameType == 0) { // I Frame @@ -209,12 +210,12 @@ const Graphics::Surface *SVQ1Decoder::decodeImage(Common::SeekableReadStream *st byte *currentP = current[i]; for (uint16 y = 0; y < height; y += 16) { for (uint16 x = 0; x < width; x += 16) { - if (!svq1DecodeBlockIntra(&frameData, ¤tP[x], width)) { + if (!svq1DecodeBlockIntra(&frameData, ¤tP[x], pitch)) { warning("svq1DecodeBlockIntra decode failure"); return _surface; } } - currentP += 16 * width; + currentP += 16 * pitch; } } else { // Delta frame (P or B) @@ -222,9 +223,9 @@ const Graphics::Surface *SVQ1Decoder::decodeImage(Common::SeekableReadStream *st // Prediction Motion Vector Common::Point *pmv = new Common::Point[(width / 8) + 3]; - byte *previous; + byte *previous = 0; if (frameType == 2) { // B Frame - warning("B Frame not supported currently"); + error("SVQ1 Video: B Frames not supported"); //previous = _next[i]; } else { previous = _last[i]; @@ -233,7 +234,7 @@ const Graphics::Surface *SVQ1Decoder::decodeImage(Common::SeekableReadStream *st byte *currentP = current[i]; for (uint16 y = 0; y < height; y += 16) { for (uint16 x = 0; x < width; x += 16) { - if (!svq1DecodeDeltaBlock(&frameData, ¤tP[x], previous, width, pmv, x, y)) { + if (!svq1DecodeDeltaBlock(&frameData, ¤tP[x], previous, pitch, pmv, x, y)) { warning("svq1DecodeDeltaBlock decode failure"); return _surface; } @@ -241,7 +242,7 @@ const Graphics::Surface *SVQ1Decoder::decodeImage(Common::SeekableReadStream *st pmv[0].x = pmv[0].y = 0; - currentP += 16 * width; + currentP += 16 * pitch; } delete[] pmv; @@ -256,7 +257,21 @@ const Graphics::Surface *SVQ1Decoder::decodeImage(Common::SeekableReadStream *st _surface->h = _height; } - convertYUV410ToRGB(_surface, current[0], current[1], current[2], yWidth, yHeight, yWidth, uvWidth); + // We need to massage the chrominance data a bit to be able to be used by the converter + // Since the thing peeks at values one column and one row beyond the data, we need to fill it in + + // First, fill in the column-after-last with the last column's value + for (uint i = 0; i < uvHeight; i++) { + current[1][i * uvPitch + uvWidth] = current[1][i * uvPitch + uvWidth - 1]; + current[2][i * uvPitch + uvWidth] = current[2][i * uvPitch + uvWidth - 1]; + } + + // Then, copy the last row to the one after the last row + memcpy(current[1] + uvHeight * uvPitch, current[1] + (uvHeight - 1) * uvPitch, uvWidth + 1); + memcpy(current[2] + uvHeight * uvPitch, current[2] + (uvHeight - 1) * uvPitch, uvWidth + 1); + + // Finally, actually do the conversion ;) + YUVToRGBMan.convert410(_surface, Graphics::YUVToRGBManager::kScaleFull, current[0], current[1], current[2], yWidth, yHeight, yWidth, uvPitch); // Store the current surfaces for later and free the old ones for (int i = 0; i < 3; i++) { @@ -317,7 +332,7 @@ bool SVQ1Decoder::svq1DecodeBlockIntra(Common::BitStream *s, byte *pixels, int p for (uint y = 0; y < height; y++) memset(&dst[y * (pitch / 4)], mean, width); } else { - const uint32 *codebook = s_svq1IntraCodebooks[level]; + const uint32 *codebook = (const uint32 *)s_svq1IntraCodebooks[level]; uint32 bitCache = s->getBits(stages * 4); // calculate codebook entries for this vector @@ -336,7 +351,7 @@ bool SVQ1Decoder::svq1DecodeBlockIntra(Common::BitStream *s, byte *pixels, int p // add codebook entries to vector for (int j = 0; j < stages; j++) { - n3 = codebook[entries[j]] ^ 0x80808080; + n3 = READ_UINT32(&codebook[entries[j]]) ^ 0x80808080; n1 += (n3 & 0xFF00FF00) >> 8; n2 += n3 & 0x00FF00FF; } @@ -409,7 +424,7 @@ bool SVQ1Decoder::svq1DecodeBlockNonIntra(Common::BitStream *s, byte *pixels, in } int mean = _interMean->getSymbol(*s) - 256; - const uint32 *codebook = s_svq1InterCodebooks[level]; + const uint32 *codebook = (const uint32 *)s_svq1InterCodebooks[level]; uint32 bitCache = s->getBits(stages * 4); // calculate codebook entries for this vector @@ -430,7 +445,7 @@ bool SVQ1Decoder::svq1DecodeBlockNonIntra(Common::BitStream *s, byte *pixels, in // add codebook entries to vector for (int j = 0; j < stages; j++) { - n3 = codebook[entries[j]] ^ 0x80808080; + n3 = READ_UINT32(&codebook[entries[j]]) ^ 0x80808080; n1 += (n3 & 0xFF00FF00) >> 8; n2 += n3 & 0x00FF00FF; } diff --git a/video/codecs/svq1.h b/video/codecs/svq1.h index e5066abfd4..6667fea344 100644 --- a/video/codecs/svq1.h +++ b/video/codecs/svq1.h @@ -33,6 +33,12 @@ struct Point; namespace Video { +/** + * Sorenson Vector Quantizer 1 decoder. + * + * Used in video: + * - QuickTimeDecoder + */ class SVQ1Decoder : public Codec { public: SVQ1Decoder(uint16 width, uint16 height); diff --git a/video/codecs/svq1_cb.h b/video/codecs/svq1_cb.h index f9a8c54e40..8281b3fc28 100644 --- a/video/codecs/svq1_cb.h +++ b/video/codecs/svq1_cb.h @@ -30,1477 +30,1477 @@ namespace Video { -static const uint32 s_svq1InterCodebook4x2[192] = { - 0xf9fa0207, 0xfcfd0307, 0x0807fef9, 0x0403fcf8, - 0x03091113, 0xf8f4f0f2, 0xfdf8f0ee, 0x080c0e0b, - 0x14f6f007, 0x14f6ef07, 0xeb0812fa, 0xec0912f9, - 0xf2ec0319, 0xf3ee071d, 0x0e15fce3, 0x0e14fae1, - 0xe8e4e6ed, 0x0a16201f, 0x1c1f180f, 0xf3eae0e0, - 0xe6e9f802, 0x231b03f7, 0x15150b03, 0xdee5fc08, - 0x2f0ce1e2, 0x2f0de2e3, 0xd2ef1e26, 0xd2ed1a22, - 0xd5cdced6, 0x30373022, 0x2a333630, 0xd1cbccd4, - 0xfa000504, 0x0100fefe, 0xfefffaf5, 0x01090801, - 0x05fa0100, 0x02f40108, 0x08f9f207, 0x0800f805, - 0x080b0401, 0xfb00f8f4, 0x040001ff, 0x1003f8f1, - 0xfafc0811, 0xf8f3fc09, 0xee010602, 0xf40b0bff, - 0x00020006, 0xebf9060e, 0xecf3ff01, 0x150a0101, - 0x0d07fbea, 0x0c04fff5, 0x130e00f9, 0xedfb03fc, - 0x0f0af2e6, 0xfefa0412, 0xeefb1319, 0x0204f9ec, - 0xfcfffaf3, 0xddfe2519, 0x01010405, 0x2b02dceb, - 0x03fffe02, 0xfffafe08, 0x0c02fdfe, 0xfffefefb, - 0xfbfffffd, 0xfe0807ff, 0xfd050702, 0xf8fd0101, - 0xfefdfffd, 0x0d02fdfe, 0xfaf5000f, 0x00000003, - 0xfcfbf7fa, 0x03010412, 0x0400030c, 0xfd03fdf0, - 0x021203ef, 0xfffffdff, 0x00f810fa, 0x00f90ef7, - 0xfb0ef303, 0xfc0ef303, 0xe90e14f9, 0x04f8f908, - 0x10edf108, 0xfd0b0df6, 0x1a01ff09, 0x02e5f105, - 0xfc1007ec, 0x011f09d8, 0xf9e2f41a, 0x04edfe28, - 0x00000006, 0x0201fefa, 0xfa00ff00, 0xfffe0009, - 0xfd0208f9, 0x02fd02ff, 0x04fefc07, 0xfa000002, - 0x0209fefd, 0xfc00fffe, 0xfd00fdfd, 0x040a02fa, - 0x08f60003, 0x04fc0000, 0x020401ff, 0x07f7f903, - 0xfcf70102, 0x00000cff, 0xfc07ff03, 0x0204f203, - 0x0b01f7f4, 0x00010502, 0x02000103, 0xed060800, - 0xfcf9f6fa, 0x07050709, 0xfd031506, 0xfefbf7f5, - 0xfff0f7fc, 0x2401fbfe, 0x00130b08, 0xd7fc0502, - 0xfffeffff, 0x0601fefe, 0xf8010400, 0x00010101, - 0x0004fdfe, 0xfd03ff02, 0x01fc0301, 0xfb0003ff, - 0x03020403, 0xfffafdfe, 0x02fefdfe, 0x000108fc, - 0x060204f9, 0x0001fff9, 0x01fc02fe, 0xff02fa08, - 0x020002fa, 0xfff80405, 0x0900f5ff, 0x0202fe00, - 0xfffcfb11, 0xfefefcff, 0xfd09f300, 0x02f90cff, - 0x02fbfe00, 0xfd14fbf9, 0xe2ff0707, 0x01080503, - 0xfcff03fa, 0x12f5fe02, 0x0e03f900, 0xf7eefd14, - 0xff00fe07, 0xff0000fe, 0x0001fffc, 0x040002fe, - 0x0102fd01, 0x01fb0103, 0xfeff00fd, 0xfd000107, - 0xfe000502, 0x01fffb02, 0xff04feff, 0x0005fd00, - 0xfeff0300, 0xff0501fc, 0x09ff00ff, 0xfffffeff, - 0xff0505fe, 0xfefd02fe, 0x01f50201, 0x02030102, - 0xfefff602, 0x01040204, 0x01fb0504, 0x01f50600, - 0x06060001, 0xf1010200, 0x09050307, 0x020202e2, - 0x020901de, 0x02080805, 0x06060207, 0x0401e502 +static const int8 s_svq1InterCodebook4x2[768] = { + 7, 2, -6, -7, 7, 3, -3, -4, -7, -2, 7, 8, -8, -4, 3, 4, + 19, 17, 9, 3,-14,-16,-12, -8,-18,-16, -8, -3, 11, 14, 12, 8, + 7,-16,-10, 20, 7,-17,-10, 20, -6, 18, 8,-21, -7, 18, 9,-20, + 25, 3,-20,-14, 29, 7,-18,-13,-29, -4, 21, 14,-31, -6, 20, 14, + -19,-26,-28,-24, 31, 32, 22, 10, 15, 24, 31, 28,-32,-32,-22,-13, + 2, -8,-23,-26, -9, 3, 27, 35, 3, 11, 21, 21, 8, -4,-27,-34, + -30,-31, 12, 47,-29,-30, 13, 47, 38, 30,-17,-46, 34, 26,-19,-46, + -42,-50,-51,-43, 34, 48, 55, 48, 48, 54, 51, 42,-44,-52,-53,-47, + 4, 5, 0, -6, -2, -2, 0, 1,-11, -6, -1, -2, 1, 8, 9, 1, + 0, 1, -6, 5, 8, 1,-12, 2, 7,-14, -7, 8, 5, -8, 0, 8, + 1, 4, 11, 8,-12, -8, 0, -5, -1, 1, 0, 4,-15, -8, 3, 16, + 17, 8, -4, -6, 9, -4,-13, -8, 2, 6, 1,-18, -1, 11, 11,-12, + 6, 0, 2, 0, 14, 6, -7,-21, 1, -1,-13,-20, 1, 1, 10, 21, + -22, -5, 7, 13,-11, -1, 4, 12, -7, 0, 14, 19, -4, 3, -5,-19, + -26,-14, 10, 15, 18, 4, -6, -2, 25, 19, -5,-18,-20, -7, 4, 2, + -13, -6, -1, -4, 25, 37, -2,-35, 5, 4, 1, 1,-21,-36, 2, 43, + 2, -2, -1, 3, 8, -2, -6, -1, -2, -3, 2, 12, -5, -2, -2, -1, + -3, -1, -1, -5, -1, 7, 8, -2, 2, 7, 5, -3, 1, 1, -3, -8, + -3, -1, -3, -2, -2, -3, 2, 13, 15, 0,-11, -6, 3, 0, 0, 0, + -6, -9, -5, -4, 18, 4, 1, 3, 12, 3, 0, 4,-16, -3, 3, -3, + -17, 3, 18, 2, -1, -3, -1, -1, -6, 16, -8, 0, -9, 14, -7, 0, + 3,-13, 14, -5, 3,-13, 14, -4, -7, 20, 14,-23, 8, -7, -8, 4, + 8,-15,-19, 16,-10, 13, 11, -3, 9, -1, 1, 26, 5,-15,-27, 2, + -20, 7, 16, -4,-40, 9, 31, 1, 26,-12,-30, -7, 40, -2,-19, 4, + 6, 0, 0, 0, -6, -2, 1, 2, 0, -1, 0, -6, 9, 0, -2, -1, + -7, 8, 2, -3, -1, 2, -3, 2, 7, -4, -2, 4, 2, 0, 0, -6, + -3, -2, 9, 2, -2, -1, 0, -4, -3, -3, 0, -3, -6, 2, 10, 4, + 3, 0,-10, 8, 0, 0, -4, 4, -1, 1, 4, 2, 3, -7, -9, 7, + 2, 1, -9, -4, -1, 12, 0, 0, 3, -1, 7, -4, 3,-14, 4, 2, + -12, -9, 1, 11, 2, 5, 1, 0, 3, 1, 0, 2, 0, 8, 6,-19, + -6,-10, -7, -4, 9, 7, 5, 7, 6, 21, 3, -3,-11, -9, -5, -2, + -4, -9,-16, -1, -2, -5, 1, 36, 8, 11, 19, 0, 2, 5, -4,-41, + -1, -1, -2, -1, -2, -2, 1, 6, 0, 4, 1, -8, 1, 1, 1, 0, + -2, -3, 4, 0, 2, -1, 3, -3, 1, 3, -4, 1, -1, 3, 0, -5, + 3, 4, 2, 3, -2, -3, -6, -1, -2, -3, -2, 2, -4, 8, 1, 0, + -7, 4, 2, 6, -7, -1, 1, 0, -2, 2, -4, 1, 8, -6, 2, -1, + -6, 2, 0, 2, 5, 4, -8, -1, -1,-11, 0, 9, 0, -2, 2, 2, + 17, -5, -4, -1, -1, -4, -2, -2, 0,-13, 9, -3, -1, 12, -7, 2, + 0, -2, -5, 2, -7, -5, 20, -3, 7, 7, -1,-30, 3, 5, 8, 1, + -6, 3, -1, -4, 2, -2,-11, 18, 0, -7, 3, 14, 20, -3,-18, -9, + 7, -2, 0, -1, -2, 0, 0, -1, -4, -1, 1, 0, -2, 2, 0, 4, + 1, -3, 2, 1, 3, 1, -5, 1, -3, 0, -1, -2, 7, 1, 0, -3, + 2, 5, 0, -2, 2, -5, -1, 1, -1, -2, 4, -1, 0, -3, 5, 0, + 0, 3, -1, -2, -4, 1, 5, -1, -1, 0, -1, 9, -1, -2, -1, -1, + -2, 5, 5, -1, -2, 2, -3, -2, 1, 2,-11, 1, 2, 1, 3, 2, + 2,-10, -1, -2, 4, 2, 4, 1, 4, 5, -5, 1, 0, 6,-11, 1, + 1, 0, 6, 6, 0, 2, 1,-15, 7, 3, 5, 9,-30, 2, 2, 2, + -34, 1, 9, 2, 5, 8, 8, 2, 7, 2, 6, 6, 2,-27, 1, 4 }; -static const uint32 s_svq1InterCodebook4x4[384] = { - 0xf9fa0004, 0xf7f3f8fc, 0x06fff8f8, 0x1b1605fe, - 0x0a0bf9f0, 0x0a0df9ee, 0x080cfcf1, 0x0509fff7, - 0xf00f02fe, 0xed1302fd, 0xed1302fd, 0xf20f03fe, - 0x10161611, 0xfefbf9fa, 0xf4f0f0f4, 0xfdff0101, - 0x0800ef0b, 0x09ffeb0e, 0x08feeb0e, 0x06fef00b, - 0x0bf0fe07, 0x0eebfe09, 0x0eeaff0a, 0x0aeeff08, - 0xf70310f6, 0xf50414f3, 0xf60415f2, 0xf80310f5, - 0xf7f7040b, 0xf2f4060f, 0xf2f40811, 0xf5f90a10, - 0x0d0e0a04, 0x100f07ff, 0x0803f9f4, 0xf6eee9ec, - 0xe7e6eef6, 0xf5fa0104, 0x030b0f0d, 0x080d0f0c, - 0xf5f0edf0, 0x0b0f0c07, 0x0b10100b, 0xf6f5f7fa, - 0x050c1312, 0xfc051012, 0xf1f60006, 0xeae9eff7, - 0x15fff2f6, 0x1d00eff5, 0x1e01f0f5, 0x1700f2f6, - 0xfaf4eff0, 0xf9f2eded, 0x0201fffd, 0x131d231b, - 0x1717f8db, 0x1d1cf7d6, 0x1c1af6d5, 0x1613f5da, - 0xdff01020, 0xdbee1427, 0xdaed1326, 0xdeef0f20, - 0xfcfa0918, 0x03faf6ff, 0x03fff7f8, 0xfa020703, - 0x00fffdff, 0xf90204ff, 0xf0030bfd, 0xee091401, - 0x0c06f8fd, 0x0d07f6fb, 0x0705f7fa, 0xff02fbfb, - 0xfffd0cf8, 0x01fd0ff6, 0x01fc0df5, 0x02fd08f5, - 0xf4fb0609, 0xf3f80003, 0xfffffcfc, 0x120f01fc, - 0x0c0e0d09, 0xfeff0304, 0xfbf8fbfe, 0xfcf7f5f9, - 0xfcf9fb07, 0xfcf9fe0e, 0xfbf80011, 0xfbf9010f, - 0x0406fff6, 0x0402f7f1, 0x00fdff02, 0xf6f80d19, - 0xf0fd0b07, 0xf1fd0b07, 0xf7fe0706, 0xfbfd0204, - 0x00fffff9, 0x0602fef7, 0x0e06fcf4, 0x1308faf3, - 0xfbf5eeee, 0x040300fd, 0x06060806, 0x06060606, - 0xf60d03fb, 0xf70f01fa, 0xfa0ffdfa, 0xfd0afafa, - 0xf7f70109, 0x0506090b, 0x07080300, 0xfbfaf2f1, - 0x130bfaf5, 0xf8f7fbfe, 0xf6f70206, 0x05040506, - 0x0f08fdf9, 0x0f0a03ff, 0xfeff0505, 0xe7ebfe04, - 0x05fafa06, 0x09f9f708, 0x0df9f408, 0x0ef9f204, - 0x0101fdfc, 0xfdfefbfd, 0xfcfe0007, 0xfcfc0714, - 0x0afaecfd, 0x01000006, 0xff050805, 0xfe0000fd, - 0x02ff060d, 0x03020305, 0x000300fd, 0xfbfef8f0, - 0x00faf9fe, 0x01fdfafd, 0xff02fffb, 0x05100cff, - 0x080901f9, 0x0305fef6, 0x030702fa, 0xf9ff00fc, - 0xe8f70403, 0x03060200, 0x0704ffff, 0xfeff0305, - 0x02f70603, 0x01f30601, 0x02f60801, 0x01f90801, - 0x1602fdfd, 0x0cfbfdfe, 0x02f6fdfe, 0x02fcfffd, - 0x02080c0b, 0xf8fbfbfb, 0xfd00fcfa, 0x0303fffe, - 0xfffefa0c, 0xfefef80c, 0xfd00f909, 0xfe02fa04, - 0xfd0c01ed, 0xfc0504fc, 0xfffe0106, 0x07fefc04, - 0xf8f9fcfd, 0x00fefcfc, 0x100e02ff, 0x0404fefc, - 0xfb0207ff, 0x01ff00fe, 0x0dfffd04, 0x08f2f406, - 0xfb0405ff, 0xf70305fe, 0xf40407fe, 0xf70407ff, - 0x0101fdfa, 0xfa000b0b, 0xf9fe0406, 0x0a03f6f4, - 0xfefdfdfe, 0x0a0e0b06, 0xf6f6f5f7, 0x02030202, - 0xfff9fbf9, 0x070002ff, 0x090001ff, 0xfffb0403, - 0xfff1ff0a, 0x02fb0104, 0x01ff01fd, 0x040401fd, - 0x0a04ff02, 0x00ff0206, 0xf4f90202, 0xfd0002fc, - 0xf8fffcff, 0xf702ff03, 0xfb050004, 0x03080002, - 0x01010203, 0x0300fe04, 0x0104ff02, 0xe7ff0600, - 0xfcfefeff, 0xfcff00fd, 0x02fcffff, 0x1902fa00, - 0x0005fff5, 0x02fe0007, 0x04fdff0a, 0xfffefbfb, - 0xff030600, 0x01fffffe, 0xfbf4f9ff, 0x04020608, - 0xfaff0602, 0xfcff0a09, 0x00fc0001, 0xfbf7fe03, - 0x000403fc, 0x000303fc, 0x020300f5, 0x020703f5, - 0x0307fc02, 0x0107f801, 0x0104f4ff, 0x0202f703, - 0x09fefe02, 0x0103fdef, 0xfa0107fc, 0x03ff0405, - 0xfc0002ff, 0xff0c08f9, 0xfb0405fe, 0xfef8fb03, - 0xfefb0000, 0x1b03f8fe, 0x06fdfcff, 0xf9fe01fd, - 0xff010404, 0xfdf9f6f9, 0x03050a0a, 0xfdfcfefe, - 0x07050100, 0xecf0fe04, 0x08070400, 0xfffe0002, - 0x110301fe, 0xfffe01fd, 0xfefffeff, 0x00fffbff, - 0x0001fd05, 0x0000fe06, 0xfd00feff, 0xff0801f5, - 0x00000003, 0x01040200, 0x01060002, 0x02fdeefe, - 0x010600f2, 0x01fffefb, 0x010001ff, 0x00040701, - 0xfc0100ff, 0xfc030801, 0x030104fd, 0x01fc01fa, - 0x0303f401, 0xff00f6ff, 0x01020002, 0x04020203, - 0x03000003, 0x01fe0002, 0x00fb0205, 0xfff2ff06, - 0xfdfdfafe, 0x0504ff02, 0x00feff06, 0xfbff0404, - 0x00f501fc, 0x01fc02ff, 0xff03fd02, 0x000ffe01, - 0xfe00ff01, 0x01f9fc01, 0x15fffafe, 0x01ff02fe, - 0x00feff15, 0xfe01fdff, 0xff02fef7, 0xfffc0102, - 0xfa020801, 0x0004fff6, 0x0303fdfc, 0xffff0005, - 0xfe010203, 0x0304fefe, 0xeffc0205, 0x0304fe00, - 0x0300fcf9, 0xff020909, 0xff00faf5, 0x01000105, - 0xf5051100, 0x00fafe03, 0x01fcfe02, 0xff0201fc, - 0xfdfbfffb, 0xfefd05fd, 0xfb021004, 0xffff05fe, - 0x01fc0000, 0x0b0502ff, 0x01feffff, 0xfffdfefc, - 0x000afffb, 0xfd000106, 0x0001fc00, 0xff03fcfe, - 0x00030906, 0x00fe01fe, 0xfefefdfe, 0xfa010001, - 0x01020001, 0x01fe03ff, 0x00f1ff00, 0x060205ff, - 0x02020002, 0x06fcf400, 0xff040100, 0xfc010201, - 0x00f9fe01, 0xff000000, 0x030b02fb, 0xfa000301, - 0xfcf7fd00, 0x00ff0301, 0x00fe0104, 0x06fffd07, - 0x0206fe01, 0xfe03ff00, 0x020004fe, 0x02f202ff, - 0xff000202, 0xf2fd03fe, 0xfd030200, 0x02030105, - 0xf204fd01, 0xff0bfe01, 0x0003ff00, 0x020001ff, - 0x02fd03fe, 0x03fcfffc, 0x030102ff, 0x0702fefa, - 0x000201fe, 0xff0000fe, 0x02ff050c, 0xf901fff8, - 0x02fcfe02, 0xfef5000b, 0xfffd0103, 0xff010300, - 0xfe000300, 0xfdfffa00, 0x00fef90c, 0x0101fe07, - 0x02020201, 0x020002ff, 0x000400e9, 0x03010203, - 0x05fffbfc, 0xff0a05fd, 0xfc030000, 0xfb02ff01 +static const int8 s_svq1InterCodebook4x4[1536] = { + 4, 0, -6, -7, -4, -8,-13, -9, -8, -8, -1, 6, -2, 5, 22, 27, + -16, -7, 11, 10,-18, -7, 13, 10,-15, -4, 12, 8, -9, -1, 9, 5, + -2, 2, 15,-16, -3, 2, 19,-19, -3, 2, 19,-19, -2, 3, 15,-14, + 17, 22, 22, 16, -6, -7, -5, -2,-12,-16,-16,-12, 1, 1, -1, -3, + 11,-17, 0, 8, 14,-21, -1, 9, 14,-21, -2, 8, 11,-16, -2, 6, + 7, -2,-16, 11, 9, -2,-21, 14, 10, -1,-22, 14, 8, -1,-18, 10, + -10, 16, 3, -9,-13, 20, 4,-11,-14, 21, 4,-10,-11, 16, 3, -8, + 11, 4, -9, -9, 15, 6,-12,-14, 17, 8,-12,-14, 16, 10, -7,-11, + 4, 10, 14, 13, -1, 7, 15, 16,-12, -7, 3, 8,-20,-23,-18,-10, + -10,-18,-26,-25, 4, 1, -6,-11, 13, 15, 11, 3, 12, 15, 13, 8, + -16,-19,-16,-11, 7, 12, 15, 11, 11, 16, 16, 11, -6, -9,-11,-10, + 18, 19, 12, 5, 18, 16, 5, -4, 6, 0,-10,-15, -9,-17,-23,-22, + -10,-14, -1, 21,-11,-17, 0, 29,-11,-16, 1, 30,-10,-14, 0, 23, + -16,-17,-12, -6,-19,-19,-14, -7, -3, -1, 1, 2, 27, 35, 29, 19, + -37, -8, 23, 23,-42, -9, 28, 29,-43,-10, 26, 28,-38,-11, 19, 22, + 32, 16,-16,-33, 39, 20,-18,-37, 38, 19,-19,-38, 32, 15,-17,-34, + 24, 9, -6, -4, -1,-10, -6, 3, -8, -9, -1, 3, 3, 7, 2, -6, + -1, -3, -1, 0, -1, 4, 2, -7, -3, 11, 3,-16, 1, 20, 9,-18, + -3, -8, 6, 12, -5,-10, 7, 13, -6, -9, 5, 7, -5, -5, 2, -1, + -8, 12, -3, -1,-10, 15, -3, 1,-11, 13, -4, 1,-11, 8, -3, 2, + 9, 6, -5,-12, 3, 0, -8,-13, -4, -4, -1, -1, -4, 1, 15, 18, + 9, 13, 14, 12, 4, 3, -1, -2, -2, -5, -8, -5, -7,-11, -9, -4, + 7, -5, -7, -4, 14, -2, -7, -4, 17, 0, -8, -5, 15, 1, -7, -5, + -10, -1, 6, 4,-15, -9, 2, 4, 2, -1, -3, 0, 25, 13, -8,-10, + 7, 11, -3,-16, 7, 11, -3,-15, 6, 7, -2, -9, 4, 2, -3, -5, + -7, -1, -1, 0, -9, -2, 2, 6,-12, -4, 6, 14,-13, -6, 8, 19, + -18,-18,-11, -5, -3, 0, 3, 4, 6, 8, 6, 6, 6, 6, 6, 6, + -5, 3, 13,-10, -6, 1, 15, -9, -6, -3, 15, -6, -6, -6, 10, -3, + 9, 1, -9, -9, 11, 9, 6, 5, 0, 3, 8, 7,-15,-14, -6, -5, + -11, -6, 11, 19, -2, -5, -9, -8, 6, 2, -9,-10, 6, 5, 4, 5, + -7, -3, 8, 15, -1, 3, 10, 15, 5, 5, -1, -2, 4, -2,-21,-25, + 6, -6, -6, 5, 8, -9, -7, 9, 8,-12, -7, 13, 4,-14, -7, 14, + -4, -3, 1, 1, -3, -5, -2, -3, 7, 0, -2, -4, 20, 7, -4, -4, + -3,-20, -6, 10, 6, 0, 0, 1, 5, 8, 5, -1, -3, 0, 0, -2, + 13, 6, -1, 2, 5, 3, 2, 3, -3, 0, 3, 0,-16, -8, -2, -5, + -2, -7, -6, 0, -3, -6, -3, 1, -5, -1, 2, -1, -1, 12, 16, 5, + -7, 1, 9, 8,-10, -2, 5, 3, -6, 2, 7, 3, -4, 0, -1, -7, + 3, 4, -9,-24, 0, 2, 6, 3, -1, -1, 4, 7, 5, 3, -1, -2, + 3, 6, -9, 2, 1, 6,-13, 1, 1, 8,-10, 2, 1, 8, -7, 1, + -3, -3, 2, 22, -2, -3, -5, 12, -2, -3,-10, 2, -3, -1, -4, 2, + 11, 12, 8, 2, -5, -5, -5, -8, -6, -4, 0, -3, -2, -1, 3, 3, + 12, -6, -2, -1, 12, -8, -2, -2, 9, -7, 0, -3, 4, -6, 2, -2, + -19, 1, 12, -3, -4, 4, 5, -4, 6, 1, -2, -1, 4, -4, -2, 7, + -3, -4, -7, -8, -4, -4, -2, 0, -1, 2, 14, 16, -4, -2, 4, 4, + -1, 7, 2, -5, -2, 0, -1, 1, 4, -3, -1, 13, 6,-12,-14, 8, + -1, 5, 4, -5, -2, 5, 3, -9, -2, 7, 4,-12, -1, 7, 4, -9, + -6, -3, 1, 1, 11, 11, 0, -6, 6, 4, -2, -7,-12,-10, 3, 10, + -2, -3, -3, -2, 6, 11, 14, 10, -9,-11,-10,-10, 2, 2, 3, 2, + -7, -5, -7, -1, -1, 2, 0, 7, -1, 1, 0, 9, 3, 4, -5, -1, + 10, -1,-15, -1, 4, 1, -5, 2, -3, 1, -1, 1, -3, 1, 4, 4, + 2, -1, 4, 10, 6, 2, -1, 0, 2, 2, -7,-12, -4, 2, 0, -3, + -1, -4, -1, -8, 3, -1, 2, -9, 4, 0, 5, -5, 2, 0, 8, 3, + 3, 2, 1, 1, 4, -2, 0, 3, 2, -1, 4, 1, 0, 6, -1,-25, + -1, -2, -2, -4, -3, 0, -1, -4, -1, -1, -4, 2, 0, -6, 2, 25, + -11, -1, 5, 0, 7, 0, -2, 2, 10, -1, -3, 4, -5, -5, -2, -1, + 0, 6, 3, -1, -2, -1, -1, 1, -1, -7,-12, -5, 8, 6, 2, 4, + 2, 6, -1, -6, 9, 10, -1, -4, 1, 0, -4, 0, 3, -2, -9, -5, + -4, 3, 4, 0, -4, 3, 3, 0,-11, 0, 3, 2,-11, 3, 7, 2, + 2, -4, 7, 3, 1, -8, 7, 1, -1,-12, 4, 1, 3, -9, 2, 2, + 2, -2, -2, 9,-17, -3, 3, 1, -4, 7, 1, -6, 5, 4, -1, 3, + -1, 2, 0, -4, -7, 8, 12, -1, -2, 5, 4, -5, 3, -5, -8, -2, + 0, 0, -5, -2, -2, -8, 3, 27, -1, -4, -3, 6, -3, 1, -2, -7, + 4, 4, 1, -1, -7,-10, -7, -3, 10, 10, 5, 3, -2, -2, -4, -3, + 0, 1, 5, 7, 4, -2,-16,-20, 0, 4, 7, 8, 2, 0, -2, -1, + -2, 1, 3, 17, -3, 1, -2, -1, -1, -2, -1, -2, -1, -5, -1, 0, + 5, -3, 1, 0, 6, -2, 0, 0, -1, -2, 0, -3,-11, 1, 8, -1, + 3, 0, 0, 0, 0, 2, 4, 1, 2, 0, 6, 1, -2,-18, -3, 2, + -14, 0, 6, 1, -5, -2, -1, 1, -1, 1, 0, 1, 1, 7, 4, 0, + -1, 0, 1, -4, 1, 8, 3, -4, -3, 4, 1, 3, -6, 1, -4, 1, + 1,-12, 3, 3, -1,-10, 0, -1, 2, 0, 2, 1, 3, 2, 2, 4, + 3, 0, 0, 3, 2, 0, -2, 1, 5, 2, -5, 0, 6, -1,-14, -1, + -2, -6, -3, -3, 2, -1, 4, 5, 6, -1, -2, 0, 4, 4, -1, -5, + -4, 1,-11, 0, -1, 2, -4, 1, 2, -3, 3, -1, 1, -2, 15, 0, + 1, -1, 0, -2, 1, -4, -7, 1, -2, -6, -1, 21, -2, 2, -1, 1, + 21, -1, -2, 0, -1, -3, 1, -2, -9, -2, 2, -1, 2, 1, -4, -1, + 1, 8, 2, -6,-10, -1, 4, 0, -4, -3, 3, 3, 5, 0, -1, -1, + 3, 2, 1, -2, -2, -2, 4, 3, 5, 2, -4,-17, 0, -2, 4, 3, + -7, -4, 0, 3, 9, 9, 2, -1,-11, -6, 0, -1, 5, 1, 0, 1, + 0, 17, 5,-11, 3, -2, -6, 0, 2, -2, -4, 1, -4, 1, 2, -1, + -5, -1, -5, -3, -3, 5, -3, -2, 4, 16, 2, -5, -2, 5, -1, -1, + 0, 0, -4, 1, -1, 2, 5, 11, -1, -1, -2, 1, -4, -2, -3, -1, + -5, -1, 10, 0, 6, 1, 0, -3, 0, -4, 1, 0, -2, -4, 3, -1, + 6, 9, 3, 0, -2, 1, -2, 0, -2, -3, -2, -2, 1, 0, 1, -6, + 1, 0, 2, 1, -1, 3, -2, 1, 0, -1,-15, 0, -1, 5, 2, 6, + 2, 0, 2, 2, 0,-12, -4, 6, 0, 1, 4, -1, 1, 2, 1, -4, + 1, -2, -7, 0, 0, 0, 0, -1, -5, 2, 11, 3, 1, 3, 0, -6, + 0, -3, -9, -4, 1, 3, -1, 0, 4, 1, -2, 0, 7, -3, -1, 6, + 1, -2, 6, 2, 0, -1, 3, -2, -2, 4, 0, 2, -1, 2,-14, 2, + 2, 2, 0, -1, -2, 3, -3,-14, 0, 2, 3, -3, 5, 1, 3, 2, + 1, -3, 4,-14, 1, -2, 11, -1, 0, -1, 3, 0, -1, 1, 0, 2, + -2, 3, -3, 2, -4, -1, -4, 3, -1, 2, 1, 3, -6, -2, 2, 7, + -2, 1, 2, 0, -2, 0, 0, -1, 12, 5, -1, 2, -8, -1, 1, -7, + 2, -2, -4, 2, 11, 0,-11, -2, 3, 1, -3, -1, 0, 3, 1, -1, + 0, 3, 0, -2, 0, -6, -1, -3, 12, -7, -2, 0, 7, -2, 1, 1, + 1, 2, 2, 2, -1, 2, 0, 2,-23, 0, 4, 0, 3, 2, 1, 3, + -4, -5, -1, 5, -3, 5, 10, -1, 0, 0, 3, -4, 1, -1, 2, -5 }; -static const uint32 s_svq1InterCodebook8x4[768] = { - 0x00040809, 0xfdfcfcfd, 0xff040809, 0xfdfbfbfc, - 0xfe030708, 0xfcfbfbfb, 0xfe010406, 0xfdfcfbfc, - 0xfcf5f2f4, 0x06060501, 0xfbf9f6f8, 0x010101fe, - 0x01030405, 0xffff0000, 0x06090d0d, 0xfeff0003, - 0xfffdfcfc, 0x0b080401, 0xfefcfafb, 0x0c080300, - 0xfcfaf9f9, 0x0a0702fe, 0xfcfbf9f9, 0x080501fe, - 0x01fffefd, 0x06070603, 0x07050302, 0x04060808, - 0x03040504, 0xf9fafe01, 0xf9fe0001, 0xf0eff2f6, - 0x0801fcfb, 0xf9fd0309, 0x0b01faf9, 0xf8fd050c, - 0x0900f9f8, 0xf9fd050b, 0x05fffaf8, 0xfafe0408, - 0xf8f9fbfc, 0xfaf8f7f7, 0xf9fafbfc, 0xfefcfaf9, - 0x03020100, 0x090a0805, 0x06030201, 0x0d0e0c09, - 0x05060605, 0x01020304, 0x07070605, 0x04060606, - 0x010100ff, 0x05050503, 0xefeff0f3, 0xfcfaf6f2, - 0x100d0b09, 0x0a0c0d0f, 0xf9fafbfc, 0xfbfaf9f9, - 0xf9f9fafa, 0xfbfaf9f9, 0x0000fffe, 0xff000000, - 0xf0f1f3f5, 0xf6f4f2f0, 0x05040302, 0x03030304, - 0x08080706, 0x05060708, 0x03030403, 0x03030303, - 0x01040403, 0xeff3f9fe, 0x05070705, 0xedf3fb01, - 0x08090806, 0xf0f7ff05, 0x0a0a0806, 0xf5fc0207, - 0xf6ff0912, 0x00fcf7f3, 0xf4ff0c16, 0x02fcf6f1, - 0xf6000d17, 0x02fdf7f3, 0xfa020c14, 0x02fefaf7, - 0xf9fafafa, 0xfaf9f9f9, 0xf8f8f9fa, 0xf8f7f7f7, - 0xfdfdfdfd, 0xfdfdfdfd, 0x15120f0c, 0x0e111315, - 0x1212100e, 0x0d0f1012, 0x05060605, 0x03040405, - 0xf6f7f9fa, 0xf9f7f6f6, 0xf2f3f5f6, 0xf6f4f3f2, - 0x05fcefe5, 0x070a0a09, 0x07fdede0, 0x080b0c0b, - 0x08fef0e2, 0x070a0c0c, 0x0700f4e9, 0x06090b0a, - 0x0c101110, 0xf4f8ff06, 0x0a0f1211, 0xeef1f801, - 0x040a0e0f, 0xe9ecf2fb, 0xff04080a, 0xeaebf0f7, - 0xf5f4f4f6, 0x140e04fb, 0xf4f1f3f5, 0x1b1307fc, - 0xf5f2f3f5, 0x1c1508fd, 0xf7f4f5f6, 0x191208fe, - 0x01ffffff, 0x05060604, 0x02000000, 0xfe010304, - 0x04020000, 0xf6f9ff04, 0x05030000, 0xf1f5fd03, - 0xfff8f3f2, 0xfcff0303, 0x04fffcfb, 0x00030808, - 0x03020203, 0x01030504, 0xfe000305, 0xfffffffe, - 0xfafa0109, 0xfffefdfb, 0xfafa010c, 0x00fffefc, - 0xfcfc040e, 0xfffffefe, 0xffff060e, 0xffffffff, - 0x0a080604, 0x0507090b, 0x00ffffff, 0xfeffff00, - 0xfbfcfcfe, 0xfcfbfbfb, 0xfcfdfdfe, 0xfffefdfc, - 0x04040302, 0x00000103, 0x050401ff, 0x03040506, - 0x02fefaf8, 0x03040403, 0xfbf7f3f2, 0x0000fffe, - 0xfcfbfcfd, 0x0d0c0700, 0xfbfbfcfd, 0x0a0904fe, - 0xfbfcfdfe, 0x0403fffc, 0xfdfeffff, 0x0100fefd, - 0xf8fe0509, 0xfcf9f6f5, 0x02060a0c, 0x0000ff00, - 0x04030202, 0x01010103, 0x00fcf8f7, 0x00010201, - 0x05080806, 0xf3f5fb01, 0x02020100, 0xf5f8fcff, - 0x0301fefd, 0xfcff0103, 0x0502fffe, 0x01040606, - 0x05050403, 0xfafd0104, 0x02040605, 0xfd000202, - 0xfb000506, 0xfefffefb, 0xf5fd0407, 0xfefdf9f4, - 0xffff0001, 0x000000ff, 0x04040302, 0x03040505, - 0xf6f7f7f9, 0xfaf9f7f6, 0x06050403, 0x05050505, - 0xf9f9f9f9, 0xfcfbfafa, 0xfffdfcfb, 0x0000ffff, - 0x0401fefd, 0x05050505, 0x0603fffe, 0x090a0a09, - 0x030a01f2, 0x010100fe, 0x030d02f0, 0x0001fffd, - 0x030c02f1, 0x0101fefc, 0x020a03f6, 0x0101fffd, - 0x02040100, 0x0bfdf6fb, 0x020401ff, 0x0ffef3fa, - 0x010300ff, 0x0ffff4fa, 0x010201ff, 0x0b00f8fc, - 0xfefe050a, 0xfc010502, 0xfaf80007, 0xfc020501, - 0xf9f4fb02, 0xff040702, 0xfcf6f9ff, 0x02070904, - 0xfafcfbfb, 0xfdfbfbfa, 0xfcfefeff, 0xfcfbfafb, - 0x04070706, 0xfdfdfe00, 0x0a0d0e0d, 0xfeff0105, - 0x02020101, 0x02020202, 0xf7f8fafb, 0xfaf9f8f7, - 0x0b0a0907, 0x0507090b, 0xfdfdfeff, 0xfdfcfcfc, - 0x0000ffff, 0xffff0000, 0xfbfcfdfd, 0xfefdfdfc, - 0xfdff0102, 0x00fffefd, 0x03080c0c, 0x01000001, - 0xfaf8f8fa, 0x080602fe, 0xfeff0101, 0x07050300, - 0xff010303, 0x020000ff, 0xff000100, 0xfffeffff, - 0x00000001, 0x04020000, 0x04030102, 0x02000103, - 0x00000102, 0x0300ffff, 0xf4fa0105, 0x04fff8f3, - 0xfeff00fe, 0x030200ff, 0x00fefdfa, 0x01010101, - 0x0400fbf7, 0x00010305, 0x0703fdf8, 0x00010408, - 0x03020201, 0xfdff0103, 0x06050504, 0x00020506, - 0x00000000, 0xfcfe0001, 0xfdfcfdfd, 0xf8f9fcfd, - 0xff060c0e, 0x0000fdfd, 0xfd010507, 0xfffefcfb, - 0xfefefefe, 0xfffffefe, 0x01fffcfa, 0xff000101, - 0xfd010202, 0xfdfaf9fa, 0xfdff0001, 0x060401fe, - 0x02010000, 0x07080704, 0x00000000, 0xf8f9fcff, - 0xfe010200, 0xfffefdfd, 0xfd0001ff, 0x0200fefb, - 0xfbfefffe, 0x090601fc, 0xfcfdfefd, 0x0d0b05fe, - 0x0602fefc, 0xf2f6fd04, 0x0401fffe, 0xfeff0104, - 0xfeff0000, 0x060400fe, 0xfd000202, 0x090500fd, - 0x01fefcfc, 0xf9030906, 0xfffefefe, 0xf5000804, - 0x00000101, 0xf6ff0602, 0x00010202, 0xf9000402, - 0xfafdfeff, 0xf8f8f8f9, 0x01030302, 0xfcfdfeff, - 0x04050505, 0xff000203, 0x03030303, 0x01010202, - 0xfe020303, 0x0a0700fd, 0xfe020201, 0x0300fcfb, - 0x02040300, 0xfcfafbfd, 0x04040200, 0xf9f9fc01, - 0x05050402, 0x06060505, 0xfbfdfcfc, 0xfefdfdfb, - 0xfbfcfcfd, 0xfefefefc, 0x00000101, 0x04050402, - 0x040300fe, 0x02020304, 0x00fcf9f7, 0x06060603, - 0xfefdfbfb, 0x04030100, 0xfe020505, 0xfdfbfafc, - 0x07fcfa01, 0x01fefe05, 0x06fcfb05, 0x01fcfb04, - 0x06fcfb05, 0x01fdfb04, 0x08fdf901, 0x01fdff07, - 0x00fcf9f8, 0x05050402, 0x02050605, 0xf9f9fbff, - 0x01040605, 0xfbfafbfd, 0xfefbf9f9, 0x0a090601, - 0x01000306, 0xf2f80003, 0x01ff0003, 0xfc000304, - 0x01000001, 0x01010102, 0x0201ffff, 0x00ffff01, - 0x01010101, 0x00fdfe00, 0x00010201, 0xfcf7f8fe, - 0x02030301, 0x01fdfd01, 0x01010100, 0x08040101, - 0x07090502, 0x01ffff02, 0x0001fffc, 0x02fffcfd, - 0x030300fd, 0x0200ff00, 0x0101fffc, 0xfcfbfcfe, - 0xfefeff01, 0x050402ff, 0x00010102, 0x0000ffff, - 0x05040302, 0x00010204, 0xfdfaf7f7, 0xffffffff, - 0x0704fafa, 0xfefffe00, 0x0605feff, 0xff00feff, - 0x0001ff04, 0xfe00fefc, 0xfeff0107, 0x010301fd, - 0x03010204, 0x02010103, 0x00fcfe02, 0x00000103, - 0xfcf8fc01, 0x00010201, 0xfaf7fd02, 0x02030300, - 0xff00ffff, 0x020100ff, 0xf8fc0103, 0x0201fdf9, - 0xfefdff02, 0x000100ff, 0x0b0500ff, 0xfdff0309, - 0xfffefeff, 0x01010101, 0x0300ff00, 0x05050606, - 0xffff0102, 0xfcfafbfe, 0x01020202, 0xfbfbfcff, - 0xf9fafdff, 0x01fffcfa, 0x04030505, 0x05040304, - 0xfefdfeff, 0x0100fefe, 0x00000000, 0x03020100, - 0xfffcfafa, 0x02020202, 0xfefbf9fa, 0x00ffff00, - 0x04020202, 0x04030304, 0xff000102, 0x04020000, - 0xf8fb050c, 0x020200fb, 0xfdfafd02, 0xfeff0000, - 0x03fffdfe, 0xfdfe0104, 0x04030202, 0xffff0103, - 0x00010203, 0x00030401, 0xfb000304, 0x030300fa, - 0xf9010302, 0x0301faf4, 0xff040301, 0x0100fcfa, - 0x0602fcf7, 0x00010407, 0x0604fff9, 0xfdfd0004, - 0x040400fa, 0xfefdfe01, 0x020301fc, 0x00fffe00, - 0xfb020500, 0xfc0103fd, 0xfa0204fe, 0xfd0406fd, - 0xfb0305ff, 0xfc0307ff, 0xfa0002ff, 0xfd0305fd, - 0x0503fdf8, 0xfefe0103, 0xfe040402, 0x0301fdfc, - 0xfbfd0102, 0x030403fd, 0x03fbfafb, 0xfbff080a, - 0xfc020300, 0x0600f9f7, 0x0705fffb, 0xfdfdff04, - 0x03fefbfb, 0xfcff0506, 0xfc000609, 0xff0101fe, - 0x01ffffff, 0x00ff0001, 0x000000ff, 0x00ffff00, - 0xfffe0102, 0x00000101, 0xff02080c, 0xf9f9fcff, - 0x06030102, 0x00020407, 0x00ff0001, 0xf8f9fcff, - 0x00ff0000, 0xfdff0000, 0x00000000, 0xfe000101, - 0x010100ff, 0xfeff0000, 0xfdff0000, 0x01fffdfc, - 0x000000ff, 0x0c0a0401, 0xfefe00ff, 0x01fffdfd, - 0xfcfefffd, 0x07090902, 0xfdff00fd, 0x01ff0200, - 0xfdfe01ff, 0x00fdff00, 0xfefd0000, 0x01ffff00, - 0xfffffeff, 0xfefffffe, 0xfffeff02, 0xfe000100, - 0x02feff03, 0xfdff0305, 0x01fbfb01, 0x00020606, - 0xff000201, 0xfe000100, 0x00fffdfb, 0xfe010201, - 0xfefefbf9, 0x0100fefe, 0x010100ff, 0x0c090300, - 0x01050600, 0x0300fdfe, 0x01050600, 0x03020101, - 0xfdfefefb, 0x00000000, 0xfefdfdfa, 0xfeff0000, - 0x01020404, 0x00ffff00, 0x0100fefe, 0x00010102, - 0xff010202, 0xf6f7fbfd, 0xffff0102, 0x01040401, - 0xfefe0004, 0x00fffefe, 0xfdfc0107, 0x010100fe, - 0xfeff050a, 0x00010100, 0xfcfd0105, 0xfefffffd, - 0xfdff0102, 0xff0101fd, 0x0003fffe, 0x000101ff, - 0x020701fd, 0x00fffefd, 0xff0804fe, 0x0200fbf8, - 0x0201fffc, 0xfefcfd01, 0x01fefdfb, 0x06040404, - 0xfdfcfefd, 0x02010100, 0x01020202, 0xffff0102, - 0xff00fffc, 0xfffffdfd, 0x02040401, 0xfdfeff00, - 0x03050604, 0xfcfe0102, 0x01010100, 0xfafcff01, - 0xff020201, 0x02fffbfa, 0x0101fefd, 0x0502fdfc, - 0x0202fffe, 0x0300fcfd, 0x0602fefe, 0x02010205, - 0x00fdfd02, 0x01030200, 0x0301ff03, 0xfbff0201, - 0xfefcf9fb, 0x01080801, 0x000200ff, 0xfd0100fd, - 0xfefbfbfe, 0xfe00fffd, 0x0400fcff, 0x00040200, - 0x0a080000, 0xff030102, 0x0302fdfc, 0xff01fdfd, - 0x02fcfe01, 0xfffe0307, 0xfffe0406, 0x03ff0002, - 0xfefe0101, 0x04fdfbfe, 0x0101fefa, 0x04fefcff, - 0xfefefffe, 0xfe000100, 0xff0001ff, 0xfdff0000, - 0xfcfe0100, 0x0000fffd, 0x00050806, 0x03020100, - 0x0502fefe, 0x01000002, 0xfffefe02, 0x040201ff, - 0x0100ff02, 0x01000000, 0x01fff9f8, 0x0301ffff, - 0x02060300, 0x000201fe, 0x00fff9f6, 0x0102fffd, - 0x02020000, 0xff010101, 0xfefe0003, 0x00010200, - 0x00000108, 0x00fffdfe, 0x0502fe02, 0x01fffe01, - 0xfffdfafd, 0x02fffdfd, 0x02010002, 0x00000102, - 0xfeffff01, 0x000100ff, 0xff02090f, 0xfdfdfdfe, - 0x00fefd00, 0xffffff00, 0x00010001, 0xffffff00, - 0xfe020200, 0xf8f9fdfd, 0x00020200, 0x01010201, - 0x02020201, 0x03000103, 0xfeff0001, 0x0500feff, - 0x01fffaf5, 0xfd010302, 0xff030401, 0xff0201fe, - 0xff010202, 0xff0100fe, 0xffff0000, 0x02030200, - 0x01020101, 0xfc0001ff, 0xfe000000, 0xfe0402fe, - 0x0000fdfe, 0xfa0102ff, 0x05050200, 0xf9ff0203, - 0x00000204, 0xff010303, 0x03ffff00, 0xff010406, - 0x0200fefe, 0xfefe0002, 0xfbff00ff, 0x01fffbf9, - 0x00feff05, 0xfb020402, 0x02fefb00, 0xfa000201, - 0x01000106, 0x0204fffe, 0x00fdfd02, 0x0000feff, - 0x0200ff01, 0x0b060000, 0x00ffff02, 0x0503fefd, - 0x00fffe00, 0xfd0000ff, 0xffffff01, 0xf9fdfffe, - 0xfefe0101, 0xfe010301, 0xff0002ff, 0x000001ff, - 0xff0302fc, 0x0100fefe, 0x0504fef5, 0xfeff0206, - 0xff01fefa, 0x0901fcfd, 0x030300fd, 0x03fdfd02, - 0x00000101, 0x03feff01, 0xfdfd0002, 0x03ffff00, - 0x01fdff01, 0x06fcfa02, 0xfefbfe00, 0x03fefd00, - 0xfe010202, 0xff0201fe, 0xfe0101ff, 0xff0706ff, - 0xfefc0001, 0x01fdfe01, 0xfefd00fc, 0x00fd0002, - 0x010304fd, 0xff000708, 0xfc0104fd, 0xfdfe0302, - 0xfc0106fd, 0xffff0101, 0xfdfd04fe, 0xffff0003, - 0x02fc0201, 0x02fffd04, 0x05fcff03, 0x02fdfa04 +static const int8 s_svq1InterCodebook8x4[3072] = { + 9, 8, 4, 0, -3, -4, -4, -3, 9, 8, 4, -1, -4, -5, -5, -3, + 8, 7, 3, -2, -5, -5, -5, -4, 6, 4, 1, -2, -4, -5, -4, -3, + -12,-14,-11, -4, 1, 5, 6, 6, -8,-10, -7, -5, -2, 1, 1, 1, + 5, 4, 3, 1, 0, 0, -1, -1, 13, 13, 9, 6, 3, 0, -1, -2, + -4, -4, -3, -1, 1, 4, 8, 11, -5, -6, -4, -2, 0, 3, 8, 12, + -7, -7, -6, -4, -2, 2, 7, 10, -7, -7, -5, -4, -2, 1, 5, 8, + -3, -2, -1, 1, 3, 6, 7, 6, 2, 3, 5, 7, 8, 8, 6, 4, + 4, 5, 4, 3, 1, -2, -6, -7, 1, 0, -2, -7,-10,-14,-17,-16, + -5, -4, 1, 8, 9, 3, -3, -7, -7, -6, 1, 11, 12, 5, -3, -8, + -8, -7, 0, 9, 11, 5, -3, -7, -8, -6, -1, 5, 8, 4, -2, -6, + -4, -5, -7, -8, -9, -9, -8, -6, -4, -5, -6, -7, -7, -6, -4, -2, + 0, 1, 2, 3, 5, 8, 10, 9, 1, 2, 3, 6, 9, 12, 14, 13, + 5, 6, 6, 5, 4, 3, 2, 1, 5, 6, 7, 7, 6, 6, 6, 4, + -1, 0, 1, 1, 3, 5, 5, 5,-13,-16,-17,-17,-14,-10, -6, -4, + 9, 11, 13, 16, 15, 13, 12, 10, -4, -5, -6, -7, -7, -7, -6, -5, + -6, -6, -7, -7, -7, -7, -6, -5, -2, -1, 0, 0, 0, 0, 0, -1, + -11,-13,-15,-16,-16,-14,-12,-10, 2, 3, 4, 5, 4, 3, 3, 3, + 6, 7, 8, 8, 8, 7, 6, 5, 3, 4, 3, 3, 3, 3, 3, 3, + 3, 4, 4, 1, -2, -7,-13,-17, 5, 7, 7, 5, 1, -5,-13,-19, + 6, 8, 9, 8, 5, -1, -9,-16, 6, 8, 10, 10, 7, 2, -4,-11, + 18, 9, -1,-10,-13, -9, -4, 0, 22, 12, -1,-12,-15,-10, -4, 2, + 23, 13, 0,-10,-13, -9, -3, 2, 20, 12, 2, -6, -9, -6, -2, 2, + -6, -6, -6, -7, -7, -7, -7, -6, -6, -7, -8, -8, -9, -9, -9, -8, + -3, -3, -3, -3, -3, -3, -3, -3, 12, 15, 18, 21, 21, 19, 17, 14, + 14, 16, 18, 18, 18, 16, 15, 13, 5, 6, 6, 5, 5, 4, 4, 3, + -6, -7, -9,-10,-10,-10, -9, -7,-10,-11,-13,-14,-14,-13,-12,-10, + -27,-17, -4, 5, 9, 10, 10, 7,-32,-19, -3, 7, 11, 12, 11, 8, + -30,-16, -2, 8, 12, 12, 10, 7,-23,-12, 0, 7, 10, 11, 9, 6, + 16, 17, 16, 12, 6, -1, -8,-12, 17, 18, 15, 10, 1, -8,-15,-18, + 15, 14, 10, 4, -5,-14,-20,-23, 10, 8, 4, -1, -9,-16,-21,-22, + -10,-12,-12,-11, -5, 4, 14, 20,-11,-13,-15,-12, -4, 7, 19, 27, + -11,-13,-14,-11, -3, 8, 21, 28,-10,-11,-12, -9, -2, 8, 18, 25, + -1, -1, -1, 1, 4, 6, 6, 5, 0, 0, 0, 2, 4, 3, 1, -2, + 0, 0, 2, 4, 4, -1, -7,-10, 0, 0, 3, 5, 3, -3,-11,-15, + -14,-13, -8, -1, 3, 3, -1, -4, -5, -4, -1, 4, 8, 8, 3, 0, + 3, 2, 2, 3, 4, 5, 3, 1, 5, 3, 0, -2, -2, -1, -1, -1, + 9, 1, -6, -6, -5, -3, -2, -1, 12, 1, -6, -6, -4, -2, -1, 0, + 14, 4, -4, -4, -2, -2, -1, -1, 14, 6, -1, -1, -1, -1, -1, -1, + 4, 6, 8, 10, 11, 9, 7, 5, -1, -1, -1, 0, 0, -1, -1, -2, + -2, -4, -4, -5, -5, -5, -5, -4, -2, -3, -3, -4, -4, -3, -2, -1, + 2, 3, 4, 4, 3, 1, 0, 0, -1, 1, 4, 5, 6, 5, 4, 3, + -8, -6, -2, 2, 3, 4, 4, 3,-14,-13, -9, -5, -2, -1, 0, 0, + -3, -4, -5, -4, 0, 7, 12, 13, -3, -4, -5, -5, -2, 4, 9, 10, + -2, -3, -4, -5, -4, -1, 3, 4, -1, -1, -2, -3, -3, -2, 0, 1, + 9, 5, -2, -8,-11,-10, -7, -4, 12, 10, 6, 2, 0, -1, 0, 0, + 2, 2, 3, 4, 3, 1, 1, 1, -9, -8, -4, 0, 1, 2, 1, 0, + 6, 8, 8, 5, 1, -5,-11,-13, 0, 1, 2, 2, -1, -4, -8,-11, + -3, -2, 1, 3, 3, 1, -1, -4, -2, -1, 2, 5, 6, 6, 4, 1, + 3, 4, 5, 5, 4, 1, -3, -6, 5, 6, 4, 2, 2, 2, 0, -3, + 6, 5, 0, -5, -5, -2, -1, -2, 7, 4, -3,-11,-12, -7, -3, -2, + 1, 0, -1, -1, -1, 0, 0, 0, 2, 3, 4, 4, 5, 5, 4, 3, + -7, -9, -9,-10,-10, -9, -7, -6, 3, 4, 5, 6, 5, 5, 5, 5, + -7, -7, -7, -7, -6, -6, -5, -4, -5, -4, -3, -1, -1, -1, 0, 0, + -3, -2, 1, 4, 5, 5, 5, 5, -2, -1, 3, 6, 9, 10, 10, 9, + -14, 1, 10, 3, -2, 0, 1, 1,-16, 2, 13, 3, -3, -1, 1, 0, + -15, 2, 12, 3, -4, -2, 1, 1,-10, 3, 10, 2, -3, -1, 1, 1, + 0, 1, 4, 2, -5,-10, -3, 11, -1, 1, 4, 2, -6,-13, -2, 15, + -1, 0, 3, 1, -6,-12, -1, 15, -1, 1, 2, 1, -4, -8, 0, 11, + 10, 5, -2, -2, 2, 5, 1, -4, 7, 0, -8, -6, 1, 5, 2, -4, + 2, -5,-12, -7, 2, 7, 4, -1, -1, -7,-10, -4, 4, 9, 7, 2, + -5, -5, -4, -6, -6, -5, -5, -3, -1, -2, -2, -4, -5, -6, -5, -4, + 6, 7, 7, 4, 0, -2, -3, -3, 13, 14, 13, 10, 5, 1, -1, -2, + 1, 1, 2, 2, 2, 2, 2, 2, -5, -6, -8, -9, -9, -8, -7, -6, + 7, 9, 10, 11, 11, 9, 7, 5, -1, -2, -3, -3, -4, -4, -4, -3, + -1, -1, 0, 0, 0, 0, -1, -1, -3, -3, -4, -5, -4, -3, -3, -2, + 2, 1, -1, -3, -3, -2, -1, 0, 12, 12, 8, 3, 1, 0, 0, 1, + -6, -8, -8, -6, -2, 2, 6, 8, 1, 1, -1, -2, 0, 3, 5, 7, + 3, 3, 1, -1, -1, 0, 0, 2, 0, 1, 0, -1, -1, -1, -2, -1, + 1, 0, 0, 0, 0, 0, 2, 4, 2, 1, 3, 4, 3, 1, 0, 2, + 2, 1, 0, 0, -1, -1, 0, 3, 5, 1, -6,-12,-13, -8, -1, 4, + -2, 0, -1, -2, -1, 0, 2, 3, -6, -3, -2, 0, 1, 1, 1, 1, + -9, -5, 0, 4, 5, 3, 1, 0, -8, -3, 3, 7, 8, 4, 1, 0, + 1, 2, 2, 3, 3, 1, -1, -3, 4, 5, 5, 6, 6, 5, 2, 0, + 0, 0, 0, 0, 1, 0, -2, -4, -3, -3, -4, -3, -3, -4, -7, -8, + 14, 12, 6, -1, -3, -3, 0, 0, 7, 5, 1, -3, -5, -4, -2, -1, + -2, -2, -2, -2, -2, -2, -1, -1, -6, -4, -1, 1, 1, 1, 0, -1, + 2, 2, 1, -3, -6, -7, -6, -3, 1, 0, -1, -3, -2, 1, 4, 6, + 0, 0, 1, 2, 4, 7, 8, 7, 0, 0, 0, 0, -1, -4, -7, -8, + 0, 2, 1, -2, -3, -3, -2, -1, -1, 1, 0, -3, -5, -2, 0, 2, + -2, -1, -2, -5, -4, 1, 6, 9, -3, -2, -3, -4, -2, 5, 11, 13, + -4, -2, 2, 6, 4, -3,-10,-14, -2, -1, 1, 4, 4, 1, -1, -2, + 0, 0, -1, -2, -2, 0, 4, 6, 2, 2, 0, -3, -3, 0, 5, 9, + -4, -4, -2, 1, 6, 9, 3, -7, -2, -2, -2, -1, 4, 8, 0,-11, + 1, 1, 0, 0, 2, 6, -1,-10, 2, 2, 1, 0, 2, 4, 0, -7, + -1, -2, -3, -6, -7, -8, -8, -8, 2, 3, 3, 1, -1, -2, -3, -4, + 5, 5, 5, 4, 3, 2, 0, -1, 3, 3, 3, 3, 2, 2, 1, 1, + 3, 3, 2, -2, -3, 0, 7, 10, 1, 2, 2, -2, -5, -4, 0, 3, + 0, 3, 4, 2, -3, -5, -6, -4, 0, 2, 4, 4, 1, -4, -7, -7, + 2, 4, 5, 5, 5, 5, 6, 6, -4, -4, -3, -5, -5, -3, -3, -2, + -3, -4, -4, -5, -4, -2, -2, -2, 1, 1, 0, 0, 2, 4, 5, 4, + -2, 0, 3, 4, 4, 3, 2, 2, -9, -7, -4, 0, 3, 6, 6, 6, + -5, -5, -3, -2, 0, 1, 3, 4, 5, 5, 2, -2, -4, -6, -5, -3, + 1, -6, -4, 7, 5, -2, -2, 1, 5, -5, -4, 6, 4, -5, -4, 1, + 5, -5, -4, 6, 4, -5, -3, 1, 1, -7, -3, 8, 7, -1, -3, 1, + -8, -7, -4, 0, 2, 4, 5, 5, 5, 6, 5, 2, -1, -5, -7, -7, + 5, 6, 4, 1, -3, -5, -6, -5, -7, -7, -5, -2, 1, 6, 9, 10, + 6, 3, 0, 1, 3, 0, -8,-14, 3, 0, -1, 1, 4, 3, 0, -4, + 1, 0, 0, 1, 2, 1, 1, 1, -1, -1, 1, 2, 1, -1, -1, 0, + 1, 1, 1, 1, 0, -2, -3, 0, 1, 2, 1, 0, -2, -8, -9, -4, + 1, 3, 3, 2, 1, -3, -3, 1, 0, 1, 1, 1, 1, 1, 4, 8, + 2, 5, 9, 7, 2, -1, -1, 1, -4, -1, 1, 0, -3, -4, -1, 2, + -3, 0, 3, 3, 0, -1, 0, 2, -4, -1, 1, 1, -2, -4, -5, -4, + 1, -1, -2, -2, -1, 2, 4, 5, 2, 1, 1, 0, -1, -1, 0, 0, + 2, 3, 4, 5, 4, 2, 1, 0, -9, -9, -6, -3, -1, -1, -1, -1, + -6, -6, 4, 7, 0, -2, -1, -2, -1, -2, 5, 6, -1, -2, 0, -1, + 4, -1, 1, 0, -4, -2, 0, -2, 7, 1, -1, -2, -3, 1, 3, 1, + 4, 2, 1, 3, 3, 1, 1, 2, 2, -2, -4, 0, 3, 1, 0, 0, + 1, -4, -8, -4, 1, 2, 1, 0, 2, -3, -9, -6, 0, 3, 3, 2, + -1, -1, 0, -1, -1, 0, 1, 2, 3, 1, -4, -8, -7, -3, 1, 2, + 2, -1, -3, -2, -1, 0, 1, 0, -1, 0, 5, 11, 9, 3, -1, -3, + -1, -2, -2, -1, 1, 1, 1, 1, 0, -1, 0, 3, 6, 6, 5, 5, + 2, 1, -1, -1, -2, -5, -6, -4, 2, 2, 2, 1, -1, -4, -5, -5, + -1, -3, -6, -7, -6, -4, -1, 1, 5, 5, 3, 4, 4, 3, 4, 5, + -1, -2, -3, -2, -2, -2, 0, 1, 0, 0, 0, 0, 0, 1, 2, 3, + -6, -6, -4, -1, 2, 2, 2, 2, -6, -7, -5, -2, 0, -1, -1, 0, + 2, 2, 2, 4, 4, 3, 3, 4, 2, 1, 0, -1, 0, 0, 2, 4, + 12, 5, -5, -8, -5, 0, 2, 2, 2, -3, -6, -3, 0, 0, -1, -2, + -2, -3, -1, 3, 4, 1, -2, -3, 2, 2, 3, 4, 3, 1, -1, -1, + 3, 2, 1, 0, 1, 4, 3, 0, 4, 3, 0, -5, -6, 0, 3, 3, + 2, 3, 1, -7,-12, -6, 1, 3, 1, 3, 4, -1, -6, -4, 0, 1, + -9, -4, 2, 6, 7, 4, 1, 0, -7, -1, 4, 6, 4, 0, -3, -3, + -6, 0, 4, 4, 1, -2, -3, -2, -4, 1, 3, 2, 0, -2, -1, 0, + 0, 5, 2, -5, -3, 3, 1, -4, -2, 4, 2, -6, -3, 6, 4, -3, + -1, 5, 3, -5, -1, 7, 3, -4, -1, 2, 0, -6, -3, 5, 3, -3, + -8, -3, 3, 5, 3, 1, -2, -2, 2, 4, 4, -2, -4, -3, 1, 3, + 2, 1, -3, -5, -3, 3, 4, 3, -5, -6, -5, 3, 10, 8, -1, -5, + 0, 3, 2, -4, -9, -7, 0, 6, -5, -1, 5, 7, 4, -1, -3, -3, + -5, -5, -2, 3, 6, 5, -1, -4, 9, 6, 0, -4, -2, 1, 1, -1, + -1, -1, -1, 1, 1, 0, -1, 0, -1, 0, 0, 0, 0, -1, -1, 0, + 2, 1, -2, -1, 1, 1, 0, 0, 12, 8, 2, -1, -1, -4, -7, -7, + 2, 1, 3, 6, 7, 4, 2, 0, 1, 0, -1, 0, -1, -4, -7, -8, + 0, 0, -1, 0, 0, 0, -1, -3, 0, 0, 0, 0, 1, 1, 0, -2, + -1, 0, 1, 1, 0, 0, -1, -2, 0, 0, -1, -3, -4, -3, -1, 1, + -1, 0, 0, 0, 1, 4, 10, 12, -1, 0, -2, -2, -3, -3, -1, 1, + -3, -1, -2, -4, 2, 9, 9, 7, -3, 0, -1, -3, 0, 2, -1, 1, + -1, 1, -2, -3, 0, -1, -3, 0, 0, 0, -3, -2, 0, -1, -1, 1, + -1, -2, -1, -1, -2, -1, -1, -2, 2, -1, -2, -1, 0, 1, 0, -2, + 3, -1, -2, 2, 5, 3, -1, -3, 1, -5, -5, 1, 6, 6, 2, 0, + 1, 2, 0, -1, 0, 1, 0, -2, -5, -3, -1, 0, 1, 2, 1, -2, + -7, -5, -2, -2, -2, -2, 0, 1, -1, 0, 1, 1, 0, 3, 9, 12, + 0, 6, 5, 1, -2, -3, 0, 3, 0, 6, 5, 1, 1, 1, 2, 3, + -5, -2, -2, -3, 0, 0, 0, 0, -6, -3, -3, -2, 0, 0, -1, -2, + 4, 4, 2, 1, 0, -1, -1, 0, -2, -2, 0, 1, 2, 1, 1, 0, + 2, 2, 1, -1, -3, -5, -9,-10, 2, 1, -1, -1, 1, 4, 4, 1, + 4, 0, -2, -2, -2, -2, -1, 0, 7, 1, -4, -3, -2, 0, 1, 1, + 10, 5, -1, -2, 0, 1, 1, 0, 5, 1, -3, -4, -3, -1, -1, -2, + 2, 1, -1, -3, -3, 1, 1, -1, -2, -1, 3, 0, -1, 1, 1, 0, + -3, 1, 7, 2, -3, -2, -1, 0, -2, 4, 8, -1, -8, -5, 0, 2, + -4, -1, 1, 2, 1, -3, -4, -2, -5, -3, -2, 1, 4, 4, 4, 6, + -3, -2, -4, -3, 0, 1, 1, 2, 2, 2, 2, 1, 2, 1, -1, -1, + -4, -1, 0, -1, -3, -3, -1, -1, 1, 4, 4, 2, 0, -1, -2, -3, + 4, 6, 5, 3, 2, 1, -2, -4, 0, 1, 1, 1, 1, -1, -4, -6, + 1, 2, 2, -1, -6, -5, -1, 2, -3, -2, 1, 1, -4, -3, 2, 5, + -2, -1, 2, 2, -3, -4, 0, 3, -2, -2, 2, 6, 5, 2, 1, 2, + 2, -3, -3, 0, 0, 2, 3, 1, 3, -1, 1, 3, 1, 2, -1, -5, + -5, -7, -4, -2, 1, 8, 8, 1, -1, 0, 2, 0, -3, 0, 1, -3, + -2, -5, -5, -2, -3, -1, 0, -2, -1, -4, 0, 4, 0, 2, 4, 0, + 0, 0, 8, 10, 2, 1, 3, -1, -4, -3, 2, 3, -3, -3, 1, -1, + 1, -2, -4, 2, 7, 3, -2, -1, 6, 4, -2, -1, 2, 0, -1, 3, + 1, 1, -2, -2, -2, -5, -3, 4, -6, -2, 1, 1, -1, -4, -2, 4, + -2, -1, -2, -2, 0, 1, 0, -2, -1, 1, 0, -1, 0, 0, -1, -3, + 0, 1, -2, -4, -3, -1, 0, 0, 6, 8, 5, 0, 0, 1, 2, 3, + -2, -2, 2, 5, 2, 0, 0, 1, 2, -2, -2, -1, -1, 1, 2, 4, + 2, -1, 0, 1, 0, 0, 0, 1, -8, -7, -1, 1, -1, -1, 1, 3, + 0, 3, 6, 2, -2, 1, 2, 0,-10, -7, -1, 0, -3, -1, 2, 1, + 0, 0, 2, 2, 1, 1, 1, -1, 3, 0, -2, -2, 0, 2, 1, 0, + 8, 1, 0, 0, -2, -3, -1, 0, 2, -2, 2, 5, 1, -2, -1, 1, + -3, -6, -3, -1, -3, -3, -1, 2, 2, 0, 1, 2, 2, 1, 0, 0, + 1, -1, -1, -2, -1, 0, 1, 0, 15, 9, 2, -1, -2, -3, -3, -3, + 0, -3, -2, 0, 0, -1, -1, -1, 1, 0, 1, 0, 0, -1, -1, -1, + 0, 2, 2, -2, -3, -3, -7, -8, 0, 2, 2, 0, 1, 2, 1, 1, + 1, 2, 2, 2, 3, 1, 0, 3, 1, 0, -1, -2, -1, -2, 0, 5, + -11, -6, -1, 1, 2, 3, 1, -3, 1, 4, 3, -1, -2, 1, 2, -1, + 2, 2, 1, -1, -2, 0, 1, -1, 0, 0, -1, -1, 0, 2, 3, 2, + 1, 1, 2, 1, -1, 1, 0, -4, 0, 0, 0, -2, -2, 2, 4, -2, + -2, -3, 0, 0, -1, 2, 1, -6, 0, 2, 5, 5, 3, 2, -1, -7, + 4, 2, 0, 0, 3, 3, 1, -1, 0, -1, -1, 3, 6, 4, 1, -1, + -2, -2, 0, 2, 2, 0, -2, -2, -1, 0, -1, -5, -7, -5, -1, 1, + 5, -1, -2, 0, 2, 4, 2, -5, 0, -5, -2, 2, 1, 2, 0, -6, + 6, 1, 0, 1, -2, -1, 4, 2, 2, -3, -3, 0, -1, -2, 0, 0, + 1, -1, 0, 2, 0, 0, 6, 11, 2, -1, -1, 0, -3, -2, 3, 5, + 0, -2, -1, 0, -1, 0, 0, -3, 1, -1, -1, -1, -2, -1, -3, -7, + 1, 1, -2, -2, 1, 3, 1, -2, -1, 2, 0, -1, -1, 1, 0, 0, + -4, 2, 3, -1, -2, -2, 0, 1,-11, -2, 4, 5, 6, 2, -1, -2, + -6, -2, 1, -1, -3, -4, 1, 9, -3, 0, 3, 3, 2, -3, -3, 3, + 1, 1, 0, 0, 1, -1, -2, 3, 2, 0, -3, -3, 0, -1, -1, 3, + 1, -1, -3, 1, 2, -6, -4, 6, 0, -2, -5, -2, 0, -3, -2, 3, + 2, 2, 1, -2, -2, 1, 2, -1, -1, 1, 1, -2, -1, 6, 7, -1, + 1, 0, -4, -2, 1, -2, -3, 1, -4, 0, -3, -2, 2, 0, -3, 0, + -3, 4, 3, 1, 8, 7, 0, -1, -3, 4, 1, -4, 2, 3, -2, -3, + -3, 6, 1, -4, 1, 1, -1, -1, -2, 4, -3, -3, 3, 0, -1, -1, + 1, 2, -4, 2, 4, -3, -1, 2, 3, -1, -4, 5, 4, -6, -3, 2 }; -static const uint32 s_svq1InterCodebook8x8[1536] = { - 0x0504fdfc, 0x00010102, 0x0505fdfb, 0x00000102, - 0x0505fcfa, 0x00000102, 0x0504fcf9, 0x00000102, - 0x0403fbf8, 0x00000102, 0x0403faf8, 0x00010101, - 0x0402faf8, 0x00010102, 0x0402faf8, 0x01010101, - 0xffffffff, 0xffffffff, 0xfefefeff, 0xfefefefe, - 0xfdfdfdfe, 0xfdfdfdfd, 0xfdfdfdfe, 0xfdfcfdfd, - 0xfefefefe, 0xfefdfdfe, 0x01010101, 0xffff0001, - 0x05050504, 0x02030304, 0x08080707, 0x05060708, - 0x04020102, 0xfafc0004, 0x05020101, 0xf9fb0105, - 0x04010201, 0xf8fb0105, 0x05010101, 0xf8fa0005, - 0x05010100, 0xf7fa0106, 0x04010000, 0xf8fb0005, - 0x04010000, 0xf9fb0005, 0x04010000, 0xf9fc0104, - 0x00030201, 0xfffdfcfd, 0x00040301, 0xfffdfcfd, - 0x01050402, 0xfefdfcfd, 0x01060502, 0xfefcfbfd, - 0x01060603, 0xfefcfbfd, 0x01060603, 0xfefcfbfd, - 0x01060603, 0xfefcfbfd, 0x01050503, 0xfefcfcfd, - 0x02020202, 0xff000001, 0x03040404, 0x00010102, - 0x04040504, 0x01020303, 0x04040404, 0x02020304, - 0x03030302, 0x01020303, 0xffffffff, 0x00000000, - 0xfbfafafb, 0xfdfdfcfb, 0xf8f7f7f9, 0xfbfafaf9, - 0x06060606, 0x04050506, 0x03040404, 0x02030303, - 0xffffff00, 0xfffffefe, 0xfafafbfd, 0xfcfbfafa, - 0xf9fafbfd, 0xfcfbfafa, 0xfefefeff, 0xfffffefe, - 0x01010100, 0x01010101, 0x03030303, 0x03030303, - 0xfbfe0102, 0x050200fc, 0xfafe0102, 0x050300fb, - 0xfafe0102, 0x0603fffa, 0xf9fe0203, 0x070400fa, - 0xf9fe0102, 0x070500fb, 0xfafe0102, 0x070400fb, - 0xfafe0102, 0x060400fc, 0xfbfe0101, 0x060300fc, - 0xfcfaf7f6, 0x020302ff, 0xfdfbf7f6, 0x03040400, - 0xfffdf9f7, 0x03050502, 0x00fefbf9, 0x03050503, - 0x0100fdfa, 0x03050604, 0x0201fefc, 0x02040503, - 0x020100fe, 0x01030402, 0x020201ff, 0x01030302, - 0xfafbfbfc, 0xfbfafafa, 0xfcfcfdfd, 0xfcfcfcfc, - 0x00000000, 0xffffffff, 0x05060505, 0x02030405, - 0x07070605, 0x04050607, 0x04040303, 0x02030404, - 0x0000ff00, 0xff00ffff, 0xfcfcfdfd, 0xfdfdfcfc, - 0x01fbfe01, 0x00020405, 0x01fafd01, 0x00020506, - 0x00f9fc00, 0x01020606, 0xfff7fbff, 0x01030606, - 0xfef6faff, 0x01030606, 0xfef7faff, 0x01030605, - 0xfef7fafe, 0x01030505, 0xfef9fafe, 0x01020404, - 0xf7f8f9fb, 0xfaf9f8f7, 0xf9fafafb, 0xfbfafaf9, - 0xfcfdfdfd, 0xfcfcfbfb, 0xff0000ff, 0xffffffff, - 0x02020100, 0x01020202, 0x05040302, 0x04050505, - 0x06050403, 0x07080808, 0x06050403, 0x06070707, - 0x08070605, 0x090a0a09, 0x07060403, 0x08090908, - 0x03020100, 0x05050504, 0xfffffeff, 0x02020100, - 0xfdfdfdfe, 0x00fffefd, 0xfbfbfcfd, 0xfcfbfbfb, - 0xfafbfbfc, 0xfbfaf9f9, 0xfafbfcfd, 0xfafaf9f9, - 0xfd00070d, 0xfbfcfcfd, 0xfd00070e, 0xfcfcfcfd, - 0xfcff080f, 0xfcfbfcfc, 0xfcff080f, 0xfdfcfbfc, - 0xfcff070f, 0xfcfbfbfb, 0xfcff070e, 0xfdfcfcfc, - 0xfcff060c, 0xfdfcfcfc, 0xfcff050b, 0xfdfcfcfc, - 0x0405fcef, 0x03030404, 0x0405fbee, 0x03030404, - 0x0406fbed, 0x02030404, 0x0406fbec, 0x03030404, - 0x0406fcec, 0x03030504, 0x0406fbed, 0x03030504, - 0x0405fcee, 0x02030404, 0x0304fbef, 0x03030404, - 0xfcfafafa, 0x0b0601fe, 0xfcf9f9fa, 0x0d0802fe, - 0xfcf9f8f8, 0x0e0903fe, 0xfbf9f8f8, 0x100a04ff, - 0xfbf9f8f8, 0x110a04ff, 0xfcf9f8f8, 0x100a0500, - 0xfdfaf8f8, 0x0f090400, 0xfdfbf9f9, 0x0c080400, - 0x05070708, 0xf2f8fe02, 0x05070808, 0xf1f8fe02, - 0x05070808, 0xf0f7fd01, 0x05070808, 0xeff6fd01, - 0x05080908, 0xeff6fd01, 0x04070808, 0xf0f6fc01, - 0x04070707, 0xf2f7fd01, 0x03060706, 0xf3f7fd00, - 0xfcfc0105, 0x0000fffd, 0xfdfd0207, 0x0001fffe, - 0xfdfd0107, 0x010100ff, 0xfefd0106, 0x000101ff, - 0xfefc0006, 0x000100ff, 0xfdfc0005, 0xff0000ff, - 0xfffd0005, 0xfe000000, 0xfffe0104, 0xff000100, - 0x01010202, 0xf8f8fafe, 0x01010101, 0xf8f8fbfe, - 0x00010101, 0xfbfbfdff, 0x00000000, 0xfeffffff, - 0x0000ff00, 0x00010000, 0x00000001, 0x02030201, - 0x01010102, 0x03040302, 0x03030303, 0x04050404, - 0xfefdfcfc, 0x01010000, 0xfefdfcfc, 0x010000ff, - 0xfffefefe, 0x0000ffff, 0x00000100, 0xff000000, - 0x02020202, 0x01010202, 0x04040403, 0x03040404, - 0x03010101, 0x03030403, 0xfcfbfafb, 0xfefefdfd, - 0xfffffefc, 0x0100ffff, 0xfffffefc, 0x0100ffff, - 0xfffffefd, 0x020100ff, 0xfffefdfc, 0x030301ff, - 0xfffdfdfc, 0x050401ff, 0xfefefdfc, 0x070401ff, - 0xfffffefe, 0x08060200, 0x010000ff, 0x08070401, - 0xfefdfdfd, 0x00fffffe, 0x0100ffff, 0x03030202, - 0x04020100, 0x05060605, 0x030200ff, 0x03050605, - 0x0200ffff, 0x01020303, 0x00fffefe, 0xfcfcfdff, - 0xffff0000, 0xf9f8fcfe, 0x00010201, 0xf9fafcff, - 0xfa0104fe, 0x00000300, 0xf90105fe, 0x00000300, - 0xf80105fd, 0xffff0300, 0xf70106fe, 0xff000300, - 0xf80206fe, 0xff000400, 0xf90105fd, 0x00000401, - 0xf90104fe, 0x00010400, 0xfa0104ff, 0x00010300, - 0x03000000, 0x01040504, 0x02010101, 0x00020303, - 0x02010202, 0xfeff0102, 0x01010304, 0xfbfdff00, - 0xff010305, 0xfafcfdfe, 0xfe000305, 0xf9fafbfd, - 0xfe000304, 0xfbfbfcfd, 0xff000304, 0xfdfdfefe, - 0x00000000, 0x06fefbff, 0x01000000, 0x08fefaff, - 0x02000000, 0x09fdfa00, 0x0200ff00, 0x0afef900, - 0x0200ff00, 0x0afdf8ff, 0x02ffff00, 0x09fdf9ff, - 0x0100ff00, 0x08fdfaff, 0x01000000, 0x07fefb00, - 0x02030302, 0xffff0001, 0x02030403, 0xfeff0001, - 0x02040403, 0xfdfeff01, 0x02030302, 0xfdfeff00, - 0x010100ff, 0xfefeff00, 0xfffdfcfb, 0x01010100, - 0xfffbf8f8, 0x03040301, 0x00fbf7f6, 0x05060503, - 0x0504fffb, 0x00000103, 0x0504fffa, 0xfeff0002, - 0x0405fffa, 0xfefeff02, 0x0404fff9, 0xfdfdfe01, - 0x0405fffa, 0xfdfdfe01, 0x040400fb, 0xfefeff01, - 0x040500fc, 0xfeffff01, 0x030401fd, 0xfeffff01, - 0x01fefdfe, 0x03050604, 0x00fcfcfd, 0x02040503, - 0xfffbfbfd, 0x01030402, 0xfffcfafc, 0xff020402, - 0x01fdfcfe, 0xff020402, 0x01fefcfe, 0xfe010303, - 0x01fefdfe, 0xfe010303, 0x01fffefe, 0xfe000303, - 0xfefdfcfc, 0x070502ff, 0xfdfdfcfc, 0x070501fe, - 0xfdfefdfe, 0x0503fffd, 0xfe00ffff, 0x0402fefd, - 0xff010101, 0x0301fdfc, 0xff020304, 0x01fffdfc, - 0x00030406, 0x00fefdfd, 0x01030506, 0xfffefdfe, - 0x04080b0c, 0xfffefe00, 0x0206090a, 0x00fffeff, - 0x00020304, 0x0100ffff, 0xffffffff, 0x020100fe, - 0xfefcfbfd, 0x030200fe, 0xfefcfbfb, 0x020100ff, - 0xfefcfbfb, 0x010100ff, 0xfefdfcfc, 0x0000fffe, - 0xff020303, 0xfefdfcfd, 0xfe000203, 0xfefdfcfc, - 0xff010202, 0xfdfcfbfd, 0x01030303, 0xfdfdfdfe, - 0x03040404, 0xfefefe00, 0x03050505, 0xfefeff00, - 0x02040505, 0xfefdfeff, 0x00030303, 0xfcfcfcfe, - 0xfe04ffff, 0xfb0206fe, 0xfe0400ff, 0xfa0206fd, - 0xfe0400ff, 0xf90307fd, 0xfd04ffff, 0xf90308fc, - 0xfd04ff00, 0xfa0307fc, 0xfd04ffff, 0xfa0307fc, - 0xfd03ffff, 0xfa0306fc, 0xfe0300ff, 0xfb0306fd, - 0x02f9fe01, 0x01fffe05, 0x03f8fe01, 0x02fffd06, - 0x04f7fe02, 0x02fefc07, 0x05f7ff03, 0x03fffc07, - 0x04f7ff03, 0x02fefc07, 0x04f9ff03, 0x01fefc06, - 0x04fa0002, 0x01fffc06, 0x03fb0002, 0x01fffd04, - 0x000202fe, 0xfcfdff00, 0x010202fe, 0xfcfe0001, - 0x020202fe, 0xfeff0102, 0x030302fd, 0xfe000204, - 0x020302fd, 0xfd000204, 0x010201fc, 0xfdff0102, - 0x000100fb, 0xfdfe0101, 0x000000fc, 0xfdfe0001, - 0xfeff0000, 0x080702fe, 0xfdff0000, 0x070601fe, - 0xfdff0100, 0x050400fd, 0xff000100, 0x030100ff, - 0x01010200, 0x0100ff00, 0x020100fe, 0xffff0001, - 0x0100fefb, 0xfdfd0001, 0x01fffcfa, 0xfcfdff01, - 0x0502fefc, 0x02030406, 0x0401fdfb, 0x00000204, - 0x0200fefc, 0xfefeff01, 0x0100fffe, 0xfefdfe00, - 0x000000fe, 0xfffeffff, 0x00fffffe, 0x02010000, - 0xfffffefe, 0x04030100, 0xfffefdfe, 0x05040200, - 0xfefe0102, 0x000100ff, 0xfdfd0001, 0x000100ff, - 0xfdfdff00, 0x010101ff, 0xfffd0000, 0x03030201, - 0xfffdff00, 0x03030301, 0xfefcfefe, 0x04040301, - 0xfefcfdfd, 0x04030301, 0xfefbfdfe, 0x03030201, - 0x04030504, 0x05040404, 0x00010303, 0x01000000, - 0xfeff0101, 0xfefdfcfd, 0xfe000202, 0xfefdfcfe, - 0xff010302, 0xfefdfdff, 0x00000201, 0xfffefeff, - 0xff000100, 0xfffefdff, 0xff000101, 0xfefefeff, - 0x00fffffe, 0x00010201, 0x05030201, 0x03050506, - 0x04030201, 0x03040505, 0xfdfdfefe, 0x0000fffe, - 0xfbfcfdfd, 0xfffefdfc, 0xfefeffff, 0x0000fffe, - 0xff000100, 0x010000ff, 0xfeff00ff, 0xfffefefd, - 0x05060707, 0xfeff0204, 0x02020303, 0xfdfe0001, - 0xffffff00, 0xfefeff00, 0xfffefdff, 0x01000000, - 0xfffefe00, 0x020201ff, 0xffff0103, 0x020201ff, - 0xfdfe0103, 0x0201fffe, 0xfafbfe01, 0x00fefdfb, - 0xfdfeff00, 0xfefe00ff, 0xffff0000, 0xfeff0100, - 0xfffe0000, 0xfe000000, 0xfdfdfeff, 0xfdfdfffe, - 0xfdfdfeff, 0xfcfdfefe, 0x00000202, 0xfeff0000, - 0x02030505, 0xff000202, 0x05060808, 0x01020404, - 0xfdfaf8f9, 0xfffeffff, 0x00fdfbfb, 0x00000102, - 0x0300ffff, 0x01010304, 0x03010102, 0x02020304, - 0x02000203, 0x02010203, 0xffff0204, 0x01010100, - 0xfdfe0203, 0x0100fffe, 0xfcfd0103, 0x0100fefd, - 0x02fffefc, 0x00010303, 0xfefcfbf9, 0xfeff0000, - 0xfcfbfbfa, 0xfdfefefe, 0xffff00ff, 0xff000000, - 0x02020302, 0x00010202, 0x03040503, 0x00010001, - 0x02030403, 0xffffff00, 0x01030505, 0xffffff00, - 0xff000101, 0xfcfafbfd, 0x00000101, 0xfffdfd00, - 0x00ffff00, 0x03010001, 0xfffdfefe, 0x07040202, - 0x00fefefe, 0x06030202, 0x010000ff, 0x03000001, - 0x03030300, 0xfffdfe01, 0x03040301, 0xfcfbfd00, - 0xff000200, 0xfefefcfd, 0x00020401, 0xfffefdfe, - 0x01030603, 0xff00fefe, 0x01040704, 0x00fffdfe, - 0x00030603, 0x00fffdfd, 0xff000301, 0x0101fefd, - 0xfeff0100, 0x0202fffd, 0xfdfdfffe, 0x0201fffd, - 0x00ff0103, 0x00000001, 0xfffeff02, 0xffff0001, - 0x00feff01, 0xfdfe0001, 0x01fffe00, 0xfbfd0103, - 0x02fffe00, 0xfbfd0205, 0x04fffe00, 0xfbfe0306, - 0x0400fe00, 0xfcfe0407, 0x0400fe00, 0xfcfe0406, - 0xfcfdfefe, 0x00fffefd, 0xff000101, 0x0100ffff, - 0x02020303, 0x01010101, 0x02020202, 0x01000001, - 0x00000000, 0xffffffff, 0xfcfcfcfc, 0xfdfcfcfc, - 0xfdfefdfd, 0x00fffffe, 0x05040403, 0x07060605, - 0xfe07feff, 0x00fffffc, 0xff09feff, 0x00fffffc, - 0xff0afdff, 0x01fffffc, 0xfe0afdff, 0x02fffffd, - 0xfe0afeff, 0x02fffffc, 0xfe09feff, 0x02fffffc, - 0xfe08feff, 0x01ff00fc, 0xfe07fe00, 0x0200fffd, - 0x0301fc03, 0x0001fefd, 0x0401fb03, 0x0001fefd, - 0x0502fa03, 0x0003fffd, 0x0502fa03, 0x0002fffd, - 0x0501fa03, 0x0003fefc, 0x0501fa03, 0x0002fefd, - 0x0401fa02, 0x0001fffd, 0x0401fa02, 0x0001fffe, - 0x01010000, 0x02000001, 0x0101ff00, 0x02000001, - 0x0000ff00, 0x02000000, 0x0000ff00, 0x00ff0000, - 0x00010001, 0xfffeff00, 0x00010103, 0xfdfcfe00, - 0x01020305, 0xfcfbfd00, 0x00020405, 0xfbfbfcff, - 0xfeff0001, 0xf7fafdfe, 0xffff0002, 0xfafd0000, - 0xff000001, 0xfbfe0000, 0x01010102, 0xfdff0201, - 0x01020101, 0xff010202, 0x01020101, 0x01010101, - 0x01020000, 0x02020000, 0x02020100, 0x02020000, - 0x0100fdfc, 0x03040604, 0x0000fefd, 0x00010402, - 0x0000ffff, 0xfdfe0101, 0x00010101, 0xfbfd0101, - 0x00010101, 0xfbfd0101, 0xff0000ff, 0xfcfe0101, - 0xff0000ff, 0xfe000201, 0x000000ff, 0x00010302, - 0x040300ff, 0xfbfbfc00, 0x05040000, 0xfefdfe02, - 0x0402ff00, 0x00ffff02, 0x01fffe00, 0x0100fe00, - 0x00fefe01, 0x01ffff00, 0x00fdfe01, 0x00ff0001, - 0x01fefe01, 0x00000103, 0x02fffe01, 0x00000204, - 0x02030201, 0x01020200, 0x000100ff, 0x010101fd, - 0xfe0000ff, 0x010200fc, 0xff0202ff, 0x010200fb, - 0xff0403ff, 0x010200fb, 0x000402fe, 0x0000fffc, - 0x000200fc, 0x0000fefc, 0x0102fffb, 0x020301fe, - 0x00010001, 0xfeff0201, 0xfeff0002, 0xff000301, - 0xfcfe0003, 0x00010300, 0xfbfd0105, 0x010102fe, - 0xfbfe0106, 0x010001fe, 0xfbff0105, 0x00ff00fe, - 0xfcfe0003, 0x00ff00fe, 0xfe00ff01, 0x01000100, - 0x03020101, 0x02010102, 0x0100ffff, 0x01010001, - 0x0000fdfc, 0x02010101, 0x0200fdfc, 0x02030202, - 0x0100fcfb, 0x02010101, 0xfffffcfb, 0x00fffefe, - 0x0000fefd, 0xfffefdfe, 0x04040302, 0x00000002, - 0x0100fefc, 0x00000000, 0x0101fffd, 0x00000000, - 0x020200fe, 0x02000000, 0x010201ff, 0x050300ff, - 0xff010200, 0x060500fe, 0xfd000100, 0x060400fd, - 0xfcfe0101, 0x0201fdfc, 0xfcfe0001, 0x00fefcfb, - 0xfdfdfdff, 0xfffffefd, 0x00010203, 0x01010100, - 0x02030405, 0x02020101, 0xfe000102, 0xfffffefe, - 0xff000000, 0xfefefefe, 0x03030100, 0xffff0102, - 0x04030100, 0xff010203, 0x01fffdfc, 0xfdfdfe00, - 0xf8f9fcfd, 0x02fffcf9, 0xfcfdff00, 0x0200fefc, - 0xff000001, 0x0200fefd, 0x00010102, 0x0200ffff, - 0x01010101, 0x01000000, 0x01010101, 0x01010101, - 0x02010000, 0x02020303, 0x03010000, 0x02030404, - 0x00030303, 0x020100ff, 0xff010101, 0x01fffffe, - 0xfdfffefe, 0x00fefefd, 0xfefefcfc, 0x00fdfefe, - 0x01fffcfc, 0x02ff0001, 0x0302fffd, 0x05030304, - 0x030200fe, 0x03030303, 0x0000fefe, 0x01000000, - 0xff010200, 0xfe03fffd, 0xffff00ff, 0xfe0400fd, - 0xfefefefe, 0xfe0501fe, 0xfffdfefd, 0xfd0401fe, - 0x00ff00fe, 0xfb0300ff, 0x02010201, 0xfb020000, - 0x03020402, 0xfd030101, 0x01010201, 0xfe040100, - 0xfffcfd04, 0x03010303, 0xfffcfc04, 0x02000203, - 0x00fcfd04, 0x01ff0202, 0x01fefd04, 0x00fe0102, - 0x01fefc02, 0x00fd0002, 0x00fefd02, 0x02fe0001, - 0x00ffff03, 0x03000000, 0xfefefe02, 0x02ffffff, - 0x04030202, 0xff000103, 0x02010001, 0xfefeff01, - 0x01020102, 0xffff0001, 0x03040304, 0x01010102, - 0x02020203, 0x01010101, 0x00fffeff, 0xffffffff, - 0xfffefdfd, 0xfefefefe, 0xfdfdfcfc, 0xfdfdfcfc, - 0xfdff0102, 0x0403fefc, 0xff010202, 0x0201fefd, - 0x03030201, 0xfefffe00, 0x040200ff, 0xfdff0002, - 0x0300fefe, 0xfd000203, 0xfffdfe00, 0xff020201, - 0xfbfcff03, 0x000200fd, 0xfafe0306, 0x010300fb, - 0x00fe03fe, 0x01fefe03, 0x00fd04fd, 0x02fffe03, - 0x00fd05fd, 0x02fffe04, 0xfffc04fe, 0x02fefd03, - 0x00fd04fd, 0x02fffd03, 0x00fe05fe, 0x02fffd03, - 0x01fd04fe, 0x02fffe03, 0x01fe03fe, 0x0200fe03, - 0xff000001, 0xfffc0201, 0xff000002, 0xfefc0201, - 0xff010101, 0x00fe0402, 0xff01ff00, 0x01ff0502, - 0xfe00ff00, 0x01ff0501, 0xfeffff00, 0xfffd0300, - 0xfe000101, 0xfffd0300, 0xfd000101, 0x00fe0300, - 0x01ff0001, 0x05040201, 0x01ff0001, 0x07050101, - 0xfffe0000, 0x050300ff, 0xfffeff00, 0x0302ffff, - 0xfffdff00, 0x0201ffff, 0xfefcfeff, 0x0000fefe, - 0xfffefeff, 0x0000fefe, 0x00ffff00, 0x0000ffff, - 0xff000303, 0x040401ff, 0xfe000302, 0x010100fe, - 0xff010302, 0x000100ff, 0xff000201, 0xfe00ffff, - 0xff000100, 0xfe00fffe, 0xff000100, 0x0001fffe, - 0xfdff0101, 0x0301fdfc, 0xfdff0201, 0x0301fcfb, - 0x0100fefd, 0xfe000101, 0x01010100, 0xfdff0000, - 0x01010201, 0xfeffff00, 0xfffdff00, 0xff00ffff, - 0xfdfafd00, 0x0101fffe, 0xfdfcff02, 0x020200fe, - 0x01010405, 0x02030100, 0x01020405, 0x0100ff00, - 0xfbfe00fe, 0x0000fdfa, 0x000100fe, 0x020201ff, - 0x030100fe, 0x01020202, 0x040200fe, 0x01010203, - 0x030200fe, 0x00ff0002, 0x0101fffd, 0x01ffff00, - 0x0001fffc, 0x0200feff, 0xff00fffc, 0x0401feff, - 0xff0000fd, 0x00010101, 0xff0001fd, 0xffff0000, - 0x000303ff, 0x01000001, 0xfe0202fd, 0x010000ff, - 0xfe0000fb, 0x020001ff, 0x0001fef9, 0x02020201, - 0x020300fb, 0x02020303, 0x010402fd, 0xfdfe0000, - 0xfefe0205, 0xffffff00, 0xfdfcff02, 0xfffffeff, - 0x01fefe00, 0x0100ff02, 0x03fffeff, 0x0200ff03, - 0x03000001, 0x02fffe03, 0x03010102, 0x00fefe02, - 0x01ff0001, 0xfefdfd01, 0x02010001, 0x00000003, - 0x00fdfbfc, 0xfffeff01, 0x01fffdfe, 0x00000002, - 0x01020101, 0x01010102, 0x01030403, 0xfffffe00, - 0x00020303, 0xfefdfdfe, 0xff000101, 0xfefefcfe, - 0x00000102, 0x0100ff00, 0x01010102, 0x03010101, - 0xff000000, 0x0001fffe, 0xfefffffe, 0x0000fefd, - 0xff0000ff, 0x010100fe, 0xff000101, 0x010301ff, - 0xfe000202, 0x000302ff, 0xffff0103, 0xfe020401, - 0xfffd0002, 0xfb000503, 0x00feff01, 0xfaff0303, - 0x040300ff, 0x02010002, 0x0100fffe, 0x0100feff, - 0xfdfefdfe, 0xfdfaf9fa, 0x01030202, 0xfefdfeff, - 0x01030202, 0x00000000, 0x00010102, 0x01000101, - 0x00000001, 0x02010100, 0x00ff0001, 0x01020200, - 0x01030101, 0x01ffffff, 0x0000fffe, 0x02fffefe, - 0x0101fefe, 0x03010001, 0xff00fefe, 0x0200ff00, - 0x00010000, 0x01feffff, 0x01020203, 0x01fefe00, - 0x02030305, 0x04010101, 0xfbfcfd00, 0x01fffdfc, - 0x02fffcfa, 0xff000002, 0x0301fefc, 0x00020203, - 0x02fffefd, 0x00020303, 0x01fefefd, 0xff010102, - 0x00fefefe, 0xff010202, 0x01ffffff, 0x00020302, - 0x01feffff, 0xff020202, 0x00feff00, 0xff000102, - 0x01020406, 0x01000000, 0xfeff0204, 0xfffffefe, - 0xfeff0102, 0xfffefefe, 0xfe000202, 0x00fffefe, - 0xfeff0000, 0x0100fffe, 0xfffefdfd, 0x00fffeff, - 0x0302fefd, 0xfeff0002, 0x050400fe, 0xff000205, - 0x00020405, 0xfffffeff, 0x01020304, 0xff00ff00, - 0x01000101, 0xff010001, 0x00fffffe, 0xfdfefe00, - 0x000000ff, 0xfbfdfdff, 0xff010100, 0xfdfefeff, - 0xfeffffff, 0x010301ff, 0xfffefeff, 0x05060502, - 0x01fefdfd, 0xfffffe01, 0x04030201, 0xfdfffd01, - 0x01000203, 0xfdfffdff, 0x00ff0001, 0x0001ffff, - 0x01000101, 0x03050202, 0x02010101, 0x00030202, - 0xfefefffd, 0xfdfffdfd, 0x000101ff, 0xfe00ffff, - 0xfefe0002, 0xfe010402, 0xfffe0001, 0xff020503, - 0xfefdfeff, 0xfe010301, 0xfffffeff, 0xff010200, - 0x01010000, 0x00020201, 0x04040100, 0x01030202, - 0x0102fffe, 0xfdfefdfe, 0x000100ff, 0xfbfcfcfd, - 0xfcfd0004, 0xfffefcfc, 0x00ff0005, 0xfffefdff, - 0x01000004, 0x00000001, 0xfffefd00, 0x00010000, - 0x0000fe00, 0x01020101, 0x00000002, 0x00010101, - 0xffff0002, 0x00010101, 0xfefeff01, 0x02020200, - 0x00fefbfd, 0x00fdfdff, 0x0200fe00, 0x03000002, - 0x00feff02, 0x02ffff00, 0xffff0205, 0x02ffffff, - 0xff000205, 0x02ff00ff, 0x00000102, 0x02000100, - 0x0101ffff, 0x02010202, 0x0000fefd, 0xfffe0000, - 0x00020300, 0xfdfdfdfe, 0x01030300, 0x02010000, - 0xfeff00ff, 0x0301ffff, 0xfeff00ff, 0x0200ffff, - 0xfeff00ff, 0x02ff0000, 0xfeff00ff, 0x01feffff, - 0xfd000100, 0x02ffffff, 0xff020505, 0x0301ffff, - 0xff010000, 0x0200fefd, 0x00030101, 0x0100fefe, - 0x01030101, 0xffff0000, 0x0102ff00, 0xfdff0001, - 0x0101feff, 0xfcfe0001, 0x010200ff, 0xfdff0001, - 0x02030101, 0xfdfe0001, 0x02040202, 0xfcfeff01, - 0x02020201, 0x0200fe00, 0xfdfeffff, 0x01fdfbfc, - 0x00010100, 0x01ffffff, 0x01010100, 0x02000000, - 0x02010100, 0x02010101, 0x0200ffff, 0x03020202, - 0xfffcfcfe, 0x00fefefe, 0x01000001, 0x01000000, - 0xfefdff00, 0x01020200, 0xfdfeff00, 0x02010100, - 0xfdfe0001, 0x010000ff, 0xfeff00ff, 0x00ff0000, - 0x000101ff, 0x00000202, 0x01030200, 0x02030503, - 0xfe0101ff, 0x01010300, 0xfc0000ff, 0xfffffffc, - 0x000101ff, 0x02010201, 0x000100fd, 0x02000101, - 0xfffffdfb, 0x01000100, 0xfdfefdfc, 0x00fffffe, - 0xfeff0000, 0x00fefefe, 0x00020403, 0x01000000, - 0x00000102, 0x00ff0000, 0x03020100, 0x02030404, - 0x040704ff, 0x00000000, 0x030604ff, 0x01010100, - 0x00040300, 0x010000ff, 0xfe010100, 0xffff00ff, - 0xffff00ff, 0x000000ff, 0x00ffffff, 0x00000000, - 0x00fdfdff, 0xfffeff01, 0xfefcfcfd, 0xfffefeff, - 0x00010202, 0xfd000101, 0x0000fffe, 0xfd000101, - 0x0100fffe, 0xfe010102, 0x02020201, 0xff020303, - 0x00010201, 0xff020101, 0xfcfe0100, 0xff0100fe, - 0xfdff0101, 0xfdff00fe, 0xff000201, 0xfcff0100, - 0xfefeffff, 0x03040300, 0xfdff0101, 0x000000fe, - 0x02020202, 0xffff0102, 0x03010101, 0xfefe0003, - 0xffffff00, 0xfffffe00, 0xfdfcfdff, 0x0200fefe, - 0x0100ffff, 0x05030202, 0x00fffffe, 0x01000000, - 0x0002fdfe, 0xff010100, 0xfe01fcff, 0x000202ff, - 0xfe00fc01, 0xff0101fe, 0xff01fd02, 0xff0101ff, - 0x0103fe03, 0xff010100, 0x0102fd01, 0xff000100, - 0x0001fbff, 0x010100ff, 0x0303fd00, 0x03030201, - 0x01feff00, 0xff020505, 0xfffeff01, 0xfbfe0101, - 0xfeff0101, 0xfdffffff, 0xffff0101, 0x030402ff, - 0xffffffff, 0x030400ff, 0x0100ffff, 0xfffffdff, - 0x02000000, 0xff000002, 0x00fdfe00, 0x02030101, - 0x01020302, 0xfefe0000, 0x01000302, 0x02030301, - 0xfffd0000, 0x030202ff, 0x01fdfefe, 0x01010201, - 0x02fefffe, 0xfeff0101, 0x02000100, 0xfefe0000, - 0x02000100, 0xfefe0000, 0x00fefefd, 0xfdfefeff, - 0x03ff0100, 0xff0301ff, 0x03ff0100, 0xfd02ffff, - 0x03fe0101, 0xfd00fdff, 0x03fe0202, 0xfe01fe00, - 0x03fd0101, 0xfd01feff, 0x03fd0101, 0xfe01ff00, - 0x04ff0201, 0xfe01ff00, 0x03ff0100, 0xfd00fdff, - 0x01fffdfd, 0xfeff0102, 0x0200fefe, 0xfefe0001, - 0x0201fefd, 0xfffeff01, 0x0402fefd, 0x01fefe00, - 0x0402fffd, 0x02fefe00, 0x030401ff, 0x02fefdff, - 0x02040200, 0x02fffeff, 0x00020100, 0x030100ff, - 0x01fb0003, 0x01000004, 0x02fbfe01, 0x01feff05, - 0x030000ff, 0xff000103, 0xfe0403fe, 0xfe00fffd, - 0xfd0503fd, 0xfe0000fd, 0xfe0203ff, 0xff0202fe, - 0xff000002, 0x00000000, 0x01fefd00, 0xfefe0003 +static const int8 s_svq1InterCodebook8x8[6144] = { + -4, -3, 4, 5, 2, 1, 1, 0, -5, -3, 5, 5, 2, 1, 0, 0, + -6, -4, 5, 5, 2, 1, 0, 0, -7, -4, 4, 5, 2, 1, 0, 0, + -8, -5, 3, 4, 2, 1, 0, 0, -8, -6, 3, 4, 1, 1, 1, 0, + -8, -6, 2, 4, 2, 1, 1, 0, -8, -6, 2, 4, 1, 1, 1, 1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -2, -2, -2, -2, -2, -2, + -2, -3, -3, -3, -3, -3, -3, -3, -2, -3, -3, -3, -3, -3, -4, -3, + -2, -2, -2, -2, -2, -3, -3, -2, 1, 1, 1, 1, 1, 0, -1, -1, + 4, 5, 5, 5, 4, 3, 3, 2, 7, 7, 8, 8, 8, 7, 6, 5, + 2, 1, 2, 4, 4, 0, -4, -6, 1, 1, 2, 5, 5, 1, -5, -7, + 1, 2, 1, 4, 5, 1, -5, -8, 1, 1, 1, 5, 5, 0, -6, -8, + 0, 1, 1, 5, 6, 1, -6, -9, 0, 0, 1, 4, 5, 0, -5, -8, + 0, 0, 1, 4, 5, 0, -5, -7, 0, 0, 1, 4, 4, 1, -4, -7, + 1, 2, 3, 0, -3, -4, -3, -1, 1, 3, 4, 0, -3, -4, -3, -1, + 2, 4, 5, 1, -3, -4, -3, -2, 2, 5, 6, 1, -3, -5, -4, -2, + 3, 6, 6, 1, -3, -5, -4, -2, 3, 6, 6, 1, -3, -5, -4, -2, + 3, 6, 6, 1, -3, -5, -4, -2, 3, 5, 5, 1, -3, -4, -4, -2, + 2, 2, 2, 2, 1, 0, 0, -1, 4, 4, 4, 3, 2, 1, 1, 0, + 4, 5, 4, 4, 3, 3, 2, 1, 4, 4, 4, 4, 4, 3, 2, 2, + 2, 3, 3, 3, 3, 3, 2, 1, -1, -1, -1, -1, 0, 0, 0, 0, + -5, -6, -6, -5, -5, -4, -3, -3, -7, -9, -9, -8, -7, -6, -6, -5, + 6, 6, 6, 6, 6, 5, 5, 4, 4, 4, 4, 3, 3, 3, 3, 2, + 0, -1, -1, -1, -2, -2, -1, -1, -3, -5, -6, -6, -6, -6, -5, -4, + -3, -5, -6, -7, -6, -6, -5, -4, -1, -2, -2, -2, -2, -2, -1, -1, + 0, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 1, -2, -5, -4, 0, 2, 5, 2, 1, -2, -6, -5, 0, 3, 5, + 2, 1, -2, -6, -6, -1, 3, 6, 3, 2, -2, -7, -6, 0, 4, 7, + 2, 1, -2, -7, -5, 0, 5, 7, 2, 1, -2, -6, -5, 0, 4, 7, + 2, 1, -2, -6, -4, 0, 4, 6, 1, 1, -2, -5, -4, 0, 3, 6, + -10, -9, -6, -4, -1, 2, 3, 2,-10, -9, -5, -3, 0, 4, 4, 3, + -9, -7, -3, -1, 2, 5, 5, 3, -7, -5, -2, 0, 3, 5, 5, 3, + -6, -3, 0, 1, 4, 6, 5, 3, -4, -2, 1, 2, 3, 5, 4, 2, + -2, 0, 1, 2, 2, 4, 3, 1, -1, 1, 2, 2, 2, 3, 3, 1, + -4, -5, -5, -6, -6, -6, -6, -5, -3, -3, -4, -4, -4, -4, -4, -4, + 0, 0, 0, 0, -1, -1, -1, -1, 5, 5, 6, 5, 5, 4, 3, 2, + 5, 6, 7, 7, 7, 6, 5, 4, 3, 3, 4, 4, 4, 4, 3, 2, + 0, -1, 0, 0, -1, -1, 0, -1, -3, -3, -4, -4, -4, -4, -3, -3, + 1, -2, -5, 1, 5, 4, 2, 0, 1, -3, -6, 1, 6, 5, 2, 0, + 0, -4, -7, 0, 6, 6, 2, 1, -1, -5, -9, -1, 6, 6, 3, 1, + -1, -6,-10, -2, 6, 6, 3, 1, -1, -6, -9, -2, 5, 6, 3, 1, + -2, -6, -9, -2, 5, 5, 3, 1, -2, -6, -7, -2, 4, 4, 2, 1, + -5, -7, -8, -9, -9, -8, -7, -6, -5, -6, -6, -7, -7, -6, -6, -5, + -3, -3, -3, -4, -5, -5, -4, -4, -1, 0, 0, -1, -1, -1, -1, -1, + 0, 1, 2, 2, 2, 2, 2, 1, 2, 3, 4, 5, 5, 5, 5, 4, + 3, 4, 5, 6, 8, 8, 8, 7, 3, 4, 5, 6, 7, 7, 7, 6, + 5, 6, 7, 8, 9, 10, 10, 9, 3, 4, 6, 7, 8, 9, 9, 8, + 0, 1, 2, 3, 4, 5, 5, 5, -1, -2, -1, -1, 0, 1, 2, 2, + -2, -3, -3, -3, -3, -2, -1, 0, -3, -4, -5, -5, -5, -5, -5, -4, + -4, -5, -5, -6, -7, -7, -6, -5, -3, -4, -5, -6, -7, -7, -6, -6, + 13, 7, 0, -3, -3, -4, -4, -5, 14, 7, 0, -3, -3, -4, -4, -4, + 15, 8, -1, -4, -4, -4, -5, -4, 15, 8, -1, -4, -4, -5, -4, -3, + 15, 7, -1, -4, -5, -5, -5, -4, 14, 7, -1, -4, -4, -4, -4, -3, + 12, 6, -1, -4, -4, -4, -4, -3, 11, 5, -1, -4, -4, -4, -4, -3, + -17, -4, 5, 4, 4, 4, 3, 3,-18, -5, 5, 4, 4, 4, 3, 3, + -19, -5, 6, 4, 4, 4, 3, 2,-20, -5, 6, 4, 4, 4, 3, 3, + -20, -4, 6, 4, 4, 5, 3, 3,-19, -5, 6, 4, 4, 5, 3, 3, + -18, -4, 5, 4, 4, 4, 3, 2,-17, -5, 4, 3, 4, 4, 3, 3, + -6, -6, -6, -4, -2, 1, 6, 11, -6, -7, -7, -4, -2, 2, 8, 13, + -8, -8, -7, -4, -2, 3, 9, 14, -8, -8, -7, -5, -1, 4, 10, 16, + -8, -8, -7, -5, -1, 4, 10, 17, -8, -8, -7, -4, 0, 5, 10, 16, + -8, -8, -6, -3, 0, 4, 9, 15, -7, -7, -5, -3, 0, 4, 8, 12, + 8, 7, 7, 5, 2, -2, -8,-14, 8, 8, 7, 5, 2, -2, -8,-15, + 8, 8, 7, 5, 1, -3, -9,-16, 8, 8, 7, 5, 1, -3,-10,-17, + 8, 9, 8, 5, 1, -3,-10,-17, 8, 8, 7, 4, 1, -4,-10,-16, + 7, 7, 7, 4, 1, -3, -9,-14, 6, 7, 6, 3, 0, -3, -9,-13, + 5, 1, -4, -4, -3, -1, 0, 0, 7, 2, -3, -3, -2, -1, 1, 0, + 7, 1, -3, -3, -1, 0, 1, 1, 6, 1, -3, -2, -1, 1, 1, 0, + 6, 0, -4, -2, -1, 0, 1, 0, 5, 0, -4, -3, -1, 0, 0, -1, + 5, 0, -3, -1, 0, 0, 0, -2, 4, 1, -2, -1, 0, 1, 0, -1, + 2, 2, 1, 1, -2, -6, -8, -8, 1, 1, 1, 1, -2, -5, -8, -8, + 1, 1, 1, 0, -1, -3, -5, -5, 0, 0, 0, 0, -1, -1, -1, -2, + 0, -1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 2, 3, 2, + 2, 1, 1, 1, 2, 3, 4, 3, 3, 3, 3, 3, 4, 4, 5, 4, + -4, -4, -3, -2, 0, 0, 1, 1, -4, -4, -3, -2, -1, 0, 0, 1, + -2, -2, -2, -1, -1, -1, 0, 0, 0, 1, 0, 0, 0, 0, 0, -1, + 2, 2, 2, 2, 2, 2, 1, 1, 3, 4, 4, 4, 4, 4, 4, 3, + 1, 1, 1, 3, 3, 4, 3, 3, -5, -6, -5, -4, -3, -3, -2, -2, + -4, -2, -1, -1, -1, -1, 0, 1, -4, -2, -1, -1, -1, -1, 0, 1, + -3, -2, -1, -1, -1, 0, 1, 2, -4, -3, -2, -1, -1, 1, 3, 3, + -4, -3, -3, -1, -1, 1, 4, 5, -4, -3, -2, -2, -1, 1, 4, 7, + -2, -2, -1, -1, 0, 2, 6, 8, -1, 0, 0, 1, 1, 4, 7, 8, + -3, -3, -3, -2, -2, -1, -1, 0, -1, -1, 0, 1, 2, 2, 3, 3, + 0, 1, 2, 4, 5, 6, 6, 5, -1, 0, 2, 3, 5, 6, 5, 3, + -1, -1, 0, 2, 3, 3, 2, 1, -2, -2, -1, 0, -1, -3, -4, -4, + 0, 0, -1, -1, -2, -4, -8, -7, 1, 2, 1, 0, -1, -4, -6, -7, + -2, 4, 1, -6, 0, 3, 0, 0, -2, 5, 1, -7, 0, 3, 0, 0, + -3, 5, 1, -8, 0, 3, -1, -1, -2, 6, 1, -9, 0, 3, 0, -1, + -2, 6, 2, -8, 0, 4, 0, -1, -3, 5, 1, -7, 1, 4, 0, 0, + -2, 4, 1, -7, 0, 4, 1, 0, -1, 4, 1, -6, 0, 3, 1, 0, + 0, 0, 0, 3, 4, 5, 4, 1, 1, 1, 1, 2, 3, 3, 2, 0, + 2, 2, 1, 2, 2, 1, -1, -2, 4, 3, 1, 1, 0, -1, -3, -5, + 5, 3, 1, -1, -2, -3, -4, -6, 5, 3, 0, -2, -3, -5, -6, -7, + 4, 3, 0, -2, -3, -4, -5, -5, 4, 3, 0, -1, -2, -2, -3, -3, + 0, 0, 0, 0, -1, -5, -2, 6, 0, 0, 0, 1, -1, -6, -2, 8, + 0, 0, 0, 2, 0, -6, -3, 9, 0, -1, 0, 2, 0, -7, -2, 10, + 0, -1, 0, 2, -1, -8, -3, 10, 0, -1, -1, 2, -1, -7, -3, 9, + 0, -1, 0, 1, -1, -6, -3, 8, 0, 0, 0, 1, 0, -5, -2, 7, + 2, 3, 3, 2, 1, 0, -1, -1, 3, 4, 3, 2, 1, 0, -1, -2, + 3, 4, 4, 2, 1, -1, -2, -3, 2, 3, 3, 2, 0, -1, -2, -3, + -1, 0, 1, 1, 0, -1, -2, -2, -5, -4, -3, -1, 0, 1, 1, 1, + -8, -8, -5, -1, 1, 3, 4, 3,-10, -9, -5, 0, 3, 5, 6, 5, + -5, -1, 4, 5, 3, 1, 0, 0, -6, -1, 4, 5, 2, 0, -1, -2, + -6, -1, 5, 4, 2, -1, -2, -2, -7, -1, 4, 4, 1, -2, -3, -3, + -6, -1, 5, 4, 1, -2, -3, -3, -5, 0, 4, 4, 1, -1, -2, -2, + -4, 0, 5, 4, 1, -1, -1, -2, -3, 1, 4, 3, 1, -1, -1, -2, + -2, -3, -2, 1, 4, 6, 5, 3, -3, -4, -4, 0, 3, 5, 4, 2, + -3, -5, -5, -1, 2, 4, 3, 1, -4, -6, -4, -1, 2, 4, 2, -1, + -2, -4, -3, 1, 2, 4, 2, -1, -2, -4, -2, 1, 3, 3, 1, -2, + -2, -3, -2, 1, 3, 3, 1, -2, -2, -2, -1, 1, 3, 3, 0, -2, + -4, -4, -3, -2, -1, 2, 5, 7, -4, -4, -3, -3, -2, 1, 5, 7, + -2, -3, -2, -3, -3, -1, 3, 5, -1, -1, 0, -2, -3, -2, 2, 4, + 1, 1, 1, -1, -4, -3, 1, 3, 4, 3, 2, -1, -4, -3, -1, 1, + 6, 4, 3, 0, -3, -3, -2, 0, 6, 5, 3, 1, -2, -3, -2, -1, + 12, 11, 8, 4, 0, -2, -2, -1, 10, 9, 6, 2, -1, -2, -1, 0, + 4, 3, 2, 0, -1, -1, 0, 1, -1, -1, -1, -1, -2, 0, 1, 2, + -3, -5, -4, -2, -2, 0, 2, 3, -5, -5, -4, -2, -1, 0, 1, 2, + -5, -5, -4, -2, -1, 0, 1, 1, -4, -4, -3, -2, -2, -1, 0, 0, + 3, 3, 2, -1, -3, -4, -3, -2, 3, 2, 0, -2, -4, -4, -3, -2, + 2, 2, 1, -1, -3, -5, -4, -3, 3, 3, 3, 1, -2, -3, -3, -3, + 4, 4, 4, 3, 0, -2, -2, -2, 5, 5, 5, 3, 0, -1, -2, -2, + 5, 5, 4, 2, -1, -2, -3, -2, 3, 3, 3, 0, -2, -4, -4, -4, + -1, -1, 4, -2, -2, 6, 2, -5, -1, 0, 4, -2, -3, 6, 2, -6, + -1, 0, 4, -2, -3, 7, 3, -7, -1, -1, 4, -3, -4, 8, 3, -7, + 0, -1, 4, -3, -4, 7, 3, -6, -1, -1, 4, -3, -4, 7, 3, -6, + -1, -1, 3, -3, -4, 6, 3, -6, -1, 0, 3, -2, -3, 6, 3, -5, + 1, -2, -7, 2, 5, -2, -1, 1, 1, -2, -8, 3, 6, -3, -1, 2, + 2, -2, -9, 4, 7, -4, -2, 2, 3, -1, -9, 5, 7, -4, -1, 3, + 3, -1, -9, 4, 7, -4, -2, 2, 3, -1, -7, 4, 6, -4, -2, 1, + 2, 0, -6, 4, 6, -4, -1, 1, 2, 0, -5, 3, 4, -3, -1, 1, + -2, 2, 2, 0, 0, -1, -3, -4, -2, 2, 2, 1, 1, 0, -2, -4, + -2, 2, 2, 2, 2, 1, -1, -2, -3, 2, 3, 3, 4, 2, 0, -2, + -3, 2, 3, 2, 4, 2, 0, -3, -4, 1, 2, 1, 2, 1, -1, -3, + -5, 0, 1, 0, 1, 1, -2, -3, -4, 0, 0, 0, 1, 0, -2, -3, + 0, 0, -1, -2, -2, 2, 7, 8, 0, 0, -1, -3, -2, 1, 6, 7, + 0, 1, -1, -3, -3, 0, 4, 5, 0, 1, 0, -1, -1, 0, 1, 3, + 0, 2, 1, 1, 0, -1, 0, 1, -2, 0, 1, 2, 1, 0, -1, -1, + -5, -2, 0, 1, 1, 0, -3, -3, -6, -4, -1, 1, 1, -1, -3, -4, + -4, -2, 2, 5, 6, 4, 3, 2, -5, -3, 1, 4, 4, 2, 0, 0, + -4, -2, 0, 2, 1, -1, -2, -2, -2, -1, 0, 1, 0, -2, -3, -2, + -2, 0, 0, 0, -1, -1, -2, -1, -2, -1, -1, 0, 0, 0, 1, 2, + -2, -2, -1, -1, 0, 1, 3, 4, -2, -3, -2, -1, 0, 2, 4, 5, + 2, 1, -2, -2, -1, 0, 1, 0, 1, 0, -3, -3, -1, 0, 1, 0, + 0, -1, -3, -3, -1, 1, 1, 1, 0, 0, -3, -1, 1, 2, 3, 3, + 0, -1, -3, -1, 1, 3, 3, 3, -2, -2, -4, -2, 1, 3, 4, 4, + -3, -3, -4, -2, 1, 3, 3, 4, -2, -3, -5, -2, 1, 2, 3, 3, + 4, 5, 3, 4, 4, 4, 4, 5, 3, 3, 1, 0, 0, 0, 0, 1, + 1, 1, -1, -2, -3, -4, -3, -2, 2, 2, 0, -2, -2, -4, -3, -2, + 2, 3, 1, -1, -1, -3, -3, -2, 1, 2, 0, 0, -1, -2, -2, -1, + 0, 1, 0, -1, -1, -3, -2, -1, 1, 1, 0, -1, -1, -2, -2, -2, + -2, -1, -1, 0, 1, 2, 1, 0, 1, 2, 3, 5, 6, 5, 5, 3, + 1, 2, 3, 4, 5, 5, 4, 3, -2, -2, -3, -3, -2, -1, 0, 0, + -3, -3, -4, -5, -4, -3, -2, -1, -1, -1, -2, -2, -2, -1, 0, 0, + 0, 1, 0, -1, -1, 0, 0, 1, -1, 0, -1, -2, -3, -2, -2, -1, + 7, 7, 6, 5, 4, 2, -1, -2, 3, 3, 2, 2, 1, 0, -2, -3, + 0, -1, -1, -1, 0, -1, -2, -2, -1, -3, -2, -1, 0, 0, 0, 1, + 0, -2, -2, -1, -1, 1, 2, 2, 3, 1, -1, -1, -1, 1, 2, 2, + 3, 1, -2, -3, -2, -1, 1, 2, 1, -2, -5, -6, -5, -3, -2, 0, + 0, -1, -2, -3, -1, 0, -2, -2, 0, 0, -1, -1, 0, 1, -1, -2, + 0, 0, -2, -1, 0, 0, 0, -2, -1, -2, -3, -3, -2, -1, -3, -3, + -1, -2, -3, -3, -2, -2, -3, -4, 2, 2, 0, 0, 0, 0, -1, -2, + 5, 5, 3, 2, 2, 2, 0, -1, 8, 8, 6, 5, 4, 4, 2, 1, + -7, -8, -6, -3, -1, -1, -2, -1, -5, -5, -3, 0, 2, 1, 0, 0, + -1, -1, 0, 3, 4, 3, 1, 1, 2, 1, 1, 3, 4, 3, 2, 2, + 3, 2, 0, 2, 3, 2, 1, 2, 4, 2, -1, -1, 0, 1, 1, 1, + 3, 2, -2, -3, -2, -1, 0, 1, 3, 1, -3, -4, -3, -2, 0, 1, + -4, -2, -1, 2, 3, 3, 1, 0, -7, -5, -4, -2, 0, 0, -1, -2, + -6, -5, -5, -4, -2, -2, -2, -3, -1, 0, -1, -1, 0, 0, 0, -1, + 2, 3, 2, 2, 2, 2, 1, 0, 3, 5, 4, 3, 1, 0, 1, 0, + 3, 4, 3, 2, 0, -1, -1, -1, 5, 5, 3, 1, 0, -1, -1, -1, + 1, 1, 0, -1, -3, -5, -6, -4, 1, 1, 0, 0, 0, -3, -3, -1, + 0, -1, -1, 0, 1, 0, 1, 3, -2, -2, -3, -1, 2, 2, 4, 7, + -2, -2, -2, 0, 2, 2, 3, 6, -1, 0, 0, 1, 1, 0, 0, 3, + 0, 3, 3, 3, 1, -2, -3, -1, 1, 3, 4, 3, 0, -3, -5, -4, + 0, 2, 0, -1, -3, -4, -2, -2, 1, 4, 2, 0, -2, -3, -2, -1, + 3, 6, 3, 1, -2, -2, 0, -1, 4, 7, 4, 1, -2, -3, -1, 0, + 3, 6, 3, 0, -3, -3, -1, 0, 1, 3, 0, -1, -3, -2, 1, 1, + 0, 1, -1, -2, -3, -1, 2, 2, -2, -1, -3, -3, -3, -1, 1, 2, + 3, 1, -1, 0, 1, 0, 0, 0, 2, -1, -2, -1, 1, 0, -1, -1, + 1, -1, -2, 0, 1, 0, -2, -3, 0, -2, -1, 1, 3, 1, -3, -5, + 0, -2, -1, 2, 5, 2, -3, -5, 0, -2, -1, 4, 6, 3, -2, -5, + 0, -2, 0, 4, 7, 4, -2, -4, 0, -2, 0, 4, 6, 4, -2, -4, + -2, -2, -3, -4, -3, -2, -1, 0, 1, 1, 0, -1, -1, -1, 0, 1, + 3, 3, 2, 2, 1, 1, 1, 1, 2, 2, 2, 2, 1, 0, 0, 1, + 0, 0, 0, 0, -1, -1, -1, -1, -4, -4, -4, -4, -4, -4, -4, -3, + -3, -3, -2, -3, -2, -1, -1, 0, 3, 4, 4, 5, 5, 6, 6, 7, + -1, -2, 7, -2, -4, -1, -1, 0, -1, -2, 9, -1, -4, -1, -1, 0, + -1, -3, 10, -1, -4, -1, -1, 1, -1, -3, 10, -2, -3, -1, -1, 2, + -1, -2, 10, -2, -4, -1, -1, 2, -1, -2, 9, -2, -4, -1, -1, 2, + -1, -2, 8, -2, -4, 0, -1, 1, 0, -2, 7, -2, -3, -1, 0, 2, + 3, -4, 1, 3, -3, -2, 1, 0, 3, -5, 1, 4, -3, -2, 1, 0, + 3, -6, 2, 5, -3, -1, 3, 0, 3, -6, 2, 5, -3, -1, 2, 0, + 3, -6, 1, 5, -4, -2, 3, 0, 3, -6, 1, 5, -3, -2, 2, 0, + 2, -6, 1, 4, -3, -1, 1, 0, 2, -6, 1, 4, -2, -1, 1, 0, + 0, 0, 1, 1, 1, 0, 0, 2, 0, -1, 1, 1, 1, 0, 0, 2, + 0, -1, 0, 0, 0, 0, 0, 2, 0, -1, 0, 0, 0, 0, -1, 0, + 1, 0, 1, 0, 0, -1, -2, -1, 3, 1, 1, 0, 0, -2, -4, -3, + 5, 3, 2, 1, 0, -3, -5, -4, 5, 4, 2, 0, -1, -4, -5, -5, + 1, 0, -1, -2, -2, -3, -6, -9, 2, 0, -1, -1, 0, 0, -3, -6, + 1, 0, 0, -1, 0, 0, -2, -5, 2, 1, 1, 1, 1, 2, -1, -3, + 1, 1, 2, 1, 2, 2, 1, -1, 1, 1, 2, 1, 1, 1, 1, 1, + 0, 0, 2, 1, 0, 0, 2, 2, 0, 1, 2, 2, 0, 0, 2, 2, + -4, -3, 0, 1, 4, 6, 4, 3, -3, -2, 0, 0, 2, 4, 1, 0, + -1, -1, 0, 0, 1, 1, -2, -3, 1, 1, 1, 0, 1, 1, -3, -5, + 1, 1, 1, 0, 1, 1, -3, -5, -1, 0, 0, -1, 1, 1, -2, -4, + -1, 0, 0, -1, 1, 2, 0, -2, -1, 0, 0, 0, 2, 3, 1, 0, + -1, 0, 3, 4, 0, -4, -5, -5, 0, 0, 4, 5, 2, -2, -3, -2, + 0, -1, 2, 4, 2, -1, -1, 0, 0, -2, -1, 1, 0, -2, 0, 1, + 1, -2, -2, 0, 0, -1, -1, 1, 1, -2, -3, 0, 1, 0, -1, 0, + 1, -2, -2, 1, 3, 1, 0, 0, 1, -2, -1, 2, 4, 2, 0, 0, + 1, 2, 3, 2, 0, 2, 2, 1, -1, 0, 1, 0, -3, 1, 1, 1, + -1, 0, 0, -2, -4, 0, 2, 1, -1, 2, 2, -1, -5, 0, 2, 1, + -1, 3, 4, -1, -5, 0, 2, 1, -2, 2, 4, 0, -4, -1, 0, 0, + -4, 0, 2, 0, -4, -2, 0, 0, -5, -1, 2, 1, -2, 1, 3, 2, + 1, 0, 1, 0, 1, 2, -1, -2, 2, 0, -1, -2, 1, 3, 0, -1, + 3, 0, -2, -4, 0, 3, 1, 0, 5, 1, -3, -5, -2, 2, 1, 1, + 6, 1, -2, -5, -2, 1, 0, 1, 5, 1, -1, -5, -2, 0, -1, 0, + 3, 0, -2, -4, -2, 0, -1, 0, 1, -1, 0, -2, 0, 1, 0, 1, + 1, 1, 2, 3, 2, 1, 1, 2, -1, -1, 0, 1, 1, 0, 1, 1, + -4, -3, 0, 0, 1, 1, 1, 2, -4, -3, 0, 2, 2, 2, 3, 2, + -5, -4, 0, 1, 1, 1, 1, 2, -5, -4, -1, -1, -2, -2, -1, 0, + -3, -2, 0, 0, -2, -3, -2, -1, 2, 3, 4, 4, 2, 0, 0, 0, + -4, -2, 0, 1, 0, 0, 0, 0, -3, -1, 1, 1, 0, 0, 0, 0, + -2, 0, 2, 2, 0, 0, 0, 2, -1, 1, 2, 1, -1, 0, 3, 5, + 0, 2, 1, -1, -2, 0, 5, 6, 0, 1, 0, -3, -3, 0, 4, 6, + 1, 1, -2, -4, -4, -3, 1, 2, 1, 0, -2, -4, -5, -4, -2, 0, + -1, -3, -3, -3, -3, -2, -1, -1, 3, 2, 1, 0, 0, 1, 1, 1, + 5, 4, 3, 2, 1, 1, 2, 2, 2, 1, 0, -2, -2, -2, -1, -1, + 0, 0, 0, -1, -2, -2, -2, -2, 0, 1, 3, 3, 2, 1, -1, -1, + 0, 1, 3, 4, 3, 2, 1, -1, -4, -3, -1, 1, 0, -2, -3, -3, + -3, -4, -7, -8, -7, -4, -1, 2, 0, -1, -3, -4, -4, -2, 0, 2, + 1, 0, 0, -1, -3, -2, 0, 2, 2, 1, 1, 0, -1, -1, 0, 2, + 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 1, 2, 3, 3, 2, 2, 0, 0, 1, 3, 4, 4, 3, 2, + 3, 3, 3, 0, -1, 0, 1, 2, 1, 1, 1, -1, -2, -1, -1, 1, + -2, -2, -1, -3, -3, -2, -2, 0, -4, -4, -2, -2, -2, -2, -3, 0, + -4, -4, -1, 1, 1, 0, -1, 2, -3, -1, 2, 3, 4, 3, 3, 5, + -2, 0, 2, 3, 3, 3, 3, 3, -2, -2, 0, 0, 0, 0, 0, 1, + 0, 2, 1, -1, -3, -1, 3, -2, -1, 0, -1, -1, -3, 0, 4, -2, + -2, -2, -2, -2, -2, 1, 5, -2, -3, -2, -3, -1, -2, 1, 4, -3, + -2, 0, -1, 0, -1, 0, 3, -5, 1, 2, 1, 2, 0, 0, 2, -5, + 2, 4, 2, 3, 1, 1, 3, -3, 1, 2, 1, 1, 0, 1, 4, -2, + 4, -3, -4, -1, 3, 3, 1, 3, 4, -4, -4, -1, 3, 2, 0, 2, + 4, -3, -4, 0, 2, 2, -1, 1, 4, -3, -2, 1, 2, 1, -2, 0, + 2, -4, -2, 1, 2, 0, -3, 0, 2, -3, -2, 0, 1, 0, -2, 2, + 3, -1, -1, 0, 0, 0, 0, 3, 2, -2, -2, -2, -1, -1, -1, 2, + 2, 2, 3, 4, 3, 1, 0, -1, 1, 0, 1, 2, 1, -1, -2, -2, + 2, 1, 2, 1, 1, 0, -1, -1, 4, 3, 4, 3, 2, 1, 1, 1, + 3, 2, 2, 2, 1, 1, 1, 1, -1, -2, -1, 0, -1, -1, -1, -1, + -3, -3, -2, -1, -2, -2, -2, -2, -4, -4, -3, -3, -4, -4, -3, -3, + 2, 1, -1, -3, -4, -2, 3, 4, 2, 2, 1, -1, -3, -2, 1, 2, + 1, 2, 3, 3, 0, -2, -1, -2, -1, 0, 2, 4, 2, 0, -1, -3, + -2, -2, 0, 3, 3, 2, 0, -3, 0, -2, -3, -1, 1, 2, 2, -1, + 3, -1, -4, -5, -3, 0, 2, 0, 6, 3, -2, -6, -5, 0, 3, 1, + -2, 3, -2, 0, 3, -2, -2, 1, -3, 4, -3, 0, 3, -2, -1, 2, + -3, 5, -3, 0, 4, -2, -1, 2, -2, 4, -4, -1, 3, -3, -2, 2, + -3, 4, -3, 0, 3, -3, -1, 2, -2, 5, -2, 0, 3, -3, -1, 2, + -2, 4, -3, 1, 3, -2, -1, 2, -2, 3, -2, 1, 3, -2, 0, 2, + 1, 0, 0, -1, 1, 2, -4, -1, 2, 0, 0, -1, 1, 2, -4, -2, + 1, 1, 1, -1, 2, 4, -2, 0, 0, -1, 1, -1, 2, 5, -1, 1, + 0, -1, 0, -2, 1, 5, -1, 1, 0, -1, -1, -2, 0, 3, -3, -1, + 1, 1, 0, -2, 0, 3, -3, -1, 1, 1, 0, -3, 0, 3, -2, 0, + 1, 0, -1, 1, 1, 2, 4, 5, 1, 0, -1, 1, 1, 1, 5, 7, + 0, 0, -2, -1, -1, 0, 3, 5, 0, -1, -2, -1, -1, -1, 2, 3, + 0, -1, -3, -1, -1, -1, 1, 2, -1, -2, -4, -2, -2, -2, 0, 0, + -1, -2, -2, -1, -2, -2, 0, 0, 0, -1, -1, 0, -1, -1, 0, 0, + 3, 3, 0, -1, -1, 1, 4, 4, 2, 3, 0, -2, -2, 0, 1, 1, + 2, 3, 1, -1, -1, 0, 1, 0, 1, 2, 0, -1, -1, -1, 0, -2, + 0, 1, 0, -1, -2, -1, 0, -2, 0, 1, 0, -1, -2, -1, 1, 0, + 1, 1, -1, -3, -4, -3, 1, 3, 1, 2, -1, -3, -5, -4, 1, 3, + -3, -2, 0, 1, 1, 1, 0, -2, 0, 1, 1, 1, 0, 0, -1, -3, + 1, 2, 1, 1, 0, -1, -1, -2, 0, -1, -3, -1, -1, -1, 0, -1, + 0, -3, -6, -3, -2, -1, 1, 1, 2, -1, -4, -3, -2, 0, 2, 2, + 5, 4, 1, 1, 0, 1, 3, 2, 5, 4, 2, 1, 0, -1, 0, 1, + -2, 0, -2, -5, -6, -3, 0, 0, -2, 0, 1, 0, -1, 1, 2, 2, + -2, 0, 1, 3, 2, 2, 2, 1, -2, 0, 2, 4, 3, 2, 1, 1, + -2, 0, 2, 3, 2, 0, -1, 0, -3, -1, 1, 1, 0, -1, -1, 1, + -4, -1, 1, 0, -1, -2, 0, 2, -4, -1, 0, -1, -1, -2, 1, 4, + -3, 0, 0, -1, 1, 1, 1, 0, -3, 1, 0, -1, 0, 0, -1, -1, + -1, 3, 3, 0, 1, 0, 0, 1, -3, 2, 2, -2, -1, 0, 0, 1, + -5, 0, 0, -2, -1, 1, 0, 2, -7, -2, 1, 0, 1, 2, 2, 2, + -5, 0, 3, 2, 3, 3, 2, 2, -3, 2, 4, 1, 0, 0, -2, -3, + 5, 2, -2, -2, 0, -1, -1, -1, 2, -1, -4, -3, -1, -2, -1, -1, + 0, -2, -2, 1, 2, -1, 0, 1, -1, -2, -1, 3, 3, -1, 0, 2, + 1, 0, 0, 3, 3, -2, -1, 2, 2, 1, 1, 3, 2, -2, -2, 0, + 1, 0, -1, 1, 1, -3, -3, -2, 1, 0, 1, 2, 3, 0, 0, 0, + -4, -5, -3, 0, 1, -1, -2, -1, -2, -3, -1, 1, 2, 0, 0, 0, + 1, 1, 2, 1, 2, 1, 1, 1, 3, 4, 3, 1, 0, -2, -1, -1, + 3, 3, 2, 0, -2, -3, -3, -2, 1, 1, 0, -1, -2, -4, -2, -2, + 2, 1, 0, 0, 0, -1, 0, 1, 2, 1, 1, 1, 1, 1, 1, 3, + 0, 0, 0, -1, -2, -1, 1, 0, -2, -1, -1, -2, -3, -2, 0, 0, + -1, 0, 0, -1, -2, 0, 1, 1, 1, 1, 0, -1, -1, 1, 3, 1, + 2, 2, 0, -2, -1, 2, 3, 0, 3, 1, -1, -1, 1, 4, 2, -2, + 2, 0, -3, -1, 3, 5, 0, -5, 1, -1, -2, 0, 3, 3, -1, -6, + -1, 0, 3, 4, 2, 0, 1, 2, -2, -1, 0, 1, -1, -2, 0, 1, + -2, -3, -2, -3, -6, -7, -6, -3, 2, 2, 3, 1, -1, -2, -3, -2, + 2, 2, 3, 1, 0, 0, 0, 0, 2, 1, 1, 0, 1, 1, 0, 1, + 1, 0, 0, 0, 0, 1, 1, 2, 1, 0, -1, 0, 0, 2, 2, 1, + 1, 1, 3, 1, -1, -1, -1, 1, -2, -1, 0, 0, -2, -2, -1, 2, + -2, -2, 1, 1, 1, 0, 1, 3, -2, -2, 0, -1, 0, -1, 0, 2, + 0, 0, 1, 0, -1, -1, -2, 1, 3, 2, 2, 1, 0, -2, -2, 1, + 5, 3, 3, 2, 1, 1, 1, 4, 0, -3, -4, -5, -4, -3, -1, 1, + -6, -4, -1, 2, 2, 0, 0, -1, -4, -2, 1, 3, 3, 2, 2, 0, + -3, -2, -1, 2, 3, 3, 2, 0, -3, -2, -2, 1, 2, 1, 1, -1, + -2, -2, -2, 0, 2, 2, 1, -1, -1, -1, -1, 1, 2, 3, 2, 0, + -1, -1, -2, 1, 2, 2, 2, -1, 0, -1, -2, 0, 2, 1, 0, -1, + 6, 4, 2, 1, 0, 0, 0, 1, 4, 2, -1, -2, -2, -2, -1, -1, + 2, 1, -1, -2, -2, -2, -2, -1, 2, 2, 0, -2, -2, -2, -1, 0, + 0, 0, -1, -2, -2, -1, 0, 1, -3, -3, -2, -1, -1, -2, -1, 0, + -3, -2, 2, 3, 2, 0, -1, -2, -2, 0, 4, 5, 5, 2, 0, -1, + 5, 4, 2, 0, -1, -2, -1, -1, 4, 3, 2, 1, 0, -1, 0, -1, + 1, 1, 0, 1, 1, 0, 1, -1, -2, -1, -1, 0, 0, -2, -2, -3, + -1, 0, 0, 0, -1, -3, -3, -5, 0, 1, 1, -1, -1, -2, -2, -3, + -1, -1, -1, -2, -1, 1, 3, 1, -1, -2, -2, -1, 2, 5, 6, 5, + -3, -3, -2, 1, 1, -2, -1, -1, 1, 2, 3, 4, 1, -3, -1, -3, + 3, 2, 0, 1, -1, -3, -1, -3, 1, 0, -1, 0, -1, -1, 1, 0, + 1, 1, 0, 1, 2, 2, 5, 3, 1, 1, 1, 2, 2, 2, 3, 0, + -3, -1, -2, -2, -3, -3, -1, -3, -1, 1, 1, 0, -1, -1, 0, -2, + 2, 0, -2, -2, 2, 4, 1, -2, 1, 0, -2, -1, 3, 5, 2, -1, + -1, -2, -3, -2, 1, 3, 1, -2, -1, -2, -1, -1, 0, 2, 1, -1, + 0, 0, 1, 1, 1, 2, 2, 0, 0, 1, 4, 4, 2, 2, 3, 1, + -2, -1, 2, 1, -2, -3, -2, -3, -1, 0, 1, 0, -3, -4, -4, -5, + 4, 0, -3, -4, -4, -4, -2, -1, 5, 0, -1, 0, -1, -3, -2, -1, + 4, 0, 0, 1, 1, 0, 0, 0, 0, -3, -2, -1, 0, 0, 1, 0, + 0, -2, 0, 0, 1, 1, 2, 1, 2, 0, 0, 0, 1, 1, 1, 0, + 2, 0, -1, -1, 1, 1, 1, 0, 1, -1, -2, -2, 0, 2, 2, 2, + -3, -5, -2, 0, -1, -3, -3, 0, 0, -2, 0, 2, 2, 0, 0, 3, + 2, -1, -2, 0, 0, -1, -1, 2, 5, 2, -1, -1, -1, -1, -1, 2, + 5, 2, 0, -1, -1, 0, -1, 2, 2, 1, 0, 0, 0, 1, 0, 2, + -1, -1, 1, 1, 2, 2, 1, 2, -3, -2, 0, 0, 0, 0, -2, -1, + 0, 3, 2, 0, -2, -3, -3, -3, 0, 3, 3, 1, 0, 0, 1, 2, + -1, 0, -1, -2, -1, -1, 1, 3, -1, 0, -1, -2, -1, -1, 0, 2, + -1, 0, -1, -2, 0, 0, -1, 2, -1, 0, -1, -2, -1, -1, -2, 1, + 0, 1, 0, -3, -1, -1, -1, 2, 5, 5, 2, -1, -1, -1, 1, 3, + 0, 0, 1, -1, -3, -2, 0, 2, 1, 1, 3, 0, -2, -2, 0, 1, + 1, 1, 3, 1, 0, 0, -1, -1, 0, -1, 2, 1, 1, 0, -1, -3, + -1, -2, 1, 1, 1, 0, -2, -4, -1, 0, 2, 1, 1, 0, -1, -3, + 1, 1, 3, 2, 1, 0, -2, -3, 2, 2, 4, 2, 1, -1, -2, -4, + 1, 2, 2, 2, 0, -2, 0, 2, -1, -1, -2, -3, -4, -5, -3, 1, + 0, 1, 1, 0, -1, -1, -1, 1, 0, 1, 1, 1, 0, 0, 0, 2, + 0, 1, 1, 2, 1, 1, 1, 2, -1, -1, 0, 2, 2, 2, 2, 3, + -2, -4, -4, -1, -2, -2, -2, 0, 1, 0, 0, 1, 0, 0, 0, 1, + 0, -1, -3, -2, 0, 2, 2, 1, 0, -1, -2, -3, 0, 1, 1, 2, + 1, 0, -2, -3, -1, 0, 0, 1, -1, 0, -1, -2, 0, 0, -1, 0, + -1, 1, 1, 0, 2, 2, 0, 0, 0, 2, 3, 1, 3, 5, 3, 2, + -1, 1, 1, -2, 0, 3, 1, 1, -1, 0, 0, -4, -4, -1, -1, -1, + -1, 1, 1, 0, 1, 2, 1, 2, -3, 0, 1, 0, 1, 1, 0, 2, + -5, -3, -1, -1, 0, 1, 0, 1, -4, -3, -2, -3, -2, -1, -1, 0, + 0, 0, -1, -2, -2, -2, -2, 0, 3, 4, 2, 0, 0, 0, 0, 1, + 2, 1, 0, 0, 0, 0, -1, 0, 0, 1, 2, 3, 4, 4, 3, 2, + -1, 4, 7, 4, 0, 0, 0, 0, -1, 4, 6, 3, 0, 1, 1, 1, + 0, 3, 4, 0, -1, 0, 0, 1, 0, 1, 1, -2, -1, 0, -1, -1, + -1, 0, -1, -1, -1, 0, 0, 0, -1, -1, -1, 0, 0, 0, 0, 0, + -1, -3, -3, 0, 1, -1, -2, -1, -3, -4, -4, -2, -1, -2, -2, -1, + 2, 2, 1, 0, 1, 1, 0, -3, -2, -1, 0, 0, 1, 1, 0, -3, + -2, -1, 0, 1, 2, 1, 1, -2, 1, 2, 2, 2, 3, 3, 2, -1, + 1, 2, 1, 0, 1, 1, 2, -1, 0, 1, -2, -4, -2, 0, 1, -1, + 1, 1, -1, -3, -2, 0, -1, -3, 1, 2, 0, -1, 0, 1, -1, -4, + -1, -1, -2, -2, 0, 3, 4, 3, 1, 1, -1, -3, -2, 0, 0, 0, + 2, 2, 2, 2, 2, 1, -1, -1, 1, 1, 1, 3, 3, 0, -2, -2, + 0, -1, -1, -1, 0, -2, -1, -1, -1, -3, -4, -3, -2, -2, 0, 2, + -1, -1, 0, 1, 2, 2, 3, 5, -2, -1, -1, 0, 0, 0, 0, 1, + -2, -3, 2, 0, 0, 1, 1, -1, -1, -4, 1, -2, -1, 2, 2, 0, + 1, -4, 0, -2, -2, 1, 1, -1, 2, -3, 1, -1, -1, 1, 1, -1, + 3, -2, 3, 1, 0, 1, 1, -1, 1, -3, 2, 1, 0, 1, 0, -1, + -1, -5, 1, 0, -1, 0, 1, 1, 0, -3, 3, 3, 1, 2, 3, 3, + 0, -1, -2, 1, 5, 5, 2, -1, 1, -1, -2, -1, 1, 1, -2, -5, + 1, 1, -1, -2, -1, -1, -1, -3, 1, 1, -1, -1, -1, 2, 4, 3, + -1, -1, -1, -1, -1, 0, 4, 3, -1, -1, 0, 1, -1, -3, -1, -1, + 0, 0, 0, 2, 2, 0, 0, -1, 0, -2, -3, 0, 1, 1, 3, 2, + 2, 3, 2, 1, 0, 0, -2, -2, 2, 3, 0, 1, 1, 3, 3, 2, + 0, 0, -3, -1, -1, 2, 2, 3, -2, -2, -3, 1, 1, 2, 1, 1, + -2, -1, -2, 2, 1, 1, -1, -2, 0, 1, 0, 2, 0, 0, -2, -2, + 0, 1, 0, 2, 0, 0, -2, -2, -3, -2, -2, 0, -1, -2, -2, -3, + 0, 1, -1, 3, -1, 1, 3, -1, 0, 1, -1, 3, -1, -1, 2, -3, + 1, 1, -2, 3, -1, -3, 0, -3, 2, 2, -2, 3, 0, -2, 1, -2, + 1, 1, -3, 3, -1, -2, 1, -3, 1, 1, -3, 3, 0, -1, 1, -2, + 1, 2, -1, 4, 0, -1, 1, -2, 0, 1, -1, 3, -1, -3, 0, -3, + -3, -3, -1, 1, 2, 1, -1, -2, -2, -2, 0, 2, 1, 0, -2, -2, + -3, -2, 1, 2, 1, -1, -2, -1, -3, -2, 2, 4, 0, -2, -2, 1, + -3, -1, 2, 4, 0, -2, -2, 2, -1, 1, 4, 3, -1, -3, -2, 2, + 0, 2, 4, 2, -1, -2, -1, 2, 0, 1, 2, 0, -1, 0, 1, 3, + 3, 0, -5, 1, 4, 0, 0, 1, 1, -2, -5, 2, 5, -1, -2, 1, + -1, 0, 0, 3, 3, 1, 0, -1, -2, 3, 4, -2, -3, -1, 0, -2, + -3, 3, 5, -3, -3, 0, 0, -2, -1, 3, 2, -2, -2, 2, 2, -1, + 2, 0, 0, -1, 0, 0, 0, 0, 0, -3, -2, 1, 3, 0, -2, -2 }; -const uint32 *const s_svq1InterCodebooks[6] = { +static const int8 *const s_svq1InterCodebooks[6] = { s_svq1InterCodebook4x2, s_svq1InterCodebook4x4, s_svq1InterCodebook8x4, s_svq1InterCodebook8x8, 0, 0 }; -static const uint32 s_svq1IntraCodebook4x2[192] = { - 0x0b0d0d0c, 0xeff1f6f9, 0xf6f4f1f0, 0x0c0f0f0b, - 0x0f141102, 0x0d02e8d3, 0xdcfa1415, 0xe5ff100c, - 0x2d0aebee, 0x15f9ecf5, 0x00e4f82b, 0x03e4f021, - 0xfaeeeef4, 0x371cf6ec, 0xeeebeefb, 0xecfa1e38, - 0xea1d1bde, 0xe71a1de2, 0x1a21221e, 0xdfdde1e7, - 0xe0dcdde1, 0x1f25241d, 0x2226f4b9, 0x212affc1, - 0xc4e1253a, 0xc3df2237, 0x5d16c7c7, 0x5d15c6c7, - 0x3e46453b, 0xc4bcbcc1, 0xc0b9b9c0, 0x3e48493f, - 0x0f0700fe, 0x05fdf6f5, 0xf6f6f8fb, 0x090e0901, - 0xf5fc080f, 0xf4f5020c, 0x1c1300f8, 0xe6f1ff04, - 0xf2021bf1, 0xf70116f2, 0xf6f3fafc, 0x2f06f2fa, - 0x1706ecdd, 0x04060906, 0xea1702fa, 0xeb1c04f9, - 0x06feea14, 0x08fbe416, 0xf4f0eef6, 0xff021324, - 0x080400fd, 0x1717f6d3, 0xddec0f28, 0x0104fffc, - 0x18dffb09, 0x13e60308, 0xfd0604ff, 0xcff31920, - 0x070f1818, 0xf9ede5ef, 0x182700d1, 0x0407faeb, - 0xf3f600ff, 0x10050101, 0xf7fd0514, 0xfafefcff, - 0x0401f9ef, 0x0000070c, 0x0b0c0003, 0xe90001fd, - 0x00fa1104, 0x00e70306, 0x05080aef, 0x040104f2, - 0x02040a0d, 0x0201f7e9, 0x0701fd03, 0x14f9e901, - 0x0c02eef9, 0x090afcfb, 0xe8070a04, 0xf6040306, - 0x06eaf216, 0xff050500, 0xfcf503fc, 0xf2071ff9, - 0x2afff0fb, 0xfbf7fefc, 0xfdfaf805, 0xfbebfc2a, - 0xf4140cee, 0x07f6f30d, 0xeefef7f8, 0x082806f0, - 0x0400ff0a, 0xf3ff04fd, 0xf10106fe, 0x02010305, - 0x0301fefc, 0xfcf7000f, 0xfcfcfcfd, 0xfd1005fd, - 0x04030d02, 0x00f6f8fd, 0xfffcfefa, 0x17fafdfe, - 0x0107fa06, 0x0105ee04, 0x0ef101ff, 0x04fc06fb, - 0x06020202, 0x030702e8, 0x030300e6, 0x06010705, - 0xfdeefe0e, 0x02fc0507, 0x012003fa, 0xf4fafafa, - 0x0607dc05, 0x000bff09, 0x03050404, 0xda030f04, - 0xd6fb170a, 0x04040400, 0xf3fa1117, 0x1d01dbf3, - 0x01fff205, 0x01030005, 0x02fb0400, 0xf6000008, - 0xfdfe0704, 0x010103f6, 0x030dfff4, 0xfd01ff00, - 0x0103ff00, 0x0903f7fa, 0xfafc01fa, 0x0800ff08, - 0x1200fdfd, 0xfffcfffb, 0xfc03fef8, 0xfbff1100, - 0xf609fe05, 0xfb06fb01, 0x03020204, 0x01f8f20a, - 0xffeefeff, 0x020114ff, 0x01f701ff, 0xfc16f7ff, - 0xfd08fc06, 0x05ed07ff, 0xfcfc1ff9, 0xfbfb00fa, - 0xfcedf8f9, 0x20040101, 0x04f8ff26, 0xf4faf8f9, - 0x01f900ff, 0x00ff09ff, 0x00ffff09, 0xfd01fa02, - 0x010200f4, 0x00080101, 0x02000109, 0x00f501fe, - 0xf6020800, 0x00ff02ff, 0xfb00fcfe, 0x0efffffe, - 0x05ff07fd, 0x0101f600, 0xff0efbff, 0xfefd01fe, - 0x060000fa, 0x04f70302, 0xfffffb04, 0xff0803f9, - 0xf5fffc02, 0x0001020b, 0x090302ff, 0xf1000200, - 0x03ec0503, 0x0303ff03, 0x0110ff01, 0x0209e302, - 0xfdfffaf3, 0xfdf8ff24, 0x02040502, 0x030b09db +static const int8 s_svq1IntraCodebook4x2[768] = { + 12, 13, 13, 11, -7,-10,-15,-17,-16,-15,-12,-10, 11, 15, 15, 12, + 2, 17, 20, 15,-45,-24, 2, 13, 21, 20, -6,-36, 12, 16, -1,-27, + -18,-21, 10, 45,-11,-20, -7, 21, 43, -8,-28, 0, 33,-16,-28, 3, + -12,-18,-18, -6,-20,-10, 28, 55, -5,-18,-21,-18, 56, 30, -6,-20, + -34, 27, 29,-22,-30, 29, 26,-25, 30, 34, 33, 26,-25,-31,-35,-33, + -31,-35,-36,-32, 29, 36, 37, 31,-71,-12, 38, 34,-63, -1, 42, 33, + 58, 37,-31,-60, 55, 34,-33,-61,-57,-57, 22, 93,-57,-58, 21, 93, + 59, 69, 70, 62,-63,-68,-68,-60,-64,-71,-71,-64, 63, 73, 72, 62, + -2, 0, 7, 15,-11,-10, -3, 5, -5, -8,-10,-10, 1, 9, 14, 9, + 15, 8, -4,-11, 12, 2,-11,-12, -8, 0, 19, 28, 4, -1,-15,-26, + -15, 27, 2,-14,-14, 22, 1, -9, -4, -6,-13,-10, -6,-14, 6, 47, + -35,-20, 6, 23, 6, 9, 6, 4, -6, 2, 23,-22, -7, 4, 28,-21, + 20,-22, -2, 6, 22,-28, -5, 8,-10,-18,-16,-12, 36, 19, 2, -1, + -3, 0, 4, 8,-45,-10, 23, 23, 40, 15,-20,-35, -4, -1, 4, 1, + 9, -5,-33, 24, 8, 3,-26, 19, -1, 4, 6, -3, 32, 25,-13,-49, + 24, 24, 15, 7,-17,-27,-19, -7,-47, 0, 39, 24,-21, -6, 7, 4, + -1, 0,-10,-13, 1, 1, 5, 16, 20, 5, -3, -9, -1, -4, -2, -6, + -17, -7, 1, 4, 12, 7, 0, 0, 3, 0, 12, 11, -3, 1, 0,-23, + 4, 17, -6, 0, 6, 3,-25, 0,-17, 10, 8, 5,-14, 4, 1, 4, + 13, 10, 4, 2,-23, -9, 1, 2, 3, -3, 1, 7, 1,-23, -7, 20, + -7,-18, 2, 12, -5, -4, 10, 9, 4, 10, 7,-24, 6, 3, 4,-10, + 22,-14,-22, 6, 0, 5, 5, -1, -4, 3,-11, -4, -7, 31, 7,-14, + -5,-16, -1, 42, -4, -2, -9, -5, 5, -8, -6, -3, 42, -4,-21, -5, + -18, 12, 20,-12, 13,-13,-10, 7, -8, -9, -2,-18,-16, 6, 40, 8, + 10, -1, 0, 4, -3, 4, -1,-13, -2, 6, 1,-15, 5, 3, 1, 2, + -4, -2, 1, 3, 15, 0, -9, -4, -3, -4, -4, -4, -3, 5, 16, -3, + 2, 13, 3, 4, -3, -8,-10, 0, -6, -2, -4, -1, -2, -3, -6, 23, + 6, -6, 7, 1, 4,-18, 5, 1, -1, 1,-15, 14, -5, 6, -4, 4, + 2, 2, 2, 6,-24, 2, 7, 3,-26, 0, 3, 3, 5, 7, 1, 6, + 14, -2,-18, -3, 7, 5, -4, 2, -6, 3, 32, 1, -6, -6, -6,-12, + 5,-36, 7, 6, 9, -1, 11, 0, 4, 4, 5, 3, 4, 15, 3,-38, + 10, 23, -5,-42, 0, 4, 4, 4, 23, 17, -6,-13,-13,-37, 1, 29, + 5,-14, -1, 1, 5, 0, 3, 1, 0, 4, -5, 2, 8, 0, 0,-10, + 4, 7, -2, -3,-10, 3, 1, 1,-12, -1, 13, 3, 0, -1, 1, -3, + 0, -1, 3, 1, -6, -9, 3, 9, -6, 1, -4, -6, 8, -1, 0, 8, + -3, -3, 0, 18, -5, -1, -4, -1, -8, -2, 3, -4, 0, 17, -1, -5, + 5, -2, 9,-10, 1, -5, 6, -5, 4, 2, 2, 3, 10,-14, -8, 1, + -1, -2,-18, -1, -1, 20, 1, 2, -1, 1, -9, 1, -1, -9, 22, -4, + 6, -4, 8, -3, -1, 7,-19, 5, -7, 31, -4, -4, -6, 0, -5, -5, + -7, -8,-19, -4, 1, 1, 4, 32, 38, -1, -8, 4, -7, -8, -6,-12, + -1, 0, -7, 1, -1, 9, -1, 0, 9, -1, -1, 0, 2, -6, 1, -3, + -12, 0, 2, 1, 1, 1, 8, 0, 9, 1, 0, 2, -2, 1,-11, 0, + 0, 8, 2,-10, -1, 2, -1, 0, -2, -4, 0, -5, -2, -1, -1, 14, + -3, 7, -1, 5, 0,-10, 1, 1, -1, -5, 14, -1, -2, 1, -3, -2, + -6, 0, 0, 6, 2, 3, -9, 4, 4, -5, -1, -1, -7, 3, 8, -1, + 2, -4, -1,-11, 11, 2, 1, 0, -1, 2, 3, 9, 0, 2, 0,-15, + 3, 5,-20, 3, 3, -1, 3, 3, 1, -1, 16, 1, 2,-29, 9, 2, + -13, -6, -1, -3, 36, -1, -8, -3, 2, 5, 4, 2,-37, 9, 11, 3 }; -static const uint32 s_svq1IntraCodebook4x4[384] = { - 0x0603fdf5, 0x0705fff6, 0x0706fff7, 0x0604fff7, - 0xf2000705, 0xf1020906, 0xf1020906, 0xf2000604, - 0xfafb0310, 0xf8f80110, 0xf7f7ff0e, 0xf8f8000c, - 0x11100c08, 0x090602fe, 0x00fcf8f6, 0xf9f5f2f1, - 0x10fef6f9, 0x12fdf5f9, 0x14fff5f9, 0x1301f8fa, - 0xeff0f3f7, 0xf7f9fe02, 0xff04080b, 0x070b0f10, - 0x0f0dfeea, 0x100efee8, 0x0f0dfce7, 0x0d0afae7, - 0x10161a1a, 0x03090f11, 0xf2f5fafe, 0xe4e4e7ec, - 0xebe7e5e5, 0xf9f5f1f0, 0x0d0c0803, 0x1e1f1c17, - 0xdff91014, 0xddfa1316, 0xdefa1316, 0xe0fa1114, - 0x2602ecec, 0x2802eaeb, 0x2802eaeb, 0x2603ecec, - 0x1a18fcd1, 0x1b1afdce, 0x1b1afdce, 0x1a18fcd1, - 0xe5e9062d, 0xe4e70530, 0xe4e60530, 0xe5e8062c, - 0x4cf6dce2, 0x4ef5dbe1, 0x4ef5dbe1, 0x4df6dce1, - 0x3423e0cb, 0x3424deca, 0x3424deca, 0x3322dfcb, - 0x413edea3, 0x423edea3, 0x413edea3, 0x403cdea3, - 0x020200f9, 0x0303fff8, 0x050400f8, 0x050501fa, - 0x0b0b0703, 0x03030202, 0xf9fafe01, 0xf3f5fb01, - 0xfdfcfe03, 0xfbfb0007, 0xf9fb040c, 0xf9fc060e, - 0xfe030e12, 0xfd000406, 0x00fefbf8, 0x02fef5f0, - 0x1207faf8, 0x0d02f8f9, 0x06fefafc, 0x01fdfc00, - 0xeef3fd01, 0xf9fbff00, 0x070601ff, 0x110f04fe, - 0xfef9f2f1, 0x00fffbfa, 0x01030606, 0x01060d0f, - 0x0af5fe02, 0x0bf4ff02, 0x0bf4ff03, 0x0bf5fe02, - 0xfbff0ef7, 0xfbfe0ff7, 0xfbfe10f8, 0xfcff0ff9, - 0x08080602, 0x0c0903fe, 0x0a04fbf5, 0x00f8f0ed, - 0xf1f9080e, 0xf2f9070c, 0xf7fc0508, 0xfcff0305, - 0x02fef20c, 0x03fff10d, 0x03fff10e, 0x03fff20d, - 0xf30a0600, 0xf10a0600, 0xef090700, 0xf0080601, - 0xfe0ffbf8, 0xfe11faf8, 0xfd10faf8, 0xfe0ffbf8, - 0xf6f5f5f7, 0x08090a09, 0x090a0a08, 0xf9f8f7f8, - 0x07090a09, 0xf6f6f6f8, 0xf5f5f6f9, 0x080b0c0b, - 0x00070a00, 0xfa000700, 0xfafb0200, 0xfffcfffe, - 0xf7fa0005, 0x01020202, 0x070500fe, 0x0401fbfa, - 0xff02f803, 0x0003f704, 0x0003f905, 0x0003fb07, - 0x0902fdfb, 0x0801fdfa, 0x0701fdfa, 0x0400fefb, - 0x0103080d, 0xfffcfbfd, 0x00fdf9f8, 0x020301ff, - 0xf4fb0203, 0xf7fe0304, 0xfc010403, 0xff040503, - 0x00fcf8f7, 0x00020608, 0x0003080a, 0xfffdfbfa, - 0xfbf4f7fd, 0x00fbfd00, 0x04020302, 0x06070805, - 0x0c05feff, 0x0905ffff, 0xfeff0102, 0xeff5ff02, - 0xff0303f9, 0xff0403f7, 0xff0604f6, 0x000705f7, - 0x0202f9ee, 0x030501f8, 0x00010403, 0xfdfe0509, - 0x080600fe, 0xfdfbfbfc, 0xf8fafe01, 0xff03090a, - 0x00fefe00, 0x00fbfc00, 0x08fcf8fe, 0x1806f9fb, - 0x01f90109, 0x01f80109, 0x01f60008, 0xfff5ff08, - 0x03060808, 0x02030405, 0x00fffdfe, 0xfcf8f3f6, - 0xfd020400, 0xfb030600, 0xf4020a03, 0xeafc0a05, - 0x03fffc00, 0x05fffc01, 0x0800fb01, 0x07fefaff, - 0xfcfeffff, 0xfafcfeff, 0xfeffffff, 0x090a0501, - 0xfe00030a, 0xfbfeff06, 0xfafeff03, 0xfb000002, - 0x00000306, 0x01010306, 0x01fefe04, 0xfef7f700, - 0x0201fdf5, 0x050402fa, 0x040302fd, 0x020101fe, - 0xfefffcfa, 0xfeffff02, 0xfefe020a, 0xfffc020b, - 0x02fe0006, 0x00000303, 0x000303fa, 0x0005ffef, - 0x0b0a04ff, 0x0100fefd, 0xfdfbfcfd, 0xfffefeff, - 0xf4f7fd02, 0x02030303, 0x04040202, 0xfeff0102, - 0xf60509fe, 0xfb0505fd, 0x000201fe, 0x01fefeff, - 0xfe07fdfe, 0xfd07fdff, 0xfc08feff, 0xfd07fefe, - 0x0cfdf801, 0x04fefe02, 0xfb000301, 0xf90205ff, - 0xfb0103ff, 0x0103fef9, 0x02fef9fe, 0xfffb0314, - 0xfefd0005, 0x0600f9f9, 0x060700fa, 0xf9000602, - 0x01f906fe, 0x03f807fe, 0x03f907fe, 0x02fa07ff, - 0x0705fefb, 0xf8fc0104, 0xfbfe0306, 0x0703fbf9, - 0x0506ffff, 0xfc01ff00, 0xf9000102, 0xfc000001, - 0x010300f8, 0xffff01fe, 0x01fdff01, 0x0901fe01, - 0xfcfd0205, 0xfdff00ff, 0x010301fd, 0x020400fc, - 0x0cfefe02, 0x03fbfe00, 0x01fd00ff, 0x01fefffd, - 0x00030501, 0x01fefcfa, 0x02fefe00, 0xfffc0106, - 0xfffbfbfd, 0x04050503, 0xff010300, 0xfdfe01fe, - 0xfdfbfc02, 0xfefdfe04, 0xffff0006, 0x00000107, - 0x00fefefd, 0xfffbfdfe, 0xff0002fe, 0xff090bff, - 0xf6ff0100, 0xfa0001ff, 0x04010001, 0x0dfffb02, - 0x000504fe, 0x030601fb, 0x0203fefa, 0xfe00fefb, - 0xfe0101ff, 0x0200feff, 0x07050505, 0xf9f8fc00, - 0xfbff0200, 0xfd0202ff, 0xfb030500, 0xf4020803, - 0xfe000408, 0xfffcff0a, 0x00fdfa03, 0x0000fbfc, - 0x02fcf600, 0x0503faff, 0x0406fdff, 0x0204fe00, - 0xff010800, 0xfd010b00, 0xfcfe06ff, 0xfcf9fefd, - 0xffffff00, 0x05060504, 0xfbf8f7fb, 0x02030202, - 0x01060200, 0x00030002, 0xfefffe01, 0xfafdff00, - 0x00020000, 0x01020004, 0x0000fe05, 0x02fff7fe, - 0xf6000100, 0x000801ff, 0x0004feff, 0xff02ff01, - 0xff02fefd, 0xfd02fffd, 0x0001ff00, 0x03ff0108, - 0x02010100, 0x00fefc00, 0xff01fbff, 0x020bfffe, - 0xfefe0501, 0x00fc0200, 0x01fb01fe, 0x01000500, - 0x0600fdfb, 0x000002fc, 0x000105fd, 0x000003fd, - 0x01fdfe03, 0x0800fc01, 0x03fefdfe, 0xffff0201, - 0x02000101, 0x06010002, 0x0102ff01, 0xed000300, - 0x02fefd01, 0xf9fe0506, 0x010301fd, 0x0200ffff, - 0xfcfffff8, 0x02ff0101, 0x03020304, 0x000301fb, - 0x01ff0200, 0x050000fd, 0x0800fefb, 0x06fcfcfc, - 0x02010201, 0x02fd0202, 0x00f70004, 0x01f50007, - 0xfe000000, 0xfaff0303, 0xf6fd0304, 0x020602ff, - 0x05fdfe07, 0xff0300fc, 0xf90102fc, 0x03ffff02, - 0x02020203, 0xfbf9f9fb, 0x02040605, 0x0100fffe +static const int8 s_svq1IntraCodebook4x4[1536] = { + -11, -3, 3, 6,-10, -1, 5, 7, -9, -1, 6, 7, -9, -1, 4, 6, + 5, 7, 0,-14, 6, 9, 2,-15, 6, 9, 2,-15, 4, 6, 0,-14, + 16, 3, -5, -6, 16, 1, -8, -8, 14, -1, -9, -9, 12, 0, -8, -8, + 8, 12, 16, 17, -2, 2, 6, 9,-10, -8, -4, 0,-15,-14,-11, -7, + -7,-10, -2, 16, -7,-11, -3, 18, -7,-11, -1, 20, -6, -8, 1, 19, + -9,-13,-16,-17, 2, -2, -7, -9, 11, 8, 4, -1, 16, 15, 11, 7, + -22, -2, 13, 15,-24, -2, 14, 16,-25, -4, 13, 15,-25, -6, 10, 13, + 26, 26, 22, 16, 17, 15, 9, 3, -2, -6,-11,-14,-20,-25,-28,-28, + -27,-27,-25,-21,-16,-15,-11, -7, 3, 8, 12, 13, 23, 28, 31, 30, + 20, 16, -7,-33, 22, 19, -6,-35, 22, 19, -6,-34, 20, 17, -6,-32, + -20,-20, 2, 38,-21,-22, 2, 40,-21,-22, 2, 40,-20,-20, 3, 38, + -47, -4, 24, 26,-50, -3, 26, 27,-50, -3, 26, 27,-47, -4, 24, 26, + 45, 6,-23,-27, 48, 5,-25,-28, 48, 5,-26,-28, 44, 6,-24,-27, + -30,-36,-10, 76,-31,-37,-11, 78,-31,-37,-11, 78,-31,-36,-10, 77, + -53,-32, 35, 52,-54,-34, 36, 52,-54,-34, 36, 52,-53,-33, 34, 51, + -93,-34, 62, 65,-93,-34, 62, 66,-93,-34, 62, 65,-93,-34, 60, 64, + -7, 0, 2, 2, -8, -1, 3, 3, -8, 0, 4, 5, -6, 1, 5, 5, + 3, 7, 11, 11, 2, 2, 3, 3, 1, -2, -6, -7, 1, -5,-11,-13, + 3, -2, -4, -3, 7, 0, -5, -5, 12, 4, -5, -7, 14, 6, -4, -7, + 18, 14, 3, -2, 6, 4, 0, -3, -8, -5, -2, 0,-16,-11, -2, 2, + -8, -6, 7, 18, -7, -8, 2, 13, -4, -6, -2, 6, 0, -4, -3, 1, + 1, -3,-13,-18, 0, -1, -5, -7, -1, 1, 6, 7, -2, 4, 15, 17, + -15,-14, -7, -2, -6, -5, -1, 0, 6, 6, 3, 1, 15, 13, 6, 1, + 2, -2,-11, 10, 2, -1,-12, 11, 3, -1,-12, 11, 2, -2,-11, 11, + -9, 14, -1, -5, -9, 15, -2, -5, -8, 16, -2, -5, -7, 15, -1, -4, + 2, 6, 8, 8, -2, 3, 9, 12,-11, -5, 4, 10,-19,-16, -8, 0, + 14, 8, -7,-15, 12, 7, -7,-14, 8, 5, -4, -9, 5, 3, -1, -4, + 12,-14, -2, 2, 13,-15, -1, 3, 14,-15, -1, 3, 13,-14, -1, 3, + 0, 6, 10,-13, 0, 6, 10,-15, 0, 7, 9,-17, 1, 6, 8,-16, + -8, -5, 15, -2, -8, -6, 17, -2, -8, -6, 16, -3, -8, -5, 15, -2, + -9,-11,-11,-10, 9, 10, 9, 8, 8, 10, 10, 9, -8, -9, -8, -7, + 9, 10, 9, 7, -8,-10,-10,-10, -7,-10,-11,-11, 11, 12, 11, 8, + 0, 10, 7, 0, 0, 7, 0, -6, 0, 2, -5, -6, -2, -1, -4, -1, + 5, 0, -6, -9, 2, 2, 2, 1, -2, 0, 5, 7, -6, -5, 1, 4, + 3, -8, 2, -1, 4, -9, 3, 0, 5, -7, 3, 0, 7, -5, 3, 0, + -5, -3, 2, 9, -6, -3, 1, 8, -6, -3, 1, 7, -5, -2, 0, 4, + 13, 8, 3, 1, -3, -5, -4, -1, -8, -7, -3, 0, -1, 1, 3, 2, + 3, 2, -5,-12, 4, 3, -2, -9, 3, 4, 1, -4, 3, 5, 4, -1, + -9, -8, -4, 0, 8, 6, 2, 0, 10, 8, 3, 0, -6, -5, -3, -1, + -3, -9,-12, -5, 0, -3, -5, 0, 2, 3, 2, 4, 5, 8, 7, 6, + -1, -2, 5, 12, -1, -1, 5, 9, 2, 1, -1, -2, 2, -1,-11,-17, + -7, 3, 3, -1, -9, 3, 4, -1,-10, 4, 6, -1, -9, 5, 7, 0, + -18, -7, 2, 2, -8, 1, 5, 3, 3, 4, 1, 0, 9, 5, -2, -3, + -2, 0, 6, 8, -4, -5, -5, -3, 1, -2, -6, -8, 10, 9, 3, -1, + 0, -2, -2, 0, 0, -4, -5, 0, -2, -8, -4, 8, -5, -7, 6, 24, + 9, 1, -7, 1, 9, 1, -8, 1, 8, 0,-10, 1, 8, -1,-11, -1, + 8, 8, 6, 3, 5, 4, 3, 2, -2, -3, -1, 0,-10,-13, -8, -4, + 0, 4, 2, -3, 0, 6, 3, -5, 3, 10, 2,-12, 5, 10, -4,-22, + 0, -4, -1, 3, 1, -4, -1, 5, 1, -5, 0, 8, -1, -6, -2, 7, + -1, -1, -2, -4, -1, -2, -4, -6, -1, -1, -1, -2, 1, 5, 10, 9, + 10, 3, 0, -2, 6, -1, -2, -5, 3, -1, -2, -6, 2, 0, 0, -5, + 6, 3, 0, 0, 6, 3, 1, 1, 4, -2, -2, 1, 0, -9, -9, -2, + -11, -3, 1, 2, -6, 2, 4, 5, -3, 2, 3, 4, -2, 1, 1, 2, + -6, -4, -1, -2, 2, -1, -1, -2, 10, 2, -2, -2, 11, 2, -4, -1, + 6, 0, -2, 2, 3, 3, 0, 0, -6, 3, 3, 0,-17, -1, 5, 0, + -1, 4, 10, 11, -3, -2, 0, 1, -3, -4, -5, -3, -1, -2, -2, -1, + 2, -3, -9,-12, 3, 3, 3, 2, 2, 2, 4, 4, 2, 1, -1, -2, + -2, 9, 5,-10, -3, 5, 5, -5, -2, 1, 2, 0, -1, -2, -2, 1, + -2, -3, 7, -2, -1, -3, 7, -3, -1, -2, 8, -4, -2, -2, 7, -3, + 1, -8, -3, 12, 2, -2, -2, 4, 1, 3, 0, -5, -1, 5, 2, -7, + -1, 3, 1, -5, -7, -2, 3, 1, -2, -7, -2, 2, 20, 3, -5, -1, + 5, 0, -3, -2, -7, -7, 0, 6, -6, 0, 7, 6, 2, 6, 0, -7, + -2, 6, -7, 1, -2, 7, -8, 3, -2, 7, -7, 3, -1, 7, -6, 2, + -5, -2, 5, 7, 4, 1, -4, -8, 6, 3, -2, -5, -7, -5, 3, 7, + -1, -1, 6, 5, 0, -1, 1, -4, 2, 1, 0, -7, 1, 0, 0, -4, + -8, 0, 3, 1, -2, 1, -1, -1, 1, -1, -3, 1, 1, -2, 1, 9, + 5, 2, -3, -4, -1, 0, -1, -3, -3, 1, 3, 1, -4, 0, 4, 2, + 2, -2, -2, 12, 0, -2, -5, 3, -1, 0, -3, 1, -3, -1, -2, 1, + 1, 5, 3, 0, -6, -4, -2, 1, 0, -2, -2, 2, 6, 1, -4, -1, + -3, -5, -5, -1, 3, 5, 5, 4, 0, 3, 1, -1, -2, 1, -2, -3, + 2, -4, -5, -3, 4, -2, -3, -2, 6, 0, -1, -1, 7, 1, 0, 0, + -3, -2, -2, 0, -2, -3, -5, -1, -2, 2, 0, -1, -1, 11, 9, -1, + 0, 1, -1,-10, -1, 1, 0, -6, 1, 0, 1, 4, 2, -5, -1, 13, + -2, 4, 5, 0, -5, 1, 6, 3, -6, -2, 3, 2, -5, -2, 0, -2, + -1, 1, 1, -2, -1, -2, 0, 2, 5, 5, 5, 7, 0, -4, -8, -7, + 0, 2, -1, -5, -1, 2, 2, -3, 0, 5, 3, -5, 3, 8, 2,-12, + 8, 4, 0, -2, 10, -1, -4, -1, 3, -6, -3, 0, -4, -5, 0, 0, + 0,-10, -4, 2, -1, -6, 3, 5, -1, -3, 6, 4, 0, -2, 4, 2, + 0, 8, 1, -1, 0, 11, 1, -3, -1, 6, -2, -4, -3, -2, -7, -4, + 0, -1, -1, -1, 4, 5, 6, 5, -5, -9, -8, -5, 2, 2, 3, 2, + 0, 2, 6, 1, 2, 0, 3, 0, 1, -2, -1, -2, 0, -1, -3, -6, + 0, 0, 2, 0, 4, 0, 2, 1, 5, -2, 0, 0, -2, -9, -1, 2, + 0, 1, 0,-10, -1, 1, 8, 0, -1, -2, 4, 0, 1, -1, 2, -1, + -3, -2, 2, -1, -3, -1, 2, -3, 0, -1, 1, 0, 8, 1, -1, 3, + 0, 1, 1, 2, 0, -4, -2, 0, -1, -5, 1, -1, -2, -1, 11, 2, + 1, 5, -2, -2, 0, 2, -4, 0, -2, 1, -5, 1, 0, 5, 0, 1, + -5, -3, 0, 6, -4, 2, 0, 0, -3, 5, 1, 0, -3, 3, 0, 0, + 3, -2, -3, 1, 1, -4, 0, 8, -2, -3, -2, 3, 1, 2, -1, -1, + 1, 1, 0, 2, 2, 0, 1, 6, 1, -1, 2, 1, 0, 3, 0,-19, + 1, -3, -2, 2, 6, 5, -2, -7, -3, 1, 3, 1, -1, -1, 0, 2, + -8, -1, -1, -4, 1, 1, -1, 2, 4, 3, 2, 3, -5, 1, 3, 0, + 0, 2, -1, 1, -3, 0, 0, 5, -5, -2, 0, 8, -4, -4, -4, 6, + 1, 2, 1, 2, 2, 2, -3, 2, 4, 0, -9, 0, 7, 0,-11, 1, + 0, 0, 0, -2, 3, 3, -1, -6, 4, 3, -3,-10, -1, 2, 6, 2, + 7, -2, -3, 5, -4, 0, 3, -1, -4, 2, 1, -7, 2, -1, -1, 3, + 3, 2, 2, 2, -5, -7, -7, -5, 5, 6, 4, 2, -2, -1, 0, 1 }; -static const uint32 s_svq1IntraCodebook8x4[768] = { - 0x06060605, 0x08080707, 0x00000000, 0x03020100, - 0xfbfcfcfd, 0xfefdfcfb, 0xfbfcfcfc, 0xfdfdfcfc, - 0x02020201, 0x03030302, 0x04030302, 0x05050504, - 0x010100ff, 0x04040302, 0xf7f7f6f7, 0xfbfaf9f8, - 0xfafbfcfc, 0xf9f9f9fa, 0xfefeff00, 0xfcfcfdfd, - 0x03030404, 0x00010102, 0x06070707, 0x04040506, - 0x06050402, 0xfafd0104, 0x05050403, 0xf8fb0004, - 0x04040302, 0xf6f9fe02, 0x01020202, 0xf4f7fc00, - 0x01fdf9f7, 0x03030404, 0x03fef9f6, 0x03030505, - 0x03fefaf7, 0x03040506, 0x03fffaf8, 0x02030404, - 0xfbfbfbfb, 0x070401fd, 0xfcfbfbfb, 0x080601fe, - 0xfdfcfbfc, 0x0a0803ff, 0xfefdfcfd, 0x0b090501, - 0xfefefefe, 0xfefefefe, 0xfbfbfbfc, 0xfcfbfbfb, - 0xfcfcfcfd, 0xfdfcfcfc, 0x0b0a0a09, 0x0a0a0b0b, - 0xfe010407, 0xf6f7fafc, 0x00030709, 0xf7f8fcfe, - 0x0204080b, 0xf8fafd00, 0x0305090b, 0xf9fbfe01, - 0xf4f3f3f3, 0xf8f8f6f5, 0x03020100, 0x03040404, - 0x06050403, 0x04050606, 0x04040403, 0x02030303, - 0x0a0b0a0a, 0x07080909, 0x06060606, 0x02030405, - 0xff000000, 0xfcfcfdfe, 0xf4f5f6f6, 0xf2f2f2f3, - 0x10111010, 0x0b0c0d0f, 0xfcfdfeff, 0xfdfcfcfc, - 0xfafafbfc, 0xfafafafa, 0xfafafafb, 0xfbfbfafa, - 0xf4f3f3f3, 0xfaf8f6f5, 0xfaf9f8f7, 0x0100fefc, - 0x0301fffe, 0x09080705, 0x0b090705, 0x0f0f0e0d, - 0x070b0e10, 0xf7f9fd02, 0x03080c0e, 0xf5f7faff, - 0x0004090b, 0xf3f5f8fc, 0xfd010508, 0xf2f4f6fa, - 0xfdf7f1ee, 0x0b090601, 0xfff9f3ef, 0x0c0b0703, - 0x01fbf5f1, 0x0d0c0905, 0x02fdf7f3, 0x0d0b0905, - 0x0f131516, 0xf7fc030a, 0x090f1214, 0xeff4fb02, - 0x01080d10, 0xe8ecf2f9, 0xf8ff060a, 0xe5e7ebf1, - 0xf2ece9e7, 0x0e0901f9, 0xf7f0ebe9, 0x15100900, - 0xfff6f0ec, 0x19161008, 0x06fdf5f1, 0x1b19140e, - 0x0100fefc, 0x02020202, 0x0200fefb, 0x03030303, - 0x01fffcfa, 0x03030302, 0x00fefbf9, 0x02020101, - 0x01010102, 0xfdfe0001, 0x01020303, 0xfcfdff00, - 0x01020304, 0xfafcfe00, 0x01030405, 0xfafbfdff, - 0x04060605, 0xfdfe0002, 0x04040403, 0xff000103, - 0xfffffefe, 0xfefeffff, 0xfefdfcfb, 0xfdfdfefe, - 0xffffffff, 0xffffffff, 0xfcfcfcfd, 0xfdfdfdfd, - 0xffffffff, 0xfeffffff, 0x06060605, 0x03040506, - 0x04040404, 0x07060504, 0xffffff00, 0x020100ff, - 0xfdfdfdfe, 0x00fffefd, 0xfcfcfdfd, 0xfffefdfc, - 0xfcfcfe00, 0x030200fe, 0xfdfdfe00, 0x050402ff, - 0xfdfcfeff, 0x06050300, 0xfdfcfdfe, 0x050402ff, - 0xfd000409, 0x0100fffd, 0xfcff0408, 0x0201fffd, - 0xfbfd0206, 0x0100fefc, 0xfcfd0105, 0x0100fefc, - 0xff010305, 0xf6f6f8fc, 0x01020303, 0xfcfdfe00, - 0x02010101, 0x00010203, 0x020100ff, 0x02030403, - 0x02020100, 0xfdfdff01, 0x01010100, 0xfdfcfeff, - 0xfdfdfdfd, 0x02fffdfd, 0x00fdfcfc, 0x0e0c0703, - 0xfafafbfb, 0xfbfafafa, 0x01020202, 0x00000000, - 0x02030404, 0x00000001, 0x04050606, 0x01010202, - 0xfdfaf9f9, 0x08070400, 0xfdfdfeff, 0x0201fffe, - 0xff010303, 0xfffefefe, 0x02040606, 0xfefefe00, - 0x02fefbfa, 0x0c0b0905, 0x00fefcfc, 0x06050402, - 0xfefefefd, 0x0100fffe, 0xfdfefefe, 0xfefdfdfd, - 0x0301fdf9, 0xfbfd0003, 0x0503fefa, 0xfbfd0104, - 0x0604fffb, 0xfcfd0205, 0x070500fc, 0xfdff0306, - 0x00000000, 0x00000000, 0xfdfdfefe, 0xfffefdfd, - 0x09080706, 0x06070809, 0xfbfbfcfc, 0xfcfbfafa, - 0xfcfaf8f7, 0x06060300, 0x03fffcfb, 0x03050605, - 0x06060301, 0xfbfe0104, 0x01050706, 0xf5f6f9fd, - 0x0105090a, 0xfcfafafd, 0xfbff0305, 0x02fefbfa, - 0xfafafcfe, 0x0a0601fc, 0xfcf9f9fa, 0x0c0b0701, - 0x02030506, 0x00000000, 0xfeff0102, 0xfffffefd, - 0xfcfeff00, 0x01fffefc, 0xfeff0000, 0x030200ff, - 0xfefeff00, 0xfffffefe, 0x01020405, 0x00000000, - 0x01030506, 0x00000000, 0xfcfe0002, 0xfefefdfc, - 0x0200fcf9, 0x01020202, 0x0000fdf9, 0x00000000, - 0x0101fffc, 0x01000000, 0x020201ff, 0x03030202, - 0x020200fe, 0x01010101, 0x020201ff, 0xff000001, - 0x02040200, 0xfdfeff00, 0x01030201, 0xfafafcfe, - 0x04020201, 0x01040605, 0xffffff00, 0xfcfe0000, - 0xfeff0000, 0xfafcfefe, 0x00000102, 0xfdff0101, - 0x01010101, 0x03030201, 0x00010000, 0x04040201, - 0xffffffff, 0x03020100, 0xfbfbfcfc, 0x00fffdfb, - 0xfcfbfbfa, 0xfffffefd, 0x010000ff, 0x03030201, - 0x01010100, 0x04030202, 0xffff0000, 0x03020100, - 0x01010100, 0xffff0000, 0x02030301, 0xfefeff01, - 0x020200fe, 0x01010202, 0xfefcf8f7, 0x03030301, - 0xfeffffff, 0xfcfdfdfd, 0xff000000, 0xfdfdfefe, - 0x00020202, 0xffffffff, 0x03040505, 0x02020202, - 0xfcff0306, 0x0101fffd, 0xfcfdff02, 0x000202ff, - 0x01fefeff, 0xfd010404, 0x0401fffe, 0xf8fd0306, - 0x01020303, 0xfefefeff, 0xfffefcfc, 0x04040301, - 0xfcfbfbfc, 0x020200fe, 0x01040707, 0xfefdfeff, - 0x000301ff, 0x0600fafc, 0x010401fe, 0x07fffafc, - 0x020401fd, 0x06fffafd, 0x020300fe, 0x04fffbfe, - 0x01feff01, 0xf9ff0404, 0xfffcff01, 0xfa000605, - 0xfdfc0003, 0xfc020603, 0xfcfb0003, 0xfd010401, - 0x03030202, 0x02020303, 0xf9fafbfc, 0xfaf9f9f9, - 0x03030201, 0x02020303, 0x01010000, 0x01020201, - 0x03fdfd03, 0x02fefe04, 0x04fcfc03, 0x02fcfc04, - 0x04fcfc04, 0x03fdfc04, 0x03fcfd03, 0x03fdfd03, - 0xfefefefe, 0xfffffefe, 0x08080706, 0x05060708, - 0xf9f9fafb, 0xfbfaf9f8, 0x02020101, 0x01010202, - 0x00000000, 0x0000ff00, 0x000000ff, 0x0000ff00, - 0xfefefdfe, 0xfdfdfdfe, 0x06050302, 0x00010204, - 0x00020608, 0x00000000, 0x00000104, 0xffffff00, - 0x0000ff01, 0xfdfeff00, 0x00fffefe, 0xfbfcfe00, - 0xfeff0103, 0xfbfbfcfd, 0x00000102, 0x00000101, - 0x00ffff00, 0x02020202, 0x01fffeff, 0x02020202, - 0xfffeff00, 0x00ffffff, 0xfffefeff, 0x010000ff, - 0x02010102, 0x00010102, 0x01030506, 0xfcfcfe00, - 0x00fffefd, 0xff000101, 0x04030100, 0x01030505, - 0x00ffffff, 0xfeff0001, 0xfffefefe, 0xfdfeff00, - 0xfefeff00, 0x0200ffff, 0xfffeff01, 0x0200ffff, - 0xfefe0001, 0x0501fefe, 0xfefeff01, 0x0a0500fe, - 0x00000000, 0xffffff00, 0x00ffffff, 0x02010000, - 0x03020201, 0x05060404, 0xfefdfdfd, 0xfdfdfdfe, - 0xfefeff01, 0x07050300, 0xfdfe0002, 0x030200fe, - 0xfdfe0103, 0xfffffefd, 0xff000103, 0xffffffff, - 0x04050301, 0xfcfdff02, 0x0201fefd, 0xfeff0001, - 0x0200fdfb, 0x00000102, 0x0201fffd, 0x00000102, - 0xffffff00, 0x04030201, 0xfdfcfcfd, 0x010000ff, - 0xfffefdfe, 0x01010101, 0x0300fefe, 0x02030404, - 0xfefdfcfc, 0x030201ff, 0x01010100, 0xfdfdfeff, - 0x04050403, 0xfdfdff02, 0x0200fefe, 0x00010202, - 0x070500fc, 0xfcfcff04, 0x030402ff, 0xfefdfd00, - 0xff000102, 0x0100fefe, 0xfeff0000, 0x0201fffe, - 0xfffefdfc, 0x02020100, 0x0005090a, 0xfefdfcfd, - 0xfefeff01, 0x000000ff, 0x01fffefe, 0xff000101, - 0x0300fdfb, 0xfe000204, 0x0100fffe, 0xffff0001, - 0xfeff0203, 0x0101fffe, 0xfbff0507, 0x0402fefa, - 0xfd0303fe, 0xfe0201fc, 0xfd0403fd, 0xfe0302fc, - 0xfd0403fd, 0xfe0302fc, 0xfe0402fc, 0xff0201fd, - 0xfdff0304, 0x0201fffd, 0x00fcfafc, 0x01040504, - 0x06050200, 0xfcfbfd02, 0xfdff0101, 0x0402fefb, - 0x020100ff, 0x04030302, 0x010100ff, 0xffffff00, - 0x020100ff, 0xfeff0102, 0x00fffefd, 0xfdfeff00, - 0x01010101, 0x02010000, 0x00ff0001, 0x00010100, - 0xfffcfe01, 0x00010201, 0xfdf9fc01, 0x01020301, - 0x01010101, 0xff000101, 0x00010101, 0x00020201, - 0x00000101, 0xfd000200, 0xff000203, 0xf7fafeff, - 0x01000000, 0x02010000, 0x00000001, 0x0200ffff, - 0x01010100, 0x00fefdff, 0x0601fbf9, 0xffff0206, - 0xfdff0103, 0x0401fefc, 0xfdfe0002, 0x02fffdfc, - 0x01010202, 0x01000001, 0x00000101, 0x01000000, - 0xfe0101ff, 0xfffcfafb, 0x030401ff, 0x02010002, - 0x030200ff, 0x01000001, 0x000100ff, 0x00ffff00, - 0x02020100, 0x01fffe00, 0xfefffffe, 0x080602ff, - 0xfdfeffff, 0x020100fe, 0xff0000ff, 0xffff00ff, - 0x01010102, 0x00000001, 0x01010000, 0x01ffff01, - 0x020200ff, 0x03fefdff, 0x00030200, 0x04fef9fb, - 0x000000ff, 0xfdfdfeff, 0xfeff00ff, 0xfefefefe, - 0x00000101, 0xff000201, 0x02010201, 0x00020605, - 0x00fdfcfe, 0xfd000202, 0x01000103, 0xfdfe0102, - 0x00000103, 0xff000000, 0xfefeff01, 0x030301ff, - 0x02010203, 0xfe010304, 0xfdfcfcfe, 0xfdfe00ff, - 0xffff0001, 0xff000100, 0x00000203, 0x00010100, - 0x00000101, 0x00000000, 0x02030302, 0x01010202, - 0xfdfeff00, 0xfcfbfbfb, 0xff000101, 0x03030100, - 0x00fefaf7, 0x02020101, 0x0201fefa, 0x01000101, - 0x020201fe, 0x01010101, 0x01020200, 0x01010100, - 0x00000001, 0x00ff0000, 0x00000000, 0x00fefdff, - 0xfefdfdfd, 0x090703ff, 0x02020201, 0xfdfcfe00, - 0xfffe0002, 0xfaff0403, 0xfdfe0001, 0x000303ff, - 0x00030300, 0x0101fffe, 0x0203fffa, 0x0100feff, - 0xfe000305, 0x010200fd, 0x02020101, 0xf9fcfe00, - 0x0201fefd, 0xfcff0102, 0xfe000202, 0x020200fe, - 0xfdfe0000, 0x0000fffe, 0x00000000, 0x02020000, - 0x0100fffe, 0x03020100, 0x0000fefc, 0x030200ff, - 0xfffefefe, 0x040200ff, 0x00000000, 0x0100ffff, - 0xffffff00, 0x0000ffff, 0x00020406, 0xfffffeff, - 0x01010100, 0xf6fbff01, 0x01010101, 0xfc000101, - 0x01010001, 0xff010101, 0x01010102, 0x00000000, - 0x030401fd, 0x00ff0103, 0x000100fc, 0x000000ff, - 0x010200fb, 0xff000101, 0xfe0102ff, 0xff00fffe, - 0x03050402, 0x0201ff00, 0x00010000, 0xfffffefe, - 0xfefefefe, 0x00fffefd, 0x00010000, 0x02010000, - 0xfdfefe00, 0xff0202ff, 0x00000001, 0xfe030501, - 0xff00ffff, 0xfb000200, 0x000100ff, 0xfe020200, - 0xffff0103, 0x02010100, 0x01000001, 0x01010101, - 0x01fef8f6, 0x01010102, 0x010201ff, 0x00000000, - 0x0100ffff, 0x01020202, 0x00ffffff, 0xfcfbfdff, - 0x01020101, 0x02000001, 0xfffffeff, 0x040200ff, - 0x00fbf9fd, 0x00000002, 0x01feff03, 0x02010102, - 0x01fffe01, 0x01000102, 0x0300ff00, 0xffffff02, - 0x00010102, 0x00000000, 0x03fef9f7, 0x01010203, - 0xfe000203, 0x0101fffe, 0x0000ff00, 0x00000101, - 0x0101fffe, 0x00000001, 0xfe010201, 0x0201fdfc, - 0xfe010201, 0x010300fd, 0x0000ffff, 0xfc000301, - 0x01ff0002, 0x03fefe02, 0x02ff0002, 0x01fcfe03, - 0x01010100, 0xfefafe02, 0x000000ff, 0xfffe0002, - 0x0201ffff, 0xfefdfe01, 0xfffeff03, 0x020100ff, - 0x0000040a, 0xfffefeff, 0xfffeff03, 0x00ffff00, - 0x010702fb, 0x0001fefc, 0xff0302fe, 0x000200fd, - 0x00000102, 0xfeff0101, 0xfffefe01, 0x0000feff, - 0xf9fe0300, 0x000003ff, 0xfbfd0301, 0x00ff0302, - 0xfefe0200, 0x00fe0204, 0x00ff01ff, 0x01feff02, - 0xfcfd0004, 0x010201fe, 0x05030000, 0xfeff0103, - 0xff010101, 0x0101fffd, 0xfefeff01, 0xfeff0000 +static const int8 s_svq1IntraCodebook8x4[3072] = { + 5, 6, 6, 6, 7, 7, 8, 8, 0, 0, 0, 0, 0, 1, 2, 3, + -3, -4, -4, -5, -5, -4, -3, -2, -4, -4, -4, -5, -4, -4, -3, -3, + 1, 2, 2, 2, 2, 3, 3, 3, 2, 3, 3, 4, 4, 5, 5, 5, + -1, 0, 1, 1, 2, 3, 4, 4, -9,-10, -9, -9, -8, -7, -6, -5, + -4, -4, -5, -6, -6, -7, -7, -7, 0, -1, -2, -2, -3, -3, -4, -4, + 4, 4, 3, 3, 2, 1, 1, 0, 7, 7, 7, 6, 6, 5, 4, 4, + 2, 4, 5, 6, 4, 1, -3, -6, 3, 4, 5, 5, 4, 0, -5, -8, + 2, 3, 4, 4, 2, -2, -7,-10, 2, 2, 2, 1, 0, -4, -9,-12, + -9, -7, -3, 1, 4, 4, 3, 3,-10, -7, -2, 3, 5, 5, 3, 3, + -9, -6, -2, 3, 6, 5, 4, 3, -8, -6, -1, 3, 4, 4, 3, 2, + -5, -5, -5, -5, -3, 1, 4, 7, -5, -5, -5, -4, -2, 1, 6, 8, + -4, -5, -4, -3, -1, 3, 8, 10, -3, -4, -3, -2, 1, 5, 9, 11, + -2, -2, -2, -2, -2, -2, -2, -2, -4, -5, -5, -5, -5, -5, -5, -4, + -3, -4, -4, -4, -4, -4, -4, -3, 9, 10, 10, 11, 11, 11, 10, 10, + 7, 4, 1, -2, -4, -6, -9,-10, 9, 7, 3, 0, -2, -4, -8, -9, + 11, 8, 4, 2, 0, -3, -6, -8, 11, 9, 5, 3, 1, -2, -5, -7, + -13,-13,-13,-12,-11,-10, -8, -8, 0, 1, 2, 3, 4, 4, 4, 3, + 3, 4, 5, 6, 6, 6, 5, 4, 3, 4, 4, 4, 3, 3, 3, 2, + 10, 10, 11, 10, 9, 9, 8, 7, 6, 6, 6, 6, 5, 4, 3, 2, + 0, 0, 0, -1, -2, -3, -4, -4,-10,-10,-11,-12,-13,-14,-14,-14, + 16, 16, 17, 16, 15, 13, 12, 11, -1, -2, -3, -4, -4, -4, -4, -3, + -4, -5, -6, -6, -6, -6, -6, -6, -5, -6, -6, -6, -6, -6, -5, -5, + -13,-13,-13,-12,-11,-10, -8, -6, -9, -8, -7, -6, -4, -2, 0, 1, + -2, -1, 1, 3, 5, 7, 8, 9, 5, 7, 9, 11, 13, 14, 15, 15, + 16, 14, 11, 7, 2, -3, -7, -9, 14, 12, 8, 3, -1, -6, -9,-11, + 11, 9, 4, 0, -4, -8,-11,-13, 8, 5, 1, -3, -6,-10,-12,-14, + -18,-15, -9, -3, 1, 6, 9, 11,-17,-13, -7, -1, 3, 7, 11, 12, + -15,-11, -5, 1, 5, 9, 12, 13,-13, -9, -3, 2, 5, 9, 11, 13, + 22, 21, 19, 15, 10, 3, -4, -9, 20, 18, 15, 9, 2, -5,-12,-17, + 16, 13, 8, 1, -7,-14,-20,-24, 10, 6, -1, -8,-15,-21,-25,-27, + -25,-23,-20,-14, -7, 1, 9, 14,-23,-21,-16, -9, 0, 9, 16, 21, + -20,-16,-10, -1, 8, 16, 22, 25,-15,-11, -3, 6, 14, 20, 25, 27, + -4, -2, 0, 1, 2, 2, 2, 2, -5, -2, 0, 2, 3, 3, 3, 3, + -6, -4, -1, 1, 2, 3, 3, 3, -7, -5, -2, 0, 1, 1, 2, 2, + 2, 1, 1, 1, 1, 0, -2, -3, 3, 3, 2, 1, 0, -1, -3, -4, + 4, 3, 2, 1, 0, -2, -4, -6, 5, 4, 3, 1, -1, -3, -5, -6, + 5, 6, 6, 4, 2, 0, -2, -3, 3, 4, 4, 4, 3, 1, 0, -1, + -2, -2, -1, -1, -1, -1, -2, -2, -5, -4, -3, -2, -2, -2, -3, -3, + -1, -1, -1, -1, -1, -1, -1, -1, -3, -4, -4, -4, -3, -3, -3, -3, + -1, -1, -1, -1, -1, -1, -1, -2, 5, 6, 6, 6, 6, 5, 4, 3, + 4, 4, 4, 4, 4, 5, 6, 7, 0, -1, -1, -1, -1, 0, 1, 2, + -2, -3, -3, -3, -3, -2, -1, 0, -3, -3, -4, -4, -4, -3, -2, -1, + 0, -2, -4, -4, -2, 0, 2, 3, 0, -2, -3, -3, -1, 2, 4, 5, + -1, -2, -4, -3, 0, 3, 5, 6, -2, -3, -4, -3, -1, 2, 4, 5, + 9, 4, 0, -3, -3, -1, 0, 1, 8, 4, -1, -4, -3, -1, 1, 2, + 6, 2, -3, -5, -4, -2, 0, 1, 5, 1, -3, -4, -4, -2, 0, 1, + 5, 3, 1, -1, -4, -8,-10,-10, 3, 3, 2, 1, 0, -2, -3, -4, + 1, 1, 1, 2, 3, 2, 1, 0, -1, 0, 1, 2, 3, 4, 3, 2, + 0, 1, 2, 2, 1, -1, -3, -3, 0, 1, 1, 1, -1, -2, -4, -3, + -3, -3, -3, -3, -3, -3, -1, 2, -4, -4, -3, 0, 3, 7, 12, 14, + -5, -5, -6, -6, -6, -6, -6, -5, 2, 2, 2, 1, 0, 0, 0, 0, + 4, 4, 3, 2, 1, 0, 0, 0, 6, 6, 5, 4, 2, 2, 1, 1, + -7, -7, -6, -3, 0, 4, 7, 8, -1, -2, -3, -3, -2, -1, 1, 2, + 3, 3, 1, -1, -2, -2, -2, -1, 6, 6, 4, 2, 0, -2, -2, -2, + -6, -5, -2, 2, 5, 9, 11, 12, -4, -4, -2, 0, 2, 4, 5, 6, + -3, -2, -2, -2, -2, -1, 0, 1, -2, -2, -2, -3, -3, -3, -3, -2, + -7, -3, 1, 3, 3, 0, -3, -5, -6, -2, 3, 5, 4, 1, -3, -5, + -5, -1, 4, 6, 5, 2, -3, -4, -4, 0, 5, 7, 6, 3, -1, -3, + 0, 0, 0, 0, 0, 0, 0, 0, -2, -2, -3, -3, -3, -3, -2, -1, + 6, 7, 8, 9, 9, 8, 7, 6, -4, -4, -5, -5, -6, -6, -5, -4, + -9, -8, -6, -4, 0, 3, 6, 6, -5, -4, -1, 3, 5, 6, 5, 3, + 1, 3, 6, 6, 4, 1, -2, -5, 6, 7, 5, 1, -3, -7,-10,-11, + 10, 9, 5, 1, -3, -6, -6, -4, 5, 3, -1, -5, -6, -5, -2, 2, + -2, -4, -6, -6, -4, 1, 6, 10, -6, -7, -7, -4, 1, 7, 11, 12, + 6, 5, 3, 2, 0, 0, 0, 0, 2, 1, -1, -2, -3, -2, -1, -1, + 0, -1, -2, -4, -4, -2, -1, 1, 0, 0, -1, -2, -1, 0, 2, 3, + 0, -1, -2, -2, -2, -2, -1, -1, 5, 4, 2, 1, 0, 0, 0, 0, + 6, 5, 3, 1, 0, 0, 0, 0, 2, 0, -2, -4, -4, -3, -2, -2, + -7, -4, 0, 2, 2, 2, 2, 1, -7, -3, 0, 0, 0, 0, 0, 0, + -4, -1, 1, 1, 0, 0, 0, 1, -1, 1, 2, 2, 2, 2, 3, 3, + -2, 0, 2, 2, 1, 1, 1, 1, -1, 1, 2, 2, 1, 0, 0, -1, + 0, 2, 4, 2, 0, -1, -2, -3, 1, 2, 3, 1, -2, -4, -6, -6, + 1, 2, 2, 4, 5, 6, 4, 1, 0, -1, -1, -1, 0, 0, -2, -4, + 0, 0, -1, -2, -2, -2, -4, -6, 2, 1, 0, 0, 1, 1, -1, -3, + 1, 1, 1, 1, 1, 2, 3, 3, 0, 0, 1, 0, 1, 2, 4, 4, + -1, -1, -1, -1, 0, 1, 2, 3, -4, -4, -5, -5, -5, -3, -1, 0, + -6, -5, -5, -4, -3, -2, -1, -1, -1, 0, 0, 1, 1, 2, 3, 3, + 0, 1, 1, 1, 2, 2, 3, 4, 0, 0, -1, -1, 0, 1, 2, 3, + 0, 1, 1, 1, 0, 0, -1, -1, 1, 3, 3, 2, 1, -1, -2, -2, + -2, 0, 2, 2, 2, 2, 1, 1, -9, -8, -4, -2, 1, 3, 3, 3, + -1, -1, -1, -2, -3, -3, -3, -4, 0, 0, 0, -1, -2, -2, -3, -3, + 2, 2, 2, 0, -1, -1, -1, -1, 5, 5, 4, 3, 2, 2, 2, 2, + 6, 3, -1, -4, -3, -1, 1, 1, 2, -1, -3, -4, -1, 2, 2, 0, + -1, -2, -2, 1, 4, 4, 1, -3, -2, -1, 1, 4, 6, 3, -3, -8, + 3, 3, 2, 1, -1, -2, -2, -2, -4, -4, -2, -1, 1, 3, 4, 4, + -4, -5, -5, -4, -2, 0, 2, 2, 7, 7, 4, 1, -1, -2, -3, -2, + -1, 1, 3, 0, -4, -6, 0, 6, -2, 1, 4, 1, -4, -6, -1, 7, + -3, 1, 4, 2, -3, -6, -1, 6, -2, 0, 3, 2, -2, -5, -1, 4, + 1, -1, -2, 1, 4, 4, -1, -7, 1, -1, -4, -1, 5, 6, 0, -6, + 3, 0, -4, -3, 3, 6, 2, -4, 3, 0, -5, -4, 1, 4, 1, -3, + 2, 2, 3, 3, 3, 3, 2, 2, -4, -5, -6, -7, -7, -7, -7, -6, + 1, 2, 3, 3, 3, 3, 2, 2, 0, 0, 1, 1, 1, 2, 2, 1, + 3, -3, -3, 3, 4, -2, -2, 2, 3, -4, -4, 4, 4, -4, -4, 2, + 4, -4, -4, 4, 4, -4, -3, 3, 3, -3, -4, 3, 3, -3, -3, 3, + -2, -2, -2, -2, -2, -2, -1, -1, 6, 7, 8, 8, 8, 7, 6, 5, + -5, -6, -7, -7, -8, -7, -6, -5, 1, 1, 2, 2, 2, 2, 1, 1, + 0, 0, 0, 0, 0, -1, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, + -2, -3, -2, -2, -2, -3, -3, -3, 2, 3, 5, 6, 4, 2, 1, 0, + 8, 6, 2, 0, 0, 0, 0, 0, 4, 1, 0, 0, 0, -1, -1, -1, + 1, -1, 0, 0, 0, -1, -2, -3, -2, -2, -1, 0, 0, -2, -4, -5, + 3, 1, -1, -2, -3, -4, -5, -5, 2, 1, 0, 0, 1, 1, 0, 0, + 0, -1, -1, 0, 2, 2, 2, 2, -1, -2, -1, 1, 2, 2, 2, 2, + 0, -1, -2, -1, -1, -1, -1, 0, -1, -2, -2, -1, -1, 0, 0, 1, + 2, 1, 1, 2, 2, 1, 1, 0, 6, 5, 3, 1, 0, -2, -4, -4, + -3, -2, -1, 0, 1, 1, 0, -1, 0, 1, 3, 4, 5, 5, 3, 1, + -1, -1, -1, 0, 1, 0, -1, -2, -2, -2, -2, -1, 0, -1, -2, -3, + 0, -1, -2, -2, -1, -1, 0, 2, 1, -1, -2, -1, -1, -1, 0, 2, + 1, 0, -2, -2, -2, -2, 1, 5, 1, -1, -2, -2, -2, 0, 5, 10, + 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, 0, 0, 0, 1, 2, + 1, 2, 2, 3, 4, 4, 6, 5, -3, -3, -3, -2, -2, -3, -3, -3, + 1, -1, -2, -2, 0, 3, 5, 7, 2, 0, -2, -3, -2, 0, 2, 3, + 3, 1, -2, -3, -3, -2, -1, -1, 3, 1, 0, -1, -1, -1, -1, -1, + 1, 3, 5, 4, 2, -1, -3, -4, -3, -2, 1, 2, 1, 0, -1, -2, + -5, -3, 0, 2, 2, 1, 0, 0, -3, -1, 1, 2, 2, 1, 0, 0, + 0, -1, -1, -1, 1, 2, 3, 4, -3, -4, -4, -3, -1, 0, 0, 1, + -2, -3, -2, -1, 1, 1, 1, 1, -2, -2, 0, 3, 4, 4, 3, 2, + -4, -4, -3, -2, -1, 1, 2, 3, 0, 1, 1, 1, -1, -2, -3, -3, + 3, 4, 5, 4, 2, -1, -3, -3, -2, -2, 0, 2, 2, 2, 1, 0, + -4, 0, 5, 7, 4, -1, -4, -4, -1, 2, 4, 3, 0, -3, -3, -2, + 2, 1, 0, -1, -2, -2, 0, 1, 0, 0, -1, -2, -2, -1, 1, 2, + -4, -3, -2, -1, 0, 1, 2, 2, 10, 9, 5, 0, -3, -4, -3, -2, + 1, -1, -2, -2, -1, 0, 0, 0, -2, -2, -1, 1, 1, 1, 0, -1, + -5, -3, 0, 3, 4, 2, 0, -2, -2, -1, 0, 1, 1, 0, -1, -1, + 3, 2, -1, -2, -2, -1, 1, 1, 7, 5, -1, -5, -6, -2, 2, 4, + -2, 3, 3, -3, -4, 1, 2, -2, -3, 3, 4, -3, -4, 2, 3, -2, + -3, 3, 4, -3, -4, 2, 3, -2, -4, 2, 4, -2, -3, 1, 2, -1, + 4, 3, -1, -3, -3, -1, 1, 2, -4, -6, -4, 0, 4, 5, 4, 1, + 0, 2, 5, 6, 2, -3, -5, -4, 1, 1, -1, -3, -5, -2, 2, 4, + -1, 0, 1, 2, 2, 3, 3, 4, -1, 0, 1, 1, 0, -1, -1, -1, + -1, 0, 1, 2, 2, 1, -1, -2, -3, -2, -1, 0, 0, -1, -2, -3, + 1, 1, 1, 1, 0, 0, 1, 2, 1, 0, -1, 0, 0, 1, 1, 0, + 1, -2, -4, -1, 1, 2, 1, 0, 1, -4, -7, -3, 1, 3, 2, 1, + 1, 1, 1, 1, 1, 1, 0, -1, 1, 1, 1, 0, 1, 2, 2, 0, + 1, 1, 0, 0, 0, 2, 0, -3, 3, 2, 0, -1, -1, -2, -6, -9, + 0, 0, 0, 1, 0, 0, 1, 2, 1, 0, 0, 0, -1, -1, 0, 2, + 0, 1, 1, 1, -1, -3, -2, 0, -7, -5, 1, 6, 6, 2, -1, -1, + 3, 1, -1, -3, -4, -2, 1, 4, 2, 0, -2, -3, -4, -3, -1, 2, + 2, 2, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, + -1, 1, 1, -2, -5, -6, -4, -1, -1, 1, 4, 3, 2, 0, 1, 2, + -1, 0, 2, 3, 1, 0, 0, 1, -1, 0, 1, 0, 0, -1, -1, 0, + 0, 1, 2, 2, 0, -2, -1, 1, -2, -1, -1, -2, -1, 2, 6, 8, + -1, -1, -2, -3, -2, 0, 1, 2, -1, 0, 0, -1, -1, 0, -1, -1, + 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, -1, -1, 1, + -1, 0, 2, 2, -1, -3, -2, 3, 0, 2, 3, 0, -5, -7, -2, 4, + -1, 0, 0, 0, -1, -2, -3, -3, -1, 0, -1, -2, -2, -2, -2, -2, + 1, 1, 0, 0, 1, 2, 0, -1, 1, 2, 1, 2, 5, 6, 2, 0, + -2, -4, -3, 0, 2, 2, 0, -3, 3, 1, 0, 1, 2, 1, -2, -3, + 3, 1, 0, 0, 0, 0, 0, -1, 1, -1, -2, -2, -1, 1, 3, 3, + 3, 2, 1, 2, 4, 3, 1, -2, -2, -4, -4, -3, -1, 0, -2, -3, + 1, 0, -1, -1, 0, 1, 0, -1, 3, 2, 0, 0, 0, 1, 1, 0, + 1, 1, 0, 0, 0, 0, 0, 0, 2, 3, 3, 2, 2, 2, 1, 1, + 0, -1, -2, -3, -5, -5, -5, -4, 1, 1, 0, -1, 0, 1, 3, 3, + -9, -6, -2, 0, 1, 1, 2, 2, -6, -2, 1, 2, 1, 1, 0, 1, + -2, 1, 2, 2, 1, 1, 1, 1, 0, 2, 2, 1, 0, 1, 1, 1, + 1, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, -1, -3, -2, 0, + -3, -3, -3, -2, -1, 3, 7, 9, 1, 2, 2, 2, 0, -2, -4, -3, + 2, 0, -2, -1, 3, 4, -1, -6, 1, 0, -2, -3, -1, 3, 3, 0, + 0, 3, 3, 0, -2, -1, 1, 1, -6, -1, 3, 2, -1, -2, 0, 1, + 5, 3, 0, -2, -3, 0, 2, 1, 1, 1, 2, 2, 0, -2, -4, -7, + -3, -2, 1, 2, 2, 1, -1, -4, 2, 2, 0, -2, -2, 0, 2, 2, + 0, 0, -2, -3, -2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, + -2, -1, 0, 1, 0, 1, 2, 3, -4, -2, 0, 0, -1, 0, 2, 3, + -2, -2, -2, -1, -1, 0, 2, 4, 0, 0, 0, 0, -1, -1, 0, 1, + 0, -1, -1, -1, -1, -1, 0, 0, 6, 4, 2, 0, -1, -2, -1, -1, + 0, 1, 1, 1, 1, -1, -5,-10, 1, 1, 1, 1, 1, 1, 0, -4, + 1, 0, 1, 1, 1, 1, 1, -1, 2, 1, 1, 1, 0, 0, 0, 0, + -3, 1, 4, 3, 3, 1, -1, 0, -4, 0, 1, 0, -1, 0, 0, 0, + -5, 0, 2, 1, 1, 1, 0, -1, -1, 2, 1, -2, -2, -1, 0, -1, + 2, 4, 5, 3, 0, -1, 1, 2, 0, 0, 1, 0, -2, -2, -1, -1, + -2, -2, -2, -2, -3, -2, -1, 0, 0, 0, 1, 0, 0, 0, 1, 2, + 0, -2, -2, -3, -1, 2, 2, -1, 1, 0, 0, 0, 1, 5, 3, -2, + -1, -1, 0, -1, 0, 2, 0, -5, -1, 0, 1, 0, 0, 2, 2, -2, + 3, 1, -1, -1, 0, 1, 1, 2, 1, 0, 0, 1, 1, 1, 1, 1, + -10, -8, -2, 1, 2, 1, 1, 1, -1, 1, 2, 1, 0, 0, 0, 0, + -1, -1, 0, 1, 2, 2, 2, 1, -1, -1, -1, 0, -1, -3, -5, -4, + 1, 1, 2, 1, 1, 0, 0, 2, -1, -2, -1, -1, -1, 0, 2, 4, + -3, -7, -5, 0, 2, 0, 0, 0, 3, -1, -2, 1, 2, 1, 1, 2, + 1, -2, -1, 1, 2, 1, 0, 1, 0, -1, 0, 3, 2, -1, -1, -1, + 2, 1, 1, 0, 0, 0, 0, 0, -9, -7, -2, 3, 3, 2, 1, 1, + 3, 2, 0, -2, -2, -1, 1, 1, 0, -1, 0, 0, 1, 1, 0, 0, + -2, -1, 1, 1, 1, 0, 0, 0, 1, 2, 1, -2, -4, -3, 1, 2, + 1, 2, 1, -2, -3, 0, 3, 1, -1, -1, 0, 0, 1, 3, 0, -4, + 2, 0, -1, 1, 2, -2, -2, 3, 2, 0, -1, 2, 3, -2, -4, 1, + 0, 1, 1, 1, 2, -2, -6, -2, -1, 0, 0, 0, 2, 0, -2, -1, + -1, -1, 1, 2, 1, -2, -3, -2, 3, -1, -2, -1, -1, 0, 1, 2, + 10, 4, 0, 0, -1, -2, -2, -1, 3, -1, -2, -1, 0, -1, -1, 0, + -5, 2, 7, 1, -4, -2, 1, 0, -2, 2, 3, -1, -3, 0, 2, 0, + 2, 1, 0, 0, 1, 1, -1, -2, 1, -2, -2, -1, -1, -2, 0, 0, + 0, 3, -2, -7, -1, 3, 0, 0, 1, 3, -3, -5, 2, 3, -1, 0, + 0, 2, -2, -2, 4, 2, -2, 0, -1, 1, -1, 0, 2, -1, -2, 1, + 4, 0, -3, -4, -2, 1, 2, 1, 0, 0, 3, 5, 3, 1, -1, -2, + 1, 1, 1, -1, -3, -1, 1, 1, 1, -1, -2, -2, 0, 0, -1, -2 }; -static const uint32 s_svq1IntraCodebook8x8[1536] = { - 0x02030404, 0xff000102, 0x02030304, 0xffff0001, - 0x02020303, 0xfeff0001, 0x01020203, 0xfdfeff00, - 0x00010202, 0xfdfeffff, 0x00000102, 0xfcfdfeff, - 0xff000001, 0xfcfcfdfe, 0xfeff0000, 0xfcfcfdfe, - 0x03030302, 0x03030303, 0x02020202, 0x03030202, - 0x02020201, 0x02020202, 0x01010100, 0x01010101, - 0x000000ff, 0x01010000, 0xfffffefe, 0xffffffff, - 0xfdfdfdfd, 0xfefefdfd, 0xfcfcfcfb, 0xfdfcfcfc, - 0x00fffefc, 0x03020201, 0x00fffefc, 0x03030201, - 0x00fffdfc, 0x03030201, 0x00fffdfc, 0x03030201, - 0x00fffdfb, 0x03030201, 0x00fffdfb, 0x03030201, - 0x00fffdfb, 0x03020101, 0xfffefdfb, 0x03020100, - 0x05050404, 0x07070606, 0x03020202, 0x04040403, - 0x00000000, 0x02010101, 0xfefefefe, 0x00ffffff, - 0xfefefefe, 0xfefefefe, 0xfefefefe, 0xfefefefe, - 0xfefefeff, 0xfefefefe, 0xffffffff, 0xfefefefe, - 0xff010305, 0xfdfdfdfe, 0xff010305, 0xfdfdfdfe, - 0xff010305, 0xfdfdfdfe, 0xff010305, 0xfdfdfdfe, - 0x00010405, 0xfdfdfdfe, 0x00020406, 0xfdfdfefe, - 0x00020406, 0xfdfefeff, 0x01020406, 0xfefefeff, - 0x030301ff, 0xfafd0002, 0x040301ff, 0xfafd0003, - 0x040401ff, 0xfafd0103, 0x040301ff, 0xfafd0103, - 0x040301fe, 0xfafd0103, 0x040301fe, 0xf9fd0103, - 0x030301fe, 0xf9fd0002, 0x030200fe, 0xfafd0002, - 0x0608090a, 0x04040506, 0x03040506, 0x01020202, - 0xff000102, 0xfffefefe, 0xfcfdfeff, 0xfdfcfcfc, - 0xfcfcfdfe, 0xfdfcfcfb, 0xfdfdfefe, 0xfefefdfd, - 0xffffffff, 0x00ffffff, 0x01010101, 0x02010101, - 0x0201fffe, 0x08070504, 0x0100fefd, 0x08070503, - 0x00fffdfc, 0x07060402, 0xfffefcfb, 0x07050301, - 0xfefdfbfa, 0x06040200, 0xfefcfbfa, 0x050301ff, - 0xfdfbfaf9, 0x030200fe, 0xfcfbf9f8, 0x0201fffd, - 0x0507090b, 0xffff0103, 0x0306080a, 0xfefe0001, - 0x02050709, 0xfcfdfe00, 0x01030608, 0xfcfcfdff, - 0xff020406, 0xfbfbfcfd, 0xfe000305, 0xfafafbfc, - 0xfdff0103, 0xf9f9fafb, 0xfcfe0002, 0xf9f9fafa, - 0x07070605, 0x08080807, 0x05050403, 0x06060606, - 0x03020200, 0x05040404, 0x0100fffe, 0x03030202, - 0xfffefdfc, 0x02010100, 0xfdfcfbfa, 0x00fffefe, - 0xfafaf9f8, 0xfdfdfcfb, 0xf8f8f7f6, 0xfbfafaf9, - 0x01030506, 0xf8fafdff, 0x02040506, 0xf8fafdff, - 0x02040506, 0xf8fafd00, 0x02040506, 0xf8fafd00, - 0x02040606, 0xf8fafd00, 0x02040506, 0xf8fafd00, - 0x02040506, 0xf8fafd00, 0x02040506, 0xf8fbfdff, - 0x08090a0b, 0x04050607, 0x06070808, 0x02030405, - 0x04040506, 0x00010202, 0x01020303, 0xfeff0000, - 0xff000101, 0xfdfdfefe, 0xfdfeffff, 0xfbfbfcfc, - 0xfbfcfcfd, 0xf9f9fafa, 0xf9fafbfb, 0xf8f8f8f8, - 0xf5f4f3f2, 0xfcfaf9f7, 0xf7f6f5f4, 0xfffdfbf9, - 0xfaf9f7f6, 0x0200fefd, 0xfefcfaf8, 0x05040200, - 0x0200fdfb, 0x08070504, 0x040200fe, 0x0a090806, - 0x07050300, 0x0c0b0a08, 0x08070503, 0x0c0c0b0a, - 0xeeeeeded, 0xf2f1f0ef, 0xf3f2f1f1, 0xf7f6f5f4, - 0xf8f7f6f5, 0xfdfcfbfa, 0xfefdfbfa, 0x020100ff, - 0x030200ff, 0x06060504, 0x08070604, 0x0a0a0a09, - 0x0c0b0a09, 0x0e0e0e0d, 0x0f0e0e0c, 0x10101010, - 0x11131516, 0x05090b0e, 0x0e111314, 0x0104080b, - 0x0a0d0f11, 0xfc000306, 0x05080b0d, 0xf7fbfe01, - 0xff030609, 0xf3f5f9fc, 0xfafd0004, 0xeff1f4f7, - 0xf5f8fbfe, 0xeceef0f2, 0xf0f3f6f8, 0xeaebedef, - 0x12121211, 0x0e101011, 0x0f0f1010, 0x0b0c0d0e, - 0x0a0b0c0c, 0x05070809, 0x04060607, 0xff010203, - 0xfeff0001, 0xfafbfcfd, 0xf8f9fafb, 0xf4f5f6f7, - 0xf2f3f4f5, 0xeff0f0f1, 0xeeefeff0, 0xecececed, - 0x00000000, 0xfdfeffff, 0x00000001, 0xfdfeff00, - 0x00000101, 0xfefeffff, 0x00010101, 0xfeffff00, - 0x01010102, 0xfeffff00, 0x01010202, 0xfeff0000, - 0x01010202, 0xffff0001, 0x01010202, 0xfe000001, - 0x00ffff00, 0x03020100, 0x00ffff00, 0x02020101, - 0xffffffff, 0x02020100, 0xfffeffff, 0x02010100, - 0xfffefeff, 0x02010000, 0xfefefeff, 0x020100ff, - 0xfffeffff, 0x02010000, 0xffffffff, 0x02010100, - 0x02020203, 0x00000101, 0x02020203, 0x00000102, - 0x01020202, 0x00000101, 0x01010202, 0xff000001, - 0x00010101, 0xffff0000, 0xffff0000, 0xffffffff, - 0xfefefefe, 0xfefefefe, 0xfdfdfdfe, 0xfefefefe, - 0x00000205, 0x000000ff, 0xff000204, 0xff00ffff, - 0xffff0104, 0xfffffffe, 0xffff0104, 0xfffffffe, - 0xfeff0104, 0xfffffffe, 0xfeff0104, 0xfffffffe, - 0xffff0104, 0xffffffff, 0xff000204, 0xff000000, - 0x0100fffe, 0x01010101, 0x0100fffd, 0x01010101, - 0x0100fffd, 0x01010101, 0x0100fffd, 0x01010101, - 0x0100fefd, 0x01010202, 0x0100fefc, 0x02020202, - 0x01fffdfb, 0x02010201, 0x00fefdfb, 0x01010101, - 0x00010303, 0xfbfcfcfe, 0x00020303, 0xfcfdfeff, - 0x01010202, 0xfefeff00, 0x01010101, 0x00000001, - 0x01000000, 0x01010101, 0x00fffffe, 0x02020100, - 0xfffefefd, 0x03020100, 0xfffefdfd, 0x03020100, - 0xfdfdfdfd, 0xfefefefd, 0xfefefdfd, 0xfffffffe, - 0xfffefefe, 0xffffffff, 0x00ffffff, 0x00000000, - 0x00000000, 0x01010101, 0x01010000, 0x02020202, - 0x02010101, 0x03030302, 0x02020202, 0x03030303, - 0xfdfbf9f8, 0xff00fffe, 0x00fffdfc, 0x01010201, - 0x030201ff, 0x01020203, 0x03030201, 0x00010202, - 0x02030302, 0xff000001, 0x00010201, 0xffffffff, - 0xff000101, 0xfffefeff, 0x00000101, 0xff00ffff, - 0x00fefdfc, 0x03030201, 0x00fefdfc, 0x02020201, - 0xfffefdfd, 0x01010100, 0xfffefefe, 0x000000ff, - 0xffffff00, 0xffffffff, 0x00010102, 0xfeffff00, - 0x01030303, 0xfefeff00, 0x02040405, 0xfeff0001, - 0x00000000, 0x03030201, 0x0000ff00, 0x03030201, - 0x0000ff00, 0x02030201, 0x01000000, 0x02020201, - 0x01010102, 0x00010101, 0x01020202, 0xfeff0000, - 0x00000102, 0xfafbfdfe, 0xfdffff00, 0xf7f8fafb, - 0x020100fe, 0xfcff0102, 0x020200fe, 0xfcff0102, - 0x020200fe, 0xfdff0102, 0x020200fe, 0xfdff0102, - 0x0202fffe, 0xfdff0102, 0x0201fffe, 0xfdff0102, - 0x0201fffd, 0xfdff0102, 0x0201fffe, 0xfdff0102, - 0xff0101ff, 0x0400fdfd, 0xff0101ff, 0x0400fdfd, - 0x000101ff, 0x0400fdfd, 0x000201ff, 0x0500fdfd, - 0x00020100, 0x0400fcfd, 0x00020100, 0x0500fcfd, - 0x00020100, 0x0400fdfd, 0xff020100, 0x0400fefe, - 0x06050606, 0x05050505, 0x02020202, 0x02020202, - 0x00000000, 0x00000000, 0xffffffff, 0xfefefefe, - 0xfefefefe, 0xfefefefe, 0xfefefefe, 0xfefefefe, - 0xfffeffff, 0xffffffff, 0xffffffff, 0xffffffff, - 0x02020202, 0x02020202, 0x00010100, 0x00000000, - 0xfefefeff, 0xfffefefe, 0xfdfdfdfd, 0xfefdfdfd, - 0xfdfcfcfd, 0xfefefdfd, 0xfefefefe, 0x0000ffff, - 0x01010100, 0x03030202, 0x05040403, 0x06060605, - 0xfdfe0104, 0x0301fffd, 0xfcfe0104, 0x0301fffd, - 0xfcfe0105, 0x0401fffd, 0xfdfe0105, 0x0402fffd, - 0xfdfe0105, 0x0402fffd, 0xfcfd0004, 0x0402fffd, - 0xfdfd0004, 0x0301fffd, 0xfdfe0003, 0x0301fffe, - 0xfcfcfcfd, 0xfcfcfcfc, 0xffffffff, 0xfefeffff, - 0x02010102, 0x01010102, 0x04030303, 0x03030304, - 0x04030303, 0x03030404, 0x02010201, 0x02020202, - 0xfffefefe, 0x0000ffff, 0xfcfcfcfc, 0xfdfdfdfd, - 0xfdfdfeff, 0x00fffefe, 0xfefeff00, 0x0100fffe, - 0xffff0102, 0x0100ffff, 0xff000103, 0x010000ff, - 0xff000203, 0x01000000, 0xff000103, 0x01000000, - 0xff000103, 0x01000000, 0x00000102, 0x01000000, - 0x01000000, 0x04030201, 0x00ff0000, 0x03020000, - 0xffffff00, 0x0100ffff, 0xffffff00, 0x00ffffff, - 0xffff0000, 0xfffefeff, 0xff000001, 0xfffefeff, - 0x00010202, 0xffffffff, 0x01020303, 0x00ffff00, - 0x00010001, 0xfffeff00, 0x00000000, 0xfffeffff, - 0x0000ff00, 0xffffffff, 0x00ffffff, 0x00000000, - 0x00ffffff, 0x01010000, 0x00ffffff, 0x03020101, - 0x00fffefe, 0x04030201, 0x00fffefe, 0x05040201, - 0x0001fffd, 0x0100ff00, 0x000100fd, 0x0200ffff, - 0x000100fd, 0x0200ffff, 0x000201fe, 0x0200ffff, - 0x000201fe, 0x0200ffff, 0x000201fe, 0x0200ffff, - 0x000202ff, 0x0200ffff, 0x000101ff, 0x01ffffff, - 0x01fffefe, 0x01030403, 0x00fffefe, 0x00020302, - 0x00fffefe, 0xff010201, 0x00ffffff, 0xff010201, - 0x00ffffff, 0xfe000101, 0x00ffff00, 0xff000101, - 0x00ffff00, 0xff010101, 0x00ffff00, 0xff000100, - 0x0100fffe, 0x01010101, 0x0000fffe, 0x00000000, - 0x00fffffe, 0xfefeffff, 0xfffffffe, 0xfdfefeff, - 0x010100ff, 0xfefeff00, 0x03030201, 0x00000102, - 0x03030201, 0x00010203, 0x01010000, 0x00000101, - 0xffffff00, 0x00000000, 0x00000001, 0x01010000, - 0x01010101, 0x01010101, 0x02020101, 0x01010101, - 0x01010101, 0x01010101, 0x010000ff, 0x00000001, - 0xfffffefd, 0xff00ffff, 0xfdfcfbfb, 0xfffefefe, - 0x01010101, 0xff000102, 0x02010101, 0xff000101, - 0x01010101, 0xfe000101, 0x01010102, 0xfe000101, - 0x00000101, 0xfdff0000, 0x00000101, 0xfdfeff00, - 0x00000101, 0xfcfeffff, 0xff000001, 0xfcfdfefe, - 0x03050708, 0x01010102, 0x00000102, 0xfffeffff, - 0xfeffffff, 0xfffefefe, 0xffffffff, 0x00ffff00, - 0x01000000, 0x00000001, 0x01010000, 0x00000001, - 0x000000ff, 0xffff0000, 0xfffffefe, 0xfffefeff, - 0xfe000409, 0xfffffefe, 0xfeff0207, 0x0000fffe, - 0xfefe0004, 0x010100ff, 0xfefefe01, 0x010100ff, - 0xfffefeff, 0x01010100, 0x00fffeff, 0x00010101, - 0x0100ffff, 0xff000101, 0x0100ff00, 0xffff0000, - 0x01010100, 0x00000101, 0x02020201, 0x00000001, - 0x02020202, 0xffff0001, 0x00010101, 0xfefefeff, - 0xff000000, 0xfefefdfe, 0xfeffffff, 0x00fffefe, - 0xffffffff, 0x02010000, 0x00ffffff, 0x04030201, - 0x0000ffff, 0xfdfdfeff, 0x0000ffff, 0xffffff00, - 0x00fffefe, 0x01010101, 0x00fefefe, 0x03030201, - 0x00ffffff, 0x03030301, 0x00000001, 0x02020101, - 0x00010202, 0xffffff00, 0x00010203, 0xfdfdfeff, - 0xfeffffff, 0xfbfcfdfe, 0xff000000, 0xfcfdfdff, - 0x00010101, 0xfdfeff00, 0x01020202, 0xffff0001, - 0x02020202, 0xff000101, 0x02020202, 0x00000102, - 0x01020101, 0x00000101, 0x01010000, 0xff000000, - 0x010302fe, 0xff0101ff, 0x000302fd, 0xff0101ff, - 0x000302fd, 0xff0101ff, 0x000302fc, 0xfe0101ff, - 0x000301fc, 0xfe0101ff, 0xff0301fc, 0xfe0101fe, - 0x000201fd, 0xfe0101ff, 0x000201fd, 0xff0101ff, - 0xfeffffff, 0xfefefefe, 0x01010101, 0x00000000, - 0x02020201, 0x02020202, 0x01010000, 0x02020201, - 0xfffffefe, 0x000000ff, 0xfdfdfdfd, 0xfefdfdfd, - 0xffffffff, 0xfefefefe, 0x04040404, 0x02030304, - 0xfffefdfd, 0x05020100, 0xfefdfdfd, 0x060301ff, - 0xfefefdfd, 0x05030200, 0xfefefefd, 0x05030100, - 0xfffefefe, 0x050301ff, 0xfffffefe, 0x04020100, - 0xffffffff, 0x04010100, 0xffffffff, 0x03020100, - 0x0100ff00, 0xffff0001, 0x01000000, 0xffff0002, - 0x00ff0001, 0x00000001, 0xfffeff01, 0x00000000, - 0xfffdfe01, 0x01000000, 0xfefdff01, 0x02010100, - 0xfffeff01, 0x02010100, 0x00ff0002, 0x02020101, - 0x01010101, 0x02010000, 0xff0000ff, 0x01000000, - 0xfffffefd, 0x010100ff, 0x00fffefc, 0x01010100, - 0x0000fefd, 0x01010101, 0x0100fffd, 0x00000101, - 0x010100ff, 0xff000001, 0x02020100, 0xff000001, - 0xfdfcfcfc, 0xfffffffe, 0xfffefefe, 0x00000000, - 0x000000ff, 0x01010101, 0x01010000, 0x01010101, - 0x01010000, 0x00010202, 0x01010000, 0x00010101, - 0x01000000, 0x00010101, 0x010000ff, 0x00000101, - 0x02020201, 0xfcfeff01, 0x02020101, 0xfcfe0001, - 0x01010100, 0xfdff0001, 0x010100ff, 0xfeff0000, - 0x010100ff, 0xff000001, 0x0000fffe, 0xff000000, - 0x0100ffff, 0x00000001, 0x010100ff, 0x00010101, - 0xff000202, 0xfefffffe, 0xfeff0101, 0xfefffffe, - 0xfeff0101, 0xff0000fe, 0xfe000101, 0x000101ff, - 0xff000101, 0x010201ff, 0xff000101, 0x010201ff, - 0xff000101, 0x010101ff, 0xff000101, 0x01010100, - 0xfeff0000, 0xfcfcfcfc, 0x02030303, 0x00000001, - 0x03030303, 0x02020202, 0x00000000, 0x01000000, - 0xffffffff, 0x00ffffff, 0x0000ff00, 0x000000ff, - 0x00000000, 0x00000000, 0x00000000, 0x00ffff00, - 0xff00ffff, 0xff0201ff, 0x00000101, 0xff030200, - 0xff000101, 0xff0301ff, 0xfe000101, 0xfe0100fe, - 0xfe000001, 0xfd0100fe, 0x00000000, 0xfd0101ff, - 0x00010100, 0xfd010201, 0x010100ff, 0xfc010201, - 0x0100fdfc, 0x00000101, 0x0100fefc, 0xff000101, - 0x0101fffd, 0xffff0001, 0x010101ff, 0x00ff0001, - 0x01020201, 0x0000ff00, 0x00010202, 0x0100ffff, - 0xff000102, 0x0100fffe, 0xff000202, 0x0101fffe, - 0x00000101, 0xffffffff, 0xffffff00, 0xffffffff, - 0xffffffff, 0xffffffff, 0xffff0000, 0xffffffff, - 0xff000001, 0xffffffff, 0x00000102, 0xffffffff, - 0x01020305, 0x00000000, 0x02030506, 0x00000001, - 0x01030404, 0x01000000, 0x01020303, 0x01000000, - 0x00010202, 0x0100ffff, 0xff000000, 0x0100ffff, - 0xffff0000, 0x0200fffe, 0xfeffff00, 0x0100fefe, - 0xfeffff00, 0x00fffefe, 0xfeff0000, 0x00fffefe, - 0xffff0000, 0x030200ff, 0xfefeff00, 0x0201ffff, - 0xffff0001, 0x000000ff, 0x00010101, 0xffff0000, - 0x00010201, 0xffffff00, 0x000000ff, 0xffffffff, - 0xfffffefd, 0x02010100, 0x01fffdfc, 0x05050302, - 0x00000000, 0x00000000, 0x01010101, 0x01010101, - 0xff000000, 0x01000000, 0xfefeffff, 0x00fffffe, - 0x00000000, 0x01010100, 0x02020202, 0x03030302, - 0x01010101, 0x01010202, 0xfcfcfdfc, 0xfdfdfcfc, - 0x020100ff, 0x03030302, 0xffffffff, 0x00000000, - 0xfeff0000, 0xfefdfdfe, 0x00010203, 0xfefefeff, - 0x01020304, 0x00000001, 0x01010202, 0x01010100, - 0xffffff00, 0x010000ff, 0xfefefefe, 0x0000fffe, - 0x0200ff01, 0x01fffe01, 0x0200ff01, 0x01fefe01, - 0x0300ff01, 0x01fffe02, 0x0300fe00, 0x01fefe02, - 0x0300fe00, 0x01fefe02, 0x0300fe00, 0x01fffe01, - 0x0200fe00, 0x01fefe01, 0x0200ff00, 0x01fffe01, - 0x02020100, 0x02020303, 0x02010100, 0x01020303, - 0x02010000, 0x01020202, 0x010000ff, 0x01010101, - 0x0000ffff, 0x00000000, 0xffffffff, 0xffffffff, - 0xfefefefe, 0xfffefefe, 0xfefefefe, 0xfffefefe, - 0xfeff0000, 0x050300ff, 0xffff0000, 0x040200ff, - 0x00000101, 0x0201ffff, 0x01010201, 0x00ffff00, - 0x01020100, 0xfefeff00, 0x020100ff, 0xfdfd0001, - 0x0201fffe, 0xfcfe0002, 0x0200fffe, 0xfdff0102, - 0x00000000, 0xffff0000, 0x00ff0000, 0x00000000, - 0xffffffff, 0x00000000, 0xffffffff, 0x00ffffff, - 0xffffffff, 0x00ffffff, 0x000000ff, 0x00ffff00, - 0x01010000, 0x01000000, 0x04030303, 0x03030303, - 0xfefe0105, 0xff000000, 0xfffdff04, 0xff000000, - 0x00ffff03, 0xff000101, 0x01000002, 0xfe000101, - 0x01000001, 0xfe000101, 0xffffff00, 0xff000000, - 0xffffff00, 0xff0000ff, 0x00000102, 0x00000100, - 0x01010001, 0x00000101, 0x01000001, 0x00000001, - 0x00ffff01, 0x00000000, 0xffff0002, 0x00ffffff, - 0xffff0103, 0xfffefefe, 0x00010204, 0xfffefeff, - 0x00000102, 0x0000ffff, 0xffffff00, 0x010100ff, - 0x02020100, 0xfdff0102, 0x01010000, 0xfeff0001, - 0x00000000, 0xffff0000, 0x00ff0000, 0x00010100, - 0x00ff0000, 0x01010101, 0x00000000, 0x00010101, - 0x01010000, 0xfdff0102, 0x01000000, 0xfbfcff01, - 0xfffefefe, 0x02020200, 0x00000000, 0x00010101, - 0x01010101, 0xfdfe0001, 0x01010000, 0xfcfdff00, - 0x0100ffff, 0xfdfe0000, 0x0100ffff, 0xff000101, - 0x01000000, 0x00010101, 0x01010101, 0x00000000, - 0x00000100, 0x02010101, 0x00000201, 0x01ff0000, - 0xff000200, 0x00ff0001, 0x00000100, 0x01000102, - 0x00ff0100, 0x01000202, 0xffff00ff, 0x02010102, - 0xfefdfefe, 0x01010100, 0xfdfdfefe, 0x00ffffff, - 0x0100fffd, 0x00010102, 0x0100fffd, 0x01010102, - 0x010000fe, 0x01010101, 0x000000ff, 0x00000000, - 0x000000fe, 0x00ffff00, 0x000000fe, 0xffff0000, - 0x010100fd, 0x01000101, 0x0100fefb, 0x02010202, - 0x00fffffe, 0x03020100, 0x01010000, 0x02010000, - 0x00010000, 0x0100ffff, 0xffffffff, 0x00fffefe, - 0xfefefefe, 0x0100fffe, 0xff000000, 0x02020100, - 0x00000102, 0x02020100, 0xff000102, 0x0000ffff, - 0x01010100, 0xfcff0101, 0x0100ffff, 0xfd000101, - 0x0000fffe, 0xfe020201, 0x000000ff, 0xff030200, - 0x000000ff, 0x00020100, 0xfeff0000, 0x000101ff, - 0xfeff0000, 0x010200fe, 0xfeff0000, 0x020201ff, - 0x00000001, 0xfdfefdfe, 0x00010000, 0xfffffefe, - 0x0101ff00, 0x0000ffff, 0x0101ff00, 0x0000ffff, - 0x01020100, 0x0100ffff, 0x02030201, 0x02010000, - 0x010200ff, 0x03020000, 0x0000fffe, 0x020100ff, - 0xff000101, 0x01fffefe, 0xff010101, 0x0200fefe, - 0xff010101, 0x0200ffff, 0x00000000, 0x02010000, - 0x00ffffff, 0x02010000, 0x01fffeff, 0x00000101, - 0x01fffeff, 0xff000202, 0x02fffeff, 0xff000202, - 0xfeffffff, 0x0100ffff, 0xffff0000, 0x020100ff, - 0x00000001, 0x02010100, 0x00000101, 0x01010101, - 0x01000101, 0x01010001, 0x01010101, 0xffffff00, - 0x00010201, 0xfdfefeff, 0x00010202, 0xfcfcfdfe, - 0x0101fefc, 0x00000101, 0x000100fe, 0x00000000, - 0xfe010100, 0x0100fffe, 0xfe010202, 0x0201fffe, - 0xfe010201, 0x0201fffe, 0xff0101ff, 0x0100ffff, - 0x010100fe, 0x00ffff00, 0x020200fe, 0x00ffff01, - 0x00000101, 0x00000100, 0xfefdfdfe, 0x0000fffe, - 0xfefdfcfd, 0x000000ff, 0x0100ffff, 0x01020302, - 0x03020100, 0x01020303, 0x02010101, 0xff000001, - 0x00000000, 0xffffffff, 0x00ffff00, 0x00000000, - 0x00000101, 0x0200ffff, 0x00010000, 0x0101ffff, - 0x0100fffe, 0x01010101, 0x0200fdfd, 0x00010102, - 0x0100fefe, 0x00000101, 0x00000001, 0xffff0000, - 0xfdff0103, 0x0100fffe, 0xfdff0204, 0x0201fffd, - 0xff000000, 0xffffffff, 0x00010201, 0xffff0000, - 0x02030302, 0xffff0001, 0x02040403, 0xfeff0001, - 0x01020303, 0xfefeff00, 0xff000101, 0xfdfefeff, - 0xff000000, 0xfefefeff, 0xffffffff, 0xfffefeff, - 0x02020201, 0x02020102, 0x01010100, 0x00000001, - 0x01000000, 0xfeff0001, 0x00000000, 0xfcff0001, - 0x00000001, 0xfbfe0000, 0x00000001, 0xfcff0000, - 0x00ff0001, 0xfdff0000, 0x00ffff00, 0xff010101, - 0x0000fffe, 0xfeffffff, 0x000000ff, 0xfefeffff, - 0x00010100, 0xfeffffff, 0x00010100, 0xffff0000, - 0x00000100, 0x00010101, 0x00000101, 0x01020201, - 0x00000101, 0x01020201, 0xff000101, 0x00010100, - 0x00010204, 0x01010100, 0x00010204, 0x01000000, - 0x00000103, 0x00ffffff, 0xff000001, 0x00fffeff, - 0x00000000, 0x00ffffff, 0x0000ffff, 0x0100ffff, - 0xff00fffe, 0x010000ff, 0xfefffefe, 0x010000ff, - 0x01010100, 0xff000102, 0x00ffffff, 0xfefeff00, - 0x00ff00ff, 0xfffeff00, 0x00000000, 0x02010000, - 0x00000000, 0x03020000, 0xffff00ff, 0x0300ffff, - 0xff0000ff, 0x0300feff, 0x00000000, 0x0401ffff, - 0x00000202, 0x01000000, 0xfeff0101, 0x01fffeff, - 0xfefeffff, 0x00fefdfe, 0xffff00ff, 0x01fffeff, - 0x00000101, 0x01000001, 0x00000202, 0x01000001, - 0x00000202, 0xffff0000, 0x00000202, 0xffff0001, - 0x010100ff, 0xffffff00, 0x02030201, 0x00000001, - 0x01010100, 0x0000ff00, 0x00fffefe, 0x00000001, - 0x02fffefe, 0x00010202, 0x0100fffe, 0xff000001, - 0x0000ffff, 0xfefffeff, 0x01010100, 0x01010000, - 0xfefdfdfd, 0xfefeffff, 0x0100ffff, 0x00000102, - 0x02010101, 0x00000102, 0x01010101, 0x01ff0001, - 0xffff0001, 0x01ff0000, 0xffffff00, 0x01ffff00, - 0x00ff0001, 0x0200ff00, 0x00ff0002, 0x02000000, - 0xfffe0001, 0x00010100, 0xffff0002, 0x00010100, - 0xfffe0001, 0xff000100, 0xffff0001, 0xff000100, - 0x00010100, 0x00000101, 0x010201fe, 0x01000000, - 0x010200fb, 0x0100ff00, 0x0102fffa, 0x0000ff00, - 0xff000305, 0xfffffffe, 0xff000101, 0xffff00ff, - 0x010100ff, 0x00010202, 0x0100fffe, 0x01010102, - 0xfffffffe, 0x0100ff00, 0x00000100, 0x0000ffff, - 0x01010100, 0x00000001, 0x0100fefd, 0xff000001, - 0x000100ff, 0x030200ff, 0xfe0000ff, 0x00fffefc, - 0x00010100, 0xff00fffe, 0x01030201, 0x00010100, - 0x010100ff, 0x00010101, 0x00fefdfe, 0x00010000, - 0x00fefeff, 0xff000001, 0x00000103, 0xffff0001, - 0x0000fffe, 0x0000ffff, 0x000000ff, 0x01010100, - 0x00ffffff, 0x01010101, 0xfffdfe00, 0x00000001, - 0xfffdff01, 0xff000101, 0x01ff0103, 0xff000202, - 0x01000103, 0x00010102, 0xfffefe00, 0x000000ff, - 0xffff0001, 0x00010201, 0xfffeff00, 0x01020201, - 0x00ffffff, 0x00020100, 0x000000fe, 0xff010000, - 0x000100ff, 0xffffffff, 0x02010100, 0x00fffe00, - 0x02020201, 0x00ffff01, 0x01010100, 0xfffefe00, - 0xffff0000, 0xfefeffff, 0x00ff0000, 0x01020201, - 0xffff0000, 0x02020100, 0xfeff0101, 0xffffffff, - 0x00010202, 0xfefeff00, 0x01020201, 0xfefe0000, - 0x00000000, 0xff000101, 0xffffff00, 0x01020302, - 0x0201fe00, 0x010000ff, 0x0302feff, 0x000000ff, - 0x0302fe00, 0x0000ffff, 0x0203ff00, 0x000100fe, - 0x0103ff00, 0x000100fe, 0x0102ff00, 0xff0001ff, - 0xff010000, 0xff0000fe, 0xfe000001, 0xfffffffe, - 0x01010101, 0xfeffff01, 0x01000000, 0x01010101, - 0x01000000, 0x03020101, 0xff000001, 0x02010000, - 0xfeffff00, 0x020100ff, 0xfefefefe, 0x010100ff, - 0xffffffff, 0xff000000, 0x00020202, 0xfcfeffff, - 0xfffffeff, 0x03020100, 0xffffffff, 0x03020100, - 0x00ff0001, 0x020100ff, 0x00000001, 0x020200ff, - 0xffff0001, 0x020100fe, 0xfefefe00, 0x0100fffd, - 0xfefefe00, 0x0101fffe, 0x00000000, 0x02020100 +static const int8 s_svq1IntraCodebook8x8[6144] = { + 4, 4, 3, 2, 2, 1, 0, -1, 4, 3, 3, 2, 1, 0, -1, -1, + 3, 3, 2, 2, 1, 0, -1, -2, 3, 2, 2, 1, 0, -1, -2, -3, + 2, 2, 1, 0, -1, -1, -2, -3, 2, 1, 0, 0, -1, -2, -3, -4, + 1, 0, 0, -1, -2, -3, -4, -4, 0, 0, -1, -2, -2, -3, -4, -4, + 2, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 3, 3, + 1, 2, 2, 2, 2, 2, 2, 2, 0, 1, 1, 1, 1, 1, 1, 1, + -1, 0, 0, 0, 0, 0, 1, 1, -2, -2, -1, -1, -1, -1, -1, -1, + -3, -3, -3, -3, -3, -3, -2, -2, -5, -4, -4, -4, -4, -4, -4, -3, + -4, -2, -1, 0, 1, 2, 2, 3, -4, -2, -1, 0, 1, 2, 3, 3, + -4, -3, -1, 0, 1, 2, 3, 3, -4, -3, -1, 0, 1, 2, 3, 3, + -5, -3, -1, 0, 1, 2, 3, 3, -5, -3, -1, 0, 1, 2, 3, 3, + -5, -3, -1, 0, 1, 1, 2, 3, -5, -3, -2, -1, 0, 1, 2, 3, + 4, 4, 5, 5, 6, 6, 7, 7, 2, 2, 2, 3, 3, 4, 4, 4, + 0, 0, 0, 0, 1, 1, 1, 2, -2, -2, -2, -2, -1, -1, -1, 0, + -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, + -1, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -2, -2, -2, -2, + 5, 3, 1, -1, -2, -3, -3, -3, 5, 3, 1, -1, -2, -3, -3, -3, + 5, 3, 1, -1, -2, -3, -3, -3, 5, 3, 1, -1, -2, -3, -3, -3, + 5, 4, 1, 0, -2, -3, -3, -3, 6, 4, 2, 0, -2, -2, -3, -3, + 6, 4, 2, 0, -1, -2, -2, -3, 6, 4, 2, 1, -1, -2, -2, -2, + -1, 1, 3, 3, 2, 0, -3, -6, -1, 1, 3, 4, 3, 0, -3, -6, + -1, 1, 4, 4, 3, 1, -3, -6, -1, 1, 3, 4, 3, 1, -3, -6, + -2, 1, 3, 4, 3, 1, -3, -6, -2, 1, 3, 4, 3, 1, -3, -7, + -2, 1, 3, 3, 2, 0, -3, -7, -2, 0, 2, 3, 2, 0, -3, -6, + 10, 9, 8, 6, 6, 5, 4, 4, 6, 5, 4, 3, 2, 2, 2, 1, + 2, 1, 0, -1, -2, -2, -2, -1, -1, -2, -3, -4, -4, -4, -4, -3, + -2, -3, -4, -4, -5, -4, -4, -3, -2, -2, -3, -3, -3, -3, -2, -2, + -1, -1, -1, -1, -1, -1, -1, 0, 1, 1, 1, 1, 1, 1, 1, 2, + -2, -1, 1, 2, 4, 5, 7, 8, -3, -2, 0, 1, 3, 5, 7, 8, + -4, -3, -1, 0, 2, 4, 6, 7, -5, -4, -2, -1, 1, 3, 5, 7, + -6, -5, -3, -2, 0, 2, 4, 6, -6, -5, -4, -2, -1, 1, 3, 5, + -7, -6, -5, -3, -2, 0, 2, 3, -8, -7, -5, -4, -3, -1, 1, 2, + 11, 9, 7, 5, 3, 1, -1, -1, 10, 8, 6, 3, 1, 0, -2, -2, + 9, 7, 5, 2, 0, -2, -3, -4, 8, 6, 3, 1, -1, -3, -4, -4, + 6, 4, 2, -1, -3, -4, -5, -5, 5, 3, 0, -2, -4, -5, -6, -6, + 3, 1, -1, -3, -5, -6, -7, -7, 2, 0, -2, -4, -6, -6, -7, -7, + 5, 6, 7, 7, 7, 8, 8, 8, 3, 4, 5, 5, 6, 6, 6, 6, + 0, 2, 2, 3, 4, 4, 4, 5, -2, -1, 0, 1, 2, 2, 3, 3, + -4, -3, -2, -1, 0, 1, 1, 2, -6, -5, -4, -3, -2, -2, -1, 0, + -8, -7, -6, -6, -5, -4, -3, -3,-10, -9, -8, -8, -7, -6, -6, -5, + 6, 5, 3, 1, -1, -3, -6, -8, 6, 5, 4, 2, -1, -3, -6, -8, + 6, 5, 4, 2, 0, -3, -6, -8, 6, 5, 4, 2, 0, -3, -6, -8, + 6, 6, 4, 2, 0, -3, -6, -8, 6, 5, 4, 2, 0, -3, -6, -8, + 6, 5, 4, 2, 0, -3, -6, -8, 6, 5, 4, 2, -1, -3, -5, -8, + 11, 10, 9, 8, 7, 6, 5, 4, 8, 8, 7, 6, 5, 4, 3, 2, + 6, 5, 4, 4, 2, 2, 1, 0, 3, 3, 2, 1, 0, 0, -1, -2, + 1, 1, 0, -1, -2, -2, -3, -3, -1, -1, -2, -3, -4, -4, -5, -5, + -3, -4, -4, -5, -6, -6, -7, -7, -5, -5, -6, -7, -8, -8, -8, -8, + -14,-13,-12,-11, -9, -7, -6, -4,-12,-11,-10, -9, -7, -5, -3, -1, + -10, -9, -7, -6, -3, -2, 0, 2, -8, -6, -4, -2, 0, 2, 4, 5, + -5, -3, 0, 2, 4, 5, 7, 8, -2, 0, 2, 4, 6, 8, 9, 10, + 0, 3, 5, 7, 8, 10, 11, 12, 3, 5, 7, 8, 10, 11, 12, 12, + -19,-19,-18,-18,-17,-16,-15,-14,-15,-15,-14,-13,-12,-11,-10, -9, + -11,-10, -9, -8, -6, -5, -4, -3, -6, -5, -3, -2, -1, 0, 1, 2, + -1, 0, 2, 3, 4, 5, 6, 6, 4, 6, 7, 8, 9, 10, 10, 10, + 9, 10, 11, 12, 13, 14, 14, 14, 12, 14, 14, 15, 16, 16, 16, 16, + 22, 21, 19, 17, 14, 11, 9, 5, 20, 19, 17, 14, 11, 8, 4, 1, + 17, 15, 13, 10, 6, 3, 0, -4, 13, 11, 8, 5, 1, -2, -5, -9, + 9, 6, 3, -1, -4, -7,-11,-13, 4, 0, -3, -6, -9,-12,-15,-17, + -2, -5, -8,-11,-14,-16,-18,-20, -8,-10,-13,-16,-17,-19,-21,-22, + 17, 18, 18, 18, 17, 16, 16, 14, 16, 16, 15, 15, 14, 13, 12, 11, + 12, 12, 11, 10, 9, 8, 7, 5, 7, 6, 6, 4, 3, 2, 1, -1, + 1, 0, -1, -2, -3, -4, -5, -6, -5, -6, -7, -8, -9,-10,-11,-12, + -11,-12,-13,-14,-15,-16,-16,-17,-16,-17,-17,-18,-19,-20,-20,-20, + 0, 0, 0, 0, -1, -1, -2, -3, 1, 0, 0, 0, 0, -1, -2, -3, + 1, 1, 0, 0, -1, -1, -2, -2, 1, 1, 1, 0, 0, -1, -1, -2, + 2, 1, 1, 1, 0, -1, -1, -2, 2, 2, 1, 1, 0, 0, -1, -2, + 2, 2, 1, 1, 1, 0, -1, -1, 2, 2, 1, 1, 1, 0, 0, -2, + 0, -1, -1, 0, 0, 1, 2, 3, 0, -1, -1, 0, 1, 1, 2, 2, + -1, -1, -1, -1, 0, 1, 2, 2, -1, -1, -2, -1, 0, 1, 1, 2, + -1, -2, -2, -1, 0, 0, 1, 2, -1, -2, -2, -2, -1, 0, 1, 2, + -1, -1, -2, -1, 0, 0, 1, 2, -1, -1, -1, -1, 0, 1, 1, 2, + 3, 2, 2, 2, 1, 1, 0, 0, 3, 2, 2, 2, 2, 1, 0, 0, + 2, 2, 2, 1, 1, 1, 0, 0, 2, 2, 1, 1, 1, 0, 0, -1, + 1, 1, 1, 0, 0, 0, -1, -1, 0, 0, -1, -1, -1, -1, -1, -1, + -2, -2, -2, -2, -2, -2, -2, -2, -2, -3, -3, -3, -2, -2, -2, -2, + 5, 2, 0, 0, -1, 0, 0, 0, 4, 2, 0, -1, -1, -1, 0, -1, + 4, 1, -1, -1, -2, -1, -1, -1, 4, 1, -1, -1, -2, -1, -1, -1, + 4, 1, -1, -2, -2, -1, -1, -1, 4, 1, -1, -2, -2, -1, -1, -1, + 4, 1, -1, -1, -1, -1, -1, -1, 4, 2, 0, -1, 0, 0, 0, -1, + -2, -1, 0, 1, 1, 1, 1, 1, -3, -1, 0, 1, 1, 1, 1, 1, + -3, -1, 0, 1, 1, 1, 1, 1, -3, -1, 0, 1, 1, 1, 1, 1, + -3, -2, 0, 1, 2, 2, 1, 1, -4, -2, 0, 1, 2, 2, 2, 2, + -5, -3, -1, 1, 1, 2, 1, 2, -5, -3, -2, 0, 1, 1, 1, 1, + 3, 3, 1, 0, -2, -4, -4, -5, 3, 3, 2, 0, -1, -2, -3, -4, + 2, 2, 1, 1, 0, -1, -2, -2, 1, 1, 1, 1, 1, 0, 0, 0, + 0, 0, 0, 1, 1, 1, 1, 1, -2, -1, -1, 0, 0, 1, 2, 2, + -3, -2, -2, -1, 0, 1, 2, 3, -3, -3, -2, -1, 0, 1, 2, 3, + -3, -3, -3, -3, -3, -2, -2, -2, -3, -3, -2, -2, -2, -1, -1, -1, + -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 2, 2, 2, 2, + 1, 1, 1, 2, 2, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, + -8, -7, -5, -3, -2, -1, 0, -1, -4, -3, -1, 0, 1, 2, 1, 1, + -1, 1, 2, 3, 3, 2, 2, 1, 1, 2, 3, 3, 2, 2, 1, 0, + 2, 3, 3, 2, 1, 0, 0, -1, 1, 2, 1, 0, -1, -1, -1, -1, + 1, 1, 0, -1, -1, -2, -2, -1, 1, 1, 0, 0, -1, -1, 0, -1, + -4, -3, -2, 0, 1, 2, 3, 3, -4, -3, -2, 0, 1, 2, 2, 2, + -3, -3, -2, -1, 0, 1, 1, 1, -2, -2, -2, -1, -1, 0, 0, 0, + 0, -1, -1, -1, -1, -1, -1, -1, 2, 1, 1, 0, 0, -1, -1, -2, + 3, 3, 3, 1, 0, -1, -2, -2, 5, 4, 4, 2, 1, 0, -1, -2, + 0, 0, 0, 0, 1, 2, 3, 3, 0, -1, 0, 0, 1, 2, 3, 3, + 0, -1, 0, 0, 1, 2, 3, 2, 0, 0, 0, 1, 1, 2, 2, 2, + 2, 1, 1, 1, 1, 1, 1, 0, 2, 2, 2, 1, 0, 0, -1, -2, + 2, 1, 0, 0, -2, -3, -5, -6, 0, -1, -1, -3, -5, -6, -8, -9, + -2, 0, 1, 2, 2, 1, -1, -4, -2, 0, 2, 2, 2, 1, -1, -4, + -2, 0, 2, 2, 2, 1, -1, -3, -2, 0, 2, 2, 2, 1, -1, -3, + -2, -1, 2, 2, 2, 1, -1, -3, -2, -1, 1, 2, 2, 1, -1, -3, + -3, -1, 1, 2, 2, 1, -1, -3, -2, -1, 1, 2, 2, 1, -1, -3, + -1, 1, 1, -1, -3, -3, 0, 4, -1, 1, 1, -1, -3, -3, 0, 4, + -1, 1, 1, 0, -3, -3, 0, 4, -1, 1, 2, 0, -3, -3, 0, 5, + 0, 1, 2, 0, -3, -4, 0, 4, 0, 1, 2, 0, -3, -4, 0, 5, + 0, 1, 2, 0, -3, -3, 0, 4, 0, 1, 2, -1, -2, -2, 0, 4, + 6, 6, 5, 6, 5, 5, 5, 5, 2, 2, 2, 2, 2, 2, 2, 2, + 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -2, -2, -2, -2, + -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, + -1, -1, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 1, 0, 0, 0, 0, 0, + -1, -2, -2, -2, -2, -2, -2, -1, -3, -3, -3, -3, -3, -3, -3, -2, + -3, -4, -4, -3, -3, -3, -2, -2, -2, -2, -2, -2, -1, -1, 0, 0, + 0, 1, 1, 1, 2, 2, 3, 3, 3, 4, 4, 5, 5, 6, 6, 6, + 4, 1, -2, -3, -3, -1, 1, 3, 4, 1, -2, -4, -3, -1, 1, 3, + 5, 1, -2, -4, -3, -1, 1, 4, 5, 1, -2, -3, -3, -1, 2, 4, + 5, 1, -2, -3, -3, -1, 2, 4, 4, 0, -3, -4, -3, -1, 2, 4, + 4, 0, -3, -3, -3, -1, 1, 3, 3, 0, -2, -3, -2, -1, 1, 3, + -3, -4, -4, -4, -4, -4, -4, -4, -1, -1, -1, -1, -1, -1, -2, -2, + 2, 1, 1, 2, 2, 1, 1, 1, 3, 3, 3, 4, 4, 3, 3, 3, + 3, 3, 3, 4, 4, 4, 3, 3, 1, 2, 1, 2, 2, 2, 2, 2, + -2, -2, -2, -1, -1, -1, 0, 0, -4, -4, -4, -4, -3, -3, -3, -3, + -1, -2, -3, -3, -2, -2, -1, 0, 0, -1, -2, -2, -2, -1, 0, 1, + 2, 1, -1, -1, -1, -1, 0, 1, 3, 1, 0, -1, -1, 0, 0, 1, + 3, 2, 0, -1, 0, 0, 0, 1, 3, 1, 0, -1, 0, 0, 0, 1, + 3, 1, 0, -1, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 1, 1, 2, 3, 4, 0, 0, -1, 0, 0, 0, 2, 3, + 0, -1, -1, -1, -1, -1, 0, 1, 0, -1, -1, -1, -1, -1, -1, 0, + 0, 0, -1, -1, -1, -2, -2, -1, 1, 0, 0, -1, -1, -2, -2, -1, + 2, 2, 1, 0, -1, -1, -1, -1, 3, 3, 2, 1, 0, -1, -1, 0, + 1, 0, 1, 0, 0, -1, -2, -1, 0, 0, 0, 0, -1, -1, -2, -1, + 0, -1, 0, 0, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, + -1, -1, -1, 0, 0, 0, 1, 1, -1, -1, -1, 0, 1, 1, 2, 3, + -2, -2, -1, 0, 1, 2, 3, 4, -2, -2, -1, 0, 1, 2, 4, 5, + -3, -1, 1, 0, 0, -1, 0, 1, -3, 0, 1, 0, -1, -1, 0, 2, + -3, 0, 1, 0, -1, -1, 0, 2, -2, 1, 2, 0, -1, -1, 0, 2, + -2, 1, 2, 0, -1, -1, 0, 2, -2, 1, 2, 0, -1, -1, 0, 2, + -1, 2, 2, 0, -1, -1, 0, 2, -1, 1, 1, 0, -1, -1, -1, 1, + -2, -2, -1, 1, 3, 4, 3, 1, -2, -2, -1, 0, 2, 3, 2, 0, + -2, -2, -1, 0, 1, 2, 1, -1, -1, -1, -1, 0, 1, 2, 1, -1, + -1, -1, -1, 0, 1, 1, 0, -2, 0, -1, -1, 0, 1, 1, 0, -1, + 0, -1, -1, 0, 1, 1, 1, -1, 0, -1, -1, 0, 0, 1, 0, -1, + -2, -1, 0, 1, 1, 1, 1, 1, -2, -1, 0, 0, 0, 0, 0, 0, + -2, -1, -1, 0, -1, -1, -2, -2, -2, -1, -1, -1, -1, -2, -2, -3, + -1, 0, 1, 1, 0, -1, -2, -2, 1, 2, 3, 3, 2, 1, 0, 0, + 1, 2, 3, 3, 3, 2, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, + 0, -1, -1, -1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, -1, 0, 0, 1, 1, 0, 0, 0, + -3, -2, -1, -1, -1, -1, 0, -1, -5, -5, -4, -3, -2, -2, -2, -1, + 1, 1, 1, 1, 2, 1, 0, -1, 1, 1, 1, 2, 1, 1, 0, -1, + 1, 1, 1, 1, 1, 1, 0, -2, 2, 1, 1, 1, 1, 1, 0, -2, + 1, 1, 0, 0, 0, 0, -1, -3, 1, 1, 0, 0, 0, -1, -2, -3, + 1, 1, 0, 0, -1, -1, -2, -4, 1, 0, 0, -1, -2, -2, -3, -4, + 8, 7, 5, 3, 2, 1, 1, 1, 2, 1, 0, 0, -1, -1, -2, -1, + -1, -1, -1, -2, -2, -2, -2, -1, -1, -1, -1, -1, 0, -1, -1, 0, + 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, + -1, 0, 0, 0, 0, 0, -1, -1, -2, -2, -1, -1, -1, -2, -2, -1, + 9, 4, 0, -2, -2, -2, -1, -1, 7, 2, -1, -2, -2, -1, 0, 0, + 4, 0, -2, -2, -1, 0, 1, 1, 1, -2, -2, -2, -1, 0, 1, 1, + -1, -2, -2, -1, 0, 1, 1, 1, -1, -2, -1, 0, 1, 1, 1, 0, + -1, -1, 0, 1, 1, 1, 0, -1, 0, -1, 0, 1, 0, 0, -1, -1, + 0, 1, 1, 1, 1, 1, 0, 0, 1, 2, 2, 2, 1, 0, 0, 0, + 2, 2, 2, 2, 1, 0, -1, -1, 1, 1, 1, 0, -1, -2, -2, -2, + 0, 0, 0, -1, -2, -3, -2, -2, -1, -1, -1, -2, -2, -2, -1, 0, + -1, -1, -1, -1, 0, 0, 1, 2, -1, -1, -1, 0, 1, 2, 3, 4, + -1, -1, 0, 0, -1, -2, -3, -3, -1, -1, 0, 0, 0, -1, -1, -1, + -2, -2, -1, 0, 1, 1, 1, 1, -2, -2, -2, 0, 1, 2, 3, 3, + -1, -1, -1, 0, 1, 3, 3, 3, 1, 0, 0, 0, 1, 1, 2, 2, + 2, 2, 1, 0, 0, -1, -1, -1, 3, 2, 1, 0, -1, -2, -3, -3, + -1, -1, -1, -2, -2, -3, -4, -5, 0, 0, 0, -1, -1, -3, -3, -4, + 1, 1, 1, 0, 0, -1, -2, -3, 2, 2, 2, 1, 1, 0, -1, -1, + 2, 2, 2, 2, 1, 1, 0, -1, 2, 2, 2, 2, 2, 1, 0, 0, + 1, 1, 2, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, -1, + -2, 2, 3, 1, -1, 1, 1, -1, -3, 2, 3, 0, -1, 1, 1, -1, + -3, 2, 3, 0, -1, 1, 1, -1, -4, 2, 3, 0, -1, 1, 1, -2, + -4, 1, 3, 0, -1, 1, 1, -2, -4, 1, 3, -1, -2, 1, 1, -2, + -3, 1, 2, 0, -1, 1, 1, -2, -3, 1, 2, 0, -1, 1, 1, -1, + -1, -1, -1, -2, -2, -2, -2, -2, 1, 1, 1, 1, 0, 0, 0, 0, + 1, 2, 2, 2, 2, 2, 2, 2, 0, 0, 1, 1, 1, 2, 2, 2, + -2, -2, -1, -1, -1, 0, 0, 0, -3, -3, -3, -3, -3, -3, -3, -2, + -1, -1, -1, -1, -2, -2, -2, -2, 4, 4, 4, 4, 4, 3, 3, 2, + -3, -3, -2, -1, 0, 1, 2, 5, -3, -3, -3, -2, -1, 1, 3, 6, + -3, -3, -2, -2, 0, 2, 3, 5, -3, -2, -2, -2, 0, 1, 3, 5, + -2, -2, -2, -1, -1, 1, 3, 5, -2, -2, -1, -1, 0, 1, 2, 4, + -1, -1, -1, -1, 0, 1, 1, 4, -1, -1, -1, -1, 0, 1, 2, 3, + 0, -1, 0, 1, 1, 0, -1, -1, 0, 0, 0, 1, 2, 0, -1, -1, + 1, 0, -1, 0, 1, 0, 0, 0, 1, -1, -2, -1, 0, 0, 0, 0, + 1, -2, -3, -1, 0, 0, 0, 1, 1, -1, -3, -2, 0, 1, 1, 2, + 1, -1, -2, -1, 0, 1, 1, 2, 2, 0, -1, 0, 1, 1, 2, 2, + 1, 1, 1, 1, 0, 0, 1, 2, -1, 0, 0, -1, 0, 0, 0, 1, + -3, -2, -1, -1, -1, 0, 1, 1, -4, -2, -1, 0, 0, 1, 1, 1, + -3, -2, 0, 0, 1, 1, 1, 1, -3, -1, 0, 1, 1, 1, 0, 0, + -1, 0, 1, 1, 1, 0, 0, -1, 0, 1, 2, 2, 1, 0, 0, -1, + -4, -4, -4, -3, -2, -1, -1, -1, -2, -2, -2, -1, 0, 0, 0, 0, + -1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, + 0, 0, 1, 1, 2, 2, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, + 0, 0, 0, 1, 1, 1, 1, 0, -1, 0, 0, 1, 1, 1, 0, 0, + 1, 2, 2, 2, 1, -1, -2, -4, 1, 1, 2, 2, 1, 0, -2, -4, + 0, 1, 1, 1, 1, 0, -1, -3, -1, 0, 1, 1, 0, 0, -1, -2, + -1, 0, 1, 1, 1, 0, 0, -1, -2, -1, 0, 0, 0, 0, 0, -1, + -1, -1, 0, 1, 1, 0, 0, 0, -1, 0, 1, 1, 1, 1, 1, 0, + 2, 2, 0, -1, -2, -1, -1, -2, 1, 1, -1, -2, -2, -1, -1, -2, + 1, 1, -1, -2, -2, 0, 0, -1, 1, 1, 0, -2, -1, 1, 1, 0, + 1, 1, 0, -1, -1, 1, 2, 1, 1, 1, 0, -1, -1, 1, 2, 1, + 1, 1, 0, -1, -1, 1, 1, 1, 1, 1, 0, -1, 0, 1, 1, 1, + 0, 0, -1, -2, -4, -4, -4, -4, 3, 3, 3, 2, 1, 0, 0, 0, + 3, 3, 3, 3, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 1, + -1, -1, -1, -1, -1, -1, -1, 0, 0, -1, 0, 0, -1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, 0, + -1, -1, 0, -1, -1, 1, 2, -1, 1, 1, 0, 0, 0, 2, 3, -1, + 1, 1, 0, -1, -1, 1, 3, -1, 1, 1, 0, -2, -2, 0, 1, -2, + 1, 0, 0, -2, -2, 0, 1, -3, 0, 0, 0, 0, -1, 1, 1, -3, + 0, 1, 1, 0, 1, 2, 1, -3, -1, 0, 1, 1, 1, 2, 1, -4, + -4, -3, 0, 1, 1, 1, 0, 0, -4, -2, 0, 1, 1, 1, 0, -1, + -3, -1, 1, 1, 1, 0, -1, -1, -1, 1, 1, 1, 1, 0, -1, 0, + 1, 2, 2, 1, 0, -1, 0, 0, 2, 2, 1, 0, -1, -1, 0, 1, + 2, 1, 0, -1, -2, -1, 0, 1, 2, 2, 0, -1, -2, -1, 1, 1, + 1, 1, 0, 0, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, -1, -1, -1, -1, -1, -1, + 1, 0, 0, -1, -1, -1, -1, -1, 2, 1, 0, 0, -1, -1, -1, -1, + 5, 3, 2, 1, 0, 0, 0, 0, 6, 5, 3, 2, 1, 0, 0, 0, + 4, 4, 3, 1, 0, 0, 0, 1, 3, 3, 2, 1, 0, 0, 0, 1, + 2, 2, 1, 0, -1, -1, 0, 1, 0, 0, 0, -1, -1, -1, 0, 1, + 0, 0, -1, -1, -2, -1, 0, 2, 0, -1, -1, -2, -2, -2, 0, 1, + 0, -1, -1, -2, -2, -2, -1, 0, 0, 0, -1, -2, -2, -2, -1, 0, + 0, 0, -1, -1, -1, 0, 2, 3, 0, -1, -2, -2, -1, -1, 1, 2, + 1, 0, -1, -1, -1, 0, 0, 0, 1, 1, 1, 0, 0, 0, -1, -1, + 1, 2, 1, 0, 0, -1, -1, -1, -1, 0, 0, 0, -1, -1, -1, -1, + -3, -2, -1, -1, 0, 1, 1, 2, -4, -3, -1, 1, 2, 3, 5, 5, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, -1, 0, 0, 0, 1, -1, -1, -2, -2, -2, -1, -1, 0, + 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, + 1, 1, 1, 1, 2, 2, 1, 1, -4, -3, -4, -4, -4, -4, -3, -3, + -1, 0, 1, 2, 2, 3, 3, 3, -1, -1, -1, -1, 0, 0, 0, 0, + 0, 0, -1, -2, -2, -3, -3, -2, 3, 2, 1, 0, -1, -2, -2, -2, + 4, 3, 2, 1, 1, 0, 0, 0, 2, 2, 1, 1, 0, 1, 1, 1, + 0, -1, -1, -1, -1, 0, 0, 1, -2, -2, -2, -2, -2, -1, 0, 0, + 1, -1, 0, 2, 1, -2, -1, 1, 1, -1, 0, 2, 1, -2, -2, 1, + 1, -1, 0, 3, 2, -2, -1, 1, 0, -2, 0, 3, 2, -2, -2, 1, + 0, -2, 0, 3, 2, -2, -2, 1, 0, -2, 0, 3, 1, -2, -1, 1, + 0, -2, 0, 2, 1, -2, -2, 1, 0, -1, 0, 2, 1, -2, -1, 1, + 0, 1, 2, 2, 3, 3, 2, 2, 0, 1, 1, 2, 3, 3, 2, 1, + 0, 0, 1, 2, 2, 2, 2, 1, -1, 0, 0, 1, 1, 1, 1, 1, + -1, -1, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, + -2, -2, -2, -2, -2, -2, -2, -1, -2, -2, -2, -2, -2, -2, -2, -1, + 0, 0, -1, -2, -1, 0, 3, 5, 0, 0, -1, -1, -1, 0, 2, 4, + 1, 1, 0, 0, -1, -1, 1, 2, 1, 2, 1, 1, 0, -1, -1, 0, + 0, 1, 2, 1, 0, -1, -2, -2, -1, 0, 1, 2, 1, 0, -3, -3, + -2, -1, 1, 2, 2, 0, -2, -4, -2, -1, 0, 2, 2, 1, -1, -3, + 0, 0, 0, 0, 0, 0, -1, -1, 0, 0, -1, 0, 0, 0, 0, 0, + -1, -1, -1, -1, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, 0, + -1, -1, -1, -1, -1, -1, -1, 0, -1, 0, 0, 0, 0, -1, -1, 0, + 0, 0, 1, 1, 0, 0, 0, 1, 3, 3, 3, 4, 3, 3, 3, 3, + 5, 1, -2, -2, 0, 0, 0, -1, 4, -1, -3, -1, 0, 0, 0, -1, + 3, -1, -1, 0, 1, 1, 0, -1, 2, 0, 0, 1, 1, 1, 0, -2, + 1, 0, 0, 1, 1, 1, 0, -2, 0, -1, -1, -1, 0, 0, 0, -1, + 0, -1, -1, -1, -1, 0, 0, -1, 2, 1, 0, 0, 0, 1, 0, 0, + 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, + 1, -1, -1, 0, 0, 0, 0, 0, 2, 0, -1, -1, -1, -1, -1, 0, + 3, 1, -1, -1, -2, -2, -2, -1, 4, 2, 1, 0, -1, -2, -2, -1, + 2, 1, 0, 0, -1, -1, 0, 0, 0, -1, -1, -1, -1, 0, 1, 1, + 0, 1, 2, 2, 2, 1, -1, -3, 0, 0, 1, 1, 1, 0, -1, -2, + 0, 0, 0, 0, 0, 0, -1, -1, 0, 0, -1, 0, 0, 1, 1, 0, + 0, 0, -1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, + 0, 0, 1, 1, 2, 1, -1, -3, 0, 0, 0, 1, 1, -1, -4, -5, + -2, -2, -2, -1, 0, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 0, + 1, 1, 1, 1, 1, 0, -2, -3, 0, 0, 1, 1, 0, -1, -3, -4, + -1, -1, 0, 1, 0, 0, -2, -3, -1, -1, 0, 1, 1, 1, 0, -1, + 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, + 0, 1, 0, 0, 1, 1, 1, 2, 1, 2, 0, 0, 0, 0, -1, 1, + 0, 2, 0, -1, 1, 0, -1, 0, 0, 1, 0, 0, 2, 1, 0, 1, + 0, 1, -1, 0, 2, 2, 0, 1, -1, 0, -1, -1, 2, 1, 1, 2, + -2, -2, -3, -2, 0, 1, 1, 1, -2, -2, -3, -3, -1, -1, -1, 0, + -3, -1, 0, 1, 2, 1, 1, 0, -3, -1, 0, 1, 2, 1, 1, 1, + -2, 0, 0, 1, 1, 1, 1, 1, -1, 0, 0, 0, 0, 0, 0, 0, + -2, 0, 0, 0, 0, -1, -1, 0, -2, 0, 0, 0, 0, 0, -1, -1, + -3, 0, 1, 1, 1, 1, 0, 1, -5, -2, 0, 1, 2, 2, 1, 2, + -2, -1, -1, 0, 0, 1, 2, 3, 0, 0, 1, 1, 0, 0, 1, 2, + 0, 0, 1, 0, -1, -1, 0, 1, -1, -1, -1, -1, -2, -2, -1, 0, + -2, -2, -2, -2, -2, -1, 0, 1, 0, 0, 0, -1, 0, 1, 2, 2, + 2, 1, 0, 0, 0, 1, 2, 2, 2, 1, 0, -1, -1, -1, 0, 0, + 0, 1, 1, 1, 1, 1, -1, -4, -1, -1, 0, 1, 1, 1, 0, -3, + -2, -1, 0, 0, 1, 2, 2, -2, -1, 0, 0, 0, 0, 2, 3, -1, + -1, 0, 0, 0, 0, 1, 2, 0, 0, 0, -1, -2, -1, 1, 1, 0, + 0, 0, -1, -2, -2, 0, 2, 1, 0, 0, -1, -2, -1, 1, 2, 2, + 1, 0, 0, 0, -2, -3, -2, -3, 0, 0, 1, 0, -2, -2, -1, -1, + 0, -1, 1, 1, -1, -1, 0, 0, 0, -1, 1, 1, -1, -1, 0, 0, + 0, 1, 2, 1, -1, -1, 0, 1, 1, 2, 3, 2, 0, 0, 1, 2, + -1, 0, 2, 1, 0, 0, 2, 3, -2, -1, 0, 0, -1, 0, 1, 2, + 1, 1, 0, -1, -2, -2, -1, 1, 1, 1, 1, -1, -2, -2, 0, 2, + 1, 1, 1, -1, -1, -1, 0, 2, 0, 0, 0, 0, 0, 0, 1, 2, + -1, -1, -1, 0, 0, 0, 1, 2, -1, -2, -1, 1, 1, 1, 0, 0, + -1, -2, -1, 1, 2, 2, 0, -1, -1, -2, -1, 2, 2, 2, 0, -1, + -1, -1, -1, -2, -1, -1, 0, 1, 0, 0, -1, -1, -1, 0, 1, 2, + 1, 0, 0, 0, 0, 1, 1, 2, 1, 1, 0, 0, 1, 1, 1, 1, + 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, -1, -1, -1, + 1, 2, 1, 0, -1, -2, -2, -3, 2, 2, 1, 0, -2, -3, -4, -4, + -4, -2, 1, 1, 1, 1, 0, 0, -2, 0, 1, 0, 0, 0, 0, 0, + 0, 1, 1, -2, -2, -1, 0, 1, 2, 2, 1, -2, -2, -1, 1, 2, + 1, 2, 1, -2, -2, -1, 1, 2, -1, 1, 1, -1, -1, -1, 0, 1, + -2, 0, 1, 1, 0, -1, -1, 0, -2, 0, 2, 2, 1, -1, -1, 0, + 1, 1, 0, 0, 0, 1, 0, 0, -2, -3, -3, -2, -2, -1, 0, 0, + -3, -4, -3, -2, -1, 0, 0, 0, -1, -1, 0, 1, 2, 3, 2, 1, + 0, 1, 2, 3, 3, 3, 2, 1, 1, 1, 1, 2, 1, 0, 0, -1, + 0, 0, 0, 0, -1, -1, -1, -1, 0, -1, -1, 0, 0, 0, 0, 0, + 1, 1, 0, 0, -1, -1, 0, 2, 0, 0, 1, 0, -1, -1, 1, 1, + -2, -1, 0, 1, 1, 1, 1, 1, -3, -3, 0, 2, 2, 1, 1, 0, + -2, -2, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, -1, -1, + 3, 1, -1, -3, -2, -1, 0, 1, 4, 2, -1, -3, -3, -1, 1, 2, + 0, 0, 0, -1, -1, -1, -1, -1, 1, 2, 1, 0, 0, 0, -1, -1, + 2, 3, 3, 2, 1, 0, -1, -1, 3, 4, 4, 2, 1, 0, -1, -2, + 3, 3, 2, 1, 0, -1, -2, -2, 1, 1, 0, -1, -1, -2, -2, -3, + 0, 0, 0, -1, -1, -2, -2, -2, -1, -1, -1, -1, -1, -2, -2, -1, + 1, 2, 2, 2, 2, 1, 2, 2, 0, 1, 1, 1, 1, 0, 0, 0, + 0, 0, 0, 1, 1, 0, -1, -2, 0, 0, 0, 0, 1, 0, -1, -4, + 1, 0, 0, 0, 0, 0, -2, -5, 1, 0, 0, 0, 0, 0, -1, -4, + 1, 0, -1, 0, 0, 0, -1, -3, 0, -1, -1, 0, 1, 1, 1, -1, + -2, -1, 0, 0, -1, -1, -1, -2, -1, 0, 0, 0, -1, -1, -2, -2, + 0, 1, 1, 0, -1, -1, -1, -2, 0, 1, 1, 0, 0, 0, -1, -1, + 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 2, 2, 1, + 1, 1, 0, 0, 1, 2, 2, 1, 1, 1, 0, -1, 0, 1, 1, 0, + 4, 2, 1, 0, 0, 1, 1, 1, 4, 2, 1, 0, 0, 0, 0, 1, + 3, 1, 0, 0, -1, -1, -1, 0, 1, 0, 0, -1, -1, -2, -1, 0, + 0, 0, 0, 0, -1, -1, -1, 0, -1, -1, 0, 0, -1, -1, 0, 1, + -2, -1, 0, -1, -1, 0, 0, 1, -2, -2, -1, -2, -1, 0, 0, 1, + 0, 1, 1, 1, 2, 1, 0, -1, -1, -1, -1, 0, 0, -1, -2, -2, + -1, 0, -1, 0, 0, -1, -2, -1, 0, 0, 0, 0, 0, 0, 1, 2, + 0, 0, 0, 0, 0, 0, 2, 3, -1, 0, -1, -1, -1, -1, 0, 3, + -1, 0, 0, -1, -1, -2, 0, 3, 0, 0, 0, 0, -1, -1, 1, 4, + 2, 2, 0, 0, 0, 0, 0, 1, 1, 1, -1, -2, -1, -2, -1, 1, + -1, -1, -2, -2, -2, -3, -2, 0, -1, 0, -1, -1, -1, -2, -1, 1, + 1, 1, 0, 0, 1, 0, 0, 1, 2, 2, 0, 0, 1, 0, 0, 1, + 2, 2, 0, 0, 0, 0, -1, -1, 2, 2, 0, 0, 1, 0, -1, -1, + -1, 0, 1, 1, 0, -1, -1, -1, 1, 2, 3, 2, 1, 0, 0, 0, + 0, 1, 1, 1, 0, -1, 0, 0, -2, -2, -1, 0, 1, 0, 0, 0, + -2, -2, -1, 2, 2, 2, 1, 0, -2, -1, 0, 1, 1, 0, 0, -1, + -1, -1, 0, 0, -1, -2, -1, -2, 0, 1, 1, 1, 0, 0, 1, 1, + -3, -3, -3, -2, -1, -1, -2, -2, -1, -1, 0, 1, 2, 1, 0, 0, + 1, 1, 1, 2, 2, 1, 0, 0, 1, 1, 1, 1, 1, 0, -1, 1, + 1, 0, -1, -1, 0, 0, -1, 1, 0, -1, -1, -1, 0, -1, -1, 1, + 1, 0, -1, 0, 0, -1, 0, 2, 2, 0, -1, 0, 0, 0, 0, 2, + 1, 0, -2, -1, 0, 1, 1, 0, 2, 0, -1, -1, 0, 1, 1, 0, + 1, 0, -2, -1, 0, 1, 0, -1, 1, 0, -1, -1, 0, 1, 0, -1, + 0, 1, 1, 0, 1, 1, 0, 0, -2, 1, 2, 1, 0, 0, 0, 1, + -5, 0, 2, 1, 0, -1, 0, 1, -6, -1, 2, 1, 0, -1, 0, 0, + 5, 3, 0, -1, -2, -1, -1, -1, 1, 1, 0, -1, -1, 0, -1, -1, + -1, 0, 1, 1, 2, 2, 1, 0, -2, -1, 0, 1, 2, 1, 1, 1, + -2, -1, -1, -1, 0, -1, 0, 1, 0, 1, 0, 0, -1, -1, 0, 0, + 0, 1, 1, 1, 1, 0, 0, 0, -3, -2, 0, 1, 1, 0, 0, -1, + -1, 0, 1, 0, -1, 0, 2, 3, -1, 0, 0, -2, -4, -2, -1, 0, + 0, 1, 1, 0, -2, -1, 0, -1, 1, 2, 3, 1, 0, 1, 1, 0, + -1, 0, 1, 1, 1, 1, 1, 0, -2, -3, -2, 0, 0, 0, 1, 0, + -1, -2, -2, 0, 1, 0, 0, -1, 3, 1, 0, 0, 1, 0, -1, -1, + -2, -1, 0, 0, -1, -1, 0, 0, -1, 0, 0, 0, 0, 1, 1, 1, + -1, -1, -1, 0, 1, 1, 1, 1, 0, -2, -3, -1, 1, 0, 0, 0, + 1, -1, -3, -1, 1, 1, 0, -1, 3, 1, -1, 1, 2, 2, 0, -1, + 3, 1, 0, 1, 2, 1, 1, 0, 0, -2, -2, -1, -1, 0, 0, 0, + 1, 0, -1, -1, 1, 2, 1, 0, 0, -1, -2, -1, 1, 2, 2, 1, + -1, -1, -1, 0, 0, 1, 2, 0, -2, 0, 0, 0, 0, 0, 1, -1, + -1, 0, 1, 0, -1, -1, -1, -1, 0, 1, 1, 2, 0, -2, -1, 0, + 1, 2, 2, 2, 1, -1, -1, 0, 0, 1, 1, 1, 0, -2, -2, -1, + 0, 0, -1, -1, -1, -1, -2, -2, 0, 0, -1, 0, 1, 2, 2, 1, + 0, 0, -1, -1, 0, 1, 2, 2, 1, 1, -1, -2, -1, -1, -1, -1, + 2, 2, 1, 0, 0, -1, -2, -2, 1, 2, 2, 1, 0, 0, -2, -2, + 0, 0, 0, 0, 1, 1, 0, -1, 0, -1, -1, -1, 2, 3, 2, 1, + 0, -2, 1, 2, -1, 0, 0, 1, -1, -2, 2, 3, -1, 0, 0, 0, + 0, -2, 2, 3, -1, -1, 0, 0, 0, -1, 3, 2, -2, 0, 1, 0, + 0, -1, 3, 1, -2, 0, 1, 0, 0, -1, 2, 1, -1, 1, 0, -1, + 0, 0, 1, -1, -2, 0, 0, -1, 1, 0, 0, -2, -2, -1, -1, -1, + 1, 1, 1, 1, 1, -1, -1, -2, 0, 0, 0, 1, 1, 1, 1, 1, + 0, 0, 0, 1, 1, 1, 2, 3, 1, 0, 0, -1, 0, 0, 1, 2, + 0, -1, -1, -2, -1, 0, 1, 2, -2, -2, -2, -2, -1, 0, 1, 1, + -1, -1, -1, -1, 0, 0, 0, -1, 2, 2, 2, 0, -1, -1, -2, -4, + -1, -2, -1, -1, 0, 1, 2, 3, -1, -1, -1, -1, 0, 1, 2, 3, + 1, 0, -1, 0, -1, 0, 1, 2, 1, 0, 0, 0, -1, 0, 2, 2, + 1, 0, -1, -1, -2, 0, 1, 2, 0, -2, -2, -2, -3, -1, 0, 1, + 0, -2, -2, -2, -2, -1, 1, 1, 0, 0, 0, 0, 0, 1, 2, 2 }; -const uint32 *const s_svq1IntraCodebooks[6] = { +static const int8 *const s_svq1IntraCodebooks[6] = { s_svq1IntraCodebook4x2, s_svq1IntraCodebook4x4, s_svq1IntraCodebook8x4, s_svq1IntraCodebook8x8, 0, 0 diff --git a/video/codecs/truemotion1.h b/video/codecs/truemotion1.h index 628cfa4584..b2a35cf873 100644 --- a/video/codecs/truemotion1.h +++ b/video/codecs/truemotion1.h @@ -32,6 +32,12 @@ namespace Video { +/** + * Duck TrueMotion 1 decoder. + * + * Used in video: + * - AVIDecoder + */ class TrueMotion1Decoder : public Codec { public: TrueMotion1Decoder(uint16 width, uint16 height); diff --git a/video/coktel_decoder.cpp b/video/coktel_decoder.cpp index 0c7ade1b8a..08340a19a6 100644 --- a/video/coktel_decoder.cpp +++ b/video/coktel_decoder.cpp @@ -53,7 +53,8 @@ CoktelDecoder::CoktelDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundT _mixer(mixer), _soundType(soundType), _width(0), _height(0), _x(0), _y(0), _defaultX(0), _defaultY(0), _features(0), _frameCount(0), _paletteDirty(false), _ownSurface(true), _frameRate(12), _hasSound(false), _soundEnabled(false), - _soundStage(kSoundNone), _audioStream(0) { + _soundStage(kSoundNone), _audioStream(0), _startTime(0), _pauseStartTime(0), + _isPaused(false) { assert(_mixer); @@ -261,6 +262,10 @@ bool CoktelDecoder::isPaletted() const { return true; } +int CoktelDecoder::getCurFrame() const { + return _curFrame; +} + void CoktelDecoder::close() { disableSound(); freeSurface(); @@ -273,9 +278,22 @@ void CoktelDecoder::close() { _features = 0; - _frameCount = 0; + _curFrame = -1; + _frameCount = 0; + + _startTime = 0; _hasSound = false; + + _isPaused = false; +} + +Audio::Mixer::SoundType CoktelDecoder::getSoundType() const { + return _soundType; +} + +Audio::AudioStream *CoktelDecoder::getAudioStream() const { + return _audioStream; } uint16 CoktelDecoder::getWidth() const { @@ -291,6 +309,7 @@ uint32 CoktelDecoder::getFrameCount() const { } const byte *CoktelDecoder::getPalette() { + _paletteDirty = false; return _palette; } @@ -625,14 +644,45 @@ Common::Rational CoktelDecoder::getFrameRate() const { return _frameRate; } +uint32 CoktelDecoder::getTimeToNextFrame() const { + if (endOfVideo() || _curFrame < 0) + return 0; + + uint32 elapsedTime = g_system->getMillis() - _startTime; + uint32 nextFrameStartTime = (Common::Rational((_curFrame + 1) * 1000) / getFrameRate()).toInt(); + + if (nextFrameStartTime <= elapsedTime) + return 0; + + return nextFrameStartTime - elapsedTime; +} + uint32 CoktelDecoder::getStaticTimeToNextFrame() const { return (1000 / _frameRate).toInt(); } +void CoktelDecoder::pauseVideo(bool pause) { + if (_isPaused != pause) { + if (_isPaused) { + // Add the time we were paused to the initial starting time + _startTime += g_system->getMillis() - _pauseStartTime; + } else { + // Store the time we paused for use later + _pauseStartTime = g_system->getMillis(); + } + + _isPaused = pause; + } +} + inline void CoktelDecoder::unsignedToSigned(byte *buffer, int length) { while (length-- > 0) *buffer++ ^= 0x80; } +bool CoktelDecoder::endOfVideo() const { + return !isVideoLoaded() || (getCurFrame() >= (int32)getFrameCount() - 1); +} + PreIMDDecoder::PreIMDDecoder(uint16 width, uint16 height, Audio::Mixer *mixer, Audio::Mixer::SoundType soundType) : CoktelDecoder(mixer, soundType), @@ -705,8 +755,6 @@ bool PreIMDDecoder::loadStream(Common::SeekableReadStream *stream) { } void PreIMDDecoder::close() { - reset(); - CoktelDecoder::close(); delete _stream; @@ -1159,8 +1207,6 @@ bool IMDDecoder::loadFrameTables(uint32 framePosPos, uint32 frameCoordsPos) { } void IMDDecoder::close() { - reset(); - CoktelDecoder::close(); delete _stream; @@ -1225,8 +1271,6 @@ void IMDDecoder::processFrame() { _dirtyRects.clear(); - _paletteDirty = false; - uint32 cmd = 0; bool hasNextCmd = false; bool startSound = false; @@ -1322,7 +1366,7 @@ void IMDDecoder::processFrame() { // Start the audio stream if necessary if (startSound && _soundEnabled) { _mixer->playStream(_soundType, &_audioHandle, _audioStream, - -1, getVolume(), getBalance(), DisposeAfterUse::NO); + -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO); _soundStage = kSoundPlaying; } @@ -1504,16 +1548,6 @@ Graphics::PixelFormat IMDDecoder::getPixelFormat() const { return Graphics::PixelFormat::createFormatCLUT8(); } -void IMDDecoder::updateVolume() { - if (g_system->getMixer()->isSoundHandleActive(_audioHandle)) - g_system->getMixer()->setChannelVolume(_audioHandle, getVolume()); -} - -void IMDDecoder::updateBalance() { - if (g_system->getMixer()->isSoundHandleActive(_audioHandle)) - g_system->getMixer()->setChannelBalance(_audioHandle, getBalance()); -} - VMDDecoder::File::File() { offset = 0; @@ -1552,7 +1586,7 @@ VMDDecoder::VMDDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundType) : _soundLastFilledFrame(0), _audioFormat(kAudioFormat8bitRaw), _hasVideo(false), _videoCodec(0), _blitMode(0), _bytesPerPixel(0), _firstFramePos(0), _videoBufferSize(0), _externalCodec(false), _codec(0), - _subtitle(-1), _isPaletted(true) { + _subtitle(-1), _isPaletted(true), _autoStartSound(true) { _videoBuffer [0] = 0; _videoBuffer [1] = 0; @@ -2014,8 +2048,6 @@ bool VMDDecoder::readFiles() { } void VMDDecoder::close() { - reset(); - CoktelDecoder::close(); delete _stream; @@ -2095,7 +2127,6 @@ void VMDDecoder::processFrame() { _dirtyRects.clear(); - _paletteDirty = false; _subtitle = -1; bool startSound = false; @@ -2215,8 +2246,9 @@ void VMDDecoder::processFrame() { if (startSound && _soundEnabled) { if (_hasSound && _audioStream) { - _mixer->playStream(_soundType, &_audioHandle, _audioStream, - -1, getVolume(), getBalance(), DisposeAfterUse::NO); + if (_autoStartSound) + _mixer->playStream(_soundType, &_audioHandle, _audioStream, + -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO); _soundStage = kSoundPlaying; } else _soundStage = kSoundNone; @@ -2742,14 +2774,92 @@ bool VMDDecoder::isPaletted() const { return _isPaletted; } -void VMDDecoder::updateVolume() { - if (g_system->getMixer()->isSoundHandleActive(_audioHandle)) - g_system->getMixer()->setChannelVolume(_audioHandle, getVolume()); +void VMDDecoder::setAutoStartSound(bool autoStartSound) { + _autoStartSound = autoStartSound; +} + +AdvancedVMDDecoder::AdvancedVMDDecoder(Audio::Mixer::SoundType soundType) { + _decoder = new VMDDecoder(g_system->getMixer(), soundType); + _decoder->setAutoStartSound(false); +} + +AdvancedVMDDecoder::~AdvancedVMDDecoder() { + close(); + delete _decoder; +} + +bool AdvancedVMDDecoder::loadStream(Common::SeekableReadStream *stream) { + close(); + + if (!_decoder->loadStream(stream)) + return false; + + if (_decoder->hasVideo()) { + _videoTrack = new VMDVideoTrack(_decoder); + addTrack(_videoTrack); + } + + if (_decoder->hasSound()) { + _audioTrack = new VMDAudioTrack(_decoder); + addTrack(_audioTrack); + } + + return true; +} + +void AdvancedVMDDecoder::close() { + VideoDecoder::close(); + _decoder->close(); +} + +AdvancedVMDDecoder::VMDVideoTrack::VMDVideoTrack(VMDDecoder *decoder) : _decoder(decoder) { +} + +uint16 AdvancedVMDDecoder::VMDVideoTrack::getWidth() const { + return _decoder->getWidth(); +} + +uint16 AdvancedVMDDecoder::VMDVideoTrack::getHeight() const { + return _decoder->getHeight(); +} + +Graphics::PixelFormat AdvancedVMDDecoder::VMDVideoTrack::getPixelFormat() const { + return _decoder->getPixelFormat(); +} + +int AdvancedVMDDecoder::VMDVideoTrack::getCurFrame() const { + return _decoder->getCurFrame(); +} + +int AdvancedVMDDecoder::VMDVideoTrack::getFrameCount() const { + return _decoder->getFrameCount(); +} + +const Graphics::Surface *AdvancedVMDDecoder::VMDVideoTrack::decodeNextFrame() { + return _decoder->decodeNextFrame(); +} + +const byte *AdvancedVMDDecoder::VMDVideoTrack::getPalette() const { + return _decoder->getPalette(); +} + +bool AdvancedVMDDecoder::VMDVideoTrack::hasDirtyPalette() const { + return _decoder->hasDirtyPalette(); +} + +Common::Rational AdvancedVMDDecoder::VMDVideoTrack::getFrameRate() const { + return _decoder->getFrameRate(); +} + +AdvancedVMDDecoder::VMDAudioTrack::VMDAudioTrack(VMDDecoder *decoder) : _decoder(decoder) { +} + +Audio::Mixer::SoundType AdvancedVMDDecoder::VMDAudioTrack::getSoundType() const { + return _decoder->getSoundType(); } -void VMDDecoder::updateBalance() { - if (g_system->getMixer()->isSoundHandleActive(_audioHandle)) - g_system->getMixer()->setChannelBalance(_audioHandle, getBalance()); +Audio::AudioStream *AdvancedVMDDecoder::VMDAudioTrack::getAudioStream() const { + return _decoder->getAudioStream(); } } // End of namespace Video diff --git a/video/coktel_decoder.h b/video/coktel_decoder.h index c88d982191..91d52b65e6 100644 --- a/video/coktel_decoder.h +++ b/video/coktel_decoder.h @@ -64,7 +64,7 @@ class Codec; * - gob * - sci */ -class CoktelDecoder : public FixedRateVideoDecoder { +class CoktelDecoder { public: struct State { /** Set accordingly to what was done. */ @@ -77,7 +77,7 @@ public: CoktelDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundType = Audio::Mixer::kPlainSoundType); - ~CoktelDecoder(); + virtual ~CoktelDecoder(); /** Replace the current video stream with this identical one. */ virtual bool reloadStream(Common::SeekableReadStream *stream) = 0; @@ -98,6 +98,8 @@ public: /** Override the video's frame rate. */ void setFrameRate(Common::Rational frameRate); + /** Get the video's frame rate. */ + Common::Rational getFrameRate() const; /** Get the video's default X position. */ uint16 getDefaultX() const; @@ -138,21 +140,52 @@ public: /** Is the video paletted or true color? */ virtual bool isPaletted() const; + /** + * Get the current frame + * @see VideoDecoder::getCurFrame() + */ + int getCurFrame() const; + + /** + * Decode the next frame + * @see VideoDecoder::decodeNextFrame() + */ + virtual const Graphics::Surface *decodeNextFrame() = 0; + + /** + * Load a video from a stream + * @see VideoDecoder::loadStream() + */ + virtual bool loadStream(Common::SeekableReadStream *stream) = 0; - // VideoDecoder interface + /** Has a video been loaded? */ + virtual bool isVideoLoaded() const = 0; + /** Has the end of the video been reached? */ + bool endOfVideo() const; + + /** Close the video. */ void close(); + /** Get the Mixer SoundType audio is being played with. */ + Audio::Mixer::SoundType getSoundType() const; + /** Get the AudioStream for the audio. */ + Audio::AudioStream *getAudioStream() const; + uint16 getWidth() const; uint16 getHeight() const; + virtual Graphics::PixelFormat getPixelFormat() const = 0; uint32 getFrameCount() const; const byte *getPalette(); bool hasDirtyPalette() const; + uint32 getTimeToNextFrame() const; uint32 getStaticTimeToNextFrame() const; + void pauseVideo(bool pause); + protected: enum SoundStage { kSoundNone = 0, ///< No sound. @@ -186,8 +219,11 @@ protected: uint32 _features; + int32 _curFrame; uint32 _frameCount; + uint32 _startTime; + byte _palette[768]; bool _paletteDirty; @@ -228,10 +264,9 @@ protected: // Sound helper functions inline void unsignedToSigned(byte *buffer, int length); - - // FixedRateVideoDecoder interface - - Common::Rational getFrameRate() const; +private: + uint32 _pauseStartTime; + bool _isPaused; }; class PreIMDDecoder : public CoktelDecoder { @@ -244,9 +279,6 @@ public: bool seek(int32 frame, int whence = SEEK_SET, bool restart = false); - - // VideoDecoder interface - bool loadStream(Common::SeekableReadStream *stream); void close(); @@ -279,9 +311,6 @@ public: void setXY(uint16 x, uint16 y); - - // VideoDecoder interface - bool loadStream(Common::SeekableReadStream *stream); void close(); @@ -291,11 +320,6 @@ public: Graphics::PixelFormat getPixelFormat() const; -protected: - // VideoDecoder API - void updateVolume(); - void updateBalance(); - private: enum Command { kCommandNextSound = 0xFF00, @@ -367,6 +391,8 @@ private: }; class VMDDecoder : public CoktelDecoder { +friend class AdvancedVMDDecoder; + public: VMDDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundType = Audio::Mixer::kPlainSoundType); ~VMDDecoder(); @@ -390,9 +416,6 @@ public: bool hasVideo() const; bool isPaletted() const; - - // VideoDecoder interface - bool loadStream(Common::SeekableReadStream *stream); void close(); @@ -403,9 +426,7 @@ public: Graphics::PixelFormat getPixelFormat() const; protected: - // VideoDecoder API - void updateVolume(); - void updateBalance(); + void setAutoStartSound(bool autoStartSound); private: enum PartType { @@ -478,6 +499,7 @@ private: uint32 _soundDataSize; uint32 _soundLastFilledFrame; AudioFormat _audioFormat; + bool _autoStartSound; // Video properties bool _hasVideo; @@ -532,6 +554,57 @@ private: bool getPartCoords(int16 frame, PartType type, int16 &x, int16 &y, int16 &width, int16 &height); }; +/** + * A wrapper around the VMD code that implements the VideoDecoder + * API. + */ +class AdvancedVMDDecoder : public VideoDecoder { +public: + AdvancedVMDDecoder(Audio::Mixer::SoundType soundType = Audio::Mixer::kPlainSoundType); + ~AdvancedVMDDecoder(); + + bool loadStream(Common::SeekableReadStream *stream); + void close(); + +private: + class VMDVideoTrack : public FixedRateVideoTrack { + public: + VMDVideoTrack(VMDDecoder *decoder); + + uint16 getWidth() const; + uint16 getHeight() const; + Graphics::PixelFormat getPixelFormat() const; + int getCurFrame() const; + int getFrameCount() const; + const Graphics::Surface *decodeNextFrame(); + const byte *getPalette() const; + bool hasDirtyPalette() const; + + protected: + Common::Rational getFrameRate() const; + + private: + VMDDecoder *_decoder; + }; + + class VMDAudioTrack : public AudioTrack { + public: + VMDAudioTrack(VMDDecoder *decoder); + + Audio::Mixer::SoundType getSoundType() const; + + protected: + virtual Audio::AudioStream *getAudioStream() const; + + private: + VMDDecoder *_decoder; + }; + + VMDDecoder *_decoder; + VMDVideoTrack *_videoTrack; + VMDAudioTrack *_audioTrack; +}; + } // End of namespace Video #endif // VIDEO_COKTELDECODER_H diff --git a/video/dxa_decoder.cpp b/video/dxa_decoder.cpp index 7d1112a59c..5ac9bd2088 100644 --- a/video/dxa_decoder.cpp +++ b/video/dxa_decoder.cpp @@ -37,41 +37,43 @@ namespace Video { DXADecoder::DXADecoder() { - _fileStream = 0; - _surface = 0; - _dirtyPalette = false; +} - _frameBuffer1 = 0; - _frameBuffer2 = 0; - _scaledBuffer = 0; +DXADecoder::~DXADecoder() { + close(); +} - _inBuffer = 0; - _inBufferSize = 0; +bool DXADecoder::loadStream(Common::SeekableReadStream *stream) { + close(); - _decompBuffer = 0; - _decompBufferSize = 0; + uint32 tag = stream->readUint32BE(); - _width = 0; - _height = 0; + if (tag != MKTAG('D','E','X','A')) { + close(); + return false; + } - _frameSize = 0; - _frameCount = 0; - _frameRate = 0; + DXAVideoTrack *track = new DXAVideoTrack(stream); + addTrack(track); - _scaleMode = S_NONE; -} + readSoundData(stream); -DXADecoder::~DXADecoder() { - close(); + track->setFrameStartPos(); + return true; } -bool DXADecoder::loadStream(Common::SeekableReadStream *stream) { - close(); +void DXADecoder::readSoundData(Common::SeekableReadStream *stream) { + // Skip over the tag by default + stream->readUint32BE(); +} +DXADecoder::DXAVideoTrack::DXAVideoTrack(Common::SeekableReadStream *stream) { _fileStream = stream; - - uint32 tag = _fileStream->readUint32BE(); - assert(tag == MKTAG('D','E','X','A')); + _curFrame = -1; + _frameStartOffset = 0; + _decompBuffer = 0; + _inBuffer = 0; + memset(_palette, 0, 256 * 3); uint8 flags = _fileStream->readByte(); _frameCount = _fileStream->readUint16BE(); @@ -105,18 +107,14 @@ bool DXADecoder::loadStream(Common::SeekableReadStream *stream) { _frameSize = _width * _height; _decompBufferSize = _frameSize; - _frameBuffer1 = (uint8 *)malloc(_frameSize); + _frameBuffer1 = new byte[_frameSize]; memset(_frameBuffer1, 0, _frameSize); - _frameBuffer2 = (uint8 *)malloc(_frameSize); + _frameBuffer2 = new byte[_frameSize]; memset(_frameBuffer2, 0, _frameSize); - if (!_frameBuffer1 || !_frameBuffer2) - error("DXADecoder: Error allocating frame buffers (size %u)", _frameSize); _scaledBuffer = 0; if (_scaleMode != S_NONE) { - _scaledBuffer = (uint8 *)malloc(_frameSize); - if (!_scaledBuffer) - error("Error allocating scale buffer (size %u)", _frameSize); + _scaledBuffer = new byte[_frameSize]; memset(_scaledBuffer, 0, _frameSize); } @@ -148,36 +146,33 @@ bool DXADecoder::loadStream(Common::SeekableReadStream *stream) { } while (tag != 0); } #endif - - // Read the sound header - _soundTag = _fileStream->readUint32BE(); - - return true; } -void DXADecoder::close() { - if (!_fileStream) - return; - +DXADecoder::DXAVideoTrack::~DXAVideoTrack() { delete _fileStream; - _fileStream = 0; - delete _surface; - _surface = 0; + delete[] _frameBuffer1; + delete[] _frameBuffer2; + delete[] _scaledBuffer; + delete[] _inBuffer; + delete[] _decompBuffer; +} - free(_frameBuffer1); - free(_frameBuffer2); - free(_scaledBuffer); - free(_inBuffer); - free(_decompBuffer); +bool DXADecoder::DXAVideoTrack::rewind() { + _curFrame = -1; + _fileStream->seek(_frameStartOffset); + return true; +} - _inBuffer = 0; - _decompBuffer = 0; +Graphics::PixelFormat DXADecoder::DXAVideoTrack::getPixelFormat() const { + return _surface->format; +} - reset(); +void DXADecoder::DXAVideoTrack::setFrameStartPos() { + _frameStartOffset = _fileStream->pos(); } -void DXADecoder::decodeZlib(byte *data, int size, int totalSize) { +void DXADecoder::DXAVideoTrack::decodeZlib(byte *data, int size, int totalSize) { #ifdef USE_ZLIB unsigned long dstLen = totalSize; Common::uncompress(data, &dstLen, _inBuffer, size); @@ -187,14 +182,13 @@ void DXADecoder::decodeZlib(byte *data, int size, int totalSize) { #define BLOCKW 4 #define BLOCKH 4 -void DXADecoder::decode12(int size) { +void DXADecoder::DXAVideoTrack::decode12(int size) { #ifdef USE_ZLIB - if (_decompBuffer == NULL) { - _decompBuffer = (byte *)malloc(_decompBufferSize); + if (!_decompBuffer) { + _decompBuffer = new byte[_decompBufferSize]; memset(_decompBuffer, 0, _decompBufferSize); - if (_decompBuffer == NULL) - error("Error allocating decomp buffer (size %u)", _decompBufferSize); } + /* decompress the input data */ decodeZlib(_decompBuffer, size, _decompBufferSize); @@ -287,15 +281,13 @@ void DXADecoder::decode12(int size) { #endif } -void DXADecoder::decode13(int size) { +void DXADecoder::DXAVideoTrack::decode13(int size) { #ifdef USE_ZLIB uint8 *codeBuf, *dataBuf, *motBuf, *maskBuf; - if (_decompBuffer == NULL) { - _decompBuffer = (byte *)malloc(_decompBufferSize); + if (!_decompBuffer) { + _decompBuffer = new byte[_decompBufferSize]; memset(_decompBuffer, 0, _decompBufferSize); - if (_decompBuffer == NULL) - error("Error allocating decomp buffer (size %u)", _decompBufferSize); } /* decompress the input data */ @@ -475,7 +467,7 @@ void DXADecoder::decode13(int size) { #endif } -const Graphics::Surface *DXADecoder::decodeNextFrame() { +const Graphics::Surface *DXADecoder::DXAVideoTrack::decodeNextFrame() { uint32 tag = _fileStream->readUint32BE(); if (tag == MKTAG('C','M','A','P')) { _fileStream->read(_palette, 256 * 3); @@ -486,11 +478,10 @@ const Graphics::Surface *DXADecoder::decodeNextFrame() { if (tag == MKTAG('F','R','A','M')) { byte type = _fileStream->readByte(); uint32 size = _fileStream->readUint32BE(); - if ((_inBuffer == NULL) || (_inBufferSize < size)) { - free(_inBuffer); - _inBuffer = (byte *)malloc(size); - if (_inBuffer == NULL) - error("Error allocating input buffer (size %u)", size); + + if (!_inBuffer || _inBufferSize < size) { + delete[] _inBuffer; + _inBuffer = new byte[size]; memset(_inBuffer, 0, size); _inBufferSize = size; } @@ -551,9 +542,6 @@ const Graphics::Surface *DXADecoder::decodeNextFrame() { _curFrame++; - if (_curFrame == 0) - _startTime = g_system->getMillis(); - return _surface; } diff --git a/video/dxa_decoder.h b/video/dxa_decoder.h index d13cd3076c..b3f2eca5e2 100644 --- a/video/dxa_decoder.h +++ b/video/dxa_decoder.h @@ -41,62 +41,74 @@ namespace Video { * - sword1 * - sword2 */ -class DXADecoder : public FixedRateVideoDecoder { +class DXADecoder : public VideoDecoder { public: DXADecoder(); virtual ~DXADecoder(); bool loadStream(Common::SeekableReadStream *stream); - void close(); - - bool isVideoLoaded() const { return _fileStream != 0; } - uint16 getWidth() const { return _width; } - uint16 getHeight() const { return _height; } - uint32 getFrameCount() const { return _frameCount; } - const Graphics::Surface *decodeNextFrame(); - Graphics::PixelFormat getPixelFormat() const { return Graphics::PixelFormat::createFormatCLUT8(); } - const byte *getPalette() { _dirtyPalette = false; return _palette; } - bool hasDirtyPalette() const { return _dirtyPalette; } +protected: /** - * Get the sound chunk tag of the loaded DXA file + * Read the sound data out of the given DXA stream */ - uint32 getSoundTag() { return _soundTag; } - -protected: - Common::Rational getFrameRate() const { return _frameRate; } - - Common::SeekableReadStream *_fileStream; + virtual void readSoundData(Common::SeekableReadStream *stream); private: - void decodeZlib(byte *data, int size, int totalSize); - void decode12(int size); - void decode13(int size); - - enum ScaleMode { - S_NONE, - S_INTERLACED, - S_DOUBLE + class DXAVideoTrack : public FixedRateVideoTrack { + public: + DXAVideoTrack(Common::SeekableReadStream *stream); + ~DXAVideoTrack(); + + bool isRewindable() const { return true; } + bool rewind(); + + uint16 getWidth() const { return _width; } + uint16 getHeight() const { return _height; } + Graphics::PixelFormat getPixelFormat() const; + int getCurFrame() const { return _curFrame; } + int getFrameCount() const { return _frameCount; } + const Graphics::Surface *decodeNextFrame(); + const byte *getPalette() const { _dirtyPalette = false; return _palette; } + bool hasDirtyPalette() const { return _dirtyPalette; } + + void setFrameStartPos(); + + protected: + Common::Rational getFrameRate() const { return _frameRate; } + + private: + void decodeZlib(byte *data, int size, int totalSize); + void decode12(int size); + void decode13(int size); + + enum ScaleMode { + S_NONE, + S_INTERLACED, + S_DOUBLE + }; + + Common::SeekableReadStream *_fileStream; + Graphics::Surface *_surface; + + byte *_frameBuffer1; + byte *_frameBuffer2; + byte *_scaledBuffer; + byte *_inBuffer; + uint32 _inBufferSize; + byte *_decompBuffer; + uint32 _decompBufferSize; + uint16 _curHeight; + uint32 _frameSize; + ScaleMode _scaleMode; + uint16 _width, _height; + uint32 _frameRate; + uint32 _frameCount; + byte _palette[256 * 3]; + mutable bool _dirtyPalette; + int _curFrame; + uint32 _frameStartOffset; }; - - Graphics::Surface *_surface; - byte _palette[256 * 3]; - bool _dirtyPalette; - - byte *_frameBuffer1; - byte *_frameBuffer2; - byte *_scaledBuffer; - byte *_inBuffer; - uint32 _inBufferSize; - byte *_decompBuffer; - uint32 _decompBufferSize; - uint16 _curHeight; - uint32 _frameSize; - ScaleMode _scaleMode; - uint32 _soundTag; - uint16 _width, _height; - uint32 _frameRate; - uint32 _frameCount; }; } // End of namespace Video diff --git a/video/flic_decoder.cpp b/video/flic_decoder.cpp index bdcdedc142..de545366b1 100644 --- a/video/flic_decoder.cpp +++ b/video/flic_decoder.cpp @@ -26,13 +26,11 @@ #include "common/stream.h" #include "common/system.h" #include "common/textconsole.h" +#include "graphics/surface.h" namespace Video { FlicDecoder::FlicDecoder() { - _paletteChanged = false; - _fileStream = 0; - _surface = 0; } FlicDecoder::~FlicDecoder() { @@ -42,35 +40,59 @@ FlicDecoder::~FlicDecoder() { bool FlicDecoder::loadStream(Common::SeekableReadStream *stream) { close(); - _fileStream = stream; - - /* uint32 frameSize = */ _fileStream->readUint32LE(); - uint16 frameType = _fileStream->readUint16LE(); + /* uint32 frameSize = */ stream->readUint32LE(); + uint16 frameType = stream->readUint16LE(); // Check FLC magic number if (frameType != 0xAF12) { - warning("FlicDecoder::FlicDecoder(): attempted to load non-FLC data (type = 0x%04X)", frameType); - delete _fileStream; - _fileStream = 0; + warning("FlicDecoder::loadStream(): attempted to load non-FLC data (type = 0x%04X)", frameType); return false; } - - _frameCount = _fileStream->readUint16LE(); - uint16 width = _fileStream->readUint16LE(); - uint16 height = _fileStream->readUint16LE(); - uint16 colorDepth = _fileStream->readUint16LE(); + uint16 frameCount = stream->readUint16LE(); + uint16 width = stream->readUint16LE(); + uint16 height = stream->readUint16LE(); + uint16 colorDepth = stream->readUint16LE(); if (colorDepth != 8) { - warning("FlicDecoder::FlicDecoder(): attempted to load an FLC with a palette of color depth %d. Only 8-bit color palettes are supported", frameType); - delete _fileStream; - _fileStream = 0; + warning("FlicDecoder::loadStream(): attempted to load an FLC with a palette of color depth %d. Only 8-bit color palettes are supported", colorDepth); return false; } + addTrack(new FlicVideoTrack(stream, frameCount, width, height)); + return true; +} + +const Common::List<Common::Rect> *FlicDecoder::getDirtyRects() const { + const Track *track = getTrack(0); + + if (track) + return ((const FlicVideoTrack *)track)->getDirtyRects(); + + return 0; +} + +void FlicDecoder::clearDirtyRects() { + Track *track = getTrack(0); + + if (track) + ((FlicVideoTrack *)track)->clearDirtyRects(); +} + +void FlicDecoder::copyDirtyRectsToBuffer(uint8 *dst, uint pitch) { + Track *track = getTrack(0); + + if (track) + ((FlicVideoTrack *)track)->copyDirtyRectsToBuffer(dst, pitch); +} + +FlicDecoder::FlicVideoTrack::FlicVideoTrack(Common::SeekableReadStream *stream, uint16 frameCount, uint16 width, uint16 height) { + _fileStream = stream; + _frameCount = frameCount; + _fileStream->readUint16LE(); // flags // Note: The normal delay is a 32-bit integer (dword), whereas the overridden delay is a 16-bit integer (word) // the frame delay is the FLIC "speed", in milliseconds. - _frameRate = Common::Rational(1000, _fileStream->readUint32LE()); + _frameDelay = _startFrameDelay = _fileStream->readUint32LE(); _fileStream->seek(80); _offsetFrame1 = _fileStream->readUint32LE(); @@ -78,121 +100,63 @@ bool FlicDecoder::loadStream(Common::SeekableReadStream *stream) { _surface = new Graphics::Surface(); _surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8()); - _palette = (byte *)malloc(3 * 256); + _palette = new byte[3 * 256]; memset(_palette, 0, 3 * 256); - _paletteChanged = false; + _dirtyPalette = false; + + _curFrame = -1; + _nextFrameStartTime = 0; + _atRingFrame = false; // Seek to the first frame _fileStream->seek(_offsetFrame1); - return true; } -void FlicDecoder::close() { - if (!_fileStream) - return; - +FlicDecoder::FlicVideoTrack::~FlicVideoTrack() { delete _fileStream; - _fileStream = 0; + delete[] _palette; _surface->free(); delete _surface; - _surface = 0; - - free(_palette); - _dirtyRects.clear(); - - reset(); } -void FlicDecoder::decodeByteRun(uint8 *data) { - byte *ptr = (byte *)_surface->pixels; - while ((int32)(ptr - (byte *)_surface->pixels) < (getWidth() * getHeight())) { - int chunks = *data++; - while (chunks--) { - int count = (int8)*data++; - if (count > 0) { - memset(ptr, *data++, count); - } else { - count = -count; - memcpy(ptr, data, count); - data += count; - } - ptr += count; - } - } - - // Redraw - _dirtyRects.clear(); - _dirtyRects.push_back(Common::Rect(0, 0, getWidth(), getHeight())); +bool FlicDecoder::FlicVideoTrack::endOfTrack() const { + return getCurFrame() >= getFrameCount() - 1; } -#define OP_PACKETCOUNT 0 -#define OP_UNDEFINED 1 -#define OP_LASTPIXEL 2 -#define OP_LINESKIPCOUNT 3 - -void FlicDecoder::decodeDeltaFLC(uint8 *data) { - uint16 linesInChunk = READ_LE_UINT16(data); data += 2; - uint16 currentLine = 0; - uint16 packetCount = 0; +bool FlicDecoder::FlicVideoTrack::rewind() { + _curFrame = -1; + _nextFrameStartTime = 0; - while (linesInChunk--) { - uint16 opcode; - - // First process all the opcodes. - do { - opcode = READ_LE_UINT16(data); data += 2; + if (endOfTrack() && _fileStream->pos() < _fileStream->size()) + _atRingFrame = true; + else + _fileStream->seek(_offsetFrame1); - switch ((opcode >> 14) & 3) { - case OP_PACKETCOUNT: - packetCount = opcode; - break; - case OP_UNDEFINED: - break; - case OP_LASTPIXEL: - *((byte *)_surface->pixels + currentLine * getWidth() + getWidth() - 1) = (opcode & 0xFF); - _dirtyRects.push_back(Common::Rect(getWidth() - 1, currentLine, getWidth(), currentLine + 1)); - break; - case OP_LINESKIPCOUNT: - currentLine += -(int16)opcode; - break; - } - } while (((opcode >> 14) & 3) != OP_PACKETCOUNT); + _frameDelay = _startFrameDelay; + return true; +} - uint16 column = 0; +uint16 FlicDecoder::FlicVideoTrack::getWidth() const { + return _surface->w; +} - // Now interpret the RLE data - while (packetCount--) { - column += *data++; - int rleCount = (int8)*data++; - if (rleCount > 0) { - memcpy((byte *)_surface->pixels + (currentLine * getWidth()) + column, data, rleCount * 2); - data += rleCount * 2; - _dirtyRects.push_back(Common::Rect(column, currentLine, column + rleCount * 2, currentLine + 1)); - } else if (rleCount < 0) { - rleCount = -rleCount; - uint16 dataWord = READ_UINT16(data); data += 2; - for (int i = 0; i < rleCount; ++i) { - WRITE_UINT16((byte *)_surface->pixels + currentLine * getWidth() + column + i * 2, dataWord); - } - _dirtyRects.push_back(Common::Rect(column, currentLine, column + rleCount * 2, currentLine + 1)); - } else { // End of cutscene ? - return; - } - column += rleCount * 2; - } +uint16 FlicDecoder::FlicVideoTrack::getHeight() const { + return _surface->h; +} - currentLine++; - } +Graphics::PixelFormat FlicDecoder::FlicVideoTrack::getPixelFormat() const { + return _surface->format; } #define FLI_SETPAL 4 #define FLI_SS2 7 #define FLI_BRUN 15 +#define FLI_COPY 16 #define PSTAMP 18 #define FRAME_TYPE 0xF1FA -const Graphics::Surface *FlicDecoder::decodeNextFrame() { +const Graphics::Surface *FlicDecoder::FlicVideoTrack::decodeNextFrame() { // Read chunk uint32 frameSize = _fileStream->readUint32LE(); uint16 frameType = _fileStream->readUint16LE(); @@ -201,15 +165,12 @@ const Graphics::Surface *FlicDecoder::decodeNextFrame() { switch (frameType) { case FRAME_TYPE: { - // FIXME: FLIC should be switched over to a variable frame rate VideoDecoder to handle - // this properly. - chunkCount = _fileStream->readUint16LE(); // Note: The overridden delay is a 16-bit integer (word), whereas the normal delay is a 32-bit integer (dword) // the frame delay is the FLIC "speed", in milliseconds. uint16 newFrameDelay = _fileStream->readUint16LE(); // "speed", in milliseconds if (newFrameDelay > 0) - _frameRate = Common::Rational(1000, newFrameDelay); + _frameDelay = newFrameDelay; _fileStream->readUint16LE(); // reserved, always 0 uint16 newWidth = _fileStream->readUint16LE(); @@ -240,10 +201,11 @@ const Graphics::Surface *FlicDecoder::decodeNextFrame() { frameType = _fileStream->readUint16LE(); uint8 *data = new uint8[frameSize - 6]; _fileStream->read(data, frameSize - 6); + switch (frameType) { case FLI_SETPAL: unpackPalette(data); - _paletteChanged = true; + _dirtyPalette = true; break; case FLI_SS2: decodeDeltaFLC(data); @@ -251,6 +213,9 @@ const Graphics::Surface *FlicDecoder::decodeNextFrame() { case FLI_BRUN: decodeByteRun(data); break; + case FLI_COPY: + copyFrame(data); + break; case PSTAMP: /* PSTAMP - skip for now */ break; @@ -264,26 +229,119 @@ const Graphics::Surface *FlicDecoder::decodeNextFrame() { } _curFrame++; + _nextFrameStartTime += _frameDelay; - // If we just processed the ring frame, set the next frame - if (_curFrame == (int32)_frameCount) { - _curFrame = 0; + if (_atRingFrame) { + // If we decoded the ring frame, seek to the second frame + _atRingFrame = false; _fileStream->seek(_offsetFrame2); } - if (_curFrame == 0) - _startTime = g_system->getMillis(); - return _surface; } -void FlicDecoder::reset() { - FixedRateVideoDecoder::reset(); - if (_fileStream) - _fileStream->seek(_offsetFrame1); +void FlicDecoder::FlicVideoTrack::copyDirtyRectsToBuffer(uint8 *dst, uint pitch) { + for (Common::List<Common::Rect>::const_iterator it = _dirtyRects.begin(); it != _dirtyRects.end(); ++it) { + for (int y = (*it).top; y < (*it).bottom; ++y) { + const int x = (*it).left; + memcpy(dst + y * pitch + x, (byte *)_surface->pixels + y * getWidth() + x, (*it).right - x); + } + } + + clearDirtyRects(); } -void FlicDecoder::unpackPalette(uint8 *data) { +void FlicDecoder::FlicVideoTrack::copyFrame(uint8 *data) { + memcpy((byte *)_surface->pixels, data, getWidth() * getHeight()); + + // Redraw + _dirtyRects.clear(); + _dirtyRects.push_back(Common::Rect(0, 0, getWidth(), getHeight())); +} + +void FlicDecoder::FlicVideoTrack::decodeByteRun(uint8 *data) { + byte *ptr = (byte *)_surface->pixels; + while ((int32)(ptr - (byte *)_surface->pixels) < (getWidth() * getHeight())) { + int chunks = *data++; + while (chunks--) { + int count = (int8)*data++; + if (count > 0) { + memset(ptr, *data++, count); + } else { + count = -count; + memcpy(ptr, data, count); + data += count; + } + ptr += count; + } + } + + // Redraw + _dirtyRects.clear(); + _dirtyRects.push_back(Common::Rect(0, 0, getWidth(), getHeight())); +} + +#define OP_PACKETCOUNT 0 +#define OP_UNDEFINED 1 +#define OP_LASTPIXEL 2 +#define OP_LINESKIPCOUNT 3 + +void FlicDecoder::FlicVideoTrack::decodeDeltaFLC(uint8 *data) { + uint16 linesInChunk = READ_LE_UINT16(data); data += 2; + uint16 currentLine = 0; + uint16 packetCount = 0; + + while (linesInChunk--) { + uint16 opcode; + + // First process all the opcodes. + do { + opcode = READ_LE_UINT16(data); data += 2; + + switch ((opcode >> 14) & 3) { + case OP_PACKETCOUNT: + packetCount = opcode; + break; + case OP_UNDEFINED: + break; + case OP_LASTPIXEL: + *((byte *)_surface->pixels + currentLine * getWidth() + getWidth() - 1) = (opcode & 0xFF); + _dirtyRects.push_back(Common::Rect(getWidth() - 1, currentLine, getWidth(), currentLine + 1)); + break; + case OP_LINESKIPCOUNT: + currentLine += -(int16)opcode; + break; + } + } while (((opcode >> 14) & 3) != OP_PACKETCOUNT); + + uint16 column = 0; + + // Now interpret the RLE data + while (packetCount--) { + column += *data++; + int rleCount = (int8)*data++; + if (rleCount > 0) { + memcpy((byte *)_surface->pixels + (currentLine * getWidth()) + column, data, rleCount * 2); + data += rleCount * 2; + _dirtyRects.push_back(Common::Rect(column, currentLine, column + rleCount * 2, currentLine + 1)); + } else if (rleCount < 0) { + rleCount = -rleCount; + uint16 dataWord = READ_UINT16(data); data += 2; + for (int i = 0; i < rleCount; ++i) { + WRITE_UINT16((byte *)_surface->pixels + currentLine * getWidth() + column + i * 2, dataWord); + } + _dirtyRects.push_back(Common::Rect(column, currentLine, column + rleCount * 2, currentLine + 1)); + } else { // End of cutscene ? + return; + } + column += rleCount * 2; + } + + currentLine++; + } +} + +void FlicDecoder::FlicVideoTrack::unpackPalette(uint8 *data) { uint16 numPackets = READ_LE_UINT16(data); data += 2; if (0 == READ_LE_UINT16(data)) { //special case @@ -308,14 +366,4 @@ void FlicDecoder::unpackPalette(uint8 *data) { } } -void FlicDecoder::copyDirtyRectsToBuffer(uint8 *dst, uint pitch) { - for (Common::List<Common::Rect>::const_iterator it = _dirtyRects.begin(); it != _dirtyRects.end(); ++it) { - for (int y = (*it).top; y < (*it).bottom; ++y) { - const int x = (*it).left; - memcpy(dst + y * pitch + x, (byte *)_surface->pixels + y * getWidth() + x, (*it).right - x); - } - } - _dirtyRects.clear(); -} - } // End of namespace Video diff --git a/video/flic_decoder.h b/video/flic_decoder.h index 9badc3da2e..c20a092a32 100644 --- a/video/flic_decoder.h +++ b/video/flic_decoder.h @@ -25,15 +25,17 @@ #include "video/video_decoder.h" #include "common/list.h" -#include "common/rational.h" #include "common/rect.h" -#include "graphics/pixelformat.h" -#include "graphics/surface.h" namespace Common { class SeekableReadStream; } +namespace Graphics { +struct PixelFormat; +struct Surface; +} + namespace Video { /** @@ -42,58 +44,64 @@ namespace Video { * Video decoder used in engines: * - tucker */ -class FlicDecoder : public FixedRateVideoDecoder { +class FlicDecoder : public VideoDecoder { public: FlicDecoder(); virtual ~FlicDecoder(); - /** - * Load a video file - * @param stream the stream to load - */ bool loadStream(Common::SeekableReadStream *stream); - void close(); - - /** - * Decode the next frame and return the frame's surface - * @note the return surface should *not* be freed - * @note this may return 0, in which case the last frame should be kept on screen - */ - const Graphics::Surface *decodeNextFrame(); - - bool isVideoLoaded() const { return _fileStream != 0; } - uint16 getWidth() const { return _surface->w; } - uint16 getHeight() const { return _surface->h; } - uint32 getFrameCount() const { return _frameCount; } - Graphics::PixelFormat getPixelFormat() const { return Graphics::PixelFormat::createFormatCLUT8(); } - - const Common::List<Common::Rect> *getDirtyRects() const { return &_dirtyRects; } - void clearDirtyRects() { _dirtyRects.clear(); } - void copyDirtyRectsToBuffer(uint8 *dst, uint pitch); - - const byte *getPalette() { _paletteChanged = false; return _palette; } - bool hasDirtyPalette() const { return _paletteChanged; } - void reset(); -protected: - Common::Rational getFrameRate() const { return _frameRate; } + const Common::List<Common::Rect> *getDirtyRects() const; + void clearDirtyRects(); + void copyDirtyRectsToBuffer(uint8 *dst, uint pitch); private: - uint16 _offsetFrame1; - uint16 _offsetFrame2; - byte *_palette; - bool _paletteChanged; - - void decodeByteRun(uint8 *data); - void decodeDeltaFLC(uint8 *data); - void unpackPalette(uint8 *mem); - - Common::SeekableReadStream *_fileStream; - Graphics::Surface *_surface; - uint32 _frameCount; - Common::Rational _frameRate; - - Common::List<Common::Rect> _dirtyRects; + class FlicVideoTrack : public VideoTrack { + public: + FlicVideoTrack(Common::SeekableReadStream *stream, uint16 frameCount, uint16 width, uint16 height); + ~FlicVideoTrack(); + + bool endOfTrack() const; + bool isRewindable() const { return true; } + bool rewind(); + + uint16 getWidth() const; + uint16 getHeight() const; + Graphics::PixelFormat getPixelFormat() const; + int getCurFrame() const { return _curFrame; } + int getFrameCount() const { return _frameCount; } + uint32 getNextFrameStartTime() const { return _nextFrameStartTime; } + const Graphics::Surface *decodeNextFrame(); + const byte *getPalette() const { _dirtyPalette = false; return _palette; } + bool hasDirtyPalette() const { return _dirtyPalette; } + + const Common::List<Common::Rect> *getDirtyRects() const { return &_dirtyRects; } + void clearDirtyRects() { _dirtyRects.clear(); } + void copyDirtyRectsToBuffer(uint8 *dst, uint pitch); + + private: + Common::SeekableReadStream *_fileStream; + Graphics::Surface *_surface; + + int _curFrame; + bool _atRingFrame; + + uint16 _offsetFrame1; + uint16 _offsetFrame2; + byte *_palette; + mutable bool _dirtyPalette; + + uint32 _frameCount; + uint32 _frameDelay, _startFrameDelay; + uint32 _nextFrameStartTime; + + Common::List<Common::Rect> _dirtyRects; + + void copyFrame(uint8 *data); + void decodeByteRun(uint8 *data); + void decodeDeltaFLC(uint8 *data); + void unpackPalette(uint8 *mem); + }; }; } // End of namespace Video diff --git a/video/module.mk b/video/module.mk index cebd403ca2..287e14ce18 100644 --- a/video/module.mk +++ b/video/module.mk @@ -26,5 +26,10 @@ MODULE_OBJS += \ bink_decoder.o endif +ifdef USE_THEORADEC +MODULE_OBJS += \ + theora_decoder.o +endif + # Include common rules include $(srcdir)/rules.mk diff --git a/video/psx_decoder.cpp b/video/psx_decoder.cpp index df91a2badd..57c8972ee5 100644 --- a/video/psx_decoder.cpp +++ b/video/psx_decoder.cpp @@ -149,22 +149,12 @@ static const uint32 s_huffmanACSymbols[AC_CODE_COUNT] = { END_OF_BLOCK }; -PSXStreamDecoder::PSXStreamDecoder(CDSpeed speed, uint32 frameCount) : _nextFrameStartTime(0, speed), _frameCount(frameCount) { +PSXStreamDecoder::PSXStreamDecoder(CDSpeed speed, uint32 frameCount) : _speed(speed), _frameCount(frameCount) { _stream = 0; - _audStream = 0; - _surface = new Graphics::Surface(); - _yBuffer = _cbBuffer = _crBuffer = 0; - _acHuffman = new Common::Huffman(0, AC_CODE_COUNT, s_huffmanACCodes, s_huffmanACLengths, s_huffmanACSymbols); - _dcHuffmanChroma = new Common::Huffman(0, DC_CODE_COUNT, s_huffmanDCChromaCodes, s_huffmanDCChromaLengths, s_huffmanDCSymbols); - _dcHuffmanLuma = new Common::Huffman(0, DC_CODE_COUNT, s_huffmanDCLumaCodes, s_huffmanDCLumaLengths, s_huffmanDCSymbols); } PSXStreamDecoder::~PSXStreamDecoder() { close(); - delete _surface; - delete _acHuffman; - delete _dcHuffmanLuma; - delete _dcHuffmanChroma; } #define RAW_CD_SECTOR_SIZE 2352 @@ -178,95 +168,30 @@ bool PSXStreamDecoder::loadStream(Common::SeekableReadStream *stream) { close(); _stream = stream; - - Common::SeekableReadStream *sector = readSector(); - - if (!sector) { - close(); - return false; - } - - // Rip out video info from the first frame - sector->seek(18); - byte sectorType = sector->readByte() & CDXA_TYPE_MASK; - - if (sectorType != CDXA_TYPE_VIDEO && sectorType != CDXA_TYPE_DATA) { - close(); - return false; - } - - sector->seek(40); - - uint16 width = sector->readUint16LE(); - uint16 height = sector->readUint16LE(); - _surface->create(width, height, g_system->getScreenFormat()); - - _macroBlocksW = (width + 15) / 16; - _macroBlocksH = (height + 15) / 16; - _yBuffer = new byte[_macroBlocksW * _macroBlocksH * 16 * 16]; - _cbBuffer = new byte[_macroBlocksW * _macroBlocksH * 8 * 8]; - _crBuffer = new byte[_macroBlocksW * _macroBlocksH * 8 * 8]; - - delete sector; - _stream->seek(0); + readNextPacket(); return true; } void PSXStreamDecoder::close() { - if (!_stream) - return; + VideoDecoder::close(); + _audioTrack = 0; + _videoTrack = 0; + _frameCount = 0; delete _stream; _stream = 0; - - // Deinitialize sound - g_system->getMixer()->stopHandle(_audHandle); - _audStream = 0; - - _surface->free(); - - memset(&_adpcmStatus, 0, sizeof(_adpcmStatus)); - - _macroBlocksW = _macroBlocksH = 0; - delete[] _yBuffer; _yBuffer = 0; - delete[] _cbBuffer; _cbBuffer = 0; - delete[] _crBuffer; _crBuffer = 0; - - reset(); -} - -uint32 PSXStreamDecoder::getTime() const { - // TODO: Currently, the audio is always after the video so using this - // can often lead to gaps in the audio... - //if (_audStream) - // return _mixer->getSoundElapsedTime(_audHandle); - - return VideoDecoder::getTime(); -} - -uint32 PSXStreamDecoder::getTimeToNextFrame() const { - if (!isVideoLoaded() || endOfVideo()) - return 0; - - uint32 nextTimeMillis = _nextFrameStartTime.msecs(); - uint32 elapsedTime = getTime(); - - if (elapsedTime > nextTimeMillis) - return 0; - - return nextTimeMillis - elapsedTime; } #define VIDEO_DATA_CHUNK_SIZE 2016 #define VIDEO_DATA_HEADER_SIZE 56 -const Graphics::Surface *PSXStreamDecoder::decodeNextFrame() { +void PSXStreamDecoder::readNextPacket() { Common::SeekableReadStream *sector = 0; byte *partialFrame = 0; int sectorsRead = 0; - while (!endOfVideo()) { + while (_stream->pos() < _stream->size()) { sector = readSector(); sectorsRead++; @@ -284,6 +209,11 @@ const Graphics::Surface *PSXStreamDecoder::decodeNextFrame() { case CDXA_TYPE_DATA: case CDXA_TYPE_VIDEO: if (track == 1) { + if (!_videoTrack) { + _videoTrack = new PSXVideoTrack(sector, _speed, _frameCount); + addTrack(_videoTrack); + } + sector->seek(28); uint16 curSector = sector->readUint16LE(); uint16 sectorCount = sector->readUint16LE(); @@ -303,35 +233,27 @@ const Graphics::Surface *PSXStreamDecoder::decodeNextFrame() { // Done assembling the frame Common::SeekableReadStream *frame = new Common::MemoryReadStream(partialFrame, frameSize, DisposeAfterUse::YES); - decodeFrame(frame); - + _videoTrack->decodeFrame(frame, sectorsRead); + delete frame; delete sector; - - _curFrame++; - if (_curFrame == 0) - _startTime = g_system->getMillis(); - - // Increase the time by the amount of sectors we read - // One may notice that this is still not the most precise - // method since a frame takes up the time its sectors took - // up instead of the amount of time it takes the next frame - // to be read from the sectors. The actual frame rate should - // be constant instead of variable, so the slight difference - // in a frame's showing time is negligible (1/150 of a second). - _nextFrameStartTime = _nextFrameStartTime.addFrames(sectorsRead); - - return _surface; + return; } } else error("Unhandled multi-track video"); break; case CDXA_TYPE_AUDIO: // We only handle one audio channel so far - if (track == 1) - queueAudioFromSector(sector); - else + if (track == 1) { + if (!_audioTrack) { + _audioTrack = new PSXAudioTrack(sector); + addTrack(_audioTrack); + } + + _audioTrack->queueAudioFromSector(sector); + } else { warning("Unhandled multi-track audio"); + } break; default: // This shows up way too often, but the other sectors @@ -343,7 +265,19 @@ const Graphics::Surface *PSXStreamDecoder::decodeNextFrame() { delete sector; } - return 0; + if (_stream->pos() >= _stream->size()) { + if (_videoTrack) + _videoTrack->setEndOfTrack(); + + if (_audioTrack) + _audioTrack->setEndOfTrack(); + } +} + +bool PSXStreamDecoder::useAudioSync() const { + // Audio sync is disabled since most audio data comes after video + // data. + return false; } static const byte s_syncHeader[12] = { 0x00, 0xff ,0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 }; @@ -363,7 +297,7 @@ Common::SeekableReadStream *PSXStreamDecoder::readSector() { // Ha! It's palindromic! #define AUDIO_DATA_CHUNK_SIZE 2304 -#define AUDIO_DATA_SAMPLE_COUNT 4032 +#define AUDIO_DATA_SAMPLE_COUNT 4032 static const int s_xaTable[5][2] = { { 0, 0 }, @@ -373,20 +307,29 @@ static const int s_xaTable[5][2] = { { 122, -60 } }; -void PSXStreamDecoder::queueAudioFromSector(Common::SeekableReadStream *sector) { +PSXStreamDecoder::PSXAudioTrack::PSXAudioTrack(Common::SeekableReadStream *sector) { assert(sector); + _endOfTrack = false; + + sector->seek(19); + byte format = sector->readByte(); + bool stereo = (format & (1 << 0)) != 0; + uint rate = (format & (1 << 2)) ? 18900 : 37800; + _audStream = Audio::makeQueuingAudioStream(rate, stereo); - if (!_audStream) { - // Initialize audio stream - sector->seek(19); - byte format = sector->readByte(); + memset(&_adpcmStatus, 0, sizeof(_adpcmStatus)); +} - bool stereo = (format & (1 << 0)) != 0; - uint rate = (format & (1 << 2)) ? 18900 : 37800; +PSXStreamDecoder::PSXAudioTrack::~PSXAudioTrack() { + delete _audStream; +} - _audStream = Audio::makeQueuingAudioStream(rate, stereo); - g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &_audHandle, _audStream, -1, getVolume(), getBalance()); - } +bool PSXStreamDecoder::PSXAudioTrack::endOfTrack() const { + return AudioTrack::endOfTrack() && _endOfTrack; +} + +void PSXStreamDecoder::PSXAudioTrack::queueAudioFromSector(Common::SeekableReadStream *sector) { + assert(sector); sector->seek(24); @@ -472,7 +415,54 @@ void PSXStreamDecoder::queueAudioFromSector(Common::SeekableReadStream *sector) delete[] buf; } -void PSXStreamDecoder::decodeFrame(Common::SeekableReadStream *frame) { +Audio::AudioStream *PSXStreamDecoder::PSXAudioTrack::getAudioStream() const { + return _audStream; +} + + +PSXStreamDecoder::PSXVideoTrack::PSXVideoTrack(Common::SeekableReadStream *firstSector, CDSpeed speed, int frameCount) : _nextFrameStartTime(0, speed), _frameCount(frameCount) { + assert(firstSector); + + firstSector->seek(40); + uint16 width = firstSector->readUint16LE(); + uint16 height = firstSector->readUint16LE(); + _surface = new Graphics::Surface(); + _surface->create(width, height, g_system->getScreenFormat()); + + _macroBlocksW = (width + 15) / 16; + _macroBlocksH = (height + 15) / 16; + _yBuffer = new byte[_macroBlocksW * _macroBlocksH * 16 * 16]; + _cbBuffer = new byte[_macroBlocksW * _macroBlocksH * 8 * 8]; + _crBuffer = new byte[_macroBlocksW * _macroBlocksH * 8 * 8]; + + _endOfTrack = false; + _curFrame = -1; + _acHuffman = new Common::Huffman(0, AC_CODE_COUNT, s_huffmanACCodes, s_huffmanACLengths, s_huffmanACSymbols); + _dcHuffmanChroma = new Common::Huffman(0, DC_CODE_COUNT, s_huffmanDCChromaCodes, s_huffmanDCChromaLengths, s_huffmanDCSymbols); + _dcHuffmanLuma = new Common::Huffman(0, DC_CODE_COUNT, s_huffmanDCLumaCodes, s_huffmanDCLumaLengths, s_huffmanDCSymbols); +} + +PSXStreamDecoder::PSXVideoTrack::~PSXVideoTrack() { + _surface->free(); + delete _surface; + + delete[] _yBuffer; + delete[] _cbBuffer; + delete[] _crBuffer; + delete _acHuffman; + delete _dcHuffmanChroma; + delete _dcHuffmanLuma; +} + +uint32 PSXStreamDecoder::PSXVideoTrack::getNextFrameStartTime() const { + return _nextFrameStartTime.msecs(); +} + +const Graphics::Surface *PSXStreamDecoder::PSXVideoTrack::decodeNextFrame() { + return _surface; +} + +void PSXStreamDecoder::PSXVideoTrack::decodeFrame(Common::SeekableReadStream *frame, uint sectorCount) { // A frame is essentially an MPEG-1 intra frame Common::BitStream16LEMSB bits(frame); @@ -493,10 +483,21 @@ void PSXStreamDecoder::decodeFrame(Common::SeekableReadStream *frame) { decodeMacroBlock(&bits, mbX, mbY, scale, version); // Output data onto the frame - Graphics::convertYUV420ToRGB(_surface, _yBuffer, _cbBuffer, _crBuffer, _surface->w, _surface->h, _macroBlocksW * 16, _macroBlocksW * 8); + YUVToRGBMan.convert420(_surface, Graphics::YUVToRGBManager::kScaleFull, _yBuffer, _cbBuffer, _crBuffer, _surface->w, _surface->h, _macroBlocksW * 16, _macroBlocksW * 8); + + _curFrame++; + + // Increase the time by the amount of sectors we read + // One may notice that this is still not the most precise + // method since a frame takes up the time its sectors took + // up instead of the amount of time it takes the next frame + // to be read from the sectors. The actual frame rate should + // be constant instead of variable, so the slight difference + // in a frame's showing time is negligible (1/150 of a second). + _nextFrameStartTime = _nextFrameStartTime.addFrames(sectorCount); } -void PSXStreamDecoder::decodeMacroBlock(Common::BitStream *bits, int mbX, int mbY, uint16 scale, uint16 version) { +void PSXStreamDecoder::PSXVideoTrack::decodeMacroBlock(Common::BitStream *bits, int mbX, int mbY, uint16 scale, uint16 version) { int pitchY = _macroBlocksW * 16; int pitchC = _macroBlocksW * 8; @@ -533,7 +534,7 @@ static const byte s_quantizationTable[8 * 8] = { 27, 29, 35, 38, 46, 56, 69, 83 }; -void PSXStreamDecoder::dequantizeBlock(int *coefficients, float *block, uint16 scale) { +void PSXStreamDecoder::PSXVideoTrack::dequantizeBlock(int *coefficients, float *block, uint16 scale) { // Dequantize the data, un-zig-zagging as we go along for (int i = 0; i < 8 * 8; i++) { if (i == 0) // Special case for the DC coefficient @@ -543,7 +544,7 @@ void PSXStreamDecoder::dequantizeBlock(int *coefficients, float *block, uint16 s } } -int PSXStreamDecoder::readDC(Common::BitStream *bits, uint16 version, PlaneType plane) { +int PSXStreamDecoder::PSXVideoTrack::readDC(Common::BitStream *bits, uint16 version, PlaneType plane) { // Version 2 just has its coefficient as 10-bits if (version == 2) return readSignedCoefficient(bits); @@ -573,7 +574,7 @@ int PSXStreamDecoder::readDC(Common::BitStream *bits, uint16 version, PlaneType if (count > 63) \ error("PSXStreamDecoder::readAC(): Too many coefficients") -void PSXStreamDecoder::readAC(Common::BitStream *bits, int *block) { +void PSXStreamDecoder::PSXVideoTrack::readAC(Common::BitStream *bits, int *block) { // Clear the block first for (int i = 0; i < 63; i++) block[i] = 0; @@ -608,7 +609,7 @@ void PSXStreamDecoder::readAC(Common::BitStream *bits, int *block) { } } -int PSXStreamDecoder::readSignedCoefficient(Common::BitStream *bits) { +int PSXStreamDecoder::PSXVideoTrack::readSignedCoefficient(Common::BitStream *bits) { uint val = bits->getBits(10); // extend the sign @@ -630,7 +631,7 @@ static const double s_idct8x8[8][8] = { { 0.353553390593274, -0.490392640201615, 0.461939766255643, -0.415734806151273, 0.353553390593273, -0.277785116509801, 0.191341716182545, -0.097545161008064 } }; -void PSXStreamDecoder::idct(float *dequantData, float *result) { +void PSXStreamDecoder::PSXVideoTrack::idct(float *dequantData, float *result) { // IDCT code based on JPEG's IDCT code // TODO: Switch to the integer-based one mentioned in the docs // This is by far the costliest operation here @@ -669,7 +670,7 @@ void PSXStreamDecoder::idct(float *dequantData, float *result) { } } -void PSXStreamDecoder::decodeBlock(Common::BitStream *bits, byte *block, int pitch, uint16 scale, uint16 version, PlaneType plane) { +void PSXStreamDecoder::PSXVideoTrack::decodeBlock(Common::BitStream *bits, byte *block, int pitch, uint16 scale, uint16 version, PlaneType plane) { // Version 2 just has signed 10 bits for DC // Version 3 has them huffman coded int coefficients[8 * 8]; @@ -686,22 +687,13 @@ void PSXStreamDecoder::decodeBlock(Common::BitStream *bits, byte *block, int pit // Now output the data for (int y = 0; y < 8; y++) { - byte *start = block + pitch * y; + byte *dst = block + pitch * y; // Convert the result to be in the range [0, 255] for (int x = 0; x < 8; x++) - *start++ = (int)CLIP<float>(idctData[y * 8 + x], -128.0f, 127.0f) + 128; + *dst++ = (int)CLIP<float>(idctData[y * 8 + x], -128.0f, 127.0f) + 128; } } -void PSXStreamDecoder::updateVolume() { - if (g_system->getMixer()->isSoundHandleActive(_audHandle)) - g_system->getMixer()->setChannelVolume(_audHandle, getVolume()); -} - -void PSXStreamDecoder::updateBalance() { - if (g_system->getMixer()->isSoundHandleActive(_audHandle)) - g_system->getMixer()->setChannelBalance(_audHandle, getBalance()); -} } // End of namespace Video diff --git a/video/psx_decoder.h b/video/psx_decoder.h index 4364ec4bbb..d1d5204e37 100644 --- a/video/psx_decoder.h +++ b/video/psx_decoder.h @@ -71,59 +71,85 @@ public: bool loadStream(Common::SeekableReadStream *stream); void close(); - bool isVideoLoaded() const { return _stream != 0; } - uint16 getWidth() const { return _surface->w; } - uint16 getHeight() const { return _surface->h; } - uint32 getFrameCount() const { return _frameCount; } - uint32 getTime() const; - uint32 getTimeToNextFrame() const; - const Graphics::Surface *decodeNextFrame(); - Graphics::PixelFormat getPixelFormat() const { return _surface->format; } - bool endOfVideo() const { return _stream->pos() >= _stream->size(); } - protected: - // VideoDecoder API - void updateVolume(); - void updateBalance(); + void readNextPacket(); + bool useAudioSync() const; private: - void initCommon(); - Common::SeekableReadStream *_stream; - Graphics::Surface *_surface; + class PSXVideoTrack : public VideoTrack { + public: + PSXVideoTrack(Common::SeekableReadStream *firstSector, CDSpeed speed, int frameCount); + ~PSXVideoTrack(); + + uint16 getWidth() const { return _surface->w; } + uint16 getHeight() const { return _surface->h; } + Graphics::PixelFormat getPixelFormat() const { return _surface->format; } + bool endOfTrack() const { return _endOfTrack; } + int getCurFrame() const { return _curFrame; } + int getFrameCount() const { return _frameCount; } + uint32 getNextFrameStartTime() const; + const Graphics::Surface *decodeNextFrame(); + + void setEndOfTrack() { _endOfTrack = true; } + void decodeFrame(Common::SeekableReadStream *frame, uint sectorCount); + + private: + Graphics::Surface *_surface; + uint32 _frameCount; + Audio::Timestamp _nextFrameStartTime; + bool _endOfTrack; + int _curFrame; + + enum PlaneType { + kPlaneY = 0, + kPlaneU = 1, + kPlaneV = 2 + }; + + uint16 _macroBlocksW, _macroBlocksH; + byte *_yBuffer, *_cbBuffer, *_crBuffer; + void decodeMacroBlock(Common::BitStream *bits, int mbX, int mbY, uint16 scale, uint16 version); + void decodeBlock(Common::BitStream *bits, byte *block, int pitch, uint16 scale, uint16 version, PlaneType plane); + + void readAC(Common::BitStream *bits, int *block); + Common::Huffman *_acHuffman; + + int readDC(Common::BitStream *bits, uint16 version, PlaneType plane); + Common::Huffman *_dcHuffmanLuma, *_dcHuffmanChroma; + int _lastDC[3]; + + void dequantizeBlock(int *coefficients, float *block, uint16 scale); + void idct(float *dequantData, float *result); + int readSignedCoefficient(Common::BitStream *bits); + }; - uint32 _frameCount; - Audio::Timestamp _nextFrameStartTime; + class PSXAudioTrack : public AudioTrack { + public: + PSXAudioTrack(Common::SeekableReadStream *sector); + ~PSXAudioTrack(); - Audio::SoundHandle _audHandle; - Audio::QueuingAudioStream *_audStream; - void queueAudioFromSector(Common::SeekableReadStream *sector); + bool endOfTrack() const; - enum PlaneType { - kPlaneY = 0, - kPlaneU = 1, - kPlaneV = 2 - }; + void setEndOfTrack() { _endOfTrack = true; } + void queueAudioFromSector(Common::SeekableReadStream *sector); - uint16 _macroBlocksW, _macroBlocksH; - byte *_yBuffer, *_cbBuffer, *_crBuffer; - void decodeFrame(Common::SeekableReadStream *frame); - void decodeMacroBlock(Common::BitStream *bits, int mbX, int mbY, uint16 scale, uint16 version); - void decodeBlock(Common::BitStream *bits, byte *block, int pitch, uint16 scale, uint16 version, PlaneType plane); + private: + Audio::AudioStream *getAudioStream() const; - void readAC(Common::BitStream *bits, int *block); - Common::Huffman *_acHuffman; + Audio::QueuingAudioStream *_audStream; - int readDC(Common::BitStream *bits, uint16 version, PlaneType plane); - Common::Huffman *_dcHuffmanLuma, *_dcHuffmanChroma; - int _lastDC[3]; + struct ADPCMStatus { + int16 sample[2]; + } _adpcmStatus[2]; - void dequantizeBlock(int *coefficients, float *block, uint16 scale); - void idct(float *dequantData, float *result); - int readSignedCoefficient(Common::BitStream *bits); + bool _endOfTrack; + }; - struct ADPCMStatus { - int16 sample[2]; - } _adpcmStatus[2]; + CDSpeed _speed; + uint32 _frameCount; + Common::SeekableReadStream *_stream; + PSXVideoTrack *_videoTrack; + PSXAudioTrack *_audioTrack; Common::SeekableReadStream *readSector(); }; diff --git a/video/qt_decoder.cpp b/video/qt_decoder.cpp index aba545abc0..9e26e96c7b 100644 --- a/video/qt_decoder.cpp +++ b/video/qt_decoder.cpp @@ -33,14 +33,12 @@ #include "audio/audiostream.h" #include "common/debug.h" -#include "common/endian.h" #include "common/memstream.h" #include "common/system.h" #include "common/textconsole.h" #include "common/util.h" // Video codecs -#include "video/codecs/codec.h" #include "video/codecs/cinepak.h" #include "video/codecs/mjpeg.h" #include "video/codecs/qtrle.h" @@ -56,97 +54,43 @@ namespace Video { //////////////////////////////////////////// QuickTimeDecoder::QuickTimeDecoder() { - _setStartTime = false; _scaledSurface = 0; - _dirtyPalette = false; - _palette = 0; _width = _height = 0; - _needUpdate = false; } QuickTimeDecoder::~QuickTimeDecoder() { close(); } -int32 QuickTimeDecoder::getCurFrame() const { - // TODO: This is rather simplistic and doesn't take edits that - // repeat sections of the media into account. Doing that - // over-complicates things and shouldn't be necessary, but - // it would be nice to have in the future. - - int32 frame = -1; - - for (uint32 i = 0; i < _handlers.size(); i++) - if (_handlers[i]->getTrackType() == TrackHandler::kTrackTypeVideo) - frame += ((VideoTrackHandler *)_handlers[i])->getCurFrame() + 1; - - return frame; -} - -uint32 QuickTimeDecoder::getFrameCount() const { - uint32 count = 0; - - for (uint32 i = 0; i < _handlers.size(); i++) - if (_handlers[i]->getTrackType() == TrackHandler::kTrackTypeVideo) - count += ((VideoTrackHandler *)_handlers[i])->getFrameCount(); - - return count; -} - -void QuickTimeDecoder::startAudio() { - updateAudioBuffer(); - - for (uint32 i = 0; i < _audioTracks.size(); i++) { - g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &_audioHandles[i], _audioTracks[i], -1, getVolume(), getBalance(), DisposeAfterUse::NO); +bool QuickTimeDecoder::loadFile(const Common::String &filename) { + if (!Common::QuickTimeParser::parseFile(filename)) + return false; - // Pause the audio again if we're still paused - if (isPaused()) - g_system->getMixer()->pauseHandle(_audioHandles[i], true); - } + init(); + return true; } -void QuickTimeDecoder::stopAudio() { - for (uint32 i = 0; i < _audioHandles.size(); i++) - g_system->getMixer()->stopHandle(_audioHandles[i]); -} +bool QuickTimeDecoder::loadStream(Common::SeekableReadStream *stream) { + if (!Common::QuickTimeParser::parseStream(stream)) + return false; -void QuickTimeDecoder::pauseVideoIntern(bool pause) { - for (uint32 i = 0; i < _audioHandles.size(); i++) - g_system->getMixer()->pauseHandle(_audioHandles[i], pause); + init(); + return true; } -QuickTimeDecoder::VideoTrackHandler *QuickTimeDecoder::findNextVideoTrack() const { - VideoTrackHandler *bestTrack = 0; - uint32 bestTime = 0xffffffff; - - for (uint32 i = 0; i < _handlers.size(); i++) { - if (_handlers[i]->getTrackType() == TrackHandler::kTrackTypeVideo && !_handlers[i]->endOfTrack()) { - VideoTrackHandler *track = (VideoTrackHandler *)_handlers[i]; - uint32 time = track->getNextFrameStartTime(); +void QuickTimeDecoder::close() { + VideoDecoder::close(); + Common::QuickTimeParser::close(); - if (time < bestTime) { - bestTime = time; - bestTrack = track; - } - } + if (_scaledSurface) { + _scaledSurface->free(); + delete _scaledSurface; + _scaledSurface = 0; } - - return bestTrack; } const Graphics::Surface *QuickTimeDecoder::decodeNextFrame() { - if (!_nextVideoTrack) - return 0; - - const Graphics::Surface *frame = _nextVideoTrack->decodeNextFrame(); - - if (!_setStartTime) { - _startTime = g_system->getMillis(); - _setStartTime = true; - } - - _nextVideoTrack = findNextVideoTrack(); - _needUpdate = false; + const Graphics::Surface *frame = VideoDecoder::decodeNextFrame(); // Update audio buffers too // (needs to be done after we find the next track) @@ -166,138 +110,7 @@ const Graphics::Surface *QuickTimeDecoder::decodeNextFrame() { return frame; } -void QuickTimeDecoder::scaleSurface(const Graphics::Surface *src, Graphics::Surface *dst, Common::Rational scaleFactorX, Common::Rational scaleFactorY) { - assert(src && dst); - - for (int32 j = 0; j < dst->h; j++) - for (int32 k = 0; k < dst->w; k++) - memcpy(dst->getBasePtr(k, j), src->getBasePtr((k * scaleFactorX).toInt() , (j * scaleFactorY).toInt()), src->format.bytesPerPixel); -} - -bool QuickTimeDecoder::endOfVideo() const { - if (!isVideoLoaded()) - return true; - - for (uint32 i = 0; i < _handlers.size(); i++) - if (!_handlers[i]->endOfTrack()) - return false; - - return true; -} - -uint32 QuickTimeDecoder::getTime() const { - // Try to base sync off an active audio track - for (uint32 i = 0; i < _audioHandles.size(); i++) { - if (g_system->getMixer()->isSoundHandleActive(_audioHandles[i])) { - uint32 time = g_system->getMixer()->getSoundElapsedTime(_audioHandles[i]) + _audioStartOffset.msecs(); - if (Audio::Timestamp(time, 1000) < _audioTracks[i]->getLength()) - return time; - } - } - - // Just use time elapsed since the beginning - return SeekableVideoDecoder::getTime(); -} - -uint32 QuickTimeDecoder::getTimeToNextFrame() const { - if (_needUpdate) - return 0; - - if (_nextVideoTrack) { - uint32 nextFrameStartTime = _nextVideoTrack->getNextFrameStartTime(); - - if (nextFrameStartTime == 0) - return 0; - - // TODO: Add support for rate modification - - uint32 elapsedTime = getTime(); - - if (elapsedTime < nextFrameStartTime) - return nextFrameStartTime - elapsedTime; - } - - return 0; -} - -bool QuickTimeDecoder::loadFile(const Common::String &filename) { - if (!Common::QuickTimeParser::parseFile(filename)) - return false; - - init(); - return true; -} - -bool QuickTimeDecoder::loadStream(Common::SeekableReadStream *stream) { - if (!Common::QuickTimeParser::parseStream(stream)) - return false; - - init(); - return true; -} - -void QuickTimeDecoder::updateVolume() { - for (uint32 i = 0; i < _audioHandles.size(); i++) - if (g_system->getMixer()->isSoundHandleActive(_audioHandles[i])) - g_system->getMixer()->setChannelVolume(_audioHandles[i], getVolume()); -} - -void QuickTimeDecoder::updateBalance() { - for (uint32 i = 0; i < _audioHandles.size(); i++) - if (g_system->getMixer()->isSoundHandleActive(_audioHandles[i])) - g_system->getMixer()->setChannelBalance(_audioHandles[i], getBalance()); -} - -void QuickTimeDecoder::init() { - Audio::QuickTimeAudioDecoder::init(); - - _startTime = 0; - _setStartTime = false; - - // Initialize all the audio tracks - if (!_audioTracks.empty()) { - _audioHandles.resize(_audioTracks.size()); - - for (uint32 i = 0; i < _audioTracks.size(); i++) - _handlers.push_back(new AudioTrackHandler(this, _audioTracks[i])); - } - - // Initialize all the video tracks - for (uint32 i = 0; i < _tracks.size(); i++) { - if (_tracks[i]->codecType == CODEC_TYPE_VIDEO) { - for (uint32 j = 0; j < _tracks[i]->sampleDescs.size(); j++) - ((VideoSampleDesc *)_tracks[i]->sampleDescs[j])->initCodec(); - - _handlers.push_back(new VideoTrackHandler(this, _tracks[i])); - } - } - - // Prepare the first video track - _nextVideoTrack = findNextVideoTrack(); - - if (_nextVideoTrack) { - if (_scaleFactorX != 1 || _scaleFactorY != 1) { - // We have to take the scale into consideration when setting width/height - _width = (_nextVideoTrack->getWidth() / _scaleFactorX).toInt(); - _height = (_nextVideoTrack->getHeight() / _scaleFactorY).toInt(); - } else { - _width = _nextVideoTrack->getWidth().toInt(); - _height = _nextVideoTrack->getHeight().toInt(); - } - - _needUpdate = true; - } else { - _needUpdate = false; - } - - // Now start any audio - if (!_audioTracks.empty()) { - startAudio(); - _audioStartOffset = Audio::Timestamp(0); - } -} - -Common::QuickTimeParser::SampleDesc *QuickTimeDecoder::readSampleDesc(Track *track, uint32 format) { +Common::QuickTimeParser::SampleDesc *QuickTimeDecoder::readSampleDesc(Common::QuickTimeParser::Track *track, uint32 format, uint32 descSize) { if (track->codecType == CODEC_TYPE_VIDEO) { debug(0, "Video Codec FourCC: \'%s\'", tag2str(format)); @@ -392,64 +205,55 @@ Common::QuickTimeParser::SampleDesc *QuickTimeDecoder::readSampleDesc(Track *tra } // Pass it on up - return Audio::QuickTimeAudioDecoder::readSampleDesc(track, format); -} - -void QuickTimeDecoder::close() { - stopAudio(); - freeAllTrackHandlers(); - - if (_scaledSurface) { - _scaledSurface->free(); - delete _scaledSurface; - _scaledSurface = 0; - } - - _width = _height = 0; - - Common::QuickTimeParser::close(); - SeekableVideoDecoder::reset(); + return Audio::QuickTimeAudioDecoder::readSampleDesc(track, format, descSize); } -void QuickTimeDecoder::freeAllTrackHandlers() { - for (uint32 i = 0; i < _handlers.size(); i++) - delete _handlers[i]; - - _handlers.clear(); -} +void QuickTimeDecoder::init() { + Audio::QuickTimeAudioDecoder::init(); -void QuickTimeDecoder::seekToTime(const Audio::Timestamp &time) { - stopAudio(); - _audioStartOffset = time; + // Initialize all the audio tracks + for (uint32 i = 0; i < _audioTracks.size(); i++) + addTrack(new AudioTrackHandler(this, _audioTracks[i])); - // Sets all tracks to this time - for (uint32 i = 0; i < _handlers.size(); i++) - _handlers[i]->seekToTime(time); + // Initialize all the video tracks + const Common::Array<Common::QuickTimeParser::Track *> &tracks = Common::QuickTimeParser::_tracks; + for (uint32 i = 0; i < tracks.size(); i++) { + if (tracks[i]->codecType == CODEC_TYPE_VIDEO) { + for (uint32 j = 0; j < tracks[i]->sampleDescs.size(); j++) + ((VideoSampleDesc *)tracks[i]->sampleDescs[j])->initCodec(); - startAudio(); + addTrack(new VideoTrackHandler(this, tracks[i])); + } + } - // Reset our start time - _startTime = g_system->getMillis() - time.msecs(); - _setStartTime = true; - resetPauseStartTime(); + // Prepare the first video track + VideoTrackHandler *nextVideoTrack = (VideoTrackHandler *)findNextVideoTrack(); - // Reset the next video track too - _nextVideoTrack = findNextVideoTrack(); - _needUpdate = _nextVideoTrack != 0; + if (nextVideoTrack) { + if (_scaleFactorX != 1 || _scaleFactorY != 1) { + // We have to take the scale into consideration when setting width/height + _width = (nextVideoTrack->getScaledWidth() / _scaleFactorX).toInt(); + _height = (nextVideoTrack->getScaledHeight() / _scaleFactorY).toInt(); + } else { + _width = nextVideoTrack->getWidth(); + _height = nextVideoTrack->getHeight(); + } + } } void QuickTimeDecoder::updateAudioBuffer() { // Updates the audio buffers for all audio tracks - for (uint32 i = 0; i < _handlers.size(); i++) - if (_handlers[i]->getTrackType() == TrackHandler::kTrackTypeAudio) - ((AudioTrackHandler *)_handlers[i])->updateBuffer(); + for (TrackListIterator it = getTrackListBegin(); it != getTrackListEnd(); it++) + if ((*it)->getTrackType() == VideoDecoder::Track::kTrackTypeAudio) + ((AudioTrackHandler *)*it)->updateBuffer(); } -Graphics::PixelFormat QuickTimeDecoder::getPixelFormat() const { - if (_nextVideoTrack) - return _nextVideoTrack->getPixelFormat(); +void QuickTimeDecoder::scaleSurface(const Graphics::Surface *src, Graphics::Surface *dst, const Common::Rational &scaleFactorX, const Common::Rational &scaleFactorY) { + assert(src && dst); - return Graphics::PixelFormat(); + for (int32 j = 0; j < dst->h; j++) + for (int32 k = 0; k < dst->w; k++) + memcpy(dst->getBasePtr(k, j), src->getBasePtr((k * scaleFactorX).toInt() , (j * scaleFactorY).toInt()), src->format.bytesPerPixel); } QuickTimeDecoder::VideoSampleDesc::VideoSampleDesc(Common::QuickTimeParser::Track *parentTrack, uint32 codecTag) : Common::QuickTimeParser::SampleDesc(parentTrack, codecTag) { @@ -504,25 +308,8 @@ void QuickTimeDecoder::VideoSampleDesc::initCodec() { } } -bool QuickTimeDecoder::endOfVideoTracks() const { - for (uint32 i = 0; i < _handlers.size(); i++) - if (_handlers[i]->getTrackType() == TrackHandler::kTrackTypeVideo && !_handlers[i]->endOfTrack()) - return false; - - return true; -} - -QuickTimeDecoder::TrackHandler::TrackHandler(QuickTimeDecoder *decoder, Track *parent) : _decoder(decoder), _parent(parent), _fd(_decoder->_fd) { - _curEdit = 0; -} - -bool QuickTimeDecoder::TrackHandler::endOfTrack() { - // A track is over when we've finished going through all edits - return _curEdit == _parent->editCount; -} - QuickTimeDecoder::AudioTrackHandler::AudioTrackHandler(QuickTimeDecoder *decoder, QuickTimeAudioTrack *audioTrack) - : TrackHandler(decoder, audioTrack->getParent()), _audioTrack(audioTrack) { + : _decoder(decoder), _audioTrack(audioTrack) { } void QuickTimeDecoder::AudioTrackHandler::updateBuffer() { @@ -532,21 +319,21 @@ void QuickTimeDecoder::AudioTrackHandler::updateBuffer() { _audioTrack->queueAudio(Audio::Timestamp(_decoder->getTimeToNextFrame() + 500, 1000)); } -bool QuickTimeDecoder::AudioTrackHandler::endOfTrack() { - return _audioTrack->endOfData(); +Audio::SeekableAudioStream *QuickTimeDecoder::AudioTrackHandler::getSeekableAudioStream() const { + return _audioTrack; } -void QuickTimeDecoder::AudioTrackHandler::seekToTime(Audio::Timestamp time) { - _audioTrack->seek(time); -} - -QuickTimeDecoder::VideoTrackHandler::VideoTrackHandler(QuickTimeDecoder *decoder, Common::QuickTimeParser::Track *parent) : TrackHandler(decoder, parent) { +QuickTimeDecoder::VideoTrackHandler::VideoTrackHandler(QuickTimeDecoder *decoder, Common::QuickTimeParser::Track *parent) : _decoder(decoder), _parent(parent) { + _curEdit = 0; enterNewEditList(false); _holdNextFrameStartTime = false; _curFrame = -1; _durationOverride = -1; _scaledSurface = 0; + _curPalette = 0; + _dirtyPalette = false; + _reversed = false; } QuickTimeDecoder::VideoTrackHandler::~VideoTrackHandler() { @@ -556,158 +343,240 @@ QuickTimeDecoder::VideoTrackHandler::~VideoTrackHandler() { } } -const Graphics::Surface *QuickTimeDecoder::VideoTrackHandler::decodeNextFrame() { - if (endOfTrack()) - return 0; +bool QuickTimeDecoder::VideoTrackHandler::endOfTrack() const { + // A track is over when we've finished going through all edits + return _reversed ? (_curEdit == 0 && _curFrame < 0) : atLastEdit(); +} - const Graphics::Surface *frame = bufferNextFrame(); +bool QuickTimeDecoder::VideoTrackHandler::seek(const Audio::Timestamp &requestedTime) { + uint32 convertedFrames = requestedTime.convertToFramerate(_decoder->_timeScale).totalNumberOfFrames(); + for (_curEdit = 0; !atLastEdit(); _curEdit++) + if (convertedFrames >= _parent->editList[_curEdit].timeOffset && convertedFrames < _parent->editList[_curEdit].timeOffset + _parent->editList[_curEdit].trackDuration) + break; - if (_holdNextFrameStartTime) { - // Don't set the next frame start time here; we just did a seek - _holdNextFrameStartTime = false; - } else if (_durationOverride >= 0) { - // Use our own duration from the edit list calculation - _nextFrameStartTime += _durationOverride; - _durationOverride = -1; - } else { - _nextFrameStartTime += getFrameDuration(); - } + // If we did reach the end of the track, break out + if (atLastEdit()) + return true; - // Update the edit list, if applicable - // HACK: We're also accepting the time minus one because edit lists - // aren't as accurate as one would hope. - if (!endOfTrack() && getRateAdjustedFrameTime() >= getCurEditTimeOffset() + getCurEditTrackDuration() - 1) { - _curEdit++; + // If this track is in an empty edit, position us at the next non-empty + // edit. There's nothing else to do after this. + if (_parent->editList[_curEdit].mediaTime == -1) { + while (!atLastEdit() && _parent->editList[_curEdit].mediaTime == -1) + _curEdit++; - if (!endOfTrack()) + if (!atLastEdit()) enterNewEditList(true); - } - - if (frame && (_parent->scaleFactorX != 1 || _parent->scaleFactorY != 1)) { - if (!_scaledSurface) { - _scaledSurface = new Graphics::Surface(); - _scaledSurface->create(getWidth().toInt(), getHeight().toInt(), getPixelFormat()); - } - _decoder->scaleSurface(frame, _scaledSurface, _parent->scaleFactorX, _parent->scaleFactorY); - return _scaledSurface; + return true; } - return frame; -} + enterNewEditList(false); -void QuickTimeDecoder::VideoTrackHandler::enterNewEditList(bool bufferFrames) { - // Bypass all empty edit lists first - while (!endOfTrack() && _parent->editList[_curEdit].mediaTime == -1) - _curEdit++; + // One extra check for the end of a track + if (atLastEdit()) + return true; - if (endOfTrack()) - return; + // Now we're in the edit and need to figure out what frame we need + Audio::Timestamp time = requestedTime.convertToFramerate(_parent->timeScale); + while (getRateAdjustedFrameTime() < (uint32)time.totalNumberOfFrames()) { + _curFrame++; + if (_durationOverride >= 0) { + _nextFrameStartTime += _durationOverride; + _durationOverride = -1; + } else { + _nextFrameStartTime += getFrameDuration(); + } + } - uint32 frameNum = 0; - bool done = false; - uint32 totalDuration = 0; - uint32 prevDuration = 0; + // All that's left is to figure out what our starting time is going to be + // Compare the starting point for the frame to where we need to be + _holdNextFrameStartTime = getRateAdjustedFrameTime() != (uint32)time.totalNumberOfFrames(); - // Track down where the mediaTime is in the media - for (int32 i = 0; i < _parent->timeToSampleCount && !done; i++) { - for (int32 j = 0; j < _parent->timeToSample[i].count; j++) { - if (totalDuration == (uint32)_parent->editList[_curEdit].mediaTime) { - done = true; - prevDuration = totalDuration; - break; - } else if (totalDuration > (uint32)_parent->editList[_curEdit].mediaTime) { - done = true; - frameNum--; - break; - } + // If we went past the time, go back a frame. _curFrame before this point is at the frame + // that should be displayed. This adjustment ensures it is on the frame before the one that + // should be displayed. + if (_holdNextFrameStartTime) + _curFrame--; - prevDuration = totalDuration; - totalDuration += _parent->timeToSample[i].duration; - frameNum++; - } - } + if (_reversed) { + // Call setReverse again to update + setReverse(true); + } else { + // Handle the keyframe here + int32 destinationFrame = _curFrame + 1; - if (bufferFrames) { - // Track down the keyframe - _curFrame = findKeyFrame(frameNum) - 1; - while (_curFrame < (int32)frameNum - 1) + assert(destinationFrame < (int32)_parent->frameCount); + _curFrame = findKeyFrame(destinationFrame) - 1; + while (_curFrame < destinationFrame - 1) bufferNextFrame(); - } else { - _curFrame = frameNum - 1; } - _nextFrameStartTime = getCurEditTimeOffset(); + return true; +} - // Set an override for the duration since we came up in-between two frames - if (prevDuration != totalDuration) - _durationOverride = totalDuration - prevDuration; +Audio::Timestamp QuickTimeDecoder::VideoTrackHandler::getDuration() const { + return Audio::Timestamp(0, _parent->duration, _decoder->_timeScale); } -const Graphics::Surface *QuickTimeDecoder::VideoTrackHandler::bufferNextFrame() { - _curFrame++; +uint16 QuickTimeDecoder::VideoTrackHandler::getWidth() const { + return getScaledWidth().toInt(); +} - // Get the next packet - uint32 descId; - Common::SeekableReadStream *frameData = getNextFramePacket(descId); +uint16 QuickTimeDecoder::VideoTrackHandler::getHeight() const { + return getScaledHeight().toInt(); +} - if (!frameData || !descId || descId > _parent->sampleDescs.size()) +Graphics::PixelFormat QuickTimeDecoder::VideoTrackHandler::getPixelFormat() const { + return ((VideoSampleDesc *)_parent->sampleDescs[0])->_videoCodec->getPixelFormat(); +} + +int QuickTimeDecoder::VideoTrackHandler::getFrameCount() const { + return _parent->frameCount; +} + +uint32 QuickTimeDecoder::VideoTrackHandler::getNextFrameStartTime() const { + if (endOfTrack()) return 0; - // Find which video description entry we want - VideoSampleDesc *entry = (VideoSampleDesc *)_parent->sampleDescs[descId - 1]; + // Convert to milliseconds so the tracks can be compared + return getRateAdjustedFrameTime() * 1000 / _parent->timeScale; +} - if (!entry->_videoCodec) +const Graphics::Surface *QuickTimeDecoder::VideoTrackHandler::decodeNextFrame() { + if (endOfTrack()) return 0; - const Graphics::Surface *frame = entry->_videoCodec->decodeImage(frameData); - delete frameData; + if (_reversed) { + // Subtract one to place us on the frame before the current displayed frame. + _curFrame--; - // Update the palette - if (entry->_videoCodec->containsPalette()) { - // The codec itself contains a palette - if (entry->_videoCodec->hasDirtyPalette()) { - _decoder->_palette = entry->_videoCodec->getPalette(); - _decoder->_dirtyPalette = true; + // We have one "dummy" frame at the end to so the last frame is displayed + // for the right amount of time. + if (_curFrame < 0) + return 0; + + // Decode from the last key frame to the frame before the one we need. + // TODO: Probably would be wise to do some caching + int targetFrame = _curFrame; + _curFrame = findKeyFrame(targetFrame) - 1; + while (_curFrame != targetFrame - 1) + bufferNextFrame(); + } + + const Graphics::Surface *frame = bufferNextFrame(); + + if (_reversed) { + if (_holdNextFrameStartTime) { + // Don't set the next frame start time here; we just did a seek + _holdNextFrameStartTime = false; + } else { + // Just need to subtract the time + _nextFrameStartTime -= getFrameDuration(); } } else { - // Check if the video description has been updated - byte *palette = entry->_palette; + if (_holdNextFrameStartTime) { + // Don't set the next frame start time here; we just did a seek + _holdNextFrameStartTime = false; + } else if (_durationOverride >= 0) { + // Use our own duration from the edit list calculation + _nextFrameStartTime += _durationOverride; + _durationOverride = -1; + } else { + _nextFrameStartTime += getFrameDuration(); + } + + // Update the edit list, if applicable + // HACK: We're also accepting the time minus one because edit lists + // aren't as accurate as one would hope. + if (!atLastEdit() && getRateAdjustedFrameTime() >= getCurEditTimeOffset() + getCurEditTrackDuration() - 1) { + _curEdit++; + + if (!atLastEdit()) + enterNewEditList(true); + } + } - if (palette !=_decoder-> _palette) { - _decoder->_palette = palette; - _decoder->_dirtyPalette = true; + if (frame && (_parent->scaleFactorX != 1 || _parent->scaleFactorY != 1)) { + if (!_scaledSurface) { + _scaledSurface = new Graphics::Surface(); + _scaledSurface->create(getScaledWidth().toInt(), getScaledHeight().toInt(), getPixelFormat()); } + + _decoder->scaleSurface(frame, _scaledSurface, _parent->scaleFactorX, _parent->scaleFactorY); + return _scaledSurface; } return frame; } -uint32 QuickTimeDecoder::VideoTrackHandler::getNextFrameStartTime() { - if (endOfTrack()) - return 0; +bool QuickTimeDecoder::VideoTrackHandler::setReverse(bool reverse) { + _reversed = reverse; - // Convert to milliseconds so the tracks can be compared - return getRateAdjustedFrameTime() * 1000 / _parent->timeScale; -} + if (_reversed) { + if (_parent->editCount != 1) { + // TODO: Myst's holo.mov needs this :( + warning("Can only set reverse without edits"); + return false; + } -uint32 QuickTimeDecoder::VideoTrackHandler::getFrameCount() { - return _parent->frameCount; -} + if (atLastEdit()) { + // If we're at the end of the video, go to the penultimate edit. + // The current frame is set to one beyond the last frame here; + // one "past" the currently displayed frame. + _curEdit = _parent->editCount - 1; + _curFrame = _parent->frameCount; + _nextFrameStartTime = _parent->editList[_curEdit].trackDuration + _parent->editList[_curEdit].timeOffset; + } else if (_holdNextFrameStartTime) { + // We just seeked, so "pivot" around the frame that should be displayed + _curFrame++; + _nextFrameStartTime -= getFrameDuration(); + _curFrame++; + } else { + // We need to put _curFrame to be the one after the one that should be displayed. + // Since we're on the frame that should be displaying right now, add one. + _curFrame++; + } + } else { + // Update the edit list, if applicable + // HACK: We're also accepting the time minus one because edit lists + // aren't as accurate as one would hope. + if (!atLastEdit() && getRateAdjustedFrameTime() >= getCurEditTimeOffset() + getCurEditTrackDuration() - 1) { + _curEdit++; + + if (atLastEdit()) + return true; + } -uint32 QuickTimeDecoder::VideoTrackHandler::getFrameDuration() { - uint32 curFrameIndex = 0; - for (int32 i = 0; i < _parent->timeToSampleCount; i++) { - curFrameIndex += _parent->timeToSample[i].count; - if ((uint32)_curFrame < curFrameIndex) { - // Ok, now we have what duration this frame has. - return _parent->timeToSample[i].duration; + if (_holdNextFrameStartTime) { + // We just seeked, so "pivot" around the frame that should be displayed + _curFrame--; + _nextFrameStartTime += getFrameDuration(); + } + + // We need to put _curFrame to be the one before the one that should be displayed. + // Since we're on the frame that should be displaying right now, subtract one. + // (As long as the current frame isn't -1, of course) + if (_curFrame > 0) { + // We then need to handle the keyframe situation + int targetFrame = _curFrame - 1; + _curFrame = findKeyFrame(targetFrame) - 1; + while (_curFrame < targetFrame) + bufferNextFrame(); + } else if (_curFrame == 0) { + // Make us start at the first frame (no keyframe needed) + _curFrame--; } } - // This should never occur - error("Cannot find duration for frame %d", _curFrame); - return 0; + return true; +} + +Common::Rational QuickTimeDecoder::VideoTrackHandler::getScaledWidth() const { + return Common::Rational(_parent->width) / _parent->scaleFactorX; +} + +Common::Rational QuickTimeDecoder::VideoTrackHandler::getScaledHeight() const { + return Common::Rational(_parent->height) / _parent->scaleFactorY; } Common::SeekableReadStream *QuickTimeDecoder::VideoTrackHandler::getNextFramePacket(uint32 &descId) { @@ -737,23 +606,39 @@ Common::SeekableReadStream *QuickTimeDecoder::VideoTrackHandler::getNextFramePac } // Next seek to that frame - _fd->seek(_parent->chunkOffsets[actualChunk]); + Common::SeekableReadStream *stream = _decoder->_fd; + stream->seek(_parent->chunkOffsets[actualChunk]); // Then, if the chunk holds more than one frame, seek to where the frame we want is located for (int32 i = _curFrame - sampleInChunk; i < _curFrame; i++) { if (_parent->sampleSize != 0) - _fd->skip(_parent->sampleSize); + stream->skip(_parent->sampleSize); else - _fd->skip(_parent->sampleSizes[i]); + stream->skip(_parent->sampleSizes[i]); } // Finally, read in the raw data for the frame - //debug("Frame Data[%d]: Offset = %d, Size = %d", _curFrame, _fd->pos(), _parent->sampleSizes[_curFrame]); + //debug("Frame Data[%d]: Offset = %d, Size = %d", _curFrame, stream->pos(), _parent->sampleSizes[_curFrame]); if (_parent->sampleSize != 0) - return _fd->readStream(_parent->sampleSize); + return stream->readStream(_parent->sampleSize); + + return stream->readStream(_parent->sampleSizes[_curFrame]); +} - return _fd->readStream(_parent->sampleSizes[_curFrame]); +uint32 QuickTimeDecoder::VideoTrackHandler::getFrameDuration() { + uint32 curFrameIndex = 0; + for (int32 i = 0; i < _parent->timeToSampleCount; i++) { + curFrameIndex += _parent->timeToSample[i].count; + if ((uint32)_curFrame < curFrameIndex) { + // Ok, now we have what duration this frame has. + return _parent->timeToSample[i].duration; + } + } + + // This should never occur + error("Cannot find duration for frame %d", _curFrame); + return 0; } uint32 QuickTimeDecoder::VideoTrackHandler::findKeyFrame(uint32 frame) const { @@ -765,74 +650,121 @@ uint32 QuickTimeDecoder::VideoTrackHandler::findKeyFrame(uint32 frame) const { return frame; } -void QuickTimeDecoder::VideoTrackHandler::seekToTime(Audio::Timestamp time) { - // First, figure out what edit we're in - time = time.convertToFramerate(_parent->timeScale); - - // Continue until we get to where we need to be - for (_curEdit = 0; !endOfTrack(); _curEdit++) - if ((uint32)time.totalNumberOfFrames() >= getCurEditTimeOffset() && (uint32)time.totalNumberOfFrames() < getCurEditTimeOffset() + getCurEditTrackDuration()) - break; +void QuickTimeDecoder::VideoTrackHandler::enterNewEditList(bool bufferFrames) { + // Bypass all empty edit lists first + while (!atLastEdit() && _parent->editList[_curEdit].mediaTime == -1) + _curEdit++; - // This track is done - if (endOfTrack()) + if (atLastEdit()) return; - enterNewEditList(false); + uint32 frameNum = 0; + bool done = false; + uint32 totalDuration = 0; + uint32 prevDuration = 0; - // One extra check for the end of a track - if (endOfTrack()) - return; + // Track down where the mediaTime is in the media + // This is basically time -> frame mapping + // Note that this code uses first frame = 0 + for (int32 i = 0; i < _parent->timeToSampleCount && !done; i++) { + for (int32 j = 0; j < _parent->timeToSample[i].count; j++) { + if (totalDuration == (uint32)_parent->editList[_curEdit].mediaTime) { + done = true; + prevDuration = totalDuration; + break; + } else if (totalDuration > (uint32)_parent->editList[_curEdit].mediaTime) { + done = true; + frameNum--; + break; + } - // Now we're in the edit and need to figure out what frame we need - while (getRateAdjustedFrameTime() < (uint32)time.totalNumberOfFrames()) { - _curFrame++; - if (_durationOverride >= 0) { - _nextFrameStartTime += _durationOverride; - _durationOverride = -1; - } else { - _nextFrameStartTime += getFrameDuration(); + prevDuration = totalDuration; + totalDuration += _parent->timeToSample[i].duration; + frameNum++; } } - // All that's left is to figure out what our starting time is going to be - // Compare the starting point for the frame to where we need to be - _holdNextFrameStartTime = getRateAdjustedFrameTime() != (uint32)time.totalNumberOfFrames(); - - // If we went past the time, go back a frame - if (_holdNextFrameStartTime) - _curFrame--; + if (bufferFrames) { + // Track down the keyframe + // Then decode until the frame before target + _curFrame = findKeyFrame(frameNum) - 1; + while (_curFrame < (int32)frameNum - 1) + bufferNextFrame(); + } else { + // Since frameNum is the frame that needs to be displayed + // we'll set _curFrame to be the "last frame displayed" + _curFrame = frameNum - 1; + } - // Handle the keyframe here - int32 destinationFrame = _curFrame + 1; + _nextFrameStartTime = getCurEditTimeOffset(); - assert(destinationFrame < (int32)_parent->frameCount); - _curFrame = findKeyFrame(destinationFrame) - 1; - while (_curFrame < destinationFrame - 1) - bufferNextFrame(); + // Set an override for the duration since we came up in-between two frames + if (prevDuration != totalDuration) + _durationOverride = totalDuration - prevDuration; + else + _durationOverride = -1; } -Common::Rational QuickTimeDecoder::VideoTrackHandler::getWidth() const { - return Common::Rational(_parent->width) / _parent->scaleFactorX; -} +const Graphics::Surface *QuickTimeDecoder::VideoTrackHandler::bufferNextFrame() { + _curFrame++; -Common::Rational QuickTimeDecoder::VideoTrackHandler::getHeight() const { - return Common::Rational(_parent->height) / _parent->scaleFactorY; -} + // Get the next packet + uint32 descId; + Common::SeekableReadStream *frameData = getNextFramePacket(descId); -Graphics::PixelFormat QuickTimeDecoder::VideoTrackHandler::getPixelFormat() const { - return ((VideoSampleDesc *)_parent->sampleDescs[0])->_videoCodec->getPixelFormat(); + if (!frameData || !descId || descId > _parent->sampleDescs.size()) + return 0; + + // Find which video description entry we want + VideoSampleDesc *entry = (VideoSampleDesc *)_parent->sampleDescs[descId - 1]; + + if (!entry->_videoCodec) + return 0; + + const Graphics::Surface *frame = entry->_videoCodec->decodeImage(frameData); + delete frameData; + + // Update the palette + if (entry->_videoCodec->containsPalette()) { + // The codec itself contains a palette + if (entry->_videoCodec->hasDirtyPalette()) { + _curPalette = entry->_videoCodec->getPalette(); + _dirtyPalette = true; + } + } else { + // Check if the video description has been updated + byte *palette = entry->_palette; + + if (palette != _curPalette) { + _curPalette = palette; + _dirtyPalette = true; + } + } + + return frame; } uint32 QuickTimeDecoder::VideoTrackHandler::getRateAdjustedFrameTime() const { // Figure out what time the next frame is at taking the edit list rate into account - uint32 convertedTime = (Common::Rational(_nextFrameStartTime - getCurEditTimeOffset()) / _parent->editList[_curEdit].mediaRate).toInt(); + uint32 convertedTime = (Common::Rational(_nextFrameStartTime - getCurEditTimeOffset()) / _parent->editList[_curEdit].mediaRate).toInt(); return convertedTime + getCurEditTimeOffset(); } uint32 QuickTimeDecoder::VideoTrackHandler::getCurEditTimeOffset() const { // Need to convert to the track scale - return _parent->editList[_curEdit].timeOffset * _parent->timeScale / _decoder->_timeScale; + + // We have to round the time off to the nearest in the scale, otherwise + // bad things happen. QuickTime docs are pretty silent on all this stuff, + // so this was found from samples. It doesn't help that this is really + // the only open source implementation of QuickTime edits. + + uint32 mult = _parent->editList[_curEdit].timeOffset * _parent->timeScale; + uint32 result = mult / _decoder->_timeScale; + + if ((mult % _decoder->_timeScale) > (_decoder->_timeScale / 2)) + result++; + + return result; } uint32 QuickTimeDecoder::VideoTrackHandler::getCurEditTrackDuration() const { @@ -840,4 +772,8 @@ uint32 QuickTimeDecoder::VideoTrackHandler::getCurEditTrackDuration() const { return _parent->editList[_curEdit].trackDuration * _parent->timeScale / _decoder->_timeScale; } +bool QuickTimeDecoder::VideoTrackHandler::atLastEdit() const { + return _curEdit == _parent->editCount; +} + } // End of namespace Video diff --git a/video/qt_decoder.h b/video/qt_decoder.h index ce32562d64..28314f2e63 100644 --- a/video/qt_decoder.h +++ b/video/qt_decoder.h @@ -31,16 +31,17 @@ #ifndef VIDEO_QT_DECODER_H #define VIDEO_QT_DECODER_H -#include "audio/mixer.h" #include "audio/decoders/quicktime_intern.h" #include "common/scummsys.h" -#include "common/rational.h" -#include "graphics/pixelformat.h" #include "video/video_decoder.h" namespace Common { - class Rational; +class Rational; +} + +namespace Graphics { +struct PixelFormat; } namespace Video { @@ -52,70 +53,36 @@ class Codec; * * Video decoder used in engines: * - mohawk + * - pegasus * - sci */ -class QuickTimeDecoder : public SeekableVideoDecoder, public Audio::QuickTimeAudioDecoder { +class QuickTimeDecoder : public VideoDecoder, public Audio::QuickTimeAudioDecoder { public: QuickTimeDecoder(); virtual ~QuickTimeDecoder(); - /** - * Returns the width of the video - * @return the width of the video - */ - uint16 getWidth() const { return _width; } - - /** - * Returns the height of the video - * @return the height of the video - */ - uint16 getHeight() const { return _height; } - - /** - * Returns the amount of frames in the video - * @return the amount of frames in the video - */ - uint32 getFrameCount() const; - - /** - * Load a video file - * @param filename the filename to load - */ bool loadFile(const Common::String &filename); - - /** - * Load a QuickTime video file from a SeekableReadStream - * @param stream the stream to load - */ bool loadStream(Common::SeekableReadStream *stream); - - /** - * Close a QuickTime encoded video file - */ void close(); + uint16 getWidth() const { return _width; } + uint16 getHeight() const { return _height; } + const Graphics::Surface *decodeNextFrame(); + Audio::Timestamp getDuration() const { return Audio::Timestamp(0, _duration, _timeScale); } - /** - * Returns the palette of the video - * @return the palette of the video - */ - const byte *getPalette() { _dirtyPalette = false; return _palette; } - bool hasDirtyPalette() const { return _dirtyPalette; } +protected: + Common::QuickTimeParser::SampleDesc *readSampleDesc(Common::QuickTimeParser::Track *track, uint32 format, uint32 descSize); - int32 getCurFrame() const; +private: + void init(); - bool isVideoLoaded() const { return isOpen(); } - const Graphics::Surface *decodeNextFrame(); - bool endOfVideo() const; - uint32 getTime() const; - uint32 getTimeToNextFrame() const; - Graphics::PixelFormat getPixelFormat() const; + void updateAudioBuffer(); - // SeekableVideoDecoder API - void seekToFrame(uint32 frame); - void seekToTime(const Audio::Timestamp &time); - uint32 getDuration() const { return _duration * 1000 / _timeScale; } + uint16 _width, _height; + + Graphics::Surface *_scaledSurface; + void scaleSurface(const Graphics::Surface *src, Graphics::Surface *dst, + const Common::Rational &scaleFactorX, const Common::Rational &scaleFactorY); -protected: class VideoSampleDesc : public Common::QuickTimeParser::SampleDesc { public: VideoSampleDesc(Common::QuickTimeParser::Track *parentTrack, uint32 codecTag); @@ -131,110 +98,62 @@ protected: Codec *_videoCodec; }; - Common::QuickTimeParser::SampleDesc *readSampleDesc(Track *track, uint32 format); - - // VideoDecoder API - void updateVolume(); - void updateBalance(); - -private: - void init(); - - void startAudio(); - void stopAudio(); - void updateAudioBuffer(); - void readNextAudioChunk(); - Common::Array<Audio::SoundHandle> _audioHandles; - Audio::Timestamp _audioStartOffset; - - Codec *createCodec(uint32 codecTag, byte bitsPerPixel); - uint32 findKeyFrame(uint32 frame) const; - - bool _dirtyPalette; - const byte *_palette; - bool _setStartTime; - bool _needUpdate; - - uint16 _width, _height; - - Graphics::Surface *_scaledSurface; - void scaleSurface(const Graphics::Surface *src, Graphics::Surface *dst, - Common::Rational scaleFactorX, Common::Rational scaleFactorY); - - void pauseVideoIntern(bool pause); - bool endOfVideoTracks() const; - - // The TrackHandler is a class that wraps around a QuickTime Track - // and handles playback in this decoder class. - class TrackHandler { - public: - TrackHandler(QuickTimeDecoder *decoder, Track *parent); - virtual ~TrackHandler() {} - - enum TrackType { - kTrackTypeAudio, - kTrackTypeVideo - }; - - virtual TrackType getTrackType() const = 0; - - virtual void seekToTime(Audio::Timestamp time) = 0; - - virtual bool endOfTrack(); - - protected: - uint32 _curEdit; - QuickTimeDecoder *_decoder; - Common::SeekableReadStream *_fd; - Track *_parent; - }; - // The AudioTrackHandler is currently just a wrapper around some // QuickTimeDecoder functions. - class AudioTrackHandler : public TrackHandler { + class AudioTrackHandler : public SeekableAudioTrack { public: AudioTrackHandler(QuickTimeDecoder *decoder, QuickTimeAudioTrack *audioTrack); - TrackType getTrackType() const { return kTrackTypeAudio; } void updateBuffer(); - void seekToTime(Audio::Timestamp time); - bool endOfTrack(); + + protected: + Audio::SeekableAudioStream *getSeekableAudioStream() const; private: + QuickTimeDecoder *_decoder; QuickTimeAudioTrack *_audioTrack; }; // The VideoTrackHandler is the bridge between the time of playback // and the media for the given track. It calculates when to start // tracks and at what rate to play the media using the edit list. - class VideoTrackHandler : public TrackHandler { + class VideoTrackHandler : public VideoTrack { public: - VideoTrackHandler(QuickTimeDecoder *decoder, Track *parent); + VideoTrackHandler(QuickTimeDecoder *decoder, Common::QuickTimeParser::Track *parent); ~VideoTrackHandler(); - TrackType getTrackType() const { return kTrackTypeVideo; } - - const Graphics::Surface *decodeNextFrame(); - - uint32 getNextFrameStartTime(); - - uint32 getFrameCount(); - - int32 getCurFrame() { return _curFrame; } + bool endOfTrack() const; + bool isSeekable() const { return true; } + bool seek(const Audio::Timestamp &time); + Audio::Timestamp getDuration() const; + uint16 getWidth() const; + uint16 getHeight() const; Graphics::PixelFormat getPixelFormat() const; + int getCurFrame() const { return _curFrame; } + int getFrameCount() const; + uint32 getNextFrameStartTime() const; + const Graphics::Surface *decodeNextFrame(); + const byte *getPalette() const { _dirtyPalette = false; return _curPalette; } + bool hasDirtyPalette() const { return _curPalette; } + bool setReverse(bool reverse); + bool isReversed() const { return _reversed; } - void seekToTime(Audio::Timestamp time); - - Common::Rational getWidth() const; - Common::Rational getHeight() const; + Common::Rational getScaledWidth() const; + Common::Rational getScaledHeight() const; private: + QuickTimeDecoder *_decoder; + Common::QuickTimeParser::Track *_parent; + uint32 _curEdit; int32 _curFrame; uint32 _nextFrameStartTime; Graphics::Surface *_scaledSurface; bool _holdNextFrameStartTime; int32 _durationOverride; + const byte *_curPalette; + mutable bool _dirtyPalette; + bool _reversed; Common::SeekableReadStream *getNextFramePacket(uint32 &descId); uint32 getFrameDuration(); @@ -244,13 +163,8 @@ private: uint32 getRateAdjustedFrameTime() const; uint32 getCurEditTimeOffset() const; uint32 getCurEditTrackDuration() const; + bool atLastEdit() const; }; - - Common::Array<TrackHandler *> _handlers; - VideoTrackHandler *_nextVideoTrack; - VideoTrackHandler *findNextVideoTrack() const; - - void freeAllTrackHandlers(); }; } // End of namespace Video diff --git a/video/smk_decoder.cpp b/video/smk_decoder.cpp index 359f4cb9bd..c49791100d 100644 --- a/video/smk_decoder.cpp +++ b/video/smk_decoder.cpp @@ -204,8 +204,7 @@ BigHuffmanTree::BigHuffmanTree(Common::BitStream &bs, int allocSize) delete _hiBytes; } -BigHuffmanTree::~BigHuffmanTree() -{ +BigHuffmanTree::~BigHuffmanTree() { delete[] _tree; } @@ -278,24 +277,17 @@ uint32 BigHuffmanTree::getCode(Common::BitStream &bs) { return v; } -SmackerDecoder::SmackerDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundType) - : _audioStarted(false), _audioStream(0), _mixer(mixer), _soundType(soundType) { - _surface = 0; +SmackerDecoder::SmackerDecoder(Audio::Mixer::SoundType soundType) : _soundType(soundType) { _fileStream = 0; - _dirtyPalette = false; + _firstFrameStart = 0; + _frameTypes = 0; + _frameSizes = 0; } SmackerDecoder::~SmackerDecoder() { close(); } -uint32 SmackerDecoder::getTime() const { - if (_audioStream && _audioStarted) - return _mixer->getSoundElapsedTime(_audioHandle); - - return FixedRateVideoDecoder::getTime(); -} - bool SmackerDecoder::loadStream(Common::SeekableReadStream *stream) { close(); @@ -309,25 +301,29 @@ bool SmackerDecoder::loadStream(Common::SeekableReadStream *stream) { uint32 width = _fileStream->readUint32LE(); uint32 height = _fileStream->readUint32LE(); - _frameCount = _fileStream->readUint32LE(); - int32 frameRate = _fileStream->readSint32LE(); - - // framerate contains 2 digits after the comma, so 1497 is actually 14.97 fps - if (frameRate > 0) - _frameRate = Common::Rational(1000, frameRate); - else if (frameRate < 0) - _frameRate = Common::Rational(100000, -frameRate); + uint32 frameCount = _fileStream->readUint32LE(); + int32 frameDelay = _fileStream->readSint32LE(); + + // frame rate contains 2 digits after the comma, so 1497 is actually 14.97 fps + Common::Rational frameRate; + if (frameDelay > 0) + frameRate = Common::Rational(1000, frameDelay); + else if (frameDelay < 0) + frameRate = Common::Rational(100000, -frameDelay); else - _frameRate = 1000; + frameRate = 1000; // Flags are determined by which bit is set, which can be one of the following: // 0 - set to 1 if file contains a ring frame. // 1 - set to 1 if file is Y-interlaced // 2 - set to 1 if file is Y-doubled // If bits 1 or 2 are set, the frame should be scaled to twice its height - // before it is displayed. + // before it is displayed. _header.flags = _fileStream->readUint32LE(); + SmackerVideoTrack *videoTrack = createVideoTrack(width, height, frameCount, frameRate, _header.flags, _header.signature); + addTrack(videoTrack); + // TODO: should we do any extra processing for Smacker files with ring frames? // TODO: should we do any extra processing for Y-doubled videos? Are they the @@ -374,92 +370,77 @@ bool SmackerDecoder::loadStream(Common::SeekableReadStream *stream) { warning("Unhandled Smacker v2 audio compression"); if (i == 0) - _audioStream = Audio::makeQueuingAudioStream(_header.audioInfo[0].sampleRate, _header.audioInfo[0].isStereo); + addTrack(new SmackerAudioTrack(_header.audioInfo[i], _soundType)); } } _header.dummy = _fileStream->readUint32LE(); - _frameSizes = new uint32[_frameCount]; - for (i = 0; i < _frameCount; ++i) + _frameSizes = new uint32[frameCount]; + for (i = 0; i < frameCount; ++i) _frameSizes[i] = _fileStream->readUint32LE(); - _frameTypes = new byte[_frameCount]; - for (i = 0; i < _frameCount; ++i) + _frameTypes = new byte[frameCount]; + for (i = 0; i < frameCount; ++i) _frameTypes[i] = _fileStream->readByte(); byte *huffmanTrees = (byte *) malloc(_header.treesSize); _fileStream->read(huffmanTrees, _header.treesSize); Common::BitStream8LSB bs(new Common::MemoryReadStream(huffmanTrees, _header.treesSize, DisposeAfterUse::YES), true); + videoTrack->readTrees(bs, _header.mMapSize, _header.mClrSize, _header.fullSize, _header.typeSize); - _MMapTree = new BigHuffmanTree(bs, _header.mMapSize); - _MClrTree = new BigHuffmanTree(bs, _header.mClrSize); - _FullTree = new BigHuffmanTree(bs, _header.fullSize); - _TypeTree = new BigHuffmanTree(bs, _header.typeSize); - - _surface = new Graphics::Surface(); + _firstFrameStart = _fileStream->pos(); - // Height needs to be doubled if we have flags (Y-interlaced or Y-doubled) - _surface->create(width, height * (_header.flags ? 2 : 1), Graphics::PixelFormat::createFormatCLUT8()); - - memset(_palette, 0, 3 * 256); return true; } void SmackerDecoder::close() { - if (!_fileStream) - return; - - if (_audioStream) { - if (_audioStarted) { - // The mixer will delete the stream. - _mixer->stopHandle(_audioHandle); - _audioStarted = false; - } else { - delete _audioStream; - } - _audioStream = 0; - } + VideoDecoder::close(); delete _fileStream; _fileStream = 0; - _surface->free(); - delete _surface; - _surface = 0; - - delete _MMapTree; - delete _MClrTree; - delete _FullTree; - delete _TypeTree; + delete[] _frameTypes; + _frameTypes = 0; delete[] _frameSizes; - delete[] _frameTypes; + _frameSizes = 0; +} + +bool SmackerDecoder::rewind() { + // Call the parent method to rewind the tracks first + if (!VideoDecoder::rewind()) + return false; - reset(); + // And seek back to where the first frame begins + _fileStream->seek(_firstFrameStart); + return true; } -const Graphics::Surface *SmackerDecoder::decodeNextFrame() { +void SmackerDecoder::readNextPacket() { + SmackerVideoTrack *videoTrack = (SmackerVideoTrack *)getTrack(0); + + if (videoTrack->endOfTrack()) + return; + + videoTrack->increaseCurFrame(); + uint i; uint32 chunkSize = 0; uint32 dataSizeUnpacked = 0; uint32 startPos = _fileStream->pos(); - _curFrame++; - // Check if we got a frame with palette data, and // call back the virtual setPalette function to set // the current palette - if (_frameTypes[_curFrame] & 1) { - unpackPalette(); - _dirtyPalette = true; - } + if (_frameTypes[videoTrack->getCurFrame()] & 1) + videoTrack->unpackPalette(_fileStream); // Load audio tracks for (i = 0; i < 7; ++i) { - if (!(_frameTypes[_curFrame] & (2 << i))) + if (!(_frameTypes[videoTrack->getCurFrame()] & (2 << i))) continue; chunkSize = _fileStream->readUint32LE(); @@ -475,29 +456,109 @@ const Graphics::Surface *SmackerDecoder::decodeNextFrame() { handleAudioTrack(i, chunkSize, dataSizeUnpacked); } - uint32 frameSize = _frameSizes[_curFrame] & ~3; -// uint32 remainder = _frameSizes[_curFrame] & 3; + uint32 frameSize = _frameSizes[videoTrack->getCurFrame()] & ~3; +// uint32 remainder = _frameSizes[videoTrack->getCurFrame()] & 3; if (_fileStream->pos() - startPos > frameSize) error("Smacker actual frame size exceeds recorded frame size"); uint32 frameDataSize = frameSize - (_fileStream->pos() - startPos); - _frameData = (byte *)malloc(frameDataSize + 1); + byte *frameData = (byte *)malloc(frameDataSize + 1); // Padding to keep the BigHuffmanTrees from reading past the data end - _frameData[frameDataSize] = 0x00; + frameData[frameDataSize] = 0x00; + + _fileStream->read(frameData, frameDataSize); + + Common::BitStream8LSB bs(new Common::MemoryReadStream(frameData, frameDataSize + 1, DisposeAfterUse::YES), true); + videoTrack->decodeFrame(bs); + + _fileStream->seek(startPos + frameSize); +} - _fileStream->read(_frameData, frameDataSize); +void SmackerDecoder::handleAudioTrack(byte track, uint32 chunkSize, uint32 unpackedSize) { + if (_header.audioInfo[track].hasAudio && chunkSize > 0 && track == 0) { + // Get the audio track, which start at offset 1 (first track is video) + SmackerAudioTrack *audioTrack = (SmackerAudioTrack *)getTrack(track + 1); - Common::BitStream8LSB bs(new Common::MemoryReadStream(_frameData, frameDataSize + 1, DisposeAfterUse::YES), true); + // If it's track 0, play the audio data + byte *soundBuffer = (byte *)malloc(chunkSize + 1); + // Padding to keep the SmallHuffmanTrees from reading past the data end + soundBuffer[chunkSize] = 0x00; + _fileStream->read(soundBuffer, chunkSize); + + if (_header.audioInfo[track].compression == kCompressionRDFT || _header.audioInfo[track].compression == kCompressionDCT) { + // TODO: Compressed audio (Bink RDFT/DCT encoded) + free(soundBuffer); + return; + } else if (_header.audioInfo[track].compression == kCompressionDPCM) { + // Compressed audio (Huffman DPCM encoded) + audioTrack->queueCompressedBuffer(soundBuffer, chunkSize + 1, unpackedSize); + free(soundBuffer); + } else { + // Uncompressed audio (PCM) + audioTrack->queuePCM(soundBuffer, chunkSize); + } + } else { + // Ignore the rest of the audio tracks, if they exist + // TODO: Are there any Smacker videos with more than one audio stream? + // If yes, we should play the rest of the audio streams as well + if (chunkSize > 0) + _fileStream->skip(chunkSize); + } +} + +SmackerDecoder::SmackerVideoTrack::SmackerVideoTrack(uint32 width, uint32 height, uint32 frameCount, const Common::Rational &frameRate, uint32 flags, uint32 signature) { + _surface = new Graphics::Surface(); + _surface->create(width, height * (flags ? 2 : 1), Graphics::PixelFormat::createFormatCLUT8()); + _frameCount = frameCount; + _frameRate = frameRate; + _flags = flags; + _signature = signature; + _curFrame = -1; + _dirtyPalette = false; + _MMapTree = _MClrTree = _FullTree = _TypeTree = 0; + memset(_palette, 0, 3 * 256); +} + +SmackerDecoder::SmackerVideoTrack::~SmackerVideoTrack() { + _surface->free(); + delete _surface; + + delete _MMapTree; + delete _MClrTree; + delete _FullTree; + delete _TypeTree; +} + +uint16 SmackerDecoder::SmackerVideoTrack::getWidth() const { + return _surface->w; +} + +uint16 SmackerDecoder::SmackerVideoTrack::getHeight() const { + return _surface->h; +} + +Graphics::PixelFormat SmackerDecoder::SmackerVideoTrack::getPixelFormat() const { + return _surface->format; +} + +void SmackerDecoder::SmackerVideoTrack::readTrees(Common::BitStream &bs, uint32 mMapSize, uint32 mClrSize, uint32 fullSize, uint32 typeSize) { + _MMapTree = new BigHuffmanTree(bs, mMapSize); + _MClrTree = new BigHuffmanTree(bs, mClrSize); + _FullTree = new BigHuffmanTree(bs, fullSize); + _TypeTree = new BigHuffmanTree(bs, typeSize); +} + +void SmackerDecoder::SmackerVideoTrack::decodeFrame(Common::BitStream &bs) { _MMapTree->reset(); _MClrTree->reset(); _FullTree->reset(); _TypeTree->reset(); // Height needs to be doubled if we have flags (Y-interlaced or Y-doubled) - uint doubleY = _header.flags ? 2 : 1; + uint doubleY = _flags ? 2 : 1; uint bw = getWidth() / 4; uint bh = getHeight() / doubleY / 4; @@ -508,6 +569,7 @@ const Graphics::Surface *SmackerDecoder::decodeNextFrame() { uint type, run, j, mode; uint32 p1, p2, clr, map; byte hi, lo; + uint i; while (block < blocks) { type = _TypeTree->getCode(bs); @@ -536,7 +598,7 @@ const Graphics::Surface *SmackerDecoder::decodeNextFrame() { break; case SMK_BLOCK_FULL: // Smacker v2 has one mode, Smacker v4 has three - if (_header.signature == MKTAG('S','M','K','2')) { + if (_signature == MKTAG('S','M','K','2')) { mode = 0; } else { // 00 - mode 0 @@ -628,60 +690,81 @@ const Graphics::Surface *SmackerDecoder::decodeNextFrame() { break; } } +} - _fileStream->seek(startPos + frameSize); +void SmackerDecoder::SmackerVideoTrack::unpackPalette(Common::SeekableReadStream *stream) { + uint startPos = stream->pos(); + uint32 len = 4 * stream->readByte(); - if (_curFrame == 0) - _startTime = g_system->getMillis(); + byte *chunk = (byte *)malloc(len); + stream->read(chunk, len); + byte *p = chunk; - return _surface; -} + byte oldPalette[3 * 256]; + memcpy(oldPalette, _palette, 3 * 256); -void SmackerDecoder::handleAudioTrack(byte track, uint32 chunkSize, uint32 unpackedSize) { - if (_header.audioInfo[track].hasAudio && chunkSize > 0 && track == 0) { - // If it's track 0, play the audio data - byte *soundBuffer = (byte *)malloc(chunkSize + 1); - // Padding to keep the SmallHuffmanTrees from reading past the data end - soundBuffer[chunkSize] = 0x00; + byte *pal = _palette; - _fileStream->read(soundBuffer, chunkSize); + int sz = 0; + byte b0; + while (sz < 256) { + b0 = *p++; + if (b0 & 0x80) { // if top bit is 1 (0x80 = 10000000) + sz += (b0 & 0x7f) + 1; // get lower 7 bits + 1 (0x7f = 01111111) + pal += 3 * ((b0 & 0x7f) + 1); + } else if (b0 & 0x40) { // if top 2 bits are 01 (0x40 = 01000000) + byte c = (b0 & 0x3f) + 1; // get lower 6 bits + 1 (0x3f = 00111111) + uint s = 3 * *p++; + sz += c; - if (_header.audioInfo[track].compression == kCompressionRDFT || _header.audioInfo[track].compression == kCompressionDCT) { - // TODO: Compressed audio (Bink RDFT/DCT encoded) - free(soundBuffer); - return; - } else if (_header.audioInfo[track].compression == kCompressionDPCM) { - // Compressed audio (Huffman DPCM encoded) - queueCompressedBuffer(soundBuffer, chunkSize + 1, unpackedSize, track); - free(soundBuffer); - } else { - // Uncompressed audio (PCM) - byte flags = 0; - if (_header.audioInfo[track].is16Bits) - flags = flags | Audio::FLAG_16BITS; - if (_header.audioInfo[track].isStereo) - flags = flags | Audio::FLAG_STEREO; - - _audioStream->queueBuffer(soundBuffer, chunkSize, DisposeAfterUse::YES, flags); - // The sound buffer will be deleted by QueuingAudioStream - } + while (c--) { + *pal++ = oldPalette[s + 0]; + *pal++ = oldPalette[s + 1]; + *pal++ = oldPalette[s + 2]; + s += 3; + } + } else { // top 2 bits are 00 + sz++; + // get the lower 6 bits for each component (0x3f = 00111111) + byte b = b0 & 0x3f; + byte g = (*p++) & 0x3f; + byte r = (*p++) & 0x3f; - if (!_audioStarted) { - _mixer->playStream(_soundType, &_audioHandle, _audioStream, -1, getVolume(), getBalance()); - _audioStarted = true; + assert(g < 0xc0 && b < 0xc0); + + // upscale to full 8-bit color values by multiplying by 4 + *pal++ = b * 4; + *pal++ = g * 4; + *pal++ = r * 4; } - } else { - // Ignore the rest of the audio tracks, if they exist - // TODO: Are there any Smacker videos with more than one audio stream? - // If yes, we should play the rest of the audio streams as well - if (chunkSize > 0) - _fileStream->skip(chunkSize); } + + stream->seek(startPos + len); + free(chunk); + + _dirtyPalette = true; +} + +SmackerDecoder::SmackerAudioTrack::SmackerAudioTrack(const AudioInfo &audioInfo, Audio::Mixer::SoundType soundType) : + _audioInfo(audioInfo), _soundType(soundType) { + _audioStream = Audio::makeQueuingAudioStream(_audioInfo.sampleRate, _audioInfo.isStereo); +} + +SmackerDecoder::SmackerAudioTrack::~SmackerAudioTrack() { + delete _audioStream; } -void SmackerDecoder::queueCompressedBuffer(byte *buffer, uint32 bufferSize, - uint32 unpackedSize, int streamNum) { +bool SmackerDecoder::SmackerAudioTrack::rewind() { + delete _audioStream; + _audioStream = Audio::makeQueuingAudioStream(_audioInfo.sampleRate, _audioInfo.isStereo); + return true; +} +Audio::AudioStream *SmackerDecoder::SmackerAudioTrack::getAudioStream() const { + return _audioStream; +} + +void SmackerDecoder::SmackerAudioTrack::queueCompressedBuffer(byte *buffer, uint32 bufferSize, uint32 unpackedSize) { Common::BitStream8LSB audioBS(new Common::MemoryReadStream(buffer, bufferSize), true); bool dataPresent = audioBS.getBit(); @@ -689,9 +772,9 @@ void SmackerDecoder::queueCompressedBuffer(byte *buffer, uint32 bufferSize, return; bool isStereo = audioBS.getBit(); - assert(isStereo == _header.audioInfo[streamNum].isStereo); + assert(isStereo == _audioInfo.isStereo); bool is16Bits = audioBS.getBit(); - assert(is16Bits == _header.audioInfo[streamNum].is16Bits); + assert(is16Bits == _audioInfo.is16Bits); int numBytes = 1 * (isStereo ? 2 : 1) * (is16Bits ? 2 : 1); @@ -759,74 +842,21 @@ void SmackerDecoder::queueCompressedBuffer(byte *buffer, uint32 bufferSize, for (int k = 0; k < numBytes; k++) delete audioTrees[k]; - byte flags = 0; - if (_header.audioInfo[0].is16Bits) - flags = flags | Audio::FLAG_16BITS; - if (_header.audioInfo[0].isStereo) - flags = flags | Audio::FLAG_STEREO; - _audioStream->queueBuffer(unpackedBuffer, unpackedSize, DisposeAfterUse::YES, flags); - // unpackedBuffer will be deleted by QueuingAudioStream + queuePCM(unpackedBuffer, unpackedSize); } -void SmackerDecoder::unpackPalette() { - uint startPos = _fileStream->pos(); - uint32 len = 4 * _fileStream->readByte(); - - byte *chunk = (byte *)malloc(len); - _fileStream->read(chunk, len); - byte *p = chunk; - - byte oldPalette[3*256]; - memcpy(oldPalette, _palette, 3 * 256); - - byte *pal = _palette; - - int sz = 0; - byte b0; - while (sz < 256) { - b0 = *p++; - if (b0 & 0x80) { // if top bit is 1 (0x80 = 10000000) - sz += (b0 & 0x7f) + 1; // get lower 7 bits + 1 (0x7f = 01111111) - pal += 3 * ((b0 & 0x7f) + 1); - } else if (b0 & 0x40) { // if top 2 bits are 01 (0x40 = 01000000) - byte c = (b0 & 0x3f) + 1; // get lower 6 bits + 1 (0x3f = 00111111) - uint s = 3 * *p++; - sz += c; - - while (c--) { - *pal++ = oldPalette[s + 0]; - *pal++ = oldPalette[s + 1]; - *pal++ = oldPalette[s + 2]; - s += 3; - } - } else { // top 2 bits are 00 - sz++; - // get the lower 6 bits for each component (0x3f = 00111111) - byte b = b0 & 0x3f; - byte g = (*p++) & 0x3f; - byte r = (*p++) & 0x3f; - - assert(g < 0xc0 && b < 0xc0); - - // upscale to full 8-bit color values by multiplying by 4 - *pal++ = b * 4; - *pal++ = g * 4; - *pal++ = r * 4; - } - } - - _fileStream->seek(startPos + len); - free(chunk); -} +void SmackerDecoder::SmackerAudioTrack::queuePCM(byte *buffer, uint32 bufferSize) { + byte flags = 0; + if (_audioInfo.is16Bits) + flags |= Audio::FLAG_16BITS; + if (_audioInfo.isStereo) + flags |= Audio::FLAG_STEREO; -void SmackerDecoder::updateVolume() { - if (g_system->getMixer()->isSoundHandleActive(_audioHandle)) - g_system->getMixer()->setChannelVolume(_audioHandle, getVolume()); + _audioStream->queueBuffer(buffer, bufferSize, DisposeAfterUse::YES, flags); } -void SmackerDecoder::updateBalance() { - if (g_system->getMixer()->isSoundHandleActive(_audioHandle)) - g_system->getMixer()->setChannelBalance(_audioHandle, getBalance()); +SmackerDecoder::SmackerVideoTrack *SmackerDecoder::createVideoTrack(uint32 width, uint32 height, uint32 frameCount, const Common::Rational &frameRate, uint32 flags, uint32 signature) const { + return new SmackerVideoTrack(width, height, frameCount, frameRate, flags, signature); } } // End of namespace Video diff --git a/video/smk_decoder.h b/video/smk_decoder.h index 516882e7c8..7227238373 100644 --- a/video/smk_decoder.h +++ b/video/smk_decoder.h @@ -34,6 +34,7 @@ class QueuingAudioStream; } namespace Common { +class BitStream; class SeekableReadStream; } @@ -56,42 +57,72 @@ class BigHuffmanTree; * - sword2 * - toon */ -class SmackerDecoder : public FixedRateVideoDecoder { +class SmackerDecoder : public VideoDecoder { public: - SmackerDecoder(Audio::Mixer *mixer, - Audio::Mixer::SoundType soundType = Audio::Mixer::kSFXSoundType); + SmackerDecoder(Audio::Mixer::SoundType soundType = Audio::Mixer::kSFXSoundType); virtual ~SmackerDecoder(); - bool loadStream(Common::SeekableReadStream *stream); + virtual bool loadStream(Common::SeekableReadStream *stream); void close(); - bool isVideoLoaded() const { return _fileStream != 0; } - uint16 getWidth() const { return _surface->w; } - uint16 getHeight() const { return _surface->h; } - uint32 getFrameCount() const { return _frameCount; } - uint32 getTime() const; - const Graphics::Surface *decodeNextFrame(); - Graphics::PixelFormat getPixelFormat() const { return Graphics::PixelFormat::createFormatCLUT8(); } - const byte *getPalette() { _dirtyPalette = false; return _palette; } - bool hasDirtyPalette() const { return _dirtyPalette; } - virtual void handleAudioTrack(byte track, uint32 chunkSize, uint32 unpackedSize); + bool rewind(); protected: - Common::SeekableReadStream *_fileStream; + void readNextPacket(); + + virtual void handleAudioTrack(byte track, uint32 chunkSize, uint32 unpackedSize); - // VideoDecoder API - void updateVolume(); - void updateBalance(); + class SmackerVideoTrack : public FixedRateVideoTrack { + public: + SmackerVideoTrack(uint32 width, uint32 height, uint32 frameCount, const Common::Rational &frameRate, uint32 flags, uint32 signature); + ~SmackerVideoTrack(); - // FixedRateVideoDecoder API - Common::Rational getFrameRate() const { return _frameRate; } + bool isRewindable() const { return true; } + bool rewind() { _curFrame = -1; return true; } -protected: - void unpackPalette(); - // Possible runs of blocks - uint getBlockRun(int index) { return (index <= 58) ? index + 1 : 128 << (index - 59); } - void queueCompressedBuffer(byte *buffer, uint32 bufferSize, uint32 unpackedSize, int streamNum); + uint16 getWidth() const; + uint16 getHeight() const; + Graphics::PixelFormat getPixelFormat() const; + int getCurFrame() const { return _curFrame; } + int getFrameCount() const { return _frameCount; } + const Graphics::Surface *decodeNextFrame() { return _surface; } + const byte *getPalette() const { _dirtyPalette = false; return _palette; } + bool hasDirtyPalette() const { return _dirtyPalette; } + + void readTrees(Common::BitStream &bs, uint32 mMapSize, uint32 mClrSize, uint32 fullSize, uint32 typeSize); + void increaseCurFrame() { _curFrame++; } + void decodeFrame(Common::BitStream &bs); + void unpackPalette(Common::SeekableReadStream *stream); + + protected: + Common::Rational getFrameRate() const { return _frameRate; } + + Graphics::Surface *_surface; + + private: + Common::Rational _frameRate; + uint32 _flags, _signature; + + byte _palette[3 * 256]; + mutable bool _dirtyPalette; + + int _curFrame; + uint32 _frameCount; + + BigHuffmanTree *_MMapTree; + BigHuffmanTree *_MClrTree; + BigHuffmanTree *_FullTree; + BigHuffmanTree *_TypeTree; + // Possible runs of blocks + static uint getBlockRun(int index) { return (index <= 58) ? index + 1 : 128 << (index - 59); } + }; + + virtual SmackerVideoTrack *createVideoTrack(uint32 width, uint32 height, uint32 frameCount, const Common::Rational &frameRate, uint32 flags, uint32 signature) const; + + Common::SeekableReadStream *_fileStream; + +private: enum AudioCompression { kCompressionNone, kCompressionDPCM, @@ -120,6 +151,28 @@ protected: uint32 dummy; } _header; + class SmackerAudioTrack : public AudioTrack { + public: + SmackerAudioTrack(const AudioInfo &audioInfo, Audio::Mixer::SoundType soundType); + ~SmackerAudioTrack(); + + bool isRewindable() const { return true; } + bool rewind(); + + Audio::Mixer::SoundType getSoundType() const { return _soundType; } + + void queueCompressedBuffer(byte *buffer, uint32 bufferSize, uint32 unpackedSize); + void queuePCM(byte *buffer, uint32 bufferSize); + + protected: + Audio::AudioStream *getAudioStream() const; + + private: + Audio::Mixer::SoundType _soundType; + Audio::QueuingAudioStream *_audioStream; + AudioInfo _audioInfo; + }; + uint32 *_frameSizes; // The FrameTypes section of a Smacker file contains an array of bytes, where // the 8 bits of each byte describe the contents of the corresponding frame. @@ -127,25 +180,10 @@ protected: // and so on), so there can be up to 7 different audio tracks. When the lowest bit // (bit 0) is set, it denotes a frame that contains a palette record byte *_frameTypes; - byte *_frameData; - // The RGB palette - byte _palette[3 * 256]; - bool _dirtyPalette; - Common::Rational _frameRate; - uint32 _frameCount; - Graphics::Surface *_surface; + uint32 _firstFrameStart; Audio::Mixer::SoundType _soundType; - Audio::Mixer *_mixer; - bool _audioStarted; - Audio::QueuingAudioStream *_audioStream; - Audio::SoundHandle _audioHandle; - - BigHuffmanTree *_MMapTree; - BigHuffmanTree *_MClrTree; - BigHuffmanTree *_FullTree; - BigHuffmanTree *_TypeTree; }; } // End of namespace Video diff --git a/video/theora_decoder.cpp b/video/theora_decoder.cpp new file mode 100644 index 0000000000..63aa93e2f5 --- /dev/null +++ b/video/theora_decoder.cpp @@ -0,0 +1,487 @@ +/* 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. + * + */ + +/* + * Source is based on the player example from libvorbis package, + * available at: http://svn.xiph.org/trunk/theora/examples/player_example.c + * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. + * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ + * + */ + +#include "video/theora_decoder.h" + +#include "audio/audiostream.h" +#include "audio/decoders/raw.h" +#include "common/stream.h" +#include "common/system.h" +#include "common/textconsole.h" +#include "common/util.h" +#include "graphics/pixelformat.h" +#include "graphics/yuv_to_rgb.h" + +namespace Video { + +TheoraDecoder::TheoraDecoder(Audio::Mixer::SoundType soundType) : _soundType(soundType) { + _fileStream = 0; + + _videoTrack = 0; + _audioTrack = 0; + _hasVideo = _hasAudio = false; +} + +TheoraDecoder::~TheoraDecoder() { + close(); +} + +bool TheoraDecoder::loadStream(Common::SeekableReadStream *stream) { + close(); + + _fileStream = stream; + + // start up Ogg stream synchronization layer + ogg_sync_init(&_oggSync); + + // init supporting Vorbis structures needed in header parsing + vorbis_info_init(&_vorbisInfo); + vorbis_comment vorbisComment; + vorbis_comment_init(&vorbisComment); + + // init supporting Theora structures needed in header parsing + th_info theoraInfo; + th_info_init(&theoraInfo); + th_comment theoraComment; + th_comment_init(&theoraComment); + th_setup_info *theoraSetup = 0; + + uint theoraPackets = 0, vorbisPackets = 0; + + // Ogg file open; parse the headers + // Only interested in Vorbis/Theora streams + bool foundHeader = false; + while (!foundHeader) { + int ret = bufferData(); + + if (ret == 0) + break; // FIXME: Shouldn't this error out? + + while (ogg_sync_pageout(&_oggSync, &_oggPage) > 0) { + ogg_stream_state test; + + // is this a mandated initial header? If not, stop parsing + if (!ogg_page_bos(&_oggPage)) { + // don't leak the page; get it into the appropriate stream + queuePage(&_oggPage); + foundHeader = true; + break; + } + + ogg_stream_init(&test, ogg_page_serialno(&_oggPage)); + ogg_stream_pagein(&test, &_oggPage); + ogg_stream_packetout(&test, &_oggPacket); + + // identify the codec: try theora + if (theoraPackets == 0 && th_decode_headerin(&theoraInfo, &theoraComment, &theoraSetup, &_oggPacket) >= 0) { + // it is theora + memcpy(&_theoraOut, &test, sizeof(test)); + theoraPackets = 1; + _hasVideo = true; + } else if (vorbisPackets == 0 && vorbis_synthesis_headerin(&_vorbisInfo, &vorbisComment, &_oggPacket) >= 0) { + // it is vorbis + memcpy(&_vorbisOut, &test, sizeof(test)); + vorbisPackets = 1; + _hasAudio = true; + } else { + // whatever it is, we don't care about it + ogg_stream_clear(&test); + } + } + // fall through to non-bos page parsing + } + + // we're expecting more header packets. + while ((theoraPackets && theoraPackets < 3) || (vorbisPackets && vorbisPackets < 3)) { + int ret; + + // look for further theora headers + while (theoraPackets && (theoraPackets < 3) && (ret = ogg_stream_packetout(&_theoraOut, &_oggPacket))) { + if (ret < 0) + error("Error parsing Theora stream headers; corrupt stream?"); + + if (!th_decode_headerin(&theoraInfo, &theoraComment, &theoraSetup, &_oggPacket)) + error("Error parsing Theora stream headers; corrupt stream?"); + + theoraPackets++; + } + + // look for more vorbis header packets + while (vorbisPackets && (vorbisPackets < 3) && (ret = ogg_stream_packetout(&_vorbisOut, &_oggPacket))) { + if (ret < 0) + error("Error parsing Vorbis stream headers; corrupt stream?"); + + if (vorbis_synthesis_headerin(&_vorbisInfo, &vorbisComment, &_oggPacket)) + error("Error parsing Vorbis stream headers; corrupt stream?"); + + vorbisPackets++; + + if (vorbisPackets == 3) + break; + } + + // The header pages/packets will arrive before anything else we + // care about, or the stream is not obeying spec + + if (ogg_sync_pageout(&_oggSync, &_oggPage) > 0) { + queuePage(&_oggPage); // demux into the appropriate stream + } else { + ret = bufferData(); // someone needs more data + + if (ret == 0) + error("End of file while searching for codec headers."); + } + } + + // And now we have it all. Initialize decoders next + if (_hasVideo) { + _videoTrack = new TheoraVideoTrack(getDefaultHighColorFormat(), theoraInfo, theoraSetup); + addTrack(_videoTrack); + } + + th_info_clear(&theoraInfo); + th_comment_clear(&theoraComment); + th_setup_free(theoraSetup); + + if (_hasAudio) { + _audioTrack = new VorbisAudioTrack(_soundType, _vorbisInfo); + + // Get enough audio data to start us off + while (!_audioTrack->hasAudio()) { + // Queue more data + bufferData(); + while (ogg_sync_pageout(&_oggSync, &_oggPage) > 0) + queuePage(&_oggPage); + + queueAudio(); + } + + addTrack(_audioTrack); + } + + vorbis_comment_clear(&vorbisComment); + + return true; +} + +void TheoraDecoder::close() { + VideoDecoder::close(); + + if (!_fileStream) + return; + + if (_videoTrack) { + ogg_stream_clear(&_theoraOut); + _videoTrack = 0; + } + + if (_audioTrack) { + ogg_stream_clear(&_vorbisOut); + _audioTrack = 0; + } + + ogg_sync_clear(&_oggSync); + vorbis_info_clear(&_vorbisInfo); + + delete _fileStream; + _fileStream = 0; + + _hasVideo = _hasAudio = false; +} + +void TheoraDecoder::readNextPacket() { + // First, let's get our frame + if (_hasVideo) { + while (!_videoTrack->endOfTrack()) { + // theora is one in, one out... + if (ogg_stream_packetout(&_theoraOut, &_oggPacket) > 0) { + if (_videoTrack->decodePacket(_oggPacket)) + break; + } else if (_theoraOut.e_o_s || _fileStream->eos()) { + // If we can't get any more frames, we're done. + _videoTrack->setEndOfVideo(); + } else { + // Queue more data + bufferData(); + while (ogg_sync_pageout(&_oggSync, &_oggPage) > 0) + queuePage(&_oggPage); + } + + // Update audio if we can + queueAudio(); + } + } + + // Then make sure we have enough audio buffered + ensureAudioBufferSize(); +} + +TheoraDecoder::TheoraVideoTrack::TheoraVideoTrack(const Graphics::PixelFormat &format, th_info &theoraInfo, th_setup_info *theoraSetup) { + _theoraDecode = th_decode_alloc(&theoraInfo, theoraSetup); + + if (theoraInfo.pixel_fmt != TH_PF_420) + error("Only theora YUV420 is supported"); + + int postProcessingMax; + th_decode_ctl(_theoraDecode, TH_DECCTL_GET_PPLEVEL_MAX, &postProcessingMax, sizeof(postProcessingMax)); + th_decode_ctl(_theoraDecode, TH_DECCTL_SET_PPLEVEL, &postProcessingMax, sizeof(postProcessingMax)); + + _surface.create(theoraInfo.frame_width, theoraInfo.frame_height, format); + + // Set up a display surface + _displaySurface.pixels = _surface.getBasePtr(theoraInfo.pic_x, theoraInfo.pic_y); + _displaySurface.w = theoraInfo.pic_width; + _displaySurface.h = theoraInfo.pic_height; + _displaySurface.format = format; + _displaySurface.pitch = _surface.pitch; + + // Set the frame rate + _frameRate = Common::Rational(theoraInfo.fps_numerator, theoraInfo.fps_denominator); + + _endOfVideo = false; + _nextFrameStartTime = 0.0; + _curFrame = -1; +} + +TheoraDecoder::TheoraVideoTrack::~TheoraVideoTrack() { + th_decode_free(_theoraDecode); + + _surface.free(); + _displaySurface.pixels = 0; +} + +bool TheoraDecoder::TheoraVideoTrack::decodePacket(ogg_packet &oggPacket) { + if (th_decode_packetin(_theoraDecode, &oggPacket, 0) == 0) { + _curFrame++; + + // Convert YUV data to RGB data + th_ycbcr_buffer yuv; + th_decode_ycbcr_out(_theoraDecode, yuv); + translateYUVtoRGBA(yuv); + + double time = th_granule_time(_theoraDecode, oggPacket.granulepos); + + // We need to calculate when the next frame should be shown + // This is all in floating point because that's what the Ogg code gives us + // Ogg is a lossy container format, so it doesn't always list the time to the + // next frame. In such cases, we need to calculate it ourselves. + if (time == -1.0) + _nextFrameStartTime += _frameRate.getInverse().toDouble(); + else + _nextFrameStartTime = time; + + return true; + } + + return false; +} + +enum TheoraYUVBuffers { + kBufferY = 0, + kBufferU = 1, + kBufferV = 2 +}; + +void TheoraDecoder::TheoraVideoTrack::translateYUVtoRGBA(th_ycbcr_buffer &YUVBuffer) { + // Width and height of all buffers have to be divisible by 2. + assert((YUVBuffer[kBufferY].width & 1) == 0); + assert((YUVBuffer[kBufferY].height & 1) == 0); + assert((YUVBuffer[kBufferU].width & 1) == 0); + assert((YUVBuffer[kBufferV].width & 1) == 0); + + // UV images have to have a quarter of the Y image resolution + assert(YUVBuffer[kBufferU].width == YUVBuffer[kBufferY].width >> 1); + assert(YUVBuffer[kBufferV].width == YUVBuffer[kBufferY].width >> 1); + assert(YUVBuffer[kBufferU].height == YUVBuffer[kBufferY].height >> 1); + assert(YUVBuffer[kBufferV].height == YUVBuffer[kBufferY].height >> 1); + + YUVToRGBMan.convert420(&_surface, Graphics::YUVToRGBManager::kScaleITU, YUVBuffer[kBufferY].data, YUVBuffer[kBufferU].data, YUVBuffer[kBufferV].data, YUVBuffer[kBufferY].width, YUVBuffer[kBufferY].height, YUVBuffer[kBufferY].stride, YUVBuffer[kBufferU].stride); +} + +static vorbis_info *info = 0; + +TheoraDecoder::VorbisAudioTrack::VorbisAudioTrack(Audio::Mixer::SoundType soundType, vorbis_info &vorbisInfo) : _soundType(soundType) { + vorbis_synthesis_init(&_vorbisDSP, &vorbisInfo); + vorbis_block_init(&_vorbisDSP, &_vorbisBlock); + info = &vorbisInfo; + + _audStream = Audio::makeQueuingAudioStream(vorbisInfo.rate, vorbisInfo.channels); + + _audioBufferFill = 0; + _audioBuffer = 0; + _endOfAudio = false; +} + +TheoraDecoder::VorbisAudioTrack::~VorbisAudioTrack() { + vorbis_dsp_clear(&_vorbisDSP); + vorbis_block_clear(&_vorbisBlock); + delete _audStream; + free(_audioBuffer); +} + +Audio::AudioStream *TheoraDecoder::VorbisAudioTrack::getAudioStream() const { + return _audStream; +} + +#define AUDIOFD_FRAGSIZE 10240 + +static double rint(double v) { + return floor(v + 0.5); +} + +bool TheoraDecoder::VorbisAudioTrack::decodeSamples() { + float **pcm; + + // if there's pending, decoded audio, grab it + int ret = vorbis_synthesis_pcmout(&_vorbisDSP, &pcm); + + if (ret > 0) { + if (!_audioBuffer) { + _audioBuffer = (ogg_int16_t *)malloc(AUDIOFD_FRAGSIZE * sizeof(ogg_int16_t)); + assert(_audioBuffer); + } + + int channels = _audStream->isStereo() ? 2 : 1; + int count = _audioBufferFill / 2; + int maxsamples = ((AUDIOFD_FRAGSIZE - _audioBufferFill) / channels) >> 1; + int i; + + for (i = 0; i < ret && i < maxsamples; i++) { + for (int j = 0; j < channels; j++) { + int val = CLIP((int)rint(pcm[j][i] * 32767.f), -32768, 32767); + _audioBuffer[count++] = val; + } + } + + vorbis_synthesis_read(&_vorbisDSP, i); + _audioBufferFill += (i * channels) << 1; + + if (_audioBufferFill == AUDIOFD_FRAGSIZE) { + byte flags = Audio::FLAG_16BITS; + + if (_audStream->isStereo()) + flags |= Audio::FLAG_STEREO; + +#ifdef SCUMM_LITTLE_ENDIAN + flags |= Audio::FLAG_LITTLE_ENDIAN; +#endif + + _audStream->queueBuffer((byte *)_audioBuffer, AUDIOFD_FRAGSIZE, DisposeAfterUse::YES, flags); + + // The audio mixer is now responsible for the old audio buffer. + // We need to create a new one. + _audioBuffer = 0; + _audioBufferFill = 0; + } + + return true; + } + + return false; +} + +bool TheoraDecoder::VorbisAudioTrack::hasAudio() const { + return _audStream->numQueuedStreams() > 0; +} + +bool TheoraDecoder::VorbisAudioTrack::needsAudio() const { + // TODO: 5 is very arbitrary. We probably should do something like QuickTime does. + return !_endOfAudio && _audStream->numQueuedStreams() < 5; +} + +void TheoraDecoder::VorbisAudioTrack::synthesizePacket(ogg_packet &oggPacket) { + if (vorbis_synthesis(&_vorbisBlock, &oggPacket) == 0) // test for success + vorbis_synthesis_blockin(&_vorbisDSP, &_vorbisBlock); +} + +void TheoraDecoder::queuePage(ogg_page *page) { + if (_hasVideo) + ogg_stream_pagein(&_theoraOut, page); + + if (_hasAudio) + ogg_stream_pagein(&_vorbisOut, page); +} + +int TheoraDecoder::bufferData() { + char *buffer = ogg_sync_buffer(&_oggSync, 4096); + int bytes = _fileStream->read(buffer, 4096); + + ogg_sync_wrote(&_oggSync, bytes); + + return bytes; +} + +bool TheoraDecoder::queueAudio() { + if (!_hasAudio) + return false; + + bool queuedAudio = false; + + for (;;) { + if (_audioTrack->decodeSamples()) { + // we queued some pending audio + queuedAudio = true; + } else if (ogg_stream_packetout(&_vorbisOut, &_oggPacket) > 0) { + // no pending audio; is there a pending packet to decode? + _audioTrack->synthesizePacket(_oggPacket); + } else { + // we've buffered all we have, break out for now + break; + } + } + + return queuedAudio; +} + +void TheoraDecoder::ensureAudioBufferSize() { + if (!_hasAudio) + return; + + // Force at least some audio to be buffered + while (_audioTrack->needsAudio()) { + bufferData(); + while (ogg_sync_pageout(&_oggSync, &_oggPage) > 0) + queuePage(&_oggPage); + + bool queuedAudio = queueAudio(); + if ((_vorbisOut.e_o_s || _fileStream->eos()) && !queuedAudio) { + _audioTrack->setEndOfAudio(); + break; + } + } +} + +} // End of namespace Video diff --git a/video/theora_decoder.h b/video/theora_decoder.h new file mode 100644 index 0000000000..ac808cdfe6 --- /dev/null +++ b/video/theora_decoder.h @@ -0,0 +1,158 @@ +/* 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 "common/scummsys.h" // for USE_THEORADEC + +#ifdef USE_THEORADEC + +#ifndef VIDEO_THEORA_DECODER_H +#define VIDEO_THEORA_DECODER_H + +#include "common/rational.h" +#include "video/video_decoder.h" +#include "audio/mixer.h" +#include "graphics/surface.h" + +#include <theora/theoradec.h> +#include <vorbis/codec.h> + +namespace Common { +class SeekableReadStream; +} + +namespace Audio { +class AudioStream; +class QueuingAudioStream; +} + +namespace Video { + +/** + * + * Decoder for Theora videos. + * Video decoder used in engines: + * - sword25 + * - wintermute + */ +class TheoraDecoder : public VideoDecoder { +public: + TheoraDecoder(Audio::Mixer::SoundType soundType = Audio::Mixer::kMusicSoundType); + virtual ~TheoraDecoder(); + + /** + * Load a video file + * @param stream the stream to load + */ + bool loadStream(Common::SeekableReadStream *stream); + void close(); + +protected: + void readNextPacket(); + +private: + class TheoraVideoTrack : public VideoTrack { + public: + TheoraVideoTrack(const Graphics::PixelFormat &format, th_info &theoraInfo, th_setup_info *theoraSetup); + ~TheoraVideoTrack(); + + bool endOfTrack() const { return _endOfVideo; } + uint16 getWidth() const { return _displaySurface.w; } + uint16 getHeight() const { return _displaySurface.h; } + Graphics::PixelFormat getPixelFormat() const { return _displaySurface.format; } + int getCurFrame() const { return _curFrame; } + uint32 getNextFrameStartTime() const { return (uint32)(_nextFrameStartTime * 1000); } + const Graphics::Surface *decodeNextFrame() { return &_displaySurface; } + + bool decodePacket(ogg_packet &oggPacket); + void setEndOfVideo() { _endOfVideo = true; } + + private: + int _curFrame; + bool _endOfVideo; + Common::Rational _frameRate; + double _nextFrameStartTime; + + Graphics::Surface _surface; + Graphics::Surface _displaySurface; + + th_dec_ctx *_theoraDecode; + + void translateYUVtoRGBA(th_ycbcr_buffer &YUVBuffer); + }; + + class VorbisAudioTrack : public AudioTrack { + public: + VorbisAudioTrack(Audio::Mixer::SoundType soundType, vorbis_info &vorbisInfo); + ~VorbisAudioTrack(); + + Audio::Mixer::SoundType getSoundType() const { return _soundType; } + + bool decodeSamples(); + bool hasAudio() const; + bool needsAudio() const; + void synthesizePacket(ogg_packet &oggPacket); + void setEndOfAudio() { _endOfAudio = true; } + + protected: + Audio::AudioStream *getAudioStream() const; + + private: + // single audio fragment audio buffering + int _audioBufferFill; + ogg_int16_t *_audioBuffer; + + Audio::Mixer::SoundType _soundType; + Audio::QueuingAudioStream *_audStream; + + vorbis_block _vorbisBlock; + vorbis_dsp_state _vorbisDSP; + + bool _endOfAudio; + }; + + void queuePage(ogg_page *page); + int bufferData(); + bool queueAudio(); + void ensureAudioBufferSize(); + + Common::SeekableReadStream *_fileStream; + + Audio::Mixer::SoundType _soundType; + + ogg_sync_state _oggSync; + ogg_page _oggPage; + ogg_packet _oggPacket; + + ogg_stream_state _theoraOut, _vorbisOut; + bool _hasVideo, _hasAudio; + + vorbis_info _vorbisInfo; + + TheoraVideoTrack *_videoTrack; + VorbisAudioTrack *_audioTrack; +}; + +} // End of namespace Video + +#endif + +#endif diff --git a/video/video_decoder.cpp b/video/video_decoder.cpp index 44d7917652..ebe15c5fc1 100644 --- a/video/video_decoder.cpp +++ b/video/video_decoder.cpp @@ -22,6 +22,7 @@ #include "video/video_decoder.h" +#include "audio/audiostream.h" #include "audio/mixer.h" // for kMaxChannelVolume #include "common/rational.h" @@ -33,7 +34,45 @@ namespace Video { VideoDecoder::VideoDecoder() { - reset(); + _startTime = 0; + _dirtyPalette = false; + _palette = 0; + _playbackRate = 0; + _audioVolume = Audio::Mixer::kMaxChannelVolume; + _audioBalance = 0; + _pauseLevel = 0; + _needsUpdate = false; + _lastTimeChange = 0; + _endTime = 0; + _endTimeSet = false; + _nextVideoTrack = 0; + + // Find the best format for output + _defaultHighColorFormat = g_system->getScreenFormat(); + + if (_defaultHighColorFormat.bytesPerPixel == 1) + _defaultHighColorFormat = Graphics::PixelFormat(4, 8, 8, 8, 8, 8, 16, 24, 0); +} + +void VideoDecoder::close() { + if (isPlaying()) + stop(); + + for (TrackList::iterator it = _tracks.begin(); it != _tracks.end(); it++) + delete *it; + + _tracks.clear(); + _dirtyPalette = false; + _palette = 0; + _startTime = 0; + _audioVolume = Audio::Mixer::kMaxChannelVolume; + _audioBalance = 0; + _pauseLevel = 0; + _needsUpdate = false; + _lastTimeChange = 0; + _endTime = 0; + _endTimeSet = false; + _nextVideoTrack = 0; } bool VideoDecoder::loadFile(const Common::String &filename) { @@ -47,28 +86,8 @@ bool VideoDecoder::loadFile(const Common::String &filename) { return loadStream(file); } -uint32 VideoDecoder::getTime() const { - return g_system->getMillis() - _startTime; -} - -void VideoDecoder::setSystemPalette() { - g_system->getPaletteManager()->setPalette(getPalette(), 0, 256); -} - bool VideoDecoder::needsUpdate() const { - return !endOfVideo() && getTimeToNextFrame() == 0; -} - -void VideoDecoder::reset() { - _curFrame = -1; - _startTime = 0; - _pauseLevel = 0; - _audioVolume = Audio::Mixer::kMaxChannelVolume; - _audioBalance = 0; -} - -bool VideoDecoder::endOfVideo() const { - return !isVideoLoaded() || (getCurFrame() >= (int32)getFrameCount() - 1); + return hasFramesLeft() && getTimeToNextFrame() == 0; } void VideoDecoder::pauseVideo(bool pause) { @@ -86,10 +105,14 @@ void VideoDecoder::pauseVideo(bool pause) { if (_pauseLevel == 1 && pause) { _pauseStartTime = g_system->getMillis(); // Store the starting time from pausing to keep it for later - pauseVideoIntern(true); + + for (TrackList::iterator it = _tracks.begin(); it != _tracks.end(); it++) + (*it)->pause(true); } else if (_pauseLevel == 0) { - pauseVideoIntern(false); - addPauseTime(g_system->getMillis() - _pauseStartTime); + for (TrackList::iterator it = _tracks.begin(); it != _tracks.end(); it++) + (*it)->pause(false); + + _startTime += (g_system->getMillis() - _pauseStartTime); } } @@ -100,33 +123,667 @@ void VideoDecoder::resetPauseStartTime() { void VideoDecoder::setVolume(byte volume) { _audioVolume = volume; - updateVolume(); + + for (TrackList::iterator it = _tracks.begin(); it != _tracks.end(); it++) + if ((*it)->getTrackType() == Track::kTrackTypeAudio) + ((AudioTrack *)*it)->setVolume(_audioVolume); } void VideoDecoder::setBalance(int8 balance) { _audioBalance = balance; - updateBalance(); + + for (TrackList::iterator it = _tracks.begin(); it != _tracks.end(); it++) + if ((*it)->getTrackType() == Track::kTrackTypeAudio) + ((AudioTrack *)*it)->setBalance(_audioBalance); +} + +bool VideoDecoder::isVideoLoaded() const { + return !_tracks.empty(); +} + +uint16 VideoDecoder::getWidth() const { + for (TrackList::const_iterator it = _tracks.begin(); it != _tracks.end(); it++) + if ((*it)->getTrackType() == Track::kTrackTypeVideo) + return ((VideoTrack *)*it)->getWidth(); + + return 0; +} + +uint16 VideoDecoder::getHeight() const { + for (TrackList::const_iterator it = _tracks.begin(); it != _tracks.end(); it++) + if ((*it)->getTrackType() == Track::kTrackTypeVideo) + return ((VideoTrack *)*it)->getHeight(); + + return 0; +} + +Graphics::PixelFormat VideoDecoder::getPixelFormat() const { + for (TrackList::const_iterator it = _tracks.begin(); it != _tracks.end(); it++) + if ((*it)->getTrackType() == Track::kTrackTypeVideo) + return ((VideoTrack *)*it)->getPixelFormat(); + + return Graphics::PixelFormat(); +} + +const Graphics::Surface *VideoDecoder::decodeNextFrame() { + _needsUpdate = false; + + readNextPacket(); + + // If we have no next video track at this point, there shouldn't be + // any frame available for us to display. + if (!_nextVideoTrack) + return 0; + + const Graphics::Surface *frame = _nextVideoTrack->decodeNextFrame(); + + if (_nextVideoTrack->hasDirtyPalette()) { + _palette = _nextVideoTrack->getPalette(); + _dirtyPalette = true; + } + + // Look for the next video track here for the next decode. + findNextVideoTrack(); + + return frame; +} + +bool VideoDecoder::setReverse(bool reverse) { + // Can only reverse video-only videos + if (reverse && hasAudio()) + return false; + + // Attempt to make sure all the tracks are in the requested direction + for (TrackList::iterator it = _tracks.begin(); it != _tracks.end(); it++) { + if ((*it)->getTrackType() == Track::kTrackTypeVideo && ((VideoTrack *)*it)->isReversed() != reverse) { + if (!((VideoTrack *)*it)->setReverse(reverse)) + return false; + + _needsUpdate = true; // force an update + } + } + + findNextVideoTrack(); + return true; +} + +const byte *VideoDecoder::getPalette() { + _dirtyPalette = false; + return _palette; +} + +int VideoDecoder::getCurFrame() const { + int32 frame = -1; + + for (TrackList::const_iterator it = _tracks.begin(); it != _tracks.end(); it++) + if ((*it)->getTrackType() == Track::kTrackTypeVideo) + frame += ((VideoTrack *)*it)->getCurFrame() + 1; + + return frame; +} + +uint32 VideoDecoder::getFrameCount() const { + int count = 0; + + for (TrackList::const_iterator it = _tracks.begin(); it != _tracks.end(); it++) + if ((*it)->getTrackType() == Track::kTrackTypeVideo) + count += ((VideoTrack *)*it)->getFrameCount(); + + return count; +} + +uint32 VideoDecoder::getTime() const { + if (!isPlaying()) + return _lastTimeChange.msecs(); + + if (isPaused()) + return MAX<int>((_playbackRate * (_pauseStartTime - _startTime)).toInt(), 0); + + if (useAudioSync()) { + for (TrackList::const_iterator it = _tracks.begin(); it != _tracks.end(); it++) { + if ((*it)->getTrackType() == Track::kTrackTypeAudio && !(*it)->endOfTrack()) { + uint32 time = ((const AudioTrack *)*it)->getRunningTime(); + + if (time != 0) + return time + _lastTimeChange.msecs(); + } + } + } + + return MAX<int>((_playbackRate * (g_system->getMillis() - _startTime)).toInt(), 0); +} + +uint32 VideoDecoder::getTimeToNextFrame() const { + if (endOfVideo() || _needsUpdate || !_nextVideoTrack) + return 0; + + uint32 currentTime = getTime(); + uint32 nextFrameStartTime = _nextVideoTrack->getNextFrameStartTime(); + + if (_nextVideoTrack->isReversed()) { + // For reversed videos, we need to handle the time difference the opposite way. + if (nextFrameStartTime >= currentTime) + return 0; + + return currentTime - nextFrameStartTime; + } + + // Otherwise, handle it normally. + if (nextFrameStartTime <= currentTime) + return 0; + + return nextFrameStartTime - currentTime; +} + +bool VideoDecoder::endOfVideo() const { + for (TrackList::const_iterator it = _tracks.begin(); it != _tracks.end(); it++) + if (!(*it)->endOfTrack() && (!isPlaying() || (*it)->getTrackType() != Track::kTrackTypeVideo || !_endTimeSet || ((VideoTrack *)*it)->getNextFrameStartTime() < (uint)_endTime.msecs())) + return false; + + return true; +} + +bool VideoDecoder::isRewindable() const { + if (!isVideoLoaded()) + return false; + + for (TrackList::const_iterator it = _tracks.begin(); it != _tracks.end(); it++) + if (!(*it)->isRewindable()) + return false; + + return true; +} + +bool VideoDecoder::rewind() { + if (!isRewindable()) + return false; + + // Stop all tracks so they can be rewound + if (isPlaying()) + stopAudio(); + + for (TrackList::iterator it = _tracks.begin(); it != _tracks.end(); it++) + if (!(*it)->rewind()) + return false; + + // Now that we've rewound, start all tracks again + if (isPlaying()) + startAudio(); + + _lastTimeChange = 0; + _startTime = g_system->getMillis(); + resetPauseStartTime(); + findNextVideoTrack(); + return true; +} + +bool VideoDecoder::isSeekable() const { + if (!isVideoLoaded()) + return false; + + for (TrackList::const_iterator it = _tracks.begin(); it != _tracks.end(); it++) + if (!(*it)->isSeekable()) + return false; + + return true; +} + +bool VideoDecoder::seek(const Audio::Timestamp &time) { + if (!isSeekable()) + return false; + + // Stop all tracks so they can be seeked + if (isPlaying()) + stopAudio(); + + for (TrackList::iterator it = _tracks.begin(); it != _tracks.end(); it++) + if (!(*it)->seek(time)) + return false; + + _lastTimeChange = time; + + // Now that we've seeked, start all tracks again + // Also reset our start time + if (isPlaying()) { + startAudio(); + _startTime = g_system->getMillis() - (time.msecs() / _playbackRate).toInt(); + } + + resetPauseStartTime(); + findNextVideoTrack(); + _needsUpdate = true; + return true; +} + +bool VideoDecoder::seekToFrame(uint frame) { + VideoTrack *track = 0; + + for (TrackList::iterator it = _tracks.begin(); it != _tracks.end(); it++) { + if (!(*it)->isSeekable()) + return false; + + if ((*it)->getTrackType() == Track::kTrackTypeVideo) { + // We only allow seeking by frame when one video track + // is present + if (track) + return false; + + track = (VideoTrack *)*it; + } + } + + // If we didn't find a video track, we can't seek by frame (of course) + if (!track) + return false; + + Audio::Timestamp time = track->getFrameTime(frame); + + if (time < 0) + return false; + + return seek(time); +} + +void VideoDecoder::start() { + if (!isPlaying()) + setRate(1); +} + +void VideoDecoder::stop() { + if (!isPlaying()) + return; + + // Stop audio here so we don't have it affect getTime() + stopAudio(); + + // Keep the time marked down in case we start up again + // We do this before _playbackRate is set so we don't get + // _lastTimeChange returned, but before _pauseLevel is + // reset. + _lastTimeChange = getTime(); + + _playbackRate = 0; + _startTime = 0; + _palette = 0; + _dirtyPalette = false; + _needsUpdate = false; + + // Also reset the pause state. + _pauseLevel = 0; + + // Reset the pause state of the tracks too + for (TrackList::iterator it = _tracks.begin(); it != _tracks.end(); it++) + (*it)->pause(false); +} + +void VideoDecoder::setRate(const Common::Rational &rate) { + if (!isVideoLoaded() || _playbackRate == rate) + return; + + if (rate == 0) { + stop(); + return; + } else if (rate != 1 && hasAudio()) { + warning("Cannot set custom rate in videos with audio"); + return; + } + + Common::Rational targetRate = rate; + + // Attempt to set the reverse + if (!setReverse(rate < 0)) { + assert(rate < 0); // We shouldn't fail for forward. + warning("Cannot set custom rate to backwards"); + setReverse(false); + targetRate = 1; + + if (_playbackRate == targetRate) + return; + } + + if (_playbackRate != 0) + _lastTimeChange = getTime(); + + _playbackRate = targetRate; + _startTime = g_system->getMillis(); + + // Adjust start time if we've seeked to something besides zero time + if (_lastTimeChange != 0) + _startTime -= (_lastTimeChange.msecs() / _playbackRate).toInt(); + + startAudio(); +} + +bool VideoDecoder::isPlaying() const { + return _playbackRate != 0; +} + +Audio::Timestamp VideoDecoder::getDuration() const { + Audio::Timestamp maxDuration(0, 1000); + + for (TrackList::const_iterator it = _tracks.begin(); it != _tracks.end(); it++) { + Audio::Timestamp duration = (*it)->getDuration(); + + if (duration > maxDuration) + maxDuration = duration; + } + + return maxDuration; +} + +VideoDecoder::Track::Track() { + _paused = false; +} + +bool VideoDecoder::Track::isRewindable() const { + return isSeekable(); +} + +bool VideoDecoder::Track::rewind() { + return seek(Audio::Timestamp(0, 1000)); +} + +void VideoDecoder::Track::pause(bool shouldPause) { + _paused = shouldPause; + pauseIntern(shouldPause); +} + +Audio::Timestamp VideoDecoder::Track::getDuration() const { + return Audio::Timestamp(0, 1000); +} + +bool VideoDecoder::VideoTrack::endOfTrack() const { + return getCurFrame() >= (getFrameCount() - 1); +} + +Audio::Timestamp VideoDecoder::VideoTrack::getFrameTime(uint frame) const { + // Default implementation: Return an invalid (negative) number + return Audio::Timestamp().addFrames(-1); +} + +uint32 VideoDecoder::FixedRateVideoTrack::getNextFrameStartTime() const { + if (endOfTrack() || getCurFrame() < 0) + return 0; + + return getFrameTime(getCurFrame() + 1).msecs(); +} + +Audio::Timestamp VideoDecoder::FixedRateVideoTrack::getFrameTime(uint frame) const { + // Try to get as accurate as possible, considering we have a fractional frame rate + // (which Audio::Timestamp doesn't support). + Common::Rational frameRate = getFrameRate(); + + if (frameRate == frameRate.toInt()) // The nice case (a whole number) + return Audio::Timestamp(0, frame, frameRate.toInt()); + + // Just convert to milliseconds. + Common::Rational time = frame * 1000; + time /= frameRate; + return Audio::Timestamp(time.toInt(), 1000); +} + +uint VideoDecoder::FixedRateVideoTrack::getFrameAtTime(const Audio::Timestamp &time) const { + Common::Rational frameRate = getFrameRate(); + + // Easy conversion + if (frameRate == time.framerate()) + return time.totalNumberOfFrames(); + + // Default case + return (time.totalNumberOfFrames() * frameRate / time.framerate()).toInt(); +} + +Audio::Timestamp VideoDecoder::FixedRateVideoTrack::getDuration() const { + return getFrameTime(getFrameCount()); +} + +bool VideoDecoder::AudioTrack::endOfTrack() const { + Audio::AudioStream *stream = getAudioStream(); + return !stream || !g_system->getMixer()->isSoundHandleActive(_handle) || stream->endOfData(); +} + +void VideoDecoder::AudioTrack::setVolume(byte volume) { + _volume = volume; + + if (g_system->getMixer()->isSoundHandleActive(_handle)) + g_system->getMixer()->setChannelVolume(_handle, _volume); +} + +void VideoDecoder::AudioTrack::setBalance(int8 balance) { + _balance = balance; + + if (g_system->getMixer()->isSoundHandleActive(_handle)) + g_system->getMixer()->setChannelBalance(_handle, _balance); +} + +void VideoDecoder::AudioTrack::start() { + stop(); + + Audio::AudioStream *stream = getAudioStream(); + assert(stream); + + g_system->getMixer()->playStream(getSoundType(), &_handle, stream, -1, getVolume(), getBalance(), DisposeAfterUse::NO); + + // Pause the audio again if we're still paused + if (isPaused()) + g_system->getMixer()->pauseHandle(_handle, true); +} + +void VideoDecoder::AudioTrack::stop() { + g_system->getMixer()->stopHandle(_handle); +} + +void VideoDecoder::AudioTrack::start(const Audio::Timestamp &limit) { + stop(); + + Audio::AudioStream *stream = getAudioStream(); + assert(stream); + + stream = Audio::makeLimitingAudioStream(stream, limit, DisposeAfterUse::NO); + + g_system->getMixer()->playStream(getSoundType(), &_handle, stream, -1, getVolume(), getBalance(), DisposeAfterUse::YES); + + // Pause the audio again if we're still paused + if (isPaused()) + g_system->getMixer()->pauseHandle(_handle, true); +} + +uint32 VideoDecoder::AudioTrack::getRunningTime() const { + if (g_system->getMixer()->isSoundHandleActive(_handle)) + return g_system->getMixer()->getSoundElapsedTime(_handle); + + return 0; +} + +void VideoDecoder::AudioTrack::pauseIntern(bool shouldPause) { + if (g_system->getMixer()->isSoundHandleActive(_handle)) + g_system->getMixer()->pauseHandle(_handle, shouldPause); +} + +Audio::AudioStream *VideoDecoder::RewindableAudioTrack::getAudioStream() const { + return getRewindableAudioStream(); +} + +bool VideoDecoder::RewindableAudioTrack::rewind() { + Audio::RewindableAudioStream *stream = getRewindableAudioStream(); + assert(stream); + return stream->rewind(); +} + +Audio::Timestamp VideoDecoder::SeekableAudioTrack::getDuration() const { + Audio::SeekableAudioStream *stream = getSeekableAudioStream(); + assert(stream); + return stream->getLength(); +} + +Audio::AudioStream *VideoDecoder::SeekableAudioTrack::getAudioStream() const { + return getSeekableAudioStream(); +} + +bool VideoDecoder::SeekableAudioTrack::seek(const Audio::Timestamp &time) { + Audio::SeekableAudioStream *stream = getSeekableAudioStream(); + assert(stream); + return stream->seek(time); +} + +VideoDecoder::StreamFileAudioTrack::StreamFileAudioTrack() { + _stream = 0; +} + +VideoDecoder::StreamFileAudioTrack::~StreamFileAudioTrack() { + delete _stream; +} + +bool VideoDecoder::StreamFileAudioTrack::loadFromFile(const Common::String &baseName) { + // TODO: Make sure the stream isn't being played + delete _stream; + _stream = Audio::SeekableAudioStream::openStreamFile(baseName); + return _stream != 0; +} + +void VideoDecoder::addTrack(Track *track) { + _tracks.push_back(track); + + if (track->getTrackType() == Track::kTrackTypeAudio) { + // Update volume settings if it's an audio track + ((AudioTrack *)track)->setVolume(_audioVolume); + ((AudioTrack *)track)->setBalance(_audioBalance); + } else if (track->getTrackType() == Track::kTrackTypeVideo) { + // If this track has a better time, update _nextVideoTrack + if (!_nextVideoTrack || ((VideoTrack *)track)->getNextFrameStartTime() < _nextVideoTrack->getNextFrameStartTime()) + _nextVideoTrack = (VideoTrack *)track; + } + + // Keep the track paused if we're paused + if (isPaused()) + track->pause(true); + + // Start the track if we're playing + if (isPlaying() && track->getTrackType() == Track::kTrackTypeAudio) + ((AudioTrack *)track)->start(); } -uint32 FixedRateVideoDecoder::getTimeToNextFrame() const { - if (endOfVideo() || _curFrame < 0) +bool VideoDecoder::addStreamFileTrack(const Common::String &baseName) { + // Only allow adding external tracks if a video is already loaded + if (!isVideoLoaded()) + return false; + + StreamFileAudioTrack *track = new StreamFileAudioTrack(); + + bool result = track->loadFromFile(baseName); + + if (result) + addTrack(track); + + return result; +} + +void VideoDecoder::setEndTime(const Audio::Timestamp &endTime) { + Audio::Timestamp startTime = 0; + + if (isPlaying()) { + startTime = getTime(); + stopAudio(); + } + + _endTime = endTime; + _endTimeSet = true; + + if (startTime > endTime) + return; + + if (isPlaying()) { + // We'll assume the audio track is going to start up at the same time it just was + // and therefore not do any seeking. + // Might want to set it anyway if we're seekable. + startAudioLimit(_endTime.msecs() - startTime.msecs()); + _lastTimeChange = startTime; + } +} + +VideoDecoder::Track *VideoDecoder::getTrack(uint track) { + if (track > _tracks.size()) return 0; - uint32 elapsedTime = getTime(); - uint32 nextFrameStartTime = getFrameBeginTime(_curFrame + 1); + return _tracks[track]; +} - // If the time that the next frame should be shown has past - // the frame should be shown ASAP. - if (nextFrameStartTime <= elapsedTime) +const VideoDecoder::Track *VideoDecoder::getTrack(uint track) const { + if (track > _tracks.size()) return 0; - return nextFrameStartTime - elapsedTime; + return _tracks[track]; } -uint32 FixedRateVideoDecoder::getFrameBeginTime(uint32 frame) const { - Common::Rational beginTime = frame * 1000; - beginTime /= getFrameRate(); - return beginTime.toInt(); +bool VideoDecoder::endOfVideoTracks() const { + for (TrackList::const_iterator it = _tracks.begin(); it != _tracks.end(); it++) + if ((*it)->getTrackType() == Track::kTrackTypeVideo && !(*it)->endOfTrack()) + return false; + + return true; +} + +VideoDecoder::VideoTrack *VideoDecoder::findNextVideoTrack() { + _nextVideoTrack = 0; + uint32 bestTime = 0xFFFFFFFF; + + for (TrackList::iterator it = _tracks.begin(); it != _tracks.end(); it++) { + if ((*it)->getTrackType() == Track::kTrackTypeVideo && !(*it)->endOfTrack()) { + VideoTrack *track = (VideoTrack *)*it; + uint32 time = track->getNextFrameStartTime(); + + if (time < bestTime) { + bestTime = time; + _nextVideoTrack = track; + } + } + } + + return _nextVideoTrack; +} + +void VideoDecoder::startAudio() { + if (_endTimeSet) { + // HACK: Timestamp's subtraction asserts out when subtracting two times + // with different rates. + startAudioLimit(_endTime - _lastTimeChange.convertToFramerate(_endTime.framerate())); + return; + } + + for (TrackList::iterator it = _tracks.begin(); it != _tracks.end(); it++) + if ((*it)->getTrackType() == Track::kTrackTypeAudio) + ((AudioTrack *)*it)->start(); +} + +void VideoDecoder::stopAudio() { + for (TrackList::iterator it = _tracks.begin(); it != _tracks.end(); it++) + if ((*it)->getTrackType() == Track::kTrackTypeAudio) + ((AudioTrack *)*it)->stop(); +} + +void VideoDecoder::startAudioLimit(const Audio::Timestamp &limit) { + for (TrackList::iterator it = _tracks.begin(); it != _tracks.end(); it++) + if ((*it)->getTrackType() == Track::kTrackTypeAudio) + ((AudioTrack *)*it)->start(limit); +} + +bool VideoDecoder::hasFramesLeft() const { + // This is similar to endOfVideo(), except it doesn't take Audio into account (and returns true if not the end of the video) + // This is only used for needsUpdate() atm so that setEndTime() works properly + // And unlike endOfVideoTracks(), this takes into account _endTime + for (TrackList::const_iterator it = _tracks.begin(); it != _tracks.end(); it++) + if ((*it)->getTrackType() == Track::kTrackTypeVideo && !(*it)->endOfTrack() && (!isPlaying() || !_endTimeSet || ((VideoTrack *)*it)->getNextFrameStartTime() < (uint)_endTime.msecs())) + return true; + + return false; +} + +bool VideoDecoder::hasAudio() const { + for (TrackList::const_iterator it = _tracks.begin(); it != _tracks.end(); it++) + if ((*it)->getTrackType() == Track::kTrackTypeAudio) + return true; + + return false; } } // End of namespace Video diff --git a/video/video_decoder.h b/video/video_decoder.h index 3bb75ade09..d0a6e08005 100644 --- a/video/video_decoder.h +++ b/video/video_decoder.h @@ -23,18 +23,24 @@ #ifndef VIDEO_DECODER_H #define VIDEO_DECODER_H -#include "common/str.h" - +#include "audio/mixer.h" #include "audio/timestamp.h" // TODO: Move this to common/ ? +#include "common/array.h" +#include "common/rational.h" +#include "common/str.h" +#include "graphics/pixelformat.h" +namespace Audio { +class AudioStream; +class RewindableAudioStream; +class SeekableAudioStream; +} namespace Common { -class Rational; class SeekableReadStream; } namespace Graphics { -struct PixelFormat; struct Surface; } @@ -48,10 +54,14 @@ public: VideoDecoder(); virtual ~VideoDecoder() {} + ///////////////////////////////////////// + // Opening/Closing a Video + ///////////////////////////////////////// + /** * Load a video from a file with the given name. * - * A default implementation using loadStream is provided. + * A default implementation using Common::File and loadStream is provided. * * @param filename the filename to load * @return whether loading the file succeeded @@ -62,6 +72,10 @@ public: * Load a video from a generic read stream. The ownership of the * stream object transfers to this VideoDecoder instance, which is * hence also responsible for eventually deleting it. + * + * Implementations of this function are required to call addTrack() + * for each track in the video upon success. + * * @param stream the stream to load * @return whether loading the stream succeeded */ @@ -69,60 +83,162 @@ public: /** * Close the active video stream and free any associated resources. + * + * All subclasses that need to close their own resources should still + * call the base class' close() function at the start of their function. */ - virtual void close() = 0; + virtual void close(); /** * Returns if a video stream is currently loaded or not. */ - virtual bool isVideoLoaded() const = 0; + bool isVideoLoaded() const; + ///////////////////////////////////////// + // Playback Control + ///////////////////////////////////////// /** - * Returns the width of the video's frames. - * @return the width of the video's frames + * Begin playback of the video at normal speed. + * + * @note This has no effect if the video is already playing. */ - virtual uint16 getWidth() const = 0; + void start(); /** - * Returns the height of the video's frames. - * @return the height of the video's frames + * Stop playback of the video. + * + * @note This has no effect if the video is not playing. */ - virtual uint16 getHeight() const = 0; + void stop(); /** - * Get the pixel format of the currently loaded video. + * Set the rate of playback. + * + * For instance, a rate of 0 would stop the video, while a rate of 1 + * would play the video normally. Passing 2 to this function would + * play the video at twice the normal speed. + * + * @note This function does not work for non-0/1 rates on videos that + * have audio tracks. + * + * @todo This currently does not implement backwards playback, but will + * be implemented soon. */ - virtual Graphics::PixelFormat getPixelFormat() const = 0; + void setRate(const Common::Rational &rate); /** - * Get the palette for the video in RGB format (if 8bpp or less). + * Returns the rate at which the video is being played. */ - virtual const byte *getPalette() { return 0; } + Common::Rational getRate() const { return _playbackRate; } /** - * Returns if the palette is dirty or not. + * Returns if the video is currently playing or not. + * + * This is not equivalent to the inverse of endOfVideo(). A video keeps + * its playing status even after reaching the end of the video. This will + * return true after calling start() and will continue to return true + * until stop() (or close()) is called. + */ + bool isPlaying() const; + + /** + * Returns if a video is rewindable or not. The default implementation + * polls each track for rewindability. + */ + virtual bool isRewindable() const; + + /** + * Rewind a video to its beginning. + * + * If the video is playing, it will continue to play. The default + * implementation will rewind each track. + * + * @return true on success, false otherwise + */ + virtual bool rewind(); + + /** + * Returns if a video is seekable or not. The default implementation + * polls each track for seekability. + */ + virtual bool isSeekable() const; + + /** + * Seek to a given time in the video. + * + * If the video is playing, it will continue to play. The default + * implementation will seek each track and must still be called + * from any other implementation. + * + * @param time The time to seek to + * @return true on success, false otherwise + */ + virtual bool seek(const Audio::Timestamp &time); + + /** + * Seek to a given frame. + * + * This only works when one video track is present, and that track + * supports getFrameTime(). This calls seek() internally. */ - virtual bool hasDirtyPalette() const { return false; } + bool seekToFrame(uint frame); /** - * Set the system palette to the palette returned by getPalette. - * @see getPalette + * Pause or resume the video. This should stop/resume any audio playback + * and other stuff. The initial pause time is kept so that any timing + * variables can be updated appropriately. + * + * This is a convenience method which automatically keeps track on how + * often the video has been paused, ensuring that after pausing an video + * e.g. twice, it has to be unpaused twice before actuallying resuming. + * + * @param pause true to pause the video, false to resume it */ - void setSystemPalette(); + void pauseVideo(bool pause); + + /** + * Return whether the video is currently paused or not. + */ + bool isPaused() const { return _pauseLevel != 0; } + + /** + * Set the time for this video to end at. At this time in the video, + * all audio will stop and endOfVideo() will return true. + * + * While the setting is stored even if a video is not playing, + * endOfVideo() is only affected when the video is playing. + */ + void setEndTime(const Audio::Timestamp &endTime); + + /** + * Get the stop time of the video (if not set, zero) + */ + Audio::Timestamp getEndTime() const { return _endTime; } + + + ///////////////////////////////////////// + // Playback Status + ///////////////////////////////////////// + + /** + * Returns if the video has reached the end or not. + * @return true if the video has finished playing or if none is loaded, false otherwise + */ + bool endOfVideo() const; /** * Returns the current frame number of the video. * @return the last frame decoded by the video */ - virtual int32 getCurFrame() const { return _curFrame; } + int getCurFrame() const; /** * Returns the number of frames in the video. * @return the number of frames in the video */ - virtual uint32 getFrameCount() const = 0; + uint32 getFrameCount() const; /** * Returns the time position (in ms) of the current video. @@ -138,103 +254,493 @@ public: * completely accurate (since our mixer does not have precise * timing). */ - virtual uint32 getTime() const; + uint32 getTime() const; + + + ///////////////////////////////////////// + // Video Info + ///////////////////////////////////////// + + /** + * Returns the width of the video's frames. + * + * By default, this finds the largest width between all of the loaded + * tracks. However, a subclass may override this if it does any kind + * of post-processing on it. + * + * @return the width of the video's frames + */ + virtual uint16 getWidth() const; + + /** + * Returns the height of the video's frames. + * + * By default, this finds the largest height between all of the loaded + * tracks. However, a subclass may override this if it does any kind + * of post-processing on it. + * + * @return the height of the video's frames + */ + virtual uint16 getHeight() const; + + /** + * Get the pixel format of the currently loaded video. + */ + Graphics::PixelFormat getPixelFormat() const; + + /** + * Get the duration of the video. + * + * If the duration is unknown, this will return 0. If this is not + * overriden, it will take the length of the longest track. + */ + virtual Audio::Timestamp getDuration() const; + + + ///////////////////////////////////////// + // Frame Decoding + ///////////////////////////////////////// + + /** + * Get the palette for the video in RGB format (if 8bpp or less). + * + * The palette's format is the same as PaletteManager's palette + * (interleaved RGB values). + */ + const byte *getPalette(); + + /** + * Returns if the palette is dirty or not. + */ + bool hasDirtyPalette() const { return _dirtyPalette; } /** * Return the time (in ms) until the next frame should be displayed. */ - virtual uint32 getTimeToNextFrame() const = 0; + uint32 getTimeToNextFrame() const; /** * Check whether a new frame should be decoded, i.e. because enough * time has elapsed since the last frame was decoded. * @return whether a new frame should be decoded or not */ - virtual bool needsUpdate() const; + bool needsUpdate() const; /** * Decode the next frame into a surface and return the latter. + * + * A subclass may override this, but must still call this function. As an + * example, a subclass may do this to apply some global video scale to + * individual track's frame. + * + * Note that this will call readNextPacket() internally first before calling + * the next video track's decodeNextFrame() function. + * * @return a surface containing the decoded frame, or 0 * @note Ownership of the returned surface stays with the VideoDecoder, * hence the caller must *not* free it. * @note this may return 0, in which case the last frame should be kept on screen */ - virtual const Graphics::Surface *decodeNextFrame() = 0; + virtual const Graphics::Surface *decodeNextFrame(); /** - * Returns if the video has finished playing or not. - * @return true if the video has finished playing or if none is loaded, false otherwise + * Set the default high color format for videos that convert from YUV. + * + * By default, VideoDecoder will attempt to use the screen format + * if it's >8bpp and use a 32bpp format when not. + * + * This must be set before calling loadStream(). */ - virtual bool endOfVideo() const; + void setDefaultHighColorFormat(const Graphics::PixelFormat &format) { _defaultHighColorFormat = format; } /** - * Pause or resume the video. This should stop/resume any audio playback - * and other stuff. The initial pause time is kept so that any timing - * variables can be updated appropriately. + * Set the video to decode frames in reverse. * - * This is a convenience method which automatically keeps track on how - * often the video has been paused, ensuring that after pausing an video - * e.g. twice, it has to be unpaused twice before actuallying resuming. + * By default, VideoDecoder will decode forward. * - * @param pause true to pause the video, false to resume it + * @note This is used by setRate() + * @note This will not work if an audio track is present + * @param reverse true for reverse, false for forward + * @return true on success, false otherwise */ - void pauseVideo(bool pause); + bool setReverse(bool reverse); - /** - * Return whether the video is currently paused or not. - */ - bool isPaused() const { return _pauseLevel != 0; } + ///////////////////////////////////////// + // Audio Control + ///////////////////////////////////////// /** * Get the current volume at which the audio in the video is being played * @return the current volume at which the audio in the video is being played */ - virtual byte getVolume() const { return _audioVolume; } + byte getVolume() const { return _audioVolume; } /** * Set the volume at which the audio in the video should be played. - * This setting remains until reset() is called (which may be called - * from loadStream() or close()). The default volume is the maximum. - * - * @note This function calls updateVolume() by default. + * This setting remains until close() is called (which may be called + * from loadStream()). The default volume is the maximum. * * @param volume The volume at which to play the audio in the video */ - virtual void setVolume(byte volume); + void setVolume(byte volume); /** * Get the current balance at which the audio in the video is being played * @return the current balance at which the audio in the video is being played */ - virtual int8 getBalance() const { return _audioBalance; } + int8 getBalance() const { return _audioBalance; } /** * Set the balance at which the audio in the video should be played. - * This setting remains until reset() is called (which may be called - * from loadStream() or close()). The default balance is 0. - * - * @note This function calls updateBalance() by default. + * This setting remains until close() is called (which may be called + * from loadStream()). The default balance is 0. * * @param balance The balance at which to play the audio in the video */ - virtual void setBalance(int8 balance); + void setBalance(int8 balance); + + /** + * Add an audio track from a stream file. + * + * This calls SeekableAudioStream::openStreamFile() internally + */ + bool addStreamFileTrack(const Common::String &baseName); protected: /** - * Resets _curFrame and _startTime. Should be called from every close() function. + * An abstract representation of a track in a movie. Since tracks here are designed + * to work independently, they should not reference any other track(s) in the video. + */ + class Track { + public: + Track(); + virtual ~Track() {} + + /** + * The types of tracks this class can be. + */ + enum TrackType { + kTrackTypeNone, + kTrackTypeVideo, + kTrackTypeAudio + }; + + /** + * Get the type of track. + */ + virtual TrackType getTrackType() const = 0; + + /** + * Return if the track has finished. + */ + virtual bool endOfTrack() const = 0; + + /** + * Return if the track is rewindable. + * + * If a video is seekable, it does not need to implement this + * for it to also be rewindable. + */ + virtual bool isRewindable() const; + + /** + * Rewind the video to the beginning. + * + * If a video is seekable, it does not need to implement this + * for it to also be rewindable. + * + * @return true on success, false otherwise. + */ + virtual bool rewind(); + + /** + * Return if the track is seekable. + */ + virtual bool isSeekable() const { return false; } + + /** + * Seek to the given time. + * @param time The time to seek to, from the beginning of the video. + * @return true on success, false otherwise. + */ + virtual bool seek(const Audio::Timestamp &time) { return false; } + + /** + * Set the pause status of the track. + */ + void pause(bool shouldPause); + + /** + * Return if the track is paused. + */ + bool isPaused() const { return _paused; } + + /** + * Get the duration of the track (starting from this track's start time). + * + * By default, this returns 0 for unknown. + */ + virtual Audio::Timestamp getDuration() const; + + protected: + /** + * Function called by pause() for subclasses to implement. + */ + virtual void pauseIntern(bool shouldPause) {} + + private: + bool _paused; + }; + + /** + * An abstract representation of a video track. + */ + class VideoTrack : public Track { + public: + VideoTrack() {} + virtual ~VideoTrack() {} + + TrackType getTrackType() const { return kTrackTypeVideo; } + virtual bool endOfTrack() const; + + /** + * Get the width of this track + */ + virtual uint16 getWidth() const = 0; + + /** + * Get the height of this track + */ + virtual uint16 getHeight() const = 0; + + /** + * Get the pixel format of this track + */ + virtual Graphics::PixelFormat getPixelFormat() const = 0; + + /** + * Get the current frame of this track + * + * @see VideoDecoder::getCurFrame() + */ + virtual int getCurFrame() const = 0; + + /** + * Get the frame count of this track + * + * @note If the frame count is unknown, return 0 (which is also + * the default implementation of the function). However, one must + * also implement endOfTrack() in that case. + */ + virtual int getFrameCount() const { return 0; } + + /** + * Get the start time of the next frame in milliseconds since + * the start of the video + */ + virtual uint32 getNextFrameStartTime() const = 0; + + /** + * Decode the next frame + */ + virtual const Graphics::Surface *decodeNextFrame() = 0; + + /** + * Get the palette currently in use by this track + */ + virtual const byte *getPalette() const { return 0; } + + /** + * Does the palette currently in use by this track need to be updated? + */ + virtual bool hasDirtyPalette() const { return false; } + + /** + * Get the time the given frame should be shown. + * + * By default, this returns a negative (invalid) value. This function + * should only be used by VideoDecoder::seekToFrame(). + */ + virtual Audio::Timestamp getFrameTime(uint frame) const; + + /** + * Set the video track to play in reverse or forward. + * + * By default, a VideoTrack must decode forward. + * + * @param reverse true for reverse, false for forward + * @return true for success, false for failure + */ + virtual bool setReverse(bool reverse) { return !reverse; } + + /** + * Is the video track set to play in reverse? + */ + virtual bool isReversed() const { return false; } + }; + + /** + * A VideoTrack that is played at a constant rate. + * + * If the frame count is unknown, you must override endOfTrack(). + */ + class FixedRateVideoTrack : public VideoTrack { + public: + FixedRateVideoTrack() {} + virtual ~FixedRateVideoTrack() {} + + uint32 getNextFrameStartTime() const; + virtual Audio::Timestamp getDuration() const; + Audio::Timestamp getFrameTime(uint frame) const; + + protected: + /** + * Get the rate at which this track is played. + */ + virtual Common::Rational getFrameRate() const = 0; + + /** + * Get the frame that should be displaying at the given time. This is + * helpful for someone implementing seek(). + */ + uint getFrameAtTime(const Audio::Timestamp &time) const; + }; + + /** + * An abstract representation of an audio track. */ - void reset(); + class AudioTrack : public Track { + public: + AudioTrack() {} + virtual ~AudioTrack() {} + + TrackType getTrackType() const { return kTrackTypeAudio; } + + virtual bool endOfTrack() const; + + /** + * Start playing this track + */ + void start(); + + /** + * Stop playing this track + */ + void stop(); + + void start(const Audio::Timestamp &limit); + + /** + * Get the volume for this track + */ + byte getVolume() const { return _volume; } + + /** + * Set the volume for this track + */ + void setVolume(byte volume); + + /** + * Get the balance for this track + */ + int8 getBalance() const { return _balance; } + + /** + * Set the balance for this track + */ + void setBalance(int8 balance); + + /** + * Get the time the AudioStream behind this track has been + * running + */ + uint32 getRunningTime() const; + + /** + * Get the sound type to be used when playing this audio track + */ + virtual Audio::Mixer::SoundType getSoundType() const { return Audio::Mixer::kPlainSoundType; } + + protected: + void pauseIntern(bool shouldPause); + + /** + * Get the AudioStream that is the representation of this AudioTrack + */ + virtual Audio::AudioStream *getAudioStream() const = 0; + + private: + Audio::SoundHandle _handle; + byte _volume; + int8 _balance; + }; + + /** + * An AudioTrack that implements isRewindable() and rewind() using + * RewindableAudioStream. + */ + class RewindableAudioTrack : public AudioTrack { + public: + RewindableAudioTrack() {} + virtual ~RewindableAudioTrack() {} + + bool isRewindable() const { return true; } + bool rewind(); + + protected: + Audio::AudioStream *getAudioStream() const; + + /** + * Get the RewindableAudioStream pointer to be used by this class + * for rewind() and getAudioStream() + */ + virtual Audio::RewindableAudioStream *getRewindableAudioStream() const = 0; + }; /** - * Actual implementation of pause by subclasses. See pause() - * for details. + * An AudioTrack that implements isSeekable() and seek() using + * SeekableAudioStream. */ - virtual void pauseVideoIntern(bool pause) {} + class SeekableAudioTrack : public AudioTrack { + public: + SeekableAudioTrack() {} + virtual ~SeekableAudioTrack() {} + + bool isSeekable() const { return true; } + bool seek(const Audio::Timestamp &time); + + Audio::Timestamp getDuration() const; + + protected: + Audio::AudioStream *getAudioStream() const; + + /** + * Get the SeekableAudioStream pointer to be used by this class + * for seek(), getDuration(), and getAudioStream() + */ + virtual Audio::SeekableAudioStream *getSeekableAudioStream() const = 0; + }; /** - * Add the time the video has been paused to maintain sync + * A SeekableAudioTrack that constructs its SeekableAudioStream using + * SeekableAudioStream::openStreamFile() */ - virtual void addPauseTime(uint32 ms) { _startTime += ms; } + class StreamFileAudioTrack : public SeekableAudioTrack { + public: + StreamFileAudioTrack(); + ~StreamFileAudioTrack(); + + /** + * Load the track from a file with the given base name. + * + * @return true on success, false otherwise + */ + bool loadFromFile(const Common::String &baseName); + + protected: + Audio::SeekableAudioStream *_stream; + Audio::SeekableAudioStream *getSeekableAudioStream() const { return _stream; } + }; /** * Reset the pause start time (which should be called when seeking) @@ -242,79 +748,108 @@ protected: void resetPauseStartTime(); /** - * Update currently playing audio tracks with the new volume setting + * Decode enough data for the next frame and enough audio to last that long. + * + * This function is used by this class' decodeNextFrame() function. A subclass + * of a Track may decide to just have its decodeNextFrame() function read + * and decode the frame, but only if it is the only track in the video. */ - virtual void updateVolume() {} + virtual void readNextPacket() {} /** - * Update currently playing audio tracks with the new balance setting + * Define a track to be used by this class. + * + * The pointer is then owned by this base class. */ - virtual void updateBalance() {} + void addTrack(Track *track); - int32 _curFrame; - int32 _startTime; - -private: - uint32 _pauseLevel; - uint32 _pauseStartTime; - byte _audioVolume; - int8 _audioBalance; -}; + /** + * Whether or not getTime() will sync with a playing audio track. + * + * A subclass can override this to disable this feature. + */ + virtual bool useAudioSync() const { return true; } -/** - * A VideoDecoder wrapper that implements getTimeToNextFrame() based on getFrameRate(). - */ -class FixedRateVideoDecoder : public virtual VideoDecoder { -public: - uint32 getTimeToNextFrame() const; + /** + * Get the given track based on its index. + * + * @return A valid track pointer on success, 0 otherwise + */ + Track *getTrack(uint track); -protected: /** - * Return the frame rate in frames per second. - * This returns a Rational because videos can have rates that are not integers and - * there are some videos with frame rates < 1. + * Get the given track based on its index + * + * @return A valid track pointer on success, 0 otherwise */ - virtual Common::Rational getFrameRate() const = 0; + const Track *getTrack(uint track) const; -private: - uint32 getFrameBeginTime(uint32 frame) const; -}; + /** + * Find out if all video tracks have finished + * + * This is useful if one wants to figure out if they need to buffer all + * remaining audio in a file. + */ + bool endOfVideoTracks() const; -/** - * A VideoDecoder that can be rewound back to the beginning. - */ -class RewindableVideoDecoder : public virtual VideoDecoder { -public: /** - * Rewind to the beginning of the video. + * Get the default high color format */ - virtual void rewind() = 0; -}; + Graphics::PixelFormat getDefaultHighColorFormat() const { return _defaultHighColorFormat; } -/** - * A VideoDecoder that can seek to a frame or point in time. - */ -class SeekableVideoDecoder : public virtual RewindableVideoDecoder { -public: /** - * Seek to the specified time. + * Set _nextVideoTrack to the video track with the lowest start time for the next frame. + * + * @return _nextVideoTrack */ - virtual void seekToTime(const Audio::Timestamp &time) = 0; + VideoTrack *findNextVideoTrack(); /** - * Seek to the specified time (in ms). + * Typedef helpers for accessing tracks */ - void seekToTime(uint32 msecs) { seekToTime(Audio::Timestamp(msecs, 1000)); } + typedef Common::Array<Track *> TrackList; + typedef TrackList::iterator TrackListIterator; /** - * Implementation of RewindableVideoDecoder::rewind(). + * Get the begin iterator of the tracks */ - virtual void rewind() { seekToTime(0); } + TrackListIterator getTrackListBegin() { return _tracks.begin(); } /** - * Get the total duration of the video (in ms). + * Get the end iterator of the tracks */ - virtual uint32 getDuration() const = 0; + TrackListIterator getTrackListEnd() { return _tracks.end(); } + +private: + // Tracks owned by this VideoDecoder + TrackList _tracks; + + // Current playback status + bool _needsUpdate; + Audio::Timestamp _lastTimeChange, _endTime; + bool _endTimeSet; + Common::Rational _playbackRate; + VideoTrack *_nextVideoTrack; + + // Palette settings from individual tracks + mutable bool _dirtyPalette; + const byte *_palette; + + // Default PixelFormat settings + Graphics::PixelFormat _defaultHighColorFormat; + + // Internal helper functions + void stopAudio(); + void startAudio(); + void startAudioLimit(const Audio::Timestamp &limit); + bool hasFramesLeft() const; + bool hasAudio() const; + + int32 _startTime; + uint32 _pauseLevel; + uint32 _pauseStartTime; + byte _audioVolume; + int8 _audioBalance; }; } // End of namespace Video |